aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPo Lu2023-02-15 12:23:03 +0800
committerPo Lu2023-02-15 12:23:03 +0800
commita158c1d5b964fda36f752998cef076d581dc4df4 (patch)
treeb9c7a22a7259dbd876be0e047304a338ef05e334 /src
parent5a7855e84abd56e55b456aef4a43ae7623e76899 (diff)
downloademacs-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.c14
-rw-r--r--src/android.c295
-rw-r--r--src/android.h10
-rw-r--r--src/androidgui.h60
-rw-r--r--src/androidterm.c1037
-rw-r--r--src/coding.c2
-rw-r--r--src/frame.c17
-rw-r--r--src/frame.h62
-rw-r--r--src/keyboard.c50
-rw-r--r--src/lisp.h3
-rw-r--r--src/process.c1
-rw-r--r--src/textconv.c906
-rw-r--r--src/textconv.h38
-rw-r--r--src/xdisp.c28
-rw-r--r--src/xfns.c2
-rw-r--r--src/xterm.c2
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
6939mark_frame (struct Lisp_Vector *ptr) 6939mark_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
6955static void 6969static 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
103struct android_emacs_pixmap 105struct 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. */
210static unsigned int event_serial; 212unsigned int event_serial;
211 213
212/* Unused pointer used to control compiler optimizations. */ 214/* Unused pointer used to control compiler optimizations. */
213void *unused_pointer; 215void *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. */
415static 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
541static void 548void
542android_write_event (union android_event *event) 549android_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
1837extern JNIEXPORT void JNICALL 1856JNIEXPORT void JNICALL
1838NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv, 1857NATIVE_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
1931extern JNIEXPORT void JNICALL 1950JNIEXPORT void JNICALL
1932NATIVE_NAME (emacsAbort) (JNIEnv *env, jobject object) 1951NATIVE_NAME (emacsAbort) (JNIEnv *env, jobject object)
1933{ 1952{
1934 emacs_abort (); 1953 emacs_abort ();
1935} 1954}
1936 1955
1937extern JNIEXPORT void JNICALL 1956JNIEXPORT void JNICALL
1938NATIVE_NAME (quit) (JNIEnv *env, jobject object) 1957NATIVE_NAME (quit) (JNIEnv *env, jobject object)
1939{ 1958{
1940 Vquit_flag = Qt; 1959 Vquit_flag = Qt;
1941} 1960}
1942 1961
1943extern JNIEXPORT jlong JNICALL 1962JNIEXPORT jlong JNICALL
1944NATIVE_NAME (sendConfigureNotify) (JNIEnv *env, jobject object, 1963NATIVE_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
1964extern JNIEXPORT jlong JNICALL 1983JNIEXPORT jlong JNICALL
1965NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object, 1984NATIVE_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
1984extern JNIEXPORT jlong JNICALL 2003JNIEXPORT jlong JNICALL
1985NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object, 2004NATIVE_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
2004extern JNIEXPORT jlong JNICALL 2023JNIEXPORT jlong JNICALL
2005NATIVE_NAME (sendFocusIn) (JNIEnv *env, jobject object, 2024NATIVE_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
2019extern JNIEXPORT jlong JNICALL 2038JNIEXPORT jlong JNICALL
2020NATIVE_NAME (sendFocusOut) (JNIEnv *env, jobject object, 2039NATIVE_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
2034extern JNIEXPORT jlong JNICALL 2053JNIEXPORT jlong JNICALL
2035NATIVE_NAME (sendWindowAction) (JNIEnv *env, jobject object, 2054NATIVE_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
2049extern JNIEXPORT jlong JNICALL 2068JNIEXPORT jlong JNICALL
2050NATIVE_NAME (sendEnterNotify) (JNIEnv *env, jobject object, 2069NATIVE_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
2067extern JNIEXPORT jlong JNICALL 2086JNIEXPORT jlong JNICALL
2068NATIVE_NAME (sendLeaveNotify) (JNIEnv *env, jobject object, 2087NATIVE_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
2085extern JNIEXPORT jlong JNICALL 2104JNIEXPORT jlong JNICALL
2086NATIVE_NAME (sendMotionNotify) (JNIEnv *env, jobject object, 2105NATIVE_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
2103extern JNIEXPORT jlong JNICALL 2122JNIEXPORT jlong JNICALL
2104NATIVE_NAME (sendButtonPress) (JNIEnv *env, jobject object, 2123NATIVE_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
2124extern JNIEXPORT jlong JNICALL 2143JNIEXPORT jlong JNICALL
2125NATIVE_NAME (sendButtonRelease) (JNIEnv *env, jobject object, 2144NATIVE_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
2145extern JNIEXPORT jlong JNICALL 2164JNIEXPORT jlong JNICALL
2146NATIVE_NAME (sendTouchDown) (JNIEnv *env, jobject object, 2165NATIVE_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
2164extern JNIEXPORT jlong JNICALL 2183JNIEXPORT jlong JNICALL
2165NATIVE_NAME (sendTouchUp) (JNIEnv *env, jobject object, 2184NATIVE_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
2183extern JNIEXPORT jlong JNICALL 2202JNIEXPORT jlong JNICALL
2184NATIVE_NAME (sendTouchMove) (JNIEnv *env, jobject object, 2203NATIVE_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
2202extern JNIEXPORT jlong JNICALL 2221JNIEXPORT jlong JNICALL
2203NATIVE_NAME (sendWheel) (JNIEnv *env, jobject object, 2222NATIVE_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
2224extern JNIEXPORT jlong JNICALL 2243JNIEXPORT jlong JNICALL
2225NATIVE_NAME (sendIconified) (JNIEnv *env, jobject object, 2244NATIVE_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
2238extern JNIEXPORT jlong JNICALL 2257JNIEXPORT jlong JNICALL
2239NATIVE_NAME (sendDeiconified) (JNIEnv *env, jobject object, 2258NATIVE_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
2252extern JNIEXPORT jlong JNICALL 2271JNIEXPORT jlong JNICALL
2253NATIVE_NAME (sendContextMenu) (JNIEnv *env, jobject object, 2272NATIVE_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
2267extern JNIEXPORT jlong JNICALL 2286JNIEXPORT jlong JNICALL
2268NATIVE_NAME (sendExpose) (JNIEnv *env, jobject object, 2287NATIVE_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
2307static void android_begin_query (void);
2308static void android_end_query (void);
2309
2310JNIEXPORT void JNICALL
2311NATIVE_NAME (beginSynchronous) (JNIEnv *env, jobject object)
2312{
2313 android_begin_query ();
2314}
2315
2316JNIEXPORT void JNICALL
2317NATIVE_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. */
5195static bool android_servicing_query;
5196
5197/* Function that is waiting to be run in the Emacs thread. */
5198static void (*android_query_function) (void *);
5199
5200/* Context for that function. */
5201static 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
5224void
5225android_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
5257static void
5258android_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
5277static void
5278android_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
5294int
5295android_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
5349void
5350android_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
5387void
5388android_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
109extern Lisp_Object android_browse_url (Lisp_Object); 109extern Lisp_Object android_browse_url (Lisp_Object);
110 110
111
112
113/* Event loop functions. */
114
115extern void android_check_query (void);
116extern int android_run_in_emacs_thread (void (*) (void *), void *);
117extern void android_write_event (union android_event *);
118
119extern 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
240struct android_any_event 241struct 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
423enum 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
435struct 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
422union android_event 469union 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
452enum 502enum
@@ -463,6 +513,13 @@ enum android_lookup_status
463 ANDROID_LOOKUP_BOTH, 513 ANDROID_LOOKUP_BOTH,
464 }; 514 };
465 515
516enum android_ic_mode
517 {
518 ANDROID_IC_MODE_NULL = 0,
519 ANDROID_IC_MODE_ACTION = 1,
520 ANDROID_IC_MODE_TEXT = 2,
521 };
522
466extern int android_pending (void); 523extern int android_pending (void);
467extern void android_next_event (union android_event *); 524extern void android_next_event (union android_event *);
468 525
@@ -554,6 +611,9 @@ extern void android_sync (void);
554extern int android_wc_lookup_string (android_key_pressed_event *, 611extern 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 *);
614extern void android_update_ic (android_window, ptrdiff_t, ptrdiff_t,
615 ptrdiff_t, ptrdiff_t);
616extern 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
62static struct frame * 73static struct frame *
63android_window_to_frame (struct android_display_info *dpyinfo, 74android_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
545static Lisp_Object
546android_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
572static void
573android_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
530static int 638static int
531handle_one_android_event (struct android_display_info *dpyinfo, 639handle_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. */
4295static unsigned long edit_counter;
4296
4297/* The last counter known to have completed. */
4298static unsigned long last_edit_counter;
4299
4300/* Semaphore posted every time the counter increases. */
4301static 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
4311static void
4312android_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
4345static unsigned short *
4346android_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
4374JNIEXPORT void JNICALL
4375NATIVE_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
4393JNIEXPORT void JNICALL
4394NATIVE_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
4412JNIEXPORT void JNICALL
4413NATIVE_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
4444JNIEXPORT void JNICALL
4445NATIVE_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
4476JNIEXPORT void JNICALL
4477NATIVE_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
4497JNIEXPORT void JNICALL
4498NATIVE_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
4519struct 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
4539static void
4540android_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
4575static jstring
4576android_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
4651JNIEXPORT jstring JNICALL
4652NATIVE_NAME (getSelectedText) (JNIEnv *env, jobject object,
4653 jshort window)
4654{
4655 return NULL;
4656}
4657
4658JNIEXPORT jstring JNICALL
4659NATIVE_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
4700JNIEXPORT jstring JNICALL
4701NATIVE_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
4742JNIEXPORT void JNICALL
4743NATIVE_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
4775JNIEXPORT void JNICALL
4776NATIVE_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
4795JNIEXPORT void JNICALL
4796NATIVE_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
4818struct 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
4831static void
4832android_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
4856JNIEXPORT jint JNICALL
4857NATIVE_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
4874JNIEXPORT void JNICALL
4875NATIVE_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
4896struct 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
4920static void
4921android_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
4944struct 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
4954struct 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
4966JNIEXPORT jobject JNICALL
4967NATIVE_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
5081static void
5082android_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
5115static void
5116android_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
5136static void
5137android_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
5146static void
5147android_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
5154static void
5155android_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
5171static 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
4151extern frame_parm_handler android_frame_parm_handlers[]; 5181extern 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
81enum 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
97struct 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
115struct 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
81struct frame 138struct 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. */
5233extern void reset_frame_state (struct frame *);
5233extern void report_selected_window_change (struct frame *); 5234extern void report_selected_window_change (struct frame *);
5235extern 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
45static struct textconv_interface *text_interface; 48static struct textconv_interface *text_interface;
46 49
50/* Flags used to determine what must be sent after a batch edit
51 ends. */
52
53enum 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
100int 115int
101textconv_query (struct frame *f, struct textconv_callback_struct *query) 116textconv_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
341void
342reset_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
385bool
386detect_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
401static void
402restore_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
412static void
413really_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
529static void
530really_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
552static void
553really_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
652static void
653really_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
700static void
701really_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
733static void
734really_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
769static void
770complete_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
779static void
780handle_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
862void
863handle_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
904void
905start_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
925void
926end_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
946void
947commit_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
968void
969finish_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
999void
1000set_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
1023void
1024set_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
1051void
1052textconv_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
1074void
1075delete_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
1103char *
1104get_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)
298void 1168void
299report_selected_window_change (struct frame *f) 1169report_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
1188void
1189report_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
309void 1203void
310register_texconv_interface (struct textconv_interface *interface) 1204register_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
106extern int textconv_query (struct frame *, struct textconv_callback_struct *); 120#define TEXTCONV_SKIP_CONVERSION_REGION (1 << 0)
107extern void register_texconv_interface (struct textconv_interface *); 121
122extern int textconv_query (struct frame *, struct textconv_callback_struct *,
123 int);
124extern bool detect_conversion_events (void);
125extern void handle_pending_conversion_events (void);
126extern void start_batch_edit (struct frame *, unsigned long);
127extern void end_batch_edit (struct frame *, unsigned long);
128extern void commit_text (struct frame *, Lisp_Object, ptrdiff_t,
129 unsigned long);
130extern void finish_composing_text (struct frame *, unsigned long);
131extern void set_composing_text (struct frame *, Lisp_Object,
132 ptrdiff_t, unsigned long);
133extern void set_composing_region (struct frame *, ptrdiff_t, ptrdiff_t,
134 unsigned long);
135extern void textconv_set_point (struct frame *, ptrdiff_t, unsigned long);
136extern void delete_surrounding_text (struct frame *, ptrdiff_t,
137 ptrdiff_t, unsigned long);
138extern char *get_extracted_text (struct frame *, ptrdiff_t, ptrdiff_t *,
139 ptrdiff_t *, ptrdiff_t *, ptrdiff_t *);
140
141extern 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
17267mark_window_display_accurate_1 (struct window *w, bool accurate_p) 17267mark_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