From 3c85bea0bf1b889be59c0e48314781521e5ef860 Mon Sep 17 00:00:00 2001 From: Matthew Kosarek Date: Thu, 16 Apr 2026 14:55:48 -0400 Subject: [PATCH 1/2] Providing each Wayland interface instance with access to their client --- .../wayland_rs/build_script/cpp_builder.rs | 60 +++++++++++++++++++ .../wayland_rs/build_script/ffi_generation.rs | 1 + .../wayland_rs/build_script/main.rs | 32 ++++++++-- 3 files changed, 88 insertions(+), 5 deletions(-) diff --git a/src/server/frontend_wayland/wayland_rs/build_script/cpp_builder.rs b/src/server/frontend_wayland/wayland_rs/build_script/cpp_builder.rs index d9e3536215..9de2da938d 100644 --- a/src/server/frontend_wayland/wayland_rs/build_script/cpp_builder.rs +++ b/src/server/frontend_wayland/wayland_rs/build_script/cpp_builder.rs @@ -149,6 +149,48 @@ impl CppBuilder { } } + if !class.protected_constructor_args.is_empty() + || !class.protected_members.is_empty() + { + result.push_str("protected:\n"); + + if !class.protected_constructor_args.is_empty() { + let ctor_args_str: Vec = class + .protected_constructor_args + .iter() + .map(|arg| { + format!( + "{} {}", + cpp_arg_type_to_cpp_source(&arg.cpp_type, true), + arg.name + ) + }) + .collect(); + let initialiser_list: Vec = class + .protected_constructor_args + .iter() + .map(|arg| format!("{} ( std::move({}) )", arg.name, arg.name)) + .collect(); + result.push_str(&format!( + " {}({}) : {} {{}}\n", + class.name, + ctor_args_str.join(", "), + initialiser_list.join(", ") + )); + result.push_str("\n"); + } + + for member in &class.protected_members { + result.push_str(&format!( + " {} {};\n", + cpp_arg_type_to_cpp_source(&member.cpp_type, true), + member.name + )); + } + + result.push_str("\n"); + } + result.push_str("private:\n"); for member in &class.private_members { if member.optional { @@ -338,6 +380,8 @@ pub struct CppClass { pub name: String, pub methods: Vec, pub enums: Vec, + pub protected_constructor_args: Vec, + pub protected_members: Vec, pub private_members: Vec, } @@ -347,6 +391,8 @@ impl CppClass { name: sanitize_identifier(&name.into()), methods: vec![], enums: vec![], + protected_constructor_args: vec![], + protected_members: vec![], private_members: vec![], } } @@ -365,6 +411,20 @@ impl CppClass { .expect("enums cannot be empty after push") } + pub fn add_protected_constructor_arg(&mut self, arg: CppArg) -> &mut CppArg { + self.protected_constructor_args.push(arg); + self.protected_constructor_args + .last_mut() + .expect("protected_constructor_args cannot be empty after push") + } + + pub fn add_protected_member(&mut self, member: CppArg) -> &mut CppArg { + self.protected_members.push(member); + self.protected_members + .last_mut() + .expect("members cannot be empty after push") + } + pub fn add_private_member(&mut self, member: CppArg) -> &mut CppArg { self.private_members.push(member); self.private_members diff --git a/src/server/frontend_wayland/wayland_rs/build_script/ffi_generation.rs b/src/server/frontend_wayland/wayland_rs/build_script/ffi_generation.rs index 8ab0815983..ac386ebc9f 100644 --- a/src/server/frontend_wayland/wayland_rs/build_script/ffi_generation.rs +++ b/src/server/frontend_wayland/wayland_rs/build_script/ffi_generation.rs @@ -51,6 +51,7 @@ pub fn generate_ffi(protocols: &Vec, builders: &Vec fn uid(self: &WaylandClient) -> Result; fn gid(self: &WaylandClient) -> Result; fn equals(self: &WaylandClient, id: &WaylandClientId) -> bool; + fn id(self: &WaylandClient) -> Box; type WaylandClientId; diff --git a/src/server/frontend_wayland/wayland_rs/build_script/main.rs b/src/server/frontend_wayland/wayland_rs/build_script/main.rs index 9893e8176a..5414403520 100644 --- a/src/server/frontend_wayland/wayland_rs/build_script/main.rs +++ b/src/server/frontend_wayland/wayland_rs/build_script/main.rs @@ -205,8 +205,8 @@ fn generate_global_dispatch_impl( { fn bind( _state: &mut Self, - _handle: &wayland_server::DisplayHandle, - _client: &wayland_server::Client, + handle: &wayland_server::DisplayHandle, + client: &wayland_server::Client, resource: New<#namespace_name::#interface_name::#interface_struct_name>, // The global data is an Arc> instead of just a UniquePtr because it // has to be accessed mutability in order to call methods across the Rust -> C++ @@ -215,11 +215,12 @@ fn generate_global_dispatch_impl( data_init: &mut wayland_server::DataInit<'_, Self>, ) { use crate::ffi; + let wayland_client = Box::new(WaylandClient::new(client.clone(), handle.clone())); let mut guard = global_data.lock().unwrap(); // Methods on C++ classes must operate on Pin<&mut X> because those are the // only ones that can cross the FFI boundary from Rust -> C++. - let global = (&mut *guard).pin_mut().#create_global_method(); + let global = (&mut *guard).pin_mut().#create_global_method(wayland_client); let arc = Arc::new(Mutex::new(global)); // The initialization strategy here requires a "double initialization". First, @@ -590,7 +591,7 @@ fn write_dispatch_rs(protocols: &Vec) { use crate::protocols; use crate::wayland_server_core::ServerState; use crate::ffi; - use crate::wayland_client::WaylandClientId; + use crate::wayland_client::{WaylandClient, WaylandClientId}; use std::os::fd::{AsRawFd, RawFd}; use std::sync::{Arc, LazyLock, Mutex, RwLock}; use std::collections::HashMap; @@ -642,6 +643,7 @@ fn create_global_factory(protocols: &Vec) -> CppBuilder { builder.add_header_include(""); let mut namespace = CppNamespace::new(vec!["mir", "wayland_rs"]); let mut class = CppClass::new("GlobalFactory"); + namespace.add_forward_declaration_class("WaylandClient"); protocols.iter().for_each(|protocol| { protocol .interfaces @@ -652,7 +654,7 @@ fn create_global_factory(protocols: &Vec) -> CppBuilder { let class_name = format_wayland_interface_to_cpp_class(&global_interface.name); namespace.add_forward_declaration_class(&class_name); - let method = CppMethod::new( + let mut method = CppMethod::new( format!("create_{}", global_interface.name), Some(CppType::Object(class_name)), true, @@ -660,6 +662,11 @@ fn create_global_factory(protocols: &Vec) -> CppBuilder { true, true, ); + method.add_arg(CppArg::new( + CppType::Box("WaylandClient".to_string()), + "client", + false, + )); class.add_method(method); }) }); @@ -770,6 +777,8 @@ fn create_ffi_fwd_builder(protocols: &Vec) -> CppBuilder { // WaylandServer is declared in ffi.rs but used nowhere in the protocol headers; // include it for completeness so all Rust types are forward-declared. namespace.add_forward_declaration_class("WaylandServer"); + // WaylandClient is used in the protected constructor of every generated XxxImpl. + namespace.add_forward_declaration_class("WaylandClient"); for protocol in protocols { for interface in &protocol.interfaces { @@ -914,6 +923,19 @@ fn wayland_interface_to_cpp_class(interface: &WaylandInterface) -> CppClass { ); class.add_method(get_box_method); + // Add a protected constructor and member so that subclasses know which client + // they are serving and can interact with it as they please. + class.add_protected_constructor_arg(CppArg::new( + CppType::Box("WaylandClient".to_string()), + "client", + false, + )); + class.add_protected_member(CppArg::new( + CppType::Box("WaylandClient".to_string()), + "client", + false, + )); + for method in methods { class.add_method(method); } From 1ad9abe0595889e6591e789518a9d68cfa48dad4 Mon Sep 17 00:00:00 2001 From: Matthew Kosarek Date: Tue, 21 Apr 2026 11:48:16 -0400 Subject: [PATCH 2/2] Fix missing def --- .../wayland_rs/build_script/ffi_generation.rs | 1 + .../frontend_wayland/wayland_rs/src/wayland_client.rs | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/server/frontend_wayland/wayland_rs/build_script/ffi_generation.rs b/src/server/frontend_wayland/wayland_rs/build_script/ffi_generation.rs index ac386ebc9f..06433e6771 100644 --- a/src/server/frontend_wayland/wayland_rs/build_script/ffi_generation.rs +++ b/src/server/frontend_wayland/wayland_rs/build_script/ffi_generation.rs @@ -52,6 +52,7 @@ pub fn generate_ffi(protocols: &Vec, builders: &Vec fn gid(self: &WaylandClient) -> Result; fn equals(self: &WaylandClient, id: &WaylandClientId) -> bool; fn id(self: &WaylandClient) -> Box; + fn clone_box(self: &WaylandClient) -> Box; type WaylandClientId; diff --git a/src/server/frontend_wayland/wayland_rs/src/wayland_client.rs b/src/server/frontend_wayland/wayland_rs/src/wayland_client.rs index d124e3b043..9cc8f60e47 100644 --- a/src/server/frontend_wayland/wayland_rs/src/wayland_client.rs +++ b/src/server/frontend_wayland/wayland_rs/src/wayland_client.rs @@ -1,6 +1,7 @@ use wayland_server::{backend::ClientId, Client, DisplayHandle}; /// A C++ friendly wrapper around a wayland [Client] object. +#[derive(Clone)] pub struct WaylandClient { client: Client, handle: DisplayHandle, @@ -38,10 +39,20 @@ impl WaylandClient { .gid) } + /// Retrieve the [WaylandClientId] for this client. + pub fn id(&self) -> Box { + Box::new(WaylandClientId::new(self.client.id())) + } + /// Check if this client is wrapping the provided [WaylandClientId]. pub fn equals(&self, id: &WaylandClientId) -> bool { self.client.id() == id.id } + + /// Clone this client into a new [Box]. + pub fn clone_box(&self) -> Box { + Box::new(self.clone()) + } } /// An opaque ID for the WaylandClient.