summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog50
-rw-r--r--README16
-rw-r--r--autostart/.cvsignore14
-rw-r--r--autostart/AUTHORS5
-rw-r--r--autostart/COPYING2
-rw-r--r--autostart/ChangeLog4
-rw-r--r--autostart/INSTALL236
-rw-r--r--autostart/Makefile.am16
-rw-r--r--autostart/NEWS0
-rw-r--r--autostart/README6
-rwxr-xr-xautostart/autogen.sh3
-rw-r--r--autostart/autostart-spec.xml273
-rw-r--r--autostart/configure.in61
-rw-r--r--basedir/.gitignore1
-rw-r--r--basedir/Makefile7
-rw-r--r--basedir/basedir-spec.xml298
-rw-r--r--desktop-entry/.cvsignore2
-rw-r--r--desktop-entry/ChangeLog182
-rw-r--r--desktop-entry/desktop-entry-spec.xml1392
-rw-r--r--help-system/help-system-spec.xml195
-rw-r--r--menu/.cvsignore11
-rw-r--r--menu/AUTHORS0
-rw-r--r--menu/COPYING2
-rw-r--r--menu/ChangeLog143
-rw-r--r--menu/Makefile.am16
-rw-r--r--menu/NEWS0
-rw-r--r--menu/README1
-rwxr-xr-xmenu/autogen.sh3
-rw-r--r--menu/configure.in61
-rw-r--r--menu/menu-spec.xml2392
-rw-r--r--menu/menu.dtd84
-rw-r--r--menu/tests/ChangeLog70
-rw-r--r--menu/tests/README44
-rw-r--r--menu/tests/TODO5
-rw-r--r--menu/tests/data/Help.desktop68
-rw-r--r--menu/tests/data/Home.desktop127
-rw-r--r--menu/tests/data/KEdit.desktop34
-rw-r--r--menu/tests/data/Kfind.desktop66
-rw-r--r--menu/tests/data/apps.directory66
-rw-r--r--menu/tests/data/freecell.desktop83
-rw-r--r--menu/tests/data/gataxx.desktop81
-rw-r--r--menu/tests/data/gideon-legacy.desktop12
-rw-r--r--menu/tests/data/gideon.desktop13
-rw-r--r--menu/tests/data/glines-2.desktop83
-rw-r--r--menu/tests/data/glines.desktop82
-rw-r--r--menu/tests/data/hidden.desktop8
-rw-r--r--menu/tests/data/hidden.directory5
-rw-r--r--menu/tests/data/kate.desktop30
-rw-r--r--menu/tests/data/kbabel.desktop69
-rw-r--r--menu/tests/data/kedit-legacy.desktop33
-rw-r--r--menu/tests/data/kwrite.desktop84
-rw-r--r--menu/tests/data/mahjongg-2.desktop86
-rw-r--r--menu/tests/data/mahjongg.desktop86
-rw-r--r--menu/tests/data/quanta.desktop10
-rwxr-xr-xmenu/tests/expand12
-rwxr-xr-xmenu/tests/menutest200
-rw-r--r--menu/tests/tests/All/result4
-rw-r--r--menu/tests/tests/All/test26
-rw-r--r--menu/tests/tests/And/result1
-rw-r--r--menu/tests/tests/And/test29
-rw-r--r--menu/tests/tests/AppDir-relative/result3
-rw-r--r--menu/tests/tests/AppDir-relative/test27
-rw-r--r--menu/tests/tests/AppDir/test4
-rw-r--r--menu/tests/tests/Category/result3
-rw-r--r--menu/tests/tests/Category/test29
-rw-r--r--menu/tests/tests/DefaultMergeDirs/result5
-rw-r--r--menu/tests/tests/DefaultMergeDirs/test43
-rw-r--r--menu/tests/tests/Deleted/result2
-rw-r--r--menu/tests/tests/Deleted/test35
-rw-r--r--menu/tests/tests/DesktopFileID/result4
-rw-r--r--menu/tests/tests/DesktopFileID/test26
-rw-r--r--menu/tests/tests/Directory/result3
-rw-r--r--menu/tests/tests/Directory/test29
-rw-r--r--menu/tests/tests/DirectoryDir-relative/result3
-rw-r--r--menu/tests/tests/DirectoryDir-relative/test28
-rw-r--r--menu/tests/tests/DirectoryDir/test4
-rw-r--r--menu/tests/tests/Exclude/result3
-rw-r--r--menu/tests/tests/Exclude/test32
-rw-r--r--menu/tests/tests/Filename/result1
-rw-r--r--menu/tests/tests/Filename/test26
-rw-r--r--menu/tests/tests/LegacyDir-Move/result2
-rw-r--r--menu/tests/tests/LegacyDir-Move/test39
-rw-r--r--menu/tests/tests/LegacyDir-relative/result9
-rw-r--r--menu/tests/tests/LegacyDir-relative/test42
-rw-r--r--menu/tests/tests/Merge-combined/result1
-rw-r--r--menu/tests/tests/Merge-combined/test46
-rw-r--r--menu/tests/tests/MergeDir-absolute/test3
-rw-r--r--menu/tests/tests/MergeDir-relative/result5
-rw-r--r--menu/tests/tests/MergeDir-relative/test61
-rw-r--r--menu/tests/tests/MergeFile-absolute/test3
-rw-r--r--menu/tests/tests/MergeFile-parent/result5
-rw-r--r--menu/tests/tests/MergeFile-parent/test61
-rw-r--r--menu/tests/tests/MergeFile-path/result5
-rw-r--r--menu/tests/tests/MergeFile-path/test61
-rw-r--r--menu/tests/tests/MergeFile-recursive/result5
-rw-r--r--menu/tests/tests/MergeFile-recursive/test58
-rw-r--r--menu/tests/tests/MergeFile-relative/result5
-rw-r--r--menu/tests/tests/MergeFile-relative/test44
-rw-r--r--menu/tests/tests/MergeFile2/result5
-rw-r--r--menu/tests/tests/MergeFile2/test57
-rw-r--r--menu/tests/tests/MergeFile3/result5
-rw-r--r--menu/tests/tests/MergeFile3/test56
-rw-r--r--menu/tests/tests/Move-collapsing/result4
-rw-r--r--menu/tests/tests/Move-collapsing/test40
-rw-r--r--menu/tests/tests/Move-ordering/result3
-rw-r--r--menu/tests/tests/Move-ordering/test49
-rw-r--r--menu/tests/tests/Move-submenu/result1
-rw-r--r--menu/tests/tests/Move-submenu/test32
-rw-r--r--menu/tests/tests/Move/result2
-rw-r--r--menu/tests/tests/Move/test34
-rw-r--r--menu/tests/tests/NoDisplay/result1
-rw-r--r--menu/tests/tests/NoDisplay/test37
-rw-r--r--menu/tests/tests/NoDisplay2/result1
-rw-r--r--menu/tests/tests/NoDisplay2/test38
-rw-r--r--menu/tests/tests/NotOnlyUnallocated-default/result2
-rw-r--r--menu/tests/tests/NotOnlyUnallocated-default/test33
-rw-r--r--menu/tests/tests/OnlyUnallocated/result3
-rw-r--r--menu/tests/tests/OnlyUnallocated/test44
-rw-r--r--menu/tests/tests/Or/result4
-rw-r--r--menu/tests/tests/Or/test29
-rw-r--r--menu/tests/tests/boolean-logic/result3
-rw-r--r--menu/tests/tests/boolean-logic/test36
-rw-r--r--menu/tests/tests/desktop-name-collision/result3
-rw-r--r--menu/tests/tests/desktop-name-collision/test53
-rw-r--r--menu/tests/tests/menu-multiple-matching/result5
-rw-r--r--menu/tests/tests/menu-multiple-matching/test36
-rw-r--r--menu/tests/tests/official-categories/categories.list10
-rw-r--r--menu/tests/tests/official-categories/test73
-rw-r--r--menu/tests/tests/official-categories/unique-entry.desktop9
-rw-r--r--menu/tests/tests/submenu-collision/result5
-rw-r--r--menu/tests/tests/submenu-collision/test32
-rwxr-xr-xmenu/tests/tet_menutest39
-rw-r--r--menu/tet_scen5
-rw-r--r--menu/tetexec.cfg1
-rw-r--r--secret-service/.gitignore3
-rw-r--r--secret-service/Makefile17
-rw-r--r--secret-service/README20
-rw-r--r--secret-service/docbook-params.xsl39
-rw-r--r--secret-service/html/style.css127
-rw-r--r--secret-service/org.freedesktop.Secrets.xml490
-rw-r--r--secret-service/specification.xml580
-rw-r--r--secret-service/tools/resolve-type.xsl122
-rw-r--r--secret-service/tools/spec-to-docbook.xsl1242
-rw-r--r--secret-service/tools/spec-to-introspect.xsl144
-rw-r--r--systemtray/ChangeLog10
-rw-r--r--systemtray/systemtray-spec.xml444
-rw-r--r--trash/trashspec.html457
-rw-r--r--web-export/README9
-rw-r--r--web-export/specs.idx91
-rwxr-xr-xweb-export/update.py274
-rw-r--r--wm-spec/Makefile7
-rw-r--r--wm-spec/wm-spec.xml2760
152 files changed, 15384 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..0c70587
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,50 @@
+=== No ChangeLog ===
+
+ Since this module is using git, we rely on commit messages to provide change
+ history. Please write commit messages in the format described at
+ http://live.gnome.org/Git/CommitMessages
+
+ Below is a copy of this format:
+
+=== begin example commit ===
+tag: Short explanation of the commit
+
+Longer explanation explaining exactly what's changed, whether any
+external or private interfaces changed, what bugs were fixed (with bug
+tracker reference if applicable) and so forth. Be concise but not too brief.
+=== end example commit ===
+
+ - The commit message is mainly for the other people, so they should be able
+ to understand it now and six months later.
+
+ - Always add a brief description of the commit to the _first_ line of the
+ commit and terminate by two newlines (it will work without the second
+ newline, but that is not nice for the interfaces).
+
+ - First line (the brief description) must only be one sentence and should
+ start with a capital letter unless it starts with a lowercase symbol or
+ identifier. Don't use a trailing period either. Don't exceed 72 characters.
+
+ - You can prefix the first line with one tag, to make it easier to know to
+ which part of the module the commit applies. For example, a commit with
+ "fish: Make it work with newer fortune" in the gnome-panel module clearly
+ applies to the fish applet.
+
+ - The main description (the body) is normal prose and should use normal
+ punctuation and capital letters where appropriate. Normally, for patches
+ sent to a mailing list, the body is copied from there. This main
+ description can be empty if the change is self-explanatory (eg: "Add DOAP
+ file").
+
+ - When committing code on behalf of others use the --author option, e.g. git
+ commit -a --author "Joe Coder <joe@coder.org>".
+
+ - When referring to a bug, you can use this form: bgo#12345. Use bgo for
+ bugzilla.gnome.org, but you can also reference bugs in other bug trackers:
+ rh means bugzilla.redhat.com, bnc means bugzilla.novell.com, lp means
+ launchpad.net, etc. Whenever possible, use the full URL of the bug, though.
+
+ - When a commit closes a bug, the commit message should contain a line like:
+ Closes: http://bugzilla.gnome.org/show_bug.cgi?id=12345
+ or simply:
+ http://bugzilla.gnome.org/show_bug.cgi?id=12345
diff --git a/README b/README
new file mode 100644
index 0000000..490170f
--- /dev/null
+++ b/README
@@ -0,0 +1,16 @@
+xdg-specs
+=========
+
+This repository contains the XDG specifications.
+
+To discuss the specifications, you may use the xdg mailing list:
+
+ http://lists.freedesktop.org/mailman/listinfo/xdg
+
+
+How to report issues
+====================
+
+Issues should be reported to the freedesktop.org bug tracking system:
+
+ https://bugs.freedesktop.org/ (product Specifications)
diff --git a/autostart/.cvsignore b/autostart/.cvsignore
new file mode 100644
index 0000000..f5518ef
--- /dev/null
+++ b/autostart/.cvsignore
@@ -0,0 +1,14 @@
+config.log
+config.status
+config.sub
+configure
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+config.guess
+config.h
+config.h.in
+install-sh
+missing
+autostart-spec.html
diff --git a/autostart/AUTHORS b/autostart/AUTHORS
new file mode 100644
index 0000000..cc42641
--- /dev/null
+++ b/autostart/AUTHORS
@@ -0,0 +1,5 @@
+John Palmieri <johnp@redhat.com>
+Kévin Ottens <ervin@ipsquad.net>
+Renato Caldas <seventhguardian_@hotmail.com>
+Rodrigo Moya <rodrigo@gnome-db.org>
+Waldo Bastian <waldo.bastian@intel.com>
diff --git a/autostart/COPYING b/autostart/COPYING
new file mode 100644
index 0000000..73e7995
--- /dev/null
+++ b/autostart/COPYING
@@ -0,0 +1,2 @@
+FIXME - we need a license for specs
+
diff --git a/autostart/ChangeLog b/autostart/ChangeLog
new file mode 100644
index 0000000..b7b5318
--- /dev/null
+++ b/autostart/ChangeLog
@@ -0,0 +1,4 @@
+2006-02-13 Waldo Bastian <waldo.bastian@intel.com>
+
+ * Import of autostart spec
+
diff --git a/autostart/INSTALL b/autostart/INSTALL
new file mode 100644
index 0000000..56b077d
--- /dev/null
+++ b/autostart/INSTALL
@@ -0,0 +1,236 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
+Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about. Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory. After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PREFIX'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PREFIX', the package will
+use PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the `--target=TYPE' option to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script). Here is a another example:
+
+ /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
+configuration-related scripts to be executed by `/bin/bash'.
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/autostart/Makefile.am b/autostart/Makefile.am
new file mode 100644
index 0000000..f13d2c9
--- /dev/null
+++ b/autostart/Makefile.am
@@ -0,0 +1,16 @@
+HTML_FILES= autostart-spec.html
+
+XML_FILES= autostart-spec.xml
+
+EXTRA_DIST= $(HTML_FILES) $(XML_FILES)
+
+if XML_DOCS_ENABLED
+all-local: $(HTML_FILES)
+endif
+
+%.html: %.xml
+ $(XMLTO) html-nochunks $<
+
+maintainer-clean-local:
+ rm -f $(HTML_FILES)
+
diff --git a/autostart/NEWS b/autostart/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/autostart/NEWS
diff --git a/autostart/README b/autostart/README
new file mode 100644
index 0000000..7700629
--- /dev/null
+++ b/autostart/README
@@ -0,0 +1,6 @@
+This is the autostart specification.
+
+It covers
+* automatic launching of applications when the user logs in
+* automatic launching of applications when a new media is mounted
+
diff --git a/autostart/autogen.sh b/autostart/autogen.sh
new file mode 100755
index 0000000..b1376df
--- /dev/null
+++ b/autostart/autogen.sh
@@ -0,0 +1,3 @@
+#! /bin/sh
+autoreconf -v --install || exit 1
+./configure --enable-maintainer-mode "$@"
diff --git a/autostart/autostart-spec.xml b/autostart/autostart-spec.xml
new file mode 100644
index 0000000..ba5487e
--- /dev/null
+++ b/autostart/autostart-spec.xml
@@ -0,0 +1,273 @@
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+ <!ENTITY version "0.5">
+ <!ENTITY dtd-version "0.5">
+ ]>
+
+<article id="index">
+ <articleinfo>
+ <title>Desktop Application Autostart Specification</title>
+ <releaseinfo>Version &version;</releaseinfo>
+ <date>13 February 2006</date>
+ <authorgroup>
+ <author>
+ <firstname>John</firstname>
+ <surname>Palmieri</surname>
+ <affiliation>
+ <address>
+ <email>johnp@redhat.com</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Kévin</firstname>
+ <surname>Ottens</surname>
+ <affiliation>
+ <address>
+ <email>ervin@ipsquad.net</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Renato</firstname>
+ <surname>Caldas</surname>
+ <affiliation>
+ <address>
+ <email>seventhguardian_@hotmail.com</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Rodrigo</firstname>
+ <surname>Moya</surname>
+ <affiliation>
+ <address>
+ <email>rodrigo@gnome-db.org</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Waldo</firstname>
+ <surname>Bastian</surname>
+ <affiliation>
+ <address>
+ <email>bastian@kde.org</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+ </articleinfo>
+
+ <sect1 id="introduction">
+ <title>Introduction</title>
+ <para>
+ This DRAFT document defines a method for automatically starting
+ applications during the startup of a desktop environment and after
+ mounting a removable medium.
+ </para>
+ <para>
+ Some of the file locations in this specification are specified based
+ on the <ulink url="http://standards.freedesktop.org/basedir-spec/">
+ "desktop base directory specification"</ulink>.
+ </para>
+ <para>
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL
+ NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and
+ "OPTIONAL" in this document are to be interpreted as
+ described in RFC 2119.
+ </para>
+ </sect1>
+ <sect1 id="startup">
+ <title>Autostart Of Applications During Startup</title>
+ <para>
+ By placing an application's .desktop file
+ in one of the Autostart directories the application will be
+ automatically launched during startup of the user's desktop environment after the user
+ has logged in.
+ </para>
+ <sect2>
+ <title>Autostart Directories</title>
+ <para>
+ The Autostart Directories are $XDG_CONFIG_DIRS/autostart as defined
+ in accordance with the
+ <ulink url="http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html#referencing">
+ "Referencing this specification"</ulink> section in the
+ <ulink url="http://standards.freedesktop.org/basedir-spec/">
+ "desktop base directory specification"</ulink>.
+ </para>
+ <para>
+ If the same filename is located under multiple Autostart Directories
+ only the file under the most important directory should be used.
+ </para>
+ <informalexample>
+ <para>
+ Example: If $XDG_CONFIG_HOME is not set the Autostart Directory
+ in the user's home directory is ~/.config/autostart/
+ </para>
+ </informalexample>
+ <informalexample>
+ <para>
+ Example: If $XDG_CONFIG_DIRS is not set the system wide Autostart
+ Directory is /etc/xdg/autostart/
+ </para>
+ </informalexample>
+ <informalexample>
+ <para>
+ Example: If $XDG_CONFIG_HOME and $XDG_CONFIG_DIRS are not set and
+ the two files /etc/xdg/autostart/foo.desktop and
+ ~/.config/autostart/foo.desktop exist then only the file
+ ~/.config/autostart/foo.desktop will be used because
+ ~/.config/autostart/ is more important than /etc/xdg/autostart/
+ </para>
+ </informalexample>
+ </sect2>
+ <sect2>
+ <title>Application .desktop Files</title>
+ <para>
+ An application .desktop file must have the format as defined in
+ the <ulink url="http://standards.freedesktop.org/desktop-entry-spec/">
+ "Desktop Entry Specification"</ulink>. All keys should be
+ interpreted as defined with the following exceptions in order to
+ take into account that the .desktop files in an autostart directory
+ are not shown in a menu.
+ </para>
+ <sect3>
+ <title>Hidden Key</title>
+ <para>
+ When the .desktop file has the Hidden key set to true, the .desktop
+ file MUST be ignored. When multiple .desktop files with the
+ same name exists in multiple directories then only the Hidden key
+ in the most important .desktop file must be considered: If it is
+ set to true all .desktop files with the same name
+ in the other directories MUST be ignored as well.
+ </para>
+ </sect3>
+ <sect3>
+ <title>OnlyShowIn and NotShowIn Keys</title>
+ <para>
+ The OnlyShownIn entry may contain a list of strings identifying
+ the desktop environments that MUST autostart this application,
+ all other desktop environments MUST NOT autostart this application.
+ </para>
+ <para>
+ The NotShownIn entry may contain a list of strings identifying
+ the desktop environments that MUST NOT autostart this
+ application, all other desktop environments MUST autostart this
+ application.
+ </para>
+ <para>
+ Only one of these keys, either OnlyShowIn or NotShowIn, may appear
+ in a single .desktop file.
+ </para>
+ </sect3>
+ <sect3>
+ <title>TryExec Key</title>
+ <para>
+ A .desktop file with a non-empty TryExec field MUST NOT be
+ autostarted if the value of the TryExec key does NOT match with
+ an installed executable program. The value of the TryExec field
+ may either be an absolute path or the name of an executable
+ without any path components. If the name of an executable is
+ specified without any path components then the $PATH environment
+ is searched to find a matching executable program.
+ </para>
+ </sect3>
+ </sect2>
+ <sect2>
+ <title>Implementation Notes</title>
+ <informalexample>
+ <para>
+ If an application autostarts by having a .desktop file installed
+ in the system wide autostart directory, an individual user can
+ disable the autotomatic start of this application by placing a
+ .desktop file of the same name in its personal autostart directory
+ which contains the key Hidden=true.
+ </para>
+ </informalexample>
+ </sect2>
+ </sect1>
+ <sect1 id="mounting">
+ <title>Autostart Of Applications After Mount</title>
+ <para>
+ When a desktop environment mounts a new medium, the medium may contain
+ an Autostart file that can suggest to start an application or
+ an Autoopen file that can suggest to open a specific file located
+ on the medium.
+ </para>
+ <sect2>
+ <title>Autostart Files</title>
+ <para>
+ When a new medium is mounted the root directory of the medium should
+ be checked for the following Autostart files in order of precendence:
+ .autorun, autorun, autorun.sh
+ Only the first file that is present should be considered.
+ </para>
+ <para>
+ The desktop environment MAY ignore Autostart files altogether
+ based on policy set by the user, system administrator or vendor.
+ </para>
+ <para>
+ The desktop environment MUST prompt the user for confirmation before
+ automatically starting an application.
+ </para>
+ <para>
+ When an Autostart file has been detected and the user has confirmed
+ its execution the autostart file MUST be executed with the
+ current working directory (CWD) set to the root directory of the
+ medium.
+ </para>
+ </sect2>
+ <sect2>
+ <title>Autoopen Files</title>
+ <para>
+ When a new medium is mounted and a) the medium does not contain an
+ Autostart file or b) a policy to ignore Autostart files is in effect
+ then the root directory of the medium should be checked for the
+ following Autoopen files in order of precedence:
+ .autoopen, autoopen .
+ Only the first file that is present should be considered.
+ </para>
+ <para>
+ The desktop environment MAY ignore Autoopen files altogether
+ based on policy set by the user, system administrator or vendor.
+ </para>
+ <para>
+ An Autoopen file MUST contain a single relative path that points to
+ a non-executable file contained on the medium.
+ If the file contains a newline or carriage return character then the
+ newline or carriage return character itself and all characters that
+ follow MUST be ignored.
+ </para>
+ <para>
+ The relative path MUST NOT contain path components that refer to a
+ parent directory (../)
+ </para>
+ <para>
+ The relative path MUST NOT point to an executable file.
+ </para>
+ <para>
+ The desktop environment MUST verify that the relative path points to
+ a file that is actually located on the medium, taking into account any
+ symbolic or other links and MUST ignore any relative path that
+ points to a file location outside the medium itself.
+ </para>
+ <para>
+ If the relative path points to an executable file then the desktop
+ environment MUST NOT execute the file.
+ </para>
+ <para>
+ The desktop environment MUST prompt the user for confirmation before
+ opening the file.
+ </para>
+ <para>
+ When an Autoopen file has been detected and the user has confirmed
+ that the file indicated in the Autoopen file should be opened then
+ the file indicated in the Autoopen file MUST be opened in the
+ application normally preferred by the user for files of its kind
+ UNLESS the user instructed otherwise.
+ </para>
+ </sect2>
+ </sect1>
+
+</article>
+
diff --git a/autostart/configure.in b/autostart/configure.in
new file mode 100644
index 0000000..7df76ac
--- /dev/null
+++ b/autostart/configure.in
@@ -0,0 +1,61 @@
+dnl -*- mode: m4 -*-
+AC_PREREQ(2.52)
+
+AC_INIT(autostart-spec.xml)
+
+AM_INIT_AUTOMAKE(autostart-spec, 0.5)
+
+# Honor aclocal flags
+ACLOCAL="$ACLOCAL $ACLOCAL_FLAGS"
+
+ ## must come before we use the $USE_MAINTAINER_MODE variable later
+AM_MAINTAINER_MODE
+
+AC_ARG_ENABLE(xml-docs, [ --enable-xml-docs build XML into HTML (requires xmlto)],enable_xml_docs=$enableval,enable_xml_docs=auto)
+
+### XML tools
+
+AC_PATH_PROG(XMLTO, xmlto, no)
+
+AC_MSG_CHECKING([whether to build XML documentation])
+
+if test x$XMLTO = xno ; then
+ have_xmlto=no
+else
+ have_xmlto=yes
+fi
+
+if test x$enable_xml_docs = xauto ; then
+ if test x$have_xmlto = xno ; then
+ enable_xml_docs=no
+ else
+ enable_xml_docs=yes
+ fi
+fi
+
+if test x$enable_xml_docs = xyes; then
+ if test x$have_xmlto = xno; then
+ AC_MSG_ERROR([Building XML docs explicitly required, but xmlto not found])
+ fi
+fi
+
+AM_CONDITIONAL(XML_DOCS_ENABLED, test x$enable_xml_docs = xyes)
+AC_MSG_RESULT(yes)
+
+AC_OUTPUT([
+Makefile
+])
+
+dnl ==========================================================================
+echo "
+ Autostart specification $VERSION
+ ==============
+
+ prefix: ${prefix}
+ source code location: ${srcdir}
+ xmlto: ${XMLTO}"
+
+echo "
+ Maintainer mode: ${USE_MAINTAINER_MODE}
+ Building XML docs: ${enable_xml_docs}
+"
diff --git a/basedir/.gitignore b/basedir/.gitignore
new file mode 100644
index 0000000..126e056
--- /dev/null
+++ b/basedir/.gitignore
@@ -0,0 +1 @@
+basedir-spec.html
diff --git a/basedir/Makefile b/basedir/Makefile
new file mode 100644
index 0000000..35a73df
--- /dev/null
+++ b/basedir/Makefile
@@ -0,0 +1,7 @@
+all: basedir-spec.html
+
+%.html: %.xml
+ xmlto html-nochunks $<
+
+clean:
+ rm -f basedir-spec.html
diff --git a/basedir/basedir-spec.xml b/basedir/basedir-spec.xml
new file mode 100644
index 0000000..03a982b
--- /dev/null
+++ b/basedir/basedir-spec.xml
@@ -0,0 +1,298 @@
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+ ]>
+<article id="index">
+ <articleinfo>
+ <title>XDG Base Directory Specification</title>
+ <releaseinfo>Version 0.7</releaseinfo>
+ <date>24th November 2010</date>
+ <authorgroup>
+ <author>
+ <firstname>Waldo</firstname>
+ <surname>Bastian</surname>
+ <affiliation>
+ <address>
+ <email>bastian@kde.org</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Ryan</firstname>
+ <surname>Lortie</surname>
+ <affiliation>
+ <address>
+ <email>desrt@desrt.ca</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Lennart</firstname>
+ <surname>Poettering</surname>
+ <affiliation>
+ <address>
+ <email>lennart@poettering.net</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+ </articleinfo>
+
+ <sect1 id="introduction">
+ <title>Introduction</title>
+ <para>
+ Various specifications specify files and file formats. This
+ specification defines where these files should be looked for by
+ defining one or more base directories relative to which files
+ should be located.
+ </para>
+ </sect1>
+
+ <sect1 id="basics">
+ <title>Basics</title>
+ <para>
+ The XDG Base Directory Specification is based on the following concepts:
+ <itemizedlist>
+ <listitem>
+ <para>
+ There is a single base directory relative to which user-specific
+ data files should be written. This directory is defined by the
+ environment variable <literal>$XDG_DATA_HOME</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ There is a single base directory relative to which user-specific
+ configuration files should be written. This directory is defined by the
+ environment variable <literal>$XDG_CONFIG_HOME</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ There is a set of preference ordered base directories relative to
+ which data files should be searched. This set of directories is defined
+ by the environment variable <literal>$XDG_DATA_DIRS</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ There is a set of preference ordered base directories relative to
+ which configuration files should be searched.
+ This set of directories is defined
+ by the environment variable <literal>$XDG_CONFIG_DIRS</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ There is a single base directory relative to which user-specific
+ non-essential (cached) data should be written.
+ This directory is defined by the
+ environment variable <literal>$XDG_CACHE_HOME</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ There is a single base directory relative to which
+ user-specific runtime files and other file objects should
+ be placed. This directory is defined by the environment
+ variable <literal>$XDG_RUNTIME_DIR</literal>.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+
+ <para>All paths set in these environment variables must be
+ absolute. If an implementation encounters a relative path in any
+ of these variables it should consider the path invalid and ignore
+ it.</para>
+ </sect1>
+
+
+ <sect1 id="variables">
+ <title>Environment variables</title>
+ <para>
+ <literal>$XDG_DATA_HOME</literal> defines the base directory relative to
+ which user specific data files should be stored. If
+ <literal>$XDG_DATA_HOME</literal> is either not set or empty, a default equal to
+ <literal>$HOME</literal>/.local/share should be used.
+ </para>
+ <para>
+ <literal>$XDG_CONFIG_HOME</literal> defines the base directory relative to
+ which user specific configuration files should be stored. If
+ <literal>$XDG_CONFIG_HOME</literal> is either not set or empty, a default equal to
+ <literal>$HOME</literal>/.config should be used.
+ </para>
+ <para>
+ <literal>$XDG_DATA_DIRS</literal> defines the preference-ordered set of
+ base directories to search for data files in addition to the
+ <literal>$XDG_DATA_HOME</literal> base directory.
+ The directories in <literal>$XDG_DATA_DIRS</literal> should be seperated
+ with a colon ':'.
+ </para>
+ <para>
+ If <literal>$XDG_DATA_DIRS</literal> is either not set or empty, a value equal to
+ /usr/local/share/:/usr/share/ should be used.
+ </para>
+ <para>
+ <literal>$XDG_CONFIG_DIRS</literal> defines the preference-ordered set of
+ base directories to search for configuration files in addition to the
+ <literal>$XDG_CONFIG_HOME</literal> base directory.
+ The directories in <literal>$XDG_CONFIG_DIRS</literal> should be seperated
+ with a colon ':'.
+ </para>
+ <para>
+ If <literal>$XDG_CONFIG_DIRS</literal> is either not set or empty, a value equal to
+ /etc/xdg should be used.
+ </para>
+ <para>
+ The order of base directories denotes their importance; the first
+ directory listed is the most important. When the same information is
+ defined in multiple places the information defined relative to the more
+ important base directory takes precedent. The base directory defined
+ by <literal>$XDG_DATA_HOME</literal> is considered more important than
+ any of the base directories defined by <literal>$XDG_DATA_DIRS</literal>.
+ The base directory defined
+ by <literal>$XDG_CONFIG_HOME</literal> is considered more important than
+ any of the base directories defined by <literal>$XDG_CONFIG_DIRS</literal>.
+ </para>
+ <para>
+ <literal>$XDG_CACHE_HOME</literal> defines the base directory relative to
+ which user specific non-essential data files should be stored. If
+ <literal>$XDG_CACHE_HOME</literal> is either not set or empty, a default equal to
+ <literal>$HOME</literal>/.cache should be used.
+ </para>
+
+ <para>
+ <literal>$XDG_RUNTIME_DIR</literal> defines the base directory
+ relative to which user-specific non-essential runtime files and
+ other file objects (such as sockets, named pipes, ...) should be
+ stored. The directory MUST be owned by the user, and he MUST be
+ the only one having read and write access to it. Its Unix access
+ mode MUST be 0700.
+ </para>
+ <para>
+ The lifetime of the directory MUST be bound to the user being
+ logged in. It MUST be created when the user first logs in and if
+ the user fully logs out the directory MUST be removed. If the
+ user logs in more than once he should get pointed to the same
+ directory, and it is mandatory that the directory continues to
+ exist from his first login to his last logout on the system, and
+ not removed in between. Files in the directory MUST not survive
+ reboot or a full logout/login cycle.
+ </para>
+ <para>
+ The directory MUST be on a local file system and not shared with
+ any other system. The directory MUST by fully-featured by the
+ standards of the operating system. More specifically, on
+ Unix-like operating systems AF_UNIX sockets, symbolic links,
+ hard links, proper permissions, file locking, sparse files,
+ memory mapping, file change notifications, a reliable hard link
+ count must be supported, and no restrictions on the file name
+ character set should be imposed. Files in this directory MAY be
+ subjected to periodic clean-up. To ensure that your files are
+ not removed, they should have their access time timestamp
+ modified at least once every 6 hours of monotonic time or the
+ 'sticky' bit should be set on the file.
+ </para>
+ <para>
+ If <literal>$XDG_RUNTIME_DIR</literal> is not set applications
+ should fall back to a replacement directory with similar
+ capabilities and print a warning message. Applications should
+ use this directory for communication and synchronization
+ purposes and should not place larger files in it, since it might
+ reside in runtime memory and cannot necessarily be swapped out
+ to disk.
+ </para>
+ </sect1>
+
+ <sect1 id="referencing">
+ <title>Referencing this specification</title>
+ <para>
+ Other specifications may reference this specification by specifying the
+ location of a data file as
+ <literal>$XDG_DATA_DIRS</literal>/subdir/filename. This implies that:
+ <itemizedlist>
+ <listitem>
+ <para>
+ Such file should be installed to <literal>$datadir</literal>/subdir/filename
+ with <literal>$datadir</literal> defaulting to /usr/share.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ A user specific version of the data file may be created in
+ <literal>$XDG_DATA_HOME</literal>/subdir/filename, taking into
+ account the default value for <literal>$XDG_DATA_HOME</literal> if
+ <literal>$XDG_DATA_HOME</literal> is not set.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Lookups of the data file should search for ./subdir/filename relative to
+ all base directories specified by <literal>$XDG_DATA_HOME</literal> and
+ <literal>$XDG_DATA_DIRS</literal> . If an environment
+ variable is either not set or empty, its default value as defined by this specification
+ should be used instead.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ Specifications may reference this specification by specifying the
+ location of a configuration file as
+ <literal>$XDG_CONFIG_DIRS</literal>/subdir/filename. This implies that:
+ <itemizedlist>
+ <listitem>
+ <para>
+ Default configuration files should be installed to <literal>$sysconfdir</literal>/xdg/subdir/filename
+ with <literal>$sysconfdir</literal> defaulting to /etc.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ A user specific version of the configuration file may be created in
+ <literal>$XDG_CONFIG_HOME</literal>/subdir/filename, taking into
+ account the default value for <literal>$XDG_CONFIG_HOME</literal> if
+ <literal>$XDG_CONFIG_HOME</literal> is not set.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Lookups of the configuration file should search for ./subdir/filename relative to
+ all base directories indicated by <literal>$XDG_CONFIG_HOME</literal> and
+ <literal>$XDG_CONFIG_DIRS</literal> . If an environment
+ variable is either not set or empty, its default value as defined by this specification
+ should be used instead.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ If, when attempting to write a file, the destination
+ directory is non-existant an attempt should be made to create it
+ with permission <literal>0700</literal>. If the destination directory
+ exists already the permissions should not be changed.
+ The application should be prepared to handle the case where the file
+ could not be written, either because the directory was non-existant
+ and could not be created, or for any other reason. In such case it
+ may chose to present an error message to the user.
+ </para>
+ <para>
+ When attempting to read a file, if for any reason a file in a certain
+ directory is unaccessible, e.g. because the directory is non-existant,
+ the file is non-existant or the user is not authorized to open the file,
+ then the processing of the file in that directory should be skipped.
+ If due to this a required file could not be found at all, the
+ application may chose to present an error message to the user.
+ </para>
+ <para>
+ A specification that refers to <literal>$XDG_DATA_DIRS</literal> or
+ <literal>$XDG_CONFIG_DIRS</literal> should define what the behaviour
+ must be when a file is located under multiple base directories.
+ It could, for example, define that only the file under the most
+ important base directory should be used or, as another example,
+ it could define rules for merging the information from the different
+ files.
+ </para>
+ </sect1>
+
+</article>
diff --git a/desktop-entry/.cvsignore b/desktop-entry/.cvsignore
new file mode 100644
index 0000000..e2bf8b5
--- /dev/null
+++ b/desktop-entry/.cvsignore
@@ -0,0 +1,2 @@
+*.ps
+*.html
diff --git a/desktop-entry/ChangeLog b/desktop-entry/ChangeLog
new file mode 100644
index 0000000..7113101
--- /dev/null
+++ b/desktop-entry/ChangeLog
@@ -0,0 +1,182 @@
+2008-09-12 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: reword sentence about characters allowed in
+ key names to avoid some ambiguity.
+ Patch by Simon McVittie <simon.mcvittie@collabora.co.uk>
+
+2008-03-04 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: bump version to 1.1-draft, so we can publish
+ it. See http://bugs.freedesktop.org/show_bug.cgi?id=14097
+
+2007-09-10 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: fix Icon key in example: it was using the
+ .png extension for an icon name. Which is wrong :-)
+
+2007-07-27 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: don't say that the files are encoded as
+ lines of 8-bit characters, which is wrong for UTF-8. Use something a
+ bit better proposed by Matthias Clasen <mclasen@redhat.com>
+ Bug #11417.
+
+2007-05-30 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: s/DESKTOP_LAUNCH_ID/DESKTOP_STARTUP_ID/
+ Thanks to Dan Winship <danw@gnome.org> for catching this.
+ Bug #11081.
+
+2007-04-28 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: scanf behavior is locale-dependent, and the
+ numeric type shouldn't be. Specify that the value is a floating point
+ number as recognized by scanf in the C locale.
+ Thanks to Dan Winship <danw@gnome.org> for catching this.
+ http://lists.freedesktop.org/archives/xdg/2007-April/009732.html
+
+2007-04-28 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: clarify description for the TryExec key.
+ http://lists.freedesktop.org/archives/xdg/2007-March/009556.html
+
+2007-04-28 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: change "the $PATH" to "the $PATH environment
+ variable".
+ http://lists.freedesktop.org/archives/xdg/2007-March/009552.html
+
+2007-04-28 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: improve the description of the Comment key.
+ http://lists.freedesktop.org/archives/xdg/2007-March/009440.html
+
+2007-04-28 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: a desktop file of type Application should
+ always have an Exec key. Same for Link Type and URL key.
+ http://lists.freedesktop.org/archives/xdg/2007-March/009437.html
+
+2007-04-28 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: desktop files of type Directory have a
+ .directory extension right now (at least in GNOME), but according to
+ the spec, this is wrong. The patch fixes this. Also, it removes a
+ small part of text explaining the name of a desktop file describing a
+ directory.
+ http://lists.freedesktop.org/archives/xdg/2007-March/009437.html
+
+2007-04-28 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml:clarify a bit the text about keys that only
+ makes sense in a specific context. Also, state that the keys should
+ not be used outside of this context. For example, the URL key should
+ not be present if the type is not Link.
+ http://lists.freedesktop.org/archives/xdg/2007-March/009437.html
+
+2007-04-28 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: small typo
+ http://lists.freedesktop.org/archives/xdg/2007-March/009437.html
+
+2007-04-28 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: this changes the type of the version from
+ numeric to string. It's needed if we consider the possibility that
+ there are some desktop entry files out there using 0.9.x as a value
+ for this key. The change doesn't break anything.
+ http://lists.freedesktop.org/archives/xdg/2007-March/009437.html
+
+2007-04-28 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: UnmountIcon is a localestring key.
+ http://lists.freedesktop.org/archives/xdg/2007-March/009437.html
+
+2007-04-28 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: clarify that lists should always end with a
+ semicolon, even if there's only one value.
+ http://lists.freedesktop.org/archives/xdg/2007-March/009437.html
+
+2007-02-06 Waldo Bastian <waldo.bastian@intel.com>
+ * Clearify that %U may result in either local paths or file: URLs
+ * Deprecate %d, %n, %D and %N. They aren't usable on Gnome and
+ you can do the same with dirname and basename in a script.
+ * Clearify that %f, %u, %F or %U should only be used once.
+ * Clearify that these codes should be ignored if there is no file
+ to open.
+ * Forbid the use of field codes in quoted arguments.
+ * Clearify that %F and %U should only be used as a stand alone argument.
+ * Bumped version to 1.0
+
+2006-10-30 Waldo Bastian <waldo.bastian@intel.com>
+ * Remove equal sign from reserved characters in Exec args
+ * Add backslash to list of reserved characters in Exec args
+
+2006-10-04 Waldo Bastian <waldo.bastian@intel.com>
+ * Deprecate %v, it is FSDevice specific
+
+2006-08-30 Waldo Bastian <waldo.bastian@intel.com>
+ * list FSDevice type as KDE specific
+ * refer to Icon Theme Specification
+
+2006-08-22 Waldo Bastian <waldo.bastian@intel.com>
+ * clarification of Exec key
+ * correction of StartupWMClass, clarify StartupNotify (Lubos Lunak)
+ * deprecate SortOrder and FilePattern key (Vincent Untz)
+ * remove section on "MIME Type caching" from spec (Vincent Untz)
+ * groups and keys must be unique (Vincent Untz)
+ See http://lists.freedesktop.org/archives/xdg/2006-August/008446.html
+
+2006-05-28 Vincent Untz <vuntz@gnome.org>
+ * cleanup of text and some reorg for clearity
+ * move all of the legacy-mixed encoding stuff to the appendix
+ * deprecate the Swallow* keys
+ * improve definition of group headers
+ * reorder the list of standard keys so they are sorted by type
+ See http://lists.freedesktop.org/archives/xdg/2006-May/008131.html
+
+Tue Jul 13 18:04:11 2004 Jonathan Blandford <jrb@gnome.org>
+
+ * desktop-entry-spec.xml: Update the MIME description to make it
+ more relevent.
+
+2004-04-19 Mark McLoughlin <mark@skynet.ie>
+
+ Pointed out by Ville Skyttä <ville.skytta@iki.fi>
+
+ * desktop-entry-spec.xml: fix minor typo.
+
+2004-04-18 Mark McLoughlin <mark@skynet.ie>
+
+ Patch from Ville Skyttä <ville.skytta@iki.fi>
+
+ * desktop-entry-spec.xml: editorial changes, fixes for
+ typos and general cleanups.
+
+Mon Jul 7 18:26:14 2003 Owen Taylor <otaylor@redhat.com>
+
+ * destop-entry-spec.sgml: Merge changes by Havoc
+ Pennington that had been made in web module:
+
+ 2002/04/20: add changes posted to xdg-list a while back
+
+Sat Aug 24 17:00:48 2002 Owen Taylor <otaylor@redhat.com>
+
+ * desktop-entry-spec.sgml: In legacy-mixed table, fix duplicate
+ pt entry, language code for Armenian. Make explicit that
+ language-country locale tags can match a single language in
+ the table. (Pablo Saratxaga)
+
+Tue Mar 13 12:37:03 2001 Owen Taylor <otaylor@redhat.com>
+
+ * 0.9.3
+
+ * Reworked the description of encoding handling for
+ Legacy-Mixed for clarity and added a bit more details
+ on how encoding-matching should be done.
+
+Thu Mar 8 17:42:05 2001 Owen Taylor <otaylor@redhat.com>
+
+ * Import into CVS.
+
diff --git a/desktop-entry/desktop-entry-spec.xml b/desktop-entry/desktop-entry-spec.xml
new file mode 100644
index 0000000..7d99d0a
--- /dev/null
+++ b/desktop-entry/desktop-entry-spec.xml
@@ -0,0 +1,1392 @@
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+ ]>
+<article id="index">
+ <articleinfo>
+ <title>Desktop Entry Specification</title>
+ <releaseinfo>Version 1.1-draft</releaseinfo>
+ <date>4 Mar 2008</date>
+ <authorgroup>
+ <author>
+ <firstname>Preston</firstname>
+ <surname>Brown</surname>
+ <affiliation>
+ <address>
+ <email>pbrown@kde.org</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Jonathan</firstname>
+ <surname>Blandford</surname>
+ <affiliation>
+ <address>
+ <email>jrb@redhat.com</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Owen</firstname>
+ <surname>Taylor</surname>
+ <affiliation>
+ <address>
+ <email>otaylor@gtk.org</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Vincent</firstname>
+ <surname>Untz</surname>
+ <affiliation>
+ <address>
+ <email>vuntz@gnome.org</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Waldo</firstname>
+ <surname>Bastian</surname>
+ <affiliation>
+ <address>
+ <email>waldo.bastian@intel.com</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+ </articleinfo>
+
+ <sect1 id="introduction">
+ <title>Introduction</title>
+ <para>
+ Both the KDE and GNOME desktop environments have adopted a similar
+ format for "desktop entries", or configuration files describing how a
+ particular program is to be launched, how it appears in menus, etc.
+ It is to the larger community's benefit that a unified standard be
+ agreed upon by all parties such that interoperation between the two
+ environments, and indeed any additional environments that implement
+ the specification, becomes simpler.
+ </para>
+ </sect1>
+ <sect1 id="basic-format">
+ <title>Basic format of the file</title>
+ <para>
+ Desktop entry files should have the <filename>.desktop</filename>
+ extension, except for files of <varname>Type</varname>
+ <constant>Directory</constant> which should have the
+ <filename>.directory</filename> extension. Determining file type on basis
+ of extension makes determining the file type very easy and quick.
+ When no file extension is present, the desktop system should
+ fall back to recognition via "magic detection".
+ </para>
+ <para>
+ Desktop entry files are encoded in UTF-8. A file is interpreted as a
+ series of lines that are separated by linefeed characters. Case is
+ significant everywhere in the file.
+ </para>
+ <para>
+ Compliant implementations MUST not remove any fields from the file,
+ even if they don't support them. Such fields must be maintained in a
+ list somewhere, and if the file is "rewritten", they will be included.
+ This ensures that any desktop-specific extensions will be preserved
+ even if another system accesses and changes the file.
+ </para>
+ <sect2 id="comments">
+ <title>Comments</title>
+ <para>
+ Lines beginning with a <literal>#</literal> and blank lines are
+ considered comments and will be ignored, however they should be
+ preserved across reads and writes of the desktop entry file.
+ </para>
+ <para>
+ Comment lines are uninterpreted and may contain any character
+ (except for LF). However, using UTF-8 for comment lines that
+ contain characters not in ASCII is encouraged.
+ </para>
+ </sect2>
+ <sect2 id="group-header">
+ <title>Group headers</title>
+ <para>
+ A group header with name <literal>groupname</literal> is a line in the
+ format:
+ </para>
+ <programlisting>[groupname]</programlisting>
+ <para>
+ Group names may contain all ASCII characters except for
+ <literal>[</literal> and <literal>]</literal> and control characters.
+ </para>
+ <para>
+ Multiple groups may not have the same name.
+ </para>
+ <para>
+ All <literal>{key,value}</literal> pairs following a group header until
+ a new group header belong to the group.
+ </para>
+ <para>
+ The basic format of the desktop entry file requires that there be
+ a group header named <literal>Desktop Entry</literal>. There may
+ be other groups present in the file, but this is the most
+ important group which explicitly needs to be supported. This
+ group should also be used as the "magic key" for automatic MIME
+ type detection. There should be nothing preceding this group in
+ the desktop entry file but possibly one or more comments.
+ </para>
+ </sect2>
+ <sect2 id="entries">
+ <title>Entries</title>
+ <para>
+ Entries in the file are <literal>{key,value}</literal> pairs in the
+ format:
+ </para>
+ <programlisting>Key=Value</programlisting>
+ <para>
+ Space before and after the equals sign should be ignored; the
+ <literal>=</literal> sign is the actual delimiter.
+ </para>
+ <para>
+ Only the characters <literal>A-Za-z0-9-</literal> may be used in
+ key names.
+ </para>
+ <para>
+ As the case is significant, the keys <varname>Name</varname> and
+ <varname>NAME</varname> are not equivalent.
+ </para>
+ <para>
+ Multiple keys in the same group may not have the same name. Keys in
+ different groups may have the same name.
+ </para>
+ </sect2>
+ </sect1>
+ <sect1 id="value-types">
+ <title>Possible value types</title>
+ <para>
+ The value types recognized are <literal>string</literal>,
+ <literal>localestring</literal>,
+ <literal>boolean</literal>, and
+ <literal>numeric</literal>.
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ Values of type <literal>string</literal> may contain all ASCII
+ characters except for control characters.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Values of type <literal>localestring</literal> are user displayable,
+ and are encoded in UTF-8.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Values of type <literal>boolean</literal> must either be the string
+ <literal>true</literal> or <literal>false</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Values of type <literal>numeric</literal> must be a valid floating
+ point number as recognized by the <literal>%f</literal> specifier for
+ <function>scanf</function> in the <literal>C</literal> locale.
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ The escape sequences <literal>\s</literal>, <literal>\n</literal>,
+ <literal>\t</literal>, <literal>\r</literal>, and
+ <literal>\\</literal> are supported for values of type
+ <literal>string</literal> and <literal>localestring</literal>, meaning
+ ASCII space, newline, tab, carriage return, and backslash, respectively.
+ </para>
+ <para>
+ Some keys can have multiple values. In such a case, the value of the key
+ is specified as a plural: for example, <literal>string(s)</literal>. The
+ multiple values should be separated by a semicolon, and the value of the
+ key should have a semicolon as trailing character. Semicolons in these
+ values need to be escaped using <literal>\;</literal>.
+ </para>
+ </sect1>
+ <sect1 id="localized-keys">
+ <title>Localized values for keys</title>
+ <para>
+ Keys with type <literal>localestring</literal> may be postfixed by
+ [<replaceable>LOCALE</replaceable>],
+ where <replaceable>LOCALE</replaceable> is the locale type of the
+ entry. <replaceable>LOCALE</replaceable> must be of the form
+ <literal><replaceable>lang</replaceable>_<replaceable>COUNTRY</replaceable>.<replaceable>ENCODING</replaceable>@<replaceable>MODIFIER</replaceable></literal>,
+ where
+ <literal>_<replaceable>COUNTRY</replaceable></literal>,
+ <literal>.<replaceable>ENCODING</replaceable></literal>,
+ and <literal>@<replaceable>MODIFIER</replaceable></literal>
+ may be omitted. If a postfixed key occurs, the same
+ key must be also present without the postfix.
+ </para>
+ <para>
+ When reading in the desktop entry file, the value of the key is
+ selected by matching the current POSIX locale for the
+ <varname>LC_MESSAGES</varname> category against the
+ <replaceable>LOCALE</replaceable> postfixes of all occurrences
+ of the key, with the
+ <literal>.<replaceable>ENCODING</replaceable></literal> part
+ stripped.
+ </para>
+ <para>
+ The matching is done as follows. If
+ <varname>LC_MESSAGES</varname> is of the form
+ <literal><replaceable>lang</replaceable>_<replaceable>COUNTRY</replaceable>.<replaceable>ENCODING</replaceable>@<replaceable>MODIFIER</replaceable></literal>,
+ then it will match a key of the form
+ <literal><replaceable>lang</replaceable>_<replaceable>COUNTRY</replaceable>@<replaceable>MODIFIER</replaceable></literal>.
+ If such a key does not exist, it will attempt to match
+ <literal><replaceable>lang</replaceable>_<replaceable>COUNTRY</replaceable></literal>
+ followed by
+ <literal><replaceable>lang</replaceable>@<replaceable>MODIFIER</replaceable></literal>.
+ Then, a match against <replaceable>lang</replaceable> by itself
+ will be attempted. Finally, if no matching key is found the
+ required key without a locale specified is used. The encoding
+ from the <varname>LC_MESSAGES</varname> value is ignored
+ when matching.
+ </para>
+ <para>
+ If <varname>LC_MESSAGES</varname> does not have a <replaceable>MODIFIER</replaceable>
+ field, then no key with a modifier will be matched. Similarly, if
+ <varname>LC_MESSAGES</varname> does not have a <replaceable>COUNTRY</replaceable>
+ field, then no key with a country specified will be matched. If
+ <varname>LC_MESSAGES</varname> just has a <replaceable>lang</replaceable> field, then
+ it will do a straight match to a key with a similar value. The
+ following table lists possible matches of various <varname>LC_MESSAGES</varname> values in
+ the order in which they are matched. Note that the
+ <replaceable>ENCODING</replaceable> field isn't shown.
+ </para>
+ <table>
+ <title>Locale Matching</title>
+ <tgroup cols="2">
+<thead>
+ <row>
+ <entry><varname>LC_MESSAGES</varname> value</entry>
+ <entry>Possible keys in order of matching</entry>
+ </row>
+</thead>
+<tbody>
+ <row>
+ <entry><literal><replaceable>lang</replaceable>_<replaceable>COUNTRY</replaceable>@<replaceable>MODIFIER</replaceable></literal></entry>
+ <entry>
+ <literal><replaceable>lang</replaceable>_<replaceable>COUNTRY</replaceable>@<replaceable>MODIFIER</replaceable></literal>,
+ <literal><replaceable>lang</replaceable>_<replaceable>COUNTRY</replaceable></literal>,
+ <literal><replaceable>lang</replaceable>@<replaceable>MODIFIER</replaceable></literal>,
+ <literal><replaceable>lang</replaceable></literal>,
+ default value
+ </entry>
+ </row>
+ <row>
+ <entry><literal><replaceable>lang</replaceable>_<replaceable>COUNTRY</replaceable></literal></entry>
+ <entry>
+ <literal><replaceable>lang</replaceable>_<replaceable>COUNTRY</replaceable></literal>,
+ <replaceable>lang</replaceable>,
+ default value
+ </entry>
+ </row>
+ <row>
+ <entry><literal><replaceable>lang</replaceable>@<replaceable>MODIFIER</replaceable></literal></entry>
+ <entry>
+ <literal><replaceable>lang</replaceable>@<replaceable>MODIFIER</replaceable></literal>,
+ <replaceable>lang</replaceable>,
+ default value
+ </entry>
+ </row>
+ <row>
+ <entry><replaceable>lang</replaceable></entry>
+ <entry>
+ <replaceable>lang</replaceable>,
+ default value
+ </entry>
+ </row>
+</tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ For example, if the current value of the <varname>LC_MESSAGES</varname> category
+ is <literal>sr_YU@Latn</literal> and the desktop file includes:
+ </para>
+ <programlisting>
+ Name=Foo
+ Name[sr_YU]=...
+ Name[sr@Latn]=...
+ Name[sr]=...</programlisting>
+ <para>
+ then the value of the <varname>Name</varname> keyed by <literal>sr_YU</literal> is used.
+ </para>
+ </sect1>
+ <sect1 id="recognized-keys">
+ <title>Recognized desktop entry keys</title>
+ <para>
+ Keys are either OPTIONAL or REQUIRED. If a key is OPTIONAL it may or
+ may not be present in the file. However, if it isn't, the
+ implementation of the standard should not blow up, it must provide
+ some sane defaults.
+ </para>
+ <para>
+ Some keys only make sense in the context when another particular key
+ is also present and set to a specific value. Those keys should not be
+ used if the particular key is not present or not set to the specific
+ value. For example, the <varname>Terminal</varname> key can only be used
+ when the value of the <varname>Type</varname> key is
+ <constant>Application</constant>.
+ </para>
+ <para>
+ If a REQUIRED key is only valid in the context of another key set to a
+ specific value, then it has to be present only if the other key is set to
+ the specific value. For example, the <varname>URL</varname> key has to be
+ present when and only when when the value of the <varname>Type</varname>
+ key is <constant>Link</constant>.
+ </para>
+ <para>
+ Some example keys: <varname>Name[C]</varname>, <varname>Comment[it]</varname>.
+ </para>
+ <table>
+ <title>Standard Keys</title>
+ <tgroup cols="5">
+ <thead>
+ <row>
+ <entry>Key</entry>
+ <entry>Description</entry>
+ <entry>Value Type</entry>
+ <entry>REQ?</entry>
+ <entry>Type</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry id="key-type"><varname>Type</varname></entry>
+ <entry>
+ This specification defines 3 types of desktop entries:
+ <constant>Application</constant> (type 1),
+ <constant>Link</constant> (type 2)
+ and <constant>Directory</constant> (type 3).
+ To allow the addition of new types in the future,
+ implementations should ignore desktop entries with an
+ unknown type.
+ </entry>
+ <entry>string</entry>
+ <entry>YES</entry>
+ </row>
+ <row>
+ <entry id="key-version"><varname>Version</varname></entry>
+ <entry>
+ Version of the Desktop Entry Specification that the
+ desktop entry conforms with. Entries that confirm with this
+ version of the specification should use <literal>1.0</literal>.
+ Note that the version field is not required to be present.
+ </entry>
+ <entry>string</entry>
+ <entry>NO</entry>
+ <entry>1-3</entry>
+ </row>
+ <row>
+ <entry id="key-name"><varname>Name</varname></entry>
+ <entry>
+ Specific name of the application, for example "Mozilla".
+ </entry>
+ <entry>localestring</entry>
+ <entry>YES</entry>
+ <entry>1-3</entry>
+ </row>
+ <row>
+ <entry id="key-genericname"><varname>GenericName</varname></entry>
+ <entry>
+ Generic name of the application, for example "Web Browser".
+ </entry>
+ <entry>localestring</entry>
+ <entry>NO</entry>
+ <entry>1-3</entry>
+ </row>
+ <row>
+ <entry id="key-nodisplay"><varname>NoDisplay</varname></entry>
+ <entry>
+ <varname>NoDisplay</varname> means "this application exists, but don't display it in the menus".
+ This can be useful to e.g. associate this application with MIME types, so that
+ it gets launched from a file manager (or other apps), without having a menu
+ entry for it (there are tons of good reasons for this, including e.g. the
+ <literal>netscape -remote</literal>, or <literal>kfmclient openURL</literal> kind of stuff).
+ </entry>
+ <entry>boolean</entry>
+ <entry>NO</entry>
+ <entry>1-3</entry>
+ </row>
+ <row>
+ <entry id="key-comment"><varname>Comment</varname></entry>
+ <entry>
+ Tooltip for the entry, for example "View sites on the
+ Internet". The value should not be redundant with the values of
+ <varname>Name</varname> and <varname>GenericName</varname>.
+ </entry>
+ <entry>localestring</entry>
+ <entry>NO</entry>
+ <entry>1-3</entry>
+ </row>
+ <row>
+ <entry id="key-icon"><varname>Icon</varname></entry>
+ <entry>
+ Icon to display in file manager, menus, etc. If the
+ name is an absolute path, the given file will be
+ used. If the name is not an absolute path, the algorithm described
+ in the <ulink
+ url="http://freedesktop.org/wiki/Standards/icon-theme-spec">Icon
+ Theme Specification</ulink> will be used to locate the icon.
+ </entry>
+ <entry>localestring</entry>
+ <entry>NO</entry>
+ <entry>1-3</entry>
+ </row>
+ <row>
+ <entry id="key-hidden"><varname>Hidden</varname></entry>
+ <entry>
+ <varname>Hidden</varname> should have been called <varname>Deleted</varname>.
+ It means the user deleted (at his level)
+ something that was present (at an upper level, e.g. in the system dirs). It's
+ strictly equivalent to the <filename>.desktop</filename> file not existing at all, as far as that user is
+ concerned. This can also be used to "uninstall" existing files (e.g. due to a renaming)
+ - by letting <literal>make install</literal> install a file with <literal>Hidden=true</literal> in it.
+ </entry>
+ <entry>boolean</entry>
+ <entry>NO</entry>
+ <entry>1-3</entry>
+ </row>
+ <row>
+ <entry id="key-onlyshowin"><varname>OnlyShowIn</varname>, <varname>NotShowIn</varname></entry>
+ <entry>
+ A list of strings identifying the environments that should
+ display/not display a given desktop entry. Only one of
+ these keys, either <varname>OnlyShowIn</varname> or
+ <varname>NotShowIn</varname>, may appear in a group (for
+ possible values see the <ulink
+ url="http://www.freedesktop.org/Standards/menu-spec">Desktop
+ Menu Specification</ulink>).
+ </entry>
+ <entry>string(s)</entry>
+ <entry>NO</entry>
+ <entry>1-3</entry>
+ </row>
+ <row>
+ <entry id="key-tryexec"><varname>TryExec</varname></entry>
+ <entry>
+ Path to an executable file on disk used to determine if the program
+ is actually installed. If the path is not an absolute path, the file
+ is looked up in the $PATH environment variable. If the file is not
+ present or if it is not executable, the entry may be ignored (not be
+ used in menus, for example).
+ </entry>
+ <entry>string</entry>
+ <entry>NO</entry>
+ <entry>1</entry>
+ </row>
+ <row>
+ <entry id="key-exec"><varname>Exec</varname></entry>
+ <entry>
+ Program to execute, possibly with arguments. See the <link
+ linkend="exec-variables"><varname>Exec</varname> key</link> for
+ details on how this key works.
+ </entry>
+ <entry>string</entry>
+ <entry>YES</entry>
+ <entry>1</entry>
+ </row>
+ <row>
+ <entry id="key-path"><varname>Path</varname></entry>
+ <entry>
+ If entry is of type <constant>Application</constant>, the working directory to run the program in.
+ </entry>
+ <entry>string</entry>
+ <entry>NO</entry>
+ <entry>1</entry>
+ </row>
+ <row>
+ <entry id="key-terminal"><varname>Terminal</varname></entry>
+ <entry>
+ Whether the program runs in a terminal window.
+ </entry>
+ <entry>boolean</entry>
+ <entry>NO</entry>
+ <entry>1</entry>
+ </row>
+ <row>
+ <entry id="key-actions"><varname>Actions</varname></entry>
+ <entry>
+ Identifiers for application actions. This can be used to tell the
+ application to make a specific action, different from the default
+ behavior. The <link linkend="extra-actions">Application actions</link>
+ section describes how actions work.
+ </entry>
+ <entry>string(s)</entry>
+ <entry>NO</entry>
+ <entry>1</entry>
+ </row>
+ <row>
+ <entry id="key-mimetype"><varname>MimeType</varname></entry>
+ <entry>
+ The MIME type(s) supported by this application.
+ </entry>
+ <entry>string(s)</entry>
+ <entry>NO</entry>
+ <entry>1</entry>
+ </row>
+ <row>
+ <entry id="key-categories"><varname>Categories</varname></entry>
+ <entry>
+ Categories in which the entry should be shown in a menu (for
+ possible values see the <ulink
+ url="http://www.freedesktop.org/Standards/menu-spec">Desktop
+ Menu Specification</ulink>).
+ </entry>
+ <entry>string(s)</entry>
+ <entry>NO</entry>
+ <entry>1</entry>
+ </row>
+ <row>
+ <entry id="key-keywords"><varname>Keywords</varname></entry>
+ <entry>
+ A list of strings which may be used in addition to other
+ metadata to describe this entry. This can be useful e.g. to
+ facilitate searching through entries. The values are not meant
+ for display, and should not be redundant with the values of
+ <varname>Name</varname> or <varname>GenericName</varname>.
+ </entry>
+ <entry>localestring(s)</entry>
+ <entry>NO</entry>
+ <entry>1</entry>
+ </row>
+ <row>
+ <entry id="key-startupnotify"><varname>StartupNotify</varname></entry>
+ <entry>
+ If true, it is KNOWN that the application will send a "remove"
+ message when started with the DESKTOP_STARTUP_ID environment variable set.
+ If false, it is KNOWN that the application does not work
+ with startup notification at all (does not shown any window, breaks
+ even when using StartupWMClass, etc.).
+ If absent, a reasonable handling is up to implementations (assuming false,
+ using StartupWMClass, etc.). (See the <ulink url="http://www.freedesktop.org/Standards/startup-notification-spec">Startup Notification Protocol Specification</ulink> for more details).
+ </entry>
+ <entry>boolean</entry>
+ <entry>NO</entry>
+ <entry>1</entry>
+ </row>
+ <row>
+ <entry id="key-startupwmclass"><varname>StartupWMClass</varname></entry>
+ <entry>
+ If specified, it is known that the application will map at least one
+ window with the given string as its WM class or WM name hint (see the <ulink url="http://www.freedesktop.org/Standards/startup-notification-spec">Startup Notification Protocol Specification</ulink> for more details).
+ </entry>
+ <entry>string</entry>
+ <entry>NO</entry>
+ <entry>1</entry>
+ </row>
+ <row>
+ <entry id="key-url"><varname>URL</varname></entry>
+ <entry>
+ If entry is Link type, the URL to access.
+ </entry>
+ <entry>string</entry>
+ <entry>YES</entry>
+ <entry>2</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+ <sect1 id="exec-variables">
+ <title>The <varname>Exec</varname> key</title>
+ <para>
+ The <varname>Exec</varname> key must contain a command line.
+ A command line consists of an executable program optionally followed
+ by one or more arguments.
+ The executable program can either be specified with its full path or
+ with the name of the executable only. If no full path is provided the
+ executable is looked up in the $PATH environment variable used by the
+ desktop environment.
+ The name or path of the executable program may not contain the equal
+ sign ("="). Arguments are separated by a space.
+ </para>
+ <para>
+ Arguments may be quoted in whole. If an argument contains a reserved
+ character the argument must be quoted. The rules for quoting of
+ arguments is also applicable to the executable name or path of the
+ executable program as provided.
+ </para>
+ <para>
+ Quoting must be done by enclosing the argument between double quotes
+ and escaping the double quote character, backtick character ("`"),
+ dollar sign ("$") and backslash character ("\") by preceding it with an
+ additional backslash character. Implementations must undo quoting before
+ expanding field codes and before passing the argument to the executable
+ program. Reserved characters are space (" "), tab, newline, double
+ quote, single quote ("'"), backslash character ("\"),
+ greater-than sign ("&gt;"), less-than sign ("&lt;"),
+ tilde ("~"), vertical bar ("|"), ampersand ("&amp;"), semicolon (";"),
+ dollar sign ("$"), asterisk ("*"), question mark ("?"), hash mark ("#"),
+ parenthesis ("(") and (")") and backtick character ("`").
+ </para>
+ <para>
+ Note that the general escape rule for values of type string states that
+ the backslash character can be escaped as ("\\") as well and that this
+ escape rule is applied before the quoting rule. As such, to
+ unambiguously represent a literal backslash character in a quoted
+ argument in a desktop entry file requires the use of four successive
+ backslash characters ("\\\\"). Likewise, a literal dollar sign in a
+ quoted argument in a desktop entry file is unambiguously represented
+ with ("\\$").
+ </para>
+ <para>
+ A number of special field codes have been defined which will be
+ expanded by the file manager or program launcher when encountered
+ in the command line.
+ Field codes consist of the percentage character ("%") followed by
+ an alpha character. Literal percentage characters must be escaped
+ as <literal>%%</literal>.
+ Deprecated field codes should be removed from the command line and
+ ignored.
+ Field codes are expanded only once, the string that is used to
+ replace the field code should not be checked for field codes itself.
+ </para>
+ <para>
+ Command lines that contain a field code that is not listed in this
+ specification are invalid and must not be processed, in particular
+ implementations may not introduce support for field codes not listed
+ in this specification. Extensions, if any, should be introduced by
+ means of a new key.
+ </para>
+ <para>
+ Implementations must take care not to expand field codes into multiple
+ arguments unless explicitly instructed by this specification.
+ This means that name fields, filenames and other replacements that
+ can contain spaces must be passed as a single argument
+ to the executable program after expansion.
+ </para>
+ <para>
+ Although the <varname>Exec</varname> key is defined to have a value
+ of the type string, which is limited to ASCII characters, field code
+ expansion may introduce non-ASCII characters in arguments.
+ Implementations must take care that all characters in arguments
+ passed to the executable program are properly encoded according to
+ the applicable locale setting.
+ </para>
+ <para>
+ Recognized field codes are as follows:
+ </para>
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Code</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>%f</literal></entry>
+ <entry>
+ A single file name, even if multiple files are selected. The system
+ reading the desktop entry should recognize that the program in
+ question cannot handle multiple file arguments, and it should
+ should probably spawn and execute multiple copies of a program
+ for each selected file if the program is not able to handle
+ additional file arguments. If files are not on the local file system
+ (i.e. are on HTTP or FTP locations), the files will be copied to the local
+ file system and <literal>%f</literal> will be expanded to point at the temporary
+ file. Used for programs that do not understand the URL syntax.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%F</literal></entry>
+ <entry>
+ A list of files. Use for apps that can open several local
+ files at once.
+ Each file is passed as a separate argument to
+ the executable program.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%u</literal></entry>
+ <entry>
+ A single URL. Local files may either be passed as
+ file: URLs or as file path.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%U</literal></entry>
+ <entry>
+ A list of URLs.
+ Each URL is passed as a separate argument to
+ the executable program. Local files may either be passed as
+ file: URLs or as file path.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%d</literal></entry>
+ <entry>
+ Deprecated.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%D</literal></entry>
+ <entry>
+ Deprecated.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%n</literal></entry>
+ <entry>
+ Deprecated.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%N</literal></entry>
+ <entry>
+ Deprecated.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%i</literal></entry>
+ <entry>
+ The <varname>Icon</varname> key of the desktop entry
+ expanded as two arguments, first
+ <literal>--icon</literal> and then the value of the
+ <varname>Icon</varname> key. Should not expand to any
+ arguments if the <varname>Icon</varname> key is empty
+ or missing.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%c</literal></entry>
+ <entry>
+ The translated name of the application as listed in
+ the appropriate <varname>Name</varname> key in the
+ desktop entry.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%k</literal></entry>
+ <entry>
+ The location of the desktop file as either a URI (if for
+ example gotten from the vfolder system) or a local
+ filename or empty if no location is known.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%v</literal></entry>
+ <entry>
+ Deprecated.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%m</literal></entry>
+ <entry>
+ Deprecated.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ <para>
+ A command line may contain at most one %f, %u, %F or %U field code.
+ If the application should not open any
+ file the %f, %u, %F and %U field codes must be removed from the
+ command line and ignored.
+ </para>
+ <para>
+ Field codes must not be used inside a quoted argument, the result of
+ field code expansion inside a quoted argument is undefined. The %F and
+ %U field codes may only be used as an argument on their own.
+ </para>
+ </sect1>
+ <sect1 id="mime-types">
+ <title>Registering MIME Types</title>
+ <para>
+ The <varname>MimeType</varname> key is used to indicate the MIME
+ Types that an application knows how to handle. It is expected that
+ for some applications this list could become long. An application
+ is expected to be able to reasonably open files of these types
+ using the command listed in the <varname>Exec</varname> key.
+ </para>
+ <para>
+ There should be no priority for MIME Types in this field, or any
+ form of priority in the desktop file. Priority for applications
+ is handled external to the <filename>.desktop</filename> files.
+ </para>
+<!--
+ <sect2 id="mime-caching">
+ <title>Caching MIME Types</title>
+ <para>
+ To make parsing of all the desktop files less costly, a
+ <command>update-desktop-database</command> program is provided
+ that will generate a cache file. The concept is identical to
+ that of the 'update-mime-database' program in that it lets
+ applications avoid reading in (potentially) hundreds of files.
+ It will need to be run after every desktop file is installed.
+ One cache file is created for every directory in
+ $XDG_DATA_DIRS/applications/, and will create a file called
+ $XDG_DATA_DIRS/applications/mimeinfo.cache.
+ </para>
+ <para>
+ The format of the cache is similar to that of the desktop file,
+ and is just a list mapping mime-types to desktop files. Here's
+ a quick example of what it would look like:
+ </para>
+ <programlisting>application/x-foo=foo.desktop;bar.desktop;
+application/x-bar=bar.desktop;</programlisting>
+ <para>
+ Each MIME Type is listed only once per cache file, and the
+ desktop-id is expected to exist in that particular directory.
+ That is to say, if the cache file is located at
+ <filename>/usr/share/applications/mimeinfo.cache</filename>,
+ bar.desktop refers to the file
+ <filename>/usr/share/applications/bar.desktop</filename>.
+ </para>
+ </sect2>
+ <sect2 id="mime-priority">
+ <title>Priority of MIME Types and desktop files</title>
+ <para>
+ There is also a preference list to determine preferred
+ application of a given MIME Type. It defines the 'default'
+ application to handle a given MIME Type. It has the same format
+ as the cache list.
+ </para>
+ <programlisting>mime/type=desktop-id.desktop;</programlisting>
+ <para>
+ If a mime type is listed multiple times (either in the same
+ file, or in another file further down the search path), the
+ latter mention wins. If a listed file doesn't exist, or is
+ precluded through the <varname>OnlyShowIn</varname> or
+ <varname>NotShowIn</varname> keys, they should be ignored.
+ This means that applications will have to keep a history of the
+ preferred applications that they run into, so that if the top
+ desktop file for a given MIME Type isn't available, the second
+ one can be tested, etc.
+ </para>
+ <para>
+ It is also worth noting who this mechanism is defined for. It
+ is primarily intended for use by distributors/sysadmins to
+ provide a sane set of defaults for their users. Additionally,
+ users themselves can use this mechanism to override the user
+ defaults. We intentionally don't provide a way for application
+ authors themselves to list themselves as the default for a given
+ type, as we felt that that cannot work.
+ </para>
+ </sect2>
+-->
+ </sect1>
+ <sect1 id="extra-actions">
+ <title>Additional applications actions</title>
+ <para>
+ Desktop entries of type Application can include one or more actions. An
+ action represents an additional way to invoke the application.
+ Application launchers should expose them to the user (for example, as a
+ submenu) within the context of the application. This is used to build
+ so called "Quicklists" or "Jumplists".
+ </para>
+ <sect2 id="extra-actions-identifier">
+ <title>Action identifier</title>
+ <para>
+ Each action is identified by a string, following the same format
+ as key names (see <xref linkend="entries"/>). Each identifier is associated
+ with an action group that must be present in the <filename>.desktop</filename>
+ file. The action group is a group named <varname>Desktop Action %s</varname>,
+ where <varname>%s</varname> is the action identifier.
+ </para>
+ <para>
+ It is not valid to have an action group for an action identifier not
+ mentioned in the <varname>Actions</varname> key. Such an action group
+ must be ignored by implementors.
+ </para>
+ </sect2>
+ <sect2 id="extra-actions-keys">
+ <title>Action keys</title>
+ <para>
+ The following keys are supported within each action group. If a
+ REQUIRED key is not present in an action group, then the implementor
+ should ignore this action.
+ </para>
+ <table>
+ <title>Action Specific Keys</title>
+ <tgroup cols="5">
+ <thead>
+ <row>
+ <entry>Key</entry>
+ <entry>Description</entry>
+ <entry>Value Type</entry>
+ <entry>REQ?</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry id="key-action-group-name"><varname>Name</varname></entry>
+ <entry>
+ Label that will be shown to the user. Since actions are
+ always shown in the context of a specific application (that is, as
+ a submenu of a launcher), this only needs to be unambiguous within
+ one application and should not include the application name.
+ </entry>
+ <entry>localestring</entry>
+ <entry>YES</entry>
+ </row>
+ <row>
+ <entry id="key-action-group-icon"><varname>Icon</varname></entry>
+ <entry>
+ Icon to be shown togheter with the action. If the name is
+ an absolute path, the given file will be used. If the name is not
+ an absolute path, the algorithm described in the <ulink
+ url="http://freedesktop.org/wiki/Standards/icon-theme-spec">Icon
+ Theme Specification</ulink> will be used to locate the icon.
+ Implementations may choose to ignore it.
+ </entry>
+ <entry>localestring</entry>
+ <entry>NO</entry>
+ </row>
+ <row>
+ <entry id="key-action-group-osi-nsi">
+ <varname>OnlyShowIn</varname>, <varname>NotShowIn</varname>
+ </entry>
+ <entry>
+ A list of strings identifying the environments that should
+ display/not display this specific action. These keys are to be
+ interpreted in addition to the <varname>OnlyShowIn</varname>
+ and <varname>NotShowIn</varname> keys of the <varname>Desktop
+ Entry</varname> group, and are not replacing them. Only one of
+ these keys, either <varname>OnlyShowIn</varname> or
+ <varname>NotShowIn</varname>, may appear in each action group
+ (for possible values see the <ulink
+ url="http://www.freedesktop.org/Standards/menu-spec">Desktop
+ Menu Specification</ulink>).
+ </entry>
+ <entry>string(s)</entry>
+ <entry>NO</entry>
+ </row>
+ <row>
+ <entry id="key-action-group-exec"><varname>Exec</varname></entry>
+ <entry>
+ Program to execute for this action, possibly with arguments.
+ See the <link linkend="exec-variables"><varname>Exec</varname>
+ key</link> for details on how this key works.
+ </entry>
+ <entry>string</entry>
+ <entry>YES</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect2>
+ <sect2 id="extra-actions-implementation-notes">
+ <title>Implementation notes</title>
+ <para>
+ Application actions should be supported by implementors. However, in
+ case they are not supported, implementors can simply ignore the
+ <varname>Actions</varname> key and the associated <varname>Desktop
+ Action</varname> action groups, and keep using the <varname>Desktop
+ Entry</varname> group: the primary way to describe and invoke the
+ application is through the Name, Icon and Exec keys from the
+ <varname>Desktop Entry</varname> group.
+ </para>
+ <para>
+ It is not expected that other desktop components showing application
+ lists (software installers, for instance) will provide any user
+ interface for these actions. Therefore applications must only include
+ actions that make sense as general launchers.
+ </para>
+ </sect2>
+ </sect1>
+ <sect1 id="extending">
+ <title>Extending the format</title>
+ <para>
+ If the standard is to be amended with a new <literal>{key,value}</literal> pair which
+ should be applicable to all supporting parties, a group discussion
+ will take place. This is the preferred method for introducing
+ changes. If one particular party wishes to add a field for personal
+ use, they should prefix the key with the string <varname>X-<replaceable>PRODUCT</replaceable></varname>,
+ e.g. <varname>X-NewDesktop-Foo</varname>, following the precedent set by other IETF and RFC
+ standards.
+ </para>
+ <para>
+ Alternatively, fields can be placed in their own group, where they may
+ then have arbitrary key names. If this is the case, the group should
+ follow the scheme outlined above,
+ i.e. <literal>[X-<replaceable>PRODUCT</replaceable>
+ <replaceable>GROUPNAME</replaceable>]</literal> or
+ something similar. These steps will avoid namespace clashes between
+ different yet similar environments.
+ </para>
+ </sect1>
+ <appendix id="example">
+ <title>Example Desktop Entry File</title>
+ <programlisting>
+[Desktop Entry]
+Version=1.0
+Type=Application
+Name=Foo Viewer
+Comment=The best viewer for Foo objects available!
+TryExec=fooview
+Exec=fooview %F
+Icon=fooview
+MimeType=image/x-foo;
+Actions=Gallery;Create;
+
+[Desktop Action Gallery]
+Exec=fooview --gallery
+Name=Browse Gallery
+
+[Desktop Action Create]
+Exec=fooview --create-new
+Name=Create a new Foo!
+Icon=fooview-new
+ </programlisting>
+ </appendix>
+ <appendix id="kde-items">
+ <title>Currently reserved for use within KDE</title>
+ <para>
+ For historical reasons KDE is using some KDE-specific extensions
+ that are currently not prefixed by a <literal>X-KDE-</literal> prefix.
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ KDE specific keys: <varname>ServiceTypes</varname>,
+ <varname>DocPath</varname>, <varname>InitialPreference</varname>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ KDE specific types: <constant>ServiceType</constant>,
+ <constant>Service</constant> and <constant>FSDevice</constant>
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ KDE also used the <varname>Keywords</varname> key before it was
+ standardized, using commas instead of semi-colons as separators.
+ At the time of standardization, the field had been prefixed with a
+ <literal>X-KDE</literal> prefix, but the Trinity fork still used
+ the non-prefixed variant.
+ </para>
+ <para>
+ KDE uses the following additional keys for desktop entries of the
+ <constant>FSDevice</constant> type.
+ </para>
+ <table>
+ <title>FSDevice Specific Keys</title>
+ <tgroup cols="3">
+ <thead>
+ <row>
+ <entry>Key</entry>
+ <entry>Description</entry>
+ <entry>Value Type</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry id="key-dev"><varname>Dev</varname></entry>
+ <entry>
+ The device to mount.
+ </entry>
+ <entry>string</entry>
+ </row>
+ <row>
+ <entry id="key-fstype"><varname>FSType</varname></entry>
+ <entry>
+ The type of file system to try to mount.
+ </entry>
+ <entry>string</entry>
+ </row>
+ <row>
+ <entry id="key-mountpoint"><varname>MountPoint</varname></entry>
+ <entry>
+ The mount point of the device in question.
+ </entry>
+ <entry>string</entry>
+ </row>
+ <row>
+ <entry id="key-readonly"><varname>ReadOnly</varname></entry>
+ <entry>
+ Specifies whether or not the device is read only.
+ </entry>
+ <entry>boolean</entry>
+ </row>
+ <row>
+ <entry id="key-unmounticon"><varname>UnmountIcon</varname></entry>
+ <entry>
+ Icon to display when device is not mounted. Mounted devices display icon from the <varname>Icon</varname> key.
+ </entry>
+ <entry>localestring</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </appendix>
+ <appendix id="deprecated-items">
+ <title>Deprecated Items</title>
+ <para>
+ As this standard is quite old there are some deprecated items that
+ may or may not be used by several implementations.
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>Type=MimeType</literal> is deprecated as there is a
+ new standard for this now, see the <ulink
+ url="http://www.freedesktop.org/Standards/shared-mime-info-spec">Shared
+ MIME-info Database specification</ulink> for more
+ information. In consequence the Keys
+ <varname>Patterns</varname> (various file name extensions
+ associated with the MIME type) and
+ <varname>DefaultApp</varname> (the default application
+ associated with this MIME type) are also deprecated.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Using <filename>.kdelnk</filename> instead of
+ <filename>.desktop</filename> as the file extension is
+ deprecated.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Using <literal>[KDE Desktop Entry]</literal> instead of
+ <literal>[Desktop Entry]</literal> as header is deprecated.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The <literal>Encoding</literal> key is deprecated. It was used to
+ specify whether keys of type <literal>localestring</literal> were
+ encoded in UTF-8 or in the specified locale. Possible values are
+ <literal>UTF-8</literal> and <literal>Legacy-Mixed</literal>. See
+ <xref linkend="legacy-mixed"/> for more details.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Deprecated <varname>Exec</varname> field codes:
+ <literal>%m</literal> (the mini-icon associated with the
+ desktop entry, this should be expanded as two arguments,
+ <literal>--miniicon</literal> and the content of the
+ <varname>MiniIcon</varname> key, it can also be ignored by
+ expanding it to no arguments), %v (the device as listed
+ in the <varname>Dev</varname> key in the desktop file),
+ %d (the directory of a file), %D (the directories of
+ files), %n (the base name of a file) and %N (the base names
+ of files).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Deprecated keys: <varname>MiniIcon</varname> (small icon for
+ menus, etc.), <varname>TerminalOptions</varname> (if the
+ program runs in a terminal, any options that should be
+ passed to the terminal emulator before actually executing
+ the program), <varname>Protocols</varname>,
+ <varname>Extensions</varname>,
+ <varname>BinaryPattern</varname>,
+ <varname>MapNotify</varname>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The <literal>SwallowTitle</literal> and
+ <literal>SwallowExec</literal> keys are deprecated.
+ The <literal>SwallowTitle</literal> key is of type
+ <literal>localestring</literal> and specifies the title of the window
+ if is swallowed onto the panel. The <literal>SwallowExec</literal>
+ key is of type <literal>string</literal> and specifies the
+ program to exec if swallowed app is clicked.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The <literal>SortOrder</literal> key is deprecated. It is of type
+ <literal>string(s)</literal> and may be used to specify the order in
+ which to display files. The <ulink
+ url="http://www.freedesktop.org/Standards/menu-spec">Desktop
+ Menu Specification</ulink> defines another mechanism for defining the
+ order of menu items.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The <literal>FilePattern</literal> key is deprecated.
+ The value is a list of regular
+ expressions to match against for a file manager to determine if this
+ entry's icon should be displayed. Usually simply the name of the main
+ executable and friends.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Historically some booleans have been represented by the numeric
+ entries <constant>0</constant> or <constant>1</constant>. With
+ this version of the standard they are now to be represented as a
+ boolean string. However, if an implementation is reading a pre-1.0
+ desktop entry, it should interpret <constant>0</constant> and
+ <constant>1</constant> as <constant>false</constant> and
+ <constant>true</constant>, respectively.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Historically lists have been comma separated. This is inconsistent with other lists which are separated by a semicolon. When reading a pre-1.0 desktop entry, comma separated lists should continue to be supported.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </appendix>
+ <appendix id="legacy-mixed">
+ <title>The <constant>Legacy-Mixed</constant> Encoding (Deprecated)</title>
+ <para>
+ The <constant>Legacy-Mixed</constant> encoding corresponds to the
+ traditional encoding of desktop files in older versions of the GNOME and
+ KDE desktop files. In this encoding, the encoding of each
+ <literal>localestring</literal> key is determined by the locale tag for
+ that key, if any, instead of being UTF-8. For keys without a locale tag,
+ the value must contain only ASCII characters.
+ </para>
+ <para>
+ If the file specifies an unsupported encoding, the implementation
+ should either ignore the file, or, if the user has requested a direct
+ operation on the file (such as opening it for editing), display an
+ appropriate error indication to the user.
+ </para>
+ <para>
+ In the absence of an <varname>Encoding</varname> key, the implementation may choose
+ to autodetect the encoding of the file by using such factors
+ as:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ The location of the file on the file system
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Whether the contents of the file are valid UTF-8
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ If the implementation does not perform such auto-detection, it should
+ treat a file without an <varname>Encoding</varname> key in the same way as a file with an
+ unsupported <varname>Encoding</varname> key.
+ </para>
+ <para>
+ If the locale tag includes an <literal>.<replaceable>ENCODING</replaceable></literal> part, then that determines
+ the encoding for the line. Otherwise, the encoding is determined
+ by the language, or
+ <literal><replaceable>lang</replaceable>_<replaceable>COUNTRY</replaceable></literal>
+ pair from the locale tag, according to the following table.
+ </para>
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Encoding</entry>
+ <entry>Aliases</entry>
+ <entry>Tags</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>ARMSCII-8 (*)</entry><entry></entry><entry>hy</entry>
+ </row><row>
+ <entry>BIG5</entry><entry></entry><entry>zh_TW</entry>
+ </row><row>
+ <entry>CP1251</entry><entry></entry><entry>be bg</entry>
+ </row><row>
+ <entry>EUC-CN</entry><entry>GB2312</entry><entry>zh_CN</entry>
+ </row><row>
+ <entry>EUC-JP</entry><entry></entry><entry>ja</entry>
+ </row><row>
+ <entry>EUC-KR</entry><entry></entry><entry>ko</entry>
+ </row><row>
+ <entry>GEORGIAN-ACADEMY (*)</entry><entry></entry><entry></entry>
+ </row><row>
+ <entry>GEORGIAN-PS (*)</entry><entry></entry><entry>ka</entry>
+ </row><row>
+ <entry>ISO-8859-1</entry><entry></entry><entry>br ca da de en es eu fi fr gl it nl no pt sv wa</entry>
+ </row><row>
+ <entry>ISO-8859-2</entry><entry></entry><entry>cs hr hu pl ro sk sl sq sr</entry>
+ </row><row>
+ <entry>ISO-8859-3 </entry><entry></entry><entry>eo</entry>
+ </row><row>
+ <entry>ISO-8859-5</entry><entry></entry><entry>mk sp</entry>
+ </row><row>
+ <entry>ISO-8859-7</entry><entry></entry><entry>el</entry>
+ </row><row>
+ <entry>ISO-8859-9</entry><entry></entry><entry>tr</entry>
+ </row><row>
+ <entry>ISO-8859-13</entry><entry></entry><entry>lt lv mi</entry>
+ </row><row>
+ <entry>ISO-8859-14</entry><entry></entry><entry>cy ga</entry>
+ </row><row>
+ <entry>ISO-8859-15</entry><entry></entry><entry>et</entry>
+ </row><row>
+ <entry>KOI8-R</entry><entry></entry><entry>ru</entry>
+ </row><row>
+ <entry>KOI8-U</entry><entry></entry><entry>uk</entry>
+ </row><row>
+ <entry>TCVN-5712 (*)</entry><entry>TCVN</entry><entry>vi</entry>
+ </row><row>
+ <entry>TIS-620</entry><entry></entry><entry>th</entry>
+ </row><row>
+ <entry>VISCII</entry><entry></entry><entry></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ <variablelist>
+ <varlistentry>
+ <term>Encoding</term>
+ <listitem>
+ <para>
+ The name given here is listed here is typically the
+ canonical name for the encoding in the GNU C Library's
+ <function>iconv</function> facility. Encodings marked with (*) are not
+ currently supported by the GNU C Library; for this reason,
+ implementations may choose to ignore lines in desktop
+ files that resolve to this encoding. Desktop files with
+ these encodings are currently rare or non-existent.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Aliases</term>
+ <listitem>
+ <para>
+ Other names for the encoding found in existing desktop
+ files.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Tags</term>
+ <listitem>
+ <para>
+ Language tags for which this is the default encoding.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ <para>
+ This table above covers all tags and encodings that are known to
+ be currently in use. Implementors may choose to support
+ encodings not in the above set. For tags without defaults listed
+ in the above table, desktop file creators must specify the
+ <literal>.<replaceable>ENCODING</replaceable></literal> part of the locale tag.
+ </para>
+ <para>
+ Matching the <literal>.<replaceable>ENCODING</replaceable></literal> part of the locale tag against a locale
+ name or alias should be done by stripping all punctuation
+ characters from both the tag and the name or alias, converting
+ both name and alias to lowercase, and comparing the result.
+ This is necessary because, for example, <literal>Big5</literal> is frequently
+ found instead of <literal>BIG5</literal> and <literal>georgianacademy</literal> instead of
+ <literal>GEORGIAN-ACADEMY</literal>. Desktop files creators should, however, use
+ the name as it appears in the "Encoding" column above.
+ </para>
+ </appendix>
+</article>
diff --git a/help-system/help-system-spec.xml b/help-system/help-system-spec.xml
new file mode 100644
index 0000000..ffe1a5d
--- /dev/null
+++ b/help-system/help-system-spec.xml
@@ -0,0 +1,195 @@
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<article id="index">
+ <title>Help System Specification</title>
+
+ <articleinfo>
+ <title>Help System Specification</title>
+ <date>2010-04-30</date>
+ <authorgroup>
+ <author>
+ <firstname>Shaun</firstname>
+ <surname>McCance</surname>
+ <affiliation>
+ <address>
+ <email>shaunm@gnome.org</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+ </articleinfo>
+
+ <section id="overview">
+ <title>Overview</title>
+ <para>This specification provides a common directory layout and URI
+ scheme for locally installed help documents. Documents installed and
+ referenced using this system will have better interoperability between
+ different desktop environments and help applications.</para>
+ </section>
+
+ <section id="layout">
+ <title>Directory Layout</title>
+
+ <para>A document is a collection of files, possibly from multiple
+ directories in a path. Documents may be in any format, or even in
+ multiple formats; see <xref linkend="formats"/> for details. A
+ document has a <emphasis>document identifier</emphasis> that
+ identifies it uniquely and specifies where it can be found on
+ the file system. A document identifier consists of one or more
+ characters from the following: digits (U+0030-U+0039), letters
+ (U+0041-U+005A and U+0061-U+007A), hyphen (U+002D), underscore
+ (U+005F), period (U+002E), and percent (U+0025).</para>
+
+ <para>Document identifiers are not explicitly namespaced. To
+ avoid conflicts, document identifiers should start with the name
+ of the application or package that provides the document. In many
+ cases, the name of the application or package alone may be used.
+ Otherwise, the document identifier should start with the name
+ of the package or application followed by a hyphen.</para>
+
+ <para>Documents are installed under the <filename>help</filename>
+ subdirectory in <envar>$XDG_DATA_HOME</envar> or in the
+ <envar>$XDG_DATA_DIRS</envar> path. See the
+ <ulink url="http://standards.freedesktop.org/basedir-spec/latest/">XDG
+ Base Directory Specification</ulink> for details on <envar>$XDG_DATA_HOME</envar>
+ and <envar>$XDG_DATA_DIRS</envar>. Each <filename>help</filename>
+ directory in the path contains subdirectories for languages. Each
+ language subdirectory contains subdirectories for documents, where
+ the name of each subdirectory matches the document identifier of
+ a document.</para>
+
+ <para>The <emphasis>document path</emphasis> for a given document
+ is the list of directories of the following form:</para>
+
+ <programlisting><replaceable>datadir</replaceable>/help/<replaceable>lang</replaceable>/<replaceable>document</replaceable></programlisting>
+
+ <variablelist>
+ <varlistentry>
+ <term><filename><replaceable>datadir</replaceable></filename></term>
+ <listitem><para>Either <envar>$XDG_DATA_HOME</envar> or a directory in
+ the <envar>$XDG_DATA_DIRS</envar> path.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename><replaceable>lang</replaceable></filename></term>
+ <listitem><para>The language code of a language in the user's
+ list of preferred languages.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename><replaceable>document</replaceable></filename></term>
+ <listitem><para>The document identifier of the document.</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>The document path is ordered first according to the position of
+ <filename><replaceable>datadir</replaceable></filename> in the path, then
+ by the position of <filename><replaceable>lang</replaceable></filename>
+ in the user's preferred list of languages. So, for example, if the
+ user's preferred language list is <systemitem>pt_BT:pt:C</systemitem>,
+ then <filename>~/.local/share/help/pt/beanstalk</filename> would take
+ precedence over <filename>/usr/share/help/pt_BR/beanstalk</filename>.</para>
+ </section>
+
+ <section id="uri-scheme">
+ <title>URI Scheme</title>
+
+ <para>Documents are referenced using the <systemitem>help:</systemitem>
+ URI scheme. The <systemitem>help:</systemitem> URI scheme has the
+ following form:</para>
+
+ <programlisting>help:<replaceable>document</replaceable>/<replaceable>page</replaceable>?<replaceable>options</replaceable>#<replaceable>anchor</replaceable></programlisting>
+
+ <variablelist>
+ <varlistentry>
+ <term><replaceable>document</replaceable></term>
+ <listitem><para>The document identifier.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><replaceable>page</replaceable></term>
+ <listitem><para>An identifier for a page within the document. Documents
+ often consist of multiple logical pages, which may not be reflected in
+ the actual files on the system. The page identifier is optional. If it
+ is not present, the preceding <systemitem>/</systemitem> character
+ should be omitted.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><replaceable>options</replaceable></term>
+ <listitem><para>Additional options that can influence how applications
+ present a document. Options are optional. If they are not present, the
+ preceding <systemitem>?</systemitem> character should be omitted. If
+ they are present, they must conform to the
+ <systemitem>application/x-www-form-urlencoded</systemitem> MIME type.
+ Options may be used, for example, to override language settings or to
+ provide keys for conditional processing. This specification makes no
+ specific recommendations for the options.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><replaceable>anchor</replaceable></term>
+ <listitem><para>An anchor point within a page. Applications should
+ scroll to an appropriate point whenever possible. The anchor is
+ optional. If it is not present, the preceding <systemitem>#</systemitem>
+ character should be omitted.</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>Page identifiers and anchors may contain any character that is
+ valid in a document identifier. Some document types may have further
+ restrictions on page identifiers or anchors.</para>
+ </section>
+
+ <section id="formats">
+ <title>Help Formats</title>
+
+ <para>Documents may be installed in any format. Not all help applications
+ may recognize and handle the same types of documents. Whenever possible,
+ help applications should support HTML. Documents may be installed in
+ multiple formats. Help applications choose which format to use when
+ multiple formats are present.</para>
+
+ <para>For any format, a help application must map the information found
+ in the <systemitem>help:</systemitem> URI scheme to the information in
+ that format. This specification contains recommendations for finding
+ and handling documents in DocBook, Mallard, HTML, and XHTML.</para>
+
+ <section id="docbook">
+ <title>DocBook</title>
+ <para>DocBook documents have a file named <filename>index.docbook</filename>
+ in the document path. Any files referenced from the DocBook file, including
+ XML fragments included through XInclude, should be looked up according to
+ the document path.</para>
+ <para>When a help application displays a DocBook document, it will often
+ display it in multiple pages or chunks, rather than as a single long page.
+ The page identifier specifies which chunk to display. Not all applications
+ will split a DocBook document into chunks in the same way, so the page
+ identifier may not map exactly to the <sgmltag>id</sgmltag> attribute
+ of an element that is chunked. In this case, the applicaiton displays
+ the nearest enclosing chunk and treats the page identifier as an anchor
+ if no anchor was explicitly provided.</para>
+ </section>
+
+ <section id="mallard">
+ <title>Mallard</title>
+ <para>Mallard documents have a file named <filename>index.page</filename>
+ in the document path. Other page files may be different directories in
+ the document path. Any files referenced in any page file, including XML
+ fragments included through XInclude, should be looked up according to
+ the document path.</para>
+ <para>The page identifier specifies the id of a Mallard page file. The
+ anchor specifies the id of a section within a Mallard page file.</para>
+ </section>
+
+ <section id="html">
+ <title>HTML and XHTML</title>
+ <para>HTML documents have a file named <filename>index.html</filename>
+ in the document path. XHTML documents have a file named <filename>index.xhtml</filename>
+ in the document path. Other HTML or XHTML files in the document may
+ be in different directories in the document path. Any files referenced
+ in any HTML or XHTML page, including XML fragments included through
+ XInclude in XHTML, should be looked up according to the document path.</para>
+ <para>The page identifier specifies the base file name, without the
+ <filename>.html</filename> or <filename>.xhtml</filename> extension,
+ of an HTML or XHTML file in the document. The anchor specifies a named
+ anchor within the HTML or XHTML file.</para>
+ </section>
+ </section>
+</article>
diff --git a/menu/.cvsignore b/menu/.cvsignore
new file mode 100644
index 0000000..f90eaaf
--- /dev/null
+++ b/menu/.cvsignore
@@ -0,0 +1,11 @@
+config.log
+config.status
+config.sub
+configure
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+config.guess
+config.h
+config.h.in
diff --git a/menu/AUTHORS b/menu/AUTHORS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/menu/AUTHORS
diff --git a/menu/COPYING b/menu/COPYING
new file mode 100644
index 0000000..73e7995
--- /dev/null
+++ b/menu/COPYING
@@ -0,0 +1,2 @@
+FIXME - we need a license for specs
+
diff --git a/menu/ChangeLog b/menu/ChangeLog
new file mode 100644
index 0000000..025e9e1
--- /dev/null
+++ b/menu/ChangeLog
@@ -0,0 +1,143 @@
+2009-01-10 Vincent Untz <vuntz@gnome.org>
+
+ * menu-spec.xml: bump version to 1.1-draft
+
+2009-01-10 Vincent Untz <vuntz@gnome.org>
+
+ * menu-spec.xml: add LXDE as registered desktop environment for
+ OnlyShowIn.
+ See http://lists.freedesktop.org/archives/xdg/2008-December/010103.html
+
+2007-08-18 Vincent Untz <vuntz@gnome.org>
+
+ * menu-spec.xml: accept X-Foo for environments in
+ OnlyShowIn/NotShowIn.
+ Fix bug #11564.
+
+2007-07-27 Vincent Untz <vuntz@gnome.org>
+
+ * menu-spec.xml: change sysconfdir/desktop/menus/ to sysconfdir/menus/
+ in one place.
+ Fix bug #8289.
+
+2007-02-06 Waldo Bastian <waldo.bastian@intel.com>
+ * Added scondary menu categories
+ * Bumped version to 1.0
+
+2007-01-18 Waldo Bastian <waldo.bastian@intel.com>
+
+ * Added support for LSB's tet testing framework
+
+ * Renamed test to more meaningful names:
+ 1 -> Category
+ 2 -> OnlyUnallocated
+ 3 -> Deleted
+ 4 -> Move
+ 5 -> All
+ 6 -> Filename
+ 7 -> And
+ 8 -> Or
+ 9 -> Exclude
+ a -> DesktopFileID
+ b -> menu-multiple-matching
+ c -> Directory
+ d -> submenu-collision
+ e -> LegacyDir-relative
+ f -> Move-collapsing
+ g -> AppDir-relative
+ h -> DirectoryDir-relative
+ i -> DefaultMergeDirs
+ j -> MergeDir-relative
+ k -> MergeFile-relative
+ l -> MergeFile2
+ m -> NoDisplay
+ n -> NoDisplay2
+ o -> boolean-logic
+ p -> MergeFile3
+ q -> MergeFile-parent
+ r -> MergeFile-path
+ s -> desktop-name-collision
+ t -> Move-ordering
+ u -> MergeFile-recursive
+ v -> NotOnlyUnallocated-default
+ w -> LegacyDir-Move
+ x -> Move-submenu
+ y -> Merge-combined
+
+ * Added the following new tests:
+ AppDir
+ DirectoryDir
+ MergeDir-absolute
+ MergeFile-absolute
+ official-categories
+
+2006-03-28 Waldo Bastian <waldo.bastian@intel.com>
+
+ * menu-spec.xml: Added example for installing sub-menu.
+
+2006-03-21 Waldo Bastian <waldo.bastian@intel.com>
+
+ * menu-spec.xml: Recommend to use /usr/share for datadir
+
+2005-03-30 Waldo Bastian <bastian@kde.org>
+
+ * menu-spec.xml: Change semantics and remove restrictions on <Move>
+ element to facilitate menu editing.
+
+2005-03-23 Waldo Bastian <bastian@kde.org>
+
+ * menu-spec.xml: Clearify DTD conformance and version compatibility
+
+2005-03-22 Waldo Bastian <bastian@kde.org>
+
+ * menu-spec.xml, menu.dtd: Added type attribute to <MergeFile>
+ element to facilitate menu-editing.
+
+2005-02-24 Waldo Bastian <bastian@kde.org>
+
+ * menu-spec.xml: Fixed links to www.freedesktop.org
+
+2004-12-13 Mark McLoughlin <mark@skynet.ie>
+
+ * menu-spec.xml: elaboration from Waldo.
+
+2004-12-08 Mark McLoughlin <mark@skynet.ie>
+
+ * menu-spec.xml: make <OnlyUnallocated/> be for entries
+ which don't match any <Include> rule rather than entries
+ which don't appear in any menu.
+
+2004-09-03 Mark McLoughlin <mark@skynet.ie>
+
+ * menu-spec.xml: minor typo - "This specification adds two
+ new fields to desktop entries" - s/two/three/
+
+2004-05-07 Mark McLoughlin <mark@skynet.ie>
+
+ * menu-spec.xml: fix typo in <Menuname> description. Its
+ inline_header rather than inline_title according to the DTD.
+
+2004-04-18 Mark McLoughlin <mark@skynet.ie>
+
+ Patch from Ville Skyttä <ville.skytta@iki.fi>
+
+ * menu-spec.xml: editorial changes and typo fixes.
+
+2003-12-12 Waldo Bastian <bastian@kde.org>
+ * menu-spec.xml: Correct example in "Legacy Menu Hierarchies"
+
+2003-11-09 Heinrich Wendel <h_wendel@cojobo.net>
+ * menu-spec.xml: patch for categories based on Suse 9.0
+ * menu-spec.xml: Grahpics -> Graphics
+ * menu-spec.xml: DTD fix
+ * menu.dtd: added dtd
+ * testsuite: fixed errors highlighted by havoc
+
+2003-10-23 Havoc Pennington <hp@redhat.com>
+
+ * menu-spec.xml: clarify how <Move> elements are resolved
+
+2003-10-21 Havoc Pennington <hp@redhat.com>
+
+ * create autotools setup
+
diff --git a/menu/Makefile.am b/menu/Makefile.am
new file mode 100644
index 0000000..92448da
--- /dev/null
+++ b/menu/Makefile.am
@@ -0,0 +1,16 @@
+HTML_FILES= menu-spec.html
+
+XML_FILES= menu-spec.xml
+
+EXTRA_DIST= $(HTML_FILES) $(XML_FILES)
+
+if XML_DOCS_ENABLED
+all-local: $(HTML_FILES)
+endif
+
+%.html: %.xml
+ $(XMLTO) html-nochunks $<
+
+maintainer-clean-local:
+ rm -f $(HTML_FILES)
+
diff --git a/menu/NEWS b/menu/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/menu/NEWS
diff --git a/menu/README b/menu/README
new file mode 100644
index 0000000..0edf632
--- /dev/null
+++ b/menu/README
@@ -0,0 +1 @@
+This is the specification and test suite for the menu system.
diff --git a/menu/autogen.sh b/menu/autogen.sh
new file mode 100755
index 0000000..b1376df
--- /dev/null
+++ b/menu/autogen.sh
@@ -0,0 +1,3 @@
+#! /bin/sh
+autoreconf -v --install || exit 1
+./configure --enable-maintainer-mode "$@"
diff --git a/menu/configure.in b/menu/configure.in
new file mode 100644
index 0000000..7a5a76c
--- /dev/null
+++ b/menu/configure.in
@@ -0,0 +1,61 @@
+dnl -*- mode: m4 -*-
+AC_PREREQ(2.52)
+
+AC_INIT(menu-spec.xml)
+
+AM_INIT_AUTOMAKE(desktop-menu-spec, 0.7)
+
+# Honor aclocal flags
+ACLOCAL="$ACLOCAL $ACLOCAL_FLAGS"
+
+ ## must come before we use the $USE_MAINTAINER_MODE variable later
+AM_MAINTAINER_MODE
+
+AC_ARG_ENABLE(xml-docs, [ --enable-xml-docs build XML into HTML (requires xmlto)],enable_xml_docs=$enableval,enable_xml_docs=auto)
+
+### XML tools
+
+AC_PATH_PROG(XMLTO, xmlto, no)
+
+AC_MSG_CHECKING([whether to build XML documentation])
+
+if test x$XMLTO = xno ; then
+ have_xmlto=no
+else
+ have_xmlto=yes
+fi
+
+if test x$enable_xml_docs = xauto ; then
+ if test x$have_xmlto = xno ; then
+ enable_xml_docs=no
+ else
+ enable_xml_docs=yes
+ fi
+fi
+
+if test x$enable_xml_docs = xyes; then
+ if test x$have_xmlto = xno; then
+ AC_MSG_ERROR([Building XML docs explicitly required, but xmlto not found])
+ fi
+fi
+
+AM_CONDITIONAL(XML_DOCS_ENABLED, test x$enable_xml_docs = xyes)
+AC_MSG_RESULT(yes)
+
+AC_OUTPUT([
+Makefile
+])
+
+dnl ==========================================================================
+echo "
+ Menu specification $VERSION
+ ==============
+
+ prefix: ${prefix}
+ source code location: ${srcdir}
+ xmlto: ${XMLTO}"
+
+echo "
+ Maintainer mode: ${USE_MAINTAINER_MODE}
+ Building XML docs: ${enable_xml_docs}
+"
diff --git a/menu/menu-spec.xml b/menu/menu-spec.xml
new file mode 100644
index 0000000..7bd8568
--- /dev/null
+++ b/menu/menu-spec.xml
@@ -0,0 +1,2392 @@
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+ <!ENTITY version "1.1-draft">
+ <!ENTITY dtd-version "1.0">
+ ]>
+
+<article id="index">
+ <articleinfo>
+ <title>Desktop Menu Specification</title>
+ <releaseinfo>Version &version;</releaseinfo>
+ <date>31 March 2011</date>
+ <authorgroup>
+ <author>
+ <firstname>Waldo</firstname>
+ <surname>Bastian</surname>
+ <affiliation>
+ <address>
+ <email>waldo.bastian@intel.com</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Francois</firstname>
+ <surname>Gouget</surname>
+ <affiliation>
+ <address>
+ <email>fgouget@codeweavers.com</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Alex</firstname>
+ <surname>Graveley</surname>
+ <affiliation>
+ <address>
+ <email>alex@ximian.com</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>George</firstname>
+ <surname>Lebl</surname>
+ <affiliation>
+ <address>
+ <email>jirka@5z.com</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Havoc</firstname>
+ <surname>Pennington</surname>
+ <affiliation>
+ <address>
+ <email>hp@pobox.com</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Heinrich</firstname>
+ <surname>Wendel</surname>
+ <affiliation>
+ <address>
+ <email>h_wendel@cojobo.net</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+ </articleinfo>
+
+ <sect1 id="introduction">
+ <title>Introduction</title>
+ <para>
+ This document defines how to construct a user-visible hierarchy of
+ applications, typically displayed as a menu. It allows third-party
+ software to add menu items that work for all desktops, and allows system
+ administrators to edit menus in a way that affects all desktops.
+ </para>
+ <para>
+ The basic scheme is very simple. Information about each application (menu
+ item) is stored in a desktop entry (see <ulink
+ url="http://www.freedesktop.org/Standards/desktop-entry-spec">Desktop
+ Entry Standard</ulink>). Then an XML configuration file defines the
+ hierarchical arrangement (layout) of menu items, and which menu items are
+ actually displayed.
+ </para>
+ <para>
+ Things are complicated somewhat by the need to support legacy desktop
+ entry hierarchies, and the need to allow third parties to extend the menu
+ layout. Both of these issues are addressed by the idea of
+ <firstterm>merging</firstterm> two menu layouts.
+ </para>
+ <para>
+ In addition to a strict definition of the contents of each menu this
+ specification also foresees in a number of layout / presentation hints.
+ This part of the specification is optional, implementations may chose to
+ ignore these hints.
+ </para>
+ </sect1>
+ <sect1 id="paths">
+ <title>File locations</title>
+ <para>
+ Files involved in this specification are located according to the <ulink
+ url="http://www.freedesktop.org/Standards/basedir-spec">"desktop
+ base directory specification"</ulink>.
+ </para>
+ <para>
+ Here are the files defined by this specification:
+ <variablelist>
+ <varlistentry>
+
+<term><varname>$XDG_CONFIG_DIRS</varname>/menus/<varname>${XDG_MENU_PREFIX}</varname>applications.menu</term>
+ <listitem>
+ <para>
+ This file contains the XML definition of the main application
+ menu layout. The first file found in the search path should be
+ used; other files are ignored. This implies that if the user
+ has their own <varname>${XDG_MENU_PREFIX}</varname>applications.menu,
+ it replaces the system wide one.
+ (Though the user's menu may explicitly merge the system wide
+ one.)
+ </para>
+ <para>
+ Systems that offer multiple desktop environments and that want
+ to use distinct menu layouts in the different environments can
+ use differently prefixed .menu files. In this case the
+ <varname>$XDG_MENU_PREFIX</varname> environment variable must
+ be set by the system to reflect the .menu file that is being
+ used.
+ </para>
+ <para>
+ For example if a system contains both the GNOME and the KDE
+ desktop environments it can decide to use
+ gnome-applications.menu as the menu layout in GNOME sessions
+ and kde-applications.menu as the menu layout in KDE sessions.
+ To correctly reflect this, it should set the
+ <varname>$XDG_MENU_PREFIX</varname> environment variable to
+ "gnome-" respectively "kde-".
+ </para>
+ <para>
+ Implementations may chose to use .menu files with other names
+ for tasks or menus other than the main application menu. Such
+ usage is not covered by this specification.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>$XDG_CONFIG_DIRS</varname>/menus/applications-merged/</term>
+ <listitem>
+ <para>
+ The default merge directories included in the
+ &lt;DefaultMergeDirs&gt; element. By convention, third parties
+ may add new &lt;Menu&gt; files in this location to create their
+ own sub-menus.
+ </para>
+ <para>
+ Note that a system that uses either gnome-applications.menu or
+ kde-applications.menu depending on the desktop environment in
+ use must still use applications-merged as the default merge
+ directory in both cases.
+ </para>
+ <para>
+ Implementations may chose to use .menu files with names other
+ than application.menu for tasks or menus other than the main
+ application menu. In that case the first part of the name of
+ the default merge directory is derived from the name of the
+ .menu file.
+ </para>
+ <para>
+ For example in a system that uses a preferences.menu file to
+ describe an additional menu, the default merge directories
+ included in the &lt;DefaultMergeDirs&gt; element in the
+ preferences.menu file would become
+ <varname>$XDG_CONFIG_DIRS</varname>/menus/preferences-merged/
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>$XDG_DATA_DIRS</varname>/applications/</term>
+ <listitem>
+ <para>
+ This directory contains a .desktop file for each possible menu
+ item. Each directory in the <varname>$XDG_DATA_DIRS</varname>
+ search path should be used (i.e. desktop entries are collected
+ from all of them, not just the first one that exists). When two
+ desktop entries have the same name, the one appearing earlier in
+ the path is used.
+ </para>
+ <para>
+ The &lt;DefaultAppDirs&gt; element in a menu file indicates that
+ this default list of desktop entry locations should be scanned at
+ that point. If a menu file does not contain
+ &lt;DefaultAppDirs&gt;, then these locations are not scanned.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>$XDG_DATA_DIRS</varname>/desktop-directories/</term>
+ <listitem>
+ <para>
+ This directory contains directory entries which may be associated
+ with folders in the menu layout. Each directory
+ in the search path should be used. Only files ending in
+ .directory are used; other files are ignored.
+ </para>
+ <para>
+ The &lt;DefaultDirectoryDirs&gt; element in a menu file indicates that
+ this default list of directory entry locations should be scanned at
+ that point. If a menu file does not contain
+ &lt;DefaultDirectoryDirs&gt;, then these locations are not scanned.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </sect1>
+ <sect1 id="desktop-entry-extensions">
+ <title>Extensions to the desktop entry format</title>
+ <para>
+ This specification adds three new fields to <ulink
+ url="http://www.freedesktop.org/Standards/desktop-entry-spec">desktop
+ entries</ulink>: <varname>Categories</varname>,
+ <varname>OnlyShowIn</varname> and <varname>NotShowIn</varname>.
+ </para>
+ <para>
+ The <varname>Categories</varname> field is a list of strings used to
+ classify menu items. For example, applications in the
+ <literal>AudioVideo</literal> category might end up in the
+ "Sound &amp; Video" submenu. <xref linkend="category-registry"/>
+ enumerates the standard categories. Categories not in this document
+ must be prefixed by the string "X-" indicating that they are extensions.
+ Categories are case-sensitive.
+ </para>
+ <para>
+ Desktop entries should list all categories that clearly apply. They should
+ not list categories that only vaguely or possibly apply, because the user
+ will end up seeing the same desktop entry in a half-dozen places. But
+ it's typical that several categories will apply to a given desktop entry.
+ </para>
+ <para>
+ The <varname>OnlyShowIn</varname> field is a list of strings identifying
+ the environments that should display a given menu item. If an
+ <varname>OnlyShowIn</varname> field is present, a given environment should
+ only display the menu item if the string identifying that environment is
+ present. The strings are case-sensitive. <xref
+ linkend="onlyshowin-registry"/> enumerates the strings to use for
+ some common environments.
+ </para>
+ <para>
+ The <varname>NotShowIn</varname> field is a list of strings identifying
+ the environments that should not display a given menu item. If an
+ <varname>NotShowIn</varname> field is present, a given environment should
+ only display the menu item if the string identifying that environment is
+ not present. The strings are case-sensitive. <xref
+ linkend="onlyshowin-registry"/> enumerates the strings to use for
+ some common environments.
+ </para>
+ <para>
+ Environments not in this document must be prefixed by the string "X-"
+ indicating that they are extensions. Environments are case-sensitive.
+ </para>
+ <sect2 id="desktop-entry-extensions-examples">
+ <title>Examples of using <varname>Categories</varname> and <varname>OnlyShowIn</varname></title>
+ <para>
+ A desktop entry for a Qt-based image viewer might contain
+ this <varname>Categories</varname> line:
+ <informalexample>
+ <programlisting>
+ Categories=Qt;Graphics;RasterGraphics;Viewer;
+ </programlisting>
+ </informalexample>
+ </para>
+ <para>
+ A desktop entry for Octave, a command-line mathematics program (which
+ would also have the field <literal>Terminal=true</literal>), might have:
+ <informalexample>
+ <programlisting>
+ Categories=ConsoleOnly;Math;
+ </programlisting>
+ </informalexample>
+ </para>
+ <para>
+ A desktop entry for a GNOME-specific calculator program
+ that should only appear in GNOME might have:
+ <informalexample>
+ <programlisting>
+ Categories=GNOME;Utility;
+ OnlyShowIn=GNOME;
+ </programlisting>
+ </informalexample>
+ Note that the <varname>OnlyShowIn</varname> field is a
+ <emphasis>list</emphasis> and thus ends in a semicolon.
+ </para>
+ </sect2>
+ </sect1>
+ <sect1 id="menu-file-format">
+ <title>Format of menu files</title>
+ <para>
+ Menu files must be well-formed XML files and end in the extension
+ ".menu". They should also conform to the menu file DTD which implies
+ that implementation-specific extensions to the file format are not
+ allowed. Implementations may stop processing if they encounter a menu
+ file which does not comply with the associated DTD. Note that the
+ associated DTD may differ in version from the one defined in this document.
+ </para>
+ <para>
+ When an implementation updates an existing menu file it may need to
+ update the identifier to a newer version of the DTD. Implementations
+ should never update the identifier of an existing menu file to an
+ older version. In order to remain compatible with newer versions,
+ implementations should ignore and preserve any XML elements,
+ attributes and attribute values that it does not know how to handle.
+ </para>
+ <sect2 id="menu-file-doctype">
+ <title>Document Type Declaration</title>
+ <para>
+ Menu files for this version of the specification must use the following
+ namespace, public and system identifiers:
+ <variablelist>
+ <varlistentry>
+ <term>Namespace</term>
+ <listitem>
+ <para>
+ <literal>http://www.freedesktop.org/standards/menu</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Public Identifier for &version;</term>
+ <listitem>
+ <para>
+ <literal>PUBLIC "-//freedesktop//DTD Menu &dtd-version;//EN"</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>System Identifier for &version;</term>
+ <listitem>
+ <para>
+<literal>http://www.freedesktop.org/standards/menu-spec/menu-&dtd-version;.dtd</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ Here is a sample document type declaration:
+ <informalexample>
+ <programlisting>
+ &lt;!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu &dtd-version;//EN"
+ "http://www.freedesktop.org/standards/menu-spec/menu-&dtd-version;.dtd"&gt;
+ </programlisting>
+ </informalexample>
+
+ All menu files MUST include the document type declaration, so that
+ implementations can adapt to different versions of this specification
+ (and so implementations can validate the menu file against
+ the DTD).
+ </para>
+ </sect2>
+ <sect2 id="menu-file-elements">
+ <title>Elements</title>
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>&lt;Menu&gt;</term>
+ <listitem>
+ <para>
+ The root element is &lt;Menu&gt;. Each &lt;Menu&gt; element may
+ contain any number of nested &lt;Menu&gt; elements, indicating submenus.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;AppDir&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear below &lt;Menu&gt;. The
+ content of this element is a directory name. Desktop entries
+ in this directory are scanned and added to the pool of entries
+ which can be included in this &lt;Menu&gt; and its submenus.
+ Only files ending in ".desktop" should be used, other files are
+ ignored.
+ </para>
+ <para>
+ Desktop entries in the pool of available entries are identified
+ by their <firstterm>desktop-file id</firstterm> (see <xref
+ linkend="term-desktop-file-id"/>).
+ The desktop-file id of a desktop entry is equal to its filename,
+ with any path components removed.
+ So given a &lt;AppDir&gt;
+ <filename>/foo/bar</filename> and desktop entry
+ <filename>/foo/bar/Hello.desktop</filename> the desktop
+ entry would get a desktop-file id of <filename>Hello.desktop</filename>
+ </para>
+ <para>
+ If the directory contains sub-directories then these sub-directories
+ should be (recursively) scanned as well. The name of the subdirectory
+ should be added as prefix to the desktop-file id together with a dash character ("-")
+ So given a &lt;AppDir&gt;
+ <filename>/foo/bar</filename> and desktop entry
+ <filename>/foo/bar/booz/Hello.desktop</filename> the desktop
+ entry would get a desktop-file id of <filename>booz-Hello.desktop</filename>
+ A desktop entry <filename>/foo/bar/bo/oz/Hello.desktop</filename> would result
+ in a desktop-file id of <filename>bo-oz-Hello.desktop</filename>
+ </para>
+ <para>
+ &lt;AppDir&gt; elements appearing later in the menu file have
+ priority in case of collisions between desktop-file ids.
+ </para>
+ <para>
+ If the filename given as an &lt;AppDir&gt; is not an absolute
+ path, it should be located relative to the location of the menu
+ file being parsed.
+ </para>
+ <para>
+ Duplicate &lt;AppDir&gt; elements (that specify the same
+ directory) should be ignored, but the <emphasis>last</emphasis>
+ duplicate in the file should be used when establishing the order
+ in which to scan the directories. This is important when merging
+ (see <xref linkend="merge-algorithm"/>). The order of
+ &lt;AppDir&gt; elements with respect to &lt;Include&gt; and
+ &lt;Exclude&gt; elements is not relevant, also to facilitate
+ merging.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;DefaultAppDirs&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear below &lt;Menu&gt;. The element has
+ no content. The element should be treated as if it were a list
+ of &lt;AppDir&gt; elements containing the default app dir
+ locations
+ (<replaceable>datadir</replaceable>/applications/ etc.). When expanding
+ &lt;DefaultAppDirs&gt; to a list of &lt;AppDir&gt;, the default
+ locations that are earlier in the search path go later in the
+ &lt;Menu&gt; so that they have priority.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;DirectoryDir&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear below &lt;Menu&gt;. The content of
+ this element is a directory name. Each directory listed in a
+ &lt;DirectoryDir&gt; element will be searched for directory
+ entries to be used when resolving the &lt;Directory&gt; element
+ for this menu and its submenus.
+ If the filename given as a &lt;DirectoryDir&gt; is not an absolute path,
+ it should be located relative to the location
+ of the menu file being parsed.
+ </para>
+ <para>
+ Directory entries in the pool of available entries are identified
+ by their <firstterm>relative path</firstterm> (see <xref
+ linkend="term-relative-path"/>).
+ </para>
+ <para>
+ If two directory entries have duplicate relative paths, the one from
+ the last (furthest down) element in the menu file must be used.
+ Only files ending in the extension ".directory" should be
+ loaded, other files should be ignored.
+ </para>
+ <para>
+ Duplicate &lt;DirectoryDir&gt; elements (that specify the same
+ directory) are handled as with duplicate &lt;AppDir&gt;
+ elements (the last duplicate is used).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;DefaultDirectoryDirs&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear below &lt;Menu&gt;. The element has
+ no content. The element should be treated as if it were a list
+ of &lt;DirectoryDir&gt; elements containing the default desktop dir
+ locations
+ (<replaceable>datadir</replaceable>/desktop-directories/ etc.). The default
+ locations that are earlier in the search path go later in the
+ &lt;Menu&gt; so that they have priority.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Name&gt;</term>
+ <listitem>
+ <para>
+ Each &lt;Menu&gt; element must have a single &lt;Name&gt;
+ element. The content of the &lt;Name&gt; element is a name to
+ be used when referring to the given menu. Each submenu of a
+ given &lt;Menu&gt; must have a unique name. &lt;Menu&gt;
+ elements can thus be referenced by a menu path, for example
+ "Applications/Graphics." The &lt;Name&gt; field must not contain
+ the slash character ("/"); implementations should discard
+ any name containing a slash. See also <xref linkend="term-menu-path"/>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Directory&gt;</term>
+ <listitem>
+ <para>
+ Each &lt;Menu&gt; element has any number of &lt;Directory&gt;
+ elements. The content of the &lt;Directory&gt; element
+ is the relative path of a directory entry containing meta information
+ about the &lt;Menu&gt;, such as its icon and localized name.
+ If no &lt;Directory&gt; is specified for a &lt;Menu&gt;,
+ its &lt;Name&gt; field should be used as the user-visible
+ name of the menu.
+ </para>
+ <para>
+ Duplicate &lt;Directory&gt; elements are allowed in order
+ to simplify menu merging, and allow user menus to override
+ system menus. The last &lt;Directory&gt; element to appear
+ in the menu file "wins" and other elements are ignored,
+ unless the last element points to a nonexistent directory
+ entry, in which case the previous element should be tried instead,
+ and so on.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;OnlyUnallocated&gt; and &lt;NotOnlyUnallocated&gt;</term>
+ <listitem>
+ <para>
+ Each &lt;Menu&gt; may contain any number of
+ &lt;OnlyUnallocated&gt; and &lt;NotOnlyUnallocated&gt;
+ elements. Only the last such element to appear is relevant, as
+ it determines whether the &lt;Menu&gt; can contain any desktop
+ entries, or only those desktop entries that do not match other
+ menus. If neither &lt;OnlyUnallocated&gt; nor
+ &lt;NotOnlyUnallocated&gt; elements are present, the default
+ is &lt;NotOnlyUnallocated&gt;.
+ </para>
+ <para>
+ To handle &lt;OnlyUnallocated&gt;, the menu file must be
+ analyzed in two conceptual passes. The first pass processes
+ &lt;Menu&gt; elements that can match any desktop entry. During
+ this pass, each desktop entry is marked as allocated according
+ to whether it was matched by an &lt;Include&gt; rule in some
+ &lt;Menu&gt;. The second pass processes only &lt;Menu&gt;
+ elements that are restricted to unallocated desktop entries.
+ During the second pass, queries may only match desktop entries
+ that were not marked as allocated during the first pass.
+ See <xref linkend="query-algorithm"/>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Deleted&gt; and &lt;NotDeleted&gt;</term>
+ <listitem>
+ <para>
+ Each &lt;Menu&gt; may contain any number of &lt;Deleted&gt; and
+ &lt;NotDeleted&gt; elements. Only the last such element to
+ appear is relevant, as it determines whether the &lt;Menu&gt;
+ has been deleted. If neither &lt;Deleted&gt; nor
+ &lt;NotDeleted&gt; elements are present, the default is
+ &lt;NotDeleted&gt;. The purpose of this element is to support
+ menu editing. If a menu contains a &lt;Deleted&gt; element
+ not followed by a &lt;NotDeleted&gt; element, that menu
+ should be ignored.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Include&gt;</term>
+ <listitem>
+ <para>
+ An &lt;Include&gt; element is a set of rules attempting to match
+ some of the known desktop entries. The &lt;Include&gt; element
+ contains a list of any number of matching rules. Matching rules
+ are specified using the elements &lt;And&gt;, &lt;Or&gt;,
+ &lt;Not&gt;, &lt;All&gt;, &lt;Filename&gt;, and
+ &lt;Category&gt;. Each rule in a list of rules has a logical OR
+ relationship, that is, desktop entries which match any rule
+ are included in the menu.
+ </para>
+ <para>
+ &lt;Include&gt; elements must appear immediately under
+ &lt;Menu&gt; elements. The desktop entries they match are
+ included in the menu. &lt;Include&gt; and &lt;Exclude&gt;
+ elements for a given &lt;Menu&gt; are processed in order,
+ with queries earlier in the file handled first. This has
+ implications for merging, see <xref linkend="merge-algorithm"/>.
+ See <xref linkend="query-algorithm"/> for full details on
+ how to process &lt;Include&gt; and &lt;Exclude&gt; elements.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Exclude&gt;</term>
+ <listitem>
+ <para>
+ Any number of &lt;Exclude&gt; elements may appear below a
+ &lt;Menu&gt; element. The content of an &lt;Exclude&gt; element
+ is a list of matching rules, just as with an
+ &lt;Include&gt;. However, the desktop entries matched are
+ removed from the list of desktop entries included so far. (Thus
+ an &lt;Exclude&gt; element that appears before any
+ &lt;Include&gt; elements will have no effect, for example, as no
+ desktop entries have been included yet.)
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Filename&gt;</term>
+ <listitem>
+ <para>
+ The &lt;Filename&gt; element is the most basic matching rule.
+ It matches a desktop entry if the desktop entry has the given
+ desktop-file id. See <xref linkend="term-desktop-file-id"/>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Category&gt;</term>
+ <listitem>
+ <para>
+ The &lt;Category&gt; element is another basic matching
+ predicate. It matches a desktop entry if the desktop entry has
+ the given category in its <varname>Categories</varname> field.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;All&gt;</term>
+ <listitem>
+ <para>
+ The &lt;All&gt; element is a matching rule that matches
+ all desktop entries.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;And&gt;</term>
+ <listitem>
+ <para>
+ The &lt;And&gt; element contains a list of matching rules.
+ If each of the matching rules inside the &lt;And&gt;
+ element match a desktop entry, then the entire
+ &lt;And&gt; rule matches the desktop entry.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Or&gt;</term>
+ <listitem>
+ <para>
+ The &lt;Or&gt; element contains a list of matching rules.
+ If any of the matching rules inside the &lt;Or&gt;
+ element match a desktop entry, then the entire
+ &lt;Or&gt; rule matches the desktop entry.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Not&gt;</term>
+ <listitem>
+ <para>
+ The &lt;Not&gt; element contains a list of matching rules. If
+ any of the matching rules inside the &lt;Not&gt; element matches
+ a desktop entry, then the entire &lt;Not&gt; rule does
+ <emphasis>not</emphasis> match the desktop entry. That is,
+ matching rules below &lt;Not&gt; have a logical OR relationship.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;MergeFile [type="path"|"parent"] &gt;</term>
+ <listitem>
+ <para>
+ Any number of &lt;MergeFile&gt; elements may be listed below a
+ &lt;Menu&gt; element, giving the name of another menu file to
+ be merged into this one. <xref linkend="merge-algorithm"/>
+ specifies how merging is done. The root &lt;Menu&gt; of the
+ merged file will be merged into the immediate parent of the
+ &lt;MergeFile&gt; element. The &lt;Name&gt; element of the
+ root &lt;Menu&gt; of the merged file are ignored.
+ </para>
+ <para>
+ If the type attribute is missing or set to "path" then the
+ contents of the &lt;MergeFile&gt; element indicates the
+ file to be merged. If this is not an absolute path then the
+ file to be merged should be located relative to the location
+ of the menu file that contains this &lt;MergeFile&gt; element.
+ </para>
+ <para>
+ Duplicate &lt;MergeFile&gt; elements (that specify the same
+ file) are handled as with duplicate &lt;AppDir&gt;
+ elements (the last duplicate is used).
+ </para>
+ <para>
+ If the type attribute is set to "parent" and the file that
+ contains this &lt;MergeFile&gt; element is located under one
+ of the paths specified by <varname>$XDG_CONFIG_DIRS</varname>,
+ the contents of the element should be ignored and the remaining
+ paths specified by <varname>$XDG_CONFIG_DIRS</varname> are
+ searched for a file with the same relative filename. The first
+ file encountered should be merged. There should be no
+ merging at all if no matching file is found.
+ </para>
+ <para>
+ Compatibility note: The filename specified inside the &lt;MergeFile&gt;
+ element should be ignored if the type attribute is set to "parent",
+ it should however be expected that implementations based on
+ previous versions of this specification will ignore the
+ type attribute and that such implementations will use the
+ filename inside the &lt;MergeFile&gt; element instead.
+ </para>
+ <para>
+ Example 1: If <varname>$XDG_CONFIG_HOME</varname> is "~/.config/" and
+ <varname>$XDG_CONFIG_DIRS</varname> is "/opt/gnome/:/etc/xdg/"
+ and the file ~/.config/menus/applications.menu contains
+ &lt;MergeFile type="parent"&gt;/opt/kde3/etc/xdg/menus/applications.menu&lt;/MergeFile&gt;
+ then the file /opt/gnome/menus/applications.menu
+ should be merged if it exists. If that file does not exists
+ then the file /etc/xdg/menus/applications.menu
+ should be merged instead.
+ </para>
+ <para>
+ Example 2: If <varname>$XDG_CONFIG_HOME</varname> is "~/.config/" and
+ <varname>$XDG_CONFIG_DIRS</varname> is "/opt/gnome/:/etc/xdg/"
+ and the file /opt/gnome/menus/applications.menu contains
+ &lt;MergeFile type="parent"&gt;/opt/kde3/etc/xdg/menus/applications.menu&lt;/MergeFile&gt;
+ then the file /etc/xdg/menus/applications.menu should be merged
+ if it exists.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;MergeDir&gt;</term>
+ <listitem>
+ <para>
+ Any number of &lt;MergeDir&gt; elements may be listed below a
+ &lt;Menu&gt; element. A &lt;MergeDir&gt; contains the name of a
+ directory. Each file in the given directory which ends in the
+ ".menu" extension should be merged in the same way that a
+ &lt;MergeFile&gt; would be. If the filename given as a
+ &lt;MergeDir&gt; is not an absolute path, it should be located
+ relative to the location of the menu file being parsed.
+ The files inside the merged directory are not merged in any
+ specified order.
+ </para>
+ <para>
+ Duplicate &lt;MergeDir&gt; elements (that specify the same
+ directory) are handled as with duplicate &lt;AppDir&gt;
+ elements (the last duplicate is used).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;DefaultMergeDirs&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear below &lt;Menu&gt;. The element has
+ no content. The element should be treated as if it were a list
+ of &lt;MergeDir&gt; elements containing the default merge
+ directory locations. When expanding &lt;DefaultMergeDirs&gt; to a
+ list of &lt;MergeDir&gt;, the default locations that are earlier
+ in the search path go later in the &lt;Menu&gt; so that they
+ have priority.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;LegacyDir&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear below &lt;Menu&gt;. The text
+ content of this element is a directory name. Each directory
+ listed in a &lt;LegacyDir&gt; element will be an old-style
+ legacy hierarchy of desktop entries, see <xref
+ linkend="legacy-hierarchies"/> for how to load such a
+ hierarchy. Implementations must not load legacy hierarchies that
+ are not explicitly specified in the menu file (because for
+ example the menu file may not be the main menu). If the
+ filename given as a &lt;LegacyDir&gt; is not an absolute path,
+ it should be located relative to the location of the menu file
+ being parsed.
+ </para>
+ <para>
+ Duplicate &lt;LegacyDir&gt; elements (that specify the same
+ directory) are handled as with duplicate &lt;AppDir&gt;
+ elements (the last duplicate is used).
+ </para>
+ <para>
+ The &lt;LegacyDir&gt; element may have one attribute,
+ <literal>prefix</literal>. Normally, given a &lt;LegacyDir&gt;
+ <filename>/foo/bar</filename> and desktop entry
+ <filename>/foo/bar/baz/Hello.desktop</filename> the desktop
+ entry would get a desktop-file id of <filename>Hello.desktop</filename>.
+ Given a prefix of <literal>boo-</literal>, it would instead be
+ assigned the desktop-file id <filename>boo-Hello.desktop</filename>.
+ The prefix should not contain path separator ('/') characters.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;KDELegacyDirs&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear below &lt;Menu&gt;. The element has
+ no content. The element should be treated as if it were a list
+ of &lt;LegacyDir&gt; elements containing the traditional desktop
+ file locations supported by KDE with a hard coded prefix of "kde-".
+ When expanding &lt;KDELegacyDirs&gt; to a list of &lt;LegacyDir&gt;, the
+ locations that are earlier in the search path go later in the
+ &lt;Menu&gt; so that they have priority.
+ The search path can be obtained by running <filename>kde-config --path apps</filename>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Move&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear below &lt;Menu&gt;. The
+ &lt;Move&gt; element contains pairs of &lt;Old&gt;/&lt;New&gt;
+ elements indicating how to rename a descendant of the current
+ &lt;Menu&gt;. If the destination path already exists, the moved
+ menu is merged with the destination menu (see <xref
+ linkend="merge-algorithm"/> for details).
+ </para>
+ <para>
+ &lt;Move&gt; is used primarily to fix up legacy directories.
+ For example, say you are merging a &lt;LegacyDir&gt; with folder
+ names that don't match the current hierarchy; the legacy folder
+ names can be moved to the new names, where they will be merged
+ with the new folders.
+ </para>
+ <para>
+ &lt;Move&gt; is also useful for implementing menu
+ editing, see <xref linkend="menu-editing"/>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Old&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear below &lt;Move&gt;, and
+ must be followed by a &lt;New&gt; element. The content of both
+ &lt;Old&gt; and &lt;New&gt; should be a menu path
+ (slash-separated concatenation of &lt;Name&gt; fields, see
+ <xref linkend="term-menu-path"/>).
+ Paths are interpreted relative to the menu containing
+ the &lt;Move&gt; element.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;New&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear below &lt;Move&gt;, and must
+ be preceded by an &lt;Old&gt; element. The &lt;New&gt; element
+ specifies the new path for the preceding &lt;Old&gt; element.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Layout&gt;</term>
+ <listitem>
+ <para>
+ The &lt;Layout&gt; element is an optional part of this specification.
+ Implementations that do not support the &lt;Layout&gt; element should
+ preserve any &lt;Layout&gt; elements and their contents as far as
+ possible.
+ Each &lt;Menu&gt; may optionally contain a &lt;Layout&gt; element.
+ If multiple elements appear then only the last such element is relevant.
+ The purpose of this element is to offer suggestions for the presentation
+ of the menu.
+ If a menu does not contain a &lt;Layout&gt; element or if it contains
+ an empty &lt;Layout&gt; element then the default layout should be used.
+ The &lt;Layout&gt; element may contain &lt;Filename&gt;, &lt;Menuname&gt;,
+ &lt;Separator&gt; and &lt;Merge&gt; elements. The &lt;Layout&gt; element
+ defines a suggested layout for the menu starting from top to bottom.
+ References to desktop entries that are not contained in this
+ menu as defined by the &lt;Include&gt; and &lt;Exclude&gt; elements should
+ be ignored. References to sub-menus that are not directly contained in this
+ menu as defined by the &lt;Menu&gt; elements should be ignored.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;DefaultLayout [show_empty="false"] [inline="false"] [inline_limit="4"] [inline_header="true"] [inline_alias="false"]&gt;</term>
+ <listitem>
+ <para>
+ The &lt;DefaultLayout&gt; element is an optional part of this specification.
+ Implementations that do not support the &lt;DefaultLayout&gt; element should
+ preserve any &lt;DefaultLayout&gt; elements and their contents as far as
+ possible.
+ Each &lt;Menu&gt; may optionally contain a &lt;DefaultLayout&gt; element
+ which defines the default-layout for the current menu and all its sub-menus.
+ If a menu has a &lt;DefaultLayout&gt; element then this will override
+ any default-layout specified by a parent menu.
+ The default-layout defines the suggested layout if a &lt;Menu&gt; element
+ does either not have &lt;Layout&gt; element or if it has an empty &lt;Layout&gt; element.
+ For explanations of the various attributes see the &lt;Menuname&gt; element.
+ If no default-layout has been specified then the layout as specified by the following
+ elements should be assumed:
+ &lt;DefaultLayout show_empty="false" inline="false" inline_limit="4" inline_header="true" inline_alias="false"&gt;&lt;Merge type="menus"/&gt;&lt;Merge type="files"/&gt;&lt;/DefaultLayout&gt;
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Menuname [show_empty="..."] [inline="..."] [inline_limit="..."] [inline_header="..."] [inline_alias="..."]&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear as a child of a &lt;Layout&gt; or &lt;DefaultLayout&gt;
+ menu. Its contents references an immediate sub-menu of the current menu as defined
+ with the &lt;Menu&gt; element, as such it should never contain a slash.
+ If no such sub-menu exists the element should be ignored.
+ This element may have various attributes, the default values are taken from the DefaultLayout key.
+ The show_empty attribute defines whether a menu that contains no desktop
+ entries and no sub-menus should be shown at all. The show_empty attribute
+ can be "true" or "false".
+ It may have an inline attribute that can be either "true" or "false".
+ If the inline attribute is "true" the menu that is referenced may be copied into the
+ current menu at the current point instead of being inserted as sub-menu of the current menu.
+ The optional inline_limit attribute defines the maximum number of entries that can be inlined.
+ If the sub-menu has more entries than inline_limit, the sub-menu will not be inlined.
+ If the inline_limit is 0 (zero) there is no limit.
+ The optional inline_header attribute defines whether an inlined menu should be preceded with
+ a header entry listing the caption of the sub-menu.
+ The inline_header attribute can be either "true" or "false".
+ The optional inline_alias attribute defines whether a single inlined entry should adopt the
+ caption of the inlined menu. In such case no additional header entry will be added regardless
+ of the value of the inline_header attribute.
+ The inline_alias attribute can be either "true" or "false".
+ Example: if a menu has a sub-menu titled "WordProcessor" with a single entry "OpenOffice 4.2",
+ and both inline="true" and inline_alias="true" are specified then this would result in the
+ "OpenOffice 4.2" entry being inlined in the current menu but the "OpenOffice 4.2" caption
+ of the entry would be replaced with "WordProcessor".
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Separator&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear as a child of a &lt;Layout&gt; or &lt;DefaultLayout&gt;
+ menu. It indicates a suggestion to draw a visual separator at this point in the menu.
+ &lt;Separator&gt; elements at the start of a menu, at the end of a menu or that directly
+ follow other &lt;Separator&gt; elements may be ignored.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Merge type="menus"|"files"|"all"/&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear as a child of a &lt;Layout&gt; or &lt;DefaultLayout&gt;
+ menu. It indicates the point where desktop entries and sub-menus that are not explicitly
+ mentioned within the &lt;Layout&gt; or &lt;DefaultLayout&gt; element are to be inserted.
+ It has a type attribute that indicates which elements should be inserted:
+ type="menus"
+ means that all sub-menus that are not explicitly mentioned should be inserted in
+ alphabetical order of their visual caption at this point.
+ type="files"
+ means that all desktop entries
+ contained in this menu that are not explicitly mentioned should be inserted in
+ alphabetical order of their visual caption at this point.
+ type="all" means that a mix of all sub-menus
+ and all desktop entries that are not explicitly mentioned should be inserted in
+ alphabetical order of their visual caption at this point.
+ Each &lt;Layout&gt; or &lt;DefaultLayout&gt; element shall have exactly one &lt;Merge type="all"&gt;
+ element or it shall have exactly one &lt;Merge type="files"&gt; and exactly one
+ &lt;Merge type="menus"&gt; element. An exception is made for a completely empty &lt;Layout&gt;
+ element which may be used to indicate that the default-layout should be used instead.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </sect2>
+ </sect1>
+
+ <sect1 id="merge-algorithm">
+ <title>Merging</title>
+ <para>
+ Sometimes two menu layouts need to be merged. This is done when folding in
+ legacy menu hierarchies (see <xref linkend="legacy-hierarchies"/>) and also
+ for files specified in &lt;MergeFile&gt; elements. A common case is that
+ per-user menu files might merge the system menu file. Merging is also used
+ to avoid cut-and-paste, for example to include a common submenu in
+ multiple menu files.
+ </para>
+ <para>
+ Merging involves a base &lt;Menu&gt; and a merged &lt;Menu&gt;. The base
+ is the "target" menu and the merged &lt;Menu&gt; is being added to it. The
+ result of the merge is termed the "combined menu."
+ </para>
+ <para>
+ As a preparatory step, the goal is to resolve all files into
+ XML elements. To do so, traverse the entire menu tree. For each
+ &lt;MergeFile&gt;, &lt;MergeDir&gt;, or &lt;LegacyDir&gt; element, replace
+ the &lt;MergeFile&gt;, &lt;MergeDir&gt;, or &lt;LegacyDir&gt; element with
+ the child elements of the root &lt;Menu&gt; of the file(s) being
+ merged. As a special exception, remove the &lt;Name&gt; element from the
+ root element of each file being merged. To generate a
+ &lt;Menu&gt; based on a &lt;LegacyDir&gt;, see
+ <xref linkend="legacy-hierarchies"/>.
+ </para>
+ <para>
+ Continue processing until no &lt;MergeFile&gt;, &lt;MergeDir&gt;, or
+ &lt;LegacyDir&gt; elements remain, taking care to avoid infinite loops
+ caused by files that reference one another.
+ </para>
+ <para>
+ Once all files have been loaded into a single tree, scan the tree
+ recursively performing these steps to remove duplicates:
+ <orderedlist>
+ <listitem>
+ <para>
+ Consolidate child menus. Each group of child &lt;Menu&gt;s with the same
+ name must be consolidated into a single child menu with that name.
+ Concatenate the child elements of all menus with the same name, in
+ the order that they appear, and insert those elements as the
+ children of the <emphasis>last</emphasis> menu with that name.
+ Delete all the newly empty &lt;Menu&gt; elements, keeping the
+ last one.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Expand &lt;DefaultAppDirs&gt; and &lt;DefaultDirectoryDirs&gt;
+ elements to &lt;AppDir&gt; and &lt;DirectoryDir&gt; elements.
+ Consolidate duplicate &lt;AppDir&gt;, &lt;DirectoryDir&gt;,
+ and &lt;Directory&gt; elements by keeping the last one.
+ For &lt;Directory&gt; elements that refer to distinct directory
+ entries, all of them should be kept - if the last one points
+ to a nonexistent file, the one before that can be used instead,
+ and so forth.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Recurse into each child &lt;Menu&gt;, performing this list of
+ steps for each child in order.
+ </para>
+ </listitem>
+ </orderedlist>
+ </para>
+ <para>
+ After recursing once to remove duplicates, recurse a second time to
+ resolve &lt;Move&gt; elements for each menu starting with any child
+ menu before handling the more top level menus.
+ So the deepest menus have their &lt;Move&gt; operations performed first.
+ Within each &lt;Menu&gt;, execute &lt;Move&gt; operations in the order
+ that they appear. If the destination path does not exist, simply relocate
+ the origin &lt;Menu&gt; element, and change its &lt;Name&gt; field to
+ match the destination path. If the origin path does not exist, do
+ nothing. If both paths exist, take the origin &lt;Menu&gt; element,
+ delete its &lt;Name&gt; element, and prepend its remaining child elements
+ to the destination &lt;Menu&gt; element.
+ </para>
+ <para>
+ If any &lt;Move&gt; operations affect a menu, then re-run the
+ steps to resolve duplicates in case any duplicates have been
+ created.
+ </para>
+ <para>
+ Finally, for each &lt;Menu&gt; containing a &lt;Deleted&gt; element which
+ is not followed by a &lt;NotDeleted&gt; element, remove that menu and all
+ its child menus.
+ </para>
+
+ <para>
+ Merged menu elements are kept in order because &lt;Include&gt; and
+ &lt;Exclude&gt; elements later in the file override &lt;Include&gt; and
+ &lt;Exclude&gt; elements earlier in the file. This means that if the user's
+ menu file merges the system menu file, the user can always override what
+ the system menu specifies by placing elements after the &lt;MergeFile&gt;
+ that incorporates the system file.
+ </para>
+ <para>
+ To prevent that a desktop entry from one party inadvertently cancels out
+ the desktop entry from another party because both happen to get the same
+ desktop-file id it is recommended that providers of desktop-files ensure that
+ all desktop-file ids start with a vendor prefix. A vendor prefix
+ consists of [a-zA-Z] and is terminated with a dash ("-"). Open Source
+ projects and commercial parties are encouraged to use a word or phrase,
+ preferably their name, as prefix for which they hold a trademark. Open Source
+ applications can also ask to make use of the vendor prefix of another open
+ source project (such as GNOME or KDE) they consider themselves affiliated
+ with, at the discretion of these projects.
+ </para>
+ <para>
+ For example, to ensure that GNOME applications start with a vendor prefix of "gnome-",
+ it could either add "gnome-" to all the desktop files it installs in
+ <filename><replaceable>datadir</replaceable>/applications/</filename> or it could
+ install desktop files in a <filename><replaceable>datadir</replaceable>/applications/gnome</filename>
+ subdirectory. When including legacy menu hierarchies the <literal>prefix</literal> argument
+ of the &lt;LegacyDir&gt; element can be used to specify a prefix.
+ </para>
+ </sect1>
+
+ <sect1 id="query-algorithm">
+ <title>Generating the menus</title>
+ <para>
+ After merging the menus, the result should be a single menu layout
+ description. For each &lt;Menu&gt;, we have a list of directories where
+ desktop entries can be found, a list of directories where directory
+ entries can be found, and a series of &lt;Include&gt; and &lt;Exclude&gt;
+ directives.
+ </para>
+ <para>
+ For each &lt;Menu&gt; element, build a pool of desktop entries by
+ collecting entries found in each &lt;AppDir&gt; for the menu element. If
+ two entries have the same desktop-file id, the entry for the earlier (closer
+ to the top of the file) &lt;AppDir&gt; must be discarded. Next, add to the
+ pool the entries for any &lt;AppDir&gt;s specified by ancestor
+ &lt;Menu&gt; elements. If a parent menu has a duplicate entry (same
+ desktop-file id), the entry for the child menu has priority.
+ </para>
+ <para>
+ Next, walk through all &lt;Include&gt; and &lt;Exclude&gt; statements.
+ For each &lt;Include&gt;, match the rules against the pool of all desktop
+ entries. For each desktop entry that matches one of the rules,
+ add it to the menu to be displayed and mark it as having been allocated.
+ For each &lt;Exclude&gt;, match the rules against the currently-included
+ desktop entries. For each desktop entry that matches, remove it again
+ from the menu. Note that an entry that is included in a menu but excluded
+ again by a later &lt;Exclude&gt; is still considered allocated (for the
+ purposes of &lt;OnlyUnallocated&gt;) even though that entry no longer
+ appears in the menu.
+ </para>
+ <para>
+ Two passes are necessary, once for regular menus where any entry may
+ be matched, and once for &lt;OnlyUnallocated&gt; menus where only entries
+ which have not been marked as allocated may be matched.
+ </para>
+ <para>
+ The result is a tree of desktop entries, of course.
+ </para>
+ </sect1>
+
+ <sect1 id="legacy-hierarchies">
+ <title>Legacy Menu Hierarchies</title>
+ <para>
+ Traditionally, menus were defined as a filesystem hierarchy, with each
+ filesystem directory corresponding to a submenu. Implementations of this
+ specification must be able to load these old-style hierarchies
+ as specified in this section.
+ </para>
+ <para>
+ The general approach is: the legacy hierarchy is converted into a
+ &lt;Menu&gt;, and then this menu layout is merged with the menu that
+ specified &lt;LegacyDir&gt;.
+ </para>
+ <para>
+ Desktop entries in the legacy hierarchy should be added to the pool of
+ desktop entries as if the &lt;LegacyDir&gt; were an
+ &lt;AppDir&gt;. Directory entries in the legacy hierarchy should be added
+ to the pool of directory entries as if the &lt;LegacyDir&gt; were a
+ &lt;DirectoryDir&gt;. This can be trivially implemented by adding
+ appropriate &lt;AppDir&gt; and &lt;DirectoryDir&gt; statements to the root
+ legacy &lt;Menu&gt;. There is one slight complexity, namely the
+ "prefix" attribute of &lt;LegacyDir&gt;.
+ </para>
+ <para>
+ The menu layout corresponds conceptually to the following, though actually
+ generating the XML is not necessary:
+ <itemizedlist>
+ <listitem>
+ <para>
+ For each directory in the legacy hierarchy, a
+ &lt;Menu&gt; is created with the same &lt;Name&gt;
+ as the directory on disk.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ This menu then contains an &lt;Include&gt; element that includes
+ each desktop entry in the directory. That is, it should have a
+
+ &lt;Filename&gt;<replaceable>Foo/Bar/foo.desktop</replaceable>&lt;/Filename&gt;
+ for each desktop entry in the directory.
+ </para>
+ <para>
+ As a special exception, if a desktop entry in a directory contains
+ a <varname>Categories</varname> field, that desktop entry should
+ <emphasis>not</emphasis> be included in the legacy menu.
+ That is, no &lt;Include&gt; element should be generated for
+ the entry. This allows a desktop entry to be installed
+ in a legacy location but still work optimally with the
+ menu system specified in this document.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If the legacy directory contains a ".directory" file, then
+ a &lt;Directory&gt; element should be generated that points to said
+ ".directory" file.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Legacy desktop entries should not be assigned any
+ <varname>Categories</varname> fields if they didn't have them
+ already, except that all legacy entries should have the
+ "Legacy" category added to allow menu files to treat them
+ specially. (If the same directory is given as both
+ a &lt;LegacyDir&gt; and an &lt;AppDir&gt;, its desktop
+ entries should be labeled "Legacy" only if the &lt;LegacyDir&gt;
+ appears later in the file than the &lt;AppDir&gt;.)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ For example, say we have the following legacy directory hierarchy:
+ <informalexample>
+ <programlisting>
+ /usr/share/applnk
+ /usr/share/applnk/.directory
+ /usr/share/applnk/bar.desktop
+ /usr/share/applnk/System
+ /usr/share/applnk/System/.directory
+ /usr/share/applnk/System/foo.desktop
+ </programlisting>
+ </informalexample>
+ Conceptually that is converted to the following &lt;Menu&gt;:
+ <informalexample>
+ <programlisting>
+ &lt;!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu &dtd-version;//EN"
+ "http://www.freedesktop.org/standards/menu-spec/menu-&dtd-version;.dtd"&gt;
+
+ &lt;Menu&gt;
+ &lt;Name&gt;Applications&lt;/Name&gt;
+ &lt;AppDir&gt;/usr/share/applnk&lt;/AppDir&gt;
+ &lt;DirectoryDir&gt;/usr/share/applnk&lt;/DirectoryDir&gt;
+ &lt;Directory&gt;.directory&lt;/Directory&gt;
+ &lt;Include&gt;
+ &lt;Filename&gt;bar.desktop&lt;/Filename&gt;
+ &lt;/Include&gt;
+ &lt;Menu&gt;
+ &lt;Name&gt;System&lt;/Name&gt;
+ &lt;AppDir&gt;/usr/share/applnk/System&lt;/AppDir&gt;
+ &lt;DirectoryDir&gt;/usr/share/applnk/System&lt;/DirectoryDir&gt;
+ &lt;Directory&gt;.directory&lt;/Directory&gt;
+ &lt;Include&gt;
+ &lt;Filename&gt;foo.desktop&lt;/Filename&gt;
+ &lt;/Include&gt;
+ &lt;/Menu&gt;
+ &lt;/Menu&gt;
+ </programlisting>
+ </informalexample>
+ This &lt;Menu&gt; is then merged as if it were in a file
+ and loaded with &lt;MergeFile&gt;.
+ </para>
+ </sect1>
+
+ <sect1 id="example">
+ <title>Example Menu File</title>
+ <para>
+ <informalexample>
+ <programlisting>
+ &lt;!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu &dtd-version;//EN"
+ "http://www.freedesktop.org/standards/menu-spec/menu-&dtd-version;.dtd"&gt;
+
+ &lt;Menu&gt;
+ &lt;Name&gt;Applications&lt;/Name&gt;
+ &lt;Directory&gt;Applications.directory&lt;/Directory&gt;
+
+ &lt;-- Search the default locations --&gt;
+ &lt;DefaultAppDirs/&gt;
+ &lt;DefaultDirectoryDirs/&gt;
+
+ &lt;-- Merge third-party submenus --&gt;
+ &lt;MergeDir&gt;applications-merged&lt;/MergeDir&gt;
+
+ &lt;-- Merge legacy hierarchy --&gt;
+ &lt;LegacyDir&gt;/usr/share/applnk&lt;/LegacyDir&gt;
+
+ &lt;-- Define default layout --&gt;
+ &lt;DefaultLayout&gt;
+ &lt;Merge type="menus"/&gt;
+ &lt;Merge type="files"/&gt;
+ &lt;Separator/&gt;
+ &lt;Menuname&gt;More&lt;/Menuname&gt;
+ &lt;/DefaultLayout&gt;
+
+ &lt;-- some random moves, maybe to clean up legacy dirs,
+ maybe from menu editing --&gt;
+ &lt;Move&gt;
+ &lt;Old&gt;Foo&lt;/Old&gt;
+ &lt;New&gt;Bar&lt;/New&gt;
+ &lt;Old&gt;Foo2&lt;/Old&gt;
+ &lt;New&gt;Bar2&lt;/New&gt;
+ &lt;/Move&gt;
+
+ &lt;-- A preferences submenu, kept in a separate file
+ so it can also be used standalone --&gt;
+ &lt;Menu&gt;
+ &lt;Name&gt;Preferences&lt;/Name&gt;
+ &lt;Directory&gt;Preferences.directory&lt;/Directory&gt;
+ &lt;MergeFile&gt;preferences.menu&lt;/MergeFile&gt;
+ &lt;/Menu&gt;
+
+ &lt;-- An Office submenu, specified inline --&gt;
+ &lt;Menu&gt;
+ &lt;Name&gt;Office&lt;/Name&gt;
+ &lt;Directory&gt;Office.directory&lt;/Directory&gt;
+ &lt;Include&gt;
+ &lt;Category&gt;Office&lt;/Category&gt;
+ &lt;/Include&gt;
+ &lt;Exclude&gt;
+ &lt;Filename&gt;foo.desktop&lt;/Filename&gt;
+ &lt;/Exclude&gt;
+ &lt;/Menu&gt;
+
+ &lt;/Menu&gt;
+ </programlisting>
+ </informalexample>
+ </para>
+ </sect1>
+
+ <appendix id="category-registry">
+ <title>Registered Categories</title>
+ <para>
+ This section contains a number of well known categories and
+ suggestions on how to use them. The list of Main Categories consist
+ of those categories that every conforming desktop environment MUST
+ support. By including one of these categories in an application's
+ desktop entry file the application will be ensured that it will
+ show up in a section of the application menu dedicated to this
+ category. The list of Additional Categories provides categories
+ that can be used to provide more fine grained information about
+ the application.
+ </para>
+ <para>
+ Category-based menus based on the Main Categories listed in this
+ specification do not provide a complete ontology for all
+ available applications. Category-based menu implementations
+ SHOULD therefore provide a "catch-all" submenu for applications
+ that cannot be appropriately placed elsewhere.
+ </para>
+ <para>
+ The table below lists all Main Categories.
+ Note that category names are case-sensitive.
+ <informaltable>
+ <tgroup cols="3">
+ <thead>
+ <row>
+ <entry>Main Category</entry>
+ <entry>Description</entry>
+ <entry>Notes</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>AudioVideo</entry>
+ <entry>Application for presenting, creating, or processing multimedia (audio/video)</entry>
+ </row><row>
+ <entry>Audio</entry>
+ <entry>An audio application</entry>
+ <entry>Desktop entry must include AudioVideo as well</entry>
+ </row><row>
+ <entry>Video</entry>
+ <entry>A video application</entry>
+ <entry>Desktop entry must include AudioVideo as well</entry>
+ </row><row>
+ <entry>Development</entry>
+ <entry>An application for development</entry>
+ </row><row>
+ <entry>Education</entry>
+ <entry>Educational software</entry>
+ </row><row>
+ <entry>Game</entry>
+ <entry>A game</entry>
+ </row><row>
+ <entry>Graphics</entry>
+ <entry>Application for viewing, creating, or processing graphics</entry>
+ </row><row>
+ <entry>Network</entry>
+ <entry>Network application such as a web browser</entry>
+ </row><row>
+ <entry>Office</entry>
+ <entry>An office type application</entry>
+ </row><row>
+ <entry>Settings</entry>
+ <entry>Settings applications</entry>
+ <entry>Entries may appear in a separate menu or as part of a
+ "Control Center"</entry>
+ </row><row>
+ <entry>System</entry>
+ <entry>System application, "System Tools" such as say a log viewer or network monitor</entry>
+ </row><row>
+ <entry>Utility</entry>
+ <entry>Small utility application, "Accessories"</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </para>
+ <para>
+ The table below describes Additional Categories. The Related
+ Categories column lists one or more categories that are suggested
+ to be used in conjunction with the Additional Category. If multiple
+ Main Categories are included in a single
+ desktop entry file, the entry may appear more than once in the menu.
+ If the Related Categories column is blank, the
+ Additional Category can be used with any Main Category.
+ <informaltable>
+ <tgroup cols="3">
+ <thead>
+ <row>
+ <entry>Additional Category</entry>
+ <entry>Description</entry>
+ <entry>Related Categories</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Building</entry>
+ <entry>A tool to build applications</entry>
+ <entry>Development</entry>
+
+ </row><row>
+ <entry>Debugger</entry>
+ <entry>A tool to debug applications</entry>
+ <entry>Development</entry>
+
+ </row><row>
+ <entry>IDE</entry>
+ <entry>IDE application</entry>
+ <entry>Development</entry>
+
+ </row><row>
+ <entry>GUIDesigner</entry>
+ <entry>A GUI designer application</entry>
+ <entry>Development</entry>
+
+ </row><row>
+ <entry>Profiling</entry>
+ <entry>A profiling tool</entry>
+ <entry>Development</entry>
+
+ </row><row>
+ <entry>RevisionControl</entry>
+ <entry>Applications like cvs or subversion</entry>
+ <entry>Development</entry>
+
+ </row><row>
+ <entry>Translation</entry>
+ <entry>A translation tool</entry>
+ <entry>Development</entry>
+
+ </row><row>
+ <entry>Calendar</entry>
+ <entry>Calendar application</entry>
+ <entry>Office</entry>
+
+ </row><row>
+ <entry>ContactManagement</entry>
+ <entry>E.g. an address book</entry>
+ <entry>Office</entry>
+
+ </row><row>
+ <entry>Database</entry>
+ <entry>Application to manage a database</entry>
+ <entry>Office or Development or AudioVideo</entry>
+
+ </row><row>
+ <entry>Dictionary</entry>
+ <entry>A dictionary</entry>
+ <entry>Office;TextTools</entry>
+
+ </row><row>
+ <entry>Chart</entry>
+ <entry>Chart application</entry>
+ <entry>Office</entry>
+
+ </row><row>
+ <entry>Email</entry>
+ <entry>Email application</entry>
+ <entry>Office;Network</entry>
+
+ </row><row>
+ <entry>Finance</entry>
+ <entry>Application to manage your finance</entry>
+ <entry>Office</entry>
+
+ </row><row>
+ <entry>FlowChart</entry>
+ <entry>A flowchart application</entry>
+ <entry>Office</entry>
+
+ </row><row>
+ <entry>PDA</entry>
+ <entry>Tool to manage your PDA</entry>
+ <entry>Office</entry>
+
+ </row><row>
+ <entry>ProjectManagement</entry>
+ <entry>Project management application</entry>
+ <entry>Office;Development</entry>
+
+ </row><row>
+ <entry>Presentation</entry>
+ <entry>Presentation software</entry>
+ <entry>Office</entry>
+
+ </row><row>
+ <entry>Spreadsheet</entry>
+ <entry>A spreadsheet</entry>
+ <entry>Office</entry>
+
+ </row><row>
+ <entry>WordProcessor</entry>
+ <entry>A word processor</entry>
+ <entry>Office</entry>
+
+ </row><row>
+ <entry>2DGraphics</entry>
+ <entry>2D based graphical application</entry>
+ <entry>Graphics</entry>
+
+ </row><row>
+ <entry>VectorGraphics</entry>
+ <entry>Application for viewing, creating, or processing vector graphics</entry>
+ <entry>Graphics;2DGraphics</entry>
+
+ </row><row>
+ <entry>RasterGraphics</entry>
+ <entry>Application for viewing, creating, or processing raster (bitmap) graphics</entry>
+ <entry>Graphics;2DGraphics</entry>
+
+ </row><row>
+ <entry>3DGraphics</entry>
+ <entry>Application for viewing, creating, or processing 3-D graphics</entry>
+ <entry>Graphics</entry>
+
+ </row><row>
+ <entry>Scanning</entry>
+ <entry>Tool to scan a file/text</entry>
+ <entry>Graphics</entry>
+
+ </row><row>
+ <entry>OCR</entry>
+ <entry>Optical character recognition application</entry>
+ <entry>Graphics;Scanning</entry>
+
+ </row><row>
+ <entry>Photography</entry>
+ <entry>Camera tools, etc.</entry>
+ <entry>Graphics or Office</entry>
+
+ </row><row>
+ <entry>Publishing</entry>
+ <entry>Desktop Publishing applications and Color Management tools</entry>
+ <entry>Graphics or Office</entry>
+
+ </row><row>
+ <entry>Viewer</entry>
+ <entry>Tool to view e.g. a graphic or pdf file</entry>
+ <entry>Graphics or Office</entry>
+
+ </row><row>
+ <entry>TextTools</entry>
+ <entry>A text tool utiliy</entry>
+ <entry>Utility</entry>
+
+ </row><row>
+ <entry>DesktopSettings</entry>
+ <entry>Configuration tool for the GUI</entry>
+ <entry>Settings</entry>
+
+ </row><row>
+ <entry>HardwareSettings</entry>
+ <entry>A tool to manage hardware components, like sound cards, video cards or printers</entry>
+ <entry>Settings</entry>
+
+ </row><row>
+ <entry>Printing</entry>
+ <entry>A tool to manage printers</entry>
+ <entry>HardwareSettings;Settings</entry>
+
+ </row><row>
+ <entry>PackageManager</entry>
+ <entry>A package manager application</entry>
+ <entry>Settings</entry>
+
+ </row><row>
+ <entry>Dialup</entry>
+ <entry>A dial-up program</entry>
+ <entry>Network</entry>
+
+ </row><row>
+ <entry>InstantMessaging</entry>
+ <entry>An instant messaging client</entry>
+ <entry>Network</entry>
+
+ </row><row>
+ <entry>Chat</entry>
+ <entry>A chat client</entry>
+ <entry>Network</entry>
+
+ </row><row>
+ <entry>IRCClient</entry>
+ <entry>An IRC client</entry>
+ <entry>Network</entry>
+
+ </row><row>
+ <entry>FileTransfer</entry>
+ <entry>Tools like FTP or P2P programs</entry>
+ <entry>Network</entry>
+
+ </row><row>
+ <entry>HamRadio</entry>
+ <entry>HAM radio software</entry>
+ <entry>Network or Audio</entry>
+
+ </row><row>
+ <entry>News</entry>
+ <entry>A news reader or a news ticker</entry>
+ <entry>Network</entry>
+
+ </row><row>
+ <entry>P2P</entry>
+ <entry>A P2P program</entry>
+ <entry>Network</entry>
+
+ </row><row>
+ <entry>RemoteAccess</entry>
+ <entry>A tool to remotely manage your PC</entry>
+ <entry>Network</entry>
+
+ </row><row>
+ <entry>Telephony</entry>
+ <entry>Telephony via PC</entry>
+ <entry>Network</entry>
+
+ </row><row>
+ <entry>TelephonyTools</entry>
+ <entry>Telephony tools, to dial a number, manage PBX, ...</entry>
+ <entry>Utility</entry>
+
+ </row><row>
+ <entry>VideoConference</entry>
+ <entry>Video Conference software</entry>
+ <entry>Network</entry>
+
+ </row><row>
+ <entry>WebBrowser</entry>
+ <entry>A web browser</entry>
+ <entry>Network</entry>
+
+ </row><row>
+ <entry>WebDevelopment</entry>
+ <entry>A tool for web developers</entry>
+ <entry>Network or Development</entry>
+
+ </row><row>
+ <entry>Midi</entry>
+ <entry>An app related to MIDI</entry>
+ <entry>AudioVideo;Audio</entry>
+
+ </row><row>
+ <entry>Mixer</entry>
+ <entry>Just a mixer</entry>
+ <entry>AudioVideo;Audio</entry>
+
+ </row><row>
+ <entry>Sequencer</entry>
+ <entry>A sequencer</entry>
+ <entry>AudioVideo;Audio</entry>
+
+ </row><row>
+ <entry>Tuner</entry>
+ <entry>A tuner</entry>
+ <entry>AudioVideo;Audio</entry>
+
+ </row><row>
+ <entry>TV</entry>
+ <entry>A TV application</entry>
+ <entry>AudioVideo;Video</entry>
+
+ </row><row>
+ <entry>AudioVideoEditing</entry>
+ <entry>Application to edit audio/video files</entry>
+ <entry>Audio or Video or AudioVideo</entry>
+
+ </row><row>
+ <entry>Player</entry>
+ <entry>Application to play audio/video files</entry>
+ <entry>Audio or Video or AudioVideo</entry>
+
+ </row><row>
+ <entry>Recorder</entry>
+ <entry>Application to record audio/video files</entry>
+ <entry>Audio or Video or AudioVideo</entry>
+
+ </row><row>
+ <entry>DiscBurning</entry>
+ <entry>Application to burn a disc</entry>
+ <entry>AudioVideo</entry>
+
+ </row><row>
+ <entry>ActionGame</entry>
+ <entry>An action game</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>AdventureGame</entry>
+ <entry>Adventure style game</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>ArcadeGame</entry>
+ <entry>Arcade style game</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>BoardGame</entry>
+ <entry>A board game</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>BlocksGame</entry>
+ <entry>Falling blocks game</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>CardGame</entry>
+ <entry>A card game</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>KidsGame</entry>
+ <entry>A game for kids</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>LogicGame</entry>
+ <entry>Logic games like puzzles, etc</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>RolePlaying</entry>
+ <entry>A role playing game</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>Simulation</entry>
+ <entry>A simulation game</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>SportsGame</entry>
+ <entry>A sports game</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>StrategyGame</entry>
+ <entry>A strategy game</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>Art</entry>
+ <entry>Software to teach arts</entry>
+ <entry>Education</entry>
+
+ </row><row>
+ <entry>Construction</entry>
+ <entry></entry>
+ <entry>Education</entry>
+
+ </row><row>
+ <entry>Music</entry>
+ <entry>Musical software</entry>
+ <entry>AudioVideo;Education</entry>
+
+ </row><row>
+ <entry>Languages</entry>
+ <entry>Software to learn foreign languages</entry>
+ <entry>Education</entry>
+
+ </row><row>
+ <entry>Science</entry>
+ <entry>Scientific software</entry>
+ <entry>Education</entry>
+
+ </row><row>
+ <entry>ArtificialIntelligence</entry>
+ <entry>Artificial Intelligence software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>Astronomy</entry>
+ <entry>Astronomy software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>Biology</entry>
+ <entry>Biology software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>Chemistry</entry>
+ <entry>Chemistry software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>ComputerScience</entry>
+ <entry>ComputerSience software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>DataVisualization</entry>
+ <entry>Data visualization software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>Economy</entry>
+ <entry>Economy software</entry>
+ <entry>Education</entry>
+
+ </row><row>
+ <entry>Electricity</entry>
+ <entry>Electricity software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>Geography</entry>
+ <entry>Geography software</entry>
+ <entry>Education</entry>
+
+ </row><row>
+ <entry>Geology</entry>
+ <entry>Geology software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>Geoscience</entry>
+ <entry>Geoscience software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>History</entry>
+ <entry>History software</entry>
+ <entry>Education</entry>
+
+ </row><row>
+ <entry>ImageProcessing</entry>
+ <entry>Image Processing software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>Literature</entry>
+ <entry>Literature software</entry>
+ <entry>Education</entry>
+
+ </row><row>
+ <entry>Math</entry>
+ <entry>Math software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>NumericalAnalysis</entry>
+ <entry>Numerical analysis software</entry>
+ <entry>Education;Science;Math</entry>
+
+ </row><row>
+ <entry>MedicalSoftware</entry>
+ <entry>Medical software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>Physics</entry>
+ <entry>Physics software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>Robotics</entry>
+ <entry>Robotics software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>Sports</entry>
+ <entry>Sports software</entry>
+ <entry>Education</entry>
+
+ </row><row>
+ <entry>ParallelComputing</entry>
+ <entry>Parallel computing software</entry>
+ <entry>Education;Science;ComputerScience</entry>
+
+ </row><row>
+ <entry>Amusement</entry>
+ <entry>A simple amusement</entry>
+ <entry></entry>
+
+
+ </row><row>
+ <entry>Archiving</entry>
+ <entry>A tool to archive/backup data</entry>
+ <entry>Utility</entry>
+
+ </row><row>
+ <entry>Compression</entry>
+ <entry>A tool to manage compressed data/archives</entry>
+ <entry>Utility;Archiving</entry>
+
+ </row><row>
+ <entry>Electronics</entry>
+ <entry>Electronics software, e.g. a circuit designer</entry>
+ <entry></entry>
+
+ </row><row>
+ <entry>Emulator</entry>
+ <entry>Emulator of another platform, such as a DOS emulator</entry>
+ <entry>System or Game</entry>
+
+ </row><row>
+ <entry>Engineering</entry>
+ <entry>Engineering software, e.g. CAD programs</entry>
+ <entry></entry>
+
+ </row><row>
+ <entry>FileTools</entry>
+ <entry>A file tool utility</entry>
+ <entry>Utility or System</entry>
+
+ </row><row>
+ <entry>FileManager</entry>
+ <entry>A file manager</entry>
+ <entry>System;FileTools</entry>
+
+ </row><row>
+ <entry>TerminalEmulator</entry>
+ <entry>A terminal emulator application</entry>
+ <entry>System</entry>
+
+ </row><row>
+ <entry>Filesystem</entry>
+ <entry>A file system tool</entry>
+ <entry>System</entry>
+
+ </row><row>
+ <entry>Monitor</entry>
+ <entry>Monitor application/applet that monitors some resource or activity</entry>
+ <entry>System</entry>
+
+ </row><row>
+ <entry>Security</entry>
+ <entry>A security tool</entry>
+ <entry>Settings or System</entry>
+
+ </row><row>
+ <entry>Accessibility</entry>
+ <entry>Accessibility</entry>
+ <entry>Settings or Utility</entry>
+
+ </row><row>
+ <entry>Calculator</entry>
+ <entry>A calculator</entry>
+ <entry>Utility</entry>
+
+ </row><row>
+ <entry>Clock</entry>
+ <entry>A clock application/applet</entry>
+ <entry>Utility</entry>
+
+ </row><row>
+ <entry>TextEditor</entry>
+ <entry>A text editor</entry>
+ <entry>Utility</entry>
+
+ </row><row>
+ <entry>Documentation</entry>
+ <entry>Help or documentation</entry>
+ <entry></entry>
+
+ </row><row>
+ <entry>Adult</entry>
+ <entry>Application handles adult or explicit material</entry>
+ <entry></entry>
+
+ </row><row>
+ <entry>Core</entry>
+ <entry>Important application, core to the desktop such as a file manager or a help browser</entry>
+ <entry></entry>
+
+ </row><row>
+ <entry>KDE</entry>
+ <entry>Application based on KDE libraries</entry>
+ <entry>QT</entry>
+
+ </row><row>
+ <entry>GNOME</entry>
+ <entry>Application based on GNOME libraries</entry>
+ <entry>GTK</entry>
+
+ </row><row>
+ <entry>GTK</entry>
+ <entry>Application based on GTK+ libraries</entry>
+ <entry></entry>
+
+ </row><row>
+ <entry>Qt</entry>
+ <entry>Application based on Qt libraries</entry>
+ <entry></entry>
+
+ </row><row>
+ <entry>Motif</entry>
+ <entry>Application based on Motif libraries</entry>
+ <entry></entry>
+
+ </row><row>
+ <entry>Java</entry>
+ <entry>Application based on Java GUI libraries, such as AWT or Swing</entry>
+ <entry></entry>
+
+ </row><row>
+ <entry>ConsoleOnly</entry>
+ <entry>Application that only works inside a terminal (text-based or command line application)</entry>
+ <entry></entry>
+
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ </para>
+ <para>
+ The table below describes Reserved Categories.
+ Reserved Categories have a specific desktop specific meaning
+ that has not been standardized (yet). Desktop entry files that use
+ a reserved category MUST also include an appropriate OnlyShowIn= entry
+ to restrict themselves to those environments that properly support the
+ reserved category as used.
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Reserved Category</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Screensaver</entry>
+ <entry>A screen saver (launching this desktop entry should activate the screen saver)</entry>
+ </row><row>
+ <entry>TrayIcon</entry>
+ <entry>An application that is primarily an icon for the "system tray" or "notification area" (apps that open a normal window and just happen to have a tray icon as well should not list this category)</entry>
+ </row><row>
+ <entry>Applet</entry>
+ <entry>An applet that will run inside a panel or another such application, likely desktop specific</entry>
+ </row><row>
+ <entry>Shell</entry>
+ <entry>A shell (an actual specific shell such as
+ <filename>bash</filename> or <filename>tcsh</filename>, not a TerminalEmulator)</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ </para>
+ </appendix>
+ <appendix id="onlyshowin-registry">
+ <title>Registered OnlyShowIn Environments</title>
+ <para>
+ Remember, these are case-sensitive. "KDE" not "kde" should be
+ used.
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>OnlyShowIn Value</entry>
+ <entry>Environment</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>GNOME</entry><entry>GNOME Desktop</entry>
+ </row><row>
+ <entry>KDE</entry><entry>KDE Desktop</entry>
+ </row><row>
+ <entry>LXDE</entry><entry>LXDE Desktop</entry>
+ </row><row>
+ <entry>MATE</entry><entry>MATÉ Desktop</entry>
+ </row><row>
+ <entry>Razor</entry><entry>Razor-qt Desktop</entry>
+ </row><row>
+ <entry>ROX</entry><entry>ROX Desktop</entry>
+ </row><row>
+ <entry>Unity</entry><entry>Unity Shell</entry>
+ </row><row>
+ <entry>XFCE</entry><entry>XFCE Desktop</entry>
+ </row><row>
+ <entry>Old</entry><entry>Legacy menu systems</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </para>
+ </appendix>
+ <appendix id="third-party-howto">
+ <title>Integrating your application in the menus</title>
+ <sect1 id="adding-items">
+ <title>Adding menu items</title>
+ <para>
+ The following steps describe how a third party application can add
+ menu items to the menu system:
+ <itemizedlist>
+ <listitem>
+ <para>
+ Install desktop entries to
+ <replaceable>datadir</replaceable>/applications/ for each menu
+ item. Please namespace the filename, as in "vendor-foo.desktop", or
+ use a subdirectory of
+ <replaceable>datadir</replaceable>/applications/ so you have
+ "vendor/foo.desktop." Please be sure all desktop entries are valid
+ (see the <ulink
+ url="http://www.freedesktop.org/software/desktop-file-utils/">
+ desktop-file-utils</ulink> package for a validation utility).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Install an XML menu file to <replaceable>sysconfdir</replaceable>/menus/applications-merged/ to add any submenus, if your desktop entries aren't already
+ included in some common categories.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Install any directory entries needed for your submenus to <replaceable>datadir</replaceable>/desktop-directories/, taking care to namespace and validate
+ the directory entries.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </sect1>
+ <sect1 id="locations">
+ <title>Install Locations</title>
+ <para>
+ If an application is intended to be installed by root on a system wide
+ basis then /usr/share is recommended to be used as value for
+ <replaceable>datadir</replaceable> and /etc/xdg is recommended to be
+ used as value for <replaceable>sysconfdir</replaceable>.
+ In case the /usr/share hierarchy is not writable it is recommended to
+ use /usr/local/share as value for <replaceable>datadir</replaceable>
+ instead.
+ </para>
+ <para>
+ If an application is intended to be installed by an unprivileged user
+ for exclusive use by that user only then
+ <varname>$XDG_DATA_HOME</varname> should be used as value
+ for <replaceable>datadir</replaceable> and
+ <varname>$XDG_CONFIG_HOME</varname> should be used as value
+ for <replaceable>sysconfdir</replaceable>.
+ If <varname>$XDG_DATA_HOME</varname> is not set, the default value of
+ $HOME/.local/share should be used for it.
+ If <varname>$XDG_CONFIG_HOME</varname> is not set, the default value of
+ $HOME/.config should be used for it.
+ </para>
+ </sect1>
+ <sect1 id="menu-add-example">
+ <title>Example</title>
+ <para>
+ The company ShinyThings Inc. has developed an application named
+ <emphasis>WebMirror 1.0</emphasis> and would like to add its own
+ submenu to the system menus consisting of a <emphasis>WebMirror</emphasis>
+ menu item and a <emphasis>WebMirror Admin Tool</emphasis>
+ menu item. The company will use "shinythings" as its vendor id.
+ For the purpose of this example all menu items will be available
+ in two languages, English and Dutch.
+ The language code for Dutch is <emphasis>nl</emphasis>.
+ </para>
+ <para>
+ First the company needs to create two .desktop files that describe
+ the two menu items:
+ <informalexample>
+ <programlisting>
+ <replaceable>datadir</replaceable>/applications/shinythings-webmirror.desktop:
+
+ [Desktop Entry]
+ Encoding=UTF-8
+ Type=Application
+
+ Exec=webmirror
+ Icon=webmirror
+
+ Name=WebMirror
+ Name[nl]=WebSpiegel
+ </programlisting>
+ </informalexample>
+ and
+ <informalexample>
+ <programlisting>
+ <replaceable>datadir</replaceable>/applications/shinythings-webmirror-admin.desktop:
+
+ [Desktop Entry]
+ Encoding=UTF-8
+ Type=Application
+
+ Exec=webmirror-admintool
+ Icon=webmirror-admintool
+
+ Name=WebMirror Admin Tool
+ Name[nl]=WebSpiegel Administratie Tool
+ </programlisting>
+ </informalexample>
+ A .directory file needs to be installed to provide a title and icon
+ for the sub-menu itself:
+ <informalexample>
+ <programlisting>
+ <replaceable>datadir</replaceable>/desktop-directories/shinythings-webmirror.directory:
+
+ [Desktop Entry]
+ Encoding=UTF-8
+
+ Icon=webmirror
+
+ Name=WebMirror
+ Name[nl]=WebSpiegel
+ </programlisting>
+ </informalexample>
+ And finally, a .menu file needs to be provided that links it all
+ togther:
+ <informalexample>
+ <programlisting>
+ <replaceable>sysconfdir</replaceable>/menus/application-merged/shinythings-webmirror.menu:
+
+ &lt;!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu &dtd-version;//EN"
+ "http://www.freedesktop.org/standards/menu-spec/menu-&dtd-version;.dtd"&gt;
+ &lt;Menu&gt;
+ &lt;Name&gt;Applications&lt;/Name&gt;
+ &lt;Menu&gt;
+ &lt;Name&gt;WebMirror&lt;/Name&gt;
+ &lt;Directory&gt;shinythings-webmirror.directory&lt;/Directory&gt;
+ &lt;Include&gt;
+ &lt;Filename&gt;shinythings-webmirror.desktop&lt;/Filename&gt;
+ &lt;Filename&gt;shinythings-webmirror-admin.desktop&lt;/Filename&gt;
+ &lt;/Include&gt;
+ &lt;/Menu&gt;
+ </programlisting>
+ </informalexample>
+
+ </para>
+ </sect1>
+ <sect1 id="compatibility">
+ <title>Backward Compatibility</title>
+ <para>
+ For a limited time, installing a directory hierarchy to
+ the old GNOME/KDE specific locations such as /usr/share/applnk and
+ /usr/share/gnome/apps will continue to work as way to add your
+ application to the menu system as well. There are two ways to support
+ both the old and new menu systems at the same time:
+ <itemizedlist>
+ <listitem>
+ <para>
+ If you add a <varname>Categories</varname> line to the desktop
+ entries in the legacy hierarchy, implementations of this
+ specification will ignore their location in the legacy hierarchy,
+ and arrange them according to <varname>Categories</varname> instead.
+ This allows you to install a single desktop file that works in all
+ cases, though on the down side it's in a legacy location.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If you add the line <literal>OnlyShowIn=Old;</literal> to a desktop
+ entry, then old legacy implementations that ignore
+ <varname>OnlyShowIn</varname> will still show the desktop entry, but
+ implementations of this specification will not. Thus you can
+ add an "<literal>OnlyShowIn=Old;</literal>" entry to the legacy
+ hierarchy, and a new-style desktop entry to
+ <replaceable>datadir</replaceable>/applications/, and still get
+ only one entry in the menus.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </sect1>
+ </appendix>
+ <appendix id="implementation-notes">
+ <title>Implementation notes</title>
+ <sect1 id="menu-editing">
+ <title>Menu editing</title>
+ <para>
+ To implement menu editing, the intent is that a per-user file is
+ created. The per-user file should specify a &lt;MergeFile&gt; with the
+ system wide file, so that system changes are inherited. When the user
+ deletes a menu item, you add
+ <literal>&lt;Exclude&gt;&lt;Filename&gt;foo.desktop&lt;/Filename&gt;&lt;/Exclude&gt;</literal>. If
+ the user adds a menu item, you use
+ <literal>&lt;Include&gt;&lt;Filename&gt;foo.desktop&lt;/Filename&gt;&lt;/Include&gt;</literal>.
+ </para>
+ <para>
+ If the user moves a folder you can use &lt;Move&gt; elements
+ to represent the move. &lt;Move&gt; elements used for menu-editing
+ should always be added to the most top-level menu to ensure that moves
+ are performed in the order in which they are specified; moves specified
+ in child menus are always performed before moves specified in a more
+ top level menu regardless of their location in the menu file.
+ </para>
+ <para>
+ To delete a folder, simply append the &lt;Deleted&gt; element.
+ </para>
+ <para>
+ When adding a new folder or moving an existing folder, menu editing
+ implementations are advised not to re-use the menu path of a previously
+ deleted folder.
+ </para>
+ <para>
+ Menu editors probably need to do some kind of consolidation/compression
+ to avoid an XML tree that grows infinitely over time.
+ </para>
+ </sect1>
+ </appendix>
+ <glossary><title>Glossary</title>
+ <para>
+ This glossary defines some of the terms used in this specification.
+ </para>
+
+ <glossentry id="term-desktop-entry"><glossterm>Desktop entry</glossterm>
+ <glossdef>
+ <para>
+ A desktop entry is a file with a name ending in the ".desktop"
+ extension which conforms to the <ulink
+ url="http://www.freedesktop.org/Standards/desktop-entry-spec">desktop
+ entry specification</ulink> with <literal>Type=Application</literal>.
+ It describes a menu item, including a name, an icon, and what to do when the item is selected.
+ Desktop entries are also known as ".desktop files."
+ </para>
+ </glossdef>
+ </glossentry>
+
+ <glossentry id="term-desktop-file-id"><glossterm>Desktop-File Id</glossterm>
+ <glossdef>
+ <para>
+ The id to identify a desktop entry with.
+ For example, if <filename>/usr/share/applications</filename> is
+ specified as an &lt;AppDir&gt;, and <filename>/opt/ude</filename>
+ as &lt;LegacyDir prefix="foo-"&gt;
+ then
+ <filename>/usr/share/applications/foo/bar.desktop</filename>,
+ <filename>/usr/share/applications/foo-bar.desktop</filename>
+ and
+ <filename>/opt/ude/Settings/bar.desktop</filename> all have
+ the same desktop-file id <literal>foo-bar.desktop</literal>
+ </para>
+ </glossdef>
+ </glossentry>
+
+ <glossentry id="term-directory-entry"><glossterm>Directory entry</glossterm>
+ <glossdef>
+ <para>
+ A directory entry is a file with a name ending in the ".directory"
+ extension which conforms to the <ulink
+ url="http://www.freedesktop.org/Standards/desktop-entry-spec">desktop
+ entry specification</ulink> with <literal>Type=Directory</literal>.
+ It provides a localized name and an icon for a submenu.
+ Directory entries are also known as ".directory files."
+ </para>
+ </glossdef>
+ </glossentry>
+
+ <glossentry id="term-menu-path"><glossterm>Menu path</glossterm>
+ <glossdef>
+ <para>
+ A "menu path" is the path to a particular menu. Menu paths are
+ always "relative" so never start with a slash character.
+ The path to a menu is simply the &lt;Name&gt; of each parent
+ of the menu, followed by the &lt;Name&gt; of the menu itself.
+ For example, "Foo/Bar/Baz" is a valid menu path.
+ </para>
+ </glossdef>
+ </glossentry>
+
+ <glossentry id="term-relative-path"><glossterm>Relative path</glossterm>
+ <glossdef>
+ <para>
+ The canonical path to a directory entry, relative to the
+ &lt;DirectoryDir&gt; containing the
+ entry. For example, if <filename>/usr/share/desktop-directories</filename> is
+ specified as an &lt;DirectoryDir&gt;, the relative path to
+ <filename>/usr/share/desktop-directories/foo/bar.directory</filename> is
+ <filename>foo/bar.directory</filename>.
+ </para>
+ </glossdef>
+ </glossentry>
+ </glossary>
+</article>
+
diff --git a/menu/menu.dtd b/menu/menu.dtd
new file mode 100644
index 0000000..1bca3ed
--- /dev/null
+++ b/menu/menu.dtd
@@ -0,0 +1,84 @@
+<!-- For explanations see http://www.freedesktop.org/standards/menu-spec -->
+<!ELEMENT Menu (
+ Name, (
+ Directory
+ | DefaultAppDirs
+ | AppDir
+ | DefaultDirectoryDirs
+ | DirectoryDir
+ | LegacyDir
+ | KDELegacyDirs
+ | MergeFile
+ | DefaultMergeDirs
+ | MergeDir
+ | OnlyUnallocated
+ | NotOnlyUnallocated
+ | Deleted
+ | NotDeleted
+ | Include
+ | Exclude
+ | Move
+ | Menu
+ | Layout
+ | DefaultLayout
+ )*
+)>
+
+<!ELEMENT Name (#PCDATA)>
+
+<!ELEMENT Directory (#PCDATA)>
+
+<!ELEMENT DefaultAppDirs EMPTY>
+<!ELEMENT AppDir (#PCDATA)>
+
+<!ELEMENT DefaultDirectoryDirs EMPTY>
+<!ELEMENT DirectoryDir (#PCDATA)>
+
+<!ELEMENT LegacyDir (#PCDATA)>
+<!ATTLIST LegacyDir prefix CDATA #IMPLIED>
+<!ELEMENT KDELegacyDirs EMPTY>
+
+<!ELEMENT MergeFile (#PCDATA)>
+<!ATTLIST MergeFile type (path|parent) #IMPLIED>
+
+<!ELEMENT DefaultMergeDirs EMPTY>
+<!ELEMENT MergeDir (#PCDATA)>
+
+<!ELEMENT OnlyUnallocated EMPTY>
+<!ELEMENT NotOnlyUnallocated EMPTY>
+
+<!ELEMENT Deleted EMPTY>
+<!ELEMENT NotDeleted EMPTY>
+
+<!ELEMENT Exclude ((Category|Filename|And|Or|Not|All)*)>
+<!ELEMENT Include ((Category|Filename|And|Or|Not|All)*)>
+
+<!ELEMENT And ((Category|Filename|And|Or|Not|All)*)>
+<!ELEMENT Or ((Category|Filename|And|Or|Not|All)*)>
+<!ELEMENT Not ((Category|Filename|And|Or|Not|All)*)>
+<!ELEMENT Filename (#PCDATA)>
+<!ELEMENT Category (#PCDATA)>
+<!ELEMENT All EMPTY>
+
+<!ELEMENT Move ((Old,New)*)>
+<!ELEMENT Old (#PCDATA)>
+<!ELEMENT New (#PCDATA)>
+
+<!ELEMENT Layout ((Filename|Menuname|Separator|Merge)*)>
+<!ELEMENT DefaultLayout ((Filename|Menuname|Separator|Merge)*)>
+<!ATTLIST DefaultLayout show_empty (true|false) #IMPLIED>
+<!ATTLIST DefaultLayout inline (true|false) #IMPLIED>
+<!ATTLIST DefaultLayout inline_limit CDATA #IMPLIED>
+<!ATTLIST DefaultLayout inline_header (true|false) #IMPLIED>
+<!ATTLIST DefaultLayout inline_alias (true|false) #IMPLIED>
+
+<!ELEMENT Menuname (#PCDATA)>
+<!ATTLIST Menuname inline (true|false) #IMPLIED>
+<!ATTLIST Menuname inline_limit CDATA #IMPLIED>
+<!ATTLIST Menuname inline_header (true|false) #IMPLIED>
+<!ATTLIST Menuname inline_alias (true|false) #IMPLIED>
+
+<!ELEMENT Separator EMPTY>
+
+<!ELEMENT Merge EMPTY>
+<!ATTLIST Merge type (menus|files|all) #REQUIRED>
diff --git a/menu/tests/ChangeLog b/menu/tests/ChangeLog
new file mode 100644
index 0000000..ac55e4f
--- /dev/null
+++ b/menu/tests/ChangeLog
@@ -0,0 +1,70 @@
+2005-04-25 Mark McLoughlin <mark@skynet.ie>
+
+ * tests/u: add test for recursive inclusion of .menu files.
+ Also know as the "get really hosed and eat all the RAM
+ you can find" test.
+
+2005-04-20 Waldo Bastian <bastian@kde.org>
+
+ * tests/s/result,
+ tests/s/test: Test that .desktop files under $HOME correctly
+ override .desktop files at system level
+
+ * tests/t/result,
+ tests/t/test: Test order in which <Move> elements are processed
+
+2005-02-18 Mark McLoughlin <mark@skynet.ie>
+
+ Make it a bit more difficult in order to catch:
+ http://bugzilla.gnome.org/show_bug.cgi?id=167758
+ Thanks to Chris Lahey for the test case.
+
+ * tests/o/test: put freecell.desktop in a subdirectory.
+
+2005-02-18 Mark McLoughlin <mark@skynet.ie>
+
+ * tests/o/result,
+ tests/o/test: test that
+ <And><Category>foo</Category><Not><Category>foo</Category></Not></And>
+ doesn't match anything.
+
+ * README: add bit about how to test the GNOME impl.
+
+2005-02-18 Mark McLoughlin <mark@skynet.ie>
+
+ Problem pointed out by Waldo.
+
+ * menutest: don't try and run the "CVS" test :)
+
+ * tests/m/result,
+ tests/n/result: items in a hidden or deleted menu
+ should be considered allocated.
+
+2004-12-08 Mark McLoughlin <mark@skynet.ie>
+
+ * tests/2/result,
+ tests/2/test: test the new <OnlyUnallocated> behaviour.
+
+2004-08-29 Mark McLoughlin <mark@skynet.ie>
+
+ * tests/m: test for NoDisplay=true behavious in .desktop and
+ .directory files.
+
+ * data/hidden.desktop,
+ data/hidden.directory: add.
+
+ * data/apps.directory: add Type=Directory.
+
+ * menutest: actually generate a test result if one doesn't
+ exist - makes it easier to write new tests.
+
+2003-10-23 Havoc Pennington <hp@redhat.com>
+
+ * tests/f/test: remove <OnlyUnallocated/> from inside <Include>
+ statement, not allowed.
+
+2003-10-16 Havoc Pennington <hp@redhat.com>
+
+ * menutest: cat log file on failure; print list of failed tests
+ when we're done
+
diff --git a/menu/tests/README b/menu/tests/README
new file mode 100644
index 0000000..b003dc0
--- /dev/null
+++ b/menu/tests/README
@@ -0,0 +1,44 @@
+This directory contains regression tests for the menu-spec.
+
+To run these tests your menu-spec implementation should be
+able to generate a menu in the following text format:
+
+<Full Menu Name><tab><Desktop File Id><tab><Full Path to Desktop File>
+
+Example:
+
+Editors/ kde-kwrite.desktop /home/bastian/.local/share/applications/kde-kwrite.desktop
+Editors/ kde-kate.desktop /home/bastian/.local/share/applications/kde-kate.desktop
+Editors/ kde-KEdit.desktop /home/bastian/.local/share/applications/kde-KEdit.desktop
+Development/ kde-gideon.desktop /opt/kde3/share/applnk/Development/gideon.desktop
+Development/ kde-kbabel.desktop /opt/kde3/share/applnk/Development/kbabel.desktop
+Development/ kde-quanta.desktop /opt/kde3/share/applnk/Development/quanta.desktop
+/ kde-Kfind.desktop /opt/kde3/share/applnk/Kfind.desktop
+/ kde-Home.desktop /opt/kde3/share/applnk/Home.desktop
+/ kde-Help.desktop /opt/kde3/share/applnk/Help.desktop
+
+
+
+The environment variable $MENUTEST should point to a command that is
+able to generate the menu in the above format.
+
+To test KDE one can use:
+ MENUTEST="kbuildsycoca --menutest"
+
+With GNOME, you can use:
+ MENUTEST=gnome-menu-spec-test
+
+The menutest script should be used to run the tests. The following commands can
+be used:
+
+ MENUTEST="foo -bar" ./menutest
+
+to run all tests
+
+ MENUTEST="foo -bar" TESTS="Deleted Directory" ./menutest
+
+to run the tests named "Deleted" and "Directory" only
+
+All tests contain of a test setup script named "test" and a file describing
+the expected menu named "result".
+
diff --git a/menu/tests/TODO b/menu/tests/TODO
new file mode 100644
index 0000000..6e178bf
--- /dev/null
+++ b/menu/tests/TODO
@@ -0,0 +1,5 @@
+I hope you understand what i mean ;)
+
+desktop-file-id stuff & overwrite stuff absolute path
+
+onlyshowin - notshowin
diff --git a/menu/tests/data/Help.desktop b/menu/tests/data/Help.desktop
new file mode 100644
index 0000000..dc867cd
--- /dev/null
+++ b/menu/tests/data/Help.desktop
@@ -0,0 +1,68 @@
+[Desktop Entry]
+Encoding=UTF-8
+Exec=khelpcenter
+Icon=khelpcenter
+DocPath=khelpcenter/index.html
+Type=Application
+Terminal=0
+
+Name=Help
+Name[af]=Hulp
+Name[az]=Yardım
+Name[be]=Дапамога
+Name[bg]=Помощ
+Name[br]=Skoazell
+Name[bs]=Pomoć
+Name[ca]=Ajuda
+Name[cs]=Nápověda
+Name[cy]=Cymorth
+Name[da]=Hjælp
+Name[de]=Hilfe
+Name[el]=Βοήθεια
+Name[eo]=Helpo
+Name[es]=Ayuda
+Name[et]=Abiinfo
+Name[eu]=Laguntza
+Name[fa]=راهنما
+Name[fi]=Ohje
+Name[fr]=Aide
+Name[gl]=Axuda
+Name[he]=עזרה
+Name[hr]=Pomoć
+Name[hu]=Segítség
+Name[id]=Keterangan bantu
+Name[is]=Hjálp
+Name[it]=Aiuto
+Name[ja]=ヘルプ
+Name[ko]=도움말
+Name[lo]=ລະບົບຊ່ວຍເຫືລອ
+Name[lt]=Pagalba
+Name[lv]=Palīdzība
+Name[mn]=Тусламж
+Name[mt]=Għajnuna
+Name[nb]=Hjelp
+Name[nn]=Hjelp
+Name[nso]=Thuso
+Name[oc]=Ajuda
+Name[pl]=Pomoc
+Name[pt]=Ajuda
+Name[pt_BR]=Ajuda
+Name[ro]=Ajutor
+Name[ru]=Справка
+Name[sk]=Pomocník
+Name[sl]=Pomoč
+Name[sr]=Pomoć
+Name[ss]=Sita
+Name[sv]=Hjälp
+Name[ta]=¯¾Å¢
+Name[th]=ระบบช่วยเหลือ
+Name[tr]=Yardım
+Name[uk]=Довідка
+Name[ven]=Thuso
+Name[vi]=Trợ giúp
+Name[wa]=Aidance
+Name[xh]=Uncedo
+Name[zh_CN]=帮助
+Name[zh_TW]=求助
+Name[zu]=Usizo
+
diff --git a/menu/tests/data/Home.desktop b/menu/tests/data/Home.desktop
new file mode 100644
index 0000000..2bf8178
--- /dev/null
+++ b/menu/tests/data/Home.desktop
@@ -0,0 +1,127 @@
+[Desktop Entry]
+Encoding=UTF-8
+Type=Application
+Exec=kfmclient openProfile filemanagement
+Icon=folder_home
+Terminal=0
+
+Name=Home
+Name[af]=Huis
+Name[az]=Başlanğıc
+Name[be]=Хатні
+Name[bg]=Домашна директория
+Name[br]=Er-gêr
+Name[bs]=Početak
+Name[ca]=Inici
+Name[cs]=Můj adresář
+Name[cy]=Cartref
+Name[da]=Hjem
+Name[de]=Persönliches Verzeichnis
+Name[el]=Σπίτι
+Name[eo]=Hejmo
+Name[es]=Personal
+Name[et]=Kodukataloog
+Name[eu]=Etxea
+Name[fa]=خانه
+Name[fi]=Koti
+Name[fr]=Dossier personnel
+Name[gl]=Persoal
+Name[he]=בית
+Name[hr]=Početak
+Name[hu]=Saját könyvtár
+Name[id]=Rumah
+Name[is]=Heimasvæðið þitt
+Name[ja]=ホーム
+Name[ko]=홈
+Name[lo]=ພື້ນທີ່ສ່ວນຕົວ
+Name[lt]=Pradžia
+Name[lv]=Mājas
+Name[mn]=Хувийн лавлах
+Name[mt]=Direttorju Personali
+Name[nb]=Hjem
+Name[nl]=Persoonlijke map
+Name[nn]=Heim
+Name[nso]=Gae
+Name[oc]=Inici
+Name[pl]=Katalog domowy
+Name[pt]=Casa
+Name[ro]=Acasă
+Name[ru]=Домой
+Name[sk]=Domov
+Name[sl]=Domov
+Name[sr]=Кориснички директоријум
+Name[ss]=Ekhaya
+Name[sv]=Hem
+Name[ta]=¦¾¡¼ì¸õ
+Name[th]=พื้นที่ส่วนตัว
+Name[tr]=Başlangıç
+Name[uk]=Домівка
+Name[ven]=Haya
+Name[wa]=Måjhon
+Name[xh]=Ikhaya
+Name[xx]=xxHomexx
+Name[zh_CN]=起点
+Name[zh_TW]=家目錄
+Name[zu]=Ikhaya
+
+GenericName=Personal Files
+GenericName[af]=Persoonlike Lêers
+GenericName[az]=Şəxsi Fayllar
+GenericName[be]=Пэрсанальныя файлы
+GenericName[bg]=Лични файлове
+GenericName[br]=Restroù deoc'h
+GenericName[bs]=Osobne datoteke
+GenericName[ca]=Fitxers personals
+GenericName[cs]=Osobní soubory
+GenericName[cy]=Ffeiliau Personol
+GenericName[da]=Personlige filer
+GenericName[de]=Eigene Dateien
+GenericName[el]=Προσωπικά Αρχεία
+GenericName[eo]=Personaj dosieroj
+GenericName[es]=Archivos personales
+GenericName[et]=Isiklikud failid
+GenericName[eu]=Fitxategi Pertsonalak
+GenericName[fa]=پرونده‌های شخصی
+GenericName[fi]=Omat tiedostot
+GenericName[fr]=Fichiers personnels
+GenericName[gl]=Ficheiros Persoais
+GenericName[he]=קבצים אישיים
+GenericName[hr]=Osobne datoteke
+GenericName[hu]=személyes fájlok
+GenericName[id]=File Pribadi
+GenericName[is]=Skrárnar þínar
+GenericName[it]=File personali
+GenericName[ja]=個人のファイル
+GenericName[ko]=혼자만 쓰는 파일
+GenericName[lo]=ທີ່ເກັບແຟ້ມແລະເອກະສານສວ່ນຕົວຫລືອື່ນຯ
+GenericName[lt]=Asmeninės Bylos
+GenericName[lv]=Personālie Faili
+GenericName[mn]=Өөрийн файлууд
+GenericName[mt]=Fajls Personali
+GenericName[nb]=Personlige filer
+GenericName[nl]=persoonlijke bestanden
+GenericName[nn]=Personlege filer
+GenericName[nso]=Difaele tsa Botho
+GenericName[oc]=FiquièRs personals
+GenericName[pl]=Pliki osobiste
+GenericName[pt]=Ficheiros Pessoais
+GenericName[pt_BR]=Arquivos Pessoais
+GenericName[ro]=Fişiere personale
+GenericName[ru]=Личные файлы
+GenericName[sk]=Osobné súbory
+GenericName[sl]=Osebne datoteke
+GenericName[sr]=Овај директоријум садржи ваше личне фајлове
+GenericName[sv]=Personliga filer
+GenericName[ta]=¦º¡ó¾ì §¸¡ôÒì¸û
+GenericName[th]=ที่เก็บแฟ้มและเอกสารส่วนตัว หรืออื่น ๆ
+GenericName[tr]=Kişisel Dosyalar
+GenericName[uk]=Особисті файли
+GenericName[ven]=Dzifaela dza vhune
+GenericName[vi]=File cá nhân
+GenericName[wa]=Fitchîs da vosse
+GenericName[xh]=Iifayile Zobuqu
+GenericName[xx]=xxPersonal Filesxx
+GenericName[zh_CN]=个人文件
+GenericName[zh_TW]=個人檔案
+GenericName[zu]=Amafayela Omuntu siqu
+
diff --git a/menu/tests/data/KEdit.desktop b/menu/tests/data/KEdit.desktop
new file mode 100644
index 0000000..4274a2b
--- /dev/null
+++ b/menu/tests/data/KEdit.desktop
@@ -0,0 +1,34 @@
+[Desktop Entry]
+Encoding=UTF-8
+BinaryPattern=kedit;
+MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;
+GenericName=Simple Text Editor
+GenericName[ca]=Editor de text
+GenericName[cs]=Jednoduchý textový editor
+GenericName[da]=Simpel teksteditor
+GenericName[es]=Editor de texto sencillo
+GenericName[fr]=Éditeur de texte élémentaire
+GenericName[pt_BR]=Editor de Texto Simples
+GenericName[sv]=Enkel texteditor
+GenericName[wa]=Simpe aspougneu di tecse
+Exec=kedit -caption "%c" %i %m %u
+Icon=kedit
+TerminalOptions=
+Path=
+DocPath=kedit/index.html
+Type=Application
+Terminal=0
+Name=KEdit
+Name[af]=Kredigeer
+Name[eo]=Redaktilo
+Name[hr]=Uređivač
+Name[lv]=KRediģēt
+Name[pl]=Edytor
+Name[sv]=Kedit
+Name[th]=แก้ไขข้อความ
+Name[ven]=U sengulusa ha K
+Name[xh]=Abahleli Be K
+Name[zh_TW]=KDE 編輯器
+X-KDE-StartupNotify=true
+X-DCOP-ServiceType=Multi
+Categories=Qt;KDE;TextEditor
diff --git a/menu/tests/data/Kfind.desktop b/menu/tests/data/Kfind.desktop
new file mode 100644
index 0000000..8984f18
--- /dev/null
+++ b/menu/tests/data/Kfind.desktop
@@ -0,0 +1,66 @@
+[Desktop Entry]
+Encoding=UTF-8
+Exec=kfind %f
+Icon=kfind
+DocPath=kfind/index.html
+TerminalOptions=
+Path=
+Type=Application
+Terminal=0
+Name=Find Files
+Name[af]=Soek Lêers
+Name[be]=Шукаць файлы
+Name[bg]=Търсене на файлове
+Name[br]=Klask restroù
+Name[bs]=Pronađi datoteke
+Name[ca]=Cerca fitxers
+Name[cs]=Najít soubory
+Name[cy]=Canfod Ffeiliau
+Name[da]=Find filer
+Name[de]=Dateien suchen
+Name[el]=Εύρεση αρχείων
+Name[eo]=Trovu dosierojn
+Name[es]=KFind
+Name[et]=Failide otsimine
+Name[eu]=Fitxategiak Bilatu
+Name[fa]=یافتن پرونده‌ها
+Name[fi]=Etsi tiedostoja
+Name[fr]=Recherche de fichiers
+Name[gl]=Buscar Ficheiros
+Name[he]=חפש קבצים
+Name[hr]=Nađi datoteke
+Name[hu]=Fájlkeresés
+Name[id]=Cari Berkas
+Name[it]=Trova file
+Name[ja]=ファイルを検索
+Name[ko]=파일 찾기
+Name[lo]=ຄົ້ນຫາແຟ້ມ
+Name[lt]=Rasti bylas
+Name[lv]=Meklēt Failus
+Name[mn]=Файл хайх
+Name[mt]=Sib Fajls
+Name[nb]=Finn filer
+Name[nl]=Bestanden zoeken
+Name[nn]=Finn filer
+Name[nso]=Hwetsa Difaele
+Name[oc]=Cerca fiquièrs
+Name[pl]=Znajdź pliki
+Name[pt]=Procurar Ficheiros
+Name[pt_BR]=Encontrar arquivos
+Name[ro]=Caută fişiere
+Name[ru]=Поиск файлов
+Name[sk]=Hľadať súbory
+Name[sl]=Poišči datoteke
+Name[sr]=Pretraga fajlova
+Name[sv]=Hitta filer
+Name[ta]=§¸¡ôÒì ¸ñÎÀ¢Ê
+Name[th]=ค้นหาแฟ้ม
+Name[tr]=Dosyalarda Bul
+Name[uk]=Знайти файли
+Name[ven]=Todani faela
+Name[vi]=Tìm file
+Name[xh]=Fumana Iifayile
+Name[zh_CN]=查找文件
+Name[zh_TW]=尋找檔案
+Name[zu]=Thola Amafayela
+X-KDE-StartupNotify=true
diff --git a/menu/tests/data/apps.directory b/menu/tests/data/apps.directory
new file mode 100644
index 0000000..d54b4bd
--- /dev/null
+++ b/menu/tests/data/apps.directory
@@ -0,0 +1,66 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Apps
+Name[af]=Programme
+Name[ar]=تطبيقات
+Name[az]=Proqram Tə'minatları
+Name[be]=Дастасаваньні
+Name[bg]=Приложения
+Name[br]=Arloadoù
+Name[bs]=Aplikacije
+Name[ca]=Aplicacions
+Name[cs]=Aplikace
+Name[da]=Øvrige programmer
+Name[de]=Programme
+Name[el]=Εφαρμογές
+Name[eo]=Aplikaĵoj
+Name[es]=Aplicaciones
+Name[et]=Rakendused
+Name[eu]=Aplikazioak
+Name[fa]=برنامه‌های کاربردی
+Name[fi]=Sovellukset
+Name[fo]=Nýtsluskipanir
+Name[gl]=Aplicacións
+Name[he]=יישומים
+Name[hr]=Programi
+Name[hu]=Alkalmazások
+Name[id]=Aplikasi
+Name[is]=Forrit
+Name[it]=Applicazioni
+Name[ja]=アプリケーション
+Name[ko]=응용 프로그램
+Name[lo]=ອັບພລິກເຄເຊິນ
+Name[lt]=Programos
+Name[lv]=Aplikācijas
+Name[mk]=Апликации
+Name[mt]=Applikazzjonijiet
+Name[nb]=Programmer
+Name[nl]=Programma's
+Name[nn]=Program
+Name[nso]=Ditshomiso
+Name[oc]=Aplicacions
+Name[pl]=Aplikacje
+Name[pt]=Aplicações
+Name[pt_BR]=Aplicativos
+Name[ro]=Aplicaţii
+Name[ru]=Приложения
+Name[se]=Prográmmat
+Name[sk]=Aplikácie
+Name[sl]=Uporabniški programi
+Name[sr]=Aplikacije
+Name[ss]=Ticelo
+Name[sv]=Program
+Name[ta]=ÀÂýÀ¡Î¸û
+Name[th]=แอพพลิเคชัน
+Name[tr]=Uygulamalar
+Name[uk]=Програми
+Name[ven]=Apulikhesheni
+Name[vi]=Chương trình
+Name[wa]=Programes
+Name[xh]=Izicelo
+Name[zh_CN]=应用程序
+Name[zh_TW]=應用程式
+Name[zu]=Abayaleli
+Type=Directory
+Icon=package_applications
+
diff --git a/menu/tests/data/freecell.desktop b/menu/tests/data/freecell.desktop
new file mode 100644
index 0000000..da6d251
--- /dev/null
+++ b/menu/tests/data/freecell.desktop
@@ -0,0 +1,83 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=FreeCell
+Name[be]=Вольная ячэя
+Name[bg]=Свободна Клетка
+Name[bn]=ফ্রীসেল
+Name[ca]=FreeCell
+Name[cs]=FreeCell
+Name[da]=Napoleon
+Name[de]=FreeCell
+Name[el]=FreeCell
+Name[es]=FreeCell
+Name[et]=Freecell
+Name[fi]=Vapaakenttä
+Name[fr]=Freecell
+Name[ga]=FreeCell
+Name[gl]=FreeCell
+Name[he]=פריסל
+Name[hu]=FreeCell
+Name[it]=FreeCell
+Name[ja]=フリーセル
+Name[ko]=프리셀
+Name[lt]=FreeCell
+Name[lv]=FreeCell
+Name[mn]=Фриселл
+Name[ms]=FreeCell
+Name[nl]=FreeCell
+Name[no]=Freecell
+Name[pl]=FreeCell
+Name[pt]=Freecell
+Name[pt_BR]=FreeCell
+Name[ru]=Фриселл
+Name[sk]=FreeCell
+Name[sl]=FreeCell
+Name[sv]=Napoleon på S:t Helena
+Name[tr]=İskambil falı
+Name[uk]=Вільна комірка
+Name[vi]=FreeCell
+Name[wa]=Freecell
+Name[zh_CN]=空当接龙
+Name[zh_TW]=Freecell
+Comment=FreeCell game
+Comment[be]=Гульня ў Вольную ячэю
+Comment[bg]=игра Свободна Клетка
+Comment[bn]=ফ্রীসেল খেলা
+Comment[ca]=Joc del FreeCell
+Comment[cs]=Hra FreeCell
+Comment[da]=Kortspillet Napoleon
+Comment[de]=FreeCell-Spiel
+Comment[el]=Το παιχνίδι FreeCell
+Comment[es]=Juego FreeCell
+Comment[et]=Kaardimäng FreeCell
+Comment[fi]=Vapaakenttä-peli
+Comment[fr]=Jeu de cartes Freecell
+Comment[he]=משחק פריסל
+Comment[hu]=FreeCell játék
+Comment[it]=FreeCell
+Comment[ja]=フリーセルの GNOME 版
+Comment[lv]=FreeCell spēle
+Comment[mn]=GNOME Фриселл тоглоом
+Comment[ms]=Permainan FreeCell
+Comment[nl]=FreeCell Spel
+Comment[no]=Spillet FreeCell
+Comment[pl]=Gra FreeCell
+Comment[pt]=Jogo FreeCell
+Comment[pt_BR]=Jogo de FreeCell
+Comment[ru]=Пасьянс "Ячейка"
+Comment[sk]=Hra FreeCell
+Comment[sl]=Igra Freecell
+Comment[sv]=Spelet Napoleon på S:t Helena
+Comment[tr]=İskambil falı oyunu
+Comment[uk]=Гра "Вільна комірка"
+Comment[vi]=Trò chơi FreeCell
+Comment[zh_TW]=Freecell 紙牌接龍遊戲
+Exec=freecell
+Icon=gnome-cardgame.png
+Terminal=false
+Type=Application
+Categories=GNOME;Application;Game;CardGame;
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-games
+X-GNOME-Bugzilla-Component=freecell
+StartupNotify=true
diff --git a/menu/tests/data/gataxx.desktop b/menu/tests/data/gataxx.desktop
new file mode 100644
index 0000000..10dfec6
--- /dev/null
+++ b/menu/tests/data/gataxx.desktop
@@ -0,0 +1,81 @@
+[Desktop Entry]
+Encoding=UTF-8
+Icon=gataxx.png
+Name=Gataxx
+Name[am]=Gataxx
+Name[be]=Gataxx
+Name[bg]=Гатаксс
+Name[bn]=জীঅ্যাটাক্স
+Name[ca]=Gataxx
+Name[cs]=Gataxx
+Name[da]=Ataxx
+Name[de]=Gataxx
+Name[el]=Gataxx
+Name[es]=Gataxx
+Name[et]=Gataxx
+Name[fi]=Gataxx
+Name[fr]=Gataxx
+Name[he]=Gataxx
+Name[hu]=Gataxx
+Name[it]=Gataxx
+Name[ja]=Gataxx
+Name[ko]=Gataxx
+Name[lv]=Gataxx
+Name[mn]=Gataxx
+Name[ms]=Gataxx
+Name[nl]=Gataxx
+Name[no]=Gataxx
+Name[pl]=Gataxx
+Name[pt]=Gataxx
+Name[pt_BR]=Gataxx
+Name[ru]=Gataxx
+Name[sk]=Gataxx
+Name[sl]=Gataxx
+Name[sv]=Gataxx
+Name[tr]=Gataxx
+Name[uk]=Атаки
+Name[vi]=Gataxx
+Name[wa]=Gataxx
+Name[zh_CN]=Gataxx
+Name[zh_TW]=Gataxx
+Comment=Ataxx game
+Comment[be]=Гульня Ataxx
+Comment[bg]=Игра атаксс
+Comment[bn]=অ্যাটাক্স খেলা
+Comment[ca]=Joc del Ataxx
+Comment[cs]=Hra Ataxx
+Comment[da]=Spillet Ataxx
+Comment[de]=Ataxx-Spiel
+Comment[el]=Παιχνίδι Ataxx
+Comment[es]=Un juego tipo Ataxx.
+Comment[et]=Mäng nimega ataxx
+Comment[fi]=Ataxx-peli
+Comment[fr]=Jeu Ataxx
+Comment[he]=משחק Ataxx
+Comment[hu]=Ataxx játék
+Comment[it]=Gioco Ataxx
+Comment[ja]=Ataxx ゲーム
+Comment[lv]=Ataxx spēle
+Comment[mn]=Ataxx тоглоом.
+Comment[ms]=Permainan Ataxx
+Comment[nl]=Ataxx
+Comment[no]=Ataxx-spill
+Comment[pl]=Gra Ataxx
+Comment[pt]=Jogo Ataxx
+Comment[pt_BR]=Jogo Ataxx
+Comment[ru]=Игра ataxx
+Comment[sk]=Hra Ataxx
+Comment[sl]=Igra Ataxx
+Comment[sv]=Spelet Ataxx
+Comment[tr]=Ataxx oyunu
+Comment[uk]=Гра "Атаки"
+Comment[vi]=Trò chơi Ataxx.
+Comment[zh_TW]=Ataxx 遊戲
+Exec=gataxx
+Terminal=false
+Type=Application
+Categories=GNOME;Application;Game;BoardGame;
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-games
+X-GNOME-Bugzilla-Component=gataxx
+StartupNotify=true
diff --git a/menu/tests/data/gideon-legacy.desktop b/menu/tests/data/gideon-legacy.desktop
new file mode 100644
index 0000000..4595087
--- /dev/null
+++ b/menu/tests/data/gideon-legacy.desktop
@@ -0,0 +1,12 @@
+[KDE Desktop Entry]
+Encoding=UTF-8
+BinaryPattern=kdevelop;
+Type=Application
+Exec=gideon %u
+MimeType=application/x-kdevelop;
+Icon=gideon
+DocPath=kdevelop/index.html
+Terminal=0
+Name=KDevelop 3.0
+Comment=IDE for C++/Qt/KDE
+X-DCOP-ServiceType=Multi
diff --git a/menu/tests/data/gideon.desktop b/menu/tests/data/gideon.desktop
new file mode 100644
index 0000000..1e11dc1
--- /dev/null
+++ b/menu/tests/data/gideon.desktop
@@ -0,0 +1,13 @@
+[Desktop Entry]
+Encoding=UTF-8
+BinaryPattern=kdevelop;
+Type=Application
+Exec=gideon %u
+MimeType=application/x-kdevelop;
+Icon=gideon
+DocPath=kdevelop/index.html
+Terminal=0
+Name=KDevelop 3.0
+Comment=IDE for C++/Qt/KDE
+X-DCOP-ServiceType=Multi
+Categories=Qt;KDE;Development
diff --git a/menu/tests/data/glines-2.desktop b/menu/tests/data/glines-2.desktop
new file mode 100644
index 0000000..266747d
--- /dev/null
+++ b/menu/tests/data/glines-2.desktop
@@ -0,0 +1,83 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Glines
+Name[be]=Glines
+Name[bg]=ГЛинии
+Name[bn]=জীলাইন্‌স
+Name[ca]=Glines
+Name[cs]=Glines
+Name[da]=Linjer
+Name[de]=Glines
+Name[el]=Glines
+Name[es]=Glines
+Name[et]=Glines
+Name[eu]=Glines
+Name[fi]=Glines
+Name[fr]=Glines
+Name[he]=Glines
+Name[hu]=Glines
+Name[it]=Glines
+Name[ja]=Glines
+Name[ko]=Glines
+Name[lt]=Glines
+Name[lv]=Glines
+Name[mn]=Glines
+Name[ms]=Glines
+Name[nl]=GLines
+Name[no]=Glines
+Name[pl]=Glines
+Name[pt]=GLinhas
+Name[pt_BR]=Glines
+Name[ro]=Glines
+Name[ru]=Glines
+Name[sk]=GČiary
+Name[sl]=Glines
+Name[sv]=Glinjer
+Name[tr]=Sıradaki toplar
+Name[uk]=Ряди
+Name[vi]=Glines
+Name[wa]=GRoyes
+Name[zh_CN]=Glines
+Name[zh_TW]=Glines
+Comment=Color lines game
+Comment[be]=Гульня "Каляровыя лініі"
+Comment[bg]=Игра Цветни линии
+Comment[bn]=কালার লাইন্‌স খেলা
+Comment[ca]=Joc de línies de color
+Comment[cs]=Hra Barevné linie
+Comment[da]=Farvede linjer-spil
+Comment[de]=Farblinienspiel
+Comment[el]=Παιχνίδι γραμμών χρώματος
+Comment[es]=Juego del tipo líneas de colores.
+Comment[fi]=Väriviivapeli
+Comment[fr]=Jeu de lignes colorées
+Comment[he]=משחק שורות צבעוניות
+Comment[hu]=Color lines játék
+Comment[it]=Gioco Color Lines
+Comment[ja]=カラーラインのゲーム
+Comment[lv]=Krāsu līniju spēle
+Comment[mn]=Өнгөт бөмбөлөг тоглоом
+Comment[ms]=Permainan garisan warna
+Comment[nl]=Kleurlijnen
+Comment[no]=Spill med fargelinjer
+Comment[pl]=Gra w kolorowe linie
+Comment[pt]=Jogo das linhas coloridas
+Comment[pt_BR]=Jogo de linhas coloridas
+Comment[ru]=Игра "Цветные линии"
+Comment[sk]=Hra Farebné čiary
+Comment[sl]=Igra barvnih črt
+Comment[sv]=Färglinjespel
+Comment[tr]=Color lines oyunu
+Comment[uk]=Гра "Кольорові ряди"
+Comment[vi]=Trò chơi ColorLines
+Comment[zh_TW]=Color lines 遊戲
+Exec=glines
+Icon=glines.png
+Terminal=false
+Type=Application
+Categories=GNOME;Application;Game;PuzzleGame;
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-games
+X-GNOME-Bugzilla-Component=glines
+StartupNotify=true
+NoDisplay=true
diff --git a/menu/tests/data/glines.desktop b/menu/tests/data/glines.desktop
new file mode 100644
index 0000000..5e5b465
--- /dev/null
+++ b/menu/tests/data/glines.desktop
@@ -0,0 +1,82 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Glines
+Name[be]=Glines
+Name[bg]=ГЛинии
+Name[bn]=জীলাইন্‌স
+Name[ca]=Glines
+Name[cs]=Glines
+Name[da]=Linjer
+Name[de]=Glines
+Name[el]=Glines
+Name[es]=Glines
+Name[et]=Glines
+Name[eu]=Glines
+Name[fi]=Glines
+Name[fr]=Glines
+Name[he]=Glines
+Name[hu]=Glines
+Name[it]=Glines
+Name[ja]=Glines
+Name[ko]=Glines
+Name[lt]=Glines
+Name[lv]=Glines
+Name[mn]=Glines
+Name[ms]=Glines
+Name[nl]=GLines
+Name[no]=Glines
+Name[pl]=Glines
+Name[pt]=GLinhas
+Name[pt_BR]=Glines
+Name[ro]=Glines
+Name[ru]=Glines
+Name[sk]=GČiary
+Name[sl]=Glines
+Name[sv]=Glinjer
+Name[tr]=Sıradaki toplar
+Name[uk]=Ряди
+Name[vi]=Glines
+Name[wa]=GRoyes
+Name[zh_CN]=Glines
+Name[zh_TW]=Glines
+Comment=Color lines game
+Comment[be]=Гульня "Каляровыя лініі"
+Comment[bg]=Игра Цветни линии
+Comment[bn]=কালার লাইন্‌স খেলা
+Comment[ca]=Joc de línies de color
+Comment[cs]=Hra Barevné linie
+Comment[da]=Farvede linjer-spil
+Comment[de]=Farblinienspiel
+Comment[el]=Παιχνίδι γραμμών χρώματος
+Comment[es]=Juego del tipo líneas de colores.
+Comment[fi]=Väriviivapeli
+Comment[fr]=Jeu de lignes colorées
+Comment[he]=משחק שורות צבעוניות
+Comment[hu]=Color lines játék
+Comment[it]=Gioco Color Lines
+Comment[ja]=カラーラインのゲーム
+Comment[lv]=Krāsu līniju spēle
+Comment[mn]=Өнгөт бөмбөлөг тоглоом
+Comment[ms]=Permainan garisan warna
+Comment[nl]=Kleurlijnen
+Comment[no]=Spill med fargelinjer
+Comment[pl]=Gra w kolorowe linie
+Comment[pt]=Jogo das linhas coloridas
+Comment[pt_BR]=Jogo de linhas coloridas
+Comment[ru]=Игра "Цветные линии"
+Comment[sk]=Hra Farebné čiary
+Comment[sl]=Igra barvnih črt
+Comment[sv]=Färglinjespel
+Comment[tr]=Color lines oyunu
+Comment[uk]=Гра "Кольорові ряди"
+Comment[vi]=Trò chơi ColorLines
+Comment[zh_TW]=Color lines 遊戲
+Exec=glines
+Icon=glines.png
+Terminal=false
+Type=Application
+Categories=GNOME;Application;Game;PuzzleGame;
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-games
+X-GNOME-Bugzilla-Component=glines
+StartupNotify=true
diff --git a/menu/tests/data/hidden.desktop b/menu/tests/data/hidden.desktop
new file mode 100644
index 0000000..dbbbff9
--- /dev/null
+++ b/menu/tests/data/hidden.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Hidden
+Comment=You shouldn't see this
+Exec=hidden
+Type=Application
+Categories=GNOME;Application;TextEditor;
+NoDisplay=true
diff --git a/menu/tests/data/hidden.directory b/menu/tests/data/hidden.directory
new file mode 100644
index 0000000..5f1e776
--- /dev/null
+++ b/menu/tests/data/hidden.directory
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Hidden
+Type=Directory
+NoDisplay=true
diff --git a/menu/tests/data/kate.desktop b/menu/tests/data/kate.desktop
new file mode 100644
index 0000000..3ca1c5d
--- /dev/null
+++ b/menu/tests/data/kate.desktop
@@ -0,0 +1,30 @@
+[Desktop Entry]
+Encoding=UTF-8
+GenericName=Advanced Text Editor
+GenericName[cy]=Golygydd Testun Uwch
+GenericName[da]= Avanceret teksteditor
+GenericName[el]=Προχωρημένος διορθωτής κειμένου
+GenericName[es]=Editor de texto avanzado
+GenericName[fa]=ویرایشگر متن پیشرفته
+GenericName[hu]=szövegszerkesztő
+GenericName[pt_BR]=Editor de Texto Avançado
+GenericName[sr]=Napredni editor teksta
+GenericName[sv]=Avancerad texteditor
+BinaryPattern=
+Name=Kate
+Name[ar]=كيت
+Name[bg]=Редактор Kate
+Name[eo]=Kodredaktilo
+Name[fa]=کِیت
+Name[ko]=카테
+Name[ru]=Редактор Kate
+MimeType=text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;text/rdf;
+Exec=kate %u
+TerminalOptions=
+Icon=kate
+Path=
+DocPath=kate/index.html
+Type=Application
+Terminal=0
+X-DCOP-ServiceType=Multi
+Categories=Qt;KDE;TextEditor
diff --git a/menu/tests/data/kbabel.desktop b/menu/tests/data/kbabel.desktop
new file mode 100644
index 0000000..2892cb8
--- /dev/null
+++ b/menu/tests/data/kbabel.desktop
@@ -0,0 +1,69 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=KBabel
+Name[af]=Kbabel
+Name[ar]=المترجم
+Name[eo]=Babelo-tradukilo
+Name[ko]=K바벨
+Name[pt_BR]=Editor de POTFiles
+Name[sv]=Kbabel
+Name[xx]=xxKBabelxx
+Exec=kbabel %i %m -caption "%c" %U
+Icon=kbabel
+MiniIcon=kbabel
+Type=Application
+DocPath=kbabel/index.html
+MimeType=application/x-gettext;
+GenericName=Translation Tool
+GenericName[af]=Vertaling Program
+GenericName[ar]=أداة الترجمة
+GenericName[bg]=Инструменти за Превод
+GenericName[bs]=Alat za prevođenje
+GenericName[ca]=Eina de traducció
+GenericName[cs]=Překladatelský nástroj
+GenericName[cy]=Erfyn Cyfieithu
+GenericName[da]=Oversættelsesværktøj
+GenericName[de]=Übersetzungsprogramm
+GenericName[el]=Εργαλείο μετάφρασης
+GenericName[eo]=Tradukilo por Qt-programoj
+GenericName[es]=Herramienta de traducción
+GenericName[et]=Tõlkimise rakendus
+GenericName[eu]=Itzulpenerako Tresnak
+GenericName[fi]=Käännöstyökalu
+GenericName[fo]=Umsetingaramboð
+GenericName[fr]=Outil de traduction
+GenericName[he]=כלי תרגום
+GenericName[hr]=Uslužni program za prevođenje
+GenericName[hu]=segédprogram fordítóknak
+GenericName[it]=Strumento per le traduzioni
+GenericName[ja]=翻訳ツール
+GenericName[lt]=Vertimo įrankis
+GenericName[lv]=Tulkošanas Rīks
+GenericName[mt]=Għodda tat-traduzzjoni
+GenericName[nb]=Oversettingsverktøy
+GenericName[nl]=vertaalprogramma
+GenericName[nn]=Omsetjingsverktøy
+GenericName[nso]=Sebereka sa Thlathollo
+GenericName[pl]=Narzędzie dla tłumaczy
+GenericName[pt]=Ferramenta de Tradução
+GenericName[pt_BR]=Ferramenta de Tradução
+GenericName[ro]=Utilitar de traducere
+GenericName[ru]=Утилита локализации приложений
+GenericName[sk]=Prekladací nástroj
+GenericName[sl]=Orodje za prevajanje
+GenericName[sv]=Översättningsverktyg
+GenericName[ta]=¦Á¡Æ¢¦ÀÂ÷ôÒì ¸ÕÅ¢
+GenericName[th]=เครื่องมือแปลภาษา
+GenericName[tr]=Çeviri Aracı
+GenericName[uk]=Засіб для перекладів
+GenericName[ven]=Zwishumiswa zwau Dologa
+GenericName[vi]=Công cụ dịch
+GenericName[xh]=Isixhobo Soguqulelo lomsebenzi kolunye ulwimi
+GenericName[xx]=xxTranslation Toolxx
+GenericName[zh_CN]=翻译工具
+GenericName[zh_TW]=翻譯工具
+GenericName[zu]=Ithuluzi Lokuguqulela
+Terminal=0
+X-KDE-StartupNotify=true
+X-DCOP-ServiceType=Unique
+Categories=Qt;KDE;Development
diff --git a/menu/tests/data/kedit-legacy.desktop b/menu/tests/data/kedit-legacy.desktop
new file mode 100644
index 0000000..fa9700e
--- /dev/null
+++ b/menu/tests/data/kedit-legacy.desktop
@@ -0,0 +1,33 @@
+[Desktop Entry]
+Encoding=UTF-8
+BinaryPattern=kedit;
+MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;
+GenericName=Simple Text Editor
+GenericName[ca]=Editor de text
+GenericName[cs]=Jednoduchý textový editor
+GenericName[da]=Simpel teksteditor
+GenericName[es]=Editor de texto sencillo
+GenericName[fr]=Éditeur de texte élémentaire
+GenericName[pt_BR]=Editor de Texto Simples
+GenericName[sv]=Enkel texteditor
+GenericName[wa]=Simpe aspougneu di tecse
+Exec=kedit -caption "%c" %i %m %u
+Icon=kedit
+TerminalOptions=
+Path=
+DocPath=kedit/index.html
+Type=Application
+Terminal=0
+Name=KEdit
+Name[af]=Kredigeer
+Name[eo]=Redaktilo
+Name[hr]=Uređivač
+Name[lv]=KRediģēt
+Name[pl]=Edytor
+Name[sv]=Kedit
+Name[th]=แก้ไขข้อความ
+Name[ven]=U sengulusa ha K
+Name[xh]=Abahleli Be K
+Name[zh_TW]=KDE 編輯器
+X-KDE-StartupNotify=true
+X-DCOP-ServiceType=Multi
diff --git a/menu/tests/data/kwrite.desktop b/menu/tests/data/kwrite.desktop
new file mode 100644
index 0000000..9804fa6
--- /dev/null
+++ b/menu/tests/data/kwrite.desktop
@@ -0,0 +1,84 @@
+[Desktop Entry]
+Encoding=UTF-8
+GenericName=Text Editor
+GenericName[af]=Teks Redigeerder
+GenericName[ar]=محرر نصوص
+GenericName[be]=Тэкставы рэдактар
+GenericName[bg]=Текстов Редактор
+GenericName[bs]=Tekst editor
+GenericName[ca]=Editor de text
+GenericName[cs]=Textový editor
+GenericName[cy]=Golygydd Testun
+GenericName[da]= Teksteditor
+GenericName[de]=Texteditor
+GenericName[el]=Διορθωτής Κειμένου
+GenericName[eo]=Tekstredaktilo
+GenericName[es]=Editor de texto
+GenericName[et]=Tekstiredaktor
+GenericName[eu]=Testu Editorea
+GenericName[fa]=ویرایشگر متن
+GenericName[fi]=Tekstieditori
+GenericName[fo]=Tekstritil
+GenericName[fr]=Éditeur de texte
+GenericName[he]=עורך טקסט
+GenericName[hr]=Uređivač teksta
+GenericName[hu]=szövegszerkesztő
+GenericName[is]=Textaritill
+GenericName[it]=Editor di testi
+GenericName[ja]=テキストエディタ
+GenericName[ko]=글월 편집기
+GenericName[lo]=ເຄື່ອງມືແກ້ໄຂຂໍ້ຄວາມ
+GenericName[lt]=Teksto redaktorius
+GenericName[lv]=Teksta Redaktors
+GenericName[mn]=Текст боловсруулагч
+GenericName[mt]=Editur tat-test
+GenericName[nb]=Skriveprogram
+GenericName[nl]=teksteditor
+GenericName[nn]=Skriveprogram
+GenericName[nso]=Mofetosi wa Sengwalwana
+GenericName[pl]=Edytor tekstowy
+GenericName[pt]=Editor de Texto
+GenericName[pt_BR]=Editor de Texto
+GenericName[ro]=Editor de text
+GenericName[ru]=Текстовый редактор
+GenericName[sk]=Textový editor
+GenericName[sl]=Urejevalnik besedil
+GenericName[sr]=Editor teksta
+GenericName[ss]=Sihleli sembhalo
+GenericName[sv]=Texteditor
+GenericName[ta]=¯¨Ã ¦¾¡ÌôÀ¡Ç÷
+GenericName[th]=เครื่องมือแก้ไขข้อความ
+GenericName[tr]=Metin Düzenleyici
+GenericName[uk]=Редактор текстів
+GenericName[ven]=Musengulusi wa Manwalwa
+GenericName[vi]=Trình soạn văn bản
+GenericName[wa]=Aspougneu di tecse
+GenericName[xh]=Umhleli Wombhalo
+GenericName[zh_CN]=文本编辑器
+GenericName[zh_TW]=文字編輯器
+GenericName[zu]=Umlungisi wombhalo
+BinaryPattern=
+Name=KWrite
+Name[af]=Kskryf
+Name[ar]=كاتب كيدي
+Name[eo]=Simpla kodredaktilo
+Name[fa]=نوشتار K
+Name[fo]=KSkriva
+Name[lo]=Kwrite
+Name[lv]=KRakstīt
+Name[nso]=KNgwala
+Name[ru]=Редактор KWrite
+Name[sv]=Kwrite
+Name[ven]=Nwala ha K
+MimeType=text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;text/x-diff;text/rdf;
+Exec=kwrite %u
+X-KDE-StartupNotify=true
+TerminalOptions=
+Icon=kwrite
+Path=
+DocPath=kwrite/index.html
+Type=Application
+Terminal=0
+InitialPreference=8
+X-DCOP-ServiceType=Multi
+Categories=Qt;KDE;TextEditor
diff --git a/menu/tests/data/mahjongg-2.desktop b/menu/tests/data/mahjongg-2.desktop
new file mode 100644
index 0000000..95cfe27
--- /dev/null
+++ b/menu/tests/data/mahjongg-2.desktop
@@ -0,0 +1,86 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Mahjongg
+Name[am]=ማህጆንግ
+Name[be]=Маджонг
+Name[bg]=Махджонг
+Name[bn]=মাহজং
+Name[ca]=Mahjongg
+Name[cs]=Mahjongg
+Name[da]=Mahjongg
+Name[de]=Mahjongg
+Name[el]=Mahjongg
+Name[es]=Mahjongg
+Name[et]=Mahjongg
+Name[fa]=ماهجونگ
+Name[fi]=Mahjongg
+Name[fr]=Mahjongg
+Name[gl]=Mahjongg
+Name[he]=Mahjongg
+Name[hu]=Mahjongg
+Name[it]=Mahjongg
+Name[ja]=GNOME 上海
+Name[ko]=그놈 마작
+Name[lt]=Mahjongg
+Name[lv]=Mahjongg
+Name[mn]=Маджонг
+Name[ms]=Mahjongg
+Name[nl]=Mahjongg
+Name[no]=Mahjongg
+Name[pl]=Mahjongg
+Name[pt]=Mahjongg
+Name[pt_BR]=Mahjongg
+Name[ru]=Маджонг
+Name[sk]=Mahjongg
+Name[sl]=Mahjongg
+Name[sv]=Mah Jong
+Name[tr]=Mahjongg
+Name[uk]=Магджонґ
+Name[vi]=Mahjongg
+Name[wa]=Mahjongg
+Name[zh_CN]=堆麻将
+Name[zh_TW]=上海麻將
+Comment=Mahjongg game
+Comment[am]=የማህጆንግ ጨዋታ
+Comment[be]=Гульня Маджонг
+Comment[bg]=Игра на Махджонг
+Comment[bn]=মাহজং খেলা
+Comment[ca]=Joc del Mahjongg
+Comment[cs]=Hra Mahjongg
+Comment[da]=Mahjonggspil
+Comment[de]=Mahjongg-Spiel
+Comment[el]=Παιχνίδι Mahjongg
+Comment[es]=Juego Mahjongg
+Comment[et]=Mäng nimega mahjongg
+Comment[fa]=بازی ماهجونگ
+Comment[fi]=Mahjongg-peli
+Comment[fr]=Jeu de Mahjongg
+Comment[he]=משחק Mahjongg
+Comment[hu]=Mahjongg játék
+Comment[it]=Gioco Mahjongg
+Comment[ja]=麻雀ゲーム
+Comment[lv]=Mahjongg spēle
+Comment[mn]=Mahjongg тоглоом
+Comment[ms]=Permainan Mahjongg
+Comment[nl]=Mahjongg
+Comment[no]=Mahjongg-spill
+Comment[pl]=Gra Mahjongg
+Comment[pt]=Jogo Mahjongg
+Comment[pt_BR]=Jogo Mahjongg
+Comment[ru]=Игра Маджонг
+Comment[sk]=Hra Mahjongg
+Comment[sl]=Igra Mahjongg
+Comment[sv]=Mah Jong-spel
+Comment[tr]=Mahjongg oyunu
+Comment[uk]=Гра "Магджонґ"
+Comment[vi]=Trò chơi Mahjongg
+Comment[zh_TW]=上海麻將遊戲
+Exec=mahjongg
+Icon=gnome-mahjongg.png
+Terminal=false
+Type=Application
+Categories=GNOME;Application;Development;
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-games
+X-GNOME-Bugzilla-Component=mahjongg
+StartupNotify=true
diff --git a/menu/tests/data/mahjongg.desktop b/menu/tests/data/mahjongg.desktop
new file mode 100644
index 0000000..0d5f5de
--- /dev/null
+++ b/menu/tests/data/mahjongg.desktop
@@ -0,0 +1,86 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Mahjongg
+Name[am]=ማህጆንግ
+Name[be]=Маджонг
+Name[bg]=Махджонг
+Name[bn]=মাহজং
+Name[ca]=Mahjongg
+Name[cs]=Mahjongg
+Name[da]=Mahjongg
+Name[de]=Mahjongg
+Name[el]=Mahjongg
+Name[es]=Mahjongg
+Name[et]=Mahjongg
+Name[fa]=ماهجونگ
+Name[fi]=Mahjongg
+Name[fr]=Mahjongg
+Name[gl]=Mahjongg
+Name[he]=Mahjongg
+Name[hu]=Mahjongg
+Name[it]=Mahjongg
+Name[ja]=GNOME 上海
+Name[ko]=그놈 마작
+Name[lt]=Mahjongg
+Name[lv]=Mahjongg
+Name[mn]=Маджонг
+Name[ms]=Mahjongg
+Name[nl]=Mahjongg
+Name[no]=Mahjongg
+Name[pl]=Mahjongg
+Name[pt]=Mahjongg
+Name[pt_BR]=Mahjongg
+Name[ru]=Маджонг
+Name[sk]=Mahjongg
+Name[sl]=Mahjongg
+Name[sv]=Mah Jong
+Name[tr]=Mahjongg
+Name[uk]=Магджонґ
+Name[vi]=Mahjongg
+Name[wa]=Mahjongg
+Name[zh_CN]=堆麻将
+Name[zh_TW]=上海麻將
+Comment=Mahjongg game
+Comment[am]=የማህጆንግ ጨዋታ
+Comment[be]=Гульня Маджонг
+Comment[bg]=Игра на Махджонг
+Comment[bn]=মাহজং খেলা
+Comment[ca]=Joc del Mahjongg
+Comment[cs]=Hra Mahjongg
+Comment[da]=Mahjonggspil
+Comment[de]=Mahjongg-Spiel
+Comment[el]=Παιχνίδι Mahjongg
+Comment[es]=Juego Mahjongg
+Comment[et]=Mäng nimega mahjongg
+Comment[fa]=بازی ماهجونگ
+Comment[fi]=Mahjongg-peli
+Comment[fr]=Jeu de Mahjongg
+Comment[he]=משחק Mahjongg
+Comment[hu]=Mahjongg játék
+Comment[it]=Gioco Mahjongg
+Comment[ja]=麻雀ゲーム
+Comment[lv]=Mahjongg spēle
+Comment[mn]=Mahjongg тоглоом
+Comment[ms]=Permainan Mahjongg
+Comment[nl]=Mahjongg
+Comment[no]=Mahjongg-spill
+Comment[pl]=Gra Mahjongg
+Comment[pt]=Jogo Mahjongg
+Comment[pt_BR]=Jogo Mahjongg
+Comment[ru]=Игра Маджонг
+Comment[sk]=Hra Mahjongg
+Comment[sl]=Igra Mahjongg
+Comment[sv]=Mah Jong-spel
+Comment[tr]=Mahjongg oyunu
+Comment[uk]=Гра "Магджонґ"
+Comment[vi]=Trò chơi Mahjongg
+Comment[zh_TW]=上海麻將遊戲
+Exec=mahjongg
+Icon=gnome-mahjongg.png
+Terminal=false
+Type=Application
+Categories=GNOME;Application;Game;BoardGame;
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-games
+X-GNOME-Bugzilla-Component=mahjongg
+StartupNotify=true
diff --git a/menu/tests/data/quanta.desktop b/menu/tests/data/quanta.desktop
new file mode 100644
index 0000000..4b23aef
--- /dev/null
+++ b/menu/tests/data/quanta.desktop
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Quanta Plus
+Exec=quanta
+Icon=quanta
+Type=Application
+MimeType=text/html
+DocPath=quanta/index.html
+Comment=Web Development Environment
+Categories=Qt;KDE;Development
diff --git a/menu/tests/expand b/menu/tests/expand
new file mode 100755
index 0000000..5dbaac8
--- /dev/null
+++ b/menu/tests/expand
@@ -0,0 +1,12 @@
+#!/usr/bin/perl
+
+# This script performs expansion of environment variables of the form ${HOME}
+
+while(<>)
+{
+ while (($a) = ($_ =~ /[^\$]*\$\{([^\}]*)\}.*/))
+ {
+ s/([^\$]*)(\$\{$a\})(.*)/$1$ENV{$a}$3/;
+ }
+ printf $_;
+} \ No newline at end of file
diff --git a/menu/tests/menutest b/menu/tests/menutest
new file mode 100755
index 0000000..9d37246
--- /dev/null
+++ b/menu/tests/menutest
@@ -0,0 +1,200 @@
+#!/bin/bash
+# these vars are usable *only* for debugging purposes; they're not allowed as part of the spec, thus don't use them.
+# set MENU_FAKE_PREFIX to a non empty val to force testing for if things would succeed if XDG_MENU_PREFIX were
+# implemented
+# set MENU_FAKE_APPLICATIONS to force a work around for applications-merged postfixing.
+#
+function installData()
+{
+ local DIR="$1"
+ shift
+ mkdir -p "${DIR}"
+ for file in $*; do
+ cp "data/${file}" "${DIR}"
+ WIPE[$WIPE_IDX]="${DIR}/${file}"
+ WIPE_IDX=$(( $WIPE_IDX + 1 ))
+ done
+}
+
+function installDataAs()
+{
+ local DIR="$1"
+ mkdir -p "${DIR}"
+ cp "data/$2" "${DIR}/$3"
+ WIPE[${WIPE_IDX}]="${DIR}/${3}"
+ WIPE_IDX=$(( $WIPE_IDX + 1 ))
+}
+
+setup_local_xdg_vars() {
+ export XDG_CONFIG_HOME="${MENUTESTDIR}/xdg_config_home"
+ export XDG_DATA_HOME="${MENUTESTDIR}/xdg_data_home"
+ export XDG_CONFIG_DIR="${MENUTESTDIR}/xdg_config_dir"
+ export XDG_CONFIG_DIRS="$XDG_CONFIG_DIR:${XDG_CONFIG_DIRS}2"
+ export XDG_DATA_DIR="${MENUTESTDIR}/xdg_data_dir"
+ export XDG_DATA_DIRS="$XDG_DATA_DIR:${XDG_DATA_DIR}2"
+ export XDG_CACHE_HOME="${MENUTESTDIR}/xdg_cache_home"
+}
+
+setup_xdg_system_data_vars()
+{
+ export XDG_CACHE_HOME="${XDG_DATA_HOME:-${HOME}/.cache}"
+ export XDG_DATA_HOME="${XDG_DATA_HOME:-${HOME}/.local/share}"
+ export XDG_CONFIG_DIR="${XDG_CONFIG_DIR:-/etc/xdg}"
+ if [ -z "${XDG_DATA_DIRS}" ]; then
+ export XDG_DATA_DIRS="/usr/local/share:/usr/share"
+ fi
+ export XDG_DATA_DIRS="${XDG_DATA_DIRS}:${MENUTESTDIR}/xdg_cache_dir"
+ export XDG_DATA_DIR="${XDG_DATA_DIRS//*:}"
+ if [ -z "${XDG_CONFIG_DIRS}" ]; then
+ export XDG_CONFIG_DIRS="/etc/xdg"
+ fi
+ export XDG_CONFIG_DIR="${XDG_CONFIG_DIRS/:*}"
+}
+
+run_test() {
+ if [ -z "$1" ]; then
+ echo "requires name of test directory to run"
+ exit 1
+ fi
+ local TEST="$1"
+ rm -rf "${MENUTESTDIR}" 2> /dev/null
+ mkdir "${MENUTESTDIR}"
+ RESULT="${TEST}/result"
+
+ (
+ unset WIPE WIPE_IDX
+ declare -a WIPE
+ declare -i WIPE_IDX=0
+ unset MODE
+
+ . ${TEST}/test
+ echo ">>> Running test ${TEST}, purpose $TEST_PURPOSE"
+
+ if [ "${MODE:-local}" == "local" ]; then
+ setup_local_xdg_vars
+ elif [ "${MODE}" == "system_data" ]; then
+ setup_xdg_system_data_vars
+ else
+ echo "!!! unknown MODE from $TEST, bailing"
+ exit -1
+ fi
+
+ test_code
+
+ declare -i IDX=0
+ while [ $WIPE_IDX -gt $IDX ]; do
+ echo "${WIPE[$IDX]}" >> "${MENUTESTDIR}/wipe"
+ IDX=$(( $IDX + 1 ))
+ done
+
+ DEBUG_OVERRIDES=''
+ [ -n "$MENU_FAKE_PREFIX" ] && DEBUG_OVERRIDES=.menu
+ [ -n "$MENU_FAKE_APPLICATIONS" ] && DEBUG_OVERIDES="${DEBUG_OVERRIDES} -merged"
+
+ for x in dir home; do
+ for y in ${DEBUG_OVERRIDES}; do
+ if [ -e "${MENUTESTDIR}/xdg_config_${x}/menus/applications${y}" ]; then
+ ln -s applications${y} "${MENUTESTDIR}/xdg_config_${x}/menus/kde-applications${y}"
+ ln -s applications${y} "${MENUTESTDIR}/xdg_config_${x}/menus/gnome-applications${y}"
+ fi
+ done
+ unset y
+ done
+ unset x DEBUG_OVERRIDES
+
+ $MENUTEST > ${MENUTESTDIR}/run-result 2> ${MENUTESTDIR}/log
+
+ if [ -e "${RESULT}" ]; then
+ ./expand "${RESULT}" > "${MENUTESTDIR}/required-result"
+ fi
+
+ if [ "$(type -t interpret_results)" == "function" ]; then
+ interpret_results
+ else
+ default_interpret_results
+ fi
+ ret=$?
+ if [ -e "${MENUTESTDIR}/wipe" ]; then
+ cat "${MENUTESTDIR}/wipe" | while read l; do
+ [ -z "$l" ] && continue
+ rm "$l"
+ done
+ fi
+ return $ret
+ )
+}
+
+default_interpret_results() {
+ if [ ! -e "${RESULT}" ]; then
+ echo "!!! Result file (${RESULT}) for ${TEST} missing"
+ echo '>>> Failed'
+ return 1
+ elif diff -q "${MENUTESTDIR}/run-result" "${MENUTESTDIR}/required-result" > /dev/null; then
+ echo '>>> OK'
+ return 0
+ fi
+ sort "${MENUTESTDIR}/run-result" > "${MENUTESTDIR}/run-result.sorted"
+ sort "${MENUTESTDIR}/required-result" > "${MENUTESTDIR}/required-result.sorted"
+ if diff -u "${MENUTESTDIR}/run-result.sorted" "${MENUTESTDIR}/required-result.sorted" > "${MENUTESTDIR}/result.diff"; then
+ echo '>>> OK (different order)'
+ return 0
+ fi
+ grep "${MENUTESTDIR}" "${MENUTESTDIR}/run-result" > "${MENUTESTDIR}/run-result.filtered" 2> /dev/null
+ if diff -q "${MENUTESTDIR}/run-result.filtered" "${MENUTESTDIR}/required-result" > /dev/null; then
+ echo '>>> OK (additional system items)'
+ return 0
+ fi
+ grep "${MENUTESTDIR}" "${MENUTESTDIR}/run-result.sorted" > "${MENUTESTDIR}/required-result.filtered" 2> /dev/null
+ if diff -u "${MENUTESTDIR}/run-result.filtered" "${MENUTESTDIR}/required-result.sorted" > "${MENUTESTDIR}/result.diff"; then
+ echo '>>> OK (different order, additional system items)'
+ return 0
+ fi
+ echo '>>> Failed'
+ cat "${MENUTESTDIR}/result.diff"
+ cat "${MENUTESTDIR}/log"
+ return 1
+}
+
+if [ -z "${TESTS}" ]; then
+ export TESTS=`ls tests/*/test | sed -e 's:^\(\./\)\?tests/\+::' -e 's:/\+test$::'`
+fi
+
+if [ -z "$TET_RUN" ]; then
+
+ if [ "x${MENUTEST}" == "x" ]; then
+ echo 'To run the test set $MENUTEST to your menu-spec implementation.'
+ exit 1
+ fi
+
+ if [ "x${MENUTESTDIR}" == "x" ]; then
+ MENUTESTDIR=/tmp/menutestdir
+ echo Using ${MENUTESTDIR} as test directory, override with \$MENUTESTDIR.
+ else
+ echo Using ${MENUTESTDIR} as test directory.
+ fi
+
+ export MENUTESTDIR
+ export USER=${USER:-test}
+
+
+ FAILED=
+ SUCCEEDED=
+
+ for TESTCASE in ${TESTS}; do
+ if [ "${TESTCASE}" == "CVS" ]; then
+ continue
+ fi
+ echo
+ if ! run_test "tests/${TESTCASE}" ${MODE}; then
+ FAILED="${FAILED} ${TESTCASE}"
+ else
+ SUCCEEDED="${SUCCEEDED} ${TESTCASE}"
+ fi
+ done
+
+ echo "OK tests: ${SUCCEEDED}"
+ [ -n "${FAILED}" ] && echo "Failed tests: ${FAILED}"
+ echo "$(echo ${SUCCEEDED} | wc -w) tests passed, $(echo ${FAILED} | wc -w) tests failed"
+ [ -z "${FAILED}" ] && exit 0
+ exit 1
+fi
diff --git a/menu/tests/tests/All/result b/menu/tests/tests/All/result
new file mode 100644
index 0000000..fc75a82
--- /dev/null
+++ b/menu/tests/tests/All/result
@@ -0,0 +1,4 @@
+Applications/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop
+Applications/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop
+Applications/ glines.desktop ${XDG_DATA_DIR}/applications/glines.desktop
+Applications/ mahjongg.desktop ${XDG_DATA_DIR}/applications/mahjongg.desktop
diff --git a/menu/tests/tests/All/test b/menu/tests/tests/All/test
new file mode 100644
index 0000000..e97bddc
--- /dev/null
+++ b/menu/tests/tests/All/test
@@ -0,0 +1,26 @@
+TEST_PURPOSE="<All> Keyword"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <All/>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/And/result b/menu/tests/tests/And/result
new file mode 100644
index 0000000..dc159fa
--- /dev/null
+++ b/menu/tests/tests/And/result
@@ -0,0 +1 @@
+Applications/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop
diff --git a/menu/tests/tests/And/test b/menu/tests/tests/And/test
new file mode 100644
index 0000000..31571db
--- /dev/null
+++ b/menu/tests/tests/And/test
@@ -0,0 +1,29 @@
+TEST_PURPOSE="<And> Keyword"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <And>
+ <Filename>freecell.desktop</Filename>
+ <Category>Game</Category>
+ </And>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/AppDir-relative/result b/menu/tests/tests/AppDir-relative/result
new file mode 100644
index 0000000..9e1d622
--- /dev/null
+++ b/menu/tests/tests/AppDir-relative/result
@@ -0,0 +1,3 @@
+Applications/ KEdit.desktop ${XDG_CONFIG_DIR}/menus/apps/KEdit.desktop
+Applications/ kate.desktop ${XDG_CONFIG_DIR}/menus/apps/kate.desktop
+Applications/ kwrite.desktop ${XDG_CONFIG_DIR}/menus/apps/kwrite.desktop
diff --git a/menu/tests/tests/AppDir-relative/test b/menu/tests/tests/AppDir-relative/test
new file mode 100644
index 0000000..75bd201
--- /dev/null
+++ b/menu/tests/tests/AppDir-relative/test
@@ -0,0 +1,27 @@
+TEST_PURPOSE="<AppDir> relative path tag ..."
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <AppDir>apps</AppDir>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData "${XDG_CONFIG_DIR}/menus/apps" kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop
+ installDataAs "${XDG_CONFIG_DIR}/menus/apps" kwrite.desktop should_be_ignored.notdesktop
+}
diff --git a/menu/tests/tests/AppDir/test b/menu/tests/tests/AppDir/test
new file mode 100644
index 0000000..8faff91
--- /dev/null
+++ b/menu/tests/tests/AppDir/test
@@ -0,0 +1,4 @@
+. tests/AppDir-relative/test
+export PATH_EXPANSION='${XDG_CONFIG_DIR}/menus/'
+TEST_PURPOSE="<AppDir> absolute path"
+
diff --git a/menu/tests/tests/Category/result b/menu/tests/tests/Category/result
new file mode 100644
index 0000000..73b00c9
--- /dev/null
+++ b/menu/tests/tests/Category/result
@@ -0,0 +1,3 @@
+Editors/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Editors/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Editors/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
diff --git a/menu/tests/tests/Category/test b/menu/tests/tests/Category/test
new file mode 100644
index 0000000..3ee0777
--- /dev/null
+++ b/menu/tests/tests/Category/test
@@ -0,0 +1,29 @@
+TEST_PURPOSE="<Category> tag"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>Editors</Name>
+ <Include>
+ <Category>TextEditor</Category>
+ <!-- note it's lowercase, this is intentional to verify
+ it's a case sensitive implementation -->
+ <Category>application</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+# Install .desktop files, freecell is daft but intentional to verify category support is case sensitive
+installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop kate.desktop freecell.desktop
+}
diff --git a/menu/tests/tests/DefaultMergeDirs/result b/menu/tests/tests/DefaultMergeDirs/result
new file mode 100644
index 0000000..9e17189
--- /dev/null
+++ b/menu/tests/tests/DefaultMergeDirs/result
@@ -0,0 +1,5 @@
+Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Development/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop
+Development/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop
diff --git a/menu/tests/tests/DefaultMergeDirs/test b/menu/tests/tests/DefaultMergeDirs/test
new file mode 100644
index 0000000..3b570f2
--- /dev/null
+++ b/menu/tests/tests/DefaultMergeDirs/test
@@ -0,0 +1,43 @@
+TEST_PURPOSE="<DefaultMergeDirs> tag ..."
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <DefaultAppDirs/>
+ <DefaultMergeDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ mkdir ${XDG_CONFIG_DIR}/menus/applications-merged/
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications-merged/test.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop
+}
diff --git a/menu/tests/tests/Deleted/result b/menu/tests/tests/Deleted/result
new file mode 100644
index 0000000..c121419
--- /dev/null
+++ b/menu/tests/tests/Deleted/result
@@ -0,0 +1,2 @@
+BoardGames/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop
+BoardGames/ mahjongg.desktop ${XDG_DATA_DIR}/applications/mahjongg.desktop
diff --git a/menu/tests/tests/Deleted/test b/menu/tests/tests/Deleted/test
new file mode 100644
index 0000000..db7a413
--- /dev/null
+++ b/menu/tests/tests/Deleted/test
@@ -0,0 +1,35 @@
+TEST_PURPOSE="<Deleted> tag"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>BoardGames</Name>
+ <Include>
+ <Category>BoardGame</Category>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Games</Name>
+ <Include>
+ <Category>Game</Category>
+ </Include>
+ <Deleted/>
+ <NotDeleted/>
+ <Deleted/>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/DesktopFileID/result b/menu/tests/tests/DesktopFileID/result
new file mode 100644
index 0000000..9629384
--- /dev/null
+++ b/menu/tests/tests/DesktopFileID/result
@@ -0,0 +1,4 @@
+Applications/ company-games-freecell.desktop ${XDG_DATA_DIR}/applications/company/games/freecell.desktop
+Applications/ company-games-gataxx.desktop ${XDG_DATA_DIR}/applications/company/games/gataxx.desktop
+Applications/ company-games-glines.desktop ${XDG_DATA_DIR}/applications/company/games/glines.desktop
+Applications/ company-games-mahjongg.desktop ${XDG_DATA_DIR}/applications/company/games/mahjongg.desktop
diff --git a/menu/tests/tests/DesktopFileID/test b/menu/tests/tests/DesktopFileID/test
new file mode 100644
index 0000000..ae2c679
--- /dev/null
+++ b/menu/tests/tests/DesktopFileID/test
@@ -0,0 +1,26 @@
+TEST_PURPOSE="DesktopFileIDs in submenus"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>Game</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications/company/games gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/Directory/result b/menu/tests/tests/Directory/result
new file mode 100644
index 0000000..2cd1d1f
--- /dev/null
+++ b/menu/tests/tests/Directory/result
@@ -0,0 +1,3 @@
+Apps/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Apps/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Apps/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
diff --git a/menu/tests/tests/Directory/test b/menu/tests/tests/Directory/test
new file mode 100644
index 0000000..fe7e126
--- /dev/null
+++ b/menu/tests/tests/Directory/test
@@ -0,0 +1,29 @@
+TEST_PURPOSE="<Directory> tag ..."
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+ <DefaultDirectoryDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Directory>apps.directory</Directory>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop
+ installData ${XDG_DATA_DIR}/desktop-directories apps.directory
+}
diff --git a/menu/tests/tests/DirectoryDir-relative/result b/menu/tests/tests/DirectoryDir-relative/result
new file mode 100644
index 0000000..2cd1d1f
--- /dev/null
+++ b/menu/tests/tests/DirectoryDir-relative/result
@@ -0,0 +1,3 @@
+Apps/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Apps/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Apps/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
diff --git a/menu/tests/tests/DirectoryDir-relative/test b/menu/tests/tests/DirectoryDir-relative/test
new file mode 100644
index 0000000..4a5e039
--- /dev/null
+++ b/menu/tests/tests/DirectoryDir-relative/test
@@ -0,0 +1,28 @@
+TEST_PURPOSE="relative <DirectoryDir> tag ..."
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+ <DirectoryDir>${PATH_EXPANSION}desktop-directories</DirectoryDir>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Directory>apps.directory</Directory>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop
+ installData ${XDG_CONFIG_DIR}/menus/desktop-directories apps.directory
+}
diff --git a/menu/tests/tests/DirectoryDir/test b/menu/tests/tests/DirectoryDir/test
new file mode 100644
index 0000000..dc8bb3b
--- /dev/null
+++ b/menu/tests/tests/DirectoryDir/test
@@ -0,0 +1,4 @@
+. tests/DirectoryDir-relative/test
+export PATH_EXPANSION='${XDG_CONFIG_DIR}/menus/'
+TEST_PURPOSE="<DirectoryDir> absolute path"
+
diff --git a/menu/tests/tests/Exclude/result b/menu/tests/tests/Exclude/result
new file mode 100644
index 0000000..dfbb122
--- /dev/null
+++ b/menu/tests/tests/Exclude/result
@@ -0,0 +1,3 @@
+Applications/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop
+Applications/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop
+Applications/ mahjongg.desktop ${XDG_DATA_DIR}/applications/mahjongg.desktop
diff --git a/menu/tests/tests/Exclude/test b/menu/tests/tests/Exclude/test
new file mode 100644
index 0000000..728ec99
--- /dev/null
+++ b/menu/tests/tests/Exclude/test
@@ -0,0 +1,32 @@
+TEST_PURPOSE="<Exclude> Keyword"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Or>
+ <Filename>freecell.desktop</Filename>
+ <Category>Game</Category>
+ </Or>
+ </Include>
+ <Exclude>
+ <Filename>glines.desktop</Filename>
+ </Exclude>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/Filename/result b/menu/tests/tests/Filename/result
new file mode 100644
index 0000000..dc159fa
--- /dev/null
+++ b/menu/tests/tests/Filename/result
@@ -0,0 +1 @@
+Applications/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop
diff --git a/menu/tests/tests/Filename/test b/menu/tests/tests/Filename/test
new file mode 100644
index 0000000..b9d021d
--- /dev/null
+++ b/menu/tests/tests/Filename/test
@@ -0,0 +1,26 @@
+TEST_PURPOSE="<Filename> tag"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Filename>freecell.desktop</Filename>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/LegacyDir-Move/result b/menu/tests/tests/LegacyDir-Move/result
new file mode 100644
index 0000000..9a2b9db
--- /dev/null
+++ b/menu/tests/tests/LegacyDir-Move/result
@@ -0,0 +1,2 @@
+Editors/ gideon-legacy.desktop ${LEGACY_DIR}/Development/gideon-legacy.desktop
+/ Home.desktop ${LEGACY_DIR}/Home.desktop
diff --git a/menu/tests/tests/LegacyDir-Move/test b/menu/tests/tests/LegacyDir-Move/test
new file mode 100644
index 0000000..7e6e85e
--- /dev/null
+++ b/menu/tests/tests/LegacyDir-Move/test
@@ -0,0 +1,39 @@
+TEST_PURPOSE="move entries from <LegacyDir>"
+
+test_code() {
+ LEGACY_DIR=${MENUTESTDIR}/legacy_applnk
+ export LEGACY_DIR
+
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <LegacyDir>${LEGACY_DIR}</LegacyDir>
+ <DefaultAppDirs/>
+ <DefaultDirectoryDirs/>
+
+ <Menu>
+ <Name>Development</Name>
+ <Exclude>
+ <Filename>gideon-legacy.desktop</Filename>
+ </Exclude>
+ </Menu>
+ <Menu>
+ <Name>Editors</Name>
+ <Include>
+ <Filename>gideon-legacy.desktop</Filename>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+
+ installData ${LEGACY_DIR} Home.desktop
+ installData ${LEGACY_DIR}/Development gideon-legacy.desktop
+}
diff --git a/menu/tests/tests/LegacyDir-relative/result b/menu/tests/tests/LegacyDir-relative/result
new file mode 100644
index 0000000..0c2af9b
--- /dev/null
+++ b/menu/tests/tests/LegacyDir-relative/result
@@ -0,0 +1,9 @@
+Development/ gideon-legacy.desktop ${LEGACY_DIR}/Development/gideon-legacy.desktop
+Development/ kbabel.desktop ${LEGACY_DIR}/Development/kbabel.desktop
+Development/ quanta.desktop ${LEGACY_DIR}/Development/quanta.desktop
+Editors/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Editors/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Editors/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+/ Help.desktop ${LEGACY_DIR}/Help.desktop
+/ Home.desktop ${LEGACY_DIR}/Home.desktop
+/ Kfind.desktop ${LEGACY_DIR}/Kfind.desktop
diff --git a/menu/tests/tests/LegacyDir-relative/test b/menu/tests/tests/LegacyDir-relative/test
new file mode 100644
index 0000000..5082590
--- /dev/null
+++ b/menu/tests/tests/LegacyDir-relative/test
@@ -0,0 +1,42 @@
+TEST_PURPOSE="Simple <LegacyDir> test"
+
+test_code() {
+ LEGACY_DIR=${MENUTESTDIR}/legacy_applnk
+ export LEGACY_DIR
+
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <LegacyDir>${LEGACY_DIR}</LegacyDir>
+ <DefaultAppDirs/>
+ <DefaultDirectoryDirs/>
+
+ <Menu>
+ <Name>Editors</Name>
+ <Directory>kde-editors.directory</Directory>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Development</Name>
+ <Directory>kde-development.directory</Directory>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop kate.desktop
+ installData ${LEGACY_DIR}/Development gideon-legacy.desktop kbabel.desktop quanta.desktop
+ installData ${LEGACY_DIR} Kfind.desktop Home.desktop Help.desktop
+}
+
diff --git a/menu/tests/tests/Merge-combined/result b/menu/tests/tests/Merge-combined/result
new file mode 100644
index 0000000..999bd1e
--- /dev/null
+++ b/menu/tests/tests/Merge-combined/result
@@ -0,0 +1 @@
+Editors/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
diff --git a/menu/tests/tests/Merge-combined/test b/menu/tests/tests/Merge-combined/test
new file mode 100644
index 0000000..07cfab3
--- /dev/null
+++ b/menu/tests/tests/Merge-combined/test
@@ -0,0 +1,46 @@
+TEST_PURPOSE="Merge Two Menus and a Legacy Menu"
+
+test_code() {
+ LEGACY_DIR=${MENUTESTDIR}/legacy_applnk
+ export LEGACY_DIR
+
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+ <DefaultDirectoryDirs/>
+ <LegacyDir>${LEGACY_DIR}</LegacyDir>
+
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Filename>kate.desktop</Filename>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Filename>KEdit.desktop</Filename>
+ </Include>
+ <Deleted/>
+ </Menu>
+ <Menu>
+ <Name>Editors</Name>
+ <Include>
+ <Filename>kwrite.desktop</Filename>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+
+ installData ${LEGACY_DIR}/Development gideon-legacy.desktop
+ installData ${XDG_DATA_DIR}/applications kate.desktop kwrite.desktop KEdit.desktop
+}
diff --git a/menu/tests/tests/MergeDir-absolute/test b/menu/tests/tests/MergeDir-absolute/test
new file mode 100644
index 0000000..db6c895
--- /dev/null
+++ b/menu/tests/tests/MergeDir-absolute/test
@@ -0,0 +1,3 @@
+. tests/MergeDir-relative/test
+export PATH_EXPANSION='${XDG_CONFIG_DIR}/menus/'
+export TEST_PURPOSE="<MergeDir> absolute path test"
diff --git a/menu/tests/tests/MergeDir-relative/result b/menu/tests/tests/MergeDir-relative/result
new file mode 100644
index 0000000..9e17189
--- /dev/null
+++ b/menu/tests/tests/MergeDir-relative/result
@@ -0,0 +1,5 @@
+Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Development/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop
+Development/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop
diff --git a/menu/tests/tests/MergeDir-relative/test b/menu/tests/tests/MergeDir-relative/test
new file mode 100644
index 0000000..ba96b1a
--- /dev/null
+++ b/menu/tests/tests/MergeDir-relative/test
@@ -0,0 +1,61 @@
+TEST_PURPOSE="<MergeDir> relative path ..."
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p "${XDG_CONFIG_DIR}/menus"
+ ./expand > "${XDG_CONFIG_DIR}/menus/applications.menu" <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <DefaultAppDirs/>
+ <MergeDir>${PATH_EXPANSION}applications-merged</MergeDir>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ mkdir "${XDG_CONFIG_DIR}/menus/applications-merged/"
+ ./expand > "${XDG_CONFIG_DIR}/menus/applications-merged/test.menu" <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # intentional crap entry to verify it does _not_ get picked up
+ ./expand > "${XDG_CONFIG_DIR}/menus/applications-merged/dar.notmenu" <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <Menu>
+ <Name>testing</Name>
+ <Include>
+ <All/>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+
+ # Install .desktop files
+ installData "${XDG_DATA_DIR}/applications" kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop
+}
diff --git a/menu/tests/tests/MergeFile-absolute/test b/menu/tests/tests/MergeFile-absolute/test
new file mode 100644
index 0000000..24cf669
--- /dev/null
+++ b/menu/tests/tests/MergeFile-absolute/test
@@ -0,0 +1,3 @@
+. tests/MergeFile-relative/test
+export PATH_EXPANSION='${XDG_CONFIG_DIR}/menus/'
+export TEST_PURPOSE="<MergeFile> absolute path"
diff --git a/menu/tests/tests/MergeFile-parent/result b/menu/tests/tests/MergeFile-parent/result
new file mode 100644
index 0000000..9e17189
--- /dev/null
+++ b/menu/tests/tests/MergeFile-parent/result
@@ -0,0 +1,5 @@
+Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Development/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop
+Development/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop
diff --git a/menu/tests/tests/MergeFile-parent/test b/menu/tests/tests/MergeFile-parent/test
new file mode 100644
index 0000000..00ed006
--- /dev/null
+++ b/menu/tests/tests/MergeFile-parent/test
@@ -0,0 +1,61 @@
+TEST_PURPOSE="<MergeFile> tag ..."
+
+test_code() {
+ # Tests the type attribute in <MergeFile>
+
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_HOME}/menus
+ ./expand > ${XDG_CONFIG_HOME}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <DefaultAppDirs/>
+ <MergeFile type="parent">${XDG_CONFIG_DIR}/menus/test.menu</MergeFile>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/test.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <Menu>
+ <Name>Games</Name>
+ <Include>
+ <Category>Game</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/MergeFile-path/result b/menu/tests/tests/MergeFile-path/result
new file mode 100644
index 0000000..fbe635e
--- /dev/null
+++ b/menu/tests/tests/MergeFile-path/result
@@ -0,0 +1,5 @@
+Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Games/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop
+Games/ glines.desktop ${XDG_DATA_DIR}/applications/glines.desktop
diff --git a/menu/tests/tests/MergeFile-path/test b/menu/tests/tests/MergeFile-path/test
new file mode 100644
index 0000000..97daa5c
--- /dev/null
+++ b/menu/tests/tests/MergeFile-path/test
@@ -0,0 +1,61 @@
+TEST_PURPOSE="<MergeFile> tag ..."
+
+test_code() {
+ # Tests the type attribute in <MergeFile>
+
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_HOME}/menus
+ ./expand > ${XDG_CONFIG_HOME}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <DefaultAppDirs/>
+ <MergeFile type="path">${XDG_CONFIG_DIR}/menus/test.menu</MergeFile>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/test.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <Menu>
+ <Name>Games</Name>
+ <Include>
+ <Category>Game</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/MergeFile-recursive/result b/menu/tests/tests/MergeFile-recursive/result
new file mode 100644
index 0000000..9e17189
--- /dev/null
+++ b/menu/tests/tests/MergeFile-recursive/result
@@ -0,0 +1,5 @@
+Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Development/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop
+Development/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop
diff --git a/menu/tests/tests/MergeFile-recursive/test b/menu/tests/tests/MergeFile-recursive/test
new file mode 100644
index 0000000..4dbab21
--- /dev/null
+++ b/menu/tests/tests/MergeFile-recursive/test
@@ -0,0 +1,58 @@
+TEST_PURPOSE="test elaborate recursive look in <MergeFile>s"
+
+test_code(){
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <DefaultAppDirs/>
+ <MergeFile>applications-merged/test.menu</MergeFile>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+
+ mkdir ${XDG_CONFIG_DIR}/menus/applications-merged/
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications-merged/test.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <MergeFile>extra/test.menu</MergeFile>
+</Menu>
+EOF
+
+ mkdir ${XDG_CONFIG_DIR}/menus/applications-merged/extra/
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications-merged/extra/test.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+
+ <MergeFile>../test.menu</MergeFile>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop
+}
diff --git a/menu/tests/tests/MergeFile-relative/result b/menu/tests/tests/MergeFile-relative/result
new file mode 100644
index 0000000..9e17189
--- /dev/null
+++ b/menu/tests/tests/MergeFile-relative/result
@@ -0,0 +1,5 @@
+Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Development/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop
+Development/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop
diff --git a/menu/tests/tests/MergeFile-relative/test b/menu/tests/tests/MergeFile-relative/test
new file mode 100644
index 0000000..c3478db
--- /dev/null
+++ b/menu/tests/tests/MergeFile-relative/test
@@ -0,0 +1,44 @@
+TEST_PURPOSE="<MergeFile> tag relative path"
+
+test_code(){
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <DefaultAppDirs/>
+ <MergeFile>${PATH_EXPANSION}applications-merged/test.menu</MergeFile>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ mkdir ${XDG_CONFIG_DIR}/menus/applications-merged/
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications-merged/test.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+<!-- test -->
+
+<Menu>
+ <Name>KDE</Name>
+
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop
+}
diff --git a/menu/tests/tests/MergeFile2/result b/menu/tests/tests/MergeFile2/result
new file mode 100644
index 0000000..9e17189
--- /dev/null
+++ b/menu/tests/tests/MergeFile2/result
@@ -0,0 +1,5 @@
+Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Development/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop
+Development/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop
diff --git a/menu/tests/tests/MergeFile2/test b/menu/tests/tests/MergeFile2/test
new file mode 100644
index 0000000..e5b1591
--- /dev/null
+++ b/menu/tests/tests/MergeFile2/test
@@ -0,0 +1,57 @@
+TEST_PURPOSE="<MergeFile> tag ..."
+
+test_code() {
+ # Tests the use of relative paths in <MergeFile>
+
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <DefaultAppDirs/>
+ <MergeFile>applications-merged/test.menu</MergeFile>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ mkdir ${XDG_CONFIG_DIR}/menus/applications-merged/
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications-merged/test.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <MergeFile>extra/test.menu</MergeFile>
+</Menu>
+EOF
+
+ mkdir ${XDG_CONFIG_DIR}/menus/applications-merged/extra/
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications-merged/extra/test.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop
+}
diff --git a/menu/tests/tests/MergeFile3/result b/menu/tests/tests/MergeFile3/result
new file mode 100644
index 0000000..9e17189
--- /dev/null
+++ b/menu/tests/tests/MergeFile3/result
@@ -0,0 +1,5 @@
+Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Development/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop
+Development/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop
diff --git a/menu/tests/tests/MergeFile3/test b/menu/tests/tests/MergeFile3/test
new file mode 100644
index 0000000..8781888
--- /dev/null
+++ b/menu/tests/tests/MergeFile3/test
@@ -0,0 +1,56 @@
+TEST_PURPOSE="<MergeFile> tag ..."
+
+test_code() {
+ # Tests the use of relative paths in <MergeFile>
+
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <DefaultAppDirs/>
+ <MergeFile>test.menu</MergeFile>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ ./expand > ${XDG_CONFIG_DIR}/menus/test.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <MergeFile>extra/test.menu</MergeFile>
+</Menu>
+EOF
+
+ mkdir ${XDG_CONFIG_DIR}/menus/extra/
+ ./expand > ${XDG_CONFIG_DIR}/menus/extra/test.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop
+}
diff --git a/menu/tests/tests/Move-collapsing/result b/menu/tests/tests/Move-collapsing/result
new file mode 100644
index 0000000..10ccec6
--- /dev/null
+++ b/menu/tests/tests/Move-collapsing/result
@@ -0,0 +1,4 @@
+Games/BoardGame/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop
+Games/BoardGame/ mahjongg.desktop ${XDG_DATA_DIR}/applications/mahjongg.desktop
+Games/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop
+Games/ glines.desktop ${XDG_DATA_DIR}/applications/glines.desktop
diff --git a/menu/tests/tests/Move-collapsing/test b/menu/tests/tests/Move-collapsing/test
new file mode 100644
index 0000000..b842b0a
--- /dev/null
+++ b/menu/tests/tests/Move-collapsing/test
@@ -0,0 +1,40 @@
+TEST_PURPOSE="complicated <Move> operation"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+ <Move>
+ <Old>Games1</Old>
+ <New>Games</New>
+ </Move>
+
+ <Menu>
+ <Name>Games1</Name>
+ <Menu>
+ <Name>BoardGame</Name>
+ <Include>
+ <Category>BoardGame</Category>
+ </Include>
+ </Menu>
+ </Menu>
+ <Menu>
+ <Name>Games</Name>
+ <Include>
+ <Category>Game</Category>
+ </Include>
+ <OnlyUnallocated/>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/Move-ordering/result b/menu/tests/tests/Move-ordering/result
new file mode 100644
index 0000000..b941f58
--- /dev/null
+++ b/menu/tests/tests/Move-ordering/result
@@ -0,0 +1,3 @@
+Development/ gideon.desktop ${XDG_DATA_DIR}/applications/gideon.desktop
+Games-Correct/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop
+Games-Correct/ glines.desktop ${XDG_DATA_DIR}/applications/glines.desktop
diff --git a/menu/tests/tests/Move-ordering/test b/menu/tests/tests/Move-ordering/test
new file mode 100644
index 0000000..f8883b9
--- /dev/null
+++ b/menu/tests/tests/Move-ordering/test
@@ -0,0 +1,49 @@
+TEST_PURPOSE="Order of <Move> operations ..."
+
+test_code() {
+ # Tests the type attribute in <MergeFile>
+
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <DefaultAppDirs/>
+ <Menu>
+ <Name>Development</Name>
+ <Menu>
+ <Name>Games</Name>
+ <Include>
+ <Category>Game</Category>
+ </Include>
+ </Menu>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+ <Move>
+ <Old>Development/Games-New</Old>
+ <New>Games-Correct</New>
+ </Move>
+ <Move>
+ <Old>Development/Games</Old>
+ <New>Games-Wrong</New>
+ </Move>
+ <Menu>
+ <Name>Development</Name>
+ <Move>
+ <Old>Games</Old>
+ <New>Games-New</New>
+ </Move>
+ </Menu>
+</Menu>
+EOF
+
+ # Move operations in sub-menus should be performed first
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications freecell.desktop glines.desktop gideon.desktop
+}
diff --git a/menu/tests/tests/Move-submenu/result b/menu/tests/tests/Move-submenu/result
new file mode 100644
index 0000000..f1c44ef
--- /dev/null
+++ b/menu/tests/tests/Move-submenu/result
@@ -0,0 +1 @@
+A/B/Development/ gideon-legacy.desktop ${XDG_DATA_DIR}/applications/gideon-legacy.desktop
diff --git a/menu/tests/tests/Move-submenu/test b/menu/tests/tests/Move-submenu/test
new file mode 100644
index 0000000..2751983
--- /dev/null
+++ b/menu/tests/tests/Move-submenu/test
@@ -0,0 +1,32 @@
+TEST_PURPOSE="Move into a new Submenu"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+ <DefaultDirectoryDirs/>
+
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Filename>gideon-legacy.desktop</Filename>
+ </Include>
+ </Menu>
+ <Move>
+ <Old>Development</Old>
+ <New>A/B/Development</New>
+ </Move>
+</Menu>
+EOF
+
+ # Install .desktop files
+
+ installData ${XDG_DATA_DIR}/applications gideon-legacy.desktop
+}
diff --git a/menu/tests/tests/Move/result b/menu/tests/tests/Move/result
new file mode 100644
index 0000000..d9a802d
--- /dev/null
+++ b/menu/tests/tests/Move/result
@@ -0,0 +1,2 @@
+Games/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop
+Games/ mahjongg.desktop ${XDG_DATA_DIR}/applications/mahjongg.desktop
diff --git a/menu/tests/tests/Move/test b/menu/tests/tests/Move/test
new file mode 100644
index 0000000..9f2d162
--- /dev/null
+++ b/menu/tests/tests/Move/test
@@ -0,0 +1,34 @@
+TEST_PURPOSE="simple <Move> operation"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+ <Move>
+ <Old>Foo</Old>
+ <New>Bar</New>
+ <Old>BoardGames</Old>
+ <New>Apps</New>
+ <Old>BoardGames</Old>
+ <New>Games</New>
+ </Move>
+
+ <Menu>
+ <Name>BoardGames</Name>
+ <Include>
+ <Category>BoardGame</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/NoDisplay/result b/menu/tests/tests/NoDisplay/result
new file mode 100644
index 0000000..7ed7169
--- /dev/null
+++ b/menu/tests/tests/NoDisplay/result
@@ -0,0 +1 @@
+Other/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop
diff --git a/menu/tests/tests/NoDisplay/test b/menu/tests/tests/NoDisplay/test
new file mode 100644
index 0000000..48e94ea
--- /dev/null
+++ b/menu/tests/tests/NoDisplay/test
@@ -0,0 +1,37 @@
+TEST_PURPOSE="NoDisplay desktop entry values"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <DefaultAppDirs/>
+ <DefaultDirectoryDirs/>
+
+ <Menu>
+ <Name>Shouldn't see this</Name>
+ <Directory>hidden.directory</Directory>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+
+ <Menu>
+ <Name>Other</Name>
+ <OnlyUnallocated/>
+ <Include>
+ <All/>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop kate.desktop freecell.desktop hidden.desktop
+ installData ${XDG_DATA_DIR}/desktop-directories hidden.directory
+}
diff --git a/menu/tests/tests/NoDisplay2/result b/menu/tests/tests/NoDisplay2/result
new file mode 100644
index 0000000..7ed7169
--- /dev/null
+++ b/menu/tests/tests/NoDisplay2/result
@@ -0,0 +1 @@
+Other/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop
diff --git a/menu/tests/tests/NoDisplay2/test b/menu/tests/tests/NoDisplay2/test
new file mode 100644
index 0000000..62ba5a7
--- /dev/null
+++ b/menu/tests/tests/NoDisplay2/test
@@ -0,0 +1,38 @@
+TEST_PURPOSE="Allocation of desktop entry values from deleted menus"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <DefaultAppDirs/>
+ <DefaultDirectoryDirs/>
+
+ <Menu>
+ <Name>Shouldn't see this</Name>
+ <Directory>apps.directory</Directory>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ <Deleted/>
+ </Menu>
+
+ <Menu>
+ <Name>Other</Name>
+ <OnlyUnallocated/>
+ <Include>
+ <All/>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop kate.desktop freecell.desktop hidden.desktop
+ installData ${XDG_DATA_DIR}/desktop-directories apps.directory
+}
diff --git a/menu/tests/tests/NotOnlyUnallocated-default/result b/menu/tests/tests/NotOnlyUnallocated-default/result
new file mode 100644
index 0000000..0c398b7
--- /dev/null
+++ b/menu/tests/tests/NotOnlyUnallocated-default/result
@@ -0,0 +1,2 @@
+Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Editors/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
diff --git a/menu/tests/tests/NotOnlyUnallocated-default/test b/menu/tests/tests/NotOnlyUnallocated-default/test
new file mode 100644
index 0000000..2f4956e
--- /dev/null
+++ b/menu/tests/tests/NotOnlyUnallocated-default/test
@@ -0,0 +1,33 @@
+TEST_PURPOSE="Another <OnlyUnallocated> test"
+
+test_code() {
+ # Tests <OnlyUnallocated>
+
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Filename>kwrite.desktop</Filename>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Editors</Name>
+ <Include>
+ <Filename>kwrite.desktop</Filename>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop
+}
diff --git a/menu/tests/tests/OnlyUnallocated/result b/menu/tests/tests/OnlyUnallocated/result
new file mode 100644
index 0000000..8be4bc7
--- /dev/null
+++ b/menu/tests/tests/OnlyUnallocated/result
@@ -0,0 +1,3 @@
+BoardGames/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop
+BoardGames/ mahjongg.desktop ${XDG_DATA_DIR}/applications/mahjongg.desktop
+Games/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop
diff --git a/menu/tests/tests/OnlyUnallocated/test b/menu/tests/tests/OnlyUnallocated/test
new file mode 100644
index 0000000..efda0ff
--- /dev/null
+++ b/menu/tests/tests/OnlyUnallocated/test
@@ -0,0 +1,44 @@
+TEST_PURPOSE="<OnlyAllocated> tag"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>BoardGames</Name>
+ <Include>
+ <And>
+ <Category>Game</Category>
+ <Not>
+ <Category>CardGame</Category>
+ </Not>
+ </And>
+ </Include>
+ <Exclude>
+ <Category>PuzzleGame</Category>
+ </Exclude>
+ </Menu>
+ <Menu>
+ <Name>Games</Name>
+ <Include>
+ <Category>Game</Category>
+ </Include>
+ <OnlyUnallocated/>
+ <NotOnlyUnallocated/>
+ <OnlyUnallocated/>
+ </Menu>
+</Menu>
+EOF
+
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/Or/result b/menu/tests/tests/Or/result
new file mode 100644
index 0000000..fc75a82
--- /dev/null
+++ b/menu/tests/tests/Or/result
@@ -0,0 +1,4 @@
+Applications/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop
+Applications/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop
+Applications/ glines.desktop ${XDG_DATA_DIR}/applications/glines.desktop
+Applications/ mahjongg.desktop ${XDG_DATA_DIR}/applications/mahjongg.desktop
diff --git a/menu/tests/tests/Or/test b/menu/tests/tests/Or/test
new file mode 100644
index 0000000..cd10cfd
--- /dev/null
+++ b/menu/tests/tests/Or/test
@@ -0,0 +1,29 @@
+TEST_PURPOSE="<Or> Keyword"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Or>
+ <Filename>freecell.desktop</Filename>
+ <Category>Game</Category>
+ </Or>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/boolean-logic/result b/menu/tests/tests/boolean-logic/result
new file mode 100644
index 0000000..ea47f01
--- /dev/null
+++ b/menu/tests/tests/boolean-logic/result
@@ -0,0 +1,3 @@
+Apps/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Apps/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Apps/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
diff --git a/menu/tests/tests/boolean-logic/test b/menu/tests/tests/boolean-logic/test
new file mode 100644
index 0000000..dc045c4
--- /dev/null
+++ b/menu/tests/tests/boolean-logic/test
@@ -0,0 +1,36 @@
+TEST_PURPOSE="<And><Category>foo</Category><Not><Category>foo</Category></Not></And> shouldn't match anything"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <DefaultAppDirs/>
+ <DefaultDirectoryDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Directory>apps.directory</Directory>
+ <Include>
+ <Or>
+ <Category>TextEditor</Category>
+ <And>
+ <Category>Game</Category>
+ <Not><Category>Game</Category></Not>
+ </And>
+ </Or>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop kate.desktop
+ installData ${XDG_DATA_DIR}/applications/test freecell.desktop
+ installData ${XDG_DATA_DIR}/desktop-directories apps.directory
+}
diff --git a/menu/tests/tests/desktop-name-collision/result b/menu/tests/tests/desktop-name-collision/result
new file mode 100644
index 0000000..e1e9221
--- /dev/null
+++ b/menu/tests/tests/desktop-name-collision/result
@@ -0,0 +1,3 @@
+Development/ kde-gideon.desktop ${XDG_DATA_HOME}/applications/kde-gideon.desktop
+Development/ mahjongg.desktop ${XDG_DATA_HOME}/applications/mahjongg.desktop
+Games/ freecell.desktop ${XDG_DATA_HOME}/applications/freecell.desktop
diff --git a/menu/tests/tests/desktop-name-collision/test b/menu/tests/tests/desktop-name-collision/test
new file mode 100644
index 0000000..46b5ded
--- /dev/null
+++ b/menu/tests/tests/desktop-name-collision/test
@@ -0,0 +1,53 @@
+TEST_PURPOSE=".desktop files with same name ..."
+
+test_code() {
+ # Tests the type attribute in <MergeFile>
+
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <DefaultAppDirs/>
+ <Menu>
+ <Name>Games</Name>
+ <Include>
+ <Category>Game</Category>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications freecell.desktop glines.desktop mahjongg.desktop
+
+ # freecell.desktop is installed twice, only the version under ${XDG_DATA_HOME}/applications
+ # should show up in the menu
+ # freecell.desktop in ${XDG_DATA_DIR}/applications should be ignored.
+ installData ${XDG_DATA_HOME}/applications freecell.desktop
+
+ # ${XDG_DATA_HOME}/applications/glines.desktop has NoDisplay=true
+ # glines.desktop should not be shown
+ # glines.desktop in ${XDG_DATA_DIR}/applications should be ignored.
+ installDataAs ${XDG_DATA_HOME}/applications glines-2.desktop glines.desktop
+
+ # ${XDG_DATA_HOME}/applications/mahjongg.desktop has Categories=Development
+ # mahjongg.desktop should be shown under the Development menu
+ # mahjongg.desktop in ${XDG_DATA_DIR}/applications should be ignored.
+ installDataAs ${XDG_DATA_HOME}/applications mahjongg-2.desktop mahjongg.desktop
+
+ # kde/gideon.desktop and kde-gideon.desktop are equivalent
+ # only the version under ${XDG_DATA_HOME}/applications should show up in the menu
+ # gideon.desktop in ${XDG_DATA_DIR}/applications/kde should be ignored.
+ installDataAs ${XDG_DATA_DIR}/applications/kde gideon.desktop
+ installDataAs ${XDG_DATA_HOME}/applications gideon.desktop kde-gideon.desktop
+}
diff --git a/menu/tests/tests/menu-multiple-matching/result b/menu/tests/tests/menu-multiple-matching/result
new file mode 100644
index 0000000..986c873
--- /dev/null
+++ b/menu/tests/tests/menu-multiple-matching/result
@@ -0,0 +1,5 @@
+Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Applications/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop
+Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Applications/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop
diff --git a/menu/tests/tests/menu-multiple-matching/test b/menu/tests/tests/menu-multiple-matching/test
new file mode 100644
index 0000000..d8914ab
--- /dev/null
+++ b/menu/tests/tests/menu-multiple-matching/test
@@ -0,0 +1,36 @@
+TEST_PURPOSE="complicated rule ..."
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <And>
+ <Category>Game</Category>
+ <Category>BoardGame</Category>
+ </And>
+ <Or>
+ <Category>TextEditor</Category>
+ <Filename>quanta.desktop</Filename>
+ </Or>
+ </Include>
+ <Exclude>
+ <Filename>mahjongg.desktop</Filename>
+ </Exclude>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop
+}
diff --git a/menu/tests/tests/official-categories/categories.list b/menu/tests/tests/official-categories/categories.list
new file mode 100644
index 0000000..6e2198d
--- /dev/null
+++ b/menu/tests/tests/official-categories/categories.list
@@ -0,0 +1,10 @@
+AudioVideo
+Development
+Education
+Game
+Graphics
+Network
+Office
+Settings
+System
+Utility
diff --git a/menu/tests/tests/official-categories/test b/menu/tests/tests/official-categories/test
new file mode 100644
index 0000000..09621cd
--- /dev/null
+++ b/menu/tests/tests/official-categories/test
@@ -0,0 +1,73 @@
+TEST_PURPOSE="verify all required categories are supported"
+
+base_loc="tests/official-categories"
+
+test_code()
+{
+ local category
+ for category in $(< "${base_loc}/categories.list"); do
+ CATEGORY="${category}" ./expand "${base_loc}/unique-entry.desktop" > "data/${category}.desktop"
+ installData "${XDG_DATA_DIR}/applications" "${category}.desktop"
+ rm "data/${category}.desktop"
+ done
+}
+
+query()
+{
+ echo "$@"
+ ret=''
+ while [ -z "$ret" ]; do
+ echo -n "y/n? :"
+ read ret
+ if ! [ "$ret" == "y" -o "$ret" == "n" ]; then
+ echo "invalid response; must be 'y' or 'n'"
+ ret=''
+ fi
+ done
+ [ "$ret" == "y" ] && return 0
+ return 1
+}
+
+interpret_results()
+{
+ # inefficient, but works.
+ local missed=''
+ local correct=''
+ for category in $(< "${base_loc}/categories.list"); do
+ if grep "/${category}\.desktop" "${MENUTESTDIR}/run-result" > /dev/null; then
+ correct="${correct} ${category}"
+ else
+ missed="${missed} ${category}"
+ fi
+ done
+ if [ -z "${missed}" ]; then
+ echo ">>> OK"
+ return 0
+ fi
+ if [ "$(echo $missed)" != "Settings" ]; then
+ # failures.
+ cat "${MENUTESTDIR}/run-result"
+ echo "missed categories $missed"
+ echo "matched ${correct}"
+ echo ">>> Failed (missed $(echo $missed | wc -w) out of $(wc -l "${base_loc}/categories.list")"
+ return 1
+ fi
+ echo ">>> Settings failed; checking interactively"
+ local ret
+ if [ "$(id -u)" != "0" ]; then
+ echo ">>> Cannot go interactive due to test being ran as non-root; re-run as root"
+ return 1;
+ elif ! which xdg-desktop-menu &> /dev/null; then
+ echo ">>> xdg-desktop-menu is not available; cannot do interactive test"
+ return 1;
+ fi
+ xdg-desktop-menu install --mode system --novendor "${XDG_DATA_DIR}/applications/Settings.desktop"
+ (
+ query "Please check for a 'menu-spec-testing' in any gnome/kde system settings panel"
+ )
+ ret=$?
+ xdg-desktop-menu uninstall --mode system "${XDG_DATA_DIR}/applications/Settings.desktop"
+ return $(($ret))
+}
+
+MODE=system_data
diff --git a/menu/tests/tests/official-categories/unique-entry.desktop b/menu/tests/tests/official-categories/unique-entry.desktop
new file mode 100644
index 0000000..6ee3b88
--- /dev/null
+++ b/menu/tests/tests/official-categories/unique-entry.desktop
@@ -0,0 +1,9 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=menu-spec-testing
+Exec=true
+Icon=quanta
+Type=Application
+MimeType=text/html
+Comment=menu-spec testing
+Categories=${CATEGORY};
diff --git a/menu/tests/tests/submenu-collision/result b/menu/tests/tests/submenu-collision/result
new file mode 100644
index 0000000..6aa47d7
--- /dev/null
+++ b/menu/tests/tests/submenu-collision/result
@@ -0,0 +1,5 @@
+Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Applications/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop
+Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Applications/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop
diff --git a/menu/tests/tests/submenu-collision/test b/menu/tests/tests/submenu-collision/test
new file mode 100644
index 0000000..7d29896
--- /dev/null
+++ b/menu/tests/tests/submenu-collision/test
@@ -0,0 +1,32 @@
+TEST_PURPOSE="two submenus with the same name ..."
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop
+}
diff --git a/menu/tests/tet_menutest b/menu/tests/tet_menutest
new file mode 100755
index 0000000..1aae67e
--- /dev/null
+++ b/menu/tests/tet_menutest
@@ -0,0 +1,39 @@
+#!/bin/bash
+TET_RUN="asdf"
+export MENUTESTDIR="${MENUTESTDIR:-/tmp/menutest}"
+. menutest
+# hack, figure out the var to use here
+
+tpstart() {
+ tet_infoline "$*"
+ FAIL=N
+}
+
+tet_startup=''
+tet_cleanup=''
+declare -i count=1
+iclist=''
+echo $TESTS
+for TESTCASE in ${TESTS}; do
+ [ ! -e "tests/${TESTCASE}/test" ] && continue;
+ # this basically curries the arg to run_test.
+ eval "tp${count}() {
+ . tests/${TESTCASE}/test
+ tpstart \"$(. tests/${TESTCASE}/test; echo ${TEST_PURPOSE-none stated})\";
+ if ! run_test tests/\"$TESTCASE\"; then
+ tet_result FAIL;
+ else
+ tet_result PASS;
+ fi
+ set +x
+ }";
+ iclist="${iclist} ic${count}"
+ eval "ic${count}=tp${count}"
+ # force subshelling, so that it doesn't pull a die on us
+ ((count+=1))
+done
+tet_iclist=iclist
+. /opt/lsb-tet3-lite/lib/posix_sh/tcm.sh
+tet_outputline 100 "xdg menu test"
+tet_tcm_main $tet_iclist
+
diff --git a/menu/tet_scen b/menu/tet_scen
new file mode 100644
index 0000000..4fe544e
--- /dev/null
+++ b/menu/tet_scen
@@ -0,0 +1,5 @@
+all
+ "Starting menu tests"
+ /tests/tet_menutest
+ "finished menu tests"
+
diff --git a/menu/tetexec.cfg b/menu/tetexec.cfg
new file mode 100644
index 0000000..ea150c9
--- /dev/null
+++ b/menu/tetexec.cfg
@@ -0,0 +1 @@
+TET_PASS_TC_NAME=True
diff --git a/secret-service/.gitignore b/secret-service/.gitignore
new file mode 100644
index 0000000..551e415
--- /dev/null
+++ b/secret-service/.gitignore
@@ -0,0 +1,3 @@
+
+/html/*.html
+reference.xml
diff --git a/secret-service/Makefile b/secret-service/Makefile
new file mode 100644
index 0000000..00efeb9
--- /dev/null
+++ b/secret-service/Makefile
@@ -0,0 +1,17 @@
+
+SPEC = org.freedesktop.Secrets.xml
+
+all: html/index.html
+
+html/index.html: reference.xml docbook-params.xsl specification.xml
+ xmlto --skip-validation -o html/ -x docbook-params.xsl xhtml specification.xml
+
+reference.xml: tools/spec-to-docbook.xsl $(SPEC)
+ xsltproc tools/spec-to-docbook.xsl $(SPEC) > $@
+
+clean:
+ rm -f reference.xml
+ rm -f html/*.html
+
+upload: all
+ rsync -Hvax html/./ specs.freedesktop.org:/srv/specifications.freedesktop.org/www/secret-service/./
diff --git a/secret-service/README b/secret-service/README
new file mode 100644
index 0000000..72a9997
--- /dev/null
+++ b/secret-service/README
@@ -0,0 +1,20 @@
+Generating the specification consists of separate steps:
+
+1. Use an xslt processor to create a DocBook XML document from the Telepathy
+ Introspection document using the provided stylesheet:
+
+$ xsltproc tools/spec-to-docbook.xsl \
+ org.freedesktop.Secrets.xml >reference.xml
+
+2. Convert specification.xml and reference.xml to a suitable output format
+ (specification.xml includes reference.xml), eg html:
+
+$ xmlto --skip-validation -o html -p params-html.xsl xhtml specification.xml
+
+If you choose a different directory for html generation, be sure to copy
+html/style.css to that location.
+
+Then open the resulting html/index.html in your favourite browser.
+
+
+Michael Leupold <lemma@confuego.org>
diff --git a/secret-service/docbook-params.xsl b/secret-service/docbook-params.xsl
new file mode 100644
index 0000000..5d8591a
--- /dev/null
+++ b/secret-service/docbook-params.xsl
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<!--
+ Parameters for DocBook transformation.
+
+ Copyright (C) 2009 Michael Leupold <lemma@confuego.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+-->
+
+ <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/xhtml/chunk.xsl"/>
+
+ <xsl:param name="toc.max.depth">3</xsl:param>
+ <xsl:param name="generate.section.toc.level">0</xsl:param>
+ <xsl:param name="generate.toc">
+ book toc
+ part nop
+ chapter toc
+ </xsl:param>
+ <xsl:param name="html.stylesheet">style.css</xsl:param>
+ <xsl:param name="funcsynopsis.style">ansi</xsl:param>
+ <xsl:param name="funcsynopsis.decoration">1</xsl:param>
+ <xsl:param name="refentry.generate.name">0</xsl:param>
+ <xsl:param name="refentry.generate.title">1</xsl:param>
+
+</xsl:stylesheet>
diff --git a/secret-service/html/style.css b/secret-service/html/style.css
new file mode 100644
index 0000000..f8a03b0
--- /dev/null
+++ b/secret-service/html/style.css
@@ -0,0 +1,127 @@
+/* reference.css, a stylesheet for reference documentation
+ * generated by the DocBook XSL Stylesheets */
+/* $Id: reference.css 8234 2009-02-09 12:10:48Z xmldoc $ */
+
+div.legalnotice {
+ font-size: 80%;
+}
+
+div.note, div.tip, div.warning {
+ margin-left: 5%;
+ margin-right: 10%;
+ padding: 5px;
+}
+
+div.note, div.tip {
+ border-left: solid #d5dee3 20px;
+ border-right: solid #d5dee3 20px;
+}
+
+div.note, div.tip {
+ border-left: solid palegreen 20px;
+ border-right: solid palegreen 20px;
+}
+
+div.warning {
+ border-left: solid yellow 20px;
+ border-right: solid yellow 20px;
+}
+
+div.note p, div.tip p, div.warning p {
+ margin-top: 0px;
+ margin-bottom: 4px;
+}
+
+div.note h3, div.tip h3, div.warning h3 {
+ margin-top: 0;
+}
+
+div.informalexample {
+ background-color: #d5dee3;
+ border-top-width: 2px;
+ border-top-style: double;
+ border-top-color: #d3d3d3;
+ border-bottom-width: 2px;
+ border-bottom-style: double;
+ border-bottom-color: #d3d3d3;
+ padding: 4px;
+ margin: 0em;
+ margin-left: 2em;
+}
+
+pre.programlisting, pre.synopsis {
+ whitespace: pre;
+ font-family: monospace;
+ background-color: #d5dee3;
+ border-top-width: 1px;
+ border-top-style: single;
+ border-top-color: #d3d3d3;
+ border-bottom-width: 1px;
+ border-bottom-style: single;
+ border-bottom-color: #d3d3d3;
+ padding: 4px;
+ margin: 0em;
+ margin-top: 6px;
+ margin-bottom: 6px;
+}
+
+div.informalexample pre {
+ whitespace: pre;
+ font-family: monospace;
+ border-top-width: 0px;
+ border-bottom-width: 0px;
+ padding: 0px;
+}
+
+/* Parameter and PI titles */
+ div.refnamediv h2 {
+ font-size: 2em;
+}
+
+div.funcsynopsis, code.fieldsynopsis,
+div.refsect2 div.refsynopsisdiv {
+ padding: 0.5em;
+ background-color: #F4F4F4;
+ border: 1px solid gray;
+ display: block;
+ width: 80%;
+}
+
+div.refsynopsisdiv code.fieldsynopsis {
+ padding: 0;
+ border: 0;
+}
+
+code.fieldsynopsis span.modifier {
+ min-width: 8em;
+ display: inline-block;
+}
+
+code.fieldsynopsis span.type {
+ min-width: 11em;
+ display: inline-block;
+}
+
+code.fieldsynopsis span.varname {
+ min-width: 11em;
+ display: inline-block;
+ font-size: 1.1em;
+ font-weight: bold;
+}
+
+div.funcprototype-spacer {
+ max-height: 0.5em;
+}
+
+table.funcprototype-table * td {
+ font-size: 0.9em;
+}
+
+table.funcprototype-table * td code.funcdef {
+ font-size: 1.1em;
+}
+
+b.fsfunc {
+ display: inline-block;
+ min-width: 11em;
+} \ No newline at end of file
diff --git a/secret-service/org.freedesktop.Secrets.xml b/secret-service/org.freedesktop.Secrets.xml
new file mode 100644
index 0000000..b6fba64
--- /dev/null
+++ b/secret-service/org.freedesktop.Secrets.xml
@@ -0,0 +1,490 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+
+<!--
+ * This is the well known dbus service name for controlling the
+ * entire daemon. The service manages collections of secrets. These
+ * are analogous to the gnome-keyring 'keyrings'.
+-->
+<tp:spec xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+ <tp:title>Secret Service API Specification</tp:title>
+ <tp:version>0.1</tp:version>
+ <tp:copyright>
+ Copyright (C) 2009 Stef Walter &lt;stef@memberwebs.com&gt;
+ </tp:copyright>
+ <tp:copyright>
+ Copyright (C) 2009 Michael Leupold &lt;lemma@confuego.org&gt;
+ </tp:copyright>
+
+ <!--
+ * ======================================================================================
+ * ERROR CODES
+ *
+ -->
+
+ <tp:errors namespace="org.freedesktop.Secret.Error">
+ <tp:docstring>Errors returned by the Secret Service API.</tp:docstring>
+ <tp:error name="IsLocked">
+ <tp:docstring>The object must be unlocked before this action can be carried out.</tp:docstring>
+ </tp:error>
+ <tp:error name="NoSession">
+ <tp:docstring>The session does not exist.</tp:docstring>
+ </tp:error>
+ <tp:error name="NoSuchObject">
+ <tp:docstring>No such item or collection exists.</tp:docstring>
+ </tp:error>
+ </tp:errors>
+
+ <!--
+ * ======================================================================================
+ * SECRET STRUCT
+ * Signature: (oayays)
+ *
+ -->
+
+ <tp:struct name="Secret">
+ <tp:docstring>The Secret type holds a (possibly encoded) secret.</tp:docstring>
+ <tp:member type="o" name="session">
+ <tp:docstring>The session that was used to encode the secret.</tp:docstring>
+ </tp:member>
+ <tp:member type="ay" name="parameters">
+ <tp:docstring>Algorithm dependent parameters for secret value encoding.</tp:docstring>
+ </tp:member>
+ <tp:member type="ay" name="value">
+ <tp:docstring>Possibly encoded secret value</tp:docstring>
+ </tp:member>
+ <tp:member type="s" name="content_type">
+ <tp:docstring>The content type of the secret. For example: 'text/plain; charset=utf8'</tp:docstring>
+ </tp:member>
+ </tp:struct>
+
+ <tp:mapping name="ObjectPath_Secret_Map">
+ <tp:docstring>A mapping from object-paths to Secret structs</tp:docstring>
+ <tp:member type="o" name="Key">
+ <tp:docstring>D-Bus object-path</tp:docstring>
+ </tp:member>
+ <tp:member type="(oayays)" name="Value" tp:type="Secret">
+ <tp:docstring>A secret</tp:docstring>
+ </tp:member>
+ </tp:mapping>
+
+ <!--
+ * ======================================================================================
+ * SERVICE INTERFACE
+ *
+ -->
+
+ <node name="/org/freedesktop/Secrets">
+
+ <interface name="org.freedesktop.Secret.Service">
+
+ <tp:docstring>
+ The Secret Service manages all the sessions and collections.
+ </tp:docstring>
+
+ <property name="Collections" type="ao" access="read">
+ <tp:docstring>The object paths of all collections (ie: keyrings)</tp:docstring>
+ </property>
+
+ <method name="OpenSession">
+ <tp:docstring>Open a unique session for the caller application.</tp:docstring>
+ <arg name="algorithm" type="s" direction="in">
+ <tp:docstring>The algorithm the caller wishes to use.</tp:docstring>
+ </arg>
+ <arg name="input" type="v" direction="in">
+ <tp:docstring>Input arguments for the algorithm.</tp:docstring>
+ </arg>
+ <arg name="output" type="v" direction="out">
+ <tp:docstring>Output of the session algorithm negotiation.</tp:docstring>
+ </arg>
+ <arg name="result" type="o" direction="out">
+ <tp:docstring>The object path of the session, if session was created.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="CreateCollection">
+ <tp:docstring>Create a new collection with the specified properties.</tp:docstring>
+ <arg name="properties" type="a{sv}" direction="in">
+ <tp:docstring xmlns:docbook="http://docbook.org/ns/docbook">
+ <para>
+ Properties for the new collection. This allows setting the new collection's
+ properties upon its creation. All READWRITE properties are useable. Specify
+ the property names in full interface.Property form.
+ <example>
+ <title>Example for properties</title>
+ <programlisting>
+<![CDATA[
+properties = { "org.freedesktop.Secret.Collection.Label": "MyCollection" }
+]]>
+ </programlisting>
+ </example>
+ </para>
+ </tp:docstring>
+ </arg>
+ <arg name="alias" type="s" direction="in">
+ <tp:docstring xmlns:docbook="http://docbook.org/ns/docbook">
+ <para>
+ If creating this connection for a well known alias then a string like
+ <literal>default</literal>. If an collection with this well-known alias already
+ exists, then that collection will be returned instead of creating a new
+ collection. Any readwrite properties provided to this function will be set on
+ the collection.
+ </para>
+ <para>
+ Set this to an empty string if the new collection should not be associated with a
+ well known alias.
+ </para>
+ </tp:docstring>
+ </arg>
+ <arg name="collection" type="o" direction="out">
+ <tp:docstring>The new collection object, or '/' if prompting is necessary.</tp:docstring>
+ </arg>
+ <arg name="prompt" type="o" direction="out">
+ <tp:docstring>A prompt object if prompting is necessary, or '/' if no prompt was needed.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="SearchItems">
+ <tp:docstring>Find items in any collection.</tp:docstring>
+ <arg name="attributes" type="a{ss}" direction="in">
+ <tp:docstring>Find secrets in any collection.</tp:docstring>
+ </arg>
+ <arg name="unlocked" type="ao" direction="out">
+ <tp:docstring>Items found.</tp:docstring>
+ </arg>
+ <arg name="locked" type="ao" direction="out">
+ <tp:docstring>Items found that require authentication.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="Unlock">
+ <tp:docstring>Unlock the specified objects.</tp:docstring>
+ <arg name="objects" type="ao" direction="in">
+ <tp:docstring>Objects to unlock.</tp:docstring>
+ </arg>
+ <arg name="unlocked" type="ao" direction="out">
+ <tp:docstring>Objects that were unlocked without a prompt.</tp:docstring>
+ </arg>
+ <arg name="prompt" type="o" direction="out">
+ <tp:docstring>A prompt object which can be used to unlock the remaining objects, or the special value '/' when no prompt is necessary.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="Lock">
+ <tp:docstring>Lock the items.</tp:docstring>
+ <arg name="objects" type="ao" direction="in">
+ <tp:docstring>Objects to lock.</tp:docstring>
+ </arg>
+ <arg name="locked" type="ao" direction="out">
+ <tp:docstring>Objects that were locked without a prompt.</tp:docstring>
+ </arg>
+ <arg name="Prompt" type="o" direction="out">
+ <tp:docstring>A prompt to lock the objects, or the special value '/' when no prompt is necessary.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="GetSecrets">
+ <tp:docstring>Retrieve multiple secrets from different items.</tp:docstring>
+ <arg name="items" type="ao" direction="in">
+ <tp:docstring>Items to get secrets for.</tp:docstring>
+ </arg>
+ <arg name="session" type="o" direction="in">
+ <tp:docstring>The session to use to encode the secrets.</tp:docstring>
+ </arg>
+ <arg name="secrets" type="a{o(oayays)}" direction="out" tp:type="ObjectPath_Secret_Map">
+ <tp:docstring>Secrets for the items.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="ReadAlias">
+ <tp:docstring>Get the collection with the given alias.</tp:docstring>
+ <arg name="name" type='s' direction='in'>
+ <tp:docstring>An alias, such as 'default'.</tp:docstring>
+ </arg>
+ <arg name="collection" type='o' direction='out'>
+ <tp:docstring>The collection or the the path '/' if no such collection exists.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="SetAlias">
+ <tp:docstring>Setup a collection alias.</tp:docstring>
+ <arg name="name" type='s' direction='in'>
+ <tp:docstring>An alias, such as 'default'.</tp:docstring>
+ </arg>
+ <arg name="collection" type='o' direction='in'>
+ <tp:docstring>
+ The collection to make the alias point to. To remove an alias use the special value '/'.
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <signal name="CollectionCreated">
+ <tp:docstring>A collection was created.</tp:docstring>
+ <arg name="collection" type="o">
+ <tp:docstring>Collection that was created</tp:docstring>
+ </arg>
+ </signal>
+
+ <signal name="CollectionDeleted">
+ <tp:docstring>A collection was deleted.</tp:docstring>
+ <arg name="collection" type="o">
+ <tp:docstring>Collection that was deleted.</tp:docstring>
+ </arg>
+ </signal>
+
+ <signal name="CollectionChanged">
+ <tp:docstring>A collection was changed.</tp:docstring>
+ <arg name="collection" type="o">
+ <tp:docstring>Collection that was changed.</tp:docstring>
+ </arg>
+ </signal>
+
+ </interface>
+
+ </node>
+
+ <!--
+ * ======================================================================================
+ * COLLECTION INTERFACE
+ *
+ -->
+
+ <node name="/org/freedesktop/Secrets/collection/xxxx">
+
+ <interface name="org.freedesktop.Secret.Collection">
+
+ <tp:docstring>A collection of items containing secrets.</tp:docstring>
+
+ <property name="Items" type="ao" access="read">
+ <tp:docstring>Items in this collection.</tp:docstring>
+ </property>
+
+ <property name="Label" type="s" access="readwrite">
+ <tp:docstring>The displayable label of this collection.</tp:docstring>
+ </property>
+
+ <property name="Locked" type="b" access="read">
+ <tp:docstring>
+ Whether the collection is locked and must be authenticated by the client application.
+ </tp:docstring>
+ </property>
+
+ <property name="Created" type="t" access="read">
+ <tp:docstring>The unix time when the collection was created.</tp:docstring>
+ </property>
+
+ <property name="Modified" type="t" access="read">
+ <tp:docstring>The unix time when the collection was last modified.</tp:docstring>
+ </property>
+
+ <method name="Delete">
+ <tp:docstring>Delete this collection.</tp:docstring>
+ <arg name="prompt" type="o" direction="out">
+ <tp:docstring>A prompt to delete the collection, or the special value '/' when no prompt is necessary.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="SearchItems">
+ <tp:docstring>Search for items in this collection matching the lookup attributes.</tp:docstring>
+ <arg name="attributes" type="a{ss}" direction="in">
+ <tp:docstring>Attributes to match.</tp:docstring>
+ </arg>
+ <arg name="results" type="ao" direction="out">
+ <tp:docstring>Items that matched the attributes.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="CreateItem">
+ <tp:docstring>Create an item with the given attributes, secret and label. If replace is set, then it replaces an item already present with the same values for the attributes.</tp:docstring>
+ <arg name="properties" type="a{sv}" direction="in">
+ <tp:docstring>The properties for the new item.</tp:docstring>
+ <tp:docstring xmlns:docbook="http://docbook.org/ns/docbook">
+ <para>
+ Properties for the new item. This allows setting the new item's
+ properties upon its creation. All READWRITE properties are useable.
+ Specify the property names in full interface.Property form.
+ <example>
+ <title>Example for properties</title>
+ <programlisting>
+<![CDATA[
+properties = {
+ "org.freedesktop.Secret.Item.Label": "MyItem",
+ "org.freedesktop.Secret.Item.Attributes": {
+ "Attribute1": "Value1",
+ "Attribute2": "Value2"
+ }
+ }
+]]>
+ </programlisting>
+ </example>
+ <note>
+ <para>
+ Please note that there is a distinction between the terms
+ <emphasis>Property</emphasis>, which refers to a D-Bus properties
+ of an object, and <emphasis>Attribute</emphasis>, which refers to one
+ of a secret item's string-valued attributes.
+ </para>
+ </note>
+ </para>
+ </tp:docstring>
+ </arg>
+ <arg name="secret" type="(oayays)" tp:type="Secret" direction="in">
+ <tp:docstring>The secret to store in the item, encoded with the included session.</tp:docstring>
+ </arg>
+ <arg name="replace" type="b" direction="in">
+ <tp:docstring>Whether to replace an item with the same attributes or not.</tp:docstring>
+ </arg>
+ <arg name="item" type="o" direction="out">
+ <tp:docstring>The item created, or the special value '/' if a prompt is necessary.</tp:docstring>
+ </arg>
+ <arg name="prompt" type="o" direction="out">
+ <tp:docstring>A prompt object, or the special value '/' if no prompt is necessary.</tp:docstring>
+ </arg>
+ </method>
+
+ <signal name="ItemCreated">
+ <tp:docstring>A new item in this collection was created.</tp:docstring>
+ <arg name="item" type="o">
+ <tp:docstring>The item that was created.</tp:docstring>
+ </arg>
+ </signal>
+
+ <signal name="ItemDeleted">
+ <tp:docstring>An item in this collection was deleted.</tp:docstring>
+
+ <arg name="item" type="o">
+ <tp:docstring>The item that was deleted.</tp:docstring>
+ </arg>
+ </signal>
+
+ <signal name="ItemChanged">
+ <tp:docstring>An item in this collection changed.</tp:docstring>
+
+ <arg name="item" type="o">
+ <tp:docstring>The item that was changed.</tp:docstring>
+ </arg>
+ </signal>
+
+ </interface>
+
+ </node>
+
+ <!--
+ * ======================================================================================
+ * ITEM INTERFACE
+ *
+ -->
+
+ <node name="/org/freedesktop/Secret/collection/xxxx/iiii">
+
+ <interface name="org.freedesktop.Secret.Item">
+
+ <tp:docstring>An item contains a secret, lookup attributes and has a label.</tp:docstring>
+
+ <property name="Locked" type="b" access="read">
+ <tp:docstring>Whether the item is locked and requires authentication, or not.</tp:docstring>
+ </property>
+
+ <property name="Attributes" type="a{ss}" access="readwrite">
+ <tp:docstring>The lookup attributes for this item.</tp:docstring>
+ </property>
+
+ <property name="Label" type="s" access="readwrite">
+ <tp:docstring>The displayable label for this item.</tp:docstring>
+ </property>
+
+ <property name="Created" type="t" access="read">
+ <tp:docstring>The unix time when the item was created.</tp:docstring>
+ </property>
+
+ <property name="Modified" type="t" access="read">
+ <tp:docstring>The unix time when the item was last modified.</tp:docstring>
+ </property>
+
+ <method name="Delete">
+ <tp:docstring>Delete this item.</tp:docstring>
+ <arg name="Prompt" type="o" direction="out">
+ <tp:docstring>A prompt object, or the special value '/' if no prompt is necessary.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="GetSecret">
+ <tp:docstring>Retrieve the secret for this item.</tp:docstring>
+ <arg name="session" type="o" direction="in">
+ <tp:docstring>The session to use to encode the secret.</tp:docstring>
+ </arg>
+ <arg name="secret" type="(oayays)" tp:type="Secret" direction="out">
+ <tp:docstring>The secret retrieved.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="SetSecret">
+ <tp:docstring>Set the secret for this item.</tp:docstring>
+ <arg name="secret" type="(oayays)" tp:type="Secret" direction="in">
+ <tp:docstring>The secret to set, encoded for the included session.</tp:docstring>
+ </arg>
+ </method>
+
+ </interface>
+
+ </node>
+
+ <!--
+ * ======================================================================================
+ * SESSION INTERFACE
+ *
+ -->
+
+ <node name="/org/freedesktop/Secret/session/ssss">
+
+ <interface name="org.freedesktop.Secret.Session">
+
+ <tp:docstring>A session tracks state between the service and a client application.</tp:docstring>
+
+ <method name="Close">
+ <tp:docstring>Close this session.</tp:docstring>
+ </method>
+
+ </interface>
+
+ </node>
+
+ <!--
+ * ======================================================================================
+ * PROMPT INTERFACE
+ *
+ -->
+
+ <node name="/org/freedesktop/Secret/prompts/pppp">
+
+ <interface name="org.freedesktop.Secret.Prompt">
+ <tp:docstring>A prompt necessary to complete an operation.</tp:docstring>
+
+ <method name="Prompt">
+ <tp:docstring>Perform the prompt.</tp:docstring>
+ <arg name="window-id" type="s" direction="in">
+ <tp:docstring>Platform specific window handle to use for
+ showing the prompt.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="Dismiss">
+ <tp:docstring>Dismiss the prompt.</tp:docstring>
+ </method>
+
+ <signal name="Completed">
+ <tp:docstring>The prompt and operation completed.</tp:docstring>
+ <arg name="dismissed" type="b">
+ <tp:docstring>Whether the prompt and operation were dismissed or not.</tp:docstring>
+ </arg>
+ <arg name="result" type="v">
+ <tp:docstring>The possibly empty, operation specific,
+ result.</tp:docstring>
+ </arg>
+ </signal>
+
+ </interface>
+
+ </node>
+
+</tp:spec>
diff --git a/secret-service/specification.xml b/secret-service/specification.xml
new file mode 100644
index 0000000..507eb78
--- /dev/null
+++ b/secret-service/specification.xml
@@ -0,0 +1,580 @@
+<?xml version="1.0"?>
+<book xml:id="index" xmlns="http://docbook.org/ns/docbook" version="5.0">
+ <bookinfo>
+ <title>Secret Service API Draft</title>
+ <releaseinfo>
+ Secret Service 0.2 DRAFT
+ </releaseinfo>
+
+ <authorgroup>
+ <author>
+ <firstname>Stef</firstname>
+ <surname>Walter</surname>
+ <affiliation>
+ <jobtitle>GNOME Keyring Developer</jobtitle>
+ <address>
+ <email>stefw@collabora.co.uk</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Michael</firstname>
+ <surname>Leupold</surname>
+ <affiliation>
+ <jobtitle>KWallet Developer</jobtitle>
+ <address>
+ <email>lemma@confuego.org</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>2008-2011</year>
+ <holder>The Secret Service API Authors</holder>
+ </copyright>
+
+ </bookinfo>
+
+ <part xml:id="description">
+ <title>API Documentation</title>
+ <chapter>
+ <title>Introduction</title>
+
+ <para>The Secret Service API allows client applications to store secrets securely in a
+ service running in the user's login session. </para>
+
+ <para>The secrets are usually stored in an encrypted manner by the service. The
+ service may need to be unlocked by the user before the secrets become available
+ for retrieval by client applications.</para>
+
+ <para>The Secret Service stores a secret along with a set of lookup attributes.
+ The attributes can be used to look up and retrieve a secret at a later date. The
+ lookup attributes are not treated as secret material, and the service may choose
+ not to encrypt attributes when storing them to disk.</para>
+
+ <para>This API was desigened by GNOME and KDE developers with the goal of having
+ a common way to store secrets. Its predecessors are the desktop-specific APIs
+ used by GNOME Keyring and KWallet.</para>
+ </chapter>
+
+ <chapter>
+ <title>Secrets</title>
+
+ <para>A secret is something an application wishes to store securely. A good example
+ is a password that an application needs to save and use at a later date.</para>
+
+ <para>Within this API a secret value is treated as an array of bytes. It is
+ recommended that a secret consist of user-readable text, although this API has
+ no such requirement.</para>
+
+ <para>Applications wishing to store multiple values as part of a single secret, may
+ choose to use a textual format to combine these values into one. For example, multiple
+ values may be combined using the 'desktop' key file format, or XML.</para>
+
+ <para>Secrets may be <link linkend='transfer-secrets'>encrypted when transferred</link>
+ to or from the client application.</para>
+
+ <para>The <link linkend='type-Secret'><classname>Secret</classname> structure</link> encapsulates
+ a secret value along with its transfer encryption parameters.</para>
+ </chapter>
+
+ <chapter>
+ <title>Collection and Items</title>
+
+ <para>Each secret is stored together with
+ <link linkend='lookup-attributes'>lookup attributes</link> and a label. These together
+ form an <link linkend='org.freedesktop.Secret.Item'>item</link>.</para>
+
+ <para>A group of items together form a
+ <link linkend='org.freedesktop.Secret.Collection'>collection</link>.
+ A collection is similar in concept to the terms 'keyring' or 'wallet'.</para>
+
+ <para>Collections and items are represented as DBus objects, and each has its own
+ object path. Under normal circumstances, the object path of a collection or item
+ should not change for its lifetime.</para>
+
+ <para>It is strongly recommended that client applications use
+ <link linkend='lookup-attributes'>lookup attributes</link> to find items rather than
+ recording the object path of a stored item. This allows maximum interoperability.</para>
+
+ <para>An item or a collection may be initially in a locked state. When in a locked
+ state the item or collection may not be modified in any way, and the secret may not
+ be read. Client applications that require access to the secret of a locked item, or
+ desire to modify a locked item, must <link linkend='unlocking'>unlock it</link> before use.</para>
+
+ <para>The service must prevent modification of locked collections or items. On
+ such an invalid access the
+ <link linkend='org.freedesktop.Secret.Error.IsLocked'><errorname>IsLocked</errorname></link>
+ error should be raised.</para>
+
+ <para>Client applications without special requirements should store in the default
+ collection. The default collection is always accessible through a
+ <link linkend='object-paths'>specific object path</link>.</para>
+
+ <para>A new item can be created with the
+ <link linkend='org.freedesktop.Secret.Collection.CreateItem'>
+ <function>CreateItem()</function></link>
+ method on the Collection interface. When creating an item, the properties of the new
+ item are specified. The service may ignore or change these properties when creating
+ the item.</para>
+
+ <para>When creating an item, the service may need to prompt the user for additional
+ information. In this case, a <link linkend='prompts'>prompt object</link> is returned. It
+ must be <link linkend='org.freedesktop.Secret.Prompt.Prompt'>acted upon</link> in order for
+ the collection to be created. In this case, the
+ <link linkend='org.freedesktop.Secret.Prompt.Completed'>result of the prompt</link>
+ will contain the object path of the new item.</para>
+
+ <para>An item can be deleted by calling the
+ <link linkend='org.freedesktop.Secret.Item.Delete'><function>Delete()</function></link>
+ method on the Item interface.</para>
+
+ <para>When deleting an item, the service may need to prompt the user for additional
+ information. In this case, a <link linkend='prompts'>prompt object</link> is returned. It
+ must be <link linkend='org.freedesktop.Secret.Prompt.Prompt'>acted upon</link> in order for
+ the item to be deleted.</para>
+
+ <para>Client applications with special needs can create a new collection by calling the
+ <link linkend='org.freedesktop.Secret.Service.CreateCollection'>
+ <function>CreateCollection()</function></link>
+ method on the Service interface. When creating a collection, the properties of the new
+ collection are specified. The service may ignore or change these properties when creating
+ the collection.</para>
+
+ <para>When creating a collection, the service may need to prompt the user for additional
+ information. In this case, a <link linkend='prompts'>prompt object</link> is returned. It
+ must be <link linkend='org.freedesktop.Secret.Prompt.Prompt'>acted upon</link> in order for
+ the collection to be created. In this case, the
+ <link linkend='org.freedesktop.Secret.Prompt.Completed'>result of the prompt</link>
+ will contain the object path of the new collection.</para>
+
+ <para>A collection can be deleted by calling the
+ <link linkend='org.freedesktop.Secret.Collection.Delete'><function>Delete()</function></link>
+ method on the Collection interface.</para>
+
+ <para>When deleting a collection, the service may need to prompt the user for additional
+ information. In this case, a <link linkend='prompts'>prompt object</link> is returned. It
+ must be <link linkend='org.freedesktop.Secret.Prompt.Prompt'>acted upon</link> in order for
+ the collection to be deleted.</para>
+ </chapter>
+
+ <chapter xml:id="aliases">
+ <title>Aliases</title>
+
+ <para>Collections may be accessed via well known aliases. For example an alias
+ called <literal>default</literal> tells applications which is the default
+ collection to store secrets.</para>
+
+ <para>The aliased collections will be available at a
+ <link linkend='object-paths'>well-known DBus object path</link>.</para>
+
+ <para>If an application needs to create a collection with a given alias, this can
+ be done in a race free fashion by specifying the alias parameter of the
+ <link linkend='org.freedesktop.Secret.Service.CreateCollection'>CreateCollection()</link>
+ method on the service interface. If a collection with that alias already exists,
+ then it will be returned instead of creating a new one.</para>
+
+ <para>For applications like password managers it can be useful to allow the user to
+ configure which collection is associated with which well known alias. To alias or
+ unalias a collection use the
+ <link linkend='org.freedesktop.Secret.Service.SetAlias'>SetAlias()</link> method on the
+ service interface. Use the
+ <link linkend='org.freedesktop.Secret.Service.ReadAlias'>ReadAlias()</link> method on the
+ service interface to discover which collection is associated with a given alias.</para>
+ </chapter>
+
+ <chapter xml:id="lookup-attributes">
+ <title>Lookup Attributes</title>
+
+ <para>Attributes can and should be stored with a secret to facilitate lookup
+ of the secret at a later date.</para>
+
+ <para>An attribute constists of a name, and a value. Both parts are simple
+ strings.</para>
+
+ <para>The service may have additional requirements as to what can be present
+ in an attribute name. It is recommended that attribute names are human
+ readable, and kept simple for the sake of simplicity.</para>
+
+ <para>During a lookup, attribute names and values are matched via case-sensitive
+ string equality.</para>
+
+ <para>It's important to remember that attributes are not part of the secret.
+ Services implementing this API will probably store attributes in an unencrypted
+ manner in order to support simple and effecient lookups.</para>
+
+ <para>In order to search for items, use the
+ <link linkend='org.freedesktop.Secret.Service.SearchItems'><function>SearchItems()</function></link>
+ method of the Service interface. The matched items will be returned in two sets. The
+ <parameter class='function'>unlocked</parameter> return value will contain the object paths
+ of all the items that are not locked. The <parameter class='function'>locked</parameter> return
+ value will contain object paths of items that are locked, which can be
+ <link linkend='unlocking'>unlocked if desired</link>.</para>
+
+ <para>The
+ <link linkend='org.freedesktop.Secret.Collection.SearchItems'><function>SearchItems()</function></link>
+ method of the Collection interface is similar, except for it only searches a single collection.</para>
+
+ </chapter>
+
+ <chapter xml:id="sessions">
+ <title>Sessions</title>
+
+ <para>A session is established between a client application and a service. A session
+ is used to <link linkend='transfer-secrets'>transfer secrets</link> between the client
+ application and the service.</para>
+
+ <para>A session is established by calling the service's
+ <link linkend='org.freedesktop.Secret.Service.OpenSession'>
+ <function>OpenSession()</function></link>
+ method. Once established, a session is bound to calling application's connection to
+ the DBus session bus.</para>
+
+ <para>A session is closed when the client application disconnects from the DBus
+ session bus. Alternatively the client application can call the
+ <link linkend='org.freedesktop.Secret.Session.Close'><function>Close()</function></link>
+ method on the session interface. Once a session is closed all session specific
+ negotiations will be dropped by the service.</para>
+
+ <para>More than one session may opened by a client application, although this is
+ not normally necessary.</para>
+ </chapter>
+
+ <chapter xml:id='transfer-secrets'>
+ <title>Transfer of Secrets</title>
+
+ <para>To access or store secrets, use the
+ <link linkend='org.freedesktop.Secret.Item.GetSecret'><function>GetSecret()</function></link>,
+ <link linkend='org.freedesktop.Secret.Item.SetSecret'><function>SetSecret()</function></link>
+ methods on the item interface, or the
+ <link linkend='org.freedesktop.Secret.Service.GetSecrets'><function>GetSecrets()</function></link>,
+ method on the service interface.</para>
+
+ <para>You must specify a session when retrieving or storing a secret. The session
+ controls how the secret is encoded during transfer. Since this is a D-Bus API, the
+ data in all method calls and other accesses in this API will go through multiple
+ processes, and may be cached arbitrarilyby the OS or elsewhere.</para>
+
+ <para>The Secrets API has provision to encrypt secrets while in transit between
+ the service and the client application. The encryption is not envisioned to withstand
+ man in the middle attacks, or other active attacks. It is envisioned to minimize
+ storage of plain text secrets in memory and prevent storage plain text storage of
+ secrets in a swap file or other caching mechanism.</para>
+
+ <para>Many client applications may choose not to make use of the provisions to
+ encrypt secrets in transit. In fact for applications unable to prevent their own
+ memory from being paged to disk (eg: Java, C# or Python apps), transfering
+ encrypted secrets would be an excersize of questionable value.</para>
+
+ <section>
+ <title>Negotiation of Algorithms</title>
+
+ <para>In order to encrypt secrets in transit, the service and the client
+ application must agree on an algorithm, and some algorithm specific
+ parameters (eg: a key).</para>
+
+ <para>When the client application opens a <link linkend='sessions'>session</link>
+ with the service, it calls the
+ <link linkend='org.freedesktop.Secret.Service.OpenSession'><function>
+ OpenSession()</function></link> method on the service. The algorithms argument to the
+ <function>OpenSession()</function> method specifies a set of algorithms to be used
+ together for key agreement and encryption. The other arguments are algorithm
+ specific.</para>
+
+ <para>If a service does not support a specific set of algorithms, a
+ <errorname>org.freedesktop.DBus.Error.NotSupported</errorname>
+ error is returned, and the client is free to try another set of algorithms.
+ The <emphasis>plain</emphasis> algorithm is almost always supported.</para>
+
+ <para>An algorithm may require that the <function>OpenSession()</function> method is
+ called multiple times in succession to be complete. Each iteration transfers
+ algorithm specific data back forth between the service and the client. The object path
+ '/' is returned from <function>OpenSession()</function> when session negotiation is
+ incomplete.</para>
+
+ <para>None of the algorithms documented in this initial version of the specification
+ require multiple calls to <function>OpenSession()</function>.</para>
+
+ <para>When <function>OpenSession()</function> completes, it returns the session object
+ path along with a valid session object path.</para>
+
+ <para>Once an session algorithm has been negotiated, it is used for all transfer a
+ secrets whenever that session is specified along with the
+ <link linkend='type-Secret'><classname>secret</classname></link>.</para>
+ </section>
+
+ <section>
+ <title>Algorithm: plain</title>
+
+ <segmentedlist>
+ <?dbhtml list-presentation="list"?>
+ <segtitle>Session algorithm</segtitle>
+ <segtitle><link linkend='org.freedesktop.Secret.Service.OpenSession'>
+ Session input</link></segtitle>
+ <segtitle><link linkend='org.freedesktop.Secret.Service.OpenSession'>
+ Session output</link></segtitle>
+ <segtitle><link linkend='type-Secret'>
+ <classname>Secret</classname> parameter</link></segtitle>
+ <seglistitem>
+ <!-- TODO: literal? -->
+ <seg><emphasis>plain</emphasis></seg>
+ <seg>empty string</seg>
+ <seg>empty string</seg>
+ <seg>empty string</seg>
+ </seglistitem>
+ </segmentedlist>
+
+ <para>The plain algorithm does no encryption whatsoever.</para>
+
+ <para>It is strongly recommended that a service implementing this API support
+ the <emphasis>plain</emphasis> algorithm.</para>
+ </section>
+
+ <section>
+ <title>Algorithm: dh-ietf1024-sha256-aes128-cbc-pkcs7</title>
+
+ <segmentedlist>
+ <?dbhtml list-presentation="list"?>
+ <segtitle>Session algorithm</segtitle>
+ <segtitle><link linkend='org.freedesktop.Secret.Service.OpenSession'>
+ Session input</link></segtitle>
+ <segtitle><link linkend='org.freedesktop.Secret.Service.OpenSession'>
+ Session output</link></segtitle>
+ <segtitle><link linkend='type-Secret'>
+ <classname>Secret</classname> parameter</link></segtitle>
+ <seglistitem>
+ <!-- TODO: literal? -->
+ <seg><emphasis>dh-ietf1024-sha256-aes128-cbc-pkcs7</emphasis></seg>
+ <seg>Client DH pub key as an array of bytes</seg>
+ <seg>Service DH pub key as an array of bytes</seg>
+ <seg>16 byte AES initialization vector</seg>
+ </seglistitem>
+ </segmentedlist>
+
+ <para>DH key agreement <citation>rfc2631</citation> is used to create a secret key
+ using 1024 bit parameters of the standard IETF 'Second Oakley Group'
+ <citation>rfc2409</citation>. The secret key is then digested into a 128-bit key
+ appropriate for AES. This is done using HKDF <citation>rfc5869</citation> with NULL
+ salt and empty info, using the SHA-2 256 hash algorithm
+ <citation>fips-180-3.2008</citation>. The secrets are encrypted using AES
+ <citation>fips-197.2001</citation> in cipher block chaining mode with pkcs7 style
+ padding <citation>rfc2315</citation>.</para>
+
+ <para>The public keys are transferred as an array of bytes representing an
+ unsigned integer of arbitrary size, most-significant byte first (e.g., the
+ integer 32768 is represented as the 2-byte string 0x80 0x00)</para>
+ </section>
+
+ </chapter>
+
+ <chapter xml:id='unlocking'>
+ <title>Locking and Unlocking</title>
+
+ <para>Some items and/or collections may be marked as locked by the service.
+ The secrets of locked items cannot be accessed. Additionally, locked items or collections
+ cannot be modified by the client application.</para>
+
+ <para>It's up to the service whether to unlock items individually, or collections as a
+ whole. The client application should act as if it must unlock each item individually.</para>
+
+ <para>A service may upon unlocking a collection, unlock all items in that collection. If
+ a service is not able to unlock an item individually, it should treat a request to unlock
+ an item as a request to unlock the connection that the item is in.</para>
+
+ <para>A service may choose to unlock items or collections just for a single client
+ application. Alternatively the service may choose to allow any client application to access
+ items or collections unlocked by a another client application.</para>
+
+ <para>A client application should always be ready to unlock the items for the secrets
+ it needs, or objects it must modify. It must not assume that an item is already unlocked
+ for whatever reason.</para>
+
+ <para>A service may lock previously unlocked items for any reason at any time. Usually this
+ is done in response to user actions, timeouts, or external actions (such as the computer
+ sleeping). The inherent race conditions present due to this are unavoidable, and must be
+ handled gracefully.</para>
+
+ <para>In order to unlock an item or collection the service's
+ <link linkend='org.freedesktop.Secret.Service.Unlock'>
+ <function>Unlock()</function></link>
+ method is called with one or more DBus object paths of items or collections. The
+ <function>Unlock()</function> will return the DBus object paths of objects it could
+ immediately unlock without prompting.</para>
+
+ <para>The <function>Unlock()</function> method may also return a
+ <link linkend='org.freedesktop.Secret.Prompt.Prompt'>prompt object</link>. If a prompt
+ object is returned, it must be <link linkend='prompts'>acted upon</link> in order to complete
+ the unlocking of the remaining objects. The
+ <link linkend='org.freedesktop.Secret.Prompt.Completed'>result of the prompt</link>
+ will contain the object paths that were successfully unlocked by the prompt.</para>
+
+ <para>In order to lock an item or collection the service's
+ <link linkend='org.freedesktop.Secret.Service.Unlock'>
+ <function>Lock()</function></link>
+ method is called with one or more DBus object paths of items or collections. The
+ <function>Lock()</function> will return the DBus object paths of objects it could
+ immediately lock without prompting.</para>
+
+ <para>The <function>Lock()</function> method may also return a
+ <link linkend='org.freedesktop.Secret.Prompt.Prompt'>prompt object</link>. If a prompt
+ object is returned, it must be <link linkend='prompts'>acted upon</link> in order to complete
+ the locking of the remaining objects. The
+ <link linkend='org.freedesktop.Secret.Prompt.Completed'>result of the prompt</link>
+ will contain the object paths that were successfully locked by the prompt.</para>
+ </chapter>
+
+ <chapter xml:id='prompts'>
+ <title>Prompts and Prompting</title>
+
+ <para>In order to complete various operations, such as unlocking a collection, the
+ service may need to prompt the user for additional information, such as a master password.</para>
+
+ <para>The prompts are displayed by the service on behalf of the client application.</para>
+
+ <para>Operations that require a prompt to complete will return a prompt object. The client
+ application must then call the
+ <link linkend='org.freedesktop.Secret.Prompt.Prompt'><function>Prompt()</function></link>
+ method of the prompt object to display the prompt. Client applications can use the
+ <parameter class='function'>window-id</parameter>
+ argument to display the prompt attached to their application window.</para>
+
+ <para>Once the user provides the additional required information to the prompt, the service
+ completes the operation that required the prompt. Then it emits the the
+ <link linkend='org.freedesktop.Secret.Prompt.Completed'><function>Completed</function></link>
+ signal of the prompt object. The <parameter class='function'>result</parameter> argument of
+ the signal contains operation an operation specific result.</para>
+
+ <para>Either the user or the client application can dismiss a prompt. In this case the
+ operation that required the additional information is cancelled. The client application
+ can dismiss a prompt by calling the
+ <link linkend='org.freedesktop.Secret.Prompt.Dismiss'><function>Dismiss()</function></link>
+ method of the prompt object. The <function>Completed</function> signal will be emitted
+ with its <parameter class='function'>dismissed</parameter> argument set to
+ <constant>TRUE</constant>.</para>
+
+ <para>Once the <function>Completed</function> signal is emitted on a prompt object, it
+ is no longer valid. Prompt objects are specific to the client application's connection
+ to the DBus bus. Once an application disconnects, all its prompts are no longer valid.</para>
+
+ <para>There is an inherent race between the <function>Dismiss()</function> method and the
+ <function>Completed</function> signal. An application calling <function>Dismiss()</function>
+ must be prepared to handle the fact that the <function>Completed</function> has already been
+ emitted (although perhaps not received by the client). In addition the client must be prepared
+ to handle the fact that the prompt object is no longer valid.</para>
+ </chapter>
+
+ <chapter>
+ <title>What's not included in the API</title>
+
+ <para>A service may implement additional DBus interfaces for further capabilities not
+ included in this specification. Password management applications or other narrowly
+ focused tools should make use of these when necessary.</para>
+
+ <para>This specification does not mandate the use of master passwords to lock a
+ collection of secrets. The service may choose to implement any method for locking
+ secrets.</para>
+
+ <para>This specification does not mandate any form of access control. The service may
+ choose to allow certain applications to access a keyring, and others.</para>
+
+ <para>[TODO: complete]</para>
+ </chapter>
+
+ <chapter>
+ <title>Notes for Service Implementors</title>
+
+ <para>[TODO: complete]</para>
+ </chapter>
+
+ </part>
+
+ <part xml:id="ref-dbus-api">
+ <title>D-Bus API Reference</title>
+
+ <chapter xml:id='object-paths'>
+ <title>Object Paths</title>
+
+ <para>The various DBus object paths used with the Secret Service API are designed to be human
+ readable but not displayed to the user. The object path of an item or collection should
+ not change for its lifetime, under normal circumstances.</para>
+
+ <programlisting>/org/freedesktop/secrets</programlisting>
+ <para>The object path for the service.</para>
+
+ <programlisting>/org/freedesktop/secrets/collection/<emphasis>xxxx</emphasis></programlisting>
+ <para>The object path for a collection, where <emphasis>xxxx</emphasis> represents a
+ possibly encoded or truncated version of the initial label of the collection.</para>
+
+ <programlisting>/org/freedesktop/secrets/collection/<emphasis>xxxx</emphasis>/<emphasis>iiii</emphasis></programlisting>
+ <para>The object path for an item, where <emphasis>xxxx</emphasis> is the collection (above)
+ and <emphasis>iiii</emphasis> is an auto-generated item specific identifier.</para>
+
+ <programlisting>/org/freedesktop/secrets/session/<emphasis>ssss</emphasis></programlisting>
+ <para>The object path for a session, where <emphasis>ssss</emphasis> is an auto-generated
+ session specific identifier.</para>
+
+ <programlisting>/org/freedesktop/secrets/aliases/default</programlisting>
+ <para>The default collection for client applications to store secrets is available under
+ this object path in addition to its real object path (above). Other aliases may also be
+ present.</para>
+ </chapter>
+
+ <xi:include href="reference.xml" xpointer="interfaces" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:fallback/>
+ </xi:include>
+ <xi:include href="reference.xml" xpointer="types" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:fallback/>
+ </xi:include>
+ <xi:include href="reference.xml" xpointer="errors" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:fallback/>
+ </xi:include>
+
+ </part>
+
+ <xi:include href="xml/annotation-glossary.xml" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:fallback/>
+ </xi:include>
+
+ <bibliography>
+ <title>References</title>
+
+ <bibliomixed>
+ <abbrev>rfc2315</abbrev>
+ IETF <ulink url="http://www.ietf.org/rfc/rfc2315.txt">RFC 2315</ulink>:
+ PKCS #7: Cryptographic Message Syntax Version 1.5
+ </bibliomixed>
+
+ <bibliomixed>
+ <abbrev>rfc2409</abbrev>
+ IETF <ulink url="http://www.ietf.org/rfc/rfc2409.txt">RFC 2409</ulink>:
+ The Internet Key Exchange (IKE)
+ </bibliomixed>
+
+ <bibliomixed>
+ <abbrev>rfc2631</abbrev>
+ IETF <ulink url="http://www.ietf.org/rfc/rfc2631.txt">RFC 2631</ulink>:
+ Diffie-Hellman Key Agreement Method
+ </bibliomixed>
+
+ <bibliomixed>
+ <abbrev>rfc5869</abbrev>
+ IETF <ulink url="http://www.ietf.org/rfc/rfc5869.txt">RFC 5869</ulink>:
+ HMAC-based Extract-and-Expand Key Derivation Function (HKDF)
+ </bibliomixed>
+
+ <bibliomixed>
+ <abbrev>fips-180-3.2008</abbrev>
+ NIST <ulink url="http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf">FIPS PUB 180-3</ulink>:
+ Secure Hash Standard (SHS), October 2008
+ </bibliomixed>
+
+ <bibliomixed>
+ <abbrev>fips-197.2001</abbrev>
+ NIST <ulink url="http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf">FIPS PUB 197</ulink>:
+ Advanced Encryption Standard (AES), November 2001
+ </bibliomixed>
+
+ </bibliography>
+
+</book>
diff --git a/secret-service/tools/resolve-type.xsl b/secret-service/tools/resolve-type.xsl
new file mode 100644
index 0000000..a37f2af
--- /dev/null
+++ b/secret-service/tools/resolve-type.xsl
@@ -0,0 +1,122 @@
+<?xml version="1.0"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ exclude-result-prefixes="tp html">
+
+<!--
+ Helper templates for Telepathy D-Bus Introspection conversion.
+
+ Copyright (C) 2009 Michael Leupold <lemma@confuego.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+-->
+
+ <!-- Resolve the type a node has. This will first look at tp:type and
+ - if not found - use the type attribute -->
+ <xsl:template name="ResolveType">
+ <xsl:param name="node"/>
+ <xsl:variable name="unstripped">
+ <xsl:choose>
+ <xsl:when test="$node//@tp:type">
+ <xsl:call-template name="TpType">
+ <xsl:with-param name="type" select="$node//@tp:type"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="$node//@type">
+ <xsl:call-template name="DBusType">
+ <xsl:with-param name="type" select="$node//@type"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ Node doesn't contain a type.
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:value-of select="translate(translate($unstripped, ' ', ''), '&#xa;', '')"/>
+ </xsl:template>
+
+ <!-- Map a D-Bus type to its EggDBus counterpart -->
+ <xsl:template name="DBusType">
+ <xsl:param name="type"/>
+ <xsl:choose>
+ <xsl:when test="$type='o'">ObjectPath</xsl:when>
+ <xsl:when test="$type='s'">String</xsl:when>
+ <xsl:when test="$type='y'">Byte</xsl:when>
+ <xsl:when test="$type='b'">Boolean</xsl:when>
+ <xsl:when test="$type='n'">Int16</xsl:when>
+ <xsl:when test="$type='q'">UInt16</xsl:when>
+ <xsl:when test="$type='i'">Int32</xsl:when>
+ <xsl:when test="$type='u'">UInt32</xsl:when>
+ <xsl:when test="$type='x'">Int64</xsl:when>
+ <xsl:when test="$type='t'">UInt64</xsl:when>
+ <xsl:when test="$type='d'">Double</xsl:when>
+ <xsl:when test="$type='g'">Signature</xsl:when>
+ <xsl:when test="$type='v'">Variant</xsl:when>
+ <xsl:when test="starts-with($type, 'a{')">
+ Dict&lt;
+ <xsl:call-template name="DBusType">
+ <xsl:with-param name="type" select="substring($type, 3, 1)"/>
+ </xsl:call-template>
+ ,
+ <xsl:call-template name="DBusType">
+ <xsl:with-param name="type" select="substring($type, 4, 1)"/>
+ </xsl:call-template>
+ &gt;
+ </xsl:when>
+ <xsl:when test="starts-with($type, 'a')">
+ Array&lt;
+ <xsl:call-template name="DBusType">
+ <xsl:with-param name="type" select="substring($type, 2)"/>
+ </xsl:call-template>
+ &gt;
+ </xsl:when>
+ <!-- TODO: doesn't implement dict-entries and structs -->
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ Unknown DBus Type <xsl:value-of select="$type"/>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- Resolve tp:type attributes by searching for matching tp:struct
+ and tp:mapping elements -->
+ <xsl:template name="TpType">
+ <xsl:param name="type"/>
+ <xsl:choose>
+ <xsl:when test="/tp:spec/tp:struct[@name=$type]">
+ <xsl:value-of select="$type"/>
+ </xsl:when>
+ <xsl:when test="/tp:spec/tp:mapping[@name=$type]">
+ Dict&lt;
+ <xsl:call-template name="ResolveType">
+ <xsl:with-param name="node" select="/tp:spec/tp:mapping[@name=$type]/tp:member[@name='Key']"/>
+ </xsl:call-template>,
+ <xsl:call-template name="ResolveType">
+ <xsl:with-param name="node" select="/tp:spec/tp:mapping[@name=$type]/tp:member[@name='Value']"/>
+ </xsl:call-template>
+ &gt;
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ Unspecified type <xsl:value-of select="$type"/>.
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+</xsl:stylesheet>
diff --git a/secret-service/tools/spec-to-docbook.xsl b/secret-service/tools/spec-to-docbook.xsl
new file mode 100644
index 0000000..a6b18a3
--- /dev/null
+++ b/secret-service/tools/spec-to-docbook.xsl
@@ -0,0 +1,1242 @@
+<?xml version="1.0"?>
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:docbook="http://docbook.org/ns/docbook"
+ exclude-result-prefixes="tp html">
+
+<!--
+ Telepathy D-Bus Introspection to Docbook XML translator.
+ Based on Telepathy's doc-generator.xsl.
+
+ Copyright (C) 2006-2008 Collabora Limited
+ Copyright (C) 2009 Michael Leupold <lemma@confuego.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+-->
+
+ <xsl:output method="xml" indent="yes" encoding="ascii"
+ omit-xml-declaration="no"/>
+
+ <xsl:include href="resolve-type.xsl"/>
+
+ <xsl:param name="allow-undefined-interfaces" select="false()"/>
+
+ <xsl:template match="docbook:* | html:* | @*">
+ <xsl:copy>
+ <xsl:apply-templates/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template name="direction">
+ <xsl:param name="indirection"/>
+ <xsl:choose>
+ <xsl:when test="$indirection = 'in'">IN</xsl:when>
+ <xsl:otherwise>OUT</xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="tp:type">
+ <xsl:call-template name="tp-type">
+ <xsl:with-param name="tp-type" select="string(.)"/>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!-- tp:dbus-ref: reference a D-Bus interface, signal, method or property -->
+ <xsl:template match="tp:dbus-ref">
+ <xsl:variable name="name">
+ <xsl:choose>
+ <xsl:when test="@namespace">
+ <xsl:value-of select="@namespace"/>
+ <xsl:text>.</xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ <xsl:value-of select="string(.)"/>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="//interface[@name=$name]
+ or //interface/method[concat(../@name, '.', @name)=$name]
+ or //interface/signal[concat(../@name, '.', @name)=$name]
+ or //interface/property[concat(../@name, '.', @name)=$name]
+ or //interface[@name=concat($name, '.DRAFT')]
+ or //interface/method[
+ concat(../@name, '.', @name)=concat($name, '.DRAFT')]
+ or //interface/signal[
+ concat(../@name, '.', @name)=concat($name, '.DRAFT')]
+ or //interface/property[
+ concat(../@name, '.', @name)=concat($name, '.DRAFT')]
+ ">
+ <link linkend="{$name}">
+ <literal><xsl:value-of select="$name"/></literal>
+ </link>
+ </xsl:when>
+
+ <xsl:when test="$allow-undefined-interfaces">
+ <!-- TODO: Convert to docbook -->
+ <span xmlns="http://www.w3.org/1999/xhtml" title="defined elsewhere">
+ <xsl:value-of select="string(.)"/>
+ </span>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: cannot find D-Bus interface, method, </xsl:text>
+ <xsl:text>signal or property called '</xsl:text>
+ <xsl:value-of select="$name"/>
+ <xsl:text>'&#10;</xsl:text>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- tp:member-ref: reference a property of the current interface -->
+ <xsl:template match="tp:member-ref">
+ <xsl:variable name="prefix" select="concat(ancestor::interface/@name,
+ '.')"/>
+ <xsl:variable name="name" select="string(.)"/>
+
+ <xsl:if test="not(ancestor::interface)">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: Cannot use tp:member-ref when not in an</xsl:text>
+ <xsl:text> &lt;interface&gt;&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:choose>
+ <xsl:when test="ancestor::interface/signal[@name=$name]"/>
+ <xsl:when test="ancestor::interface/method[@name=$name]"/>
+ <xsl:when test="ancestor::interface/property[@name=$name]"/>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: interface </xsl:text>
+ <xsl:value-of select="ancestor::interface/@name"/>
+ <xsl:text> has no signal/method/property called </xsl:text>
+ <xsl:value-of select="$name"/>
+ <xsl:text>&#10;</xsl:text>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <link linkend="{$prefix}{$name}">
+ <literal><xsl:value-of select="concat($prefix, $name)"/></literal>
+ </link>
+ </xsl:template>
+
+ <xsl:template match="*" mode="identity">
+ <xsl:copy>
+ <xsl:apply-templates mode="identity"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="tp:docstring">
+ <para>
+ <xsl:copy-of select="child::node()"/>
+ </para>
+ </xsl:template>
+
+ <xsl:template match="tp:docstring" mode="nopara">
+ <xsl:copy-of select="child::node()"/>
+ </xsl:template>
+
+ <xsl:template match="tp:added">
+ <para>
+ Added in version <xsl:value-of select="@version"/>.
+ <xsl:apply-templates select="node()"/>
+ </para>
+ </xsl:template>
+
+ <xsl:template match="tp:changed">
+ <xsl:choose>
+ <xsl:when test="node()">
+ <para>
+ Changed in version <xsl:value-of select="@version"/>:
+ <xsl:apply-templates select="node()"/></para>
+ </xsl:when>
+ <xsl:otherwise>
+ <para>Changed in version
+ <xsl:value-of select="@version"/></para>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="tp:deprecated">
+ <para>
+ Deprecated since version <xsl:value-of select="@version"/>.
+ <xsl:apply-templates select="node()"/>
+ </para>
+ </xsl:template>
+
+ <xsl:template match="tp:rationale">
+ <!-- TODO: special? -->
+ <para>
+ <xsl:apply-templates select="node()"/>
+ </para>
+ </xsl:template>
+
+ <xsl:template match="tp:errors">
+ <title>Errors</title>
+ <xsl:apply-templates/>
+ </xsl:template>
+
+ <xsl:template name="generic-types">
+ <chapter>
+ <xsl:attribute name="xml:id">types</xsl:attribute>
+ <title>Types</title>
+ <xsl:call-template name="do-types"/>
+ </chapter>
+ </xsl:template>
+
+ <xsl:template name="do-types">
+ <xsl:if test="tp:simple-type">
+ <section>
+ <title>Simple types</title>
+ <xsl:apply-templates select="tp:simple-type"/>
+ </section>
+ </xsl:if>
+
+ <xsl:if test="tp:enum">
+ <section>
+ <title>Enumerated types</title>
+ <xsl:apply-templates select="tp:enum"/>
+ </section>
+ </xsl:if>
+
+ <xsl:if test="tp:flags">
+ <section>
+ <title>Sets of flags</title>
+ <xsl:apply-templates select="tp:flags"/>
+ </section>
+ </xsl:if>
+
+ <xsl:if test="tp:struct">
+ <section>
+ <title>Struct types</title>
+ <xsl:apply-templates select="tp:struct"/>
+ </section>
+ </xsl:if>
+
+ <xsl:if test="tp:mapping">
+ <section>
+ <title>Map types</title>
+ <xsl:apply-templates select="tp:mapping"/>
+ </section>
+ </xsl:if>
+
+ <xsl:if test="tp:external-type">
+ <section>
+ <title>Types defined elsewhere</title>
+ <glosslist>
+ <xsl:apply-templates select="tp:external-type"/>
+ </glosslist>
+ </section>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="tp:error">
+ <simplesect>
+ <title>
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="concat(../@namespace, '.', translate(@name, ' ', ''))"/>
+ </xsl:attribute>
+ <literal><xsl:value-of select="concat(../@namespace, '.', translate(@name, ' ', ''))"/></literal>
+ </title>
+ <xsl:apply-templates select="tp:docstring"/>
+ <xsl:apply-templates select="tp:added"/>
+ <xsl:apply-templates select="tp:changed"/>
+ <xsl:apply-templates select="tp:deprecated"/>
+ </simplesect>
+ </xsl:template>
+
+ <xsl:template match="/tp:spec/tp:copyright">
+ <!-- TODO: use <copyright> -->
+ <legalnotice>
+ <para><xsl:apply-templates mode="text"/></para>
+ </legalnotice>
+ </xsl:template>
+ <xsl:template match="/tp:spec/tp:license">
+ <!-- TODO: right tag? -->
+ <legalnotice>
+ <para>
+ <xsl:apply-templates/>
+ </para>
+ </legalnotice>
+ </xsl:template>
+
+ <xsl:template match="tp:copyright"/>
+ <xsl:template match="tp:license"/>
+
+ <xsl:template match="interface">
+
+ <refentry>
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="@name"/>
+ </xsl:attribute>
+ <refmeta>
+ <refentrytitle><literal><xsl:value-of select="@name"/></literal></refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+ <refdescriptor><xsl:value-of select="@name"/></refdescriptor>
+ <refname><xsl:value-of select="@name"/></refname>
+ <refpurpose><xsl:apply-templates select="tp:docstring" mode="nopara"/></refpurpose>
+ </refnamediv>
+
+ <xsl:if test="tp:added">
+ <refsection>
+ <xsl:apply-templates select="tp:added"/>
+ </refsection>
+ </xsl:if>
+ <xsl:if test="tp:changed">
+ <refsection>
+ <xsl:apply-templates select="tp:changed"/>
+ </refsection>
+ </xsl:if>
+ <xsl:if test="tp:deprecated">
+ <refsection>
+ <xsl:apply-templates select="tp:deprecated"/>
+ </refsection>
+ </xsl:if>
+
+ <xsl:if test="@tp:causes-havoc">
+ <refsection>
+ <warning>
+ <para>
+ This interface is <xsl:value-of select="@tp:causes-havoc"/>
+ and is likely to cause havoc to your API/ABI if bindings are generated.
+ Don't include it in libraries that care about compatibility.
+ </para>
+ </warning>
+ </refsection>
+ </xsl:if>
+
+ <xsl:if test="tp:requires">
+ <refsection>
+ <tip>
+ <para>Implementations of this interface must also implement:</para>
+ <itemizedlist>
+ <xsl:for-each select="tp:requires">
+ <listitem>
+ <para>
+ <link linkend="{@interface}">
+ <literal><xsl:value-of select="@interface"/></literal>
+ </link>
+ </para>
+ </listitem>
+ </xsl:for-each>
+ </itemizedlist>
+ </tip>
+ </refsection>
+ </xsl:if>
+
+ <refsynopsisdiv>
+ <xsl:if test="method">
+ <refsect2>
+ <title>Methods</title>
+ <funcsynopsis>
+ <xsl:apply-templates select="method" mode="funcsynopsislinked"/>
+ </funcsynopsis>
+ </refsect2>
+ </xsl:if>
+ <xsl:if test="signal">
+ <refsect2>
+ <title>Signals</title>
+ <funcsynopsis>
+ <xsl:apply-templates select="signal" mode="funcsynopsislinked"/>
+ </funcsynopsis>
+ </refsect2>
+ </xsl:if>
+ <xsl:if test="property">
+ <refsect2>
+ <title>Properties</title>
+ <refsynopsisdiv>
+ <title> </title>
+ <xsl:apply-templates select="property" mode="fieldsynopsislinked"/>
+ </refsynopsisdiv>
+ </refsect2>
+ </xsl:if>
+ </refsynopsisdiv>
+
+ <xsl:if test="method">
+ <refsection>
+ <title>Methods</title>
+ <xsl:apply-templates select="method" mode="detail"/>
+ </refsection>
+ </xsl:if>
+
+ <xsl:if test="signal">
+ <refsection>
+ <title>Signals</title>
+ <xsl:apply-templates select="signal" mode="detail"/>
+ </refsection>
+ </xsl:if>
+
+ <xsl:if test="tp:property">
+ <refsection>
+ <title>Telepathy Properties</title>
+ <para>
+ Accessed using the
+ <link linkend="org.freedesktop.Telepathy.Properties">
+ <literal>org.freedesktop.Telepathy.Properties</literal>
+ </link>
+ </para>
+ <glosslist>
+ <xsl:apply-templates select="tp:property" mode="detail"/>
+ </glosslist>
+ </refsection>
+ </xsl:if>
+
+ <xsl:if test="property">
+ <refsection>
+ <title>D-Bus Properties</title>
+ <para>
+ Accessed using the org.freedesktop.DBus.Properties interface.
+ </para>
+ <xsl:apply-templates select="property" mode="detail"/>
+ </refsection>
+ </xsl:if>
+
+ <xsl:call-template name="do-types"/>
+
+ </refentry>
+
+ </xsl:template>
+
+ <xsl:template match="tp:flags">
+
+ <xsl:if test="not(@name) or @name = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @name on a tp:flags type&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:if test="not(@type) or @type = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @type on tp:flags type</xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text>&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <section>
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="@name"/>
+ </xsl:attribute>
+ <title><literal><xsl:value-of select="@name"/></literal></title>
+ <xsl:apply-templates select="tp:docstring" />
+ <xsl:apply-templates select="tp:added"/>
+ <xsl:apply-templates select="tp:changed"/>
+ <xsl:apply-templates select="tp:deprecated"/>
+ <glosslist>
+ <xsl:variable name="value-prefix">
+ <xsl:choose>
+ <xsl:when test="@value-prefix">
+ <xsl:value-of select="@value-prefix"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="@name"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:for-each select="tp:flag">
+ <glossentry>
+ <glossterm>
+ <xsl:value-of select="concat($value-prefix, '_', @suffix)"/> = <xsl:value-of select="@value"/>
+ </glossterm>
+ <glossdef>
+ <xsl:choose>
+ <xsl:when test="tp:docstring">
+ <xsl:apply-templates select="tp:docstring" />
+ <xsl:apply-templates select="tp:added"/>
+ <xsl:apply-templates select="tp:changed"/>
+ <xsl:apply-templates select="tp:deprecated"/>
+ </xsl:when>
+ <xsl:otherwise>
+ (Undocumented)
+ </xsl:otherwise>
+ </xsl:choose>
+ </glossdef>
+ </glossentry>
+ </xsl:for-each>
+ </glosslist>
+ </section>
+ </xsl:template>
+
+ <xsl:template match="tp:enum">
+
+ <xsl:if test="not(@name) or @name = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @name on a tp:enum type&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:if test="not(@type) or @type = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @type on tp:enum type</xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text>&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <section>
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="concat('type-', @name)"/>
+ </xsl:attribute>
+ <title><literal><xsl:value-of select="@name"/></literal></title>
+ <xsl:apply-templates select="tp:docstring" />
+ <xsl:apply-templates select="tp:added"/>
+ <xsl:apply-templates select="tp:changed"/>
+ <xsl:apply-templates select="tp:deprecated"/>
+ <glosslist>
+ <xsl:variable name="value-prefix">
+ <xsl:choose>
+ <xsl:when test="@value-prefix">
+ <xsl:value-of select="@value-prefix"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="@name"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:for-each select="tp:enumvalue">
+ <glossentry>
+ <glossterm>
+ <xsl:value-of select="concat($value-prefix, '_', @suffix)"/> = <xsl:value-of select="@value"/>
+ </glossterm>
+ <glossdef>
+ <xsl:choose>
+ <xsl:when test="tp:docstring">
+ <xsl:apply-templates select="tp:docstring" />
+ <xsl:apply-templates select="tp:added"/>
+ <xsl:apply-templates select="tp:changed"/>
+ <xsl:apply-templates select="tp:deprecated"/>
+ </xsl:when>
+ <xsl:otherwise>
+ (Undocumented)
+ </xsl:otherwise>
+ </xsl:choose>
+ </glossdef>
+ </glossentry>
+ </xsl:for-each>
+ </glosslist>
+ </section>
+ </xsl:template>
+
+ <xsl:template match="property" mode="fieldsynopsis">
+
+ <fieldsynopsis>
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="concat(../@name, '.', @name)"/>
+ </xsl:attribute>
+ <modifier>
+ <xsl:choose>
+ <xsl:when test="@access = 'read'">
+ <xsl:text>READ</xsl:text>
+ </xsl:when>
+ <xsl:when test="@access = 'write'">
+ <xsl:text>WRITE</xsl:text>
+ </xsl:when>
+ <xsl:when test="@access = 'readwrite'">
+ <xsl:text>READWRITE</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: unknown or missing value for </xsl:text>
+ <xsl:text>@access on property </xsl:text>
+ <xsl:value-of select="concat(../@name, '.', @name)"/>
+ <xsl:text>: '</xsl:text>
+ <xsl:value-of select="@access"/>
+ <xsl:text>'&#10;</xsl:text>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </modifier>
+ <type>
+ <xsl:call-template name="ResolveType">
+ <xsl:with-param name="node" select="."/>
+ </xsl:call-template>
+ </type>
+ <varname>
+ <xsl:value-of select="@name"/>
+ </varname>
+ </fieldsynopsis>
+
+ </xsl:template>
+
+ <xsl:template match="property" mode="fieldsynopsislinked">
+
+ <fieldsynopsis>
+ <modifier>
+ <xsl:choose>
+ <xsl:when test="@access = 'read'">
+ <xsl:text>READ</xsl:text>
+ </xsl:when>
+ <xsl:when test="@access = 'write'">
+ <xsl:text>WRITE</xsl:text>
+ </xsl:when>
+ <xsl:when test="@access = 'readwrite'">
+ <xsl:text>READWRITE</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: unknown or missing value for </xsl:text>
+ <xsl:text>@access on property </xsl:text>
+ <xsl:value-of select="concat(../@name, '.', @name)"/>
+ <xsl:text>: '</xsl:text>
+ <xsl:value-of select="@access"/>
+ <xsl:text>'&#10;</xsl:text>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </modifier>
+ <type>
+ <xsl:call-template name="ResolveType">
+ <xsl:with-param name="node" select="."/>
+ </xsl:call-template>
+ </type>
+ <varname>
+ <xsl:attribute name="xlink:href">
+ <xsl:value-of select="concat('#', ../@name, '.', @name)"/>
+ </xsl:attribute>
+ <xsl:value-of select="@name"/>
+ </varname>
+ </fieldsynopsis>
+
+ </xsl:template>
+
+ <xsl:template match="property" mode="detail">
+
+ <xsl:if test="not(parent::interface)">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: property </xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text> does not have an interface as parent&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:if test="not(@name) or @name = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @name on a property of </xsl:text>
+ <xsl:value-of select="../@name"/>
+ <xsl:text>&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:if test="not(@type) or @type = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @type on property </xsl:text>
+ <xsl:value-of select="concat(../@name, '.', @name)"/>
+ <xsl:text>: '</xsl:text>
+ <xsl:value-of select="@access"/>
+ <xsl:text>'&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:apply-templates select="." mode="fieldsynopsis"/>
+
+ <xsl:apply-templates select="tp:docstring"/>
+ <xsl:apply-templates select="tp:added"/>
+ <xsl:apply-templates select="tp:changed"/>
+ <xsl:apply-templates select="tp:deprecated"/>
+ </xsl:template>
+
+ <xsl:template match="tp:property" mode="detail">
+ <glossentry>
+ <glossterm>
+ <xsl:if test="@name">
+ <xsl:value-of select="@name"/> −
+ </xsl:if>
+ <xsl:value-of select="@type"/>
+ </glossterm>
+ <glossdef>
+ <xsl:apply-templates select="tp:docstring"/>
+ <xsl:apply-templates select="tp:added"/>
+ <xsl:apply-templates select="tp:changed"/>
+ <xsl:apply-templates select="tp:deprecated"/>
+ </glossdef>
+ </glossentry>
+ </xsl:template>
+
+ <xsl:template match="tp:mapping">
+ <section>
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="concat('type-', @name)"/>
+ </xsl:attribute>
+ <title>
+ <literal><xsl:value-of select="@name"/></literal>
+ </title>
+ <xsl:apply-templates select="tp:docstring"/>
+ <xsl:if test="string(@array-name) != ''">
+ <para>
+ In bindings that need a separate name, arrays of
+ <xsl:value-of select="@name"/> should be called
+ <xsl:value-of select="@array-name"/>.
+ </para>
+ </xsl:if>
+ <section>
+ <title>Members</title>
+ <glosslist>
+ <xsl:apply-templates select="tp:member" mode="description"/>
+ </glosslist>
+ </section>
+ </section>
+ </xsl:template>
+
+ <xsl:template match="tp:docstring" mode="in-index"/>
+
+ <xsl:template match="tp:simple-type | tp:enum | tp:flags | tp:external-type"
+ mode="in-index">
+ − <xsl:value-of select="@type"/>
+ </xsl:template>
+
+ <xsl:template match="tp:simple-type">
+
+ <xsl:if test="not(@name) or @name = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @name on a tp:simple-type&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:if test="not(@type) or @type = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @type on tp:simple-type</xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text>&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <section>
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="concat('type-', @name)"/>
+ </xsl:attribute>
+ <title>
+ <literal><xsl:value-of select="@name"/> − <xsl:value-of select="@type"/></literal>
+ </title>
+ <para>
+ <xsl:apply-templates select="tp:docstring"/>
+ <xsl:apply-templates select="tp:added"/>
+ <xsl:apply-templates select="tp:changed"/>
+ <xsl:apply-templates select="tp:deprecated"/>
+ </para>
+ </section>
+ </xsl:template>
+
+ <xsl:template match="tp:external-type">
+
+ <xsl:if test="not(@name) or @name = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @name on a tp:external-type&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:if test="not(@type) or @type = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @type on tp:external-type</xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text>&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <glossentry>
+ <glossterm>
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="concat('type-', @name)"/>
+ </xsl:attribute>
+ <xsl:value-of select="@name"/> − <xsl:value-of select="@type"/>
+ </glossterm>
+ <glossdef>Defined by: <xsl:value-of select="@from"/></glossdef>
+ </glossentry>
+ </xsl:template>
+
+ <xsl:template match="tp:struct" mode="in-index">
+ − ( <xsl:for-each select="tp:member">
+ <xsl:value-of select="@type"/>
+ <xsl:if test="position() != last()">, </xsl:if>
+ </xsl:for-each> )
+ </xsl:template>
+
+ <xsl:template match="tp:mapping" mode="in-index">
+ − a{ <xsl:for-each select="tp:member">
+ <xsl:value-of select="@type"/>
+ <xsl:if test="position() != last()"> &#x2192; </xsl:if>
+ </xsl:for-each> }
+ </xsl:template>
+
+ <xsl:template match="tp:struct">
+ <section>
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="concat('type-', @name)"/>
+ </xsl:attribute>
+ <title>
+ <literal>
+ <xsl:value-of select="@name"/>
+ </literal>
+ </title>
+ <xsl:apply-templates select="tp:docstring"/>
+ <xsl:apply-templates select="tp:added"/>
+ <xsl:apply-templates select="tp:changed"/>
+ <xsl:apply-templates select="tp:deprecated"/>
+ <xsl:choose>
+ <xsl:when test="string(@array-name) != ''">
+ <para>In bindings that need a separate name, arrays of
+ <xsl:value-of select="@name"/> should be called
+ <xsl:value-of select="@array-name"/>.</para>
+ </xsl:when>
+ <xsl:otherwise>
+ <para>Arrays of <xsl:value-of select="@name"/> don't generally
+ make sense.</para>
+ </xsl:otherwise>
+ </xsl:choose>
+ <classsynopsis>
+ <ooclass>
+ <modifier>struct</modifier>
+ <classname><xsl:value-of select="@name"/></classname>
+ </ooclass>
+ <xsl:apply-templates select="tp:member" mode="fieldsynopsis"/>
+ </classsynopsis>
+ <glosslist>
+ <xsl:apply-templates select="tp:member" mode="description"/>
+ </glosslist>
+ </section>
+ </xsl:template>
+
+ <xsl:template match="arg" mode="paramdef">
+ <paramdef>
+ <xsl:call-template name="direction">
+ <xsl:with-param name="indirection" select="@direction"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ <type>
+ <xsl:call-template name="ResolveType">
+ <xsl:with-param name="node" select="."/>
+ </xsl:call-template>
+ </type>
+ <xsl:text> </xsl:text>
+ <parameter><xsl:value-of select="@name"/></parameter>
+ </paramdef>
+ </xsl:template>
+
+ <xsl:template match="arg" mode="paramtable">
+ <glossentry>
+ <glossterm><literal><xsl:value-of select="@name"/></literal></glossterm>
+ <glossdef>
+ <para><xsl:apply-templates select="tp:docstring" mode="nopara"/></para>
+ </glossdef>
+ </glossentry>
+ </xsl:template>
+
+ <xsl:template match="method|signal" mode="funcsynopsis">
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>
+ <function>
+ <xsl:value-of select="@name"/>
+ </function>
+ </funcdef>
+ <xsl:choose>
+ <xsl:when test="arg">
+ <xsl:apply-templates select="arg" mode="paramdef"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <void/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </funcprototype>
+ </funcsynopsis>
+ </xsl:template>
+
+ <xsl:template match="method|signal" mode="funcsynopsislinked">
+ <funcprototype>
+ <funcdef>
+ <function linkend="{concat(parent::interface//@name, '.', @name)}">
+ <xsl:value-of select="@name"/>
+ </function>
+ </funcdef>
+ <xsl:choose>
+ <xsl:when test="arg">
+ <xsl:apply-templates select="arg" mode="paramdef"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <void/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </funcprototype>
+ </xsl:template>
+
+ <xsl:template match="method" mode="detail">
+
+ <xsl:if test="not(parent::interface)">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: method </xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text> does not have an interface as parent&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:if test="not(@name) or @name = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @name on a method of </xsl:text>
+ <xsl:value-of select="../@name"/>
+ <xsl:text>&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:for-each select="arg">
+ <xsl:if test="not(@type) or @type = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: an arg of method </xsl:text>
+ <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+ <xsl:text> has no type</xsl:text>
+ </xsl:message>
+ </xsl:if>
+ <xsl:choose>
+ <xsl:when test="@direction='in'">
+ <xsl:if test="not(@name) or @name = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: an 'in' arg of method </xsl:text>
+ <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+ <xsl:text> has no name</xsl:text>
+ </xsl:message>
+ </xsl:if>
+ </xsl:when>
+ <xsl:when test="@direction='out'">
+ <xsl:if test="not(@name) or @name = ''">
+ <xsl:message terminate="no">
+ <xsl:text>INFO: an 'out' arg of method </xsl:text>
+ <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+ <xsl:text> has no name</xsl:text>
+ </xsl:message>
+ </xsl:if>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: an arg of method </xsl:text>
+ <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+ <xsl:text> has direction neither 'in' nor 'out'</xsl:text>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:for-each>
+
+ <refsection>
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="concat(../@name, concat('.', @name))"/>
+ </xsl:attribute>
+ <title>
+ <literal><xsl:value-of select="concat(../@name, concat('.', @name))"/></literal>
+ </title>
+ <xsl:apply-templates select="." mode="funcsynopsis"/>
+ <xsl:apply-templates select="tp:docstring"/>
+ <xsl:apply-templates select="tp:added"/>
+ <xsl:apply-templates select="tp:changed"/>
+ <xsl:apply-templates select="tp:deprecated"/>
+
+ <xsl:if test="arg">
+ <glosslist>
+ <xsl:apply-templates select="arg" mode="paramtable"/>
+ </glosslist>
+ </xsl:if>
+
+ <xsl:if test="tp:possible-errors">
+ <formalpara>
+ <title>Possible errors</title>
+ <para>
+ <glosslist>
+ <xsl:apply-templates select="tp:possible-errors/tp:error"/>
+ </glosslist>
+ </para>
+ </formalpara>
+ </xsl:if>
+ </refsection>
+ </xsl:template>
+
+ <xsl:template name="tp-type">
+ <xsl:param name="tp-type"/>
+ <xsl:param name="type"/>
+
+ <xsl:variable name="single-type">
+ <xsl:choose>
+ <xsl:when test="contains($tp-type, '[]')">
+ <xsl:value-of select="substring-before($tp-type, '[]')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$tp-type"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:variable name="type-of-tp-type">
+ <xsl:if test="contains($tp-type, '[]')">
+ <!-- one 'a', plus one for each [ after the [], and delete all ] -->
+ <xsl:value-of select="concat('a',
+ translate(substring-after($tp-type, '[]'), '[]', 'a'))"/>
+ </xsl:if>
+
+ <xsl:choose>
+ <xsl:when test="//tp:simple-type[@name=$single-type]">
+ <xsl:value-of select="string(//tp:simple-type[@name=$single-type]/@type)"/>
+ </xsl:when>
+ <xsl:when test="//tp:struct[@name=$single-type]">
+ <xsl:text>(</xsl:text>
+ <xsl:for-each select="//tp:struct[@name=$single-type]/tp:member">
+ <xsl:value-of select="@type"/>
+ </xsl:for-each>
+ <xsl:text>)</xsl:text>
+ </xsl:when>
+ <xsl:when test="//tp:enum[@name=$single-type]">
+ <xsl:value-of select="string(//tp:enum[@name=$single-type]/@type)"/>
+ </xsl:when>
+ <xsl:when test="//tp:flags[@name=$single-type]">
+ <xsl:value-of select="string(//tp:flags[@name=$single-type]/@type)"/>
+ </xsl:when>
+ <xsl:when test="//tp:mapping[@name=$single-type]">
+ <xsl:text>a{</xsl:text>
+ <xsl:for-each select="//tp:mapping[@name=$single-type]/tp:member">
+ <xsl:value-of select="@type"/>
+ </xsl:for-each>
+ <xsl:text>}</xsl:text>
+ </xsl:when>
+ <xsl:when test="//tp:external-type[@name=$single-type]">
+ <xsl:value-of select="string(//tp:external-type[@name=$single-type]/@type)"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: Unable to find type '</xsl:text>
+ <xsl:value-of select="$tp-type"/>
+ <xsl:text>'&#10;</xsl:text>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:if test="string($type) != '' and
+ string($type-of-tp-type) != string($type)">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: tp:type '</xsl:text>
+ <xsl:value-of select="$tp-type"/>
+ <xsl:text>' has D-Bus type '</xsl:text>
+ <xsl:value-of select="$type-of-tp-type"/>
+ <xsl:text>' but has been used with type='</xsl:text>
+ <xsl:value-of select="$type"/>
+ <xsl:text>'&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <link linkend="type-{$single-type}">
+ <literal><xsl:value-of select="concat('type-', $single-type)"/></literal>
+ </link>
+
+ </xsl:template>
+
+ <xsl:template name="parenthesized-tp-type">
+ <xsl:if test="@tp:type">
+ <xsl:text> (</xsl:text>
+ <xsl:call-template name="tp-type">
+ <xsl:with-param name="tp-type" select="@tp:type"/>
+ <xsl:with-param name="type" select="@type"/>
+ </xsl:call-template>
+ <xsl:text>)</xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="tp:member" mode="fieldsynopsis">
+ <xsl:variable name="type">
+ <xsl:call-template name="ResolveType">
+ <xsl:with-param name="node" select="."/>
+ </xsl:call-template>
+ </xsl:variable>
+ <fieldsynopsis>
+ <type><xsl:value-of select="normalize-space($type)"/></type>
+ <varname><xsl:value-of select="@name"/></varname>
+ </fieldsynopsis>
+ </xsl:template>
+
+ <xsl:template match="tp:member" mode="description">
+ <glossentry>
+ <glossterm>
+ <varname><xsl:value-of select="@name"/></varname>
+ </glossterm>
+ <glossdef>
+ <xsl:choose>
+ <xsl:when test="tp:docstring">
+ <xsl:apply-templates select="tp:docstring" />
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- emphasize -->
+ (undocumented)
+ </xsl:otherwise>
+ </xsl:choose>
+ </glossdef>
+ </glossentry>
+ </xsl:template>
+
+ <xsl:template match="tp:possible-errors/tp:error">
+ <glossentry>
+ <glossterm>
+ <xsl:value-of select="@name"/>
+ </glossterm>
+ <glossdef>
+ <xsl:variable name="name" select="@name"/>
+ <xsl:choose>
+ <xsl:when test="tp:docstring">
+ <xsl:apply-templates select="tp:docstring"/>
+ </xsl:when>
+ <xsl:when test="//tp:errors/tp:error[concat(../@namespace, '.', translate(@name, ' ', ''))=$name]/tp:docstring">
+ <xsl:apply-templates select="//tp:errors/tp:error[concat(../@namespace, '.', translate(@name, ' ', ''))=$name]/tp:docstring"/> <!-- TODO: emphasize -->(generic description)
+ </xsl:when>
+ <xsl:otherwise>
+ (Undocumented.)
+ </xsl:otherwise>
+ </xsl:choose>
+ </glossdef>
+ </glossentry>
+ </xsl:template>
+
+ <xsl:template match="signal" mode="detail">
+
+ <xsl:if test="not(parent::interface)">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: signal </xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text> does not have an interface as parent&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:if test="not(@name) or @name = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @name on a signal of </xsl:text>
+ <xsl:value-of select="../@name"/>
+ <xsl:text>&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:for-each select="arg">
+ <xsl:if test="not(@type) or @type = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: an arg of signal </xsl:text>
+ <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+ <xsl:text> has no type</xsl:text>
+ </xsl:message>
+ </xsl:if>
+ <xsl:if test="not(@name) or @name = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: an arg of signal </xsl:text>
+ <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+ <xsl:text> has no name</xsl:text>
+ </xsl:message>
+ </xsl:if>
+ <xsl:choose>
+ <xsl:when test="not(@direction)"/>
+ <xsl:when test="@direction='in'"/>
+ <!-- This doesn't work with the DTD (see comment in DTD)
+ <xsl:when test="@direction='in'">
+ <xsl:message terminate="no">
+ <xsl:text>INFO: an arg of signal </xsl:text>
+ <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+ <xsl:text> has unnecessary direction 'in'</xsl:text>
+ </xsl:message>
+ </xsl:when>
+ -->
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: an arg of signal </xsl:text>
+ <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+ <xsl:text> has direction other than 'in'</xsl:text>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:for-each>
+
+ <refsection>
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="concat(../@name, concat('.', @name))"/>
+ </xsl:attribute>
+ <title>
+ <literal><xsl:value-of select="concat(../@name, concat('.', @name))"/></literal>
+ </title>
+ <xsl:apply-templates select="." mode="funcsynopsis"/>
+ <xsl:apply-templates select="tp:docstring"/>
+ <xsl:apply-templates select="tp:added"/>
+ <xsl:apply-templates select="tp:changed"/>
+ <xsl:apply-templates select="tp:deprecated"/>
+
+ <xsl:if test="arg">
+ <glosslist>
+ <xsl:apply-templates select="arg" mode="paramtable"/>
+ </glosslist>
+ </xsl:if>
+ </refsection>
+ </xsl:template>
+
+ <xsl:template match="/tp:spec">
+ <book xmlns="http://docbook.org/ns/docbook" version="5.0">
+ <bookinfo>
+ <title><xsl:value-of select="tp:title"/></title>
+ <xsl:apply-templates select="tp:copyright"/>
+ <xsl:apply-templates select="tp:license"/>
+ <xsl:if test="tp:docstring">
+ <abstract>
+ <xsl:apply-templates select="tp:docstring"/>
+ </abstract>
+ </xsl:if>
+ <!-- TODO: Version
+ <xsl:if test="tp:version">
+ <xsl:text> version </xsl:text>
+ <xsl:value-of select="tp:version"/>
+ </xsl:if> -->
+ </bookinfo>
+ <chapter>
+ <xsl:attribute name="xml:id">interfaces</xsl:attribute>
+ <title>Interfaces</title>
+ <xsl:apply-templates select="//node"/>
+ </chapter>
+ <xsl:call-template name="generic-types"/>
+ <xsl:if test="tp:errors">
+ <chapter>
+ <xsl:attribute name="xml:id">errors</xsl:attribute>
+ <xsl:apply-templates select="tp:errors"/>
+ </chapter>
+ </xsl:if>
+ </book>
+ </xsl:template>
+
+ <xsl:template match="node">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="text()">
+ <xsl:if test="normalize-space(.) != ''">
+ <xsl:message terminate="yes">
+ <xsl:text>Stray text: {{{</xsl:text>
+ <xsl:value-of select="." />
+ <xsl:text>}}}&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="*">
+ <xsl:message terminate="yes">
+ <xsl:text>Unrecognised element: {</xsl:text>
+ <xsl:value-of select="namespace-uri(.)" />
+ <xsl:text>}</xsl:text>
+ <xsl:value-of select="local-name(.)" />
+ <xsl:text>&#10;</xsl:text>
+ </xsl:message>
+ </xsl:template>
+</xsl:stylesheet>
+
+<!-- vim:set sw=2 sts=2 et: -->
diff --git a/secret-service/tools/spec-to-introspect.xsl b/secret-service/tools/spec-to-introspect.xsl
new file mode 100644
index 0000000..2468237
--- /dev/null
+++ b/secret-service/tools/spec-to-introspect.xsl
@@ -0,0 +1,144 @@
+<?xml version="1.0"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ exclude-result-prefixes="tp">
+
+<!--
+ Telepathy D-Bus Introspection to EggDBus Introspection format translator.
+
+ Copyright 2009 Michael Leupold <lemma@confuego.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+-->
+
+<!--
+ TODO:
+ - Enable conversion of dictionary element types (eg. "{ss}") and
+ struct types (eg. "(sayay)")
+ - unhandled: tp:simple-type, tp:enum, tp:flags, tp:external-type
+ - tp:docstring may contain XHTML which this template doesn't handle
+-->
+
+ <xsl:include href="resolve-type.xsl"/>
+
+ <!-- main template -->
+ <xsl:template match="tp:spec">
+ <node>
+ <xsl:apply-templates select="tp:errors"/>
+ <xsl:apply-templates select="tp:struct"/>
+ <!-- TODO: <xsl:apply-templates select="tp:mapping"/> -->
+ <xsl:apply-templates select="node/interface"/>
+ </node>
+ </xsl:template>
+
+ <!-- handle most of the D-Bus introspection elements -->
+ <xsl:template match="interface|annotation|method|signal">
+ <xsl:copy>
+ <xsl:for-each select="@*">
+ <xsl:if test="not(starts-with(name(), 'tp:'))">
+ <xsl:copy/>
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:apply-templates/>
+ </xsl:copy>
+ </xsl:template>
+
+ <!-- handle the arg and property D-Bus introspection elements.
+ They get special handling because they may contain a tp:type
+ attribute -->
+ <xsl:template match="arg|property">
+ <xsl:copy>
+ <xsl:for-each select="@*">
+ <xsl:if test="not(starts-with(name(), 'tp:'))">
+ <xsl:copy/>
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:for-each select="@tp:type">
+ <xsl:variable name="type">
+ <xsl:call-template name="TpType">
+ <xsl:with-param name="type" select="."/>
+ </xsl:call-template>
+ </xsl:variable>
+ <annotation name="org.gtk.EggDBus.Type">
+ <xsl:attribute name="value">
+ <xsl:value-of select="translate(translate($type, ' ', ''), '&#xa;', '')"/>
+ </xsl:attribute>
+ </annotation>
+ </xsl:for-each>
+ <xsl:apply-templates/>
+ </xsl:copy>
+ </xsl:template>
+
+ <!-- tp:docstring to org.gtk.EggDBus.DocString -->
+ <xsl:template match="tp:docstring">
+ <annotation name="org.gtk.EggDBus.DocString">
+ <xsl:attribute name="value">
+ <xsl:value-of select="normalize-space(text())"/>
+ </xsl:attribute>
+ </annotation>
+ </xsl:template>
+
+ <!-- tp:errors to org.gtk.EggDBus.DeclareErrorDomain -->
+ <xsl:template match="tp:errors">
+ <annotation value="Error" name="org.gtk.EggDBus.DeclareErrorDomain">
+ <xsl:apply-templates select="tp:docstring"/>
+ <xsl:apply-templates select="tp:error"/>
+ </annotation>
+ </xsl:template>
+
+ <!-- tp:error to org.gtk.EggDBus.ErrorDomain.Member -->
+ <xsl:template match="tp:error">
+ <annotation name="org.gtk.EggDBus.ErrorDomain.Member">
+ <xsl:attribute name="value">
+ <xsl:value-of select="concat(../@namespace, '.', @name)"/>
+ </xsl:attribute>
+ <xsl:apply-templates select="tp:docstring"/>
+ </annotation>
+ </xsl:template>
+
+ <!-- tp:struct to org.gtk.EggDBus.DeclareStruct -->
+ <xsl:template match="tp:struct">
+ <annotation name="org.gtk.EggDBus.DeclareStruct">
+ <xsl:attribute name="value">
+ <xsl:value-of select="@name"/>
+ </xsl:attribute>
+ <xsl:apply-templates select="tp:docstring"/>
+ <xsl:apply-templates select="tp:member"/>
+ </annotation>
+ </xsl:template>
+
+ <!-- tp:member to org.gtk.EggDBus.Struct.Member -->
+ <xsl:template match="tp:member">
+ <xsl:variable name="type">
+ <xsl:call-template name="ResolveType">
+ <xsl:with-param name="node" select="."/>
+ </xsl:call-template>
+ </xsl:variable>
+ <annotation name="org.gtk.EggDBus.Struct.Member">
+ <xsl:attribute name="value">
+ <xsl:value-of select="concat(normalize-space($type), ':', @name)"/>
+ </xsl:attribute>
+ <xsl:apply-templates select="tp:docstring"/>
+ </annotation>
+ </xsl:template>
+
+ <xsl:template match="text()"/>
+
+ <xsl:output method="xml" indent="yes" encoding="UTF-8"
+ omit-xml-declaration="no"
+ doctype-system="http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"
+ doctype-public="-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"/>
+
+</xsl:stylesheet>
diff --git a/systemtray/ChangeLog b/systemtray/ChangeLog
new file mode 100644
index 0000000..9487095
--- /dev/null
+++ b/systemtray/ChangeLog
@@ -0,0 +1,10 @@
+2009-01-10 Vincent Untz <vuntz@gnome.org>
+
+ * systemtray-spec.xml: add _NET_SYSTEM_TRAY_VISUAL and a description of
+ visual and background pixmap handling..
+ See http://lists.freedesktop.org/archives/xdg/2009-January/010122.html
+
+2004-11-30 Mark McLoughlin <mark@skynet.ie>
+
+ * systemtray-spec.xml: add _NET_SYSTEM_TRAY_ORIENTATION.
+
diff --git a/systemtray/systemtray-spec.xml b/systemtray/systemtray-spec.xml
new file mode 100644
index 0000000..669f3d0
--- /dev/null
+++ b/systemtray/systemtray-spec.xml
@@ -0,0 +1,444 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<article id="index">
+ <articleinfo>
+ <title>System Tray Protocol Specification</title>
+ <releaseinfo>Version 0.3</releaseinfo>
+ <date>10 January 2009</date>
+ <authorgroup>
+ <author>
+ <firstname>Havoc</firstname>
+ <surname>Pennington</surname>
+ <affiliation>
+ <address>
+ <email>hp@redhat.com</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+ </articleinfo>
+
+ <sect1 id="overview">
+ <title>Overview</title>
+ <para>
+ The "system tray" is an application running on a given X screen
+ that can display small icons provided by running
+ applications. Windows XP calls this feature the "notification area."
+ <footnote><para>According to the MSDN documentation for the
+ <literal>Shell_NotifyIcon()</literal> function,
+ "The taskbar notification area is sometimes erroneously called
+ the 'tray.'" So presumably "notification area" is the official
+ term on Windows. Parts of the docs also call it the "status
+ area."</para></footnote> Inspired by KDE, this specification
+ uses the term "system tray."
+ </para>
+ <para>
+ From a UI standpoint, the system tray is normally used for
+ transient icons that indicate some special state, while
+ full-blown "applets" are used for permanent dock/panel
+ features. For example, a system tray icon might appear to tell
+ the user that they have new mail, or have an incoming instant
+ message, or something along those lines.
+ </para>
+ <para>
+ The basic idea is that creating an icon in the notification
+ area is less annoying than popping up a dialog. However it's
+ also harder to notice, so Windows XP adds a feature allowing
+ tray icons to pop up small message balloons. (Users can disable
+ these via a hidden registry setting.) This specification
+ also supports the balloon feature.
+ </para>
+ </sect1>
+
+ <sect1 id="definitions">
+ <title>Definitions</title>
+ <variablelist>
+ <varlistentry>
+ <term>System tray</term>
+ <listitem>
+ <para>
+ The system tray is an X client which owns a special
+ manager selection on a given screen and provides
+ container windows.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Selection owner window</term>
+ <listitem>
+ <para>
+ The selection owner window is the window belonging to the
+ System Tray that owns the manager selection (as in
+ <literal>XGetSelectionOwner()</literal>/<literal>XSetSelectionOwner()</literal>.
+ Note that this probably is not the same window that's used
+ to contain the system tray icons.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Tray icon</term>
+ <listitem>
+ <para>
+ The tray icon is a window to be embedded in the
+ system tray.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect1>
+
+ <sect1 id="locating">
+ <title>Locating the system tray</title>
+ <para>
+ On startup, the system tray must acquire a manager selection
+ called <literal>_NET_SYSTEM_TRAY_Sn</literal>, replacing
+ <literal>n</literal> with the screen number the tray wants to
+ use. The conventions for manager selections are defined in the
+ ICCCM.
+ </para>
+ <para>
+ Because the selection owner window should be destroyed when the
+ manager selection is lost, normally the selection owner window
+ will not be the same as any of the user-visible windows provided
+ by the system tray.
+ </para>
+ <para>
+ A system tray that fails to get the selection or loses the
+ selection should assume that another system tray is running,
+ and let the selection owner handle tray icons.
+ </para>
+ <para>
+ An application wishing to provide an icon to the system tray
+ should first locate the system tray by requesting the owner
+ window of the manager selection. If the manager selection has no
+ owner, clients may use the method described in the ICCCM
+ (watching for a <literal>MANAGER</literal> client message) to be
+ notified when a system tray appears.
+ </para>
+ </sect1>
+
+ <sect1 id="messages">
+ <title>Opcode messages</title>
+ <para>
+ Tray icons can send "opcodes" to
+ the system tray. These are X client messages, sent with
+ <literal>NoEventMask</literal>, a
+ <literal>message_type</literal> of
+ <literal>_NET_SYSTEM_TRAY_OPCODE</literal>, and format 32.
+ The first data field in the message is a timestamp (the stamp
+ of the current event, if available, otherwise CurrentTime).
+ The second data field is an integer indicating the op code
+ of the message:
+ <programlisting>
+#define SYSTEM_TRAY_REQUEST_DOCK 0
+#define SYSTEM_TRAY_BEGIN_MESSAGE 1
+#define SYSTEM_TRAY_CANCEL_MESSAGE 2
+ </programlisting>
+ The content remaining three data fields depends on the type of
+ message being sent. If they are unused by a particular
+ message, they should always be set to 0.
+ </para>
+ <para>
+ Here is an example of how to send a client message:
+ <programlisting><!--
+-->#include &lt;X11/Xlib.h&gt;
+
+void send_message(
+ Display* dpy, /* display */
+ Window w, /* sender (tray icon window) */
+ long message, /* message opcode */
+ long data1 /* message data 1 */
+ long data2 /* message data 2 */
+ long data3 /* message data 3 */
+){
+ XEvent ev;
+
+ memset(&amp;ev, 0, sizeof(ev));
+ ev.xclient.type = ClientMessage;
+ ev.xclient.window = w;
+ ev.xclient.message_type = XInternAtom (dpy, "_NET_SYSTEM_TRAY_OPCODE", False );
+ ev.xclient.format = 32;
+ ev.xclient.data.l[0] = x_time;
+ ev.xclient.data.l[1] = message;
+ ev.xclient.data.l[2] = data1;
+ ev.xclient.data.l[3] = data2;
+ ev.xclient.data.l[4] = data3;
+
+ trap_errors();
+ XSendEvent(dpy, w, False, NoEventMask, &amp;ev);
+ XSync(dpy, False);
+ if (untrap_errors()) {
+ /* Handle failure */
+ }
+}<!--
+ --></programlisting>
+
+ </para>
+
+ </sect1>
+
+ <sect1 id="docking">
+ <title>Docking a tray icon</title>
+ <para>
+ A tray icon must support the "client" or "plug" side of the
+ XEMBED specification. XEMBED is a protocol for cross-toolkit
+ widget embedding.
+ </para>
+ <para>
+ To begin the docking process, the tray icon application sends
+ a client message event to the manager selection owner window,
+ as described in <xref linkend="messages"/>. This event
+ should contain the <literal>SYSTEM_TRAY_REQUEST_DOCK</literal>
+ opcode, <literal>xclient.data.l[2]</literal> should contain
+ the X window ID of the tray icon to be docked.
+ </para>
+ <para>
+ At this point the "embedding life cycle" explained in the XEMBED
+ specification begins. The XEMBED specification explains how the
+ embedding application will interact with the embedded tray
+ icon, and how the embedder/embedded relationship may be ended.
+ </para>
+ <para>
+ Tray icons may be assigned any size by the system tray, and
+ should do their best to cope with any size effectively.
+ </para>
+ </sect1>
+
+ <sect1 id="visuals">
+ <title>Visual and background pixmap handling</title>
+ <para>
+ If the _NET_SYSTEM_TRAY_VISUAL property (see below) is present,
+ tray icon windows should be created using that visual. If the
+ property is not present, then tray icon windows should be
+ created using the default visual of the screen.
+ </para>
+ <para>
+ Historically, to allow the appearance of icons with transparent
+ backgrounds on servers that did not support visuals with an
+ alpha channel or the Composite extension, a convention was
+ adopted where a background pixmap was set on the XEMBED embedder
+ window, aligned properly to match the contents of the embedder
+ window's parent, the tray icon window was set to have a
+ background of ParentRelative, and drawing of the icon done on
+ top of this background.
+ </para>
+ <para>
+ Setting the background of a window to ParentRelative when the
+ depth of the window does not match the depth of the window's
+ parent, or reparenting a window with a ParentRelative
+ background into a parent with a non-matching depth produces a
+ BadMatch error, so the embedder window must be created to match
+ the visual of the tray icon window, even if the tray icon window
+ does not have the visual provided in _NET_SYSTEM_TRAY_VISUAL. If
+ convenient, the tray manager may set an appropriate background
+ pixmap on the embedder window to provide the appearance of
+ transparency. However, the preferred method of transparency is
+ to use a visual with an alpha channel in _NET_SYSTEM_TRAY_VISUAL.
+ </para>
+ </sect1>
+
+ <sect1 id="hints">
+ <title>Tray icon hints</title>
+ <para>
+ Tray icons should set the following hints to help the system
+ tray provide a nice user interface. The name and icon hints
+ are used if the system tray needs to refer to a tray icon;
+ for example, the system tray may present a list of tray
+ icons and let the user reorder them or change their properties.
+ </para>
+
+ <sect2><title>_NET_WM_NAME</title>
+ <programlisting><![CDATA[
+_NET_WM_NAME, UTF8_STRING
+]]></programlisting>
+ <para>
+ This hint should be set as it would be for a normal toplevel
+ window, as defined in the Extended Window Manager Hints
+ Specification (EWMH). The hint MUST be in UTF-8 encoding. It
+ provides a human-readable, localized name for the tray icon.
+ </para>
+ </sect2>
+
+ <sect2><title>WM_CLASS</title>
+ <programlisting><![CDATA[
+WM_CLASS, STRING
+]]></programlisting>
+ <para>
+ This hint should be set as it would be for a normal toplevel
+ window, as defined in the ICCCM. The system tray can use it
+ to distinguish different kinds of tray icon. This is useful
+ for example if the system tray wants to save and restore the
+ positions of the icons in the tray.
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>_NET_WM_ICON</title>
+ <programlisting><![CDATA[
+_NET_WM_ICON CARDINAL[][2+n]/32
+]]></programlisting>
+ <para>
+ This hint should be set as it would be for a normal toplevel
+ window, as defined in the Extended Window Manager Hints
+ Specification (EWMH). See that specification for the format
+ of the icon data.
+ </para>
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="manager-hints">
+ <title>Tray manager hints</title>
+ <para>
+The tray manager should set the following hints on the selection
+owner window.
+ </para>
+
+ <sect2>
+ <title>_NET_SYSTEM_TRAY_ORIENTATION</title>
+ <programlisting><![CDATA[
+_NET_SYSTEM_TRAY_ORIENTATION orientation CARDINAL/32
+]]>
+ #define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0
+ #define _NET_SYSTEM_TRAY_ORIENTATION_VERT 1
+ </programlisting>
+
+ <para>
+The property should be set by the tray manager to indicate the current
+orientation of the tray. Tray icons may use this hint in order to
+maintain the icon's aspect ratio and also as an indication of how the
+icon contents should be laid out.
+ </para>
+
+ </sect2>
+
+ <sect2>
+ <title>_NET_SYSTEM_TRAY_VISUAL</title>
+ <programlisting><![CDATA[
+_NET_SYSTEM_TRAY_VISUAL visual_id VISUALID/32
+]]>
+ </programlisting>
+
+ <para>
+The property should be set by the tray manager to indicate the preferred
+visual for icon windows. To avoid ambiguity about the colormap to use
+this visual must either be the default visual for the screen or it
+must be a TrueColor visual. If this property is set to a visual with
+an alpha channel, the tray manager must use the Composite extension to
+composite the icon against the background using PictOpOver.
+ </para>
+
+ </sect2>
+ </sect1>
+
+ <sect1 id="balloon">
+ <title>Balloon messages</title>
+ <para>
+ Tray icons may ask the system tray to display a balloon message
+ to the user. The system tray coordinates balloon messages
+ to ensure that they have a consistent look-and-feel, and to
+ avoid displaying multiple balloon messages at once.
+ </para>
+ <para>
+ A balloon message is a short text message to be displayed to
+ the user. The message may have a timeout; if so, the message
+ will be taken down after the timeout expires. Messages are
+ displayed in a queue, as only one can appear at a time;
+ if a message has a timeout, the timer begins when the message
+ is first displayed. Users may be allowed to close messages at
+ any time, and may be allowed to disable all message display.
+ </para>
+ <para>
+ System trays may display balloon messages in any way they
+ see fit; for example, instead of popping up a balloon, they
+ could choose to put a special indicator around icons with
+ pending messages, and display the message on mouseover.
+ </para>
+ <para>
+ Balloon messages are sent from the tray icon to the system tray
+ selection owner window as a series of client messages. The first
+ client message is an opcode message, and contains the usual timestamp,
+ and the op code <literal>SYSTEM_TRAY_BEGIN_MESSAGE</literal>.
+ <literal>xclient.data.l[2]</literal> contains the timeout in
+ thousandths of a second or zero for infinite timeout,
+ <literal>xclient.data.l[3]</literal> contains the length
+ of the message string in bytes, not including any nul bytes, and
+ <literal>xclient.data.l[4]</literal> contains an ID number
+ for the message. This ID number should never be reused by
+ the same tray icon. (The simplest way to generate the ID number
+ is to increment it with each message sent.)
+ </para>
+ <para>
+ Following the <literal>SYSTEM_TRAY_BEGIN_MESSAGE</literal>
+ op code, the tray icon should send a series of client messages
+ with a <literal>message_type</literal> of
+ <literal>_NET_SYSTEM_TRAY_MESSAGE_DATA</literal>. These client
+ messages must have their <literal>window</literal> field set
+ to the window ID of the tray icon, and have a
+ <literal>format</literal> of 8.
+ </para>
+ <para>
+ Each <literal>_NET_SYSTEM_TRAY_MESSAGE_DATA</literal> message
+ contains 20 bytes of the message string, up to the length given
+ in the <literal>SYSTEM_TRAY_BEGIN_MESSAGE</literal> opcode.
+ If the message string is zero-length, then no messages need be
+ sent beyond the <literal>SYSTEM_TRAY_BEGIN_MESSAGE</literal>.
+ A terminating nul byte should never be sent.
+ </para>
+ <para>
+ System trays may receive portions of messages from several
+ tray icons at once, so are required to reassemble the messages
+ based on the window ID of the tray icon.
+ </para>
+ <para>
+ The tray icon may wish to cancel a previously-sent balloon
+ message. To do so, it sends a
+ <literal>SYSTEM_TRAY_CANCEL_MESSAGE</literal> opcode with
+ <literal>data.l[2]</literal> set to the ID number of the message
+ to cancel.
+ </para>
+ </sect1>
+
+ <appendix id="changes">
+ <title>Change history</title>
+ <formalpara>
+ <title>Version 0.3, 10 January 2009, Owen Taylor</title>
+ <para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ Added the _NET_SYSTEM_TRAY_VISUAL hint and a
+ description of visual and background pixmap handling.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </formalpara>
+ <formalpara>
+ <title>Version 0.2, 23 November 2004, Mark McLoughlin</title>
+ <para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ Added the _NET_SYSTEM_TRAY_ORIENTATION hint.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </formalpara>
+ <formalpara>
+ <title>Version 0.1, 20 April 2002, Havoc Pennington</title>
+ <para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ Created initial draft.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </formalpara>
+ </appendix>
+</article>
diff --git a/trash/trashspec.html b/trash/trashspec.html
new file mode 100644
index 0000000..0950084
--- /dev/null
+++ b/trash/trashspec.html
@@ -0,0 +1,457 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=koi8-r">
+ <TITLE>Trash specification</TITLE>
+ <META NAME="GENERATOR" CONTENT="OpenOffice.org 1.1.0 (Linux)">
+ <META NAME="AUTHOR" CONTENT="Mikhail Ramendik">
+ <STYLE>
+ <!--
+ P.sdfootnote { margin-left: 0.5cm; text-indent: -0.5cm; margin-bottom: 0cm; font-size: 80% }
+ A.sdfootnoteanc { font-size: 57% }
+ -->
+ </STYLE>
+</HEAD>
+<BODY LANG="en-US" DIR="LTR">
+<H1>The FreeDesktop.org Trash specification</H1>
+<H3>Written by Mikhail Ramendik &lt;<A HREF="mailto:mr@ramendik.ru">mr@ramendik.ru</A>&gt;</H3>
+<H3>Content by David Faure &lt;<A HREF="mailto:faure@kde.org">faure@kde.org</A>&gt;,
+Alexander Larsson &lt;<A HREF="mailto:alexl@redhat.com">alexl@redhat.com</A>&gt;
+and others on the FreeDesktop.org mailing list</H3>
+<H3>Version 0.7</H3>
+<H2>Abstract</H2>
+<P>The purpose of this Specification is to provide a common way in
+which all &ldquo;Trash can&rdquo; implementations should store, list,
+and undelete trashed files. By complying with this Specification,
+various Trash implementations will be able to work with the same
+devices and use the same Trash storage. For example, if one
+implementation sends a file into the Trash can, another will be able
+to list it, undelete it, or clear it from the Trash.</P>
+<H2>Introduction</H2>
+<P>An ability to recover accidentally deleted files has become the de
+facto standard for today's desktop user experience.
+</P>
+<P>Users do not expect that anything they delete is permanently gone.
+Instead, they are used to a &ldquo;Trash can&rdquo; metaphor. A
+deleted document ends up in a &ldquo;Trash can&rdquo;, and stays
+there at least for some time &mdash; until the can is manually or
+automatically cleaned.
+</P>
+<P>This system has its own problems. Notably, cleaning disk space
+becomes a two-step operation &mdash; delete files and empty trash;
+this can lead to confusion for inexperienced users (&ldquo;what's
+taking up my space?!&rdquo;). Also, it is not easy to adapt the
+system to a multiuser environment. Besides, there is a potential for
+abuse by uneducated users &mdash; anecdotal evidence says they
+sometimes store important documents in the Trash can, and lose them
+when it gets cleaned!</P>
+<P>However, the benefits of this system are so great, and the user
+expectation for it so high, that it definitely should be implemented
+on a free desktop system. And in fact, several implementations
+already exist &mdash; some as command line utilities, some as
+preloaded libraries, and some as parts of major desktop environments.
+For example, both Gnome and KDE have their own trash mechanisms.</P>
+<P>This Specification is to provide a common way in which all Trash
+can implementations should store trashed files. By complying with
+this Specification, various Trash implementations will be able to
+work with the same devices and use the same Trash storage.
+</P>
+<P>This is important, at least, for shared network resources,
+removable devices, and in cases when different implementations are
+used on the same machine at different moments (i.e. some users prefer
+Gnome, others prefer KDE, and yet others are command-line fans).</P>
+<H2>Scope and limitations</H2>
+<P>This Specification only describes the Trash storage. It does <B>not</B>
+limit the ways in which the actual implementations should operate, as
+long as they use the same Trash storage. Command line utilities,
+desktop-integrated solutions and preloaded libraries can work with
+this specification. <A CLASS="sdfootnoteanc" NAME="sdfootnote1anc" HREF="#sdfootnote1sym"><SUP>1</SUP></A></P>
+<P>This Specification is geared towards the Unix file system tree
+approach. However, with slight modifications, it can easily be used
+with another kind of file system tree (for example, with drive
+letters).
+</P>
+<P>A multi-user environment, where users have specific numeric
+identifiers, is essential for this Specification.
+</P>
+<P>File systems and logon systems can be
+case-sensitive or non-case-sensitive; therefore, systems should
+generally not allow user names that differ only in case.</P>
+<H2>Definitions</H2>
+<P>Trash, or Trash can &mdash; the storage of files that were trashed
+(&ldquo;deleted&rdquo;) by the user. These files can be listed,
+undeleted, or cleaned from the trash can.</P>
+<P>Trashing &mdash; a &ldquo;delete&rdquo; operation in which files
+are transferred into the Trash can.</P>
+<P>Erasing &mdash; an operation in which files (possibly already in
+the Trash can) are removed (unlinked) from the file system. An erased
+file is generally considered to be non-recoverable; the space used by
+this file is freed. [A &ldquo;shredding&rdquo; operation, physically
+overwriting the data, may or may not accompany an erasing operation;
+the question of shredding is beyond the scope of this document].</P>
+<P>Original location &mdash; the name and location that a file
+(currently in the trash) had prior to getting trashed.</P>
+<P>Original filename &mdash; the name that a file (currently in the
+trash) had prior to getting trashed.
+</P>
+<P>Top directory , $topdir &mdash; the directory where a file system
+is mounted. &ldquo;/&rdquo; is the top directory for the root file
+system, but not for the other mounted file systems. For example,
+separate FSes can be mounted on &ldquo;/home&rdquo;, &ldquo;/mnt/flash&rdquo;,
+etc. In this text, the designation &ldquo;$topdir&rdquo; is used for
+&ldquo;any top directory&rdquo;.</P>
+<P>User identifier , $uid &mdash; the numeric user identifier for a
+user. $uid is used here as &ldquo;the numeric user identifier of the
+user who is currently logged on&rdquo;.</P>
+<P>Trash directory &mdash; a directory where trashed files, as well
+as the information on their original name/location and time of
+trashing, are stored. There may be several trash directories on one
+system; this Specification defines their location and contents. In
+this text, the designation &ldquo;$trash&rdquo; is used for &ldquo;any
+trash directory&rdquo;.</P>
+<P>&ldquo;Home trash&rdquo; directory &mdash; a user's main trash
+directory. Its name and location is defined in this document.</P>
+<P>The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;,
+&quot;SHALL&quot;, &quot;SHALL NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD
+NOT&quot;, &quot;RECOMMENDED&quot;, &quot;MAY&quot;, and &quot;OPTIONAL&quot;
+in this document are to be interpreted as described in <A HREF="http://www.faqs.org/rfcs/rfc2119.html">RFC
+2119</A>.</P>
+<H2>Trash directories</H2>
+<P>A system can have one or more trash directories. The contents of
+any trash directory are to be compliant with the same standard,
+described below.</P>
+<P>For every user<A CLASS="sdfootnoteanc" NAME="sdfootnote2anc" HREF="#sdfootnote2sym"><SUP>2</SUP></A>
+a &ldquo;home trash&rdquo; directory MUST be available<A CLASS="sdfootnoteanc" NAME="sdfootnote3anc" HREF="#sdfootnote3sym"><SUP>3</SUP></A>.
+Its name and location are $XDG_DATA_HOME/Trash ; $XDG_DATA_HOME is
+the base directory for user-specific data, as defined in the <A HREF="http://www.freedesktop.org/Standards/basedir-spec">Desktop
+Base Directory Specification</A> .
+</P>
+<P>The &ldquo;home trash&rdquo; should function as the user's main
+trash directory. Files that the user trashes from the same file
+system (device/partition) should be stored here (see the next section
+for the storage details). A &ldquo;home trash&rdquo; directory SHOULD
+be automatically created for any new user. If this directory is
+needed for a trashing operation but does not exist, the
+implementation SHOULD automatically create it, without any warnings
+or delays.</P>
+<P>The implementation MAY also support trashing files from the rest
+of the system (including other partitions, shared network resources,
+and removable devices) into the &ldquo;home trash&rdquo; directory .
+This is a &ldquo;failsafe&rdquo; method: trashing works for all file
+locations, the user can not fill up any space except the home
+directory, and as other users generally do not have access to it, no
+security issues arise.</P>
+<P>However, this solution leads to costly file copying (between
+partitions, over the network, from a removable device, etc.) A delay
+instead of a quick &ldquo;delete&rdquo; operation can be unpleasant
+to users.</P>
+<P>An implementation may choose not to support trashing in some of
+these cases (notably on network resources and removable devices).
+This is what some well known operating systems do.</P>
+<P>It may also choose to provide trashing in the &ldquo;top
+directories&rdquo; of some or all mounted resources. This trashing is
+done in two ways, described below as (1) and (2).
+</P>
+<P>(1) An administrator can create an $topdir/.Trash directory. The
+permissions on this directories should permit all users who can trash
+files at all to write in it.; and the &ldquo;sticky bit&rdquo; in the
+permissions must be set, if the file system supports it.
+</P>
+<P>When trashing a file from a non-home partition/device<A CLASS="sdfootnoteanc" NAME="sdfootnote4anc" HREF="#sdfootnote4sym"><SUP>4</SUP></A>
+, an implementation (if it supports trashing in top directories) MUST
+check for the presence of $topdir/.Trash.</P>
+<P>When preparing a list of all trashed files (i.e. to show to the
+user), an implementation also MUST check for .Trash in all top
+directories that are known to it.</P>
+<P>If this directory is present, the implementation MUST, by default,
+check for the &ldquo;sticky bit&rdquo;. (It MAY provide a way for the
+administrator, <I>and only the administrator</I>, to disable this
+checking for a particular top directory, in order to support file
+systems that do not have the &ldquo;sticky bit&rdquo;).</P>
+<P>The implementation also MUST check that
+this directory is not a symbolic link.
+</P>
+<P>If any of these checks fail, the
+implementation MUST NOT use this directory for either trashing or
+undeleting files, even is an appropriate $uid directory (see below)
+already exists in it. Besides, the implementation SHOULD report the
+failed check to the administrator, and MAY also report it to the
+user.</P>
+<P>The following paragraph applies ONLY to
+the case when the implementation supports trashing in the top
+directory, and a $topdir/.Trash exists and has passed the checks:</P>
+<P STYLE="margin-left: 2cm">If the directory exists and passes the
+checks, a subdirectory of the $topdir/.Trash directory is to be used
+as the user's trash directory for this partition/device. The name of
+this subdirectory is the numeric identifier of the current user
+($topdir/.Trash/$uid). When trashing a file, if this directory does
+not exist for the current user, the implementation MUST immediately
+create it, without any warnings or delays for the user.</P>
+<P>(2) If an $topdir/.Trash directory is
+absent, an $topdir/.Trash-$uid directory is to be used as the user's
+trash directory for this device/partition. $uid is the user's numeric
+identifier.</P>
+<P>The following paragraph applies ONLY to
+the case when the implementation supports trashing in the top
+directory, and a $topdir/.Trash does not exist or has not passed the
+checks:</P>
+<P STYLE="margin-left: 2cm">When trashing a file,
+if an $topdir/.Trash-$uid directory does not exist, the
+implementation MUST immediately create it, without any warnings or
+delays for the user.</P>
+<P>When trashing a file, if this directory
+does not exist for the current user, the implementation MUST
+immediately create it, without any warnings or delays for the user.</P>
+<P><B>Notes.</B> If an implementation provides trashing in top
+directories at all, it MUST support both (1) and (2).
+</P>
+<P>If an implementation does NOT provide such trashing, and does
+provide the user with some interface to view and/or undelete trashed
+files, it SHOULD make a &ldquo;best effort&rdquo; to show files
+trashed in top directories (by both methods) to the user, among other
+trashed files or in a clearly accessible separate way.</P>
+<P>When trashing a file, if the method (1) fails at any point &mdash;
+i.e. the $topdir/.Trash directory does not exist, or it fails the
+checks, or the system refuses to create an $uid directory in it &mdash;
+the implementation MUST, by default, fall back to method (2),
+described below. Except for the case when $topdir/.Trash fails the
+checks, the fallback must be immediate, without any warnings or
+delays. The implementation MAY, however, a way for the administrator
+to disable (2) completely.</P>
+<P>If both (1) and (2) fail (i.e. no
+$topdir/.Trash directory exists, and an attempt to create
+$topdir/.Trash-$uid fails), the implementation MUST either trash the
+file into the user's &ldquo;home trash&rdquo; or refuse to trash it.
+The choice between these options can be pre-determined, or it can
+depend on the particular situation (i.e. No trashing of very large
+files). However, if an implementation refuses to trash a file after a
+user action that generally causes trashing, it MUST clearly warn the
+user about this, and request confirmation for the action.</P>
+<P>For showing trashed files, implementations SHOULD support (1) and
+(2) at the same time (i.e. if both $topdir/.Trash/$uid and
+$topdir/.Trash-$uid are present, it should list trashed files from
+both of them).</P>
+<H2>Contents of a trash directory</H2>
+<P>The previous section has described the location of trash
+directories. This section concerns the contents of any trash
+directory (including the &ldquo;home trash&rdquo; directory). This
+trash directory will be named &ldquo;$trash&rdquo; here.</P>
+<P>A trash directory contains two subdirectories, named <B>info </B>and
+<B>files</B>.</P>
+<P>The <B>$trash/files</B> directory contains the files and
+directories that were trashed. When a file or directory is trashed,
+it MUST be moved into this directory<A CLASS="sdfootnoteanc" NAME="sdfootnote5anc" HREF="#sdfootnote5sym"><SUP>5</SUP></A>
+. The names of files in this directory are to be determined by the
+implementation; the only limitation is that they must be unique
+within the directory. <I>Even if a file with the same name and
+location gets trashed many times, each subsequent trashing must not
+overwrite a previous copy. </I>The access rights, access time,
+modification time and extended attributes (if any) for a
+file/directory in $trash/files SHOULD be the same as the
+file/directory had before getting trashed.</P>
+<P><B>IMPORTANT NOTE. While an implementation may choose to base
+filenames in the $trash/files directory on the original filenames,
+this is never to be taken for granted<A CLASS="sdfootnoteanc" NAME="sdfootnote6anc" HREF="#sdfootnote6sym"><SUP>6</SUP></A>.
+A filename in the $trash/files directory MUST NEVER be used to
+recover the original filename; use the info file (see below) for
+that. </B>(If an info file corresponding to a file/directory in
+$trash/files is not available, this is an emergency case, and MUST be
+clearly presented as such to the user or to the system
+administrator).
+</P>
+<P>The <B>$trash/info </B>directory contains an &ldquo;information
+file&rdquo; for every file and directory in $trash/files. This file
+MUST have exactly the same name as the file or directory in
+$trash/files, plus the extension &ldquo;.trashinfo&rdquo;<A CLASS="sdfootnoteanc" NAME="sdfootnote7anc" HREF="#sdfootnote7sym"><SUP>7</SUP></A>.
+</P>
+<P>The format of this file is similar to the format of a desktop
+entry file, as described in the <A HREF="http://www.freedesktop.org/Standards/desktop-entry-spec">Desktop
+Entry Specification</A> . Its first line must be [Trash Info].
+</P>
+<P>It also must have two lines that are
+key/value pairs as described in the Desktop Entry Specification:</P>
+<UL>
+ <LI><P>The key &ldquo;Path&rdquo;
+ contains the original location of the file/directory, as either an
+ absolute pathname (starting with the slash character &ldquo;/&rdquo;)
+ or a relative pathname (starting with any other character). A
+ relative pathname is to be from the directory in which the trash
+ directory resides (i.e., from $XDG_DATA_HOME for the &ldquo;home
+ trash&rdquo; directory); it MUST not include a &ldquo;..&rdquo;
+ directory, and for files not &ldquo;under&rdquo; that directory,
+ absolute pathnames must be used. The system SHOULD only support
+ absolute pathnames in the &ldquo;home trash&rdquo; directory, not in
+ the directories under $topdir.
+ </P>
+ <P>The value type for this key is
+ &ldquo;string&rdquo;; it should store the file name as the
+ sequence of bytes produced by the file system, with characters escaped
+ as in URLs (as defined by <A HREF="http://www.faqs.org/rfcs/rfc2396.html">RFC
+2396</A>, section 2).</P>
+ <LI><P>The key &ldquo;DeletionDate&rdquo; contains
+ the date and time when the file/directory was trashed. The date and
+ time are to be in the YYYY-MM-DDThh:mm:ss
+ format (see <A HREF="http://www.faqs.org/rfcs/rfc3339.html">RFC
+ 3339</A>). The time zone should be the user's (or
+ filesystem's) local time. The value type for this key is &ldquo;string&rdquo;.</P>
+</UL>
+<P>Example:</P>
+<PRE>[Trash Info]
+Path=foo/bar/meow.bow-wow
+DeletionDate=20040831T22:32:08</PRE><P>
+The implementation MUST ignore any other lines in this file, except
+the first line (must be [Trash Info]) and these two key/value pairs.
+If a string that starts with &ldquo;Path=&rdquo; or &ldquo;DeletionDate=&rdquo;
+occurs several times, the first occurence is to be used.<A CLASS="sdfootnoteanc" NAME="sdfootnote8anc" HREF="#sdfootnote8sym"><SUP>8</SUP></A></P>
+<P>Note that $trash/info has no subdirectories. For a directory in
+$trash/files, only an information file for its own name is needed.
+This is because, when a subdirectory gets trashed, it must be moved
+to $trash/files with its entire contents. The names of the files and
+directories within the directory MUST NOT be altered; the
+implementation also SHOULD preserve the access and modification time
+for them.</P>
+<P>When trashing a file or directory, the implementation MUST create
+the corresponding file in $trash/info first. When trashing a file or
+directory, the implementation MUST create thecorresponding file in
+$trash/info first. Moreover, it MUST try to do this in an atomic
+fashion, so that if two processes try trash files with the same
+filename this will result in two different trash files. On Unix-line
+systems this is done by generating a filename, and then opening with
+O_EXCL. If that succeeds the creation was atomic (at least on the
+same machine), if it fails you need to pick another filename.</P>
+<H2>Implementation notes</H2>
+<P>The names of the files/directories in $trash/info SHOULD be
+somehow related to original file names. This can help manual recovery
+in emergency cases (for example, if the corresponding info file is
+lost).</P>
+<P>When trashing a file or directory, the implementation should check
+whether the user has the necessary permissions to delete it, before
+starting the trashing operation itself.</P>
+<P>When copying, rather than moving, a file into the trash (i.e. When
+trashing to the &ldquo;home trash&rdquo; from a different partition),
+exact preservation of permissions might be impossible. Notably, a
+file.directory that was owned by another user will now be owned by
+this user (changing owners is usually only available to root). This
+should not cause the trashing operation to fail.</P>
+<P>In this same situation, setting the permissions should be done
+<I>after</I> writing the copied file, as they may make it
+unwriteable..</P>
+<P>A trashing operation might be refused because of insufficient
+permissions, even when the user does have the right to delete a file
+or directory. This may happen when the user has the right to delete a
+file/directory, but not to read it (or, in the case of a directory,
+to list it). In this case, the best solution is probably to warn the
+user, offering options to delete the file/directory or leave it
+alone.</P>
+<P>Automatic trash cleaning may, and probably eventually should, be
+implemented. But the implementation should be somehow known to the
+user.</P>
+<P>If a directory was trashed in its entirety, it is easiest to
+undelete it or remove it from the trash only in its entirety as well,
+not as separate files. The user might not have the permissions to
+delete some files in it even while he does have the permission to
+delete the directory!</P>
+<P><B>Important note on scope</B>. This specification currently does
+NOT define trashing on remote machines where multiuser permissions
+are implemented but the numeric user ID is not supported, like FTP
+sites and CIFS shares. In systems implementing this specification,
+trashing of files from such machines is to be done only to the user's
+home trash directory (if at all). A future version may address this
+limitation.</P>
+<H2>Administrativia</H2>
+<H3>Status of this document</H3>
+<P>This document is, at this moment, only a draft. It will hopefully
+become an official or semi-official FreeDesktop.org specification in
+the future.</P>
+<P>Date of first public distribution: August 30, 2004. This document
+will serve as evidence of prior art for any patent filed after this
+date.</P>
+<H3>Copyright and License</H3>
+<P>Copyright (C) 2004 Mikhail Ramendik , <A HREF="mailto:mr@ramendik.ru">mr@ramendik.ru</A>
+.
+</P>
+<P>The originators of the ideas that are described here did not
+object to this copyright. The author is ready to transfer the
+copyright to a standards body that would be committed to keeping this
+specification, or any successor to it, an open standard.</P>
+<P>The license: Use and distribute as you wish. If you make a
+modified version and redistribute it, (a) keep the name of the author
+and contributors somewhere, and (b) indicate that this is a modified
+version.</P>
+<P>Implementation under any license at all is explicitly allowed.</P>
+<H3>Location</H3>
+<P><A HREF="http://www.ramendik.ru/docs/trashspec.html">http://www.ramendik.ru/docs/trashspec.html</A>
+. If this document gets hosted by FreeDesktop.org, a link to the page
+will still be available at this location.</P>
+<P><A HREF="http://www.ramendik.ru/docs/trashspec.0.7.html">http://www.ramendik.ru/docs/trashspec.0.7.html</A>
+is the permanent location of this version.
+</P>
+<H3>Version history</H3>
+<P>0.1 &ldquo;First try&rdquo;, August 30, 2004. Initial draft.
+&ldquo;Implementation notes&rdquo; not written as yet.</P>
+<P>0.2 August 30, 2004. Updated with feedback by Alexander Larsson
+&lt;<A HREF="mailto:alexl@redhat.com">alexl@redhat.com</A>&gt; and by
+Dave Cridland &lt;<A HREF="mailto:dave@cridland.net">dave@cridland.net</A>&gt;</P>
+<P>0.3 September 8, 2004. Changed the name and location of the &ldquo;home
+trash&rdquo; directory, and introduced the generic term &ldquo;home
+trash&rdquo;. Changed the trash info file format to a .desktop-like
+one. Added directions on creation of info files and copying of
+trashed files. Changed user names to user ids. Added implementation
+notes. Added a copyright notice.</P>
+<P>0.4 September 9, 2004. Changed [Trash entry] to [Trash info] and
+fixed some typo's</P>
+<P>0.5 September 9, 2004. Changed [Trash info] to [Trash Info]</P>
+<P>0.6 October 8, 2004. Corrections by Alexander Larsson
+&lt;<A HREF="mailto:alexl@redhat.com">alexl@redhat.com</A>&gt; . Also
+added &ldquo;note on scope&rdquo;. Cleaned up HTML. Added a link to this
+document on the freedesktop.org standards page</P>
+<P>0.7 April 12, 2005. Added URL-style encoding for the name of the deleted file,
+as implemented in KDE 3.4</P>
+<P><BR><BR>
+</P>
+<DIV ID="sdfootnote1">
+ <P CLASS="sdfootnote" STYLE="margin-bottom: 0.5cm"><A CLASS="sdfootnotesym" NAME="sdfootnote1sym" HREF="#sdfootnote1anc">1</A>However,
+ developers of preloaded libraries should somehow work around the
+ case when a desktop environment also supporting the Trash
+ specification is run on top of them. &ldquo;Double trashing&rdquo;
+ and &ldquo;trashing of the trash&rdquo; should be avoided.</P>
+</DIV>
+<DIV ID="sdfootnote2">
+ <P CLASS="sdfootnote" STYLE="margin-bottom: 0.5cm"><A CLASS="sdfootnotesym" NAME="sdfootnote2sym" HREF="#sdfootnote2anc">2</A>To
+ be more precise, for every user who can use the trash facility. In
+ general, all human users, and possibly some &ldquo;robotic&rdquo;
+ ones like ftp, should be able to use the trash facility.
+ </P>
+</DIV>
+<DIV ID="sdfootnote3">
+ <P CLASS="sdfootnote" STYLE="margin-bottom: 0.5cm"><A CLASS="sdfootnotesym" NAME="sdfootnote3sym" HREF="#sdfootnote3anc">3</A>Note
+ the dot in the beginning, and for case sensitive file systems, note
+ the case.</P>
+</DIV>
+<DIV ID="sdfootnote4">
+ <P CLASS="sdfootnote" STYLE="margin-bottom: 0.5cm"><A CLASS="sdfootnotesym" NAME="sdfootnote4sym" HREF="#sdfootnote4anc">4</A>To
+ be more precise, from a partition/device different from the one on
+ which $XDG_DATA_HOME resides.
+ </P>
+</DIV>
+<DIV ID="sdfootnote5">
+ <P CLASS="sdfootnote" STYLE="margin-bottom: 0.5cm"><A CLASS="sdfootnotesym" NAME="sdfootnote5sym" HREF="#sdfootnote5anc">5</A>&ldquo;$trash/files/&rdquo;,
+ <B>not </B>into &ldquo;$trash/&rdquo; as in many existing
+ implementations!</P>
+</DIV>
+<DIV ID="sdfootnote6">
+ <P CLASS="sdfootnote" STYLE="margin-bottom: 0.5cm"><A CLASS="sdfootnotesym" NAME="sdfootnote6sym" HREF="#sdfootnote6anc">6</A>At
+ least because another implementation might trash files into the same
+ trash directory</P>
+</DIV>
+<DIV ID="sdfootnote7">
+ <P CLASS="sdfootnote" STYLE="margin-bottom: 0.5cm"><A CLASS="sdfootnotesym" NAME="sdfootnote7sym" HREF="#sdfootnote7anc">7</A>For
+ example, if the file in $trash/files is named foo.bar , the
+ corresponding file in $trash/info must be named foo.bar.trashinfo</P>
+</DIV>
+<DIV ID="sdfootnote8">
+ <P CLASS="sdfootnote" STYLE="margin-bottom: 0.5cm"><A CLASS="sdfootnotesym" NAME="sdfootnote8sym" HREF="#sdfootnote8anc">8</A>This
+ provides for future extension</P>
+</DIV>
+</BODY>
+</HTML>
diff --git a/web-export/README b/web-export/README
new file mode 100644
index 0000000..94cbdbc
--- /dev/null
+++ b/web-export/README
@@ -0,0 +1,9 @@
+This directory contains the files used to export the specifications on
+ http://specifications.freedesktop.org/
+
+It's mostly two files:
+
+ - specs.idx: information on where to fetch specific versions of different
+ specifications (usually CVS or git)
+
+ - update.py: hacky script that uses specs.idx and creates html pages
diff --git a/web-export/specs.idx b/web-export/specs.idx
new file mode 100644
index 0000000..4d0dde1
--- /dev/null
+++ b/web-export/specs.idx
@@ -0,0 +1,91 @@
+git:xdg/xdg-specs:autostart/autostart-spec.xml master 0.5 autostart-spec
+
+git:xdg/xdg-specs:basedir/basedir-spec.xml master 0.8 basedir-spec
+git:xdg/xdg-specs:basedir/basedir-spec.xml 5c950648d4e3b186bda82c0c27ef089ee6747cbd 0.7 basedir-spec
+git:xdg/xdg-specs:basedir/basedir-spec.xml 2c096d0c74244bd573027b217be3a69335a923f4 0.6 basedir-spec
+# Invalid docbook:
+#git:xdg/xdg-specs:basedir/basedir-spec.xml c8475bef1d1f84afcc89c710b6b5b214f11a670f 0.5 basedir-spec
+
+icccm-extensions/selections/clipboards.txt 1.2 0.1 clipboards-spec
+icccm-extensions/selections/clipboard-extensions.txt 1.1 0.1 clipboard-extensions-spec
+
+git:dbus/dbus:doc/introspect.dtd 7652304bff969afb3969603149bb385efe861fe8 1.0 dbus
+
+git:xdg/xdg-specs:desktop-entry/desktop-entry-spec.xml master 1.1 desktop-entry-spec
+git:xdg/xdg-specs:desktop-entry/desktop-entry-spec.xml e1775a202af19adf9c73e6954293e2e95f8f13a0 1.0 desktop-entry-spec
+git:xdg/xdg-specs:desktop-entry/desktop-entry-spec.xml 7d249792235d0f3b69f833f89bef545a367f0b6b 0.9.8 desktop-entry-spec
+git:xdg/xdg-specs:desktop-entry/desktop-entry-spec.xml 3e675b52041139085cc280dcdf2562e38cba8a31 0.9.7 desktop-entry-spec
+git:xdg/xdg-specs:desktop-entry/desktop-entry-spec.xml af1b346daa90b9cb88ec9e3b6d3ebd05942a73d7 0.9.6 desktop-entry-spec
+git:xdg/xdg-specs:desktop-entry/desktop-entry-spec.xml 6855384d021d88557f80674798c584c15b547f36 0.9.5 desktop-entry-spec
+# Invalid docbook:
+#git:xdg/xdg-specs:desktop-entry/desktop-entry-spec.xml b818b36ff0ee3c7f35df7b38db15a18b434f6375 0.9.4 desktop-entry-spec
+git:xdg/xdg-specs:desktop-entry/desktop-entry-spec.sgml e7d2b4770815f8d8dcca934d3520d3fe55f86b09 0.9.3 desktop-entry-spec
+git:xdg/xdg-specs:desktop-entry/desktop-entry-spec.sgml 9c493d5fca5fcceca4e1bc0b2b2ba8a13c5eb12e 0.9.2 desktop-entry-spec
+
+git:xdg/default-icon-theme:spec/icon-naming-spec.xml master 0.8.90 icon-naming-spec
+git:xdg/default-icon-theme:spec/icon-naming-spec.xml 77bc8ba641ca24bf2e55292f7d31926d60295e5c 0.8 icon-naming-spec
+git:xdg/default-icon-theme:spec/icon-naming-spec.xml aba09c448ecc055f93a959a54497ef02be4ceab2 0.7 icon-naming-spec
+git:xdg/default-icon-theme:spec/icon-naming-spec.xml 678139104048733b680c331d28ee09f6f16f386c 0.6 icon-naming-spec
+git:xdg/default-icon-theme:spec/icon-naming-spec.xml 1dc55c9db340a7d0a9ac84e1e42735df311cdad5 0.4 icon-naming-spec
+# Invalid docbook:
+#git:xdg/default-icon-theme:spec/icon-naming-spec.xml db9bb955c0cf20322b149676b908ecd69929d2f1 0.3 icon-naming-spec
+
+git:xdg/default-icon-theme:spec/icon-theme-spec.xml e54ef4b65ae0eab72ffe23e18a470072e1719471 0.12 icon-theme-spec
+git:xdg/default-icon-theme:spec/icon-theme-spec.xml f09150e444382a18d147ef45afa9310e2e27c713 0.11 icon-theme-spec
+git:xdg/default-icon-theme:spec/icon-theme-spec.xml 37f3118e81954b2171a333614901a0b74c295edc 0.10 icon-theme-spec
+git:xdg/default-icon-theme:spec/icon-theme-spec.xml 400de5cb3e4f8046763ce816fb93a9ec287e6e39 0.9 icon-theme-spec
+git:xdg/default-icon-theme:spec/icon-theme-spec.xml 61ce5bd0f09d5026b32f015f7f8d209524b85cf1 0.8 icon-theme-spec
+git:xdg/default-icon-theme:spec/icon-theme-spec.xml bd6077c5fc235d830d325a977770150bd2d71136 0.7 icon-theme-spec
+git:xdg/default-icon-theme:spec/icon-theme-spec.xml b1a891e95eb4f28da4e43fb6cb7a662bc0431529 0.6 icon-theme-spec
+
+git:xdg/xdg-specs:menu/menu-spec.xml master 1.1 menu-spec
+git:xdg/xdg-specs:menu/menu-spec.xml 647153fb87e5f642a7b7a3fdcec90961f36e89ab 1.0 menu-spec
+git:xdg/xdg-specs:menu/menu-spec.xml a61b06aa1aee4743f50024b0695795fe9568d929 0.92 menu-spec
+git:xdg/xdg-specs:menu/menu-spec.xml 2403355e486ac6bc972fd150d38e3c44d086960a 0.91 menu-spec
+git:xdg/xdg-specs:menu/menu-spec.xml e1b6bf680dc4d021e7f2122841f812975a31985c 0.9 menu-spec
+# Invalid docbook:
+#git:xdg/xdg-specs:menu/menu-spec.xml 7d616706bba168427214848c6fbb77da5a30e04d 0.8 menu-spec
+#git:xdg/xdg-specs:menu/menu-spec.xml 28a4c60cb40502c01417bfdbd8a2e093eb4af2d9 0.7 menu-spec
+#git:xdg/xdg-specs:menu/menu-spec.xml 9f969d0e6625b2da24fb971ebd3cf7f913a1c5f1 0.6 menu-spec
+#git:xdg/xdg-specs:menu/menu-spec.xml c15b9df59efac72ea80fb715bf2a9d7f7afd1bba 0.5 menu-spec
+#git:xdg/xdg-specs:menu/menu-spec.xml c8475bef1d1f84afcc89c710b6b5b214f11a670f 0.4 menu-spec
+git:xdg/xdg-specs:menu/menu.dtd 2403355e486ac6bc972fd150d38e3c44d086960a 0.91 menu-spec
+
+recent-files/recent-file-spec/recent-file-spec.xml 1.1 0.2 recent-file-spec
+
+git:xdg/shared-mime-info:shared-mime-info-spec.xml 2f909cff817a3f08d12321769173cac2de0da1b4 0.20 shared-mime-info-spec
+git:xdg/shared-mime-info:shared-mime-info-spec.xml 30349a8810143c1f808a74c96bceba87d48e8364 0.19 shared-mime-info-spec
+git:xdg/shared-mime-info:shared-mime-info-spec.xml 1bac8224c7439452a85693dc1a25f64d3a467e7d 0.18 shared-mime-info-spec
+git:xdg/shared-mime-info:shared-mime-info-spec.xml 83c091bbb4d32231b308864d227ada6106bc59a5 0.17 shared-mime-info-spec
+git:xdg/shared-mime-info:shared-mime-info-spec.xml 6c474418318c6c192abfe0f17f85209c65bfa1cf 0.16 shared-mime-info-spec
+git:xdg/shared-mime-info:shared-mime-info-spec.xml 7fab0df72e558a1d651e2557d52ac37b8d5020e9 0.15 shared-mime-info-spec
+git:xdg/shared-mime-info:shared-mime-info-spec.xml 0239582644c9edffa7edd4562ccb967d14d63e73 0.14 shared-mime-info-spec
+git:xdg/shared-mime-info:shared-mime-info-spec.xml f364fdbba5bb7fe8b69e0294aa353845515b191e 0.13 shared-mime-info-spec
+git:xdg/shared-mime-info:shared-mime-info-spec.xml ad3203089d3d029b5c18ebd4baa6f7991dd9bb22 0.12 shared-mime-info-spec
+git:xdg/shared-mime-info:shared-mime-info-spec.xml 92cca5e4cec157f252fd77b5ce23853974603906 0.11 shared-mime-info-spec
+git:xdg/shared-mime-info:shared-mime-info-spec.xml dc2724f1d1b010a80b269ef21a1e613f55170777 0.10 shared-mime-info-spec
+git:xdg/shared-mime-info:shared-mime-info-spec.xml e1359233cf0544509854178e8591ed39b5ccbcdf 0.9 shared-mime-info-spec
+git:xdg/shared-mime-info:shared-mime-info-spec.xml 7b25e407e6dc6cda94c2fb1f83b4ac4956a22fbf 0.8 shared-mime-info-spec
+git:xdg/shared-mime-info:shared-mime-info-spec.xml d08ddd99abf32cbf770fecc43f950f4a8c7a4dc0 0.7 shared-mime-info-spec
+git:xdg/shared-mime-info:shared-mime-info-spec.xml 1dd7f6ce4610378b970674cfef61e1bda2389ba5 0.6 shared-mime-info-spec
+git:xdg/shared-mime-info:shared-mime-info-spec.xml 2e3dfe6117ec97da5493669a05459fdcac914e1c 0.5 shared-mime-info-spec
+git:xdg/shared-mime-info:shared-mime-info-spec.xml bfd730e142344783603ac4cdf0467ccf2e2ff1fd 0.4 shared-mime-info-spec
+git:xdg/shared-mime-info:shared-mime-info-spec.xml f6fa8bf89055da53e9323aca59d5337729178ade 0.3 shared-mime-info-spec
+git:xdg/shared-mime-info:shared-mime-info-spec.xml e235408efaeec39b5be22d4f1d4218da64247d74 0.2 shared-mime-info-spec
+
+git:startup-notification:doc/startup-notification.txt master 0.2 startup-notification-spec
+git:startup-notification:doc/startup-notification.txt e1d689b36635ed632cbb91cf3104f84bd0b59bfd 0.1 startup-notification-spec
+
+git:xdg/xdg-specs:systemtray/systemtray-spec.xml cbe62e957475dba73d9d830dd755c76f8d3abd23 0.3 systemtray-spec
+git:xdg/xdg-specs:systemtray/systemtray-spec.xml 87fb46a8631a8b509bb1b8026b88c7059f869ffd 0.2 systemtray-spec
+git:xdg/xdg-specs:systemtray/systemtray-spec.xml c8475bef1d1f84afcc89c710b6b5b214f11a670f 0.1 systemtray-spec
+
+git:xdg/xdg-specs:wm-spec/wm-spec.xml master 1.5 wm-spec
+git:xdg/xdg-specs:wm-spec/wm-spec.xml a95af55a102b0814e81095bda46a691068009c3c 1.4 wm-spec
+git:xdg/xdg-specs:wm-spec/wm-spec.xml 2eafacb23421d25147533a66a9eb8f0f4650a5e4 1.3 wm-spec
+git:xdg/xdg-specs:wm-spec/wm-spec.sgml d10d819f39c4c7e878288192f14248536d9d4b96 1.2 wm-spec
+git:xdg/xdg-specs:wm-spec/wm-spec.sgml a6e9a0a88112e5f6ac7099530e5c732c7ec6fb7f 1.1 wm-spec
+# Invalid docbook:
+#git:xdg/xdg-specs:wm-spec/wm-spec.sgml b58352845cebc1d3b5754215a03ed047d1b28b11 1.0 wm-spec
+
+xembed/xembed/spec/xembed-spec.xml 1.2 0.5 xembed-spec
diff --git a/web-export/update.py b/web-export/update.py
new file mode 100755
index 0000000..cd54f28
--- /dev/null
+++ b/web-export/update.py
@@ -0,0 +1,274 @@
+#!/usr/bin/env python
+
+# Dependencies to run this:
+# - xmlto in $PATH
+
+# FIXME:
+# - correctly handle all exceptions
+# - copy dtd files where they should be
+# - new structure for website:
+# specs.fd.o/index.html -- general index
+# specs.fd.o/desktop-entry/index.html -- index of all versions of desktop entry, with all formats
+# specs.fd.o/desktop-entry/1.0/desktop-entry-spec.xml -- docbook version of the spec 1.0
+# specs.fd.o/desktop-entry/1.0/index.html -- one-page html version of the spec 1.0
+# specs.fd.o/desktop-entry/1.0/split/ -- multiple-page html version of the spec 1.0
+# specs.fd.o/desktop-entry/latest/ -- link to directory containing latest version of the spec
+
+import os
+import sys
+
+import errno
+
+import StringIO
+import hashlib
+import shutil
+import subprocess
+import urllib
+import urllib2
+import urlparse
+
+DEVELOPMENT = False
+
+CVSWEB = 'http://cvs.freedesktop.org'
+GITWEB = 'http://cgit.freedesktop.org'
+HASH = 'md5'
+
+
+def safe_mkdir(dir):
+ if not dir:
+ return
+
+ try:
+ os.mkdir(dir)
+ except OSError, e:
+ if e.errno != errno.EEXIST:
+ raise e
+
+
+def get_hash_from_fd(fd, algo = HASH, read_blocks = 1024):
+ if algo not in [ 'md5' ]:
+ raise Exception('Internal error: hash algorithm \'%s\' not planned in code.' % algo)
+
+ hash = hashlib.new(algo)
+ while True:
+ data = fd.read(read_blocks)
+ if not data:
+ break
+ hash.update(data)
+ return hash.digest()
+
+
+def get_hash_from_url(url, algo = HASH):
+ fd = urllib2.urlopen(url, None)
+ digest = get_hash_from_fd(fd, algo)
+ fd.close()
+ return digest
+
+
+def get_hash_from_path(path, algo = HASH):
+ fd = open(path, 'rb')
+ digest = get_hash_from_fd(fd, algo, read_blocks = 32768)
+ fd.close()
+ return digest
+
+
+def get_hash_from_data(data, algo = HASH):
+ fd = StringIO.StringIO(data)
+ digest = get_hash_from_fd(fd, algo, read_blocks = 32768)
+ fd.close()
+ return digest
+
+
+class VcsObject:
+ def __init__(self, vcs, repo, file, revision = None):
+ self.vcs = vcs
+ self.repo = repo
+ self.file = file
+ self.revision = revision
+ self.data = None
+
+ def get_url(self):
+ query = {}
+ if self.vcs == 'git':
+ baseurl = GITWEB
+ path = '/'.join((self.repo, 'plain', self.file))
+ if self.revision:
+ query['id'] = self.revision
+ elif self.vcs == 'cvs':
+ baseurl = CVSWEB
+ path = self.file
+ if self.revision:
+ query['rev'] = self.revision
+ else:
+ raise Exception('Unknown VCS: %s' % self.vcs)
+
+ (scheme, netloc, basepath) = urlparse.urlsplit(baseurl)[0:3]
+ full_path = '/'.join((basepath, path))
+
+ query_str = urllib.urlencode(query)
+ return urlparse.urlunsplit((scheme, netloc, full_path, query_str, ''))
+
+ def fetch(self):
+ if self.data:
+ return
+
+ url = self.get_url()
+ fd = urllib2.urlopen(url, None)
+ self.data = fd.read()
+ fd.close()
+
+ def get_hash(self):
+ self.fetch()
+ return get_hash_from_data(self.data)
+
+
+class SpecObject():
+ def __init__(self, vcs, spec_dir, version):
+ self.vcs = vcs
+ self.spec_dir = spec_dir
+ self.version = version
+
+ basename = os.path.basename(self.vcs.file)
+ (self.basename_no_ext, self.ext) = os.path.splitext(basename)
+
+ self.filename = '%s-%s%s' % (self.basename_no_ext, self.version, self.ext)
+
+ if self.ext not in ['.xml', '.sgml', '.txt', '.dtd']:
+ raise Exception('Format \'%s\' not supported for %s' % (self.ext, self.vcs.get_url()))
+
+ self.downloaded = False
+ self.one_chunk = False
+ self.multiple_chunks = False
+
+ def download(self):
+ safe_mkdir(self.spec_dir)
+ path = os.path.join(self.spec_dir, self.filename)
+
+ if os.path.exists(path):
+ current_hash = get_hash_from_path(path)
+ vcs_hash = self.vcs.get_hash()
+ if current_hash == vcs_hash:
+ return
+
+ self.vcs.fetch()
+ fd = open(path, 'wb')
+ fd.write(self.vcs.data)
+ fd.close()
+
+ self.downloaded = True
+
+ def htmlize(self, force = False):
+ if not self.downloaded and not force:
+ return
+
+ path = os.path.join(self.spec_dir, self.filename)
+ (path_no_ext, ext) = os.path.splitext(path)
+
+ # One-chunk HTML
+ html_path = '%s%s' % (path_no_ext, '.html')
+ if os.path.exists(html_path):
+ os.unlink(html_path)
+
+ # Multiple chunks
+ html_dir = os.path.join(self.spec_dir, self.version)
+ if os.path.exists(html_dir):
+ shutil.rmtree(html_dir)
+
+ one_chunk_command = None
+ multiple_chunks_command = None
+
+ if self.ext == '.xml':
+ one_chunk_command = ['xmlto', '-o', self.spec_dir, 'html-nochunks', path]
+ multiple_chunks_command = ['xmlto', '-o', html_dir, 'html', path]
+ elif self.ext == '.sgml':
+ one_chunk_command = ['docbook2html', '-o', self.spec_dir, '--nochunks', path]
+ multiple_chunks_command = ['docbook2html', '-o', html_dir, path]
+
+ if one_chunk_command:
+ retcode = subprocess.call(one_chunk_command)
+ if retcode != 0:
+ raise Exception('Cannot convert \'%s\' to HTML.' % path)
+ self.one_chunk = True
+
+ if multiple_chunks_command:
+ safe_mkdir(html_dir)
+ retcode = subprocess.call(multiple_chunks_command)
+ if retcode != 0:
+ raise Exception('Cannot convert \'%s\' to multiple-chunks HTML.' % path)
+ self.multiple_chunks = True
+
+ def latestize(self):
+ filename_latest = '%s-latest%s' % (self.basename_no_ext, self.ext)
+
+ path_latest = os.path.join(self.spec_dir, filename_latest)
+ if os.path.exists(path_latest):
+ os.unlink(path_latest)
+ os.symlink(self.filename, path_latest)
+
+ if self.ext in ['.xml', '.sgml']:
+ # One-chunk HTML
+ html_path_latest = os.path.join(self.spec_dir, '%s-latest%s' % (self.basename_no_ext, '.html'))
+ if os.path.exists(html_path_latest):
+ os.unlink(html_path_latest)
+
+ (filename_no_ext, ext) = os.path.splitext(self.filename)
+ html_filename = '%s%s' % (filename_no_ext, '.html')
+ html_path = os.path.join(self.spec_dir, html_filename)
+ if os.path.exists(html_path):
+ os.symlink(html_filename, html_path_latest)
+
+ # Multiple chunks
+ html_dir_latest = os.path.join(self.spec_dir, 'latest')
+ if os.path.exists(html_dir_latest):
+ os.unlink(html_dir_latest)
+
+ html_dir = os.path.join(self.spec_dir, self.version)
+ if os.path.exists(html_dir):
+ os.symlink(self.version, html_dir_latest)
+
+
+SCRIPT = VcsObject('git', 'xdg/xdg-specs', 'web-export/update.py')
+SPECS_INDEX = VcsObject('git', 'xdg/xdg-specs', 'web-export/specs.idx')
+
+
+def is_up_to_date():
+ current_hash = get_hash_from_path(__file__)
+ vcs_hash = SCRIPT.get_hash()
+
+ return current_hash == vcs_hash
+
+
+if not DEVELOPMENT:
+ if not is_up_to_date():
+ print >>sys.stderr, 'Script is not up-to-date, please download %s' % SCRIPT.get_url()
+ sys.exit(1)
+
+ SPECS_INDEX.fetch()
+ lines = SPECS_INDEX.data.split('\n')
+else:
+ lines = open('specs.idx').readlines()
+
+
+latests = []
+
+for line in lines:
+ line = line.strip()
+ if not line or line.startswith('#'):
+ continue
+
+ (data, revision, version, path) = line.split()
+ if data.startswith("git:"):
+ git_data = data.split(":")
+ vcs = VcsObject('git', git_data[1], git_data[2], revision)
+ else:
+ vcs = VcsObject('cvs', None, data, revision)
+
+ spec = SpecObject(vcs, path, version)
+
+ spec.download()
+ spec.htmlize()
+
+ # Create latest links if it's the first time we see this spec
+ if (spec.spec_dir, spec.basename_no_ext) not in latests:
+ latests.append((spec.spec_dir, spec.basename_no_ext))
+ spec.latestize()
diff --git a/wm-spec/Makefile b/wm-spec/Makefile
new file mode 100644
index 0000000..d7db04b
--- /dev/null
+++ b/wm-spec/Makefile
@@ -0,0 +1,7 @@
+all: wm-spec.html
+
+%.html: %.xml
+ xmlto html-nochunks $<
+
+clean:
+ rm -f wm-spec.html
diff --git a/wm-spec/wm-spec.xml b/wm-spec/wm-spec.xml
new file mode 100644
index 0000000..87dc02d
--- /dev/null
+++ b/wm-spec/wm-spec.xml
@@ -0,0 +1,2760 @@
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+<!ENTITY version "Draft version 1.5.draft-1">
+<!ENTITY date "Fri November 29, 2011">
+]>
+<article id="index">
+<articleinfo>
+ <authorgroup>
+ <corpauthor>
+ <ulink url="http://www.freedesktop.org">X Desktop Group</ulink>
+ </corpauthor>
+ </authorgroup>
+<title>Extended Window Manager Hints</title>
+<date>&date;</date>
+<releaseinfo>&version;</releaseinfo>
+</articleinfo>
+<sect1>
+ <title>Introduction</title>
+ <sect2>
+ <title>Version</title>
+ <para>
+This is &version; of the Extended Window Manager Hints (EWMH) spec, updated
+&date;. The canonical home for this document is <ulink
+url="http://www.freedesktop.org/standards/wm-spec/">http://www.freedesktop.org</ulink>,
+which also contains directions for reporting bugs or contributing to future
+versions.
+ </para>
+ </sect2>
+ <sect2>
+ <title>What is this spec?</title>
+ <para>
+This spec defines interactions between window managers, compositing
+managers, applications, and the utilities that form part of a desktop
+environment. It builds on the Inter-Client Communication Conventions
+Manual <citation><link linkend="ICCCM">ICCCM</link></citation>, which
+defines window manager interactions at a lower level. The ICCCM does
+not provide ways to implement many features that modern desktop users
+expect. The GNOME and KDE desktop projects originally developed their
+own extensions to the ICCCM to support these features; this spec
+replaces those custom extensions with a standardized set of ICCCM
+additions that any desktop environment can adopt.
+ </para>
+ </sect2>
+ <sect2>
+ <title>Language used in this specification</title>
+ <para>
+The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
+"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
+interpreted as described in RFC 2119.
+ </para>
+ <para>
+The key words "Window Manager" refer to a window manager which is adopting this
+specification. "Pager" refers to desktop utility applications, including
+pagers and taskbars. "Application" refers to other clients. "Clients" refers
+to Pagers and Applications ie. all X clients, except for the Window Manager.
+ </para>
+ </sect2>
+ <sect2>
+ <title>Prerequisites for adoption of this specification</title>
+ <para>
+Window Managers and Clients which aim to fulfill this specification MUST adhere
+to the ICCCM on which this specification builds. If this specification
+explicitly modifies the ICCCM Window Managers and Clients MUST fulfill these
+modifications.
+ </para>
+ </sect2>
+</sect1>
+<sect1>
+<title>Non-ICCCM features</title>
+<para>There is a number of window management features or behaviors which are
+not specified in the ICCCM, but are commonly met in modern window managers and desktop environments.</para>
+<sect2>
+<title>Additional States</title>
+<para>The ICCCM allows window managers to implement additional window states, which will
+appear to clients as substates of NormalState and IconicState. Two
+commonly met examples are Maximized and Shaded. A window manager may implement these
+as proper substates of NormalState and IconicState, or it may treat them
+as independent flags, allowing e.g. a maximized window to be iconified
+and to re-appear as maximized upon de-iconification.</para>
+<sect3>
+<title>Maximization</title>
+<para>Maximization is a very old feature of window managers. There was even a ZoomedState
+in early ICCCM drafts. Maximizing a window should give it as much of the
+screen area as possible (this may not be the full screen area, but only
+a smaller 'workarea', since the window manager may have reserved certain areas for other
+windows). A window manager is expected to remember the geometry of a maximized window
+and restore it upon de-maximization. Modern window managers typically allow separate
+horizontal and vertical maximization.</para>
+<para>With the introduction of the Xinerama extension in X11 R6.4, maximization
+has become more involved. Xinerama allows a screen to span multiple
+monitors in a freely configurable geometry. In such a setting, maximizing
+a window would ideally not grow it to fill the whole screen, but only the
+monitor it is shown on. There are of course borderline cases for windows
+crossing monitor boundaries, and 'real' maximization to the full screen may
+sometimes be useful.</para>
+</sect3>
+<sect3>
+<title>Shading</title>
+<para>Some desktop environments offer shading (also known as rollup) as an alternative to
+iconification. A shaded window typically shows only the titlebar, the client
+window is hidden, thus shading is not useful for windows which are not
+decorated with a titlebar.</para>
+</sect3>
+</sect2>
+<sect2>
+<title>Modality</title>
+<para>The WM_TRANSIENT_FOR hint of the ICCCM allows clients to specify that a
+toplevel window may be closed before the client finishes. A typical example
+of a transient window is a dialog. Some dialogs can be open for a long time,
+while the user continues to work in the main window. Other dialogs have to be
+closed before the user can continue to work in the main window. This property
+is called modality. While clients can implement modal windows in an ICCCM
+compliant way using the globally active input model, some window managers offer support
+for handling modality.
+</para>
+</sect2>
+<sect2 id="largedesks">
+<title>Large Desktops</title>
+<para>The window manager may offer to arrange the managed windows on a desktop that is
+larger than the root window. The screen functions as a viewport on this large
+desktop. Different policies regarding the positioning of the viewport on the
+desktop can be implemented: The window manager may only allow the viewport
+position to change in increments of the screen size (paging) or it may allow
+arbitrary positions (scrolling).</para>
+<para>To fulfill the ICCCM principle that clients should behave the same
+regardless whether a window manager is running or not, window managers which
+implement large desktops must interpret all client-provided geometries with
+respect to the current viewport.</para>
+<sect3 id="largedesksimpl">
+<title>Implementation note</title>
+<para>There are two options for implementing a large desktop: The first is to
+keep the managed windows (or, if reparenting, their frames) as children
+of the root window. Moving the viewport is achieved by moving all managed
+windows in the opposite direction.</para>
+<para>The second alternative is to reparent all managed windows to a dedicated
+large window (somewhat inappropriately called a 'virtual root'). Moving
+the viewport is then achieved by moving the virtual root in the opposite
+direction.</para>
+<para>Both alternatives are completely ICCCM compliant, although the second one
+may be somewhat problematic for clients trying to figure out the window manager decorations
+around their toplevel windows and for clients trying to draw background
+images on the root window.</para>
+</sect3>
+</sect2>
+<sect2>
+<title>Sticky windows</title>
+<para>A window manager which implements a large desktop typically offers a way for the user
+to make certain windows 'stick to the glass', i.e. these windows will stay
+at the same position on the screen when the viewport is moved.</para>
+</sect2>
+<sect2>
+<title>Virtual Desktops</title>
+<para>Most X servers have only a single screen. The window manager may virtualize this
+resource and offer multiple so-called 'virtual desktops', of which only one
+can be shown on the screen at a time. There is some variation among the
+features of virtual desktop implementations. There may be a fixed number
+of desktops, or new ones may be created dynamically. The size of the desktops
+may be fixed or variable. If the desktops are larger than the root window,
+their viewports (see <xref linkend="largedesks"/>) may be independent or forced
+to be at the same position.</para>
+<para>A window manager which implements virtual desktops generally offers a way for the user
+to move clients between desktops. Clients may be allowed to occupy more than
+one desktop simultaneously.</para>
+<sect3>
+<title>Implementation note</title>
+<para>There are at least two options for implementing virtual desktops.
+The first is to use multiple virtual roots (see <xref linkend="largedesksimpl"/>) and change the current
+desktop by manipulating the stacking order of the virtual roots. This is
+completely ICCCM compliant, but has the issues outlined in <xref linkend="largedesksimpl"/></para>
+<para>The second option is to keep all managed windows as children of the root
+window and unmap the frames of those which are not on the current
+desktop. Unmapped windows should be placed in IconicState, according to
+the ICCCM. Windows which are actually iconified or minimized
+should have the _NET_WM_STATE_HIDDEN property set, to
+communicate to pagers that the window should not be represented as
+"onscreen."
+</para>
+</sect3>
+</sect2>
+<sect2>
+<title>Pagers</title>
+<para>A pager offers a different UI for window management tasks. It shows a
+miniature view of the desktop(s) representing managed windows by small
+rectangles and allows the user to initiate various window manager actions by manipulating
+these representations. Typically offered actions are activation (see <xref linkend="activation"/>),
+moving, restacking, iconification, maximization and closing. On a large
+desktop, the pager may offer a way to move the viewport. On virtual desktops,
+the pager may offer ways to move windows between desktops and to change the
+current desktop.</para>
+</sect2>
+<sect2>
+<title>Taskbars</title>
+<para>A taskbar offers another UI for window management tasks. It typically
+represents client windows as a list of buttons labelled with the window
+titles and possibly icons. Pressing a button initiates a window manager action on the
+represented window, typical actions being activation and iconification.
+In environments with a taskbar, icons are often considered inappropriate,
+since the iconified windows are already represented in the taskbar.</para>
+</sect2>
+<sect2 id="activation">
+<title>Activation</title>
+<para>In the X world, activating a window means to give it the input focus.
+This may not be possible if the window is unmapped, because it is on a
+different desktop. Thus, activating a window may involve additional steps
+like moving it to the current desktop (or changing to the desktop the window
+is on), deiconifying it or raising it.</para>
+</sect2>
+<sect2>
+<title>Animated iconification</title>
+<para>Some window managers display some form of animation when (de-)iconifying a window.
+This may be a line drawing connecting the corners of the window with
+the corners of the icon or the window may be opaquely moved and resized
+on some trajectory joining the window location and the icon location.</para>
+</sect2>
+<sect2>
+<title>Window-in-window MDI</title>
+<para>Window-in-window MDI is a multiple document interface known from MS
+Windows platforms. Programs employing it have a single top-level window
+which contains a workspace which contains the subwindows for the open
+documents. These subwindows are decorated with window manager frames and can be
+manipulated within their parent window just like ordinary top-level
+windows on the root window.</para>
+</sect2>
+<sect2>
+ <title>Override-redirect windows</title>
+ <para>
+Override-redirect windows are ignored by traditional window managers,
+but not by compositing managers. Compositing managers are responsible
+for painting all windows to the screen, including override-redirect
+windows.
+</para>
+ <para>
+To enable compositing managers to decorate override-redirect window
+properly, for example by making them translucent or by changing the
+shape of the windows, this spec allows clients to set properties on
+override-redirect windows indicating the function of the windows.
+</para>
+</sect2>
+<sect2>
+<title>Layered stacking order</title>
+<para>
+Some window managers keep the toplevel windows not in a single linear stack,
+but subdivide the stack into several layers. There is a lot of variation
+among the features of layered stacking order implementations. The number of
+layers may or may not be fixed. The layer of a toplevel window may be explicit
+and directly modifiable or derived from other properties of the window, e.g.
+the <emphasis>type</emphasis> of the window. The stacking order may or may not
+be strict, i.e. not allow the user to raise or lower windows beyond their
+layer.
+</para>
+</sect2>
+<sect2>
+<title>Scope of this spec</title>
+<para>This spec tries to address the following issues:</para>
+<itemizedlist>
+<listitem><para>Allow clients to influence their initial state with respect
+to maximization, shading, stickiness, desktop, stacking order.</para></listitem>
+<listitem><para>Improve the window manager's ability to vary window
+decorations and maintain the stacking order by allowing clients to hint the
+window manager about the type of their windows.</para></listitem>
+<listitem><para>Improve the compositing manager's ability to apply
+decorations and effects to override-redirect windows</para></listitem>
+<listitem><para>Enable pagers and taskbars to be implemented as separate
+clients and allow them to work with any compliant window manager.</para></listitem>
+</itemizedlist>
+<para>This spec doesn't cover any of the following:</para>
+<itemizedlist>
+<listitem><para>Other IPC mechanisms like ICE or Corba.</para></listitem>
+<listitem><para>Window manager configuration.</para></listitem>
+<listitem><para>Window manager documentation.</para></listitem>
+<listitem><para>Clients appearing on a proper subset of desktops.</para></listitem>
+<listitem><para>Window-in-window MDI.</para></listitem>
+</itemizedlist>
+<para>The window manager is supposed to be in charge of window management
+policy, so that there is consistent behavior on the user's screen no matter
+who wrote the clients.</para>
+<para>The spec offers a lot of external control about window manager actions.
+This is intended mainly to allow pagers, taskbars and similar window manager
+UIs to be implemented as separate clients. "Ordinary" clients shouldn't use
+these except maybe in response to a direct user request (i.e. setting a
+config option to start maximized or specifying a -desk n command line
+argument).</para>
+</sect2>
+</sect1>
+<sect1>
+ <title>Root Window Properties (and Related Messages)</title>
+ <para>
+Whenever this spec speaks about <quote>sending a message to the root
+window</quote>, it is understood that the client is supposed to create
+a ClientMessage event with the specified contents and send it by using
+a SendEvent request with the following arguments:
+ <programlisting><![CDATA[
+destination root
+propagate False
+event-mask (SubstructureNotify|SubstructureRedirect)
+event the specified ClientMessage
+]]></programlisting>
+ </para>
+ <sect2><title>_NET_SUPPORTED</title>
+ <programlisting><![CDATA[
+_NET_SUPPORTED, ATOM[]/32
+]]></programlisting>
+ <para>
+This property MUST be set by the Window Manager to indicate which hints it
+supports. For example: considering _NET_WM_STATE
+both this atom and all supported states e.g. _NET_WM_STATE_MODAL,
+_NET_WM_STATE_STICKY, would be listed. This assumes that backwards
+incompatible changes will not be made to the hints (without being renamed).
+ </para>
+ </sect2><sect2><title>_NET_CLIENT_LIST</title>
+ <programlisting><![CDATA[
+_NET_CLIENT_LIST, WINDOW[]/32
+_NET_CLIENT_LIST_STACKING, WINDOW[]/32
+]]></programlisting>
+ <para>
+These arrays contain all X Windows managed by the Window Manager.
+_NET_CLIENT_LIST has initial mapping order, starting with the oldest window.
+_NET_CLIENT_LIST_STACKING has bottom-to-top stacking order. These properties
+SHOULD be set and updated by the Window Manager.
+ </para>
+ </sect2>
+ <sect2>
+ <title>_NET_NUMBER_OF_DESKTOPS</title>
+ <programlisting><![CDATA[
+_NET_NUMBER_OF_DESKTOPS, CARDINAL/32
+]]></programlisting>
+ <para>
+This property SHOULD be set and updated by the Window Manager to indicate the
+number of virtual desktops.
+ </para>
+ <para>
+A Pager can request a change in the number of desktops by sending a _NET_NUMBER_OF_DESKTOPS message to the root window:
+ </para>
+ <programlisting><![CDATA[
+_NET_NUMBER_OF_DESKTOPS
+ message_type = _NET_NUMBER_OF_DESKTOPS
+ format = 32
+ data.l[0] = new_number_of_desktops
+ other data.l[] elements = 0
+]]></programlisting>
+ <para>
+The Window Manager is free to honor or reject this request. If the request is honored _NET_NUMBER_OF_DESKTOPS MUST be set to the new number of desktops, _NET_VIRTUAL_ROOTS MUST be set to store the new number of desktop virtual root window IDs and _NET_DESKTOP_VIEWPORT and _NET_WORKAREA must also be changed accordingly. The _NET_DESKTOP_NAMES property MAY remain unchanged.
+ </para>
+ <para>
+If the number of desktops is shrinking and _NET_CURRENT_DESKTOP is out of the new range of available desktops, then this MUST be set to the last available desktop from the new set. Clients that are still present on desktops that are out of the new range MUST be moved to the very last desktop from the new set. For these _NET_WM_DESKTOP MUST be updated.
+ </para>
+ </sect2>
+ <sect2>
+ <title>_NET_DESKTOP_GEOMETRY</title>
+ <programlisting><![CDATA[
+_NET_DESKTOP_GEOMETRY width, height, CARDINAL[2]/32
+]]></programlisting>
+ <para>
+ Array of two cardinals that defines the common size of all desktops
+ (this is equal to the screen size if the Window Manager doesn't support
+ large desktops, otherwise it's equal to the virtual size of the
+ desktop). This property SHOULD be set by the Window Manager.
+ </para>
+ <para>
+A Pager can request a change in the desktop geometry by sending a _NET_DESKTOP_GEOMETRY client
+message to the root window:
+ </para>
+ <programlisting><![CDATA[
+_NET_DESKTOP_GEOMETRY
+ message_type = _NET_DESKTOP_GEOMETRY
+ format = 32
+ data.l[0] = new_width
+ data.l[1] = new_height
+ other data.l[] elements = 0
+]]></programlisting>
+ <para>
+The Window Manager MAY choose to ignore this message, in which case _NET_DESKTOP_GEOMETRY property will remain unchanged.
+ </para>
+ </sect2>
+ <sect2>
+ <title>_NET_DESKTOP_VIEWPORT</title>
+ <programlisting><![CDATA[
+_NET_DESKTOP_VIEWPORT x, y, CARDINAL[][2]/32
+]]></programlisting>
+ <para>
+Array of pairs of cardinals that define the top left corner of each desktop's
+viewport. For Window Managers that don't support large desktops, this MUST
+always be set to (0,0).
+ </para>
+ <para>
+A Pager can request to change the viewport for the current desktop by sending a
+_NET_DESKTOP_VIEWPORT client message to the root window:
+ </para>
+ <programlisting><![CDATA[
+_NET_DESKTOP_VIEWPORT
+ message_type = _NET_DESKTOP_VIEWPORT
+ format = 32
+ data.l[0] = new_vx
+ data.l[1] = new_vy
+ other data.l[] elements = 0
+]]></programlisting>
+ <para>
+The Window Manager MAY choose to ignore this message, in which case _NET_DESKTOP_VIEWPORT property will remain unchanged.
+ </para>
+ </sect2><sect2><title>_NET_CURRENT_DESKTOP</title>
+ <programlisting><![CDATA[
+_NET_CURRENT_DESKTOP desktop, CARDINAL/32
+]]></programlisting>
+ <para>
+The index of the current desktop. This is always an integer between 0 and
+_NET_NUMBER_OF_DESKTOPS - 1. This MUST be set and updated by the Window
+Manager. If a Pager wants to switch to another virtual desktop, it MUST send
+a _NET_CURRENT_DESKTOP client message to the root window:
+ </para>
+ <programlisting><![CDATA[
+_NET_CURRENT_DESKTOP
+ message_type = _NET_CURRENT_DESKTOP
+ format = 32
+ data.l[0] = new_index
+ data.l[1] = timestamp
+ other data.l[] elements = 0
+]]></programlisting>
+ <para>
+Note that the timestamp may be 0 for clients using an older version of
+this spec, in which case the timestamp field should be ignored.
+ </para>
+ </sect2><sect2><title>_NET_DESKTOP_NAMES</title>
+ <programlisting><![CDATA[
+_NET_DESKTOP_NAMES, UTF8_STRING[]
+]]></programlisting>
+ <para>
+The names of all virtual desktops. This is a list of NULL-terminated strings in
+ UTF-8 encoding <citation><link linkend="UTF8">UTF8</link></citation>. This property MAY be
+ changed by a Pager or the Window Manager at any time.
+ </para>
+<para>
+Note: The number of names could be different from _NET_NUMBER_OF_DESKTOPS.
+If it is less than _NET_NUMBER_OF_DESKTOPS, then the desktops with high
+numbers are unnamed. If it is larger than _NET_NUMBER_OF_DESKTOPS, then the
+excess names outside of the _NET_NUMBER_OF_DESKTOPS are considered to be
+reserved in case the number of desktops is increased.
+</para>
+<para>
+Rationale: The name is not a necessary attribute of a virtual desktop. Thus
+the availability or unavailability of names has no impact on virtual desktop
+functionality. Since names are set by users and users are likely to preset
+names for a fixed number of desktops, it doesn't make sense to shrink or grow
+this list when the number of available desktops changes.
+</para>
+ </sect2><sect2><title>_NET_ACTIVE_WINDOW</title>
+ <programlisting><![CDATA[
+_NET_ACTIVE_WINDOW, WINDOW/32
+]]></programlisting>
+ <para>
+The window ID of the currently active window or None if no window has the focus.
+This is a read-only property set by the
+Window Manager. If a Client wants to activate
+another window, it MUST send a _NET_ACTIVE_WINDOW client message to the root
+window:
+ </para>
+ <programlisting><![CDATA[
+_NET_ACTIVE_WINDOW
+ window = window to activate
+ message_type = _NET_ACTIVE_WINDOW
+ format = 32
+ data.l[0] = source indication
+ data.l[1] = timestamp
+ data.l[2] = requestor's currently active window, 0 if none
+ other data.l[] elements = 0
+]]></programlisting>
+ <para>
+Source indication should be 1 when the request comes from an application, and 2
+when it comes from a pager. Clients using older version of this spec use 0
+as source indication, see <xref linkend="sourceindication"/> for details.
+The timestamp is Client's last user activity timestamp (see _NET_WM_USER_TIME)
+at the time of the request, and the currently active window
+is the Client's active toplevel window, if any (the Window Manager may
+be e.g. more likely to obey the request if it will mean transferring
+focus from one active window to another).
+ </para>
+ <para>
+Depending on the information provided with the message, the Window Manager may
+decide to refuse the request (either completely ignore it, or e.g. use
+_NET_WM_STATE_DEMANDS_ATTENTION).
+ </para>
+ </sect2><sect2><title>_NET_WORKAREA</title>
+ <programlisting><![CDATA[
+_NET_WORKAREA, x, y, width, height CARDINAL[][4]/32
+]]>
+ </programlisting>
+ <para>
+This property MUST be set by the Window Manager upon calculating the work area for
+each desktop. Contains a geometry for each desktop. These geometries are
+specified relative to the viewport on each desktop and specify an area that is
+completely contained within the viewport.
+ Work area SHOULD be used by desktop applications to place desktop icons appropriately.
+ </para>
+ <para>
+The Window Manager SHOULD calculate this space by taking the current
+page minus space occupied by dock and panel windows, as indicated by
+the <link linkend="NETWMSTRUT">_NET_WM_STRUT</link> or <link
+linkend="NETWMSTRUTPARTIAL">_NET_WM_STRUT_PARTIAL</link> properties set on
+client windows.
+ </para>
+ </sect2>
+ <sect2>
+ <title>_NET_SUPPORTING_WM_CHECK</title>
+ <programlisting><![CDATA[
+_NET_SUPPORTING_WM_CHECK, WINDOW/32
+]]></programlisting>
+ <para>
+The Window Manager MUST set this property on the root window to be the ID of a
+ child window created by himself, to indicate that a compliant window manager is
+ active. The child window MUST also have the _NET_SUPPORTING_WM_CHECK
+ property set to the ID of the child window. The child window MUST also
+ have the _NET_WM_NAME property set to the name of the Window Manager.
+ </para>
+ <para>
+Rationale: The child window is used to distinguish an active Window Manager
+ from a stale _NET_SUPPORTING_WM_CHECK
+ property that happens to point to another window. If the
+ _NET_SUPPORTING_WM_CHECK window on the client window is missing
+ or not properly set, clients SHOULD assume that no conforming
+ Window Manager is present.
+ </para>
+ </sect2>
+ <sect2>
+ <title>_NET_VIRTUAL_ROOTS</title>
+ <programlisting><![CDATA[
+_NET_VIRTUAL_ROOTS, WINDOW[]/32
+]]></programlisting>
+ <para>
+To implement virtual desktops, some Window Managers reparent client windows to
+a child of the root window. Window Managers using this technique MUST set
+this property to a list of IDs for windows that are acting as virtual root
+windows. This property allows background setting programs to work with
+virtual roots and allows clients to figure out the window manager frame windows of their
+windows.
+ </para>
+ </sect2>
+ <sect2>
+ <title>_NET_DESKTOP_LAYOUT</title>
+ <programlisting><![CDATA[
+_NET_DESKTOP_LAYOUT, orientation, columns, rows, starting_corner CARDINAL[4]/32
+]]>
+ #define _NET_WM_ORIENTATION_HORZ 0
+ #define _NET_WM_ORIENTATION_VERT 1
+
+ #define _NET_WM_TOPLEFT 0
+ #define _NET_WM_TOPRIGHT 1
+ #define _NET_WM_BOTTOMRIGHT 2
+ #define _NET_WM_BOTTOMLEFT 3
+</programlisting>
+ <para>
+ <emphasis>This property is set by a Pager, not by the Window
+ Manager.</emphasis>
+ When setting this property, the Pager must own a manager selection (as
+ defined in the ICCCM 2.8). The manager selection is called
+ _NET_DESKTOP_LAYOUT_S<literal>n</literal> where
+ <literal>n</literal> is the screen number. The purpose of
+ this property is to allow the Window Manager to know the desktop
+ layout displayed by the Pager.
+ </para>
+ <para>
+ _NET_DESKTOP_LAYOUT describes the layout of virtual
+ desktops relative to each other. More specifically, it describes the layout
+ used by the owner of the manager selection. The Window Manager may use
+ this layout information or may choose to ignore it.
+ The property contains four values: the Pager orientation, the number of
+ desktops in the X direction, the number in the Y direction, and the
+ starting corner of the layout, i.e. the corner containing the first desktop.
+ </para>
+ <para>
+ Note: In order to inter-operate with Pagers implementing an earlier
+ draft of this document, Window Managers should accept a
+ _NET_DESKTOP_LAYOUT property of length 3 and
+ use _NET_WM_TOPLEFT as the starting corner in this case.
+ </para>
+ <para>
+ The virtual desktops are arranged in a rectangle with
+ <literal>rows</literal> rows and <literal>columns</literal> columns.
+ If <literal>rows</literal> times <literal>columns</literal> does not match
+ the total number of desktops as specified by
+ _NET_NUMBER_OF_DESKTOPS, the highest-numbered
+ workspaces are assumed to be nonexistent. Either <literal>rows</literal> or
+ <literal>columns</literal> (but not both) may be specified as 0 in which
+ case its actual value will be derived from _NET_NUMBER_OF_DESKTOPS.
+ </para>
+ <para>
+ When the orientation is _NET_WM_ORIENTATION_HORZ
+ the desktops are laid out in rows, with the first desktop in the
+ specified starting corner. So a layout with four columns and three rows
+ starting in the _NET_WM_TOPLEFT corner looks like this:
+<programlisting>
+ +--+--+--+--+
+ | 0| 1| 2| 3|
+ +--+--+--+--+
+ | 4| 5| 6| 7|
+ +--+--+--+--+
+ | 8| 9|10|11|
+ +--+--+--+--+
+</programlisting>
+With starting_corner _NET_WM_BOTTOMRIGHT, it looks like this:
+<programlisting>
+ +--+--+--+--+
+ |11|10| 9| 8|
+ +--+--+--+--+
+ | 7| 6| 5| 4|
+ +--+--+--+--+
+ | 3| 2| 1| 0|
+ +--+--+--+--+
+</programlisting>
+
+ </para>
+ <para>
+ When the orientation is _NET_WM_ORIENTATION_VERT
+ the layout with four columns and three rows starting in the _NET_WM_TOPLEFT
+ corner looks like:
+
+<programlisting>
+ +--+--+--+--+
+ | 0| 3| 6| 9|
+ +--+--+--+--+
+ | 1| 4| 7|10|
+ +--+--+--+--+
+ | 2| 5| 8|11|
+ +--+--+--+--+
+</programlisting>
+With starting_corner _NET_WM_TOPRIGHT, it looks like:
+
+<programlisting>
+ +--+--+--+--+
+ | 9| 6| 3| 0|
+ +--+--+--+--+
+ |10| 7| 4| 1|
+ +--+--+--+--+
+ |11| 8| 5| 2|
+ +--+--+--+--+
+</programlisting>
+ </para>
+ <para>
+ The numbers here are the desktop numbers, as for
+ _NET_CURRENT_DESKTOP.
+ </para>
+ </sect2>
+
+ <sect2><title>_NET_SHOWING_DESKTOP</title>
+ <programlisting><![CDATA[
+_NET_SHOWING_DESKTOP desktop, CARDINAL/32
+]]></programlisting>
+ <para>
+ Some Window Managers have a "showing the desktop" mode in which windows
+ are hidden, and the desktop background is displayed and focused. If a
+ Window Manager supports the _NET_SHOWING_DESKTOP hint, it MUST set it
+ to a value of 1 when the Window Manager is in "showing the desktop" mode,
+ and a value of zero if the Window Manager is not in this mode.
+ </para>
+ <para>
+ If a Pager wants to enter or leave the mode, it MUST
+ send a _NET_SHOWING_DESKTOP client message to the root window
+ requesting the change:
+ <programlisting><![CDATA[
+_NET_SHOWING_DESKTOP
+ message_type = _NET_SHOWING_DESKTOP
+ format = 32
+ data.l[0] = boolean 0 or 1
+ other data.l[] elements = 0
+]]></programlisting>
+ The Window Manager may choose to ignore this client message.
+ </para>
+ </sect2>
+
+ </sect1>
+ <sect1>
+ <title>Other Root Window Messages</title>
+ <sect2><title>_NET_CLOSE_WINDOW</title>
+ <programlisting><![CDATA[
+_NET_CLOSE_WINDOW
+]]></programlisting>
+ <para>
+ Pagers wanting to close a window MUST send a _NET_CLOSE_WINDOW client
+ message request to the root window:
+ </para>
+<programlisting><![CDATA[
+_NET_CLOSE_WINDOW
+ window = window to close
+ message_type = _NET_CLOSE_WINDOW
+ format = 32
+ data.l[0] = timestamp
+ data.l[1] = source indication
+ other data.l[] elements = 0
+]]></programlisting>
+ <para>
+The Window Manager MUST then attempt to close the window specified.
+See <xref linkend="sourceindication"/> for details on the source indication.
+ </para>
+ <para>
+ Rationale: A Window Manager might be more clever than the usual method (send WM_DELETE message if the protocol is selected, XKillClient otherwise). It might introduce a timeout, for example. Instead of duplicating the code, the Window Manager can easily do the job.
+ </para>
+ </sect2>
+ <sect2><title>_NET_MOVERESIZE_WINDOW</title>
+<programlisting><![CDATA[
+_NET_MOVERESIZE_WINDOW
+ window = window to be moved or resized
+ message_type = _NET_MOVERESIZE_WINDOW
+ format = 32
+ data.l[0] = gravity and flags
+ data.l[1] = x
+ data.l[2] = y
+ data.l[3] = width
+ data.l[4] = height
+]]></programlisting>
+ <para>
+ The low byte of data.l[0] contains the gravity to use; it may contain
+ any value allowed for the WM_SIZE_HINTS.win_gravity property:
+ NorthWest (1), North (2), NorthEast (3), West (4), Center (5), East
+ (6), SouthWest (7), South (8), SouthEast (9) and Static (10). A
+ gravity of 0 indicates that the Window Manager should use the gravity
+ specified in WM_SIZE_HINTS.win_gravity. The bits 8 to 11 indicate the
+ presence of x, y, width and height. The bits 12 to 15 indicate the
+ source (see <xref linkend="sourceindication"/>), so 0001 indicates
+ the application and 0010 indicates a Pager or a Taskbar.
+ The remaining bits should be set to zero.
+ </para>
+ <para>
+ Pagers wanting to move or resize a window may send a
+ _NET_MOVERESIZE_WINDOW client message request to the root window
+ instead of using a ConfigureRequest.
+ </para>
+ <para>
+ Window Managers should treat a _NET_MOVERESIZE_WINDOW message exactly
+ like a ConfigureRequest (in particular, adhering to the ICCCM rules
+ about synthetic ConfigureNotify events), except that they should use
+ the gravity specified in the message.
+ </para>
+ <para>
+ Rationale: Using a _NET_MOVERESIZE_WINDOW message with StaticGravity
+ allows Pagers to exactly position and resize a window including its
+ decorations without knowing the size of the decorations.
+ </para>
+ </sect2>
+ <sect2><title>_NET_WM_MOVERESIZE</title>
+ <programlisting><![CDATA[
+_NET_WM_MOVERESIZE
+ window = window to be moved or resized
+ message_type = _NET_WM_MOVERESIZE
+ format = 32
+ data.l[0] = x_root
+ data.l[1] = y_root
+ data.l[2] = direction
+ data.l[3] = button
+ data.l[4] = source indication
+]]></programlisting>
+ <para>
+ This message allows Clients to initiate window movement or
+ resizing. They can define their own move and size
+ "grips", whilst letting the Window Manager control the actual operation.
+ This means that all moves/resizes can happen in a consistent manner as
+ defined by the Window Manager. See <xref linkend="sourceindication"/>
+ for details on the source indication.
+ </para>
+ <para>
+ When sending this message in response to a button press event, button
+ SHOULD indicate the button which was pressed, x_root and y_root MUST
+ indicate the position of the button press with respect to the root
+ window and direction MUST indicate whether this is a move or resize
+ event, and if it is a resize event, which edges of the window the size
+ grip applies to. When sending this message in response to a key event,
+ the direction MUST indicate whether this this is a move or resize
+ event and the other fields are unused.
+ </para>
+ <programlisting><![CDATA[
+#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0
+#define _NET_WM_MOVERESIZE_SIZE_TOP 1
+#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2
+#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
+#define _NET_WM_MOVERESIZE_SIZE_LEFT 7
+#define _NET_WM_MOVERESIZE_MOVE 8 /* movement only */
+#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 /* size via keyboard */
+#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */
+#define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */
+]]></programlisting>
+ <para>
+ The Client MUST release all grabs prior to sending such message (except
+ for the _NET_WM_MOVERESIZE_CANCEL message).
+ </para>
+ <para>
+ The Window Manager can use the button field to determine the
+ events on which it terminates the operation initiated by the
+ _NET_WM_MOVERESIZE message. Since there is a race condition between
+ a client sending the _NET_WM_MOVERESIZE message and the user releasing
+ the button, Window Managers are advised to offer some other means to
+ terminate the operation, e.g. by pressing the ESC key. The special value
+ _NET_WM_MOVERESIZE_CANCEL also allows clients to cancel the operation
+ by sending such message if they detect the release themselves
+ (clients should send it if they get the button release after sending
+ the move resize message, indicating that the WM did not get a grab in time
+ to get the release).
+ </para>
+ </sect2>
+ <sect2><title>_NET_RESTACK_WINDOW</title>
+ <programlisting><![CDATA[
+_NET_RESTACK_WINDOW
+]]></programlisting>
+ <para>
+ Pagers wanting to restack a window SHOULD send a _NET_RESTACK_WINDOW client
+ message request to the root window:
+ </para>
+<programlisting><![CDATA[
+_NET_RESTACK_WINDOW
+ window = window to restack
+ message_type = _NET_RESTACK_WINDOW
+ format = 32
+ data.l[0] = source indication
+ data.l[1] = sibling window
+ data.l[2] = detail
+ other data.l[] elements = 0
+]]></programlisting>
+ <para>
+ This request is similar to ConfigureRequest with CWSibling and CWStackMode flags. It should be used only by pagers,
+ applications can use normal ConfigureRequests. The source indication field should be therefore set to 2,
+ see <xref linkend="sourceindication"/> for details.
+ </para>
+ <para>
+ Rationale: A Window Manager may put restrictions on configure requests from applications, for example it may
+ under some conditions refuse to raise a window. This request makes it clear it comes from a pager or similar
+ tool, and therefore the Window Manager should always obey it.
+ </para>
+ </sect2>
+ <sect2><title>_NET_REQUEST_FRAME_EXTENTS</title>
+ <programlisting><![CDATA[
+_NET_REQUEST_FRAME_EXTENTS
+ window = window for which to set _NET_FRAME_EXTENTS
+ message_type = _NET_REQUEST_FRAME_EXTENTS
+]]></programlisting>
+ <para>
+A Client whose window has not yet been mapped can request of the
+Window Manager an estimate of the frame extents it will be given upon
+mapping. To retrieve such an estimate, the Client MUST send a
+_NET_REQUEST_FRAME_EXTENTS message to the root window. The Window
+Manager MUST respond by estimating the prospective frame extents and
+setting the window's _NET_FRAME_EXTENTS property accordingly. The
+Client MUST handle the resulting _NET_FRAME_EXTENTS PropertyNotify
+event. So that the Window Manager has a good basis for estimation,
+the Client MUST set any window properties it intends to set
+<emphasis>before</emphasis> sending this message. The Client MUST be able to cope
+with imperfect estimates.
+ </para>
+ <para>
+Rationale: A client cannot calculate the dimensions of its window's
+frame before the window is mapped, but some toolkits need this
+information. Asking the window manager for an estimate of the extents
+is a workable solution. The estimate may depend on the current theme,
+font sizes or other window properties. The client can track changes
+to the frame's dimensions by listening for _NET_FRAME_EXTENTS
+PropertyNotify events.
+ </para>
+ </sect2>
+</sect1>
+ <sect1>
+ <title>Application Window Properties</title>
+ <sect2><title>_NET_WM_NAME</title>
+ <programlisting><![CDATA[
+_NET_WM_NAME, UTF8_STRING
+]]></programlisting>
+ <para>
+The Client SHOULD set this to the title of the window in UTF-8 encoding. If
+set, the Window Manager should use this in preference to WM_NAME.
+ </para>
+ </sect2>
+
+ <sect2><title>_NET_WM_VISIBLE_NAME</title>
+ <programlisting><![CDATA[
+_NET_WM_VISIBLE_NAME, UTF8_STRING
+]]></programlisting>
+ <para>
+If the Window Manager displays a window name other than _NET_WM_NAME the Window Manager MUST set this to the title displayed in UTF-8 encoding.
+ </para>
+ <para>
+Rationale: This property is for Window Managers that display a title different from the _NET_WM_NAME or WM_NAME of the window (i.e. xterm &lt;1&gt;, xterm &lt;2&gt;, ... is shown, but _NET_WM_NAME / WM_NAME is still xterm for each window) thereby allowing Pagers to display the same title as the Window Manager.
+ </para>
+ </sect2>
+
+ <sect2><title>_NET_WM_ICON_NAME</title>
+ <programlisting><![CDATA[
+_NET_WM_ICON_NAME, UTF8_STRING
+]]></programlisting>
+ <para>
+The Client SHOULD set this to the title of the icon for this window in UTF-8
+encoding. If set, the Window Manager should use this in preference to
+WM_ICON_NAME.
+ </para>
+ </sect2>
+
+ <sect2><title>_NET_WM_VISIBLE_ICON_NAME</title>
+ <programlisting><![CDATA[
+_NET_WM_VISIBLE_ICON_NAME, UTF8_STRING
+]]></programlisting>
+ <para>
+If the Window Manager displays an icon name other than _NET_WM_ICON_NAME
+the Window Manager MUST set this to the title displayed in UTF-8 encoding.
+ </para>
+ </sect2>
+
+ <sect2><title>_NET_WM_DESKTOP</title>
+ <programlisting><![CDATA[
+_NET_WM_DESKTOP desktop, CARDINAL/32
+]]></programlisting>
+ <para>
+Cardinal to determine the desktop the window is in (or wants to be) starting
+with 0 for the first desktop. A Client MAY choose not to set this property,
+in which case the Window Manager SHOULD place it as it wishes. 0xFFFFFFFF
+indicates that the window SHOULD appear on all desktops.
+ </para>
+ <para>
+The Window Manager should honor _NET_WM_DESKTOP whenever a withdrawn window
+requests to be mapped.
+ </para>
+ <para>
+The Window Manager should remove the property whenever
+a window is withdrawn but it should leave the property in place when it is
+shutting down, e.g. in response to losing ownership of the WM_Sn manager
+selection.
+ </para>
+ <para>
+Rationale: Removing the property upon window withdrawal helps legacy
+applications which want to reuse withdrawn windows. Not removing the property
+upon shutdown allows the next Window Manager to restore windows to their
+previous desktops.
+ </para>
+ <para>
+A Client can request a change of desktop for a non-withdrawn window by sending
+a _NET_WM_DESKTOP client message to the root window:
+ </para>
+ <programlisting><![CDATA[
+_NET_WM_DESKTOP
+ window = the respective client window
+ message_type = _NET_WM_DESKTOP
+ format = 32
+ data.l[0] = new_desktop
+ data.l[1] = source indication
+ other data.l[] elements = 0
+]]></programlisting>
+ <para>
+ See <xref linkend="sourceindication"/> for details on the source
+ indication.
+ The Window Manager MUST keep this property updated on all windows.
+ </para>
+ </sect2><sect2><title>_NET_WM_WINDOW_TYPE</title>
+ <programlisting><![CDATA[
+_NET_WM_WINDOW_TYPE, ATOM[]/32
+]]></programlisting>
+ <para>
+This SHOULD be set by the Client before mapping to a list of atoms indicating
+the functional type of the window. This property SHOULD be used by the window
+manager in determining the decoration, stacking position and other behavior
+of the window. The Client SHOULD specify window types in order of preference
+(the first being most preferable) but MUST include at least one of the basic
+window type atoms from the list below. This is to allow for extension of the
+list of types whilst providing default behavior for Window Managers that do
+not recognize the extensions.
+ </para>
+ <para>
+This hint SHOULD also be set for override-redirect windows to allow
+compositing managers to apply consistent decorations to menus,
+tooltips etc.
+</para>
+ <para>
+Rationale: This hint is intended to replace the MOTIF hints. One of the
+objections to the MOTIF hints is that they are a purely visual description of
+the window decoration. By describing the function of the window, the Window
+Manager can apply consistent decoration and behavior to windows of the same
+type. Possible examples of behavior include keeping dock/panels on top or
+allowing pinnable menus / toolbars to only be hidden when another window has
+focus (NextStep style).
+ </para>
+ <programlisting><![CDATA[
+_NET_WM_WINDOW_TYPE_DESKTOP, ATOM
+_NET_WM_WINDOW_TYPE_DOCK, ATOM
+_NET_WM_WINDOW_TYPE_TOOLBAR, ATOM
+_NET_WM_WINDOW_TYPE_MENU, ATOM
+_NET_WM_WINDOW_TYPE_UTILITY, ATOM
+_NET_WM_WINDOW_TYPE_SPLASH, ATOM
+_NET_WM_WINDOW_TYPE_DIALOG, ATOM
+_NET_WM_WINDOW_TYPE_DROPDOWN_MENU, ATOM
+_NET_WM_WINDOW_TYPE_POPUP_MENU, ATOM
+_NET_WM_WINDOW_TYPE_TOOLTIP, ATOM
+_NET_WM_WINDOW_TYPE_NOTIFICATION, ATOM
+_NET_WM_WINDOW_TYPE_COMBO, ATOM
+_NET_WM_WINDOW_TYPE_DND, ATOM
+_NET_WM_WINDOW_TYPE_NORMAL, ATOM
+]]></programlisting>
+ <para>
+_NET_WM_WINDOW_TYPE_DESKTOP indicates a desktop feature. This can include a
+single window containing desktop icons with the same dimensions as the screen,
+allowing the desktop environment to have full control of the desktop, without
+the need for proxying root window clicks.
+ </para>
+ <para>
+_NET_WM_WINDOW_TYPE_DOCK indicates a dock or panel feature. Typically a
+Window Manager would keep such windows on top of all other windows.
+ </para>
+ <para>
+_NET_WM_WINDOW_TYPE_TOOLBAR and _NET_WM_WINDOW_TYPE_MENU indicate
+toolbar and pinnable menu windows, respectively (i.e. toolbars and
+menus "torn off" from the main application). Windows of this type may
+set the WM_TRANSIENT_FOR hint indicating the main application
+window. Note that the _NET_WM_WINDOW_TYPE_MENU should be set on
+torn-off managed windows, where _NET_WM_WINDOW_TYPE_DROPDOWN_MENU and
+_NET_WM_WINDOW_TYPE_POPUP_MENU are typically used on override-redirect
+windows.
+ </para>
+ <para>
+_NET_WM_WINDOW_TYPE_UTILITY indicates a small persistent utility window, such as
+a palette or toolbox. It is distinct from type TOOLBAR because it does not
+correspond to a toolbar torn off from the main application. It's distinct from
+type DIALOG because it isn't a transient dialog, the user will probably keep it
+open while they're working. Windows of this type may set the WM_TRANSIENT_FOR
+hint indicating the main application window.
+ </para>
+ <para>
+_NET_WM_WINDOW_TYPE_SPLASH indicates that the window is a splash screen
+displayed as an application is starting up.
+ </para>
+ <para>
+_NET_WM_WINDOW_TYPE_DIALOG indicates that this is a dialog window. If
+_NET_WM_WINDOW_TYPE is not set, then managed windows with
+WM_TRANSIENT_FOR set MUST be taken as this type. Override-redirect
+windows with WM_TRANSIENT_FOR, but without _NET_WM_WINDOW_TYPE must be
+taken as _NET_WM_WINDOW_TYPE_NORMAL.
+ </para>
+ <para>
+_NET_WM_WINDOW_TYPE_DROPDOWN_MENU indicates that the
+window in question is a dropdown menu, ie., the kind of menu that
+typically appears when the user clicks on a menubar, as opposed to a
+popup menu which typically appears when the user right-clicks on an
+object. This property is typically used on override-redirect windows.
+ </para>
+ <para>
+_NET_WM_WINDOW_TYPE_POPUP_MENU indicates that the
+window in question is a popup menu, ie., the kind of menu that
+typically appears when the user right clicks on an object, as opposed
+to a dropdown menu which typically appears when the user clicks on a
+menubar. This property is typically used on override-redirect windows.
+ </para>
+ <para>
+_NET_WM_WINDOW_TYPE_TOOLTIP indicates that the window
+in question is a tooltip, ie., a short piece of explanatory text that
+typically appear after the mouse cursor hovers over an object for a
+while. This property is typically used on override-redirect windows.
+ </para>
+ <para>
+_NET_WM_WINDOW_TYPE_NOTIFICATION indicates a
+notification. An example of a notification would be a bubble appearing
+with informative text such as "Your laptop is running out of power"
+etc. This property is typically used on override-redirect windows.
+ </para>
+ <para>
+_NET_WM_WINDOW_TYPE_COMBO should be used on the windows that are
+popped up by combo boxes. An example is a window that appears below a
+text field with a list of suggested completions. This property is
+typically used on override-redirect windows.
+ </para>
+ <para>
+_NET_WM_WINDOW_TYPE_DND indicates that the window is being
+dragged. Clients should set this hint when the window in question
+contains a representation of an object being dragged from one place to
+another. An example would be a window containing an icon that is being
+dragged from one file manager window to another. This property is
+typically used on override-redirect windows.
+ </para>
+ <para>
+_NET_WM_WINDOW_TYPE_NORMAL indicates that this is a normal, top-level
+window, either managed or override-redirect. Managed windows with
+neither _NET_WM_WINDOW_TYPE nor WM_TRANSIENT_FOR set MUST be taken as
+this type. Override-redirect windows without _NET_WM_WINDOW_TYPE, must
+be taken as this type, whether or not they have WM_TRANSIENT_FOR set.
+</para>
+ </sect2>
+ <sect2>
+ <title>_NET_WM_STATE</title>
+ <programlisting><![CDATA[
+ _NET_WM_STATE, ATOM[]
+]]></programlisting>
+ <para>
+A list of hints describing the window state. Atoms present in the list MUST be
+considered set, atoms not present in the list MUST be considered not set. The
+Window Manager SHOULD honor
+_NET_WM_STATE whenever a withdrawn window requests to be mapped. A Client
+wishing to change the state of a window MUST send a _NET_WM_STATE client
+message to the root window (see below). The Window Manager MUST keep this
+property updated to reflect the current state of the window.
+ </para>
+ <para>
+The Window Manager should remove the property whenever
+a window is withdrawn, but it should leave the property in place when it is
+shutting down, e.g. in response to losing ownership of the WM_Sn manager
+selection.
+ </para>
+ <para>
+Rationale: Removing the property upon window withdrawal helps legacy
+applications which want to reuse withdrawn windows. Not removing the property
+upon shutdown allows the next Window Manager to restore windows to their
+previous state.
+ </para>
+ <para>
+Possible atoms are:
+ </para>
+ <programlisting><![CDATA[
+_NET_WM_STATE_MODAL, ATOM
+_NET_WM_STATE_STICKY, ATOM
+_NET_WM_STATE_MAXIMIZED_VERT, ATOM
+_NET_WM_STATE_MAXIMIZED_HORZ, ATOM
+_NET_WM_STATE_SHADED, ATOM
+_NET_WM_STATE_SKIP_TASKBAR, ATOM
+_NET_WM_STATE_SKIP_PAGER, ATOM
+_NET_WM_STATE_HIDDEN, ATOM
+_NET_WM_STATE_FULLSCREEN, ATOM
+_NET_WM_STATE_ABOVE, ATOM
+_NET_WM_STATE_BELOW, ATOM
+_NET_WM_STATE_DEMANDS_ATTENTION, ATOM
+_NET_WM_STATE_FOCUSED, ATOM
+]]></programlisting>
+ <para>
+An implementation MAY add new atoms to this list. Implementations
+without extensions MUST ignore any unknown atoms, effectively removing
+them from the list. These extension atoms MUST NOT start with the prefix
+_NET.
+ </para>
+ <para>
+_NET_WM_STATE_MODAL indicates that this is a modal dialog box.
+If the WM_TRANSIENT_FOR hint is set to another toplevel window, the
+dialog is modal for that window; if WM_TRANSIENT_FOR is not set or set
+to the root window the dialog is modal for its window group.
+ </para>
+ <para>
+_NET_WM_STATE_STICKY indicates that the Window Manager SHOULD keep the
+window's position fixed on the screen, even when the virtual desktop scrolls.
+ </para>
+ <para>
+_NET_WM_STATE_MAXIMIZED_{VERT,HORZ} indicates that the window is
+{vertically,horizontally} maximized.
+ </para>
+ <para>
+_NET_WM_STATE_SHADED indicates that the window is shaded.
+ </para>
+ <para>
+_NET_WM_STATE_SKIP_TASKBAR indicates that the window should not be
+included on a taskbar. This hint should be requested by the
+application, i.e. it indicates that the window by nature is never
+in the taskbar. Applications should not set this hint if
+_NET_WM_WINDOW_TYPE already conveys the exact nature of the
+window.
+ </para>
+ <para>
+_NET_WM_STATE_SKIP_PAGER indicates that the window should not be
+included on a Pager. This hint should be requested by the application,
+i.e. it indicates that the window by nature is never in the
+Pager. Applications should not set this hint if _NET_WM_WINDOW_TYPE
+already conveys the exact nature of the window.
+ </para>
+ <para>
+_NET_WM_STATE_HIDDEN should be set by the Window Manager to indicate
+that a window would not be visible on the screen if its
+desktop/viewport were active and its coordinates were within the
+screen bounds. The canonical example is that minimized windows should
+be in the _NET_WM_STATE_HIDDEN state. Pagers and similar applications
+should use _NET_WM_STATE_HIDDEN instead of WM_STATE to decide whether
+to display a window in miniature representations of the windows on a
+desktop.
+ </para>
+<para>
+Implementation note: if an Application asks to toggle
+_NET_WM_STATE_HIDDEN the Window Manager should probably just ignore
+the request, since _NET_WM_STATE_HIDDEN is a function of some other
+aspect of the window such as minimization, rather than an independent
+state.
+</para>
+ <para>
+_NET_WM_STATE_FULLSCREEN indicates that the window should fill the entire
+ screen and have no window decorations. Additionally the Window
+ Manager is responsible for restoring the original geometry after a
+ switch from fullscreen back to normal window. For example, a
+ presentation program would use this hint.
+ </para>
+ <para>
+_NET_WM_STATE_ABOVE indicates that the window should be on top of most
+windows (see <xref linkend="STACKINGORDER"/> for details).
+ </para>
+
+ <para>
+_NET_WM_STATE_BELOW indicates that the window should be below most
+windows (see <xref linkend="STACKINGORDER"/> for details).
+ </para>
+
+ <para>
+ _NET_WM_STATE_ABOVE and _NET_WM_STATE_BELOW are mainly meant for user
+ preferences and should not be used by applications e.g. for drawing
+ attention to their dialogs (the Urgency
+ hint should be used in that case, see <xref linkend="URGENCY"/>).'
+ </para>
+
+ <para>
+_NET_WM_STATE_DEMANDS_ATTENTION indicates that some action in or with the window
+happened. For example, it may be set by the Window Manager if the window requested
+activation but the Window Manager refused it, or the application may set it if it
+finished some work. This state may be set by both the Client and
+the Window Manager. It should be unset by the Window Manager when it decides
+the window got the required attention (usually, that it got activated).
+ </para>
+
+ <para>
+_NET_WM_STATE_FOCUSED indicates whether the window's decorations are drawn in an
+active state. Clients MUST regard it as a read-only hint. It cannot be set at
+map time or changed via a _NET_WM_STATE client message. The window given by
+_NET_ACTIVE_WINDOW will usually have this hint, but at times other windows may
+as well, if they have a strong association with the active window and will be
+considered as a unit with it by the user. Clients that modify the appearance of
+internal elements when a toplevel has keyboard focus SHOULD check for the
+availability of this state in _NET_SUPPORTED and, if it is available,
+use it in preference to tracking focus via FocusIn events. By doing so they will
+match the window decorations and accurately reflect the intentions of the Window
+Manager.
+ </para>
+
+ <para>
+To change the state of a mapped window, a Client MUST send a _NET_WM_STATE
+client message to the root window:
+ </para>
+<programlisting><![CDATA[
+ window = the respective client window
+ message_type = _NET_WM_STATE
+ format = 32
+ data.l[0] = the action, as listed below
+ data.l[1] = first property to alter
+ data.l[2] = second property to alter
+ data.l[3] = source indication
+ other data.l[] elements = 0
+]]></programlisting>
+ <para>
+This message allows two properties to be changed simultaneously, specifically
+to allow both horizontal and vertical maximization to be altered together.
+l[2] MUST be set to zero if only one property is to be changed.
+See <xref linkend="sourceindication"/> for details on the source indication.
+l[0], the action, MUST be one of:
+ </para>
+ <programlisting><![CDATA[
+_NET_WM_STATE_REMOVE 0 /* remove/unset property */
+_NET_WM_STATE_ADD 1 /* add/set property */
+_NET_WM_STATE_TOGGLE 2 /* toggle property */
+]]></programlisting>
+ <para>
+ See also the implementation notes on <link linkend="URGENCY">urgency</link> and <link linkend="NORESIZE">fixed size windows</link>.
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>_NET_WM_ALLOWED_ACTIONS</title>
+ <programlisting><![CDATA[
+_NET_WM_ALLOWED_ACTIONS, ATOM[]
+]]></programlisting>
+ <para>
+A list of atoms indicating user operations that the Window Manager supports for
+this window. Atoms present in the list indicate allowed actions, atoms not
+present in the list indicate actions that are not supported for this window.
+The Window Manager MUST keep this property updated to reflect the
+actions which are currently "active" or "sensitive" for a window.
+Taskbars, Pagers, and other tools use _NET_WM_ALLOWED_ACTIONS to
+decide which actions should be made available to the user.
+ </para>
+ <para>
+Possible atoms are:
+ </para>
+ <programlisting><![CDATA[
+_NET_WM_ACTION_MOVE, ATOM
+_NET_WM_ACTION_RESIZE, ATOM
+_NET_WM_ACTION_MINIMIZE, ATOM
+_NET_WM_ACTION_SHADE, ATOM
+_NET_WM_ACTION_STICK, ATOM
+_NET_WM_ACTION_MAXIMIZE_HORZ, ATOM
+_NET_WM_ACTION_MAXIMIZE_VERT, ATOM
+_NET_WM_ACTION_FULLSCREEN, ATOM
+_NET_WM_ACTION_CHANGE_DESKTOP, ATOM
+_NET_WM_ACTION_CLOSE, ATOM
+_NET_WM_ACTION_ABOVE, ATOM
+_NET_WM_ACTION_BELOW, ATOM
+]]></programlisting>
+ <para>
+An implementation MAY add new atoms to this list. Implementations
+without extensions MUST ignore any unknown atoms, effectively removing
+them from the list. These extension atoms MUST NOT start with the prefix
+_NET.
+ </para>
+ <para>
+Note that the actions listed here are those that the <emphasis>Window
+Manager</emphasis> will honor for this window. The operations must still be
+requested through the normal mechanisms outlined in this specification. For
+example, _NET_WM_ACTION_CLOSE does not mean that clients can send a
+WM_DELETE_WINDOW message to this window; it means that clients can use a
+_NET_CLOSE_WINDOW message to ask the Window Manager to do so.
+ </para>
+ <para>
+Window Managers SHOULD ignore the value of _NET_WM_ALLOWED_ACTIONS when they
+initially manage a window. This value may be left over from a previous Window
+Manager with different policies.
+ </para>
+ <para>
+_NET_WM_ACTION_MOVE indicates that the window may be moved around the screen.
+ </para>
+ <para>
+_NET_WM_ACTION_RESIZE indicates that the window may be resized.
+(Implementation note: Window Managers can identify a non-resizable
+window because its minimum and maximum size in WM_NORMAL_HINTS will be the same.)
+ </para>
+ <para>
+_NET_WM_ACTION_MINIMIZE indicates that the window may be iconified.
+ </para>
+ <para>
+_NET_WM_ACTION_SHADE indicates that the window may be shaded.
+ </para>
+ <para>
+_NET_WM_ACTION_STICK indicates that the window may have its sticky state
+toggled (as for _NET_WM_STATE_STICKY). Note that this state has to do with
+viewports, not desktops.
+ </para>
+ <para>
+_NET_WM_ACTION_MAXIMIZE_HORZ indicates that the window may be maximized horizontally.
+ </para>
+ <para>
+_NET_WM_ACTION_MAXIMIZE_VERT indicates that the window may be maximized vertically.
+ </para>
+ <para>
+_NET_WM_ACTION_FULLSCREEN indicates that the window may be brought to
+ fullscreen state.
+ </para>
+ <para>
+_NET_WM_ACTION_CHANGE_DESKTOP indicates that the window may be moved between desktops.
+ </para>
+ <para>
+_NET_WM_ACTION_CLOSE indicates that the window may be closed (i.e. a _NET_CLOSE_WINDOW
+message may be sent).
+ </para>
+ <para>
+_NET_WM_ACTION_ABOVE indicates that the window may placed in the "above" layer of windows (i.e. will respond to _NET_WM_STATE_ABOVE changes; see also <xref linkend="STACKINGORDER"/> for details).
+ </para>
+ <para>
+_NET_WM_ACTION_BELOW indicates that the window may placed in the "below" layer of windows (i.e. will respond to _NET_WM_STATE_BELOW changes; see also <xref linkend="STACKINGORDER"/> for details)).
+ </para>
+</sect2>
+
+<sect2><title>_NET_WM_STRUT</title>
+ <programlisting id="NETWMSTRUT"><![CDATA[
+_NET_WM_STRUT, left, right, top, bottom, CARDINAL[4]/32
+]]></programlisting>
+ <para>
+This property is equivalent to a _NET_WM_STRUT_PARTIAL property where all start
+values are 0 and all end values are the height or width of the logical screen.
+_NET_WM_STRUT_PARTIAL was introduced later than _NET_WM_STRUT, however, so
+clients MAY set this property in addition to _NET_WM_STRUT_PARTIAL to ensure
+backward compatibility with Window Managers supporting older versions of the
+Specification.
+ </para>
+</sect2>
+<sect2><title>_NET_WM_STRUT_PARTIAL</title>
+ <programlisting id="NETWMSTRUTPARTIAL"><![CDATA[
+_NET_WM_STRUT_PARTIAL, left, right, top, bottom, left_start_y, left_end_y,
+right_start_y, right_end_y, top_start_x, top_end_x, bottom_start_x,
+bottom_end_x,CARDINAL[12]/32
+]]></programlisting>
+ <para>
+This property MUST be set by the Client if the window is to reserve space at the
+edge of the screen. The property contains 4 cardinals specifying the width of
+the reserved area at each border of the screen, and an additional 8 cardinals
+specifying the beginning and end corresponding to each of the four struts. The
+order of the values is left, right, top, bottom, left_start_y, left_end_y,
+right_start_y, right_end_y, top_start_x, top_end_x, bottom_start_x,
+bottom_end_x. All coordinates are root window coordinates. The client MAY change
+this property at any time, therefore the Window Manager MUST watch for
+property notify events if the Window Manager uses this property to assign
+special semantics to the window.
+ </para>
+ <para>
+If both this property and the _NET_WM_STRUT property are set, the Window Manager
+MUST ignore the _NET_WM_STRUT property values and use instead the values for
+_NET_WM_STRUT_PARTIAL. This will ensure that Clients can safely set both
+properties without giving up the improved semantics of the new property.
+ </para>
+ <para>
+The purpose of struts is to reserve space at the borders of the
+desktop. This is very useful for a docking area, a taskbar or a panel,
+for instance. The Window Manager should take this reserved area into
+account when constraining window positions - maximized windows, for
+example, should not cover that area.
+ </para>
+ <para>
+The start and end values associated with each strut allow areas to be
+reserved which do not span the entire width or height of the screen.
+Struts MUST be specified in root window coordinates, that is, they are
+<emphasis>not</emphasis> relative to the edges of any view port or Xinerama
+monitor.
+ </para>
+ <para>
+For example, for a panel-style Client appearing at the bottom of the
+screen, 50 pixels tall, and occupying the space from 200-600 pixels
+from the left of the screen edge would set a bottom strut of 50, and
+set bottom_start_x to 200 and bottom_end_x to 600. Another example is
+a panel on a screen using the Xinerama extension. Assume that the set
+up uses two monitors, one running at 1280x1024 and the other to the
+right running at 1024x768, with the top edge of the two physical
+displays aligned. If the panel wants to fill the entire bottom edge
+of the smaller display with a panel 50 pixels tall, it should set a
+bottom strut of 306, with bottom_start_x of 1280, and bottom_end_x of
+2303. Note that the strut is relative to the screen edge, and not the
+edge of the xinerama monitor.
+ </para>
+ <para>
+Rationale: A simple "do not cover" hint is not enough for dealing with e.g.
+auto-hide panels.
+ </para>
+ <para>
+Notes: An auto-hide panel SHOULD set the strut to be its minimum, hidden size.
+A "corner" panel that does not extend for the full length of a screen border
+SHOULD only set one strut.
+ </para>
+</sect2>
+<sect2><title>_NET_WM_ICON_GEOMETRY</title>
+<programlisting><![CDATA[
+_NET_WM_ICON_GEOMETRY, x, y, width, height, CARDINAL[4]/32
+]]></programlisting>
+ <para>
+This optional property MAY be set by stand alone tools like a taskbar or an
+iconbox. It specifies the geometry of a possible icon in case the window is iconified.
+ </para>
+ <para>
+Rationale: This makes it possible for a Window Manager to display a nice
+animation like morphing the window into its icon.
+ </para>
+ </sect2>
+ <sect2>
+ <title>_NET_WM_ICON</title>
+ <programlisting><![CDATA[
+_NET_WM_ICON CARDINAL[][2+n]/32
+]]></programlisting>
+ <para>
+This is an array of possible icons for the client. This specification does
+not stipulate what size these icons should be, but individual desktop
+environments or toolkits may do so. The Window Manager MAY scale any of these
+icons to an appropriate size.
+ </para>
+ <para>
+This is an array of 32bit packed CARDINAL ARGB with high byte being A, low
+byte being B. The first two cardinals are width, height. Data is in rows, left to
+right and top to bottom.
+ </para>
+ </sect2>
+ <sect2>
+ <title>_NET_WM_PID</title>
+ <programlisting><![CDATA[
+_NET_WM_PID CARDINAL/32
+]]></programlisting>
+ <para>
+If set, this property MUST contain the process ID of the client owning this
+window. This MAY be used by the Window Manager to kill windows which do not
+respond to the _NET_WM_PING protocol.
+ </para>
+ <para>
+If _NET_WM_PID is set, the ICCCM-specified property WM_CLIENT_MACHINE
+MUST also be set. While the ICCCM only requests that WM_CLIENT_MACHINE is set
+<quote> to a string that forms the name of the machine running the client as
+seen from the machine running the server</quote> conformance to this
+specification requires that WM_CLIENT_MACHINE be set to the fully-qualified domain
+name of the client's host.
+ </para>
+ <para>
+See also the implementation notes on <link linkend="KILLINGWINDOWS">killing hung processes</link>.
+ </para>
+ </sect2>
+ <sect2><title>_NET_WM_HANDLED_ICONS</title>
+ <programlisting><![CDATA[
+_NET_WM_HANDLED_ICONS
+]]></programlisting>
+ <para>
+ This property can be set by a Pager on one of its own toplevel windows
+ to indicate that the Window Manager need not provide icons for
+ iconified windows, for example if it is a taskbar and provides buttons
+ for iconified windows.
+ </para>
+ </sect2>
+ <sect2><title>_NET_WM_USER_TIME</title>
+ <programlisting><![CDATA[
+_NET_WM_USER_TIME CARDINAL/32
+]]></programlisting>
+ <para>
+This property contains the XServer time at which last user activity in this
+window took place.
+ </para>
+ <para>
+Clients should set this property on every new toplevel window (or on the window
+pointed out by the _NET_WM_USER_TIME_WINDOW property), before mapping
+the window, to the timestamp of the user interaction that caused the window to
+appear. A client that only deals with core events, might, for example, use the
+timestamp of the last KeyPress or ButtonPress event. ButtonRelease and
+KeyRelease events should not generally be considered to be user interaction,
+because an application may receive KeyRelease events from global keybindings,
+and generally release events may have later timestamp than actions that were
+triggered by the matching press events. Clients can obtain the timestamp that
+caused its first window to appear from the DESKTOP_STARTUP_ID environment
+variable, if the app was launched with startup notification. If the client does
+not know the timestamp of the user interaction that caused the first window to
+appear (e.g. because it was not launched with startup notification), then it
+should not set the property for that window. The special value of zero on a
+newly mapped window can be used to request that the window not be initially
+focused when it is mapped.
+ </para>
+ <para>
+If the client has the active window, it should also update this property
+on the window whenever there's user activity.
+ </para>
+ <para>
+Rationale: This property allows a Window Manager to alter the focus,
+stacking, and/or placement behavior of windows when they are
+mapped depending on whether the new window was created by a user
+action or is a "pop-up" window activated by a timer or some other
+event.
+ </para>
+ </sect2>
+
+ <sect2><title>_NET_WM_USER_TIME_WINDOW</title>
+ <programlisting><![CDATA[
+_NET_WM_USER_TIME_WINDOW WINDOW/32
+]]></programlisting>
+ <para>
+This property contains the XID of a window on which the client sets
+the _NET_WM_USER_TIME property. Clients should check whether the
+window manager supports _NET_WM_USER_TIME_WINDOW and fall back to
+setting the _NET_WM_USER_TIME property on the toplevel window if it
+doesn't.
+ </para>
+ <para>
+Rationale: Storing the frequently changing _NET_WM_USER_TIME property
+on the toplevel window itself causes every application that is
+interested in any of the properties of that window to be woken up
+on every keypress, which is particularly bad for laptops running on
+battery power.
+ </para>
+ </sect2>
+
+ <sect2><title>_NET_FRAME_EXTENTS</title>
+ <programlisting><![CDATA[
+_NET_FRAME_EXTENTS, left, right, top, bottom, CARDINAL[4]/32
+]]></programlisting>
+ <para>
+The Window Manager MUST set _NET_FRAME_EXTENTS to the extents of the
+window's frame. left, right, top and bottom are widths of the
+respective borders added by the Window Manager.
+ </para>
+ </sect2>
+
+ <sect2><title>_NET_WM_OPAQUE_REGION</title>
+ <programlisting><![CDATA[
+_NET_WM_OPAQUE_REGION, x, y, width, height, CARDINAL[][4]/32
+]]></programlisting>
+ <para>
+The Client MAY set this property to a list of 4-tuples [x, y, width,
+height], each representing a rectangle in window coordinates where the
+pixels of the window's contents have a fully opaque alpha value. If
+the window is drawn by the compositor without adding any transparency,
+then such a rectangle will occlude whatever is drawn behind it. When
+the window has an RGB visual rather than an ARGB visual, this property
+is not typically useful, since the effective opaque region of a
+window is exactly the bounding region of the window as set via the
+shape extension. For windows with an ARGB visual and also a bounding
+region set via the shape extension, the effective opaque region is
+given by the intersection of the region set by this property and the
+bounding region set via the shape extension. The compositing manager
+MAY ignore this hint.
+ </para>
+ <para>
+Rationale: This gives the compositing manager more room for optimizations.
+For example, it can avoid drawing occluded portions behind the window.
+ </para>
+ </sect2>
+</sect1>
+<sect1>
+ <title>Window Manager Protocols</title>
+ <sect2>
+ <title>_NET_WM_PING</title>
+ <para>
+This protocol allows the Window Manager to determine if the Client is still
+processing X events. This can be used by the Window Manager to determine if a
+window which fails to close after being sent WM_DELETE_WINDOW has stopped
+responding or has stalled for some other reason, such as waiting for user
+confirmation. A Client SHOULD indicate that it is willing to participate in
+this protocol by listing _NET_WM_PING in the WM_PROTOCOLS property of the
+client window.
+ </para>
+ <para>
+A Window Manager can use this protocol at any time by sending a client message
+as follows:
+ </para>
+ <programlisting><![CDATA[
+type = ClientMessage
+window = the respective client window
+message_type = WM_PROTOCOLS
+format = 32
+data.l[0] = _NET_WM_PING
+data.l[1] = timestamp
+data.l[2] = the respective client window
+other data.l[] elements = 0
+]]></programlisting>
+ <para>
+A participating Client receiving this message MUST send it back to the root
+window immediately, by setting window = root, and calling XSendEvent with
+the same event mask like all other root window messages in this specification use.
+The Client MUST NOT alter any field in the event other than the window. This
+includes all 5 longs in the data.l[5] array. The Window Manager can uniquely
+identify the ping by the timestamp and the data.l[2] field if necessary.
+Note that some older clients may not preserve data.l[2] through data.l[4].
+ </para>
+ <para>
+The Window Manager MAY kill the Client (using _NET_WM_PID) if it fails to
+respond to this protocol within a reasonable time.
+ </para>
+ <para>
+See also the implementation notes on <link linkend="KILLINGWINDOWS">killing hung processes</link>.
+ </para>
+ </sect2>
+ <sect2>
+ <title>_NET_WM_SYNC_REQUEST</title>
+ <para>
+This protocol uses the XSync extension (see <ulink
+url="http://freedesktop.org/cgi-bin/viewcvs.cgi/xorg/xc/doc/hardcopy/Xext/sync.PS.gz">the
+protocol specification</ulink> and <ulink
+url="http://freedesktop.org/cgi-bin/viewcvs.cgi/xorg/xc/doc/hardcopy/Xext/synclib.PS.gz">
+the library documentation</ulink>) to let client and window manager
+synchronize the repaint of the window manager frame and the client
+window. A client indicates that it is willing to participate in the
+protocol by listing _NET_WM_SYNC_REQUEST in the WM_PROTOCOLS property
+of the client window and storing the XID of an XSync counter in the
+property _NET_WM_SYNC_REQUEST_COUNTER. The initial value of this
+counter is not defined by this specification.
+ </para>
+ <para>
+A window manager uses this protocol by preceding a ConfigureNotify
+event sent to a client by a client message as follows:
+ </para>
+ <programlisting><![CDATA[
+type = ClientMessage
+window = the respective client window
+message_type = WM_PROTOCOLS
+format = 32
+data.l[0] = _NET_WM_SYNC_REQUEST
+data.l[1] = timestamp
+data.l[2] = low 32 bits of the update request number
+data.l[3] = high 32 bits of the update request number
+other data.l[] elements = 0
+]]></programlisting>
+
+ <para>
+After receiving one or more such message/ConfigureNotify pairs, and
+having handled all repainting associated with the ConfigureNotify
+events, the client MUST set the _NET_WM_SYNC_REQUEST_COUNTER to the 64
+bit number indicated by the data.l[2] and data.l[3] fields of the last
+client message received.
+ </para>
+ <para>
+By using either the Alarm or the Await mechanisms of the XSync
+extension, the window manager can know when the client has finished
+handling the ConfigureNotify events. The window manager SHOULD not
+resize the window faster than the client can keep up.
+ </para>
+ <para>
+The update request number in the client message is determined by the
+window manager subject to the restriction that it MUST NOT be 0. The
+number is generally intended to be incremented by one for each message
+sent. Since the initial value of the XSync counter is not defined by
+this specification, the window manager MAY set the value of the XSync
+counter at any time, and MUST do so when it first manages a new
+window.
+ </para>
+ </sect2>
+ <sect2>
+ <title>_NET_WM_FULLSCREEN_MONITORS</title>
+ <programlisting><![CDATA[
+_NET_WM_FULLSCREEN_MONITORS, CARDINAL[4]/32
+]]></programlisting>
+ <para>
+A read-only list of 4 monitor indices indicating the top, bottom, left, and
+right edges of the window when the fullscreen state is enabled. The indices
+are from the set returned by the Xinerama extension.
+ </para>
+ <para>
+Windows transient for the window with _NET_WM_FULLSCREEN_MONITORS set, such as
+those with type _NEW_WM_WINDOW_TYPE_DIALOG, are generally expected to be
+positioned (e.g. centered) with respect to only one of the monitors. This
+might be the monitor containing the mouse pointer or the monitor containing the
+non-full-screen window.
+ </para>
+ <para>
+A Client wishing to change this list MUST send a _NET_WM_FULLSCREEN_MONITORS
+client message to the root window. The Window Manager MUST
+keep this list updated to reflect the current state of the window.
+ </para>
+<programlisting><![CDATA[
+ window = the respective client window
+ message_type = _NET_WM_FULLSCREEN_MONITORS
+ format = 32
+ data.l[0] = the monitor whose top edge defines the top edge of the fullscreen window
+ data.l[1] = the monitor whose bottom edge defines the bottom edge of the fullscreen window
+ data.l[2] = the monitor whose left edge defines the left edge of the fullscreen window
+ data.l[3] = the monitor whose right edge defines the right edge of the fullscreen window
+ data.l[4] = source indication
+]]></programlisting>
+ <para>
+See <xref linkend="sourceindication"/> for details on the source indication.
+ </para>
+ <para>
+Virtual machine software may use this hint to have a virtual operating system
+instance that sees multiple monitors. The application window stretches over
+several monitors, giving the appearance that these monitors have been taken
+over by the guest virtual machine.
+ </para>
+ <para>
+This hint might also be used by a movie or presentation application allowing
+users to display the media spanned over several monitors.
+ </para>
+ <para>
+In both cases, the application would have some user interface allowing users
+to configure which monitors the application fullscreens to. The window manager
+need not provide such an interface, though it could.
+ </para>
+ <para>
+In the event of a change in monitor configuration, the application is
+responsible for re-computing the monitors on which it wants to appear.
+The window manager may continue using the same monitor indices as before
+or simply clear the list, returning to "normal" fullscreen.
+ </para>
+ </sect2>
+</sect1>
+
+
+<sect1>
+ <title>Other Properties</title>
+ <sect2>
+ <title>_NET_WM_FULL_PLACEMENT</title>
+ <para>
+By including this hint in _NET_SUPPORTED the Window Manager announces
+that it performs reasonable window placement for all window types it supports
+(for example centering dialogs on the mainwindow or whatever handling the
+Window Manager considers reasonable). This in turn means that Clients,
+when they detect that this hint is supported, SHOULD NOT abuse or often even
+use PPosition and USPosition hints for requesting placement. In particular:
+ </para>
+<itemizedlist>
+<listitem><para>USPosition is reserved to be used only to indicate that the position was
+specified by the user and MUST NOT be used for anything else (see ICCCM
+section 4.1.2.3 for details)</para></listitem>
+<listitem><para>PPosition SHOULD be used for for specifying position only if a specific
+position should be used. Position SHOULD NOT be specified for "default"
+placement such as centering dialog windows on their mainwindow.</para></listitem>
+</itemizedlist>
+ <para>
+Rationale: Window managers can often perform better placement (that may be
+even configurable) for windows than the application. However at the time of
+writing this it is problematic for Window managers to decide when to use them
+because many applications abuse positioning flags and/or provide unnecessary
+default positions.
+ </para>
+ <para>
+Note: The property is not used anywhere else besides being listed in _NET_SUPPORTED.
+ </para>
+ </sect2>
+</sect1>
+
+<sect1>
+ <title>Compositing Managers</title>
+ <para>
+A compositing manager is an X client that uses the Composite extension
+to redirect all windows to offscreen pixmaps, and the Damage extension
+to track when painting occur on those offscreen pixmaps. It is the
+responsibility of the compositing manager to paint the pixmaps on the
+screen, possibly adding effects like translucency or deformations.
+</para>
+ <para>
+This section specifies interactions between compositing managers
+and applications.
+</para>
+ <sect2>
+ <title>_NET_WM_CM_S<literal>n</literal> Manager Selection</title>
+ <para>
+For each screen they manage, compositing manager MUST acquire
+ownership of a selection named _NET_WM_CM_S<literal>n</literal>, where
+<literal>n</literal> is the screen number. Compositing managers MUST
+comply with the conventions for "Manager Selections" described in
+section 2.8 of the <citation><link linkend="ICCCM">ICCCM</link></citation>.
+</para>
+ </sect2>
+ <sect2>
+ <title>WM_TRANSIENT_FOR for override-redirect windows</title>
+ <para>
+The WM_TRANSIENT_FOR property is defined by the <citation><link
+linkend="ICCCM">ICCCM</link></citation>.for managed windows. This
+specification extends the use of the property to override-redirect
+windows. If an override-redirect is a pop-up on behalf of another
+window, then the Client SHOULD set WM_TRANSIENT_FOR on the
+override-redirect to this other window.
+ </para>
+ <para>
+As an example, a Client should set WM_TRANSIENT_FOR on dropdown menus
+to the toplevel application window that contains the menubar.
+ </para>
+ </sect2>
+</sect1>
+<sect1>
+ <title>Implementation notes</title>
+ <sect2>
+ <title>Desktop/workspace model</title>
+ <para>
+This spec assumes a desktop model that consists of one or more completely
+independent desktops which may or may not be larger than the screen area.
+When a desktop is larger than the screen it is left to the Window Manager if
+it will implement scrolling or paging.
+ </para>
+ </sect2>
+ <sect2>
+ <title>File Manager desktop</title>
+ <para>
+This spec suggests implementing the file manager desktop by mapping a
+desktop-sized window (no shape) to all desktops, with
+_NET_WM_WINDOW_TYPE_DESKTOP. This makes the desktop focusable and greatly
+simplifies implementation of the file manager. It is also faster than
+managing lots of small shaped windows. The file manager draws the background
+on this window. There should be a root property with a window handle for use
+in applications that want to draw the background (xearth).
+ </para>
+ </sect2>
+ <sect2>
+ <title>Implementing enhanced support for application transient windows</title>
+ <para>
+If the WM_TRANSIENT_FOR property is set to None or Root window, the window
+should be treated as a transient for all other windows in the same group. It
+has been noted that this is a slight ICCCM violation, but as this behavior is
+pretty standard for many toolkits and window managers, and is extremely
+unlikely to break anything, it seems reasonable to document it as standard.
+ </para>
+ </sect2>
+ <sect2 id="URGENCY">
+ <title>Urgency</title>
+ <para>
+Windows expecting immediate user action should indicate this using the
+urgency bit in the WM_HINTS.flags property, as defined in the ICCCM.
+ </para>
+ </sect2>
+ <sect2 id="NORESIZE">
+ <title>Fixed size windows</title>
+ <para>
+ Windows can indicate that they are non-resizable by setting minheight = maxheight and minwidth = maxwidth in the ICCCM WM_NORMAL_HINTS property. The Window Manager MAY decorate such windows differently.
+ </para>
+ </sect2>
+ <sect2>
+ <title>Pagers and Taskbars</title>
+ <para>
+ This specification attempts to make reasonable provisions for window
+ manager independent pagers and taskbars. Window Managers that require
+ / desire additional functionality beyond what can be achieved using the
+ mechanisms set out in this specification may choose to implement their
+ own pagers, which communicate with the Window Manager using further,
+ window manager specific hints, or some other means.
+ </para>
+ <para>
+ Pagers should decide whether to show a miniature version of a
+ window using the following guidelines:
+ <itemizedlist>
+ <listitem>
+ <para>
+ If either _NET_WM_STATE_SKIP_PAGER or
+ _NET_WM_STATE_HIDDEN are set on a window, then the
+ pager should not show that window.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The pager may choose not to display windows with
+ certain semantic types; this spec has no
+ recommendations, but common practice is to avoid
+ displaying _NET_WM_WINDOW_TYPE_DOCK for example.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If the _NET_WM_STATE_SKIP_PAGER and
+ _NET_WM_STATE_HIDDEN hints are not present, and the
+ Window Manager claims to support _NET_WM_STATE_HIDDEN,
+ then the window should be shown if it's in either
+ NormalState or IconicState.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ For Window Managers that do not support
+ _NET_WM_STATE_HIDDEN, the pager should
+ not show windows in IconicState. These Window
+ Managers are probably using an older version
+ of this specification.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </sect2>
+ <sect2>
+ <title>Window Geometry</title>
+ <para>
+Window manager implementors should refer to the ICCCM for definitive
+specifications of how to handle MapRequest and ConfigureRequest events.
+However, since these aspects of the ICCCM are easily misread, this
+document offers the following clarifications:
+ </para>
+ <itemizedlist>
+ <listitem><para>
+Window Managers MUST honor the win_gravity field of WM_NORMAL_HINTS
+for both MapRequest <emphasis>and</emphasis> ConfigureRequest events
+(ICCCM Version 2.0, &sect;4.1.2.3 and &sect;4.1.5)
+ </para></listitem>
+ <listitem><para>
+When generating synthetic ConfigureNotify events, the position given
+MUST be the top-left corner of the client window in relation to the
+origin of the root window (i.e., ignoring win_gravity)
+(ICCCM Version 2.0, &sect;4.2.3)
+ </para></listitem>
+ <listitem><para>
+Window Managers maintain a reference point for each client window and place
+the window relative to this reference point depending on the window's
+win_gravity as follows:
+ </para>
+ <informaltable>
+ <tgroup cols="2">
+ <tbody>
+ <row>
+ <entry>win_gravity:</entry>
+ <entry>placed at the reference point</entry>
+ </row>
+ <row>
+ <entry>StaticGravity</entry>
+ <entry>the left top corner of the client window</entry>
+ </row>
+ <row>
+ <entry>NorthWestGravity</entry>
+ <entry>the left top corner of the frame window</entry>
+ </row>
+ <row>
+ <entry>NorthGravity</entry>
+ <entry>the center of the frame window's top side</entry>
+ </row>
+ <row>
+ <entry>NorthEastGravity</entry>
+ <entry>the right top corner of the frame window</entry>
+ </row>
+ <row>
+ <entry>EastGravity</entry>
+ <entry>the center of the frame window's right side</entry>
+ </row>
+ <row>
+ <entry>SouthEastGravity</entry>
+ <entry>the right bottom corner of the frame window</entry>
+ </row>
+ <row>
+ <entry>SouthGravity</entry>
+ <entry>the center of the frame window's bottom side</entry>
+ </row>
+ <row>
+ <entry>SouthWestGravity</entry>
+ <entry>the left bottom corner of the frame window</entry>
+ </row>
+ <row>
+ <entry>WestGravity</entry>
+ <entry>the center of the frame window's left side</entry>
+ </row>
+ <row>
+ <entry>CenterGravity</entry>
+ <entry>the center of the frame window</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </listitem>
+ <listitem><para>
+Applications are free to change their win_gravity setting at any time.
+ </para>
+ <para>
+If an Application changes its win_gravity then the Window Manager should
+adjust the reference point, so that the client window will not move as the
+result. For example if the Application's win_gravity was NorthWestGravity and
+reference point was at the top-left corner of the frame window, then after
+change of win_gravity to SouthEastGravity the reference point should be
+adjusted to point to the lower-right corner of the frame.
+ </para>
+ <note>
+ <para>
+Changing the win_gravity for a single configure request and back afterwards is
+unlikely to work as intended, due to a race condition. The window manager sees
+a property notify for WM_NORMAL_HINTS, followed by the configure request,
+followed by another property notify for WM_NORMAL_HINTS. By the time the
+window manager gets around to request the changed WM_NORMAL_HINTS in response
+to the first property notify, the server may have already processed the second
+property change.
+ </para>
+ <para>
+If the window manager supports it, applications should use
+_NET_MOVERESIZE_WINDOW with a specified gravity to avoid this problem.
+ </para>
+ </note>
+ </listitem>
+ <listitem><para>
+If the Application requests a new position (x, y) (and possibly also a
+new size), the Window Manager calculates a new reference point (ref_x, ref_y),
+based on the client window's (possibly new) size (width, height), border
+width (bw) and win_gravity as explained in the table below.
+ </para>
+ <para>
+The Window Manager will use the new reference point until the next request for
+a new position.
+ </para>
+ <informaltable>
+ <tgroup cols="3">
+ <tbody>
+ <row>
+ <entry>win_gravity:</entry>
+ <entry>ref_x:</entry>
+ <entry>ref_y:</entry>
+ </row>
+ <row>
+ <entry>StaticGravity</entry>
+ <entry>x</entry>
+ <entry>y</entry>
+ </row>
+ <row>
+ <entry>NorthWestGravity</entry>
+ <entry>x-bw</entry>
+ <entry>y-bw</entry>
+ </row>
+ <row>
+ <entry>NorthGravity</entry>
+ <entry>x+(width/2)</entry>
+ <entry>y-bw</entry>
+ </row>
+ <row>
+ <entry>NorthEastGravity</entry>
+ <entry>x+width+bw</entry>
+ <entry>y-bw</entry>
+ </row>
+ <row>
+ <entry>EastGravity</entry>
+ <entry>x+width+bw</entry>
+ <entry>y+(height/2)</entry>
+ </row>
+ <row>
+ <entry>SouthEastGravity</entry>
+ <entry>x+width+bw</entry>
+ <entry>y+height+bw</entry>
+ </row>
+ <row>
+ <entry>SouthGravity</entry>
+ <entry>x+(width/2)</entry>
+ <entry>y+height+bw</entry>
+ </row>
+ <row>
+ <entry>SouthWestGravity</entry>
+ <entry>x-bw</entry>
+ <entry>y+height+bw</entry>
+ </row>
+ <row>
+ <entry>WestGravity</entry>
+ <entry>x-bw</entry>
+ <entry>y+(height/2)</entry>
+ </row>
+ <row>
+ <entry>CenterGravity</entry>
+ <entry>x+(width/2)</entry>
+ <entry>y+(height/2)</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </listitem>
+ <listitem><para>
+If an Application requests just a new size, its reference point does not
+move. So for example if client window has win_gravity SouthEastGravity and
+is resized, the bottom right corner of its frame will not move but instead
+the top left corner will be adjusted by the difference in size.
+ </para></listitem>
+ <listitem><para>
+When calculating the reference point at the time of initial placement,
+the Window Manager should take the initial window's size into consideration,
+as if it was the frame for this window.
+ </para></listitem>
+ </itemizedlist>
+ </sect2>
+ <sect2>
+ <title>Window-in-Window MDI</title>
+ <para>
+ The authors of this specification acknowledge that there is no standard
+ method to allow the Window Manager to manage windows that are part of a
+ Window-in-Window MDI application. Application authors are advised to
+ use some other form of MDI, or to propose a mechanism to be included in
+ a future revision of this specification.
+ </para>
+ </sect2>
+ <sect2 id="KILLINGWINDOWS">
+ <title>Killing Hung Processes</title>
+ <para>
+If processes fail to respond to the _NET_WM_PING protocol _NET_WM_PID may be
+used in combination with the ICCCM specified WM_CLIENT_MACHINE to
+attempt to kill a process.
+ </para>
+
+ <para>
+WM_CLIENT_MACHINE is usually set by calling XSetWMProperties(). The hostname for the current host can be be retrieved using gethostname(), when gethostname()
+is not available on the platform implementors may use the value of the
+nodename field of struct utsname as returned by uname(). Note also that the value of WM_CLIENT_MACHINE is not guaranteed
+to be a fully fully-qualified domain name of the host. An example of how to
+retrieve the hostname:
+ </para>
+ <para>
+ <programlisting><![CDATA[
+int net_get_hostname (char *buf, size_t maxlen)
+{
+#ifdef HAVE_GETHOSTNAME
+ if (buf == NULL) return 0;
+
+ gethostname (buf, maxlen);
+ buf [maxlen - 1] = '\0';
+
+ return strlen(buf);
+#else
+ struct utsname name;
+ size_t len;
+
+ if (buf == NULL) return 0;
+
+ uname (&name);
+ len = strlen (name.nodename);
+
+ if (len >= maxlen) len = maxlen - 1;
+ strncpy (buf, name.nodename, len);
+ buf[len] = '\0';
+
+ return len;
+#endif
+}
+]]></programlisting>
+ </para>
+ </sect2>
+
+ <sect2 id="STACKINGORDER">
+ <title>Stacking order</title>
+ <para>
+ To obtain good interoperability between different Desktop Environments,
+ the following layered stacking order is recommended, from the bottom:
+ <itemizedlist>
+ <listitem><para>windows of type _NET_WM_TYPE_DESKTOP</para></listitem>
+ <listitem><para>windows having state _NET_WM_STATE_BELOW</para></listitem>
+ <listitem><para>windows not belonging in any other layer</para></listitem>
+ <listitem><para>windows of type _NET_WM_TYPE_DOCK (unless they have
+ state _NET_WM_TYPE_BELOW) and windows having state
+ _NET_WM_STATE_ABOVE</para></listitem>
+ <listitem><para>focused windows having state
+ _NET_WM_STATE_FULLSCREEN</para></listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ Windows that are transient for another window should be kept
+ above this window.
+ </para>
+ <para>
+ The window manager may choose to put some windows in different
+ stacking positions, for example to allow the user to bring currently
+ a active window to the top and return it back when the window looses
+ focus.
+ </para>
+ </sect2>
+
+ <sect2 id="sourceindication">
+ <title>Source indication in requests</title>
+ <para>
+ Some requests from Clients include type of the Client, for example
+ the _NET_ACTIVE_WINDOW message. Currently the types can be
+ 1 for normal applications, and 2 for pagers and other Clients
+ that represent direct user actions (the Window Manager may decide
+ to treat requests from applications differently than requests
+ that are result of direct user actions).
+ Clients that support only older version of this spec will have 0
+ as their source indication, thus not specifying their source at all.
+ This also may mean that some of the fields in the message comply
+ only with the older specification version.
+ </para>
+ </sect2>
+ </sect1>
+ <sect1>
+ <title>References</title>
+ <variablelist>
+ <varlistentry>
+ <term>[UTF8]</term>
+ <listitem>
+ <para id="UTF8">
+ F. Yergeau,"UTF-8, a transformation format of ISO 10646", RFC 2279
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>[ICCCM]</term>
+ <listitem>
+ <para id="ICCCM">
+ David Rosenthal and Stuart W. Marks, "Inter-Client Communication
+ Conventions Manual (Version 2.0)", X Consortium Standard, X Version 11,
+ Release 6.3
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect1>
+ <sect1>
+ <title>Copyright</title>
+ <para>
+Copyright (C) 2000-2003 See Contributors List
+ </para>
+ <para>
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom
+the Software is furnished to do so, subject to the following
+conditions:
+ </para>
+ <para>
+The above copyright notice and this permission notice shall
+be included in all copies or substantial portions of the
+Software.
+ </para>
+ <para>
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+ </para>
+ </sect1>
+ <sect1>
+ <title>Contributors</title>
+
+ <para>Sasha Vasko</para>
+ <para>Bradley T. Hughes</para>
+ <para>Dominik Vogt</para>
+ <para>Havoc Pennington</para>
+ <para>Jeff Raven</para>
+ <para>Jim Gettys</para>
+ <para>John Harper</para>
+ <para>Julian Adams</para>
+ <para>Matthias Ettrich</para>
+ <para>Micheal Rogers</para>
+ <para>Nathan Clemons</para>
+ <para>Tim Janik</para>
+ <para>Tomi Ollila</para>
+ <para>Sam Lantinga</para>
+ <para>The Rasterman</para>
+ <para>Paul Warren</para>
+ <para>Owen Taylor</para>
+ <para>Marko Macek</para>
+ <para>Greg Badros</para>
+ <para>Matthias Clasen</para>
+ <para>David Rosenthal</para>
+ <para>Lubos Lunak</para>
+ <para>Rob Adams</para>
+ <para>Thomas Fitzsimmons</para>
+ <para>Olivier Chapuis</para>
+ <para>S&oslash;ren Sandmann</para>
+ <para>Grant Patterson</para>
+ </sect1>
+ <sect1>
+ <title>Change history</title>
+ <sect2>
+ <title>Changes since 1.4draft</title>
+ <itemizedlist>
+ <listitem><para>
+Added _NET_WM_STATE_FOCUSED.
+ </para></listitem>
+ <listitem><para>
+Added _NET_WM_OPAQUE_REGION
+ </para></listitem>
+ </itemizedlist>
+ </sect2>
+ <sect2>
+ <title>Changes since 1.3</title>
+ <itemizedlist>
+ <listitem><para>
+Added _NET_WM_MOVERESIZE_CANCEL.
+ </para></listitem>
+ <listitem><para>
+New window types to be used on override-redirect windows:
+_NET_WM_WINDOW_TYPE_DROPDOWN_MENU, _NET_WM_WINDOW_TYPE_POPUP_MENU,
+_NET_WM_WINDOW_TYPE_TOOLTIP, _NET_WM_WINDOW_TYPE_NOTIFICATION,
+_NET_WM_WINDOW_TYPE_COMBO, and _NET_WM_WINDOW_TYPE_DND
+ </para></listitem>
+ <listitem><para>
+New _NET_WM_CM_Sn manager selection for compositing managers.
+ </para></listitem>
+ <listitem><para>
+Added note WM_TRANSIENT_FOR for override-redirect windows
+ </para></listitem>
+ <listitem><para>
+Added _NET_WM_USER_TIME_WINDOW.
+ </para></listitem>
+ <listitem><para>
+Added _NET_WM_FULL_PLACEMENT.
+ </para></listitem>
+ <listitem><para>
+Added _NET_WM_FULLSCREEN_MONITORS.
+ </para></listitem>
+ </itemizedlist>
+ </sect2>
+ <sect2>
+ <title>Changes since 1.2</title>
+ <itemizedlist>
+ <listitem><para>
+Added source indication to _NET_CLOSE_WINDOW, _NET_WM_MOVERESIZE,
+_NET_MOVERESIZE_WINDOW, _NET_WM_DESKTOP and _NET_WM_STATE message.
+ </para></listitem>
+ <listitem><para>
+Added _NET_WM_SYNC_REQUEST to allow synchronized repaint of
+application window and window manager frame during opaque resize.
+ </para></listitem>
+ <listitem><para>
+Added _NET_REQUEST_FRAME_EXTENTS and _NET_FRAME_EXTENTS to allow a
+client to retrieve its window's frame extents.
+ </para></listitem>
+ <listitem><para>
+Added new state _NET_WM_STATE_DEMANDS_ATTENTION.
+ </para></listitem>
+ <listitem><para>
+Added timestamp, source indication and requestor's active window
+fields to the _NET_ACTIVE_WINDOW message.
+ </para></listitem>
+ <listitem><para>
+Added _NET_RESTACK_WINDOW message.
+ </para></listitem>
+ <listitem><para>
+Added new property _NET_WM_STRUT_PARTIAL to allow partial-width struts.
+ </para></listitem>
+ <listitem><para>
+Rewrote the implementation notes on "Window Movement", retitled it
+to "Window Geometry".
+ </para></listitem>
+ <listitem><para>
+Rewrote the implementation notes on "Urgency", making it clear that
+the hint is not just about dialogs.
+ </para></listitem>
+ <listitem><para>
+Fixed the specification of the X and Y members of _NET_DESKTOP_LAYOUT
+and renamed them to columns and row for clarity.
+ </para></listitem>
+ <listitem><para>
+Change the description of _NET_WM_STATE_MODAL to no longer require apps to
+break the ICCCM for group-modal windows, but still support the
+WM_TRANSIENT_FOR=root dialect.
+ </para></listitem>
+ <listitem><para>
+Specified that (yet) unused fields in client messages must be set to 0.
+ </para></listitem>
+ <listitem><para>
+_NET_WM_PING message now has the client window identified in data.l[2] field.
+ </para></listitem>
+ <listitem><para>
+Added _NET_WM_USER_TIME to detect user activity in windows.
+ </para></listitem>
+ <listitem><para>
+Explicitly specify that the window manager should restore original geometry
+when _NET_WM_STATE_FULLSCREEN is reset.
+ </para></listitem>
+ </itemizedlist>
+ </sect2>
+ <sect2>
+ <title>Changes since 1.1</title>
+ <itemizedlist>
+ <listitem><para>
+ Changed WM_CLIENT_NAME(STRING) from suggested to required for _NET_WM_PID.
+ </para></listitem>
+ <listitem><para>
+ Specification and sample code for the content of WM_CLIENT_NAME(STRING).
+ </para></listitem>
+ <listitem><para>
+ Added _NET_WM_WINDOW_TYPE_SPLASH, _NET_WM_WINDOW_TYPE_UTILITY.
+ </para></listitem>
+ <listitem><para>
+ Added _NET_WM_STATE_FULLSCREEN.
+ </para></listitem>
+ <listitem><para>
+ Added _NET_WM_ALLOWED_ACTIONS.
+ </para></listitem>
+ <listitem><para>
+ Added _NET_WM_STATE_HIDDEN and clarified purpose of
+ _NET_WM_STATE_SKIP_PAGER and _NET_WM_STATE_SKIP_TASKBAR. Changed
+ section on virtual desktop implementation to suggest ICCCM compliance
+ regarding IconicState, using _NET_WM_STATE_HIDDEN to avoid confusion.
+ Added implementation note for pagers on when to display a window.
+ </para></listitem>
+ <listitem><para>
+ Added button field and new directions for keyboard-initiated actions
+ to the _NET_WM_MOVERESIZE message.
+ </para></listitem>
+ <listitem><para>
+ Added advice on removing _NET_WM_STATE and _NET_WM_DESKTOP when a window
+ is withdrawn.
+ </para></listitem>
+ <listitem><para>
+ Added _NET_DESKTOP_LAYOUT to allow a Pager to specify inter-desktop geometry.
+ </para></listitem>
+ <listitem><para>
+ Added _NET_SHOWING_DESKTOP.
+ </para></listitem>
+ <listitem><para>
+ Added _NET_WM_STATE_ABOVE and _NET_WM_STATE_BELOW and a recommended layered
+ stacking order.
+ </para></listitem>
+ <listitem><para>
+ Added _NET_MOVERESIZE_WINDOW.
+ </para></listitem>
+ <listitem><para>
+ Improve markup of citations.
+ </para></listitem>
+ <listitem><para>
+ Explain _NET_DESKTOP_GEOMETRY and _NET_WM_HANDLED_ICONS in more detail and
+ improve the explanation of WM_CLIENT_MACHINE in
+ <xref linkend="KILLINGWINDOWS"/>.
+ </para></listitem>
+ <listitem><para>
+ Add Lubos Lunak to the list of contributors.
+ </para></listitem>
+ </itemizedlist>
+ </sect2>
+ <sect2>
+ <title>Changes since 1.0</title>
+ <itemizedlist>
+ <listitem><para>
+Fix doctype, add author info, update data.
+ </para></listitem>
+ <listitem><para>
+Change specification description wording to be more inclusive, and to reflect the joint nature of the specification.
+ </para></listitem>
+ <listitem><para>
+Fix miscellaneous typographical, grammar and spelling errors.
+ </para></listitem>
+ <listitem><para>
+Clarified _NET_SUPPORTED to include ALL atoms, not just the property names.
+ </para></listitem>
+ <listitem><para>
+Various corrections to use of MUST and SHOULD.
+ </para></listitem>
+ <listitem><para>
+Fix problem in _NET_WM_ICON where 'bytes' should have been 'cardinals'
+ </para></listitem>
+ <listitem><para>
+Replaced ISO-8559-1 characters with entities.
+ </para></listitem>
+ </itemizedlist>
+ </sect2>
+ <sect2>
+ <title>Changes since 1.0pre5</title>
+ <itemizedlist>
+ <listitem><para>
+Change history moved to end.
+ </para></listitem>
+ <listitem><para>
+UTF-8 Reference updated.
+ </para></listitem>
+ <listitem><para>
+Window Gravity information updated.
+ </para></listitem>
+ <listitem><para>
+Copyright Added.
+ </para></listitem>
+ <listitem><para>
+Minor typo corrections.
+ </para></listitem>
+ </itemizedlist>
+ </sect2>
+ <sect2>
+ <title>Changes since 1.0pre4</title>
+ <itemizedlist>
+ <listitem><para>
+Clarified the interpretation of client-provided geometries on large desktops.
+ </para></listitem>
+ <listitem><para>
+Added more explanation for _NET_DESKTOP_NAMES.
+ </para></listitem>
+ <listitem><para>
+Added _NET_WM_ICON_NAME and _NET_WM_VISIBLE_ICON_NAME.
+ </para></listitem>
+ <listitem><para>
+Tried to improve the wording of _NET_WM_STRUT explanation.
+ </para></listitem>
+ <listitem><para>
+Changed _NET_WORKAREA to an array of viewport-relative geometries.
+ </para></listitem>
+ <listitem><para>
+Updated list of <quote>dependent</quote> properties for _NET_NUMBER_OF_DESKTOPS
+to include _NET_WORKAREA and _NET_DESKTOP_VIEWPORT.
+ </para></listitem>
+ <listitem><para>
+Tidied formatting of all client messages.
+ </para></listitem>
+ </itemizedlist>
+ </sect2>
+ <sect2>
+ <title>Changes since 1.0pre3</title>
+ <itemizedlist>
+ <listitem><para>
+Added information about common non-ICCCM features.
+ </para></listitem>
+ <listitem><para>
+Added explanation of sending messages to the root window.
+ </para></listitem>
+ <listitem><para>
+Removed XA_ prefix from type names.
+ </para></listitem>
+ <listitem><para>
+Clarified that <quote>mapping order</quote> refers to inital mapping
+and specify the directions of both orders.
+ </para></listitem>
+ <listitem><para>
+Clarified that desktops have a common size specified by _NET_DESKTOP_GEOMETRY.
+ </para></listitem>
+ <listitem><para>
+Rewrote explanation of _NET_DESKTOP_VIEWPORT.
+ </para></listitem>
+ <listitem><para>
+Tidied formatting of _NET_CURRENT_DESKTOP.
+ </para></listitem>
+ <listitem><para>
+Replaced <quote>window handle</quote> by <quote>window ID</quote>.
+ </para></listitem>
+ <listitem><para>
+Tidied formatting of _NET_WORKAREA.
+ </para></listitem>
+ <listitem><para>
+Rewrote the motivation for _NET_VIRTUAL_ROOTS.
+ </para></listitem>
+ <listitem><para>
+Added advice on Pointer grabs to _NET_WM_MOVERESIZE.
+ </para></listitem>
+ <listitem><para>
+Fixed typos in _NET_WM_STATE.
+ </para></listitem>
+ <listitem><para>
+Added _NET_WM_STATE_SKIP_PAGER.
+ </para></listitem>
+ <listitem><para>
+Tidied formatting of _NET_WM_STRUT.
+ </para></listitem>
+ <listitem><para>
+Tidied formatting of _NET_WM_ICON_GEOMETRY.
+ </para></listitem>
+ </itemizedlist>
+ </sect2>
+ <sect2>
+ <title>Changes since 1.0pre2</title>
+ <itemizedlist>
+ <listitem><para>
+_NET_SET_NUMBER_OF_DESKTOPS -> _NET_NUMBER_OF_DESKTOPS for consistency.
+ </para></listitem>
+ <listitem><para>
+_NET_WM_VISIBLE_NAME_STRING -> _NET_WM_VISIBLE_NAME for consistency.
+ </para></listitem>
+ <listitem><para>
+_NET_WM_STATE: added explanation of permitted extensions. Added explanation of
+being set / not set.
+ </para></listitem>
+ <listitem><para>
+Spellchecked, corrected various typos.
+ </para></listitem>
+ <listitem><para>
+UTF8 -> UTF-8 for consistency.
+ </para></listitem>
+ <listitem><para>
+added references to the ICCCM an UTF-8 (incomplete).
+ </para></listitem>
+ <listitem><para>
+added data and event formats where missing.
+ </para></listitem>
+ <listitem><para>
+clarified _NET_SUPPORTING_WM_CHECK.
+ </para></listitem>
+ <listitem><para>
+fixed formatting of _NET_CLOSE_WINDOW message.
+ </para></listitem>
+ </itemizedlist>
+ </sect2>
+ <sect2>
+ <title>Changes since 1.0pre1</title>
+ <itemizedlist>
+ <listitem><para>
+Removed implementation note concerning Gnome's (potential) file manager behavior.
+ </para></listitem>
+ <listitem><para>
+The Window Movement section of the implementation notes has been revised.
+ </para></listitem>
+ </itemizedlist>
+ </sect2>
+ <sect2>
+ <title>Changes since 1.9f</title>
+ <itemizedlist>
+ <listitem><para>
+Revised revision number for first accepted release 1.9XX -> 1.0preXX.
+ </para></listitem>
+ <listitem><para>
+Prerequisites for adoption of this specification added.
+ </para></listitem>
+ <listitem><para>
+Tidied formatting of _NET_CURRENT_DESKTOP for consistency.
+ </para></listitem>
+ <listitem><para>
+Tidied formatting of _NET_ACTIVE_WINDOW for consistency. Removed doubled text.
+ </para></listitem>
+ <listitem><para>
+Tidied formatting of _NET_WM_DESKTOP for consistency.
+ </para></listitem>
+ <listitem><para>
+Killing Hung Processes implementation note added. _NET_WM_PID and _NET_WM_PING now link to this.
+ </para></listitem>
+ <listitem><para>
+Clarified x_root and y_root meaning for _NET_WM_MOVERESIZE.
+ </para></listitem>
+ <listitem><para>
+Added contributor list.
+ </para></listitem>
+ </itemizedlist>
+ </sect2>
+ <sect2>
+ <title>Changes since 1.9e</title>
+ <itemizedlist>
+ <listitem><para>
+Added _NET_WM_VISIBLE_NAME_STRING
+ </para></listitem>
+ <listitem><para>
+Removed ambiguity from _NET_NUMBER_OF_DESKTOPS and _NET_DESKTOP_NAMES in combination.
+ </para></listitem>
+ <listitem><para>
+Set _NET_WM_MOVERESIZE format to 32 for consistency.
+ </para></listitem>
+ <listitem><para>
+Removed _NET_PROPERTIES.
+ </para></listitem>
+ <listitem><para>
+Removed comment from _NET_WM_MOVERESIZE.
+ </para></listitem>
+ </itemizedlist>
+ </sect2>
+ <sect2>
+ <title>Changes since 1.9d</title>
+ <itemizedlist>
+ <listitem><para>
+Added _NET_VIRTUAL_ROOTS
+ </para></listitem>
+ <listitem><para>
+Added note about ICCCM compliant window moves.
+ </para></listitem>
+ <listitem><para>
+Added _NET_WM_HANDLED_ICONS
+ </para></listitem>
+ <listitem><para>
+Added _NET_SUPPORTING_WM_CHECK
+ </para></listitem>
+ <listitem><para>
+Removed degrees of activation
+ </para></listitem>
+ </itemizedlist>
+ </sect2>
+ <sect2>
+ <title>Changes since 1.9c</title>
+ <itemizedlist>
+ <listitem>
+ <para>
+Removed packaging of hints into 2 X properties. Jim Gettys points out that the
+performance gains of fewer round trips can be better achieved using Xlib
+routines.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+Clarified that _NET_DESKTOP_VIEWPORT is in pixels
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+_NET_DESKTOP_VIEWPORT is now an array, one for each desktop, to allow for
+different active viewports on different desktops
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+_NET_WM_STRUT now only applies on desktops on which the client is visible
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+Introduced RFC 2119 language, and attempted to clarify the roles of the Window
+Manager, Pagers and Applications
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+Added _NET_WM_NAME
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+_NET_DESKTOP_NAMES now in UTF8
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+Desktops now start from 0
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+Added _NET_WM_PID
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+Added _NET_WM_PING protocol
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+Added _NET_WM_STATE_SKIP_TASKBAR
+ </para>
+ </listitem>
+ </itemizedlist>
+ </sect2>
+
+ <sect2>
+ <title>Changes since 1.9b</title>
+ <itemizedlist>
+ <listitem><para>Removed _NET_NUMBER_OF_DESKTOPS client message, as it overlaps unnecessarily with _NET_{INSERT/DELETE}_DESKTOP.</para>
+ </listitem>
+ <listitem><para>Replaced _NET_WM_LAYER and _NET_WM_HINTS with _NET_WM_WINDOW_TYPE functional hint.</para></listitem>
+ <listitem><para>Changed _NET_WM_STATE to a list of atoms, for extensibility.</para></listitem>
+ <listitem><para>Expanded description of _NET_WORKAREA and _NET_WM_STRUT.</para></listitem>
+ <listitem><para>Removed _NET_WM_SIZEMOVE_NOTIFY protocol. </para></listitem>
+ <listitem><para>Added degrees of activation to _NET_ACTIVE_WINDOW client message</para></listitem>
+ <listitem><para>Added _NET_WM_ICON</para></listitem>
+ <listitem><para>My comments are in [[ ]]. Comments from Marko's draft are in [[MM: ]]</para></listitem>
+ </itemizedlist>
+ </sect2>
+ </sect1>
+</article>
+