Wednesday, March 24, 2010

krunner, QDBusServiceWatcher

Ah, KRunner, lovely time saver, let me count the ways I love thee. :) This blog entry is about KRunner and has something for "everyone": user, artist, developer ...

One of the most immediately obvious changes in truck right now is the streamlining of the look. Perching KRunner at the top of the screen has been very successful, but it did make it very obvious to my eyes just how unnecessarily big the window was when it first appeared. It really doesn't look very much like the original mockups, at least in the details. So I chopped down the margins and replaced the odd CSS-styled buttons (a relic of the 4.2 release, if I recall correctly) and switched it to use the same toolbuttons seen elsewhere in the Plasma Desktop shell. Here's what it looks like when it appears:



... and with mouse hovers and keyboard focus on a button in a rather unnatural pose to illustrate the various button states ...



I'm hoping to get some new artwork for the '?' and 'X' icons and we (Pinheiro and I) are toying with an even more radical concept: making it possible for KRunner to be always visible with "windows can cover" style autohiding known from plasma-desktop's panels. That hasn't been done yet, but I plan to at least put it in as an experiment for the 4.5 betas. Pinheiro has also suggested putting the hardware related entries from the system tray in there as well. We'll see.

The other day someone came into the #plasma channel on IRC and asked how to get the unit conversion runner to "convert" timezones. I said that it sounded more like a good case for a new Runner plugin altogether and that it wouldn't be hard. I was having an annoying day and so decided to see how quickly I could pound something like that together. Answer: ~15 minutes. The KTimeZone classes and the Plasma::AbstractRunner architecture made it painfully simple (112 lines of code, counting the C++ header and all the usual C++ fluff, most of it boilerplate with some snippets borrowed from the time DataEngine) and now you can do things like this:



It also understands things like "date cest". You can mix and match the words "date" and "time" with a city or timezone name and it spits out the results. Activating the answer moves it into the edit area (so you can edit it if you wish) and copies the text to the clipboard. This is true of any informational match in KRunner, actually, something I find really handy when doing simple bits of math.

This also prompted me to improve a little thing with the informational match selection: it now puts the query ("time oslo"), rather than the answer ("17:20"), into the command history. Speaking of the command history, hopefully I'll be able to blog shortly about command history and keyboard navigation improvements as well. ;)

In addition to the date and time Runner, there is also an events runner under review that fetches and adds todos and events from Akonadi. So KRunner will get to continue its "a few more plugins every release" pace. More are always welcome.

Finally, some DBus foo that others using DBus in similar ways may benefit from: Today I discovered QDBusServiceWatcher, a new class in Qt 4.6. Some of the runners use DBus services, and when that service isn't available they will stop processing queries and when the service appears they will start again. Up until now they were using the QDBusConnectionInterface::serviceOwnerChanged signal, but this results in a message every time anything appears or disappears from the bus. Yes, that means every time a KDE app (or other DBus using app) started or quit. Obviously, this isn't great.

My first bit of research uncovered DBus match rules, which among other things can be used to prevent signals from being send from the DBus server that the receiving application already knows it won't care about. In the case of the plasma-desktop-runner (responsible for things like "desktop console" in KRunner), it only cares about serviceOwnerChanged when the service in question is org.kde.plasma-desktop. I figured I could send an appropriate match rule and avoid KRunner being woken up and doing some processing every time an app starts/stops. Turns out QtDBus can already do this for us by defining the signature parameters when connecting to a signal. Even better, this is exactly what QDBusServiceWatcher does specifically for watching for specific services coming and going! With each bit of information I dug up, I discovered I'd have less work to do. Awesomeness! I heart Qt.

One Runner plugin is now using QDBusServiceWatcher and by the end of the day so will all the rest of the code in the Plasmasphere that needs it. There are somewhere over 100 other files in KDE that should similarly be moved to QDBusServiceWatcher.

One more trick came to light yesterday via Alex Merry's exploration into pauses and hiccups related to the audio player Runner. Since queries are processed in threads, Runners are able to keep complexity down by not worrying about blocking. So they tend to make non-async DBus calls. Unfortunately, this leads to pauses elsewhere as synchronous DBus calls far too easily end up blocking each other, defeating the purpose of the threading altogether in those cases. Fortunately there is asyncCall() which returns a QDBusPendingReply. Unfortunately this means writing async code even in the threads where we don't care too much about blocking for a bit here and there, increasing the complexity there .. or does it? QDBusReply has a constructor that takes a QDBusPendingReply, and in that case it blocks waiting for the async reply to come back (or timeout, in the failure case). Which means code like this in a thread:

QDBusReply reply = favicon.asyncCall("iconForUrl", url.url());


will cause the thread to block (preserving the simple, clear synchronous code path) while the DBus call itself is asynchronous. All in one line of code. Beauty! I heart Qt some more.

17 comments:

xenoterracide said...

yes. krunner is awesome hawtness... except when it's frozen or crashing. which it seems to enjoy doing in 4.4.x... which I'm very annoyed about. I wonder what changed between 4.3 and 4.4 that caused it to be so unstable...

