commit f7c3d628333e051c18b0493271b23f27933f2598 (HEAD, refs/remotes/origin/master) Author: Po Lu Date: Sat Jan 8 15:30:02 2022 +0800 * lisp/cus-start.el (standard): Fix standard customizables for PGTK. diff --git a/lisp/cus-start.el b/lisp/cus-start.el index 4227cec425..cdadf08a89 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -856,6 +856,9 @@ since it could result in memory overflow and make Emacs crash." (featurep 'ns)) ((string-match "\\`haiku-" (symbol-name symbol)) (featurep 'haiku)) + ((eq symbol 'x-gtk-use-native-input) + (and (featurep 'x) + (featurep 'gtk))) ((string-match "\\`x-.*gtk" (symbol-name symbol)) (featurep 'gtk)) ((string-match "clipboard-manager" (symbol-name symbol)) commit 79cc87f3b7a90376b16c8e45bb94eccdab4458e9 Author: Po Lu Date: Sat Jan 8 15:27:50 2022 +0800 Fix build on PGTK * src/gtkutil.c (xg_mark_data): * src/gtkutil.h (xg_filter_key): Ifdef out definitions on PGTK. diff --git a/src/gtkutil.c b/src/gtkutil.c index 4c516a4479..f236227514 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -2956,6 +2956,7 @@ xg_mark_data (void) } } +#ifndef HAVE_PGTK if (xg_pending_quit_event.kind != NO_EVENT) { eassert (xg_pending_quit_event.kind == ASCII_KEYSTROKE_EVENT); @@ -2963,6 +2964,7 @@ xg_mark_data (void) mark_object (xg_pending_quit_event.frame_or_window); mark_object (xg_pending_quit_event.arg); } +#endif } /* Callback called when a menu item is destroyed. Used to free data. diff --git a/src/gtkutil.h b/src/gtkutil.h index a1dd281f1d..b74244d84d 100644 --- a/src/gtkutil.h +++ b/src/gtkutil.h @@ -217,7 +217,9 @@ extern void xg_print_frames_dialog (Lisp_Object); extern bool xg_is_menu_window (Display *dpy, Window); #endif +#ifndef HAVE_PGTK extern bool xg_filter_key (struct frame *frame, XEvent *xkey); +#endif /* Mark all callback data that are Lisp_object:s during GC. */ extern void xg_mark_data (void); commit d76fb0c11e9859db0d03d6496f5a720d304f4ca9 Author: Po Lu Date: Sat Jan 8 15:21:51 2022 +0800 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 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) commit 63c83e40dad88036d5ceef17eace51056a18b55f Author: Po Lu Date: Sat Jan 8 11:18:59 2022 +0800 * src/pgtkterm.c (pgtk_enqueue_string): Free return of g_utf8_to_ucs4. diff --git a/src/pgtkterm.c b/src/pgtkterm.c index 1d301d11f6..1195d53d32 100644 --- a/src/pgtkterm.c +++ b/src/pgtkterm.c @@ -5231,9 +5231,9 @@ pgtk_emacs_to_gtk_modifiers (struct pgtk_display_info *dpyinfo, int state) void pgtk_enqueue_string (struct frame *f, gchar * str) { - gunichar *ustr; + gunichar *ustr, *uptr; - ustr = g_utf8_to_ucs4 (str, -1, NULL, NULL, NULL); + uptr = ustr = g_utf8_to_ucs4 (str, -1, NULL, NULL, NULL); if (ustr == NULL) return; for (; *ustr != 0; ustr++) @@ -5252,6 +5252,7 @@ pgtk_enqueue_string (struct frame *f, gchar * str) evq_enqueue (&inev); } + g_free (uptr); } void commit 9bb71f823695b26c324712f846fc66e70ff8f1c3 Author: Po Lu Date: Sat Jan 8 11:16:22 2022 +0800 Call EVENT_INIT in some places * src/xfns.c (xic_preedit_done_callback): (xic_preedit_draw_callback): Call EVENT_INIT. diff --git a/src/xfns.c b/src/xfns.c index fd3b875244..705fa548a2 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -2930,6 +2930,7 @@ xic_preedit_done_callback (XIC xic, XPointer client_data, struct frame *f = x_xic_to_frame (xic); struct x_output *output; struct input_event ie; + EVENT_INIT (ie); if (f) { @@ -3001,6 +3002,7 @@ xic_preedit_draw_callback (XIC xic, XPointer client_data, char *text; char *chg_start, *chg_end; struct input_event ie; + EVENT_INIT (ie); if (f) { commit ad5cf84fa737d26ed12e75e09e5f079df0efe5f6 Author: Sam Steingold Date: Thu Jan 6 14:42:10 2022 -0500 Add `auth-info-password' and use it instead of ad hoc code * lisp/auth-source.el (auth-info-password): Extract from `auth-source-pick-first-password'. (auth-source-pick-first-password, auth-source-secrets-create) (auth-source-user-and-password): Use `auth-info-password'. * lisp/erc/erc-services.el (erc-nickserv-get-password): Use `auth-source-pick-first-password'. * lisp/erc/erc.el (erc-open, erc-server-join-channel): Likewise. * lisp/gnus/mail-source.el (mail-source-set-1): Add a comment. * lisp/gnus/nnimap.el (nnimap-credentials): Use `auth-info-password'. * lisp/gnus/nntp.el (nntp-send-authinfo): Likewise. * lisp/mail/rmail.el (rmail-get-remote-password): Likewise. * lisp/mail/smtpmail.el (smtpmail-try-auth-methods): Likewise. * lisp/net/sieve-manage.el (sieve-sasl-auth): Likewise. * lisp/net/tramp.el (tramp-read-passwd): Likewise. * lisp/net/rcirc.el (rcirc): Likewise (fixes a bug: the possibility that password might be a function was not handled). diff --git a/lisp/auth-source.el b/lisp/auth-source.el index 80c220561a..046a685d74 100644 --- a/lisp/auth-source.el +++ b/lisp/auth-source.el @@ -853,15 +853,17 @@ while \(:host t) would find all host entries." (cl-return 'no))) 'no)))) -(defun auth-source-pick-first-password (&rest spec) - "Pick the first secret found from applying SPEC to `auth-source-search'." - (let* ((result (nth 0 (apply #'auth-source-search (plist-put spec :max 1)))) - (secret (plist-get result :secret))) - +(defun auth-info-password (auth-info) + "Return the :secret password from the AUTH-INFO." + (let ((secret (plist-get auth-info :secret))) (if (functionp secret) (funcall secret) secret))) +(defun auth-source-pick-first-password (&rest spec) + "Pick the first secret found from applying SPEC to `auth-source-search'." + (auth-info-password (car (apply #'auth-source-search (plist-put spec :max 1))))) + (defun auth-source-format-prompt (prompt alist) "Format PROMPT using %x (for any character x) specifiers in ALIST. Remove trailing \": \"." @@ -1800,10 +1802,9 @@ authentication tokens: (plist-put artificial :save-function - (let* ((collection collection) - (item (plist-get artificial :label)) - (secret (plist-get artificial :secret)) - (secret (if (functionp secret) (funcall secret) secret))) + (let ((collection collection) + (item (plist-get artificial :label)) + (secret (auth-info-password artificial))) (lambda () (auth-source-secrets-saver collection item secret args))))) @@ -2410,9 +2411,7 @@ MODE can be \"login\" or \"password\"." :require '(:user :secret) :create nil)))) (user (plist-get auth-info :user)) - (password (plist-get auth-info :secret))) - (when (functionp password) - (setq password (funcall password))) + (password (auth-info-password auth-info))) (list user password auth-info))) ;;; Tiny mode for editing .netrc/.authinfo modes (that basically just diff --git a/lisp/erc/erc-services.el b/lisp/erc/erc-services.el index dcd786411f..4b3ca7d23f 100644 --- a/lisp/erc/erc-services.el +++ b/lisp/erc/erc-services.el @@ -444,15 +444,12 @@ it returns nil." (cl-second (assoc network erc-nickserv-passwords))))) (when erc-use-auth-source-for-nickserv-password - (let ((secret (cl-first (auth-source-search - :max 1 :require '(:secret) - :host server - ;; Ensure a string for :port - :port (format "%s" port) - :user nick)))) - (when secret - (let ((passwd (plist-get secret :secret))) - (if (functionp passwd) (funcall passwd) passwd))))) + (auth-source-pick-first-password + :require '(:secret) + :host server + ;; Ensure a string for :port + :port (format "%s" port) + :user nick)) (when erc-prompt-for-nickserv-password (read-passwd (format "NickServ password for %s on %s (RET to cancel): " diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index 24f4762571..5faeda9a13 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -2062,19 +2062,12 @@ Returns the buffer for the given server or channel." ;; password stuff (setq erc-session-password (or passwd - (let ((secret - (plist-get - (nth 0 - (auth-source-search :host server - :max 1 - :user nick - ;; secrets.el wouldn’t accept a number - :port (if (numberp port) (number-to-string port) port) - :require '(:secret))) - :secret))) - (if (functionp secret) - (funcall secret) - secret)))) + (auth-source-pick-first-password + :host server + :user nick + ;; secrets.el wouldn’t accept a number + :port (if (numberp port) (number-to-string port) port) + :require '(:secret)))) ;; client certificate (only useful if connecting over TLS) (setq erc-session-client-certificate client-certificate) ;; debug output buffer @@ -3187,16 +3180,12 @@ For a list of user commands (/join /part, ...): (put 'erc-cmd-HELP 'process-not-needed t) (defun erc-server-join-channel (server channel &optional secret) - (let* ((secret (or secret - (plist-get (nth 0 (auth-source-search - :max 1 - :host server - :port "irc" - :user channel)) - :secret))) - (password (if (functionp secret) - (funcall secret) - secret))) + (let ((password + (or secret + (auth-source-pick-first-password + :host server + :port "irc" + :user channel)))) (erc-log (format "cmd: JOIN: %s" channel)) (erc-server-send (concat "JOIN " channel (if password diff --git a/lisp/gnus/mail-source.el b/lisp/gnus/mail-source.el index d2f5b9a97e..9a48f710e5 100644 --- a/lisp/gnus/mail-source.el +++ b/lisp/gnus/mail-source.el @@ -454,7 +454,7 @@ the `mail-source-keyword-map' variable." search)))) :user))) user-auth) - ((and + ((and ; cf. 'auth-source-pick-first-password' (eq keyword :password) (setq pass-auth (plist-get diff --git a/lisp/gnus/nnimap.el b/lisp/gnus/nnimap.el index ab4243e867..090cb9b245 100644 --- a/lisp/gnus/nnimap.el +++ b/lisp/gnus/nnimap.el @@ -40,6 +40,7 @@ (autoload 'auth-source-forget+ "auth-source") (autoload 'auth-source-search "auth-source") +(autoload 'auth-info-password "auth-source") (nnoo-declare nnimap) @@ -407,10 +408,7 @@ during splitting, which may be slow." :create t)))) (if found (list (plist-get found :user) - (let ((secret (plist-get found :secret))) - (if (functionp secret) - (funcall secret) - secret)) + (auth-info-password found) (plist-get found :save-function)) nil))) diff --git a/lisp/gnus/nntp.el b/lisp/gnus/nntp.el index 990bb4426f..624c64d4d7 100644 --- a/lisp/gnus/nntp.el +++ b/lisp/gnus/nntp.el @@ -36,6 +36,7 @@ (eval-when-compile (require 'cl-lib)) (autoload 'auth-source-search "auth-source") +(autoload 'auth-info-password "auth-source") (defgroup nntp nil "NNTP access for Gnus." @@ -1175,10 +1176,7 @@ If SEND-IF-FORCE, only send authinfo to the server if the "563" "nntps" "snews")))) (auth-user (plist-get auth-info :user)) (auth-force (plist-get auth-info :force)) - (auth-passwd (plist-get auth-info :secret)) - (auth-passwd (if (functionp auth-passwd) - (funcall auth-passwd) - auth-passwd)) + (auth-passwd (auth-info-password auth-info)) (force (or (netrc-get alist "force") nntp-authinfo-force auth-force)) diff --git a/lisp/mail/rmail.el b/lisp/mail/rmail.el index 55921eca68..3795377cd2 100644 --- a/lisp/mail/rmail.el +++ b/lisp/mail/rmail.el @@ -4489,10 +4489,7 @@ password." :max 1 :user user :host host :require '(:secret))))) (if found - (let ((secret (plist-get found :secret))) - (if (functionp secret) - (funcall secret) - secret)) + (auth-info-password found) (read-passwd (if imap "IMAP password: " "POP password: ")))))) diff --git a/lisp/mail/smtpmail.el b/lisp/mail/smtpmail.el index 8ac0cd7e7c..88e55e968c 100644 --- a/lisp/mail/smtpmail.el +++ b/lisp/mail/smtpmail.el @@ -554,11 +554,9 @@ for `smtpmail-try-auth-method'.") :create ask-for-password))) (mech (or (plist-get auth-info :smtp-auth) (car mechs))) (user (plist-get auth-info :user)) - (password (plist-get auth-info :secret)) + (password (auth-info-password auth-info)) (save-function (and ask-for-password (plist-get auth-info :save-function)))) - (when (functionp password) - (setq password (funcall password))) (when (and user (not password)) ;; The user has stored the user name, but not the password, so @@ -573,9 +571,7 @@ for `smtpmail-try-auth-method'.") :user smtpmail-smtp-user :require '(:user :secret) :create t)) - password (plist-get auth-info :secret))) - (when (functionp password) - (setq password (funcall password))) + password (auth-info-password auth-info))) (let ((result (catch 'done (if (and mech user password) (smtpmail-try-auth-method process mech user password) diff --git a/lisp/net/rcirc.el b/lisp/net/rcirc.el index 9a1153b3c6..9d1600ed72 100644 --- a/lisp/net/rcirc.el +++ b/lisp/net/rcirc.el @@ -560,8 +560,8 @@ If ARG is non-nil, instead prompt for connection parameters." (auth (auth-source-search :host server :user user-name :port port)) - (fn (plist-get (car auth) :secret))) - (setq password (funcall fn))) + (pwd (auth-info-password (car auth)))) + (setq password pwd)) (when server (let (connected) (dolist (p (rcirc-process-list)) diff --git a/lisp/net/sieve-manage.el b/lisp/net/sieve-manage.el index 468bc90a9d..50342b9105 100644 --- a/lisp/net/sieve-manage.el +++ b/lisp/net/sieve-manage.el @@ -79,6 +79,7 @@ (require 'sasl) (autoload 'sasl-find-mechanism "sasl") (autoload 'auth-source-search "auth-source") +(autoload 'auth-info-password "auth-source") ;; User customizable variables: @@ -230,10 +231,7 @@ Return the buffer associated with the connection." :max 1 :create t)) (user-name (or (plist-get (nth 0 auth-info) :user) "")) - (user-password (or (plist-get (nth 0 auth-info) :secret) "")) - (user-password (if (functionp user-password) - (funcall user-password) - user-password)) + (user-password (or (auth-info-password (nth 0 auth-info)) "")) (client (sasl-make-client (sasl-find-mechanism (list mech)) user-name "sieve" sieve-manage-server)) (sasl-read-passphrase diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index adde443fdd..fdbb78123d 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -5756,10 +5756,7 @@ Invokes `password-read' if available, `read-passwd' else." :create t)) tramp-password-save-function (plist-get auth-info :save-function) - auth-passwd (plist-get auth-info :secret))) - (while (functionp auth-passwd) - (setq auth-passwd (funcall auth-passwd))) - auth-passwd) + auth-passwd (auth-info-password auth-info)))) ;; Try the password cache. (progn commit 19c6cad1821eb896b2ddd0f6eab030f0880ea254 Author: Eli Zaretskii Date: Fri Jan 7 20:30:17 2022 +0200 Fix "C-SPC C-SPC" after "C-x C-x" * lisp/simple.el (exchange-point-and-mark): Fix what the command does when 'transient-mark-mode' is OFF. (Bug#52896) diff --git a/lisp/simple.el b/lisp/simple.el index 070d2764fe..cbcde9fb8d 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -6664,7 +6664,7 @@ mode temporarily." (user-error "No mark set in this buffer")) (set-mark (point)) (goto-char omark) - (cond (temp-highlight + (cond ((and (not arg) (not temp-highlight)) (setq-local transient-mark-mode (cons 'only transient-mark-mode))) ((xor arg (not (region-active-p))) (deactivate-mark)) commit c168afb6f6d631a61c2ba764eec7218d25b838a5 Author: Eli Zaretskii Date: Fri Jan 7 16:38:47 2022 +0200 Minor improvements for 'pgtk' documentation * src/dispnew.c (syms_of_display) : * src/frame.c (Fwindow_system): * doc/lispref/display.texi (Defining Faces, Window Systems): * doc/lispref/frames.texi (Frames): Mention/explain 'pgtk'/'haiku'. * doc/lispref/commands.texi (Misc Events): Fix @example and markup. diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index fdbc05ce3d..01aa1e1fa4 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -2139,19 +2139,29 @@ On X, @var{arg} is a string describing some text to place behind the cursor. It can be @code{nil}, which means to remove any text previously displayed. -On PGTK, @var{arg} is a list of strings with color information. Its -structure is as follows: +On PGTK frames (@pxref{Frames}), @var{arg} is a list of strings with +information about their color and underline attributes. It has the +following form: @example - ( - (TEXT (ul . COLOR) (bg . COLOR) (fg . COLOR)) - ; ... +@group + ((@var{string1} + (ul . @var{underline-color}) + (bg . @var{background-color}) + (fg . @var{foreground-color})) + (@var{string2} + (ul . @var{underline-color}) + (bg . @var{background-color}) + (fg . @var{foreground-color})) + @dots{} ) +@end group @end example -Color information can be omitted. @code{COLOR} of @code{ul} can be -@code{t} or a string. @var{arg} can be @code{nil}, which means to -remove any text previously displayed. +Color information can be omitted, leaving just the text of the +strings. @var{underline-color} can be @code{t}, meaning underlined +text with default underline color, or it can be a string, the name of +the color to draw the underline. This is a special event (@pxref{Special Events}), which normally should not be bound by the user to any command. Emacs will typically diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index 4bc7884e9e..f191c400ab 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -2802,7 +2802,7 @@ apply to. Here are the possible values of @var{characteristic}: The kind of window system the terminal uses---either @code{graphic} (any graphics-capable display), @code{x}, @code{pc} (for the MS-DOS console), @code{w32} (for MS Windows 9X/NT/2K/XP), @code{haiku} (for -Haiku), @code{pgtk} (for GTK), or @code{tty} (a non-graphics-capable +Haiku), @code{pgtk} (for pure GTK), or @code{tty} (a non-graphics-capable display). @xref{Window Systems, window-system}. @item class @@ -8386,6 +8386,8 @@ GNUstep and macOS). Emacs is displaying the frame using MS-DOS direct screen writes. @item haiku Emacs is displaying the frame using the Application Kit on Haiku. +@item pgtk +Emacs is displaying the frame using pure GTK facilities. @item nil Emacs is displaying the frame on a character-based terminal. @end table diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi index ceaa11529f..ca7d9ada0b 100644 --- a/doc/lispref/frames.texi +++ b/doc/lispref/frames.texi @@ -60,6 +60,8 @@ The frame is displayed on a GNUstep or Macintosh Cocoa graphical terminal. @item pc The frame is displayed on an MS-DOS terminal. +@item pgtk +The frame is displayed using pure GTK facilities. @end table @end defun diff --git a/src/dispnew.c b/src/dispnew.c index 178d5caffb..6337bcf130 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -6662,6 +6662,8 @@ The value is a symbol: `w32' for an Emacs frame that is a window on MS-Windows display, `ns' for an Emacs frame on a GNUstep or Macintosh Cocoa display, `pc' for a direct-write MS-DOS frame. + `pgtk' for an Emacs frame using pure GTK facilities. + `haiku' for an Emacs frame running in Haiku. Use of this variable as a boolean is deprecated. Instead, use `display-graphic-p' or any of the other `display-*-p' @@ -6675,6 +6677,8 @@ The value is a symbol: `w32' for an Emacs frame that is a window on MS-Windows display, `ns' for an Emacs frame on a GNUstep or Macintosh Cocoa display, `pc' for a direct-write MS-DOS frame. + `pgtk' for an Emacs frame using pure GTK facilities. + `haiku' for an Emacs frame running in Haiku. Use of this variable as a boolean is deprecated. Instead, use `display-graphic-p' or any of the other `display-*-p' diff --git a/src/frame.c b/src/frame.c index 92120792f8..c0f4f3ecde 100644 --- a/src/frame.c +++ b/src/frame.c @@ -277,6 +277,8 @@ The value is a symbol: `w32' for an Emacs frame that is a window on MS-Windows display, `ns' for an Emacs frame on a GNUstep or Macintosh Cocoa display, `pc' for a direct-write MS-DOS frame. + `pgtk' for an Emacs frame using pure GTK facilities. + `haiku' for an Emacs frame running in Haiku. FRAME defaults to the currently selected frame. commit f424ef88e2cae6d3fb06041d63902af2b8cffe9b Author: Yuuki Harano Date: Fri Jan 7 22:09:13 2022 +0900 Handle internal events specially on PGTK * src/xgselect.c (xg_select): If there are pending events already, don't sleep. diff --git a/src/xgselect.c b/src/xgselect.c index 674c259db7..d22340fc9b 100644 --- a/src/xgselect.c +++ b/src/xgselect.c @@ -96,10 +96,17 @@ xg_select (int fds_lim, fd_set *rfds, fd_set *wfds, fd_set *efds, int n_gfds, retval = 0, our_fds = 0, max_fds = fds_lim - 1; int i, nfds, tmo_in_millisec, must_free = 0; bool need_to_dispatch; +#ifdef HAVE_PGTK + bool already_has_events; +#endif context = g_main_context_default (); acquire_select_lock (context); +#ifdef HAVE_PGTK + already_has_events = g_main_context_pending (context); +#endif + if (rfds) all_rfds = *rfds; else FD_ZERO (&all_rfds); if (wfds) all_wfds = *wfds; @@ -146,10 +153,41 @@ xg_select (int fds_lim, fd_set *rfds, fd_set *wfds, fd_set *efds, tmop = &tmo; } +#ifndef HAVE_PGTK fds_lim = max_fds + 1; nfds = thread_select (pselect, fds_lim, &all_rfds, have_wfds ? &all_wfds : NULL, efds, tmop, sigmask); +#else + /* + On PGTK, when you type a key, the key press event are received, + and one more key press event seems to be received internally. + The second event is not via a socket, so there are weird status: + - socket read buffer is empty + - a key press event is pending + In that case, we should not sleep, and dispatch the event immediately. + Bug#52761 + */ + if (!already_has_events) + { + fds_lim = max_fds + 1; + nfds = thread_select (pselect, fds_lim, + &all_rfds, have_wfds ? &all_wfds : NULL, efds, + tmop, sigmask); + } + else + { + /* Emulate return values */ + nfds = 1; + FD_ZERO (&all_rfds); + if (have_wfds) + FD_ZERO (&all_wfds); + if (efds) + FD_ZERO (efds); + our_fds++; + } +#endif + if (nfds < 0) retval = nfds; else if (nfds > 0) commit a263824ae36e69df74916980f448da245ee0f241 Author: Yuuki Harano Date: Fri Jan 7 21:13:16 2022 +0900 Write documentation about ARG of the preedit-text event on PGTK * doc/lispref/commands.texi (Misc Events): diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index 3c3b69c64d..fdbc05ce3d 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -2137,7 +2137,21 @@ of @var{arg} are dependent on the window system being used. On X, @var{arg} is a string describing some text to place behind the cursor. It can be @code{nil}, which means to remove any text -previously displayed. @c FIXME: what is the value of ARG on PGTK? +previously displayed. + +On PGTK, @var{arg} is a list of strings with color information. Its +structure is as follows: + +@example + ( + (TEXT (ul . COLOR) (bg . COLOR) (fg . COLOR)) + ; ... + ) +@end example + +Color information can be omitted. @code{COLOR} of @code{ul} can be +@code{t} or a string. @var{arg} can be @code{nil}, which means to +remove any text previously displayed. This is a special event (@pxref{Special Events}), which normally should not be bound by the user to any command. Emacs will typically commit d4e48c36420ec3a56c56f24cfa3888811b02e019 Author: Po Lu Date: Fri Jan 7 20:01:11 2022 +0800 Fix special-cased cursor foreground and background correctly on PGTK * src/pgtkfns.c (x_set_foreground_color): (x_set_background_color): Set cursor color as well if appropriate. (bug#53073) diff --git a/src/pgtkfns.c b/src/pgtkfns.c index e94c2dfb30..c604e2f100 100644 --- a/src/pgtkfns.c +++ b/src/pgtkfns.c @@ -186,18 +186,27 @@ pgtk_display_info_for_name (Lisp_Object name) static void x_set_foreground_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval) { - unsigned long fg; + unsigned long fg, old_fg; + block_input (); + old_fg = FRAME_FOREGROUND_COLOR (f); fg = x_decode_color (f, arg, BLACK_PIX_DEFAULT (f)); FRAME_FOREGROUND_PIXEL (f) = fg; FRAME_X_OUTPUT (f)->foreground_color = fg; if (FRAME_GTK_WIDGET (f)) { + if (FRAME_X_OUTPUT (f)->cursor_color == old_fg) + { + FRAME_X_OUTPUT (f)->cursor_color = fg; + FRAME_X_OUTPUT (f)->cursor_xgcv.background = fg; + } + update_face_from_frame_parameter (f, Qforeground_color, arg); if (FRAME_VISIBLE_P (f)) SET_FRAME_GARBAGED (f); } + unblock_input (); } @@ -206,6 +215,7 @@ x_set_background_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval) { unsigned long bg; + block_input (); bg = x_decode_color (f, arg, WHITE_PIX_DEFAULT (f)); FRAME_BACKGROUND_PIXEL (f) = bg; @@ -214,12 +224,14 @@ x_set_background_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval) pgtk_clear_frame (f); FRAME_X_OUTPUT (f)->background_color = bg; + FRAME_X_OUTPUT (f)->cursor_xgcv.foreground = bg; xg_set_background_color (f, bg); update_face_from_frame_parameter (f, Qbackground_color, arg); if (FRAME_VISIBLE_P (f)) SET_FRAME_GARBAGED (f); + unblock_input (); } static void commit 62f3942b89ca45f083cdff5b0bf3eadb98cf8808 Author: Po Lu Date: Fri Jan 7 19:08:56 2022 +0800 Add documentation for the `none' input style * doc/emacs/xresources.texi (Table of Resources): Document missing value of `inputStyle'. diff --git a/doc/emacs/xresources.texi b/doc/emacs/xresources.texi index 7c5ce101b1..c5dc4e8150 100644 --- a/doc/emacs/xresources.texi +++ b/doc/emacs/xresources.texi @@ -350,6 +350,11 @@ provided by Emacs. Display the preview text inside a popup window at the location of point in the current window. +@item none +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 root Use some location on display specific to the input method for displaying the preview text. commit 4c6d9b921cbc906151072cf5e88efde952fc22d9 Author: Po Lu Date: Fri Jan 7 19:05:42 2022 +0800 ; * doc/lispref/commands.texi (Misc Events): Fix typo. diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index 062b8ea911..3c3b69c64d 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -2141,8 +2141,8 @@ previously displayed. @c FIXME: what is the value of ARG on PGTK? This is a special event (@pxref{Special Events}), which normally should not be bound by the user to any command. Emacs will typically -the text contained in the event behind the cursor on-screen when it is -received. +display the text contained in the event in an overlay behind point +when it is received. @cindex @code{drag-n-drop} event @item (drag-n-drop @var{position} @var{files}) commit 11626f0c0cca1213bfff2cba51479c635befd814 Author: Protesilaos Stavrou Date: Fri Jan 7 11:40:54 2022 +0200 * admin/MAINTAINERS: Describe Modus themes as externally maintained package diff --git a/admin/MAINTAINERS b/admin/MAINTAINERS index 0258eab93d..e87c3e0204 100644 --- a/admin/MAINTAINERS +++ b/admin/MAINTAINERS @@ -309,6 +309,13 @@ Tramp doc/misc/tramp*.texi test/lisp/net/tramp*-tests.el +Modus themes + Maintainer: Protesilaos Stavrou + Repository and issue tracker: https://gitlab.com/protesilaos/modus-themes + + doc/misc/modus-themes.org + etc/themes/modus*.el + ;;; Local Variables: ;;; coding: utf-8 commit f501b572edd3500c07c591962adc4df36148f923 Author: Po Lu Date: Fri Jan 7 17:24:41 2022 +0800 * doc/lispref/display.texi (Face Attributes): Update doc for new values. diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index 1b08eda200..4bc7884e9e 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -2485,8 +2485,9 @@ GNU Emacs Manual}. @item :width Relative character width. This should be one of the symbols @code{ultra-condensed}, @code{extra-condensed}, @code{condensed}, -@code{semi-condensed}, @code{normal}, @code{semi-expanded}, -@code{expanded}, @code{extra-expanded}, or @code{ultra-expanded}. +@code{semi-condensed}, @code{normal}, @code{regular}, @code{medium}, +@code{semi-expanded}, @code{expanded}, @code{extra-expanded}, or +@code{ultra-expanded}. @item :height The height of the font. In the simplest case, this is an integer in commit f041d610083ca0815ab18c589f115bacc523b9bb Author: Po Lu Date: Fri Jan 7 17:23:25 2022 +0800 Improve documentation of the preedit-text event * doc/lispref/commands.texi (Misc Events): Document how `preedit-text' events are used. diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index eb364d2f49..062b8ea911 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -2140,7 +2140,9 @@ cursor. It can be @code{nil}, which means to remove any text previously displayed. @c FIXME: what is the value of ARG on PGTK? This is a special event (@pxref{Special Events}), which normally -should not be bound by the user to any command. +should not be bound by the user to any command. Emacs will typically +the text contained in the event behind the cursor on-screen when it is +received. @cindex @code{drag-n-drop} event @item (drag-n-drop @var{position} @var{files}) commit 259229bf37e9eba39473c3b9f96f51f1accf80fe Author: Po Lu Date: Fri Jan 7 17:22:14 2022 +0800 Disable new input method behaviour by default on X * src/xfns.c (supported_xim_styles): Default to STYLE_NONE. diff --git a/src/xfns.c b/src/xfns.c index dbba374d75..fd3b875244 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -2624,8 +2624,8 @@ xic_free_xfontset (struct frame *f) static const XIMStyle supported_xim_styles[] = { - STYLE_CALLBACK, STYLE_NONE, + STYLE_CALLBACK, STYLE_OVERTHESPOT, STYLE_OFFTHESPOT, STYLE_ROOT commit 1fae01dacf180d77482ac67bba81a476328a53ff Author: Eli Zaretskii Date: Fri Jan 7 10:11:33 2022 +0200 ; * doc/lispref/commands.texi (Misc Events): Fix wording. diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index 855b371cac..eb364d2f49 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -2131,16 +2131,16 @@ size of the object beneath the gesture: image, window, etc. @cindex @code{preedit-text} event @item (preedit-text @var{arg}) -This kind of event is sent when a system input method tells Emacs to -display some text to indicate to the user what will be inserted. The -contents of @var{arg} are dependent on the window system being used. +This event is sent when a system input method tells Emacs to display +some text to indicate to the user what will be inserted. The contents +of @var{arg} are dependent on the window system being used. On X, @var{arg} is a string describing some text to place behind the cursor. It can be @code{nil}, which means to remove any text previously displayed. @c FIXME: what is the value of ARG on PGTK? -It is a special event (@xref{Special Events}), which should normally -not be bound by the user. +This is a special event (@pxref{Special Events}), which normally +should not be bound by the user to any command. @cindex @code{drag-n-drop} event @item (drag-n-drop @var{position} @var{files})