1 puntos por GN⁺ 2025-08-16 | 1 comentarios | Compartir por WhatsApp
  • El equipo de Ghostty reescribió por completo la aplicación GTK y aprovechó activamente el sistema de tipos GObject
  • En este proceso, la integración con el lenguaje Zig y la verificación de problemas de memoria con Valgrind jugaron un papel importante
  • Al adoptar el sistema GObject, se simplificaron más que antes la gestión de memoria y la implementación de widgets personalizados
  • Al usar Valgrind, comprobaron en la práctica que la seguridad de memoria de Ghostty mejoró considerablemente
  • El nuevo Ghostty GTK ya se convirtió en la opción predeterminada para compilar desde el código fuente y está previsto que se incluya en la versión 1.2

Introducción

  • Ghostty es un emulador de terminal multiplataforma compatible con macOS, Linux y FreeBSD
  • Se distingue por usar frameworks GUI nativos en cada plataforma
    • macOS: aplicación de gran escala basada en Swift y Xcode
    • Linux y BSD: aplicación basada en GTK, con integración directa con X11/Wayland, etc.
    • El núcleo compartido está escrito en Zig y ofrece una API compatible con C ABI
  • Para conocer el motivo de la reescritura de la aplicación GTK dentro de la estructura anterior, puede consultarse el PR original
  • Este texto se centra especialmente en la integración con el sistema de tipos GObject y en los problemas de memoria verificados con Valgrind

El sistema de tipos GObject y Zig

  • Al usar GTK, la estructura exige interactuar de forma predeterminada con el sistema de tipos GObject
  • En el pasado intentaron evitar el sistema GObject y hacer coincidir manualmente el ciclo de vida entre objetos Zig sin conteo de referencias y objetos GObject, pero una y otra vez surgieron problemas de liberación incorrecta de memoria
    • Ejemplo: la memoria del lado de Zig ya se había liberado, pero la del lado de GTK seguía viva, o ocurría la situación contraria
  • Este enfoque no solo generaba problemas de corrección, sino que también dificultaba usar funciones propias de GTK (señales de eventos, binding de propiedades, acciones)
  • Un ejemplo concreto fue que, al recargar la estructura de configuración (config), todos los elementos GUI conectados debían actualizarse de forma consistente, pero ese proceso era complejo y propenso a errores
    • Ahora se gestiona mediante un GhosttyConfig GObject con conteo de referencias que envuelve la estructura Config de Zig, y las notificaciones de cambio de propiedades propagan los cambios de manera natural por toda la aplicación
  • También se volvió más fácil crear widgets GObject personalizados, lo que permite usar tecnologías modernas de UI para GTK como Blueprint
    • Recientemente, con la adopción de Blueprint, resultó más sencillo introducir nuevas funciones como pestañas en la barra de título de GTK y bordes animados para la campana

Valgrind, GTK y Zig

  • Durante todo el proceso de desarrollo se validaron de forma sistemática, con Valgrind, problemas como fugas de memoria y accesos a memoria no definida
  • Revisar una aplicación GTK con Valgrind es complicado, y requiere un gran volumen de archivos de suppressions (80% corresponde al propio GTK, y el resto a librerías de terceros y drivers GPU)
  • Gracias a las verificaciones repetidas, fue posible descubrir de antemano errores de memoria complejos que solo ocurrían en ciertos casos
    • Ejemplo: si no se inicializa correctamente un WeakRef de GObject, cuando el objeto objetivo se libera más adelante puede producirse un acceso a memoria no definida; Valgrind permitió detectarlo antes
  • En la experiencia real, los problemas dentro del codebase de Zig fueron solo 2 en total (1 fuga, 1 acceso no definido), y aun esos ocurrieron durante la integración con APIs C de terceros
    • También quedó demostrada la efectividad del asignador de depuración de Zig y de sus funciones de integración con Valgrind
  • La mayoría de los demás problemas de memoria detectados provenían de los límites con APIs C y de la compleja gestión del ciclo de vida en el sistema GObject
    • En conclusión, para usar con seguridad la API C de librerías complejas, se necesitan herramientas como Valgrind
  • Las funciones de apoyo a la seguridad de memoria de Zig demostraron su efectividad no solo en lo teórico, sino también a través de experiencia empírica en proyectos reales

