Nueva versión de ActivityAppLauncher

Acabo de subir una nueva versión de ActivityAppLauncher, la extensión de Gnome Shell que muestra un menú de categorías en el modo Overview. El motivo de actualizarla, después de años sin tener que tocarla prácticamente, es que en Gnome Shell 3.36.0 han hecho varios cambios internos que rompen las cosas que tuve que hacer para que funcionase.

Sin embargo, aproveché para echar un vistazo más en detalle al viejo código (a fin de cuentas, fue una de las primeras extensiones que escribí), y encontré una manera más elegante y, sobre todo, fiable, de añadir la vista con las aplicaciones: ahora es una página «con todas las de la ley» dentro del modo Overview, y no un pegote metido con calzador. Esto tiene la ventaja de que hay una animación al pasar de la lista de ventanas al de aplicaciones y viceversa, pero a cambio no me ha sido posible conservar la lista de escritorios cuando se muestra la lista de aplicaciones.

También aproveché para cambiar el sistema de instalación por uno basado en meson, vilmente fusilado del utilizado en desktop-icons.

En breve estará disponible en la página de extensiones de Gnome Shell, pero mientras tanto, se puede bajar del repositorio GIT.

Compilando Gnome Shell

Estos últimos meses he estado preparando un parche para Mutter, y he necesitado instalar la rama de desarrollo de Gnome Shell. Y aunque hay bastante documentación, hay detalles que están algo incompletos, así que voy a poner aquí lo que he ido aprendiendo.

Para empezar, toda la compilación se hace mediante jhbuild, lo que simplifica muchísimo el trabajo. Y aunque es mucho más sencillo que bajarse a mano todo, aún tiene algunos detalles peliagudos.

Lo primero, si queréis tener éxito en compilar una versión de desarrollo, es mejor que utilicéis Fedora, en concreto la versión de desarrollo (o beta) también. De esta manera no tendréis problemas de versiones con las dependencias. Para ello, nada mejor que utilizar una máquina virtual. Aunque VirtualBox es una buena opción, yo tuve serios problemas con los controladores de pantalla (y es raro… con Debian o Ubuntu siempre fueron perfectamente), por lo que al final me decanté por Gnome Boxes, una herramienta más sencilla, pero con la que no tuve ningún problema.

Una vez instalada, bajamos jhbuild y lo instalamos con:

git clone https://gitlab.gnome.org/GNOME/jhbuild.git
cd jhbuild
./autogen.sh
make
make install

Una vez hecho esto debemos añadir (si no lo tenemos ya) a nuestro PATH el directorio ~/.local/bin. Y ya tendremos jhbuild instalado.

El siguiente paso es pedir que nos construya Gnome Shell. Para ello escribimos:

jhbuild build gnome-shell

Sin embargo, lo más probable es que no funcione y nos diga que nos faltan un montón de dependencias. Para instalarlas, basta con ejecutar:

jhbuild sysdeps – -install gnome-shell

y tomarse un café (o dos), porque tarda un buen rato en bajar e instalar las dependencias. Cuando termine volvemos a ejecutar el comando previo, y probablemente nos dirá que nos falta aún libavcodec y compañía. Por suerte es posible compilarlo aún así, mediante:

jhbuild build – -nodeps gnome-shell

Ahí empezará a compilar por fin, y tardará, tardará mucho (y requerirá mucho disco duro, por cierto. Aseguraos de que vuestra máquina virtual tiene suficiente). Pero lo importante que debemos saber es que el código lo baja en ~/jhbuild/checkout, por lo que si queremos aplicar parches, es ahí donde hay que tocar el código.

Cuando termina de compilar, instala todo en ~/jhbuild/install, y ya podemos probarlo. Yo lo que suelo hacer es cerrar la sesión gráfica, irme a una consola de texto (con Alt+F2), y ahí ejecutar primero:

jhbuild shell

para tener una shell dentro del entorno, y luego ejecutar:

dbus-run-session – – ~/jhbuild/install/bin/gnome-shell – -wayland – -display-server

y se arranca la sesión. Un problema que yo no he conseguido resolver es que la opción de Cerrar sesión no funciona. Yo lo resuelvo abriendo una terminal y matando el proceso.

Parche para Gnome Boxes

