Heute werden wir darüber sprechen, wie die grafische Phantom-Benutzeroberfläche angeordnet ist.
(Was ist Phantom OS?
Lesen Sie diese Artikel .)
Genauer gesagt - wie diese grafische Benutzeroberfläche geboren wurde. Das Phantom hatte lange Zeit nur eine grafische Schlussfolgerung - es war fast unmöglich, mit der Maus etwas an das System zu übermitteln.
Jetzt ist es an der Zeit, zumindest einfache Anwendungen zu erstellen, was bedeutet, dass Sie eine Benutzeroberfläche benötigen. Wie auch immer - das System, wir werden ehrlich sein, es sah beängstigend aus. Und das ist jetzt nicht in Mode.
Was war zu Beginn des UI-Projekts verfügbar? Im Prinzip viel.
Tatsächlich gab es Grafiken - einen Grafiktreiber, ein Fenstersubsystem im Nur-Anzeige-Modus, Bitmap-Schriftarten, ein Subsystem von Fensterereignissen (Ereignissen), Fensterfokussteuerung und verwandte Grundelemente.
Nun die Schritte und noch ein bisschen mehr.
Das Videotreiber-Subsystem kann nacheinander die Probe () -Funktion mehrerer Treiber ausführen, Anforderungen von ihnen nach maximaler Auflösung und Farbbitität empfangen und im 2D-Beschleunigermodus arbeiten. Das System benötigt mindestens 24-Bit-Farben. Auf dieser Ebene haben wir einen Framebuffer (einen Bildschirm im Speicher), eine Maus und verschiedene Arten von Bitblt-Grundelementen.
Bitblt-Grundelemente - drei Grundtypen wurden implementiert - vollständiges Kopieren der Grafiken (mit ausgeschnittenen Rechtecken), Kopieren unter Berücksichtigung der binären Transparenz (ein Pixel ist entweder vollständig transparent oder vollständig undurchsichtig) und des Z-Puffers. Das heißt, die Möglichkeit, nur die Pixel auf dem Bildschirm zu kopieren, deren Z-Koordinate größer als die Z-Koordinate eines vorhandenen Pixels ist, um eine teilweise Überlappung von Fenstern zu ermitteln.
Die nächste Funktionsebene ist das Fenstersubsystem. Hier gibt es das Konzept eines Fensters, eine Fensterdekoration (Rahmen, Titelfenster mit Schaltflächen), x / y / z-Koordinaten von Fenstern und eine Reihe von Funktionen, die für das Zeichnen von Fenstern auf dem Bildschirm und die Steuerung ihrer Bewegung entlang aller Achsen verantwortlich sind.
Die folgenden Ereignisse folgen: Die Mikrotask-Warteschlange, die vom untergeordneten Treiber zum Rendern und Verwalten des Windows-Status verarbeitet wird.
Es sollte beachtet werden, dass die besten Köpfe der Menschheit behaupten, dass ein Fenster-Grafik-System, das in einer mutithreaded-Umgebung stabil und ohne Probleme funktionieren würde, nicht ohne eine Warteschlange von Ereignissen geschrieben werden kann. Meine bescheidenen Versuche, diese Aussage zu ignorieren, haben sie bisher nur bestätigt. Es ist sehr schwierig, auf die Nachrichtenwarteschlange zu verzichten und alle Threads dazu zu bringen, Fensterereignisse für das Programm anzufordern, und führt manchmal zu einem Krieg auf dem Bildschirm.
Daher werden die meisten Grundelemente des Fenstersystems, die sich auf etwas beziehen, das größer als das Bild innerhalb des Fensters ist, über eine Nachrichtenwarteschlange implementiert. Die Anforderung sendet die Nachricht "Diesen Bereich auf dem Bildschirm zeichnen" oder "Fenster über anderen neu anordnen" an die Warteschlange, und ein separater Thread unten führt sie ordnungsgemäß und nachdenklich aus.
Der Ereignisstrom wird einfach von der Maus (gedrückt, gezogen), der Tastatur (gedrückt, losgelassen) und dem Fenstersystem selbst (sekundäre Ereignisse - nach dem Verschieben des Fensters den Bildschirmbereich neu zeichnen) abgerufen.
Eine separate Aufgabe auf der Ebene des Ereignisflusses ist der sogenannte Fokus. Ein fokussiertes Fenster empfängt einen Strom von Ereignissen von der Tastatur und wird auf dem Bildschirm deutlich hervorgehoben, um die Benutzeraktivität zu adressieren. Neben der offensichtlichen Aufgabe, ein Fenster für die Steuerung des Ereignisses auszuwählen, informiert dieses System das Fenster auch über den Fokusverlust, der manchmal wichtig ist.
Die nächste Ebene sind die grafischen Grundelemente zum Zeichnen im Fenster.
Es gibt zwei Hauptoptionen für die Implementierung. Alt, sparsam - wenn in einem Fenster keine Kopie dessen gespeichert ist, was hineingezogen wird. Wenn ein solches Fenster gelöscht wird und Sie das gelöschte Fenster erneut zeichnen müssen (z. B. wurde das Fenster vom Rand auf den Bildschirm zurückgesetzt), ruft das Fenster die Funktion aus seinem Programm auf, und diese Funktion sollte alles zeichnen, was benötigt wird. Dies ist ein typisches Modell und aus verschiedenen Gründen furchtbar unpraktisch. Die zweite Option wird in Phantom ausgewählt. Jedes Fenster verfügt über eine Bitmap, in der der Inhalt des Fensters aktuell gezeichnet wird. Das Grafiksystem kann jederzeit auf diese Kopie verweisen und sie auf dem Bildschirm aktualisieren, ohne das Benutzerprogramm aufzurufen.
Beachten Sie, dass das Fenster, das zum Benutzerprogramm (und nicht zum Kernel) in Phantom gehört, natürlich persistent ist, im permanenten Speicher gespeichert wird und nach dem Neustart des Betriebssystems alles speichert, was darin gezeichnet ist. Dies ist übrigens überraschend nützlich und vereinfacht den Anwendungscode an einigen Stellen zu unanständig.
Mit einer Reihe von Zeichnungsprimitiven kann der Anwendungscode wie gewohnt einen Punkt, eine Linie, eine Bitmap, eine Textzeile in einer Bitmap-Schriftart und einige andere Kleinigkeiten im Fenster zeichnen.
Über diesen Reichtum des Grafik-Subsystems zu Beginn des Projekts "New Phantom UI" und endete. Im Prinzip war dieses Gentleman-Kit für viel genug, aber nur für den Benutzer. Keine Eingabe.
Genauer gesagt gab es eine rudimentäre Unterstützung für das Konzept der „Schaltfläche“, jedoch nur mit der Maus, nur in der Symbolleiste und nur zum Schließen des Fensters. :) :)
Die Entwicklungsaufgabe war wie folgt:
- TrueType Ohne das ist es eine Schande.
- Tastaturereignisse und Tastatursteuerungen. Zumindest grundlegend.
- Über die Lokalisierung von Layouts nachdenken - zumindest über das kyrillische Alphabet, aber den Grundstein für das Ändern von Layouts legen.
- Steuerelemente - Schaltflächen, Optionsfelder, Textfelder, Beschriftungen, Menüs usw.
- Der Schwerpunkt der Steuerung liegt auf der Auswahl eines Kontrollpunkts innerhalb des Fensters.
- Eine Art Bildschirmkomponente zum Verwalten von Fenstern auf dem Bildschirm. Taskleiste?
- Eigentlich sollten die Bilder von Steuerelementen und im Allgemeinen eine Art UI-Design nicht so kollektiv sein wie es war.
Es war so:

