Presentación en HN: Libmui, una biblioteca de widgets clásicos de macOS para Linux
(github.com/buserror)- Libmui es una biblioteca de UI que replica una parte considerable de la API “Toolbox” de Macintosh Classic; no es una implementación completa, pero ofrece las funciones necesarias para el emulador MII Apple //e y aplicaciones sencillas
- Comenzó como una biblioteca de UI para MII, con el objetivo de ofrecer una UI dispuesta manualmente, sin muchas dependencias y sin menús tipo juego de “flechas + Enter + Escape”
- El renderizado se hace dibujando en un búfer ARGB y luego copiándolo a una textura OpenGL o a un pixmap compartido de X11/XCB; rastrea regiones inválidas para redibujar solo las áreas necesarias
- A diferencia del Macintosh Toolbox original, la API funciona de forma asíncrona y basada en callbacks: cuando cambia el estado, la UI se redibuja cuando lo necesita, y los eventos se entregan por callbacks en lugar de polling
- Incluye funciones de Window, Menu, Control, List, Alert y Standard File, pero no ofrece zooming, resizing, diálogo de guardado, modo oscuro, themes, Wayland, GTK/QT/SDL ni bindings para Rust/Go/Python
Qué crea Libmui
- Libmui es una biblioteca que replica gran parte de la API “Toolbox” de Macintosh Classic
- No es una implementación completa, pero incluye lo necesario para algunas aplicaciones sencillas y para el emulador MII Apple //e
- El punto de partida fue la necesidad de una biblioteca de UI para MII, con pocas dependencias y que no estuviera inclinada hacia la navegación de menús tipo juego
- Primero se probó Nuklear, una UI de modo inmediato, pero se consideró que su aspecto no era el deseado, que tenía grandes limitaciones para personalizarla y que su motor de layout colocaba los elementos en posiciones distintas a las buscadas
- Otra razón fue la experiencia de que las UI de modo inmediato mantienen internamente estado basado en hashes, y las colisiones de hash pueden convertirse en problemas reales de depuración
- El objetivo está más cerca de una UI afinada a mano que de una UI decidida automáticamente por un motor de layout
Renderizado y modelo de funcionamiento
- Libmui dibuja la UI en una “screen” compuesta por un búfer ARGB
- En MII, este búfer se superpone como una textura OpenGL
- La demo playground de la carpeta example lo copia a una ventana X11 mediante un pixmap compartido de XCB, y también funciona con X11 remoto
- Como en los sistemas operativos antiguos, rastrea las regiones inválidas para redibujar solo las partes necesarias
- No vuelve a dibujar todo cada vez, por lo que hay muy poco overdraw
- Para dibujar en un framebuffer de 16 bits u otro similar, hay que convertir directamente desde la salida ARGB
- Como solo se convierten las regiones sucias, la carga no es grande
- También sería posible vectorizar el renderizado con vertex buffers u otros métodos, pero se considera que el enfoque actual es lo suficientemente rápido y que no hace falta volver a un comportamiento de redibujar todo como en una UI de modo inmediato
Diferencias con el Macintosh Toolbox original
- El aspecto parte de MacOS 8/9, pero con la sensación de haber quitado los elementos en escala de grises; se eligió el estilo más plano de System 7 porque se consideró que envejeció mejor
- El popup menu se parece más a OS8, y la scrollbar se acerca más a GS/OS
- La gran diferencia de la API es su funcionamiento totalmente asíncrono
- No se puede dibujar en una window o GrafPort con un spinloop en cualquier momento, como en el original
- Cuando se cambia el estado de la UI, la UI se redibuja por sí sola cuando lo necesita
- El manejo de eventos está basado en callbacks
- No se hace polling para saber qué ocurrió en la UI
- Cuando se hace clic en un ítem de menú o se usa un atajo de teclado, se llama al callback de action
- La estructura conceptual es más simple que la original
- Todo es
mui_windowomui_control - windows, menubars y menus son
mui_window - los títulos de menú, ítems de menú, todos los elementos dentro de una window y las líneas separadoras son
mui_control
- Todo es
Managers y controles incluidos
-
Window Manager
- Soporta la creación de ventanas y el dibujo dentro de ellas
- Soporta hasta 15 layers, clipping, comportamiento BringToFront y arrastre de ventanas
- El sistema de coordenadas se limita, de forma similar al original, a dos tipos: screen coordinates y window content coordinates
- Gestiona una lista de rectángulos inválidos para no volver a dibujar toda la ventana cada vez
- zooming y resizing están en TODO
- Las transparent windows no se soportan intencionalmente
- Porque las ventanas se dibujan de arriba hacia abajo para optimizar el clipping
- Para manejar transparencia habría que dibujar de abajo hacia arriba, lo que obligaría a redibujar más contenido
- Sí es posible aplicar alpha blend a toda la UI screen donde se quiera
-
Menu Manager
- Soporta menubar, menus, checkmarks y keyboard shortcuts
- Está diseñado para verse como System 7/8 o GS/OS
- Hay hierarchical menus, pero no son exactamente iguales al original y necesitan mejoras
- La visualización y el scroll de popups muy grandes están en TODO
- El soporte para sticky menus está implementado a medias, pero todavía no encaja bien, así que está desactivado
-
Control Manager
- Soporta buttons, checkboxes, radio buttons, vertical scrollbars, wrapping textboxes, entre otros
- Edit Field está en desarrollo y Slider no está incluido
- El prototipo de text edit control funciona bien para entrada de una línea, pero todavía no encaja para cajas de texto multilínea
-
List Manager
- Actualmente está bastante hardcodeado para mostrar nombres de archivos
- Maneja arrow keys, page up/down y scroll wheel
- Como en el MacOS original, permite encontrar el ítem deseado mediante typeahead
- La función para usar font compression o ellipsis abbreviation cuando el texto del ítem es demasiado largo está en TODO
-
Alerts y Standard File
- Alert ofrece un diálogo común de Cancel + OK
- Más tipos de alert están en TODO
- Standard File ofrece un clásico Open file dialog
- Esta función fue uno de los objetivos principales iniciales de la biblioteca
- Save dialog está en TODO
- Hay un popup adicional que muestra directorios usados recientemente
- Soporta arrow keys, page up/down y búsqueda de archivos con typeahead
-
Resource Manager
- No hay Resource Manager
- Se considera que haría falta una herramienta como ResEdit, por lo que queda fuera del alcance actual
- Existe una idea de formato MessagePack para recursos, pero queda para trabajo posterior
Dependencias y compilación
- La única dependencia externa es libpixman
- libpixman es una biblioteca para procesamiento de píxeles y ofrece funciones de regiones útiles para clipping
- No es tan buena como las regiones de QuickDraw, pero se considera suficiente
- También hay componentes incluidos en el código fuente
- libcg: un pequeño renderer antialiased similar a cairo, compuesto por 2 archivos
- stb_truetype.h: se usa para cargar fuentes TrueType
stb_ttc.h: extensión destb_truetype.hque compone el diccionario de fuentes/glifos, hash table, textura de fuentes, etc.- El código de geometría 2D tiene más de 25 años y también fue incluido en libc3
- La compilación usa un Makefile simple: ejecutar
makeen el directorio raíz - Para compilar tests, demos y samples se necesitan
xcb,xcb-shm,xcb-randr,xkbcommon-x11 - Si se usa el driver binario de Nvidia, para que
mui_shellfuncione hay que agregarOption "AllowSHMPixmaps" "1"enDevicedentro de/etc/X11/xorg.conf
Uso y flujo de desarrollo
- Como punto de partida, se recomienda modificar
mui_shell.cymui_widgets_demo.c ui_mui_shellcargamui_widgets_demo.socomo plugin y, cuando detecta cambios, lo recarga automáticamente- Al modificar
mui_widgets_demo.c, tras la recarga vuelve a ejecutarse, lo que permite crear nuevos diálogos rápidamente - Si se ejecuta
make watchen el directoriolibmui, se recompilan automáticamente la biblioteca ymui_shellcuando hay cambios - Usado junto con el auto save del editor, permite un flujo en el que se compila y ejecuta continuamente mientras se edita
Lo que explícitamente no ofrece
- No hay modo oscuro
- No hay soporte de themes
- No hay transparent windows ni cube effect
- Los sticky menus actualmente no están activados
- No usa
cmake,meson,ninjani autotools - No hay bindings para lenguajes como Rust, Go o Python
- No usa frameworks como GTK o QT
- No usa SDL
- No hay soporte para Wayland
1 comentarios
Opiniones en Hacker News
Relacionado con esto, hay una fuente TrueType de dominio público que replica bastante bien la fuente de sistema Chicago original: https://fontlibrary.org/en/font/chicagoflf
Charcoal, usada desde System 8.x en adelante, es mucho menos conocida y, personalmente, la considero una mejora bastante grande.
Aun así, cambiar la biblioteca a Chicago es en realidad bastante fácil. Además de la réplica mencionada, por ahí circula una versión TTF “plain” de la Chicago original.
Después basta con convertir el TTF a OTF con la herramienta de línea de comandos de FontForge: https://www.macintoshrepository.org/1682-mac-os-7-6-x
fontforge -script -c 'Open($1); Generate($2);' input_font.ttf output_font.otfRealmente genial. Michel la hizo para su emulador de Apple II, y yo la estoy usando, un poco en broma, para reemplazar el frontend de un emulador de Archimedes.
Todavía está en una etapa temprana, pero si yo puedo entender la API, seguro que es una buena API :)
Aun así, le tengo cariño a la interfaz clásica de Mac.
Me gustó el rasterizador de gráficos 2D de doble encabezado que usa este proyecto: https://github.com/xboot/libcg
Siempre sorprende que se pueda crear software potente con tan pocas dependencias.
¿De verdad es tan difícil crear una biblioteca de UI limpia y potente que pueda ser una alternativa a Electron?
Impresionante. ¡Con licencia MIT y escrito en C! Si se le agrega un shim para la API de AppKit, quizá podría competir con GNUstep.
Se ve bien. Ojalá pudiera cambiar toda mi UI de macOS para que se viera así.
Buen proyecto. Me encantaba la antigua UI clásica de Mac.
Todos los ejemplos se ven excelentes, y al ver el código de demo de los widgets parece que también es fácil de usar.
Realmente genial. Me pregunto cuánto esfuerzo haría falta para leer archivos de recursos (.rsrc) del resource fork y construir la UI a partir de ellos.
Si fuera posible, se podría usar ResEdit :-)