Jump to contentJump to page navigation: previous page [access key p]/next page [access key n]

    Startup notification protocol

    Authors:
    Lubos Lunak
    Havoc Pennington

    Publication Date: 2002-10-20, Version: 0.1

    This document specifies a mechanism allowing a desktop environment to track application startup, to provide user feedback and other features.

    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 updating an existing startup sequence. If a client
              has not seen a "new:" message for the same sequence, then
              all "change:" messages should be ignored. i.e. a "change:"
              message should not be taken as a "new:" message.

              "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.

      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+current time).

    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.

      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.

      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.

    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.

    StartupWMClass=STRING

      If true, 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.


    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;
             }