fix: non-grab mode uses event sourcing (replay_and_inject) to avoid double-letter race conditions
This commit is contained in:
parent
82d0796059
commit
3612939643
1 changed files with 19 additions and 27 deletions
|
|
@ -1205,35 +1205,27 @@ fn run_with_evdev(
|
|||
}
|
||||
|
||||
if !grabbed {
|
||||
// Legacy mode: only forward to engine on press events.
|
||||
// Raw keystrokes reach the application BEFORE the daemon
|
||||
// can correct them, so VNI/Telex control keys (1-9, f,s,r,...)
|
||||
// appear on screen as literal characters. We must account
|
||||
// for this by adding one extra backspace for control keys.
|
||||
if value != 1 {
|
||||
continue;
|
||||
}
|
||||
if is_modifier_pressed(&key_state) {
|
||||
continue;
|
||||
}
|
||||
if let Some(ch) = key_to_char(key) {
|
||||
let mut commands = daemon.process_key(ch);
|
||||
// In non-grabbing mode, VNI/Telex control keys (1-9, f,s,r,x,j)
|
||||
// reach the application as literal characters before the daemon
|
||||
// can inject corrections. Add one extra backspace to remove
|
||||
// the control character from the screen.
|
||||
if !commands.is_empty()
|
||||
&& is_vn_control_key(&daemon.config.input_method, ch)
|
||||
{
|
||||
// Find and increment the first Backspace command
|
||||
for cmd in &mut commands {
|
||||
if let OutputCommand::Backspace(ref mut n) = cmd {
|
||||
*n += 1;
|
||||
break;
|
||||
}
|
||||
// Non-grabbing mode: raw keystrokes reach the application
|
||||
// directly. Use Event Sourcing (replay_and_inject) which
|
||||
// tracks what's actually on screen and computes corrections
|
||||
// based on the screen state — not the engine's internal
|
||||
// buffer. This avoids race conditions where a correction
|
||||
// backspace removes the wrong characters because the user
|
||||
// typed more while the clipboard paste was in flight.
|
||||
if value == 1 {
|
||||
if is_modifier_pressed(&key_state) {
|
||||
continue;
|
||||
}
|
||||
if let Some(ch) = key_to_char(key) {
|
||||
if ch == '\x08' {
|
||||
// Backspace: let the raw key through, but also
|
||||
// update the event sourcing state.
|
||||
daemon.replay_backspace();
|
||||
} else {
|
||||
let commands = daemon.replay_and_inject(ch);
|
||||
execute_commands(&*injector, &commands, false);
|
||||
}
|
||||
}
|
||||
execute_commands(&*injector, &commands, false);
|
||||
}
|
||||
} else {
|
||||
// Grabbing mode: all output goes through uinput only.
|
||||
|
|
|
|||
Loading…
Reference in a new issue