adesklets was made to be easily usable from a variety of langage environments1. This chapter explains to programmers how the adesklets interpreter hooks itself into the system in a language-neutral fashion.
If you are not minimally used to POSIX systems or do not have basic notions on operating system programming, this chapter is probably of no interest to you.
Please note that the Python package under scripting/python/adesklets is a good complement for what you are about to read.
If you want to use the adesklets interpreter from your favorite language but it is not supported out of the box2, you can still do it, provided your langague supports one of these features:
It should also be able to either3:
Finally, it should also be able to start a child process.
Those a pretty light requirements from a POSIX point of view, but let us mention for the sake of completeness that if your language environment does not meet these requirements, you could still use it provided you can get your application to talk reliably with another program4 that meets them. Of course, this is not an ideal situation and it should be avoided as much as possible.
As you probably know (See Using adesklets.), there are two modes of operation for adesklets, as it can be called:
On a new X session, the typical startup sequence is:
ADESKLETS_IDenvironment variable with a string representation of the proper integer ID, then execute 5 the associated script.
ADESKLETS_IDvariable without altering it. It is the desklet's duty to make sure that:
stderr, get separately redirected, in a way that parent process can easily write to adesklets'
stdin, and read from
stderr. Use of pipes, FIFOs or event regular files are all possible.
stdinand reads to the two other streams are all unbuffered.
ADESKLETS_IDvariable and the command line absolute desklet name given as its first argument to lookup its unscriptable characteristics6 from $HOME/.adesklets. When this operation is over, the
ready!event is generated (See Events.), and the interpreter is ready to process commands.
When a new desklet gets called directly, the startup sequence
described in the previous subsection is the same, except for the
first, second and third steps which simply don't occur. This
means that the environment
ADESKLETS_ID is not set;
the new adesklets
interpreter uses this fact to detect this is a new desklet, parse
configuration file and allocate the first free ID to the
It should be noted that if the desklet file
name given as the first argument to the child adesklets instance is relative or is not
both readable and executable by the current user, the desklet
will not get registered in $HOME/.adesklets, and therefore not
automatically restarted in subsequent new X sessions by the
launcher. In case
stdin is a terminal7,
the registration does not take place either.
This way, from the scripted desklet perspective, both cases are identical, and no conditionnal treatment is needed. All the desklet initialization job is synthesized in the fourth point in the subsection above.
If needed later on, the desklet can use the
get_id command to determine its ID.
As explained previously, a desklet communicates with its private adesklets interpreter via redirected unbuffered streams. Here, let us see what information they convey.
On the interpreter's
stdin, the desklet outputs
the commands it wants to execute in the order in which it wants
them to be executed, one command per line8. Since reading
this stream is suspended while a command is processed, a desklet
should generally wait for a command to be processed before
emitting another9 (See the next section).
The command format is pretty simple: it is a
command name, followed by arguments separated by spaces, all in
plain text representation, including numbers. adesklets does not
care about the number of spaces between arguments, as long as
there is at least one. In the special case of commands where the
last argument is a string (
text_draw, for instance),
adesklets will choose its first non-space characters to be its
beginning; the rest of the line, including spaces, are considered
to be part of the string.
Two files, automatically kept in sync with
the source (scripting/prototypes and scripting/enums) from the base source
distribution, provide in tab-separated plain text format the
description of all the commands and numeric constants that can be
made available to the desklets10. A good binding for a
specific language should come with some automation to
auto-generate pertinent parts of its own codebase based on those
files; for instance, the Python binding adds a
protoize action to its setup.py
distutils script to do
so. Ideally, this automation code should be redacted in the
target language with a reasonable set of libraries11; if it is not well suited to this
task, you need to limit yourself to use:
if you want your work to become part of the official package.
interpreter frequently polls its
stdin for new
characters, and reads from it as needed. Whenever it receives an
End-Of-Line ('\n'), it stops reading from it and tries executing
the command. Once the command has been carried out (possibly
emitting a few lines on
stdout), it sends as its
last entry on it a status message of the form:
command RANK ok: MESSAGE_STRING
if the command was successful, or:
command RANK error: ERROR_STRING
if an error occurred.
RANK is the numerical ID of the
command (an unsigned integer) that starts at zero when the
interpreter is created, and then is automatically incremented by
one for every subsquent line it receives. All commands are
guaranteed to be processed in the order they were submitted.
ERROR_STRING is a free-form message in
human-readable form explaining the source of the error.
MESSAGE_STRING is also in human-readable form, but
is formatted in such a way that it can be used to retrieve
algorithmically useful return values for any commands. Those
return values can be:
context_get_colorand the like)
images_infoand the like) for commands that output multiple lines on
stdout, with the last line being the status message
Algorithmically, your "return value
retrieving routine" should first try to retrieve integers from
MESSAGE_STRING13. If there is one or more
integers present, it should verify that the message is not a mere
copy of an emitted command; if it is, the return value follows
the fifth case (see above); otherwise, if only one integer is
found, this is the first case; if more than one is found, the
second one. If no integers are found and there was a list of
lines sent to stdout before the status message of the command,
and after the status message of the previous command, it then
returns according to third case. In what's left, if the status
message is different from the emitted command, it is the fourth
case. All remaining conditions should be mapped to the fifth
case. This retrieving algorithm is working for all commands
listed in scripting/prototypes.
All remaining asynchronous event reports are sent to the
stderr. They are asynchronous
regarding the processing of commands; if they will never occur
while a command is processed (in the time interval between the
submission of the complete command on
stdin and the
output of its message status line on
can be sent at any other time14.
Event reports are single lines of the form15:
In a stylized Backus Naur form, the grammar for EVENT_MESSAGE would be:
EVENT_MESSAGE :: ready! | backgroundgrab | menufire MENU_ID MENU_STR | motionnotify X Y | enternotify X Y | leavenotify X Y | buttonpress X Y BUTTON_ID | buttonrelease X Y BUTTON_ID ;;
BUTTON_ID are all integers
greater than or equal to zero, and
MENU_STR is a
string of characters, possibly including spaces. Let us detail
things a bit further:
All events but Ready! (namely: MotionNotify,
EnterNotify, LeaveNotify, ButtonPress, ButtonRelease,
BackgroundGrab and MenuFire) are masquable by the desklet. In
provides the desklet with commands for specifically handling the
generation and output of events. They are:
events_set_send_sigusr1. It should be noted that
those functions are not listed in scripting/prototypes as you most probably do
not want your users having access to them. You should probably
start an interactive session and play with them to make sure you
fully understand this. This is also the right time to mention the
script, which comes in handy when interactively experimenting
Two last things are worth mentionning about events:
events_purge. You can change this with
Whenever your language allows it, you should install some kind of SIGCHILD handler, so you can at least be notified if the adesklets interpreter ever exists without a reason (child zombies notwithstanding)16.
adesklets also supports differed execution (indirect mode) and textual replacement of variables. Here how variables work in indirect mode:
playcommand, See Programming adesklets.), variables get expanded in a single pass, and no indirect reference is possible. Expansion is pretty straightforward. The stored command line is scanned for any non-empty sequence of non-space characters beginning with a single '$'. Each such sequence is replaced with the corresponding
$variablevalue, or removed if no variable is found.
The same expansion mechanism also applies to the direct mode of execution. All this leads to the fact that, using the Python package for instance, these two code snippets are equivalent:
This did not require any alteration of our Python code, since Python is a dynamic language, but some other languages will require further thinking.
You should now know pretty much everything needed for integrating adesklets with your language environment. It may appear to be a daunting task, but a very clean binding can be produced by one programmer in only a few days. Once again, have a look at src/adesklets_debug.sh; it is a (very lame) Bourne shell binding for adesklets written in only fifty lines, including comments.
If you ever need assistance, you are encouraged to post on the adesklets-devel mailing list ...
 We use the term “langage environment” because the problem is not the syntax or the paradigm used (should it be imperative, functional, or anything else); it is the way you can handle basic POSIX-related operations with files, signals, etc. (See Requirements.). Thus, a BASH user should use adesklets easily, while a Linux JDK user would probably encounter more difficulties (no flames intended).
 As of adesklets 0.6.1, both Python and Perl are supported out of the box.
 This said, having both IPC support and poll/select will make things both simplier and cleaner. Being able to block signals is also very useful.
 This would be a wrapper, basically.
 With a call from the execve* family; see
man 2 execve.
 They are, namely, its screen and coordinates.
 Which means the session is interactive
 A '\n' character should be emitted after each command.
 This is to avoid process blocking by overflowing the stream with arbitrarily long commands; of course, there is no problem sending three commands of a few hundred bytes in one shot!
 See scripting/protoize.sh.in and scripting/enums.sh for details on the format of the two files.
 The key idea being that most users of your binding should be able to run it more or less from their base installation of the language.
 Nowadays, there is no problem running GNU sed on practically any POSIX system.
 All parameters from the
MESSAGE_STRING are separated by single spaces.
 No event is ever lost anyway; its report is only delayed.
 All other outputs on
stderr can safely be discarded.
man 2 signal is a very
good reference on signals.