2 Punkte von GN⁺ 2024-04-02 | 1 Kommentare | Auf WhatsApp teilen
  • Libmui ist eine UI-Bibliothek, die große Teile der Macintosh-Classic-„Toolbox“-API nachbildet; sie ist keine vollständige Implementierung, bietet aber die Funktionen, die der MII Apple //e-Emulator und einfache Anwendungen benötigen.
  • Sie begann als UI-Bibliothek für MII und zielt auf eine manuell platzierte UI statt auf viele Abhängigkeiten oder spielartige Menüs nach dem Muster „Pfeiltasten + Return + Escape“.
  • Das Rendering erfolgt in einen ARGB-Puffer, der anschließend in eine OpenGL-Textur oder ein X11/XCB-Shared-Pixmap kopiert wird; dabei werden Invalid-Regions verfolgt, sodass nur benötigte Bereiche neu gezeichnet werden.
  • Anders als die ursprüngliche Macintosh Toolbox arbeitet die API asynchron und callback-basiert; bei Zustandsänderungen wird die UI bei Bedarf neu gezeichnet, und Ereignisse werden per Callback statt per Polling übergeben.
  • Vorhanden sind Funktionen für Window, Menu, Control, List, Alert und Standard File, aber es gibt kein Zooming, kein Resizing, keinen Save-Dialog, keinen Dark Mode, keine Themes, kein Wayland, kein GTK/QT/SDL und keine Bindings für Rust/Go/Python.

Was Libmui bietet

  • Libmui ist eine Bibliothek, die große Teile der Macintosh-Classic-„Toolbox“-API nachbildet.
  • Sie ist keine vollständige Implementierung, enthält aber die Teile, die für einige einfache Anwendungen und den MII Apple //e emulator benötigt werden.
  • Ausgangspunkt war der Bedarf an einer UI-Bibliothek für MII, mit möglichst wenigen Abhängigkeiten und einer UI, die nicht auf spielartige Menübedienung ausgerichtet ist.
  • Zunächst wurde eine Nuklear-Immediate-Mode-UI ausprobiert, doch das Aussehen gefiel nicht, die Möglichkeiten zur Anpassung waren stark begrenzt, und die Layout-Engine platzierte Elemente nicht dort, wo sie gewünscht waren.
  • Hinzu kam die Erfahrung, dass Immediate-Mode-UIs intern hashbasierten Zustand verwalten und Hash-Kollisionen in der Praxis zu echten Debugging-Problemen führen können.
  • Das Ziel liegt näher bei einer direkt ausgearbeiteten UI als bei einer UI, die von einer Layout-Engine bestimmt wird.

Rendering- und Laufzeitmodell

  • Libmui zeichnet die UI auf einen „screen“ aus ARGB-Puffer.
    • In MII wird dieser Puffer als Overlay in eine OpenGL-Textur gelegt.
    • Die Playground-Demo im example-Ordner kopiert ihn als XCB-Shared-Pixmap in ein X11-Fenster und funktioniert auch über Remote-X11.
  • Wie in älteren Betriebssystemen werden Invalid-Regions verfolgt, sodass nur nötige Teile neu gezeichnet werden.
    • Es wird nicht jedes Mal alles neu gezeichnet, daher gibt es sehr wenig Overdraw.
    • Für die Ausgabe auf etwa 16-Bit-Framebuffer muss direkt aus der ARGB-Ausgabe konvertiert werden.
    • Da nur Dirty-Regions konvertiert werden müssen, hält sich der Aufwand in Grenzen.
  • Das Rendering ließe sich zwar in Vertex-Buffer und Ähnliches vektorisieren, aber der aktuelle Ansatz ist schnell genug, und es besteht keine Notwendigkeit, wieder zu einem Verhalten zurückzukehren, bei dem wie bei Immediate-Mode-UIs immer alles neu gezeichnet wird.

Unterschiede zur ursprünglichen Macintosh Toolbox

  • Das Erscheinungsbild begann bei MacOS 8/9, wirkt aber wie eine Version ohne Graustufen-Elemente; gewählt wurde letztlich eher die flachere Optik von System 7, die besser gealtert ist.
  • Popup-Menüs sind näher an OS8, Scrollbars eher näher an GS/OS.
  • Der große API-Unterschied ist der vollständig asynchrone Betrieb.
    • Anders als im Original kann nicht jederzeit per Spinloop direkt in ein Window oder einen GrafPort gezeichnet werden.
    • Wenn sich der UI-Zustand ändert, zeichnet sich die UI bei Bedarf selbst neu.
  • Die Ereignisverarbeitung ist callback-basiert.
    • Es wird nicht gepollt, was in der UI passiert ist.
    • Wenn ein Menüpunkt angeklickt oder ein Tastaturkürzel ausgelöst wird, wird ein Action-Callback aufgerufen.
  • Auch die konzeptuelle Struktur ist einfacher als im Original.
    • Alles ist entweder mui_window oder mui_control.
    • Windows, Menubars und Menüs sind mui_window.
    • Menütitel, Menüeinträge, alle Elemente innerhalb eines Fensters und Trennlinien sind mui_control.

