From 0a9c8855b0ce9618219ef70bb489107ce7194ba1 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Wed, 16 Feb 2022 10:10:13 +0800 Subject: [PATCH] Don't decode text within XIM callbacks or handle_one_xevent * src/keyboard.c (kbd_buffer_get_event_1): (kbd_buffer_get_event_2): New functions. (kbd_buffer_get_event): Accept a new meaning of MULTIBYTE_CHAR_KEYSTROKE_EVENT where .arg can be a unibyte string to be decoded in the locale coding system. * src/termhooks.h (enum event_kind): Document new meaning of .arg in a multibyte keystroke event. * src/xfns.c (struct x_xim_text_conversion_data): New struct. (x_xim_text_to_utf8_unix_1): (x_xim_text_to_utf8_unix_2): New functions. (x_xim_text_to_utf8_unix): Handle decode failure correctly. (xic_preedit_draw_callback): Abort IM context if text could not be decoded correctly. * src/xterm.c (handle_one_xevent): Utilize new meaning of MULTIBYTE_CHAR_KEYSTROKE_EVENT. --- src/keyboard.c | 67 +++++++++++++++++++++++-- src/termhooks.h | 15 ++++-- src/xfns.c | 85 ++++++++++++++++++++++++-------- src/xterm.c | 126 ++++-------------------------------------------- 4 files changed, 149 insertions(+), 144 deletions(-) diff --git a/src/keyboard.c b/src/keyboard.c index be9fb665d76..800632aa25d 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -3835,6 +3835,18 @@ clear_event (struct input_event *event) event->kind = NO_EVENT; } +static Lisp_Object +kbd_buffer_get_event_1 (Lisp_Object arg) +{ + return code_convert_string (arg, Vlocale_coding_system, + Qnil, 0, false, 0); +} + +static Lisp_Object +kbd_buffer_get_event_2 (Lisp_Object val) +{ + return Qnil; +} /* Read one event from the event buffer, waiting if necessary. The value is a Lisp object representing the event. @@ -3847,7 +3859,7 @@ kbd_buffer_get_event (KBOARD **kbp, bool *used_mouse_menu, struct timespec *end_time) { - Lisp_Object obj; + Lisp_Object obj, str; #ifdef subprocesses if (kbd_on_hold_p () && kbd_buffer_nr_stored () < KBD_BUFFER_SIZE / 4) @@ -4120,6 +4132,47 @@ kbd_buffer_get_event (KBOARD **kbp, } } + if (event->kind == MULTIBYTE_CHAR_KEYSTROKE_EVENT + /* This string has to be decoded. */ + && STRINGP (event->ie.arg)) + { + str = internal_condition_case_1 (kbd_buffer_get_event_1, + event->ie.arg, Qt, + kbd_buffer_get_event_2); + + /* Decoding the string failed, so use the original, + where at least ASCII text will work. */ + if (NILP (str)) + str = event->ie.arg; + + if (!SCHARS (str)) + { + kbd_fetch_ptr = next_kbd_event (event); + obj = Qnil; + break; + } + + /* car is the index of the next character in the + string that will be sent and cdr is the string + itself. */ + event->ie.arg = Fcons (make_fixnum (0), str); + } + + if (event->kind == MULTIBYTE_CHAR_KEYSTROKE_EVENT + && CONSP (event->ie.arg)) + { + eassert (FIXNUMP (XCAR (event->ie.arg))); + eassert (STRINGP (XCDR (event->ie.arg))); + eassert (XFIXNUM (XCAR (event->ie.arg)) + < SCHARS (XCDR (event->ie.arg))); + + event->ie.code = XFIXNUM (Faref (XCDR (event->ie.arg), + XCAR (event->ie.arg))); + + XSETCAR (event->ie.arg, + make_fixnum (XFIXNUM (XCAR (event->ie.arg)) + 1)); + } + obj = make_lispy_event (&event->ie); #ifdef HAVE_EXT_MENU_BAR @@ -4142,9 +4195,15 @@ kbd_buffer_get_event (KBOARD **kbp, *used_mouse_menu = true; #endif - /* Wipe out this event, to catch bugs. */ - clear_event (&event->ie); - kbd_fetch_ptr = next_kbd_event (event); + if (event->kind != MULTIBYTE_CHAR_KEYSTROKE_EVENT + || !CONSP (event->ie.arg) + || (XFIXNUM (XCAR (event->ie.arg)) + >= SCHARS (XCDR (event->ie.arg)))) + { + /* Wipe out this event, to catch bugs. */ + clear_event (&event->ie); + kbd_fetch_ptr = next_kbd_event (event); + } } } } diff --git a/src/termhooks.h b/src/termhooks.h index 518e855eae1..4276d8ac2f2 100644 --- a/src/termhooks.h +++ b/src/termhooks.h @@ -80,10 +80,17 @@ enum event_kind which the key was typed. .timestamp gives a timestamp (in milliseconds) for the keystroke. */ - MULTIBYTE_CHAR_KEYSTROKE_EVENT, /* The multibyte char code is in .code, - perhaps with modifiers applied. - The others are the same as - ASCII_KEYSTROKE_EVENT. */ + MULTIBYTE_CHAR_KEYSTROKE_EVENT, /* The multibyte char code is + in .code, perhaps with + modifiers applied. The + others are the same as + ASCII_KEYSTROKE_EVENT, + except when ARG is a + string, which will be + decoded and the decoded + string's characters will be + used as .code + individually. */ NON_ASCII_KEYSTROKE_EVENT, /* .code is a number identifying the function key. A code N represents a key whose name is diff --git a/src/xfns.c b/src/xfns.c index 0f8d6933b39..f00b7389086 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -3139,14 +3139,64 @@ xic_preedit_done_callback (XIC xic, XPointer client_data, } } +struct x_xim_text_conversion_data +{ + struct coding_system *coding; + char *source; +}; + +static Lisp_Object +x_xim_text_to_utf8_unix_1 (ptrdiff_t nargs, + Lisp_Object *args) +{ + struct x_xim_text_conversion_data *data; + ptrdiff_t nbytes; + + data = xmint_pointer (args[0]); + nbytes = strlen (data->source); + + data->coding->destination = NULL; + + setup_coding_system (Vlocale_coding_system, + data->coding); + data->coding->mode |= (CODING_MODE_LAST_BLOCK + | CODING_MODE_SAFE_ENCODING); + data->coding->source = (const unsigned char *) data->source; + data->coding->dst_bytes = 2048; + data->coding->destination = xmalloc (2048); + decode_coding_object (data->coding, Qnil, 0, 0, + nbytes, nbytes, Qnil); + + return Qnil; +} + +static Lisp_Object +x_xim_text_to_utf8_unix_2 (Lisp_Object val, + ptrdiff_t nargs, + Lisp_Object *args) +{ + struct x_xim_text_conversion_data *data; + + data = xmint_pointer (args[0]); + + if (data->coding->destination) + xfree (data->coding->destination); + + data->coding->destination = NULL; + + return Qnil; +} + /* The string returned is not null-terminated. */ static char * x_xim_text_to_utf8_unix (XIMText *text, ptrdiff_t *length) { unsigned char *wchar_buf; ptrdiff_t wchar_actual_length, i; - ptrdiff_t nbytes; struct coding_system coding; + struct x_xim_text_conversion_data data; + bool was_waiting_for_input_p; + Lisp_Object arg; if (text->encoding_is_wchar) { @@ -3161,17 +3211,16 @@ x_xim_text_to_utf8_unix (XIMText *text, ptrdiff_t *length) return (char *) wchar_buf; } - nbytes = strlen (text->string.multi_byte); - setup_coding_system (Vlocale_coding_system, &coding); - coding.mode |= (CODING_MODE_LAST_BLOCK - | CODING_MODE_SAFE_ENCODING); - coding.source = (const unsigned char *) text->string.multi_byte; - coding.dst_bytes = 2048; - coding.destination = xmalloc (2048); - decode_coding_object (&coding, Qnil, 0, 0, nbytes, nbytes, Qnil); + data.coding = &coding; + data.source = text->string.multi_byte; - /* coding.destination has either been allocated by us, or - reallocated by decode_coding_object. */ + was_waiting_for_input_p = waiting_for_input; + /* Otherwise Fsignal will crash. */ + waiting_for_input = false; + arg = make_mint_ptr (&data); + internal_condition_case_n (x_xim_text_to_utf8_unix_1, 1, &arg, + Qt, x_xim_text_to_utf8_unix_2); + waiting_for_input = was_waiting_for_input_p; *length = coding.produced; return (char *) coding.destination; @@ -3198,16 +3247,14 @@ xic_preedit_draw_callback (XIC xic, XPointer client_data, if (!output->preedit_active) return; - /* If we don't bail out here then GTK can crash - from the resulting signal in `setup_coding_system'. */ - if (NILP (Fcoding_system_p (Vlocale_coding_system))) + if (call_data->text) { - text = NULL; - goto im_abort; - } + text = x_xim_text_to_utf8_unix (call_data->text, &text_length); - if (call_data->text) - text = x_xim_text_to_utf8_unix (call_data->text, &text_length); + if (!text) + /* Decoding the IM text failed. */ + goto im_abort; + } else text = NULL; diff --git a/src/xterm.c b/src/xterm.c index 23317bf6959..568351d2e3d 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -9124,7 +9124,6 @@ handle_one_xevent (struct x_display_info *dpyinfo, int do_help = 0; ptrdiff_t nbytes = 0; struct frame *any, *f = NULL; - struct coding_system coding; Mouse_HLInfo *hlinfo = &dpyinfo->mouse_highlight; /* This holds the state XLookupString needs to implement dead keys and other tricks known as "compose processing". _X Window System_ @@ -9134,8 +9133,6 @@ handle_one_xevent (struct x_display_info *dpyinfo, XEvent configureEvent; XEvent next_event; - USE_SAFE_ALLOCA; - *finish = X_EVENT_NORMAL; EVENT_INIT (inev.ie); @@ -9843,7 +9840,6 @@ handle_one_xevent (struct x_display_info *dpyinfo, unsigned char *copy_bufptr = copy_buffer; int copy_bufsiz = sizeof (copy_buffer); int modifiers; - Lisp_Object coding_system = Qlatin_1; Lisp_Object c; /* `xkey' will be modified, but it's not important to modify `event' itself. */ @@ -9884,7 +9880,6 @@ handle_one_xevent (struct x_display_info *dpyinfo, { Status status_return; - coding_system = Vlocale_coding_system; nbytes = XmbLookupString (FRAME_XIC (f), &xkey, (char *) copy_bufptr, copy_bufsiz, &keysym, @@ -10051,64 +10046,12 @@ handle_one_xevent (struct x_display_info *dpyinfo, { /* Raw bytes, not keysym. */ ptrdiff_t i; - int nchars, len; - - for (i = 0, nchars = 0; i < nbytes; i++) - { - if (ASCII_CHAR_P (copy_bufptr[i])) - nchars++; - STORE_KEYSYM_FOR_DEBUG (copy_bufptr[i]); - } - - if (nchars < nbytes) - { - /* If we don't bail out here then GTK can crash - from the resulting signal in `setup_coding_system'. */ - if (NILP (Fcoding_system_p (coding_system))) - goto done_keysym; - - /* Decode the input data. */ - - /* The input should be decoded with `coding_system' - which depends on which X*LookupString function - we used just above and the locale. */ - setup_coding_system (coding_system, &coding); - coding.src_multibyte = false; - coding.dst_multibyte = true; - /* The input is converted to events, thus we can't - handle composition. Anyway, there's no XIM that - gives us composition information. */ - coding.common_flags &= ~CODING_ANNOTATION_MASK; - - SAFE_NALLOCA (coding.destination, MAX_MULTIBYTE_LENGTH, - nbytes); - coding.dst_bytes = MAX_MULTIBYTE_LENGTH * nbytes; - coding.mode |= CODING_MODE_LAST_BLOCK; - decode_coding_c_string (&coding, copy_bufptr, nbytes, Qnil); - nbytes = coding.produced; - nchars = coding.produced_char; - copy_bufptr = coding.destination; - } - /* Convert the input data to a sequence of - character events. */ - for (i = 0; i < nbytes; i += len) - { - int ch; - if (nchars == nbytes) - ch = copy_bufptr[i], len = 1; - else - ch = string_char_and_length (copy_bufptr + i, &len); - inev.ie.kind = (SINGLE_BYTE_CHAR_P (ch) - ? ASCII_KEYSTROKE_EVENT - : MULTIBYTE_CHAR_KEYSTROKE_EVENT); - inev.ie.code = ch; - kbd_buffer_store_buffered_event (&inev, hold_quit); - } - - count += nchars; + for (i = 0; i < nbytes; i++) + STORE_KEYSYM_FOR_DEBUG (copy_bufptr[i]); - inev.ie.kind = NO_EVENT; /* Already stored above. */ + inev.ie.kind = MULTIBYTE_CHAR_KEYSTROKE_EVENT; + inev.ie.arg = make_unibyte_string ((char *) copy_bufptr, nbytes); if (keysym == NoSymbol) break; @@ -11497,10 +11440,8 @@ handle_one_xevent (struct x_display_info *dpyinfo, KeySym keysym; char copy_buffer[81]; char *copy_bufptr = copy_buffer; - unsigned char *copy_ubufptr; int copy_bufsiz = sizeof (copy_buffer); ptrdiff_t i; - int nchars, len; struct xi_device_t *device; device = xi_device_from_id (dpyinfo, xev->deviceid); @@ -11802,62 +11743,14 @@ handle_one_xevent (struct x_display_info *dpyinfo, goto xi_done_keysym; } - for (i = 0, nchars = 0; i < nbytes; i++) - { - if (ASCII_CHAR_P (copy_bufptr[i])) - nchars++; - STORE_KEYSYM_FOR_DEBUG (copy_bufptr[i]); - } + for (i = 0; i < nbytes; i++) + STORE_KEYSYM_FOR_DEBUG (copy_bufptr[i]); - if (nchars < nbytes) - { - /* If we don't bail out here then GTK can crash - from the resulting signal in `setup_coding_system'. */ - if (NILP (Fcoding_system_p (Vlocale_coding_system))) - goto xi_done_keysym; - - /* Decode the input data. */ - - setup_coding_system (Vlocale_coding_system, &coding); - coding.src_multibyte = false; - coding.dst_multibyte = true; - /* The input is converted to events, thus we can't - handle composition. Anyway, there's no XIM that - gives us composition information. */ - coding.common_flags &= ~CODING_ANNOTATION_MASK; - - SAFE_NALLOCA (coding.destination, MAX_MULTIBYTE_LENGTH, - nbytes); - coding.dst_bytes = MAX_MULTIBYTE_LENGTH * nbytes; - coding.mode |= CODING_MODE_LAST_BLOCK; - decode_coding_c_string (&coding, (unsigned char *) copy_bufptr, - nbytes, Qnil); - nbytes = coding.produced; - nchars = coding.produced_char; - copy_bufptr = (char *) coding.destination; - } - - copy_ubufptr = (unsigned char *) copy_bufptr; - - /* Convert the input data to a sequence of - character events. */ - for (i = 0; i < nbytes; i += len) - { - int ch; - if (nchars == nbytes) - ch = copy_ubufptr[i], len = 1; - else - ch = string_char_and_length (copy_ubufptr + i, &len); - inev.ie.kind = (SINGLE_BYTE_CHAR_P (ch) - ? ASCII_KEYSTROKE_EVENT - : MULTIBYTE_CHAR_KEYSTROKE_EVENT); - inev.ie.code = ch; - kbd_buffer_store_buffered_event (&inev, hold_quit); - } - - inev.ie.kind = NO_EVENT; + inev.ie.kind = MULTIBYTE_CHAR_KEYSTROKE_EVENT; + inev.ie.arg = make_unibyte_string (copy_bufptr, nbytes); goto xi_done_keysym; } + goto XI_OTHER; } @@ -12352,7 +12245,6 @@ handle_one_xevent (struct x_display_info *dpyinfo, /* Sometimes event processing draws to the frame outside redisplay. To ensure that these changes become visible, draw them here. */ flush_dirty_back_buffers (); - SAFE_FREE (); return count; } -- 2.39.5