Inhaltsverzeichnis

Vega – Fensterverwaltungssystem

Vega ist der Compositor und Window Manager von Lyx OS. Er läuft als privilegierter Ring-3-Prozess und übernimmt alle Aufgaben, die klassisch einem Display-Server zufallen: GOP-Framebuffer beanspruchen, Fenster verwalten, Eingaben (Maus, Tastatur) verarbeiten und jeden Frame kompositionieren.

Lyx OS hat keinen Display-Server und kein Socket-Protokoll (kein X11, kein Wayland). Der Kernel stellt den Framebuffer und die Fenstertabelle direkt bereit; Vega liest sie ohne Umweg über IPC.

Übersicht · Kernel-Interna · Syscall-ABI


1. Architektur

vega/
├── vega.lyx        ← Compositor: Frame-Loop, Fensterzustand, Drag-Logik
└── bsys_vega.lyx   ← Bridge-Unit: Syscall-Wrapper, Display-Backend,
                       VGA-Font, Fenster-Dekoration, Maus-Cursor

bsys_vega muss als erstes Unit importiert werden; Init() muss vor allen anderen Aufrufen stehen:

import bsys_vega;

fn main(): int64 {
    Init();             // r3_sc_block binden + VGA-Font initialisieren
    DisplayInit();      // GOP-Framebuffer beanspruchen + Back-Buffer allokieren
    SysSetMouseBounds(scrw - 1, scrh - 1);
    // ...
}


2. Frame-Loop

Der Frame-Loop ist der Kern von Vega. Er läuft mit ~100 Hz (PIT-Tick):

while (1 == 1) {
    SysVsyncWait();                         // auf nächsten PIT-Tick warten (~10 ms)

    var mx:  int64 := SysGetMouseX();
    var my:  int64 := SysGetMouseY();
    var btn: int64 := SysGetMouseBtn();

    handle_drag(mx, my, btn);               // Titelleisten-Drag + Z-Order-Änderung

    var ch: int64 := KbdPoll();             // non-blocking Tastatureingabe
    if (ch != 0) { term_putch(ch); redraw_term_fb(); }

    composite_frame(mx, my);               // Desktop + Fenster + Cursor rendern
    DisplayFlip();                          // Back-Buffer → GOP-Framebuffer schreiben
}


3. Display-Backend

Das Display-Backend abstrahiert den GOP-Framebuffer über einen Double-Buffering-Ansatz: Alle Zeichenoperationen gehen in den Back-Buffer (RAM); DisplayFlip() überträgt ihn dann in den physischen GOP-Framebuffer.

Initialisierung

var ok: int64 := DisplayInit();    // 1 = Erfolg, 0 = kein GOP-Framebuffer
var w:  int64 := DisplayGetWidth();
var h:  int64 := DisplayGetHeight();
var s:  int64 := DisplayGetStride();   // Bytes pro Zeile (kann > w×4 sein)

Zeichenoperationen

Funktion Beschreibung
DisplayFill(color) Back-Buffer vollflächig füllen
DisplayFillRect(x, y, w, h, color) Rechteck im Back-Buffer füllen
DisplayBlit(src, src_w, src_h, dst_x, dst_y) Pixel-Buffer in Back-Buffer kopieren
DisplayFlip() Back-Buffer → GOP-Front-Buffer (8-Byte-Chunks, stride-aware)

Farbformat

Alle Farben sind 32-Bit BGRA (Byte-Reihenfolge im Speicher: B, G, R, A):

con DESK_BG:     int64 := 0xFF1A1A2E;   // deep navy (Desktop-Hintergrund)
con WIN_BG_TERM: int64 := 0xFF0D1117;   // Terminalfenster-Hintergrund
con WIN_FG:      int64 := 0xFFCDD6F4;   // primärer Text (helles Lavendel)

DisplayFlip() optimiert den Transfer: bei stride = w×4 werden alle Pixel als 8-Byte-Chunks (poke64) übertragen, was die MMIO-Transaktionen halbiert im Vergleich zu 4-Byte-Writes.