Vorhandene Manager und Controls

  • Window Manager

    • Unterstützt das Erzeugen von Fenstern und das Zeichnen innerhalb von Fenstern.
    • Unterstützt bis zu 15 Layer, Clipping, BringToFront-Verhalten und das Ziehen von Fenstern.
    • Das Koordinatensystem ist wie im Original auf zwei Systeme beschränkt: Screen-Koordinaten und Window-Content-Koordinaten.
    • Verwaltet Listen ungültiger Rechtecke, damit nicht jedes Mal das gesamte Fenster neu gezeichnet werden muss.
    • Zooming und Resizing sind TODO.
    • Transparente Fenster werden bewusst nicht unterstützt.
      • Fenster werden von oben nach unten gezeichnet, um das Clipping zu optimieren.
      • Für Transparenz müsste von unten nach oben gezeichnet werden, wodurch mehr Inhalte neu gezeichnet werden müssten.
      • Es ist jedoch möglich, den gesamten UI-Screen an beliebiger Stelle per Alpha-Blending zu mischen.
  • Menu Manager

    • Unterstützt Menubar, Menüs, Häkchen und Tastaturkürzel.
    • Ist so gestaltet, dass es wie System 7/8 oder GS/OS aussieht.
    • Hierarchische Menüs sind vorhanden, entsprechen aber nicht vollständig dem Original und brauchen Verbesserungen.
    • Anzeige und Scrollen sehr großer Popups sind TODO.
    • Unterstützung für Sticky Menus ist halb implementiert, aber noch nicht korrekt und daher deaktiviert.
  • Control Manager

    • Unterstützt Buttons, Checkboxen, Radio Buttons, vertikale Scrollbars und Textboxen mit Zeilenumbruch.
    • Edit Field ist in Arbeit, Slider fehlt.
    • Ein Prototyp für ein Text-Edit-Control ist für einzeilige Eingaben brauchbar, passt aber noch nicht für mehrzeilige Textboxen.
  • List Manager

    • Ist derzeit weitgehend hart auf die Anzeige von Dateinamen zugeschnitten.
    • Verarbeitet Pfeiltasten, Page Up/Down und das Scrollrad.
    • Wie im ursprünglichen MacOS lassen sich gewünschte Einträge per Typeahead finden.
    • Funktionen wie Font Compression oder Ellipsis-Abkürzung für zu lange Eintragstexte sind TODO.
  • Alerts und Standard File

    • Alert bietet den üblichen Cancel + OK-Dialog.
    • Weitere Alert-Typen sind TODO.
    • Standard File bietet einen klassischen Open-File-Dialog.
      • Diese Funktion war eines der frühen Hauptziele der Bibliothek.
      • Ein Save-Dialog ist TODO.
      • Es gibt ein zusätzliches Popup, das zuletzt verwendete Verzeichnisse anzeigt.
      • Unterstützt Pfeiltasten, Page Up/Down und Typeahead-Dateisuche.
  • Resource Manager

    • Es gibt keinen Resource Manager.
    • Dafür wäre nach Einschätzung des Projekts ein Werkzeug wie ResEdit nötig, daher ist es derzeit nicht Teil des Umfangs.
    • Es gibt eine Idee für ein MessagePack-Format für Ressourcen, aber das ist späterer Arbeit vorbehalten.

Abhängigkeiten und Build

  • Die einzige externe Abhängigkeit ist libpixman.
    • libpixman ist eine Bibliothek für Pixelverarbeitung und bietet nützliche Region-Funktionen für Clipping.
    • Sie ist nicht so gut wie die Regions von QuickDraw, wird aber als ausreichend angesehen.
  • Einige Komponenten sind auch im Quellcode enthalten.
    • libcg: ein kleiner antialiasender Renderer ähnlich wie cairo, bestehend aus zwei Dateien
    • stb_truetype.h: wird zum Laden von TrueType-Schriften verwendet
    • stb_ttc.h: eine Erweiterung von stb_truetype.h, die Font-/Glyph-Wörterbuch, Hash-Tabelle, Font-Texturen usw. aufbaut
    • Der 2D-Geometrie-Code ist über 25 Jahre alt und ist auch in libc3 enthalten.
  • Der Build erfolgt über ein einfaches Makefile, indem im Root-Verzeichnis make ausgeführt wird.
  • Für den Build von tests, demos und samples werden xcb, xcb-shm, xcb-randr und xkbcommon-x11 benötigt.
  • Bei Verwendung des proprietären Nvidia-Treibers muss für mui_shell in /etc/X11/xorg.conf im Abschnitt Device die Option Option "AllowSHMPixmaps" "1" hinzugefügt werden.

