├── input-event-codes-sys ├── import.h ├── src │ └── lib.rs ├── Cargo.toml └── build.rs ├── .cargo └── config.toml ├── .gitignore ├── .gitmodules ├── rust-toolchain.toml ├── wayland-tools ├── parser │ ├── src │ │ ├── lib.rs │ │ └── protocol.rs │ ├── Cargo.toml │ └── tests │ │ └── parsing.rs ├── codegen │ ├── tests │ │ └── generate.rs │ └── Cargo.toml ├── macros │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── scanner │ ├── Cargo.toml │ └── src │ └── lib.rs ├── runa-wayland-types ├── Cargo.toml └── src │ └── lib.rs ├── runa-orbiter ├── src │ ├── time.rs │ ├── renderer_capability.rs │ ├── globals │ │ └── xdg_shell.rs │ ├── lib.rs │ ├── shell │ │ ├── surface │ │ │ └── roles │ │ │ │ ├── mod.rs │ │ │ │ ├── xdg.rs │ │ │ │ └── subsurface.rs │ │ ├── mod.rs │ │ ├── buffers.rs │ │ └── output.rs │ ├── objects │ │ ├── mod.rs │ │ └── shm.rs │ └── utils │ │ └── mod.rs └── Cargo.toml ├── runa-wayland-protocols ├── src │ └── lib.rs ├── Cargo.toml └── build.rs ├── runa-io-traits └── Cargo.toml ├── rustfmt.toml ├── crescent ├── shaders │ └── shader.wgsl └── Cargo.toml ├── runa-macros ├── Cargo.toml └── tests │ └── generate.rs ├── runa-io ├── Cargo.toml └── src │ ├── utils.rs │ └── buf.rs ├── Cargo.toml ├── runa-core ├── Cargo.toml └── src │ ├── events │ ├── mod.rs │ ├── single_state.rs │ ├── aggregate.rs │ └── broadcast.rs │ ├── utils.rs │ ├── server │ ├── mod.rs │ └── traits.rs │ ├── error.rs │ ├── client │ └── mod.rs │ ├── globals.rs │ └── provide_any.rs ├── flake.nix ├── README.md ├── flake.lock └── .github └── workflows └── ci.yml /input-event-codes-sys/import.h: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /input-event-codes-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 2 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [env] 2 | CARGO_WORKSPACE_DIR = { value = "", relative = true } 3 | 4 | [build] 5 | rustflags = ["-Clink-args=-fuse-ld=mold"] 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /testbench 3 | /Cargo.lock 4 | .gdb_history 5 | *.skip 6 | vgcore.* 7 | dhat.out.* 8 | perf.data 9 | perf.data.old 10 | /.direnv 11 | rustc-ice-* 12 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "protocols/wayland-protocols"] 2 | path = runa-wayland-protocols/spec/wayland-protocols 3 | url = https://gitlab.freedesktop.org/wayland/wayland-protocols 4 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | components = [ "rustfmt", "rust-src", "miri" ] 4 | targets = [ "x86_64-unknown-linux-gnu" ] 5 | profile = "minimal" 6 | 7 | -------------------------------------------------------------------------------- /wayland-tools/parser/src/lib.rs: -------------------------------------------------------------------------------- 1 | /// Crates for parsing the wayland xml specification files 2 | /// 3 | /// This crate is mostly lifted from wayland-rs/wayland-scanner. 4 | pub mod parse; 5 | pub mod protocol; 6 | -------------------------------------------------------------------------------- /runa-wayland-types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "runa-wayland-types" 3 | description = "Types defined by the Wayland protocol" 4 | repository = "https://github.com/yshui/runa" 5 | license = "MIT OR Apache-2.0" 6 | version = "0.1.0" 7 | edition = "2021" 8 | 9 | [dependencies] 10 | fixed = "1.19.0" 11 | -------------------------------------------------------------------------------- /runa-orbiter/src/time.rs: -------------------------------------------------------------------------------- 1 | lazy_static::lazy_static! { 2 | static ref TIME: std::time::Instant = std::time::Instant::now(); 3 | } 4 | 5 | /// Elapsed time from an unspecified point in the past. This time point is fixed 6 | /// during the lifetime of the program. 7 | pub fn elapsed() -> std::time::Duration { 8 | TIME.elapsed() 9 | } 10 | -------------------------------------------------------------------------------- /wayland-tools/parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "runa-wayland-spec-parser" 3 | description = "Parsing and representing wayland protocol files" 4 | repository = "https://github.com/yshui/runa" 5 | license = "MIT OR Apache-2.0" 6 | version = "0.1.0" 7 | edition = "2021" 8 | 9 | [dependencies] 10 | quick-xml = "0.28.1" 11 | thiserror.workspace = true 12 | -------------------------------------------------------------------------------- /wayland-tools/parser/tests/parsing.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn parse() { 3 | let file = std::fs::File::open(concat!( 4 | env!("CARGO_WORKSPACE_DIR"), 5 | "/runa-wayland-protocols/spec/wayland.xml" 6 | )) 7 | .unwrap(); 8 | eprintln!( 9 | "{:?}", 10 | runa_wayland_spec_parser::parse::parse(file).unwrap() 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /input-event-codes-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "input-event-codes-sys" 3 | description = "Rust bindings for the Linux input-event-codes.h header file" 4 | repository = "https://github.com/yshui/runa" 5 | license = "MIT OR Apache-2.0" 6 | version = "0.1.0" 7 | edition = "2021" 8 | 9 | [build-dependencies] 10 | anyhow.workspace = true 11 | bindgen = "0.64.0" 12 | -------------------------------------------------------------------------------- /runa-orbiter/src/renderer_capability.rs: -------------------------------------------------------------------------------- 1 | //! Render capability. 2 | //! 3 | //! An interface for compositor implementation to tell us what its renderer can 4 | //! do. 5 | 6 | pub use runa_wayland_protocols::wayland::wl_shm::v1::enums::Format; 7 | 8 | /// The renderer's capability 9 | pub trait RendererCapability { 10 | /// List of supported buffer pixel formats 11 | fn formats(&self) -> Vec; 12 | } 13 | -------------------------------------------------------------------------------- /input-event-codes-sys/build.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | fn main() -> Result<(), anyhow::Error> { 4 | println!("cargo:rerun-if-changed=import.h"); 5 | 6 | let bindings = bindgen::builder() 7 | .header(concat!(env!("CARGO_MANIFEST_DIR"), "/import.h")) 8 | .rustified_enum(".*") 9 | .generate()?; 10 | bindings.write_to_file(Path::new(&std::env::var("OUT_DIR").unwrap()).join("bindings.rs"))?; 11 | Ok(()) 12 | } 13 | -------------------------------------------------------------------------------- /runa-wayland-protocols/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::too_many_arguments, clippy::module_inception)] 2 | include!(concat!(env!("OUT_DIR"), "/wayland_generated.rs")); 3 | pub mod stable { 4 | include!(concat!(env!("OUT_DIR"), "/stable_generated.rs")); 5 | } 6 | pub mod staging { 7 | include!(concat!(env!("OUT_DIR"), "/staging_generated.rs")); 8 | } 9 | pub mod unstable { 10 | include!(concat!(env!("OUT_DIR"), "/unstable_generated.rs")); 11 | } 12 | -------------------------------------------------------------------------------- /runa-io-traits/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "runa-io-traits" 3 | description = "Traits for communicating using the Wayland wire protocol" 4 | repository = "https://github.com/yshui/runa" 5 | license = "MIT OR Apache-2.0" 6 | version = "0.0.1-alpha1" 7 | edition = "2021" 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [dependencies] 12 | bytes.workspace = true 13 | futures-io = "0.3.26" 14 | -------------------------------------------------------------------------------- /wayland-tools/codegen/tests/generate.rs: -------------------------------------------------------------------------------- 1 | use runa_wayland_scanner_codegen::generate_protocol; 2 | #[test] 3 | fn generate() { 4 | let f = std::fs::File::open( 5 | std::path::Path::new(&std::env::var("CARGO_WORKSPACE_DIR").unwrap()) 6 | .join("runa-wayland-protocols") 7 | .join("spec") 8 | .join("wayland.xml"), 9 | ) 10 | .unwrap(); 11 | let proto = spec_parser::parse::parse(f).unwrap(); 12 | 13 | generate_protocol(&proto).unwrap(); 14 | } 15 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | binop_separator = "Back" 2 | enum_discrim_align_threshold = 40 3 | struct_field_align_threshold = 40 4 | format_code_in_doc_comments = true 5 | format_strings = true 6 | indent_style = "Block" 7 | match_arm_blocks = false 8 | match_arm_leading_pipes = "Preserve" 9 | match_block_trailing_comma = true 10 | imports_granularity = "Crate" 11 | newline_style = "Unix" 12 | overflow_delimited_expr = true 13 | reorder_impl_items = true 14 | group_imports = "StdExternalCrate" 15 | trailing_semicolon = false 16 | use_field_init_shorthand = true 17 | wrap_comments = true 18 | -------------------------------------------------------------------------------- /runa-wayland-protocols/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "runa-wayland-protocols" 3 | description = "Generated types and traits for the Wayland protocols" 4 | repository = "https://github.com/yshui/runa" 5 | license = "MIT OR Apache-2.0" 6 | version = "0.1.0" 7 | edition = "2021" 8 | 9 | [build-dependencies] 10 | runa-wayland-scanner = { version = "0.1.0", path = "../wayland-tools/scanner" } 11 | runa-wayland-spec-parser = { version = "0.1.0", path = "../wayland-tools/parser" } 12 | 13 | [dependencies] 14 | futures-executor = "0.3" 15 | futures-lite = "1.12.0" 16 | num_enum = "0.5.7" 17 | runa-wayland-scanner = { version = "0.1.0", path = "../wayland-tools/scanner" } 18 | -------------------------------------------------------------------------------- /wayland-tools/macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "runa-wayland-scanner-macros" 3 | description = "Macros for runa-wayland-scanner" 4 | repository = "https://github.com/yshui/runa" 5 | license = "MIT OR Apache-2.0" 6 | version = "0.1.0" 7 | edition = "2021" 8 | 9 | [lib] 10 | proc-macro = true 11 | 12 | [dependencies] 13 | parser = { version = "0.1.0", package = "runa-wayland-spec-parser", path = "../parser" } 14 | codegen = { version = "0.1.0", package = "runa-wayland-scanner-codegen", path = "../codegen" } 15 | syn.workspace = true 16 | proc-macro2.workspace = true 17 | proc-macro-error = { version = "1.0.4", default-features = false, features = ["syn-error"] } 18 | -------------------------------------------------------------------------------- /crescent/shaders/shader.wgsl: -------------------------------------------------------------------------------- 1 | @group(1) @binding(0) var texture: texture_2d; 2 | @group(1) @binding(1) var s: sampler; 3 | @group(0) @binding(0) var screen_size: vec2; 4 | 5 | struct VertexOutput { 6 | @builtin(position) position: vec4, 7 | @location(0) uv: vec2, 8 | } 9 | 10 | struct VertexInput { 11 | @location(0) position: vec2, 12 | @location(1) uv: vec2, 13 | } 14 | 15 | @vertex 16 | fn vs_main(input: VertexInput) -> VertexOutput { 17 | var output: VertexOutput; 18 | output.position = vec4(input.position / screen_size * 2.0 - 1.0, 0.0, 1.0); 19 | output.uv = input.uv; 20 | return output; 21 | } 22 | 23 | @fragment 24 | fn fg_main(input: VertexOutput) -> @location(0) vec4 { 25 | return textureSample(texture, s, input.uv); 26 | //return vec4(1., 1., 1., 1.); 27 | } 28 | -------------------------------------------------------------------------------- /wayland-tools/scanner/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "runa-wayland-scanner" 3 | description = "Generate Rust types and traits from Wayland XML protocol specifications" 4 | repository = "https://github.com/yshui/runa" 5 | license = "MIT OR Apache-2.0" 6 | version = "0.1.0" 7 | edition = "2021" 8 | 9 | [dependencies] 10 | bitflags.workspace = true 11 | num_enum = "0.5.7" 12 | pin-project-lite = "0.2.9" 13 | runa-wayland-types = { version = "0.1.0", path = "../../runa-wayland-types" } 14 | runa-io-traits = { version = "0.0.1-alpha1", path = "../../runa-io-traits" } 15 | futures-lite.workspace = true 16 | bytes.workspace = true 17 | thiserror.workspace = true 18 | macros = { version = "0.1.0", package = "runa-wayland-scanner-macros", path = "../macros" } 19 | codegen = { version = "0.1.0", package = "runa-wayland-scanner-codegen", path = "../codegen" } 20 | -------------------------------------------------------------------------------- /wayland-tools/codegen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "runa-wayland-scanner-codegen" 3 | description = "Generate code from wayland protocol specifications" 4 | repository = "https://github.com/yshui/runa" 5 | license = "MIT OR Apache-2.0" 6 | version = "0.1.0" 7 | edition = "2021" 8 | 9 | [dependencies] 10 | heck = "0.4.0" 11 | proc-macro2 = "1.0.43" 12 | quote = "1.0.21" 13 | rust-format = { version = "0.3.4", features = ["token_stream"] } 14 | thiserror = "1.0.33" 15 | spec-parser = { version = "0.1.0", package = "runa-wayland-spec-parser", path = "../parser" } 16 | syn = "1.0.109" 17 | linkify = "0.9.0" 18 | regex = "1.6.0" 19 | lazy_static = "1.4.0" 20 | hashbrown.workspace = true 21 | 22 | [dev-dependencies] 23 | rust-format = { version = "0.3.4", features = ["token_stream"] } 24 | spec-parser = { package = "runa-wayland-spec-parser", path = "../parser" } 25 | -------------------------------------------------------------------------------- /runa-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "runa-macros" 3 | description = "Macros for runa-core" 4 | version = "0.0.1-alpha1" 5 | repository = "https://github.com/yshui/runa" 6 | license = "MIT OR Apache-2.0" 7 | edition = "2021" 8 | 9 | [lib] 10 | proc-macro = true 11 | 12 | [dependencies] 13 | darling = "0.14.1" 14 | heck = "0.4.0" 15 | proc-macro2.workspace = true 16 | quote = "1.0.21" 17 | syn = { workspace = true, features = ["visit-mut"] } 18 | 19 | [dev-dependencies] 20 | futures-executor = "0.3" 21 | futures-lite.workspace = true 22 | runa-wayland-scanner = { version = "0.1.0", path = "../wayland-tools/scanner" } 23 | runa-wayland-types = { version = "0.1.0", path = "../runa-wayland-types" } 24 | num_enum = "0.5.7" 25 | futures-task = "0.3.24" 26 | runa-io = { version = "0.0.1-alpha1", path = "../runa-io" } 27 | bytes.workspace = true 28 | 29 | [features] 30 | default = [ "tracing" ] 31 | # logging from generated code 32 | tracing = [] 33 | -------------------------------------------------------------------------------- /wayland-tools/macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro_error::ResultExt; 2 | #[proc_macro] 3 | #[proc_macro_error::proc_macro_error] 4 | pub fn generate_protocol(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream { 5 | use std::io::Read; 6 | let tokens2 = tokens.clone(); 7 | let path = syn::parse_macro_input!(tokens2 as syn::LitStr); 8 | let path = 9 | std::path::Path::new(&std::env::var("CARGO_WORKSPACE_DIR").unwrap()).join(path.value()); 10 | let tokens: proc_macro2::TokenStream = tokens.into(); 11 | let mut file = std::fs::File::open(path) 12 | .map_err(|e| syn::Error::new_spanned(&tokens, e)) 13 | .unwrap_or_abort(); 14 | let mut contents = String::new(); 15 | file.read_to_string(&mut contents) 16 | .map_err(|e| syn::Error::new_spanned(&tokens, e)) 17 | .unwrap_or_abort(); 18 | let protocol = parser::parse::parse(contents.as_bytes()).unwrap(); 19 | codegen::generate_protocol(&protocol).unwrap().into() 20 | } 21 | -------------------------------------------------------------------------------- /runa-orbiter/src/globals/xdg_shell.rs: -------------------------------------------------------------------------------- 1 | //! Globals defined in the xdg_shell protocol. 2 | 3 | use std::future::Future; 4 | 5 | use runa_core::{ 6 | client::traits::Client, 7 | globals::{Bind, MonoGlobal}, 8 | }; 9 | use runa_wayland_protocols::stable::xdg_shell::xdg_wm_base::v5 as xdg_wm_base; 10 | 11 | /// Implementation of the `xdg_wm_base` global. 12 | #[derive(Debug, Clone, Copy)] 13 | pub struct WmBase; 14 | 15 | impl MonoGlobal for WmBase { 16 | type Object = crate::objects::xdg_shell::WmBase; 17 | 18 | const INTERFACE: &'static str = xdg_wm_base::NAME; 19 | const MAYBE_DEFAULT: Option = Some(Self); 20 | const VERSION: u32 = xdg_wm_base::VERSION; 21 | 22 | #[inline] 23 | fn new_object() -> Self::Object { 24 | crate::objects::xdg_shell::WmBase 25 | } 26 | } 27 | impl Bind for WmBase { 28 | type BindFut<'a> = impl Future> + 'a; 29 | 30 | fn bind<'a>(&'a self, _client: &'a mut Ctx, _object_id: u32) -> Self::BindFut<'a> { 31 | // TODO: setup event 32 | futures_util::future::ok(()) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /runa-io/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "runa-io" 3 | description = "Utilities for communicating with the wayland wire protocol" 4 | repository = "https://github.com/yshui/runa" 5 | license = "MIT OR Apache-2.0" 6 | version = "0.0.1-alpha1" 7 | edition = "2021" 8 | 9 | [dependencies] 10 | async-io = "1.9.0" 11 | backtrace = "0.3.66" 12 | byteorder = "1.4.3" 13 | futures-core = "0.3.24" 14 | futures-lite = "1.11.0" 15 | libc = "0.2.132" 16 | nix = "0.26" 17 | pin-project-lite.workspace = true 18 | smol = "1.2.5" 19 | tinyvec = "1.6.0" 20 | tracing.workspace = true 21 | bytes.workspace = true 22 | runa-io-traits = { version = "0.0.1-alpha1", path = "../runa-io-traits" } 23 | 24 | [dev-dependencies] 25 | anyhow = "1.0.65" 26 | arbitrary = "1.1.6" 27 | futures-executor = "0.3.24" 28 | futures-test = "0.3.25" 29 | futures-util.workspace = true 30 | rand = { version = "0.8.5", features = ["small_rng", "std"], default-features = false } 31 | smol = "1.2.5" 32 | tracing-subscriber = "0.3.15" 33 | 34 | # used by tests 35 | runa-wayland-protocols = { version = "0.1.0", path = "../runa-wayland-protocols" } 36 | runa-wayland-types = { version = "0.1.0", path = "../runa-wayland-types" } 37 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "runa-wayland-protocols", 5 | "runa-io", 6 | "runa-io-traits", 7 | "runa-macros", 8 | "runa-wayland-types", 9 | "runa-core", 10 | "runa-orbiter", 11 | "crescent", 12 | "input-event-codes-sys", 13 | 14 | # tools that deals with wayland protocol specs 15 | "wayland-tools/parser", 16 | "wayland-tools/codegen", 17 | "wayland-tools/scanner", 18 | "wayland-tools/macros" 19 | ] 20 | exclude = [ 21 | "testbench" 22 | ] 23 | 24 | [workspace.dependencies] 25 | tracing = { version = "0.1", features = [ "release_max_level_off" ] } 26 | hashbrown = "0.14" 27 | anyhow = "1.0.70" 28 | bitflags = "2.0.2" 29 | futures-lite = "2.2.0" 30 | futures-util = "0.3.24" 31 | thiserror = "1.0.35" 32 | bytes = "1" 33 | dlv-list = "0.5.0" 34 | pin-project-lite = "0.2" 35 | ordered-float = "4.2.0" 36 | indexmap = "2.2.5" 37 | tinyvec = "1.6.0" 38 | rustix = "0.38.31" 39 | syn = "1.0.109" 40 | derive-where = "1.2.7" 41 | proc-macro2 = "1.0.53" 42 | 43 | [workspace.package] 44 | license = "MIT OR Apache-2.0" 45 | authors = [ "Yuxuan Shui " ] 46 | edition = "2021" 47 | repository = "https://github.com/yshui/runa" 48 | -------------------------------------------------------------------------------- /crescent/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crescent" 3 | description = "Refernce wayland compositor for runa" 4 | version = "0.0.1-alpha1" 5 | license = "MPL-2.0" 6 | repository = "https://github.com/yshui/runa" 7 | edition = "2021" 8 | 9 | [dependencies] 10 | anyhow = "1.0.65" 11 | smol = "2.0" 12 | tracing-subscriber = "0.3.15" 13 | runa-io = { version = "0.0.1-alpha1", path = "../runa-io" } 14 | runa-core = { version = "0.0.1-alpha1", path = "../runa-core" } 15 | futures-util.workspace = true 16 | tracing.workspace = true 17 | runa-orbiter = { version = "0.0.1-alpha1", path = "../runa-orbiter" } 18 | runa-wayland-protocols = { version = "0.1.0", path = "../runa-wayland-protocols" } 19 | raw-window-handle = "0.6.0" 20 | wgpu = "0.19" 21 | winit = { version = "0.29.13", default-features = false, features = ["x11", "rwh_06"] } 22 | bytemuck = { version = "1.12.3", features = ["derive"] } 23 | png = "0.17.7" 24 | dlv-list.workspace = true 25 | slotmap = "1.0.6" 26 | ordered-float.workspace = true 27 | input-event-codes-sys = { version = "0.1.0", path = "../input-event-codes-sys" } 28 | xkbcommon = { version = "0.5.0", default-features = false } 29 | x11rb = "0.11.1" 30 | tinyvec = { workspace = true, features = ["alloc"] } 31 | memfd = "0.6.2" 32 | derive-where.workspace = true 33 | 34 | [features] 35 | default = [] 36 | dump_texture = [] 37 | -------------------------------------------------------------------------------- /runa-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "runa-core" 3 | description = "Fundamental scaffolding for a Wayland compositor" 4 | repository = "https://github.com/yshui/runa" 5 | license = "MIT OR Apache-2.0" 6 | version = "0.0.1-alpha1" 7 | edition = "2021" 8 | 9 | [dependencies] 10 | anyhow = "1.0.65" 11 | bitvec = "1.0.1" 12 | event-listener = "2.5.3" 13 | rustix = { workspace = true, features = ["fs"] } 14 | smol = "1.2.5" 15 | runa-io = { version = "0.0.1-alpha1", path = "../runa-io" } 16 | runa-wayland-protocols = { version = "0.1.0", path = "../runa-wayland-protocols" } 17 | xdg = "2.4.1" 18 | thiserror.workspace = true 19 | tracing.workspace = true 20 | hashbrown.workspace = true 21 | futures-lite.workspace = true 22 | futures-util.workspace = true 23 | dlv-list.workspace = true 24 | runa-wayland-types = { version = "0.1.0", path = "../runa-wayland-types" } 25 | memmap2 = "0.5.7" 26 | lazy_static = "1.4.0" 27 | static_assertions = "1.1.0" 28 | runa-macros = { version = "0.0.1-alpha1", path = "../runa-macros" } 29 | indexmap.workspace = true 30 | futures-sink = "0.3.26" 31 | slotmap = "1.0.6" 32 | futures-channel = "0.3.26" 33 | async-broadcast = "0.5.0" 34 | futures-core = "0.3.26" 35 | async-lock = "2.6.0" 36 | pin-project = "1.0.12" 37 | bytes.workspace = true 38 | derive-where.workspace = true 39 | 40 | [features] 41 | default = [] 42 | -------------------------------------------------------------------------------- /runa-orbiter/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "runa-orbiter" 3 | description = "Reference implementation of Wayland protocol interfaces" 4 | repository = "https://github.com/yshui/runa" 5 | license = "MIT OR Apache-2.0" 6 | version = "0.0.1-alpha2" 7 | edition = "2021" 8 | 9 | [dependencies] 10 | bitflags.workspace = true 11 | dlv-list.workspace = true 12 | dyn-clone = "1.0.9" 13 | futures-util.workspace = true 14 | intrusive-collections = "0.9.4" 15 | lazy_static = "1.4.0" 16 | runa-wayland-protocols = { version = "0.1.0", path = "../runa-wayland-protocols" } 17 | runa-core = { version = "0.0.1-alpha1", path = "../runa-core" } 18 | runa-wayland-types = { version = "0.1.0", path = "../runa-wayland-types" } 19 | tracing.workspace = true 20 | futures-channel = "0.3.25" 21 | runa-io = { version = "0.0.1-alpha1", path = "../runa-io" } 22 | memmap2 = "0.5.7" 23 | spin = { version = "0.9.7", default-features = false, features = ["mutex", "ticket_mutex", "use_ticket_mutex"] } 24 | libc = "0.2.137" 25 | hashbrown.workspace = true 26 | num-traits = "0.2.15" 27 | async-lock = "2.6.0" 28 | futures-core = "0.3.26" 29 | tinyvecdeq = { version = "0.1", features = ["std"] } 30 | enum_dispatch = "0.3.11" 31 | thiserror.workspace = true 32 | ordered-float.workspace = true 33 | tinyvec = { workspace = true, features = ["alloc"] } 34 | derive-where.workspace = true 35 | -------------------------------------------------------------------------------- /runa-orbiter/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of various wayland protocols and interfaces. 2 | //! 3 | //! As a compositor writer, you can pick and choose implementations of wayland 4 | //! objects and globals here to assemble your `Object` and `Global`. (See 5 | //! [`runa_core::objects::AnyObject`] and [`runa_core::globals::AnyGlobal`] for 6 | //! more information.) However, some of the objects or globals can have 7 | //! dependencies on other objects or globals, meaning you must use those 8 | //! together. 9 | //! 10 | //! Generally speaking, if you choose to use a global, you must also use the 11 | //! objects it can create. For example, if you use 12 | //! [`Compositor`](crate::globals::Compositor), you must also use 13 | //! [`Surface`](crate::objects::compositor::Surface) and 14 | //! [`Subsurface`](crate::objects::compositor::Subsurface). 15 | //! 16 | //! There are other dependencies, such as 17 | //! [`Pointer`](crate::objects::input::Pointer) and 18 | //! [`Keyboard`](crate::objects::input::Keyboard), which depends on our surface 19 | //! implementation. Pay attention to the documentation of the objects and 20 | //! globals you choose to use. 21 | 22 | #![allow(incomplete_features)] 23 | #![feature(impl_trait_in_assoc_type, trait_upcasting)] 24 | #![warn( 25 | missing_debug_implementations, 26 | missing_copy_implementations, 27 | missing_docs, 28 | rust_2018_idioms, 29 | single_use_lifetimes 30 | )] 31 | 32 | pub mod globals; 33 | pub mod objects; 34 | pub mod renderer_capability; 35 | pub mod shell; 36 | mod time; 37 | pub mod utils; 38 | -------------------------------------------------------------------------------- /runa-core/src/events/mod.rs: -------------------------------------------------------------------------------- 1 | //! Traits and types related to event 2 | //! 3 | //! The [`EventSource`] trait is the core of how events are delivered. And it's 4 | //! quite simple. Types that can emit events, will implement this trait. And a 5 | //! stream of events can be obtained from that type by calling 6 | //! [`EventSource::subscribe`]. This stream of events can then be registered 7 | //! with [`Client::EventDispatcher`](crate::client::traits::Client::EventDispatcher) 8 | //! together with an event handler. And the event dispatcher will call the 9 | //! handler whenever a new event is received from the stream. Note as it is the 10 | //! compositor author who will implement the `Client` trait, so they are the 11 | //! ones who are responsible for calling event handlers. 12 | //! 13 | //! This crate and other `runa` related crates will provide some event sources. 14 | //! For example, [`Store`](crate::client::store::Store) is an event source that 15 | //! emits events whenever there is an object being inserted or removed from the 16 | //! store. But `runa` crates will also expect downstream compositor authors to 17 | //! implement their own event sources. You will see `EventSource` trait bounds 18 | //! when that is the case. 19 | 20 | /// Event source 21 | /// 22 | /// An event source is something you can get a stream of events from. 23 | pub trait EventSource { 24 | /// Type of event stream you get from this event source. 25 | type Source: futures_util::stream::Stream + 'static; 26 | 27 | /// Get a stream of events from the event source. 28 | fn subscribe(&self) -> Self::Source; 29 | } 30 | 31 | // TODO: this is equivalent to `async_broadcast` with `set_overflow(true)` and 32 | // `set_capacity(1)`. Contemplate if we should just use `async_broadcast` 33 | // directly. 34 | pub mod aggregate; 35 | pub mod broadcast; 36 | pub mod single_state; 37 | -------------------------------------------------------------------------------- /runa-orbiter/src/shell/surface/roles/mod.rs: -------------------------------------------------------------------------------- 1 | use runa_core::provide_any; 2 | 3 | pub mod subsurface; 4 | pub mod xdg; 5 | 6 | pub use subsurface::Subsurface; 7 | pub use xdg::Surface as XdgSurface; 8 | pub use xdg::TopLevel as XdgTopLevel; 9 | pub use xdg::Popup as XdgPopup; 10 | 11 | /// A surface role 12 | pub trait Role: std::any::Any { 13 | /// The name of the interface of this role. 14 | fn name(&self) -> &'static str; 15 | /// Returns true if the role is active. 16 | /// 17 | /// As specified by the wayland protocol, a surface can be assigned a role, 18 | /// then have the role object destroyed. This makes the role "inactive", 19 | /// but the surface cannot be assigned a different role. So we keep the 20 | /// role object but "deactivate" it. 21 | fn is_active(&self) -> bool; 22 | /// Deactivate the role. 23 | fn deactivate(&mut self, shell: &mut S); 24 | /// Provides type based access to member variables of this role. 25 | fn provide<'a>(&'a self, _demand: &mut provide_any::Demand<'a>) {} 26 | /// Provides type based access to member variables of this role. 27 | fn provide_mut<'a>(&'a mut self, _demand: &mut provide_any::Demand<'a>) {} 28 | /// Called before the pending state becomes the current state, in 29 | /// [`Surface::commit`]. If an error is returned, the commit will be 30 | /// stopped. 31 | fn pre_commit(&mut self, _shell: &mut S, _surfacee: &super::Surface) -> Result<(), &'static str> { 32 | Ok(()) 33 | } 34 | /// Called after the pending state becomes the current state, in 35 | /// [`Surface::commit`] 36 | fn post_commit(&mut self, _shell: &mut S, _surface: &super::Surface) {} 37 | } 38 | 39 | /// A double-buffer state associated with a role 40 | pub trait State: std::any::Any + dyn_clone::DynClone + std::fmt::Debug + 'static {} 41 | 42 | impl provide_any::Provider for dyn Role { 43 | fn provide<'a>(&'a self, demand: &mut provide_any::Demand<'a>) { 44 | self.provide(demand); 45 | } 46 | 47 | fn provide_mut<'a>(&'a mut self, demand: &mut provide_any::Demand<'a>) { 48 | self.provide_mut(demand); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /runa-wayland-protocols/build.rs: -------------------------------------------------------------------------------- 1 | use std::{ffi::OsStr, os::unix::ffi::OsStrExt}; 2 | 3 | use runa_wayland_scanner::generate_protocol; 4 | use runa_wayland_spec_parser as spec; 5 | 6 | fn main() { 7 | use std::io::Write; 8 | 9 | let protocols = 10 | std::path::Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("spec"); 11 | let out_dir = std::env::var("OUT_DIR").unwrap(); 12 | println!("cargo:rerun-if-changed={}", protocols.display()); 13 | let generate_from_dir = |name: &str| { 14 | let dest_path = std::path::Path::new(&out_dir).join(format!("{name}_generated.rs")); 15 | let mut outfile = std::fs::File::create(dest_path).unwrap(); 16 | let dir_path = protocols.join("wayland-protocols").join(name); 17 | for dir in std::fs::read_dir(dir_path).unwrap() { 18 | let dir = dir.unwrap(); 19 | let metadata = dir.metadata().unwrap(); 20 | if !metadata.is_dir() { 21 | continue 22 | } 23 | for file in std::fs::read_dir(dir.path()).unwrap() { 24 | let file = file.unwrap(); 25 | let metadata = file.metadata().unwrap(); 26 | if !metadata.is_file() { 27 | continue 28 | } 29 | if file.path().extension() != Some(OsStr::from_bytes(b"xml")) { 30 | continue 31 | } 32 | let protocol = 33 | spec::parse::parse(std::fs::read_to_string(file.path()).unwrap().as_bytes()) 34 | .unwrap(); 35 | //let source = fmt 36 | // .format_tokens(generate_protocol(&protocol).unwrap()) 37 | // .unwrap(); 38 | let source = generate_protocol(&protocol).unwrap().to_string(); 39 | write!(outfile, "{source}").unwrap(); 40 | } 41 | } 42 | }; 43 | 44 | let contents = std::fs::read_to_string(protocols.join("wayland.xml")).unwrap(); 45 | let protocol = spec::parse::parse(contents.as_bytes()).unwrap(); 46 | let source = generate_protocol(&protocol).unwrap().to_string(); 47 | //let source = fmt 48 | // .format_tokens(generate_protocol(&protocol).unwrap()) 49 | // .unwrap(); 50 | 51 | let dest_path = std::path::Path::new(&out_dir).join("wayland_generated.rs"); 52 | std::fs::write(dest_path, source).unwrap(); 53 | 54 | generate_from_dir("stable"); 55 | generate_from_dir("staging"); 56 | generate_from_dir("unstable"); 57 | } 58 | -------------------------------------------------------------------------------- /runa-core/src/events/single_state.rs: -------------------------------------------------------------------------------- 1 | //! A event source that a single event, and overwrites it when a new event is 2 | //! sent. 3 | 4 | use std::{ 5 | cell::{Cell, RefCell}, 6 | future::Future, 7 | pin::Pin, 8 | rc::{Rc, Weak}, 9 | task::{ 10 | ready, 11 | Poll::{self, Ready}, 12 | }, 13 | }; 14 | 15 | use derive_where::derive_where; 16 | use futures_core::Stream; 17 | 18 | #[derive(Debug)] 19 | #[derive_where(Default)] 20 | struct Inner { 21 | event_handle: event_listener::Event, 22 | state: RefCell>, 23 | version: Cell, 24 | } 25 | 26 | /// An event source whose receivers will receive an event when a state 27 | /// is changed. It's possible for multiple changes to be 28 | /// aggregated into a single event. 29 | #[derive(Debug)] 30 | #[derive_where(Default)] 31 | pub struct Sender { 32 | inner: Rc>, 33 | } 34 | 35 | /// Sender for a single state event source. 36 | impl Sender { 37 | /// Create a new event source 38 | pub fn new() -> Self { 39 | Default::default() 40 | } 41 | 42 | /// Send a new event to all receivers, replace the sent previous event 43 | pub fn send(&self, state: E) { 44 | *self.inner.state.borrow_mut() = Some(state); 45 | self.inner.version.set(self.inner.version.get() + 1); 46 | self.inner.event_handle.notify(usize::MAX); 47 | } 48 | 49 | /// Create a new receiver for this event source 50 | pub fn new_receiver(&self) -> Receiver { 51 | Receiver { 52 | inner: Rc::downgrade(&self.inner), 53 | version: self.inner.version.get(), 54 | listener: None, 55 | } 56 | } 57 | } 58 | 59 | /// Receiver for a single state event source. 60 | #[derive(Debug)] 61 | pub struct Receiver { 62 | inner: Weak>, 63 | listener: Option, 64 | version: u64, 65 | } 66 | 67 | impl Stream for Receiver { 68 | type Item = E; 69 | 70 | fn poll_next( 71 | self: std::pin::Pin<&mut Self>, 72 | cx: &mut std::task::Context<'_>, 73 | ) -> Poll> { 74 | let this = self.get_mut(); 75 | let Some(inner) = this.inner.upgrade() else { 76 | // All senders are gone 77 | return Ready(None) 78 | }; 79 | loop { 80 | if this.version != inner.version.get() { 81 | this.listener = None; 82 | this.version = inner.version.get(); 83 | return Ready(Some(inner.state.borrow().clone().unwrap())) 84 | } 85 | let listener = this 86 | .listener 87 | .get_or_insert_with(|| inner.event_handle.listen()); 88 | ready!(Pin::new(listener).poll(cx)); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /runa-core/src/utils.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod one_shot_signal { 2 | //! A simple signal that can only be sent once. 3 | //! 4 | //! A pair of `Sender` and `Receiver` can created with `new_pair()`. 5 | //! The `Sender` can be used to signal the `Receiver`, which is a `Future` 6 | //! that is resolved when signaled. For simplicity, dropping the `Sender` 7 | //! will cause `Receiver` to never resolve. 8 | use std::{ 9 | cell::{Cell, RefCell}, 10 | rc::Rc, 11 | task::Waker, 12 | }; 13 | #[derive(Debug, Default)] 14 | struct Inner { 15 | set: Cell, 16 | waker: RefCell>, 17 | } 18 | 19 | #[derive(Debug, Default)] 20 | pub(crate) struct Sender { 21 | inner: Rc, 22 | } 23 | 24 | #[derive(Debug, Default)] 25 | pub(crate) struct Receiver { 26 | inner: Option>, 27 | } 28 | 29 | impl std::future::Future for Receiver { 30 | type Output = (); 31 | 32 | fn poll( 33 | mut self: std::pin::Pin<&mut Self>, 34 | cx: &mut std::task::Context<'_>, 35 | ) -> std::task::Poll { 36 | if let Some(inner) = self.inner.as_mut() { 37 | if inner.set.get() { 38 | self.inner.take(); 39 | std::task::Poll::Ready(()) 40 | } else { 41 | if inner 42 | .waker 43 | .borrow() 44 | .as_ref() 45 | .map(|waker| !waker.will_wake(cx.waker())) 46 | .unwrap_or(true) 47 | { 48 | if let Some(old_waker) = std::mem::replace( 49 | &mut *inner.waker.borrow_mut(), 50 | Some(cx.waker().clone()), 51 | ) { 52 | old_waker.wake(); 53 | } 54 | } 55 | std::task::Poll::Pending 56 | } 57 | } else { 58 | std::task::Poll::Ready(()) 59 | } 60 | } 61 | } 62 | 63 | impl futures_core::FusedFuture for Receiver { 64 | fn is_terminated(&self) -> bool { 65 | self.inner.is_none() 66 | } 67 | } 68 | 69 | impl Sender { 70 | pub fn send(&self) { 71 | self.inner.set.set(true); 72 | if let Some(waker) = self.inner.waker.borrow_mut().take() { 73 | waker.wake(); 74 | } 75 | } 76 | } 77 | 78 | pub(crate) fn new_pair() -> (Sender, Receiver) { 79 | let inner = Rc::new(Inner::default()); 80 | ( 81 | Sender { 82 | inner: inner.clone(), 83 | }, 84 | Receiver { inner: Some(inner) }, 85 | ) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /runa-core/src/server/mod.rs: -------------------------------------------------------------------------------- 1 | //! Traits and types related to the server global context. 2 | 3 | use std::{future::Future, rc::Rc}; 4 | 5 | use derive_where::derive_where; 6 | 7 | use crate::{ 8 | events::{self, EventSource}, 9 | Serial, 10 | }; 11 | 12 | pub mod traits; 13 | 14 | use traits::GlobalsUpdate; 15 | 16 | /// Reference implementation of the [`GlobalStore`](traits::GlobalStore) trait. 17 | #[derive_where(Debug)] 18 | pub struct GlobalStore { 19 | globals: crate::IdAlloc>, 20 | update_event: events::broadcast::Broadcast>, 21 | } 22 | 23 | impl FromIterator for GlobalStore { 24 | fn from_iter>(iter: T) -> Self { 25 | let mut id_alloc = crate::IdAlloc::default(); 26 | for global in iter.into_iter() { 27 | id_alloc.next_serial(Rc::new(global)); 28 | } 29 | GlobalStore { 30 | globals: id_alloc, 31 | update_event: Default::default(), 32 | } 33 | } 34 | } 35 | 36 | impl EventSource> for GlobalStore { 37 | type Source = 38 | > as EventSource>>::Source; 39 | 40 | fn subscribe(&self) -> Self::Source { 41 | self.update_event.subscribe() 42 | } 43 | } 44 | 45 | /// GlobalStore will notify listeners when globals are added or removed. The 46 | /// notification will be sent to the slot registered as "wl_registry". 47 | impl traits::GlobalStore for GlobalStore { 48 | type InsertFut<'a, I> = impl Future + 'a where Self: 'a, I: 'a + Into; 49 | type Iter<'a> = impl Iterator)> where Self: 'a, G: 'a; 50 | type RemoveFut<'a> = impl Future + 'a where Self: 'a; 51 | 52 | fn insert<'a, I: Into + 'a>(&'a mut self, global: I) -> Self::InsertFut<'_, I> { 53 | let global = Rc::new(global.into()); 54 | let id = self.globals.next_serial(global.clone()); 55 | async move { 56 | self.update_event 57 | .broadcast(GlobalsUpdate::Added(id, global)) 58 | .await; 59 | id 60 | } 61 | } 62 | 63 | fn get(&self, id: u32) -> Option<&Rc> { 64 | self.globals.get(id) 65 | } 66 | 67 | fn remove(&mut self, id: u32) -> Self::RemoveFut<'_> { 68 | let removed = self.globals.expire(id); 69 | async move { 70 | if removed { 71 | self.update_event 72 | .broadcast(GlobalsUpdate::Removed(id)) 73 | .await; 74 | } 75 | removed 76 | } 77 | } 78 | 79 | fn iter(&self) -> Self::Iter<'_> { 80 | self.into_iter() 81 | } 82 | } 83 | impl<'a, G> IntoIterator for &'a GlobalStore 84 | where 85 | G: 'a, 86 | { 87 | type Item = (u32, &'a Rc); 88 | 89 | type IntoIter = impl Iterator)> + 'a; 90 | 91 | fn into_iter(self) -> Self::IntoIter { 92 | self.globals.iter() 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs.fenix = { 3 | inputs.nixpkgs.follows = "nixpkgs"; 4 | url = github:nix-community/fenix; 5 | }; 6 | inputs.rust-manifest = { 7 | flake = false; 8 | url = "https://static.rust-lang.org/dist/2024-02-29/channel-rust-nightly.toml"; 9 | }; 10 | inputs.flake-utils.url = github:numtide/flake-utils; 11 | description = "runa - wayland compositor toolkit"; 12 | 13 | outputs = { self, nixpkgs, fenix, flake-utils, ... } @ inputs: flake-utils.lib.eachDefaultSystem (system: 14 | let 15 | pkgs = import nixpkgs { inherit system; overlays = [ fenix.overlays.default ]; }; 16 | rust-toolchain = (pkgs.fenix.fromManifestFile inputs.rust-manifest).withComponents [ 17 | "rustfmt" 18 | "rust-src" 19 | "clippy" 20 | "rustc" 21 | "cargo" 22 | ]; 23 | rustPlatform = pkgs.makeRustPlatform { 24 | cargo = rust-toolchain; 25 | rustc = rust-toolchain; 26 | }; 27 | runtimeDependencies = (with pkgs; [ libGL vulkan-loader ]) ++ 28 | (with pkgs.xorg; [ libX11 libXdmcp libXrender libXcursor libXau libxcb libXfixes libXrandr libXext libXi pkgs.libxkbcommon ]); 29 | in 30 | with pkgs; { 31 | packages = rec { 32 | crescent = rustPlatform.buildRustPackage rec { 33 | inherit runtimeDependencies; 34 | pname = "crescent"; 35 | version = "0.1.0"; 36 | src = ./.; 37 | buildInputs = [ libxkbcommon gccForLibs.lib ]; 38 | nativeBuildInputs = [ autoPatchelfHook ]; 39 | postUnpack = '' 40 | echo $(pwd) 41 | ls $sourceRoot/runa-wayland-protocols/spec 42 | cp -v ${cargoLock.lockFile} $sourceRoot/Cargo.lock 43 | ''; 44 | cargoLock.lockFile = ./nix/Cargo.lock; 45 | LIBCLANG_PATH = lib.makeLibraryPath [ llvmPackages_17.libclang.lib ]; 46 | BINDGEN_EXTRA_CLANG_ARGS = 47 | # Includes with normal include path 48 | (builtins.map (a: ''-I"${a}/include"'') [ 49 | linuxHeaders 50 | ]) ++ [ 51 | ''-isystem "${pkgs.llvmPackages_latest.libclang.lib}/lib/clang/${lib.versions.major pkgs.llvmPackages_latest.libclang.version}/include"'' 52 | ''-isystem "${glibc.dev}/include"'' 53 | ]; 54 | }; 55 | default = crescent; 56 | }; 57 | devShells.default = mkShell { 58 | nativeBuildInputs = [ pkg-config cmake rust-toolchain mold cargo-bloat ]; 59 | buildInputs = [ libxkbcommon ]; 60 | shellHook = '' 61 | export LD_LIBRARY_PATH="${lib.makeLibraryPath (runtimeDependencies ++ [ wayland ])}:$LD_LIBRARY_PATH" 62 | ''; 63 | LIBCLANG_PATH = lib.makeLibraryPath [ llvmPackages_17.libclang.lib ]; 64 | 65 | BINDGEN_EXTRA_CLANG_ARGS = 66 | # Includes with normal include path 67 | (builtins.map (a: ''-I"${a}/include"'') [ 68 | linuxHeaders 69 | ]) ++ [ 70 | ''-isystem "${pkgs.llvmPackages_latest.libclang.lib}/lib/clang/${lib.versions.major pkgs.llvmPackages_latest.libclang.version}/include"'' 71 | ''-isystem "${glibc.dev}/include"'' 72 | ]; 73 | }; 74 | }); 75 | } 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # runa - wayland compositor toolbox 2 | 3 | ## Table of contents 4 | * [Demo](#demo) 5 | * [Project status, goals and plans](#project-status-goals-and-plans) 6 | * [Chat room](#chat-room) 7 | * [Roadmap](#roadmap) 8 | * [Q&A](#qa) 9 | 10 | ## Demo 11 | 12 | https://user-images.githubusercontent.com/366851/228325348-7b988f84-9837-4f5b-8fcd-d2085ea7edea.mp4 13 | 14 | ## Project status, goals and plans 15 | 16 | This project is still pretty much a "tech demo" in this stage. What you can see above is all that works. And it took a lot of short cuts to even get that far. (And please don't run `cargo test`.) 17 | 18 | Creating a wayland compositor library from the ground up is a daunting task, and I don't think I can do this just by myself. That's why this project is announced in this state - because I wanted to attract interests, and potentially collaborators to this project. 19 | 20 | I tried to document the code base in its current state as best as I can. Have a look at the documentation, or the code base, and get in touch if you are interested: 21 | 22 | - [runa-core](https://yshui.github.io/runa/runa_core/index.html): base traits and types for a wayland compositor. 23 | - [runa-orbiter](https://yshui.github.io/runa/runa_orbiter/index.html): wayland protocol impls based on `runa-core`. 24 | 25 | ## Chat room 26 | 27 | I am currently hanging out in the `#runa` channel on libra.chat. 28 | 29 | ## Roadmap 30 | 31 | ### 0.0.2 32 | 33 | - Have unittests for easily testable components. 34 | - All of the core and stable wayland interfaces implemented. No more "not implemented" error. 35 | - Fix interface versioning 36 | 37 | ### 0.0.3 - 0.0.4 38 | 39 | - Crescent able to run as DRM master 40 | - We likely want to create a crate for making accessing raw display devices and input devices easier. 41 | or use an existing crate. winit might become a viable option (rust-windowing/winit#2272) 42 | - Support hardware accelerated clients 43 | 44 | ### 0.1.0 45 | 46 | - Crescent can function as a minimal wayland compositor 47 | - A tutorial for writing compositors with runa 48 | 49 | ### 0.2.0 50 | 51 | - Xwayland support 52 | 53 | ## Q&A 54 | 55 | - **Why runa, instead of other wayland libraries like wlroots, or smithay?** 56 | 57 | wlroots has a model that is not very suitable for a Rust binding - people has [tried and failed](https://way-cooler.org/blog/2019/04/29/rewriting-way-cooler-in-c.html) at creating a binding for it. And even if I do manage to create a wlroots binding, it's not going to be ergonomic Rust. 58 | 59 | As for smithay, runa's design philosophy is pretty different. First of all, smithay was created quite a few years back, and Rust has evolved a lot in that time. runa tries to take advantage of new Rust features, most notably `async`, as much as possible. (this does unfortunately mean a nightly Rust compiler is required for now, at least until TAIT is stabilized). 60 | 61 | Also, runa took a page from other amazing Rust crates like [smol](https://docs.rs/smol/latest/smol/) and [futures](https://docs.rs/futures/latest/futures), and has a very modular design, unlike smithay which is everything inside a single crate. This way 1) users can bring their own implementation if what we provide is not suitable; 2) some of our crates can be useful outside runa. 62 | -------------------------------------------------------------------------------- /runa-core/src/server/traits.rs: -------------------------------------------------------------------------------- 1 | //! Traits related to the server context 2 | 3 | use std::{cell::RefCell, future::Future, rc::Rc}; 4 | 5 | use derive_where::derive_where; 6 | 7 | use crate::{ 8 | client::traits::Client, 9 | events::EventSource, 10 | globals::{AnyGlobal, Bind}, 11 | }; 12 | 13 | /// A server context 14 | pub trait Server: Sized { 15 | /// The per client context type. 16 | type ClientContext: Client; 17 | /// Type of connection to the client, this is what the connection 18 | /// listener yields. For example, 19 | /// see [`wayland_listener_auto`](crate::wayland_listener_auto). 20 | type Conn; 21 | /// Type of error returned by [`Self::new_connection`]. 22 | type Error; 23 | /// The global store 24 | type GlobalStore: GlobalStore; 25 | /// Type of globals 26 | type Global: Bind 27 | + AnyGlobal::Object>; 28 | 29 | /// Returns a reference to the global store. 30 | fn globals(&self) -> &RefCell; 31 | 32 | /// Call each time a new client connects. 33 | fn new_connection(&self, conn: Self::Conn) -> Result<(), Self::Error>; 34 | } 35 | 36 | /// Global updates 37 | /// 38 | /// Note, unlike [`StoreUpdate`](crate::client::traits::StoreUpdate), this 39 | /// doesn't have a "Replaced" event to represent a global being added after 40 | /// it's removed. This is because the global IDs are entirely controlled 41 | /// by the compositor, and the compositor should avoid reusing global 42 | /// IDs anyway, to avoid race condition with the client. So we could 43 | /// assume ID reuse doesn't happen within a comfortably long time frame, 44 | /// which should be long enough for event listeners to handle their 45 | /// events. 46 | #[derive_where(Clone)] 47 | pub enum GlobalsUpdate { 48 | /// A global was added. 49 | Added(u32, Rc), 50 | /// A global was removed. 51 | Removed(u32), 52 | } 53 | 54 | impl std::fmt::Debug for GlobalsUpdate { 55 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 56 | match self { 57 | GlobalsUpdate::Added(id, _) => f.debug_tuple("Added").field(id).field(&"...").finish(), 58 | GlobalsUpdate::Removed(id) => f.debug_tuple("Removed").field(id).finish(), 59 | } 60 | } 61 | } 62 | 63 | /// A global store 64 | pub trait GlobalStore: EventSource> { 65 | /// Type of iterator returned by [`Self::iter`]. 66 | type Iter<'a>: Iterator)> 67 | where 68 | Self: 'a, 69 | G: 'a; 70 | /// Type of future returned by [`Self::insert`]. 71 | type InsertFut<'a, IntoG>: Future + 'a 72 | where 73 | Self: 'a, 74 | IntoG: 'a + Into; 75 | /// Type of future returned by [`Self::remove`]. 76 | type RemoveFut<'a>: Future + 'a 77 | where 78 | Self: 'a; 79 | /// Add a global to the store, return its allocated ID. 80 | fn insert<'a, IntoG: Into + 'a>(&'a mut self, global: IntoG) -> Self::InsertFut<'a, IntoG>; 81 | /// Get the global with the given id. 82 | fn get(&self, id: u32) -> Option<&Rc>; 83 | /// Remove the global with the given id. 84 | fn remove(&mut self, id: u32) -> Self::RemoveFut<'_>; 85 | /// Iterate over all the globals. 86 | fn iter(&self) -> Self::Iter<'_>; 87 | } 88 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "fenix": { 4 | "inputs": { 5 | "nixpkgs": [ 6 | "nixpkgs" 7 | ], 8 | "rust-analyzer-src": "rust-analyzer-src" 9 | }, 10 | "locked": { 11 | "lastModified": 1709101340, 12 | "narHash": "sha256-C5DqUNDlcI6t11w14MMIt0ojLOD05m35ea9DQ7gIQpY=", 13 | "owner": "nix-community", 14 | "repo": "fenix", 15 | "rev": "daa2bef600e492c137c34d01792e10067589c31d", 16 | "type": "github" 17 | }, 18 | "original": { 19 | "owner": "nix-community", 20 | "repo": "fenix", 21 | "type": "github" 22 | } 23 | }, 24 | "flake-utils": { 25 | "inputs": { 26 | "systems": "systems" 27 | }, 28 | "locked": { 29 | "lastModified": 1709126324, 30 | "narHash": "sha256-q6EQdSeUZOG26WelxqkmR7kArjgWCdw5sfJVHPH/7j8=", 31 | "owner": "numtide", 32 | "repo": "flake-utils", 33 | "rev": "d465f4819400de7c8d874d50b982301f28a84605", 34 | "type": "github" 35 | }, 36 | "original": { 37 | "owner": "numtide", 38 | "repo": "flake-utils", 39 | "type": "github" 40 | } 41 | }, 42 | "nixpkgs": { 43 | "locked": { 44 | "lastModified": 1708984720, 45 | "narHash": "sha256-gJctErLbXx4QZBBbGp78PxtOOzsDaQ+yw1ylNQBuSUY=", 46 | "owner": "NixOS", 47 | "repo": "nixpkgs", 48 | "rev": "13aff9b34cc32e59d35c62ac9356e4a41198a538", 49 | "type": "github" 50 | }, 51 | "original": { 52 | "id": "nixpkgs", 53 | "type": "indirect" 54 | } 55 | }, 56 | "root": { 57 | "inputs": { 58 | "fenix": "fenix", 59 | "flake-utils": "flake-utils", 60 | "nixpkgs": "nixpkgs", 61 | "rust-manifest": "rust-manifest" 62 | } 63 | }, 64 | "rust-analyzer-src": { 65 | "flake": false, 66 | "locked": { 67 | "lastModified": 1709057983, 68 | "narHash": "sha256-ZwIAbq4qRR6Kfdmq9P9/Siieebe2R9dkwTvppsK3oSM=", 69 | "owner": "rust-lang", 70 | "repo": "rust-analyzer", 71 | "rev": "0ac05c05271f31c43d31017cbd288e8737a0edb0", 72 | "type": "github" 73 | }, 74 | "original": { 75 | "owner": "rust-lang", 76 | "ref": "nightly", 77 | "repo": "rust-analyzer", 78 | "type": "github" 79 | } 80 | }, 81 | "rust-manifest": { 82 | "flake": false, 83 | "locked": { 84 | "narHash": "sha256-Qd1WmOySuAkmDz3b6O/Whv2jwu0mNyIzYuDjqAM9GXA=", 85 | "type": "file", 86 | "url": "https://static.rust-lang.org/dist/2024-02-29/channel-rust-nightly.toml" 87 | }, 88 | "original": { 89 | "type": "file", 90 | "url": "https://static.rust-lang.org/dist/2024-02-29/channel-rust-nightly.toml" 91 | } 92 | }, 93 | "systems": { 94 | "locked": { 95 | "lastModified": 1681028828, 96 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 97 | "owner": "nix-systems", 98 | "repo": "default", 99 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 100 | "type": "github" 101 | }, 102 | "original": { 103 | "owner": "nix-systems", 104 | "repo": "default", 105 | "type": "github" 106 | } 107 | } 108 | }, 109 | "root": "root", 110 | "version": 7 111 | } 112 | -------------------------------------------------------------------------------- /runa-macros/tests/generate.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | ffi::CStr, 3 | os::{ 4 | fd::{self, IntoRawFd, OwnedFd}, 5 | unix::prelude::AsRawFd, 6 | }, 7 | pin::Pin, 8 | }; 9 | 10 | use bytes::{buf, BufMut}; 11 | use futures_lite::{io::AsyncWriteExt, AsyncBufReadExt}; 12 | use runa_io::{ 13 | traits::{ 14 | de::Deserialize, ser::Serialize, AsyncBufReadWithFd, RawMessage, ReadMessage, WriteMessage, 15 | }, 16 | utils::{ReadPool, WritePool}, 17 | Connection, 18 | }; 19 | use runa_wayland_types::{Fd, NewId, Object, Str}; 20 | use wayland::wl_display::v1::{events, Event}; 21 | runa_wayland_scanner::generate_protocol!("runa-wayland-protocols/spec/wayland.xml"); 22 | 23 | #[test] 24 | fn test_roundtrip() { 25 | futures_executor::block_on(async { 26 | let orig_item = Event::Error(events::Error { 27 | object_id: Object(1), 28 | code: 2, 29 | message: Str(b"test"), 30 | }); 31 | let mut tx = Connection::new(WritePool::new(), 4096); 32 | // Put an object id 33 | tx.send(1, orig_item).await.unwrap(); 34 | tx.flush().await.unwrap(); 35 | let pool = tx.into_inner(); 36 | let (buf, fds) = pool.into_inner(); 37 | let fds = fds 38 | .into_iter() 39 | .map(|fd| fd.into_raw_fd()) 40 | .collect::>(); 41 | 42 | let mut rx = ReadPool::new(buf, fds); 43 | let RawMessage { 44 | object_id, data, .. 45 | } = Pin::new(&mut rx).next_message().await.unwrap(); 46 | assert_eq!(object_id, 1); 47 | let item = Event::deserialize(data, &[]).unwrap(); 48 | 49 | eprintln!("{:#?}", item); 50 | assert_eq!( 51 | &item, 52 | &Event::Error(events::Error { 53 | object_id: Object(1), 54 | code: 2, 55 | message: Str(b"test"), 56 | }) 57 | ); 58 | 59 | let (data_len, fds_len) = (rx.buffer().len(), rx.fds().len()); 60 | AsyncBufReadWithFd::consume(Pin::new(&mut rx), data_len, fds_len); 61 | assert!(rx.is_eof()); 62 | }) 63 | } 64 | 65 | #[test] 66 | fn test_roundtrip_with_fd() { 67 | use std::os::unix::io::IntoRawFd; 68 | 69 | use wayland::wl_shm::v1::{requests, Request}; 70 | futures_executor::block_on(async { 71 | let fd = std::fs::File::open("/dev/null").unwrap(); 72 | let orig_item = Request::CreatePool(requests::CreatePool { 73 | id: NewId(1), 74 | fd: Fd::Owned(fd.into()), 75 | size: 100, 76 | }); 77 | 78 | let mut tx = Connection::new(WritePool::new(), 4096); 79 | // Put an object id 80 | tx.send(1, orig_item).await.unwrap(); 81 | tx.flush().await.unwrap(); 82 | let (buf, fds) = tx.into_inner().into_inner(); 83 | eprintln!("{:x?}", buf); 84 | 85 | let fds = fds 86 | .into_iter() 87 | .map(|fd| fd.into_raw_fd()) 88 | .collect::>(); 89 | let mut rx = ReadPool::new(buf, fds); 90 | let RawMessage { 91 | object_id, 92 | data, 93 | fds, 94 | .. 95 | } = Pin::new(&mut rx).next_message().await.unwrap(); 96 | assert_eq!(object_id, 1); 97 | let item = Request::deserialize(data, fds).unwrap(); 98 | eprintln!("{:#?}", item); 99 | 100 | // Compare the result skipping the file descriptor 101 | match item { 102 | Request::CreatePool(requests::CreatePool { id, ref fd, size }) => { 103 | assert!(fd.as_raw_fd() >= 0); // make sure fd is valid 104 | assert_eq!(id, NewId(1)); 105 | assert_eq!(size, 100); 106 | }, 107 | } 108 | 109 | drop(item); 110 | let (data_len, fds_len) = (rx.buffer().len(), rx.fds().len()); 111 | AsyncBufReadWithFd::consume(Pin::new(&mut rx), data_len, fds_len); 112 | assert!(rx.is_eof()); 113 | }) 114 | } 115 | -------------------------------------------------------------------------------- /wayland-tools/scanner/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[doc(inline)] 2 | pub use codegen::generate_protocol; 3 | #[doc(inline)] 4 | pub use macros::generate_protocol; 5 | 6 | #[derive(thiserror::Error, Debug)] 7 | pub enum Error { 8 | #[error("{0}")] 9 | Io(#[from] std::io::Error), 10 | #[error("Unknown opcode {0}")] 11 | UnknownOpcode(u32), 12 | } 13 | 14 | pub mod io { 15 | use std::os::unix::io::RawFd; 16 | 17 | pub use futures_lite::AsyncBufRead; 18 | pub use runa_io_traits::*; 19 | #[inline] 20 | pub fn pop_fd(fds: &mut &[RawFd]) -> RawFd { 21 | assert!(!fds.is_empty(), "Not enough fds in buffer"); 22 | // Safety: we checked that the slice is long enough 23 | let ret = unsafe { *fds.get_unchecked(0) }; 24 | *fds = &fds[1..]; 25 | ret 26 | } 27 | 28 | #[inline] 29 | /// Pop `len` bytes from the buffer and returns it. Advances the buffer by 30 | /// `len` aligned up to 4 bytes. 31 | pub fn pop_bytes<'a>(bytes: &mut &'a [u8], len: usize) -> &'a [u8] { 32 | use std::slice::from_raw_parts; 33 | let blen = bytes.len(); 34 | let len_aligned = (len + 3) & !3; 35 | assert!( 36 | blen >= len_aligned, 37 | "Not enough bytes in buffer, has {blen}, asking for {len_aligned}" 38 | ); 39 | let ptr = bytes.as_ptr(); 40 | // Safety: we checked that the slice is long enough 41 | let (ret, rest) = unsafe { 42 | ( 43 | from_raw_parts(ptr, len), 44 | from_raw_parts(ptr.add(len_aligned), blen - len_aligned), 45 | ) 46 | }; 47 | *bytes = rest; 48 | ret 49 | } 50 | 51 | #[inline] 52 | pub fn pop_i32(bytes: &mut &[u8]) -> i32 { 53 | let slice = pop_bytes(bytes, 4); 54 | // Safety: slice is guaranteed to be 4 bytes long 55 | i32::from_ne_bytes(unsafe { *(slice.as_ptr() as *const [u8; 4]) }) 56 | } 57 | 58 | #[inline] 59 | pub fn pop_u32(bytes: &mut &[u8]) -> u32 { 60 | let slice = pop_bytes(bytes, 4); 61 | // Safety: slice is guaranteed to be 4 bytes long 62 | u32::from_ne_bytes(unsafe { *(slice.as_ptr() as *const [u8; 4]) }) 63 | } 64 | } 65 | 66 | pub use bitflags::bitflags; 67 | pub use bytes::{BufMut, BytesMut}; 68 | pub use futures_lite::ready; 69 | pub use num_enum; 70 | pub use runa_wayland_types as types; 71 | 72 | pub mod future { 73 | use std::{pin::Pin, task::Poll}; 74 | pub struct PollMapFn<'a, F, O, T> { 75 | inner: Option>, 76 | f: F, 77 | _marker: ::std::marker::PhantomData, 78 | } 79 | impl<'a, F, O, T> ::std::future::Future for PollMapFn<'a, F, O, T> 80 | where 81 | F: Fn(Pin<&'a mut T>, &mut ::std::task::Context<'_>) -> Poll + Unpin, 82 | O: Unpin, 83 | { 84 | type Output = O; 85 | 86 | fn poll( 87 | mut self: ::std::pin::Pin<&mut Self>, 88 | cx: &mut ::std::task::Context<'_>, 89 | ) -> ::std::task::Poll { 90 | use std::ptr::NonNull; 91 | let inner = self 92 | .inner 93 | .take() 94 | .expect("PollMapFn polled after completion"); 95 | unsafe { 96 | // Safety: 97 | // 1. We don't move the reader using the &mut we get from 98 | // Pin::get_mut_unchecked. 2. We know Fn(Pin<&'de mut D>, cx) -> 99 | // T is implemented for F, so we know the lifetime going from 100 | // 'de to T is sound. 3. In case of Ready, we won't touch 101 | // raw_reader ever again here, and inner is left empty. 102 | // 4. In case of Pending, we know the inner is no longer borrowed (contract of 103 | // the poll_map_fn function), so we can get our Pin<&mut 104 | // inner> back safely. 5. self.f must be called with Pin created 105 | // from raw_inner to satisfy stacked borrow rules. 106 | let mut raw_inner = NonNull::from(inner.get_unchecked_mut()); 107 | match (self.f)(Pin::new_unchecked(raw_inner.as_mut()), cx) { 108 | Poll::Ready(v) => Poll::Ready(v), 109 | Poll::Pending => { 110 | self.inner = Some(Pin::new_unchecked(raw_inner.as_mut())); 111 | Poll::Pending 112 | }, 113 | } 114 | } 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /wayland-tools/parser/src/protocol.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Debug)] 2 | pub struct Protocol { 3 | pub name: String, 4 | pub copyright: Option, 5 | pub description: Option<(String, String)>, 6 | pub interfaces: Vec, 7 | } 8 | 9 | impl Protocol { 10 | pub fn new(name: String) -> Protocol { 11 | Protocol { 12 | name, 13 | copyright: None, 14 | description: None, 15 | interfaces: Vec::new(), 16 | } 17 | } 18 | } 19 | 20 | #[derive(Clone, Debug)] 21 | pub struct Interface { 22 | pub name: String, 23 | pub version: u32, 24 | pub description: Option<(String, String)>, 25 | pub requests: Vec, 26 | pub events: Vec, 27 | pub enums: Vec, 28 | } 29 | 30 | impl Default for Interface { 31 | fn default() -> Interface { 32 | Interface { 33 | name: String::new(), 34 | version: 1, 35 | description: None, 36 | requests: Vec::new(), 37 | events: Vec::new(), 38 | enums: Vec::new(), 39 | } 40 | } 41 | } 42 | 43 | impl Interface { 44 | pub fn new() -> Interface { 45 | Interface::default() 46 | } 47 | } 48 | 49 | #[derive(Clone, Debug)] 50 | pub struct Message { 51 | pub name: String, 52 | pub typ: Option, 53 | pub since: u32, 54 | pub description: Option<(String, String)>, 55 | pub args: Vec, 56 | } 57 | impl Default for Message { 58 | fn default() -> Self { 59 | Message { 60 | name: String::new(), 61 | typ: None, 62 | since: 1, 63 | description: None, 64 | args: Vec::new(), 65 | } 66 | } 67 | } 68 | 69 | impl Message { 70 | pub fn new() -> Message { 71 | Message::default() 72 | } 73 | 74 | pub fn all_null(&self) -> bool { 75 | self.args 76 | .iter() 77 | .all(|a| !((a.typ == Type::Object || a.typ == Type::NewId) && a.interface.is_some())) 78 | } 79 | } 80 | 81 | #[derive(Clone, Debug)] 82 | pub struct Arg { 83 | pub name: String, 84 | pub typ: Type, 85 | pub interface: Option, 86 | pub summary: Option, 87 | pub description: Option<(String, String)>, 88 | pub allow_null: bool, 89 | pub enum_: Option, 90 | } 91 | 92 | impl Default for Arg { 93 | fn default() -> Arg { 94 | Arg { 95 | name: String::new(), 96 | typ: Type::Object, 97 | interface: None, 98 | summary: None, 99 | description: None, 100 | allow_null: false, 101 | enum_: None, 102 | } 103 | } 104 | } 105 | 106 | impl Arg { 107 | pub fn new() -> Arg { 108 | Arg::default() 109 | } 110 | } 111 | 112 | #[derive(Clone, Debug)] 113 | pub struct Enum { 114 | pub name: String, 115 | pub since: u16, 116 | pub description: Option<(String, String)>, 117 | pub entries: Vec, 118 | pub bitfield: bool, 119 | } 120 | 121 | impl Default for Enum { 122 | fn default() -> Enum { 123 | Enum { 124 | name: String::new(), 125 | since: 1, 126 | description: None, 127 | entries: Vec::new(), 128 | bitfield: false, 129 | } 130 | } 131 | } 132 | 133 | impl Enum { 134 | pub fn new() -> Enum { 135 | Enum::default() 136 | } 137 | } 138 | 139 | #[derive(Clone, Debug)] 140 | pub struct Entry { 141 | pub name: String, 142 | pub value: u32, 143 | pub since: u16, 144 | pub description: Option<(String, String)>, 145 | pub summary: Option, 146 | } 147 | 148 | impl Default for Entry { 149 | fn default() -> Entry { 150 | Entry { 151 | name: String::new(), 152 | value: 0, 153 | since: 1, 154 | description: None, 155 | summary: None, 156 | } 157 | } 158 | } 159 | impl Entry { 160 | pub fn new() -> Entry { 161 | Entry::default() 162 | } 163 | } 164 | 165 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] 166 | pub enum Type { 167 | Int, 168 | Uint, 169 | Fixed, 170 | String, 171 | Object, 172 | NewId, 173 | Array, 174 | Fd, 175 | Destructor, 176 | } 177 | 178 | impl Type { 179 | pub fn nullable(self) -> bool { 180 | matches!(self, Type::String | Type::Object) 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /runa-core/src/events/aggregate.rs: -------------------------------------------------------------------------------- 1 | //! An event source that aggregates sent events together for each receiver. 2 | //! 3 | //! This can be used to create a sort of event-based diff update mechanism, 4 | //! where a normal stream-of-event sources are not suitable. 5 | //! 6 | //! A good example is the object store. If store updates are a stream of 7 | //! events, we can imagine a sequence of events like: 8 | //! 9 | //! - Insert Object { id: 1, interface: wl_surface } 10 | //! - Remove Object { id: 1 } 11 | //! - Insert Object { id: 1, interface: wl_buffer } 12 | //! 13 | //! If listener only started reading the events after the third event, they 14 | //! will start processing the first insertion event and find out object 1 is 15 | //! not actually a wl_surface, and be confused. 16 | //! 17 | //! What should really happen is that the first two events should "cancel 18 | //! out" for that listener, and the listener should only see the third one. 19 | //! 20 | //! The aggregate event source will do exactly that. 21 | 22 | use std::{ 23 | cell::RefCell, 24 | pin::Pin, 25 | rc::Rc, 26 | task::{Context, Poll, Waker}, 27 | }; 28 | 29 | use futures_core::{FusedStream, Stream}; 30 | 31 | #[derive(Debug)] 32 | struct ReceiverInner { 33 | event: E, 34 | waker: Option, 35 | } 36 | 37 | /// A sender for the aggregate event source. 38 | /// 39 | /// The type parameter `E` is not the event type, it represents the internal 40 | /// state of the event source. It can be seen as an aggregate of the actual 41 | /// event type. 42 | #[derive(Debug)] 43 | pub struct Sender { 44 | receivers: RefCell>>>>, 45 | } 46 | 47 | impl Default for Sender { 48 | fn default() -> Self { 49 | Self { 50 | receivers: Vec::new().into(), 51 | } 52 | } 53 | } 54 | 55 | impl Sender { 56 | /// Create a new sender for a aggregate event source 57 | pub fn new() -> Self { 58 | Default::default() 59 | } 60 | } 61 | 62 | impl Sender { 63 | /// Creata a new receiver for this sender 64 | pub fn new_receiver(&self) -> Receiver { 65 | let inner = Rc::new(RefCell::new(ReceiverInner { 66 | event: E::default(), 67 | waker: None, 68 | })); 69 | self.receivers.borrow_mut().push(inner.clone()); 70 | Receiver { 71 | inner, 72 | terminated: false, 73 | } 74 | } 75 | } 76 | 77 | /// A receiver for the aggregate event source. 78 | /// 79 | /// The type parameter `E` is not the event type, it represents the internal 80 | /// state of the event source. It can be seen as an aggregate of the actual 81 | /// event type. 82 | #[derive(Debug)] 83 | pub struct Receiver { 84 | inner: Rc>>, 85 | terminated: bool, 86 | } 87 | 88 | impl Sender { 89 | /// Send an event to all receivers 90 | pub fn send(&self, event: I) 91 | where 92 | E: Extend, 93 | { 94 | for receiver in self.receivers.borrow().iter() { 95 | let mut receiver = receiver.borrow_mut(); 96 | receiver.event.extend(Some(event.clone())); 97 | if let Some(waker) = receiver.waker.take() { 98 | waker.wake_by_ref(); 99 | } 100 | } 101 | } 102 | } 103 | 104 | impl super::EventSource for Sender { 105 | type Source = impl Stream + FusedStream + Unpin; 106 | 107 | fn subscribe(&self) -> Self::Source { 108 | self.new_receiver() 109 | } 110 | } 111 | 112 | impl Stream for Receiver { 113 | type Item = E::Item; 114 | 115 | fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 116 | let Self { inner, terminated } = self.get_mut(); 117 | let strong_count = Rc::strong_count(inner); 118 | let mut inner = inner.borrow_mut(); 119 | // If another task is already polling this receiver, we wake up that task. 120 | if let Some(old_waker) = inner.waker.take() { 121 | if !old_waker.will_wake(cx.waker()) { 122 | old_waker.wake() 123 | } 124 | } 125 | 126 | if let Some(item) = inner.event.next() { 127 | return Poll::Ready(Some(item)) 128 | } 129 | 130 | if strong_count == 1 { 131 | // We are the only owner, meaning the sender is gone 132 | *terminated = true; 133 | return Poll::Ready(None) 134 | } 135 | 136 | inner.waker = Some(cx.waker().clone()); 137 | Poll::Pending 138 | } 139 | } 140 | 141 | impl FusedStream for Receiver { 142 | fn is_terminated(&self) -> bool { 143 | self.terminated 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: 4 | push: 5 | branches: 6 | - next 7 | pull_request: 8 | 9 | jobs: 10 | format: 11 | runs-on: ubuntu-22.04 12 | steps: 13 | - name: Checkout sources 14 | uses: actions/checkout@v3 15 | with: 16 | submodules: recursive 17 | - name: Rust toolchain 18 | uses: dtolnay/rust-toolchain@nightly 19 | with: 20 | components: rustfmt 21 | - name: Cargo cache 22 | uses: actions/cache@v3 23 | with: 24 | path: | 25 | ~/.cargo/registry 26 | ~/.cargo/git 27 | key: ${{ runner.os }}-cargo-rust_stable-${{ hashFiles('**/Cargo.toml') }} 28 | - name: Format 29 | run: cargo fmt --all -- --check 30 | 31 | clippy-check: 32 | runs-on: ubuntu-22.04 33 | steps: 34 | - name: Checkout sources 35 | uses: actions/checkout@v3 36 | with: 37 | submodules: recursive 38 | - name: Rust toolchain 39 | uses: dtolnay/rust-toolchain@nightly 40 | with: 41 | components: clippy 42 | - name: Get date for registry cache 43 | id: date 44 | run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT 45 | - name: Cargo registry cache 46 | uses: actions/cache@v3 47 | with: 48 | path: | 49 | ~/.cargo/registry/index 50 | ~/.cargo/registry/cache 51 | ~/.cargo/git 52 | key: ${{ runner.os }}-cargo-registry-${{ steps.date.outputs.date }} 53 | restore-keys: ${{ runner.os }}-cargo-registry- 54 | - name: Get cargo-cache latest version 55 | id: cargocacheversion 56 | run: echo "version=$(cargo search 'cargo-cache' --limit 1 | head -n 1 | cut -d ' ' -f 3 | cut -d '"' -f 2)" >> $GITHUB_OUTPUT 57 | - name: Cargo binaries cache 58 | uses: actions/cache@v3 59 | with: 60 | path: | 61 | ~/.cargo/bin/cargo-cache 62 | ~/.cargo/.crates.toml 63 | ~/.cargo/.crates2.json 64 | key: ${{ runner.os }}-cargo-binaries-${{ steps.cargocacheversion.outputs.version }} 65 | - name: Install cargo-cache 66 | run: cargo install cargo-cache --version ${{ steps.cargocacheversion.outputs.version }} 67 | - name: Clean cargo cache of old items 68 | run: cargo cache clean-unref 69 | - name: System dependencies 70 | run: sudo apt-get update; sudo apt-get install -y libudev-dev libgbm-dev libxkbcommon-dev libegl1-mesa-dev libwayland-dev libinput-dev libdbus-1-dev libsystemd-dev libseat-dev 71 | - name: Clippy 72 | run: cargo clippy -- -D warnings 73 | 74 | doc: 75 | name: Documentation on Github Pages 76 | runs-on: ubuntu-22.04 77 | 78 | steps: 79 | - name: Checkout sources 80 | uses: actions/checkout@v3 81 | with: 82 | submodules: recursive 83 | 84 | - name: Get date for registry cache 85 | id: date 86 | run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT 87 | - name: Cargo registry cache 88 | uses: actions/cache@v3 89 | with: 90 | path: | 91 | ~/.cargo/registry/index 92 | ~/.cargo/registry/cache 93 | ~/.cargo/git 94 | key: ${{ runner.os }}-cargo-registry-${{ steps.date.outputs.date }} 95 | restore-keys: ${{ runner.os }}-cargo-registry- 96 | 97 | - name: Rust toolchain 98 | uses: dtolnay/rust-toolchain@nightly 99 | 100 | - name: System dependencies 101 | run: sudo apt-get update; sudo apt-get install -y libudev-dev libgbm-dev libxkbcommon-dev libegl1-mesa-dev libwayland-dev libinput-dev libdbus-1-dev libsystemd-dev libseat-dev 102 | 103 | - name: Build Documentation 104 | run: cargo doc --no-deps -p runa-core -p runa-orbiter -p runa-io -p runa-io-traits -p runa-wayland-protocols 105 | 106 | - name: Setup index 107 | run: echo "" > ./target/doc/index.html 108 | 109 | - name: Upload pages artifact 110 | uses: actions/upload-pages-artifact@v1 111 | with: 112 | path: ./target/doc 113 | 114 | # Deploy job 115 | deploy: 116 | needs: doc 117 | if: ${{ github.event_name == 'push' }} 118 | 119 | # Grant GITHUB_TOKEN the permissions required to make a Pages deployment 120 | permissions: 121 | pages: write # to deploy to Pages 122 | id-token: write # to verify the deployment originates from an appropriate source 123 | 124 | # Deploy to the github-pages environment 125 | environment: 126 | name: github-pages 127 | url: ${{ steps.deployment.outputs.page_url }} 128 | 129 | runs-on: ubuntu-latest 130 | steps: 131 | - name: Deploy to GitHub Pages 132 | id: deployment 133 | uses: actions/deploy-pages@v1 134 | -------------------------------------------------------------------------------- /runa-wayland-types/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::os::unix::{ 2 | io::OwnedFd, 3 | prelude::{AsRawFd, FromRawFd, RawFd}, 4 | }; 5 | 6 | use fixed::{traits::ToFixed, types::extra::U8}; 7 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 8 | pub struct NewId(pub u32); 9 | 10 | impl From for NewId { 11 | fn from(id: u32) -> Self { 12 | NewId(id) 13 | } 14 | } 15 | 16 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 17 | pub struct Fixed(pub fixed::FixedI32); 18 | #[derive(Debug)] 19 | pub enum Fd { 20 | Raw(RawFd), 21 | Owned(OwnedFd), 22 | } 23 | impl From for Fd { 24 | fn from(fd: RawFd) -> Self { 25 | Fd::Raw(fd) 26 | } 27 | } 28 | 29 | impl From for Fd { 30 | fn from(fd: OwnedFd) -> Self { 31 | Fd::Owned(fd) 32 | } 33 | } 34 | 35 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 36 | pub struct Object(pub u32); 37 | 38 | impl From for Object { 39 | fn from(id: u32) -> Self { 40 | Object(id) 41 | } 42 | } 43 | 44 | #[derive(Debug, Clone, PartialEq, Eq, Copy)] 45 | pub struct Str<'a>(pub &'a [u8]); 46 | #[derive(Debug, Clone, PartialEq, Eq)] 47 | pub struct String(pub Vec); 48 | 49 | impl From for String { 50 | fn from(s: std::string::String) -> Self { 51 | String(s.into_bytes()) 52 | } 53 | } 54 | 55 | impl<'a> From<&'a [u8]> for Str<'a> { 56 | fn from(value: &'a [u8]) -> Self { 57 | Str(value) 58 | } 59 | } 60 | 61 | impl<'a, const N: usize> From<&'a [u8; N]> for Str<'a> { 62 | fn from(value: &'a [u8; N]) -> Self { 63 | Str(value) 64 | } 65 | } 66 | 67 | impl String { 68 | pub fn as_str(&self) -> Str { 69 | Str(&self.0[..]) 70 | } 71 | } 72 | 73 | impl ::std::fmt::Display for NewId { 74 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 75 | write!(f, "NewId({})", self.0) 76 | } 77 | } 78 | 79 | impl AsRawFd for Fd { 80 | fn as_raw_fd(&self) -> RawFd { 81 | match self { 82 | Fd::Raw(fd) => *fd, 83 | Fd::Owned(fd) => fd.as_raw_fd(), 84 | } 85 | } 86 | } 87 | 88 | impl ::std::cmp::PartialEq for Fd { 89 | fn eq(&self, other: &Self) -> bool { 90 | self.as_raw_fd() == other.as_raw_fd() 91 | } 92 | } 93 | 94 | impl ::std::cmp::Eq for Fd {} 95 | 96 | impl Fd { 97 | /// Convert a `Fd` into an `OwnedFd`. 98 | /// 99 | /// # Safety 100 | /// 101 | /// The `Fd` must own the contained file descriptor, i.e. this file 102 | /// descriptor must not be used elsewhere. This is because `OwnedFd` 103 | /// will close the file descriptor when it is dropped, and invalidate 104 | /// the file descriptor if it's incorrectly used elsewhere. 105 | pub unsafe fn assume_owned(&mut self) -> &mut OwnedFd { 106 | match self { 107 | Fd::Raw(fd) => { 108 | *self = Fd::Owned(OwnedFd::from_raw_fd(*fd)); 109 | match self { 110 | Fd::Owned(fd) => fd, 111 | // Safety: we just assigned OwnedFd to self 112 | Fd::Raw(_) => std::hint::unreachable_unchecked(), 113 | } 114 | }, 115 | Fd::Owned(fd) => fd, 116 | } 117 | } 118 | 119 | /// Take ownership of the file descriptor. 120 | /// 121 | /// This will return `None` if the `Fd` does not own the file descriptor. 122 | pub fn take(&mut self) -> Option { 123 | match self { 124 | Fd::Raw(_) => None, 125 | Fd::Owned(fd) => { 126 | let mut fd2 = Fd::Raw(fd.as_raw_fd()); 127 | std::mem::swap(self, &mut fd2); 128 | match fd2 { 129 | Fd::Owned(fd) => Some(fd), 130 | // Safety: we just swapped a Fd::Owned into fd2 131 | Fd::Raw(_) => unsafe { std::hint::unreachable_unchecked() }, 132 | } 133 | }, 134 | } 135 | } 136 | 137 | /// Take ownership of the file descriptor. 138 | /// 139 | /// # Panic 140 | /// 141 | /// Panics if the `Fd` does not own the file descriptor. 142 | pub fn unwrap_owned(self) -> OwnedFd { 143 | match self { 144 | Fd::Raw(_) => panic!("file descriptor was not owned"), 145 | Fd::Owned(fd) => fd, 146 | } 147 | } 148 | 149 | /// Get a mutable reference to the owned file descriptor. 150 | /// 151 | /// # Panic 152 | /// 153 | /// Panics if the `Fd` does not own the file descriptor. 154 | pub fn unwrap_owned_mut(&mut self) -> &mut OwnedFd { 155 | match self { 156 | Fd::Raw(_) => panic!("file descriptor was not owned"), 157 | Fd::Owned(fd) => fd, 158 | } 159 | } 160 | } 161 | 162 | impl Fixed { 163 | pub fn from_bits(v: i32) -> Self { 164 | Self(fixed::FixedI32::from_bits(v)) 165 | } 166 | 167 | pub fn from_num(src: Src) -> Self { 168 | Self(fixed::FixedI32::from_num(src)) 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /runa-orbiter/src/objects/mod.rs: -------------------------------------------------------------------------------- 1 | //! Objects defined in wayland protocols 2 | 3 | use std::future::Future; 4 | 5 | use hashbrown::{HashMap, HashSet}; 6 | use runa_core::{ 7 | client::traits::{Client, EventDispatcher, EventHandler, EventHandlerAction, Store}, 8 | error::Error, 9 | objects::wayland_object, 10 | }; 11 | use runa_io::traits::WriteMessage; 12 | use runa_wayland_protocols::wayland::{wl_buffer::v1 as wl_buffer, wl_output::v4 as wl_output}; 13 | 14 | use crate::{ 15 | shell::{ 16 | buffers::{AttachableBuffer, BufferEvent, BufferLike, HasBuffer}, 17 | output::Output as ShellOutput, 18 | HasShell, 19 | }, 20 | utils::WeakPtr, 21 | }; 22 | 23 | pub mod compositor; 24 | pub mod input; 25 | pub mod shm; 26 | pub mod xdg_shell; 27 | 28 | /// Implementation of the `wl_buffer` interface. 29 | #[derive(Debug)] 30 | pub struct Buffer { 31 | pub(crate) buffer: AttachableBuffer, 32 | _event_handler_abort: crate::utils::AutoAbort, 33 | } 34 | 35 | impl Buffer { 36 | pub(crate) fn new, E: EventDispatcher>( 37 | buffer: B2, 38 | event_dispatcher: &mut E, 39 | ) -> Self { 40 | struct BufferEventHandler { 41 | object_id: u32, 42 | } 43 | impl EventHandler for BufferEventHandler { 44 | type Message = BufferEvent; 45 | 46 | type Future<'ctx> = impl Future< 47 | Output = Result>, 48 | > + 'ctx; 49 | 50 | fn handle_event<'ctx>( 51 | &'ctx mut self, 52 | _objects: &'ctx mut Ctx::ObjectStore, 53 | connection: &'ctx mut Ctx::Connection, 54 | _server_context: &'ctx Ctx::ServerContext, 55 | _message: &'ctx mut Self::Message, 56 | ) -> Self::Future<'ctx> { 57 | async move { 58 | connection 59 | .send(self.object_id, wl_buffer::events::Release {}) 60 | .await?; 61 | Ok(EventHandlerAction::Keep) 62 | } 63 | } 64 | } 65 | let buffer = buffer.into(); 66 | let object_id = buffer.object_id(); 67 | let rx = buffer.subscribe(); 68 | let (rx, abort_handle) = futures_util::stream::abortable(rx); 69 | let ret = Self { 70 | buffer: AttachableBuffer::new(buffer), 71 | _event_handler_abort: abort_handle.into(), 72 | }; 73 | event_dispatcher.add_event_handler(rx, BufferEventHandler { object_id }); 74 | ret 75 | } 76 | } 77 | 78 | #[wayland_object] 79 | impl wl_buffer::RequestDispatch for Buffer 80 | where 81 | Ctx: Client, 82 | Ctx::ServerContext: HasBuffer, 83 | { 84 | type Error = Error; 85 | 86 | type DestroyFut<'a> = impl Future> + 'a where Ctx: 'a; 87 | 88 | fn destroy(ctx: &mut Ctx, object_id: u32) -> Self::DestroyFut<'_> { 89 | ctx.objects_mut().remove(object_id); 90 | futures_util::future::ok(()) 91 | } 92 | } 93 | 94 | #[doc(hidden)] 95 | #[derive(Debug, Default)] 96 | pub struct OutputState { 97 | /// A map of shell outputs to their set of object ids 98 | pub(crate) all_outputs: HashMap, HashSet>, 99 | } 100 | 101 | /// Implementation of the `wl_output` interface. 102 | #[derive(Debug)] 103 | pub struct Output { 104 | /// The corresponding shell output object. 105 | pub(crate) output: WeakPtr, 106 | pub(crate) event_handler_abort: Option, 107 | } 108 | 109 | #[wayland_object(state = "OutputState")] 110 | impl wl_output::RequestDispatch for Output 111 | where 112 | Ctx::ServerContext: HasShell, 113 | Ctx: Client, 114 | { 115 | type Error = Error; 116 | 117 | type ReleaseFut<'a> = impl Future> + 'a where Ctx: 'a; 118 | 119 | fn release(ctx: &mut Ctx, object_id: u32) -> Self::ReleaseFut<'_> { 120 | async move { 121 | use runa_core::objects::AnyObject; 122 | let objects = ctx.objects_mut(); 123 | let object = objects.remove(object_id).unwrap(); 124 | let object = object.cast::().unwrap(); 125 | 126 | // Remove ourself from all_outputs 127 | let Some(state) = objects.get_state_mut::() else { 128 | // No bound output objects left anymore 129 | return Ok(()); 130 | }; 131 | 132 | use hashbrown::hash_map::Entry; 133 | let Entry::Occupied(mut all_outputs_entry) = 134 | state.all_outputs.entry(object.output.clone()) 135 | else { 136 | panic!("Output object not found in all_outputs"); 137 | }; 138 | 139 | let ids = all_outputs_entry.get_mut(); 140 | ids.remove(&object_id); 141 | if ids.is_empty() { 142 | all_outputs_entry.remove(); 143 | } 144 | 145 | Ok(()) 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /runa-core/src/error.rs: -------------------------------------------------------------------------------- 1 | //! Error traits and types 2 | 3 | use runa_wayland_protocols::wayland::wl_display::v1 as wl_display; 4 | use thiserror::Error; 5 | 6 | use crate::objects::DISPLAY_ID; 7 | 8 | /// Converting Rust errors to Wayland error events 9 | pub trait ProtocolError: std::error::Error + Send + Sync + 'static { 10 | /// Returns an object id and a wayland error code associated with this 11 | /// error, in that order. If not None, an error event will be sent to 12 | /// the client. 13 | fn wayland_error(&self) -> Option<(u32, u32)>; 14 | /// Returns whether this error is fatal. If true, the client should be 15 | /// disconnected. 16 | fn fatal(&self) -> bool; 17 | } 18 | 19 | /// Common errors that can occur when handling client requests 20 | #[derive(Error, Debug)] 21 | pub enum Error { 22 | /// Failed to deserialize a message 23 | #[error("Deserialization error: {0}")] 24 | Deserialization(#[from] runa_io::traits::de::Error), 25 | /// I/O error 26 | #[error("I/O error: {0}")] 27 | Io(#[from] std::io::Error), 28 | /// Client tried to use an ID that is already in use 29 | #[error("NewId {0} sent by the client is already in use")] 30 | IdExists(u32), 31 | /// Client tried to use an object that's not suitable for the request 32 | #[error("Object {0} is invalid for this operation")] 33 | InvalidObject(u32), 34 | /// Client tried to use an unknown global 35 | #[error("Unknown Global: {0}")] 36 | UnknownGlobal(u32), 37 | /// Client tried to use an unknown object 38 | #[error("Unknown object: {0}")] 39 | UnknownObject(u32), 40 | /// Unknown error 41 | #[error("An unknown error occurred: {0}")] 42 | UnknownError(&'static str), 43 | /// Unknown error that is fatal 44 | #[error("An unknown fatal error occurred: {0}")] 45 | UnknownFatalError(&'static str), 46 | /// Client tried to invoke an unimplemented method 47 | #[error("{0} is not implemented yet")] 48 | NotImplemented(&'static str), 49 | /// Custom error defined by external object implementations 50 | #[error("{source}")] 51 | Custom { 52 | /// The source error 53 | #[source] 54 | source: Box, 55 | /// The ID of the object that caused the error, and the wayland error 56 | /// code If not None, an error will be sent to the client. 57 | object_id_and_code: Option<(u32, u32)>, 58 | /// Whehther the client should be disconnected 59 | is_fatal: bool, 60 | }, 61 | } 62 | 63 | impl From for Error { 64 | fn from(f: std::convert::Infallible) -> Self { 65 | match f {} 66 | } 67 | } 68 | 69 | impl From> for Error { 70 | fn from(e: Box) -> Self { 71 | Self::Custom { 72 | object_id_and_code: Some((DISPLAY_ID, wl_display::enums::Error::Implementation as u32)), 73 | is_fatal: true, 74 | source: e, 75 | } 76 | } 77 | } 78 | 79 | impl Error { 80 | /// Create a new error from a custom error type 81 | pub fn custom(e: E) -> Self 82 | where 83 | E: ProtocolError + Send + Sync + 'static, 84 | { 85 | Self::Custom { 86 | object_id_and_code: e.wayland_error(), 87 | is_fatal: e.fatal(), 88 | source: Box::new(e), 89 | } 90 | } 91 | } 92 | impl ProtocolError for Error { 93 | fn wayland_error(&self) -> Option<(u32, u32)> { 94 | use runa_wayland_protocols::wayland::wl_display::v1::enums::Error::*; 95 | match self { 96 | Self::Deserialization(_) => Some((DISPLAY_ID, InvalidMethod as u32)), 97 | Self::IdExists(_) => Some((DISPLAY_ID, InvalidObject as u32)), 98 | Self::UnknownGlobal(_) => Some((DISPLAY_ID, InvalidObject as u32)), 99 | Self::UnknownObject(_) => Some((DISPLAY_ID, InvalidObject as u32)), 100 | Self::InvalidObject(_) => Some((DISPLAY_ID, InvalidObject as u32)), 101 | Self::UnknownError(_) => Some((DISPLAY_ID, Implementation as u32)), 102 | Self::UnknownFatalError(_) => Some((DISPLAY_ID, Implementation as u32)), 103 | Self::NotImplemented(_) => Some((DISPLAY_ID, Implementation as u32)), 104 | Self::Custom { 105 | object_id_and_code, .. 106 | } => *object_id_and_code, 107 | Self::Io(_) => None, /* We had an I/O error communicating with the client, so we 108 | * better not send anything further */ 109 | } 110 | } 111 | 112 | fn fatal(&self) -> bool { 113 | match self { 114 | Self::Deserialization(_) | 115 | Self::Io(_) | 116 | Self::IdExists(_) | 117 | Self::UnknownGlobal(_) | 118 | Self::UnknownObject(_) | 119 | Self::InvalidObject(_) | 120 | Self::UnknownFatalError(_) | 121 | Self::NotImplemented(_) => true, 122 | Self::UnknownError(_) => false, 123 | Self::Custom { 124 | is_fatal: fatal, .. 125 | } => *fatal, 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /runa-io/src/utils.rs: -------------------------------------------------------------------------------- 1 | #![doc(hidden)] 2 | 3 | use std::{ 4 | os::{ 5 | fd::FromRawFd, 6 | unix::io::{OwnedFd, RawFd}, 7 | }, 8 | pin::Pin, 9 | task::Poll, 10 | }; 11 | 12 | use runa_io_traits::ReadMessage; 13 | 14 | use super::traits::{self, AsyncBufReadWithFd, AsyncReadWithFd, AsyncWriteWithFd, OwnedFds}; 15 | 16 | /// An in-memory buffer that implements `AsyncWrite` and `AsyncWriteWithFd`. 17 | /// Intended for testing purposes. 18 | #[derive(Default, Debug)] 19 | pub struct WritePool { 20 | inner: Vec, 21 | fds: Vec, 22 | } 23 | 24 | impl futures_lite::AsyncWrite for WritePool { 25 | fn poll_write( 26 | mut self: Pin<&mut Self>, 27 | _cx: &mut std::task::Context<'_>, 28 | buf: &[u8], 29 | ) -> Poll> { 30 | self.inner.extend_from_slice(buf); 31 | Poll::Ready(Ok(buf.len())) 32 | } 33 | 34 | fn poll_flush( 35 | self: Pin<&mut Self>, 36 | _cx: &mut std::task::Context<'_>, 37 | ) -> Poll> { 38 | Poll::Ready(Ok(())) 39 | } 40 | 41 | fn poll_close( 42 | self: Pin<&mut Self>, 43 | _cx: &mut std::task::Context<'_>, 44 | ) -> Poll> { 45 | Poll::Ready(Ok(())) 46 | } 47 | } 48 | 49 | impl AsyncWriteWithFd for WritePool { 50 | fn poll_write_with_fds( 51 | mut self: Pin<&mut Self>, 52 | _cx: &mut std::task::Context<'_>, 53 | buf: &[u8], 54 | fds: &mut Fds, 55 | ) -> Poll> { 56 | self.inner.extend_from_slice(buf); 57 | fds.take(&mut self.fds); 58 | Poll::Ready(Ok(buf.len())) 59 | } 60 | } 61 | 62 | impl WritePool { 63 | /// Create a new `WritePool`. 64 | pub fn new() -> Self { 65 | Self { 66 | inner: Vec::new(), 67 | fds: Vec::new(), 68 | } 69 | } 70 | 71 | /// Consume the `WritePool` and return the inner buffer and file 72 | /// descriptors. 73 | pub fn into_inner(self) -> (Vec, Vec) { 74 | (self.inner, self.fds) 75 | } 76 | } 77 | 78 | /// An in-memory buffer that implements `AsyncRead` and `AsyncReadWithFd`. 79 | /// Intended for testing purposes. 80 | #[derive(Debug)] 81 | pub struct ReadPool { 82 | inner: Vec, 83 | fds: Vec, 84 | } 85 | 86 | impl std::io::Read for ReadPool { 87 | fn read(&mut self, buf: &mut [u8]) -> std::io::Result { 88 | let len = std::cmp::min(buf.len(), self.inner.len()); 89 | buf[..len].copy_from_slice(&self.inner[..len]); 90 | self.inner.drain(..len); 91 | Ok(len) 92 | } 93 | } 94 | 95 | impl futures_lite::AsyncRead for ReadPool { 96 | fn poll_read( 97 | mut self: Pin<&mut Self>, 98 | _cx: &mut std::task::Context<'_>, 99 | buf: &mut [u8], 100 | ) -> Poll> { 101 | use std::io::Read; 102 | std::task::Poll::Ready(self.read(buf)) 103 | } 104 | } 105 | 106 | impl futures_lite::AsyncBufRead for ReadPool { 107 | fn poll_fill_buf( 108 | self: Pin<&mut Self>, 109 | _cx: &mut std::task::Context<'_>, 110 | ) -> Poll> { 111 | Poll::Ready(Ok(self.get_mut().inner.as_slice())) 112 | } 113 | 114 | fn consume(mut self: Pin<&mut Self>, amt: usize) { 115 | self.inner.drain(..amt); 116 | } 117 | } 118 | 119 | unsafe impl AsyncBufReadWithFd for ReadPool { 120 | fn poll_fill_buf_until<'a>( 121 | self: Pin<&'a mut Self>, 122 | _cx: &mut std::task::Context<'_>, 123 | len: usize, 124 | ) -> Poll> { 125 | if len > self.inner.len() { 126 | Poll::Ready(Err(std::io::Error::new( 127 | std::io::ErrorKind::UnexpectedEof, 128 | "Unexpected EOF", 129 | ))) 130 | } else { 131 | Poll::Ready(Ok(())) 132 | } 133 | } 134 | 135 | fn fds(&self) -> &[RawFd] { 136 | &self.fds[..] 137 | } 138 | 139 | fn buffer(&self) -> &[u8] { 140 | &self.inner[..] 141 | } 142 | 143 | fn consume(mut self: Pin<&mut Self>, amt: usize, amt_fd: usize) { 144 | self.inner.drain(..amt); 145 | self.fds.drain(..amt_fd); 146 | } 147 | } 148 | 149 | impl ReadMessage for ReadPool {} 150 | 151 | impl AsyncReadWithFd for ReadPool { 152 | fn poll_read_with_fds( 153 | mut self: Pin<&mut Self>, 154 | _cx: &mut std::task::Context<'_>, 155 | buf: &mut [u8], 156 | fds: &mut Fds, 157 | ) -> Poll> { 158 | use std::io::Read; 159 | let len = self.read(buf).unwrap(); 160 | let fd_len = std::cmp::min(fds.len(), self.fds.len()); 161 | fds.extend( 162 | self.fds 163 | .drain(..fd_len) 164 | .map(|fd| unsafe { OwnedFd::from_raw_fd(fd) }), 165 | ); 166 | Poll::Ready(Ok(len)) 167 | } 168 | } 169 | 170 | impl ReadPool { 171 | /// Create a new `ReadPool` with the given data and file descriptors. 172 | pub fn new(data: Vec, fds: Vec) -> Self { 173 | Self { inner: data, fds } 174 | } 175 | 176 | pub fn is_eof(&self) -> bool { 177 | self.inner.is_empty() && self.fds.is_empty() 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /runa-core/src/events/broadcast.rs: -------------------------------------------------------------------------------- 1 | //! A simple broadcast event source based on [`async_broadcast`] 2 | 3 | use std::{future::Future, pin::Pin}; 4 | 5 | use async_broadcast::TrySendError; 6 | use derive_where::derive_where; 7 | /// An event source implementation based on the [`async_broadcast`] 8 | /// channels. 9 | /// 10 | /// This event source is mpmc, so it can be cloned, enabling multiple sender 11 | /// to send at the same time 12 | #[derive(Debug)] 13 | #[derive_where(Clone)] 14 | pub struct Broadcast( 15 | async_broadcast::Sender, 16 | // Intentionally not used, just here to keep the receiver alive 17 | #[allow(dead_code)] async_broadcast::InactiveReceiver, 18 | ); 19 | 20 | /// An event source implementation based on the [`async_broadcast`] 21 | /// channels. 22 | /// 23 | /// Like [`Broadcast`], except when the internal queue is full, the oldest 24 | /// event will be dropped. 25 | /// 26 | /// This event source is mpmc, so it can be cloned, enabling multiple sender 27 | /// to send at the same time 28 | #[derive(Debug)] 29 | #[derive_where(Clone)] 30 | pub struct Ring( 31 | async_broadcast::Sender, 32 | // Intentionally not used, just here to keep the receiver alive 33 | #[allow(dead_code)] async_broadcast::InactiveReceiver, 34 | ); 35 | 36 | impl Default for Broadcast { 37 | fn default() -> Self { 38 | let (mut tx, rx) = async_broadcast::broadcast(16); 39 | tx.set_await_active(false); 40 | Self(tx, rx.deactivate()) 41 | } 42 | } 43 | 44 | impl Ring { 45 | /// Create a new ring broadcast event source 46 | pub fn new(capacity: usize) -> Self { 47 | let (mut tx, rx) = async_broadcast::broadcast(capacity); 48 | tx.set_await_active(false); 49 | tx.set_overflow(true); 50 | Self(tx, rx.deactivate()) 51 | } 52 | 53 | /// Change the number of events the ring event source can hold 54 | pub fn set_capacity(&mut self, capacity: usize) { 55 | self.0.set_capacity(capacity); 56 | } 57 | 58 | /// Return the number of sender associated with this event source 59 | pub fn sender_count(&self) -> usize { 60 | // The sender stored in `Ring` is never used for sending, so minus 1 here 61 | self.0.sender_count() 62 | } 63 | } 64 | 65 | impl Ring { 66 | /// Send a new event to all receivers 67 | pub fn broadcast(&self, msg: E) { 68 | assert!(!self.0.is_closed()); 69 | let result = self.0.try_broadcast(msg); 70 | assert!(!matches!(result, Err(TrySendError::Full(_)))); 71 | } 72 | } 73 | 74 | impl Broadcast { 75 | /// Send a new event to all receivers 76 | pub fn broadcast(&self, msg: E) -> impl Future + '_ { 77 | // The sender is never closed because we hold a inactive receiver. 78 | assert!(!self.0.is_closed()); 79 | 80 | // Here this should only error if there is no active receiver, which 81 | // is fine. 82 | async move { 83 | let _ = self.0.broadcast(msg).await; 84 | } 85 | } 86 | 87 | /// Like [`Self::broadcast`], but the returned future doesn't borrow from 88 | /// `self`. 89 | pub fn broadcast_owned(&self, msg: E) -> impl Future 90 | where 91 | E: 'static, 92 | { 93 | assert!(!self.0.is_closed()); 94 | let sender = self.0.clone(); 95 | 96 | async move { 97 | let _ = sender.broadcast(msg).await; 98 | } 99 | } 100 | 101 | /// Like [`Self::broadcast`], but if the queue is full, instead of waiting, 102 | /// this function will allocate more space in the queue. 103 | /// 104 | /// # Warning 105 | /// 106 | /// If there are stuck receivers, this will cause unbounded memory growth. 107 | pub fn broadcast_reserve(&mut self, msg: E) { 108 | assert!(!self.0.is_closed()); 109 | 110 | if self.0.is_full() { 111 | self.0.set_capacity(self.0.capacity() * 2); 112 | } 113 | 114 | let result = self.0.try_broadcast(msg); 115 | assert!(!matches!(result, Err(TrySendError::Full(_)))); 116 | } 117 | } 118 | 119 | /// The event stream for Broadcast and RingBroadcast event sources 120 | /// 121 | /// A wrapper of the broadcast receiver that deactivates the 122 | /// receiver, without closing the sender, when dropped. 123 | #[derive(Debug)] 124 | pub struct Receiver(Option>); 125 | impl Drop for Receiver { 126 | fn drop(&mut self) { 127 | if let Some(receiver) = self.0.take() { 128 | receiver.deactivate(); 129 | } 130 | } 131 | } 132 | impl futures_core::Stream for Receiver { 133 | type Item = E; 134 | 135 | fn poll_next( 136 | mut self: std::pin::Pin<&mut Self>, 137 | cx: &mut std::task::Context<'_>, 138 | ) -> std::task::Poll> { 139 | let this = self.0.as_mut().unwrap(); 140 | Pin::new(this).poll_next(cx) 141 | } 142 | } 143 | 144 | impl super::EventSource for Broadcast { 145 | type Source = Receiver; 146 | 147 | fn subscribe(&self) -> Self::Source { 148 | Receiver(Some(self.0.new_receiver())) 149 | } 150 | } 151 | 152 | impl super::EventSource for Ring { 153 | type Source = Receiver; 154 | 155 | fn subscribe(&self) -> Self::Source { 156 | Receiver(Some(self.0.new_receiver())) 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /runa-orbiter/src/shell/surface/roles/xdg.rs: -------------------------------------------------------------------------------- 1 | //! Types and traits for the xdg shell. 2 | 3 | use std::{collections::VecDeque, num::NonZeroU32}; 4 | 5 | use runa_core::provide_any::Demand; 6 | use runa_wayland_protocols::stable::xdg_shell::{ 7 | xdg_surface::v5 as xdg_surface, xdg_toplevel::v5 as xdg_toplevel, 8 | }; 9 | use runa_wayland_types::NewId; 10 | 11 | use crate::{ 12 | shell::Shell, 13 | utils::geometry::{coords, Extent, Rectangle}, 14 | }; 15 | 16 | /// The xdg_surface "role" 17 | /// 18 | /// This is not technically a role, since the surface can be assigned either a 19 | /// top-level or a popup role after this "role" is attached to it. But we still 20 | /// use the role interface because it's convenient. 21 | #[derive(Debug, Clone)] 22 | pub struct Surface { 23 | active: bool, 24 | geometry: Option>, 25 | pub(crate) pending_geometry: Option>, 26 | /// Pending configure events that haven't been ACK'd, associated with a 27 | /// oneshot channel which will be notified once the client ACK the 28 | /// configure event. 29 | pub(crate) pending_serial: VecDeque, 30 | /// The serial in the last ack_configure request. 31 | pub(crate) last_ack: Option, 32 | pub(crate) serial: NonZeroU32, 33 | pub(crate) object_id: u32, 34 | } 35 | 36 | impl Surface { 37 | #[inline] 38 | pub(crate) fn new(object_id: NewId) -> Self { 39 | Self { 40 | active: false, 41 | geometry: None, 42 | pending_geometry: None, 43 | pending_serial: VecDeque::new(), 44 | last_ack: None, 45 | serial: NonZeroU32::new(1).unwrap(), 46 | object_id: object_id.0, 47 | } 48 | } 49 | 50 | fn commit( 51 | &mut self, 52 | shell: &mut S, 53 | surface: &crate::shell::surface::Surface, 54 | _object_id: u32, 55 | ) -> Result<(), &'static str> { 56 | tracing::debug!("Committing xdg_surface"); 57 | if surface.pending().buffer().is_some() && self.last_ack.is_none() { 58 | return Err("Cannot attach buffer before the initial configure sequence is completed") 59 | } 60 | if self.pending_serial.is_empty() && self.last_ack.is_none() { 61 | // We haven't sent out the first configure event yet. 62 | // notify the configure listener which will send out the configure event. 63 | tracing::debug!("sending initial configure event"); 64 | surface.notify_layout_changed(shell.layout(surface.current_key())); 65 | } 66 | self.geometry = self.pending_geometry; 67 | 68 | Ok(()) 69 | } 70 | } 71 | 72 | impl super::Role for Surface { 73 | fn name(&self) -> &'static str { 74 | xdg_surface::NAME 75 | } 76 | 77 | fn provide<'a>(&'a self, demand: &mut Demand<'a>) { 78 | demand.provide_ref(self); 79 | } 80 | 81 | fn provide_mut<'a>(&'a mut self, demand: &mut Demand<'a>) { 82 | demand.provide_mut(self); 83 | } 84 | 85 | fn deactivate(&mut self, _shell: &mut S) { 86 | if !self.active { 87 | return 88 | } 89 | self.active = false; 90 | } 91 | 92 | fn is_active(&self) -> bool { 93 | self.active 94 | } 95 | } 96 | 97 | #[derive(Default, Debug, Clone, Copy)] 98 | pub(crate) struct TopLevelState { 99 | pub(crate) min_size: Option>, 100 | pub(crate) max_size: Option>, 101 | } 102 | 103 | /// The xdg_toplevel role 104 | #[derive(Debug)] 105 | pub struct TopLevel { 106 | pub(crate) base: Surface, 107 | is_active: bool, 108 | pub(crate) app_id: Option, 109 | pub(crate) title: Option, 110 | pub(crate) current: TopLevelState, 111 | pub(crate) pending: TopLevelState, 112 | pub(crate) object_id: u32, 113 | } 114 | 115 | impl TopLevel { 116 | pub(crate) fn new(base: Surface, object_id: u32) -> Self { 117 | Self { 118 | base, 119 | is_active: true, 120 | app_id: None, 121 | title: None, 122 | current: TopLevelState::default(), 123 | pending: TopLevelState::default(), 124 | object_id, 125 | } 126 | } 127 | 128 | /// Geometry of the surface, as defined by the xdg_toplevel role. 129 | pub fn geometry(&self) -> Option> { 130 | self.base.geometry 131 | } 132 | } 133 | 134 | impl super::Role for TopLevel { 135 | fn name(&self) -> &'static str { 136 | xdg_toplevel::NAME 137 | } 138 | 139 | fn deactivate(&mut self, _shell: &mut S) { 140 | if !self.is_active { 141 | return 142 | } 143 | self.is_active = false; 144 | } 145 | 146 | fn is_active(&self) -> bool { 147 | self.is_active 148 | } 149 | 150 | fn provide<'a>(&'a self, demand: &mut Demand<'a>) { 151 | demand.provide_ref(self).provide_ref(&self.base); 152 | } 153 | 154 | fn provide_mut<'a>(&'a mut self, demand: &mut Demand<'a>) { 155 | if let Some(mut receiver) = demand.maybe_provide_mut() { 156 | receiver.provide(self); 157 | } else if let Some(mut receiver) = demand.maybe_provide_mut() { 158 | receiver.provide(&mut self.base); 159 | } 160 | } 161 | 162 | fn pre_commit( 163 | &mut self, 164 | shell: &mut S, 165 | surface: &crate::shell::surface::Surface, 166 | ) -> Result<(), &'static str> { 167 | tracing::debug!("Committing xdg_toplevel"); 168 | let object_id = self.object_id; 169 | self.current = self.pending; 170 | 171 | self.base.commit(shell, surface, object_id)?; 172 | Ok(()) 173 | } 174 | } 175 | 176 | /// The xdg_popup role 177 | /// 178 | /// TODO: not implemented yet 179 | #[derive(Clone, Debug, Copy)] 180 | pub struct Popup; 181 | -------------------------------------------------------------------------------- /runa-core/src/client/mod.rs: -------------------------------------------------------------------------------- 1 | //! Per-client state 2 | //! 3 | //! A wayland compositor needs to maintain a set of states for each 4 | //! connected wayland client. For example, each client could have a set of bound 5 | //! objects. This module defines a common interface for such state types, so a 6 | //! crate that implements different wayland protcol interfaces can rely on this 7 | //! common interface and be generic over whatever state type the compositor 8 | //! author chooses to use. 9 | //! 10 | //! See [`traits`] for definition of the interface. The rest of this module 11 | //! provides some reference implementations of these interfaces. 12 | //! 13 | //! The documentation on [`traits::Client`] should give you a good idea of how 14 | //! most of the stuffs fit together. 15 | //! 16 | //! See also the crate level documentation for how `Client` fits into the 17 | //! overall picture. 18 | 19 | /// Wayland divides the ID space into a client portion and a server portion. The 20 | /// client is only allowed to allocate IDs up to this number, above this number 21 | /// are reserved for the the server. 22 | const CLIENT_MAX_ID: u32 = 0xfeffffff; 23 | 24 | pub mod event_dispatcher; 25 | pub mod store; 26 | pub mod traits; 27 | 28 | use std::{os::fd::RawFd, pin::Pin}; 29 | 30 | #[doc(inline)] 31 | pub use event_dispatcher::EventDispatcher; 32 | use runa_io::traits::ReadMessage; 33 | #[doc(inline)] 34 | pub use store::Store; 35 | 36 | /// Reference implementation of the [`traits::Client::dispatch`] method. 37 | /// 38 | /// This function reads a message from `reader`, looks up the corresponding 39 | /// object, and then calls the object's `dispatch` method to handle the message. 40 | /// It then checks if the `dispatch` method returned an error, and if so, sends 41 | /// the error to the client. The returned future resolves to a boolean, which 42 | /// indicates whether the client should be disconnected. 43 | /// 44 | /// You can simply call this function as the implementation of your own 45 | /// `dispatch` method. 46 | /// 47 | /// Notice there is a type bound requiring `Ctx::Object` to accept a `(&[u8], 48 | /// &[RawFd])` as its request type. This would be the case if you use 49 | /// [`#[derive(Object)`](crate::objects::Object) to generate you object type. If 50 | /// not, you are supposed to deserialize a wayland message from a 51 | /// `(&[u8], &[RawFd])` tuple yourself and then dispatch the deserialized 52 | /// message properly. 53 | pub async fn dispatch_to<'a, Ctx, R>(ctx: &'a mut Ctx, mut reader: Pin<&'a mut R>) -> bool 54 | where 55 | R: ReadMessage, 56 | Ctx: traits::Client, 57 | Ctx::Object: for<'b> crate::objects::Object = (&'b [u8], &'b [RawFd])>, 58 | { 59 | use runa_io::traits::{RawMessage, WriteMessage}; 60 | use runa_wayland_protocols::wayland::wl_display::v1 as wl_display; 61 | use runa_wayland_types as types; 62 | use traits::Store; 63 | 64 | use crate::{error::ProtocolError, objects::AnyObject}; 65 | let RawMessage { 66 | object_id, 67 | len, 68 | data, 69 | fds, 70 | } = match R::next_message(reader.as_mut()).await { 71 | Ok(v) => v, 72 | // I/O error, no point sending the error to the client 73 | Err(_) => return true, 74 | }; 75 | let (ret, bytes_read, fds_read) = <::Object as crate::objects::Object< 76 | Ctx, 77 | >>::dispatch(ctx, object_id, (data, fds)) 78 | .await; 79 | let (mut fatal, error) = match ret { 80 | Ok(_) => (false, None), 81 | Err(e) => ( 82 | e.fatal(), 83 | e.wayland_error() 84 | .map(|(object_id, error_code)| (object_id, error_code, e.to_string())), 85 | ), 86 | }; 87 | if let Some((object_id, error_code, msg)) = error { 88 | // We are going to disconnect the client so we don't care about the 89 | // error. 90 | fatal |= ctx 91 | .connection_mut() 92 | .send(crate::objects::DISPLAY_ID, wl_display::events::Error { 93 | object_id: types::Object(object_id), 94 | code: error_code, 95 | message: types::Str(msg.as_bytes()), 96 | }) 97 | .await 98 | .is_err(); 99 | } 100 | if !fatal { 101 | if bytes_read != len { 102 | let len_opcode = u32::from_ne_bytes(data[0..4].try_into().unwrap()); 103 | let opcode = len_opcode & 0xffff; 104 | tracing::error!( 105 | "unparsed bytes in buffer, read ({bytes_read}) != received ({len}). object_id: \ 106 | {}@{object_id}, opcode: {opcode}", 107 | ctx.objects() 108 | .get::(object_id) 109 | .map(|o| o.interface()) 110 | .unwrap_or("unknown") 111 | ); 112 | fatal = true; 113 | } 114 | reader.consume(bytes_read, fds_read); 115 | } 116 | fatal 117 | } 118 | 119 | #[doc(hidden)] // not ready for use yet 120 | pub mod serial { 121 | use hashbrown::HashMap; 122 | 123 | #[derive(Debug)] 124 | pub struct EventSerial { 125 | serials: HashMap, 126 | last_serial: u32, 127 | expire: std::time::Duration, 128 | } 129 | 130 | /// A serial allocator for event serials. Serials are automatically 131 | /// forgotten after a set amount of time. 132 | impl EventSerial { 133 | pub fn new(expire: std::time::Duration) -> Self { 134 | Self { 135 | serials: HashMap::new(), 136 | last_serial: 0, 137 | expire, 138 | } 139 | } 140 | 141 | fn clean_up(&mut self) { 142 | let now = std::time::Instant::now(); 143 | self.serials.retain(|_, (_, t)| *t + self.expire > now); 144 | } 145 | } 146 | 147 | impl crate::Serial for EventSerial { 148 | type Data = D; 149 | type Iter<'a> = <&'a Self as IntoIterator>::IntoIter; 150 | 151 | fn next_serial(&mut self, data: Self::Data) -> u32 { 152 | self.last_serial += 1; 153 | 154 | self.clean_up(); 155 | if self 156 | .serials 157 | .insert(self.last_serial, (data, std::time::Instant::now())) 158 | .is_some() 159 | { 160 | panic!( 161 | "serial {} already in use, expiration duration too long?", 162 | self.last_serial 163 | ); 164 | } 165 | self.last_serial 166 | } 167 | 168 | fn get(&self, serial: u32) -> Option<&Self::Data> { 169 | let now = std::time::Instant::now(); 170 | self.serials.get(&serial).and_then(|(d, timestamp)| { 171 | if *timestamp + self.expire > now { 172 | Some(d) 173 | } else { 174 | None 175 | } 176 | }) 177 | } 178 | 179 | fn expire(&mut self, serial: u32) -> bool { 180 | self.clean_up(); 181 | self.serials.remove(&serial).is_some() 182 | } 183 | 184 | fn iter(&self) -> Self::Iter<'_> { 185 | self.into_iter() 186 | } 187 | } 188 | impl<'a, D: 'a> IntoIterator for &'a EventSerial { 189 | type Item = (u32, &'a D); 190 | 191 | type IntoIter = impl Iterator + 'a where Self: 'a; 192 | 193 | fn into_iter(self) -> Self::IntoIter { 194 | self.serials.iter().map(|(k, (d, _))| (*k, d)) 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /runa-orbiter/src/shell/mod.rs: -------------------------------------------------------------------------------- 1 | //! Traits and types for the shell. 2 | //! 3 | //! A shell is the user facing component of the compositor. It manages a set of 4 | //! surfaces, and is responsible of presenting them to the user, as well as 5 | //! accepting user interactions. It is in control of all the visual aspects of 6 | //! a compositor. 7 | 8 | pub mod buffers; 9 | pub mod output; 10 | pub mod surface; 11 | use std::cell::RefCell; 12 | 13 | use runa_core::events::EventSource; 14 | use runa_wayland_protocols::wayland::{wl_keyboard::v9 as wl_keyboard, wl_seat::v9 as wl_seat}; 15 | 16 | /// Events emitted by a shell 17 | #[derive(Clone, Debug, Copy)] 18 | pub enum ShellEvent { 19 | /// The current set of surfaces have been rendered to screen. 20 | /// 21 | /// TODO: in future, we wants to be finer grained. as one render doesn't 22 | /// necessarily render all of the surfaces, e.g. some surfaces might not 23 | /// be visible. 24 | Render, 25 | } 26 | 27 | /// Shell 28 | /// 29 | /// This is the fundamental interface to the compositor's shell. A shell needs 30 | /// to support management of surface states, i.e. allocation, deallocation, and 31 | /// access. The allocated states are reference via a token, in order to avoid 32 | /// lifetime and ownership difficulties. 33 | /// 34 | /// The shell interface also defines a number of callbacks, which `runa-orbiter` 35 | /// will call in response to various operations done to surfaces. 36 | /// 37 | /// The shell is also an event source, it must emit the event defined in 38 | /// [`ShellEvent`] for various interfaces implemented here to function properly. 39 | pub trait Shell: Sized + EventSource + 'static { 40 | /// A token to surfaces. 41 | /// 42 | /// Eq and PartialEq should compare if the keys point to the same surface 43 | /// state. 44 | /// 45 | /// A token must be released, impls of Shell can choose to panic 46 | /// if it was dropped it without being released. 47 | type Token: std::fmt::Debug + Copy + PartialEq + Eq + std::hash::Hash; 48 | 49 | /// A buffer type. We allow a user supplied buffer type instead of `dyn 50 | /// Buffer` to avoid virtual call overhead, and allow for a more 51 | /// flexible Buffer trait. 52 | type Buffer: buffers::BufferLike; 53 | 54 | /// Allocate a SurfaceState and returns a handle to it. 55 | fn allocate(&mut self, state: surface::State) -> Self::Token; 56 | 57 | /// Release a token. 58 | fn destroy(&mut self, key: Self::Token); 59 | 60 | /// Get a reference to a SurfaceState by key. 61 | /// 62 | /// Returns None if the key is invalid. 63 | fn get(&self, key: Self::Token) -> &surface::State; 64 | 65 | /// Get a mutable reference to a SurfaceState. 66 | fn get_mut(&mut self, key: Self::Token) -> &mut surface::State { 67 | self.get_disjoint_mut([key])[0] 68 | } 69 | 70 | /// Get mutable references to multiple SurfaceStates. 71 | /// 72 | /// # Panic 73 | /// 74 | /// May panic if any of the keys are invalid, or if any two of the keys are 75 | /// equal. 76 | fn get_disjoint_mut( 77 | &mut self, 78 | keys: [Self::Token; N], 79 | ) -> [&mut surface::State; N]; 80 | 81 | /// Callback which is called when a role is added to a surface corresponds 82 | /// to the given surface state. A role can be attached using a committed 83 | /// state or a pending state, and they should have the same effects. 84 | /// 85 | /// # Panic 86 | /// 87 | /// May panic if the handle is invalid. 88 | #[allow(unused_variables)] 89 | fn role_added(&mut self, key: Self::Token, role: &'static str) {} 90 | 91 | /// Callback that is called when a surface has its assigned role 92 | /// deactivated. 93 | /// 94 | /// # Panic 95 | /// 96 | /// May panic if the handle is invalid. 97 | #[allow(unused_variables)] 98 | fn role_deactivated(&mut self, key: Self::Token, role: &'static str) {} 99 | 100 | /// A commit happened on the surface which used to have surface state `old`. 101 | /// The new state is `new`. If `old` is None, this is the first commit 102 | /// on the surface. After this call returns, `new` is considered currently 103 | /// committed. 104 | /// 105 | /// old can be equal to new, if no changes has been made to double buffered 106 | /// surface states since the last commit. 107 | /// 108 | /// Note, for synced subsurface, this is called when `new` became cached 109 | /// state. 110 | /// 111 | /// # Panic 112 | /// 113 | /// This function may panic if either handle is invalid. Or if 114 | /// `old` has never been committed before. 115 | #[allow(unused_variables)] 116 | fn post_commit(&mut self, old: Option, new: Self::Token) {} 117 | } 118 | 119 | /// Shell access 120 | /// 121 | /// Implemented by the server context (see 122 | /// [`ServerContext`](runa_core::client::traits::Client::ServerContext)) to 123 | /// indicate that it has a shell. 124 | pub trait HasShell: buffers::HasBuffer { 125 | /// Type of the shell 126 | type Shell: Shell::Buffer>; 127 | 128 | /// Get a reference to the shell 129 | fn shell(&self) -> &RefCell; 130 | } 131 | 132 | /// Keyboard repeat repeat_info 133 | /// 134 | /// See `wl_keyboard.repeat_info` for more details. 135 | #[allow(missing_docs)] 136 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 137 | pub struct RepeatInfo { 138 | pub rate: i32, 139 | pub delay: i32, 140 | } 141 | 142 | /// Keymap 143 | #[derive(Debug)] 144 | pub struct Keymap { 145 | /// Format of the keymap 146 | pub format: wl_keyboard::enums::KeymapFormat, 147 | /// File descriptor of the keymap 148 | pub fd: std::os::unix::io::OwnedFd, 149 | /// Size of the keymap in bytes 150 | pub size: u32, 151 | } 152 | 153 | /// Events emitted by a seat 154 | /// 155 | /// TODO: change to a bitflag struct, and use the aggregate event source. 156 | #[derive(Clone, Debug, Copy)] 157 | pub enum SeatEvent { 158 | /// The keymap has changed 159 | KeymapChanged, 160 | /// The repeat info has changed 161 | RepeatInfoChanged, 162 | /// The capabilities of the seat has changed 163 | CapabilitiesChanged, 164 | /// The name of the seat has changed 165 | NameChanged, 166 | } 167 | 168 | /// Seat 169 | /// 170 | /// A seat is a set of input output devices attached to a computer. This trait 171 | /// is mainly used by interface implementations to get information about mouse 172 | /// and keyboard devices attached. 173 | /// 174 | /// This is also an event source, it must emit events when information about the 175 | /// seat changes. 176 | /// 177 | /// This needs to be implemented by the 178 | /// [`ServerContext`](runa_core::client::traits::Client::ServerContext). 179 | pub trait Seat: EventSource { 180 | /// Get the capabilities of the seat. 181 | /// 182 | /// Whether the seat has a pointer, keyboard, or touch device attached. 183 | fn capabilities(&self) -> wl_seat::enums::Capability; 184 | 185 | /// Get the repeat info of the keyboard. 186 | fn repeat_info(&self) -> RepeatInfo; 187 | 188 | /// Get the current keymap. 189 | fn keymap(&self) -> &Keymap; 190 | 191 | /// Get the name of the seat. 192 | fn name(&self) -> &str; 193 | } 194 | 195 | pub mod xdg { 196 | //! Extensions to [`super::Shell`] to provide xdg shell specific 197 | //! functionalities. 198 | 199 | use crate::utils::geometry::{coords, Extent, Point}; 200 | 201 | /// Surface layout 202 | /// 203 | /// A surface layout is where the surface is positioned on the screen, and 204 | /// its screen space size. 205 | #[derive(Debug, Default, Clone, Copy)] 206 | pub struct Layout { 207 | /// The position of the surface on the screen. 208 | pub position: Option>, 209 | /// The size of the surface on the screen. 210 | pub extent: Option>, 211 | } 212 | 213 | /// Extension of [`super::Shell`] to provide xdg shell specific 214 | /// information. 215 | pub trait XdgShell: super::Shell { 216 | /// Ask the shell to calculate the layout of the given surface. 217 | fn layout(&self, _key: Self::Token) -> Layout { 218 | Layout::default() 219 | } 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /runa-orbiter/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | //! Utilities (we might need to separate this out to another crate). 2 | 3 | use std::{ 4 | hash::Hash, 5 | ops::Deref, 6 | rc::{Rc, Weak}, 7 | }; 8 | 9 | pub mod geometry; 10 | 11 | /// Wrapper of `futures_util::stream::AbortHandle` that automatically abort the 12 | /// stream when dropped. 13 | #[derive(Debug)] 14 | pub(crate) struct AutoAbort(futures_util::stream::AbortHandle); 15 | 16 | impl From for AutoAbort { 17 | fn from(value: futures_util::stream::AbortHandle) -> Self { 18 | Self(value) 19 | } 20 | } 21 | 22 | impl Drop for AutoAbort { 23 | fn drop(&mut self) { 24 | self.0.abort(); 25 | } 26 | } 27 | 28 | /// A wrapper of `Rc` that implements `Hash` and `Eq` by comparing 29 | /// raw pointer addresses. 30 | #[derive(Debug)] 31 | pub(crate) struct RcPtr(Rc); 32 | 33 | #[allow(dead_code)] // anticipating future use 34 | impl RcPtr { 35 | /// Create a new `RcPtr` from a value. 36 | pub(crate) fn new(value: T) -> Self { 37 | Self(Rc::new(value)) 38 | } 39 | 40 | /// Return the inner `Rc`. 41 | pub(crate) fn into_inner(self) -> Rc { 42 | self.0 43 | } 44 | } 45 | 46 | impl Hash for RcPtr { 47 | fn hash(&self, state: &mut H) { 48 | std::ptr::hash(Rc::as_ptr(&self.0), state) 49 | } 50 | } 51 | 52 | impl PartialEq for RcPtr { 53 | fn eq(&self, other: &Self) -> bool { 54 | Rc::ptr_eq(&self.0, &other.0) 55 | } 56 | } 57 | 58 | impl Eq for RcPtr {} 59 | 60 | impl From> for RcPtr { 61 | fn from(value: Rc) -> Self { 62 | Self(value) 63 | } 64 | } 65 | impl Deref for RcPtr { 66 | type Target = Rc; 67 | 68 | fn deref(&self) -> &Self::Target { 69 | &self.0 70 | } 71 | } 72 | impl AsRef> for RcPtr { 73 | fn as_ref(&self) -> &Rc { 74 | &self.0 75 | } 76 | } 77 | impl AsMut> for RcPtr { 78 | fn as_mut(&mut self) -> &mut Rc { 79 | &mut self.0 80 | } 81 | } 82 | 83 | /// A wrapper of `Weak` that implements `Hash` and `Eq` by comparing 84 | /// raw pointer addresses. 85 | pub struct WeakPtr(Weak); 86 | 87 | impl std::fmt::Debug for WeakPtr { 88 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 89 | f.debug_struct("WeakPtr") 90 | .field("ptr", &self.0.as_ptr()) 91 | .finish() 92 | } 93 | } 94 | 95 | impl Clone for WeakPtr { 96 | fn clone(&self) -> Self { 97 | Self(Weak::clone(&self.0)) 98 | } 99 | } 100 | 101 | impl WeakPtr { 102 | /// Create a `WeakPtr` by downgrading a `Rc`. 103 | pub fn from_rc(t: &Rc) -> Self { 104 | Self(Rc::downgrade(t)) 105 | } 106 | 107 | /// Create an empty `WeakPtr`. 108 | pub fn new() -> Self { 109 | Self(Weak::new()) 110 | } 111 | 112 | /// Attempt to upgrade the `WeakPtr` to an `Rc`. 113 | pub fn upgrade(&self) -> Option> { 114 | self.0.upgrade() 115 | } 116 | 117 | /// Return the inner `Weak`. 118 | pub fn into_inner(self) -> Weak { 119 | self.0 120 | } 121 | 122 | /// Get a reference to the inner `Weak`. 123 | pub const fn as_weak(&self) -> &Weak { 124 | &self.0 125 | } 126 | } 127 | 128 | impl PartialEq for WeakPtr { 129 | fn eq(&self, other: &Self) -> bool { 130 | self.0.ptr_eq(&other.0) 131 | } 132 | } 133 | 134 | impl Eq for WeakPtr {} 135 | 136 | impl Hash for WeakPtr { 137 | fn hash(&self, state: &mut H) { 138 | std::ptr::hash(self.0.as_ptr(), state) 139 | } 140 | } 141 | 142 | impl From> for WeakPtr { 143 | fn from(value: Weak) -> Self { 144 | Self(value) 145 | } 146 | } 147 | 148 | pub(crate) mod stream { 149 | use std::{ 150 | cell::RefCell, 151 | pin::Pin, 152 | rc::Rc, 153 | task::{Context, Poll, Waker}, 154 | }; 155 | 156 | use derive_where::derive_where; 157 | use futures_core::{FusedStream, Stream}; 158 | #[derive(Debug)] 159 | #[derive_where(Default)] 160 | struct ReplaceInner { 161 | stream: Option, 162 | waker: Option, 163 | } 164 | /// A stream whose inner is replaceable. 165 | /// 166 | /// The inner can be replaced using the corresponding [`Replace`] handle. 167 | /// The new stream will be polled the next time the [`Replaceable`] 168 | /// stream is polled. If the inner is `None`, then `Poll::Pending` is 169 | /// returned if the corresponding `Replace` handle hasn't been dropped, 170 | /// otherwise `Poll::Ready(None)` will be returned, since it is no 171 | /// longer possible to put anything that's not `None` into the inner. 172 | #[derive(Debug)] 173 | pub struct Replaceable { 174 | inner: Rc>>, 175 | } 176 | 177 | // TODO: wake up task when inner is replaced 178 | #[derive(Debug)] 179 | pub struct Replace { 180 | inner: Rc>>, 181 | } 182 | 183 | impl Stream for Replaceable { 184 | type Item = S::Item; 185 | 186 | fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 187 | let strong = Rc::strong_count(&self.inner); 188 | let mut inner = self.inner.borrow_mut(); 189 | inner.waker.take(); 190 | 191 | let p = inner 192 | .stream 193 | .as_mut() 194 | .map(|s| Pin::new(s).poll_next(cx)) 195 | .unwrap_or(Poll::Ready(None)); 196 | 197 | if matches!(p, Poll::Ready(Some(_)) | Poll::Pending) { 198 | // If p is Pending, normally the inner stream would be responsible for waking 199 | // the task up, but if the inner stream is replaced before the 200 | // task is woken up, we still need to notify the task. 201 | if p.is_pending() && strong > 1 { 202 | inner.waker = Some(cx.waker().clone()); 203 | } 204 | return p 205 | } 206 | 207 | // p == Poll::Ready(None), replace the stream with None 208 | inner.stream = None; 209 | if strong == 1 { 210 | // Replace handle has been dropped 211 | Poll::Ready(None) 212 | } else { 213 | inner.waker = Some(cx.waker().clone()); 214 | Poll::Pending 215 | } 216 | } 217 | } 218 | 219 | impl FusedStream for Replaceable { 220 | fn is_terminated(&self) -> bool { 221 | Rc::strong_count(&self.inner) == 1 && self.inner.borrow().stream.is_none() 222 | } 223 | } 224 | 225 | impl Replace { 226 | /// Replace the stream in the corresponding [`Replaceable`] with the 227 | /// given stream. Returns the old stream. 228 | /// 229 | /// Note the new stream will not be immediately polled, so if there is 230 | /// currently a task waiting on the corresponding 231 | /// [`Replaceable`], it will not be woken up. You must arrange for it 232 | /// to be polled. 233 | pub fn replace(&self, stream: Option) -> Option { 234 | if stream.is_some() { 235 | if let Some(waker) = self.inner.borrow_mut().waker.take() { 236 | // Wake up the task so the new stream will be polled. 237 | waker.wake(); 238 | } 239 | } 240 | std::mem::replace(&mut self.inner.borrow_mut().stream, stream) 241 | } 242 | } 243 | 244 | impl Drop for Replace { 245 | fn drop(&mut self) { 246 | if let Some(waker) = self.inner.borrow_mut().waker.take() { 247 | // If we drop the Replace, the corresponding Replaceable could return 248 | // Poll::Ready(None) if polled again, so we need to wake up the task to poll it 249 | // again. 250 | waker.wake(); 251 | } 252 | } 253 | } 254 | 255 | /// Create a pair of [`Replaceable`] and [`Replace`]. The `Replace` can be 256 | /// used to replace the stream in the `Replaceable`. 257 | pub fn replaceable() -> (Replaceable, Replace) { 258 | let inner = Rc::new(RefCell::new(ReplaceInner::default())); 259 | ( 260 | Replaceable { 261 | inner: inner.clone(), 262 | }, 263 | Replace { inner }, 264 | ) 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /runa-orbiter/src/shell/buffers.rs: -------------------------------------------------------------------------------- 1 | //! Various kind of buffers used in wayland protocols. 2 | 3 | use std::{cell::Cell, fmt::Debug, rc::Rc}; 4 | 5 | use derive_where::derive_where; 6 | use runa_core::events::{single_state, EventSource}; 7 | 8 | use crate::utils::geometry::{coords, Extent}; 9 | 10 | /// The base buffer trait. 11 | pub trait BufferLike: EventSource + Debug + 'static { 12 | // TODO: take rectangles 13 | /// Damage the buffer 14 | fn damage(&self); 15 | /// Clear buffer damage 16 | fn clear_damage(&self); 17 | /// Returns whether the buffer is damaged 18 | fn get_damage(&self) -> bool; 19 | /// The buffer dimensions 20 | fn dimension(&self) -> Extent; 21 | /// Return the object id for the buffer object. 22 | /// Used for sending release event to the client. 23 | fn object_id(&self) -> u32; 24 | 25 | /// Send the Released event for this buffer. After this is called, the 26 | /// buffer should prevent any further access of the client resources. 27 | /// Repeated calls to this function without intervening calls to 28 | /// [`BufferLike::acquire`] are allowed, but should have no effect. 29 | /// 30 | /// When a buffer is just created, it is in the released state. 31 | fn release(&self); 32 | 33 | /// Acquire the buffer so it can be read. 34 | fn acquire(&self); 35 | } 36 | 37 | #[derive(Debug)] 38 | pub(crate) struct AttachedBuffer { 39 | pub(crate) inner: Rc, 40 | attach_count: Rc>, 41 | } 42 | 43 | impl Clone for AttachedBuffer { 44 | fn clone(&self) -> Self { 45 | let count = self.attach_count.get(); 46 | self.attach_count.set(count + 1); 47 | Self { 48 | inner: self.inner.clone(), 49 | attach_count: self.attach_count.clone(), 50 | } 51 | } 52 | } 53 | 54 | impl PartialEq for AttachedBuffer { 55 | fn eq(&self, other: &Self) -> bool { 56 | let eq = Rc::ptr_eq(&self.inner, &other.inner); 57 | assert_eq!(eq, Rc::ptr_eq(&self.attach_count, &other.attach_count)); 58 | eq 59 | } 60 | } 61 | 62 | impl Eq for AttachedBuffer {} 63 | 64 | impl Drop for AttachedBuffer { 65 | fn drop(&mut self) { 66 | let count = self.attach_count.get(); 67 | self.attach_count.set(count - 1); 68 | if count == 1 { 69 | self.inner.release(); 70 | } 71 | } 72 | } 73 | 74 | /// A wrapper of buffer to automatically keep track of how many surface 75 | /// states a buffer is attached to, and automatically release the buffer when 76 | /// the count reaches 0. 77 | #[derive(Debug)] 78 | #[derive_where(Clone)] 79 | pub(crate) struct AttachableBuffer { 80 | pub(crate) inner: Rc, 81 | attach_count: Rc>, 82 | } 83 | 84 | impl From for AttachableBuffer { 85 | fn from(inner: B) -> Self { 86 | Self { 87 | inner: Rc::new(inner), 88 | attach_count: Rc::new(Cell::new(0)), 89 | } 90 | } 91 | } 92 | 93 | impl AttachableBuffer { 94 | pub fn new(inner: B) -> Self { 95 | Self::from(inner) 96 | } 97 | 98 | pub fn attach(&self) -> AttachedBuffer { 99 | let count = self.attach_count.get(); 100 | self.attach_count.set(count + 1); 101 | AttachedBuffer { 102 | inner: self.inner.clone(), 103 | attach_count: self.attach_count.clone(), 104 | } 105 | } 106 | } 107 | 108 | /// The server context has a buffer type 109 | /// 110 | /// This is used to allow the user to define their own buffer type. 111 | pub trait HasBuffer { 112 | /// The buffer type 113 | type Buffer: BufferLike; 114 | } 115 | 116 | /// Buffer base 117 | /// 118 | /// Various buffer implementations can choose to provide this struct as the 119 | /// implementation of the wl_buffer interface. 120 | /// 121 | /// All buffer implementations in this crate uses this. 122 | #[derive(Debug)] 123 | pub struct Buffer { 124 | damaged: Cell, 125 | object_id: u32, 126 | base: Base, 127 | dimension: Extent, 128 | is_released: Cell, 129 | event_source: single_state::Sender, 130 | } 131 | 132 | impl Buffer { 133 | /// Create a new buffer 134 | pub fn new(dimension: Extent, object_id: u32, inner: Base) -> Self { 135 | Self { 136 | damaged: Cell::new(false), 137 | object_id, 138 | base: inner, 139 | dimension, 140 | is_released: Cell::new(true), 141 | event_source: Default::default(), 142 | } 143 | } 144 | 145 | /// Get a reference to the base type 146 | pub fn base(&self) -> &Base { 147 | assert!(!self.is_released.get()); 148 | &self.base 149 | } 150 | 151 | /// Get a mutable reference to the base type 152 | pub fn base_mut(&mut self) -> &mut Base { 153 | assert!(!self.is_released.get()); 154 | &mut self.base 155 | } 156 | 157 | /// Map the base type of the buffer. Note that already created event sources 158 | /// attached to the origin buffer, are still be attached to the 159 | /// resultant buffer. 160 | pub fn map_base U>(self, f: F) -> Buffer { 161 | let Self { 162 | damaged, 163 | object_id, 164 | base, 165 | dimension, 166 | is_released, 167 | event_source, 168 | } = self; 169 | Buffer { 170 | damaged, 171 | object_id, 172 | base: f(base), 173 | dimension, 174 | is_released, 175 | event_source, 176 | } 177 | } 178 | } 179 | 180 | impl EventSource for Buffer { 181 | type Source = single_state::Receiver; 182 | 183 | fn subscribe(&self) -> Self::Source { 184 | self.event_source.new_receiver() 185 | } 186 | } 187 | 188 | impl BufferLike for Buffer { 189 | #[inline] 190 | fn damage(&self) { 191 | self.damaged.set(true); 192 | } 193 | 194 | #[inline] 195 | fn clear_damage(&self) { 196 | self.damaged.set(false); 197 | } 198 | 199 | #[inline] 200 | fn get_damage(&self) -> bool { 201 | self.damaged.get() 202 | } 203 | 204 | #[inline] 205 | fn dimension(&self) -> Extent { 206 | self.dimension 207 | } 208 | 209 | #[inline] 210 | fn object_id(&self) -> u32 { 211 | self.object_id 212 | } 213 | 214 | #[inline] 215 | fn release(&self) { 216 | if !self.is_released.get() { 217 | tracing::trace!("Releasing buffer {}", self.object_id); 218 | self.is_released.set(true); 219 | self.event_source.send(BufferEvent::Released); 220 | } 221 | } 222 | 223 | #[inline] 224 | fn acquire(&self) { 225 | tracing::trace!("Acquiring buffer {}", self.object_id); 226 | self.is_released.set(false); 227 | } 228 | } 229 | 230 | /// An empty private trait just to make enum_dispatch generate the `From` impls. 231 | #[enum_dispatch::enum_dispatch] 232 | #[allow(dead_code)] 233 | trait BufferBaseFrom {} 234 | 235 | /// An enum of all buffer base types defined in this crate. 236 | #[derive(Debug)] 237 | #[enum_dispatch::enum_dispatch(BufferBaseFrom)] 238 | pub enum BufferBase { 239 | /// A shm buffer 240 | Shm(crate::objects::shm::Buffer), 241 | } 242 | 243 | /// Events emitted by a buffer 244 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 245 | pub enum BufferEvent { 246 | /// The buffer is no longer used by the compositor. See `wl_buffer.release` 247 | /// for more information about what this means. 248 | Released, 249 | } 250 | 251 | /// A buffer with additional user data. 252 | #[derive(Debug)] 253 | pub struct UserBuffer { 254 | buffer: B, 255 | /// The user data 256 | pub data: Data, 257 | } 258 | 259 | impl UserBuffer { 260 | /// Get a reference to the buffer 261 | pub fn buffer(&self) -> &B { 262 | &self.buffer 263 | } 264 | 265 | /// Apply a function to the buffer component of a `UserBuffer` 266 | pub fn map_buffer U>(self, f: F) -> UserBuffer { 267 | UserBuffer { 268 | buffer: f(self.buffer), 269 | data: self.data, 270 | } 271 | } 272 | } 273 | 274 | impl UserBuffer { 275 | /// Create a new `UserBuffer` 276 | pub fn new(buffer: B, data: Data) -> Self { 277 | Self { buffer, data } 278 | } 279 | 280 | /// Create a new `UserBuffer` with default data 281 | pub fn with_default_data(buffer: B) -> Self 282 | where 283 | Data: Default, 284 | { 285 | Self::new(buffer, Default::default()) 286 | } 287 | } 288 | 289 | impl From> for UserBuffer, Data> 290 | where 291 | U: From, 292 | { 293 | fn from(value: Buffer) -> Self { 294 | Self::with_default_data(value.map_base(U::from)) 295 | } 296 | } 297 | 298 | impl EventSource for UserBuffer { 299 | type Source = >::Source; 300 | 301 | fn subscribe(&self) -> Self::Source { 302 | self.buffer.subscribe() 303 | } 304 | } 305 | 306 | impl BufferLike for UserBuffer { 307 | #[inline] 308 | fn damage(&self) { 309 | self.buffer.damage() 310 | } 311 | 312 | #[inline] 313 | fn clear_damage(&self) { 314 | self.buffer.clear_damage() 315 | } 316 | 317 | #[inline] 318 | fn get_damage(&self) -> bool { 319 | self.buffer.get_damage() 320 | } 321 | 322 | #[inline] 323 | fn dimension(&self) -> Extent { 324 | self.buffer.dimension() 325 | } 326 | 327 | #[inline] 328 | fn object_id(&self) -> u32 { 329 | self.buffer.object_id() 330 | } 331 | 332 | #[inline] 333 | fn release(&self) { 334 | self.buffer.release() 335 | } 336 | 337 | #[inline] 338 | fn acquire(&self) { 339 | self.buffer.acquire() 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /runa-core/src/globals.rs: -------------------------------------------------------------------------------- 1 | //! Traits and type related to wayland globals 2 | 3 | use std::{future::Future, pin::Pin}; 4 | 5 | use runa_io::traits::WriteMessage; 6 | use runa_wayland_protocols::wayland::{wl_display, wl_registry::v1 as wl_registry}; 7 | 8 | use crate::{ 9 | client::traits::{ 10 | Client, ClientParts, EventDispatcher, EventHandler, EventHandlerAction, StoreUpdate, 11 | }, 12 | events::EventSource, 13 | objects::DISPLAY_ID, 14 | server::traits::{GlobalStore, GlobalsUpdate, Server}, 15 | }; 16 | 17 | /// A monomorphic global 18 | /// 19 | /// i.e. a global with a concrete type. This is analogous to the 20 | /// [`MonoObject`](crate::objects::MonoObject) trait for objects. 21 | pub trait MonoGlobal: Sized { 22 | /// Type of the proxy object for this global. 23 | type Object; 24 | 25 | /// The interface name of this global. 26 | const INTERFACE: &'static str; 27 | /// The version number of this global. 28 | const VERSION: u32; 29 | /// A value that can be used to create an instance of `Self`. Or None if 30 | /// there is no compile time default value. 31 | const MAYBE_DEFAULT: Option; 32 | /// Create a new proxy object of this global. 33 | fn new_object() -> Self::Object; 34 | } 35 | 36 | /// Bind a global to a client 37 | /// 38 | /// TODO: authorization support, i.e. some globals should only be accessible to 39 | /// authorized clients. 40 | pub trait Bind { 41 | /// Type of future returned by [`bind`](Self::bind). 42 | type BindFut<'a>: Future> + 'a 43 | where 44 | Ctx: 'a, 45 | Self: 'a; 46 | 47 | /// Setup a proxy of this global, the proxy object has already been inserted 48 | /// into the client's object store, with the given object id. 49 | /// 50 | /// If Err() is returned, an protocol error will be sent to the client, and 51 | /// the client will be disconnected. 52 | fn bind<'a>(&'a self, client: &'a mut Ctx, object_id: u32) -> Self::BindFut<'a>; 53 | } 54 | 55 | /// A polymorphic global 56 | /// 57 | /// This is analogous to the [`AnyObject`](crate::objects::AnyObject) trait for 58 | /// objects, as well as the [`Any`](std::any::Any) trait. 59 | /// 60 | /// Typically a polymorphic global type will be a enum of all the global types 61 | /// used in the compositor. If you have such an enum, the 62 | /// [`globals!`](crate::globals!) macro will generate this trait impl for you. 63 | /// 64 | /// Unlike `AnyObject`, this trait doesn't have a `cast_mut` method because 65 | /// globals are shared so it's not possible to get a unique reference to them. 66 | pub trait AnyGlobal { 67 | /// Type of the proxy object for this global. Since this global is 68 | /// polymorphic, the type of the proxy object is also polymorphic. This 69 | /// should match the object type you have in you 70 | /// [`Client`](crate::client::traits::Client) trait implementation. 71 | type Object: crate::objects::AnyObject; 72 | 73 | /// Create a proxy of this global, called when the client tries to bound 74 | /// the global. The new proxy object will be incomplete until [`Bind::bind`] 75 | /// is called with that object. 76 | fn new_object(&self) -> Self::Object; 77 | 78 | /// The interface name of this global. 79 | fn interface(&self) -> &'static str; 80 | 81 | /// The version number of this global. 82 | fn version(&self) -> u32; 83 | 84 | /// Cast this polymorphic global to a more concrete type. 85 | fn cast(&self) -> Option<&T> 86 | where 87 | Self: 'static + Sized; 88 | } 89 | 90 | /// Implementation of the `wl_display` global 91 | #[derive(Debug, Clone, Copy, Default)] 92 | pub struct Display; 93 | 94 | impl MonoGlobal for Display { 95 | type Object = crate::objects::Display; 96 | 97 | const INTERFACE: &'static str = wl_display::v1::NAME; 98 | const MAYBE_DEFAULT: Option = Some(Self); 99 | const VERSION: u32 = wl_display::v1::VERSION; 100 | 101 | #[inline] 102 | fn new_object() -> Self::Object { 103 | crate::objects::Display { initialized: false } 104 | } 105 | } 106 | 107 | impl Bind for Display { 108 | type BindFut<'a > = impl Future> + 'a where Self: 'a, Ctx: 'a; 109 | 110 | fn bind<'a>(&'a self, client: &'a mut Ctx, object_id: u32) -> Self::BindFut<'a> { 111 | use crate::client::traits::Store; 112 | let ClientParts { 113 | objects, 114 | event_dispatcher, 115 | .. 116 | } = client.as_mut_parts(); 117 | objects 118 | .get_mut::<::Object>(object_id) 119 | .unwrap() 120 | .initialized = true; 121 | 122 | struct DisplayEventHandler; 123 | impl EventHandler for DisplayEventHandler { 124 | type Message = StoreUpdate; 125 | 126 | type Future<'ctx> = impl Future< 127 | Output = Result< 128 | EventHandlerAction, 129 | Box, 130 | >, 131 | > + 'ctx; 132 | 133 | fn handle_event<'ctx>( 134 | &'ctx mut self, 135 | _objects: &'ctx mut ::ObjectStore, 136 | connection: &'ctx mut ::Connection, 137 | _server_context: &'ctx ::ServerContext, 138 | message: &'ctx mut Self::Message, 139 | ) -> Self::Future<'ctx> { 140 | async move { 141 | use crate::client::traits::StoreUpdateKind::*; 142 | if matches!(message.kind, Removed { .. } | Replaced { .. }) { 143 | // Replaced can really only happen for server created objects, because 144 | // we haven't sent the DeleteId event yet, so the client can't have 145 | // reused that ID. 146 | // For server create objects, it possible that the client destroyed 147 | // that object and we immediately created another one with that ID, 148 | // this should be the only case for us to get a Replaced event. 149 | connection 150 | .send(DISPLAY_ID, wl_display::v1::events::DeleteId { 151 | id: message.object_id, 152 | }) 153 | .await?; 154 | } 155 | Ok(EventHandlerAction::Keep) 156 | } 157 | } 158 | } 159 | event_dispatcher.add_event_handler(objects.subscribe(), DisplayEventHandler); 160 | futures_util::future::ok(()) 161 | } 162 | } 163 | 164 | /// The registry singleton. This is an interface only object. The actual list of 165 | /// globals is stored in the `Globals` implementation of the server context. 166 | /// 167 | /// If you use this as your registry global implementation, you must also use 168 | /// [`crate::objects::Registry`] as your client side registry proxy 169 | /// implementation. 170 | #[derive(Debug, Clone, Copy, Default)] 171 | pub struct Registry; 172 | 173 | impl MonoGlobal for Registry { 174 | type Object = crate::objects::Registry; 175 | 176 | const INTERFACE: &'static str = wl_registry::NAME; 177 | const MAYBE_DEFAULT: Option = Some(Self); 178 | const VERSION: u32 = wl_registry::VERSION; 179 | 180 | #[inline] 181 | fn new_object() -> Self::Object { 182 | crate::objects::Registry(None) 183 | } 184 | } 185 | 186 | impl Bind for Registry { 187 | type BindFut<'a> = impl Future> + 'a where Self: 'a, Ctx: 'a; 188 | 189 | fn bind<'a>(&'a self, client: &'a mut Ctx, object_id: u32) -> Self::BindFut<'a> { 190 | async move { 191 | let ClientParts { 192 | server_context, 193 | connection, 194 | event_dispatcher, 195 | .. 196 | } = client.as_mut_parts(); 197 | let rx = server_context.globals().borrow().subscribe(); 198 | // Send existing globals 199 | let globals: Vec<_> = server_context 200 | .globals() 201 | .borrow() 202 | .iter() 203 | .map(|(id, global)| (id, global.clone())) 204 | .collect(); 205 | 206 | for (id, global) in globals { 207 | let interface = global.interface(); 208 | let version = global.version(); 209 | connection 210 | .send(object_id, wl_registry::events::Global { 211 | name: id, 212 | interface: interface.as_bytes().into(), 213 | version, 214 | }) 215 | .await 216 | .unwrap() 217 | } 218 | connection.flush().await.unwrap(); 219 | event_dispatcher.add_event_handler(rx, RegistryEventHandler { 220 | registry_id: object_id, 221 | }); 222 | Ok(()) 223 | } 224 | } 225 | } 226 | 227 | struct RegistryEventHandler { 228 | registry_id: u32, 229 | } 230 | 231 | impl EventHandler for RegistryEventHandler { 232 | type Message = GlobalsUpdate<::Global>; 233 | 234 | type Future<'ctx> = impl Future< 235 | Output = Result< 236 | EventHandlerAction, 237 | Box, 238 | >, 239 | > + 'ctx; 240 | 241 | fn handle_event<'ctx>( 242 | &'ctx mut self, 243 | _objects: &'ctx mut Ctx::ObjectStore, 244 | connection: &'ctx mut Ctx::Connection, 245 | _server_context: &'ctx Ctx::ServerContext, 246 | message: &'ctx mut Self::Message, 247 | ) -> Self::Future<'ctx> { 248 | async move { 249 | let mut connection = Pin::new(connection); 250 | match message { 251 | GlobalsUpdate::Removed(name) => { 252 | connection 253 | .send(self.registry_id, wl_registry::events::GlobalRemove { 254 | name: *name, 255 | }) 256 | .await?; 257 | }, 258 | GlobalsUpdate::Added(name, global) => { 259 | let interface = global.interface(); 260 | let version = global.version(); 261 | connection 262 | .send(self.registry_id, wl_registry::events::Global { 263 | name: *name, 264 | interface: interface.as_bytes().into(), 265 | version, 266 | }) 267 | .await?; 268 | }, 269 | } 270 | 271 | // Client can't drop the registry object, so we will never stop this listener 272 | Ok(EventHandlerAction::Keep) 273 | } 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /runa-orbiter/src/shell/output.rs: -------------------------------------------------------------------------------- 1 | //! Type for representing output screens 2 | 3 | use std::{ 4 | cell::Cell, 5 | rc::{Rc, Weak}, 6 | }; 7 | 8 | use runa_core::events::{self, EventSource}; 9 | use runa_io::traits::WriteMessage; 10 | 11 | use crate::utils::geometry::{coords, Extent, Point, Rectangle, Scale, Transform}; 12 | 13 | /// Output 14 | /// 15 | /// An output is a screen, where the rendered result in displayed. 16 | #[derive(Debug)] 17 | pub struct Output { 18 | /// Resolution of the output, in pixels 19 | size: Cell>, 20 | /// Position of the output on the unified screen. 21 | position: Cell>, 22 | /// Scale independent position of the output, used to calculate the 23 | /// of outputs and surfaces. 24 | logical_position: Cell>, 25 | /// Physical size in millimeters 26 | physical_size: Extent, 27 | make: String, 28 | model: String, 29 | name: String, 30 | transform: Cell, 31 | /// The scaling factor for this output, this is the numerator of a fraction 32 | /// with a denominator of 120. 33 | /// See the fractional_scale_v1::preferred_scale for why 120. 34 | scale: Cell>, 35 | global_id: u32, 36 | change_event: events::broadcast::Broadcast, 37 | } 38 | 39 | bitflags::bitflags! { 40 | /// Changes that can happen to an output 41 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 42 | #[repr(transparent)] 43 | pub struct OutputChange: u32 { 44 | /// The output's geometry has changed 45 | const GEOMETRY = 1; 46 | /// The output's name has changed 47 | const NAME = 2; 48 | /// The output's scale has changed 49 | const SCALE = 4; 50 | } 51 | } 52 | 53 | impl Default for OutputChange { 54 | fn default() -> Self { 55 | Self::empty() 56 | } 57 | } 58 | 59 | impl std::hash::Hash for Output { 60 | fn hash(&self, state: &mut H) { 61 | self.global_id.hash(state); 62 | } 63 | } 64 | 65 | /// Event emitted when an output changes 66 | #[derive(Clone, Debug)] 67 | pub struct OutputChangeEvent { 68 | pub(crate) output: Weak, 69 | pub(crate) change: OutputChange, 70 | } 71 | 72 | impl Output { 73 | /// Checks whether an output overlaps a given rectangle. 74 | /// 75 | /// # Note 76 | /// 77 | /// This check uses a special coordinate system, 78 | /// [`coords::ScreenNormalized`](crate::utils::geometry::coords::ScreenNormalized), 79 | /// see the documentation there for the justification. If this is not what 80 | /// is the most suitable for your compositor, you are free implement 81 | /// other overlap checks. This is not used internally by this crate. 82 | pub fn overlaps(&self, other: &Rectangle) -> bool { 83 | self.logical_geometry().overlaps(other) 84 | } 85 | 86 | /// Notify that the output has changed 87 | pub async fn notify_change(self: &Rc, change: OutputChange) { 88 | self.change_event 89 | .broadcast(OutputChangeEvent { 90 | output: Rc::downgrade(self), 91 | change, 92 | }) 93 | .await; 94 | } 95 | 96 | /// Create a new output 97 | pub fn new( 98 | physical_size: Extent, 99 | make: &str, 100 | model: &str, 101 | name: &str, 102 | global_id: u32, 103 | ) -> Self { 104 | Self { 105 | size: Default::default(), 106 | position: Default::default(), 107 | logical_position: Default::default(), 108 | physical_size, 109 | make: make.to_owned(), 110 | model: model.to_owned(), 111 | name: name.to_owned(), 112 | transform: Default::default(), 113 | scale: Scale::new(1, 1).into(), 114 | global_id, 115 | change_event: Default::default(), 116 | } 117 | } 118 | 119 | /// The global ID in the compositor's global store. 120 | pub fn global_id(&self) -> u32 { 121 | self.global_id 122 | } 123 | 124 | /// Scale of the output 125 | pub fn scale(&self) -> Scale { 126 | self.scale.get() 127 | } 128 | 129 | /// Scale of the output as a f32 130 | pub fn scale_f32(&self) -> Scale { 131 | let scale = self.scale.get().to::(); 132 | Scale::new(scale.x / 120., scale.y / 120.) 133 | } 134 | 135 | /// Set the scale of the output 136 | pub fn set_scale(&self, scale: u32) { 137 | self.scale.set(Scale::new(scale, scale)); 138 | } 139 | 140 | /// Name of the output 141 | pub fn name(&self) -> &str { 142 | &self.name 143 | } 144 | 145 | /// Scale invariant logical geometry of the output. This is used to 146 | /// determine where a window is placed, so that it is not dependent on 147 | /// the scale of the window. To avoid a dependency circle: window scale 148 | /// -> window placement -> window scale. 149 | /// 150 | /// Currently this is calculated as the size of the output divided by its 151 | /// scale. 152 | pub fn logical_geometry(&self) -> Rectangle { 153 | use crate::utils::geometry::coords::Map; 154 | let transform = self.transform.get(); 155 | let scale = self.scale_f32(); 156 | let logical_size = self 157 | .size 158 | .get() 159 | .map(|x| transform.transform_size(x.to() / scale).floor().to()); 160 | let logical_position = self.logical_position.get(); 161 | Rectangle::from_loc_and_size(logical_position, logical_size.to()) 162 | } 163 | 164 | /// Geometry of the output in screen space. 165 | pub fn geometry(&self) -> Rectangle { 166 | use crate::utils::geometry::coords::Map; 167 | let transform = self.transform.get(); 168 | let size = self.size.get().map(|x| transform.transform_size(x)); 169 | let position = self.position.get(); 170 | Rectangle::from_loc_and_size(position, size.to()) 171 | } 172 | 173 | /// Size of the output in screen space. 174 | pub fn size(&self) -> Extent { 175 | self.size.get() 176 | } 177 | 178 | /// Position of the output in screen space. 179 | pub fn position(&self) -> Point { 180 | self.position.get() 181 | } 182 | 183 | /// Scale invariant logical position of the output. 184 | /// 185 | /// See [`logical_geometry`](Self::logical_geometry). 186 | pub fn logical_position(&self) -> Point { 187 | self.logical_position.get() 188 | } 189 | 190 | /// Set the screen space size of the output 191 | pub fn set_size(&self, geometry: Extent) { 192 | self.size.set(geometry); 193 | } 194 | 195 | /// Set the screen space position of the output 196 | pub fn set_position(&self, position: Point) { 197 | self.position.set(position); 198 | } 199 | 200 | /// Set the scale invariant logical position of the output. 201 | /// 202 | /// See [`logical_geometry`](Self::logical_geometry). 203 | pub fn set_logical_position(&self, logical_position: Point) { 204 | self.logical_position.set(logical_position); 205 | } 206 | 207 | /// Model name of the output 208 | pub fn model(&self) -> &str { 209 | &self.model 210 | } 211 | 212 | /// Make of the output 213 | pub fn make(&self) -> &str { 214 | &self.make 215 | } 216 | 217 | /// Output transformation 218 | pub fn transform(&self) -> Transform { 219 | self.transform.get() 220 | } 221 | 222 | /// Set the output transformation 223 | pub fn set_transform(&self, transform: Transform) { 224 | self.transform.set(transform); 225 | } 226 | 227 | /// Physical size in millimeters 228 | pub fn physical_size(&self) -> Extent { 229 | self.physical_size 230 | } 231 | 232 | /// Send a wl_output.geometry event to the client 233 | pub(crate) async fn send_geometry( 234 | &self, 235 | client: &mut T, 236 | object_id: u32, 237 | ) -> std::io::Result<()> { 238 | use runa_wayland_protocols::wayland::wl_output::v4 as wl_output; 239 | let geometry = self.geometry(); 240 | let physical_size = self.physical_size(); 241 | client 242 | .send( 243 | object_id, 244 | wl_output::Event::Geometry(wl_output::events::Geometry { 245 | x: geometry.loc.x, 246 | y: geometry.loc.y, 247 | physical_width: physical_size.w as i32, 248 | physical_height: physical_size.h as i32, 249 | subpixel: wl_output::enums::Subpixel::Unknown, // TODO 250 | make: self.make().as_bytes().into(), 251 | model: self.model().as_bytes().into(), 252 | transform: wl_output::enums::Transform::Normal, // TODO 253 | }), 254 | ) 255 | .await 256 | } 257 | 258 | /// Send a wl_output.name event to the client 259 | pub(crate) async fn send_name( 260 | &self, 261 | client: &mut T, 262 | object_id: u32, 263 | ) -> std::io::Result<()> { 264 | use runa_wayland_protocols::wayland::wl_output::v4 as wl_output; 265 | client 266 | .send( 267 | object_id, 268 | wl_output::Event::Name(wl_output::events::Name { 269 | name: self.name().as_bytes().into(), 270 | }), 271 | ) 272 | .await 273 | } 274 | 275 | pub(crate) async fn send_scale( 276 | &self, 277 | client: &mut T, 278 | object_id: u32, 279 | ) -> std::io::Result<()> { 280 | use runa_wayland_protocols::wayland::wl_output::v4 as wl_output; 281 | client 282 | .send(object_id, wl_output::events::Scale { 283 | factor: (self.scale.get().x / 120) as i32, 284 | }) 285 | .await 286 | } 287 | 288 | /// Send a wl_output.done event to the client 289 | pub(crate) async fn send_done( 290 | client: &mut T, 291 | object_id: u32, 292 | ) -> std::io::Result<()> { 293 | use runa_wayland_protocols::wayland::wl_output::v4 as wl_output; 294 | client 295 | .send( 296 | object_id, 297 | wl_output::Event::Done(wl_output::events::Done {}), 298 | ) 299 | .await 300 | } 301 | 302 | /// Send all information about this output to the client. 303 | pub(crate) async fn send_all( 304 | &self, 305 | client: &mut T, 306 | object_id: u32, 307 | ) -> std::io::Result<()> { 308 | self.send_geometry(client, object_id).await?; 309 | self.send_name(client, object_id).await?; 310 | self.send_scale(client, object_id).await?; 311 | Self::send_done(client, object_id).await 312 | } 313 | } 314 | 315 | impl EventSource for Output { 316 | type Source = 317 | as EventSource>::Source; 318 | 319 | fn subscribe(&self) -> Self::Source { 320 | self.change_event.subscribe() 321 | } 322 | } 323 | 324 | /// A collection of outputs 325 | #[derive(Debug, Default)] 326 | pub struct Screen { 327 | #[allow(missing_docs)] 328 | pub outputs: Vec>, 329 | } 330 | 331 | impl Screen { 332 | /// Find all outputs that overlaps with `geometry` 333 | pub fn find_outputs<'a>( 334 | &'a self, 335 | geometry: &'a Rectangle, 336 | ) -> impl Iterator { 337 | self.outputs 338 | .iter() 339 | .map(|o| o.as_ref()) 340 | .filter(|output| output.overlaps(geometry)) 341 | } 342 | 343 | /// Create a new screen with a single output 344 | pub fn from_single_output(output: &Rc) -> Self { 345 | Self { 346 | outputs: vec![output.clone()], 347 | } 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /runa-core/src/provide_any.rs: -------------------------------------------------------------------------------- 1 | //! The provide_any API "borrowed" from the Rust libstd. 2 | //! 3 | //! Remove after the API is stabilized. (see: ) 4 | #![allow(missing_docs, missing_debug_implementations)] 5 | 6 | use std::any::TypeId; 7 | 8 | /////////////////////////////////////////////////////////////////////////////// 9 | // Type tags 10 | /////////////////////////////////////////////////////////////////////////////// 11 | 12 | mod tags { 13 | //! Type tags are used to identify a type using a separate value. This 14 | //! module includes type tags for some very common types. 15 | //! 16 | //! Currently type tags are not exposed to the user. But in the future, if 17 | //! you want to use the Provider API with more complex types (typically 18 | //! those including lifetime parameters), you will need to write your 19 | //! own tags. 20 | 21 | use std::marker::PhantomData; 22 | 23 | /// This trait is implemented by specific tag types in order to allow 24 | /// describing a type which can be requested for a given lifetime `'a`. 25 | /// 26 | /// A few example implementations for type-driven tags can be found in this 27 | /// module, although crates may also implement their own tags for more 28 | /// complex types with internal lifetimes. 29 | pub trait Type<'a>: Sized + 'static { 30 | /// The type of values which may be tagged by this tag for the given 31 | /// lifetime. 32 | type Reified: 'a; 33 | } 34 | 35 | /// Similar to the [`Type`] trait, but represents a type which may be 36 | /// unsized (i.e., has a `?Sized` bound). E.g., `str`. 37 | pub trait MaybeSizedType<'a>: Sized + 'static { 38 | type Reified: 'a + ?Sized; 39 | } 40 | 41 | impl<'a, T: Type<'a>> MaybeSizedType<'a> for T { 42 | type Reified = T::Reified; 43 | } 44 | 45 | /// Type-based tag for types bounded by `'static`, i.e., with no borrowed 46 | /// elements. 47 | #[derive(Debug)] 48 | pub struct Value(PhantomData); 49 | 50 | impl Type<'_> for Value { 51 | type Reified = T; 52 | } 53 | 54 | /// Type-based tag similar to [`Value`] but which may be unsized (i.e., has 55 | /// a `?Sized` bound). 56 | #[derive(Debug)] 57 | pub struct MaybeSizedValue(PhantomData); 58 | 59 | impl MaybeSizedType<'_> for MaybeSizedValue { 60 | type Reified = T; 61 | } 62 | 63 | /// Type-based tag for reference types (`&'a T`, where T is represented by 64 | /// `>::Reified`. 65 | #[derive(Debug)] 66 | pub struct Ref(PhantomData); 67 | 68 | impl<'a, I: MaybeSizedType<'a>> Type<'a> for Ref { 69 | type Reified = &'a I::Reified; 70 | } 71 | 72 | #[derive(Debug)] 73 | pub struct RefMut(PhantomData); 74 | 75 | impl<'a, I: MaybeSizedType<'a>> Type<'a> for RefMut { 76 | type Reified = &'a mut I::Reified; 77 | } 78 | } 79 | /// An object of arbitrary type. This whole API is stolen from Rust's 80 | /// std::any::Provider. We should remove this when that stabilizes. 81 | pub trait Provider { 82 | fn provide<'a>(&'a self, demand: &mut Demand<'a>); 83 | fn provide_mut<'a>(&'a mut self, _demand: &mut Demand<'a>) {} 84 | } 85 | 86 | /// Like [`Provider`], but with a "slot" to hint on what's being requested. 87 | /// Normally with `Provider`, the provider needs to provide everything it can 88 | /// provide, leading to `O(n)` time complexity. This trait instead provide a 89 | /// "slot" to hint on what's being requested, allowing providers to provide only 90 | /// what's needed, thus speed things up. 91 | pub trait SlottedProvider { 92 | fn provide<'a>(&'a self, slot: usize, demand: &mut Demand<'a>); 93 | fn provide_mut<'a>(&'a mut self, _slot: usize, _demand: &mut Demand<'a>) {} 94 | } 95 | 96 | pub struct ProviderArray { 97 | pub providers: [Option>; N], 98 | } 99 | 100 | impl ProviderArray { 101 | pub fn set(&mut self, slot: usize, provider: T) { 102 | if slot < N { 103 | self.providers[slot] = Some(Box::new(provider)); 104 | } else { 105 | panic!("slot out of range"); 106 | } 107 | } 108 | } 109 | 110 | impl Default for ProviderArray { 111 | fn default() -> Self { 112 | // Needed to work around the required `Copy` bound. 113 | const NONE: Option> = None; 114 | Self { 115 | providers: [NONE; N], 116 | } 117 | } 118 | } 119 | 120 | impl SlottedProvider for ProviderArray { 121 | fn provide<'a>(&'a self, slot: usize, demand: &mut Demand<'a>) { 122 | if slot < N { 123 | if let Some(provider) = &self.providers[slot] { 124 | provider.provide(demand); 125 | } 126 | } 127 | } 128 | 129 | fn provide_mut<'a>(&'a mut self, slot: usize, demand: &mut Demand<'a>) { 130 | if slot < N { 131 | if let Some(provider) = &mut self.providers[slot] { 132 | provider.provide_mut(demand); 133 | } 134 | } 135 | } 136 | } 137 | 138 | #[repr(transparent)] 139 | pub struct Demand<'a>(dyn Erased<'a> + 'a); 140 | 141 | pub struct Receiver<'a, 'b, I: tags::Type<'a>>(&'b mut TaggedOption<'a, I>); 142 | 143 | impl<'a, I: tags::Type<'a>> Receiver<'a, '_, I> { 144 | #[inline] 145 | pub fn provide(&mut self, value: I::Reified) { 146 | self.0 .0 = Some(value) 147 | } 148 | } 149 | 150 | impl<'a> Demand<'a> { 151 | /// Create a new `&mut Demand` from a `&mut dyn Erased` trait object. 152 | fn new<'b>(erased: &'b mut (dyn Erased<'a> + 'a)) -> &'b mut Demand<'a> { 153 | // SAFETY: transmuting `&mut (dyn Erased<'a> + 'a)` to `&mut Demand<'a>` is safe 154 | // since `Demand` is repr(transparent). 155 | unsafe { &mut *(erased as *mut dyn Erased<'a> as *mut Demand<'a>) } 156 | } 157 | 158 | /// Provide a value or other type with only static lifetimes. 159 | /// 160 | /// # Examples 161 | /// 162 | /// Provides a `String` by cloning. 163 | /// 164 | /// ```rust 165 | /// use runa_core::provide_any::{Demand, Provider}; 166 | /// # struct SomeConcreteType { field: String } 167 | /// 168 | /// impl Provider for SomeConcreteType { 169 | /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) { 170 | /// demand.provide_value::(|| self.field.clone()); 171 | /// } 172 | /// } 173 | /// ``` 174 | pub fn provide_value(&mut self, fulfil: F) -> &mut Self 175 | where 176 | T: 'static, 177 | F: FnOnce() -> T, 178 | { 179 | self.provide_with::, F>(fulfil) 180 | } 181 | 182 | /// Provide a reference, note that the referee type must be bounded by 183 | /// `'static`, but may be unsized. 184 | /// 185 | /// # Examples 186 | /// 187 | /// Provides a reference to a field as a `&str`. 188 | /// 189 | /// ```rust 190 | /// use runa_core::provide_any::{Demand, Provider}; 191 | /// # struct SomeConcreteType { field: String } 192 | /// 193 | /// impl Provider for SomeConcreteType { 194 | /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) { 195 | /// demand.provide_ref::(&self.field); 196 | /// } 197 | /// } 198 | /// ``` 199 | pub fn provide_ref(&mut self, value: &'a T) -> &mut Self { 200 | self.provide::>>(value) 201 | } 202 | 203 | pub fn provide_mut(&mut self, value: &'a mut T) -> &mut Self { 204 | self.provide::>>(value) 205 | } 206 | 207 | /// Provide a mutable references. But first check if `T` will be accepted. 208 | /// 209 | /// This is because `provide_mut` takes a `&'a mut T`, which means once you 210 | /// called that, you won't be able to provide anything else. Because 211 | /// it's not possible to have multiple mutable references. 212 | /// 213 | /// This method breaks up the process into two steps, first you check if `T` 214 | /// will be accepted, and you only pass the `&'a mut T` only if it will 215 | /// be accepted. 216 | pub fn maybe_provide_mut( 217 | &mut self, 218 | ) -> Option>>> { 219 | self.0 220 | .downcast_mut::>>() 221 | .map(Receiver) 222 | } 223 | 224 | /// Provide a value with the given `Type` tag. 225 | fn provide(&mut self, value: I::Reified) -> &mut Self 226 | where 227 | I: tags::Type<'a>, 228 | { 229 | if let Some(res @ TaggedOption(None)) = self.0.downcast_mut::() { 230 | res.0 = Some(value); 231 | } 232 | self 233 | } 234 | 235 | /// Provide a value with the given `Type` tag, using a closure to prevent 236 | /// unnecessary work. 237 | fn provide_with(&mut self, fulfil: F) -> &mut Self 238 | where 239 | I: tags::Type<'a>, 240 | F: FnOnce() -> I::Reified, 241 | { 242 | if let Some(res @ TaggedOption(None)) = self.0.downcast_mut::() { 243 | res.0 = Some(fulfil()); 244 | } 245 | self 246 | } 247 | } 248 | 249 | impl std::fmt::Debug for Demand<'_> { 250 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 251 | f.debug_struct("Demand").finish_non_exhaustive() 252 | } 253 | } 254 | 255 | /// Represents a type-erased but identifiable object. 256 | /// 257 | /// This trait is exclusively implemented by the `TaggedOption` type. 258 | /// 259 | /// # Safety 260 | /// 261 | /// If this trait is not implemented correctly, downcast_mut will cast to the 262 | /// wrong type and cause undefined behavior. 263 | unsafe trait Erased<'a>: 'a { 264 | /// The `TypeId` of the erased type. 265 | fn tag_id(&self) -> TypeId; 266 | } 267 | 268 | unsafe impl<'a, I: tags::Type<'a>> Erased<'a> for TaggedOption<'a, I> { 269 | fn tag_id(&self) -> TypeId { 270 | TypeId::of::() 271 | } 272 | } 273 | 274 | impl<'a> dyn Erased<'a> + 'a { 275 | /// Returns some reference to the dynamic value if it is tagged with `I`, 276 | /// or `None` otherwise. 277 | #[inline] 278 | fn downcast_mut(&mut self) -> Option<&mut TaggedOption<'a, I>> 279 | where 280 | I: tags::Type<'a>, 281 | { 282 | if self.tag_id() == TypeId::of::() { 283 | // SAFETY: Just checked whether we're pointing to an I. 284 | Some(unsafe { &mut *(self as *mut Self).cast::>() }) 285 | } else { 286 | None 287 | } 288 | } 289 | } 290 | 291 | #[repr(transparent)] 292 | struct TaggedOption<'a, I: tags::Type<'a>>(Option); 293 | 294 | impl<'a, I: tags::Type<'a>> TaggedOption<'a, I> { 295 | fn as_demand(&mut self) -> &mut Demand<'a> { 296 | Demand::new(self as &mut (dyn Erased<'a> + 'a)) 297 | } 298 | } 299 | 300 | pub fn request_ref<'a, T, P>(provider: &'a P) -> Option<&'a T> 301 | where 302 | T: 'static + ?Sized, 303 | P: Provider + ?Sized, 304 | { 305 | request_by_type_tag::<'a, tags::Ref>, P>(provider) 306 | } 307 | 308 | pub fn request_ref_by_slot<'a, T, P>(provider: &'a P, slot: usize) -> Option<&'a T> 309 | where 310 | T: 'static + ?Sized, 311 | P: SlottedProvider + ?Sized, 312 | { 313 | let mut tagged = TaggedOption::<'a, tags::Ref>>(None); 314 | provider.provide(slot, tagged.as_demand()); 315 | tagged.0 316 | } 317 | 318 | pub fn request_mut<'a, T, P>(provider: &'a mut P) -> Option<&'a mut T> 319 | where 320 | T: 'static + ?Sized, 321 | P: Provider + ?Sized, 322 | { 323 | let mut tagged = TaggedOption::<'a, tags::RefMut>>(None); 324 | provider.provide_mut(tagged.as_demand()); 325 | tagged.0 326 | } 327 | 328 | /// Request a specific value by tag from the `Provider`. 329 | fn request_by_type_tag<'a, I, P>(provider: &'a P) -> Option 330 | where 331 | I: tags::Type<'a>, 332 | P: Provider + ?Sized, 333 | { 334 | let mut tagged = TaggedOption::<'a, I>(None); 335 | provider.provide(tagged.as_demand()); 336 | tagged.0 337 | } 338 | 339 | impl Provider for () { 340 | fn provide<'a>(&'a self, demand: &mut Demand<'a>) { 341 | demand.provide_ref(self); 342 | } 343 | 344 | fn provide_mut<'a>(&'a mut self, demand: &mut Demand<'a>) { 345 | demand.provide_mut(self); 346 | } 347 | } 348 | -------------------------------------------------------------------------------- /runa-io/src/buf.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | os::{ 3 | fd::{FromRawFd, OwnedFd}, 4 | unix::io::RawFd, 5 | }, 6 | pin::Pin, 7 | task::{ready, Poll}, 8 | }; 9 | 10 | use pin_project_lite::pin_project; 11 | use runa_io_traits::{OwnedFds, ReadMessage}; 12 | 13 | use crate::traits::{AsyncBufReadWithFd, AsyncReadWithFd}; 14 | 15 | pin_project! { 16 | /// A buffered reader for reading data with file descriptors. 17 | /// 18 | /// #Note 19 | /// 20 | /// Because of the special treatment of file descriptors, i.e. they are closed if we don't call 21 | /// `recvmsg` with a big enough buffer, so every time we read, we have to read all of them, whehter 22 | /// there are spare buffer left or not. This means the file descriptors buffer will grow 23 | /// indefinitely if they are not read from BufWithFd. 24 | /// 25 | /// Also, users are encouraged to use up all the available data before calling 26 | /// poll_fill_buf/poll_fill_buf_until again, otherwise there is potential for causing a lot of 27 | /// allocations and memcpys. 28 | #[derive(Debug)] 29 | pub struct BufReaderWithFd { 30 | #[pin] 31 | inner: T, 32 | buf: Vec, 33 | cap_data: usize, 34 | filled_data: usize, 35 | pos_data: usize, 36 | 37 | fd_buf: Vec, 38 | } 39 | } 40 | 41 | impl BufReaderWithFd { 42 | #[inline] 43 | pub fn new(inner: T) -> Self { 44 | Self::with_capacity(inner, 4 * 1024, 32) 45 | } 46 | 47 | #[inline] 48 | pub fn shrink(self: Pin<&mut Self>) { 49 | // We have something to do if either: 50 | // 1. pos_data > 0 - we can move data to the front 51 | // 2. buf.len() > filled_data, and buf.len() > cap_data - we can shrink the 52 | // buffer down to filled_data or cap_data 53 | if self.pos_data > 0 || self.buf.len() > std::cmp::max(self.filled_data, self.cap_data) { 54 | let this = self.project(); 55 | let data_len = *this.filled_data - *this.pos_data; 56 | // Safety: pos_data and filled_data are valid indices. u8 is Copy and !Drop 57 | unsafe { 58 | std::ptr::copy( 59 | this.buf[*this.pos_data..].as_ptr(), 60 | this.buf.as_mut_ptr(), 61 | data_len, 62 | ) 63 | }; 64 | this.buf.truncate(std::cmp::max(data_len, *this.cap_data)); 65 | this.buf.shrink_to_fit(); 66 | *this.pos_data = 0; 67 | *this.filled_data = data_len; 68 | } 69 | } 70 | 71 | #[inline] 72 | pub fn with_capacity(inner: T, cap_data: usize, cap_fd: usize) -> Self { 73 | // TODO: consider using box::new_uninit_slice when #63291 is stablized 74 | // Actually, we can't use MaybeUninit here, AsyncRead::poll_read has no 75 | // guarantee that it will definitely initialize the number of bytes it 76 | // claims to have read. That's why tokio uses a ReadBuf type track how 77 | // many bytes have been initialized. 78 | Self { 79 | inner, 80 | buf: vec![0; cap_data], 81 | filled_data: 0, 82 | pos_data: 0, 83 | cap_data, 84 | 85 | fd_buf: Vec::with_capacity(cap_fd), 86 | } 87 | } 88 | 89 | #[inline] 90 | fn buffer(&self) -> &[u8] { 91 | let range = self.pos_data..self.filled_data; 92 | // Safety: invariant: filled_data <= buf.len() 93 | unsafe { self.buf.get_unchecked(range) } 94 | } 95 | } 96 | 97 | unsafe impl AsyncBufReadWithFd for BufReaderWithFd { 98 | fn poll_fill_buf_until( 99 | mut self: Pin<&mut Self>, 100 | cx: &mut std::task::Context<'_>, 101 | len: usize, 102 | ) -> Poll> { 103 | if self.pos_data + len > self.buf.len() || self.filled_data == self.pos_data { 104 | // Try to shrink buffer before we grow it. Or adjust buffer pointers when the 105 | // buf is empty. 106 | self.as_mut().shrink(); 107 | } 108 | while self.filled_data - self.pos_data < len { 109 | let this = self.as_mut().project(); 110 | if this.filled_data == this.pos_data { 111 | *this.filled_data = 0; 112 | *this.pos_data = 0; 113 | } 114 | if *this.pos_data + len > this.buf.len() { 115 | this.buf.resize(len + *this.pos_data, 0); 116 | } 117 | 118 | // Safety: loop invariant: filled_data < len + pos_data 119 | // post condition from the if above: buf.len() >= len + pos_data 120 | // combined: filled_data < buf.len() 121 | let buf = unsafe { &mut this.buf.get_unchecked_mut(*this.filled_data..) }; 122 | // Safety: OwnedFd is repr(transparent) over RawFd. 123 | let fd_buf = unsafe { 124 | std::mem::transmute::<&mut Vec, &mut Vec>(&mut *this.fd_buf) 125 | }; 126 | let nfds = fd_buf.len(); 127 | let bytes = ready!(this.inner.poll_read_with_fds(cx, buf, fd_buf))?; 128 | if bytes == 0 && (fd_buf.len() == nfds) { 129 | // We hit EOF while the buffer is not filled 130 | tracing::debug!( 131 | "EOF while the buffer is not filled, filled {}", 132 | this.filled_data 133 | ); 134 | return Poll::Ready(Err(std::io::ErrorKind::UnexpectedEof.into())) 135 | } 136 | *this.filled_data += bytes; 137 | } 138 | 139 | Poll::Ready(Ok(())) 140 | } 141 | 142 | #[inline] 143 | fn fds(&self) -> &[RawFd] { 144 | &self.fd_buf[..] 145 | } 146 | 147 | #[inline] 148 | fn buffer(&self) -> &[u8] { 149 | self.buffer() 150 | } 151 | 152 | fn consume(self: Pin<&mut Self>, amt: usize, amt_fd: usize) { 153 | let this = self.project(); 154 | assert!(amt <= *this.filled_data - *this.pos_data); 155 | *this.pos_data += amt; 156 | this.fd_buf.drain(..amt_fd); 157 | } 158 | } 159 | 160 | impl ReadMessage for BufReaderWithFd {} 161 | 162 | impl AsyncReadWithFd for BufReaderWithFd { 163 | fn poll_read_with_fds( 164 | mut self: Pin<&mut Self>, 165 | cx: &mut std::task::Context<'_>, 166 | mut buf: &mut [u8], 167 | fds: &mut Fds, 168 | ) -> Poll> { 169 | ready!(self.as_mut().poll_fill_buf_until(cx, 1))?; 170 | let our_buf = self.as_ref().get_ref().buffer(); 171 | let read_len = std::cmp::min(our_buf.len(), buf.len()); 172 | buf[..read_len].copy_from_slice(&our_buf[..read_len]); 173 | buf = &mut buf[read_len..]; 174 | 175 | let this = self.as_mut().project(); 176 | fds.extend( 177 | this.fd_buf 178 | .drain(..) 179 | .map(|fd| unsafe { OwnedFd::from_raw_fd(fd) }), 180 | ); 181 | 182 | self.as_mut().consume(read_len, 0); 183 | 184 | let mut read = read_len; 185 | if !buf.is_empty() { 186 | // If we still have buffer left, we try to read directly into the buffer to 187 | // opportunistically avoid copying. 188 | // 189 | // If `poll_read_with_fds` returns `Poll::Pending`, next time this function is 190 | // called we will fill our buffer instead re-entering this if 191 | // branch. 192 | let this = self.project(); 193 | match this.inner.poll_read_with_fds(cx, buf, fds)? { 194 | Poll::Ready(bytes) => { 195 | read += bytes; 196 | }, 197 | Poll::Pending => {}, // This is fine - we already read data. 198 | } 199 | } 200 | 201 | Poll::Ready(Ok(read)) 202 | } 203 | } 204 | 205 | #[cfg(test)] 206 | mod test { 207 | use std::{os::fd::AsRawFd, pin::Pin}; 208 | 209 | use arbitrary::Arbitrary; 210 | use futures_util::io::Write; 211 | use smol::{future::poll_fn, Task}; 212 | use tracing::debug; 213 | 214 | use crate::{ 215 | traits::{AsyncBufReadWithFd, AsyncWriteWithFd}, 216 | BufReaderWithFd, 217 | }; 218 | struct WriteExactWithFd<'a> { 219 | data: &'a [u8], 220 | fds: &'a mut Vec, 221 | stream: &'a mut crate::WriteWithFd, 222 | } 223 | impl<'a> std::future::Future for WriteExactWithFd<'a> { 224 | type Output = std::io::Result<()>; 225 | 226 | fn poll( 227 | self: Pin<&mut Self>, 228 | cx: &mut std::task::Context<'_>, 229 | ) -> std::task::Poll { 230 | let this = self.get_mut(); 231 | while !this.data.is_empty() { 232 | let n = std::task::ready!(Pin::new(&mut *this.stream).poll_write_with_fds( 233 | cx, 234 | &this.data[..], 235 | this.fds 236 | ))?; 237 | this.data = &this.data[n..]; 238 | } 239 | std::task::Poll::Ready(Ok(())) 240 | } 241 | } 242 | async fn buf_roundtrip_seeded(raw: &[u8]) { 243 | let mut source = arbitrary::Unstructured::new(raw); 244 | let (rx, tx) = std::os::unix::net::UnixStream::pair().unwrap(); 245 | let (_, mut tx) = crate::split_unixstream(tx).unwrap(); 246 | let (rx, _) = crate::split_unixstream(rx).unwrap(); 247 | let mut rx = BufReaderWithFd::new(rx); 248 | let receiver = async move { 249 | debug!("receiver start"); 250 | 251 | let mut bytes = Vec::new(); 252 | let mut fds = Vec::new(); 253 | loop { 254 | let buf = if let Err(e) = rx.fill_buf_until(4).await { 255 | if e.kind() == std::io::ErrorKind::UnexpectedEof { 256 | break Ok((bytes, fds)) 257 | } else { 258 | break Err(e) 259 | } 260 | } else { 261 | rx.buffer() 262 | }; 263 | assert!(buf.len() >= 4); 264 | let len: [u8; 4] = buf[..4].try_into().unwrap(); 265 | let len = u32::from_le_bytes(len) as usize; 266 | debug!("received len: {:?}", len); 267 | rx.fill_buf_until(len).await?; 268 | assert!(rx.buffer().len() >= len); 269 | bytes.extend_from_slice(&rx.buffer()[4..len]); 270 | fds.extend_from_slice(rx.fds()); 271 | debug!("received fds: {:?}", rx.fds()); 272 | let nfds = rx.fds().len(); 273 | Pin::new(&mut rx).consume(len, nfds); 274 | } 275 | }; 276 | let sender = async move { 277 | let mut sent_bytes = Vec::new(); 278 | let mut sent_fds = Vec::new(); 279 | while let Ok(packet) = <&[u8]>::arbitrary(&mut source) { 280 | if packet.is_empty() { 281 | break 282 | } 283 | let has_fd = bool::arbitrary(&mut source).unwrap(); 284 | let mut fds = if has_fd { 285 | let fd: std::os::unix::io::OwnedFd = 286 | std::fs::File::open("/dev/null").unwrap().into(); 287 | sent_fds.push(fd.as_raw_fd()); 288 | vec![fd] 289 | } else { 290 | Vec::new() 291 | }; 292 | let len = (packet.len() as u32 + 4).to_ne_bytes(); 293 | (WriteExactWithFd { 294 | data: &len[..], 295 | fds: &mut fds, 296 | stream: &mut tx, 297 | }) 298 | .await 299 | .unwrap(); 300 | (WriteExactWithFd { 301 | data: packet, 302 | fds: &mut Vec::new(), 303 | stream: &mut tx, 304 | }) 305 | .await 306 | .unwrap(); 307 | debug!("send len: {:?}", packet.len() + 4); 308 | sent_bytes.extend_from_slice(packet); 309 | } 310 | (sent_bytes, sent_fds) 311 | }; 312 | let (received, (sent_bytes, sent_fds)) = futures_util::join!(receiver, sender); 313 | let (bytes, fds) = received.unwrap(); 314 | assert_eq!(bytes, sent_bytes); 315 | // The actual file descriptor number is not preserved, so we just check the 316 | // number of file descriptors matches. 317 | assert_eq!(fds.len(), sent_fds.len()); 318 | } 319 | #[test] 320 | fn buf_roundtrip() { 321 | use rand::{Rng, SeedableRng}; 322 | tracing_subscriber::fmt::init(); 323 | let mut rng = rand::rngs::SmallRng::seed_from_u64(0x1238_aefb_d129_3a12); 324 | let mut raw: Vec = Vec::with_capacity(1024 * 1024); 325 | raw.resize(1024 * 1024, 0); 326 | rng.fill(raw.as_mut_slice()); 327 | futures_executor::block_on(buf_roundtrip_seeded(&raw)); 328 | } 329 | } 330 | -------------------------------------------------------------------------------- /runa-orbiter/src/shell/surface/roles/subsurface.rs: -------------------------------------------------------------------------------- 1 | //! Role object for `wl_subsurface`. 2 | 3 | use std::{ 4 | cell::Cell, 5 | rc::{Rc, Weak}, 6 | }; 7 | 8 | use derive_where::derive_where; 9 | use dlv_list::Index; 10 | use runa_core::provide_any; 11 | use runa_wayland_protocols::wayland::wl_subsurface; 12 | 13 | use crate::{ 14 | shell::{ 15 | surface::{Surface, StackEntry, State as SurfaceState}, 16 | Shell, 17 | }, 18 | utils::geometry::{coords, Point}, 19 | }; 20 | 21 | /// The `wl_subsurface` role. 22 | /// 23 | /// # Note about cache and pending states 24 | /// 25 | /// A surface normally has a pending and a current state. Changes are 26 | /// stored in the pending state first, then applied to the current 27 | /// state when `wl_surface.commit` is called. 28 | /// 29 | /// Subsurfaces has one more state - the cached state. This state exists if 30 | /// the subsurface is in synchronized mode. In sync mode, commit applies 31 | /// pending state to a cached state, and the cached state is applied to 32 | /// current state when the parent calls commit, if the partent is desynced; 33 | /// otherwise the cached state becomes part of the parent's cached 34 | /// state. 35 | /// 36 | /// We can see this as a tree of surface states, rooted at a "top-level" 37 | /// surface, such as a surface with the `xdg_toplevel` role. The root's 38 | /// current state references the children's current states, and the 39 | /// children's current states in turn reference the grand-children's, so 40 | /// on and so forth. When a synced child commits, its current state updates, 41 | /// but it doesn't update its parent's reference to its current state. 42 | /// So the parent still references the previous state, until the parent 43 | /// also commits. 44 | /// 45 | /// A complication is when a child is destroyed, either by destroying the 46 | /// surface or deactivating its role, it's immediately removed, without 47 | /// going through the pending or the cached state. We can detect this by 48 | /// checking if the role object is active, while going through the tree 49 | /// of surfaces. 50 | #[derive(Debug)] 51 | #[derive_where(Clone)] 52 | pub struct Subsurface { 53 | sync: bool, 54 | inherited_sync: bool, 55 | pub(super) is_active: Rc>, 56 | /// Index of this surface in parent's `stack` list. 57 | /// Note this index should be stable across parent updates, including 58 | /// appending to the stack, reordering the stack. a guarantee 59 | /// from VecList. 60 | pub(crate) stack_index: Index>, 61 | pub(super) parent: Weak>, 62 | } 63 | 64 | #[derive(Debug, Clone)] 65 | pub(in crate::shell) struct State { 66 | /// Parent surface *state* of this surface *state*. A surface state is 67 | /// only considered a parent after it has been committed. This 68 | /// is different from [`Subsurface::parent`], which is the 69 | /// `Rc` (surface, not surface state) that is the parent of 70 | /// this surface. A surface can have multiple surface states 71 | /// each have different parent surface states. But a surface can 72 | /// have only one parent surface. 73 | /// 74 | /// A surface state can have multiple parents because of the sync 75 | /// mechanism of subsurfaces. i.e. a subsurface can be attached 76 | /// to a parent, then the parent has its own parent. When 77 | /// the parent commits, its old state will still be referenced by 78 | /// the grandparent, and it will have a new cached state. 79 | /// Both the old state and the new state will be "parents" 80 | /// of this surface state. 81 | /// 82 | /// If that's the case, this field will point to the oldest, still valid 83 | /// parent. For states visible from a "root" surface (e.g. a 84 | /// xdg_toplevel), this conveniently forms a path towards the 85 | /// root's current state. 86 | pub(in crate::shell) parent: Option, 87 | 88 | /// See [`Subsurface::stack_index`] 89 | pub(in crate::shell) stack_index: Index>, 90 | 91 | /// Whether the corresponding role is active. 92 | pub(in crate::shell) is_active: Rc>, 93 | } 94 | impl super::State for State {} 95 | impl Subsurface { 96 | /// Attach a surface to a parent surface, and add the subsurface role to 97 | /// id. 98 | pub fn attach(parent: Rc>, surface: Rc>, shell: &mut S) -> bool { 99 | if surface.role.borrow().is_some() { 100 | // already has a role 101 | tracing::debug!("surface {:p} already has a role", Rc::as_ptr(&surface)); 102 | return false 103 | } 104 | // Preventing cycle creation 105 | if Rc::ptr_eq(&parent.root(), &surface) { 106 | tracing::debug!("cycle detected"); 107 | return false 108 | } 109 | let mut parent_pending = parent.pending_mut(); 110 | let stack_index = parent_pending 111 | .stack 112 | .push_front(StackEntry::Subsurface { 113 | token: surface.current_key(), 114 | position: Point::new(0, 0), 115 | }); 116 | let is_active = Rc::new(Cell::new(true)); 117 | let role = Self { 118 | sync: true, 119 | inherited_sync: true, 120 | is_active: is_active.clone(), 121 | stack_index, 122 | parent: Rc::downgrade(&parent), 123 | }; 124 | tracing::debug!( 125 | "attach {:p} to {:p}", 126 | Rc::as_ptr(&surface), 127 | Rc::as_ptr(&parent) 128 | ); 129 | surface.set_role(role, shell); 130 | surface 131 | .pending_mut() 132 | .set_role_state(State:: { 133 | parent: None, 134 | stack_index, 135 | is_active, 136 | }); 137 | true 138 | } 139 | 140 | /// Returns a weak reference to the parent surface. 141 | pub fn parent(&self) -> &Weak> { 142 | &self.parent 143 | } 144 | } 145 | 146 | impl super::Role for Subsurface { 147 | fn name(&self) -> &'static str { 148 | wl_subsurface::v1::NAME 149 | } 150 | 151 | fn is_active(&self) -> bool { 152 | self.is_active.get() 153 | } 154 | 155 | fn deactivate(&mut self, _shell: &mut S) { 156 | tracing::debug!("deactivate subsurface role {}", self.is_active.get()); 157 | if !self.is_active.get() { 158 | return 159 | } 160 | // Deactivating the subsurface role is immediate, but we don't know 161 | // how many other surface states there are that are referencing this 162 | // subsurface state, as our ancestors can have any number of "cached" 163 | // states. And we aren't keeping track of all of them. Instead we 164 | // mark it inactive, and skip over inactive states when we iterate 165 | // over the subsurface tree. 166 | self.is_active.set(false); 167 | 168 | // Remove ourself from parent's pending stack, so when the parent 169 | // eventually commits, it will drop us. 170 | let parent = self 171 | .parent 172 | .upgrade() 173 | .expect("surface is destroyed but its state is still being used"); 174 | let mut parent_pending_state = parent.pending_mut(); 175 | parent_pending_state.stack.remove(self.stack_index).unwrap(); 176 | self.parent = Weak::new(); 177 | } 178 | 179 | fn provide<'a>(&'a self, demand: &mut provide_any::Demand<'a>) { 180 | demand.provide_ref(self); 181 | } 182 | 183 | fn provide_mut<'a>(&'a mut self, demand: &mut provide_any::Demand<'a>) { 184 | demand.provide_mut(self); 185 | } 186 | 187 | fn post_commit(&mut self, shell: &mut S, surface: &Surface) { 188 | // update the state referenced in parent's pending state's stack. 189 | let parent = self 190 | .parent 191 | .upgrade() 192 | .expect("surface is destroyed but its state is still being used"); 193 | 194 | let mut parent_pending_state = parent.pending_mut(); 195 | let parent_pending_stack_entry = parent_pending_state 196 | .stack 197 | .get_mut(self.stack_index) 198 | .unwrap(); 199 | let StackEntry::Subsurface { token, .. } = parent_pending_stack_entry else { 200 | panic!("subsurface stack entry has unexpected type") 201 | }; 202 | *token = surface.current_key(); 203 | 204 | // the current state is now referenced by the parent's pending state, 205 | // clear the parent field. (parent could have been set because pending state was 206 | // cloned from a previous current state) 207 | let current = surface.current_mut(shell); 208 | let role_state = current 209 | .role_state_mut::>() 210 | .expect("subsurface role state missing") 211 | .expect("subsurface role state has unexpected type"); 212 | role_state.parent = None; 213 | } 214 | } 215 | 216 | /// Double ended iterator for iterating over a surface and its subsurfaces 217 | /// in the order they are stacked. 218 | /// 219 | /// The forward order is from bottom to top. This iterates over the 220 | /// committed states of the surfaces, as defined by `wl_surface.commit`. 221 | pub fn subsurface_iter( 222 | root: S::Token, 223 | s: &S, 224 | ) -> impl DoubleEndedIterator)> + '_ { 225 | macro_rules! generate_advance { 226 | ($next_in_stack:ident, $next_maybe_deactivated:ident, $next:ident, $id:literal) => { 227 | /// Advance the front pointer to the next surface in the 228 | /// stack. The next surface might be deactivated. 229 | fn $next_maybe_deactivated(&mut self) { 230 | if self.head[0].0 == self.head[1].0 { 231 | self.is_empty = true; 232 | } 233 | if self.is_empty { 234 | return 235 | } 236 | 237 | // The head we are advancing 238 | let curr_head = &mut self.head[$id]; 239 | 240 | let ret = self.shell.get(curr_head.0); 241 | if let Some((next, offset)) = 242 | SurfaceState::$next_in_stack(curr_head.0, ret.stack_index.into(), self.shell) 243 | { 244 | curr_head.1 += offset; 245 | curr_head.0 = next; 246 | } else { 247 | // `ret` is at the bottom/top of its own stack. this includes the case of 248 | // `ret` being the only surface in its stack. So we need return 249 | // upwards to the parent, and find the next surface in the parent's 250 | // stack. We do this repeatedly if we are also at the end of the 251 | // parent's stack. 252 | let mut curr = ret; 253 | let mut offset = curr_head.1; 254 | *curr_head = loop { 255 | let role_state = curr 256 | .role_state::>() 257 | .expect("subsurface role state missing") 258 | .expect("subsurface role state has unexpected type"); 259 | let parent_key = role_state.parent.unwrap_or_else(|| { 260 | panic!( 261 | "surface state {curr:?} (key: {:?}) has no parent, but is in a \ 262 | stack", 263 | curr_head.0 264 | ) 265 | }); 266 | let parent = self.shell.get(parent_key); 267 | let stack_index = role_state.stack_index; 268 | offset -= parent.stack.get(stack_index).unwrap().position(); 269 | 270 | if let Some((next, next_offset)) = 271 | SurfaceState::$next_in_stack(parent_key, stack_index.into(), self.shell) 272 | { 273 | offset += next_offset; 274 | break (next, offset) 275 | } 276 | curr = parent; 277 | }; 278 | } 279 | } 280 | 281 | fn $next(&mut self) { 282 | while !self.is_empty { 283 | self.$next_maybe_deactivated(); 284 | let ret = self.shell.get(self.head[0].0); 285 | let role_active = ret 286 | .role_state::>() 287 | // If the role state is not SubsurfaceState, or if the role state 288 | // doesn't exist, then the surface is top-level. 289 | .flatten() 290 | .map_or(true, |role_state| role_state.is_active.get()); 291 | if role_active { 292 | break 293 | } 294 | } 295 | } 296 | }; 297 | } 298 | struct SubsurfaceIter<'a, S: Shell> { 299 | shell: &'a S, 300 | /// Key and offset from the root surface. 301 | head: [(S::Token, Point); 2], 302 | is_empty: bool, 303 | } 304 | 305 | impl SubsurfaceIter<'_, S> { 306 | generate_advance!(next_in_stack, next_maybe_deactivated, next, 0); 307 | 308 | generate_advance!(prev_in_stack, prev_maybe_deactivated, prev, 1); 309 | } 310 | 311 | impl Iterator for SubsurfaceIter<'_, S> { 312 | type Item = (S::Token, Point); 313 | 314 | fn next(&mut self) -> Option { 315 | if self.is_empty { 316 | None 317 | } else { 318 | let ret = self.head[0]; 319 | self.next(); 320 | Some(ret) 321 | } 322 | } 323 | } 324 | impl DoubleEndedIterator for SubsurfaceIter<'_, S> { 325 | fn next_back(&mut self) -> Option { 326 | if self.is_empty { 327 | None 328 | } else { 329 | let ret = self.head[1]; 330 | self.prev(); 331 | Some(ret) 332 | } 333 | } 334 | } 335 | 336 | SubsurfaceIter { 337 | shell: s, 338 | head: [ 339 | SurfaceState::top(root, s), 340 | SurfaceState::bottom(root, s), 341 | ], 342 | is_empty: false, 343 | } 344 | } 345 | -------------------------------------------------------------------------------- /runa-orbiter/src/objects/shm.rs: -------------------------------------------------------------------------------- 1 | //! The `wl_shm` object 2 | 3 | use std::{ 4 | cell::{Cell, Ref, RefCell}, 5 | future::Future, 6 | os::{fd::AsRawFd, unix::io::OwnedFd}, 7 | rc::Rc, 8 | }; 9 | 10 | use dlv_list::{Index, VecList}; 11 | use runa_core::{ 12 | client::traits::{Client, ClientParts, Store}, 13 | error::{self, ProtocolError}, 14 | objects::{wayland_object, DISPLAY_ID}, 15 | }; 16 | use runa_io::traits::WriteMessage; 17 | use runa_wayland_protocols::wayland::{ 18 | wl_display::v1 as wl_display, wl_shm::v1 as wl_shm, wl_shm_pool::v1 as wl_shm_pool, 19 | }; 20 | use runa_wayland_types::{Fd as WaylandFd, NewId}; 21 | use spin::mutex::Mutex; 22 | 23 | use crate::{ 24 | objects, 25 | shell::buffers::{self, HasBuffer}, 26 | utils::geometry::Extent, 27 | }; 28 | 29 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] 30 | struct MapRecord { 31 | start: *const libc::c_void, 32 | len: usize, 33 | } 34 | 35 | impl MapRecord { 36 | fn contains(&self, ptr: *const libc::c_void) -> bool { 37 | ptr >= self.start && ptr < unsafe { self.start.add(self.len) } 38 | } 39 | } 40 | 41 | // Safety: `start` is never dereferenced. 42 | unsafe impl Send for MapRecord {} 43 | // Safety: `start` is never dereferenced. 44 | unsafe impl Sync for MapRecord {} 45 | 46 | lazy_static::lazy_static! { 47 | /// List of all mapped regions. Used by the SIGBUS handler to decide whether a SIGBUS is 48 | /// generated because a client shrink its shm pool. 49 | /// 50 | /// # Regarding `Mutex` 51 | /// 52 | /// Using `std::sync::Mutex` can be undefined behavior in signal handlers, so we use a spin 53 | /// lock here instead. 54 | static ref MAP_RECORDS: Mutex> = Mutex::new(VecList::new()); 55 | } 56 | 57 | thread_local! { 58 | static SIGBUS_COUNT: Cell = const { Cell::new(0) }; 59 | } 60 | 61 | /// The number of times a recoverable SIGBUS has occurred for the current 62 | /// thread. Can be used to detect if a client shrunk its shm pool. 63 | pub fn sigbus_count() -> usize { 64 | SIGBUS_COUNT.with(|c| c.get()) 65 | } 66 | 67 | unsafe fn map_zeroed(addr: *const libc::c_void, len: usize) -> Result<(), libc::c_int> { 68 | unsafe { 69 | libc::munmap(addr as *mut libc::c_void, len); 70 | } 71 | let ret = unsafe { 72 | libc::mmap( 73 | addr as *mut libc::c_void, 74 | len, 75 | libc::PROT_READ, 76 | libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_FIXED, 77 | -1, 78 | 0, 79 | ) 80 | }; 81 | if ret == libc::MAP_FAILED { 82 | Err(unsafe { *libc::__errno_location() }) 83 | } else { 84 | Ok(()) 85 | } 86 | } 87 | 88 | /// Handle a SIGBUS signal. Tries to recover from SIGBUS caused by a client 89 | /// shrinking its shm pool. You MUST call this function in your SIGBUS handler 90 | /// if you want to map shm pools. 91 | /// 92 | /// Returns `true` if the signal was handled, `false` otherwise. Usually you 93 | /// should reraise the signal if this function returns `false`. 94 | /// 95 | /// # Safety 96 | /// 97 | /// Must be called from a SIGBUS handler, with `info` provided to the signal 98 | /// handler. 99 | pub unsafe fn handle_sigbus(info: &libc::siginfo_t) -> bool { 100 | let faulty_ptr = unsafe { info.si_addr() } as *const libc::c_void; 101 | // # Regarding deadlocks 102 | // 103 | // This function will deadlock if a SIGBUS occurs while the current thread is 104 | // holding a lock on `MAP_RECORDS`. 105 | // The only thing we do while holding this lock is accessing the `VecList` 106 | // inside, and it should be completely safe and never trigger a SIGBUS. 107 | let records = MAP_RECORDS.lock(); 108 | if let Some(record) = records.iter().find(|r| r.contains(faulty_ptr)) { 109 | SIGBUS_COUNT.with(|c| c.set(c.get() + 1)); 110 | unsafe { map_zeroed(record.start, record.len) }.is_ok() 111 | } else { 112 | false 113 | } 114 | } 115 | 116 | /// Implementation of the `wl_shm` interface. 117 | #[derive(Default, Debug, Clone, Copy)] 118 | pub struct Shm; 119 | 120 | enum ShmError { 121 | Mapping(u32, i32), 122 | } 123 | 124 | impl std::fmt::Debug for ShmError { 125 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 126 | match self { 127 | ShmError::Mapping(_, err) => write!(f, "Mapping error: {err}"), 128 | } 129 | } 130 | } 131 | 132 | impl std::fmt::Display for ShmError { 133 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 134 | std::fmt::Debug::fmt(self, f) 135 | } 136 | } 137 | 138 | impl std::error::Error for ShmError {} 139 | 140 | impl ProtocolError for ShmError { 141 | fn fatal(&self) -> bool { 142 | match self { 143 | ShmError::Mapping(..) => true, 144 | } 145 | } 146 | 147 | fn wayland_error(&self) -> Option<(u32, u32)> { 148 | match self { 149 | ShmError::Mapping(object_id, _) => 150 | Some((*object_id, wl_shm::enums::Error::InvalidFd as u32)), 151 | } 152 | } 153 | } 154 | 155 | // TODO: Add a trait for ShmPool and make Shm generic over the pool type. 156 | 157 | #[wayland_object] 158 | impl wl_shm::RequestDispatch for Shm 159 | where 160 | Ctx: Client, 161 | Ctx::Object: From, 162 | { 163 | type Error = error::Error; 164 | 165 | type CreatePoolFut<'a> = impl Future> + 'a where Ctx: 'a; 166 | 167 | fn create_pool( 168 | ctx: &mut Ctx, 169 | object_id: u32, 170 | id: NewId, 171 | mut fd: WaylandFd, 172 | size: i32, 173 | ) -> Self::CreatePoolFut<'_> { 174 | tracing::debug!("creating shm_pool with size {}", size); 175 | async move { 176 | let conn = ctx.connection_mut(); 177 | if size <= 0 { 178 | conn.send(DISPLAY_ID, wl_display::events::Error { 179 | code: wl_shm::enums::Error::InvalidStride as u32, 180 | object_id: object_id.into(), 181 | message: b"invalid size".into(), 182 | }) 183 | .await?; 184 | return Ok(()) 185 | } 186 | let fd = unsafe { 187 | fd.assume_owned(); 188 | fd.take().unwrap_unchecked() 189 | }; 190 | // Safety: mapping the file descriptor is harmless until we try to access it. 191 | let addr = unsafe { 192 | libc::mmap( 193 | std::ptr::null_mut(), 194 | size as usize, 195 | libc::PROT_READ, 196 | libc::MAP_SHARED, 197 | fd.as_raw_fd(), 198 | 0, 199 | ) 200 | }; 201 | if addr == libc::MAP_FAILED { 202 | return Err(error::Error::custom(ShmError::Mapping(object_id, unsafe { 203 | *libc::__errno_location() 204 | }))) 205 | } 206 | let pool = ShmPool { 207 | inner: Rc::new(RefCell::new(ShmPoolInner { 208 | fd, 209 | addr, 210 | len: size as usize, 211 | map: MAP_RECORDS.lock().push_back(MapRecord { 212 | start: addr, 213 | len: size as usize, 214 | }), 215 | })), 216 | }; 217 | if ctx.objects_mut().insert(id.0, pool).is_err() { 218 | ctx.connection_mut() 219 | .send(DISPLAY_ID, wl_display::events::Error { 220 | code: wl_display::enums::Error::InvalidObject as u32, 221 | object_id: object_id.into(), 222 | message: b"id already in use".into(), 223 | }) 224 | .await?; 225 | } 226 | Ok(()) 227 | } 228 | } 229 | } 230 | 231 | impl Drop for ShmPoolInner { 232 | fn drop(&mut self) { 233 | self.unmap() 234 | } 235 | } 236 | 237 | impl ShmPoolInner { 238 | // Safety: caller must ensure all the requirements states on [`ShmPool::map`] 239 | // are met. 240 | unsafe fn as_ref(&self) -> &[u8] { 241 | tracing::debug!("mapping shm_pool {:p}, size {}", self, self.len); 242 | assert!(!self.addr.is_null()); 243 | unsafe { std::slice::from_raw_parts(self.addr as *const u8, self.len) } 244 | } 245 | 246 | fn unmap(&mut self) { 247 | // we might already be unmapped. e.g. if `resize` failed. 248 | if let Some(record) = MAP_RECORDS.lock().remove(self.map) { 249 | // Safety: `unmap` takes an exclusive reference, meaning no one can be holding 250 | // the slice returned by `as_ref`. 251 | unsafe { 252 | libc::munmap(record.start as *mut libc::c_void, record.len); 253 | } 254 | self.addr = std::ptr::null(); 255 | self.len = 0; 256 | } 257 | } 258 | } 259 | 260 | #[derive(Debug)] 261 | pub(crate) struct ShmPoolInner { 262 | fd: OwnedFd, 263 | addr: *const libc::c_void, 264 | len: usize, 265 | map: Index, 266 | } 267 | 268 | /// A shm memory pool. 269 | #[derive(Debug)] 270 | pub struct ShmPool { 271 | inner: Rc>, 272 | } 273 | 274 | impl ShmPool { 275 | /// Map the pool into memory. 276 | /// 277 | /// This can be called repeatedly to retrieve the slice whenever you need 278 | /// it. The map operation is only performed once. 279 | /// 280 | /// # Safety 281 | /// 282 | /// The file descriptor MUST be suitable for mapping. 283 | /// 284 | /// You MUST setup a SIGBUS handler that calls `handle_sigbus`. Otherwise if 285 | /// the client shrunk the pool after you have mapped it, you will get a 286 | /// SIGBUS when accessing the removed section of memory. `handle_sigbus` 287 | /// will automatcally map in zero pages in that case. 288 | pub unsafe fn map(&self) -> Ref<'_, [u8]> { 289 | Ref::map(self.inner.borrow(), |inner: &ShmPoolInner| inner.as_ref()) 290 | } 291 | } 292 | 293 | #[wayland_object] 294 | impl wl_shm_pool::RequestDispatch for ShmPool 295 | where 296 | Ctx: Client, 297 | Ctx::ServerContext: HasBuffer, 298 | B: From>, 299 | Ctx::Object: From>, 300 | { 301 | type Error = error::Error; 302 | 303 | type CreateBufferFut<'a> = impl Future> + 'a where Ctx: 'a; 304 | type DestroyFut<'a> = impl Future> + 'a where Ctx: 'a; 305 | type ResizeFut<'a> = impl Future> + 'a where Ctx: 'a; 306 | 307 | fn create_buffer( 308 | ctx: &mut Ctx, 309 | object_id: u32, 310 | id: NewId, 311 | offset: i32, 312 | width: i32, 313 | height: i32, 314 | stride: i32, 315 | format: runa_wayland_protocols::wayland::wl_shm::v1::enums::Format, 316 | ) -> Self::CreateBufferFut<'_> { 317 | async move { 318 | let ClientParts { 319 | objects, 320 | event_dispatcher, 321 | .. 322 | } = ctx.as_mut_parts(); 323 | let pool = objects.get::(object_id).unwrap().inner.clone(); 324 | 325 | let inserted = objects 326 | .try_insert_with(id.0, || { 327 | let buffer: objects::Buffer = objects::Buffer::new( 328 | buffers::Buffer::new( 329 | Extent::new(width as u32, height as u32), 330 | id.0, 331 | Buffer { 332 | pool, 333 | offset, 334 | width, 335 | height, 336 | stride, 337 | format, 338 | }, 339 | ), 340 | event_dispatcher, 341 | ); 342 | buffer.into() 343 | }) 344 | .is_some(); 345 | if !inserted { 346 | Err(runa_core::error::Error::IdExists(id.0)) 347 | } else { 348 | Ok(()) 349 | } 350 | } 351 | } 352 | 353 | fn destroy(ctx: &mut Ctx, object_id: u32) -> Self::DestroyFut<'_> { 354 | ctx.objects_mut().remove(object_id).unwrap(); 355 | futures_util::future::ok(()) 356 | } 357 | 358 | fn resize(ctx: &mut Ctx, object_id: u32, size: i32) -> Self::ResizeFut<'_> { 359 | async move { 360 | let len = size as usize; 361 | let pool = ctx.objects().get::(object_id).unwrap().inner.clone(); 362 | let mut inner = pool.borrow_mut(); 363 | tracing::debug!("resize shm_pool {:p} to {}", &*inner, size); 364 | if len > inner.len { 365 | let fd = inner.fd.as_raw_fd(); 366 | inner.unmap(); 367 | 368 | // Safety: mapping the file descriptor is harmless until we try to access it. 369 | let addr = unsafe { 370 | libc::mmap( 371 | std::ptr::null_mut(), 372 | len, 373 | libc::PROT_READ, 374 | libc::MAP_SHARED, 375 | fd, 376 | 0, 377 | ) 378 | }; 379 | if addr == libc::MAP_FAILED { 380 | return Err(error::Error::UnknownFatalError("mmap failed")) 381 | } 382 | 383 | inner.addr = addr; 384 | inner.len = len; 385 | // update th map record 386 | inner.map = MAP_RECORDS.lock().push_back(MapRecord { start: addr, len }); 387 | } 388 | Ok(()) 389 | } 390 | } 391 | } 392 | 393 | /// A shm buffer 394 | #[derive(Debug)] 395 | pub struct Buffer { 396 | pool: Rc>, 397 | offset: i32, 398 | width: i32, 399 | height: i32, 400 | stride: i32, 401 | format: wl_shm::enums::Format, 402 | } 403 | 404 | impl Buffer { 405 | /// Get the pool this buffer was created from. 406 | pub fn pool(&self) -> ShmPool { 407 | ShmPool { 408 | inner: self.pool.clone(), 409 | } 410 | } 411 | 412 | /// Offset of this buffer in the pool. 413 | pub fn offset(&self) -> i32 { 414 | self.offset 415 | } 416 | 417 | /// Width of this buffer. 418 | pub fn width(&self) -> i32 { 419 | self.width 420 | } 421 | 422 | /// Height of this buffer. 423 | pub fn height(&self) -> i32 { 424 | self.height 425 | } 426 | 427 | /// Stride of this buffer. 428 | pub fn stride(&self) -> i32 { 429 | self.stride 430 | } 431 | 432 | /// Format of this buffer. 433 | pub fn format(&self) -> wl_shm::enums::Format { 434 | self.format 435 | } 436 | } 437 | --------------------------------------------------------------------------------