1
0
Fork 0
forked from wry/wry

control-center: add xwayland pane

This commit is contained in:
Julian Orth 2026-02-23 20:38:22 +01:00
parent 6f103eac88
commit dee142b3bb
5 changed files with 110 additions and 4 deletions

View file

@ -2,7 +2,7 @@ use {
crate::{ crate::{
control_center::{ control_center::{
cc_color_management::ColorManagementPane, cc_compositor::CompositorPane, cc_color_management::ColorManagementPane, cc_compositor::CompositorPane,
cc_idle::IdlePane, cc_idle::IdlePane, cc_xwayland::XwaylandPane,
}, },
egui_adapter::egui_platform::{ egui_adapter::egui_platform::{
EggError, EggWindow, EggWindowOwner, EggError, EggWindow, EggWindowOwner,
@ -37,6 +37,7 @@ mod cc_color_management;
mod cc_compositor; mod cc_compositor;
mod cc_idle; mod cc_idle;
mod cc_sidebar; mod cc_sidebar;
mod cc_xwayland;
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum ControlCenterError { pub enum ControlCenterError {
@ -72,6 +73,7 @@ bitflags! {
CCI_COMPOSITOR, CCI_COMPOSITOR,
CCI_IDLE, CCI_IDLE,
CCI_COLOR_MANAGEMENT, CCI_COLOR_MANAGEMENT,
CCI_XWAYLAND,
} }
pub struct ControlCenter { pub struct ControlCenter {
@ -115,6 +117,7 @@ enum PaneType {
Compositor(CompositorPane), Compositor(CompositorPane),
Idle(IdlePane), Idle(IdlePane),
ColorManagement(ColorManagementPane), ColorManagement(ColorManagementPane),
Xwayland(XwaylandPane),
} }
struct CcBehavior<'a> { struct CcBehavior<'a> {
@ -136,14 +139,16 @@ impl Pane {
PaneType::Compositor(v) => v.title(res), PaneType::Compositor(v) => v.title(res),
PaneType::Idle(v) => v.title(res), PaneType::Idle(v) => v.title(res),
PaneType::ColorManagement(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 { match &mut self.ty {
PaneType::Compositor(p) => p.show(ui), PaneType::Compositor(p) => p.show(ui),
PaneType::Idle(p) => p.show(ui), PaneType::Idle(p) => p.show(ui),
PaneType::ColorManagement(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::Compositor(_) => CCI_COMPOSITOR,
PaneType::Idle(_) => CCI_IDLE, PaneType::Idle(_) => CCI_IDLE,
PaneType::ColorManagement(_) => CCI_COLOR_MANAGEMENT, PaneType::ColorManagement(_) => CCI_COLOR_MANAGEMENT,
PaneType::Xwayland(_) => CCI_XWAYLAND,
} }
} }
} }
@ -420,7 +426,6 @@ fn grid_label_ui<R>(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> Inn
ui.with_layout(Layout::right_to_left(Align::Center), add_contents) 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)) { fn tip(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui)) {
icon_label(ICON_INFO).ui(ui).on_hover_ui(add_contents); icon_label(ICON_INFO).ui(ui).on_hover_ui(add_contents);
} }

View file

@ -11,6 +11,7 @@ enum PaneName {
Compositor, Compositor,
Idle, Idle,
ColorManagement, ColorManagement,
Xwayland,
} }
impl PaneName { impl PaneName {
@ -19,6 +20,7 @@ impl PaneName {
PaneName::Compositor => "Compositor", PaneName::Compositor => "Compositor",
PaneName::Idle => "Idle", PaneName::Idle => "Idle",
PaneName::ColorManagement => "Color Management", PaneName::ColorManagement => "Color Management",
PaneName::Xwayland => "Xwayland",
} }
} }
} }
@ -50,6 +52,9 @@ impl ControlCenterInner {
PaneName::ColorManagement => { PaneName::ColorManagement => {
PaneType::ColorManagement(self.create_color_management_pane()) PaneType::ColorManagement(self.create_color_management_pane())
} }
PaneName::Xwayland => {
PaneType::Xwayland(self.create_xwayland_pane())
}
}; };
self.open(tree, ty); self.open(tree, ty);
ui.ctx().request_repaint(); ui.ctx().request_repaint();

