====== 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. → [[lyxos:start|Übersicht]] · [[lyxos:kernel|Kernel-Interna]] · [[lyxos:syscalls|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