From 4e24760a22430d0b57ab69684df1ee28eb5a4dd7 Mon Sep 17 00:00:00 2001 From: Vasilito Date: Mon, 11 May 2026 10:10:25 +0100 Subject: [PATCH] fix: update custom recipe implementations Update cpufreqd, driver-params, firmware-loader, seatd, redox-drm, and redox-driver-sys. Add XHCI controller quirk table. Update redbear-compositor protocol handlers. --- local/patches/htop/P1-warning-cleanups.patch | 0 .../drivers/redox-driver-sys/recipe.toml | 4 +- .../src/quirks/xhci_controller_table.rs | 449 ++++++++++++++++ local/recipes/gpu/redox-drm/source/Cargo.toml | 2 +- .../gpu/redox-drm/source/daemon/Cargo.toml | 11 + .../gpu/redox-drm/source/daemon/src/lib.rs | 149 ++++++ .../recipes/system/cpufreqd/source/Cargo.toml | 1 - .../system/cpufreqd/source/src/main.rs | 18 +- .../system/driver-params/source/src/main.rs | 14 +- .../system/firmware-loader/source/src/main.rs | 2 - local/recipes/system/seatd/recipe.toml | 16 +- .../source/src/display_backend.rs | 483 ++++++++++++++++++ .../redbear-compositor/source/src/handlers.rs | 388 ++++++++++++++ .../redbear-compositor/source/src/protocol.rs | 177 +++++++ .../redbear-compositor/source/src/state.rs | 198 +++++++ .../redbear-compositor/source/src/wire.rs | 197 +++++++ 16 files changed, 2076 insertions(+), 33 deletions(-) delete mode 100644 local/patches/htop/P1-warning-cleanups.patch create mode 100644 local/recipes/drivers/redox-driver-sys/source/src/quirks/xhci_controller_table.rs create mode 100644 local/recipes/gpu/redox-drm/source/daemon/Cargo.toml create mode 100644 local/recipes/gpu/redox-drm/source/daemon/src/lib.rs create mode 100644 local/recipes/wayland/redbear-compositor/source/src/display_backend.rs create mode 100644 local/recipes/wayland/redbear-compositor/source/src/handlers.rs create mode 100644 local/recipes/wayland/redbear-compositor/source/src/protocol.rs create mode 100644 local/recipes/wayland/redbear-compositor/source/src/state.rs create mode 100644 local/recipes/wayland/redbear-compositor/source/src/wire.rs diff --git a/local/patches/htop/P1-warning-cleanups.patch b/local/patches/htop/P1-warning-cleanups.patch deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/local/recipes/drivers/redox-driver-sys/recipe.toml b/local/recipes/drivers/redox-driver-sys/recipe.toml index 50ece1c7d3..4efc31440a 100644 --- a/local/recipes/drivers/redox-driver-sys/recipe.toml +++ b/local/recipes/drivers/redox-driver-sys/recipe.toml @@ -15,8 +15,8 @@ mkdir -p "${COOKBOOK_STAGE}/usr/lib" -j "${COOKBOOK_MAKE_JOBS}" \ ${COOKBOOK_CARGO_FLAGS[@]} -cp "${COOKBOOK_SOURCE}/target/${TARGET}/release/libredox_driver_sys.a" \ +cp "${COOKBOOK_BUILD}/target/${TARGET}/release/libredox_driver_sys.a" \ "${COOKBOOK_STAGE}/usr/lib/libredox_driver_sys.a" -cp "${COOKBOOK_SOURCE}/target/${TARGET}/release/libredox_driver_sys.rlib" \ +cp "${COOKBOOK_BUILD}/target/${TARGET}/release/libredox_driver_sys.rlib" \ "${COOKBOOK_STAGE}/usr/lib/libredox_driver_sys.rlib" """ diff --git a/local/recipes/drivers/redox-driver-sys/source/src/quirks/xhci_controller_table.rs b/local/recipes/drivers/redox-driver-sys/source/src/quirks/xhci_controller_table.rs new file mode 100644 index 0000000000..b69451b844 --- /dev/null +++ b/local/recipes/drivers/redox-driver-sys/source/src/quirks/xhci_controller_table.rs @@ -0,0 +1,449 @@ +use super::{XhciControllerQuirk, XhciControllerQuirkFlags}; + +const INTEL_BASE: XhciControllerQuirkFlags = XhciControllerQuirkFlags::from_bits_truncate( + XhciControllerQuirkFlags::INTEL_HOST.bits() + | XhciControllerQuirkFlags::LPM_SUPPORT.bits() + | XhciControllerQuirkFlags::AVOID_BEI.bits(), +); + +const AMD_LIMIT_AND_PLL: XhciControllerQuirkFlags = XhciControllerQuirkFlags::from_bits_truncate( + XhciControllerQuirkFlags::LIMIT_EP_INTERVAL_9.bits() + | XhciControllerQuirkFlags::AMD_PLL_FIX.bits(), +); + +// Linux 7.0 `drivers/usb/host/xhci-pci.c` mined table. +// +// Fresco Logic revision-specific quirks are collapsed to the device level here because the +// Red Bear controller lookup surface is keyed by vendor/device only. +pub const XHCI_CONTROLLER_QUIRK_TABLE: &[XhciControllerQuirk] = &[ + XhciControllerQuirk { + vendor: 0x8086, + device: 0x1e31, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x1e31, + flags: XhciControllerQuirkFlags::SPURIOUS_REBOOT, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x8c31, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x9c31, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x9c31, + flags: XhciControllerQuirkFlags::SPURIOUS_REBOOT, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x9cb1, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x9cb1, + flags: XhciControllerQuirkFlags::SPURIOUS_REBOOT, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x22b5, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x22b5, + flags: XhciControllerQuirkFlags::PME_STUCK, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0xa12f, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0xa12f, + flags: XhciControllerQuirkFlags::PME_STUCK, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x9d2f, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x9d2f, + flags: XhciControllerQuirkFlags::PME_STUCK, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x0aa8, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x0aa8, + flags: XhciControllerQuirkFlags::PME_STUCK, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x1aa8, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x1aa8, + flags: XhciControllerQuirkFlags::PME_STUCK, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x5aa8, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x5aa8, + flags: XhciControllerQuirkFlags::PME_STUCK, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x19d0, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x19d0, + flags: XhciControllerQuirkFlags::PME_STUCK, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x8a13, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x9a13, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0xa0ed, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0xa3af, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0xa3af, + flags: XhciControllerQuirkFlags::PME_STUCK, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x51ed, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x54ed, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x1138, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x15b5, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x15b6, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x15c1, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x15db, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x15d4, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x15e9, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x15ec, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x8086, + device: 0x15f0, + flags: INTEL_BASE, + }, + XhciControllerQuirk { + vendor: 0x1022, + device: 0x13ed, + flags: AMD_LIMIT_AND_PLL, + }, + XhciControllerQuirk { + vendor: 0x1022, + device: 0x13ee, + flags: AMD_LIMIT_AND_PLL, + }, + XhciControllerQuirk { + vendor: 0x1022, + device: 0x145c, + flags: XhciControllerQuirkFlags::AMD_PLL_FIX, + }, + XhciControllerQuirk { + vendor: 0x1022, + device: 0x145c, + flags: XhciControllerQuirkFlags::SUSPEND_DELAY, + }, + XhciControllerQuirk { + vendor: 0x1022, + device: 0x148c, + flags: AMD_LIMIT_AND_PLL, + }, + XhciControllerQuirk { + vendor: 0x1022, + device: 0x15d4, + flags: AMD_LIMIT_AND_PLL, + }, + XhciControllerQuirk { + vendor: 0x1022, + device: 0x15d5, + flags: AMD_LIMIT_AND_PLL, + }, + XhciControllerQuirk { + vendor: 0x1022, + device: 0x15e0, + flags: AMD_LIMIT_AND_PLL, + }, + XhciControllerQuirk { + vendor: 0x1022, + device: 0x15e0, + flags: XhciControllerQuirkFlags::SUSPEND_DELAY, + }, + XhciControllerQuirk { + vendor: 0x1022, + device: 0x15e1, + flags: AMD_LIMIT_AND_PLL, + }, + XhciControllerQuirk { + vendor: 0x1022, + device: 0x15e1, + flags: XhciControllerQuirkFlags::SUSPEND_DELAY, + }, + XhciControllerQuirk { + vendor: 0x1022, + device: 0x15e5, + flags: AMD_LIMIT_AND_PLL, + }, + XhciControllerQuirk { + vendor: 0x1022, + device: 0x15e5, + flags: XhciControllerQuirkFlags::DISABLE_SPARSE, + }, + XhciControllerQuirk { + vendor: 0x1022, + device: 0x15e5, + flags: XhciControllerQuirkFlags::RESET_ON_RESUME, + }, + XhciControllerQuirk { + vendor: 0x1022, + device: 0x1639, + flags: XhciControllerQuirkFlags::AMD_PLL_FIX, + }, + XhciControllerQuirk { + vendor: 0x1022, + device: 0x43b9, + flags: XhciControllerQuirkFlags::AMD_PLL_FIX, + }, + XhciControllerQuirk { + vendor: 0x1022, + device: 0x43b9, + flags: XhciControllerQuirkFlags::NO_SOFT_RETRY, + }, + XhciControllerQuirk { + vendor: 0x1022, + device: 0x43ba, + flags: XhciControllerQuirkFlags::AMD_PLL_FIX, + }, + XhciControllerQuirk { + vendor: 0x1022, + device: 0x43bb, + flags: XhciControllerQuirkFlags::AMD_PLL_FIX, + }, + XhciControllerQuirk { + vendor: 0x1022, + device: 0x43bb, + flags: XhciControllerQuirkFlags::SUSPEND_DELAY, + }, + XhciControllerQuirk { + vendor: 0x1022, + device: 0x43bb, + flags: XhciControllerQuirkFlags::NO_SOFT_RETRY, + }, + XhciControllerQuirk { + vendor: 0x1022, + device: 0x43bc, + flags: XhciControllerQuirkFlags::AMD_PLL_FIX, + }, + XhciControllerQuirk { + vendor: 0x1002, + device: 0x7316, + flags: XhciControllerQuirkFlags::LIMIT_EP_INTERVAL_9, + }, + XhciControllerQuirk { + vendor: 0x1b73, + device: 0x1000, + flags: XhciControllerQuirkFlags::BROKEN_STREAMS, + }, + XhciControllerQuirk { + vendor: 0x1b73, + device: 0x1000, + flags: XhciControllerQuirkFlags::BROKEN_MSI, + }, + XhciControllerQuirk { + vendor: 0x1b73, + device: 0x1000, + flags: XhciControllerQuirkFlags::RESET_EP_QUIRK, + }, + XhciControllerQuirk { + vendor: 0x1b73, + device: 0x1000, + flags: XhciControllerQuirkFlags::SLOW_SUSPEND, + }, + XhciControllerQuirk { + vendor: 0x1b73, + device: 0x1009, + flags: XhciControllerQuirkFlags::BROKEN_STREAMS, + }, + XhciControllerQuirk { + vendor: 0x1b73, + device: 0x1400, + flags: XhciControllerQuirkFlags::BROKEN_MSI, + }, + XhciControllerQuirk { + vendor: 0x1b6f, + device: 0x7023, + flags: XhciControllerQuirkFlags::RESET_ON_RESUME, + }, + XhciControllerQuirk { + vendor: 0x1b6f, + device: 0x7023, + flags: XhciControllerQuirkFlags::BROKEN_STREAMS, + }, + XhciControllerQuirk { + vendor: 0x1b6f, + device: 0x7023, + flags: XhciControllerQuirkFlags::NO_SOFT_RETRY, + }, + XhciControllerQuirk { + vendor: 0x1b6f, + device: 0x7052, + flags: XhciControllerQuirkFlags::RESET_ON_RESUME, + }, + XhciControllerQuirk { + vendor: 0x1b6f, + device: 0x7052, + flags: XhciControllerQuirkFlags::BROKEN_STREAMS, + }, + XhciControllerQuirk { + vendor: 0x1b6f, + device: 0x7052, + flags: XhciControllerQuirkFlags::NO_SOFT_RETRY, + }, + XhciControllerQuirk { + vendor: 0x1b21, + device: 0x1042, + flags: XhciControllerQuirkFlags::SPURIOUS_SUCCESS, + }, + XhciControllerQuirk { + vendor: 0x1b21, + device: 0x1042, + flags: XhciControllerQuirkFlags::BROKEN_STREAMS, + }, + XhciControllerQuirk { + vendor: 0x1b21, + device: 0x1142, + flags: XhciControllerQuirkFlags::NO_64BIT_SUPPORT, + }, + XhciControllerQuirk { + vendor: 0x1b21, + device: 0x1242, + flags: XhciControllerQuirkFlags::NO_64BIT_SUPPORT, + }, + XhciControllerQuirk { + vendor: 0x1b21, + device: 0x2142, + flags: XhciControllerQuirkFlags::NO_64BIT_SUPPORT, + }, + XhciControllerQuirk { + vendor: 0x1b21, + device: 0x3042, + flags: XhciControllerQuirkFlags::RESET_ON_RESUME, + }, + XhciControllerQuirk { + vendor: 0x1b21, + device: 0x3242, + flags: XhciControllerQuirkFlags::NO_64BIT_SUPPORT, + }, + XhciControllerQuirk { + vendor: 0x1912, + device: 0x0014, + flags: XhciControllerQuirkFlags::ZERO_64B_REGS, + }, + XhciControllerQuirk { + vendor: 0x1912, + device: 0x0015, + flags: XhciControllerQuirkFlags::ZERO_64B_REGS, + }, + XhciControllerQuirk { + vendor: 0x1912, + device: 0x0015, + flags: XhciControllerQuirkFlags::RESET_ON_RESUME, + }, + XhciControllerQuirk { + vendor: 0x1106, + device: 0x3432, + flags: XhciControllerQuirkFlags::RESET_ON_RESUME, + }, + XhciControllerQuirk { + vendor: 0x1106, + device: 0x3432, + flags: XhciControllerQuirkFlags::BROKEN_STREAMS, + }, + XhciControllerQuirk { + vendor: 0x1106, + device: 0x3483, + flags: XhciControllerQuirkFlags::RESET_ON_RESUME, + }, + XhciControllerQuirk { + vendor: 0x1106, + device: 0x3483, + flags: XhciControllerQuirkFlags::TRB_OVERFETCH, + }, +]; diff --git a/local/recipes/gpu/redox-drm/source/Cargo.toml b/local/recipes/gpu/redox-drm/source/Cargo.toml index 60510aac04..6a93ea9788 100644 --- a/local/recipes/gpu/redox-drm/source/Cargo.toml +++ b/local/recipes/gpu/redox-drm/source/Cargo.toml @@ -11,7 +11,7 @@ libredox = "0.1" redox_syscall = { version = "0.7", features = ["std"] } syscall04 = { package = "redox_syscall", version = "0.4" } redox-scheme = "0.11" -daemon = { path = "../../../../../recipes/core/base/source/daemon" } +daemon = { path = "daemon" } log = "0.4" thiserror = "2" bitflags = "2" diff --git a/local/recipes/gpu/redox-drm/source/daemon/Cargo.toml b/local/recipes/gpu/redox-drm/source/daemon/Cargo.toml new file mode 100644 index 0000000000..c51acf611b --- /dev/null +++ b/local/recipes/gpu/redox-drm/source/daemon/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "daemon" +description = "Redox daemon library" +version = "0.0.0" +edition = "2021" + +[dependencies] +libc = "0.2" +libredox = "0.1" +redox-scheme = "0.11" +redox_syscall = { version = "0.7", features = ["std"] } diff --git a/local/recipes/gpu/redox-drm/source/daemon/src/lib.rs b/local/recipes/gpu/redox-drm/source/daemon/src/lib.rs new file mode 100644 index 0000000000..aa54905a16 --- /dev/null +++ b/local/recipes/gpu/redox-drm/source/daemon/src/lib.rs @@ -0,0 +1,149 @@ +//! A library for creating and managing daemons for RedoxOS. +#![feature(never_type)] + +use std::io::{self, PipeWriter, Read, Write}; +use std::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd}; +use std::os::unix::process::CommandExt; +use std::process::Command; + +use libredox::Fd; +use redox_scheme::Socket; +use redox_scheme::scheme::{SchemeAsync, SchemeSync}; + +unsafe fn get_fd(var: &str) -> Option { + let fd: RawFd = match std::env::var(var) + .map_err(|e| eprintln!("daemon: env var {var} not set: {e}")) + .ok() + .and_then(|val| { + val.parse() + .map_err(|e| eprintln!("daemon: failed to parse {var} as fd: {e}")) + .ok() + }) { + Some(fd) => fd, + None => return None, + }; + if unsafe { libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC) } == -1 { + eprintln!( + "daemon: failed to set CLOEXEC flag for {var} fd: {}", + io::Error::last_os_error() + ); + return None; + } + Some(fd) +} + +unsafe fn pass_fd(cmd: &mut Command, env: &str, fd: OwnedFd) { + cmd.env(env, format!("{}", fd.as_raw_fd())); + unsafe { + cmd.pre_exec(move || { + // Pass notify pipe to child + if libc::fcntl(fd.as_raw_fd(), libc::F_SETFD, 0) == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } + }); + } +} + +/// A long running background process that handles requests. +#[must_use = "Daemon::ready must be called"] +pub struct Daemon { + write_pipe: Option, +} + +impl Daemon { + /// Create a new daemon. + pub fn new(f: impl FnOnce(Daemon) -> !) -> ! { + let write_pipe = unsafe { get_fd("INIT_NOTIFY").map(|fd| io::PipeWriter::from_raw_fd(fd)) }; + + f(Daemon { write_pipe }) + } + + /// Notify the process that the daemon is ready to accept requests. + pub fn ready(mut self) { + if let Some(write_pipe) = self.write_pipe.as_mut() { + if let Err(err) = write_pipe.write_all(&[0]) { + if err.kind() != io::ErrorKind::BrokenPipe { + eprintln!("daemon::ready write failed: {err}"); + } + } + } + } + + /// Executes `Command` as a child process. + // FIXME remove once the service spawning of hwd and pcid-spawner is moved to init + #[deprecated] + pub fn spawn(mut cmd: Command) { + let (mut read_pipe, write_pipe) = io::pipe().unwrap(); + + unsafe { pass_fd(&mut cmd, "INIT_NOTIFY", write_pipe.into()) }; + + if let Err(err) = cmd.spawn() { + eprintln!("daemon: failed to execute {cmd:?}: {err}"); + return; + } + + let mut data = [0]; + match read_pipe.read_exact(&mut data) { + Ok(()) => {} + Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => { + eprintln!("daemon: {cmd:?} exited without notifying readiness"); + } + Err(err) => { + eprintln!("daemon: failed to wait for {cmd:?}: {err}"); + } + } + } +} + +/// A long running background process that handles requests using schemes. +#[must_use = "SchemeDaemon::ready must be called"] +pub struct SchemeDaemon { + write_pipe: Option, +} + +impl SchemeDaemon { + /// Create a new daemon for use with schemes. + pub fn new(f: impl FnOnce(SchemeDaemon) -> !) -> ! { + let write_pipe = unsafe { get_fd("INIT_NOTIFY").map(|fd| io::PipeWriter::from_raw_fd(fd)) }; + + f(SchemeDaemon { write_pipe }) + } + + /// Notify the process that the scheme daemon is ready to accept requests. + pub fn ready_with_fd(self, cap_fd: Fd) -> syscall::Result<()> { + let cap_raw = cap_fd.into_raw(); + if let Some(write_pipe) = self.write_pipe { + syscall::call_wo( + write_pipe.as_raw_fd() as usize, + &cap_raw.to_ne_bytes(), + syscall::CallFlags::FD, + &[], + )?; + } + Ok(()) + } + + /// Notify the process that the synchronous scheme daemon is ready to accept requests. + pub fn ready_sync_scheme( + self, + socket: &Socket, + scheme: &mut S, + ) -> syscall::Result<()> { + let cap_id = scheme.scheme_root()?; + let cap_fd = socket.create_this_scheme_fd(0, cap_id, 0, 0)?; + self.ready_with_fd(Fd::new(cap_fd)) + } + + /// Notify the process that the asynchronous scheme daemon is ready to accept requests. + pub fn ready_async_scheme( + self, + socket: &Socket, + scheme: &mut S, + ) -> syscall::Result<()> { + let cap_id = scheme.scheme_root()?; + let cap_fd = socket.create_this_scheme_fd(0, cap_id, 0, 0)?; + self.ready_with_fd(Fd::new(cap_fd)) + } +} diff --git a/local/recipes/system/cpufreqd/source/Cargo.toml b/local/recipes/system/cpufreqd/source/Cargo.toml index b763430211..545ab79de3 100644 --- a/local/recipes/system/cpufreqd/source/Cargo.toml +++ b/local/recipes/system/cpufreqd/source/Cargo.toml @@ -9,4 +9,3 @@ path = "src/main.rs" [dependencies] redox_syscall = "0.7" -log = "0.4" diff --git a/local/recipes/system/cpufreqd/source/src/main.rs b/local/recipes/system/cpufreqd/source/src/main.rs index f664ff2c1a..eeba7f14bb 100644 --- a/local/recipes/system/cpufreqd/source/src/main.rs +++ b/local/recipes/system/cpufreqd/source/src/main.rs @@ -2,7 +2,6 @@ use std::env; use std::fs; use std::io::{Read, Write}; use std::time::{Duration, Instant}; -use log::{info, warn, LevelFilter}; const IA32_PERF_CTL: u32 = 0x199; const POLL_MS: u64 = 100; @@ -14,13 +13,6 @@ const THERMAL_CACHE_MS: u64 = 1000; #[derive(Clone, Copy, PartialEq, Debug)] enum Governor { Performance, Powersave, Ondemand, Conservative, Schedutil } -struct StderrLogger; -impl log::Log for StderrLogger { - fn enabled(&self, m: &log::Metadata) -> bool { m.level() <= LevelFilter::Info } - fn log(&self, r: &log::Record) { eprintln!("[{}] cpufreqd: {}", r.level(), r.args()); } - fn flush(&self) {} -} - #[derive(Clone)] struct PState { freq_khz: u32, power_mw: u32, latency_us: u32, ctl: u64 } @@ -120,18 +112,16 @@ fn write_scheme_state(governor: Governor, cpus: &[CpuInfo]) { } fn main() { - log::set_logger(&StderrLogger).ok(); - log::set_max_level(LevelFilter::Info); let governor = match env::var("CPUFREQ_GOVERNOR").unwrap_or_default().as_str() { "performance" => Governor::Performance, "powersave" => Governor::Powersave, "conservative" => Governor::Conservative, "schedutil" => Governor::Schedutil, _ => Governor::Ondemand, }; let cpus = detect_cpus(); - info!("detected {} CPU(s), governor={:?}", cpus.len(), governor); + eprintln!("[INFO] cpufreqd: detected {} CPU(s), governor={:?}", cpus.len(), governor); let mut ci: Vec = cpus.iter().map(|&id| { let ps = read_acpi_pss(id); - info!("CPU{}: {} P-states ({} - {} kHz)", id, ps.len(), ps.first().map_or(0, |p| p.freq_khz), ps.last().map_or(0, |p| p.freq_khz)); + eprintln!("[INFO] cpufreqd: CPU{}: {} P-states ({} - {} kHz)", id, ps.len(), ps.first().map_or(0, |p| p.freq_khz), ps.last().map_or(0, |p| p.freq_khz)); CpuInfo { id, pstates: ps, current_idx: 0, load_history: [0.0; SAMPLE_WINDOW], load_idx: 0, throttle: false, msr_errors: 0, msr_suppressed: false } }).collect(); let mut prev: Vec<(u64, u64)> = vec![(0, 0); cpus.len()]; @@ -149,11 +139,11 @@ fn main() { if n != c.current_idx && n < c.pstates.len() { let ct = c.pstates[n].ctl; if write_msr(c.id, IA32_PERF_CTL, ct) { - info!("CPU{}: P{}→P{} ({}→{} kHz, load={:.0}%)", c.id, c.current_idx, n, c.pstates[c.current_idx].freq_khz, c.pstates[n].freq_khz, l * 100.0); + eprintln!("[INFO] cpufreqd: CPU{}: P{}→P{} ({}→{} kHz, load={:.0}%)", c.id, c.current_idx, n, c.pstates[c.current_idx].freq_khz, c.pstates[n].freq_khz, l * 100.0); c.current_idx = n; c.msr_errors = 0; c.msr_suppressed = false; } else { c.msr_errors += 1; - if !c.msr_suppressed { warn!("CPU{}: MSR write failed ({}/{})", c.id, c.msr_errors, MSR_ERROR_SUPPRESS_COUNT); if c.msr_errors >= MSR_ERROR_SUPPRESS_COUNT { c.msr_suppressed = true; } } + if !c.msr_suppressed { eprintln!("[WARN] cpufreqd: CPU{}: MSR write failed ({}/{})", c.id, c.msr_errors, MSR_ERROR_SUPPRESS_COUNT); if c.msr_errors >= MSR_ERROR_SUPPRESS_COUNT { c.msr_suppressed = true; } } } } } diff --git a/local/recipes/system/driver-params/source/src/main.rs b/local/recipes/system/driver-params/source/src/main.rs index 891c59b67f..788071ec5b 100644 --- a/local/recipes/system/driver-params/source/src/main.rs +++ b/local/recipes/system/driver-params/source/src/main.rs @@ -538,11 +538,9 @@ fn bound_path_from_env() -> PathBuf { } #[cfg(target_os = "redox")] -fn init_notify_fd() -> std::result::Result { - let raw = env::var("INIT_NOTIFY") - .map_err(|_| "driver-params: INIT_NOTIFY not set".to_string())?; - raw.parse::() - .map_err(|_| "driver-params: INIT_NOTIFY is not a valid fd".to_string()) +fn init_notify_fd() -> Option { + let raw = env::var("INIT_NOTIFY").ok()?; + raw.parse::().ok() } #[cfg(target_os = "redox")] @@ -570,14 +568,16 @@ fn notify_scheme_ready( #[cfg(target_os = "redox")] fn run_daemon(bound_path: PathBuf) -> std::result::Result<(), String> { - let notify_fd = init_notify_fd()?; + let notify_fd = init_notify_fd(); let socket = Socket::create() .map_err(|err| format!("driver-params: failed to create scheme socket: {err}"))?; let mut state = SchemeState::new(); let mut scheme = DriverParamsScheme::new(bound_path); scheme.read_driver_manager_bound(); - notify_scheme_ready(notify_fd, &socket, &mut scheme)?; + if let Some(fd) = notify_fd { + notify_scheme_ready(fd, &socket, &mut scheme)?; + } libredox::call::setrens(0, 0) .map_err(|err| format!("driver-params: failed to enter null namespace: {err}"))?; diff --git a/local/recipes/system/firmware-loader/source/src/main.rs b/local/recipes/system/firmware-loader/source/src/main.rs index df83555432..b35df00463 100644 --- a/local/recipes/system/firmware-loader/source/src/main.rs +++ b/local/recipes/system/firmware-loader/source/src/main.rs @@ -51,11 +51,9 @@ fn default_firmware_dir() -> PathBuf { #[cfg(target_os = "redox")] unsafe fn get_init_notify_fd() -> Option { let Ok(value) = env::var("INIT_NOTIFY") else { - eprintln!("firmware-loader: INIT_NOTIFY not set; readiness notification disabled"); return None; }; let Ok(fd) = value.parse::() else { - eprintln!("firmware-loader: INIT_NOTIFY is not a valid fd; readiness notification disabled"); return None; }; libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC); diff --git a/local/recipes/system/seatd/recipe.toml b/local/recipes/system/seatd/recipe.toml index 383546361d..adde00c9b7 100644 --- a/local/recipes/system/seatd/recipe.toml +++ b/local/recipes/system/seatd/recipe.toml @@ -1,13 +1,17 @@ -#TODO not compiled or tested [source] -git = "https://github.com/jackpot51/seatd" -branch = "redox" +tar = "https://git.sr.ht/~kennylevinsen/seatd/archive/0.9.1.tar.gz" +blake3 = "0bca2188cb2f56d7d50774657435db9e5ead358854cfbde8c4d7d6504929a49f" [build] template = "meson" +dependencies = [ + "expat", +] mesonflags = [ + "-Dlibseat-seatd=enabled", + "-Dlibseat-builtin=disabled", + "-Dlibseat-logind=disabled", + "-Dserver=enabled", + "-Dexamples=disabled", "-Dman-pages=disabled", ] - -[package.files] -"/usr/bin/seatd" = "/usr/bin/seatd" diff --git a/local/recipes/wayland/redbear-compositor/source/src/display_backend.rs b/local/recipes/wayland/redbear-compositor/source/src/display_backend.rs new file mode 100644 index 0000000000..257a8be56c --- /dev/null +++ b/local/recipes/wayland/redbear-compositor/source/src/display_backend.rs @@ -0,0 +1,483 @@ +fn map_framebuffer(_phys: usize, size: usize) -> Vec { + vec![0u8; size] +} + +pub struct DrawBufferTarget { + pub ptr: *mut u8, + pub len: usize, + pub stride: usize, +} + +pub struct DisplayBackend { + drm: Option, + fallback: Vec, + width: u32, + height: u32, + stride: u32, +} + +impl DisplayBackend { + pub fn open_or_framebuffer() -> Self { + if let Some(drm) = drm_backend::DrmOutput::open() { + eprintln!( + "redbear-compositor: using DRM/KMS output {}x{}", + drm.width, drm.height + ); + return Self { + width: drm.width, + height: drm.height, + stride: drm.stride, + drm: Some(drm), + fallback: Vec::new(), + }; + } + + let width: u32 = std::env::var("FRAMEBUFFER_WIDTH") + .unwrap_or_else(|_| "1280".into()) + .parse() + .unwrap_or(1280); + let height: u32 = std::env::var("FRAMEBUFFER_HEIGHT") + .unwrap_or_else(|_| "720".into()) + .parse() + .unwrap_or(720); + let stride: u32 = std::env::var("FRAMEBUFFER_STRIDE") + .unwrap_or_else(|_| (width * 4).to_string()) + .parse() + .unwrap_or(width * 4); + let fb_phys_str = std::env::var("FRAMEBUFFER_ADDR").unwrap_or_else(|_| "0x80000000".into()); + let fb_phys = + usize::from_str_radix(fb_phys_str.trim_start_matches("0x"), 16).unwrap_or(0x80000000); + eprintln!( + "redbear-compositor: fb {}x{} stride {} phys 0x{:X}", + width, height, stride, fb_phys + ); + + Self { + drm: None, + fallback: map_framebuffer(fb_phys, height as usize * stride as usize), + width, + height, + stride, + } + } + + pub fn dimensions(&self) -> (u32, u32, u32) { + (self.width, self.height, self.stride) + } + + pub fn draw_target(&mut self) -> Option { + if let Some(drm) = self.drm.as_mut() { + return drm.draw_target(); + } + + Some(DrawBufferTarget { + ptr: self.fallback.as_mut_ptr(), + len: self.fallback.len(), + stride: self.stride as usize, + }) + } + + pub fn submit(&self) { + if let Some(drm) = self.drm.as_ref() { + drm.submit(); + } + } +} + +// ── DRM/KMS backend ── +// Uses /scheme/drm/card0 for hardware-accelerated display output. +// Cross-referenced with Linux DRM KMS API (drm_mode.h). +// I/O: writes [u64_le ioctl_code][payload] to the scheme file, reads response. + +#[cfg(target_os = "redox")] +mod drm_backend { + use super::DrawBufferTarget; + use std::fs::File; + use std::io::{Read, Write}; + use std::sync::atomic::{AtomicUsize, Ordering}; + + const DRM_IOCTL_BASE: usize = 0x00A0; + const DRM_IOCTL_MODE_GETCONNECTOR: usize = DRM_IOCTL_BASE + 7; + const DRM_IOCTL_MODE_SETCRTC: usize = DRM_IOCTL_BASE + 2; + const DRM_IOCTL_MODE_CREATE_DUMB: usize = DRM_IOCTL_BASE + 18; + const DRM_IOCTL_MODE_MAP_DUMB: usize = DRM_IOCTL_BASE + 19; + const DRM_IOCTL_MODE_ADDFB: usize = DRM_IOCTL_BASE + 21; + const DRM_IOCTL_MODE_PAGE_FLIP: usize = DRM_IOCTL_BASE + 16; + + fn drm_ioctl(file: &mut File, code: usize, req: &[u8], resp: &mut [u8]) -> std::io::Result<()> { + let mut wbuf = Vec::with_capacity(8 + req.len()); + wbuf.extend_from_slice(&(code as u64).to_le_bytes()); + wbuf.extend_from_slice(req); + file.write_all(&wbuf)?; + if !resp.is_empty() { + file.read_exact(resp)?; + } + Ok(()) + } + + #[repr(C)] + #[derive(Clone, Copy)] + struct DrmResources { + connector_count: u32, + crtc_count: u32, + encoder_count: u32, + } + #[repr(C)] + struct DrmConnector { + connector_id: u32, + connection: u32, + connector_type: u32, + mm_width: u32, + mm_height: u32, + encoder_id: u32, + mode_count: u32, + } + #[repr(C)] + #[derive(Clone, Copy)] + struct DrmModeInfo { + clock: u32, + hdisplay: u16, + hsync_start: u16, + hsync_end: u16, + htotal: u16, + hskew: u16, + vdisplay: u16, + vsync_start: u16, + vsync_end: u16, + vtotal: u16, + vscan: u16, + vrefresh: u32, + flags: u32, + type_: u32, + } + #[repr(C)] + #[derive(Clone, Copy)] + struct DrmGetEncoder { + encoder_id: u32, + encoder_type: u32, + crtc_id: u32, + possible_crtcs: u32, + possible_clones: u32, + } + #[repr(C)] + struct DrmCreateDumb { + height: u32, + width: u32, + bpp: u32, + flags: u32, + handle: u32, + pitch: u32, + size: u64, + } + #[repr(C)] + struct DrmMapDumb { + handle: u32, + pad: u32, + offset: u64, + } + #[repr(C)] + struct DrmAddFb { + width: u32, + height: u32, + pitch: u32, + bpp: u32, + depth: u32, + handle: u32, + fb_id: u32, + } + #[repr(C)] + struct DrmSetCrtc { + crtc_id: u32, + fb_handle: u32, + connector_count: u32, + connectors: [u32; 8], + mode: DrmModeInfo, + } + + pub struct DrmOutput { + pub width: u32, + pub height: u32, + pub stride: u32, + pub buffers: Vec<(usize, usize)>, + fb_ids: Vec, + pub current: AtomicUsize, + _file: File, + } + + impl DrmOutput { + pub fn open() -> Option { + let mut file = File::open("/scheme/drm/card0").ok()?; + eprintln!("redbear-compositor: opened /scheme/drm/card0"); + + let mut resources_resp = vec![0u8; std::mem::size_of::() + 32]; + if drm_ioctl(&mut file, DRM_IOCTL_BASE, &[], &mut resources_resp).is_err() { + return None; + } + let resources = unsafe { *(resources_resp.as_ptr() as *const DrmResources) }; + if resources.connector_count == 0 { + return None; + } + let connector_id = unsafe { + *(resources_resp + .as_ptr() + .add(std::mem::size_of::()) as *const u32) + }; + if connector_id == 0 { + return None; + } + + let mut conn: DrmConnector = unsafe { std::mem::zeroed() }; + conn.connector_id = connector_id; + conn.mode_count = 1; + let mut req = vec![0u8; std::mem::size_of::()]; + unsafe { + std::ptr::copy_nonoverlapping( + &conn as *const DrmConnector as *const u8, + req.as_mut_ptr(), + req.len(), + ); + } + let mut resp = + vec![0u8; std::mem::size_of::() + std::mem::size_of::()]; + if drm_ioctl(&mut file, DRM_IOCTL_MODE_GETCONNECTOR, &req, &mut resp).is_err() { + return None; + } + unsafe { + std::ptr::copy_nonoverlapping( + resp.as_ptr(), + &mut conn as *mut DrmConnector as *mut u8, + std::mem::size_of::(), + ); + } + if conn.mode_count == 0 { + return None; + } + let mode = unsafe { + &*(resp.as_ptr().add(std::mem::size_of::()) as *const DrmModeInfo) + }; + let width = mode.hdisplay as u32; + let height = mode.vdisplay as u32; + eprintln!("redbear-compositor: DRM mode {}x{}", width, height); + + let mut crtc_id = 1u32; + if conn.encoder_id != 0 { + let mut encoder_req: DrmGetEncoder = unsafe { std::mem::zeroed() }; + encoder_req.encoder_id = conn.encoder_id; + let mut encoder_req_buf = vec![0u8; std::mem::size_of::()]; + unsafe { + std::ptr::copy_nonoverlapping( + &encoder_req as *const DrmGetEncoder as *const u8, + encoder_req_buf.as_mut_ptr(), + encoder_req_buf.len(), + ); + } + let mut encoder_resp = vec![0u8; std::mem::size_of::()]; + if drm_ioctl( + &mut file, + DRM_IOCTL_MODE_GETCONNECTOR - 1, + &encoder_req_buf, + &mut encoder_resp, + ) + .is_ok() + { + let encoder = unsafe { *(encoder_resp.as_ptr() as *const DrmGetEncoder) }; + if encoder.crtc_id != 0 { + crtc_id = encoder.crtc_id; + } + } + } + + let mut buffers = Vec::new(); + let mut fb_ids = Vec::new(); + let mut stride = 0u32; + for _ in 0..2 { + let mut dumb: DrmCreateDumb = unsafe { std::mem::zeroed() }; + dumb.height = height; + dumb.width = width; + dumb.bpp = 32; + let mut dumb_req = vec![0u8; std::mem::size_of::()]; + unsafe { + std::ptr::copy_nonoverlapping( + &dumb as *const DrmCreateDumb as *const u8, + dumb_req.as_mut_ptr(), + dumb_req.len(), + ); + } + let mut dumb_resp = vec![0u8; std::mem::size_of::()]; + if drm_ioctl( + &mut file, + DRM_IOCTL_MODE_CREATE_DUMB, + &dumb_req, + &mut dumb_resp, + ) + .is_err() + { + return None; + } + unsafe { + std::ptr::copy_nonoverlapping( + dumb_resp.as_ptr(), + &mut dumb as *mut DrmCreateDumb as *mut u8, + std::mem::size_of::(), + ); + } + if dumb.handle == 0 { + return None; + } + stride = dumb.pitch; + + let mut map = DrmMapDumb { + handle: dumb.handle, + pad: 0, + offset: 0, + }; + let mut map_req = vec![0u8; std::mem::size_of::()]; + unsafe { + std::ptr::copy_nonoverlapping( + &map as *const DrmMapDumb as *const u8, + map_req.as_mut_ptr(), + map_req.len(), + ); + } + let mut map_resp = vec![0u8; std::mem::size_of::()]; + if drm_ioctl(&mut file, DRM_IOCTL_MODE_MAP_DUMB, &map_req, &mut map_resp).is_err() { + return None; + } + unsafe { + std::ptr::copy_nonoverlapping( + map_resp.as_ptr(), + &mut map as *mut DrmMapDumb as *mut u8, + std::mem::size_of::(), + ); + } + let buf_size = dumb.size as usize; + if map.offset == 0 { + return None; + } + buffers.push((map.offset as usize, buf_size)); + + let mut addfb = DrmAddFb { + width, + height, + pitch: stride, + bpp: 32, + depth: 24, + handle: dumb.handle, + fb_id: 0, + }; + let mut addfb_req = vec![0u8; std::mem::size_of::()]; + unsafe { + std::ptr::copy_nonoverlapping( + &addfb as *const DrmAddFb as *const u8, + addfb_req.as_mut_ptr(), + addfb_req.len(), + ); + } + let mut addfb_resp = vec![0u8; std::mem::size_of::()]; + if drm_ioctl(&mut file, DRM_IOCTL_MODE_ADDFB, &addfb_req, &mut addfb_resp).is_err() + { + return None; + } + unsafe { + std::ptr::copy_nonoverlapping( + addfb_resp.as_ptr(), + &mut addfb as *mut DrmAddFb as *mut u8, + std::mem::size_of::(), + ); + } + if addfb.fb_id == 0 { + return None; + } + fb_ids.push(addfb.fb_id); + } + + let mut setcrtc: DrmSetCrtc = unsafe { std::mem::zeroed() }; + setcrtc.crtc_id = crtc_id; + setcrtc.fb_handle = fb_ids[0]; + setcrtc.connector_count = 1; + setcrtc.connectors[0] = connector_id; + setcrtc.mode = *mode; + let mut setcrtc_req = vec![0u8; std::mem::size_of::()]; + unsafe { + std::ptr::copy_nonoverlapping( + &setcrtc as *const DrmSetCrtc as *const u8, + setcrtc_req.as_mut_ptr(), + setcrtc_req.len(), + ); + } + if drm_ioctl(&mut file, DRM_IOCTL_MODE_SETCRTC, &setcrtc_req, &mut []).is_err() { + return None; + } + + eprintln!( + "redbear-compositor: DRM output {}x{} stride={} connector={} crtc={}", + width, height, stride, connector_id, crtc_id + ); + Some(DrmOutput { + width, + height, + stride, + buffers, + fb_ids, + current: AtomicUsize::new(0), + _file: file, + }) + } + + pub fn flip(&self) { + if self.fb_ids.len() < 2 { + return; + } + let cur = self.current.load(Ordering::Relaxed); + let next = (cur + 1) % self.fb_ids.len(); + let fb_id = self.fb_ids[next]; + let mut buf = Vec::with_capacity(12); + buf.extend_from_slice(&(DRM_IOCTL_MODE_PAGE_FLIP as u64).to_le_bytes()); + buf.extend_from_slice(&fb_id.to_le_bytes()); + if let Ok(mut f) = File::open("/scheme/drm/card0") { + let _ = f.write_all(&buf); + } + self.current.store(next, Ordering::Relaxed); + } + + pub fn submit(&self) { + self.flip(); + } + + pub(super) fn draw_target(&mut self) -> Option { + if self.buffers.is_empty() { + return None; + } + let idx = (self.current.load(Ordering::Relaxed) + 1) % self.buffers.len(); + let (addr, len) = self.buffers[idx]; + Some(DrawBufferTarget { + ptr: addr as *mut u8, + len, + stride: self.stride as usize, + }) + } + } +} + +#[cfg(not(target_os = "redox"))] +mod drm_backend { + use super::DrawBufferTarget; + + pub struct DrmOutput { + pub width: u32, + pub height: u32, + pub stride: u32, + } + + impl DrmOutput { + pub fn open() -> Option { + None + } + + pub fn submit(&self) {} + + pub(super) fn draw_target(&mut self) -> Option { + None + } + } +} diff --git a/local/recipes/wayland/redbear-compositor/source/src/handlers.rs b/local/recipes/wayland/redbear-compositor/source/src/handlers.rs new file mode 100644 index 0000000000..157102bdbc --- /dev/null +++ b/local/recipes/wayland/redbear-compositor/source/src/handlers.rs @@ -0,0 +1,388 @@ +use std::os::unix::net::UnixStream; + +use crate::protocol; +use crate::protocol::*; +use crate::state::{ + DataDeviceState, DataSourceState, ShellSurfaceKind, ShellSurfaceState, SubsurfaceState, + SurfaceRole, +}; +use crate::wire::read_wayland_string; +use crate::Compositor; + +impl Compositor { + pub fn handle_wl_shell( + &self, + client_id: u32, + opcode: u16, + payload: &[u8], + ) -> Result<(), String> { + match opcode { + WL_SHELL_GET_SHELL_SURFACE => { + if payload.len() >= 8 { + let new_id = + u32::from_le_bytes([payload[0], payload[1], payload[2], payload[3]]); + let surface_id = + u32::from_le_bytes([payload[4], payload[5], payload[6], payload[7]]); + 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); + client.shell_surfaces.insert( + new_id, + ShellSurfaceState { + object_id: new_id, + surface_id, + ..ShellSurfaceState::default() + }, + ); + if let Some(surface) = client.surfaces.get_mut(&surface_id) { + surface.role = Some(SurfaceRole::Shell(ShellSurfaceState { + object_id: new_id, + surface_id, + ..ShellSurfaceState::default() + })); + } + } + } + Ok(()) + } + _ => Err(format!( + "redbear-compositor: unhandled opcode {} on wl_shell", + opcode + )), + } + } + + pub fn handle_wl_shell_surface( + &self, + client_id: u32, + object_id: u32, + opcode: u16, + payload: &[u8], + ) -> Result<(), String> { + match opcode { + protocol::WL_SHELL_SURFACE_SET_TOPLEVEL + | protocol::WL_SHELL_SURFACE_PONG + | protocol::WL_SHELL_SURFACE_SET_TRANSIENT + | protocol::WL_SHELL_SURFACE_SET_FULLSCREEN + | protocol::WL_SHELL_SURFACE_SET_POPUP + | protocol::WL_SHELL_SURFACE_SET_MAXIMIZED + | protocol::WL_SHELL_SURFACE_SET_TITLE + | protocol::WL_SHELL_SURFACE_SET_CLASS + | protocol::WL_SHELL_SURFACE_MOVE => { + let mut clients = self.clients.lock().unwrap(); + if let Some(client) = clients.get_mut(&client_id) { + if let Some(shell_surface) = client.shell_surfaces.get_mut(&object_id) { + match opcode { + protocol::WL_SHELL_SURFACE_SET_TOPLEVEL => { + shell_surface.kind = ShellSurfaceKind::Toplevel; + } + protocol::WL_SHELL_SURFACE_PONG if payload.len() >= 4 => { + shell_surface.last_ping_serial = Some(u32::from_le_bytes([ + payload[0], payload[1], payload[2], payload[3], + ])); + } + protocol::WL_SHELL_SURFACE_SET_TRANSIENT if payload.len() >= 16 => { + shell_surface.kind = ShellSurfaceKind::Transient; + let parent_id = u32::from_le_bytes([ + payload[0], payload[1], payload[2], payload[3], + ]); + shell_surface.parent_surface_id = + (parent_id != 0).then_some(parent_id); + } + protocol::WL_SHELL_SURFACE_SET_FULLSCREEN => { + shell_surface.kind = ShellSurfaceKind::Fullscreen; + } + protocol::WL_SHELL_SURFACE_SET_POPUP if payload.len() >= 20 => { + shell_surface.kind = ShellSurfaceKind::Popup; + let parent_id = u32::from_le_bytes([ + payload[0], payload[1], payload[2], payload[3], + ]); + shell_surface.parent_surface_id = + (parent_id != 0).then_some(parent_id); + shell_surface.popup_serial = Some(u32::from_le_bytes([ + payload[16], + payload[17], + payload[18], + payload[19], + ])); + } + protocol::WL_SHELL_SURFACE_SET_MAXIMIZED => { + shell_surface.kind = ShellSurfaceKind::Maximized; + } + protocol::WL_SHELL_SURFACE_SET_TITLE => { + let mut cursor = 0; + if let Ok(title) = read_wayland_string(payload, &mut cursor) { + shell_surface.title = Some(title); + } + } + protocol::WL_SHELL_SURFACE_SET_CLASS => { + let mut cursor = 0; + if let Ok(class) = read_wayland_string(payload, &mut cursor) { + shell_surface.class = Some(class); + } + } + _ => {} + } + + if let Some(surface) = client.surfaces.get_mut(&shell_surface.surface_id) { + surface.role = Some(SurfaceRole::Shell(shell_surface.clone())); + } + } + } + Ok(()) + } + _ => Err(format!( + "redbear-compositor: unhandled opcode {} on object {}", + opcode, object_id + )), + } + } + + pub fn handle_wl_data_device_manager( + &self, + client_id: u32, + opcode: u16, + payload: &[u8], + ) -> Result<(), String> { + match opcode { + WL_DATA_DEVICE_MANAGER_CREATE_DATA_SOURCE => { + if payload.len() >= 4 { + let new_id = + u32::from_le_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_DATA_SOURCE); + client + .data_sources + .insert(new_id, DataSourceState::default()); + } + } + Ok(()) + } + WL_DATA_DEVICE_MANAGER_GET_DATA_DEVICE => { + if payload.len() >= 4 { + let new_id = + u32::from_le_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_DATA_DEVICE); + client + .data_devices + .insert(new_id, DataDeviceState::default()); + } + } + Ok(()) + } + _ => Err(format!( + "redbear-compositor: unhandled data_device_manager opcode {}", + opcode + )), + } + } + + pub fn handle_wl_data_source( + &self, + client_id: u32, + object_id: u32, + opcode: u16, + payload: &[u8], + stream: &mut UnixStream, + ) -> Result<(), String> { + match opcode { + WL_DATA_SOURCE_DESTROY => { + let mut clients = self.clients.lock().unwrap(); + if let Some(client) = clients.get_mut(&client_id) { + client.objects.remove(&object_id); + client.data_sources.remove(&object_id); + } + drop(clients); + self.send_delete_id(stream, object_id); + Ok(()) + } + WL_DATA_SOURCE_OFFER | WL_DATA_SOURCE_SET_ACTIONS => { + let mut clients = self.clients.lock().unwrap(); + if let Some(client) = clients.get_mut(&client_id) { + if let Some(source) = client.data_sources.get_mut(&object_id) { + match opcode { + WL_DATA_SOURCE_OFFER => { + let mut cursor = 0; + if let Ok(mime) = read_wayland_string(payload, &mut cursor) { + if !mime.is_empty() { + source.mime_types.push(mime); + } + } + } + WL_DATA_SOURCE_SET_ACTIONS if payload.len() >= 8 => { + source.actions = Some(u32::from_le_bytes([ + payload[0], payload[1], payload[2], payload[3], + ])); + } + _ => {} + } + } + } + Ok(()) + } + _ => Err(format!( + "redbear-compositor: unhandled data_source opcode {} on object {}", + opcode, object_id + )), + } + } + + pub fn handle_wl_data_device( + &self, + client_id: u32, + object_id: u32, + opcode: u16, + payload: &[u8], + stream: &mut UnixStream, + ) -> Result<(), String> { + match opcode { + WL_DATA_DEVICE_RELEASE => { + let mut clients = self.clients.lock().unwrap(); + if let Some(client) = clients.get_mut(&client_id) { + client.objects.remove(&object_id); + client.data_devices.remove(&object_id); + } + drop(clients); + self.send_delete_id(stream, object_id); + Ok(()) + } + WL_DATA_DEVICE_START_DRAG | WL_DATA_DEVICE_SET_SELECTION => { + let mut clients = self.clients.lock().unwrap(); + if let Some(client) = clients.get_mut(&client_id) { + if let Some(device) = client.data_devices.get_mut(&object_id) { + match opcode { + WL_DATA_DEVICE_START_DRAG if payload.len() >= 4 => { + let source_id = u32::from_le_bytes([ + payload[0], payload[1], payload[2], payload[3], + ]); + device.drag_source = (source_id != 0).then_some(source_id); + } + WL_DATA_DEVICE_SET_SELECTION if payload.len() >= 4 => { + let source_id = u32::from_le_bytes([ + payload[0], payload[1], payload[2], payload[3], + ]); + device.selection_source = (source_id != 0).then_some(source_id); + } + _ => {} + } + } + } + Ok(()) + } + _ => Err(format!( + "redbear-compositor: unhandled data_device opcode {} on object {}", + opcode, object_id + )), + } + } + + pub fn handle_wl_subcompositor( + &self, + client_id: u32, + opcode: u16, + payload: &[u8], + ) -> Result<(), String> { + match opcode { + WL_SUBCOMPOSITOR_GET_SUBSURFACE => { + if payload.len() >= 12 { + let new_id = + u32::from_le_bytes([payload[0], payload[1], payload[2], payload[3]]); + let surface_id = + u32::from_le_bytes([payload[4], payload[5], payload[6], payload[7]]); + let parent_surface_id = + u32::from_le_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) { + client.objects.insert(new_id, OBJECT_TYPE_WL_SUBSURFACE); + client.subsurfaces.insert( + new_id, + SubsurfaceState { + surface_id, + parent_surface_id, + sync: true, + ..SubsurfaceState::default() + }, + ); + Compositor::stack_surface_relative( + client, + surface_id, + parent_surface_id, + true, + ); + } + } + Ok(()) + } + _ => Err(format!( + "redbear-compositor: unhandled subcompositor opcode {}", + opcode + )), + } + } + + pub fn handle_wl_subsurface( + &self, + client_id: u32, + object_id: u32, + opcode: u16, + payload: &[u8], + stream: &mut UnixStream, + ) -> Result<(), String> { + if opcode == WL_SUBSURFACE_DESTROY { + let mut clients = self.clients.lock().unwrap(); + if let Some(client) = clients.get_mut(&client_id) { + client.objects.remove(&object_id); + client.subsurfaces.remove(&object_id); + } + drop(clients); + self.send_delete_id(stream, object_id); + return Ok(()); + } + + let mut clients = self.clients.lock().unwrap(); + if let Some(client) = clients.get_mut(&client_id) { + let mut restack: Option<(u32, u32, bool)> = None; + if let Some(subsurface) = client.subsurfaces.get_mut(&object_id) { + match opcode { + protocol::WL_SUBSURFACE_SET_POSITION if payload.len() >= 8 => { + subsurface.x = + i32::from_le_bytes([payload[0], payload[1], payload[2], payload[3]]); + subsurface.y = + i32::from_le_bytes([payload[4], payload[5], payload[6], payload[7]]); + if let Some(surface) = client.surfaces.get_mut(&subsurface.surface_id) { + surface.x = subsurface.x.max(0) as u32; + surface.y = subsurface.y.max(0) as u32; + } + } + protocol::WL_SUBSURFACE_SET_SYNC => subsurface.sync = true, + protocol::WL_SUBSURFACE_SET_DESYNC => subsurface.sync = false, + protocol::WL_SUBSURFACE_PLACE_ABOVE | protocol::WL_SUBSURFACE_PLACE_BELOW + if payload.len() >= 4 => + { + let sibling_surface_id = + u32::from_le_bytes([payload[0], payload[1], payload[2], payload[3]]); + restack = Some(( + subsurface.surface_id, + sibling_surface_id, + opcode == protocol::WL_SUBSURFACE_PLACE_ABOVE, + )); + } + _ => {} + } + } + if let Some((surface_id, sibling_surface_id, place_above)) = restack { + Compositor::stack_surface_relative( + client, + surface_id, + sibling_surface_id, + place_above, + ); + } + } + + Ok(()) + } +} diff --git a/local/recipes/wayland/redbear-compositor/source/src/protocol.rs b/local/recipes/wayland/redbear-compositor/source/src/protocol.rs new file mode 100644 index 0000000000..66557699c7 --- /dev/null +++ b/local/recipes/wayland/redbear-compositor/source/src/protocol.rs @@ -0,0 +1,177 @@ +pub const WL_DISPLAY_SYNC: u16 = 0; +pub const WL_DISPLAY_GET_REGISTRY: u16 = 1; +#[allow(dead_code)] +pub const WL_DISPLAY_ERROR: u16 = 0; +pub const WL_DISPLAY_DELETE_ID: u16 = 2; + +pub const WL_REGISTRY_BIND: u16 = 0; +pub const WL_REGISTRY_GLOBAL: u16 = 0; +pub const WL_REGISTRY_GLOBAL_REMOVE: u16 = 1; + +pub const WL_FIXES_DESTROY: u16 = 0; +pub const WL_FIXES_DESTROY_REGISTRY: u16 = 1; +pub const WL_FIXES_ACK_GLOBAL_REMOVE: u16 = 2; + +pub const WL_COMPOSITOR_CREATE_SURFACE: u16 = 0; +pub const WL_COMPOSITOR_CREATE_REGION: u16 = 1; + +pub const WL_SHM_CREATE_POOL: u16 = 0; +pub const WL_SHM_RELEASE: u16 = 1; +pub const WL_SHM_FORMAT: u16 = 0; + +pub const WL_SHM_POOL_CREATE_BUFFER: u16 = 0; +pub const WL_SHM_POOL_DESTROY: u16 = 1; +pub const WL_SHM_POOL_RESIZE: u16 = 2; + +pub const WL_BUFFER_DESTROY: u16 = 0; +pub const WL_BUFFER_RELEASE: u16 = 0; + +pub const WL_SURFACE_DESTROY: u16 = 0; +pub const WL_SURFACE_ATTACH: u16 = 1; +pub const WL_SURFACE_DAMAGE: u16 = 2; +pub const WL_SURFACE_FRAME: u16 = 3; +pub const WL_SURFACE_SET_OPAQUE_REGION: u16 = 4; +pub const WL_SURFACE_SET_INPUT_REGION: u16 = 5; +pub const WL_SURFACE_COMMIT: u16 = 6; +pub const WL_REGION_DESTROY: u16 = 0; +pub const WL_REGION_ADD: u16 = 1; +pub const WL_REGION_SUBTRACT: u16 = 2; + +pub const WL_SHELL_GET_SHELL_SURFACE: u16 = 0; + +pub const WL_SHELL_SURFACE_PONG: u16 = 0; +pub const WL_SHELL_SURFACE_MOVE: u16 = 1; +pub const WL_SHELL_SURFACE_SET_TOPLEVEL: u16 = 2; +pub const WL_SHELL_SURFACE_SET_TRANSIENT: u16 = 3; +pub const WL_SHELL_SURFACE_SET_FULLSCREEN: u16 = 4; +pub const WL_SHELL_SURFACE_SET_POPUP: u16 = 5; +pub const WL_SHELL_SURFACE_SET_MAXIMIZED: u16 = 6; +#[allow(dead_code)] +pub const WL_SHELL_SURFACE_PING: u16 = 0; +#[allow(dead_code)] +pub const WL_SHELL_SURFACE_CONFIGURE: u16 = 1; +pub const WL_SHELL_SURFACE_SET_TITLE: u16 = 8; +pub const WL_SHELL_SURFACE_SET_CLASS: u16 = 9; + +pub const XDG_WM_BASE_DESTROY: u16 = 0; +pub const XDG_WM_BASE_CREATE_POSITIONER: u16 = 1; +pub const XDG_WM_BASE_GET_XDG_SURFACE: u16 = 2; +pub const XDG_WM_BASE_PONG: u16 = 3; +pub const XDG_WM_BASE_PING: u16 = 0; + +pub const XDG_SURFACE_DESTROY: u16 = 0; +pub const XDG_SURFACE_GET_TOPLEVEL: u16 = 1; +pub const XDG_SURFACE_GET_POPUP: u16 = 2; +pub const XDG_SURFACE_SET_WINDOW_GEOMETRY: u16 = 3; +pub const XDG_SURFACE_ACK_CONFIGURE: u16 = 4; +pub const XDG_SURFACE_CONFIGURE: u16 = 0; + +pub const XDG_TOPLEVEL_CONFIGURE: u16 = 0; +pub const XDG_TOPLEVEL_DESTROY: u16 = 0; +pub const XDG_TOPLEVEL_SET_PARENT: u16 = 1; +pub const XDG_TOPLEVEL_SET_TITLE: u16 = 2; +pub const XDG_TOPLEVEL_SET_APP_ID: u16 = 3; +pub const XDG_TOPLEVEL_SHOW_WINDOW_MENU: u16 = 4; +pub const XDG_TOPLEVEL_MOVE: u16 = 5; +pub const XDG_TOPLEVEL_RESIZE: u16 = 6; +pub const XDG_TOPLEVEL_SET_MAX_SIZE: u16 = 7; +pub const XDG_TOPLEVEL_SET_MIN_SIZE: u16 = 8; +pub const XDG_TOPLEVEL_SET_MAXIMIZED: u16 = 9; +pub const XDG_TOPLEVEL_UNSET_MAXIMIZED: u16 = 10; +pub const XDG_TOPLEVEL_SET_FULLSCREEN: u16 = 11; +pub const XDG_TOPLEVEL_UNSET_FULLSCREEN: u16 = 12; +pub const XDG_TOPLEVEL_SET_MINIMIZED: u16 = 13; + +pub const XDG_POSITIONER_DESTROY: u16 = 0; +pub const XDG_POSITIONER_SET_SIZE: u16 = 1; +pub const XDG_POSITIONER_SET_ANCHOR_RECT: u16 = 2; +pub const XDG_POSITIONER_SET_ANCHOR: u16 = 3; +pub const XDG_POSITIONER_SET_GRAVITY: u16 = 4; +pub const XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT: u16 = 5; +pub const XDG_POSITIONER_SET_OFFSET: u16 = 6; + +pub const XDG_POPUP_DESTROY: u16 = 0; +pub const XDG_POPUP_GRAB: u16 = 1; +pub const XDG_POPUP_REPOSITION: u16 = 2; +pub const XDG_POPUP_CONFIGURE: u16 = 0; + +pub const WL_SEAT_GET_POINTER: u16 = 0; +pub const WL_SEAT_GET_KEYBOARD: u16 = 1; +pub const WL_SEAT_GET_TOUCH: u16 = 2; +pub const WL_SEAT_RELEASE: u16 = 3; +pub const WL_SEAT_CAPABILITIES: u16 = 0; +pub const WL_SEAT_NAME: u16 = 1; + +pub const WL_POINTER_RELEASE: u16 = 0; +pub const WL_POINTER_ENTER: u16 = 0; +pub const WL_POINTER_LEAVE: u16 = 1; +pub const WL_KEYBOARD_RELEASE: u16 = 0; +pub const WL_TOUCH_RELEASE: u16 = 0; +pub const WL_KEYBOARD_ENTER: u16 = 1; +pub const WL_KEYBOARD_LEAVE: u16 = 2; +pub const WL_KEYBOARD_MODIFIERS: u16 = 4; +pub const WL_KEYBOARD_REPEAT_INFO: u16 = 5; + +pub const WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP: u32 = 0; + +pub const WL_DATA_DEVICE_MANAGER_CREATE_DATA_SOURCE: u16 = 0; +pub const WL_DATA_DEVICE_MANAGER_GET_DATA_DEVICE: u16 = 1; +pub const WL_DATA_SOURCE_OFFER: u16 = 0; +pub const WL_DATA_SOURCE_DESTROY: u16 = 1; +pub const WL_DATA_SOURCE_SET_ACTIONS: u16 = 2; +pub const WL_DATA_DEVICE_START_DRAG: u16 = 0; +pub const WL_DATA_DEVICE_SET_SELECTION: u16 = 1; +pub const WL_DATA_DEVICE_RELEASE: u16 = 2; + +#[allow(dead_code)] +pub const WL_KEYBOARD_KEYMAP: u16 = 0; +#[allow(dead_code)] +pub const WL_KEYBOARD_KEY: u16 = 3; + +pub const WL_OUTPUT_GEOMETRY: u16 = 0; +pub const WL_OUTPUT_MODE: u16 = 1; +pub const WL_OUTPUT_DONE: u16 = 2; +pub const WL_OUTPUT_SCALE: u16 = 3; +pub const WL_OUTPUT_RELEASE: u16 = 0; +pub const WL_OUTPUT_NAME: u16 = 4; +pub const WL_OUTPUT_DESCRIPTION: u16 = 5; + +pub const WL_CALLBACK_DONE: u16 = 0; + +pub const WL_SHM_FORMAT_XRGB8888: u32 = 1; +pub const WL_SHM_FORMAT_ARGB8888: u32 = 0; + +pub const OBJECT_TYPE_WL_DISPLAY: u32 = 1; +pub const OBJECT_TYPE_WL_REGISTRY: u32 = 2; +pub const OBJECT_TYPE_WL_COMPOSITOR: u32 = 3; +pub const OBJECT_TYPE_WL_SHM: u32 = 4; +pub const OBJECT_TYPE_WL_SHELL: u32 = 5; +pub const OBJECT_TYPE_WL_SEAT: u32 = 6; +pub const OBJECT_TYPE_WL_OUTPUT: u32 = 7; +pub const OBJECT_TYPE_XDG_WM_BASE: u32 = 8; +pub const OBJECT_TYPE_WL_SURFACE: u32 = 9; +pub const OBJECT_TYPE_WL_BUFFER: u32 = 10; +pub const OBJECT_TYPE_WL_SHELL_SURFACE: u32 = 11; +pub const OBJECT_TYPE_XDG_SURFACE: u32 = 12; +pub const OBJECT_TYPE_XDG_TOPLEVEL: u32 = 13; +pub const OBJECT_TYPE_WL_SHM_POOL: u32 = 14; +pub const OBJECT_TYPE_WL_POINTER: u32 = 15; +pub const OBJECT_TYPE_WL_KEYBOARD: u32 = 16; +pub const OBJECT_TYPE_WL_DATA_DEVICE_MANAGER: u32 = 17; +pub const OBJECT_TYPE_WL_SUBCOMPOSITOR: u32 = 18; +pub const OBJECT_TYPE_WL_DATA_DEVICE: u32 = 19; +pub const OBJECT_TYPE_WL_SUBSURFACE: u32 = 20; +pub const OBJECT_TYPE_WL_FIXES: u32 = 21; +pub const OBJECT_TYPE_WL_REGION: u32 = 22; +pub const OBJECT_TYPE_WL_TOUCH: u32 = 23; +pub const OBJECT_TYPE_WL_DATA_SOURCE: u32 = 24; +pub const OBJECT_TYPE_XDG_POSITIONER: u32 = 25; +pub const OBJECT_TYPE_XDG_POPUP: u32 = 26; + +pub const WL_SUBCOMPOSITOR_GET_SUBSURFACE: u16 = 1; +pub const WL_SUBSURFACE_DESTROY: u16 = 0; +pub const WL_SUBSURFACE_SET_POSITION: u16 = 1; +pub const WL_SUBSURFACE_PLACE_ABOVE: u16 = 2; +pub const WL_SUBSURFACE_PLACE_BELOW: u16 = 3; +pub const WL_SUBSURFACE_SET_SYNC: u16 = 4; +pub const WL_SUBSURFACE_SET_DESYNC: u16 = 5; diff --git a/local/recipes/wayland/redbear-compositor/source/src/state.rs b/local/recipes/wayland/redbear-compositor/source/src/state.rs new file mode 100644 index 0000000000..4053d772eb --- /dev/null +++ b/local/recipes/wayland/redbear-compositor/source/src/state.rs @@ -0,0 +1,198 @@ +use std::collections::{HashMap, HashSet}; + +pub struct Global { + pub name: u32, + pub interface: String, + pub version: u32, +} + +pub struct ShmPool { + pub file: std::fs::File, + pub size: usize, +} + +#[derive(Clone)] +pub struct Buffer { + pub pool_id: u32, + pub offset: u32, + pub width: u32, + pub height: u32, + pub stride: u32, + pub _format: u32, +} + +#[derive(Clone)] +pub struct Surface { + pub buffer: Option, + pub pending_buffer_id: Option, + pub committed_buffer_id: Option, + pub x: u32, + pub y: u32, + pub _width: u32, + pub _height: u32, + pub geometry: Option, + pub role: Option, + pub mapped: bool, +} + +#[derive(Clone, Copy)] +pub struct WindowGeometry { + pub x: i32, + pub y: i32, + pub width: i32, + pub height: i32, +} + +#[derive(Clone, Default)] +pub struct ConfigureState { + pub pending_serial: Option, + pub last_acked_serial: Option, + pub configured: bool, +} + +impl ConfigureState { + pub fn note_sent(&mut self, serial: u32) { + self.pending_serial = Some(serial); + } + + pub fn ack(&mut self, serial: u32) { + self.last_acked_serial = Some(serial); + if self.pending_serial == Some(serial) { + self.configured = true; + } + } + + pub fn can_present(&self) -> bool { + self.pending_serial.is_none() || self.configured + } +} + +#[derive(Clone, Default)] +pub struct ToplevelState { + pub object_id: u32, + pub parent_id: Option, + pub title: Option, + pub app_id: Option, + pub min_size: Option<(i32, i32)>, + pub max_size: Option<(i32, i32)>, + pub maximized: bool, + pub fullscreen: bool, + pub minimized: bool, + pub configure: ConfigureState, +} + +#[derive(Clone, Default)] +pub struct PopupState { + pub object_id: u32, + pub parent_id: Option, + pub positioner_id: Option, + pub grab_serial: Option, + pub configure: ConfigureState, +} + +#[derive(Clone)] +pub enum SurfaceRole { + Toplevel(ToplevelState), + Popup(PopupState), + Shell(ShellSurfaceState), +} + +impl SurfaceRole { + pub fn can_present(&self) -> bool { + match self { + Self::Toplevel(state) => state.configure.can_present(), + Self::Popup(state) => { + state.object_id != 0 + && state.parent_id.is_some() + && state.positioner_id.is_some() + && state.configure.can_present() + } + Self::Shell(state) => state.object_id != 0, + } + } + + pub fn ack_configure(&mut self, serial: u32) { + match self { + Self::Toplevel(state) => state.configure.ack(serial), + Self::Popup(state) => state.configure.ack(serial), + Self::Shell(_) => { + let _ = serial; + } + } + } +} + +#[derive(Clone, Copy, Default)] +pub enum ShellSurfaceKind { + #[default] + None, + Toplevel, + Popup, + Transient, + Fullscreen, + Maximized, +} + +#[derive(Clone, Default)] +pub struct ShellSurfaceState { + pub object_id: u32, + pub surface_id: u32, + pub kind: ShellSurfaceKind, + pub title: Option, + pub class: Option, + pub parent_surface_id: Option, + pub popup_serial: Option, + pub last_ping_serial: Option, +} + +#[derive(Clone, Default)] +pub struct PositionerState { + pub size: Option<(i32, i32)>, + pub anchor_rect: Option<(i32, i32, i32, i32)>, + pub anchor: Option, + pub gravity: Option, + pub constraint_adjustment: Option, + pub offset: Option<(i32, i32)>, +} + +#[derive(Clone, Default)] +pub struct DataSourceState { + pub mime_types: Vec, + pub actions: Option, +} + +#[derive(Clone, Default)] +pub struct DataDeviceState { + pub selection_source: Option, + pub drag_source: Option, +} + +#[derive(Clone, Default)] +pub struct SubsurfaceState { + pub surface_id: u32, + pub parent_surface_id: u32, + pub x: i32, + pub y: i32, + pub sync: bool, +} + +pub struct ClientState { + pub objects: HashMap, + pub object_versions: HashMap, + pub surfaces: HashMap, + pub surface_order: Vec, + pub buffers: HashMap, + pub shm_pools: HashMap, + pub positioners: HashMap, + pub shell_surfaces: HashMap, + pub data_sources: HashMap, + pub data_devices: HashMap, + pub subsurfaces: HashMap, + pub keyboard_object_id: Option, + pub pointer_object_id: Option, + pub touch_object_id: Option, + pub keyboard_focus_surface: Option, + pub pointer_focus_surface: Option, + pub acked_global_removals: HashSet, + pub _next_id: u32, +} diff --git a/local/recipes/wayland/redbear-compositor/source/src/wire.rs b/local/recipes/wayland/redbear-compositor/source/src/wire.rs new file mode 100644 index 0000000000..9a94014541 --- /dev/null +++ b/local/recipes/wayland/redbear-compositor/source/src/wire.rs @@ -0,0 +1,197 @@ +use std::collections::VecDeque; +use std::io::Write; +use std::mem; +use std::os::fd::{AsRawFd, RawFd}; +use std::os::unix::net::UnixStream; + +pub fn push_u32(buf: &mut Vec, value: u32) { + buf.extend_from_slice(&value.to_le_bytes()); +} + +pub fn push_i32(buf: &mut Vec, value: i32) { + buf.extend_from_slice(&value.to_le_bytes()); +} + +pub fn push_header(buf: &mut Vec, object_id: u32, opcode: u16, payload_len: usize) { + push_u32(buf, object_id); + let size = (8 + payload_len) as u32; + push_u32(buf, (size << 16) | u32::from(opcode)); +} + +pub fn pad_to_4(buf: &mut Vec) { + while buf.len() % 4 != 0 { + buf.push(0); + } +} + +pub fn push_wayland_string(buf: &mut Vec, value: &str) { + let bytes = value.as_bytes(); + push_u32(buf, (bytes.len() + 1) as u32); + buf.extend_from_slice(bytes); + buf.push(0); + pad_to_4(buf); +} + +pub fn read_u32(data: &[u8], cursor: &mut usize) -> Result { + if *cursor + 4 > data.len() { + return Err(String::from("unexpected end of message while reading u32")); + } + + let value = u32::from_le_bytes([ + data[*cursor], + data[*cursor + 1], + data[*cursor + 2], + data[*cursor + 3], + ]); + *cursor += 4; + Ok(value) +} + +pub fn read_wayland_string(data: &[u8], cursor: &mut usize) -> Result { + if *cursor + 4 > data.len() { + return Err(String::from( + "unexpected end of message while reading string length", + )); + } + let length = u32::from_le_bytes([ + data[*cursor], + data[*cursor + 1], + data[*cursor + 2], + data[*cursor + 3], + ]) as usize; + *cursor += 4; + if length == 0 { + return Ok(String::new()); + } + if *cursor + length > data.len() { + return Err(String::from( + "unexpected end of message while reading string", + )); + } + + let bytes = &data[*cursor..*cursor + length]; + let string_len = bytes + .iter() + .position(|byte| *byte == 0) + .unwrap_or(bytes.len()); + *cursor += length; + while *cursor % 4 != 0 { + *cursor += 1; + } + + std::str::from_utf8(&bytes[..string_len]) + .map(str::to_owned) + .map_err(|err| format!("invalid UTF-8 in Wayland string: {err}")) +} + +pub fn recv_with_rights( + stream: &mut UnixStream, + data: &mut [u8], +) -> std::io::Result<(usize, VecDeque)> { + let mut iov = libc::iovec { + iov_base: data.as_mut_ptr().cast(), + iov_len: data.len(), + }; + let mut control = [0u8; 256]; + let mut header = libc::msghdr { + msg_name: std::ptr::null_mut(), + msg_namelen: 0, + msg_iov: &mut iov, + msg_iovlen: 1, + msg_control: control.as_mut_ptr().cast(), + msg_controllen: control.len(), + msg_flags: 0, + }; + + let read = unsafe { libc::recvmsg(stream.as_raw_fd(), &mut header, 0) }; + if read < 0 { + return Err(std::io::Error::last_os_error()); + } + + let mut fds = VecDeque::new(); + let mut cmsg = unsafe { libc::CMSG_FIRSTHDR(&header) }; + while !cmsg.is_null() { + let is_rights = unsafe { + (*cmsg).cmsg_level == libc::SOL_SOCKET && (*cmsg).cmsg_type == libc::SCM_RIGHTS + }; + if is_rights { + let data_len = unsafe { (*cmsg).cmsg_len as usize } + .saturating_sub(mem::size_of::()); + let fd_count = data_len / mem::size_of::(); + let data_ptr = unsafe { libc::CMSG_DATA(cmsg).cast::() }; + for index in 0..fd_count { + fds.push_back(unsafe { *data_ptr.add(index) }); + } + } + cmsg = unsafe { libc::CMSG_NXTHDR(&header, cmsg) }; + } + + Ok((read as usize, fds)) +} + +pub fn send_with_rights( + stream: &mut UnixStream, + object_id: u32, + opcode: u16, + payload: &[u8], + fds: &[RawFd], +) -> std::io::Result<()> { + let size = 8 + payload.len(); + let mut msg = Vec::with_capacity(size); + push_u32(&mut msg, object_id); + push_u32(&mut msg, ((size as u32) << 16) | u32::from(opcode)); + msg.extend_from_slice(payload); + + if fds.is_empty() { + stream.write_all(&msg)?; + return Ok(()); + } + + let mut iov = libc::iovec { + iov_base: msg.as_mut_ptr().cast(), + iov_len: msg.len(), + }; + let control_len = + unsafe { libc::CMSG_SPACE((fds.len() * mem::size_of::()) as u32) as usize }; + let mut control = vec![0u8; control_len]; + let header = libc::msghdr { + msg_name: std::ptr::null_mut(), + msg_namelen: 0, + msg_iov: &mut iov, + msg_iovlen: 1, + msg_control: control.as_mut_ptr().cast(), + msg_controllen: control.len(), + msg_flags: 0, + }; + + unsafe { + let cmsg = libc::CMSG_FIRSTHDR(&header); + if cmsg.is_null() { + return Err(std::io::Error::other( + "failed to allocate SCM_RIGHTS header", + )); + } + (*cmsg).cmsg_level = libc::SOL_SOCKET; + (*cmsg).cmsg_type = libc::SCM_RIGHTS; + (*cmsg).cmsg_len = libc::CMSG_LEN((fds.len() * mem::size_of::()) as u32) as _; + std::ptr::copy_nonoverlapping( + fds.as_ptr().cast::(), + libc::CMSG_DATA(cmsg).cast::(), + fds.len() * mem::size_of::(), + ); + } + + let written = unsafe { libc::sendmsg(stream.as_raw_fd(), &header, 0) }; + if written < 0 { + return Err(std::io::Error::last_os_error()); + } + if written as usize != msg.len() { + return Err(std::io::Error::other(format!( + "short sendmsg write: expected {}, got {}", + msg.len(), + written + ))); + } + + Ok(()) +}