diff options
Diffstat (limited to 'accounts')
100 files changed, 8577 insertions, 3290 deletions
diff --git a/accounts/gkleen@eostre.nix b/accounts/gkleen@eostre.nix index 72818d44..28daf3fd 100644 --- a/accounts/gkleen@eostre.nix +++ b/accounts/gkleen@eostre.nix | |||
| @@ -1,16 +1,16 @@ | |||
| 1 | { flake, userName, pkgs, ... }: | 1 | { flake, userName, pkgs, ... }: |
| 2 | { | 2 | { |
| 3 | imports = with flake.nixosModules.userProfiles.${userName}; [ | 3 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 4 | zsh utils tmux | 4 | utils |
| 5 | ]; | 5 | ]; |
| 6 | 6 | ||
| 7 | config = { | 7 | config = { |
| 8 | home-manager.users.${userName} = { | 8 | home-manager.users.${userName} = { |
| 9 | home.stateVersion = "20.09"; | 9 | home.stateVersion = "20.09"; |
| 10 | 10 | ||
| 11 | nixpkgs.config = { | 11 | # nixpkgs.config = { |
| 12 | allowUnfree = true; | 12 | # allowUnfree = true; |
| 13 | }; | 13 | # }; |
| 14 | 14 | ||
| 15 | home.packages = with pkgs; [ | 15 | home.packages = with pkgs; [ |
| 16 | thunderbird libreoffice element-desktop keepassxc vlc | 16 | thunderbird libreoffice element-desktop keepassxc vlc |
diff --git a/accounts/gkleen@installer.nix b/accounts/gkleen@installer.nix index c7a418f8..5fe1db38 100644 --- a/accounts/gkleen@installer.nix +++ b/accounts/gkleen@installer.nix | |||
| @@ -1,7 +1,11 @@ | |||
| 1 | { userName, ... }: | 1 | { flake, userName, ... }: |
| 2 | 2 | ||
| 3 | { | 3 | { |
| 4 | home-manager.users.${userName} = { config, ... } : { | 4 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 5 | zsh tmux | ||
| 6 | ]; | ||
| 7 | |||
| 8 | config.home-manager.users.${userName} = { config, ... } : { | ||
| 5 | home.stateVersion = config.home.version.release; | 9 | home.stateVersion = config.home.version.release; |
| 6 | }; | 10 | }; |
| 7 | } | 11 | } |
diff --git a/accounts/gkleen@sif/default.nix b/accounts/gkleen@sif/default.nix index 00707e87..36b722e4 100644 --- a/accounts/gkleen@sif/default.nix +++ b/accounts/gkleen@sif/default.nix | |||
| @@ -4,33 +4,6 @@ with lib; | |||
| 4 | 4 | ||
| 5 | let | 5 | let |
| 6 | cfg = config.home-manager.users.${userName}; | 6 | cfg = config.home-manager.users.${userName}; |
| 7 | emacsScratch = pkgs.stdenv.mkDerivation (sources.emacs-scratch_el // rec { | ||
| 8 | phases = [ "installPhase" ]; | ||
| 9 | |||
| 10 | installPhase = '' | ||
| 11 | mkdir -p $out/share/emacs/site-lisp | ||
| 12 | cp $src/scratch.el $out/share/emacs/site-lisp/default.el | ||
| 13 | ''; | ||
| 14 | }); | ||
| 15 | muteScript = pkgs.stdenv.mkDerivation { | ||
| 16 | name = "mute"; | ||
| 17 | src = ./scripts/mute.zsh; | ||
| 18 | |||
| 19 | buildInputs = with pkgs; [ makeWrapper ]; | ||
| 20 | |||
| 21 | phases = [ "installPhase" ]; | ||
| 22 | |||
| 23 | installPhase = '' | ||
| 24 | mkdir -p $out/bin | ||
| 25 | install -m 0755 $src $out/bin/mute | ||
| 26 | wrapProgram $out/bin/mute \ | ||
| 27 | --prefix PATH : ${pkgs.zsh}/bin \ | ||
| 28 | --prefix PATH : ${pkgs.findutils}/bin \ | ||
| 29 | --prefix PATH : ${pkgs.util-linux}/bin \ | ||
| 30 | --prefix PATH : ${pkgs.coreutils}/bin \ | ||
| 31 | --prefix PATH : ${pkgs.pulseaudio}/bin | ||
| 32 | ''; | ||
| 33 | }; | ||
| 34 | wrapElectron = { package, bin ? package.meta.mainProgram or package.pname or (pkgs.lib.strings.nameFromURL package.name "-"), outBin ? bin, sandbox ? true }: pkgs.symlinkJoin { | 7 | wrapElectron = { package, bin ? package.meta.mainProgram or package.pname or (pkgs.lib.strings.nameFromURL package.name "-"), outBin ? bin, sandbox ? true }: pkgs.symlinkJoin { |
| 35 | name = "${package.name}-wrapped"; | 8 | name = "${package.name}-wrapped"; |
| 36 | buildInputs = with pkgs; [ makeWrapper ]; | 9 | buildInputs = with pkgs; [ makeWrapper ]; |
| @@ -47,10 +20,6 @@ let | |||
| 47 | ''; | 20 | ''; |
| 48 | }; | 21 | }; |
| 49 | 22 | ||
| 50 | wrappedChrome = wrapElectron { package = pkgs.google-chrome; outBin = "google-chrome"; }; | ||
| 51 | wrappedZulip = wrapElectron { package = pkgs.zulip; bin = "zulip"; outBin = "zulip"; }; | ||
| 52 | wrappedElementDesktop = wrapElectron { package = pkgs.element-desktop; bin = "element-desktop"; }; | ||
| 53 | wrappedRocketChatDesktop = wrapElectron { package = pkgs.rocketchat-desktop; bin = "rocketchat-desktop"; outBin = "rocketchat"; }; | ||
| 54 | wrappedYTMDesktop = wrapElectron { package = pkgs.ytmdesktop; sandbox = false; }; | 23 | wrappedYTMDesktop = wrapElectron { package = pkgs.ytmdesktop; sandbox = false; }; |
| 55 | 24 | ||
| 56 | wrappedKeepassxc = pkgs.symlinkJoin { | 25 | wrappedKeepassxc = pkgs.symlinkJoin { |
| @@ -63,7 +32,7 @@ let | |||
| 63 | text = '' | 32 | text = '' |
| 64 | [D-BUS Service] | 33 | [D-BUS Service] |
| 65 | Name=org.keepassxc.KeePassXC.MainWindow | 34 | Name=org.keepassxc.KeePassXC.MainWindow |
| 66 | Exec=${pkgs.coreutils}/bin/false | 35 | Exec=${lib.getExe' pkgs.coreutils "false"} |
| 67 | SystemdService=keepassxc.service | 36 | SystemdService=keepassxc.service |
| 68 | ''; | 37 | ''; |
| 69 | }) | 38 | }) |
| @@ -73,35 +42,48 @@ let | |||
| 73 | text = '' | 42 | text = '' |
| 74 | [D-BUS Service] | 43 | [D-BUS Service] |
| 75 | Name=org.freedesktop.secrets | 44 | Name=org.freedesktop.secrets |
| 76 | Exec=${pkgs.coreutils}/bin/false | 45 | Exec=${lib.getExe' pkgs.coreutils "false"} |
| 77 | SystemdService=keepassxc.service | 46 | SystemdService=keepassxc.service |
| 78 | ''; | 47 | ''; |
| 79 | }) | 48 | }) |
| 80 | ]; | 49 | ]; |
| 81 | }; | 50 | }; |
| 82 | 51 | ||
| 83 | lockCommand = "${config.systemd.package}/bin/systemctl --user start gtklock.service"; | 52 | # lockCommand = "${lib.getExe' config.systemd.package "systemctl"} --user start gtklock.service"; |
| 53 | lockCommand = "${lib.getExe' cfg.programs.quickshell.package "qs"} ipc call Lockscreen setLocked true"; | ||
| 54 | |||
| 55 | editor = pkgs.symlinkJoin { | ||
| 56 | inherit (cfg.services.emacs.package) name; | ||
| 57 | buildInputs = with pkgs; [ makeWrapper ]; | ||
| 58 | paths = [ cfg.services.emacs.package ]; | ||
| 59 | postBuild = '' | ||
| 60 | wrapProgram $out/bin/emacsclient \ | ||
| 61 | --inherit-argv0 \ | ||
| 62 | --add-flags ${lib.escapeShellArg (lib.escapeShellArgs cfg.services.emacs.client.arguments)} | ||
| 63 | ''; | ||
| 64 | }; | ||
| 84 | in { | 65 | in { |
| 85 | imports = with flake.nixosModules.userProfiles.${userName}; [ | 66 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 86 | mpv yt-dlp (args: import ./xcompose.nix (inputs // args)) | 67 | zsh tmux mpv yt-dlp (args: import ./xcompose.nix (inputs // args)) |
| 87 | ]; | 68 | ]; |
| 88 | 69 | ||
| 89 | config = { | 70 | config = { |
| 90 | services.displayManager.defaultSession = "Hyprland"; # "none+xmonad"; | ||
| 91 | |||
| 92 | home-manager.users.${userName} = { | 71 | home-manager.users.${userName} = { |
| 93 | imports = [ | 72 | imports = [ |
| 94 | ./libvirt | 73 | ./libvirt |
| 95 | flakeInputs.nix-index-database.hmModules.nix-index | 74 | ./niri |
| 75 | ./shell | ||
| 76 | ./synadm | ||
| 77 | flakeInputs.nix-index-database.homeModules.nix-index | ||
| 96 | flakeInputs.impermanence.nixosModules.home-manager.impermanence | 78 | flakeInputs.impermanence.nixosModules.home-manager.impermanence |
| 97 | ]; | 79 | ]; |
| 98 | 80 | ||
| 99 | home.stateVersion = "20.09"; | 81 | home.stateVersion = "20.09"; |
| 100 | 82 | ||
| 101 | nixpkgs.config = { | 83 | # nixpkgs.config = { |
| 102 | allowUnfree = true; | 84 | # allowUnfree = true; |
| 103 | zathura.useMupdf = false; | 85 | # zathura.useMupdf = false; |
| 104 | }; | 86 | # }; |
| 105 | 87 | ||
| 106 | nix.registry = { | 88 | nix.registry = { |
| 107 | "flk" = { | 89 | "flk" = { |
| @@ -111,14 +93,14 @@ in { | |||
| 111 | }; | 93 | }; |
| 112 | to = { | 94 | to = { |
| 113 | type = "git"; | 95 | type = "git"; |
| 114 | url = "file:///home/gkleen/config/nixos-flakes"; | 96 | url = "file:///home/gkleen/projects/machines"; |
| 115 | }; | 97 | }; |
| 116 | }; | 98 | }; |
| 117 | }; | 99 | }; |
| 118 | 100 | ||
| 119 | programs = { | 101 | programs = { |
| 120 | ssh = { | 102 | ssh = { |
| 121 | matchBlocks = import ./ssh-hosts.nix { inherit pkgs; }; # customUtils.nixImport { dir = ./ssh-hosts; }; | 103 | matchBlocks = import ./ssh-hosts.nix inputs; # customUtils.nixImport { dir = ./ssh-hosts; }; |
| 122 | extraConfig = '' | 104 | extraConfig = '' |
| 123 | Match host uniworx3.ifi.lmu.de,uniworx4.ifi.lmu.de,uniworx5.ifi.lmu.de,uni2workgw.ifi.lmu.de,blackbeard.tcs.ifi.lmu.de,gitlab2.rz.ifi.lmu.de,oregon.tcs.ifi.lmu.de !exec "nc -z -w 1 %h %p &>/dev/null" | 105 | Match host uniworx3.ifi.lmu.de,uniworx4.ifi.lmu.de,uniworx5.ifi.lmu.de,uni2workgw.ifi.lmu.de,blackbeard.tcs.ifi.lmu.de,gitlab2.rz.ifi.lmu.de,oregon.tcs.ifi.lmu.de !exec "nc -z -w 1 %h %p &>/dev/null" |
| 124 | ProxyJump remote.cip.ifi.lmu.de | 106 | ProxyJump remote.cip.ifi.lmu.de |
| @@ -136,8 +118,8 @@ in { | |||
| 136 | ''} | 118 | ''} |
| 137 | 119 | ||
| 138 | Match host *.mathinst.loc,*.math.lmu.de !host ssh.math.lmu.de !exec "nc -z -w 1 %h %p &>/dev/null" | 120 | Match host *.mathinst.loc,*.math.lmu.de !host ssh.math.lmu.de !exec "nc -z -w 1 %h %p &>/dev/null" |
| 139 | # ProxyCommand ${pkgs.socat}/bin/socat - SOCKS4A:127.0.0.1:%h:%p,socksport=8118 | 121 | ProxyCommand ${lib.getExe pkgs.socat} - SOCKS4A:127.0.0.1:%h:%p,socksport=8118 |
| 140 | ProxyJump ssh.math.lmu.de | 122 | # ProxyJump ssh.math.lmu.de |
| 141 | 123 | ||
| 142 | Match host *.cipmath.loc !host cip04.cipmath.loc,mgmt-cls01.cipmath.loc !exec "nc -z -w 1 %h %p &>/dev/null" | 124 | Match host *.cipmath.loc !host cip04.cipmath.loc,mgmt-cls01.cipmath.loc !exec "nc -z -w 1 %h %p &>/dev/null" |
| 143 | ProxyJump cip04 | 125 | ProxyJump cip04 |
| @@ -154,22 +136,31 @@ in { | |||
| 154 | 136 | ||
| 155 | emacs = { | 137 | emacs = { |
| 156 | enable = true; | 138 | enable = true; |
| 157 | package = pkgs.emacs29-pgtk; | 139 | package = pkgs.emacs-pgtk; |
| 158 | extraPackages = epkgs: with epkgs; [ | 140 | extraPackages = epkgs: with epkgs; [ |
| 159 | evil evil-dvorak undo-tree magit haskell-tng-mode nix-mode | 141 | evil evil-dvorak undo-tree magit haskell-tng-mode nix-mode |
| 160 | yaml-mode json-mode shakespeare-mode smart-mode-line | 142 | yaml-mode json-mode shakespeare-mode smart-mode-line |
| 161 | highlight-parentheses highlight-symbol ag sass-mode lua-mode | 143 | highlight-parentheses highlight-symbol ag sass-mode |
| 162 | fira-code-mode use-package wanderlust # notmuch | 144 | lua-mode fira-code-mode use-package wanderlust # notmuch |
| 163 | use-package-ensure-system-package git-gutter emacsScratch | 145 | git-gutter scratch edit-server mediawiki editorconfig |
| 164 | edit-server mediawiki editorconfig typescript-mode | 146 | typescript-mode markdown-mode nftables-mode rustic |
| 165 | markdown-mode nftables-mode rustic lsp-mode lsp-ui | 147 | lsp-mode lsp-ui direnv company projectile |
| 166 | direnv company projectile tomorrow-night-paradise-theme | 148 | tomorrow-night-paradise-theme |
| 167 | treesit-grammars.with-all-grammars magit-delta scad-mode | 149 | treesit-grammars.with-all-grammars magit-delta scad-mode |
| 168 | ]; | 150 | ]; |
| 169 | overrides = self: super: { | 151 | overrides = self: super: { |
| 170 | tomorrow-night-paradise-theme = super.trivialBuild { | 152 | tomorrow-night-paradise-theme = super.trivialBuild { |
| 171 | inherit (sources.tomorrow-night-paradise-theme) pname version src; | 153 | inherit (sources.tomorrow-night-paradise-theme) pname version src; |
| 172 | }; | 154 | }; |
| 155 | scratch = pkgs.stdenv.mkDerivation { | ||
| 156 | inherit (sources.emacs-scratch_el) pname version src; | ||
| 157 | |||
| 158 | phases = [ "unpackPhase" "installPhase" ]; | ||
| 159 | |||
| 160 | installPhase = '' | ||
| 161 | install -Dt $out/share/emacs/site-lisp scratch.el | ||
| 162 | ''; | ||
| 163 | }; | ||
| 173 | }; | 164 | }; |
| 174 | }; | 165 | }; |
| 175 | firefox = { | 166 | firefox = { |
| @@ -183,8 +174,14 @@ in { | |||
| 183 | }; | 174 | }; |
| 184 | }; | 175 | }; |
| 185 | }; | 176 | }; |
| 177 | chromium.enable = true; | ||
| 186 | 178 | ||
| 187 | zathura.enable = true; | 179 | zathura = { |
| 180 | enable = true; | ||
| 181 | options = { | ||
| 182 | scroll-page-aware = true; | ||
| 183 | }; | ||
| 184 | }; | ||
| 188 | imv.enable = true; | 185 | imv.enable = true; |
| 189 | 186 | ||
| 190 | mpv.config = { | 187 | mpv.config = { |
| @@ -193,13 +190,93 @@ in { | |||
| 193 | gpu-api = "vulkan"; | 190 | gpu-api = "vulkan"; |
| 194 | }; | 191 | }; |
| 195 | 192 | ||
| 196 | zsh.initExtra = '' | 193 | zsh.initContent = let |
| 197 | source ${./zshrc} | 194 | zshrc = pkgs.resholve.mkDerivation { |
| 195 | pname = "zshrc"; | ||
| 196 | version = "0.0.0"; | ||
| 197 | |||
| 198 | src = ./zshrc; | ||
| 199 | |||
| 200 | dontUnpack = true; | ||
| 201 | dontConfigure = true; | ||
| 202 | dontBuild = true; | ||
| 203 | |||
| 204 | installPhase = '' | ||
| 205 | mkdir -p $out/share | ||
| 206 | install "$src" $out/share/zshrc | ||
| 207 | ''; | ||
| 208 | |||
| 209 | solutions = { | ||
| 210 | default = { | ||
| 211 | scripts = [ "share/zshrc" ]; | ||
| 212 | interpreter = "none"; | ||
| 213 | inputs = with pkgs; [ | ||
| 214 | coreutils | ||
| 215 | rpm | ||
| 216 | binutils | ||
| 217 | squashfsTools | ||
| 218 | unzip | ||
| 219 | cfg.programs.git.package | ||
| 220 | magickWrapped | ||
| 221 | curl | ||
| 222 | file | ||
| 223 | gnutar | ||
| 224 | cpio | ||
| 225 | magic-wormhole | ||
| 226 | cfg.programs.zsh.package | ||
| 227 | fuse | ||
| 228 | util-linux | ||
| 229 | findutils | ||
| 230 | qrencode | ||
| 231 | tty-clock | ||
| 232 | cfg.programs.jq.package | ||
| 233 | eza | ||
| 234 | less | ||
| 235 | config.systemd.package | ||
| 236 | config.programs.ssh.package | ||
| 237 | gnused | ||
| 238 | miniserve | ||
| 239 | p7zip | ||
| 240 | ]; | ||
| 241 | execer = with pkgs; [ | ||
| 242 | "cannot:${lib.getExe' rpm "rpm2cpio"}" | ||
| 243 | "cannot:${lib.getExe' squashfsTools "unsquashfs"}" | ||
| 244 | "cannot:${lib.getExe' unzip "unzip"}" | ||
| 245 | "cannot:${lib.getExe cfg.programs.git.package}" | ||
| 246 | "cannot:${lib.getExe cpio}" | ||
| 247 | "cannot:${lib.getExe' magic-wormhole "wormhole"}" | ||
| 248 | "cannot:${lib.getExe' fuse "fusermount"}" | ||
| 249 | "cannot:${lib.getExe less}" | ||
| 250 | "cannot:${lib.getExe' config.systemd.package "systemctl"}" | ||
| 251 | "cannot:${lib.getExe config.programs.ssh.package}" | ||
| 252 | "cannot:${lib.getExe' p7zip "7z"}" | ||
| 253 | ]; | ||
| 254 | wrapper = with pkgs; [ | ||
| 255 | "${lib.getExe' magickWrapped "magick"}:${lib.getExe' imagemagick "magick"}" | ||
| 256 | ]; | ||
| 257 | fake = { | ||
| 258 | builtin = ["print"]; | ||
| 259 | external = ["sudo" "umount"]; | ||
| 260 | }; | ||
| 261 | }; | ||
| 262 | }; | ||
| 263 | }; | ||
| 264 | magickWrapped = pkgs.symlinkJoin { | ||
| 265 | inherit (pkgs.imagemagick) name; | ||
| 266 | paths = [ pkgs.imagemagick ]; | ||
| 267 | |||
| 268 | buildInputs = with pkgs; [ makeWrapper ]; | ||
| 269 | postBuild = '' | ||
| 270 | wrapProgram $out/bin/magick \ | ||
| 271 | --prefix PATH : ${lib.makeBinPath (with pkgs; [ ghostscript ])} | ||
| 272 | ''; | ||
| 273 | }; | ||
| 274 | in '' | ||
| 275 | source ${zshrc}/share/zshrc | ||
| 198 | ''; | 276 | ''; |
| 199 | zsh.dirHashes = let | 277 | zsh.dirHashes = let |
| 200 | flakeHashes = mapAttrs' (n: v: nameValuePair (inputNames.${n} or n) (toString v)) flakeInputs; | 278 | flakeHashes = mapAttrs' (n: v: nameValuePair (inputNames.${n} or n) (toString v)) flakeInputs; |
| 201 | inputNames = { | 279 | inputNames = { |
| 202 | "nixpkgs" = "nixos"; | ||
| 203 | }; | 280 | }; |
| 204 | in flakeHashes // { | 281 | in flakeHashes // { |
| 205 | u2w = "$HOME/projects/uni2work"; | 282 | u2w = "$HOME/projects/uni2work"; |
| @@ -211,6 +288,16 @@ in { | |||
| 211 | pro = "$HOME/projects/pro"; | 288 | pro = "$HOME/projects/pro"; |
| 212 | media = "$HOME/media"; | 289 | media = "$HOME/media"; |
| 213 | }; | 290 | }; |
| 291 | jq.colors = { | ||
| 292 | arrays = "1;37"; | ||
| 293 | "false" = "0;37"; | ||
| 294 | "null" = "2;37"; | ||
| 295 | numbers = "0;37"; | ||
| 296 | objectKeys = "1;34"; | ||
| 297 | objects = "1;37"; | ||
| 298 | strings = "0;32"; | ||
| 299 | "true" = "0;37"; | ||
| 300 | }; | ||
| 214 | 301 | ||
| 215 | obs-studio = { | 302 | obs-studio = { |
| 216 | enable = true; | 303 | enable = true; |
| @@ -220,7 +307,7 @@ in { | |||
| 220 | gh = { | 307 | gh = { |
| 221 | enable = true; | 308 | enable = true; |
| 222 | settings = { | 309 | settings = { |
| 223 | editor = "${config.home-manager.users.${userName}.programs.emacs.package}/bin/emacsclient"; | 310 | editor = lib.getExe' editor "emacsclient"; |
| 224 | gitProtocol = "ssh"; | 311 | gitProtocol = "ssh"; |
| 225 | }; | 312 | }; |
| 226 | }; | 313 | }; |
| @@ -246,309 +333,23 @@ in { | |||
| 246 | # notify_on_cmd_finish = "invisible 120"; | 333 | # notify_on_cmd_finish = "invisible 120"; |
| 247 | }; | 334 | }; |
| 248 | keybindings = { | 335 | keybindings = { |
| 249 | "kitty_mod+n" = "detach_window"; | 336 | "kitty_mod+n" = "new_os_window_with_cwd"; |
| 250 | "kitty_mod+m" = "detach_window ask"; | 337 | "kitty_mod+m" = "detach_window ask"; |
| 251 | }; | 338 | "kitty_mod+enter" = "new_window_with_cwd"; |
| 252 | }; | 339 | "kitty_mod+t" = "new_tab_with_cwd"; |
| 253 | waybar = { | ||
| 254 | enable = true; | ||
| 255 | systemd = { | ||
| 256 | enable = true; | ||
| 257 | target = "hyprland-session.target"; | ||
| 258 | }; | ||
| 259 | settings = let | ||
| 260 | windowRewrites = { | ||
| 261 | "(.*) — Mozilla Firefox" = "$1"; | ||
| 262 | "(.*) - Mozilla Thunderbird" = "$1"; | ||
| 263 | "(.*) - mpv" = "$1"; | ||
| 264 | }; | ||
| 265 | iconSize = 11; | ||
| 266 | in [ | ||
| 267 | { | ||
| 268 | layer = "top"; | ||
| 269 | position = "top"; | ||
| 270 | height = 14; | ||
| 271 | output = [ "eDP-1" "DP-2" "DP-3" ]; | ||
| 272 | modules-left = [ "hyprland/workspaces" ]; | ||
| 273 | modules-center = [ "hyprland/window" ]; | ||
| 274 | modules-right = [ "custom/worktime" "custom/worktime-today" "custom/weather" "custom/keymap" "privacy" "tray" "wireplumber" "backlight" "battery" "idle_inhibitor" "clock" ]; | ||
| 275 | |||
| 276 | "custom/weather" = { | ||
| 277 | format = "{}"; | ||
| 278 | tooltip = true; | ||
| 279 | interval = 3600; | ||
| 280 | exec = "${lib.getExe pkgs.wttrbar} --hide-conditions --custom-indicator \"<span font=\\\"Symbols Nerd Font Mono\\\" size=\\\"120%\\\">{ICON}</span> {FeelsLikeC}°\""; | ||
| 281 | return-type = "json"; | ||
| 282 | }; | ||
| 283 | "custom/keymap" = { | ||
| 284 | format = "{}"; | ||
| 285 | tooltip = true; | ||
| 286 | return-type = "json"; | ||
| 287 | exec = pkgs.writers.writePython3 "keymap" {} '' | ||
| 288 | import os | ||
| 289 | import socket | ||
| 290 | import re | ||
| 291 | import subprocess | ||
| 292 | import json | ||
| 293 | |||
| 294 | |||
| 295 | def output(keymap): | ||
| 296 | short = keymap | ||
| 297 | if keymap == "English (programmer Dvorak)": | ||
| 298 | short = "dvp" | ||
| 299 | elif keymap == "English (US)": | ||
| 300 | short = "<span color=\"#ffffff\">us</span>" | ||
| 301 | print(json.dumps({'text': short, 'tooltip': keymap}, separators=(',', ':')), flush=True) # noqa: E501 | ||
| 302 | |||
| 303 | |||
| 304 | r = subprocess.run(["hyprctl", "devices", "-j"], check=True, stdout=subprocess.PIPE, text=True) # noqa: E501 | ||
| 305 | for keyboard in json.loads(r.stdout)['keyboards']: | ||
| 306 | if keyboard['name'] != "at-translated-set-2-keyboard": | ||
| 307 | continue | ||
| 308 | output(keyboard['active_keymap']) | ||
| 309 | |||
| 310 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | ||
| 311 | sock.connect(os.environ["XDG_RUNTIME_DIR"] + "/hypr/" + os.environ["HYPRLAND_INSTANCE_SIGNATURE"] + "/.socket2.sock") # noqa: E501 | ||
| 312 | expected = re.compile(r'^activelayout>>at-translated-set-2-keyboard,(?P<keymap>.+)$') # noqa: E501 | ||
| 313 | for line in sock.makefile(buffering=1, encoding='utf-8'): | ||
| 314 | if match := expected.match(line): | ||
| 315 | output(match.group("keymap")) | ||
| 316 | ''; | ||
| 317 | on-click = "hyprctl switchxkblayout at-translated-set-2-keyboard next"; | ||
| 318 | }; | ||
| 319 | "custom/worktime" = { | ||
| 320 | interval = 60; | ||
| 321 | exec = getExe pkgs.worktime; | ||
| 322 | tooltip = false; | ||
| 323 | }; | ||
| 324 | "custom/worktime-today" = { | ||
| 325 | interval = 60; | ||
| 326 | exec = "${getExe pkgs.worktime} today"; | ||
| 327 | tooltip = false; | ||
| 328 | }; | ||
| 329 | "hyprland/workspaces" = { | ||
| 330 | all-outputs = true; | ||
| 331 | }; | ||
| 332 | "hyprland/window" = { | ||
| 333 | separate-outputs = true; | ||
| 334 | icon = true; | ||
| 335 | icon-size = 14; | ||
| 336 | rewrite = windowRewrites; | ||
| 337 | }; | ||
| 338 | clock = { | ||
| 339 | interval = 1; | ||
| 340 | # timezone = "Europe/Berlin"; | ||
| 341 | format = "W{:%V-%u %F %H:%M:%S%Ez}"; | ||
| 342 | tooltip-format = "<tt><small>{calendar}</small></tt>"; | ||
| 343 | calendar = { | ||
| 344 | mode = "year"; | ||
| 345 | mode-mon-col = 3; | ||
| 346 | weeks-pos = "left"; | ||
| 347 | on-scroll = 1; | ||
| 348 | format = { | ||
| 349 | months = "<span color='#ffead3'><b>{}</b></span>"; | ||
| 350 | days = "{}"; | ||
| 351 | weeks = "<span color='#99ffdd'><b>{}</b></span>"; | ||
| 352 | weekdays = "<span color='#ffcc66'><b>{}</b></span>"; | ||
| 353 | today = "<span color='#ff6699'><b>{}</b></span>"; | ||
| 354 | }; | ||
| 355 | }; | ||
| 356 | }; | ||
| 357 | battery = { | ||
| 358 | format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>"; | ||
| 359 | icon-size = iconSize - 2; | ||
| 360 | states = { warning = 30; critical = 15; }; | ||
| 361 | format-icons = ["󰂎" "󰁺" "󰁻" "󰁼" "󰁽" "󰁾" "󰁿" "󰂀" "󰂁" "󰂂" "󰁹" ]; | ||
| 362 | format-charging = "󰂄"; | ||
| 363 | format-plugged = "󰚥"; | ||
| 364 | tooltip-format = "{capacity}% {timeTo}"; | ||
| 365 | interval = 20; | ||
| 366 | }; | ||
| 367 | tray = { | ||
| 368 | icon-size = 16; | ||
| 369 | # show-passive-items = true; | ||
| 370 | spacing = 1; | ||
| 371 | }; | ||
| 372 | privacy = { | ||
| 373 | icon-spacing = 7; | ||
| 374 | icon-size = iconSize; | ||
| 375 | modules = [ | ||
| 376 | { type = "screenshare"; } | ||
| 377 | { type = "audio-in"; } | ||
| 378 | ]; | ||
| 379 | }; | ||
| 380 | idle_inhibitor = { | ||
| 381 | format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>"; | ||
| 382 | icon-size = iconSize; | ||
| 383 | format-icons = { activated = "󰈈"; deactivated = "󰈉"; }; | ||
| 384 | timeout = 120; | ||
| 385 | }; | ||
| 386 | backlight = { | ||
| 387 | format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>"; | ||
| 388 | icon-size = iconSize; | ||
| 389 | tooltip-format = "{percent}%"; | ||
| 390 | format-icons = ["󰃚" "󰃛" "󰃜" "󰃝" "󰃞" "󰃟" "󰃠"]; | ||
| 391 | on-scroll-up = "lightctl -d -e4 -n1 up"; | ||
| 392 | on-scroll-down = "lightctl -d -e4 -n1 down"; | ||
| 393 | }; | ||
| 394 | wireplumber = { | ||
| 395 | format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>"; | ||
| 396 | icon-size = iconSize; | ||
| 397 | tooltip-format = "{volume}% {node_name}"; | ||
| 398 | format-icons = ["󰕿" "󰖀" "󰕾"]; | ||
| 399 | format-muted = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">󰝟</span>"; | ||
| 400 | # ignored-sinks = ["Easy Effects Sink"]; | ||
| 401 | on-scroll-up = "volumectl -d -u up"; | ||
| 402 | on-scroll-down = "volumectl -d -u down"; | ||
| 403 | on-click = "volumectl -d toggle-mute"; | ||
| 404 | }; | ||
| 405 | } | ||
| 406 | { | ||
| 407 | layer = "top"; | ||
| 408 | position = "top"; | ||
| 409 | height = 14; | ||
| 410 | output = [ "!eDP-1" "!DP-2" "!DP-3" ]; | ||
| 411 | modules-left = [ "hyprland/workspaces" ]; | ||
| 412 | modules-center = [ "hyprland/window" ]; | ||
| 413 | modules-right = [ "clock" ]; | ||
| 414 | |||
| 415 | "hyprland/workspaces" = { | ||
| 416 | all-outputs = false; | ||
| 417 | }; | ||
| 418 | "hyprland/window" = { | ||
| 419 | separate-outputs = true; | ||
| 420 | icon = true; | ||
| 421 | icon-size = 14; | ||
| 422 | rewrite = windowRewrites; | ||
| 423 | }; | ||
| 424 | clock = { | ||
| 425 | interval = 1; | ||
| 426 | # timezone = "Europe/Berlin"; | ||
| 427 | format = "{:%H:%M}"; | ||
| 428 | tooltip-format = "W{:%V-%u %F %H:%M:%S%Ez}"; | ||
| 429 | }; | ||
| 430 | } | ||
| 431 | ]; | ||
| 432 | style = '' | ||
| 433 | @define-color white #ffffff; | ||
| 434 | @define-color grey #555555; | ||
| 435 | @define-color blue #1a8fff; | ||
| 436 | @define-color green #23fd00; | ||
| 437 | @define-color orange #f28a21; | ||
| 438 | @define-color red #f2201f; | ||
| 439 | |||
| 440 | * { | ||
| 441 | border: none; | ||
| 442 | font-family: "Fira Sans Nerd Font"; | ||
| 443 | font-size: 10pt; | ||
| 444 | min-height: 0; | ||
| 445 | } | ||
| 446 | |||
| 447 | window#waybar { | ||
| 448 | background-color: rgba(0, 0, 0, 0.66); | ||
| 449 | color: @white; | ||
| 450 | } | ||
| 451 | |||
| 452 | .modules-left { | ||
| 453 | margin-left: 9px; | ||
| 454 | } | ||
| 455 | .modules-right { | ||
| 456 | margin-right: 9px; | ||
| 457 | } | ||
| 458 | |||
| 459 | .module { | ||
| 460 | margin: 0 5px; | ||
| 461 | } | ||
| 462 | |||
| 463 | #workspaces button { | ||
| 464 | color: @grey; | ||
| 465 | } | ||
| 466 | #workspaces button.hosting-monitor { | ||
| 467 | color: @white; | ||
| 468 | } | ||
| 469 | #workspaces button.visible { | ||
| 470 | color: @blue; | ||
| 471 | } | ||
| 472 | #workspaces button.active { | ||
| 473 | color: @green; | ||
| 474 | } | ||
| 475 | #workspaces button.urgent { | ||
| 476 | color: @red; | ||
| 477 | } | ||
| 478 | |||
| 479 | #custom-weather, #custom-keymap, #custom-worktime, #custom-worktime-today { | ||
| 480 | color: @grey; | ||
| 481 | margin: 0 5px; | ||
| 482 | } | ||
| 483 | #custom-weather, #custom-worktime-today { | ||
| 484 | margin-right: 3px; | ||
| 485 | } | ||
| 486 | #custom-keymap, #custom-weather { | ||
| 487 | margin-left: 3px; | ||
| 488 | } | ||
| 489 | |||
| 490 | #tray { | ||
| 491 | margin: 0; | ||
| 492 | } | ||
| 493 | #battery, #idle_inhibitor, #backlight, #wireplumber { | ||
| 494 | color: @grey; | ||
| 495 | margin: 0 5px 0 2px; | ||
| 496 | } | ||
| 497 | #idle_inhibitor { | ||
| 498 | margin-right: 2px; | ||
| 499 | margin-left: 3px; | ||
| 500 | } | ||
| 501 | #battery { | ||
| 502 | margin-right: 3px; | ||
| 503 | } | ||
| 504 | #battery.discharging { | ||
| 505 | color: @white; | ||
| 506 | } | ||
| 507 | #battery.warning { | ||
| 508 | color: @orange; | ||
| 509 | } | ||
| 510 | #battery.critical { | ||
| 511 | color: @red; | ||
| 512 | } | ||
| 513 | #battery.charging { | ||
| 514 | color: @white; | ||
| 515 | } | ||
| 516 | #idle_inhibitor.activated { | ||
| 517 | color: @white; | ||
| 518 | } | ||
| 519 | |||
| 520 | #idle_inhibitor { | ||
| 521 | padding-top: 1px; | ||
| 522 | } | ||
| 523 | |||
| 524 | #privacy { | ||
| 525 | color: @red; | ||
| 526 | margin: -1px 2px 0px 5px; | ||
| 527 | } | ||
| 528 | #clock { | ||
| 529 | /* margin-right: 5px; */ | ||
| 530 | } | ||
| 531 | ''; | ||
| 532 | }; | ||
| 533 | wpaperd = { | ||
| 534 | enable = true; | ||
| 535 | settings.default = { | ||
| 536 | path = "~/.wallpapers"; | ||
| 537 | duration = "8h"; | ||
| 538 | mode = "center"; | ||
| 539 | }; | 340 | }; |
| 540 | }; | 341 | }; |
| 541 | fuzzel = { | 342 | fuzzel = { |
| 542 | enable = true; | 343 | enable = true; |
| 543 | settings = { | 344 | settings = { |
| 544 | main = { | 345 | main = { |
| 545 | terminal = lib.getExe pkgs.kitty; | 346 | terminal = lib.getExe cfg.programs.kitty.package; |
| 546 | layer = "overlay"; | 347 | layer = "overlay"; |
| 547 | icon-theme = "Paper"; | 348 | icon-theme = "Paper"; |
| 548 | font = "Fira Sans"; | 349 | font = "Fira Sans"; |
| 549 | }; | 350 | }; |
| 550 | colors = { | 351 | colors = { |
| 551 | background = "000000aa"; | 352 | background = "000000cc"; |
| 552 | text = "cdd6f4ff"; | 353 | text = "cdd6f4ff"; |
| 553 | match = "94e2d5ff"; | 354 | match = "94e2d5ff"; |
| 554 | selection = "585b70ff"; | 355 | selection = "585b70ff"; |
| @@ -561,34 +362,46 @@ in { | |||
| 561 | }; | 362 | }; |
| 562 | }; | 363 | }; |
| 563 | }; | 364 | }; |
| 365 | pandoc = { | ||
| 366 | enable = true; | ||
| 367 | extraAbbreviations = ["i.A." "d.h." "D.h." "gdw."]; | ||
| 368 | }; | ||
| 369 | nushell = { | ||
| 370 | enable = true; | ||
| 371 | settings.show_banner = false; | ||
| 372 | }; | ||
| 373 | fd.enable = true; | ||
| 564 | }; | 374 | }; |
| 565 | 375 | ||
| 566 | services = { | 376 | services = { |
| 567 | dunst = { | 377 | wpaperd = { |
| 568 | settings = import ./dunst-settings.nix inputs; | 378 | enable = false; |
| 569 | iconTheme = { | 379 | settings.default = { |
| 570 | package = pkgs.paper-icon-theme; | 380 | path = "~/.wallpapers"; |
| 571 | name = "Paper"; | 381 | duration = "15m"; |
| 382 | mode = "center"; | ||
| 572 | }; | 383 | }; |
| 573 | enable = true; | ||
| 574 | }; | 384 | }; |
| 575 | emacs = { | 385 | emacs = { |
| 576 | enable = true; | 386 | enable = true; |
| 577 | socketActivation.enable = true; | 387 | socketActivation.enable = true; |
| 578 | client = { | 388 | client = { |
| 579 | enable = true; | 389 | enable = true; |
| 580 | arguments = mkForce ["--reuse-frame" "--alternate-editor" "\"\""]; | 390 | arguments = mkForce ["--create-frame" "--alternate-editor" (lib.getExe cfg.services.emacs.package)]; |
| 581 | }; | 391 | }; |
| 582 | }; | 392 | }; |
| 583 | gpg-agent = { | 393 | gpg-agent = { |
| 584 | enable = true; | 394 | enable = true; |
| 585 | enableSshSupport = true; | 395 | enableSshSupport = true; |
| 586 | extraConfig = '' | 396 | extraConfig = '' |
| 587 | pinentry-program ${pkgs.pinentry-gtk2}/bin/pinentry | 397 | pinentry-program ${lib.getExe' pkgs.pinentry-gtk2 "pinentry"} |
| 588 | grab | 398 | grab |
| 589 | ''; | 399 | ''; |
| 590 | }; | 400 | }; |
| 591 | xembed-sni-proxy.enable = true; | 401 | xembed-sni-proxy = { |
| 402 | enable = true; | ||
| 403 | package = pkgs.kdePackages.plasma-workspace; | ||
| 404 | }; | ||
| 592 | udiskie = { | 405 | udiskie = { |
| 593 | enable = true; | 406 | enable = true; |
| 594 | automount = false; | 407 | automount = false; |
| @@ -599,6 +412,12 @@ in { | |||
| 599 | notification_actions = { | 412 | notification_actions = { |
| 600 | device_mounted = []; | 413 | device_mounted = []; |
| 601 | }; | 414 | }; |
| 415 | device_config = [ | ||
| 416 | { loop_file = "/nix/store/*-etc-metadata.erofs"; is_mounted = false; ignore = true; } | ||
| 417 | { mount_path = "/run/nixos-etc-metadata"; ignore = true; } | ||
| 418 | { mount_path = "/run/nixos-etc-metadata.*"; ignore = true; } | ||
| 419 | ]; | ||
| 420 | icon_names.media = ["drive-removable-media-symbolic"]; | ||
| 602 | }; | 421 | }; |
| 603 | }; | 422 | }; |
| 604 | network-manager-applet.enable = true; | 423 | network-manager-applet.enable = true; |
| @@ -615,7 +434,7 @@ in { | |||
| 615 | batch = "true"; | 434 | batch = "true"; |
| 616 | log = "false"; | 435 | log = "false"; |
| 617 | repeat = "watch"; | 436 | repeat = "watch"; |
| 618 | sshcmd = "${pkgs.openssh}/bin/ssh"; | 437 | sshcmd = lib.getExe' pkgs.openssh "ssh"; |
| 619 | ui = "text"; | 438 | ui = "text"; |
| 620 | }; | 439 | }; |
| 621 | }; | 440 | }; |
| @@ -631,35 +450,7 @@ in { | |||
| 631 | serverUrl = "https://etesync.yggdrasil.li"; | 450 | serverUrl = "https://etesync.yggdrasil.li"; |
| 632 | }; | 451 | }; |
| 633 | 452 | ||
| 634 | swayidle = { | ||
| 635 | enable = true; | ||
| 636 | events = [ | ||
| 637 | { event = "before-sleep"; command = lockCommand; } | ||
| 638 | { event = "after-resume"; command = "${cfg.wayland.windowManager.hyprland.package}/bin/hyprctl dispatch dpms on"; } | ||
| 639 | { event = "lock"; command = lockCommand; } | ||
| 640 | ]; | ||
| 641 | timeouts = [ | ||
| 642 | { timeout = 300; | ||
| 643 | command = "${cfg.wayland.windowManager.hyprland.package}/bin/hyprctl dispatch dpms off"; | ||
| 644 | } | ||
| 645 | { timeout = 330; command = lockCommand; } | ||
| 646 | ]; | ||
| 647 | extraArgs = [ | ||
| 648 | "idlehint" "30" | ||
| 649 | ]; | ||
| 650 | }; | ||
| 651 | poweralertd.enable = true; | 453 | poweralertd.enable = true; |
| 652 | avizo = { | ||
| 653 | enable = true; | ||
| 654 | settings.default = { | ||
| 655 | time = "1.0"; | ||
| 656 | background = "rgba(0, 0, 0, 0.8)"; | ||
| 657 | border-color = "rgba(0, 0, 0, 1)"; | ||
| 658 | bar-fg-color = "rgba(160, 160, 160, 1)"; | ||
| 659 | bar-bg-color = "rgba(32, 32, 32, 0.96)"; | ||
| 660 | # y-offset = "0.25"; | ||
| 661 | }; | ||
| 662 | }; | ||
| 663 | }; | 454 | }; |
| 664 | 455 | ||
| 665 | home.pointerCursor = { | 456 | home.pointerCursor = { |
| @@ -690,6 +481,15 @@ in { | |||
| 690 | name = "Paper-Mono-Dark"; | 481 | name = "Paper-Mono-Dark"; |
| 691 | }; | 482 | }; |
| 692 | }; | 483 | }; |
| 484 | qt.enable = true; | ||
| 485 | qt.platformTheme.name = "gtk"; | ||
| 486 | |||
| 487 | qt.kde.settings = { | ||
| 488 | kwalletrc = { | ||
| 489 | KSecretD.Enabled = false; | ||
| 490 | Wallet."Default Wallet" = "store"; | ||
| 491 | }; | ||
| 492 | }; | ||
| 693 | 493 | ||
| 694 | xsession.preferStatusNotifierItems = true; | 494 | xsession.preferStatusNotifierItems = true; |
| 695 | 495 | ||
| @@ -699,18 +499,19 @@ in { | |||
| 699 | packages = with pkgs; [ | 499 | packages = with pkgs; [ |
| 700 | fira fira-code pwvucontrol wrappedKeepassxc wl-clipboard-rs | 500 | fira fira-code pwvucontrol wrappedKeepassxc wl-clipboard-rs |
| 701 | mumble pulseaudio-ctl pamixer libnotify screen-message | 501 | mumble pulseaudio-ctl pamixer libnotify screen-message |
| 702 | wrappedYTMDesktop libsForQt5.qt5ct playerctl evince | 502 | wrappedYTMDesktop libsForQt5.qt5ct playerctl evince papers |
| 703 | thunderbird zoom-us steam steam-run wireshark virt-manager | 503 | thunderbird zoom-us xdg-desktop-portal steam steam-run |
| 704 | rclone cached-nix-shell worktime fira-code-symbols | 504 | wireshark virt-manager rclone cached-nix-shell worktime |
| 705 | libreoffice xournalpp google-chrome nixos-shell virt-viewer | 505 | fira-code-symbols libreoffice xournalpp |
| 706 | freerdp gnome-icon-theme paper-icon-theme sshpassSecret | 506 | nixos-shell virt-viewer freerdp gnome-icon-theme |
| 707 | weechat element-desktop matrix-synapse-tools.synadm | 507 | paper-icon-theme sshpassSecret weechat element-desktop |
| 708 | flakeInputs.deploy-rs.packages.${config.nixpkgs.system}.deploy-rs | 508 | sieve-connect gimp3 inkscape udiskie glab nitrokey-app |
| 709 | sieve-connect gimp inkscape udiskie glab nitrokey-app | ||
| 710 | pynitrokey gtklock wlrctl remmina openscad spice-record | 509 | pynitrokey gtklock wlrctl remmina openscad spice-record |
| 711 | libguestfs-with-appliance nerd-fonts.fira-mono | 510 | nerd-fonts.fira-mono |
| 712 | nerd-fonts.symbols-only nerd-fonts.fira-code powerline-fonts | 511 | nerd-fonts.symbols-only nerd-fonts.fira-code powerline-fonts |
| 713 | ]; | 512 | swtpm (hunspell.withDicts (dicts: with dicts; [en_GB-large de_DE])) |
| 513 | libation libqalculate | ||
| 514 | ] ++ mapAttrsToList (_name: pkg: pkgs.callPackage pkg {}) (customUtils.nixImport { dir = ./utils; }); | ||
| 714 | 515 | ||
| 715 | file = { | 516 | file = { |
| 716 | ".backup-munin".source = ./backup-patterns; | 517 | ".backup-munin".source = ./backup-patterns; |
| @@ -727,15 +528,12 @@ in { | |||
| 727 | sessionVariables = { | 528 | sessionVariables = { |
| 728 | # GDK_SCALE = 96.0 / 282.0; | 529 | # GDK_SCALE = 96.0 / 282.0; |
| 729 | # QT_AUTO_SCREEN_SCALE_FACTOR = 1; | 530 | # QT_AUTO_SCREEN_SCALE_FACTOR = 1; |
| 730 | QT_QPA_PLATFORMTHEME = "qt5ct"; | 531 | QT_QPA_PLATFORMTHEME = lib.mkForce "gtk3"; |
| 731 | LIBVIRT_DEFAULT_URI = "qemu:///system"; | 532 | LIBVIRT_DEFAULT_URI = "qemu:///system"; |
| 732 | STACK_XDG = 1; | 533 | STACK_XDG = 1; |
| 733 | EDITOR = pkgs.writeShellScript "editor" '' | 534 | EDITOR = lib.getExe' editor "emacsclient"; |
| 734 | args=("--reuse-frame" "--alternate-editor" "") | 535 | RCLONE_PASSWORD_COMMAND = "${lib.getExe' pkgs.libsecret "secret-tool"} lookup service rclone"; |
| 735 | args+=("$@") | 536 | SYSTEMD_TINT_BACKGROUND = "false"; |
| 736 | exec -a emacsclient ${cfg.services.emacs.package}/bin/emacsclient "''${args[@]}" | ||
| 737 | ''; | ||
| 738 | RCLONE_PASSWORD_COMMAND = "${pkgs.libsecret}/bin/secret-tool lookup service rclone"; | ||
| 739 | }; | 537 | }; |
| 740 | 538 | ||
| 741 | extraProfileCommands = '' | 539 | extraProfileCommands = '' |
| @@ -744,18 +542,11 @@ in { | |||
| 744 | }; | 542 | }; |
| 745 | 543 | ||
| 746 | xdg.configFile = { | 544 | xdg.configFile = { |
| 747 | "dunst/dunstrc.d" = { | ||
| 748 | source = ./dunstrc.d; | ||
| 749 | recursive = true; | ||
| 750 | onChange = '' | ||
| 751 | ${pkgs.systemd}/bin/systemctl --user try-restart dunst | ||
| 752 | ''; | ||
| 753 | }; | ||
| 754 | "wireplumber" = { | 545 | "wireplumber" = { |
| 755 | source = ./wireplumber; | 546 | source = ./wireplumber; |
| 756 | recursive = true; | 547 | recursive = true; |
| 757 | onChange = '' | 548 | onChange = '' |
| 758 | ${pkgs.systemd}/bin/systemctl --user try-restart wireplumber | 549 | ${lib.getExe' config.systemd.package "systemctl"} --user try-restart wireplumber |
| 759 | ''; | 550 | ''; |
| 760 | }; | 551 | }; |
| 761 | "stack/config.yaml" = { | 552 | "stack/config.yaml" = { |
| @@ -779,37 +570,36 @@ in { | |||
| 779 | General = { | 570 | General = { |
| 780 | dot_as_separator = 0; | 571 | dot_as_separator = 0; |
| 781 | }; | 572 | }; |
| 573 | Mode = { | ||
| 574 | calculate_as_you_type = 1; | ||
| 575 | }; | ||
| 782 | }; | 576 | }; |
| 783 | }; | 577 | }; |
| 784 | "emacs/init.el".source = ./emacs.el; | 578 | "emacs/init.el".source = pkgs.substitute { |
| 579 | src = ./emacs.el; | ||
| 580 | substitutions = [ | ||
| 581 | "--subst-var-by" "ksshaskpass" (lib.getExe pkgs.kdePackages.ksshaskpass) | ||
| 582 | ]; | ||
| 583 | }; | ||
| 584 | # "systemd/user/xdg-desktop-portal.service.d/after-graphical-session.conf".text = '' | ||
| 585 | # [Unit] | ||
| 586 | # After=graphical-session.target | ||
| 587 | # ''; | ||
| 588 | "systemd/user/home-manager.service.d/before-graphical-session.conf".text = '' | ||
| 589 | [Unit] | ||
| 590 | Before=graphical-session-pre.target | ||
| 591 | ''; | ||
| 592 | "pdfpc/pdfpcrc".text = '' | ||
| 593 | mouse 8 prev | ||
| 594 | mouse 9 next | ||
| 595 | ''; | ||
| 785 | }; | 596 | }; |
| 786 | 597 | ||
| 787 | xdg.dataFile = { | 598 | xdg.dataFile = { |
| 788 | "pandoc/abbreviations" = { | ||
| 789 | source = pkgs.runCommand "pandoc-abbreviations" { | ||
| 790 | buildInputs = [ pkgs.pandoc pkgs.coreutils ]; | ||
| 791 | } (let | ||
| 792 | germanAbbrevs = pkgs.fetchFromGitHub { | ||
| 793 | owner = "jfilter"; | ||
| 794 | repo = "german-abbreviations"; | ||
| 795 | rev = "8eb9dae93b6f05d7c53374cd217ab2dc89558e0c"; | ||
| 796 | sha256 = "SaD3tSqzen6Y3SPICe6/9vhe4iMHlArZ3kFQaEk7Hps="; | ||
| 797 | }; | ||
| 798 | in '' | ||
| 799 | cat \ | ||
| 800 | <(pandoc --print-default-data-file=abbreviations) \ | ||
| 801 | <(grep -E '^[^ ]+\.$' ${germanAbbrevs}/german_abbreviations.txt) \ | ||
| 802 | ${pkgs.writeText "abbrevs.txt" '' | ||
| 803 | i.A. | ||
| 804 | d.h. | ||
| 805 | D.h. | ||
| 806 | gdw. | ||
| 807 | ''} \ | ||
| 808 | | sort | uniq >$out | ||
| 809 | ''); | ||
| 810 | }; | ||
| 811 | "dbus-1/services/org.keepassxc.KeePassXC.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.keepassxc.KeePassXC.service"; | 599 | "dbus-1/services/org.keepassxc.KeePassXC.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.keepassxc.KeePassXC.service"; |
| 812 | "dbus-1/services/org.freedesktop.secrets.service.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.freedesktop.secrets.service.service"; | 600 | "dbus-1/services/org.freedesktop.secrets.service.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.freedesktop.secrets.service.service"; |
| 601 | "dbus-1/services/org.kde.kwalletd6.service".source = "${pkgs.kdePackages.kwallet}/share/dbus-1/services/org.kde.kwalletd6.service"; | ||
| 602 | "dbus-1/services/org.kde.kwalletd5.service".source = "${pkgs.kdePackages.kwallet}/share/dbus-1/services/org.kde.kwalletd5.service"; | ||
| 813 | "emoji-data/list.txt".source = pkgs.stdenv.mkDerivation { | 603 | "emoji-data/list.txt".source = pkgs.stdenv.mkDerivation { |
| 814 | inherit (sources.emoji-data) pname src; | 604 | inherit (sources.emoji-data) pname src; |
| 815 | version = lib.removePrefix "v" sources.emoji-data.version; | 605 | version = lib.removePrefix "v" sources.emoji-data.version; |
| @@ -895,19 +685,68 @@ in { | |||
| 895 | name = "Rainbow"; | 685 | name = "Rainbow"; |
| 896 | exec = toString (pkgs.writeShellScript "rainbow" '' | 686 | exec = toString (pkgs.writeShellScript "rainbow" '' |
| 897 | exec -- \ | 687 | exec -- \ |
| 898 | ${config.systemd.package}/bin/systemd-run --wait --user --slice-inherit \ | 688 | ${lib.getExe' config.systemd.package "systemd-run"} --wait --user --slice-inherit \ |
| 899 | --property 'CPUAccounting=yes' --property 'CPUQuotaPeriodSec=50ms' \ | 689 | --property 'CPUAccounting=yes' --property 'CPUQuotaPeriodSec=50ms' \ |
| 900 | --property 'Environment=DSCP=46' \ | 690 | -E DSCP=46 -E NIXOS_OZONE_WL \ |
| 901 | -- ${pkgs.dscp}/bin/dscp ${pkgs.google-chrome}/bin/google-chrome-stable \ | 691 | -- ${lib.getExe pkgs.dscp} ${lib.getExe cfg.programs.chromium.package} \ |
| 902 | --force-device-scale-factor=1.5 \ | ||
| 903 | --class=Rainbow \ | 692 | --class=Rainbow \ |
| 904 | --kiosk "https://web.openrainbow.com" \ | 693 | --app="https://web.openrainbow.com" \ |
| 905 | --user-data-dir=''${HOME}/.config/google-chrome-rainbow | 694 | --user-data-dir=''${HOME}/.config/chromium-rainbow |
| 906 | ''); | 695 | ''); |
| 907 | icon = pkgs.fetchurl { | 696 | icon = pkgs.fetchurl { |
| 908 | url = "https://web.openrainbow.com/rb/2.139.17/assets/skins/rainbow/images/homepage/logo__rainbow.svg"; | 697 | url = "https://web.openrainbow.com/rb/2.139.17/assets/skins/rainbow/images/homepage/logo__rainbow.svg"; |
| 909 | hash = "sha256-5fmo8rDqVDpzkGaPjk4Y+SsSZpAsY7VUQSFW6WdHwuU="; | 698 | hash = "sha256-5fmo8rDqVDpzkGaPjk4Y+SsSZpAsY7VUQSFW6WdHwuU="; |
| 910 | }; | 699 | }; |
| 700 | settings = { | ||
| 701 | StartupWMClass = "Rainbow"; | ||
| 702 | }; | ||
| 703 | }; | ||
| 704 | kimai = { | ||
| 705 | name = "Kimai"; | ||
| 706 | exec = toString (pkgs.writeShellScript "kimai" '' | ||
| 707 | exec -- \ | ||
| 708 | ${lib.getExe cfg.programs.chromium.package} \ | ||
| 709 | --class=Kimai \ | ||
| 710 | --app="https://kimai.yggdrasil.li" \ | ||
| 711 | --user-data-dir=''${HOME}/.config/chromium-kimai | ||
| 712 | ''); | ||
| 713 | icon = pkgs.fetchurl { | ||
| 714 | url = "https://www.kimai.org/images/kimai_logo.png"; | ||
| 715 | hash = "sha256-lnlOttzR2SwXA70R+egJUkeKr4U5V0avqTk8uX4bqfs="; | ||
| 716 | }; | ||
| 717 | settings = { | ||
| 718 | StartupWMClass = "Kimai"; | ||
| 719 | StartupNotify = "true"; | ||
| 720 | }; | ||
| 721 | }; | ||
| 722 | audiobookshelf = { | ||
| 723 | name = "Audiobookshelf"; | ||
| 724 | exec = toString (pkgs.writeShellScript "audiobookshelf" '' | ||
| 725 | exec -- \ | ||
| 726 | ${lib.getExe cfg.programs.chromium.package} \ | ||
| 727 | --class=Audiobookshelf \ | ||
| 728 | --app="https://audiobookshelf.yggdrasil.li" \ | ||
| 729 | --user-data-dir=''${HOME}/.config/chromium-audiobookshelf | ||
| 730 | ''); | ||
| 731 | icon = pkgs.fetchurl { | ||
| 732 | url = "https://www.audiobookshelf.org/Logo.png"; | ||
| 733 | hash = "sha256-JGPk+WNT1C4DC4lSMb0K0YmAMT5LvmSOeO0QRzkc7Lk="; | ||
| 734 | }; | ||
| 735 | settings = { | ||
| 736 | StartupWMClass = "Audiobookshelf"; | ||
| 737 | StartupNotify = "true"; | ||
| 738 | }; | ||
| 739 | }; | ||
| 740 | thunderbird-lmu = { | ||
| 741 | name = "Thunderbird (LMU)"; | ||
| 742 | exec = "thunderbird --name thunderbird -P lmu %U"; | ||
| 743 | icon = "thunderbird"; | ||
| 744 | genericName = "Email Client"; | ||
| 745 | categories = [ "Network" "Chat" "Email" "Feed" "GTK" "News" ]; | ||
| 746 | settings = { | ||
| 747 | StartupWMClass = "thunderbird"; | ||
| 748 | StartupNotify = "true"; | ||
| 749 | }; | ||
| 911 | }; | 750 | }; |
| 912 | }; | 751 | }; |
| 913 | 752 | ||
| @@ -923,23 +762,6 @@ in { | |||
| 923 | color-scheme = "prefer-dark"; | 762 | color-scheme = "prefer-dark"; |
| 924 | }; | 763 | }; |
| 925 | }; | 764 | }; |
| 926 | |||
| 927 | wayland.windowManager.hyprland = { | ||
| 928 | enable = true; | ||
| 929 | settings = import ./hyprland.nix inputs; | ||
| 930 | }; | ||
| 931 | |||
| 932 | xdg.portal = { | ||
| 933 | enable = true; | ||
| 934 | xdgOpenUsePortal = true; | ||
| 935 | config = { | ||
| 936 | common.default = [ "gtk" ]; | ||
| 937 | hyprland.default = [ "gtk" "kde" "hyprland" ]; | ||
| 938 | }; | ||
| 939 | extraPortals = with pkgs; [ | ||
| 940 | xdg-desktop-portal-kde xdg-desktop-portal-gtk xdg-desktop-portal-wlr xdg-desktop-portal-hyprland | ||
| 941 | ]; | ||
| 942 | }; | ||
| 943 | }; | 765 | }; |
| 944 | }; | 766 | }; |
| 945 | } | 767 | } |
diff --git a/accounts/gkleen@sif/dunst-settings.nix b/accounts/gkleen@sif/dunst-settings.nix deleted file mode 100644 index 72687aea..00000000 --- a/accounts/gkleen@sif/dunst-settings.nix +++ /dev/null | |||
| @@ -1,45 +0,0 @@ | |||
| 1 | { pkgs, ... }: | ||
| 2 | { | ||
| 3 | global = { | ||
| 4 | font = "Fira Sans 12"; | ||
| 5 | markup = "full"; | ||
| 6 | format = "<i>%s</i> %p\\n%b"; | ||
| 7 | alignment = "left"; | ||
| 8 | # geometry = "1216x10-32+64"; | ||
| 9 | width = 500; | ||
| 10 | height = 100; | ||
| 11 | offset = "4x4"; | ||
| 12 | origin = "top-right"; | ||
| 13 | shrink = true; | ||
| 14 | monitor = 0; | ||
| 15 | follow = "none"; | ||
| 16 | padding = 6; | ||
| 17 | horizontal_padding = 6; | ||
| 18 | separator_height = 1; | ||
| 19 | separator_color = "frame"; | ||
| 20 | idle_threshold = 0; | ||
| 21 | |||
| 22 | transparency = 10; | ||
| 23 | |||
| 24 | frame_width = 1; | ||
| 25 | frame_color = "#999999"; | ||
| 26 | |||
| 27 | word_wrap = true; | ||
| 28 | show_age_threshold = 15; | ||
| 29 | show_indicators = false; | ||
| 30 | icon_position = "right"; | ||
| 31 | min_icon_size = 25; | ||
| 32 | max_icon_size = 25; | ||
| 33 | sort = false; | ||
| 34 | sticky_history = false; | ||
| 35 | |||
| 36 | dmenu = "fuzzel --dmenu"; | ||
| 37 | browser = "${pkgs.xdg-utils}/bin/xdg-open"; | ||
| 38 | }; | ||
| 39 | # shortcuts = { | ||
| 40 | # close = "ctrl+space"; | ||
| 41 | # close_all = "ctrl+shift+space"; | ||
| 42 | # history = "ctrl+comma"; | ||
| 43 | # context = "ctrl+period"; | ||
| 44 | # }; | ||
| 45 | } | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/00-urgency_low.conf b/accounts/gkleen@sif/dunstrc.d/00-urgency_low.conf deleted file mode 100644 index 98c94b64..00000000 --- a/accounts/gkleen@sif/dunstrc.d/00-urgency_low.conf +++ /dev/null | |||
| @@ -1,4 +0,0 @@ | |||
| 1 | [urgency_low] | ||
| 2 | background="#000000aa" | ||
| 3 | foreground="#999999" | ||
| 4 | timeout=5 \ No newline at end of file | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/01-urgency_normal.conf b/accounts/gkleen@sif/dunstrc.d/01-urgency_normal.conf deleted file mode 100644 index f8fa8e2d..00000000 --- a/accounts/gkleen@sif/dunstrc.d/01-urgency_normal.conf +++ /dev/null | |||
| @@ -1,4 +0,0 @@ | |||
| 1 | [urgency_normal] | ||
| 2 | background="#000000aa" | ||
| 3 | foreground="#ffffff" | ||
| 4 | timeout=15 \ No newline at end of file | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/02-urgency_critical.conf b/accounts/gkleen@sif/dunstrc.d/02-urgency_critical.conf deleted file mode 100644 index a08bf4b1..00000000 --- a/accounts/gkleen@sif/dunstrc.d/02-urgency_critical.conf +++ /dev/null | |||
| @@ -1,4 +0,0 @@ | |||
| 1 | [urgency_critical] | ||
| 2 | background="#900000aa" | ||
| 3 | foreground="#ffffff" | ||
| 4 | timeout=0 | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/10-brightness.conf b/accounts/gkleen@sif/dunstrc.d/10-brightness.conf deleted file mode 100644 index c54595ab..00000000 --- a/accounts/gkleen@sif/dunstrc.d/10-brightness.conf +++ /dev/null | |||
| @@ -1,5 +0,0 @@ | |||
| 1 | [brightness] | ||
| 2 | appname="brightness" | ||
| 3 | set_stack_tag="brightness" | ||
| 4 | set_transient=yes | ||
| 5 | history_ignore=yes | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/10-pulseaudio-ctl.conf b/accounts/gkleen@sif/dunstrc.d/10-pulseaudio-ctl.conf deleted file mode 100644 index 074f4535..00000000 --- a/accounts/gkleen@sif/dunstrc.d/10-pulseaudio-ctl.conf +++ /dev/null | |||
| @@ -1,5 +0,0 @@ | |||
| 1 | [pulseaudio-ctl] | ||
| 2 | body="Current is *" | ||
| 3 | history_ignore=yes | ||
| 4 | set_stack_tag="volume" | ||
| 5 | summary="Volume *" | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/20-element.conf b/accounts/gkleen@sif/dunstrc.d/20-element.conf deleted file mode 100644 index 5ff6031e..00000000 --- a/accounts/gkleen@sif/dunstrc.d/20-element.conf +++ /dev/null | |||
| @@ -1,3 +0,0 @@ | |||
| 1 | [element-im] | ||
| 2 | appname=Element | ||
| 3 | timeout=0 \ No newline at end of file | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/20-kitty.conf b/accounts/gkleen@sif/dunstrc.d/20-kitty.conf deleted file mode 100644 index b27ee27e..00000000 --- a/accounts/gkleen@sif/dunstrc.d/20-kitty.conf +++ /dev/null | |||
| @@ -1,3 +0,0 @@ | |||
| 1 | [kitty] | ||
| 2 | appname=kitty | ||
| 3 | urgency=low | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/20-mail.conf b/accounts/gkleen@sif/dunstrc.d/20-mail.conf deleted file mode 100644 index cb568e01..00000000 --- a/accounts/gkleen@sif/dunstrc.d/20-mail.conf +++ /dev/null | |||
| @@ -1,3 +0,0 @@ | |||
| 1 | [element] | ||
| 2 | appname="notmuch" | ||
| 3 | timeout=0 \ No newline at end of file | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/20-zulip.conf b/accounts/gkleen@sif/dunstrc.d/20-zulip.conf deleted file mode 100644 index d7fbd32c..00000000 --- a/accounts/gkleen@sif/dunstrc.d/20-zulip.conf +++ /dev/null | |||
| @@ -1,3 +0,0 @@ | |||
| 1 | [zulip] | ||
| 2 | appname="Zulip" | ||
| 3 | timeout=0 \ No newline at end of file | ||
diff --git a/accounts/gkleen@sif/emacs.el b/accounts/gkleen@sif/emacs.el index b1b1b198..3beefba6 100644 --- a/accounts/gkleen@sif/emacs.el +++ b/accounts/gkleen@sif/emacs.el | |||
| @@ -14,7 +14,7 @@ | |||
| 14 | (setq package-archives nil) | 14 | (setq package-archives nil) |
| 15 | (package-initialize) | 15 | (package-initialize) |
| 16 | (require 'use-package) | 16 | (require 'use-package) |
| 17 | (use-package use-package-ensure-system-package :ensure t) | 17 | ;; (use-package use-package-ensure-system-package :ensure t) |
| 18 | 18 | ||
| 19 | (require 'evil) | 19 | (require 'evil) |
| 20 | (evil-mode 1) | 20 | (evil-mode 1) |
| @@ -51,7 +51,7 @@ | |||
| 51 | 51 | ||
| 52 | ;; (require 'scratch) | 52 | ;; (require 'scratch) |
| 53 | (global-set-key (kbd "C-x B") 'scratch-create) | 53 | (global-set-key (kbd "C-x B") 'scratch-create) |
| 54 | (setq initial-major-mode 'scratch-mode) | 54 | ;; (setq initial-major-mode 'scratch-mode) |
| 55 | (setq initial-scratch-message "") | 55 | (setq initial-scratch-message "") |
| 56 | 56 | ||
| 57 | (global-set-key (kbd "C-x K") 'kill-current-buffer) | 57 | (global-set-key (kbd "C-x K") 'kill-current-buffer) |
| @@ -228,6 +228,7 @@ necessarily running." | |||
| 228 | (global-set-key (kbd "C-x k") 'kill-buffer-with-special-emacsclient-handling)) | 228 | (global-set-key (kbd "C-x k") 'kill-buffer-with-special-emacsclient-handling)) |
| 229 | 229 | ||
| 230 | (add-hook 'server-switch-hook 'install-emacsclient-wrapped-kill-buffer) | 230 | (add-hook 'server-switch-hook 'install-emacsclient-wrapped-kill-buffer) |
| 231 | (add-hook 'server-switch-hook #'raise-frame) | ||
| 231 | 232 | ||
| 232 | (defun move-file (new-location) | 233 | (defun move-file (new-location) |
| 233 | "Write this file to NEW-LOCATION, and delete the old one." | 234 | "Write this file to NEW-LOCATION, and delete the old one." |
| @@ -253,3 +254,5 @@ necessarily running." | |||
| 253 | (bind-key "C-x C-m" #'move-file) | 254 | (bind-key "C-x C-m" #'move-file) |
| 254 | 255 | ||
| 255 | (let ((ssh_auth_sock (string-chop-newline (shell-command-to-string "gpgconf --list-dirs agent-ssh-socket")))) (setenv "SSH_AUTH_SOCK" ssh_auth_sock)) | 256 | (let ((ssh_auth_sock (string-chop-newline (shell-command-to-string "gpgconf --list-dirs agent-ssh-socket")))) (setenv "SSH_AUTH_SOCK" ssh_auth_sock)) |
| 257 | (setenv "SSH_ASKPASS_REQUIRE" "prefer") | ||
| 258 | (setenv "SSH_ASKPASS" "@ksshaskpass@") | ||
diff --git a/accounts/gkleen@sif/firefox-chrome.css b/accounts/gkleen@sif/firefox-chrome.css index 8900e2b9..726f1e4b 100644 --- a/accounts/gkleen@sif/firefox-chrome.css +++ b/accounts/gkleen@sif/firefox-chrome.css | |||
| @@ -4,6 +4,21 @@ | |||
| 4 | font-size:12px; | 4 | font-size:12px; |
| 5 | } | 5 | } |
| 6 | 6 | ||
| 7 | #sidebar-main:has([expanded]) { | ||
| 8 | min-width:20em !important; | ||
| 9 | max-width:20em !important; | ||
| 10 | } | ||
| 11 | |||
| 12 | #sidebar, #sidebar-box { | ||
| 13 | min-width:35em !important; | ||
| 14 | max-width:35em !important; | ||
| 15 | } | ||
| 16 | |||
| 17 | #sidebar-box { | ||
| 18 | margin-right: var(--space-small); | ||
| 19 | } | ||
| 20 | |||
| 21 | /* | ||
| 7 | #sidebar { | 22 | #sidebar { |
| 8 | min-width:20em !important; | 23 | min-width:20em !important; |
| 9 | max-width:20em !important; | 24 | max-width:20em !important; |
| @@ -19,8 +34,21 @@ | |||
| 19 | } | 34 | } |
| 20 | 35 | ||
| 21 | #toolbar-menubar[inactive="true"] + #TabsToolbar { | 36 | #toolbar-menubar[inactive="true"] + #TabsToolbar { |
| 22 | visibility: collapse !important; | 37 | visibility: collapse !important; |
| 23 | } | 38 | } |
| 24 | 39 | ||
| 25 | #sidebar-box[sidebarcommand="tabcenter-reborn_ariasuni-sidebar-action"] #sidebar-header { visibility: collapse !important; } | 40 | #sidebar-box[sidebarcommand="tabcenter-reborn_ariasuni-sidebar-action"] #sidebar-header { visibility: collapse !important; } |
| 26 | #sidebar-box[sidebarcommand="_3c078156-979c-498b-8990-85f7987dd929_-sidebar-action"] #sidebar-header { visibility: collapse !important; } | 41 | #sidebar-box[sidebarcommand="_3c078156-979c-498b-8990-85f7987dd929_-sidebar-action"] #sidebar-header { visibility: collapse !important; } |
| 42 | */ | ||
| 43 | |||
| 44 | .titlebar-buttonbox-container{ display: none; } | ||
| 45 | #vertical-spacer { display: none; } | ||
| 46 | #tabbrowser-tabs[orient="vertical"] .tab-background { | ||
| 47 | border-radius: var(--border-radius-small) !important; | ||
| 48 | } | ||
| 49 | hbox:has(> #tabs-newtab-button) { | ||
| 50 | display: none; | ||
| 51 | } | ||
| 52 | #sidebar-main .tools-and-extensions { | ||
| 53 | justify-content: space-around !important; | ||
| 54 | } | ||
diff --git a/accounts/gkleen@sif/hyprland.nix b/accounts/gkleen@sif/hyprland.nix deleted file mode 100644 index d3061c61..00000000 --- a/accounts/gkleen@sif/hyprland.nix +++ /dev/null | |||
| @@ -1,424 +0,0 @@ | |||
| 1 | { pkgs, lib, config, userName, ... }: | ||
| 2 | let | ||
| 3 | cfg = config.home-manager.users.${userName}; | ||
| 4 | in { | ||
| 5 | monitor = [ | ||
| 6 | ",preferred,auto,auto,bitdepth,8" | ||
| 7 | "eDP-1,3840x2160@60,auto,1.5,bitdepth,8" | ||
| 8 | ]; | ||
| 9 | |||
| 10 | "$terminal" = "kitty"; | ||
| 11 | "$menu" = "fuzzel"; | ||
| 12 | |||
| 13 | exec-once = [ | ||
| 14 | "wpaperd" | ||
| 15 | ]; | ||
| 16 | |||
| 17 | env = [ | ||
| 18 | "NIXOS_OZONE_WL,1" | ||
| 19 | "QT_QPA_PLATFORM,wayland" | ||
| 20 | "QT_WAYLAND_DISABLE_WINDOWDECORATION,1" | ||
| 21 | "GDK_BACKEND,wayland" | ||
| 22 | "GDK_SCALE,0.66" | ||
| 23 | "QT_AUTO_SCREEN_SCALE_FACTOR,1" | ||
| 24 | "SDL_VIDEODRIVER,wayland" | ||
| 25 | # "AQ_DRM_DEVICES,/dev/dri/by-path/pci-0000:01:00.0-card" | ||
| 26 | "__NV_PRIME_RENDER_OFFLOAD,1" | ||
| 27 | "__NV_PRIME_RENDER_OFFLOAD_PROVIDER,NVIDIA-G0" | ||
| 28 | "__GLX_VENDOR_LIBRARY_NAME,nvidia" | ||
| 29 | "__VK_LAYER_NV_optimus,NVIDIA_only" | ||
| 30 | ]; | ||
| 31 | |||
| 32 | xwayland.force_zero_scaling = true; | ||
| 33 | |||
| 34 | general = { | ||
| 35 | gaps_in = 3; | ||
| 36 | gaps_out = 9; | ||
| 37 | "col.active_border" = "rgba(33ccffee) rgba(00ff95ee) 45deg"; | ||
| 38 | "col.inactive_border" = "rgba(595959aa)"; | ||
| 39 | |||
| 40 | resize_on_border = false; | ||
| 41 | |||
| 42 | allow_tearing = false; | ||
| 43 | |||
| 44 | layout = "dwindle"; | ||
| 45 | }; | ||
| 46 | |||
| 47 | decoration = { | ||
| 48 | rounding = 5; | ||
| 49 | dim_special = 0.0; | ||
| 50 | }; | ||
| 51 | |||
| 52 | animations = { | ||
| 53 | enabled = true; | ||
| 54 | bezier = "myBezier, 0.05, 0.9, 0.1, 1.05"; | ||
| 55 | animation = [ | ||
| 56 | "windows, 1, 1, default, popin 80%" | ||
| 57 | "windowsMove, 0" | ||
| 58 | # "windows, 1, 7, myBezier" | ||
| 59 | # "windowsOut, 1, 7, myBezier, popin 80%" | ||
| 60 | "border, 1, 10, default" | ||
| 61 | "borderangle, 1, 8, default" | ||
| 62 | "fade, 1, 1, default" | ||
| 63 | "workspaces, 1, 1, default, fade" | ||
| 64 | # "workspaces, 1, 6, default" | ||
| 65 | ]; | ||
| 66 | }; | ||
| 67 | |||
| 68 | dwindle = { | ||
| 69 | pseudotile = false; | ||
| 70 | preserve_split = true; | ||
| 71 | }; | ||
| 72 | |||
| 73 | master = { | ||
| 74 | new_status = "master"; | ||
| 75 | }; | ||
| 76 | |||
| 77 | misc = { | ||
| 78 | disable_hyprland_logo = true; | ||
| 79 | disable_splash_rendering = true; | ||
| 80 | # focus_on_activate = true; | ||
| 81 | mouse_move_enables_dpms = true; | ||
| 82 | key_press_enables_dpms = true; | ||
| 83 | new_window_takes_over_fullscreen = 1; | ||
| 84 | exit_window_retains_fullscreen = true; | ||
| 85 | }; | ||
| 86 | |||
| 87 | cursor = { | ||
| 88 | hide_on_key_press = true; | ||
| 89 | }; | ||
| 90 | |||
| 91 | input = { | ||
| 92 | kb_layout = "us,us"; | ||
| 93 | kb_variant = "dvp,"; | ||
| 94 | kb_model = ""; | ||
| 95 | kb_options = "compose:caps,grp:win_space_toggle"; | ||
| 96 | kb_rules = ""; | ||
| 97 | |||
| 98 | follow_mouse = 1; | ||
| 99 | |||
| 100 | sensitivity = 0; | ||
| 101 | |||
| 102 | touchpad = { | ||
| 103 | natural_scroll = false; | ||
| 104 | }; | ||
| 105 | }; | ||
| 106 | |||
| 107 | device = [ | ||
| 108 | { name = "synaptics-tm3512-010"; | ||
| 109 | sensitivity = 0.4; | ||
| 110 | } | ||
| 111 | { name = "tpps/2-elan-trackpoint"; | ||
| 112 | sensitivity = 0.2; | ||
| 113 | } | ||
| 114 | { name = "logitech-ergo-m575"; | ||
| 115 | sensitivity = 1.333; | ||
| 116 | } | ||
| 117 | ]; | ||
| 118 | |||
| 119 | gestures = { | ||
| 120 | workspace_swipe = false; | ||
| 121 | }; | ||
| 122 | |||
| 123 | dwindle = { | ||
| 124 | # no_gaps_when_only = 1; | ||
| 125 | }; | ||
| 126 | |||
| 127 | "$mainMod" = "SUPER"; | ||
| 128 | |||
| 129 | bind = [ | ||
| 130 | "$mainMod, return, exec, $terminal" | ||
| 131 | "$mainMod, Q, killactive" | ||
| 132 | "$mainMod SHIFT, Q, exec, hyprctl kill" | ||
| 133 | "$mainMod, V, togglefloating" | ||
| 134 | "$mainMod, D, exec, $menu" | ||
| 135 | "$mainMod SHIFT, D, exec, $menu --list-executables-in-path" | ||
| 136 | # "$mainMod, J, togglesplit," | ||
| 137 | |||
| 138 | "$mainMod SHIFT, L, exec, loginctl lock-session" | ||
| 139 | "$mainMod SHIFT, E, exit" | ||
| 140 | |||
| 141 | "$mainMod, left, movefocus, l" | ||
| 142 | "$mainMod, right, movefocus, r" | ||
| 143 | "$mainMod, up, movefocus, u" | ||
| 144 | "$mainMod, down, movefocus, d" | ||
| 145 | "$mainMod SHIFT, left, swapwindow, l" | ||
| 146 | "$mainMod SHIFT, right, swapwindow, r" | ||
| 147 | "$mainMod SHIFT, up, swapwindow, u" | ||
| 148 | "$mainMod SHIFT, down, swapwindow, d" | ||
| 149 | |||
| 150 | "$mainMod, T, cyclenext" | ||
| 151 | |||
| 152 | "$mainMod, G, focusmonitor, 0" | ||
| 153 | "$mainMod, C, focusmonitor, 1" | ||
| 154 | "$mainMod, R, focusmonitor, 2" | ||
| 155 | "$mainMod, L, focusmonitor, 3" | ||
| 156 | |||
| 157 | "$mainMod CTRL, G, movecurrentworkspacetomonitor, 0" | ||
| 158 | "$mainMod CTRL, C, movecurrentworkspacetomonitor, 1" | ||
| 159 | "$mainMod CTRL, R, movecurrentworkspacetomonitor, 2" | ||
| 160 | "$mainMod CTRL, L, movecurrentworkspacetomonitor, 3" | ||
| 161 | |||
| 162 | "$mainMod, F, fullscreen, 1" | ||
| 163 | "$mainMod SHIFT, F, fullscreen, 0" | ||
| 164 | "$mainMod CTRL SHIFT, F, fullscreenstate, 1, 2" | ||
| 165 | |||
| 166 | "$mainMod, code:14, workspace, 1" | ||
| 167 | "$mainMod, code:17, workspace, 2" | ||
| 168 | "$mainMod, code:13, workspace, 3" | ||
| 169 | "$mainMod, code:18, workspace, 4" | ||
| 170 | "$mainMod, code:12, workspace, 5" | ||
| 171 | "$mainMod, code:19, workspace, 6" | ||
| 172 | "$mainMod, code:11, workspace, 7" | ||
| 173 | "$mainMod, code:20, workspace, 8" | ||
| 174 | "$mainMod, code:15, workspace, 9" | ||
| 175 | "$mainMod, code:16, workspace, 10" | ||
| 176 | |||
| 177 | "$mainMod SHIFT, code:14, movetoworkspacesilent, 1" | ||
| 178 | "$mainMod SHIFT, code:17, movetoworkspacesilent, 2" | ||
| 179 | "$mainMod SHIFT, code:13, movetoworkspacesilent, 3" | ||
| 180 | "$mainMod SHIFT, code:18, movetoworkspacesilent, 4" | ||
| 181 | "$mainMod SHIFT, code:12, movetoworkspacesilent, 5" | ||
| 182 | "$mainMod SHIFT, code:19, movetoworkspacesilent, 6" | ||
| 183 | "$mainMod SHIFT, code:11, movetoworkspacesilent, 7" | ||
| 184 | "$mainMod SHIFT, code:20, movetoworkspacesilent, 8" | ||
| 185 | "$mainMod SHIFT, code:15, movetoworkspacesilent, 9" | ||
| 186 | "$mainMod SHIFT, code:16, movetoworkspacesilent, 10" | ||
| 187 | |||
| 188 | "$mainMod, semicolon, exec, dunstctl close" | ||
| 189 | "$mainMod SHIFT, semicolon, exec, dunstctl close-all" | ||
| 190 | "$mainMod, period, exec, dunstctl context" | ||
| 191 | "$mainMod, comma, exec, dunstctl history-pop" | ||
| 192 | |||
| 193 | "$mainMod ALT, E, exec, emacsclient -c" | ||
| 194 | "$mainMod ALT, Y, exec, ${pkgs.writeShellScript "yt-dlp" '' | ||
| 195 | export PATH="${lib.makeBinPath (with pkgs; [ wl-clipboard-rs socat ])}:$PATH" | ||
| 196 | socat STDIO UNIX-CONNECT:"$XDG_RUNTIME_DIR"/yt-dlp.sock <<<$'{ "urls": ["'"$(wl-paste)"$'"] }' | ||
| 197 | ''}" | ||
| 198 | "$mainMod ALT, L, exec, ${pkgs.writeShellScript "mpv" '' | ||
| 199 | export PATH="${lib.makeBinPath (with pkgs; [ wl-clipboard-rs ])}:$PATH" | ||
| 200 | exec mpv "$(wl-paste)" | ||
| 201 | ''}" | ||
| 202 | |||
| 203 | ", Print, exec, ${pkgs.writeShellScript "screenshot" '' | ||
| 204 | export PATH="${lib.makeBinPath (with pkgs; [ grim slurp wl-clipboard-rs coreutils ])}:$PATH" | ||
| 205 | |||
| 206 | outFile="$HOME/screenshots/$(date +"%Y-%m-%dT%H:%M:%S").png" | ||
| 207 | grim -g "$(slurp -b 00000080 -c FFFFFFFF -s 00000000 -w 1)" "$outFile" | ||
| 208 | wl-copy --type image/png <"$outFile" | ||
| 209 | ''}" | ||
| 210 | "SHIFT, Print, exec, ${pkgs.writeShellScript "screenshot" '' | ||
| 211 | export PATH="${lib.makeBinPath (with pkgs; [ grim jq wl-clipboard-rs coreutils ])}:$PATH" | ||
| 212 | |||
| 213 | outFile="$HOME/screenshots/$(date +"%Y-%m-%dT%H:%M:%S").png" | ||
| 214 | grim -o "$(hyprctl monitors -j | jq -r '.[] | select(.focused) | .name')" "$outFile" | ||
| 215 | wl-copy --type image/png <"$outFile" | ||
| 216 | ''}" | ||
| 217 | "CTRL SHIFT, Print, exec, ${pkgs.runCommand "picker" { | ||
| 218 | buildInputs = [ pkgs.makeWrapper ]; | ||
| 219 | } '' | ||
| 220 | makeWrapper ${lib.getExe pkgs.hyprpicker} $out \ | ||
| 221 | --prefix PATH : ${lib.makeBinPath [pkgs.wl-clipboard-rs]} | ||
| 222 | ''} -a" | ||
| 223 | "$mainMod, M, exec, ${pkgs.writeShellScript "qalc-fuzzel" '' | ||
| 224 | export PATH="${lib.makeBinPath (with pkgs; [ wl-clipboard-rs libqalculate cfg.programs.fuzzel.package coreutils findutils libnotify gnugrep ])}:$PATH" | ||
| 225 | |||
| 226 | RESULTS_DIR="$HOME/.cache/qalc-fuzzel" | ||
| 227 | prev() { | ||
| 228 | FOUND=false | ||
| 229 | while IFS= read -r line; do | ||
| 230 | [[ -n "$line" ]] || continue | ||
| 231 | FOUND=true | ||
| 232 | echo $line | ||
| 233 | done < <(export LC_ALL=C.UTF-8; echo; find "$RESULTS_DIR" -type f -printf $'%T@ %p\n' | sort -n | cut -d' ' -f2- | xargs -r cat) | ||
| 234 | $FOUND || echo | ||
| 235 | } | ||
| 236 | FUZZEL_RES=$(prev | fuzzel --dmenu --prompt "qalc> ") || exit $? | ||
| 237 | if [[ "$FUZZEL_RES" =~ .*\ =\ .* ]]; then | ||
| 238 | QALC_RES="$FUZZEL_RES" | ||
| 239 | QALC_RET=0 | ||
| 240 | else | ||
| 241 | QALC_RES=$(qalc "$FUZZEL_RES" 2>&1) | ||
| 242 | QALC_RET=$? | ||
| 243 | fi | ||
| 244 | [[ -n "$QALC_RES" ]] || exit 1 | ||
| 245 | EXISTING=false | ||
| 246 | fgrep -xrl "$QALC_RES" "$RESULTS_DIR" | xargs -r touch | ||
| 247 | [[ ''${PIPESTATUS[0]} -eq 0 ]] && EXISTING=true | ||
| 248 | if [[ $QALC_RET -eq 0 ]] && ! $EXISTING; then | ||
| 249 | RES_FILE="$RESULTS_DIR"/$(date -uIs).$(tr -Cd 'a-zA-Z0-9' </dev/random | head -c 10) | ||
| 250 | cat >"$RES_FILE" <<<"$QALC_RES" | ||
| 251 | fi | ||
| 252 | [[ "$QALC_RES" =~ .*\ =\ (.*) ]] && QALC_RES="''${BASH_REMATCH[1]}" | ||
| 253 | [[ $QALC_RET -eq 0 ]] && wl-copy "$QALC_RES" | ||
| 254 | notify-send "$QALC_RES" | ||
| 255 | ''}" | ||
| 256 | "$mainMod, E, exec, ${pkgs.writeShellScript "emoji-fuzzel" '' | ||
| 257 | export PATH="${lib.makeBinPath (with pkgs; [ cfg.programs.fuzzel.package wtype wl-clipboard-rs ])}:$PATH" | ||
| 258 | |||
| 259 | FUZZEL_RES=$(fuzzel --dmenu --prompt "emoji> " <$HOME/.local/share/emoji-data/list.txt) || exit $? | ||
| 260 | [[ -n "$FUZZEL_RES" ]] || exit 1 | ||
| 261 | wl-copy "$(cut -d ':' -f 1 <<<"$FUZZEL_RES" | tr -d '\n')" && wtype -k XF86Paste | ||
| 262 | ''}" | ||
| 263 | "$mainMod, B, exec, ${pkgs.writeShellScript "bring" '' | ||
| 264 | export PATH="${lib.makeBinPath (with pkgs; [ cfg.programs.fuzzel.package gawk gojq cfg.wayland.windowManager.hyprland.package ])}:$PATH" | ||
| 265 | |||
| 266 | state="$(hyprctl -j clients)" | ||
| 267 | active_window="$(hyprctl -j activewindow)" | ||
| 268 | active_workspace="$(hyprctl -j activeworkspace | gojq -r '.id')" | ||
| 269 | |||
| 270 | current_addr="$(echo "$active_window" | gojq -r '.address')" | ||
| 271 | |||
| 272 | window="$(echo "$state" | | ||
| 273 | gojq -r '.[] | select(.workspace.id == '"$active_workspace"') | select(.monitor != -1 ) | "\(.title)\t\(.address)"' | | ||
| 274 | fuzzel --log-level=warning --dmenu)" | ||
| 275 | |||
| 276 | addr="$(echo "$window" | awk -F $'\t' '{print $2}')" | ||
| 277 | |||
| 278 | if [[ "$addr" = "$current_addr" ]]; then | ||
| 279 | exit 0 | ||
| 280 | fi | ||
| 281 | |||
| 282 | fullscreen_on_same_ws="$(echo "$state" | gojq -r '.[] | select(.fullscreen == true) | select(.workspace.id == '"$active_workspace"') | .address')" | ||
| 283 | |||
| 284 | if [[ "$window" != "" ]]; then | ||
| 285 | if [[ "$fullscreen_on_same_ws" == "" ]]; then | ||
| 286 | hyprctl dispatch focuswindow address:"''${addr}" | ||
| 287 | else | ||
| 288 | # If we want to focus app_A and app_B is fullscreen on the same workspace, | ||
| 289 | # app_A will get focus, but app_B will remain on top. | ||
| 290 | # This monstrosity is to make sure app_A will end up on top instead. | ||
| 291 | # XXX: doesn't handle fullscreen 0, but I don't care. | ||
| 292 | hyprctl --batch "dispatch focuswindow address:''${fullscreen_on_same_ws}; dispatch fullscreen 1; dispatch focuswindow address:''${addr}; dispatch fullscreen 1" | ||
| 293 | fi | ||
| 294 | fi | ||
| 295 | ''}" | ||
| 296 | "$mainMod SHIFT, B, exec, ${pkgs.writeShellScript "bring" '' | ||
| 297 | export PATH="${lib.makeBinPath (with pkgs; [ cfg.programs.fuzzel.package gawk gojq cfg.wayland.windowManager.hyprland.package ])}:$PATH" | ||
| 298 | |||
| 299 | state="$(hyprctl -j clients)" | ||
| 300 | active_window="$(hyprctl -j activewindow)" | ||
| 301 | |||
| 302 | current_addr="$(echo "$active_window" | gojq -r '.address')" | ||
| 303 | |||
| 304 | window="$(echo "$state" | | ||
| 305 | gojq -r '.[] | select(.monitor != -1 ) | "\(.title)\t\(.workspace.name)\t\(.address)"' | | ||
| 306 | fuzzel --log-level=warning --dmenu)" | ||
| 307 | |||
| 308 | addr="$(echo "$window" | awk -F $'\t' '{print $3}')" | ||
| 309 | ws="$(echo "$window" | awk -F $'\t' '{print $2}')" | ||
| 310 | |||
| 311 | if [[ "$addr" = "$current_addr" ]]; then | ||
| 312 | exit 0 | ||
| 313 | fi | ||
| 314 | |||
| 315 | fullscreen_on_same_ws="$(echo "$state" | gojq -r ".[] | select(.fullscreen == true) | select(.workspace.name == \"$ws\") | .address")" | ||
| 316 | |||
| 317 | if [[ "$window" != "" ]]; then | ||
| 318 | if [[ "$fullscreen_on_same_ws" == "" ]]; then | ||
| 319 | hyprctl dispatch focuswindow address:"''${addr}" | ||
| 320 | else | ||
| 321 | # If we want to focus app_A and app_B is fullscreen on the same workspace, | ||
| 322 | # app_A will get focus, but app_B will remain on top. | ||
| 323 | # This monstrosity is to make sure app_A will end up on top instead. | ||
| 324 | # XXX: doesn't handle fullscreen 0, but I don't care. | ||
| 325 | hyprctl --batch "dispatch focuswindow address:''${fullscreen_on_same_ws}; dispatch fullscreen 1; dispatch focuswindow address:''${addr}; dispatch fullscreen 1" | ||
| 326 | fi | ||
| 327 | fi | ||
| 328 | ''}" | ||
| 329 | |||
| 330 | "$mainMod CTRL, return, togglespecialworkspace, term" | ||
| 331 | "$mainMod CTRL, e, togglespecialworkspace, edit" | ||
| 332 | "$mainMod CTRL, a, togglespecialworkspace, pwvucontrol" | ||
| 333 | "$mainMod CTRL, o, togglespecialworkspace, easyeffects" | ||
| 334 | "$mainMod CTRL, b, togglespecialworkspace, blueman" | ||
| 335 | "$mainMod CTRL, p, togglespecialworkspace, keepass" | ||
| 336 | ]; | ||
| 337 | bindm = [ | ||
| 338 | "$mainMod, mouse:272, movewindow" | ||
| 339 | "$mainMod, mouse:273, resizewindow" | ||
| 340 | ]; | ||
| 341 | bindel = [ | ||
| 342 | ", XF86MonBrightnessUp, exec, lightctl -d -e4 -n1 up" | ||
| 343 | ", XF86MonBrightnessDown, exec, lightctl -d -e4 -n1 down" | ||
| 344 | ", XF86AudioRaiseVolume, exec, volumectl -d -u up" | ||
| 345 | ", XF86AudioLowerVolume, exec, volumectl -d -u down" | ||
| 346 | ]; | ||
| 347 | bindl = [ | ||
| 348 | ", XF86AudioMute, exec, volumectl -d toggle-mute" | ||
| 349 | ", XF86AudioMicMute, exec, volumectl -d -m toggle-mute" | ||
| 350 | "$mainMod SHIFT, S, exec, systemctl suspend" | ||
| 351 | |||
| 352 | ", switch:off:Lid Switch,exec,hyprctl dispatch dpms on eDP-1" | ||
| 353 | ", switch:on:Lid Switch,exec,hyprctl dispatch dpms off eDP-1" | ||
| 354 | |||
| 355 | ", switch:off:Lid Switch,exec,${pkgs.writeShellScript "clamshell-off" '' | ||
| 356 | export PATH="${lib.makeBinPath (with pkgs; [ jq ])}:$PATH" | ||
| 357 | [[ $(hyprctl monitors -j | jq '.[] | select(.name == "eDP-1") | .disabled') = "true" ]] || exit 0 | ||
| 358 | |||
| 359 | hyprctl keyword monitor "eDP-1,3840x2160@60,auto,1.5" | ||
| 360 | ''}" | ||
| 361 | ", switch:on:Lid Switch,exec,${pkgs.writeShellScript "clamshell-on" '' | ||
| 362 | export PATH="${lib.makeBinPath (with pkgs; [ jq ])}:$PATH" | ||
| 363 | |||
| 364 | [[ $(hyprctl monitors -j | jq 'reduce (.[] | select(.disabled == false)) as $_ (0; .+1)') -gt 1 ]] || exit 0 | ||
| 365 | |||
| 366 | hyprctl keyword monitor "eDP-1,disable" | ||
| 367 | ''}" | ||
| 368 | ]; | ||
| 369 | |||
| 370 | windowrulev2 = [ | ||
| 371 | "suppressevent maximize fullscreen, class:.*" | ||
| 372 | |||
| 373 | # "maximize, class:^(Element|thunderbird)$" | ||
| 374 | "workspace special:pwvucontrol, class:^com\.saivert\.pwvucontrol$" | ||
| 375 | "workspace special:easyeffects, class:^com\.github\.wwmm\.easyeffects$" | ||
| 376 | "workspace special:blueman, class:^\.blueman-manager-wrapped$" | ||
| 377 | "workspace special:keepass silent, class:^org\.keepassxc\.KeePassXC$, title:^(?!Unlock Database.*)(?!.*(Access Request|Passkey credentials))" | ||
| 378 | # "group set always lock invade, class:^Element$" | ||
| 379 | "workspace 2, class:^firefox$" | ||
| 380 | "workspace 4, class:^evince$" | ||
| 381 | "workspace 4, class:^imv$" | ||
| 382 | "workspace 4, class:^org\.pwmt\.zathura$" | ||
| 383 | "workspace 10, class:^mpv$" | ||
| 384 | "workspace 1, class:^Element$" | ||
| 385 | "workspace 1, class:^thunderbird$" | ||
| 386 | "workspace 5, class:^virt-manager$" | ||
| 387 | "workspace 5, class:^qemu$" | ||
| 388 | "float, class:^org\.keepassxc\.KeePassXC$, title:Access Request$" | ||
| 389 | "center, class:^org\.keepassxc\.KeePassXC$, title:Access Request$" | ||
| 390 | "float, class:^org\.keepassxc\.KeePassXC$, title:Passkey credentials$" | ||
| 391 | "center, class:^org\.keepassxc\.KeePassXC$, title:Passkey credentials$" | ||
| 392 | "float, class:^org\.keepassxc\.KeePassXC$, title:^Unlock Database" | ||
| 393 | "center, class:^org\.keepassxc\.KeePassXC$, title:^Unlock Database" | ||
| 394 | "float, class:^xdg-desktop-portal-gtk$" | ||
| 395 | "center, class:^xdg-desktop-portal-gtk$" | ||
| 396 | |||
| 397 | "bordercolor rgba(ffaa33ee) rgba(bfff00ee) 45deg, fullscreen:1" | ||
| 398 | "bordercolor rgba(3366ffee) rgba(6a00ffee) 45deg, xwayland:1" | ||
| 399 | "bordercolor rgba(6633ffee) rgba(ea00ffee) 45deg, xwayland:1, fullscreen:1" | ||
| 400 | ]; | ||
| 401 | |||
| 402 | workspace = [ | ||
| 403 | "s[true], gapsout:100" | ||
| 404 | |||
| 405 | "special:term, on-created-empty:kitty" | ||
| 406 | "special:edit, on-created-empty:emacsclient -c" | ||
| 407 | "special:pwvucontrol, on-created-empty:pwvucontrol" | ||
| 408 | "special:easyeffects, on-created-empty:easyeffects" | ||
| 409 | "special:blueman, on-created-empty:blueman-manager" | ||
| 410 | "special:keepass, on-created-empty:keepassxc" | ||
| 411 | |||
| 412 | "1, defaultName:comm" | ||
| 413 | "2, defaultName:web" | ||
| 414 | "3, defaultName:work" | ||
| 415 | "4, defaultName:read" | ||
| 416 | ]; | ||
| 417 | |||
| 418 | layerrule = [ | ||
| 419 | "blur, waybar" | ||
| 420 | "blur, launcher" | ||
| 421 | "noanim, notifications" | ||
| 422 | "blur, notifications" | ||
| 423 | ]; | ||
| 424 | } | ||
diff --git a/accounts/gkleen@sif/libvirt/default.nix b/accounts/gkleen@sif/libvirt/default.nix index f86a68a2..4e5a9b90 100644 --- a/accounts/gkleen@sif/libvirt/default.nix +++ b/accounts/gkleen@sif/libvirt/default.nix | |||
| @@ -7,6 +7,7 @@ with flakeInputs.nixVirt.lib; | |||
| 7 | config = { | 7 | config = { |
| 8 | virtualisation.libvirt = { | 8 | virtualisation.libvirt = { |
| 9 | enable = true; | 9 | enable = true; |
| 10 | swtpm.enable = true; | ||
| 10 | connections."qemu:///session" = { | 11 | connections."qemu:///session" = { |
| 11 | domains = [ | 12 | domains = [ |
| 12 | { definition = domain.writeXML (updateManyAttrsByPath [ | 13 | { definition = domain.writeXML (updateManyAttrsByPath [ |
| @@ -16,8 +17,8 @@ with flakeInputs.nixVirt.lib; | |||
| 16 | memory = { count = 16; unit = "GiB"; }; | 17 | memory = { count = 16; unit = "GiB"; }; |
| 17 | storage_vol = "/home/gkleen/.local/share/libvirt/images/lmmirzm-vmrz01.qcow2"; | 18 | storage_vol = "/home/gkleen/.local/share/libvirt/images/lmmirzm-vmrz01.qcow2"; |
| 18 | nvram_path = "/home/gkleen/.local/share/libvirt/lmmirzm-vmrz01.nvram"; | 19 | nvram_path = "/home/gkleen/.local/share/libvirt/lmmirzm-vmrz01.nvram"; |
| 19 | virtio_drive = false; | 20 | virtio_drive = true; |
| 20 | virtio_video = false; | 21 | virtio_video = true; |
| 21 | install_virtio = false; | 22 | install_virtio = false; |
| 22 | }) { | 23 | }) { |
| 23 | qemu-commandline.env = [ | 24 | qemu-commandline.env = [ |
| @@ -33,11 +34,12 @@ with flakeInputs.nixVirt.lib; | |||
| 33 | os.bootmenu.enable = true; | 34 | os.bootmenu.enable = true; |
| 34 | devices.graphics = { | 35 | devices.graphics = { |
| 35 | listen.type = "address"; | 36 | listen.type = "address"; |
| 36 | # gl.enable = true; | 37 | gl.enable = false; |
| 37 | }; | 38 | }; |
| 39 | devices.video.model.acceleration.accel3d = false; | ||
| 38 | devices.interface = { | 40 | devices.interface = { |
| 39 | # model.type = "virtio"; | 41 | model.type = "virtio"; |
| 40 | model.type = "e1000e"; | 42 | # model.type = "e1000e"; |
| 41 | type = "bridge"; | 43 | type = "bridge"; |
| 42 | mac.address = "52:54:00:b9:f3:ed"; | 44 | mac.address = "52:54:00:b9:f3:ed"; |
| 43 | source.bridge = "rz-0971"; | 45 | source.bridge = "rz-0971"; |
| @@ -47,6 +49,15 @@ with flakeInputs.nixVirt.lib; | |||
| 47 | type = "unix"; | 49 | type = "unix"; |
| 48 | target = { type = "virtio"; name = "org.qemu.guest_agent.0"; }; | 50 | target = { type = "virtio"; name = "org.qemu.guest_agent.0"; }; |
| 49 | } | 51 | } |
| 52 | { | ||
| 53 | type = "spicevmc"; | ||
| 54 | target = { type = "virtio"; name = "com.redhat.spice.0"; }; | ||
| 55 | } | ||
| 56 | { | ||
| 57 | type = "spiceport"; | ||
| 58 | target = { type = "virtio"; name = "org.spice-space.webdav.0"; }; | ||
| 59 | source.channel = "org.spice-space.webdav.0"; | ||
| 60 | } | ||
| 50 | ]; | 61 | ]; |
| 51 | devices.tpm.model = "tpm-tis"; | 62 | devices.tpm.model = "tpm-tis"; |
| 52 | })); | 63 | })); |
diff --git a/accounts/gkleen@sif/niri/default.nix b/accounts/gkleen@sif/niri/default.nix new file mode 100644 index 00000000..5ae372c1 --- /dev/null +++ b/accounts/gkleen@sif/niri/default.nix | |||
| @@ -0,0 +1,978 @@ | |||
| 1 | { config, hostConfig, pkgs, lib, flakeInputs, ... }: | ||
| 2 | let | ||
| 3 | cfg = config.programs.niri; | ||
| 4 | |||
| 5 | kdl = flakeInputs.niri-flake.lib.kdl; | ||
| 6 | sleaf = name: arg: kdl.node name [arg] []; | ||
| 7 | |||
| 8 | niri = cfg.package; | ||
| 9 | terminal = lib.getExe config.programs.kitty.package; | ||
| 10 | |||
| 11 | focus_or_spawn = pkgs.writeShellApplication { | ||
| 12 | name = "focus-or-spawn"; | ||
| 13 | runtimeInputs = [ niri pkgs.gojq pkgs.gnugrep pkgs.socat ]; | ||
| 14 | text = '' | ||
| 15 | window_select="$1" | ||
| 16 | shift | ||
| 17 | workspace_name="$1" | ||
| 18 | shift | ||
| 19 | |||
| 20 | workspaces_json="$(niri msg -j workspaces)" | ||
| 21 | workspace_output="$(jq -r --arg workspace_name "$workspace_name" '.[] | select(.name == $workspace_name) | .output' <<<"$workspaces_json")" | ||
| 22 | # active_workspace="$(jq -r --arg workspace_output "$workspace_output" '.[] | select(.output == $workspace_output and .is_active) | .id' <<<"$workspaces_json")" | ||
| 23 | active_output="$(jq -r '.[] | select(.is_focused) | .output' <<<"$workspaces_json")" | ||
| 24 | if [[ $workspace_output != "$active_output" ]]; then | ||
| 25 | niri msg action move-workspace-to-monitor --reference "$workspace_name" "$active_output" | ||
| 26 | # socat STDIO "$NIRI_SOCKET" <<<'{"Action":{"FocusWorkspace":{"reference":{"Id":'"''${active_workspace}"'}}}}' | ||
| 27 | # niri msg action move-workspace-to-index --reference "$workspace_name" 1 | ||
| 28 | fi | ||
| 29 | |||
| 30 | while IFS=$'\n' read -r window_json; do | ||
| 31 | if [[ -n $(jq -c "$window_select" <<<"$window_json") ]]; then | ||
| 32 | if jq -e '.is_focused' <<<"$window_json" >/dev/null; then | ||
| 33 | niri msg action focus-workspace-previous | ||
| 34 | else | ||
| 35 | if [[ $(jq -r --arg workspace_name "$workspace_name" 'map(select(.name == $workspace_name)) | .[0].is_focused' <<<"$workspaces_json") != "true" ]] && [[ $(jq -r --arg workspace_name "$workspace_name" 'map(select(.name == $workspace_name)) | .[0].id' <<<"$workspaces_json") = $(jq -r '.workspace_id' <<<"$window_json") ]]; then | ||
| 36 | niri msg action focus-workspace "$workspace_name" | ||
| 37 | else | ||
| 38 | niri msg action focus-window --id "$(jq -r '.id' <<<"$window_json")" | ||
| 39 | fi | ||
| 40 | fi | ||
| 41 | exit 0 | ||
| 42 | fi | ||
| 43 | done < <(niri msg -j windows | jq -c '.[]') | ||
| 44 | |||
| 45 | exec "$@" | ||
| 46 | ''; | ||
| 47 | }; | ||
| 48 | focus-or-spawn-action = config.lib.niri.actions.spawn (lib.getExe focus_or_spawn); | ||
| 49 | |||
| 50 | with_adjacent_workspace = pkgs.writeShellApplication { | ||
| 51 | name = "with-adjacent-workspace"; | ||
| 52 | runtimeInputs = [ niri pkgs.gojq pkgs.socat ]; | ||
| 53 | text = '' | ||
| 54 | blacklist="$1" | ||
| 55 | shift | ||
| 56 | direction="$1" | ||
| 57 | shift | ||
| 58 | action="$1" | ||
| 59 | shift | ||
| 60 | |||
| 61 | workspaces_json="$(niri msg -j workspaces)" | ||
| 62 | active_workspace="$(jq -r '.[] | select(.is_focused) | .id' <<<"$workspaces_json")" | ||
| 63 | workspace_output="$(jq -r --arg active_workspace "$active_workspace" '.[] | select(.id == ($active_workspace | tonumber)) | .output' <<<"$workspaces_json")" | ||
| 64 | workspace_idx="$(jq -r '.[] | select(.is_focused) | .idx' <<<"$workspaces_json")" | ||
| 65 | |||
| 66 | jq_script='map(select(' | ||
| 67 | case "$direction" in | ||
| 68 | down) | ||
| 69 | # shellcheck disable=SC2016 | ||
| 70 | jq_script=''${jq_script}'.idx > ($workspace_idx | tonumber)';; | ||
| 71 | up) | ||
| 72 | # shellcheck disable=SC2016 | ||
| 73 | jq_script=''${jq_script}'.idx < ($workspace_idx | tonumber)';; | ||
| 74 | esac | ||
| 75 | # shellcheck disable=SC2016 | ||
| 76 | jq_script=''${jq_script}' and .output == $workspace_output and ((.name == null) or (.name | test($blacklist) | not)))) | sort_by(.idx)' | ||
| 77 | [[ $direction == "up" ]] && jq_script=''${jq_script}' | reverse' | ||
| 78 | jq_script=''${jq_script}' | .[0]' | ||
| 79 | |||
| 80 | workspace_json=$(jq -c --arg blacklist "$blacklist" --arg workspace_output "$workspace_output" --arg workspace_idx "$workspace_idx" "$jq_script" <<<"$workspaces_json") | ||
| 81 | [[ -n $workspace_json && $workspace_json != null ]] || exit 0 | ||
| 82 | jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET" | ||
| 83 | ''; | ||
| 84 | }; | ||
| 85 | with-adjacent-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_adjacent_workspace) "^${lib.concatMapStringsSep "|" ({ name, ...}: name) cfg.scratchspaces}$"; | ||
| 86 | focus-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}''; | ||
| 87 | move-column-to-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}, "focus": true}}}''; | ||
| 88 | |||
| 89 | with_unnamed_workspace = pkgs.writeShellApplication { | ||
| 90 | name = "with-unnamed-workspace"; | ||
| 91 | runtimeInputs = [ niri pkgs.gojq pkgs.socat ]; | ||
| 92 | text = '' | ||
| 93 | action="$1" | ||
| 94 | shift | ||
| 95 | |||
| 96 | workspaces_json="$(niri msg -j workspaces)" | ||
| 97 | active_output="$(jq -r '.[] | select(.is_focused) | .output' <<<"$workspaces_json")" | ||
| 98 | active_workspace="$(jq -r '.[] | select(.is_focused) | .id' <<<"$workspaces_json")" | ||
| 99 | |||
| 100 | history_json="$(socat STDIO UNIX-CONNECT:"$XDG_RUNTIME_DIR"/niri-workspace-history.sock)" | ||
| 101 | workspace_json="$(jq -c --arg active_output "$active_output" --argjson history "$history_json" 'map(select(.output == $active_output and .name == null)) | map({"value": ., "history_idx": ((. as $workspace | ($history[$active_output] | index($workspace | .id))) as $active_idx | if $active_idx then $active_idx else ($history[$active_output] | length) + 1 end)}) | sort_by(.history_idx, .value.idx) | map(.value) | .[0]' <<<"$workspaces_json")" | ||
| 102 | [[ -n $workspace_json && $workspace_json != null ]] || exit 0 | ||
| 103 | jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET" | ||
| 104 | ''; | ||
| 105 | }; | ||
| 106 | with-unnamed-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_unnamed_workspace); | ||
| 107 | |||
| 108 | with_empty_unnamed_workspace = pkgs.writeShellApplication { | ||
| 109 | name = "with-empty-unnamed-workspace"; | ||
| 110 | runtimeInputs = [ niri pkgs.gojq pkgs.socat ]; | ||
| 111 | text = '' | ||
| 112 | action="$1" | ||
| 113 | shift | ||
| 114 | |||
| 115 | workspaces_json="$(niri msg -j workspaces)" | ||
| 116 | active_output="$(jq '.[] | select(.is_focused) | .output' <<<"$workspaces_json")" | ||
| 117 | target_workspace_id="$(jq --argjson active_output "$active_output" 'map(select(.active_window_id == null and .name == null and .output == $active_output)) | sort_by(.idx) | .[0].id' <<<"$workspaces_json")" | ||
| 118 | jq --argjson workspace_id "$target_workspace_id" -nc "$action" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET" | ||
| 119 | ''; | ||
| 120 | }; | ||
| 121 | with-empty-unnamed-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_empty_unnamed_workspace); | ||
| 122 | |||
| 123 | with_select_window = pkgs.writeShellApplication { | ||
| 124 | name = "with-select-window"; | ||
| 125 | runtimeInputs = [ niri pkgs.gojq pkgs.socat config.programs.fuzzel.package pkgs.gawk ]; | ||
| 126 | text = '' | ||
| 127 | window_select="$1" | ||
| 128 | shift | ||
| 129 | action="$1" | ||
| 130 | shift | ||
| 131 | |||
| 132 | windows_json="$(niri msg -j windows)" | ||
| 133 | active_workspace="$(jq -r '.[] | select(.is_focused) | .workspace_id' <<<"$windows_json")" | ||
| 134 | window_ix="$(gojq -r --arg active_workspace "$active_workspace" '.[] | select('"$window_select"') | "\(.title)\u0000icon\u001f\(.app_id)"' <<<"$windows_json" | fuzzel --width=60 --log-level=warning --dmenu --index)" | ||
| 135 | # shellcheck disable=SC2016 | ||
| 136 | window_json="$(gojq -rc --arg active_workspace "$active_workspace" --arg window_ix "$window_ix" 'map(select('"$window_select"')) | .[($window_ix | tonumber)]' <<<"$windows_json")" | ||
| 137 | |||
| 138 | [[ -z "$window_json" ]] && exit 1 | ||
| 139 | |||
| 140 | jq -c "$action" <<<"$window_json" | socat STDIO "$NIRI_SOCKET" | ||
| 141 | ''; | ||
| 142 | }; | ||
| 143 | with-select-window-action = config.lib.niri.actions.spawn (lib.getExe with_select_window); | ||
| 144 | |||
| 145 | with_predicate_window = pred: pkgs.writeShellApplication { | ||
| 146 | name = "with-predicate-window"; | ||
| 147 | runtimeInputs = [ niri pkgs.gojq pkgs.socat ]; | ||
| 148 | text = '' | ||
| 149 | action="$1" | ||
| 150 | shift | ||
| 151 | |||
| 152 | windows_json="$(niri msg -j windows)" | ||
| 153 | window_json="$(gojq -rc 'map(select(${pred})) | .[0]' <<<"$windows_json")" | ||
| 154 | |||
| 155 | [[ -z "$window_json" || $window_json = "null" ]] && exit 1 | ||
| 156 | |||
| 157 | jq -c "$action" <<<"$window_json" | socat STDIO "$NIRI_SOCKET" | ||
| 158 | ''; | ||
| 159 | }; | ||
| 160 | |||
| 161 | with-urgent-window-action = config.lib.niri.actions.spawn (lib.getExe (with_predicate_window ".is_urgent")); | ||
| 162 | with-focused-window-action = config.lib.niri.actions.spawn (lib.getExe (with_predicate_window ".is_focused")); | ||
| 163 | in { | ||
| 164 | options = { | ||
| 165 | programs.niri.scratchspaces = lib.mkOption { | ||
| 166 | type = lib.types.listOf (lib.types.submodule ({ config, ... }: { | ||
| 167 | options = { | ||
| 168 | name = lib.mkOption { | ||
| 169 | type = lib.types.str; | ||
| 170 | }; | ||
| 171 | match = lib.mkOption { | ||
| 172 | type = lib.types.listOf (lib.types.attrsOf kdl.types.kdl-args); | ||
| 173 | default = []; | ||
| 174 | }; | ||
| 175 | exclude = lib.mkOption { | ||
| 176 | type = lib.types.listOf (lib.types.attrsOf kdl.types.kdl-args); | ||
| 177 | default = []; | ||
| 178 | }; | ||
| 179 | windowRuleExtra = lib.mkOption { | ||
| 180 | type = kdl.types.kdl-nodes; | ||
| 181 | default = []; | ||
| 182 | }; | ||
| 183 | key = lib.mkOption { | ||
| 184 | type = lib.types.nullOr lib.types.str; | ||
| 185 | default = null; | ||
| 186 | }; | ||
| 187 | moveKey = lib.mkOption { | ||
| 188 | type = lib.types.nullOr lib.types.str; | ||
| 189 | default = let | ||
| 190 | keys = lib.splitString "+" config.key; | ||
| 191 | defMoveKey = lib.concatStringsSep "+" (lib.flatten [ | ||
| 192 | (lib.take (lib.length keys - 1) keys) | ||
| 193 | ["Shift"] | ||
| 194 | (lib.takeEnd 1 keys) | ||
| 195 | ]); | ||
| 196 | in if config.key == null then null else defMoveKey; | ||
| 197 | }; | ||
| 198 | spawn = lib.mkOption { | ||
| 199 | type = lib.types.nullOr (lib.types.listOf lib.types.str); | ||
| 200 | default = null; | ||
| 201 | }; | ||
| 202 | app-id = lib.mkOption { | ||
| 203 | type = lib.types.nullOr lib.types.str; | ||
| 204 | default = null; | ||
| 205 | }; | ||
| 206 | selector = lib.mkOption { | ||
| 207 | type = lib.types.nullOr lib.types.str; | ||
| 208 | default = null; | ||
| 209 | }; | ||
| 210 | }; | ||
| 211 | |||
| 212 | config = lib.mkMerge [ | ||
| 213 | (lib.mkIf (config.app-id != null) { | ||
| 214 | match = lib.mkDefault [ { app-id = "^${lib.escapeRegex config.app-id}$"; } ]; | ||
| 215 | selector = lib.mkDefault "select(.app_id == \"${config.app-id}\")"; | ||
| 216 | }) | ||
| 217 | ]; | ||
| 218 | })); | ||
| 219 | default = []; | ||
| 220 | }; | ||
| 221 | }; | ||
| 222 | |||
| 223 | config = { | ||
| 224 | home.packages = [ pkgs.xwayland-satellite-unstable ]; | ||
| 225 | |||
| 226 | systemd.user.sockets.niri-workspace-history = { | ||
| 227 | Socket = { | ||
| 228 | ListenStream = "%t/niri-workspace-history.sock"; | ||
| 229 | SocketMode = "0600"; | ||
| 230 | }; | ||
| 231 | }; | ||
| 232 | systemd.user.services.niri-workspace-history = { | ||
| 233 | Unit = { | ||
| 234 | BindsTo = [ "niri.service" ]; | ||
| 235 | After = [ "niri.service" ]; | ||
| 236 | }; | ||
| 237 | Install = { | ||
| 238 | WantedBy = [ "niri.service" ]; | ||
| 239 | }; | ||
| 240 | Service = { | ||
| 241 | Type = "simple"; | ||
| 242 | Sockets = [ "niri-workspace-history.socket" ]; | ||
| 243 | ExecStart = pkgs.writers.writePython3 "niri-workspace-history" { flakeIgnore = ["E501"]; } '' | ||
| 244 | import os | ||
| 245 | import socket | ||
| 246 | import json | ||
| 247 | # import sys | ||
| 248 | from collections import defaultdict | ||
| 249 | from threading import Thread, Lock | ||
| 250 | from socketserver import StreamRequestHandler, ThreadingTCPServer | ||
| 251 | from contextlib import contextmanager | ||
| 252 | from io import TextIOWrapper | ||
| 253 | |||
| 254 | |||
| 255 | @contextmanager | ||
| 256 | def detaching(thing): | ||
| 257 | try: | ||
| 258 | yield thing | ||
| 259 | finally: | ||
| 260 | thing.detach() | ||
| 261 | |||
| 262 | |||
| 263 | workspace_history = defaultdict(list) | ||
| 264 | history_lock = Lock() | ||
| 265 | |||
| 266 | |||
| 267 | def monitor_niri(): | ||
| 268 | workspaces = list() | ||
| 269 | |||
| 270 | def focus_workspace(output, workspace): | ||
| 271 | with history_lock: | ||
| 272 | workspace_history[output] = [workspace] + [ws for ws in workspace_history[output] if ws != workspace] | ||
| 273 | # print(json.dumps(workspace_history), file=sys.stderr) | ||
| 274 | |||
| 275 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | ||
| 276 | sock.connect(os.environ["NIRI_SOCKET"]) | ||
| 277 | sock.send(b"\"EventStream\"\n") | ||
| 278 | for line in sock.makefile(buffering=1, encoding='utf-8'): | ||
| 279 | if line_json := json.loads(line): | ||
| 280 | if "WorkspacesChanged" in line_json: | ||
| 281 | workspaces = line_json["WorkspacesChanged"]["workspaces"] | ||
| 282 | for ws in workspaces: | ||
| 283 | if ws["is_focused"]: | ||
| 284 | focus_workspace(ws["output"], ws["id"]) | ||
| 285 | if "WorkspaceActivated" in line_json: | ||
| 286 | for ws in workspaces: | ||
| 287 | if ws["id"] != line_json["WorkspaceActivated"]["id"]: | ||
| 288 | continue | ||
| 289 | focus_workspace(ws["output"], ws["id"]) | ||
| 290 | break | ||
| 291 | |||
| 292 | |||
| 293 | class RequestHandler(StreamRequestHandler): | ||
| 294 | def handle(self): | ||
| 295 | with detaching(TextIOWrapper(self.wfile, encoding='utf-8', write_through=True)) as out: | ||
| 296 | with history_lock: | ||
| 297 | json.dump(workspace_history, out) | ||
| 298 | |||
| 299 | |||
| 300 | class Server(ThreadingTCPServer): | ||
| 301 | def __init__(self): | ||
| 302 | ThreadingTCPServer.__init__(self, ("", 8000), RequestHandler, bind_and_activate=False) | ||
| 303 | self.socket = socket.fromfd(3, self.address_family, self.socket_type) | ||
| 304 | |||
| 305 | |||
| 306 | def run_server(): | ||
| 307 | with Server() as server: | ||
| 308 | server.serve_forever() | ||
| 309 | |||
| 310 | |||
| 311 | niri = Thread(target=monitor_niri) | ||
| 312 | niri.daemon = True | ||
| 313 | niri.start() | ||
| 314 | |||
| 315 | server_thread = Thread(target=run_server) | ||
| 316 | server_thread.daemon = True | ||
| 317 | server_thread.start() | ||
| 318 | |||
| 319 | while True: | ||
| 320 | server_thread.join(timeout=0.5) | ||
| 321 | niri.join(timeout=0.5) | ||
| 322 | |||
| 323 | if not (niri.is_alive() and server_thread.is_alive()): | ||
| 324 | break | ||
| 325 | ''; | ||
| 326 | }; | ||
| 327 | }; | ||
| 328 | systemd.user.services.niri-workspace-sort = { | ||
| 329 | Unit = { | ||
| 330 | BindsTo = [ "niri.service" ]; | ||
| 331 | After = [ "niri.service" ]; | ||
| 332 | }; | ||
| 333 | Install = { | ||
| 334 | WantedBy = [ "niri.service" ]; | ||
| 335 | }; | ||
| 336 | Service = { | ||
| 337 | Type = "simple"; | ||
| 338 | ExecStart = pkgs.writers.writePython3 "niri-workspace-sort" { flakeIgnore = ["E501"]; } '' | ||
| 339 | import os | ||
| 340 | import sys | ||
| 341 | import socket | ||
| 342 | import json | ||
| 343 | |||
| 344 | outputs = None | ||
| 345 | only = {'HDMI-A-1': {'bmr'}, 'eDP-1': {'vid'}} | ||
| 346 | |||
| 347 | |||
| 348 | class Niri(socket.socket): | ||
| 349 | def __init__(self): | ||
| 350 | super().__init__(socket.AF_UNIX, socket.SOCK_STREAM) | ||
| 351 | super().connect(os.environ["NIRI_SOCKET"]) | ||
| 352 | self.fh = super().makefile(mode='rw', buffering=1, encoding='utf-8') | ||
| 353 | |||
| 354 | def cmd(self, obj): | ||
| 355 | print(json.dumps(obj, separators=(',', ':')), flush=True, file=self.fh) | ||
| 356 | |||
| 357 | def event_stream(self): | ||
| 358 | self.cmd("EventStream") | ||
| 359 | return self.fh | ||
| 360 | |||
| 361 | |||
| 362 | with Niri() as niri, Niri().event_stream() as niri_stream: | ||
| 363 | for line in niri_stream: | ||
| 364 | workspaces = None | ||
| 365 | if line_json := json.loads(line): | ||
| 366 | if "WorkspacesChanged" in line_json: | ||
| 367 | workspaces = line_json["WorkspacesChanged"]["workspaces"] | ||
| 368 | |||
| 369 | if workspaces is None: | ||
| 370 | continue | ||
| 371 | |||
| 372 | old_outputs = outputs | ||
| 373 | outputs = {ws["output"] for ws in workspaces} | ||
| 374 | if old_outputs is None: | ||
| 375 | print("Initial outputs: {}".format(outputs), file=sys.stderr) | ||
| 376 | continue | ||
| 377 | |||
| 378 | new_outputs = outputs - old_outputs | ||
| 379 | if not new_outputs: | ||
| 380 | continue | ||
| 381 | print("New outputs: {}".format(new_outputs), file=sys.stderr) | ||
| 382 | |||
| 383 | relevant_workspaces = list(filter(lambda ws: (ws["name"] is not None) or (ws["active_window_id"] is not None), workspaces)) | ||
| 384 | target_output = next(iter(outputs - set(only.keys()))) | ||
| 385 | if not target_output: | ||
| 386 | continue | ||
| 387 | for ws in relevant_workspaces: | ||
| 388 | ws_ident = ws["name"] if ws["name"] is not None else (ws["output"], ws["idx"]) | ||
| 389 | if ws["output"] not in set(only.keys()): | ||
| 390 | continue | ||
| 391 | if ws_ident in only[ws["output"]]: | ||
| 392 | continue | ||
| 393 | |||
| 394 | print("{} -> {}".format(ws_ident, target_output), file=sys.stderr) | ||
| 395 | niri.cmd({"Action": {"MoveWorkspaceToMonitor": {"reference": {"Id": ws["id"]}, "output": target_output}}}) | ||
| 396 | ''; | ||
| 397 | Restart = "on-failure"; | ||
| 398 | RestartSec = 10; | ||
| 399 | }; | ||
| 400 | }; | ||
| 401 | |||
| 402 | programs.niri.scratchspaces = [ | ||
| 403 | { name = "pwctl"; | ||
| 404 | key = "Mod+Control+A"; | ||
| 405 | spawn = ["pwvucontrol"]; | ||
| 406 | app-id = "com.saivert.pwvucontrol"; | ||
| 407 | } | ||
| 408 | { name = "kpxc"; | ||
| 409 | exclude = [ | ||
| 410 | { title = "^Unlock Database.*"; } | ||
| 411 | { title = "^Access Request.*"; } | ||
| 412 | { title = ".*Passkey credentials$"; } | ||
| 413 | ]; | ||
| 414 | windowRuleExtra = with kdl; [ | ||
| 415 | (sleaf "open-focused" false) | ||
| 416 | ]; | ||
| 417 | key = "Mod+Control+P"; | ||
| 418 | app-id = "org.keepassxc.KeePassXC"; | ||
| 419 | spawn = [ "keepassxc" ]; | ||
| 420 | } | ||
| 421 | { name = "bmgr"; | ||
| 422 | key = "Mod+Control+B"; | ||
| 423 | app-id = ".blueman-manager-wrapped"; | ||
| 424 | spawn = [ "blueman-manager" ]; | ||
| 425 | } | ||
| 426 | { name = "term"; | ||
| 427 | key = "Mod+Control+Return"; | ||
| 428 | app-id = "kitty-scratch"; | ||
| 429 | spawn = [ "kitty" "--app-id" "kitty-scratch" ]; | ||
| 430 | } | ||
| 431 | { name = "edit"; | ||
| 432 | match = [ { title = "^scratch$"; app-id = "^emacs$"; } ]; | ||
| 433 | key = "Mod+Control+E"; | ||
| 434 | selector = "select(.app_id == \"emacs\" and .title == \"scratch\")"; | ||
| 435 | spawn = [ "emacsclient" "-c" "--frame-parameters=(quote (name . \"scratch\"))" ]; | ||
| 436 | } | ||
| 437 | { name = "eff"; | ||
| 438 | key = "Mod+Control+O"; | ||
| 439 | app-id = "com.github.wwmm.easyeffects"; | ||
| 440 | spawn = [ "easyeffects" ]; | ||
| 441 | } | ||
| 442 | { name = "time"; | ||
| 443 | key = "Mod+Control+K"; | ||
| 444 | app-id = "chrome-kimai.yggdrasil.li__-Default"; | ||
| 445 | spawn = [ (toString (pkgs.resholve.writeScript "kimai" { | ||
| 446 | interpreter = pkgs.runtimeShell; | ||
| 447 | inputs = [ pkgs.dex ]; | ||
| 448 | execer = [ "cannot:${lib.getExe pkgs.dex}" ]; | ||
| 449 | } '' | ||
| 450 | exec dex $HOME/.local/state/nix/profile/share/applications/kimai.desktop | ||
| 451 | '')) ]; | ||
| 452 | windowRuleExtra = with kdl; [ | ||
| 453 | (sleaf "block-out-from" "screencast") | ||
| 454 | ]; | ||
| 455 | } | ||
| 456 | ]; | ||
| 457 | programs.niri.config = | ||
| 458 | let | ||
| 459 | inherit (kdl) node plain leaf flag; | ||
| 460 | optional-node = cond: v: | ||
| 461 | if cond | ||
| 462 | then v | ||
| 463 | else null; | ||
| 464 | opt-props = lib.filterAttrs (lib.const (value: value != null)); | ||
| 465 | normalize-nodes = nodes: lib.remove null (lib.flatten nodes); | ||
| 466 | in | ||
| 467 | normalize-nodes [ | ||
| 468 | (flag "prefer-no-csd") | ||
| 469 | |||
| 470 | (sleaf "screenshot-path" "~/screenshots/%Y-%m-%dT%H:%M:%S.png") | ||
| 471 | |||
| 472 | (plain "hotkey-overlay" [ | ||
| 473 | (flag "skip-at-startup") | ||
| 474 | ]) | ||
| 475 | |||
| 476 | (plain "input" [ | ||
| 477 | (plain "keyboard" [ | ||
| 478 | (sleaf "repeat-delay" 300) | ||
| 479 | (sleaf "repeat-rate" 50) | ||
| 480 | |||
| 481 | (plain "xkb" [ | ||
| 482 | (sleaf "layout" "us,us") | ||
| 483 | (sleaf "variant" "dvp,") | ||
| 484 | (sleaf "options" "compose:caps,grp:win_space_toggle") | ||
| 485 | ]) | ||
| 486 | ]) | ||
| 487 | |||
| 488 | (flag "workspace-auto-back-and-forth") | ||
| 489 | # (sleaf "focus-follows-mouse" {}) | ||
| 490 | # (flag "warp-mouse-to-focus") | ||
| 491 | |||
| 492 | # (plain "touchpad" [ (flag "off") ]) | ||
| 493 | (plain "trackball" [ | ||
| 494 | (sleaf "scroll-method" "on-button-down") | ||
| 495 | (sleaf "scroll-button" 278) | ||
| 496 | ]) | ||
| 497 | (plain "touch" [ | ||
| 498 | (sleaf "map-to-output" "eDP-1") | ||
| 499 | ]) | ||
| 500 | ]) | ||
| 501 | |||
| 502 | (plain "gestures" [ | ||
| 503 | (plain "hot-corners" [(flag "off")]) | ||
| 504 | ]) | ||
| 505 | |||
| 506 | (plain "environment" (lib.mapAttrsToList sleaf { | ||
| 507 | NIXOS_OZONE_WL = "1"; | ||
| 508 | QT_QPA_PLATFORM = "wayland"; | ||
| 509 | QT_WAYLAND_DISABLE_WINDOWDECORATION = "1"; | ||
| 510 | GDK_BACKEND = "wayland"; | ||
| 511 | SDL_VIDEODRIVER = "wayland"; | ||
| 512 | DISPLAY = ":0"; | ||
| 513 | ELECTRON_OZONE_PLATFORM_HINT = "auto"; | ||
| 514 | SSH_ASKPASS_REQUIRE = "prefer"; | ||
| 515 | SSH_ASKPASS = lib.getExe pkgs.kdePackages.ksshaskpass; | ||
| 516 | SUDO_ASKPASS = lib.getExe pkgs.kdePackages.ksshaskpass; | ||
| 517 | })) | ||
| 518 | |||
| 519 | (node "output" ["eDP-1"] [ | ||
| 520 | (sleaf "scale" 1.5) | ||
| 521 | (sleaf "position" { x = 0; y = 0; }) | ||
| 522 | ]) | ||
| 523 | (node "output" ["Ancor Communications Inc ASUS PB287Q 0x0000DD9B"] [ | ||
| 524 | (sleaf "scale" 1.5) | ||
| 525 | (sleaf "position" { x = 2560; y = 0; }) | ||
| 526 | ]) | ||
| 527 | (node "output" ["HP Inc. HP 727pu CN4417143K"] [ | ||
| 528 | (sleaf "mode" "2560x1440@119.998") | ||
| 529 | (sleaf "scale" 1) | ||
| 530 | (sleaf "position" { x = 2560; y = 0; }) | ||
| 531 | (flag "variable-refresh-rate") | ||
| 532 | ]) | ||
| 533 | |||
| 534 | (plain "debug" [ | ||
| 535 | (sleaf "render-drm-device" "/dev/dri/by-path/pci-0000:00:02.0-render") | ||
| 536 | ]) | ||
| 537 | |||
| 538 | (plain "animations" [ | ||
| 539 | (sleaf "slowdown" 0.5) | ||
| 540 | (plain "workspace-switch" [(flag "off")]) | ||
| 541 | ]) | ||
| 542 | |||
| 543 | (plain "layout" [ | ||
| 544 | (sleaf "gaps" 8) | ||
| 545 | (plain "struts" [ | ||
| 546 | (sleaf "left" 26) | ||
| 547 | (sleaf "right" 26) | ||
| 548 | (sleaf "top" 0) | ||
| 549 | (sleaf "bottom" 0) | ||
| 550 | ]) | ||
| 551 | (plain "border" [ | ||
| 552 | (sleaf "width" 2) | ||
| 553 | (sleaf "active-gradient" { | ||
| 554 | from = "hsla(195 100% 45% 1)"; | ||
| 555 | to = "hsla(155 100% 37.5% 1)"; | ||
| 556 | angle = 29; | ||
| 557 | relative-to = "workspace-view"; | ||
| 558 | }) | ||
| 559 | (sleaf "inactive-gradient" { | ||
| 560 | from = "hsla(0 0% 27.7% 1)"; | ||
| 561 | to = "hsla(0 0% 23% 1)"; | ||
| 562 | angle = 29; | ||
| 563 | relative-to = "workspace-view"; | ||
| 564 | }) | ||
| 565 | ]) | ||
| 566 | (plain "focus-ring" [ | ||
| 567 | (flag "off") | ||
| 568 | ]) | ||
| 569 | |||
| 570 | (plain "preset-column-widths" (map (prop: sleaf "proportion" prop) [ | ||
| 571 | (1. / 4.) (1. / 3.) (1. / 2.) (2. / 3.) (3. / 4.) (1.) | ||
| 572 | ])) | ||
| 573 | (plain "default-column-width" [ (sleaf "proportion" (1. / 2.)) ]) | ||
| 574 | (plain "preset-window-heights" (map (prop: sleaf "proportion" prop) [ | ||
| 575 | (1. / 3.) (1. / 2.) (2. / 3.) (1.) | ||
| 576 | ])) | ||
| 577 | |||
| 578 | (flag "always-center-single-column") | ||
| 579 | |||
| 580 | (plain "tab-indicator" [ | ||
| 581 | (sleaf "gap" 4) | ||
| 582 | (sleaf "width" 8) | ||
| 583 | (sleaf "gaps-between-tabs" 4) | ||
| 584 | (flag "place-within-column") | ||
| 585 | (sleaf "length" { total-proportion = 1.; }) | ||
| 586 | (sleaf "active-gradient" { | ||
| 587 | from = "hsla(195 100% 60% 0.75)"; | ||
| 588 | to = "hsla(155 100% 50% 0.75)"; | ||
| 589 | angle = 29; | ||
| 590 | relative-to = "workspace-view"; | ||
| 591 | }) | ||
| 592 | (sleaf "inactive-gradient" { | ||
| 593 | from = "hsla(0 0% 42% 0.66)"; | ||
| 594 | to = "hsla(0 0% 35% 0.66)"; | ||
| 595 | angle = 29; | ||
| 596 | relative-to = "workspace-view"; | ||
| 597 | }) | ||
| 598 | ]) | ||
| 599 | ]) | ||
| 600 | |||
| 601 | (plain "cursor" [ | ||
| 602 | (flag "hide-when-typing") | ||
| 603 | ]) | ||
| 604 | |||
| 605 | (map (name: | ||
| 606 | (node "workspace" [name] [ | ||
| 607 | (sleaf "open-on-output" "eDP-1") | ||
| 608 | ]) | ||
| 609 | ) (map ({name, ...}: name) cfg.scratchspaces)) | ||
| 610 | (map (name: | ||
| 611 | (sleaf "workspace" name) | ||
| 612 | ) ["comm" "web" "vid" "bmr"]) | ||
| 613 | |||
| 614 | (plain "window-rule" [ | ||
| 615 | (sleaf "clip-to-geometry" true) | ||
| 616 | ]) | ||
| 617 | |||
| 618 | (plain "window-rule" [ | ||
| 619 | (sleaf "match" { is-floating = true; }) | ||
| 620 | (sleaf "geometry-corner-radius" 8) | ||
| 621 | (plain "shadow" [ (flag "on") ]) | ||
| 622 | ]) | ||
| 623 | |||
| 624 | (plain "window-rule" [ | ||
| 625 | (sleaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; }) | ||
| 626 | (sleaf "block-out-from" "screencast") | ||
| 627 | ]) | ||
| 628 | (plain "window-rule" (normalize-nodes [ | ||
| 629 | (map (title: | ||
| 630 | (sleaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; inherit title; }) | ||
| 631 | ) ["^Unlock Database.*" "^Access Request.*" ".*Passkey credentials$" "Browser Access Request$"]) | ||
| 632 | (sleaf "open-focused" true) | ||
| 633 | (sleaf "open-floating" true) | ||
| 634 | ])) | ||
| 635 | |||
| 636 | (map ({ name, match, exclude, windowRuleExtra, ... }: | ||
| 637 | (optional-node (match != []) (plain "window-rule" (normalize-nodes [ | ||
| 638 | (map (sleaf "match") match) | ||
| 639 | (map (sleaf "exclude") exclude) | ||
| 640 | (sleaf "open-on-workspace" name) | ||
| 641 | (sleaf "open-maximized" true) | ||
| 642 | windowRuleExtra | ||
| 643 | ]))) | ||
| 644 | ) cfg.scratchspaces) | ||
| 645 | |||
| 646 | (plain "window-rule" [ | ||
| 647 | (sleaf "match" { app-id = "^emacs$"; }) | ||
| 648 | (sleaf "match" { app-id = "^firefox$"; }) | ||
| 649 | (plain "default-column-width" [(sleaf "proportion" (2. / 3.))]) | ||
| 650 | ]) | ||
| 651 | (plain "window-rule" [ | ||
| 652 | (sleaf "match" { app-id = "^kitty$"; }) | ||
| 653 | (sleaf "match" { app-id = "^kitty-play$"; }) | ||
| 654 | (plain "default-column-width" [(sleaf "proportion" (1. / 3.))]) | ||
| 655 | ]) | ||
| 656 | |||
| 657 | (plain "window-rule" [ | ||
| 658 | (sleaf "match" { app-id = "^thunderbird$"; }) | ||
| 659 | (sleaf "match" { app-id = "^Element$"; }) | ||
| 660 | (sleaf "match" { app-id = "^chrome-web\.openrainbow\.com__-Default$"; }) | ||
| 661 | (sleaf "open-on-workspace" "comm") | ||
| 662 | ]) | ||
| 663 | (plain "window-rule" [ | ||
| 664 | (sleaf "match" { app-id = "^firefox$"; }) | ||
| 665 | (sleaf "open-on-workspace" "web") | ||
| 666 | (sleaf "open-maximized" true) | ||
| 667 | ]) | ||
| 668 | (plain "window-rule" [ | ||
| 669 | (sleaf "match" { app-id = "^mpv$"; }) | ||
| 670 | (sleaf "open-on-workspace" "vid") | ||
| 671 | (plain "default-column-width" [(sleaf "proportion" 1.)]) | ||
| 672 | ]) | ||
| 673 | (plain "window-rule" [ | ||
| 674 | (sleaf "match" { app-id = "^kitty-play$"; }) | ||
| 675 | (sleaf "open-on-workspace" "vid") | ||
| 676 | (sleaf "open-focused" false) | ||
| 677 | ]) | ||
| 678 | (plain "window-rule" [ | ||
| 679 | (sleaf "match" { app-id = "^chrome-audiobookshelf\.yggdrasil\.li__-Default$"; }) | ||
| 680 | (sleaf "match" { app-id = "^YouTube Music Desktop App$"; }) | ||
| 681 | (sleaf "open-on-workspace" "vid") | ||
| 682 | ]) | ||
| 683 | (plain "window-rule" [ | ||
| 684 | (sleaf "match" { app-id = "^pdfpc$"; }) | ||
| 685 | (plain "default-column-width" [(sleaf "proportion" 1.)]) | ||
| 686 | ]) | ||
| 687 | (plain "window-rule" [ | ||
| 688 | (sleaf "match" { app-id = "^pdfpc$"; title = "^.*presentation.*$"; }) | ||
| 689 | (plain "default-column-width" [(sleaf "proportion" 1.)]) | ||
| 690 | (sleaf "open-fullscreen" true) | ||
| 691 | (sleaf "open-on-workspace" "bmr") | ||
| 692 | (sleaf "open-focused" false) | ||
| 693 | ]) | ||
| 694 | (plain "window-rule" (normalize-nodes [ | ||
| 695 | (map (sleaf "match") [ | ||
| 696 | { app-id = "^Gimp-"; title = "^Quit GIMP$"; } | ||
| 697 | { app-id = "^org\\.kde\\.polkit-kde-authentication-agent-1$"; } | ||
| 698 | { app-id = "^xdg-desktop-portal-gtk$"; } | ||
| 699 | ]) | ||
| 700 | (sleaf "open-floating" true) | ||
| 701 | ])) | ||
| 702 | (plain "window-rule" [ | ||
| 703 | (sleaf "match" { app-id = "^org\\.pwmt\\.zathura$"; }) | ||
| 704 | (sleaf "match" { app-id = "^evince$"; }) | ||
| 705 | (sleaf "match" { app-id = "^org\\.gnome\\.Papers$"; }) | ||
| 706 | (sleaf "default-column-display" "tabbed") | ||
| 707 | ]) | ||
| 708 | |||
| 709 | (plain "layer-rule" [ | ||
| 710 | (sleaf "match" { namespace = "^notifications$"; }) | ||
| 711 | (sleaf "match" { namespace = "^bar$"; }) | ||
| 712 | (sleaf "match" { namespace = "^launcher$"; }) | ||
| 713 | (sleaf "block-out-from" "screencast") | ||
| 714 | ]) | ||
| 715 | |||
| 716 | (plain "binds" | ||
| 717 | (let | ||
| 718 | bind = name: cfg: node name [(lib.removeAttrs cfg ["action"])] (lib.mapAttrsToList leaf (lib.removeAttrs cfg.action ["__functor"])); | ||
| 719 | in | ||
| 720 | normalize-nodes [ | ||
| 721 | (lib.mapAttrsToList bind (with config.lib.niri.actions; { | ||
| 722 | "Mod+Slash".action = show-hotkey-overlay; | ||
| 723 | |||
| 724 | "Mod+Return".action = spawn terminal; | ||
| 725 | "Mod+Shift+Return".action = | ||
| 726 | let | ||
| 727 | nushellKitty = pkgs.symlinkJoin { | ||
| 728 | name = "nushell-kitty"; | ||
| 729 | paths = [ config.programs.kitty.package ]; | ||
| 730 | buildInputs = [ pkgs.makeWrapper ]; | ||
| 731 | postBuild = '' | ||
| 732 | wrapProgram $out/bin/kitty \ | ||
| 733 | --add-flags "--config ${pkgs.writeText "kitty.conf" '' | ||
| 734 | include $HOME/${config.xdg.configFile."kitty/kitty.conf".target} | ||
| 735 | shell ${lib.getExe config.programs.nushell.package} | ||
| 736 | ''}" | ||
| 737 | ''; | ||
| 738 | }; | ||
| 739 | in spawn (lib.getExe' nushellKitty "kitty"); | ||
| 740 | "Mod+Q".action = close-window; | ||
| 741 | "Mod+O".action = spawn (lib.getExe config.programs.fuzzel.package); | ||
| 742 | "Mod+Shift+O".action = spawn (lib.getExe config.programs.fuzzel.package) "--list-executables-in-path"; | ||
| 743 | |||
| 744 | "Mod+Alt+E".action = spawn (lib.getExe' config.services.emacs.package "emacsclient") "-c"; | ||
| 745 | "Mod+Alt+Y".action = spawn (lib.getExe (pkgs.writeShellApplication { | ||
| 746 | name = "queue-yt-dlp"; | ||
| 747 | runtimeInputs = with pkgs; [ wl-clipboard-rs socat ]; | ||
| 748 | text = '' | ||
| 749 | socat STDIO UNIX-CONNECT:"$XDG_RUNTIME_DIR"/yt-dlp.sock <<<$'{ "urls": ["'"$(wl-paste)"$'"] }' | ||
| 750 | ''; | ||
| 751 | })); | ||
| 752 | "Mod+Alt+L".action = spawn (lib.getExe (pkgs.writeShellApplication { | ||
| 753 | name = "queue-yt-dlp"; | ||
| 754 | runtimeInputs = with pkgs; [ wl-clipboard-rs config.programs.kitty.package ]; | ||
| 755 | text = '' | ||
| 756 | exec -- kitty --app-id kitty-play --directory "$HOME"/media mpv "$(wl-paste)" | ||
| 757 | ''; | ||
| 758 | })); | ||
| 759 | "Mod+Alt+M".action = spawn (lib.getExe' pkgs.screen-message "sm") "-n" "Fira Mono" "-a" "1" "-f" "#fff" "-b" "#000"; | ||
| 760 | |||
| 761 | "Mod+U".action = spawn (lib.getExe (pkgs.writeShellApplication { | ||
| 762 | name = "qalc-fuzzel"; | ||
| 763 | runtimeInputs = with pkgs; [ wl-clipboard-rs libqalculate config.programs.fuzzel.package coreutils findutils libnotify gnugrep ]; | ||
| 764 | text = '' | ||
| 765 | RESULTS_DIR="$HOME/.cache/qalc-fuzzel" | ||
| 766 | prev() { | ||
| 767 | FOUND=false | ||
| 768 | while IFS= read -r line; do | ||
| 769 | [[ -n "$line" ]] || continue | ||
| 770 | FOUND=true | ||
| 771 | echo "$line" | ||
| 772 | done < <(export LC_ALL=C.UTF-8; echo; find "$RESULTS_DIR" -type f -printf $'%T@ %p\n' | sort -n | cut -d' ' -f2- | xargs -r cat) | ||
| 773 | $FOUND || echo | ||
| 774 | } | ||
| 775 | FUZZEL_RES=$(prev | fuzzel --dmenu --prompt "qalc> " --width=60) || exit $? | ||
| 776 | if [[ "$FUZZEL_RES" =~ .*\ =\ .* ]]; then | ||
| 777 | QALC_RES="$FUZZEL_RES" | ||
| 778 | QALC_RET=0 | ||
| 779 | else | ||
| 780 | QALC_RES=$(qalc -set "autocalc off" "$FUZZEL_RES" 2>&1) | ||
| 781 | QALC_RET=$? | ||
| 782 | fi | ||
| 783 | [[ -n "$QALC_RES" ]] || exit 1 | ||
| 784 | EXISTING=false | ||
| 785 | set +o pipefail | ||
| 786 | grep -Fxrl "$QALC_RES" "$RESULTS_DIR" | xargs -r touch | ||
| 787 | [[ ''${PIPESTATUS[0]} -eq 0 ]] && EXISTING=true | ||
| 788 | set -o pipefail | ||
| 789 | if [[ $QALC_RET -eq 0 ]] && ! $EXISTING; then | ||
| 790 | set +o pipefail | ||
| 791 | RES_FILE="$RESULTS_DIR"/$(date -uIs).$(tr -Cd 'a-zA-Z0-9' </dev/random | head -c 10) | ||
| 792 | set -o pipefail | ||
| 793 | cat >"$RES_FILE" <<<"$QALC_RES" | ||
| 794 | fi | ||
| 795 | [[ "$QALC_RES" =~ .*\ =\ (.*) ]] && QALC_RES="''${BASH_REMATCH[1]}" | ||
| 796 | [[ $QALC_RET -eq 0 ]] && wl-copy "$QALC_RES" | ||
| 797 | notify-send "$QALC_RES" | ||
| 798 | ''; | ||
| 799 | })); | ||
| 800 | "Mod+Shift+U".action = | ||
| 801 | let | ||
| 802 | qalcKitty = pkgs.symlinkJoin { | ||
| 803 | name = "qalc-kitty"; | ||
| 804 | paths = [ config.programs.kitty.package ]; | ||
| 805 | buildInputs = [ pkgs.makeWrapper ]; | ||
| 806 | postBuild = '' | ||
| 807 | wrapProgram $out/bin/kitty \ | ||
| 808 | --add-flags "--config ${pkgs.writeText "kitty.conf" '' | ||
| 809 | include $HOME/${config.xdg.configFile."kitty/kitty.conf".target} | ||
| 810 | shell ${lib.getExe pkgs.libqalculate} | ||
| 811 | ''}" | ||
| 812 | ''; | ||
| 813 | }; | ||
| 814 | in spawn (lib.getExe' qalcKitty "kitty"); | ||
| 815 | "Mod+E".action = spawn (lib.getExe (pkgs.writeShellApplication { | ||
| 816 | name = "emoji-fuzzel"; | ||
| 817 | runtimeInputs = with pkgs; [ config.programs.fuzzel.package wtype wl-clipboard-rs ]; | ||
| 818 | text = '' | ||
| 819 | FUZZEL_RES=$(fuzzel --dmenu --prompt "emoji> " --cache "$HOME"/.cache/fuzzel-emoji --width=60 <"$HOME"/.local/share/emoji-data/list.txt) || exit $? | ||
| 820 | [[ -n "$FUZZEL_RES" ]] || exit 1 | ||
| 821 | wl-copy "$(cut -d ':' -f 1 <<<"$FUZZEL_RES" | tr -d '\n')" && wtype -k XF86Paste | ||
| 822 | ''; | ||
| 823 | })); | ||
| 824 | "Print".action = screenshot; | ||
| 825 | "Control+Print".action = screenshot-window; | ||
| 826 | "Shift+Print".action = kdl.magic-leaf "screenshot-screen"; | ||
| 827 | "Mod+B".action = with-select-window-action ".workspace_id == ($active_workspace | tonumber)" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; | ||
| 828 | "Mod+Shift+B".action = with-select-window-action "true" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; | ||
| 829 | |||
| 830 | "Mod+Escape" = { | ||
| 831 | allow-inhibiting = false; | ||
| 832 | action = toggle-keyboard-shortcuts-inhibit; | ||
| 833 | }; | ||
| 834 | |||
| 835 | "Mod+H".action = focus-column-left; | ||
| 836 | "Mod+T".action = focus-window-down; | ||
| 837 | "Mod+N".action = focus-window-up; | ||
| 838 | "Mod+S".action = focus-column-right; | ||
| 839 | |||
| 840 | "Mod+Shift+H".action = move-column-left; | ||
| 841 | "Mod+Shift+T".action = move-window-down; | ||
| 842 | "Mod+Shift+N".action = move-window-up; | ||
| 843 | "Mod+Shift+S".action = move-column-right; | ||
| 844 | |||
| 845 | "Mod+Control+H".action = focus-monitor-left; | ||
| 846 | "Mod+Control+T".action = focus-monitor-down; | ||
| 847 | "Mod+Control+N".action = focus-monitor-up; | ||
| 848 | "Mod+Control+S".action = focus-monitor-right; | ||
| 849 | |||
| 850 | "Mod+Shift+Control+H".action = move-workspace-to-monitor-left; | ||
| 851 | "Mod+Shift+Control+T".action = move-workspace-to-monitor-down; | ||
| 852 | "Mod+Shift+Control+N".action = move-workspace-to-monitor-up; | ||
| 853 | "Mod+Shift+Control+S".action = move-workspace-to-monitor-right; | ||
| 854 | |||
| 855 | "Mod+G".action = focus-adjacent-workspace "down"; | ||
| 856 | "Mod+C".action = focus-adjacent-workspace "up"; | ||
| 857 | |||
| 858 | "Mod+Shift+G".action = move-column-to-adjacent-workspace "down"; | ||
| 859 | "Mod+Shift+C".action = move-column-to-adjacent-workspace "up"; | ||
| 860 | |||
| 861 | "Mod+Shift+Control+G".action = move-workspace-down; | ||
| 862 | "Mod+Shift+Control+C".action = move-workspace-up; | ||
| 863 | |||
| 864 | "Mod+ParenLeft".action = focus-workspace "comm"; | ||
| 865 | "Mod+Shift+ParenLeft".action = kdl.magic-leaf "move-column-to-workspace" "comm"; | ||
| 866 | |||
| 867 | "Mod+ParenRight".action = focus-workspace "web"; | ||
| 868 | "Mod+Shift+ParenRight".action = kdl.magic-leaf "move-column-to-workspace" "web"; | ||
| 869 | |||
| 870 | "Mod+BraceRight".action = focus-workspace "read"; | ||
| 871 | "Mod+Shift+BraceRight".action = kdl.magic-leaf "move-column-to-workspace" "read"; | ||
| 872 | |||
| 873 | "Mod+BraceLeft".action = focus-workspace "mon"; | ||
| 874 | "Mod+Shift+BraceLeft".action = kdl.magic-leaf "move-column-to-workspace" "mon"; | ||
| 875 | |||
| 876 | "Mod+Asterisk".action = focus-workspace "vid"; | ||
| 877 | "Mod+Shift+Asterisk".action = kdl.magic-leaf "move-column-to-workspace" "vid"; | ||
| 878 | |||
| 879 | "Mod+Plus".action = with-unnamed-workspace-action ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}''; | ||
| 880 | "Mod+Shift+Plus".action = with-unnamed-workspace-action ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}, "focus": true}}}''; | ||
| 881 | |||
| 882 | "Mod+M".action = consume-or-expel-window-left; | ||
| 883 | "Mod+W".action = consume-or-expel-window-right; | ||
| 884 | |||
| 885 | "Mod+Shift+M".action = toggle-column-tabbed-display; | ||
| 886 | |||
| 887 | "Mod+R".action = switch-preset-column-width; | ||
| 888 | "Mod+Shift+R".action = maximize-column; | ||
| 889 | "Mod+Shift+Ctrl+R".action = switch-preset-window-height; | ||
| 890 | "Mod+F".action = center-column; | ||
| 891 | "Mod+Shift+F".action = toggle-windowed-fullscreen; | ||
| 892 | "Mod+Ctrl+Shift+F".action = fullscreen-window; | ||
| 893 | |||
| 894 | "Mod+V".action = switch-focus-between-floating-and-tiling; | ||
| 895 | "Mod+Shift+V".action = toggle-window-floating; | ||
| 896 | |||
| 897 | "Mod+Left".action = set-column-width "-10%"; | ||
| 898 | "Mod+Down".action = set-window-height "-10%"; | ||
| 899 | "Mod+Up".action = set-window-height "+10%"; | ||
| 900 | "Mod+Right".action = set-column-width "+10%"; | ||
| 901 | |||
| 902 | "Mod+Shift+Z" = { | ||
| 903 | action = power-off-monitors; | ||
| 904 | allow-when-locked = true; | ||
| 905 | }; | ||
| 906 | "Mod+Shift+E".action = quit; | ||
| 907 | |||
| 908 | # "Mod+Semicolon".action = spawn makoctl "dismiss" "--group"; | ||
| 909 | # "Mod+Shift+Semicolon".action = spawn makoctl "dismiss" "--all"; | ||
| 910 | # "Mod+Period".action = spawn makoctl "menu" "--" (lib.getExe config.programs.fuzzel.package) "--dmenu"; | ||
| 911 | # "Mod+Comma".action = spawn makoctl "restore"; | ||
| 912 | |||
| 913 | "Mod+Control+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"FocusWorkspace\":{\"reference\":{\"Id\": $workspace_id}}}}"; | ||
| 914 | "Mod+Control+Shift+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"MoveColumnToWorkspace\":{\"reference\":{\"Id\": $workspace_id}, \"focus\": true}}}"; | ||
| 915 | |||
| 916 | "Mod+X".action = set-dynamic-cast-window; | ||
| 917 | "Mod+Shift+X".action = set-dynamic-cast-monitor; | ||
| 918 | "Mod+Control+Shift+X".action = clear-dynamic-cast-target; | ||
| 919 | |||
| 920 | "Mod+D".action = with-urgent-window-action "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; | ||
| 921 | "Mod+Shift+D".action = with-focused-window-action "{\"Action\":{\"UnsetUrgent\":{\"id\": .id}}}"; | ||
| 922 | |||
| 923 | "Mod+K".action = spawn (lib.getExe' pkgs.worktime "worktime-ui"); | ||
| 924 | "Mod+Shift+K".action = spawn (lib.getExe' pkgs.worktime "worktime-stop"); | ||
| 925 | })) | ||
| 926 | (lib.mapAttrsToList (name: cfg: node name [(lib.removeAttrs cfg ["action"])] [cfg.action]) (let | ||
| 927 | shell = obj: leaf "send-unix" [ | ||
| 928 | { path = ''''${XDG_RUNTIME_DIR}/shell.sock''; } | ||
| 929 | (builtins.toJSON obj + "\n") | ||
| 930 | ]; | ||
| 931 | in { | ||
| 932 | "XF86AudioRaiseVolume" = { | ||
| 933 | allow-when-locked = true; | ||
| 934 | action = shell { Volume.volume = "up"; }; | ||
| 935 | }; | ||
| 936 | "XF86AudioLowerVolume" = { | ||
| 937 | allow-when-locked = true; | ||
| 938 | action = shell { Volume.volume = "down"; }; | ||
| 939 | }; | ||
| 940 | "XF86AudioMute" = { | ||
| 941 | allow-when-locked = true; | ||
| 942 | action = shell { Volume.muted = "toggle"; }; | ||
| 943 | }; | ||
| 944 | "XF86AudioMicMute" = { | ||
| 945 | allow-when-locked = true; | ||
| 946 | action = shell { Volume."mic-muted" = "toggle"; }; | ||
| 947 | }; | ||
| 948 | "XF86MonBrightnessUp" = { | ||
| 949 | action = shell { Brightness = "up"; }; | ||
| 950 | allow-when-locked = true; | ||
| 951 | }; | ||
| 952 | "XF86MonBrightnessDown" = { | ||
| 953 | action = shell { Brightness = "down"; }; | ||
| 954 | allow-when-locked = true; | ||
| 955 | }; | ||
| 956 | "Mod+Shift+L".action = shell { LockSession = {}; }; | ||
| 957 | "Mod+Shift+Minus" = { | ||
| 958 | action = shell { Suspend = {}; }; | ||
| 959 | allow-when-locked = true; | ||
| 960 | }; | ||
| 961 | "Mod+Shift+Control+Minus" = { | ||
| 962 | action = shell { Hibernate = {}; }; | ||
| 963 | allow-when-locked = true; | ||
| 964 | }; | ||
| 965 | "Mod+Shift+P" = { | ||
| 966 | action = shell { Mpris = { PauseAll = {}; }; }; | ||
| 967 | allow-when-locked = true; | ||
| 968 | }; | ||
| 969 | "Mod+Semicolon".action = shell { Notifications = { DismissGroup = {}; }; }; | ||
| 970 | "Mod+Shift+Semicolon".action = shell { Notifications = { DismissAll = {}; }; }; | ||
| 971 | })) | ||
| 972 | (map ({ name, selector, spawn, key, ...}: if key != null && selector != null && spawn != null then bind key { action = focus-or-spawn-action selector name spawn; } else null) cfg.scratchspaces) | ||
| 973 | (map ({ name, moveKey, ...}: if moveKey != null then bind moveKey { action = kdl.magic-leaf "move-column-to-workspace" name; } else null) cfg.scratchspaces) | ||
| 974 | ] | ||
| 975 | )) | ||
| 976 | ]; | ||
| 977 | }; | ||
| 978 | } | ||
diff --git a/accounts/gkleen@sif/niri/mako.nix b/accounts/gkleen@sif/niri/mako.nix new file mode 100644 index 00000000..3d246d96 --- /dev/null +++ b/accounts/gkleen@sif/niri/mako.nix | |||
| @@ -0,0 +1,112 @@ | |||
| 1 | { config, lib, pkgs, ... }: | ||
| 2 | { | ||
| 3 | config = lib.mkIf false { | ||
| 4 | services.mako = { | ||
| 5 | enable = true; | ||
| 6 | settings = { | ||
| 7 | font = "Fira Sans 10"; | ||
| 8 | format = "<i>%s</i>\\n%b"; | ||
| 9 | margin = "2"; | ||
| 10 | max-visible = -1; | ||
| 11 | background-color = "#000000dd"; | ||
| 12 | progress-color = "source #223544ff"; | ||
| 13 | width = 384; | ||
| 14 | outer-margin = 1; | ||
| 15 | max-history = 100; | ||
| 16 | max-icon-size = 48; | ||
| 17 | |||
| 18 | grouped.format = "<b>(%g)</b> <i>%s</i>\\n%b"; | ||
| 19 | "urgency=low".text-color = "#999999ff"; | ||
| 20 | "urgency=critical".background-color = "#900000dd"; | ||
| 21 | "app-name=Element".group-by = "summary"; | ||
| 22 | "app-name=poweralertd" = { | ||
| 23 | history = false; | ||
| 24 | ignore-timeout = true; | ||
| 25 | default-timeout = 2000; | ||
| 26 | }; | ||
| 27 | "app-name=worktime".history = false; | ||
| 28 | "mode=silent".invisible = true; | ||
| 29 | }; | ||
| 30 | package = pkgs.symlinkJoin { | ||
| 31 | name = "${pkgs.mako.name}-wrapped"; | ||
| 32 | paths = with pkgs; [ mako ]; | ||
| 33 | inherit (pkgs.mako) meta; | ||
| 34 | postBuild = '' | ||
| 35 | rm -r $out/share/dbus-1 | ||
| 36 | ''; | ||
| 37 | }; | ||
| 38 | }; | ||
| 39 | systemd.user.services.mako = { | ||
| 40 | Unit = { | ||
| 41 | Description = "Mako notification daemon"; | ||
| 42 | PartOf = [ "graphical-session.target" ]; | ||
| 43 | }; | ||
| 44 | Install = { | ||
| 45 | WantedBy = [ "graphical-session.target" ]; | ||
| 46 | }; | ||
| 47 | Service = { | ||
| 48 | Type = "dbus"; | ||
| 49 | BusName = "org.freedesktop.Notifications"; | ||
| 50 | ExecStart = lib.getExe config.services.mako.package; | ||
| 51 | RestartSec = 5; | ||
| 52 | Restart = "always"; | ||
| 53 | }; | ||
| 54 | }; | ||
| 55 | |||
| 56 | systemd.user.services.mako-follows-focus = { | ||
| 57 | Unit = { | ||
| 58 | BindsTo = [ "niri.service" "mako.service" ]; | ||
| 59 | After = [ "niri.service" "mako.service" ]; | ||
| 60 | }; | ||
| 61 | Service = { | ||
| 62 | Type = "simple"; | ||
| 63 | Restart = "always"; | ||
| 64 | ExecStart = pkgs.writers.writePython3 "mako-follows-focus" { | ||
| 65 | libraries = with pkgs.python3Packages; []; | ||
| 66 | } '' | ||
| 67 | import os | ||
| 68 | import socket | ||
| 69 | import json | ||
| 70 | import subprocess | ||
| 71 | |||
| 72 | |||
| 73 | current_output = None | ||
| 74 | workspaces = [] | ||
| 75 | |||
| 76 | |||
| 77 | def output_changed(new_output): | ||
| 78 | global current_output | ||
| 79 | |||
| 80 | if current_output == new_output: | ||
| 81 | return | ||
| 82 | |||
| 83 | current_output = new_output | ||
| 84 | subprocess.run(["makoctl", "reload"]) | ||
| 85 | |||
| 86 | |||
| 87 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | ||
| 88 | sock.connect(os.environ["NIRI_SOCKET"]) | ||
| 89 | sock.send(b"\"EventStream\"\n") | ||
| 90 | for line in sock.makefile(buffering=1, encoding='utf-8'): | ||
| 91 | if line_json := json.loads(line): | ||
| 92 | if "WorkspacesChanged" in line_json: | ||
| 93 | workspaces = line_json["WorkspacesChanged"]["workspaces"] | ||
| 94 | for workspace in workspaces: | ||
| 95 | if not workspace["is_focused"]: | ||
| 96 | continue | ||
| 97 | output_changed(workspace["output"]) | ||
| 98 | break | ||
| 99 | if "WorkspaceActivated" in line_json and line_json["WorkspaceActivated"]["focused"]: # noqa: E501 | ||
| 100 | for workspace in workspaces: | ||
| 101 | if not workspace["id"] == line_json["WorkspaceActivated"]["id"]: # noqa: E501 | ||
| 102 | continue | ||
| 103 | output_changed(workspace["output"]) | ||
| 104 | break | ||
| 105 | ''; | ||
| 106 | }; | ||
| 107 | Install = { | ||
| 108 | WantedBy = [ "mako.service" ]; | ||
| 109 | }; | ||
| 110 | }; | ||
| 111 | }; | ||
| 112 | } | ||
diff --git a/accounts/gkleen@sif/shell/default.nix b/accounts/gkleen@sif/shell/default.nix new file mode 100644 index 00000000..44462865 --- /dev/null +++ b/accounts/gkleen@sif/shell/default.nix | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | { config, pkgs, lib, ... }: | ||
| 2 | |||
| 3 | { | ||
| 4 | config = { | ||
| 5 | programs.quickshell = { | ||
| 6 | enable = true; | ||
| 7 | package = pkgs.symlinkJoin { | ||
| 8 | pname = pkgs.quickshell.pname + "-wrapped"; | ||
| 9 | inherit (pkgs.quickshell) version meta; | ||
| 10 | paths = [ pkgs.quickshell ]; | ||
| 11 | buildInputs = [ pkgs.makeWrapper ]; | ||
| 12 | postBuild = '' | ||
| 13 | for binary in quickshell qs; do | ||
| 14 | wrapProgram $out/bin/$binary \ | ||
| 15 | --prefix QML_IMPORT_PATH : ${pkgs.qt6Packages.callPackage ./quickshell-plugins {}}/${pkgs.qt6.qtbase.qtQmlPrefix} | ||
| 16 | done | ||
| 17 | ''; | ||
| 18 | }; | ||
| 19 | config = { | ||
| 20 | src = ./quickshell; | ||
| 21 | replacements = { | ||
| 22 | ignore_workspaces = builtins.toJSON (map ({ name, ... }: name) config.programs.niri.scratchspaces); | ||
| 23 | wallpapers = builtins.toJSON (pkgs.stdenvNoCC.mkDerivation { | ||
| 24 | name = "wallpapers"; | ||
| 25 | srcs = [ | ||
| 26 | (pkgs.fetchurl { | ||
| 27 | url = "https://esawebb.org/media/archives/images/publicationtiff10k/carinanebula3.tif"; | ||
| 28 | hash = "sha256-YxZEweDKJfvfrdxb/QFmgJhcZDEJYxotoHrG+RRn1tw="; | ||
| 29 | }) | ||
| 30 | (pkgs.fetchurl { | ||
| 31 | url = "https://esawebb.org/media/archives/images/original/pillarsofcreation_composite.tif"; | ||
| 32 | hash = "sha256-qRiODxR0lZWdxgYXna0fNRFFDErpBJDwOJuQl6sNjRc="; | ||
| 33 | }) | ||
| 34 | (pkgs.fetchurl { | ||
| 35 | url = "https://esawebb.org/media/archives/images/publicationtiff10k/weic2212a.tif"; | ||
| 36 | hash = "sha256-l2fqE/z//C1a0xkvZwsnwPbTSb+WuA11h+SUl3E1dhw="; | ||
| 37 | }) | ||
| 38 | (pkgs.fetchurl { | ||
| 39 | url = "https://esawebb.org/media/archives/images/publicationtiff10k/weic2415a.tif"; | ||
| 40 | hash = "sha256-onBy7cPoUpDuzQStbY2E+qmlGgSLXPwFCLX53ukAb4c="; | ||
| 41 | }) | ||
| 42 | (pkgs.fetchurl { | ||
| 43 | url = "https://esawebb.org/media/archives/images/publicationtiff10k/weic2330a.tif"; | ||
| 44 | hash = "sha256-nn0ZtjZIrPcpj3YcLTsrL7XiXvyh3QYgCSmdDMD+3OM="; | ||
| 45 | }) | ||
| 46 | (pkgs.fetchurl { | ||
| 47 | url = "https://esawebb.org/media/archives/images/original/weic2426a.tif"; | ||
| 48 | hash = "sha256-EDnfPn3GE9jt6XPqiGInP7E2F3Az7d25NqATSWltDv0="; | ||
| 49 | }) | ||
| 50 | (pkgs.fetchurl { | ||
| 51 | url = "https://esawebb.org/media/archives/images/original/weic2503a.tif"; | ||
| 52 | hash = "sha256-3/RX6RQp8naELcgReHPd5/zhJkoCjnA10w5BEnNo+qI="; | ||
| 53 | }) | ||
| 54 | (pkgs.fetchurl { | ||
| 55 | url = "https://esawebb.org/media/archives/images/original/weic2506a.tif"; | ||
| 56 | hash = "sha256-aDld0aoY1owRxDVf7Jcyw71TH42M1foYotxn2thyFYw="; | ||
| 57 | }) | ||
| 58 | (pkgs.fetchurl { | ||
| 59 | url = "https://esawebb.org/media/archives/images/original/weic2514a.tif"; | ||
| 60 | hash = "sha256-jTi1G1Ofo5xsF6ggrbtYJHxqLaCQ7edM5B3uORiVQtg="; | ||
| 61 | }) | ||
| 62 | (pkgs.fetchurl { | ||
| 63 | url = "https://esawebb.org/media/archives/images/original/weic2425c.tif"; | ||
| 64 | hash = "sha256-oaEOexSJHEGj090dJF3ct5HAoR+Y5gRiPrUlxdvnTRo="; | ||
| 65 | }) | ||
| 66 | ]; | ||
| 67 | |||
| 68 | dontUnpack = true; | ||
| 69 | |||
| 70 | buildInputs = [ pkgs.imagemagick ]; | ||
| 71 | buildPhase = '' | ||
| 72 | runHook preBuild | ||
| 73 | |||
| 74 | typeset sources=($srcs) | ||
| 75 | |||
| 76 | mkdir -p $out | ||
| 77 | magick ''${sources[0]} -crop 10000x5625+0+79 +repage -define jpeg:extent=10MB $out/carinanebula3.jpeg | ||
| 78 | magick ''${sources[1]} -crop 6716x3778+329+80 +repage -define jpeg:extent=10MB $out/pillarsofcreation_composite.jpeg | ||
| 79 | magick ''${sources[2]} -crop 10000x5625+0+79 +repage -define jpeg:extent=10MB $out/weic2212a.jpeg | ||
| 80 | magick ''${sources[3]} -crop 7650x4302+1166+389 +repage -define jpeg:extent=10MB $out/weic2415a.jpeg | ||
| 81 | magick ''${sources[4]} -crop 8732x4912+0+434 +repage -define jpeg:extent=10MB $out/weic2330a.jpeg | ||
| 82 | magick ''${sources[5]} -crop 5302x2982+636+0 +repage -define jpeg:extent=10MB $out/weic2426a.jpeg | ||
| 83 | magick ''${sources[6]} -crop 4328x2434+0+906 +repage -define jpeg:extent=10MB $out/weic2503a.jpeg | ||
| 84 | magick ''${sources[7]} -crop 4152x2335+0+666 +repage -define jpeg:extent=10MB $out/weic2506a.jpeg | ||
| 85 | magick ''${sources[8]} -crop 4320x2430+0+0 +repage -define jpeg:extent=10MB $out/weic2514a.jpeg | ||
| 86 | magick ''${sources[9]} -crop 5863x3298+0+477 +repage -define jpeg:extent=10MB $out/weic2425c.jpeg | ||
| 87 | |||
| 88 | runHook postBuild | ||
| 89 | ''; | ||
| 90 | }); | ||
| 91 | niri_session = builtins.toJSON [ | ||
| 92 | (pkgs.writeShellScript "niri-session" '' | ||
| 93 | exec ${lib.getExe pkgs.dex} -w ${config.programs.niri.package}/share/wayland-sessions/niri.desktop &>/tmp/niri-session-$$.log | ||
| 94 | '') | ||
| 95 | # (lib.getExe pkgs.dex) | ||
| 96 | # "${config.programs.niri.package}/share/wayland-sessions/niri.desktop" | ||
| 97 | ]; | ||
| 98 | username = builtins.toJSON config.home.username; | ||
| 99 | mdi = builtins.toJSON (pkgs.fetchFromGitHub { | ||
| 100 | owner = "Templarian"; | ||
| 101 | repo = "MaterialDesign"; | ||
| 102 | rev = "2424e748e0cc63ab7b9c095a099b9fe239b737c0"; | ||
| 103 | hash = "sha256-QMGl7soAhErrrnY3aKOZpt49yebkSNzy10p/v5OaqQ0="; | ||
| 104 | }); | ||
| 105 | worktime = builtins.toJSON (lib.getExe pkgs.worktime); | ||
| 106 | }; | ||
| 107 | }; | ||
| 108 | }; | ||
| 109 | systemd.user.services.quickshell = { | ||
| 110 | Service = { | ||
| 111 | RuntimeDirectory = "quickshell"; | ||
| 112 | }; | ||
| 113 | }; | ||
| 114 | }; | ||
| 115 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/CMakeLists.txt b/accounts/gkleen@sif/shell/quickshell-plugins/CMakeLists.txt new file mode 100644 index 00000000..020c0515 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/CMakeLists.txt | |||
| @@ -0,0 +1,168 @@ | |||
| 1 | set(INSTALL_QMLDIR "" CACHE STRING "QML install dir") | ||
| 2 | set(INSTALL_QML_PREFIX "" CACHE STRING "QML install prefix") | ||
| 3 | |||
| 4 | # There doesn't seem to be a standard cross-distro qml install path. | ||
| 5 | if ("${INSTALL_QMLDIR}" STREQUAL "" AND "${INSTALL_QML_PREFIX}" STREQUAL "") | ||
| 6 | message(WARNING "Neither INSTALL_QMLDIR nor INSTALL_QML_PREFIX is set. QML modules will not be installed.") | ||
| 7 | else() | ||
| 8 | if ("${INSTALL_QMLDIR}" STREQUAL "") | ||
| 9 | set(QML_FULL_INSTALLDIR "${CMAKE_INSTALL_PREFIX}/${INSTALL_QML_PREFIX}") | ||
| 10 | else() | ||
| 11 | set(QML_FULL_INSTALLDIR "${INSTALL_QMLDIR}") | ||
| 12 | endif() | ||
| 13 | |||
| 14 | message(STATUS "QML install dir: ${QML_FULL_INSTALLDIR}") | ||
| 15 | endif() | ||
| 16 | |||
| 17 | # Install a given target as a QML module. This is mostly pulled from ECM, as there does not seem | ||
| 18 | # to be an official way to do it. | ||
| 19 | # see https://github.com/KDE/extra-cmake-modules/blob/fe0f606bf7f222e36f7560fd7a2c33ef993e23bb/modules/ECMQmlModule6.cmake#L160 | ||
| 20 | function(install_qml_module arg_TARGET) | ||
| 21 | if (NOT DEFINED QML_FULL_INSTALLDIR) | ||
| 22 | return() | ||
| 23 | endif() | ||
| 24 | |||
| 25 | qt_query_qml_module(${arg_TARGET} | ||
| 26 | URI module_uri | ||
| 27 | VERSION module_version | ||
| 28 | PLUGIN_TARGET module_plugin_target | ||
| 29 | TARGET_PATH module_target_path | ||
| 30 | QMLDIR module_qmldir | ||
| 31 | TYPEINFO module_typeinfo | ||
| 32 | QML_FILES module_qml_files | ||
| 33 | RESOURCES module_resources | ||
| 34 | ) | ||
| 35 | |||
| 36 | set(module_dir "${QML_FULL_INSTALLDIR}/${module_target_path}") | ||
| 37 | |||
| 38 | if (NOT TARGET "${module_plugin_target}") | ||
| 39 | message(FATAL_ERROR "install_qml_modules called for a target without a plugin") | ||
| 40 | endif() | ||
| 41 | |||
| 42 | get_target_property(target_type "${arg_TARGET}" TYPE) | ||
| 43 | if (NOT "${target_type}" STREQUAL "STATIC_LIBRARY") | ||
| 44 | install( | ||
| 45 | TARGETS "${arg_TARGET}" | ||
| 46 | LIBRARY DESTINATION "${module_dir}" | ||
| 47 | RUNTIME DESTINATION "${module_dir}" | ||
| 48 | ) | ||
| 49 | |||
| 50 | install( | ||
| 51 | TARGETS "${module_plugin_target}" | ||
| 52 | LIBRARY DESTINATION "${module_dir}" | ||
| 53 | RUNTIME DESTINATION "${module_dir}" | ||
| 54 | ) | ||
| 55 | endif() | ||
| 56 | |||
| 57 | install(FILES "${module_qmldir}" DESTINATION "${module_dir}") | ||
| 58 | install(FILES "${module_typeinfo}" DESTINATION "${module_dir}") | ||
| 59 | |||
| 60 | # Install QML files | ||
| 61 | list(LENGTH module_qml_files num_files) | ||
| 62 | if (NOT "${module_qml_files}" MATCHES "NOTFOUND" AND ${num_files} GREATER 0) | ||
| 63 | qt_query_qml_module(${arg_TARGET} QML_FILES_DEPLOY_PATHS qml_files_deploy_paths) | ||
| 64 | |||
| 65 | math(EXPR last_index "${num_files} - 1") | ||
| 66 | foreach(i RANGE 0 ${last_index}) | ||
| 67 | list(GET module_qml_files ${i} src_file) | ||
| 68 | list(GET qml_files_deploy_paths ${i} deploy_path) | ||
| 69 | get_filename_component(dst_name "${deploy_path}" NAME) | ||
| 70 | get_filename_component(dest_dir "${deploy_path}" DIRECTORY) | ||
| 71 | install(FILES "${src_file}" DESTINATION "${module_dir}/${dest_dir}" RENAME "${dst_name}") | ||
| 72 | endforeach() | ||
| 73 | endif() | ||
| 74 | |||
| 75 | # Install resources | ||
| 76 | list(LENGTH module_resources num_files) | ||
| 77 | if (NOT "${module_resources}" MATCHES "NOTFOUND" AND ${num_files} GREATER 0) | ||
| 78 | qt_query_qml_module(${arg_TARGET} RESOURCES_DEPLOY_PATHS resources_deploy_paths) | ||
| 79 | |||
| 80 | math(EXPR last_index "${num_files} - 1") | ||
| 81 | foreach(i RANGE 0 ${last_index}) | ||
| 82 | list(GET module_resources ${i} src_file) | ||
| 83 | list(GET resources_deploy_paths ${i} deploy_path) | ||
| 84 | get_filename_component(dst_name "${deploy_path}" NAME) | ||
| 85 | get_filename_component(dest_dir "${deploy_path}" DIRECTORY) | ||
| 86 | install(FILES "${src_file}" DESTINATION "${module_dir}/${dest_dir}" RENAME "${dst_name}") | ||
| 87 | endforeach() | ||
| 88 | endif() | ||
| 89 | endfunction() | ||
| 90 | |||
| 91 | |||
| 92 | cmake_minimum_required(VERSION 3.20) | ||
| 93 | project(custom LANGUAGES CXX) | ||
| 94 | |||
| 95 | find_package(Qt6 REQUIRED COMPONENTS Core Qml DBus) | ||
| 96 | |||
| 97 | qt_standard_project_setup(REQUIRES 6.6) | ||
| 98 | |||
| 99 | qt6_policy(SET QTP0001 NEW) | ||
| 100 | qt6_add_qml_module(customplugin | ||
| 101 | URI "Custom" | ||
| 102 | PLUGIN_TARGET customplugin | ||
| 103 | ) | ||
| 104 | |||
| 105 | set_source_files_properties(org.keepassxc.KeePassXC.MainWindow.xml PROPERTIES | ||
| 106 | CLASSNAME DBusKeePassXC | ||
| 107 | NO_NAMESPACE TRUE | ||
| 108 | ) | ||
| 109 | qt_add_dbus_interface(DBUS_INTERFACES | ||
| 110 | org.keepassxc.KeePassXC.MainWindow.xml | ||
| 111 | dbus_keepassxc | ||
| 112 | ) | ||
| 113 | |||
| 114 | set_source_files_properties(org.freedesktop.systemd1.Manager.xml PROPERTIES | ||
| 115 | CLASSNAME DBusSystemdManager | ||
| 116 | NO_NAMESPACE TRUE | ||
| 117 | ) | ||
| 118 | qt_add_dbus_interface(DBUS_INTERFACES | ||
| 119 | org.freedesktop.systemd1.Manager.xml | ||
| 120 | dbus_systemd_manager | ||
| 121 | ) | ||
| 122 | |||
| 123 | set_source_files_properties(org.freedesktop.login1.Manager.xml PROPERTIES | ||
| 124 | CLASSNAME DBusLogindManager | ||
| 125 | NO_NAMESPACE TRUE | ||
| 126 | ) | ||
| 127 | qt_add_dbus_interface(DBUS_INTERFACES | ||
| 128 | org.freedesktop.login1.Manager.xml | ||
| 129 | dbus_logind_manager | ||
| 130 | ) | ||
| 131 | |||
| 132 | set_source_files_properties(org.freedesktop.login1.Session.xml PROPERTIES | ||
| 133 | CLASSNAME DBusLogindSession | ||
| 134 | NO_NAMESPACE TRUE | ||
| 135 | ) | ||
| 136 | qt_add_dbus_interface(DBUS_INTERFACES | ||
| 137 | org.freedesktop.login1.Session.xml | ||
| 138 | dbus_logind_session | ||
| 139 | ) | ||
| 140 | |||
| 141 | set_source_files_properties(org.freedesktop.DBus.Properties.xml PROPERTIES | ||
| 142 | CLASSNAME DBusProperties | ||
| 143 | NO_NAMESPACE TRUE | ||
| 144 | ) | ||
| 145 | qt_add_dbus_interface(DBUS_INTERFACES | ||
| 146 | org.freedesktop.DBus.Properties.xml | ||
| 147 | dbus_properties | ||
| 148 | ) | ||
| 149 | |||
| 150 | include_directories(${CMAKE_SOURCE_DIR}/build) | ||
| 151 | |||
| 152 | target_compile_features(customplugin PUBLIC cxx_std_26) | ||
| 153 | |||
| 154 | target_link_libraries(customplugin PRIVATE | ||
| 155 | Qt6::Core | ||
| 156 | Qt6::Qml | ||
| 157 | Qt6::DBus | ||
| 158 | ) | ||
| 159 | |||
| 160 | target_sources(customplugin PRIVATE | ||
| 161 | Chrono.cpp Chrono.hpp | ||
| 162 | FileSelector.cpp FileSelector.hpp | ||
| 163 | KeePassXC.cpp KeePassXC.hpp | ||
| 164 | Systemd.cpp Systemd.hpp | ||
| 165 | ${DBUS_INTERFACES} | ||
| 166 | ) | ||
| 167 | |||
| 168 | install_qml_module(customplugin) | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.cpp b/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.cpp new file mode 100644 index 00000000..22b3469b --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.cpp | |||
| @@ -0,0 +1,83 @@ | |||
| 1 | #include "Chrono.hpp" | ||
| 2 | |||
| 3 | #include <format> | ||
| 4 | #include <iostream> | ||
| 5 | #include <cmath> | ||
| 6 | |||
| 7 | Chrono::Chrono(QObject* parent): QObject(parent) { | ||
| 8 | QObject::connect(&this->timer, &QTimer::timeout, this, &Chrono::onTimeout); | ||
| 9 | this->update(); | ||
| 10 | } | ||
| 11 | |||
| 12 | bool Chrono::enabled() const { return this->mEnabled; } | ||
| 13 | |||
| 14 | void Chrono::setEnabled(bool enabled) { | ||
| 15 | if (enabled == this->mEnabled) return; | ||
| 16 | this->mEnabled = enabled; | ||
| 17 | emit this->enabledChanged(); | ||
| 18 | this->update(); | ||
| 19 | } | ||
| 20 | |||
| 21 | Chrono::Precision Chrono::precision() const { return this->mPrecision; } | ||
| 22 | |||
| 23 | void Chrono::setPrecision(Chrono::Precision precision) { | ||
| 24 | if (precision == this->mPrecision) return; | ||
| 25 | this->mPrecision = precision; | ||
| 26 | emit this->precisionChanged(); | ||
| 27 | this->update(); | ||
| 28 | } | ||
| 29 | |||
| 30 | void Chrono::onTimeout() { | ||
| 31 | this->setTime(this->targetTime); | ||
| 32 | this->schedule(this->targetTime); | ||
| 33 | } | ||
| 34 | |||
| 35 | void Chrono::update() { | ||
| 36 | if (this->mEnabled) { | ||
| 37 | this->setTime(std::chrono::time_point<std::chrono::system_clock>()); | ||
| 38 | this->schedule(std::chrono::time_point<std::chrono::system_clock>()); | ||
| 39 | } else { | ||
| 40 | this->timer.stop(); | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | void Chrono::setTime(const std::chrono::time_point<std::chrono::system_clock>& targetTime) { | ||
| 45 | using namespace std::chrono_literals; | ||
| 46 | |||
| 47 | auto currentTime = std::chrono::system_clock::now(); | ||
| 48 | auto offset = std::chrono::duration_cast<std::chrono::milliseconds>(targetTime - currentTime); | ||
| 49 | this->currentTime = abs(offset) < 500ms ? targetTime : currentTime; | ||
| 50 | |||
| 51 | switch (this->mPrecision) { | ||
| 52 | case Chrono::Hours: this->currentTime = std::chrono::time_point_cast<std::chrono::hours>(this->currentTime); | ||
| 53 | case Chrono::Minutes: this->currentTime = std::chrono::time_point_cast<std::chrono::minutes>(this->currentTime); | ||
| 54 | case Chrono::Seconds: this->currentTime = std::chrono::time_point_cast<std::chrono::seconds>(this->currentTime); | ||
| 55 | } | ||
| 56 | |||
| 57 | emit this->dateChanged(); | ||
| 58 | } | ||
| 59 | |||
| 60 | void Chrono::schedule(const std::chrono::time_point<std::chrono::system_clock>& targetTime) { | ||
| 61 | using namespace std::chrono_literals; | ||
| 62 | |||
| 63 | auto currentTime = std::chrono::system_clock::now(); | ||
| 64 | auto offset = std::chrono::duration_cast<std::chrono::milliseconds>(targetTime - currentTime); | ||
| 65 | auto nextTime = abs(offset) < 500ms ? targetTime : currentTime; | ||
| 66 | |||
| 67 | switch (this->mPrecision) { | ||
| 68 | case Chrono::Hours: nextTime = std::chrono::time_point_cast<std::chrono::hours>(nextTime) + 1h; | ||
| 69 | case Chrono::Minutes: nextTime = std::chrono::time_point_cast<std::chrono::minutes>(nextTime) + 1min; | ||
| 70 | case Chrono::Seconds: nextTime = std::chrono::time_point_cast<std::chrono::seconds>(nextTime) + 1s; | ||
| 71 | } | ||
| 72 | |||
| 73 | this->targetTime = nextTime; | ||
| 74 | this->timer.start(std::chrono::duration_cast<std::chrono::milliseconds>(nextTime - currentTime)); | ||
| 75 | } | ||
| 76 | |||
| 77 | QString Chrono::format(const QString& fmt) const { | ||
| 78 | return QString::fromStdString(std::format(std::runtime_format(fmt.toStdString()), std::chrono::zoned_time(std::chrono::current_zone(), std::chrono::time_point_cast<std::chrono::seconds>(this->currentTime)))); | ||
| 79 | } | ||
| 80 | |||
| 81 | QDateTime Chrono::date() const { | ||
| 82 | return QDateTime::fromStdTimePoint(std::chrono::time_point_cast<std::chrono::milliseconds>(this->currentTime)); | ||
| 83 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.hpp b/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.hpp new file mode 100644 index 00000000..04080187 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.hpp | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <chrono> | ||
| 4 | |||
| 5 | #include <QDateTime> | ||
| 6 | #include <QObject> | ||
| 7 | #include <QTimer> | ||
| 8 | |||
| 9 | #include <qqmlintegration.h> | ||
| 10 | |||
| 11 | class Chrono : public QObject { | ||
| 12 | Q_OBJECT; | ||
| 13 | Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged); | ||
| 14 | Q_PROPERTY(Chrono::Precision precision READ precision WRITE setPrecision NOTIFY precisionChanged); | ||
| 15 | Q_PROPERTY(QDateTime date READ date NOTIFY dateChanged) | ||
| 16 | QML_ELEMENT; | ||
| 17 | |||
| 18 | public: | ||
| 19 | enum Precision : quint8 { | ||
| 20 | Hours = 1, | ||
| 21 | Minutes = 2, | ||
| 22 | Seconds = 3, | ||
| 23 | }; | ||
| 24 | Q_ENUM(Precision); | ||
| 25 | |||
| 26 | explicit Chrono(QObject* parent = nullptr); | ||
| 27 | |||
| 28 | bool enabled() const; | ||
| 29 | void setEnabled(bool enabled); | ||
| 30 | |||
| 31 | Chrono::Precision precision() const; | ||
| 32 | void setPrecision(Chrono::Precision precision); | ||
| 33 | |||
| 34 | Q_INVOKABLE QString format(const QString& fmt) const; | ||
| 35 | |||
| 36 | QDateTime date() const; | ||
| 37 | |||
| 38 | signals: | ||
| 39 | void enabledChanged(); | ||
| 40 | void precisionChanged(); | ||
| 41 | void dateChanged(); | ||
| 42 | |||
| 43 | private slots: | ||
| 44 | void onTimeout(); | ||
| 45 | |||
| 46 | private: | ||
| 47 | bool mEnabled = true; | ||
| 48 | Chrono::Precision mPrecision = Chrono::Seconds; | ||
| 49 | QTimer timer; | ||
| 50 | std::chrono::time_point<std::chrono::system_clock> currentTime, targetTime; | ||
| 51 | |||
| 52 | void update(); | ||
| 53 | void setTime(const std::chrono::time_point<std::chrono::system_clock>& targetTime); | ||
| 54 | void schedule(const std::chrono::time_point<std::chrono::system_clock>& targetTime); | ||
| 55 | }; | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.cpp b/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.cpp new file mode 100644 index 00000000..d7051d2a --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.cpp | |||
| @@ -0,0 +1,102 @@ | |||
| 1 | #include "FileSelector.hpp" | ||
| 2 | |||
| 3 | #include <sstream> | ||
| 4 | #include <vector> | ||
| 5 | #include <random> | ||
| 6 | #include <algorithm> | ||
| 7 | |||
| 8 | #include <iostream> | ||
| 9 | #include <format> | ||
| 10 | |||
| 11 | namespace fs = std::filesystem; | ||
| 12 | |||
| 13 | FileSelector::FileSelector(QObject* parent): QObject(parent) { | ||
| 14 | QObject::connect(&this->timer, &QTimer::timeout, this, &FileSelector::onTimeout); | ||
| 15 | this->timer.setTimerType(Qt::PreciseTimer); | ||
| 16 | } | ||
| 17 | |||
| 18 | QString FileSelector::directory() const { | ||
| 19 | return QString::fromStdString(this->mDirectory->string()); | ||
| 20 | } | ||
| 21 | void FileSelector::setDirectory(QString directory) { | ||
| 22 | this->mDirectory = directory.toStdString(); | ||
| 23 | if (this->mDirectory && this->mEpoch) | ||
| 24 | this->update(); | ||
| 25 | emit this->directoryChanged(); | ||
| 26 | } | ||
| 27 | |||
| 28 | unsigned int FileSelector::epoch() const { | ||
| 29 | return std::chrono::duration_cast<std::chrono::milliseconds>(*this->mEpoch).count(); | ||
| 30 | } | ||
| 31 | void FileSelector::setEpoch(unsigned int epoch) { | ||
| 32 | this->mEpoch = std::chrono::milliseconds{epoch}; | ||
| 33 | if (this->mDirectory && this->mEpoch) | ||
| 34 | this->update(); | ||
| 35 | emit this->epochChanged(); | ||
| 36 | } | ||
| 37 | |||
| 38 | QString FileSelector::seed() const { | ||
| 39 | return this->mSeed; | ||
| 40 | } | ||
| 41 | void FileSelector::setSeed(QString seed) { | ||
| 42 | this->mSeed = seed; | ||
| 43 | emit this->seedChanged(); | ||
| 44 | if (this->mDirectory && this->mEpoch) | ||
| 45 | emit this->selectedChanged(); | ||
| 46 | } | ||
| 47 | |||
| 48 | QString FileSelector::selected() const { | ||
| 49 | if (!this->mDirectory || !this->mEpoch) | ||
| 50 | return QString(); | ||
| 51 | |||
| 52 | std::vector<fs::path> shuffled(this->mFiles.begin(), this->mFiles.end()); | ||
| 53 | std::sort(shuffled.begin(), shuffled.end()); | ||
| 54 | |||
| 55 | auto currentTime = std::chrono::system_clock::now(); | ||
| 56 | uint64_t currentEpoch = currentTime.time_since_epoch() / *this->mEpoch; | ||
| 57 | std::chrono::milliseconds timeInEpoch = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime.time_since_epoch()) % *this->mEpoch; | ||
| 58 | |||
| 59 | std::ostringstream seed; | ||
| 60 | seed << this->mSeed.size() << ";" << this->mSeed.toStdString() << ";"; | ||
| 61 | seed << *this->mEpoch << ";"; | ||
| 62 | seed << currentEpoch << ";"; | ||
| 63 | seed << this->mDirectory->string().size() << ";" << *this->mDirectory << ";"; | ||
| 64 | seed << this->mFiles.size() << ";"; | ||
| 65 | for (const fs::path& p: this->mFiles) | ||
| 66 | seed << p.string().size() << ";" << p << ";"; | ||
| 67 | |||
| 68 | std::vector<std::seed_seq::result_type> v; | ||
| 69 | v.reserve(seed.str().size()); | ||
| 70 | for (const char& c: seed.str()) | ||
| 71 | v.push_back(c); | ||
| 72 | |||
| 73 | std::seed_seq engine_seed(v.begin(), v.end()); | ||
| 74 | std::mt19937 g(engine_seed); | ||
| 75 | std::shuffle(shuffled.begin(), shuffled.end(), g); | ||
| 76 | |||
| 77 | std::vector<fs::path>::size_type ix = shuffled.size() * timeInEpoch / *this->mEpoch; | ||
| 78 | return QString::fromStdString((*this->mDirectory / shuffled[ix]).string()); | ||
| 79 | } | ||
| 80 | |||
| 81 | void FileSelector::onTimeout() { | ||
| 82 | if (!this->mFiles.size()) | ||
| 83 | return; | ||
| 84 | |||
| 85 | auto currentTime = std::chrono::system_clock::now(); | ||
| 86 | uint64_t currentMinorEpoch = currentTime.time_since_epoch() / (*this->mEpoch / this->mFiles.size()); | ||
| 87 | auto nextTime = std::chrono::time_point<std::chrono::system_clock>((currentMinorEpoch + 1) * (*this->mEpoch / this->mFiles.size())); | ||
| 88 | this->timer.start(std::chrono::duration_cast<std::chrono::milliseconds>(nextTime - currentTime)); | ||
| 89 | |||
| 90 | emit this->selectedChanged(); | ||
| 91 | } | ||
| 92 | |||
| 93 | void FileSelector::update() { | ||
| 94 | this->mFiles = std::set<fs::path>{}; | ||
| 95 | for (const fs::directory_entry& entry: | ||
| 96 | fs::recursive_directory_iterator(*this->mDirectory, fs::directory_options::follow_directory_symlink)) | ||
| 97 | { | ||
| 98 | this->mFiles.insert(fs::relative(entry, *this->mDirectory)); | ||
| 99 | } | ||
| 100 | |||
| 101 | this->onTimeout(); | ||
| 102 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.hpp b/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.hpp new file mode 100644 index 00000000..72c4f2a7 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.hpp | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <filesystem> | ||
| 4 | #include <chrono> | ||
| 5 | #include <set> | ||
| 6 | #include <optional> | ||
| 7 | |||
| 8 | #include <QObject> | ||
| 9 | #include <QTimer> | ||
| 10 | |||
| 11 | #include <qqmlintegration.h> | ||
| 12 | |||
| 13 | class FileSelector : public QObject { | ||
| 14 | Q_OBJECT; | ||
| 15 | Q_PROPERTY(QString directory READ directory WRITE setDirectory NOTIFY directoryChanged REQUIRED); | ||
| 16 | Q_PROPERTY(unsigned int epoch READ epoch WRITE setEpoch NOTIFY epochChanged REQUIRED); | ||
| 17 | Q_PROPERTY(QString seed READ seed WRITE setSeed NOTIFY seedChanged); | ||
| 18 | Q_PROPERTY(QString selected READ selected NOTIFY selectedChanged); | ||
| 19 | QML_ELEMENT; | ||
| 20 | |||
| 21 | public: | ||
| 22 | explicit FileSelector(QObject* parent = nullptr); | ||
| 23 | |||
| 24 | QString directory() const; | ||
| 25 | void setDirectory(QString directory); | ||
| 26 | |||
| 27 | unsigned int epoch() const; | ||
| 28 | void setEpoch(unsigned int epoch); | ||
| 29 | |||
| 30 | QString seed() const; | ||
| 31 | void setSeed(QString seed); | ||
| 32 | |||
| 33 | QString selected() const; | ||
| 34 | |||
| 35 | signals: | ||
| 36 | void directoryChanged(); | ||
| 37 | void epochChanged(); | ||
| 38 | void seedChanged(); | ||
| 39 | void selectedChanged(); | ||
| 40 | |||
| 41 | private slots: | ||
| 42 | void onTimeout(); | ||
| 43 | |||
| 44 | private: | ||
| 45 | std::optional<std::filesystem::path> mDirectory; | ||
| 46 | std::optional<std::chrono::milliseconds> mEpoch; | ||
| 47 | std::set<std::filesystem::path> mFiles; | ||
| 48 | QString mSeed; | ||
| 49 | QTimer timer; | ||
| 50 | |||
| 51 | void update(); | ||
| 52 | }; | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.cpp b/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.cpp new file mode 100644 index 00000000..f6e4dd6e --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.cpp | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | #include "KeePassXC.hpp" | ||
| 2 | |||
| 3 | #include <QDBusConnection> | ||
| 4 | |||
| 5 | KeePassXC::KeePassXC() { | ||
| 6 | this->service = new DBusKeePassXC(DBusKeePassXC::staticInterfaceName(), "/keepassxc", QDBusConnection::sessionBus(), this); | ||
| 7 | } | ||
| 8 | KeePassXC::~KeePassXC() { | ||
| 9 | if (this->service) | ||
| 10 | delete this->service; | ||
| 11 | } | ||
| 12 | |||
| 13 | void KeePassXC::lockAllDatabases() { | ||
| 14 | if (!this->service) | ||
| 15 | return; | ||
| 16 | |||
| 17 | this->service->lockAllDatabases(); | ||
| 18 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.hpp b/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.hpp new file mode 100644 index 00000000..c4cd71e0 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.hpp | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "dbus_keepassxc.h" | ||
| 4 | |||
| 5 | #include <QObject> | ||
| 6 | #include <QtQmlIntegration/qqmlintegration.h> | ||
| 7 | |||
| 8 | class KeePassXC : public QObject { | ||
| 9 | Q_OBJECT; | ||
| 10 | QML_SINGLETON; | ||
| 11 | QML_ELEMENT; | ||
| 12 | |||
| 13 | public: | ||
| 14 | explicit KeePassXC(); | ||
| 15 | ~KeePassXC(); | ||
| 16 | |||
| 17 | Q_INVOKABLE void lockAllDatabases(); | ||
| 18 | |||
| 19 | private: | ||
| 20 | DBusKeePassXC* service = nullptr; | ||
| 21 | }; | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.cpp b/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.cpp new file mode 100644 index 00000000..308659e9 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.cpp | |||
| @@ -0,0 +1,198 @@ | |||
| 1 | #include "Systemd.hpp" | ||
| 2 | |||
| 3 | #include <sstream> | ||
| 4 | |||
| 5 | #include <QDBusConnection> | ||
| 6 | #include <QDBusReply> | ||
| 7 | #include <QDebug> | ||
| 8 | #include <QDBusUnixFileDescriptor> | ||
| 9 | #include <QDBusObjectPath> | ||
| 10 | |||
| 11 | Systemd::Systemd(QObject* parent) : QObject(parent) { | ||
| 12 | this->systemdManager = new DBusSystemdManager( | ||
| 13 | "org.freedesktop.systemd1", | ||
| 14 | "/org/freedesktop/systemd1", | ||
| 15 | QDBusConnection::sessionBus(), | ||
| 16 | this | ||
| 17 | ); | ||
| 18 | this->logindManager = new DBusLogindManager( | ||
| 19 | "org.freedesktop.login1", | ||
| 20 | "/org/freedesktop/login1", | ||
| 21 | QDBusConnection::systemBus(), | ||
| 22 | this | ||
| 23 | ); | ||
| 24 | |||
| 25 | QDBusReply<QDBusObjectPath> sessionPath = this->logindManager->GetSession("auto"); | ||
| 26 | qDebug() << sessionPath; | ||
| 27 | this->logindSession = new DBusLogindSession( | ||
| 28 | "org.freedesktop.login1", | ||
| 29 | sessionPath.value().path(), | ||
| 30 | QDBusConnection::systemBus(), | ||
| 31 | this | ||
| 32 | ); | ||
| 33 | this->logindSessionProperties = new DBusProperties( | ||
| 34 | "org.freedesktop.login1", | ||
| 35 | sessionPath.value().path(), | ||
| 36 | QDBusConnection::systemBus(), | ||
| 37 | this | ||
| 38 | ); | ||
| 39 | |||
| 40 | QObject::connect(this->logindManager, &DBusLogindManager::PrepareForShutdown, this, &Systemd::shutdown); | ||
| 41 | QObject::connect(this->logindManager, &DBusLogindManager::PrepareForSleep, this, &Systemd::sleep); | ||
| 42 | QObject::connect(this->logindSession, &DBusLogindSession::Lock, this, &Systemd::lock); | ||
| 43 | QObject::connect(this->logindSession, &DBusLogindSession::Unlock, this, &Systemd::unlock); | ||
| 44 | QObject::connect(this->logindSessionProperties, &DBusProperties::PropertiesChanged, this, &Systemd::onLogindSessionPropertiesChanged); | ||
| 45 | } | ||
| 46 | |||
| 47 | void Systemd::onLogindSessionPropertiesChanged(const QString& interface_name, const QVariantMap& changed_properties, const QStringList& invalidated_properties) { | ||
| 48 | if (changed_properties.contains("IdleHint") || invalidated_properties.contains("IdleHint")) | ||
| 49 | emit this->idleHintChanged(); | ||
| 50 | if (changed_properties.contains("LockedHint") || invalidated_properties.contains("LockedHint")) | ||
| 51 | emit this->lockedHintChanged(); | ||
| 52 | } | ||
| 53 | |||
| 54 | void Systemd::stopUserUnit(const QString& unit, const QString& mode) { this->systemdManager->StopUnit(unit, mode); } | ||
| 55 | |||
| 56 | void Systemd::setBrightness(const QString& subsystem, const QString& name, quint32 brightness) { this->logindSession->SetBrightness(subsystem, name, brightness); } | ||
| 57 | |||
| 58 | bool Systemd::idleHint() { return this->logindSession->idleHint(); } | ||
| 59 | void Systemd::setIdleHint(bool idle) { this->logindSession->SetIdleHint(idle); } | ||
| 60 | bool Systemd::lockedHint() { return this->logindSession->lockedHint(); } | ||
| 61 | void Systemd::setLockedHint(bool locked) { this->logindSession->SetLockedHint(locked); } | ||
| 62 | void Systemd::lockSession() { this->logindSession->call("Lock"); } | ||
| 63 | void Systemd::suspend() { this->logindManager->Suspend(true); } | ||
| 64 | void Systemd::hibernate() { this->logindManager->Hibernate(true); } | ||
| 65 | |||
| 66 | std::string SystemdInhibitorParams::toString(SystemdInhibitorParams::WhatItem what) { | ||
| 67 | if (what == SystemdInhibitorParams::Shutdown) | ||
| 68 | return "shutdown"; | ||
| 69 | else if (what == SystemdInhibitorParams::Sleep) | ||
| 70 | return "sleep"; | ||
| 71 | else if (what == SystemdInhibitorParams::Idle) | ||
| 72 | return "idle"; | ||
| 73 | else if (what == SystemdInhibitorParams::HandlePowerKey) | ||
| 74 | return "handle-power-key"; | ||
| 75 | else if (what == SystemdInhibitorParams::HandleSuspendKey) | ||
| 76 | return "handle-suspend-key"; | ||
| 77 | else if (what == SystemdInhibitorParams::HandleHibernateKey) | ||
| 78 | return "handle-hibernate-key"; | ||
| 79 | else if (what == SystemdInhibitorParams::HandleLidSwitch) | ||
| 80 | return "handle-lid-switch"; | ||
| 81 | return ""; | ||
| 82 | } | ||
| 83 | |||
| 84 | std::string SystemdInhibitorParams::toString(SystemdInhibitorParams::What what) { | ||
| 85 | std::ostringstream res; | ||
| 86 | bool first = true; | ||
| 87 | for (const WhatItem& item: SystemdInhibitorParams::allWhatItems) { | ||
| 88 | if (!(what & item)) | ||
| 89 | continue; | ||
| 90 | |||
| 91 | if (!first) | ||
| 92 | res << ":"; | ||
| 93 | else | ||
| 94 | first = false; | ||
| 95 | res << SystemdInhibitorParams::toString(item); | ||
| 96 | } | ||
| 97 | return res.str(); | ||
| 98 | } | ||
| 99 | |||
| 100 | std::string SystemdInhibitorParams::toString(SystemdInhibitorParams::Mode mode) { | ||
| 101 | if (mode == SystemdInhibitorParams::Block) | ||
| 102 | return "block"; | ||
| 103 | else if (mode == SystemdInhibitorParams::BlockWeak) | ||
| 104 | return "block-weak"; | ||
| 105 | else if (mode == SystemdInhibitorParams::Delay) | ||
| 106 | return "delay"; | ||
| 107 | return ""; | ||
| 108 | } | ||
| 109 | |||
| 110 | bool SystemdInhibitor::enabled() const { return static_cast<bool>(this->activeInhibitor); } | ||
| 111 | void SystemdInhibitor::setEnabled(bool enabled) { | ||
| 112 | this->mEnabled = enabled; | ||
| 113 | |||
| 114 | if (enabled) | ||
| 115 | this->update(); | ||
| 116 | else | ||
| 117 | this->release(); | ||
| 118 | } | ||
| 119 | |||
| 120 | SystemdInhibitorParams::What SystemdInhibitor::what() const { return this->mWhat; } | ||
| 121 | void SystemdInhibitor::setWhat(SystemdInhibitorParams::What what) { | ||
| 122 | this->mWhat = what; | ||
| 123 | this->update(); | ||
| 124 | } | ||
| 125 | |||
| 126 | QString SystemdInhibitor::who() const { return this->mWho; } | ||
| 127 | void SystemdInhibitor::setWho(QString who) { | ||
| 128 | this->mWho = who; | ||
| 129 | this->update(); | ||
| 130 | } | ||
| 131 | |||
| 132 | QString SystemdInhibitor::why() const { return this->mWhy; } | ||
| 133 | void SystemdInhibitor::setWhy(QString why) { | ||
| 134 | this->mWhy = why; | ||
| 135 | this->update(); | ||
| 136 | } | ||
| 137 | |||
| 138 | SystemdInhibitorParams::Mode SystemdInhibitor::mode() const { return this->mMode; } | ||
| 139 | void SystemdInhibitor::setMode(SystemdInhibitorParams::Mode mode) { | ||
| 140 | this->mMode = mode; | ||
| 141 | this->update(); | ||
| 142 | } | ||
| 143 | |||
| 144 | SystemdInhibitor::ActiveSystemdInhibitor::ActiveSystemdInhibitor(SystemdInhibitorParams::What what_, QString who_, QString why_, SystemdInhibitorParams::Mode mode_): what(what_), who(who_), why(why_), mode(mode_) { | ||
| 145 | DBusLogindManager logindManager( | ||
| 146 | "org.freedesktop.login1", | ||
| 147 | "/org/freedesktop/login1", | ||
| 148 | QDBusConnection::systemBus() | ||
| 149 | ); | ||
| 150 | QDBusReply<QDBusUnixFileDescriptor> fd_ = logindManager.Inhibit(QString::fromStdString(SystemdInhibitorParams::toString(what)), who, why, QString::fromStdString(SystemdInhibitorParams::toString(mode))); | ||
| 151 | if (fd_.error().isValid()) | ||
| 152 | throw fd_.error(); | ||
| 153 | this->fd = ::dup(fd_.value().fileDescriptor()); | ||
| 154 | } | ||
| 155 | SystemdInhibitor::ActiveSystemdInhibitor::~ActiveSystemdInhibitor() { | ||
| 156 | if (this->fd != -1) | ||
| 157 | ::close(this->fd); | ||
| 158 | } | ||
| 159 | |||
| 160 | void SystemdInhibitor::release() { | ||
| 161 | if (!this->activeInhibitor) | ||
| 162 | return; | ||
| 163 | |||
| 164 | this->activeInhibitor.reset(); | ||
| 165 | emit this->enabledChanged(); | ||
| 166 | } | ||
| 167 | |||
| 168 | void SystemdInhibitor::update() { | ||
| 169 | if (!this->mWhat || this->mWho.isEmpty() || this->mWhy.isEmpty() || !this->mMode || !this->mEnabled) | ||
| 170 | if (this->activeInhibitor) | ||
| 171 | this->release(); | ||
| 172 | else | ||
| 173 | return; | ||
| 174 | |||
| 175 | if (this->activeInhibitor && this->mWhat == this->activeInhibitor->what && this->mWho == this->activeInhibitor->who && this->mWhy == this->activeInhibitor->why && this->mMode == this->activeInhibitor->mode) | ||
| 176 | return; | ||
| 177 | |||
| 178 | std::unique_ptr<ActiveSystemdInhibitor> otherInhibitor; | ||
| 179 | try { | ||
| 180 | otherInhibitor.reset(new SystemdInhibitor::ActiveSystemdInhibitor(this->mWhat, this->mWho, this->mWhy, this->mMode)); | ||
| 181 | } catch (const QDBusError& err) { | ||
| 182 | qCritical().noquote() | ||
| 183 | << err.name().toStdString() << " " << err.message().toStdString(); | ||
| 184 | return; | ||
| 185 | } | ||
| 186 | this->activeInhibitor.swap(otherInhibitor); | ||
| 187 | |||
| 188 | if (!otherInhibitor && this->activeInhibitor) | ||
| 189 | emit this->enabledChanged(); | ||
| 190 | if (otherInhibitor && otherInhibitor->what != this->activeInhibitor->what) | ||
| 191 | emit this->whatChanged(); | ||
| 192 | if (otherInhibitor && otherInhibitor->who != this->activeInhibitor->who) | ||
| 193 | emit this->whoChanged(); | ||
| 194 | if (otherInhibitor && otherInhibitor->why != this->activeInhibitor->why) | ||
| 195 | emit this->whyChanged(); | ||
| 196 | if (otherInhibitor && otherInhibitor->mode != this->activeInhibitor->mode) | ||
| 197 | emit this->modeChanged(); | ||
| 198 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.hpp b/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.hpp new file mode 100644 index 00000000..615024d2 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.hpp | |||
| @@ -0,0 +1,147 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <cstdint> | ||
| 4 | #include <memory> | ||
| 5 | #include <string> | ||
| 6 | |||
| 7 | #include <QObject> | ||
| 8 | #include <QDBusInterface> | ||
| 9 | #include <QtQmlIntegration/qqmlintegration.h> | ||
| 10 | |||
| 11 | #include "dbus_systemd_manager.h" | ||
| 12 | #include "dbus_logind_manager.h" | ||
| 13 | #include "dbus_logind_session.h" | ||
| 14 | #include "dbus_properties.h" | ||
| 15 | |||
| 16 | class Systemd : public QObject { | ||
| 17 | Q_OBJECT; | ||
| 18 | QML_SINGLETON; | ||
| 19 | QML_ELEMENT; | ||
| 20 | |||
| 21 | public: | ||
| 22 | explicit Systemd(QObject* parent = nullptr); | ||
| 23 | |||
| 24 | Q_PROPERTY(bool idleHint READ idleHint WRITE setIdleHint NOTIFY idleHintChanged) | ||
| 25 | Q_PROPERTY(bool lockedHint READ lockedHint WRITE setLockedHint NOTIFY lockedHintChanged) | ||
| 26 | |||
| 27 | Q_INVOKABLE void stopUserUnit(const QString& unit, const QString& mode); | ||
| 28 | Q_INVOKABLE void setBrightness(const QString& subsystem, const QString& name, quint32 brightness); | ||
| 29 | Q_INVOKABLE void setIdleHint(bool idle); | ||
| 30 | Q_INVOKABLE void setLockedHint(bool locked); | ||
| 31 | Q_INVOKABLE void lockSession(); | ||
| 32 | Q_INVOKABLE void suspend(); | ||
| 33 | Q_INVOKABLE void hibernate(); | ||
| 34 | |||
| 35 | bool idleHint(); | ||
| 36 | bool lockedHint(); | ||
| 37 | |||
| 38 | signals: | ||
| 39 | void shutdown(bool before); | ||
| 40 | void sleep(bool before); | ||
| 41 | void lock(); | ||
| 42 | void unlock(); | ||
| 43 | void idleHintChanged(); | ||
| 44 | void lockedHintChanged(); | ||
| 45 | |||
| 46 | private slots: | ||
| 47 | void onLogindSessionPropertiesChanged(const QString& interface_name, const QVariantMap& changed_properties, const QStringList& invalidated_properties); | ||
| 48 | |||
| 49 | private: | ||
| 50 | DBusSystemdManager* systemdManager; | ||
| 51 | DBusLogindManager* logindManager; | ||
| 52 | DBusLogindSession* logindSession; | ||
| 53 | DBusProperties* logindSessionProperties; | ||
| 54 | }; | ||
| 55 | |||
| 56 | class SystemdInhibitorParams : public QObject { | ||
| 57 | Q_OBJECT; | ||
| 58 | QML_ELEMENT; | ||
| 59 | QML_SINGLETON; | ||
| 60 | |||
| 61 | public: | ||
| 62 | enum WhatItem : uint8_t { | ||
| 63 | Shutdown = 0b1, | ||
| 64 | Sleep = 0b10, | ||
| 65 | Idle = 0b100, | ||
| 66 | HandlePowerKey = 0b1000, | ||
| 67 | HandleSuspendKey = 0b10000, | ||
| 68 | HandleHibernateKey = 0b100000, | ||
| 69 | HandleLidSwitch = 0b1000000, | ||
| 70 | }; | ||
| 71 | Q_ENUM(WhatItem); | ||
| 72 | Q_DECLARE_FLAGS(What, WhatItem); | ||
| 73 | |||
| 74 | enum Mode : uint8_t { | ||
| 75 | Block = 1, | ||
| 76 | BlockWeak = 2, | ||
| 77 | Delay = 3, | ||
| 78 | }; | ||
| 79 | Q_ENUM(Mode); | ||
| 80 | |||
| 81 | Q_INVOKABLE static std::string toString(WhatItem what); | ||
| 82 | Q_INVOKABLE static std::string toString(What what); | ||
| 83 | Q_INVOKABLE static std::string toString(Mode mode); | ||
| 84 | |||
| 85 | static constexpr WhatItem allWhatItems[] = { Shutdown, Sleep, Idle, HandlePowerKey, HandleSuspendKey, HandleHibernateKey, HandleLidSwitch }; | ||
| 86 | }; | ||
| 87 | Q_DECLARE_OPERATORS_FOR_FLAGS(SystemdInhibitorParams::What) | ||
| 88 | |||
| 89 | class SystemdInhibitor : public QObject { | ||
| 90 | Q_OBJECT; | ||
| 91 | Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged); | ||
| 92 | Q_PROPERTY(SystemdInhibitorParams::What what READ what WRITE setWhat NOTIFY whatChanged); | ||
| 93 | Q_PROPERTY(QString who READ who WRITE setWho NOTIFY whoChanged); | ||
| 94 | Q_PROPERTY(QString why READ why WRITE setWhy NOTIFY whyChanged); | ||
| 95 | Q_PROPERTY(SystemdInhibitorParams::Mode mode READ mode WRITE setMode NOTIFY modeChanged); | ||
| 96 | QML_ELEMENT; | ||
| 97 | |||
| 98 | public: | ||
| 99 | explicit SystemdInhibitor(QObject* parent = nullptr): QObject(parent) {} | ||
| 100 | |||
| 101 | bool enabled() const; | ||
| 102 | void setEnabled(bool enabled); | ||
| 103 | |||
| 104 | SystemdInhibitorParams::What what() const; | ||
| 105 | void setWhat(SystemdInhibitorParams::What what); | ||
| 106 | |||
| 107 | QString who() const; | ||
| 108 | void setWho(QString who); | ||
| 109 | |||
| 110 | QString why() const; | ||
| 111 | void setWhy(QString why); | ||
| 112 | |||
| 113 | SystemdInhibitorParams::Mode mode() const; | ||
| 114 | void setMode(SystemdInhibitorParams::Mode mode); | ||
| 115 | |||
| 116 | Q_INVOKABLE void release(); | ||
| 117 | |||
| 118 | signals: | ||
| 119 | void enabledChanged(); | ||
| 120 | void whatChanged(); | ||
| 121 | void whoChanged(); | ||
| 122 | void whyChanged(); | ||
| 123 | void modeChanged(); | ||
| 124 | |||
| 125 | private: | ||
| 126 | class ActiveSystemdInhibitor { | ||
| 127 | public: | ||
| 128 | uint32_t fd = -1; | ||
| 129 | SystemdInhibitorParams::What what; | ||
| 130 | QString who; | ||
| 131 | QString why; | ||
| 132 | SystemdInhibitorParams::Mode mode; | ||
| 133 | |||
| 134 | ActiveSystemdInhibitor(SystemdInhibitorParams::What what_, QString who_, QString why_, SystemdInhibitorParams::Mode mode_); | ||
| 135 | ~ActiveSystemdInhibitor(); | ||
| 136 | }; | ||
| 137 | |||
| 138 | void update(); | ||
| 139 | |||
| 140 | bool mEnabled = true; | ||
| 141 | std::unique_ptr<ActiveSystemdInhibitor> activeInhibitor; | ||
| 142 | SystemdInhibitorParams::What mWhat = static_cast<SystemdInhibitorParams::What>(0); | ||
| 143 | QString mWho; | ||
| 144 | QString mWhy; | ||
| 145 | SystemdInhibitorParams::Mode mMode = static_cast<SystemdInhibitorParams::Mode>(0); | ||
| 146 | }; | ||
| 147 | |||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/customplugin.h b/accounts/gkleen@sif/shell/quickshell-plugins/customplugin.h new file mode 100644 index 00000000..e66ba9e3 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/customplugin.h | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | #include <QQmlEngineExtensionPlugin> | ||
| 2 | |||
| 3 | class CustomPlugin : public QQmlEngineExtensionPlugin | ||
| 4 | { | ||
| 5 | Q_OBJECT | ||
| 6 | Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid) | ||
| 7 | }; | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/default.nix b/accounts/gkleen@sif/shell/quickshell-plugins/default.nix new file mode 100644 index 00000000..20a195eb --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/default.nix | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | { lib | ||
| 2 | , stdenv | ||
| 3 | , cmake | ||
| 4 | , qt6 | ||
| 5 | , fmt | ||
| 6 | , keepassxc | ||
| 7 | , systemd | ||
| 8 | }: | ||
| 9 | |||
| 10 | stdenv.mkDerivation rec { | ||
| 11 | name = "quickshell-custom"; | ||
| 12 | |||
| 13 | src = ./.; | ||
| 14 | |||
| 15 | prePatch = '' | ||
| 16 | cp ${keepassxc.src}/src/gui/org.keepassxc.KeePassXC.MainWindow.xml . | ||
| 17 | ''; | ||
| 18 | |||
| 19 | nativeBuildInputs = [ cmake qt6.wrapQtAppsHook ]; | ||
| 20 | buildInputs = [ | ||
| 21 | qt6.qtbase | ||
| 22 | qt6.qtdeclarative | ||
| 23 | ]; | ||
| 24 | |||
| 25 | cmakeFlags = [ | ||
| 26 | (lib.cmakeFeature "INSTALL_QML_PREFIX" qt6.qtbase.qtQmlPrefix) | ||
| 27 | ]; | ||
| 28 | |||
| 29 | LC_ALL = "C.UTF-8"; | ||
| 30 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.DBus.Properties.xml b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.DBus.Properties.xml new file mode 100644 index 00000000..7588e7a5 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.DBus.Properties.xml | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" | ||
| 2 | "https://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> | ||
| 3 | <node> | ||
| 4 | <interface name="org.freedesktop.DBus.Properties"> | ||
| 5 | <method name="Get"> | ||
| 6 | <arg name="interface_name" direction="in" type="s"/> | ||
| 7 | <arg name="property_name" direction="in" type="s"/> | ||
| 8 | <arg name="value" direction="out" type="v"/> | ||
| 9 | </method> | ||
| 10 | <method name="GetAll"> | ||
| 11 | <arg name="interface_name" direction="in" type="s"/> | ||
| 12 | <arg name="props" direction="out" type="a{sv}"/> | ||
| 13 | <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/> | ||
| 14 | </method> | ||
| 15 | <method name="Set"> | ||
| 16 | <arg name="interface_name" direction="in" type="s"/> | ||
| 17 | <arg name="property_name" direction="in" type="s"/> | ||
| 18 | <arg name="value" direction="in" type="v"/> | ||
| 19 | </method> | ||
| 20 | <signal name="PropertiesChanged"> | ||
| 21 | <arg type="s" name="interface_name"/> | ||
| 22 | <arg type="a{sv}" name="changed_properties"/> | ||
| 23 | <annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/> | ||
| 24 | <arg type="as" name="invalidated_properties"/> | ||
| 25 | </signal> | ||
| 26 | </interface> | ||
| 27 | </node> | ||
| 28 | |||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Manager.xml b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Manager.xml new file mode 100644 index 00000000..120a06d9 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Manager.xml | |||
| @@ -0,0 +1,445 @@ | |||
| 1 | <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" | ||
| 2 | "https://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> | ||
| 3 | <node> | ||
| 4 | <interface name="org.freedesktop.login1.Manager"> | ||
| 5 | <property name="EnableWallMessages" type="b" access="readwrite"> | ||
| 6 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 7 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 8 | </property> | ||
| 9 | <property name="WallMessage" type="s" access="readwrite"> | ||
| 10 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 11 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 12 | </property> | ||
| 13 | <property name="NAutoVTs" type="u" access="read"> | ||
| 14 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 15 | </property> | ||
| 16 | <property name="KillOnlyUsers" type="as" access="read"> | ||
| 17 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 18 | </property> | ||
| 19 | <property name="KillExcludeUsers" type="as" access="read"> | ||
| 20 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 21 | </property> | ||
| 22 | <property name="KillUserProcesses" type="b" access="read"> | ||
| 23 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 24 | </property> | ||
| 25 | <property name="RebootParameter" type="s" access="read"> | ||
| 26 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 27 | </property> | ||
| 28 | <property name="RebootToFirmwareSetup" type="b" access="read"> | ||
| 29 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 30 | </property> | ||
| 31 | <property name="RebootToBootLoaderMenu" type="t" access="read"> | ||
| 32 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 33 | </property> | ||
| 34 | <property name="RebootToBootLoaderEntry" type="s" access="read"> | ||
| 35 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 36 | </property> | ||
| 37 | <property name="BootLoaderEntries" type="as" access="read"> | ||
| 38 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 39 | </property> | ||
| 40 | <property name="IdleHint" type="b" access="read"> | ||
| 41 | </property> | ||
| 42 | <property name="IdleSinceHint" type="t" access="read"> | ||
| 43 | </property> | ||
| 44 | <property name="IdleSinceHintMonotonic" type="t" access="read"> | ||
| 45 | </property> | ||
| 46 | <property name="BlockInhibited" type="s" access="read"> | ||
| 47 | </property> | ||
| 48 | <property name="BlockWeakInhibited" type="s" access="read"> | ||
| 49 | </property> | ||
| 50 | <property name="DelayInhibited" type="s" access="read"> | ||
| 51 | </property> | ||
| 52 | <property name="InhibitDelayMaxUSec" type="t" access="read"> | ||
| 53 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 54 | </property> | ||
| 55 | <property name="UserStopDelayUSec" type="t" access="read"> | ||
| 56 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 57 | </property> | ||
| 58 | <property name="SleepOperation" type="as" access="read"> | ||
| 59 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 60 | </property> | ||
| 61 | <property name="HandlePowerKey" type="s" access="read"> | ||
| 62 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 63 | </property> | ||
| 64 | <property name="HandlePowerKeyLongPress" type="s" access="read"> | ||
| 65 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 66 | </property> | ||
| 67 | <property name="HandleRebootKey" type="s" access="read"> | ||
| 68 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 69 | </property> | ||
| 70 | <property name="HandleRebootKeyLongPress" type="s" access="read"> | ||
| 71 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 72 | </property> | ||
| 73 | <property name="HandleSuspendKey" type="s" access="read"> | ||
| 74 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 75 | </property> | ||
| 76 | <property name="HandleSuspendKeyLongPress" type="s" access="read"> | ||
| 77 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 78 | </property> | ||
| 79 | <property name="HandleHibernateKey" type="s" access="read"> | ||
| 80 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 81 | </property> | ||
| 82 | <property name="HandleHibernateKeyLongPress" type="s" access="read"> | ||
| 83 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 84 | </property> | ||
| 85 | <property name="HandleLidSwitch" type="s" access="read"> | ||
| 86 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 87 | </property> | ||
| 88 | <property name="HandleLidSwitchExternalPower" type="s" access="read"> | ||
| 89 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 90 | </property> | ||
| 91 | <property name="HandleLidSwitchDocked" type="s" access="read"> | ||
| 92 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 93 | </property> | ||
| 94 | <property name="HandleSecureAttentionKey" type="s" access="read"> | ||
| 95 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 96 | </property> | ||
| 97 | <property name="HoldoffTimeoutUSec" type="t" access="read"> | ||
| 98 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 99 | </property> | ||
| 100 | <property name="IdleAction" type="s" access="read"> | ||
| 101 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 102 | </property> | ||
| 103 | <property name="IdleActionUSec" type="t" access="read"> | ||
| 104 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 105 | </property> | ||
| 106 | <property name="PreparingForShutdown" type="b" access="read"> | ||
| 107 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 108 | </property> | ||
| 109 | <property name="PreparingForShutdownWithMetadata" type="a{sv}" access="read"> | ||
| 110 | <annotation name="org.qtproject.QtDBus.QtTypeName" value="QVariantMap"/> | ||
| 111 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 112 | </property> | ||
| 113 | <property name="PreparingForSleep" type="b" access="read"> | ||
| 114 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 115 | </property> | ||
| 116 | <!-- <property name="ScheduledShutdown" type="(st)" access="read"> --> | ||
| 117 | <!-- </property> --> | ||
| 118 | <property name="DesignatedMaintenanceTime" type="s" access="read"> | ||
| 119 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 120 | </property> | ||
| 121 | <property name="Docked" type="b" access="read"> | ||
| 122 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 123 | </property> | ||
| 124 | <property name="LidClosed" type="b" access="read"> | ||
| 125 | </property> | ||
| 126 | <property name="OnExternalPower" type="b" access="read"> | ||
| 127 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 128 | </property> | ||
| 129 | <property name="RemoveIPC" type="b" access="read"> | ||
| 130 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 131 | </property> | ||
| 132 | <property name="RuntimeDirectorySize" type="t" access="read"> | ||
| 133 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 134 | </property> | ||
| 135 | <property name="RuntimeDirectoryInodesMax" type="t" access="read"> | ||
| 136 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 137 | </property> | ||
| 138 | <property name="InhibitorsMax" type="t" access="read"> | ||
| 139 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 140 | </property> | ||
| 141 | <property name="NCurrentInhibitors" type="t" access="read"> | ||
| 142 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 143 | </property> | ||
| 144 | <property name="SessionsMax" type="t" access="read"> | ||
| 145 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 146 | </property> | ||
| 147 | <property name="NCurrentSessions" type="t" access="read"> | ||
| 148 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 149 | </property> | ||
| 150 | <property name="StopIdleSessionUSec" type="t" access="read"> | ||
| 151 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 152 | </property> | ||
| 153 | <method name="GetSession"> | ||
| 154 | <arg type="s" name="session_id" direction="in"/> | ||
| 155 | <arg type="o" name="object_path" direction="out"/> | ||
| 156 | </method> | ||
| 157 | <method name="GetSessionByPID"> | ||
| 158 | <arg type="u" name="pid" direction="in"/> | ||
| 159 | <arg type="o" name="object_path" direction="out"/> | ||
| 160 | </method> | ||
| 161 | <method name="GetUser"> | ||
| 162 | <arg type="u" name="uid" direction="in"/> | ||
| 163 | <arg type="o" name="object_path" direction="out"/> | ||
| 164 | </method> | ||
| 165 | <method name="GetUserByPID"> | ||
| 166 | <arg type="u" name="pid" direction="in"/> | ||
| 167 | <arg type="o" name="object_path" direction="out"/> | ||
| 168 | </method> | ||
| 169 | <method name="GetSeat"> | ||
| 170 | <arg type="s" name="seat_id" direction="in"/> | ||
| 171 | <arg type="o" name="object_path" direction="out"/> | ||
| 172 | </method> | ||
| 173 | <!-- <method name="ListSessions"> --> | ||
| 174 | <!-- <arg type="a(susso)" name="sessions" direction="out"/> --> | ||
| 175 | <!-- </method> --> | ||
| 176 | <!-- <method name="ListSessionsEx"> --> | ||
| 177 | <!-- <arg type="a(sussussbto)" name="sessions" direction="out"/> --> | ||
| 178 | <!-- </method> --> | ||
| 179 | <!-- <method name="ListUsers"> --> | ||
| 180 | <!-- <arg type="a(uso)" name="users" direction="out"/> --> | ||
| 181 | <!-- </method> --> | ||
| 182 | <!-- <method name="ListSeats"> --> | ||
| 183 | <!-- <arg type="a(so)" name="seats" direction="out"/> --> | ||
| 184 | <!-- </method> --> | ||
| 185 | <!-- <method name="ListInhibitors"> --> | ||
| 186 | <!-- <arg type="a(ssssuu)" name="inhibitors" direction="out"/> --> | ||
| 187 | <!-- </method> --> | ||
| 188 | <!-- <method name="CreateSession"> --> | ||
| 189 | <!-- <arg type="u" name="uid" direction="in"/> --> | ||
| 190 | <!-- <arg type="u" name="pid" direction="in"/> --> | ||
| 191 | <!-- <arg type="s" name="service" direction="in"/> --> | ||
| 192 | <!-- <arg type="s" name="type" direction="in"/> --> | ||
| 193 | <!-- <arg type="s" name="class" direction="in"/> --> | ||
| 194 | <!-- <arg type="s" name="desktop" direction="in"/> --> | ||
| 195 | <!-- <arg type="s" name="seat_id" direction="in"/> --> | ||
| 196 | <!-- <arg type="u" name="vtnr" direction="in"/> --> | ||
| 197 | <!-- <arg type="s" name="tty" direction="in"/> --> | ||
| 198 | <!-- <arg type="s" name="display" direction="in"/> --> | ||
| 199 | <!-- <arg type="b" name="remote" direction="in"/> --> | ||
| 200 | <!-- <arg type="s" name="remote_user" direction="in"/> --> | ||
| 201 | <!-- <arg type="s" name="remote_host" direction="in"/> --> | ||
| 202 | <!-- <arg type="a(sv)" name="properties" direction="in"/> --> | ||
| 203 | <!-- <arg type="s" name="session_id" direction="out"/> --> | ||
| 204 | <!-- <arg type="o" name="object_path" direction="out"/> --> | ||
| 205 | <!-- <arg type="s" name="runtime_path" direction="out"/> --> | ||
| 206 | <!-- <arg type="h" name="fifo_fd" direction="out"/> --> | ||
| 207 | <!-- <arg type="u" name="uid" direction="out"/> --> | ||
| 208 | <!-- <arg type="s" name="seat_id" direction="out"/> --> | ||
| 209 | <!-- <arg type="u" name="vtnr" direction="out"/> --> | ||
| 210 | <!-- <arg type="b" name="existing" direction="out"/> --> | ||
| 211 | <!-- <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> --> | ||
| 212 | <!-- </method> --> | ||
| 213 | <!-- <method name="CreateSessionWithPIDFD"> --> | ||
| 214 | <!-- <arg type="u" name="uid" direction="in"/> --> | ||
| 215 | <!-- <arg type="h" name="pidfd" direction="in"/> --> | ||
| 216 | <!-- <arg type="s" name="service" direction="in"/> --> | ||
| 217 | <!-- <arg type="s" name="type" direction="in"/> --> | ||
| 218 | <!-- <arg type="s" name="class" direction="in"/> --> | ||
| 219 | <!-- <arg type="s" name="desktop" direction="in"/> --> | ||
| 220 | <!-- <arg type="s" name="seat_id" direction="in"/> --> | ||
| 221 | <!-- <arg type="u" name="vtnr" direction="in"/> --> | ||
| 222 | <!-- <arg type="s" name="tty" direction="in"/> --> | ||
| 223 | <!-- <arg type="s" name="display" direction="in"/> --> | ||
| 224 | <!-- <arg type="b" name="remote" direction="in"/> --> | ||
| 225 | <!-- <arg type="s" name="remote_user" direction="in"/> --> | ||
| 226 | <!-- <arg type="s" name="remote_host" direction="in"/> --> | ||
| 227 | <!-- <arg type="t" name="flags" direction="in"/> --> | ||
| 228 | <!-- <arg type="a(sv)" name="properties" direction="in"/> --> | ||
| 229 | <!-- <arg type="s" name="session_id" direction="out"/> --> | ||
| 230 | <!-- <arg type="o" name="object_path" direction="out"/> --> | ||
| 231 | <!-- <arg type="s" name="runtime_path" direction="out"/> --> | ||
| 232 | <!-- <arg type="h" name="fifo_fd" direction="out"/> --> | ||
| 233 | <!-- <arg type="u" name="uid" direction="out"/> --> | ||
| 234 | <!-- <arg type="s" name="seat_id" direction="out"/> --> | ||
| 235 | <!-- <arg type="u" name="vtnr" direction="out"/> --> | ||
| 236 | <!-- <arg type="b" name="existing" direction="out"/> --> | ||
| 237 | <!-- <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> --> | ||
| 238 | <!-- </method> --> | ||
| 239 | <method name="ReleaseSession"> | ||
| 240 | <arg type="s" name="session_id" direction="in"/> | ||
| 241 | </method> | ||
| 242 | <method name="ActivateSession"> | ||
| 243 | <arg type="s" name="session_id" direction="in"/> | ||
| 244 | </method> | ||
| 245 | <method name="ActivateSessionOnSeat"> | ||
| 246 | <arg type="s" name="session_id" direction="in"/> | ||
| 247 | <arg type="s" name="seat_id" direction="in"/> | ||
| 248 | </method> | ||
| 249 | <method name="LockSession"> | ||
| 250 | <arg type="s" name="session_id" direction="in"/> | ||
| 251 | </method> | ||
| 252 | <method name="UnlockSession"> | ||
| 253 | <arg type="s" name="session_id" direction="in"/> | ||
| 254 | </method> | ||
| 255 | <method name="LockSessions"> | ||
| 256 | </method> | ||
| 257 | <method name="UnlockSessions"> | ||
| 258 | </method> | ||
| 259 | <method name="KillSession"> | ||
| 260 | <arg type="s" name="session_id" direction="in"/> | ||
| 261 | <arg type="s" name="whom" direction="in"/> | ||
| 262 | <arg type="i" name="signal_number" direction="in"/> | ||
| 263 | </method> | ||
| 264 | <method name="KillUser"> | ||
| 265 | <arg type="u" name="uid" direction="in"/> | ||
| 266 | <arg type="i" name="signal_number" direction="in"/> | ||
| 267 | </method> | ||
| 268 | <method name="TerminateSession"> | ||
| 269 | <arg type="s" name="session_id" direction="in"/> | ||
| 270 | </method> | ||
| 271 | <method name="TerminateUser"> | ||
| 272 | <arg type="u" name="uid" direction="in"/> | ||
| 273 | </method> | ||
| 274 | <method name="TerminateSeat"> | ||
| 275 | <arg type="s" name="seat_id" direction="in"/> | ||
| 276 | </method> | ||
| 277 | <method name="SetUserLinger"> | ||
| 278 | <arg type="u" name="uid" direction="in"/> | ||
| 279 | <arg type="b" name="enable" direction="in"/> | ||
| 280 | <arg type="b" name="interactive" direction="in"/> | ||
| 281 | </method> | ||
| 282 | <method name="AttachDevice"> | ||
| 283 | <arg type="s" name="seat_id" direction="in"/> | ||
| 284 | <arg type="s" name="sysfs_path" direction="in"/> | ||
| 285 | <arg type="b" name="interactive" direction="in"/> | ||
| 286 | </method> | ||
| 287 | <method name="FlushDevices"> | ||
| 288 | <arg type="b" name="interactive" direction="in"/> | ||
| 289 | </method> | ||
| 290 | <method name="PowerOff"> | ||
| 291 | <arg type="b" name="interactive" direction="in"/> | ||
| 292 | </method> | ||
| 293 | <method name="PowerOffWithFlags"> | ||
| 294 | <arg type="t" name="flags" direction="in"/> | ||
| 295 | </method> | ||
| 296 | <method name="Reboot"> | ||
| 297 | <arg type="b" name="interactive" direction="in"/> | ||
| 298 | </method> | ||
| 299 | <method name="RebootWithFlags"> | ||
| 300 | <arg type="t" name="flags" direction="in"/> | ||
| 301 | </method> | ||
| 302 | <method name="Halt"> | ||
| 303 | <arg type="b" name="interactive" direction="in"/> | ||
| 304 | </method> | ||
| 305 | <method name="HaltWithFlags"> | ||
| 306 | <arg type="t" name="flags" direction="in"/> | ||
| 307 | </method> | ||
| 308 | <method name="Suspend"> | ||
| 309 | <arg type="b" name="interactive" direction="in"/> | ||
| 310 | </method> | ||
| 311 | <method name="SuspendWithFlags"> | ||
| 312 | <arg type="t" name="flags" direction="in"/> | ||
| 313 | </method> | ||
| 314 | <method name="Hibernate"> | ||
| 315 | <arg type="b" name="interactive" direction="in"/> | ||
| 316 | </method> | ||
| 317 | <method name="HibernateWithFlags"> | ||
| 318 | <arg type="t" name="flags" direction="in"/> | ||
| 319 | </method> | ||
| 320 | <method name="HybridSleep"> | ||
| 321 | <arg type="b" name="interactive" direction="in"/> | ||
| 322 | </method> | ||
| 323 | <method name="HybridSleepWithFlags"> | ||
| 324 | <arg type="t" name="flags" direction="in"/> | ||
| 325 | </method> | ||
| 326 | <method name="SuspendThenHibernate"> | ||
| 327 | <arg type="b" name="interactive" direction="in"/> | ||
| 328 | </method> | ||
| 329 | <method name="SuspendThenHibernateWithFlags"> | ||
| 330 | <arg type="t" name="flags" direction="in"/> | ||
| 331 | </method> | ||
| 332 | <method name="Sleep"> | ||
| 333 | <arg type="t" name="flags" direction="in"/> | ||
| 334 | </method> | ||
| 335 | <method name="CanPowerOff"> | ||
| 336 | <arg type="s" name="result" direction="out"/> | ||
| 337 | </method> | ||
| 338 | <method name="CanReboot"> | ||
| 339 | <arg type="s" name="result" direction="out"/> | ||
| 340 | </method> | ||
| 341 | <method name="CanHalt"> | ||
| 342 | <arg type="s" name="result" direction="out"/> | ||
| 343 | </method> | ||
| 344 | <method name="CanSuspend"> | ||
| 345 | <arg type="s" name="result" direction="out"/> | ||
| 346 | </method> | ||
| 347 | <method name="CanHibernate"> | ||
| 348 | <arg type="s" name="result" direction="out"/> | ||
| 349 | </method> | ||
| 350 | <method name="CanHybridSleep"> | ||
| 351 | <arg type="s" name="result" direction="out"/> | ||
| 352 | </method> | ||
| 353 | <method name="CanSuspendThenHibernate"> | ||
| 354 | <arg type="s" name="result" direction="out"/> | ||
| 355 | </method> | ||
| 356 | <method name="CanSleep"> | ||
| 357 | <arg type="s" name="result" direction="out"/> | ||
| 358 | </method> | ||
| 359 | <method name="ScheduleShutdown"> | ||
| 360 | <arg type="s" name="type" direction="in"/> | ||
| 361 | <arg type="t" name="usec" direction="in"/> | ||
| 362 | </method> | ||
| 363 | <method name="CancelScheduledShutdown"> | ||
| 364 | <arg type="b" name="cancelled" direction="out"/> | ||
| 365 | </method> | ||
| 366 | <method name="Inhibit"> | ||
| 367 | <arg type="s" name="what" direction="in"/> | ||
| 368 | <arg type="s" name="who" direction="in"/> | ||
| 369 | <arg type="s" name="why" direction="in"/> | ||
| 370 | <arg type="s" name="mode" direction="in"/> | ||
| 371 | <arg type="h" name="pipe_fd" direction="out"/> | ||
| 372 | </method> | ||
| 373 | <method name="CanRebootParameter"> | ||
| 374 | <arg type="s" name="result" direction="out"/> | ||
| 375 | </method> | ||
| 376 | <method name="SetRebootParameter"> | ||
| 377 | <arg type="s" name="parameter" direction="in"/> | ||
| 378 | </method> | ||
| 379 | <method name="CanRebootToFirmwareSetup"> | ||
| 380 | <arg type="s" name="result" direction="out"/> | ||
| 381 | </method> | ||
| 382 | <method name="SetRebootToFirmwareSetup"> | ||
| 383 | <arg type="b" name="enable" direction="in"/> | ||
| 384 | </method> | ||
| 385 | <method name="CanRebootToBootLoaderMenu"> | ||
| 386 | <arg type="s" name="result" direction="out"/> | ||
| 387 | </method> | ||
| 388 | <method name="SetRebootToBootLoaderMenu"> | ||
| 389 | <arg type="t" name="timeout" direction="in"/> | ||
| 390 | </method> | ||
| 391 | <method name="CanRebootToBootLoaderEntry"> | ||
| 392 | <arg type="s" name="result" direction="out"/> | ||
| 393 | </method> | ||
| 394 | <method name="SetRebootToBootLoaderEntry"> | ||
| 395 | <arg type="s" name="boot_loader_entry" direction="in"/> | ||
| 396 | </method> | ||
| 397 | <method name="SetWallMessage"> | ||
| 398 | <arg type="s" name="wall_message" direction="in"/> | ||
| 399 | <arg type="b" name="enable" direction="in"/> | ||
| 400 | </method> | ||
| 401 | <signal name="SecureAttentionKey"> | ||
| 402 | <arg type="s" name="seat_id"/> | ||
| 403 | <arg type="o" name="object_path"/> | ||
| 404 | </signal> | ||
| 405 | <signal name="SessionNew"> | ||
| 406 | <arg type="s" name="session_id"/> | ||
| 407 | <arg type="o" name="object_path"/> | ||
| 408 | </signal> | ||
| 409 | <signal name="SessionRemoved"> | ||
| 410 | <arg type="s" name="session_id"/> | ||
| 411 | <arg type="o" name="object_path"/> | ||
| 412 | </signal> | ||
| 413 | <signal name="UserNew"> | ||
| 414 | <arg type="u" name="uid"/> | ||
| 415 | <arg type="o" name="object_path"/> | ||
| 416 | </signal> | ||
| 417 | <signal name="UserRemoved"> | ||
| 418 | <arg type="u" name="uid"/> | ||
| 419 | <arg type="o" name="object_path"/> | ||
| 420 | </signal> | ||
| 421 | <signal name="SeatNew"> | ||
| 422 | <arg type="s" name="seat_id"/> | ||
| 423 | <arg type="o" name="object_path"/> | ||
| 424 | </signal> | ||
| 425 | <signal name="SeatRemoved"> | ||
| 426 | <arg type="s" name="seat_id"/> | ||
| 427 | <arg type="o" name="object_path"/> | ||
| 428 | </signal> | ||
| 429 | <signal name="PrepareForShutdown"> | ||
| 430 | <arg type="b" name="start"/> | ||
| 431 | </signal> | ||
| 432 | <signal name="PrepareForShutdownWithMetadata"> | ||
| 433 | <arg type="b" name="start"/> | ||
| 434 | <arg type="a{sv}" name="metadata"/> | ||
| 435 | <annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/> | ||
| 436 | </signal> | ||
| 437 | <signal name="PrepareForSleep"> | ||
| 438 | <arg type="b" name="start"/> | ||
| 439 | </signal> | ||
| 440 | </interface> | ||
| 441 | <node name="user"/> | ||
| 442 | <node name="session"/> | ||
| 443 | <node name="seat"/> | ||
| 444 | </node> | ||
| 445 | |||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Session.xml b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Session.xml new file mode 100644 index 00000000..7d6fc8ee --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Session.xml | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" | ||
| 2 | "https://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> | ||
| 3 | <node> | ||
| 4 | <interface name="org.freedesktop.login1.Session"> | ||
| 5 | <property name="Id" type="s" access="read"> | ||
| 6 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 7 | </property> | ||
| 8 | <property name="User" type="o" access="read"> | ||
| 9 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 10 | </property> | ||
| 11 | <property name="Name" type="s" access="read"> | ||
| 12 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 13 | </property> | ||
| 14 | <property name="Timestamp" type="t" access="read"> | ||
| 15 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 16 | </property> | ||
| 17 | <property name="TimestampMonotonic" type="t" access="read"> | ||
| 18 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 19 | </property> | ||
| 20 | <property name="VTNr" type="u" access="read"> | ||
| 21 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 22 | </property> | ||
| 23 | <property name="Seat" type="o" access="read"> | ||
| 24 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 25 | </property> | ||
| 26 | <property name="TTY" type="s" access="read"> | ||
| 27 | </property> | ||
| 28 | <property name="Display" type="s" access="read"> | ||
| 29 | </property> | ||
| 30 | <property name="Remote" type="b" access="read"> | ||
| 31 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 32 | </property> | ||
| 33 | <property name="RemoteHost" type="s" access="read"> | ||
| 34 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 35 | </property> | ||
| 36 | <property name="RemoteUser" type="s" access="read"> | ||
| 37 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 38 | </property> | ||
| 39 | <property name="Service" type="s" access="read"> | ||
| 40 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 41 | </property> | ||
| 42 | <property name="Desktop" type="s" access="read"> | ||
| 43 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 44 | </property> | ||
| 45 | <property name="Scope" type="s" access="read"> | ||
| 46 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 47 | </property> | ||
| 48 | <property name="Leader" type="u" access="read"> | ||
| 49 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 50 | </property> | ||
| 51 | <property name="Audit" type="u" access="read"> | ||
| 52 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 53 | </property> | ||
| 54 | <property name="Type" type="s" access="read"> | ||
| 55 | </property> | ||
| 56 | <!-- <property name="Class" type="s" access="read"> --> | ||
| 57 | <!-- </property> --> | ||
| 58 | <property name="Active" type="b" access="read"> | ||
| 59 | </property> | ||
| 60 | <property name="State" type="s" access="read"> | ||
| 61 | </property> | ||
| 62 | <property name="IdleHint" type="b" access="read"> | ||
| 63 | </property> | ||
| 64 | <property name="IdleSinceHint" type="t" access="read"> | ||
| 65 | </property> | ||
| 66 | <property name="IdleSinceHintMonotonic" type="t" access="read"> | ||
| 67 | </property> | ||
| 68 | <property name="CanIdle" type="b" access="read"> | ||
| 69 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 70 | </property> | ||
| 71 | <property name="CanLock" type="b" access="read"> | ||
| 72 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 73 | </property> | ||
| 74 | <property name="LockedHint" type="b" access="read"> | ||
| 75 | </property> | ||
| 76 | <method name="Terminate"> | ||
| 77 | </method> | ||
| 78 | <method name="Activate"> | ||
| 79 | </method> | ||
| 80 | <!-- <method name="Lock"> --> | ||
| 81 | <!-- </method> --> | ||
| 82 | <!-- <method name="Unlock"> --> | ||
| 83 | <!-- </method> --> | ||
| 84 | <method name="SetIdleHint"> | ||
| 85 | <arg type="b" name="idle" direction="in"/> | ||
| 86 | </method> | ||
| 87 | <method name="SetLockedHint"> | ||
| 88 | <arg type="b" name="locked" direction="in"/> | ||
| 89 | </method> | ||
| 90 | <method name="Kill"> | ||
| 91 | <arg type="s" name="whom" direction="in"/> | ||
| 92 | <arg type="i" name="signal_number" direction="in"/> | ||
| 93 | </method> | ||
| 94 | <method name="TakeControl"> | ||
| 95 | <arg type="b" name="force" direction="in"/> | ||
| 96 | </method> | ||
| 97 | <method name="ReleaseControl"> | ||
| 98 | </method> | ||
| 99 | <method name="SetType"> | ||
| 100 | <arg type="s" name="type" direction="in"/> | ||
| 101 | </method> | ||
| 102 | <!-- <method name="SetClass"> --> | ||
| 103 | <!-- <arg type="s" name="class" direction="in"/> --> | ||
| 104 | <!-- </method> --> | ||
| 105 | <method name="SetDisplay"> | ||
| 106 | <arg type="s" name="display" direction="in"/> | ||
| 107 | </method> | ||
| 108 | <method name="SetTTY"> | ||
| 109 | <arg type="h" name="tty_fd" direction="in"/> | ||
| 110 | </method> | ||
| 111 | <method name="TakeDevice"> | ||
| 112 | <arg type="u" name="major" direction="in"/> | ||
| 113 | <arg type="u" name="minor" direction="in"/> | ||
| 114 | <arg type="h" name="fd" direction="out"/> | ||
| 115 | <arg type="b" name="inactive" direction="out"/> | ||
| 116 | </method> | ||
| 117 | <method name="ReleaseDevice"> | ||
| 118 | <arg type="u" name="major" direction="in"/> | ||
| 119 | <arg type="u" name="minor" direction="in"/> | ||
| 120 | </method> | ||
| 121 | <method name="PauseDeviceComplete"> | ||
| 122 | <arg type="u" name="major" direction="in"/> | ||
| 123 | <arg type="u" name="minor" direction="in"/> | ||
| 124 | </method> | ||
| 125 | <method name="SetBrightness"> | ||
| 126 | <arg type="s" name="subsystem" direction="in"/> | ||
| 127 | <arg type="s" name="name" direction="in"/> | ||
| 128 | <arg type="u" name="brightness" direction="in"/> | ||
| 129 | </method> | ||
| 130 | <signal name="PauseDevice"> | ||
| 131 | <arg type="u" name="major"/> | ||
| 132 | <arg type="u" name="minor"/> | ||
| 133 | <arg type="s" name="type"/> | ||
| 134 | </signal> | ||
| 135 | <signal name="ResumeDevice"> | ||
| 136 | <arg type="u" name="major"/> | ||
| 137 | <arg type="u" name="minor"/> | ||
| 138 | <arg type="h" name="fd"/> | ||
| 139 | </signal> | ||
| 140 | <signal name="Lock"> | ||
| 141 | </signal> | ||
| 142 | <signal name="Unlock"> | ||
| 143 | </signal> | ||
| 144 | </interface> | ||
| 145 | </node> | ||
| 146 | |||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.systemd1.Manager.xml b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.systemd1.Manager.xml new file mode 100644 index 00000000..b4f84a13 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.systemd1.Manager.xml | |||
| @@ -0,0 +1,817 @@ | |||
| 1 | <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" | ||
| 2 | "https://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> | ||
| 3 | <node> | ||
| 4 | <interface name="org.freedesktop.systemd1.Manager"> | ||
| 5 | <property name="Version" type="s" access="read"> | ||
| 6 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 7 | </property> | ||
| 8 | <property name="Features" type="s" access="read"> | ||
| 9 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 10 | </property> | ||
| 11 | <property name="Virtualization" type="s" access="read"> | ||
| 12 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 13 | </property> | ||
| 14 | <property name="ConfidentialVirtualization" type="s" access="read"> | ||
| 15 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 16 | </property> | ||
| 17 | <property name="Architecture" type="s" access="read"> | ||
| 18 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 19 | </property> | ||
| 20 | <property name="Tainted" type="s" access="read"> | ||
| 21 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 22 | </property> | ||
| 23 | <property name="FirmwareTimestamp" type="t" access="read"> | ||
| 24 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 25 | </property> | ||
| 26 | <property name="FirmwareTimestampMonotonic" type="t" access="read"> | ||
| 27 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 28 | </property> | ||
| 29 | <property name="LoaderTimestamp" type="t" access="read"> | ||
| 30 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 31 | </property> | ||
| 32 | <property name="LoaderTimestampMonotonic" type="t" access="read"> | ||
| 33 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 34 | </property> | ||
| 35 | <property name="KernelTimestamp" type="t" access="read"> | ||
| 36 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 37 | </property> | ||
| 38 | <property name="KernelTimestampMonotonic" type="t" access="read"> | ||
| 39 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 40 | </property> | ||
| 41 | <property name="InitRDTimestamp" type="t" access="read"> | ||
| 42 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 43 | </property> | ||
| 44 | <property name="InitRDTimestampMonotonic" type="t" access="read"> | ||
| 45 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 46 | </property> | ||
| 47 | <property name="UserspaceTimestamp" type="t" access="read"> | ||
| 48 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 49 | </property> | ||
| 50 | <property name="UserspaceTimestampMonotonic" type="t" access="read"> | ||
| 51 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 52 | </property> | ||
| 53 | <property name="FinishTimestamp" type="t" access="read"> | ||
| 54 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 55 | </property> | ||
| 56 | <property name="FinishTimestampMonotonic" type="t" access="read"> | ||
| 57 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 58 | </property> | ||
| 59 | <property name="ShutdownStartTimestamp" type="t" access="read"> | ||
| 60 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 61 | </property> | ||
| 62 | <property name="ShutdownStartTimestampMonotonic" type="t" access="read"> | ||
| 63 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 64 | </property> | ||
| 65 | <property name="SecurityStartTimestamp" type="t" access="read"> | ||
| 66 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 67 | </property> | ||
| 68 | <property name="SecurityStartTimestampMonotonic" type="t" access="read"> | ||
| 69 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 70 | </property> | ||
| 71 | <property name="SecurityFinishTimestamp" type="t" access="read"> | ||
| 72 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 73 | </property> | ||
| 74 | <property name="SecurityFinishTimestampMonotonic" type="t" access="read"> | ||
| 75 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 76 | </property> | ||
| 77 | <property name="GeneratorsStartTimestamp" type="t" access="read"> | ||
| 78 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 79 | </property> | ||
| 80 | <property name="GeneratorsStartTimestampMonotonic" type="t" access="read"> | ||
| 81 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 82 | </property> | ||
| 83 | <property name="GeneratorsFinishTimestamp" type="t" access="read"> | ||
| 84 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 85 | </property> | ||
| 86 | <property name="GeneratorsFinishTimestampMonotonic" type="t" access="read"> | ||
| 87 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 88 | </property> | ||
| 89 | <property name="UnitsLoadStartTimestamp" type="t" access="read"> | ||
| 90 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 91 | </property> | ||
| 92 | <property name="UnitsLoadStartTimestampMonotonic" type="t" access="read"> | ||
| 93 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 94 | </property> | ||
| 95 | <property name="UnitsLoadFinishTimestamp" type="t" access="read"> | ||
| 96 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 97 | </property> | ||
| 98 | <property name="UnitsLoadFinishTimestampMonotonic" type="t" access="read"> | ||
| 99 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 100 | </property> | ||
| 101 | <property name="UnitsLoadTimestamp" type="t" access="read"> | ||
| 102 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 103 | </property> | ||
| 104 | <property name="UnitsLoadTimestampMonotonic" type="t" access="read"> | ||
| 105 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 106 | </property> | ||
| 107 | <property name="InitRDSecurityStartTimestamp" type="t" access="read"> | ||
| 108 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 109 | </property> | ||
| 110 | <property name="InitRDSecurityStartTimestampMonotonic" type="t" access="read"> | ||
| 111 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 112 | </property> | ||
| 113 | <property name="InitRDSecurityFinishTimestamp" type="t" access="read"> | ||
| 114 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 115 | </property> | ||
| 116 | <property name="InitRDSecurityFinishTimestampMonotonic" type="t" access="read"> | ||
| 117 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 118 | </property> | ||
| 119 | <property name="InitRDGeneratorsStartTimestamp" type="t" access="read"> | ||
| 120 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 121 | </property> | ||
| 122 | <property name="InitRDGeneratorsStartTimestampMonotonic" type="t" access="read"> | ||
| 123 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 124 | </property> | ||
| 125 | <property name="InitRDGeneratorsFinishTimestamp" type="t" access="read"> | ||
| 126 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 127 | </property> | ||
| 128 | <property name="InitRDGeneratorsFinishTimestampMonotonic" type="t" access="read"> | ||
| 129 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 130 | </property> | ||
| 131 | <property name="InitRDUnitsLoadStartTimestamp" type="t" access="read"> | ||
| 132 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 133 | </property> | ||
| 134 | <property name="InitRDUnitsLoadStartTimestampMonotonic" type="t" access="read"> | ||
| 135 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 136 | </property> | ||
| 137 | <property name="InitRDUnitsLoadFinishTimestamp" type="t" access="read"> | ||
| 138 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 139 | </property> | ||
| 140 | <property name="InitRDUnitsLoadFinishTimestampMonotonic" type="t" access="read"> | ||
| 141 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 142 | </property> | ||
| 143 | <property name="LogLevel" type="s" access="readwrite"> | ||
| 144 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 145 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 146 | </property> | ||
| 147 | <property name="LogTarget" type="s" access="readwrite"> | ||
| 148 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 149 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 150 | </property> | ||
| 151 | <property name="NNames" type="u" access="read"> | ||
| 152 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 153 | </property> | ||
| 154 | <property name="NFailedUnits" type="u" access="read"> | ||
| 155 | </property> | ||
| 156 | <property name="NJobs" type="u" access="read"> | ||
| 157 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 158 | </property> | ||
| 159 | <property name="NInstalledJobs" type="u" access="read"> | ||
| 160 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 161 | </property> | ||
| 162 | <property name="NFailedJobs" type="u" access="read"> | ||
| 163 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 164 | </property> | ||
| 165 | <property name="Progress" type="d" access="read"> | ||
| 166 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 167 | </property> | ||
| 168 | <property name="Environment" type="as" access="read"> | ||
| 169 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 170 | </property> | ||
| 171 | <property name="ConfirmSpawn" type="b" access="read"> | ||
| 172 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 173 | </property> | ||
| 174 | <property name="ShowStatus" type="b" access="read"> | ||
| 175 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 176 | </property> | ||
| 177 | <property name="UnitPath" type="as" access="read"> | ||
| 178 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 179 | </property> | ||
| 180 | <property name="DefaultStandardOutput" type="s" access="read"> | ||
| 181 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 182 | </property> | ||
| 183 | <property name="DefaultStandardError" type="s" access="read"> | ||
| 184 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 185 | </property> | ||
| 186 | <property name="WatchdogDevice" type="s" access="read"> | ||
| 187 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 188 | </property> | ||
| 189 | <property name="WatchdogLastPingTimestamp" type="t" access="read"> | ||
| 190 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 191 | </property> | ||
| 192 | <property name="WatchdogLastPingTimestampMonotonic" type="t" access="read"> | ||
| 193 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 194 | </property> | ||
| 195 | <property name="RuntimeWatchdogUSec" type="t" access="readwrite"> | ||
| 196 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 197 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 198 | </property> | ||
| 199 | <property name="RuntimeWatchdogPreUSec" type="t" access="readwrite"> | ||
| 200 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 201 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 202 | </property> | ||
| 203 | <property name="RuntimeWatchdogPreGovernor" type="s" access="readwrite"> | ||
| 204 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 205 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 206 | </property> | ||
| 207 | <property name="RebootWatchdogUSec" type="t" access="readwrite"> | ||
| 208 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 209 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 210 | </property> | ||
| 211 | <property name="KExecWatchdogUSec" type="t" access="readwrite"> | ||
| 212 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 213 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 214 | </property> | ||
| 215 | <property name="ServiceWatchdogs" type="b" access="readwrite"> | ||
| 216 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 217 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 218 | </property> | ||
| 219 | <property name="ControlGroup" type="s" access="read"> | ||
| 220 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 221 | </property> | ||
| 222 | <property name="SystemState" type="s" access="read"> | ||
| 223 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 224 | </property> | ||
| 225 | <property name="ExitCode" type="y" access="read"> | ||
| 226 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 227 | </property> | ||
| 228 | <property name="DefaultTimerAccuracyUSec" type="t" access="read"> | ||
| 229 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 230 | </property> | ||
| 231 | <property name="DefaultTimeoutStartUSec" type="t" access="read"> | ||
| 232 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 233 | </property> | ||
| 234 | <property name="DefaultTimeoutStopUSec" type="t" access="read"> | ||
| 235 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 236 | </property> | ||
| 237 | <property name="DefaultTimeoutAbortUSec" type="t" access="read"> | ||
| 238 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 239 | </property> | ||
| 240 | <property name="DefaultDeviceTimeoutUSec" type="t" access="read"> | ||
| 241 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 242 | </property> | ||
| 243 | <property name="DefaultRestartUSec" type="t" access="read"> | ||
| 244 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 245 | </property> | ||
| 246 | <property name="DefaultStartLimitIntervalUSec" type="t" access="read"> | ||
| 247 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 248 | </property> | ||
| 249 | <property name="DefaultStartLimitBurst" type="u" access="read"> | ||
| 250 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 251 | </property> | ||
| 252 | <property name="DefaultCPUAccounting" type="b" access="read"> | ||
| 253 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 254 | </property> | ||
| 255 | <property name="DefaultBlockIOAccounting" type="b" access="read"> | ||
| 256 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 257 | </property> | ||
| 258 | <property name="DefaultIOAccounting" type="b" access="read"> | ||
| 259 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 260 | </property> | ||
| 261 | <property name="DefaultIPAccounting" type="b" access="read"> | ||
| 262 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 263 | </property> | ||
| 264 | <property name="DefaultMemoryAccounting" type="b" access="read"> | ||
| 265 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 266 | </property> | ||
| 267 | <property name="DefaultTasksAccounting" type="b" access="read"> | ||
| 268 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 269 | </property> | ||
| 270 | <property name="DefaultLimitCPU" type="t" access="read"> | ||
| 271 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 272 | </property> | ||
| 273 | <property name="DefaultLimitCPUSoft" type="t" access="read"> | ||
| 274 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 275 | </property> | ||
| 276 | <property name="DefaultLimitFSIZE" type="t" access="read"> | ||
| 277 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 278 | </property> | ||
| 279 | <property name="DefaultLimitFSIZESoft" type="t" access="read"> | ||
| 280 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 281 | </property> | ||
| 282 | <property name="DefaultLimitDATA" type="t" access="read"> | ||
| 283 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 284 | </property> | ||
| 285 | <property name="DefaultLimitDATASoft" type="t" access="read"> | ||
| 286 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 287 | </property> | ||
| 288 | <property name="DefaultLimitSTACK" type="t" access="read"> | ||
| 289 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 290 | </property> | ||
| 291 | <property name="DefaultLimitSTACKSoft" type="t" access="read"> | ||
| 292 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 293 | </property> | ||
| 294 | <property name="DefaultLimitCORE" type="t" access="read"> | ||
| 295 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 296 | </property> | ||
| 297 | <property name="DefaultLimitCORESoft" type="t" access="read"> | ||
| 298 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 299 | </property> | ||
| 300 | <property name="DefaultLimitRSS" type="t" access="read"> | ||
| 301 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 302 | </property> | ||
| 303 | <property name="DefaultLimitRSSSoft" type="t" access="read"> | ||
| 304 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 305 | </property> | ||
| 306 | <property name="DefaultLimitNOFILE" type="t" access="read"> | ||
| 307 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 308 | </property> | ||
| 309 | <property name="DefaultLimitNOFILESoft" type="t" access="read"> | ||
| 310 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 311 | </property> | ||
| 312 | <property name="DefaultLimitAS" type="t" access="read"> | ||
| 313 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 314 | </property> | ||
| 315 | <property name="DefaultLimitASSoft" type="t" access="read"> | ||
| 316 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 317 | </property> | ||
| 318 | <property name="DefaultLimitNPROC" type="t" access="read"> | ||
| 319 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 320 | </property> | ||
| 321 | <property name="DefaultLimitNPROCSoft" type="t" access="read"> | ||
| 322 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 323 | </property> | ||
| 324 | <property name="DefaultLimitMEMLOCK" type="t" access="read"> | ||
| 325 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 326 | </property> | ||
| 327 | <property name="DefaultLimitMEMLOCKSoft" type="t" access="read"> | ||
| 328 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 329 | </property> | ||
| 330 | <property name="DefaultLimitLOCKS" type="t" access="read"> | ||
| 331 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 332 | </property> | ||
| 333 | <property name="DefaultLimitLOCKSSoft" type="t" access="read"> | ||
| 334 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 335 | </property> | ||
| 336 | <property name="DefaultLimitSIGPENDING" type="t" access="read"> | ||
| 337 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 338 | </property> | ||
| 339 | <property name="DefaultLimitSIGPENDINGSoft" type="t" access="read"> | ||
| 340 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 341 | </property> | ||
| 342 | <property name="DefaultLimitMSGQUEUE" type="t" access="read"> | ||
| 343 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 344 | </property> | ||
| 345 | <property name="DefaultLimitMSGQUEUESoft" type="t" access="read"> | ||
| 346 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 347 | </property> | ||
| 348 | <property name="DefaultLimitNICE" type="t" access="read"> | ||
| 349 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 350 | </property> | ||
| 351 | <property name="DefaultLimitNICESoft" type="t" access="read"> | ||
| 352 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 353 | </property> | ||
| 354 | <property name="DefaultLimitRTPRIO" type="t" access="read"> | ||
| 355 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 356 | </property> | ||
| 357 | <property name="DefaultLimitRTPRIOSoft" type="t" access="read"> | ||
| 358 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 359 | </property> | ||
| 360 | <property name="DefaultLimitRTTIME" type="t" access="read"> | ||
| 361 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 362 | </property> | ||
| 363 | <property name="DefaultLimitRTTIMESoft" type="t" access="read"> | ||
| 364 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 365 | </property> | ||
| 366 | <property name="DefaultTasksMax" type="t" access="read"> | ||
| 367 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 368 | </property> | ||
| 369 | <property name="DefaultMemoryPressureThresholdUSec" type="t" access="read"> | ||
| 370 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 371 | </property> | ||
| 372 | <property name="DefaultMemoryPressureWatch" type="s" access="read"> | ||
| 373 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 374 | </property> | ||
| 375 | <property name="TimerSlackNSec" type="t" access="read"> | ||
| 376 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 377 | </property> | ||
| 378 | <property name="DefaultOOMPolicy" type="s" access="read"> | ||
| 379 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 380 | </property> | ||
| 381 | <property name="DefaultOOMScoreAdjust" type="i" access="read"> | ||
| 382 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 383 | </property> | ||
| 384 | <property name="CtrlAltDelBurstAction" type="s" access="read"> | ||
| 385 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 386 | </property> | ||
| 387 | <property name="SoftRebootsCount" type="u" access="read"> | ||
| 388 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 389 | </property> | ||
| 390 | <method name="GetUnit"> | ||
| 391 | <arg type="s" name="name" direction="in"/> | ||
| 392 | <arg type="o" name="unit" direction="out"/> | ||
| 393 | </method> | ||
| 394 | <method name="GetUnitByPID"> | ||
| 395 | <arg type="u" name="pid" direction="in"/> | ||
| 396 | <arg type="o" name="unit" direction="out"/> | ||
| 397 | </method> | ||
| 398 | <method name="GetUnitByInvocationID"> | ||
| 399 | <arg type="ay" name="invocation_id" direction="in"/> | ||
| 400 | <arg type="o" name="unit" direction="out"/> | ||
| 401 | </method> | ||
| 402 | <method name="GetUnitByControlGroup"> | ||
| 403 | <arg type="s" name="cgroup" direction="in"/> | ||
| 404 | <arg type="o" name="unit" direction="out"/> | ||
| 405 | </method> | ||
| 406 | <method name="GetUnitByPIDFD"> | ||
| 407 | <arg type="h" name="pidfd" direction="in"/> | ||
| 408 | <arg type="o" name="unit" direction="out"/> | ||
| 409 | <arg type="s" name="unit_id" direction="out"/> | ||
| 410 | <arg type="ay" name="invocation_id" direction="out"/> | ||
| 411 | </method> | ||
| 412 | <method name="LoadUnit"> | ||
| 413 | <arg type="s" name="name" direction="in"/> | ||
| 414 | <arg type="o" name="unit" direction="out"/> | ||
| 415 | </method> | ||
| 416 | <method name="StartUnit"> | ||
| 417 | <arg type="s" name="name" direction="in"/> | ||
| 418 | <arg type="s" name="mode" direction="in"/> | ||
| 419 | <arg type="o" name="job" direction="out"/> | ||
| 420 | </method> | ||
| 421 | <method name="StartUnitWithFlags"> | ||
| 422 | <arg type="s" name="name" direction="in"/> | ||
| 423 | <arg type="s" name="mode" direction="in"/> | ||
| 424 | <arg type="t" name="flags" direction="in"/> | ||
| 425 | <arg type="o" name="job" direction="out"/> | ||
| 426 | </method> | ||
| 427 | <method name="StartUnitReplace"> | ||
| 428 | <arg type="s" name="old_unit" direction="in"/> | ||
| 429 | <arg type="s" name="new_unit" direction="in"/> | ||
| 430 | <arg type="s" name="mode" direction="in"/> | ||
| 431 | <arg type="o" name="job" direction="out"/> | ||
| 432 | </method> | ||
| 433 | <method name="StopUnit"> | ||
| 434 | <arg type="s" name="name" direction="in"/> | ||
| 435 | <arg type="s" name="mode" direction="in"/> | ||
| 436 | <arg type="o" name="job" direction="out"/> | ||
| 437 | </method> | ||
| 438 | <method name="ReloadUnit"> | ||
| 439 | <arg type="s" name="name" direction="in"/> | ||
| 440 | <arg type="s" name="mode" direction="in"/> | ||
| 441 | <arg type="o" name="job" direction="out"/> | ||
| 442 | </method> | ||
| 443 | <method name="RestartUnit"> | ||
| 444 | <arg type="s" name="name" direction="in"/> | ||
| 445 | <arg type="s" name="mode" direction="in"/> | ||
| 446 | <arg type="o" name="job" direction="out"/> | ||
| 447 | </method> | ||
| 448 | <method name="TryRestartUnit"> | ||
| 449 | <arg type="s" name="name" direction="in"/> | ||
| 450 | <arg type="s" name="mode" direction="in"/> | ||
| 451 | <arg type="o" name="job" direction="out"/> | ||
| 452 | </method> | ||
| 453 | <method name="ReloadOrRestartUnit"> | ||
| 454 | <arg type="s" name="name" direction="in"/> | ||
| 455 | <arg type="s" name="mode" direction="in"/> | ||
| 456 | <arg type="o" name="job" direction="out"/> | ||
| 457 | </method> | ||
| 458 | <method name="ReloadOrTryRestartUnit"> | ||
| 459 | <arg type="s" name="name" direction="in"/> | ||
| 460 | <arg type="s" name="mode" direction="in"/> | ||
| 461 | <arg type="o" name="job" direction="out"/> | ||
| 462 | </method> | ||
| 463 | <!-- <method name="EnqueueUnitJob"> --> | ||
| 464 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 465 | <!-- <arg type="s" name="job_type" direction="in"/> --> | ||
| 466 | <!-- <arg type="s" name="job_mode" direction="in"/> --> | ||
| 467 | <!-- <arg type="u" name="job_id" direction="out"/> --> | ||
| 468 | <!-- <arg type="o" name="job_path" direction="out"/> --> | ||
| 469 | <!-- <arg type="s" name="unit_id" direction="out"/> --> | ||
| 470 | <!-- <arg type="o" name="unit_path" direction="out"/> --> | ||
| 471 | <!-- <arg type="s" name="job_type" direction="out"/> --> | ||
| 472 | <!-- <arg type="a(uosos)" name="affected_jobs" direction="out"/> --> | ||
| 473 | <!-- </method> --> | ||
| 474 | <method name="KillUnit"> | ||
| 475 | <arg type="s" name="name" direction="in"/> | ||
| 476 | <arg type="s" name="whom" direction="in"/> | ||
| 477 | <arg type="i" name="signal" direction="in"/> | ||
| 478 | </method> | ||
| 479 | <method name="QueueSignalUnit"> | ||
| 480 | <arg type="s" name="name" direction="in"/> | ||
| 481 | <arg type="s" name="whom" direction="in"/> | ||
| 482 | <arg type="i" name="signal" direction="in"/> | ||
| 483 | <arg type="i" name="value" direction="in"/> | ||
| 484 | </method> | ||
| 485 | <method name="CleanUnit"> | ||
| 486 | <arg type="s" name="name" direction="in"/> | ||
| 487 | <arg type="as" name="mask" direction="in"/> | ||
| 488 | </method> | ||
| 489 | <method name="FreezeUnit"> | ||
| 490 | <arg type="s" name="name" direction="in"/> | ||
| 491 | </method> | ||
| 492 | <method name="ThawUnit"> | ||
| 493 | <arg type="s" name="name" direction="in"/> | ||
| 494 | </method> | ||
| 495 | <method name="ResetFailedUnit"> | ||
| 496 | <arg type="s" name="name" direction="in"/> | ||
| 497 | </method> | ||
| 498 | <!-- <method name="SetUnitProperties"> --> | ||
| 499 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 500 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 501 | <!-- <arg type="a(sv)" name="properties" direction="in"/> --> | ||
| 502 | <!-- </method> --> | ||
| 503 | <method name="BindMountUnit"> | ||
| 504 | <arg type="s" name="name" direction="in"/> | ||
| 505 | <arg type="s" name="source" direction="in"/> | ||
| 506 | <arg type="s" name="destination" direction="in"/> | ||
| 507 | <arg type="b" name="read_only" direction="in"/> | ||
| 508 | <arg type="b" name="mkdir" direction="in"/> | ||
| 509 | </method> | ||
| 510 | <!-- <method name="MountImageUnit"> --> | ||
| 511 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 512 | <!-- <arg type="s" name="source" direction="in"/> --> | ||
| 513 | <!-- <arg type="s" name="destination" direction="in"/> --> | ||
| 514 | <!-- <arg type="b" name="read_only" direction="in"/> --> | ||
| 515 | <!-- <arg type="b" name="mkdir" direction="in"/> --> | ||
| 516 | <!-- <arg type="a(ss)" name="options" direction="in"/> --> | ||
| 517 | <!-- </method> --> | ||
| 518 | <method name="RefUnit"> | ||
| 519 | <arg type="s" name="name" direction="in"/> | ||
| 520 | </method> | ||
| 521 | <method name="UnrefUnit"> | ||
| 522 | <arg type="s" name="name" direction="in"/> | ||
| 523 | </method> | ||
| 524 | <!-- <method name="StartTransientUnit"> --> | ||
| 525 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 526 | <!-- <arg type="s" name="mode" direction="in"/> --> | ||
| 527 | <!-- <arg type="a(sv)" name="properties" direction="in"/> --> | ||
| 528 | <!-- <arg type="a(sa(sv))" name="aux" direction="in"/> --> | ||
| 529 | <!-- <arg type="o" name="job" direction="out"/> --> | ||
| 530 | <!-- </method> --> | ||
| 531 | <!-- <method name="GetUnitProcesses"> --> | ||
| 532 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 533 | <!-- <arg type="a(sus)" name="processes" direction="out"/> --> | ||
| 534 | <!-- </method> --> | ||
| 535 | <!-- <method name="AttachProcessesToUnit"> --> | ||
| 536 | <!-- <arg type="s" name="unit_name" direction="in"/> --> | ||
| 537 | <!-- <arg type="s" name="subcgroup" direction="in"/> --> | ||
| 538 | <!-- <arg type="au" name="pids" direction="in"/> --> | ||
| 539 | <!-- </method> --> | ||
| 540 | <method name="AbandonScope"> | ||
| 541 | <arg type="s" name="name" direction="in"/> | ||
| 542 | </method> | ||
| 543 | <method name="GetJob"> | ||
| 544 | <arg type="u" name="id" direction="in"/> | ||
| 545 | <arg type="o" name="job" direction="out"/> | ||
| 546 | </method> | ||
| 547 | <!-- <method name="GetJobAfter"> --> | ||
| 548 | <!-- <arg type="u" name="id" direction="in"/> --> | ||
| 549 | <!-- <arg type="a(usssoo)" name="jobs" direction="out"/> --> | ||
| 550 | <!-- </method> --> | ||
| 551 | <!-- <method name="GetJobBefore"> --> | ||
| 552 | <!-- <arg type="u" name="id" direction="in"/> --> | ||
| 553 | <!-- <arg type="a(usssoo)" name="jobs" direction="out"/> --> | ||
| 554 | <!-- </method> --> | ||
| 555 | <method name="CancelJob"> | ||
| 556 | <arg type="u" name="id" direction="in"/> | ||
| 557 | </method> | ||
| 558 | <method name="ClearJobs"> | ||
| 559 | </method> | ||
| 560 | <method name="ResetFailed"> | ||
| 561 | </method> | ||
| 562 | <method name="SetShowStatus"> | ||
| 563 | <arg type="s" name="mode" direction="in"/> | ||
| 564 | </method> | ||
| 565 | <!-- <method name="ListUnits"> --> | ||
| 566 | <!-- <arg type="a(ssssssouso)" name="units" direction="out"/> --> | ||
| 567 | <!-- </method> --> | ||
| 568 | <!-- <method name="ListUnitsFiltered"> --> | ||
| 569 | <!-- <arg type="as" name="states" direction="in"/> --> | ||
| 570 | <!-- <arg type="a(ssssssouso)" name="units" direction="out"/> --> | ||
| 571 | <!-- </method> --> | ||
| 572 | <!-- <method name="ListUnitsByPatterns"> --> | ||
| 573 | <!-- <arg type="as" name="states" direction="in"/> --> | ||
| 574 | <!-- <arg type="as" name="patterns" direction="in"/> --> | ||
| 575 | <!-- <arg type="a(ssssssouso)" name="units" direction="out"/> --> | ||
| 576 | <!-- </method> --> | ||
| 577 | <!-- <method name="ListUnitsByNames"> --> | ||
| 578 | <!-- <arg type="as" name="names" direction="in"/> --> | ||
| 579 | <!-- <arg type="a(ssssssouso)" name="units" direction="out"/> --> | ||
| 580 | <!-- </method> --> | ||
| 581 | <!-- <method name="ListJobs"> --> | ||
| 582 | <!-- <arg type="a(usssoo)" name="jobs" direction="out"/> --> | ||
| 583 | <!-- </method> --> | ||
| 584 | <method name="Subscribe"> | ||
| 585 | </method> | ||
| 586 | <method name="Unsubscribe"> | ||
| 587 | </method> | ||
| 588 | <method name="Dump"> | ||
| 589 | <arg type="s" name="output" direction="out"/> | ||
| 590 | </method> | ||
| 591 | <method name="DumpUnitsMatchingPatterns"> | ||
| 592 | <arg type="as" name="patterns" direction="in"/> | ||
| 593 | <arg type="s" name="output" direction="out"/> | ||
| 594 | </method> | ||
| 595 | <method name="DumpByFileDescriptor"> | ||
| 596 | <arg type="h" name="fd" direction="out"/> | ||
| 597 | </method> | ||
| 598 | <method name="DumpUnitsMatchingPatternsByFileDescriptor"> | ||
| 599 | <arg type="as" name="patterns" direction="in"/> | ||
| 600 | <arg type="h" name="fd" direction="out"/> | ||
| 601 | </method> | ||
| 602 | <method name="Reload"> | ||
| 603 | </method> | ||
| 604 | <method name="Reexecute"> | ||
| 605 | <annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/> | ||
| 606 | </method> | ||
| 607 | <method name="Exit"> | ||
| 608 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 609 | </method> | ||
| 610 | <method name="Reboot"> | ||
| 611 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 612 | </method> | ||
| 613 | <method name="SoftReboot"> | ||
| 614 | <arg type="s" name="new_root" direction="in"/> | ||
| 615 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 616 | </method> | ||
| 617 | <method name="PowerOff"> | ||
| 618 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 619 | </method> | ||
| 620 | <method name="Halt"> | ||
| 621 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 622 | </method> | ||
| 623 | <method name="KExec"> | ||
| 624 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 625 | </method> | ||
| 626 | <method name="SwitchRoot"> | ||
| 627 | <arg type="s" name="new_root" direction="in"/> | ||
| 628 | <arg type="s" name="init" direction="in"/> | ||
| 629 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 630 | </method> | ||
| 631 | <method name="SetEnvironment"> | ||
| 632 | <arg type="as" name="assignments" direction="in"/> | ||
| 633 | </method> | ||
| 634 | <method name="UnsetEnvironment"> | ||
| 635 | <arg type="as" name="names" direction="in"/> | ||
| 636 | </method> | ||
| 637 | <method name="UnsetAndSetEnvironment"> | ||
| 638 | <arg type="as" name="names" direction="in"/> | ||
| 639 | <arg type="as" name="assignments" direction="in"/> | ||
| 640 | </method> | ||
| 641 | <method name="EnqueueMarkedJobs"> | ||
| 642 | <arg type="ao" name="jobs" direction="out"/> | ||
| 643 | </method> | ||
| 644 | <!-- <method name="ListUnitFiles"> --> | ||
| 645 | <!-- <arg type="a(ss)" name="unit_files" direction="out"/> --> | ||
| 646 | <!-- </method> --> | ||
| 647 | <!-- <method name="ListUnitFilesByPatterns"> --> | ||
| 648 | <!-- <arg type="as" name="states" direction="in"/> --> | ||
| 649 | <!-- <arg type="as" name="patterns" direction="in"/> --> | ||
| 650 | <!-- <arg type="a(ss)" name="unit_files" direction="out"/> --> | ||
| 651 | <!-- </method> --> | ||
| 652 | <method name="GetUnitFileState"> | ||
| 653 | <arg type="s" name="file" direction="in"/> | ||
| 654 | <arg type="s" name="state" direction="out"/> | ||
| 655 | </method> | ||
| 656 | <!-- <method name="EnableUnitFiles"> --> | ||
| 657 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 658 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 659 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 660 | <!-- <arg type="b" name="carries_install_info" direction="out"/> --> | ||
| 661 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 662 | <!-- </method> --> | ||
| 663 | <!-- <method name="DisableUnitFiles"> --> | ||
| 664 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 665 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 666 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 667 | <!-- </method> --> | ||
| 668 | <!-- <method name="EnableUnitFilesWithFlags"> --> | ||
| 669 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 670 | <!-- <arg type="t" name="flags" direction="in"/> --> | ||
| 671 | <!-- <arg type="b" name="carries_install_info" direction="out"/> --> | ||
| 672 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 673 | <!-- </method> --> | ||
| 674 | <!-- <method name="DisableUnitFilesWithFlags"> --> | ||
| 675 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 676 | <!-- <arg type="t" name="flags" direction="in"/> --> | ||
| 677 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 678 | <!-- </method> --> | ||
| 679 | <!-- <method name="DisableUnitFilesWithFlagsAndInstallInfo"> --> | ||
| 680 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 681 | <!-- <arg type="t" name="flags" direction="in"/> --> | ||
| 682 | <!-- <arg type="b" name="carries_install_info" direction="out"/> --> | ||
| 683 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 684 | <!-- </method> --> | ||
| 685 | <!-- <method name="ReenableUnitFiles"> --> | ||
| 686 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 687 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 688 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 689 | <!-- <arg type="b" name="carries_install_info" direction="out"/> --> | ||
| 690 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 691 | <!-- </method> --> | ||
| 692 | <!-- <method name="LinkUnitFiles"> --> | ||
| 693 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 694 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 695 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 696 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 697 | <!-- </method> --> | ||
| 698 | <!-- <method name="PresetUnitFiles"> --> | ||
| 699 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 700 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 701 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 702 | <!-- <arg type="b" name="carries_install_info" direction="out"/> --> | ||
| 703 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 704 | <!-- </method> --> | ||
| 705 | <!-- <method name="PresetUnitFilesWithMode"> --> | ||
| 706 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 707 | <!-- <arg type="s" name="mode" direction="in"/> --> | ||
| 708 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 709 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 710 | <!-- <arg type="b" name="carries_install_info" direction="out"/> --> | ||
| 711 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 712 | <!-- </method> --> | ||
| 713 | <!-- <method name="MaskUnitFiles"> --> | ||
| 714 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 715 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 716 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 717 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 718 | <!-- </method> --> | ||
| 719 | <!-- <method name="UnmaskUnitFiles"> --> | ||
| 720 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 721 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 722 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 723 | <!-- </method> --> | ||
| 724 | <!-- <method name="RevertUnitFiles"> --> | ||
| 725 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 726 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 727 | <!-- </method> --> | ||
| 728 | <!-- <method name="SetDefaultTarget"> --> | ||
| 729 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 730 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 731 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 732 | <!-- </method> --> | ||
| 733 | <method name="GetDefaultTarget"> | ||
| 734 | <arg type="s" name="name" direction="out"/> | ||
| 735 | </method> | ||
| 736 | <!-- <method name="PresetAllUnitFiles"> --> | ||
| 737 | <!-- <arg type="s" name="mode" direction="in"/> --> | ||
| 738 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 739 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 740 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 741 | <!-- </method> --> | ||
| 742 | <!-- <method name="AddDependencyUnitFiles"> --> | ||
| 743 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 744 | <!-- <arg type="s" name="target" direction="in"/> --> | ||
| 745 | <!-- <arg type="s" name="type" direction="in"/> --> | ||
| 746 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 747 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 748 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 749 | <!-- </method> --> | ||
| 750 | <method name="GetUnitFileLinks"> | ||
| 751 | <arg type="s" name="name" direction="in"/> | ||
| 752 | <arg type="b" name="runtime" direction="in"/> | ||
| 753 | <arg type="as" name="links" direction="out"/> | ||
| 754 | </method> | ||
| 755 | <method name="SetExitCode"> | ||
| 756 | <arg type="y" name="number" direction="in"/> | ||
| 757 | </method> | ||
| 758 | <method name="LookupDynamicUserByName"> | ||
| 759 | <arg type="s" name="name" direction="in"/> | ||
| 760 | <arg type="u" name="uid" direction="out"/> | ||
| 761 | </method> | ||
| 762 | <method name="LookupDynamicUserByUID"> | ||
| 763 | <arg type="u" name="uid" direction="in"/> | ||
| 764 | <arg type="s" name="name" direction="out"/> | ||
| 765 | </method> | ||
| 766 | <!-- <method name="GetDynamicUsers"> --> | ||
| 767 | <!-- <arg type="a(us)" name="users" direction="out"/> --> | ||
| 768 | <!-- </method> --> | ||
| 769 | <!-- <method name="DumpUnitFileDescriptorStore"> --> | ||
| 770 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 771 | <!-- <arg type="a(suuutuusu)" name="entries" direction="out"/> --> | ||
| 772 | <!-- </method> --> | ||
| 773 | <!-- <method name="StartAuxiliaryScope"> --> | ||
| 774 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 775 | <!-- <arg type="ah" name="pidfds" direction="in"/> --> | ||
| 776 | <!-- <arg type="t" name="flags" direction="in"/> --> | ||
| 777 | <!-- <arg type="a(sv)" name="properties" direction="in"/> --> | ||
| 778 | <!-- <arg type="o" name="job" direction="out"/> --> | ||
| 779 | <!-- <annotation name="org.freedesktop.DBus.Deprecated" value="true"/> --> | ||
| 780 | <!-- </method> --> | ||
| 781 | <signal name="UnitNew"> | ||
| 782 | <arg type="s" name="id"/> | ||
| 783 | <arg type="o" name="unit"/> | ||
| 784 | </signal> | ||
| 785 | <signal name="UnitRemoved"> | ||
| 786 | <arg type="s" name="id"/> | ||
| 787 | <arg type="o" name="unit"/> | ||
| 788 | </signal> | ||
| 789 | <signal name="JobNew"> | ||
| 790 | <arg type="u" name="id"/> | ||
| 791 | <arg type="o" name="job"/> | ||
| 792 | <arg type="s" name="unit"/> | ||
| 793 | </signal> | ||
| 794 | <signal name="JobRemoved"> | ||
| 795 | <arg type="u" name="id"/> | ||
| 796 | <arg type="o" name="job"/> | ||
| 797 | <arg type="s" name="unit"/> | ||
| 798 | <arg type="s" name="result"/> | ||
| 799 | </signal> | ||
| 800 | <signal name="StartupFinished"> | ||
| 801 | <arg type="t" name="firmware"/> | ||
| 802 | <arg type="t" name="loader"/> | ||
| 803 | <arg type="t" name="kernel"/> | ||
| 804 | <arg type="t" name="initrd"/> | ||
| 805 | <arg type="t" name="userspace"/> | ||
| 806 | <arg type="t" name="total"/> | ||
| 807 | </signal> | ||
| 808 | <signal name="UnitFilesChanged"> | ||
| 809 | </signal> | ||
| 810 | <signal name="Reloading"> | ||
| 811 | <arg type="b" name="active"/> | ||
| 812 | </signal> | ||
| 813 | </interface> | ||
| 814 | <node name="unit"/> | ||
| 815 | <node name="job"/> | ||
| 816 | </node> | ||
| 817 | |||
diff --git a/accounts/gkleen@sif/shell/quickshell/ActiveWindowDisplay.qml b/accounts/gkleen@sif/shell/quickshell/ActiveWindowDisplay.qml new file mode 100644 index 00000000..dcc23279 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/ActiveWindowDisplay.qml | |||
| @@ -0,0 +1,172 @@ | |||
| 1 | import QtQuick | ||
| 2 | import qs.Services | ||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Widgets | ||
| 5 | |||
| 6 | Item { | ||
| 7 | id: activeWindowDisplay | ||
| 8 | |||
| 9 | required property int maxWidth | ||
| 10 | required property var screen | ||
| 11 | |||
| 12 | property var activeWindow: { | ||
| 13 | let currWindowId = Array.from(NiriService.workspaces).find(ws => { | ||
| 14 | return ws.output === screen.name && ws.is_active; | ||
| 15 | })?.active_window_id; | ||
| 16 | |||
| 17 | return currWindowId ? Array.from(NiriService.windows).find(win => win.id == currWindowId) : null; | ||
| 18 | } | ||
| 19 | property var windowEntry: activeWindow ? DesktopEntries.heuristicLookup(activeWindow.app_id) : null | ||
| 20 | |||
| 21 | anchors.verticalCenter: parent.verticalCenter | ||
| 22 | width: activeWindowDisplayContent.width | ||
| 23 | height: parent.height | ||
| 24 | |||
| 25 | WrapperMouseArea { | ||
| 26 | id: widgetMouseArea | ||
| 27 | |||
| 28 | anchors.fill: parent | ||
| 29 | |||
| 30 | hoverEnabled: true | ||
| 31 | |||
| 32 | Item { | ||
| 33 | anchors.fill: parent | ||
| 34 | |||
| 35 | Row { | ||
| 36 | id: activeWindowDisplayContent | ||
| 37 | |||
| 38 | width: childrenRect.width | ||
| 39 | height: parent.height | ||
| 40 | anchors.verticalCenter: parent.verticalCenter | ||
| 41 | spacing: 8 | ||
| 42 | |||
| 43 | IconImage { | ||
| 44 | id: activeWindowIcon | ||
| 45 | |||
| 46 | implicitSize: 14 | ||
| 47 | |||
| 48 | anchors.verticalCenter: parent.verticalCenter | ||
| 49 | |||
| 50 | source: { | ||
| 51 | let icon = activeWindowDisplay.windowEntry?.icon | ||
| 52 | if (typeof icon === 'string' || icon instanceof String) { | ||
| 53 | if (icon.includes("?path=")) { | ||
| 54 | const split = icon.split("?path=") | ||
| 55 | if (split.length !== 2) | ||
| 56 | return icon | ||
| 57 | const name = split[0] | ||
| 58 | const path = split[1] | ||
| 59 | const fileName = name.substring( | ||
| 60 | name.lastIndexOf("/") + 1) | ||
| 61 | return `file://${path}/${fileName}` | ||
| 62 | } else | ||
| 63 | icon = Quickshell.iconPath(icon); | ||
| 64 | return icon | ||
| 65 | } | ||
| 66 | return "" | ||
| 67 | } | ||
| 68 | asynchronous: true | ||
| 69 | smooth: true | ||
| 70 | mipmap: true | ||
| 71 | } | ||
| 72 | |||
| 73 | Text { | ||
| 74 | id: windowTitle | ||
| 75 | |||
| 76 | width: Math.min(implicitWidth, activeWindowDisplay.maxWidth - activeWindowIcon.width - activeWindowDisplayContent.spacing) | ||
| 77 | |||
| 78 | property var appAliases: { "Firefox": "Mozilla Firefox", "mpv Media Player": "mpv", "Thunderbird": "Mozilla Thunderbird", "Thunderbird (LMU)": "Mozilla Thunderbird" } | ||
| 79 | |||
| 80 | elide: Text.ElideRight | ||
| 81 | maximumLineCount: 1 | ||
| 82 | color: "white" | ||
| 83 | anchors.verticalCenter: parent.verticalCenter | ||
| 84 | text: { | ||
| 85 | if (!activeWindowDisplay.activeWindow) | ||
| 86 | return ""; | ||
| 87 | |||
| 88 | var title = activeWindowDisplay.activeWindow.title; | ||
| 89 | var appName = activeWindowDisplay.windowEntry?.name; | ||
| 90 | if (appAliases[appName]) | ||
| 91 | appName = appAliases[appName]; | ||
| 92 | if (appName && title.endsWith(appName)) { | ||
| 93 | const oldTitle = title; | ||
| 94 | title = title.substring(0, title.length - appName.length); | ||
| 95 | title = title.replace(/\s*(—|-)\s*$/, ""); | ||
| 96 | if (!title) | ||
| 97 | title = oldTitle; | ||
| 98 | } | ||
| 99 | return title; | ||
| 100 | } | ||
| 101 | } | ||
| 102 | } | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | Loader { | ||
| 107 | id: tooltipLoader | ||
| 108 | |||
| 109 | active: false | ||
| 110 | |||
| 111 | Connections { | ||
| 112 | target: widgetMouseArea | ||
| 113 | function onContainsMouseChanged() { | ||
| 114 | if (widgetMouseArea.containsMouse) | ||
| 115 | tooltipLoader.active = true; | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | PopupWindow { | ||
| 120 | id: tooltip | ||
| 121 | |||
| 122 | property bool nextVisible: widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse | ||
| 123 | |||
| 124 | anchor { | ||
| 125 | item: widgetMouseArea | ||
| 126 | edges: Edges.Bottom | Edges.Left | ||
| 127 | } | ||
| 128 | visible: false | ||
| 129 | |||
| 130 | onNextVisibleChanged: hangTimer.restart() | ||
| 131 | |||
| 132 | Timer { | ||
| 133 | id: hangTimer | ||
| 134 | interval: tooltip.visible ? 100 : 500 | ||
| 135 | onTriggered: { | ||
| 136 | tooltip.visible = tooltip.nextVisible; | ||
| 137 | if (!tooltip.visible) | ||
| 138 | tooltipLoader.active = false; | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | implicitWidth: widgetTooltipText.contentWidth + 16 | ||
| 143 | implicitHeight: widgetTooltipText.contentHeight + 16 | ||
| 144 | color: "black" | ||
| 145 | |||
| 146 | WrapperMouseArea { | ||
| 147 | id: tooltipMouseArea | ||
| 148 | |||
| 149 | hoverEnabled: true | ||
| 150 | enabled: true | ||
| 151 | |||
| 152 | anchors.fill: parent | ||
| 153 | |||
| 154 | Item { | ||
| 155 | anchors.fill: parent | ||
| 156 | |||
| 157 | Text { | ||
| 158 | id: widgetTooltipText | ||
| 159 | |||
| 160 | anchors.centerIn: parent | ||
| 161 | |||
| 162 | font.pointSize: 10 | ||
| 163 | font.family: "Fira Mono" | ||
| 164 | color: "white" | ||
| 165 | |||
| 166 | text: JSON.stringify(Object.assign({}, activeWindowDisplay.activeWindow), null, 2) | ||
| 167 | } | ||
| 168 | } | ||
| 169 | } | ||
| 170 | } | ||
| 171 | } | ||
| 172 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Bar.qml b/accounts/gkleen@sif/shell/quickshell/Bar.qml new file mode 100644 index 00000000..9210066c --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Bar.qml | |||
| @@ -0,0 +1,117 @@ | |||
| 1 | import Quickshell | ||
| 2 | import Quickshell.Wayland | ||
| 3 | import QtQuick | ||
| 4 | |||
| 5 | PanelWindow { | ||
| 6 | id: bar | ||
| 7 | |||
| 8 | required property var modelData | ||
| 9 | screen: modelData | ||
| 10 | |||
| 11 | WlrLayershell.namespace: "bar" | ||
| 12 | |||
| 13 | anchors { | ||
| 14 | top: true | ||
| 15 | left: true | ||
| 16 | right: true | ||
| 17 | } | ||
| 18 | margins { | ||
| 19 | left: 26 + 8 | ||
| 20 | right: 26 + 8 | ||
| 21 | } | ||
| 22 | |||
| 23 | implicitHeight: 21 | ||
| 24 | color: "transparent" | ||
| 25 | |||
| 26 | Rectangle { | ||
| 27 | color: Qt.rgba(0, 0, 0, 0.75) | ||
| 28 | anchors.fill: parent | ||
| 29 | // bottomLeftRadius: 8 | ||
| 30 | // bottomRightRadius: 8 | ||
| 31 | } | ||
| 32 | |||
| 33 | Row { | ||
| 34 | id: left | ||
| 35 | |||
| 36 | height: parent.height | ||
| 37 | width: childrenRect.width | ||
| 38 | anchors.left: parent.left | ||
| 39 | anchors.leftMargin: 8 | ||
| 40 | anchors.verticalCenter: parent.verticalCenter | ||
| 41 | spacing: 8 | ||
| 42 | |||
| 43 | WorkspaceSwitcher { | ||
| 44 | screen: bar.screen | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | Row { | ||
| 49 | id: center | ||
| 50 | |||
| 51 | height: parent.height | ||
| 52 | width: childrenRect.width | ||
| 53 | anchors.centerIn: parent | ||
| 54 | spacing: 5 | ||
| 55 | |||
| 56 | ActiveWindowDisplay { | ||
| 57 | screen: bar.screen | ||
| 58 | maxWidth: bar.width - 2*Math.max(left.width, right.width) - 2*8 | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | Row { | ||
| 63 | id: right | ||
| 64 | |||
| 65 | height: parent.height | ||
| 66 | width: childrenRect.width | ||
| 67 | anchors.right: parent.right | ||
| 68 | anchors.rightMargin: 8 | ||
| 69 | anchors.verticalCenter: parent.verticalCenter | ||
| 70 | spacing: 0 | ||
| 71 | |||
| 72 | // WorktimeWidget { command: "time"; } | ||
| 73 | |||
| 74 | // WorktimeWidget { command: "today"; } | ||
| 75 | |||
| 76 | KeyboardLayout {} | ||
| 77 | |||
| 78 | Item { | ||
| 79 | visible: privacy.visible | ||
| 80 | height: parent.height | ||
| 81 | width: 8 - 4 | ||
| 82 | } | ||
| 83 | |||
| 84 | PrivacyWidget { | ||
| 85 | id: privacy | ||
| 86 | } | ||
| 87 | |||
| 88 | Item { | ||
| 89 | visible: privacy.visible | ||
| 90 | height: parent.height | ||
| 91 | width: 8 - 4 | ||
| 92 | } | ||
| 93 | |||
| 94 | SystemTray {} | ||
| 95 | |||
| 96 | PipewireWidget {} | ||
| 97 | |||
| 98 | BrightnessWidget {} | ||
| 99 | |||
| 100 | BatteryWidget {} | ||
| 101 | |||
| 102 | WaylandInhibitorWidget { | ||
| 103 | window: bar | ||
| 104 | } | ||
| 105 | |||
| 106 | NotificationInhibitorWidget {} | ||
| 107 | |||
| 108 | LidSwitchInhibitorWidget {} | ||
| 109 | |||
| 110 | Item { | ||
| 111 | height: parent.height | ||
| 112 | width: 8 - 4 | ||
| 113 | } | ||
| 114 | |||
| 115 | Clock {} | ||
| 116 | } | ||
| 117 | } \ No newline at end of file | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/BatteryWidget.qml b/accounts/gkleen@sif/shell/quickshell/BatteryWidget.qml new file mode 100644 index 00000000..da17df2a --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/BatteryWidget.qml | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | import QtQuick | ||
| 2 | import Quickshell | ||
| 3 | import Quickshell.Widgets | ||
| 4 | import Quickshell.Services.UPower | ||
| 5 | |||
| 6 | Item { | ||
| 7 | id: root | ||
| 8 | |||
| 9 | height: parent.height | ||
| 10 | width: batteryIcon.width + 8 | ||
| 11 | anchors.verticalCenter: parent.verticalCenter | ||
| 12 | |||
| 13 | property var batteryDevice: Array.from(UPower.devices.values).find(dev => dev.isLaptopBattery) | ||
| 14 | |||
| 15 | WrapperMouseArea { | ||
| 16 | id: widgetMouseArea | ||
| 17 | |||
| 18 | anchors.fill: parent | ||
| 19 | |||
| 20 | hoverEnabled: true | ||
| 21 | |||
| 22 | Item { | ||
| 23 | anchors.fill: parent | ||
| 24 | |||
| 25 | MaterialDesignIcon { | ||
| 26 | id: batteryIcon | ||
| 27 | |||
| 28 | implicitSize: 14 | ||
| 29 | anchors.centerIn: parent | ||
| 30 | |||
| 31 | icon: { | ||
| 32 | if (!root.batteryDevice?.ready) | ||
| 33 | return "battery-unknown"; | ||
| 34 | |||
| 35 | if (root.batteryDevice.state == UPowerDeviceState.FullyCharged) | ||
| 36 | return "power-plug-battery"; | ||
| 37 | |||
| 38 | const perdec = 10 * Math.max(1, Math.ceil(root.batteryDevice.percentage * 10)); | ||
| 39 | if (root.batteryDevice.state == UPowerDeviceState.Charging) | ||
| 40 | return `battery-charging-${perdec}`; | ||
| 41 | if (perdec == 100) | ||
| 42 | return "battery"; | ||
| 43 | return `battery-${perdec}`; | ||
| 44 | } | ||
| 45 | color: { | ||
| 46 | if (!root.batteryDevice?.ready) | ||
| 47 | return "#555"; | ||
| 48 | |||
| 49 | if (root.batteryDevice.state != UPowerDeviceState.FullyCharged && root.batteryDevice.state != UPowerDeviceState.Charging && root.batteryDevice.timeToEmpty < 20 * 60) | ||
| 50 | return "#f2201f"; | ||
| 51 | if (root.batteryDevice.state != UPowerDeviceState.FullyCharged && root.batteryDevice.state != UPowerDeviceState.Charging && root.batteryDevice.timeToEmpty < 40 * 60) | ||
| 52 | return "#f28a21"; | ||
| 53 | if (root.batteryDevice.state != UPowerDeviceState.FullyCharged) | ||
| 54 | return "#fff"; | ||
| 55 | return "#555"; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | PopupWindow { | ||
| 62 | id: tooltip | ||
| 63 | |||
| 64 | property bool nextVisible: widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse | ||
| 65 | |||
| 66 | anchor { | ||
| 67 | item: widgetMouseArea | ||
| 68 | edges: Edges.Bottom | Edges.Left | ||
| 69 | } | ||
| 70 | visible: false | ||
| 71 | |||
| 72 | onNextVisibleChanged: hangTimer.restart() | ||
| 73 | |||
| 74 | Timer { | ||
| 75 | id: hangTimer | ||
| 76 | interval: 100 | ||
| 77 | onTriggered: tooltip.visible = tooltip.nextVisible | ||
| 78 | } | ||
| 79 | |||
| 80 | implicitWidth: widgetTooltipText.contentWidth + 16 | ||
| 81 | implicitHeight: widgetTooltipText.contentHeight + 16 | ||
| 82 | color: "black" | ||
| 83 | |||
| 84 | WrapperMouseArea { | ||
| 85 | id: tooltipMouseArea | ||
| 86 | |||
| 87 | hoverEnabled: true | ||
| 88 | enabled: true | ||
| 89 | |||
| 90 | anchors.fill: parent | ||
| 91 | |||
| 92 | Item { | ||
| 93 | anchors.fill: parent | ||
| 94 | |||
| 95 | Text { | ||
| 96 | id: widgetTooltipText | ||
| 97 | |||
| 98 | anchors.centerIn: parent | ||
| 99 | |||
| 100 | font.pointSize: 10 | ||
| 101 | font.family: "Fira Sans" | ||
| 102 | color: "white" | ||
| 103 | |||
| 104 | text: { | ||
| 105 | const stateStr = UPowerDeviceState.toString(root.batteryDevice.state); | ||
| 106 | var outStr = stateStr; | ||
| 107 | if (root.batteryDevice.state != UPowerDeviceState.FullyCharged) | ||
| 108 | outStr += ` ${Math.round(root.batteryDevice.percentage * 100)}%`; | ||
| 109 | |||
| 110 | function formatTime(t) { | ||
| 111 | var res = ""; | ||
| 112 | for (const unit of [{ "s": "h", "v": 3600 }, { "s": "m", "v": 60 }, { "s": "s", "v": 1 }]) { | ||
| 113 | if (t < unit.v) | ||
| 114 | continue; | ||
| 115 | res += Math.floor(t / unit.v) + unit.s; | ||
| 116 | t %= unit.v; | ||
| 117 | } | ||
| 118 | return res; | ||
| 119 | } | ||
| 120 | if (root.batteryDevice.timeToEmpty != 0) { | ||
| 121 | const tStr = formatTime(Math.floor(root.batteryDevice.timeToEmpty / 60) * 60); | ||
| 122 | if (tStr) | ||
| 123 | outStr += " " + tStr; | ||
| 124 | } else if (root.batteryDevice.timeToFull != 0) { | ||
| 125 | const tStr = formatTime(Math.ceil(root.batteryDevice.timeToFull / 60) * 60); | ||
| 126 | if (tStr) | ||
| 127 | outStr += " " + tStr; | ||
| 128 | } | ||
| 129 | |||
| 130 | return outStr; | ||
| 131 | } | ||
| 132 | } | ||
| 133 | } | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/BrightnessOSD.qml b/accounts/gkleen@sif/shell/quickshell/BrightnessOSD.qml new file mode 100644 index 00000000..a432179e --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/BrightnessOSD.qml | |||
| @@ -0,0 +1,117 @@ | |||
| 1 | import QtQuick | ||
| 2 | import QtQuick.Layouts | ||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Widgets | ||
| 5 | import qs.Services | ||
| 6 | |||
| 7 | Scope { | ||
| 8 | id: root | ||
| 9 | |||
| 10 | property bool show: false | ||
| 11 | property bool inhibited: true | ||
| 12 | |||
| 13 | Connections { | ||
| 14 | target: Brightness | ||
| 15 | |||
| 16 | function onCurrBrightnessChanged() { | ||
| 17 | root.show = true; | ||
| 18 | hideTimer.restart(); | ||
| 19 | } | ||
| 20 | } | ||
| 21 | |||
| 22 | onShowChanged: { | ||
| 23 | if (show) | ||
| 24 | hideTimer.restart(); | ||
| 25 | } | ||
| 26 | |||
| 27 | Timer { | ||
| 28 | id: hideTimer | ||
| 29 | interval: 1000 | ||
| 30 | onTriggered: root.show = false | ||
| 31 | } | ||
| 32 | |||
| 33 | Timer { | ||
| 34 | id: startInhibit | ||
| 35 | interval: 100 | ||
| 36 | running: true | ||
| 37 | onTriggered: { | ||
| 38 | root.show = false; | ||
| 39 | root.inhibited = false; | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | LazyLoader { | ||
| 44 | active: root.show && !root.inhibited | ||
| 45 | |||
| 46 | Variants { | ||
| 47 | model: Quickshell.screens | ||
| 48 | |||
| 49 | delegate: Scope { | ||
| 50 | id: screenScope | ||
| 51 | |||
| 52 | required property var modelData | ||
| 53 | |||
| 54 | PanelWindow { | ||
| 55 | id: window | ||
| 56 | |||
| 57 | screen: screenScope.modelData | ||
| 58 | |||
| 59 | anchors.top: true | ||
| 60 | margins.top: screen.height / 2 - 50 + 3.5 | ||
| 61 | exclusiveZone: 0 | ||
| 62 | exclusionMode: ExclusionMode.Ignore | ||
| 63 | |||
| 64 | implicitWidth: 400 | ||
| 65 | implicitHeight: 50 | ||
| 66 | |||
| 67 | mask: Region {} | ||
| 68 | |||
| 69 | color: "transparent" | ||
| 70 | |||
| 71 | Rectangle { | ||
| 72 | anchors.fill: parent | ||
| 73 | color: Qt.rgba(0, 0, 0, 0.75) | ||
| 74 | } | ||
| 75 | |||
| 76 | RowLayout { | ||
| 77 | id: layout | ||
| 78 | |||
| 79 | anchors.centerIn: parent | ||
| 80 | |||
| 81 | height: 50 - 8*2 | ||
| 82 | width: 400 - 8*2 | ||
| 83 | |||
| 84 | MaterialDesignIcon { | ||
| 85 | id: volumeIcon | ||
| 86 | |||
| 87 | implicitWidth: parent.height | ||
| 88 | implicitHeight: parent.height | ||
| 89 | |||
| 90 | icon: `brightness-${Math.min(7, Math.floor(Brightness.currBrightness * 7) + 1)}` | ||
| 91 | } | ||
| 92 | |||
| 93 | Rectangle { | ||
| 94 | Layout.fillWidth: true | ||
| 95 | |||
| 96 | implicitHeight: 10 | ||
| 97 | |||
| 98 | color: "#50ffffff" | ||
| 99 | |||
| 100 | Rectangle { | ||
| 101 | anchors { | ||
| 102 | left: parent.left | ||
| 103 | top: parent.top | ||
| 104 | bottom: parent.bottom | ||
| 105 | } | ||
| 106 | |||
| 107 | color: "white" | ||
| 108 | |||
| 109 | implicitWidth: parent.width * Brightness.currBrightness | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | ||
| 117 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/BrightnessWidget.qml b/accounts/gkleen@sif/shell/quickshell/BrightnessWidget.qml new file mode 100644 index 00000000..3bb5a80e --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/BrightnessWidget.qml | |||
| @@ -0,0 +1,84 @@ | |||
| 1 | import QtQuick | ||
| 2 | import Quickshell | ||
| 3 | import Quickshell.Widgets | ||
| 4 | import qs.Services | ||
| 5 | |||
| 6 | Item { | ||
| 7 | height: parent.height | ||
| 8 | width: brightnessIcon.width + 8 | ||
| 9 | anchors.verticalCenter: parent.verticalCenter | ||
| 10 | |||
| 11 | WrapperMouseArea { | ||
| 12 | id: widgetMouseArea | ||
| 13 | |||
| 14 | anchors.fill: parent | ||
| 15 | |||
| 16 | hoverEnabled: true | ||
| 17 | |||
| 18 | property real sensitivity: (1 / 50) / 120 | ||
| 19 | onWheel: event => Brightness.currBrightness += event.angleDelta.y * sensitivity | ||
| 20 | |||
| 21 | Item { | ||
| 22 | anchors.fill: parent | ||
| 23 | |||
| 24 | MaterialDesignIcon { | ||
| 25 | id: brightnessIcon | ||
| 26 | |||
| 27 | implicitSize: 14 | ||
| 28 | anchors.centerIn: parent | ||
| 29 | |||
| 30 | icon: `brightness-${Math.min(7, Math.floor(Brightness.currBrightness * 7) + 1)}` | ||
| 31 | color: "#555" | ||
| 32 | } | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | PopupWindow { | ||
| 37 | id: tooltip | ||
| 38 | |||
| 39 | property bool nextVisible: widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse | ||
| 40 | |||
| 41 | anchor { | ||
| 42 | item: widgetMouseArea | ||
| 43 | edges: Edges.Bottom | Edges.Left | ||
| 44 | } | ||
| 45 | visible: false | ||
| 46 | |||
| 47 | onNextVisibleChanged: hangTimer.restart() | ||
| 48 | |||
| 49 | Timer { | ||
| 50 | id: hangTimer | ||
| 51 | interval: 100 | ||
| 52 | onTriggered: tooltip.visible = tooltip.nextVisible | ||
| 53 | } | ||
| 54 | |||
| 55 | implicitWidth: widgetTooltipText.contentWidth + 16 | ||
| 56 | implicitHeight: widgetTooltipText.contentHeight + 16 | ||
| 57 | color: "black" | ||
| 58 | |||
| 59 | WrapperMouseArea { | ||
| 60 | id: tooltipMouseArea | ||
| 61 | |||
| 62 | hoverEnabled: true | ||
| 63 | enabled: true | ||
| 64 | |||
| 65 | anchors.fill: parent | ||
| 66 | |||
| 67 | Item { | ||
| 68 | anchors.fill: parent | ||
| 69 | |||
| 70 | Text { | ||
| 71 | id: widgetTooltipText | ||
| 72 | |||
| 73 | anchors.centerIn: parent | ||
| 74 | |||
| 75 | font.pointSize: 10 | ||
| 76 | font.family: "Fira Sans" | ||
| 77 | color: "white" | ||
| 78 | |||
| 79 | text: `${Math.round(Brightness.currBrightness * 100)}%` | ||
| 80 | } | ||
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Clock.qml b/accounts/gkleen@sif/shell/quickshell/Clock.qml new file mode 100644 index 00000000..b7004528 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Clock.qml | |||
| @@ -0,0 +1,295 @@ | |||
| 1 | import QtQml | ||
| 2 | import QtQuick | ||
| 3 | import Quickshell | ||
| 4 | import Custom as Custom | ||
| 5 | import QtQuick.Controls | ||
| 6 | import QtQuick.Layouts | ||
| 7 | import Quickshell.Widgets | ||
| 8 | |||
| 9 | Item { | ||
| 10 | id: clockItem | ||
| 11 | |||
| 12 | property bool calendarPopup: true | ||
| 13 | |||
| 14 | width: clock.contentWidth | ||
| 15 | height: parent.height | ||
| 16 | anchors.verticalCenter: parent.verticalCenter | ||
| 17 | |||
| 18 | WrapperMouseArea { | ||
| 19 | id: clockMouseArea | ||
| 20 | |||
| 21 | anchors.fill: parent | ||
| 22 | hoverEnabled: true | ||
| 23 | enabled: clockItem.calendarPopup | ||
| 24 | |||
| 25 | Item { | ||
| 26 | anchors.fill: parent | ||
| 27 | |||
| 28 | Text { | ||
| 29 | id: clock | ||
| 30 | color: "white" | ||
| 31 | |||
| 32 | anchors.verticalCenter: parent.verticalCenter | ||
| 33 | |||
| 34 | Custom.Chrono { | ||
| 35 | id: chrono | ||
| 36 | |||
| 37 | onDateChanged: clock.text = format("W{0:%V-%u} {0:%F} {0:%H:%M:%S%Ez}") | ||
| 38 | } | ||
| 39 | |||
| 40 | font.pointSize: 10 | ||
| 41 | font.family: "Fira Sans" | ||
| 42 | font.features: { "tnum": 1 } | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | Loader { | ||
| 48 | id: tooltipLoader | ||
| 49 | |||
| 50 | active: false | ||
| 51 | |||
| 52 | Connections { | ||
| 53 | target: clockMouseArea | ||
| 54 | function onContainsMouseChanged() { | ||
| 55 | if (clockMouseArea.containsMouse) | ||
| 56 | tooltipLoader.active = true; | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | sourceComponent: PopupWindow { | ||
| 61 | id: tooltip | ||
| 62 | |||
| 63 | property bool nextVisible: clockMouseArea.containsMouse || tooltipMouseArea.containsMouse | ||
| 64 | |||
| 65 | anchor { | ||
| 66 | item: clockMouseArea | ||
| 67 | edges: Edges.Bottom | Edges.Left | ||
| 68 | } | ||
| 69 | visible: false | ||
| 70 | |||
| 71 | onNextVisibleChanged: hangTimer.restart() | ||
| 72 | |||
| 73 | Timer { | ||
| 74 | id: hangTimer | ||
| 75 | interval: 100 | ||
| 76 | onTriggered: { | ||
| 77 | tooltip.visible = tooltip.nextVisible; | ||
| 78 | if (!tooltip.visible) | ||
| 79 | tooltipLoader.active = false; | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | implicitWidth: tooltipLayout.childrenRect.width + 16 | ||
| 84 | implicitHeight: tooltipLayout.childrenRect.height + 16 | ||
| 85 | color: "black" | ||
| 86 | |||
| 87 | onVisibleChanged: { | ||
| 88 | yearCalendar.year = chrono.date.getFullYear(); | ||
| 89 | yearCalendar.angleRem = 0; | ||
| 90 | } | ||
| 91 | |||
| 92 | WrapperMouseArea { | ||
| 93 | id: tooltipMouseArea | ||
| 94 | |||
| 95 | hoverEnabled: true | ||
| 96 | enabled: true | ||
| 97 | |||
| 98 | onWheel: event => yearCalendar.scrollYear(event) | ||
| 99 | |||
| 100 | anchors.fill: parent | ||
| 101 | |||
| 102 | Item { | ||
| 103 | id: clockTooltipContent | ||
| 104 | |||
| 105 | anchors.fill: parent | ||
| 106 | |||
| 107 | ColumnLayout { | ||
| 108 | id: tooltipLayout | ||
| 109 | |||
| 110 | anchors { | ||
| 111 | left: parent.left | ||
| 112 | top: parent.top | ||
| 113 | leftMargin: 8 | ||
| 114 | topMargin: 8 | ||
| 115 | } | ||
| 116 | |||
| 117 | Text { | ||
| 118 | id: yearLabel | ||
| 119 | |||
| 120 | horizontalAlignment: Text.AlignHCenter | ||
| 121 | |||
| 122 | font.pointSize: 14 | ||
| 123 | font.family: "Fira Sans" | ||
| 124 | font.features: { "tnum": 1 } | ||
| 125 | color: "white" | ||
| 126 | |||
| 127 | text: yearCalendar.year | ||
| 128 | |||
| 129 | Layout.fillWidth: true | ||
| 130 | Layout.bottomMargin: 8 | ||
| 131 | } | ||
| 132 | |||
| 133 | GridLayout { | ||
| 134 | property int year: chrono.date.getFullYear() | ||
| 135 | |||
| 136 | id: yearCalendar | ||
| 137 | |||
| 138 | columns: 3 | ||
| 139 | columnSpacing: 16 | ||
| 140 | rowSpacing: 16 | ||
| 141 | |||
| 142 | Layout.alignment: Qt.AlignHCenter | ||
| 143 | Layout.fillWidth: false | ||
| 144 | |||
| 145 | property real angleRem: 0 | ||
| 146 | property real sensitivity: 1 / 120 | ||
| 147 | |||
| 148 | function scrollYear(event) { | ||
| 149 | angleRem += event.angleDelta.y; | ||
| 150 | const d = Math.round(angleRem * sensitivity); | ||
| 151 | yearCalendar.year += d; | ||
| 152 | angleRem -= d / sensitivity; | ||
| 153 | } | ||
| 154 | |||
| 155 | Connections { | ||
| 156 | target: clockMouseArea | ||
| 157 | function onWheel(event) { yearCalendar.scrollYear(event); } | ||
| 158 | } | ||
| 159 | |||
| 160 | Repeater { | ||
| 161 | model: 12 | ||
| 162 | |||
| 163 | GridLayout { | ||
| 164 | columns: 2 | ||
| 165 | |||
| 166 | required property int index | ||
| 167 | property int month: index | ||
| 168 | |||
| 169 | id: monthCalendar | ||
| 170 | |||
| 171 | Layout.alignment: Qt.AlignTop | Qt.AlignRight | ||
| 172 | Layout.fillWidth: false | ||
| 173 | |||
| 174 | Text { | ||
| 175 | Layout.column: 1 | ||
| 176 | Layout.fillWidth: true | ||
| 177 | |||
| 178 | horizontalAlignment: Text.AlignHCenter | ||
| 179 | |||
| 180 | font.pointSize: 10 | ||
| 181 | font.family: "Fira Sans" | ||
| 182 | |||
| 183 | text: new Date(yearCalendar.year, monthCalendar.month, 1).toLocaleString(Qt.locale("en_DK"), "MMMM") | ||
| 184 | |||
| 185 | color: "#ffead3" | ||
| 186 | } | ||
| 187 | |||
| 188 | DayOfWeekRow { | ||
| 189 | locale: grid.locale | ||
| 190 | |||
| 191 | Layout.row: 1 | ||
| 192 | Layout.column: 1 | ||
| 193 | Layout.fillWidth: true | ||
| 194 | |||
| 195 | delegate: WrapperItem { | ||
| 196 | required property string shortName | ||
| 197 | |||
| 198 | width: dowLabel.contentWidth + 6 | ||
| 199 | |||
| 200 | Text { | ||
| 201 | id: dowLabel | ||
| 202 | |||
| 203 | anchors.fill: parent | ||
| 204 | |||
| 205 | font.pointSize: 10 | ||
| 206 | font.family: "Fira Sans" | ||
| 207 | |||
| 208 | text: parent.shortName | ||
| 209 | color: "#ffcc66" | ||
| 210 | |||
| 211 | horizontalAlignment: Text.AlignHCenter | ||
| 212 | verticalAlignment: Text.AlignVCenter | ||
| 213 | } | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | WeekNumberColumn { | ||
| 218 | month: grid.month | ||
| 219 | year: grid.year | ||
| 220 | locale: grid.locale | ||
| 221 | |||
| 222 | Layout.fillHeight: true | ||
| 223 | |||
| 224 | delegate: Text { | ||
| 225 | required property int weekNumber | ||
| 226 | |||
| 227 | opacity: { | ||
| 228 | const simple = new Date(weekNumber == 1 && monthCalendar.month == 12 ? yearCalendar.year + 1 : yearCalendar.year, 0, 1 + (weekNumber - 1) * 7); | ||
| 229 | const dayOfWeek = simple.getDay(); | ||
| 230 | const isoWeekStart = simple; | ||
| 231 | |||
| 232 | isoWeekStart.setDate(simple.getDate() - dayOfWeek + 1); | ||
| 233 | if (dayOfWeek > 4) { | ||
| 234 | isoWeekStart.setDate(isoWeekStart.getDate() + 7); | ||
| 235 | } | ||
| 236 | |||
| 237 | for (let i = 0; i < 7; i++) { | ||
| 238 | const dayInWeek = new Date(isoWeekStart); | ||
| 239 | dayInWeek.setDate(dayInWeek.getDate() + i); | ||
| 240 | if (dayInWeek.getMonth() == monthCalendar.month) | ||
| 241 | return 1; | ||
| 242 | } | ||
| 243 | |||
| 244 | return 0; | ||
| 245 | } | ||
| 246 | |||
| 247 | font.pointSize: 10 | ||
| 248 | font.family: "Fira Sans" | ||
| 249 | font.features: { "tnum": 1 } | ||
| 250 | |||
| 251 | text: weekNumber | ||
| 252 | color: "#99ffdd" | ||
| 253 | |||
| 254 | horizontalAlignment: Text.AlignRight | ||
| 255 | verticalAlignment: Text.AlignVCenter | ||
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 | MonthGrid { | ||
| 260 | id: grid | ||
| 261 | |||
| 262 | year: yearCalendar.year | ||
| 263 | month: monthCalendar.month | ||
| 264 | locale: Qt.locale("en_DK") | ||
| 265 | |||
| 266 | Layout.fillWidth: true | ||
| 267 | Layout.fillHeight: true | ||
| 268 | |||
| 269 | delegate: Text { | ||
| 270 | required property var model | ||
| 271 | |||
| 272 | opacity: model.month === monthCalendar.month ? 1 : 0 | ||
| 273 | |||
| 274 | font.pointSize: 10 | ||
| 275 | font.family: "Fira Sans" | ||
| 276 | font.features: { "tnum": 1 } | ||
| 277 | |||
| 278 | property bool today: chrono.date.getFullYear() == model.year && chrono.date.getMonth() == model.month && chrono.date.getDate() == model.day | ||
| 279 | |||
| 280 | text: model.day | ||
| 281 | color: today ? "#ff6699" : "white" | ||
| 282 | |||
| 283 | horizontalAlignment: Text.AlignRight | ||
| 284 | verticalAlignment: Text.AlignVCenter | ||
| 285 | } | ||
| 286 | } | ||
| 287 | } | ||
| 288 | } | ||
| 289 | } | ||
| 290 | } | ||
| 291 | } | ||
| 292 | } | ||
| 293 | } | ||
| 294 | } | ||
| 295 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/KeyboardLayout.qml b/accounts/gkleen@sif/shell/quickshell/KeyboardLayout.qml new file mode 100644 index 00000000..46302e54 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/KeyboardLayout.qml | |||
| @@ -0,0 +1,112 @@ | |||
| 1 | import Quickshell | ||
| 2 | import QtQuick | ||
| 3 | import qs.Services | ||
| 4 | import Quickshell.Widgets | ||
| 5 | |||
| 6 | Item { | ||
| 7 | width: kbdLabel.contentWidth + 8 | ||
| 8 | height: parent.height | ||
| 9 | anchors.verticalCenter: parent.verticalCenter | ||
| 10 | |||
| 11 | WrapperMouseArea { | ||
| 12 | id: kbdMouseArea | ||
| 13 | |||
| 14 | anchors.fill: parent | ||
| 15 | |||
| 16 | hoverEnabled: true | ||
| 17 | cursorShape: Qt.PointingHandCursor | ||
| 18 | enabled: true | ||
| 19 | onClicked: { | ||
| 20 | NiriService.sendCommand({ "Action": { "SwitchLayout": { "layout": "Next" } } }, _ => {}) | ||
| 21 | } | ||
| 22 | onWheel: event => { | ||
| 23 | NiriService.sendCommand({ "Action": { "SwitchLayout": { "layout": event.angleDelta > 0 ? "Next" : "Prev" } } }, _ => {}) | ||
| 24 | } | ||
| 25 | |||
| 26 | Rectangle { | ||
| 27 | id: kbdWidget | ||
| 28 | |||
| 29 | property var keyboardAbbrev: { "English (programmer Dvorak)": "dvp", "English (US)": "us" } | ||
| 30 | |||
| 31 | anchors.fill: parent | ||
| 32 | color: { | ||
| 33 | if (kbdMouseArea.containsMouse) { | ||
| 34 | return "#33808080"; | ||
| 35 | } | ||
| 36 | return "transparent"; | ||
| 37 | } | ||
| 38 | |||
| 39 | Text { | ||
| 40 | id: kbdLabel | ||
| 41 | |||
| 42 | font.pointSize: 10 | ||
| 43 | font.family: "Fira Sans" | ||
| 44 | color: { | ||
| 45 | if (NiriService.keyboardLayouts?.current_idx === 0) | ||
| 46 | return "#555"; | ||
| 47 | return "white"; | ||
| 48 | } | ||
| 49 | anchors.centerIn: parent | ||
| 50 | |||
| 51 | text: { | ||
| 52 | const currentLayout = NiriService.keyboardLayouts?.names?.[NiriService.keyboardLayouts.current_idx]; | ||
| 53 | if (!currentLayout) | ||
| 54 | return ""; | ||
| 55 | return kbdWidget.keyboardAbbrev[currentLayout] ? kbdWidget.keyboardAbbrev[currentLayout] : currentLayout; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | PopupWindow { | ||
| 62 | id: tooltip | ||
| 63 | |||
| 64 | property bool nextVisible: kbdMouseArea.containsMouse || tooltipMouseArea.containsMouse | ||
| 65 | |||
| 66 | anchor { | ||
| 67 | item: kbdMouseArea | ||
| 68 | edges: Edges.Bottom | Edges.Left | ||
| 69 | } | ||
| 70 | visible: false | ||
| 71 | |||
| 72 | onNextVisibleChanged: hangTimer.restart() | ||
| 73 | |||
| 74 | Timer { | ||
| 75 | id: hangTimer | ||
| 76 | interval: 100 | ||
| 77 | onTriggered: tooltip.visible = tooltip.nextVisible | ||
| 78 | } | ||
| 79 | |||
| 80 | implicitWidth: kbdTooltipText.contentWidth + 16 | ||
| 81 | implicitHeight: kbdTooltipText.contentHeight + 16 | ||
| 82 | color: "black" | ||
| 83 | |||
| 84 | WrapperMouseArea { | ||
| 85 | id: tooltipMouseArea | ||
| 86 | |||
| 87 | hoverEnabled: true | ||
| 88 | enabled: true | ||
| 89 | |||
| 90 | anchors.fill: parent | ||
| 91 | |||
| 92 | Item { | ||
| 93 | anchors.fill: parent | ||
| 94 | |||
| 95 | Text { | ||
| 96 | id: kbdTooltipText | ||
| 97 | |||
| 98 | anchors.centerIn: parent | ||
| 99 | |||
| 100 | font.pointSize: 10 | ||
| 101 | font.family: "Fira Sans" | ||
| 102 | color: "white" | ||
| 103 | |||
| 104 | text: { | ||
| 105 | const currentLayout = NiriService.keyboardLayouts?.names?.[NiriService.keyboardLayouts.current_idx]; | ||
| 106 | return currentLayout || ""; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | } | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/LidSwitchInhibitorWidget.qml b/accounts/gkleen@sif/shell/quickshell/LidSwitchInhibitorWidget.qml new file mode 100644 index 00000000..8410dcda --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/LidSwitchInhibitorWidget.qml | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | import Quickshell | ||
| 2 | import QtQuick | ||
| 3 | import Quickshell.Widgets | ||
| 4 | import qs.Services | ||
| 5 | |||
| 6 | Item { | ||
| 7 | id: root | ||
| 8 | |||
| 9 | width: icon.width + 8 | ||
| 10 | height: parent.height | ||
| 11 | anchors.verticalCenter: parent.verticalCenter | ||
| 12 | |||
| 13 | WrapperMouseArea { | ||
| 14 | id: widgetMouseArea | ||
| 15 | |||
| 16 | anchors.fill: parent | ||
| 17 | |||
| 18 | hoverEnabled: true | ||
| 19 | cursorShape: Qt.PointingHandCursor | ||
| 20 | |||
| 21 | onClicked: InhibitorState.lidSwitchInhibited = !InhibitorState.lidSwitchInhibited | ||
| 22 | |||
| 23 | Rectangle { | ||
| 24 | anchors.fill: parent | ||
| 25 | color: { | ||
| 26 | if (widgetMouseArea.containsMouse) { | ||
| 27 | return "#33808080"; | ||
| 28 | } | ||
| 29 | return "transparent"; | ||
| 30 | } | ||
| 31 | |||
| 32 | Item { | ||
| 33 | anchors.fill: parent | ||
| 34 | |||
| 35 | MaterialDesignIcon { | ||
| 36 | id: icon | ||
| 37 | |||
| 38 | implicitSize: 14 | ||
| 39 | anchors.centerIn: parent | ||
| 40 | |||
| 41 | icon: InhibitorState.lidSwitchInhibited ? "laptop-off" : "laptop" | ||
| 42 | color: InhibitorState.lidSwitchInhibited ? "#f28a21" : "#555" | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
| 46 | } | ||
| 47 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/LockSurface.qml b/accounts/gkleen@sif/shell/quickshell/LockSurface.qml new file mode 100644 index 00000000..f4f8f0cd --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/LockSurface.qml | |||
| @@ -0,0 +1,227 @@ | |||
| 1 | import Quickshell.Widgets | ||
| 2 | import QtQuick.Effects | ||
| 3 | import QtQuick.Layouts | ||
| 4 | import QtQuick | ||
| 5 | import QtQuick.Controls | ||
| 6 | import QtQuick.Controls.Fusion | ||
| 7 | import qs.Services | ||
| 8 | import QtQml | ||
| 9 | |||
| 10 | Item { | ||
| 11 | id: lockSurface | ||
| 12 | |||
| 13 | property var screen | ||
| 14 | property list<var> messages: [] | ||
| 15 | property bool responseRequired: false | ||
| 16 | property bool responseVisible: false | ||
| 17 | property string currentText: "" | ||
| 18 | property bool authRunning: false | ||
| 19 | |||
| 20 | signal response(string responseText) | ||
| 21 | |||
| 22 | anchors.fill: parent | ||
| 23 | |||
| 24 | Item { | ||
| 25 | id: background | ||
| 26 | |||
| 27 | anchors.fill: parent | ||
| 28 | |||
| 29 | property Img current: one | ||
| 30 | property string source: selector.selected | ||
| 31 | |||
| 32 | WallpaperSelector { | ||
| 33 | id: selector | ||
| 34 | seed: lockSurface.screen?.name || "" | ||
| 35 | } | ||
| 36 | |||
| 37 | onSourceChanged: { | ||
| 38 | if (!source) | ||
| 39 | current = null; | ||
| 40 | else if (current === one) | ||
| 41 | two.update() | ||
| 42 | else | ||
| 43 | one.update() | ||
| 44 | } | ||
| 45 | |||
| 46 | Img { id: one } | ||
| 47 | Img { id: two } | ||
| 48 | |||
| 49 | component Img: Item { | ||
| 50 | id: img | ||
| 51 | |||
| 52 | property string source | ||
| 53 | |||
| 54 | function update() { | ||
| 55 | source = background.source || "" | ||
| 56 | } | ||
| 57 | |||
| 58 | anchors.fill: parent | ||
| 59 | |||
| 60 | Image { | ||
| 61 | id: imageSource | ||
| 62 | |||
| 63 | source: img.source | ||
| 64 | sourceSize: Qt.size(parent.width, parent.height) | ||
| 65 | fillMode: Image.PreserveAspectCrop | ||
| 66 | smooth: true | ||
| 67 | visible: false | ||
| 68 | asynchronous: true | ||
| 69 | cache: false | ||
| 70 | |||
| 71 | onStatusChanged: { | ||
| 72 | if (status === Image.Ready) { | ||
| 73 | background.current = img | ||
| 74 | } | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | MultiEffect { | ||
| 79 | id: imageEffect | ||
| 80 | |||
| 81 | source: imageSource | ||
| 82 | anchors.fill: parent | ||
| 83 | blurEnabled: true | ||
| 84 | blur: 1 | ||
| 85 | blurMax: 64 | ||
| 86 | blurMultiplier: 2 | ||
| 87 | |||
| 88 | opacity: 0 | ||
| 89 | |||
| 90 | states: State { | ||
| 91 | name: "visible" | ||
| 92 | when: background.current === img | ||
| 93 | |||
| 94 | PropertyChanges { | ||
| 95 | imageEffect.opacity: 1 | ||
| 96 | } | ||
| 97 | StateChangeScript { | ||
| 98 | name: "unloadOther" | ||
| 99 | script: { | ||
| 100 | if (img === one) | ||
| 101 | two.source = "" | ||
| 102 | if (img === two) | ||
| 103 | one.source = "" | ||
| 104 | } | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | transitions: Transition { | ||
| 109 | SequentialAnimation { | ||
| 110 | NumberAnimation { | ||
| 111 | target: imageEffect | ||
| 112 | properties: "opacity" | ||
| 113 | duration: { | ||
| 114 | if (img === one && two.source == "" || img === two && one.source == "") | ||
| 115 | return 0; | ||
| 116 | return 5000; | ||
| 117 | } | ||
| 118 | easing.type: Easing.OutCubic | ||
| 119 | } | ||
| 120 | ScriptAction { | ||
| 121 | scriptName: "unloadOther" | ||
| 122 | } | ||
| 123 | } | ||
| 124 | } | ||
| 125 | } | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | Item { | ||
| 130 | anchors { | ||
| 131 | top: lockSurface.top | ||
| 132 | left: lockSurface.left | ||
| 133 | right: lockSurface.right | ||
| 134 | } | ||
| 135 | |||
| 136 | implicitWidth: lockSurface.width | ||
| 137 | implicitHeight: 21 | ||
| 138 | |||
| 139 | Rectangle { | ||
| 140 | anchors.fill: parent | ||
| 141 | color: Qt.rgba(0, 0, 0, 0.75) | ||
| 142 | } | ||
| 143 | |||
| 144 | Clock { | ||
| 145 | anchors.centerIn: parent | ||
| 146 | calendarPopup: false | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | WrapperRectangle { | ||
| 151 | id: unlockUi | ||
| 152 | |||
| 153 | Keys.onPressed: event => { | ||
| 154 | if (!lockSurface.authRunning) { | ||
| 155 | event.accepted = true; | ||
| 156 | lockSurface.authRunning = true; | ||
| 157 | } | ||
| 158 | } | ||
| 159 | focus: !passwordBox.visible | ||
| 160 | |||
| 161 | visible: lockSurface.authRunning | ||
| 162 | |||
| 163 | color: Qt.rgba(0, 0, 0, 0.75) | ||
| 164 | margin: 8 | ||
| 165 | |||
| 166 | anchors.centerIn: parent | ||
| 167 | |||
| 168 | ColumnLayout { | ||
| 169 | spacing: 4 | ||
| 170 | |||
| 171 | BusyIndicator { | ||
| 172 | visible: running | ||
| 173 | running: !Array.from(lockSurface.messages).length && !lockSurface.responseRequired | ||
| 174 | } | ||
| 175 | |||
| 176 | Repeater { | ||
| 177 | model: lockSurface.messages | ||
| 178 | |||
| 179 | Text { | ||
| 180 | required property var modelData | ||
| 181 | |||
| 182 | font.pointSize: 10 | ||
| 183 | font.family: "Fira Sans" | ||
| 184 | color: modelData.error ? "#f28a21" : "#ffffff" | ||
| 185 | |||
| 186 | text: String(modelData.text).trim() | ||
| 187 | |||
| 188 | Layout.fillWidth: true | ||
| 189 | horizontalAlignment: Text.AlignHCenter | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | TextField { | ||
| 194 | id: passwordBox | ||
| 195 | |||
| 196 | visible: lockSurface.responseRequired | ||
| 197 | echoMode: lockSurface.responseVisible ? TextInput.Normal : TextInput.Password | ||
| 198 | inputMethodHints: Qt.ImhSensitiveData | ||
| 199 | |||
| 200 | onTextChanged: lockSurface.currentText = passwordBox.text | ||
| 201 | onAccepted: { | ||
| 202 | passwordBox.readOnly = true; | ||
| 203 | lockSurface.response(lockSurface.currentText); | ||
| 204 | } | ||
| 205 | |||
| 206 | Connections { | ||
| 207 | target: lockSurface | ||
| 208 | function onCurrentTextChanged() { | ||
| 209 | passwordBox.text = lockSurface.currentText | ||
| 210 | } | ||
| 211 | } | ||
| 212 | Connections { | ||
| 213 | target: lockSurface | ||
| 214 | function onResponseRequiredChanged() { | ||
| 215 | if (lockSurface.responseRequired) | ||
| 216 | passwordBox.readOnly = false; | ||
| 217 | passwordBox.focus = true; | ||
| 218 | passwordBox.selectAll(); | ||
| 219 | } | ||
| 220 | } | ||
| 221 | |||
| 222 | Layout.topMargin: 4 | ||
| 223 | Layout.fillWidth: true | ||
| 224 | } | ||
| 225 | } | ||
| 226 | } | ||
| 227 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml b/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml new file mode 100644 index 00000000..996fd41b --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml | |||
| @@ -0,0 +1,132 @@ | |||
| 1 | import Quickshell | ||
| 2 | import Quickshell.Wayland | ||
| 3 | import Quickshell.Io | ||
| 4 | import Quickshell.Services.Pam | ||
| 5 | import Quickshell.Services.Mpris | ||
| 6 | import Custom as Custom | ||
| 7 | import qs.Services | ||
| 8 | import QtQml | ||
| 9 | |||
| 10 | Scope { | ||
| 11 | id: lockscreen | ||
| 12 | |||
| 13 | property string currentText: "" | ||
| 14 | |||
| 15 | PamContext { | ||
| 16 | id: pam | ||
| 17 | |||
| 18 | property list<var> messages: [] | ||
| 19 | |||
| 20 | config: "quickshell" | ||
| 21 | onCompleted: result => { | ||
| 22 | if (result === PamResult.Success) { | ||
| 23 | lock.locked = false; | ||
| 24 | } | ||
| 25 | } | ||
| 26 | onPamMessage: { | ||
| 27 | messages = Array.from(messages).concat([{ "text": pam.message, "error": pam.messageIsError }]) | ||
| 28 | } | ||
| 29 | onActiveChanged: { | ||
| 30 | messages = []; | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | IpcHandler { | ||
| 35 | target: "Lockscreen" | ||
| 36 | |||
| 37 | function setLocked(locked: bool): void { lock.locked = locked; } | ||
| 38 | function getLocked(): bool { return lock.locked; } | ||
| 39 | } | ||
| 40 | |||
| 41 | Connections { | ||
| 42 | target: Custom.Systemd | ||
| 43 | function onSleep(before: bool) { | ||
| 44 | console.log(`received prepare for sleep ${before}`); | ||
| 45 | if (before) | ||
| 46 | lock.locked = true; | ||
| 47 | } | ||
| 48 | function onLock() { lock.locked = true; } | ||
| 49 | function onUnlock() { lock.locked = false; } | ||
| 50 | } | ||
| 51 | |||
| 52 | IdleMonitor { | ||
| 53 | id: idleMonitor | ||
| 54 | enabled: !lock.secure | ||
| 55 | timeout: 600 | ||
| 56 | respectInhibitors: true | ||
| 57 | |||
| 58 | onIsIdleChanged: { | ||
| 59 | if (idleMonitor.isIdle) | ||
| 60 | lock.locked = true; | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | Custom.SystemdInhibitor { | ||
| 65 | enabled: !lock.secure | ||
| 66 | |||
| 67 | what: Custom.SystemdInhibitorParams.Sleep | ||
| 68 | who: "quickshell" | ||
| 69 | why: "Lock session" | ||
| 70 | mode: Custom.SystemdInhibitorParams.Delay | ||
| 71 | } | ||
| 72 | |||
| 73 | Binding { | ||
| 74 | target: NotificationManager | ||
| 75 | property: "lockscreenActive" | ||
| 76 | value: lock.locked | ||
| 77 | } | ||
| 78 | |||
| 79 | WlSessionLock { | ||
| 80 | id: lock | ||
| 81 | |||
| 82 | onLockStateChanged: { | ||
| 83 | if (!locked && pam.active) | ||
| 84 | pam.abort(); | ||
| 85 | |||
| 86 | if (locked) { | ||
| 87 | NiriService.sendCommand({ "Action": { "PowerOffMonitors": {} } }, _ => {}); | ||
| 88 | Custom.KeePassXC.lockAllDatabases(); | ||
| 89 | Array.from(MprisProxy.players).forEach(player => { | ||
| 90 | if (player.canPause && player.isPlaying) | ||
| 91 | player.pause(); | ||
| 92 | }); | ||
| 93 | // Custom.Systemd.stopUserUnit("gpg-agent.service", "replace"); | ||
| 94 | GpgAgent.reloadAgent(); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | Component.onCompleted: { (_ => {})(MprisProxy.players); } | ||
| 98 | |||
| 99 | onSecureStateChanged: Custom.Systemd.lockedHint = lock.secure | ||
| 100 | |||
| 101 | WlSessionLockSurface { | ||
| 102 | id: lockSurface | ||
| 103 | |||
| 104 | color: "black" | ||
| 105 | |||
| 106 | LockSurface { | ||
| 107 | id: surfaceContent | ||
| 108 | |||
| 109 | onResponse: responseText => pam.respond(responseText) | ||
| 110 | onAuthRunningChanged: { | ||
| 111 | if (authRunning) | ||
| 112 | pam.start(); | ||
| 113 | } | ||
| 114 | Connections { | ||
| 115 | target: pam | ||
| 116 | function onMessagesChanged() { surfaceContent.messages = pam.messages; } | ||
| 117 | function onResponseRequiredChanged() { surfaceContent.responseRequired = pam.responseRequired; } | ||
| 118 | function onActiveChanged() { surfaceContent.authRunning = pam.active; } | ||
| 119 | } | ||
| 120 | onCurrentTextChanged: lockscreen.currentText = currentText | ||
| 121 | Connections { | ||
| 122 | target: lockscreen | ||
| 123 | function onCurrentTextChanged() { surfaceContent.currentText = lockscreen.currentText; } | ||
| 124 | } | ||
| 125 | Connections { | ||
| 126 | target: lockSurface | ||
| 127 | function onScreenChanged() { surfaceContent.screen = lockSurface.screen; } | ||
| 128 | } | ||
| 129 | } | ||
| 130 | } | ||
| 131 | } | ||
| 132 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/MaterialDesignIcon.qml b/accounts/gkleen@sif/shell/quickshell/MaterialDesignIcon.qml new file mode 100644 index 00000000..155a009e --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/MaterialDesignIcon.qml | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | import QtQuick | ||
| 2 | import QtQuick.Effects | ||
| 3 | |||
| 4 | Item { | ||
| 5 | id: root | ||
| 6 | |||
| 7 | required property string icon | ||
| 8 | property color color: "white" | ||
| 9 | |||
| 10 | property real implicitSize: 0 | ||
| 11 | |||
| 12 | readonly property real actualSize: Math.min(root.width, root.height) | ||
| 13 | |||
| 14 | implicitWidth: root.implicitSize | ||
| 15 | implicitHeight: root.implicitSize | ||
| 16 | |||
| 17 | Image { | ||
| 18 | id: sourceImage | ||
| 19 | source: "file://" + @mdi@ + "/svg/" + root.icon + ".svg" | ||
| 20 | anchors.fill: parent | ||
| 21 | fillMode: Image.PreserveAspectFit | ||
| 22 | |||
| 23 | sourceSize.width: root.actualSize | ||
| 24 | sourceSize.height: root.actualSize | ||
| 25 | |||
| 26 | layer.enabled: true | ||
| 27 | layer.effect: MultiEffect { | ||
| 28 | id: effect | ||
| 29 | |||
| 30 | brightness: 1 | ||
| 31 | colorization: 1 | ||
| 32 | colorizationColor: root.color | ||
| 33 | } | ||
| 34 | } | ||
| 35 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/NiriIdle.qml b/accounts/gkleen@sif/shell/quickshell/NiriIdle.qml new file mode 100644 index 00000000..beff205c --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/NiriIdle.qml | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | import QtQml | ||
| 2 | import Quickshell | ||
| 3 | import Quickshell.Wayland | ||
| 4 | import qs.Services | ||
| 5 | import Custom as Custom | ||
| 6 | |||
| 7 | Scope { | ||
| 8 | IdleMonitor { | ||
| 9 | id: idleMonitor30 | ||
| 10 | timeout: 30 | ||
| 11 | |||
| 12 | onIsIdleChanged: Custom.Systemd.setIdleHint(idleMonitor30.isIdle) | ||
| 13 | } | ||
| 14 | IdleMonitor { | ||
| 15 | id: idleMonitor540 | ||
| 16 | timeout: 540 | ||
| 17 | |||
| 18 | onIsIdleChanged: { | ||
| 19 | if (idleMonitor540.isIdle) | ||
| 20 | NiriService.sendCommand({ "Action": { "PowerOffMonitors": {} } }, _ => {}); | ||
| 21 | } | ||
| 22 | } | ||
| 23 | Connections { | ||
| 24 | target: Custom.Systemd | ||
| 25 | function onSleep(before: bool) { | ||
| 26 | if (!before) | ||
| 27 | NiriService.sendCommand({ "Action": { "PowerOnMonitors": {} } }, _ => {}); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml b/accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml new file mode 100644 index 00000000..cc0e49b1 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml | |||
| @@ -0,0 +1,340 @@ | |||
| 1 | import QtQml | ||
| 2 | import QtQml.Models | ||
| 3 | import QtQuick | ||
| 4 | import Quickshell | ||
| 5 | import Quickshell.Widgets | ||
| 6 | import Quickshell.Wayland | ||
| 7 | import qs.Services | ||
| 8 | import QtQuick.Layouts | ||
| 9 | import Quickshell.Services.Notifications | ||
| 10 | |||
| 11 | Scope { | ||
| 12 | id: root | ||
| 13 | |||
| 14 | property var activeScreen: Array.from(Quickshell.screens).find(screen => screen.name === Array.from(NiriService.workspaces).find(ws => ws.is_focused)?.output) ?? null | ||
| 15 | |||
| 16 | Instantiator { | ||
| 17 | id: notifsRepeater | ||
| 18 | |||
| 19 | model: ScriptModel { | ||
| 20 | values: NotificationManager.groups | ||
| 21 | } | ||
| 22 | |||
| 23 | delegate: PanelWindow { | ||
| 24 | id: notifWindow | ||
| 25 | |||
| 26 | visible: NotificationManager.active | ||
| 27 | |||
| 28 | screen: root.activeScreen | ||
| 29 | |||
| 30 | WlrLayershell.namespace: "notifications" | ||
| 31 | |||
| 32 | required property var modelData | ||
| 33 | required property var index | ||
| 34 | |||
| 35 | property int activeIx: modelData.length - 1 | ||
| 36 | onModelDataChanged: { | ||
| 37 | notifWindow.activeIx = modelData.length - 1; | ||
| 38 | } | ||
| 39 | |||
| 40 | property color textColor: { | ||
| 41 | if (notifWindow.modelData?.[notifWindow.activeIx]?.urgency == NotificationUrgency.Low) | ||
| 42 | return "#ff999999"; | ||
| 43 | return "white"; | ||
| 44 | } | ||
| 45 | property color backgroundColor: { | ||
| 46 | if (notifWindow.modelData?.[notifWindow.activeIx]?.urgency == NotificationUrgency.Critical) | ||
| 47 | return "#dd900000"; | ||
| 48 | return "black"; | ||
| 49 | } | ||
| 50 | |||
| 51 | anchors { | ||
| 52 | right: true | ||
| 53 | top: true | ||
| 54 | } | ||
| 55 | |||
| 56 | readonly property real spaceAbove: { | ||
| 57 | var res = 0; | ||
| 58 | for (let i = 0; i < notifWindow.index; i++) { | ||
| 59 | (_ => {})(notifsRepeater.objectAt(i).modelData); | ||
| 60 | res += notifsRepeater.objectAt(i).height + 8; | ||
| 61 | } | ||
| 62 | return res; | ||
| 63 | } | ||
| 64 | |||
| 65 | margins { | ||
| 66 | right: 26 + 8 | ||
| 67 | top: 8 + spaceAbove | ||
| 68 | } | ||
| 69 | |||
| 70 | color: "transparent" | ||
| 71 | |||
| 72 | implicitHeight: Math.max(notifCount.visible ? notifCount.contentHeight : 0, notifSummary.contentHeight) + (notifBody.visible ? 8 + notifBody.contentHeight : 0) + (notifActions.visible ? 8 + notifActions.height : 0) + (notifTime.visible ? 8 + notifTime.contentHeight : 0) + 16 | ||
| 73 | implicitWidth: 400 | ||
| 74 | |||
| 75 | WrapperMouseArea { | ||
| 76 | enabled: true | ||
| 77 | |||
| 78 | anchors.fill: parent | ||
| 79 | |||
| 80 | cursorShape: Qt.PointingHandCursor | ||
| 81 | |||
| 82 | onClicked: { | ||
| 83 | for (const notif of notifWindow.modelData) | ||
| 84 | notif.dismiss(); | ||
| 85 | } | ||
| 86 | |||
| 87 | property real angleRem: 0 | ||
| 88 | property real sensitivity: 1 / 120 | ||
| 89 | onWheel: event => { | ||
| 90 | angleRem += event.angleDelta.y; | ||
| 91 | const d = Math.round(angleRem * sensitivity); | ||
| 92 | angleRem -= d / sensitivity; | ||
| 93 | notifWindow.activeIx = ((notifWindow.modelData?.length ?? 1) + notifWindow.activeIx - d) % (notifWindow.modelData?.length ?? 1); | ||
| 94 | } | ||
| 95 | |||
| 96 | Rectangle { | ||
| 97 | color: notifWindow.backgroundColor | ||
| 98 | anchors.fill: parent | ||
| 99 | border { | ||
| 100 | color: Qt.hsla(195/360, 1, 0.45, 1) | ||
| 101 | width: 2 | ||
| 102 | } | ||
| 103 | |||
| 104 | GridLayout { | ||
| 105 | id: notifLayout | ||
| 106 | |||
| 107 | width: 400 - 16 | ||
| 108 | anchors.fill: parent | ||
| 109 | anchors.margins: 8 | ||
| 110 | columnSpacing: 8 | ||
| 111 | rowSpacing: 8 | ||
| 112 | |||
| 113 | columns: notifImage.visible ? 3 : 2 | ||
| 114 | rows: { | ||
| 115 | var res = 1; | ||
| 116 | if (notifBody.visible) | ||
| 117 | res += 1; | ||
| 118 | if (notifActions.visible) | ||
| 119 | res += 1; | ||
| 120 | if (notifTime.visible) | ||
| 121 | res += 1; | ||
| 122 | return res; | ||
| 123 | } | ||
| 124 | |||
| 125 | Text { | ||
| 126 | id: notifCount | ||
| 127 | |||
| 128 | visible: notifWindow.modelData?.length > 1 ?? false | ||
| 129 | text: `${notifWindow.activeIx + 1}/${notifWindow.modelData?.length ?? ""}` | ||
| 130 | |||
| 131 | font.pointSize: 10 | ||
| 132 | font.family: "Fira Sans" | ||
| 133 | font.bold: true | ||
| 134 | font.features: { "tnum": 1 } | ||
| 135 | color: notifWindow.textColor | ||
| 136 | maximumLineCount: 1 | ||
| 137 | |||
| 138 | Layout.fillWidth: false | ||
| 139 | Layout.row: 0 | ||
| 140 | Layout.column: notifImage.visible ? 1 : 0 | ||
| 141 | } | ||
| 142 | |||
| 143 | Text { | ||
| 144 | id: notifSummary | ||
| 145 | |||
| 146 | text: notifWindow.modelData?.[notifWindow.activeIx]?.summary ?? "" | ||
| 147 | |||
| 148 | font.pointSize: 10 | ||
| 149 | font.family: "Fira Sans" | ||
| 150 | font.italic: true | ||
| 151 | color: notifWindow.textColor | ||
| 152 | maximumLineCount: 1 | ||
| 153 | elide: Text.ElideRight | ||
| 154 | |||
| 155 | Layout.fillWidth: true | ||
| 156 | Layout.row: 0 | ||
| 157 | Layout.column: (notifCount.visible ? 1 : 0) + (notifImage.visible ? 1 : 0) | ||
| 158 | Layout.columnSpan: notifCount.visible ? 1 : 2 | ||
| 159 | } | ||
| 160 | |||
| 161 | Image { | ||
| 162 | id: notifImage | ||
| 163 | |||
| 164 | visible: (notifWindow.modelData?.[notifWindow.activeIx]?.image || notifWindow.modelData?.[notifWindow.activeIx]?.appIcon) ?? false | ||
| 165 | |||
| 166 | onStatusChanged: { | ||
| 167 | if (notifImage.status == Image.Error) | ||
| 168 | notifImage.visible = false; | ||
| 169 | } | ||
| 170 | |||
| 171 | source: (notifWindow.modelData?.[notifWindow.activeIx]?.image || notifWindow.modelData?.[notifWindow.activeIx]?.appIcon) ?? "" | ||
| 172 | fillMode: Image.PreserveAspectFit | ||
| 173 | asynchronous: true | ||
| 174 | smooth: true | ||
| 175 | mipmap: true | ||
| 176 | |||
| 177 | Layout.maximumWidth: 50 | ||
| 178 | Layout.column: 0 | ||
| 179 | Layout.row: 0 | ||
| 180 | Layout.fillHeight: true | ||
| 181 | Layout.rowSpan: 1 + (notifBody.visible ? 1 : 0) + (notifTime.visible ? 1 : 0) | ||
| 182 | } | ||
| 183 | |||
| 184 | Text { | ||
| 185 | id: notifBody | ||
| 186 | |||
| 187 | visible: notifWindow.modelData?.[notifWindow.activeIx]?.body ?? false | ||
| 188 | text: notifWindow.modelData?.[notifWindow.activeIx]?.body ?? "" | ||
| 189 | textFormat: Text.RichText | ||
| 190 | wrapMode: Text.Wrap | ||
| 191 | |||
| 192 | font.pointSize: 10 | ||
| 193 | font.family: "Fira Sans" | ||
| 194 | color: notifWindow.textColor | ||
| 195 | |||
| 196 | Layout.fillWidth: true | ||
| 197 | Layout.row: 1 | ||
| 198 | Layout.column: notifImage.visible ? 1 : 0 | ||
| 199 | Layout.columnSpan: notifCount.visible ? 2 : 1 | ||
| 200 | } | ||
| 201 | |||
| 202 | Text { | ||
| 203 | id: notifTime | ||
| 204 | |||
| 205 | Connections { | ||
| 206 | target: NotificationManager.clock | ||
| 207 | function onDateChanged() { | ||
| 208 | notifTime.text = NotificationManager.formatTime(notifWindow.modelData?.[notifWindow.activeIx]?.receivedTime); | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | visible: notifTime.text && notifTime.text !== "now" | ||
| 213 | text: NotificationManager.formatTime(notifWindow.modelData?.[notifWindow.activeIx]?.receivedTime) | ||
| 214 | |||
| 215 | font.pointSize: 8 | ||
| 216 | font.family: "Fira Sans" | ||
| 217 | font.italic: true | ||
| 218 | color: "#555" | ||
| 219 | maximumLineCount: 1 | ||
| 220 | horizontalAlignment: Text.AlignRight | ||
| 221 | |||
| 222 | Layout.fillWidth: true | ||
| 223 | Layout.row: notifBody.visible ? 2 : 1 | ||
| 224 | Layout.column: notifImage.visible ? 1 : 0 | ||
| 225 | Layout.columnSpan: 2 | ||
| 226 | } | ||
| 227 | |||
| 228 | RowLayout { | ||
| 229 | id: notifActions | ||
| 230 | |||
| 231 | visible: notifWindow.modelData?.[notifWindow.activeIx]?.actions.length > 0 ?? false | ||
| 232 | |||
| 233 | spacing: 8 | ||
| 234 | uniformCellSizes: true | ||
| 235 | |||
| 236 | width: 400 - 16 | ||
| 237 | Layout.row: 1 + (notifBody.visible ? 1 : 0) + (notifTime.visible ? 1 : 0) | ||
| 238 | Layout.column: 0 | ||
| 239 | Layout.columnSpan: 2 + (notifImage.visible ? 1 : 0) | ||
| 240 | |||
| 241 | Repeater { | ||
| 242 | model: ScriptModel { | ||
| 243 | values: notifWindow.modelData?.[notifWindow.activeIx]?.actions | ||
| 244 | } | ||
| 245 | |||
| 246 | delegate: WrapperMouseArea { | ||
| 247 | id: actionMouseArea | ||
| 248 | |||
| 249 | required property var modelData | ||
| 250 | |||
| 251 | height: actionLabelWrapper.implicitHeight | ||
| 252 | Layout.fillWidth: true | ||
| 253 | Layout.horizontalStretchFactor: 1 | ||
| 254 | |||
| 255 | hoverEnabled: true | ||
| 256 | cursorShape: Qt.PointingHandCursor | ||
| 257 | |||
| 258 | onClicked: actionMouseArea.modelData?.invoke() | ||
| 259 | |||
| 260 | Rectangle { | ||
| 261 | anchors.fill: parent | ||
| 262 | |||
| 263 | color: actionMouseArea.containsMouse ? "#20ffffff" : "transparent" | ||
| 264 | |||
| 265 | border { | ||
| 266 | width: 2 | ||
| 267 | color: "#20ffffff" | ||
| 268 | } | ||
| 269 | |||
| 270 | WrapperItem { | ||
| 271 | id: actionLabelWrapper | ||
| 272 | |||
| 273 | margin: 8 | ||
| 274 | anchors.centerIn: parent | ||
| 275 | |||
| 276 | RowLayout { | ||
| 277 | id: actionLabelLayout | ||
| 278 | |||
| 279 | spacing: 8 | ||
| 280 | |||
| 281 | IconImage { | ||
| 282 | id: actionIcon | ||
| 283 | |||
| 284 | visible: notifWindow.modelData?.[notifWindow.activeIx]?.hasActionIcons | ||
| 285 | |||
| 286 | onStatusChanged: { | ||
| 287 | if (actionIcon.status == Image.Error) | ||
| 288 | actionIcon.visible = false; | ||
| 289 | } | ||
| 290 | |||
| 291 | implicitSize: 16 | ||
| 292 | source: { | ||
| 293 | if (!actionIcon.visible) | ||
| 294 | return ""; | ||
| 295 | |||
| 296 | let icon = actionMouseArea.modelData?.identifier ?? "" | ||
| 297 | if (icon.includes("?path=")) { | ||
| 298 | const split = icon.split("?path=") | ||
| 299 | if (split.length !== 2) | ||
| 300 | return icon | ||
| 301 | const name = split[0] | ||
| 302 | const path = split[1] | ||
| 303 | const fileName = name.substring( | ||
| 304 | name.lastIndexOf("/") + 1) | ||
| 305 | return `file://${path}/${fileName}` | ||
| 306 | } | ||
| 307 | return icon | ||
| 308 | } | ||
| 309 | asynchronous: true | ||
| 310 | smooth: true | ||
| 311 | mipmap: true | ||
| 312 | |||
| 313 | Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter | ||
| 314 | } | ||
| 315 | |||
| 316 | Text { | ||
| 317 | id: actionLabel | ||
| 318 | |||
| 319 | visible: actionMouseArea.modelData?.text ?? false | ||
| 320 | |||
| 321 | text: actionMouseArea.modelData?.text ?? "" | ||
| 322 | |||
| 323 | font.pointSize: 10 | ||
| 324 | font.family: "Fira Sans" | ||
| 325 | color: notifWindow.textColor | ||
| 326 | maximumLineCount: 1 | ||
| 327 | elide: Text.ElideRight | ||
| 328 | } | ||
| 329 | } | ||
| 330 | } | ||
| 331 | } | ||
| 332 | } | ||
| 333 | } | ||
| 334 | } | ||
| 335 | } | ||
| 336 | } | ||
| 337 | } | ||
| 338 | } | ||
| 339 | } | ||
| 340 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/NotificationInhibitorWidget.qml b/accounts/gkleen@sif/shell/quickshell/NotificationInhibitorWidget.qml new file mode 100644 index 00000000..b58467b3 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/NotificationInhibitorWidget.qml | |||
| @@ -0,0 +1,266 @@ | |||
| 1 | import Quickshell | ||
| 2 | import QtQuick | ||
| 3 | import Quickshell.Widgets | ||
| 4 | import qs.Services | ||
| 5 | import QtQuick.Controls | ||
| 6 | import QtQuick.Layouts | ||
| 7 | import QtQuick.Shapes | ||
| 8 | |||
| 9 | Item { | ||
| 10 | id: root | ||
| 11 | |||
| 12 | width: icon.width + 8 | ||
| 13 | height: parent.height | ||
| 14 | anchors.verticalCenter: parent.verticalCenter | ||
| 15 | |||
| 16 | WrapperMouseArea { | ||
| 17 | id: widgetMouseArea | ||
| 18 | |||
| 19 | anchors.fill: parent | ||
| 20 | |||
| 21 | hoverEnabled: true | ||
| 22 | cursorShape: Qt.PointingHandCursor | ||
| 23 | |||
| 24 | onClicked: NotificationManager.displayInhibited = !NotificationManager.displayInhibited | ||
| 25 | |||
| 26 | Rectangle { | ||
| 27 | anchors.fill: parent | ||
| 28 | color: { | ||
| 29 | if (widgetMouseArea.containsMouse) { | ||
| 30 | return "#33808080"; | ||
| 31 | } | ||
| 32 | return "transparent"; | ||
| 33 | } | ||
| 34 | |||
| 35 | Item { | ||
| 36 | anchors.fill: parent | ||
| 37 | |||
| 38 | MaterialDesignIcon { | ||
| 39 | id: icon | ||
| 40 | |||
| 41 | implicitSize: 14 | ||
| 42 | anchors.centerIn: parent | ||
| 43 | |||
| 44 | icon: NotificationManager.active ? "message" : "message-off" | ||
| 45 | color: { | ||
| 46 | if (!NotificationManager.active && !NotificationManager.displayInhibited) | ||
| 47 | return "#f28a21"; | ||
| 48 | if (NotificationManager.displayInhibited) | ||
| 49 | return "white"; | ||
| 50 | return "#555"; | ||
| 51 | } | ||
| 52 | } | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | Loader { | ||
| 58 | id: tooltipLoader | ||
| 59 | |||
| 60 | active: false | ||
| 61 | |||
| 62 | Connections { | ||
| 63 | target: widgetMouseArea | ||
| 64 | function onContainsMouseChanged() { | ||
| 65 | if (widgetMouseArea.containsMouse) | ||
| 66 | tooltipLoader.active = true; | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | sourceComponent: PopupWindow { | ||
| 71 | id: tooltip | ||
| 72 | |||
| 73 | property bool nextVisible: NotificationManager.active && (widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse) | ||
| 74 | |||
| 75 | anchor { | ||
| 76 | item: widgetMouseArea | ||
| 77 | edges: Edges.Bottom | Edges.Left | ||
| 78 | } | ||
| 79 | visible: false | ||
| 80 | |||
| 81 | onNextVisibleChanged: hangTimer.restart() | ||
| 82 | |||
| 83 | Timer { | ||
| 84 | id: hangTimer | ||
| 85 | interval: tooltip.visible ? 100 : 500 | ||
| 86 | onTriggered: { | ||
| 87 | tooltip.visible = tooltip.nextVisible; | ||
| 88 | if (!tooltip.visible) | ||
| 89 | tooltipLoader.active = false; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | implicitWidth: 400 | ||
| 94 | implicitHeight: Math.min(tooltip.screen.height * 0.66, Math.max(100, scroll.contentHeight + 16)) | ||
| 95 | color: "black" | ||
| 96 | |||
| 97 | WrapperMouseArea { | ||
| 98 | id: tooltipMouseArea | ||
| 99 | |||
| 100 | hoverEnabled: true | ||
| 101 | enabled: true | ||
| 102 | |||
| 103 | anchors.fill: parent | ||
| 104 | |||
| 105 | WrapperItem { | ||
| 106 | margin: 8 | ||
| 107 | |||
| 108 | ScrollView { | ||
| 109 | id: scroll | ||
| 110 | |||
| 111 | contentWidth: availableWidth | ||
| 112 | // ScrollBar.vertical.policy: ScrollBar.AlwaysOn | ||
| 113 | |||
| 114 | ColumnLayout { | ||
| 115 | id: historyLayout | ||
| 116 | anchors { | ||
| 117 | left: parent.left | ||
| 118 | right: parent.right | ||
| 119 | } | ||
| 120 | |||
| 121 | spacing: 8 | ||
| 122 | |||
| 123 | Repeater { | ||
| 124 | model: ScriptModel { | ||
| 125 | values: [...NotificationManager.history].reverse().map(o => o.notification) | ||
| 126 | } | ||
| 127 | |||
| 128 | delegate: GridLayout { | ||
| 129 | id: notif | ||
| 130 | |||
| 131 | Layout.fillWidth: true | ||
| 132 | Layout.preferredHeight: notifSummary.contentHeight + (notifBody.visible ? notifBody.contentHeight + 8 : 0) + (notifSep.visible ? notifSep.height + 8 : 0) + notifTime.contentHeight + 8 | ||
| 133 | |||
| 134 | required property var modelData | ||
| 135 | required property int index | ||
| 136 | |||
| 137 | columnSpacing: 8 | ||
| 138 | rowSpacing: 8 | ||
| 139 | |||
| 140 | columns: notifImage.visible ? 2 : 1 | ||
| 141 | rows: { | ||
| 142 | var res = 2; | ||
| 143 | if (notifBody.visible) | ||
| 144 | res += 1; | ||
| 145 | if (notifSep.visible) | ||
| 146 | res += 1; | ||
| 147 | return res; | ||
| 148 | } | ||
| 149 | |||
| 150 | Shape { | ||
| 151 | id: notifSep | ||
| 152 | |||
| 153 | visible: notif.index != 0 | ||
| 154 | |||
| 155 | height: 2 | ||
| 156 | width: 400 - 32 | ||
| 157 | |||
| 158 | ShapePath { | ||
| 159 | strokeWidth: 2 | ||
| 160 | strokeColor: "#20ffffff" | ||
| 161 | startX: 0; startY: 0; | ||
| 162 | PathLine { x: 400 - 32; y: 0; } | ||
| 163 | } | ||
| 164 | |||
| 165 | Layout.row: 0 | ||
| 166 | Layout.column: 0 | ||
| 167 | Layout.columnSpan: notifImage.visible ? 2 : 1 | ||
| 168 | Layout.alignment: Qt.AlignHCenter | ||
| 169 | } | ||
| 170 | |||
| 171 | Text { | ||
| 172 | id: notifSummary | ||
| 173 | |||
| 174 | text: notif.modelData?.summary ?? "" | ||
| 175 | |||
| 176 | font.pointSize: 10 | ||
| 177 | font.family: "Fira Sans" | ||
| 178 | font.italic: true | ||
| 179 | color: "white" | ||
| 180 | maximumLineCount: 1 | ||
| 181 | elide: Text.ElideRight | ||
| 182 | |||
| 183 | Layout.fillWidth: true | ||
| 184 | Layout.row: notifSep.visible ? 1 : 0 | ||
| 185 | Layout.column: notifImage.visible ? 1 : 0 | ||
| 186 | } | ||
| 187 | |||
| 188 | Image { | ||
| 189 | id: notifImage | ||
| 190 | |||
| 191 | visible: (notif.modelData?.image || notif.modelData?.appIcon) ?? false | ||
| 192 | |||
| 193 | onStatusChanged: { | ||
| 194 | if (notifImage.status == Image.Error) | ||
| 195 | notifImage.visible = false; | ||
| 196 | } | ||
| 197 | |||
| 198 | source: (notif.modelData?.image || notif.modelData?.appIcon) ?? "" | ||
| 199 | fillMode: Image.PreserveAspectFit | ||
| 200 | asynchronous: true | ||
| 201 | smooth: true | ||
| 202 | mipmap: true | ||
| 203 | |||
| 204 | Layout.maximumWidth: 50 | ||
| 205 | Layout.column: 0 | ||
| 206 | Layout.row: notifSep.visible ? 1 : 0 | ||
| 207 | Layout.fillHeight: true | ||
| 208 | Layout.rowSpan: notifBody.visible ? 3 : 2 | ||
| 209 | } | ||
| 210 | |||
| 211 | Text { | ||
| 212 | id: notifBody | ||
| 213 | |||
| 214 | visible: notif.modelData?.body ?? false | ||
| 215 | text: notif.modelData?.body ?? "" | ||
| 216 | textFormat: Text.RichText | ||
| 217 | wrapMode: Text.Wrap | ||
| 218 | |||
| 219 | font.pointSize: 10 | ||
| 220 | font.family: "Fira Sans" | ||
| 221 | color: "white" | ||
| 222 | |||
| 223 | Layout.fillWidth: true | ||
| 224 | Layout.row: notifSep.visible ? 2 : 1 | ||
| 225 | Layout.column: notifImage.visible ? 1 : 0 | ||
| 226 | } | ||
| 227 | |||
| 228 | Text { | ||
| 229 | id: notifTime | ||
| 230 | |||
| 231 | Connections { | ||
| 232 | target: NotificationManager.clock | ||
| 233 | function onDateChanged() { | ||
| 234 | notifTime.text = NotificationManager.formatTime(notif.modelData?.receivedTime); | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | text: NotificationManager.formatTime(notif.modelData?.receivedTime) | ||
| 239 | |||
| 240 | font.pointSize: 8 | ||
| 241 | font.family: "Fira Sans" | ||
| 242 | font.italic: true | ||
| 243 | color: "#555" | ||
| 244 | maximumLineCount: 1 | ||
| 245 | horizontalAlignment: Text.AlignRight | ||
| 246 | |||
| 247 | Layout.fillWidth: true | ||
| 248 | Layout.row: { | ||
| 249 | var res = 1; | ||
| 250 | if (notifSep.visible) | ||
| 251 | res += 1; | ||
| 252 | if (notifBody.visible) | ||
| 253 | res += 1; | ||
| 254 | return res; | ||
| 255 | } | ||
| 256 | Layout.column: notifImage.visible ? 1 : 0 | ||
| 257 | } | ||
| 258 | } | ||
| 259 | } | ||
| 260 | } | ||
| 261 | } | ||
| 262 | } | ||
| 263 | } | ||
| 264 | } | ||
| 265 | } | ||
| 266 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml b/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml new file mode 100644 index 00000000..9c6b65a4 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml | |||
| @@ -0,0 +1,483 @@ | |||
| 1 | import QtQuick | ||
| 2 | import QtQuick.Layouts | ||
| 3 | import QtQuick.Controls.Fusion | ||
| 4 | import Quickshell | ||
| 5 | import Quickshell.Services.Pipewire | ||
| 6 | import Quickshell.Widgets | ||
| 7 | |||
| 8 | Item { | ||
| 9 | height: parent.height | ||
| 10 | width: volumeIcon.width + 8 | ||
| 11 | anchors.verticalCenter: parent.verticalCenter | ||
| 12 | |||
| 13 | PwObjectTracker { | ||
| 14 | objects: [Pipewire.defaultAudioSink] | ||
| 15 | } | ||
| 16 | |||
| 17 | WrapperMouseArea { | ||
| 18 | id: widgetMouseArea | ||
| 19 | |||
| 20 | anchors.fill: parent | ||
| 21 | hoverEnabled: true | ||
| 22 | cursorShape: Qt.PointingHandCursor | ||
| 23 | |||
| 24 | onClicked: { | ||
| 25 | if (!Pipewire.defaultAudioSink) | ||
| 26 | return; | ||
| 27 | Pipewire.defaultAudioSink.audio.muted = !Pipewire.defaultAudioSink.audio.muted; | ||
| 28 | } | ||
| 29 | |||
| 30 | property real sensitivity: (1 / 40) / 120 | ||
| 31 | onWheel: event => { | ||
| 32 | if (!Pipewire.defaultAudioSink) | ||
| 33 | return; | ||
| 34 | Pipewire.defaultAudioSink.audio.volume += event.angleDelta.y * sensitivity; | ||
| 35 | } | ||
| 36 | |||
| 37 | Rectangle { | ||
| 38 | id: volumeWidget | ||
| 39 | |||
| 40 | anchors.fill: parent | ||
| 41 | color: { | ||
| 42 | if (widgetMouseArea.containsMouse) | ||
| 43 | return "#33808080"; | ||
| 44 | return "transparent"; | ||
| 45 | } | ||
| 46 | |||
| 47 | Item { | ||
| 48 | anchors.fill: parent | ||
| 49 | |||
| 50 | MaterialDesignIcon { | ||
| 51 | id: volumeIcon | ||
| 52 | |||
| 53 | implicitSize: 14 | ||
| 54 | anchors.centerIn: parent | ||
| 55 | |||
| 56 | icon: { | ||
| 57 | if (!Pipewire.defaultAudioSink || Pipewire.defaultAudioSink.audio.muted) | ||
| 58 | return "volume-off"; | ||
| 59 | if (Pipewire.defaultAudioSink.audio.volume <= 0.33) | ||
| 60 | return "volume-low"; | ||
| 61 | if (Pipewire.defaultAudioSink.audio.volume <= 0.67) | ||
| 62 | return "volume-medium"; | ||
| 63 | return "volume-high"; | ||
| 64 | } | ||
| 65 | color: "#555" | ||
| 66 | } | ||
| 67 | } | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | Loader { | ||
| 72 | id: tooltipLoader | ||
| 73 | |||
| 74 | active: false | ||
| 75 | |||
| 76 | Connections { | ||
| 77 | target: widgetMouseArea | ||
| 78 | function onContainsMouseChanged() { | ||
| 79 | if (widgetMouseArea.containsMouse) | ||
| 80 | tooltipLoader.active = true; | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | PwObjectTracker { | ||
| 85 | objects: Pipewire.devices | ||
| 86 | } | ||
| 87 | PwObjectTracker { | ||
| 88 | objects: Pipewire.nodes | ||
| 89 | } | ||
| 90 | |||
| 91 | sourceComponent: PopupWindow { | ||
| 92 | id: tooltip | ||
| 93 | |||
| 94 | property bool openPopup: false | ||
| 95 | property bool nextVisible: widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse || openPopup | ||
| 96 | |||
| 97 | anchor { | ||
| 98 | item: widgetMouseArea | ||
| 99 | edges: Edges.Bottom | Edges.Left | ||
| 100 | } | ||
| 101 | visible: false | ||
| 102 | |||
| 103 | onNextVisibleChanged: hangTimer.restart() | ||
| 104 | |||
| 105 | Timer { | ||
| 106 | id: hangTimer | ||
| 107 | interval: 100 | ||
| 108 | onTriggered: { | ||
| 109 | tooltip.visible = tooltip.nextVisible; | ||
| 110 | if (!tooltip.visible) | ||
| 111 | tooltipLoader.active = false; | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | implicitWidth: tooltipContent.width | ||
| 116 | implicitHeight: tooltipContent.height | ||
| 117 | color: "transparent" | ||
| 118 | |||
| 119 | Rectangle { | ||
| 120 | width: tooltip.width | ||
| 121 | height: tooltipLayout.childrenRect.height + 16 | ||
| 122 | color: "black" | ||
| 123 | } | ||
| 124 | |||
| 125 | WrapperItem { | ||
| 126 | id: tooltipContent | ||
| 127 | |||
| 128 | bottomMargin: Math.max(0, 200 - tooltipLayout.implicitHeight) | ||
| 129 | |||
| 130 | WrapperMouseArea { | ||
| 131 | id: tooltipMouseArea | ||
| 132 | |||
| 133 | hoverEnabled: true | ||
| 134 | enabled: true | ||
| 135 | |||
| 136 | WrapperItem { | ||
| 137 | margin: 8 | ||
| 138 | bottomMargin: 8 | ||
| 139 | |||
| 140 | GridLayout { | ||
| 141 | id: tooltipLayout | ||
| 142 | |||
| 143 | columns: 4 | ||
| 144 | |||
| 145 | Repeater { | ||
| 146 | model: Array.from(Pipewire.devices.values).filter(dev => dev.type == "Audio/Device") | ||
| 147 | |||
| 148 | Item { | ||
| 149 | id: descItem | ||
| 150 | |||
| 151 | required property var modelData | ||
| 152 | required property int index | ||
| 153 | |||
| 154 | Layout.column: 0 | ||
| 155 | Layout.row: index | ||
| 156 | |||
| 157 | implicitWidth: descText.contentWidth | ||
| 158 | implicitHeight: descText.contentHeight | ||
| 159 | |||
| 160 | Text { | ||
| 161 | id: descText | ||
| 162 | |||
| 163 | color: "white" | ||
| 164 | font.pointSize: 10 | ||
| 165 | font.family: "Fira Sans" | ||
| 166 | |||
| 167 | text: descItem.modelData.description | ||
| 168 | } | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | Repeater { | ||
| 173 | id: defaultSinkRepeater | ||
| 174 | |||
| 175 | model: { | ||
| 176 | Array.from(Pipewire.devices.values) | ||
| 177 | .filter(dev => dev.type == "Audio/Device") | ||
| 178 | .map(device => Array.from(Pipewire.nodes.values).find(node => node.type == PwNodeType.AudioSink && node.device?.id == device.id )); | ||
| 179 | } | ||
| 180 | |||
| 181 | Item { | ||
| 182 | id: defaultSinkItem | ||
| 183 | |||
| 184 | required property var modelData | ||
| 185 | required property int index | ||
| 186 | |||
| 187 | visible: Boolean(modelData) | ||
| 188 | |||
| 189 | PwObjectTracker { | ||
| 190 | objects: [defaultSinkItem.modelData] | ||
| 191 | } | ||
| 192 | |||
| 193 | Layout.column: 1 | ||
| 194 | Layout.row: index | ||
| 195 | |||
| 196 | Layout.fillHeight: true | ||
| 197 | |||
| 198 | implicitWidth: 16 + 8 | ||
| 199 | |||
| 200 | WrapperMouseArea { | ||
| 201 | id: defaultSinkMouseArea | ||
| 202 | |||
| 203 | anchors.fill: parent | ||
| 204 | hoverEnabled: true | ||
| 205 | cursorShape: Qt.PointingHandCursor | ||
| 206 | |||
| 207 | onClicked: { | ||
| 208 | Pipewire.preferredDefaultAudioSink = defaultSinkItem.modelData | ||
| 209 | } | ||
| 210 | |||
| 211 | onWheel: event => scrollVolume(event); | ||
| 212 | property real sensitivity: (1 / 40) / 120 | ||
| 213 | function scrollVolume(event) { | ||
| 214 | defaultSinkItem.modelData.audio.volume += event.angleDelta.y * sensitivity; | ||
| 215 | } | ||
| 216 | |||
| 217 | Rectangle { | ||
| 218 | id: defaultSinkWidget | ||
| 219 | |||
| 220 | anchors.fill: parent | ||
| 221 | color: { | ||
| 222 | if (defaultSinkMouseArea.containsMouse) | ||
| 223 | return "#33808080"; | ||
| 224 | return "transparent"; | ||
| 225 | } | ||
| 226 | |||
| 227 | MaterialDesignIcon { | ||
| 228 | width: 16 | ||
| 229 | height: 16 | ||
| 230 | anchors.centerIn: parent | ||
| 231 | |||
| 232 | icon: { | ||
| 233 | if (defaultSinkItem.modelData?.id == Pipewire.defaultAudioSink?.id) | ||
| 234 | return "speaker"; | ||
| 235 | return "speaker-off"; | ||
| 236 | } | ||
| 237 | color: icon == "speaker" ? "white" : "#555" | ||
| 238 | } | ||
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 242 | PopupWindow { | ||
| 243 | id: volumeTooltip | ||
| 244 | |||
| 245 | property bool nextVisible: defaultSinkMouseArea.containsMouse || volumeTooltipMouseArea.containsMouse | ||
| 246 | |||
| 247 | anchor { | ||
| 248 | item: defaultSinkMouseArea | ||
| 249 | edges: Edges.Bottom | Edges.Left | ||
| 250 | } | ||
| 251 | visible: false | ||
| 252 | |||
| 253 | onNextVisibleChanged: volumeHangTimer.restart() | ||
| 254 | |||
| 255 | onVisibleChanged: tooltip.openPopup = volumeTooltip.visible | ||
| 256 | |||
| 257 | Timer { | ||
| 258 | id: volumeHangTimer | ||
| 259 | interval: 100 | ||
| 260 | onTriggered: volumeTooltip.visible = volumeTooltip.nextVisible | ||
| 261 | } | ||
| 262 | |||
| 263 | implicitWidth: volumeTooltipText.contentWidth + 16 | ||
| 264 | implicitHeight: volumeTooltipText.contentHeight + 16 | ||
| 265 | color: "black" | ||
| 266 | |||
| 267 | WrapperMouseArea { | ||
| 268 | id: volumeTooltipMouseArea | ||
| 269 | |||
| 270 | hoverEnabled: true | ||
| 271 | enabled: true | ||
| 272 | |||
| 273 | onWheel: event => defaultSinkMouseArea.scrollVolume(event); | ||
| 274 | |||
| 275 | anchors.fill: parent | ||
| 276 | |||
| 277 | Item { | ||
| 278 | anchors.fill: parent | ||
| 279 | |||
| 280 | Text { | ||
| 281 | id: volumeTooltipText | ||
| 282 | |||
| 283 | anchors.centerIn: parent | ||
| 284 | |||
| 285 | font.pointSize: 10 | ||
| 286 | font.family: "Fira Sans" | ||
| 287 | color: "white" | ||
| 288 | |||
| 289 | text: `${Math.round(defaultSinkItem.modelData?.audio?.volume * 100)}%` | ||
| 290 | } | ||
| 291 | } | ||
| 292 | } | ||
| 293 | } | ||
| 294 | } | ||
| 295 | } | ||
| 296 | |||
| 297 | Repeater { | ||
| 298 | id: defaultSourceRepeater | ||
| 299 | |||
| 300 | model: { | ||
| 301 | Array.from(Pipewire.devices.values) | ||
| 302 | .filter(dev => dev.type == "Audio/Device") | ||
| 303 | .map(device => Array.from(Pipewire.nodes.values).find(node => node.type == PwNodeType.AudioSource && node.device?.id == device.id )); | ||
| 304 | } | ||
| 305 | |||
| 306 | Item { | ||
| 307 | id: defaultSourceItem | ||
| 308 | |||
| 309 | required property var modelData | ||
| 310 | required property int index | ||
| 311 | |||
| 312 | visible: Boolean(modelData) | ||
| 313 | |||
| 314 | PwObjectTracker { | ||
| 315 | objects: [defaultSourceItem.modelData] | ||
| 316 | } | ||
| 317 | |||
| 318 | Layout.column: 2 | ||
| 319 | Layout.row: index | ||
| 320 | |||
| 321 | Layout.fillHeight: true | ||
| 322 | |||
| 323 | implicitWidth: 16 + 8 | ||
| 324 | |||
| 325 | WrapperMouseArea { | ||
| 326 | id: defaultSourceMouseArea | ||
| 327 | |||
| 328 | anchors.fill: parent | ||
| 329 | hoverEnabled: true | ||
| 330 | cursorShape: Qt.PointingHandCursor | ||
| 331 | |||
| 332 | onClicked: { | ||
| 333 | Pipewire.preferredDefaultAudioSource = defaultSourceItem.modelData | ||
| 334 | } | ||
| 335 | |||
| 336 | onWheel: event => scrollVolume(event); | ||
| 337 | property real sensitivity: (1 / 40) / 120 | ||
| 338 | function scrollVolume(event) { | ||
| 339 | defaultSourceItem.modelData.audio.volume += event.angleDelta.y * sensitivity; | ||
| 340 | } | ||
| 341 | |||
| 342 | Rectangle { | ||
| 343 | id: defaultSourceWidget | ||
| 344 | |||
| 345 | anchors.fill: parent | ||
| 346 | color: { | ||
| 347 | if (defaultSourceMouseArea.containsMouse) | ||
| 348 | return "#33808080"; | ||
| 349 | return "transparent"; | ||
| 350 | } | ||
| 351 | |||
| 352 | MaterialDesignIcon { | ||
| 353 | width: 16 | ||
| 354 | height: 16 | ||
| 355 | anchors.centerIn: parent | ||
| 356 | |||
| 357 | icon: { | ||
| 358 | if (defaultSourceItem.modelData?.id == Pipewire.defaultAudioSource?.id) | ||
| 359 | return "microphone"; | ||
| 360 | return "microphone-off"; | ||
| 361 | } | ||
| 362 | color: icon == "microphone" ? "white" : "#555" | ||
| 363 | } | ||
| 364 | } | ||
| 365 | } | ||
| 366 | |||
| 367 | PopupWindow { | ||
| 368 | id: volumeTooltip | ||
| 369 | |||
| 370 | property bool nextVisible: defaultSourceMouseArea.containsMouse || volumeTooltipMouseArea.containsMouse | ||
| 371 | |||
| 372 | anchor { | ||
| 373 | item: defaultSourceMouseArea | ||
| 374 | edges: Edges.Bottom | Edges.Left | ||
| 375 | } | ||
| 376 | visible: false | ||
| 377 | |||
| 378 | onNextVisibleChanged: volumeHangTimer.restart() | ||
| 379 | |||
| 380 | onVisibleChanged: tooltip.openPopup = volumeTooltip.visible | ||
| 381 | |||
| 382 | Timer { | ||
| 383 | id: volumeHangTimer | ||
| 384 | interval: 100 | ||
| 385 | onTriggered: volumeTooltip.visible = volumeTooltip.nextVisible | ||
| 386 | } | ||
| 387 | |||
| 388 | implicitWidth: volumeTooltipText.contentWidth + 16 | ||
| 389 | implicitHeight: volumeTooltipText.contentHeight + 16 | ||
| 390 | color: "black" | ||
| 391 | |||
| 392 | WrapperMouseArea { | ||
| 393 | id: volumeTooltipMouseArea | ||
| 394 | |||
| 395 | hoverEnabled: true | ||
| 396 | enabled: true | ||
| 397 | |||
| 398 | onWheel: event => defaultSourceMouseArea.scrollVolume(event); | ||
| 399 | |||
| 400 | anchors.fill: parent | ||
| 401 | |||
| 402 | Item { | ||
| 403 | anchors.fill: parent | ||
| 404 | |||
| 405 | Text { | ||
| 406 | id: volumeTooltipText | ||
| 407 | |||
| 408 | anchors.centerIn: parent | ||
| 409 | |||
| 410 | font.pointSize: 10 | ||
| 411 | font.family: "Fira Sans" | ||
| 412 | color: "white" | ||
| 413 | |||
| 414 | text: `${Math.round(defaultSourceItem.modelData?.audio?.volume * 100)}%` | ||
| 415 | } | ||
| 416 | } | ||
| 417 | } | ||
| 418 | } | ||
| 419 | } | ||
| 420 | } | ||
| 421 | |||
| 422 | Repeater { | ||
| 423 | id: profileRepeater | ||
| 424 | |||
| 425 | model: Array.from(Pipewire.devices.values).filter(dev => dev.type == "Audio/Device") | ||
| 426 | |||
| 427 | Item { | ||
| 428 | id: profileItem | ||
| 429 | |||
| 430 | required property var modelData | ||
| 431 | required property int index | ||
| 432 | |||
| 433 | PwObjectTracker { | ||
| 434 | objects: [profileItem.modelData] | ||
| 435 | } | ||
| 436 | |||
| 437 | Layout.column: 3 | ||
| 438 | Layout.row: index | ||
| 439 | |||
| 440 | Layout.fillWidth: true | ||
| 441 | |||
| 442 | implicitWidth: Math.max(profileBox.implicitWidth, 300) | ||
| 443 | implicitHeight: profileBox.height | ||
| 444 | |||
| 445 | ComboBox { | ||
| 446 | id: profileBox | ||
| 447 | |||
| 448 | model: profileItem.modelData.profiles | ||
| 449 | |||
| 450 | textRole: "description" | ||
| 451 | valueRole: "index" | ||
| 452 | onActivated: profileItem.modelData.setProfile(currentValue) | ||
| 453 | |||
| 454 | anchors.fill: parent | ||
| 455 | |||
| 456 | implicitContentWidthPolicy: ComboBox.WidestText | ||
| 457 | |||
| 458 | Connections { | ||
| 459 | target: profileItem.modelData | ||
| 460 | function onCurrentProfileChanged() { | ||
| 461 | profileBox.currentIndex = Array.from(profileItem.modelData.profiles).findIndex(profile => profile.index == profileItem.modelData.currentProfile); | ||
| 462 | } | ||
| 463 | } | ||
| 464 | Component.onCompleted: { | ||
| 465 | profileBox.currentIndex = Array.from(profileItem.modelData.profiles).findIndex(profile => profile.index == profileItem.modelData.currentProfile); | ||
| 466 | } | ||
| 467 | |||
| 468 | Connections { | ||
| 469 | target: profileBox.popup | ||
| 470 | function onVisibleChanged() { | ||
| 471 | tooltip.openPopup = profileBox.popup.visible | ||
| 472 | } | ||
| 473 | } | ||
| 474 | } | ||
| 475 | } | ||
| 476 | } | ||
| 477 | } | ||
| 478 | } | ||
| 479 | } | ||
| 480 | } | ||
| 481 | } | ||
| 482 | } | ||
| 483 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/PrivacyWidget.qml b/accounts/gkleen@sif/shell/quickshell/PrivacyWidget.qml new file mode 100644 index 00000000..d7ffadfe --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/PrivacyWidget.qml | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | import QtQuick | ||
| 2 | import QtQuick.Layouts | ||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Widgets | ||
| 5 | import qs.Services | ||
| 6 | |||
| 7 | Item { | ||
| 8 | height: parent.height | ||
| 9 | width: layout.childrenRect.width | ||
| 10 | anchors.verticalCenter: parent.verticalCenter | ||
| 11 | |||
| 12 | visible: Array.from(Privacy.activeItems).length > 0 | ||
| 13 | |||
| 14 | RowLayout { | ||
| 15 | id: layout | ||
| 16 | |||
| 17 | anchors.fill: parent | ||
| 18 | |||
| 19 | spacing: 8 | ||
| 20 | |||
| 21 | Repeater { | ||
| 22 | model: Privacy.activeItems | ||
| 23 | |||
| 24 | Item { | ||
| 25 | id: privacyItem | ||
| 26 | |||
| 27 | required property var modelData; | ||
| 28 | |||
| 29 | height: parent.height | ||
| 30 | width: icon.width | ||
| 31 | |||
| 32 | MaterialDesignIcon { | ||
| 33 | id: icon | ||
| 34 | |||
| 35 | implicitSize: 14 | ||
| 36 | anchors.centerIn: parent | ||
| 37 | |||
| 38 | icon: { | ||
| 39 | if (privacyItem.modelData == Privacy.Item.Microphone) | ||
| 40 | return "microphone"; | ||
| 41 | if (privacyItem.modelData == Privacy.Item.Screensharing) | ||
| 42 | return "monitor-share"; | ||
| 43 | } | ||
| 44 | color: "#f2201f" | ||
| 45 | } | ||
| 46 | } | ||
| 47 | } | ||
| 48 | } | ||
| 49 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/Brightness.qml b/accounts/gkleen@sif/shell/quickshell/Services/Brightness.qml new file mode 100644 index 00000000..8318df50 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/Brightness.qml | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | pragma Singleton | ||
| 2 | |||
| 3 | import QtQml | ||
| 4 | import Quickshell | ||
| 5 | import Quickshell.Io | ||
| 6 | import Custom as Custom | ||
| 7 | |||
| 8 | Singleton { | ||
| 9 | id: root | ||
| 10 | |||
| 11 | property string subsystem: "backlight" | ||
| 12 | property string device: "intel_backlight" | ||
| 13 | |||
| 14 | property real currBrightness | ||
| 15 | property real exponent: 4 | ||
| 16 | |||
| 17 | function calcCurrBrightness() { | ||
| 18 | if (!currFile.loaded || !maxFile.loaded) | ||
| 19 | return undefined; | ||
| 20 | const curr = Number(currFile.text()); | ||
| 21 | const max = Number(maxFile.text()); | ||
| 22 | const val = Math.pow(curr / max, 1 / root.exponent); | ||
| 23 | return val; | ||
| 24 | } | ||
| 25 | |||
| 26 | Connections { | ||
| 27 | target: currFile | ||
| 28 | function onLoaded() { | ||
| 29 | const b = root.calcCurrBrightness(); | ||
| 30 | if (typeof b !== 'undefined') | ||
| 31 | root.currBrightness = b; | ||
| 32 | } | ||
| 33 | } | ||
| 34 | Connections { | ||
| 35 | target: maxFile | ||
| 36 | function onLoaded() { | ||
| 37 | const b = root.calcCurrBrightness(); | ||
| 38 | if (typeof b !== 'undefined') | ||
| 39 | root.currBrightness = b; | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | onCurrBrightnessChanged: { | ||
| 44 | root.currBrightness = Math.max(0, Math.min(1, root.currBrightness)); | ||
| 45 | |||
| 46 | const prev = root.calcCurrBrightness(); | ||
| 47 | if (typeof prev === 'undefined' || Math.abs(root.currBrightness - prev) < 0.01) | ||
| 48 | return; | ||
| 49 | |||
| 50 | const max = Number(maxFile.text()); | ||
| 51 | const actual = Number(currFile.text()); | ||
| 52 | let curr = Math.max(0, Math.min(max, Math.pow(root.currBrightness, root.exponent) * max)); | ||
| 53 | if (Math.round(curr) == actual && curr < actual) | ||
| 54 | curr = Math.max(0, actual - 1); | ||
| 55 | else if (Math.round(curr) == actual && curr > actual) | ||
| 56 | curr = Math.min(max, actual + 1); | ||
| 57 | // root.currBrightness = Math.pow(curr / max, 1 / root.exponent); | ||
| 58 | Custom.Systemd.setBrightness(root.subsystem, root.device, Math.round(curr)); | ||
| 59 | } | ||
| 60 | |||
| 61 | FileView { | ||
| 62 | id: currFile | ||
| 63 | path: `/sys/class/${root.subsystem}/${root.device}/brightness` | ||
| 64 | blockAllReads: true | ||
| 65 | watchChanges: true | ||
| 66 | onFileChanged: reload() | ||
| 67 | } | ||
| 68 | FileView { | ||
| 69 | id: maxFile | ||
| 70 | path: `/sys/class/${root.subsystem}/${root.device}/max_brightness` | ||
| 71 | blockAllReads: true | ||
| 72 | watchChanges: true | ||
| 73 | onFileChanged: reload() | ||
| 74 | } | ||
| 75 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/GpgAgent.qml b/accounts/gkleen@sif/shell/quickshell/Services/GpgAgent.qml new file mode 100644 index 00000000..3de69535 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/GpgAgent.qml | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | pragma Singleton | ||
| 2 | |||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Io | ||
| 5 | |||
| 6 | Singleton { | ||
| 7 | id: root | ||
| 8 | |||
| 9 | Socket { | ||
| 10 | id: agentSocket | ||
| 11 | connected: true | ||
| 12 | path: `${Quickshell.env("XDG_RUNTIME_DIR")}/gnupg/S.gpg-agent` | ||
| 13 | } | ||
| 14 | |||
| 15 | function reloadAgent() { | ||
| 16 | agentSocket.write("RELOADAGENT\n") | ||
| 17 | } | ||
| 18 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/InhibitorState.qml b/accounts/gkleen@sif/shell/quickshell/Services/InhibitorState.qml new file mode 100644 index 00000000..fe48fd7f --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/InhibitorState.qml | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | pragma Singleton | ||
| 2 | |||
| 3 | import Quickshell | ||
| 4 | import Custom as Custom | ||
| 5 | |||
| 6 | Singleton { | ||
| 7 | id: inhibitorState | ||
| 8 | |||
| 9 | property bool waylandIdleInhibited: false | ||
| 10 | property alias lidSwitchInhibited: lidSwitchInhibitor.enabled | ||
| 11 | |||
| 12 | Custom.SystemdInhibitor { | ||
| 13 | id: lidSwitchInhibitor | ||
| 14 | |||
| 15 | enabled: false | ||
| 16 | |||
| 17 | what: Custom.SystemdInhibitorParams.HandleLidSwitch | ||
| 18 | who: "quickshell" | ||
| 19 | why: "User request" | ||
| 20 | mode: Custom.SystemdInhibitorParams.BlockWeak | ||
| 21 | } | ||
| 22 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/MprisProxy.qml b/accounts/gkleen@sif/shell/quickshell/Services/MprisProxy.qml new file mode 100644 index 00000000..e3ab9755 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/MprisProxy.qml | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | pragma Singleton | ||
| 2 | |||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Services.Mpris | ||
| 5 | |||
| 6 | Scope { | ||
| 7 | property list<var> players: Mpris.players.values | ||
| 8 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/NiriService.qml b/accounts/gkleen@sif/shell/quickshell/Services/NiriService.qml new file mode 100644 index 00000000..cce614eb --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/NiriService.qml | |||
| @@ -0,0 +1,194 @@ | |||
| 1 | pragma Singleton | ||
| 2 | |||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Io | ||
| 5 | import QtQuick | ||
| 6 | |||
| 7 | Singleton { | ||
| 8 | id: root | ||
| 9 | |||
| 10 | property var workspaces: [] | ||
| 11 | property var outputs: {} | ||
| 12 | property var keyboardLayouts: {} | ||
| 13 | property var windows: [] | ||
| 14 | readonly property string socketPath: Quickshell.env("NIRI_SOCKET") | ||
| 15 | |||
| 16 | function refreshOutputs() { | ||
| 17 | commandSocket.sendCommand("Outputs", data => { | ||
| 18 | outputs = data.Ok.Outputs; | ||
| 19 | }); | ||
| 20 | } | ||
| 21 | |||
| 22 | function sendCommand(command, callback) { | ||
| 23 | commandSocket.sendCommand(command, callback); | ||
| 24 | } | ||
| 25 | |||
| 26 | Socket { | ||
| 27 | id: eventStreamSocket | ||
| 28 | path: root.socketPath | ||
| 29 | connected: true | ||
| 30 | |||
| 31 | property bool acked: false | ||
| 32 | |||
| 33 | onConnectionStateChanged: { | ||
| 34 | if (connected) { | ||
| 35 | acked = false; | ||
| 36 | write('"EventStream"\n'); | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | parser: SplitParser { | ||
| 41 | onRead: line => { | ||
| 42 | try { | ||
| 43 | const event = JSON.parse(line) | ||
| 44 | |||
| 45 | // console.log(JSON.stringify(event)) | ||
| 46 | |||
| 47 | if (event.WorkspacesChanged) { | ||
| 48 | root.workspaces = event.WorkspacesChanged.workspaces | ||
| 49 | root.refreshOutputs(); | ||
| 50 | } else if (event.WorkspaceActivated) | ||
| 51 | eventWorkspaceActivated(event.WorkspaceActivated); | ||
| 52 | else if (event.WorkspaceUrgencyChanged) | ||
| 53 | eventWorkspaceUrgencyChanged(event.WorkspaceUrgencyChanged); | ||
| 54 | else if (event.WorkspaceActiveWindowChanged) | ||
| 55 | eventWorkspaceActiveWindowChanged(event.WorkspaceActiveWindowChanged); | ||
| 56 | else if (event.KeyboardLayoutsChanged) | ||
| 57 | root.keyboardLayouts = event.KeyboardLayoutsChanged.keyboard_layouts; | ||
| 58 | else if (event.KeyboardLayoutSwitched) | ||
| 59 | root.keyboardLayouts = Object.assign({}, root.keyboardLayouts, {"current_idx": event.KeyboardLayoutSwitched.idx }); | ||
| 60 | else if (event.WindowsChanged) | ||
| 61 | root.windows = event.WindowsChanged.windows | ||
| 62 | else if (event.WindowOpenedOrChanged) | ||
| 63 | eventWindowOpenedOrChanged(event.WindowOpenedOrChanged); | ||
| 64 | else if (event.WindowClosed) | ||
| 65 | eventWindowClosed(event.WindowClosed); | ||
| 66 | else if (event.WindowFocusChanged) | ||
| 67 | eventWindowFocusChanged(event.WindowFocusChanged); | ||
| 68 | else if (event.WindowUrgencyChanged) | ||
| 69 | eventWindowUrgencyChanged(event.WindowUrgencyChanged); | ||
| 70 | else if (event.WindowLayoutsChanged) | ||
| 71 | eventWindowLayoutsChanged(event.WindowLayoutsChanged); | ||
| 72 | else if (event.Ok && !eventStreamSocket.acked) { eventStreamSocket.acked = true; } | ||
| 73 | else if (event.OverviewOpenedOrClosed) {} | ||
| 74 | else if (event.ConfigLoaded) {} | ||
| 75 | else | ||
| 76 | console.log(JSON.stringify(event)); | ||
| 77 | } catch (e) { | ||
| 78 | console.warn("NiriService: Failed to parse event:", line, e) | ||
| 79 | } | ||
| 80 | } | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | Socket { | ||
| 85 | id: commandSocket | ||
| 86 | path: root.socketPath | ||
| 87 | connected: true | ||
| 88 | |||
| 89 | property var awaitingAnswer: null | ||
| 90 | property var cmdQueue: [] | ||
| 91 | |||
| 92 | parser: SplitParser { | ||
| 93 | onRead: line => { | ||
| 94 | if (commandSocket.awaitingAnswer === null) | ||
| 95 | return; | ||
| 96 | |||
| 97 | try { | ||
| 98 | const response = JSON.parse(line); | ||
| 99 | commandSocket.awaitingAnswer.callback(response); | ||
| 100 | commandSocket.awaitingAnswer = null; | ||
| 101 | } catch (e) { | ||
| 102 | console.warn("NiriService: Failed to parse response:", line, e) | ||
| 103 | } | ||
| 104 | commandSocket._handleQueue(); | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | onCmdQueueChanged: { | ||
| 109 | _handleQueue(); | ||
| 110 | } | ||
| 111 | onAwaitingAnswerChanged: { | ||
| 112 | _handleQueue(); | ||
| 113 | } | ||
| 114 | |||
| 115 | function _handleQueue() { | ||
| 116 | if (cmdQueue.length <= 0 || awaitingAnswer !== null) | ||
| 117 | return; | ||
| 118 | |||
| 119 | let localQueue = Array.from(cmdQueue); | ||
| 120 | awaitingAnswer = localQueue.shift(); | ||
| 121 | cmdQueue = localQueue; | ||
| 122 | write(JSON.stringify(awaitingAnswer.command) + '\n'); | ||
| 123 | } | ||
| 124 | |||
| 125 | function sendCommand(command, callback) { | ||
| 126 | cmdQueue = Array.from(cmdQueue).concat([{ "command": command, "callback": callback }]) | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | function eventWorkspaceActivated(data) { | ||
| 131 | let relevant_output = null; | ||
| 132 | Array.from(root.workspaces).forEach(ws => { | ||
| 133 | if (data.id === ws.id) | ||
| 134 | relevant_output = ws.output; | ||
| 135 | }); | ||
| 136 | root.workspaces = Array.from(root.workspaces).map(ws => { | ||
| 137 | if (data.focused) | ||
| 138 | ws.is_focused = false; | ||
| 139 | if (ws.output === relevant_output) | ||
| 140 | ws.is_active = false; | ||
| 141 | if (data.id === ws.id) { | ||
| 142 | ws.is_active = true; | ||
| 143 | ws.is_focused = data.focused; | ||
| 144 | } | ||
| 145 | return ws; | ||
| 146 | }); | ||
| 147 | } | ||
| 148 | function eventWorkspaceUrgencyChanged(data) { | ||
| 149 | root.workspaces = Array.from(root.workspaces).map(ws => { | ||
| 150 | if (data.id == ws.id) | ||
| 151 | ws.is_urgent = data.urgent; | ||
| 152 | return ws; | ||
| 153 | }); | ||
| 154 | } | ||
| 155 | function eventWorkspaceActiveWindowChanged(data) { | ||
| 156 | root.workspaces = Array.from(root.workspaces).map(ws => { | ||
| 157 | if (data.workspace_id === ws.id) | ||
| 158 | ws.active_window_id = data.active_window_id; | ||
| 159 | return ws; | ||
| 160 | }); | ||
| 161 | } | ||
| 162 | function eventWindowOpenedOrChanged(data) { | ||
| 163 | root.windows = Array.from(root.windows).map(win => { | ||
| 164 | if (data.window.is_focused) | ||
| 165 | win.is_focused = false; | ||
| 166 | return win; | ||
| 167 | }).filter(win => win.id !== data.window.id).concat([data.window]); | ||
| 168 | } | ||
| 169 | function eventWindowClosed(data) { | ||
| 170 | root.windows = Array.from(root.windows).filter(win => win.id !== data.id); | ||
| 171 | } | ||
| 172 | function eventWindowFocusChanged(data) { | ||
| 173 | root.windows = Array.from(root.windows).map(win => { | ||
| 174 | win.is_focused = win.id === data.id; | ||
| 175 | return win; | ||
| 176 | }); | ||
| 177 | } | ||
| 178 | function eventWindowUrgencyChanged(data) { | ||
| 179 | root.windows = Array.from(root.windows).map(win => { | ||
| 180 | if (win.id === data.id) | ||
| 181 | win.is_urgent = data.urgent; | ||
| 182 | return win; | ||
| 183 | }); | ||
| 184 | } | ||
| 185 | function eventWindowLayoutsChanged(data) { | ||
| 186 | root.windows = Array.from(root.windows).map(win => { | ||
| 187 | Array.from(data.changes).forEach(change => { | ||
| 188 | if (win.id === change[0]) | ||
| 189 | win.layout = change[1]; | ||
| 190 | }); | ||
| 191 | return win; | ||
| 192 | }); | ||
| 193 | } | ||
| 194 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml b/accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml new file mode 100644 index 00000000..f02d1695 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml | |||
| @@ -0,0 +1,162 @@ | |||
| 1 | pragma Singleton | ||
| 2 | |||
| 3 | import QtQml | ||
| 4 | import Quickshell | ||
| 5 | import Quickshell.Services.Notifications | ||
| 6 | |||
| 7 | Singleton { | ||
| 8 | id: root | ||
| 9 | |||
| 10 | readonly property bool active: !root.lockscreenActive && !root.displayInhibited | ||
| 11 | property bool lockscreenActive: false | ||
| 12 | property bool displayInhibited: false | ||
| 13 | property alias trackedNotifications: server.trackedNotifications | ||
| 14 | readonly property var groups: { | ||
| 15 | function matchesGroupKey(notif, groupKey) { | ||
| 16 | var matches = true; | ||
| 17 | for (const prop in groupKey.test) { | ||
| 18 | if (notif[prop] !== groupKey.test[prop]) { | ||
| 19 | matches = false; | ||
| 20 | break; | ||
| 21 | } | ||
| 22 | } | ||
| 23 | return matches; | ||
| 24 | } | ||
| 25 | |||
| 26 | var groups = new Map(); | ||
| 27 | var notifs = new Array(); | ||
| 28 | for (const [ix, notif] of server.trackedNotifications.values.entries()) { | ||
| 29 | var didGroup = false; | ||
| 30 | for (const groupKey of root.groupKeys) { | ||
| 31 | if (!matchesGroupKey(notif, groupKey)) | ||
| 32 | continue; | ||
| 33 | |||
| 34 | const key = JSON.stringify({ | ||
| 35 | "key": groupKey, | ||
| 36 | "values": Object.assign({}, ...(Array.from(groupKey["group-by"]).map(prop => { | ||
| 37 | var res = {}; | ||
| 38 | res[prop] = notif[prop]; | ||
| 39 | return res; | ||
| 40 | }))) | ||
| 41 | }); | ||
| 42 | if (!groups.has(key)) | ||
| 43 | groups.set(key, new Array()); | ||
| 44 | groups.get(key).push({ "ix": ix, "notif": notif }); | ||
| 45 | didGroup = true; | ||
| 46 | break; | ||
| 47 | } | ||
| 48 | |||
| 49 | if (!didGroup) | ||
| 50 | notifs.push([{ "ix": ix, "notif": notif }]); | ||
| 51 | } | ||
| 52 | notifs.push(...groups.values()); | ||
| 53 | notifs.sort((as, bs) => Math.min(...(as.map(o => o.ix))) - Math.min(...(bs.map(o => o.ix)))); | ||
| 54 | return notifs.map(ns => ns.map(n => n.notif)); | ||
| 55 | } | ||
| 56 | |||
| 57 | property var groupKeys: [ | ||
| 58 | { "test": { "appName": "Element" }, "group-by": [ "summary" ] } | ||
| 59 | ]; | ||
| 60 | |||
| 61 | property int historyLimit: 100 | ||
| 62 | property var history: [] | ||
| 63 | |||
| 64 | Component { | ||
| 65 | id: expirationTimer | ||
| 66 | |||
| 67 | QtObject { | ||
| 68 | id: timer | ||
| 69 | |||
| 70 | required property QtObject parent | ||
| 71 | required property int expirationTime | ||
| 72 | |||
| 73 | property list<QtObject> data: [ | ||
| 74 | Timer { | ||
| 75 | running: root.active && !timer.expired | ||
| 76 | interval: timer.expirationTime | ||
| 77 | onTriggered: { | ||
| 78 | timer.parent.expirationTimer.destroy(); | ||
| 79 | timer.parent.expirationTimer = null; | ||
| 80 | timer.parent.expire(); | ||
| 81 | } | ||
| 82 | } | ||
| 83 | ] | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | Component { | ||
| 88 | id: notificationLock | ||
| 89 | |||
| 90 | RetainableLock {} | ||
| 91 | } | ||
| 92 | |||
| 93 | readonly property SystemClock clock: SystemClock { | ||
| 94 | precision: SystemClock.Minutes | ||
| 95 | } | ||
| 96 | |||
| 97 | function formatTime(time) { | ||
| 98 | const now = root.clock.date; | ||
| 99 | const diff = now - time; | ||
| 100 | const minutes = Math.ceil(diff / 60000); | ||
| 101 | const hours = Math.floor(minutes / 60); | ||
| 102 | |||
| 103 | if (hours < 1) { | ||
| 104 | if (minutes < 1) | ||
| 105 | return "now"; | ||
| 106 | if (minutes == 1) | ||
| 107 | return "1 minute"; | ||
| 108 | return `${minutes} minutes`; | ||
| 109 | } | ||
| 110 | |||
| 111 | const nowDate = new Date(now.getFullYear(), now.getMonth(), now.getDate()) | ||
| 112 | const timeDate = new Date(time.getFullYear(), time.getMonth(), time.getDate()) | ||
| 113 | const days = Math.floor((nowDate - timeDate) / (1000 * 86400)) | ||
| 114 | |||
| 115 | const timeStr = time.toLocaleTimeString(Qt.locale(), "HH:mm"); | ||
| 116 | |||
| 117 | if (days === 0) | ||
| 118 | return timeStr; | ||
| 119 | if (days === 1) | ||
| 120 | return `yesterday ${timeStr}`; | ||
| 121 | |||
| 122 | const dateStr = time.toLocaleTimeString(Qt.locale(), "YYYY-MM-DD"); | ||
| 123 | return `${dateStr} ${timeStr}`; | ||
| 124 | } | ||
| 125 | |||
| 126 | NotificationServer { | ||
| 127 | id: server | ||
| 128 | |||
| 129 | bodySupported: true | ||
| 130 | actionsSupported: true | ||
| 131 | actionIconsSupported: true | ||
| 132 | imageSupported: true | ||
| 133 | bodyMarkupSupported: true | ||
| 134 | bodyImagesSupported: true | ||
| 135 | |||
| 136 | onNotification: notification => { | ||
| 137 | var timeout = notification.expireTimeout * 1000; | ||
| 138 | if (notification.appName == "poweralertd") | ||
| 139 | timeout = 2000; | ||
| 140 | if (timeout > 0) { | ||
| 141 | Object.defineProperty(notification, "expirationTimer", { configurable: true, enumerable: true, writable: true }); | ||
| 142 | notification.expirationTimer = expirationTimer.createObject(notification, { parent: notification, expirationTime: timeout }); | ||
| 143 | } | ||
| 144 | Object.defineProperty(notification, "receivedTime", { configurable: true, enumerable: true, writable: true }); | ||
| 145 | notification.receivedTime = root.clock.date; | ||
| 146 | notification.closed.connect((reason) => server.onNotificationClosed(notification, reason)); | ||
| 147 | notification.tracked = true; | ||
| 148 | } | ||
| 149 | |||
| 150 | function onNotificationClosed(notification, reason) { | ||
| 151 | while (root.history.length >= root.historyLimit) { | ||
| 152 | root.history[0].lock.locked = false; | ||
| 153 | root.history.shift(); | ||
| 154 | } | ||
| 155 | |||
| 156 | root.history.push({ | ||
| 157 | lock: notificationLock.createObject(root, { locked: true, object: notification }), | ||
| 158 | notification: notification | ||
| 159 | }); | ||
| 160 | } | ||
| 161 | } | ||
| 162 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/Privacy.qml b/accounts/gkleen@sif/shell/quickshell/Services/Privacy.qml new file mode 100644 index 00000000..9c813e49 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/Privacy.qml | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | pragma Singleton | ||
| 2 | |||
| 3 | import QtQml | ||
| 4 | import Quickshell | ||
| 5 | import Quickshell.Services.Pipewire | ||
| 6 | |||
| 7 | Singleton { | ||
| 8 | id: root | ||
| 9 | |||
| 10 | PwObjectTracker { | ||
| 11 | objects: Pipewire.nodes.values | ||
| 12 | } | ||
| 13 | |||
| 14 | enum Item { | ||
| 15 | Microphone, | ||
| 16 | Screensharing | ||
| 17 | } | ||
| 18 | |||
| 19 | readonly property list<var> activeItems: { | ||
| 20 | var items = []; | ||
| 21 | if (microphoneActive) | ||
| 22 | items.push(Privacy.Item.Microphone); | ||
| 23 | if (screensharingActive) | ||
| 24 | items.push(Privacy.Item.Screensharing); | ||
| 25 | return items; | ||
| 26 | } | ||
| 27 | |||
| 28 | readonly property bool microphoneActive: { | ||
| 29 | if (!Pipewire.ready || !Pipewire.nodes?.values) { | ||
| 30 | return false | ||
| 31 | } | ||
| 32 | |||
| 33 | for (const node of Pipewire.nodes.values) { | ||
| 34 | if (!node || (node.type & PwNodeType.AudioInStream) != PwNodeType.AudioInStream) | ||
| 35 | continue; | ||
| 36 | |||
| 37 | if (node.properties?.["stream.monitor"] === "true") | ||
| 38 | continue; | ||
| 39 | |||
| 40 | if (node.audio?.muted) | ||
| 41 | continue; | ||
| 42 | |||
| 43 | return true; | ||
| 44 | } | ||
| 45 | |||
| 46 | return false; | ||
| 47 | } | ||
| 48 | |||
| 49 | readonly property bool screensharingActive: { | ||
| 50 | if (!Pipewire.ready || !Pipewire.nodes?.values) { | ||
| 51 | return false | ||
| 52 | } | ||
| 53 | |||
| 54 | for (const node of Pipewire.nodes.values) { | ||
| 55 | if (!node || (node.type & PwNodeType.VideoInStream) != PwNodeType.VideoInStream) | ||
| 56 | continue; | ||
| 57 | |||
| 58 | return true; | ||
| 59 | } | ||
| 60 | |||
| 61 | return false; | ||
| 62 | } | ||
| 63 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/WallpaperSelector.qml b/accounts/gkleen@sif/shell/quickshell/Services/WallpaperSelector.qml new file mode 100644 index 00000000..3c524955 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/WallpaperSelector.qml | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | import Custom as Custom | ||
| 2 | |||
| 3 | Custom.FileSelector { | ||
| 4 | id: root | ||
| 5 | |||
| 6 | directory: @wallpapers@ | ||
| 7 | epoch: 72000000 | ||
| 8 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/SystemTray.qml b/accounts/gkleen@sif/shell/quickshell/SystemTray.qml new file mode 100644 index 00000000..f7b4ed96 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/SystemTray.qml | |||
| @@ -0,0 +1,201 @@ | |||
| 1 | import QtQuick | ||
| 2 | import QtQuick.Effects | ||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Widgets | ||
| 5 | import Quickshell.Services.SystemTray | ||
| 6 | |||
| 7 | Item { | ||
| 8 | anchors.verticalCenter: parent.verticalCenter | ||
| 9 | width: systemTrayRow.childrenRect.width | ||
| 10 | height: parent.height | ||
| 11 | clip: true | ||
| 12 | |||
| 13 | Row { | ||
| 14 | id: systemTrayRow | ||
| 15 | anchors.centerIn: parent | ||
| 16 | width: childrenRect.width | ||
| 17 | height: parent.height | ||
| 18 | spacing: 0 | ||
| 19 | |||
| 20 | Repeater { | ||
| 21 | model: ScriptModel { | ||
| 22 | values: { | ||
| 23 | var trayItems = Array.from(SystemTray.items.values).filter(item => item.status !== Status.Passive); | ||
| 24 | trayItems.sort((a, b) => a.category !== b.category ? b.category - a.category : a.id.localeCompare(b.id)) | ||
| 25 | return trayItems; | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | delegate: Item { | ||
| 30 | id: trayItemWrapper | ||
| 31 | |||
| 32 | required property var modelData | ||
| 33 | required property int index | ||
| 34 | |||
| 35 | property var trayItem: modelData | ||
| 36 | property string iconSource: { | ||
| 37 | let icon = trayItem && trayItem.icon | ||
| 38 | if (typeof icon === 'string' || icon instanceof String) { | ||
| 39 | if (icon.includes("?path=")) { | ||
| 40 | const split = icon.split("?path=") | ||
| 41 | if (split.length !== 2) | ||
| 42 | return icon | ||
| 43 | const name = split[0] | ||
| 44 | const path = split[1] | ||
| 45 | const fileName = name.substring( | ||
| 46 | name.lastIndexOf("/") + 1) | ||
| 47 | return `file://${path}/${fileName}` | ||
| 48 | } | ||
| 49 | return icon | ||
| 50 | } | ||
| 51 | return "" | ||
| 52 | } | ||
| 53 | |||
| 54 | width: icon.width + 6 | ||
| 55 | height: parent.height | ||
| 56 | anchors.verticalCenter: parent.verticalCenter | ||
| 57 | |||
| 58 | WrapperMouseArea { | ||
| 59 | id: trayItemArea | ||
| 60 | |||
| 61 | anchors.fill: parent | ||
| 62 | acceptedButtons: Qt.LeftButton | Qt.RightButton | ||
| 63 | hoverEnabled: true | ||
| 64 | cursorShape: trayItem.onlyMenu ? Qt.ArrowCursor : Qt.PointingHandCursor | ||
| 65 | onClicked: mouse => { | ||
| 66 | if (!trayItem) | ||
| 67 | return | ||
| 68 | |||
| 69 | if (mouse.button === Qt.LeftButton | ||
| 70 | && !trayItem.onlyMenu) { | ||
| 71 | trayItem.activate() | ||
| 72 | return | ||
| 73 | } | ||
| 74 | |||
| 75 | if (trayItem.hasMenu) { | ||
| 76 | var globalPos = mapToGlobal(0, 0) | ||
| 77 | var currentScreen = screen || Screen | ||
| 78 | var screenX = currentScreen.x || 0 | ||
| 79 | var relativeX = globalPos.x - screenX | ||
| 80 | menuAnchor.menu = trayItem.menu | ||
| 81 | menuAnchor.anchor.window = bar | ||
| 82 | menuAnchor.anchor.rect = Qt.rect( | ||
| 83 | relativeX, | ||
| 84 | 21, | ||
| 85 | parent.width, 1) | ||
| 86 | menuAnchor.open() | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | Rectangle { | ||
| 91 | anchors.fill: parent | ||
| 92 | color: { | ||
| 93 | if (trayItemArea.containsMouse) | ||
| 94 | return "#33808080"; | ||
| 95 | return "transparent"; | ||
| 96 | } | ||
| 97 | |||
| 98 | Item { | ||
| 99 | anchors.fill: parent | ||
| 100 | |||
| 101 | layer.enabled: true | ||
| 102 | layer.effect: MultiEffect { | ||
| 103 | colorization: 1 | ||
| 104 | colorizationColor: "#555" | ||
| 105 | } | ||
| 106 | |||
| 107 | IconImage { | ||
| 108 | id: icon | ||
| 109 | |||
| 110 | anchors.centerIn: parent | ||
| 111 | implicitSize: 16 | ||
| 112 | source: trayItemWrapper.iconSource | ||
| 113 | asynchronous: true | ||
| 114 | smooth: true | ||
| 115 | mipmap: true | ||
| 116 | |||
| 117 | layer.enabled: true | ||
| 118 | layer.effect: MultiEffect { | ||
| 119 | id: effect | ||
| 120 | |||
| 121 | brightness: 1 | ||
| 122 | } | ||
| 123 | } | ||
| 124 | } | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | PopupWindow { | ||
| 129 | id: tooltip | ||
| 130 | |||
| 131 | property bool nextVisible: (trayItem.tooltipTitle || trayItem.tooltipDescription) && (trayItemArea.containsMouse || tooltipMouseArea.containsMouse) && !menuAnchor.visible | ||
| 132 | |||
| 133 | anchor { | ||
| 134 | item: trayItemArea | ||
| 135 | edges: Edges.Bottom | ||
| 136 | } | ||
| 137 | |||
| 138 | visible: false | ||
| 139 | onNextVisibleChanged: hangTimer.restart() | ||
| 140 | |||
| 141 | Timer { | ||
| 142 | id: hangTimer | ||
| 143 | interval: 100 | ||
| 144 | onTriggered: tooltip.visible = tooltip.nextVisible | ||
| 145 | } | ||
| 146 | |||
| 147 | color: "black" | ||
| 148 | |||
| 149 | implicitWidth: Math.max(tooltipTitle.contentWidth, tooltipDescription.contentWidth) + 16 | ||
| 150 | implicitHeight: (trayItem.tooltipTitle ? tooltipTitle.contentHeight : 0) + (trayItem.tooltipDescription ? tooltipDescription.contentHeight : 0) + 16 | ||
| 151 | |||
| 152 | WrapperMouseArea { | ||
| 153 | id: tooltipMouseArea | ||
| 154 | |||
| 155 | hoverEnabled: true | ||
| 156 | enabled: true | ||
| 157 | |||
| 158 | margin: 4 | ||
| 159 | |||
| 160 | anchors.fill: parent | ||
| 161 | |||
| 162 | Item { | ||
| 163 | anchors.fill: parent | ||
| 164 | |||
| 165 | Column { | ||
| 166 | anchors.centerIn: parent | ||
| 167 | Text { | ||
| 168 | id: tooltipTitle | ||
| 169 | |||
| 170 | enabled: trayItem.tooltipTitle | ||
| 171 | |||
| 172 | font.pointSize: 10 | ||
| 173 | font.family: "Fira Sans" | ||
| 174 | font.bold: true | ||
| 175 | color: "white" | ||
| 176 | |||
| 177 | text: trayItem.tooltipTitle | ||
| 178 | } | ||
| 179 | |||
| 180 | Text { | ||
| 181 | id: tooltipDescription | ||
| 182 | |||
| 183 | enabled: trayItem.tooltipDescription | ||
| 184 | |||
| 185 | font.pointSize: 10 | ||
| 186 | font.family: "Fira Sans" | ||
| 187 | color: "white" | ||
| 188 | |||
| 189 | text: trayItem.tooltipDescription | ||
| 190 | } | ||
| 191 | } | ||
| 192 | } | ||
| 193 | } | ||
| 194 | } | ||
| 195 | } | ||
| 196 | } | ||
| 197 | } | ||
| 198 | QsMenuAnchor { | ||
| 199 | id: menuAnchor | ||
| 200 | } | ||
| 201 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/UnixIPC.qml b/accounts/gkleen@sif/shell/quickshell/UnixIPC.qml new file mode 100644 index 00000000..05a40dbc --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/UnixIPC.qml | |||
| @@ -0,0 +1,97 @@ | |||
| 1 | import Quickshell | ||
| 2 | import Quickshell.Io | ||
| 3 | import Quickshell.Services.Pipewire | ||
| 4 | import Quickshell.Services.Mpris | ||
| 5 | import qs.Services | ||
| 6 | import Custom as Custom | ||
| 7 | import QtQml | ||
| 8 | |||
| 9 | Scope { | ||
| 10 | id: root | ||
| 11 | |||
| 12 | SocketServer { | ||
| 13 | active: true | ||
| 14 | path: `${Quickshell.env("XDG_RUNTIME_DIR")}/shell.sock` | ||
| 15 | handler: Socket { | ||
| 16 | parser: SplitParser { | ||
| 17 | onRead: line => { | ||
| 18 | const command = (() => { | ||
| 19 | try { | ||
| 20 | return JSON.parse(line); | ||
| 21 | } catch (e) { | ||
| 22 | console.warn("UnixIPC: Failed to parse command:", line, e); | ||
| 23 | } | ||
| 24 | })(); | ||
| 25 | if (!command) | ||
| 26 | return; | ||
| 27 | |||
| 28 | if (command.Volume) | ||
| 29 | root.onCommandVolume(command.Volume); | ||
| 30 | else if (command.Brightness) | ||
| 31 | root.onCommandBrightness(command.Brightness); | ||
| 32 | else if (command.LockSession) | ||
| 33 | Custom.Systemd.lockSession(); | ||
| 34 | else if (command.Suspend) | ||
| 35 | Custom.Systemd.suspend(); | ||
| 36 | else if (command.Hibernate) | ||
| 37 | Custom.Systemd.hibernate(); | ||
| 38 | else if (command.Mpris) | ||
| 39 | root.onCommandMpris(command.Mpris); | ||
| 40 | else if (command.Notifications) | ||
| 41 | root.onCommandNotifications(command.Notifications); | ||
| 42 | else | ||
| 43 | console.warn("UnixIPC: Command not handled:", JSON.stringify(command)); | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | onError: e => { | ||
| 48 | if (e == 1) | ||
| 49 | return; | ||
| 50 | console.warn("QLocalSocket::LocalSocketError", e); | ||
| 51 | } | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | PwObjectTracker { | ||
| 56 | objects: [ Pipewire.defaultAudioSink, Pipewire.defaultAudioSource ] | ||
| 57 | } | ||
| 58 | function onCommandVolume(command) { | ||
| 59 | if (command.muted === "toggle") | ||
| 60 | Pipewire.defaultAudioSink.audio.muted = !Pipewire.defaultAudioSink.audio.muted; | ||
| 61 | if (command.volume === "up") | ||
| 62 | Pipewire.defaultAudioSink.audio.volume += 0.02; | ||
| 63 | if (command.volume === "down") | ||
| 64 | Pipewire.defaultAudioSink.audio.volume -= 0.02; | ||
| 65 | |||
| 66 | if (command["mic-muted"] === "toggle") | ||
| 67 | Pipewire.defaultAudioSource.audio.muted = !Pipewire.defaultAudioSource.audio.muted; | ||
| 68 | } | ||
| 69 | |||
| 70 | function onCommandBrightness(command) { | ||
| 71 | if (command === "up") | ||
| 72 | Brightness.currBrightness += 0.02 | ||
| 73 | if (command === "down") | ||
| 74 | Brightness.currBrightness -= 0.02 | ||
| 75 | } | ||
| 76 | |||
| 77 | function onCommandMpris(command) { | ||
| 78 | if (command.PauseAll) | ||
| 79 | Array.from(MprisProxy.players).forEach(player => { | ||
| 80 | if (player.canPause && player.isPlaying) | ||
| 81 | player.pause(); | ||
| 82 | }); | ||
| 83 | } | ||
| 84 | Component.onCompleted: { (_ => {})(MprisProxy.players); } | ||
| 85 | |||
| 86 | function onCommandNotifications(command) { | ||
| 87 | if (command.DismissGroup && NotificationManager.active) { | ||
| 88 | if (NotificationManager.groups.length > 0) | ||
| 89 | for (const notif of [...NotificationManager.groups[0]]) | ||
| 90 | notif.dismiss(); | ||
| 91 | } | ||
| 92 | if (command.DismissAll && NotificationManager.active) { | ||
| 93 | for (const notif of [...NotificationManager.trackedNotifications.values]) | ||
| 94 | notif.dismiss(); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/VolumeOSD.qml b/accounts/gkleen@sif/shell/quickshell/VolumeOSD.qml new file mode 100644 index 00000000..653f4763 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/VolumeOSD.qml | |||
| @@ -0,0 +1,163 @@ | |||
| 1 | import QtQuick | ||
| 2 | import QtQuick.Layouts | ||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Services.Pipewire | ||
| 5 | import Quickshell.Widgets | ||
| 6 | |||
| 7 | Scope { | ||
| 8 | id: root | ||
| 9 | |||
| 10 | property string show: "" | ||
| 11 | property bool inhibited: true | ||
| 12 | |||
| 13 | PwObjectTracker { | ||
| 14 | objects: [ Pipewire.defaultAudioSink, Pipewire.defaultAudioSource ] | ||
| 15 | } | ||
| 16 | |||
| 17 | Connections { | ||
| 18 | enabled: Pipewire.defaultAudioSink | ||
| 19 | target: Pipewire.defaultAudioSink?.audio | ||
| 20 | |||
| 21 | function onVolumeChanged() { | ||
| 22 | root.show = "sink"; | ||
| 23 | hideTimer.restart(); | ||
| 24 | } | ||
| 25 | function onMutedChanged() { | ||
| 26 | root.show = "sink"; | ||
| 27 | hideTimer.restart(); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | Connections { | ||
| 32 | enabled: Pipewire.defaultAudioSource | ||
| 33 | target: Pipewire.defaultAudioSource?.audio | ||
| 34 | |||
| 35 | function onVolumeChanged() { | ||
| 36 | root.show = "source"; | ||
| 37 | hideTimer.restart(); | ||
| 38 | } | ||
| 39 | function onMutedChanged() { | ||
| 40 | root.show = "source"; | ||
| 41 | hideTimer.restart(); | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | onShowChanged: { | ||
| 46 | if (show) | ||
| 47 | hideTimer.restart(); | ||
| 48 | } | ||
| 49 | |||
| 50 | Timer { | ||
| 51 | id: hideTimer | ||
| 52 | interval: 1000 | ||
| 53 | onTriggered: root.show = "" | ||
| 54 | } | ||
| 55 | |||
| 56 | Timer { | ||
| 57 | id: startInhibit | ||
| 58 | interval: 100 | ||
| 59 | running: true | ||
| 60 | onTriggered: { | ||
| 61 | root.show = ""; | ||
| 62 | root.inhibited = false; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | LazyLoader { | ||
| 67 | active: root.show && !root.inhibited | ||
| 68 | |||
| 69 | Variants { | ||
| 70 | model: Quickshell.screens | ||
| 71 | |||
| 72 | delegate: Scope { | ||
| 73 | id: screenScope | ||
| 74 | |||
| 75 | required property var modelData | ||
| 76 | |||
| 77 | PanelWindow { | ||
| 78 | id: window | ||
| 79 | |||
| 80 | screen: screenScope.modelData | ||
| 81 | |||
| 82 | anchors.top: true | ||
| 83 | margins.top: screen.height / 2 - 50 + 3.5 | ||
| 84 | exclusiveZone: 0 | ||
| 85 | exclusionMode: ExclusionMode.Ignore | ||
| 86 | |||
| 87 | implicitWidth: 400 | ||
| 88 | implicitHeight: 50 | ||
| 89 | |||
| 90 | mask: Region {} | ||
| 91 | |||
| 92 | color: "transparent" | ||
| 93 | |||
| 94 | Rectangle { | ||
| 95 | anchors.fill: parent | ||
| 96 | color: Qt.rgba(0, 0, 0, 0.75) | ||
| 97 | } | ||
| 98 | |||
| 99 | RowLayout { | ||
| 100 | id: layout | ||
| 101 | |||
| 102 | anchors.centerIn: parent | ||
| 103 | |||
| 104 | height: 50 - 8*2 | ||
| 105 | width: 400 - 8*2 | ||
| 106 | |||
| 107 | MaterialDesignIcon { | ||
| 108 | id: volumeIcon | ||
| 109 | |||
| 110 | implicitWidth: parent.height | ||
| 111 | implicitHeight: parent.height | ||
| 112 | |||
| 113 | icon: { | ||
| 114 | if (root.show == "sink") { | ||
| 115 | if (!Pipewire.defaultAudioSink || Pipewire.defaultAudioSink.audio.muted) | ||
| 116 | return "volume-off"; | ||
| 117 | if (Pipewire.defaultAudioSink.audio.volume <= 0.33) | ||
| 118 | return "volume-low"; | ||
| 119 | if (Pipewire.defaultAudioSink.audio.volume <= 0.67) | ||
| 120 | return "volume-medium"; | ||
| 121 | return "volume-high"; | ||
| 122 | } else if (root.show == "source") { | ||
| 123 | if (!Pipewire.defaultAudioSource || Pipewire.defaultAudioSource.audio.muted) | ||
| 124 | return "microphone-off"; | ||
| 125 | if (Pipewire.defaultAudioSource.audio.volume > 1) | ||
| 126 | return "microphone-plus"; | ||
| 127 | return "microphone"; | ||
| 128 | } | ||
| 129 | return "volume-high"; | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | Rectangle { | ||
| 134 | Layout.fillWidth: true | ||
| 135 | |||
| 136 | implicitHeight: 10 | ||
| 137 | |||
| 138 | color: "#50ffffff" | ||
| 139 | |||
| 140 | Rectangle { | ||
| 141 | anchors { | ||
| 142 | left: parent.left | ||
| 143 | top: parent.top | ||
| 144 | bottom: parent.bottom | ||
| 145 | } | ||
| 146 | |||
| 147 | color: Pipewire.defaultAudioSink?.audio.muted ? "#70ffffff" : "white" | ||
| 148 | |||
| 149 | implicitWidth: { | ||
| 150 | if (root.show == "sink") | ||
| 151 | return parent.width * (Pipewire.defaultAudioSink?.audio.volume ?? 0); | ||
| 152 | else if (root.show == "source") | ||
| 153 | return parent.width * Math.min(1, (Pipewire.defaultAudioSource?.audio.volume ?? 0)); | ||
| 154 | return 0; | ||
| 155 | } | ||
| 156 | } | ||
| 157 | } | ||
| 158 | } | ||
| 159 | } | ||
| 160 | } | ||
| 161 | } | ||
| 162 | } | ||
| 163 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/WallpaperBackground.qml b/accounts/gkleen@sif/shell/quickshell/WallpaperBackground.qml new file mode 100644 index 00000000..4f85a900 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/WallpaperBackground.qml | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | import QtQuick | ||
| 2 | import Quickshell | ||
| 3 | import qs.Services | ||
| 4 | |||
| 5 | Item { | ||
| 6 | id: root | ||
| 7 | |||
| 8 | anchors.fill: parent | ||
| 9 | |||
| 10 | required property string screen | ||
| 11 | |||
| 12 | property Img current: one | ||
| 13 | property string source: selector.selected | ||
| 14 | |||
| 15 | WallpaperSelector { | ||
| 16 | id: selector | ||
| 17 | seed: screen | ||
| 18 | } | ||
| 19 | |||
| 20 | onSourceChanged: { | ||
| 21 | if (!source) | ||
| 22 | current = null; | ||
| 23 | else if (current === one) | ||
| 24 | two.update() | ||
| 25 | else | ||
| 26 | one.update() | ||
| 27 | } | ||
| 28 | |||
| 29 | Img { id: one } | ||
| 30 | Img { id: two } | ||
| 31 | |||
| 32 | component Img: Image { | ||
| 33 | id: img | ||
| 34 | |||
| 35 | function update() { | ||
| 36 | source = root.source || "" | ||
| 37 | } | ||
| 38 | |||
| 39 | anchors.fill: parent | ||
| 40 | fillMode: Image.PreserveAspectCrop | ||
| 41 | smooth: true | ||
| 42 | asynchronous: true | ||
| 43 | cache: false | ||
| 44 | |||
| 45 | opacity: 0 | ||
| 46 | |||
| 47 | onStatusChanged: { | ||
| 48 | if (status === Image.Ready) { | ||
| 49 | root.current = this | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | states: State { | ||
| 54 | name: "visible" | ||
| 55 | when: root.current === img | ||
| 56 | |||
| 57 | PropertyChanges { | ||
| 58 | img.opacity: 1 | ||
| 59 | } | ||
| 60 | StateChangeScript { | ||
| 61 | name: "unloadOther" | ||
| 62 | script: { | ||
| 63 | if (img === one) | ||
| 64 | two.source = "" | ||
| 65 | if (img === two) | ||
| 66 | one.source = "" | ||
| 67 | } | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | transitions: Transition { | ||
| 72 | SequentialAnimation { | ||
| 73 | NumberAnimation { | ||
| 74 | target: img | ||
| 75 | properties: "opacity" | ||
| 76 | duration: { | ||
| 77 | if (img === one && two.source == "" || img === two && one.source == "") | ||
| 78 | return 0; | ||
| 79 | return 5000; | ||
| 80 | } | ||
| 81 | easing.type: Easing.OutCubic | ||
| 82 | } | ||
| 83 | ScriptAction { | ||
| 84 | scriptName: "unloadOther" | ||
| 85 | } | ||
| 86 | } | ||
| 87 | } | ||
| 88 | } | ||
| 89 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/WaylandInhibitorWidget.qml b/accounts/gkleen@sif/shell/quickshell/WaylandInhibitorWidget.qml new file mode 100644 index 00000000..0512ff51 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/WaylandInhibitorWidget.qml | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | import Quickshell | ||
| 2 | import QtQuick | ||
| 3 | import Quickshell.Widgets | ||
| 4 | import Quickshell.Wayland | ||
| 5 | import qs.Services | ||
| 6 | |||
| 7 | Item { | ||
| 8 | id: root | ||
| 9 | |||
| 10 | required property var window | ||
| 11 | |||
| 12 | width: icon.width + 8 | ||
| 13 | height: parent.height | ||
| 14 | anchors.verticalCenter: parent.verticalCenter | ||
| 15 | |||
| 16 | IdleInhibitor { | ||
| 17 | id: inhibitor | ||
| 18 | enabled: InhibitorState.waylandIdleInhibited | ||
| 19 | window: root.window | ||
| 20 | } | ||
| 21 | |||
| 22 | WrapperMouseArea { | ||
| 23 | id: widgetMouseArea | ||
| 24 | |||
| 25 | anchors.fill: parent | ||
| 26 | |||
| 27 | hoverEnabled: true | ||
| 28 | cursorShape: Qt.PointingHandCursor | ||
| 29 | |||
| 30 | onClicked: InhibitorState.waylandIdleInhibited = !InhibitorState.waylandIdleInhibited | ||
| 31 | |||
| 32 | Rectangle { | ||
| 33 | anchors.fill: parent | ||
| 34 | color: { | ||
| 35 | if (widgetMouseArea.containsMouse) { | ||
| 36 | return "#33808080"; | ||
| 37 | } | ||
| 38 | return "transparent"; | ||
| 39 | } | ||
| 40 | |||
| 41 | Item { | ||
| 42 | anchors.fill: parent | ||
| 43 | |||
| 44 | MaterialDesignIcon { | ||
| 45 | id: icon | ||
| 46 | |||
| 47 | implicitSize: 14 | ||
| 48 | anchors.centerIn: parent | ||
| 49 | |||
| 50 | icon: inhibitor.enabled ? "eye" : "eye-off" | ||
| 51 | color: inhibitor.enabled ? "white" : "#555" | ||
| 52 | } | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/WorkspaceSwitcher.qml b/accounts/gkleen@sif/shell/quickshell/WorkspaceSwitcher.qml new file mode 100644 index 00000000..3ae94346 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/WorkspaceSwitcher.qml | |||
| @@ -0,0 +1,204 @@ | |||
| 1 | import Quickshell | ||
| 2 | import QtQuick | ||
| 3 | import qs.Services | ||
| 4 | import Quickshell.Widgets | ||
| 5 | import QtQuick.Layouts | ||
| 6 | |||
| 7 | Row { | ||
| 8 | id: workspaces | ||
| 9 | |||
| 10 | required property var screen | ||
| 11 | |||
| 12 | property var ignoreWorkspaces: @ignore_workspaces@ | ||
| 13 | |||
| 14 | height: parent.height | ||
| 15 | anchors.verticalCenter: parent.verticalCenter | ||
| 16 | spacing: 0 | ||
| 17 | |||
| 18 | Repeater { | ||
| 19 | model: ScriptModel { | ||
| 20 | values: { | ||
| 21 | let currWorkspaces = NiriService.workspaces; | ||
| 22 | const ignoreWorkspaces = Array.from(workspaces.ignoreWorkspaces); | ||
| 23 | currWorkspaces = currWorkspaces.filter(ws => ws.output == workspaces.screen.name).filter(ws => ws.is_active || ignoreWorkspaces.every(iws => iws !== ws.name)); | ||
| 24 | currWorkspaces.sort((a, b) => { | ||
| 25 | if (NiriService.outputs?.[a.output]?.logical?.x !== NiriService.outputs?.[b.output]?.logical?.x) | ||
| 26 | return NiriService.outputs?.[a.output]?.logical?.x - NiriService.outputs?.[b.output]?.logical?.x | ||
| 27 | if (NiriService.outputs?.[a.output]?.logical?.y !== NiriService.outputs?.[b.output]?.logical?.y) | ||
| 28 | return NiriService.outputs?.[a.output]?.logical?.y - NiriService.outputs?.[b.output]?.logical?.y | ||
| 29 | return a.idx - b.idx; | ||
| 30 | }); | ||
| 31 | return currWorkspaces; | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | Item { | ||
| 36 | id: wsItem | ||
| 37 | |||
| 38 | property var workspaceData: modelData | ||
| 39 | |||
| 40 | width: wsLabel.contentWidth + 8 | ||
| 41 | height: parent.height | ||
| 42 | anchors.verticalCenter: parent.verticalCenter | ||
| 43 | |||
| 44 | WrapperMouseArea { | ||
| 45 | id: mouseArea | ||
| 46 | |||
| 47 | anchors.fill: parent | ||
| 48 | |||
| 49 | hoverEnabled: true | ||
| 50 | cursorShape: Qt.PointingHandCursor | ||
| 51 | enabled: true | ||
| 52 | onClicked: { | ||
| 53 | NiriService.sendCommand({ "Action": { "FocusWorkspace": { "reference": { "Id": workspaceData.id } } } }, _ => {}); | ||
| 54 | } | ||
| 55 | |||
| 56 | Rectangle { | ||
| 57 | anchors.fill: parent | ||
| 58 | |||
| 59 | color: { | ||
| 60 | if (mouseArea.containsMouse) { | ||
| 61 | return "#33808080"; | ||
| 62 | } | ||
| 63 | return "transparent"; | ||
| 64 | } | ||
| 65 | |||
| 66 | Text { | ||
| 67 | id: wsLabel | ||
| 68 | |||
| 69 | anchors.centerIn: parent | ||
| 70 | |||
| 71 | font.pointSize: 10 | ||
| 72 | font.family: "Fira Sans" | ||
| 73 | color: { | ||
| 74 | if (workspaceData.is_active) | ||
| 75 | return "#23fd00"; | ||
| 76 | if (workspaceData.active_window_id === null) | ||
| 77 | return "#555"; | ||
| 78 | return "white"; | ||
| 79 | } | ||
| 80 | |||
| 81 | text: workspaceData.name ? workspaceData.name : workspaceData.idx | ||
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | PopupWindow { | ||
| 87 | id: tooltip | ||
| 88 | |||
| 89 | property bool nextVisible: (mouseArea.containsMouse || tooltipMouseArea.containsMouse) && [...windowsModel.values].length > 0 | ||
| 90 | |||
| 91 | anchor { | ||
| 92 | item: mouseArea | ||
| 93 | edges: Edges.Bottom | Edges.Left | ||
| 94 | } | ||
| 95 | visible: false | ||
| 96 | |||
| 97 | onNextVisibleChanged: hangTimer.restart() | ||
| 98 | |||
| 99 | Timer { | ||
| 100 | id: hangTimer | ||
| 101 | interval: 100 | ||
| 102 | onTriggered: tooltip.visible = tooltip.nextVisible | ||
| 103 | } | ||
| 104 | |||
| 105 | implicitWidth: tooltipContent.implicitWidth | ||
| 106 | implicitHeight: tooltipContent.implicitHeight | ||
| 107 | color: "black" | ||
| 108 | |||
| 109 | WrapperMouseArea { | ||
| 110 | id: tooltipMouseArea | ||
| 111 | |||
| 112 | hoverEnabled: true | ||
| 113 | enabled: true | ||
| 114 | |||
| 115 | anchors.fill: parent | ||
| 116 | |||
| 117 | WrapperItem { | ||
| 118 | id: tooltipContent | ||
| 119 | |||
| 120 | margin: 0 | ||
| 121 | |||
| 122 | ColumnLayout { | ||
| 123 | spacing: 0 | ||
| 124 | |||
| 125 | Repeater { | ||
| 126 | model: ScriptModel { | ||
| 127 | id: windowsModel | ||
| 128 | |||
| 129 | values: { | ||
| 130 | let currWindows = Array.from(NiriService.windows).filter(win => win.workspace_id == wsItem.workspaceData.id); | ||
| 131 | currWindows.sort((a, b) => { | ||
| 132 | if (a.is_floating !== b.is_floating) | ||
| 133 | return b.is_floating - a.is_floating; | ||
| 134 | if (a.layout.tile_pos_in_workspace_view?.[0] !== b.layout.tile_pos_in_workspace_view?.[0]) | ||
| 135 | return a.layout.tile_pos_in_workspace_view?.[0] - b.layout.tile_pos_in_workspace_view?.[0] | ||
| 136 | if (a.layout.tile_pos_in_workspace_view?.[1] !== b.layout.tile_pos_in_workspace_view?.[1]) | ||
| 137 | return a.layout.tile_pos_in_workspace_view?.[1] - b.layout.tile_pos_in_workspace_view?.[1] | ||
| 138 | if (a.layout.pos_in_scrolling_layout?.[0] !== b.layout.pos_in_scrolling_layout?.[0]) | ||
| 139 | return a.layout.pos_in_scrolling_layout?.[0] - b.layout.pos_in_scrolling_layout?.[0] | ||
| 140 | if (a.layout.pos_in_scrolling_layout?.[1] !== b.layout.pos_in_scrolling_layout?.[1]) | ||
| 141 | return a.layout.pos_in_scrolling_layout?.[1] - b.layout.pos_in_scrolling_layout?.[1] | ||
| 142 | if (a.app_id !== b.app_id) | ||
| 143 | return a.app_id.localeCompare(b.app_id); | ||
| 144 | |||
| 145 | return a.title.localeCompare(b.title); | ||
| 146 | }); | ||
| 147 | return currWindows; | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | WrapperMouseArea { | ||
| 152 | id: windowMouseArea | ||
| 153 | |||
| 154 | required property int index | ||
| 155 | required property var modelData | ||
| 156 | property var windowData: modelData | ||
| 157 | |||
| 158 | hoverEnabled: true | ||
| 159 | cursorShape: Qt.PointingHandCursor | ||
| 160 | enabled: true | ||
| 161 | |||
| 162 | Layout.fillWidth: true | ||
| 163 | |||
| 164 | onClicked: { | ||
| 165 | NiriService.sendCommand({ "Action": { "FocusWindow": { "id": windowData.id } } }, _ => {}) | ||
| 166 | } | ||
| 167 | |||
| 168 | WrapperRectangle { | ||
| 169 | color: windowMouseArea.containsMouse ? "#33808080" : "transparent"; | ||
| 170 | |||
| 171 | WrapperItem { | ||
| 172 | rightMargin: 8 | ||
| 173 | leftMargin: 8 | ||
| 174 | topMargin: windowMouseArea.index == 0 ? 8 : 4 | ||
| 175 | bottomMargin: windowMouseArea.index == windowsModel.values.length - 1 ? 8 : 4 | ||
| 176 | |||
| 177 | Text { | ||
| 178 | id: windowLabel | ||
| 179 | |||
| 180 | font.pointSize: 10 | ||
| 181 | font.family: "Fira Sans" | ||
| 182 | color: { | ||
| 183 | if (windowData.is_focused) | ||
| 184 | return "#23fd00"; | ||
| 185 | if (NiriService.workspaces.find(ws => ws.id == windowData.workspace_id)?.active_window_id == windowData.id) | ||
| 186 | return "white"; | ||
| 187 | return "#555"; | ||
| 188 | } | ||
| 189 | |||
| 190 | text: windowData.title | ||
| 191 | |||
| 192 | horizontalAlignment: Text.AlignLeft | ||
| 193 | } | ||
| 194 | } | ||
| 195 | } | ||
| 196 | } | ||
| 197 | } | ||
| 198 | } | ||
| 199 | } | ||
| 200 | } | ||
| 201 | } | ||
| 202 | } | ||
| 203 | } | ||
| 204 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml b/accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml new file mode 100644 index 00000000..04bcc581 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml | |||
| @@ -0,0 +1,120 @@ | |||
| 1 | import QtQml | ||
| 2 | import Quickshell | ||
| 3 | import Quickshell.Io | ||
| 4 | import QtQuick | ||
| 5 | import Quickshell.Widgets | ||
| 6 | |||
| 7 | Item { | ||
| 8 | id: root | ||
| 9 | |||
| 10 | required property string command | ||
| 11 | property var state: null | ||
| 12 | |||
| 13 | height: parent.height | ||
| 14 | width: label.contentWidth + 8 | ||
| 15 | anchors.verticalCenter: parent.verticalCenter | ||
| 16 | |||
| 17 | Process { | ||
| 18 | id: process | ||
| 19 | running: true | ||
| 20 | command: [ @worktime@, root.command, "--waybar" ] | ||
| 21 | stdout: StdioCollector { | ||
| 22 | id: processCollector | ||
| 23 | onStreamFinished: { | ||
| 24 | try { | ||
| 25 | root.state = JSON.parse(processCollector.text); | ||
| 26 | } catch (e) { | ||
| 27 | console.warn("Worktime: Failed to parse output:", processCollector.text, e); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | Timer { | ||
| 34 | running: true | ||
| 35 | interval: 60 | ||
| 36 | repeat: true | ||
| 37 | onTriggered: process.running = true | ||
| 38 | } | ||
| 39 | |||
| 40 | WrapperMouseArea { | ||
| 41 | id: mouseArea | ||
| 42 | |||
| 43 | anchors.fill: parent | ||
| 44 | |||
| 45 | enabled: true | ||
| 46 | hoverEnabled: true | ||
| 47 | |||
| 48 | Item { | ||
| 49 | anchors.fill: parent | ||
| 50 | |||
| 51 | Text { | ||
| 52 | id: label | ||
| 53 | |||
| 54 | anchors.centerIn: parent | ||
| 55 | |||
| 56 | visible: root.state?.text ?? false | ||
| 57 | text: root.state?.text ?? "" | ||
| 58 | |||
| 59 | font.pointSize: 10 | ||
| 60 | font.family: "Fira Sans" | ||
| 61 | color: { | ||
| 62 | if (root.state?.class == "running") | ||
| 63 | return "white"; | ||
| 64 | if (root.state?.class == "over") | ||
| 65 | return "#f28a21"; | ||
| 66 | return "#555"; | ||
| 67 | } | ||
| 68 | } | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | PopupWindow { | ||
| 73 | id: tooltip | ||
| 74 | |||
| 75 | property bool nextVisible: Boolean(root.state?.tooltip ?? false) && (mouseArea.containsMouse || tooltipMouseArea.containsMouse) | ||
| 76 | |||
| 77 | anchor { | ||
| 78 | item: mouseArea | ||
| 79 | edges: Edges.Bottom | Edges.Left | ||
| 80 | } | ||
| 81 | visible: false | ||
| 82 | |||
| 83 | onNextVisibleChanged: hangTimer.restart() | ||
| 84 | |||
| 85 | Timer { | ||
| 86 | id: hangTimer | ||
| 87 | interval: 100 | ||
| 88 | onTriggered: tooltip.visible = tooltip.nextVisible | ||
| 89 | } | ||
| 90 | |||
| 91 | implicitWidth: tooltipText.contentWidth + 16 | ||
| 92 | implicitHeight: tooltipText.contentHeight + 16 | ||
| 93 | color: "black" | ||
| 94 | |||
| 95 | WrapperMouseArea { | ||
| 96 | id: tooltipMouseArea | ||
| 97 | |||
| 98 | enabled: true | ||
| 99 | hoverEnabled: true | ||
| 100 | |||
| 101 | anchors.fill: parent | ||
| 102 | |||
| 103 | Item { | ||
| 104 | anchors.fill: parent | ||
| 105 | |||
| 106 | Text { | ||
| 107 | id: tooltipText | ||
| 108 | |||
| 109 | anchors.centerIn: parent | ||
| 110 | |||
| 111 | font.pointSize: 10 | ||
| 112 | font.family: "Fira Sans" | ||
| 113 | color: "white" | ||
| 114 | |||
| 115 | text: root.state?.tooltip ?? "" | ||
| 116 | } | ||
| 117 | } | ||
| 118 | } | ||
| 119 | } | ||
| 120 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/displaymanager.qml b/accounts/gkleen@sif/shell/quickshell/displaymanager.qml new file mode 100644 index 00000000..b452c03d --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/displaymanager.qml | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | //@ pragma UseQApplication | ||
| 2 | |||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Wayland | ||
| 5 | import Quickshell.Io | ||
| 6 | import Quickshell.Services.Greetd | ||
| 7 | import QtQml | ||
| 8 | |||
| 9 | |||
| 10 | ShellRoot { | ||
| 11 | id: displaymanager | ||
| 12 | |||
| 13 | settings.watchFiles: false | ||
| 14 | |||
| 15 | property string currentText: "" | ||
| 16 | property string username: @username@ | ||
| 17 | property list<string> command: @niri_session@ | ||
| 18 | property list<var> messages: [] | ||
| 19 | property bool responseRequired: false | ||
| 20 | property bool responseVisible: false | ||
| 21 | |||
| 22 | signal startAuth() | ||
| 23 | |||
| 24 | onStartAuth: { | ||
| 25 | if (Greetd.state !== GreetdState.Inactive) | ||
| 26 | Greetd.cancelSession(); | ||
| 27 | displaymanager.messages = []; | ||
| 28 | Greetd.createSession(displaymanager.username); | ||
| 29 | } | ||
| 30 | |||
| 31 | Connections { | ||
| 32 | target: Greetd | ||
| 33 | function onStateChanged() { | ||
| 34 | console.log("greetd state: ", GreetdState.toString(Greetd.state)); | ||
| 35 | if (Greetd.state === GreetdState.ReadyToLaunch) | ||
| 36 | Greetd.launch(displaymanager.command); | ||
| 37 | } | ||
| 38 | function onAuthMessage(message: string, error: bool, responseRequired: bool, echoResponse: bool) { | ||
| 39 | displaymanager.responseVisible = echoResponse; | ||
| 40 | displaymanager.responseRequired = responseRequired; | ||
| 41 | displaymanager.messages = Array.from(displaymanager.messages).concat([{ "text": message, "error": error }]); | ||
| 42 | } | ||
| 43 | function onAuthFailure(message: string) { | ||
| 44 | displaymanager.responseRequired = false; | ||
| 45 | displaymanager.messages = Array.from(displaymanager.messages).concat([{ "text": message, "error": true }]); | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | Component.onCompleted: { | ||
| 50 | if (Greetd.state !== GreetdState.Inactive) | ||
| 51 | Greetd.cancelSession(); | ||
| 52 | } | ||
| 53 | |||
| 54 | Variants { | ||
| 55 | model: Quickshell.screens | ||
| 56 | |||
| 57 | delegate: Scope { | ||
| 58 | id: screenScope | ||
| 59 | |||
| 60 | required property var modelData | ||
| 61 | |||
| 62 | PanelWindow { | ||
| 63 | color: "black" | ||
| 64 | |||
| 65 | screen: screenScope.modelData | ||
| 66 | |||
| 67 | WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive | ||
| 68 | |||
| 69 | anchors.top: true | ||
| 70 | anchors.bottom: true | ||
| 71 | anchors.left: true | ||
| 72 | anchors.right: true | ||
| 73 | |||
| 74 | LockSurface { | ||
| 75 | id: surfaceContent | ||
| 76 | |||
| 77 | screen: screenScope.modelData | ||
| 78 | |||
| 79 | onCurrentTextChanged: displaymanager.currentText = currentText | ||
| 80 | Connections { | ||
| 81 | target: displaymanager | ||
| 82 | function onCurrentTextChanged() { surfaceContent.currentText = displaymanager.currentText; } | ||
| 83 | function onMessagesChanged() { surfaceContent.messages = Array.from(displaymanager.messages); } | ||
| 84 | function onResponseRequiredChanged() { surfaceContent.responseRequired = displaymanager.responseRequired; } | ||
| 85 | function onResponseVisibleChanged() { surfaceContent.responseVisible = displaymanager.responseVisible; } | ||
| 86 | } | ||
| 87 | |||
| 88 | onResponse: responseText => Greetd.respond(responseText); | ||
| 89 | Connections { | ||
| 90 | target: Greetd | ||
| 91 | function onStateChanged() { | ||
| 92 | if (Greetd.state === GreetdState.Authenticating) { | ||
| 93 | surfaceContent.authRunning = true; | ||
| 94 | } else { | ||
| 95 | surfaceContent.authRunning = false; | ||
| 96 | } | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | onAuthRunningChanged: { | ||
| 101 | if (surfaceContent.authRunning && Greetd.state !== GreetdState.Authenticating) | ||
| 102 | displaymanager.startAuth(); | ||
| 103 | } | ||
| 104 | Component.onCompleted: { | ||
| 105 | surfaceContent.authRunning = Greetd.state === GreetdState.Authenticating | ||
| 106 | surfaceContent.messages = Array.from(displaymanager.messages); | ||
| 107 | surfaceContent.responseVisible = displaymanager.responseVisible; | ||
| 108 | surfaceContent.responseRequired = displaymanager.responseRequired; | ||
| 109 | surfaceContent.currentText = displaymanager.currentText; | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/shell.qml b/accounts/gkleen@sif/shell/quickshell/shell.qml new file mode 100644 index 00000000..fb8b16dc --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/shell.qml | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | //@ pragma UseQApplication | ||
| 2 | |||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Wayland | ||
| 5 | |||
| 6 | ShellRoot { | ||
| 7 | settings.watchFiles: false | ||
| 8 | |||
| 9 | Variants { | ||
| 10 | model: Quickshell.screens | ||
| 11 | |||
| 12 | delegate: Scope { | ||
| 13 | id: screenScope | ||
| 14 | |||
| 15 | required property var modelData | ||
| 16 | |||
| 17 | PanelWindow { | ||
| 18 | id: bgWindow | ||
| 19 | |||
| 20 | screen: screenScope.modelData | ||
| 21 | |||
| 22 | WlrLayershell.layer: WlrLayer.Background | ||
| 23 | WlrLayershell.namespace: "background" | ||
| 24 | exclusionMode: ExclusionMode.Ignore | ||
| 25 | |||
| 26 | anchors.top: true | ||
| 27 | anchors.bottom: true | ||
| 28 | anchors.left: true | ||
| 29 | anchors.right: true | ||
| 30 | |||
| 31 | color: "black" | ||
| 32 | |||
| 33 | WallpaperBackground { | ||
| 34 | screen: bgWindow.screen.name | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | Bar { | ||
| 39 | modelData: screenScope.modelData | ||
| 40 | } | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | Lockscreen {} | ||
| 45 | NiriIdle {} | ||
| 46 | |||
| 47 | VolumeOSD {} | ||
| 48 | BrightnessOSD {} | ||
| 49 | |||
| 50 | NotificationDisplay {} | ||
| 51 | |||
| 52 | UnixIPC {} | ||
| 53 | } | ||
diff --git a/accounts/gkleen@sif/ssh-hosts.nix b/accounts/gkleen@sif/ssh-hosts.nix index 107f1e76..a250509b 100644 --- a/accounts/gkleen@sif/ssh-hosts.nix +++ b/accounts/gkleen@sif/ssh-hosts.nix | |||
| @@ -1,5 +1,12 @@ | |||
| 1 | { pkgs, ... }: | 1 | { lib, pkgs, ... }: |
| 2 | { | 2 | let |
| 3 | autosshProxyPorts = { | ||
| 4 | "ssh.math.lmu.de" = 8118; | ||
| 5 | "mathw0h" = 8122; | ||
| 6 | "mathw0e" = 8124; | ||
| 7 | }; | ||
| 8 | autosshProxy = host: "${lib.getExe pkgs.socat} - SOCKS4A:127.0.0.1:%h:%p,socksport=${toString autosshProxyPorts.${host}}"; | ||
| 9 | in { | ||
| 3 | "git.ymir" = | 10 | "git.ymir" = |
| 4 | { hostname = "ymir.yggdrasil.li"; | 11 | { hostname = "ymir.yggdrasil.li"; |
| 5 | user = "gitolite"; | 12 | user = "gitolite"; |
| @@ -290,15 +297,15 @@ | |||
| 290 | }; | 297 | }; |
| 291 | "mathw0d" = | 298 | "mathw0d" = |
| 292 | { hostname = "mathw0d.mathinst.loc"; | 299 | { hostname = "mathw0d.mathinst.loc"; |
| 293 | proxyJump = "mathw0h"; | 300 | proxyCommand = autosshProxy "mathw0h"; |
| 294 | }; | 301 | }; |
| 295 | "mathw0e" = | 302 | "mathw0e" = |
| 296 | { hostname = "mathw0e.mathinst.loc"; | 303 | { hostname = "mathw0e.mathinst.loc"; |
| 297 | proxyJump = "mathw0h"; | 304 | proxyCommand = autosshProxy "mathw0h"; |
| 298 | }; | 305 | }; |
| 299 | "mathw0f" = | 306 | "mathw0f" = |
| 300 | { hostname = "mathw0f.mathinst.loc"; | 307 | { hostname = "mathw0f.mathinst.loc"; |
| 301 | proxyJump = "mathw0h"; | 308 | proxyCommand = autosshProxy "mathw0h"; |
| 302 | }; | 309 | }; |
| 303 | "mathw0g" = | 310 | "mathw0g" = |
| 304 | { hostname = "mathw0g.mathinst.loc"; | 311 | { hostname = "mathw0g.mathinst.loc"; |
| @@ -306,8 +313,8 @@ | |||
| 306 | "mathw0h" = | 313 | "mathw0h" = |
| 307 | { hostname = "mathw0h.mathinst.loc"; | 314 | { hostname = "mathw0h.mathinst.loc"; |
| 308 | }; | 315 | }; |
| 309 | "proxy.mathw0g" = | 316 | "proxy.ssh.math.lmu.de" = |
| 310 | { hostname = "mathw0g.mathinst.loc"; | 317 | { hostname = "ssh.math.lmu.de"; |
| 311 | extraOptions = { | 318 | extraOptions = { |
| 312 | ControlPath = "none"; | 319 | ControlPath = "none"; |
| 313 | ExitOnForwardFailure = "yes"; | 320 | ExitOnForwardFailure = "yes"; |
| @@ -317,7 +324,17 @@ | |||
| 317 | }; | 324 | }; |
| 318 | "proxy.mathw0h" = | 325 | "proxy.mathw0h" = |
| 319 | { hostname = "mathw0h.mathinst.loc"; | 326 | { hostname = "mathw0h.mathinst.loc"; |
| 320 | proxyJump = "proxy.mathw0g"; | 327 | proxyCommand = autosshProxy "ssh.math.lmu.de"; |
| 328 | extraOptions = { | ||
| 329 | ControlPath = "none"; | ||
| 330 | ExitOnForwardFailure = "yes"; | ||
| 331 | ServerAliveCountMax = "15"; | ||
| 332 | ServerAliveInterval = "2"; | ||
| 333 | }; | ||
| 334 | }; | ||
| 335 | "proxy.mathw0e" = | ||
| 336 | { hostname = "mathw0e.mathinst.loc"; | ||
| 337 | proxyCommand = autosshProxy "mathw0h"; | ||
| 321 | extraOptions = { | 338 | extraOptions = { |
| 322 | ControlPath = "none"; | 339 | ControlPath = "none"; |
| 323 | ExitOnForwardFailure = "yes"; | 340 | ExitOnForwardFailure = "yes"; |
| @@ -327,7 +344,7 @@ | |||
| 327 | }; | 344 | }; |
| 328 | "vrt-kvm06" = | 345 | "vrt-kvm06" = |
| 329 | { hostname = "vrt-kvm06"; | 346 | { hostname = "vrt-kvm06"; |
| 330 | proxyJump = "mathw0e"; | 347 | proxyCommand = autosshProxy "mathw0e"; |
| 331 | user = "root"; | 348 | user = "root"; |
| 332 | extraOptions = { | 349 | extraOptions = { |
| 333 | PasswordAuthentication = "yes"; | 350 | PasswordAuthentication = "yes"; |
| @@ -336,7 +353,7 @@ | |||
| 336 | }; | 353 | }; |
| 337 | "vrt-kvm05" = | 354 | "vrt-kvm05" = |
| 338 | { hostname = "vrt-kvm05"; | 355 | { hostname = "vrt-kvm05"; |
| 339 | proxyJump = "mathw0e"; | 356 | proxyCommand = autosshProxy "mathw0e"; |
| 340 | user = "root"; | 357 | user = "root"; |
| 341 | extraOptions = { | 358 | extraOptions = { |
| 342 | PasswordAuthentication = "yes"; | 359 | PasswordAuthentication = "yes"; |
| @@ -345,7 +362,7 @@ | |||
| 345 | }; | 362 | }; |
| 346 | "vrt-kvm04" = | 363 | "vrt-kvm04" = |
| 347 | { hostname = "vrt-kvm04"; | 364 | { hostname = "vrt-kvm04"; |
| 348 | proxyJump = "mathw0e"; | 365 | proxyCommand = autosshProxy "mathw0e"; |
| 349 | user = "root"; | 366 | user = "root"; |
| 350 | extraOptions = { | 367 | extraOptions = { |
| 351 | PasswordAuthentication = "yes"; | 368 | PasswordAuthentication = "yes"; |
| @@ -354,7 +371,7 @@ | |||
| 354 | }; | 371 | }; |
| 355 | "vrt-kvm02" = | 372 | "vrt-kvm02" = |
| 356 | { hostname = "vrt-kvm02"; | 373 | { hostname = "vrt-kvm02"; |
| 357 | proxyJump = "mathw0e"; | 374 | proxyCommand = autosshProxy "mathw0e"; |
| 358 | user = "root"; | 375 | user = "root"; |
| 359 | extraOptions = { | 376 | extraOptions = { |
| 360 | PasswordAuthentication = "yes"; | 377 | PasswordAuthentication = "yes"; |
| @@ -363,7 +380,7 @@ | |||
| 363 | }; | 380 | }; |
| 364 | "vrt-kvm03" = | 381 | "vrt-kvm03" = |
| 365 | { hostname = "vrt-kvm03"; | 382 | { hostname = "vrt-kvm03"; |
| 366 | proxyJump = "mathw0e"; | 383 | proxyCommand = autosshProxy "mathw0e"; |
| 367 | user = "root"; | 384 | user = "root"; |
| 368 | extraOptions = { | 385 | extraOptions = { |
| 369 | PasswordAuthentication = "yes"; | 386 | PasswordAuthentication = "yes"; |
| @@ -372,7 +389,7 @@ | |||
| 372 | }; | 389 | }; |
| 373 | "vrt-kvm01" = | 390 | "vrt-kvm01" = |
| 374 | { hostname = "vrt-kvm01"; | 391 | { hostname = "vrt-kvm01"; |
| 375 | proxyJump = "mathw0e"; | 392 | proxyCommand = autosshProxy "mathw0e"; |
| 376 | user = "root"; | 393 | user = "root"; |
| 377 | extraOptions = { | 394 | extraOptions = { |
| 378 | PasswordAuthentication = "yes"; | 395 | PasswordAuthentication = "yes"; |
| @@ -381,39 +398,44 @@ | |||
| 381 | }; | 398 | }; |
| 382 | "tts-www01" = | 399 | "tts-www01" = |
| 383 | { hostname = "tts-www01.mathinst.loc"; | 400 | { hostname = "tts-www01.mathinst.loc"; |
| 384 | proxyJump = "mathw0h"; | 401 | proxyCommand = autosshProxy "mathw0h"; |
| 385 | user = "root"; | 402 | user = "root"; |
| 386 | }; | 403 | }; |
| 387 | "vpn-wg01" = | 404 | "vpn-wg01" = |
| 388 | { hostname = "vpn-wg01.mathinst.loc"; | 405 | { hostname = "vpn-wg01.mathinst.loc"; |
| 389 | proxyJump = "mathw0h"; | 406 | proxyCommand = autosshProxy "mathw0h"; |
| 390 | user = "root"; | 407 | user = "root"; |
| 391 | }; | 408 | }; |
| 392 | "repo-apt01" = | 409 | "repo-apt01" = |
| 393 | { hostname = "repo-apt01.mathinst.loc"; | 410 | { hostname = "repo-apt01.mathinst.loc"; |
| 394 | proxyJump = "mathw0h"; | 411 | proxyCommand = autosshProxy "mathw0h"; |
| 395 | user = "root"; | 412 | user = "root"; |
| 396 | }; | 413 | }; |
| 397 | "ldap-lmumr01" = | 414 | "ldap-lmumr01" = |
| 398 | { hostname = "ldap-lmumr01.mathinst.loc"; | 415 | { hostname = "ldap-lmumr01.mathinst.loc"; |
| 399 | proxyJump = "mathw0h"; | 416 | proxyCommand = autosshProxy "mathw0h"; |
| 400 | user = "root"; | 417 | user = "root"; |
| 401 | }; | 418 | }; |
| 402 | "mail-mi01" = | 419 | "mail-mi01" = |
| 403 | { hostname = "mail-mi01.mathinst.loc"; | 420 | { hostname = "mail-mi01.mathinst.loc"; |
| 404 | proxyJump = "mathw0h"; | 421 | proxyCommand = autosshProxy "mathw0h"; |
| 405 | }; | 422 | }; |
| 406 | "mail-www02" = | 423 | "mail-www02" = |
| 407 | { hostname = "mail-www02.mathinst.loc"; | 424 | { hostname = "mail-www02.mathinst.loc"; |
| 408 | proxyJump = "mathw0h"; | 425 | proxyCommand = autosshProxy "mathw0h"; |
| 409 | }; | 426 | }; |
| 410 | "dpl-fai01" = | 427 | "dpl-fai01" = |
| 411 | { hostname = "dpl-fai01.mathinst.loc"; | 428 | { hostname = "dpl-fai01.mathinst.loc"; |
| 412 | user = "root"; | 429 | user = "root"; |
| 413 | }; | 430 | }; |
| 431 | "dpl-fai02" = | ||
| 432 | { hostname = "dpl-fai02.mathinst.loc"; | ||
| 433 | user = "root"; | ||
| 434 | proxyJump = "mgmt01"; | ||
| 435 | }; | ||
| 414 | "math05" = | 436 | "math05" = |
| 415 | { hostname = "math05.mathinst.loc"; | 437 | { hostname = "math05.mathinst.loc"; |
| 416 | proxyJump = "mathw0h"; | 438 | proxyCommand = autosshProxy "mathw0h"; |
| 417 | extraOptions.KexAlgorithms = "+diffie-hellman-group1-sha1"; | 439 | extraOptions.KexAlgorithms = "+diffie-hellman-group1-sha1"; |
| 418 | }; | 440 | }; |
| 419 | "switch01" = | 441 | "switch01" = |
| @@ -439,20 +461,20 @@ | |||
| 439 | }; | 461 | }; |
| 440 | "www-mi01" = | 462 | "www-mi01" = |
| 441 | { hostname = "www-mi01.mathinst.loc"; | 463 | { hostname = "www-mi01.mathinst.loc"; |
| 442 | proxyJump = "mathw0h"; | 464 | proxyCommand = autosshProxy "mathw0h"; |
| 443 | }; | 465 | }; |
| 444 | "cip04" = | 466 | "cip04" = |
| 445 | { hostname = "cip04.cipmath.loc"; | 467 | { hostname = "cip04.cipmath.loc"; |
| 446 | proxyJump = "mathw0h"; | 468 | proxyCommand = autosshProxy "mathw0h"; |
| 447 | }; | 469 | }; |
| 448 | "mgmt-cls01" = | 470 | "mgmt-cls01" = |
| 449 | { user = "root"; | 471 | { user = "root"; |
| 450 | hostname = "mgmt-cls01.cipmath.loc"; | 472 | hostname = "mgmt-cls01.cipmath.loc"; |
| 451 | proxyJump = "ssh.math.lmu.de"; | 473 | proxyCommand = autosshProxy "ssh.math.lmu.de"; |
| 452 | }; | 474 | }; |
| 453 | "mgmt01" = | 475 | "mgmt01" = |
| 454 | { hostname = "mgmt01.mathinst.loc"; | 476 | { hostname = "mgmt01.mathinst.loc"; |
| 455 | proxyJump = "mathw0h"; | 477 | proxyCommand = autosshProxy "mathw0h"; |
| 456 | user = "root"; | 478 | user = "root"; |
| 457 | }; | 479 | }; |
| 458 | "ssh-lb01" = | 480 | "ssh-lb01" = |
| @@ -471,17 +493,17 @@ | |||
| 471 | "rdlx02" = { hostname = "rdlx02.mathinst.loc"; proxyJump = "mgmt01"; }; | 493 | "rdlx02" = { hostname = "rdlx02.mathinst.loc"; proxyJump = "mgmt01"; }; |
| 472 | "math0d" = | 494 | "math0d" = |
| 473 | { hostname = "math0d.mathinst.loc"; | 495 | { hostname = "math0d.mathinst.loc"; |
| 474 | proxyJump = "mathw0h"; | 496 | proxyCommand = autosshProxy "mathw0h"; |
| 475 | }; | 497 | }; |
| 476 | "dhcp01" = | 498 | "dhcp01" = |
| 477 | { hostname = "dhcp01.mathinst.loc"; | 499 | { hostname = "dhcp01.mathinst.loc"; |
| 478 | user = "root"; | 500 | user = "root"; |
| 479 | proxyJump = "mathw0h"; | 501 | proxyCommand = autosshProxy "mathw0h"; |
| 480 | }; | 502 | }; |
| 481 | "dhcp02" = | 503 | "dhcp02" = |
| 482 | { hostname = "dhcp02.mathinst.loc"; | 504 | { hostname = "dhcp02.mathinst.loc"; |
| 483 | user = "root"; | 505 | user = "root"; |
| 484 | proxyJump = "mathw0h"; | 506 | proxyCommand = autosshProxy "mathw0h"; |
| 485 | }; | 507 | }; |
| 486 | "cc-gpu-l01" = | 508 | "cc-gpu-l01" = |
| 487 | { hostname = "cc-gpu-l01.mathinst.loc"; | 509 | { hostname = "cc-gpu-l01.mathinst.loc"; |
| @@ -546,7 +568,7 @@ | |||
| 546 | user = "root"; | 568 | user = "root"; |
| 547 | }; | 569 | }; |
| 548 | "nas*" = | 570 | "nas*" = |
| 549 | { proxyJump = "mathw0e"; | 571 | { proxyCommand = autosshProxy "mathw0e"; |
| 550 | user = "admin"; | 572 | user = "admin"; |
| 551 | extraOptions = { | 573 | extraOptions = { |
| 552 | PasswordAuthentication = "yes"; | 574 | PasswordAuthentication = "yes"; |
| @@ -554,9 +576,4 @@ | |||
| 554 | HostKeyAlgorithms = "+ecdsa-sha2-nistp256"; | 576 | HostKeyAlgorithms = "+ecdsa-sha2-nistp256"; |
| 555 | }; | 577 | }; |
| 556 | }; | 578 | }; |
| 557 | "game01" = | ||
| 558 | { hostname = "game01.yggdrasil.li"; | ||
| 559 | user = "factorio"; | ||
| 560 | identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil"; | ||
| 561 | }; | ||
| 562 | } | 579 | } |
diff --git a/accounts/gkleen@sif/synadm/default.nix b/accounts/gkleen@sif/synadm/default.nix new file mode 100644 index 00000000..0a8e0d4c --- /dev/null +++ b/accounts/gkleen@sif/synadm/default.nix | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | { config, pkgs, ... }: | ||
| 2 | { | ||
| 3 | home.packages = with pkgs; [ synadm ]; | ||
| 4 | sops.secrets."synadm.yaml" = { | ||
| 5 | format = "binary"; | ||
| 6 | sopsFile = ./synadm_yaml; | ||
| 7 | path = config.xdg.configHome + "/synadm.yaml"; | ||
| 8 | }; | ||
| 9 | } | ||
diff --git a/accounts/gkleen@sif/synadm/synadm_yaml b/accounts/gkleen@sif/synadm/synadm_yaml new file mode 100644 index 00000000..8d951ccc --- /dev/null +++ b/accounts/gkleen@sif/synadm/synadm_yaml | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:qJy4Pmbbxja4jmW7OaHsD0mQZ7anZwLhiVmAgkavb+CqwWGDnUBXdz22/MHCbxng5NshcFSpBoCBhgY6B9V2bUiES6bH9AtMlDcs9ebKGMArBTUTnQ2MjWQGfQTqraWdNgy+n327uj9swwCH8EZXdYH/Hlv0t/re470W+VOHeXhGghQ3Y9IGz2sgfvMGr8QxaJNydZz85rgs5QUP/PglCwWIOw2mY1EX2vYwnmiAo49LmIEaxWvRi++KHaeBveDt0nlkJwzUlipL2VOKWxkgpK3yGucQn2mz+FRe1btp+4KGm8H17eUI9FO9sBwq,iv:kgM921ovwCgDYHQj3c5Rupy/8JxHehxUD2jb1k9Ik2Y=,tag:3TLQkJbv679VWy8V2TMugw==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "age": [ | ||
| 5 | { | ||
| 6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6bzVHUGNxZTF2WC9MYmZr\neGdVVzJXN3lGdEk3cTBER3J6UTFtcUJna2d3CjdNQmRXd2haZW1MYlJzNkk1dWVD\nVTFQc2gvS0JrejJ6SFh2MXpPWDZpRE0KLS0tIE0wTC85bEpvSnlGdGFkZVFhNjFZ\nbzRiZkxMWUg2ODNVUlBmNFlPNGRrZlkK1VXLJWcssv3ETyZSSM/Hhn5VIaI9iov9\nzShZA9Zx/FX6PYTuUMC29pJ57gKourcIxa/7HwSv/xYn1A6WcYfgSg==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 8 | } | ||
| 9 | ], | ||
| 10 | "lastmodified": "2025-05-18T11:03:42Z", | ||
| 11 | "mac": "ENC[AES256_GCM,data:yonJC68PhilAgEHNNJQ8nO53Qo3rx/LnfiOWfuMm24bOUIH9QM3WZZxpigd7bHI4eC4TqRb4LvcSi0nEURTRAhwiTqGNrWbpw2Iv3n5dhLEN9aTcetG5ZuhaXqfVUoML45/ovdBZG/0l8+XIHqxN2M/g/h4JwKoR/6lqzcrVhgo=,iv:xvxBJwy+E5zUdjhGPdZPdy7tnBIEj50hfiDJFsS3wNg=,tag:L4Fas36ZOg4h0QQwC4gjNA==,type:str]", | ||
| 12 | "unencrypted_suffix": "_unencrypted", | ||
| 13 | "version": "3.10.2" | ||
| 14 | } | ||
| 15 | } | ||
diff --git a/accounts/gkleen@sif/systemd.nix b/accounts/gkleen@sif/systemd.nix index 33bf7ef2..e601b49c 100644 --- a/accounts/gkleen@sif/systemd.nix +++ b/accounts/gkleen@sif/systemd.nix | |||
| @@ -6,7 +6,7 @@ let | |||
| 6 | cfg = config.home-manager.users.${userName}; | 6 | cfg = config.home-manager.users.${userName}; |
| 7 | 7 | ||
| 8 | autossh-socks-script = pkgs.writeScript "autossh" '' | 8 | autossh-socks-script = pkgs.writeScript "autossh" '' |
| 9 | #!${pkgs.zsh}/bin/zsh -xe | 9 | #!${lib.getExe pkgs.zsh} -xe |
| 10 | 10 | ||
| 11 | host="''${1%:*}" | 11 | host="''${1%:*}" |
| 12 | port="''${1#*:}" | 12 | port="''${1#*:}" |
| @@ -15,31 +15,29 @@ let | |||
| 15 | cmd=() | 15 | cmd=() |
| 16 | 16 | ||
| 17 | if [[ -n "''${SSHPASS_SECRET}" ]]; then | 17 | if [[ -n "''${SSHPASS_SECRET}" ]]; then |
| 18 | cmd+=(${pkgs.sshpassSecret}/bin/sshpass-secret) | 18 | cmd+=(${lib.getExe' pkgs.sshpassSecret "sshpass-secret"}) |
| 19 | cmd+=("''${(@s/:/)SSHPASS_SECRET}") | 19 | cmd+=("''${(@s/:/)SSHPASS_SECRET}") |
| 20 | cmd+=(--) | 20 | cmd+=(--) |
| 21 | fi | 21 | fi |
| 22 | 22 | ||
| 23 | cmd+=(${pkgs.openssh}/bin/ssh -vN -D localhost:''${port} "''${host}") | 23 | cmd+=(${lib.getExe' pkgs.openssh "ssh"} -vN -D 127.0.0.1:''${port} "''${host}") |
| 24 | 24 | ||
| 25 | ( exec -a "''${cmd[1]}" -- ''${cmd} ) & | 25 | ( exec -a "''${cmd[1]}" -- ''${cmd} ) & |
| 26 | pid=$! | 26 | pid=$! |
| 27 | 27 | ||
| 28 | newpid="" | 28 | newpid="" |
| 29 | i=200 | 29 | i=200 |
| 30 | while ! newpid=$(${pkgs.lsof}/bin/lsof -Pi @localhost:"''${port}" -sTCP:LISTEN -t); do | 30 | while ! newpid=$(${lib.getExe pkgs.lsof} -Pi @localhost:"''${port}" -sTCP:LISTEN -t); do |
| 31 | if ! kill -0 "''${pid}"; then | 31 | if ! kill -0 "''${pid}"; then |
| 32 | wait "''${pid}" | 32 | wait "''${pid}" |
| 33 | exit $? | 33 | exit $? |
| 34 | fi | 34 | fi |
| 35 | [[ "''${i}" -gt 0 ]] || exit 1 | 35 | [[ "''${i}" -gt 0 ]] || exit 1 |
| 36 | i=$((''${i} - 1)) | 36 | i=$((''${i} - 1)) |
| 37 | ${pkgs.coreutils}/bin/sleep 0.1 | 37 | ${lib.getExe' pkgs.coreutils "sleep"} 0.1 |
| 38 | done | 38 | done |
| 39 | 39 | ||
| 40 | ${config.systemd.package}/bin/systemd-notify --ready | 40 | ${lib.getExe' config.systemd.package "systemd-notify"} --pid=''${newpid} --ready |
| 41 | |||
| 42 | wait "''${pid}" "''${newpid}" | ||
| 43 | ''; | 41 | ''; |
| 44 | in { | 42 | in { |
| 45 | tmpfiles.rules = [ | 43 | tmpfiles.rules = [ |
| @@ -48,11 +46,11 @@ in { | |||
| 48 | ]; | 46 | ]; |
| 49 | 47 | ||
| 50 | services = { | 48 | services = { |
| 51 | sync-keepass = { | 49 | "sync-keepass@" = { |
| 52 | Service = { | 50 | Service = { |
| 53 | Type = "oneshot"; | 51 | Type = "oneshot"; |
| 54 | WorkingDirectory = "~"; | 52 | WorkingDirectory = "~"; |
| 55 | ExecStart = toString (pkgs.writers.writePython3 "sync-keepass" { | 53 | ExecStart = "${pkgs.writers.writePython3 "sync-keepass" { |
| 56 | libraries = with pkgs.python3Packages; [ python-dateutil ]; | 54 | libraries = with pkgs.python3Packages; [ python-dateutil ]; |
| 57 | } '' | 55 | } '' |
| 58 | import json | 56 | import json |
| @@ -61,13 +59,13 @@ in { | |||
| 61 | from datetime import datetime | 59 | from datetime import datetime |
| 62 | from dateutil.tz import tzlocal | 60 | from dateutil.tz import tzlocal |
| 63 | from dateutil.parser import isoparse | 61 | from dateutil.parser import isoparse |
| 64 | from sys import stderr | 62 | from sys import stderr, argv |
| 65 | 63 | ||
| 66 | 64 | ||
| 67 | remote_fs = 'surtr' | 65 | remote_fs = 'surtr' if argv[1] == 'store.kdbx' else 'mathcloud' |
| 68 | remote_file = 'store.kdbx' | 66 | remote_file = argv[1] |
| 69 | target_file = expanduser('~/store.kdbx') | 67 | target_file = expanduser(f'~/{argv[1]}') |
| 70 | meta_file = expanduser('~/.store.kdbx.json') | 68 | meta_file = expanduser(f'~/.{argv[1]}.json') |
| 71 | 69 | ||
| 72 | upload_time = None | 70 | upload_time = None |
| 73 | our_last_upload_time = None | 71 | our_last_upload_time = None |
| @@ -117,22 +115,14 @@ in { | |||
| 117 | do_upload() | 115 | do_upload() |
| 118 | elif upload_time is not None and (mod_time is None or upload_time > mod_time) and (our_last_upload_time is None or upload_time > our_last_upload_time): # noqa: E501 | 116 | elif upload_time is not None and (mod_time is None or upload_time > mod_time) and (our_last_upload_time is None or upload_time > our_last_upload_time): # noqa: E501 |
| 119 | do_download() | 117 | do_download() |
| 120 | ''); | 118 | ''} \"%I\""; |
| 121 | Environment = [ "RCLONE_PASSWORD_COMMAND=\"${pkgs.coreutils}/bin/cat ${config.sops.secrets.gkleen-rclone.path}\"" "PATH=${pkgs.rclone}/bin" ]; | 119 | Environment = [ "RCLONE_PASSWORD_COMMAND=\"${pkgs.coreutils}/bin/cat ${config.sops.secrets.gkleen-rclone.path}\"" "PATH=${pkgs.rclone}/bin" ]; |
| 122 | }; | 120 | }; |
| 123 | }; | 121 | }; |
| 124 | emacs = { | 122 | emacs = { |
| 125 | Unit = { | 123 | Unit = { |
| 126 | After = ["graphical-session-pre.target"]; | 124 | After = [ "graphical-session.target" ]; |
| 127 | }; | 125 | BindsTo = [ "graphical-session.target" ]; |
| 128 | }; | ||
| 129 | dunst = { | ||
| 130 | Service = { | ||
| 131 | ExecStart = lib.mkForce "${cfg.services.dunst.package}/bin/dunst"; | ||
| 132 | Restart = "always"; | ||
| 133 | }; | ||
| 134 | Install = { | ||
| 135 | WantedBy = ["graphical-session.target"]; | ||
| 136 | }; | 126 | }; |
| 137 | }; | 127 | }; |
| 138 | keepassxc = { | 128 | keepassxc = { |
| @@ -144,8 +134,8 @@ in { | |||
| 144 | Environment = [ "QT_QPA_PLATFORM=wayland" ]; | 134 | Environment = [ "QT_QPA_PLATFORM=wayland" ]; |
| 145 | }; | 135 | }; |
| 146 | Unit = { | 136 | Unit = { |
| 147 | Requires = ["graphical-session-pre.target"]; | 137 | After = [ "graphical-session.target" ]; |
| 148 | After = ["graphical-session-pre.target"]; | 138 | BindsTo = [ "graphical-session.target" ]; |
| 149 | }; | 139 | }; |
| 150 | }; | 140 | }; |
| 151 | mpris-proxy = { | 141 | mpris-proxy = { |
| @@ -154,7 +144,7 @@ in { | |||
| 154 | Service.ExecStart = "${pkgs.bluez}/bin/mpris-proxy"; | 144 | Service.ExecStart = "${pkgs.bluez}/bin/mpris-proxy"; |
| 155 | Install.WantedBy = [ "default.target" ]; | 145 | Install.WantedBy = [ "default.target" ]; |
| 156 | }; | 146 | }; |
| 157 | "autossh-socks@proxy.mathw0h:8119" = { | 147 | "autossh-socks@proxy.ssh.math.lmu.de:8119" = { |
| 158 | Service = { | 148 | Service = { |
| 159 | Type = "notify"; | 149 | Type = "notify"; |
| 160 | NotifyAccess = "all"; | 150 | NotifyAccess = "all"; |
| @@ -162,7 +152,7 @@ in { | |||
| 162 | Restart = "always"; | 152 | Restart = "always"; |
| 163 | RestartSec = "23s"; | 153 | RestartSec = "23s"; |
| 164 | ExecStart = "${autossh-socks-script} \"%I\""; | 154 | ExecStart = "${autossh-socks-script} \"%I\""; |
| 165 | Environment = [ "SSHPASS_SECRET=gkleen@mathw0g.math.lmu.de" ]; | 155 | Environment = [ "SSHPASS_SECRET=gkleen@ssh.math.lmu.de" ]; |
| 166 | }; | 156 | }; |
| 167 | Unit = { | 157 | Unit = { |
| 168 | StopWhenUnneeded = true; | 158 | StopWhenUnneeded = true; |
| @@ -183,44 +173,58 @@ in { | |||
| 183 | StopWhenUnneeded = true; | 173 | StopWhenUnneeded = true; |
| 184 | }; | 174 | }; |
| 185 | }; | 175 | }; |
| 186 | swayidle = { | 176 | "autossh-socks@proxy.mathw0h:8123" = { |
| 187 | Service = { | 177 | Service = { |
| 188 | RuntimeDirectory = "swayidle"; | 178 | Type = "notify"; |
| 189 | }; | 179 | NotifyAccess = "all"; |
| 190 | }; | 180 | WorkingDirectory = "~"; |
| 191 | psi-notify = { | 181 | Restart = "always"; |
| 192 | Install = { | 182 | RestartSec = "23s"; |
| 193 | WantedBy = ["graphical-session.target"]; | 183 | ExecStart = "${autossh-socks-script} \"%I\""; |
| 184 | Environment = [ "SSHPASS_SECRET=gkleen@mathw0h.mathinst.loc" ]; | ||
| 194 | }; | 185 | }; |
| 195 | Unit = { | 186 | Unit = { |
| 196 | Requires = ["graphical-session-pre.target"]; | 187 | StopWhenUnneeded = true; |
| 197 | After = ["graphical-session-pre.target"]; | 188 | StartLimitInterval = "180s"; |
| 189 | StartLimitBurst = 7; | ||
| 198 | }; | 190 | }; |
| 191 | }; | ||
| 192 | "autossh-socks@proxy.mathw0e:8125" = { | ||
| 199 | Service = { | 193 | Service = { |
| 200 | ExecStart = lib.getExe pkgs.psi-notify; | ||
| 201 | ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; | ||
| 202 | Type = "notify"; | 194 | Type = "notify"; |
| 195 | NotifyAccess = "all"; | ||
| 196 | WorkingDirectory = "~"; | ||
| 203 | Restart = "always"; | 197 | Restart = "always"; |
| 204 | WatchdogSec = "2s"; | 198 | RestartSec = "23s"; |
| 199 | ExecStart = "${autossh-socks-script} \"%I\""; | ||
| 200 | Environment = [ "SSHPASS_SECRET=gkleen@mathw0e.mathinst.loc" ]; | ||
| 201 | }; | ||
| 202 | Unit = { | ||
| 203 | StopWhenUnneeded = true; | ||
| 204 | StartLimitInterval = "180s"; | ||
| 205 | StartLimitBurst = 7; | ||
| 205 | }; | 206 | }; |
| 206 | }; | 207 | }; |
| 207 | polkit-gnome-authentication-agent-1 = { | 208 | psi-notify = { |
| 208 | Install = { | 209 | Install = { |
| 209 | WantedBy = ["graphical-session.target"]; | 210 | WantedBy = ["graphical-session.target"]; |
| 210 | }; | 211 | }; |
| 211 | Unit = { | 212 | Unit = { |
| 212 | PartOf = ["graphical-session.target"]; | 213 | After = [ "graphical-session.target" ]; |
| 213 | Requires = ["graphical-session-pre.target"]; | 214 | PartOf = [ "graphical-session.target" ]; |
| 214 | After = ["graphical-session-pre.target"]; | ||
| 215 | }; | 215 | }; |
| 216 | Service = { | 216 | Service = { |
| 217 | ExecStart = "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1"; | 217 | ExecStart = lib.getExe pkgs.psi-notify; |
| 218 | Restart = "on-failure"; | 218 | ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; |
| 219 | Type = "notify"; | ||
| 220 | Restart = "always"; | ||
| 221 | WatchdogSec = "2s"; | ||
| 219 | }; | 222 | }; |
| 220 | }; | 223 | }; |
| 221 | gtklock = { | 224 | gtklock = { |
| 222 | Unit = { | 225 | Unit = { |
| 223 | Requisite = ["graphical-session.target"]; | 226 | Requisite = ["graphical-session.target"]; |
| 227 | After = [ "graphical-session.target" ]; | ||
| 224 | PartOf = ["graphical-session.target"]; | 228 | PartOf = ["graphical-session.target"]; |
| 225 | }; | 229 | }; |
| 226 | Service = { | 230 | Service = { |
| @@ -228,53 +232,55 @@ in { | |||
| 228 | RuntimeDirectory = "gtklock"; | 232 | RuntimeDirectory = "gtklock"; |
| 229 | CacheDirectory = "gtklock"; | 233 | CacheDirectory = "gtklock"; |
| 230 | ExecStartPre = [ | 234 | ExecStartPre = [ |
| 231 | "${pkgs.libsForQt5.qt5.qttools.bin}/bin/qdbus org.keepassxc.KeePassXC.MainWindow /keepassxc org.keepassxc.KeePassXC.MainWindow.lockAllDatabases" | 235 | "-${lib.getExe' pkgs.libsForQt5.qt5.qttools.bin "qdbus"} org.keepassxc.KeePassXC.MainWindow /keepassxc org.keepassxc.KeePassXC.MainWindow.lockAllDatabases" |
| 232 | "${config.systemd.package}/bin/systemctl --user stop gpg-agent.service" | 236 | "-${lib.getExe' config.systemd.package "systemctl"} --user stop gpg-agent.service" |
| 233 | (pkgs.writeShellScript "generate-css" '' | 237 | "-${lib.getExe pkgs.playerctl} -a pause" |
| 234 | set -x | 238 | "-${lib.getExe (pkgs.writeShellApplication { |
| 235 | export PATH="${lib.makeBinPath [cfg.programs.wpaperd.package pkgs.jq pkgs.coreutils pkgs.imagemagick pkgs.findutils]}:$PATH" | 239 | name = "generate-css"; |
| 240 | runtimeInputs = with pkgs; [cfg.services.wpaperd.package jq coreutils imagemagick findutils]; | ||
| 241 | text = '' | ||
| 242 | declare -A monitors | ||
| 243 | monitors=() | ||
| 244 | while IFS= read -r entry; do | ||
| 245 | path=$(jq -r ".path" <<<"$entry") | ||
| 246 | [[ -z "$path" || ! -f "$path" ]] && continue | ||
| 247 | blurred_path="$CACHE_DIRECTORY"/"$(b2sum -l 128 <<<"$path" | cut -d' ' -f1)"."''${path##*.}" | ||
| 248 | monitor=$(jq -r ".display" <<<"$entry") | ||
| 249 | if [[ ! -f "$blurred_path" ]]; then | ||
| 250 | mkdir -p "$(dirname "$blurred_path")" | ||
| 251 | magick "$path" -filter Gaussian -resize 6.25% -define filter:sigma=2.5 -resize 1600% "$blurred_path" & | ||
| 252 | fi | ||
| 253 | monitors+=([$monitor]="$blurred_path") | ||
| 254 | done < <(wpaperctl all-wallpapers -j | jq -c ".[]") | ||
| 255 | # wait | ||
| 236 | 256 | ||
| 237 | declare -A monitors | 257 | cp --no-preserve=mode ${pkgs.writeText "gtklock.css" '' |
| 238 | monitors=() | 258 | #window-box { |
| 239 | while IFS= read -r entry; do | 259 | padding: 64px; |
| 240 | path=$(jq -r ".path" <<<"$entry") | 260 | /* border: 1px solid black; */ |
| 241 | [[ -z "$path" || ! -f "$path" ]] && continue | 261 | border-radius: 4px; |
| 242 | blurred_path="$CACHE_DIRECTORY"/"$(b2sum -l 128 <<<"$path" | cut -d' ' -f1)"."''${path##*.}" | 262 | box-shadow: rgba(0, 0, 0, 0.8) 0px 4px 12px; |
| 243 | monitor=$(jq -r ".display" <<<"$entry") | 263 | /* background-color: white; */ |
| 244 | if [[ ! -f "$blurred_path" ]]; then | 264 | background-color: rgba(0, 0, 0, 0.5); |
| 245 | mkdir -p "$(dirname "$blurred_path")" | 265 | } |
| 246 | magick "$path" -filter Gaussian -resize 6.25% -define filter:sigma=2.5 -resize 1600% "$blurred_path" & | 266 | ''} "$RUNTIME_DIRECTORY"/style.css |
| 247 | fi | 267 | for monitor in "''${!monitors[@]}"; do |
| 248 | monitors+=([$monitor]="$blurred_path") | 268 | cat >>"$RUNTIME_DIRECTORY"/style.css <<EOF |
| 249 | done < <(wpaperctl all-wallpapers -j | jq -c ".[]") | 269 | window#''${monitor} { |
| 250 | wait | 270 | background-image: url("''${monitors[$monitor]}"); |
| 251 | 271 | background-repeat: no-repeat; | |
| 252 | cp --no-preserve=mode ${pkgs.writeText "gtklock.css" '' | 272 | background-size: 100% 100%; |
| 253 | #window-box { | 273 | background-origin: content-box; |
| 254 | padding: 64px; | ||
| 255 | /* border: 1px solid black; */ | ||
| 256 | border-radius: 4px; | ||
| 257 | box-shadow: rgba(0, 0, 0, 0.8) 0px 4px 12px; | ||
| 258 | /* background-color: white; */ | ||
| 259 | background-color: rgba(0, 0, 0, 0.5); | ||
| 260 | } | 274 | } |
| 261 | ''} "$RUNTIME_DIRECTORY"/style.css | 275 | EOF |
| 262 | for monitor in "''${!monitors[@]}"; do | 276 | done |
| 263 | cat >>"$RUNTIME_DIRECTORY"/style.css <<EOF | 277 | ''; |
| 264 | window#''${monitor} { | 278 | })}" |
| 265 | background-image: url("''${monitors[$monitor]}"); | ||
| 266 | background-repeat: no-repeat; | ||
| 267 | background-size: 100% 100%; | ||
| 268 | background-origin: content-box; | ||
| 269 | } | ||
| 270 | EOF | ||
| 271 | done | ||
| 272 | '') | ||
| 273 | ]; | 279 | ]; |
| 274 | NotifyAccess = "all"; | 280 | NotifyAccess = "all"; |
| 275 | ExecStart = ''${lib.getExe pkgs.gtklock} -s "''${RUNTIME_DIRECTORY}/style.css" -L ${pkgs.writeShellScript "after-lock" '' | 281 | ExecStart = ''${lib.getExe pkgs.gtklock} -s "''${RUNTIME_DIRECTORY}/style.css" -L ${pkgs.writeShellScript "after-lock" '' |
| 276 | ${cfg.wayland.windowManager.hyprland.package}/bin/hyprctl dispatch dpms off | 282 | ${lib.getExe cfg.programs.niri.package} msg action power-off-monitors |
| 277 | ${config.systemd.package}/bin/systemd-notify --ready | 283 | ${lib.getExe' config.systemd.package "systemd-notify"} --ready |
| 278 | ''}''; | 284 | ''}''; |
| 279 | }; | 285 | }; |
| 280 | }; | 286 | }; |
| @@ -322,15 +328,60 @@ in { | |||
| 322 | ExecStopPost = "${pkgs.coreutils}/bin/rm -rfv \"$CACHE_DIRECTORY\""; | 328 | ExecStopPost = "${pkgs.coreutils}/bin/rm -rfv \"$CACHE_DIRECTORY\""; |
| 323 | }; | 329 | }; |
| 324 | }; | 330 | }; |
| 331 | # wpaperd = { | ||
| 332 | # Install = { | ||
| 333 | # WantedBy = ["graphical-session.target"]; | ||
| 334 | # }; | ||
| 335 | # Unit = { | ||
| 336 | # After = [ "graphical-session.target" ]; | ||
| 337 | # PartOf = [ "graphical-session.target" ]; | ||
| 338 | # }; | ||
| 339 | # Service = { | ||
| 340 | # ExecStart = lib.getExe cfg.services.wpaperd.package; | ||
| 341 | # Type = "simple"; | ||
| 342 | # Restart = "always"; | ||
| 343 | # RestartSec = "2s"; | ||
| 344 | # }; | ||
| 345 | # }; | ||
| 346 | xembed-sni-proxy = { | ||
| 347 | Unit = { | ||
| 348 | PartOf = lib.mkForce ["tray.target"]; | ||
| 349 | }; | ||
| 350 | }; | ||
| 351 | poweralertd = { | ||
| 352 | Unit = { | ||
| 353 | After = ["graphical-session.target"]; | ||
| 354 | }; | ||
| 355 | }; | ||
| 356 | network-manager-applet = { | ||
| 357 | Unit = { | ||
| 358 | PartOf = lib.mkForce ["tray.target"]; | ||
| 359 | }; | ||
| 360 | }; | ||
| 361 | udiskie = { | ||
| 362 | Unit = { | ||
| 363 | PartOf = lib.mkForce ["tray.target"]; | ||
| 364 | }; | ||
| 365 | }; | ||
| 366 | blueman-applet = { | ||
| 367 | Unit = { | ||
| 368 | PartOf = lib.mkForce ["tray.target"]; | ||
| 369 | }; | ||
| 370 | Install = { | ||
| 371 | WantedBy = lib.mkForce ["tray.target"]; | ||
| 372 | }; | ||
| 373 | }; | ||
| 325 | } // listToAttrs (map ({host, port}: nameValuePair "proxy-to-autossh-socks@${toString port}" { | 374 | } // listToAttrs (map ({host, port}: nameValuePair "proxy-to-autossh-socks@${toString port}" { |
| 326 | Unit = { | 375 | Unit = { |
| 327 | Requires = ["autossh-socks@${host}:${toString (port + 1)}.service" "proxy-to-autossh-socks@${toString port}.socket"]; | 376 | BindsTo = ["autossh-socks@${host}:${toString (port + 1)}.service" "proxy-to-autossh-socks@${toString port}.socket"]; |
| 328 | After = ["autossh-socks@${host}:${toString (port + 1)}.service" "proxy-to-autossh-socks@${toString port}.socket"]; | 377 | After = ["autossh-socks@${host}:${toString (port + 1)}.service" "proxy-to-autossh-socks@${toString port}.socket"]; |
| 329 | }; | 378 | }; |
| 330 | Service = { | 379 | Service = { |
| 331 | ExecStart = "${config.systemd.package}/lib/systemd/systemd-socket-proxyd --exit-idle-time=10s localhost:${toString (port + 1)}"; | 380 | ExecStart = "${config.systemd.package}/lib/systemd/systemd-socket-proxyd --exit-idle-time=60s 127.0.0.1:${toString (port + 1)}"; |
| 381 | Restart = "always"; | ||
| 382 | RestartSec = "23s"; | ||
| 332 | }; | 383 | }; |
| 333 | }) [{ host = "proxy.mathw0h"; port = 8118; } { host = "proxy.vidhar"; port = 8120; }]); | 384 | }) [{ host = "proxy.ssh.math.lmu.de"; port = 8118; } { host = "proxy.vidhar"; port = 8120; } { host = "proxy.mathw0h"; port = 8122; } { host = "proxy.mathw0e"; port = 8124; }]); |
| 334 | sockets = listToAttrs (map (port: nameValuePair "proxy-to-autossh-socks@${toString port}" { | 385 | sockets = listToAttrs (map (port: nameValuePair "proxy-to-autossh-socks@${toString port}" { |
| 335 | Socket = { | 386 | Socket = { |
| 336 | ListenStream = "%I"; | 387 | ListenStream = "%I"; |
| @@ -338,7 +389,7 @@ in { | |||
| 338 | Install = { | 389 | Install = { |
| 339 | WantedBy = ["default.target"]; | 390 | WantedBy = ["default.target"]; |
| 340 | }; | 391 | }; |
| 341 | }) [8118 8120]) // { | 392 | }) [8118 8120 8122 8124]) // { |
| 342 | "yt-dlp" = { | 393 | "yt-dlp" = { |
| 343 | Socket = { | 394 | Socket = { |
| 344 | SocketMode = "0600"; | 395 | SocketMode = "0600"; |
| @@ -352,7 +403,7 @@ in { | |||
| 352 | }; | 403 | }; |
| 353 | }; | 404 | }; |
| 354 | timers = { | 405 | timers = { |
| 355 | sync-keepass = { | 406 | "sync-keepass@store.kdbx" = { |
| 356 | Timer = { | 407 | Timer = { |
| 357 | OnActiveSec = "1m"; | 408 | OnActiveSec = "1m"; |
| 358 | OnUnitActiveSec = "1m"; | 409 | OnUnitActiveSec = "1m"; |
| @@ -362,6 +413,16 @@ in { | |||
| 362 | WantedBy = ["default.target"]; | 413 | WantedBy = ["default.target"]; |
| 363 | }; | 414 | }; |
| 364 | }; | 415 | }; |
| 416 | "sync-keepass@rz.kdbx" = { | ||
| 417 | Timer = { | ||
| 418 | OnActiveSec = "1d"; | ||
| 419 | OnUnitActiveSec = "1d"; | ||
| 420 | }; | ||
| 421 | |||
| 422 | Install = { | ||
| 423 | WantedBy = ["default.target"]; | ||
| 424 | }; | ||
| 425 | }; | ||
| 365 | }; | 426 | }; |
| 366 | targets = { | 427 | targets = { |
| 367 | graphical-session = { | 428 | graphical-session = { |
| @@ -372,6 +433,9 @@ in { | |||
| 372 | }; | 433 | }; |
| 373 | tray = { | 434 | tray = { |
| 374 | Unit = { | 435 | Unit = { |
| 436 | PartOf = [ "graphical-session.target" ]; | ||
| 437 | # Requires = [ "waybar.service" ]; | ||
| 438 | After = [ "graphical-session.target" ]; # "waybar.service" ]; | ||
| 375 | Wants = ["blueman-applet.service" "udiskie.service" "network-manager-applet.service"]; | 439 | Wants = ["blueman-applet.service" "udiskie.service" "network-manager-applet.service"]; |
| 376 | }; | 440 | }; |
| 377 | }; | 441 | }; |
diff --git a/accounts/gkleen@sif/taffybar/default.nix b/accounts/gkleen@sif/taffybar/default.nix deleted file mode 100644 index 98366d8f..00000000 --- a/accounts/gkleen@sif/taffybar/default.nix +++ /dev/null | |||
| @@ -1,2 +0,0 @@ | |||
| 1 | { haskellPackages ? (import <nixpkgs> {}).haskellPackages }: | ||
| 2 | haskellPackages.callCabal2nix "gkleen-sif-taffybar" ./. {} | ||
diff --git a/accounts/gkleen@sif/taffybar/gkleen-sif-taffybar.cabal b/accounts/gkleen@sif/taffybar/gkleen-sif-taffybar.cabal deleted file mode 100644 index e32cb473..00000000 --- a/accounts/gkleen@sif/taffybar/gkleen-sif-taffybar.cabal +++ /dev/null | |||
| @@ -1,32 +0,0 @@ | |||
| 1 | name: gkleen-sif-taffybar | ||
| 2 | version: 0.0.0 | ||
| 3 | build-type: Simple | ||
| 4 | cabal-version: >=1.10 | ||
| 5 | |||
| 6 | data-files: taffybar.css | ||
| 7 | |||
| 8 | executable taffybar | ||
| 9 | hs-source-dirs: src | ||
| 10 | main-is: taffybar.hs | ||
| 11 | ghc-options: -threaded -rtsopts -with-rtsopts=-N -O2 -Wall | ||
| 12 | build-depends: base | ||
| 13 | , containers | ||
| 14 | , directory | ||
| 15 | , filepath | ||
| 16 | , gtk3 | ||
| 17 | , taffybar | ||
| 18 | , X11>=1.8 | ||
| 19 | , transformers | ||
| 20 | , gi-gtk | ||
| 21 | , time, time-locale-compat | ||
| 22 | , text | ||
| 23 | , HStringTemplate | ||
| 24 | , gtk-sni-tray | ||
| 25 | , hslogger | ||
| 26 | other-modules: Paths_gkleen_sif_taffybar | ||
| 27 | , System.Taffybar.Widget.Clock | ||
| 28 | , System.Taffybar.Widget.TooltipBattery | ||
| 29 | default-language: Haskell2010 | ||
| 30 | default-extensions: ScopedTypeVariables | ||
| 31 | , LambdaCase | ||
| 32 | , NamedFieldPuns \ No newline at end of file | ||
diff --git a/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/Clock.hs b/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/Clock.hs deleted file mode 100644 index e8dc480f..00000000 --- a/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/Clock.hs +++ /dev/null | |||
| @@ -1,111 +0,0 @@ | |||
| 1 | {-# LANGUAGE OverloadedStrings #-} | ||
| 2 | module System.Taffybar.Widget.Clock | ||
| 3 | ( textClockNew | ||
| 4 | , textClockNewWith | ||
| 5 | , defaultClockConfig | ||
| 6 | , ClockConfig(..) | ||
| 7 | , ClockUpdateStrategy(..) | ||
| 8 | ) where | ||
| 9 | |||
| 10 | import Control.Monad.IO.Class | ||
| 11 | import Data.Maybe | ||
| 12 | import qualified Data.Text as T | ||
| 13 | import qualified Data.Time.Clock as Clock | ||
| 14 | import Data.Time.Format | ||
| 15 | import Data.Time.LocalTime | ||
| 16 | import qualified Data.Time.Locale.Compat as L | ||
| 17 | import GI.Gtk | ||
| 18 | import System.Taffybar.Widget.Generic.PollingLabel | ||
| 19 | |||
| 20 | type ClockFormat = L.TimeLocale -> ZonedTime -> T.Text | ||
| 21 | |||
| 22 | -- | Create the widget. I recommend passing @Nothing@ for the TimeLocale | ||
| 23 | -- parameter. The format string can include Pango markup | ||
| 24 | -- (<http://developer.gnome.org/pango/stable/PangoMarkupFormat.html>). | ||
| 25 | textClockNew :: | ||
| 26 | MonadIO m => Maybe L.TimeLocale -> ClockFormat -> Double -> m GI.Gtk.Widget | ||
| 27 | textClockNew userLocale format interval = | ||
| 28 | textClockNewWith cfg | ||
| 29 | where | ||
| 30 | cfg = defaultClockConfig { clockTimeLocale = userLocale | ||
| 31 | , clockFormat = format | ||
| 32 | , clockUpdateStrategy = ConstantInterval interval | ||
| 33 | } | ||
| 34 | |||
| 35 | data ClockUpdateStrategy | ||
| 36 | = ConstantInterval Double | ||
| 37 | | RoundedTargetInterval Int Double | ||
| 38 | deriving (Eq, Ord, Show) | ||
| 39 | |||
| 40 | data ClockConfig = ClockConfig | ||
| 41 | { clockTimeZone :: Maybe TimeZone | ||
| 42 | , clockTimeLocale :: Maybe L.TimeLocale | ||
| 43 | , clockFormat :: ClockFormat | ||
| 44 | , clockUpdateStrategy :: ClockUpdateStrategy | ||
| 45 | } | ||
| 46 | |||
| 47 | -- | A clock configuration that defaults to the current locale | ||
| 48 | defaultClockConfig :: ClockConfig | ||
| 49 | defaultClockConfig = ClockConfig | ||
| 50 | { clockTimeZone = Nothing | ||
| 51 | , clockTimeLocale = Nothing | ||
| 52 | , clockFormat = \locale zonedTime -> T.pack $ formatTime locale "%a %b %_d %r" zonedTime | ||
| 53 | , clockUpdateStrategy = RoundedTargetInterval 5 0.0 | ||
| 54 | } | ||
| 55 | |||
| 56 | systemGetTZ :: IO TimeZone | ||
| 57 | systemGetTZ = getCurrentTimeZone | ||
| 58 | |||
| 59 | -- | A configurable text-based clock widget. It currently allows for | ||
| 60 | -- a configurable time zone through the 'ClockConfig'. | ||
| 61 | -- | ||
| 62 | -- See also 'textClockNew'. | ||
| 63 | textClockNewWith :: MonadIO m => ClockConfig -> m Widget | ||
| 64 | textClockNewWith ClockConfig | ||
| 65 | { clockTimeZone = userZone | ||
| 66 | , clockTimeLocale = userLocale | ||
| 67 | , clockFormat = format | ||
| 68 | , clockUpdateStrategy = updateStrategy | ||
| 69 | } = liftIO $ do | ||
| 70 | let getTZ = maybe systemGetTZ return userZone | ||
| 71 | locale = fromMaybe L.defaultTimeLocale userLocale | ||
| 72 | |||
| 73 | let getUserZonedTime = | ||
| 74 | utcToZonedTime <$> getTZ <*> Clock.getCurrentTime | ||
| 75 | |||
| 76 | doTimeFormat = format locale | ||
| 77 | |||
| 78 | getRoundedTimeAndNextTarget = do | ||
| 79 | zonedTime <- getUserZonedTime | ||
| 80 | return $ case updateStrategy of | ||
| 81 | ConstantInterval interval -> | ||
| 82 | (doTimeFormat zonedTime, Nothing, interval) | ||
| 83 | RoundedTargetInterval roundSeconds offset -> | ||
| 84 | let roundSecondsDiffTime = fromIntegral roundSeconds | ||
| 85 | addTheRound = addLocalTime roundSecondsDiffTime | ||
| 86 | localTime = zonedTimeToLocalTime zonedTime | ||
| 87 | ourLocalTimeOfDay = localTimeOfDay localTime | ||
| 88 | seconds = round $ todSec ourLocalTimeOfDay | ||
| 89 | secondsFactor = seconds `div` roundSeconds | ||
| 90 | displaySeconds = secondsFactor * roundSeconds | ||
| 91 | baseLocalTimeOfDay = | ||
| 92 | ourLocalTimeOfDay { todSec = fromIntegral displaySeconds } | ||
| 93 | ourLocalTime = | ||
| 94 | localTime { localTimeOfDay = baseLocalTimeOfDay } | ||
| 95 | roundedLocalTime = | ||
| 96 | if seconds `mod` roundSeconds > roundSeconds `div` 2 | ||
| 97 | then addTheRound ourLocalTime | ||
| 98 | else ourLocalTime | ||
| 99 | roundedZonedTime = | ||
| 100 | zonedTime { zonedTimeToLocalTime = roundedLocalTime } | ||
| 101 | nextTarget = addTheRound ourLocalTime | ||
| 102 | amountToWait = realToFrac $ diffLocalTime nextTarget localTime | ||
| 103 | in (doTimeFormat roundedZonedTime, Nothing, amountToWait - offset) | ||
| 104 | |||
| 105 | label <- pollingLabelWithVariableDelay getRoundedTimeAndNextTarget | ||
| 106 | ebox <- eventBoxNew | ||
| 107 | containerAdd ebox label | ||
| 108 | eventBoxSetVisibleWindow ebox False | ||
| 109 | widgetShowAll ebox | ||
| 110 | toWidget ebox | ||
| 111 | |||
diff --git a/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/TooltipBattery.hs b/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/TooltipBattery.hs deleted file mode 100644 index 9dc52774..00000000 --- a/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/TooltipBattery.hs +++ /dev/null | |||
| @@ -1,101 +0,0 @@ | |||
| 1 | {-# LANGUAGE OverloadedStrings #-} | ||
| 2 | {-# LANGUAGE ScopedTypeVariables #-} | ||
| 3 | module System.Taffybar.Widget.TooltipBattery ( batteryIconTooltipNew ) where | ||
| 4 | |||
| 5 | import Control.Applicative | ||
| 6 | import Control.Monad | ||
| 7 | import Control.Monad.IO.Class | ||
| 8 | import Control.Monad.Trans.Reader | ||
| 9 | import Data.Int (Int64) | ||
| 10 | import qualified Data.Text as T | ||
| 11 | import GI.Gtk | ||
| 12 | import Prelude | ||
| 13 | import StatusNotifier.Tray (scalePixbufToSize) | ||
| 14 | import System.Taffybar.Context | ||
| 15 | import System.Taffybar.Information.Battery | ||
| 16 | import System.Taffybar.Util | ||
| 17 | import System.Taffybar.Widget.Generic.AutoSizeImage | ||
| 18 | import System.Taffybar.Widget.Generic.ChannelWidget | ||
| 19 | import Text.Printf | ||
| 20 | import Text.StringTemplate | ||
| 21 | import Data.Function ((&)) | ||
| 22 | |||
| 23 | -- | Just the battery info that will be used for display (this makes combining | ||
| 24 | -- several easier). | ||
| 25 | data BatteryWidgetInfo = BWI | ||
| 26 | { seconds :: Maybe Int64 | ||
| 27 | , percent :: Double | ||
| 28 | , status :: String | ||
| 29 | , rate :: Maybe Double | ||
| 30 | } deriving (Eq, Show) | ||
| 31 | |||
| 32 | -- | Format a duration expressed as seconds to hours and minutes | ||
| 33 | formatDuration :: Int64 -> String | ||
| 34 | formatDuration secs = let minutes, hours, minutes' :: Int64 | ||
| 35 | minutes = secs `div` 60 | ||
| 36 | (hours, minutes') = minutes `divMod` 60 | ||
| 37 | in printf "%02d:%02d" hours minutes' | ||
| 38 | |||
| 39 | getBatteryWidgetInfo :: BatteryInfo -> BatteryWidgetInfo | ||
| 40 | getBatteryWidgetInfo info = | ||
| 41 | let battPctNum :: Double | ||
| 42 | battPctNum = batteryPercentage info | ||
| 43 | battTime :: Maybe Int64 | ||
| 44 | battTime = | ||
| 45 | case batteryState info of | ||
| 46 | BatteryStateCharging -> Just $ batteryTimeToFull info | ||
| 47 | BatteryStateDischarging -> Just $ batteryTimeToEmpty info | ||
| 48 | _ -> Nothing | ||
| 49 | battStatus :: String | ||
| 50 | battStatus = | ||
| 51 | case batteryState info of | ||
| 52 | BatteryStateCharging -> "↑" | ||
| 53 | BatteryStateDischarging -> "↓" | ||
| 54 | BatteryStateEmpty -> "⤓" | ||
| 55 | BatteryStateFullyCharged -> "⤒" | ||
| 56 | _ -> "?" | ||
| 57 | battRate :: Maybe Double | ||
| 58 | battRate | rawRate < 0.1 = Nothing | ||
| 59 | | otherwise = Just rawRate | ||
| 60 | where rawRate = batteryEnergyRate info | ||
| 61 | in BWI{ seconds = battTime, percent = battPctNum, status = battStatus, rate = battRate } | ||
| 62 | |||
| 63 | -- | Given (maybe summarized) battery info and format: provides the string to display | ||
| 64 | formatBattInfo :: BatteryWidgetInfo -> String -> T.Text | ||
| 65 | formatBattInfo info fmt = | ||
| 66 | let tpl = newSTMP fmt | ||
| 67 | tpl' = tpl | ||
| 68 | & setManyAttrib [ ("percentage", printf "%.0f" $ percent info) | ||
| 69 | , ("status", status info) | ||
| 70 | ] | ||
| 71 | & setManyAttrib [ ("time", formatDuration <$> seconds info) | ||
| 72 | , ("rate", printf "%.0f" <$> rate info) | ||
| 73 | ] | ||
| 74 | in render tpl' | ||
| 75 | |||
| 76 | themeLoadFlags :: [IconLookupFlags] | ||
| 77 | themeLoadFlags = [IconLookupFlagsGenericFallback, IconLookupFlagsUseBuiltin] | ||
| 78 | |||
| 79 | batteryIconTooltipNew :: String -> TaffyIO Widget | ||
| 80 | batteryIconTooltipNew format = do | ||
| 81 | DisplayBatteryChanVar (chan, _) <- setupDisplayBatteryChanVar ["IconName", "State", "Percentage", "TimeToFull", "TimeToEmpty", "EnergyRate"] | ||
| 82 | ctx <- ask | ||
| 83 | liftIO $ do | ||
| 84 | image <- imageNew | ||
| 85 | styleCtx <- widgetGetStyleContext =<< toWidget image | ||
| 86 | defaultTheme <- iconThemeGetDefault | ||
| 87 | let getCurrentBatteryIconNameStringTooltip = do | ||
| 88 | info <- runReaderT getDisplayBatteryInfo ctx | ||
| 89 | let iconNameString = T.pack $ batteryIconName info | ||
| 90 | tooltip = formatBattInfo (getBatteryWidgetInfo info) format | ||
| 91 | return (iconNameString, tooltip) | ||
| 92 | extractPixbuf info = | ||
| 93 | fst <$> iconInfoLoadSymbolicForContext info styleCtx | ||
| 94 | setIconForSize size = do | ||
| 95 | (name, tooltip) <- getCurrentBatteryIconNameStringTooltip | ||
| 96 | widgetSetTooltipMarkup image $ Just tooltip | ||
| 97 | iconThemeLookupIcon defaultTheme name size themeLoadFlags >>= | ||
| 98 | traverse extractPixbuf >>= | ||
| 99 | traverse (scalePixbufToSize size OrientationHorizontal) | ||
| 100 | updateImage <- autoSizeImage image setIconForSize OrientationHorizontal | ||
| 101 | toWidget =<< channelWidgetNew image chan (const $ postGUIASync updateImage) | ||
diff --git a/accounts/gkleen@sif/taffybar/src/taffybar.hs b/accounts/gkleen@sif/taffybar/src/taffybar.hs deleted file mode 100644 index 67ee942d..00000000 --- a/accounts/gkleen@sif/taffybar/src/taffybar.hs +++ /dev/null | |||
| @@ -1,89 +0,0 @@ | |||
| 1 | {-# LANGUAGE OverloadedStrings #-} | ||
| 2 | |||
| 3 | module Main where | ||
| 4 | |||
| 5 | import System.Taffybar (startTaffybar) | ||
| 6 | import System.Taffybar.Context (TaffybarConfig(..)) | ||
| 7 | import System.Taffybar.Hooks | ||
| 8 | import System.Taffybar.SimpleConfig hiding (SimpleTaffyConfig(cssPaths)) | ||
| 9 | import System.Taffybar.Widget | ||
| 10 | import qualified System.Taffybar.Widget.Clock as MyClock | ||
| 11 | import System.Taffybar.Widget.TooltipBattery | ||
| 12 | |||
| 13 | import Data.Time.Format | ||
| 14 | import Data.Time.LocalTime | ||
| 15 | import Data.Time.Calendar.WeekDate | ||
| 16 | |||
| 17 | import qualified Data.Text as T | ||
| 18 | |||
| 19 | import Control.Exception (SomeException, try) | ||
| 20 | import Control.Monad.Trans.Reader (mapReaderT) | ||
| 21 | |||
| 22 | import Paths_gkleen_sif_taffybar | ||
| 23 | |||
| 24 | import System.Log.Logger | ||
| 25 | |||
| 26 | |||
| 27 | main :: IO () | ||
| 28 | main = do | ||
| 29 | logger <- getLogger "System.Taffybar" | ||
| 30 | saveGlobalLogger $ setLevel INFO logger | ||
| 31 | |||
| 32 | myCssPath <- getDataFileName "taffybar.css" | ||
| 33 | startTaffybar taffybarConfig{ cssPaths = pure myCssPath } | ||
| 34 | |||
| 35 | |||
| 36 | taffybarConfig :: TaffybarConfig | ||
| 37 | taffybarConfig = | ||
| 38 | let myWorkspacesConfig = | ||
| 39 | defaultWorkspacesConfig | ||
| 40 | { maxIcons = Just 0 | ||
| 41 | , widgetGap = 7 | ||
| 42 | , showWorkspaceFn = \case | ||
| 43 | -- Workspace{ workspaceState = Empty } -> False | ||
| 44 | Workspace{ workspaceName } | workspaceName == "NSP" -> False | ||
| 45 | _other -> True | ||
| 46 | , getWindowIconPixbuf = \i d -> either (\(_ :: SomeException) -> Nothing) id <$> mapReaderT try (defaultGetWindowIconPixbuf i d) | ||
| 47 | , urgentWorkspaceState = True | ||
| 48 | } | ||
| 49 | workspaces = workspacesNew myWorkspacesConfig | ||
| 50 | clock = MyClock.textClockNewWith MyClock.defaultClockConfig | ||
| 51 | { MyClock.clockUpdateStrategy = MyClock.RoundedTargetInterval 1 0.0 | ||
| 52 | , MyClock.clockFormat = \tl zt@ZonedTime{ zonedTimeToLocalTime = LocalTime{ localDay } } | ||
| 53 | -> let date = formatTime tl "%Y-%m-%d" localDay | ||
| 54 | weekdate = "W" <> show2 woy <> "-" <> show dow | ||
| 55 | where (_, woy, dow) = toWeekDate localDay | ||
| 56 | show2 :: Int -> String | ||
| 57 | show2 x = replicate (2 - length s) '0' ++ s | ||
| 58 | where s = show x | ||
| 59 | time = formatTime tl "%H:%M:%S%Ez" zt | ||
| 60 | in T.intercalate " " $ map T.pack [weekdate, date, time] | ||
| 61 | } | ||
| 62 | layout = layoutNew defaultLayoutConfig | ||
| 63 | windowsW = windowsNew defaultWindowsConfig | ||
| 64 | { getMenuLabel = truncatedGetMenuLabel 80 | ||
| 65 | , getActiveLabel = truncatedGetActiveLabel 80 | ||
| 66 | } | ||
| 67 | worktime = commandRunnerNew 60 "worktime" [] "worktime" | ||
| 68 | worktimeToday = commandRunnerNew 60 "worktime" ["today"] "worktime today" | ||
| 69 | -- See https://github.com/taffybar/gtk-sni-tray#statusnotifierwatcher | ||
| 70 | -- for a better way to set up the sni tray | ||
| 71 | -- tray = sniTrayThatStartsWatcherEvenThoughThisIsABadWayToDoIt | ||
| 72 | tray = sniTrayNew | ||
| 73 | myConfig = defaultSimpleTaffyConfig | ||
| 74 | { startWidgets = | ||
| 75 | workspaces : map (>>= buildContentsBox) [ layout, windowsW ] | ||
| 76 | , endWidgets = map (>>= buildContentsBox) $ reverse | ||
| 77 | -- , mpris2New | ||
| 78 | [ worktime, worktimeToday | ||
| 79 | , clock | ||
| 80 | , tray | ||
| 81 | , batteryIconTooltipNew "$status$ $percentage$%$if(time)$$if(rate)$ ($rate$W $time$)$else$ ($time$)$endif$$elseif(rate)$ ($rate$W)$endif$" | ||
| 82 | ] | ||
| 83 | , barPosition = Top | ||
| 84 | , barPadding = 2 | ||
| 85 | , barHeight = ExactSize 28 | ||
| 86 | , widgetSpacing = 10 | ||
| 87 | } | ||
| 88 | in withBatteryRefresh $ withLogServer $ | ||
| 89 | withToggleServer $ toTaffyConfig myConfig | ||
diff --git a/accounts/gkleen@sif/taffybar/taffybar.css b/accounts/gkleen@sif/taffybar/taffybar.css deleted file mode 100644 index 7a297465..00000000 --- a/accounts/gkleen@sif/taffybar/taffybar.css +++ /dev/null | |||
| @@ -1,146 +0,0 @@ | |||
| 1 | @define-color transparent rgba(0.0, 0.0, 0.0, 0.0); | ||
| 2 | @define-color white #808080; | ||
| 3 | @define-color gray #202020; | ||
| 4 | @define-color green #008000; | ||
| 5 | @define-color yellow #808000; | ||
| 6 | @define-color blue #000080; | ||
| 7 | @define-color red #800000; | ||
| 8 | @define-color black #000000; | ||
| 9 | /* @define-color taffy-blue #0c7cd5; */ | ||
| 10 | @define-color taffy-blue @blue; | ||
| 11 | |||
| 12 | @define-color active-window-color @white; | ||
| 13 | @define-color urgent-window-color @taffy-blue; | ||
| 14 | @define-color font-color @white; | ||
| 15 | @define-color menu-background-color @black; | ||
| 16 | @define-color menu-font-color @white; | ||
| 17 | |||
| 18 | /* Top level styling */ | ||
| 19 | |||
| 20 | .taffy-window * { | ||
| 21 | /* | ||
| 22 | This removes any existing styling from UI elements. Taffybar will not | ||
| 23 | cohere with your gtk theme. | ||
| 24 | */ | ||
| 25 | all: unset; | ||
| 26 | |||
| 27 | font-family: "Fira Sans", sans-serif; | ||
| 28 | font-size: 21px; | ||
| 29 | color: @font-color; | ||
| 30 | } | ||
| 31 | |||
| 32 | .taffy-box { | ||
| 33 | /* border-radius: 10px; */ | ||
| 34 | background-color: @black; | ||
| 35 | } | ||
| 36 | |||
| 37 | .inner-pad { | ||
| 38 | /* padding-bottom: 5px; */ | ||
| 39 | /* padding-top: 5px; */ | ||
| 40 | padding-left: 2px; | ||
| 41 | padding-right: 2px; | ||
| 42 | } | ||
| 43 | |||
| 44 | .contents { | ||
| 45 | /* padding-bottom: 4px; */ | ||
| 46 | /* padding-top: 4px; */ | ||
| 47 | padding-right: 2px; | ||
| 48 | padding-left: 2px; | ||
| 49 | transition: background-color .5s; | ||
| 50 | border-radius: 5px; | ||
| 51 | } | ||
| 52 | |||
| 53 | /* Workspaces styling */ | ||
| 54 | |||
| 55 | .workspace-label { | ||
| 56 | padding-right: 3px; | ||
| 57 | padding-left: 2px; | ||
| 58 | font-size: 21px; | ||
| 59 | } | ||
| 60 | |||
| 61 | .workspace-label.active { | ||
| 62 | color: @green; | ||
| 63 | } | ||
| 64 | .workspace-label.visible { | ||
| 65 | color: @yellow; | ||
| 66 | } | ||
| 67 | .workspace-label.empty { | ||
| 68 | color: @gray; | ||
| 69 | } | ||
| 70 | .workspace-label.urgent { | ||
| 71 | color: @red; | ||
| 72 | } | ||
| 73 | |||
| 74 | .active .contents { | ||
| 75 | background-color: rgba(0.0, 0.0, 0.0, 0.5); | ||
| 76 | } | ||
| 77 | |||
| 78 | .visible .contents { | ||
| 79 | background-color: rgba(0.0, 0.0, 0.0, 0.2); | ||
| 80 | } | ||
| 81 | |||
| 82 | .window-icon-container { | ||
| 83 | transition: opacity .5s, box-shadow .5s; | ||
| 84 | opacity: 1; | ||
| 85 | } | ||
| 86 | |||
| 87 | /* This gives space for the box-shadow (they look like underlines) that follow. | ||
| 88 | This will actually affect all widgets, (not just the workspace icons), but | ||
| 89 | that is what we want since we want the icons to look the same. */ | ||
| 90 | .auto-size-image, .sni-tray { | ||
| 91 | padding-top: 3px; | ||
| 92 | padding-bottom: 3px; | ||
| 93 | } | ||
| 94 | |||
| 95 | .window-icon-container.active { | ||
| 96 | box-shadow: inset 0 -3px @white; | ||
| 97 | } | ||
| 98 | |||
| 99 | .window-icon-container.urgent { | ||
| 100 | box-shadow: inset 0 -3px @urgent-window-color; | ||
| 101 | } | ||
| 102 | |||
| 103 | .window-icon-container.inactive .window-icon { | ||
| 104 | padding: 0px; | ||
| 105 | } | ||
| 106 | |||
| 107 | .window-icon-container.minimized .window-icon { | ||
| 108 | opacity: .3; | ||
| 109 | } | ||
| 110 | |||
| 111 | .window-icon { | ||
| 112 | opacity: 1; | ||
| 113 | transition: opacity .5s; | ||
| 114 | } | ||
| 115 | |||
| 116 | /* Button styling */ | ||
| 117 | |||
| 118 | button { | ||
| 119 | background-color: @transparent; | ||
| 120 | border-width: 0px; | ||
| 121 | border-radius: 0px; | ||
| 122 | } | ||
| 123 | |||
| 124 | button:checked, button:hover .Contents:hover { | ||
| 125 | box-shadow: inset 0 -3px @taffy-blue; | ||
| 126 | } | ||
| 127 | |||
| 128 | /* Menu styling */ | ||
| 129 | |||
| 130 | /* The ".taffy-window" prefixed selectors are needed because if they aren't present, | ||
| 131 | the top level .Taffybar selector takes precedence */ | ||
| 132 | .taffy-window menuitem *, menuitem * { | ||
| 133 | color: @menu-font-color; | ||
| 134 | } | ||
| 135 | |||
| 136 | .taffy-window menuitem, menuitem { | ||
| 137 | background-color: @menu-background-color; | ||
| 138 | } | ||
| 139 | |||
| 140 | .taffy-window menuitem:hover, menuitem:hover { | ||
| 141 | background-color: @taffy-blue; | ||
| 142 | } | ||
| 143 | |||
| 144 | .taffy-window menuitem:hover > label, menuitem:hover > label { | ||
| 145 | color: @white; | ||
| 146 | } | ||
diff --git a/accounts/gkleen@sif/utils/async-yt-dlp.nix b/accounts/gkleen@sif/utils/async-yt-dlp.nix new file mode 100644 index 00000000..c3b82ec5 --- /dev/null +++ b/accounts/gkleen@sif/utils/async-yt-dlp.nix | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | { writers, python3Packages, ... }: | ||
| 2 | |||
| 3 | writers.writePython3Bin "async-yt-dlp" { | ||
| 4 | libraries = with python3Packages; [ yt-dlp ]; | ||
| 5 | flakeIgnore = ["E501"]; | ||
| 6 | } '' | ||
| 7 | import sys | ||
| 8 | import os | ||
| 9 | |||
| 10 | import yt_dlp | ||
| 11 | import yt_dlp.options | ||
| 12 | from collections import namedtuple | ||
| 13 | import socket | ||
| 14 | from pathlib import Path | ||
| 15 | import json | ||
| 16 | |||
| 17 | create_parser = yt_dlp.options.create_parser | ||
| 18 | |||
| 19 | |||
| 20 | def parse_patched_options(opts): | ||
| 21 | patched_parser = create_parser() | ||
| 22 | patched_parser.defaults.update({ | ||
| 23 | 'ignoreerrors': False, | ||
| 24 | 'retries': 0, | ||
| 25 | 'fragment_retries': 0, | ||
| 26 | 'extract_flat': False, | ||
| 27 | 'concat_playlist': 'never', | ||
| 28 | }) | ||
| 29 | yt_dlp.options.create_parser = lambda: patched_parser | ||
| 30 | try: | ||
| 31 | return yt_dlp.parse_options(opts) | ||
| 32 | finally: | ||
| 33 | yt_dlp.options.create_parser = create_parser | ||
| 34 | |||
| 35 | |||
| 36 | default_opts = parse_patched_options([]).ydl_opts | ||
| 37 | |||
| 38 | |||
| 39 | def cli_to_api(opts): | ||
| 40 | opts = parse_patched_options(opts) | ||
| 41 | urls = opts.urls | ||
| 42 | opts = opts.ydl_opts | ||
| 43 | |||
| 44 | diff = {k: v for k, v in opts.items() if default_opts[k] != v} | ||
| 45 | if 'postprocessors' in diff: | ||
| 46 | diff['postprocessors'] = [pp for pp in diff['postprocessors'] | ||
| 47 | if pp not in default_opts['postprocessors']] | ||
| 48 | return namedtuple('Options', ('params', 'urls'))(diff, urls) | ||
| 49 | |||
| 50 | |||
| 51 | if __name__ == '__main__': | ||
| 52 | opts = cli_to_api(sys.argv[1:]) | ||
| 53 | with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock: | ||
| 54 | sock.connect(str(Path(os.environ["XDG_RUNTIME_DIR"]) / "yt-dlp.sock").encode('utf-8')) | ||
| 55 | with sock.makefile(mode='w', buffering=1, encoding='utf-8') as fh: | ||
| 56 | json.dump(opts._asdict(), fh) | ||
| 57 | '' | ||
diff --git a/accounts/gkleen@sif/utils/pdf2pdf.nix b/accounts/gkleen@sif/utils/pdf2pdf.nix new file mode 100644 index 00000000..9f4cbc3e --- /dev/null +++ b/accounts/gkleen@sif/utils/pdf2pdf.nix | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | pkgs@{ lib, resholve, zsh, ghostscript_headless, ... }: | ||
| 2 | |||
| 3 | resholve.writeScriptBin "pdf2pdf" { | ||
| 4 | inputs = with pkgs; [ghostscript_headless]; | ||
| 5 | interpreter = lib.getExe zsh; | ||
| 6 | } '' | ||
| 7 | exec gs -dPDFSETTINGS=/prepress -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dSAFER -dPreserveAnnots=false "-sOutputFile=''${2}" "''${1}" | ||
| 8 | '' | ||
diff --git a/accounts/gkleen@sif/utils/sieve-edit.nix b/accounts/gkleen@sif/utils/sieve-edit.nix new file mode 100644 index 00000000..f985a3f6 --- /dev/null +++ b/accounts/gkleen@sif/utils/sieve-edit.nix | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | pkgs@{ lib, resholve, zsh, sieve-connect, sops, ... }: | ||
| 2 | |||
| 3 | resholve.writeScriptBin "sieve-edit" { | ||
| 4 | inputs = with pkgs; [sieve-connect sops]; | ||
| 5 | interpreter = lib.getExe zsh; | ||
| 6 | execer = with pkgs; [ | ||
| 7 | "cannot:${lib.getExe sieve-connect}" | ||
| 8 | "cannot:${lib.getExe sops}" | ||
| 9 | ]; | ||
| 10 | } '' | ||
| 11 | host=$1; shift | ||
| 12 | case "$host" in | ||
| 13 | surtr) | ||
| 14 | sieve-connect -s surtr.yggdrasil.li -m EXTERNAL --clientkey <(sops decrypt $HOME/projects/machines/hosts/surtr/email/ca/gkleen@sif.key) --clientcert $HOME/projects/machines/hosts/surtr/email/ca/gkleen@sif.crt --edit --remotesieve sieve | ||
| 15 | ;; | ||
| 16 | ymir) | ||
| 17 | sieve-connect -s ymir.yggdrasil.li -u gkleen --edit --remotesieve sieve | ||
| 18 | ;; | ||
| 19 | *) | ||
| 20 | echo "Unknown host: ‘$host’" >&2 | ||
| 21 | return 2 | ||
| 22 | ;; | ||
| 23 | esac | ||
| 24 | '' | ||
diff --git a/accounts/gkleen@sif/xmonad/.gitignore b/accounts/gkleen@sif/xmonad/.gitignore deleted file mode 100644 index c11891cd..00000000 --- a/accounts/gkleen@sif/xmonad/.gitignore +++ /dev/null | |||
| @@ -1,4 +0,0 @@ | |||
| 1 | **/#*# | ||
| 2 | **/.stack-work/ | ||
| 3 | /stack.yaml.lock | ||
| 4 | /*.cabal | ||
diff --git a/accounts/gkleen@sif/xmonad/default.nix b/accounts/gkleen@sif/xmonad/default.nix deleted file mode 100644 index 8790c12f..00000000 --- a/accounts/gkleen@sif/xmonad/default.nix +++ /dev/null | |||
| @@ -1,7 +0,0 @@ | |||
| 1 | argumentPackages@{ ... }: | ||
| 2 | |||
| 3 | let | ||
| 4 | # defaultPackages = (import ./stackage.nix {}); | ||
| 5 | # haskellPackages = defaultPackages // argumentPackages; | ||
| 6 | haskellPackages = argumentPackages; | ||
| 7 | in haskellPackages.callPackage ./xmonad-yggdrasil.nix {} | ||
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Mpv.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Mpv.hs deleted file mode 100644 index e6accdcc..00000000 --- a/accounts/gkleen@sif/xmonad/lib/XMonad/Mpv.hs +++ /dev/null | |||
| @@ -1,127 +0,0 @@ | |||
| 1 | {-# LANGUAGE DeriveGeneric, OverloadedLists, OverloadedStrings, ViewPatterns, ExistentialQuantification, MultiWayIf #-} | ||
| 2 | |||
| 3 | module XMonad.Mpv | ||
| 4 | ( MpvCommand(..), MpvResponse(..), MpvException(..) | ||
| 5 | , mpv | ||
| 6 | , mpvDir | ||
| 7 | , mpvAll, mpvOne | ||
| 8 | , mpvResponse | ||
| 9 | ) where | ||
| 10 | |||
| 11 | import Data.Aeson | ||
| 12 | |||
| 13 | import Data.Monoid | ||
| 14 | |||
| 15 | import Network.Socket hiding (recv) | ||
| 16 | import Network.Socket.ByteString | ||
| 17 | |||
| 18 | import qualified Data.ByteString as BS | ||
| 19 | import qualified Data.ByteString.Char8 as CBS | ||
| 20 | import qualified Data.ByteString.Lazy as LBS | ||
| 21 | |||
| 22 | import GHC.Generics (Generic) | ||
| 23 | import Data.Typeable (Typeable) | ||
| 24 | import Data.String (IsString(..)) | ||
| 25 | |||
| 26 | import Control.Exception | ||
| 27 | |||
| 28 | import System.IO.Temp (getCanonicalTemporaryDirectory) | ||
| 29 | |||
| 30 | import Control.Monad | ||
| 31 | import Control.Exception (bracket) | ||
| 32 | import Control.Monad.IO.Class (MonadIO(..)) | ||
| 33 | |||
| 34 | import System.FilePath | ||
| 35 | import System.Directory (getDirectoryContents) | ||
| 36 | |||
| 37 | import Data.List | ||
| 38 | import Data.Either | ||
| 39 | import Data.Maybe | ||
| 40 | |||
| 41 | import Debug.Trace | ||
| 42 | |||
| 43 | |||
| 44 | data MpvCommand | ||
| 45 | = forall a. ToJSON a => MpvSetProperty String a | ||
| 46 | | MpvGetProperty String | ||
| 47 | data MpvResponse | ||
| 48 | = MpvError String | ||
| 49 | | MpvSuccess (Maybe Value) | ||
| 50 | deriving (Read, Show, Generic, Eq) | ||
| 51 | data MpvException = MpvException String | ||
| 52 | | MpvNoValue | ||
| 53 | | MpvNoParse String | ||
| 54 | deriving (Generic, Typeable, Read, Show) | ||
| 55 | instance Exception MpvException | ||
| 56 | |||
| 57 | |||
| 58 | instance ToJSON MpvCommand where | ||
| 59 | toJSON (MpvSetProperty name val) = Array ["set_property", fromString name, toJSON val] | ||
| 60 | toJSON (MpvGetProperty name) = Array ["get_property", fromString name] | ||
| 61 | |||
| 62 | instance FromJSON MpvResponse where | ||
| 63 | parseJSON = withObject "response object" $ \obj -> do | ||
| 64 | mval <- obj .:? "data" | ||
| 65 | err <- obj .: "error" | ||
| 66 | |||
| 67 | let ret | ||
| 68 | | err == "success" = MpvSuccess mval | ||
| 69 | | otherwise = MpvError err | ||
| 70 | |||
| 71 | return ret | ||
| 72 | |||
| 73 | mpvSocket :: FilePath -> (Socket -> IO a) -> IO a | ||
| 74 | mpvSocket sockPath = withSocketsDo . bracket mkSock close | ||
| 75 | where | ||
| 76 | mkSock = do | ||
| 77 | sock <- socket AF_UNIX Stream defaultProtocol | ||
| 78 | connect sock $ SockAddrUnix (traceId sockPath) | ||
| 79 | return sock | ||
| 80 | |||
| 81 | mpvResponse :: FromJSON v => MpvResponse -> IO v | ||
| 82 | mpvResponse (MpvError str) = throwIO $ MpvException str | ||
| 83 | mpvResponse (MpvSuccess Nothing) = throwIO MpvNoValue | ||
| 84 | mpvResponse (MpvSuccess (Just v)) = case fromJSON v of | ||
| 85 | Success v' -> return v' | ||
| 86 | Error str -> throwIO $ MpvNoParse str | ||
| 87 | |||
| 88 | mpv :: FilePath -> MpvCommand -> IO MpvResponse | ||
| 89 | mpv sockPath cmd = mpvSocket sockPath $ \sock -> do | ||
| 90 | let message = (`BS.append` "\n") . LBS.toStrict . encode $ Object [("command", toJSON cmd)] | ||
| 91 | traceIO $ show message | ||
| 92 | sendAll sock message | ||
| 93 | let recvAll = do | ||
| 94 | prefix <- recv sock 4096 | ||
| 95 | if | ||
| 96 | | (prefix', rest) <- CBS.break (== '\n') prefix | ||
| 97 | , not (BS.null rest) -> return prefix' | ||
| 98 | | BS.null prefix -> return prefix | ||
| 99 | | otherwise -> BS.append prefix <$> recvAll | ||
| 100 | response <- recvAll | ||
| 101 | traceIO $ show response | ||
| 102 | either (ioError . userError) return . traceShowId $ eitherDecodeStrict' response | ||
| 103 | |||
| 104 | mpvDir :: Exception e => FilePath -> (FilePath -> [(FilePath, Either e MpvResponse)] -> Maybe MpvCommand) -> IO [(FilePath, Either e MpvResponse)] | ||
| 105 | mpvDir dir step = do | ||
| 106 | socks <- filter (".sock" `isSuffixOf`) <$> getDirectoryContents dir | ||
| 107 | go [] socks | ||
| 108 | where | ||
| 109 | go acc [] = return acc | ||
| 110 | go acc (sock:socks) | ||
| 111 | | Just cmd <- step sock acc = do | ||
| 112 | res <- try $ mpv (dir </> sock) cmd | ||
| 113 | go ((sock, res) : acc) socks | ||
| 114 | | otherwise = | ||
| 115 | go acc socks | ||
| 116 | |||
| 117 | mpvAll :: FilePath -> MpvCommand -> IO [MpvResponse] | ||
| 118 | mpvAll dir cmd = do | ||
| 119 | results <- map snd <$> (mpvDir dir (\_ _ -> Just cmd) :: IO [(FilePath, Either SomeException MpvResponse)]) | ||
| 120 | mapM (either throwIO return) results | ||
| 121 | |||
| 122 | mpvOne :: FilePath -> MpvCommand -> IO (Maybe MpvResponse) | ||
| 123 | mpvOne dir cmd = listToMaybe . snd . partitionEithers . map snd <$> (mpvDir dir step :: IO [(FilePath, Either SomeException MpvResponse)]) | ||
| 124 | where | ||
| 125 | step _ results | ||
| 126 | | any (isRight . snd) results = Nothing | ||
| 127 | | otherwise = Just cmd | ||
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyPass.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyPass.hs deleted file mode 100644 index 1caefae5..00000000 --- a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyPass.hs +++ /dev/null | |||
| @@ -1,94 +0,0 @@ | |||
| 1 | module XMonad.Prompt.MyPass | ||
| 2 | ( | ||
| 3 | -- * Usages | ||
| 4 | -- $usages | ||
| 5 | mkPassPrompt | ||
| 6 | ) where | ||
| 7 | |||
| 8 | import Control.Monad (liftM) | ||
| 9 | import XMonad.Core | ||
| 10 | import XMonad.Prompt ( XPrompt | ||
| 11 | , showXPrompt | ||
| 12 | , commandToComplete | ||
| 13 | , nextCompletion | ||
| 14 | , getNextCompletion | ||
| 15 | , XPConfig | ||
| 16 | , mkXPrompt | ||
| 17 | , searchPredicate) | ||
| 18 | import System.Directory (getHomeDirectory) | ||
| 19 | import System.FilePath (takeExtension, dropExtension, combine) | ||
| 20 | import System.Posix.Env (getEnv) | ||
| 21 | import XMonad.Util.Run (runProcessWithInput) | ||
| 22 | |||
| 23 | -- $usages | ||
| 24 | -- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@: | ||
| 25 | -- | ||
| 26 | -- > import XMonad.Prompt.Pass | ||
| 27 | -- | ||
| 28 | -- Then add a keybinding for 'passPrompt', 'passGeneratePrompt' or 'passRemovePrompt': | ||
| 29 | -- | ||
| 30 | -- > , ((modMask x , xK_p) , passPrompt xpconfig) | ||
| 31 | -- > , ((modMask x .|. controlMask, xK_p) , passGeneratePrompt xpconfig) | ||
| 32 | -- > , ((modMask x .|. controlMask .|. shiftMask, xK_p), passRemovePrompt xpconfig) | ||
| 33 | -- | ||
| 34 | -- For detailed instructions on: | ||
| 35 | -- | ||
| 36 | -- - editing your key bindings, see "XMonad.Doc.Extending#Editing_key_bindings". | ||
| 37 | -- | ||
| 38 | -- - how to setup the password storage, see <http://git.zx2c4.com/password-store/about/> | ||
| 39 | -- | ||
| 40 | |||
| 41 | type Predicate = String -> String -> Bool | ||
| 42 | |||
| 43 | getPassCompl :: [String] -> Predicate -> String -> IO [String] | ||
| 44 | getPassCompl compls p s | ||
| 45 | | length s <= minL | ||
| 46 | , all ((> minL) . length) compls = return [] | ||
| 47 | | otherwise = do return $ filter (p s) compls | ||
| 48 | where | ||
| 49 | minL = 3 | ||
| 50 | |||
| 51 | type PromptLabel = String | ||
| 52 | |||
| 53 | data Pass = Pass PromptLabel | ||
| 54 | |||
| 55 | instance XPrompt Pass where | ||
| 56 | showXPrompt (Pass prompt) = prompt ++ ": " | ||
| 57 | commandToComplete _ c = c | ||
| 58 | nextCompletion _ = getNextCompletion | ||
| 59 | |||
| 60 | -- | Default password store folder in $HOME/.password-store | ||
| 61 | -- | ||
| 62 | passwordStoreFolderDefault :: String -> String | ||
| 63 | passwordStoreFolderDefault home = combine home ".password-store" | ||
| 64 | |||
| 65 | -- | Compute the password store's location. | ||
| 66 | -- Use the PASSWORD_STORE_DIR environment variable to set the password store. | ||
| 67 | -- If empty, return the password store located in user's home. | ||
| 68 | -- | ||
| 69 | passwordStoreFolder :: IO String | ||
| 70 | passwordStoreFolder = | ||
| 71 | getEnv "PASSWORD_STORE_DIR" >>= computePasswordStoreDir | ||
| 72 | where computePasswordStoreDir Nothing = liftM passwordStoreFolderDefault getHomeDirectory | ||
| 73 | computePasswordStoreDir (Just storeDir) = return storeDir | ||
| 74 | |||
| 75 | -- | A pass prompt factory | ||
| 76 | -- | ||
| 77 | mkPassPrompt :: PromptLabel -> (String -> X ()) -> XPConfig -> X () | ||
| 78 | mkPassPrompt promptLabel passwordFunction xpconfig = do | ||
| 79 | passwords <- io (passwordStoreFolder >>= getPasswords) | ||
| 80 | mkXPrompt (Pass promptLabel) xpconfig (getPassCompl passwords $ searchPredicate xpconfig) passwordFunction | ||
| 81 | |||
| 82 | -- | Retrieve the list of passwords from the password storage 'passwordStoreDir | ||
| 83 | getPasswords :: FilePath -> IO [String] | ||
| 84 | getPasswords passwordStoreDir = do | ||
| 85 | files <- runProcessWithInput "find" [ | ||
| 86 | passwordStoreDir, | ||
| 87 | "-type", "f", | ||
| 88 | "-name", "*.gpg", | ||
| 89 | "-printf", "%P\n"] [] | ||
| 90 | return $ map removeGpgExtension $ lines files | ||
| 91 | |||
| 92 | removeGpgExtension :: String -> String | ||
| 93 | removeGpgExtension file | takeExtension file == ".gpg" = dropExtension file | ||
| 94 | | otherwise = file | ||
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs deleted file mode 100644 index c268f87d..00000000 --- a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs +++ /dev/null | |||
| @@ -1,105 +0,0 @@ | |||
| 1 | module XMonad.Prompt.MyShell | ||
| 2 | ( Shell (..) | ||
| 3 | , shellPrompt | ||
| 4 | , prompt | ||
| 5 | , safePrompt | ||
| 6 | , unsafePrompt | ||
| 7 | , getCommands | ||
| 8 | , getShellCompl | ||
| 9 | , split | ||
| 10 | ) where | ||
| 11 | |||
| 12 | import Codec.Binary.UTF8.String (encodeString) | ||
| 13 | import Control.Exception as E | ||
| 14 | import Control.Monad (forM) | ||
| 15 | import Data.List (isPrefixOf) | ||
| 16 | import System.Directory (doesDirectoryExist, getDirectoryContents) | ||
| 17 | import System.Environment (getEnv) | ||
| 18 | import System.Posix.Files (getFileStatus, isDirectory) | ||
| 19 | |||
| 20 | import XMonad hiding (config) | ||
| 21 | import XMonad.Prompt | ||
| 22 | import XMonad.Util.Run | ||
| 23 | |||
| 24 | econst :: Monad m => a -> IOException -> m a | ||
| 25 | econst = const . return | ||
| 26 | |||
| 27 | data Shell = Shell String | ||
| 28 | |||
| 29 | instance XPrompt Shell where | ||
| 30 | showXPrompt (Shell q) = q | ||
| 31 | completionToCommand _ = escape | ||
| 32 | |||
| 33 | shellPrompt :: String -> XPConfig -> X () | ||
| 34 | shellPrompt q c = do | ||
| 35 | cmds <- io getCommands | ||
| 36 | mkXPrompt (Shell q) c (getShellCompl cmds) spawn | ||
| 37 | |||
| 38 | {- $spawns | ||
| 39 | See safe and unsafeSpawn in "XMonad.Util.Run". | ||
| 40 | prompt is an alias for safePrompt; | ||
| 41 | safePrompt and unsafePrompt work on the same principles, but will use | ||
| 42 | XPrompt to interactively query the user for input; the appearance is | ||
| 43 | set by passing an XPConfig as the second argument. The first argument | ||
| 44 | is the program to be run with the interactive input. | ||
| 45 | You would use these like this: | ||
| 46 | |||
| 47 | > , ((modm, xK_b), safePrompt "firefox" greenXPConfig) | ||
| 48 | > , ((modm .|. shiftMask, xK_c), prompt ("xterm" ++ " -e") greenXPConfig) | ||
| 49 | |||
| 50 | Note that you want to use safePrompt for Firefox input, as Firefox | ||
| 51 | wants URLs, and unsafePrompt for the XTerm example because this allows | ||
| 52 | you to easily start a terminal executing an arbitrary command, like | ||
| 53 | 'top'. -} | ||
| 54 | |||
| 55 | prompt, unsafePrompt, safePrompt :: String -> FilePath -> XPConfig -> X () | ||
| 56 | prompt = unsafePrompt | ||
| 57 | safePrompt q c config = mkXPrompt (Shell q) config (getShellCompl [c]) run | ||
| 58 | where run = safeSpawn c . return | ||
| 59 | unsafePrompt q c config = mkXPrompt (Shell q) config (getShellCompl [c]) run | ||
| 60 | where run a = unsafeSpawn $ c ++ " " ++ a | ||
| 61 | |||
| 62 | getShellCompl :: [String] -> String -> IO [String] | ||
| 63 | getShellCompl cmds s | s == "" || last s == ' ' = return [] | ||
| 64 | | otherwise = do | ||
| 65 | f <- fmap lines $ runProcessWithInput "bash" [] ("compgen -A file -- " | ||
| 66 | ++ s ++ "\n") | ||
| 67 | files <- case f of | ||
| 68 | [x] -> do fs <- getFileStatus (encodeString x) | ||
| 69 | if isDirectory fs then return [x ++ "/"] | ||
| 70 | else return [x] | ||
| 71 | _ -> return f | ||
| 72 | return . uniqSort $ files ++ commandCompletionFunction cmds s | ||
| 73 | |||
| 74 | commandCompletionFunction :: [String] -> String -> [String] | ||
| 75 | commandCompletionFunction cmds str | '/' `elem` str = [] | ||
| 76 | | otherwise = filter (isPrefixOf str) cmds | ||
| 77 | |||
| 78 | getCommands :: IO [String] | ||
| 79 | getCommands = do | ||
| 80 | p <- getEnv "PATH" `E.catch` econst [] | ||
| 81 | let ds = filter (/= "") $ split ':' p | ||
| 82 | es <- forM ds $ \d -> do | ||
| 83 | exists <- doesDirectoryExist d | ||
| 84 | if exists | ||
| 85 | then getDirectoryContents d | ||
| 86 | else return [] | ||
| 87 | return . uniqSort . filter ((/= '.') . head) . concat $ es | ||
| 88 | |||
| 89 | split :: Eq a => a -> [a] -> [[a]] | ||
| 90 | split _ [] = [] | ||
| 91 | split e l = | ||
| 92 | f : split e (rest ls) | ||
| 93 | where | ||
| 94 | (f,ls) = span (/=e) l | ||
| 95 | rest s | s == [] = [] | ||
| 96 | | otherwise = tail s | ||
| 97 | |||
| 98 | escape :: String -> String | ||
| 99 | escape [] = "" | ||
| 100 | escape (x:xs) | ||
| 101 | | isSpecialChar x = '\\' : x : escape xs | ||
| 102 | | otherwise = x : escape xs | ||
| 103 | |||
| 104 | isSpecialChar :: Char -> Bool | ||
| 105 | isSpecialChar = flip elem " &\\@\"'#?$*()[]{};" | ||
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MySsh.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MySsh.hs deleted file mode 100644 index 998c533e..00000000 --- a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MySsh.hs +++ /dev/null | |||
| @@ -1,246 +0,0 @@ | |||
| 1 | module XMonad.Prompt.MySsh | ||
| 2 | ( -- * Usage | ||
| 3 | -- $usage | ||
| 4 | sshPrompt, | ||
| 5 | Ssh, | ||
| 6 | Override (..), | ||
| 7 | mkOverride, | ||
| 8 | Conn (..), | ||
| 9 | moshCmd, | ||
| 10 | moshCmd', | ||
| 11 | sshCmd, | ||
| 12 | inTmux, | ||
| 13 | withEnv | ||
| 14 | ) where | ||
| 15 | |||
| 16 | import XMonad | ||
| 17 | import XMonad.Util.Run | ||
| 18 | import XMonad.Prompt | ||
| 19 | |||
| 20 | import System.Directory | ||
| 21 | import System.Environment | ||
| 22 | import qualified Control.Exception as E | ||
| 23 | |||
| 24 | import Control.Monad | ||
| 25 | import Data.Maybe | ||
| 26 | |||
| 27 | import Text.Parsec.String | ||
| 28 | import Text.Parsec | ||
| 29 | import Data.Char (isSpace) | ||
| 30 | |||
| 31 | econst :: Monad m => a -> E.IOException -> m a | ||
| 32 | econst = const . return | ||
| 33 | |||
| 34 | -- $usage | ||
| 35 | -- 1. In your @~\/.xmonad\/xmonad.hs@: | ||
| 36 | -- | ||
| 37 | -- > import XMonad.Prompt | ||
| 38 | -- > import XMonad.Prompt.Ssh | ||
| 39 | -- | ||
| 40 | -- 2. In your keybindings add something like: | ||
| 41 | -- | ||
| 42 | -- > , ((modm .|. controlMask, xK_s), sshPrompt defaultXPConfig) | ||
| 43 | -- | ||
| 44 | -- Keep in mind, that if you want to use the completion you have to | ||
| 45 | -- disable the "HashKnownHosts" option in your ssh_config | ||
| 46 | -- | ||
| 47 | -- For detailed instruction on editing the key binding see | ||
| 48 | -- "XMonad.Doc.Extending#Editing_key_bindings". | ||
| 49 | |||
| 50 | data Override = Override | ||
| 51 | { oUser :: Maybe String | ||
| 52 | , oHost :: String | ||
| 53 | , oPort :: Maybe Int | ||
| 54 | , oCommand :: Conn -> String | ||
| 55 | } | ||
| 56 | |||
| 57 | mkOverride = Override { oUser = Nothing, oHost = "", oPort = Nothing, oCommand = sshCmd } | ||
| 58 | sshCmd c = concat | ||
| 59 | [ "ssh -t " | ||
| 60 | , if isJust $ cUser c then (fromJust $ cUser c) ++ "@" else "" | ||
| 61 | , cHost c | ||
| 62 | , if isJust $ cPort c then " -p " ++ (show $ fromJust $ cPort c) else "" | ||
| 63 | , " -- " | ||
| 64 | , cCommand c | ||
| 65 | ] | ||
| 66 | moshCmd c = concat | ||
| 67 | [ "mosh " | ||
| 68 | , if isJust $ cUser c then (fromJust $ cUser c) ++ "@" else "" | ||
| 69 | , cHost c | ||
| 70 | , if isJust $ cPort c then " --ssh=\"ssh -p " ++ (show $ fromJust $ cPort c) ++ "\"" else "" | ||
| 71 | , " -- " | ||
| 72 | , cCommand c | ||
| 73 | ] | ||
| 74 | moshCmd' p c = concat | ||
| 75 | [ "mosh " | ||
| 76 | , "--server=" ++ p ++ " " | ||
| 77 | , if isJust $ cUser c then (fromJust $ cUser c) ++ "@" else "" | ||
| 78 | , cHost c | ||
| 79 | , if isJust $ cPort c then " --ssh=\"ssh -p " ++ (show $ fromJust $ cPort c) ++ "\"" else "" | ||
| 80 | , " -- " | ||
| 81 | , cCommand c | ||
| 82 | ] | ||
| 83 | inTmux Nothing c | ||
| 84 | | null $ cCommand c = c { cCommand = "tmux new-session" } | ||
| 85 | | otherwise = c { cCommand = "tmux new-session \"" ++ (cCommand c) ++ "\"" } | ||
| 86 | inTmux (Just h) c | ||
| 87 | | null $ cCommand c = c { cCommand = "tmux new-session -As " <> h } | ||
| 88 | | otherwise = c { cCommand = "tmux new-session \"" ++ (cCommand c) ++ "\"" } | ||
| 89 | withEnv :: [(String, String)] -> Conn -> Conn | ||
| 90 | withEnv envs c = c { cCommand = "env" ++ (concat $ map (\(n, v) -> ' ' : (n ++ "=" ++ v)) envs) ++ " " ++ (cCommand c) } | ||
| 91 | |||
| 92 | data Conn = Conn | ||
| 93 | { cUser :: Maybe String | ||
| 94 | , cHost :: String | ||
| 95 | , cPort :: Maybe Int | ||
| 96 | , cCommand :: String | ||
| 97 | } deriving (Eq, Show, Read) | ||
| 98 | |||
| 99 | data Ssh = Ssh | ||
| 100 | |||
| 101 | instance XPrompt Ssh where | ||
| 102 | showXPrompt Ssh = "SSH to: " | ||
| 103 | commandToComplete _ c = c | ||
| 104 | nextCompletion _ = getNextCompletion | ||
| 105 | |||
| 106 | toConn :: String -> Maybe Conn | ||
| 107 | toConn = toConn' . parse connParser "(unknown)" | ||
| 108 | toConn' :: Either ParseError Conn -> Maybe Conn | ||
| 109 | toConn' (Left _) = Nothing | ||
| 110 | toConn' (Right a) = Just a | ||
| 111 | |||
| 112 | connParser :: Parser Conn | ||
| 113 | connParser = do | ||
| 114 | spaces | ||
| 115 | user' <- optionMaybe $ try $ do | ||
| 116 | str <- many1 $ satisfy (\c -> (not $ isSpace c) && (c /= '@')) | ||
| 117 | char '@' | ||
| 118 | return str | ||
| 119 | host' <- many1 $ satisfy (not . isSpace) | ||
| 120 | port' <- optionMaybe $ try $ do | ||
| 121 | space | ||
| 122 | string "-p" | ||
| 123 | spaces | ||
| 124 | int <- many1 digit | ||
| 125 | (space >> return ()) <|> eof | ||
| 126 | return $ (read int :: Int) | ||
| 127 | spaces | ||
| 128 | command' <- many anyChar | ||
| 129 | eof | ||
| 130 | return $ Conn | ||
| 131 | { cHost = host' | ||
| 132 | , cUser = user' | ||
| 133 | , cPort = port' | ||
| 134 | , cCommand = command' | ||
| 135 | } | ||
| 136 | |||
| 137 | sshPrompt :: [Override] -> XPConfig -> X () | ||
| 138 | sshPrompt o c = do | ||
| 139 | sc <- io sshComplList | ||
| 140 | mkXPrompt Ssh c (mkComplFunFromList c sc) $ ssh o | ||
| 141 | |||
| 142 | ssh :: [Override] -> String -> X () | ||
| 143 | ssh overrides str = do | ||
| 144 | let cmd = applyOverrides overrides str | ||
| 145 | liftIO $ putStr "SSH Command: " | ||
| 146 | liftIO $ putStrLn cmd | ||
| 147 | runInTerm "" cmd | ||
| 148 | |||
| 149 | applyOverrides :: [Override] -> String -> String | ||
| 150 | applyOverrides [] str = "ssh " ++ str | ||
| 151 | applyOverrides (o:os) str = case (applyOverride o str) of | ||
| 152 | Just str -> str | ||
| 153 | Nothing -> applyOverrides os str | ||
| 154 | |||
| 155 | applyOverride :: Override -> String -> Maybe String | ||
| 156 | applyOverride o str = let | ||
| 157 | conn = toConn str | ||
| 158 | in | ||
| 159 | if isNothing conn then Nothing else | ||
| 160 | case (fromJust conn) `matches` o of | ||
| 161 | True -> Just $ (oCommand o) (fromJust conn) | ||
| 162 | False -> Nothing | ||
| 163 | |||
| 164 | matches :: Conn -> Override -> Bool | ||
| 165 | a `matches` b = and | ||
| 166 | [ justBool (cUser a) (oUser b) (==) | ||
| 167 | , (cHost a) == (oHost b) | ||
| 168 | , justBool (cPort a) (oPort b) (==) | ||
| 169 | ] | ||
| 170 | |||
| 171 | justBool :: Eq a => Maybe a -> Maybe a -> (a -> a -> Bool) -> Bool | ||
| 172 | justBool Nothing _ _ = True | ||
| 173 | justBool _ Nothing _ = True | ||
| 174 | justBool (Just a) (Just b) match = a `match` b | ||
| 175 | |||
| 176 | sshComplList :: IO [String] | ||
| 177 | sshComplList = uniqSort `fmap` liftM2 (++) sshComplListLocal sshComplListGlobal | ||
| 178 | |||
| 179 | sshComplListLocal :: IO [String] | ||
| 180 | sshComplListLocal = do | ||
| 181 | h <- getEnv "HOME" | ||
| 182 | s1 <- sshComplListFile $ h ++ "/.ssh/known_hosts" | ||
| 183 | s2 <- sshComplListConf $ h ++ "/.ssh/config" | ||
| 184 | return $ s1 ++ s2 | ||
| 185 | |||
| 186 | sshComplListGlobal :: IO [String] | ||
| 187 | sshComplListGlobal = do | ||
| 188 | env <- getEnv "SSH_KNOWN_HOSTS" `E.catch` econst "/nonexistent" | ||
| 189 | fs <- mapM fileExists [ env | ||
| 190 | , "/usr/local/etc/ssh/ssh_known_hosts" | ||
| 191 | , "/usr/local/etc/ssh_known_hosts" | ||
| 192 | , "/etc/ssh/ssh_known_hosts" | ||
| 193 | , "/etc/ssh_known_hosts" | ||
| 194 | ] | ||
| 195 | case catMaybes fs of | ||
| 196 | [] -> return [] | ||
| 197 | (f:_) -> sshComplListFile' f | ||
| 198 | |||
| 199 | sshComplListFile :: String -> IO [String] | ||
| 200 | sshComplListFile kh = do | ||
| 201 | f <- doesFileExist kh | ||
| 202 | if f then sshComplListFile' kh | ||
| 203 | else return [] | ||
| 204 | |||
| 205 | sshComplListFile' :: String -> IO [String] | ||
| 206 | sshComplListFile' kh = do | ||
| 207 | l <- readFile kh | ||
| 208 | return $ map (getWithPort . takeWhile (/= ',') . concat . take 1 . words) | ||
| 209 | $ filter nonComment | ||
| 210 | $ lines l | ||
| 211 | |||
| 212 | sshComplListConf :: String -> IO [String] | ||
| 213 | sshComplListConf kh = do | ||
| 214 | f <- doesFileExist kh | ||
| 215 | if f then sshComplListConf' kh | ||
| 216 | else return [] | ||
| 217 | |||
| 218 | sshComplListConf' :: String -> IO [String] | ||
| 219 | sshComplListConf' kh = do | ||
| 220 | l <- readFile kh | ||
| 221 | return $ map (!!1) | ||
| 222 | $ filter isHost | ||
| 223 | $ map words | ||
| 224 | $ lines l | ||
| 225 | where | ||
| 226 | isHost ws = take 1 ws == ["Host"] && length ws > 1 | ||
| 227 | |||
| 228 | fileExists :: String -> IO (Maybe String) | ||
| 229 | fileExists kh = do | ||
| 230 | f <- doesFileExist kh | ||
| 231 | if f then return $ Just kh | ||
| 232 | else return Nothing | ||
| 233 | |||
| 234 | nonComment :: String -> Bool | ||
| 235 | nonComment [] = False | ||
| 236 | nonComment ('#':_) = False | ||
| 237 | nonComment ('|':_) = False -- hashed, undecodeable | ||
| 238 | nonComment _ = True | ||
| 239 | |||
| 240 | getWithPort :: String -> String | ||
| 241 | getWithPort ('[':str) = host ++ " -p " ++ port | ||
| 242 | where (host,p) = break (==']') str | ||
| 243 | port = case p of | ||
| 244 | ']':':':x -> x | ||
| 245 | _ -> "22" | ||
| 246 | getWithPort str = str | ||
diff --git a/accounts/gkleen@sif/xmonad/package.yaml b/accounts/gkleen@sif/xmonad/package.yaml deleted file mode 100644 index f65137af..00000000 --- a/accounts/gkleen@sif/xmonad/package.yaml +++ /dev/null | |||
| @@ -1,31 +0,0 @@ | |||
| 1 | name: xmonad-yggdrasil | ||
| 2 | |||
| 3 | executables: | ||
| 4 | xmonad: | ||
| 5 | dependencies: | ||
| 6 | - base | ||
| 7 | - xmonad | ||
| 8 | - xmonad-contrib | ||
| 9 | - aeson | ||
| 10 | - bytestring | ||
| 11 | - text | ||
| 12 | - temporary | ||
| 13 | - filepath | ||
| 14 | - directory | ||
| 15 | - network | ||
| 16 | - unix | ||
| 17 | - utf8-string | ||
| 18 | - parsec | ||
| 19 | - process | ||
| 20 | - mtl | ||
| 21 | - X11 | ||
| 22 | - transformers | ||
| 23 | - containers | ||
| 24 | - hostname | ||
| 25 | - libnotify | ||
| 26 | - taffybar | ||
| 27 | |||
| 28 | main: xmonad.hs | ||
| 29 | source-dirs: | ||
| 30 | - . | ||
| 31 | - lib | ||
diff --git a/accounts/gkleen@sif/xmonad/stack.nix b/accounts/gkleen@sif/xmonad/stack.nix deleted file mode 100644 index 17a49e04..00000000 --- a/accounts/gkleen@sif/xmonad/stack.nix +++ /dev/null | |||
| @@ -1,17 +0,0 @@ | |||
| 1 | { ghc, nixpkgs ? import ./nixpkgs.nix {} }: | ||
| 2 | |||
| 3 | let | ||
| 4 | haskellPackages = import ./stackage.nix { inherit nixpkgs; }; | ||
| 5 | inherit (nixpkgs {}) pkgs; | ||
| 6 | in pkgs.haskell.lib.buildStackProject { | ||
| 7 | inherit ghc; | ||
| 8 | inherit (haskellPackages) stack; | ||
| 9 | name = "stackenv"; | ||
| 10 | buildInputs = (with pkgs; | ||
| 11 | [ xorg.libX11 xorg.libXrandr xorg.libXinerama xorg.libXScrnSaver xorg.libXext xorg.libXft | ||
| 12 | cairo | ||
| 13 | glib | ||
| 14 | ]) ++ (with haskellPackages; | ||
| 15 | [ | ||
| 16 | ]); | ||
| 17 | } | ||
diff --git a/accounts/gkleen@sif/xmonad/stack.yaml b/accounts/gkleen@sif/xmonad/stack.yaml deleted file mode 100644 index b8ed1147..00000000 --- a/accounts/gkleen@sif/xmonad/stack.yaml +++ /dev/null | |||
| @@ -1,10 +0,0 @@ | |||
| 1 | nix: | ||
| 2 | enable: true | ||
| 3 | shell-file: stack.nix | ||
| 4 | |||
| 5 | resolver: lts-13.21 | ||
| 6 | |||
| 7 | packages: | ||
| 8 | - . | ||
| 9 | |||
| 10 | extra-deps: [] | ||
diff --git a/accounts/gkleen@sif/xmonad/xmonad-yggdrasil.nix b/accounts/gkleen@sif/xmonad/xmonad-yggdrasil.nix deleted file mode 100644 index 7c853619..00000000 --- a/accounts/gkleen@sif/xmonad/xmonad-yggdrasil.nix +++ /dev/null | |||
| @@ -1,21 +0,0 @@ | |||
| 1 | { mkDerivation, aeson, base, bytestring, containers, directory | ||
| 2 | , filepath, hostname, hpack, mtl, network, parsec, process, lib | ||
| 3 | , temporary, transformers, unix, utf8-string, X11, xmonad | ||
| 4 | , xmonad-contrib, libnotify, taffybar | ||
| 5 | }: | ||
| 6 | mkDerivation { | ||
| 7 | pname = "xmonad-yggdrasil"; | ||
| 8 | version = "0.0.0"; | ||
| 9 | src = ./.; | ||
| 10 | isLibrary = false; | ||
| 11 | isExecutable = true; | ||
| 12 | libraryToolDepends = [ hpack ]; | ||
| 13 | executableHaskellDepends = [ | ||
| 14 | aeson base bytestring containers directory filepath hostname mtl | ||
| 15 | network parsec process temporary transformers unix utf8-string X11 | ||
| 16 | xmonad xmonad-contrib libnotify taffybar | ||
| 17 | ]; | ||
| 18 | preConfigure = "hpack"; | ||
| 19 | license = "unknown"; | ||
| 20 | hydraPlatforms = lib.platforms.none; | ||
| 21 | } | ||
diff --git a/accounts/gkleen@sif/xmonad/xmonad.hs b/accounts/gkleen@sif/xmonad/xmonad.hs deleted file mode 100644 index a44d3bb7..00000000 --- a/accounts/gkleen@sif/xmonad/xmonad.hs +++ /dev/null | |||
| @@ -1,939 +0,0 @@ | |||
| 1 | {-# LANGUAGE TupleSections, ViewPatterns, OverloadedStrings, FlexibleInstances, UndecidableInstances, MultiWayIf, NumDecimals #-} | ||
| 2 | |||
| 3 | import XMonad | ||
| 4 | import XMonad.Hooks.DynamicLog | ||
| 5 | import XMonad.Hooks.ManageDocks | ||
| 6 | import XMonad.Util.Run hiding (proc) | ||
| 7 | import XMonad.Util.Loggers | ||
| 8 | import XMonad.Util.EZConfig(additionalKeys) | ||
| 9 | import System.IO | ||
| 10 | import System.IO.Error | ||
| 11 | import System.Environment | ||
| 12 | import Data.Map (Map) | ||
| 13 | import qualified Data.Map as Map | ||
| 14 | import qualified XMonad.StackSet as W | ||
| 15 | import System.Exit | ||
| 16 | import Control.Monad.State (get) | ||
| 17 | -- import XMonad.Layout.Spiral | ||
| 18 | import Data.Ratio | ||
| 19 | import Data.List | ||
| 20 | import Data.Char | ||
| 21 | import Data.Maybe (fromMaybe, listToMaybe, maybeToList, catMaybes, isJust) | ||
| 22 | import XMonad.Layout.Tabbed | ||
| 23 | import XMonad.Prompt | ||
| 24 | import XMonad.Prompt.Input | ||
| 25 | import XMonad.Util.Scratchpad | ||
| 26 | import XMonad.Util.NamedScratchpad | ||
| 27 | import XMonad.Util.Ungrab | ||
| 28 | import Control.Monad (sequence, liftM, liftM2, join, void) | ||
| 29 | import XMonad.Util.WorkspaceCompare | ||
| 30 | import XMonad.Layout.NoBorders | ||
| 31 | import XMonad.Layout.PerWorkspace | ||
| 32 | import XMonad.Layout.SimplestFloat | ||
| 33 | import XMonad.Layout.Renamed | ||
| 34 | import XMonad.Layout.Reflect | ||
| 35 | import XMonad.Layout.OnHost | ||
| 36 | import XMonad.Layout.Combo | ||
| 37 | import XMonad.Layout.ComboP | ||
| 38 | import XMonad.Layout.Column | ||
| 39 | import XMonad.Layout.TwoPane | ||
| 40 | import XMonad.Layout.IfMax | ||
| 41 | import XMonad.Layout.LayoutBuilder | ||
| 42 | import XMonad.Layout.WindowNavigation | ||
| 43 | import XMonad.Layout.Dwindle | ||
| 44 | import XMonad.Layout.TrackFloating | ||
| 45 | import System.Process | ||
| 46 | import System.Directory (removeFile) | ||
| 47 | import System.Posix.Files | ||
| 48 | import System.FilePath ((</>)) | ||
| 49 | import Control.Concurrent | ||
| 50 | import System.Posix.Process (getProcessID) | ||
| 51 | import System.IO.Error | ||
| 52 | import System.IO | ||
| 53 | import XMonad.Hooks.ManageHelpers hiding (CW) | ||
| 54 | import XMonad.Hooks.UrgencyHook as U | ||
| 55 | import XMonad.Hooks.EwmhDesktops | ||
| 56 | import XMonad.StackSet (RationalRect (..)) | ||
| 57 | import Control.Monad (when, filterM, (<=<)) | ||
| 58 | import Graphics.X11.ExtraTypes.XF86 | ||
| 59 | import XMonad.Util.Cursor | ||
| 60 | import XMonad.Actions.Warp | ||
| 61 | import XMonad.Actions.FloatKeys | ||
| 62 | import XMonad.Util.SpawnOnce | ||
| 63 | import System.Directory | ||
| 64 | import System.FilePath | ||
| 65 | import XMonad.Actions.CopyWindow | ||
| 66 | import XMonad.Hooks.ServerMode | ||
| 67 | import XMonad.Actions.Commands | ||
| 68 | import XMonad.Actions.CycleWS | ||
| 69 | import XMonad.Actions.RotSlaves | ||
| 70 | import XMonad.Actions.UpdatePointer | ||
| 71 | import XMonad.Prompt.Window | ||
| 72 | import Data.IORef | ||
| 73 | import Data.Monoid | ||
| 74 | import Data.String | ||
| 75 | import qualified XMonad.Actions.PhysicalScreens as P | ||
| 76 | |||
| 77 | import XMonad.Layout.IM | ||
| 78 | |||
| 79 | import System.Taffybar.Support.PagerHints (pagerHints) | ||
| 80 | |||
| 81 | import XMonad.Prompt.MyShell | ||
| 82 | import XMonad.Prompt.MyPass | ||
| 83 | import XMonad.Prompt.MySsh | ||
| 84 | |||
| 85 | import XMonad.Mpv | ||
| 86 | |||
| 87 | import Network.HostName | ||
| 88 | |||
| 89 | import Control.Applicative ((<$>)) | ||
| 90 | |||
| 91 | import Libnotify as Notify hiding (appName) | ||
| 92 | import qualified Libnotify as Notify (appName) | ||
| 93 | import Libnotify (Notification) | ||
| 94 | -- import System.Information.Battery | ||
| 95 | |||
| 96 | import Data.Int (Int32) | ||
| 97 | |||
| 98 | import System.Posix.Process | ||
| 99 | import System.Posix.Signals | ||
| 100 | import System.Posix.IO as Posix | ||
| 101 | import Control.Exception | ||
| 102 | |||
| 103 | import System.IO.Unsafe | ||
| 104 | |||
| 105 | import Control.Monad.Trans.Class | ||
| 106 | import Control.Monad.Trans.Maybe | ||
| 107 | |||
| 108 | import Data.Fixed (Micro) | ||
| 109 | |||
| 110 | import qualified Data.Text as Text | ||
| 111 | import Data.Ord (comparing) | ||
| 112 | import Debug.Trace | ||
| 113 | |||
| 114 | instance MonadIO m => IsString (m ()) where | ||
| 115 | fromString = spawn | ||
| 116 | |||
| 117 | type KeyMap = Map (ButtonMask, KeySym) (X ()) | ||
| 118 | |||
| 119 | data Host = Host | ||
| 120 | { hName :: HostName | ||
| 121 | , hManageHook :: ManageHook | ||
| 122 | , hWsp :: Integer -> WorkspaceId | ||
| 123 | , hCoWsp :: String -> Maybe WorkspaceId | ||
| 124 | , hKeysMod :: XConfig Layout -> (KeyMap -> KeyMap) | ||
| 125 | , hScreens :: [P.PhysicalScreen] | ||
| 126 | , hKbLayouts :: [(String, Maybe String)] | ||
| 127 | , hCmds :: X [(String, X ())] | ||
| 128 | , hKeyUpKeys :: XConfig Layout -> KeyMap | ||
| 129 | } | ||
| 130 | |||
| 131 | defaultHost = Host { hName = "unkown" | ||
| 132 | , hManageHook = composeOne [manageScratchTerm] | ||
| 133 | , hWsp = show | ||
| 134 | , hCoWsp = const Nothing | ||
| 135 | , hKeysMod = const id | ||
| 136 | , hScreens = [0,1..] | ||
| 137 | , hKbLayouts = [ ("us", Just "dvp") | ||
| 138 | , ("us", Nothing) | ||
| 139 | , ("de", Nothing) | ||
| 140 | ] | ||
| 141 | , hCmds = return [] | ||
| 142 | , hKeyUpKeys = const Map.empty | ||
| 143 | } | ||
| 144 | |||
| 145 | browser :: String | ||
| 146 | browser = "env MOZ_USE_XINPUT2=1 firefox" | ||
| 147 | |||
| 148 | gray, darkGray, red, green :: String | ||
| 149 | gray = "#808080" | ||
| 150 | darkGray = "#202020" | ||
| 151 | red = "#800000" | ||
| 152 | green = "#008000" | ||
| 153 | |||
| 154 | hostFromName :: HostName -> Host | ||
| 155 | hostFromName h@("vali") = defaultHost { hName = h | ||
| 156 | , hManageHook = composeOne $ catMaybes [ Just manageScratchTerm | ||
| 157 | , assign "web" $ className =? ".dwb-wrapped" | ||
| 158 | , assign "web" $ className =? "Chromium" | ||
| 159 | , assign "work" $ className =? "Emacs" | ||
| 160 | , assign "media" $ className =? "mpv" | ||
| 161 | ] | ||
| 162 | , hWsp = hWsp | ||
| 163 | , hCoWsp = hCoWsp | ||
| 164 | , hKeysMod = \conf -> Map.union $ (Map.fromList $ join $ map (spawnBindings conf) [ (xK_d, ["chromium", "chromium $(xclip -o)"]) | ||
| 165 | , (xK_e, ["emacsclient -c"]) | ||
| 166 | ]) | ||
| 167 | `Map.union` | ||
| 168 | ( Map.fromList [ ((XMonad.modMask conf .|. controlMask, xK_Return), scratchpadSpawnActionCustom $ (XMonad.terminal conf) ++ " -name scratchpad -title scratchpad -e tmux new-session -D -s scratch") | ||
| 169 | ] ) | ||
| 170 | , hScreens = hScreens defaultHost | ||
| 171 | } | ||
| 172 | where | ||
| 173 | workspaceNames = Map.fromList [ (2, "web") | ||
| 174 | , (3, "work") | ||
| 175 | , (10, "media") | ||
| 176 | ] | ||
| 177 | hWsp = wspFromMap workspaceNames | ||
| 178 | hCoWsp = coWspFromMap workspaceNames | ||
| 179 | assign wsp test = (\wsp -> test -?> doShift wsp) <$> hCoWsp wsp | ||
| 180 | hostFromName h | ||
| 181 | | h `elem` ["hel", "sif"] = defaultHost { hName = h | ||
| 182 | , hManageHook = namedScratchpadManageHook scratchpads <+> composeOne (catMaybes | ||
| 183 | [ assign "mpv" $ className =? "mpv" | ||
| 184 | , assign "mpv" $ stringProperty "WM_WINDOW_ROLE" =? "presentation" | ||
| 185 | , assign "read" $ stringProperty "WM_WINDOW_ROLE" =? "presenter" | ||
| 186 | , assign "mpv" $ className =? "factorio" | ||
| 187 | , assign "mpv" $ resource =? "twitch" | ||
| 188 | , assign "web" $ className =? "chromium-browser" | ||
| 189 | , assign "web" $ className =? "Google-chrome" | ||
| 190 | , assign "work" $ (appName =? "Devtools" <&&> className =? "firefox") | ||
| 191 | , assign "work" $ className =? "Postman" | ||
| 192 | , assign "web" $ (appName =? "Navigator" <&&> className =? "firefox") | ||
| 193 | , assign "comm" $ (className =? "Emacs" <&&> title =? "Mail") | ||
| 194 | , assign "comm" $ className =? "Zulip" | ||
| 195 | , assign "comm" $ className =? "Element" | ||
| 196 | , assign "comm" $ className =? "Rocket.Chat" | ||
| 197 | , assign "comm" $ className =? "Discord" | ||
| 198 | , assign "comm" $ className =? "Rainbow" | ||
| 199 | , assign "media" $ resource =? "media" | ||
| 200 | , assign "monitor" $ className =? "Grafana" | ||
| 201 | , assign "monitor" $ className =? "Virt-viewer" | ||
| 202 | , assign "monitor" $ resource =? "htop" | ||
| 203 | , assign "monitor" $ resource =? "monitor" | ||
| 204 | , assign "monitor" $ className =? "xfreerdp" | ||
| 205 | , assign "monitor" $ className =? "org.remmina.Remmina" | ||
| 206 | , Just $ resource =? "htop" -?> centerFloat | ||
| 207 | , Just $ (className =? "Scp-dbus-service.py") -?> centerFloat | ||
| 208 | , Just $ resource =? "log" -?> centerFloat | ||
| 209 | , assign "work" $ className =? "Alacritty" | ||
| 210 | , Just $ (appName =? "Edit with Emacs FRAME") -?> centerFloat | ||
| 211 | , assign' ["work", "uni"] $ (className =? "Emacs" <&&> appName /=? "Edit with Emacs FRAME") | ||
| 212 | , assign' ["work", "uni"] $ className =? "jetbrains-idea-ce" | ||
| 213 | , assign "read" $ className =? "llpp" | ||
| 214 | , assign "read" $ className =? "Evince" | ||
| 215 | , assign "read" $ className =? "Zathura" | ||
| 216 | , assign "read" $ className =? "MuPDF" | ||
| 217 | , assign "read" $ className =? "Xournal" | ||
| 218 | , assign "read" $ appName =? "libreoffice" | ||
| 219 | , assign "read" $ appName =? "com-trollworks-gcs-app-GCS" | ||
| 220 | , assign "read" $ appName =? "Tux.py" | ||
| 221 | , assign "read" $ className =? "Gnucash" | ||
| 222 | , assign "comm" $ className =? "Skype" | ||
| 223 | , assign "comm" $ className =? "Daily" | ||
| 224 | , assign "comm" $ className =? "Pidgin" | ||
| 225 | , assign "comm" $ className =? "Thunderbird" | ||
| 226 | , assign "comm" $ className =? "Slack" | ||
| 227 | , Just $ (resource =? "xvkbd") -?> doRectFloat $ RationalRect (1 % 8) (3 % 8) (6 % 8) (4 % 8) | ||
| 228 | , Just $ (stringProperty "_NET_WM_WINDOW_TYPE" =? "_NET_WM_WINDOW_TYPE_DIALOG") -?> doFloat | ||
| 229 | , Just $ (className =? "Dunst") -?> doFloat | ||
| 230 | , Just $ (className =? "Xmessage") -?> doCenterFloat | ||
| 231 | , Just $ (className =? "Nm-openconnect-auth-dialog") -?> centerFloat | ||
| 232 | , Just $ (className =? "Pinentry") -?> doCenterFloat | ||
| 233 | , Just $ (className =? "pinentry") -?> doCenterFloat | ||
| 234 | , Just $ (stringProperty "WM_WINDOW_ROLE" =? "GtkFileChooseDialog") -?> centerFloatSmall | ||
| 235 | , Just $ (className =? "Nvidia-settings") -?> doCenterFloat | ||
| 236 | , Just $ fmap ("Minetest" `isInfixOf`) title -?> doIgnore | ||
| 237 | , Just $ fmap ("Automachef" `isInfixOf`) title -?> doIgnore | ||
| 238 | , assign "call" $ className =? "zoom" | ||
| 239 | ]) | ||
| 240 | , hWsp = hWsp | ||
| 241 | , hCoWsp = hCoWsp | ||
| 242 | , hKeysMod = \conf -> Map.union $ (Map.fromList $ join $ map (spawnBindings conf) [ (xK_e, ["emacsclient -c"]) | ||
| 243 | , (xK_d, [fromString browser, "google-chrome" {- , "notmuch-links" -}]) | ||
| 244 | , (xK_c, [ inputPrompt xPConfigMonospace "dc" ?+ dc ]) | ||
| 245 | , (xK_g, ["pidgin"]) | ||
| 246 | , (xK_s, ["skype"]) | ||
| 247 | -- , (xK_p, [mkPassPrompt "Type password" pwType xPConfig, mkPassPrompt "Show password" pwShow xPConfig, mkPassPrompt "Copy password" pwClip xPConfig]) | ||
| 248 | , (xK_w, ["sudo rewacom"]) | ||
| 249 | , (xK_y, [ "tmux new-window -dt media /var/media/link.hs $(xclip -o)" | ||
| 250 | , "tmux new-window -dt media /var/media/download.hs $(xclip -o)" | ||
| 251 | , "tmux new-window -dt media /var/media/download.hs $(xclip -o -selection clipboard)" | ||
| 252 | ]) | ||
| 253 | , (xK_l, [ "tmux new-window -dt media mpv $(xclip -o)" | ||
| 254 | , "tmux new-window -dt media mpv $(xclip -o -selection clipboard)" | ||
| 255 | , "alacritty --class media -e tmuxp load /var/media" | ||
| 256 | ]) | ||
| 257 | {- , (xK_m, [ "emacsclient -c -F \"'(title . \\\"Mail\\\")\" -e '(notmuch)'" | ||
| 258 | , "emacsclient -c -F \"'(title . \\\"Mail\\\")\" -e '(notmuch-mua-new-mail)'" | ||
| 259 | , "emacsclient -c -F \"'(title . \\\"Mail\\\")\" -e \"(browse-url-mail \"$(xclip -o)\")\"" | ||
| 260 | ]) -} | ||
| 261 | , (xK_Return, ["keynav start,windowzoom", "keynav start"]) | ||
| 262 | , (xK_t, [inputPrompt xPConfigMonospace "fuzzytime timer" ?+ fuzzytime, fuzzytime "unset", work_fuzzytime]) | ||
| 263 | , (xK_a, [inputPrompt xPConfigMonospace "adjmix" ?+ adjmix]) | ||
| 264 | , (xK_s, [ inputPromptWithCompl xPConfigMonospace "start synergy" synergyCompl ?+ synergyStart | ||
| 265 | , inputPromptWithCompl xPConfigMonospace "stop synergy" synergyCompl ?+ synergyStop | ||
| 266 | ]) | ||
| 267 | , (xK_h, [ "alacritty --class htop -e htop" | ||
| 268 | , "alacritty --class log -e journalctl -xef" | ||
| 269 | ]) | ||
| 270 | , (xK_x, [ "autorandr -c" | ||
| 271 | , "autorandr -fl def" | ||
| 272 | ]) | ||
| 273 | , (xK_z, [ "zulip -- --force-device-scale-factor=2" | ||
| 274 | ]) | ||
| 275 | ]) | ||
| 276 | `Map.union` | ||
| 277 | ( Map.fromList [ ((XMonad.modMask conf .|. controlMask, xK_Return), namedScratchpadAction scratchpads "term") | ||
| 278 | , ((XMonad.modMask conf .|. controlMask, xK_a), namedScratchpadAction scratchpads "pavucontrol") | ||
| 279 | , ((XMonad.modMask conf .|. controlMask, xK_o), namedScratchpadAction scratchpads "easyeffects") | ||
| 280 | , ((XMonad.modMask conf .|. controlMask .|. shiftMask, xK_o), namedScratchpadAction scratchpads "helvum") | ||
| 281 | , ((XMonad.modMask conf .|. controlMask, xK_w), namedScratchpadAction scratchpads "alarms") | ||
| 282 | , ((XMonad.modMask conf .|. controlMask, xK_b), namedScratchpadAction scratchpads "blueman") | ||
| 283 | , ((XMonad.modMask conf .|. controlMask, xK_p), namedScratchpadAction scratchpads "keepassxc") | ||
| 284 | , ((XMonad.modMask conf .|. controlMask, xK_t), namedScratchpadAction scratchpads "toggl") | ||
| 285 | , ((XMonad.modMask conf .|. controlMask, xK_e), namedScratchpadAction scratchpads "emacs") | ||
| 286 | , ((XMonad.modMask conf .|. controlMask, xK_m), namedScratchpadAction scratchpads "calendar") | ||
| 287 | , ((XMonad.modMask conf .|. controlMask, xK_f), namedScratchpadAction scratchpads "music") | ||
| 288 | , ((XMonad.modMask conf .|. mod1Mask, xK_Up), rotate U) | ||
| 289 | , ((XMonad.modMask conf .|. mod1Mask, xK_Down), rotate D) | ||
| 290 | , ((XMonad.modMask conf .|. mod1Mask, xK_Left), rotate L) | ||
| 291 | , ((XMonad.modMask conf .|. mod1Mask, xK_Right), rotate R) | ||
| 292 | , ((controlMask, xK_space ), "dunstctl close" ) | ||
| 293 | , ((controlMask .|. shiftMask, xK_space ), "dunstctl close-all" ) | ||
| 294 | , ((controlMask, xK_period), "dunstctl context" ) | ||
| 295 | , ((controlMask, xK_comma ), "dunstctl history-pop") | ||
| 296 | -- , ((XMonad.modMask conf .|. shiftMask, xK_a), startMute "hel") | ||
| 297 | ] ) | ||
| 298 | , hKeyUpKeys = \conf -> Map.fromList [ -- ((XMonad.modMask conf .|. shiftMask, xK_a), stopMute "hel") | ||
| 299 | ] | ||
| 300 | , hScreens = hScreens defaultHost | ||
| 301 | , hCmds = return [ ("prev-workspace", prevWS) | ||
| 302 | , ("next-workspace", nextWS) | ||
| 303 | , ("prev-window", rotAllDown) | ||
| 304 | , ("next-window", rotAllUp) | ||
| 305 | , ("banish", banishScreen LowerRight) | ||
| 306 | , ("update-gpg-tty", safeSpawn "gpg-connect-agent" ["UPDATESTARTUPTTY", "/bye"]) | ||
| 307 | , ("rescreen", rescreen) | ||
| 308 | , ("repanel", do | ||
| 309 | spawn "nm-applet" | ||
| 310 | spawn "blueman-applet" | ||
| 311 | spawn "pasystray" | ||
| 312 | spawn "kdeconnect-indicator" | ||
| 313 | spawn "dunst -print" | ||
| 314 | spawn "udiskie" | ||
| 315 | spawn "autocutsel -s PRIMARY" | ||
| 316 | spawn "autocutsel -s CLIPBOARD" | ||
| 317 | ) | ||
| 318 | , ("pause", mediaMpv $ MpvSetProperty "pause" True) | ||
| 319 | , ("unpause", mediaMpv $ MpvSetProperty "pause" False) | ||
| 320 | , ("exit", io $ exitWith ExitSuccess) | ||
| 321 | ] | ||
| 322 | } | ||
| 323 | where | ||
| 324 | withGdkScale act = void . xfork $ setEnv "GDK_SCALE" "2" >> act | ||
| 325 | workspaceNames = Map.fromList [ (1, "comm") | ||
| 326 | , (2, "web") | ||
| 327 | , (3, "work") | ||
| 328 | , (4, "read") | ||
| 329 | , (5, "monitor") | ||
| 330 | , (6, "uni") | ||
| 331 | , (8, "call") | ||
| 332 | , (9, "media") | ||
| 333 | , (10, "mpv") | ||
| 334 | ] | ||
| 335 | scratchpads = [ NS "term" "alacritty --class scratchpad --title scratchpad -e tmux new-session -AD -s scratch" (resource =? "scratchpad") centerFloat | ||
| 336 | , NS "pavucontrol" "pavucontrol" (resource =? "pavucontrol") centerFloat | ||
| 337 | , NS "helvum" "helvum" (resource =? "helvum") centerFloat | ||
| 338 | , NS "easyeffects" "easyeffects" (resource =? "easyeffects") centerFloat | ||
| 339 | , NS "alarms" "alarm-clock-applet" (className =? "Alarm-clock-applet" <&&> title =? "Alarms") centerFloat | ||
| 340 | , NS "blueman" "blueman-manager" (className =? ".blueman-manager-wrapped") centerFloat | ||
| 341 | , NS "keepassxc" "keepassxc" (className =? "KeePassXC") centerFloat | ||
| 342 | , NS "toggl" "toggldesktop" (className =? "Toggl Desktop") centerFloat | ||
| 343 | , NS "calendar" "minetime -- --force-device-scale-factor=1.6" (className =? "MineTime") centerFloat | ||
| 344 | , NS "emacs" "emacsclient -c -F \"'(title . \\\"Scratchpad\\\")\"" (className =? "Emacs" <&&> title =? "Scratchpad") centerFloat | ||
| 345 | , NS "music" "ytmdesktop" (className =? "youtube-music-desktop-app") centerFloat | ||
| 346 | ] | ||
| 347 | centerFloat = customFloating $ RationalRect (1 % 16) (1 % 16) (7 % 8) (7 % 8) | ||
| 348 | centerFloatSmall = customFloating $ RationalRect (1 % 4) (1 % 4) (1 % 2) (1 % 2) | ||
| 349 | hWsp = wspFromMap workspaceNames | ||
| 350 | hCoWsp = coWspFromMap workspaceNames | ||
| 351 | assign wsp test = (\wsp -> test -?> doShift wsp) <$> hCoWsp wsp | ||
| 352 | assign' :: [String] -> Query Bool -> Maybe MaybeManageHook | ||
| 353 | assign' wsps test = do | ||
| 354 | wsIds <- mapM hCoWsp wsps | ||
| 355 | return $ test -?> go wsIds | ||
| 356 | where | ||
| 357 | go :: [WorkspaceId] -> ManageHook | ||
| 358 | go wsps = do | ||
| 359 | visWsps <- liftX $ (\wset -> W.tag . W.workspace <$> W.current wset : W.visible wset) <$> gets windowset | ||
| 360 | case (filter (`elem` visWsps) wsps, wsps) of | ||
| 361 | (wsp : _, _) -> doShift wsp | ||
| 362 | (_, wsp : _) -> doShift wsp | ||
| 363 | ([], []) -> return mempty | ||
| 364 | rotate rot = do | ||
| 365 | safeSpawn "xrandr" ["--output", "eDP-1", "--rotate", xrandrDir] | ||
| 366 | mapM_ rotTouch touchscreens | ||
| 367 | where | ||
| 368 | xrandrDir = case rot of | ||
| 369 | U -> "normal" | ||
| 370 | L -> "left" | ||
| 371 | R -> "right" | ||
| 372 | D -> "inverted" | ||
| 373 | matrix = case rot of | ||
| 374 | U -> [ [ 1, 0, 0] | ||
| 375 | , [ 0, 1, 0] | ||
| 376 | , [ 0, 0, 1] | ||
| 377 | ] | ||
| 378 | L -> [ [ 0, -1, 1] | ||
| 379 | , [ 1, 0, 0] | ||
| 380 | , [ 0, 0, 1] | ||
| 381 | ] | ||
| 382 | R -> [ [ 0, 1, 0] | ||
| 383 | , [-1, 0, 1] | ||
| 384 | , [ 0, 0, 1] | ||
| 385 | ] | ||
| 386 | D -> [ [-1, 0, 1] | ||
| 387 | , [ 0, -1, 1] | ||
| 388 | , [ 0, 0, 1] | ||
| 389 | ] | ||
| 390 | touchscreens = [ "Wacom Co.,Ltd. Pen and multitouch sensor Finger touch" | ||
| 391 | , "Wacom Co.,Ltd. Pen and multitouch sensor Pen stylus" | ||
| 392 | , "Wacom Co.,Ltd. Pen and multitouch sensor Pen eraser" | ||
| 393 | ] | ||
| 394 | rotTouch screen = do | ||
| 395 | safeSpawn "xinput" $ ["set-prop", screen, "Coordinate Transformation Matrix"] ++ map (\n -> show n ++ ",") (concat matrix) | ||
| 396 | safeSpawn "xinput" ["map-to-output", screen, "eDP-1"] | ||
| 397 | withPw f label = io . void . forkProcess $ do | ||
| 398 | uninstallSignalHandlers | ||
| 399 | void $ createSession | ||
| 400 | (dropWhileEnd isSpace -> pw) <- readCreateProcess (proc "pass" ["show", label]) "" | ||
| 401 | void $ f pw | ||
| 402 | pwType :: String -> X () | ||
| 403 | pwType = withPw $ readCreateProcess (proc "xdotool" ["type", "--clearmodifiers", "--file", "-"]) | ||
| 404 | pwClip label = safeSpawn "pass" ["show", "--clip", label] | ||
| 405 | pwShow :: String -> X () | ||
| 406 | pwShow = withPw $ \pw -> do | ||
| 407 | xmessage <- fromMaybe "xmessage" <$> liftIO (lookupEnv "XMONAD_XMESSAGE") | ||
| 408 | readCreateProcess (proc xmessage ["-file", "-"]) pw | ||
| 409 | fuzzytime str = safeSpawn "fuzzytime" $ "timer" : words str | ||
| 410 | work_fuzzytime = io . void . forkProcess $ do | ||
| 411 | readCreateProcess (proc "worktime" []) "" >>= safeSpawn "fuzzytime" . ("timer" : ) . pure | ||
| 412 | adjmix str = safeSpawn "adjmix" $ words str | ||
| 413 | dc expr = void . xfork $ do | ||
| 414 | result <- readProcess "dc" [] $ expr ++ "f" | ||
| 415 | let | ||
| 416 | (first : rest) = filter (not . null) $ lines result | ||
| 417 | notification = Notify.summary first <> Notify.body (unlines rest) <> Notify.timeout Infinite <> Notify.urgency Normal <> Notify.appName "dc" | ||
| 418 | void $ Notify.display notification | ||
| 419 | synergyCompl = mkComplFunFromList' xPConfigMonospace ["mathw86"] | ||
| 420 | synergyStart host = safeSpawn "systemctl" ["--user", "start", "synergy-rtunnel@" ++ host ++ ".service"] | ||
| 421 | synergyStop host = safeSpawn "systemctl" ["--user", "stop", "synergy-rtunnel@" ++ host ++ ".service"] | ||
| 422 | |||
| 423 | hostFromName _ = defaultHost | ||
| 424 | |||
| 425 | -- muteRef :: IORef (Maybe (String, Notification)) | ||
| 426 | -- {-# NOINLINE muteRef #-} | ||
| 427 | -- muteRef = unsafePerformIO $ newIORef Nothing | ||
| 428 | |||
| 429 | -- startMute, stopMute :: String -> X () | ||
| 430 | -- startMute sink = liftIO $ do | ||
| 431 | -- muted <- isJust <$> readIORef muteRef | ||
| 432 | -- when (not muted) $ do | ||
| 433 | -- let | ||
| 434 | -- notification = Notify.summary "Muted" <> Notify.timeout Infinite <> Notify.urgency Normal | ||
| 435 | -- level = "0.0dB" | ||
| 436 | -- -- level <- runProcessWithInput "ssh" ["bragi", "cat", "/dev/shm/mix/" ++ sink ++ "/level"] "" | ||
| 437 | -- -- callProcess "ssh" ["bragi", "adjmix", "-t", sink, "-o", "0"] | ||
| 438 | -- hPutStrLn stderr "Mute" | ||
| 439 | -- writeIORef muteRef . Just . (level, ) =<< Notify.display notification | ||
| 440 | -- stopMute sink = liftIO $ do | ||
| 441 | -- let | ||
| 442 | -- unmute (Just (level, notification)) = do | ||
| 443 | -- hPutStrLn stderr "Unmute" | ||
| 444 | -- -- callProcess "ssh" ["bragi", "adjmix", "-t", sink, "-o", level] | ||
| 445 | -- Notify.close notification | ||
| 446 | -- unmute Nothing = return () | ||
| 447 | -- muted <- isJust <$> readIORef muteRef | ||
| 448 | -- when muted . join . atomicModifyIORef muteRef $ (Nothing, ) . unmute | ||
| 449 | |||
| 450 | wspFromMap workspaceNames = \i -> case Map.lookup i workspaceNames of | ||
| 451 | Just str -> show i ++ " " ++ str | ||
| 452 | Nothing -> show i | ||
| 453 | |||
| 454 | coWspFromMap workspaceNames = \str -> case filter ((== str) . snd) $ Map.toList workspaceNames of | ||
| 455 | [] -> Nothing | ||
| 456 | [(i, _)] -> Just $ wspFromMap workspaceNames i | ||
| 457 | _ -> Nothing | ||
| 458 | |||
| 459 | spawnModifiers = [0, controlMask, shiftMask .|. controlMask] | ||
| 460 | spawnBindings :: XConfig layout -> (KeySym, [X ()]) -> [((KeyMask, KeySym), X ())] | ||
| 461 | spawnBindings conf (k, cmds) = zipWith (\m cmd -> ((modm .|. mod1Mask .|. m, k), cmd)) spawnModifiers cmds | ||
| 462 | where | ||
| 463 | modm = XMonad.modMask conf | ||
| 464 | |||
| 465 | manageScratchTerm = (resource =? "scratchpad" <||> resource =? "keysetup") -?> doRectFloat $ RationalRect (1 % 16) (1 % 16) (7 % 8) (7 % 8) | ||
| 466 | |||
| 467 | tabbedLayout t = renamed [Replace "Tabbed"] $ reflectHoriz $ t CustomShrink $ tabbedTheme | ||
| 468 | tabbedLayoutHoriz t = renamed [Replace "Tabbed Horiz"] $ reflectVert $ t CustomShrink $ tabbedTheme | ||
| 469 | tabbedTheme = def | ||
| 470 | { activeColor = "black" | ||
| 471 | , inactiveColor = "black" | ||
| 472 | , urgentColor = "black" | ||
| 473 | , activeBorderColor = gray | ||
| 474 | , inactiveBorderColor = darkGray | ||
| 475 | , urgentBorderColor = red | ||
| 476 | , activeTextColor = gray | ||
| 477 | , inactiveTextColor = gray | ||
| 478 | , urgentTextColor = gray | ||
| 479 | , decoHeight = 32 | ||
| 480 | , fontName = "xft:Fira Sans:pixelsize=21" | ||
| 481 | } | ||
| 482 | |||
| 483 | main :: IO () | ||
| 484 | main = do | ||
| 485 | arguments <- either (const []) id <$> tryIOError getArgs | ||
| 486 | case arguments of | ||
| 487 | ["--command", s] -> do | ||
| 488 | d <- openDisplay "" | ||
| 489 | rw <- rootWindow d $ defaultScreen d | ||
| 490 | a <- internAtom d "XMONAD_COMMAND" False | ||
| 491 | m <- internAtom d s False | ||
| 492 | allocaXEvent $ \e -> do | ||
| 493 | setEventType e clientMessage | ||
| 494 | setClientMessageEvent e rw a 32 m currentTime | ||
| 495 | sendEvent d rw False structureNotifyMask e | ||
| 496 | sync d False | ||
| 497 | _ -> do | ||
| 498 | -- batteryMon <- xfork $ monitorBattery Nothing Nothing | ||
| 499 | hostname <- getHostName | ||
| 500 | let | ||
| 501 | host = hostFromName hostname | ||
| 502 | setEnv "HOST" hostname | ||
| 503 | let myConfig = withHostUrgency . ewmhFullscreen . ewmh . pagerHints $ docks def | ||
| 504 | { manageHook = hManageHook host | ||
| 505 | , terminal = "alacritty" | ||
| 506 | , layoutHook = smartBorders . avoidStruts $ windowNavigation layout' | ||
| 507 | , logHook = do | ||
| 508 | dynamicLogString xmobarPP' >>= writeProps | ||
| 509 | updatePointer (99 % 100, 98 % 100) (0, 0) | ||
| 510 | , modMask = mod4Mask | ||
| 511 | , keys = \conf -> hKeysMod host conf $ myKeys' conf host | ||
| 512 | , workspaces = take (length numKeys) $ map wsp [1..] | ||
| 513 | , startupHook = setDefaultCursor xC_left_ptr | ||
| 514 | , normalBorderColor = darkGray | ||
| 515 | , focusedBorderColor = gray | ||
| 516 | , handleEventHook = serverModeEventHookCmd' (hCmds host) <+> keyUpEventHook | ||
| 517 | } | ||
| 518 | writeProps str = do | ||
| 519 | let encodeCChar = map $ fromIntegral . fromEnum | ||
| 520 | atoms = [ "_XMONAD_WORKSPACES" | ||
| 521 | , "_XMONAD_LAYOUT" | ||
| 522 | , "_XMONAD_TITLE" | ||
| 523 | ] | ||
| 524 | (flip mapM_) (zip atoms (lines str)) $ \(atom', content) -> do | ||
| 525 | ustring <- getAtom "UTF8_STRING" | ||
| 526 | atom <- getAtom atom' | ||
| 527 | withDisplay $ \dpy -> io $ do | ||
| 528 | root <- rootWindow dpy $ defaultScreen dpy | ||
| 529 | changeProperty8 dpy root atom ustring propModeReplace $ encodeCChar content | ||
| 530 | sync dpy True | ||
| 531 | wsp = hWsp host | ||
| 532 | -- We can´t define per-host layout modifiers because we lack dependent types | ||
| 533 | layout' = onHost "skadhi" ( onWorkspace (wsp 1) (Full ||| withIM (1%5) (Title "Buddy List") tabbedLayout') $ | ||
| 534 | onWorkspace (wsp 10) Full $ | ||
| 535 | onWorkspace (wsp 2) (Full ||| tabbedLayout') $ | ||
| 536 | onWorkspace (wsp 5) tabbedLayout' $ | ||
| 537 | onWorkspace (wsp 8) (withIM (1%5) (Title "Friends") tabbedLayout') $ | ||
| 538 | defaultLayouts | ||
| 539 | ) $ | ||
| 540 | onHost "vali" ( onWorkspace (wsp 2) (Full ||| tabbedLayout' ||| combineTwo (TwoPane 0.01 0.57) Full tabbedLayout') $ | ||
| 541 | onWorkspace (wsp 3) workLayouts $ | ||
| 542 | defaultLayouts | ||
| 543 | ) $ | ||
| 544 | onHost "hel" ( onWorkspace (wsp 1) (withIM (1 % 8) (Title "Buddy List") $ trackFloating tabbedLayout') $ | ||
| 545 | onWorkspace (wsp 2) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $ | ||
| 546 | onWorkspace (wsp 3) workLayouts $ | ||
| 547 | onWorkspace (wsp 6) workLayouts $ | ||
| 548 | onWorkspace (wsp 4) (tabbedLayout' ||| tabbedLayoutHoriz' ||| Dwindle R CW 1 (5 % 100)) $ | ||
| 549 | onWorkspace (wsp 5) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $ | ||
| 550 | onWorkspace (wsp 10) (tabbedLayout''' ||| combineTwoP (TwoPane (1 % 100) (3 % 4)) tabbedLayout''' tabbedLayout''' (ClassName "mpv") ||| Dwindle R CW 1 (5 % 100)) $ | ||
| 551 | defaultLayouts | ||
| 552 | ) $ | ||
| 553 | onHost "sif" ( onWorkspace (wsp 1) (withIM (1 % 8) (Title "Buddy List") $ trackFloating tabbedLayout') $ | ||
| 554 | onWorkspace (wsp 2) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $ | ||
| 555 | onWorkspace (wsp 3) workLayouts $ | ||
| 556 | onWorkspace (wsp 6) workLayouts $ | ||
| 557 | onWorkspace (wsp 4) (tabbedLayout' ||| tabbedLayoutHoriz' ||| Dwindle R CW 1 (5 % 100)) $ | ||
| 558 | onWorkspace (wsp 5) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $ | ||
| 559 | onWorkspace (wsp 8) tabbedLayout''' $ | ||
| 560 | onWorkspace (wsp 10) (tabbedLayout''' ||| combineTwoP (TwoPane (1 % 100) (3 % 4)) tabbedLayout''' tabbedLayout''' (ClassName "mpv") ||| Dwindle R CW 1 (5 % 100)) $ | ||
| 561 | defaultLayouts | ||
| 562 | ) $ | ||
| 563 | defaultLayouts | ||
| 564 | -- tabbedLayout''' = renamed [Replace "Tabbed'"] $ IfMax 1 (noBorders Full) (tabbedLayout tabbedBottomAlways) | ||
| 565 | tabbedLayout''' = tabbedLayout tabbedBottom | ||
| 566 | tabbedLayout' = tabbedLayout tabbedBottomAlways | ||
| 567 | tabbedLayoutHoriz' = tabbedLayoutHoriz tabbedLeftAlways | ||
| 568 | defaultLayouts = {- spiralWithDir East CW (1 % 2) -} Dwindle R CW 1 (5 % 100) ||| tabbedLayout' ||| Full | ||
| 569 | -- workLayouts = {- spiralWithDir East CW (1 % 2) -} Dwindle R CW (2 % 1) (5 % 100) ||| tabbedLayout' ||| Full | ||
| 570 | workLayouts = tabbedLayout' ||| (renamed [Replace "Combined"] $ combineTwoP (TwoPane (1 % 100) (1891 % 2560)) tabbedLayout''' (Column 1.6) (ClassName "Postman" `Or` ClassName "Emacs" `Or` ClassName "jetbrains-idea-ce" `Or` (Resource "Devtools" `And` ClassName "Firefox"))) ||| Full ||| Dwindle R CW 1 (5 % 100) | ||
| 571 | sqrtTwo = approxRational (sqrt 2) (1 / 2560) | ||
| 572 | xmobarPP' = xmobarPP { ppTitle = shorten 80 | ||
| 573 | , ppSort = (liftM2 (.)) getSortByIndex $ return scratchpadFilterOutWorkspace | ||
| 574 | , ppUrgent = wrap "(" ")" . xmobarColor "#800000" "" | ||
| 575 | , ppHiddenNoWindows = xmobarColor "#202020" "" . wrap "(" ")" | ||
| 576 | , ppVisible = wrap "(" ")" . xmobarColor "#808000" "" | ||
| 577 | , ppCurrent = wrap "(" ")" . xmobarColor "#008000" "" | ||
| 578 | , ppHidden = wrap "(" ")" | ||
| 579 | , ppWsSep = " " | ||
| 580 | , ppSep = "\n" | ||
| 581 | } | ||
| 582 | withHostUrgency = case hostname of | ||
| 583 | "sif" -> withUrgencyHookC urgencyHook' $ def { suppressWhen = U.Never, remindWhen = Every 2 } | ||
| 584 | _ -> id | ||
| 585 | urgencyHook' window = do | ||
| 586 | let blinkLight = (lightHigh >> threadDelay 0.5e6) `finally` lightLow | ||
| 587 | where | ||
| 588 | lightHigh = | ||
| 589 | writeFile "/sys/class/leds/input0::capslock/brightness" =<< readFile "/sys/class/leds/input0::capslock/max_brightness" | ||
| 590 | lightLow = writeFile "/sys/class/leds/input0::capslock/brightness" "0" | ||
| 591 | runQuery ((resource =? "comm" <||> resource =? "Pidgin" <||> className =? "Gajim" <||> className =? "Skype" <||> className =? "Thunderbird") --> void (xfork blinkLight)) window | ||
| 592 | urgencyHook (BorderUrgencyHook { urgencyBorderColor = red }) window | ||
| 593 | shutdown :: SomeException -> IO a | ||
| 594 | shutdown e = do | ||
| 595 | let pids = [ -- batteryMon | ||
| 596 | ] | ||
| 597 | mapM_ (signalProcess sigTERM) pids | ||
| 598 | mapM_ (getProcessStatus False False) pids | ||
| 599 | throw e | ||
| 600 | keyUpEventHook :: Event -> X All | ||
| 601 | keyUpEventHook event = handle event >> return (All True) | ||
| 602 | where | ||
| 603 | handle (KeyEvent { ev_event_type = t, ev_state = m, ev_keycode = code }) | ||
| 604 | | t == keyRelease = withDisplay $ \dpy -> do | ||
| 605 | s <- io $ keycodeToKeysym dpy code 0 | ||
| 606 | mClean <- cleanMask m | ||
| 607 | ks <- asks $ hKeyUpKeys host . config | ||
| 608 | userCodeDef () $ whenJust (Map.lookup (mClean, s) ks) id | ||
| 609 | | otherwise = return () | ||
| 610 | handle _ = return () | ||
| 611 | handle shutdown $ launch myConfig =<< getDirectories | ||
| 612 | |||
| 613 | secs :: Int -> Int | ||
| 614 | secs = (* 1000000) | ||
| 615 | |||
| 616 | -- monitorBattery :: Maybe BatteryContext -> Maybe Notification -> IO () | ||
| 617 | -- monitorBattery Nothing n = do | ||
| 618 | -- ctx <- batteryContextNew | ||
| 619 | -- case ctx of | ||
| 620 | -- Nothing -> threadDelay (secs 10) >> monitorBattery Nothing n | ||
| 621 | -- Just _ -> monitorBattery ctx n | ||
| 622 | -- monitorBattery ctx@(Just ctx') n = do | ||
| 623 | -- batInfo <- getBatteryInfo ctx' | ||
| 624 | -- case batInfo of | ||
| 625 | -- Nothing -> threadDelay (secs 1) >> monitorBattery ctx n | ||
| 626 | -- Just batInfo -> do | ||
| 627 | -- let n' | ||
| 628 | -- | batteryState batInfo == BatteryStateDischarging | ||
| 629 | -- , timeLeft <= 1200 | ||
| 630 | -- , timeLeft > 0 = Just $ summary "Discharging" <> hint "value" percentage <> urgency u <> body (duz timeLeft ++ "left") | ||
| 631 | -- | otherwise = Nothing | ||
| 632 | -- u | ||
| 633 | -- | timeLeft <= 600 = Critical | ||
| 634 | -- | timeLeft <= 1800 = Normal | ||
| 635 | -- | otherwise = Low | ||
| 636 | -- timeLeft = batteryTimeToEmpty batInfo | ||
| 637 | -- percentage :: Int32 | ||
| 638 | -- percentage = round $ batteryPercentage batInfo | ||
| 639 | -- ts = [("s", 60), ("m", 60), ("h", 24), ("d", 365), ("y", 1)] | ||
| 640 | -- duz ms = ss | ||
| 641 | -- where (ss, _) = foldl (\(ss, x) (s, y) -> ((if rem x y > 0 then show (rem x y) ++ s ++ " " else "") ++ ss , quot x y)) ("", ms) ts | ||
| 642 | -- case n' of | ||
| 643 | -- Just n' -> Notify.display (maybe mempty reuse n <> Notify.appName "monitorBattery" <> n') >>= (\n -> threadDelay (secs 2) >> monitorBattery ctx (Just n)) | ||
| 644 | -- Nothing -> threadDelay (secs 30) >> monitorBattery ctx n | ||
| 645 | |||
| 646 | disableTouchpad, disableTrackpoint, enableTrackpoint, enableTouchpad :: X () | ||
| 647 | enableTouchpad = safeSpawn "xinput" ["enable", "SynPS/2 Synaptics TouchPad"] | ||
| 648 | disableTouchpad = safeSpawn "xinput" ["disable", "SynPS/2 Synaptics TouchPad"] | ||
| 649 | enableTrackpoint = safeSpawn "xinput" ["enable", "TPPS/2 IBM TrackPoint"] | ||
| 650 | disableTrackpoint = safeSpawn "xinput" ["disable", "TPPS/2 IBM TrackPoint"] | ||
| 651 | |||
| 652 | isDisabled :: String -> X Bool | ||
| 653 | isDisabled str = do | ||
| 654 | out <- runProcessWithInput "xinput" ["list", str] "" | ||
| 655 | return $ "disabled" `isInfixOf` out | ||
| 656 | |||
| 657 | |||
| 658 | spawnKeychain :: X () | ||
| 659 | spawnKeychain = do | ||
| 660 | home <- liftIO getHomeDirectory | ||
| 661 | let keys = (map ((home </>) . (".ssh/" ++)) ["id", "id-rsa"]) ++ ["6B13AA67"] | ||
| 662 | liftIO (maybe (return ()) (setEnv "SSH_ASKPASS") =<< findAskpass) | ||
| 663 | safeSpawn "keychain" . (["--agents", "gpg,ssh"] ++)=<< liftIO (filterM doesFileExist keys) | ||
| 664 | where | ||
| 665 | findAskpass = filter `liftM` readFile "/etc/zshrc" | ||
| 666 | filter = listToMaybe . catMaybes . map (stripPrefix "export SSH_ASKPASS=") . lines | ||
| 667 | |||
| 668 | assimilateKeychain :: X () | ||
| 669 | assimilateKeychain = liftIO $ assimilateKeychain' >> return () | ||
| 670 | assimilateKeychain' = tryIOError $ do | ||
| 671 | -- pid <- getProcessID | ||
| 672 | -- tmpDir <- lookupEnv "TMPDIR" | ||
| 673 | -- let tmpDir' = fromMaybe "/tmp" tmpDir | ||
| 674 | -- tmpFile = tmpDir' </> "xmonad-keychain" ++ (show pid) ++ ".env" | ||
| 675 | env <- runProcessWithInput "sh" ["-c", "eval $(keychain --eval --noask --agents gpg,ssh); env"] "" -- > " ++ tmpFile] "" | ||
| 676 | -- env <- readFile tmpFile | ||
| 677 | let envVars = Map.fromList $ map (\(k, v) -> (k, tail' v)) $ map (span (/= '=')) $ envLines | ||
| 678 | envVars' = Map.filterWithKey (\k _ -> k `elem` transfer) envVars | ||
| 679 | transfer = ["SSH_AUTH_SOCK", "SSH_AGENT_PID", "GPG_AGENT_INFO"] | ||
| 680 | envLines = filter (elem '=') $ lines env :: [String] | ||
| 681 | sequence $ map (\(k, c) -> setEnv k c) $ Map.toList envVars' | ||
| 682 | -- removeFile tmpFile | ||
| 683 | where | ||
| 684 | tail' [] = [] | ||
| 685 | tail' (x:xs) = xs | ||
| 686 | |||
| 687 | |||
| 688 | numKeys = [xK_parenleft, xK_parenright, xK_braceright, xK_plus, xK_braceleft, xK_bracketright, xK_bracketleft, xK_exclam, xK_equal, xK_asterisk] | ||
| 689 | |||
| 690 | instance Shrinker CustomShrink where | ||
| 691 | shrinkIt _ "" = [""] | ||
| 692 | shrinkIt s cs | ||
| 693 | | length cs >= 4 = cs : shrinkIt s ((reverse . drop 4 . reverse $ cs) ++ "...") | ||
| 694 | | otherwise = cs : shrinkIt s (init cs) | ||
| 695 | |||
| 696 | xPConfig, xPConfigMonospace :: XPConfig | ||
| 697 | xPConfig = def | ||
| 698 | { font = "xft:Fira Sans:pixelsize=21" | ||
| 699 | , height = 32 | ||
| 700 | , bgColor = "black" | ||
| 701 | , fgColor = gray | ||
| 702 | , fgHLight = green | ||
| 703 | , bgHLight = "black" | ||
| 704 | , borderColor = gray | ||
| 705 | , searchPredicate = (\needle haystack -> all (`isInfixOf` map toLower haystack) . map (map toLower) $ words needle) | ||
| 706 | , position = Top | ||
| 707 | } | ||
| 708 | xPConfigMonospace = xPConfig { font = "xft:Fira Code:pixelsize=21" } | ||
| 709 | |||
| 710 | sshOverrides host = map (\h -> mkOverride { oHost = h, oCommand = moshCmd . inTmux host} ) | ||
| 711 | [ "odin" | ||
| 712 | , "ymir" | ||
| 713 | , "surtr" | ||
| 714 | , "vidhar" | ||
| 715 | , "srv02.uniworx.de" | ||
| 716 | ] | ||
| 717 | ++ | ||
| 718 | map (\h -> mkOverride { oHost = h, oCommand = moshCmd' "/run/current-system/sw/bin/mosh-server" . withEnv [("TERM", "xterm")] . inTmux host} ) | ||
| 719 | [ "bragi", "bragi.asgard.yggdrasil" | ||
| 720 | ] | ||
| 721 | ++ | ||
| 722 | map (\h -> mkOverride { oHost = h, oCommand = sshCmd . inTmux host } ) | ||
| 723 | [ "uni2work-dev1", "srv01.uniworx.de" | ||
| 724 | ] | ||
| 725 | ++ | ||
| 726 | map (\h -> mkOverride { oHost = h, oCommand = sshCmd . withEnv [("TERM", "xterm")] . inTmux host } ) | ||
| 727 | [ "remote.cip.ifi.lmu.de" | ||
| 728 | , "uniworx3", "uniworx4", "uniworx5", "uniworxdb2" | ||
| 729 | , "testworx" | ||
| 730 | ] | ||
| 731 | |||
| 732 | backlight :: (Rational -> Rational) -> X () | ||
| 733 | backlight f = void . xfork . liftIO $ do | ||
| 734 | [ _device | ||
| 735 | , _class | ||
| 736 | , read . Text.unpack -> currentBright | ||
| 737 | , _currentPercentage | ||
| 738 | , read . Text.unpack -> maximumBright | ||
| 739 | ] <- Text.splitOn "," . Text.pack <$> readProcess "brightnessctl" ["-m"] "" | ||
| 740 | let current = currentBright % maximumBright | ||
| 741 | new' = f current * fromIntegral maximumBright | ||
| 742 | new :: Integer | ||
| 743 | new | floor new' < 0 = 0 | ||
| 744 | | ceiling new' > maximumBright = maximumBright | ||
| 745 | | new' >= maximumBright % 2 = ceiling new' | ||
| 746 | | otherwise = floor new' | ||
| 747 | callProcess "brightnessctl" ["-m", "s", show new] | ||
| 748 | |||
| 749 | cycleThrough :: [Rational] -> (Rational -> Rational) | ||
| 750 | cycleThrough opts current = fromMaybe currentOpt $ listToMaybe next' | ||
| 751 | where currentOpt = minimumBy (comparing $ abs . subtract current) opts | ||
| 752 | (_, _ : next') = break (== currentOpt) opts | ||
| 753 | |||
| 754 | cycleKbLayout :: [(String, Maybe String)] -> X () | ||
| 755 | cycleKbLayout [] = return () | ||
| 756 | cycleKbLayout layouts = liftIO $ do | ||
| 757 | next <- (getNext . extract) `liftM` runProcessWithInput "setxkbmap" ["-query"] "" | ||
| 758 | let | ||
| 759 | args = case next of | ||
| 760 | (l, Just v) -> [l, v] | ||
| 761 | (l, Nothing) -> [l] | ||
| 762 | safeSpawn "setxkbmap" args | ||
| 763 | where | ||
| 764 | extract :: String -> Maybe (String, Maybe String) | ||
| 765 | extract str = listToMaybe $ do | ||
| 766 | ["layout:", l] <- str' | ||
| 767 | [(l, Just v) | ["variant:", v] <- str'] ++ pure (l, Nothing) | ||
| 768 | where | ||
| 769 | str' = map words $ lines str | ||
| 770 | getNext :: Maybe (String, Maybe String) -> (String, Maybe String) | ||
| 771 | getNext = maybe (head layouts) getNext' | ||
| 772 | getNext' x = case elemIndex x layouts of | ||
| 773 | Nothing -> getNext Nothing | ||
| 774 | Just i -> layouts !! ((i + 1) `mod` length layouts) | ||
| 775 | |||
| 776 | mpvAll' :: MpvCommand -> IO [MpvResponse] | ||
| 777 | mpvAll' = mpvAll "/var/media/.mpv-ipc" | ||
| 778 | |||
| 779 | mpvOne' :: MpvCommand -> IO (Maybe MpvResponse) | ||
| 780 | mpvOne' = mpvOne "/var/media/.mpv-ipc" | ||
| 781 | |||
| 782 | mediaMpv :: MpvCommand -> X () | ||
| 783 | mediaMpv cmd = void . xfork $ print =<< mpvAll' cmd | ||
| 784 | |||
| 785 | mediaMpvTogglePause :: X () | ||
| 786 | mediaMpvTogglePause = void . xfork $ do | ||
| 787 | paused <- mapM mpvResponse <=< mpvAll' $ MpvGetProperty "pause" | ||
| 788 | if | ||
| 789 | | and paused -> print <=< mpvAll' $ MpvSetProperty "pause" False | ||
| 790 | | otherwise -> print <=< mpvOne' $ MpvSetProperty "pause" True | ||
| 791 | |||
| 792 | myKeys' conf host = Map.fromList $ | ||
| 793 | -- launch a terminal | ||
| 794 | [ ((modm, xK_Return), spawn $ (XMonad.terminal conf) ++ " -e tmux") | ||
| 795 | , ((modm .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf) | ||
| 796 | |||
| 797 | -- launch dmenu | ||
| 798 | --, ((modm, xK_d ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"") | ||
| 799 | , ((modm, xK_d ), shellPrompt "Run: " xPConfigMonospace) | ||
| 800 | , ((modm .|. shiftMask, xK_d ), prompt "Run in Terminal: " ("alacritty" ++ " -e") xPConfigMonospace) | ||
| 801 | , ((modm, xK_at ), sshPrompt (sshOverrides . Just $ hName host) xPConfigMonospace) | ||
| 802 | |||
| 803 | -- close focused window | ||
| 804 | , ((modm .|. shiftMask, xK_q ), kill) | ||
| 805 | , ((modm .|. controlMask .|. shiftMask, xK_q ), spawn "xkill") | ||
| 806 | |||
| 807 | -- Rotate through the available layout algorithms | ||
| 808 | , ((modm, xK_space ), sendMessage NextLayout) | ||
| 809 | |||
| 810 | -- Reset the layouts on the current workspace to default | ||
| 811 | , ((modm .|. controlMask, xK_r ), (setLayout $ XMonad.layoutHook conf) >> refresh) | ||
| 812 | |||
| 813 | -- Resize viewed windows to the correct size | ||
| 814 | , ((modm, xK_r ), refresh) | ||
| 815 | |||
| 816 | -- Move focus to the next window | ||
| 817 | , ((modm, xK_t ), windows W.focusDown) | ||
| 818 | |||
| 819 | -- Move focus to the previous window | ||
| 820 | , ((modm, xK_n ), windows W.focusUp ) | ||
| 821 | |||
| 822 | -- Move focus to the master window | ||
| 823 | , ((modm, xK_m ), windows W.focusMaster ) | ||
| 824 | |||
| 825 | -- Swap the focused window and the master window | ||
| 826 | , ((modm .|. shiftMask, xK_m ), windows W.swapMaster) | ||
| 827 | |||
| 828 | -- Swap the focused window with the next window | ||
| 829 | , ((modm .|. shiftMask, xK_t ), windows W.swapDown ) | ||
| 830 | |||
| 831 | -- Swap the focused window with the previous window | ||
| 832 | , ((modm .|. shiftMask, xK_n ), windows W.swapUp ) | ||
| 833 | |||
| 834 | -- Swap the focused window with the previous window | ||
| 835 | , ((modm .|. shiftMask .|. controlMask, xK_m), sendMessage SwapWindow) | ||
| 836 | |||
| 837 | , ((modm, xK_Right), sendMessage $ Go R) | ||
| 838 | , ((modm, xK_Left ), sendMessage $ Go L) | ||
| 839 | , ((modm, xK_Up ), sendMessage $ Go U) | ||
| 840 | , ((modm, xK_Down ), sendMessage $ Go D) | ||
| 841 | , ((modm .|. shiftMask , xK_Right), sendMessage $ Move R) | ||
| 842 | , ((modm .|. shiftMask , xK_Left ), sendMessage $ Move L) | ||
| 843 | , ((modm .|. shiftMask , xK_Up ), sendMessage $ Move U) | ||
| 844 | , ((modm .|. shiftMask , xK_Down ), sendMessage $ Move D) | ||
| 845 | -- , ((modm .|. controlMask, xK_Right), withFocused $ keysMoveWindow (10, 0)) | ||
| 846 | -- , ((modm .|. controlMask, xK_Left ), withFocused $ keysMoveWindow (-10, 0)) | ||
| 847 | -- , ((modm .|. controlMask, xK_Up ), withFocused $ keysMoveWindow (0, -10)) | ||
| 848 | -- , ((modm .|. controlMask, xK_Down ), withFocused $ keysMoveWindow (0, 10)) | ||
| 849 | -- Shrink the master area | ||
| 850 | , ((modm, xK_h ), sendMessage Shrink) | ||
| 851 | |||
| 852 | -- Expand the master area | ||
| 853 | , ((modm, xK_s ), sendMessage Expand) | ||
| 854 | |||
| 855 | -- Push window back into tiling | ||
| 856 | , ((modm .|. shiftMask, xK_space ), withFocused $ windows . W.sink) | ||
| 857 | , ((modm, xK_BackSpace), focusUrgent) | ||
| 858 | , ((modm .|. shiftMask, xK_BackSpace), clearUrgents) | ||
| 859 | |||
| 860 | -- Increment the number of windows in the master area | ||
| 861 | , ((modm , xK_comma ), sendMessage (IncMasterN 1)) | ||
| 862 | |||
| 863 | -- Deincrement the number of windows in the master area | ||
| 864 | , ((modm , xK_period), sendMessage (IncMasterN (-1))) | ||
| 865 | |||
| 866 | , ((0, xF86XK_AudioRaiseVolume), safeSpawn "pamixer" ["-i", "2"]) | ||
| 867 | , ((0, xF86XK_AudioLowerVolume), safeSpawn "pamixer" ["-d", "2"]) | ||
| 868 | , ((0, xF86XK_AudioMute), safeSpawn "pamixer" ["-t"]) | ||
| 869 | , ((0, xF86XK_AudioPause), mediaMpv $ MpvSetProperty "pause" False) | ||
| 870 | , ((0, {-xF86XK_AudioMicMute-} 269025202), safeSpawn "pulseaudio-ctl" ["mute-input"]) | ||
| 871 | , ((0, xF86XK_AudioPlay), mediaMpvTogglePause) | ||
| 872 | , ((0, xK_Print), do | ||
| 873 | home <- liftIO getHomeDirectory | ||
| 874 | unGrab | ||
| 875 | safeSpawn "scrot" ["-s", "-F", home </> "screenshots" </> "%Y-%m-%dT%H:%M:%S.png", "-e", "xclip -selection clipboard -t image/png -i $f"] | ||
| 876 | ) | ||
| 877 | , ((modm .|. mod1Mask, xK_space), mediaMpvTogglePause) | ||
| 878 | |||
| 879 | -- , ((0, xF86XK_MonBrightnessDown), backlight . cycleThrough $ reverse brCycle) | ||
| 880 | -- , ((0, xF86XK_MonBrightnessUp ), backlight $ cycleThrough brCycle) | ||
| 881 | , ((modm .|. shiftMask , xK_b), backlight . cycleThrough $ reverse brCycle) | ||
| 882 | , ((modm .|. shiftMask .|. controlMask, xK_b), backlight $ cycleThrough brCycle) | ||
| 883 | |||
| 884 | , ((modm , xK_Escape), cycleKbLayout (hKbLayouts host)) | ||
| 885 | , ((modm .|. controlMask, xK_Escape), safeSpawn "setxkbmap" $ fst (head $ hKbLayouts host) : maybeToList (snd . head $ hKbLayouts host)) | ||
| 886 | |||
| 887 | -- Toggle the status bar gap | ||
| 888 | -- Use this binding with avoidStruts from Hooks.ManageDocks. | ||
| 889 | -- See also the statusBar function from Hooks.DynamicLog. | ||
| 890 | -- | ||
| 891 | , ((modm , xK_b ), sendMessage ToggleStruts) | ||
| 892 | |||
| 893 | , ((modm .|. shiftMask, xK_p ), safeSpawn "playerctl" ["-a", "pause"]) | ||
| 894 | |||
| 895 | -- Quit xmonad | ||
| 896 | , ((modm .|. shiftMask, xK_e ), io (exitWith ExitSuccess)) | ||
| 897 | |||
| 898 | -- Restart xmonad | ||
| 899 | -- , ((modm .|. shiftMask .|. controlMask, xK_r ), void . xfork $ recompile False >>= flip when (safeSpawn "xmonad" ["--restart"])) | ||
| 900 | , ((modm .|. shiftMask, xK_r ), void . liftIO $ executeFile "xmonad" True [] Nothing) | ||
| 901 | , ((modm .|. shiftMask, xK_l ), void . xfork $ do | ||
| 902 | sessId <- getEnv "XDG_SESSION_ID" | ||
| 903 | safeSpawn "loginctl" ["lock-session", sessId] | ||
| 904 | ) | ||
| 905 | , ((modm .|. shiftMask, xK_s ), safeSpawn "systemctl" ["suspend"]) | ||
| 906 | , ((modm .|. shiftMask, xK_h ), inputPromptWithCompl xPConfigMonospace "systemctl" powerActCompl ?+ powerAct) | ||
| 907 | , ((modm, xK_v ), windows copyToAll) -- @@ Make focused window always visible | ||
| 908 | , ((modm .|. shiftMask, xK_v ), killAllOtherCopies) -- @@ Toggle window state back | ||
| 909 | , ((modm .|. shiftMask, xK_g ), windowPrompt xPConfig Goto wsWindows) | ||
| 910 | , ((modm , xK_g ), windowPrompt xPConfig Bring allWindows) | ||
| 911 | ] | ||
| 912 | ++ | ||
| 913 | |||
| 914 | -- | ||
| 915 | -- mod-[1..9], Switch to workspace N | ||
| 916 | -- | ||
| 917 | -- mod-[1..9], Switch to workspace N | ||
| 918 | -- mod-shift-[1..9], Move client to workspace N | ||
| 919 | -- | ||
| 920 | [((m .|. modm, k), windows $ f i) | ||
| 921 | | (i, k) <- zip (XMonad.workspaces conf) $ numKeys | ||
| 922 | , (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)] | ||
| 923 | ] | ||
| 924 | ++ | ||
| 925 | [((m .|. modm .|. controlMask, k), void . runMaybeT $ | ||
| 926 | MaybeT (P.getScreen def i) >>= MaybeT . screenWorkspace >>= lift . windows . f | ||
| 927 | ) | ||
| 928 | | (i, k) <- zip (hScreens host) [xK_g, xK_c, xK_r, xK_l] | ||
| 929 | , (f, m) <- [(W.view, 0), (W.shift, shiftMask)] | ||
| 930 | ] | ||
| 931 | where | ||
| 932 | modm = XMonad.modMask conf | ||
| 933 | |||
| 934 | brCycle = [0, 1 % 500, 1 % 250, 1 % 100, 1 % 10, 1 % 4, 1 % 2, 3 % 4, 1] | ||
| 935 | |||
| 936 | powerActWords = ["poweroff", "reboot", "hibernate", "suspend"] | ||
| 937 | powerActCompl = mkComplFunFromList' xPConfigMonospace powerActWords | ||
| 938 | powerAct act | act `elem` powerActWords = safeSpawn "systemctl" $ pure act | ||
| 939 | | otherwise = return () | ||
diff --git a/accounts/gkleen@sif/zshrc b/accounts/gkleen@sif/zshrc index e3f675a1..702990c3 100644 --- a/accounts/gkleen@sif/zshrc +++ b/accounts/gkleen@sif/zshrc | |||
| @@ -2,17 +2,14 @@ dir() { | |||
| 2 | curlArchive=false | 2 | curlArchive=false |
| 3 | templateArchive="" | 3 | templateArchive="" |
| 4 | repoUrl="" | 4 | repoUrl="" |
| 5 | nixShell="" | ||
| 6 | findNix=false | ||
| 7 | dir="" | 5 | dir="" |
| 8 | forceShell=false | 6 | forceShell=false |
| 9 | wormhole=false | 7 | wormhole=false |
| 10 | gitWorktree="" | 8 | gitWorktree="" |
| 11 | # notmuchMsg="" | ||
| 12 | quickserve=false | ||
| 13 | modifyPDF="" | 9 | modifyPDF="" |
| 10 | miniserve=false | ||
| 14 | 11 | ||
| 15 | while getopts ':t:a:s:Sd:ir:wqg:p:' arg; do | 12 | while getopts ':t:a:d:ir:wg:p:m' arg; do |
| 16 | case $arg in | 13 | case $arg in |
| 17 | "t") ;; | 14 | "t") ;; |
| 18 | "a") | 15 | "a") |
| @@ -23,16 +20,13 @@ dir() { | |||
| 23 | templateArchive=${OPTARG:a} | 20 | templateArchive=${OPTARG:a} |
| 24 | fi | 21 | fi |
| 25 | ;; | 22 | ;; |
| 26 | "s") nixShell=${OPTARG:a} ;; | ||
| 27 | "S") findNix=true ;; | ||
| 28 | "d") dir=${OPTARG} ;; | 23 | "d") dir=${OPTARG} ;; |
| 29 | "i") forceShell=true ;; | 24 | "i") forceShell=true ;; |
| 30 | "r") repoUrl=${OPTARG} ;; | 25 | "r") repoUrl=${OPTARG} ;; |
| 31 | "w") wormhole=true ;; | 26 | "w") wormhole=true ;; |
| 32 | "g") gitWorktree=${OPTARG} ;; | 27 | "g") gitWorktree=${OPTARG} ;; |
| 33 | # "n") notmuchMsg=${OPTARG} ;; | ||
| 34 | "q") quickserve=true ;; | ||
| 35 | "p") modifyPDF=${OPTARG:a} ;; | 28 | "p") modifyPDF=${OPTARG:a} ;; |
| 29 | "m") miniserve=true ;; | ||
| 36 | *) printf "Invalid option: %s\n" $arg >&2; exit 2 ;; | 30 | *) printf "Invalid option: %s\n" $arg >&2; exit 2 ;; |
| 37 | esac | 31 | esac |
| 38 | done | 32 | done |
| @@ -56,20 +50,34 @@ dir() { | |||
| 56 | gitWorktree="" | 50 | gitWorktree="" |
| 57 | fi | 51 | fi |
| 58 | 52 | ||
| 53 | miniservePIDFile="" | ||
| 54 | if [[ ${miniserve} = "true" ]]; then | ||
| 55 | miniservePIDFile=$(mktemp --tmpdir --suffix=.pid) | ||
| 56 | fi | ||
| 57 | |||
| 59 | cleanup() | 58 | cleanup() |
| 60 | { | 59 | { |
| 61 | cd ${modifyPDF:h} | 60 | if [[ -n ${modifyPDF} ]]; then |
| 62 | [[ -n ${modifyPDF} ]] && nix shell nixos#imagemagick -c convert -verbose ${dir}/${modifyPDF:t:r}_*.png(on) ${modifyPDF} | 61 | cd ${modifyPDF:h} |
| 62 | typeset -a pages | ||
| 63 | eval 'pages=(${dir}/${modifyPDF:t:r}_*.png(on))' | ||
| 64 | magick -verbose "$pages" ${modifyPDF} | ||
| 65 | modifyPDF="" | ||
| 66 | fi | ||
| 67 | if [[ -n ${miniservePIDFile} ]]; then | ||
| 68 | command kill --verbose -- $(cat ${miniservePIDFile}) && wait $(cat ${miniservePIDFile}) | ||
| 69 | miniservePIDFile="" | ||
| 70 | fi | ||
| 63 | } | 71 | } |
| 64 | 72 | ||
| 65 | ( | 73 | ( |
| 74 | set -o localoptions -o localtraps | ||
| 75 | trap 'return 1' INT TERM | ||
| 66 | trap cleanup EXIT | 76 | trap cleanup EXIT |
| 67 | 77 | ||
| 68 | cd ${dir} | 78 | cd ${dir} |
| 69 | export dir; | 79 | export dir; |
| 70 | 80 | ||
| 71 | ${findNix} && { nixShell=$(findNix) || return $? } | ||
| 72 | |||
| 73 | [[ -n ${repoUrl} ]] && git clone -- ${repoUrl} . | 81 | [[ -n ${repoUrl} ]] && git clone -- ${repoUrl} . |
| 74 | 82 | ||
| 75 | [[ -n ${modifyPDF} ]] && templateArchive=${modifyPDF} | 83 | [[ -n ${modifyPDF} ]] && templateArchive=${modifyPDF} |
| @@ -82,23 +90,23 @@ dir() { | |||
| 82 | } | 90 | } |
| 83 | trap cleanup EXIT | 91 | trap cleanup EXIT |
| 84 | 92 | ||
| 85 | if ${curlArchive}; then | 93 | if [[ $curlArchive = "true" ]]; then |
| 86 | archiveFile=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t}") | 94 | archiveFile=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t}") |
| 87 | 95 | ||
| 88 | curl -L -o ${archiveFile} ${templateArchive} | 96 | curl -SfL -o ${archiveFile} ${templateArchive} |
| 89 | 97 | ||
| 90 | templateArchive=${archiveFile} | 98 | templateArchive=${archiveFile} |
| 91 | fi | 99 | fi |
| 92 | 100 | ||
| 93 | unpack=true | 101 | unpack=true |
| 94 | while ${unpack}; do | 102 | while [[ $unpack = "true" ]]; do |
| 95 | case $(file --brief --mime-type --dereference ${templateArchive}) in | 103 | case $(file --brief --mime-type --dereference ${templateArchive}) in |
| 96 | application/zip) | 104 | application/zip) |
| 97 | unzip ${templateArchive} | 105 | unzip ${templateArchive} |
| 98 | unpack=false | 106 | unpack=false |
| 99 | ;; | 107 | ;; |
| 100 | application/vnd.debian.binary-package) | 108 | application/vnd.debian.binary-package) |
| 101 | nix shell nixos#binutils --command ar x ${templateArchive} | 109 | ar x ${templateArchive} |
| 102 | mkdir control data | 110 | mkdir control data |
| 103 | tar -C control -xvaf control.* | 111 | tar -C control -xvaf control.* |
| 104 | tar -C data -xvaf data.* | 112 | tar -C data -xvaf data.* |
| @@ -106,7 +114,7 @@ dir() { | |||
| 106 | ;; | 114 | ;; |
| 107 | application/x-rpm) | 115 | application/x-rpm) |
| 108 | cpioArchive=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t:r}.cpio") | 116 | cpioArchive=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t:r}.cpio") |
| 109 | nix shell nixos#busybox --command rpm2cpio ${templateArchive} > ${cpioArchive} | 117 | rpm2cpio ${templateArchive} > ${cpioArchive} |
| 110 | templateArchive=${cpioArchive} | 118 | templateArchive=${cpioArchive} |
| 111 | unpack=true | 119 | unpack=true |
| 112 | ;; | 120 | ;; |
| @@ -115,15 +123,19 @@ dir() { | |||
| 115 | unpack=false | 123 | unpack=false |
| 116 | ;; | 124 | ;; |
| 117 | application/pdf) | 125 | application/pdf) |
| 118 | nix shell nixos#ghostscript nixos#imagemagick -c convert -verbose -density 400 ${templateArchive} ${modifyPDF:t:r}_%0d.png | 126 | magick -verbose -density 400 ${templateArchive} ${modifyPDF:t:r}_%0d.png |
| 119 | unpack=false | 127 | unpack=false |
| 120 | ;; | 128 | ;; |
| 121 | application/octet-stream) | 129 | application/octet-stream) |
| 122 | if [[ $(file --brief --dereferenc ${templateArchive}) =~ Squashfs ]]; then | 130 | if [[ $(file --brief --dereference ${templateArchive}) =~ Squashfs ]]; then |
| 123 | nix shell nixos#squashfsTools -c unsquashfs -d . ${templateArchive} | 131 | unsquashfs -d . ${templateArchive} |
| 124 | unpack=false | 132 | unpack=false |
| 125 | fi | 133 | fi |
| 126 | ;; | 134 | ;; |
| 135 | application/x-iso9660-image) | ||
| 136 | 7z x ${templateArchive} | ||
| 137 | unpack=false | ||
| 138 | ;; | ||
| 127 | *) | 139 | *) |
| 128 | tar -xvaf ${templateArchive} | 140 | tar -xvaf ${templateArchive} |
| 129 | unpack=false | 141 | unpack=false |
| @@ -134,25 +146,21 @@ dir() { | |||
| 134 | fi | 146 | fi |
| 135 | 147 | ||
| 136 | 148 | ||
| 137 | ${wormhole} && wormhole receive --accept-file | 149 | [[ $wormhole = "true" ]] && wormhole receive --accept-file |
| 138 | 150 | ||
| 139 | 151 | ||
| 140 | if ${quickserve}; then | 152 | if [[ ${#@} -gt 0 ]]; then |
| 141 | quickserve --root . --upload . --show-hidden --tar gz | 153 | ${@} |
| 142 | fi | 154 | fi |
| 143 | 155 | ||
| 156 | cd $(pwd) # Needed for mounting to work | ||
| 144 | 157 | ||
| 145 | if [[ ${#@} -eq 0 ]] || ${forceShell}; then | 158 | if [[ ${miniserve} = "true" ]]; then |
| 146 | if [[ ${#@} -gt 0 ]]; then | 159 | miniserve --random-route --hidden --enable-tar-gz --enable-zip . & |
| 147 | if [[ -z ${nixShell} ]]; then | 160 | echo $! > "${miniservePIDFile}" |
| 148 | ${@} | 161 | fi |
| 149 | else | ||
| 150 | nix-shell ${nixShell} --run "${@}" | ||
| 151 | fi | ||
| 152 | fi | ||
| 153 | |||
| 154 | cd $(pwd) # Needed for mounting to work | ||
| 155 | 162 | ||
| 163 | if [[ ${#@} -eq 0 ]] && [[ ${miniserve} != "true" ]] || [[ $forceShell = "true" ]]; then | ||
| 156 | isSingleDir() { | 164 | isSingleDir() { |
| 157 | typeset -a contents | 165 | typeset -a contents |
| 158 | contents=(*(N) .*(N)) | 166 | contents=(*(N) .*(N)) |
| @@ -166,18 +174,9 @@ dir() { | |||
| 166 | } | 174 | } |
| 167 | while d=$(isSingleDir); do cd ${d}; done | 175 | while d=$(isSingleDir); do cd ${d}; done |
| 168 | 176 | ||
| 169 | 177 | zsh | |
| 170 | if [[ -z ${nixShell} ]]; then | 178 | elif [[ ${miniserve} == "true" ]]; then |
| 171 | zsh | 179 | wait $(cat "${miniservePIDFile}") |
| 172 | else | ||
| 173 | nix-shell ${nixShell} --run zsh | ||
| 174 | fi | ||
| 175 | else | ||
| 176 | if [[ -z ${nixShell} ]]; then | ||
| 177 | ${@} | ||
| 178 | else | ||
| 179 | nix-shell ${nixShell} --run "${@}" | ||
| 180 | fi | ||
| 181 | fi | 180 | fi |
| 182 | ) | 181 | ) |
| 183 | } | 182 | } |
| @@ -185,27 +184,30 @@ dir() { | |||
| 185 | tmpdir() { | 184 | tmpdir() { |
| 186 | cleanup() | 185 | cleanup() |
| 187 | { | 186 | { |
| 188 | cd / | 187 | cd / |
| 189 | unmount() { | 188 | unmount() { |
| 190 | printf "Unmounting %s\n" ${1} >&2 | 189 | printf "Unmounting %s\n" ${1} >&2 |
| 191 | fusermount -u ${1} || umount ${1} || sudo umount ${1} | 190 | fusermount -u ${1} || umount ${1} || sudo umount ${1} |
| 192 | } | 191 | } |
| 193 | 192 | ||
| 194 | if mountpoint -q -- ${dir}; then | 193 | if [[ -n ${dir} ]]; then |
| 195 | unmount ${dir} || return $? | 194 | if mountpoint -q -- ${dir}; then |
| 196 | else | 195 | unmount ${dir} || return $? |
| 197 | while read -d $'\0' subDir; do | 196 | else |
| 198 | mountpoint -q -- ${subDir} || continue | 197 | while read -d $'\0' subDir; do |
| 199 | unmount ${subDir} || return $? | 198 | mountpoint -q -- ${subDir} || continue |
| 200 | done <<<$(find ${dir} -xdev -type d -print0 | sort -zr) | 199 | unmount ${subDir} || return $? |
| 201 | fi | 200 | done <<<$(find ${dir} -xdev -type d -print0 | sort -zr) |
| 202 | 201 | fi | |
| 203 | rm -rfv --one-file-system -- ${dir} | 202 | |
| 203 | rm -rfv --one-file-system -- ${dir} | ||
| 204 | dir="" | ||
| 205 | fi | ||
| 204 | } | 206 | } |
| 205 | 207 | ||
| 206 | local tmpdir="" | 208 | local tmpdir="" |
| 207 | 209 | ||
| 208 | while getopts ':t:a:s:Sd:ir:wqg:p:' arg; do | 210 | while getopts ':t:a:d:ir:wg:p:m' arg; do |
| 209 | case $arg in | 211 | case $arg in |
| 210 | "t") tmpdir="=${OPTARG}" ;; | 212 | "t") tmpdir="=${OPTARG}" ;; |
| 211 | "?"|":") printf "Invalid option: %s\n" $arg >&2; exit 2 ;; | 213 | "?"|":") printf "Invalid option: %s\n" $arg >&2; exit 2 ;; |
| @@ -213,6 +215,8 @@ tmpdir() { | |||
| 213 | done | 215 | done |
| 214 | 216 | ||
| 215 | ( | 217 | ( |
| 218 | set -o localoptions -o localtraps | ||
| 219 | trap 'return 1' INT TERM | ||
| 216 | trap cleanup EXIT | 220 | trap cleanup EXIT |
| 217 | 221 | ||
| 218 | 222 | ||
| @@ -231,17 +235,7 @@ clock() { | |||
| 231 | } | 235 | } |
| 232 | 236 | ||
| 233 | public-ip() { | 237 | public-ip() { |
| 234 | curl -s -H 'Accept: application/json' $@ ifconfig.co | jq -r '.ip' | 238 | curl -sSf -H 'Accept: application/json' $@ ifconfig.co | jq -r '.ip' |
| 235 | } | ||
| 236 | |||
| 237 | nix-ghci() { | ||
| 238 | pkgExpr="" | ||
| 239 | if [[ ${#@} -gt 0 ]]; then | ||
| 240 | pkgExpr="${1}" | ||
| 241 | shift | ||
| 242 | fi | ||
| 243 | |||
| 244 | nix-shell -p "with (import <nixpkgs> {}); pkgs.haskellPackages.ghcWithPackages (p: with p; [${pkgExpr}])" --run "ghci ${@}" | ||
| 245 | } | 239 | } |
| 246 | 240 | ||
| 247 | swap() { | 241 | swap() { |
| @@ -271,14 +265,6 @@ l() { | |||
| 271 | ls --long --binary --git --time-style=iso --header $@ | 265 | ls --long --binary --git --time-style=iso --header $@ |
| 272 | } | 266 | } |
| 273 | 267 | ||
| 274 | re() { | ||
| 275 | systemctl --restart $@ | ||
| 276 | } | ||
| 277 | |||
| 278 | ure() { | ||
| 279 | systemctl --user --restart $@ | ||
| 280 | } | ||
| 281 | |||
| 282 | ssh-installer() { | 268 | ssh-installer() { |
| 283 | ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o IdentityFile=~/.ssh/gkleen@sif.midgard.yggdrasil $@ | 269 | ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o IdentityFile=~/.ssh/gkleen@sif.midgard.yggdrasil $@ |
| 284 | } | 270 | } |
| @@ -306,20 +292,7 @@ done < <(find ~/projects ~/uni -regextype posix-extended -maxdepth 2 -type d -re | |||
| 306 | sed -zr 's|(.*/([0-9]{2}[ws])/(.+))|\1 \2 \3|' | \ | 292 | sed -zr 's|(.*/([0-9]{2}[ws])/(.+))|\1 \2 \3|' | \ |
| 307 | sort -z -r -k2 | sort -z -s -k3 | uniq -z -f 2) | 293 | sort -z -r -k2 | sort -z -s -k3 | uniq -z -f 2) |
| 308 | 294 | ||
| 309 | alias '..'='cd ..' | ||
| 310 | alias rzadm=$'tmpdir -i sh -c \'mkdir adm; sshfs gkleen@mgmt01:/adm adm\'' | 295 | alias rzadm=$'tmpdir -i sh -c \'mkdir adm; sshfs gkleen@mgmt01:/adm adm\'' |
| 311 | alias mathcloud=$'tmpdir -i rclone mount --daemon mathcloud:// .' | 296 | alias mathcloud=$'tmpdir -i rclone mount --daemon mathcloud:// .' |
| 312 | alias -g L='| less' | ||
| 313 | alias -g S='&> /dev/null' | ||
| 314 | alias -g G='| grep' | ||
| 315 | alias -g B='&> /dev/null &' | ||
| 316 | alias -g BB='&> /dev/null &!' | ||
| 317 | 297 | ||
| 318 | export DEFAULT_USER=gkleen | 298 | export DEFAULT_USER=gkleen |
| 319 | |||
| 320 | bindkey -e | ||
| 321 | bindkey ';5C' emacs-forward-word | ||
| 322 | bindkey ';5D' emacs-backward-word | ||
| 323 | bindkey '^[[1;5C' emacs-forward-word | ||
| 324 | bindkey '^[[1;5D' emacs-backward-word | ||
| 325 | bindkey '^H' backward-kill-word | ||
diff --git a/accounts/gkleen@surtr.nix b/accounts/gkleen@surtr.nix index 58c4f21d..8f678ac9 100644 --- a/accounts/gkleen@surtr.nix +++ b/accounts/gkleen@surtr.nix | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | { userName, ... }: { | 1 | { flake, userName, ... }: { |
| 2 | home-manager.users.${userName}.home.stateVersion = "20.09"; | 2 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 3 | zsh tmux | ||
| 4 | ]; | ||
| 5 | |||
| 6 | config.home-manager.users.${userName}.home.stateVersion = "20.09"; | ||
| 3 | } | 7 | } |
diff --git a/accounts/gkleen@vidhar.nix b/accounts/gkleen@vidhar.nix index 8509c2f4..3a37c4bd 100644 --- a/accounts/gkleen@vidhar.nix +++ b/accounts/gkleen@vidhar.nix | |||
| @@ -1,4 +1,8 @@ | |||
| 1 | { flake, pkgs, userName, config, ... }: { | 1 | { flake, pkgs, userName, config, ... }: { |
| 2 | imports = with flake.nixosModules.userProfiles.${userName}; [ | ||
| 3 | zsh tmux | ||
| 4 | ]; | ||
| 5 | |||
| 2 | config = { | 6 | config = { |
| 3 | users.users.${userName} = { | 7 | users.users.${userName} = { |
| 4 | uid = 1000; | 8 | uid = 1000; |
diff --git a/accounts/mherold@eostre.nix b/accounts/mherold@eostre.nix index 51e4529a..0e2f37aa 100644 --- a/accounts/mherold@eostre.nix +++ b/accounts/mherold@eostre.nix | |||
| @@ -7,9 +7,9 @@ | |||
| 7 | home-manager.users.${userName} = { | 7 | home-manager.users.${userName} = { |
| 8 | home.stateVersion = "20.09"; | 8 | home.stateVersion = "20.09"; |
| 9 | 9 | ||
| 10 | nixpkgs.config = { | 10 | # nixpkgs.config = { |
| 11 | allowUnfree = true; | 11 | # allowUnfree = true; |
| 12 | }; | 12 | # }; |
| 13 | 13 | ||
| 14 | home.packages = with pkgs; [ | 14 | home.packages = with pkgs; [ |
| 15 | thunderbird libreoffice element-desktop keepassxc vlc | 15 | thunderbird libreoffice element-desktop keepassxc vlc |
diff --git a/accounts/root@installer.nix b/accounts/root@installer.nix index c7a418f8..5fe1db38 100644 --- a/accounts/root@installer.nix +++ b/accounts/root@installer.nix | |||
| @@ -1,7 +1,11 @@ | |||
| 1 | { userName, ... }: | 1 | { flake, userName, ... }: |
| 2 | 2 | ||
| 3 | { | 3 | { |
| 4 | home-manager.users.${userName} = { config, ... } : { | 4 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 5 | zsh tmux | ||
| 6 | ]; | ||
| 7 | |||
| 8 | config.home-manager.users.${userName} = { config, ... } : { | ||
| 5 | home.stateVersion = config.home.version.release; | 9 | home.stateVersion = config.home.version.release; |
| 6 | }; | 10 | }; |
| 7 | } | 11 | } |
diff --git a/accounts/root@sif.nix b/accounts/root@sif.nix index c9e129a0..bb816230 100644 --- a/accounts/root@sif.nix +++ b/accounts/root@sif.nix | |||
| @@ -1,6 +1,10 @@ | |||
| 1 | { userName, ... }: | 1 | { flake, userName, ... }: |
| 2 | { | 2 | { |
| 3 | home-manager.users.${userName} = { | 3 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 4 | zsh tmux | ||
| 5 | ]; | ||
| 6 | |||
| 7 | config.home-manager.users.${userName} = { | ||
| 4 | home.stateVersion = "20.09"; | 8 | home.stateVersion = "20.09"; |
| 5 | 9 | ||
| 6 | programs.ssh.matchBlocks = { | 10 | programs.ssh.matchBlocks = { |
diff --git a/accounts/root@surtr.nix b/accounts/root@surtr.nix index 58c4f21d..8f678ac9 100644 --- a/accounts/root@surtr.nix +++ b/accounts/root@surtr.nix | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | { userName, ... }: { | 1 | { flake, userName, ... }: { |
| 2 | home-manager.users.${userName}.home.stateVersion = "20.09"; | 2 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 3 | zsh tmux | ||
| 4 | ]; | ||
| 5 | |||
| 6 | config.home-manager.users.${userName}.home.stateVersion = "20.09"; | ||
| 3 | } | 7 | } |
diff --git a/accounts/root@vidhar.nix b/accounts/root@vidhar.nix index e82414a8..0fc56633 100644 --- a/accounts/root@vidhar.nix +++ b/accounts/root@vidhar.nix | |||
| @@ -1,6 +1,11 @@ | |||
| 1 | { config, userName, ... }: | 1 | { flake, config, userName, ... }: |
| 2 | |||
| 2 | { | 3 | { |
| 3 | home-manager.users.${userName} = { | 4 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 5 | zsh tmux | ||
| 6 | ]; | ||
| 7 | |||
| 8 | config.home-manager.users.${userName} = { | ||
| 4 | home.stateVersion = "20.09"; | 9 | home.stateVersion = "20.09"; |
| 5 | 10 | ||
| 6 | programs.ssh.matchBlocks = { | 11 | programs.ssh.matchBlocks = { |
