First of all: Evolution is sooooo "1337":
Guess what: I've been thinking some more about CDIS. Yesterday I had a little discussion about it on #gnome-hackers with Robot101, who seems to have some good knowledge of D-BUS. Obviously, he rejected the idea of adding one more daemon. Indeed, it shouldn't be there, I hate it myself too, but currently it's the only solution to get things working.
He suggested to specify some well-known service and object names, and call methods on the desired services that way. This is not a possible solution though: think of someone running 2 VoIP softphones (eg Ekiga and Skype), 2 IM clients (Gossip for Jabber, aMSN for MSNP),... A service is unique for one application.
So some other solution should be found.
The best one, IMHO, is to add a method to the org.freedesktop.DBus service/object, something like "GetObjectsByInterface", which accepts a string, and returns an array of (servicename, objectpath) tupples, both strings. Using that one can do a specific call on all objects that need to be called.
Next to that, there's the versioning issue. If I remember correctly, a long long time ago this has been discussed on the D-BUS mailing list, and people decided interface versioning support wouldn't be added to D-BUS. As the CDIS interface specifications won't ever be "final", it should be possible to get around this.
It's not that hard though. We can use the standard x.y versioning system, x.y is backwards compatible with x.(y-1), so in a version bump from x.y to x.(y+1) only methods or signals can be added. Bumping x allows a complete API break.
Like this, if we release version 1.2 of the org.freedesktop.CDIS.FooInterface interface, and application FooApp got an object implementing this interface version, it should register itself to the D-BUS sessionbus, and tell it it implements org.freedesktop.CDIS.FooInterface.1.0, org.freedesktop.CDIS.FooInterface.1.1 and org.freedesktop.CDIS.FooInterface.1.2. Now if some other application wants to call a function added in version 1.1 of our spec, it just checks what service/objectpath pairs implement org.freedesktop.CDIS.1.1, and calls the method(s).
All this can be done behind the scenes by some helper library that makes it easier to make applications CDIS-aware.
As now is the time to come up with some project for Google's Summer of Code, I think it'd be great to work on this (now don't steal the idea). The only issue is: would someone be willing to mentor this?
I'd like to do the initial work somewhere under the hood of the GNOME project, if that'd be possible/allowed/accepted.
Here's a TODO:
- Add the previously described method to the D-BUS service
- Come up with some XML format to define CDIS interfaces, including interface version support
Write code generators that generate helper libraries based on these XML descriptions. Some ideas:
- Plain C: using vtables with function pointers
- GObject: standard GObject things like signals and GObject interfaces
- Python: decorators
- CLI/C#: decorators too
The helpers should use the language-native D-BUS bindings, which makes things easier to debug (IMHO)
- Write up an initial interface for some specific service type, and make some projects ("servers") aware of it
- Change some other applications ("clienst") so they can make use of the servers
Regarding the helper libs: these should abstract the complete D-BUS backend and versioning issues from the actual application. An application should just initialize it, say "Hey, I'm a MusicPlayer, I want to support version x.y of the org.freedesktop.CDIS.MusicPlayer interface, here are my callbacks".
In pseudocode:
/* Hello world, I want to implement a CDIS interface */
CdisMusicPlayer *cdis_self = cdis_musicplayer_new("1.2", "com.eikke.CoolPlayer", "/com/eikke/CoolPlayer/CDISController");
/* When a client makes a "pause" call to apps implementing org.freedesktop.CDIS.MusicPlayer, please jump to myapp_playback_pause
* This uses D-BUS methods
*/
cdis_musicplayer_set_playback_pause_cb(cdis_self, myapp_playback_pause);
/* Hey listeners out there, I started playback
* This sends out a D-BUS signal
*/
cdis_musicplayer_playback_started(cdis_self);
Next to the server object, there are clients:
/* Hey, I'd like to control music players. I support version 1.5 of the org.freedesktop.CDIS.MusicPlayer interface */
CdisMusicPlayerClient *c = cdis_musicplayer_client_new_with_bus("1.5", my_dbus_connection);
/* If a player starts, please run this callback */
cdis_mediaplayer_client_set_playback_started_cb(c, playback_started);
/* Tell all MusicPlayers to pause */
cdis_mediaplayer_client_pause(c);
Maybe the "object" instance isn't even necessary!
Behind the scenes, when the client calls cdis_musicplayer_client_pause(), the helper will know this method was added in version 1.1 of the specification. It will request a list of all service/object-pairs that implement the org.freedesktop.CDIS.MusicPlayer.1.1 interface from the D-BUS service, and do the Pause() call on all returned objects.
When the server "connects" to the system, telling it implements version 1.2 of the spec, the helper will connect to the session bus and will tell the D-BUS system the object implements org.freedesktop.CDIS.MusicPlayer.1.0, org.freedesktop.CDIS.MusicPlayer.1.1 and org.freedesktop.CDIS.MusicPlayer.1.2.
Like this it should be fairly trivial to make existing and new applications CDIS aware. And that's the way it should be ;-)
Obviously, an application could use "pure" D-BUS calls to handle all this itself too. But why make things so complicated?
Next to that, it should be possible to write "testkits" too, based on the XML specs. Python might be very usefull here. One test then looks up the highest version of the spec, and executes all methods new in this version on all objects claiming to implement the interface. Then it takes the last-but-one version, and does the same thing, all the way down to x.0. This way an application developer can easily check whether his application is CDIS compliant. This needs some more thinking though ;-)
Anyway, lots of things to be done, and obviously, not all of this can be done in only 2 or 3 months. Still, some results/progress can be made, and I'm 99% certain incorporating this in the desktop would be a great thing.
I know one should just apply for SoC without thinking about a mentor. I would like to know someone'd want to be mine though before applying (just to be on the safe side), so if these things interest you and you want to get some experience on the mentor side of things, please let me know!
Comments:
He suggested to specify some well-known service and object names, and call methods on the desired services that way. This is not a possible solution though: think of someone running 2 VoIP softphones (eg Ekiga and Skype), 2 IM clients (Gossip for Jabber, aMSN for MSNP),... A service is unique for one application.
Is it not possible for 2 IM clients to register as a remote Object with the same interface? They could each receive the message and take context clues.
In the case of the music player, exactly what you want is for all players to pause.
In the case of one client providing jabber, another providing MSN, the client that handles that protocol can respond exclusively to the message. If there really is no way to differentiate, then the correct behavior could just be to send on both.
As for the IM clients, we shouldnt differentiate between protocols. Client applications dont care about the protocol, or shouldnt at least... They just wand to tell something to "IM Clients".
Or am I misunderstanding something?
But it *could* be possible to get something like that done too, yes. Although it won't be me who works on it.
And as you might have guessed, I'd love to do this as my SoC project ;-)
The whole point of the dbus "wait to aquire service stuff" is so that multiple applications can share the same interface (i.e. only one person owns it at a time).
A demon to multiplex all function calls to each interface for every breed of application is not the way to go. Should someone be listening to the screensaver demon and multiplex the calls incase i like to run two screensavers?
Gossip and aMSN