1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
|
-- Hyprland configuration, in the style of Protesilaos Stavrou's Sway config.
-- Ref: https://github.com/protesilaos/dotfiles/tree/master/wayland-tc/.config/sway
--
-- Prot runs Sway (i3-style, manual tiling); this is Hyprland (dwindle,
-- dynamic tiling). Bindings and colours are carried over faithfully; the
-- places where Hyprland has no equivalent are marked "SUBSTITUTION" below.
---------------------
---- MY PROGRAMS ----
---------------------
-- Prot uses gnome-terminal. SUBSTITUTION: ghostty, which is already
-- installed here (and you said no kitty).
local terminal = "ghostty"
local fileManager = "dolphin"
-- Prot's exact wofi invocation, minus the `| xargs swaymsg exec --` pipe,
-- which exists only to hand the command back to Sway. Hyprland execs directly.
local menu = "wofi -G -I -i -n --show drun"
-- Prot points at ~/Pictures/light.jpg, which does not exist here.
-- SUBSTITUTION: one of your own wallpapers. Change this one string to swap it.
local wallpaper = os.getenv("HOME") .. "/Pictures/wallhaven-8gdrq2_2560x1440.png"
local font = "FiraCode Nerd Font"
------------------
---- MONITORS ----
------------------
hl.monitor({
output = "",
mode = "preferred",
position = "auto",
scale = "auto",
})
-------------------
---- AUTOSTART ----
-------------------
hl.on("hyprland.start", function()
hl.exec_cmd("waybar")
hl.exec_cmd("mako")
hl.exec_cmd("swaybg -m fill -i " .. wallpaper)
hl.exec_cmd("hypridle")
hl.exec_cmd("/usr/libexec/kf6/polkit-kde-authentication-agent-1")
end)
-------------------------------
---- ENVIRONMENT VARIABLES ----
-------------------------------
hl.env("XCURSOR_SIZE", "24")
hl.env("HYPRCURSOR_SIZE", "24")
-- Qt/KDE apps (dolphin, konsole) render natively rather than through XWayland.
hl.env("QT_QPA_PLATFORM", "wayland")
hl.env("QT_WAYLAND_DISABLE_WINDOWDECORATION", "1")
hl.env("MOZ_ENABLE_WAYLAND", "1")
-----------------------
---- LOOK AND FEEL ----
-----------------------
-- Prot's colours, from his 'modus-themes' for Emacs:
-- #ffffff white focused border (matches mako border-color)
-- #f78fe7 magenta focused-but-inactive border
-- #575757 grey unfocused border
-- #a60000 red urgent
-- #ffa200 orange indicator
--
-- SUBSTITUTION: Sway's `client.*` lines set five colours per window state
-- (border, background, text, indicator, child_border) because Sway draws
-- title bars. Hyprland has no title bars, so only the border colours carry
-- over. The backgrounds/text/indicator values have nowhere to go, and
-- Hyprland has no global "urgent" border colour at all.
hl.config({
general = {
-- Prot: `gaps inner 10px` / `gaps outer 0`
gaps_in = 5, -- Hyprland counts this per-edge, so 5 => 10px between windows
gaps_out = 0,
-- Prot: `default_border pixel 3`
border_size = 2,
col = {
active_border = "rgba(ffffffff)",
inactive_border = "rgba(575757ff)",
},
resize_on_border = false,
allow_tearing = false,
layout = "dwindle",
},
decoration = {
-- Prot's Sway config has no rounding, shadows or blur. Keeping that:
-- square corners, no effects.
rounding = 0,
active_opacity = 1.0,
inactive_opacity = 1.0,
shadow = { enabled = false },
blur = { enabled = false },
},
animations = {
enabled = true,
},
-- Prot: `workspace_auto_back_and_forth yes`
binds = {
workspace_back_and_forth = true,
},
dwindle = {
preserve_split = true,
},
misc = {
force_default_wallpaper = 0,
disable_hyprland_logo = true,
},
})
-- Groups are Hyprland's equivalent of Sway's tabbed layout (see $mod+t below).
-- Coloured to match Prot's focused/unfocused window states.
hl.config({
group = {
col = {
border_active = "rgba(ffffffff)",
border_inactive = "rgba(575757ff)",
},
groupbar = {
enabled = true,
font_family = font,
font_size = 11,
col = {
active = "rgba(2f337fff)",
inactive = "rgba(575757ff)",
},
},
},
})
hl.curve("easeOutQuint", { type = "bezier", points = { {0.23, 1}, {0.32, 1} } })
hl.animation({ leaf = "global", enabled = true, speed = 8, bezier = "easeOutQuint" })
hl.animation({ leaf = "border", enabled = true, speed = 5.39, bezier = "easeOutQuint" })
hl.animation({ leaf = "workspaces", enabled = true, speed = 4, bezier = "easeOutQuint", style = "slide" })
---------------
---- INPUT ----
---------------
hl.config({
input = {
-- Prot uses `xkb_layout "us,gr"` with win+space to toggle, because he
-- writes Greek. SUBSTITUTION: us only, matching your current
-- localectl setting. Add ",gr" and the kb_options line back if you
-- ever want the second layout.
kb_layout = "us",
-- Prot: `focus_follows_mouse yes`
follow_mouse = 1,
sensitivity = 0,
},
})
---------------------
---- KEYBINDINGS ----
---------------------
local mod = "SUPER"
-- Prot's home-row direction keys, like vim.
local left, down, up, right = "H", "J", "K", "L"
hl.bind(mod .. " + Return", hl.dsp.exec_cmd(terminal))
hl.bind(mod .. " + Q", hl.dsp.window.close())
hl.bind(mod .. " + D", hl.dsp.exec_cmd(menu))
hl.bind(mod .. " + E", hl.dsp.exec_cmd(fileManager))
hl.bind(mod .. " + SHIFT + C", hl.dsp.exec_cmd("hyprctl reload"))
-- Prot wraps exit in a swaynag confirmation prompt. SUBSTITUTION: Hyprland
-- has no swaynag, so this exits immediately with no confirmation dialog.
hl.bind(mod .. " + SHIFT + E", hl.dsp.exit())
-- ADDITION: Prot's config has no lock binding (his swayidle block is
-- commented out). $mod+L is taken by "focus right", so lock goes on Escape.
hl.bind(mod .. " + Escape", hl.dsp.exec_cmd("hyprlock"))
-- Move focus, vim keys and arrows both
hl.bind(mod .. " + " .. left, hl.dsp.focus({ direction = "left" }))
hl.bind(mod .. " + " .. down, hl.dsp.focus({ direction = "down" }))
hl.bind(mod .. " + " .. up, hl.dsp.focus({ direction = "up" }))
hl.bind(mod .. " + " .. right, hl.dsp.focus({ direction = "right" }))
hl.bind(mod .. " + left", hl.dsp.focus({ direction = "left" }))
hl.bind(mod .. " + down", hl.dsp.focus({ direction = "down" }))
hl.bind(mod .. " + up", hl.dsp.focus({ direction = "up" }))
hl.bind(mod .. " + right", hl.dsp.focus({ direction = "right" }))
-- Move the focused window, same keys plus Shift
hl.bind(mod .. " + SHIFT + " .. left, hl.dsp.window.move({ direction = "left" }))
hl.bind(mod .. " + SHIFT + " .. down, hl.dsp.window.move({ direction = "down" }))
hl.bind(mod .. " + SHIFT + " .. up, hl.dsp.window.move({ direction = "up" }))
hl.bind(mod .. " + SHIFT + " .. right, hl.dsp.window.move({ direction = "right" }))
hl.bind(mod .. " + SHIFT + left", hl.dsp.window.move({ direction = "left" }))
hl.bind(mod .. " + SHIFT + down", hl.dsp.window.move({ direction = "down" }))
hl.bind(mod .. " + SHIFT + up", hl.dsp.window.move({ direction = "up" }))
hl.bind(mod .. " + SHIFT + right", hl.dsp.window.move({ direction = "right" }))
-- Workspaces
for i = 1, 10 do
local key = i % 10 -- 10 maps to key 0
hl.bind(mod .. " + " .. key, hl.dsp.focus({ workspace = i }))
hl.bind(mod .. " + SHIFT + " .. key, hl.dsp.window.move({ workspace = i }))
end
---- Layout ----
-- Prot: `$mod+s split toggle` — flips the current split's orientation.
hl.bind(mod .. " + S", hl.dsp.layout("togglesplit"))
-- Prot: `$mod+t layout toggle tabbed split`.
-- SUBSTITUTION: Hyprland calls this a "group"; the groupbar above is its
-- tab bar.
hl.bind(mod .. " + T", hl.dsp.group.toggle())
-- Sway cycles tabs with the ordinary focus keys; in Hyprland those navigate
-- out of the group instead, so grouped windows need their own bindings.
hl.bind(mod .. " + Tab", hl.dsp.group.next())
hl.bind(mod .. " + SHIFT + Tab", hl.dsp.group.prev())
hl.bind(mod .. " + F", hl.dsp.window.fullscreen())
hl.bind(mod .. " + SHIFT + space", hl.dsp.window.float({ action = "toggle" }))
-- Prot: `floating_modifier $mod normal`
hl.bind(mod .. " + mouse:272", hl.dsp.window.drag(), { mouse = true })
hl.bind(mod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true })
---- ADDITIONS (not in Prot's config) ----
-- Screenshots. Requires grim and slurp.
hl.bind("Print", hl.dsp.exec_cmd("grim - | wl-copy"))
hl.bind("SHIFT + Print", hl.dsp.exec_cmd("grim -g \"$(slurp)\" - | wl-copy"))
-- Media keys
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+"), { locked = true, repeating = true })
hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"), { locked = true, repeating = true })
hl.bind("XF86AudioMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"), { locked = true })
hl.bind("XF86AudioMicMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"), { locked = true })
hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next"), { locked = true })
hl.bind("XF86AudioPause", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true })
hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true })
hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous"), { locked = true })
--------------------------------
---- WINDOWS AND WORKSPACES ----
--------------------------------
hl.window_rule({
name = "suppress-maximize-events",
match = { class = ".*" },
suppress_event = "maximize",
})
hl.window_rule({
-- Fix some dragging issues with XWayland
name = "fix-xwayland-drags",
match = {
class = "^$",
title = "^$",
xwayland = true,
float = true,
fullscreen = false,
pin = false,
},
no_focus = true,
})
hl.window_rule({
name = "float-pavucontrol",
match = { class = "^(pavucontrol)$" },
float = true,
})
hl.window_rule({
name = "float-polkit-agent",
match = { class = "^(org\\.kde\\.polkit-kde-authentication-agent-1)$" },
float = true,
})
|