04/25/06
While I don't use this blog very often, it has been four months since my previous post, I have to clean about 10 spam messages here every single day. This has been going on for a while now, and I'm sick and tired of it. So I did a little google search for anti-comment-spam plugins. It seems like these plugins are my only hope. This one, and this this one are interesting in particular. Does anybody have any experience with one of those two? Do they work?
04/19/06
Lots of traffic on GIMPNet today, many different and interesting things were discussed between some GNOME hackers. But I guess others will write about those things (Philip? Jeff?).
Anyway, more related to myself and my own project (yeah, CDIS, you know): I had a great talk with Robert "Robot101" McQueen, who gave some very sensible insights in the D-BUS system, and how I could (ab)use it.
First of all, last night I was thinking about the D-BUS glossary a little, and came to the conclusion that "interfaces" might at a certain level be compared to "namespaces" instead of "interfaces", at least in some point of views. Might be interesting if you try to "get" D-BUS. Questions? Leave a comment ;-)
After this talk and some help from J5, #1 python-dbus guru, I managed to get a new sample setup working. In this new design, there is no central daemon at all any longer, which is great, obviously. Some changes/enhancements to the D-BUS bindings might be necessary though (I hate the fact you can't get the sender service/object path in a signal handler!).
As a picture says more than a thousand words, first open up this screenshot. Code's here.
As you can see, first the user starts his favorite media player, RhythmBox, and spins his favorite song (1). In the background, a screensaver application is running (in the sample I started it after RB, but hey, its a sample) (2). After a while (3 seconds ;-)), the user starts his VoIP softphone, Ekiga (3). As the user is very popular, he almost immediately receives a call, and accepts it (4).
As you can see, Ekiga knows about music players, and asks them to pause playback. RB is CDIS-aware, and stops playing (5). Obviously, the music player should do a generic "Paused" signal here (see later). Next, Ekiga sends out a signal on the org.freedesktop.CDIS.SoftPhone interface, "IncomingCall". This signal can be used by any application listening for it. It is not related to pausing music players: in (4-5), Ekiga tells all music players to pause. It knows about this kind of applications, and knows how to handle them. In (6) Ekiga provides application types it doesn't know about (or, maybe more likely, doesn't want to know about, doesn't care about) to do some action based on the fact there's an incoming call.
As this is a video conference, the screensaver traps that signal, and stops his internal timer (7).
After a while, the call is over, the user disconnects (8). Ekiga sends now tells all music players they can resume playback if they were playing before (9). It also sends out a "CallTerminated" signal on the SoftPhone interface, which is again seen by the screensaver, who restarts his timer.
Then, something happens that will/should not be supported, but I just implemented it as a sample. When the screensaver decides it's time to blank the screen (11), it tells all SoftPhones to go "Idle" (12). This is not the way we should handle this in "real" applications, as a screensaver should not put SoftPhones in idle state, it should send out a signal "ScreenSaverStarted", the softphone should watch for that one, and go idle when the screen blanks. Anyway, I just wanted to give one more example of the "action" paradigm, just like "Pause" and "Resume" on the MediaPlayer interface.
As usual, the prototype code is extremely ugly, but it does show what I got in mind regarding code generation. There should be 3 "objects":
- CdisMusicPlayer: inherited by a "real" music player, or by some internal CdisHandler object. Needs function pointers to all supported CDIS function calls. In the sample, used by RhythBox.
- CdisMusicPlayerController: Used by code that wants to control a class of applications. Used by Ekiga to pause all music players. Only provides "active" functions that, when called, result in a change on remote objects.
- CdisSoftPhoneClient: helper class for "generic" applications that want to be notified about things by a SoftPhone application. Used by ScreenSaver to be able to work with SoftPhone applications on the system, as the SoftPhone doesnt know about screensavers
For all of these objects, I'm sure there's some pattern for them. Philip, help! :-)
This should be fairly clear I guess... If not, ask. Lots of things can/will/should change, eg authorisation, and issue raised by sri yesterday. Don't know yet how this should be tackled thogh.
Duplicate events should be prohibited too (although this can for a big part be done by having well designed interfaces): when app A asks MusicPlayers to pause, then B does the same, and A sends a "Resume" signal, the player(s) should not resume unless B also sends the "Resume" command. This and other issues will need to be fixed!
After all a pretty fun day :-) Although you can't imagine how much I hate Blast by now :> Big thanks to all help on IRC, I should get all of you a beer at GUADEC. Sadly enough due to exams I can't be there though ;-)
04/18/06
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!
04/17/06
Writing code on easter day, after spending a day with your family (including the obligatory champagn/wine/beers) isn't such a good idea (#gnome-nl):
23:43 <@ikke> damn, help! 23:43 <@ikke> g_return_if_fail(foo() != TRUE) 23:43 <@ikke> returnt dat nu al foo() TRUE is, of FALSE? 23:43 <@ikke> ik ben helemaal aant flippen :s 23:43 <@kris> het failt als foo() != TRUE niet true is 23:43 <@kris> dus als foo() true returned 23:43 <@ikke> dus returnt als foo true is 23:43 <@kris> oh nee 23:43 <@kris> wacht ff 23:43 <@ikke> kijk, jij ook al :p 23:44 <@kris> all foo() false returnt, is foo() != TRUE true 23:44 <@kris> ja 23:44 <@kris> dus het failt als foo() true returnt 23:44 <@ikke> dus ie returnt als foo true is 23:44 <@kris> want dan is foo() != TRUE false 23:44 <+qball> leuk he :D
For the people who dont know dutch out there:
23:43 <@ikke> damn, help! 23:43 <@ikke> g_return_if_fail(foo() != TRUE) 23:43 <@ikke> does it return if foo() is TRUE, or FALSE? 23:43 <@ikke> I'm going nuts :s 23:43 <@kris> hit fails when foo() != TRUE isn't true 23:43 <@kris> so, when foo() returns true 23:43 <@ikke> so it returns when foo is true 23:43 <@kris> oh no 23:43 <@kris> wait a moment 23:43 <@ikke> look, you got the same issue :P 23:44 <@kris> when foo() returns false, foo() != TRUE is true 23:44 <@kris> yes 23:44 <@kris> so it fails when foo() returns true 23:44 <@ikke> it returns when foo is true 23:44 <@kris> caus then foo() != TRUE is false 23:44 <+qball> fun heh :D
Oh well, it's fixed :-)
I got Gentopia SVN commit access now. Gentopia is a project set up by some Gentoo devs aiming to get the Project Utopia stack properly integrated in the distribution. I committed an updated networkmanager-vpnc version, enjoy ;-)
Cleaned up the NetworkManager Gentoo backend a little too.
Could someone tell me how to mark a bug as a dupe in Bugzilla?
No CDIS mails yet, busy for some uni project (link) taking too much of my time, which I don't like actually ;-) Looks like some people picked up the idea though, great!
Next to that: jay for new fglrx drivers! Now I *will* be able to run Linux on my laptop when I get it (will not be within next 2 months though I'm afraid :-(), which is some *great* news.
04/12/06
Well, proof of concept, it's not that much actually :-)
Been fighting with DBus today to get something as I described yesterday working. Thanks to the people on the mailing list and IRC (#dbus@freenode) for all help, hints and pointers!
I hacked together a very small sample in Python (yes, I hardly know Python, so the code might be very very bad, I just wanted to test whether what I want to do is possible using DBus).
The setup uses a central daemon (don't shoot! It looks like it's the only usable way to cut the job) which keeps track of all objects that want to implement some CDIS interface. Objects subscribe to the concentrator (thats how I called it anyway), and clients talk to it too.
See this screenshot to get an overview how it works. First the concentrator is started. Then the user starts Gaim and Kopete (this is just a sample, obviously). They both register to the concentrator, which keeps track of them. Now some user app (think "a screensaver") connects to the concentrator, and asks it to set the status of all IM clients to "idle".
The concentrator receives this request, checks his list of connected objects, and runs the "SetState(s)" method on all objects implementing org.freedesktop.CDIS.InstantMessenger.
All of this is completely transparant to the actual application: it doesn't know about DBus, it shouldn't care about subscribing to the concentrator (unsubscribing can be done using NameOwnerChanged etc, not implemented yet) etc. Just provide all functions to implement the inteface.
The best way to tackle this, I think, is to write the specs using some XML format (we might want to look at the dbus-glib-bindings format), then generating code using that (plain C using function pointers, GObjects and their signals, QObjects and their in-process signal mechanism, dunno about the best way to do this in Python and others). It should be fairly painless to get this working.
Other TODO's:
- Write a *decent* concentrator (this one can be started using DBus activation on the first subscription call, jay!) that's error-savvy, handles killed applications, can scan the bus on startup for existing objects it should handle,...
- Write a spec XML format
- Write specs (this one will be the hardest part I guess)
- Get desktop apps to use this beast!
- Tons of other things
[edit] Per RealNitro's request, code is online. Beware, it's ugly.
I've been thinking: for the Python "bindings", function decorators could be very well suited...
Tomorrow I'll send something about this to XDG, I hope something useful can come out of this.