Conclusión

  • Esta fue la quinta vez que rehacen desde cero la parte GUI de Ghostty
    • GLFW, macOS SwiftUI, macOS AppKit+SwiftUI, Linux GTK (procedimental), Linux GTK+sistema de tipos GObject
  • En el proceso de reescritura iterativa, cada vez obtuvieron nuevas lecciones y crecimiento técnico
    • Planean aplicar parte de esta experiencia también al proyecto de macOS
  • También destacan la colaboración activa del equipo de mantenimiento del sistema GTK de Ghostty
  • La aplicación Ghostty GTK recién reescrita ya se convirtió en la opción predeterminada para compilar desde el código fuente, y se aplicará en la versión estable 1.2

1 comentarios

 
GN⁺ 2025-08-16
Opiniones en Hacker News
  • No tengo experiencia trabajando directamente con GTK, pero por lo que describes, siento que es muy parecido a los problemas que he tenido creando bindings de Godot en Zig. Godot tiene muchísimos conceptos de OOP: clases, métodos virtuales, propiedades, señales, etc. Y ofrece una API en C que maneja todos esos conceptos y te permite crear objetos y propiedades personalizadas. También hay que gestionar manualmente el ciclo de vida de los objetos del motor, y existe una estructura en árbol de objetos con conteo de referencias. Intentar encapsular esos problemas de ciclo de vida en una API óptima y acorde a los modismos de Zig se vuelve extremadamente complejo. Mientras lidiaba con eso, también hice la librería oopz. La API por ahora está en este estado, y un ejemplo real puede verse aquí. También me gustaría intentar hacer el frontend de Ghostty como una extensión de Godot

    • Hace tiempo usé bindings de GTK para un lenguaje directamente, y recuerdo que fue incómodo. El 98% funcionaba bien, pero en el 2% restante había cosas del tipo “si esta función toma una referencia del objeto o no depende de otro argumento”, así que analizar el ciclo de vida de los objetos era bastante doloroso
    • Quiero agradecerte y también comentar que, cuando intenté escribir código de Godot en C# con buen rendimiento, sufrí bastante por todas las conversiones entre tipos del motor, que además provocaban asignaciones repetidas. Me da curiosidad si al hacer los bindings también te topaste con ese tipo de problema
    • No sabía que había un proyecto en marcha para crear bindings de Godot en Zig. Me gustan tanto Godot como Zig, así que me entusiasma bastante. Lo seguiré de cerca
  • Este es un muy buen ejemplo de que programar bien al final significa alinearse con la forma en que el sistema fue diseñado. Da igual lo que uno piense sobre OOP o gestión de memoria: si usas GTK, inevitablemente tienes que diseñar la interfaz de alguna manera alrededor del sistema de tipos GObject. Aunque quieras evitarlo, no se puede esquivar. Nosotros intentamos evitarlo, y el resultado fue un caos enorme al intentar unir los ciclos de vida de objetos con conteo de referencias y objetos sin él. En la app GTK de Ghostty, se repetían bugs donde se liberaba la memoria de Zig pero no la de GTK, o al revés

    • La razón de que GTK tenga esta estructura está en el origen de Vala. Vala nació inspirado en C#, aprovecha GObject y transpila el código a C. Por eso muchas apps de GTK en realidad están escritas en Vala. A veces queda la sensación de que quizá habría sido mejor usar D. En muchos sentidos, D se siente como un C# compilado
    • Rendirse ante un sistema malo no es algo bueno, pero sí una decisión práctica
  • Dejando de lado mi postura sobre OOP y la gestión de memoria, sí estoy de acuerdo en que si usas GTK no te queda otra que enredarte con el sistema de tipos GObject. Por eso decidí directamente no usar GTK. Entiendo el valor de tener un tema visual unificado, pero en mi opinión las ventajas de GTK no compensan el costo que implica usarlo. Por mi experiencia tocando la periferia de apps open source alrededor de GTK, terminé convencido de que la visión de GTK y GObject no encaja bien con mi forma de pensar. No me molesta que GTK exista. Yo elijo no usarlo y con eso estoy bien, pero me parece raro que algunas personas no consideren que esa sea una elección que me corresponde. No es más que una GUI toolkit entre muchas, y aunque técnicamente es una toolkit muy refinada, a veces pienso que si GTK hubiera tenido un poco menos de cuota, todo ese polish podría haberse invertido en otras toolkits estructuralmente mejores. Claro, lo que a mí me parece bueno no necesariamente lo es para todos. Me pregunto cuánta gente usa GTK a regañadientes y cuánta realmente la considera la mejor opción

    • Coincido con eso de que el estilo fuertemente opinionated de GTK y GObject tampoco encaja mucho con mi forma de pensar. También siento que no conecto demasiado con la dirección del ecosistema de Gnome. Usar GTK para Linux en Ghostty es una elección bastante práctica. El objetivo de qué significa ser nativo de plataforma para Ghostty, especialmente en Linux, está definido aquí. GTK es lo más usado en Linux y lo que se integra de forma más natural con la mayoría del ecosistema de aplicaciones, así que era difícil tomar otra decisión. A futuro espero que libghostty permita que terceros creen distintos frontends. Un ejemplo es Wraith, un frontend nativo de Ghostty para Wayland. Está genial
    • Creo que la razón clave de que GTK esté tan extendido en Linux es justamente que tiene “bindings en C”. Eso hace que existan bindings para casi todos los lenguajes, o que al menos sea fácil generarlos automáticamente. En cambio, Qt está demasiado atado a C++ y Python, y eso reduce muchísimo su accesibilidad. Lo importante es encontrarse con el desarrollador donde ya está, sin importar el lenguaje que use. Además, cuando uno desarrolla apps de escritorio complejas, una GUI toolkit imperativa clásica suele ser más práctica, tiene widgets probados y patrones conocidos. Los enfoques recientes suelen exigir construir más cosas desde cero, y cuando el proyecto se vuelve apenas un poco complejo, se hacen bastante duros de manejar
    • Sobre eso de “aunque decidas no usar GTK, hay gente que actúa como si no fuera realmente tu elección”, me da curiosidad qué tipo de objeciones te has encontrado. Desde mi punto de vista, GTK parece destacar justo en cosas que quienes implementan su propia solución suelen descuidar, como accesibilidad o entrada no romana, así que su principal ventaja competitiva me parece estar ahí
  • Un dato curioso: en Ghostty y algunas otras apps GTK, si el mouse sale de la ventana y luego vuelve a entrar, el primer clic de scroll se ignora. Esto pasa por un bug antiquísimo reportado por primera vez en 2015. Enlace al bug. Hasta hoy no hay planes de arreglarlo, y la postura del mantenedor es que hay que esperar a Wayland

    • En realidad, parece que este problema no está en GTK en sí, sino en XInput2. Claro, GTK podría aplicar una solución alternativa con una heurística como hace Chromium, pero en el fondo es un problema de una capa superior, de XInput2
    • Si lees ese reporte de bug y los issues enlazados, queda claro que hubo varios intentos de corregirlo, pero inevitablemente dependían de ciertas heurísticas y terminaban introduciendo efectos secundarios peores que el problema original. Al final, como el origen real está en la base de X11, cualquier mejora significativa depende de una corrección de fondo. Pero como X11 está en la práctica en modo de mantenimiento, es difícil esperar mucho mientras sus fans sigan diciendo “funciona perfectamente, no hace falta tocar nada”. Así que lo único que queda es esperar la transición a Wayland
  • Sobre la parte de “verifiqué cada paso con Valgrind”, la verdad es que aunque parezca obvio, yo nunca lo he hecho y tampoco he visto que muchos otros desarrolladores lo hagan. Normalmente Valgrind se usa solo cuando ya apareció un bug concreto o una degradación de rendimiento. Si durante todo el desarrollo se usaran activamente herramientas como Valgrind, especialmente Memcheck y Helgrind, probablemente la estabilidad mejoraría muchísimo y muchos bugs se atraparían en el momento exacto en que se introducen, en vez de obligarte después a revisar cientos de commits

    • Yo personalmente, cuando trabajo en C o C++, siempre he usado valgrind de forma periódica. Los errores que detectan valgrind y asan casi nunca aparecen como crashes inmediatos, sino como bugs intermitentes y muy molestos, difíciles de encontrar, y en algunos casos incluso como vulnerabilidades de seguridad. Además, cuando pequeñas fugas de memoria se van acumulando, luego es mucho más difícil encontrar la causa de un problema serio porque todo queda enterrado bajo un montón de fugas menores. Por eso vale la pena usarlo de forma proactiva
    • He usado Valgrind, especialmente memcheck, de forma preventiva para eliminar primero problemas fáciles de corregir antes de meterme a depurar a fondo reportes de bugs. El mayor problema es que el costo en rendimiento es muy alto, así que la experiencia de uso interactivo no es muy buena. Aun así, creo que ejecutar los tests bajo Valgrind de vez en cuando da muchísimo valor
    • Eso sí, Valgrind es extremadamente lento y costoso, así que no es fácil meterlo directamente en el ciclo de editar-compilar-probar. Sí puede usarse en ciclos de testing como nightly o automatización, pero integrarlo bien requiere trabajo extra
  • Usando Ghostty, me resulta muy incómodo que en Mac no se pueda pegar varias líneas en nano. Parece depender de cómo el terminal maneja el “bracketed pasting”, pero curiosamente eso no pasa en iTerm2 ni en Term

    • Como reemplazo de terminal, Ghostty me satisface en un 99%, pero los problemas de copiar y pegar son realmente frustrantes y me los encuentro todos los días
    • Desde que empecé a usar Ghostty como terminal por defecto en una computadora nueva, lo que más extraño es la falta de una función de búsqueda. Normalmente uso mucho el atajo para encontrar texto dentro de la salida, y aquí no existe. De hecho, en este issue es de lo que más se habla
    • Al conectarme remotamente a Ubuntu, ni siquiera puedo ejecutar nano dentro de Ghostty
      $ nano
      Error opening terminal: xterm-ghostty.
      
      En el mismo entorno, funciona bien tanto en la terminal de macOS como en la terminal integrada de VS Code
    • Eso sí parece un bug real, así que te recomiendo reportarlo
    • Lo más grave para mí es que no tenga búsqueda de comandos tipo Cmd+F
  • Me pregunto si usar Rust en vez de Zig habría evitado los errores de memoria. Como la mayoría venían de la interacción Zig/C, supongo que Rust habría terminado en una situación parecida. Lo digo especulando desde la perspectiva de un desarrollador Go, pero me da curiosidad si existe algún lenguaje que realmente ofrezca más herramientas de seguridad cuando se integra a gran escala con C

    • Con Rust se habría evitado uno de los problemas, pero el resto habría sido igual. Como mencionaste, todo giraba alrededor del límite de la API en C y de su semántica, así que la seguridad real dependía de la calidad del wrapper. Rust ya tiene un ecosistema bastante maduro de wrappers bien probados, así que por ese lado el riesgo habría sido menor que escribiendo uno propio en Zig, pero en esencia no sería tan distinto. Por ejemplo, el acceso indefinido a memoria que Rust probablemente sí habría detectado fue justo lo que se resolvió en este PR. En la práctica, se copiaba memoria incorrecta en el primer frame, pero como no se usaba ni se enviaba a ningún lado, no era grave. Aun así, claramente no era correcto
    • Incluso Rust necesita gestión manual de memoria y de ciclos de vida en la frontera FFI con C/GObject. El borrow checker de Rust no puede verificar el uso de memoria dentro del código externo
    • Uno de los puntos del artículo es precisamente que la combinación zig + valgrind encontró muchos menos problemas de memoria de los esperados
    • Es mucho más difícil escribir bindings de C con Rust. Así que incluso puede que hacer bindings de GTK en Rust no sea viable
  • Usando apps basadas en GPU como Ghostty, Alacritty, WezTerm o Zed, sí sentí que iban más rápido y mejor. Pero irónicamente eso también hace mucho más evidentes las limitaciones de los drivers de Nvidia. Antes casi no usaba la GPU y no me daba cuenta, pero tanto en entornos sin compositor como Regolith i3wm como en sway/Wayland, los drivers de Nvidia fueron pésimos en cosas como compartir pantalla, volver del modo sleep o los crashes. Probé varias versiones, 550/560/575/580, y todas se comportaban igual. Solo recientemente me di cuenta de lo malos que han sido desde hace tiempo

    • Yo también tuve una experiencia parecida en Wayland. En X11, con los efectos de composición desactivados, tanto una 1050Ti como una tarjeta AMD vieja que necesita el driver radeon funcionaban sin problema. En cambio, en Wayland sí tuve stuttering, crashes y renderizado roto
  • Pude construir una app grande sin que el sistema de tipos de GTK afectara demasiado el código. Pero a cambio, conecté todos los componentes enlazando lambdas, en lugar de usar herencia o extensiones de clases. El resultado no fue tan desordenado, aunque seguramente habría confundido a desarrolladores acostumbrados al estilo tradicional de GTK

  • No entiendo todo el hype exagerado alrededor de Ghostty. Tiene una UI con poco más que pestañas y menú contextual, así que me cuesta ver si realmente vale la pena tanto trabajo de integración y reescritura. Supongo que tal vez quieran terminar construyendo un entorno GUI poderoso como iTerm2. Kitty, por ejemplo, dibuja sus propias pestañas directamente con OpenGL, lo que le permite personalización total, y además se ahorra tiempo integrándose con frameworks complejos, así que puede implementar antes funciones muy prácticas, como envolver la salida del último comando en un pager. Kitty también soporta bien el uso remoto

    • La UI de Ghostty no solo tiene pestañas: también tiene splits, banners de “el proceso terminó”, diálogos de confirmación al cerrar, un diálogo para cambiar el título, detección de pegado inseguro, campana visual animada, terminal desplegable, barras de progreso y más cosas de las que parece. En Mac también tiene integración con Apple Shortcuts y Spotlight. Claro, incluso sin eso podría haberse implementado todo puramente por cuenta propia, sin una GUI toolkit, pero la misión de Ghostty es usar las toolkits nativas de cada plataforma para que la app se sienta “verdaderamente nativa”. Si no te gusta ese enfoque, elegir algo como Kitty con pestañas de texto también es una muy buena opción. Son decisiones que dependen de qué valores y prioridades tengas. Más adelante también planean expandir bastante la GUI e integrar más a fondo funciones nativas de cada plataforma, como sincronización con iCloud
    • Sobre la afirmación de que “Kovid implementó funciones más rápido”, conviene tener cuidado porque este usuario tiene un historial que hace sospechar que podría ser el propio Kovid. Ya lo he visto en HN y Reddit presentando Kitty como si hablara de forma neutral mientras critica al desarrollador. Incluso dejó este enlace para revisar comentarios anteriores
    • Además de la explicación positiva de arriba, yo creo que la aparición de libghostty fue un game changer. Es una implementación de terminal muy potente, tipo WebKit, donde cualquiera puede hacer un drop-in y tenerla funcionando de inmediato
    • Yo también pasé mucho tiempo saltando entre terminales, y aunque Ghostty no es perfecto ni exactamente ideal, terminé cambiándome porque no encontraba nada que me dejara razonablemente satisfecho. Para mí eso ya es suficientemente valioso. En general, no lo sigo usando por una “razón decisiva”, sino justamente porque no tiene problemas grandes, y eso en sí mismo es una ventaja
    • Las fuentes se renderizan mucho más bonito en Ghostty que en Kitty. Neovide se ve todavía mejor, pero todavía no tiene soporte para pestañas y además consume más batería