diff options
| author | Po Lu | 2023-02-15 12:23:03 +0800 |
|---|---|---|
| committer | Po Lu | 2023-02-15 12:23:03 +0800 |
| commit | a158c1d5b964fda36f752998cef076d581dc4df4 (patch) | |
| tree | b9c7a22a7259dbd876be0e047304a338ef05e334 /src/androidterm.c | |
| parent | 5a7855e84abd56e55b456aef4a43ae7623e76899 (diff) | |
| download | emacs-a158c1d5b964fda36f752998cef076d581dc4df4.tar.gz emacs-a158c1d5b964fda36f752998cef076d581dc4df4.zip | |
Update Android port
* configure.ac (HAVE_TEXT_CONVERSION): Define on Android.
* doc/emacs/input.texi (On-Screen Keyboards): Document ``text
conversion'' slightly.
* doc/lispref/commands.texi (Misc Events): Document new
`text-conversion' event.
* java/org/gnu/emacs/EmacsContextMenu.java (display): Use
`syncRunnable'.
* java/org/gnu/emacs/EmacsDialog.java (display): Likewise.
* java/org/gnu/emacs/EmacsEditable.java: Delete file.
* java/org/gnu/emacs/EmacsInputConnection.java
(EmacsInputConnection): Reimplement from scratch.
* java/org/gnu/emacs/EmacsNative.java (EmacsNative): Add new
functions.
* java/org/gnu/emacs/EmacsService.java (EmacsService, getEmacsView)
(getLocationOnScreen, sync, getClipboardManager, restartEmacs):
Use syncRunnable.
(syncRunnable): New function.
(updateIC, resetIC): New functions.
* java/org/gnu/emacs/EmacsView.java (EmacsView): New field
`inputConnection' and `icMode'.
(onCreateInputConnection): Update accordingly.
(setICMode, getICMode): New functions.
* lisp/bindings.el (global-map): Ignore text conversion events.
* src/alloc.c (mark_frame): Mark text conversion data.
* src/android.c (struct android_emacs_service): New fields
`update_ic' and `reset_ic'.
(event_serial): Export.
(android_query_sem): New function.
(android_init_events): Initialize new semaphore.
(android_write_event): Export.
(android_select): Check for UI thread code.
(setEmacsParams, android_init_emacs_service): Initialize new
methods.
(android_check_query, android_begin_query, android_end_query)
(android_run_in_emacs_thread):
(android_update_ic, android_reset_ic): New functions for
managing synchronous queries from one thread to another.
* src/android.h: Export new functions.
* src/androidgui.h (enum android_event_type): Add input method
events.
(enum android_ime_operation, struct android_ime_event)
(union android_event, enum android_ic_mode): New structs and
enums.
* src/androidterm.c (android_window_to_frame): Allow DPYINFO to
be NULL.
(android_decode_utf16, android_handle_ime_event)
(handle_one_android_event, android_sync_edit)
(android_copy_java_string, beginBatchEdit, endBatchEdit)
(commitCompletion, deleteSurroundingText, finishComposingText)
(getSelectedtext, getTextAfterCursor, getTextBeforeCursor)
(setComposingText, setComposingRegion, setSelection, getSelection)
(performEditorAction, getExtractedText): New functions.
(struct android_conversion_query_context):
(android_perform_conversion_query):
(android_text_to_string):
(struct android_get_selection_context):
(android_get_selection):
(struct android_get_extracted_text_context):
(android_get_extracted_text):
(struct android_extracted_text_request_class):
(struct android_extracted_text_class):
(android_update_selection):
(android_reset_conversion):
(android_set_point):
(android_compose_region_changed):
(android_notify_conversion):
(text_conversion_interface): New functions and structures.
(android_term_init): Initialize text conversion.
* src/coding.c (syms_of_coding): Define Qutf_16le on Android.
* src/frame.c (make_frame): Clear conversion data.
(delete_frame): Reset conversion state.
* src/frame.h (enum text_conversion_operation)
(struct text_conversion_action, struct text_conversion_state)
(GCALIGNED_STRUCT): Update structures.
* src/keyboard.c (read_char, readable_events, kbd_buffer_get_event)
(syms_of_keyboard): Handle text conversion events.
* src/lisp.h:
* src/process.c: Fix includes.
* src/textconv.c (enum textconv_batch_edit_flags, textconv_query)
(reset_frame_state, detect_conversion_events)
(restore_selected_window, really_commit_text)
(really_finish_composing_text, really_set_composing_text)
(really_set_composing_region, really_delete_surrounding_text)
(really_set_point, complete_edit)
(handle_pending_conversion_events_1)
(handle_pending_conversion_events, start_batch_edit)
(end_batch_edit, commit_text, finish_composing_text)
(set_composing_text, set_composing_region, textconv_set_point)
(delete_surrounding_text, get_extracted_text)
(report_selected_window_change, report_point_change)
(register_texconv_interface): New functions.
* src/textconv.h (struct textconv_interface)
(TEXTCONV_SKIP_CONVERSION_REGION): Update prototype.
* src/xdisp.c (mark_window_display_accurate_1):
* src/xfns.c (xic_string_conversion_callback):
* src/xterm.c (init_xterm): Adjust accordingly.
Diffstat (limited to 'src/androidterm.c')
| -rw-r--r-- | src/androidterm.c | 1037 |
1 files changed, 1036 insertions, 1 deletions
diff --git a/src/androidterm.c b/src/androidterm.c index a57d5623c66..a44bae954da 100644 --- a/src/androidterm.c +++ b/src/androidterm.c | |||
| @@ -20,6 +20,9 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 20 | #include <config.h> | 20 | #include <config.h> |
| 21 | #include <stdio.h> | 21 | #include <stdio.h> |
| 22 | #include <math.h> | 22 | #include <math.h> |
| 23 | #include <stdlib.h> | ||
| 24 | #include <assert.h> | ||
| 25 | #include <semaphore.h> | ||
| 23 | 26 | ||
| 24 | #include "lisp.h" | 27 | #include "lisp.h" |
| 25 | #include "androidterm.h" | 28 | #include "androidterm.h" |
| @@ -28,6 +31,8 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 28 | #include "android.h" | 31 | #include "android.h" |
| 29 | #include "buffer.h" | 32 | #include "buffer.h" |
| 30 | #include "window.h" | 33 | #include "window.h" |
| 34 | #include "textconv.h" | ||
| 35 | #include "coding.h" | ||
| 31 | 36 | ||
| 32 | /* This is a chain of structures for all the X displays currently in | 37 | /* This is a chain of structures for all the X displays currently in |
| 33 | use. */ | 38 | use. */ |
| @@ -59,6 +64,12 @@ enum | |||
| 59 | ANDROID_EVENT_DROP, | 64 | ANDROID_EVENT_DROP, |
| 60 | }; | 65 | }; |
| 61 | 66 | ||
| 67 | /* Find the frame whose window has the identifier WDESC. | ||
| 68 | |||
| 69 | This is like x_window_to_frame in xterm.c, except that DPYINFO may | ||
| 70 | be NULL, as there is only at most one Android display, and is only | ||
| 71 | specified in order to stay consistent with X. */ | ||
| 72 | |||
| 62 | static struct frame * | 73 | static struct frame * |
| 63 | android_window_to_frame (struct android_display_info *dpyinfo, | 74 | android_window_to_frame (struct android_display_info *dpyinfo, |
| 64 | android_window wdesc) | 75 | android_window wdesc) |
| @@ -73,7 +84,7 @@ android_window_to_frame (struct android_display_info *dpyinfo, | |||
| 73 | { | 84 | { |
| 74 | f = XFRAME (frame); | 85 | f = XFRAME (frame); |
| 75 | 86 | ||
| 76 | if (!FRAME_ANDROID_P (f) || FRAME_DISPLAY_INFO (f) != dpyinfo) | 87 | if (!FRAME_ANDROID_P (f)) |
| 77 | continue; | 88 | continue; |
| 78 | 89 | ||
| 79 | if (FRAME_ANDROID_WINDOW (f) == wdesc) | 90 | if (FRAME_ANDROID_WINDOW (f) == wdesc) |
| @@ -527,6 +538,103 @@ android_find_tool (struct frame *f, int pointer_id) | |||
| 527 | return NULL; | 538 | return NULL; |
| 528 | } | 539 | } |
| 529 | 540 | ||
| 541 | /* Decode STRING, an array of N little endian UTF-16 characters, into | ||
| 542 | a Lisp string. Return Qnil if the string is too large, and the | ||
| 543 | encoded string otherwise. */ | ||
| 544 | |||
| 545 | static Lisp_Object | ||
| 546 | android_decode_utf16 (unsigned short *utf16, size_t n) | ||
| 547 | { | ||
| 548 | struct coding_system coding; | ||
| 549 | ptrdiff_t size; | ||
| 550 | |||
| 551 | if (INT_MULTIPLY_WRAPV (n, sizeof *utf16, &size)) | ||
| 552 | return Qnil; | ||
| 553 | |||
| 554 | /* Set up the coding system. Decoding a UTF-16 string (with no BOM) | ||
| 555 | should not signal. */ | ||
| 556 | |||
| 557 | memset (&coding, 0, sizeof coding); | ||
| 558 | |||
| 559 | setup_coding_system (Qutf_16le, &coding); | ||
| 560 | coding.source = (const unsigned char *) utf16; | ||
| 561 | decode_coding_object (&coding, Qnil, 0, 0, size, | ||
| 562 | size, Qt); | ||
| 563 | |||
| 564 | return coding.dst_object; | ||
| 565 | } | ||
| 566 | |||
| 567 | /* Handle a single input method event EVENT, delivered to the frame | ||
| 568 | F. | ||
| 569 | |||
| 570 | Perform the text conversion action specified inside. */ | ||
| 571 | |||
| 572 | static void | ||
| 573 | android_handle_ime_event (union android_event *event, struct frame *f) | ||
| 574 | { | ||
| 575 | Lisp_Object text; | ||
| 576 | |||
| 577 | /* First, decode the text if necessary. */ | ||
| 578 | |||
| 579 | switch (event->ime.operation) | ||
| 580 | { | ||
| 581 | case ANDROID_IME_COMMIT_TEXT: | ||
| 582 | case ANDROID_IME_FINISH_COMPOSING_TEXT: | ||
| 583 | case ANDROID_IME_SET_COMPOSING_TEXT: | ||
| 584 | text = android_decode_utf16 (event->ime.text, | ||
| 585 | event->ime.length); | ||
| 586 | xfree (event->ime.text); | ||
| 587 | break; | ||
| 588 | |||
| 589 | default: | ||
| 590 | break; | ||
| 591 | } | ||
| 592 | |||
| 593 | /* Finally, perform the appropriate conversion action. */ | ||
| 594 | |||
| 595 | switch (event->ime.operation) | ||
| 596 | { | ||
| 597 | case ANDROID_IME_COMMIT_TEXT: | ||
| 598 | commit_text (f, text, event->ime.position, | ||
| 599 | event->ime.counter); | ||
| 600 | break; | ||
| 601 | |||
| 602 | case ANDROID_IME_DELETE_SURROUNDING_TEXT: | ||
| 603 | delete_surrounding_text (f, event->ime.start, | ||
| 604 | event->ime.end, | ||
| 605 | event->ime.counter); | ||
| 606 | break; | ||
| 607 | |||
| 608 | case ANDROID_IME_FINISH_COMPOSING_TEXT: | ||
| 609 | finish_composing_text (f, event->ime.counter); | ||
| 610 | break; | ||
| 611 | |||
| 612 | case ANDROID_IME_SET_COMPOSING_TEXT: | ||
| 613 | set_composing_text (f, text, event->ime.position, | ||
| 614 | event->ime.counter); | ||
| 615 | break; | ||
| 616 | |||
| 617 | case ANDROID_IME_SET_COMPOSING_REGION: | ||
| 618 | set_composing_region (f, event->ime.start, | ||
| 619 | event->ime.end, | ||
| 620 | event->ime.counter); | ||
| 621 | break; | ||
| 622 | |||
| 623 | case ANDROID_IME_SET_POINT: | ||
| 624 | textconv_set_point (f, event->ime.position, | ||
| 625 | event->ime.counter); | ||
| 626 | break; | ||
| 627 | |||
| 628 | case ANDROID_IME_START_BATCH_EDIT: | ||
| 629 | start_batch_edit (f, event->ime.counter); | ||
| 630 | break; | ||
| 631 | |||
| 632 | case ANDROID_IME_END_BATCH_EDIT: | ||
| 633 | end_batch_edit (f, event->ime.counter); | ||
| 634 | break; | ||
| 635 | } | ||
| 636 | } | ||
| 637 | |||
| 530 | static int | 638 | static int |
| 531 | handle_one_android_event (struct android_display_info *dpyinfo, | 639 | handle_one_android_event (struct android_display_info *dpyinfo, |
| 532 | union android_event *event, int *finish, | 640 | union android_event *event, int *finish, |
| @@ -745,6 +853,17 @@ handle_one_android_event (struct android_display_info *dpyinfo, | |||
| 745 | 853 | ||
| 746 | case ANDROID_WINDOW_ACTION: | 854 | case ANDROID_WINDOW_ACTION: |
| 747 | 855 | ||
| 856 | /* This is a special event sent by android_run_in_emacs_thread | ||
| 857 | used to make Android run stuff. */ | ||
| 858 | |||
| 859 | if (!event->xaction.window && !event->xaction.action) | ||
| 860 | { | ||
| 861 | /* Check for and run anything the UI thread wants to run on the main | ||
| 862 | thread. */ | ||
| 863 | android_check_query (); | ||
| 864 | goto OTHER; | ||
| 865 | } | ||
| 866 | |||
| 748 | f = any; | 867 | f = any; |
| 749 | 868 | ||
| 750 | if (event->xaction.action == 0) | 869 | if (event->xaction.action == 0) |
| @@ -1334,6 +1453,19 @@ handle_one_android_event (struct android_display_info *dpyinfo, | |||
| 1334 | 1453 | ||
| 1335 | goto OTHER; | 1454 | goto OTHER; |
| 1336 | 1455 | ||
| 1456 | /* Input method events. textconv.c functions are called here to | ||
| 1457 | queue events, which are then executed in a safe context | ||
| 1458 | inside keyboard.c. */ | ||
| 1459 | case ANDROID_INPUT_METHOD: | ||
| 1460 | |||
| 1461 | if (!any) | ||
| 1462 | /* Free any text allocated for this event. */ | ||
| 1463 | xfree (event->ime.text); | ||
| 1464 | else | ||
| 1465 | android_handle_ime_event (event, any); | ||
| 1466 | |||
| 1467 | goto OTHER; | ||
| 1468 | |||
| 1337 | default: | 1469 | default: |
| 1338 | goto OTHER; | 1470 | goto OTHER; |
| 1339 | } | 1471 | } |
| @@ -4148,6 +4280,904 @@ android_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1) | |||
| 4148 | 4280 | ||
| 4149 | 4281 | ||
| 4150 | 4282 | ||
| 4283 | #ifdef __clang__ | ||
| 4284 | #pragma clang diagnostic push | ||
| 4285 | #pragma clang diagnostic ignored "-Wmissing-prototypes" | ||
| 4286 | #else | ||
| 4287 | #pragma GCC diagnostic push | ||
| 4288 | #pragma GCC diagnostic ignored "-Wmissing-prototypes" | ||
| 4289 | #endif | ||
| 4290 | |||
| 4291 | /* Input method related functions. Some of these are called from Java | ||
| 4292 | within the UI thread. */ | ||
| 4293 | |||
| 4294 | /* A counter used to decide when an editing request completes. */ | ||
| 4295 | static unsigned long edit_counter; | ||
| 4296 | |||
| 4297 | /* The last counter known to have completed. */ | ||
| 4298 | static unsigned long last_edit_counter; | ||
| 4299 | |||
| 4300 | /* Semaphore posted every time the counter increases. */ | ||
| 4301 | static sem_t edit_sem; | ||
| 4302 | |||
| 4303 | /* Try to synchronize with the UI thread, waiting a certain amount of | ||
| 4304 | time for outstanding editing requests to complete. | ||
| 4305 | |||
| 4306 | Every time one of the text retrieval functions is called and an | ||
| 4307 | editing request is made, Emacs gives the main thread approximately | ||
| 4308 | 50 ms to process it, in order to mostly keep the input method in | ||
| 4309 | sync with the buffer contents. */ | ||
| 4310 | |||
| 4311 | static void | ||
| 4312 | android_sync_edit (void) | ||
| 4313 | { | ||
| 4314 | struct timespec start, end, rem; | ||
| 4315 | |||
| 4316 | if (__atomic_load_n (&last_edit_counter, | ||
| 4317 | __ATOMIC_SEQ_CST) | ||
| 4318 | == edit_counter) | ||
| 4319 | return; | ||
| 4320 | |||
| 4321 | start = current_timespec (); | ||
| 4322 | end = timespec_add (start, make_timespec (0, 50000000)); | ||
| 4323 | |||
| 4324 | while (true) | ||
| 4325 | { | ||
| 4326 | rem = timespec_sub (end, current_timespec ()); | ||
| 4327 | |||
| 4328 | /* Timeout. */ | ||
| 4329 | if (timespec_sign (rem) < 0) | ||
| 4330 | break; | ||
| 4331 | |||
| 4332 | if (__atomic_load_n (&last_edit_counter, | ||
| 4333 | __ATOMIC_SEQ_CST) | ||
| 4334 | == edit_counter) | ||
| 4335 | break; | ||
| 4336 | |||
| 4337 | sem_timedwait (&edit_sem, &end); | ||
| 4338 | } | ||
| 4339 | } | ||
| 4340 | |||
| 4341 | /* Return a copy of the specified Java string and its length in | ||
| 4342 | *LENGTH. Use the JNI environment ENV. Value is NULL if copying | ||
| 4343 | *the string fails. */ | ||
| 4344 | |||
| 4345 | static unsigned short * | ||
| 4346 | android_copy_java_string (JNIEnv *env, jstring string, size_t *length) | ||
| 4347 | { | ||
| 4348 | jsize size, i; | ||
| 4349 | const jchar *java; | ||
| 4350 | unsigned short *buffer; | ||
| 4351 | |||
| 4352 | size = (*env)->GetStringLength (env, string); | ||
| 4353 | buffer = malloc (size * sizeof *buffer); | ||
| 4354 | |||
| 4355 | if (!buffer) | ||
| 4356 | return NULL; | ||
| 4357 | |||
| 4358 | java = (*env)->GetStringChars (env, string, NULL); | ||
| 4359 | |||
| 4360 | if (!java) | ||
| 4361 | { | ||
| 4362 | free (buffer); | ||
| 4363 | return NULL; | ||
| 4364 | } | ||
| 4365 | |||
| 4366 | for (i = 0; i < size; ++i) | ||
| 4367 | buffer[i] = java[i]; | ||
| 4368 | |||
| 4369 | *length = size; | ||
| 4370 | (*env)->ReleaseStringChars (env, string, java); | ||
| 4371 | return buffer; | ||
| 4372 | } | ||
| 4373 | |||
| 4374 | JNIEXPORT void JNICALL | ||
| 4375 | NATIVE_NAME (beginBatchEdit) (JNIEnv *env, jobject object, jshort window) | ||
| 4376 | { | ||
| 4377 | union android_event event; | ||
| 4378 | |||
| 4379 | event.ime.type = ANDROID_INPUT_METHOD; | ||
| 4380 | event.ime.serial = ++event_serial; | ||
| 4381 | event.ime.window = window; | ||
| 4382 | event.ime.operation = ANDROID_IME_START_BATCH_EDIT; | ||
| 4383 | event.ime.start = 0; | ||
| 4384 | event.ime.end = 0; | ||
| 4385 | event.ime.length = 0; | ||
| 4386 | event.ime.position = 0; | ||
| 4387 | event.ime.text = NULL; | ||
| 4388 | event.ime.counter = ++edit_counter; | ||
| 4389 | |||
| 4390 | android_write_event (&event); | ||
| 4391 | } | ||
| 4392 | |||
| 4393 | JNIEXPORT void JNICALL | ||
| 4394 | NATIVE_NAME (endBatchEdit) (JNIEnv *env, jobject object, jshort window) | ||
| 4395 | { | ||
| 4396 | union android_event event; | ||
| 4397 | |||
| 4398 | event.ime.type = ANDROID_INPUT_METHOD; | ||
| 4399 | event.ime.serial = ++event_serial; | ||
| 4400 | event.ime.window = window; | ||
| 4401 | event.ime.operation = ANDROID_IME_END_BATCH_EDIT; | ||
| 4402 | event.ime.start = 0; | ||
| 4403 | event.ime.end = 0; | ||
| 4404 | event.ime.length = 0; | ||
| 4405 | event.ime.position = 0; | ||
| 4406 | event.ime.text = NULL; | ||
| 4407 | event.ime.counter = ++edit_counter; | ||
| 4408 | |||
| 4409 | android_write_event (&event); | ||
| 4410 | } | ||
| 4411 | |||
| 4412 | JNIEXPORT void JNICALL | ||
| 4413 | NATIVE_NAME (commitCompletion) (JNIEnv *env, jobject object, jshort window, | ||
| 4414 | jstring completion_text, jint position) | ||
| 4415 | { | ||
| 4416 | union android_event event; | ||
| 4417 | unsigned short *text; | ||
| 4418 | size_t length; | ||
| 4419 | |||
| 4420 | /* First, obtain a copy of the Java string. */ | ||
| 4421 | text = android_copy_java_string (env, completion_text, &length); | ||
| 4422 | |||
| 4423 | if (!text) | ||
| 4424 | return; | ||
| 4425 | |||
| 4426 | /* Next, populate the event. Events will always eventually be | ||
| 4427 | delivered on Android, so handle_one_android_event can be relied | ||
| 4428 | on to free text. */ | ||
| 4429 | |||
| 4430 | event.ime.type = ANDROID_INPUT_METHOD; | ||
| 4431 | event.ime.serial = ++event_serial; | ||
| 4432 | event.ime.window = window; | ||
| 4433 | event.ime.operation = ANDROID_IME_COMMIT_TEXT; | ||
| 4434 | event.ime.start = 0; | ||
| 4435 | event.ime.end = 0; | ||
| 4436 | event.ime.length = min (length, PTRDIFF_MAX); | ||
| 4437 | event.ime.position = position; | ||
| 4438 | event.ime.text = text; | ||
| 4439 | event.ime.counter = ++edit_counter; | ||
| 4440 | |||
| 4441 | android_write_event (&event); | ||
| 4442 | } | ||
| 4443 | |||
| 4444 | JNIEXPORT void JNICALL | ||
| 4445 | NATIVE_NAME (commitText) (JNIEnv *env, jobject object, jshort window, | ||
| 4446 | jstring commit_text, jint position) | ||
| 4447 | { | ||
| 4448 | union android_event event; | ||
| 4449 | unsigned short *text; | ||
| 4450 | size_t length; | ||
| 4451 | |||
| 4452 | /* First, obtain a copy of the Java string. */ | ||
| 4453 | text = android_copy_java_string (env, commit_text, &length); | ||
| 4454 | |||
| 4455 | if (!text) | ||
| 4456 | return; | ||
| 4457 | |||
| 4458 | /* Next, populate the event. Events will always eventually be | ||
| 4459 | delivered on Android, so handle_one_android_event can be relied | ||
| 4460 | on to free text. */ | ||
| 4461 | |||
| 4462 | event.ime.type = ANDROID_INPUT_METHOD; | ||
| 4463 | event.ime.serial = ++event_serial; | ||
| 4464 | event.ime.window = window; | ||
| 4465 | event.ime.operation = ANDROID_IME_COMMIT_TEXT; | ||
| 4466 | event.ime.start = 0; | ||
| 4467 | event.ime.end = 0; | ||
| 4468 | event.ime.length = min (length, PTRDIFF_MAX); | ||
| 4469 | event.ime.position = position; | ||
| 4470 | event.ime.text = text; | ||
| 4471 | event.ime.counter = ++edit_counter; | ||
| 4472 | |||
| 4473 | android_write_event (&event); | ||
| 4474 | } | ||
| 4475 | |||
| 4476 | JNIEXPORT void JNICALL | ||
| 4477 | NATIVE_NAME (deleteSurroundingText) (JNIEnv *env, jobject object, | ||
| 4478 | jshort window, jint left_length, | ||
| 4479 | jint right_length) | ||
| 4480 | { | ||
| 4481 | union android_event event; | ||
| 4482 | |||
| 4483 | event.ime.type = ANDROID_INPUT_METHOD; | ||
| 4484 | event.ime.serial = ++event_serial; | ||
| 4485 | event.ime.window = window; | ||
| 4486 | event.ime.operation = ANDROID_IME_DELETE_SURROUNDING_TEXT; | ||
| 4487 | event.ime.start = left_length; | ||
| 4488 | event.ime.end = right_length; | ||
| 4489 | event.ime.length = 0; | ||
| 4490 | event.ime.position = 0; | ||
| 4491 | event.ime.text = NULL; | ||
| 4492 | event.ime.counter = ++edit_counter; | ||
| 4493 | |||
| 4494 | android_write_event (&event); | ||
| 4495 | } | ||
| 4496 | |||
| 4497 | JNIEXPORT void JNICALL | ||
| 4498 | NATIVE_NAME (finishComposingText) (JNIEnv *env, jobject object, | ||
| 4499 | jshort window) | ||
| 4500 | { | ||
| 4501 | union android_event event; | ||
| 4502 | |||
| 4503 | event.ime.type = ANDROID_INPUT_METHOD; | ||
| 4504 | event.ime.serial = ++event_serial; | ||
| 4505 | event.ime.window = window; | ||
| 4506 | event.ime.operation = ANDROID_IME_FINISH_COMPOSING_TEXT; | ||
| 4507 | event.ime.start = 0; | ||
| 4508 | event.ime.end = 0; | ||
| 4509 | event.ime.length = 0; | ||
| 4510 | event.ime.position = 0; | ||
| 4511 | event.ime.text = NULL; | ||
| 4512 | event.ime.counter = ++edit_counter; | ||
| 4513 | |||
| 4514 | android_write_event (&event); | ||
| 4515 | } | ||
| 4516 | |||
| 4517 | /* Structure describing the context used for a text query. */ | ||
| 4518 | |||
| 4519 | struct android_conversion_query_context | ||
| 4520 | { | ||
| 4521 | /* The conversion request. */ | ||
| 4522 | struct textconv_callback_struct query; | ||
| 4523 | |||
| 4524 | /* The window the request is being made on. */ | ||
| 4525 | android_window window; | ||
| 4526 | |||
| 4527 | /* Whether or not the request was successful. */ | ||
| 4528 | bool success; | ||
| 4529 | }; | ||
| 4530 | |||
| 4531 | /* Obtain the text from the frame whose window is that specified in | ||
| 4532 | DATA using the text conversion query specified there. | ||
| 4533 | |||
| 4534 | Adjust the query position to skip over any active composing region. | ||
| 4535 | |||
| 4536 | Set ((struct android_conversion_query_context *) DATA)->success on | ||
| 4537 | success. */ | ||
| 4538 | |||
| 4539 | static void | ||
| 4540 | android_perform_conversion_query (void *data) | ||
| 4541 | { | ||
| 4542 | struct android_conversion_query_context *context; | ||
| 4543 | struct frame *f; | ||
| 4544 | |||
| 4545 | context = data; | ||
| 4546 | |||
| 4547 | /* Find the frame associated with the window. */ | ||
| 4548 | f = android_window_to_frame (NULL, context->window); | ||
| 4549 | |||
| 4550 | if (!f) | ||
| 4551 | return; | ||
| 4552 | |||
| 4553 | textconv_query (f, &context->query, | ||
| 4554 | TEXTCONV_SKIP_CONVERSION_REGION); | ||
| 4555 | |||
| 4556 | /* context->query.text will have been set even if textconv_query | ||
| 4557 | returns 1. */ | ||
| 4558 | |||
| 4559 | context->success = true; | ||
| 4560 | } | ||
| 4561 | |||
| 4562 | /* Convert a string BUFFERS containing N characters in Emacs's | ||
| 4563 | internal multibyte encoding to a Java string utilizing the | ||
| 4564 | specified JNI environment. | ||
| 4565 | |||
| 4566 | If N is equal to BYTES, then BUFFER is a single byte buffer. | ||
| 4567 | Otherwise, BUFFER is a multibyte buffer. | ||
| 4568 | |||
| 4569 | Make sure N and BYTES are absolutely correct, or you are asking for | ||
| 4570 | trouble. | ||
| 4571 | |||
| 4572 | Value is the string upon success, NULL otherwise. Any exceptions | ||
| 4573 | generated are not cleared. */ | ||
| 4574 | |||
| 4575 | static jstring | ||
| 4576 | android_text_to_string (JNIEnv *env, char *buffer, ptrdiff_t n, | ||
| 4577 | ptrdiff_t bytes) | ||
| 4578 | { | ||
| 4579 | jchar *utf16; | ||
| 4580 | size_t size, index; | ||
| 4581 | jstring string; | ||
| 4582 | int encoded; | ||
| 4583 | |||
| 4584 | if (n == bytes) | ||
| 4585 | { | ||
| 4586 | /* This buffer holds no multibyte characters. */ | ||
| 4587 | |||
| 4588 | if (INT_MULTIPLY_WRAPV (n, sizeof *utf16, &size)) | ||
| 4589 | return NULL; | ||
| 4590 | |||
| 4591 | utf16 = malloc (size); | ||
| 4592 | index = 0; | ||
| 4593 | |||
| 4594 | if (!utf16) | ||
| 4595 | return NULL; | ||
| 4596 | |||
| 4597 | while (n--) | ||
| 4598 | { | ||
| 4599 | utf16[index] = buffer[index]; | ||
| 4600 | index++; | ||
| 4601 | } | ||
| 4602 | |||
| 4603 | string = (*env)->NewString (env, utf16, bytes); | ||
| 4604 | free (utf16); | ||
| 4605 | |||
| 4606 | return string; | ||
| 4607 | } | ||
| 4608 | |||
| 4609 | /* Allocate enough to hold N characters. */ | ||
| 4610 | |||
| 4611 | if (INT_MULTIPLY_WRAPV (n, sizeof *utf16, &size)) | ||
| 4612 | return NULL; | ||
| 4613 | |||
| 4614 | utf16 = malloc (size); | ||
| 4615 | index = 0; | ||
| 4616 | |||
| 4617 | if (!utf16) | ||
| 4618 | return NULL; | ||
| 4619 | |||
| 4620 | while (n--) | ||
| 4621 | { | ||
| 4622 | eassert (CHAR_HEAD_P (*buffer)); | ||
| 4623 | encoded = STRING_CHAR ((unsigned char *) buffer); | ||
| 4624 | |||
| 4625 | /* Now figure out how to save ENCODED into the string. | ||
| 4626 | Emacs operates on multibyte characters, not UTF-16 | ||
| 4627 | characters with surrogate pairs as Android does. | ||
| 4628 | |||
| 4629 | However, character positions in Java are represented in 2 | ||
| 4630 | byte units, meaning that the text position reported to | ||
| 4631 | Android can become out of sync if characters are found in a | ||
| 4632 | buffer that require surrogate pairs. | ||
| 4633 | |||
| 4634 | The hack used by Emacs is to simply replace each multibyte | ||
| 4635 | character that doesn't fit in a jchar with the NULL | ||
| 4636 | character. */ | ||
| 4637 | |||
| 4638 | if (encoded >= 65536) | ||
| 4639 | encoded = 0; | ||
| 4640 | |||
| 4641 | utf16[index++] = encoded; | ||
| 4642 | buffer += BYTES_BY_CHAR_HEAD (*buffer); | ||
| 4643 | } | ||
| 4644 | |||
| 4645 | /* Create the string. */ | ||
| 4646 | string = (*env)->NewString (env, utf16, index); | ||
| 4647 | free (utf16); | ||
| 4648 | return string; | ||
| 4649 | } | ||
| 4650 | |||
| 4651 | JNIEXPORT jstring JNICALL | ||
| 4652 | NATIVE_NAME (getSelectedText) (JNIEnv *env, jobject object, | ||
| 4653 | jshort window) | ||
| 4654 | { | ||
| 4655 | return NULL; | ||
| 4656 | } | ||
| 4657 | |||
| 4658 | JNIEXPORT jstring JNICALL | ||
| 4659 | NATIVE_NAME (getTextAfterCursor) (JNIEnv *env, jobject object, jshort window, | ||
| 4660 | jint length, jint flags) | ||
| 4661 | { | ||
| 4662 | struct android_conversion_query_context context; | ||
| 4663 | jstring string; | ||
| 4664 | |||
| 4665 | /* First, set up the conversion query. */ | ||
| 4666 | context.query.position = 0; | ||
| 4667 | context.query.direction = TEXTCONV_FORWARD_CHAR; | ||
| 4668 | context.query.factor = min (length, 65535); | ||
| 4669 | context.query.operation = TEXTCONV_RETRIEVAL; | ||
| 4670 | |||
| 4671 | /* Next, set the rest of the context. */ | ||
| 4672 | context.window = window; | ||
| 4673 | context.success = false; | ||
| 4674 | |||
| 4675 | /* Now try to perform the query. */ | ||
| 4676 | android_sync_edit (); | ||
| 4677 | if (android_run_in_emacs_thread (android_perform_conversion_query, | ||
| 4678 | &context)) | ||
| 4679 | return NULL; | ||
| 4680 | |||
| 4681 | if (!context.success) | ||
| 4682 | return NULL; | ||
| 4683 | |||
| 4684 | /* context->query.text now contains the text in Emacs's internal | ||
| 4685 | UTF-8 based encoding. | ||
| 4686 | |||
| 4687 | Convert it to Java's UTF-16 encoding, which is the same as | ||
| 4688 | UTF-16, except that NULL bytes are encoded as surrogate pairs. | ||
| 4689 | |||
| 4690 | This assumes that `free' can free data allocated with xmalloc. */ | ||
| 4691 | |||
| 4692 | string = android_text_to_string (env, context.query.text.text, | ||
| 4693 | context.query.text.length, | ||
| 4694 | context.query.text.bytes); | ||
| 4695 | free (context.query.text.text); | ||
| 4696 | |||
| 4697 | return string; | ||
| 4698 | } | ||
| 4699 | |||
| 4700 | JNIEXPORT jstring JNICALL | ||
| 4701 | NATIVE_NAME (getTextBeforeCursor) (JNIEnv *env, jobject object, jshort window, | ||
| 4702 | jint length, jint flags) | ||
| 4703 | { | ||
| 4704 | struct android_conversion_query_context context; | ||
| 4705 | jstring string; | ||
| 4706 | |||
| 4707 | /* First, set up the conversion query. */ | ||
| 4708 | context.query.position = 0; | ||
| 4709 | context.query.direction = TEXTCONV_BACKWARD_CHAR; | ||
| 4710 | context.query.factor = min (length, 65535); | ||
| 4711 | context.query.operation = TEXTCONV_RETRIEVAL; | ||
| 4712 | |||
| 4713 | /* Next, set the rest of the context. */ | ||
| 4714 | context.window = window; | ||
| 4715 | context.success = false; | ||
| 4716 | |||
| 4717 | /* Now try to perform the query. */ | ||
| 4718 | android_sync_edit (); | ||
| 4719 | if (android_run_in_emacs_thread (android_perform_conversion_query, | ||
| 4720 | &context)) | ||
| 4721 | return NULL; | ||
| 4722 | |||
| 4723 | if (!context.success) | ||
| 4724 | return NULL; | ||
| 4725 | |||
| 4726 | /* context->query.text now contains the text in Emacs's internal | ||
| 4727 | UTF-8 based encoding. | ||
| 4728 | |||
| 4729 | Convert it to Java's UTF-16 encoding, which is the same as | ||
| 4730 | UTF-16, except that NULL bytes are encoded as surrogate pairs. | ||
| 4731 | |||
| 4732 | This assumes that `free' can free data allocated with xmalloc. */ | ||
| 4733 | |||
| 4734 | string = android_text_to_string (env, context.query.text.text, | ||
| 4735 | context.query.text.length, | ||
| 4736 | context.query.text.bytes); | ||
| 4737 | free (context.query.text.text); | ||
| 4738 | |||
| 4739 | return string; | ||
| 4740 | } | ||
| 4741 | |||
| 4742 | JNIEXPORT void JNICALL | ||
| 4743 | NATIVE_NAME (setComposingText) (JNIEnv *env, jobject object, jshort window, | ||
| 4744 | jstring composing_text, | ||
| 4745 | jint new_cursor_position) | ||
| 4746 | { | ||
| 4747 | union android_event event; | ||
| 4748 | unsigned short *text; | ||
| 4749 | size_t length; | ||
| 4750 | |||
| 4751 | /* First, obtain a copy of the Java string. */ | ||
| 4752 | text = android_copy_java_string (env, composing_text, &length); | ||
| 4753 | |||
| 4754 | if (!text) | ||
| 4755 | return; | ||
| 4756 | |||
| 4757 | /* Next, populate the event. Events will always eventually be | ||
| 4758 | delivered on Android, so handle_one_android_event can be relied | ||
| 4759 | on to free text. */ | ||
| 4760 | |||
| 4761 | event.ime.type = ANDROID_INPUT_METHOD; | ||
| 4762 | event.ime.serial = ++event_serial; | ||
| 4763 | event.ime.window = window; | ||
| 4764 | event.ime.operation = ANDROID_IME_SET_COMPOSING_TEXT; | ||
| 4765 | event.ime.start = 0; | ||
| 4766 | event.ime.end = 0; | ||
| 4767 | event.ime.length = min (length, PTRDIFF_MAX); | ||
| 4768 | event.ime.position = new_cursor_position; | ||
| 4769 | event.ime.text = text; | ||
| 4770 | event.ime.counter = ++edit_counter; | ||
| 4771 | |||
| 4772 | android_write_event (&event); | ||
| 4773 | } | ||
| 4774 | |||
| 4775 | JNIEXPORT void JNICALL | ||
| 4776 | NATIVE_NAME (setComposingRegion) (JNIEnv *env, jobject object, jshort window, | ||
| 4777 | jint start, jint end) | ||
| 4778 | { | ||
| 4779 | union android_event event; | ||
| 4780 | |||
| 4781 | event.ime.type = ANDROID_INPUT_METHOD; | ||
| 4782 | event.ime.serial = ++event_serial; | ||
| 4783 | event.ime.window = window; | ||
| 4784 | event.ime.operation = ANDROID_IME_SET_COMPOSING_REGION; | ||
| 4785 | event.ime.start = start; | ||
| 4786 | event.ime.end = end; | ||
| 4787 | event.ime.length = 0; | ||
| 4788 | event.ime.position = 0; | ||
| 4789 | event.ime.text = NULL; | ||
| 4790 | event.ime.counter = ++edit_counter; | ||
| 4791 | |||
| 4792 | android_write_event (&event); | ||
| 4793 | } | ||
| 4794 | |||
| 4795 | JNIEXPORT void JNICALL | ||
| 4796 | NATIVE_NAME (setSelection) (JNIEnv *env, jobject object, jshort window, | ||
| 4797 | jint start, jint end) | ||
| 4798 | { | ||
| 4799 | union android_event event; | ||
| 4800 | |||
| 4801 | /* While IMEs want access to the entire selection, Emacs only | ||
| 4802 | supports setting the point. */ | ||
| 4803 | |||
| 4804 | event.ime.type = ANDROID_INPUT_METHOD; | ||
| 4805 | event.ime.serial = ++event_serial; | ||
| 4806 | event.ime.window = window; | ||
| 4807 | event.ime.operation = ANDROID_IME_SET_POINT; | ||
| 4808 | event.ime.start = 0; | ||
| 4809 | event.ime.end = 0; | ||
| 4810 | event.ime.length = 0; | ||
| 4811 | event.ime.position = start; | ||
| 4812 | event.ime.text = NULL; | ||
| 4813 | event.ime.counter = ++edit_counter; | ||
| 4814 | } | ||
| 4815 | |||
| 4816 | /* Structure describing the context for `getSelection'. */ | ||
| 4817 | |||
| 4818 | struct android_get_selection_context | ||
| 4819 | { | ||
| 4820 | /* The window in question. */ | ||
| 4821 | android_window window; | ||
| 4822 | |||
| 4823 | /* The position of the window's point when it was last | ||
| 4824 | redisplayed. */ | ||
| 4825 | ptrdiff_t point; | ||
| 4826 | }; | ||
| 4827 | |||
| 4828 | /* Function run on the main thread by `getSelection'. | ||
| 4829 | Place the character position of point in PT. */ | ||
| 4830 | |||
| 4831 | static void | ||
| 4832 | android_get_selection (void *data) | ||
| 4833 | { | ||
| 4834 | struct android_get_selection_context *context; | ||
| 4835 | struct frame *f; | ||
| 4836 | struct window *w; | ||
| 4837 | |||
| 4838 | context = data; | ||
| 4839 | |||
| 4840 | /* Look up the associated frame and its selected window. */ | ||
| 4841 | f = android_window_to_frame (NULL, context->window); | ||
| 4842 | |||
| 4843 | if (!f) | ||
| 4844 | context->point = -1; | ||
| 4845 | else | ||
| 4846 | { | ||
| 4847 | w = XWINDOW (f->selected_window); | ||
| 4848 | |||
| 4849 | /* Return W's point at the time of the last redisplay. This is | ||
| 4850 | rather important to keep the input method consistent with the | ||
| 4851 | contents of the display. */ | ||
| 4852 | context->point = w->last_point; | ||
| 4853 | } | ||
| 4854 | } | ||
| 4855 | |||
| 4856 | JNIEXPORT jint JNICALL | ||
| 4857 | NATIVE_NAME (getSelection) (JNIEnv *env, jobject object, jshort window) | ||
| 4858 | { | ||
| 4859 | struct android_get_selection_context context; | ||
| 4860 | |||
| 4861 | context.window = window; | ||
| 4862 | |||
| 4863 | android_sync_edit (); | ||
| 4864 | if (android_run_in_emacs_thread (android_get_selection, | ||
| 4865 | &context)) | ||
| 4866 | return -1; | ||
| 4867 | |||
| 4868 | if (context.point == -1) | ||
| 4869 | return -1; | ||
| 4870 | |||
| 4871 | return min (context.point, TYPE_MAXIMUM (jint)); | ||
| 4872 | } | ||
| 4873 | |||
| 4874 | JNIEXPORT void JNICALL | ||
| 4875 | NATIVE_NAME (performEditorAction) (JNIEnv *env, jobject object, | ||
| 4876 | jshort window, int action) | ||
| 4877 | { | ||
| 4878 | union android_event event; | ||
| 4879 | |||
| 4880 | /* Undocumented behavior: performEditorAction is apparently expected | ||
| 4881 | to finish composing any text. */ | ||
| 4882 | |||
| 4883 | NATIVE_NAME (finishComposingText) (env, object, window); | ||
| 4884 | |||
| 4885 | event.xkey.type = ANDROID_KEY_PRESS; | ||
| 4886 | event.xkey.serial = ++event_serial; | ||
| 4887 | event.xkey.window = window; | ||
| 4888 | event.xkey.time = 0; | ||
| 4889 | event.xkey.state = 0; | ||
| 4890 | event.xkey.keycode = 66; | ||
| 4891 | event.xkey.unicode_char = 0; | ||
| 4892 | |||
| 4893 | android_write_event (&event); | ||
| 4894 | } | ||
| 4895 | |||
| 4896 | struct android_get_extracted_text_context | ||
| 4897 | { | ||
| 4898 | /* The parameters of the request. */ | ||
| 4899 | int hint_max_chars; | ||
| 4900 | |||
| 4901 | /* Token for the request. */ | ||
| 4902 | int token; | ||
| 4903 | |||
| 4904 | /* The returned text, or NULL. */ | ||
| 4905 | char *text; | ||
| 4906 | |||
| 4907 | /* The size of that text in characters and bytes. */ | ||
| 4908 | ptrdiff_t length, bytes; | ||
| 4909 | |||
| 4910 | /* Offsets into that text. */ | ||
| 4911 | ptrdiff_t start, offset; | ||
| 4912 | |||
| 4913 | /* The window. */ | ||
| 4914 | android_window window; | ||
| 4915 | }; | ||
| 4916 | |||
| 4917 | /* Return the extracted text in the extracted text context specified | ||
| 4918 | by DATA. */ | ||
| 4919 | |||
| 4920 | static void | ||
| 4921 | android_get_extracted_text (void *data) | ||
| 4922 | { | ||
| 4923 | struct android_get_extracted_text_context *request; | ||
| 4924 | struct frame *f; | ||
| 4925 | |||
| 4926 | request = data; | ||
| 4927 | |||
| 4928 | /* Find the frame associated with the window. */ | ||
| 4929 | f = android_window_to_frame (NULL, request->window); | ||
| 4930 | |||
| 4931 | if (!f) | ||
| 4932 | return; | ||
| 4933 | |||
| 4934 | /* Now get the extracted text. */ | ||
| 4935 | request->text | ||
| 4936 | = get_extracted_text (f, min (request->hint_max_chars, 600), | ||
| 4937 | &request->start, &request->offset, | ||
| 4938 | &request->length, &request->bytes); | ||
| 4939 | } | ||
| 4940 | |||
| 4941 | /* Structure describing the `ExtractedTextRequest' class. | ||
| 4942 | Valid only on the UI thread. */ | ||
| 4943 | |||
| 4944 | struct android_extracted_text_request_class | ||
| 4945 | { | ||
| 4946 | bool initialized; | ||
| 4947 | jfieldID hint_max_chars; | ||
| 4948 | jfieldID token; | ||
| 4949 | }; | ||
| 4950 | |||
| 4951 | /* Structure describing the `ExtractedText' class. | ||
| 4952 | Valid only on the UI thread. */ | ||
| 4953 | |||
| 4954 | struct android_extracted_text_class | ||
| 4955 | { | ||
| 4956 | jclass class; | ||
| 4957 | jmethodID constructor; | ||
| 4958 | jfieldID partial_start_offset; | ||
| 4959 | jfieldID partial_end_offset; | ||
| 4960 | jfieldID selection_start; | ||
| 4961 | jfieldID selection_end; | ||
| 4962 | jfieldID start_offset; | ||
| 4963 | jfieldID text; | ||
| 4964 | }; | ||
| 4965 | |||
| 4966 | JNIEXPORT jobject JNICALL | ||
| 4967 | NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object, | ||
| 4968 | jshort window, jobject request, | ||
| 4969 | jint flags) | ||
| 4970 | { | ||
| 4971 | struct android_get_extracted_text_context context; | ||
| 4972 | static struct android_extracted_text_request_class request_class; | ||
| 4973 | static struct android_extracted_text_class text_class; | ||
| 4974 | jstring string; | ||
| 4975 | jclass class; | ||
| 4976 | jobject object; | ||
| 4977 | |||
| 4978 | /* TODO: report changes to extracted text. */ | ||
| 4979 | |||
| 4980 | /* Initialize both classes if necessary. */ | ||
| 4981 | |||
| 4982 | if (!request_class.initialized) | ||
| 4983 | { | ||
| 4984 | class | ||
| 4985 | = (*env)->FindClass (env, ("android/view/inputmethod" | ||
| 4986 | "/ExtractedTextRequest")); | ||
| 4987 | assert (class); | ||
| 4988 | |||
| 4989 | request_class.hint_max_chars | ||
| 4990 | = (*env)->GetFieldID (env, class, "hintMaxChars", "I"); | ||
| 4991 | assert (request_class.hint_max_chars); | ||
| 4992 | |||
| 4993 | request_class.token | ||
| 4994 | = (*env)->GetFieldID (env, class, "token", "I"); | ||
| 4995 | assert (request_class.token); | ||
| 4996 | |||
| 4997 | request_class.initialized = true; | ||
| 4998 | } | ||
| 4999 | |||
| 5000 | if (!text_class.class) | ||
| 5001 | { | ||
| 5002 | text_class.class | ||
| 5003 | = (*env)->FindClass (env, ("android/view/inputmethod" | ||
| 5004 | "/ExtractedText")); | ||
| 5005 | assert (text_class.class); | ||
| 5006 | |||
| 5007 | class | ||
| 5008 | = text_class.class | ||
| 5009 | = (*env)->NewGlobalRef (env, text_class.class); | ||
| 5010 | assert (text_class.class); | ||
| 5011 | |||
| 5012 | text_class.partial_start_offset | ||
| 5013 | = (*env)->GetFieldID (env, class, "partialStartOffset", "I"); | ||
| 5014 | text_class.partial_end_offset | ||
| 5015 | = (*env)->GetFieldID (env, class, "partialEndOffset", "I"); | ||
| 5016 | text_class.selection_start | ||
| 5017 | = (*env)->GetFieldID (env, class, "selectionStart", "I"); | ||
| 5018 | text_class.selection_end | ||
| 5019 | = (*env)->GetFieldID (env, class, "selectionEnd", "I"); | ||
| 5020 | text_class.start_offset | ||
| 5021 | = (*env)->GetFieldID (env, class, "startOffset", "I"); | ||
| 5022 | text_class.text | ||
| 5023 | = (*env)->GetFieldID (env, class, "text", "Ljava/lang/CharSequence;"); | ||
| 5024 | text_class.constructor | ||
| 5025 | = (*env)->GetMethodID (env, class, "<init>", "()V"); | ||
| 5026 | } | ||
| 5027 | |||
| 5028 | context.hint_max_chars | ||
| 5029 | = (*env)->GetIntField (env, request, request_class.hint_max_chars); | ||
| 5030 | context.token | ||
| 5031 | = (*env)->GetIntField (env, request, request_class.token); | ||
| 5032 | context.text = NULL; | ||
| 5033 | context.window = window; | ||
| 5034 | |||
| 5035 | android_sync_edit (); | ||
| 5036 | if (android_run_in_emacs_thread (android_get_extracted_text, | ||
| 5037 | &context)) | ||
| 5038 | return NULL; | ||
| 5039 | |||
| 5040 | if (!context.text) | ||
| 5041 | return NULL; | ||
| 5042 | |||
| 5043 | /* Encode the returned text. */ | ||
| 5044 | string = android_text_to_string (env, context.text, context.length, | ||
| 5045 | context.bytes); | ||
| 5046 | free (context.text); | ||
| 5047 | |||
| 5048 | if (!string) | ||
| 5049 | return NULL; | ||
| 5050 | |||
| 5051 | /* Create an ExtractedText object containing this information. */ | ||
| 5052 | object = (*android_java_env)->NewObject (env, text_class.class, | ||
| 5053 | text_class.constructor); | ||
| 5054 | if (!object) | ||
| 5055 | return NULL; | ||
| 5056 | |||
| 5057 | (*env)->SetIntField (env, object, text_class.partial_start_offset, -1); | ||
| 5058 | (*env)->SetIntField (env, object, text_class.partial_end_offset, -1); | ||
| 5059 | (*env)->SetIntField (env, object, text_class.selection_start, | ||
| 5060 | min (context.offset, TYPE_MAXIMUM (jint))); | ||
| 5061 | (*env)->SetIntField (env, object, text_class.selection_end, | ||
| 5062 | min (context.offset, TYPE_MAXIMUM (jint))); | ||
| 5063 | (*env)->SetIntField (env, object, text_class.start_offset, | ||
| 5064 | min (context.start, TYPE_MAXIMUM (jint))); | ||
| 5065 | (*env)->SetObjectField (env, object, text_class.text, string); | ||
| 5066 | return object; | ||
| 5067 | } | ||
| 5068 | |||
| 5069 | #ifdef __clang__ | ||
| 5070 | #pragma clang diagnostic pop | ||
| 5071 | #else | ||
| 5072 | #pragma GCC diagnostic pop | ||
| 5073 | #endif | ||
| 5074 | |||
| 5075 | |||
| 5076 | |||
| 5077 | /* Tell the input method where the composing region and selection of | ||
| 5078 | F's selected window is located. W should be F's selected window; | ||
| 5079 | if it is NULL, then F->selected_window is used in its place. */ | ||
| 5080 | |||
| 5081 | static void | ||
| 5082 | android_update_selection (struct frame *f, struct window *w) | ||
| 5083 | { | ||
| 5084 | ptrdiff_t start, end, point; | ||
| 5085 | |||
| 5086 | if (MARKERP (f->conversion.compose_region_start)) | ||
| 5087 | { | ||
| 5088 | eassert (MARKERP (f->conversion.compose_region_end)); | ||
| 5089 | |||
| 5090 | start = marker_position (f->conversion.compose_region_start); | ||
| 5091 | end = marker_position (f->conversion.compose_region_end); | ||
| 5092 | } | ||
| 5093 | else | ||
| 5094 | start = -1, end = -1; | ||
| 5095 | |||
| 5096 | /* Now constrain START and END to the maximium size of a Java | ||
| 5097 | integer. */ | ||
| 5098 | start = min (start, TYPE_MAXIMUM (jint)); | ||
| 5099 | end = min (end, TYPE_MAXIMUM (jint)); | ||
| 5100 | |||
| 5101 | if (!w) | ||
| 5102 | w = XWINDOW (f->selected_window); | ||
| 5103 | |||
| 5104 | /* Figure out where the point is. */ | ||
| 5105 | point = min (w->last_point, TYPE_MAXIMUM (jint)); | ||
| 5106 | |||
| 5107 | /* Send the update. */ | ||
| 5108 | android_update_ic (FRAME_ANDROID_WINDOW (f), point, point, | ||
| 5109 | start, end); | ||
| 5110 | } | ||
| 5111 | |||
| 5112 | /* Notice that the input method connection to F should be reset as a | ||
| 5113 | result of a change to its contents. */ | ||
| 5114 | |||
| 5115 | static void | ||
| 5116 | android_reset_conversion (struct frame *f) | ||
| 5117 | { | ||
| 5118 | /* Reset the input method. | ||
| 5119 | |||
| 5120 | Pick an appropriate ``input mode'' based on whether or not the | ||
| 5121 | minibuffer window is selected; this controls whether or not | ||
| 5122 | ``RET'' inserts a newline or sends an actual key event. */ | ||
| 5123 | android_reset_ic (FRAME_ANDROID_WINDOW (f), | ||
| 5124 | (EQ (f->selected_window, | ||
| 5125 | f->minibuffer_window) | ||
| 5126 | ? ANDROID_IC_MODE_ACTION | ||
| 5127 | : ANDROID_IC_MODE_TEXT)); | ||
| 5128 | |||
| 5129 | /* Move its selection to the specified position. */ | ||
| 5130 | android_update_selection (f, NULL); | ||
| 5131 | } | ||
| 5132 | |||
| 5133 | /* Notice that point has moved in the F's selected window's selected | ||
| 5134 | buffer. W is the window, and BUFFER is that buffer. */ | ||
| 5135 | |||
| 5136 | static void | ||
| 5137 | android_set_point (struct frame *f, struct window *w, | ||
| 5138 | struct buffer *buffer) | ||
| 5139 | { | ||
| 5140 | android_update_selection (f, w); | ||
| 5141 | } | ||
| 5142 | |||
| 5143 | /* Notice that the composition region on F's old selected window has | ||
| 5144 | changed. */ | ||
| 5145 | |||
| 5146 | static void | ||
| 5147 | android_compose_region_changed (struct frame *f) | ||
| 5148 | { | ||
| 5149 | android_update_selection (f, XWINDOW (f->old_selected_window)); | ||
| 5150 | } | ||
| 5151 | |||
| 5152 | /* Notice that the text conversion has completed. */ | ||
| 5153 | |||
| 5154 | static void | ||
| 5155 | android_notify_conversion (unsigned long counter) | ||
| 5156 | { | ||
| 5157 | int sval; | ||
| 5158 | |||
| 5159 | if (last_edit_counter < counter) | ||
| 5160 | __atomic_store_n (&last_edit_counter, counter, | ||
| 5161 | __ATOMIC_SEQ_CST); | ||
| 5162 | |||
| 5163 | sem_getvalue (&edit_sem, &sval); | ||
| 5164 | |||
| 5165 | if (sval <= 0) | ||
| 5166 | sem_post (&edit_sem); | ||
| 5167 | } | ||
| 5168 | |||
| 5169 | /* Android text conversion interface. */ | ||
| 5170 | |||
| 5171 | static struct textconv_interface text_conversion_interface = | ||
| 5172 | { | ||
| 5173 | android_reset_conversion, | ||
| 5174 | android_set_point, | ||
| 5175 | android_compose_region_changed, | ||
| 5176 | android_notify_conversion, | ||
| 5177 | }; | ||
| 5178 | |||
| 5179 | |||
| 5180 | |||
| 4151 | extern frame_parm_handler android_frame_parm_handlers[]; | 5181 | extern frame_parm_handler android_frame_parm_handlers[]; |
| 4152 | 5182 | ||
| 4153 | #endif /* !ANDROID_STUBIFY */ | 5183 | #endif /* !ANDROID_STUBIFY */ |
| @@ -4327,6 +5357,11 @@ android_term_init (void) | |||
| 4327 | 5357 | ||
| 4328 | /* Set the baud rate to the same value it gets set to on X. */ | 5358 | /* Set the baud rate to the same value it gets set to on X. */ |
| 4329 | baud_rate = 19200; | 5359 | baud_rate = 19200; |
| 5360 | |||
| 5361 | #ifndef ANDROID_STUBIFY | ||
| 5362 | sem_init (&edit_sem, false, 0); | ||
| 5363 | register_textconv_interface (&text_conversion_interface); | ||
| 5364 | #endif | ||
| 4330 | } | 5365 | } |
| 4331 | 5366 | ||
| 4332 | 5367 | ||