the big one

This commit is contained in:
elwrcl committed 2026-09-28 17:45:49 +03:00
1 parent cf181e7ed0
commit cfa4aa4969
11 files changed
+570 -80

No files matched your search

+3 -1
View File
@@ -10,13 +10,15 @@
boot.loader = {
timeout = 5;
efi = {
canTouchEfiVariables = false;
canTouchEfiVariables = true;
efiSysMountPoint = "/boot";
};
systemd-boot = {
enable = true;
configurationLimit = 5;
consoleMode = "max";
editor = false;
};
};
};
+17
View File
@@ -16,6 +16,9 @@
max-jobs = 2;
cores = 2;
min-free = 5368709120; # 5 GiB
max-free = 21474836480; # 20 GiB
substituters = [
"https://cache.nixos.org"
"https://nix-community.cachix.org"
@@ -36,6 +39,20 @@
];
};
nix.gc = {
automatic = true;
dates = "Mon 03:00";
options = "--delete-older-than 14d";
persistent = true;
randomizedDelaySec = "45min";
};
nix.optimise = {
automatic = true;
dates = [ "monthly" ];
persistent = true;
randomizedDelaySec = "45min";
};
documentation = {
man.enable = true;
doc.enable = false;
+1 -1
View File
@@ -18,6 +18,7 @@
onlyoffice-desktopeditors
whatsapp-electron
telegram-desktop
element-desktop
prismlauncher
moonlight-qt
qbittorrent
@@ -64,7 +65,6 @@
libimobiledevice
android-tools
uxplay-fixed
simple-mtpfs
usbmuxd
libmtp
scrcpy
+8 -8
View File
@@ -6,15 +6,15 @@
programs.niri.enable = true;
nixpkgs.overlays = [
(final: prev: {
xwayland-satellite = prev.xwayland-satellite.overrideAttrs (old: {
patches = (old.patches or [ ]) ++ [
(final.fetchpatch {
name = "no-focus-for-override-redirect-popups.patch";
url = "https://github.com/Supreeeme/xwayland-satellite/pull/494.diff";
hash = "sha256-X7v6rAyTGE0e1UIbhL7XoYx5puZ0C9b+YAwIgT4Gyzk=";
xwayland-satellite =
if prev.lib.versionOlder prev.xwayland-satellite.version "0.8.3" then
prev.xwayland-satellite.overrideAttrs (old: {
patches = (old.patches or [ ]) ++ [
./xwayland-satellite-no-focus-override-redirect.patch
];
})
];
});
else
prev.xwayland-satellite;
})
];
@@ -0,0 +1,437 @@
diff --git a/src/lib.rs b/src/lib.rs
index 5c861ea0..d3bf003a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -20,6 +20,7 @@ pub trait XConnection: Sized + 'static {
fn set_window_dims(&mut self, window: x::Window, dims: PendingSurfaceState) -> bool;
fn set_fullscreen(&mut self, window: x::Window, fullscreen: bool);
fn focus_window(&mut self, window: x::Window, output_name: Option<String>);
+ fn send_take_focus(&mut self, window: x::Window);
fn close_window(&mut self, window: x::Window);
fn unmap_window(&mut self, window: x::Window);
fn raise_to_top(&mut self, window: x::Window);
diff --git a/src/server/event.rs b/src/server/event.rs
index c75b9f19..4fcf9230 100644
--- a/src/server/event.rs
+++ b/src/server/event.rs
@@ -438,6 +438,7 @@ impl SurfaceEvents {
window,
output_name: None,
is_popup: true,
+ has_take_focus: window_data.attrs.has_take_focus,
});
}
}
@@ -864,10 +865,13 @@ impl Event for client::wl_keyboard::Event {
serial,
));
let output_name = get_output_name(output, &state.world);
+ let window_data = data.get::<&WindowData>();
+ let has_take_focus = window_data.as_ref().is_some_and(|d| d.attrs.has_take_focus);
state.to_focus = Some(FocusData {
window: *window,
output_name,
is_popup: false,
+ has_take_focus,
});
keyboard.enter(serial, surface, keys);
}
diff --git a/src/server/mod.rs b/src/server/mod.rs
index 379df771..a436331b 100644
--- a/src/server/mod.rs
+++ b/src/server/mod.rs
@@ -101,9 +101,10 @@ where
#[derive(Default, Debug)]
struct WindowAttributes {
- acquire_input_via_wm: bool,
+ accepts_input: bool,
has_take_focus: bool,
role: WindowRole,
+ override_redirect: bool,
dims: WindowDims,
size_hints: Option<WmNormalHints>,
title: Option<WmName>,
@@ -114,9 +115,8 @@ struct WindowAttributes {
}
impl WindowAttributes {
- /// AKA "Passive" input model
fn require_wm_focus(&self) -> bool {
- self.acquire_input_via_wm && !self.has_take_focus
+ !self.override_redirect && (self.has_take_focus || self.accepts_input)
}
}
@@ -140,6 +140,7 @@ impl WindowData {
mapped: false,
attrs: WindowAttributes {
role: WindowRole::new_basic(override_redirect),
+ override_redirect,
dims,
..Default::default()
},
@@ -398,6 +399,7 @@ struct FocusData {
window: x::Window,
output_name: Option<String>,
is_popup: bool,
+ has_take_focus: bool,
}
#[derive(Copy, Clone, Default)]
@@ -422,6 +424,9 @@ impl<S: X11Selection> XConnection for NoConnection<S> {
fn focus_window(&mut self, _: x::Window, _: Option<String>) {
debug!("could not focus window without XWayland initialized");
}
+ fn send_take_focus(&mut self, _: x::Window) {
+ debug!("could not send take focus without XWayland initialized");
+ }
fn close_window(&mut self, _: x::Window) {
debug!("could not close window without XWayland initialized");
}
@@ -742,15 +747,20 @@ impl<C: XConnection> ServerState<C> {
window,
output_name,
is_popup,
+ has_take_focus,
}) = self.to_focus.take()
{
debug!(
- "focusing {} {window:?}",
+ "focusing (take_focus={has_take_focus:?}) {} {window:?}",
if is_popup { "popup" } else { "window" }
);
- self.connection.focus_window(window, output_name);
- if !is_popup {
- self.last_focused_toplevel = Some(window);
+ if has_take_focus {
+ self.connection.send_take_focus(window);
+ } else {
+ self.connection.focus_window(window, output_name);
+ if !is_popup {
+ self.last_focused_toplevel = Some(window);
+ }
}
} else if self.unfocus {
self.connection.focus_window(x::WINDOW_NONE, None);
@@ -888,9 +898,24 @@ impl<S: X11Selection + 'static> InnerServerState<S> {
self.windows.insert(window, id);
}
+ pub fn set_override_redirect(&mut self, window: x::Window, override_redirect: bool) {
+ let Some(id) = self.windows.get(&window).copied() else {
+ debug!(
+ "not setting override_redirect {override_redirect} for unknown window {window:?}"
+ );
+ return;
+ };
+
+ self.world
+ .get::<&mut WindowData>(id)
+ .unwrap()
+ .attrs
+ .override_redirect = override_redirect;
+ }
+
pub fn set_window_role(&mut self, window: x::Window, role: WindowRole) {
let Some(id) = self.windows.get(&window).copied() else {
- debug!("not setting popup for unknown window {window:?}");
+ debug!("not setting role {role:?} for unknown window {window:?}");
return;
};
@@ -969,7 +994,7 @@ impl<S: X11Selection + 'static> InnerServerState<S> {
let attrs = &mut self.world.get::<&mut WindowData>(id).unwrap().attrs;
attrs.group = hints.window_group;
- attrs.acquire_input_via_wm = hints.acquire_input_via_wm;
+ attrs.accepts_input = hints.accepts_input;
}
pub fn set_take_focus(&mut self, window: x::Window, has_take_focus: bool) {
diff --git a/src/server/tests.rs b/src/server/tests.rs
index 0651bb2a..4e1735ee 100644
--- a/src/server/tests.rs
+++ b/src/server/tests.rs
@@ -160,6 +160,7 @@ struct WindowData {
#[derive(Default)]
struct FakeXConnection {
focused_window: Option<Window>,
+ send_take_focus_window: Option<Window>,
windows: HashMap<Window, WindowData>,
set_window_dims_counter: usize,
}
@@ -234,6 +235,15 @@ impl super::XConnection for FakeXConnection {
self.focused_window = window.into();
}
+ #[track_caller]
+ fn send_take_focus(&mut self, window: x::Window) {
+ assert!(
+ self.windows.contains_key(&window),
+ "Unknown window: {window:?}"
+ );
+ self.send_take_focus_window = window.into();
+ }
+
fn raise_to_top(&mut self, window: Window) {
assert!(
self.windows.contains_key(&window),
@@ -858,6 +868,55 @@ impl TestFixture<FakeXConnection> {
(surface, popup_id)
}
+ #[track_caller]
+ fn setup_popup_focus_case(
+ &mut self,
+ comp: &Compositor,
+ override_redirect: bool,
+ accepts_input: bool,
+ take_focus: bool,
+ ) -> (Window, Window) {
+ let win_toplevel = Window::new(1);
+ self.create_toplevel(comp, win_toplevel);
+
+ let win_popup = Window::new(2);
+ let (buffer, surface) = comp.create_surface();
+ let data = WindowData {
+ mapped: true,
+ dims: WindowDims {
+ x: 10,
+ y: 20,
+ width: 100,
+ height: 50,
+ },
+ fullscreen: false,
+ };
+ self.new_window(win_popup, override_redirect, data);
+ if !override_redirect {
+ self.satellite
+ .set_window_role(win_popup, crate::xstate::WindowRole::Popup);
+ }
+
+ self.satellite.set_win_hints(
+ win_popup,
+ super::WmHints {
+ window_group: None,
+ accepts_input,
+ },
+ );
+ if take_focus {
+ self.satellite.set_take_focus(win_popup, true);
+ }
+ self.map_window(comp, win_popup, &surface.obj, &buffer);
+ self.run();
+
+ let popup_id = self.check_new_surface();
+ self.testwl.configure_popup(popup_id);
+ self.run();
+
+ (win_toplevel, win_popup)
+ }
+
#[track_caller]
fn assert_window_dimensions(
&self,
@@ -1342,7 +1401,7 @@ fn window_group_properties() {
win,
super::WmHints {
window_group: Some(prop_win),
- acquire_input_via_wm: false,
+ accepts_input: false,
},
);
f.satellite.map_window(win);
@@ -1647,103 +1706,51 @@ fn override_redirect_choose_hover_window() {
}
#[test]
-fn popup_no_focus_without_input_hint() {
- let (mut f, comp) = TestFixture::new_with_compositor();
+fn popup_override_redirect_never_focused_nor_offered() {
+ for accepts_input in [true, false] {
+ for take_focus in [false, true] {
+ let (mut f, comp) = TestFixture::new_with_compositor();
+
+ let (win_toplevel, _) =
+ f.setup_popup_focus_case(&comp, true, accepts_input, take_focus);
+ assert_eq!(
+ f.connection().focused_window,
+ Some(win_toplevel),
+ "accepts_input={accepts_input:?} take_focus={take_focus}"
+ );
+ assert_eq!(
+ f.connection().send_take_focus_window,
+ None,
+ "accepts_input={accepts_input:?} take_focus={take_focus}"
+ );
+ }
+ }
+}
- let win_toplevel = Window::new(1);
- let (_, toplevel_id) = f.create_toplevel(&comp, win_toplevel);
+#[test]
+fn popup_send_take_focus_when_advertised() {
+ for accepts_input in [true, false] {
+ let (mut f, comp) = TestFixture::new_with_compositor();
- // A popup without acquire_input_via_wm should not receive focus.
- let win_popup = Window::new(2);
- f.create_popup(
- &comp,
- PopupBuilder::new(win_popup, win_toplevel, toplevel_id),
- );
- assert_eq!(f.connection().focused_window, Some(win_toplevel));
+ let (win_toplevel, win_popup) = f.setup_popup_focus_case(&comp, false, accepts_input, true);
+ assert_eq!(f.connection().focused_window, Some(win_toplevel));
+ assert_eq!(f.connection().send_take_focus_window, Some(win_popup));
+ }
}
#[test]
fn popup_focus_on_map_with_input_hint() {
let (mut f, comp) = TestFixture::new_with_compositor();
- let win_toplevel = Window::new(1);
- let (_, toplevel_id) = f.create_toplevel(&comp, win_toplevel);
-
- // A popup with acquire_input_via_wm should receive X11 keyboard focus
- // at map time, matching what a real X11 WM does for windows with
- // WM_HINTS input=True.
- let win_popup = Window::new(2);
- let (buffer, surface) = comp.create_surface();
- let dims = WindowDims {
- x: 10,
- y: 20,
- width: 100,
- height: 50,
- };
- let data = WindowData {
- mapped: true,
- dims,
- fullscreen: false,
- };
- f.new_window(win_popup, true, data);
- f.satellite.set_win_hints(
- win_popup,
- super::WmHints {
- window_group: None,
- acquire_input_via_wm: true,
- },
- );
- f.map_window(&comp, win_popup, &surface.obj, &buffer);
- f.run();
-
- let popup_id = f.check_new_surface();
- assert_ne!(popup_id, toplevel_id);
-
- f.testwl.configure_popup(popup_id);
- f.run();
-
- // Focus should have been given directly to the popup at map time.
+ let (_, win_popup) = f.setup_popup_focus_case(&comp, false, true, false);
assert_eq!(f.connection().focused_window, Some(win_popup));
}
#[test]
-fn popup_no_focus_input_hint_wm_take_focus() {
+fn popup_no_focus_without_input_hint() {
let (mut f, comp) = TestFixture::new_with_compositor();
- let win_toplevel = Window::new(1);
- let (_, toplevel_id) = f.create_toplevel(&comp, win_toplevel);
-
- let win_popup = Window::new(2);
- let (buffer, surface) = comp.create_surface();
- let dims = WindowDims {
- x: 10,
- y: 20,
- width: 100,
- height: 50,
- };
- let data = WindowData {
- mapped: true,
- dims,
- fullscreen: false,
- };
- f.new_window(win_popup, true, data);
- f.satellite.set_win_hints(
- win_popup,
- super::WmHints {
- window_group: None,
- acquire_input_via_wm: true,
- },
- );
- f.satellite.set_take_focus(win_popup, true);
- f.map_window(&comp, win_popup, &surface.obj, &buffer);
- f.run();
-
- let popup_id = f.check_new_surface();
- assert_ne!(popup_id, toplevel_id);
-
- f.testwl.configure_popup(popup_id);
- f.run();
-
+ let (win_toplevel, _) = f.setup_popup_focus_case(&comp, false, false, false);
assert_eq!(f.connection().focused_window, Some(win_toplevel));
}
diff --git a/src/xstate/mod.rs b/src/xstate/mod.rs
index efcb13de..7a7e02d2 100644
--- a/src/xstate/mod.rs
+++ b/src/xstate/mod.rs
@@ -653,6 +653,7 @@ impl XState {
let transient_for = self.get_transient_for(window)?;
let override_redirect = self.get_override_redirect(window)?;
+ server_state.set_override_redirect(window, override_redirect);
let window_types = self
.get_net_wm_window_types(window)?
@@ -1010,7 +1011,7 @@ impl From<&[u32]> for WmNormalHints {
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
pub struct WmHints {
pub window_group: Option<x::Window>,
- pub acquire_input_via_wm: bool,
+ pub accepts_input: bool,
}
impl From<&[u32]> for WmHints {
@@ -1022,8 +1023,11 @@ impl From<&[u32]> for WmHints {
let window = x::Window::new(value[8]);
ret.window_group = Some(window);
}
- if flags.contains(WmHintsFlags::Input) {
- ret.acquire_input_via_wm = value[1] == 1;
+
+ // if input hint isn't present, set it to true anyways in case the client is misbehaving
+ ret.accepts_input = true;
+ if flags.contains(WmHintsFlags::Input) && value[1] == 0 {
+ ret.accepts_input = false;
}
ret
@@ -1429,6 +1433,22 @@ impl XConnection for RealConnection {
}
}
+ fn send_take_focus(&mut self, window: x::Window) {
+ let resource_id = self.atoms.wm_take_focus.resource_id();
+ let data = [resource_id, x::CURRENT_TIME, 0, 0, 0];
+ let event = &x::ClientMessageEvent::new(
+ window,
+ self.atoms.wm_protocols,
+ x::ClientMessageData::Data32(data),
+ );
+ unwrap_or_skip_bad_window_ret!(self.connection.send_and_check_request(&x::SendEvent {
+ destination: x::SendEventDest::Window(window),
+ propagate: false,
+ event_mask: x::EventMask::empty(),
+ event,
+ }));
+ }
+
fn close_window(&mut self, window: x::Window) {
let cookie = self.connection.send_request(&x::GetProperty {
window,
+4 -1
View File
@@ -5,7 +5,6 @@
{
programs.gpu-screen-recorder.enable = true;
services.udisks2.enable = true;
services.gnome.sushi.enable = true;
services.gvfs.enable = true;
security.polkit.extraConfig = ''
polkit.addRule(function(action, subject) {
@@ -15,5 +14,9 @@
}
});
'';
services.udev.extraRules = ''
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="3434", ATTRS{idProduct}=="d030", MODE="0660", TAG+="uaccess", TAG+="udev-acl"
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="3434", ATTRS{idProduct}=="0b11", MODE="0660", TAG+="uaccess", TAG+="udev-acl"
'';
};
}