Today was a learning day. A few months ago, I
released
keynav, a tool to make large-area mouse
navigation very quick. However, I found myself using keynav to do the same
things over and over again. Select certain windows, clicking on certain UI
widgets, etc; things that are annoying to do repeatedly.
What if we had a way to describe input actions? What if you could say "Focus
the firefox URL bar" with a simple keystroke, without having firefox focused?
This premise is fairly simple - Focus firefox, then "click" on a certain part
of the window. The URL bar's location is pretty reliable (a few pixels from the
top).
If you're like me, firefox is on another virtual desktop. What if firefox isn't
shown right now because you're on "Desktop 1" and firefox lives on "Desktop 2"?
Well we can find out if it's hidden, then send the keystrokes (via XTEST) to
switch to "Desktop 2" and then do whatever we would normally do to focus on
Firefox's URL bar. The basic pseudocode of the script would look like:
if firefox is not shown:
go to desktop 2 ("fake send alt+2")
focus firefox (via fake click?)
fake send "Control+L" (Firefox's shortcut for focusing the urlbar)
Seems pretty simple. "Fake" above refers to events sent using the XTest, an extension to X11 (Xorg/XFree86) that lets you send keystrokes and mouse events as if they had been typed.
So, tonight I started work on a project that would let you script actions.
Generally, I'm aiming at scripting UI interaction to make common tasks such as
"take me to firefox" simpler.
I revisited some man pages for Xlib I hadn't seen in many months, and now I can
traverse the list of all open windows in X and show their status. Here's what
this tool outputs; notice
how I can tell if the window is visible ("shown" by the window manager):
+ 8388621 (1x16@424,324) [Visible]
teabag(.../home/jls/projects/navmacro)
"xterm" "XTerm"
+ 44040205 (1x16@424,324) [Hidden]
snack(~) % @ snack
"xterm" "XTerm"
+ 29360192 (1x15@1278,1008) [Hidden]
HMUG: man XWindowAttributes (3) - Mozilla Firefox
"firefox-bin" "Firefox-bin"
The other piece of learning I did tonight was to learn GTK2. Enough fiddling
around and I was able to get a input field that pops up over everything and is
as wide as the screen to input your macros in. GTK's not that bad. Its API
design is fairly intuitive and I didn't have much trouble getting things
working. I even figured out how to ask GTK what it's X window ID is. Most of
this knowledge comes from the
GDK X Windows Interaction
documentation.
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
static void activate(GtkWidget *widget, gpointer data) {
// widget->window is the GdkWindow containing this widget.
g_print("Display handle: %x\n", GDK_WINDOW_XDISPLAY(widget->window));
g_print("Window id: %d\n", GDK_WINDOW_XID(widget->window));
}
int main(int argc, char **argv) {
GtkWidget *window;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect(G_OBJECT(window), "realize",
G_CALLBACK(activate), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
Compiled with: gcc `pkg-config --cflags --libs x11` test.c
The little bit of GTK I wrote tonight can be found here. It's not much, but it
does example how to use GTK and Xlib at the same time, on the same windows.
Oops... it's getting light outside. Naptime ;)