Snirp said...

Would it not be better to implement krunner as a panel widget? That way you can have autohide on/off in the way the user is accostumed.

Aaron J. Seigo said...

@xenoterracide: mostly dbus stuff, though there was one simple-to-fix but annoying crash in the query type detection code that made it into 4.4.0. that should be fixed in 4.4.1 (or 4.4.2? 4.4.1 i think..), but at this point it's mostly dbus related annoyances.

i fixed a big one in the Places runner and the work i mention regarding bus in threads kills a host of other ones.

@Snirp: not really; most of the code is already shared via libplasma (RunnerManager, RunnerContext, AbstractRunner, QueryMatch, ...), but we wanted krunner and plasma-desktop to be separate processes so that losing one didn't leave you completely hosed.

KRunner also provides other things like the screen locking, startup feedback and system activity window that we didn't want in plasma-desktop.

xenoterracide said...

@aseigo must be some of it is in 4.4.2 because i'm running 4.4.1. I hope it's stable in 4.4.2

tidbeck said...

"I'm hoping to get some new artwork for the '?' and 'X' icons". Why, it looks great, like some cool space, action game from 2006. I actually clicked on you blog to comment on the nice look :)

rahman said...

@xenoterracide

I agreei. Krunner is freezing so much. I have disabled all plugins except two or three and I am happy with it know. No more freezes. So I guess its something about plugings.

sscherfke said...

Oi, krunner slowly gets as mighty as a console. The next logical step might be to replace the single input line with a stylish terminal window (you can see all your last comands then!!!!!) and we are—concerning usability—back in the 90s again.

;-)

Damiga said...

"making it possible for KRunner to be always visible with "windows can cover" style autohiding"
Hopefully autohiding will be an option. I like the current form and use a drop down plasma panel that I fear may conflict.
http://twitpic.com/1at58g/full

Chani said...

ahhh, that explains a few things. :)

@rahman: funny, I tried that once, all it did for me was make the crashes less frequent (back when there was about a 70% chance of it crashing on any query).
my list of plugins has been creeping upwards again, maybe I'll strip it down to just applications and live without the calculator and so on for a bit...

hrm. I wonder if that sort of dbus change could help kmail in 4.4.x freeze less too...

xenoterracide said...

If plugins are the problem then krunner needs to work on making itself still work even if one plugin has stopped responding. I really only care about being able to open it to run a command. I also showed another users how to open the task manager and kill a process with it. So applications, and command line. Are all I need. I'm pretty sure I really only need one of those 2. basically I just need it to act as a run prompt, anything else is flavor and I can't have my run prompt breaking just because nepomuk decides to go wonky (or whatever the problem is). Imagine if dolphin stopped working every time nepomuk went crazy.... what I can't browse files just because the search index doesn't want to work? that doesn't make sense.

xenoterracide said...

Actually... I'd really like to see KDE get chrome's idea. Plugins run in their own process. Tabs run in their own process.

rahman said...

@Chani
@xenoterracide

Well, after some more testing, I still have freezes but not very often like before disabling plugins. But I can say this; every new feature added to krunner made it more freezing for me. In early 4.1.x I don't remember any freeze. 4.3.x was ok if I remember correcly. 4.4.x damn, it is like a nightmare, I hesitate to use it in front of my friends that use Windows. Just disabling plugins made it MORE RESPONSIVE but I still get random freezes sometimes.

It would be great if somebody seriously think to optimize or fix it :)

panzi said...

To people for which krunner hangs an crashes all the time in KDE 4.4.x:
Try deactivating the "Control Audio Player" plugin. There is a unknown type in amaroks D-Bus interface which causes this plugin to hang and then crash. It did the trick for me.

xenoterracide said...

@panzi it's not even on my system that I have seen... so it's not and has never been the problem causer in my case

thiago said...

Hi Aaron

A couple of tidbits on QDBusPendingReply: you don't have to write asynchronous code to use it. I would recommend it in any and every case over QDBusReply.

First of all, it supports up to 8 parameter templates, so you can get multi-return calls easily.

Second, just like QDBusReply, if you try to access the value, it will implicitly waitForFinished().

Aaron J. Seigo said...

@thiago: all good points. i've also discovered (the hard way: debugging a crash in someone else's code :) that when you get a QDBusPendingReply, any errors will appear in the QDBusPendingReply but not the QDBusAbstractInterface. so if you need to do error handling, QDBusPendingReply is the only way to go.

Diego Viola said...

I'm experiencing many krunner crashes in 4.4.x too.

When krunner crashes I have to kill the process and restart krunner again so ctrl+esc (process manager) and alt+f2 (krunner) would work again.

I also get this from in the konsole, when I run krunner:

[diego@myhost ~]$ X Error: BadWindow (invalid Window parameter) 3
Major opcode: 20 (X_GetProperty)
Resource id: 0x1e004ec

[diego@myhost ~]$ X Error: BadWindow (invalid Window parameter) 3
Major opcode: 20 (X_GetProperty)
Resource id: 0x1e008fb