root/src/textconv.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. copy_buffer_text
  2. get_mark
  3. select_window
  4. textconv_query
  5. sync_overlay
  6. record_buffer_change
  7. reset_frame_state
  8. detect_conversion_events
  9. restore_selected_window
  10. really_commit_text
  11. really_finish_composing_text
  12. really_set_composing_text
  13. really_set_composing_region
  14. really_delete_surrounding_text
  15. really_request_point_update
  16. really_set_point_and_mark
  17. complete_edit
  18. complete_edit_check
  19. handle_pending_conversion_events_1
  20. decrement_inside
  21. handle_pending_conversion_events
  22. start_batch_edit
  23. end_batch_edit
  24. commit_text
  25. finish_composing_text
  26. set_composing_text
  27. set_composing_region
  28. textconv_set_point_and_mark
  29. delete_surrounding_text
  30. request_point_update
  31. textconv_barrier
  32. get_extracted_text
  33. get_surrounding_text
  34. conversion_disabled_p
  35. report_selected_window_change
  36. report_point_change
  37. disable_text_conversion
  38. resume_text_conversion
  39. register_textconv_interface
  40. check_postponed_buffers
  41. syms_of_textconv

     1 /* String conversion support for graphics terminals.
     2 
     3 Copyright (C) 2023 Free Software Foundation, Inc.
     4 
     5 This file is part of GNU Emacs.
     6 
     7 GNU Emacs is free software: you can redistribute it and/or modify
     8 it under the terms of the GNU General Public License as published by
     9 the Free Software Foundation, either version 3 of the License, or (at
    10 your option) any later version.
    11 
    12 GNU Emacs is distributed in the hope that it will be useful,
    13 but WITHOUT ANY WARRANTY; without even the implied warranty of
    14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    15 GNU General Public License for more details.
    16 
    17 You should have received a copy of the GNU General Public License
    18 along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
    19 
    20 /* String conversion support.
    21 
    22    Many input methods require access to text surrounding the cursor.
    23    They may then request that the text editor remove or substitute
    24    that text for something else, for example when providing the
    25    ability to ``undo'' or ``edit'' previously composed text.  This is
    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
    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.  */
    32 
    33 #include <config.h>
    34 
    35 #include "textconv.h"
    36 #include "buffer.h"
    37 #include "syntax.h"
    38 #include "blockinput.h"
    39 #include "keyboard.h"
    40 
    41 
    42 
    43 /* Define debugging macros.  */
    44 
    45 #if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
    46 #if 0
    47 #include <android/log.h>
    48 
    49 #define TEXTCONV_DEBUG(fmt, ...)                                        \
    50   __android_log_print (ANDROID_LOG_VERBOSE, "EmacsInputConnection",     \
    51                        "%s: " fmt, __func__, ## __VA_ARGS__)
    52 #endif /* 0 */
    53 #endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */
    54 
    55 #ifndef TEXTCONV_DEBUG
    56 #define TEXTCONV_DEBUG(...) ((void) 0)
    57 #endif /* TEXTCONV_DEBUG */
    58 
    59 
    60 
    61 /* The window system's text conversion interface.  NULL when the
    62    window system has not set up text conversion.  */
    63 
    64 static struct textconv_interface *text_interface;
    65 
    66 /* How many times text conversion has been disabled.  */
    67 
    68 static int suppress_conversion_count;
    69 
    70 /* Flags used to determine what must be sent after a batch edit
    71    ends.  */
    72 
    73 enum textconv_batch_edit_flags
    74   {
    75     PENDING_POINT_CHANGE   = 1,
    76     PENDING_COMPOSE_CHANGE = 2,
    77   };
    78 
    79 
    80 
    81 /* Copy the portion of the current buffer's text described by BEG,
    82    BEG_BYTE, END, END_BYTE to the char * buffer BUFFER, which should
    83    be at least END_BYTE - BEG_BYTEs long.  */
    84 
    85 static void
    86 copy_buffer_text (ptrdiff_t beg, ptrdiff_t beg_byte,
    87                   ptrdiff_t end, ptrdiff_t end_byte,
    88                   char *buffer)
    89 {
    90   ptrdiff_t beg0, end0, beg1, end1, size;
    91 
    92   if (beg_byte < GPT_BYTE && GPT_BYTE < end_byte)
    93     {
    94       /* Two regions, before and after the gap.  */
    95       beg0 = beg_byte;
    96       end0 = GPT_BYTE;
    97       beg1 = GPT_BYTE + GAP_SIZE - BEG_BYTE;
    98       end1 = end_byte + GAP_SIZE - BEG_BYTE;
    99     }
   100   else
   101     {
   102       /* The only region.  */
   103       beg0 = beg_byte;
   104       end0 = end_byte;
   105       beg1 = -1;
   106       end1 = -1;
   107     }
   108 
   109   size = end0 - beg0;
   110   memcpy (buffer, BYTE_POS_ADDR (beg0), size);
   111   if (beg1 != -1)
   112     memcpy (buffer + size, BEG_ADDR + beg1, end1 - beg1);
   113 }
   114 
   115 
   116 
   117 /* Conversion query.  */
   118 
   119 /* Return the position of the active mark, or -1 if there is no mark
   120    or it is not active.  */
   121 
   122 static ptrdiff_t
   123 get_mark (void)
   124 {
   125   if (!NILP (BVAR (current_buffer, mark_active))
   126       && XMARKER (BVAR (current_buffer, mark))->buffer)
   127     return marker_position (BVAR (current_buffer,
   128                                   mark));
   129 
   130   return -1;
   131 }
   132 
   133 /* Like Fselect_window.  However, if WINDOW is a minibuffer window
   134    but not the active minibuffer window, select its frame's selected
   135    window instead.  */
   136 
   137 static void
   138 select_window (Lisp_Object window, Lisp_Object norecord)
   139 {
   140   struct window *w;
   141 
   142   w = XWINDOW (window);
   143 
   144   if (MINI_WINDOW_P (w)
   145       && WINDOW_LIVE_P (window)
   146       && !EQ (window, Factive_minibuffer_window ()))
   147     window = WINDOW_XFRAME (w)->selected_window;
   148 
   149   Fselect_window (window, norecord);
   150 }
   151 
   152 /* Perform the text conversion operation specified in QUERY and return
   153    the results.
   154 
   155    Find the text between QUERY->position from point on frame F's
   156    selected window and QUERY->factor times QUERY->direction from that
   157    position.  Return it in QUERY->text.
   158 
   159    If QUERY->position is TYPE_MINIMUM (EMACS_INT) or EMACS_INT_MAX,
   160    start at the window's last point or mark, whichever is greater or
   161    smaller.
   162 
   163    Then, either delete that text from the buffer if QUERY->operation
   164    is TEXTCONV_SUBSTITUTION, or return 0.
   165 
   166    If FLAGS & TEXTCONV_SKIP_CONVERSION_REGION, then first move point
   167    past the conversion region in the specified direction if it is
   168    inside.
   169 
   170    Value is 0 if QUERY->operation was not TEXTCONV_SUBSTITUTION
   171    or if deleting the text was successful, and 1 otherwise.  */
   172 
   173 int
   174 textconv_query (struct frame *f, struct textconv_callback_struct *query,
   175                 int flags)
   176 {
   177   specpdl_ref count;
   178   ptrdiff_t pos, pos_byte, end, end_byte, start;
   179   ptrdiff_t temp, temp1, mark;
   180   char *buffer;
   181   struct window *w;
   182 
   183   /* Save the excursion, as there will be extensive changes to the
   184      selected window.  */
   185   count = SPECPDL_INDEX ();
   186   record_unwind_protect_excursion ();
   187 
   188   /* Inhibit quitting.  */
   189   specbind (Qinhibit_quit, Qt);
   190 
   191   /* Temporarily switch to F's selected window at the time of the last
   192      redisplay.  */
   193   select_window ((WINDOW_LIVE_P (f->old_selected_window)
   194                   ? f->old_selected_window
   195                   : f->selected_window), Qt);
   196   w = XWINDOW (selected_window);
   197 
   198   /* Now find the appropriate text bounds for QUERY.  First, move
   199      point QUERY->position steps forward or backwards.  */
   200 
   201   pos = PT;
   202 
   203   /* If QUERY->position is EMACS_INT_MAX, use the last mark or the
   204      ephemeral last point, whichever is greater.
   205 
   206      The opposite applies for EMACS_INT_MIN.  */
   207 
   208   mark = get_mark ();
   209 
   210   if (query->position == EMACS_INT_MAX)
   211     {
   212       pos = (mark == -1
   213              ? w->ephemeral_last_point
   214              : max (w->ephemeral_last_point, mark));
   215       goto escape1;
   216     }
   217   else if (query->position == TYPE_MINIMUM (EMACS_INT))
   218     {
   219       pos = (mark == -1
   220              ? w->ephemeral_last_point
   221              : min (w->ephemeral_last_point, mark));
   222       goto escape1;
   223     }
   224 
   225   /* Next, if POS lies within the conversion region and the caller
   226      asked for it to be moved away, move it away from the conversion
   227      region.  */
   228 
   229   if (flags & TEXTCONV_SKIP_CONVERSION_REGION
   230       && MARKERP (f->conversion.compose_region_start))
   231     {
   232       start = marker_position (f->conversion.compose_region_start);
   233       end = marker_position (f->conversion.compose_region_end);
   234 
   235       if (pos >= start && pos < end)
   236         {
   237           switch (query->direction)
   238             {
   239             case TEXTCONV_FORWARD_CHAR:
   240             case TEXTCONV_FORWARD_WORD:
   241             case TEXTCONV_CARET_DOWN:
   242             case TEXTCONV_NEXT_LINE:
   243             case TEXTCONV_LINE_START:
   244               pos = end;
   245               break;
   246 
   247             default:
   248               pos = max (BEGV, start - 1);
   249               break;
   250             }
   251         }
   252     }
   253 
   254   /* If pos is outside the accessible part of the buffer or if it
   255      overflows, move back to point or to the extremes of the
   256      accessible region.  */
   257 
   258   if (ckd_add (&pos, pos, query->position))
   259     pos = PT;
   260 
   261  escape1:
   262 
   263   if (pos < BEGV)
   264     pos = BEGV;
   265 
   266   if (pos > ZV)
   267     pos = ZV;
   268 
   269   /* Move to pos.  */
   270   set_point (pos);
   271   pos = PT;
   272   pos_byte = PT_BYTE;
   273 
   274   /* Now scan forward or backwards according to what is in QUERY.  */
   275 
   276   switch (query->direction)
   277     {
   278     case TEXTCONV_FORWARD_CHAR:
   279       /* Move forward by query->factor characters.  */
   280       if (ckd_add (&end, pos, query->factor) || end > ZV)
   281         end = ZV;
   282 
   283       end_byte = CHAR_TO_BYTE (end);
   284       break;
   285 
   286     case TEXTCONV_BACKWARD_CHAR:
   287       /* Move backward by query->factor characters.  */
   288       if (ckd_sub (&end, pos, query->factor) || end < BEGV)
   289         end = BEGV;
   290 
   291       end_byte = CHAR_TO_BYTE (end);
   292       break;
   293 
   294     case TEXTCONV_FORWARD_WORD:
   295       /* Move forward by query->factor words.  */
   296       end = scan_words (pos, (EMACS_INT) query->factor);
   297 
   298       if (!end)
   299         {
   300           end = ZV;
   301           end_byte = ZV_BYTE;
   302         }
   303       else
   304         end_byte = CHAR_TO_BYTE (end);
   305 
   306       break;
   307 
   308     case TEXTCONV_BACKWARD_WORD:
   309       /* Move backwards by query->factor words.  */
   310       end = scan_words (pos, 0 - (EMACS_INT) query->factor);
   311 
   312       if (!end)
   313         {
   314           end = BEGV;
   315           end_byte = BEGV_BYTE;
   316         }
   317       else
   318         end_byte = CHAR_TO_BYTE (end);
   319 
   320       break;
   321 
   322     case TEXTCONV_CARET_UP:
   323       /* Move upwards one visual line, keeping the column intact.  */
   324       Fvertical_motion (Fcons (Fcurrent_column (), make_fixnum (-1)),
   325                         Qnil, Qnil);
   326       end = PT;
   327       end_byte = PT_BYTE;
   328       break;
   329 
   330     case TEXTCONV_CARET_DOWN:
   331       /* Move downwards one visual line, keeping the column
   332          intact.  */
   333       Fvertical_motion (Fcons (Fcurrent_column (), make_fixnum (1)),
   334                         Qnil, Qnil);
   335       end = PT;
   336       end_byte = PT_BYTE;
   337       break;
   338 
   339     case TEXTCONV_NEXT_LINE:
   340       /* Move one line forward.  */
   341       scan_newline (pos, pos_byte, ZV, ZV_BYTE,
   342                     query->factor, false);
   343       end = PT;
   344       end_byte = PT_BYTE;
   345       break;
   346 
   347     case TEXTCONV_PREVIOUS_LINE:
   348       /* Move one line backwards.  */
   349       scan_newline (pos, pos_byte, BEGV, BEGV_BYTE,
   350                     0 - (EMACS_INT) query->factor, false);
   351       end = PT;
   352       end_byte = PT_BYTE;
   353       break;
   354 
   355     case TEXTCONV_LINE_START:
   356       /* Move to the beginning of the line.  */
   357       Fbeginning_of_line (Qnil);
   358       end = PT;
   359       end_byte = PT_BYTE;
   360       break;
   361 
   362     case TEXTCONV_LINE_END:
   363       /* Move to the end of the line.  */
   364       Fend_of_line (Qnil);
   365       end = PT;
   366       end_byte = PT_BYTE;
   367       break;
   368 
   369     case TEXTCONV_ABSOLUTE_POSITION:
   370       /* How to implement this is unclear.  */
   371       SET_PT (query->factor);
   372       end = PT;
   373       end_byte = PT_BYTE;
   374       break;
   375 
   376     default:
   377       unbind_to (count, Qnil);
   378       return 1;
   379     }
   380 
   381   /* Sort end and pos.  */
   382 
   383   if (end < pos)
   384     {
   385       eassert (end_byte < pos_byte);
   386       temp = pos_byte;
   387       temp1 = pos;
   388       pos_byte = end_byte;
   389       pos = end;
   390       end = temp1;
   391       end_byte = temp;
   392     }
   393 
   394   /* Return the string first.  */
   395   buffer = xmalloc (end_byte - pos_byte);
   396   copy_buffer_text (pos, pos_byte, end, end_byte, buffer);
   397   query->text.text = buffer;
   398   query->text.length = end - pos;
   399   query->text.bytes = end_byte - pos_byte;
   400 
   401   /* Next, perform any operation specified.  */
   402 
   403   switch (query->operation)
   404     {
   405     case TEXTCONV_SUBSTITUTION:
   406       if (safe_del_range (pos, end))
   407         {
   408           /* Undo any changes to the excursion.  */
   409           unbind_to (count, Qnil);
   410           return 1;
   411         }
   412 
   413     default:
   414       break;
   415     }
   416 
   417   /* Undo any changes to the excursion.  */
   418   unbind_to (count, Qnil);
   419   return 0;
   420 }
   421 
   422 /* Update the overlay displaying the conversion area on frame F after
   423    a change to the conversion region.  */
   424 
   425 static void
   426 sync_overlay (struct frame *f)
   427 {
   428   if (MARKERP (f->conversion.compose_region_start)
   429       && !NILP (Vtext_conversion_face))
   430     {
   431       if (NILP (f->conversion.compose_region_overlay))
   432         {
   433           f->conversion.compose_region_overlay
   434             = Fmake_overlay (f->conversion.compose_region_start,
   435                              f->conversion.compose_region_end, Qnil,
   436                              Qt, Qnil);
   437           Foverlay_put (f->conversion.compose_region_overlay,
   438                         Qface, Vtext_conversion_face);
   439         }
   440 
   441       Fmove_overlay (f->conversion.compose_region_overlay,
   442                      f->conversion.compose_region_start,
   443                      f->conversion.compose_region_end, Qnil);
   444     }
   445   else if (!NILP (f->conversion.compose_region_overlay))
   446     {
   447       Fdelete_overlay (f->conversion.compose_region_overlay);
   448       f->conversion.compose_region_overlay = Qnil;
   449     }
   450 }
   451 
   452 /* Record a change to the current buffer as a result of an
   453    asynchronous text conversion operation.
   454 
   455    Consult the doc string of `text-conversion-edits' for the meaning
   456    of BEG, END, and EPHEMERAL.  */
   457 
   458 static void
   459 record_buffer_change (ptrdiff_t beg, ptrdiff_t end,
   460                       Lisp_Object ephemeral)
   461 {
   462   Lisp_Object buffer, beg_marker, end_marker;
   463 
   464   XSETBUFFER (buffer, current_buffer);
   465 
   466   /* Make markers for both BEG and END.  */
   467   beg_marker = build_marker (current_buffer, beg,
   468                              CHAR_TO_BYTE (beg));
   469 
   470   /* If BEG and END are identical, make sure to keep the markers
   471      eq.  */
   472 
   473   if (beg == end)
   474     end_marker = beg_marker;
   475   else
   476     {
   477       end_marker = build_marker (current_buffer, end,
   478                                  CHAR_TO_BYTE (end));
   479 
   480       /* Otherwise, make sure the marker extends past inserted
   481          text.  */
   482       Fset_marker_insertion_type (end_marker, Qt);
   483     }
   484 
   485   Vtext_conversion_edits
   486     = Fcons (list4 (buffer, beg_marker, end_marker,
   487                     ephemeral),
   488              Vtext_conversion_edits);
   489 }
   490 
   491 /* Reset text conversion state of frame F.  Delete any overlays or
   492    markers inside.  */
   493 
   494 void
   495 reset_frame_state (struct frame *f)
   496 {
   497   struct text_conversion_action *last, *next;
   498 
   499   /* Make the composition region markers point elsewhere.  */
   500 
   501   if (!NILP (f->conversion.compose_region_start))
   502     {
   503       Fset_marker (f->conversion.compose_region_start, Qnil, Qnil);
   504       Fset_marker (f->conversion.compose_region_end, Qnil, Qnil);
   505       f->conversion.compose_region_start = Qnil;
   506       f->conversion.compose_region_end = Qnil;
   507     }
   508 
   509   /* Delete the composition region overlay.  */
   510 
   511   if (!NILP (f->conversion.compose_region_overlay))
   512     Fdelete_overlay (f->conversion.compose_region_overlay);
   513 
   514   /* Delete each text conversion action queued up.  */
   515 
   516   next = f->conversion.actions;
   517   while (next)
   518     {
   519       last = next;
   520       next = next->next;
   521 
   522       /* Say that the conversion is finished.  */
   523       if (text_interface && text_interface->notify_conversion)
   524         text_interface->notify_conversion (last->counter);
   525 
   526       xfree (last);
   527     }
   528   f->conversion.actions = NULL;
   529 
   530   /* Clear batch edit state.  */
   531   f->conversion.batch_edit_count = 0;
   532   f->conversion.batch_edit_flags = 0;
   533 }
   534 
   535 /* Return whether or not there are pending edits from an input method
   536    on any frame.  */
   537 
   538 bool
   539 detect_conversion_events (void)
   540 {
   541   Lisp_Object tail, frame;
   542 
   543   FOR_EACH_FRAME (tail, frame)
   544     {
   545       /* See if there's a pending edit on this frame.  */
   546       if (XFRAME (frame)->conversion.actions
   547           && ((XFRAME (frame)->conversion.actions->operation
   548                != TEXTCONV_BARRIER)
   549               || (kbd_fetch_ptr == kbd_store_ptr)))
   550         return true;
   551     }
   552 
   553   return false;
   554 }
   555 
   556 /* Restore the selected window WINDOW.  */
   557 
   558 static void
   559 restore_selected_window (Lisp_Object window)
   560 {
   561   /* FIXME: not sure what to do if WINDOW has been deleted.  */
   562   select_window (window, Qt);
   563 }
   564 
   565 /* Commit the given text in the composing region.  If there is no
   566    composing region, then insert the text after frame F's selected
   567    window's last point instead, unless the mark is active.  Finally,
   568    remove the composing region.
   569 
   570    If the mark is active, delete the text between mark and point.
   571 
   572    Then, move point to POSITION relative to TEXT.  If POSITION is
   573    greater than zero, it is relative to the character at the end of
   574    TEXT; otherwise, it is relative to the start of TEXT.  */
   575 
   576 static void
   577 really_commit_text (struct frame *f, EMACS_INT position,
   578                     Lisp_Object text)
   579 {
   580   specpdl_ref count;
   581   ptrdiff_t wanted, start, end, mark;
   582   struct window *w;
   583 
   584   /* If F's old selected window is no longer alive, fail.  */
   585 
   586   if (!WINDOW_LIVE_P (f->old_selected_window))
   587     return;
   588 
   589   count = SPECPDL_INDEX ();
   590   record_unwind_protect (restore_selected_window,
   591                          selected_window);
   592 
   593   /* Temporarily switch to F's selected window at the time of the last
   594      redisplay.  */
   595   select_window (f->old_selected_window, Qt);
   596 
   597   /* Now detect whether or not there is a composing or active region.
   598      If there is, then replace it with TEXT.  Don't do that
   599      otherwise.  */
   600 
   601   mark = get_mark ();
   602   if (MARKERP (f->conversion.compose_region_start) || mark != -1)
   603     {
   604       /* Replace its contents.  Set START and END to the start and end
   605          of the composing region if it exists.  */
   606 
   607       if (MARKERP (f->conversion.compose_region_start))
   608         {
   609           start = marker_position (f->conversion.compose_region_start);
   610           end = marker_position (f->conversion.compose_region_end);
   611         }
   612       else
   613         {
   614           /* Otherwise, set it to the start and end of the region.  */
   615           start = min (mark, PT);
   616           end = max (mark, PT);
   617         }
   618 
   619       /* Now delete whatever needs to go.  */
   620 
   621       del_range_1 (start, end, true, false);
   622       record_buffer_change (start, start, Qt);
   623 
   624       /* Don't record changes if TEXT is empty.  */
   625 
   626       if (SCHARS (text))
   627         {
   628           /* Insert the new text.  Make sure to inherit text
   629              properties from the surroundings: if this doesn't happen,
   630              CC Mode fontification can get thrown off and become very
   631              slow.  */
   632 
   633           insert_from_string (text, 0, 0, SCHARS (text),
   634                               SBYTES (text), true);
   635           record_buffer_change (start, PT, text);
   636         }
   637 
   638       /* Move to a the position specified in POSITION.  */
   639 
   640       if (position <= 0)
   641         {
   642           /* If POSITION is less than zero, it is relative to the
   643              start of the text that was inserted.  */
   644           wanted = start;
   645 
   646           if (INT_ADD_WRAPV (wanted, position, &wanted)
   647               || wanted < BEGV)
   648             wanted = BEGV;
   649 
   650           if (wanted > ZV)
   651             wanted = ZV;
   652 
   653           set_point (wanted);
   654         }
   655       else
   656         {
   657           /* Otherwise, it is relative to the last character in
   658              TEXT.  */
   659           wanted = PT;
   660 
   661           if (INT_ADD_WRAPV (wanted, position - 1, &wanted)
   662               || wanted > ZV)
   663             wanted = ZV;
   664 
   665           if (wanted < BEGV)
   666             wanted = BEGV;
   667 
   668           set_point (wanted);
   669         }
   670 
   671       /* Make the composition region markers point elsewhere.  */
   672 
   673       if (!NILP (f->conversion.compose_region_start))
   674         {
   675           Fset_marker (f->conversion.compose_region_start, Qnil, Qnil);
   676           Fset_marker (f->conversion.compose_region_end, Qnil, Qnil);
   677           f->conversion.compose_region_start = Qnil;
   678           f->conversion.compose_region_end = Qnil;
   679         }
   680 
   681       /* Delete the composition region overlay.  */
   682 
   683       if (!NILP (f->conversion.compose_region_overlay))
   684         Fdelete_overlay (f->conversion.compose_region_overlay);
   685     }
   686   else
   687     {
   688       /* Otherwise, move the text and point to an appropriate
   689          location.  */
   690       wanted = PT;
   691 
   692       /* Don't record changes if TEXT is empty.  */
   693 
   694       if (SCHARS (text))
   695         {
   696           /* Insert the new text.  Make sure to inherit text
   697              properties from the surroundings: if this doesn't happen,
   698              CC Mode fontification can get thrown off and become very
   699              slow.  */
   700 
   701           insert_from_string (text, 0, 0, SCHARS (text),
   702                               SBYTES (text), true);
   703 
   704           record_buffer_change (wanted, PT, text);
   705         }
   706 
   707       if (position <= 0)
   708         {
   709           if (INT_ADD_WRAPV (wanted, position, &wanted)
   710               || wanted < BEGV)
   711             wanted = BEGV;
   712 
   713           if (wanted > ZV)
   714             wanted = ZV;
   715 
   716           set_point (wanted);
   717         }
   718       else
   719         {
   720           wanted = PT;
   721 
   722           if (INT_ADD_WRAPV (wanted, position - 1, &wanted)
   723               || wanted > ZV)
   724             wanted = ZV;
   725 
   726           if (wanted < BEGV)
   727             wanted = BEGV;
   728 
   729           set_point (wanted);
   730         }
   731     }
   732 
   733   /* This should deactivate the mark.  */
   734   call0 (Qdeactivate_mark);
   735 
   736   /* Print some debugging information.  */
   737   TEXTCONV_DEBUG ("text inserted: %s, point now: %zd",
   738                   SSDATA (text), PT);
   739 
   740   /* Update the ephemeral last point.  */
   741   w = XWINDOW (selected_window);
   742   w->ephemeral_last_point = PT;
   743   unbind_to (count, Qnil);
   744 }
   745 
   746 /* Remove the composition region on the frame F, while leaving its
   747    contents intact.  If UPDATE, also notify the input method of the
   748    change.  */
   749 
   750 static void
   751 really_finish_composing_text (struct frame *f, bool update)
   752 {
   753   if (!NILP (f->conversion.compose_region_start))
   754     {
   755       Fset_marker (f->conversion.compose_region_start, Qnil, Qnil);
   756       Fset_marker (f->conversion.compose_region_end, Qnil, Qnil);
   757       f->conversion.compose_region_start = Qnil;
   758       f->conversion.compose_region_end = Qnil;
   759 
   760       if (update && text_interface
   761           && text_interface->compose_region_changed)
   762         (*text_interface->compose_region_changed) (f);
   763     }
   764 
   765   /* Delete the composition region overlay.  */
   766 
   767   if (!NILP (f->conversion.compose_region_overlay))
   768     Fdelete_overlay (f->conversion.compose_region_overlay);
   769 
   770   TEXTCONV_DEBUG ("conversion region removed");
   771 }
   772 
   773 /* Set the composing text on frame F to TEXT.  Then, move point to an
   774    appropriate position relative to POSITION, and call
   775    `compose_region_changed' in the text conversion interface should
   776    point not have been changed relative to F's old selected window's
   777    last point.  */
   778 
   779 static void
   780 really_set_composing_text (struct frame *f, ptrdiff_t position,
   781                            Lisp_Object text)
   782 {
   783   specpdl_ref count;
   784   ptrdiff_t start, wanted, end;
   785   struct window *w;
   786 
   787   /* If F's old selected window is no longer live, fail.  */
   788 
   789   if (!WINDOW_LIVE_P (f->old_selected_window))
   790     return;
   791 
   792   count = SPECPDL_INDEX ();
   793   record_unwind_protect (restore_selected_window,
   794                          selected_window);
   795 
   796   /* Temporarily switch to F's selected window at the time of the last
   797      redisplay.  */
   798   w = XWINDOW (f->old_selected_window);
   799   select_window (f->old_selected_window, Qt);
   800 
   801   /* Now set up the composition region if necessary.  */
   802 
   803   if (!MARKERP (f->conversion.compose_region_start))
   804     {
   805       /* Set START and END.  */
   806       start = PT;
   807       wanted = end = get_mark ();
   808 
   809       /* If END is -1, set it to start.  */
   810 
   811       if (end == -1)
   812         end = start;
   813       else
   814         {
   815           /* Now sort start and end.  */
   816           start = min (start, end);
   817           end  = max (PT, wanted);
   818         }
   819 
   820       /* If END is not the same as start, delete the text in
   821          between.  */
   822 
   823       if (end != start)
   824         {
   825           del_range_1 (start, end, true, false);
   826           set_point (start);
   827           record_buffer_change (start, start, Qt);
   828         }
   829 
   830       /* Now set the markers which denote the composition region.  */
   831       f->conversion.compose_region_start
   832         = build_marker (current_buffer, PT, PT_BYTE);
   833       f->conversion.compose_region_end
   834         = build_marker (current_buffer, PT, PT_BYTE);
   835 
   836       Fset_marker_insertion_type (f->conversion.compose_region_end,
   837                                   Qt);
   838     }
   839   else
   840     {
   841       /* Delete the text between the start of the composing region and
   842          its end.  */
   843       start = marker_position (f->conversion.compose_region_start);
   844       end = marker_position (f->conversion.compose_region_end);
   845       del_range_1 (start, end, true, false);
   846       set_point (start);
   847 
   848       if (start != end)
   849         record_buffer_change (start, start, Qt);
   850     }
   851 
   852   /* Insert the new text.  Make sure to inherit text properties from
   853      the surroundings: if this doesn't happen, CC Mode fontification
   854      can get thrown off and become very slow.  */
   855 
   856   insert_from_string (text, 0, 0, SCHARS (text),
   857                       SBYTES (text), true);
   858 
   859   if (start != PT)
   860     record_buffer_change (start, PT, Qt);
   861 
   862   /* Now move point to an appropriate location.  */
   863   if (position <= 0)
   864     {
   865       wanted = start;
   866 
   867       if (INT_SUBTRACT_WRAPV (wanted, position, &wanted)
   868           || wanted < BEGV)
   869         wanted = BEGV;
   870 
   871       if (wanted > ZV)
   872         wanted = ZV;
   873     }
   874   else
   875     {
   876       end = marker_position (f->conversion.compose_region_end);
   877       wanted = end;
   878 
   879       /* end should be PT after the edit.  */
   880       eassert (end == PT);
   881 
   882       if (INT_ADD_WRAPV (wanted, position - 1, &wanted)
   883           || wanted > ZV)
   884         wanted = ZV;
   885 
   886       if (wanted < BEGV)
   887         wanted = BEGV;
   888     }
   889 
   890   set_point (wanted);
   891 
   892   /* This should deactivate the mark.  */
   893   call0 (Qdeactivate_mark);
   894 
   895   /* Move the composition overlay.  */
   896   sync_overlay (f);
   897 
   898   /* If TEXT is empty, remove the composing region.  This goes against
   899      the documentation, but is ultimately what programs expect.  */
   900 
   901   if (!SCHARS (text))
   902     really_finish_composing_text (f, false);
   903 
   904   /* If PT hasn't changed, the conversion region definitely has.
   905      Otherwise, redisplay will update the input method instead.  */
   906 
   907   if (PT == w->ephemeral_last_point
   908       && text_interface
   909       && text_interface->compose_region_changed)
   910     {
   911       if (f->conversion.batch_edit_count > 0)
   912         f->conversion.batch_edit_flags |= PENDING_COMPOSE_CHANGE;
   913       else
   914         text_interface->compose_region_changed (f);
   915     }
   916 
   917   /* Update the ephemeral last point.  */
   918   w = XWINDOW (selected_window);
   919   w->ephemeral_last_point = PT;
   920 
   921   if (SCHARS (text))
   922     TEXTCONV_DEBUG ("conversion region set to: %td %td",
   923                     marker_position (f->conversion.compose_region_start),
   924                     marker_position (f->conversion.compose_region_end));
   925   else
   926     TEXTCONV_DEBUG ("conversion region removed; PT is now: %td", PT);
   927 
   928   unbind_to (count, Qnil);
   929 }
   930 
   931 /* Set the composing region of frame F to START by END.  Make it if
   932    it is not already set.  */
   933 
   934 static void
   935 really_set_composing_region (struct frame *f, ptrdiff_t start,
   936                              ptrdiff_t end)
   937 {
   938   specpdl_ref count;
   939   struct window *w;
   940 
   941   /* If F's old selected window is no longer live, fail.  */
   942 
   943   if (!WINDOW_LIVE_P (f->old_selected_window))
   944     return;
   945 
   946   /* If MAX (0, start) == end, then this should behave the same as
   947      really_finish_composing_text.  */
   948 
   949   if (max (0, start) == max (0, end))
   950     {
   951       really_finish_composing_text (f, false);
   952       return;
   953     }
   954 
   955   count = SPECPDL_INDEX ();
   956   record_unwind_protect (restore_selected_window,
   957                          selected_window);
   958 
   959   /* Temporarily switch to F's selected window at the time of the last
   960      redisplay.  */
   961   select_window (f->old_selected_window, Qt);
   962 
   963   /* Now set up the composition region if necessary.  */
   964 
   965   if (!MARKERP (f->conversion.compose_region_start))
   966     {
   967       f->conversion.compose_region_start = Fmake_marker ();
   968       f->conversion.compose_region_end = Fmake_marker ();
   969       Fset_marker_insertion_type (f->conversion.compose_region_end,
   970                                   Qt);
   971     }
   972 
   973   Fset_marker (f->conversion.compose_region_start,
   974                make_fixnum (start), Qnil);
   975   Fset_marker (f->conversion.compose_region_end,
   976                make_fixnum (end), Qnil);
   977   sync_overlay (f);
   978 
   979   TEXTCONV_DEBUG ("composing region set to: %td, %td; point is: %td",
   980                   start, end, PT);
   981 
   982   /* Update the ephemeral last point.  */
   983   w = XWINDOW (selected_window);
   984   w->ephemeral_last_point = PT;
   985 
   986   unbind_to (count, Qnil);
   987 }
   988 
   989 /* Delete LEFT and RIGHT chars around point or the active mark,
   990    whichever is larger, in frame F's selected window, avoiding the
   991    composing region if necessary.  */
   992 
   993 static void
   994 really_delete_surrounding_text (struct frame *f, ptrdiff_t left,
   995                                 ptrdiff_t right)
   996 {
   997   specpdl_ref count;
   998   ptrdiff_t start, end, a, b, a1, b1, lstart, rstart;
   999   struct window *w;
  1000   Lisp_Object text;
  1001 
  1002   /* If F's old selected window is no longer live, fail.  */
  1003 
  1004   if (!WINDOW_LIVE_P (f->old_selected_window))
  1005     return;
  1006 
  1007   count = SPECPDL_INDEX ();
  1008   record_unwind_protect (restore_selected_window,
  1009                          selected_window);
  1010 
  1011   /* Temporarily switch to F's selected window at the time of the last
  1012      redisplay.  */
  1013   select_window (f->old_selected_window, Qt);
  1014 
  1015   /* Figure out where to start deleting from.  */
  1016 
  1017   a = get_mark ();
  1018 
  1019   if (a != -1 && a != PT)
  1020     lstart = rstart = max (a, PT);
  1021   else
  1022     lstart = rstart = PT;
  1023 
  1024   /* Avoid the composing text.  This behavior is identical to how
  1025      Android's BaseInputConnection actually implements avoiding the
  1026      composing span.  */
  1027 
  1028   if (MARKERP (f->conversion.compose_region_start))
  1029     {
  1030       a = marker_position (f->conversion.compose_region_start);
  1031       b = marker_position (f->conversion.compose_region_end);
  1032 
  1033       a1 = min (a, b);
  1034       b1 = max (a, b);
  1035 
  1036       lstart = min (lstart, min (PT, a1));
  1037       rstart = max (rstart, max (PT, b1));
  1038     }
  1039 
  1040   if (lstart == rstart)
  1041     {
  1042       start = max (BEGV, lstart - left);
  1043       end = min (ZV, rstart + right);
  1044 
  1045       text = del_range_1 (start, end, true, true);
  1046       record_buffer_change (start, start, text);
  1047     }
  1048   else
  1049     {
  1050       /* Don't record a deletion if the text which was deleted lies
  1051          after point.  */
  1052 
  1053       start = rstart;
  1054       end = min (ZV, rstart + right);
  1055       text = del_range_1 (start, end, true, true);
  1056       record_buffer_change (start, start, Qnil);
  1057 
  1058       /* Now delete what must be deleted on the left.  */
  1059 
  1060       start = max (BEGV, lstart - left);
  1061       end = lstart;
  1062       text = del_range_1 (start, end, true, true);
  1063       record_buffer_change (start, start, text);
  1064     }
  1065 
  1066   TEXTCONV_DEBUG ("deleted surrounding text: %td, %td; PT is now %td",
  1067                   left, right, PT);
  1068 
  1069   /* if the mark is now equal to start, deactivate it.  */
  1070 
  1071   if (get_mark () == PT)
  1072     call0 (Qdeactivate_mark);
  1073 
  1074   /* Update the ephemeral last point.  */
  1075   w = XWINDOW (selected_window);
  1076   w->ephemeral_last_point = PT;
  1077 
  1078   unbind_to (count, Qnil);
  1079 }
  1080 
  1081 /* Update the interface with frame F's new point and mark.  If a batch
  1082    edit is in progress, schedule the update for when it finishes
  1083    instead.  */
  1084 
  1085 static void
  1086 really_request_point_update (struct frame *f)
  1087 {
  1088   /* If F's old selected window is no longer live, fail.  */
  1089 
  1090   if (!WINDOW_LIVE_P (f->old_selected_window))
  1091     return;
  1092 
  1093   if (f->conversion.batch_edit_count > 0)
  1094     f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
  1095   else if (text_interface && text_interface->point_changed)
  1096     text_interface->point_changed (f,
  1097                                    XWINDOW (f->old_selected_window),
  1098                                    current_buffer);
  1099 }
  1100 
  1101 /* Set point in frame F's selected window to POSITION.  If MARK is not
  1102    at POSITION, activate the mark and set MARK to that as well.
  1103 
  1104    If point was not changed, signal an update through the text input
  1105    interface, which is necessary for the IME to acknowledge that the
  1106    change has completed.  */
  1107 
  1108 static void
  1109 really_set_point_and_mark (struct frame *f, ptrdiff_t point,
  1110                            ptrdiff_t mark)
  1111 {
  1112   specpdl_ref count;
  1113   struct window *w;
  1114 
  1115   /* If F's old selected window is no longer live, fail.  */
  1116 
  1117   if (!WINDOW_LIVE_P (f->old_selected_window))
  1118     return;
  1119 
  1120   count = SPECPDL_INDEX ();
  1121   record_unwind_protect (restore_selected_window,
  1122                          selected_window);
  1123 
  1124   /* Temporarily switch to F's selected window at the time of the last
  1125      redisplay.  */
  1126   select_window (f->old_selected_window, Qt);
  1127 
  1128   if (point == PT)
  1129     {
  1130       if (f->conversion.batch_edit_count > 0)
  1131         f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
  1132       else if (text_interface && text_interface->point_changed)
  1133         text_interface->point_changed (f,
  1134                                        XWINDOW (f->old_selected_window),
  1135                                        current_buffer);
  1136     }
  1137   else
  1138     /* Set the point.  */
  1139     Fgoto_char (make_fixnum (point));
  1140 
  1141   if (mark == point
  1142       && !NILP (BVAR (current_buffer, mark_active)))
  1143     call0 (Qdeactivate_mark);
  1144   else
  1145     call1 (Qpush_mark, make_fixnum (mark));
  1146 
  1147   /* Update the ephemeral last point.  */
  1148   w = XWINDOW (selected_window);
  1149   w->ephemeral_last_point = PT;
  1150 
  1151   TEXTCONV_DEBUG ("set point and mark: %td %td",
  1152                   PT, get_mark ());
  1153 
  1154   unbind_to (count, Qnil);
  1155 }
  1156 
  1157 /* Complete the edit specified by the counter value inside *TOKEN.  */
  1158 
  1159 static void
  1160 complete_edit (void *token)
  1161 {
  1162   if (text_interface && text_interface->notify_conversion)
  1163     text_interface->notify_conversion (*(unsigned long *) token);
  1164 }
  1165 
  1166 /* Context for complete_edit_check.  */
  1167 
  1168 struct complete_edit_check_context
  1169 {
  1170   /* The window.  */
  1171   struct window *w;
  1172 
  1173   /* Whether or not editing was successful.  */
  1174   bool check;
  1175 };
  1176 
  1177 /* Convert PTR to CONTEXT.  If CONTEXT->check is false, then update
  1178    CONTEXT->w's ephemeral last point and give it to the input method,
  1179    the assumption being that an editing operation signalled.  */
  1180 
  1181 static void
  1182 complete_edit_check (void *ptr)
  1183 {
  1184   struct complete_edit_check_context *context;
  1185   struct frame *f;
  1186 
  1187   context = ptr;
  1188 
  1189   if (!context->check)
  1190     {
  1191       /* Figure out the new position of point.  */
  1192       context->w->ephemeral_last_point
  1193         = window_point (context->w);
  1194 
  1195       /* See if the frame is still alive.  */
  1196 
  1197       f = WINDOW_XFRAME (context->w);
  1198 
  1199       if (!FRAME_LIVE_P (f))
  1200         return;
  1201 
  1202       if (text_interface && text_interface->point_changed)
  1203         {
  1204           if (f->conversion.batch_edit_count > 0)
  1205             f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
  1206           else
  1207             text_interface->point_changed (f, context->w, NULL);
  1208         }
  1209     }
  1210 }
  1211 
  1212 /* Process and free the text conversion ACTION.  F must be the frame
  1213    on which ACTION will be performed.
  1214 
  1215    Value is the window which was used, or NULL.  */
  1216 
  1217 static struct window *
  1218 handle_pending_conversion_events_1 (struct frame *f,
  1219                                     struct text_conversion_action *action)
  1220 {
  1221   Lisp_Object data;
  1222   enum text_conversion_operation operation;
  1223   struct buffer *buffer UNINIT;
  1224   struct window *w;
  1225   specpdl_ref count;
  1226   unsigned long token;
  1227   struct complete_edit_check_context context;
  1228 
  1229   /* Next, process this action and free it.  */
  1230 
  1231   data = action->data;
  1232   operation = action->operation;
  1233   token = action->counter;
  1234   xfree (action);
  1235 
  1236   /* Text conversion events can still arrive immediately after
  1237      `conversion_disabled_p' becomes true.  In that case, process all
  1238      events, but don't perform any associated actions.  */
  1239 
  1240   if (conversion_disabled_p ())
  1241     return NULL;
  1242 
  1243   /* check is a flag used by complete_edit_check to determine whether
  1244      or not the editing operation completed successfully.  */
  1245   context.check = false;
  1246 
  1247   /* Make sure completion is signalled.  */
  1248   count = SPECPDL_INDEX ();
  1249   record_unwind_protect_ptr (complete_edit, &token);
  1250   w = NULL;
  1251 
  1252   if (WINDOW_LIVE_P (f->old_selected_window))
  1253     {
  1254       w = XWINDOW (f->old_selected_window);
  1255       buffer = XBUFFER (WINDOW_BUFFER (w));
  1256       context.w = w;
  1257 
  1258       /* Notify the input method of any editing failures.  */
  1259       record_unwind_protect_ptr (complete_edit_check, &context);
  1260     }
  1261 
  1262   switch (operation)
  1263     {
  1264     case TEXTCONV_START_BATCH_EDIT:
  1265       f->conversion.batch_edit_count++;
  1266       break;
  1267 
  1268     case TEXTCONV_END_BATCH_EDIT:
  1269       if (f->conversion.batch_edit_count > 0)
  1270         f->conversion.batch_edit_count--;
  1271 
  1272       if (!WINDOW_LIVE_P (f->old_selected_window))
  1273         break;
  1274 
  1275       if (f->conversion.batch_edit_flags & PENDING_POINT_CHANGE)
  1276         text_interface->point_changed (f, w, buffer);
  1277 
  1278       if (f->conversion.batch_edit_flags & PENDING_COMPOSE_CHANGE)
  1279         text_interface->compose_region_changed (f);
  1280 
  1281       f->conversion.batch_edit_flags = 0;
  1282       break;
  1283 
  1284     case TEXTCONV_COMMIT_TEXT:
  1285       really_commit_text (f, XFIXNUM (XCAR (data)), XCDR (data));
  1286       break;
  1287 
  1288     case TEXTCONV_FINISH_COMPOSING_TEXT:
  1289       really_finish_composing_text (f, !NILP (data));
  1290       break;
  1291 
  1292     case TEXTCONV_SET_COMPOSING_TEXT:
  1293       really_set_composing_text (f, XFIXNUM (XCAR (data)),
  1294                                  XCDR (data));
  1295       break;
  1296 
  1297     case TEXTCONV_SET_COMPOSING_REGION:
  1298       really_set_composing_region (f, XFIXNUM (XCAR (data)),
  1299                                    XFIXNUM (XCDR (data)));
  1300       break;
  1301 
  1302     case TEXTCONV_SET_POINT_AND_MARK:
  1303       really_set_point_and_mark (f, XFIXNUM (XCAR (data)),
  1304                                  XFIXNUM (XCDR (data)));
  1305       break;
  1306 
  1307     case TEXTCONV_DELETE_SURROUNDING_TEXT:
  1308       really_delete_surrounding_text (f, XFIXNUM (XCAR (data)),
  1309                                       XFIXNUM (XCDR (data)));
  1310       break;
  1311 
  1312     case TEXTCONV_REQUEST_POINT_UPDATE:
  1313       really_request_point_update (f);
  1314       break;
  1315 
  1316     case TEXTCONV_BARRIER:
  1317       if (kbd_fetch_ptr != kbd_store_ptr)
  1318         emacs_abort ();
  1319 
  1320       /* Once a barrier is hit, synchronize F's selected window's
  1321          `ephemeral_last_point' with its current point.  The reason
  1322          for this is because otherwise a previous keyboard event may
  1323          have taken place without redisplay happening in between.  */
  1324 
  1325       if (w)
  1326         w->ephemeral_last_point = window_point (w);
  1327       break;
  1328     }
  1329 
  1330   /* Signal success.  */
  1331   context.check = true;
  1332   unbind_to (count, Qnil);
  1333 
  1334   return w;
  1335 }
  1336 
  1337 /* Decrement the variable pointed to by *PTR.  */
  1338 
  1339 static void
  1340 decrement_inside (void *ptr)
  1341 {
  1342   int *i;
  1343 
  1344   i = ptr;
  1345   (*i)--;
  1346 }
  1347 
  1348 /* Process any outstanding text conversion events.
  1349    This may run Lisp or signal.  */
  1350 
  1351 void
  1352 handle_pending_conversion_events (void)
  1353 {
  1354   struct frame *f;
  1355   Lisp_Object tail, frame;
  1356   struct text_conversion_action *action, *next;
  1357   bool handled;
  1358   static int inside;
  1359   specpdl_ref count;
  1360   ptrdiff_t last_point;
  1361   struct window *w;
  1362 
  1363   handled = false;
  1364 
  1365   /* Reset Vtext_conversion_edits.  Do not do this if called
  1366      reentrantly.  */
  1367 
  1368   if (!inside)
  1369     Vtext_conversion_edits = Qnil;
  1370 
  1371   inside++;
  1372 
  1373   count = SPECPDL_INDEX ();
  1374   record_unwind_protect_ptr (decrement_inside, &inside);
  1375 
  1376   FOR_EACH_FRAME (tail, frame)
  1377     {
  1378       f = XFRAME (frame);
  1379       last_point = -1;
  1380       w = NULL;
  1381 
  1382       /* Test if F has any outstanding conversion events.  Then
  1383          process them in bottom to up order.  */
  1384       while (true)
  1385         {
  1386           /* Update the input method if handled &&
  1387              w->ephemeral_last_point != last_point.  */
  1388           if (w && (last_point != w->ephemeral_last_point))
  1389             {
  1390               if (handled
  1391                   && last_point != -1
  1392                   && text_interface
  1393                   && text_interface->point_changed)
  1394                 {
  1395                   if (f->conversion.batch_edit_count > 0)
  1396                     f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
  1397                   else
  1398                     text_interface->point_changed (f, NULL, NULL);
  1399                 }
  1400 
  1401               last_point = w->ephemeral_last_point;
  1402             }
  1403 
  1404           /* Reload action.  This needs to be reentrant as buffer
  1405              modification functions can call `read-char'.  */
  1406           action = f->conversion.actions;
  1407 
  1408           /* If there are no more actions, break.  */
  1409 
  1410           if (!action)
  1411             break;
  1412 
  1413           /* If action is a barrier event and the keyboard buffer is
  1414              not yet empty, break out of the loop.  */
  1415 
  1416           if (action->operation == TEXTCONV_BARRIER
  1417               && kbd_store_ptr != kbd_fetch_ptr)
  1418             break;
  1419 
  1420           /* Unlink this action.  */
  1421           next = action->next;
  1422           f->conversion.actions = next;
  1423 
  1424           /* Handle and free the action.  */
  1425           w = handle_pending_conversion_events_1 (f, action);
  1426           handled = true;
  1427         }
  1428     }
  1429 
  1430   unbind_to (count, Qnil);
  1431 }
  1432 
  1433 /* Start a ``batch edit'' in frame F.  During a batch edit,
  1434    point_changed will not be called until the batch edit ends.
  1435 
  1436    Process the actual operation in the event loop in keyboard.c; then,
  1437    call `notify_conversion' in the text conversion interface with
  1438    COUNTER.  */
  1439 
  1440 void
  1441 start_batch_edit (struct frame *f, unsigned long counter)
  1442 {
  1443   struct text_conversion_action *action, **last;
  1444 
  1445   action = xmalloc (sizeof *action);
  1446   action->operation = TEXTCONV_START_BATCH_EDIT;
  1447   action->data = Qnil;
  1448   action->next = NULL;
  1449   action->counter = counter;
  1450   for (last = &f->conversion.actions; *last; last = &(*last)->next)
  1451     ;;
  1452   *last = action;
  1453   input_pending = true;
  1454 }
  1455 
  1456 /* End a ``batch edit''.  It is ok to call this function even if a
  1457    batch edit has not yet started, in which case it does nothing.
  1458 
  1459    COUNTER means the same as in `start_batch_edit'.  */
  1460 
  1461 void
  1462 end_batch_edit (struct frame *f, unsigned long counter)
  1463 {
  1464   struct text_conversion_action *action, **last;
  1465 
  1466   action = xmalloc (sizeof *action);
  1467   action->operation = TEXTCONV_END_BATCH_EDIT;
  1468   action->data = Qnil;
  1469   action->next = NULL;
  1470   action->counter = counter;
  1471   for (last = &f->conversion.actions; *last; last = &(*last)->next)
  1472     ;;
  1473   *last = action;
  1474   input_pending = true;
  1475 }
  1476 
  1477 /* Insert the specified STRING into frame F's selected-window's
  1478    buffer's composition region, and set point to POSITION relative to
  1479    STRING.
  1480 
  1481    If there is no composition region, use the active region instead.
  1482    If that doesn't exist either, insert STRING after point.
  1483 
  1484    COUNTER means the same as in `start_batch_edit'.  */
  1485 
  1486 void
  1487 commit_text (struct frame *f, Lisp_Object string,
  1488              ptrdiff_t position, unsigned long counter)
  1489 {
  1490   struct text_conversion_action *action, **last;
  1491 
  1492   action = xmalloc (sizeof *action);
  1493   action->operation = TEXTCONV_COMMIT_TEXT;
  1494   action->data = Fcons (make_fixnum (position), string);
  1495   action->next = NULL;
  1496   action->counter = counter;
  1497   for (last = &f->conversion.actions; *last; last = &(*last)->next)
  1498     ;;
  1499   *last = action;
  1500   input_pending = true;
  1501 }
  1502 
  1503 /* Remove the composition region and its overlay from frame F's
  1504    selected-window's current buffer.  Leave the text being composed
  1505    intact.
  1506 
  1507    If UPDATE, call `compose_region_changed' after the region is
  1508    removed.
  1509 
  1510    COUNTER means the same as in `start_batch_edit'.  */
  1511 
  1512 void
  1513 finish_composing_text (struct frame *f, unsigned long counter,
  1514                        bool update)
  1515 {
  1516   struct text_conversion_action *action, **last;
  1517 
  1518   action = xmalloc (sizeof *action);
  1519   action->operation = TEXTCONV_FINISH_COMPOSING_TEXT;
  1520   action->data = update ? Qt : Qnil;
  1521   action->next = NULL;
  1522   action->counter = counter;
  1523   for (last = &f->conversion.actions; *last; last = &(*last)->next)
  1524     ;;
  1525   *last = action;
  1526   input_pending = true;
  1527 }
  1528 
  1529 /* Insert the given STRING and make it the currently active
  1530    composition.
  1531 
  1532    If there is currently no composing or active region, then the new
  1533    value of point is used as the composing region.
  1534 
  1535    Then, the composing or active region is replaced with the text in
  1536    the specified string.
  1537 
  1538    Finally, move point to new_point, which is relative to either the
  1539    start or the end of OBJECT depending on whether or not it is less
  1540    than zero.
  1541 
  1542    COUNTER means the same as in `start_batch_edit'.  */
  1543 
  1544 void
  1545 set_composing_text (struct frame *f, Lisp_Object object,
  1546                     ptrdiff_t new_point, unsigned long counter)
  1547 {
  1548   struct text_conversion_action *action, **last;
  1549 
  1550   action = xmalloc (sizeof *action);
  1551   action->operation = TEXTCONV_SET_COMPOSING_TEXT;
  1552   action->data = Fcons (make_fixnum (new_point),
  1553                         object);
  1554   action->next = NULL;
  1555   action->counter = counter;
  1556   for (last = &f->conversion.actions; *last; last = &(*last)->next)
  1557     ;;
  1558   *last = action;
  1559   input_pending = true;
  1560 }
  1561 
  1562 /* Make the region between START and END the currently active
  1563    ``composing region'' on frame F.
  1564 
  1565    The ``composing region'' is a region of text in the buffer that is
  1566    about to undergo editing by the input method.  */
  1567 
  1568 void
  1569 set_composing_region (struct frame *f, ptrdiff_t start,
  1570                       ptrdiff_t end, unsigned long counter)
  1571 {
  1572   struct text_conversion_action *action, **last;
  1573 
  1574   if (start > MOST_POSITIVE_FIXNUM)
  1575     start = MOST_POSITIVE_FIXNUM;
  1576 
  1577   if (end > MOST_POSITIVE_FIXNUM)
  1578     end = MOST_POSITIVE_FIXNUM;
  1579 
  1580   action = xmalloc (sizeof *action);
  1581   action->operation = TEXTCONV_SET_COMPOSING_REGION;
  1582   action->data = Fcons (make_fixnum (start),
  1583                         make_fixnum (end));
  1584   action->next = NULL;
  1585   action->counter = counter;
  1586   for (last = &f->conversion.actions; *last; last = &(*last)->next)
  1587     ;;
  1588   *last = action;
  1589   input_pending = true;
  1590 }
  1591 
  1592 /* Move point in frame F's selected-window's buffer to POINT and maybe
  1593    push MARK.
  1594 
  1595    COUNTER means the same as in `start_batch_edit'.  */
  1596 
  1597 void
  1598 textconv_set_point_and_mark (struct frame *f, ptrdiff_t point,
  1599                              ptrdiff_t mark, unsigned long counter)
  1600 {
  1601   struct text_conversion_action *action, **last;
  1602 
  1603   if (point > MOST_POSITIVE_FIXNUM)
  1604     point = MOST_POSITIVE_FIXNUM;
  1605 
  1606   action = xmalloc (sizeof *action);
  1607   action->operation = TEXTCONV_SET_POINT_AND_MARK;
  1608   action->data = Fcons (make_fixnum (point),
  1609                         make_fixnum (mark));
  1610   action->next = NULL;
  1611   action->counter = counter;
  1612   for (last = &f->conversion.actions; *last; last = &(*last)->next)
  1613     ;;
  1614   *last = action;
  1615   input_pending = true;
  1616 }
  1617 
  1618 /* Delete LEFT and RIGHT characters around point in frame F's old
  1619    selected window.  */
  1620 
  1621 void
  1622 delete_surrounding_text (struct frame *f, ptrdiff_t left,
  1623                          ptrdiff_t right, unsigned long counter)
  1624 {
  1625   struct text_conversion_action *action, **last;
  1626 
  1627   action = xmalloc (sizeof *action);
  1628   action->operation = TEXTCONV_DELETE_SURROUNDING_TEXT;
  1629   action->data = Fcons (make_fixnum (left),
  1630                         make_fixnum (right));
  1631   action->next = NULL;
  1632   action->counter = counter;
  1633   for (last = &f->conversion.actions; *last; last = &(*last)->next)
  1634     ;;
  1635   *last = action;
  1636   input_pending = true;
  1637 }
  1638 
  1639 /* Request an immediate call to TEXT_INTERFACE->point_changed with the
  1640    new details of frame F's region unless a batch edit is in
  1641    progress.  */
  1642 
  1643 void
  1644 request_point_update (struct frame *f, unsigned long counter)
  1645 {
  1646   struct text_conversion_action *action, **last;
  1647 
  1648   action = xmalloc (sizeof *action);
  1649   action->operation = TEXTCONV_REQUEST_POINT_UPDATE;
  1650   action->data = Qnil;
  1651   action->next = NULL;
  1652   action->counter = counter;
  1653   for (last = &f->conversion.actions; *last; last = &(*last)->next)
  1654     ;;
  1655   *last = action;
  1656   input_pending = true;
  1657 }
  1658 
  1659 /* Request that text conversion on frame F pause until the keyboard
  1660    buffer becomes empty.
  1661 
  1662    Use this function to ensure that edits associated with a keyboard
  1663    event complete before the text conversion edits after the barrier
  1664    take place.  */
  1665 
  1666 void
  1667 textconv_barrier (struct frame *f, unsigned long counter)
  1668 {
  1669   struct text_conversion_action *action, **last;
  1670 
  1671   action = xmalloc (sizeof *action);
  1672   action->operation = TEXTCONV_BARRIER;
  1673   action->data = Qnil;
  1674   action->next = NULL;
  1675   action->counter = counter;
  1676   for (last = &f->conversion.actions; *last; last = &(*last)->next)
  1677     ;;
  1678   *last = action;
  1679   input_pending = true;
  1680 }
  1681 
  1682 /* Return N characters of text around point in frame F's old selected
  1683    window.
  1684 
  1685    If N is -1, return the text between point and mark instead, given
  1686    that the mark is active.
  1687 
  1688    Set *START_RETURN to the position of the first character returned,
  1689    *START_OFFSET to the offset of the lesser of mark and point within
  1690    that text, *END_OFFSET to the greater of mark and point within that
  1691    text, and *LENGTH to the actual number of characters returned,
  1692    *BYTES to the actual number of bytes returned, and *MARK_ACTIVE to
  1693    whether or not the mark is active.
  1694 
  1695    Value is NULL upon failure, and a malloced string upon success.  */
  1696 
  1697 char *
  1698 get_extracted_text (struct frame *f, ptrdiff_t n,
  1699                     ptrdiff_t *start_return,
  1700                     ptrdiff_t *start_offset,
  1701                     ptrdiff_t *end_offset, ptrdiff_t *length,
  1702                     ptrdiff_t *bytes, bool *mark_active)
  1703 {
  1704   specpdl_ref count;
  1705   ptrdiff_t start, end, start_byte, end_byte, mark;
  1706   char *buffer;
  1707 
  1708   if (!WINDOW_LIVE_P (f->old_selected_window))
  1709     return NULL;
  1710 
  1711   /* Save the excursion, as there will be extensive changes to the
  1712      selected window.  */
  1713   count = SPECPDL_INDEX ();
  1714   record_unwind_protect_excursion ();
  1715 
  1716   /* Inhibit quitting.  */
  1717   specbind (Qinhibit_quit, Qt);
  1718 
  1719   /* Temporarily switch to F's selected window at the time of the last
  1720      redisplay.  */
  1721   select_window (f->old_selected_window, Qt);
  1722   buffer = NULL;
  1723 
  1724   /* Figure out the bounds of the text to return.  */
  1725   if (n != -1)
  1726     {
  1727       /* Make sure n is at least 4, leaving two characters around
  1728          PT.  */
  1729       n = max (4, n);
  1730 
  1731       start = PT - n / 2;
  1732       end = PT + n - n / 2;
  1733     }
  1734   else
  1735     {
  1736       if (!NILP (BVAR (current_buffer, mark_active))
  1737           && XMARKER (BVAR (current_buffer, mark))->buffer)
  1738         {
  1739           start = marker_position (BVAR (current_buffer, mark));
  1740           end = PT;
  1741 
  1742           /* Sort start and end.  start_byte is used to hold a
  1743              temporary value.  */
  1744 
  1745           if (start > end)
  1746             {
  1747               start_byte = end;
  1748               end = start;
  1749               start = start_byte;
  1750             }
  1751         }
  1752       else
  1753         goto finish;
  1754     }
  1755 
  1756   start = max (start, BEGV);
  1757   end = min (end, ZV);
  1758 
  1759   /* Detect overflow.  */
  1760 
  1761   if (!(start <= PT && PT <= end))
  1762     goto finish;
  1763 
  1764   /* Convert the character positions to byte positions.  */
  1765   start_byte = CHAR_TO_BYTE (start);
  1766   end_byte = CHAR_TO_BYTE (end);
  1767 
  1768   /* Extract the text from the buffer.  */
  1769   buffer = xmalloc (end_byte - start_byte);
  1770   copy_buffer_text (start, start_byte, end, end_byte, buffer);
  1771 
  1772   /* Get the mark.  If it's not active, use PT.  */
  1773 
  1774   mark = get_mark ();
  1775   *mark_active = true;
  1776 
  1777   if (mark == -1)
  1778     {
  1779       mark = PT;
  1780       *mark_active = false;
  1781     }
  1782 
  1783   /* Return the offsets.  */
  1784   *start_return = start;
  1785   *start_offset = min (mark - start, PT - start);
  1786   *end_offset = max (mark - start, PT - start);
  1787   *length = end - start;
  1788   *bytes = end_byte - start_byte;
  1789 
  1790   TEXTCONV_DEBUG ("get_extracted_text: PT, mark, start: %td, %td, %td",
  1791                   PT, mark, start);
  1792 
  1793  finish:
  1794   unbind_to (count, Qnil);
  1795   return buffer;
  1796 }
  1797 
  1798 /* Return the text between the positions pt - LEFT and pt + RIGHT,
  1799    where pt is the position of point in frame F's selected window.  If
  1800    the mark is active, return the range of text relative to the bounds
  1801    of the region instead.
  1802 
  1803    Set *LENGTH to the number of characters returned, *BYTES to the
  1804    number of bytes returned, *OFFSET to the character position of the
  1805    returned text, and *START_RETURN and *END_RETURN to the mark and
  1806    point relative to that position.  */
  1807 
  1808 char *
  1809 get_surrounding_text (struct frame *f, ptrdiff_t left,
  1810                       ptrdiff_t right, ptrdiff_t *length,
  1811                       ptrdiff_t *bytes, ptrdiff_t *offset,
  1812                       ptrdiff_t *start_return,
  1813                       ptrdiff_t *end_return)
  1814 {
  1815   specpdl_ref count;
  1816   ptrdiff_t start, end, start_byte, end_byte, mark, temp;
  1817   char *buffer;
  1818 
  1819   if (!WINDOW_LIVE_P (f->old_selected_window))
  1820     return NULL;
  1821 
  1822   /* Save the excursion, as there will be extensive changes to the
  1823      selected window.  */
  1824   count = SPECPDL_INDEX ();
  1825   record_unwind_protect_excursion ();
  1826 
  1827   /* Inhibit quitting.  */
  1828   specbind (Qinhibit_quit, Qt);
  1829 
  1830   /* Temporarily switch to F's selected window at the time of the last
  1831      redisplay.  */
  1832   select_window (f->old_selected_window, Qt);
  1833   buffer = NULL;
  1834 
  1835   /* Figure out the bounds of the text to return.  */
  1836 
  1837   /* First, obtain start and end.  */
  1838   end = get_mark ();
  1839   start = PT;
  1840 
  1841   /* If the mark is not active, make it start and end.  */
  1842 
  1843   if (end == -1)
  1844     end = start;
  1845 
  1846   /* Now sort start and end.  */
  1847 
  1848   if (end < start)
  1849     {
  1850       temp = start;
  1851       start = end;
  1852       end = temp;
  1853     }
  1854 
  1855   /* And subtract left and right.  */
  1856 
  1857   if (INT_SUBTRACT_WRAPV (start, left, &start)
  1858       || INT_ADD_WRAPV (end, right, &end))
  1859     goto finish;
  1860 
  1861   start = max (start, BEGV);
  1862   end = min (end, ZV);
  1863 
  1864   /* Detect overflow.  */
  1865 
  1866   if (!(start <= PT && PT <= end))
  1867     goto finish;
  1868 
  1869   /* Convert the character positions to byte positions.  */
  1870   start_byte = CHAR_TO_BYTE (start);
  1871   end_byte = CHAR_TO_BYTE (end);
  1872 
  1873   /* Extract the text from the buffer.  */
  1874   buffer = xmalloc (end_byte - start_byte);
  1875   copy_buffer_text (start, start_byte, end, end_byte, buffer);
  1876 
  1877   /* Get the mark.  If it's not active, use PT.  */
  1878 
  1879   mark = get_mark ();
  1880 
  1881   if (mark == -1)
  1882     mark = PT;
  1883 
  1884   /* Return the offsets.  Unlike `get_extracted_text', this need not
  1885      sort mark and point.  */
  1886 
  1887   *offset = start;
  1888   *start_return = mark - start;
  1889   *end_return = PT - start;
  1890   *length = end - start;
  1891   *bytes = end_byte - start_byte;
  1892 
  1893  finish:
  1894   unbind_to (count, Qnil);
  1895   return buffer;
  1896 }
  1897 
  1898 /* Return whether or not text conversion is temporarily disabled.
  1899    `reset' should always call this to determine whether or not to
  1900    disable the input method.  */
  1901 
  1902 bool
  1903 conversion_disabled_p (void)
  1904 {
  1905   return suppress_conversion_count > 0;
  1906 }
  1907 
  1908 
  1909 
  1910 /* Window system interface.  These are called from the rest of
  1911    Emacs.  */
  1912 
  1913 /* Notice that frame F's selected window has been set from redisplay.
  1914    Reset F's input method state.  */
  1915 
  1916 void
  1917 report_selected_window_change (struct frame *f)
  1918 {
  1919   struct window *w;
  1920 
  1921   reset_frame_state (f);
  1922 
  1923   if (!text_interface)
  1924     return;
  1925 
  1926   /* When called from window.c, F's selected window has already been
  1927      redisplayed, but w->last_point has not yet been updated.  Update
  1928      it here to avoid race conditions when the IM asks for the initial
  1929      selection position immediately after.  */
  1930 
  1931   if (WINDOWP (f->selected_window))
  1932     {
  1933       w = XWINDOW (f->selected_window);
  1934       w->ephemeral_last_point = window_point (w);
  1935     }
  1936 
  1937   text_interface->reset (f);
  1938 }
  1939 
  1940 /* Notice that point in frame F's selected window's current buffer has
  1941    changed.
  1942 
  1943    F is the frame whose selected window was changed, WINDOW is the
  1944    window in question, and BUFFER is that window's buffer.
  1945 
  1946    Tell the text conversion interface about the change; it will likely
  1947    pass the information on to the system input method.  */
  1948 
  1949 void
  1950 report_point_change (struct frame *f, struct window *window,
  1951                      struct buffer *buffer)
  1952 {
  1953   if (!text_interface || !text_interface->point_changed)
  1954     return;
  1955 
  1956   if (f->conversion.batch_edit_count > 0)
  1957     f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
  1958   else
  1959     text_interface->point_changed (f, window, buffer);
  1960 }
  1961 
  1962 /* Temporarily disable text conversion.  Must be paired with a
  1963    corresponding call to resume_text_conversion.  */
  1964 
  1965 void
  1966 disable_text_conversion (void)
  1967 {
  1968   Lisp_Object tail, frame;
  1969   struct frame *f;
  1970 
  1971   suppress_conversion_count++;
  1972 
  1973   if (!text_interface || suppress_conversion_count > 1)
  1974     return;
  1975 
  1976   /* Loop through and reset the input method on each window system
  1977      frame.  It should call conversion_disabled_p and then DTRT.  */
  1978 
  1979   FOR_EACH_FRAME (tail, frame)
  1980     {
  1981       f = XFRAME (frame);
  1982       reset_frame_state (f);
  1983 
  1984       if (FRAME_WINDOW_P (f) && FRAME_VISIBLE_P (f))
  1985         text_interface->reset (f);
  1986     }
  1987 }
  1988 
  1989 /* Undo the effect of the last call to `disable_text_conversion'.  */
  1990 
  1991 void
  1992 resume_text_conversion (void)
  1993 {
  1994   Lisp_Object tail, frame;
  1995   struct frame *f;
  1996 
  1997   suppress_conversion_count--;
  1998   eassert (suppress_conversion_count >= 0);
  1999 
  2000   if (!text_interface || suppress_conversion_count)
  2001     return;
  2002 
  2003   /* Loop through and reset the input method on each window system
  2004      frame.  It should call conversion_disabled_p and then DTRT.  */
  2005 
  2006   FOR_EACH_FRAME (tail, frame)
  2007     {
  2008       f = XFRAME (frame);
  2009       reset_frame_state (f);
  2010 
  2011       if (FRAME_WINDOW_P (f) && FRAME_VISIBLE_P (f))
  2012         text_interface->reset (f);
  2013     }
  2014 }
  2015 
  2016 /* Register INTERFACE as the text conversion interface.  */
  2017 
  2018 void
  2019 register_textconv_interface (struct textconv_interface *interface)
  2020 {
  2021   text_interface = interface;
  2022 }
  2023 
  2024 
  2025 
  2026 /* List of buffers whose text conversion state will be reset after a
  2027    key sequence is read.  */
  2028 static Lisp_Object postponed_buffers;
  2029 
  2030 /* Reset the text conversion style of each frame whose selected buffer
  2031    is contained inside `postponed_buffers'.  Set `postponed_buffers'
  2032    to nil.  */
  2033 
  2034 void
  2035 check_postponed_buffers (void)
  2036 {
  2037   Lisp_Object buffer, tail, frame;
  2038   struct buffer *b;
  2039   struct frame *f;
  2040 
  2041   buffer = postponed_buffers;
  2042   postponed_buffers = Qnil;
  2043 
  2044   if (!text_interface->reset)
  2045     return;
  2046 
  2047   FOR_EACH_TAIL (buffer)
  2048     {
  2049       b = XBUFFER (XCAR (buffer));
  2050 
  2051       /* Continue if this is a dead buffer.  */
  2052 
  2053       if (!BUFFER_LIVE_P (b))
  2054         continue;
  2055 
  2056       /* If no windows are displaying B anymore, continue.  */
  2057 
  2058       if (!buffer_window_count (b))
  2059         continue;
  2060 
  2061       /* Look for frames which have B selected.  */
  2062 
  2063       FOR_EACH_FRAME (tail, frame)
  2064         {
  2065           f = XFRAME (frame);
  2066 
  2067           if (WINDOW_LIVE_P (f->old_selected_window)
  2068               && FRAME_WINDOW_P (f)
  2069               /* N.B. that the same frame can't be reset twice as long
  2070                  as the list of buffers remains unique.  */
  2071               && EQ (XWINDOW (f->old_selected_window)->contents,
  2072                      XCAR (buffer)))
  2073             {
  2074               block_input ();
  2075               reset_frame_state (f);
  2076               text_interface->reset (f);
  2077               unblock_input ();
  2078             }
  2079         }
  2080     }
  2081 }
  2082 
  2083 /* Lisp interface.  */
  2084 
  2085 DEFUN ("set-text-conversion-style", Fset_text_conversion_style,
  2086        Sset_text_conversion_style, 1, 2, 0,
  2087        doc: /* Set the current buffer's text conversion style to VALUE.
  2088 
  2089 After setting `text-conversion-style', force input methods
  2090 editing in a selected window displaying this buffer on any frame
  2091 to stop themselves.
  2092 
  2093 This can lead to a significant amount of time being taken by the input
  2094 method resetting itself, so you should not use this function lightly;
  2095 instead, set `text-conversion-style' before your buffer is displayed,
  2096 and let redisplay manage the input method appropriately.
  2097 
  2098 If a key sequence is currently being read (either through the command
  2099 loop or by a call to `read-key-sequence') and AFTER-KEY-SEQUENCE is
  2100 non-nil, don't perform changes to the input method until the key
  2101 sequence is read.  This is useful within a function bound to
  2102 `input-decode-map' or `local-function-key-map', as it prevents the
  2103 input method from being redundantly enabled according to VALUE if the
  2104 replacement key sequence returned starts a new key sequence and makes
  2105 `read-key-sequence' disable text conversion again.  */)
  2106   (Lisp_Object value, Lisp_Object after_key_sequence)
  2107 {
  2108   Lisp_Object tail, frame;
  2109   struct frame *f;
  2110   Lisp_Object buffer;
  2111 
  2112   bset_text_conversion_style (current_buffer, value);
  2113 
  2114   if (!text_interface)
  2115     return Qnil;
  2116 
  2117   /* If there are any selected windows displaying this buffer, reset
  2118      text conversion on their associated frames.  */
  2119 
  2120   if (buffer_window_count (current_buffer))
  2121     {
  2122       buffer = Fcurrent_buffer ();
  2123 
  2124       /* Postpone changes to the actual text conversion state if
  2125          AFTER_KEY_SEQUENCE is non-nil and a key sequence is being
  2126          read.  */
  2127 
  2128       if (reading_key_sequence && !NILP (after_key_sequence))
  2129         {
  2130           if (NILP (Fmemq (buffer, postponed_buffers)))
  2131             /* `check_postponed_buffers' will hopefully be called soon
  2132                enough to avoid postponed_buffers growing
  2133                indefinitely.  */
  2134             postponed_buffers = Fcons (buffer, postponed_buffers);
  2135           return Qnil;
  2136         }
  2137 
  2138       FOR_EACH_FRAME (tail, frame)
  2139         {
  2140           f = XFRAME (frame);
  2141 
  2142           if (WINDOW_LIVE_P (f->old_selected_window)
  2143               && FRAME_WINDOW_P (f)
  2144               && (EQ (XWINDOW (f->old_selected_window)->contents,
  2145                       buffer)
  2146                   /* Always reset the text conversion style of the
  2147                      selected frame.  */
  2148                   || (f == SELECTED_FRAME ())))
  2149             {
  2150               block_input ();
  2151               reset_frame_state (f);
  2152               text_interface->reset (f);
  2153               unblock_input ();
  2154             }
  2155         }
  2156     }
  2157 
  2158   return Qnil;
  2159 }
  2160 
  2161 
  2162 
  2163 void
  2164 syms_of_textconv (void)
  2165 {
  2166   DEFSYM (Qaction, "action");
  2167   DEFSYM (Qtext_conversion, "text-conversion");
  2168   DEFSYM (Qpush_mark, "push-mark");
  2169   DEFSYM (Qunderline, "underline");
  2170   DEFSYM (Qoverriding_text_conversion_style,
  2171           "overriding-text-conversion-style");
  2172 
  2173   DEFVAR_LISP ("text-conversion-edits", Vtext_conversion_edits,
  2174     doc: /* List of buffers that were last edited as result of text conversion.
  2175 
  2176 This list can be used while handling a `text-conversion' event to
  2177 determine which changes have taken place.
  2178 
  2179 Each element of the list describes a single edit in a buffer, and
  2180 is of the form:
  2181 
  2182     (BUFFER BEG END EPHEMERAL)
  2183 
  2184 If an insertion or an edit to the buffer text is described, then BEG
  2185 and END are markers which denote the bounds of the text that was
  2186 changed or inserted.  If a deletion is described, then BEG and END are
  2187 the same object.
  2188 
  2189 If EPHEMERAL is t, then the input method is preparing to make further
  2190 edits to the text, so any actions that would otherwise be taken, such
  2191 as indenting or automatically filling text, should not take place.
  2192 
  2193 Otherwise, it is either a string containing text that was inserted,
  2194 text deleted before point, or nil if text was deleted after point.
  2195 
  2196 The list contents are ordered in the reverse order of editing, i.e.
  2197 the latest edit first, so you must iterate through the list in reverse.  */);
  2198   Vtext_conversion_edits = Qnil;
  2199 
  2200   DEFVAR_LISP ("overriding-text-conversion-style",
  2201                Voverriding_text_conversion_style,
  2202     doc: /* Non-buffer local version of `text-conversion-style'.
  2203 
  2204 If this variable is the symbol `lambda', it means to consult the
  2205 buffer-local value of `text-conversion-style' to determine whether or
  2206 not to activate the input method.  Otherwise, the value is used in
  2207 preference to any buffer-local value of `text-conversion-style'.  */);
  2208   Voverriding_text_conversion_style = Qlambda;
  2209 
  2210   DEFVAR_LISP ("text-conversion-face", Vtext_conversion_face,
  2211     doc: /* Face in which to display temporary edits by an input method.
  2212 The value nil means to display no indication of a temporary edit.  */);
  2213   Vtext_conversion_face = Qunderline;
  2214 
  2215   defsubr (&Sset_text_conversion_style);
  2216 
  2217   postponed_buffers = Qnil;
  2218   staticpro (&postponed_buffers);
  2219 }

/* [<][>][^][v][top][bottom][index][help] */