View file

@ -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<State>,
}
impl ControlCenterInner {
pub fn create_xwayland_pane(self: &Rc<Self>) -> 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)));
}
}
}

View file

@ -16,7 +16,9 @@ use {
cmm::{cmm_description::ColorDescription, cmm_manager::ColorManager}, cmm::{cmm_description::ColorDescription, cmm_manager::ColorManager},
compositor::{LIBEI_SOCKET, LogLevel}, compositor::{LIBEI_SOCKET, LogLevel},
config::ConfigProxy, 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, copy_device::CopyDeviceRegistry,
cpu_worker::CpuWorker, cpu_worker::CpuWorker,
criteria::{clm::ClMatcherManager, tlm::TlMatcherManager}, criteria::{clm::ClMatcherManager, tlm::TlMatcherManager},
@ -1011,6 +1013,7 @@ impl State {
} else { } else {
self.stop_xwayland(); self.stop_xwayland();
} }
self.trigger_cci(CCI_XWAYLAND);
} }
pub fn set_xwayland_use_wire_scale(&self, use_wire_scale: bool) { pub fn set_xwayland_use_wire_scale(&self, use_wire_scale: bool) {
@ -1018,6 +1021,7 @@ impl State {
return; return;
} }
self.update_xwayland_wire_scale(); self.update_xwayland_wire_scale();
self.trigger_cci(CCI_XWAYLAND);
} }
pub fn next_serial(&self, client: Option<&Client>) -> u64 { pub fn next_serial(&self, client: Option<&Client>) -> u64 {

View file

@ -5,6 +5,7 @@ use {
crate::{ crate::{
client::{ClientCaps, ClientError}, client::{ClientCaps, ClientError},
compositor::DISPLAY, compositor::DISPLAY,
control_center::CCI_XWAYLAND,
forker::{ForkerError, ForkerProxy}, forker::{ForkerError, ForkerProxy},
ifs::{ ifs::{
ipc::{DataOfferId, DataSourceId, IpcLocation, x_data_offer::XDataOffer}, ipc::{DataOfferId, DataSourceId, IpcLocation, x_data_offer::XDataOffer},
@ -117,9 +118,11 @@ pub async fn manage(state: Rc<State>) {
let display = Rc::new(format!(":{}", xsocket.id)); let display = Rc::new(format!(":{}", xsocket.id));
forker.setenv(DISPLAY.as_bytes(), display.as_bytes()); forker.setenv(DISPLAY.as_bytes(), display.as_bytes());
state.xwayland.display.set(Some(display.clone())); state.xwayland.display.set(Some(display.clone()));
state.trigger_cci(CCI_XWAYLAND);
let _unsetenv = on_drop(|| { let _unsetenv = on_drop(|| {
forker.unsetenv(DISPLAY.as_bytes()); forker.unsetenv(DISPLAY.as_bytes());
state.xwayland.display.take(); state.xwayland.display.take();
state.trigger_cci(CCI_XWAYLAND);
}); });
log::info!("Allocated display :{} for Xwayland", xsocket.id); log::info!("Allocated display :{} for Xwayland", xsocket.id);
log::info!("Waiting for connection attempt"); log::info!("Waiting for connection attempt");
@ -212,9 +215,11 @@ async fn run(
state.xwayland.queue.clear(); state.xwayland.queue.clear();
state.xwayland.pidfd.set(Some(pidfd.clone())); state.xwayland.pidfd.set(Some(pidfd.clone()));
state.xwayland.client.set(Some(client.clone())); state.xwayland.client.set(Some(client.clone()));
state.trigger_cci(CCI_XWAYLAND);
let _remove_pidfd = on_drop(|| { let _remove_pidfd = on_drop(|| {
state.xwayland.pidfd.take(); state.xwayland.pidfd.take();
state.xwayland.client.take(); state.xwayland.client.take();
state.trigger_cci(CCI_XWAYLAND);
}); });
{ {
let shared = Rc::new(XwmShared::default()); let shared = Rc::new(XwmShared::default());