4. Fenster-API

Fenster werden vom Kernel in der Fenstertabelle (max. 32 Slots) verwaltet. Vega erzeugt Fenster über Syscalls; der physische Framebuffer jedes Fensters liegt im Kernel-Heap.

Fenster anlegen und befüllen

// Fenster anlegen (Client-Bereich ohne Titelleiste)
var wid: int64 := SysWinCreate(x, y, w, h, 0);    // gibt win_id zurück oder -1
if (wid < 0) { return 1; }

// Framebuffer in den User-Adressraum mappen
var wfb: int64 := SysWinGetFb(wid);               // user_virt oder -1

// In den Fenster-FB zeichnen (direkt per poke8/poke64)
FillWinFb(wfb, w, h, 0xFF0D1117);                 // Hintergrund füllen
DrawChar(wfb, w, 4, 4, 65, 0xFFCDD6F4, 0xFF0D1117);  // 'A' bei (4,4)

Window-Flags (VEGA_WIN_*)

Konstante Wert Bedeutung
VEGA_WIN_NORMAL 0 Standard-Fenster mit Dekoration
VEGA_WIN_BORDERLESS 1 Kein Rahmen, keine Titelleiste
VEGA_WIN_FULLSCREEN 2 Vollbild (übernimmt gesamten Screen)
VEGA_WIN_RESIZABLE 4 Fenster kann vom User in der Größe geändert werden
VEGA_WIN_HIDDEN 8 Fenster existiert, wird aber nicht gerendert

Flags können kombiniert werden: VEGA_WIN_BORDERLESS | VEGA_WIN_RESIZABLE ergibt 5.

Fenster-Verwaltung

Funktion Beschreibung
SysWinCreate(x, y, w, h, flags) Neues Fenster; flags aus VEGA_WIN_*-Konstanten
SysWinDestroy(win_id) Fenster freigeben
SysWinRaise(win_id) Z-Order auf max+1 (in Vordergrund)
SysWinMove(win_id, x, y) Position verschieben
SysWinResize(win_id, new_w, new_h) Fenstergröße ändern
SysWinGetFb(win_id) Fenster-FB in User-Space mappen → user_virt
SysWinGetTablePhys() Physische Adresse der Fenstertabelle (Compositor-Direktzugriff)
SysWinSetTitle(win_id, title_ptr) Fenster-Titel setzen (WP27, nr=148)
SysWinFindByIdx(idx) win_id des Slots idx ermitteln (WP27, nr=149)
SysWinGetTitle(win_id, buf) Fenster-Titel lesen in buf (WP27, nr=150)
SysWinGetGeom(win_id, out_buf) Geometrie lesen: x,y,w,h als 4×int64 in out_buf (WP29, nr=151)
SysWinGetPid(win_id) PID des Fenster-Besitzers (WP29, nr=152)

Fenstertabelle direkt lesen (Compositor)

Vega liest die Fenstertabelle über ihre physische Adresse ohne Syscall-Overhead:

var tbl: int64 := SysWinGetTablePhys();
var i:   int64 := 0;
while (i < 32) {
    if (WinState(tbl, i) == 1) {
        var x: int64 := WinX(tbl, i);
        var y: int64 := WinY(tbl, i);
        BlitWindow(WinPhys(tbl, i), WinW(tbl, i), WinH(tbl, i), x, y);
    }
    i := i + 1;
}

Hilfsfunktion Rückgabe
WinSlot(tbl, idx) Zeiger auf Slot (tbl + idx×104)
WinId(tbl, idx) win_id des Slots
WinState(tbl, idx) 0=frei, 1=aktiv
WinX/Y/W/H/Z(tbl, idx) Position, Größe, Z-Order
WinPhys(tbl, idx) Physische Adresse des Fenster-FB

5. Fenster-Dekoration

