From ec5faa59a995fa0cdcdafeec38a17efee479e130 Mon Sep 17 00:00:00 2001 From: unexplrd Date: Sun, 9 Feb 2025 23:40:59 +0200 Subject: [PATCH] swaync and gtklock --- flake.lock | 8 +- modules/home/desktop/niri/default.nix | 774 ++++++++++++++++---------- modules/nixos/desktop/niri.nix | 1 + 3 files changed, 476 insertions(+), 307 deletions(-) diff --git a/flake.lock b/flake.lock index 851ddc7..4e1f565 100644 --- a/flake.lock +++ b/flake.lock @@ -635,11 +635,11 @@ ] }, "locked": { - "lastModified": 1738941286, - "narHash": "sha256-rBm+US6iZoimF2/AkrWNL3omc+6v4Fj39MbJ4r2U//g=", + "lastModified": 1739126593, + "narHash": "sha256-aVixbVsMsb63xH8Bi/6/xaN1rEF8uMvx0vWbN41Or6o=", "ref": "refs/heads/main", - "rev": "5bb8ba507ea45fb8a6865526c8b81bdc41e431c2", - "revCount": 3, + "rev": "c528a43b7ab04cd818441dc87ccde082b33bd12f", + "revCount": 6, "type": "git", "url": "https://gitea.linerds.us/unexplrd/Neve" }, diff --git a/modules/home/desktop/niri/default.nix b/modules/home/desktop/niri/default.nix index 65fed8e..6396c31 100644 --- a/modules/home/desktop/niri/default.nix +++ b/modules/home/desktop/niri/default.nix @@ -7,6 +7,10 @@ }: with lib; let cfg = config.desktop.niri; + terminal = "ghostty"; + launcher = "walker"; + browser = "app.zen_browser.zen"; + lockscreen = "gtklock"; in { options = { desktop.niri.enable = @@ -41,8 +45,186 @@ in { }; systemdTarget = "graphical-session.target"; }; - dunst = { + swaync = { enable = true; + settings = { + # cssPriority = "user"; + image-visibility = "when-available"; + keyboard-shortcut = true; + relative-timestamps = true; + timeout = 5; + timeout-low = 5; + timeout-critical = 0; + script-fail-notify = true; + transition-time = 200; + + # Layer settings + layer-shell = true; + layer = "overlay"; + control-center-layer = "overlay"; + + # Notification settings + positionX = "right"; + positionY = "top"; + notification-2fa-action = true; + notification-inline-replies = false; + notification-icon-size = 32; + notification-body-image-height = 100; + notification-body-image-width = 200; + notification-window-width = 300; + + # Control center settings + control-center-positionX = "right"; + control-center-positionY = "top"; + control-center-width = 500; + control-center-exclusive-zone = true; + fit-to-screen = true; + hide-on-action = true; + hide-on-clear = false; + + # Widget settings + widgets = [ + "title" + "dnd" + "notifications" + "mpris" + ]; + + widget-config = { + title = { + text = "Notifications"; + clear-all-button = true; + button-text = "Clear All"; + }; + dnd = { + text = "Do Not Disturb"; + }; + mpris = { + image-size = 96; + image-radius = 12; + blur = true; + }; + }; + }; + style = '' + /*** Global ***/ + progress, + progressbar, + trough { + border-radius: 16px; + } + + .app-icon, + .image { + -gtk-icon-effect: none; + } + + .notification-action { + border-radius: 12px; + margin: 0.5rem; + } + + .close-button { + margin: 24px; + padding: 0.2rem; + border-radius: 16px; + } + + /*** Notifications ***/ + .notification-group.collapsed + .notification-row:not(:last-child) + .notification-action, + .notification-group.collapsed + .notification-row:not(:last-child) + .notification-default-action { + opacity: 0; + } + + .trough { + margin: 4px; + border-radius: 7px; + } + + .notification, + .notification.low, + .notification.normal, + .notification.critical, + .control-center { + margin: 16px; + border-radius: 7px; + } + + .notification-content, + .floating-notifications { + border: transparent; + background: transparent; + } + + .control-center-list { + background: transparent; + } + + /*** Widgets ***/ + /* Title widget */ + .widget-title { + margin: 0.5rem; + } + + .widget-title > label { + font-weight: bold; + } + + .widget-title > button { + border-radius: 16px; + padding: 0.5rem; + } + + /* DND Widget */ + .widget-dnd { + margin: 0.5rem; + } + + .widget-dnd > label { + font-weight: bold; + } + + .widget-dnd > switch { + border-radius: 16px; + } + + .widget-dnd > switch slider { + border-radius: 16px; + padding: 0.25rem; + } + + /* Mpris widget */ + .widget-mpris .widget-mpris-player { + border-radius: 16px; + margin: 0.5rem; + padding: 0.5rem; + } + + .widget-mpris .widget-mpris-player .widget-mpris-album-art { + border-radius: 16px; + } + + .widget-mpris .widget-mpris-player .widget-mpris-title { + font-weight: bold; + } + + .widget-mpris .widget-mpris-player .widget-mpris-subtitle { + font-weight: normal; + } + + .widget-mpris .widget-mpris-player > box > button { + border: 1px solid transparent; + border-radius: 16px; + padding: 0.25rem; + } + ''; + }; + dunst = { + enable = false; settings = { global = { width = 300; @@ -67,13 +249,13 @@ in { settings = { general = { before_sleep_cmd = "loginctl lock-session"; - lock_cmd = "pidof hyprlock || hyprlock"; + lock_cmd = "pidof ${lockscreen} || ${lockscreen}"; # unlock_cmd = "loginctl unlock-session"; }; listener = [ { timeout = 600; - on-timeout = "pidof hyprlock || hyprlock"; + on-timeout = "pidof ${lockscreen} || ${lockscreen}"; } { timeout = 601; @@ -84,7 +266,7 @@ in { }; }; programs.hyprlock = { - enable = true; + enable = false; settings = { general = { disable_loading_bar = true; @@ -173,11 +355,6 @@ in { target = "niri/config.kdl"; text = let cursor_size = toString config.stylix.cursor.size; - terminal = "ghostty"; - launcher = "walker"; - browser = "app.zen_browser.zen"; - lockscreen = "hyprlock"; - # clipboard = "copyq toggle"; left = "n"; down = "e"; up = "i"; @@ -278,7 +455,6 @@ in { width 3 active-gradient from="#${config.lib.stylix.colors.base0D}" to="#${config.lib.stylix.colors.base04}" angle=40 relative-to="workspace-view" inactive-color "#${config.lib.stylix.colors.base03}" - // inactive-gradient from="#1c1c1c" to="#4e4e4e" angle=60 relative-to="workspace-view" } // You can also add a border. It's similar to the focus ring, but always visible. @@ -288,13 +464,8 @@ in { off width 4 - // active-color "#ffc87f" inactive-color "#${config.lib.stylix.colors.base00}" // active-gradient from="#${config.lib.stylix.colors.base0D}" to="#${config.lib.stylix.colors.base0C}" angle=40 relative-to="workspace-view" - // inactive-gradient from="#1c1c1c" to="#4e4e4e" angle=60 relative-to="workspace-view" - - // active-gradient from="#ffbb66" to="#ffc880" angle=45 relative-to="workspace-view" - // inactive-gradient from="#505050" to="#808080" angle=45 relative-to="workspace-view" } struts { @@ -313,7 +484,7 @@ in { clip-to-geometry true } - screenshot-path "~/pics/screenshots/screenshot-%Y-%m-%d-%H-%M-%S.png" + screenshot-path "${config.xdg.userDirs.pictures}/screenshots/screenshot-%Y-%m-%d-%H-%M-%S.png" // screenshot-path null animations { @@ -331,10 +502,10 @@ in { open-focused true } - // window-rule { - // match app-id=r#"^com\.mitchellh\.ghostty$"# - // draw-border-with-background false - // } + window-rule { + match app-id=r#"^com\.mitchellh\.ghostty$"# + draw-border-with-background false + } window-rule { match app-id=r#"^org\.keepassxc\.KeePassXC$"# @@ -355,9 +526,6 @@ in { Super+Alt+L repeat=false { spawn "${lockscreen}"; } Super+Alt+B repeat=false { spawn "${browser}"; } Super+Alt+V repeat=false { spawn "copyq" "toggle"; } - Super+Alt+D repeat=false { spawn "dunstctl" "set-paused" "toggle"; } - Super+Alt+C repeat=false { spawn "dunstctl" "close-all"; } - Super+Alt+X repeat=false { spawn "dunstctl" "history-pop"; } Mod+Alt+Q { quit; } Mod+Alt+P { power-off-monitors; } @@ -547,7 +715,11 @@ in { "clock" "wlr/taskbar" ]; - modules-center = ["group/niri" "privacy"]; + modules-center = [ + "custom/notification" + "group/niri" + "privacy" + ]; modules-right = [ "tray" "wireplumber" @@ -641,14 +813,6 @@ in { on-scroll-down = "~/.local/bin/keyboard-brightness.nu change -1"; interval = 1; }; - # "custom/kbd-backlight" = { - # exec = "cat /sys/class/leds/platform::kbd_backlight/brightness"; - # interval = 1; - # format = "{}% {icon}"; - # format-icons = ["󰌌"]; - # on-scroll-up = "light -s sysfs/leds/platform::kbd_backlight -A 5%"; - # on-scroll-down = "light -s sysfs/leds/platform::kbd_backlight -U 5%"; - # }; battery = { states = { good = 95; @@ -728,23 +892,25 @@ in { }; }; }; - style = '' - @define-color base00 #${config.lib.stylix.colors.base00}; - @define-color base01 #${config.lib.stylix.colors.base01}; - @define-color base02 #${config.lib.stylix.colors.base02}; - @define-color base03 #${config.lib.stylix.colors.base03}; - @define-color base04 #${config.lib.stylix.colors.base04}; - @define-color base05 #${config.lib.stylix.colors.base05}; - @define-color base06 #${config.lib.stylix.colors.base06}; - @define-color base07 #${config.lib.stylix.colors.base07}; - @define-color base08 #${config.lib.stylix.colors.base08}; - @define-color base09 #${config.lib.stylix.colors.base09}; - @define-color base0A #${config.lib.stylix.colors.base0A}; - @define-color base0B #${config.lib.stylix.colors.base0B}; - @define-color base0C #${config.lib.stylix.colors.base0C}; - @define-color base0D #${config.lib.stylix.colors.base0D}; - @define-color base0E #${config.lib.stylix.colors.base0E}; - @define-color base0F #${config.lib.stylix.colors.base0F}; + style = let + stylix = config.lib.stylix.colors; + in '' + @define-color base00 #${stylix.base00}; + @define-color base01 #${stylix.base01}; + @define-color base02 #${stylix.base02}; + @define-color base03 #${stylix.base03}; + @define-color base04 #${stylix.base04}; + @define-color base05 #${stylix.base05}; + @define-color base06 #${stylix.base06}; + @define-color base07 #${stylix.base07}; + @define-color base08 #${stylix.base08}; + @define-color base09 #${stylix.base09}; + @define-color base0A #${stylix.base0A}; + @define-color base0B #${stylix.base0B}; + @define-color base0C #${stylix.base0C}; + @define-color base0D #${stylix.base0D}; + @define-color base0E #${stylix.base0E}; + @define-color base0F #${stylix.base0F}; * { font-family: Iosevka Nerd Font Propo, FontAwesome; font-size: 100%; @@ -1148,157 +1314,155 @@ in { }; }; }; - style = '' - @define-color base00 #${config.lib.stylix.colors.base00}; - @define-color base01 #${config.lib.stylix.colors.base01}; - @define-color base02 #${config.lib.stylix.colors.base02}; - @define-color base03 #${config.lib.stylix.colors.base03}; - @define-color base04 #${config.lib.stylix.colors.base04}; - @define-color base05 #${config.lib.stylix.colors.base05}; - @define-color base06 #${config.lib.stylix.colors.base06}; - @define-color base07 #${config.lib.stylix.colors.base07}; - @define-color base08 #${config.lib.stylix.colors.base08}; - @define-color base09 #${config.lib.stylix.colors.base09}; - @define-color base0A #${config.lib.stylix.colors.base0A}; - @define-color base0B #${config.lib.stylix.colors.base0B}; - @define-color base0C #${config.lib.stylix.colors.base0C}; - @define-color base0D #${config.lib.stylix.colors.base0D}; - @define-color base0E #${config.lib.stylix.colors.base0E}; - @define-color base0F #${config.lib.stylix.colors.base0F}; + style = let + stylix = config.lib.stylix.colors; + in '' + @define-color base00 #${stylix.base00}; + @define-color base01 #${stylix.base01}; + @define-color base02 #${stylix.base02}; + @define-color base03 #${stylix.base03}; + @define-color base04 #${stylix.base04}; + @define-color base05 #${stylix.base05}; + @define-color base06 #${stylix.base06}; + @define-color base07 #${stylix.base07}; + @define-color base08 #${stylix.base08}; + @define-color base09 #${stylix.base09}; + @define-color base0A #${stylix.base0A}; + @define-color base0B #${stylix.base0B}; + @define-color base0C #${stylix.base0C}; + @define-color base0D #${stylix.base0D}; + @define-color base0E #${stylix.base0E}; + @define-color base0F #${stylix.base0F}; - #window, - #box, - #search, - #password, - #input, - #typeahead, - #spinner, - #list, - child, - scrollbar, - slider, - #item, - #text, - #label, - #sub, - #activationlabel { - all: unset; - } + #window, + #box, + #search, + #password, + #input, + #typeahead, + #spinner, + #list, + child, + scrollbar, + slider, + #item, + #text, + #label, + #sub, + #activationlabel { + all: unset; + } - #window { - background: none; - color: @base05; - } + #window { + background: none; + color: @base05; + } - #box { - border-radius: 20px; - border-width: 4px; - border-color: @base0D; - background: @base00; - } + #box { + border-radius: 20px; + border-width: 4px; + border-color: @base0D; + background: @base00; + } - #search { - background: @base01; - border-radius: 12px; - padding: 8px; - } + #search { + background: @base01; + border-radius: 12px; + padding: 8px; + } - #password, - #input, - #typeahead { - background: none; - box-shadow: none; - border-radius: 12px; - } + #password, + #input, + #typeahead { + background: none; + box-shadow: none; + border-radius: 12px; + } - #input > *:first-child, - #typeahead > *:first-child { - margin-right: 12px; - border-radius: 12px; - } + #input > *:first-child, + #typeahead > *:first-child { + margin-right: 12px; + border-radius: 12px; + } - /* #spinner { - } */ + /* #spinner { + } */ - #typeahead { - color: @base05; - } + #typeahead { + color: @base05; + } - #input placeholder { - opacity: 0.5; - } + #input placeholder { + opacity: 0.5; + } - #list { - padding: 8px; - border-radius: 12px; - background: @base01; - } + #list { + padding: 8px; + border-radius: 12px; + background: @base01; + } - child { - box-shadow: none; - } + child { + box-shadow: none; + } - child:selected, - child:hover { - box-shadow: none; - border-radius: 8px; - background: @base02; - } + child:selected, + child:hover { + box-shadow: none; + border-radius: 8px; + background: @base02; + } - #item { - padding: 4px; - } + #item { + padding: 4px; + } - /* #icon { - } + /* #icon { + } - #text { - } + #text { + } - #label { - } */ + #label { + } */ - #sub { - font-size: smaller; - opacity: 0.5; - } + #sub { + font-size: smaller; + opacity: 0.5; + } - #activationlabel { - opacity: 0.5; - } + #activationlabel { + opacity: 0.5; + } - .activation #activationlabel { - opacity: 1; - color: @base05; - } + .activation #activationlabel { + opacity: 1; + color: @base05; + } - .activation #text, - .activation #icon, - .activation #search { - opacity: 0.5; - } + .activation #text, + .activation #icon, + .activation #search { + opacity: 0.5; + } ''; }; }; programs.wpaperd.enable = true; home.packages = with pkgs; [ - niri + celluloid nautilus + gtklock helvum loupe + niri evince - celluloid junction - swayidle - swaylock-effects libnotify - playerctl wdisplays pwvucontrol wl-clipboard-rs - polkit_gnome - xwayland-satellite ]; gtk = { @@ -1317,158 +1481,162 @@ in { icon-theme = "${config.gtk.iconTheme.name}"; }; }; - xdg.mime.enable = true; - xdg.mimeApps = { - enable = true; - defaultApplications = let - file_manager = ["org.gnome.Nautilus.desktop"]; - web_browser = ["re.sonny.Junction.desktop"]; - image_viewer = ["org.gnome.Loupe.desktop"]; - video_player = ["io.github.celluloid_player.Celluloid.desktop"]; - pdf_reader = ["org.gnome.Evince.desktop"]; - in { - "inode/directory" = file_manager; - "video/x-matroska" = video_player; - "video/mp4" = video_player; - "video/mpeg" = video_player; - "video/x-mpeg" = video_player; - "image/png" = image_viewer; - "image/jpeg" = image_viewer; - "application/pdf" = pdf_reader; - "text/html" = web_browser; - "x-scheme-handler/http" = web_browser; - "x-scheme-handler/https" = web_browser; - "x-scheme-handler/about" = web_browser; - "x-scheme-handler/unknown" = web_browser; + xdg = { + mime.enable = true; + mimeApps = { + enable = true; + defaultApplications = let + file_manager = ["org.gnome.Nautilus.desktop"]; + web_browser = ["re.sonny.Junction.desktop"]; + image_viewer = ["org.gnome.Loupe.desktop"]; + video_player = ["io.github.celluloid_player.Celluloid.desktop"]; + pdf_reader = ["org.gnome.Evince.desktop"]; + in { + "inode/directory" = file_manager; + "video/x-matroska" = video_player; + "video/mp4" = video_player; + "video/mpeg" = video_player; + "video/x-mpeg" = video_player; + "image/png" = image_viewer; + "image/jpeg" = image_viewer; + "application/pdf" = pdf_reader; + "text/html" = web_browser; + "x-scheme-handler/http" = web_browser; + "x-scheme-handler/https" = web_browser; + "x-scheme-handler/about" = web_browser; + "x-scheme-handler/unknown" = web_browser; + }; }; }; - systemd.user.settings.Manager.DefaultEnvironment = { - # QT_QPA_PLATFORMTHEME = "gtk3"; - QT_QPA_PLATFORM = "wayland"; - DISPLAY = ":123"; - }; - systemd.user.targets.tray = { - # workaround for udiskie - Unit = { - Description = "Home Manager System Tray"; + systemd.user = { + settings.Manager.DefaultEnvironment = { + # QT_QPA_PLATFORMTHEME = "gtk3"; + QT_QPA_PLATFORM = "wayland"; + DISPLAY = ":123"; }; - }; - systemd.user.services = { - udiskie = { + targets.tray = { + # workaround for udiskie Unit = { - PartOf = ["graphical-session.target"]; - After = ["graphical-session.target"]; - Requisite = ["graphical-session.target"]; - }; - Install = { - WantedBy = ["niri.service"]; + Description = "Home Manager System Tray"; }; }; - waybar = { - Unit = { - PartOf = ["graphical-session.target"]; - After = ["graphical-session.target"]; - Requisite = ["graphical-session.target"]; + services = { + udiskie = { + Unit = { + PartOf = ["graphical-session.target"]; + After = ["graphical-session.target"]; + Requisite = ["graphical-session.target"]; + }; + Install = { + WantedBy = ["niri.service"]; + }; }; - Install = { - WantedBy = ["niri.service"]; + waybar = { + Unit = { + PartOf = ["graphical-session.target"]; + After = ["graphical-session.target"]; + Requisite = ["graphical-session.target"]; + }; + Install = { + WantedBy = ["niri.service"]; + }; }; - }; - copyq = { - Unit = { - PartOf = ["graphical-session.target"]; - After = ["graphical-session.target"]; - Requisite = ["graphical-session.target"]; + copyq = { + Unit = { + PartOf = ["graphical-session.target"]; + After = ["graphical-session.target"]; + Requisite = ["graphical-session.target"]; + }; + Install = { + WantedBy = ["niri.service"]; + }; + Service = { + Environment = lib.mkForce "QT_QPA_PLATFORM=wayland"; + Restart = "on-failure"; + RestartSec = 1; + TimeoutStopSec = 10; + }; }; - Install = { - WantedBy = ["niri.service"]; + wpaperd = { + Unit = { + PartOf = ["graphical-session.target"]; + After = ["graphical-session.target"]; + Requisite = ["graphical-session.target"]; + }; + Install = { + WantedBy = ["niri.service"]; + }; + Service = { + Type = "simple"; + ExecStart = "${pkgs.wpaperd}/bin/wpaperd"; + Restart = "on-failure"; + RestartSec = 1; + TimeoutStopSec = 10; + }; }; - Service = { - Environment = lib.mkForce "QT_QPA_PLATFORM=wayland"; - Restart = "on-failure"; - RestartSec = 1; - TimeoutStopSec = 10; + network-manager-applet = { + Unit = { + PartOf = ["graphical-session.target"]; + After = ["graphical-session.target"]; + Requisite = ["graphical-session.target"]; + }; + Install = { + WantedBy = ["niri.service"]; + }; }; - }; - wpaperd = { - Unit = { - PartOf = ["graphical-session.target"]; - After = ["graphical-session.target"]; - Requisite = ["graphical-session.target"]; + gnome-polkit-agent = { + Unit = { + PartOf = ["graphical-session.target"]; + After = ["graphical-session.target"]; + Requisite = ["graphical-session.target"]; + }; + Install = { + WantedBy = ["niri.service"]; + }; + Service = { + Type = "simple"; + ExecStart = "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1"; + Restart = "on-failure"; + RestartSec = 1; + TimeoutStopSec = 10; + }; }; - Install = { - WantedBy = ["niri.service"]; + wlsunset = { + Unit = { + PartOf = ["graphical-session.target"]; + After = ["graphical-session.target"]; + Requisite = ["graphical-session.target"]; + }; + Install = { + WantedBy = ["niri.service"]; + }; }; - Service = { - Type = "simple"; - ExecStart = "${pkgs.wpaperd}/bin/wpaperd"; - Restart = "on-failure"; - RestartSec = 1; - TimeoutStopSec = 10; + hypridle = { + Unit = { + PartOf = ["graphical-session.target"]; + After = ["graphical-session.target"]; + Requisite = ["graphical-session.target"]; + }; + Install = { + WantedBy = ["niri.service"]; + }; }; - }; - network-manager-applet = { - Unit = { - PartOf = ["graphical-session.target"]; - After = ["graphical-session.target"]; - Requisite = ["graphical-session.target"]; - }; - Install = { - WantedBy = ["niri.service"]; - }; - }; - gnome-polkit-agent = { - Unit = { - PartOf = ["graphical-session.target"]; - After = ["graphical-session.target"]; - Requisite = ["graphical-session.target"]; - }; - Install = { - WantedBy = ["niri.service"]; - }; - Service = { - Type = "simple"; - ExecStart = "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1"; - Restart = "on-failure"; - RestartSec = 1; - TimeoutStopSec = 10; - }; - }; - wlsunset = { - Unit = { - PartOf = ["graphical-session.target"]; - After = ["graphical-session.target"]; - Requisite = ["graphical-session.target"]; - }; - Install = { - WantedBy = ["niri.service"]; - }; - }; - hypridle = { - Unit = { - PartOf = ["graphical-session.target"]; - After = ["graphical-session.target"]; - Requisite = ["graphical-session.target"]; - }; - Install = { - WantedBy = ["niri.service"]; - }; - }; - xwayland-satellite = { - Unit = { - PartOf = ["graphical-session.target"]; - After = ["graphical-session.target"]; - Requisite = ["graphical-session.target"]; - }; - Install = { - WantedBy = ["niri.service"]; - }; - Service = { - Type = "simple"; - ExecStart = "${pkgs.xwayland-satellite}/bin/xwayland-satellite :123"; - Restart = "on-failure"; - RestartSec = 1; - TimeoutStopSec = 10; + xwayland-satellite = { + Unit = { + PartOf = ["graphical-session.target"]; + After = ["graphical-session.target"]; + Requisite = ["graphical-session.target"]; + }; + Install = { + WantedBy = ["niri.service"]; + }; + Service = { + Type = "simple"; + ExecStart = "${lib.getExe pkgs.xwayland-satellite} :123"; + Restart = "on-failure"; + RestartSec = 1; + TimeoutStopSec = 10; + }; }; }; }; diff --git a/modules/nixos/desktop/niri.nix b/modules/nixos/desktop/niri.nix index 22d25e0..102b8f6 100644 --- a/modules/nixos/desktop/niri.nix +++ b/modules/nixos/desktop/niri.nix @@ -20,6 +20,7 @@ in { fonts.fontDir.enable = true; security.pam = { services.hyprlock = {}; + services.gtklock = {}; loginLimits = [ { domain = "@users";