450 lines
16 KiB
Plaintext
450 lines
16 KiB
Plaintext
Startup notification protocol
|
|
===
|
|
|
|
This document specifies a mechanism allowing a desktop environment to
|
|
track application startup, to provide user feedback and other
|
|
features.
|
|
|
|
Version: 0.1
|
|
Revised: October 20, 2002
|
|
Authors: Lubos Lunak, Havoc Pennington
|
|
|
|
Terms
|
|
===
|
|
|
|
Launch: a "startup event" such as opening a new window, opening a new
|
|
application, or adding a panel applet. Note that the
|
|
launch may or may not involve creating a new UNIX process.
|
|
Launcher: code which starts up a launch
|
|
Launchee: code which is started up by the launcher
|
|
|
|
X Messages
|
|
===
|
|
|
|
"X messages" are a mechanism for sending text strings between X
|
|
clients.
|
|
|
|
To send a string as an X message, a client does the following:
|
|
|
|
- Creates an X window to be used to identify the message
|
|
uniquely. This window need not be mapped, and may be
|
|
a child of any window.
|
|
|
|
- Interns atoms type_atom and type_atom_begin indicating
|
|
the type of message.
|
|
|
|
- Decides on a target_xwindow; this is the root window
|
|
for "broadcast" messages, or a specific client's window.
|
|
|
|
- Send a series of client messages to the target X window, where each
|
|
client message contains a portion of the string. The client
|
|
messages should have the window field set to the X window
|
|
identifying the message, the format field set to 8, and
|
|
the message_type field set to the type of message, type_atom_begin
|
|
for the first client message and type_atom for any following client
|
|
messages.
|
|
|
|
The last byte used in the last client message must be nul, and no
|
|
intermediate bytes may be nul. The nul byte identifies
|
|
the end of the message.
|
|
|
|
Client messages must be sent to the chosen target_xwindow, with the
|
|
event mask PropertyChangeMask.
|
|
|
|
(FIXME this is a bad choice of mask, as we get a bunch of annoying
|
|
PropertyNotify events; but which mask would be better?)
|
|
|
|
Attachment "A" contains example code.
|
|
|
|
- Destroys the unique X window used to identify the message.
|
|
The window can be destroyed immediately, it is only used
|
|
for its window ID.
|
|
|
|
Implementations may impose a maximum length of message they are
|
|
willing to accept. Typically this length will be reasonably low,
|
|
perhaps 4K of data.
|
|
|
|
Key-value strings
|
|
===
|
|
|
|
The specific strings sent during startup notification encode a message
|
|
type, followed by a list of key-value pairs. Here is an example:
|
|
|
|
new: NAME="Hello World" PID=252
|
|
|
|
A string listing key-value pairs works as follows:
|
|
|
|
- the entire string must be valid UTF-8. Invalid strings should be
|
|
discarded as corrupt, as accepting bad data leads to
|
|
interoperability problems. (Learn from web browsers.)
|
|
|
|
Although the string is UTF-8, parsing is specified in terms of
|
|
bytes not characters in the below specification.
|
|
|
|
- all bytes up to the first ':' byte indicate the type of the
|
|
message. If the message contains no ':' byte it should be discarded
|
|
as corrupt.
|
|
|
|
- To find the start of a key, the ':' byte delimiting the message
|
|
type must be skipped. Any space (' ') bytes following it must also
|
|
be skipped. (Other kinds of whitespace must not be skipped.) The
|
|
first non-' ' byte after the ':' byte is the start of the first
|
|
key.
|
|
|
|
- All bytes until the next '=' byte form the name of the
|
|
key. The '=' byte should be discarded, as it delimits the
|
|
key from the value.
|
|
|
|
- Parsing of the value begins with the byte immediately following the
|
|
'=' byte. The value is parsed
|
|
as follows.
|
|
|
|
There are two dimensions, "escaped" and "quoted", creating 4
|
|
states:
|
|
|
|
- escaped = TRUE quoted = TRUE
|
|
. the current byte is appended literally, and the escaped
|
|
flag is set to FALSE
|
|
|
|
- escaped = TRUE quoted = FALSE
|
|
. the current byte is appended literally, and the escaped
|
|
flag is set to FALSE
|
|
|
|
- escaped = FALSE quoted = FALSE
|
|
. if the current byte is a double quote '"' it is
|
|
discarded, and the quoted flag is set to TRUE
|
|
. if the current byte is a backslash '\', it is
|
|
discarded, and the escaped flag is set to TRUE
|
|
. if the current byte is a space ' ' byte or nul byte,
|
|
the end of the value has been reached
|
|
. any other byte, INCLUDING tabs, newlines, etc., must be
|
|
appended literally.
|
|
|
|
- escaped = FALSE quoted = TRUE:
|
|
. if the current byte is a double quote '"'
|
|
it is discarded, and the quoted flag is
|
|
set to FALSE
|
|
. if the current byte is a backslash '\'
|
|
it is discarded, and the escaped flag
|
|
is set to TRUE
|
|
. otherwise the current byte is appended literally
|
|
|
|
If a nul byte is seen in a state other than escaped = FALSE
|
|
quoted = FALSE, it is an error, and the message should be discarded
|
|
as corrupt.
|
|
|
|
Note that the escaping here is simpler than either C string literal
|
|
escaping, or shell quoting. Unlike C string literals, "\n" means
|
|
"the letter n," not "newline"; unlike quoted shell strings, "\e"
|
|
means "the letter e," not "backslash followed by the letter e."
|
|
|
|
Note that an empty string can be represented by simply not
|
|
including a value before the first whitespace, as in FOO:
|
|
FOO= NAME=Hello
|
|
or by empty quotes as in BAR:
|
|
BAR="" NAME=Hello
|
|
|
|
- Once the end of the value has been reached, any space (' ') bytes
|
|
should be skipped. The first non-' ' byte is the first byte of the
|
|
next key.
|
|
|
|
Note that keys are case-sensitive, Foo and FOO are different keys.
|
|
|
|
|
|
Startup notification
|
|
===
|
|
|
|
The startup notification protocol involves sending X messages with the
|
|
message_type atom _NET_STARTUP_INFO_BEGIN/_NET_STARTUP_INFO to the
|
|
root window. In multihead setups, the messages should go to the root
|
|
window of the X screen where the launchee application is being
|
|
launched.
|
|
|
|
As a general convention, any key-value pairs in startup notification
|
|
messages that aren't understood by a given client should be ignored by
|
|
that client. Also, any keys or message types not documented here must
|
|
be prefixed by the two bytes "X-" as in "X-myproperty" or
|
|
"X-mymessage".
|
|
|
|
All messages in the startup notification protocol refer to a "startup
|
|
sequence"; a "startup sequence" reflects a single launch event.
|
|
|
|
Here are the message types ("message types" here means the type at the
|
|
beginning of the message string, not the type of the X message):
|
|
|
|
new: message indicating that a new startup sequence has been
|
|
initiated. The key-value pairs in this message indicate the
|
|
properties of the startup sequence. If this startup sequence
|
|
already exists, "new:" message is equivalent to "change:"
|
|
(i.e. the values are updated instead of creating a new
|
|
startup sequence).
|
|
|
|
|
|
change: message changing a startup sequence. Clients should update
|
|
information about the matching startup sequence to the newly
|
|
provides information. "change" messages contain
|
|
a subset of the keys allowed in a "new" message. Not all
|
|
attributes of the startup sequence are allowed to change
|
|
over time.
|
|
|
|
"change:" messages not be taken as a "new:" message,
|
|
i.e. they should not cause any feedback or similar.
|
|
|
|
If a client has not seen a "new:" message for the same sequence,
|
|
then it should remember the information for later use in case
|
|
a "new:" message comes later. "change:" messages without
|
|
a following "new:" message can be discarded after a reasonable
|
|
timeout (>= 1 minute). Rationale: The application
|
|
may want to send certain information like timestamp or description
|
|
first, before handling control to library code deciding
|
|
if there should be actually any feedback for the launch.
|
|
|
|
|
|
remove: message ending a startup sequence. Once this message
|
|
has been seen for a given sequence, any further
|
|
messages referring to the sequence should be ignored.
|
|
|
|
All messages must include these keys:
|
|
|
|
ID
|
|
|
|
uniquely identifies a startup sequence; should be some globally
|
|
unique string (for example, hostname+pid+"_TIME"+current time).
|
|
The string should be in the form of <unique>_TIME<timestamp>,
|
|
where the timestamp is the X server timestamp of the user
|
|
action that caused the launch. See the TIMESTAMP key
|
|
for details.
|
|
|
|
The following keys are required in a "new" message and may be included
|
|
in either a "new" or a "changed" message:
|
|
|
|
NAME
|
|
|
|
some human-readable name of the item being started;
|
|
for example, "Control Center" or "Untitled Document";
|
|
this name should be localized.
|
|
|
|
SCREEN
|
|
|
|
the X screen number the startup sequence is on
|
|
|
|
The following keys may be provided optionally in either a "new" or a
|
|
"changed" message:
|
|
|
|
BIN
|
|
|
|
name of the executable being started, argv[0]
|
|
|
|
ICON
|
|
|
|
a string to be interpreted exactly as the "Icon" field
|
|
in desktop entries is interpreted.
|
|
|
|
LAUNCHED_BY
|
|
|
|
identification of the top-level window in which the launch was initiated.
|
|
The value is the X window ID of the window.
|
|
|
|
DESKTOP
|
|
|
|
the desktop on which the application should appear,
|
|
counting from 0, as in _NET_WM_DESKTOP. However,
|
|
this value should never override a _NET_WM_DESKTOP
|
|
property set on window that's being mapped.
|
|
This desktop is relative to the screen provided by
|
|
the SCREEN key.
|
|
|
|
TIMESTAMP
|
|
|
|
X server timestamp of the user action that caused this
|
|
launch. For example window manager that doesn't allow
|
|
stealing of focus by newly mapped windows while the user
|
|
works in an application can use this timestamp for windows
|
|
that have matching _NET_STARTUP_ID property if they don't
|
|
have any _NET_WM_USER_TIME property set or if it's older.
|
|
See the description of _NET_WM_USER_TIME in the WM spec
|
|
for details.
|
|
|
|
This key is obsoleted by including the timestamp directly
|
|
in the ID field.
|
|
|
|
DESCRIPTION
|
|
|
|
a short description suitable for display in a dialog that
|
|
indicates what's happening. For example "Opening document
|
|
Foo" or "Launching KWord" - the description should be in
|
|
"foo-ing whatever" format, describing the current status.
|
|
|
|
WMCLASS
|
|
|
|
a string to match against the "resource name" or "resource
|
|
class" hints. If this key is present, the launchee will most
|
|
likely not send a "remove" message on its own. If the
|
|
desktop environment detects a toplevel window mapped with
|
|
this name or class, it should send a "remove" message for
|
|
the startup sequence. Note that the class hint is in
|
|
Latin-1, so the value of this key must be converted to
|
|
Latin-1 before strcmp'ing it with the window class/name.
|
|
(Though in all known cases only ASCII is involved so it
|
|
doesn't matter.)
|
|
|
|
SILENT
|
|
|
|
a boolean (1/0) value. When set to 1, there should be
|
|
no visual feedback. This can be used to suspend
|
|
the visual feedback temporarily, e.g. when
|
|
application shows a dialog during its startup before
|
|
mapping the main window. Another use is for launch
|
|
sequences for applications that are neither compliant
|
|
nor their WMClass is known, but which should preferably
|
|
have their window mapped on the desktop specified by
|
|
the value of DESKTOP.
|
|
|
|
APPLICATION_ID
|
|
|
|
When launching an application using a .desktop file from
|
|
the normal application paths (see desktop file specification),
|
|
this should be basename of the .desktop file.
|
|
For example: "foo.desktop".
|
|
|
|
When launching a .desktop file NOT in the paths, this should
|
|
be an absolute path to the .desktop file.
|
|
|
|
Some details of the startup sequence:
|
|
|
|
- "new" and "change" messages are sent by the launcher code
|
|
|
|
- the launchee code is responsible for sending a "remove"
|
|
message to end the launch sequence, unless the WMCLASS
|
|
key was set.
|
|
|
|
- the "new" message must be the first message. Other message
|
|
types should be ignored by all clients unless those clients
|
|
have seen a "new" message with the same ID.
|
|
|
|
- "change" messages can be sent at any time between "new" and
|
|
"remove"
|
|
|
|
|
|
Communicating from a launcher process to a launchee process
|
|
===
|
|
|
|
To communicate the startup sequence information from a launcher
|
|
process to a launchee process, when possible an environment variable
|
|
should be used:
|
|
|
|
DESKTOP_STARTUP_ID
|
|
value of the "ID" field in the "new" message
|
|
|
|
It is suggested to unset this environment variable in the launchee
|
|
as soon as it's read, to avoid possible reuse by some process started
|
|
later by launchee.
|
|
Mechanisms other than the environment variable may be used as well, as
|
|
long as they are reliable. The environment variable is only used when
|
|
the launchee code is in a process started by the launcher code; if
|
|
they are in the same process the environment variable may not be
|
|
relevant.
|
|
|
|
Desktop entry spec extensions
|
|
===
|
|
|
|
StartupNotify=BOOLEAN
|
|
|
|
If true, it is KNOWN that the application will send a "remove"
|
|
message when started with the DESKTOP_LAUNCH_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.).
|
|
|
|
StartupWMClass=STRING
|
|
|
|
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.
|
|
|
|
EWMH spec extensions
|
|
===
|
|
|
|
_NET_STARTUP_ID, UTF8_STRING
|
|
|
|
The ID used for the startup sequence for the window. If set
|
|
on a group leader window, applies to all application windows
|
|
in that group that do not set their own _NET_STARTUP_ID.
|
|
|
|
If a new value for the property is set, the window manager
|
|
should update the window's status accordingly (update its virtual
|
|
desktop, etc.).
|
|
|
|
|
|
Launchee failures
|
|
===
|
|
|
|
The launcher process is responsible for detecting launchee failures
|
|
such as a crash and should end the launch sequence in such case.
|
|
In case launchee fails to end the launch sequence, clients should
|
|
treat the launch sequence as ended withing a reasonable time.
|
|
|
|
|
|
A. Sample code to send X message
|
|
===
|
|
|
|
This code omits creation/destruction of "xwindow" which is the unique
|
|
identifier window for the message. It should be created just before
|
|
this code and destroyed just after.
|
|
|
|
XEvent xevent;
|
|
const char *src;
|
|
const char *src_end;
|
|
char *dest;
|
|
char *dest_end;
|
|
|
|
xevent.xclient.type = ClientMessage;
|
|
xevent.xclient.message_type = type_atom_begin;
|
|
xevent.xclient.display = xdisplay;
|
|
xevent.xclient.window = xwindow;
|
|
xevent.xclient.format = 8;
|
|
|
|
src = message;
|
|
src_end = message + strlen (message) + 1; /* +1 to include nul byte */
|
|
|
|
while (src != src_end)
|
|
{
|
|
dest = &xevent.xclient.data.b[0];
|
|
dest_end = dest + 20;
|
|
|
|
if (src == message)
|
|
{
|
|
*dest = '\0';
|
|
++dest;
|
|
}
|
|
|
|
while (dest != dest_end &&
|
|
src != src_end)
|
|
{
|
|
*dest = *src;
|
|
++dest;
|
|
++src;
|
|
}
|
|
|
|
XSendEvent (xdisplay,
|
|
target_xwindow,
|
|
False,
|
|
PropertyChangeMask,
|
|
xevent);
|
|
|
|
xevent.xclient.message_type = type_atom_begin;
|
|
}
|
|
|
|
Change history
|
|
===
|
|
|
|
Changes since 0.1:
|
|
|
|
- TIMESTAMP field is obsoleted by including the timestamp directly in the ID.
|
|
- data from "change:" messages should be used even if they precede the matching "new:" message
|
|
- added LAUNCHED_BY
|
|
- Explicitly mention that updating the _NET_STARTUP_ID property on a window should trigger
|
|
window manager's actions for the new startup notification again.
|