Aunque hasta ahora utilizaba VirtualBox para hacer virtualización, hace unas semanas me dio por probar Gnome Boxes en su lugar, y la verdad es que me ha convenció mucho. Es cierto que no tiene muchas características avanzadas de VirtualBox, como son el portapapeles bidireccional o el acceso directo a USBs, pero la verdad es que, en general, no los necesito. Y, sin embargo, sí tiene algo que me era especialmente útil, que es que la emulación de la tarjeta gráfica funciona de manera nativa en Fedora y en Debian, sin necesidad de instalar nada. El motivo de que esto me resulte especialmente útil es que tengo algunos parches pendientes para mutter, el gestor de ventanas de Gnome, y la única manera de probarlos en condiciones (sobre la versión de desarrollo) es trabajar con Fedora. Por desgracia, no se qué pasa que los drivers de VirtualBox para la pantalla no me funcionan en Fedora. No es que funcionen mal, es que no he conseguido que me funcionen. En alguna ocasión parece que van, pero de pronto dejan de funcionar sin motivo aparente. Y depurar en un sistema en 640×480 es imposible. Por eso estoy utilizando Gnome Boxes para todo esto, porque me funciona perfectamente.

Por desgracia, hace unos días me encontré con que era imposible crear una nueva máquina virtual en Boxes: cada vez que escogía la opción en le menú, el programa se cerraba con un core dump.

Tras indagar un poco descubrí que el problema era que el programa se conectaba a una URL externa para bajar una lista de recomendaciones y mostrar las tres más interesantes; sin embargo, por algún motivo, todas menos dos fallaban a la hora de descargarlas. Y aquí llega el problema: el código no comprobaba cuantas recomendaciones había e intentaba mostrar siempre tres, pero al haber sólo dos, cascaba. Como solucionarlo era trivial, decidí escribir yo mismo el parche y enviárselo, y lo aceptaron de inmediato, por lo que ya vuelve a funcionar perfectamente.

Y aún me preguntan por qué me gusta tanto el software libre…

Ubuntu arranca lento en VirtualBox

De un tiempo a esta parte, cada vez que instalaba una Ubuntu en una máquina de VirtualBox, me encontraba con que a la mitad del arranque se quedaba parada. En una de éstas me dio por probar a esperar, y muuucho tiempo después, terminó de arrancar.

Al principio parecía que tenía que ver con VirtualBox en sí, porque ocurría después de instalar los drivers para máquinas virtuales. Sin embargo, era consciente de que no tenía sentido, porque si no habría reportes por toda internet. Por eso empecé a revisar con calma la configuración, y me di cuenta de que en el procesador estaba desactivado el soporte de PAE. Esto me mosqueó, pues había leído en varios sitios que en Ubuntu al menos ahora era obligatorio, pues el núcleo por defecto viene con dicha opción activa.

Así pues, fui a la configuración, en Sistema -> Procesador, y la activé, y, efectivamente, el problema estuvo resuelto.

Creando un pincho USB de instalación de Windows

A veces no nos queda otra que usar Windows. Hay muchas circunstancias, pero podemos resumirlas en:

  • tenemos un programa que sólo funciona en Windows, y Wine no es una opción.
  • Alguien nos pide que le (re)instalemos el Windows en su equipo.

Aunque los fabricantes suelen incluir la opción de restaurar el sistema a los valores de fábrica, en mi opinión es mejor instalar la versión oficial, pues no trae mucha de la basura que meten los fabricantes. Para ello podemos bajar la ISO oficial desde la página de Microsoft. No hay problema por que nuestro ordenador no tenga una pegatina con un número de activación, pues éste se guarda en la BIOS. Es más: si no tenemos licencia, Windows 10 funciona exactamente igual, y la única limitación es que muestra un pequeño mensaje en la esquina inferior derecha pidiendo que te registres, y que no permite personalizarlo (cambiar colores, el fondo de escritorio…).

El problema viene cuando nos encontramos con que muchos equipos actuales no traen unidad de DVD, con lo que, o no podemos grabar la imagen ISO. La solución en ese caso consiste en crear un pincho USB para arrancarlo. Y aunque hay muchas utilidades para ello, en realidad es mucho más sencillo de lo que parece: basta con coger un pincho formateado en FAT32, copiar dentro todos los ficheros que hay dentro de la imagen ISO, y listo: ya tenemos un pincho arrancable en cualquier sistema UEFI… al menos hasta que descubrimos que la última versión de la ISO de Windows tiene un fichero que ocupa más de 4GB, y por tanto no se puede copiar en una partición FAT32.

