diff --git a/.gitignore b/.gitignore index f0c1769..c89325b 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ !*.glsl !*.py !*.css +!*.patch diff --git a/flake.lock b/flake.lock index f7131c1..f61a110 100755 --- a/flake.lock +++ b/flake.lock @@ -10,15 +10,14 @@ ], "nixpkgs": [ "nixpkgs" - ], - "systems": "systems" + ] }, "locked": { - "lastModified": 1770165109, - "narHash": "sha256-9VnK6Oqai65puVJ4WYtCTvlJeXxMzAp/69HhQuTdl/I=", + "lastModified": 1790530381, + "narHash": "sha256-/FjF9A8HVLVftjdmz1MPA2G0wSYWRaC628NTAsCqeyI=", "owner": "ryantm", "repo": "agenix", - "rev": "b027ee29d959fda4b60b57566d64c98a202e0feb", + "rev": "9353cb29366a8535ac9483065284785fd48012a2", "type": "github" }, "original": { @@ -36,11 +35,11 @@ ] }, "locked": { - "lastModified": 1789129569, - "narHash": "sha256-SBEBqMlw4wJsVdZuzmVQrPAZXQKRrN11sS3q00uwinc=", + "lastModified": 1790507313, + "narHash": "sha256-KNfYoS4N3LeQ+8zSBn/QuiR7nfsVqd8fqJL30shP9pI=", "owner": "chaotic-cx", "repo": "nyx", - "rev": "2ccb2d6c87e6f973a13a4277410e87f535eeeb30", + "rev": "a6a3dcf6a7b1d3cc454f919ed75d60d21179e67e", "type": "github" }, "original": { @@ -119,7 +118,7 @@ }, "flake-utils": { "inputs": { - "systems": "systems_2" + "systems": "systems" }, "locked": { "lastModified": 1731533236, @@ -143,11 +142,11 @@ ] }, "locked": { - "lastModified": 1788949964, - "narHash": "sha256-qsXxjP/dC1Q0o6LsRoWvkwqwEhMAkwlI8zT3RIRr+y8=", + "lastModified": 1790333762, + "narHash": "sha256-KHGGibq49aAfnESf/xdS+uWgZlk3zNlOkASmxJbgxx4=", "owner": "elwrcl", "repo": "heliux", - "rev": "a7598f53dff9d3a892adb417af66771ce9068f65", + "rev": "177ae55b79c30adf18203793beae2c47334b5960", "type": "github" }, "original": { @@ -164,11 +163,11 @@ ] }, "locked": { - "lastModified": 1789071706, - "narHash": "sha256-S+oyh2YSP9V5bSjWbbK0N7GxdxxMwgBfUOtUdVcAWc4=", + "lastModified": 1790365885, + "narHash": "sha256-iQ0ebhiVo64NktTnwft9hNxlFu4j5cvljVxyJaEVIP4=", "owner": "nix-community", "repo": "home-manager", - "rev": "150f9ce4ddd41544ea42eb9d6043a75bd615fa24", + "rev": "7b4c5ec4bedaf1e062bbc1bcaeddbc6bd242aa1b", "type": "github" }, "original": { @@ -184,11 +183,11 @@ ] }, "locked": { - "lastModified": 1789136855, - "narHash": "sha256-82D7ADWE8erhfALj3dqxAOeQaoa+Bwlsdcp9v2m/ExM=", + "lastModified": 1790365885, + "narHash": "sha256-iQ0ebhiVo64NktTnwft9hNxlFu4j5cvljVxyJaEVIP4=", "owner": "nix-community", "repo": "home-manager", - "rev": "d0fd95e15e0c3baf565a9fd4bc352321e2564ea3", + "rev": "7b4c5ec4bedaf1e062bbc1bcaeddbc6bd242aa1b", "type": "github" }, "original": { @@ -205,11 +204,11 @@ ] }, "locked": { - "lastModified": 1788642154, - "narHash": "sha256-sPpQFVaFTDqO/4vvCAhuAhqTgqN/ygu+9eJcs5eB0js=", + "lastModified": 1790215976, + "narHash": "sha256-VkR1rMGyjU9jtw8ISFx5uqR9e+LEaOlhHwrseFkiIVk=", "owner": "nix-community", "repo": "home-manager", - "rev": "fd0956c99c41ae3c13a73a638f1f7e963aebc4ab", + "rev": "a6631107a83ceab5872f298a2ea710859c80c4cb", "type": "github" }, "original": { @@ -227,11 +226,11 @@ ] }, "locked": { - "lastModified": 1788229478, - "narHash": "sha256-G0F2rFORVcFkEvVdE/qWQTnYekIc77YP5/vRF9nZlxU=", + "lastModified": 1789430117, + "narHash": "sha256-t+U1mijItLJ64xZOifpfQ17kpAL73nU7pDI48ScacVc=", "owner": "nix-community", "repo": "home-manager", - "rev": "1dc2d1f720ab17fc7981e087346bf54b26d284b1", + "rev": "cda90fd8838825c689fde9d3f3b4e937937790df", "type": "github" }, "original": { @@ -320,11 +319,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1784118096, - "narHash": "sha256-iW7BzSkv27lII5FvrB6kPRO0CzOZj8utOIpvcFWjHgw=", + "lastModified": 1789478421, + "narHash": "sha256-38OKZCaTtcvj3LIicEy/1lbxEKS0ZL7wCkJxzxClQw0=", "owner": "thiagokokada", "repo": "nix-alien", - "rev": "df422df4324674417f91dee49bae401eef87c6ed", + "rev": "0cca578c63e9055d2514e456d78d6f42bdd1eca2", "type": "github" }, "original": { @@ -384,11 +383,11 @@ ] }, "locked": { - "lastModified": 1783864904, - "narHash": "sha256-BQxN5UMg9FOevAsgBRwPxfxlh51Puj+dNn/8Dsi3sPM=", + "lastModified": 1789286446, + "narHash": "sha256-xVstyCdg0DpzjyrEacm72VJAIP9DMhfWJdPDzhWxHuo=", "owner": "nix-community", "repo": "nix-index-database", - "rev": "1111b9bc836afb7e31a7014e8d1272de9b1c917d", + "rev": "a74e173407559083a412ea89a1cc58ea389c3b65", "type": "github" }, "original": { @@ -404,15 +403,16 @@ "nixpkgs": [ "nixpkgs" ], + "nixpkgs-ci": "nixpkgs-ci", "nixpkgs-nixcord": "nixpkgs-nixcord", "treefmt-nix": "treefmt-nix" }, "locked": { - "lastModified": 1789115297, - "narHash": "sha256-Oa+iNm4QccTh+ytbs2N9EHTNZ5nDI5Baf7fIfxbLH50=", + "lastModified": 1790530226, + "narHash": "sha256-V9q2ADcBLo16KW/HK90Qfu2KoEEAZJ1IFSjUd+jWJ9I=", "owner": "FlameFlag", "repo": "nixcord", - "rev": "d19bed0c9ad52678cdd1e0fd6fa2f21d6abab374", + "rev": "163de914ce8542c1061ad392bccbb750a7b0077f", "type": "github" }, "original": { @@ -423,11 +423,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1784007870, - "narHash": "sha256-djcLt/JJphyNt4eDY9XTly+/WbCK5lqWq9lSgCmJkkQ=", + "lastModified": 1789286504, + "narHash": "sha256-eiEK7cKZORNEvX0GeF3RtNEF/JXhgf2RqSp3230q13E=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "18b9261cb3294b6d2a06d03f96872827b8fe2698", + "rev": "ef34387ddd751e1ab8857adf4676492d32eb24ec", "type": "github" }, "original": { @@ -437,6 +437,22 @@ "type": "github" } }, + "nixpkgs-ci": { + "locked": { + "lastModified": 1790390834, + "narHash": "sha256-StRyS/lFHtIw/UgJk+xo+HA66mtoWeDUbkJdvf2Byn4=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "d54020a6ac3211e9f4201631bdf67678818c0cdf", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs-lib": { "locked": { "lastModified": 1774748309, @@ -454,11 +470,11 @@ }, "nixpkgs-nixcord": { "locked": { - "lastModified": 1788921488, - "narHash": "sha256-8+xWRxEkD6l217cIUdRxfeUGS9lQX0hVtUuNVsBaDzk=", + "lastModified": 1790340521, + "narHash": "sha256-cRrWlIjVRJ6BHDr9caUdXsZ3owzWIKYHpNmGPHBInIU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "6aefcda9401be8acc2b74244fb3b37520ea1f0a8", + "rev": "f5c082a40f7571c266e74e80ae2e68aadd8a9fc7", "type": "github" }, "original": { @@ -486,11 +502,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1789006805, - "narHash": "sha256-xB8mKMOx1IA9vTDNLmJZ6n4wCMq/cuWBBOzGCRnqxrU=", + "lastModified": 1790463110, + "narHash": "sha256-hKlVl12B1dF0Q5vd9dY3lIJM5mFGWYSlXwSLAqHZ1+s=", "owner": "nixos", "repo": "nixpkgs", - "rev": "8ce4ef6cb6f871616146b9fe26d2a5ae594e94fe", + "rev": "e158d9ed9b51c98974c5e66e1ba1c9e0255fecaa", "type": "github" }, "original": { @@ -502,11 +518,11 @@ }, "nixpkgs_3": { "locked": { - "lastModified": 1788614874, - "narHash": "sha256-PRK1fSEiAcO29DkUoOlsVLGYRK3h1YKVr+qQJB0VcYQ=", - "rev": "c043004d1c6985732bcc1cbc5a9c9aecbbb4e0f0", + "lastModified": 1790323409, + "narHash": "sha256-m4DGo58Fza5ImOegtWOeE18nhpSO1IGzsVA6Lpsb4zw=", + "rev": "e94cb152ed51bd6e24eb4a41f1460252beb52cd2", "type": "tarball", - "url": "https://releases.nixos.org/nixos/unstable/nixos-26.11pre1068089.c043004d1c69/nixexprs.tar.zst" + "url": "https://releases.nixos.org/nixos/unstable/nixos-26.11pre1079315.e94cb152ed51/nixexprs.tar.zst" }, "original": { "type": "tarball", @@ -550,15 +566,16 @@ "nixpkgs": "nixpkgs_3" }, "locked": { - "lastModified": 1789140608, - "narHash": "sha256-ws04aukS6BLbcdl+WnLlOCziS7dmgrgZDFNI7eUUWnw=", + "lastModified": 1790523212, + "narHash": "sha256-Z8SuI0YgAZaF0sumKPBF85PxPtTjpo8jvtphbgoITv8=", "owner": "noctalia-dev", "repo": "noctalia-shell", - "rev": "9bf7e1d89be4b156c08faed9a2b2206ac9ec7441", + "rev": "08c30392b1350b4f3d3fb77e23732560559f1638", "type": "github" }, "original": { "owner": "noctalia-dev", + "ref": "cachix", "repo": "noctalia-shell", "type": "github" } @@ -636,21 +653,6 @@ "type": "github" } }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, "theme-assets": { "inputs": { "nixpkgs": [ @@ -748,11 +750,11 @@ ] }, "locked": { - "lastModified": 1789133576, - "narHash": "sha256-wJJGLMs78PDdbdXd0f50vq7s+xpKOuAUMZPsyDgXyew=", + "lastModified": 1790402100, + "narHash": "sha256-f/tmhFat/UHra627JbVg6calQ3/ZkEydQZJR71BBX0M=", "owner": "0xc000022070", "repo": "zen-browser-flake", - "rev": "975870eb40a0cb6dc1baf6d63a704f9cfd5d4f27", + "rev": "b851b56b787a61b900751aca2f17e4a5ca6c394a", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index e4c9ff6..a5db3d4 100644 --- a/flake.nix +++ b/flake.nix @@ -69,7 +69,7 @@ url = "github:thiagokokada/nix-alien"; }; noctalia = { - url = "github:noctalia-dev/noctalia-shell"; + url = "github:noctalia-dev/noctalia-shell/cachix"; }; nyi = { url = "github:elwrcl/nyi"; diff --git a/hosts/copland.ulu.nix b/hosts/copland.ulu.nix index a997324..415c225 100644 --- a/hosts/copland.ulu.nix +++ b/hosts/copland.ulu.nix @@ -15,7 +15,10 @@ in inputs.chaotic.nixosModules.default { - nixpkgs.config.allowUnfree = true; + nixpkgs.config = { + allowUnfree = true; + permittedInsecurePackages = [ "radicle-node-1.10.3" ]; + }; home-manager = { useGlobalPkgs = true; useUserPackages = true; @@ -78,6 +81,7 @@ in config.flake.homeModules.home-mimeapps config.flake.homeModules.home-file-manager config.flake.homeModules.home-file-manager-actions + config.flake.homeModules.home-gdrive-rclone config.flake.homeModules.home-easyeffects config.flake.homeModules.home-nyi config.flake.homeModules.home-discord diff --git a/modules/home/home-gdrive-rclone.ulu.nix b/modules/home/home-gdrive-rclone.ulu.nix new file mode 100644 index 0000000..54dd4a1 --- /dev/null +++ b/modules/home/home-gdrive-rclone.ulu.nix @@ -0,0 +1,24 @@ +{ ... }: +{ + flake.homeModules.home-gdrive-rclone = + { pkgs, ... }: + { + home.packages = [ pkgs.rclone ]; + + systemd.user.services.rclone-gdrive = { + Unit = { + Description = "Google Drive (rclone mount)"; + After = [ "network-online.target" ]; + }; + Service = { + Type = "notify"; + ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p %h/GoogleDrive"; + ExecStart = "${pkgs.rclone}/bin/rclone mount gdrive: %h/GoogleDrive --vfs-cache-mode writes"; + ExecStop = "${pkgs.fuse}/bin/fusermount -u %h/GoogleDrive"; + Restart = "on-failure"; + RestartSec = 10; + }; + Install.WantedBy = [ "default.target" ]; + }; + }; +} diff --git a/modules/nixos/common/common-boot.ulu.nix b/modules/nixos/common/common-boot.ulu.nix index fed9149..85fe1af 100644 --- a/modules/nixos/common/common-boot.ulu.nix +++ b/modules/nixos/common/common-boot.ulu.nix @@ -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; }; }; }; diff --git a/modules/nixos/common/common-nix.ulu.nix b/modules/nixos/common/common-nix.ulu.nix index 01c1703..0192127 100644 --- a/modules/nixos/common/common-nix.ulu.nix +++ b/modules/nixos/common/common-nix.ulu.nix @@ -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; diff --git a/modules/nixos/common/common-packages.ulu.nix b/modules/nixos/common/common-packages.ulu.nix index 1105192..254cda5 100644 --- a/modules/nixos/common/common-packages.ulu.nix +++ b/modules/nixos/common/common-packages.ulu.nix @@ -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 diff --git a/modules/nixos/desktop/desktop-niri.ulu.nix b/modules/nixos/desktop/desktop-niri.ulu.nix index f566a65..209c886 100644 --- a/modules/nixos/desktop/desktop-niri.ulu.nix +++ b/modules/nixos/desktop/desktop-niri.ulu.nix @@ -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; }) ]; diff --git a/modules/nixos/desktop/xwayland-satellite-no-focus-override-redirect.patch b/modules/nixos/desktop/xwayland-satellite-no-focus-override-redirect.patch new file mode 100644 index 0000000..5460222 --- /dev/null +++ b/modules/nixos/desktop/xwayland-satellite-no-focus-override-redirect.patch @@ -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); ++ 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, + title: Option, +@@ -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, + is_popup: bool, ++ has_take_focus: bool, + } + + #[derive(Copy, Clone, Default)] +@@ -422,6 +424,9 @@ impl XConnection for NoConnection { + fn focus_window(&mut self, _: x::Window, _: Option) { + 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 ServerState { + 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 InnerServerState { + 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 InnerServerState { + + 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, ++ send_take_focus_window: Option, + windows: HashMap, + 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 { + (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, +- 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, diff --git a/modules/nixos/hw/hw-udev.ulu.nix b/modules/nixos/hw/hw-udev.ulu.nix index c5a2e46..be676e1 100644 --- a/modules/nixos/hw/hw-udev.ulu.nix +++ b/modules/nixos/hw/hw-udev.ulu.nix @@ -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" + ''; }; }