Ogni volta che accendo il computer avvio gli stessi programmi e sposto le finestre sempre nelle stesse posizioni e negli stessi desktop. E' arrivato il momento di far eseguire queste operazioni direttamente al computer, così ho scritto uno script in bash che serve proprio a questo scopo. Lo script utilizza il programma "wmctrl" per controllare le finestre; lo troverete quasi sicuramente nel gestore di pacchetti della vostra distribuzione.
Per iniziare leggo le dimensioni del desktop perché serviranno per il posizionamento delle finestre relativo ai bordi del desktop:
DESKTOP_GEOMETRY=$( wmctrl -d | grep '^0' | cut -d ' ' -f 5 ) DESKTOP_WIDTH=$( echo "$DESKTOP_GEOMETRY" | cut -d 'x' -f 1 ) DESKTOP_HEIGHT=$( echo "$DESKTOP_GEOMETRY" | cut -d 'x' -f 2 )
Poi ho definito la funzione get_win_property per poter recuperare comodamente una proprietà di una finestra. Le proprietà sono definite come costanti all'inizio dello script:
# field position in wmctrl's output WIN_X=4 WIN_Y=5 WIN_W=6 WIN_H=7
e sono rispettivamente la posizione orizzontale e verticale, la larghezza e l'altezza di una finestra.
Questa funzione prende come parametri il titolo della finestra e una delle costanti che indica la proprietà il cui valore deve essere restituito.
function get_win_property { # window_name field_number local window=$1 local field=$2 echo "get_win_property: $window, $field" >&2 wmctrl -l -G -p | sed -e 's/ \+/ /g' | grep "$window" | cut -d ' ' -f "$field" }
Come si vedrà, tutte le funzioni che agiscono su una finestra prendono come parametro il titolo della finestra, o parte del titolo, l'importante è che sia sufficiente per individuare univocamente la finestra all'interno dell'output di wmctrl. La "ricerca" della riga riguardante la nostra finestra viene effettuata con grep, quindi come già detto anche parte del titolo può essere sufficiente.
La funzione da chiamare per spostare una finestra in una data posizione è move_win. I parametri da passare sono il solito titolo della finestra seguito dalla posizione orizzontale e da quella verticale. Le posizioni possono essere espresse in pixel o utilizzando delle stringhe che specificano una posizione relativa ai bordi del desktop: LEFT, RIGHT, TOP, BOT.
function move_win { # window_name x y local window=$1 local xpos=$2 local ypos=$3 echo "move_win: $window, $xpos, $ypos" >&2 if [ $xpos == 'LEFT' ]; then xpos=2 fi [...]
Nel caso di posizionamento relativo (qui sotto abbiamo l'esempio di RIGHT) lo script utilizza la funzione get_win_property per ottenere la larghezza della finestra; con questo dato calcola la posizione dell'angolo in alto a sinistra della finestra sottraendo dalla larghezza del desktop la larghezza della finestra stessa.
[...] if [ $xpos == 'RIGHT' ]; then width=$( get_win_property "$window" $WIN_W ) echo " width: $width" >&2 (( xpos = $DESKTOP_WIDTH - $width )) fi [...]
Nel caso di un posizionamento relativo al bordo superiore del desktop non è necessario alcun calcolo e l'angolo superiore della finestra viene collocato a 25 pixel dall'alto. Questo numero è del tutto arbitrario e nel caso del mio desktop (Gnome 3 su Linux Mint 12) corrisponde all'altezza della toolbar superiore.
[...] if [ $ypos == 'TOP' ]; then ypos=25 fi if [ $ypos == 'BOT' ]; then height=$( get_win_property "$window" $WIN_H ) echo " height: $height" >&2 (( ypos = $DESKTOP_HEIGHT - $height )) fi [...]
Con il seguente comando la finestra si sposta.
[...] wmctrl -r "$window" -e 0,$xpos,$ypos,-1,-1 sleep 1 }
La funzione resize_win ridimensiona una finestra utilizzando le dimensioni che vengono passate come secondo (larghezza) e terzo parametro (altezza). In questo caso non ci sono calcoli particolari da effettuare.
function resize_win { # window_name w h local window=$1 local width=$2 local height=$3 echo "resize_win: $window, $width, $height" >&2 wmctrl -r "$window" -e 0,-1,-1,$width,$height sleep 1 }
La seguente funzione sposta una finestra sul desktop specificato come secondo parametro: i desktop sono individuati da un numero a partire da zero.
function move_to_desktop { # window_name desktop_number local window=$1 local desktop=$2 echo "move_to_desktop: $window, $desktop" >&2 wmctrl -r "$window" -t $desktop sleep 1 }
Questa funzione chiude una finestra.
function close_win { # window_name local window=$1 wmctrl -c "$window" }
E, infine, la funzione wait_for_win. Questa funzione deve essere richiamata per attendere la comparsa di una finestra. In altre parole: quando avviate un programma non potete agire immediatamente sulla sua finestra utilizzando le funzioni che abbiamo visto sopra perché se la finestra non è disponibile tali funzioni falliscono e lo script procede oltre; l'effetto finale è che la finestra rimane nel posto in cui si è aperta.
Quindi si utilizza la seguente funzione:
function wait_for_win { # window_name local window=$1 local MAX_WAIT=30 # seconds echo "waiting for window: $window" >&2 [...]
Il cuore della funzione è il ciclo while che segue: tale ciclo rimane in attesa che nell'output di wmctrl compaia la riga relativa alla nostra finestra (cercata sempre utilizzando il titolo). Per non entrare in un loop infinito il numero di secondi di attesa non deve superare il valore di MAX_WAIT (definito qui sopra uguale a 30 secondi ma liberamente modificabile).
[...] winspec="" count=0 while [ -z "$winspec" -a $count -lt $MAX_WAIT ]; do sleep 1 winspec=$( wmctrl -l -G -p | grep "$window" ) (( count = count + 1 )) done [...]
Se dopo MAX_WAIT secondi la finestra non è ancora comparsa scrivo un messaggio di errore in standard error e procedo con lo script.
[...] if [ $count -eq $MAX_WAIT ]; then echo "window not found" >&2 else # I'll wait for 1 second more sleep 1 fi }
Per comodità riporto qui lo script completo. In fondo ci sono degli esempi d'uso.
#!/bin/bash # field position in wmctrl's output WIN_X=4 WIN_Y=5 WIN_W=6 WIN_H=7 # desktop geometry DESKTOP_GEOMETRY=$( wmctrl -d | grep '^0' | cut -d ' ' -f 5 ) DESKTOP_WIDTH=$( echo "$DESKTOP_GEOMETRY" | cut -d 'x' -f 1 ) DESKTOP_HEIGHT=$( echo "$DESKTOP_GEOMETRY" | cut -d 'x' -f 2 ) # {{{ functions function get_win_property { # window_name field_number local window=$1 local field=$2 echo "get_win_property: $window, $field" >&2 wmctrl -l -G -p | sed -e 's/ \+/ /g' | grep "$window" | cut -d ' ' -f "$field" } function move_win { # window_name x y local window=$1 local xpos=$2 local ypos=$3 echo "move_win: $window, $xpos, $ypos" >&2 if [ $xpos == 'LEFT' ]; then xpos=2 fi if [ $xpos == 'RIGHT' ]; then width=$( get_win_property "$window" $WIN_W ) echo " width: $width" >&2 (( xpos = $DESKTOP_WIDTH - $width )) fi if [ $ypos == 'TOP' ]; then ypos=25 fi if [ $ypos == 'BOT' ]; then height=$( get_win_property "$window" $WIN_H ) echo " height: $height" >&2 (( ypos = $DESKTOP_HEIGHT - $height )) fi wmctrl -r "$window" -e 0,$xpos,$ypos,-1,-1 sleep 1 } function resize_win { # window_name w h local window=$1 local width=$2 local height=$3 echo "resize_win: $window, $width, $height" >&2 wmctrl -r "$window" -e 0,-1,-1,$width,$height sleep 1 } function move_to_desktop { # [window name=""] [desktop number=""] local window=$1 local desktop=$2 echo "move_to_desktop: $window, $desktop" >&2 wmctrl -r "$window" -t $desktop sleep 1 } function close_win { # [window name=""] local window=$1 wmctrl -c "$window" } function wait_for_win { # [window name=""] local window=$1 local MAX_WAIT=30 # seconds echo "waiting for window: $window" >&2 winspec="" count=0 while [ -z "$winspec" -a $count -lt $MAX_WAIT ]; do sleep 1 winspec=$( wmctrl -l -G -p | grep "$window" ) (( count = count + 1 )) done if [ $count -eq $MAX_WAIT ]; then echo "window not found" >&2 else # I'll wait for 1 second more sleep 1 fi } # }}}
Nello stesso script, dopo la definizione delle funzioni, io ho la sezione che avvia i programmi e utilizza le suddette funzioni per manovrare le loro finestre.
# HERE GO THE PROGRAMS TO START empathy & wait_for_win 'Elenco contatti' move_win 'Elenco contatti' 'RIGHT' 'TOP' close_win 'Elenco contatti' thunderbird & wait_for_win 'Posta' move_win 'Posta' 47 57 terminator & wait_for_win 'andrea@aglinux' move_win 'andrea@aglinux' 119 66 resize_win 'andrea@aglinux' 1395 740 move_to_desktop 'andrea@aglinux' 1
Commenti
Posta un commento