From dee142b3bb7532880dad00d686fdaa788f374578 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Mon, 23 Feb 2026 20:38:22 +0100 Subject: [PATCH] control-center: add xwayland pane --- src/control_center.rs | 11 ++-- src/control_center/cc_sidebar.rs | 5 ++ src/control_center/cc_xwayland.rs | 87 +++++++++++++++++++++++++++++++ src/state.rs | 6 ++- src/xwayland.rs | 5 ++ 5 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 src/control_center/cc_xwayland.rs diff --git a/src/control_center.rs b/src/control_center.rs index 023a43f7..4f27a2a6 100644 --- a/src/control_center.rs +++ b/src/control_center.rs @@ -2,7 +2,7 @@ use { crate::{ control_center::{ cc_color_management::ColorManagementPane, cc_compositor::CompositorPane, - cc_idle::IdlePane, + cc_idle::IdlePane, cc_xwayland::XwaylandPane, }, egui_adapter::egui_platform::{ EggError, EggWindow, EggWindowOwner, @@ -37,6 +37,7 @@ mod cc_color_management; mod cc_compositor; mod cc_idle; mod cc_sidebar; +mod cc_xwayland; #[derive(Debug, Error)] pub enum ControlCenterError { @@ -72,6 +73,7 @@ bitflags! { CCI_COMPOSITOR, CCI_IDLE, CCI_COLOR_MANAGEMENT, + CCI_XWAYLAND, } pub struct ControlCenter { @@ -115,6 +117,7 @@ enum PaneType { Compositor(CompositorPane), Idle(IdlePane), ColorManagement(ColorManagementPane), + Xwayland(XwaylandPane), } struct CcBehavior<'a> { @@ -136,14 +139,16 @@ impl Pane { PaneType::Compositor(v) => v.title(res), PaneType::Idle(v) => v.title(res), PaneType::ColorManagement(v) => v.title(res), + PaneType::Xwayland(v) => v.title(res), } } - fn show(&mut self, _behavior: &mut CcBehavior<'_>, ui: &mut Ui) { + fn show(&mut self, behavior: &mut CcBehavior<'_>, ui: &mut Ui) { match &mut self.ty { PaneType::Compositor(p) => p.show(ui), PaneType::Idle(p) => p.show(ui), PaneType::ColorManagement(p) => p.show(ui), + PaneType::Xwayland(p) => p.show(behavior, ui), } } } @@ -154,6 +159,7 @@ impl PaneType { PaneType::Compositor(_) => CCI_COMPOSITOR, PaneType::Idle(_) => CCI_IDLE, PaneType::ColorManagement(_) => CCI_COLOR_MANAGEMENT, + PaneType::Xwayland(_) => CCI_XWAYLAND, } } } @@ -420,7 +426,6 @@ fn grid_label_ui(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> Inn ui.with_layout(Layout::right_to_left(Align::Center), add_contents) } -#[expect(dead_code)] fn tip(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui)) { icon_label(ICON_INFO).ui(ui).on_hover_ui(add_contents); } diff --git a/src/control_center/cc_sidebar.rs b/src/control_center/cc_sidebar.rs index 19441ca8..a3b5296e 100644 --- a/src/control_center/cc_sidebar.rs +++ b/src/control_center/cc_sidebar.rs @@ -11,6 +11,7 @@ enum PaneName { Compositor, Idle, ColorManagement, + Xwayland, } impl PaneName { @@ -19,6 +20,7 @@ impl PaneName { PaneName::Compositor => "Compositor", PaneName::Idle => "Idle", PaneName::ColorManagement => "Color Management", + PaneName::Xwayland => "Xwayland", } } } @@ -50,6 +52,9 @@ impl ControlCenterInner { PaneName::ColorManagement => { PaneType::ColorManagement(self.create_color_management_pane()) } + PaneName::Xwayland => { + PaneType::Xwayland(self.create_xwayland_pane()) + } }; self.open(tree, ty); ui.ctx().request_repaint(); diff --git a/src/control_center/cc_xwayland.rs b/src/control_center/cc_xwayland.rs new file mode 100644 index 00000000..656f059b --- /dev/null +++ b/src/control_center/cc_xwayland.rs @@ -0,0 +1,87 @@ +use { + crate::{ + compositor::DISPLAY, + control_center::{ + CcBehavior, ControlCenterInner, bool, combo_box_ui, grid, label, read_only_bool, tip, + }, + state::State, + utils::{errorfmt::ErrorFmt, oserror::OsError, static_text::StaticText}, + }, + egui::Ui, + linearize::Linearize, + std::rc::Rc, + uapi::c, +}; + +pub struct XwaylandPane { + state: Rc, +} + +impl ControlCenterInner { + pub fn create_xwayland_pane(self: &Rc) -> XwaylandPane { + XwaylandPane { + state: self.state.clone(), + } + } +} + +#[derive(Copy, Clone, PartialEq, Linearize)] +enum ScalingMode { + Default, + Downscaled, +} + +impl StaticText for ScalingMode { + fn text(&self) -> &'static str { + match self { + ScalingMode::Default => "default", + ScalingMode::Downscaled => "downscaled", + } + } +} + +impl XwaylandPane { + pub fn title(&self, res: &mut String) { + res.push_str("Xwayland"); + } + + pub fn show(&mut self, _behavior: &mut CcBehavior<'_>, ui: &mut Ui) { + let s = &self.state; + grid(ui, "settings", |ui| { + bool(ui, "Enabled", s.xwayland.enabled.get(), |b| { + s.set_xwayland_enabled(b) + }); + let mode = match self.state.xwayland.use_wire_scale.get() { + true => ScalingMode::Downscaled, + false => ScalingMode::Default, + }; + combo_box_ui( + ui, + "Scaling Mode", + |ui| { + tip(ui, |ui| { + ui.label(r#"`downscaled` is known as "X applications scale themselves""#); + }); + }, + mode, + |v| { + self.state + .set_xwayland_use_wire_scale(v == ScalingMode::Downscaled); + }, + ); + if let Some(display) = self.state.xwayland.display.get() { + label(ui, DISPLAY, &*display); + } + read_only_bool(ui, "Running", self.state.xwayland.running.get()); + if let Some(client) = self.state.xwayland.client.get() { + label(ui, "PID", client.pid_info.pid.to_string()); + } + }); + if let Some(client) = self.state.xwayland.client.get() + && ui.button("Kill").clicked() + && let Err(e) = uapi::kill(client.pid_info.pid, c::SIGTERM) + { + log::error!("Could not kill Xwayland: {}", ErrorFmt(OsError::from(e))); + } + } +} diff --git a/src/state.rs b/src/state.rs index e36cef43..97361a3c 100644 --- a/src/state.rs +++ b/src/state.rs @@ -16,7 +16,9 @@ use { cmm::{cmm_description::ColorDescription, cmm_manager::ColorManager}, compositor::{LIBEI_SOCKET, LogLevel}, config::ConfigProxy, - control_center::{CCI_COLOR_MANAGEMENT, CCI_COMPOSITOR, CCI_IDLE, ControlCenters}, + control_center::{ + CCI_COLOR_MANAGEMENT, CCI_COMPOSITOR, CCI_IDLE, CCI_XWAYLAND, ControlCenters, + }, copy_device::CopyDeviceRegistry, cpu_worker::CpuWorker, criteria::{clm::ClMatcherManager, tlm::TlMatcherManager}, @@ -1011,6 +1013,7 @@ impl State { } else { self.stop_xwayland(); } + self.trigger_cci(CCI_XWAYLAND); } pub fn set_xwayland_use_wire_scale(&self, use_wire_scale: bool) { @@ -1018,6 +1021,7 @@ impl State { return; } self.update_xwayland_wire_scale(); + self.trigger_cci(CCI_XWAYLAND); } pub fn next_serial(&self, client: Option<&Client>) -> u64 { diff --git a/src/xwayland.rs b/src/xwayland.rs index a5deb612..08a69d89 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -5,6 +5,7 @@ use { crate::{ client::{ClientCaps, ClientError}, compositor::DISPLAY, + control_center::CCI_XWAYLAND, forker::{ForkerError, ForkerProxy}, ifs::{ ipc::{DataOfferId, DataSourceId, IpcLocation, x_data_offer::XDataOffer}, @@ -117,9 +118,11 @@ pub async fn manage(state: Rc) { let display = Rc::new(format!(":{}", xsocket.id)); forker.setenv(DISPLAY.as_bytes(), display.as_bytes()); state.xwayland.display.set(Some(display.clone())); + state.trigger_cci(CCI_XWAYLAND); let _unsetenv = on_drop(|| { forker.unsetenv(DISPLAY.as_bytes()); state.xwayland.display.take(); + state.trigger_cci(CCI_XWAYLAND); }); log::info!("Allocated display :{} for Xwayland", xsocket.id); log::info!("Waiting for connection attempt"); @@ -212,9 +215,11 @@ async fn run( state.xwayland.queue.clear(); state.xwayland.pidfd.set(Some(pidfd.clone())); state.xwayland.client.set(Some(client.clone())); + state.trigger_cci(CCI_XWAYLAND); let _remove_pidfd = on_drop(|| { state.xwayland.pidfd.take(); state.xwayland.client.take(); + state.trigger_cci(CCI_XWAYLAND); }); { let shared = Rc::new(XwmShared::default());