Aunque algunas BIOS pueden arrancar también desde particiones NTFS, no todas lo permiten. Pero, afortunadamente, hay una solución, apuntada por win10guru:

  • Creamos dos particiones en el pincho USB: la primera, de unos 5GB, en formato FAT32, y la segunda en formato NTFS.
  • Copiamos en la primera todas las carpetas de la ISO excepto la de Sources.
  • Creamos una carpeta Sources vacía en la partición FAT32 y copiamos dentro únicamente el fichero boot.wim, que está dentro de la carpeta Sources de la imagen ISO.
  • Por último, copiamos todos los ficheros y carpetas de la ISO en la partición NTFS.

De esta manera, la BIOS arrancará desde la partición FAT32, y cederá el control a la partición NTFS.

Una vez instalado, conviene eliminar la basura que mete Microsoft. Hay varias opciones: la primera es eliminar Windows Search y Superfetch. La segunda, utilizar uno de los muchos scripts que eliminan programas que no se utilizan, tales como:

Por supuesto, yo no soy responsable de lo que hagan estos scripts, así que cada uno debe revisarlos antes para ver si lo que hacen es lo que se espera.

Un pasito p’alante María, y uno p’atrás

Hace cosa de un mes se publicó la versión 3.34 de Gnome Shell, la cual incorporaba muchas mejoras internas de rendimiento. Por desgracia, también incorporaba tantos cambios internos que un amplio porcentaje de extensiones no funcionan con ella. Esto era algo de lo que ya nos habíamos olvidado, pues desde hace unas cuantas versiones no había cambios de arquitectura internos tan grandes que afectasen de esta manera a las extensiones. Aunque tuve suerte con ActivityAppLauncher y con Desktop Icons NG (pues ambas funcionan perfectamente sin ningún cambio), no la hubo con la extensión Desktop Icons original, principalmente por culpa del nuevo sistema de herencia en clases Javascript, que, al permitir heredar desde clases de GObject, ya no obliga a meter un actor St en una propiedad de una clase, sino que la clase en sí puede heredar directamente de ella.

Afortunadamente, Marco Trevisan se encargó de subir un parche que corregía este problema; sin embargo, para poder garantizar que funcionaba correctamente tuve que esperar a que Debian Sid actualizase Gnome Shell desde 3.30 a 3.34, cosa que ocurrió hace casi dos semanas. Tras unas cuantas pruebas del código procedí a empaquetarlo y subirlo. Alegría, alboroto, labor cumplida, bla bla bla…

Hasta que empiezan a llegar los avisos de bugs. En concreto, había uno muy grave, que rompía completamente el escritorio cada vez que se intentaba renombrar un icono. Ante semejante problema, tuve que poner en cuarentena rápidamente la nueva versión de la extensión y empezar a trabajar en el parche. Desgraciadamente, no conseguía avanzar: todo lo que intentaba seguía fallando de manera misteriosa. Afortunadamente, una vez más Marco Trevisan se puso manos a la obra y subió otro parche que corregía este problema y, de paso, actualizaba la forma del cuadro de diálogo para renombrar ficheros, haciéndola más consistente con Nautilus (nunca le podré estar lo suficientemente agradecido).

Ya puestos, aproveché para pedirle que revisase un parche mío que llevaba tiempo esperando ser aplicado: uno que corregía el tamaño de los thumbnails. Finalmente se pudo aplicar a tiempo, por lo que es posible que los usuarios de Ubuntu 19.10 puedan disfrutarla «de serie».

Pero los problemas no se acabaron ahí, pues surgieron dos bugs extra que afectaban, esta vez sí, a Desktop Icons NG. El primero es que, por un error en Mutter, el gestor de ventanas de Gnome Shell, el drag and drop desde el escritorio hacia una ventana de Nautilus deja de funcionar, cuando en Gnome Shell 3.30 y 3.32 siempre funcionó perfectamente. Tras mucho esfuerzo conseguí encontrar el parche concreto en el que se introdujo el error, y lo reporté. EDITO: ya lo han corregido. ¡Genial!

El segundo problema era más grave, pues por un bug en Gnome Shell en sí, crear una ventana de tipo DESKTOP con Gnome Shell trabajando en X.org (no se en Wayland, pues actualmente no se puede), rompía el modo de Actividades: era entrar en él y el escritorio se quedaba tonto. Afortunadamente lo resolvieron a tiempo para la versión 3.34.1, que salió hace cuatro días, por lo que ya no es un problema

