|
|
Remote Applications
[ about |
screenshots |
structure |
interface |
configuration |
download |
related ]
(Remote APPlications) is a way of doing remote application deployment in a fairly simple way. There are plenty
of things in this arena already, but I thought I'd add to it anyway. It's now in a kinda-working state, but the code that's there needs some
tidying up -- something for when there's more time.
Rationale: It's probably worth mentioning a few words about why this exists (for my own justification if no-one elses!).
Every time I've written a graphical application, and with little exception, it's been a mess. Generally speaking, the way in which humans
interact with graphical user interfaces is far detached from the logic of the program underneath it. GUIs have a kind of concurrent behaviour;
the user can click on any button at any time (more or less), generating actions that the program must respond to. Object-oriented environments
(e.g. Java/Swing and C++/WG) typically tie interface
components to "objects" in the program. Lovely abstraction, but the concurrent aspect of interfaces breaks this somewhat. Either it's free-range
concurrency (e.g. the "event thread" with Swing), or strict serialisation through an event loop (e.g. what WG and many other toolkits do). The most
fitting language is one that is itself concurrent (e.g. occam-pi), since autonomous "processes"
can be used to represent interface components. Simple interfaces are manageable with either technique, to a greater or lesser degree, but things get
increasingly less pleaseant as the interface complexity grows. A typical example might be some kind of control interface, where some data is updated
in real-time, the user is interacting (clicking buttons, etc.), and those interactions cause other parts of the interface to change. Altogether, it's
not an entirely nice situation for the application program. RAPP is an attempt to make the application/interface bridge a little nicer for the
application. The theory is that if an application and its interface are detached in some way, the scope for what can go wrong in their linkage is
limited. It does mean, however, that the interface side needs to be aware of some extra (application related) things, e.g. that clicking a certain button
should "suspend" the interface until it is updated (preventing the user from clicking other buttons etc. in the meantime).
Version 0.2.0 of rapp and rappd require UWG 0.2.0 or later.
From version 0.1.3, you'll need to tell rappd and rapp where UWG is installed when configuring. Rappd still expects to be run from
its distribution tree -- you can install it elsewhere, but will need to edit the various configuration files it installs (for the time being).
Ordinarily, for example:
bash:~/rapp-0.2.0/$ ./configure --with-uwg-config=/home/fred/uwg/include/uwg
bash:~/rapp-0.2.0/$ make
Version 0.2.0: the distribution ships with two keypairs; one for the server and one for the user. The passphrase in both cases is "default".
 |
 |
 |
