Todo empezó un caluroso día del mes pasado. Cuando, estando trabajando, utilizando Firefox como navegador encuentro información interesante acerca de mi actual proyecto. Aunque no era el único programa en ejecución, el hecho es que tenía toda la RAM llena, y unas 20 ventanas abiertas. De repente, recibo una llamada de teléfono y tengo que ponerme inmediatamente con otra cosa. La página que tengo en mi navegador, es muy pesada, y el ordenador empieza a ir un poco lento, además, el ventilador de la CPU se pone en marcha, muy rápido y de manera muy ruidosa, haciendo que, hasta mi interlocutor se da cuenta de ello.
Mientras me cuentan mi nueva tarea, el ordenador no iba muy fluido y necesitaba consultar lo que me estaban relatando en la llamada telefónica.
De repente, vienen a mi cabeza las señales de los procesos y se me ocurre ejecutar un simple compando.
$ killall -SIGSTOP firefox
El uso de CPU baja a un nivel más o menos aceptable, dejándome abrir otro navegador para consultar la nueva información.
La historia anterior puede repetirse con muchas variaciones, pero el hecho es que, estamos acostumbrados a utilizar varias tarea en nuestro ordenador y, a veces, éstas son demasiado pesadas. Está bien, pero, si pasamos gran parte del día trabajando, puede que esa tarea pesada que estamos ejecutando, llegue un momento en el que no es prioritaria y tengamos que hacer otra cosa mientras, por lo que podemos pausarla y ya la reanudaremos luego cuando podamos destinar recursos a ello (puede que si la detenemos tengamos que empezar luego de nuevo, y eso tampoco está bien). Por cierto, para reanudar Firefox:
$ killall -SIGCONT firefox
La bombilla sobre la cabeza
Aunque, entonces se me ocurrió que podía tener una tecla rápida, tras la cual puedo seleccionar una ventana y pausar ese proceso automatizando todo el proceso, e incluso solucionando problemas; por ejemplo, si tengo varios perfiles de firefox y sólo quiero detener uno, con killall me los cargo todos, y con kill, tengo que saber la PID del firefox en concreto, y puede resultar complicado.
Para ello, voy a utilizar wmctrl, algunas herramientas del sistema, zenity para mostrar diálogos (uso KDE, pero zenity está más extendido) y notify-send para enviar notificaciones al entorno de escritorio en que nos encontramos acerca del estado de las órdenes que enviamos.
El script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | #!/bin/bash WMCTRL=`which wmctrl` ZENITY=`which zenity` # Leave this variable as is. zenity will use it to keep # associated to a Window and we don't want this WINDOWID= NOTIFY_SEND=`which notify-send` NOTIFY_SEND_ARGS="-t 2000 -i window-duplicate" # First implementation. No locales. Spanish for now function already_stopped() { WINPID=$1 NAME="$2" $ZENITY --question --text "El proceso $2 está pausado. ¿Desea reanudarlo?" --title "Proceso pausado" RES=$? if [ "$RES" == "0" ]; then kill -SIGCONT $WINPID RES=$? if [ "$RES" == "0" ]; then $NOTIFY_SEND $NOTIFY_SEND_ARGS "Se ha reanudado el proceso $NAME" fi fi } function already_running() { WINPID=$1 NAME="$2" PCPU=`ps ax -o pid,pcpu | grep $WINPID | awk '{print $2}'` $ZENITY --question --text "El proceso $2 está corriendo y consumiendo "$PCPU"% de CPU. ¿Desea pausarlo?" --title "Proceso en curso" RES=$? if [ "$RES" == "0" ]; then kill -SIGSTOP $WINPID RES=$? if [ "$RES" == "0" ]; then $NOTIFY_SEND $NOTIFY_SEND_ARGS "Se ha pausado el proceso $NAME" fi fi } function state_changer() { WINPID=$1 STATUS=`cat /proc/$WINPID/status` NAME=`echo "$STATUS" | grep "Name:" | awk '{print $2}'` STATE=`echo "$STATUS" | grep "State:" | awk '{print $2}'` if [ "$STATE" == "T" ]; then already_stopped $WINPID "$NAME" else already_running $WINPID "$NAME" fi } function get_window() { if [ "$1" == "Using" ]; then WINDOWLIST="`$WMCTRL -l -p | grep $3`" if [ -z "$WINDOWLIST" ]; then $ZENITY --error --text "No se encuentra la ventana seleccionada" exit fi WINPID=`echo "$WINDOWLIST" | awk '{print $3}'` state_changer $WINPID fi } $NOTIFY_SEND $NOTIFY_SEND_ARGS "Seleccione la ventana para ver el estado del proceso" $WMCTRL -a :SELECT: -v 2>&1 | while read line; do get_window $line; done |
Bueno, vamos a comentar esto un poco.
La joya de la corona, es wmctrl, con él, podemos pedirle información sobre una ventana, en este caso, una ventana a la que hacemos click, con :SELECT: una vez tenemos el ID de la ventana, podemos pedir una lista de ventanas incluyendo los PID de los procesos asociados (get_window()) y filtrar por el ID de la ventana en la que hemos hecho click.
Teniendo el PID, podemos averiguar el estado del proceso consultándolo en /proc/PID/status y podemos saber si lo habíamos pausado antes o no. Si no estaba pausado, lo pausamos (quise meter un diálogo por si hacemos click en una ventana equivocada. Si el proceso ya estaba pausado, nos da la opción de reanudarlo.
Advertencia
El funcionamiento teórico está bien, además funciona bien, puede detener cualquier proceso que tenga una ventana asociada, no estamos haciendo nada del otro mundo. Aunque hay procesos que no les sienta bien que los pausen, y puede que al reanudarlos tengan algún problema. Al ser procesos interactivos, muchas veces, todos los clicks que hemos hecho sobre ellos cuando estaban detenidos se producirán cuando lo reanudemos. En definitiva, que algunos procesos puede que se vuelvan inestables tras reanudarlos, pero no suele pasar.
En github
Este script forma parte de un proyecto que mantengo en github: gscripts, forks y stars son bienvenidos. Además, puede que cuando se publique el post, haya novedades en el repositorio.
The post Pausar aplicaciones haciendo click en la ventana correspondiente appeared first on Poesía Binaria.