Nutzung und Entwicklungs-Workflow

  • Als Einstieg wird empfohlen, mui_shell.c und mui_widgets_demo.c zu bearbeiten.
  • ui_mui_shell lädt mui_widgets_demo.so als Plugin und lädt es bei erkannten Änderungen automatisch neu.
  • Wenn mui_widgets_demo.c geändert wird, wird es nach dem Reload erneut ausgeführt, sodass sich neue Dialoge schnell erstellen lassen.
  • Wird im libmui-Verzeichnis make watch ausgeführt, werden die Bibliothek und mui_shell bei Änderungen automatisch neu gebaut.
  • In Kombination mit Auto-Save im Editor ergibt sich ein Workflow, bei dem beim Bearbeiten fortlaufend gebaut und ausgeführt wird.

Was ausdrücklich nicht angeboten wird

  • kein Dark Mode
  • keine Theme-Unterstützung
  • keine transparenten Fenster und kein Cube-Effekt
  • Sticky Menus sind derzeit nicht aktiviert
  • verwendet kein cmake, meson, ninja oder autotools
  • keine Sprach-Bindings für Rust, Go oder Python
  • verwendet keine Frameworks wie GTK oder QT
  • verwendet kein SDL
  • keine Wayland-Unterstützung

1 Kommentare

 
GN⁺ 2024-04-02
Hacker-News-Kommentare
  • Dazu passend gibt es eine Public-Domain-TrueType-Schrift, die die ursprüngliche Chicago-Systemschrift ziemlich gut nachbildet: https://fontlibrary.org/en/font/chicagoflf

    • Ich habe überlegt, Chicago als Standardschrift zu verwenden, aber sie ist zu stark mit einem festen Bild verbunden.
      Charcoal, das ab System 8.x verwendet wurde, ist viel weniger bekannt, und ich halte es persönlich für eine ziemlich große Verbesserung.
      Trotzdem lässt sich die Bibliothek tatsächlich recht einfach auf Chicago umstellen. Neben der erwähnten Nachbildung kursiert irgendwo auch noch eine TTF-Version des originalen Chicago in der Variante „plain“.
    • In Bezug auf die Aussage, dass es eine hervorragende Nachbildung der „ursprünglichen Chicago-Systemschrift“ gibt: Über den Download von System 7.6.1 kann man auch das echte TrueType Chicago bekommen, das von Bigelow & Holmes entworfen wurde.
      Danach muss man die TTF nur noch mit dem FontForge-Kommandozeilenwerkzeug in OTF umwandeln: https://www.macintoshrepository.org/1682-mac-os-7-6-x
      fontforge -script -c 'Open($1); Generate($2);' input_font.ttf output_font.otf
    • Selbst aktuelles macOS enthält noch die thailändische Schrift Silom, die für ihre lateinischen Glyphen Chicago verwendet.
  • Wirklich cool. Michel hat das für seinen Apple-II-Emulator gebaut, und ich verwende es halb im Scherz, um das Frontend eines Archimedes-Emulators zu ersetzen.
    Es ist noch früh, aber wenn ich die API verstehen kann, muss es wohl eine gute API sein :)

    • Es selbst für einen Apple-II-Emulator zu verwenden, wirkt schon ein bisschen wie ein Witz. Apple-II-Nutzer wollten damals, dass Apple die Apple-II-Reihe weiterführt, hielten sie aber für faktisch aufgegeben, als Apple sich auf den Mac konzentrierte, und mochten den Mac deshalb nicht besonders.
      Trotzdem habe ich eine Schwäche für das klassische Mac-Interface.
  • Mir gefiel der 2D-Grafik-Rasterizer mit dem Dual-Header-Ansatz, den dieses Projekt verwendet: https://github.com/xboot/libcg
    Es überrascht mich immer wieder, dass sich mit so minimalen Abhängigkeiten dennoch leistungsfähige Software bauen lässt.
    Ist es wirklich so schwer, eine saubere und mächtige UI-Bibliothek zu bauen, die eine Alternative zu Electron sein kann?

    • Nein. Aber wofür sollte man sonst den überschüssigen Speicher und die Performance verwenden?
  • Großartig. MIT-lizenziert und in C geschrieben! Mit einem Shim für die AppKit-API könnte es vielleicht sogar mit GNUstep konkurrieren.

    • Oder man verpasst GNUstep einfach ein Theme. Solche Themes werden unterstützt.
  • Sieht gut aus. Ich wünschte, ich könnte meine gesamte macOS-Oberfläche so umstellen.

    • Wenn man in den Bedienungshilfen den Hochkontrastmodus aktiviert, sieht es fast genauso aus.
  • Gutes Projekt. Ich mochte die klassische Mac-Benutzeroberfläche früher wirklich sehr.
    Die Beispiele sehen alle hervorragend aus, und wenn man sich den Widget-Demo-Code ansieht, wirkt es auch einfach zu benutzen.

  • Wirklich schön. Ich frage mich, wie viel Aufwand es wäre, resource fork-Ressourcendateien (.rsrc) einzulesen und daraus die UI zu bauen.
    Falls das möglich ist, könnte man ResEdit verwenden :-)