2011-01-28

XKB and groups

This is really short because of the time, but I just found out that Gnome's keyboard switcher uses XKB's notion of groups internally. If you have USA as your first preference and United Kingdom as your second preference, it maps UK in as Group 2.

This messes stuff up if you've doctored up your own custom symbol file that uses multiple groups. I just spent a good day trying to figure out why this didn't work, and it turns out it's because of that.

The moral of the story is: keyboards added in Gnome's keyboard switcher (System → Preferences → Keyboard → Layouts) will get mapped in as groups and override any groups you have in your symbols file, so make sure and use only one layout, or modify your symbol file to use, say, groups one and four, instead.

I'll write a post on how to configure the control key to switch groups tomorrow or something. And a post on how to actually add new symbols to a keyboard. There seems to be a lack of XKB documentation out there.

2011-01-22

Threading, locks, and Tkinter's after_idle

I've been getting into Tkinter lately. It's one of the few Python widget toolkits that works on PythonCE. It's actually quite nice. The UIs are not the most friendly, and there are some other things I don't particularly like about Tkinter, but all-in-all, it's been fairly good. (I still like GTK+ better, but it doesn't run on PythonCE, and thus far my applications have all needed to run on my Pocket PC.)

So, I was going along, happily writing and testing, when I hit a slight problem. That slight problem came in the form of an application that just seemed to up and hang for no apparent reason. And when I say hang, I mean the entire Tkinter main loop froze up. What?

I started sprinkling print statements throughout my code to try and find where the freeze-up was occurring, and I eventually found it: it was on an attempt to acquire a lock. Aha! Deadlock! Not the most unusual thing ever. The only problem was, there didn't seem to be any other code that could block after having acquired this lock, and I wasn't using any other locks in my application.

The reason I'm posting about this is because of what ended up being the cause of this. The code that was deadlocking was code bring run on Tkinter's main loop due to a call to tk.after_idle. It turns out that a call to after_idle outside of the event loop itself will block until the currently-running event, if any, finishes. External code was obtaining this lock and then trying to use after_idle. Because the event that would then proceed to attempt to acquire the lock itself was already running, after_idle would block. The event itself would then block.

The solution I'm going to implement is to have a queue of events and a function that runs itself on the event loop 5 or so times every second, processing all the events in the queue. This isn't optimal, but it's the simplest solution I can see at present.

So, the moral of the story is: after_idle doesn't just run its argument when the main loop is idle; it blocks completely until the main loop is idle. So be careful if you're using locks, and when in doubt, use your own queue and a periodic task that runs events in the queue.

I also apologize for sounding like a dope in this post. I'm rather more tired than I usually am for some reason...