sábado, 8 de junio de 2013

Esa teclita que tanto te toca la moral en GNU/Linux

 Actualizado: Cambiado comentario acerca de Windows 8.1
Seguramente muchos de los usuarios de GNU/Linux se habrán encontrado con algún periférico cuyos botones no tienen la función esperada. Como siempre esto hay que agradecérselo a los fabricantes de hardware. Teclas de función que no responden o que tienen funcionalidades que no coinciden con lo que deberían hacer.

El núcleo Linux tiene un sistema bastante avanzado de detección de periféricos, que por ejemplo evita la absurdez de Windows por la que si enchufas un mismo dispositivo en otro puerto USB tienes que esperar unos segundos a que termine de reinstalar el driver correspondiente (ese "bocadillo" que aparece en la esquina inferior derecha de la pantalla, en la bandeja del sistema), algo que en Linux es instantáneo.


Este sistema avanzado se incorporó en la versión 2.6 y se llama udev. Permite plug&play de cualquier cosa y mediante la interacción con un demonio y una serie de reglas permite modificar comportamientos que por defecto se detecten de forma errónea. Si los fabricantes dieran un mínimo de soporte al núcleo Linux, éstos crearían un genial repositorio de reglas. Reglas para HP, Canon, Nikon, Epson, Logitech ... y clasificadas según tipo de dispositivo (keyboards-logitech, keyboards-genius, mice-logitech, mice-genius, mice-razor, mice-steelseries ...). También habría un excelentísimo driver libre que se podría incorporar en el núcleo como módulo. En el caso de Logitech sí que ha hecho algún driver, por ejemplo el que se usa para los receptores unifying.

En fin, todo esto y más en un mundo perfecto. Pero no tenemos un mundo perfecto, más bien es ferpecto, que se parece a perfecto y sólo eso, se parece.

Así pues, en este mundo ferpecto tenemos que escribirnos nuestras propias reglas para este sistema tan chachi llamado udev y además averiguar qué es lo que no funciona bien para poderlas escribir. Y eso está muy bien, porque permite una personalización máxima y realmente accesible, sin limitarnos a una interfaz gráfica dada por el fabricante y si uno quiere hurgar algo más no necesita recurrir al registro de Windows o aplicaciones de terceros.
Evidentemente tampoco estoy diciendo que no estaría de más tener interfaces gráficas para estos menesteres, al menos para lo básico. YaST de OpenSUSE no sirve, gracias, sistema que por otra parte va a ser reescrito enteramente en Ruby.

En las explicaciones que aquí siguen emplearé como ejemplo un ratón, un Logitech T400. Además, se usa la distribución GNU/Linux Ubuntu 12.04.2 LTS. Este ratón está pensado para ese sistema operativo tan ferpecto llamado Windows 8. Resulta que este animalito tiene una pantalla de inicio que se activa con la tecla Súper (tecla Windows para los usuarios de Windows), y a los de Logitech no se les ocurrió otra cosa que dar acceso directo a esa pantalla dando por defecto la funcionalidad de tecla Súper al botón central del ratón en su parte inferior, dejando a la parte superior, la más cercana al centro y que obliga replegar el dedo, el clic central. El clic central se usa mucho, para abrir nuevas pestañas o ventanas. Si quieres recuperar el orden natural de las cosas, debes instalarte el software de Logitech, que además puede provocar incompatibilidades con otros programas como juegos. Además, resulta evidente que dicho software no está disponible en GNU/Linux.

Con Windows 8.1 la cosa no ha ido a mucho mejor, han añadido un botón inicio, sin embargo éste sigue llevando a la pantalla de baldosas. Es configurable para que muestre directamente las aplicaciones instaladas en orden alfabético, sin embargo la desorientación por un cambio brusco de entorno sigue estando ahí.

Para conseguir reconfigurar los botones en GNU/Linux hay varias alternativas. Las primeras se basan en el uso de la funcionalidad ofrecida por el sistema de ventanas, el servidor X Window de turno, en nuestro caso X.Org.
Las segundas son de más bajo nivel, utilizando la funcionalidad que nos ofrece el núcleo.

Utilizar lo que nos ofrece X.Org va bien para casos sencillos. Permite una reconfiguración rápida. Sin embargo el protocolo X11 sólo emplea un byte como identificador para las teclas, así que está limitado a 256 teclas distintas, mientras que las teclas de función adicionales suelen identificarse con más de un byte, por lo que por seguridad se suelen reasignar a teclas ya existentes por debajo del identificador 255 y que no estén en uso.

