Archivo por meses: septiembre 2014

Tablet con systemd

Hace un par de días actualicé el sistema Debian que le había instalado a mi tablet, y me encontré con la desagradable sorpresa de que mi gestor de ventanas y mi driver táctil dejaron de funcionar. El motivo es que, recientemente, Debian se ha cambiado a systemd, por lo que tuve que hacer algunos cambios para adaptarlo.

El primer y más fundamental cambio fue reemplazar los scripts en bash por ficheros de configuración de systemd. Este es el fichero para lanzar el driver táctil:

[Unit]
Description=GSLx680 user-space driver launcher for systemd

[Service]
Type=simple
ExecStart=/bin/gslx680 -new_scroll /dev/i2c-1 /etc/gslx680/firmware.cfg
ExecStop=killall gslx680

[Install]
WantedBy=multi-user.target

Este fichero lanza durante el arranque el driver, y durante el apagado del sistema lo mata.

El fichero para lanzar las X es similar:

[Unit]
Description=Launch X11

[Service]
Type=simple
User=debian
ExecStart=/usr/bin/startx
ExecStop=killall xinit

[Install]
WantedBy=multi-user.target

El único cambio es que especificamos con qué usuario queremos lanzar el comando: en este caso el usuario es debian.

Otro cambio que tuve que hacer fue eliminar, en el fichero .xinitrc, el que se lance ck-launch-session, el gestor de sesiones de ConsoleKit. Este ya no es necesario porque de ello se encarga systemd.

Por último, para apagar el sistema el gestor de ventanas ya no ejecuta halt, sino systemctl poweroff, con lo que ya no es necesario que haya un comando para ello con el bit suid activo.

Un último detalle: tuve que desinstalar el demonio pulseaudio para conseguir que reprodujese vídeos. Todavía no se el motivo de que con él lanzado no funcione (el audio queda bloqueado y tanto mplayer como vlc se quedan congelados esperando a que se libere).

Nueva tablet para cacharrear

Actualizado: he retocado algunos detalles de la línea de configuración que eran incorrectos. Al escribir la entrada corté y pegué una línea que no era.

Un colega me regaló una vieja tablet que no funcionaba para aprovechar piezas, pero como encendía y llegaba a hacer algo, decidí ver si podía resucitarla (sí, la pantalla está rota; afortunadamente sólo es el plástico, el LCD está en perfecto estado).

tablet

El primer intento consistió en encenderla pulsando a la vez el botón de encendido y el de subir volumen para entrar en modo de rescate, a ver si conseguía restaurar el sistema Android. Aparentemente funcionó, pero al intentar arrancar se volvía a quedar a la mitad, por lo que empecé a sospechar que el problema estaba en la memoria flash interna, pero para demostrarlo necesitaba acceder al sistema de alguna manera.

Echando un vistazo por dentro encontré que había cuatro pads en una esquina marcados como gnd, tx, rx y 3,3v, por lo que sospeché que podría tratarse de un puerto serie de depuración. Estos puertos serie son bastante habituales en aparatos de consumo, pero tienen el inconveniente de que trabajan con niveles TTL (0 voltios para un cero, o 5 o 3,3 voltios para un uno) en lugar de las tensiones estándar del RS-232 (3/15 voltios para un cero, -3/-15 voltios para un uno). Existen algunos conversores de serie TTL a USB, pero por diversos motivos preferí hacerme una placa con un MAX 3232 y un conversor RS-232 a USB normal y corriente. El MAX3232 es un chip similar al conocido MAX232, que convierte los niveles entre un puerto serie TTL y uno RS-232, pero con la ventaja de admitir tensiones de alimentación entre 3 y 5,5 voltios (el MAX232 está limitado a 5 voltios). Con este chip pude conectar la tablet a mi PC (pulsa en la imagen para ampliar).

tablet_conversor

Un detalle importante es que fue necesario soldar dos puentes para activar el puerto serie. Están marcados con un círculo rojo en la siguiente fotografía (pulsa en la imagen para ampliar):

 

tablet_serial

Una vez hechos los puentes y lanzado el minicom con el puerto serie configurado a 115.200 8N1, apareció el texto de arranque. Ahí pude ver que, como esperaba, primero cargaba el U-Boot, y éste cargaba el núcleo Linux de Android, siguiendo el proceso habitual. Sin embargo, al llegar a cierto punto empezaron a salir errores de lectura de la flash, tal y como temía. Sin embargo, decidí probar si podía conseguir acceso al U-Boot para cambiar las opciones de arranque, y sí, fué posible: pulsando la tecla Return en el terminal varias veces en el momento de encender la tablet detiene el proceso de arranque y ofrece un prompt en el que se puede jugar con muchas opciones.

Con el comando print eché un vistazo a las variables de entorno, y encontré la que me interesaba: la que contiene el parámetro bootargs. Esta variable define los parámetros de arranque que se le pasan al núcleo al arrancar, tales como la partición con el sistema de ficheros raíz y otras. En el caso de esta tablet, sin embargo, el proceso es ligeramente oscuro, pues lo que se hace es llamar a un script que define en cada momento la variable bootargs en función de lo que sea necesario. En concreto, este es el script tal y como viene definido:

set-rfs-ram-ota=setenv bootargs mem=${memtotal} root=/dev/ram0 rw initrd=${load-addr-initrd},0x${filesize} console=ttyS0,115200n8 init=/init androidboot.serialno=${androidboot.serialno}

