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'.
This commit is contained in:
Po Lu 2022-01-08 15:21:51 +08:00
parent 63c83e40da
commit d76fb0c11e
8 changed files with 510 additions and 17 deletions

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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 */

View file

@ -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);

View file

@ -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
}

View file

@ -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;
}

View file

@ -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)