Por lo tanto, si estás utilizando Gnome Desktop y Gnome Shell 3.34.0, te recomiendo que actualices de inmediato a Gnome Shell 3.34.1, para asegurarse de que mis extensiones trabajen lo mejor posible.

Nueva versión de Cronopete y Terminus

Acabo de lanzar una nueva versión de Términus y de Cronopete.

En el caso de Términus era necesario por un problemilla al usar el modo «Guake» desde Wayland: dado que marca la ventana para que esté encima de todas las demás, los menús contextuales del botón derecho no se pueden ver. Esto es porque un menú va en una ventana independiente, y dado que la ventana principal está situada encima de absolutamente todas, también lo estará encima de la ventana del menú.

La solución consistió en utilizar un Gtk.Popover. Este widget es similar a un menú, pero en lugar de utilizar una ventana independiente se pinta directamente en la misma ventana que el widget padre. El inconveniente es que no se puede hacer aparecer en el punto donde esté el cursor del ratón (o, al menos, yo no he sido capaz de ello).

Por otro lado se supone que es posible construir un menú «de verdad» con un Gtk.Popover, pero yo no he sido capaz, así que no me quedó otra que construirlo con Gtk.Button directamente. El resultado, pese a todo, funciona bien.

A mayores hice algunos cambios pequeños en el código para eliminar algunos warnings por utilizar funciones obsoletas.

Respecto a Cronopete, aproveché para lanzar una nueva versión con un cambio que, aunque pequeño, hace más cómodo su uso: ahora, si se produce un aviso de que un fichero se ha «desvanecido», no aparecerá un popup de aviso. Este mensaje se produce porque Rsync hace primero una lista con los ficheros que va a copiar, y una vez que la tiene es cuando procede a copiarlos de uno en uno, por lo que si, después de haber hecho la lista, uno de los ficheros desaparece (se «desvanece»), emite un aviso. Este aviso no es crítico, por lo que no tiene sentido molestar al usuario.

Otro cambio que incluye es haber eliminado el fork del proceso principal: antes, por seguridad (y por una serie de problemas en las versiones anteriores) había dos procesos de cronopete: el principal, que es el que maneja todo, y un proceso watchdog que comprueba si el principal ha fallado y lo lanza de nuevo. Sin embargo, desde que cambié el sistema de copia para que utilice Rsync, esta precaución no es necesaria, y de hecho da algún que otro problemilla al apagar los equipos, cuando uno de los procesos se queda lanzado y no quiere morirse.

Como de costumbre, se pueden bajar ambos programas en distintos formatos desde mi página web.

Terminus 1.6.0 funciona igual en Wayland y en X11

Los que utilicéis mi terminal Terminus en Wayland habréis notado que no responde exactamente igual que en X11. Esto es debido a las limitaciones que impone este servidor gráfico, que impide que un programa mantenga su ventana siempre encima de las demás, o que la fije en todos los escritorios.

Sin embargo, después de los excelentes resultados que conseguí con Desktop Icons NG, y que expliqué en una entrada anterior, se me ocurrió que podía hacer algo similar con Terminus (pues, a fin de cuentas, ya usaba una extensión en Gnome Shell para permitir que funcionase en Wayland).

Y dicho y hecho: he reutilizado la clase que diseñé para lanzar un proceso desde una extensión y detectar si una ventana pertenece a él, y ahora Terminus ya responde exactamente igual en X11 y en Wayland: la ventana siempre se mantiene arriba de todo, y aparece en todos los escritorios. A mayores, ahora se garantiza que siempre aparecerá en el monitor principal.

Como de costumbre se puede bajar desde la sección de Terminus de mi web.

Nueva versión de Cronopete y Devede

Con bastante retraso publico esta entrada. Hace dos meses lancé la versión 4.11.0 de Cronopete, que añade una funcionalidad fun-da-men-tal: avisar si no hay espacio para hacer al menos una copia. Esto puede sonar raro, pero en realidad se trata de un bug bastante sutil: resulta que si no hay espacio suficiente para hacer ni siquiera la primera copia, sencillamente… ¡nunca avisaba! Se quedaba intentándolo una y otra vez. Esto lo descubrí de casualidad gracias a un usuario que intentaba utilizar (sin éxito, claro) un pincho USB de poca capacidad para las copias.

