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

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

SƠ LƯỢC VỀ SHARED LIBS & PRELOAD LIBS

a) Shared libs

Một chương trình khi được compiled và linked với một shared libs thì tất cả các function mà nó dùng trong shared libs đó được lưu bên trong nó ở dạng function pointers. Khi hệ thống load chương trình này lên sẽ thực hiện các bước sau:

  • Tìm và load các shared libs mà chương trình này linked tới nếu như libs đó chưa được load bởi các apps khác, lấy địa chỉ của các functions.
  • Load chương trình chính, sửa đổi các function pointer bằng các địa chỉ của function tương ứng.
             +-------------------+                 +-------------+
             |    (Aplication)   |                 |   [glibc]   |
          ,--+-------------------+                 +-------------+
          |  |       ...         |                 |     ...     |
          |  +-------------------+                 |     ...     |
          |  |      printf       |------.          |     ...     |
 function |  +-------------------+      |          +-------------+
   table <   |       ...         |      `--------->|   printf    |
    (*)   |  +-------------------+                 +-------------+
          |  |    XNextEvent     |------.          |     ...     |
          |  +-------------------+      |          +-------------+
          |  |       ...         |      |
          |  |       ...         |      |          +-------------+
          `--+-------------------+      |          |   [Xlib]    |
             |                   |      |          +-------------+
             |                   |      |          |     ...     |
                                        |          +-------------+
                                        `--------->|  XNextEvent |
                                                   +-------------+
                                                   |     ...     |
                                                   +-------------+

(*) Tạm gọi bảng các function pointers này là function table cho ... dễ nhớ và dễ hiểu ;)

Ngược lại, nếu chương trình được linked với các static libs thì sẽ không có các function pointers mà code của các function đó sẽ được chèn vào bên trong chương trình và trở thành 1 phần code của chương trình.

b) Preload libs

Khi hệ thống load 1 app vào memory, nó sẽ kiểm tra xem biến môi trường (1) LD_PRELOAD có giá trị hay không, nếu có nó sẽ thử load các libs mà LD_PRELOAD chứa lên trước. Khi sửa đổi các giá trị trong function table, nếu các preloaded libs có chứa các function trùng tên với các function trong shared libs mà chương trình đang sử dụng, thì địa chỉ của các function trong preloaded libs sẽ được lưu vào function table thay vì địa chỉ của các function trong shared libs.

Ví dụ: LD_PRELOAD=/path/to/xvnkb.so

             +-------------------+                 +-------------+
             |    (Aplication)   |                 |   [glibc]   |
             +-------------------+                 +-------------+
             |       ...         |                 |     ...     |
             +-------------------+                 |     ...     |
             |      printf       |------.          |     ...     |
             +-------------------+      |          +-------------+
             |       ...         |      `--------->|   printf    |
             +-------------------+                 +-------------+
             |    XNextEvent     |------.          |     ...     |
             +-------------------+      |          +-------------+
             |       ...         |      |
             |       ...         |      |     +~~~~~~~~~~~~~~~~~~~~~+
             +-------------------+      |     |     [xvnkb.so]      |
             |                   |      |     +~~~~~~~~~~~~~~~~~~~~~+
             |                   |      |     |        ...          |
                                        |     +~~~~~~~~~~~~~~~~~~~~~+
                                        `---->|     XNextEvent      |
                                              |        ...          |
                                              |        ...          |
                                              | call Xlib:XNextVent }-.
                                              +~~~~~~~~~~~~~~~~~~~~~+ |
                                                                      |
                                                   +-------------+    |
                                                   |   [Xlib]    |    |
                                                   +-------------+    |
                                                   |     ...     |    |
                                                   +-------------+    |
                                                   |  XNextEvent |<---'
                                                   +-------------+
                                                   |     ...     |
                                                   +-------------+

Với cách trên Unix/Linux (2) đã cung cấp cho chúng ta 1 cách rất dễ dàng để có thể "nạp chống" (3) các function của ta lên trên các function của hệ thống; Cho phép ta xử lý trước/sau các dữ liệu vào/ra function của hệ thống (4).

(+) Xem thêm các sample programs về shared libs & preload libs tại: http://xvnkb.sourceforge.net/preload

(+) Chú thích:

  1. environment varirable
  2. Linux, *BSD, Sun Solaris (?)
  3. overload
  4. pre/post process

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

Để nhận được các events từ X Server, hầu hết (tất cả?) các X Apps đều gọi function XNextEvent để lấy các events trong event-queue ra để xử lý. Lợi dụng điểm này và tận dụng cơ chế "nạp chồng" function mà Unix/Linux đã cung cấp, xvnkb-0.2.x có thể chặn được các X events một cách dễ dàng bằng cách tạo lại XNextEvent function để pre/post process events. Event flow lúc này sẽ giống như sơ đồ sau:

                       +--------------+
                       |   X Server   |
                       +------+-------+
                              |
                              |
                              v
                        +----------+
                        | xvnkb.so |
                    +---+----------+---+
                    |     XNextEvent   |
                    |(pre/post process)|
                    +------------------+
                          ^      |
                          |      |
                 request  |      | return
               for events |      | events
                          |      v
                        +----------+
                        |   X app  |
                        +----------+

SEND STRING VỀ CHO CHƯƠNG TRÌNH

Trong X-Window, 1 key code có thể tương ứng với 1 chuỗi ký tự (string). Do đó, khi nhận được 1 key event, các X Apps thường gọi XLookupString để chuyển đổi (translate) key code ra thành chuỗi tương ứng. Lợi dụng điểm này xvnkb tạo ra 1 overloaded XLookupString của riêng nó để khi cần có thể gửi cả 1 string về cho chương trình gọi. Cụ thể, khi nó nhận được event có key code = 1 (không năm trong key code range của X-Window), nó sẽ gửi nguyên buffer đang xử lý về cho chương trình gọi.

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

  • Khi nhận được "d" thứ 2:
    • buffer["d"] ==> buffer["đ"]
    • push back event với key code = 1 vào event-queue.
    • push back backspace event vào trong event-queue để xoá "d" thứ nhất đã gửi đến chương trình.
    • Khi chương trình gọi XLookupString với event có key code = 1, send về string "đ"
  • Khi nhận được "o" thứ 2 làm tương tự như với "d" thứ 2:
    • buffer["đo"] ==> buffer["đô"]
  • Khi nhận được "j"
    • buffer["đông"] ==> buffer["động"]
    • push back event với key code = 1 vào event-queue.
    • push back 3 backspace events vào event-queue để xoá "ông".
    • Khi chương trình gọi XLookupString với event có key code = 1, send về string "ộng".

(+) Xem file xvnkb.c để nắm thêm chi tiết

Ngoài ra, khi nhận được string từ XLookupString, các chương trình sẽ dựa vào LANG/Locale để chuyển đổi (convert) sang dạng cần thiết để hiển thị (display) hay xử lý. Vì vậy, nếu ta đặt LANG=<something>.UTF-8 và setup các thông tin về locale cho <something>.UTF-8 thật chính xác, ta có thể gửi trả về chương trình cả chuỗi utf-8. Đây chính là cách mà xvnkb đang dùng để hỗ trợ nhập unicode text ;).

VAI TRÒ CỦA xvnkb GUI

Trong xvnkb-0.2.x, phần GUI chỉ dùng để thay đổi các cờ (flags) bên trong xvnkb.so (phần CORE) qua việc thay đổi các properties của root window. Nó không tham gia trực tiếp vào quá trình xử lý tiếng Việt. Nó có thể được thay thế bởi các apps khác có chức năng tương tự (xem utils/xvnkb_ctrl.c).

NHẬN XÉT

Với cách "chặn events" và "send string" như trên, xvnkb làm việc rất hiệu quả. So với cách làm việc của xvnkb-0.1.x thì cách làm việc của xvnkb-0.2.x đơn giản và nhanh hơn rất rất nhiều.

HẠN CHẾ

  • Hơi phức tạp trong quá trình setup LD_PRELOAD đối với những người mới dùng.
  • Không có tác dụng với các X Apps được linked static với Xlib. Tuy nhiên, rất hiếm khi xảy ra trường hợp này vì các lý do sau:
    • Xlib quá to và cồng kềnh khi link static.
    • Xlib thay đổi rất chậm, các API hầu như đã ổn định rất ít thay đổi, đặc biệt là với các standard functions như XNextEvent & XLookupString.
  • Một số chương trình khi gọi XLookupString không cần quan tâm tới giá trị của KeySym trả về, chỉ quan tâm tới nội dung của string (giá trị này thường chính xác hơn). Tuy nhiên, số còn lại (trong đó có Opera 6.x - x < 11) lại chỉ quan tâm tới giá trị KeySym. Hiện tại, xvnkb chỉ xử lý cho trường hợp #1 (vì trong UTF-8 mode, KeySym rất khó có thể xác định được).