mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-01-25 22:50:37 -08:00
C23 has added ckd_add etc. macros with functionality equivalent to the older Gnulib INT_ADD_WRAPV macros, so switch to the more-standard names. * admin/merge-gnulib (GNULIB_MODULES): Add stdckdint. This merely makes the dependency explicit, as we were already using this Gnulib module indirectly. * lib-src/etags.c, src/lisp.h: Include stdckdint.h. * lib-src/etags.c (xnmalloc, xnrealloc): * src/alloc.c (xnmalloc, xnrealloc, xpalloc, Fmake_string) (mark_memory): * src/bignum.c (emacs_mpz_pow_ui): * src/buffer.c (record_overlay_string, overlay_strings): * src/bytecode.c (exec_byte_code): * src/casefiddle.c (do_casify_multibyte_string): * src/ccl.c (ccl_driver, Fccl_execute_on_string): * src/character.c (char_width, c_string_width) (lisp_string_width, count_size_as_multibyte) (string_escape_byte8): * src/cmds.c (internal_self_insert): * src/coding.c (coding_alloc_by_realloc, produce_chars): * src/data.c (arith_driver): * src/dispnew.c (realloc_glyph_pool, init_display_interactive): * src/doprnt.c (parse_format_integer): * src/editfns.c (Freplace_buffer_contents, str2num) (styled_format): * src/emacs-module.c (module_global_reference_p) (module_make_global_ref, module_funcall): * src/eval.c (max_ensure_room): * src/fileio.c (blocks_to_bytes): * src/fns.c (Ffillarray): * src/font.c (font_intern_prop): * src/frame.c (check_frame_pixels): * src/gnutls.c (gnutls_hex_string, gnutls_symmetric_aead): * src/gtkutil.c (get_utf8_string): * src/haikuterm.c (haiku_term_init): * src/image.c (xbm_scan, image_to_emacs_colors) (image_detect_edges, png_load_body): * src/keyboard.c (Frecursion_depth): * src/keymap.c (Flookup_key, Fkey_description): * src/lisp.h (modiff_incr, SAFE_ALLOCA_LISP_EXTRA): * src/lread.c (read_bool_vector): * src/pgtkterm.c (pgtk_term_init): * src/regex-emacs.c (regex_compile): * src/term.c (encode_terminal_code): * src/termcap.c (tputs): * src/textconv.c (textconv_query): * src/timefns.c (timespec_ticks, lisp_time_hz_ticks) (Fdecode_time, check_tm_member): * src/tparam.c (tparam1): * src/w32term.c (w32_initialize_display_info): * src/xdisp.c (fill_column_indicator_column, decode_mode_spec): * src/xselect.c (selection_data_size, x_property_data_to_lisp): * src/xsmfns.c (smc_save_yourself_CB): * src/xterm.c (xm_setup_dnd_targets, x_sync_get_monotonic_time) (x_sync_current_monotonic_time, x_sync_note_frame_times) (x_display_set_last_user_time, x_term_init): Prefer the C23 stdckdint macros to their Gnulib intprops.h counterparts, since C23 is standard.
313 lines
7.6 KiB
C
313 lines
7.6 KiB
C
/* String conversion support for graphics terminals.
|
||
|
||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||
|
||
This file is part of GNU Emacs.
|
||
|
||
GNU Emacs is free software: you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation, either version 3 of the License, or (at
|
||
your option) any later version.
|
||
|
||
GNU Emacs is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||
|
||
/* String conversion support.
|
||
|
||
Many input methods require access to text surrounding the cursor.
|
||
They may then request that the text editor remove or substitute
|
||
that text for something else, for example when providing the
|
||
ability to ``undo'' or ``edit'' previously composed text. This is
|
||
most commonly seen in input methods for CJK laguages for X Windows,
|
||
and is extensively used throughout Android by input methods for all
|
||
kinds of scripts. */
|
||
|
||
#include <config.h>
|
||
|
||
#include "textconv.h"
|
||
#include "buffer.h"
|
||
#include "syntax.h"
|
||
|
||
|
||
|
||
/* The window system's text conversion interface.
|
||
NULL when the window system has not set up text conversion.
|
||
|
||
This interface will later be heavily extended on the
|
||
feature/android branch to deal with Android's much less
|
||
straightforward text conversion protocols. */
|
||
|
||
static struct textconv_interface *text_interface;
|
||
|
||
|
||
|
||
/* Copy the portion of the current buffer described by BEG, BEG_BYTE,
|
||
END, END_BYTE to the buffer BUFFER, which is END_BYTE - BEG_BYTEs
|
||
long. */
|
||
|
||
static void
|
||
copy_buffer (ptrdiff_t beg, ptrdiff_t beg_byte,
|
||
ptrdiff_t end, ptrdiff_t end_byte,
|
||
char *buffer)
|
||
{
|
||
ptrdiff_t beg0, end0, beg1, end1, size;
|
||
|
||
if (beg_byte < GPT_BYTE && GPT_BYTE < end_byte)
|
||
{
|
||
/* Two regions, before and after the gap. */
|
||
beg0 = beg_byte;
|
||
end0 = GPT_BYTE;
|
||
beg1 = GPT_BYTE + GAP_SIZE - BEG_BYTE;
|
||
end1 = end_byte + GAP_SIZE - BEG_BYTE;
|
||
}
|
||
else
|
||
{
|
||
/* The only region. */
|
||
beg0 = beg_byte;
|
||
end0 = end_byte;
|
||
beg1 = -1;
|
||
end1 = -1;
|
||
}
|
||
|
||
size = end0 - beg0;
|
||
memcpy (buffer, BYTE_POS_ADDR (beg0), size);
|
||
if (beg1 != -1)
|
||
memcpy (buffer, BEG_ADDR + beg1, end1 - beg1);
|
||
}
|
||
|
||
|
||
|
||
/* Conversion query. */
|
||
|
||
/* Perform the text conversion operation specified in QUERY and return
|
||
the results.
|
||
|
||
Find the text between QUERY->position from point on F's selected
|
||
window and QUERY->factor times QUERY->direction from that
|
||
position. Return it in QUERY->text.
|
||
|
||
Then, either delete that text from the buffer if QUERY->operation
|
||
is TEXTCONV_SUBSTITUTION, or return 0.
|
||
|
||
Value is 0 if QUERY->operation was not TEXTCONV_SUBSTITUTION
|
||
or if deleting the text was successful, and 1 otherwise. */
|
||
|
||
int
|
||
textconv_query (struct frame *f, struct textconv_callback_struct *query)
|
||
{
|
||
specpdl_ref count;
|
||
ptrdiff_t pos, pos_byte, end, end_byte;
|
||
ptrdiff_t temp, temp1;
|
||
char *buffer;
|
||
|
||
/* Save the excursion, as there will be extensive changes to the
|
||
selected window. */
|
||
count = SPECPDL_INDEX ();
|
||
record_unwind_protect_excursion ();
|
||
|
||
/* Inhibit quitting. */
|
||
specbind (Qinhibit_quit, Qt);
|
||
|
||
/* Temporarily switch to F's selected window. */
|
||
Fselect_window (f->selected_window, Qt);
|
||
|
||
/* Now find the appropriate text bounds for QUERY. First, move
|
||
point QUERY->position steps forward or backwards. */
|
||
|
||
pos = PT;
|
||
|
||
/* If pos is outside the accessible part of the buffer or if it
|
||
overflows, move back to point or to the extremes of the
|
||
accessible region. */
|
||
|
||
if (ckd_add (&pos, pos, query->position))
|
||
pos = PT;
|
||
|
||
if (pos < BEGV)
|
||
pos = BEGV;
|
||
|
||
if (pos > ZV)
|
||
pos = ZV;
|
||
|
||
/* Move to pos. */
|
||
set_point (pos);
|
||
pos = PT;
|
||
pos_byte = PT_BYTE;
|
||
|
||
/* Now scan forward or backwards according to what is in QUERY. */
|
||
|
||
switch (query->direction)
|
||
{
|
||
case TEXTCONV_FORWARD_CHAR:
|
||
/* Move forward by query->factor characters. */
|
||
if (ckd_add (&end, pos, query->factor) || end > ZV)
|
||
end = ZV;
|
||
|
||
end_byte = CHAR_TO_BYTE (end);
|
||
break;
|
||
|
||
case TEXTCONV_BACKWARD_CHAR:
|
||
/* Move backward by query->factor characters. */
|
||
if (ckd_sub (&end, pos, query->factor) || end < BEGV)
|
||
end = BEGV;
|
||
|
||
end_byte = CHAR_TO_BYTE (end);
|
||
break;
|
||
|
||
case TEXTCONV_FORWARD_WORD:
|
||
/* Move forward by query->factor word. */
|
||
end = scan_words (pos, (EMACS_INT) query->factor);
|
||
|
||
if (!end)
|
||
{
|
||
end = ZV;
|
||
end_byte = ZV_BYTE;
|
||
}
|
||
else
|
||
end_byte = CHAR_TO_BYTE (end);
|
||
|
||
break;
|
||
|
||
case TEXTCONV_BACKWARD_WORD:
|
||
/* Move backwards by query->factor word. */
|
||
end = scan_words (pos, 0 - (EMACS_INT) query->factor);
|
||
|
||
if (!end)
|
||
{
|
||
end = BEGV;
|
||
end_byte = BEGV_BYTE;
|
||
}
|
||
else
|
||
end_byte = CHAR_TO_BYTE (end);
|
||
|
||
break;
|
||
|
||
case TEXTCONV_CARET_UP:
|
||
/* Move upwards one visual line, keeping the column intact. */
|
||
Fvertical_motion (Fcons (Fcurrent_column (), make_fixnum (-1)),
|
||
Qnil, Qnil);
|
||
end = PT;
|
||
end_byte = PT_BYTE;
|
||
break;
|
||
|
||
case TEXTCONV_CARET_DOWN:
|
||
/* Move downwards one visual line, keeping the column
|
||
intact. */
|
||
Fvertical_motion (Fcons (Fcurrent_column (), make_fixnum (1)),
|
||
Qnil, Qnil);
|
||
end = PT;
|
||
end_byte = PT_BYTE;
|
||
break;
|
||
|
||
case TEXTCONV_NEXT_LINE:
|
||
/* Move one line forward. */
|
||
scan_newline (pos, pos_byte, ZV, ZV_BYTE,
|
||
query->factor, false);
|
||
end = PT;
|
||
end_byte = PT_BYTE;
|
||
break;
|
||
|
||
case TEXTCONV_PREVIOUS_LINE:
|
||
/* Move one line backwards. */
|
||
scan_newline (pos, pos_byte, BEGV, BEGV_BYTE,
|
||
0 - (EMACS_INT) query->factor, false);
|
||
end = PT;
|
||
end_byte = PT_BYTE;
|
||
break;
|
||
|
||
case TEXTCONV_LINE_START:
|
||
/* Move to the beginning of the line. */
|
||
Fbeginning_of_line (Qnil);
|
||
end = PT;
|
||
end_byte = PT_BYTE;
|
||
break;
|
||
|
||
case TEXTCONV_LINE_END:
|
||
/* Move to the end of the line. */
|
||
Fend_of_line (Qnil);
|
||
end = PT;
|
||
end_byte = PT_BYTE;
|
||
break;
|
||
|
||
case TEXTCONV_ABSOLUTE_POSITION:
|
||
/* How to implement this is unclear. */
|
||
SET_PT (query->factor);
|
||
end = PT;
|
||
end_byte = PT_BYTE;
|
||
break;
|
||
|
||
default:
|
||
unbind_to (count, Qnil);
|
||
return 1;
|
||
}
|
||
|
||
/* Sort end and pos. */
|
||
|
||
if (end < pos)
|
||
{
|
||
eassert (end_byte < pos_byte);
|
||
temp = pos_byte;
|
||
temp1 = pos;
|
||
pos_byte = end_byte;
|
||
pos = end;
|
||
end = temp1;
|
||
end_byte = temp;
|
||
}
|
||
|
||
/* Return the string first. */
|
||
buffer = xmalloc (end_byte - pos_byte);
|
||
copy_buffer (pos, pos_byte, end, end_byte, buffer);
|
||
query->text.text = buffer;
|
||
query->text.length = end - pos;
|
||
query->text.bytes = end_byte - pos_byte;
|
||
|
||
/* Next, perform any operation specified. */
|
||
|
||
switch (query->operation)
|
||
{
|
||
case TEXTCONV_SUBSTITUTION:
|
||
if (safe_del_range (pos, end))
|
||
{
|
||
/* Undo any changes to the excursion. */
|
||
unbind_to (count, Qnil);
|
||
return 1;
|
||
}
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
/* Undo any changes to the excursion. */
|
||
unbind_to (count, Qnil);
|
||
return 0;
|
||
}
|
||
|
||
|
||
|
||
/* Window system interface. These are called from the rest of
|
||
Emacs. */
|
||
|
||
/* Notice that F's selected window has been set from redisplay.
|
||
Reset F's input method state. */
|
||
|
||
void
|
||
report_selected_window_change (struct frame *f)
|
||
{
|
||
if (!text_interface)
|
||
return;
|
||
|
||
text_interface->reset (f);
|
||
}
|
||
|
||
/* Register INTERFACE as the text conversion interface. */
|
||
|
||
void
|
||
register_texconv_interface (struct textconv_interface *interface)
|
||
{
|
||
text_interface = interface;
|
||
}
|