fix: VNI/Telex control key flicker in non-grabbed mode

When a VNI/Telex control key (e.g. digit 6 for ô, w for â/ê/ô) is
pressed, the engine absorbs it in-place without emitting an event. In
non-grabbed mode the raw character already reached the terminal. Fix:
capture buf_before, compare with buf_after, and synthesize
Backspace(len+1) + Type(buf_after) when the buffer changed.
This commit is contained in:
Khoa Vo 2026-07-02 15:35:53 +07:00
parent 2553f20466
commit 035aeca997

View file

@ -310,8 +310,6 @@ impl Daemon {
commands.push(OutputCommand::Type(text)); commands.push(OutputCommand::Type(text));
} }
} }
} else {
// No event — key was consumed or ignored by engine
} }
commands commands
@ -1269,15 +1267,17 @@ fn run_with_x11_keymap(
// Use keymap lookup for character conversion // Use keymap lookup for character conversion
if let Some(ch) = capture.lookup_keycode(keycode, mod_state) { if let Some(ch) = capture.lookup_keycode(keycode, mod_state) {
let buf_before = daemon.engine.buffer();
let mut commands = daemon.process_key(ch); let mut commands = daemon.process_key(ch);
if !commands.is_empty() if commands.is_empty()
&& daemon.engine.is_enabled()
&& is_vn_control_key(daemon.app_state.effective_method(), ch) && is_vn_control_key(daemon.app_state.effective_method(), ch)
{ {
for cmd in &mut commands { let buf_after = daemon.engine.buffer();
if let OutputCommand::Backspace(ref mut n) = cmd { if buf_after != buf_before && !buf_before.is_empty() {
*n += 1; let len = buf_before.chars().count();
break; commands.push(OutputCommand::Backspace(len + 1));
} commands.push(OutputCommand::Type(buf_after));
} }
} }
execute_commands(&*injector, &commands, false); execute_commands(&*injector, &commands, false);
@ -1499,15 +1499,17 @@ fn run_with_evdev(
continue; continue;
} }
if let Some(ch) = key_to_char(key) { if let Some(ch) = key_to_char(key) {
let buf_before = daemon.engine.buffer();
let mut commands = daemon.process_key(ch); let mut commands = daemon.process_key(ch);
if !commands.is_empty() if commands.is_empty()
&& daemon.engine.is_enabled()
&& is_vn_control_key(daemon.app_state.effective_method(), ch) && is_vn_control_key(daemon.app_state.effective_method(), ch)
{ {
for cmd in &mut commands { let buf_after = daemon.engine.buffer();
if let OutputCommand::Backspace(ref mut n) = cmd { if buf_after != buf_before && !buf_before.is_empty() {
*n += 1; let len = buf_before.chars().count();
break; commands.push(OutputCommand::Backspace(len + 1));
} commands.push(OutputCommand::Type(buf_after));
} }
} }
execute_commands(&*injector, &commands, false); execute_commands(&*injector, &commands, false);