Cách người Việt gõ
Người dùng tiếng Việt mong đợi từng chữ hiện ngay trong app khi gõ, từ được sửa ngay tại chỗ
khi gõ thêm dấu thanh và không có gạch chân preedit. Gõ Sen, chế độ mặc định của Ngó Sen, hoạt động
theo cách này. Trang này giải thích app và toolkit cần làm gì để đáp ứng cách gõ đó,
Ngó Sen hiện xử lý ra sao và những chỗ upstream có thể sửa để bỏ được workaround. Trang này viết
cho người duy trì app, toolkit và các bộ gõ khác. Đây là bản dịch của
trang tiếng Anh, theo
docs/typing-without-preedit.md
trong repo.
Vì sao gõ tiếng Việt cần sửa chữ đã commit
Với Telex, kiểu gõ tiếng Việt phổ biến nhất, dấu được gõ sau chữ cần đổi:
tieengs thành tiếng qua từng phím. tieeng đã có trên màn hình
khi phím s tới và đổi ê thành ế. Có hai cách hiển thị:
- Preedit: bộ gõ giữ từ đang gõ trong một đoạn có gạch chân cho đến khi gõ xong từ. Mọi toolkit đều hỗ trợ cách này; các bộ gõ tiếng Trung, Nhật và Hàn hoạt động như vậy. Người dùng tiếng Việt không thích vì gạch chân nhấp nháy theo từng từ, còn các ô có gợi ý ngay trong dòng (thanh địa chỉ, ô tìm kiếm, ô nhắc tên trong chat) không thấy chữ cho đến khi gõ xong từ.
- Commit chữ rồi sửa ngay tại chỗ: bộ gõ commit ngay từng chữ; khi dấu làm thay đổi từ, bộ gõ xoá các ký tự trước con trỏ rồi commit từ đã sửa. Không có gạch chân; gợi ý cập nhật theo từng phím. Unikey đã làm như vậy trên Windows suốt hai mươi năm, và người dùng tiếng Việt mong đợi cách gõ này.
Cách thứ hai cần một thứ mà cách đầu không cần: một cách đáng tin để xoá chữ app đã nhận.
Các giao thức đã có gì
Cách yêu cầu app xoá chữ quanh con trỏ theo chuẩn đã có ở mọi lớp:
-
Wayland
text-input-v3:delete_surrounding_text(before, after)vàset_surrounding_textđể bộ gõ biết đang có chữ gì. -
fcitx5:
InputContext::deleteSurroundingText, với cờ khả năngSurroundingTextcho bộ gõ biết client có hỗ trợ hay không, cùng sự kiện forward key làm phương án dự phòng. - GTK 3 và 4:
gtk_im_context_delete_surroundingvàretrieve_surrounding. - Qt:
QInputMethodEventvới vùng cần thay thế. - IBus:
delete-surrounding-text, với cờ khả năng tương ứng.
XIM, giao thức của X11, hoàn toàn không có yêu cầu xoá chữ; client qua XIM chỉ có thể nhận sự kiện phím.
Vì vậy, bộ gõ tiếng Việt không cần giao thức mới. Client cần triển khai những gì các giao thức đã quy định và báo đúng khả năng của mình.
Ngó Sen hiện xoá chữ như thế nào
Ngó Sen chọn cách xoá dựa trên thông tin ô nhập báo về (frontend, cờ khả năng, surrounding text),
chỉ đối chiếu tên chương trình khi thông tin đó không đủ để phân biệt app. Các điều kiện kiểm
tra nằm trong
src/core/ngosen-app-quirks.cpp;
các test trong test/host-field-mapping.cpp,
test/ibus-dbus-forward-backspaces.cpp
và test/x11-xtest-replacement.cpp
kiểm chứng từng trường hợp.
Mặc định, Ngó Sen không yêu cầu app xoá chữ quanh con trỏ theo cách chuẩn nêu trên. Thay vào đó,
Ngó Sen gửi phím Backspace giả qua fcitx5 (forward key), vì mọi frontend trừ XIM đều chuyển được
các sự kiện phím này và client xử lý chúng đúng thứ tự với các commit theo sau. Ngó Sen chỉ dùng
deleteSurroundingText ở những nơi đã biết forward key bị bỏ hoặc đảo thứ tự. Khi
client báo surrounding text, Ngó Sen đọc để kiểm tra chữ trước con trỏ, không dùng để xoá.
| Cách xoá | Dùng cho | Chỗ gặp lỗi |
|---|---|---|
| Forward key Backspace | Mặc định: app GTK 3 và Qt qua các module fcitx5, app Chromium và Electron, app snap dùng module GTK của fcitx4, client của các frontend Wayland (KDE Plasma text-input; Sway và Hyprland input-method-v2), client XIM dưới Xwayland. | Ô có gợi ý tự điền ngay trong dòng: Backspace xoá phần gợi ý trước, nên gõ cowf trên thanh địa chỉ thành coờ. Các module IM của GTK 4 bỏ forward key. App GTK qua Wayland text-input thường xử lý commit trước sự kiện forward key Backspace được gửi ngay trước đó. LibreOffice xử lý Backspace bất đồng bộ, nên commit tiếp theo tới trước khi xoá. |
deleteSurroundingText |
Client gặp lỗi với forward key: app GTK 4 qua fcitx5-gtk4 hoặc IBus và LibreOffice. | Cần client báo surrounding text. |
| Chọn rồi ghi đè (Shift+Left, sau đó commit) | Trình duyệt dựa trên Chromium qua D-Bus, có thanh địa chỉ chọn phần gợi ý tự điền ngay sau chữ đã gõ; có thể tuỳ chọn dùng cho các ô soạn thảo của Facebook. | Terminal khớp cùng các cờ sẽ in Shift+Left ra thay vì chọn chữ. |
| Giả lập phím qua XTEST | Client XIM trong phiên X11: Wine, WordCraft, game SDL. | Cần X11. Dưới Xwayland, XTEST hỏi quyền người dùng trước, nên Ngó Sen gửi sự kiện phím tới cửa sổ của Wine thay cho XTEST. |
| Commit ký tự DEL | Terminal VTE dùng GTK 4 (Ptyxis, GNOME Console) không nhận forward key lẫn surrounding text, nhưng chuyển chữ đã commit tới shell, nơi DEL xoá như Backspace. | Chỉ dùng được với terminal. |
Mỗi dòng là một workaround cho cùng một thiếu sót: bộ gõ chưa thể tin cậy rằng mọi client Linux
đều triển khai delete_surrounding_text và xử lý đúng thứ tự với commit. Khi có thể
tin cậy như vậy, sẽ không cần giả lập phím nữa.
Hành vi app mà Ngó Sen phải đi vòng, và cách sửa từng chỗ
Đã đo trên các app được nêu tên, với version và desktop ghi trong issue hoặc PR Ngó Sen được dẫn link. “Cách sửa” là những gì client có thể thay đổi; không trường hợp nào cần đổi giao thức.
-
Trình duyệt dựa trên Chromium và app Electron (Chrome, Chromium, Edge, Brave,
Vivaldi, Opera; VS Code, Discord, Zalo và các app Electron khác) không báo surrounding text
cho fcitx5 qua module IM của GTK mà chúng dùng trên X11 và mặc định trên Wayland, nên Ngó Sen
không kiểm tra được chữ trước con trỏ và forward key Backspace. Thanh địa chỉ chọn phần gợi ý
tự điền trong dòng, ngay sau chữ đã gõ; forward key Backspace xoá phần đó. Nếu dùng Wayland
text-input thay cho module này (
--enable-wayland-ime), Chromium có gửi surrounding text; KWin chuyển tiếp thông tin này, còn Sway bỏ nó sau lần cập nhật đầu tiên (swaywm/sway#9191). Cách sửa: báo surrounding text qua module IM của GTK như khi dùng Wayland text-input. -
Firefox và các fork (Firefox ESR trên Debian, Zen, LibreWolf, Waterfox,
Floorp, Mullvad Browser, GNU IceCat) không đặt mục đích nhập URL cho thanh địa chỉ trên Linux, nên bộ gõ
không phân biệt được thanh địa chỉ với ô nhập chữ và phải đoán từ phần chữ sau con trỏ
(#161, #162,
#164). Thanh địa chỉ dùng chế độ nhập
mozAwesomebar, mà Firefox ánh xạ sang URL trên Windows và Android nhưng không ánh xạ trên GTK. Cách sửa: ánh xạ chế độ này sangGTK_INPUT_PURPOSE_URL. - GTK 3 và GTK 4 qua Wayland text-input, mà app GTK dùng mặc định dưới GNOME, xử lý commit trước sự kiện phím vừa tới trước đó, nên forward key Backspace rồi commit thường xoá chữ mới thay vì chữ cũ. Cách sửa: xử lý sự kiện phím và sự kiện text-input theo thứ tự nhận được.
- Các module IM của GTK 4 (fcitx5-gtk4 và module GTK 4 của IBus) bỏ sự kiện forward key và không chuyển tiếp mục đích nhập terminal mà VTE đã đặt, nên terminal GTK 4 không dùng được cả hai cách xoá. Cách sửa: chuyển forward key tới widget như các module GTK 3 và chuyển tiếp mục đích nhập.
-
SDL chỉ nhận commit và preedit từ bộ gõ, nên chỉ có thể xoá chữ trong game SDL
bằng sự kiện bấm phím thật, mà chỉ XTEST trên X11 mới gửi được. Cách sửa: triển khai
delete_surrounding_texttrong phần code text-input Wayland và IBus của SDL. - LibreOffice xử lý Backspace như phím tắt bất đồng bộ, nên commit gửi ngay sau đó lại tới trước khi xoá. Với giao diện Qt và KDE, mặc định trên KDE Plasma, Calc chỉ báo ô nhập khi nhận được phím, không báo khi một ô được chọn. Cách sửa: xử lý forward key Backspace đúng thứ tự với commit; báo focus khi chọn ô.
- Wine qua XIM dưới Xwayland xử lý sự kiện forward key sau một commit gửi tới muộn hơn (#158). Cách sửa: xử lý sự kiện phím X đúng thứ tự với commit XIM.
- Bản thân XIM không có yêu cầu xoá chữ. Client chỉ dùng XIM (Wine, WordCraft, các app Java cũ) nhận sự kiện bấm phím thật qua XTEST trên X11; Xwayland yêu cầu người dùng cấp quyền trước khi cho dùng XTEST. Cách sửa ở client: dùng phần xử lý IM gốc của toolkit thay cho XIM.
Các báo cáo gửi upstream
Ngó Sen đã gửi:
- Ubuntu 26.04, mutter: mất sự kiện phím dưới GNOME 50 (extension “forward-keys” trong README dùng để xử lý tạm lỗi này): https://bugs.launchpad.net/ubuntu/+source/mutter/+bug/2169784
- Thanh địa chỉ Firefox báo mục đích nhập văn bản tự do thay vì URL trên GTK; đã đo trên KDE Plasma với Firefox 157 (#215): https://bugzilla.mozilla.org/show_bug.cgi?id=2084459
- GTK xử lý commit text-input trước sự kiện phím đã nhận trước đó, nên forward key Backspace xoá chữ vừa commit; đã đo bằng một bộ gõ IBus dùng để test trên GNOME 51, GTK 3.24 và 4.24: https://gitlab.gnome.org/GNOME/gtk/-/issues/8431
Người khác đã gửi, Ngó Sen đã xác nhận:
- Sway bỏ surrounding text Chromium gửi qua Wayland text-input; đã xác nhận lại trên Sway 1.12 với Chromium 154 và fcitx5: https://github.com/swaywm/sway/issues/9191#issuecomment-6287333263
- Firefox qua Wayland text-input gửi surrounding text chậm một lần sửa, nên bộ gõ xoá chữ qua surrounding text sẽ làm sai từ, kể cả khi hai phím cách nhau 1,5 giây; đã xác nhận lại trên KDE Plasma với Firefox 157, kèm log giao thức: https://bugzilla.mozilla.org/show_bug.cgi?id=2080478#c2
- Chromium qua module IM của GTK không báo surrounding text; đã xác nhận lại trên KDE Plasma với Chromium 154 bằng các cờ khả năng của fcitx5 (#220): https://issues.chromium.org/issues/40146869#comment20
- Các module IM của GTK 4 không chuyển được forward key tới widget; đã xác nhận lại trên GNOME 51 với GTK 4.24.1, khi module GTK 4 của IBus bỏ 19 trong 19 sự kiện forward key Backspace còn module GTK 3 của IBus xử lý chúng (#225): https://gitlab.gnome.org/GNOME/gtk/-/issues/3465
- LibreOffice xử lý Backspace như phím tắt bất đồng bộ, nên chữ commit sau đó lại tới trước; đã xác nhận lại trong Writer 26.8.1 bằng một bộ gõ IBus dùng để test, 17 trong 19 lần sửa (#227): https://bugs.documentfoundation.org/show_bug.cgi?id=139807
- Giao diện Qt và KDE của LibreOffice không báo gì cho bộ gõ khi con trỏ di chuyển bằng click chuột; đã xác nhận lại trong Calc 26.8.1 trên KDE Plasma, kèm log giao thức Wayland (#227): https://bugs.documentfoundation.org/show_bug.cgi?id=173064
Người khác đã gửi:
-
SDL không xử lý
delete_surrounding_text: https://github.com/libsdl-org/SDL/issues/15987
Những hành vi khác nêu trên chưa được báo upstream. Báo cáo nên kèm cách tái hiện tối giản
không dùng Ngó Sen (một client fcitx5 hoặc text-input-v3 nhỏ có commit và xoá chữ),
version app, desktop và link tới trang này. Mỗi báo cáo có một issue trong repo Ngó Sen dẫn link
tới nó.
Cách giúp
- Người dùng Linux: mở cài đặt Ngó Sen, chạy “Kiểm tra máy tôi…” và báo những app vẫn gõ sai tại trang issue của Ngó Sen, kèm tên app, version và desktop đang dùng. Mỗi báo cáo sẽ bổ sung một trường hợp test hoặc tìm ra một lỗi nêu trên.
-
Người phát triển app và toolkit: trên Linux, báo surrounding text cho bộ gõ
và xử lý
delete_surrounding_text; đặt mục đích nhập URL cho thanh địa chỉ; chuyển forward key tới widget đang có focus. Các test nêu trên cho thấy bộ gõ mong đợi gì; chỉ cần một issue ghi tên app là bản sửa sẽ được test với Ngó Sen. - Các dự án bộ gõ khác: các bộ gõ tiếng Việt trên Linux đều gặp những vấn đề này, cũng như mọi bộ gõ sửa chữ đã commit. Xác nhận báo cáo upstream bằng cách tái hiện riêng của dự án mình có sức thuyết phục hơn báo cáo từ một dự án đơn lẻ.