Si queréis saber qué es lo que entiende X.Org al pulsar una tecla, podéis ejecutar el programa xev desde un terminal y empezar a pulsar teclas. Veréis que aparece una ventana y a medida que se pulsan teclas en el terminal sale el código de la tecla y la función/símbolo que tiene asignada. Un ejemplo de salida es el siguiente:
KeyPress event, serial 36, synthetic NO, window 0x4200001,
    root 0xcb, subw 0x0, time 54758409, (310,160), root:(311,210),
    state 0x10, keycode 40 (keysym 0x64, d), same_screen YES,
    XLookupString gives 1 bytes: (64) "d"
    XmbLookupString gives 1 bytes: (64) "d"
    XFilterEvent returns: False

KeyRelease event, serial 36, synthetic NO, window 0x4200001,
    root 0xcb, subw 0x0, time 54758489, (310,160), root:(311,210),
    state 0x10, keycode 40 (keysym 0x64, d), same_screen YES,
    XLookupString gives 1 bytes: (64) "d"
    XFilterEvent returns: False

Al pulsar la tecla D se generan dos eventos: el de pulsarla y soltarla. Vemos que dicha tecla tiene asignado al keycode 40 el keysym (key symbol, símbolo) 0x64, que es la "d". También vemos la variable state que cambia según las teclas modificadoras que estén activas (mayúscula, bloqueo de mayúsculas, scroll ...).
El keysym se podría cambiar con Xmodmap o la propia herramienta de reasignación de tu entorno de escritorio favorito. Sin embargo hay un pequeño problema y es que, ¿qué ocurre cuando el mismo keycode está mal? Pues que entonces ya no es problema de X.Org y hay que bajar un nivel más. En el caso del ratón que nos ocupa, el keycode emitido era el 133, que se corresponde con el keysym "Super_L" (meta izquierda, súper izquierda o tecla windows izquierda). Ese es el keycode que emite el ratón por defecto. Sin embargo lo que nosotros queremos es que emita lo siguiente:
ButtonPress event, serial 36, synthetic NO, window 0x4200001,
    root 0xcb, subw 0x0, time 55233879, (95,33), root:(407,191),
    state 0x10, button 2, same_screen YES

ButtonRelease event, serial 36, synthetic NO, window 0x4200001,
    root 0xcb, subw 0x0, time 55239776, (95,33), root:(407,191),
    state 0x210, button 2, same_screen YES

Entonces ya no es un evento de pulsado de tecla sino de botón. Concretamente el botón 2 que se corresponde con el botón central de un ratón.
En algún punto el sistema operativo ha de hacer el cambiazo para que el sistema de ventanas lea que se ha presionado un botón de ratón en vez de una tecla de teclado.
Habría otras maneras de hacerlo, pero resulta que los scancodes emitidos por el ratón son de más de un byte, así que ciertas herramientas no pueden usarse. Podría explicarlas pero no creo que haga falta, ya que lo aquí explicado es válido para todo
Es más se podría cambiar la asignación entre keycode y keysym, pero entonces cualquier dispositivo que emitiera el keycode 133 escribiría el mismo keysym. O si cambiásemos el keysym asignado a dicho keycode, perderíamos la función de tecla Súper en el teclado, pasando a funcionar como otra tecla. De todas formas esto no sirve para nada ya que lo que queremos es que el ratón deje de emitir pulsaciones de teclado a ojos de X.Org.

Por tanto, como X.Org es muy limitadito y sólo entiende de keysyms que asigna a keycodes, introducimos otro nuevo elemento perteneciente a un nivel más bajo en las capas: el scancode, al cual se le asigna un keycode.
Ahora tendremos que averiguar cuál es el scancode que emite el ratón (en vez del keycode) y asignarle un keycode, que será el keycode correspondiente al del botón central del ratón (keysym button 2, keycode "middle"). Ese keycode lo leerá el sistema de ventanas que por defecto tiene asignada la función que el usuario esperaría al pulsar dicho botón.

Si consultamos la referencia en línea del código fuente del núcleo Linux, daremos con la cabecera input.h. En ella encontraremos las teclas y los keycodes que tienen asignados. La D tiene el keycode 32. ¡Vaya!, no coincide con el 40. La explicación es sencilla. Por razones históricas hay un offset de 8 posiciones entre los keycodes usados por X.Org y los keycodes del núcleo del sistema. Así, si al keycode del kernel le sumamos 8 (32+8) tenemos el 40. Esto es válido para los keycodes del 0 al 255. El resto (eventos de botón y no de tecla) tienen un mapeo 1:1.