DrawDecoration() zeichnet Titelleiste, Rahmen und Schließen-Knopf in den Back-Buffer des Compositors (nicht in den Fenster-FB):

// focused: 1 = aktiv (dunkle Titelleiste), 0 = inaktiv (graue Titelleiste)
DrawDecoration(back_buf, display_width, cx, cy, cw, ch, "Terminal", 1);

Konstante Wert Bedeutung
DECO_TITLE_H 28 Titelleistenhöhe in Pixeln
DECO_BAR_FOCUS 0xFF2D3748 Aktive Titelleiste (dunkles Schiefer)
DECO_BAR_UNFOCUS 0xFF718096 Inaktive Titelleiste (grau)
DECO_CLOSE 0xFFE53E3E Schließen-Knopf (rot)
DECO_BORDER 0xFF4A5568 Fensterrahmen

Die Titelleiste sitzt oberhalb des Client-Bereichs: [cy-28 .. cy). Für Drag-Hit-Tests ergibt sich daraus:

fn in_titlebar(mx, my, wx, wy, ww): int64 {
    if (mx >= wx && mx < wx + ww && my >= wy - 28 && my < wy) { return 1; }
    return 0;
}


6. VGA-Font

Vega enthält einen eingebetteten VGA-8×16-Font für 96 druckbare ASCII-Zeichen (32–127). Der Font wird zur Laufzeit initialisiert (vga_font_init(), aufgerufen von Init()), da lyxc keine verketteten String-Literale als Initialisierungswert unterstützt.

// Einzelnes Zeichen an (px, py) in einen Pixel-Buffer zeichnen
DrawChar(buf, buf_w, px, py, ch, fg_color, bg_color);

// Null-terminierten String zeichnen; gibt x-Position nach letztem Zeichen zurück
var x_end: int64 := DrawString(buf, buf_w, px, py, "Hallo", WIN_FG, WIN_BG_TERM);

Jeder Glyph ist 8×16 Pixel; DrawChar clippt an der rechten Pufferkante. Zeichen außerhalb des druckbaren Bereichs werden als Leerzeichen (ASCII 32) dargestellt.

DrawCharToPhys und DrawStringToPhys sind Aliases, die explizit deutlich machen, dass der Buffer-Parameter eine physische Adresse ist.


7. Maus-Cursor

DrawCursor() zeichnet einen 12×20-Pfeil-Sprite mit einfachem Drop-Shadow in den Back-Buffer — immer als letztes, sodass er über allem anderen liegt:

DrawCursor(back_buf, display_width, display_height, mouse_x, mouse_y);

Der Cursor-Sprite ist als Bitmask in bsys_vega.lyx eingebettet (arrow_rows, 40 Bytes für 20 Zeilen × 12 Bit). Der Drop-Shadow wird als schwarzes Pixel (+1,+1) hinter jedem Vordergrundpixel gesetzt, überschreibt aber keine Cursor-Pixel selbst.


8. Eingabe

PS/2-Maus

SysSetMouseBounds(scrw - 1, scrh - 1);   // Koordinatengrenzen setzen (einmalig)
var mx:  int64 := SysGetMouseX();         // X-Koordinate
var my:  int64 := SysGetMouseY();         // Y-Koordinate
var btn: int64 := SysGetMouseBtn();       // Tastenbits: VEGA_MBTN_*

Konstante Wert Bedeutung
VEGA_MBTN_LEFT 1 Linke Maustaste
VEGA_MBTN_RIGHT 2 Rechte Maustaste
VEGA_MBTN_MIDDLE 4 Mitteltaste (Rad-Click)

if (btn & VEGA_MBTN_LEFT  != 0) { /* Linksklick */ }
if (btn & VEGA_MBTN_RIGHT != 0) { /* Rechtsklick */ }

Tastatur (non-blocking)

var ch: int64 := KbdPoll();   // 0 = kein Zeichen; sonst ASCII-Byte

