alpha
Login
or
Join now
hermes.pds.lassul.us
/
superconfig
forked from
lass.pds.lassul.us/superconfig
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
This repository has no description
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
Overview
Issues
Pulls
Pipelines
sway: migrate from waybar to eww
author
lassulus
date
10 months ago
(Dec 6, 2025, 8:32 PM CET)
commit
88e3e86f
88e3e86fbff39dee4568135678441596461f9f0c
parent
7514ae26
7514ae2604738e12ac544d3c899af461a4ae66d5
+463
-131
1 changed file
Expand all
Collapse all
Unified
Split
2configs
desktops
sway
default.nix
+463
-131
2configs/desktops/sway/default.nix
View file
Reviewed
···
8
8
9
9
term = "/run/current-system/sw/bin/alacritty";
10
10
11
11
-
waybarConfig = pkgs.writeText "waybar-config.json" (
12
12
-
builtins.toJSON {
13
13
-
height = 30;
14
14
-
spacing = 4;
15
15
-
modules-left = [
16
16
-
"sway/workspaces"
17
17
-
"sway/mode"
18
18
-
"sway/scratchpad"
19
19
-
];
20
20
-
modules-center = [ "sway/window" ];
21
21
-
modules-right = [
22
22
-
"idle_inhibitor"
23
23
-
"pulseaudio"
24
24
-
"network"
25
25
-
"cpu"
26
26
-
"memory"
27
27
-
"temperature"
28
28
-
"backlight"
29
29
-
"battery"
30
30
-
"clock"
31
31
-
"tray"
32
32
-
];
11
11
+
# eww configuration
12
12
+
ewwYuck = pkgs.writeText "eww.yuck" ''
13
13
+
; Variables with polling - set initial values to avoid empty string errors
14
14
+
(defpoll time :interval "1s" :initial "00:00" "date '+%H:%M'")
15
15
+
(defpoll date :interval "60s" :initial "0000-00-00" "date '+%Y-%m-%d'")
16
16
+
(defpoll cpu :interval "2s" :initial 0 "${lib.getExe cpuScript}")
17
17
+
(defpoll memory :interval "2s" :initial 0 "${lib.getExe memScript}")
18
18
+
(defpoll temperature :interval "2s" :initial 0 "${lib.getExe tempScript}")
19
19
+
(defpoll battery :interval "10s" :initial '{"capacity": "100", "icon": "", "watts": "0", "time_remaining": "--", "status": "Full"}' "${lib.getExe batteryScript}")
20
20
+
(defpoll volume :interval "1s" :initial '{"level": "0", "icon": ""}' "${lib.getExe volumeScript}")
21
21
+
(defpoll network :interval "5s" :initial " ..." "${lib.getExe networkScript}")
22
22
+
(deflisten workspaces :initial '[]' "${lib.getExe workspacesScript}")
33
23
34
34
-
"sway/mode".format = "<span style=\"italic\">{}</span>";
24
24
+
; Widget definitions
25
25
+
(defwidget bar []
26
26
+
(centerbox :orientation "h"
27
27
+
(left)
28
28
+
(center)
29
29
+
(right)))
35
30
36
36
-
"sway/scratchpad" = {
37
37
-
format = "{icon} {count}";
38
38
-
show-empty = false;
39
39
-
format-icons = [
40
40
-
""
41
41
-
""
42
42
-
];
43
43
-
tooltip = true;
44
44
-
tooltip-format = "{app}: {title}";
45
45
-
};
31
31
+
(defwidget left []
32
32
+
(box :class "left" :orientation "h" :space-evenly false :halign "start"
33
33
+
(workspaces-widget)))
46
34
47
47
-
idle_inhibitor = {
48
48
-
format = "{icon}";
49
49
-
format-icons = {
50
50
-
activated = "";
51
51
-
deactivated = "";
52
52
-
};
53
53
-
};
35
35
+
(defwidget center []
36
36
+
(box :class "center" :orientation "h" :space-evenly false :halign "center"
37
37
+
(label :text "''${time}")))
54
38
55
55
-
tray.spacing = 10;
39
39
+
(defwidget right []
40
40
+
(box :class "right" :orientation "h" :space-evenly false :halign "end" :spacing 8
41
41
+
(network-widget)
42
42
+
(volume-widget)
43
43
+
(graph-widget :value cpu :icon "" :class "cpu" :unit "%")
44
44
+
(graph-widget :value memory :icon "" :class "memory" :unit "%")
45
45
+
(graph-widget :value temperature :icon "" :class "temp" :unit "°")
46
46
+
(battery-widget)
47
47
+
(label :class "date" :text "''${date}")
48
48
+
(systray :class "systray" :icon-size 18 :spacing 4)))
56
49
57
57
-
clock = {
58
58
-
tooltip-format = "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>";
59
59
-
format-alt = "{:%Y-%m-%d}";
60
60
-
};
50
50
+
(defwidget workspaces-widget []
51
51
+
(box :class "workspaces" :orientation "h" :space-evenly false :spacing 0
52
52
+
(for ws in workspaces
53
53
+
(button :class {ws.urgent ? "workspace urgent" : (ws.focused ? "workspace focused" : "workspace")}
54
54
+
:onclick "swaymsg workspace ''${ws.name}"
55
55
+
{ws.name}))))
61
56
62
62
-
cpu = {
63
63
-
format = "{usage}% ";
64
64
-
tooltip = false;
65
65
-
};
57
57
+
(defwidget graph-widget [value icon class unit]
58
58
+
(box :class "graph-module ''${class}" :orientation "h" :space-evenly false :spacing 4
59
59
+
(graph :class "graph"
60
60
+
:value value
61
61
+
:thickness 3
62
62
+
:time-range "60s"
63
63
+
:min 0
64
64
+
:max 100
65
65
+
:dynamic true
66
66
+
:line-style "round"
67
67
+
:width 75)
68
68
+
(label :text "''${value}''${unit} ''${icon}")))
66
69
67
67
-
memory.format = "{}% ";
70
70
+
(defwidget battery-widget []
71
71
+
(box :class "battery" :orientation "h" :space-evenly false
72
72
+
:tooltip "''${battery.watts}W | ''${battery.time_remaining} ''${battery.status == 'Charging' ? 'to full' : 'remaining'}"
73
73
+
(label :text "''${battery.capacity}% ''${battery.icon}")))
68
74
69
69
-
temperature = {
70
70
-
critical-threshold = 80;
71
71
-
format = "{temperatureC}°C {icon}";
72
72
-
format-icons = [
73
73
-
""
74
74
-
""
75
75
-
""
76
76
-
];
77
77
-
};
75
75
+
(defwidget volume-widget []
76
76
+
(box :class "volume" :orientation "h" :space-evenly false
77
77
+
(label :text "''${volume.level}% ''${volume.icon}")))
78
78
79
79
-
backlight = {
80
80
-
format = "{percent}% {icon}";
81
81
-
format-icons = [
82
82
-
""
83
83
-
""
84
84
-
""
85
85
-
];
86
86
-
};
79
79
+
(defwidget network-widget []
80
80
+
(box :class "network" :orientation "h" :space-evenly false
81
81
+
(label :text "''${network}")))
87
82
88
88
-
battery = {
89
89
-
states = {
90
90
-
warning = 30;
91
91
-
critical = 15;
92
92
-
};
93
93
-
format = "{capacity}% {icon}";
94
94
-
format-full = "{capacity}% {icon}";
95
95
-
format-charging = "{capacity}% ";
96
96
-
format-plugged = "{capacity}% ";
97
97
-
format-alt = "{time} {icon}";
98
98
-
format-icons = [
99
99
-
""
100
100
-
""
101
101
-
""
102
102
-
""
103
103
-
""
104
104
-
""
105
105
-
""
106
106
-
""
107
107
-
""
108
108
-
""
109
109
-
""
110
110
-
];
111
111
-
};
83
83
+
; Window definition - screen name passed as argument
84
84
+
(defwindow bar [screen]
85
85
+
:monitor screen
86
86
+
:geometry (geometry :x "0%"
87
87
+
:y "0%"
88
88
+
:width "100%"
89
89
+
:height "36px"
90
90
+
:anchor "top center")
91
91
+
:stacking "fg"
92
92
+
:exclusive true
93
93
+
:focusable false
94
94
+
(bar))
95
95
+
'';
112
96
113
113
-
network = {
114
114
-
format-wifi = "{essid} ({signalStrength}%) ";
115
115
-
format-ethernet = "{ipaddr}/{cidr} ";
116
116
-
tooltip-format = "{ifname} via {gwaddr} ";
117
117
-
format-linked = "{ifname} (No IP) ";
118
118
-
format-disconnected = "Disconnected ";
119
119
-
format-alt = "{ifname}: {ipaddr}/{cidr}";
120
120
-
};
121
121
-
122
122
-
pulseaudio = {
123
123
-
format = "{volume}% {icon} {format_source}";
124
124
-
format-bluetooth = "{volume}% {icon} {format_source}";
125
125
-
format-bluetooth-muted = " {icon} {format_source}";
126
126
-
format-muted = " {format_source}";
127
127
-
format-source = "{volume}% ";
128
128
-
format-source-muted = "";
129
129
-
format-icons = {
130
130
-
headphone = "";
131
131
-
hands-free = "";
132
132
-
headset = "";
133
133
-
phone = "";
134
134
-
portable = "";
135
135
-
car = "";
136
136
-
default = [
137
137
-
""
138
138
-
""
139
139
-
""
140
140
-
];
141
141
-
};
142
142
-
on-click = "pavucontrol";
143
143
-
};
144
144
-
}
145
145
-
);
146
146
-
147
147
-
waybarStyle = pkgs.writeText "waybar-style.css" ''
148
148
-
@import "${pkgs.waybar}/etc/xdg/waybar/style.css";
97
97
+
ewwScss = pkgs.writeText "eww.scss" ''
149
98
* {
99
99
+
all: unset;
150
100
font-family: "Iosevka Nerd Font", monospace;
101
101
+
font-size: 14px;
151
102
}
103
103
+
104
104
+
window {
105
105
+
background-color: #1a1b26;
106
106
+
color: #c0caf5;
107
107
+
}
108
108
+
109
109
+
.left, .center, .right {
110
110
+
margin: 4px 8px;
111
111
+
}
112
112
+
113
113
+
.workspaces {
114
114
+
button {
115
115
+
padding: 0 4px;
116
116
+
margin: 0 1px;
117
117
+
border-radius: 2px;
118
118
+
min-width: 24px;
119
119
+
background-color: #24283b;
120
120
+
&.focused {
121
121
+
background-color: #7aa2f7;
122
122
+
color: #1a1b26;
123
123
+
}
124
124
+
&.urgent {
125
125
+
background-color: #f7768e;
126
126
+
color: #1a1b26;
127
127
+
}
128
128
+
&:hover {
129
129
+
background-color: #414868;
130
130
+
}
131
131
+
}
132
132
+
}
133
133
+
134
134
+
.graph-module {
135
135
+
padding: 0 8px;
136
136
+
margin: 0 2px;
137
137
+
border-radius: 4px;
138
138
+
&.cpu {
139
139
+
background-color: #9ece6a;
140
140
+
color: #1a1b26;
141
141
+
}
142
142
+
&.memory {
143
143
+
background-color: #7dcfff;
144
144
+
color: #1a1b26;
145
145
+
}
146
146
+
&.temp {
147
147
+
background-color: #ff9e64;
148
148
+
color: #1a1b26;
149
149
+
}
150
150
+
}
151
151
+
152
152
+
.graph {
153
153
+
min-height: 20px;
154
154
+
}
155
155
+
156
156
+
.battery, .volume, .network, .date {
157
157
+
padding: 0 8px;
158
158
+
margin: 0 2px;
159
159
+
border-radius: 4px;
160
160
+
background-color: #24283b;
161
161
+
}
162
162
+
163
163
+
.systray {
164
164
+
padding: 0 8px;
165
165
+
margin: 0 2px;
166
166
+
}
167
167
+
168
168
+
tooltip {
169
169
+
background-color: #1a1b26;
170
170
+
color: #c0caf5;
171
171
+
border: 1px solid #414868;
172
172
+
border-radius: 4px;
173
173
+
padding: 4px 8px;
174
174
+
}
175
175
+
'';
176
176
+
177
177
+
cpuScript = pkgs.writeShellApplication {
178
178
+
name = "eww-cpu";
179
179
+
text = ''
180
180
+
STATE_FILE="/tmp/eww-cpu-state"
181
181
+
read -r _cpu user nice system idle _rest < /proc/stat
182
182
+
total=$((user + nice + system + idle))
183
183
+
184
184
+
if [ -f "$STATE_FILE" ]; then
185
185
+
read -r prev_total prev_idle < "$STATE_FILE"
186
186
+
diff_total=$((total - prev_total))
187
187
+
diff_idle=$((idle - prev_idle))
188
188
+
if [ "$diff_total" -gt 0 ]; then
189
189
+
echo $((100 * (diff_total - diff_idle) / diff_total))
190
190
+
else
191
191
+
echo 0
192
192
+
fi
193
193
+
else
194
194
+
echo 0
195
195
+
fi
196
196
+
echo "$total $idle" > "$STATE_FILE"
197
197
+
'';
198
198
+
};
199
199
+
200
200
+
memScript = pkgs.writeShellApplication {
201
201
+
name = "eww-mem";
202
202
+
runtimeInputs = [ pkgs.gawk ];
203
203
+
text = ''
204
204
+
awk '/MemTotal/ {total=$2} /MemAvailable/ {available=$2} END {printf "%d", (total-available)*100/total}' /proc/meminfo
205
205
+
'';
206
206
+
};
207
207
+
208
208
+
tempScript = pkgs.writeShellApplication {
209
209
+
name = "eww-temp";
210
210
+
text = ''
211
211
+
temp_raw=$(cat /sys/class/thermal/thermal_zone0/temp)
212
212
+
echo $((temp_raw / 1000))
213
213
+
'';
214
214
+
};
215
215
+
216
216
+
batteryScript = pkgs.writeShellApplication {
217
217
+
name = "eww-battery";
218
218
+
runtimeInputs = [
219
219
+
pkgs.jq
220
220
+
pkgs.bc
221
221
+
];
222
222
+
text = ''
223
223
+
# Find battery device dynamically
224
224
+
bat_path=""
225
225
+
for p in /sys/class/power_supply/BAT* /sys/class/power_supply/BATT*; do
226
226
+
if [ -f "$p/capacity" ]; then
227
227
+
bat_path="$p"
228
228
+
break
229
229
+
fi
230
230
+
done
231
231
+
232
232
+
if [ -z "$bat_path" ]; then
233
233
+
echo '{"capacity": "N/A", "icon": "", "watts": "0", "time_remaining": "N/A"}'
234
234
+
exit 0
235
235
+
fi
236
236
+
237
237
+
capacity=$(cat "$bat_path/capacity" 2>/dev/null || echo 100)
238
238
+
status=$(cat "$bat_path/status" 2>/dev/null || echo "Full")
239
239
+
power_now=$(cat "$bat_path/power_now" 2>/dev/null || echo 0)
240
240
+
energy_now=$(cat "$bat_path/energy_now" 2>/dev/null || echo 0)
241
241
+
energy_full=$(cat "$bat_path/energy_full" 2>/dev/null || echo 0)
242
242
+
243
243
+
# Convert microwatts to watts
244
244
+
watts=$(echo "scale=1; $power_now / 1000000" | bc)
245
245
+
246
246
+
# Calculate time remaining
247
247
+
if [ "$power_now" -gt 0 ]; then
248
248
+
if [ "$status" = "Charging" ]; then
249
249
+
# Time to full
250
250
+
remaining_energy=$((energy_full - energy_now))
251
251
+
hours=$(echo "scale=2; $remaining_energy / $power_now" | bc)
252
252
+
else
253
253
+
# Time to empty
254
254
+
hours=$(echo "scale=2; $energy_now / $power_now" | bc)
255
255
+
fi
256
256
+
# Convert to hours:minutes
257
257
+
h=$(echo "$hours" | cut -d. -f1)
258
258
+
m=$(echo "scale=0; ($hours - $h) * 60 / 1" | bc)
259
259
+
time_remaining="''${h}h ''${m}m"
260
260
+
else
261
261
+
time_remaining="--"
262
262
+
fi
263
263
+
264
264
+
if [ "$status" = "Charging" ]; then
265
265
+
icon=""
266
266
+
elif [ "$status" = "Full" ]; then
267
267
+
icon=""
268
268
+
elif [ "$capacity" -gt 80 ]; then
269
269
+
icon=""
270
270
+
elif [ "$capacity" -gt 60 ]; then
271
271
+
icon=""
272
272
+
elif [ "$capacity" -gt 40 ]; then
273
273
+
icon=""
274
274
+
elif [ "$capacity" -gt 20 ]; then
275
275
+
icon=""
276
276
+
else
277
277
+
icon=""
278
278
+
fi
279
279
+
jq -n --arg cap "$capacity" --arg icon "$icon" --arg watts "$watts" --arg time "$time_remaining" --arg status "$status" \
280
280
+
'{capacity: $cap, icon: $icon, watts: $watts, time_remaining: $time, status: $status}'
281
281
+
'';
282
282
+
};
283
283
+
284
284
+
volumeScript = pkgs.writeShellApplication {
285
285
+
name = "eww-volume";
286
286
+
runtimeInputs = [
287
287
+
pkgs.pulseaudio
288
288
+
pkgs.jq
289
289
+
pkgs.gnugrep
290
290
+
];
291
291
+
text = ''
292
292
+
vol=$(pactl get-sink-volume @DEFAULT_SINK@ | grep -oP '\d+%' | head -1 | tr -d '%')
293
293
+
muted=$(pactl get-sink-mute @DEFAULT_SINK@ | grep -oP '(yes|no)')
294
294
+
if [ "$muted" = "yes" ]; then
295
295
+
icon=""
296
296
+
elif [ "$vol" -gt 50 ]; then
297
297
+
icon=""
298
298
+
elif [ "$vol" -gt 0 ]; then
299
299
+
icon=""
300
300
+
else
301
301
+
icon=""
302
302
+
fi
303
303
+
jq -n --arg level "$vol" --arg icon "$icon" '{level: $level, icon: $icon}'
304
304
+
'';
305
305
+
};
306
306
+
307
307
+
networkScript = pkgs.writeShellApplication {
308
308
+
name = "eww-network";
309
309
+
runtimeInputs = [ pkgs.networkmanager ];
310
310
+
text = ''
311
311
+
connection=$(nmcli -t -f NAME,TYPE,DEVICE connection show --active 2>/dev/null | head -1)
312
312
+
if [ -z "$connection" ]; then
313
313
+
echo " Disconnected"
314
314
+
else
315
315
+
name=$(echo "$connection" | cut -d: -f1)
316
316
+
type=$(echo "$connection" | cut -d: -f2)
317
317
+
if [ "$type" = "802-11-wireless" ]; then
318
318
+
echo " $name"
319
319
+
else
320
320
+
echo " $name"
321
321
+
fi
322
322
+
fi
323
323
+
'';
324
324
+
};
325
325
+
326
326
+
workspacesScript = pkgs.writeShellApplication {
327
327
+
name = "sway-workspaces";
328
328
+
runtimeInputs = [
329
329
+
pkgs.sway
330
330
+
pkgs.jq
331
331
+
];
332
332
+
text = ''
333
333
+
# Output initial state
334
334
+
swaymsg -t get_workspaces | jq -c '[.[] | {name: .name, focused: .focused, urgent: .urgent}]'
335
335
+
336
336
+
# Subscribe to workspace events and output updated state
337
337
+
swaymsg -t subscribe -m '["workspace"]' | while read -r _; do
338
338
+
swaymsg -t get_workspaces | jq -c '[.[] | {name: .name, focused: .focused, urgent: .urgent}]'
339
339
+
done
340
340
+
'';
341
341
+
};
342
342
+
343
343
+
ewwConfigDir = pkgs.runCommand "eww-config" { } ''
344
344
+
mkdir -p $out
345
345
+
cp ${ewwYuck} $out/eww.yuck
346
346
+
cp ${ewwScss} $out/eww.scss
152
347
'';
153
348
154
349
in
···
164
359
'';
165
360
};
166
361
167
167
-
systemd.user.services.waybar = {
168
168
-
description = "Highly customizable Wayland bar for Sway";
169
169
-
documentation = [ "man:waybar(5)" ];
362
362
+
# Enable realtime scheduling for sway
363
363
+
security.pam.loginLimits = [
364
364
+
{
365
365
+
domain = "@users";
366
366
+
item = "rtprio";
367
367
+
type = "-";
368
368
+
value = 1;
369
369
+
}
370
370
+
];
371
371
+
372
372
+
# eww daemon
373
373
+
systemd.user.services.eww = {
374
374
+
description = "ElKowars Wacky Widgets daemon";
170
375
partOf = [ "sway-session.target" ];
171
376
wantedBy = [ "sway-session.target" ];
377
377
+
path = [
378
378
+
pkgs.bash
379
379
+
pkgs.sway
380
380
+
];
172
381
serviceConfig = {
173
382
Type = "simple";
174
174
-
ExecStart = "${pkgs.waybar}/bin/waybar -c ${waybarConfig} -s ${waybarStyle}";
383
383
+
ExecStart = "${pkgs.eww}/bin/eww daemon --config ${ewwConfigDir} --no-daemonize";
384
384
+
ExecStop = "${pkgs.eww}/bin/eww --config ${ewwConfigDir} kill";
385
385
+
Restart = "on-failure";
386
386
+
RestartSec = 1;
387
387
+
};
388
388
+
};
389
389
+
390
390
+
# eww bar per output - template unit using output name (e.g. eww-bar@eDP-1.service)
391
391
+
systemd.user.services."eww-bar@" = {
392
392
+
description = "Eww bar on output %i";
393
393
+
partOf = [ "sway-session.target" ];
394
394
+
after = [ "eww.service" ];
395
395
+
requires = [ "eww.service" ];
396
396
+
serviceConfig = {
397
397
+
Type = "oneshot";
398
398
+
RemainAfterExit = true;
399
399
+
ExecStart = "${pkgs.eww}/bin/eww --config ${ewwConfigDir} open bar --id bar-%i --arg screen=%i";
400
400
+
ExecStop = "${pkgs.eww}/bin/eww --config ${ewwConfigDir} close bar-%i";
401
401
+
};
402
402
+
};
403
403
+
404
404
+
# Watches for output changes and starts/stops eww-bar@ instances
405
405
+
systemd.user.services.eww-output-watcher = {
406
406
+
description = "Watch for output changes and manage eww bars";
407
407
+
partOf = [ "sway-session.target" ];
408
408
+
wantedBy = [ "sway-session.target" ];
409
409
+
after = [ "eww.service" ];
410
410
+
requires = [ "eww.service" ];
411
411
+
serviceConfig = {
412
412
+
Type = "simple";
413
413
+
ExecStart = lib.getExe (
414
414
+
pkgs.writeShellApplication {
415
415
+
name = "eww-output-watcher";
416
416
+
runtimeInputs = [
417
417
+
pkgs.sway
418
418
+
pkgs.jq
419
419
+
pkgs.systemd
420
420
+
pkgs.gawk
421
421
+
pkgs.gnused
422
422
+
pkgs.gnugrep
423
423
+
];
424
424
+
text = ''
425
425
+
update_bars() {
426
426
+
# Get current outputs
427
427
+
outputs=$(swaymsg -t get_outputs | jq -r '.[].name')
428
428
+
429
429
+
# Start bars for connected outputs
430
430
+
for output in $outputs; do
431
431
+
systemctl --user start "eww-bar@$output.service" || true
432
432
+
done
433
433
+
434
434
+
# Stop bars for disconnected outputs
435
435
+
running=$(systemctl --user list-units --type=service --state=running --plain --no-legend 'eww-bar@*' | awk '{print $1}' | sed 's/eww-bar@\(.*\)\.service/\1/')
436
436
+
for output in $running; do
437
437
+
if ! echo "$outputs" | grep -qx "$output"; then
438
438
+
systemctl --user stop "eww-bar@$output.service" || true
439
439
+
fi
440
440
+
done
441
441
+
}
442
442
+
443
443
+
update_bars
444
444
+
swaymsg -t subscribe -m '["output"]' | while read -r _; do
445
445
+
sleep 0.5
446
446
+
update_bars
447
447
+
done
448
448
+
'';
449
449
+
}
450
450
+
);
175
451
Restart = "on-failure";
176
452
RestartSec = 1;
177
453
};
···
401
677
402
678
bindsym $mod+Tab focus next
403
679
bindsym $mod+Escape workspace back_and_forth
680
680
+
681
681
+
# Focus primary output
682
682
+
bindsym $mod+F1 exec ${
683
683
+
lib.getExe (
684
684
+
pkgs.writeShellApplication {
685
685
+
name = "focus-primary-output";
686
686
+
runtimeInputs = [
687
687
+
pkgs.sway
688
688
+
pkgs.jq
689
689
+
];
690
690
+
text = ''
691
691
+
PRIMARY=$(swaymsg -r -t get_outputs | jq -r 'sort_by(.x, .y) | .[0].name')
692
692
+
swaymsg focus output "$PRIMARY"
693
693
+
'';
694
694
+
}
695
695
+
)
696
696
+
}
697
697
+
698
698
+
# Cycle through secondary outputs
699
699
+
bindsym $mod+F2 exec ${
700
700
+
lib.getExe (
701
701
+
pkgs.writeShellApplication {
702
702
+
name = "cycle-secondary-outputs";
703
703
+
runtimeInputs = [
704
704
+
pkgs.sway
705
705
+
pkgs.jq
706
706
+
pkgs.gnugrep
707
707
+
pkgs.coreutils
708
708
+
];
709
709
+
text = ''
710
710
+
OUTPUTS=$(swaymsg -r -t get_outputs | jq -r 'sort_by(.x, .y)')
711
711
+
PRIMARY=$(echo "$OUTPUTS" | jq -r '.[0].name')
712
712
+
CURRENT=$(echo "$OUTPUTS" | jq -r '.[] | select(.focused == true).name')
713
713
+
SECONDARIES=$(echo "$OUTPUTS" | jq -r '.[1:] | .[].name')
714
714
+
715
715
+
if [ -z "$SECONDARIES" ]; then
716
716
+
exit 0
717
717
+
fi
718
718
+
719
719
+
if [ "$CURRENT" = "$PRIMARY" ]; then
720
720
+
# Focus first secondary
721
721
+
NEXT=$(echo "$SECONDARIES" | head -n1)
722
722
+
else
723
723
+
# Find next secondary in the list
724
724
+
NEXT=$(echo "$SECONDARIES" | grep -A1 "^$CURRENT$" | tail -n1)
725
725
+
# If we're at the end, wrap to first secondary
726
726
+
if [ "$NEXT" = "$CURRENT" ]; then
727
727
+
NEXT=$(echo "$SECONDARIES" | head -n1)
728
728
+
fi
729
729
+
fi
730
730
+
731
731
+
swaymsg focus output "$NEXT"
732
732
+
'';
733
733
+
}
734
734
+
)
735
735
+
}
404
736
405
737
# screenlock
406
738
bindsym $mod+F11 exec ${lib.getExe' pkgs.systemd "systemctl"} --user start lock.target