En cuanto a Devede, por fin incorpora algo que mucha gente pidió: cambiar la etiqueta del disco, para que no sea DVDVIDEO.

Como de costumbre, se pueden descargar en mi web.

Desktop icons, Gnome shell y Wayland

Hace cosa de un año y medio la gente de Gnome anunció que eliminaba de Nautilus todo el código que pintaba los iconos de escritorio. Para los que no se quieran leer el tocho, resumiré rápidamente que el motivo era, básicamente, que dicho código venía de muchos años atrás, era extremadamente complejo debido a las limitaciones de las versiones antiguas de GTK, y cada vez era más difícil de mantener a la vez que se añadían nuevas características al resto de Nautilus. Además, hacía ya seis años que Gnome 3 no utilizaba iconos de escritorio. Por si fuera poco, tenía muchos bugs de difícil solución (por ejemplo, el soporte multimonitor no funcionaba demasiado bien). Y a todo esto había que sumar el hecho de que en Wayland no funcionaría correctamente debido a las limitaciones que impone en aras de la seguridad: en efecto, en Wayland una aplicación no puede decidir donde colocar una ventana ni mantenerla fija en el fondo, entre otras cosas. El motivo de esta limitación es evitar que una aplicación maliciosa pueda hacerse pasar, por ejemplo, por el escritorio, o por una barra de tareas, etc, poniendo en riesgo la seguridad del sistema. Por desgracia, esto también significa que, en Wayland, cosas como las barras del escritorio, un dock o los iconos de escritorio no se pueden delegar en una aplicación, sino que tienen que ser manejadas desde dentro del gestor de ventanas. Es por esto que se creó la extensión de Gnome Shell llamada Desktop Icons: para seguir ofreciendo iconos en el escritorio en Gnome Shell para aquellos que lo deseasen (como yo).

La versión disponible en aquel momento ya implementaba la funcionalidad más básica, pero tenía un defecto que, para mí, era muy grave: no permitía utilizar una única pulsación para lanzar un icono, sino que obligaba a utilizar doble click. Como yo estoy acostumbrado a la primera manera, me lié la manta a la cabeza y envié un parche para implementarlo. Tras muchos cambios para adecuar el estilo de código al que se utiliza en el proyecto Gnome y más cosas (nunca podré agradecer lo suficiente a Carlos Soriano su infinita paciencia enseñándome a manejar Git en condiciones), lo aprobaron. Y le cogí el gustillo, con lo que detrás de él vino otro, y otro, y otro más… Hasta que, recientemente, me ofrecieron ser el mantenedor del código (lo que para mí es un honor, todo sea dicho).

Un año después, la extensión ya incorpora todo lo que se pretendía para la versión 1.0 y más, y se incluye por defecto en la versión de Gnome Shell de Ubuntu, y no puedo menos que agradecer a toda la gente que ha colaborado con parches e informes de bugs.

Por desgracia, a medida que más y más usuarios la han ido instalando, han ido surgiendo algunos problemas inherentes al hecho de que sea una extensión, problemas que no eran nada evidentes al principio y que sólo se han ido haciendo visibles a medida que la cantidad de usuarios crecía.

El primer gran problema es que todas las extensiones se ejecutan en el mismo bucle principal que el compositor de ventanas. Esto significa que una extensión que necesite mucho tiempo para ejecutar una operación puede, literalmente, congelar todo el escritorio, incluyendo el mismísimo repintado de todas las ventanas. En el caso de extensiones «normales», que se limiten a mostrar un icono en la barra de tareas o así, esto no es un problema, porque el trabajo que realizan es mínimo; sin embargo, en el caso de Desktop Icons sí lo es, pues cada vez que tiene que refrescar el escritorio (por ejemplo porque se añade o borra un fichero), tarda en torno a medio segundo en realizar todas las operaciones (leer la lista de ficheros en el directorio, obtener sus metadatos, generar los pixmaps, eliminar las cuadrículas e iconos previos, generar una nueva cuadrícula, crear los nuevos iconos, y pintarlos en su sitio), y durante ese tiempo todo el escritorio se congela. Obviamente, por que ocurra una vez cada mucho no es muy problemático, pero sin duda es molesto para el usuario.

