commit 6c8fc2b35f7077d59e12fe8b899edfb60540dd60 Author: stickynotememo Date: Wed Nov 19 16:36:43 2025 +1100 basic wayland window diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..afb64d7 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,407 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "cc" +version = "1.2.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "memfile" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f64636fdb65a5f0740f920c4281f3dbb76a71e25e25914b6d27000739897d40e" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "nix" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" +dependencies = [ + "bitflags 1.3.2", + "cc", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.37.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags 2.10.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "shared_memory" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba8593196da75d9dc4f69349682bd4c2099f8cde114257d1ef7ef1b33d1aba54" +dependencies = [ + "cfg-if", + "libc", + "nix", + "rand", + "win-sys", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "syn" +version = "2.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wayland-backend" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" +dependencies = [ + "bitflags 2.10.0", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" +dependencies = [ + "bitflags 2.10.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "win-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b7b128a98c1cfa201b09eb49ba285887deb3cbe7466a98850eb1adabb452be5" +dependencies = [ + "windows", +] + +[[package]] +name = "windows" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45296b64204227616fdbf2614cefa4c236b98ee64dfaaaa435207ed99fe7829f" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" + +[[package]] +name = "windows_i686_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" + +[[package]] +name = "windows_i686_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" + +[[package]] +name = "wolf_render" +version = "0.1.0" +dependencies = [ + "libc", + "memfile", + "shared_memory", + "wayland-client", + "wayland-protocols", +] + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d8ae379 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "wolf_render" +version = "0.1.0" +edition = "2024" + +[dependencies] +libc = "0.2.177" +memfile = "0.3.2" +shared_memory = "0.12.4" +wayland-client = "0.31" +wayland-protocols = { version = "0.32.9", features = ["client"] } diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..af02485 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,313 @@ +#![feature(more_qualified_paths)] +use std::time::Duration; +use std::{ffi::CString, thread::sleep}; +use std::os::fd::BorrowedFd; + +use wayland_client::{ + Connection, Dispatch, EventQueue, Proxy, backend::ObjectId, delegate_noop, protocol::{ + wl_buffer::{self, WlBuffer}, + wl_compositor::{self, WlCompositor}, + wl_registry, wl_shell, + wl_shm::{self, Format, WlShm}, + wl_shm_pool::{self, WlShmPool}, + wl_surface::{self, Event, WlSurface}, + } +}; + +use libc::{O_CREAT, O_RDWR, PROT_READ, PROT_WRITE, S_IROTH, S_IRWXU, ftruncate, mmap, shm_open}; + +use wayland_protocols::xdg::{ + self, + shell::client::{ + xdg_surface::{self, XdgSurface}, + xdg_toplevel::XdgToplevel, + xdg_wm_base::{self, XdgWmBase}, + }, +}; + +const WIDTH: i32 = 960; +const HEIGHT: i32 = 540; + +#[derive(Debug)] +struct State { + exit: bool, + ready: bool, + compositor: Option, + surface: Option, + shm: Option, + formats: Vec, + buffer: Option, + wm_base: Option, + xdg_surface: Option, + toplevel_surface: Option, + pool: Option, +} + +impl Dispatch for State { + fn event( + state: &mut Self, + proxy: &wl_registry::WlRegistry, + event: ::Event, + data: &(), + conn: &Connection, + qhandle: &wayland_client::QueueHandle, + ) { + // Iterate over the globals given by the registry. + // Use the details from the global (name & version) to bind the proxy to the state + // variable, specifying which global is beiong bound in the turbofish of proxy.bind() + if let wl_registry::Event::Global { + name, + interface, + version, + } = event + { + match interface.as_str() { + "wl_compositor" => { + let compositor = proxy + .bind::(name, version, qhandle, *data); + let surface = compositor.create_surface(qhandle, *data); + state.compositor = Some(compositor); + state.surface = Some(surface); + } + "wl_shm" => { + let shm = proxy.bind::(name, version, qhandle, *data); + state.shm = Some(shm); + } + "xdg_wm_base" => { + let wm_base = + proxy.bind::(name, version, qhandle, *data); + state.wm_base = Some(wm_base); + } + _ => { + // do nothing + } + }; + } + } +} + +impl Dispatch for State { + fn event( + state: &mut Self, + proxy: &WlShm, + event: ::Event, + data: &(), + conn: &Connection, + qhandle: &wayland_client::QueueHandle, + ) { + if let ::Event::Format { format } = event { + if let None = state.pool { + state.formats.push( + format + .into_result() + .expect("Couldn't understand format, or it was unknown"), + ); + // Any wayland client must support Xrgb8888 and Argb8888 + let channels: i32 = if state.formats.contains(&Format::Xrgb8888) { + 4 + } else { + 4 + }; // we know that the supported formats are only the two above, so we can safely assume that any format which is not Xrgb8888 is Argb8888, requiring 4 channels. + // FIX: 3 channels won't work, even with Xrgb8888 for some reason + // TODO: Update for more channels + + let size: i32 = (WIDTH * HEIGHT * channels * 2).try_into().unwrap(); + let name = CString::new("/wolf_renderer_buffer").expect("CString::new() failed"); // CString::new can only fail if a null byte is in the string. This string should never cause errors + + let shm_fd = unsafe { + // SAFETY: We know that that at least a few megabytes of memory + // should be able to be allocated for the virtual buffer. + // Data races should not be possible as the compositor will + // not modify the buffer. (Non owners do not have write + // permissions) + + let fildes = shm_open(name.as_ptr(), O_RDWR | O_CREAT, S_IRWXU | S_IROTH); + if fildes <= 0 { + panic!("Could not open SHM object. Error code: N/A") // TODO: implement errno reading + }; + ftruncate(fildes, size.into()); + BorrowedFd::borrow_raw(fildes) + }; + + let shm_pool = proxy.create_pool(shm_fd, size.try_into().unwrap(), qhandle, *data); + // TODO: Move drawing logic to event loop (double flush) + state.pool = Some(shm_pool); + }; + } else { + unimplemented!() + } + } +} + +impl Dispatch for State { + fn event( + state: &mut Self, + proxy: &WlBuffer, + event: ::Event, + data: &(), + conn: &Connection, + qhandle: &wayland_client::QueueHandle, + ) { + } +} + +impl Dispatch for State { + fn event( + state: &mut Self, + proxy: &XdgWmBase, + event: ::Event, + data: &(), + conn: &Connection, + qhandle: &wayland_client::QueueHandle, + ) { + if let xdg_wm_base::Event::Ping { serial } = event { + state.wm_base.as_ref().unwrap().pong(serial); // Ok to unwrap as a ping won't be recieved until + // all the globals are bound (including xdg_wm_base) + // TODO: figure out as_ref + } else { + unreachable!(); + }; + } +} + +impl Dispatch for State { + fn event( + state: &mut Self, + proxy: &XdgSurface, + event: ::Event, + data: &(), + conn: &Connection, + qhandle: &wayland_client::QueueHandle, + ) { + if let ::Event::Configure { serial } = event { + proxy.ack_configure(serial); + } + } +} + +impl Dispatch for State { + fn event( + state: &mut Self, + proxy: &XdgToplevel, + event: ::Event, + data: &(), + conn: &Connection, + qhandle: &wayland_client::QueueHandle, + ) { + if let ::Event::Configure { + width, + height, + states, + } = event + { + // state.xdg_surface.unwrap().ack_configure(serial); + // FIX: Figure out what serial I need for ack_configure with a toplevel + // xdg_surface + } + } +} + +impl Dispatch for State { + fn event( + state: &mut Self, + proxy: &WlSurface, + event: ::Event, + data: &(), + conn: &Connection, + qhandle: &wayland_client::QueueHandle, + ) { + } +} +delegate_noop!(State: wl_shm_pool::WlShmPool); +delegate_noop!(State: wl_compositor::WlCompositor); + +fn main() { + let Ok(conn) = Connection::connect_to_env() else { + panic!("Couldn't connect to Wayland environment"); + }; + + let mut state = State { + exit: false, + ready: false, + compositor: None, + surface: None, + shm: None, + formats: vec![], + buffer: None, + wm_base: None, + xdg_surface: None, + toplevel_surface: None, + pool: None, + }; + + let mut event_queue: EventQueue = conn.new_event_queue(); + + let display = conn.display(); + let qh = &event_queue.handle(); + + display.get_registry(&qh, ()); + while !state.exit { + event_queue.roundtrip(&mut state).unwrap(); + event_queue.flush().expect("hopefully flushed"); // TODO: expect + // Assign roles to the surface as required, then attach the buffer, damage and commit + if let None = &state.toplevel_surface { + if let (Some(base), Some(surface), Some(buffer)) = + (&state.wm_base, &state.surface, &state.buffer) + { + // Buffer is required because it is to be attached to the surface. + let xdg_surface = base.get_xdg_surface(surface, qh, ()); // HACK: udata =/ () + let toplevel_surface = xdg_surface.get_toplevel(qh, ()); // HACK: udata =/ () + + state.xdg_surface = Some(xdg_surface); + state.toplevel_surface = Some(toplevel_surface); + + surface.attach(Some(buffer), 0, 0); + surface.damage(0, 0, WIDTH, HEIGHT); + surface.commit(); + }; + } + state.ready = if let (Some(_), Some(_), Some(_), Some(_), Some(_)) = ( + &state.compositor, + &state.surface, + &state.shm, + &state.buffer, + &state.wm_base, + ) { + true + } else { + false + }; + + if let Some(shm_pool) = &state.pool { + let channels: i32 = if state.formats.contains(&Format::Xrgb8888) { + 4 + } else { + 4 + }; // we know that the supported formats are only the two above, so we can safely assume that any format which is not Xrgb8888 is Argb8888, requiring 4 channels. + // FIX: 3 channels won't work, even with Xrgb8888 for some reason + // TODO: Update for more channels + + let size: i32 = (WIDTH * HEIGHT * channels * 2).try_into().unwrap(); + let buffer = shm_pool.create_buffer( + 0, + WIDTH.try_into().unwrap(), + HEIGHT.try_into().unwrap(), + (WIDTH * channels).try_into().unwrap(), + if state.formats.contains(&Format::Xrgb8888) { + Format::Xrgb8888 + } else { + Format::Argb8888 + }, + qh, + (), + ); + state.buffer = Some(buffer.clone()); + } + + if state.ready { + dbg!("ready"); + sleep(Duration::from_secs(2)); + return; + }; + } +}