diff options
Diffstat (limited to 'accounts/gkleen@sif/niri/default.nix')
| -rw-r--r-- | accounts/gkleen@sif/niri/default.nix | 111 | 
1 files changed, 110 insertions, 1 deletions
| diff --git a/accounts/gkleen@sif/niri/default.nix b/accounts/gkleen@sif/niri/default.nix index bfc32254..ca2dfeff 100644 --- a/accounts/gkleen@sif/niri/default.nix +++ b/accounts/gkleen@sif/niri/default.nix | |||
| @@ -89,7 +89,8 @@ let | |||
| 89 | active_output="$(jq -r '.[] | select(.is_focused) | .output' <<<"$workspaces_json")" | 89 | active_output="$(jq -r '.[] | select(.is_focused) | .output' <<<"$workspaces_json")" | 
| 90 | active_workspace="$(jq -r '.[] | select(.is_focused) | .id' <<<"$workspaces_json")" | 90 | active_workspace="$(jq -r '.[] | select(.is_focused) | .id' <<<"$workspaces_json")" | 
| 91 | 91 | ||
| 92 | workspace_json="$(jq -c --arg active_output "$active_output" 'map(select(.output == $active_output and .name == null)) | sort_by(.idx) | .[0]' <<<"$workspaces_json")" | 92 | history_json="$(socat STDIO UNIX-CONNECT:"$XDG_RUNTIME_DIR"/niri-workspace-history.sock)" | 
| 93 | 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")" | ||
| 93 | [[ -n $workspace_json && $workspace_json != null ]] || exit 0 | 94 | [[ -n $workspace_json && $workspace_json != null ]] || exit 0 | 
| 94 | jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET" | 95 | jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET" | 
| 95 | ''; | 96 | ''; | 
| @@ -156,6 +157,113 @@ in { | |||
| 156 | ]; | 157 | ]; | 
| 157 | }; | 158 | }; | 
| 158 | 159 | ||
| 160 | systemd.user.sockets.niri-workspace-history = { | ||
| 161 | Socket = { | ||
| 162 | ListenStream = "%t/niri-workspace-history.sock"; | ||
| 163 | SocketMode = "0600"; | ||
| 164 | }; | ||
| 165 | }; | ||
| 166 | systemd.user.services.niri-workspace-history = { | ||
| 167 | Unit = { | ||
| 168 | BindsTo = [ "niri.service" ]; | ||
| 169 | After = [ "niri.service" ]; | ||
| 170 | }; | ||
| 171 | Install = { | ||
| 172 | WantedBy = [ "niri.service" ]; | ||
| 173 | }; | ||
| 174 | Service = { | ||
| 175 | Type = "simple"; | ||
| 176 | Sockets = [ "niri-workspace-history.socket" ]; | ||
| 177 | ExecStart = pkgs.writers.writePython3 "niri-workspace-history" {} '' | ||
| 178 | import os | ||
| 179 | import socket | ||
| 180 | import json | ||
| 181 | import sys | ||
| 182 | from collections import defaultdict | ||
| 183 | from threading import Thread, Lock | ||
| 184 | from socketserver import StreamRequestHandler, ThreadingTCPServer | ||
| 185 | from contextlib import contextmanager | ||
| 186 | from io import TextIOWrapper | ||
| 187 | |||
| 188 | |||
| 189 | @contextmanager | ||
| 190 | def detaching(thing): | ||
| 191 | try: | ||
| 192 | yield thing | ||
| 193 | finally: | ||
| 194 | thing.detach() | ||
| 195 | |||
| 196 | |||
| 197 | workspace_history = defaultdict(list) | ||
| 198 | history_lock = Lock() | ||
| 199 | |||
| 200 | |||
| 201 | def monitor_niri(): | ||
| 202 | workspaces = list() | ||
| 203 | |||
| 204 | def focus_workspace(output, workspace): | ||
| 205 | global workspace_history, history_lock | ||
| 206 | |||
| 207 | with history_lock: | ||
| 208 | workspace_history[output] = [workspace] + [ws for ws in workspace_history[output] if ws != workspace] # noqa: E501 | ||
| 209 | print(json.dumps(workspace_history), file=sys.stderr) | ||
| 210 | |||
| 211 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | ||
| 212 | sock.connect(os.environ["NIRI_SOCKET"]) | ||
| 213 | sock.send(b"\"EventStream\"\n") | ||
| 214 | for line in sock.makefile(buffering=1, encoding='utf-8'): | ||
| 215 | if line_json := json.loads(line): | ||
| 216 | if "WorkspacesChanged" in line_json: | ||
| 217 | workspaces = line_json["WorkspacesChanged"]["workspaces"] | ||
| 218 | for ws in workspaces: | ||
| 219 | if ws["is_focused"]: | ||
| 220 | focus_workspace(ws["output"], ws["id"]) | ||
| 221 | if "WorkspaceActivated" in line_json: | ||
| 222 | for ws in workspaces: | ||
| 223 | if ws["id"] != line_json["WorkspaceActivated"]["id"]: | ||
| 224 | continue | ||
| 225 | focus_workspace(ws["output"], ws["id"]) | ||
| 226 | break | ||
| 227 | |||
| 228 | |||
| 229 | class RequestHandler(StreamRequestHandler): | ||
| 230 | def handle(self): | ||
| 231 | global workspace_history, history_lock | ||
| 232 | |||
| 233 | with detaching(TextIOWrapper(self.wfile, encoding='utf-8', write_through=True)) as out: # noqa: E501 | ||
| 234 | with history_lock: | ||
| 235 | json.dump(workspace_history, out) | ||
| 236 | |||
| 237 | |||
| 238 | class Server(ThreadingTCPServer): | ||
| 239 | def __init__(self): | ||
| 240 | ThreadingTCPServer.__init__(self, ("", 8000), RequestHandler, bind_and_activate=False) # noqa: E501 | ||
| 241 | self.socket = socket.fromfd(3, self.address_family, self.socket_type) | ||
| 242 | |||
| 243 | |||
| 244 | def run_server(): | ||
| 245 | with Server() as server: | ||
| 246 | server.serve_forever() | ||
| 247 | |||
| 248 | |||
| 249 | niri = Thread(target=monitor_niri) | ||
| 250 | niri.daemon = True | ||
| 251 | niri.start() | ||
| 252 | |||
| 253 | server_thread = Thread(target=run_server) | ||
| 254 | server_thread.daemon = True | ||
| 255 | server_thread.start() | ||
| 256 | |||
| 257 | while True: | ||
| 258 | server_thread.join(timeout=0.5) | ||
| 259 | niri.join(timeout=0.5) | ||
| 260 | |||
| 261 | if not (niri.is_alive() and server_thread.is_alive()): | ||
| 262 | break | ||
| 263 | ''; | ||
| 264 | }; | ||
| 265 | }; | ||
| 266 | |||
| 159 | programs.niri.settings = { | 267 | programs.niri.settings = { | 
| 160 | prefer-no-csd = true; | 268 | prefer-no-csd = true; | 
| 161 | screenshot-path = "${config.home.homeDirectory}/screenshots"; | 269 | screenshot-path = "${config.home.homeDirectory}/screenshots"; | 
| @@ -209,6 +317,7 @@ in { | |||
| 209 | 317 | ||
| 210 | animations = { | 318 | animations = { | 
| 211 | slowdown = 0.5; | 319 | slowdown = 0.5; | 
| 320 | workspace-switch = null; | ||
| 212 | }; | 321 | }; | 
| 213 | 322 | ||
| 214 | layout = { | 323 | layout = { | 
