D-Bus Activation in the Desktop Entry spec
Ryan Lortie
desrt at desrt.ca
Wed Nov 13 08:51:33 PST 2013
hi Alex,
On Tue, Nov 12, 2013, at 13:35, Alex Merry wrote:
> David suggested I direct questions about this spec to you.
The xdg list is usually a better place for these types of discussions,
for future reference. I'm going to cc: the list into this discussion
because there are some details of future plans below that I think might
be interesting to everyone.
> Firstly, ActivateAction has a parameter named "parameter" that takes an
> "av". A comment in the KDE implementation of this interface
> (presumeably written by David) says that the "av" type is a workaround
> for the lack of a maybe type, but nothing there or in the spec says what
> this parameter is to be used for. This makes writing API docs for our
> implementation a bit tricky :-)
A bit more explanation on the comment first: GVariant is a type system
inside of GLib that is extremely similar to D-Bus except that it has a
couple of extensions. The most notable one is that it introduces the
idea of a maybe type -- you can have the type "ms" that it either
containing a string, or "none" (ie: NULL equivalent). You can apply
this to any type.
D-Bus lacks this type (despite attempts to introduce it multiple times)
so there is a common trick to use an array as a "fake maybe". An empty
array is equivalent to the NULL case and an array with 1 item in it is
equal to the case where that item is the item of interest. If the array
has more than 1 item, the extra items are ignored.
As for explaining the field itself: in GLib, actions take GVariant
parameters on activation. See
https://developer.gnome.org/gio/2.36/GAction.html#g-action-activate for
the API. This can be NULL (which is the empty-array case on the wire
protocol) or it can be any D-Bus value (which is the one-item-in-array
case). It's simply extra information to the action invocation.
Currently, ActivateAction in the org.freedesktop.Application interface
is only used for launching "Additional application actions" from desktop
files in the DBusActivatable=true case, as described here:
http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#extra-actions
In this case, the parameter is always NULL (ie: empty array).
The reason that we introduced the parameter field was a bit of foresight
into what we would do next. At the next freedesktop Sprint (and this is
probably interesting news to you too, David), we plan to propose
standardising something like the following:
https://wiki.gnome.org/GNotification
which we have implemented in GLib and gnome-shell. The main change here
vs. the existing specification is that notifications persist after the
application quits and are capable of reactivating the application when
clicked on. This means that your SMS or email app can send a "you have
a new message" notification after checking in the background and then
exit, to be restarted when the user clicks on the message notification
to view it.
The main difficulty here is that when the app is cleanly restarted, it
will not have any particular notification objects around on which to
send "response" signals. As such, we decided that the easiest thing to
do would be to route responses via application actions.
So, I might send a notification like so:
/---------------------------------------(x)\
| You have a new message |
| |
| Congratuations! Your email won the |
| internet lottery for $10000000!! |
| |
| ( Reply ) ( Mark as spam ) |
\------------------------------------------/
with the default "click on notification" action being to view the
message. In order for this to work, we expect that people would add the
following actions on their application:
- app.reply-to-message
- app.mark-message-as-spam
- app.view-message
and each of them would take a message ID string as an action parameter.
Let's assume our message above has ID msgid2384290. We'd then setup the
notification with the following:
- default action: "app.view-message::msgid2384290"
- action for reply button: "app.reply-to-message::msgid2384290"
- action for spam button: "app.mark-message-as-spam::msgid2384290"
Where "app.action::detail" is GLib's internal "detailed action format"
(which is not part of any spec we intend to propose). It's a convenient
way of indicating an action name along with a parameter. The "app." is
an indication that this is an action defined on the application, and we
do intend for this to be part of the spec (and it is, as we have
implemented it in GNOME).
Now let's assume that the mail program sends that notification and then
exits. The user returns to their computer and sees the notification and
clicks "spam".
The email program is not running, but fortunately it's DBusActivatable
using the org.freedesktop.Application interface. We therefore can send
a message as follows to the well-known name of the app (let's say
'org.gnome.email'):
to: org.gnome.email /org/gnome/email
method: org.freedesktop.Application.ActivateAction
parameters: ('mark-message-as-spam', [<'msgid2384290'>],
{'desktop-startup-id': <'_TIME2398420'>})
((where <> is the GVariant format for denoting the wrapping of the 'v'
type in D-Bus and the 'desktop-startup-id' thing is the blob described
in
http://standards.freedesktop.org/startup-notification-spec/startup-notification-latest.txt
as
http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#dbus
specifies sending).
Because the app is DBusActivatable, the application will now be D-Bus
activated by the system. It will not, however, receive the normal
Activate signal that would typically be associated with such an
activation (because nobody sent it). It will receive the above action
activation instead. It can mark the message as spam and exit again,
without ever having shown a window.
Of course, if the user had selected to "reply" or "view" (default
action) instead, then the compose or messsage viewer windows would have
been shown instead (and again note, the main message list window need
not be shown for these cases) -- no 'Activate' signal was received by
the app.
the TL;DR: of all of this is that we have some future stuff that we plan
to put on track for freedesktop.org standardisation that will depend on
actions having parameters routed through to them. As such, I recommend
carrying this parameter through on your APIs if at all possible.
> Secondly, we our implementation also supports enforcing an "only one
> instance" rule for the application. Currently, this just calls
> Activate() if the executable is run a second time (ie: the relevant
> D-Bus name is taken), but this discards any command-line arguments. Our
> plan is to add a CommandLine method (on the same object, but a different
> interface, such as org.kde.Application) that takes a list of
> command-line arguments. Judging from the GApplication API docs, this
> looks similar to what you do in glib. Do you have any "lessons learnt"
> that we should take into account?
In general, I'd do my best to convince people to avoid implementing a
CommandLine handler. Commandline arguments generally fall (widely) into
the following:
- customising global behaviour of the app (like enabling debug output,
etc)
- filenames or URIs to open
- special modes of starting the app, like 'myemailapp --compose' or so
For the first case, you're going to have a bad time. If you manage to
set these type of 'global state arguments' on the first run then you're
OK but any future runs will probably ignore them. Not much you can do
about that, and I believe that these sorts of arguments are probably
slightly evil in "only one instance" apps for the above reason. I like
environment variables for this sort of thing. If you insist on
processing these as commandline arguments (as many do) then I guess the
best way is to only expect them to work in the first instance that you
run, in which case they need no special treatment.
The second case (filenames) is ideally handled by converting them into
invocations of the 'Open' D-Bus method on the
org.freedesktop.Application API. GApplication does this as a matter of
default behaviour in applications that support opening files.
In the third case, for actions like --compose, I believe the best thing
to do is to convert these into action activations. In essence, send a
"compose-message" action to the already-running instance, causing it to
pop up a compose window. In the case that you have an argument like
"--compose-to a at b.c" then "a at b.c" could be a string-valued parameter to
an action called "compose-to" (which is another reason you may want to
support parameters to action activations, as discussed above).
The one case where I believe that CommandLine is truly useful is for
programs that have existing well-established conventions of how they
behave when invoked from the commandline. My absolute favourite example
here is a GUI-based text editor used as defined in the EDITOR
environment variable. When I do 'git commit', git has particular
expectations about how the command will behave. In this particular
case, it expects that the command exits when the editing is done.
This poses problems for applications in a "only one instance" world.
What if the invocation is the second invocation? Probably then the
executable will send the message and quit right away and git will
complain about "you didn't make any changes -- aborting the commit".
What about if it is the first instance, but in the middle of writing my
commit message, I want to look at another file? Then the editor will
not exit until I've closed both files.
This is one of the reasons I've pushed towards DBusActivatable
applications. In any case, invocation of the editor on the commandline
will be a small simple program that sends a D-Bus message causing
activation of the "full" program remotely. The commandline portion of
the program can now exit freely.
That said, it should only exit when the user has closed the tab that
contains the file being edited. Therefore, your CommandLine mechanism
should probably have some way of supporting controlling when the remote
instance will exit.
Your commandline mechanism should also contain support for seeing which
environment variables were set in the remote invocation, and definitely
should have support for querying the cwd on the other side (since
probably users expect to be able to pass paths to their editor relative
to their cwd and have them resolved properly). I recommend a
convenience API for "turn this commandline argument into an absolute
path, according to the cwd on the remote side", since very many apps
will want this. Your commandline mechanism may also want to support
forwarding of stdin/stdout/stderr (or even other file descriptors) from
the remote side, because your user may expect that your editor will be
able to open "-".
There are quite a lot of complicated considerations here. The
org.freedesktop.Application interface was originally only designed to
support launching desktop files (with an eye towards notifications
coming down the pipe soon). These two facts together caused me to keep
CommandLine out of the spec. I still think that there would be little
to no value to adding it because each side will have their own separate
library to support applications that want to do this and there will
probably be no system-level component that has to interoperate with
applications in this way -- this protocol will always be between an
application and itself (ie: the 'service' and the 'launcher' sides).
The one thing that gives me pause here is that we recently introduced a
gapplication(1) commandline tool that supports sending
activate/open/activateaction to the org.freedesktop.Application
interface. We have some vague plans to add support for sending
CommandLine invocations, as well, using the GLib style. It would
certainly be nice if this tool could interoperate with KDE applications
for CommandLine invocations and it would also be nice to keep the tool
"pure" in terms of only speaking freedesktop-specified interfaces, but
I'm not sure that there is enough value here to bother dragging all of
the above mess into a freedesktop-specified standard (with all the
stability requirements, etc).
In any case, I thank you for your interest in all of this and wish you
luck with the implementation. I'm glad to see that KDE is going to be
supporting all of this soon and I'm very happy to continue answering any
questions you have about it.
Cheers
More information about the xdg
mailing list