From 189a91bfb699babd936dae48b96d71a332cac8d2 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Fri, 2 Jun 2023 13:31:40 +0800 Subject: [PATCH] Update Android port * java/org/gnu/emacs/EmacsInputConnection.java (EmacsInputConnection): Apply workarounds on Vivo devices as well. * src/android.c (sendKeyPress, sendKeyRelease): Clear counter. * src/androidgui.h (struct android_key_event): New field `counter'. * src/androidterm.c (handle_one_android_event): Generate barriers as appropriate. (JNICALL): Set `counter'. * src/frame.h (enum text_conversion_operation): * src/textconv.c (detect_conversion_events) (really_set_composing_text, handle_pending_conversion_events_1) (handle_pending_conversion_events, textconv_barrier): * src/textconv.h: Implement text conversion barriers and fix various typos. --- java/org/gnu/emacs/EmacsInputConnection.java | 11 ++-- src/android.c | 2 + src/androidgui.h | 4 ++ src/androidterm.c | 12 +++- src/frame.h | 1 + src/textconv.c | 60 ++++++++++++++++++-- src/textconv.h | 1 + 7 files changed, 79 insertions(+), 12 deletions(-) diff --git a/java/org/gnu/emacs/EmacsInputConnection.java b/java/org/gnu/emacs/EmacsInputConnection.java index eb6fd5f2763..9ced7cb7aaf 100644 --- a/java/org/gnu/emacs/EmacsInputConnection.java +++ b/java/org/gnu/emacs/EmacsInputConnection.java @@ -66,11 +66,12 @@ public final class EmacsInputConnection extends BaseInputConnection || Build.MANUFACTURER.equalsIgnoreCase ("Honor")) extractAbsoluteOffsets = syncAfterCommit = true; - /* The Samsung keyboard takes `selectionStart' at face value if - some text is returned, and also searches for words solely - within that text. However, when no text is returned, it falls - back to getTextAfterCursor and getTextBeforeCursor. */ - if (Build.MANUFACTURER.equalsIgnoreCase ("Samsung")) + /* The Samsung and Vivo keyboards take `selectionStart' at face + value if some text is returned, and also searches for words + solely within that text. However, when no text is returned, it + falls back to getTextAfterCursor and getTextBeforeCursor. */ + if (Build.MANUFACTURER.equalsIgnoreCase ("Samsung") + || Build.MANUFACTURER.equalsIgnoreCase ("Vivo")) extractAbsoluteOffsets = true; }; diff --git a/src/android.c b/src/android.c index 94587344eb5..e74d40a0cdb 100644 --- a/src/android.c +++ b/src/android.c @@ -2543,6 +2543,7 @@ NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object, event.xkey.state = state; event.xkey.keycode = keycode; event.xkey.unicode_char = unicode_char; + event.xkey.counter = 0; android_write_event (&event); return event_serial; @@ -2565,6 +2566,7 @@ NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object, event.xkey.state = state; event.xkey.keycode = keycode; event.xkey.unicode_char = unicode_char; + event.xkey.counter = 0; android_write_event (&event); return event_serial; diff --git a/src/androidgui.h b/src/androidgui.h index 02cc73809b9..9e604cdcb8c 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -277,6 +277,10 @@ struct android_key_event /* If this field is -1, then android_lookup_string should be called to retrieve the associated individual characters. */ unsigned int unicode_char; + + /* If this field is non-zero, a text conversion barrier should be + generated with its value as the counter. */ + unsigned long counter; }; typedef struct android_key_event android_key_pressed_event; diff --git a/src/androidterm.c b/src/androidterm.c index c302e3f2877..211faabf5c2 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -885,6 +885,11 @@ handle_one_android_event (struct android_display_info *dpyinfo, if (!f) goto OTHER; + if (event->xkey.counter) + /* This event was generated by `performEditorAction'. Make + sure it is processed before any subsequent edits. */ + textconv_barrier (f, event->xkey.counter); + wchar_t copy_buffer[129]; wchar_t *copy_bufptr = copy_buffer; int copy_bufsiz = 128 * sizeof (wchar_t); @@ -5178,7 +5183,10 @@ NATIVE_NAME (performEditorAction) (JNIEnv *env, jobject object, android_write_event (&event); - /* Finally, send the return key press. */ + /* Finally, send the return key press. `counter' is set; this means + that a text conversion barrier will be generated once the event + is read, which will cause subsequent edits to wait until the + edits associated with this key press complete. */ event.xkey.type = ANDROID_KEY_PRESS; event.xkey.serial = ++event_serial; @@ -5187,6 +5195,7 @@ NATIVE_NAME (performEditorAction) (JNIEnv *env, jobject object, event.xkey.state = 0; event.xkey.keycode = 66; event.xkey.unicode_char = 0; + event.xkey.counter = ++edit_counter; android_write_event (&event); } @@ -5234,6 +5243,7 @@ NATIVE_NAME (performContextMenuAction) (JNIEnv *env, jobject object, event.xkey.state = 0; event.xkey.keycode = 66; event.xkey.unicode_char = 0; + event.xkey.counter = ++edit_counter; android_write_event (&event); } diff --git a/src/frame.h b/src/frame.h index e2900d1c15b..41b4cd444f6 100644 --- a/src/frame.h +++ b/src/frame.h @@ -89,6 +89,7 @@ enum text_conversion_operation TEXTCONV_SET_POINT_AND_MARK, TEXTCONV_DELETE_SURROUNDING_TEXT, TEXTCONV_REQUEST_POINT_UPDATE, + TEXTCONV_BARRIER, }; /* Structure describing a single edit being performed by the input diff --git a/src/textconv.c b/src/textconv.c index d8166bcfd03..9003816e191 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -36,6 +36,7 @@ along with GNU Emacs. If not, see . */ #include "buffer.h" #include "syntax.h" #include "blockinput.h" +#include "keyboard.h" @@ -522,7 +523,11 @@ detect_conversion_events (void) FOR_EACH_FRAME (tail, frame) { - if (XFRAME (frame)->conversion.actions) + /* See if there's a pending edit on this frame. */ + if (XFRAME (frame)->conversion.actions + && ((XFRAME (frame)->conversion.actions->operation + != TEXTCONV_BARRIER) + || (kbd_fetch_ptr == kbd_store_ptr))) return true; } @@ -740,7 +745,7 @@ really_set_composing_text (struct frame *f, ptrdiff_t position, Fset_marker_insertion_type (f->conversion.compose_region_end, Qt); - start = position; + start = PT; } else { @@ -762,7 +767,7 @@ really_set_composing_text (struct frame *f, ptrdiff_t position, record_buffer_change (start, PT, Qnil); /* Now move point to an appropriate location. */ - if (position < 0) + if (position <= 0) { wanted = start; @@ -1198,6 +1203,19 @@ handle_pending_conversion_events_1 (struct frame *f, case TEXTCONV_REQUEST_POINT_UPDATE: really_request_point_update (f); break; + + case TEXTCONV_BARRIER: + if (kbd_fetch_ptr != kbd_store_ptr) + emacs_abort (); + + /* Once a barrier is hit, synchronize F's selected window's + `ephemeral_last_point' with its current point. The reason + for this is because otherwise a previous keyboard event may + have taken place without redisplay happening in between. */ + + if (w) + w->ephemeral_last_point = window_point (w); + break; } /* Signal success. */ @@ -1231,7 +1249,7 @@ handle_pending_conversion_events (void) static int inside; specpdl_ref count; ptrdiff_t last_point; - struct window *w; + struct window *w, *w1; handled = false; @@ -1242,8 +1260,6 @@ handle_pending_conversion_events (void) Vtext_conversion_edits = Qnil; inside++; - last_point = -1; - w = NULL; count = SPECPDL_INDEX (); record_unwind_protect_ptr (decrement_inside, &inside); @@ -1251,6 +1267,8 @@ handle_pending_conversion_events (void) FOR_EACH_FRAME (tail, frame) { f = XFRAME (frame); + last_point = -1; + w = NULL; /* Test if F has any outstanding conversion events. Then process them in bottom to up order. */ @@ -1283,6 +1301,13 @@ handle_pending_conversion_events (void) if (!action) break; + /* If action is a barrier event and the keyboard buffer is + not yet empty, break out of the loop. */ + + if (action->operation == TEXTCONV_BARRIER + && kbd_store_ptr != kbd_fetch_ptr) + break; + /* Unlink this action. */ next = action->next; f->conversion.actions = next; @@ -1515,6 +1540,29 @@ request_point_update (struct frame *f, unsigned long counter) input_pending = true; } +/* Request that text conversion on F pause until the keyboard buffer + becomes empty. + + Use this function to ensure that edits associated with a keyboard + event complete before the text conversion edits after the barrier + take place. */ + +void +textconv_barrier (struct frame *f, unsigned long counter) +{ + struct text_conversion_action *action, **last; + + action = xmalloc (sizeof *action); + action->operation = TEXTCONV_BARRIER; + action->data = Qnil; + action->next = NULL; + action->counter = counter; + for (last = &f->conversion.actions; *last; last = &(*last)->next) + ;; + *last = action; + input_pending = true; +} + /* Return N characters of text around point in F's old selected window. diff --git a/src/textconv.h b/src/textconv.h index e632a9dddcf..d4d0e9d7227 100644 --- a/src/textconv.h +++ b/src/textconv.h @@ -139,6 +139,7 @@ extern void textconv_set_point_and_mark (struct frame *, ptrdiff_t, extern void delete_surrounding_text (struct frame *, ptrdiff_t, ptrdiff_t, unsigned long); extern void request_point_update (struct frame *, unsigned long); +extern void textconv_barrier (struct frame *, unsigned long); extern char *get_extracted_text (struct frame *, ptrdiff_t, ptrdiff_t *, ptrdiff_t *, ptrdiff_t *, ptrdiff_t *, ptrdiff_t *); -- 2.39.2