Dismiss Wayland internal UI on suspension and retain key repeats

This commit is contained in:
Storm Dragon committed 2026-11-11 11:25:47 -05:00
1 parent 0d5a5f7ac1
commit 12cadda7c1
5 files changed
+77 -5

No files matched your search

+8
View File
@@ -83,6 +83,14 @@ func (owner *keyboardOwner) requested_policy() keyboardPolicy {
if !keyboard.enabled.Load() || keyboard.secure.Load() {
owner.learning = false
keyboard.learn.Store(false)
if owner.uiSession != 0 {
session := owner.uiSession
owner.dismiss_internal_ui()
select {
case keyboard.commands <- input.CommandEvent{Command: input.InternalUIDismiss, UISession: session}:
default:
}
}
}
return keyboardPolicy{enabled: keyboard.enabled.Load(), browse: keyboard.browse.Load(), button: keyboard.nativeButton.Load(), secure: keyboard.secure.Load(), learn: owner.learning, bypass: keyboard.bypass.Load(), ui: owner.uiSession != 0}
}
@@ -615,6 +615,61 @@ func TestNativeInternalUIEndWaitsForHeldKeyRelease(t *testing.T) {
}
}
func TestNativeInternalUIRepeatsKeys(t *testing.T) {
monitor := start_monitor(t)
keyboard := start_keyboard(t, monitor)
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
session, err := keyboard.BeginInternalUI(ctx)
if err != nil {
t.Fatal(err)
}
monitor.key(t, 0xff08, 22, 0, false)
monitor.key(t, 0xff08, 22, 0, false)
for i := 0; i < 2; i++ {
if event := await_command(t, keyboard, input.InternalUIKey); event.UISession != session || event.Chord.Symbol != 0xff08 {
t.Fatalf("Backspace repeat %d = %+v", i, event)
}
}
monitor.key(t, 0xff08, 22, 0, true)
if err := keyboard.EndInternalUI(ctx, session); err != nil {
t.Fatal(err)
}
}
func TestNativeInternalUISuspensionDismissesBeforeHeldRelease(t *testing.T) {
monitor := start_monitor(t)
keyboard := start_keyboard(t, monitor)
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
session, err := keyboard.BeginInternalUI(ctx)
if err != nil {
t.Fatal(err)
}
monitor.key(t, 0xff0d, 36, 0, false)
await_command(t, keyboard, input.InternalUIKey)
keyboard.SetCommandGrabs(false)
if event := await_command(t, keyboard, input.InternalUIDismiss); event.UISession != session {
t.Fatalf("suspension dismiss = %+v", event)
}
if keyboard.InternalUIActive() {
t.Fatal("internal UI stayed active after command grabs were suspended")
}
if grabbed, _, _, _ := monitor.grab_state(); !grabbed {
t.Fatal("keyboard ungrabbed while Enter was held")
}
monitor.key(t, 'a', 38, 0, false)
monitor.key(t, 'a', 38, 0, true)
monitor.key(t, 0xff0d, 36, 0, true)
wait_snapshot(t, monitor, func(state MonitorSnapshot) bool { return !state.grabbed && len(state.grabs) == 0 })
if len(keyboard.commands) != 0 || len(keyboard.events) != 0 {
t.Fatal("keys from the closing UI reached command routing or monitoring")
}
if err := keyboard.EndInternalUI(ctx, session); err != nil {
t.Fatal(err)
}
}
func TestNativeShowActionsCapturePreservesHeldReaderModifier(t *testing.T) {
monitor := start_monitor(t)
keyboard := start_keyboard(t, monitor)
+10 -1
View File
@@ -255,14 +255,23 @@ func (owner *keyboardOwner) handle_key(key keyEvent) error {
if owner.policy.enabled && (key.released || key.state <= 0xffff) {
consumed, command = owner.router.Handle(symbol, key.code, uint16(key.state), !key.released)
}
if !keyboard.enabled.Load() {
consumed, command = false, ""
}
if key.released {
delete(owner.captured, key.code)
} else if consumed || owner.learning || owner.uiSession != 0 {
owner.captured[key.code] = true
}
keyboard.active.Store(len(owner.captured) != 0 || owner.learning || owner.uiSession != 0)
// The compositor can still hold an exclusive grab while the opening
// chord drains. Keys in that interval belong to the closing UI, not
// ordinary command routing or monitoring.
if owner.policy.ui && owner.uiSession == 0 || !keyboard.enabled.Load() && owner.uiSession != 0 {
return nil
}
if owner.uiSession != 0 {
return owner.handle_ui_key(key, symbol, chord, repeated, timestamp)
return owner.handle_ui_key(key, symbol, chord, timestamp)
}
if !key.released && !repeated && owner.learning {
command = input.LearnKey
+2 -2
View File
@@ -106,8 +106,8 @@ func (owner *keyboardOwner) dismiss_internal_ui() {
// handle_ui_key consumes one key for the active session. Reader modifiers
// and their chords never become field contents; Escape reports the key
// and dismisses the session, matching the Xorg owner.
func (owner *keyboardOwner) handle_ui_key(key keyEvent, symbol uint32, chord input.CommandChord, repeated bool, timestamp uint32) error {
if key.released || repeated {
func (owner *keyboardOwner) handle_ui_key(key keyEvent, symbol uint32, chord input.CommandChord, timestamp uint32) error {
if key.released {
return nil
}
if owner.router.IsModifier(key.symbol) {
+2 -2
View File
@@ -21,8 +21,8 @@ import "strings"
const Name = "Cthulhu"
const Version = "2026.10.31"
const Channel = "master"
const Version = "2026.11.11"
const Channel = "hunter"
var Build = Version