From: Po Lu Date: Sun, 7 May 2023 03:09:56 +0000 (+0800) Subject: Update Android port X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=889b61b99918d1c6313d4f884de2e2cb3ab466c9;p=emacs.git Update Android port * java/org/gnu/emacs/EmacsInputConnection.java (requestCursorUpdates): * java/org/gnu/emacs/EmacsNative.java (requestCursorUpdates): * java/org/gnu/emacs/EmacsService.java (updateCursorAnchorInfo): New functions. * src/android.c (struct android_emacs_service) (android_init_emacs_service): Add new method. (android_update_cursor_anchor_info): New function. * src/androidfns.c (android_set_preeditarea): New function. * src/androidgui.h (enum android_ime_operation): New operation `REQUEST_CURSOR_UPDATES'. (struct android_ime_event): Document new meaning of `length'. * src/androidterm.c (android_request_cursor_updates): New function. (android_handle_ime_event): Handle new operations. (handle_one_android_event, android_draw_window_cursor): Update the preedit area if needed, like on X. (requestCursorUpdates): New function. * src/androidterm.h (struct android_output): New field `need_cursor_updates'. --- diff --git a/java/org/gnu/emacs/EmacsInputConnection.java b/java/org/gnu/emacs/EmacsInputConnection.java index d13b48288ce..21bbaca5d07 100644 --- a/java/org/gnu/emacs/EmacsInputConnection.java +++ b/java/org/gnu/emacs/EmacsInputConnection.java @@ -324,6 +324,17 @@ public final class EmacsInputConnection extends BaseInputConnection return this.deleteSurroundingText (beforeLength, afterLength); } + @Override + public boolean + requestCursorUpdates (int cursorUpdateMode) + { + if (EmacsService.DEBUG_IC) + Log.d (TAG, "requestCursorUpdates: " + cursorUpdateMode); + + EmacsNative.requestCursorUpdates (windowHandle, cursorUpdateMode); + return true; + } + /* Override functions which are not implemented. */ diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index 7d13ff99abb..e699dda9ad4 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -209,6 +209,7 @@ public final class EmacsNative ExtractedTextRequest req, int flags); public static native void requestSelectionUpdate (short window); + public static native void requestCursorUpdates (short window, int mode); /* Return the current value of the selection, or -1 upon diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 33436892caa..30ef71540a9 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -25,10 +25,12 @@ import java.io.UnsupportedEncodingException; import java.util.List; +import android.graphics.Matrix; import android.graphics.Point; import android.view.InputDevice; import android.view.KeyEvent; +import android.view.inputmethod.CursorAnchorInfo; import android.view.inputmethod.ExtractedText; import android.app.Notification; @@ -635,6 +637,36 @@ public final class EmacsService extends Service window.view.imManager.restartInput (window.view); } + public void + updateCursorAnchorInfo (EmacsWindow window, float x, + float y, float yBaseline, + float yBottom) + { + CursorAnchorInfo info; + CursorAnchorInfo.Builder builder; + Matrix matrix; + int[] offsets; + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) + return; + + offsets = new int[2]; + builder = new CursorAnchorInfo.Builder (); + matrix = new Matrix (window.view.getMatrix ()); + window.view.getLocationOnScreen (offsets); + matrix.postTranslate (offsets[0], offsets[1]); + builder.setMatrix (matrix); + builder.setInsertionMarkerLocation (x, y, yBaseline, yBottom, + 0); + info = builder.build (); + + if (DEBUG_IC) + Log.d (TAG, ("updateCursorAnchorInfo: " + x + " " + y + + " " + yBaseline + "-" + yBottom)); + + window.view.imManager.updateCursorAnchorInfo (window.view, info); + } + /* Open a content URI described by the bytes BYTES, a non-terminated string; make it writable if WRITABLE, and readable if READABLE. Truncate the file if TRUNCATE. diff --git a/src/android.c b/src/android.c index 129ad6b5767..8a41a7cdec5 100644 --- a/src/android.c +++ b/src/android.c @@ -113,6 +113,7 @@ struct android_emacs_service jmethodID query_battery; jmethodID display_toast; jmethodID update_extracted_text; + jmethodID update_cursor_anchor_info; }; struct android_emacs_pixmap @@ -2209,6 +2210,8 @@ android_init_emacs_service (void) FIND_METHOD (update_extracted_text, "updateExtractedText", "(Lorg/gnu/emacs/EmacsWindow;" "Landroid/view/inputmethod/ExtractedText;I)V"); + FIND_METHOD (update_cursor_anchor_info, "updateCursorAnchorInfo", + "(Lorg/gnu/emacs/EmacsWindow;FFFF)V"); #undef FIND_METHOD } @@ -6277,6 +6280,37 @@ android_update_extracted_text (android_window window, void *text, android_exception_check_1 (text); } +/* Report the position of the cursor to the input method connection on + WINDOW. + + X is the horizontal position of the end of the insertion marker. Y + is the top of the insertion marker. Y_BASELINE is the baseline of + the row containing the insertion marker, and Y_BOTTOM is the bottom + of the insertion marker. */ + +void +android_update_cursor_anchor_info (android_window window, float x, + float y, float y_baseline, + float y_bottom) +{ + jobject object; + jmethodID method; + + object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW); + method = service_class.update_cursor_anchor_info; + + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + emacs_service, + service_class.class, + method, + object, + (jfloat) x, + (jfloat) y, + (jfloat) y_baseline, + (jfloat) y_bottom); + android_exception_check (); +} + /* Window decoration management functions. */ diff --git a/src/androidfns.c b/src/androidfns.c index 3bd34edd5b9..60b0549e7d1 100644 --- a/src/androidfns.c +++ b/src/androidfns.c @@ -3000,6 +3000,34 @@ for more details about these values. */) make_fixnum (state.temperature)); } + + +/* Miscellaneous input method related stuff. */ + +/* Report X, Y, by the phys cursor width and height as the cursor + anchor rectangle for W's frame. */ + +void +android_set_preeditarea (struct window *w, int x, int y) +{ + struct frame *f; + + f = WINDOW_XFRAME (w); + + /* Convert the window coordinates to the frame's coordinate + space. */ + x = (WINDOW_TO_FRAME_PIXEL_X (w, x) + + WINDOW_LEFT_FRINGE_WIDTH (w) + + WINDOW_LEFT_MARGIN_WIDTH (w)); + y = WINDOW_TO_FRAME_PIXEL_Y (w, y); + + /* Note that calculating the baseline is too hard, so the bottom of + the cursor is used instead. */ + android_update_cursor_anchor_info (FRAME_ANDROID_WINDOW (f), x, + y, y + w->phys_cursor_height, + y + w->phys_cursor_height); +} + #endif diff --git a/src/androidgui.h b/src/androidgui.h index ddd8e9fcf72..6db25098398 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -432,6 +432,13 @@ enum android_ime_operation ANDROID_IME_START_BATCH_EDIT, ANDROID_IME_END_BATCH_EDIT, ANDROID_IME_REQUEST_SELECTION_UPDATE, + ANDROID_IME_REQUEST_CURSOR_UPDATES, + }; + +enum + { + ANDROID_CURSOR_UPDATE_IMMEDIATE = 1, + ANDROID_CURSOR_UPDATE_MONITOR = (1 << 1), }; struct android_ime_event @@ -452,7 +459,11 @@ struct android_ime_event indices, and may actually mean ``left'' and ``right''. */ ptrdiff_t start, end, position; - /* The number of characters in TEXT. */ + /* The number of characters in TEXT. + + If OPERATION is ANDROID_IME_REQUEST_CURSOR_UPDATES, then this is + actually the cursor update mode associated with that + operation. */ size_t length; /* TEXT is either NULL, or a pointer to LENGTH bytes of malloced @@ -620,6 +631,8 @@ extern void android_update_ic (android_window, ptrdiff_t, ptrdiff_t, extern void android_reset_ic (android_window, enum android_ic_mode); extern void android_update_extracted_text (android_window, void *, int); +extern void android_update_cursor_anchor_info (android_window, float, + float, float, float); extern int android_set_fullscreen (android_window, bool); enum android_cursor_shape diff --git a/src/androidterm.c b/src/androidterm.c index 8ba7fb6a798..6f7c06875ca 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -632,6 +632,40 @@ android_decode_utf16 (unsigned short *utf16, size_t n) return coding.dst_object; } +/* Handle a cursor update request for F from the input method. + MODE specifies whether or not an update should be sent immediately, + and whether or not they are needed in the future. + + If MODE & ANDROID_CURSOR_UPDATE_IMMEDIATE, report the position of + F's old selected window's phys cursor now. + + If MODE & ANDROID_CURSOR_UPDATE_MONITOR, set + `need_cursor_updates'. */ + +static void +android_request_cursor_updates (struct frame *f, int mode) +{ + struct window *w; + + if (mode & ANDROID_CURSOR_UPDATE_IMMEDIATE + && WINDOWP (WINDOW_LIVE_P (f->old_selected_window) + ? f->old_selected_window + : f->selected_window)) + { + /* Prefer the old selected window, as its selection is what was + reported to the IME previously. */ + + w = XWINDOW (WINDOW_LIVE_P (f->old_selected_window) + ? f->old_selected_window + : f->selected_window); + android_set_preeditarea (w, w->cursor.x, w->cursor.y); + } + + /* Now say whether or not updates are needed in the future. */ + FRAME_OUTPUT_DATA (f)->need_cursor_updates + = (mode & ANDROID_CURSOR_UPDATE_MONITOR); +} + /* Handle a single input method event EVENT, delivered to the frame F. @@ -705,6 +739,10 @@ android_handle_ime_event (union android_event *event, struct frame *f) case ANDROID_IME_REQUEST_SELECTION_UPDATE: request_point_update (f, event->ime.counter); break; + + case ANDROID_IME_REQUEST_CURSOR_UPDATES: + android_request_cursor_updates (f, event->ime.length); + break; } } @@ -724,6 +762,7 @@ handle_one_android_event (struct android_display_info *dpyinfo, double scroll_unit; int keysym; ptrdiff_t nchars, i; + struct window *w; /* It is okay for this to not resemble handle_one_xevent so much. Differences in event handling code are much less nasty than @@ -812,6 +851,12 @@ handle_one_android_event (struct android_display_info *dpyinfo, inev.ie.kind = MOVE_FRAME_EVENT; XSETFRAME (inev.ie.frame_or_window, f); } + + if (f && FRAME_OUTPUT_DATA (f)->need_cursor_updates) + { + w = XWINDOW (f->selected_window); + android_set_preeditarea (w, w->cursor.x, w->cursor.y); + } } goto OTHER; @@ -954,6 +999,16 @@ handle_one_android_event (struct android_display_info *dpyinfo, goto done_keysym; done_keysym: + + /* Now proceed to tell the input method the current position of + the cursor, if required. */ + + if (f && FRAME_OUTPUT_DATA (f)->need_cursor_updates) + { + w = XWINDOW (f->selected_window); + android_set_preeditarea (w, w->cursor.x, w->cursor.y); + } + goto OTHER; case ANDROID_FOCUS_IN: @@ -4321,6 +4376,10 @@ android_draw_window_cursor (struct window *w, struct glyph_row *glyph_row, int x, int y, enum text_cursor_kinds cursor_type, int cursor_width, bool on_p, bool active_p) { + struct frame *f; + + f = WINDOW_XFRAME (w); + if (on_p) { w->phys_cursor_type = cursor_type; @@ -4362,6 +4421,13 @@ android_draw_window_cursor (struct window *w, struct glyph_row *glyph_row, emacs_abort (); } } + + /* Now proceed to tell the input method the current position of + the cursor, if required. */ + + if (FRAME_OUTPUT_DATA (f)->need_cursor_updates + && w == XWINDOW (f->selected_window)) + android_set_preeditarea (w, x, y); } } @@ -5404,6 +5470,28 @@ NATIVE_NAME (requestSelectionUpdate) (JNIEnv *env, jobject object, android_write_event (&event); } +JNIEXPORT void JNICALL +NATIVE_NAME (requestCursorUpdates) (JNIEnv *env, jobject object, + jshort window, jint mode) +{ + JNI_STACK_ALIGNMENT_PROLOGUE; + + union android_event event; + + event.ime.type = ANDROID_INPUT_METHOD; + event.ime.serial = ++event_serial; + event.ime.window = window; + event.ime.operation = ANDROID_IME_REQUEST_CURSOR_UPDATES; + event.ime.start = 0; + event.ime.end = 0; + event.ime.length = mode; + event.ime.position = 0; + event.ime.text = NULL; + event.ime.counter = ++edit_counter; + + android_write_event (&event); +} + #ifdef __clang__ #pragma clang diagnostic pop #else diff --git a/src/androidterm.h b/src/androidterm.h index 9396d5fe315..e3738fb2192 100644 --- a/src/androidterm.h +++ b/src/androidterm.h @@ -231,6 +231,10 @@ struct android_output because the frame contents have been dirtied. */ bool_bf need_buffer_flip : 1; + /* Whether or not the input method should be notified every time the + position of this frame's selected window changes. */ + bool_bf need_cursor_updates : 1; + /* Relief GCs, colors etc. */ struct relief { struct android_gc *gc; @@ -383,6 +387,7 @@ extern struct android_display_info *x_display_list; extern void android_free_gcs (struct frame *); extern void android_default_font_parameter (struct frame *, Lisp_Object); +extern void android_set_preeditarea (struct window *, int, int); /* Defined in androidterm.c. */