141 lines
3.9 KiB
Nim
141 lines
3.9 KiB
Nim
# SPDX-License-Identifier: LSL-1.0
|
|
# Copyright (c) 2026 Markus Maiwald
|
|
# Stewardship: Self Sovereign Society Foundation
|
|
#
|
|
# This file is part of the Nexus Sovereign Core.
|
|
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
|
|
|
## Nexus Membrane: Userland Graphics Compositor
|
|
|
|
# libs/membrane/compositor.nim
|
|
# Phase 35b/d: The Sovereign Compositor + Input Router
|
|
|
|
import ../../core/ion
|
|
|
|
const SYS_TABLE_ADDR = when defined(arm64): 0x50000000'u64
|
|
else: 0x83000000'u64
|
|
|
|
const
|
|
GAP = 10 # Pixels between windows
|
|
FOCUS_COLOR = 0xFF00FFFF'u32 # Cyan border for focused window
|
|
BG_COLOR = 0xFF101020'u32 # Dark Blue background
|
|
|
|
type
|
|
Surface* = object
|
|
id*: int32
|
|
buffer*: ptr UncheckedArray[uint32]
|
|
width*, height*: int
|
|
x*: int # Logical X position on the infinite strip
|
|
dirty*: bool
|
|
focused*: bool
|
|
|
|
Compositor* = object
|
|
surfaces*: seq[Surface]
|
|
view_x*: int # Viewport scroll position
|
|
focused_idx*: int # index in seq[Surface]
|
|
|
|
var c*: Compositor
|
|
|
|
# HAL Imports
|
|
proc hal_surface_alloc*(w, h: uint32): int32 {.importc, cdecl.}
|
|
proc hal_surface_get_ptr*(id: int32): ptr UncheckedArray[uint32] {.importc, cdecl.}
|
|
|
|
proc get_sys_table(): ptr ion.SysTable =
|
|
return cast[ptr ion.SysTable](SYS_TABLE_ADDR)
|
|
|
|
proc blit_surface(s: Surface, dest_x, dest_y: int) =
|
|
let sys = get_sys_table()
|
|
let fb = cast[ptr UncheckedArray[uint32]](sys.fb_addr)
|
|
let fb_w = int(sys.fb_width)
|
|
let fb_h = int(sys.fb_height)
|
|
|
|
# Clipping
|
|
let start_y = max(0, dest_y)
|
|
let end_y = min(fb_h, dest_y + s.height)
|
|
let start_x = max(0, dest_x)
|
|
let end_x = min(fb_w, dest_x + s.width)
|
|
|
|
if start_x >= end_x or start_y >= end_y: return
|
|
|
|
for y in start_y ..< end_y:
|
|
let src_y = y - dest_y
|
|
let src_row = cast[pointer](addr s.buffer[src_y * s.width + (start_x - dest_x)])
|
|
let dest_row = cast[pointer](addr fb[y * fb_w + start_x])
|
|
let copy_len = (end_x - start_x) * 4
|
|
copyMem(dest_row, src_row, copy_len)
|
|
|
|
proc draw_border(x, y, w, h: int, color: uint32) =
|
|
let sys = get_sys_table()
|
|
let fb = cast[ptr UncheckedArray[uint32]](sys.fb_addr)
|
|
let fb_w = int(sys.fb_width)
|
|
let fb_h = int(sys.fb_height)
|
|
|
|
for ix in x ..< x + w:
|
|
if ix >= 0 and ix < fb_w:
|
|
if y >= 0 and y < fb_h: fb[y * fb_w + ix] = color
|
|
if y + h - 1 >= 0 and y + h - 1 < fb_h: fb[(y + h - 1) * fb_w + ix] = color
|
|
|
|
for iy in y ..< y + h:
|
|
if iy >= 0 and iy < fb_h:
|
|
if x >= 0 and x < fb_w: fb[iy * fb_w + x] = color
|
|
if x + w - 1 >= 0 and x + w - 1 < fb_w: fb[iy * fb_w + (x + w - 1)] = color
|
|
|
|
proc layout*(c: var Compositor) =
|
|
var current_x = 0
|
|
for i, s in c.surfaces.mpairs:
|
|
s.x = current_x
|
|
s.focused = (i == c.focused_idx)
|
|
current_x += s.width + GAP
|
|
|
|
proc process_input(c: var Compositor) =
|
|
## Intercept and route input (STUB - not currently used)
|
|
discard
|
|
# var pkt: IonPacket
|
|
# if ion_user_input(addr pkt):
|
|
# ... input handling commented out until ion_user_* is replaced with kernel APIs
|
|
|
|
proc render_frame*(c: var Compositor) =
|
|
let sys = get_sys_table()
|
|
let fb = cast[ptr UncheckedArray[uint32]](sys.fb_addr)
|
|
let fb_total = sys.fb_width * sys.fb_height
|
|
|
|
for i in 0 ..< int(fb_total):
|
|
fb[i] = BG_COLOR
|
|
|
|
c.layout()
|
|
|
|
for i, s in c.surfaces:
|
|
let screen_x = s.x - c.view_x
|
|
if screen_x + s.width < 0 or screen_x >= int(sys.fb_width):
|
|
continue
|
|
|
|
let screen_y = (int(sys.fb_height) - s.height) div 2
|
|
blit_surface(s, screen_x, screen_y)
|
|
|
|
if s.focused:
|
|
draw_border(screen_x, screen_y, s.width, s.height, FOCUS_COLOR)
|
|
|
|
proc create_surface*(w, h: int): int32 =
|
|
let id = hal_surface_alloc(uint32(w), uint32(h))
|
|
if id < 0: return -1
|
|
|
|
let p = hal_surface_get_ptr(id)
|
|
if p == nil: return -1
|
|
|
|
var s: Surface
|
|
s.id = id
|
|
s.buffer = p
|
|
s.width = w
|
|
s.height = h
|
|
s.dirty = true
|
|
|
|
c.surfaces.add(s)
|
|
if c.surfaces.len == 1:
|
|
c.focused_idx = 0
|
|
|
|
return id
|
|
|
|
proc compositor_step*() =
|
|
process_input(c)
|
|
render_frame(c)
|