aboutsummaryrefslogtreecommitdiffstats
path: root/src/textconv.c
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/textconv.c
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/textconv.c')
-rw-r--r--src/textconv.c906
1 files changed, 900 insertions, 6 deletions
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}