The authentication module in RAPP 0.2.0, using public-private key pairs; user supplies passphrase and off it goes. |
|
Moderately recent 'RAPP explorer' addition, now mostly working, client-side file browser effectively; understands about .rapp files on the server -- some icons built-in, some
downloaded [icons shamelessly copied from afterstep/gnome/debian directories] |
|
Modified chat application (python script) which now understands about authenticated users ('frmb' in this case) |
|
|
|
This is an output-only bash script (actions sent by the client are absorbed in rappd). The interface contains a series of label gadgets that get updated
by the script, e.g.:
<gadget type="UWGLabel" name="utilitytemp">
<property name="geometry" value="260,164,128,16" />
<property set="cpref" />
<property set="helvfont" />
<property name="background" value="black" />
<property name="foreground" value="white" />
<property name="alignment" value="left" />
<property name="caption" value="0.0" />
</gadget>
|
|
The iotest application (fed from another shell-script):
This pretty basic application uses a bit of awk to process interaction -- more or less to demonstrate that I/O works,
probably more sensible to use python or perl.. The script is downloadable here, but kind of:
read
printf '%s\n' "$REPLY" | awk -F : '{
if ($1 != "iotest") {
printf ("::BEGIN\n");
printf ("<update name=\"thelabel\">\n");
printf ("<property name=\"caption\" value=\"erk, not me! [%s]\" />\n", $0);
printf ("</update>\n");
printf ("::END\n");
} else {
}
}' || break
|
|
|
|
|
A very basic multi-client chat application. The application logic used to be a bit of bash script,
but has now been re-written in Python (0.1.3 version).
| |
This is the start of a server-control module, implemented as a plug-in library. The initial XHPD interface
can be downloaded here, updates are generated from within the C code.
| |
This shows a collection of things going on in the RAPP client.
| |
|
The original structure of rapp was to have applications broken into two pieces. The client side (rapp) providing an interface
for some application running on the server side (rappd). This has been expanded on a bit, to provide a range of interfacing
mechanisms at either side -- the way the whole thing works should make this quite possible. The figure on the right shows the
modified structure (and what the software is heading towards):
The key piece is the rapp-server (rappd). Applications attach on one side, clients on the other. The transport between a rapp
client and the server is TCP -- over a network such as the internet, or some intranet (or through a UNIX socket locally).
The server sends the rapp client descriptions of interfaces (and control information). When the user interacts with the
interface (clicking a button for example), the rapp client sends an event back to the server, which notifies the underlying application.
The amount of network interaction can be controlled by the application, by selecting only the events it is interested in.
Due to latency introduced from networks and heavily-loaded servers, applications might not be instantly responsive (although it
can be indicated to the user that the application is waiting for a server response). Thus, this is not going to be suitable for
certain things (quake-2 style games, for example).
Application Interfaces
There are a range of server interfaces, some implemented, most not (yet). Within the rapp-server code these are referred to as "session engines".
external engine: provides a basic external application mechanism, where the XHPD descriptions, updates, actions, etc. are
communicated via the application's standard input and output. When a client requests a session of this type, a new instance of
the application is spawned. Also supports a multi-client mode, where a single instance of an application can handle multiple
clients; the server multiplexes/demultiplexes as necessary.
module engine: uses dynamic load libraries to provide RAPP applications. As with the external engine, can handle single
or multiple clients. Unlike external applications, these can have some influence within the server (e.g. configuring, authentication, etc.).
client engine: connects out to another RAPP-flavoured server, e.g. something provided by an existing application to allow interfacing
by RAPP clients (indirectly via the server). (not yet implemented)
server engine: this is designed to support external applications that use the "rappslib" library. The library
provides (will provide) a gadget abstraction to applications, that transparently connects to the rapp server. This also allows the
state of an application interface to be saved inside the module -- e.g. allowing the external application to be restarted without
breaking the state of connected clients (of course, the application needs to be aware of this to restore its own internal state correctly). (not yet implemented)
Client Interfaces
The various rapp clients connect to the server and exchange XHPD blocks only (which might not be as UWG-specific as they were originally).
rapp client: this is the native (original) rapp client that uses the micro-WG toolkit to produce interfaces on an X11 display. This the preferred client
in the sense that it supports all the possible gadgets that an application might require (applications will only produce UWG flavoured interfaces).
rapp GTK client: this will provide a GTK style interface for use on either X11 or Windows (and presumably MacOS too).
This client will need to map UWG gadgets to GTK widgets in order to produce the interface. Gadgets that cannot be supported will simply be ignored -- however, this can
be resolved to some extent with the use a proxy client. (not yet implemented)
proxy client: this is a non-visual client that acts as a proxy between the rapp server and a real client (or downstream proxy). In addition to providing
a simple proxy service (e.g. for gateway machines), this will provide mechanisms for saving/restoring a client's interface state regardless of the session engine behind it.
For clients that cannot support the whole UWG gadget set, the proxy will provide appropriate mappings. In the worst case scanario, a real client might only support
UWGImage, e.g. for rendering interfaces via a web-browser and CGI script. (not yet implemented)
I'm now using micro-WG. The interface descriptions are sent as XHPD blocks --
XML-HPD. XHPD is just an XML encoding of the traditional WG HPD file. This gets parsed and rendered by rapp (using expat
or UWG's own -- and simple -- parser to do the XML parsing). In essence, rapp is a UWG application. The server-side needs to be linked with UWG (for some of the non-visual gadgets),
but doesn't need any X11 support at run-time. All the server has to do is generate interface descriptions with appropriate control information, interpret
the events being sent back, and from that update the interface appropiately. You can use UWGBuilder for designing interfaces.
One potential target application is wireless hand-held devices, running a small X server + rapp (or some other rendering agent which
understands the XHPD-style stuff and protocols used), giving convenient access to large applications, which wouldn't normally be possible
for such small devices. I'd like to use the lcars-gadgets to provide something like a sony-viao with a home-control system, lights, TV, etc.;
that would rock.
The rapp server supports a session mechanism, the idea being that a client can save the state of a session, disconnect, come back later and
restore the session. Amongst other things, this provides a nice way to break apart back-end handlers from rappd. To this end, rappd has a series
of session-engines. The only one currently implemented is the "external engine". This provides a mechanism to launch an external program and use
its standard input/output to communicate with the rapp client. It's pretty basic, but so far appears to work ok :-).
From version 0.2.0, the configuration is held in an XML file called "rappd-config.xml", which is searched for in the current directory (or specified on the command-line).
The supplied configuration is setup to run the server in its distribution tree, having been started on the command-line.
When the client starts up without a specific file, it produces a file-selector which can be used to browse the rapp file-system (or any other). If an XHPD file
is selected (from rappd or elsewhere), the client simply renders it (with whatever local processing the file contains). Mainly intended for development, testing
and general viewing of XHPD files. If the client selects a ".rapp" file from the rapp file-system only, a new session on the server is started.
Version 0.2.0: the RAPP client now contains an 'explorer' style interface, allowing the user to browse files on the server. If used ('-E'
command-line option), it runs separately to sessions, allowing multiple sessions to be started by the client. The browser reads the '.rapp' files found
on the server and extracts descriptions and icons for them. Clients can also now authenticate with the server, acquiring 'tokens' (e.g. "trusted-user" for logging
in). These are defined in the configuration file along with the users. The authentication module's client-side '.rapp' file contains, for example:
The example "monitor.rapp" file, that the client downloads whole, contains:
rapp.name: "auth"
rapp.description: "RAPP authentication"
rapp.async: 1
rapp.icon: "rapp:///images/auth.xpm"
The important setting is "name" -- this gives the name of the session on the server side (which the client asks for). The various session definitions are
held in the server's "modulepath". For the monitor application, this is again called "monitor.rapp" (generally "X.rapp" where "X" is the "rapp.name" setting),
and for the authentication module, contains:
rapp.engine: module
rapp.modengine.module: "rappmods/auth.so"
rapp.modengine.multiclient: no
rapp.modengine.sessdir: "file://modfiles/auth"
As another example, the house-monitoring application (which gives a read-out of the temperature, electricity usage, etc.) uses:
rapp.engine: external
rapp.extengine.io: output-only
rapp.extengine.exec: "scripts/housemonitor.sh"
The "engine" setting specifies which session-engine is to be used. The only engines currently implemented are the external and module ones. The external engine
launches an external program; the module engine loads pre-compiled code into the RAPP server.
The "extengine.io" setting describes how the program given in "extengine.exec" interacts with the client. The example given (for house-monitoring)
is output-only -- it just updates the contents of various labels on the client window. The executable given in "extengine.exec" must be something that a UWGChildProcess
can run -- i.e. something on the local file-system.
Another script (added for 0.1.3) produces a window with some buttons that react when pressed. The .rapp module file for this contains the additional setting:
rapp.extengine.actions: formatted
This causes the external engine to pre-process actions sent by the client. In the current implementation (between 0.1.2 and 0.2.0) this involves parsing the XHPD
actions sent from the client and extracting the "value" field -- all properties/etc. sent back are ignored. Strings sent to the external program will be things
like "iotest:button1:on_click".
From 0.1.3 the external engine can handle multi-client applications. From 0.2.0 so can the module engine. This is enabled by the following setting:
rapp.extengine.multiclient: yes
The external program receives notifications of joining and leaving (client) sessions, and is automatically shut-down when the last client leaves. Output can
either be sent to all clients or just to a single one; input is prefixed by the session ID.
There are various similar/related bits of software out there. Here's an attempt to list some of them (if you find any I've missed, please let me know :-)).
|