QVINTVS · SCRIBET

surf-Browser mit neuen Tastenkürzeln versehen

Der Browser surf kann durch Bearbeiten der Datei config.h u.a. um neue Tastenkürzel erweitert werden, was allerdings nicht wirklich dokumentiert ist.

Vor kurzem bin ich auf Gentoo Linux umgestiegen. Mein erster Eindruck soweit: Man muss viel kompilieren. Sehr viel. Abgesehen davon bin ich aber nunmehr systemd-frei unterwegs, was die langen Kompilationszeiten wieder wettmachen sollte.

Als Browser habe ich mich für den Moment für surf entschieden, weil dieser Browser ab Werk eine annehmbare Tastaturbedienung vorsieht, die ich selbst mit Addons in Firefox nicht erreichen konnte. Abgesehen davon ist surf auch erfreulich klein. Die Kompilationszeit von Webkit allerdings sollte man nicht unterschätzen.

Das Ebuild für surf unterstützt die besondere USE-Flagge savedconfig, die es erlaubt, die standardmäßige config.h-Datei mit einer eigenen zu ersetzen. Im Fall von surf definiert diese Datei insbesondere die Tastenkürzel, die in der Standardkonfiguration recht gewöhnungsbedürftig sind (sie sehen für mich Emacs-Nutzer aus, als wäre ein Vi-Nutzer dafür verantwortlich gewesen).

Um eine eigene config.h zu verwenden und diese Tastenkürzel durch eigene zu ersetzen, geht man wie folgt vor.

Zunächst ist surf überhaupt erst einmal zu installieren.

# emerge --ask www-client/surf

Danach teilt man Portage mit, dass eine eigene config.h anstelle der standardmäßig in surf mitgelieferten zu verwenden ist. Dies geschieht durch Zuteilung der savedconfig-USE-Flagge an das entsprechende Ebuild, was widerum durch Bearbeitung von /etc/portage/package.use zu erreichen ist; ich organisiere diese Konfiguration als Verzeichnis, daher war das Erstellen einer neuen Datei /etc/portage/package.use/surf mit folgendem Inhalt erforderlich:

www-client/surf savedconfig

Dabei wird davon ausgegangen, dass savedconfig nicht bereits als globale USE-Flagge gesetzt ist.

Sodann kann die config.h bearbeitet werden, die Portage bei der ursprünglichen Installation von surf in /etc/portage/savedconfig/www-client/surf-<version> abgelegt hat.

In dieser Datei lassen sich diverse Standardeinstellungen bearbeiten, für diesen Artikel relevant sind ausschließlich die Tastenkürzel, die sich ihrerseits in zwei Abschnitte unterteilen, deren erster mit dieser Zeile beginnt:

#define SETPROP(p, q) { \

Die Struktur ist so gedacht, dass man im Bereich der #define-Direktiven Aufrufe an externe Programme kodiert. Dies ermöglicht es, im zweiten Teil der Tastenkürzelkonfiguration auf die hier definierten Makros zurückzugreifen und diesen schön formatiert zu halten; ein zwingendes Erfordernis ist die Einhaltung dieser Struktur jedoch nicht. Sie ist aber aus Übersichtlichkeitsgründen empfehlenswert.

Die Makros selbst definieren jeweils ein Array von Strings, dessen einzelne Elemente später als Argumente für den einzelnen Programmaufruf genutzt werden. In der Regel wird man als Programm /bin/sh aufrufen, um unter Durchsuchung der Umgebungsvariable PATH ein beliebiges Programm aufrufen zu können.

Die Argumente für die Makros sind frei wählbar und werden während der Auflösung im zweiten Teil der Konfiguration einfach ersetzt. Es ergibt sich grundsätzlich folgende Struktur:

#define MAKRO { .v = (char* []){ "/bin/sh", "-c", \
"PROGRAMM ARG1 ARG2 ...", SUBST1, SUBST2, ..., NULL } }

Die nach den Strings folgenden Parameter SUBST1 usw. dienen der Interpolation von Werten in die eigentlichen Strings; die Liste der Ersetzungen ist mit NULL zu beenden. Der besondere Wert winid gibt dabei die Window-ID der laufenden Instanz von surf an (zu deren Gebrauch sogleich).

Die von surf standardmäßig definierten Makros SETPROP und DOWNLOAD sind komplizierter aufgebaut, was aber nur daran liegt, dass sie (unnötigerweise) Argumente annehmen und recht komplexe Befehle absetzen. Als Beispiel für ein neues Tastenkürzel, welches eine Websuche mithilfe von DuckDuckGo startet, könnte das Makro etwa so aussehen:

#define SEARCH { .v = (char *[]){ "/bin/sh", "-c", "search $0", \
                 winid, NULL } }

Dies ruft ein Programm search auf und übergibt ihm als erstes und einziges Argument die Window-ID von surf (man beachte, wie $0 durch das erste Argument nach den Strings, winid, ersetzt wird). Das Programm search existiert freilich noch nicht, in diesem Beitrag soll es mithilfe eines kleinen Ruby-Skripts realisiert werden, welches man als /usr/local/bin/search speichert (Ausführbarmachen mit chmod a+x /usr/local/bin/search nicht vergessen) und folgenden Inhalt hat:

#!/usr/bin/ruby
require "uri"

wid=ARGV[0].strip
term = `echo " " | dmenu -p Duckduckgo: ` .strip

exit 0 if term.empty?

target = "https://duckduckgo.com/?q=#{URI.escape(term)}"

system "xprop -id #{wid} -f _SURF_GO 8s -set _SURF_GO #{target}"

