-
Notifications
You must be signed in to change notification settings - Fork 4
Lỗi backspace sai khi câu đang tự động fill bằng suggestion của browser #27
Description
Mình sẽ lấy ví dụ Firefox (có thể tái hiện lên hầu hết các browser khác như Chrome,...).
Giải thích khá dài dòng, mình sẽ để video ở đây cho mọi người dễ hình dung
output_clip.mp4
Lỗi chỉ xảy ra ở chế độ gõ non-preedit (tất cả các chế độ gõ trừ chế độ số 1 và số 2. Số 2 cũng là kiểu non-preedit nhưng vấn đề hơi khác một chút, tạm thời để sau)
Cách hoạt động kiểu gõ non-preedit của bamboo/lotus:
- Ví dụ người dùng gõ từ
Hien - Gõ thêm chữ
rđể thêm dấu hỏi - Thực chất bộ gõ không thể trực tiếp thay đổi chữ
etrong từ trên thànhẻngay được mà phải xóa chữ e và cả từ đứng sau nó -> Với từ trên, bộ gõ sẽ tự động fake 2 lần phím backspace vào chương trình - Thêm từ
ẻvào và bổ sung lại các từ đứng sau nó -> Trong trường hợp này là thêm từẻn - Ra từ
Hiẻn - Tương tự nếu thêm chữ
eđể cho raêvào
Tuy nhiên, đã xảy ra một vấn đề, nếu từ đang gõ đang bị chèn bởi chữ suggest của browser (xem video trên cho dễ hiểu) thì khi backspace sẽ bị "thiếu" một backspace nữa (bởi 1 backspace đã dùng để xóa chữ suggest kia), gây ra vấn đề như trên.
Mình đã thử sửa bằng một idea đó là dùng một field mang tên currentWordNearCursor (tên biến đã rõ ràng các bạn có thể hiểu để làm gì). Biến này được update thông qua hook SetSurroundingText(...) của ibus để cập nhật từ mới mỗi khi bạn làm chuột chữ di chuyển
Line 65 in 4ccb3a1
| currentWordNearCursor string |
Lines 161 to 163 in 4ccb3a1
| func (e *IBusBambooEngine) SetSurroundingText(text dbus.Variant, cursorPos uint32, anchorPos uint32) *dbus.Error { | |
| var str = reflect.ValueOf(reflect.ValueOf(text.Value()).Index(2).Interface()).String() | |
| e.currentWordNearCursor = getLastWordFromSentence(str) |
Từ đây, chúng ta có thể thông qua hàm updatePreviousTextInBatch(oldText, newText string, isWordBreakRune bool) xử lý phần tự động backspace và thêm từ mới (chỉ cần tập trung tham số newText sẽ chứa từ đáng ra sẽ được output, như ví dụ trên qua hàm này sẽ là từ hiẻn nhưng chưa backspace và thêm từ mới). Chỉ cần một chút thuật toán đối chiếu với field currentWordNearCursor để check có bị dư một từ sau khi thêm backspace để thêm một extra backspace nữa là xong.
ibus-lotus/engine_backspace.go
Lines 191 to 228 in 4ccb3a1
| func (e *IBusBambooEngine) updatePreviousTextInBatch(oldText, newText string, isWordBreakRune bool) { | |
| offsetRunes, nBackSpace := e.getOffsetRunes(newText, oldText) | |
| if nBackSpace > 0 { | |
| e.SendBackSpace(nBackSpace) | |
| } | |
| var buffer = []string{string(offsetRunes)} | |
| if isWordBreakRune { | |
| e.preeditor.Reset() | |
| buffer = append(buffer, "") | |
| } | |
| // isDirty means containing runes that are not committed | |
| var isDirty = false | |
| for i := 0; i < len(keyPressChan); i++ { | |
| var keyEvents = <-keyPressChan | |
| var keyVal, keyCode, state = keyEvents[0], keyEvents[1], keyEvents[2] | |
| isValidKey := isValidState(state) && e.isValidKeyVal(keyVal) | |
| if isValidKey { | |
| var commitText, isWordBreakRune0 = e.getCommitText(keyVal, keyCode, state) | |
| buffer[len(buffer)-1] = commitText | |
| if isWordBreakRune0 { | |
| buffer = append(buffer, "") | |
| } | |
| isDirty = true | |
| } else { | |
| if isDirty { | |
| e.batchCommit(oldText, strings.Join(buffer, ""), nBackSpace, isWordBreakRune) | |
| buffer = []string{""} | |
| } | |
| e.ForwardKeyEvent(keyVal, keyCode, state) | |
| } | |
| } | |
| if isDirty { | |
| e.batchCommit(oldText, strings.Join(buffer, ""), nBackSpace, isWordBreakRune) | |
| return | |
| } | |
| log.Printf("Updating Previous Text %s ---> %s\n", oldText, newText) | |
| e.bsCommitText(offsetRunes) | |
| } |
Tuy nhiên một vấn đề nhỏ là hàm SendBackspace() sử dụng ForwardKeyEvent(...) của ibus để fake phím backspace (trong hầu hết các chế độ gõ), cách này khiến cho hook SetSurroundingText(...) không trigger được cho dù trỏ chữ có di chuyển (với backspace bình thường thì được), dẫn đến việc currentWordNearCursor cũng không được update đúng cách và không lấy được từ sau khi backspace (chưa thêm chữ mới).
ibus-lotus/engine_backspace.go
Lines 267 to 324 in 4ccb3a1
| func (e *IBusBambooEngine) SendBackSpace(n int) { | |
| // Gtk/Qt apps have a serious sync issue with fake backspaces | |
| // and normal string committing, so we'll not commit right now | |
| // but delay until all the sent backspaces got processed. | |
| var now = time.Now() | |
| var delta = 50*1000*1000 - (now.UnixNano() - e.lastCommitText) | |
| if delta > 0 { | |
| time.Sleep(time.Duration(delta) * time.Nanosecond) | |
| } | |
| if e.checkInputMode(config.XTestFakeKeyEventIM) { | |
| e.setFakeBackspace(int32(n)) | |
| var sleep = func() { | |
| var count = 0 | |
| for e.getFakeBackspace() > 0 && count < 10 { | |
| time.Sleep(5 * time.Millisecond) | |
| count++ | |
| } | |
| } | |
| log.Printf("Sendding %d backspace via XTestFakeKeyEvent\n", n) | |
| time.Sleep(10 * time.Millisecond) | |
| x11SendBackspace(n, 0) | |
| sleep() | |
| time.Sleep(time.Duration(n) * (10 + BACKSPACE_INTERVAL) * time.Millisecond) | |
| } else if e.checkInputMode(config.SurroundingTextIM) { | |
| time.Sleep(20 * time.Millisecond) | |
| log.Printf("Sendding %d backspace via SurroundingText\n", n) | |
| e.DeleteSurroundingText(-int32(n), uint32(n)) | |
| time.Sleep(20 * time.Millisecond) | |
| } else if e.checkInputMode(config.ForwardAsCommitIM) { | |
| time.Sleep(20 * time.Millisecond) | |
| log.Printf("Sendding %d backspace via forwardAsCommitIM\n", n) | |
| for i := 0; i < n; i++ { | |
| e.ForwardKeyEvent(IBusBackSpace, XkBackspace-8, 0) | |
| e.ForwardKeyEvent(IBusBackSpace, XkBackspace-8, IBusReleaseMask) | |
| } | |
| time.Sleep(time.Duration(n) * (20 + BACKSPACE_INTERVAL) * time.Millisecond) | |
| } else if e.checkInputMode(config.ShiftLeftForwardingIM) { | |
| time.Sleep(30 * time.Millisecond) | |
| log.Printf("Sendding %d Shift+Left via shiftLeftForwardingIM\n", n) | |
| for i := 0; i < n; i++ { | |
| e.ForwardKeyEvent(IBusLeft, XkLeft-8, IBusShiftMask) | |
| e.ForwardKeyEvent(IBusLeft, XkLeft-8, IBusReleaseMask) | |
| } | |
| time.Sleep(time.Duration(n) * (30 + BACKSPACE_INTERVAL) * time.Millisecond) | |
| } else if e.checkInputMode(config.BackspaceForwardingIM) { | |
| time.Sleep(30 * time.Millisecond) | |
| log.Printf("Sendding %d backspace via backspaceForwardingIM\n", n) | |
| for i := 0; i < n; i++ { | |
| e.ForwardKeyEvent(IBusBackSpace, XkBackspace-8, 0) | |
| e.ForwardKeyEvent(IBusBackSpace, XkBackspace-8, IBusReleaseMask) | |
| } | |
| time.Sleep(time.Duration(n) * (30 + BACKSPACE_INTERVAL) * time.Millisecond) | |
| } else { | |
| fmt.Println("There's something wrong with wmClasses") | |
| } | |
| } |
Không biết có ai có solution để giúp mình vấn đề này không. Chỉ cần có một cách để trigger hook SetSurroundingText(...) mà không phụ thuộc vào điều kiện của ibus là được. Cám ơn mn bỏ thời gian để đọc 🥇Nếu được thì giúp mình một PR có ích nhé. (đang dang dở tại commit 4ccb3a1)