From 3f05f642817b2796d9d1b460b5cef99de945f822 Mon Sep 17 00:00:00 2001 From: The_miro Date: Sun, 26 Jul 2026 17:35:00 +0200 Subject: [PATCH] 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 --- desktopenvs/hyprdrive/hypr/usr/binds.lua | 27 +++++++++++++++++++++++- desktopenvs/hyprlua/hypr/usr/binds.lua | 27 +++++++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/desktopenvs/hyprdrive/hypr/usr/binds.lua b/desktopenvs/hyprdrive/hypr/usr/binds.lua index e2b4886..5a6e65f 100644 --- a/desktopenvs/hyprdrive/hypr/usr/binds.lua +++ b/desktopenvs/hyprdrive/hypr/usr/binds.lua @@ -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 -- 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`. +-- +-- 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 ok, aw = pcall(hl.get_active_window) if not ok or not aw or not aw.workspace then return end 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 hl.bind(mainMod .. " + CTRL + F", toggle_monocle) diff --git a/desktopenvs/hyprlua/hypr/usr/binds.lua b/desktopenvs/hyprlua/hypr/usr/binds.lua index 896bdfb..3c54adf 100644 --- a/desktopenvs/hyprlua/hypr/usr/binds.lua +++ b/desktopenvs/hyprlua/hypr/usr/binds.lua @@ -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 -- 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`. +-- +-- 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 ok, aw = pcall(hl.get_active_window) if not ok or not aw or not aw.workspace then return end 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 hl.bind(mainMod .. " + CTRL + F", toggle_monocle)