Almost every productive person I know wishes that he or she had more time. Most of us wonder where our time really goes; often it’s noon by the time I finish reading my feeds and following up on interesting URLs. (Fortunately, I’m not a morning person, so I wouldn’t accomplish much before noon anyway.)
I’ve often wished for a short X.org program that would run in the background and monitor which window had active focus. If I tracked that for a few hours or days, I’d be able to perform some interesting statistical analysis to see where I actually spend my time.
Writing a prototype took about ten minutes, thanks to Dennis Paulsen’s X11::GUITest Perl module (see Test-Driving X11 GUIs by George Nistorica for more):
Perl Prototype
#!/usr/bin/perl
use strict;
use warnings;
use X11::GUITest qw( GetInputFocus GetWindowName GetParentWindow );
while (1)
{
my $w = GetInputFocus();
my $name = GetWindowName( $w );
until ($name)
{
$w = GetParentWindow( $w );
$name = GetWindowName( $w );
}
warn "$name\n";
sleep( 1 );
}
I did notice in some brief manual testing that not all windows report a name; I suspect that some of the X11 toolkits use multiple Xlib windows to make what appears to be a single window in the GUI. (Firefox was one offender.) That’s the reason behind the loop to get a real window name.
C Prototype
After browsing the XS source code of the Perl module, I decided to write my own version in straight C against Xlib. (The X Window System programming books sitting six feet behind me on a bookshelf come in useful once in a while.) Though I’ve never found a program like this before, and I’m sure there are half a dozen that you’re rushing to tell me about right now, here’s the source code to the C version.
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <signal.h>
#include <unistd.h>
static int (*old_error_handler)(Display *, XErrorEvent *) = NULL;
static Display *d;
int ignore_bad_window(Display *d, XErrorEvent *e)
{
/* Ignore bad window errors here, handle elsewhere */
if (e->error_code != BadWindow) {
assert(old_error_handler);
(*old_error_handler)(d, e);
}
return 0;
}
int
check_window(Display *d, Window w)
{
XWindowAttributes wattrs = {0};
int status;
old_error_handler = XSetErrorHandler(ignore_bad_window);
status = XGetWindowAttributes(d, w, &wattrs);
if (!status)
{
fprintf( stderr, "Bad window %d (%d)\n", (int)w, status );
exit( EXIT_FAILURE );
}
XSetErrorHandler(old_error_handler);
return 1;
}
char *
get_window_name(Display *d, Window w)
{
char *name = NULL;
if (!check_window(d, w))
return NULL;
XFetchName(d, w, &name);
return name;
}
Window
get_parent_window(Display *d, Window w)
{
Window root = 0;
Window parent = 0;
Window *children;
unsigned int num_children;
if (XQueryTree(d, w, &root, &parent, &children, &num_children))
XFree(children);
return parent;
}
static void
sigint_handler(int signo)
{
if (d)
XCloseDisplay(d);
exit( EXIT_SUCCESS );
}
int
main (int argc, char * argv[])
{
if (signal( SIGINT, sigint_handler ) == SIG_ERR) {
fprintf( stderr, "Couldn't handle SIGINT!\n" );
exit( EXIT_FAILURE );
}
d = XOpenDisplay(NULL);
if (!d)
{
fprintf( stderr, "Cannot connect to X server\n" );
exit( EXIT_FAILURE );
}
while (1)
{
Window focused = 0;
int revert = 0;
char *name = NULL;
XGetInputFocus(d, &focused, &revert);
do {
name = get_window_name(d, focused);
if (name)
break;
focused = get_parent_window(d, focused);
} while (!name);
printf( "Focus in window '%s'\n", name );
XFree( name );
sleep( 1 );
}
}
Save it as watch_focus.c and compile it with:
$ gcc -Wall -lX11 -o watch_focus watch_focus.c
Run it with ./watch_focus and use Ctrl-C to finish.
Future Expansion
Right now the statistics and reporting aren’t terribly useful, and the one-second sleep might be too short. However, the internals of the program are there. Reporting the time spent in a window, as well as the window ID, seems useful. Perhaps instead of printing to standard output, the right decision is to write to a log file for the current user. It might be nice to detect when the user is idle and ignore reporting then, too.
This was a fun little diversion, and I only went down one rabbit-hole: trying to listen for all focus change events in the X server. Apparently you can only do that for one window at a time, which seems sensible.
A lot of programmers I know don’t do much GUI programming because there are so many GUI toolkits available. Though this isn’t a GUI program, it hooks into Xlib, and it wasn’t a painful experience at all. I wouldn’t dream of suggesting that more GUI programs should use Xlib directly, but there are probably plenty of great programs that could make use of X.org for various non-display reasons.

"it hooks into Xlib, and it wasn’t a painful experience at all"
It seems you have a strange idea of what "painful" is!
@tim, given that I've found and fixed several GC errors in Parrot recently, my threshold for pain is indeed far beyond what anyone's probably should be.
try kArm.
Hi chromatic,
You might be interested in http://getmeetimer.com/, a firefox plugin that tracks what pages you have exposed in tabs, for how long with logging, grouping and even warnings and "discouragement".