Para hacer una primera prueba introduje la tarjeta de memoria de mi otra tablet, la cual ya tiene un sistema Debian completo, junto con mi gestor de ventanas para tablets, y modifiqué la entrada anterior con:

setenv set-rfs-ram-ota setenv bootargs mem=${memtotal} root=/dev/mmcblk0p2 rw rootdelay=1 console=ttyS0,115200n8

El dispositivo /dev/mmcblk0p2 es la partición segunda de la tarjeta microSD, que es donde tengo el sistema Debian. Al principio le añadí un rootdelay=7 para retrasar siete segundos el montaje de la partición raíz, pensando que tardaría un poco en detectar la tarjeta, pero al final no es necesario. En cambio, si se quiere arrancar desde una partición en un disco USB, es muy probable que dicha opción sea necesaria para darle tiempo al núcleo a detectar los dispositivos.

Y con  esto la tablet arrancó perfectamente (pulsa en la imagen para ampliar):

tablet_booted

Ahora voy a probar a compilar mi propio núcleo y a intentar arrancarlo desde la tarjeta microSD.

Tamaño constante en GTK

Hace unos días envié un parche para Slingshot, el lanzador de aplicaciones de Elementary OS. El problema que tenía es que tenía fijados al pixel los tamaños de todos los elementos. Esto se adapta bien si se utiliza el tema de Elementary y, además, en lengua inglesa. Sin embargo, en el momento en que se utiliza otro tema, o bien una lengua en la que alguna categoría de programas sea mucho más larga que en inglés, nos encontraremos con que el conjunto de iconos queda recortado por la derecha:

Se supone que esto no debería ocurrir porque Gtk permite crear interfaces que se redimensionen de manera automática, sin embargo en slingshot esto no ocurría. El motivo es que los iconos no están hechos con un Gtk.IconView, sino con botones dentro de un Gtk.Grid. Pero los autores querían, además, que dichos botones tuviesen un tamaño fijo de 130×130 pixels exactamente, por lo que la única idea que se les ocurrió fue fijar el tamaño exacto de la ventana que contiene todo el lanzador, incluyendo la lista de categorías, el buscador, etc. El motivo de hacerlo así es que dentro de cada botón hay, además del icono, una etiqueta con el nombre de la aplicación, y salvo que el contenedor tenga un tamaño determinado, la etiqueta intentará expandirse todo lo que pueda, y ni siquiera sirve de nada pedir un tamaño deseado, porque si el texto en la etiqueta es más largo, la etiqueta se expandirá todo lo que pueda para contener todo el texto.

La primera idea que se me ocurrió fue engancharme a la señal size_allocate del botón. Esta señal se emite cada vez que un widget cambia de tamaño por el motivo que sea, y recibe como parámetro sus nuevas medidas. Con ella y la propiedad max_width_chars intenté limitar el tamaño de la etiqueta para que el botón tuviese el tamaño máximo deseado; por desgracia era un proceso relativamente lento y que, además, provocaba un parpadeo en la ventana, pues ésta primero tomaba el valor máximo y se reducía progresivamente hasta el tamaño correcto, a medida que las distintas etiquetas iban reduciendo su número de caracteres.

La segunda idea, sugerida por la gente de la lista de desarrollo de Gtk, fue encapsular la etiqueta dentro de un Gtk.Fixed, pues permite ajustar manualmente el tamaño de cualquier widget situado en su interior. Esta solución casi funcionó, pues aunque la etiqueta se mantenía en el ancho deseado, el Gtk.Fixed contenedor se redimensionaba hasta ocupar el mismo espacio que ocuparía la etiqueta si estaba sola.

Ante esto me di cuenta de que la solución iba a necesitar de algo más de investigación y, sobre todo, de meterme en los entresijos del renderizado de Gtk, así que empecé a investigar, y descubrí que todo widget tiene dos métodos que participan de manera especial en la asignación de tamaño: get_preferred_width y get_preferred_height. Estos dos métodos devuelven dos valores: el primero es el tamaño mínimo que necesita dicho widget en el eje correspondiente para mostrar su contenido, y el segundo el tamaño deseado. En el caso de una etiqueta, el tamaño deseado será el ancho que ocupe la totalidad del texto puesto en una única línea, mientras que el tamaño mínimo dependerá de varias opciones: si no hay elipsis de texto coincidirá con el tamaño deseado, pero si no, será menor, de lo necesario para pintar el borde y unos puntos suspensivos.

Cuando un contenedor tiene en su interior otro widget, llama a estos dos métodos para saber cuanto espacio debe reservar. Lo interesante es que dichos valores son, realmente, orientativos, pues el contenedor puede asignar otros tamaños si así lo considera oportuno. Así, si una etiqueta pide 100 pixels pero está en una ventana que mide 200 y tiene activo el flag de expandirse, el tamaño final de la etiqueta será de 200. De igual forma, si una etiqueta pide un tamaño deseado de 100 pixels de ancho pero la ventana mide 50, recibirá 50 y el texto tendrá que ocupar varias líneas.

Ante esto se me ocurrió probar qué ocurriría si creo una nueva clase que herede de Gtk.Label, en la que redefino (override) el método get_preferred_width para que devuelva 120 como ancho mínimo y deseado (porque cada botón tiene un margen de 5 pixels por cada lado). El resultado es justo el deseado: la etiqueta mide exactamente el ancho que se le indica, y aplica las reglas para pasar a nuevas líneas o para añadir elipsis.

Por supuesto esto es fácil de hacer en Vala; en C es posible hacerlo también, pero obliga a meterse en el interior de GObject, una tarea que, sinceramente, no me llama demasiado en este momento.