Skip to content

Mode Uinput: sleep_for trong handleUInputKeyPress chặn event loop của cả fcitx5 mỗi lần thay chữ, đổi sang timer giảm ~14 lần #488

Description

@nguyenphivn

Mô tả lỗi

Mode Uinput (Smooth / Super Smooth / Slow), mỗi lần thay chữ (eê, thêm dấu...) Lotus sleep_for ngay trong keyEvent trước khi commit:

  • src/lotus-state.cpp:449: sleep_for(sleepTime * (expected_backspaces_ - 1))
  • src/lotus-state.cpp:457: retry 3 × sleep_for(2 ms)

fcitx5 chỉ có một event loop cho cả máy, nên trong lúc ngủ mọi cửa sổ đều đứng, không riêng ô đang gõ.

Vòng retry còn tệ hơn: nó chờ surroundingText() đổi, nhưng loop đang bị chặn nên giá trị đó không thể đổi giữa các lần thử. Điều kiện sai lần đầu thì cả 3 lần đều sai, và lần nào cũng mất trọn 6 ms. Với app không có surrounding text (Chromium trên X11, terminal) thì lần thay chữ nào cũng mất 6 ms này.

Mình đo bằng cách bọc keyEvent một đồng hồ, ghi thời gian mỗi lần nó giữ loop. Bàn phím ảo uinput, 15 cụm Telex, 50 ms/phím (tốc độ tay):

tổng thời gian loop bị chặn lâu nhất số phím chặn ≥ 5 ms
Thanh địa chỉ Edge 482 ms 12,4 ms 48
gnome-terminal (Super Smooth) 483 ms 12,5 ms 48
Ô GTK3 342 ms 10,5 ms 29

Các bước tái hiện

  1. Mode = Uinput (Smooth)
  2. Gõ tiếng Việt có dấu ở bất kỳ app nào
  3. Mỗi lần Lotus thay chữ, fcitx5 đứng 2–14 ms

Kết quả mong đợi

Lotus chờ app xoá xong mà không chặn event loop.

Kết quả thực tế

sleep_for chặn cả fcitx5 mỗi lần thay chữ.

Hướng fix

Giữ nguyên khoảng chờ (sleepTime × (N − 1), cộng 6 ms nếu điều kiện retry sai), nhưng chờ bằng addTimeEvent rồi trả loop về ngay. Điều kiện retry quyết một lần lúc bắt đầu chờ, đúng như cái bản sleep_for thực chất đang làm.

Có ba chỗ phải xử lý kèm:

  • Phím tới trong lúc chờ (gõ rất nhanh): chờ nốt phần còn lại, commit, rồi xử lý phím đó như thường. Mình thử cách cất vào buffered_keys_ rồi replay bằng commitString thì Chromium X11 rơi chữ. Cách "chờ nốt" giữ đúng thứ tự như bản sleep_for, và chỉ chặn loop khi thật sự có phím chen vào.
  • Focus out trong lúc chờ: không commit sớm, để timer tự commit khi IC focus lại. Chromium X11 tự focus out/in liên tục, xem Uinput mất chữ ở thanh địa chỉ Chromium trên X11: Chromium focus out/in mỗi lần reset IM, Lotus bỏ replacement đang dở #486.
  • Test headless (smooth_buffered_key_replay, held_key_repeat) đang ngầm giả định commit xảy ra ngay trong keyEvent. Mình thêm pumpEventLoop() vào test-input-context.h để chạy loop vài chục ms. EventLoop::exec() gọi lần hai sau exit() thì sd-event ném -ESTALE, nên mình phải gọi sd_event_run qua dlsym. Chỗ này bạn có cách gọn hơn thì mình đổi.

Sau fix, cùng cách đo:

tổng bị chặn lâu nhất số phím chặn ≥ 5 ms
Thanh địa chỉ Edge 34 ms 0,6 ms 0
gnome-terminal 30 ms 0,7 ms 0
Ô GTK3 38 ms 0,7 ms 0

Gõ đúng/sai không đổi ở 10, 15, 20, 50 ms/phím và khi gõ chồng phím 30 ms (Edge, Firefox, GTK3, gnome-terminal). Bấm Backspace ngay 1 ms sau phím dấu vẫn đúng (5/5). ctest 10/10.

Đánh đổi: ở 5 ms/phím (200 phím/giây, nhanh hơn tay người nhiều) thanh địa chỉ Edge còn 17/30, bản cũ 15/15. Nguyên nhân mình chưa tìm ra. Mới đo trên X11 (GNOME), chưa đo Wayland.

Code: commit 14d064d trên nhánh nguyenphivn:b33/hen-gio. Nhánh này hiện nằm trên nền #477 và fix của #486. Bạn thấy hướng ổn thì mình tách một nhánh riêng trên dev.

Phiên bản fcitx5-lotus

dev 2fb0b82 (code liên quan giống 3.5.9)

Cách cài đặt

Tự build từ source

Hệ điều hành / Distro

Ubuntu 24.04

Môi trường Desktop

GNOME (X11)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    • Status
      Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions