A read-only mirror of https://github.com/hyprwm/Hyprland
Find a file
Psilocybin78 c8c66642e4
seat: fix dropped wl_keyboard.enter after stale keyboardFocusResource (#14143)
* seat: fix dropped wl_keyboard.enter after stale keyboardFocusResource

CSeatManager::setKeyboardFocus's leave loop was gated on
m_state.keyboardFocusResource (a WP<CWLSeatResource>). That weak
pointer can expire between focus cycles — most reliably via the
explicit `.reset()` in KeybindManager::passKeys for XWayland
targets, but also in practice after certain wl_seat rebind
sequences from toolkit-level churn. When the WP is expired, the
leave loop's `if (m_state.keyboardFocusResource)` is false, the
loop is skipped, and no wl_keyboard.leave is sent for the previous
focus. The old client's CWLKeyboardResource keeps m_currentSurface
pointing at its old surface.

On the next focus back to that surface,
CWLKeyboardResource::sendEnter's per-keyboard dedup fires
(`if (m_currentSurface == surface) return;`) and the enter is
silently dropped. Net result: client believes it's still focused,
compositor routes keys via m_state.keyboardFocusResource which now
points somewhere else, typed keystrokes silently go to a different
window. A mouse drag resyncs because
InputManager::processMouseDownNormal → refocus() →
mouseMoveUnified(refocus=true) → rawWindowFocus(…, foundSurface)
takes a different path that forces the leave/enter pair through.

Reported and reproducible in discussion #14141.

Fix: iterate PROTO::seat->m_keyboards (the global owning list) in
the leave loop and let sendLeave()'s own m_currentSurface gate
decide whether each keyboard needs the event. sendLeave() and the
sendMods(0,…) that precedes it are already gated on m_currentSurface
being set, so iterating every keyboard is a no-op for any keyboard
that wasn't on the previous focus — identical semantics to the old
code in the healthy case, correct in the broken case.

Enter loop unchanged: it's driven by surf->client() which is a
cached wl_client* on the surface resource, stable across cycles.

Verified against the repro in #14141 (two wev instances cycled via
`hyprctl dispatch focuswindow`). Before the fix both windows get
stuck in a "I am focused" state after ~3 cycles. After the fix
leave/enter pair each dispatch cleanly.

* seat: apply same leave-loop fix to setPointerFocus

Identical failure mode to the keyboard case in the previous commit:
setPointerFocus's leave loop is gated on m_state.pointerFocusResource,
which can expire between focus changes. When it does, the leave is
skipped, the previously-focused client's wl_pointer m_currentSurface
stays stuck, and the next sendEnter's per-pointer dedup silently
drops the enter.

sendPointerButton also early-returns on `if (!m_state.pointerFocusResource)`,
so the visible symptom is "a click on a taskbar / dock / menu button
doesn't register after a layer-shell Overlay surface appeared or
disappeared, until the user moves the mouse." Motion re-syncs focus
via InputManager::mouseMoveUnified → setPointerFocus with a fresh
resource, at which point clicks work again.

Fix is the same: iterate PROTO::seat->m_pointers and let sendLeave's
m_currentSurface gate decide. No-op for pointers not on the old
surface.
2026-04-27 22:39:39 +01:00
.github config/lua: init lua config manager, use lua if available (#13817) 2026-04-26 15:16:36 +01:00
assets meson: drop 2025-11-17 08:54:47 +02:00
debug-tools debug-tools: add flame 2026-04-04 08:50:36 -04:00
docs [gha] build man pages 2026-04-26 14:16:54 +00:00
example examples: fix missing permissions entry in lua example config (#14177) 2026-04-27 22:39:16 +01:00
hyprctl config/lua: init lua config manager, use lua if available (#13817) 2026-04-26 15:16:36 +01:00
hyprpm build: add glaze dependency with FetchContent fallback (#13666) 2026-03-16 00:53:28 +02:00
hyprtester tests: tolerate plugin config mismatch in CI (#14173) 2026-04-27 15:29:38 +01:00
meta config/lua: init lua config manager, use lua if available (#13817) 2026-04-26 15:16:36 +01:00
nix config/lua: init lua config manager, use lua if available (#13817) 2026-04-26 15:16:36 +01:00
protocols render/cm: various updates, remove old protocols (#12693) 2025-12-27 18:01:46 +01:00
scripts renderer: shader variants refactor (#13434) 2026-03-06 21:05:10 +00:00
src seat: fix dropped wl_keyboard.enter after stale keyboardFocusResource (#14143) 2026-04-27 22:39:39 +01:00
start internal: silence compiler warnings about unused return values (#13997) 2026-04-05 14:45:27 +01:00
subprojects subprojects: bump tracy 2026-01-05 12:57:40 +01:00
systemd desktop: Update Exec command for UWSM Hyprland desktop entry (#12580) 2025-12-07 17:48:14 +00:00
tests config/lua: init lua config manager, use lua if available (#13817) 2026-04-26 15:16:36 +01:00
.clang-format touch up the clang format and format all files 2022-12-20 02:18:47 +00:00
.clang-format-ignore style/ci: apply clang-format and verify it in ci (#4039) 2023-12-06 22:54:56 +00:00
.clang-tidy config/lua: init lua config manager, use lua if available (#13817) 2026-04-26 15:16:36 +01:00
.gitattributes Initial commit 2022-03-16 20:49:02 +01:00
.gitignore debug-tools: add flame 2026-04-04 08:50:36 -04:00
.gitmodules Core: Move to aquamarine (#6608) 2024-07-21 13:09:54 +02:00
CMakeLists.txt cmake: install the default example hyprland.lua (#14174) 2026-04-27 20:59:45 +01:00
CODE_OF_CONDUCT.md doc: Add a CoC (#3366) 2023-09-20 19:38:44 +01:00
flake.lock [gha] Nix: update inputs 2026-04-25 21:17:24 +00:00
flake.nix treewide: alejandra -> nixfmt 2026-03-02 21:03:44 +02:00
hyprland.pc.in hyprland.pc.in: add src include flag 2026-03-13 22:57:49 +02:00
LICENSE LICENSE: update year 2026-01-11 16:13:52 +01:00
Makefile config/lua: init lua config manager, use lua if available (#13817) 2026-04-26 15:16:36 +01:00
README.md README: add link to CI from badge (#11085) 2025-07-18 21:13:56 +03:00
SECURITY.md SECURITY: init security policy 2025-04-27 00:07:00 +02:00
VERSION version: bump to 0.54.0 2026-02-27 18:03:19 +00:00

banner

Badge Workflow Badge License Badge Language Badge Pull Requests Badge Issues Badge Hi Mom


Hyprland is a 100% independent, dynamic tiling Wayland compositor that doesn't sacrifice on its looks.

It provides the latest Wayland features, is highly customizable, has all the eyecandy, the most powerful plugins, easy IPC, much more QoL stuff than other compositors and more...



Install

Quick Start

Configure

Contribute



Features

  • All of the eyecandy: gradient borders, blur, animations, shadows and much more
  • A lot of customization
  • 100% independent, no wlroots, no libweston, no kwin, no mutter.
  • Custom bezier curves for the best animations
  • Powerful plugin support
  • Built-in plugin manager
  • Tearing support for better gaming performance
  • Easily expandable and readable codebase
  • Fast and active development
  • Not afraid to provide bleeding-edge features
  • Config reloaded instantly upon saving
  • Fully dynamic workspaces
  • Two built-in layouts and more available as plugins
  • Global keybinds passed to your apps of choice
  • Tiling/pseudotiling/floating/fullscreen windows
  • Special workspaces (scratchpads)
  • Window groups (tabbed mode)
  • Powerful window/monitor/layer rules
  • Socket-based IPC
  • Native IME and Input Panels Support
  • and much more...


Gallery


Preview A


Preview B


Preview C



Special Thanks


wlroots - For powering Hyprland in the past

tinywl - For showing how 2 do stuff

Sway - For showing how 2 do stuff the overkill way

Vivarium - For showing how 2 do stuff the simple way

dwl - For showing how 2 do stuff the hacky way

Wayfire - For showing how 2 do some graphics stuff