blog · git · desktop · images · contact


Gimp mit Awesome verwalten

2010-07-29

In der nächsten großen Version wird Gimp erstmals einen optionalen Ein-Fenster-Modus bringen. Wer will, der kann Gimp dann so ähnlich wie Inkscape betreiben. Bis es soweit ist, hat man aber noch fliegende Fenster und muss derer irgendwie Herr werden. Gerade bei Tiling-WMs führt das in der Regel dazu, dass man deren Tiling-Funktion aufgibt und stattdessen für Gimp einfach in den Floating-Mode zurückfällt -- denn für die normalen Tiling-Routinen ist Gimp einfach nicht gemacht. Das muss aber nicht sein. Die meisten Tiling-WMs sind sehr gut durch Skripte steuerbar und so auch Awesome. Wenn man ein bisschen Zeit investiert, dann erhält man ein, wie ich finde, sehr schönes Layout für Gimp. Ohne Floating-Mode.

Screenshot

Wichtiges Update: Man werfe einen Blick auf awesome-vain, das die hier beschriebene Funktionalität schöner umsetzt (eigenes Gimp-Layout).

– Nachtrag vom 18.05.2012: Mittlerweile ist Gimp 2.8 erschienen und der Ein-Fenster-Modus funktioniert auch gut. Dieses Rule-Set ist damit hinfällig (naja, man kann es natürlich immernoch benutzen, was ich aber nicht tue).

Ich habe das Endergebnis mal vorweg genommen, da man dadurch den folgenden Code aus der "rc.lua" bestimmt etwas besser versteht.

Wie man am Icon oben rechts sehen kann, wird das "tile"-Layout genutzt werden. Normalerweise besteht dieses aus einem Master-Bereich, in dem sich genau ein Fenster befindet, alle vorher geöffneten Fenster werden rechts in einer Spalte gesammelt und übereinander gestapelt. Man kann aber sowohl die Anzahl der Fenster im Master-Bereich als auch die Spalten rechts einstellen. Dadurch werden am Ende alle Bild-Fenster links im Master-Bereich landen und die Docks und Toolbox rechts in je einer Spalte. Die jeweiligen Anzahlen werden durch Signale on-the-fly verändert, falls neue Fenster entstehen oder welche verschwinden.

Screenshot

Fangen wir also damit an festzulegen, dass Gimp-Fenster immer auf einem bestimmten Tag eines bestimmten Screens stehen sollen:

gimp_screen = 1
gimp_tag = 8

-- Setze spezielles Layout (Tile) und dessen Master-Größe.
awful.layout.set(awful.layout.suit.tile, tags[gimp_screen][gimp_tag])
awful.tag.setmwfact(0.7, tags[gimp_screen][gimp_tag])

So alleine betrifft das erst einmal noch keine Fenster, also brauchen wir Regeln. Neben der weithin bekannten "Class"-Eigenschaft unterscheiden sich X-Fenster aber manchmal auch noch durch eine "Role"-Eigenschaft. Die kann man genauso mit "xprop" herausfinden wie auch die Klasse. Insgesamt sieht man dann, dass bei Gimp

Man kann also normale Regeln für Gimp aufstellen, die alle Fenster auf einen bestimmten Tag schieben und andere einfache Dinge tun. Die erste Regel betrifft dabei alle Gimp-Fenster, die nachfolgenden nur bestimmte davon.

-- Die Gimp-Regeln.
gimp_rules = {
    -- Setze alle Fenster der Klasse Gimp auf den speziellen Tag. Alle
    -- kriegen obiges Callback mit.
    all = {
        rule = { class = "Gimp" },
        properties = {
            tag = tags[gimp_screen][gimp_tag],
            size_hints_honor = false,
        },
        callback = gimp_init
    },

    -- Toolboxen und Docks explizit nicht floating, außerdem werden sie
    -- ans Ende der Fenster gesetzt. Dadurch landen die in den
    -- seitlichen Columns.
    toolbox = {
        rule = { class = "Gimp", role = "gimp-toolbox" },
        properties = {
            floating = false
        },
        callback = awful.client.setslave
    },

    dock = {
        rule = { class = "Gimp", role = "gimp-dock" },
        properties = {
            floating = false
        },
        callback = awful.client.setslave
    },

    -- Auch Bildfenster sind explizit nicht floating, aber sie werden
    -- nicht ans Ende gesetzt.
    image = {
        rule = { class = "Gimp", role = "gimp-image-window" },
        properties = {
            floating = false
        }
    },
}