KbdPoll() liest aus dem PS/2-Ringpuffer ohne zu blockieren (Syscall nr=131). Im Gegensatz zu sys_read(stdin) (nr=0) wartet er nicht auf Eingabe — geeignet für Frame-Loops.


9. Ereignis-Ring

Prozesse können sich gegenseitig Ereignisse (Typ + 3 Parameter) über einen kernel-seitigen Ringpuffer senden:

// Ereignis an PID 2 senden
SysEventSend(2, EV_KEY_PRESS, 65, 0, 0);

// Eigenes Postfach prüfen (non-blocking)
var ev_buf: int64 := mmap(0, 32, 3, 34, -1, 0);   // 4 × int64
var ok: int64 := SysEventRecv(ev_buf);
if (ok == 0) {
    var ev_type: int64 := peek64(ev_buf);
    var ev_a:    int64 := peek64(ev_buf + 8);
}

Der Ring fasst pro PID 16 Ereignisse (32 Bytes/Ereignis). PIDs ≥ 8 werden ignoriert. Ist der Ring voll, gehen neue Ereignisse verloren (kein Backpressure).


10. SHM und Console-Routing

var shm_id: int64 := SysShmCreate(4096);   // SHM-Region anlegen (shm_id oder -1)
var uva:    int64 := SysShmMap(shm_id);    // in User-Space mappen
SysShmUnmap(shm_id);                        // Referenzzähler dekrementieren

// PutCh-Ausgabe von PID 3 in SHM-Ringpuffer umleiten
SysSetConsole(3, shm_id);
// Routing abschalten:
SysSetConsole(3, -1);


11. std.lyxos.vega — Standardbibliothek für Ring-3-Anwendungen

std.lyxos.vega (src/std/lyxos/vega.lyx, LX-WP17) ist die öffentliche Standard-Library-Unit für Ring-3-Anwendungen. Sie bietet benannte Konstanten und Vega*-Wrapper um alle Vega-Syscalls — ohne bsys_vega zu importieren.

import std.lyxos.vega;
import std.alloc;

fn main(): int64 {
    // Fenster anlegen
    var wid: int64 := VegaWinCreate(100, 100, 640, 480, VEGA_WIN_RESIZABLE);
    if (wid == 0) { return 1; }

    // Framebuffer mappen
    var fb: int64 := VegaWinGetFb(wid);

    // Mausgrenzen setzen
    VegaSetMouseBounds(1919, 1079);

    // Frame-Loop
    while (1 == 1) {
        VegaVsyncWait();
        var mx: int64 := VegaGetMouseX();
        var my: int64 := VegaGetMouseY();
        var ch: int64 := VegaKbdPoll();          // 0 = kein Zeichen

        // Ereignisse empfangen
        var evBuf: int64 := alloc(32);
        var n: int64 := VegaEventRecv(evBuf);
        if (n > 0) {
            var t: int64 := peek64(evBuf);       // VEGA_EVT_*
            if (t == VEGA_EVT_WIN_CLOSE) { break; }
        }
        free(evBuf, 32);
    }

    VegaWinDestroy(wid);
    return 0;
}

Konstanten

Window-Flags (sys_win_create):

Konstante Wert
VEGA_WIN_NORMAL 0
VEGA_WIN_BORDERLESS 1
VEGA_WIN_FULLSCREEN 2
VEGA_WIN_RESIZABLE 4
VEGA_WIN_HIDDEN 8

Ereignis-Typen (sys_event_send / sys_event_recv):

Konstante Wert Bedeutung
VEGA_EVT_KEY_DOWN 1 Taste gedrückt
VEGA_EVT_KEY_UP 2 Taste losgelassen
VEGA_EVT_MOUSE_MOVE 3 Maus bewegt
VEGA_EVT_MOUSE_BTN 4 Maustaste gedrückt/losgelassen
VEGA_EVT_WIN_CLOSE 5 Schließen-Knopf geklickt
VEGA_EVT_WIN_RESIZE 6 Fenster in der Größe geändert
VEGA_EVT_WIN_FOCUS 7 Fokus gewonnen / verloren