Unterwegs stellte sich heraus, dass beim Überlagern von Bildern auch eine Alpha-Überblendung erforderlich war, dh eine teilweise Transparenz der Pixel. Nun, es wurde klar, dass es Zeit war, Unicode für das Euter zu berühren.
Die Herangehensweise an dieses Gewicht ist in drei große Teile unterteilt: Design, Trump, der Rest.
Kurz gesagt, über Design: Es gibt kostenlose UI-Designs im Internet ohne böse Nutzungsanforderungen. Drei Tage für die Suche und Auswahl, endlose Zeit für das künstlerische Schneiden grafischer Elemente.
Trumpfart
Ich hatte Angst davor, aber wie sich herausstellte, vergebens. Es gibt libfreetype, es gibt Anwendungsbeispiele, nach zwei Tagen funktionierte das Rendern von Vektorschriftarten im Testmodus recht gut.
Es gibt jedoch Feinheiten, und es wurde nicht der gesamte Weg abgedeckt. Nämlich. Arbeiten Sie mit Schriftarten aus dem Kernel - ist. Die Schriftarten werden dann vom Hardcode in die Kernel-Binärdatei gesteuert. Dies ist für eine Systemschriftart unvermeidlich, aber der Benutzercode muss über eigene Lademechanismen verfügen. Und obwohl einige FS im Phantom natürlich sind und sein werden, ist dieses Modell für ihn unnatürlich. Sie müssen in der Lage sein, Schriftarten in persistenten Objekten zu speichern und über das Netzwerk abzurufen.
Die zweite ist einfacher: Es gibt viele Rookeries mit kostenlosen Schriftarten, und ihre Organisation wird nicht lange dauern.
Aber der erste ...
Sie wissen es wahrscheinlich nicht, aber Zeichenfolgenvariablen im Phantom besitzen unerwartete Eigenschaften für Programmierer, die nicht an Persistenz gewöhnt sind. Sie können Dateien ersetzen. Ein Byte-Stream ist ein Byte-Stream. Nicht nur das, es ist per Definition auch Speicher zugeordnet - es ist eine Variable. Das ist im Prinzip das, was wir in einem normalen Betriebssystem in einer Datei speichern. In Phantom können Sie es einfach in eine Zeichenfolgenvariable einfügen. Ich handle so oft - und der Phantom-Compiler hat sogar ein Design -, um eine Datei in eine String-Konstante zu saugen. So dringen im Userland Phantome beispielsweise in Bitmaps ein. Dies ist aber auch eine beschämende Methode, da diese Variable zur Laufzeit analysiert werden muss, um eine funktionsfähige Darstellung des Objekts zu erhalten. Was die Bitmaps betrifft, so ist hier zu Ehren des Phantom-Konzepts alles in Ordnung. Wir kompilieren die Grafikdatei beim Kompilieren in eine Zeichenfolge. Beim ersten Start von Phantom wird sie in ein dauerhaftes Binärobjekt vom Typ Bitmep konvertiert. Sie wird bereits später nach einer beliebigen Anzahl von Neustarts des Betriebssystems verwendet und erfordert nicht die ursprüngliche Quelle. Es sollte auch mit Schriftarten gemacht werden, aber es ist etwas weniger üblich. Während der Arbeit wird die Vektorschrift in ein Raster gerendert, und es müssten nur solche gerenderten Raster gespeichert werden. Dies ist kein Trick oder Problem - sie können wieder in Phantomobjekte wie Bitmap gefaltet werden, aber es ist bereits eine Art Infrastruktur erforderlich - eine UTF-Code-Glyphen-Bitmap (Fitch-Drawing Style-Style-Style-Size-Glyph).
Dies ist nicht so schwierig, aber anscheinend die Aufgabe der nächsten Stufe. Während die Schriftarten auf Berufung gerastert werden.
Unicode
Das Rendern von Schriftarten per Definition umfasst das Arbeiten mit Unicode. Das ist natürlich gut, denn irgendwann musste man anfangen. Tatsächlich reichte es aus, den Renderer mit einem Konverter von UTF-8 nach UTF-32 auszustatten (und dies ist die Glyphennummer in der Schriftart), Schriftarten mit dem kyrillischen Alphabet herunterzuladen und dieser Teil der Lokalisierung funktionierte. Wenn wir andere Sprachen wollen, ist es außerdem notwendig und ausreichend, die Schriftart zu ersetzen. Die ausgewählte Basisschrift enthält jedoch viel - für Europa definitiv genug. China wird einen Schriftersatz brauchen, ja.
Arbeiten Sie mit der Tastatur
Es gab überhaupt keine Anzeichen von Militäraktionen, aber mehr als nur Hoffnungen musste ich kämpfen. Es stellte sich heraus, dass der alte Tastaturtreiber immer noch ... darauf hofft, Hardware vom IBM PC XT zu sehen. Ja, letztes Jahrhundert. Tatsache ist, dass der Tastatur-Controller in der Lage ist (war!), Scan-Codes moderner Tastaturen (den sogenannten zweiten Satz von Codes) in diesen alten umzuwandeln.
Es stellte sich heraus, dass aufgrund der späten QEMU eine solche Konvertierung anscheinend endgültig verworfen wurde. Oder versehentlich kaputt gegangen. Tatsache ist jedoch, dass der Fahrer sich weigerte zu arbeiten. Mit Trauer portierte ich eine Stunde lang mit Hilfe einer Mutter den Fahrer von einem freundlichen UOS nach Phantom. Nur um herauszufinden, dass er das gleiche Problem hat. Der erste Satz. Ich musste die Tabelle der Scan-Codes und den Parser neu schreiben. Ich bin nicht zum alten Fahrer zurückgekehrt, und deshalb. Es stellte sich heraus, dass der Treiber von uOS eine elegantere Schnittstelle zum System hat. Er kehrt nämlich nicht wie üblich zu einem Paar (Zeichencode, Button-Scan-Code) zurück, sondern zu einem 32-Bit-UTF-32-Zeichen. Es stellt sich heraus, dass in UTF ein spezieller Codebereich für die lokale Verwendung zugewiesen ist, der für alle möglichen Funktionstasten mehr als ausreichend ist. Das Arbeiten mit einem solchen Strom von Ereignissen im UI-Code ist viel einfacher.
Darüber hinaus fiel die Lokalisierung perfekt auf ein solches Modell. Es reicht aus, die ASCII-> UTF32-Tabelle für die gewünschte Sprache (Zeichensatz) zu überlagern, und Prost - wir haben Kyrillisch. Na ja - fast da. Jetzt wäre es entweder notwendig, es in UTF-8 zu transkodieren oder die Innereien einiger Teile der Benutzeroberfläche in UTF-32 umzuwandeln. Auch ich habe diesen Moment als niedrige Priorität eingestuft.
Kontrollen
Schaltflächen, Radio, Kontrollkästchen und andere spezifische Elemente der Benutzeroberfläche.
Die gemeinsame Infrastruktur umfasst:
- Der Mechanismus zum Speichern von Steuerelementen in Bezug auf das Fenster
- Typische Steuerungsvisualisierungselemente - Rahmen, Hintergrund, Text, Symbol usw.
- Übertragung zur Ereignissteuerung und typische Reaktionsmuster (Push / Toggle)
- Verfolgen von Mausereignissen und Schwebezustand
- Rückrufe und die Generierung von sekundären Ereignissen, um über Statusänderungen zu informieren
Fokussteuerung
Damit das Steuerelement (z. B. die Taste) ohne Maus verwendet werden kann, sind mehrere Dinge erforderlich.
- Die Möglichkeit, es über die Tastatur auszuwählen
- Zeigen Sie diese Auswahl an
- Tastaturantwort
- Fokusverlusterkennung.
Letzteres ist am schwierigsten.
Tatsächlich fokussiert das Steuerelement sowohl auf die Tastatur als auch auf die Maus, und dies ist ein und dieselbe Entität. Wenn wir das Textfeld mit der Maus auswählen, reagiert es auf die Tasten. Wenn Sie danach die Tabulatortaste drücken, wechselt das Recht, mit der Tastatur zu arbeiten, zu einem anderen.
Eine separate Aufgabe besteht darin, dass einige Steuerelemente gruppiert werden können und ihr Status auf verwandte Weise aktualisiert werden muss. Durch Drücken eines Radiobathorns werden die Nachbarn der Gruppe „gequetscht“.
Noch einmal zurück zu der Tatsache, dass wir ein beständiges Betriebssystem schreiben. Dies bedeutet, dass die potenzielle Steuerung im permanenten RAM gespeichert werden kann und den Neustart des Systemkerns überlebt.
Das heißt, seine Verbindung mit dem Kern wäre gut zu minimieren. Jeder Zeiger auf nicht persistenten Speicher (tatsächlich auf den Kernel) nach einem Neustart ist ungültig und muss wiederhergestellt werden. Dies bedeutet, dass ein solcher Zeiger nicht das Recht hat, Informationen über den Status des Steuerelements zu speichern. Die Cursorposition als Ganzzahl - ja. Ein Zeiger auf einen Puffer im Kernel, dessen Position durch die Cursorposition bestimmt wird, ist dies nicht. Nun oder ja, es ist nur noch eine ganze Zahl vorhanden und es ist wichtiger. Dies ist in der Praxis nicht sehr belastend, aber wir müssen uns daran erinnern.
Zum Schluss die Taskleiste. Dies ist so etwas am unteren Rand (Seite, oben) des Bildschirms, wo der Benutzer stößt, wenn er das Fenster verloren hat.
Im Prinzip sollte dies bereits Teil des Benutzerlandes sein, aber ... der Kernel nutzt die GUI bereits aktiv, sodass dieser Teil vorerst auch ganz unten steht. Ich hoffe vorübergehend.
Insgesamt

Meiner Meinung nach wurden die in diese Richtung gestellten Aufgaben im Allgemeinen gelöst. Natürlich sind der Perfektion keine Grenzen gesetzt, aber wie mir scheint, hat die Benutzeroberfläche einen spürbaren Schritt vom Hacker zum Universal gemacht.