Solucionar esto, aunque no es totalmente imposible, no resulta sencillo: actualmente ya utilizamos funciones asíncronas en todos los sitios posibles para evitar bloquear la cola de eventos, pero no es suficiente. Además sería necesario evitar repintar todo el escritorio y sus elementos, y sólo añadir o quitar los iconos de los ficheros que se hayan añadido o eliminado. Pese a todo, esto sólo reduciría un poco más el problema, pero no lo resolvería del todo, pues si un programa añade y borra constantemente un fichero al escritorio, por ejemplo, puede aún bloquear la cola, en buena medida porque algunas operaciones de repintado se realizan sólo cuando ya no quedan operaciones pendientes en la cola de eventos. Además, implementar esto implicaría un importante rediseño interno, y teniendo en cuenta que algunas distribuciones cuentan con Desktop Icons para sus versiones de soporte a largo plazo, no es algo que se pueda hacer de cualquier manera, sino que debe implementarse de manera muy progresiva y con una buena revisión por pares de todos y cada uno de los parches, para garantizar que no hay errores ni regresiones.

Otro problema es la imposibilidad (al menos actualmente) de integrar Drag’n’Drop al completo: aunque dentro de Gnome Shell existe soporte de Drag’n’Drop, no es compatible con operaciones desde el «espacio de usuario»; esto es: una aplicación no puede ni enviar al compositor, ni recibir de él, eventos de Drag’n’Drop . Aunque en principio sería posible implementar dicha comunicación, no es una tarea trivial, y de hecho, tras tantear a algún programador de Mutter, la conclusión es que es lo suficientemente complicado como para que sólo valga la pena pasar el trabajo de implementarlo en Wayland, pero no en X11.

Y de aquí llegamos al tercer problema: las extensiones se escriben con Clutter + St, los cuales no funcionan exactamente igual que Gtk. Un ejemplo es la forma en que se procesan los eventos de enter_notify y exit_notify, que obligó a añadir una serie de trucos en el código que permite seleccionar un grupo de iconos mediante «goma elástica», para gestionar correctamente aquellos casos en los que el cursor entraba en una ventana en mitad de una selección, o cuando pasaba por encima de la barra superior de Gnome. Esto es algo que en Gtk está resuelto desde hace años, gracias al mayor número de usuarios y programadores que trabajan con él, y que permite detectar más bugs.

El cuarto problema radica en los cambios entre versiones del escritorio. Dado que Gnome Shell no ofrece una API estable para las extensiones, éstas tienen que interactuar directamente con el código interno, por lo que cualquier cambio les puede afectar. En el caso de una extensión tan compleja como Desktop Icons este problema es aún mayor, hasta el punto de que la actual versión 19.01 será, probablemente, la última compatible con Gnome Shell 3.30 y 3.32, y las nuevas versiones necesitarán como mínimo Gnome Shell 3.34.

A la vista de todos estos problemas, hace un par de meses empecé a sopesar la posibilidad de hacer un cambio radical en el diseño y mover toda la lógica a un proceso independiente del compositor, de manera que toda la gestión de los iconos del escritorio se realice sin interferir con la composición y demás, además de utilizar Gtk directamente en lugar de St y Clutter. De hecho, cuando se decidió crear la extensión, los autores originales (Carlos Soriano y Ernestas Kulik) sopesaron seriamente esta misma posibilidad, pero lo descartaron precisamente por las limitaciones impuestas por el modelo de seguridad de Wayland, y porque hacerlo dentro de una extensión tenía más sentido en aquel momento, pues era muchísimo más fácil.

Por supuesto, esto es más sencillo de decir que de hacer, pues, como ya dije antes, aunque en X11 no hay ningún problema en que una aplicación mantenga una ventana en el fondo, o que la elimine de la lista de ventanas para que no aparezca al hacer Alt + Tab, en Wayland eso es totalmente imposible por diseño. Además, dado que las extensiones están escritas en Javascript, no es posible lanzar código en un nuevo thread, y aunque se pudiese, no sería posible que dicho código llamase a funciones de Clutter o St, por lo que hay que descartar la idea de descargar el trabajo en un thread paralelo. Lanzar un proceso independiente sí es posible, pero éste trabajaría desde fuera del compositor, por lo que, en principio, seguiríamos con el mismo problema.

