diff --git a/modules/applications/labwc/home.nix b/modules/applications/labwc/home.nix index 6365903..60e2de7 100644 --- a/modules/applications/labwc/home.nix +++ b/modules/applications/labwc/home.nix @@ -107,7 +107,7 @@ in (execute "W-v" "vicinae vicinae://launch/clipboard/history?toggle=true") (execute "W-space" "ghostty +toggle-quick-terminal") (execute "W-p" "wdisplays") - (execute "W-s" "noctalia msg panel-toggle control-center") + (execute "W-s" "noctalia msg panel-toggle launcher") (execute "W-comma" "noctalia msg settings-toggle") (action "W-S-e" "Exit") ] diff --git a/modules/applications/niri/home.nix b/modules/applications/niri/home.nix index 92b07b6..f9c4018 100644 --- a/modules/applications/niri/home.nix +++ b/modules/applications/niri/home.nix @@ -59,6 +59,15 @@ in ]; hotkey-overlay.title = "Run an Application: vicinae"; }; + "Mod+S" = { + action.spawn = [ + "noctalia" + "msg" + "panel-toggle" + "launcher" + ]; + hotkey-overlay.title = "Run an Application: Noctalia Launcher"; + }; "Mod+E" = { action.spawn = [ "nautilus" diff --git a/modules/applications/noctalia/plugins/codexbar-usage/usage.luau b/modules/applications/noctalia/plugins/codexbar-usage/usage.luau index 3eab7d7..c2c33ef 100644 --- a/modules/applications/noctalia/plugins/codexbar-usage/usage.luau +++ b/modules/applications/noctalia/plugins/codexbar-usage/usage.luau @@ -3,6 +3,7 @@ local refreshIntervalMs = 60000 local usagePage = "https://chatgpt.com/codex/settings/usage" local refreshing = false +local lastSuccessfulUsage = nil local function percent(value) if type(value) ~= "number" then @@ -71,7 +72,7 @@ local function showError(message) barWidget.setTooltip("CodexBar: " .. message) end -local function showUsage(item) +local function showUsage(item, refreshError) local usage = item.usage or {} local primary = usage.primary or {} local secondary = usage.secondary or {} @@ -90,11 +91,26 @@ local function showUsage(item) .. secondaryReset .. ")" ) - barWidget.setTooltip({ + local tooltip = { { "5-hour usage", percent(primary.usedPercent) .. " · resets in " .. primaryReset }, { "Weekly usage", percent(secondary.usedPercent) .. " · resets in " .. secondaryReset }, { "Click", "Open Codex usage settings" }, - }) + } + + if refreshError ~= nil then + table.insert(tooltip, 3, { "Status", "Showing the last successful result (refresh failed: " .. refreshError .. ")" }) + end + + barWidget.setTooltip(tooltip) +end + +local function showRefreshFailure(message) + if lastSuccessfulUsage ~= nil then + showUsage(lastSuccessfulUsage, message) + return + end + + showError(message) end local function refresh() @@ -104,27 +120,31 @@ local function refresh() refreshing = true noctalia.runAsync( - codexbar .. " usage --provider codex --source cli --format json", + -- OAuth reads the credential that Codex already stores locally and queries + -- the usage API directly. The CLI source instead starts an app-server RPC + -- for every refresh, which is intermittent on Linux. + codexbar .. " usage --provider codex --source oauth --format json", function(result) refreshing = false if result.exitCode ~= 0 then - showError(result.stderr ~= "" and result.stderr or "codexbar exited " .. tostring(result.exitCode)) + showRefreshFailure(result.stderr ~= "" and result.stderr or "codexbar exited " .. tostring(result.exitCode)) return end local data, error = noctalia.json.decode(result.stdout) if data == nil then - showError("invalid CodexBar JSON: " .. (error or "unknown error")) + showRefreshFailure("invalid CodexBar JSON: " .. (error or "unknown error")) return end local item = data[1] or data if item.error ~= nil then - showError(item.error.message or "CodexBar returned an error") + showRefreshFailure(item.error.message or "CodexBar returned an error") return end + lastSuccessfulUsage = item showUsage(item) end, 30000 diff --git a/modules/profiles/platform/desktop/nixos.nix b/modules/profiles/platform/desktop/nixos.nix index e2dd78a..856b129 100644 --- a/modules/profiles/platform/desktop/nixos.nix +++ b/modules/profiles/platform/desktop/nixos.nix @@ -1,4 +1,21 @@ { + # A physical desktop is always available for local and remote work. Disable + # every system sleep path; screen blanking while locked is handled by + # services.swayidle in the graphical session. + systemd.sleep.settings.Sleep = { + AllowSuspend = false; + AllowHibernation = false; + AllowHybridSleep = false; + AllowSuspendThenHibernate = false; + }; + + services.logind.settings.Login.IdleAction = "ignore"; + + # Keep the CPU policy at the highest-performance governor. Hardware may + # still manage turbo/thermal limits, but it will not select a power-saving + # governor. + powerManagement.cpuFreqGovernor = "performance"; + services.displayManager = { defaultSession = "labwc"; ly.settings.default_session = "labwc"; diff --git a/modules/services/swayidle/home.nix b/modules/services/swayidle/home.nix index 07d4d7f..1fdf333 100644 --- a/modules/services/swayidle/home.nix +++ b/modules/services/swayidle/home.nix @@ -1,6 +1,7 @@ { lib, pkgs, ... }: let brightnessctl = lib.getExe pkgs.brightnessctl; + loginctl = lib.getExe' pkgs.systemd "loginctl"; rm = "${pkgs.coreutils}/bin/rm"; systemctl = lib.getExe' pkgs.systemd "systemctl"; wlopm = lib.getExe pkgs.wlopm; @@ -40,6 +41,17 @@ let ${wlopm} --on '*' ${restoreBrightness} ''; + + turnOffLockedDisplays = pkgs.writeShellScript "swayidle-turn-off-locked-displays" '' + if [ -n "$XDG_SESSION_ID" ] \ + && [ "$(${loginctl} show-session "$XDG_SESSION_ID" --property=LockedHint --value)" = "yes" ]; then + ${wlopm} --off '*' + fi + ''; + + turnOnDisplays = pkgs.writeShellScript "swayidle-turn-on-displays" '' + ${wlopm} --on '*' + ''; in lib.mkIf pkgs.stdenv.hostPlatform.isLinux { services.swayidle = { @@ -55,6 +67,13 @@ lib.mkIf pkgs.stdenv.hostPlatform.isLinux { timeout = 360; command = "${suspendOnBattery}"; } + { + # Do not blank an unlocked desktop. Once the logind session is locked, + # blank its outputs after ten minutes and restore them on input. + timeout = 600; + command = "${turnOffLockedDisplays}"; + resumeCommand = "${turnOnDisplays}"; + } ]; events.after-resume = "${afterResume}"; }; @@ -63,5 +82,6 @@ lib.mkIf pkgs.stdenv.hostPlatform.isLinux { "WAYLAND_DISPLAY" "XDG_RUNTIME_DIR" "DBUS_SESSION_BUS_ADDRESS" + "XDG_SESSION_ID" ]; }