====== 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)
==== Fenster-Verwaltung ====
^ Funktion ^ Beschreibung ^
| ''SysWinCreate(x, y, w, h, flags)'' | Neues Fenster; flags derzeit immer 0 |
| ''SysWinDestroy(win_id)'' | Fenster freigeben |
| ''SysWinRaise(win_id)'' | Z-Order auf max+1 (in Vordergrund) |
| ''SysWinMove(win_id, x, y)'' | Position verschieben |
| ''SysWinGetFb(win_id)'' | Fenster-FB in User-Space mappen → user_virt |
| ''SysWinGetTablePhys()'' | Physische Adresse der Fenstertabelle (Compositor-Direktzugriff) |
==== 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(); // Bit 0 = LMB, Bit 1 = RMB
==== 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. 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 |
Letzte Aktualisierung: 2026-06-14