xwm: Set xcursor variables in Xresources db

This commit is contained in:
Victoria Brekenfeld 2026-01-09 18:34:51 +01:00 committed by Victoria Brekenfeld
parent 612ff2f523
commit e6a3a3a9c9
4 changed files with 97 additions and 30 deletions

View file

@ -180,8 +180,10 @@ impl Config {
let cosmic_comp_config = let cosmic_comp_config =
CosmicCompConfig::get_entry(&config).unwrap_or_else(|(errs, c)| { CosmicCompConfig::get_entry(&config).unwrap_or_else(|(errs, c)| {
for err in errs { if cfg!(debug_assertions) {
error!(?err, ""); for err in errs {
warn!(?err, "");
}
} }
c c
}); });
@ -189,6 +191,11 @@ impl Config {
// Listen for updates to the toolkit config // Listen for updates to the toolkit config
if let Ok(tk_config) = cosmic_config::Config::new("com.system76.CosmicTk", 1) { if let Ok(tk_config) = cosmic_config::Config::new("com.system76.CosmicTk", 1) {
fn handle_new_toolkit_config(config: CosmicTk, state: &mut State) { fn handle_new_toolkit_config(config: CosmicTk, state: &mut State) {
if cosmic::icon_theme::default() != config.icon_theme {
cosmic::icon_theme::set_default(config.icon_theme.clone());
state.common.update_xwayland_settings();
}
let mut workspace_guard = state.common.workspace_state.update(); let mut workspace_guard = state.common.workspace_state.update();
state.common.shell.write().update_toolkit( state.common.shell.write().update_toolkit(
config, config,
@ -197,19 +204,32 @@ impl Config {
); );
} }
if let Ok(config) = CosmicTk::get_entry(&tk_config) { let config = CosmicTk::get_entry(&tk_config).unwrap_or_else(|(errs, c)| {
let _ = loop_handle.insert_idle(move |state| { if cfg!(debug_assertions) {
handle_new_toolkit_config(config, state); for err in errs {
}); warn!(?err, "");
} }
}
c
});
let _ = loop_handle.insert_idle(move |state| {
handle_new_toolkit_config(config, state);
});
match cosmic_config::calloop::ConfigWatchSource::new(&tk_config) { match cosmic_config::calloop::ConfigWatchSource::new(&tk_config) {
Ok(source) => { Ok(source) => {
if let Err(err) = if let Err(err) =
loop_handle.insert_source(source, |(config, _keys), (), state| { loop_handle.insert_source(source, |(config, _keys), (), state| {
if let Ok(config) = CosmicTk::get_entry(&config) { let config =
handle_new_toolkit_config(config, state); CosmicTk::get_entry(&config).unwrap_or_else(|(errs, c)| {
} if cfg!(debug_assertions) {
for err in errs {
warn!(?err, "");
}
}
c
});
handle_new_toolkit_config(config, state);
}) })
{ {
warn!(?err, "Failed to watch com.system76.CosmicTk config"); warn!(?err, "Failed to watch com.system76.CosmicTk config");
@ -879,7 +899,7 @@ fn config_changed(config: cosmic_config::Config, keys: Vec<String>, state: &mut
let new = get_config::<XwaylandDescaling>(&config, "descale_xwayland"); let new = get_config::<XwaylandDescaling>(&config, "descale_xwayland");
if new != state.common.config.cosmic_conf.descale_xwayland { if new != state.common.config.cosmic_conf.descale_xwayland {
state.common.config.cosmic_conf.descale_xwayland = new; state.common.config.cosmic_conf.descale_xwayland = new;
state.common.update_xwayland_scale(); state.common.update_xwayland_settings();
} }
} }
"xwayland_eavesdropping" => { "xwayland_eavesdropping" => {

View file

@ -4711,10 +4711,6 @@ impl Shell {
xdg_activation_state: &XdgActivationState, xdg_activation_state: &XdgActivationState,
workspace_state: &mut WorkspaceUpdateGuard<'_, State>, workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
) { ) {
if cosmic::icon_theme::default() != toolkit.icon_theme {
cosmic::icon_theme::set_default(toolkit.icon_theme.clone());
}
let mut container = cosmic::config::COSMIC_TK.write().unwrap(); let mut container = cosmic::config::COSMIC_TK.write().unwrap();
if *container != toolkit { if *container != toolkit {
*container = toolkit; *container = toolkit;

View file

@ -579,7 +579,7 @@ impl LockedBackend<'_> {
loop_handle.insert_idle(move |state| { loop_handle.insert_idle(move |state| {
state.update_inhibitor_locks(); state.update_inhibitor_locks();
state.common.update_xwayland_scale(); state.common.update_xwayland_settings();
state.common.update_xwayland_primary_output(); state.common.update_xwayland_primary_output();
}); });

View file

@ -1,4 +1,10 @@
use std::{ffi::OsString, os::unix::io::OwnedFd, process::Stdio}; use std::{
ffi::OsString,
io::Write,
os::unix::io::OwnedFd,
process::Stdio,
sync::mpsc::{self, Receiver, Sender},
};
use crate::{ use crate::{
backend::render::cursor::{Cursor, load_cursor_env, load_cursor_theme}, backend::render::cursor::{Cursor, load_cursor_env, load_cursor_theme},
@ -64,6 +70,39 @@ pub struct XWaylandState {
pub last_modifier_state: Option<ModifiersState>, pub last_modifier_state: Option<ModifiersState>,
pub clipboard_selection_dirty: Option<Vec<String>>, pub clipboard_selection_dirty: Option<Vec<String>>,
pub primary_selection_dirty: Option<Vec<String>>, pub primary_selection_dirty: Option<Vec<String>>,
pub xrdb_thread: Sender<(String, u32)>,
}
fn xrdb_thread(rx: Receiver<(String, u32)>, display: u32) {
while let Ok((cursor_theme, cursor_size)) = rx.recv() {
if let Ok(mut child) = std::process::Command::new("xrdb")
.arg("-merge")
.env("DISPLAY", format!(":{}", display))
.stdin(Stdio::piped())
.spawn()
{
let resources = format!(
"Xcursor.theme: {}\nXcursor.size: {}\n",
cursor_theme, cursor_size,
);
if let Some(mut stdin) = child.stdin.take() {
if let Err(err) = stdin.write_all(resources.as_bytes()) {
warn!("Failed to update xresources: {}", err);
}
}
match child.wait() {
Ok(code) if code.success() => {}
Ok(code) => {
warn!("xrdb failed with code: {}", code);
}
Err(err) => {
warn!("Failed to wait for child: {}", err);
}
}
} else {
warn!("`xrdb` not found, cannot update Xresources.");
}
}
} }
impl State { impl State {
@ -101,6 +140,9 @@ impl State {
x11_socket, x11_socket,
display_number, display_number,
} => { } => {
let (tx, rx) = mpsc::channel();
std::thread::spawn(move || xrdb_thread(rx, display_number));
data.common.xwayland_state = Some(XWaylandState { data.common.xwayland_state = Some(XWaylandState {
client: client.clone(), client: client.clone(),
xwm: None, xwm: None,
@ -110,6 +152,7 @@ impl State {
last_modifier_state: None, last_modifier_state: None,
clipboard_selection_dirty: None, clipboard_selection_dirty: None,
primary_selection_dirty: None, primary_selection_dirty: None,
xrdb_thread: tx,
}); });
let wm = match X11Wm::start_wm( let wm = match X11Wm::start_wm(
@ -130,7 +173,7 @@ impl State {
xwayland_state.reload_cursor(1.); xwayland_state.reload_cursor(1.);
data.notify_ready(); data.notify_ready();
data.common.update_xwayland_scale(); data.common.update_xwayland_settings();
data.common.update_xwayland_primary_output(); data.common.update_xwayland_primary_output();
} }
XWaylandEvent::Error => { XWaylandEvent::Error => {
@ -594,7 +637,7 @@ impl Common {
} }
} }
pub fn update_xwayland_scale(&mut self) { pub fn update_xwayland_settings(&mut self) {
let new_scale = match self.config.cosmic_conf.descale_xwayland { let new_scale = match self.config.cosmic_conf.descale_xwayland {
XwaylandDescaling::Disabled => 1., XwaylandDescaling::Disabled => 1.,
XwaylandDescaling::Enabled => { XwaylandDescaling::Enabled => {
@ -619,10 +662,11 @@ impl Common {
val val
} }
}; };
let (_, cursor_size) = load_cursor_env();
// compare with current scale // compare with current scale
if Some(new_scale) != self.xwayland_scale { if let Some(xwayland) = self.xwayland_state.as_mut() {
if let Some(xwayland) = self.xwayland_state.as_mut() { let geometries = if Some(new_scale) != self.xwayland_scale {
// backup geometries // backup geometries
let geometries = self let geometries = self
.shell .shell
@ -632,8 +676,6 @@ impl Common {
.filter_map(|s| s.0.x11_surface().map(|x| (x.clone(), x.geometry()))) .filter_map(|s| s.0.x11_surface().map(|x| (x.clone(), x.geometry())))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let (_, cursor_size) = load_cursor_env();
// update xorg dpi // update xorg dpi
if let Some(xwm) = xwayland.xwm.as_mut() { if let Some(xwm) = xwayland.xwm.as_mut() {
let base = 96. * 1024.; let base = 96. * 1024.;
@ -644,10 +686,6 @@ impl Common {
if let Err(err) = xwm.set_xsettings( if let Err(err) = xwm.set_xsettings(
[ [
("Xft/DPI".into(), (dpi.round() as i32).into()), ("Xft/DPI".into(), (dpi.round() as i32).into()),
(
"Xcursor/size".into(),
((new_scale * cursor_size as f64).round() as i32).into(),
),
( (
"Gdk/UnscaledDPI".into(), "Gdk/UnscaledDPI".into(),
(unscaled_dpi.round() as i32).into(), (unscaled_dpi.round() as i32).into(),
@ -667,9 +705,22 @@ impl Common {
} }
} }
// update cursor Some(geometries)
xwayland.reload_cursor(new_scale); } else {
None
};
if let Err(_) = xwayland.xrdb_thread.send((
cosmic::icon_theme::default(),
(new_scale * cursor_size as f64).round() as u32,
)) {
warn!("xrdb thread died");
}
// update cursor
xwayland.reload_cursor(new_scale);
if let Some(geometries) = geometries {
// update client scale // update client scale
xwayland xwayland
.client .client
@ -1192,7 +1243,7 @@ impl XwmHandler for State {
output_name.as_deref().is_some_and(|o| o == output.name()); output_name.as_deref().is_some_and(|o| o == output.name());
} }
self.common.output_configuration_state.update(); self.common.output_configuration_state.update();
self.common.update_xwayland_scale(); self.common.update_xwayland_settings();
self.common self.common
.config .config
.write_outputs(self.common.output_configuration_state.outputs()); .write_outputs(self.common.output_configuration_state.outputs());