Die Callbacks sind hier von besonderer Bedeutung. Toolbox und Docks bekommen "awful.client.setslave", das heißt, sie werden im "tile"-Layout ganz ans Ende gesetzt und landen damit zunächst einmal in der (einen) rechten Spalte. Alle Gimp-Fenster werden aber noch durch die Funktion "gimp_init()" gejagt, die bis jetzt noch nicht definiert wurde. So sieht sie aus:

-- Einmalig bei neuem Fenster. Verbinde alle Signale, die per Client
-- gesetzt werden können, führe außerdem einmal das Layout aus.
function gimp_init(gimp_client, args)
    gimp_client:add_signal("property::minimized", gimp_layout)
    gimp_client:add_signal("tagged", gimp_layout)
    gimp_layout(gimp_client, args)
end

Es werden also Signal-Handler gesetzt für die Fälle, dass ein Fenster minimiert oder wiederhergestellt wird oder dass es auf einen anderen Tag verschoben wird.

Da sich das noch zu berechnende Layout auch dann ändert, wenn ein Fenster geschlossen wird, brauchen wir dafür auch noch einen Signal-Handler. Leider kann der nur global für alle Fenster gesetzt werden:

-- Wenn ein Client sich schließt, dann berechne das Gimp-Layout neu.
client.add_signal("unmanage", gimp_layout)

Damit sind die Fenster mit den nötigen Handlern ausgestattet, um entsprechend zu reagieren. Was jetzt noch fehlt, ist die Funktion "gimp_layout()". Diese zählt auf dem eingestellten Tag des eingestellten Screens alle Gimp-Fenster anhand ihrer Rolle -- minimierte Fenster werden ignoriert. Danach werden dem Tag die entsprechenden Werte mitgeteilt. Da diese Funktion für jeden Client durch das Signal "unmanage" aufgerufen wird, sollte am Anfang noch einmal geprüft werden, ob es sich wirklich um ein Gimp-Fenster handelt.

-- Ein Callback, das bei jedem Gimp-Fenster ausgeführt wird, wenn es
-- geöffnet, minimiert/wiederhergestellt, geschlossen wird oder seine
-- Tags geändert werden. Berechnet im Gimp-Tag, wieviele Docks offen
-- sind und wieviele Bildfenster offen sind. Anhand dessen wird dann die
-- Anzahl der Columns und der Fenster im Main-Bereich gesetzt.
function gimp_layout(gimp_client, args)
    -- Ist das hier ein Gimp-Fenster? Falls nicht, dann raus.
    if not awful.rules.match(gimp_client, gimp_rules.all.rule) then
        return
    end

    -- Zähle verschiedene Fenstertypen. Beschränke dich dabei auf den
    -- Gimp-Screen, den Gimp-Tag und nicht-minimierte Fenster.
    local aside = 0
    local main = 0
    local gimp_tag_obj = tags[gimp_screen][gimp_tag]

    for k, c in pairs(client.get(gimp_screen)) do
        if not c.minimized then
            for k2, t in pairs(c:tags()) do
                if t == gimp_tag_obj then
                    if awful.rules.match(c, gimp_rules.toolbox.rule) then
                        aside = aside + 1
                    end
                    if awful.rules.match(c, gimp_rules.dock.rule) then
                        aside = aside + 1
                    end
                    if awful.rules.match(c, gimp_rules.image.rule) then
                        main = main + 1
                    end
                end
            end
        end
    end

    -- Setze das so auf diesem Tag.
    awful.tag.setncol(aside, tags[gimp_screen][gimp_tag])
    awful.tag.setnmaster(main, tags[gimp_screen][gimp_tag])
end

Das ist es, jetzt gehorcht Gimp. :)

Zugegebenermaßen ist das aber manchmal etwas langsam, zum Beispiel, wenn man ein Fenster minimiert. Dann müssen viele Gimp-Fenster neu gezeichnet werden und das dauert kurz. Vielleicht liegt das aber auch an meinem etwas älteren Rechner oder meinem GTK-Theme, wer weiß.

Um "herumfliegende" Gimp-Fenster muss man sich nun aber nie wieder kümmern.

Comments?