Sin embargo, existe una alternativa extra, que es justo la que decidí investigar, que consiste en repartir el trabajo: por una parte tenemos un programa normal, que trabaja desde fuera del compositor y que utiliza GTK para gestionar absolutamente todo lo relacionado con el escritorio y sus iconos mediante una o varias ventanas normales, exactamente igual a como hacía el viejo Nautilus en el modo «iconos de escritorio»; y, por otro lado, una pequeña extensión cuyo trabajo consiste en lanzar el programa anterior (y relanzarlo cada vez que se muera), detectar la ventana que abre, y asegurarse de que ésta se mantenga donde debe. De esta manera, el código dentro de la extensión es mínimo y se limita exclusivamente a las operaciones que son imposibles de realizar desde el exterior del código del compositor.

Por supuesto, es fundamental no romper el modelo de seguridad de Wayland, lo que significa que no se puede permitir bajo ningún concepto que una aplicación extraña se pueda aprovechar de este mecanismo para colocar su propia ventana como el fondo del escritorio (o como cualquier otro elemento). Para ello, la única solución es que sea la extensión quien lance el proceso, y que cada vez que aparezca una ventana, compruebe si ésta pertenece al proceso que ella misma ha lanzado, otorgándole esos privilegios exclusivamente en el caso de que así sea. Dado que el código ha sido lanzado específicamente por la extensión, se puede considerar que es código tan confiable como el de dicha extensión (y más si el programa a lanzar forma parte de la propia extensión): si un programa malicioso puede reemplazar el código de la aplicación de escritorio, también podría hacerlo directamente con el código de la extensión, por lo que el nivel de seguridad es exactamente el mismo.

Ahora llega la cuestión de cómo detectar que una ventana pertenece al proceso lanzado desde la extensión. Mi primera idea fue utilizar metawindow_get_pid() en cada ventana nueva que apareciese, para obtener el PID del proceso que creó la ventana y compararlo con el PID del proceso que hemos lanzado desde la extensión; por desgracia, dicha llamada sólo funciona en X11 pero no en Wayland, porque utiliza un dato específico de X11. Sin embargo, existe otra llamada, metawindow_get_client_pid(), que sí funciona desde ambos sistemas; por desgracia es privada, lo que significa que sólo se puede llamar desde C y desde dentro de mutter, nunca desde una extensión escrita en Javascript.

Propuse entonces en el canal IRC de Gnome Shell que dicha llamada se cambiase a pública, pero mi idea no convenció porque existen varios ataques que involucran PIDs de procesos, por lo que, aunque yo hiciese las cosas bien, hacer pública dicha llamada podría suponer abrir la caja de Pandora. Sin embargo, sí me redirigieron al código de XWayland para que viese ahí como lo hacen de manera segura: básicamente, al lanzar el proceso crean manualmente un socket y asignan un extremo a Wayland, pasando el otro extremo al proceso hijo para que se comunique a través de él; luego basta con engancharnos a la señal map() y, por cada ventana que aparezca, comparar si su socket coincide con el que creamos nosotros para nuestro proceso hijo, en cuyo caso podemos estar seguros de que esa ventana pertenece a él y no a otro.

La idea era buena, pero por desgracia no se puede implementar directamente en Javascript porque precisa de varias llamadas privadas de mutter, además de que tampoco se pueden crear sockets desde Javascript. Ante esto me sugirieron escribir una clase GObject que lo implementase y exportase una interfaz para ello, y así lo hice: mi primer parche para mutter y mi primera clase GObject. Esta clase sólo funciona con Wayland (para X11 no tiene sentido, pues la propia aplicación puede detectarlo y hacer ella misma el trabajo), y permite lanzar un proceso y detectar si una ventana concreta pertenece o no a dicho proceso. Si se utiliza desde X11, la parte de lanzar el proceso también funciona, pero el método de detectar si una ventana pertenece o no al proceso genera una excepción (que, obviamente, se puede capturar). Esto permite simplificar el código en la extensión a la hora de hacer que funcione en ambos entornos de ventanas.

Aunque dicho parche funciona bien, aún sigue pendiente de aprobación; pero yo quería poder usar YA la nueva versión de Desktop Icons, así que, como a cabezón no me gana nadie, decidí ver qué podía hacer para detectar de manera segura la ventana de un proceso utilizando sólo lo que ya tenía disponible en Javascript. Para ello se me ocurrió que la aplicación de escritorio podría poner como título de la ventana una cadena concreta que la extensión pudiese identificar (tiene que ser en el título, pues no parece haber absolutamente nada más en una ventana que se pueda asignar de manera libre por el programa y que una extensión pueda leer). El problema es que dicha cadena no puede ser predecible, pues entonces otras aplicaciones podrían hacerse pasar por la legítima. Ante esto, decidí que la extensión generaría un UUID aleatorio de 128 bits justo antes de lanzar el proceso, y se lo pasaría a éste para que lo ponga en el título de la ventana (y, por supuesto, calculando uno nuevo cada vez que la aplicación se muera). Pero claro, pasarlo como parámetro por la línea de comandos sería completamente inseguro porque cualquier programa puede leer los parámetros de cualquier otro proceso (basta hacer un ps ax o leer /proc), por lo que tenía que ser algo más seguro. Al final la solución consistió en pasarlo a través de stdin, de manera que nadie más pueda leerlo (habría sido más elegante utilizar un pipe específico, pero por desgracia desde Javascript no es posible crear nuevos pipes).

