blog · git · desktop · images · contact


Eigene Layouts für Awesome erstellen

2010-09-25

Beim letzten Posting zu Awesome ging es um die Verwaltung der Fenster von GIMP. Was ich dort gemacht habe, war aber letztlich nichts anderes, als das fertige "tile"-Layout geschickt zu beeinflussen.

Ein großer Teil der Funktionalität von Awesome steckt in Lua-Skripten, so auch die verschiedenen Tiling-Algorithmen. Die Entwickler haben dabei auch auf Einfachheit geachtet, was zur Folge hat, dass man sehr leicht eigene Layouts implementieren kann. Wie das geht, will ich hier anhand eines sehr simplen Beispiels beschreiben.

Wirft man einen Blick in die Datei "/usr/share/awesome/lib/awful/layout/suit/floating.lua", dann sieht man, dass nur eine einzige Funktion namens "arrange()" nötig ist sowie ein Name ("name") für das Layout. Es kann also direkt losgehen mit einem ganz einfachen Dateirumpf, der vorerst nichts tut:

module("simple")

name = "simple"
function arrange(p)
end

Dies speichere man gemäß des angegebenen Modulnamens unterhalb von "~/.config/awesome" ab, in diesem Fall also als "simple.lua". Alternativ könnte man es auch ohne Modul und ohne extra Datei direkt in der "rc.lua" machen, indem man eine Tabelle entsprechend belegt -- so finde ich es aber sauberer. Man kann es jetzt auch schon benutzen, indem man es testweise auf irgendeinem Tag explizit setzt. Das geschieht zum Beispiel an geeigneter Stelle in der der "rc.lua" (nicht von späteren Anweisungen überschreiben lassen ;)):

require("simple")
awful.layout.set(simple, tags[1][7])

"simple" ist dabei der Modulname. Es hätte also auch "vain.layouts.superfancy" heißen können, sofern entsprechende Dateien existieren würden.

Startet man Awesome nun über "[Mod]-[Shift]-R" neu und wechselt auf Tag Nummer 7 des ersten Bildschirms, wird man sehen, dass schon einmal das Layout-Icon verschwunden ist. Klar, denn das benutzte Theme kennt mit sehr hoher Wahrscheinlichkeit kein Layout namens "simple". Interessanter ist es, ein Fenster zu öffnen. Huch? Man kann es nicht verschieben -- obwohl der Code genau so im Original auch steht. Das Floating-Layout ist leider ein Sonderfall, denn es ist im restlichen Code fest verdrahtet. Das spielt aber keine Rolle, wenn man einen Tiling-Algorithmus bauen will.

Die Funktion "arrange()" muss nun die Fenster in Pixelkoordinaten platzieren. Sowohl die sichtbaren Fenster als auch die Eigenschaften der Arbeitsfläche sind im übergebenen Parameter versteckt, welcher eine Tabelle ist, deren relevante Felder so aussehen:

p = {
    workarea = { x, y, width, height },
    clients = { ... },
    ...
}

"workarea" ist der Bereich des Bildschirms, der uns zur Verfügung steht -- wiboxes sind schon rausgerechnet. "clients" ist eine weitere Tabelle, die alle zu verwaltenden Clients enthält. Das sind alle Informationen, die ein Tiling-Mechanismus benötigt. Das Grundprinzip ist nun, über alle Clients in "clients" zu iterieren und ihnen eine Position und Größe zuzuweisen. Angenommen, man hat die Geometrie für einen bestimmten Client namens "c" berechnet, dann muss man eine Tabelle mit diesen Informationen füllen und "geometry()" am Client "c" aufrufen:

local wa = p.workarea
local g = {}

g.width = ...
g.height = ...
g.x = wa.x + ...
g.y = wa.y + ...

c:geometry(g)

Dieser letzte Funktionsaufruf ist das Kernstück und platziert das Fenster. Die Punkte sind noch auszufüllen. "wa.x" und "wa.y" müssen in dieser Form als Offset übernommen werden, da hiermit einerseits wiboxes verrechnet werden, andererseits ist hierin auch die Platzierung auf einem anderen Bildschirm versteckt.

Zurück zum konkreten Beispiel. Angenommen, es sollen alle Clients nebeneinander platziert werden. Dabei soll jeder die maximale Höhe einnehmen, in x-Richtung teilen sie sich den Platz gleichmäßig auf. Der Code dazu sähe so aus:

-- Hole ipairs() mit in die Umgebung, da dies nach module() nicht mehr
-- verfügbar ist.
local ipairs = ipairs

module("simple")

name = "simple"
function arrange(p)
    -- Tippersparnis.
    local wa = p.workarea
    local cls = p.clients

    for k, c in ipairs(cls) do
        -- Teil der Geometrie, die für jeden Client gleich ist.
        local g = {}
        g.width = wa.width / #cls
        g.height = wa.height
        g.y = wa.y

        -- Die Position in x-Richtung ist nun unterschiedlich. k ist ein
        -- 1-basierter Index der clients-Tabelle. Das erste Fenster soll
        -- bei (0, 0) sitzen, also muss 1 subtrahiert werden.
        g.x = wa.x + (k - 1) * g.width

        c:geometry(g)
    end
end

Das ist alles. Jeder Client besitzt dieselbe Höhe und Breite. Auch die Position in y-Richtung, "g.y", ist für alle gleich: Sie sitzen am oberen Rand des verfügbaren Workspaces. Das heißt, die Koordinaten des Workspaces beginnen bei (0, 0) in der linken oberen Ecke. Ferner sieht man, dass die Tabelle der Clients sortiert ist und neue Clients an ihren Anfang gesetzt werden. All die lästigen Details übernimmt Awesome. Man muss sich nicht darum kümmern, wann diese Funktion aufgerufen werden muss oder wie man vielleicht auf das Minimieren oder Maximieren von Clients reagieren muss. Auch um die Breite des Borders um die Fenster herum oder eine etwaige Titlebar muss man sich keine Gedanken machen. Das Verschieben der Clients innerhalb der verfügbaren Slots, sei es mit Tastatur oder Maus, wird einem ebenfalls abgenommen.

Bleibt nur noch zu klären, wie man dem Layout ein Icon zuweisen kann. Da hierfür das "beautiful"-Paket genutzt wird (siehe "/usr/share/awesome/lib/awful/widget/layoutbox.lua"), muss diese Information im benutzten Theme eingetragen werden. Zum Beispiel so:

...
theme.layout_simple = os.getenv("HOME") .. "/.config/awesome/simple.png"
...

Das Feld "layout_simple" wird dabei bei Bedarf aus dem Namen, der ganz am Anfang angegeben wurde, zusammengebaut.

Der hier gezeigte Tiling-Mechanismus ist natürlich trivial und nicht zu gebrauchen. Aber ich hoffe, es ist klar geworden, wie einfach man eigene Algorithmen einbauen kann. Auf diese Weise können auch mitgelieferte Layouts verändert werden: Man kopiere die Datei aus dem Systemverzeichnis in sein Home-Verzeichnis und ändere den Modulnamen. Dann kann man sich austoben. Es muss nichts neu kompiliert und kein Paket installiert werden, Awesome muss auch nicht beendet werden (er lädt nur den Lua-Teil neu -- wenn man wollte, könnte man das neue Layout auch über "awesome-client" in Betrieb nehmen, das ist dann allerdings nicht von Dauer).

Awesome!

– Nachtrag 29.11.2010: Im Paket awesome-vain gibt es einige sinnvollere Layouts.

Comments?