CƠ CHẾ LÀM VIỆC CỦA xvnkb 0.1.x

TÁC GIẢ: Đào Hải Lâm
THAM KHẢO: X-Window programming

"CHẶN" VÀ XỬ LÝ EVENTS

Cơ chế làm việc của X Server cho phép ta "đăng ký" nhận events gửi đến mọi window của tất cả các X Applications khác đang làm việc trong cùng một screen với chương trình của chúng ta. Đây chính là "chìa khoá" để xvnkb 0.1.x có thể tác động đến các X Apps khác. Để "đăng ký" nhận events ta dùng function XSelectInput:

XSelectInput(display, window, event_mask)

Trong đó:
display display trong chương trình của chúng ta
window window id của các chương trình khác mà ta muốn nhận events
event_mask các events mà chúng ta muốn X server "báo" cho chúng ta biết khi nó được gửi tới window tương ứng

Sau khi chúng ta đã "đăng ký" nhận events thì event flow sẽ giống như sơ đồ sau:

                       +--------------+
                       |   X Server   |
                       +------+-------+
                              |
                              | events
                              |
                   masked     |
                   events     |
                 +------------+-------------+
                 |                          |
                 |                          |
                 v                          v
             +-------+                  +-------+
             | xvnkb |                  | X app |
             +-------+                  +-------+

Với cách này, xvnkb có thể đồng thời nhận được tất cả các key events được gửi tới các X Apps khác. Sau khi xử lý các key events này, nó có thể tạo ra 1 hoặc nhiều events giả, rồi gửi tới X App đang nhận xử lý key event (focused X app) bằng cách dùng function XSendEvent.

XSendEvent(display, window, propagate, event_mask, event)

Trong đó:
display display của xvnkb
window focused window
propagate (xem thêm trong reference)
event_mask mask của event cần send (ví dụ: KeyPress, KeyRelease)
event event cần send

                       +--------------+
                       |   X Server   |
                       +------+-------+
                              |
                              | events
                              |
                   masked     |
                   events     |
                 +------------+-------------+
                 |                          |
                 |                          |
                 v                          v
             +-------+           events +-------+
             | xvnkb |           +----->| X app |
          +--+-------+--+        |      +-------+
          |     key     |        |
          |  processing |--------+
          +-------------+ (XSendEvent)

Như vậy khi user nhập vào "ddoongj" (telex mode), xvnkb sẽ xử lý như sau:

  • Khi nhận được "dd":
    • send 2 backspaces đến focused window để xoá "dd"
    • sau đó send "đ"
  • Khi nhận được "oo":
    • send 2 backspaces đến focused window để xoá "oo"
    • sau đó send "ô"
  • Khi nhận được "j":
    • send 4 backspaces đến focused window để xoá "ôngj"
    • sau đó lần lượt send "ộ", "n", "g" (tổng cộng 7 lần gọi XSendEvent)

(+) Xem file focuswin.c trong source code của xvnkb để biết thêm chi tiết, các function cần chú ý: VKSendKeyEvent, VKSendKey, VKKeyHandler

KIỂM TRA VÀ LẤY FOCUSED WINDOW

Để lấy được focused window, cách đơn giản nhất là định thời (timer) và gọi function XGetInputFocus:
XGetInputFocus(display, focus_return, revert_to_return)

(+) Xem thêm main.c/monitor.c

"ĐĂNG KÝ" NHẬN EVENTS

Vấn đề còn lại là làm sao để có thể lấy được tất cả các window trong screen hiện tại để "đăng ký" nhận key event, đồng thời khi có 1 X App mới được load lên ta phải nhận biết để cập nhật những window mới?

Để giải quyết vấn đề thứ nhất ta dung function XQueryTree để lấy tất cả các sub-windows của root window. Sau đó, dùng đệ quy (recursion) để tìm tiếp các sub-windows ở cấp sâu hơn. Cứ với mỗi window tìm được (ngoại trừ root window) ta đều "đăng ký" nhận key events.

Với vấn đề thứ hai, vì khi có 1 window mới hiện ra thì root window hoặc focused window (nếu đó là 1 sub-window của chương trình đang chạy) sẽ nhận được MapNotify event. Vậy ta chỉ việc "đăng ký" nhận thêm các MapNotify events và chỉ cần cập nhật lại các sub-windows của window mới hiện ra như đã làm ở trên.

(+) Xem file winctrl.c trong source code của xvnkb 0.1.x để biết thêm chi tiết, xem focuswin.c phần process events MapNofity

NHẬN XÉT

Với cách làm việc như trên, thực chất xvnkb không chặn được các events gửi tới focused window nào đó, nó chỉ đồng thời nhận được 1 bản copy của events, sau khi "nhận dạng" và xử lý nó sẽ gửi các events phù hợp đến focused window, nhằm thay đổi và tạo ra chuỗi ký tự nhập phù hợp.

HẠN CHẾ

  • Do không chặn được các events nên các ký tự bỏ dấu vẫn hiện ra trên focused window rồi mới bị xoá đi.
  • Việc send event xảy ra khá chậm vì cần phải gọi XSync sau mỗi lần send để đẩy tất cả các events ra khỏi event queue (nếu không gọi XSync sẽ bị mất event). Do đó sẽ xảy ra tình trạng như sau: trong khi xvnkb đang send event tới focused window thì đồng thời lúc đó user nhập thêm ký tự => thứ tự các ký tự bị lẫn lộn.
  • Ngoài ra do tốc độ send event chậm nên với 1 số X apps dùng các lib đòi hỏi xử lý nhiều phím tắt (hotkey), cần nhiều thời gian để xử lý key như Qt/Gtk2 sẽ gặp rất nhiều lỗi...