Para simplificar aún más el código escribí una pequeña clase Javascript que es compatible a nivel de métodos con la clase GObject de mi parche, de manera que, si se aprueba, sólo tendré que reemplazar una clase por otra en el código (o incluso utilizar una u otra en función de la versión de Gnome Shell).

Con esto resolví el primer problema, pero ahora quedaba el segundo: aunque ya puedo identificar qué ventana es la del escritorio ¿como hago para mantenerla donde debe estar?

La solución elegante sería cambiar el tipo de ventana a uno de los tipos estándar (en concreto, a META_WINDOW_DESKTOP). Por desgracia, desde Javascript no es posible cambiarlo, pues la llamada es privada. Obviamente preparé un parche para cambiarlo, donde, además de hacerla pública, también la renombro para que sea consistente. Este cambio convenció mucho más en el canal IRC, por lo que espero que sea finalmente aceptado junto con el otro, pues la ventaja de estos dos parches es que no son específicos para este proyecto, sino que permiten, en general, «externalizar» el trabajo que actualmente se realiza dentro del compositor, lo que permitiría que más elementos, como por ejemplo la barra superior o un dock, puedan ser gestionados desde un proceso externo.

Sin embargo, seguía queriendo poder utilizar YA el programa, así que lo que hice fue engancharme a varias señales para forzar la ventana a permanecer en su sitio:

  • raised: esta señal se emite cada vez que una ventana pasa a primer plano. En su callback llamo al método lower(), que se encarga de mandarla debajo de todas las demás ventanas, manteniéndola así al fondo.
  • position-changed: como su nombre indica, esta señal se emite cuando el usuario cambia la posición de una ventana. En el callback la devuelvo siempre a donde le corresponde. Y es que, aunque la ventana del escritorio no está decorada (y, por tanto, en principio el usuario no tendría donde pinchar para moverla), sigue siendo posible utilizar combinaciones de teclas para cambiarla de sitio, cosa que no se puede permitir.

A mayores llamo a stick() para que la ventana aparezca en todos los workspaces.

Con esto ya se puede conseguir que la ventana permanezca siempre en su sitio, pero aún queda por evitar que aparezca en la lista de ventanas. De no hacerlo, aparecerá en el modo Actividades de Gnome Shell y en el cambiador de ventanas (el de Alt + Tab). Para solucionar esto hay que reemplazar tres métodos:

  • Meta.Display.get_tab_list()
  • Shell.Global.get_window_actors()
  • Meta.Workspace.list_windows()

Con estos tres métodos, la ventana desaparece «lo suficiente» como para que sea usable (por ejemplo, en Dash to Dock no desaparece, pero es un mal menor). Por supuesto no es muy elegante, y el resultado será perfecto si se acepta el parche para cambiar el tipo de ventana.

Y de esta manera tan horrorosa (aunque sólo hasta que aprueben mis parches… si es que los aprueban, claro) conseguí mover toda la funcionalidad del escritorio fuera del compositor, lo que resuelve de un plumazo todos los problemas anteriores. El resto del trabajo fue, básicamente, convertir los widgets de St y Clutter en los equivalentes de Gtk, quitar mucho código asíncrono que ya no era necesario y sólo complicaba terriblemente la lógica, y algunos detalles a mayores como añadir código extra en la extensión para que, durante el arranque, le comunique cuantos monitores hay, así como sus coordenadas y tamaños.

La extensión ya está disponible en la página de extensiones de Gnome Shell, y ofrece, además de todo lo que ya tiene la extensión original, Drag’n’Drop, no congelar la composición del entorno gráfico cuando se refresca el escritorio, más velocidad, mostrar los nombres de ficheros demasiado largos cuando se pase por encima el ratón, y más.