milestone: desktop path Phases 1-5

Phase 1 (Runtime Substrate): 4 check binaries, --probe, POSIX tests
Phase 2 (Wayland Compositor): bounded scaffold, zero warnings
Phase 3 (KWin Session): preflight checker (KWin stub, gated on Qt6Quick)
Phase 4 (KDE Plasma): 18 KF6 enabled, preflight checker
Phase 5 (Hardware GPU): DRM/firmware/Mesa preflight checker

Build: zero warnings, all scripts syntax-clean. Oracle-verified.
This commit is contained in:
2026-04-29 09:54:06 +01:00
parent b23714f542
commit 8acc73d774
508 changed files with 76526 additions and 396 deletions
@@ -1,8 +1,13 @@
// Red Bear Wayland Compositor — a real Wayland display server for the Qt6 greeter UI.
// Replaces the KWin stub that previously created a placeholder socket with no actual compositing.
// Red Bear Wayland Compositor — bounded Wayland compositor proof scaffold.
// Replaces the KWin stub that previously created a placeholder socket.
//
// Architecture: creates a Wayland Unix socket, speaks the core Wayland wire protocol,
// accepts client SHM buffers, and composites them directly onto the vesad framebuffer.
// Architecture: creates a Wayland Unix socket, speaks a bounded subset of the core
// Wayland wire protocol, and accepts client SHM buffers.
//
// NOTE: This is a bounded proof scaffold, not a real compositor runtime proof.
// Known limitations: framebuffer compositing uses private heap memory (not real
// vesad), SHM fd passing uses payload bytes (not Unix SCM_RIGHTS), wire encoding
// uses NUL-terminated strings (not padded Wayland format).
//
// Supported protocols: wl_display, wl_registry, wl_compositor, wl_shm, wl_shm_pool,
// wl_surface, wl_shell, wl_shell_surface, wl_seat, wl_output, wl_callback, wl_buffer.
@@ -20,6 +25,8 @@ fn map_framebuffer(_phys: usize, size: usize) -> Vec<u8> {
const WL_DISPLAY_SYNC: u16 = 0;
const WL_DISPLAY_GET_REGISTRY: u16 = 1;
// Protocol constant: reserved for future implementation.
#[allow(dead_code)]
const WL_DISPLAY_ERROR: u16 = 0;
const WL_DISPLAY_DELETE_ID: u16 = 2;
@@ -27,12 +34,16 @@ const WL_REGISTRY_BIND: u16 = 0;
const WL_REGISTRY_GLOBAL: u16 = 0;
const WL_COMPOSITOR_CREATE_SURFACE: u16 = 0;
// Protocol constant: reserved for future implementation.
#[allow(dead_code)]
const WL_COMPOSITOR_CREATE_REGION: u16 = 1;
const WL_SHM_CREATE_POOL: u16 = 0;
const WL_SHM_FORMAT: u16 = 0;
const WL_SHM_POOL_CREATE_BUFFER: u16 = 0;
// Protocol constant: reserved for future implementation.
#[allow(dead_code)]
const WL_SHM_POOL_RESIZE: u16 = 1;
const WL_BUFFER_RELEASE: u16 = 0;
@@ -40,14 +51,22 @@ const WL_BUFFER_RELEASE: u16 = 0;
const WL_SURFACE_ATTACH: u16 = 0;
const WL_SURFACE_DAMAGE: u16 = 1;
const WL_SURFACE_COMMIT: u16 = 5;
// Protocol constant: reserved for future implementation.
#[allow(dead_code)]
const WL_SURFACE_ENTER: u16 = 0;
// Protocol constant: reserved for future implementation.
#[allow(dead_code)]
const WL_SURFACE_LEAVE: u16 = 1;
const WL_SHELL_GET_SHELL_SURFACE: u16 = 0;
const WL_SHELL_SURFACE_PONG: u16 = 0;
const WL_SHELL_SURFACE_SET_TOPLEVEL: u16 = 2;
// Protocol constant: reserved for future implementation.
#[allow(dead_code)]
const WL_SHELL_SURFACE_PING: u16 = 0;
// Protocol constant: reserved for future implementation.
#[allow(dead_code)]
const WL_SHELL_SURFACE_CONFIGURE: u16 = 1;
const XDG_WM_BASE_DESTROY: u16 = 0;
@@ -65,9 +84,17 @@ const WL_SEAT_GET_POINTER: u16 = 0;
const WL_SEAT_GET_KEYBOARD: u16 = 1;
const WL_SEAT_CAPABILITIES: u16 = 0;
// Protocol constant: reserved for future implementation.
#[allow(dead_code)]
const WL_KEYBOARD_KEYMAP: u16 = 0;
// Protocol constant: reserved for future implementation.
#[allow(dead_code)]
const WL_KEYBOARD_ENTER: u16 = 1;
// Protocol constant: reserved for future implementation.
#[allow(dead_code)]
const WL_KEYBOARD_LEAVE: u16 = 2;
// Protocol constant: reserved for future implementation.
#[allow(dead_code)]
const WL_KEYBOARD_KEY: u16 = 3;
const WL_OUTPUT_GEOMETRY: u16 = 0;
@@ -78,6 +105,23 @@ const WL_CALLBACK_DONE: u16 = 0;
const WL_SHM_FORMAT_XRGB8888: u32 = 1;
const WL_SHM_FORMAT_ARGB8888: u32 = 0;
const OBJECT_TYPE_WL_DISPLAY: u32 = 1;
const OBJECT_TYPE_WL_REGISTRY: u32 = 2;
const OBJECT_TYPE_WL_COMPOSITOR: u32 = 3;
const OBJECT_TYPE_WL_SHM: u32 = 4;
const OBJECT_TYPE_WL_SHELL: u32 = 5;
const OBJECT_TYPE_WL_SEAT: u32 = 6;
const OBJECT_TYPE_WL_OUTPUT: u32 = 7;
const OBJECT_TYPE_XDG_WM_BASE: u32 = 8;
const OBJECT_TYPE_WL_SURFACE: u32 = 9;
const OBJECT_TYPE_WL_BUFFER: u32 = 10;
const OBJECT_TYPE_WL_SHELL_SURFACE: u32 = 11;
const OBJECT_TYPE_XDG_SURFACE: u32 = 12;
const OBJECT_TYPE_XDG_TOPLEVEL: u32 = 13;
const OBJECT_TYPE_WL_SHM_POOL: u32 = 14;
const OBJECT_TYPE_WL_POINTER: u32 = 15;
const OBJECT_TYPE_WL_KEYBOARD: u32 = 16;
struct Global {
name: u32,
interface: String,
@@ -86,7 +130,7 @@ struct Global {
struct ShmPool {
data: &'static mut [u8],
size: usize,
_size: usize,
}
#[derive(Clone)]
@@ -96,7 +140,7 @@ struct Buffer {
width: u32,
height: u32,
stride: u32,
format: u32,
_format: u32,
}
struct Surface {
@@ -104,8 +148,8 @@ struct Surface {
committed_buffer_id: Option<u32>,
x: u32,
y: u32,
width: u32,
height: u32,
_width: u32,
_height: u32,
}
struct ClientState {
@@ -113,7 +157,7 @@ struct ClientState {
surfaces: HashMap<u32, Surface>,
buffers: HashMap<u32, (u32, Buffer)>,
shm_pools: HashMap<u32, ShmPool>,
next_id: u32,
_next_id: u32,
}
pub struct Compositor {
@@ -181,16 +225,15 @@ impl Compositor {
);
for stream in self.listener.incoming() {
match stream {
Ok(mut stream) => {
Ok(stream) => {
let client_id = self.alloc_id();
eprintln!("redbear-compositor: client {} connected", client_id);
self.send_globals(client_id, &mut stream);
self.clients.lock().unwrap().insert(client_id, ClientState {
objects: HashMap::new(),
surfaces: HashMap::new(),
buffers: HashMap::new(),
shm_pools: HashMap::new(),
next_id: 1,
_next_id: 1,
});
self.handle_client(client_id, stream);
}
@@ -200,15 +243,14 @@ impl Compositor {
Ok(())
}
fn send_globals(&self, _client_id: u32, stream: &mut UnixStream) {
// Advertise each global interface to the client
let display_id = 1u32; // wl_display id
fn send_globals(&self, stream: &mut UnixStream, registry_id: u32) {
// Advertise each global interface on the wl_registry object after get_registry.
for global in &self.globals {
let name = global.name;
let iface = global.interface.as_bytes();
let version = global.version;
let mut msg = Vec::with_capacity(16 + iface.len() + 1);
msg.extend_from_slice(&display_id.to_ne_bytes());
msg.extend_from_slice(&registry_id.to_ne_bytes());
let size = 8 + 4 + 4 + iface.len() as u16 + 1;
let header = (size as u32) << 16 | WL_REGISTRY_GLOBAL as u32;
msg.extend_from_slice(&header.to_ne_bytes());
@@ -256,6 +298,7 @@ impl Compositor {
let mut offset = 0;
while offset + 8 <= data.len() {
let object_id = u32::from_ne_bytes([data[offset], data[offset+1], data[offset+2], data[offset+3]]);
// Wayland wire format: [object_id:u32][size:u16][opcode:u16]
let size_opcode = u32::from_ne_bytes([data[offset+4], data[offset+5], data[offset+6], data[offset+7]]);
let msg_size = ((size_opcode >> 16) & 0xFFFF) as usize;
let opcode = (size_opcode & 0xFFFF) as u16;
@@ -265,220 +308,329 @@ impl Compositor {
}
let payload = &data[offset+8..offset+msg_size];
let object_type = if object_id == 1 {
OBJECT_TYPE_WL_DISPLAY
} else {
self.clients
.lock()
.unwrap()
.get(&client_id)
.and_then(|client| client.objects.get(&object_id).copied())
.unwrap_or(0)
};
match opcode {
WL_DISPLAY_SYNC => {
let callback_id = if payload.len() >= 4 {
u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]])
} else { self.alloc_id() };
self.send_callback_done(stream, callback_id, 0);
}
WL_DISPLAY_DELETE_ID => {
if payload.len() >= 4 {
let obj_id = u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]]);
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
client.objects.remove(&obj_id);
client.surfaces.remove(&obj_id);
client.buffers.remove(&obj_id);
client.shm_pools.remove(&obj_id);
match object_type {
OBJECT_TYPE_WL_DISPLAY => match opcode {
WL_DISPLAY_SYNC => {
let callback_id = if payload.len() >= 4 {
u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]])
} else { self.alloc_id() };
self.send_callback_done(stream, callback_id, 0);
}
WL_DISPLAY_DELETE_ID => {
if payload.len() >= 4 {
let obj_id = u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]]);
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
client.objects.remove(&obj_id);
client.surfaces.remove(&obj_id);
client.buffers.remove(&obj_id);
client.shm_pools.remove(&obj_id);
}
}
}
}
WL_DISPLAY_GET_REGISTRY => {
if payload.len() >= 4 {
let registry_id = u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]]);
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
client.objects.insert(registry_id, 2); // wl_registry
WL_DISPLAY_GET_REGISTRY => {
if payload.len() >= 4 {
let registry_id = u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]]);
let mut clients = self.clients.lock().unwrap();
let mut send_globals = false;
if let Some(client) = clients.get_mut(&client_id) {
client.objects.insert(registry_id, OBJECT_TYPE_WL_REGISTRY);
send_globals = true;
}
drop(clients);
if send_globals {
self.send_globals(stream, registry_id);
}
}
}
}
WL_REGISTRY_BIND => {
if payload.len() >= 12 {
let name = u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]]);
let iface_bytes = &payload[4..];
let null_pos = iface_bytes.iter().position(|&b| b == 0).unwrap_or(iface_bytes.len());
let iface = std::str::from_utf8(&iface_bytes[..null_pos]).unwrap_or("");
let version_offset = 4 + null_pos + 1;
let new_id = if payload.len() >= version_offset + 4 {
u32::from_ne_bytes([payload[version_offset], payload[version_offset+1], payload[version_offset+2], payload[version_offset+3]])
} else { continue; };
_ => {
eprintln!("redbear-compositor: unhandled opcode {} on object {}", opcode, object_id);
}
},
OBJECT_TYPE_WL_REGISTRY => match opcode {
WL_REGISTRY_BIND => {
if payload.len() >= 12 {
let _name = u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]]);
let iface_bytes = &payload[4..];
let null_pos = iface_bytes.iter().position(|&b| b == 0).unwrap_or(iface_bytes.len());
let iface = std::str::from_utf8(&iface_bytes[..null_pos]).unwrap_or("");
let version_offset = 4 + null_pos + 1;
let new_id = if payload.len() >= version_offset + 4 {
u32::from_ne_bytes([payload[version_offset], payload[version_offset+1], payload[version_offset+2], payload[version_offset+3]])
} else { continue; };
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
let type_id = match iface {
"wl_compositor" => 3,
"wl_shm" => 4,
"wl_shell" => 5,
"wl_seat" => 6,
"wl_output" => 7,
"xdg_wm_base" => 8,
_ => 0,
};
client.objects.insert(new_id, type_id);
if iface == "wl_shm" {
self.send_shm_format(stream, new_id, WL_SHM_FORMAT_ARGB8888);
self.send_shm_format(stream, new_id, WL_SHM_FORMAT_XRGB8888);
}
if iface == "wl_output" {
self.send_output_info(stream, new_id);
}
if iface == "wl_seat" {
self.send_seat_capabilities(stream, new_id);
}
}
}
}
WL_COMPOSITOR_CREATE_SURFACE => {
if payload.len() >= 4 {
let surface_id = u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]]);
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
client.objects.insert(surface_id, 8);
client.surfaces.insert(surface_id, Surface {
buffer: None,
committed_buffer_id: None,
x: 0, y: 0,
width: self.fb_width,
height: self.fb_height,
});
}
}
}
WL_SHM_CREATE_POOL => {
if payload.len() >= 8 {
let pool_id = u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]]);
let fd_val = i32::from_ne_bytes([payload[4], payload[5], payload[6], payload[7]]);
let size = if payload.len() >= 12 {
u32::from_ne_bytes([payload[8], payload[9], payload[10], payload[11]]) as usize
} else { 0 };
if fd_val >= 0 && size > 0 {
use std::os::fd::FromRawFd;
use std::io::Seek;
let mut file = unsafe { std::fs::File::from_raw_fd(fd_val) };
let mut data = vec![0u8; size];
if file.seek(std::io::SeekFrom::Start(0)).is_ok()
&& file.read_exact(&mut data).is_ok()
{
let boxed = data.into_boxed_slice();
let leaked = Box::leak(boxed);
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
client.shm_pools.insert(pool_id, ShmPool { data: leaked, size });
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
let type_id = match iface {
"wl_compositor" => OBJECT_TYPE_WL_COMPOSITOR,
"wl_shm" => OBJECT_TYPE_WL_SHM,
"wl_shell" => OBJECT_TYPE_WL_SHELL,
"wl_seat" => OBJECT_TYPE_WL_SEAT,
"wl_output" => OBJECT_TYPE_WL_OUTPUT,
"xdg_wm_base" => OBJECT_TYPE_XDG_WM_BASE,
_ => 0,
};
client.objects.insert(new_id, type_id);
if iface == "wl_shm" {
self.send_shm_format(stream, new_id, WL_SHM_FORMAT_ARGB8888);
self.send_shm_format(stream, new_id, WL_SHM_FORMAT_XRGB8888);
}
if iface == "wl_output" {
self.send_output_info(stream, new_id);
}
if iface == "wl_seat" {
self.send_seat_capabilities(stream, new_id);
}
}
}
}
}
WL_SHM_POOL_CREATE_BUFFER => {
if payload.len() >= 20 {
let buffer_id = u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]]);
let offset = u32::from_ne_bytes([payload[4], payload[5], payload[6], payload[7]]);
let width = u32::from_ne_bytes([payload[8], payload[9], payload[10], payload[11]]);
let height = u32::from_ne_bytes([payload[12], payload[13], payload[14], payload[15]]);
let stride = u32::from_ne_bytes([payload[16], payload[17], payload[18], payload[19]]);
let format = if payload.len() >= 24 {
u32::from_ne_bytes([payload[20], payload[21], payload[22], payload[23]])
} else { WL_SHM_FORMAT_ARGB8888 };
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
client.objects.insert(buffer_id, 9); // wl_buffer
client.buffers.insert(buffer_id, (object_id, Buffer {
pool_id: object_id,
offset, width, height, stride, format,
}));
}
_ => {
eprintln!("redbear-compositor: unhandled opcode {} on object {}", opcode, object_id);
}
}
WL_SURFACE_ATTACH => {
if payload.len() >= 12 {
let buffer_id = u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]]);
let _x = i32::from_ne_bytes([payload[4], payload[5], payload[6], payload[7]]);
let _y = i32::from_ne_bytes([payload[8], payload[9], payload[10], payload[11]]);
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
if let Some((pool_id, buffer)) = client.buffers.get(&buffer_id).cloned() {
if let Some(surface) = client.surfaces.get_mut(&object_id) {
surface.buffer = Some(Buffer {
pool_id,
..buffer
});
}
}
}
}
}
WL_SURFACE_COMMIT => {
let (release_id, buffer_data) = {
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
if let Some(surface) = client.surfaces.get_mut(&object_id) {
let old_buffer = surface.committed_buffer_id.take();
surface.committed_buffer_id = surface.buffer.as_ref().map(|b| {
client.buffers.iter()
.find(|(_, (_, buf))| buf.offset == b.offset && buf.width == b.width)
.map(|(id, _)| *id)
.unwrap_or(0)
},
OBJECT_TYPE_WL_COMPOSITOR => match opcode {
WL_COMPOSITOR_CREATE_SURFACE => {
if payload.len() >= 4 {
let surface_id = u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]]);
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
client.objects.insert(surface_id, OBJECT_TYPE_WL_SURFACE);
client.surfaces.insert(surface_id, Surface {
buffer: None,
committed_buffer_id: None,
x: 0, y: 0,
_width: self.fb_width,
_height: self.fb_height,
});
if let Some(ref buffer) = surface.buffer {
if let Some(pool) = client.shm_pools.get(&buffer.pool_id) {
self.composite_buffer(pool, buffer, surface);
}
}
}
_ => {
eprintln!("redbear-compositor: unhandled opcode {} on object {}", opcode, object_id);
}
},
OBJECT_TYPE_WL_SHM => match opcode {
WL_SHM_CREATE_POOL => {
if payload.len() >= 8 {
let pool_id = u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]]);
let fd_val = i32::from_ne_bytes([payload[4], payload[5], payload[6], payload[7]]);
let size = if payload.len() >= 12 {
u32::from_ne_bytes([payload[8], payload[9], payload[10], payload[11]]) as usize
} else { 0 };
if fd_val >= 0 && size > 0 {
use std::io::Seek;
use std::os::fd::FromRawFd;
let mut file = unsafe { std::fs::File::from_raw_fd(fd_val) };
let mut data = vec![0u8; size];
if file.seek(std::io::SeekFrom::Start(0)).is_ok()
&& file.read_exact(&mut data).is_ok()
{
let boxed = data.into_boxed_slice();
let leaked = Box::leak(boxed);
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
client.objects.insert(pool_id, OBJECT_TYPE_WL_SHM_POOL);
client.shm_pools.insert(pool_id, ShmPool { data: leaked, _size: size });
}
}
(old_buffer, surface.buffer.is_some())
} else { (None, false) }
} else { (None, false) }
};
if let Some(buf_id) = release_id {
if buf_id != 0 {
self.send_buffer_release(stream, buf_id);
}
}
}
}
WL_SHELL_GET_SHELL_SURFACE | WL_SEAT_GET_KEYBOARD | WL_SEAT_GET_POINTER => {
// Ack new object creation — just register the id
if payload.len() >= 4 {
let new_id = u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]]);
_ => {
eprintln!("redbear-compositor: unhandled opcode {} on object {}", opcode, object_id);
}
},
OBJECT_TYPE_WL_SHM_POOL => match opcode {
WL_SHM_POOL_CREATE_BUFFER => {
if payload.len() >= 20 {
let buffer_id = u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]]);
let offset = u32::from_ne_bytes([payload[4], payload[5], payload[6], payload[7]]);
let width = u32::from_ne_bytes([payload[8], payload[9], payload[10], payload[11]]);
let height = u32::from_ne_bytes([payload[12], payload[13], payload[14], payload[15]]);
let stride = u32::from_ne_bytes([payload[16], payload[17], payload[18], payload[19]]);
let format = if payload.len() >= 24 {
u32::from_ne_bytes([payload[20], payload[21], payload[22], payload[23]])
} else { WL_SHM_FORMAT_ARGB8888 };
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
client.objects.insert(buffer_id, OBJECT_TYPE_WL_BUFFER);
client.buffers.insert(buffer_id, (object_id, Buffer {
pool_id: object_id,
offset,
width,
height,
stride,
_format: format,
}));
}
}
}
_ => {
eprintln!("redbear-compositor: unhandled opcode {} on object {}", opcode, object_id);
}
},
OBJECT_TYPE_WL_SURFACE => match opcode {
WL_SURFACE_ATTACH => {
if payload.len() >= 12 {
let buffer_id = u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]]);
let _x = i32::from_ne_bytes([payload[4], payload[5], payload[6], payload[7]]);
let _y = i32::from_ne_bytes([payload[8], payload[9], payload[10], payload[11]]);
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
if let Some((pool_id, buffer)) = client.buffers.get(&buffer_id).cloned() {
if let Some(surface) = client.surfaces.get_mut(&object_id) {
surface.buffer = Some(Buffer {
pool_id,
..buffer
});
}
}
}
}
}
WL_SURFACE_COMMIT => {
let release_id = {
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
if let Some(surface) = client.surfaces.get_mut(&object_id) {
let old_buffer = surface.committed_buffer_id.take();
surface.committed_buffer_id = surface.buffer.as_ref().map(|b| {
client.buffers.iter()
.find(|(_, (_, buf))| buf.offset == b.offset && buf.width == b.width)
.map(|(id, _)| *id)
.unwrap_or(0)
});
if let Some(ref buffer) = surface.buffer {
if let Some(pool) = client.shm_pools.get(&buffer.pool_id) {
self.composite_buffer(pool, buffer, surface);
}
}
old_buffer
} else { None }
} else { None }
};
if let Some(buf_id) = release_id {
if buf_id != 0 {
self.send_buffer_release(stream, buf_id);
}
}
}
WL_SURFACE_DAMAGE => {
// No-op — we don't need damage tracking for a single-client greeter.
}
_ => {
eprintln!("redbear-compositor: unhandled opcode {} on object {}", opcode, object_id);
}
},
OBJECT_TYPE_WL_SHELL => match opcode {
WL_SHELL_GET_SHELL_SURFACE => {
if payload.len() >= 4 {
let new_id = u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]]);
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
client.objects.insert(new_id, OBJECT_TYPE_WL_SHELL_SURFACE);
}
}
}
_ => {
eprintln!("redbear-compositor: unhandled opcode {} on object {}", opcode, object_id);
}
},
OBJECT_TYPE_WL_SHELL_SURFACE => match opcode {
WL_SHELL_SURFACE_SET_TOPLEVEL | WL_SHELL_SURFACE_PONG => {
// No-op — we don't need window management for a single-client greeter.
}
_ => {
eprintln!("redbear-compositor: unhandled opcode {} on object {}", opcode, object_id);
}
},
OBJECT_TYPE_WL_SEAT => match opcode {
WL_SEAT_GET_POINTER | WL_SEAT_GET_KEYBOARD => {
if payload.len() >= 4 {
let new_id = u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]]);
let object_type = match opcode {
WL_SEAT_GET_POINTER => OBJECT_TYPE_WL_POINTER,
WL_SEAT_GET_KEYBOARD => OBJECT_TYPE_WL_KEYBOARD,
_ => unreachable!(),
};
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
client.objects.insert(new_id, object_type);
}
}
}
_ => {
eprintln!("redbear-compositor: unhandled opcode {} on object {}", opcode, object_id);
}
},
OBJECT_TYPE_XDG_WM_BASE => match opcode {
XDG_WM_BASE_GET_XDG_SURFACE => {
if payload.len() >= 4 {
let new_id = u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]]);
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
client.objects.insert(new_id, OBJECT_TYPE_XDG_SURFACE);
}
}
}
XDG_WM_BASE_DESTROY | XDG_WM_BASE_PONG => {
// No-op — the greeter keeps the shell global alive for the client lifetime.
}
_ => {
eprintln!("redbear-compositor: unhandled opcode {} on object {}", opcode, object_id);
}
},
OBJECT_TYPE_XDG_SURFACE => match opcode {
XDG_SURFACE_DESTROY => {
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
client.objects.insert(new_id, 10);
client.objects.remove(&object_id);
}
}
}
WL_SHELL_SURFACE_SET_TOPLEVEL | WL_SHELL_SURFACE_PONG | WL_SURFACE_DAMAGE => {
// No-op — we don't need window management for a single-client greeter
}
XDG_WM_BASE_GET_XDG_SURFACE | XDG_WM_BASE_DESTROY | XDG_WM_BASE_PONG => {
if payload.len() >= 4 {
let new_id = u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]]);
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
client.objects.insert(new_id, 11);
XDG_SURFACE_GET_TOPLEVEL => {
if payload.len() >= 4 {
let toplevel_id = u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]]);
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
client.objects.insert(toplevel_id, OBJECT_TYPE_XDG_TOPLEVEL);
}
drop(clients);
self.send_xdg_surface_configure(stream, object_id);
self.send_xdg_toplevel_configure(stream, toplevel_id);
}
}
}
XDG_SURFACE_GET_TOPLEVEL | XDG_SURFACE_DESTROY => {
if payload.len() >= 4 {
let toplevel_id = u32::from_ne_bytes([payload[0], payload[1], payload[2], payload[3]]);
let mut clients = self.clients.lock().unwrap();
if let Some(client) = clients.get_mut(&client_id) {
client.objects.insert(toplevel_id, 12);
}
drop(clients);
self.send_xdg_surface_configure(stream, object_id);
self.send_xdg_toplevel_configure(stream, toplevel_id);
XDG_SURFACE_ACK_CONFIGURE => {
// Client acknowledged — ready for first commit.
}
}
XDG_SURFACE_ACK_CONFIGURE => {
// Client acknowledged — ready for first commit
_ => {
eprintln!("redbear-compositor: unhandled opcode {} on object {}", opcode, object_id);
}
},
OBJECT_TYPE_WL_OUTPUT
| OBJECT_TYPE_WL_BUFFER
| OBJECT_TYPE_XDG_TOPLEVEL
| OBJECT_TYPE_WL_POINTER
| OBJECT_TYPE_WL_KEYBOARD => {
eprintln!("redbear-compositor: unhandled opcode {} on object {}", opcode, object_id);
}
_ => {
eprintln!("redbear-compositor: unhandled opcode {} on object {}", opcode, object_id);
eprintln!("redbear-compositor: unhandled object {} opcode {}", object_id, opcode);
}
}
@@ -621,8 +773,6 @@ fn main() {
let fb_phys = usize::from_str_radix(fb_phys_str.trim_start_matches("0x"), 16)
.unwrap_or(0x80000000);
let fb_size = (fb_height as usize) * (fb_stride as usize);
eprintln!(
"redbear-compositor: fb {}x{} stride {} phys 0x{:X}",
fb_width, fb_height, fb_stride, fb_phys