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:
parent
63c83e40da
commit
d76fb0c11e
8 changed files with 510 additions and 17 deletions
|
|
@ -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.
|
||||
|
|
|
|||
5
etc/NEWS
5
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
354
src/gtkutil.c
354
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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
37
src/xfns.c
37
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
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
114
src/xterm.c
114
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;
|
||||
}
|
||||
|
|
|
|||
10
src/xterm.h
10
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)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue