blog · git · desktop · images · contact


Klebeterminals: Erweiterung

2008-05-11

Screenshot

Ok, vermutlich wird soetwas wie das hier bald dazu führen, dass ich den Window Manager wechsele - obgleich ich noch nicht weiß, ob das woanders wirklich besser/einfacher lösbar ist.

Ich hab' ziemlich schnell Gefallen am klebenden Terminal gefunden, also wollte ich mehrere davon haben. Lässt sich prinzipiell auch einfach realisieren (oder man verwendet einfach screen), wenn ich nicht einige Extrawünsche hätte. :)

Meine Lösung ist jetzt eine Sammlung von 4 teilweise nicht mehr kurzen Shellscripten.

Bin sicher, dass es irgendwie auch einfacher und/oder eleganter geht ... ;)

Prinzip

Im "globalen" Startskript wird eine Liste der Terminals definiert, die gestartet werden sollen - hier wird einfach nur deren Geometrie jeweils angegeben.

Pro Terminal wird dann ein "Keeper" gestartet, der sich darum kümmert, dass das Terminal die richtigen Fenstereigenschaften hat und nach Beenden wieder gestartet wird. Nach jedem Start schreibt er die PID in eine Pool-Datei, bei jedem Beenden wird die letzte bekannte PID gelöscht.

Beim Drücken des Hotkeys (Zuordnung wie bisher) wird dann eine erweiterte Variante des "klebeterm-showdesktop.sh" aufgerufen, welches zuerst "Show Desktop" aktiviert und dann für alle gespeicherten PID's im Pool die Window-ID für wmctrl heraussucht, um diese Fenster wiederherzustellen.

devilspie ist leider keine Lösung - hier stimmt die Positionierung der Fenster bei mir nicht mehr. Desweiteren wird nun urxvt verwendet, da dieses direkte Unterstützung bietet, "borderless" zu sein.

Im Einzelnen

klebeterm.sh

#!/bin/bash

# Wird vom Nutzer aufgerufen und hat dafür zu sorgen, dass alle
# klebenden Terminals gestartet werden und irgendwie deren Eigenschaften
# richtig gesetzt werden. Außerdem sollen sie immer da bleiben, bzw. neu
# gestartet werden, wenn sie beendet werden.

PIDPOOL="/tmp/klebepids"
GEOMS="90x55+0+0 88x27+935+0 88x27+935+585"

if [ -f "$PIDPOOL" ]; then
    rm $PIDPOOL
fi

for i in $GEOMS; do
    /home/blabla/klebeterm-keep.sh $i &
    echo "Keeper gestartet: $!"
done

klebeterm-keep.sh

#!/bin/bash

# Verwaltet ein klebendes Terminal mit der als Parameter übergebenen Geometrie.
# Sorgt dafür, dass es bei Beenden neu gestartet wird.

# Es wird jedesmal, wenn ein Terminal (neu) gestartet wird, seine PID in eine
# Pool-Datei geschrieben. Wird es beendet, dann wird die PID wieder dort
# herausgelöscht. So kann man in klebeterm-showdesktop.sh sicher identifizieren,
# was wieder de-minimiert werden soll.

PIDPOOL="/tmp/klebepids"

function msg()
{
    echo $*
}

function err()
{
    echo $* >&2
}

function addPID()
{
    if [ -z $1 ]; then
        err "PID für Pool (add) als Parameter übergeben."
        return 1
    fi

    echo $1 >> "$PIDPOOL"
}

function removePID()
{
    if [ -z $1 ]; then
        err "PID für Pool (remove) als Parameter übergeben."
        return 1
    fi

    if [ ! -f "$PIDPOOL" ]; then
        err "PIDPOOL existiert nicht: $PIDPOOL"
        return 1
    fi

    while read LINE
    do
        if [[ "$LINE" != $1 ]]; then
            echo "$LINE" >> "$PIDPOOL.2"
        fi
    done < "$PIDPOOL"

    mv "$PIDPOOL.2" "$PIDPOOL" || return 1
}

function waitForWindow()
{
    if [ -z $1 ]; then
        err "Window ID als Parameter übergeben."
        return 1
    fi

    # Jetzt schonmal 'ne Sekunde warten - ist sicher noch nicht da, wenn man direkt nachschaut
    sleep 1

    # Maximal 30 Mal = 30 Sekunden lang warten, bis ein Fenster auf dieser PID registriert ist
    typeset -i tries=0
    while [ $tries -lt 30 ]; do
        # wmctrl auflisten lassen, zurechtstutzen und dann schauen, ob die PID
        # nach einem Blank und am Ende der Zeile auftaucht
        WINDOW=$(wmctrl -l -p | cut -d " " -f1,4 | grep "\ $TERMPID\$" | cut -d " " -f1)
        if [[ "$WINDOW" != "" ]]; then
            # Jetzt ist es da! Merke: $WINDOW ist global gesetzt
            return 0
        fi
        tries=tries+1
        err "Fenster für PID $1 nicht gefunden, warte das $tries. Mal ..."
        sleep 1
    done

    err "Fenster erschien zu lange nicht. Gebe auf."
    return 1
}

function settingsForWindow()
{
    if [ -z $1 ]; then
        err "Window ID als Parameter übergeben."
        return 1
    fi

    # Hier Eigenschaften für das Zielfenster setzen
    wmctrl -i -r $1 -b add,below || return 1
}

if [ -z $1 ]; then
    err "Usage: $0 [-v] <geometry>"
    exit 1
fi

if [[ "$1" == "-v" ]]; then
    verbose=yes
    shift
fi

while true; do
    # Terminal als Job starten
    urxvt -ssc -ip -sh 80 -bl +sb -fg grey -bg black -geometry $1 -fn "x:-*-terminus-medium-*-*-*-20-*-*-*-*-*-*-*" &

    # Die PID holen und dann warten, bis das Fenster da ist und dann die Window ID holen
    TERMPID=$!

    addPID $TERMPID || exit 1

    WINDOW=""
    waitForWindow $TERMPID || exit 1

    # Jetzt Eigenschaften für dieses Fenster setzen
    settingsForWindow $WINDOW || exit 1

    if [ "$verbose" ]; then
        msg "PID: $TERMPID, Window ID: $WINDOW"
    fi

    # Warten, bis/ob sich das Terminal beendet. Wenn ja, nochmal von vorne.
    wait $TERMPID

    if [ "$verbose" ]; then
        msg "Terminal gestorben, starte neu ..."
    fi

    removePID $TERMPID || exit 1
done

klebeterm-showdesktop.sh

#!/bin/bash

# Wird aufgerufen, wenn alle Fenster minimiert werden sollen. Stellt dann *alle*
# klebenden Terminals wieder her.

# PIDPOOL?
PIDPOOL="/tmp/klebepids"

# "Show Desktop" modus anschalten
wmctrl -k on

# Für jede registrierte KlebePID die Window ID holen und dann wiederherstellen
for i in $(cat /tmp/klebepids); do
    WINDOW=$(wmctrl -l -p | awk -v pid=$i '{if ($3 == pid) {print $1}}')
    wmctrl -i -a $WINDOW
done

klebeterm-kill.sh

#!/bin/bash

# Beendet alle klebenden Terminals wieder

PIDPOOL="/tmp/klebepids"
TARGETEXE="urxvt"

function err()
{
    echo $* >&2
}

killall klebeterm-keep.sh

if [ -f "$PIDPOOL" ]; then
    for i in $(cat "$PIDPOOL"); do
        EXENAME=$(ps --no-headers -o comm -p $i)
        if [[ "$EXENAME" == "$TARGETEXE" ]]; then
            kill $i
        else
            err "Gegegbene PID \"$i\" gehört zu \"$EXENAME\" statt \"$TARGETEXE\""
        fi
    done

    rm "$PIDPOOL"
else
    killall urxvt
fi

Natürlich muss für andere Kisten hier und da noch was angepasst werden.

Comments?