lunes, 11 de diciembre de 2017

Elementary OS: Script para mostrar escritorio con atajo de teclado

Una de las cosas que me chirrían de Elementary OS es que no tenga la opción de utilizar un atajo de teclado para mostrar el escritorio. Es cierto que en el dock que incluye por defecto hay un acceso directo al escritorio, pero no he visto una forma sencilla de activarlo desde teclado. Me he acostumbrado a que la tecla <Súper>+D me muestra el escritorio, y si vuelvo a pulsarla, me devuelve las ventanas a su aspecto original.

Lo he resuelto con un script en bash que utiliza las siguientes herramientas de XWindows:
  • wmctrl
  • xwininfo
  • xdotool


Lo primero será por tanto instalar estas herramientas en tu sistema Elementary OS:
  • sudo apt install wmctrl
  • sudo apt install xwininfo
  • sudo apt install xdotool


¿Qué hace cada una de ellas?


wmctrl -d: relación de escritorios virtuales

wmctrl -d nos muestra una línea por cada uno de los escritorios virtuales que tenemos. En mi caso tengo 4 escritorios virtuales. Fíjate que marca con un asterisco (*) el que está activo. En la imagen anterior se ve que el escritorio 0 es el que está activo ahora


wmctrl -l: ventanas activas en mi sistema
wmctrl -l nos muestra una línea por cada ventana activa en el sistema.
Veamos campo por campo el sentido de cada línea:

  1. El primer campo es el ID de la ventana
  2. El segundo campo es en qué escritorio virtual se encuentra la ventana. Fíjate que -1 es un escritorio no existente.
  3. El tercer campo es el nombre del PC
  4. El cuarto campo es el nombre de la ventana.

xwininfo: ¿Está la ventana x minimizada?
Esta herramienta me permite obtener información de detalle de una determinada ventana. Por ejemplo, ¿está minimizada?
Básicamente funciona así: xwininfo -id <idventana>
Donde, idventana es el ID que nos devuelve wmctrl.

xdotool: realizar acciones sobre una ventana
xdotool me permite realizar acciones sobre una ventana concreta:
  • maximizar
  • minimizar
  • restaurar
  • asignarle el foco, es decir, pornerla en primer plano y marcarla como activa


Básicamente, lo que hace mi script es:

  1. Contar el número de ventanas visibles del escritorio actual
  2. Si n_ventanas_visibles > 0 entonces, minimizar todas
  3. Si no, restaurar todas las ventanas del escritorio activo

Con un pequeño matiz, cuando minimizo todas, guardo en un fichero temporal cuál es la ventana activa en ese momento. Esto me permitirá que, si vuelvo a restaurar las ventanas, se vuelva a marcar como activa aquella en la que yo estaba trabajando.

A este script yo lo he llamada mDesktop.sh. Para asignarle un atajo de teclado, sencillamente configuro los atajos de teclado personalizados en Elementary OS:


A continuación te muestro el código completo del script:



#!/bin/bash
mute=0

Program=$@
program_workingDir=`dirname $(readlink -f $0)`
status_fname="$program_workingDir/.mdesktop_status"
program_systemWindows="$program_workingDir/.mdesktop_systemWindows"

n_visible=0
window_id=0
window_name=""
window_hidden=0
window_isignorable=0
window_vd=0     #vd means virtual desktop
vd_activeId=0

#-----------------------------------------------------------------------------------------------
function debug() {
 if [[ $mute == 0 ]]
 then
  echo "DEBUG: $1"
 fi
}
#-----------------------------------------------------------------------------------------------
function vd_getActiveId() {
 local vd_id=-1
 local vd_status="-"

 while read vDesktop
 do
  vd_id=`echo "$vDesktop" | tr -s ' ' | cut -f 1 -d " "`
  vd_status=`echo "$vDesktop" | tr -s ' ' | cut -f 2 -d " "`

  if [ "$vd_status" == "*" ]
  then
   echo $vd_id
   break
  fi
 done < <(wmctrl -d)
}
#-----------------------------------------------------------------------------------------------
#isSystemWindow:
# All windows in mdestop_systemWindows and also the Desktop
# How to detect the Desktop window? 
# Because if you ask xwininfo -id <window_id> -all and you grep Window type: Desktop
# System Windows are marked to vDesktop -1

