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
|
local mainMod = "SUPER"
-- Quit Keys
hl.bind(mainMod .. " + Q", hl.dsp.window.close())
hl.bind(mainMod .. " + CTRL + Q", hl.dsp.exit())
-- Program keys
hl.bind(mainMod .. " + T", hl.dsp.exec_cmd(TERMINAL))
hl.bind(mainMod .. " + R", hl.dsp.exec_cmd(MENU))
hl.bind(mainMod .. " + B", hl.dsp.exec_cmd(BROWSER))
hl.bind(mainMod .. " + O", hl.dsp.exec_cmd(HYPRLOCK))
hl.bind(mainMod .. " + G", hl.dsp.exec_cmd(STEAM))
hl.bind(mainMod .. " + S",
hl.dsp.exec_cmd("grim -o" .. MAIN_DISPLAY .. " \"${HOME}/screenshots/screenshot-$(date +%F-%T).png\""))
hl.bind(mainMod .. " + SHIFT + S", hl.dsp.exec_cmd("grim -g \"$(slurp)\" - | wl-copy"))
local function focus_hyprland(direction)
hl.dispatch(hl.dsp.focus({ direction = direction }))
end
local function is_direct_nvim(title)
title = (title or ""):lower()
return title:find("^nvim")
or title:find(" - nvim", 1, true)
or title:find("^vim")
or title:find(" - vim", 1, true)
end
local function ask_direct_nvim(win, direction)
local runtime_dir = os.getenv("XDG_RUNTIME_DIR") or "/tmp"
local socket_path = string.format(
"%s/nvim-hypr-nav-%x.sock",
runtime_dir,
tostring(win.stable_id)
)
local handle = io.popen(string.format(
"printf 'nav:%s\\n' | socat -T 0.25 -,ignoreeof UNIX-CONNECT:%s 2>/dev/null",
direction,
socket_path
))
if not handle then
return false
end
local response = handle:read("*a")
handle:close()
return response:find("success", 1, true) ~= nil
end
local function ask_tmux(win, direction)
local pid = tonumber(win.pid)
if not pid then
return false
end
local helper = (os.getenv("HOME") or "")
.. "/.config/hypr/scripts/tmux-smart-nav"
local handle = io.popen(string.format("%s %s %d", helper, direction, pid))
if not handle then
return false
end
local result = handle:read("*a")
handle:close()
return result:find("handled", 1, true) ~= nil
end
local function smart_nav(direction)
return function()
local win = hl.get_active_window()
if not win then
return
end
if win.class ~= "com.mitchellh.ghostty" then
focus_hyprland(direction)
return
end
local title = (win.title or ""):lower()
if ask_tmux(win, direction) then
return
end
-- Herdr now uses changing window titles. The helper checks whether its
-- foreground client belongs to this terminal before attempting navigation.
local pid = tonumber(win.pid)
if pid then
local helper = (os.getenv("HOME") or "")
.. "/.config/hypr/scripts/herdr-smart-nav"
local handle = io.popen(string.format("%q %s %d", helper, direction, pid))
if handle then
local result = handle:read("*a"):match("^%s*(.-)%s*$")
handle:close()
if result == "handled" then
return
elseif result == "hyprland" then
focus_hyprland(direction)
return
end
end
end
if is_direct_nvim(title) and ask_direct_nvim(win, direction) then
return
end
focus_hyprland(direction)
end
end
local movement_keys = {
H = "left",
L = "right",
J = "down",
K = "up"
}
for key, dir in pairs(movement_keys) do
hl.bind(mainMod .. " + " .. key, smart_nav(dir))
end
hl.bind(mainMod .. " + SHIFT + H", hl.dsp.window.move({ direction = "left" }))
hl.bind(mainMod .. " + SHIFT + L", hl.dsp.window.move({ direction = "right" }))
hl.bind(mainMod .. " + SHIFT + J", hl.dsp.window.move({ direction = "down" }))
hl.bind(mainMod .. " + SHIFT + K", hl.dsp.window.move({ direction = "up" }))
hl.bind(mainMod .. " + CTRL + H", hl.dsp.window.resize({ x = -10, y = 0, relative = true }), { repeating = true })
hl.bind(mainMod .. " + CTRL + L", hl.dsp.window.resize({ x = 10, y = 0, relative = true }), { repeating = true })
hl.bind(mainMod .. " + CTRL + J", hl.dsp.window.resize({ x = 0, y = -10, relative = true }), { repeating = true })
hl.bind(mainMod .. " + CTRL + K", hl.dsp.window.resize({ x = 0, y = 10, relative = true }), { repeating = true })
for i = 1, 10 do
local key = i % 10
hl.bind(mainMod .. " + " .. key, hl.dsp.focus({ workspace = i }))
hl.bind(mainMod .. " + SHIFT + " .. key, hl.dsp.window.move({ workspace = i }))
end
-- Resize Keys
hl.bind(mainMod .. " + F", hl.dsp.window.fullscreen({ action = "toggle" }))
hl.bind(mainMod .. " + v", hl.dsp.window.float())
hl.bind(mainMod .. " + mouse:272", hl.dsp.window.drag(), { mouse = true })
hl.bind(mainMod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true })
-- Multimedia Keys
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume @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"))
hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true })
hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next"), { locked = true })
hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous"), { locked = true })
-- hl.bind("code:51", hl.dsp.pass({ window = "class:^([Dd]iscord)$" }), { non_consuming = true })
|