From: Po Lu Date: Sat, 8 Jan 2022 07:21:51 +0000 (+0800) Subject: Allow using GTK+ to handle input methods on X X-Git-Tag: emacs-29.0.90~3204 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=d76fb0c11e9859db0d03d6496f5a720d304f4ca9;p=emacs.git Allow using GTK+ to handle input methods on X * doc/emacs/xresources.texi (Table of Resources): Document new value of `inputStyle'. * etc/NEWS: Announce new option. * lisp/cus-start.el (standard): Add `x-gtk-use-native-input'. * src/gtkutil.c (xg_mark_data): Mark xg_pending_quit_event. (xg_add_virtual_mods): (xg_im_context_commit): (xg_im_context_preedit_changed): (xg_im_context_preedit_end): (xg_widget_key_press_event_cb): (xg_filter_key): New functions. * src/gtkutil.h: Add prototype for `xg_filter_key'. * src/xfns.c (xic_set_preeditarea): Set cursor location for the GTK IM context as well. * src/xterm.c (xg_pending_quit_event): New variable. (x_focus_changed): Set focus on the GTK input context as well. (x_filter_event): Filter events through GTK if the user asked for it. (handle_one_xevent): Likewise. (XTread_socket): Set hold_quit to xg_pending_quit_event if it exists. (x_draw_window_cursor): Always set preedit area even if XIC doesn't exist. * src/xterm.h (struct x_display_info): New field `prefer_native_input'. (struct x_output): New field `im_context'. --- diff --git a/doc/emacs/xresources.texi b/doc/emacs/xresources.texi index c5dc4e8150f..a07c14fda9e 100644 --- a/doc/emacs/xresources.texi +++ b/doc/emacs/xresources.texi @@ -355,6 +355,10 @@ Let the input method decide how to display itself. This is usually equivalent to @samp{overthespot}, but it might work with more input methods. +@item native +Use the toolkit for handling input methods. This is currently +implemented only on GTK. + @item root Use some location on display specific to the input method for displaying the preview text. diff --git a/etc/NEWS b/etc/NEWS index ea08f7f3de7..fda2a3946f9 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -149,6 +149,11 @@ and pop-up menus. This controls the style of the pre-edit and status areas of X input methods. +--- +** New user option 'x-gtk-use-native-input'. +This controls whether or not GTK input methods are used by Emacs, +instead of XIM input methods. + --- ** New minor mode 'pixel-scroll-precision-mode'. When enabled, and if your mouse supports it, you can scroll the diff --git a/lisp/cus-start.el b/lisp/cus-start.el index 667e36b211c..4227cec425c 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -829,6 +829,7 @@ since it could result in memory overflow and make Emacs crash." (x-stretch-cursor display boolean "21.1") (scroll-bar-adjust-thumb-portion windows boolean "24.4") (x-scroll-event-delta-factor mouse float "29.1") + (x-gtk-use-native-input keyboard boolean "29.1") ;; xselect.c (x-select-enable-clipboard-manager killing boolean "24.1") ;; xsettings.c diff --git a/src/gtkutil.c b/src/gtkutil.c index 93f51d77962..4c516a4479a 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -76,6 +76,13 @@ typedef struct pgtk_output xp_output; #define XG_TEXT_OPEN GTK_STOCK_OPEN #endif +#ifndef HAVE_PGTK +static void xg_im_context_commit (GtkIMContext *, gchar *, gpointer); +static void xg_im_context_preedit_changed (GtkIMContext *, gpointer); +static void xg_im_context_preedit_end (GtkIMContext *, gpointer); +static bool xg_widget_key_press_event_cb (GtkWidget *, GdkEvent *, gpointer); +#endif + #ifndef HAVE_GTK3 #ifdef HAVE_FREETYPE @@ -1436,6 +1443,9 @@ xg_create_frame_widgets (struct frame *f) GtkWidget *wfixed; #ifndef HAVE_GTK3 GtkRcStyle *style; +#endif +#ifndef HAVE_PGTK + GtkIMContext *imc; #endif GtkWindowType type = GTK_WINDOW_TOPLEVEL; char *title = 0; @@ -1621,6 +1631,22 @@ xg_create_frame_widgets (struct frame *f) #ifndef HAVE_PGTK gtk_widget_set_tooltip_text (wtop, "Dummy text"); g_signal_connect (wtop, "query-tooltip", G_CALLBACK (qttip_cb), f); + + imc = gtk_im_multicontext_new (); + g_object_ref (imc); + gtk_im_context_set_use_preedit (imc, TRUE); + + g_signal_connect (G_OBJECT (imc), "commit", + G_CALLBACK (xg_im_context_commit), f); + g_signal_connect (G_OBJECT (imc), "preedit-changed", + G_CALLBACK (xg_im_context_preedit_changed), NULL); + g_signal_connect (G_OBJECT (imc), "preedit-end", + G_CALLBACK (xg_im_context_preedit_end), NULL); + FRAME_X_OUTPUT (f)->im_context = imc; + + g_signal_connect (G_OBJECT (wfixed), "key-press-event", + G_CALLBACK (xg_widget_key_press_event_cb), + NULL); #endif { @@ -1761,6 +1787,7 @@ xg_free_frame_widgets (struct frame *f) /* x_free_frame_resources should have taken care of it */ #ifndef HAVE_PGTK eassert (!FRAME_X_DOUBLE_BUFFERED_P (f)); + g_object_unref (FRAME_X_OUTPUT (f)->im_context); #endif gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f)); FRAME_X_WINDOW (f) = 0; /* Set to avoid XDestroyWindow in xterm.c */ @@ -2928,6 +2955,14 @@ xg_mark_data (void) } } } + + if (xg_pending_quit_event.kind != NO_EVENT) + { + eassert (xg_pending_quit_event.kind == ASCII_KEYSTROKE_EVENT); + + mark_object (xg_pending_quit_event.frame_or_window); + mark_object (xg_pending_quit_event.arg); + } } /* Callback called when a menu item is destroyed. Used to free data. @@ -5963,4 +5998,323 @@ xg_initialize (void) #endif } +#ifndef HAVE_PGTK +static void +xg_add_virtual_mods (struct x_display_info *dpyinfo, GdkEventKey *key) +{ + guint modifiers = key->state; + + if (modifiers & dpyinfo->meta_mod_mask) + { + /* GDK always assumes Mod1 is alt, but that's no reason for + us to make that mistake as well. */ + if (!dpyinfo->alt_mod_mask) + key->state |= GDK_MOD1_MASK; + else + key->state |= GDK_META_MASK; + } + + if (modifiers & dpyinfo->alt_mod_mask) + key->state |= GDK_MOD1_MASK; + if (modifiers & dpyinfo->super_mod_mask) + key->state |= GDK_SUPER_MASK; + if (modifiers & dpyinfo->hyper_mod_mask) + key->state |= GDK_HYPER_MASK; +} + +static void +xg_im_context_commit (GtkIMContext *imc, gchar *str, + gpointer user_data) +{ + struct frame *f = user_data; + struct input_event ie; + gunichar *ucs4_str; + + ucs4_str = g_utf8_to_ucs4_fast (str, -1, NULL); + + if (!ucs4_str) + return; + + for (gunichar *c = ucs4_str; *c; c++) + { + EVENT_INIT (ie); + ie.kind = (SINGLE_BYTE_CHAR_P (*c) + ? ASCII_KEYSTROKE_EVENT + : MULTIBYTE_CHAR_KEYSTROKE_EVENT); + ie.arg = Qnil; + ie.code = *c; + XSETFRAME (ie.frame_or_window, f); + ie.modifiers = 0; + ie.timestamp = 0; + + kbd_buffer_store_event (&ie); + } + + g_free (ucs4_str); +} + +static void +xg_im_context_preedit_changed (GtkIMContext *imc, gpointer user_data) +{ + PangoAttrList *list; + gchar *str; + gint cursor; + struct input_event inev; + + gtk_im_context_get_preedit_string (imc, &str, &list, &cursor); + + EVENT_INIT (inev); + inev.kind = PREEDIT_TEXT_EVENT; + inev.arg = build_string_from_utf8 (str); + kbd_buffer_store_event (&inev); + + g_free (str); + pango_attr_list_unref (list); +} + +static void +xg_im_context_preedit_end (GtkIMContext *imc, gpointer user_data) +{ + struct input_event inev; + + EVENT_INIT (inev); + inev.kind = PREEDIT_TEXT_EVENT; + inev.arg = Qnil; + kbd_buffer_store_event (&inev); +} + +static bool +xg_widget_key_press_event_cb (GtkWidget *widget, GdkEvent *event, + gpointer user_data) +{ + Lisp_Object tail, tem; + struct frame *f = NULL; + union buffered_input_event inev; + guint keysym = event->key.keyval; + gunichar *cb; + ptrdiff_t i; + glong len; + + FOR_EACH_FRAME (tail, tem) + { + if (FRAME_X_P (XFRAME (tem)) + && (FRAME_GTK_WIDGET (XFRAME (tem)) == widget)) + { + f = XFRAME (tem); + break; + } + } + + if (!f) + return true; + + if (!x_gtk_use_native_input + && !FRAME_DISPLAY_INFO (f)->prefer_native_input) + return true; + + EVENT_INIT (inev.ie); + XSETFRAME (inev.ie.frame_or_window, f); + + inev.ie.modifiers |= x_x_to_emacs_modifiers (FRAME_DISPLAY_INFO (f), + event->key.state); + + /* First deal with keysyms which have defined + translations to characters. */ + if (keysym >= 32 && keysym < 128) + /* Avoid explicitly decoding each ASCII character. */ + { + inev.ie.kind = ASCII_KEYSTROKE_EVENT; + inev.ie.code = keysym; + goto done; + } + + /* Keysyms directly mapped to Unicode characters. */ + if (keysym >= 0x01000000 && keysym <= 0x0110FFFF) + { + if (keysym < 0x01000080) + inev.ie.kind = ASCII_KEYSTROKE_EVENT; + else + inev.ie.kind = MULTIBYTE_CHAR_KEYSTROKE_EVENT; + inev.ie.code = keysym & 0xFFFFFF; + goto done; + } + + /* Random non-modifier sorts of keysyms. */ + if (((keysym >= GDK_KEY_BackSpace && keysym <= GDK_KEY_Escape) + || keysym == GDK_KEY_Delete +#ifdef GDK_KEY_ISO_Left_Tab + || (keysym >= GDK_KEY_ISO_Left_Tab && keysym <= GDK_KEY_ISO_Enter) +#endif + || IsCursorKey (keysym) /* 0xff50 <= x < 0xff60 */ + || IsMiscFunctionKey (keysym) /* 0xff60 <= x < VARIES */ +#ifdef GDK_KEY_dead_circumflex + || keysym == GDK_KEY_dead_circumflex +#endif +#ifdef GDK_KEY_dead_grave + || keysym == GDK_KEY_dead_grave +#endif +#ifdef GDK_KEY_dead_tilde + || keysym == GDK_KEY_dead_tilde +#endif +#ifdef GDK_KEY_dead_diaeresis + || keysym == GDK_KEY_dead_diaeresis +#endif +#ifdef GDK_KEY_dead_macron + || keysym == GDK_KEY_dead_macron +#endif +#ifdef GDK_KEY_dead_degree + || keysym == GDK_KEY_dead_degree +#endif +#ifdef GDK_KEY_dead_acute + || keysym == GDK_KEY_dead_acute +#endif +#ifdef GDK_KEY_dead_cedilla + || keysym == GDK_KEY_dead_cedilla +#endif +#ifdef GDK_KEY_dead_breve + || keysym == GDK_KEY_dead_breve +#endif +#ifdef GDK_KEY_dead_ogonek + || keysym == GDK_KEY_dead_ogonek +#endif +#ifdef GDK_KEY_dead_caron + || keysym == GDK_KEY_dead_caron +#endif +#ifdef GDK_KEY_dead_doubleacute + || keysym == GDK_KEY_dead_doubleacute +#endif +#ifdef GDK_KEY_dead_abovedot + || keysym == GDK_KEY_dead_abovedot +#endif + || IsKeypadKey (keysym) /* 0xff80 <= x < 0xffbe */ + || IsFunctionKey (keysym) /* 0xffbe <= x < 0xffe1 */ + /* Any "vendor-specific" key is ok. */ + || (keysym & (1 << 28)) + || (keysym != GDK_KEY_VoidSymbol && !event->key.string)) + && !(event->key.is_modifier)) + { + inev.ie.kind = NON_ASCII_KEYSTROKE_EVENT; + inev.ie.code = keysym; + goto done; + } + + if (event->key.string) + { + cb = g_utf8_to_ucs4_fast (event->key.string, -1, &len); + + for (i = 0; i < len; ++i) + { + inev.ie.kind = (SINGLE_BYTE_CHAR_P (cb[i]) + ? ASCII_KEYSTROKE_EVENT + : MULTIBYTE_CHAR_KEYSTROKE_EVENT); + inev.ie.code = cb[i]; + + kbd_buffer_store_buffered_event (&inev, &xg_pending_quit_event); + } + + g_free (cb); + + inev.ie.kind = NO_EVENT; + } + + done: + if (inev.ie.kind != NO_EVENT) + { + xg_pending_quit_event.kind = NO_EVENT; + kbd_buffer_store_buffered_event (&inev, &xg_pending_quit_event); + } + return true; +} + +bool +xg_filter_key (struct frame *frame, XEvent *xkey) +{ + GdkEvent *xg_event = gdk_event_new (GDK_KEY_PRESS); + GdkDisplay *dpy = gtk_widget_get_display (FRAME_GTK_WIDGET (frame)); + GdkKeymap *keymap = gdk_keymap_get_for_display (dpy); + GdkModifierType consumed; + struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame); + bool result; + + xg_event->any.window = gtk_widget_get_window (FRAME_GTK_WIDGET (frame)); + g_object_ref (xg_event->any.window); + +#if GTK_CHECK_VERSION (3, 20, 0) + GdkSeat *seat = gdk_display_get_default_seat (dpy); + + gdk_event_set_device (xg_event, + gdk_seat_get_keyboard (seat)); +#elif GTK_CHECK_VERSION (3, 16, 0) + GdkDeviceManager *manager = gdk_display_get_device_manager (dpy); + GList *devices = gdk_device_manager_list_devices (manager, + GDK_DEVICE_TYPE_MASTER); + GdkDevice *device; + GList *tem; + for (tem = devices; tem; tem = tem->next) + { + device = GDK_DEVICE (tem->data); + + if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD) + { + gdk_event_set_device (xg_event, device); + break; + } + } + + g_list_free (devices); +#endif + +#ifdef HAVE_XINPUT2 + if (xkey->type != GenericEvent) + { +#endif + xg_event->key.hardware_keycode = xkey->xkey.keycode; + +#ifdef HAVE_XKB + if (dpyinfo->supports_xkb) + xg_event->key.group = XkbGroupForCoreState (xkey->xkey.state); +#endif + xg_event->key.state = xkey->xkey.state; + gdk_keymap_translate_keyboard_state (keymap, + xkey->xkey.keycode, + xkey->xkey.state, + xg_event->key.group, + &xg_event->key.keyval, + NULL, NULL, &consumed); + xg_add_virtual_mods (dpyinfo, &xg_event->key); + xg_event->key.state &= ~consumed; +#if GTK_CHECK_VERSION (3, 6, 0) + xg_event->key.is_modifier = gdk_x11_keymap_key_is_modifier (keymap, + xg_event->key.hardware_keycode); +#endif +#ifdef HAVE_XINPUT2 + } + else + { + XIDeviceEvent *xev = (XIDeviceEvent *) xkey->xcookie.data; + + xg_event->key.hardware_keycode = xev->detail; + xg_event->key.group = xev->group.effective; + xg_event->key.state = xev->mods.effective; + gdk_keymap_translate_keyboard_state (keymap, + xev->detail, + xev->mods.effective, + xg_event->key.group, + &xg_event->key.keyval, + NULL, NULL, &consumed); + xg_add_virtual_mods (dpyinfo, &xg_event->key); + xg_event->key.state &= ~consumed; + xg_event->key.is_modifier = gdk_x11_keymap_key_is_modifier (keymap, + xg_event->key.hardware_keycode); + } +#endif + + result = gtk_im_context_filter_keypress (FRAME_X_OUTPUT (frame)->im_context, + &xg_event->key); + + gdk_event_free (xg_event); + + return result; +} +#endif #endif /* USE_GTK */ diff --git a/src/gtkutil.h b/src/gtkutil.h index 5a918259280..a1dd281f1d3 100644 --- a/src/gtkutil.h +++ b/src/gtkutil.h @@ -217,6 +217,8 @@ extern void xg_print_frames_dialog (Lisp_Object); extern bool xg_is_menu_window (Display *dpy, Window); #endif +extern bool xg_filter_key (struct frame *frame, XEvent *xkey); + /* Mark all callback data that are Lisp_object:s during GC. */ extern void xg_mark_data (void); diff --git a/src/xfns.c b/src/xfns.c index 705fa548a21..073200ff75a 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -2820,16 +2820,33 @@ xic_set_preeditarea (struct window *w, int x, int y) XVaNestedList attr; XPoint spot; - spot.x = WINDOW_TO_FRAME_PIXEL_X (w, x) + WINDOW_LEFT_FRINGE_WIDTH (w) + WINDOW_LEFT_MARGIN_WIDTH(w); - spot.y = WINDOW_TO_FRAME_PIXEL_Y (w, y) + FONT_BASE (FRAME_FONT (f)); - attr = XVaCreateNestedList (0, XNSpotLocation, &spot, - XNPreeditStartCallback, &Xxic_preedit_start_callback, - XNPreeditDoneCallback, &Xxic_preedit_done_callback, - XNPreeditDrawCallback, &Xxic_preedit_draw_callback, - XNPreeditCaretCallback, &Xxic_preedit_caret_callback, - NULL); - XSetICValues (FRAME_XIC (f), XNPreeditAttributes, attr, NULL); - XFree (attr); + if (FRAME_XIC (WINDOW_XFRAME (w))) + { + spot.x = WINDOW_TO_FRAME_PIXEL_X (w, x) + WINDOW_LEFT_FRINGE_WIDTH (w) + WINDOW_LEFT_MARGIN_WIDTH(w); + spot.y = WINDOW_TO_FRAME_PIXEL_Y (w, y) + FONT_BASE (FRAME_FONT (f)); + attr = XVaCreateNestedList (0, XNSpotLocation, &spot, + XNPreeditStartCallback, &Xxic_preedit_start_callback, + XNPreeditDoneCallback, &Xxic_preedit_done_callback, + XNPreeditDrawCallback, &Xxic_preedit_draw_callback, + XNPreeditCaretCallback, &Xxic_preedit_caret_callback, + NULL); + XSetICValues (FRAME_XIC (f), XNPreeditAttributes, attr, NULL); + XFree (attr); + } +#ifdef USE_GTK + GdkRectangle rect; + rect.x = (WINDOW_TO_FRAME_PIXEL_X (w, x) + + WINDOW_LEFT_FRINGE_WIDTH (w) + + WINDOW_LEFT_MARGIN_WIDTH (w)); + rect.y = (WINDOW_TO_FRAME_PIXEL_Y (w, y) + + FRAME_TOOLBAR_HEIGHT (f) + + FRAME_MENUBAR_HEIGHT (f)); + rect.width = w->phys_cursor_width; + rect.height = w->phys_cursor_height; + + gtk_im_context_set_cursor_location (FRAME_X_OUTPUT (f)->im_context, + &rect); +#endif } diff --git a/src/xterm.c b/src/xterm.c index b284fdd3123..9b4bd4b8db2 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -147,6 +147,17 @@ bool use_xim = true; bool use_xim = false; /* configure --without-xim */ #endif +#ifdef USE_GTK +/* GTK can't tolerate a call to `handle_interrupt' inside an event + signal handler, but we have to store input events inside the + handler for native input to work. + + This acts as a `hold_quit', and it is stored in the keyboard buffer + (thereby causing the call to `handle_interrupt') after the GTK + signal handler exits and control returns to XTread_socket. */ +struct input_event xg_pending_quit_event = { .kind = NO_EVENT }; +#endif + /* Non-zero means that a HELP_EVENT has been generated since Emacs start. */ @@ -4931,7 +4942,8 @@ x_new_focus_frame (struct x_display_info *dpyinfo, struct frame *frame) a FOCUS_IN_EVENT into *BUFP. */ static void -x_focus_changed (int type, int state, struct x_display_info *dpyinfo, struct frame *frame, struct input_event *bufp) +x_focus_changed (int type, int state, struct x_display_info *dpyinfo, struct frame *frame, + struct input_event *bufp) { if (type == FocusIn) { @@ -4947,7 +4959,15 @@ x_focus_changed (int type, int state, struct x_display_info *dpyinfo, struct fra #ifdef HAVE_X_I18N if (FRAME_XIC (frame)) - XSetICFocus (FRAME_XIC (frame)); + XSetICFocus (FRAME_XIC (frame)); +#ifdef USE_GTK + GtkWidget *widget; + + gtk_im_context_focus_in (FRAME_X_OUTPUT (frame)->im_context); + widget = FRAME_GTK_OUTER_WIDGET (frame); + gtk_im_context_set_client_window (FRAME_X_OUTPUT (frame)->im_context, + gtk_widget_get_window (widget)); +#endif #endif } else if (type == FocusOut) @@ -4966,6 +4986,10 @@ x_focus_changed (int type, int state, struct x_display_info *dpyinfo, struct fra #ifdef HAVE_X_I18N if (FRAME_XIC (frame)) XUnsetICFocus (FRAME_XIC (frame)); +#ifdef USE_GTK + gtk_im_context_focus_out (FRAME_X_OUTPUT (frame)->im_context); + gtk_im_context_set_client_window (FRAME_X_OUTPUT (frame)->im_context, NULL); +#endif #endif if (frame->pointer_invisible) XTtoggle_invisible_pointer (frame, false); @@ -8229,10 +8253,52 @@ x_filter_event (struct x_display_info *dpyinfo, XEvent *event) XFilterEvent because that's the one for which the IC was created. */ - struct frame *f1 = x_any_window_to_frame (dpyinfo, - event->xclient.window); + struct frame *f1; - return XFilterEvent (event, f1 ? FRAME_X_WINDOW (f1) : None); +#if defined HAVE_XINPUT2 && defined USE_GTK + bool xinput_event = false; + if (dpyinfo->supports_xi2 + && event->type == GenericEvent + && (event->xgeneric.extension + == dpyinfo->xi2_opcode) + && (event->xgeneric.evtype + == XI_KeyPress)) + { + f1 = x_any_window_to_frame (dpyinfo, + ((XIDeviceEvent *) + event->xcookie.data)->event); + xinput_event = true; + } + else +#endif + f1 = x_any_window_to_frame (dpyinfo, + event->xclient.window); + +#ifdef USE_GTK + if (!x_gtk_use_native_input + && !dpyinfo->prefer_native_input) + { +#endif + return XFilterEvent (event, f1 ? FRAME_X_WINDOW (f1) : None); +#ifdef USE_GTK + } + else if (f1 && (event->type == KeyPress +#ifdef HAVE_XINPUT2 + || xinput_event +#endif + )) + { + bool result; + + block_input (); + result = xg_filter_key (f1, event); + unblock_input (); + + return result; + } + + return 0; +#endif } #endif @@ -10679,12 +10745,23 @@ handle_one_xevent (struct x_display_info *dpyinfo, xkey.keycode = xev->detail; xkey.same_screen = True; +#ifdef USE_GTK + if ((!x_gtk_use_native_input + && x_filter_event (dpyinfo, (XEvent *) &xkey)) + || (x_gtk_use_native_input + && x_filter_event (dpyinfo, event))) + { + *finish = X_EVENT_DROP; + goto XI_OTHER; + } +#else if (x_filter_event (dpyinfo, (XEvent *) &xkey)) { *finish = X_EVENT_DROP; goto XI_OTHER; } #endif +#endif #ifdef HAVE_XKB if (dpyinfo->xkb_desc) @@ -11421,6 +11498,20 @@ XTread_socket (struct terminal *terminal, struct input_event *hold_quit) if (current_finish == X_EVENT_GOTO_OUT) break; } + + /* Now see if `xg_pending_quit_event' was set. */ + if (xg_pending_quit_event.kind != NO_EVENT) + { + /* Check that the frame is still valid. It could have been + deleted between now and the time the event was recorded. */ + if (FRAME_LIVE_P (XFRAME (xg_pending_quit_event.frame_or_window))) + /* Store that event into hold_quit and clear the pending quit + event. */ + *hold_quit = xg_pending_quit_event; + + /* If the frame is invalid, just clear the event as well. */ + xg_pending_quit_event.kind = NO_EVENT; + } #endif /* USE_GTK */ /* On some systems, an X bug causes Emacs to get no more events @@ -11726,8 +11817,7 @@ x_draw_window_cursor (struct window *w, struct glyph_row *glyph_row, int x, #ifdef HAVE_X_I18N if (w == XWINDOW (f->selected_window)) - if (FRAME_XIC (f)) - xic_set_preeditarea (w, x, y); + xic_set_preeditarea (w, x, y); #endif } @@ -15299,6 +15389,10 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name) dpyinfo->preferred_xim_style = STYLE_OFFTHESPOT; else if (!strcmp (SSDATA (value), "root")) dpyinfo->preferred_xim_style = STYLE_ROOT; +#ifdef USE_GTK + else if (!strcmp (SSDATA (value), "native")) + dpyinfo->prefer_native_input = true; +#endif } #endif } @@ -15846,4 +15940,10 @@ always uses gtk_window_move and ignores the value of this variable. */); This option is only effective when Emacs is built with XInput 2 support. */); Vx_scroll_event_delta_factor = make_float (1.0); + + DEFVAR_BOOL ("x-gtk-use-native-input", x_gtk_use_native_input, + doc: /* Non-nil means to use GTK for input method support. +This provides better support for some modern input methods, and is +only effective when Emacs is built with GTK. */); + x_gtk_use_native_input = false; } diff --git a/src/xterm.h b/src/xterm.h index a796f69ddc1..fc47fe0c6ea 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -527,6 +527,10 @@ struct x_display_info int xkb_event_type; XkbDescPtr xkb_desc; #endif + +#ifdef USE_GTK + bool prefer_native_input; +#endif }; #ifdef HAVE_X_I18N @@ -643,6 +647,8 @@ struct x_output GtkTooltip *ttip_widget; GtkWidget *ttip_lbl; GtkWindow *ttip_window; + + GtkIMContext *im_context; #endif /* USE_GTK */ /* If >=0, a bitmap index. The indicated bitmap is used for the @@ -1340,6 +1346,10 @@ extern void x_session_close (void); #define STYLE_NONE (XIMPreeditNothing | XIMStatusNothing) #endif +#ifdef USE_GTK +extern struct input_event xg_pending_quit_event; +#endif + /* Is the frame embedded into another application? */ #define FRAME_X_EMBEDDED_P(f) (FRAME_X_OUTPUT(f)->explicit_parent != 0)