commit b293e254ebe1eaaa3b1866d319c8ee8973e6c2ea (HEAD, refs/remotes/origin/master) Author: Eli Zaretskii Date: Fri Jan 28 09:37:28 2022 +0200 ; Rename 'startup--require-comp-safetly' * src/comp.c (maybe_defer_native_compilation): * lisp/startup.el (startup--require-comp-safely): Rename from 'startup--require-comp-safetly'. All callers changed. diff --git a/lisp/startup.el b/lisp/startup.el index f4216f7c4c..60aaa76002 100644 --- a/lisp/startup.el +++ b/lisp/startup.el @@ -521,7 +521,7 @@ DIRS are relative." (defvar comp--loadable) (defvar comp--delayed-sources) -(defun startup--require-comp-safetly () +(defun startup--require-comp-safely () "Require the native compiler avoiding circular dependencies." (unless (featurep 'comp) ;; Require comp with `comp--loadable' set to nil to break @@ -537,7 +537,7 @@ DIRS are relative." "Honor pending delayed deferred native compilations." (when (and (native-comp-available-p) comp--delayed-sources) - (startup--require-comp-safetly)) + (startup--require-comp-safely)) (setq comp--loadable t)) (defvar native-comp-eln-load-path) diff --git a/src/comp.c b/src/comp.c index 66288988fd..56e583eb5c 100644 --- a/src/comp.c +++ b/src/comp.c @@ -5132,7 +5132,7 @@ maybe_defer_native_compilation (Lisp_Object function_name, if (comp__loadable) { /* Startup is done, comp is usable. */ - CALL0I(startup--require-comp-safetly); + CALL0I (startup--require-comp-safely); Fputhash (function_name, definition, Vcomp_deferred_pending_h); CALLN (Ffuncall, intern_c_string ("native--compile-async"), src, Qnil, Qlate); commit 241f2857f1ce3732102ba77a98727406fb93edb0 Author: Eli Zaretskii Date: Fri Jan 28 09:30:04 2022 +0200 ; Fix a typo in recent documentation changes * doc/lispref/os.texi (Startup Summary): * doc/emacs/cmdargs.texi (Initial Options): Fix a typo. diff --git a/doc/emacs/cmdargs.texi b/doc/emacs/cmdargs.texi index da9947ece3..de1d5e0b2c 100644 --- a/doc/emacs/cmdargs.texi +++ b/doc/emacs/cmdargs.texi @@ -331,7 +331,7 @@ Do not include the @file{site-lisp} directories in @code{load-path} @item --init-directory @opindex --init-directory -Specify the directory to use when looking for the Emacs init file. +Specify the directory to use when looking for the Emacs init files. @item --no-splash @opindex --no-splash diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi index 25a2b9e2e4..9cb9bc75d0 100644 --- a/doc/lispref/os.texi +++ b/doc/lispref/os.texi @@ -365,7 +365,7 @@ Equivalent to @samp{-q --no-site-file --no-splash}. @c and --no-site-lisp, but let's not mention that here. @item --init-directory -Specify the directory to use when finding the Emacs init file. +Specify the directory to use when finding the Emacs init files. @end table commit 84d4a349190fc51bf333c6fb60ab36be39a6a678 Author: Po Lu Date: Fri Jan 28 14:06:38 2022 +0800 Fix duplicate scroll events without XI2 * src/xwidget.c (xwidget_button): Only generate scroll events when the button was released. diff --git a/src/xwidget.c b/src/xwidget.c index 2080f109cd..75e909f0e6 100644 --- a/src/xwidget.c +++ b/src/xwidget.c @@ -1295,45 +1295,48 @@ xwidget_button (struct xwidget_view *view, || FRAME_DISPLAY_INFO (view->frame)->xi2_version < 1) #endif { - GdkEvent *xg_event = gdk_event_new (GDK_SCROLL); - struct xwidget *model = XXWIDGET (view->model); - GtkWidget *target; - - x += view->clip_left; - y += view->clip_top; - - target = find_widget_at_pos (model->widgetwindow_osr, x, y, &x, &y, - true, view); - - if (!target) - target = model->widget_osr; - - xg_event->any.window = gtk_widget_get_window (target); - g_object_ref (xg_event->any.window); /* The window will be unrefed - later by gdk_event_free. */ - if (button == 4) - xg_event->scroll.direction = GDK_SCROLL_UP; - else if (button == 5) - xg_event->scroll.direction = GDK_SCROLL_DOWN; - else if (button == 6) - xg_event->scroll.direction = GDK_SCROLL_LEFT; - else - xg_event->scroll.direction = GDK_SCROLL_RIGHT; + if (!down_p) + { + GdkEvent *xg_event = gdk_event_new (GDK_SCROLL); + struct xwidget *model = XXWIDGET (view->model); + GtkWidget *target; + + x += view->clip_left; + y += view->clip_top; + + target = find_widget_at_pos (model->widgetwindow_osr, x, y, &x, &y, + true, view); + + if (!target) + target = model->widget_osr; + + xg_event->any.window = gtk_widget_get_window (target); + g_object_ref (xg_event->any.window); /* The window will be unrefed + later by gdk_event_free. */ + if (button == 4) + xg_event->scroll.direction = GDK_SCROLL_UP; + else if (button == 5) + xg_event->scroll.direction = GDK_SCROLL_DOWN; + else if (button == 6) + xg_event->scroll.direction = GDK_SCROLL_LEFT; + else + xg_event->scroll.direction = GDK_SCROLL_RIGHT; - xg_event->scroll.device = find_suitable_pointer (view->frame); + xg_event->scroll.device = find_suitable_pointer (view->frame); - xg_event->scroll.x = x; - xg_event->scroll.x_root = x; - xg_event->scroll.y = y; - xg_event->scroll.y_root = y; - xg_event->scroll.state = modifier_state; - xg_event->scroll.time = time; + xg_event->scroll.x = x; + xg_event->scroll.x_root = x; + xg_event->scroll.y = y; + xg_event->scroll.y_root = y; + xg_event->scroll.state = modifier_state; + xg_event->scroll.time = time; - xg_event->scroll.delta_x = 0; - xg_event->scroll.delta_y = 0; + xg_event->scroll.delta_x = 0; + xg_event->scroll.delta_y = 0; - gtk_main_do_event (xg_event); - gdk_event_free (xg_event); + gtk_main_do_event (xg_event); + gdk_event_free (xg_event); + } } } commit 9d0833f4083a9cac35dbcdb24b045f4ef9afeb98 Merge: 2f0d67b047 3dd47bde7a Author: Stefan Kangas Date: Fri Jan 28 06:32:22 2022 +0100 Merge from origin/emacs-28 3dd47bde7a * lisp/frame.el (clone-frame): Filter out 'parent-id' (bug... commit 2f0d67b047d6d84d51dd388fe19d46e1d2b72f9c Author: Po Lu Date: Fri Jan 28 11:08:20 2022 +0800 Clear xwidget passive grab whenever a drag begins * src/xwidget.c (xv_drag_begin_cb): New function. (xwidget_view_from_window): Attach said function to grab widget when a grab starts. * src/xwidget.h (struct xwidget_view): New field `passive_grab_drag_signal'. diff --git a/src/xwidget.c b/src/xwidget.c index c5f4c0ee94..2080f109cd 100644 --- a/src/xwidget.c +++ b/src/xwidget.c @@ -1138,6 +1138,23 @@ run_file_chooser_cb (WebKitWebView *webview, #ifdef HAVE_X_WINDOWS +static void +xv_drag_begin_cb (GtkWidget *widget, + GdkDragContext *context, + gpointer user_data) +{ + struct xwidget_view *view = user_data; + + if (view->passive_grab) + { + g_signal_handler_disconnect (view->passive_grab, + view->passive_grab_destruction_signal); + g_signal_handler_disconnect (view->passive_grab, + view->passive_grab_drag_signal); + view->passive_grab = NULL; + } +} + static void xwidget_button_1 (struct xwidget_view *view, bool down_p, int x, int y, int button, @@ -1170,6 +1187,10 @@ xwidget_button_1 (struct xwidget_view *view, = g_signal_connect (G_OBJECT (view->passive_grab), "destroy", G_CALLBACK (gtk_widget_destroyed), &view->passive_grab); + view->passive_grab_drag_signal + = g_signal_connect (G_OBJECT (view->passive_grab), + "drag-begin", G_CALLBACK (xv_drag_begin_cb), + view); } else { @@ -1230,6 +1251,8 @@ xwidget_button_1 (struct xwidget_view *view, { g_signal_handler_disconnect (view->passive_grab, view->passive_grab_destruction_signal); + g_signal_handler_disconnect (view->passive_grab, + view->passive_grab_drag_signal); view->passive_grab = NULL; } } @@ -3205,6 +3228,8 @@ DEFUN ("delete-xwidget-view", { g_signal_handler_disconnect (xv->passive_grab, xv->passive_grab_destruction_signal); + g_signal_handler_disconnect (xv->passive_grab, + xv->passive_grab_drag_signal); xv->passive_grab = NULL; } diff --git a/src/xwidget.h b/src/xwidget.h index 79dee37695..8b47a0b904 100644 --- a/src/xwidget.h +++ b/src/xwidget.h @@ -122,6 +122,7 @@ struct xwidget_view GdkWindow *last_crossing_window; GtkWidget *passive_grab; guint passive_grab_destruction_signal; + guint passive_grab_drag_signal; #else struct pgtk_display_info *dpyinfo; GtkWidget *widget; commit bc404cd8ea911917f56661d16a96c4da15a3ffbe Author: Po Lu Date: Fri Jan 28 10:51:33 2022 +0800 * src/xterm.c (handle_one_xevent): Remove obsolete workaround. diff --git a/src/xterm.c b/src/xterm.c index 30a3aee20e..3f277c5b87 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -10395,14 +10395,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, #ifdef HAVE_XWIDGETS if (xwidget_view) { - /* Don't send an enter event to the xwidget if the - first button is pressed, to avoid it releasing - the passive grab. I don't know why that happens, - but this workaround makes dragging to select text - work again. */ - if (!(enter->buttons.mask_len - && XIMaskIsSet (enter->buttons.mask, 1))) - xwidget_motion_or_crossing (xwidget_view, event); + xwidget_motion_or_crossing (xwidget_view, event); goto XI_OTHER; } commit 47b7ec2a3d05bccfc710c9e0d0fc8278ed20afd8 Author: Po Lu Date: Fri Jan 28 10:48:12 2022 +0800 Fix inappropriate use of killed xwidgets * src/xwidget.c (x_draw_xwidget_webkit_view): Don't record embedder or synthesize focus events if the widget is killed. diff --git a/src/xwidget.c b/src/xwidget.c index 0a85faf20c..c5f4c0ee94 100644 --- a/src/xwidget.c +++ b/src/xwidget.c @@ -2844,8 +2844,11 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) #endif #if defined HAVE_XINPUT2 || defined HAVE_PGTK - record_osr_embedder (xv); - synthesize_focus_in_event (xww->widget_osr); + if (!NILP (xww->buffer)) + { + record_osr_embedder (xv); + synthesize_focus_in_event (xww->widget_osr); + } #endif #ifdef USE_GTK commit 8471c6f06c611dc9853eeb9508dff9844c75df6e Author: Po Lu Date: Fri Jan 28 10:41:03 2022 +0800 Implement xwidget passive grabs * src/xwidget.c (find_widget_at_pos): New parameters for controlling whether to respect grabs. All callers changed. (window_coords_from_toplevel): Make work when the widget is the toplevel. (find_widget): Fix coding style. (xwidget_button_1): Set and clear passive grabs if appropriate. (xw_maybe_synthesize_crossing): Allow current_window to be NULL if the mode is XW_CROSSING_LEFT. diff --git a/src/xwidget.c b/src/xwidget.c index 175a289a00..0a85faf20c 100644 --- a/src/xwidget.c +++ b/src/xwidget.c @@ -107,7 +107,8 @@ webkit_decide_policy_cb (WebKitWebView *, WebKitPolicyDecision *, WebKitPolicyDecisionType, gpointer); -static GtkWidget *find_widget_at_pos (GtkWidget *, int, int, int *, int *); +static GtkWidget *find_widget_at_pos (GtkWidget *, int, int, int *, int *, bool, + struct xwidget_view *); static gboolean run_file_chooser_cb (WebKitWebView *, WebKitFileChooserRequest *, gpointer); @@ -264,6 +265,11 @@ xw_translate_x_modifiers (struct x_display_info *dpyinfo, static bool xw_maybe_synthesize_crossing (struct xwidget_view *, GdkWindow *, int, int, int, Time, unsigned int); +static void xw_notify_virtual_upwards_until (struct xwidget_view *, GdkWindow *, + GdkWindow *, GdkWindow *, unsigned int, + int, int, Time, GdkEventType, bool); +static void window_coords_from_toplevel (GdkWindow *, GdkWindow *, int, + int, int *, int *); #endif DEFUN ("make-xwidget", @@ -721,7 +727,7 @@ pick_embedded_child (GdkWindow *window, double x, double y, return NULL; child = find_widget_at_pos (widget, lrint (x), lrint (y), - &xout, &yout); + &xout, &yout, false, NULL); if (!child) return NULL; @@ -930,9 +936,9 @@ find_widget (GtkWidget *widget, } } - if ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) && - (data->x < new_allocation.x + new_allocation.width) && - (data->y < new_allocation.y + new_allocation.height)) + if ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) + && (data->x < new_allocation.x + new_allocation.width) + && (data->y < new_allocation.y + new_allocation.height)) { /* First, check if the drag is in a valid drop site in one of our children. */ @@ -966,9 +972,27 @@ find_widget (GtkWidget *widget, static GtkWidget * find_widget_at_pos (GtkWidget *w, int x, int y, - int *new_x, int *new_y) + int *new_x, int *new_y, + bool pointer_grabs, + struct xwidget_view *vw) { struct widget_search_data data; +#ifdef HAVE_X_WINDOWS + GtkWidget *grab = NULL; + + if (pointer_grabs) + { + grab = vw->passive_grab; + + if (grab && gtk_widget_get_window (grab)) + { + gtk_widget_translate_coordinates (w, grab, x, + y, new_x, new_y); + + return grab; + } + } +#endif data.x = x; data.y = y; @@ -1119,19 +1143,99 @@ xwidget_button_1 (struct xwidget_view *view, bool down_p, int x, int y, int button, int modifier_state, Time time) { - GdkEvent *xg_event = gdk_event_new (down_p ? GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE); + GdkEvent *xg_event; struct xwidget *model = XXWIDGET (view->model); GtkWidget *target; + GtkWidget *ungrab_target; + GdkWindow *toplevel, *target_window; + int view_x, view_y; /* X and Y should be relative to the origin of view->wdesc. */ x += view->clip_left; y += view->clip_top; - target = find_widget_at_pos (model->widgetwindow_osr, x, y, &x, &y); + view_x = x; + view_y = y; + + target = find_widget_at_pos (model->widgetwindow_osr, x, y, &x, &y, + true, view); if (!target) target = model->widget_osr; + if (down_p) + { + view->passive_grab = target; + view->passive_grab_destruction_signal + = g_signal_connect (G_OBJECT (view->passive_grab), + "destroy", G_CALLBACK (gtk_widget_destroyed), + &view->passive_grab); + } + else + { + ungrab_target = find_widget_at_pos (model->widgetwindow_osr, + view_x, view_y, &x, &y, + false, NULL); + + if (view->last_crossing_window && ungrab_target) + { + xw_maybe_synthesize_crossing (view, gtk_widget_get_window (ungrab_target), + view_x, view_y, XW_CROSSING_NONE, + time, modifier_state); + } + else + { + toplevel = gtk_widget_get_window (model->widgetwindow_osr); + xg_event = gdk_event_new (GDK_LEAVE_NOTIFY); + target_window = gtk_widget_get_window (target); + window_coords_from_toplevel (target_window, toplevel, view_x, + view_y, &x, &y); + + xg_event->crossing.x = x; + xg_event->crossing.y = y; + xg_event->crossing.time = time; + xg_event->crossing.focus = FALSE; + xg_event->crossing.detail = GDK_NOTIFY_ANCESTOR; + xg_event->crossing.mode = GDK_CROSSING_UNGRAB; + xg_event->crossing.window = g_object_ref (target_window); + gdk_event_set_device (xg_event, find_suitable_pointer (view->frame)); + + gtk_main_do_event (xg_event); + gdk_event_free (xg_event); + + xw_notify_virtual_upwards_until (view, target_window, toplevel, toplevel, + modifier_state, view_x, view_y, time, + GDK_LEAVE_NOTIFY, false); + + if (target_window != toplevel) + { + xg_event = gdk_event_new (GDK_LEAVE_NOTIFY); + + xg_event->crossing.x = view_y; + xg_event->crossing.y = view_y; + xg_event->crossing.time = time; + xg_event->crossing.focus = FALSE; + xg_event->crossing.detail = GDK_NOTIFY_VIRTUAL; + xg_event->crossing.mode = GDK_CROSSING_UNGRAB; + xg_event->crossing.window = g_object_ref (toplevel); + + gdk_event_set_device (xg_event, find_suitable_pointer (view->frame)); + gtk_main_do_event (xg_event); + gdk_event_free (xg_event); + } + + } + + if (view->passive_grab) + { + g_signal_handler_disconnect (view->passive_grab, + view->passive_grab_destruction_signal); + view->passive_grab = NULL; + } + } + + xg_event = gdk_event_new (down_p ? GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE); + xg_event->any.window = gtk_widget_get_window (target); g_object_ref (xg_event->any.window); /* The window will be unrefed later by gdk_event_free. */ @@ -1175,7 +1279,8 @@ xwidget_button (struct xwidget_view *view, x += view->clip_left; y += view->clip_top; - target = find_widget_at_pos (model->widgetwindow_osr, x, y, &x, &y); + target = find_widget_at_pos (model->widgetwindow_osr, x, y, &x, &y, + true, view); if (!target) target = model->widget_osr; @@ -1229,7 +1334,8 @@ xwidget_motion_notify (struct xwidget_view *view, target = find_widget_at_pos (model->widgetwindow_osr, lrint (x + view->clip_left), lrint (y + view->clip_top), - &target_x, &target_y); + &target_x, &target_y, + true, view); if (!target) { @@ -1276,7 +1382,8 @@ xwidget_scroll (struct xwidget_view *view, double x, double y, target = find_widget_at_pos (model->widgetwindow_osr, lrint (x + view->clip_left), lrint (y + view->clip_top), - &target_x, &target_y); + &target_x, &target_y, + true, view); if (!target) { @@ -1325,7 +1432,8 @@ xwidget_pinch (struct xwidget_view *view, XIGesturePinchEvent *xev) target = find_widget_at_pos (model->widgetwindow_osr, lrint (x + view->clip_left), lrint (y + view->clip_top), - &target_x, &target_y); + &target_x, &target_y, + true, view); if (!target) { @@ -1400,6 +1508,13 @@ window_coords_from_toplevel (GdkWindow *window, GdkWindow *toplevel, GList *children, *l; gdouble x_out, y_out; + if (window == toplevel) + { + *out_x = x; + *out_y = y; + return; + } + children = NULL; while ((parent = gdk_window_get_parent (window)) != toplevel) { @@ -1573,13 +1688,16 @@ xw_maybe_synthesize_crossing (struct xwidget_view *view, if (!last_crossing) { - view->last_crossing_window = g_object_ref (current_window); + if (current_window) + { + view->last_crossing_window = g_object_ref (current_window); - xw_notify_virtual_downwards_until (view, current_window, - toplevel, toplevel, - state, x, y, time, - GDK_ENTER_NOTIFY, - false); + xw_notify_virtual_downwards_until (view, current_window, + toplevel, toplevel, + state, x, y, time, + GDK_ENTER_NOTIFY, + false); + } return false; } @@ -1686,7 +1804,8 @@ xwidget_motion_or_crossing (struct xwidget_view *view, const XEvent *event) ? event->xmotion.y + view->clip_top : event->xcrossing.y + view->clip_top); target = find_widget_at_pos (model->widgetwindow_osr, - toplevel_x, toplevel_y, &x, &y); + toplevel_x, toplevel_y, &x, &y, + true, view); } #ifdef HAVE_XINPUT2 else @@ -1703,7 +1822,7 @@ xwidget_motion_or_crossing (struct xwidget_view *view, const XEvent *event) = lrint (xev->event_x + view->clip_left)), (toplevel_y = lrint (xev->event_y + view->clip_top)), - &x, &y); + &x, &y, true, view); } #endif @@ -1759,12 +1878,13 @@ xwidget_motion_or_crossing (struct xwidget_view *view, const XEvent *event) xg_event->crossing.state |= GDK_BUTTON3_MASK; } - if (xw_maybe_synthesize_crossing (view, xg_event->any.window, - toplevel_x, toplevel_y, - (xev->type == XI_Enter - ? XW_CROSSING_ENTERED - : XW_CROSSING_LEFT), - xev->time, xg_event->crossing.state)) + if (view->passive_grab + || xw_maybe_synthesize_crossing (view, xg_event->any.window, + toplevel_x, toplevel_y, + (xev->type == XI_Enter + ? XW_CROSSING_ENTERED + : XW_CROSSING_LEFT), + xev->time, xg_event->crossing.state)) { gdk_event_free (xg_event); return; @@ -1775,13 +1895,14 @@ xwidget_motion_or_crossing (struct xwidget_view *view, const XEvent *event) #endif else { - if (xw_maybe_synthesize_crossing (view, xg_event->any.window, - toplevel_x, toplevel_y, - (event->type == EnterNotify - ? XW_CROSSING_ENTERED - : XW_CROSSING_LEFT), - event->xcrossing.time, - event->xcrossing.state)) + if (view->passive_grab + || xw_maybe_synthesize_crossing (view, xg_event->any.window, + toplevel_x, toplevel_y, + (event->type == EnterNotify + ? XW_CROSSING_ENTERED + : XW_CROSSING_LEFT), + event->xcrossing.time, + event->xcrossing.state)) { gdk_event_free (xg_event); return; @@ -2450,6 +2571,7 @@ xwidget_init_view (struct xwidget *xww, xv->cursor = cursor_for_hit (xww->hit_result, s->f); xv->just_resized = false; xv->last_crossing_window = NULL; + xv->passive_grab = NULL; #elif defined HAVE_PGTK xv->dpyinfo = FRAME_DISPLAY_INFO (s->f); xv->widget = gtk_drawing_area_new (); @@ -3075,6 +3197,14 @@ DEFUN ("delete-xwidget-view", g_clear_pointer (&xv->last_crossing_window, g_object_unref); + + if (xv->passive_grab) + { + g_signal_handler_disconnect (xv->passive_grab, + xv->passive_grab_destruction_signal); + xv->passive_grab = NULL; + } + #else gtk_widget_destroy (xv->widget); #endif diff --git a/src/xwidget.h b/src/xwidget.h index 5f05a5f59d..79dee37695 100644 --- a/src/xwidget.h +++ b/src/xwidget.h @@ -120,6 +120,8 @@ struct xwidget_view Window wdesc; GdkWindow *last_crossing_window; + GtkWidget *passive_grab; + guint passive_grab_destruction_signal; #else struct pgtk_display_info *dpyinfo; GtkWidget *widget; commit 8eaf04de83fd967c2ab69a4c1dfe44a6a10aa912 Author: Lars Ingebrigtsen Date: Thu Jan 27 23:38:13 2022 +0100 Add new switch --init-directory * doc/emacs/cmdargs.texi (Initial Options): Mention it. * lisp/startup.el (normal-top-level): Move the eln init to after we've processed the command line arguments. (command-line): Interpret the --init-directory switch. * src/emacs.c (standard_args): Add. diff --git a/doc/emacs/cmdargs.texi b/doc/emacs/cmdargs.texi index 5c444fc648..da9947ece3 100644 --- a/doc/emacs/cmdargs.texi +++ b/doc/emacs/cmdargs.texi @@ -329,6 +329,10 @@ option does this too, but other options like @samp{-q} do not. Do not include the @file{site-lisp} directories in @code{load-path} (@pxref{Init File}). The @samp{-Q} option does this too. +@item --init-directory +@opindex --init-directory +Specify the directory to use when looking for the Emacs init file. + @item --no-splash @opindex --no-splash @cindex splash screen diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi index 3750abc4e8..25a2b9e2e4 100644 --- a/doc/lispref/os.texi +++ b/doc/lispref/os.texi @@ -363,6 +363,9 @@ Do not load the @file{site-start} library. @itemx -Q Equivalent to @samp{-q --no-site-file --no-splash}. @c and --no-site-lisp, but let's not mention that here. + +@item --init-directory +Specify the directory to use when finding the Emacs init file. @end table diff --git a/etc/NEWS b/etc/NEWS index abef1019ac..d1eaf08036 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -79,6 +79,9 @@ as was already the case for all the non-preloaded files. * Startup Changes in Emacs 29.1 ++++ +** Emacs now support setting 'user-emacs-directory' via --init-directory. + +++ ** Emacs now has a '--fingerprint' option. This will output a string identifying the current Emacs build. diff --git a/lisp/startup.el b/lisp/startup.el index 856d600e38..f4216f7c4c 100644 --- a/lisp/startup.el +++ b/lisp/startup.el @@ -558,26 +558,6 @@ It is the default value of the variable `top-level'." (setq user-emacs-directory (startup--xdg-or-homedot startup--xdg-config-home-emacs nil)) - (when (featurep 'native-compile) - ;; Form `native-comp-eln-load-path'. - (let ((path-env (getenv "EMACSNATIVELOADPATH"))) - (when path-env - (dolist (path (split-string path-env path-separator)) - (unless (string= "" path) - (push path native-comp-eln-load-path))))) - (push (expand-file-name "eln-cache/" user-emacs-directory) - native-comp-eln-load-path) - ;; When $HOME is set to '/nonexistent' means we are running the - ;; testsuite, add a temporary folder in front to produce there - ;; new compilations. - (when (and (equal (getenv "HOME") "/nonexistent") - ;; We may be running in a chroot environment where we - ;; can't write anything. - (file-writable-p (expand-file-name - (or temporary-file-directory "")))) - (let ((tmp-dir (make-temp-file "emacs-testsuite-" t))) - (add-hook 'kill-emacs-hook (lambda () (delete-directory tmp-dir t))) - (push tmp-dir native-comp-eln-load-path)))) ;; Look in each dir in load-path for a subdirs.el file. If we ;; find one, load it, which will add the appropriate subdirs of ;; that dir into load-path. This needs to be done before setting @@ -664,16 +644,6 @@ It is the default value of the variable `top-level'." (set pathsym (mapcar (lambda (dir) (decode-coding-string dir coding t)) path))))) - (when (featurep 'native-compile) - (let ((npath (symbol-value 'native-comp-eln-load-path))) - (set 'native-comp-eln-load-path - (mapcar (lambda (dir) - ;; Call expand-file-name to remove all the - ;; pesky ".." from the directyory names in - ;; native-comp-eln-load-path. - (expand-file-name - (decode-coding-string dir coding t))) - npath)))) (dolist (filesym '(data-directory doc-directory exec-directory installation-directory invocation-directory invocation-name @@ -789,6 +759,45 @@ It is the default value of the variable `top-level'." (font-menu-add-default)) (unless inhibit-startup-hooks (run-hooks 'window-setup-hook)))) + + ;; Do this after `command-line', since it may alter + ;; `user-emacs-directory'. + (when (featurep 'native-compile) + ;; Form `native-comp-eln-load-path'. + (let ((path-env (getenv "EMACSNATIVELOADPATH"))) + (when path-env + (dolist (path (split-string path-env path-separator)) + (unless (string= "" path) + (push path native-comp-eln-load-path))))) + (push (expand-file-name "eln-cache/" user-emacs-directory) + native-comp-eln-load-path) + ;; When $HOME is set to '/nonexistent' means we are running the + ;; testsuite, add a temporary folder in front to produce there + ;; new compilations. + (when (and (equal (getenv "HOME") "/nonexistent") + ;; We may be running in a chroot environment where we + ;; can't write anything. + (file-writable-p (expand-file-name + (or temporary-file-directory "")))) + (let ((tmp-dir (make-temp-file "emacs-testsuite-" t))) + (add-hook 'kill-emacs-hook (lambda () (delete-directory tmp-dir t))) + (push tmp-dir native-comp-eln-load-path))) + (when locale-coding-system + (let ((coding (if (eq system-type 'windows-nt) + ;; MS-Windows build converts all file names to + ;; UTF-8 during startup. + 'utf-8 + locale-coding-system)) + (npath (symbol-value 'native-comp-eln-load-path))) + (set 'native-comp-eln-load-path + (mapcar (lambda (dir) + ;; Call expand-file-name to remove all the + ;; pesky ".." from the directyory names in + ;; native-comp-eln-load-path. + (expand-file-name + (decode-coding-string dir coding t))) + npath))))) + ;; Subprocesses of Emacs do not have direct access to the terminal, so ;; unless told otherwise they should only assume a dumb terminal. ;; We are careful to do it late (after term-setup-hook), although the @@ -1153,7 +1162,8 @@ please check its value") ("--no-x-resources") ("--debug-init") ("--user") ("--iconic") ("--icon-type") ("--quick") ("--no-blinking-cursor") ("--basic-display") - ("--dump-file") ("--temacs") ("--seccomp"))) + ("--dump-file") ("--temacs") ("--seccomp") + ("--init-directory"))) (argi (pop args)) (orig-argi argi) argval) @@ -1193,6 +1203,9 @@ please check its value") (push '(vertical-scroll-bars . nil) initial-frame-alist)) ((member argi '("-q" "-no-init-file")) (setq init-file-user nil)) + ((member argi '("-init-directory")) + (setq user-emacs-directory (or argval (pop args)) + argval nil)) ((member argi '("-u" "-user")) (setq init-file-user (or argval (pop args)) argval nil)) @@ -1269,7 +1282,8 @@ please check its value") (and (eq xdg-dir user-emacs-directory) (not (eq xdg-dir startup--xdg-config-default)))) user-emacs-directory - ;; The name is not obvious, so access more directories to calculate it. + ;; The name is not obvious, so access more directories + ;; to calculate it. (setq xdg-dir (concat "~" init-file-user "/.config/emacs/")) (startup--xdg-or-homedot xdg-dir init-file-user))) diff --git a/src/emacs.c b/src/emacs.c index f6e2c01ee7..2014e97fbf 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -2472,6 +2472,7 @@ static const struct standard_args standard_args[] = { "-quick", 0, 55, 0 }, { "-q", "--no-init-file", 50, 0 }, { "-no-init-file", 0, 50, 0 }, + { "-init-directory", "--init-directory", 30, 1 }, { "-no-x-resources", "--no-x-resources", 40, 0 }, { "-no-site-file", "--no-site-file", 40, 0 }, { "-u", "--user", 30, 1 }, commit 53ebc1fa21f068862753de6cb6a8eb4ad71d8a40 Author: Glenn Morris Date: Thu Jan 27 14:21:15 2022 -0800 * lisp/doc-view.el (doc-view-epub-font-size): Fix type; add version. Flagged by test-custom-opts. diff --git a/lisp/doc-view.el b/lisp/doc-view.el index ee6aa05517..b222096ac6 100644 --- a/lisp/doc-view.el +++ b/lisp/doc-view.el @@ -254,8 +254,9 @@ Can be `dvi', `pdf', `ps', `djvu', `odf', 'epub', `cbz', `fb2', (defcustom doc-view-epub-font-size nil "Font size in points for EPUB layout." - :type 'integer - :set #'doc-view-custom-set-epub-font-size) + :type '(choice (const nil) integer) + :set #'doc-view-custom-set-epub-font-size + :version "29.1") (defcustom doc-view-scale-internally t "Whether we should try to rescale images ourselves. commit 536a57b72ce11b1bb8d1b34b339424fea6ccbcce Author: Andrea Corallo Date: Thu Jan 27 18:18:13 2022 +0100 Fix potential native compiler circular dependencies during load * lisp/startup.el (startup--require-comp-safetly): New function. (startup--honor-delayed-native-compilations): Make use of `startup--require-comp-safetly'. * src/comp.c (CALL0I): New define. (maybe_defer_native_compilation): Make use of `startup--require-comp-safetly'. diff --git a/lisp/startup.el b/lisp/startup.el index 05d829396d..856d600e38 100644 --- a/lisp/startup.el +++ b/lisp/startup.el @@ -519,22 +519,26 @@ DIRS are relative." xdg-dir) (t emacs-d-dir)))) -(defvar comp--delayed-sources) (defvar comp--loadable) +(defvar comp--delayed-sources) +(defun startup--require-comp-safetly () + "Require the native compiler avoiding circular dependencies." + (unless (featurep 'comp) + ;; Require comp with `comp--loadable' set to nil to break + ;; circularity. + (let ((comp--loadable nil)) + (require 'comp)) + (native--compile-async comp--delayed-sources nil 'late) + (setq comp--delayed-sources nil))) + (declare-function native--compile-async "comp.el" (files &optional recursively load selector)) (defun startup--honor-delayed-native-compilations () "Honor pending delayed deferred native compilations." - (if (and (native-comp-available-p) - comp--delayed-sources) - (progn - ;; Require comp before setting `comp--loadable' to break - ;; circularity. - (require 'comp) - (setq comp--loadable t) - (native--compile-async comp--delayed-sources nil 'late) - (setq comp--delayed-sources nil)) - (setq comp--loadable t))) + (when (and (native-comp-available-p) + comp--delayed-sources) + (startup--require-comp-safetly)) + (setq comp--loadable t)) (defvar native-comp-eln-load-path) (defun normal-top-level () diff --git a/src/comp.c b/src/comp.c index d755df802f..66288988fd 100644 --- a/src/comp.c +++ b/src/comp.c @@ -480,6 +480,10 @@ load_gccjit_if_necessary (bool mandatory) #define THIRD(x) \ XCAR (XCDR (XCDR (x))) +/* Like call0 but stringify and intern. */ +#define CALL0I(fun) \ + CALLN (Ffuncall, intern_c_string (STR (fun))) + /* Like call1 but stringify and intern. */ #define CALL1I(fun, arg) \ CALLN (Ffuncall, intern_c_string (STR (fun)), arg) @@ -5128,7 +5132,7 @@ maybe_defer_native_compilation (Lisp_Object function_name, if (comp__loadable) { /* Startup is done, comp is usable. */ - Frequire (Qcomp, Qnil, Qnil); + CALL0I(startup--require-comp-safetly); Fputhash (function_name, definition, Vcomp_deferred_pending_h); CALLN (Ffuncall, intern_c_string ("native--compile-async"), src, Qnil, Qlate); commit ce220524fcfcd0b51cb13cd2c15470f1b6ea0baf Author: Lars Ingebrigtsen Date: Thu Jan 27 22:26:12 2022 +0100 Fix up previous "Quit Emacs" from menu logic * lisp/files.el (files--buffers-needing-to-be-saved): Separated out into its own function... (save-some-buffers): ... from here. (save-buffers-kill-emacs): Check that we have anything to save before prompting the user. diff --git a/lisp/files.el b/lisp/files.el index 9e8afc8d52..247579efb4 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -5816,6 +5816,27 @@ of the directory that was default during command invocation." (lambda () (file-in-directory-p default-directory root)))) (put 'save-some-buffers-root 'save-some-buffers-function t) +(defun files--buffers-needing-to-be-saved (pred) + "Return a list of buffers to save according to PRED. +See `save-some-buffers' for PRED values." + (seq-filter + (lambda (buffer) + ;; Note that killing some buffers may kill others via + ;; hooks (e.g. Rmail and its viewing buffer). + (and (buffer-live-p buffer) + (buffer-modified-p buffer) + (not (buffer-base-buffer buffer)) + (or + (buffer-file-name buffer) + (with-current-buffer buffer + (or (eq buffer-offer-save 'always) + (and pred buffer-offer-save + (> (buffer-size) 0))))) + (or (not (functionp pred)) + (with-current-buffer buffer + (funcall pred))))) + (buffer-list))) + (defun save-some-buffers (&optional arg pred) "Save some modified file-visiting buffers. Asks user about each one. You can answer \\`y' or \\`SPC' to save, \\`n' or \\`DEL' not to save, \\`C-r' @@ -5872,49 +5893,36 @@ change the additional actions you can take on files." (setq files-done (map-y-or-n-p (lambda (buffer) - ;; Note that killing some buffers may kill others via - ;; hooks (e.g. Rmail and its viewing buffer). - (and (buffer-live-p buffer) - (buffer-modified-p buffer) - (not (buffer-base-buffer buffer)) - (or - (buffer-file-name buffer) - (with-current-buffer buffer - (or (eq buffer-offer-save 'always) - (and pred buffer-offer-save - (> (buffer-size) 0))))) - (or (not (functionp pred)) - (with-current-buffer buffer (funcall pred))) - (if arg - t - (setq queried t) - (if (buffer-file-name buffer) - (if (or - (equal (buffer-name buffer) - (file-name-nondirectory - (buffer-file-name buffer))) - (string-match - (concat "\\<" - (regexp-quote - (file-name-nondirectory - (buffer-file-name buffer))) - "<[^>]*>\\'") - (buffer-name buffer))) - ;; The buffer name is similar to the - ;; file name. - (format "Save file %s? " - (buffer-file-name buffer)) - ;; The buffer and file names are - ;; dissimilar; display both. - (format "Save file %s (buffer %s)? " - (buffer-file-name buffer) - (buffer-name buffer))) - ;; No file name - (format "Save buffer %s? " (buffer-name buffer)))))) + (if arg + t + (setq queried t) + (if (buffer-file-name buffer) + (if (or + (equal (buffer-name buffer) + (file-name-nondirectory + (buffer-file-name buffer))) + (string-match + (concat "\\<" + (regexp-quote + (file-name-nondirectory + (buffer-file-name buffer))) + "<[^>]*>\\'") + (buffer-name buffer))) + ;; The buffer name is similar to the file + ;; name. + (format "Save file %s? " + (buffer-file-name buffer)) + ;; The buffer and file names are dissimilar; + ;; display both. + (format "Save file %s (buffer %s)? " + (buffer-file-name buffer) + (buffer-name buffer))) + ;; No file name. + (format "Save buffer %s? " (buffer-name buffer))))) (lambda (buffer) (with-current-buffer buffer (save-buffer))) - (buffer-list) + (files--buffers-needing-to-be-saved pred) '("buffer" "buffers" "save") save-some-buffers-action-alist)) ;; Maybe to save abbrevs, and record whether @@ -7752,15 +7760,16 @@ if any returns nil. If `confirm-kill-emacs' is non-nil, calls it." (interactive "P") ;; Don't use save-some-buffers-default-predicate, because we want ;; to ask about all the buffers before killing Emacs. - (if (use-dialog-box-p) - (pcase (x-popup-dialog - t `("Unsaved Buffers" - ("Close Without Saving" . no-save) - ("Save All" . save-all) - ("Cancel" . cancel))) - ('cancel (user-error "Exit cancelled")) - ('save-all (save-some-buffers t))) - (save-some-buffers arg t)) + (when (files--buffers-needing-to-be-saved t) + (if (use-dialog-box-p) + (pcase (x-popup-dialog + t `("Unsaved Buffers" + ("Close Without Saving" . no-save) + ("Save All" . save-all) + ("Cancel" . cancel))) + ('cancel (user-error "Exit cancelled")) + ('save-all (save-some-buffers t))) + (save-some-buffers arg t))) (let ((confirm confirm-kill-emacs)) (and (or (not (memq t (mapcar (lambda (buf) commit b8ddd94aacb4478600d528e0080aff334e44e0f6 Author: Lars Ingebrigtsen Date: Thu Jan 27 19:54:48 2022 +0100 Make the save buffers prompt from Quit Emacs menu more understandable * lisp/files.el (save-buffers-kill-emacs): Use a much simpler (and more understandable) prompt when exiting Emacs from the menu bar (bug#4980). * lisp/subr.el (use-dialog-box-p): Separate out into its own function for reuse... (y-or-n-p): ... from here. diff --git a/lisp/files.el b/lisp/files.el index 79c336f782..9e8afc8d52 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -7752,7 +7752,15 @@ if any returns nil. If `confirm-kill-emacs' is non-nil, calls it." (interactive "P") ;; Don't use save-some-buffers-default-predicate, because we want ;; to ask about all the buffers before killing Emacs. - (save-some-buffers arg t) + (if (use-dialog-box-p) + (pcase (x-popup-dialog + t `("Unsaved Buffers" + ("Close Without Saving" . no-save) + ("Save All" . save-all) + ("Cancel" . cancel))) + ('cancel (user-error "Exit cancelled")) + ('save-all (save-some-buffers t))) + (save-some-buffers arg t)) (let ((confirm confirm-kill-emacs)) (and (or (not (memq t (mapcar (lambda (buf) diff --git a/lisp/subr.el b/lisp/subr.el index 29b9b6dfcf..4b4412a883 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -3249,6 +3249,13 @@ switch back again to the minibuffer before entering the character. This is not possible when using `read-key', but using `read-key' may be less confusing to some users.") +(defun use-dialog-box-p () + "Say whether the user should be prompted with a dialog popup box." + (and (display-popup-menus-p) + last-input-event ; not during startup + (listp last-nonmenu-event) + use-dialog-box)) + (defun y-or-n-p (prompt) "Ask user a \"y or n\" question. Return t if answer is \"y\" and nil if it is \"n\". @@ -3308,10 +3315,7 @@ like) while `y-or-n-p' is running)." ((and (member str '("h" "H")) help-form) (print help-form)) (t (setq temp-prompt (concat "Please answer y or n. " prompt)))))))) - ((and (display-popup-menus-p) - last-input-event ; not during startup - (listp last-nonmenu-event) - use-dialog-box) + ((use-dialog-box-p) (setq prompt (funcall padded prompt t) answer (x-popup-dialog t `(,prompt ("Yes" . act) ("No" . skip))))) (y-or-n-p-use-read-key commit 10c680551e899805a6de7360e9b65986fd87df72 Author: Eli Zaretskii Date: Thu Jan 27 20:35:22 2022 +0200 Improve detection of glyphless characters on TTY frames * src/term.c (produce_glyphs): If the terminal can report for which characters it has glyphs, use that to determine whether a given character should be displayed as glyphless. diff --git a/src/term.c b/src/term.c index 4c7a90a577..ddf0e8e2f2 100644 --- a/src/term.c +++ b/src/term.c @@ -1632,9 +1632,13 @@ produce_glyphs (struct it *it) } else { - Lisp_Object charset_list = FRAME_TERMINAL (it->f)->charset_list; + struct terminal *t = FRAME_TERMINAL (it->f); + Lisp_Object charset_list = t->charset_list, char_glyph; - if (char_charset (it->char_to_display, charset_list, NULL)) + if (char_charset (it->char_to_display, charset_list, NULL) + && (char_glyph = terminal_glyph_code (t, it->char_to_display), + NILP (char_glyph) + || (FIXNUMP (char_glyph) && XFIXNUM (char_glyph) >= 0))) { it->pixel_width = CHARACTER_WIDTH (it->char_to_display); it->nglyphs = it->pixel_width; commit 3dd47bde7a87bef332bd53f3e73c8d2bc64910c8 (refs/remotes/origin/emacs-28) Author: Juri Linkov Date: Thu Jan 27 19:47:40 2022 +0200 * lisp/frame.el (clone-frame): Filter out 'parent-id' (bug#51883). diff --git a/lisp/frame.el b/lisp/frame.el index 86c52dc438..69119b4c24 100644 --- a/lisp/frame.el +++ b/lisp/frame.el @@ -798,8 +798,9 @@ also select the new frame." (windows (unless no-windows (window-state-get (frame-root-window frame)))) (default-frame-alist - (seq-remove (lambda (elem) (eq (car elem) 'name)) - (frame-parameters frame))) + (seq-remove (lambda (elem) + (memq (car elem) '(name parent-id))) + (frame-parameters frame))) (new-frame (make-frame))) (when windows (window-state-put windows (frame-root-window new-frame) 'safe)) commit 61f5ca3b27a52a9ed86f71502d794554aa56195a Author: Arni Magnusson Date: Thu Jan 27 17:12:11 2022 +0100 Add setx highlighting to bat-mode * lisp/progmodes/bat-mode.el (bat-font-lock-keywords): (bat-font-lock-keywords): Highlight setx (bug#53568). diff --git a/lisp/progmodes/bat-mode.el b/lisp/progmodes/bat-mode.el index 7ef2500e46..6bac297a29 100644 --- a/lisp/progmodes/bat-mode.el +++ b/lisp/progmodes/bat-mode.el @@ -71,8 +71,8 @@ "doskey" "echo" "endlocal" "erase" "fc" "find" "findstr" "format" "ftype" "label" "md" "mkdir" "more" "move" "net" "path" "pause" "popd" "prompt" "pushd" "rd" "ren" "rename" "replace" "rmdir" "set" - "setlocal" "shift" "sort" "subst" "time" "title" "tree" "type" - "ver" "vol" "xcopy")) + "setlocal" "setx" "shift" "sort" "subst" "time" "title" "tree" + "type" "ver" "vol" "xcopy")) (CONTROLFLOW '("call" "cmd" "defined" "do" "else" "equ" "exist" "exit" "for" "geq" "goto" "gtr" "if" "in" "leq" "lss" "neq" "not" "start")) @@ -82,7 +82,7 @@ (2 font-lock-constant-face t)) ("^:[^:].*" . 'bat-label-face) - ("\\_<\\(defined\\|set\\)\\_>[ \t]*\\(\\(\\sw\\|\\s_\\)+\\)" + ("\\_<\\(defined\\|set\\|setx\\)\\_>[ \t]*\\(\\(\\sw\\|\\s_\\)+\\)" (2 font-lock-variable-name-face)) ("%~\\([0-9]\\)" (1 font-lock-variable-name-face)) commit 77b4e06f8ad3d7ea399f38b2211e0eebc5dbfaa8 Author: Lars Ingebrigtsen Date: Thu Jan 27 17:10:13 2022 +0100 Move some of the doc-view variables around to fix load errors diff --git a/lisp/doc-view.el b/lisp/doc-view.el index 4cc560755e..ee6aa05517 100644 --- a/lisp/doc-view.el +++ b/lisp/doc-view.el @@ -226,6 +226,32 @@ are available (see Info node `(emacs)Document View')" Higher values result in larger images." :type 'number) +(defvar doc-view-doc-type nil + "The type of document in the current buffer. +Can be `dvi', `pdf', `ps', `djvu', `odf', 'epub', `cbz', `fb2', +`'xps' or `oxps'.") + +;; FIXME: The doc-view-current-* definitions below are macros because they +;; map to accessors which we want to use via `setf' as well! +(defmacro doc-view-current-page (&optional win) + `(image-mode-window-get 'page ,win)) +(defmacro doc-view-current-info () '(image-mode-window-get 'info)) +(defmacro doc-view-current-overlay () '(image-mode-window-get 'overlay)) +(defmacro doc-view-current-image () '(image-mode-window-get 'image)) +(defmacro doc-view-current-slice () '(image-mode-window-get 'slice)) + +(defvar-local doc-view--current-cache-dir nil + "Only used internally.") + +(defun doc-view-custom-set-epub-font-size (option-name new-value) + (set-default option-name new-value) + (dolist (x (buffer-list)) + (with-current-buffer x + (when (eq doc-view-doc-type 'epub) + (delete-directory doc-view--current-cache-dir t) + (doc-view-initiate-display) + (doc-view-goto-page (doc-view-current-page)))))) + (defcustom doc-view-epub-font-size nil "Font size in points for EPUB layout." :type 'integer @@ -371,9 +397,6 @@ of the page moves to the previous page." (defvar-local doc-view--current-timer nil "Only used internally.") -(defvar-local doc-view--current-cache-dir nil - "Only used internally.") - (defvar-local doc-view--current-search-matches nil "Only used internally.") @@ -388,11 +411,6 @@ files inside an archive it is a temporary copy of the (uncompressed, extracted) file residing in `doc-view-cache-directory'.") -(defvar doc-view-doc-type nil - "The type of document in the current buffer. -Can be `dvi', `pdf', `ps', `djvu', `odf', 'epub', `cbz', `fb2', -`'xps' or `oxps'.") - (defvar doc-view-single-page-converter-function nil "Function to call to convert a single page of the document to a bitmap file. May operate on the source document or on some intermediate (typically PDF) @@ -586,24 +604,6 @@ Typically \"page-%s.png\".") ;;;; Navigation Commands -;; FIXME: The doc-view-current-* definitions below are macros because they -;; map to accessors which we want to use via `setf' as well! -(defmacro doc-view-current-page (&optional win) - `(image-mode-window-get 'page ,win)) -(defmacro doc-view-current-info () '(image-mode-window-get 'info)) -(defmacro doc-view-current-overlay () '(image-mode-window-get 'overlay)) -(defmacro doc-view-current-image () '(image-mode-window-get 'image)) -(defmacro doc-view-current-slice () '(image-mode-window-get 'slice)) - -(defun doc-view-custom-set-epub-font-size (option-name new-value) - (set-default option-name new-value) - (dolist (x (buffer-list)) - (with-current-buffer x - (when (eq doc-view-doc-type 'epub) - (delete-directory doc-view--current-cache-dir t) - (doc-view-initiate-display) - (doc-view-goto-page (doc-view-current-page)))))) - (defun doc-view-last-page-number () (length doc-view--current-files)) commit d633db5189f335873a03544f9f41dcaf77c8e31d Author: Daniel Nicolai Date: Thu Jan 27 17:03:38 2022 +0100 Add support for EPUB, CBZ, FB2 and (O)XPS extension to doc view * doc/emacs/misc.texi (Document View): Add requirements for new extensions (i.e. mutool). * lisp/doc-view.el (doc-view): Additionally update preliminary comment (doc-view-custom-set-epub-font-size): redraw image after setting (doc-view-unoconv-program): Put code all on one line (doc-view-doc-type): Update docstring. (doc-view-kill-proc): Fix comment indentation (doc-view-mode-p): Add check for new extensions and alternative check for PDF (doc-view-pdf/ps->png): Associate new extension with png converter (doc-view-convert-current-doc): Handle new extensions like PDF's (doc-view-set-doc-type): Set correct doc-type for new extensions. * lisp/files.el (auto-mode-alist): Associate new extension types with doc-view. diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi index df1e5ef238..365c079e89 100644 --- a/doc/emacs/misc.texi +++ b/doc/emacs/misc.texi @@ -455,20 +455,27 @@ servers the user has connected to. If this variable is @code{t}, @cindex PostScript file @cindex OpenDocument file @cindex Microsoft Office file +@cindex EPUB file +@cindex CBZ file +@cindex FB2 file +@cindex XPS file +@cindex OXPS file @cindex DocView mode @cindex mode, DocView @cindex document viewer (DocView) @findex doc-view-mode DocView mode is a major mode for viewing DVI, PostScript (PS), PDF, -OpenDocument, and Microsoft Office documents. It provides features -such as slicing, zooming, and searching inside documents. It works by -converting the document to a set of images using the @command{gs} -(GhostScript) or @command{mudraw}/@command{pdfdraw} (MuPDF) commands -and other external tools @footnote{For PostScript files, GhostScript -is a hard requirement. For DVI files, @code{dvipdf} or @code{dvipdfm} -is needed. For OpenDocument and Microsoft Office documents, the -@code{unoconv} tool is needed.}, and displaying those images. +OpenDocument, Microsoft Office, EPUB, CBZ, FB2, XPS and OXPS +documents. It provides features such as slicing, zooming, and +searching inside documents. It works by converting the document to a +set of images using the @command{gs} (GhostScript) or +@command{pdfdraw}/@command{mutool draw} (MuPDF) commands and other +external tools @footnote{PostScript files require GhostScript, DVI +files require @code{dvipdf} or @code{dvipdfm}, OpenDocument and +Microsoft Office documents require the @code{unoconv} tool, and EPUB, +CBZ, FB2, XPS and OXPS files require @code{mutool} to be available.}, +and displaying those images. @findex doc-view-toggle-display @findex doc-view-minor-mode diff --git a/lisp/doc-view.el b/lisp/doc-view.el index 5e160f5dff..4cc560755e 100644 --- a/lisp/doc-view.el +++ b/lisp/doc-view.el @@ -3,7 +3,7 @@ ;; Copyright (C) 2007-2022 Free Software Foundation, Inc. ;; ;; Author: Tassilo Horn -;; Keywords: files, pdf, ps, dvi +;; Keywords: files, pdf, ps, dvi, djvu, epub, cbz, fb2, xps, openxps ;; This file is part of GNU Emacs. @@ -25,17 +25,19 @@ ;; Viewing PS/PDF/DVI files requires Ghostscript, `dvipdf' (comes with ;; Ghostscript) or `dvipdfm' (comes with teTeX or TeXLive) and ;; `pdftotext', which comes with xpdf (https://www.foolabs.com/xpdf/) -;; or poppler (https://poppler.freedesktop.org/). -;; Djvu documents require `ddjvu' (from DjVuLibre). -;; ODF files require `soffice' (from LibreOffice). +;; or poppler (https://poppler.freedesktop.org/). EPUB, CBZ, FB2, XPS +;; and OXPS documents require `mutool' which comes with mupdf +;; (https://mupdf.com/index.html). Djvu documents require `ddjvu' +;; (from DjVuLibre). ODF files require `soffice' (from LibreOffice). ;;; Commentary: ;; DocView is a document viewer for Emacs. It converts a number of -;; document formats (including PDF, PS, DVI, Djvu and ODF files) to a -;; set of PNG files, one PNG for each page, and displays the PNG -;; images inside an Emacs buffer. This buffer uses `doc-view-mode' -;; which provides convenient key bindings for browsing the document. +;; document formats (including PDF, PS, DVI, Djvu, ODF, EPUB, CBZ, +;; FB2, XPS and OXPS files) to a set of PNG (or TIFF for djvu) files, +;; one image for each page, and displays the images inside an Emacs +;; buffer. This buffer uses `doc-view-mode' which provides convenient +;; key bindings for browsing the document. ;; ;; To use it simply open a document file with ;; @@ -147,7 +149,10 @@ ;;;; Customization Options (defgroup doc-view nil - "In-buffer viewer for PDF, PostScript, DVI, and DJVU files." + "In-buffer document viewer. +The viewer handles PDF, PostScript, DVI, DJVU, ODF, EPUB, CBZ, +FB2, XPS and OXPS files, if the appropriate converter programs +are available (see Info node `(emacs)Document View')" :link '(function-link doc-view) :version "22.2" :group 'applications @@ -221,6 +226,11 @@ Higher values result in larger images." :type 'number) +(defcustom doc-view-epub-font-size nil + "Font size in points for EPUB layout." + :type 'integer + :set #'doc-view-custom-set-epub-font-size) + (defcustom doc-view-scale-internally t "Whether we should try to rescale images ourselves. If nil, the document is re-rendered every time the scaling factor is modified. @@ -256,9 +266,7 @@ If this and `doc-view-dvipdfm-program' are set, `doc-view-dvipdf-program' will be preferred." :type 'file) -(define-obsolete-variable-alias 'doc-view-unoconv-program - 'doc-view-odf->pdf-converter-program - "24.4") +(define-obsolete-variable-alias 'doc-view-unoconv-program 'doc-view-odf->pdf-converter-program "24.4") (defcustom doc-view-odf->pdf-converter-program (cond @@ -382,7 +390,8 @@ the (uncompressed, extracted) file residing in (defvar doc-view-doc-type nil "The type of document in the current buffer. -Can be `dvi', `pdf', `ps', `djvu' or `odf'.") +Can be `dvi', `pdf', `ps', `djvu', `odf', 'epub', `cbz', `fb2', +`'xps' or `oxps'.") (defvar doc-view-single-page-converter-function nil "Function to call to convert a single page of the document to a bitmap file. @@ -464,17 +473,17 @@ Typically \"page-%s.png\".") ;; It's normal for this operation to result in a very large undo entry. (setq-local undo-outer-limit (* 2 (buffer-size)))) (cl-labels ((revert () - (let ((revert-buffer-preserve-modes t)) - (apply orig-fun args) - ;; Update the cached version of the pdf file, - ;; too. This is the one that's used when - ;; rendering (bug#26996). - (unless (equal buffer-file-name - doc-view--buffer-file-name) - ;; FIXME: Lars says he needed to recreate - ;; the dir, we should figure out why. - (doc-view-make-safe-dir doc-view-cache-directory) - (write-region nil nil doc-view--buffer-file-name))))) + (let ((revert-buffer-preserve-modes t)) + (apply orig-fun args) + ;; Update the cached version of the pdf file, + ;; too. This is the one that's used when + ;; rendering (bug#26996). + (unless (equal buffer-file-name + doc-view--buffer-file-name) + ;; FIXME: Lars says he needed to recreate + ;; the dir, we should figure out why. + (doc-view-make-safe-dir doc-view-cache-directory) + (write-region nil nil doc-view--buffer-file-name))))) (if (and (eq 'pdf doc-view-doc-type) (executable-find "pdfinfo")) ;; We don't want to revert if the PDF file is corrupted which @@ -586,6 +595,15 @@ Typically \"page-%s.png\".") (defmacro doc-view-current-image () '(image-mode-window-get 'image)) (defmacro doc-view-current-slice () '(image-mode-window-get 'slice)) +(defun doc-view-custom-set-epub-font-size (option-name new-value) + (set-default option-name new-value) + (dolist (x (buffer-list)) + (with-current-buffer x + (when (eq doc-view-doc-type 'epub) + (delete-directory doc-view--current-cache-dir t) + (doc-view-initiate-display) + (doc-view-goto-page (doc-view-current-page)))))) + (defun doc-view-last-page-number () (length doc-view--current-files)) @@ -738,7 +756,7 @@ at the top edge of the page moves to the previous page." (interactive) (while (consp doc-view--current-converter-processes) (ignore-errors ;; Some entries might not be processes, and maybe - ;; some are dead already? + ; some are dead already? (kill-process (pop doc-view--current-converter-processes)))) (when doc-view--current-timer (cancel-timer doc-view--current-timer) @@ -799,8 +817,8 @@ It's a subdirectory of `doc-view-cache-directory'." ;;;###autoload (defun doc-view-mode-p (type) "Return non-nil if document type TYPE is available for `doc-view'. -Document types are symbols like `dvi', `ps', `pdf', or `odf' (any -OpenDocument format)." +Document types are symbols like `dvi', `ps', `pdf', `epub', +`cbz', `fb2', `xps', `oxps', or`odf' (any OpenDocument format)." (and (display-graphic-p) (image-type-available-p 'png) (cond @@ -811,16 +829,22 @@ OpenDocument format)." (and doc-view-dvipdfm-program (executable-find doc-view-dvipdfm-program))))) ((memq type '(postscript ps eps pdf)) - (or (and doc-view-ghostscript-program + (or (and doc-view-ghostscript-program (executable-find doc-view-ghostscript-program)) - (and doc-view-pdfdraw-program - (executable-find doc-view-pdfdraw-program)))) + ;; for pdf also check for `doc-view-pdfdraw-program' + (when (eq type 'pdf) + (and doc-view-pdfdraw-program + (executable-find doc-view-pdfdraw-program))))) ((eq type 'odf) (and doc-view-odf->pdf-converter-program (executable-find doc-view-odf->pdf-converter-program) (doc-view-mode-p 'pdf))) ((eq type 'djvu) (executable-find "ddjvu")) + ((memq type '(epub cbz fb2 xps oxps)) + ;; first check if `doc-view-pdfdraw-program' is set to mutool + (and (string= doc-view-pdfdraw-program "mutool") + (executable-find "mutool"))) (t ;; unknown image type nil)))) @@ -1053,7 +1077,7 @@ Should be invoked when the cached images aren't up-to-date." ;; some file-name-handler-managed dir, for example). (let* ((default-directory (or (unhandled-file-name-directory default-directory) - (expand-file-name "~/"))) + (expand-file-name "~/"))) (proc (apply #'start-process name doc-view-conversion-buffer program args))) (push proc doc-view--current-converter-processes) @@ -1139,14 +1163,17 @@ The test is performed using `doc-view-pdfdraw-program'." (search-forward "error: cannot authenticate password" nil t))) (defun doc-view-pdf->png-converter-mupdf (pdf png page callback) - (let ((pdf-passwd (if (doc-view-pdf-password-protected-pdfdraw-p pdf) - (read-passwd "Enter password for PDF file: ")))) + (let* ((pdf-passwd (if (doc-view-pdf-password-protected-pdfdraw-p pdf) + (read-passwd "Enter password for PDF file: "))) + (options `(,(concat "-o" png) + ,(format "-r%d" (round doc-view-resolution)) + ,@(if pdf-passwd `("-p" ,pdf-passwd))))) + (when (and (eq doc-view-doc-type 'epub) doc-view-epub-font-size) + (setq options (append options (list (format "-S%s" doc-view-epub-font-size))))) (doc-view-start-process "pdf->png" doc-view-pdfdraw-program `(,@(doc-view-pdfdraw-program-subcommand) - ,(concat "-o" png) - ,(format "-r%d" (round doc-view-resolution)) - ,@(if pdf-passwd `("-p" ,pdf-passwd)) + ,@options ,pdf ,@(if page `(,(format "%d" page)))) callback))) @@ -1227,20 +1254,20 @@ Start by converting PAGES, and then the rest." (let ((rest (cdr pages))) (funcall doc-view-single-page-converter-function pdf (format png (car pages)) (car pages) - (lambda () - (if rest - (doc-view-document->bitmap pdf png rest) - ;; Yippie, the important pages are done, update the display. - (clear-image-cache) - ;; For the windows that have a message (like "Welcome to - ;; DocView") display property, clearing the image cache is - ;; not sufficient. - (dolist (win (get-buffer-window-list (current-buffer) nil 'visible)) - (with-selected-window win - (when (stringp (overlay-get (doc-view-current-overlay) 'display)) - (doc-view-goto-page (doc-view-current-page))))) - ;; Convert the rest of the pages. - (doc-view-pdf/ps->png pdf png))))))) + (lambda () + (if rest + (doc-view-document->bitmap pdf png rest) + ;; Yippie, the important pages are done, update the display. + (clear-image-cache) + ;; For the windows that have a message (like "Welcome to + ;; DocView") display property, clearing the image cache is + ;; not sufficient. + (dolist (win (get-buffer-window-list (current-buffer) nil 'visible)) + (with-selected-window win + (when (stringp (overlay-get (doc-view-current-overlay) 'display)) + (doc-view-goto-page (doc-view-current-page))))) + ;; Convert the rest of the pages. + (doc-view-pdf/ps->png pdf png))))))) (defun doc-view-pdf->txt (pdf txt callback) "Convert PDF to TXT asynchronously and call CALLBACK when finished." @@ -1337,7 +1364,9 @@ Those files are saved in the directory given by the function ;; Rename to doc.pdf (rename-file opdf pdf) (doc-view-pdf/ps->png pdf png-file))))) - ((or 'pdf 'djvu) + ;; The doc-view-mode-p check ensures that epub, cbz, fb2 and + ;; (o)xps are handled with mutool + ((or 'pdf 'djvu 'epub 'cbz 'fb2 'xps 'oxps) (let ((pages (doc-view-active-pages))) ;; Convert doc to bitmap images starting with the active pages. (doc-view-document->bitmap doc-view--buffer-file-name png-file pages))) @@ -1432,7 +1461,7 @@ dragging it to its bottom-right corner. See also (defun doc-view-guess-paper-size (iw ih) "Guess the paper size according to the aspect ratio." (cl-labels ((div (x y) - (round (/ (* 100.0 x) y)))) + (round (/ (* 100.0 x) y)))) (let ((ar (div iw ih)) (al (mapcar (lambda (l) (list (div (nth 1 l) (nth 2 l)) (car l))) @@ -1869,6 +1898,8 @@ If BACKWARD is non-nil, jump to the previous match." ("dvi" dvi) ;; PDF ("pdf" pdf) ("epdf" pdf) + ;; EPUB + ("epub" epub) ;; PostScript ("ps" ps) ("eps" ps) ;; DjVu @@ -1880,7 +1911,13 @@ If BACKWARD is non-nil, jump to the previous match." ;; Microsoft Office formats (also handled by the odf ;; conversion chain). ("doc" odf) ("docx" odf) ("xls" odf) ("xlsx" odf) - ("ppt" odf) ("pps" odf) ("pptx" odf) ("rtf" odf)) + ("ppt" odf) ("pps" odf) ("pptx" odf) ("rtf" odf) + ;; CBZ + ("cbz" cbz) + ;; FB2 + ("fb2" fb2) + ;; (Open)XPS + ("xps" xps) ("oxps" oxps)) t)))) (content-types (save-excursion @@ -1889,7 +1926,13 @@ If BACKWARD is non-nil, jump to the previous match." ((looking-at "%!") '(ps)) ((looking-at "%PDF") '(pdf)) ((looking-at "\367\002") '(dvi)) - ((looking-at "AT&TFORM") '(djvu)))))) + ((looking-at "AT&TFORM") '(djvu)) + ;; The following pattern actually is for recognizing + ;; zip-archives, so that this same association is used for + ;; cbz files. This is fine, as cbz files should be handled + ;; like epub anyway. + ((looking-at "PK") '(epub)) + )))) (setq-local doc-view-doc-type (car (or (nreverse (seq-intersection name-types content-types #'eq)) diff --git a/lisp/files.el b/lisp/files.el index 4ba71e6144..79c336f782 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -2928,7 +2928,7 @@ ARC\\|ZIP\\|LZH\\|LHA\\|ZOO\\|[JEW]AR\\|XPI\\|RAR\\|CBR\\|7Z\\|SQUASHFS\\)\\'" . ("\\.\\(diffs?\\|patch\\|rej\\)\\'" . diff-mode) ("\\.\\(dif\\|pat\\)\\'" . diff-mode) ; for MS-DOS ("\\.[eE]?[pP][sS]\\'" . ps-mode) - ("\\.\\(?:PDF\\|DVI\\|OD[FGPST]\\|DOCX\\|XLSX?\\|PPTX?\\|pdf\\|djvu\\|dvi\\|od[fgpst]\\|docx\\|xlsx?\\|pptx?\\)\\'" . doc-view-mode-maybe) + ("\\.\\(?:PDF\\|EPUB\\|CBZ\\|FB2\\|O?XPS\\|DVI\\|OD[FGPST]\\|DOCX\\|XLSX?\\|PPTX?\\|pdf\\|epub\\|cbz\\|fb2\\|o?xps\\|djvu\\|dvi\\|od[fgpst]\\|docx\\|xlsx?\\|pptx?\\)\\'" . doc-view-mode-maybe) ("configure\\.\\(ac\\|in\\)\\'" . autoconf-mode) ("\\.s\\(v\\|iv\\|ieve\\)\\'" . sieve-mode) ("BROWSE\\'" . ebrowse-tree-mode) commit b9239954b60c1c86e64ba1047eb8b48310ad4072 Author: Lars Ingebrigtsen Date: Thu Jan 27 16:30:12 2022 +0100 Tweak href/superscript fix in tex-mode * lisp/textmodes/tex-mode.el (tex-font-lock-keywords-1): Fix superscripts in href in a better way. diff --git a/lisp/textmodes/tex-mode.el b/lisp/textmodes/tex-mode.el index f41cc2c15e..ab94036d01 100644 --- a/lisp/textmodes/tex-mode.el +++ b/lisp/textmodes/tex-mode.el @@ -505,7 +505,9 @@ An alternative value is \" . \", if you use a font with a narrow period." "documentstyle" "documentclass" "verbatiminput" "includegraphics" "includegraphics*") t)) - (verbish (regexp-opt '("url" "nolinkurl" "path") t)) + (verbish (regexp-opt '("url" "nolinkurl" "path" + "href" "ProvidesFile") + t)) ;; Miscellany. (slash "\\\\") (opt " *\\(\\[[^]]*\\] *\\)*") @@ -578,9 +580,6 @@ An alternative value is \" . \", if you use a font with a narrow period." ;; "caption" "footnote" "footnotemark" "footnotetext" ) t)) - (file-like (regexp-opt - '("href" "ProvidesFile") - t)) ;; ;; Names of commands that should be fontified. (specials-1 (regexp-opt '("\\" "\\*") t)) ;; "-" @@ -601,8 +600,6 @@ An alternative value is \" . \", if you use a font with a narrow period." ;; ;; Citation args. (list (concat slash citations opt arg) 3 'font-lock-constant-face) - ;; File-like args. - (list (concat slash file-like opt arg) 3 'font-lock-constant-face) ;; ;; Text between `` quotes ''. (list (concat (regexp-opt '("``" "\"<" "\"`" "<<" "«") t) commit decd6f830c9b65da654f93d9c99c6a5b99c05977 Author: Lars Ingebrigtsen Date: Thu Jan 27 16:18:36 2022 +0100 Revert "Don't output spurious prefixes in read-char-exclusive" This reverts commit 701ec0bda2b15fc97af6ca68fb842fb1ec9aac35. This change broke display of `C-q-' when doing the `C-q' command, so it has to be fixed a different way. diff --git a/src/keyboard.c b/src/keyboard.c index 9242e8dc62..441c23e10c 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -3059,13 +3059,12 @@ read_char (int commandflag, Lisp_Object map, /* Now wipe the echo area, except for help events which do their own stuff with the echo area. */ - if (!NILP (Vecho_keystrokes) - && (!CONSP (c) - || (!(EQ (Qhelp_echo, XCAR (c))) - && !(EQ (Qswitch_frame, XCAR (c))) - /* Don't wipe echo area for select window events: These might - get delayed via `mouse-autoselect-window' (Bug#11304). */ - && !(EQ (Qselect_window, XCAR (c)))))) + if (!CONSP (c) + || (!(EQ (Qhelp_echo, XCAR (c))) + && !(EQ (Qswitch_frame, XCAR (c))) + /* Don't wipe echo area for select window events: These might + get delayed via `mouse-autoselect-window' (Bug#11304). */ + && !(EQ (Qselect_window, XCAR (c))))) { if (!NILP (echo_area_buffer[0])) { diff --git a/src/lread.c b/src/lread.c index ec54d2d81a..9910db27de 100644 --- a/src/lread.c +++ b/src/lread.c @@ -705,13 +705,8 @@ read_filtered_event (bool no_switch_frame, bool ascii_required, /* Read until we get an acceptable event. */ retry: do - { - ptrdiff_t count = SPECPDL_INDEX (); - specbind (Qecho_keystrokes, Qnil); - val = read_char (0, Qnil, (input_method ? Qnil : Qt), 0, - NUMBERP (seconds) ? &end_time : NULL); - unbind_to (count, Qnil); - } + val = read_char (0, Qnil, (input_method ? Qnil : Qt), 0, + NUMBERP (seconds) ? &end_time : NULL); while (FIXNUMP (val) && XFIXNUM (val) == -2); /* wrong_kboard_jmpbuf */ if (BUFFERP (val)) commit 0991e8686cd90a7678346b7608c438fcb7e06bc6 Author: Po Lu Date: Thu Jan 27 21:36:52 2022 +0800 Improve xwidget window ancestry calculations * src/xwidget.c (xw_find_common_ancestor): (xw_notify_virtual_upwards_until) (xw_notify_virtual_downwards_until): New functions. (xw_maybe_synthesize_crossing): Synthesize virtual events like GTK does for non-linear changes. diff --git a/src/xwidget.c b/src/xwidget.c index c42d1609d7..175a289a00 100644 --- a/src/xwidget.c +++ b/src/xwidget.c @@ -1416,16 +1416,153 @@ window_coords_from_toplevel (GdkWindow *window, GdkWindow *toplevel, *out_y = y_out; } +static GdkWindow * +xw_find_common_ancestor (GdkWindow *window, + GdkWindow *other, + GdkWindow *toplevel) +{ + GdkWindow *tem; + GList *l1 = NULL; + GList *l2 = NULL; + GList *i1, *i2; + + tem = window; + while (tem && tem != toplevel) + { + l1 = g_list_prepend (l1, tem); + tem = gdk_window_get_parent (tem); + } + + tem = other; + while (tem && tem != toplevel) + { + l2 = g_list_prepend (l2, tem); + tem = gdk_window_get_parent (tem); + } + + tem = NULL; + i1 = l1; + i2 = l2; + + while (i1 && i2 && (i1->data == i2->data)) + { + tem = i1->data; + i1 = i1->next; + i2 = i2->next; + } + + g_list_free (l1); + g_list_free (l2); + + return tem; +} + +static void +xw_notify_virtual_upwards_until (struct xwidget_view *xv, + GdkWindow *window, + GdkWindow *until, + GdkWindow *toplevel, + unsigned int state, + int x, int y, Time time, + GdkEventType type, + bool nonlinear_p) +{ + GdkEvent *xg_event; + GdkWindow *tem; + int cx, cy; + + for (tem = gdk_window_get_parent (window); + tem && (tem != until); + tem = gdk_window_get_parent (tem)) + { + xg_event = gdk_event_new (type); + + gdk_event_set_device (xg_event, find_suitable_pointer (xv->frame)); + window_coords_from_toplevel (tem, toplevel, x, y, &cx, &cy); + xg_event->crossing.x = cx; + xg_event->crossing.y = cy; + xg_event->crossing.time = time; + xg_event->crossing.focus = FALSE; + xg_event->crossing.detail = (nonlinear_p + ? GDK_NOTIFY_NONLINEAR_VIRTUAL + : GDK_NOTIFY_VIRTUAL); + xg_event->crossing.mode = GDK_CROSSING_NORMAL; + xg_event->crossing.window = g_object_ref (tem); + + gtk_main_do_event (xg_event); + gdk_event_free (xg_event); + } +} + +static void +xw_notify_virtual_downwards_until (struct xwidget_view *xv, + GdkWindow *window, + GdkWindow *until, + GdkWindow *toplevel, + unsigned int state, + int x, int y, Time time, + GdkEventType type, + bool nonlinear_p) +{ + GdkEvent *xg_event; + GdkWindow *tem; + int cx, cy; + GList *path = NULL, *it; + + tem = gdk_window_get_parent (window); + + while (tem && tem != until) + { + path = g_list_prepend (path, tem); + tem = gdk_window_get_parent (tem); + } + + for (it = path; it; it = it->next) + { + tem = it->data; + xg_event = gdk_event_new (type); + + gdk_event_set_device (xg_event, find_suitable_pointer (xv->frame)); + window_coords_from_toplevel (tem, toplevel, x, y, &cx, &cy); + xg_event->crossing.x = cx; + xg_event->crossing.y = cy; + xg_event->crossing.time = time; + xg_event->crossing.focus = FALSE; + xg_event->crossing.detail = (nonlinear_p + ? GDK_NOTIFY_NONLINEAR_VIRTUAL + : GDK_NOTIFY_VIRTUAL); + xg_event->crossing.mode = GDK_CROSSING_NORMAL; + xg_event->crossing.window = g_object_ref (tem); + + gtk_main_do_event (xg_event); + gdk_event_free (xg_event); + } + + g_list_free (path); +} + static bool xw_maybe_synthesize_crossing (struct xwidget_view *view, GdkWindow *current_window, int x, int y, int crossing, Time time, unsigned int state) { - GdkWindow *last_crossing; + GdkWindow *last_crossing, *toplevel, *ancestor; GdkEvent *xg_event; - GdkWindow *toplevel; int cx, cy; + bool nonlinear_p; + + toplevel = gtk_widget_get_window (XXWIDGET (view->model)->widgetwindow_osr); + + if (crossing == XW_CROSSING_LEFT + && (view->last_crossing_window + && !gdk_window_is_destroyed (view->last_crossing_window))) + { + xw_notify_virtual_upwards_until (view, view->last_crossing_window, + toplevel, toplevel, + state, x, y, time, + GDK_LEAVE_NOTIFY, false); + } if (view->last_crossing_window && (gdk_window_is_destroyed (view->last_crossing_window) @@ -1437,15 +1574,33 @@ xw_maybe_synthesize_crossing (struct xwidget_view *view, if (!last_crossing) { view->last_crossing_window = g_object_ref (current_window); + + xw_notify_virtual_downwards_until (view, current_window, + toplevel, toplevel, + state, x, y, time, + GDK_ENTER_NOTIFY, + false); return false; } - toplevel = gtk_widget_get_window (XXWIDGET (view->model)->widgetwindow_osr); - if (last_crossing != current_window) { view->last_crossing_window = g_object_ref (current_window); + ancestor = xw_find_common_ancestor (last_crossing, current_window, toplevel); + + if (!ancestor) + emacs_abort (); + + nonlinear_p = (last_crossing != ancestor) && (current_window != ancestor); + + if (nonlinear_p || (last_crossing != ancestor)) + xw_notify_virtual_upwards_until (view, last_crossing, + ancestor, toplevel, + state, x, y, time, + GDK_LEAVE_NOTIFY, + nonlinear_p); + xg_event = gdk_event_new (GDK_LEAVE_NOTIFY); gdk_event_set_device (xg_event, find_suitable_pointer (view->frame)); window_coords_from_toplevel (last_crossing, toplevel, @@ -1455,14 +1610,25 @@ xw_maybe_synthesize_crossing (struct xwidget_view *view, xg_event->crossing.time = time; xg_event->crossing.focus = FALSE; xg_event->crossing.state = state; - /* TODO: actually calculate the event detail and mode. */ - xg_event->crossing.detail = GDK_NOTIFY_NONLINEAR; + /* TODO: actually calculate the event mode. */ + xg_event->crossing.detail = (nonlinear_p + ? GDK_NOTIFY_NONLINEAR + : (last_crossing == ancestor + ? GDK_NOTIFY_INFERIOR + : GDK_NOTIFY_ANCESTOR)); xg_event->crossing.mode = GDK_CROSSING_NORMAL; xg_event->crossing.window = g_object_ref (last_crossing); gtk_main_do_event (xg_event); gdk_event_free (xg_event); + if (nonlinear_p || (current_window != ancestor)) + xw_notify_virtual_downwards_until (view, current_window, + ancestor, toplevel, + state, x, y, time, + GDK_ENTER_NOTIFY, + nonlinear_p); + xg_event = gdk_event_new (GDK_ENTER_NOTIFY); gdk_event_set_device (xg_event, find_suitable_pointer (view->frame)); window_coords_from_toplevel (current_window, toplevel, @@ -1472,7 +1638,11 @@ xw_maybe_synthesize_crossing (struct xwidget_view *view, xg_event->crossing.time = time; xg_event->crossing.focus = FALSE; xg_event->crossing.state = state; - xg_event->crossing.detail = GDK_NOTIFY_NONLINEAR; + xg_event->crossing.detail = (nonlinear_p + ? GDK_NOTIFY_NONLINEAR + : (current_window == ancestor + ? GDK_NOTIFY_INFERIOR + : GDK_NOTIFY_ANCESTOR)); xg_event->crossing.mode = GDK_CROSSING_NORMAL; xg_event->crossing.window = g_object_ref (current_window); commit 63255de48b498a8fab4cec5da244998f1ed23f8e Author: Andreas Schwab Date: Thu Jan 27 12:22:47 2022 +0100 * src/pdumper.c (dump_vectorlike): Handle PVEC_SYMBOL_WITH_POS. diff --git a/src/pdumper.c b/src/pdumper.c index 60280fcb04..f4e8e4af28 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -2948,7 +2948,7 @@ dump_vectorlike (struct dump_context *ctx, Lisp_Object lv, dump_off offset) { -#if CHECK_STRUCTS && !defined HASH_pvec_type_19F6CF5169 +#if CHECK_STRUCTS && !defined HASH_pvec_type_AFF6FED5BD # error "pvec_type changed. See CHECK_STRUCTS comment in config.h." #endif const struct Lisp_Vector *v = XVECTOR (lv); @@ -3032,6 +3032,8 @@ dump_vectorlike (struct dump_context *ctx, error_unsupported_dump_object (ctx, lv, "sqlite"); case PVEC_MODULE_FUNCTION: error_unsupported_dump_object (ctx, lv, "module function"); + case PVEC_SYMBOL_WITH_POS: + error_unsupported_dump_object (ctx, lv, "symbol with pos"); default: error_unsupported_dump_object(ctx, lv, "weird pseudovector"); } commit dae4fa998609af286ba7a8a2e2a2e77d22df1faa Merge: 9d34946e53 d0aac84b2a Author: Eli Zaretskii Date: Thu Jan 27 12:57:16 2022 +0200 Merge branch 'master' of git.savannah.gnu.org:/srv/git/emacs commit 9d34946e53afb928c1885df8fcb48d2618853708 Author: Zajcev Evgeny Date: Wed Jan 26 16:29:13 2022 +0300 ; Fix typo in doc/lispref/display.texi. diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index 9020b98a1e..3ce93200a5 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -5229,7 +5229,7 @@ is partitioned using the identity of the parameter, which is why the parameter is a list with one element. For instance: @lisp -(insert (propertize "foo" '(display (min-width (6.0))))) +(insert (propertize "foo" 'display '(min-width (6.0)))) @end lisp This will add padding after @samp{foo} bringing the total width up to commit d0aac84b2a8d82042475aeeeba465d0d5fd6ad3d Author: Andrea Corallo Date: Thu Jan 27 11:47:41 2022 +0100 * Fix `startup--honor-delayed-native-compilations' for (bug#53497) * lisp/startup.el (startup--honor-delayed-native-compilations): Don't forget to set `comp--loadable' when `comp--delayed-sources' is empty. diff --git a/lisp/startup.el b/lisp/startup.el index d90e7a7d26..05d829396d 100644 --- a/lisp/startup.el +++ b/lisp/startup.el @@ -525,12 +525,16 @@ DIRS are relative." (files &optional recursively load selector)) (defun startup--honor-delayed-native-compilations () "Honor pending delayed deferred native compilations." - (when (and (native-comp-available-p) - comp--delayed-sources) - (require 'comp) - (setq comp--loadable t) - (native--compile-async comp--delayed-sources nil 'late) - (setq comp--delayed-sources nil))) + (if (and (native-comp-available-p) + comp--delayed-sources) + (progn + ;; Require comp before setting `comp--loadable' to break + ;; circularity. + (require 'comp) + (setq comp--loadable t) + (native--compile-async comp--delayed-sources nil 'late) + (setq comp--delayed-sources nil)) + (setq comp--loadable t))) (defvar native-comp-eln-load-path) (defun normal-top-level () commit 60cfb90d0161af4fc3219bba00525e7a3473601a Author: Andrea Corallo Date: Thu Jan 27 10:39:53 2022 +0100 * Have `benchmark-run-compiled' use the native compiler when available * lisp/emacs-lisp/benchmark.el (benchmark-run-compiled): Use native compiler when available. diff --git a/lisp/emacs-lisp/benchmark.el b/lisp/emacs-lisp/benchmark.el index c5f621c6c8..882b1d68c4 100644 --- a/lisp/emacs-lisp/benchmark.el +++ b/lisp/emacs-lisp/benchmark.el @@ -121,7 +121,11 @@ result. The overhead of the `lambda's is accounted for." (unless (or (natnump repetitions) (and repetitions (symbolp repetitions))) (setq forms (cons repetitions forms) repetitions 1)) - `(benchmark-call (byte-compile '(lambda () ,@forms)) ,repetitions)) + `(benchmark-call (,(if (native-comp-available-p) + 'native-compile + 'byte-compile) + '(lambda () ,@forms)) + ,repetitions)) ;;;###autoload (defun benchmark (repetitions form) commit a8862f313b92ac0fad35c7240ef4da8e52d55c1d Merge: 82aa5be7ce c9524819ea Author: Stefan Kangas Date: Thu Jan 27 09:45:49 2022 +0100 Merge from origin/emacs-28 c9524819ea Partially revert a fill-region-as-paragraph regression 6075ea0b79 Fix 'make_lispy_position' when there's an image at EOB # Conflicts: # test/lisp/textmodes/fill-tests.el commit c9524819eaf4e561a184b04dfca7e42970dc8809 Author: Lars Ingebrigtsen Date: Wed Jan 26 16:17:49 2022 +0100 Partially revert a fill-region-as-paragraph regression * lisp/textmodes/fill.el (fill-region-as-paragraph): Revert e186af261 (bug#53537), because it leads to regressions. (But leave tests in place.) diff --git a/lisp/textmodes/fill.el b/lisp/textmodes/fill.el index 92e50ec290..beb30c6e95 100644 --- a/lisp/textmodes/fill.el +++ b/lisp/textmodes/fill.el @@ -714,8 +714,7 @@ space does not end a sentence, so don't break a line there." (or justify (setq justify (current-justification))) ;; Don't let Adaptive Fill mode alter the fill prefix permanently. - (let ((actual-fill-prefix fill-prefix) - (fill-prefix fill-prefix)) + (let ((fill-prefix fill-prefix)) ;; Figure out how this paragraph is indented, if desired. (when (and adaptive-fill-mode (or (null fill-prefix) (string= fill-prefix ""))) @@ -755,18 +754,9 @@ space does not end a sentence, so don't break a line there." ;; This is the actual filling loop. (goto-char from) - (let ((first t) - linebeg) - (while (< (point) to) - ;; On the first line, there may be text in the fill prefix - ;; zone (when `fill-prefix' is specified externally, and - ;; not computed). In that case, don't consider that area - ;; when trying to find a place to put a line break - ;; (bug#45720). - (if (not first) - (setq linebeg (point)) - (setq first nil - linebeg (+ (point) (length actual-fill-prefix)))) + (let (linebeg) + (while (< (point) to) + (setq linebeg (point)) (move-to-column (current-fill-column)) (if (when (and (< (point) to) (< linebeg to)) ;; Find the position where we'll break the line. diff --git a/test/lisp/textmodes/fill-tests.el b/test/lisp/textmodes/fill-tests.el index 39e5dd3d26..8b9f144dff 100644 --- a/test/lisp/textmodes/fill-tests.el +++ b/test/lisp/textmodes/fill-tests.el @@ -45,6 +45,8 @@ (should (string= (buffer-string) "Abc\nd efg\n(h ijk).")))) (ert-deftest fill-test-unbreakable-paragraph () + ;; See bug#45720 and bug#53537. + :expected-result :failed (with-temp-buffer (let ((string "aaa = baaaaaaaaaaaaaaaaaaaaaaaaaaaa\n")) (insert string) @@ -76,6 +78,27 @@ (buffer-string) "aaa = baaaaaaaa aaaaaaaaaa\n aaaaaaaaaa\n"))))) +(ert-deftest test-fill-haskell () + (should + (equal + (with-temp-buffer + (asm-mode) + (dolist (line '(" ;; a b c" + " ;; d e f" + " ;; x y z" + " ;; w")) + (insert line "\n")) + (goto-char (point-min)) + (end-of-line) + (setf fill-column 10) + (fill-paragraph nil) + (buffer-string)) + " ;; a b c + ;; d e f + ;; x y z + ;; w +"))) + (provide 'fill-tests) ;;; fill-tests.el ends here commit 6075ea0b79922765df29ac148ac3a1fbb236ed94 Author: Eli Zaretskii Date: Wed Jan 26 16:44:14 2022 +0200 Fix 'make_lispy_position' when there's an image at EOB * src/xdisp.c (move_it_to): Don't compare IT_CHARPOS with an invalid TO_CHARPOS. (Bug#53546) diff --git a/src/xdisp.c b/src/xdisp.c index 73edc0d7aa..aa70b933f1 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -10016,7 +10016,8 @@ move_it_to (struct it *it, ptrdiff_t to_charpos, int to_x, int to_y, int to_vpos could have both positions after TO_CHARPOS or both positions before it, due to bidi reordering.) */ - if (IT_CHARPOS (*it) != to_charpos + if (to_charpos > 0 + && IT_CHARPOS (*it) != to_charpos && ((IT_CHARPOS (it_backup) > to_charpos) == (IT_CHARPOS (*it) > to_charpos))) {