Natürlich hätte man auch ein gewöhnliches Shell-Skript benutzen können, aber dann wäre es schwierig geworden, den Suchbegriff in das für URLs nötige Format zu überführen. Ruby bietet mit der URI-Bibliothek jedoch eine Methode URI.escape an, die die notwendigen Ersetzungen (insbesondere von Abständen durch %20) vornimmt.

Nachdem diese Bibliothek per require "uri" eingebunden wurde, ruft das Skript zunächst das übergebene Kommandozeilenargument ab, bei dem es sich wie bekannt um die Window-ID von surf handelt. Anschließend ruft es das sehr universell einsetzbare Programm dmenu auf, welches den Nutzer nach einem Suchbegriff fragt; leider ist es mir nicht gelungen, dmenu ohne Suchwortliste auf der Standardeingabe zu starten (die in diesem Kontext einfach keinen Sinn macht). Daher habe ich dmenu einfach ein einzelnes Leerzeichen als Suchwort übergeben, was vielleicht nicht elegant ist, aber funktioniert. Vorschläge zur Verbesserung nehme ich gern entgegen.

Bricht der Nutzer die Eingabe in dmenu ab, gibt das Programm nichts aus. In diesem Fall wird auch das Skript beendet und tut nichts weiter.

Hat der Nutzer dagegen eine Eingabe vorgenommen, wird diese zunächst mithilfe von URI.escape in ein URL-freundliches Format überführt und dann in eine URL eingesetzt, die unmittelbar die Suchfunktion von DuckDuckGo aufruft.

Die letzte Zeile ist die eigentlich interessante: Hier wird die so konstruierte URL wieder an surf übergeben. surf bietet dafür einen wohl einzigartigen Mechanismus an: X11-Properties. Dazu muss man wissen, dass auf einem X-System jedes Fenster eine beliebige Liste von Eigenschaften (Properties) besitzt, die sozusagen Meta-Informationen über das Fenster enthalten. So ist etwa der Fenstertitel in der Eigenschaft WM_NAME enthalten. Jedes Fenster darf beliebig viele Eigenschaften haben und neue definieren, nur ein begrenzter Satz ist durch die EWMH (extended window manager hints) standardisiert. Mithilfe des Programms xprop können diese Eigenschaften sowohl ausgelesen als auch geschrieben werden.

surf bietet nun vor allem drei relevante Eigenschaften in seinem Fenster an:

WM_NAME
Dies ist der Fenstertitel. Entspricht einem Präfix von surf selbst, das mit einem Balken | abschließt, der vom Inhalt des <title>-Tags gefolgt wird.
_SURF_URI
Diese nicht standardisierte Eigenschaft enthält die URI der Webseite, die surf gerade anzeigt.
_SURF_GO
Wird in diese nicht standardisierte Eigenschaft geschrieben, ruft surf die hier hinterlegte URI auf. Will man also erreichen, dass surf eine Seite aufruft, schreibt man deren URI hier hinein. In der Folge werden _SURF_URI und WM_NAME automatisch aktualisiert und die neue Webseite dem Nutzer angezeigt.

Die besondere Eigenart von _SURF_GO macht sich obiges Skript zu Nutze, um surf direkt auf DuckDuckGo zu schicken. Der Parameter -id gibt an, welches Fenster xprop bearbeiten soll, -f gibt den Typ für eine Eigenschaft an. Da _SURF_GO gesetzt werden soll, wird auch deren Typ festgelegt, und zwar auf 8s — das scheint ein gewöhnlicher String zu sein. Ich habe das nicht weiter kontrolliert, sondern einfach aus der Definition des bereitgestellten Makros SETPROP kopiert. Die Manpage von xprop(1) jedenfalls gibt dazu nichts her. Schließlich wird mit -set verlangt, dass die Eigenschaft _SURF_GO auf den nachfolgenden Wert geändert wird, bei dem es sich um die vollständige URI zur Suchfunktion von DuckDuckGo handelt. Sobald surf die Änderung der Eigenschaft bemerkt, ruft es die neue Seite auf und der Nutzer sieht die Ergebnismaske von DuckDuckGo.

Nachdem dieses Skript abgespeichert wurde, muss es noch an ein Tastenkürzel in surf gebunden werden. Dies geschicht im zweiten Teil der Konfiguration in config.h, der langen Liste nach dieser Zeile:

static Key keys[] = {

Jeder Eintrag der Liste folgt einem einfachen Schema:

{ Modifier, Haupttaste, Typ, Ziel },

Die verschiedenen Aktionstypen lassen sich der bestehenden config.h ohne weiteres entnehmen. Modifier bezeichnet die Modifikationstasten, also etwa Umschalt und Strg (das Makro MODKEY zeigt standardmäßig auf die Strg-Taste). Die zulässigen Werte für die Haupttasten lassen sich der Datei /usr/include/gtk-2.0/gdk/gdkkeysyms-compat.h entnehmen.

Für dieses Beispiel relevant ist nur der Aktionstyp spawn, der ein externes Programm startet. Folgende Zeile kann daher der Konfiguration hinzugefügt werden:

{ MODKEY, GDK_d, spawn, SEARCH },

Damit wird für die Tastenkombination Strg+d SEARCH aufgerufen. Dieses Makro wurde weiter oben bereits definiert; es ruft das Skript search auf. Damit sind die erforderlichen Änderungen vorgenommen.

Zuletzt muss surf neu kompiliert werden. Da die USE-Flagge bereits angepasst wurde, genügt dazu:

# emerge www-client/surf

Auf diese Weise lassen sich beliebige neue Befehle in surf einbauen und bestehende verändern.

Valete.