diff options
| author | Po Lu | 2023-02-15 12:23:03 +0800 |
|---|---|---|
| committer | Po Lu | 2023-02-15 12:23:03 +0800 |
| commit | a158c1d5b964fda36f752998cef076d581dc4df4 (patch) | |
| tree | b9c7a22a7259dbd876be0e047304a338ef05e334 /src | |
| parent | 5a7855e84abd56e55b456aef4a43ae7623e76899 (diff) | |
| download | emacs-a158c1d5b964fda36f752998cef076d581dc4df4.tar.gz emacs-a158c1d5b964fda36f752998cef076d581dc4df4.zip | |
Update Android port
* configure.ac (HAVE_TEXT_CONVERSION): Define on Android.
* doc/emacs/input.texi (On-Screen Keyboards): Document ``text
conversion'' slightly.
* doc/lispref/commands.texi (Misc Events): Document new
`text-conversion' event.
* java/org/gnu/emacs/EmacsContextMenu.java (display): Use
`syncRunnable'.
* java/org/gnu/emacs/EmacsDialog.java (display): Likewise.
* java/org/gnu/emacs/EmacsEditable.java: Delete file.
* java/org/gnu/emacs/EmacsInputConnection.java
(EmacsInputConnection): Reimplement from scratch.
* java/org/gnu/emacs/EmacsNative.java (EmacsNative): Add new
functions.
* java/org/gnu/emacs/EmacsService.java (EmacsService, getEmacsView)
(getLocationOnScreen, sync, getClipboardManager, restartEmacs):
Use syncRunnable.
(syncRunnable): New function.
(updateIC, resetIC): New functions.
* java/org/gnu/emacs/EmacsView.java (EmacsView): New field
`inputConnection' and `icMode'.
(onCreateInputConnection): Update accordingly.
(setICMode, getICMode): New functions.
* lisp/bindings.el (global-map): Ignore text conversion events.
* src/alloc.c (mark_frame): Mark text conversion data.
* src/android.c (struct android_emacs_service): New fields
`update_ic' and `reset_ic'.
(event_serial): Export.
(android_query_sem): New function.
(android_init_events): Initialize new semaphore.
(android_write_event): Export.
(android_select): Check for UI thread code.
(setEmacsParams, android_init_emacs_service): Initialize new
methods.
(android_check_query, android_begin_query, android_end_query)
(android_run_in_emacs_thread):
(android_update_ic, android_reset_ic): New functions for
managing synchronous queries from one thread to another.
* src/android.h: Export new functions.
* src/androidgui.h (enum android_event_type): Add input method
events.
(enum android_ime_operation, struct android_ime_event)
(union android_event, enum android_ic_mode): New structs and
enums.
* src/androidterm.c (android_window_to_frame): Allow DPYINFO to
be NULL.
(android_decode_utf16, android_handle_ime_event)
(handle_one_android_event, android_sync_edit)
(android_copy_java_string, beginBatchEdit, endBatchEdit)
(commitCompletion, deleteSurroundingText, finishComposingText)
(getSelectedtext, getTextAfterCursor, getTextBeforeCursor)
(setComposingText, setComposingRegion, setSelection, getSelection)
(performEditorAction, getExtractedText): New functions.
(struct android_conversion_query_context):
(android_perform_conversion_query):
(android_text_to_string):
(struct android_get_selection_context):
(android_get_selection):
(struct android_get_extracted_text_context):
(android_get_extracted_text):
(struct android_extracted_text_request_class):
(struct android_extracted_text_class):
(android_update_selection):
(android_reset_conversion):
(android_set_point):
(android_compose_region_changed):
(android_notify_conversion):
(text_conversion_interface): New functions and structures.
(android_term_init): Initialize text conversion.
* src/coding.c (syms_of_coding): Define Qutf_16le on Android.
* src/frame.c (make_frame): Clear conversion data.
(delete_frame): Reset conversion state.
* src/frame.h (enum text_conversion_operation)
(struct text_conversion_action, struct text_conversion_state)
(GCALIGNED_STRUCT): Update structures.
* src/keyboard.c (read_char, readable_events, kbd_buffer_get_event)
(syms_of_keyboard): Handle text conversion events.
* src/lisp.h:
* src/process.c: Fix includes.
* src/textconv.c (enum textconv_batch_edit_flags, textconv_query)
(reset_frame_state, detect_conversion_events)
(restore_selected_window, really_commit_text)
(really_finish_composing_text, really_set_composing_text)
(really_set_composing_region, really_delete_surrounding_text)
(really_set_point, complete_edit)
(handle_pending_conversion_events_1)
(handle_pending_conversion_events, start_batch_edit)
(end_batch_edit, commit_text, finish_composing_text)
(set_composing_text, set_composing_region, textconv_set_point)
(delete_surrounding_text, get_extracted_text)
(report_selected_window_change, report_point_change)
(register_texconv_interface): New functions.
* src/textconv.h (struct textconv_interface)
(TEXTCONV_SKIP_CONVERSION_REGION): Update prototype.
* src/xdisp.c (mark_window_display_accurate_1):
* src/xfns.c (xic_string_conversion_callback):
* src/xterm.c (init_xterm): Adjust accordingly.
Diffstat (limited to 'src')
| -rw-r--r-- | src/alloc.c | 14 | ||||
| -rw-r--r-- | src/android.c | 295 | ||||
| -rw-r--r-- | src/android.h | 10 | ||||
| -rw-r--r-- | src/androidgui.h | 60 | ||||
| -rw-r--r-- | src/androidterm.c | 1037 | ||||
| -rw-r--r-- | src/coding.c | 2 | ||||
| -rw-r--r-- | src/frame.c | 17 | ||||
| -rw-r--r-- | src/frame.h | 62 | ||||
| -rw-r--r-- | src/keyboard.c | 50 | ||||
| -rw-r--r-- | src/lisp.h | 3 | ||||
| -rw-r--r-- | src/process.c | 1 | ||||
| -rw-r--r-- | src/textconv.c | 906 | ||||
| -rw-r--r-- | src/textconv.h | 38 | ||||
| -rw-r--r-- | src/xdisp.c | 28 | ||||
| -rw-r--r-- | src/xfns.c | 2 | ||||
| -rw-r--r-- | src/xterm.c | 2 |
16 files changed, 2490 insertions, 37 deletions
diff --git a/src/alloc.c b/src/alloc.c index bc43f22005d..6d8658e7bb0 100644 --- a/src/alloc.c +++ b/src/alloc.c | |||
| @@ -6939,6 +6939,11 @@ static void | |||
| 6939 | mark_frame (struct Lisp_Vector *ptr) | 6939 | mark_frame (struct Lisp_Vector *ptr) |
| 6940 | { | 6940 | { |
| 6941 | struct frame *f = (struct frame *) ptr; | 6941 | struct frame *f = (struct frame *) ptr; |
| 6942 | #ifdef HAVE_TEXT_CONVERSION | ||
| 6943 | struct text_conversion_action *tem; | ||
| 6944 | #endif | ||
| 6945 | |||
| 6946 | |||
| 6942 | mark_vectorlike (&ptr->header); | 6947 | mark_vectorlike (&ptr->header); |
| 6943 | mark_face_cache (f->face_cache); | 6948 | mark_face_cache (f->face_cache); |
| 6944 | #ifdef HAVE_WINDOW_SYSTEM | 6949 | #ifdef HAVE_WINDOW_SYSTEM |
| @@ -6950,6 +6955,15 @@ mark_frame (struct Lisp_Vector *ptr) | |||
| 6950 | mark_vectorlike (&font->header); | 6955 | mark_vectorlike (&font->header); |
| 6951 | } | 6956 | } |
| 6952 | #endif | 6957 | #endif |
| 6958 | |||
| 6959 | #ifdef HAVE_TEXT_CONVERSION | ||
| 6960 | mark_object (f->conversion.compose_region_start); | ||
| 6961 | mark_object (f->conversion.compose_region_end); | ||
| 6962 | mark_object (f->conversion.compose_region_overlay); | ||
| 6963 | |||
| 6964 | for (tem = f->conversion.actions; tem; tem = tem->next) | ||
| 6965 | mark_object (tem->data); | ||
| 6966 | #endif | ||
| 6953 | } | 6967 | } |
| 6954 | 6968 | ||
| 6955 | static void | 6969 | static void |
diff --git a/src/android.c b/src/android.c index 479eb9b10d4..8f446224dab 100644 --- a/src/android.c +++ b/src/android.c | |||
| @@ -98,6 +98,8 @@ struct android_emacs_service | |||
| 98 | jmethodID sync; | 98 | jmethodID sync; |
| 99 | jmethodID browse_url; | 99 | jmethodID browse_url; |
| 100 | jmethodID restart_emacs; | 100 | jmethodID restart_emacs; |
| 101 | jmethodID update_ic; | ||
| 102 | jmethodID reset_ic; | ||
| 101 | }; | 103 | }; |
| 102 | 104 | ||
| 103 | struct android_emacs_pixmap | 105 | struct android_emacs_pixmap |
| @@ -207,7 +209,7 @@ static struct android_emacs_window window_class; | |||
| 207 | 209 | ||
| 208 | /* The last event serial used. This is a 32 bit value, but it is | 210 | /* The last event serial used. This is a 32 bit value, but it is |
| 209 | stored in unsigned long to be consistent with X. */ | 211 | stored in unsigned long to be consistent with X. */ |
| 210 | static unsigned int event_serial; | 212 | unsigned int event_serial; |
| 211 | 213 | ||
| 212 | /* Unused pointer used to control compiler optimizations. */ | 214 | /* Unused pointer used to control compiler optimizations. */ |
| 213 | void *unused_pointer; | 215 | void *unused_pointer; |
| @@ -408,6 +410,10 @@ android_handle_sigusr1 (int sig, siginfo_t *siginfo, void *arg) | |||
| 408 | 410 | ||
| 409 | #endif | 411 | #endif |
| 410 | 412 | ||
| 413 | /* Semaphore used to indicate completion of a query. | ||
| 414 | This should ideally be defined further down. */ | ||
| 415 | static sem_t android_query_sem; | ||
| 416 | |||
| 411 | /* Set up the global event queue by initializing the mutex and two | 417 | /* Set up the global event queue by initializing the mutex and two |
| 412 | condition variables, and the linked list of events. This must be | 418 | condition variables, and the linked list of events. This must be |
| 413 | called before starting the Emacs thread. Also, initialize the | 419 | called before starting the Emacs thread. Also, initialize the |
| @@ -438,6 +444,7 @@ android_init_events (void) | |||
| 438 | 444 | ||
| 439 | sem_init (&android_pselect_sem, 0, 0); | 445 | sem_init (&android_pselect_sem, 0, 0); |
| 440 | sem_init (&android_pselect_start_sem, 0, 0); | 446 | sem_init (&android_pselect_start_sem, 0, 0); |
| 447 | sem_init (&android_query_sem, 0, 0); | ||
| 441 | 448 | ||
| 442 | event_queue.events.next = &event_queue.events; | 449 | event_queue.events.next = &event_queue.events; |
| 443 | event_queue.events.last = &event_queue.events; | 450 | event_queue.events.last = &event_queue.events; |
| @@ -538,7 +545,7 @@ android_next_event (union android_event *event_return) | |||
| 538 | pthread_mutex_unlock (&event_queue.mutex); | 545 | pthread_mutex_unlock (&event_queue.mutex); |
| 539 | } | 546 | } |
| 540 | 547 | ||
| 541 | static void | 548 | void |
| 542 | android_write_event (union android_event *event) | 549 | android_write_event (union android_event *event) |
| 543 | { | 550 | { |
| 544 | struct android_event_container *container; | 551 | struct android_event_container *container; |
| @@ -576,6 +583,10 @@ android_select (int nfds, fd_set *readfds, fd_set *writefds, | |||
| 576 | static char byte; | 583 | static char byte; |
| 577 | #endif | 584 | #endif |
| 578 | 585 | ||
| 586 | /* Check for and run anything the UI thread wants to run on the main | ||
| 587 | thread. */ | ||
| 588 | android_check_query (); | ||
| 589 | |||
| 579 | pthread_mutex_lock (&event_queue.mutex); | 590 | pthread_mutex_lock (&event_queue.mutex); |
| 580 | 591 | ||
| 581 | if (event_queue.num_events) | 592 | if (event_queue.num_events) |
| @@ -634,6 +645,10 @@ android_select (int nfds, fd_set *readfds, fd_set *writefds, | |||
| 634 | if (nfds_return < 0) | 645 | if (nfds_return < 0) |
| 635 | errno = EINTR; | 646 | errno = EINTR; |
| 636 | 647 | ||
| 648 | /* Now check for and run anything the UI thread wants to run in the | ||
| 649 | main thread. */ | ||
| 650 | android_check_query (); | ||
| 651 | |||
| 637 | return nfds_return; | 652 | return nfds_return; |
| 638 | } | 653 | } |
| 639 | 654 | ||
| @@ -1431,7 +1446,7 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object, | |||
| 1431 | 1446 | ||
| 1432 | /* This may be called from multiple threads. setEmacsParams should | 1447 | /* This may be called from multiple threads. setEmacsParams should |
| 1433 | only ever be called once. */ | 1448 | only ever be called once. */ |
| 1434 | if (__atomic_fetch_add (&emacs_initialized, -1, __ATOMIC_RELAXED)) | 1449 | if (__atomic_fetch_add (&emacs_initialized, -1, __ATOMIC_SEQ_CST)) |
| 1435 | { | 1450 | { |
| 1436 | ANDROID_THROW (env, "java/lang/IllegalArgumentException", | 1451 | ANDROID_THROW (env, "java/lang/IllegalArgumentException", |
| 1437 | "Emacs was already initialized!"); | 1452 | "Emacs was already initialized!"); |
| @@ -1705,6 +1720,10 @@ android_init_emacs_service (void) | |||
| 1705 | FIND_METHOD (browse_url, "browseUrl", "(Ljava/lang/String;)" | 1720 | FIND_METHOD (browse_url, "browseUrl", "(Ljava/lang/String;)" |
| 1706 | "Ljava/lang/String;"); | 1721 | "Ljava/lang/String;"); |
| 1707 | FIND_METHOD (restart_emacs, "restartEmacs", "()V"); | 1722 | FIND_METHOD (restart_emacs, "restartEmacs", "()V"); |
| 1723 | FIND_METHOD (update_ic, "updateIC", | ||
| 1724 | "(Lorg/gnu/emacs/EmacsWindow;IIII)V"); | ||
| 1725 | FIND_METHOD (reset_ic, "resetIC", | ||
| 1726 | "(Lorg/gnu/emacs/EmacsWindow;I)V"); | ||
| 1708 | #undef FIND_METHOD | 1727 | #undef FIND_METHOD |
| 1709 | } | 1728 | } |
| 1710 | 1729 | ||
| @@ -1834,7 +1853,7 @@ android_init_emacs_window (void) | |||
| 1834 | #undef FIND_METHOD | 1853 | #undef FIND_METHOD |
| 1835 | } | 1854 | } |
| 1836 | 1855 | ||
| 1837 | extern JNIEXPORT void JNICALL | 1856 | JNIEXPORT void JNICALL |
| 1838 | NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv, | 1857 | NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv, |
| 1839 | jobject dump_file_object, jint api_level) | 1858 | jobject dump_file_object, jint api_level) |
| 1840 | { | 1859 | { |
| @@ -1928,19 +1947,19 @@ NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv, | |||
| 1928 | emacs_abort (); | 1947 | emacs_abort (); |
| 1929 | } | 1948 | } |
| 1930 | 1949 | ||
| 1931 | extern JNIEXPORT void JNICALL | 1950 | JNIEXPORT void JNICALL |
| 1932 | NATIVE_NAME (emacsAbort) (JNIEnv *env, jobject object) | 1951 | NATIVE_NAME (emacsAbort) (JNIEnv *env, jobject object) |
| 1933 | { | 1952 | { |
| 1934 | emacs_abort (); | 1953 | emacs_abort (); |
| 1935 | } | 1954 | } |
| 1936 | 1955 | ||
| 1937 | extern JNIEXPORT void JNICALL | 1956 | JNIEXPORT void JNICALL |
| 1938 | NATIVE_NAME (quit) (JNIEnv *env, jobject object) | 1957 | NATIVE_NAME (quit) (JNIEnv *env, jobject object) |
| 1939 | { | 1958 | { |
| 1940 | Vquit_flag = Qt; | 1959 | Vquit_flag = Qt; |
| 1941 | } | 1960 | } |
| 1942 | 1961 | ||
| 1943 | extern JNIEXPORT jlong JNICALL | 1962 | JNIEXPORT jlong JNICALL |
| 1944 | NATIVE_NAME (sendConfigureNotify) (JNIEnv *env, jobject object, | 1963 | NATIVE_NAME (sendConfigureNotify) (JNIEnv *env, jobject object, |
| 1945 | jshort window, jlong time, | 1964 | jshort window, jlong time, |
| 1946 | jint x, jint y, jint width, | 1965 | jint x, jint y, jint width, |
| @@ -1961,7 +1980,7 @@ NATIVE_NAME (sendConfigureNotify) (JNIEnv *env, jobject object, | |||
| 1961 | return event_serial; | 1980 | return event_serial; |
| 1962 | } | 1981 | } |
| 1963 | 1982 | ||
| 1964 | extern JNIEXPORT jlong JNICALL | 1983 | JNIEXPORT jlong JNICALL |
| 1965 | NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object, | 1984 | NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object, |
| 1966 | jshort window, jlong time, | 1985 | jshort window, jlong time, |
| 1967 | jint state, jint keycode, | 1986 | jint state, jint keycode, |
| @@ -1981,7 +2000,7 @@ NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object, | |||
| 1981 | return event_serial; | 2000 | return event_serial; |
| 1982 | } | 2001 | } |
| 1983 | 2002 | ||
| 1984 | extern JNIEXPORT jlong JNICALL | 2003 | JNIEXPORT jlong JNICALL |
| 1985 | NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object, | 2004 | NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object, |
| 1986 | jshort window, jlong time, | 2005 | jshort window, jlong time, |
| 1987 | jint state, jint keycode, | 2006 | jint state, jint keycode, |
| @@ -2001,7 +2020,7 @@ NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object, | |||
| 2001 | return event_serial; | 2020 | return event_serial; |
| 2002 | } | 2021 | } |
| 2003 | 2022 | ||
| 2004 | extern JNIEXPORT jlong JNICALL | 2023 | JNIEXPORT jlong JNICALL |
| 2005 | NATIVE_NAME (sendFocusIn) (JNIEnv *env, jobject object, | 2024 | NATIVE_NAME (sendFocusIn) (JNIEnv *env, jobject object, |
| 2006 | jshort window, jlong time) | 2025 | jshort window, jlong time) |
| 2007 | { | 2026 | { |
| @@ -2016,7 +2035,7 @@ NATIVE_NAME (sendFocusIn) (JNIEnv *env, jobject object, | |||
| 2016 | return event_serial; | 2035 | return event_serial; |
| 2017 | } | 2036 | } |
| 2018 | 2037 | ||
| 2019 | extern JNIEXPORT jlong JNICALL | 2038 | JNIEXPORT jlong JNICALL |
| 2020 | NATIVE_NAME (sendFocusOut) (JNIEnv *env, jobject object, | 2039 | NATIVE_NAME (sendFocusOut) (JNIEnv *env, jobject object, |
| 2021 | jshort window, jlong time) | 2040 | jshort window, jlong time) |
| 2022 | { | 2041 | { |
| @@ -2031,7 +2050,7 @@ NATIVE_NAME (sendFocusOut) (JNIEnv *env, jobject object, | |||
| 2031 | return ++event_serial; | 2050 | return ++event_serial; |
| 2032 | } | 2051 | } |
| 2033 | 2052 | ||
| 2034 | extern JNIEXPORT jlong JNICALL | 2053 | JNIEXPORT jlong JNICALL |
| 2035 | NATIVE_NAME (sendWindowAction) (JNIEnv *env, jobject object, | 2054 | NATIVE_NAME (sendWindowAction) (JNIEnv *env, jobject object, |
| 2036 | jshort window, jint action) | 2055 | jshort window, jint action) |
| 2037 | { | 2056 | { |
| @@ -2046,7 +2065,7 @@ NATIVE_NAME (sendWindowAction) (JNIEnv *env, jobject object, | |||
| 2046 | return event_serial; | 2065 | return event_serial; |
| 2047 | } | 2066 | } |
| 2048 | 2067 | ||
| 2049 | extern JNIEXPORT jlong JNICALL | 2068 | JNIEXPORT jlong JNICALL |
| 2050 | NATIVE_NAME (sendEnterNotify) (JNIEnv *env, jobject object, | 2069 | NATIVE_NAME (sendEnterNotify) (JNIEnv *env, jobject object, |
| 2051 | jshort window, jint x, jint y, | 2070 | jshort window, jint x, jint y, |
| 2052 | jlong time) | 2071 | jlong time) |
| @@ -2064,7 +2083,7 @@ NATIVE_NAME (sendEnterNotify) (JNIEnv *env, jobject object, | |||
| 2064 | return event_serial; | 2083 | return event_serial; |
| 2065 | } | 2084 | } |
| 2066 | 2085 | ||
| 2067 | extern JNIEXPORT jlong JNICALL | 2086 | JNIEXPORT jlong JNICALL |
| 2068 | NATIVE_NAME (sendLeaveNotify) (JNIEnv *env, jobject object, | 2087 | NATIVE_NAME (sendLeaveNotify) (JNIEnv *env, jobject object, |
| 2069 | jshort window, jint x, jint y, | 2088 | jshort window, jint x, jint y, |
| 2070 | jlong time) | 2089 | jlong time) |
| @@ -2082,7 +2101,7 @@ NATIVE_NAME (sendLeaveNotify) (JNIEnv *env, jobject object, | |||
| 2082 | return event_serial; | 2101 | return event_serial; |
| 2083 | } | 2102 | } |
| 2084 | 2103 | ||
| 2085 | extern JNIEXPORT jlong JNICALL | 2104 | JNIEXPORT jlong JNICALL |
| 2086 | NATIVE_NAME (sendMotionNotify) (JNIEnv *env, jobject object, | 2105 | NATIVE_NAME (sendMotionNotify) (JNIEnv *env, jobject object, |
| 2087 | jshort window, jint x, jint y, | 2106 | jshort window, jint x, jint y, |
| 2088 | jlong time) | 2107 | jlong time) |
| @@ -2100,7 +2119,7 @@ NATIVE_NAME (sendMotionNotify) (JNIEnv *env, jobject object, | |||
| 2100 | return event_serial; | 2119 | return event_serial; |
| 2101 | } | 2120 | } |
| 2102 | 2121 | ||
| 2103 | extern JNIEXPORT jlong JNICALL | 2122 | JNIEXPORT jlong JNICALL |
| 2104 | NATIVE_NAME (sendButtonPress) (JNIEnv *env, jobject object, | 2123 | NATIVE_NAME (sendButtonPress) (JNIEnv *env, jobject object, |
| 2105 | jshort window, jint x, jint y, | 2124 | jshort window, jint x, jint y, |
| 2106 | jlong time, jint state, | 2125 | jlong time, jint state, |
| @@ -2121,7 +2140,7 @@ NATIVE_NAME (sendButtonPress) (JNIEnv *env, jobject object, | |||
| 2121 | return event_serial; | 2140 | return event_serial; |
| 2122 | } | 2141 | } |
| 2123 | 2142 | ||
| 2124 | extern JNIEXPORT jlong JNICALL | 2143 | JNIEXPORT jlong JNICALL |
| 2125 | NATIVE_NAME (sendButtonRelease) (JNIEnv *env, jobject object, | 2144 | NATIVE_NAME (sendButtonRelease) (JNIEnv *env, jobject object, |
| 2126 | jshort window, jint x, jint y, | 2145 | jshort window, jint x, jint y, |
| 2127 | jlong time, jint state, | 2146 | jlong time, jint state, |
| @@ -2142,7 +2161,7 @@ NATIVE_NAME (sendButtonRelease) (JNIEnv *env, jobject object, | |||
| 2142 | return event_serial; | 2161 | return event_serial; |
| 2143 | } | 2162 | } |
| 2144 | 2163 | ||
| 2145 | extern JNIEXPORT jlong JNICALL | 2164 | JNIEXPORT jlong JNICALL |
| 2146 | NATIVE_NAME (sendTouchDown) (JNIEnv *env, jobject object, | 2165 | NATIVE_NAME (sendTouchDown) (JNIEnv *env, jobject object, |
| 2147 | jshort window, jint x, jint y, | 2166 | jshort window, jint x, jint y, |
| 2148 | jlong time, jint pointer_id) | 2167 | jlong time, jint pointer_id) |
| @@ -2161,7 +2180,7 @@ NATIVE_NAME (sendTouchDown) (JNIEnv *env, jobject object, | |||
| 2161 | return event_serial; | 2180 | return event_serial; |
| 2162 | } | 2181 | } |
| 2163 | 2182 | ||
| 2164 | extern JNIEXPORT jlong JNICALL | 2183 | JNIEXPORT jlong JNICALL |
| 2165 | NATIVE_NAME (sendTouchUp) (JNIEnv *env, jobject object, | 2184 | NATIVE_NAME (sendTouchUp) (JNIEnv *env, jobject object, |
| 2166 | jshort window, jint x, jint y, | 2185 | jshort window, jint x, jint y, |
| 2167 | jlong time, jint pointer_id) | 2186 | jlong time, jint pointer_id) |
| @@ -2180,7 +2199,7 @@ NATIVE_NAME (sendTouchUp) (JNIEnv *env, jobject object, | |||
| 2180 | return event_serial; | 2199 | return event_serial; |
| 2181 | } | 2200 | } |
| 2182 | 2201 | ||
| 2183 | extern JNIEXPORT jlong JNICALL | 2202 | JNIEXPORT jlong JNICALL |
| 2184 | NATIVE_NAME (sendTouchMove) (JNIEnv *env, jobject object, | 2203 | NATIVE_NAME (sendTouchMove) (JNIEnv *env, jobject object, |
| 2185 | jshort window, jint x, jint y, | 2204 | jshort window, jint x, jint y, |
| 2186 | jlong time, jint pointer_id) | 2205 | jlong time, jint pointer_id) |
| @@ -2199,7 +2218,7 @@ NATIVE_NAME (sendTouchMove) (JNIEnv *env, jobject object, | |||
| 2199 | return event_serial; | 2218 | return event_serial; |
| 2200 | } | 2219 | } |
| 2201 | 2220 | ||
| 2202 | extern JNIEXPORT jlong JNICALL | 2221 | JNIEXPORT jlong JNICALL |
| 2203 | NATIVE_NAME (sendWheel) (JNIEnv *env, jobject object, | 2222 | NATIVE_NAME (sendWheel) (JNIEnv *env, jobject object, |
| 2204 | jshort window, jint x, jint y, | 2223 | jshort window, jint x, jint y, |
| 2205 | jlong time, jint state, | 2224 | jlong time, jint state, |
| @@ -2221,7 +2240,7 @@ NATIVE_NAME (sendWheel) (JNIEnv *env, jobject object, | |||
| 2221 | return event_serial; | 2240 | return event_serial; |
| 2222 | } | 2241 | } |
| 2223 | 2242 | ||
| 2224 | extern JNIEXPORT jlong JNICALL | 2243 | JNIEXPORT jlong JNICALL |
| 2225 | NATIVE_NAME (sendIconified) (JNIEnv *env, jobject object, | 2244 | NATIVE_NAME (sendIconified) (JNIEnv *env, jobject object, |
| 2226 | jshort window) | 2245 | jshort window) |
| 2227 | { | 2246 | { |
| @@ -2235,7 +2254,7 @@ NATIVE_NAME (sendIconified) (JNIEnv *env, jobject object, | |||
| 2235 | return event_serial; | 2254 | return event_serial; |
| 2236 | } | 2255 | } |
| 2237 | 2256 | ||
| 2238 | extern JNIEXPORT jlong JNICALL | 2257 | JNIEXPORT jlong JNICALL |
| 2239 | NATIVE_NAME (sendDeiconified) (JNIEnv *env, jobject object, | 2258 | NATIVE_NAME (sendDeiconified) (JNIEnv *env, jobject object, |
| 2240 | jshort window) | 2259 | jshort window) |
| 2241 | { | 2260 | { |
| @@ -2249,7 +2268,7 @@ NATIVE_NAME (sendDeiconified) (JNIEnv *env, jobject object, | |||
| 2249 | return event_serial; | 2268 | return event_serial; |
| 2250 | } | 2269 | } |
| 2251 | 2270 | ||
| 2252 | extern JNIEXPORT jlong JNICALL | 2271 | JNIEXPORT jlong JNICALL |
| 2253 | NATIVE_NAME (sendContextMenu) (JNIEnv *env, jobject object, | 2272 | NATIVE_NAME (sendContextMenu) (JNIEnv *env, jobject object, |
| 2254 | jshort window, jint menu_event_id) | 2273 | jshort window, jint menu_event_id) |
| 2255 | { | 2274 | { |
| @@ -2264,7 +2283,7 @@ NATIVE_NAME (sendContextMenu) (JNIEnv *env, jobject object, | |||
| 2264 | return event_serial; | 2283 | return event_serial; |
| 2265 | } | 2284 | } |
| 2266 | 2285 | ||
| 2267 | extern JNIEXPORT jlong JNICALL | 2286 | JNIEXPORT jlong JNICALL |
| 2268 | NATIVE_NAME (sendExpose) (JNIEnv *env, jobject object, | 2287 | NATIVE_NAME (sendExpose) (JNIEnv *env, jobject object, |
| 2269 | jshort window, jint x, jint y, | 2288 | jshort window, jint x, jint y, |
| 2270 | jint width, jint height) | 2289 | jint width, jint height) |
| @@ -2283,6 +2302,23 @@ NATIVE_NAME (sendExpose) (JNIEnv *env, jobject object, | |||
| 2283 | return event_serial; | 2302 | return event_serial; |
| 2284 | } | 2303 | } |
| 2285 | 2304 | ||
| 2305 | /* Forward declarations of deadlock prevention functions. */ | ||
| 2306 | |||
| 2307 | static void android_begin_query (void); | ||
| 2308 | static void android_end_query (void); | ||
| 2309 | |||
| 2310 | JNIEXPORT void JNICALL | ||
| 2311 | NATIVE_NAME (beginSynchronous) (JNIEnv *env, jobject object) | ||
| 2312 | { | ||
| 2313 | android_begin_query (); | ||
| 2314 | } | ||
| 2315 | |||
| 2316 | JNIEXPORT void JNICALL | ||
| 2317 | NATIVE_NAME (endSynchronous) (JNIEnv *env, jobject object) | ||
| 2318 | { | ||
| 2319 | android_end_query (); | ||
| 2320 | } | ||
| 2321 | |||
| 2286 | #ifdef __clang__ | 2322 | #ifdef __clang__ |
| 2287 | #pragma clang diagnostic pop | 2323 | #pragma clang diagnostic pop |
| 2288 | #else | 2324 | #else |
| @@ -5155,6 +5191,215 @@ android_get_current_api_level (void) | |||
| 5155 | 5191 | ||
| 5156 | 5192 | ||
| 5157 | 5193 | ||
| 5194 | /* Whether or not a query is currently being made. */ | ||
| 5195 | static bool android_servicing_query; | ||
| 5196 | |||
| 5197 | /* Function that is waiting to be run in the Emacs thread. */ | ||
| 5198 | static void (*android_query_function) (void *); | ||
| 5199 | |||
| 5200 | /* Context for that function. */ | ||
| 5201 | static void *android_query_context; | ||
| 5202 | |||
| 5203 | /* Deadlock protection. The UI thread and the Emacs thread must | ||
| 5204 | sometimes make synchronous queries to each other, which are | ||
| 5205 | normally answered inside each thread's respective event loop. | ||
| 5206 | Deadlocks can happen when both threads simultaneously make such | ||
| 5207 | synchronous queries and block waiting for each others responses. | ||
| 5208 | |||
| 5209 | The Emacs thread can be interrupted to service any queries made by | ||
| 5210 | the UI thread, but is not possible the other way around. | ||
| 5211 | |||
| 5212 | To avoid such deadlocks, an atomic counter is provided. This | ||
| 5213 | counter is incremented every time a query starts, and is set to | ||
| 5214 | zerp every time one ends. If the UI thread tries to make a query | ||
| 5215 | and sees that the counter is non-zero, it simply returns so that | ||
| 5216 | its event loop can proceed to perform and respond to the query. If | ||
| 5217 | the Emacs thread sees the same thing, then it stops to service all | ||
| 5218 | queries being made by the input method, then proceeds to make its | ||
| 5219 | query. */ | ||
| 5220 | |||
| 5221 | /* Run any function that the UI thread has asked to run, and then | ||
| 5222 | signal its completion. */ | ||
| 5223 | |||
| 5224 | void | ||
| 5225 | android_check_query (void) | ||
| 5226 | { | ||
| 5227 | void (*proc) (void *); | ||
| 5228 | void *closure; | ||
| 5229 | |||
| 5230 | if (!__atomic_load_n (&android_servicing_query, __ATOMIC_SEQ_CST)) | ||
| 5231 | return; | ||
| 5232 | |||
| 5233 | /* First, load the procedure and closure. */ | ||
| 5234 | __atomic_load (&android_query_context, &closure, __ATOMIC_SEQ_CST); | ||
| 5235 | __atomic_load (&android_query_function, &proc, __ATOMIC_SEQ_CST); | ||
| 5236 | |||
| 5237 | if (!proc) | ||
| 5238 | return; | ||
| 5239 | |||
| 5240 | proc (closure); | ||
| 5241 | |||
| 5242 | /* Finish the query. */ | ||
| 5243 | __atomic_store_n (&android_query_context, NULL, __ATOMIC_SEQ_CST); | ||
| 5244 | __atomic_store_n (&android_query_function, NULL, __ATOMIC_SEQ_CST); | ||
| 5245 | __atomic_clear (&android_servicing_query, __ATOMIC_SEQ_CST); | ||
| 5246 | |||
| 5247 | /* Signal completion. */ | ||
| 5248 | sem_post (&android_query_sem); | ||
| 5249 | } | ||
| 5250 | |||
| 5251 | /* Notice that the Emacs thread will start blocking waiting for a | ||
| 5252 | response from the UI thread. Process any pending queries from the | ||
| 5253 | UI thread. | ||
| 5254 | |||
| 5255 | This function may be called from Java. */ | ||
| 5256 | |||
| 5257 | static void | ||
| 5258 | android_begin_query (void) | ||
| 5259 | { | ||
| 5260 | if (__atomic_test_and_set (&android_servicing_query, | ||
| 5261 | __ATOMIC_SEQ_CST)) | ||
| 5262 | { | ||
| 5263 | /* Answer the query that is currently being made. */ | ||
| 5264 | assert (android_query_function != NULL); | ||
| 5265 | android_check_query (); | ||
| 5266 | |||
| 5267 | /* Wait for that query to complete. */ | ||
| 5268 | while (__atomic_load_n (&android_servicing_query, | ||
| 5269 | __ATOMIC_SEQ_CST)) | ||
| 5270 | ;; | ||
| 5271 | } | ||
| 5272 | } | ||
| 5273 | |||
| 5274 | /* Notice that a query has stopped. This function may be called from | ||
| 5275 | Java. */ | ||
| 5276 | |||
| 5277 | static void | ||
| 5278 | android_end_query (void) | ||
| 5279 | { | ||
| 5280 | __atomic_clear (&android_servicing_query, __ATOMIC_SEQ_CST); | ||
| 5281 | } | ||
| 5282 | |||
| 5283 | /* Synchronously ask the Emacs thread to run the specified PROC with | ||
| 5284 | the given CLOSURE. Return if this fails, or once PROC is run. | ||
| 5285 | |||
| 5286 | PROC may be run from inside maybe_quit. | ||
| 5287 | |||
| 5288 | It is not okay to run Lisp code which signals or performs non | ||
| 5289 | trivial tasks inside PROC. | ||
| 5290 | |||
| 5291 | Return 1 if the Emacs thread is currently waiting for the UI thread | ||
| 5292 | to respond and PROC could not be run, or 0 otherwise. */ | ||
| 5293 | |||
| 5294 | int | ||
| 5295 | android_run_in_emacs_thread (void (*proc) (void *), void *closure) | ||
| 5296 | { | ||
| 5297 | union android_event event; | ||
| 5298 | |||
| 5299 | event.xaction.type = ANDROID_WINDOW_ACTION; | ||
| 5300 | event.xaction.serial = ++event_serial; | ||
| 5301 | event.xaction.window = 0; | ||
| 5302 | event.xaction.action = 0; | ||
| 5303 | |||
| 5304 | /* Set android_query_function and android_query_context. */ | ||
| 5305 | __atomic_store_n (&android_query_context, closure, __ATOMIC_SEQ_CST); | ||
| 5306 | __atomic_store_n (&android_query_function, proc, __ATOMIC_SEQ_CST); | ||
| 5307 | |||
| 5308 | /* Don't allow deadlocks to happen; make sure the Emacs thread is | ||
| 5309 | not waiting for something to be done. */ | ||
| 5310 | |||
| 5311 | if (__atomic_test_and_set (&android_servicing_query, | ||
| 5312 | __ATOMIC_SEQ_CST)) | ||
| 5313 | { | ||
| 5314 | __atomic_store_n (&android_query_context, NULL, | ||
| 5315 | __ATOMIC_SEQ_CST); | ||
| 5316 | __atomic_store_n (&android_query_function, NULL, | ||
| 5317 | __ATOMIC_SEQ_CST); | ||
| 5318 | |||
| 5319 | return 1; | ||
| 5320 | } | ||
| 5321 | |||
| 5322 | /* Send a dummy event. `android_check_query' will be called inside | ||
| 5323 | wait_reading_process_output after the event arrives. | ||
| 5324 | |||
| 5325 | Otherwise, android_select will call android_check_thread the next | ||
| 5326 | time it is entered. */ | ||
| 5327 | android_write_event (&event); | ||
| 5328 | |||
| 5329 | /* Start waiting for the function to be executed. */ | ||
| 5330 | while (sem_wait (&android_query_sem) < 0) | ||
| 5331 | ;; | ||
| 5332 | |||
| 5333 | return 0; | ||
| 5334 | } | ||
| 5335 | |||
| 5336 | |||
| 5337 | |||
| 5338 | /* Input method related functions. */ | ||
| 5339 | |||
| 5340 | /* Change WINDOW's active selection to the characters between | ||
| 5341 | SELECTION_START and SELECTION_END. | ||
| 5342 | |||
| 5343 | Also, update the composing region to COMPOSING_REGION_START and | ||
| 5344 | COMPOSING_REGION_END. | ||
| 5345 | |||
| 5346 | If any value cannot fit in jint, then the behavior of the input | ||
| 5347 | method is undefined. */ | ||
| 5348 | |||
| 5349 | void | ||
| 5350 | android_update_ic (android_window window, ptrdiff_t selection_start, | ||
| 5351 | ptrdiff_t selection_end, ptrdiff_t composing_region_start, | ||
| 5352 | ptrdiff_t composing_region_end) | ||
| 5353 | { | ||
| 5354 | jobject object; | ||
| 5355 | |||
| 5356 | object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW); | ||
| 5357 | |||
| 5358 | (*android_java_env)->CallVoidMethod (android_java_env, | ||
| 5359 | emacs_service, | ||
| 5360 | service_class.update_ic, | ||
| 5361 | object, | ||
| 5362 | (jint) selection_start, | ||
| 5363 | (jint) selection_end, | ||
| 5364 | (jint) composing_region_start, | ||
| 5365 | (jint) composing_region_end); | ||
| 5366 | android_exception_check (); | ||
| 5367 | } | ||
| 5368 | |||
| 5369 | /* Reinitialize any ongoing input method connection on WINDOW. | ||
| 5370 | |||
| 5371 | Any input method that is connected to WINDOW will invalidate its | ||
| 5372 | cache of the buffer contents. | ||
| 5373 | |||
| 5374 | MODE controls certain aspects of the input method's behavior: | ||
| 5375 | |||
| 5376 | - If MODE is ANDROID_IC_MODE_NULL, the input method will be | ||
| 5377 | deactivated, and an ASCII only keyboard will be displayed | ||
| 5378 | instead. | ||
| 5379 | |||
| 5380 | - If MODE is ANDROID_IC_MODE_ACTION, the input method will | ||
| 5381 | edit text normally, but send ``return'' as a key event. | ||
| 5382 | This is useful inside the mini buffer. | ||
| 5383 | |||
| 5384 | - If MODE is ANDROID_IC_MODE_TEXT, the input method is free | ||
| 5385 | to behave however it wants. */ | ||
| 5386 | |||
| 5387 | void | ||
| 5388 | android_reset_ic (android_window window, enum android_ic_mode mode) | ||
| 5389 | { | ||
| 5390 | jobject object; | ||
| 5391 | |||
| 5392 | object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW); | ||
| 5393 | |||
| 5394 | (*android_java_env)->CallVoidMethod (android_java_env, | ||
| 5395 | emacs_service, | ||
| 5396 | service_class.reset_ic, | ||
| 5397 | object, (jint) mode); | ||
| 5398 | android_exception_check (); | ||
| 5399 | } | ||
| 5400 | |||
| 5401 | |||
| 5402 | |||
| 5158 | #else /* ANDROID_STUBIFY */ | 5403 | #else /* ANDROID_STUBIFY */ |
| 5159 | 5404 | ||
| 5160 | /* X emulation functions for Android. */ | 5405 | /* X emulation functions for Android. */ |
diff --git a/src/android.h b/src/android.h index 9006f5f34c5..ec4fa33dfc3 100644 --- a/src/android.h +++ b/src/android.h | |||
| @@ -108,6 +108,16 @@ extern void android_closedir (struct android_dir *); | |||
| 108 | 108 | ||
| 109 | extern Lisp_Object android_browse_url (Lisp_Object); | 109 | extern Lisp_Object android_browse_url (Lisp_Object); |
| 110 | 110 | ||
| 111 | |||
| 112 | |||
| 113 | /* Event loop functions. */ | ||
| 114 | |||
| 115 | extern void android_check_query (void); | ||
| 116 | extern int android_run_in_emacs_thread (void (*) (void *), void *); | ||
| 117 | extern void android_write_event (union android_event *); | ||
| 118 | |||
| 119 | extern unsigned int event_serial; | ||
| 120 | |||
| 111 | #endif | 121 | #endif |
| 112 | 122 | ||
| 113 | /* JNI functions should not be built when Emacs is stubbed out for the | 123 | /* JNI functions should not be built when Emacs is stubbed out for the |
diff --git a/src/androidgui.h b/src/androidgui.h index 1f0d34e67aa..25dc6754fff 100644 --- a/src/androidgui.h +++ b/src/androidgui.h | |||
| @@ -235,6 +235,7 @@ enum android_event_type | |||
| 235 | ANDROID_DEICONIFIED, | 235 | ANDROID_DEICONIFIED, |
| 236 | ANDROID_CONTEXT_MENU, | 236 | ANDROID_CONTEXT_MENU, |
| 237 | ANDROID_EXPOSE, | 237 | ANDROID_EXPOSE, |
| 238 | ANDROID_INPUT_METHOD, | ||
| 238 | }; | 239 | }; |
| 239 | 240 | ||
| 240 | struct android_any_event | 241 | struct android_any_event |
| @@ -419,6 +420,52 @@ struct android_menu_event | |||
| 419 | int menu_event_id; | 420 | int menu_event_id; |
| 420 | }; | 421 | }; |
| 421 | 422 | ||
| 423 | enum android_ime_operation | ||
| 424 | { | ||
| 425 | ANDROID_IME_COMMIT_TEXT, | ||
| 426 | ANDROID_IME_DELETE_SURROUNDING_TEXT, | ||
| 427 | ANDROID_IME_FINISH_COMPOSING_TEXT, | ||
| 428 | ANDROID_IME_SET_COMPOSING_TEXT, | ||
| 429 | ANDROID_IME_SET_COMPOSING_REGION, | ||
| 430 | ANDROID_IME_SET_POINT, | ||
| 431 | ANDROID_IME_START_BATCH_EDIT, | ||
| 432 | ANDROID_IME_END_BATCH_EDIT, | ||
| 433 | }; | ||
| 434 | |||
| 435 | struct android_ime_event | ||
| 436 | { | ||
| 437 | /* Type of the event. */ | ||
| 438 | enum android_event_type type; | ||
| 439 | |||
| 440 | /* The event serial. */ | ||
| 441 | unsigned long serial; | ||
| 442 | |||
| 443 | /* The associated window. */ | ||
| 444 | android_window window; | ||
| 445 | |||
| 446 | /* What operation is being performed. */ | ||
| 447 | enum android_ime_operation operation; | ||
| 448 | |||
| 449 | /* The details of the operation. START and END provide buffer | ||
| 450 | indices, and may actually mean ``left'' and ``right''. */ | ||
| 451 | ptrdiff_t start, end, position; | ||
| 452 | |||
| 453 | /* The number of characters in TEXT. */ | ||
| 454 | size_t length; | ||
| 455 | |||
| 456 | /* TEXT is either NULL, or a pointer to LENGTH bytes of malloced | ||
| 457 | UTF-16 encoded text that must be decoded by Emacs. | ||
| 458 | |||
| 459 | POSITION is where point should end up after the text is | ||
| 460 | committed, relative to TEXT. If POSITION is less than 0, it is | ||
| 461 | relative to TEXT's start; otherwise, it is relative to its | ||
| 462 | end. */ | ||
| 463 | unsigned short *text; | ||
| 464 | |||
| 465 | /* Value to set the counter to after the operation completes. */ | ||
| 466 | unsigned long counter; | ||
| 467 | }; | ||
| 468 | |||
| 422 | union android_event | 469 | union android_event |
| 423 | { | 470 | { |
| 424 | enum android_event_type type; | 471 | enum android_event_type type; |
| @@ -447,6 +494,9 @@ union android_event | |||
| 447 | 494 | ||
| 448 | /* This is only used to transmit selected menu items. */ | 495 | /* This is only used to transmit selected menu items. */ |
| 449 | struct android_menu_event menu; | 496 | struct android_menu_event menu; |
| 497 | |||
| 498 | /* This is used to dispatch input method editing requests. */ | ||
| 499 | struct android_ime_event ime; | ||
| 450 | }; | 500 | }; |
| 451 | 501 | ||
| 452 | enum | 502 | enum |
| @@ -463,6 +513,13 @@ enum android_lookup_status | |||
| 463 | ANDROID_LOOKUP_BOTH, | 513 | ANDROID_LOOKUP_BOTH, |
| 464 | }; | 514 | }; |
| 465 | 515 | ||
| 516 | enum android_ic_mode | ||
| 517 | { | ||
| 518 | ANDROID_IC_MODE_NULL = 0, | ||
| 519 | ANDROID_IC_MODE_ACTION = 1, | ||
| 520 | ANDROID_IC_MODE_TEXT = 2, | ||
| 521 | }; | ||
| 522 | |||
| 466 | extern int android_pending (void); | 523 | extern int android_pending (void); |
| 467 | extern void android_next_event (union android_event *); | 524 | extern void android_next_event (union android_event *); |
| 468 | 525 | ||
| @@ -554,6 +611,9 @@ extern void android_sync (void); | |||
| 554 | extern int android_wc_lookup_string (android_key_pressed_event *, | 611 | extern int android_wc_lookup_string (android_key_pressed_event *, |
| 555 | wchar_t *, int, int *, | 612 | wchar_t *, int, int *, |
| 556 | enum android_lookup_status *); | 613 | enum android_lookup_status *); |
| 614 | extern void android_update_ic (android_window, ptrdiff_t, ptrdiff_t, | ||
| 615 | ptrdiff_t, ptrdiff_t); | ||
| 616 | extern void android_reset_ic (android_window, enum android_ic_mode); | ||
| 557 | 617 | ||
| 558 | #endif | 618 | #endif |
| 559 | 619 | ||
diff --git a/src/androidterm.c b/src/androidterm.c index a57d5623c66..a44bae954da 100644 --- a/src/androidterm.c +++ b/src/androidterm.c | |||
| @@ -20,6 +20,9 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 20 | #include <config.h> | 20 | #include <config.h> |
| 21 | #include <stdio.h> | 21 | #include <stdio.h> |
| 22 | #include <math.h> | 22 | #include <math.h> |
| 23 | #include <stdlib.h> | ||
| 24 | #include <assert.h> | ||
| 25 | #include <semaphore.h> | ||
| 23 | 26 | ||
| 24 | #include "lisp.h" | 27 | #include "lisp.h" |
| 25 | #include "androidterm.h" | 28 | #include "androidterm.h" |
| @@ -28,6 +31,8 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 28 | #include "android.h" | 31 | #include "android.h" |
| 29 | #include "buffer.h" | 32 | #include "buffer.h" |
| 30 | #include "window.h" | 33 | #include "window.h" |
| 34 | #include "textconv.h" | ||
| 35 | #include "coding.h" | ||
| 31 | 36 | ||
| 32 | /* This is a chain of structures for all the X displays currently in | 37 | /* This is a chain of structures for all the X displays currently in |
| 33 | use. */ | 38 | use. */ |
| @@ -59,6 +64,12 @@ enum | |||
| 59 | ANDROID_EVENT_DROP, | 64 | ANDROID_EVENT_DROP, |
| 60 | }; | 65 | }; |
| 61 | 66 | ||
| 67 | /* Find the frame whose window has the identifier WDESC. | ||
| 68 | |||
| 69 | This is like x_window_to_frame in xterm.c, except that DPYINFO may | ||
| 70 | be NULL, as there is only at most one Android display, and is only | ||
| 71 | specified in order to stay consistent with X. */ | ||
| 72 | |||
| 62 | static struct frame * | 73 | static struct frame * |
| 63 | android_window_to_frame (struct android_display_info *dpyinfo, | 74 | android_window_to_frame (struct android_display_info *dpyinfo, |
| 64 | android_window wdesc) | 75 | android_window wdesc) |
| @@ -73,7 +84,7 @@ android_window_to_frame (struct android_display_info *dpyinfo, | |||
| 73 | { | 84 | { |
| 74 | f = XFRAME (frame); | 85 | f = XFRAME (frame); |
| 75 | 86 | ||
| 76 | if (!FRAME_ANDROID_P (f) || FRAME_DISPLAY_INFO (f) != dpyinfo) | 87 | if (!FRAME_ANDROID_P (f)) |
| 77 | continue; | 88 | continue; |
| 78 | 89 | ||
| 79 | if (FRAME_ANDROID_WINDOW (f) == wdesc) | 90 | if (FRAME_ANDROID_WINDOW (f) == wdesc) |
| @@ -527,6 +538,103 @@ android_find_tool (struct frame *f, int pointer_id) | |||
| 527 | return NULL; | 538 | return NULL; |
| 528 | } | 539 | } |
| 529 | 540 | ||
| 541 | /* Decode STRING, an array of N little endian UTF-16 characters, into | ||
| 542 | a Lisp string. Return Qnil if the string is too large, and the | ||
| 543 | encoded string otherwise. */ | ||
| 544 | |||
| 545 | static Lisp_Object | ||
| 546 | android_decode_utf16 (unsigned short *utf16, size_t n) | ||
| 547 | { | ||
| 548 | struct coding_system coding; | ||
| 549 | ptrdiff_t size; | ||
| 550 | |||
| 551 | if (INT_MULTIPLY_WRAPV (n, sizeof *utf16, &size)) | ||
| 552 | return Qnil; | ||
| 553 | |||
| 554 | /* Set up the coding system. Decoding a UTF-16 string (with no BOM) | ||
| 555 | should not signal. */ | ||
| 556 | |||
| 557 | memset (&coding, 0, sizeof coding); | ||
| 558 | |||
| 559 | setup_coding_system (Qutf_16le, &coding); | ||
| 560 | coding.source = (const unsigned char *) utf16; | ||
| 561 | decode_coding_object (&coding, Qnil, 0, 0, size, | ||
| 562 | size, Qt); | ||
| 563 | |||
| 564 | return coding.dst_object; | ||
| 565 | } | ||
| 566 | |||
| 567 | /* Handle a single input method event EVENT, delivered to the frame | ||
| 568 | F. | ||
| 569 | |||
| 570 | Perform the text conversion action specified inside. */ | ||
| 571 | |||
| 572 | static void | ||
| 573 | android_handle_ime_event (union android_event *event, struct frame *f) | ||
| 574 | { | ||
| 575 | Lisp_Object text; | ||
| 576 | |||
| 577 | /* First, decode the text if necessary. */ | ||
| 578 | |||
| 579 | switch (event->ime.operation) | ||
| 580 | { | ||
| 581 | case ANDROID_IME_COMMIT_TEXT: | ||
| 582 | case ANDROID_IME_FINISH_COMPOSING_TEXT: | ||
| 583 | case ANDROID_IME_SET_COMPOSING_TEXT: | ||
| 584 | text = android_decode_utf16 (event->ime.text, | ||
| 585 | event->ime.length); | ||
| 586 | xfree (event->ime.text); | ||
| 587 | break; | ||
| 588 | |||
| 589 | default: | ||
| 590 | break; | ||
| 591 | } | ||
| 592 | |||
| 593 | /* Finally, perform the appropriate conversion action. */ | ||
| 594 | |||
| 595 | switch (event->ime.operation) | ||
| 596 | { | ||
| 597 | case ANDROID_IME_COMMIT_TEXT: | ||
| 598 | commit_text (f, text, event->ime.position, | ||
| 599 | event->ime.counter); | ||
| 600 | break; | ||
| 601 | |||
| 602 | case ANDROID_IME_DELETE_SURROUNDING_TEXT: | ||
| 603 | delete_surrounding_text (f, event->ime.start, | ||
| 604 | event->ime.end, | ||
| 605 | event->ime.counter); | ||
| 606 | break; | ||
| 607 | |||
| 608 | case ANDROID_IME_FINISH_COMPOSING_TEXT: | ||
| 609 | finish_composing_text (f, event->ime.counter); | ||
| 610 | break; | ||
| 611 | |||
| 612 | case ANDROID_IME_SET_COMPOSING_TEXT: | ||
| 613 | set_composing_text (f, text, event->ime.position, | ||
| 614 | event->ime.counter); | ||
| 615 | break; | ||
| 616 | |||
| 617 | case ANDROID_IME_SET_COMPOSING_REGION: | ||
| 618 | set_composing_region (f, event->ime.start, | ||
| 619 | event->ime.end, | ||
| 620 | event->ime.counter); | ||
| 621 | break; | ||
| 622 | |||
| 623 | case ANDROID_IME_SET_POINT: | ||
| 624 | textconv_set_point (f, event->ime.position, | ||
| 625 | event->ime.counter); | ||
| 626 | break; | ||
| 627 | |||
| 628 | case ANDROID_IME_START_BATCH_EDIT: | ||
| 629 | start_batch_edit (f, event->ime.counter); | ||
| 630 | break; | ||
| 631 | |||
| 632 | case ANDROID_IME_END_BATCH_EDIT: | ||
| 633 | end_batch_edit (f, event->ime.counter); | ||
| 634 | break; | ||
| 635 | } | ||
| 636 | } | ||
| 637 | |||
| 530 | static int | 638 | static int |
| 531 | handle_one_android_event (struct android_display_info *dpyinfo, | 639 | handle_one_android_event (struct android_display_info *dpyinfo, |
| 532 | union android_event *event, int *finish, | 640 | union android_event *event, int *finish, |
| @@ -745,6 +853,17 @@ handle_one_android_event (struct android_display_info *dpyinfo, | |||
| 745 | 853 | ||
| 746 | case ANDROID_WINDOW_ACTION: | 854 | case ANDROID_WINDOW_ACTION: |
| 747 | 855 | ||
| 856 | /* This is a special event sent by android_run_in_emacs_thread | ||
| 857 | used to make Android run stuff. */ | ||
| 858 | |||
| 859 | if (!event->xaction.window && !event->xaction.action) | ||
| 860 | { | ||
| 861 | /* Check for and run anything the UI thread wants to run on the main | ||
| 862 | thread. */ | ||
| 863 | android_check_query (); | ||
| 864 | goto OTHER; | ||
| 865 | } | ||
| 866 | |||
| 748 | f = any; | 867 | f = any; |
| 749 | 868 | ||
| 750 | if (event->xaction.action == 0) | 869 | if (event->xaction.action == 0) |
| @@ -1334,6 +1453,19 @@ handle_one_android_event (struct android_display_info *dpyinfo, | |||
| 1334 | 1453 | ||
| 1335 | goto OTHER; | 1454 | goto OTHER; |
| 1336 | 1455 | ||
| 1456 | /* Input method events. textconv.c functions are called here to | ||
| 1457 | queue events, which are then executed in a safe context | ||
| 1458 | inside keyboard.c. */ | ||
| 1459 | case ANDROID_INPUT_METHOD: | ||
| 1460 | |||
| 1461 | if (!any) | ||
| 1462 | /* Free any text allocated for this event. */ | ||
| 1463 | xfree (event->ime.text); | ||
| 1464 | else | ||
| 1465 | android_handle_ime_event (event, any); | ||
| 1466 | |||
| 1467 | goto OTHER; | ||
| 1468 | |||
| 1337 | default: | 1469 | default: |
| 1338 | goto OTHER; | 1470 | goto OTHER; |
| 1339 | } | 1471 | } |
| @@ -4148,6 +4280,904 @@ android_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1) | |||
| 4148 | 4280 | ||
| 4149 | 4281 | ||
| 4150 | 4282 | ||
| 4283 | #ifdef __clang__ | ||
| 4284 | #pragma clang diagnostic push | ||
| 4285 | #pragma clang diagnostic ignored "-Wmissing-prototypes" | ||
| 4286 | #else | ||
| 4287 | #pragma GCC diagnostic push | ||
| 4288 | #pragma GCC diagnostic ignored "-Wmissing-prototypes" | ||
| 4289 | #endif | ||
| 4290 | |||
| 4291 | /* Input method related functions. Some of these are called from Java | ||
| 4292 | within the UI thread. */ | ||
| 4293 | |||
| 4294 | /* A counter used to decide when an editing request completes. */ | ||
| 4295 | static unsigned long edit_counter; | ||
| 4296 | |||
| 4297 | /* The last counter known to have completed. */ | ||
| 4298 | static unsigned long last_edit_counter; | ||
| 4299 | |||
| 4300 | /* Semaphore posted every time the counter increases. */ | ||
| 4301 | static sem_t edit_sem; | ||
| 4302 | |||
| 4303 | /* Try to synchronize with the UI thread, waiting a certain amount of | ||
| 4304 | time for outstanding editing requests to complete. | ||
| 4305 | |||
| 4306 | Every time one of the text retrieval functions is called and an | ||
| 4307 | editing request is made, Emacs gives the main thread approximately | ||
| 4308 | 50 ms to process it, in order to mostly keep the input method in | ||
| 4309 | sync with the buffer contents. */ | ||
| 4310 | |||
| 4311 | static void | ||
| 4312 | android_sync_edit (void) | ||
| 4313 | { | ||
| 4314 | struct timespec start, end, rem; | ||
| 4315 | |||
| 4316 | if (__atomic_load_n (&last_edit_counter, | ||
| 4317 | __ATOMIC_SEQ_CST) | ||
| 4318 | == edit_counter) | ||
| 4319 | return; | ||
| 4320 | |||
| 4321 | start = current_timespec (); | ||
| 4322 | end = timespec_add (start, make_timespec (0, 50000000)); | ||
| 4323 | |||
| 4324 | while (true) | ||
| 4325 | { | ||
| 4326 | rem = timespec_sub (end, current_timespec ()); | ||
| 4327 | |||
| 4328 | /* Timeout. */ | ||
| 4329 | if (timespec_sign (rem) < 0) | ||
| 4330 | break; | ||
| 4331 | |||
| 4332 | if (__atomic_load_n (&last_edit_counter, | ||
| 4333 | __ATOMIC_SEQ_CST) | ||
| 4334 | == edit_counter) | ||
| 4335 | break; | ||
| 4336 | |||
| 4337 | sem_timedwait (&edit_sem, &end); | ||
| 4338 | } | ||
| 4339 | } | ||
| 4340 | |||
| 4341 | /* Return a copy of the specified Java string and its length in | ||
| 4342 | *LENGTH. Use the JNI environment ENV. Value is NULL if copying | ||
| 4343 | *the string fails. */ | ||
| 4344 | |||
| 4345 | static unsigned short * | ||
| 4346 | android_copy_java_string (JNIEnv *env, jstring string, size_t *length) | ||
| 4347 | { | ||
| 4348 | jsize size, i; | ||
| 4349 | const jchar *java; | ||
| 4350 | unsigned short *buffer; | ||
| 4351 | |||
| 4352 | size = (*env)->GetStringLength (env, string); | ||
| 4353 | buffer = malloc (size * sizeof *buffer); | ||
| 4354 | |||
| 4355 | if (!buffer) | ||
| 4356 | return NULL; | ||
| 4357 | |||
| 4358 | java = (*env)->GetStringChars (env, string, NULL); | ||
| 4359 | |||
| 4360 | if (!java) | ||
| 4361 | { | ||
| 4362 | free (buffer); | ||
| 4363 | return NULL; | ||
| 4364 | } | ||
| 4365 | |||
| 4366 | for (i = 0; i < size; ++i) | ||
| 4367 | buffer[i] = java[i]; | ||
| 4368 | |||
| 4369 | *length = size; | ||
| 4370 | (*env)->ReleaseStringChars (env, string, java); | ||
| 4371 | return buffer; | ||
| 4372 | } | ||
| 4373 | |||
| 4374 | JNIEXPORT void JNICALL | ||
| 4375 | NATIVE_NAME (beginBatchEdit) (JNIEnv *env, jobject object, jshort window) | ||
| 4376 | { | ||
| 4377 | union android_event event; | ||
| 4378 | |||
| 4379 | event.ime.type = ANDROID_INPUT_METHOD; | ||
| 4380 | event.ime.serial = ++event_serial; | ||
| 4381 | event.ime.window = window; | ||
| 4382 | event.ime.operation = ANDROID_IME_START_BATCH_EDIT; | ||
| 4383 | event.ime.start = 0; | ||
| 4384 | event.ime.end = 0; | ||
| 4385 | event.ime.length = 0; | ||
| 4386 | event.ime.position = 0; | ||
| 4387 | event.ime.text = NULL; | ||
| 4388 | event.ime.counter = ++edit_counter; | ||
| 4389 | |||
| 4390 | android_write_event (&event); | ||
| 4391 | } | ||
| 4392 | |||
| 4393 | JNIEXPORT void JNICALL | ||
| 4394 | NATIVE_NAME (endBatchEdit) (JNIEnv *env, jobject object, jshort window) | ||
| 4395 | { | ||
| 4396 | union android_event event; | ||
| 4397 | |||
| 4398 | event.ime.type = ANDROID_INPUT_METHOD; | ||
| 4399 | event.ime.serial = ++event_serial; | ||
| 4400 | event.ime.window = window; | ||
| 4401 | event.ime.operation = ANDROID_IME_END_BATCH_EDIT; | ||
| 4402 | event.ime.start = 0; | ||
| 4403 | event.ime.end = 0; | ||
| 4404 | event.ime.length = 0; | ||
| 4405 | event.ime.position = 0; | ||
| 4406 | event.ime.text = NULL; | ||
| 4407 | event.ime.counter = ++edit_counter; | ||
| 4408 | |||
| 4409 | android_write_event (&event); | ||
| 4410 | } | ||
| 4411 | |||
| 4412 | JNIEXPORT void JNICALL | ||
| 4413 | NATIVE_NAME (commitCompletion) (JNIEnv *env, jobject object, jshort window, | ||
| 4414 | jstring completion_text, jint position) | ||
| 4415 | { | ||
| 4416 | union android_event event; | ||
| 4417 | unsigned short *text; | ||
| 4418 | size_t length; | ||
| 4419 | |||
| 4420 | /* First, obtain a copy of the Java string. */ | ||
| 4421 | text = android_copy_java_string (env, completion_text, &length); | ||
| 4422 | |||
| 4423 | if (!text) | ||
| 4424 | return; | ||
| 4425 | |||
| 4426 | /* Next, populate the event. Events will always eventually be | ||
| 4427 | delivered on Android, so handle_one_android_event can be relied | ||
| 4428 | on to free text. */ | ||
| 4429 | |||
| 4430 | event.ime.type = ANDROID_INPUT_METHOD; | ||
| 4431 | event.ime.serial = ++event_serial; | ||
| 4432 | event.ime.window = window; | ||
| 4433 | event.ime.operation = ANDROID_IME_COMMIT_TEXT; | ||
| 4434 | event.ime.start = 0; | ||
| 4435 | event.ime.end = 0; | ||
| 4436 | event.ime.length = min (length, PTRDIFF_MAX); | ||
| 4437 | event.ime.position = position; | ||
| 4438 | event.ime.text = text; | ||
| 4439 | event.ime.counter = ++edit_counter; | ||
| 4440 | |||
| 4441 | android_write_event (&event); | ||
| 4442 | } | ||
| 4443 | |||
| 4444 | JNIEXPORT void JNICALL | ||
| 4445 | NATIVE_NAME (commitText) (JNIEnv *env, jobject object, jshort window, | ||
| 4446 | jstring commit_text, jint position) | ||
| 4447 | { | ||
| 4448 | union android_event event; | ||
| 4449 | unsigned short *text; | ||
| 4450 | size_t length; | ||
| 4451 | |||
| 4452 | /* First, obtain a copy of the Java string. */ | ||
| 4453 | text = android_copy_java_string (env, commit_text, &length); | ||
| 4454 | |||
| 4455 | if (!text) | ||
| 4456 | return; | ||
| 4457 | |||
| 4458 | /* Next, populate the event. Events will always eventually be | ||
| 4459 | delivered on Android, so handle_one_android_event can be relied | ||
| 4460 | on to free text. */ | ||
| 4461 | |||
| 4462 | event.ime.type = ANDROID_INPUT_METHOD; | ||
| 4463 | event.ime.serial = ++event_serial; | ||
| 4464 | event.ime.window = window; | ||
| 4465 | event.ime.operation = ANDROID_IME_COMMIT_TEXT; | ||
| 4466 | event.ime.start = 0; | ||
| 4467 | event.ime.end = 0; | ||
| 4468 | event.ime.length = min (length, PTRDIFF_MAX); | ||
| 4469 | event.ime.position = position; | ||
| 4470 | event.ime.text = text; | ||
| 4471 | event.ime.counter = ++edit_counter; | ||
| 4472 | |||
| 4473 | android_write_event (&event); | ||
| 4474 | } | ||
| 4475 | |||
| 4476 | JNIEXPORT void JNICALL | ||
| 4477 | NATIVE_NAME (deleteSurroundingText) (JNIEnv *env, jobject object, | ||
| 4478 | jshort window, jint left_length, | ||
| 4479 | jint right_length) | ||
| 4480 | { | ||
| 4481 | union android_event event; | ||
| 4482 | |||
| 4483 | event.ime.type = ANDROID_INPUT_METHOD; | ||
| 4484 | event.ime.serial = ++event_serial; | ||
| 4485 | event.ime.window = window; | ||
| 4486 | event.ime.operation = ANDROID_IME_DELETE_SURROUNDING_TEXT; | ||
| 4487 | event.ime.start = left_length; | ||
| 4488 | event.ime.end = right_length; | ||
| 4489 | event.ime.length = 0; | ||
| 4490 | event.ime.position = 0; | ||
| 4491 | event.ime.text = NULL; | ||
| 4492 | event.ime.counter = ++edit_counter; | ||
| 4493 | |||
| 4494 | android_write_event (&event); | ||
| 4495 | } | ||
| 4496 | |||
| 4497 | JNIEXPORT void JNICALL | ||
| 4498 | NATIVE_NAME (finishComposingText) (JNIEnv *env, jobject object, | ||
| 4499 | jshort window) | ||
| 4500 | { | ||
| 4501 | union android_event event; | ||
| 4502 | |||
| 4503 | event.ime.type = ANDROID_INPUT_METHOD; | ||
| 4504 | event.ime.serial = ++event_serial; | ||
| 4505 | event.ime.window = window; | ||
| 4506 | event.ime.operation = ANDROID_IME_FINISH_COMPOSING_TEXT; | ||
| 4507 | event.ime.start = 0; | ||
| 4508 | event.ime.end = 0; | ||
| 4509 | event.ime.length = 0; | ||
| 4510 | event.ime.position = 0; | ||
| 4511 | event.ime.text = NULL; | ||
| 4512 | event.ime.counter = ++edit_counter; | ||
| 4513 | |||
| 4514 | android_write_event (&event); | ||
| 4515 | } | ||
| 4516 | |||
| 4517 | /* Structure describing the context used for a text query. */ | ||
| 4518 | |||
| 4519 | struct android_conversion_query_context | ||
| 4520 | { | ||
| 4521 | /* The conversion request. */ | ||
| 4522 | struct textconv_callback_struct query; | ||
| 4523 | |||
| 4524 | /* The window the request is being made on. */ | ||
| 4525 | android_window window; | ||
| 4526 | |||
| 4527 | /* Whether or not the request was successful. */ | ||
| 4528 | bool success; | ||
| 4529 | }; | ||
| 4530 | |||
| 4531 | /* Obtain the text from the frame whose window is that specified in | ||
| 4532 | DATA using the text conversion query specified there. | ||
| 4533 | |||
| 4534 | Adjust the query position to skip over any active composing region. | ||
| 4535 | |||
| 4536 | Set ((struct android_conversion_query_context *) DATA)->success on | ||
| 4537 | success. */ | ||
| 4538 | |||
| 4539 | static void | ||
| 4540 | android_perform_conversion_query (void *data) | ||
| 4541 | { | ||
| 4542 | struct android_conversion_query_context *context; | ||
| 4543 | struct frame *f; | ||
| 4544 | |||
| 4545 | context = data; | ||
| 4546 | |||
| 4547 | /* Find the frame associated with the window. */ | ||
| 4548 | f = android_window_to_frame (NULL, context->window); | ||
| 4549 | |||
| 4550 | if (!f) | ||
| 4551 | return; | ||
| 4552 | |||
| 4553 | textconv_query (f, &context->query, | ||
| 4554 | TEXTCONV_SKIP_CONVERSION_REGION); | ||
| 4555 | |||
| 4556 | /* context->query.text will have been set even if textconv_query | ||
| 4557 | returns 1. */ | ||
| 4558 | |||
| 4559 | context->success = true; | ||
| 4560 | } | ||
| 4561 | |||
| 4562 | /* Convert a string BUFFERS containing N characters in Emacs's | ||
| 4563 | internal multibyte encoding to a Java string utilizing the | ||
| 4564 | specified JNI environment. | ||
| 4565 | |||
| 4566 | If N is equal to BYTES, then BUFFER is a single byte buffer. | ||
| 4567 | Otherwise, BUFFER is a multibyte buffer. | ||
| 4568 | |||
| 4569 | Make sure N and BYTES are absolutely correct, or you are asking for | ||
| 4570 | trouble. | ||
| 4571 | |||
| 4572 | Value is the string upon success, NULL otherwise. Any exceptions | ||
| 4573 | generated are not cleared. */ | ||
| 4574 | |||
| 4575 | static jstring | ||
| 4576 | android_text_to_string (JNIEnv *env, char *buffer, ptrdiff_t n, | ||
| 4577 | ptrdiff_t bytes) | ||
| 4578 | { | ||
| 4579 | jchar *utf16; | ||
| 4580 | size_t size, index; | ||
| 4581 | jstring string; | ||
| 4582 | int encoded; | ||
| 4583 | |||
| 4584 | if (n == bytes) | ||
| 4585 | { | ||
| 4586 | /* This buffer holds no multibyte characters. */ | ||
| 4587 | |||
| 4588 | if (INT_MULTIPLY_WRAPV (n, sizeof *utf16, &size)) | ||
| 4589 | return NULL; | ||
| 4590 | |||
| 4591 | utf16 = malloc (size); | ||
| 4592 | index = 0; | ||
| 4593 | |||
| 4594 | if (!utf16) | ||
| 4595 | return NULL; | ||
| 4596 | |||
| 4597 | while (n--) | ||
| 4598 | { | ||
| 4599 | utf16[index] = buffer[index]; | ||
| 4600 | index++; | ||
| 4601 | } | ||
| 4602 | |||
| 4603 | string = (*env)->NewString (env, utf16, bytes); | ||
| 4604 | free (utf16); | ||
| 4605 | |||
| 4606 | return string; | ||
| 4607 | } | ||
| 4608 | |||
| 4609 | /* Allocate enough to hold N characters. */ | ||
| 4610 | |||
| 4611 | if (INT_MULTIPLY_WRAPV (n, sizeof *utf16, &size)) | ||
| 4612 | return NULL; | ||
| 4613 | |||
| 4614 | utf16 = malloc (size); | ||
| 4615 | index = 0; | ||
| 4616 | |||
| 4617 | if (!utf16) | ||
| 4618 | return NULL; | ||
| 4619 | |||
| 4620 | while (n--) | ||
| 4621 | { | ||
| 4622 | eassert (CHAR_HEAD_P (*buffer)); | ||
| 4623 | encoded = STRING_CHAR ((unsigned char *) buffer); | ||
| 4624 | |||
| 4625 | /* Now figure out how to save ENCODED into the string. | ||
| 4626 | Emacs operates on multibyte characters, not UTF-16 | ||
| 4627 | characters with surrogate pairs as Android does. | ||
| 4628 | |||
| 4629 | However, character positions in Java are represented in 2 | ||
| 4630 | byte units, meaning that the text position reported to | ||
| 4631 | Android can become out of sync if characters are found in a | ||
| 4632 | buffer that require surrogate pairs. | ||
| 4633 | |||
| 4634 | The hack used by Emacs is to simply replace each multibyte | ||
| 4635 | character that doesn't fit in a jchar with the NULL | ||
| 4636 | character. */ | ||
| 4637 | |||
| 4638 | if (encoded >= 65536) | ||
| 4639 | encoded = 0; | ||
| 4640 | |||
| 4641 | utf16[index++] = encoded; | ||
| 4642 | buffer += BYTES_BY_CHAR_HEAD (*buffer); | ||
| 4643 | } | ||
| 4644 | |||
| 4645 | /* Create the string. */ | ||
| 4646 | string = (*env)->NewString (env, utf16, index); | ||
| 4647 | free (utf16); | ||
| 4648 | return string; | ||
| 4649 | } | ||
| 4650 | |||
| 4651 | JNIEXPORT jstring JNICALL | ||
| 4652 | NATIVE_NAME (getSelectedText) (JNIEnv *env, jobject object, | ||
| 4653 | jshort window) | ||
| 4654 | { | ||
| 4655 | return NULL; | ||
| 4656 | } | ||
| 4657 | |||
| 4658 | JNIEXPORT jstring JNICALL | ||
| 4659 | NATIVE_NAME (getTextAfterCursor) (JNIEnv *env, jobject object, jshort window, | ||
| 4660 | jint length, jint flags) | ||
| 4661 | { | ||
| 4662 | struct android_conversion_query_context context; | ||
| 4663 | jstring string; | ||
| 4664 | |||
| 4665 | /* First, set up the conversion query. */ | ||
| 4666 | context.query.position = 0; | ||
| 4667 | context.query.direction = TEXTCONV_FORWARD_CHAR; | ||
| 4668 | context.query.factor = min (length, 65535); | ||
| 4669 | context.query.operation = TEXTCONV_RETRIEVAL; | ||
| 4670 | |||
| 4671 | /* Next, set the rest of the context. */ | ||
| 4672 | context.window = window; | ||
| 4673 | context.success = false; | ||
| 4674 | |||
| 4675 | /* Now try to perform the query. */ | ||
| 4676 | android_sync_edit (); | ||
| 4677 | if (android_run_in_emacs_thread (android_perform_conversion_query, | ||
| 4678 | &context)) | ||
| 4679 | return NULL; | ||
| 4680 | |||
| 4681 | if (!context.success) | ||
| 4682 | return NULL; | ||
| 4683 | |||
| 4684 | /* context->query.text now contains the text in Emacs's internal | ||
| 4685 | UTF-8 based encoding. | ||
| 4686 | |||
| 4687 | Convert it to Java's UTF-16 encoding, which is the same as | ||
| 4688 | UTF-16, except that NULL bytes are encoded as surrogate pairs. | ||
| 4689 | |||
| 4690 | This assumes that `free' can free data allocated with xmalloc. */ | ||
| 4691 | |||
| 4692 | string = android_text_to_string (env, context.query.text.text, | ||
| 4693 | context.query.text.length, | ||
| 4694 | context.query.text.bytes); | ||
| 4695 | free (context.query.text.text); | ||
| 4696 | |||
| 4697 | return string; | ||
| 4698 | } | ||
| 4699 | |||
| 4700 | JNIEXPORT jstring JNICALL | ||
| 4701 | NATIVE_NAME (getTextBeforeCursor) (JNIEnv *env, jobject object, jshort window, | ||
| 4702 | jint length, jint flags) | ||
| 4703 | { | ||
| 4704 | struct android_conversion_query_context context; | ||
| 4705 | jstring string; | ||
| 4706 | |||
| 4707 | /* First, set up the conversion query. */ | ||
| 4708 | context.query.position = 0; | ||
| 4709 | context.query.direction = TEXTCONV_BACKWARD_CHAR; | ||
| 4710 | context.query.factor = min (length, 65535); | ||
| 4711 | context.query.operation = TEXTCONV_RETRIEVAL; | ||
| 4712 | |||
| 4713 | /* Next, set the rest of the context. */ | ||
| 4714 | context.window = window; | ||
| 4715 | context.success = false; | ||
| 4716 | |||
| 4717 | /* Now try to perform the query. */ | ||
| 4718 | android_sync_edit (); | ||
| 4719 | if (android_run_in_emacs_thread (android_perform_conversion_query, | ||
| 4720 | &context)) | ||
| 4721 | return NULL; | ||
| 4722 | |||
| 4723 | if (!context.success) | ||
| 4724 | return NULL; | ||
| 4725 | |||
| 4726 | /* context->query.text now contains the text in Emacs's internal | ||
| 4727 | UTF-8 based encoding. | ||
| 4728 | |||
| 4729 | Convert it to Java's UTF-16 encoding, which is the same as | ||
| 4730 | UTF-16, except that NULL bytes are encoded as surrogate pairs. | ||
| 4731 | |||
| 4732 | This assumes that `free' can free data allocated with xmalloc. */ | ||
| 4733 | |||
| 4734 | string = android_text_to_string (env, context.query.text.text, | ||
| 4735 | context.query.text.length, | ||
| 4736 | context.query.text.bytes); | ||
| 4737 | free (context.query.text.text); | ||
| 4738 | |||
| 4739 | return string; | ||
| 4740 | } | ||
| 4741 | |||
| 4742 | JNIEXPORT void JNICALL | ||
| 4743 | NATIVE_NAME (setComposingText) (JNIEnv *env, jobject object, jshort window, | ||
| 4744 | jstring composing_text, | ||
| 4745 | jint new_cursor_position) | ||
| 4746 | { | ||
| 4747 | union android_event event; | ||
| 4748 | unsigned short *text; | ||
| 4749 | size_t length; | ||
| 4750 | |||
| 4751 | /* First, obtain a copy of the Java string. */ | ||
| 4752 | text = android_copy_java_string (env, composing_text, &length); | ||
| 4753 | |||
| 4754 | if (!text) | ||
| 4755 | return; | ||
| 4756 | |||
| 4757 | /* Next, populate the event. Events will always eventually be | ||
| 4758 | delivered on Android, so handle_one_android_event can be relied | ||
| 4759 | on to free text. */ | ||
| 4760 | |||
| 4761 | event.ime.type = ANDROID_INPUT_METHOD; | ||
| 4762 | event.ime.serial = ++event_serial; | ||
| 4763 | event.ime.window = window; | ||
| 4764 | event.ime.operation = ANDROID_IME_SET_COMPOSING_TEXT; | ||
| 4765 | event.ime.start = 0; | ||
| 4766 | event.ime.end = 0; | ||
| 4767 | event.ime.length = min (length, PTRDIFF_MAX); | ||
| 4768 | event.ime.position = new_cursor_position; | ||
| 4769 | event.ime.text = text; | ||
| 4770 | event.ime.counter = ++edit_counter; | ||
| 4771 | |||
| 4772 | android_write_event (&event); | ||
| 4773 | } | ||
| 4774 | |||
| 4775 | JNIEXPORT void JNICALL | ||
| 4776 | NATIVE_NAME (setComposingRegion) (JNIEnv *env, jobject object, jshort window, | ||
| 4777 | jint start, jint end) | ||
| 4778 | { | ||
| 4779 | union android_event event; | ||
| 4780 | |||
| 4781 | event.ime.type = ANDROID_INPUT_METHOD; | ||
| 4782 | event.ime.serial = ++event_serial; | ||
| 4783 | event.ime.window = window; | ||
| 4784 | event.ime.operation = ANDROID_IME_SET_COMPOSING_REGION; | ||
| 4785 | event.ime.start = start; | ||
| 4786 | event.ime.end = end; | ||
| 4787 | event.ime.length = 0; | ||
| 4788 | event.ime.position = 0; | ||
| 4789 | event.ime.text = NULL; | ||
| 4790 | event.ime.counter = ++edit_counter; | ||
| 4791 | |||
| 4792 | android_write_event (&event); | ||
| 4793 | } | ||
| 4794 | |||
| 4795 | JNIEXPORT void JNICALL | ||
| 4796 | NATIVE_NAME (setSelection) (JNIEnv *env, jobject object, jshort window, | ||
| 4797 | jint start, jint end) | ||
| 4798 | { | ||
| 4799 | union android_event event; | ||
| 4800 | |||
| 4801 | /* While IMEs want access to the entire selection, Emacs only | ||
| 4802 | supports setting the point. */ | ||
| 4803 | |||
| 4804 | event.ime.type = ANDROID_INPUT_METHOD; | ||
| 4805 | event.ime.serial = ++event_serial; | ||
| 4806 | event.ime.window = window; | ||
| 4807 | event.ime.operation = ANDROID_IME_SET_POINT; | ||
| 4808 | event.ime.start = 0; | ||
| 4809 | event.ime.end = 0; | ||
| 4810 | event.ime.length = 0; | ||
| 4811 | event.ime.position = start; | ||
| 4812 | event.ime.text = NULL; | ||
| 4813 | event.ime.counter = ++edit_counter; | ||
| 4814 | } | ||
| 4815 | |||
| 4816 | /* Structure describing the context for `getSelection'. */ | ||
| 4817 | |||
| 4818 | struct android_get_selection_context | ||
| 4819 | { | ||
| 4820 | /* The window in question. */ | ||
| 4821 | android_window window; | ||
| 4822 | |||
| 4823 | /* The position of the window's point when it was last | ||
| 4824 | redisplayed. */ | ||
| 4825 | ptrdiff_t point; | ||
| 4826 | }; | ||
| 4827 | |||
| 4828 | /* Function run on the main thread by `getSelection'. | ||
| 4829 | Place the character position of point in PT. */ | ||
| 4830 | |||
| 4831 | static void | ||
| 4832 | android_get_selection (void *data) | ||
| 4833 | { | ||
| 4834 | struct android_get_selection_context *context; | ||
| 4835 | struct frame *f; | ||
| 4836 | struct window *w; | ||
| 4837 | |||
| 4838 | context = data; | ||
| 4839 | |||
| 4840 | /* Look up the associated frame and its selected window. */ | ||
| 4841 | f = android_window_to_frame (NULL, context->window); | ||
| 4842 | |||
| 4843 | if (!f) | ||
| 4844 | context->point = -1; | ||
| 4845 | else | ||
| 4846 | { | ||
| 4847 | w = XWINDOW (f->selected_window); | ||
| 4848 | |||
| 4849 | /* Return W's point at the time of the last redisplay. This is | ||
| 4850 | rather important to keep the input method consistent with the | ||
| 4851 | contents of the display. */ | ||
| 4852 | context->point = w->last_point; | ||
| 4853 | } | ||
| 4854 | } | ||
| 4855 | |||
| 4856 | JNIEXPORT jint JNICALL | ||
| 4857 | NATIVE_NAME (getSelection) (JNIEnv *env, jobject object, jshort window) | ||
| 4858 | { | ||
| 4859 | struct android_get_selection_context context; | ||
| 4860 | |||
| 4861 | context.window = window; | ||
| 4862 | |||
| 4863 | android_sync_edit (); | ||
| 4864 | if (android_run_in_emacs_thread (android_get_selection, | ||
| 4865 | &context)) | ||
| 4866 | return -1; | ||
| 4867 | |||
| 4868 | if (context.point == -1) | ||
| 4869 | return -1; | ||
| 4870 | |||
| 4871 | return min (context.point, TYPE_MAXIMUM (jint)); | ||
| 4872 | } | ||
| 4873 | |||
| 4874 | JNIEXPORT void JNICALL | ||
| 4875 | NATIVE_NAME (performEditorAction) (JNIEnv *env, jobject object, | ||
| 4876 | jshort window, int action) | ||
| 4877 | { | ||
| 4878 | union android_event event; | ||
| 4879 | |||
| 4880 | /* Undocumented behavior: performEditorAction is apparently expected | ||
| 4881 | to finish composing any text. */ | ||
| 4882 | |||
| 4883 | NATIVE_NAME (finishComposingText) (env, object, window); | ||
| 4884 | |||
| 4885 | event.xkey.type = ANDROID_KEY_PRESS; | ||
| 4886 | event.xkey.serial = ++event_serial; | ||
| 4887 | event.xkey.window = window; | ||
| 4888 | event.xkey.time = 0; | ||
| 4889 | event.xkey.state = 0; | ||
| 4890 | event.xkey.keycode = 66; | ||
| 4891 | event.xkey.unicode_char = 0; | ||
| 4892 | |||
| 4893 | android_write_event (&event); | ||
| 4894 | } | ||
| 4895 | |||
| 4896 | struct android_get_extracted_text_context | ||
| 4897 | { | ||
| 4898 | /* The parameters of the request. */ | ||
| 4899 | int hint_max_chars; | ||
| 4900 | |||
| 4901 | /* Token for the request. */ | ||
| 4902 | int token; | ||
| 4903 | |||
| 4904 | /* The returned text, or NULL. */ | ||
| 4905 | char *text; | ||
| 4906 | |||
| 4907 | /* The size of that text in characters and bytes. */ | ||
| 4908 | ptrdiff_t length, bytes; | ||
| 4909 | |||
| 4910 | /* Offsets into that text. */ | ||
| 4911 | ptrdiff_t start, offset; | ||
| 4912 | |||
| 4913 | /* The window. */ | ||
| 4914 | android_window window; | ||
| 4915 | }; | ||
| 4916 | |||
| 4917 | /* Return the extracted text in the extracted text context specified | ||
| 4918 | by DATA. */ | ||
| 4919 | |||
| 4920 | static void | ||
| 4921 | android_get_extracted_text (void *data) | ||
| 4922 | { | ||
| 4923 | struct android_get_extracted_text_context *request; | ||
| 4924 | struct frame *f; | ||
| 4925 | |||
| 4926 | request = data; | ||
| 4927 | |||
| 4928 | /* Find the frame associated with the window. */ | ||
| 4929 | f = android_window_to_frame (NULL, request->window); | ||
| 4930 | |||
| 4931 | if (!f) | ||
| 4932 | return; | ||
| 4933 | |||
| 4934 | /* Now get the extracted text. */ | ||
| 4935 | request->text | ||
| 4936 | = get_extracted_text (f, min (request->hint_max_chars, 600), | ||
| 4937 | &request->start, &request->offset, | ||
| 4938 | &request->length, &request->bytes); | ||
| 4939 | } | ||
| 4940 | |||
| 4941 | /* Structure describing the `ExtractedTextRequest' class. | ||
| 4942 | Valid only on the UI thread. */ | ||
| 4943 | |||
| 4944 | struct android_extracted_text_request_class | ||
| 4945 | { | ||
| 4946 | bool initialized; | ||
| 4947 | jfieldID hint_max_chars; | ||
| 4948 | jfieldID token; | ||
| 4949 | }; | ||
| 4950 | |||
| 4951 | /* Structure describing the `ExtractedText' class. | ||
| 4952 | Valid only on the UI thread. */ | ||
| 4953 | |||
| 4954 | struct android_extracted_text_class | ||
| 4955 | { | ||
| 4956 | jclass class; | ||
| 4957 | jmethodID constructor; | ||
| 4958 | jfieldID partial_start_offset; | ||
| 4959 | jfieldID partial_end_offset; | ||
| 4960 | jfieldID selection_start; | ||
| 4961 | jfieldID selection_end; | ||
| 4962 | jfieldID start_offset; | ||
| 4963 | jfieldID text; | ||
| 4964 | }; | ||
| 4965 | |||
| 4966 | JNIEXPORT jobject JNICALL | ||
| 4967 | NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object, | ||
| 4968 | jshort window, jobject request, | ||
| 4969 | jint flags) | ||
| 4970 | { | ||
| 4971 | struct android_get_extracted_text_context context; | ||
| 4972 | static struct android_extracted_text_request_class request_class; | ||
| 4973 | static struct android_extracted_text_class text_class; | ||
| 4974 | jstring string; | ||
| 4975 | jclass class; | ||
| 4976 | jobject object; | ||
| 4977 | |||
| 4978 | /* TODO: report changes to extracted text. */ | ||
| 4979 | |||
| 4980 | /* Initialize both classes if necessary. */ | ||
| 4981 | |||
| 4982 | if (!request_class.initialized) | ||
| 4983 | { | ||
| 4984 | class | ||
| 4985 | = (*env)->FindClass (env, ("android/view/inputmethod" | ||
| 4986 | "/ExtractedTextRequest")); | ||
| 4987 | assert (class); | ||
| 4988 | |||
| 4989 | request_class.hint_max_chars | ||
| 4990 | = (*env)->GetFieldID (env, class, "hintMaxChars", "I"); | ||
| 4991 | assert (request_class.hint_max_chars); | ||
| 4992 | |||
| 4993 | request_class.token | ||
| 4994 | = (*env)->GetFieldID (env, class, "token", "I"); | ||
| 4995 | assert (request_class.token); | ||
| 4996 | |||
| 4997 | request_class.initialized = true; | ||
| 4998 | } | ||
| 4999 | |||
| 5000 | if (!text_class.class) | ||
| 5001 | { | ||
| 5002 | text_class.class | ||
| 5003 | = (*env)->FindClass (env, ("android/view/inputmethod" | ||
| 5004 | "/ExtractedText")); | ||
| 5005 | assert (text_class.class); | ||
| 5006 | |||
| 5007 | class | ||
| 5008 | = text_class.class | ||
| 5009 | = (*env)->NewGlobalRef (env, text_class.class); | ||
| 5010 | assert (text_class.class); | ||
| 5011 | |||
| 5012 | text_class.partial_start_offset | ||
| 5013 | = (*env)->GetFieldID (env, class, "partialStartOffset", "I"); | ||
| 5014 | text_class.partial_end_offset | ||
| 5015 | = (*env)->GetFieldID (env, class, "partialEndOffset", "I"); | ||
| 5016 | text_class.selection_start | ||
| 5017 | = (*env)->GetFieldID (env, class, "selectionStart", "I"); | ||
| 5018 | text_class.selection_end | ||
| 5019 | = (*env)->GetFieldID (env, class, "selectionEnd", "I"); | ||
| 5020 | text_class.start_offset | ||
| 5021 | = (*env)->GetFieldID (env, class, "startOffset", "I"); | ||
| 5022 | text_class.text | ||
| 5023 | = (*env)->GetFieldID (env, class, "text", "Ljava/lang/CharSequence;"); | ||
| 5024 | text_class.constructor | ||
| 5025 | = (*env)->GetMethodID (env, class, "<init>", "()V"); | ||
| 5026 | } | ||
| 5027 | |||
| 5028 | context.hint_max_chars | ||
| 5029 | = (*env)->GetIntField (env, request, request_class.hint_max_chars); | ||
| 5030 | context.token | ||
| 5031 | = (*env)->GetIntField (env, request, request_class.token); | ||
| 5032 | context.text = NULL; | ||
| 5033 | context.window = window; | ||
| 5034 | |||
| 5035 | android_sync_edit (); | ||
| 5036 | if (android_run_in_emacs_thread (android_get_extracted_text, | ||
| 5037 | &context)) | ||
| 5038 | return NULL; | ||
| 5039 | |||
| 5040 | if (!context.text) | ||
| 5041 | return NULL; | ||
| 5042 | |||
| 5043 | /* Encode the returned text. */ | ||
| 5044 | string = android_text_to_string (env, context.text, context.length, | ||
| 5045 | context.bytes); | ||
| 5046 | free (context.text); | ||
| 5047 | |||
| 5048 | if (!string) | ||
| 5049 | return NULL; | ||
| 5050 | |||
| 5051 | /* Create an ExtractedText object containing this information. */ | ||
| 5052 | object = (*android_java_env)->NewObject (env, text_class.class, | ||
| 5053 | text_class.constructor); | ||
| 5054 | if (!object) | ||
| 5055 | return NULL; | ||
| 5056 | |||
| 5057 | (*env)->SetIntField (env, object, text_class.partial_start_offset, -1); | ||
| 5058 | (*env)->SetIntField (env, object, text_class.partial_end_offset, -1); | ||
| 5059 | (*env)->SetIntField (env, object, text_class.selection_start, | ||
| 5060 | min (context.offset, TYPE_MAXIMUM (jint))); | ||
| 5061 | (*env)->SetIntField (env, object, text_class.selection_end, | ||
| 5062 | min (context.offset, TYPE_MAXIMUM (jint))); | ||
| 5063 | (*env)->SetIntField (env, object, text_class.start_offset, | ||
| 5064 | min (context.start, TYPE_MAXIMUM (jint))); | ||
| 5065 | (*env)->SetObjectField (env, object, text_class.text, string); | ||
| 5066 | return object; | ||
| 5067 | } | ||
| 5068 | |||
| 5069 | #ifdef __clang__ | ||
| 5070 | #pragma clang diagnostic pop | ||
| 5071 | #else | ||
| 5072 | #pragma GCC diagnostic pop | ||
| 5073 | #endif | ||
| 5074 | |||
| 5075 | |||
| 5076 | |||
| 5077 | /* Tell the input method where the composing region and selection of | ||
| 5078 | F's selected window is located. W should be F's selected window; | ||
| 5079 | if it is NULL, then F->selected_window is used in its place. */ | ||
| 5080 | |||
| 5081 | static void | ||
| 5082 | android_update_selection (struct frame *f, struct window *w) | ||
| 5083 | { | ||
| 5084 | ptrdiff_t start, end, point; | ||
| 5085 | |||
| 5086 | if (MARKERP (f->conversion.compose_region_start)) | ||
| 5087 | { | ||
| 5088 | eassert (MARKERP (f->conversion.compose_region_end)); | ||
| 5089 | |||
| 5090 | start = marker_position (f->conversion.compose_region_start); | ||
| 5091 | end = marker_position (f->conversion.compose_region_end); | ||
| 5092 | } | ||
| 5093 | else | ||
| 5094 | start = -1, end = -1; | ||
| 5095 | |||
| 5096 | /* Now constrain START and END to the maximium size of a Java | ||
| 5097 | integer. */ | ||
| 5098 | start = min (start, TYPE_MAXIMUM (jint)); | ||
| 5099 | end = min (end, TYPE_MAXIMUM (jint)); | ||
| 5100 | |||
| 5101 | if (!w) | ||
| 5102 | w = XWINDOW (f->selected_window); | ||
| 5103 | |||
| 5104 | /* Figure out where the point is. */ | ||
| 5105 | point = min (w->last_point, TYPE_MAXIMUM (jint)); | ||
| 5106 | |||
| 5107 | /* Send the update. */ | ||
| 5108 | android_update_ic (FRAME_ANDROID_WINDOW (f), point, point, | ||
| 5109 | start, end); | ||
| 5110 | } | ||
| 5111 | |||
| 5112 | /* Notice that the input method connection to F should be reset as a | ||
| 5113 | result of a change to its contents. */ | ||
| 5114 | |||
| 5115 | static void | ||
| 5116 | android_reset_conversion (struct frame *f) | ||
| 5117 | { | ||
| 5118 | /* Reset the input method. | ||
| 5119 | |||
| 5120 | Pick an appropriate ``input mode'' based on whether or not the | ||
| 5121 | minibuffer window is selected; this controls whether or not | ||
| 5122 | ``RET'' inserts a newline or sends an actual key event. */ | ||
| 5123 | android_reset_ic (FRAME_ANDROID_WINDOW (f), | ||
| 5124 | (EQ (f->selected_window, | ||
| 5125 | f->minibuffer_window) | ||
| 5126 | ? ANDROID_IC_MODE_ACTION | ||
| 5127 | : ANDROID_IC_MODE_TEXT)); | ||
| 5128 | |||
| 5129 | /* Move its selection to the specified position. */ | ||
| 5130 | android_update_selection (f, NULL); | ||
| 5131 | } | ||
| 5132 | |||
| 5133 | /* Notice that point has moved in the F's selected window's selected | ||
| 5134 | buffer. W is the window, and BUFFER is that buffer. */ | ||
| 5135 | |||
| 5136 | static void | ||
| 5137 | android_set_point (struct frame *f, struct window *w, | ||
| 5138 | struct buffer *buffer) | ||
| 5139 | { | ||
| 5140 | android_update_selection (f, w); | ||
| 5141 | } | ||
| 5142 | |||
| 5143 | /* Notice that the composition region on F's old selected window has | ||
| 5144 | changed. */ | ||
| 5145 | |||
| 5146 | static void | ||
| 5147 | android_compose_region_changed (struct frame *f) | ||
| 5148 | { | ||
| 5149 | android_update_selection (f, XWINDOW (f->old_selected_window)); | ||
| 5150 | } | ||
| 5151 | |||
| 5152 | /* Notice that the text conversion has completed. */ | ||
| 5153 | |||
| 5154 | static void | ||
| 5155 | android_notify_conversion (unsigned long counter) | ||
| 5156 | { | ||
| 5157 | int sval; | ||
| 5158 | |||
| 5159 | if (last_edit_counter < counter) | ||
| 5160 | __atomic_store_n (&last_edit_counter, counter, | ||
| 5161 | __ATOMIC_SEQ_CST); | ||
| 5162 | |||
| 5163 | sem_getvalue (&edit_sem, &sval); | ||
| 5164 | |||
| 5165 | if (sval <= 0) | ||
| 5166 | sem_post (&edit_sem); | ||
| 5167 | } | ||
| 5168 | |||
| 5169 | /* Android text conversion interface. */ | ||
| 5170 | |||
| 5171 | static struct textconv_interface text_conversion_interface = | ||
| 5172 | { | ||
| 5173 | android_reset_conversion, | ||
| 5174 | android_set_point, | ||
| 5175 | android_compose_region_changed, | ||
| 5176 | android_notify_conversion, | ||
| 5177 | }; | ||
| 5178 | |||
| 5179 | |||
| 5180 | |||
| 4151 | extern frame_parm_handler android_frame_parm_handlers[]; | 5181 | extern frame_parm_handler android_frame_parm_handlers[]; |
| 4152 | 5182 | ||
| 4153 | #endif /* !ANDROID_STUBIFY */ | 5183 | #endif /* !ANDROID_STUBIFY */ |
| @@ -4327,6 +5357,11 @@ android_term_init (void) | |||
| 4327 | 5357 | ||
| 4328 | /* Set the baud rate to the same value it gets set to on X. */ | 5358 | /* Set the baud rate to the same value it gets set to on X. */ |
| 4329 | baud_rate = 19200; | 5359 | baud_rate = 19200; |
| 5360 | |||
| 5361 | #ifndef ANDROID_STUBIFY | ||
| 5362 | sem_init (&edit_sem, false, 0); | ||
| 5363 | register_textconv_interface (&text_conversion_interface); | ||
| 5364 | #endif | ||
| 4330 | } | 5365 | } |
| 4331 | 5366 | ||
| 4332 | 5367 | ||
diff --git a/src/coding.c b/src/coding.c index be5a0252ede..3ac4ada2939 100644 --- a/src/coding.c +++ b/src/coding.c | |||
| @@ -11759,7 +11759,7 @@ syms_of_coding (void) | |||
| 11759 | DEFSYM (Qutf_8_unix, "utf-8-unix"); | 11759 | DEFSYM (Qutf_8_unix, "utf-8-unix"); |
| 11760 | DEFSYM (Qutf_8_emacs, "utf-8-emacs"); | 11760 | DEFSYM (Qutf_8_emacs, "utf-8-emacs"); |
| 11761 | 11761 | ||
| 11762 | #if defined (WINDOWSNT) || defined (CYGWIN) | 11762 | #if defined (WINDOWSNT) || defined (CYGWIN) || defined HAVE_ANDROID |
| 11763 | /* No, not utf-16-le: that one has a BOM. */ | 11763 | /* No, not utf-16-le: that one has a BOM. */ |
| 11764 | DEFSYM (Qutf_16le, "utf-16le"); | 11764 | DEFSYM (Qutf_16le, "utf-16le"); |
| 11765 | #endif | 11765 | #endif |
diff --git a/src/frame.c b/src/frame.c index a1d73d0799d..874e8c4cac1 100644 --- a/src/frame.c +++ b/src/frame.c | |||
| @@ -997,6 +997,16 @@ make_frame (bool mini_p) | |||
| 997 | f->select_mini_window_flag = false; | 997 | f->select_mini_window_flag = false; |
| 998 | /* This one should never be zero. */ | 998 | /* This one should never be zero. */ |
| 999 | f->change_stamp = 1; | 999 | f->change_stamp = 1; |
| 1000 | |||
| 1001 | #ifdef HAVE_TEXT_CONVERSION | ||
| 1002 | f->conversion.compose_region_start = Qnil; | ||
| 1003 | f->conversion.compose_region_end = Qnil; | ||
| 1004 | f->conversion.compose_region_overlay = Qnil; | ||
| 1005 | f->conversion.batch_edit_count = 0; | ||
| 1006 | f->conversion.batch_edit_flags = 0; | ||
| 1007 | f->conversion.actions = NULL; | ||
| 1008 | #endif | ||
| 1009 | |||
| 1000 | root_window = make_window (); | 1010 | root_window = make_window (); |
| 1001 | rw = XWINDOW (root_window); | 1011 | rw = XWINDOW (root_window); |
| 1002 | if (mini_p) | 1012 | if (mini_p) |
| @@ -2264,6 +2274,13 @@ delete_frame (Lisp_Object frame, Lisp_Object force) | |||
| 2264 | f->terminal = 0; /* Now the frame is dead. */ | 2274 | f->terminal = 0; /* Now the frame is dead. */ |
| 2265 | unblock_input (); | 2275 | unblock_input (); |
| 2266 | 2276 | ||
| 2277 | /* Clear markers and overlays set by F on behalf of an input | ||
| 2278 | method. */ | ||
| 2279 | #ifdef HAVE_TEXT_CONVERSION | ||
| 2280 | if (FRAME_WINDOW_P (f)) | ||
| 2281 | reset_frame_state (f); | ||
| 2282 | #endif | ||
| 2283 | |||
| 2267 | /* If needed, delete the terminal that this frame was on. | 2284 | /* If needed, delete the terminal that this frame was on. |
| 2268 | (This must be done after the frame is killed.) */ | 2285 | (This must be done after the frame is killed.) */ |
| 2269 | terminal->reference_count--; | 2286 | terminal->reference_count--; |
diff --git a/src/frame.h b/src/frame.h index 4405c2df860..27484936ff1 100644 --- a/src/frame.h +++ b/src/frame.h | |||
| @@ -76,6 +76,63 @@ enum ns_appearance_type | |||
| 76 | #endif | 76 | #endif |
| 77 | #endif /* HAVE_WINDOW_SYSTEM */ | 77 | #endif /* HAVE_WINDOW_SYSTEM */ |
| 78 | 78 | ||
| 79 | #ifdef HAVE_TEXT_CONVERSION | ||
| 80 | |||
| 81 | enum text_conversion_operation | ||
| 82 | { | ||
| 83 | TEXTCONV_START_BATCH_EDIT, | ||
| 84 | TEXTCONV_END_BATCH_EDIT, | ||
| 85 | TEXTCONV_COMMIT_TEXT, | ||
| 86 | TEXTCONV_FINISH_COMPOSING_TEXT, | ||
| 87 | TEXTCONV_SET_COMPOSING_TEXT, | ||
| 88 | TEXTCONV_SET_COMPOSING_REGION, | ||
| 89 | TEXTCONV_SET_POINT, | ||
| 90 | TEXTCONV_DELETE_SURROUNDING_TEXT, | ||
| 91 | }; | ||
| 92 | |||
| 93 | /* Structure describing a single edit being performed by the input | ||
| 94 | method that should be executed in the context of | ||
| 95 | kbd_buffer_get_event. */ | ||
| 96 | |||
| 97 | struct text_conversion_action | ||
| 98 | { | ||
| 99 | /* The next text conversion action. */ | ||
| 100 | struct text_conversion_action *next; | ||
| 101 | |||
| 102 | /* Any associated data. */ | ||
| 103 | Lisp_Object data; | ||
| 104 | |||
| 105 | /* The operation being performed. */ | ||
| 106 | enum text_conversion_operation operation; | ||
| 107 | |||
| 108 | /* Counter value. */ | ||
| 109 | unsigned long counter; | ||
| 110 | }; | ||
| 111 | |||
| 112 | /* Structure describing the text conversion state associated with a | ||
| 113 | frame. */ | ||
| 114 | |||
| 115 | struct text_conversion_state | ||
| 116 | { | ||
| 117 | /* List of text conversion actions associated with this frame. */ | ||
| 118 | struct text_conversion_action *actions; | ||
| 119 | |||
| 120 | /* Markers representing the composing region. */ | ||
| 121 | Lisp_Object compose_region_start, compose_region_end; | ||
| 122 | |||
| 123 | /* Overlay representing the composing region. */ | ||
| 124 | Lisp_Object compose_region_overlay; | ||
| 125 | |||
| 126 | /* The number of ongoing ``batch edits'' that are causing point | ||
| 127 | reporting to be delayed. */ | ||
| 128 | int batch_edit_count; | ||
| 129 | |||
| 130 | /* Mask containing what must be updated after batch edits end. */ | ||
| 131 | int batch_edit_flags; | ||
| 132 | }; | ||
| 133 | |||
| 134 | #endif | ||
| 135 | |||
| 79 | /* The structure representing a frame. */ | 136 | /* The structure representing a frame. */ |
| 80 | 137 | ||
| 81 | struct frame | 138 | struct frame |
| @@ -664,6 +721,11 @@ struct frame | |||
| 664 | enum ns_appearance_type ns_appearance; | 721 | enum ns_appearance_type ns_appearance; |
| 665 | bool_bf ns_transparent_titlebar; | 722 | bool_bf ns_transparent_titlebar; |
| 666 | #endif | 723 | #endif |
| 724 | |||
| 725 | #ifdef HAVE_TEXT_CONVERSION | ||
| 726 | /* Text conversion state used by certain input methods. */ | ||
| 727 | struct text_conversion_state conversion; | ||
| 728 | #endif | ||
| 667 | } GCALIGNED_STRUCT; | 729 | } GCALIGNED_STRUCT; |
| 668 | 730 | ||
| 669 | /* Most code should use these functions to set Lisp fields in struct frame. */ | 731 | /* Most code should use these functions to set Lisp fields in struct frame. */ |
diff --git a/src/keyboard.c b/src/keyboard.c index 11fa1e62c89..538fdffc199 100644 --- a/src/keyboard.c +++ b/src/keyboard.c | |||
| @@ -44,6 +44,11 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 44 | #include "atimer.h" | 44 | #include "atimer.h" |
| 45 | #include "process.h" | 45 | #include "process.h" |
| 46 | #include "menu.h" | 46 | #include "menu.h" |
| 47 | |||
| 48 | #ifdef HAVE_TEXT_CONVERSION | ||
| 49 | #include "textconv.h" | ||
| 50 | #endif | ||
| 51 | |||
| 47 | #include <errno.h> | 52 | #include <errno.h> |
| 48 | 53 | ||
| 49 | #ifdef HAVE_PTHREAD | 54 | #ifdef HAVE_PTHREAD |
| @@ -3020,6 +3025,10 @@ read_char (int commandflag, Lisp_Object map, | |||
| 3020 | { | 3025 | { |
| 3021 | struct buffer *prev_buffer = current_buffer; | 3026 | struct buffer *prev_buffer = current_buffer; |
| 3022 | last_input_event = c; | 3027 | last_input_event = c; |
| 3028 | |||
| 3029 | /* All a `text-conversion' event does is prevent Emacs from | ||
| 3030 | staying idle. It is not useful. */ | ||
| 3031 | |||
| 3023 | call4 (Qcommand_execute, tem, Qnil, Fvector (1, &last_input_event), Qt); | 3032 | call4 (Qcommand_execute, tem, Qnil, Fvector (1, &last_input_event), Qt); |
| 3024 | 3033 | ||
| 3025 | if (CONSP (c) && !NILP (Fmemq (XCAR (c), Vwhile_no_input_ignore_events)) | 3034 | if (CONSP (c) && !NILP (Fmemq (XCAR (c), Vwhile_no_input_ignore_events)) |
| @@ -3582,6 +3591,11 @@ readable_events (int flags) | |||
| 3582 | return 1; | 3591 | return 1; |
| 3583 | #endif | 3592 | #endif |
| 3584 | 3593 | ||
| 3594 | #ifdef HAVE_TEXT_CONVERSION | ||
| 3595 | if (detect_conversion_events ()) | ||
| 3596 | return 1; | ||
| 3597 | #endif | ||
| 3598 | |||
| 3585 | if (!(flags & READABLE_EVENTS_IGNORE_SQUEEZABLES) && some_mouse_moved ()) | 3599 | if (!(flags & READABLE_EVENTS_IGNORE_SQUEEZABLES) && some_mouse_moved ()) |
| 3586 | return 1; | 3600 | return 1; |
| 3587 | if (single_kboard) | 3601 | if (single_kboard) |
| @@ -3914,6 +3928,11 @@ kbd_buffer_get_event (KBOARD **kbp, | |||
| 3914 | 3928 | ||
| 3915 | had_pending_selection_requests = false; | 3929 | had_pending_selection_requests = false; |
| 3916 | #endif | 3930 | #endif |
| 3931 | #ifdef HAVE_TEXT_CONVERSION | ||
| 3932 | bool had_pending_conversion_events; | ||
| 3933 | |||
| 3934 | had_pending_conversion_events = false; | ||
| 3935 | #endif | ||
| 3917 | 3936 | ||
| 3918 | #ifdef subprocesses | 3937 | #ifdef subprocesses |
| 3919 | if (kbd_on_hold_p () && kbd_buffer_nr_stored () < KBD_BUFFER_SIZE / 4) | 3938 | if (kbd_on_hold_p () && kbd_buffer_nr_stored () < KBD_BUFFER_SIZE / 4) |
| @@ -3978,6 +3997,13 @@ kbd_buffer_get_event (KBOARD **kbp, | |||
| 3978 | break; | 3997 | break; |
| 3979 | } | 3998 | } |
| 3980 | #endif | 3999 | #endif |
| 4000 | #ifdef HAVE_TEXT_CONVERSION | ||
| 4001 | if (detect_conversion_events ()) | ||
| 4002 | { | ||
| 4003 | had_pending_conversion_events = true; | ||
| 4004 | break; | ||
| 4005 | } | ||
| 4006 | #endif | ||
| 3981 | if (end_time) | 4007 | if (end_time) |
| 3982 | { | 4008 | { |
| 3983 | struct timespec now = current_timespec (); | 4009 | struct timespec now = current_timespec (); |
| @@ -4024,6 +4050,14 @@ kbd_buffer_get_event (KBOARD **kbp, | |||
| 4024 | x_handle_pending_selection_requests (); | 4050 | x_handle_pending_selection_requests (); |
| 4025 | #endif | 4051 | #endif |
| 4026 | 4052 | ||
| 4053 | #ifdef HAVE_TEXT_CONVERSION | ||
| 4054 | /* Handle pending ``text conversion'' requests from an input | ||
| 4055 | method. */ | ||
| 4056 | |||
| 4057 | if (had_pending_conversion_events) | ||
| 4058 | handle_pending_conversion_events (); | ||
| 4059 | #endif | ||
| 4060 | |||
| 4027 | if (CONSP (Vunread_command_events)) | 4061 | if (CONSP (Vunread_command_events)) |
| 4028 | { | 4062 | { |
| 4029 | Lisp_Object first; | 4063 | Lisp_Object first; |
| @@ -4381,11 +4415,24 @@ kbd_buffer_get_event (KBOARD **kbp, | |||
| 4381 | else if (had_pending_selection_requests) | 4415 | else if (had_pending_selection_requests) |
| 4382 | obj = Qnil; | 4416 | obj = Qnil; |
| 4383 | #endif | 4417 | #endif |
| 4418 | #ifdef HAVE_TEXT_CONVERSION | ||
| 4419 | /* This is an internal event used to prevent Emacs from becoming | ||
| 4420 | idle immediately after a text conversion operation. */ | ||
| 4421 | else if (had_pending_conversion_events) | ||
| 4422 | obj = Qtext_conversion; | ||
| 4423 | #endif | ||
| 4384 | else | 4424 | else |
| 4385 | /* We were promised by the above while loop that there was | 4425 | /* We were promised by the above while loop that there was |
| 4386 | something for us to read! */ | 4426 | something for us to read! */ |
| 4387 | emacs_abort (); | 4427 | emacs_abort (); |
| 4388 | 4428 | ||
| 4429 | #ifdef HAVE_TEXT_CONVERSION | ||
| 4430 | /* While not implemented as keyboard commands, changes made by the | ||
| 4431 | input method still mean that Emacs is no longer idle. */ | ||
| 4432 | if (had_pending_conversion_events) | ||
| 4433 | timer_stop_idle (); | ||
| 4434 | #endif | ||
| 4435 | |||
| 4389 | input_pending = readable_events (0); | 4436 | input_pending = readable_events (0); |
| 4390 | 4437 | ||
| 4391 | Vlast_event_frame = internal_last_event_frame; | 4438 | Vlast_event_frame = internal_last_event_frame; |
| @@ -12902,6 +12949,9 @@ See also `pre-command-hook'. */); | |||
| 12902 | 12949 | ||
| 12903 | DEFSYM (Qcoding, "coding"); | 12950 | DEFSYM (Qcoding, "coding"); |
| 12904 | DEFSYM (Qtouchscreen, "touchscreen"); | 12951 | DEFSYM (Qtouchscreen, "touchscreen"); |
| 12952 | #ifdef HAVE_TEXT_CONVERSION | ||
| 12953 | DEFSYM (Qtext_conversion, "text-conversion"); | ||
| 12954 | #endif | ||
| 12905 | 12955 | ||
| 12906 | Fset (Qecho_area_clear_hook, Qnil); | 12956 | Fset (Qecho_area_clear_hook, Qnil); |
| 12907 | 12957 | ||
diff --git a/src/lisp.h b/src/lisp.h index 894b939b7b9..2dc51d32481 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -5230,7 +5230,10 @@ extern char *emacs_root_dir (void); | |||
| 5230 | 5230 | ||
| 5231 | #ifdef HAVE_TEXT_CONVERSION | 5231 | #ifdef HAVE_TEXT_CONVERSION |
| 5232 | /* Defined in textconv.c. */ | 5232 | /* Defined in textconv.c. */ |
| 5233 | extern void reset_frame_state (struct frame *); | ||
| 5233 | extern void report_selected_window_change (struct frame *); | 5234 | extern void report_selected_window_change (struct frame *); |
| 5235 | extern void report_point_change (struct frame *, struct window *, | ||
| 5236 | struct buffer *); | ||
| 5234 | #endif | 5237 | #endif |
| 5235 | 5238 | ||
| 5236 | #ifdef HAVE_NATIVE_COMP | 5239 | #ifdef HAVE_NATIVE_COMP |
diff --git a/src/process.c b/src/process.c index 9a8b0d7fd85..e7ccb2c604e 100644 --- a/src/process.c +++ b/src/process.c | |||
| @@ -121,6 +121,7 @@ static struct rlimit nofile_limit; | |||
| 121 | 121 | ||
| 122 | #ifdef HAVE_ANDROID | 122 | #ifdef HAVE_ANDROID |
| 123 | #include "android.h" | 123 | #include "android.h" |
| 124 | #include "androidterm.h" | ||
| 124 | #endif | 125 | #endif |
| 125 | 126 | ||
| 126 | #ifdef HAVE_WINDOW_SYSTEM | 127 | #ifdef HAVE_WINDOW_SYSTEM |
diff --git a/src/textconv.c b/src/textconv.c index d5db6d11717..b62ed7d1365 100644 --- a/src/textconv.c +++ b/src/textconv.c | |||
| @@ -25,7 +25,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 25 | ability to ``undo'' or ``edit'' previously composed text. This is | 25 | ability to ``undo'' or ``edit'' previously composed text. This is |
| 26 | most commonly seen in input methods for CJK laguages for X Windows, | 26 | most commonly seen in input methods for CJK laguages for X Windows, |
| 27 | and is extensively used throughout Android by input methods for all | 27 | and is extensively used throughout Android by input methods for all |
| 28 | kinds of scripts. */ | 28 | kinds of scripts. |
| 29 | |||
| 30 | In addition, these input methods may also need to make detailed | ||
| 31 | edits to the content of a buffer. That is also handled here. */ | ||
| 29 | 32 | ||
| 30 | #include <config.h> | 33 | #include <config.h> |
| 31 | 34 | ||
| @@ -44,6 +47,15 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 44 | 47 | ||
| 45 | static struct textconv_interface *text_interface; | 48 | static struct textconv_interface *text_interface; |
| 46 | 49 | ||
| 50 | /* Flags used to determine what must be sent after a batch edit | ||
| 51 | ends. */ | ||
| 52 | |||
| 53 | enum textconv_batch_edit_flags | ||
| 54 | { | ||
| 55 | PENDING_POINT_CHANGE = 1, | ||
| 56 | PENDING_COMPOSE_CHANGE = 2, | ||
| 57 | }; | ||
| 58 | |||
| 47 | 59 | ||
| 48 | 60 | ||
| 49 | /* Copy the portion of the current buffer described by BEG, BEG_BYTE, | 61 | /* Copy the portion of the current buffer described by BEG, BEG_BYTE, |
| @@ -94,14 +106,18 @@ copy_buffer (ptrdiff_t beg, ptrdiff_t beg_byte, | |||
| 94 | Then, either delete that text from the buffer if QUERY->operation | 106 | Then, either delete that text from the buffer if QUERY->operation |
| 95 | is TEXTCONV_SUBSTITUTION, or return 0. | 107 | is TEXTCONV_SUBSTITUTION, or return 0. |
| 96 | 108 | ||
| 109 | If FLAGS & TEXTCONV_SKIP_CONVERSION_REGION, then first move PT past | ||
| 110 | the conversion region in the specified direction if it is inside. | ||
| 111 | |||
| 97 | Value is 0 if QUERY->operation was not TEXTCONV_SUBSTITUTION | 112 | Value is 0 if QUERY->operation was not TEXTCONV_SUBSTITUTION |
| 98 | or if deleting the text was successful, and 1 otherwise. */ | 113 | or if deleting the text was successful, and 1 otherwise. */ |
| 99 | 114 | ||
| 100 | int | 115 | int |
| 101 | textconv_query (struct frame *f, struct textconv_callback_struct *query) | 116 | textconv_query (struct frame *f, struct textconv_callback_struct *query, |
| 117 | int flags) | ||
| 102 | { | 118 | { |
| 103 | specpdl_ref count; | 119 | specpdl_ref count; |
| 104 | ptrdiff_t pos, pos_byte, end, end_byte; | 120 | ptrdiff_t pos, pos_byte, end, end_byte, start; |
| 105 | ptrdiff_t temp, temp1; | 121 | ptrdiff_t temp, temp1; |
| 106 | char *buffer; | 122 | char *buffer; |
| 107 | 123 | ||
| @@ -113,14 +129,46 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query) | |||
| 113 | /* Inhibit quitting. */ | 129 | /* Inhibit quitting. */ |
| 114 | specbind (Qinhibit_quit, Qt); | 130 | specbind (Qinhibit_quit, Qt); |
| 115 | 131 | ||
| 116 | /* Temporarily switch to F's selected window. */ | 132 | /* Temporarily switch to F's selected window at the time of the last |
| 117 | Fselect_window (f->selected_window, Qt); | 133 | redisplay. */ |
| 134 | Fselect_window ((WINDOW_LIVE_P (f->old_selected_window) | ||
| 135 | ? f->old_selected_window | ||
| 136 | : f->selected_window), Qt); | ||
| 118 | 137 | ||
| 119 | /* Now find the appropriate text bounds for QUERY. First, move | 138 | /* Now find the appropriate text bounds for QUERY. First, move |
| 120 | point QUERY->position steps forward or backwards. */ | 139 | point QUERY->position steps forward or backwards. */ |
| 121 | 140 | ||
| 122 | pos = PT; | 141 | pos = PT; |
| 123 | 142 | ||
| 143 | /* Next, if POS lies within the conversion region and the caller | ||
| 144 | asked for it to be moved away, move it away from the conversion | ||
| 145 | region. */ | ||
| 146 | |||
| 147 | if (flags & TEXTCONV_SKIP_CONVERSION_REGION | ||
| 148 | && MARKERP (f->conversion.compose_region_start)) | ||
| 149 | { | ||
| 150 | start = marker_position (f->conversion.compose_region_start); | ||
| 151 | end = marker_position (f->conversion.compose_region_end); | ||
| 152 | |||
| 153 | if (pos >= start && pos < end) | ||
| 154 | { | ||
| 155 | switch (query->direction) | ||
| 156 | { | ||
| 157 | case TEXTCONV_FORWARD_CHAR: | ||
| 158 | case TEXTCONV_FORWARD_WORD: | ||
| 159 | case TEXTCONV_CARET_DOWN: | ||
| 160 | case TEXTCONV_NEXT_LINE: | ||
| 161 | case TEXTCONV_LINE_START: | ||
| 162 | pos = end; | ||
| 163 | break; | ||
| 164 | |||
| 165 | default: | ||
| 166 | pos = max (BEGV, start - 1); | ||
| 167 | break; | ||
| 168 | } | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 124 | /* If pos is outside the accessible part of the buffer or if it | 172 | /* If pos is outside the accessible part of the buffer or if it |
| 125 | overflows, move back to point or to the extremes of the | 173 | overflows, move back to point or to the extremes of the |
| 126 | accessible region. */ | 174 | accessible region. */ |
| @@ -287,6 +335,828 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query) | |||
| 287 | return 0; | 335 | return 0; |
| 288 | } | 336 | } |
| 289 | 337 | ||
| 338 | /* Reset F's text conversion state. Delete any overlays or | ||
| 339 | markers inside. */ | ||
| 340 | |||
| 341 | void | ||
| 342 | reset_frame_state (struct frame *f) | ||
| 343 | { | ||
| 344 | struct text_conversion_action *last, *next; | ||
| 345 | |||
| 346 | /* Make the composition region markers point elsewhere. */ | ||
| 347 | |||
| 348 | if (!NILP (f->conversion.compose_region_start)) | ||
| 349 | { | ||
| 350 | Fset_marker (f->conversion.compose_region_start, Qnil, Qnil); | ||
| 351 | Fset_marker (f->conversion.compose_region_end, Qnil, Qnil); | ||
| 352 | f->conversion.compose_region_start = Qnil; | ||
| 353 | f->conversion.compose_region_end = Qnil; | ||
| 354 | } | ||
| 355 | |||
| 356 | /* Delete the composition region overlay. */ | ||
| 357 | |||
| 358 | if (!NILP (f->conversion.compose_region_overlay)) | ||
| 359 | Fdelete_overlay (f->conversion.compose_region_overlay); | ||
| 360 | |||
| 361 | /* Delete each text conversion action queued up. */ | ||
| 362 | |||
| 363 | next = f->conversion.actions; | ||
| 364 | while (next) | ||
| 365 | { | ||
| 366 | last = next; | ||
| 367 | next = next->next; | ||
| 368 | |||
| 369 | /* Say that the conversion is finished. */ | ||
| 370 | if (text_interface && text_interface->notify_conversion) | ||
| 371 | text_interface->notify_conversion (last->counter); | ||
| 372 | |||
| 373 | xfree (last); | ||
| 374 | } | ||
| 375 | f->conversion.actions = NULL; | ||
| 376 | |||
| 377 | /* Clear batch edit state. */ | ||
| 378 | f->conversion.batch_edit_count = 0; | ||
| 379 | f->conversion.batch_edit_flags = 0; | ||
| 380 | } | ||
| 381 | |||
| 382 | /* Return whether or not there are pending edits from an input method | ||
| 383 | on any frame. */ | ||
| 384 | |||
| 385 | bool | ||
| 386 | detect_conversion_events (void) | ||
| 387 | { | ||
| 388 | Lisp_Object tail, frame; | ||
| 389 | |||
| 390 | FOR_EACH_FRAME (tail, frame) | ||
| 391 | { | ||
| 392 | if (XFRAME (frame)->conversion.actions) | ||
| 393 | return true; | ||
| 394 | } | ||
| 395 | |||
| 396 | return false; | ||
| 397 | } | ||
| 398 | |||
| 399 | /* Restore the selected window WINDOW. */ | ||
| 400 | |||
| 401 | static void | ||
| 402 | restore_selected_window (Lisp_Object window) | ||
| 403 | { | ||
| 404 | /* FIXME: not sure what to do if WINDOW has been deleted. */ | ||
| 405 | Fselect_window (window, Qt); | ||
| 406 | } | ||
| 407 | |||
| 408 | /* Commit the given text in the composing region. If there is no | ||
| 409 | composing region, then insert the text after F's selected window's | ||
| 410 | last point instead. Finally, remove the composing region. */ | ||
| 411 | |||
| 412 | static void | ||
| 413 | really_commit_text (struct frame *f, EMACS_INT position, | ||
| 414 | Lisp_Object text) | ||
| 415 | { | ||
| 416 | specpdl_ref count; | ||
| 417 | ptrdiff_t wanted, start, end; | ||
| 418 | |||
| 419 | /* If F's old selected window is no longer live, fail. */ | ||
| 420 | |||
| 421 | if (!WINDOW_LIVE_P (f->old_selected_window)) | ||
| 422 | return; | ||
| 423 | |||
| 424 | count = SPECPDL_INDEX (); | ||
| 425 | record_unwind_protect (restore_selected_window, | ||
| 426 | selected_window); | ||
| 427 | |||
| 428 | /* Temporarily switch to F's selected window at the time of the last | ||
| 429 | redisplay. */ | ||
| 430 | Fselect_window (f->old_selected_window, Qt); | ||
| 431 | |||
| 432 | /* Now detect whether or not there is a composing region. | ||
| 433 | If there is, then replace it with TEXT. Don't do that | ||
| 434 | otherwise. */ | ||
| 435 | |||
| 436 | if (MARKERP (f->conversion.compose_region_start)) | ||
| 437 | { | ||
| 438 | /* Replace its contents. */ | ||
| 439 | start = marker_position (f->conversion.compose_region_start); | ||
| 440 | end = marker_position (f->conversion.compose_region_end); | ||
| 441 | safe_del_range (start, end); | ||
| 442 | Finsert (1, &text); | ||
| 443 | |||
| 444 | /* Move to a the position specified in POSITION. */ | ||
| 445 | |||
| 446 | if (position < 0) | ||
| 447 | { | ||
| 448 | wanted | ||
| 449 | = marker_position (f->conversion.compose_region_start); | ||
| 450 | |||
| 451 | if (INT_SUBTRACT_WRAPV (wanted, position, &wanted) | ||
| 452 | || wanted < BEGV) | ||
| 453 | wanted = BEGV; | ||
| 454 | |||
| 455 | if (wanted > ZV) | ||
| 456 | wanted = ZV; | ||
| 457 | |||
| 458 | set_point (wanted); | ||
| 459 | } | ||
| 460 | else | ||
| 461 | { | ||
| 462 | wanted | ||
| 463 | = marker_position (f->conversion.compose_region_end); | ||
| 464 | |||
| 465 | if (INT_ADD_WRAPV (wanted, position - 1, &wanted) | ||
| 466 | || wanted > ZV) | ||
| 467 | wanted = ZV; | ||
| 468 | |||
| 469 | if (wanted < BEGV) | ||
| 470 | wanted = BEGV; | ||
| 471 | |||
| 472 | set_point (wanted); | ||
| 473 | } | ||
| 474 | |||
| 475 | /* Make the composition region markers point elsewhere. */ | ||
| 476 | |||
| 477 | if (!NILP (f->conversion.compose_region_start)) | ||
| 478 | { | ||
| 479 | Fset_marker (f->conversion.compose_region_start, Qnil, Qnil); | ||
| 480 | Fset_marker (f->conversion.compose_region_end, Qnil, Qnil); | ||
| 481 | f->conversion.compose_region_start = Qnil; | ||
| 482 | f->conversion.compose_region_end = Qnil; | ||
| 483 | } | ||
| 484 | |||
| 485 | /* Delete the composition region overlay. */ | ||
| 486 | |||
| 487 | if (!NILP (f->conversion.compose_region_overlay)) | ||
| 488 | Fdelete_overlay (f->conversion.compose_region_overlay); | ||
| 489 | } | ||
| 490 | else | ||
| 491 | { | ||
| 492 | /* Otherwise, move the text and point to an appropriate | ||
| 493 | location. */ | ||
| 494 | wanted = PT; | ||
| 495 | Finsert (1, &text); | ||
| 496 | |||
| 497 | if (position < 0) | ||
| 498 | { | ||
| 499 | if (INT_SUBTRACT_WRAPV (wanted, position, &wanted) | ||
| 500 | || wanted < BEGV) | ||
| 501 | wanted = BEGV; | ||
| 502 | |||
| 503 | if (wanted > ZV) | ||
| 504 | wanted = ZV; | ||
| 505 | |||
| 506 | set_point (wanted); | ||
| 507 | } | ||
| 508 | else | ||
| 509 | { | ||
| 510 | wanted = PT; | ||
| 511 | |||
| 512 | if (INT_ADD_WRAPV (wanted, position - 1, &wanted) | ||
| 513 | || wanted > ZV) | ||
| 514 | wanted = ZV; | ||
| 515 | |||
| 516 | if (wanted < BEGV) | ||
| 517 | wanted = BEGV; | ||
| 518 | |||
| 519 | set_point (wanted); | ||
| 520 | } | ||
| 521 | } | ||
| 522 | |||
| 523 | unbind_to (count, Qnil); | ||
| 524 | } | ||
| 525 | |||
| 526 | /* Remove the composition region on the frame F, while leaving its | ||
| 527 | contents intact. */ | ||
| 528 | |||
| 529 | static void | ||
| 530 | really_finish_composing_text (struct frame *f) | ||
| 531 | { | ||
| 532 | if (!NILP (f->conversion.compose_region_start)) | ||
| 533 | { | ||
| 534 | Fset_marker (f->conversion.compose_region_start, Qnil, Qnil); | ||
| 535 | Fset_marker (f->conversion.compose_region_end, Qnil, Qnil); | ||
| 536 | f->conversion.compose_region_start = Qnil; | ||
| 537 | f->conversion.compose_region_end = Qnil; | ||
| 538 | } | ||
| 539 | |||
| 540 | /* Delete the composition region overlay. */ | ||
| 541 | |||
| 542 | if (!NILP (f->conversion.compose_region_overlay)) | ||
| 543 | Fdelete_overlay (f->conversion.compose_region_overlay); | ||
| 544 | } | ||
| 545 | |||
| 546 | /* Set the composing text on F to TEXT. Then, move point to an | ||
| 547 | appropriate position relative to POSITION, and call | ||
| 548 | `compose_region_changed' in the text conversion interface should | ||
| 549 | point not have been changed relative to F's old selected window's | ||
| 550 | last point. */ | ||
| 551 | |||
| 552 | static void | ||
| 553 | really_set_composing_text (struct frame *f, ptrdiff_t position, | ||
| 554 | Lisp_Object text) | ||
| 555 | { | ||
| 556 | specpdl_ref count; | ||
| 557 | ptrdiff_t start, wanted, end; | ||
| 558 | struct window *w; | ||
| 559 | |||
| 560 | /* If F's old selected window is no longer live, fail. */ | ||
| 561 | |||
| 562 | if (!WINDOW_LIVE_P (f->old_selected_window)) | ||
| 563 | return; | ||
| 564 | |||
| 565 | count = SPECPDL_INDEX (); | ||
| 566 | record_unwind_protect (restore_selected_window, | ||
| 567 | selected_window); | ||
| 568 | |||
| 569 | /* Temporarily switch to F's selected window at the time of the last | ||
| 570 | redisplay. */ | ||
| 571 | w = XWINDOW (f->old_selected_window); | ||
| 572 | Fselect_window (f->old_selected_window, Qt); | ||
| 573 | |||
| 574 | /* Now set up the composition region if necessary. */ | ||
| 575 | |||
| 576 | if (!MARKERP (f->conversion.compose_region_start)) | ||
| 577 | { | ||
| 578 | f->conversion.compose_region_start = Fmake_marker (); | ||
| 579 | f->conversion.compose_region_end = Fmake_marker (); | ||
| 580 | Fset_marker (f->conversion.compose_region_start, | ||
| 581 | Fpoint (), Qnil); | ||
| 582 | Fset_marker (f->conversion.compose_region_end, | ||
| 583 | Fpoint (), Qnil); | ||
| 584 | Fset_marker_insertion_type (f->conversion.compose_region_end, | ||
| 585 | Qt); | ||
| 586 | } | ||
| 587 | else | ||
| 588 | { | ||
| 589 | /* Delete the text between the start of the composition region | ||
| 590 | and its end. TODO: avoid this widening. */ | ||
| 591 | record_unwind_protect (save_restriction_restore, | ||
| 592 | save_restriction_save ()); | ||
| 593 | Fwiden (); | ||
| 594 | start = marker_position (f->conversion.compose_region_start); | ||
| 595 | end = marker_position (f->conversion.compose_region_end); | ||
| 596 | safe_del_range (start, end); | ||
| 597 | set_point (start); | ||
| 598 | } | ||
| 599 | |||
| 600 | /* Insert the new text. */ | ||
| 601 | Finsert (1, &text); | ||
| 602 | |||
| 603 | /* Now move point to an appropriate location. */ | ||
| 604 | if (position < 0) | ||
| 605 | { | ||
| 606 | wanted = start; | ||
| 607 | |||
| 608 | if (INT_SUBTRACT_WRAPV (wanted, position, &wanted) | ||
| 609 | || wanted < BEGV) | ||
| 610 | wanted = BEGV; | ||
| 611 | |||
| 612 | if (wanted > ZV) | ||
| 613 | wanted = ZV; | ||
| 614 | } | ||
| 615 | else | ||
| 616 | { | ||
| 617 | end = marker_position (f->conversion.compose_region_end); | ||
| 618 | wanted = end; | ||
| 619 | |||
| 620 | /* end should be PT after the edit. */ | ||
| 621 | eassert (end == PT); | ||
| 622 | |||
| 623 | if (INT_ADD_WRAPV (wanted, position - 1, &wanted) | ||
| 624 | || wanted > ZV) | ||
| 625 | wanted = ZV; | ||
| 626 | |||
| 627 | if (wanted < BEGV) | ||
| 628 | wanted = BEGV; | ||
| 629 | } | ||
| 630 | |||
| 631 | set_point (wanted); | ||
| 632 | |||
| 633 | /* If PT hasn't changed, the conversion region definitely has. | ||
| 634 | Otherwise, redisplay will update the input method instead. */ | ||
| 635 | |||
| 636 | if (PT == w->last_point | ||
| 637 | && text_interface | ||
| 638 | && text_interface->compose_region_changed) | ||
| 639 | { | ||
| 640 | if (f->conversion.batch_edit_count > 0) | ||
| 641 | f->conversion.batch_edit_flags |= PENDING_COMPOSE_CHANGE; | ||
| 642 | else | ||
| 643 | text_interface->compose_region_changed (f); | ||
| 644 | } | ||
| 645 | |||
| 646 | unbind_to (count, Qnil); | ||
| 647 | } | ||
| 648 | |||
| 649 | /* Set the composing region to START by END. Make it that it is not | ||
| 650 | already set. */ | ||
| 651 | |||
| 652 | static void | ||
| 653 | really_set_composing_region (struct frame *f, ptrdiff_t start, | ||
| 654 | ptrdiff_t end) | ||
| 655 | { | ||
| 656 | specpdl_ref count; | ||
| 657 | |||
| 658 | /* If F's old selected window is no longer live, fail. */ | ||
| 659 | |||
| 660 | if (!WINDOW_LIVE_P (f->old_selected_window)) | ||
| 661 | return; | ||
| 662 | |||
| 663 | /* If MAX (0, start) == end, then this should behave the same as | ||
| 664 | really_finish_composing_text. */ | ||
| 665 | |||
| 666 | if (max (0, start) == max (0, end)) | ||
| 667 | { | ||
| 668 | really_finish_composing_text (f); | ||
| 669 | return; | ||
| 670 | } | ||
| 671 | |||
| 672 | count = SPECPDL_INDEX (); | ||
| 673 | record_unwind_protect (restore_selected_window, | ||
| 674 | selected_window); | ||
| 675 | |||
| 676 | /* Temporarily switch to F's selected window at the time of the last | ||
| 677 | redisplay. */ | ||
| 678 | Fselect_window (f->old_selected_window, Qt); | ||
| 679 | |||
| 680 | /* Now set up the composition region if necessary. */ | ||
| 681 | |||
| 682 | if (!MARKERP (f->conversion.compose_region_start)) | ||
| 683 | { | ||
| 684 | f->conversion.compose_region_start = Fmake_marker (); | ||
| 685 | f->conversion.compose_region_end = Fmake_marker (); | ||
| 686 | Fset_marker_insertion_type (f->conversion.compose_region_end, | ||
| 687 | Qt); | ||
| 688 | } | ||
| 689 | |||
| 690 | Fset_marker (f->conversion.compose_region_start, | ||
| 691 | make_fixnum (start), Qnil); | ||
| 692 | Fset_marker (f->conversion.compose_region_end, | ||
| 693 | make_fixnum (end), Qnil); | ||
| 694 | |||
| 695 | unbind_to (count, Qnil); | ||
| 696 | } | ||
| 697 | |||
| 698 | /* Delete LEFT and RIGHT chars around point. */ | ||
| 699 | |||
| 700 | static void | ||
| 701 | really_delete_surrounding_text (struct frame *f, ptrdiff_t left, | ||
| 702 | ptrdiff_t right) | ||
| 703 | { | ||
| 704 | specpdl_ref count; | ||
| 705 | ptrdiff_t start, end; | ||
| 706 | |||
| 707 | /* If F's old selected window is no longer live, fail. */ | ||
| 708 | |||
| 709 | if (!WINDOW_LIVE_P (f->old_selected_window)) | ||
| 710 | return; | ||
| 711 | |||
| 712 | count = SPECPDL_INDEX (); | ||
| 713 | record_unwind_protect (restore_selected_window, | ||
| 714 | selected_window); | ||
| 715 | |||
| 716 | /* Temporarily switch to F's selected window at the time of the last | ||
| 717 | redisplay. */ | ||
| 718 | Fselect_window (f->old_selected_window, Qt); | ||
| 719 | |||
| 720 | start = max (BEGV, PT - left); | ||
| 721 | end = min (ZV, PT + right); | ||
| 722 | |||
| 723 | safe_del_range (start, end); | ||
| 724 | unbind_to (count, Qnil); | ||
| 725 | } | ||
| 726 | |||
| 727 | /* Set point in F to POSITION. | ||
| 728 | |||
| 729 | If it has not changed, signal an update through the text input | ||
| 730 | interface, which is necessary for the IME to acknowledge that the | ||
| 731 | change has completed. */ | ||
| 732 | |||
| 733 | static void | ||
| 734 | really_set_point (struct frame *f, ptrdiff_t point) | ||
| 735 | { | ||
| 736 | specpdl_ref count; | ||
| 737 | |||
| 738 | /* If F's old selected window is no longer live, fail. */ | ||
| 739 | |||
| 740 | if (!WINDOW_LIVE_P (f->old_selected_window)) | ||
| 741 | return; | ||
| 742 | |||
| 743 | count = SPECPDL_INDEX (); | ||
| 744 | record_unwind_protect (restore_selected_window, | ||
| 745 | selected_window); | ||
| 746 | |||
| 747 | /* Temporarily switch to F's selected window at the time of the last | ||
| 748 | redisplay. */ | ||
| 749 | Fselect_window (f->old_selected_window, Qt); | ||
| 750 | |||
| 751 | if (point == PT) | ||
| 752 | { | ||
| 753 | if (f->conversion.batch_edit_count > 0) | ||
| 754 | f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE; | ||
| 755 | else | ||
| 756 | text_interface->point_changed (f, | ||
| 757 | XWINDOW (f->old_selected_window), | ||
| 758 | current_buffer); | ||
| 759 | } | ||
| 760 | else | ||
| 761 | /* Set the point. */ | ||
| 762 | Fgoto_char (make_fixnum (point)); | ||
| 763 | |||
| 764 | unbind_to (count, Qnil); | ||
| 765 | } | ||
| 766 | |||
| 767 | /* Complete the edit specified by the counter value inside *TOKEN. */ | ||
| 768 | |||
| 769 | static void | ||
| 770 | complete_edit (void *token) | ||
| 771 | { | ||
| 772 | if (text_interface && text_interface->notify_conversion) | ||
| 773 | text_interface->notify_conversion (*(unsigned long *) token); | ||
| 774 | } | ||
| 775 | |||
| 776 | /* Process and free the text conversion ACTION. F must be the frame | ||
| 777 | on which ACTION will be performed. */ | ||
| 778 | |||
| 779 | static void | ||
| 780 | handle_pending_conversion_events_1 (struct frame *f, | ||
| 781 | struct text_conversion_action *action) | ||
| 782 | { | ||
| 783 | Lisp_Object data; | ||
| 784 | enum text_conversion_operation operation; | ||
| 785 | struct buffer *buffer; | ||
| 786 | struct window *w; | ||
| 787 | specpdl_ref count; | ||
| 788 | unsigned long token; | ||
| 789 | |||
| 790 | /* Next, process this action and free it. */ | ||
| 791 | |||
| 792 | data = action->data; | ||
| 793 | operation = action->operation; | ||
| 794 | token = action->counter; | ||
| 795 | xfree (action); | ||
| 796 | |||
| 797 | /* Make sure completion is signalled. */ | ||
| 798 | count = SPECPDL_INDEX (); | ||
| 799 | record_unwind_protect_ptr (complete_edit, &token); | ||
| 800 | |||
| 801 | switch (operation) | ||
| 802 | { | ||
| 803 | case TEXTCONV_START_BATCH_EDIT: | ||
| 804 | f->conversion.batch_edit_count++; | ||
| 805 | break; | ||
| 806 | |||
| 807 | case TEXTCONV_END_BATCH_EDIT: | ||
| 808 | if (f->conversion.batch_edit_count > 0) | ||
| 809 | f->conversion.batch_edit_count--; | ||
| 810 | |||
| 811 | if (!WINDOW_LIVE_P (f->old_selected_window)) | ||
| 812 | break; | ||
| 813 | |||
| 814 | if (f->conversion.batch_edit_flags & PENDING_POINT_CHANGE) | ||
| 815 | { | ||
| 816 | w = XWINDOW (f->old_selected_window); | ||
| 817 | buffer = XBUFFER (WINDOW_BUFFER (w)); | ||
| 818 | |||
| 819 | text_interface->point_changed (f, w, buffer); | ||
| 820 | } | ||
| 821 | |||
| 822 | if (f->conversion.batch_edit_flags & PENDING_COMPOSE_CHANGE) | ||
| 823 | text_interface->compose_region_changed (f); | ||
| 824 | |||
| 825 | f->conversion.batch_edit_flags = 0; | ||
| 826 | break; | ||
| 827 | |||
| 828 | case TEXTCONV_COMMIT_TEXT: | ||
| 829 | really_commit_text (f, XFIXNUM (XCAR (data)), XCDR (data)); | ||
| 830 | break; | ||
| 831 | |||
| 832 | case TEXTCONV_FINISH_COMPOSING_TEXT: | ||
| 833 | really_finish_composing_text (f); | ||
| 834 | break; | ||
| 835 | |||
| 836 | case TEXTCONV_SET_COMPOSING_TEXT: | ||
| 837 | really_set_composing_text (f, XFIXNUM (XCAR (data)), | ||
| 838 | XCDR (data)); | ||
| 839 | break; | ||
| 840 | |||
| 841 | case TEXTCONV_SET_COMPOSING_REGION: | ||
| 842 | really_set_composing_region (f, XFIXNUM (XCAR (data)), | ||
| 843 | XFIXNUM (XCDR (data))); | ||
| 844 | break; | ||
| 845 | |||
| 846 | case TEXTCONV_SET_POINT: | ||
| 847 | really_set_point (f, XFIXNUM (data)); | ||
| 848 | break; | ||
| 849 | |||
| 850 | case TEXTCONV_DELETE_SURROUNDING_TEXT: | ||
| 851 | really_delete_surrounding_text (f, XFIXNUM (XCAR (data)), | ||
| 852 | XFIXNUM (XCDR (data))); | ||
| 853 | break; | ||
| 854 | } | ||
| 855 | |||
| 856 | unbind_to (count, Qnil); | ||
| 857 | } | ||
| 858 | |||
| 859 | /* Process any outstanding text conversion events. | ||
| 860 | This may run Lisp or signal. */ | ||
| 861 | |||
| 862 | void | ||
| 863 | handle_pending_conversion_events (void) | ||
| 864 | { | ||
| 865 | struct frame *f; | ||
| 866 | Lisp_Object tail, frame; | ||
| 867 | struct text_conversion_action *action, *next; | ||
| 868 | bool handled; | ||
| 869 | |||
| 870 | handled = false; | ||
| 871 | |||
| 872 | FOR_EACH_FRAME (tail, frame) | ||
| 873 | { | ||
| 874 | f = XFRAME (frame); | ||
| 875 | |||
| 876 | /* Test if F has any outstanding conversion events. Then | ||
| 877 | process them in bottom to up order. */ | ||
| 878 | for (action = f->conversion.actions; action; action = next) | ||
| 879 | { | ||
| 880 | /* Redisplay in between if there is more than one | ||
| 881 | action. */ | ||
| 882 | |||
| 883 | if (handled) | ||
| 884 | redisplay (); | ||
| 885 | |||
| 886 | /* Unlink this action. */ | ||
| 887 | next = action->next; | ||
| 888 | f->conversion.actions = next; | ||
| 889 | |||
| 890 | /* Handle and free the action. */ | ||
| 891 | handle_pending_conversion_events_1 (f, action); | ||
| 892 | handled = true; | ||
| 893 | } | ||
| 894 | } | ||
| 895 | } | ||
| 896 | |||
| 897 | /* Start a ``batch edit'' in F. During a batch edit, point_changed | ||
| 898 | will not be called until the batch edit ends. | ||
| 899 | |||
| 900 | Process the actual operation in the event loop in keyboard.c; then, | ||
| 901 | call `notify_conversion' in the text conversion interface with | ||
| 902 | COUNTER. */ | ||
| 903 | |||
| 904 | void | ||
| 905 | start_batch_edit (struct frame *f, unsigned long counter) | ||
| 906 | { | ||
| 907 | struct text_conversion_action *action, **last; | ||
| 908 | |||
| 909 | action = xmalloc (sizeof *action); | ||
| 910 | action->operation = TEXTCONV_START_BATCH_EDIT; | ||
| 911 | action->data = Qnil; | ||
| 912 | action->next = NULL; | ||
| 913 | action->counter = counter; | ||
| 914 | for (last = &f->conversion.actions; *last; last = &(*last)->next) | ||
| 915 | ;; | ||
| 916 | *last = action; | ||
| 917 | input_pending = true; | ||
| 918 | } | ||
| 919 | |||
| 920 | /* End a ``batch edit''. It is ok to call this function even if a | ||
| 921 | batch edit has not yet started, in which case it does nothing. | ||
| 922 | |||
| 923 | COUNTER means the same as in `start_batch_edit'. */ | ||
| 924 | |||
| 925 | void | ||
| 926 | end_batch_edit (struct frame *f, unsigned long counter) | ||
| 927 | { | ||
| 928 | struct text_conversion_action *action, **last; | ||
| 929 | |||
| 930 | action = xmalloc (sizeof *action); | ||
| 931 | action->operation = TEXTCONV_END_BATCH_EDIT; | ||
| 932 | action->data = Qnil; | ||
| 933 | action->next = NULL; | ||
| 934 | action->counter = counter; | ||
| 935 | for (last = &f->conversion.actions; *last; last = &(*last)->next) | ||
| 936 | ;; | ||
| 937 | *last = action; | ||
| 938 | input_pending = true; | ||
| 939 | } | ||
| 940 | |||
| 941 | /* Insert the specified STRING into F's current buffer's composition | ||
| 942 | region, and set point to POSITION relative to STRING. | ||
| 943 | |||
| 944 | COUNTER means the same as in `start_batch_edit'. */ | ||
| 945 | |||
| 946 | void | ||
| 947 | commit_text (struct frame *f, Lisp_Object string, | ||
| 948 | ptrdiff_t position, unsigned long counter) | ||
| 949 | { | ||
| 950 | struct text_conversion_action *action, **last; | ||
| 951 | |||
| 952 | action = xmalloc (sizeof *action); | ||
| 953 | action->operation = TEXTCONV_COMMIT_TEXT; | ||
| 954 | action->data = Fcons (make_fixnum (position), string); | ||
| 955 | action->next = NULL; | ||
| 956 | action->counter = counter; | ||
| 957 | for (last = &f->conversion.actions; *last; last = &(*last)->next) | ||
| 958 | ;; | ||
| 959 | *last = action; | ||
| 960 | input_pending = true; | ||
| 961 | } | ||
| 962 | |||
| 963 | /* Remove the composition region and its overlay from F's current | ||
| 964 | buffer. Leave the text being composed intact. | ||
| 965 | |||
| 966 | COUNTER means the same as in `start_batch_edit'. */ | ||
| 967 | |||
| 968 | void | ||
| 969 | finish_composing_text (struct frame *f, unsigned long counter) | ||
| 970 | { | ||
| 971 | struct text_conversion_action *action, **last; | ||
| 972 | |||
| 973 | action = xmalloc (sizeof *action); | ||
| 974 | action->operation = TEXTCONV_FINISH_COMPOSING_TEXT; | ||
| 975 | action->data = Qnil; | ||
| 976 | action->next = NULL; | ||
| 977 | action->counter = counter; | ||
| 978 | for (last = &f->conversion.actions; *last; last = &(*last)->next) | ||
| 979 | ;; | ||
| 980 | *last = action; | ||
| 981 | input_pending = true; | ||
| 982 | } | ||
| 983 | |||
| 984 | /* Insert the given STRING and make it the currently active | ||
| 985 | composition. | ||
| 986 | |||
| 987 | If there is currently no composing region, then the new value of | ||
| 988 | point is used as the composing region. | ||
| 989 | |||
| 990 | Then, the composing region is replaced with the text in the | ||
| 991 | specified string. | ||
| 992 | |||
| 993 | Finally, move point to new_point, which is relative to either the | ||
| 994 | start or the end of OBJECT depending on whether or not it is less | ||
| 995 | than zero. | ||
| 996 | |||
| 997 | COUNTER means the same as in `start_batch_edit'. */ | ||
| 998 | |||
| 999 | void | ||
| 1000 | set_composing_text (struct frame *f, Lisp_Object object, | ||
| 1001 | ptrdiff_t new_point, unsigned long counter) | ||
| 1002 | { | ||
| 1003 | struct text_conversion_action *action, **last; | ||
| 1004 | |||
| 1005 | action = xmalloc (sizeof *action); | ||
| 1006 | action->operation = TEXTCONV_SET_COMPOSING_TEXT; | ||
| 1007 | action->data = Fcons (make_fixnum (new_point), | ||
| 1008 | object); | ||
| 1009 | action->next = NULL; | ||
| 1010 | action->counter = counter; | ||
| 1011 | for (last = &f->conversion.actions; *last; last = &(*last)->next) | ||
| 1012 | ;; | ||
| 1013 | *last = action; | ||
| 1014 | input_pending = true; | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | /* Make the region between START and END the currently active | ||
| 1018 | ``composing region''. | ||
| 1019 | |||
| 1020 | The ``composing region'' is a region of text in the buffer that is | ||
| 1021 | about to undergo editing by the input method. */ | ||
| 1022 | |||
| 1023 | void | ||
| 1024 | set_composing_region (struct frame *f, ptrdiff_t start, | ||
| 1025 | ptrdiff_t end, unsigned long counter) | ||
| 1026 | { | ||
| 1027 | struct text_conversion_action *action, **last; | ||
| 1028 | |||
| 1029 | if (start > MOST_POSITIVE_FIXNUM) | ||
| 1030 | start = MOST_POSITIVE_FIXNUM; | ||
| 1031 | |||
| 1032 | if (end > MOST_POSITIVE_FIXNUM) | ||
| 1033 | end = MOST_POSITIVE_FIXNUM; | ||
| 1034 | |||
| 1035 | action = xmalloc (sizeof *action); | ||
| 1036 | action->operation = TEXTCONV_SET_COMPOSING_REGION; | ||
| 1037 | action->data = Fcons (make_fixnum (start), | ||
| 1038 | make_fixnum (end)); | ||
| 1039 | action->next = NULL; | ||
| 1040 | action->counter = counter; | ||
| 1041 | for (last = &f->conversion.actions; *last; last = &(*last)->next) | ||
| 1042 | ;; | ||
| 1043 | *last = action; | ||
| 1044 | input_pending = true; | ||
| 1045 | } | ||
| 1046 | |||
| 1047 | /* Move point in F's selected buffer to POINT. | ||
| 1048 | |||
| 1049 | COUNTER means the same as in `start_batch_edit'. */ | ||
| 1050 | |||
| 1051 | void | ||
| 1052 | textconv_set_point (struct frame *f, ptrdiff_t point, | ||
| 1053 | unsigned long counter) | ||
| 1054 | { | ||
| 1055 | struct text_conversion_action *action, **last; | ||
| 1056 | |||
| 1057 | if (point > MOST_POSITIVE_FIXNUM) | ||
| 1058 | point = MOST_POSITIVE_FIXNUM; | ||
| 1059 | |||
| 1060 | action = xmalloc (sizeof *action); | ||
| 1061 | action->operation = TEXTCONV_SET_POINT; | ||
| 1062 | action->data = make_fixnum (point); | ||
| 1063 | action->next = NULL; | ||
| 1064 | action->counter = counter; | ||
| 1065 | for (last = &f->conversion.actions; *last; last = &(*last)->next) | ||
| 1066 | ;; | ||
| 1067 | *last = action; | ||
| 1068 | input_pending = true; | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | /* Delete LEFT and RIGHT characters around point in F's old selected | ||
| 1072 | window. */ | ||
| 1073 | |||
| 1074 | void | ||
| 1075 | delete_surrounding_text (struct frame *f, ptrdiff_t left, | ||
| 1076 | ptrdiff_t right, unsigned long counter) | ||
| 1077 | { | ||
| 1078 | struct text_conversion_action *action, **last; | ||
| 1079 | |||
| 1080 | action = xmalloc (sizeof *action); | ||
| 1081 | action->operation = TEXTCONV_DELETE_SURROUNDING_TEXT; | ||
| 1082 | action->data = Fcons (make_fixnum (left), | ||
| 1083 | make_fixnum (right)); | ||
| 1084 | action->next = NULL; | ||
| 1085 | action->counter = counter; | ||
| 1086 | for (last = &f->conversion.actions; *last; last = &(*last)->next) | ||
| 1087 | ;; | ||
| 1088 | *last = action; | ||
| 1089 | input_pending = true; | ||
| 1090 | } | ||
| 1091 | |||
| 1092 | /* Return N characters of text around point in F's old selected | ||
| 1093 | window. | ||
| 1094 | |||
| 1095 | Set *N to the actual number of characters returned, *START_RETURN | ||
| 1096 | to the position of the first character returned, *OFFSET to the | ||
| 1097 | offset of point within that text, *LENGTH to the actual number of | ||
| 1098 | characters returned, and *BYTES to the actual number of bytes | ||
| 1099 | returned. | ||
| 1100 | |||
| 1101 | Value is NULL upon failure, and a malloced string upon success. */ | ||
| 1102 | |||
| 1103 | char * | ||
| 1104 | get_extracted_text (struct frame *f, ptrdiff_t n, | ||
| 1105 | ptrdiff_t *start_return, | ||
| 1106 | ptrdiff_t *offset, ptrdiff_t *length, | ||
| 1107 | ptrdiff_t *bytes) | ||
| 1108 | { | ||
| 1109 | specpdl_ref count; | ||
| 1110 | ptrdiff_t start, end, start_byte, end_byte; | ||
| 1111 | char *buffer; | ||
| 1112 | |||
| 1113 | if (!WINDOW_LIVE_P (f->old_selected_window)) | ||
| 1114 | return NULL; | ||
| 1115 | |||
| 1116 | /* Save the excursion, as there will be extensive changes to the | ||
| 1117 | selected window. */ | ||
| 1118 | count = SPECPDL_INDEX (); | ||
| 1119 | record_unwind_protect_excursion (); | ||
| 1120 | |||
| 1121 | /* Inhibit quitting. */ | ||
| 1122 | specbind (Qinhibit_quit, Qt); | ||
| 1123 | |||
| 1124 | /* Temporarily switch to F's selected window at the time of the last | ||
| 1125 | redisplay. */ | ||
| 1126 | Fselect_window (f->old_selected_window, Qt); | ||
| 1127 | |||
| 1128 | /* Figure out the bounds of the text to return. */ | ||
| 1129 | start = PT - n / 2; | ||
| 1130 | end = PT + n - n / 2; | ||
| 1131 | start = max (start, BEGV); | ||
| 1132 | end = min (end, ZV); | ||
| 1133 | buffer = NULL; | ||
| 1134 | |||
| 1135 | /* Detect overflow. */ | ||
| 1136 | |||
| 1137 | if (!(start <= PT <= end)) | ||
| 1138 | goto finish; | ||
| 1139 | |||
| 1140 | /* Convert the character positions to byte positions. */ | ||
| 1141 | start_byte = CHAR_TO_BYTE (start); | ||
| 1142 | end_byte = CHAR_TO_BYTE (end); | ||
| 1143 | |||
| 1144 | /* Extract the text from the buffer. */ | ||
| 1145 | buffer = xmalloc (end_byte - start_byte); | ||
| 1146 | copy_buffer (start, start_byte, end, end_byte, | ||
| 1147 | buffer); | ||
| 1148 | |||
| 1149 | /* Return the offsets. */ | ||
| 1150 | *start_return = start; | ||
| 1151 | *offset = PT - start; | ||
| 1152 | *length = end - start; | ||
| 1153 | *bytes = end_byte - start_byte; | ||
| 1154 | |||
| 1155 | finish: | ||
| 1156 | unbind_to (count, Qnil); | ||
| 1157 | return buffer; | ||
| 1158 | } | ||
| 1159 | |||
| 290 | 1160 | ||
| 291 | 1161 | ||
| 292 | /* Window system interface. These are called from the rest of | 1162 | /* Window system interface. These are called from the rest of |
| @@ -298,16 +1168,40 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query) | |||
| 298 | void | 1168 | void |
| 299 | report_selected_window_change (struct frame *f) | 1169 | report_selected_window_change (struct frame *f) |
| 300 | { | 1170 | { |
| 1171 | reset_frame_state (f); | ||
| 1172 | |||
| 301 | if (!text_interface) | 1173 | if (!text_interface) |
| 302 | return; | 1174 | return; |
| 303 | 1175 | ||
| 304 | text_interface->reset (f); | 1176 | text_interface->reset (f); |
| 305 | } | 1177 | } |
| 306 | 1178 | ||
| 1179 | /* Notice that the point in F's selected window's current buffer has | ||
| 1180 | changed. | ||
| 1181 | |||
| 1182 | F is the frame whose selected window was changed, W is the window | ||
| 1183 | in question, and BUFFER is that window's current buffer. | ||
| 1184 | |||
| 1185 | Tell the text conversion interface about the change; it will likely | ||
| 1186 | pass the information on to the system input method. */ | ||
| 1187 | |||
| 1188 | void | ||
| 1189 | report_point_change (struct frame *f, struct window *window, | ||
| 1190 | struct buffer *buffer) | ||
| 1191 | { | ||
| 1192 | if (!text_interface || !text_interface->point_changed) | ||
| 1193 | return; | ||
| 1194 | |||
| 1195 | if (f->conversion.batch_edit_count > 0) | ||
| 1196 | f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE; | ||
| 1197 | else | ||
| 1198 | text_interface->point_changed (f, window, buffer); | ||
| 1199 | } | ||
| 1200 | |||
| 307 | /* Register INTERFACE as the text conversion interface. */ | 1201 | /* Register INTERFACE as the text conversion interface. */ |
| 308 | 1202 | ||
| 309 | void | 1203 | void |
| 310 | register_texconv_interface (struct textconv_interface *interface) | 1204 | register_textconv_interface (struct textconv_interface *interface) |
| 311 | { | 1205 | { |
| 312 | text_interface = interface; | 1206 | text_interface = interface; |
| 313 | } | 1207 | } |
diff --git a/src/textconv.h b/src/textconv.h index f6e7eb7925f..ce003847637 100644 --- a/src/textconv.h +++ b/src/textconv.h | |||
| @@ -34,6 +34,20 @@ struct textconv_interface | |||
| 34 | happen if the window is deleted or switches buffers, or an | 34 | happen if the window is deleted or switches buffers, or an |
| 35 | unexpected buffer change occurs.) */ | 35 | unexpected buffer change occurs.) */ |
| 36 | void (*reset) (struct frame *); | 36 | void (*reset) (struct frame *); |
| 37 | |||
| 38 | /* Notice that point has moved in the specified frame's selected | ||
| 39 | window's selected buffer. The second argument is the window | ||
| 40 | whose point changed, and the third argument is the buffer. */ | ||
| 41 | void (*point_changed) (struct frame *, struct window *, | ||
| 42 | struct buffer *); | ||
| 43 | |||
| 44 | /* Notice that the preconversion region has changed without point | ||
| 45 | being moved. */ | ||
| 46 | void (*compose_region_changed) (struct frame *); | ||
| 47 | |||
| 48 | /* Notice that an asynch conversion identified by COUNTER has | ||
| 49 | completed. */ | ||
| 50 | void (*notify_conversion) (unsigned long); | ||
| 37 | }; | 51 | }; |
| 38 | 52 | ||
| 39 | 53 | ||
| @@ -103,7 +117,27 @@ struct textconv_callback_struct | |||
| 103 | struct textconv_conversion_text text; | 117 | struct textconv_conversion_text text; |
| 104 | }; | 118 | }; |
| 105 | 119 | ||
| 106 | extern int textconv_query (struct frame *, struct textconv_callback_struct *); | 120 | #define TEXTCONV_SKIP_CONVERSION_REGION (1 << 0) |
| 107 | extern void register_texconv_interface (struct textconv_interface *); | 121 | |
| 122 | extern int textconv_query (struct frame *, struct textconv_callback_struct *, | ||
| 123 | int); | ||
| 124 | extern bool detect_conversion_events (void); | ||
| 125 | extern void handle_pending_conversion_events (void); | ||
| 126 | extern void start_batch_edit (struct frame *, unsigned long); | ||
| 127 | extern void end_batch_edit (struct frame *, unsigned long); | ||
| 128 | extern void commit_text (struct frame *, Lisp_Object, ptrdiff_t, | ||
| 129 | unsigned long); | ||
| 130 | extern void finish_composing_text (struct frame *, unsigned long); | ||
| 131 | extern void set_composing_text (struct frame *, Lisp_Object, | ||
| 132 | ptrdiff_t, unsigned long); | ||
| 133 | extern void set_composing_region (struct frame *, ptrdiff_t, ptrdiff_t, | ||
| 134 | unsigned long); | ||
| 135 | extern void textconv_set_point (struct frame *, ptrdiff_t, unsigned long); | ||
| 136 | extern void delete_surrounding_text (struct frame *, ptrdiff_t, | ||
| 137 | ptrdiff_t, unsigned long); | ||
| 138 | extern char *get_extracted_text (struct frame *, ptrdiff_t, ptrdiff_t *, | ||
| 139 | ptrdiff_t *, ptrdiff_t *, ptrdiff_t *); | ||
| 140 | |||
| 141 | extern void register_textconv_interface (struct textconv_interface *); | ||
| 108 | 142 | ||
| 109 | #endif /* _TEXTCONV_H_ */ | 143 | #endif /* _TEXTCONV_H_ */ |
diff --git a/src/xdisp.c b/src/xdisp.c index 8bbb80b1c8c..4b91fcf31ca 100644 --- a/src/xdisp.c +++ b/src/xdisp.c | |||
| @@ -17267,6 +17267,9 @@ static void | |||
| 17267 | mark_window_display_accurate_1 (struct window *w, bool accurate_p) | 17267 | mark_window_display_accurate_1 (struct window *w, bool accurate_p) |
| 17268 | { | 17268 | { |
| 17269 | struct buffer *b = XBUFFER (w->contents); | 17269 | struct buffer *b = XBUFFER (w->contents); |
| 17270 | #ifdef HAVE_TEXT_CONVERSION | ||
| 17271 | ptrdiff_t prev_point; | ||
| 17272 | #endif | ||
| 17270 | 17273 | ||
| 17271 | w->last_modified = accurate_p ? BUF_MODIFF (b) : 0; | 17274 | w->last_modified = accurate_p ? BUF_MODIFF (b) : 0; |
| 17272 | w->last_overlay_modified = accurate_p ? BUF_OVERLAY_MODIFF (b) : 0; | 17275 | w->last_overlay_modified = accurate_p ? BUF_OVERLAY_MODIFF (b) : 0; |
| @@ -17296,11 +17299,36 @@ mark_window_display_accurate_1 (struct window *w, bool accurate_p) | |||
| 17296 | w->last_cursor_vpos = w->cursor.vpos; | 17299 | w->last_cursor_vpos = w->cursor.vpos; |
| 17297 | w->last_cursor_off_p = w->cursor_off_p; | 17300 | w->last_cursor_off_p = w->cursor_off_p; |
| 17298 | 17301 | ||
| 17302 | #ifdef HAVE_TEXT_CONVERSION | ||
| 17303 | prev_point = w->last_point; | ||
| 17304 | #endif | ||
| 17305 | |||
| 17299 | if (w == XWINDOW (selected_window)) | 17306 | if (w == XWINDOW (selected_window)) |
| 17300 | w->last_point = BUF_PT (b); | 17307 | w->last_point = BUF_PT (b); |
| 17301 | else | 17308 | else |
| 17302 | w->last_point = marker_position (w->pointm); | 17309 | w->last_point = marker_position (w->pointm); |
| 17303 | 17310 | ||
| 17311 | #ifdef HAVE_TEXT_CONVERSION | ||
| 17312 | /* Point motion is only propagated to the input method for use | ||
| 17313 | in text conversion during a redisplay. While this can lead | ||
| 17314 | to inconsistencies when point has moved but the change has | ||
| 17315 | not yet been displayed, it leads to better results most of | ||
| 17316 | the time, as point often changes within calls to | ||
| 17317 | `save-excursion', and the only way to detect such calls is to | ||
| 17318 | observe that the next redisplay never ends with those changes | ||
| 17319 | applied. | ||
| 17320 | |||
| 17321 | Changes to buffer text are immediately propagated to the | ||
| 17322 | input method, and the position of point is also updated | ||
| 17323 | during such a change, so the consequences are not that | ||
| 17324 | severe. */ | ||
| 17325 | |||
| 17326 | if (prev_point != w->last_point | ||
| 17327 | && FRAME_WINDOW_P (WINDOW_XFRAME (w)) | ||
| 17328 | && w == XWINDOW (WINDOW_XFRAME (w)->selected_window)) | ||
| 17329 | report_point_change (WINDOW_XFRAME (w), w, b); | ||
| 17330 | #endif | ||
| 17331 | |||
| 17304 | w->window_end_valid = true; | 17332 | w->window_end_valid = true; |
| 17305 | w->update_mode_line = false; | 17333 | w->update_mode_line = false; |
| 17306 | w->preserve_vscroll_p = false; | 17334 | w->preserve_vscroll_p = false; |
diff --git a/src/xfns.c b/src/xfns.c index 9e004f6a678..0e4a25de04a 100644 --- a/src/xfns.c +++ b/src/xfns.c | |||
| @@ -3861,7 +3861,7 @@ xic_string_conversion_callback (XIC ic, XPointer client_data, | |||
| 3861 | request.operation = TEXTCONV_RETRIEVAL; | 3861 | request.operation = TEXTCONV_RETRIEVAL; |
| 3862 | 3862 | ||
| 3863 | /* Now perform the string conversion. */ | 3863 | /* Now perform the string conversion. */ |
| 3864 | rc = textconv_query (f, &request); | 3864 | rc = textconv_query (f, &request, 0); |
| 3865 | 3865 | ||
| 3866 | if (rc) | 3866 | if (rc) |
| 3867 | { | 3867 | { |
diff --git a/src/xterm.c b/src/xterm.c index ccaacec2c57..49b88de2759 100644 --- a/src/xterm.c +++ b/src/xterm.c | |||
| @@ -31615,7 +31615,7 @@ init_xterm (void) | |||
| 31615 | #endif | 31615 | #endif |
| 31616 | 31616 | ||
| 31617 | #ifdef HAVE_X_I18N | 31617 | #ifdef HAVE_X_I18N |
| 31618 | register_texconv_interface (&text_conversion_interface); | 31618 | register_textconv_interface (&text_conversion_interface); |
| 31619 | #endif | 31619 | #endif |
| 31620 | } | 31620 | } |
| 31621 | 31621 | ||