feat(hyprdrive,hyprlua): trap pointer to focused window in monocle (super+ctrl+f)

Monocle now also confines the cursor to the focused full-size window so it
can't slip onto another monitor. Hyprland has no cursor-confine dispatcher, so
this drives the confine_pointer window rule: one lazily-created rule per
workspace, matched by workspace id (follows focus), enabled/disabled in
lock-step with the monocle toggle and refreshed immediately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
main
Amir Alexander Abdelbaki 2026-07-26 17:35:00 +02:00
parent 967af1c0f2
commit 3f05f64281
2 changed files with 52 additions and 2 deletions

View File

@ -107,11 +107,36 @@ hl.bind(mainMod .. " + CTRL + M", hl.dsp.exec_cmd("~/.config/scripts/togg
-- Switch the focused window's workspace to the "monocle" layout (one full-size window -- Switch the focused window's workspace to the "monocle" layout (one full-size window
-- at a time), toggling back to the session default (columns) on a second press. -- at a time), toggling back to the session default (columns) on a second press.
-- Uses the same layouts registry the astro-menu popup drives via `hyprctl eval`. -- Uses the same layouts registry the astro-menu popup drives via `hyprctl eval`.
--
-- Monocle also traps the pointer to the focused window. Hyprland has no cursor-confine
-- dispatcher, but the `confine_pointer` window rule pins the pointer to whichever window
-- is focused on a matched workspace, so the trap follows focus as you cycle the single
-- full-size window and the cursor can't slip onto another monitor. We keep one rule per
-- workspace, created lazily and enabled/disabled in lock-step with the monocle toggle.
local confine_rules = {} -- wid(str) -> HL.WindowRule
local function set_pointer_trap(wid, on)
local rule = confine_rules[wid]
if not rule then
local okr, r = pcall(hl.window_rule, {
name = "monocle-confine-" .. wid,
match = { workspace = wid },
confine_pointer = true,
enabled = false,
})
if not okr then return end
rule, confine_rules[wid] = r, r
end
rule:set_enabled(on)
-- apply to the already-focused window now, instead of on the next window event
if hl.exec_scheduled_prop_refresh_immediately then hl.exec_scheduled_prop_refresh_immediately() end
end
local function toggle_monocle() local function toggle_monocle()
local ok, aw = pcall(hl.get_active_window) local ok, aw = pcall(hl.get_active_window)
if not ok or not aw or not aw.workspace then return end if not ok or not aw or not aw.workspace then return end
local wid = tostring(aw.workspace.id) local wid = tostring(aw.workspace.id)
layouts.set(wid, layouts.current[wid] == "monocle" and "columns" or "monocle") local to_monocle = layouts.current[wid] ~= "monocle"
layouts.set(wid, to_monocle and "monocle" or "columns")
set_pointer_trap(wid, to_monocle)
end end
hl.bind(mainMod .. " + CTRL + F", toggle_monocle) hl.bind(mainMod .. " + CTRL + F", toggle_monocle)

View File

@ -111,11 +111,36 @@ hl.bind(mainMod .. " + CTRL + M", hl.dsp.exec_cmd("~/.config/scripts/togg
-- Switch the focused window's workspace to the "monocle" layout (one full-size window -- Switch the focused window's workspace to the "monocle" layout (one full-size window
-- at a time), toggling back to the session default (columns) on a second press. -- at a time), toggling back to the session default (columns) on a second press.
-- Uses the same layouts registry the astal-menu popup drives via `hyprctl eval`. -- Uses the same layouts registry the astal-menu popup drives via `hyprctl eval`.
--
-- Monocle also traps the pointer to the focused window. Hyprland has no cursor-confine
-- dispatcher, but the `confine_pointer` window rule pins the pointer to whichever window
-- is focused on a matched workspace, so the trap follows focus as you cycle the single
-- full-size window and the cursor can't slip onto another monitor. We keep one rule per
-- workspace, created lazily and enabled/disabled in lock-step with the monocle toggle.
local confine_rules = {} -- wid(str) -> HL.WindowRule
local function set_pointer_trap(wid, on)
local rule = confine_rules[wid]
if not rule then
local okr, r = pcall(hl.window_rule, {
name = "monocle-confine-" .. wid,
match = { workspace = wid },
confine_pointer = true,
enabled = false,
})
if not okr then return end
rule, confine_rules[wid] = r, r
end
rule:set_enabled(on)
-- apply to the already-focused window now, instead of on the next window event
if hl.exec_scheduled_prop_refresh_immediately then hl.exec_scheduled_prop_refresh_immediately() end
end
local function toggle_monocle() local function toggle_monocle()
local ok, aw = pcall(hl.get_active_window) local ok, aw = pcall(hl.get_active_window)
if not ok or not aw or not aw.workspace then return end if not ok or not aw or not aw.workspace then return end
local wid = tostring(aw.workspace.id) local wid = tostring(aw.workspace.id)
layouts.set(wid, layouts.current[wid] == "monocle" and "columns" or "monocle") local to_monocle = layouts.current[wid] ~= "monocle"
layouts.set(wid, to_monocle and "monocle" or "columns")
set_pointer_trap(wid, to_monocle)
end end
hl.bind(mainMod .. " + CTRL + F", toggle_monocle) hl.bind(mainMod .. " + CTRL + F", toggle_monocle)