function isSystemWindow() {
 local window_isSystem=0
 local window_name=$1
 local window_id=$2
 local window_isDesktop=0
 
 window_isSystem=$(grep "$window_name" $program_systemWindows | wc -l)
 
 if [[ $window_isSystem == 1 ]]
 then
  echo $window_isSystem
  return
 fi

 window_isDesktop=$(xwininfo -id $window_id -all | grep "Desktop" | wc -l)
 if [[ $window_isDesktop == 1 ]]
 then
  echo "1"
 else
  echo "0"
 fi

}
#-----------------------------------------------------------------------------------------------
# window_activeId
#  Window id which actually is focused, it is, the one which is active
#  This value will be saved in .desktop_status so it can be restored
#-----------------------------------------------------------------------------------------------
function windows_minimize_all() {
 local window_activeId=-1
 debug "windows_minimize_all..."

 window_activeId=$(xdotool getactivewindow)
 echo "$window_activeId" > $status_fname
 while read Window
 do
  # window_name=`echo "$Window" | tr -s ' ' | cut -f 4 -d " "`
  window_id=`echo "$Window" | tr -s ' ' | cut -f 1 -d " "`
  window_vd=`echo "$Window" | tr -s ' ' | cut -f 2 -d " "`

  if [[ $(isSystemWindow $window_name $window_id) == 1 ]]
  then
   debug "NOT minimize $window_id: system window"
   continue
  fi
  
  if [[ $window_vd != $vd_activeId ]] 
  then
   debug "NOT minimize $window_id: in another desktop"
   continue
  fi
  

  debug "MINIMIZE $window_id"
  xdotool windowminimize $window_id
 done < <(wmctrl -l)
}
#-----------------------------------------------------------------------------------------------

function windows_restore_all() {
 while read Window
 do
  # window_name=`echo "$Window" | tr -s ' ' | cut -f 4 -d " "`
  window_id=`echo "$Window" | tr -s ' ' | cut -f 1 -d " "`
  window_vd=`echo "$Window" | tr -s ' ' | cut -f 2 -d " "`

  if [[ $(isSystemWindow $window_name $window_id) == 1 ]]
  then
   debug "NOT RESTORE $window_id: system window"
   continue
  fi
  
  if [[ $window_vd != $vd_activeId ]] 
  then
   debug "NOT RESTORE $window_id: in another desktop"
   continue
  fi

 xdotool windowactivate $window_id
 done < <(wmctrl -l)
 
 # Set focus to the window was active before minimizing all
 window_activeId=$(cat $status_fname)
 debug "SETTING FOCUS TO: $window_activeId"

 xdotool windowactivate $window_activeId
 
}
#-----------------------------------------------------------------------------------------------
debug "###############################################"
debug "###############################################"
debug "# MAIN  ##########################################"
debug "###############################################"
debug "###############################################"

vd_activeId=$(vd_getActiveId)
debug "current virtual desktop=$vd_activeId"

while read Window
do
 debug "-----------------------------------------------------------------------"
 debug "### 1"
 debug "$Window"
 
 window_id=`echo "$Window" | tr -s ' ' | cut -f 1 -d " "`
 window_vd=`echo "$Window" | tr -s ' ' | cut -f 2 -d " "`
 window_name=`echo "$Window" | tr -s ' ' | cut -f 4 -d " "`
 window_hidden=`xwininfo -all -id $window_id | grep "Hidden" | wc -l`
 debug "Window Id: $window_id"
 debug "Window vd: $window_vd"
 debug "Window Hidden: $window_hidden"
 debug "Window Name: $window_name"

 if [[ $window_vd == "-1" ]]
 then 
  debug "Window virtual desktip is -1, ignore actual window"
  continue
 fi

 window_isignorable=$(isSystemWindow $window_name $window_id)
 debug "Window is system: $window_isignorable"

 # is current window an ignore Window?
 if [[ $window_isignorable == 1 ]] 
 then
  debug "Ignore actual Window"
  continue
 fi

 # is current active virtual desktop?
 if [[ $window_vd != $vd_activeId ]] 
 then
  debug "Ignore actual Window: in another desktop"
  continue
 fi

 # Is there any visible window?
 if [[ $window_hidden == 0 ]]
 then
  debug "show_desktop"
  let n_visible++
 fi

 debug "n_visible = $n_visible"
 debug "### 4"
done < <(wmctrl -l)


# We must exec every window and minimize or maximize, depending wether it was already minimized or not
 
debug "###n_visible = $n_visible"
# windows_restore_all
if [[ $n_visible == 0 ]]
then
 debug "restore all windows"
 windows_restore_all
else
 debug "minimize all windows"
 windows_minimize_all
fi



Fuentes: