[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
master d76fb0c11e: Allow using GTK+ to handle input methods on X
From: |
Po Lu |
Subject: |
master d76fb0c11e: Allow using GTK+ to handle input methods on X |
Date: |
Sat, 8 Jan 2022 02:26:18 -0500 (EST) |
branch: master
commit d76fb0c11e9859db0d03d6496f5a720d304f4ca9
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>
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'.
---
doc/emacs/xresources.texi | 4 +
etc/NEWS | 5 +
lisp/cus-start.el | 1 +
src/gtkutil.c | 354 ++++++++++++++++++++++++++++++++++++++++++++++
src/gtkutil.h | 2 +
src/xfns.c | 37 +++--
src/xterm.c | 114 ++++++++++++++-
src/xterm.h | 10 ++
8 files changed, 510 insertions(+), 17 deletions(-)
diff --git a/doc/emacs/xresources.texi b/doc/emacs/xresources.texi
index c5dc4e8150..a07c14fda9 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 ea08f7f3de..fda2a3946f 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 667e36b211..4227cec425 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 93f51d7796..4c516a4479 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 5a91825928..a1dd281f1d 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 705fa548a2..073200ff75 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 b284fdd312..9b4bd4b8db 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 a796f69ddc..fc47fe0c6e 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)
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- master d76fb0c11e: Allow using GTK+ to handle input methods on X,
Po Lu <=