La página contesta
De quién es la pulsación
La regla en la que rumb se ha asentado es que el destino del teclado por defecto es la página, y la barra de direcciones es la excepción.
Eso invierte lo que hacía el shell antes. La barra de URL era lo único que había pedido alguna pulsación, así que las recibía todas, y un campo de texto en una página era decoración. Ahora url_focused es un único booleano, false al arrancar. Pasa a true con Cmd/Ctrl+L o con un clic sobre la barra, y vuelve a false con Enter, que navega, o con Escape, que abandona la edición. En cualquier otro caso, las teclas van a Servo.
Conseguir que se disparara siquiera el comportamiento viejo exigió un arreglo que vale la pena recordar, porque el síntoma mentía. El bug se reportó como «backspace no borra la URL». La realidad era que ninguna pulsación hacía absolutamente nada: GPUI entrega los eventos de teclado solo a un elemento que esté en la cadena de foco, y eso exige a la vez track_focus(&FocusHandle) sobre un elemento enfocable y que la ventana haya enfocado de verdad ese handle. El callback on_key_down no se invocaba nunca. Ahora el shell mantiene un focus handle, lo rastrea en la raíz y vuelve a enfocar en cada render si el foco se ha desviado — la ventana pierde el foco, un elemento hijo se lo lleva, lo que sea.
De pulsación de GPUI a pulsación de Servo
map_key hace la traducción. Una lista corta de teclas con nombre — las flechas, enter, escape, backspace, home y end entre ellas — se mapea directamente. Cualquier otra cosa se convierte en un carácter tomado del key_char de GPUI, que ya ha resuelto shift y AltGr, así que shift+a llega como «A» y option+s como «ß». Cuando no hay key_char — GPUI lo suprime en las combinaciones con Ctrl — se usa en su lugar un nombre de tecla de un solo carácter, que es como Ctrl+C sobrevive convertido en el carácter «c». Cualquier otra cosa no devuelve nada y se descarta: los modificadores solos y todas las teclas de función.
Un segundo helper mapea los cuatro booleanos de modificador de GPUI sobre los bitflags de Servo, con el modificador platform de GPUI — Cmd en macOS, Super en Linux — convertido en META.
Después el evento se construye con KeyboardEvent::new_without_event, y dos de sus argumentos son confesiones. GPUI no le da al shell un scan code físico, así que code es siempre Unidentified y location es siempre standard. Servo gobierna la entrada de texto a partir del valor de la tecla más los modificadores, así que escribir funciona — pero una página que lea event.code, un juego que ate WASD por posición física o un atajo pensado para sobrevivir a una distribución Dvorak no sacan nada aprovechable de rumb. Los dos últimos argumentos son repeat e is_composing, ambos hardcodeados a false, porque el shell no rastrea ninguno de los dos.
El key-up también se reenvía, para las páginas que lo escuchan, y se omite mientras la barra de direcciones tiene el foco, porque la barra solo actúa en el key-down.
Dónde está el puntero de verdad
Los eventos de puntero llegan de GPUI en píxeles lógicos relativos a la ventana. Servo quiere un punto en el espacio propio del WebView, en píxeles de dispositivo. Entre los dos hay dos correcciones.
La primera es el desplazamiento: el contenido web no empieza en lo alto de la ventana, ahí empieza la interfaz del navegador. Antes de que esto se tratara, el mapeo pasaba la y relativa a la ventana tal cual, y desplazaba cada evento de puntero hacia abajo exactamente la altura de la barra de herramientas. Lo que lo hacía confuso es que el error no era uniforme — como la escala vertical usaba la altura completa de la ventana en lugar de la altura del área de contenido, el error crecía linealmente con y. Los clics cerca de lo alto de una página aterrizaban más o menos donde apuntabas. Los clics cerca del final de una página larga se desviaban varios puntos porcentuales.
El arreglo son dos funciones. content_metrics devuelve el rectángulo de contenido para la disposición actual — la barra de URL es siempre una banda a todo lo ancho arriba, y la barra de pestañas ocupa o una banda horizontal o una columna vertical según dónde la haya puesto el usuario, así que los desplazamientos cambian con cada disposición. pointer_to_webview resta ese desplazamiento, recorta dentro del rectángulo y reescala por el tamaño de render de Servo dividido entre el tamaño del contenido.
El reescalado no es redundante. El quad en pantalla de GPUI y el destino de render de Servo pueden diferir — un WM en mosaico forzando un tamaño, escalado fraccionario — y cuando coinciden se colapsa en una multiplicación identidad. La altura de la barra de herramientas vive en una constante con nombre, al lado de la disposición que la define, que es la única razón por la que siguió siendo correcta cuando después se añadió una barra de pestañas por encima de la barra de URL y el desplazamiento cambió.
Cursores, y las formas que GPUI no tiene
Servo le dice a la aplicación que lo embebe qué puntero quiere a través de notify_cursor_changed, en el WebViewDelegate. El shell lo guarda en una celda y — esta es la parte que se escapa fácil — levanta el flag de sucio cuando el valor ha cambiado, para que se programe un frame de GPUI que lo aplique. Sin eso, pasar por encima de un enlace en una página por lo demás estática fijaría el cursor y no se repintaría nada que lo mostrara.
cursor_to_gpui mapea las palabras clave de CSS sobre el CursorStyle de GPUI: pointer a una mano que señala, text a una barra en I, las ocho direcciones de redimensionado a sus equivalentes. Dos notas de esa función. GPUI no tiene equivalente para wait, progress, help, zoom-in, zoom-out ni all-scroll, así que esos caen a la flecha — una página que muestra un cursor de ocupado no muestra nada en rumb. Y las direcciones diagonales de redimensionado se emparejan por geometría, por lo que significan los nombres de las variantes, y no por los comentarios de documentación que GPUI trae sobre ellas, que están mal etiquetados.
La pantalla completa entró en el mismo commit, con F11 y un botón de la barra de herramientas — pantalla completa y no maximizar porque todos los compositores la respetan, _NET_WM_STATE_FULLSCREEN en X11 y xdg_toplevel.set_fullscreen en Wayland, incluidos los gestores de ventanas en mosaico que ignoran de plano una petición de maximizar. El botón existe porque algunos compositores se quedan F11 antes.
Qué sigue sin conectar
Sin composición IME: is_composing está hardcodeado a false y no hay ningún camino de preedición, así que quien escriba en japonés, chino o coreano no puede usar este navegador. Sin arrastrar y soltar sobre la página, sin táctil, sin mando.
Los botones del ratón se quedan en izquierdo, derecho y central; los botones de navegación de GPUI — los laterales que significan atrás y adelante — se reconocen y luego se descartan, a la espera de decidir si son de la página o del shell. Los eventos de rueda se envían en modo píxel, y cuando GPUI informa de un scroll en líneas el shell multiplica por 16 como sustituto de una altura de línea que no lee de ninguna parte.
Y todos los atajos que el shell reclama, la página no los verá nunca. Cmd/Ctrl+T, W, L y R, Ctrl+Tab, Ctrl+Shift+D, F11 y F12 salen antes de que se ejecute el código de reenvío, y todas las demás teclas de función las descarta map_key por falta de un mapeo. Una aplicación web que quiera F1 para la ayuda, o Ctrl+W como atajo propio, se queda sin suerte y no tiene forma de pedirlo.
La barra de direcciones tampoco es un campo de texto de verdad. Hacer clic en ella pone el cursor al final y en ningún otro sitio: no puedes hacer clic en medio de una URL, ni arrastrar para seleccionar un trozo.