Screen Lock on the Freerunner

28 January 2009, 02:56 UTC

(golly, nearly two years since I have posted anything. I probably just think that I have nothing interesting to say...)

About 6 months ago I purchased an Openmoko Neo Freerunner - a nice (if not totally modern) piece of hardware that could make a nice feature-phone if it had working software (which some day it probably will). I've been playing with it on-and-off over the months, though have never used it as a regular phone.

Just lately I have had various bits of leave which have allowed me time to play more intensively with the phone and get it to do some things that it didn't before, or to get it to do them differently. And I thought it might be nice to write about some of that.

Background: I use the "FSO" distribution. However I've discarded illume and e-wm in favour of matchbox-window-manager and matchbox-panel. They might not look as pretty by they seem to be much lighter on resources and they stay quite nicely out of my way. I also don't use "zhone" and have disabled most of "frameworkd"...

All of my programming has been with python using the GTK toolkit for display (via pygtk). I wouldn't say that GTK is a particularly good tool kit (I've used TK before (with TkTcl) and my memory is that it was a much more well rounded kit. But memory could be playing tricks on me). I'm using GTK simply because it was something different but easy to learn.

The first little project I want to talk about and share is my program for locking the screen.

Screen Locking

This project grew out of a frustration while using tangogps. The screen would dim after a period of non-use, I would tap the screen to wake it up, tangogps would get the tap event and pop up a menu, which would obscure the map that I tapped in order to be able to see better. There had to be a better way.

Of course there is. When the screen is dimmed, tap events used to wake it up should not be passed on to the application. Linux provides a very simply way to do this. Input devices (such as /dev/input/event1 which received touch screen input) can be 'grabbed'. When this is done, only the program which owns the grab will get to see any events (normally events go to every program which has the device open).


Unfortunately earlier Openmoko kernels had the feature broken, for reason that I won't go in to here. In recent kernels this is fixed so EVIOC_GRAB can be used.

Now I don't want screen taps to always wake the device up. I've had enough trouble with my normal mobile phone making incorrect calls because of varying pressure on the keyboard while in my pocket (those times that I forgot to lock it). When the screen has gone completely blank, I wan't more than just a tap to wake it up. I could probably arrange some unique sequence of taps, such as once in each corner. But as the Freerunner has two buttons which cannot easily be tapped by accident, just using them to wake up from "blank" is sufficient.

So that is what the program does. After 30 seconds of inactivity, the screen is dimmed and the input devices (screen and two buttons) are 'grabbed' so nothing else will respond to them. 15 seconds later the screen is turned completely off.

When the screen is fully on, any tap or button restarts the 30 second inactivity counter. When the screen is dim, any input wakes up, un-grabs, brightens the screen, and restarts the counter. If the screen is off, then taps are ignored and it requires a button press to wake it up again.

Auto-Suspend

But I want more than just blanking the screen, I want auto-suspend as well. If I'm not using the phone, I want it to go to sleep to save power. So after 30 seconds of blank screen, we consider suspending the system. But there are a number of things that will stop the suspend from happening.

My first thought was to disable suspend if there are any active TCP connection to other computers. After all, if I've 'ssh'ed in and am working on the phone remotely I don't want it to suspend. However it turned out that this didn't work very well. It is too easy to leave half-active connections around (e.g. when a network interface goes does) that are indistinguishable from really active connections. So I gave up on that idea.

Mostly when I'm working remotely the device is plugged in to external power (either charger to usb cable from computer) so I disable auto-suspend if there is a power source present. I also disable if the load average is greater than 0.5 as there is an implication that the CPU is doing something useful. It turns out that this is enough to stop the phone from suspending while playing music.

I also have a lock file that I get an exclusive lock on before entering suspend. The idea is that other applications that want the system not to suspend simply get a shared lock on that file and they can be certain of avoiding auto-matic suspend (manual suspend can still happen). However I haven't really used this yet.

User Control

The last bit missing is that sometimes the automatic choice is not a good choice. Sometimes I don't want the screen to dim or blank, but I don't want to keep tapping it. For this I have a panel widget what can cycle between "automatic" mode - as described above, "disabled" mode where the screen never blanks, and "blank only" mode which allows the screen to blank but disables auto-suspend.

Fine tuning

But then there is Sudoku. Sometime while playing sudoku I need to think for a while and I don't want the screen to blank every 30 seconds as that breaks my train of thought. But nor do I want to disable the screen blanking as if I get distracted, I want the device to save power.

So if the device is woken up while the display is dim, the assumption is that the user is attentive but inactive so a longer timeout is appropriate. The 30 seconds is extended by 30 seconds every time we wake-on-dim until it reaches 2 minutes. So if I stare at the sudoku screen for more than 2 minutes I'll have to tap to wake up up, but I think that is a reasonable compromise. When the screen is eventually blanked, the timeout reverts to 30 seconds.

Problems

The solution works well but probably isn't perfect. By grabbing the two buttons I also grab the 'headset is inserted' event, but I completely ignore that event. So any headset insertion while the device is not at full brightness will be ignored. I need to work out what best to do about that.

Source Code

You can grab the latest source code for my lock program, which includes pixmaps for the various states of the panel widget from by git tree via

git clone git://neil.brown.name/freerunner
or browser it at http://neil.brown.name/git/freerunner.

There is no installation script, you have to do things by hand. The .png files need to go in /usr/local/pixmaps/ and the lock.py needs to go at e.g. /usr/local/bin/lock.


All code released under GPLv2.







[æ]