summaryrefslogtreecommitdiff
path: root/accounts/gkleen@sif/niri
diff options
context:
space:
mode:
Diffstat (limited to 'accounts/gkleen@sif/niri')
-rw-r--r--accounts/gkleen@sif/niri/default.nix133
-rw-r--r--accounts/gkleen@sif/niri/waybar.nix16
2 files changed, 135 insertions, 14 deletions
diff --git a/accounts/gkleen@sif/niri/default.nix b/accounts/gkleen@sif/niri/default.nix
index 4a207589..7e187c84 100644
--- a/accounts/gkleen@sif/niri/default.nix
+++ b/accounts/gkleen@sif/niri/default.nix
@@ -28,7 +28,11 @@ let
28 28
29 while IFS=$'\n' read -r window_json; do 29 while IFS=$'\n' read -r window_json; do
30 if [[ -n $(jq -c "$window_select" <<<"$window_json") ]]; then 30 if [[ -n $(jq -c "$window_select" <<<"$window_json") ]]; then
31 niri msg action focus-window --id "$(jq -r '.id' <<<"$window_json")" 31 if jq -e '.is_focused' <<<"$window_json" >/dev/null; then
32 niri msg action focus-workspace-previous
33 else
34 niri msg action focus-window --id "$(jq -r '.id' <<<"$window_json")"
35 fi
32 exit 0 36 exit 0
33 fi 37 fi
34 done < <(niri msg -j windows | jq -c '.[]') 38 done < <(niri msg -j windows | jq -c '.[]')
@@ -89,7 +93,8 @@ let
89 active_output="$(jq -r '.[] | select(.is_focused) | .output' <<<"$workspaces_json")" 93 active_output="$(jq -r '.[] | select(.is_focused) | .output' <<<"$workspaces_json")"
90 active_workspace="$(jq -r '.[] | select(.is_focused) | .id' <<<"$workspaces_json")" 94 active_workspace="$(jq -r '.[] | select(.is_focused) | .id' <<<"$workspaces_json")"
91 95
92 workspace_json="$(jq -c --arg active_output "$active_output" 'map(select(.output == $active_output and .name == null)) | sort_by(.idx) | .[0]' <<<"$workspaces_json")" 96 history_json="$(socat STDIO UNIX-CONNECT:"$XDG_RUNTIME_DIR"/niri-workspace-history.sock)"
97 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 98 [[ -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" 99 jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET"
95 ''; 100 '';
@@ -156,6 +161,113 @@ in {
156 ]; 161 ];
157 }; 162 };
158 163
164 systemd.user.sockets.niri-workspace-history = {
165 Socket = {
166 ListenStream = "%t/niri-workspace-history.sock";
167 SocketMode = "0600";
168 };
169 };
170 systemd.user.services.niri-workspace-history = {
171 Unit = {
172 BindsTo = [ "niri.service" ];
173 After = [ "niri.service" ];
174 };
175 Install = {
176 WantedBy = [ "niri.service" ];
177 };
178 Service = {
179 Type = "simple";
180 Sockets = [ "niri-workspace-history.socket" ];
181 ExecStart = pkgs.writers.writePython3 "niri-workspace-history" {} ''
182 import os
183 import socket
184 import json
185 import sys
186 from collections import defaultdict
187 from threading import Thread, Lock
188 from socketserver import StreamRequestHandler, ThreadingTCPServer
189 from contextlib import contextmanager
190 from io import TextIOWrapper
191
192
193 @contextmanager
194 def detaching(thing):
195 try:
196 yield thing
197 finally:
198 thing.detach()
199
200
201 workspace_history = defaultdict(list)
202 history_lock = Lock()
203
204
205 def monitor_niri():
206 workspaces = list()
207
208 def focus_workspace(output, workspace):
209 global workspace_history, history_lock
210
211 with history_lock:
212 workspace_history[output] = [workspace] + [ws for ws in workspace_history[output] if ws != workspace] # noqa: E501
213 print(json.dumps(workspace_history), file=sys.stderr)
214
215 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
216 sock.connect(os.environ["NIRI_SOCKET"])
217 sock.send(b"\"EventStream\"\n")
218 for line in sock.makefile(buffering=1, encoding='utf-8'):
219 if line_json := json.loads(line):
220 if "WorkspacesChanged" in line_json:
221 workspaces = line_json["WorkspacesChanged"]["workspaces"]
222 for ws in workspaces:
223 if ws["is_focused"]:
224 focus_workspace(ws["output"], ws["id"])
225 if "WorkspaceActivated" in line_json:
226 for ws in workspaces:
227 if ws["id"] != line_json["WorkspaceActivated"]["id"]:
228 continue
229 focus_workspace(ws["output"], ws["id"])
230 break
231
232
233 class RequestHandler(StreamRequestHandler):
234 def handle(self):
235 global workspace_history, history_lock
236
237 with detaching(TextIOWrapper(self.wfile, encoding='utf-8', write_through=True)) as out: # noqa: E501
238 with history_lock:
239 json.dump(workspace_history, out)
240
241
242 class Server(ThreadingTCPServer):
243 def __init__(self):
244 ThreadingTCPServer.__init__(self, ("", 8000), RequestHandler, bind_and_activate=False) # noqa: E501
245 self.socket = socket.fromfd(3, self.address_family, self.socket_type)
246
247
248 def run_server():
249 with Server() as server:
250 server.serve_forever()
251
252
253 niri = Thread(target=monitor_niri)
254 niri.daemon = True
255 niri.start()
256
257 server_thread = Thread(target=run_server)
258 server_thread.daemon = True
259 server_thread.start()
260
261 while True:
262 server_thread.join(timeout=0.5)
263 niri.join(timeout=0.5)
264
265 if not (niri.is_alive() and server_thread.is_alive()):
266 break
267 '';
268 };
269 };
270
159 programs.niri.settings = { 271 programs.niri.settings = {
160 prefer-no-csd = true; 272 prefer-no-csd = true;
161 screenshot-path = "${config.home.homeDirectory}/screenshots"; 273 screenshot-path = "${config.home.homeDirectory}/screenshots";
@@ -209,6 +321,7 @@ in {
209 321
210 animations = { 322 animations = {
211 slowdown = 0.5; 323 slowdown = 0.5;
324 workspace-switch = null;
212 }; 325 };
213 326
214 layout = { 327 layout = {
@@ -274,13 +387,14 @@ in {
274 }; 387 };
275 388
276 window-rules = [ 389 window-rules = [
277 # { 390 {
278 # geometry-corner-radius = 391 matches = [ { is-floating = true; } ];
279 # let 392 geometry-corner-radius =
280 # allCorners = r: { bottom-left = r; bottom-right = r; top-left = r; top-right = r; }; 393 let
281 # in allCorners 4.; 394 allCorners = r: { bottom-left = r; bottom-right = r; top-left = r; top-right = r; };
282 # clip-to-geometry = true; 395 in allCorners 8.;
283 # } 396 clip-to-geometry = true;
397 }
284 { 398 {
285 matches = [ { app-id = "^com\.saivert\.pwvucontrol$"; } ]; 399 matches = [ { app-id = "^com\.saivert\.pwvucontrol$"; } ];
286 open-on-workspace = "pwctl"; 400 open-on-workspace = "pwctl";
@@ -397,6 +511,7 @@ in {
397 matches = [ 511 matches = [
398 { app-id = "^Gimp-"; title = "^Quit GIMP$"; } 512 { app-id = "^Gimp-"; title = "^Quit GIMP$"; }
399 { app-id = "^org\.kde\.polkit-kde-authentication-agent-1$"; } 513 { app-id = "^org\.kde\.polkit-kde-authentication-agent-1$"; }
514 { app-id = "^xdg-desktop-portal-gtk$"; }
400 ]; 515 ];
401 open-floating = true; 516 open-floating = true;
402 } 517 }
diff --git a/accounts/gkleen@sif/niri/waybar.nix b/accounts/gkleen@sif/niri/waybar.nix
index c3820508..3f1f8119 100644
--- a/accounts/gkleen@sif/niri/waybar.nix
+++ b/accounts/gkleen@sif/niri/waybar.nix
@@ -61,7 +61,7 @@ in {
61 text = f"<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>" # noqa: E501 61 text = f"<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>" # noqa: E501
62 if is_silent: 62 if is_silent:
63 text = f"<span color=\"#ffffff\">{text}</span>" 63 text = f"<span color=\"#ffffff\">{text}</span>"
64 print(json.dumps({'text': text}, separators=(',', ':')), flush=True) # noqa: E501 64 print(json.dumps({'text': text, 'tooltip': ', '.join(modes)}, separators=(',', ':')), flush=True) # noqa: E501
65 65
66 async def on_properties_changed(interface_name, changed_properties, invalidated_properties): # noqa: E501 66 async def on_properties_changed(interface_name, changed_properties, invalidated_properties): # noqa: E501
67 if "Modes" not in invalidated_properties: 67 if "Modes" not in invalidated_properties:
@@ -122,13 +122,13 @@ in {
122 }; 122 };
123 "custom/worktime" = { 123 "custom/worktime" = {
124 interval = 60; 124 interval = 60;
125 exec = lib.getExe pkgs.worktime; 125 exec = "${lib.getExe pkgs.worktime} time --waybar";
126 tooltip = false; 126 return-type = "json";
127 }; 127 };
128 "custom/worktime-today" = { 128 "custom/worktime-today" = {
129 interval = 60; 129 interval = 60;
130 exec = "${lib.getExe pkgs.worktime} today"; 130 exec = "${lib.getExe pkgs.worktime} today --waybar";
131 tooltip = false; 131 return-type = "json";
132 }; 132 };
133 "niri/workspaces" = { 133 "niri/workspaces" = {
134 ignore = ["eff" "pwctl" "kpxc" "bmgr" "edit" "term"]; 134 ignore = ["eff" "pwctl" "kpxc" "bmgr" "edit" "term"];
@@ -323,6 +323,12 @@ in {
323 #idle_inhibitor.activated { 323 #idle_inhibitor.activated {
324 color: @white; 324 color: @white;
325 } 325 }
326 #custom-worktime.running, #custom-worktime-today.running {
327 color: @white;
328 }
329 #custom-worktime.over, #custom-worktime-today.over {
330 color: @orange;
331 }
326 332
327 #idle_inhibitor { 333 #idle_inhibitor {
328 padding-top: 1px; 334 padding-top: 1px;