Maustastenbits (sys_get_mouse_btn):

Konstante Wert
VEGA_MBTN_LEFT 1
VEGA_MBTN_RIGHT 2
VEGA_MBTN_MIDDLE 4

Funktionen

Fenster-Verwaltung:

Signatur Beschreibung
VegaWinCreate(x, y, w, h, flags): int64 Fenster anlegen; gibt win_id zurück (0 = Fehler)
VegaWinDestroy(winId): int64 Fenster freigeben
VegaWinRaise(winId): int64 In Vordergrund bringen
VegaWinMove(winId, x, y): int64 Fenster verschieben
VegaWinResize(winId, newW, newH): int64 Fenstergröße ändern
VegaWinGetFb(winId): int64 Fenster-Framebuffer → UVA (0 = Fehler)
VegaWinGetTablePhys(): int64 Physische Adresse der Fenstertabelle
VegaWinSetTitle(winId, titlePtr): int64 Fenster-Titel setzen (WP27)
VegaWinFindByIdx(idx): int64 win_id für Slot-Index ermitteln (WP27)
VegaWinGetTitle(winId, buf): int64 Fenster-Titel lesen (WP27)
VegaWinGetGeom(winId, outBuf): int64 Geometrie lesen: x,y,w,h in outBuf (4×int64) (WP29)
VegaWinGetPid(winId): int64 PID des Fenster-Besitzers (WP29)

Display / Framebuffer:

Signatur Beschreibung
VegaVsyncWait(): int64 Blockiert bis zum nächsten VSync/PIT-Tick
VegaFbMapUser(fbPhys, fbW, fbH, stride): int64 Physischen Framebuffer in User-Adressraum mappen → UVA

Shared Memory:

Signatur Beschreibung
VegaShmCreate(size): int64 SHM-Region anlegen; gibt shm_id zurück (0 = Fehler)
VegaShmMap(shmId): int64 In User-Space mappen → UVA (0 = Fehler)
VegaShmUnmap(shmId): int64 Referenzzähler dekrementieren

Ereignisse:

Signatur Beschreibung
VegaEventSend(pid, evtType, a, b): int64 Ereignis an PID senden; evtType aus VEGA_EVT_*
VegaEventRecv(outBuf): int64 Eigenes Postfach prüfen; gibt Anzahl geschriebener Ereignisse zurück
VegaSetConsole(pid, shmId): int64 PutCh-Ausgabe von PID in SHM-Ringpuffer umleiten (shmId=-1 zum Deaktivieren)

PS/2-Maus:

Signatur Beschreibung
VegaGetMouseX(): int64 X-Koordinate
VegaGetMouseY(): int64 Y-Koordinate
VegaGetMouseBtn(): int64 Tastenbits (VEGA_MBTN_*)
VegaSetMouseBounds(maxX, maxY): int64 Koordinatengrenzen setzen

Tastatur:

Signatur Beschreibung
VegaKbdPoll(): int64 Non-blocking; 0 = kein Zeichen, sonst Scancode

Zeit:

Signatur Beschreibung
VegaTimeNs(): int64 Nanosekunden seit Systemstart

12. Quelldateien

Datei Inhalt
vega/vega.lyx Compositor-Hauptprogramm: Frame-Loop, Fenstergeometrie, Drag-Logik, Kompositionierung
vega/bsys_vega.lyx Bridge-Unit: Syscall-Wrapper (WP17/WP17b), Display-Backend, VGA-Font, Dekoration, Maus-Cursor, Fenster-Tabellenhelfer
vega/bsys_vega.lyu Vorkompilierte Unit (lyxc-Output)
vega/vega.elf Fertig gelinktes ELF64-Binary
src/std/lyxos/vega.lyx Standard-Library-Unit (LX-WP17): öffentliche Vega-API für Ring-3-Anwendungen

Letzte Aktualisierung: 2026-06-25