Hacer una ventana con el fondo transparente utilizando Gtk es un clásico; de hecho hay ejemplos de como hacerlo en python, en C y en Vala. Pero falta como hacerlo con Javascript, y dado que es el lenguaje de moda para Gnome (y también por una serie de circunstancias extra) me he decidido a hacer el ejemplo. Este es el código:
#!/usr/bin/env gjs
imports.gi.versions.Gtk = '3.0';
const Gtk = imports.gi.Gtk;
const Gdk = imports.gi.Gdk;
Gtk.init(null);
let window = new Gtk.Window();
window.set_app_paintable(true);
let screen = window.get_screen();
let visual = screen.get_rgba_visual();
if (visual && screen.is_composited()) {
window.set_visual(visual);
window.connect('draw', (widget, cr) => {
Gdk.cairo_set_source_rgba(cr, new Gdk.RGBA({red: 0.0,
green: 0.0,
blue: 0.0,
alpha: 0.0}));
cr.paint();
return false;
});
window.connect('delete-event', () => {
Gtk.main_quit();
});
window.show_all();
Gtk.main();
} else {
print("El entorno de ventanas no admite transparencia");
}
Aunque el código no tiene nada de especial comparado con las versiones en otros lenguajes, sí es cierto que tuve problemas con Gdk.cairo_set_source_rgba, pues, como se ve, no sigue la estructura «normal» de llamadas como si fuera un objeto, sino que sigue el formato de C. Lo lógico sería hacer cr.cairo_set_source_rgba(…), pero no funciona. Y lo mismo con algunas otras.
Recién salida del horno está Cronopete 4.10. Los cambios son relativamente pequeños, pero necesarios.
Para empezar, ahora ya no copia las carpetas .gvfs, .dbus, .cache ni .var/app/*/cache si se ha marcado la casilla de copiar también las carpetas ocultas del directorio raíz. Esto era importante porque dichas carpetas no contienen datos del usuario, sino otras cosas que no tiene sentido preservar. En el caso de .gvfs (aunque desde hace un tiempo ya no se utiliza este destino sino otro en /run/user), es una carpeta donde se montan las unidades remotas a las que se accede mediante Gio, el framework de Gtk para acceder a ficheros, por lo que lo que hay ahí son datos remotos que deberían ser preservados desde la otra máquina. En el caso de .dbus, contiene el socket para acceder al bus de sesión de D-Bus, por lo que no hay nada que preservar (de hecho, intentar sacar una copia de dicha carpeta da error). En el caso de .cache, como su nombre indica, lo que contiene son ficheros temporales, como por ejemplo la caché del navegador. No tiene ningún sentido desperdiciar disco duro para copiar eso. Y lo mismo para las carpetas .var/app/*/cache, las cuales contienen ficheros temporales de programas en formato flatpack, y que no tiene sentido preservar por el mismo motivo que la carpeta .cache.
El segundo cambio es que ahora permite salir del programa, una opción que me pidieron expresamente. Por supuesto está relativamente escondida para evitar activarla por accidente (un programa de copia de seguridad, por definición, no debe ser trivial de desactivar).
Por último, remodelé un poquito la interfaz de configuración por petición de varios usuarios, pues aunque era una copia perfecta de la de Time Machine de Apple, en la práctica los textos no eran demasiado claros, y había que usar prueba y error para encontrar algunas cosas. Ahora están más claros.
En 1984, hace ya la friolera de 35 años, un grupo de investigadores de la división de gráficos por ordenador de Lucasfilm (lo que un par de años después pasaría a llamarse Píxar) publicó un paper donde presentó la solución definitiva a muchos de los problemas del momento de los gráficos por ordenador orientados al cine y al entretenimiento. Me refiero al Distributed Ray Tracing. Esta técnica permitía añadir a los gráficos por ordenador hechos mediante Ray Tracing elementos fotorrealistas tales como motion blur, profundidad de campo, sombras con penumbras, antialiasing, y más, y todo ello sin añadir prácticamente carga computacional extra. Esa era su gran novedad.
Es cierto que el Ray Tracing es una técnica tan costosa a nivel de procesador y, sobre todo, de memoria, que, durante décadas, Renderman, el render desarrollado por Píxar y utilizado en la inmensa mayoría de escenas de películas hechas por ordenador, no la utilizaba, sino que se basaba en la técnica REYES (Renders Everything You Ever Saw). Era la única manera de poder hacer gráficos fotorrealistas en los equipos de los 80, los 90 y buena parte de los 2000. Sin embargo, algunas de las técnicas desarrolladas en el paper se pudieron adaptar a dicho algoritmo, específicamente el motion blur y la profundidad de campo. Hoy en día, en cambio, sí que se utiliza Ray Tracing de manera rutinaria, basado en mejoras de las técnicas presentadas en este paper. Así trabajan hoy en día Ray Tracers comerciales como Arnold, o el propio Renderman a partir de su versión 19.
Sin embargo, además de todo esto, al final de dicho paper aparece una imagen que muestra las capacidades de los algoritmos descritos en él, y que ya se puede considerar icónica. Dicha imagen se titula 1984 y muestra una mesa de billar con cinco bolas, las cuales exhiben los efectos de motion blur, sombras suaves, reflectividad (lo que permite ver la ventana de la sala y a una persona sosteniendo un taco)…
¿Y a qué viene toda esta chapa? Pues a que he querido hacer un pequeño homenaje a dicha publicación y a los genios detrás de ella programando un pequeño Ray Tracer propio con el que reproducir dicha imagen. Y éste es el resultado:
Mi versión/homenaje. Pulsa sobre ella para ampliarla.
Al igual que en la imagen original, todas las bolas son reflectantes (lo que permite apreciar el entorno de la sala), y cuatro de ellas tienen motion blur: algunas (como la blanca y la bola 9) tienen un motion blur sencillo, y las otras dos, la 8 y la 4, uno más complejo, al estar detenidas durante parte del tiempo de apertura del obturador y moverse sólo durante parte del tiempo de exposición. Por supuesto, no cabe esperar una reproducción fidedigna (¡Soy programador, Jim, no artista!), pero lo importante para mí no es tanto la imagen en sí, sino el hecho de que he sido capaz de programar un Ray Tracer desde cero. Desde que leí el paper original quedé prendado de la elegancia del algoritmo, y tenía ganas de intentar implementarlo.
Sin embargo, lo divertido (relativamente) es que acabé escribiendo no uno, sino dos Ray Tracers: cuando empecé con este proyecto, hace un par de semanas, aún no tenía muy claro el nivel de complejidad que me iba a encontrar, así que decidí no complicarme la vida más de lo estrictamente necesario y opté por utilizar Python para escribirlo, junto con numpy para toda la parte de vectores y matrices que necesitaba. Primero construí un Ray Caster capaz de mostrar esferas y planos. Luego le añadí soporte de mapeado de texturas. A continuación antialiasing y soporte de luces. Llegado a este punto añadí soporte de motion blur y empecé a preparar ya la escena de la sala de billar en lugar de las imágenes de demostración con una Cornell Box.
El resultado es el de ahí arriba. Sin embargo, tenía un problema serio: es desesperadamente lento. Es cierto que, como dije antes, la técnica de Ray Tracing es muy costosa computacionalmente, pero es que hablamos de ¡18 horas para esa imagen a 1920×1080! ¡Y eso que ya había hecho unas cuantas optimizaciones que me permitieron reducir los tiempos a, aproximadamente, dos tercios de los originales, además de utilizar todos los núcleos de mi ordenador! Es más o menos lo que habría tardado a principios de los 90 con PovRay y un 8086.
Fue por esto que, aunque el objetivo original ya estaba cumplido, tomé la decisión de portar el código a C++ para ver cuanto podía arañar. Quería saber si el problema estaba en Python o en mi código. Aunque dicho así puede sonar a locura, lo cierto es que el código de Python era bastante sencillo (actualmente son un total de 1415 líneas de código, que, tras quitar líneas en blanco y comentarios, se quedan en 899), con lo que era factible coger cada uno de los ficheros y traducirlo de un lenguaje a otro. Sólo tuve que escribir un par de clases para trabajar con vectores y matrices y alguna cosa más, lo que no fue especialmente difícil. Es cierto que probablemente podría haber usado cualquiera de las alternativas que seguro que hay por ahí ya escritas, pero dado que sólo tengo que trabajar con matrices y vectores de tamaño fijo, y sólo necesito un puñado de operaciones muy concretas, era más trabajo aprender a usarlas que escribirlo yo. Y encima es probable que me diese más rendimiento (repito: tamaño fijo).
El resultado fue, en una sola palabra: pasmoso. De las más de 18 horas pasé a 398 segundos. Repito: menos de siete minutos para la misma imagen: 1920×1080 y supersampling de 8×8.
Sí es cierto que me encontré con un problema algo raro: en Python utilicé fork() para poder aprovechar todos mis núcleos porque, como es de sobra conocido, el GIL no permite que varios threads se ejecuten de manera concurrente. Esto complicó bastante el código, pues tuve que usar pipes para que el programa principal fuese repartiendo nuevas líneas a cada proceso hijo a medida que iban terminando y quedando libres, y también para que éstos enviasen las líneas ya renderizadas al padre para que las uniese en una sola imagen. Para la versión en C++ no quería complicarme tanto, así que decidí utilizar pthreads desde el principio, de manera que todos los hijos accediesen a la misma memoria para ir depositando sus líneas en el punto correcto sin tener que enviarlas mediante IPC, y autoasignándose la siguiente línea mediante un semáforo. Sin embargo, algo raro ocurría: tardaba más que cuando utilizaba un único proceso. Si no fuese porque top indicaba una carga de procesador del 400%, y que el procesador indicaba que había seis threads de mi programa, habría asegurado que estaba utilizando una biblioteca de threads en espacio de usuario, sin threads reales.
Después de muchas pruebas, de quemarme las pestañas viendo el código, y buceando en Stack Overflow y otros sitios, no conseguí descubrir qué ocurría, así que decidí utilizar también fork() en C++, pero añadiendo memoria compartida para simplificar la comunicación y la complejidad al máximo, y ahí sí que conseguí la ganancia de rendimiento esperada; esto es, multiplicar la velocidad por (prácticamente) el número de núcleos del ordenador.
Y esto es más o menos todo. Aquellos que quieran curiosear un poco en mi código, lo tienen disponible en mi repositorio git:
Como muchos sabrán ya, las extensiones, al igual que el resto de Gnome Shell, están escritas en Javascript, y además no utilizan Gtk sino Clutter/St. Sin embargo, Javascript también se puede utilizar para escribir aplicaciones normales, de escritorio, con Gtk de toda la vida. Para ello basta con utilizar el shebang#!/usr/bin/gjs al principio del archivo. Personalmente sigo prefiriendo Python, pero obviamente para gustos hay colores.
Y precisamente aquí me encontré con el primer gran problema: estoy intentando portar parte de una extensión a espacio de usuario, y dado que el código de la extensión está repartido en varios archivos, quería mantenerlo así también en la aplicación. Por desgracia, por más que buscaba no encontraba como resolver el problema, pues aunque existen algunas aplicaciones Gtk en Javascript que ocupaban varios archivos, no tenía muy claro cómo hacían para que, al llamar a imports, la máquina virtual encontrase el fichero. Pero por fin, después de buscar y rebuscar, encontré la clave:
Esa llamada recibe como parámetro un string donde podemos añadir una ruta extra donde debe buscar ficheros. Basta con que pongamos la carpeta donde se encuentran el resto de ficheros .js de nuestro proyecto, y el comando imports los encontrará igual que encuentra los ficheros del sistema. Por supuesto es necesario añadirla antes de hacer ningún imports.
Este finde he lanzado nuevas versiones de programas. Para empezar, la versión 4.9.0 de Cronopete. El cambio principal consiste en que, ahora sí, detecta correctamente cuando el disco está lleno y procede a borrar una copia antigua para hacer sitio para la nueva. Aunque todo el código era correcto, había un diminuto bug a la hora de detectar que había ocurrido dicho problema: cuando eso ocurre, rsync termina con un código de error 11, pero resulta que waitpid y las funciones equivalentes en vala no devuelven el código de error «tal cual», sino que lo desplazan a los ocho bits superiores, reservando los inferiores para indicar si fue una señal la que provocó la finalización del programa, y cual de ellas. El resultado es que no se detectaba correctamente la situación.
Por otro lado, he lanzado la versión 1.5.0 de Terminus, en la que he corregido la funcionalidad de paste: ahora utiliza el valor del portapapeles en lugar de utilizar lo que haya en el buffer primario. Podría intentar explicar de qué va eso, pero seguro que no lo haría bien porque es un verdadero cristo, así que quien quiera enterarse de como va la selección en X11, que lea esta entrada de freedesktop sobre como funciona el clipboard, y para clarificar algunos conceptos, la entrada del estándar ICCCM.
Ayer lancé la versión 1.14.0 de Autovala. Básicamente corrige varios errores de mesón, en concreto cuando se especifica una ruta de instalación no estándar para los ficheros .gir o para una biblioteca. También permite, por fin, hacer «includes» de ficheros en meson, para personalizar la instalación. Y, por último, ya no instala los ficheros CMakeList.txt cuando se utiliza ninja para instalar una aplicación.
Hace tiempo me compré una caja Elite 110 de Cooler Master. Esta caja tiene la ventaja de ser muy pequeña y compacta gracias a que no tiene hueco para lector de CD/DVD, por lo que me gustaba mucho. Su inconveniente, sin embargo, es que sólo permite placas madre mini-ITX; esto es, de un máximo de 17x17cm, aunque para mí tampoco era un problema, pues mi placa madre era de ese formato. Sin embargo, recientemente se me estropeó, y ahí empezaron mis problemas, porque por más que buscaba no encontraba ninguna placa decente para socket Intel LGA 1150 en formato mini-ITX. Sí las había en formato micro-ATX, pero obviamente eran demasiado grandes y no entraban, o bien un par de modelos chinos a la venta en Amazon que me inspiraban poca o ninguna confianza.
Sin embargo, de pura casualidad, encontré que en una tienda de aquí vendían la Gigabyte GA-B85M-DS3H, que medía 22,6 x 19,3 centímetros. Armado con el metro vi que sí entraba, justita pero entraba, así que la compré. Cual no sería mi decepción cuando fui a montarla, al descubrir que no entraba por unos míseros tres milímetros.
Afortunadamente no tropezaba con el borde de la caja, sino con un tabique interno que permite montar discos duros o bien dos ventiladores extra, así que, tras verlo con calma, comprobé que era posible recortar parte del tabique de manera que la placa entrase. Y dicho y hecho.
Lo primero fue desmontar el tabique. Para ello utilicé el taladro y la broca de 3 milímetros para romper los siete remaches que lo sujetaban (tres delante, dos debajo y dos detrás). Una vez retirado, usé la dremel para recortar la parte inferior. Primero corté el nervio central, como se ve en esta foto (pulsa sobre las imágenes para ampliarlas):
Luego corté por los laterales. En la parte trasera corté justo por debajo del agujero del remache, pero en la delantera corté primero en vertical y luego de manera perpendicular, para así dejar más sitio para la placa. En la siguiente foto se ve, marcado en rojo, la dirección de corte para cada lado:
Una vez hecho esto limamos la pieza con una lima de metal para quitar asperezas que puedan dañar los cables, y limpiamos a conciencia la pieza para eliminar restos de limaduras de aluminio que puedan causar cortocircuitos (yo suelo lavarla un poco y luego secarla cuidadosamente; por hacerlo una vez no es un problema).
Y llega el momento de volver a colocarla en su sitio: para ello colocamos primero los cinco remaches que quedan, pero sin apretarlos aún. Una vez introducidos, les damos un primer apretón a cada uno para asegurarnos de que la pieza se vaya situando en su lugar, y sólo entonces damos el segundo apretón, el que romperá el vástago interno del remache. Este es el resultado:
Ahora ya tenemos espacio suficiente para montar la placa sin que tropiece con nada, y teniendo incluso unos milímetros de espacio extra:
Sin embargo, ya que había desmontado la caja, decidí intentar mejorarla un poco. Por un lado, echaba de menos disponer de un conector eSATA, y por otro, en varias ocasiones me encontré con que necesitaba leer tarjetas SD y similares. Aunque disponía de un lector de tarjetas para montar en una bahía de 3 1/2 pulgadas, era bastante largo; afortunadamente, por dentro estaba prácticamente hueco, así que cogí la dremel y recorté su contenedor hasta el tamaño mínimo, y luego lo sujeté a la parte superior del frontal así:
Fijarlo no fue algo inmediato: el tornillo izquierdo fue relativamente sencillo, pues coincidía con un agujero del frontal y era suficiente con añadir una arandela, pero el tornillo derecho caía justo en la abertura grande que se ve junto al lector. La solución consistió en tomar un trozo de alambre de un clip y doblarlo en forma de gancho:
Este gancho lo metí en el tornillo derecho (también con una arandela) y luego lo doblé por uno de los agujeros del ventilador, situados debajo de la abertura grande, tal y como se ve en estas fotos:
Luego sólo fue necesario hacer un recorte con la dremel en la parte superior del frontal para que sobresaliese el lector.
El conector eSATA fue lo más sencillo de implementar: sólo tuve que coger el taladro y abrir un agujero rectangular del tamaño adecuado en la parte superior derecha del frontal, además de dos agujeros para los tornillos:
Por último, decidí montar el ventilador frontal en este lado del aluminio en lugar de hacerlo en la zona útil de la caja, para así ganar un par de centímetros extra. Por desgracia casi no había sitio, entre el lector de tarjetas y el interruptor de encendido; sin embargo, recortando una de las esquinas conseguí que entrase:
Y con esto ya está terminada: una caja compacta pero con la que no renuncio a nada.
Acabo de lanzar una corrección rápida de Terminus, mi programa de terminal. Básicamente corrige la funcionalidad de COPY mediante el teclado. Por un error, sólo copiaba la selección al buffer primario, pero no al portapapeles, con lo que, en la práctica, no funcionaba.
También aproveché para traducir algunas frases que estaban pendientes.
Acabo de descubrir que mi servidor tiene soporte de Let’s encrypt, un servicio que ofrece certificados https gratuitos, así que he aprovechado para activarlo. A partir de ahora ya se puede acceder tanto a mi página personal como a mi blog de manera segura.
Acabo de subir una nueva versión de Autovala, la 1.12. Los principales cambios son tres:
Para empezar, he completado el soporte de Meson. Ahora ya funciona completamente y soporta todas las funcionalidades. Y teniendo en cuenta la diferencia de velocidad a la hora de compilar, es algo que se agradece mucho.
Por otro lado, ahora ya compila los ficheros .gir a .typelib. Este era un paso necesario para que las bibliotecas con Gobject Introspection funcionasen, pero hasta que ayer intenté desarrollar una y utilizarla desde Gnome Shell, no supe que faltaba ese paso.
Por último, corrige un problema en el espacio de nombres. Hasta ahora, al hacer un programa, se creaban una serie de constantes (como el número de versión, o el prefijo de la ruta de instalación) bajo el namespaceConstants. Sin embargo, en el caso de una biblioteca, dichas constantes se metían en un namespace diferente, llamado como la biblioteca pero con Constants añadido al final. Así, si la biblioteca se llamaba Pepito, se supone que tú utilizarás el namespacePepito para el código de la biblioteca, pero las constantes estarán en el namespacePepitoConstants.
Originalmente esto se hizo para evitar conflictos con otros nombres; por desgracia, el compilador de .gir a .typelib exige que cada namespace tenga su propio fichero .gir; y, por otro lado, Vala se empeña en generar un único fichero .gir con todos los espacios de nombres juntos, uno detrás de otro. Además, no podía ser de otra manera si se quería mantener compatibilidad con Meson. El resultado es que era imposible compilar los ficheros .gir de una biblioteca desarrollada con Autovala.
La única solución fue cambiar eso y hacer que las constantes estén en el mismo espacio de nombres que el resto de código de la biblioteca. Es cierto que esto obligará a modificar el código que use dichas constantes, pero también es verdad que los cambios son inmediatos y directos, por lo que no es un problema real.
Como de costumbre, se puede descargar desde mi página web, estando disponible tanto en forma de código fuente (en gitlab) como en paquetes para Debian, Ubuntu, Fedora y Arch.
Utilizamos cookies para garantizar que tenga la mejor experiencia en nuestro sitio web.