Una vez explicado más o menos lo que hay que hacer y lo que se necesita, pongámonos manos a la obra. Todo se realiza mediante terminal. Primero de todo necesitamos saber qué dispositivo se corresponde con el ratón. Resulta que las pulsaciones de botón se emiten como si fuera un teclado más. Para encontrar los teclados de nuestro sistema podemos utilizar el comando /lib/udev/findkeyboards, en mi caso devuelve la siguiente salida:
user@host:~$ sudo /lib/udev/findkeyboards 
[sudo] password for user: 
USB keyboard: input/event6
USB keyboard: input/mouse0
AT keyboard: input/event3

El AT keyboard se corresponde con el teclado integrado en el portátil. Los otros 2 dispositivos USB se corresponden con el ratón. Tendremos que utilizar el input/event6. Este archivo se encuentra en el directorio /dev/input/ de nuestro sistema.

Una vez averiguado cual es el dispositivo que se corresponde con el ratón, tenemos que averiguar qué scancodes emite. El comando /lib/udev/keymap viene al rescate:
user@host:~$ sudo /lib/udev/keymap -i input/event6
Press ESC to finish, or Control-C if this device is not your primary keyboard
scan code: 0x700E3   key code: leftmeta
scan code: 0x700E3   key code: leftmeta
scan code: 0x90003   key code: 112
scan code: 0x90003   key code: 112
(no scan code received)  key code: 112

El scancode 0x700E3 es nuestro objetivo. Hay que cambiar ese leftmeta por un 0x112. Dicho y hecho. Creemos un nuevo mapa para que al conectarse el ratón udev aplique dicho mapa de teclas al ratón.
user@nhost:~$ sudo vim /lib/udev/keymaps/logitech-t400 
[sudo] password for user:

Podéis invocar el editor que queráis desde el terminal (gedit, geany, vim, nano, kate, kwrite, ...), pero eso sí, con permisos de superusuario.

Tenéis que añadir lo siguiente a dicho keymap (un archivo de texto normal y corriente):
0x700E3 0x112 #boton central de raton

Guardamos y listos. Ahora si ejecutáis un sudo /lib/udev/keymap input/event6 logitech-t400 aplicará dicho cambio. No hace falta indicar la ruta absoluta al archivo ya que lo tenemos puesto en la ruta donde por defecto busca mapas de teclas.

El scancode pasa a tener asignado el que se corresponde con el botón central del ratón y el servidor de ventanas X.Org recibirá ese keycode, tratándolo como un evento de botón en vez de teclado.

Peero, aún no hemos terminado. Tenemos que hacer que este cambio sea permanente. Así, tendremos que crear una regla para udev de forma que ejecute el anterior comando cada vez que se conecte el ratón. Para escribir dichas reglas viene bien echarle un vistazo a este manual y de paso tratar de entender la salida de los comandos que nos dan información acerca de los dispositivos.

Hay básicamente dos órdenes:
  • udevadm info --export-db | less
  • udevadm info -a -p `udevadm info -q path -n /dev/input/event6` | less
Con ellas podremos averiguar las propiedades que tiene nuestro dispositivo. Para este ratón que está situado en /dev/input/event6, obtenemos la siguiente salida:
En ambos el event6 es el dispositivo correspondiente al emisor de eventos de teclado en el ratón. El input6 es el dispositivo padre. Las propiedades que se muestran en dichos archivos son las que hay que usar para la regla de udev. Basándome en las reglas ya existentes para teclados, he creado un nuevo archivo. Sólo hay que copiarlo en el directorio /lib/udev/rules.d/. Para copiarlo ahí se necesitan permisos de superusuario.
Esa regla aún la tendría que tunear un poco, ya que si hay más de un dispositivo conectado al receptor, hará la misma sustitución a todos los que sean de tipo "event".

Con eso ya tendréis el botón del ratón debidamente asignado. Si tenéis otro dispositivo que configurar, espero que con lo aquí explicado y la documentación enlazada más algo de Google podáis ponerlo a vuestro gusto.

BONUS TRACK
En la wiki de Ubuntu vemos que hay un artículo, algo anticuado, sobre cómo remapear las teclas de un Logitech G15. Si leemos dicho documento vemos que lo que se describe es el cambio de la asignación keycode => keysym por defecto por otra que se ha creado a mano.
Mejor sería hacer un keymap para udev y que éste haga el cambio del keycode por uno de los disponibles para mandos, volantes, etc. y además así sólo el teclado G15 tendrá esa asignación de keycodes. A otro teclado se le asignará otra diferente. Todo esto claro siempre y cuando los scancodes sean detectados sin necesidad del demonio que ahí mencionan.

No hay comentarios:

Publicar un comentario