From 5e07d1d0fc93bfd59983d5a832d08e771e5b6212 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Wed, 10 Aug 2022 10:33:43 +0800 Subject: Improve MPX interaction with xwidgets and drag-and-drop * src/xterm.c (handle_one_xevent): Handle focus interaction in more places. --- src/xterm.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/xterm.c b/src/xterm.c index ab43a8ec517..41537ade155 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -21333,6 +21333,10 @@ handle_one_xevent (struct x_display_info *dpyinfo, } #endif + if (f && device) + xi_handle_interaction (dpyinfo, f, device, + xev->time); + if (xev->evtype == XI_ButtonPress && x_dnd_last_seen_window != None) { @@ -21579,11 +21583,19 @@ handle_one_xevent (struct x_display_info *dpyinfo, xev->send_event); source = xi_device_from_id (dpyinfo, xev->sourceid); + device = xi_device_from_id (dpyinfo, xev->deviceid); #ifdef HAVE_XWIDGETS xvw = xwidget_view_from_window (xev->event); if (xvw) { + /* If the user interacts with a frame that's focused + on another device, but not the current focus + frame, make it the focus frame. */ + if (device) + xi_handle_interaction (dpyinfo, xvw->frame, + device, xev->time); + xwidget_button (xvw, xev->evtype == XI_ButtonPress, lrint (xev->event_x), lrint (xev->event_y), xev->detail, xi_convert_event_state (xev), @@ -21603,8 +21615,6 @@ handle_one_xevent (struct x_display_info *dpyinfo, } #endif - device = xi_device_from_id (dpyinfo, xev->deviceid); - if (!device) goto XI_OTHER; -- cgit v1.2.1 From 8c0c9398f324bed79200c85ca9b0d4e95bf4a686 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Wed, 10 Aug 2022 19:30:55 +0800 Subject: Fix some undesirable frame focus changes * src/xterm.c (handle_one_xevent): Only detach upon actual device tree change. --- src/xterm.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/xterm.c b/src/xterm.c index 41537ade155..17043e078c7 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -22436,11 +22436,14 @@ handle_one_xevent (struct x_display_info *dpyinfo, case XI_HierarchyChanged: { - XIHierarchyEvent *hev = (XIHierarchyEvent *) xi_event; + XIHierarchyEvent *hev; XIDeviceInfo *info; int i, ndevices, n_disabled, *disabled; struct xi_device_t *device; + bool any_changed; + any_changed = false; + hev = (XIHierarchyEvent *) xi_event; disabled = SAFE_ALLOCA (sizeof *disabled * hev->num_info); n_disabled = 0; @@ -22450,8 +22453,17 @@ handle_one_xevent (struct x_display_info *dpyinfo, { /* Handle all disabled devices now, to prevent things happening out-of-order later. */ - xi_disable_devices (dpyinfo, disabled, n_disabled); - n_disabled = 0; + + if (ndevices) + { + xi_disable_devices (dpyinfo, disabled, n_disabled); + n_disabled = 0; + + /* This flag really just means that disabled + devices were handled early and should be + used in conjunction with n_disabled. */ + any_changed = true; + } x_catch_errors (dpyinfo->display); info = XIQueryDevice (dpyinfo->display, hev->info[i].deviceid, @@ -22502,9 +22514,12 @@ handle_one_xevent (struct x_display_info *dpyinfo, event. */ xi_disable_devices (dpyinfo, disabled, n_disabled); - /* Now that the device hierarchy has been changed, - recompute focus. */ - xi_handle_focus_change (dpyinfo); + /* If the device hierarchy has been changed, recompute + focus. This might seem like a micro-optimization but + it actually keeps the focus from changing in some + cases where it would be undesierable. */ + if (any_changed || n_disabled) + xi_handle_focus_change (dpyinfo); goto XI_OTHER; } -- cgit v1.2.1 From edffda8609bd5828c08dceea30f2373bbcfe7c1c Mon Sep 17 00:00:00 2001 From: Po Lu Date: Thu, 11 Aug 2022 09:56:15 +0800 Subject: Adjust client pointer upon help event * src/xterm.c (handle_one_xevent): Keep track of the device that set do_help and make it the client pointer so that tooltips show up under the right pointer. --- src/xterm.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/xterm.c b/src/xterm.c index 17043e078c7..4372fdb959d 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -17316,6 +17316,10 @@ handle_one_xevent (struct x_display_info *dpyinfo, union buffered_input_event inev; int count = 0; int do_help = 0; +#ifdef HAVE_XINPUT2 + struct xi_device_t *gen_help_device; + Time gen_help_time; +#endif ptrdiff_t nbytes = 0; struct frame *any, *f = NULL; Mouse_HLInfo *hlinfo = &dpyinfo->mouse_highlight; @@ -17345,6 +17349,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, EVENT_INIT (inev.ie); inev.ie.kind = NO_EVENT; inev.ie.arg = Qnil; +#ifdef HAVE_XINPUT2 + gen_help_device = NULL; +#endif /* Ignore events coming from various extensions, such as XFIXES and XKB. */ @@ -21270,7 +21277,15 @@ handle_one_xevent (struct x_display_info *dpyinfo, has changed, generate a HELP_EVENT. */ if (!NILP (help_echo_string) || !NILP (previous_help_echo_string)) - do_help = 1; + { + /* Also allow the focus and client pointer to be + adjusted accordingly, in case a help tooltip is + shown. */ + gen_help_device = device; + gen_help_time = xev->time; + + do_help = 1; + } goto XI_OTHER; } @@ -23207,6 +23222,12 @@ handle_one_xevent (struct x_display_info *dpyinfo, if (do_help > 0) { any_help_event_p = true; +#ifdef HAVE_XINPUT2 + if (gen_help_device) + xi_handle_interaction (dpyinfo, f, + gen_help_device, + gen_help_time); +#endif gen_help_event (help_echo_string, frame, help_echo_window, help_echo_object, help_echo_pos); } -- cgit v1.2.1 From ae348b719e8f1c29c3ee6ed5d723326b3bc54883 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Mitsuharu Date: Thu, 11 Aug 2022 18:16:24 +0900 Subject: Fix wrong metrics for bitmap-only fonts with HarfBuzz 5 * src/ftcrfont.c (ftcrhbfont_begin_hb_font): Always use the standard position unit value on HarfBuzz 5 and later regardless of whether the font is bitmap-only or not. (Bug#57066) --- src/ftcrfont.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ftcrfont.c b/src/ftcrfont.c index 39ea068556b..d5d3e440af6 100644 --- a/src/ftcrfont.c +++ b/src/ftcrfont.c @@ -568,7 +568,11 @@ ftcrhbfont_begin_hb_font (struct font *font, double *position_unit) ftcrfont_info->ft_size = ft_face->size; hb_font_t *hb_font = fthbfont_begin_hb_font (font, position_unit); - if (ftcrfont_info->bitmap_position_unit) + /* HarfBuzz 5 correctly scales bitmap-only fonts without position + unit adjustment. + (https://github.com/harfbuzz/harfbuzz/issues/489) */ + if (!hb_version_atleast (5, 0, 0) + && ftcrfont_info->bitmap_position_unit) *position_unit = ftcrfont_info->bitmap_position_unit; return hb_font; -- cgit v1.2.1 From 48215c41d16fadb69e85121b3baca0dfca82cc44 Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Thu, 11 Aug 2022 19:31:09 +0000 Subject: New debugging facility: backtraces from errors in Lisp called from redisplay Setting backtrace-on-redisplay-error to non-nil enables the generation of a Lisp backtrace in buffer *Redisplay-trace* following an error in Lisp called from redisplay. * doc/lispref/debugging.texi (Debugging Redisplay): New subsection. (Error Debugging): Reference to the new subsection. * etc/NEWS: New entry for the new facility. * src/eval.c (redisplay_deep_handler): New variable. (init_eval): Initialize redisplay_deep_handler. (call_debugger): Don't throw to top-level after calling debug-early (internal_condition_case_n): "Bind" redisplay_deep_handler to the current handler. (backtrace_yet): New boolean variable. (signal_or_quit): New code section to handle Lisp errors occurring in redisplay. (syms_of_eval): New DEFVAR_BOOL backtrace-on-redisplay-error. * src/keyboard.c (command_loop_1): Set backtrace_yet to false each time around the loop. (safe_run_hooks_error): Allow args to be up to four Lisp_Objects long. (safe_run_hooks_2): New function. * src/lisp.h (top level): declare as externs backtrace_yet and safe_run_hooks_2. * src/xdisp.c (run_window_scroll_functions): Replace a call to run_hook_with_args_2 with one to safe_run_hooks_2. --- src/eval.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/keyboard.c | 14 +++++++++++++- src/lisp.h | 2 ++ src/xdisp.c | 4 ++-- 4 files changed, 75 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/eval.c b/src/eval.c index d82d05797b2..56b42966623 100644 --- a/src/eval.c +++ b/src/eval.c @@ -57,6 +57,12 @@ Lisp_Object Vrun_hooks; /* FIXME: We should probably get rid of this! */ Lisp_Object Vsignaling_function; +/* The handler structure which will catch errors in Lisp hooks called + from redisplay. We do not use it for this; we compare it with the + handler which is about to be used in signal_or_quit, and if it + matches, cause a backtrace to be generated. */ +static struct handler *redisplay_deep_handler; + /* These would ordinarily be static, but they need to be visible to GDB. */ bool backtrace_p (union specbinding *) EXTERNALLY_VISIBLE; Lisp_Object *backtrace_args (union specbinding *) EXTERNALLY_VISIBLE; @@ -246,6 +252,7 @@ init_eval (void) lisp_eval_depth = 0; /* This is less than the initial value of num_nonmacro_input_events. */ when_entered_debugger = -1; + redisplay_deep_handler = NULL; } /* Ensure that *M is at least A + B if possible, or is its maximum @@ -333,7 +340,8 @@ call_debugger (Lisp_Object arg) /* Interrupting redisplay and resuming it later is not safe under all circumstances. So, when the debugger returns, abort the interrupted redisplay by going back to the top-level. */ - if (debug_while_redisplaying) + if (debug_while_redisplaying + && !EQ (Vdebugger, Qdebug_early)) Ftop_level (); return unbind_to (count, val); @@ -1556,12 +1564,16 @@ internal_condition_case_n (Lisp_Object (*bfun) (ptrdiff_t, Lisp_Object *), ptrdiff_t nargs, Lisp_Object *args)) { + struct handler *old_deep = redisplay_deep_handler; struct handler *c = push_handler (handlers, CONDITION_CASE); + if (redisplaying_p) + redisplay_deep_handler = c; if (sys_setjmp (c->jmp)) { Lisp_Object val = handlerlist->val; clobbered_eassert (handlerlist == c); handlerlist = handlerlist->next; + redisplay_deep_handler = old_deep; return hfun (val, nargs, args); } else @@ -1569,6 +1581,7 @@ internal_condition_case_n (Lisp_Object (*bfun) (ptrdiff_t, Lisp_Object *), Lisp_Object val = bfun (nargs, args); eassert (handlerlist == c); handlerlist = c->next; + redisplay_deep_handler = old_deep; return val; } } @@ -1701,6 +1714,11 @@ quit (void) return signal_or_quit (Qquit, Qnil, true); } +/* Has an error in redisplay giving rise to a backtrace occurred as + yet in the current command? This gets reset in the command + loop. */ +bool backtrace_yet = false; + /* Signal an error, or quit. ERROR_SYMBOL and DATA are as with Fsignal. If KEYBOARD_QUIT, this is a quit; ERROR_SYMBOL should be Qquit and DATA should be Qnil, and this function may return. @@ -1816,6 +1834,40 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) unbind_to (count, Qnil); } + /* If an error is signalled during a Lisp hook in redisplay, write a + backtrace into the buffer *Redisplay-trace*. */ + if (!debugger_called && !NILP (error_symbol) + && backtrace_on_redisplay_error + && (NILP (clause) || h == redisplay_deep_handler) + && NILP (Vinhibit_debugger) + && !NILP (Ffboundp (Qdebug_early))) + { + max_ensure_room (&max_lisp_eval_depth, lisp_eval_depth, 100); + specpdl_ref count = SPECPDL_INDEX (); + ptrdiff_t counti = specpdl_ref_to_count (count); + AUTO_STRING (redisplay_trace, "*Redisplay_trace*"); + Lisp_Object redisplay_trace_buffer; + AUTO_STRING (gap, "\n\n\n\n"); /* Separates things in *Redisplay-trace* */ + Lisp_Object delayed_warning; + max_ensure_room (&max_specpdl_size, counti, 200); + redisplay_trace_buffer = Fget_buffer_create (redisplay_trace, Qnil); + current_buffer = XBUFFER (redisplay_trace_buffer); + if (!backtrace_yet) /* Are we on the first backtrace of the command? */ + Ferase_buffer (); + else + Finsert (1, &gap); + backtrace_yet = true; + specbind (Qstandard_output, redisplay_trace_buffer); + specbind (Qdebugger, Qdebug_early); + call_debugger (list2 (Qerror, Fcons (error_symbol, data))); + unbind_to (count, Qnil); + delayed_warning = make_string + ("Error in a redisplay Lisp hook. See buffer *Redisplay_trace*", 61); + + Vdelayed_warnings_list = Fcons (list2 (Qerror, delayed_warning), + Vdelayed_warnings_list); + } + if (!NILP (clause)) { Lisp_Object unwind_data @@ -4278,6 +4330,11 @@ Does not apply if quit is handled by a `condition-case'. */); DEFVAR_BOOL ("debug-on-next-call", debug_on_next_call, doc: /* Non-nil means enter debugger before next `eval', `apply' or `funcall'. */); + DEFVAR_BOOL ("backtrace-on-redisplay-error", backtrace_on_redisplay_error, + doc: /* Non-nil means create a backtrace if a lisp error occurs in redisplay. +The backtrace is written to buffer *Redisplay-trace*. */); + backtrace_on_redisplay_error = false; + DEFVAR_BOOL ("debugger-may-continue", debugger_may_continue, doc: /* Non-nil means debugger may continue execution. This is nil when the debugger is called under circumstances where it diff --git a/src/keyboard.c b/src/keyboard.c index 4ad6e4e6bd1..719226caedc 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -1331,6 +1331,7 @@ command_loop_1 (void) display_malloc_warning (); Vdeactivate_mark = Qnil; + backtrace_yet = false; /* Don't ignore mouse movements for more than a single command loop. (This flag is set in xdisp.c whenever the tool bar is @@ -1841,7 +1842,7 @@ safe_run_hooks_1 (ptrdiff_t nargs, Lisp_Object *args) static Lisp_Object safe_run_hooks_error (Lisp_Object error, ptrdiff_t nargs, Lisp_Object *args) { - eassert (nargs == 2); + eassert (nargs >= 2 && nargs <= 4); AUTO_STRING (format, "Error in %s (%S): %S"); Lisp_Object hook = args[0]; Lisp_Object fun = args[1]; @@ -1915,6 +1916,17 @@ safe_run_hooks_maybe_narrowed (Lisp_Object hook, struct window *w) unbind_to (count, Qnil); } +void +safe_run_hooks_2 (Lisp_Object hook, Lisp_Object arg1, Lisp_Object arg2) +{ + specpdl_ref count = SPECPDL_INDEX (); + + specbind (Qinhibit_quit, Qt); + run_hook_with_args (4, ((Lisp_Object []) {hook, hook, arg1, arg2}), + safe_run_hook_funcall); + unbind_to (count, Qnil); +} + /* Nonzero means polling for input is temporarily suppressed. */ diff --git a/src/lisp.h b/src/lisp.h index fe6e98843d1..2f73ba4c617 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4530,6 +4530,7 @@ extern Lisp_Object Vrun_hooks; extern Lisp_Object Vsignaling_function; extern Lisp_Object inhibit_lisp_code; extern bool signal_quit_p (Lisp_Object); +extern bool backtrace_yet; /* To run a normal hook, use the appropriate function from the list below. The calling convention: @@ -4831,6 +4832,7 @@ extern bool detect_input_pending_ignore_squeezables (void); extern bool detect_input_pending_run_timers (bool); extern void safe_run_hooks (Lisp_Object); extern void safe_run_hooks_maybe_narrowed (Lisp_Object, struct window *); +extern void safe_run_hooks_2 (Lisp_Object, Lisp_Object, Lisp_Object); extern void cmd_error_internal (Lisp_Object, const char *); extern Lisp_Object command_loop_2 (Lisp_Object); extern Lisp_Object read_menu_command (void); diff --git a/src/xdisp.c b/src/xdisp.c index 855f48f2bde..5268c359ecd 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -18133,8 +18133,8 @@ run_window_scroll_functions (Lisp_Object window, struct text_pos startp) { specpdl_ref count = SPECPDL_INDEX (); specbind (Qinhibit_quit, Qt); - run_hook_with_args_2 (Qwindow_scroll_functions, window, - make_fixnum (CHARPOS (startp))); + safe_run_hooks_2 + (Qwindow_scroll_functions, window, make_fixnum (CHARPOS (startp))); unbind_to (count, Qnil); SET_TEXT_POS_FROM_MARKER (startp, w->start); /* In case the hook functions switch buffers. */ -- cgit v1.2.1 From 05c4e7bfbdc5c0ed462be7356e6841e1e59d3851 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Fri, 12 Aug 2022 09:25:12 +0800 Subject: Prevent SelectionNotify etc from reaching GTK 3 * src/xterm.c (handle_one_xevent): Prevent selection events from reaching GTK if they are destined for an Emacs window. --- src/xterm.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src') diff --git a/src/xterm.c b/src/xterm.c index 4372fdb959d..48f10269dfc 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -17877,6 +17877,12 @@ handle_one_xevent (struct x_display_info *dpyinfo, if (!x_window_to_frame (dpyinfo, event->xselection.requestor)) goto OTHER; #endif /* not USE_X_TOOLKIT and not USE_GTK */ +#ifdef HAVE_GTK3 + /* GTK 3 apparently chokes on these events since they have no + associated device. (bug#56869, another bug as well that I + can't find) */ + *finish = X_EVENT_DROP; +#endif x_handle_selection_notify (&event->xselection); break; @@ -17885,6 +17891,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, if (!x_window_to_frame (dpyinfo, event->xselectionclear.window)) goto OTHER; #endif /* not USE_X_TOOLKIT and not USE_GTK */ +#ifdef HAVE_GTK3 + *finish = X_EVENT_DROP; +#endif { const XSelectionClearEvent *eventp = &event->xselectionclear; @@ -17911,6 +17920,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, if (!x_window_to_frame (dpyinfo, event->xselectionrequest.owner)) goto OTHER; #endif /* USE_X_TOOLKIT */ +#ifdef HAVE_GTK3 + *finish = X_EVENT_DROP; +#endif { const XSelectionRequestEvent *eventp = &event->xselectionrequest; -- cgit v1.2.1 From e311d05ab100b5518b974ccaee148a35ae2dada0 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sat, 13 Aug 2022 10:35:08 +0800 Subject: Improve MPX interaction with drag-and-drop * src/xfns.c (Fx_set_mouse_absolute_pixel_position): Use internal client pointer record. * src/xterm.c (x_dnd_cancel_dnd_early): New function. Only used on XI2 builds so far. (x_dnd_begin_drag_and_drop): Set the pointer device used for DND events. (xi_disable_devices): Cancel the drag-and-drop operation if that device is disabled. (x_send_scroll_bar_event): Update outdated comment. (handle_one_xevent): Only accept DND events from that device. (frame_set_mouse_pixel_position): Use internal client pointer record. --- src/xfns.c | 19 ++++----- src/xterm.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 131 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/xfns.c b/src/xfns.c index 2845ecca6a9..144f64f6f62 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -6851,17 +6851,16 @@ The coordinates X and Y are interpreted in pixels relative to a position #ifdef HAVE_XINPUT2 int deviceid; - if (FRAME_DISPLAY_INFO (f)->supports_xi2) + deviceid = FRAME_DISPLAY_INFO (f)->client_pointer_device; + + if (FRAME_DISPLAY_INFO (f)->supports_xi2 + && deviceid != -1) { - XGrabServer (FRAME_X_DISPLAY (f)); - if (XIGetClientPointer (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), - &deviceid)) - { - XIWarpPointer (FRAME_X_DISPLAY (f), deviceid, None, - FRAME_DISPLAY_INFO (f)->root_window, - 0, 0, 0, 0, xval, yval); - } - XUngrabServer (FRAME_X_DISPLAY (f)); + x_catch_errors_for_lisp (FRAME_X_DISPLAY (f)); + XIWarpPointer (FRAME_X_DISPLAY (f), deviceid, None, + FRAME_DISPLAY_INFO (f)->root_window, + 0, 0, 0, 0, xval, yval); + x_uncatch_errors_for_lisp (FRAME_X_DISPLAY (f)); } else #endif diff --git a/src/xterm.c b/src/xterm.c index 48f10269dfc..e48d6fd2513 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -1398,6 +1398,12 @@ static int x_dnd_last_tooltip_x, x_dnd_last_tooltip_y; /* Whether or not those values are actually known yet. */ static bool x_dnd_last_tooltip_valid; +#ifdef HAVE_XINPUT2 +/* The master pointer device being used for the drag-and-drop + operation. */ +static int x_dnd_pointer_device; +#endif + /* Structure describing a single window that can be the target of drag-and-drop operations. */ struct x_client_list_window @@ -4705,6 +4711,67 @@ x_restore_events_after_dnd (struct frame *f, XWindowAttributes *wa) dpyinfo->Xatom_XdndTypeList); } +#ifdef HAVE_XINPUT2 + +/* Cancel the current drag-and-drop operation, sending leave messages + to any relevant toplevels. This is called from the event loop when + an event is received telling Emacs to gracefully cancel the + drag-and-drop operation. */ + +static void +x_dnd_cancel_dnd_early (void) +{ + struct frame *f; + xm_drop_start_message dmsg; + + eassert (x_dnd_frame && x_dnd_in_progress); + + f = x_dnd_frame; + + if (x_dnd_last_seen_window != None + && x_dnd_last_protocol_version != -1) + x_dnd_send_leave (x_dnd_frame, + x_dnd_last_seen_window); + else if (x_dnd_last_seen_window != None + && !XM_DRAG_STYLE_IS_DROP_ONLY (x_dnd_last_motif_style) + && x_dnd_last_motif_style != XM_DRAG_STYLE_NONE + && x_dnd_motif_setup_p) + { + dmsg.reason = XM_DRAG_REASON (XM_DRAG_ORIGINATOR_INITIATOR, + XM_DRAG_REASON_DROP_START); + dmsg.byte_order = XM_BYTE_ORDER_CUR_FIRST; + dmsg.timestamp = FRAME_DISPLAY_INFO (f)->last_user_time; + dmsg.side_effects + = XM_DRAG_SIDE_EFFECT (xm_side_effect_from_action (FRAME_DISPLAY_INFO (f), + x_dnd_wanted_action), + XM_DROP_SITE_VALID, x_dnd_motif_operations, + XM_DROP_ACTION_DROP_CANCEL); + dmsg.x = 0; + dmsg.y = 0; + dmsg.index_atom = x_dnd_motif_atom; + dmsg.source_window = FRAME_X_WINDOW (f); + + x_dnd_send_xm_leave_for_drop (FRAME_DISPLAY_INFO (f), f, + x_dnd_last_seen_window, + FRAME_DISPLAY_INFO (f)->last_user_time); + xm_send_drop_message (FRAME_DISPLAY_INFO (f), FRAME_X_WINDOW (f), + x_dnd_last_seen_window, &dmsg); + } + + x_dnd_last_seen_window = None; + x_dnd_last_seen_toplevel = None; + x_dnd_in_progress = false; + x_dnd_waiting_for_finish = false; + x_dnd_return_frame_object = NULL; + x_dnd_movement_frame = NULL; + x_dnd_wheel_frame = NULL; + x_dnd_frame = NULL; + x_dnd_action = None; + x_dnd_action_symbol = Qnil; +} + +#endif + static void x_dnd_cleanup_drag_and_drop (void *frame) { @@ -12089,6 +12156,25 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, x_dnd_wheel_frame = NULL; x_dnd_init_type_lists = false; x_dnd_need_send_drop = false; + +#ifdef HAVE_XINPUT2 + + if (FRAME_DISPLAY_INFO (f)->supports_xi2) + { + /* Only accept input from the last master pointer to have interacted + with Emacs. This prevents another pointer device getting our + idea of the button state messed up. */ + if (FRAME_DISPLAY_INFO (f)->client_pointer_device != -1) + x_dnd_pointer_device + = FRAME_DISPLAY_INFO (f)->client_pointer_device; + else + /* This returns Bool but cannot actually fail. */ + XIGetClientPointer (FRAME_X_DISPLAY (f), None, + &x_dnd_pointer_device); + } + +#endif + #ifdef HAVE_XKB x_dnd_keyboard_state = 0; @@ -12882,6 +12968,13 @@ xi_disable_devices (struct x_display_info *dpyinfo, { if (to_disable[j] == dpyinfo->devices[i].device_id) { + if (x_dnd_in_progress + /* If the drag-and-drop pointer device is being + disabled, then cancel the drag and drop + operation. */ + && to_disable[j] == x_dnd_pointer_device) + x_dnd_cancel_dnd_early (); + /* Free any scroll valuators that might be on this device. */ #ifdef HAVE_XINPUT2_1 @@ -14164,11 +14257,13 @@ x_send_scroll_bar_event (Lisp_Object window, enum scroll_bar_part part, ev->window = FRAME_X_WINDOW (f); ev->format = 32; - /* A 32-bit X client on a 64-bit X server can pass a window pointer - as-is. A 64-bit client on a 32-bit X server is in trouble - because a pointer does not fit and would be truncated while - passing through the server. So use two slots and hope that X12 - will resolve such issues someday. */ + /* A 32-bit X client can pass a window pointer through the X server + as-is. + + A 64-bit client is in trouble because a pointer does not fit in + the 32 bits given for ClientMessage data and will be truncated by + Xlib. So use two slots and hope that X12 will resolve such + issues someday. */ ev->data.l[0] = iw >> 31 >> 1; ev->data.l[1] = sign_shift <= 0 ? iw : iw << sign_shift >> sign_shift; ev->data.l[2] = part; @@ -18465,6 +18560,11 @@ handle_one_xevent (struct x_display_info *dpyinfo, #endif if (x_dnd_in_progress + /* When _NET_WM_CLIENT_LIST stacking is being used, changes + in that property are watched for, and it's not necessary + to update the state in response to ordinary window + substructure events. */ + && !x_dnd_use_toplevels && dpyinfo == FRAME_DISPLAY_INFO (x_dnd_frame)) x_dnd_update_state (dpyinfo, dpyinfo->last_user_time); @@ -20299,6 +20399,11 @@ handle_one_xevent (struct x_display_info *dpyinfo, case CirculateNotify: if (x_dnd_in_progress + /* When _NET_WM_CLIENT_LIST stacking is being used, changes + in that property are watched for, and it's not necessary + to update the state in response to ordinary window + substructure events. */ + && !x_dnd_use_toplevels && dpyinfo == FRAME_DISPLAY_INFO (x_dnd_frame)) x_dnd_update_state (dpyinfo, dpyinfo->last_user_time); goto OTHER; @@ -20987,6 +21092,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, `x-dnd-movement-function`. */ && (command_loop_level + minibuf_level <= x_dnd_recursion_depth) + && xev->deviceid == x_dnd_pointer_device && dpyinfo == FRAME_DISPLAY_INFO (x_dnd_frame)) { Window target, toplevel; @@ -21321,6 +21427,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, if (x_dnd_in_progress && (command_loop_level + minibuf_level <= x_dnd_recursion_depth) + && xev->deviceid == x_dnd_pointer_device && dpyinfo == FRAME_DISPLAY_INFO (x_dnd_frame)) { f = mouse_or_wdesc_frame (dpyinfo, xev->event); @@ -26005,27 +26112,25 @@ x_set_window_size (struct frame *f, bool change_gravity, void frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y) { - block_input (); #ifdef HAVE_XINPUT2 int deviceid; - if (FRAME_DISPLAY_INFO (f)->supports_xi2) + deviceid = FRAME_DISPLAY_INFO (f)->client_pointer_device; + + if (FRAME_DISPLAY_INFO (f)->supports_xi2 + && deviceid != -1) { - if (XIGetClientPointer (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), - &deviceid)) - { - x_ignore_errors_for_next_request (FRAME_DISPLAY_INFO (f)); - XIWarpPointer (FRAME_X_DISPLAY (f), deviceid, None, - FRAME_X_WINDOW (f), 0, 0, 0, 0, pix_x, pix_y); - x_stop_ignoring_errors (FRAME_DISPLAY_INFO (f)); - } + block_input (); + x_ignore_errors_for_next_request (FRAME_DISPLAY_INFO (f)); + XIWarpPointer (FRAME_X_DISPLAY (f), deviceid, None, + FRAME_X_WINDOW (f), 0, 0, 0, 0, pix_x, pix_y); + x_stop_ignoring_errors (FRAME_DISPLAY_INFO (f)); + unblock_input (); } else #endif XWarpPointer (FRAME_X_DISPLAY (f), None, FRAME_X_WINDOW (f), 0, 0, 0, 0, pix_x, pix_y); - unblock_input (); } /* Raise frame F. */ -- cgit v1.2.1 From 4e23ecb882698fef45f09128300435edcb54e3d7 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sat, 13 Aug 2022 10:36:54 +0800 Subject: ; * src/xfns.c (Fx_set_mouse_absolute_pixel_position): Fix typo. --- src/xfns.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/xfns.c b/src/xfns.c index 144f64f6f62..6ed93ee42ca 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -6856,11 +6856,11 @@ The coordinates X and Y are interpreted in pixels relative to a position if (FRAME_DISPLAY_INFO (f)->supports_xi2 && deviceid != -1) { - x_catch_errors_for_lisp (FRAME_X_DISPLAY (f)); + x_catch_errors_for_lisp (FRAME_DISPLAY_INFO (f)); XIWarpPointer (FRAME_X_DISPLAY (f), deviceid, None, FRAME_DISPLAY_INFO (f)->root_window, 0, 0, 0, 0, xval, yval); - x_uncatch_errors_for_lisp (FRAME_X_DISPLAY (f)); + x_uncatch_errors_for_lisp (FRAME_DISPLAY_INFO (f)); } else #endif -- cgit v1.2.1 From 0ab572d5c991dee4818d364a65e004d20f1f45a1 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sat, 13 Aug 2022 21:12:20 +0800 Subject: Add new value `dont-save' to `deactivate-mark' * etc/NEWS: Announce new value of `deactivate-mark'. * lisp/simple.el (deactivate-mark): Handle new value. (bug#57147) * src/keyboard.c (syms_of_keyboard): Update doc string of `deactivate-mark'. --- src/keyboard.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/keyboard.c b/src/keyboard.c index 719226caedc..05c68ea7fbc 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -12650,10 +12650,14 @@ cancels any modification. */); DEFSYM (Qdeactivate_mark, "deactivate-mark"); DEFVAR_LISP ("deactivate-mark", Vdeactivate_mark, - doc: /* If an editing command sets this to t, deactivate the mark afterward. + doc: /* If an editing command sets this to t, deactivate the mark afterward. The command loop sets this to nil before each command, and tests the value when the command returns. -Buffer modification stores t in this variable. */); +Buffer modification stores t in this variable. + +By default, deactivating the mark will save the contents of the region +according to `select-active-regions'. If this is set to the symbol +`dont-save', the region will not be saved.*/); Vdeactivate_mark = Qnil; Fmake_variable_buffer_local (Qdeactivate_mark); -- cgit v1.2.1 From 94c10c426e305037126cf75cc5cf23c9f8df4664 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 13 Aug 2022 16:59:22 +0300 Subject: Speed up display of long lines under 'truncate-lines' * src/xdisp.c (partial_line_height): Return zero for long and truncated lines. (fast_move_it_horizontally): New function. (hscroll_window_tree, display_line): Use 'fast_move_it_horizontally' in preference to 'move_it_in_display_line_to', when dealing with long and truncated lines. (redisplay_internal): Optimize "optimization 3" for long and truncated lines. * src/buffer.c (syms_of_buffer) : New variable. * etc/NEWS: Announce 'large-hscroll-threshold'. --- src/buffer.c | 18 +++++++++ src/xdisp.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 132 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/buffer.c b/src/buffer.c index e5601af5051..6ab516d5f51 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -6442,6 +6442,24 @@ If nil, these display shortcuts will always remain disabled. There is no reason to change that value except for debugging purposes. */); XSETFASTINT (Vlong_line_threshold, 10000); + DEFVAR_INT ("large-hscroll-threshold", large_hscroll_threshold, + doc: /* Horizontal scroll of truncated lines above which to use redisplay shortcuts. + +The value should be a positive integer. + +Shortcuts in the display code intended to speed up redisplay for long +and truncated lines will automatically be enabled when a line's +horizontal scroll amount is or about to become larger than the value +of this variable. + +This variable has effect only in buffers which contain one or more +lines whose length is above `long-line-threshold', which see. +To disable redisplay shortcuts for long truncated line, set this +variable to `most-positive-fixnum'. + +There is no reason to change that value except for debugging purposes. */); + large_hscroll_threshold = 10000; + defsubr (&Sbuffer_live_p); defsubr (&Sbuffer_list); defsubr (&Sget_buffer); diff --git a/src/xdisp.c b/src/xdisp.c index 5268c359ecd..719b131baa1 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -11054,6 +11054,15 @@ move_it_by_lines (struct it *it, ptrdiff_t dvpos) int partial_line_height (struct it *it_origin) { + /* In a buffer with very long and truncated lines, we ignore the + possibly-partial height of the last line in the window: it is too + expensive to compute that (since in most cases that involves + going all the way to ZV), and the effect of ignoring it is + relatively minor. */ + if (XBUFFER (it_origin->w->contents)->long_line_optimizations_p + && it_origin->line_wrap == TRUNCATE) + return 0; + int partial_height; void *it_data = NULL; struct it it; @@ -11077,6 +11086,51 @@ partial_line_height (struct it *it_origin) return partial_height; } +/* Approximate move_it_in_display_line_to for very long and truncated + display lines, when moving horizontally. This is used when the + buffer's long_line_optimizations_p flag is set. It ignores various + complications, like different font sizes, invisible text, display + and overlay strings, and, to some degree, bidirectional text. So + caveat emptor! + + Starting from IT's position, reseat IT after skipping NCHARS + characters or to the next newline/ZV, whichever comes first. Return + what move_it_in_display_line_to would have returned in this case. */ + +static enum move_it_result +fast_move_it_horizontally (struct it *it, ptrdiff_t nchars) +{ + ptrdiff_t nl_bytepos; + ptrdiff_t nl_pos = find_newline_no_quit (IT_CHARPOS (*it), IT_BYTEPOS (*it), + 1, &nl_bytepos); + struct text_pos new_pos; + enum move_it_result move_result; + + if (nl_pos - IT_CHARPOS (*it) > nchars) + { + SET_TEXT_POS (new_pos, + IT_CHARPOS (*it) + nchars, + CHAR_TO_BYTE (IT_CHARPOS (*it) + nchars)); + move_result = MOVE_X_REACHED; + } + else + { + if (nl_bytepos < ZV_BYTE + || (nl_bytepos > BEGV_BYTE + && FETCH_BYTE (nl_bytepos - 1) == '\n')) + { + nl_pos--; + nl_bytepos--; + move_result = MOVE_NEWLINE_OR_CR; + } + else + move_result = MOVE_POS_MATCH_OR_ZV; + SET_TEXT_POS (new_pos, nl_pos, nl_bytepos); + } + reseat (it, new_pos, false); + return move_result; +} + /* Return true if IT points into the middle of a display vector. */ bool @@ -15790,7 +15844,20 @@ hscroll_window_tree (Lisp_Object window) it.first_visible_x = window_hscroll_limited (w, it.f) * FRAME_COLUMN_WIDTH (it.f); it.last_visible_x = DISP_INFINITY; - move_it_in_display_line_to (&it, pt, -1, MOVE_TO_POS); + + ptrdiff_t nchars = pt - IT_CHARPOS (it); + if (current_buffer->long_line_optimizations_p + && nchars > large_hscroll_threshold) + { + /* Special optimization for very long and truncated + lines which need to be hscrolled far to the left: + jump directly to the (approximate) first position + that is visible, instead of slowly walking there. */ + fast_move_it_horizontally (&it, nchars); + it.current_x += nchars * FRAME_COLUMN_WIDTH (it.f); + } + else + move_it_in_display_line_to (&it, pt, -1, MOVE_TO_POS); /* If the line ends in an overlay string with a newline, we might infloop, because displaying the window will want to put the cursor after the overlay, i.e. at X @@ -15803,7 +15870,14 @@ hscroll_window_tree (Lisp_Object window) if (hscl) it.first_visible_x = (window_hscroll_limited (w, it.f) * FRAME_COLUMN_WIDTH (it.f)); - move_it_in_display_line_to (&it, pt - 1, -1, MOVE_TO_POS); + if (current_buffer->long_line_optimizations_p + && nchars > large_hscroll_threshold) + { + fast_move_it_horizontally (&it, nchars - 1); + it.current_x += (nchars - 1) * FRAME_COLUMN_WIDTH (it.f); + } + else + move_it_in_display_line_to (&it, pt - 1, -1, MOVE_TO_POS); } current_buffer = saved_current_buffer; @@ -16741,9 +16815,23 @@ redisplay_internal (void) it.current_y = this_line_y; it.vpos = this_line_vpos; - /* The call to move_it_to stops in front of PT, but - moves over before-strings. */ - move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS); + if (current_buffer->long_line_optimizations_p + && it.line_wrap == TRUNCATE + && PT - CHARPOS (tlbufpos) > large_hscroll_threshold) + { + /* When lines are very long and truncated, jumping to + the next visible line is much faster than slowly + iterating there. */ + reseat_at_next_visible_line_start (&it, false); + if (IT_CHARPOS (it) <= PT) /* point moved off this line */ + it.vpos = this_line_vpos + 1; + } + else + { + /* The call to move_it_to stops in front of PT, but + moves over before-strings. */ + move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS); + } if (it.vpos == this_line_vpos && (row = MATRIX_ROW (w->current_matrix, this_line_vpos), @@ -24510,8 +24598,27 @@ display_line (struct it *it, int cursor_vpos) it->first_visible_x += x_incr; it->last_visible_x += x_incr; } - move_result = move_it_in_display_line_to (it, ZV, it->first_visible_x, - MOVE_TO_POS | MOVE_TO_X); + if (current_buffer->long_line_optimizations_p + && it->line_wrap == TRUNCATE + && window_hscroll_limited (it->w, it->f) > large_hscroll_threshold) + { + /* Special optimization for very long and truncated lines + which are hscrolled far to the left: jump directly to the + (approximate) position that is visible, instead of slowly + walking there. */ + ptrdiff_t chars_to_skip = + it->first_visible_x / FRAME_COLUMN_WIDTH (it->f); + enum move_it_result rc = + fast_move_it_horizontally (it, chars_to_skip); + + if (rc == MOVE_X_REACHED) + it->current_x = it->first_visible_x; + else /* use arbitrary value < first_visible_x */ + it->current_x = it->first_visible_x - FRAME_COLUMN_WIDTH (it->f); + } + else + move_result = move_it_in_display_line_to (it, ZV, it->first_visible_x, + MOVE_TO_POS | MOVE_TO_X); /* If we are under a large hscroll, move_it_in_display_line_to could hit the end of the line without reaching first_visible_x. Pretend that we did reach it. This is -- cgit v1.2.1 From a1cf3b96f84794b708a9d80281d4e9deadfb610c Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 13 Aug 2022 18:52:46 +0300 Subject: ; Fix documentation of 'deactivate-mark' * src/keyboard.c (syms_of_keyboard): * etc/NEWS: Fix the documentation of 'deactivate-mark' and its new value 'dont-save'. (Bug#57147) --- src/keyboard.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/keyboard.c b/src/keyboard.c index 05c68ea7fbc..8a2b7d58c4b 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -12650,14 +12650,17 @@ cancels any modification. */); DEFSYM (Qdeactivate_mark, "deactivate-mark"); DEFVAR_LISP ("deactivate-mark", Vdeactivate_mark, - doc: /* If an editing command sets this to t, deactivate the mark afterward. + doc: /* Whether to deactivate the mark after an editing command. The command loop sets this to nil before each command, and tests the value when the command returns. -Buffer modification stores t in this variable. +If an editing command sets this non-nil, deactivate the mark after +the command returns. + +Buffer modifications store t in this variable. By default, deactivating the mark will save the contents of the region -according to `select-active-regions'. If this is set to the symbol -`dont-save', the region will not be saved.*/); +according to `select-active-regions', unless this is set to the symbol +`dont-save'. */); Vdeactivate_mark = Qnil; Fmake_variable_buffer_local (Qdeactivate_mark); -- cgit v1.2.1 From 0a9375938529c7603f19204cd166716f63e8a640 Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Sat, 13 Aug 2022 20:30:12 +0200 Subject: Improve time-convert docstring formatting for readability * src/timefns.c (Ftime_convert): Doc fix; improve formatting for readability. --- src/timefns.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/timefns.c b/src/timefns.c index 1112f174763..8b1e729d4f4 100644 --- a/src/timefns.c +++ b/src/timefns.c @@ -1719,14 +1719,20 @@ With optional FORM, convert to that timestamp form. Truncate the returned value toward minus infinity. If FORM is nil (the default), return the same form as `current-time'. + If FORM is a positive integer, return a pair of integers (TICKS . FORM), where TICKS is the number of clock ticks and FORM is the clock frequency -in ticks per second. If FORM is t, return (TICKS . PHZ), where -PHZ is a suitable clock frequency in ticks per second. If FORM is -`integer', return an integer count of seconds. If FORM is `list', -return an integer list (HIGH LOW USEC PSEC), where HIGH has the most -significant bits of the seconds, LOW has the least significant 16 -bits, and USEC and PSEC are the microsecond and picosecond counts. */) +in ticks per second. + +If FORM is t, return (TICKS . PHZ), where PHZ is a suitable clock +frequency in ticks per second. + +If FORM is `integer', return an integer count of seconds. + +If FORM is `list', return an integer list (HIGH LOW USEC PSEC), where +HIGH has the most significant bits of the seconds, LOW has the least +significant 16 bits, and USEC and PSEC are the microsecond and +picosecond counts. */) (Lisp_Object time, Lisp_Object form) { struct lisp_time t; -- cgit v1.2.1 From 864847bf3c9f041256e32353b323ddafee86f6da Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" Date: Sat, 13 Aug 2022 21:59:39 +0300 Subject: Document time-convert FORM argument as mandatory This is a followup to commit b70369c557 of 2022-08-05 "time-convert): Deprecate calls without an explicit FORM arg". * doc/lispref/os.texi (Time Conversion): * src/timefns.c (Ftime_convert): Describe FORM argument as required as per the advertised calling convention. --- src/timefns.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/timefns.c b/src/timefns.c index 8b1e729d4f4..7db50ea81cc 100644 --- a/src/timefns.c +++ b/src/timefns.c @@ -1714,11 +1714,10 @@ usage: (encode-time TIME &rest OBSOLESCENT-ARGUMENTS) */) } DEFUN ("time-convert", Ftime_convert, Stime_convert, 1, 2, 0, - doc: /* Convert TIME value to a Lisp timestamp. -With optional FORM, convert to that timestamp form. + doc: /* Convert TIME value to a Lisp timestamp of the given FORM. Truncate the returned value toward minus infinity. -If FORM is nil (the default), return the same form as `current-time'. +If FORM is nil, return the same form as `current-time'. If FORM is a positive integer, return a pair of integers (TICKS . FORM), where TICKS is the number of clock ticks and FORM is the clock frequency -- cgit v1.2.1 From 47e23ad7a82bd707dedcd17425e89e8024b5a763 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sun, 14 Aug 2022 09:39:08 +0800 Subject: More fixes to MPX drag-and-drop interaction * src/xterm.c (x_dnd_begin_drag_and_drop): Also initialize the keyboard device used for drag-and-drop. (handle_one_xevent): Verify deviceid in more places; also test keyboard device before swallowing F1. --- src/xterm.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/xterm.c b/src/xterm.c index e48d6fd2513..6cf44e162b1 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -1402,6 +1402,9 @@ static bool x_dnd_last_tooltip_valid; /* The master pointer device being used for the drag-and-drop operation. */ static int x_dnd_pointer_device; + +/* The keyboard device attached to that pointer device. */ +static int x_dnd_keyboard_device; #endif /* Structure describing a single window that can be the target of @@ -11976,6 +11979,9 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, struct x_display_info *event_display; #endif unsigned int additional_mask; +#ifdef HAVE_XINPUT2 + struct xi_device_t *device; +#endif base = SPECPDL_INDEX (); @@ -12171,6 +12177,14 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, /* This returns Bool but cannot actually fail. */ XIGetClientPointer (FRAME_X_DISPLAY (f), None, &x_dnd_pointer_device); + + x_dnd_keyboard_device = -1; + + device = xi_device_from_id (FRAME_DISPLAY_INFO (f), + x_dnd_pointer_device); + + if (device) + x_dnd_keyboard_device = device->attachment; } #endif @@ -21000,6 +21014,12 @@ handle_one_xevent (struct x_display_info *dpyinfo, operation, don't send an event. We only have to set the user time. */ if (x_dnd_in_progress + /* If another master device moved the + pointer, we should put a wheel event on + the keyboard buffer as usual. It will be + run once the drag-and-drop operation + completes. */ + && xev->deviceid == x_dnd_pointer_device && (command_loop_level + minibuf_level <= x_dnd_recursion_depth) && dpyinfo == FRAME_DISPLAY_INFO (x_dnd_frame)) @@ -22306,7 +22326,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, inev.ie.modifiers = x_x_to_emacs_modifiers (dpyinfo, state); #ifdef XK_F1 - if (x_dnd_in_progress && keysym == XK_F1) + if (x_dnd_in_progress + && xev->deviceid == x_dnd_keyboard_device + && keysym == XK_F1) { x_dnd_xm_use_help = true; goto xi_done_keysym; -- cgit v1.2.1 From 0df8f481dde9b46c8566db6b432191f374cd578c Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sun, 14 Aug 2022 09:34:37 +0300 Subject: ; * src/xdisp.c (display_line): Fix a typo. --- src/xdisp.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/xdisp.c b/src/xdisp.c index 719b131baa1..7ee42918eb6 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -24608,10 +24608,9 @@ display_line (struct it *it, int cursor_vpos) walking there. */ ptrdiff_t chars_to_skip = it->first_visible_x / FRAME_COLUMN_WIDTH (it->f); - enum move_it_result rc = - fast_move_it_horizontally (it, chars_to_skip); + move_result = fast_move_it_horizontally (it, chars_to_skip); - if (rc == MOVE_X_REACHED) + if (move_result == MOVE_X_REACHED) it->current_x = it->first_visible_x; else /* use arbitrary value < first_visible_x */ it->current_x = it->first_visible_x - FRAME_COLUMN_WIDTH (it->f); -- cgit v1.2.1 From a71c05b44de74fe16691f680df34c4534992e472 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sun, 14 Aug 2022 15:47:59 +0300 Subject: Further speedups of redisplay of long and truncated lines * src/xdisp.c (mode_line_update_needed, redisplay_window) (decode_mode_spec): Don't avoid calling current_column, as it is now fast enough. (redisplay_window) : Don't call 'move_it_to' if its result will not be used. (Flong_line_optimizations_p): New primitive. * src/indent.c (Fcurrent_column): Doc fix. (current_column, scan_for_column): When in a buffer with long and/or truncated lines, quickly return an approximate value. * src/window.c (Frecenter): Use the old text-mode code when the buffer has very long lines. * lisp/simple.el (line-move): Avoid costly calls to 'line-move-partial' and 'line-move-visual' when lines are truncated and/or very long. (move-beginning-of-line): Call 'line-beginning-position' instead of the slower 'skip-chars-backward'. * etc/NEWS: Announce 'long-line-optimizations-p'. --- src/indent.c | 60 +++++++++++++++++++++++++++++++++++++++------ src/window.c | 9 ++++--- src/xdisp.c | 79 ++++++++++++++++++++++++++++++------------------------------ 3 files changed, 99 insertions(+), 49 deletions(-) (limited to 'src') diff --git a/src/indent.c b/src/indent.c index d2dfaee254e..cb368024d97 100644 --- a/src/indent.c +++ b/src/indent.c @@ -306,8 +306,8 @@ and point (e.g., control characters will have a width of 2 or 4, tabs will have a variable width). Ignores finite width of frame, which means that this function may return values greater than (frame-width). -In a buffer with very long lines, the value can be zero, because calculating -the exact number is very expensive. +In a buffer with very long lines, the value will be an approximation, +because calculating the exact number is very expensive. Whether the line is visible (if `selective-display' is t) has no effect; however, ^M is treated as end of line when `selective-display' is t. Text that has an invisible property is considered as having width 0, unless @@ -316,8 +316,6 @@ Text that has an invisible property is considered as having width 0, unless { Lisp_Object temp; - if (current_buffer->long_line_optimizations_p) - return make_fixnum (0); XSETFASTINT (temp, current_column ()); return temp; } @@ -346,6 +344,14 @@ current_column (void) && MODIFF == last_known_column_modified) return last_known_column; + ptrdiff_t line_beg = find_newline (PT, PT_BYTE, BEGV, BEGV_BYTE, -1, + NULL, NULL, 1); + + /* Avoid becoming abysmally slow for very long lines. */ + if (current_buffer->long_line_optimizations_p + && !NILP (Vlong_line_threshold) + && PT - line_beg > XFIXNUM (Vlong_line_threshold)) + return PT - line_beg; /* this is an approximation! */ /* If the buffer has overlays, text properties, or multibyte characters, use a more general algorithm. */ if (buffer_intervals (current_buffer) @@ -561,13 +567,53 @@ scan_for_column (ptrdiff_t *endpos, EMACS_INT *goalcol, ptrdiff_t scan, scan_byte, next_boundary, prev_pos, prev_bpos; scan = find_newline (PT, PT_BYTE, BEGV, BEGV_BYTE, -1, NULL, &scan_byte, 1); - next_boundary = scan; - prev_pos = scan; - prev_bpos = scan_byte; window = Fget_buffer_window (Fcurrent_buffer (), Qnil); w = ! NILP (window) ? XWINDOW (window) : NULL; + if (current_buffer->long_line_optimizations_p) + { + bool lines_truncated = false; + + if (!NILP (BVAR (current_buffer, truncate_lines))) + lines_truncated = true; + else if (w && FIXNUMP (Vtruncate_partial_width_windows)) + lines_truncated = + w->total_cols < XFIXNAT (Vtruncate_partial_width_windows); + else if (w && !NILP (Vtruncate_partial_width_windows)) + lines_truncated = + w->total_cols < FRAME_COLS (XFRAME (WINDOW_FRAME (w))); + /* Special optimization for buffers with long and truncated + lines: assumes that each character is a single column. */ + if (lines_truncated) + { + ptrdiff_t bolpos = scan; + /* The newline which ends this line or ZV. */ + ptrdiff_t eolpos = + find_newline (PT, PT_BYTE, ZV, ZV_BYTE, 1, NULL, NULL, 1); + + scan = bolpos + goal; + if (scan > end) + scan = end; + if (scan > eolpos) + scan = (eolpos == ZV ? ZV : eolpos - 1); + col = scan - bolpos; + if (col > large_hscroll_threshold) + { + prev_col = col - 1; + prev_pos = scan - 1; + prev_bpos = CHAR_TO_BYTE (scan); + goto endloop; + } + /* Restore the values we've overwritten above. */ + scan = bolpos; + col = 0; + } + } + next_boundary = scan; + prev_pos = scan; + prev_bpos = scan_byte; + memset (&cmp_it, 0, sizeof cmp_it); cmp_it.id = -1; composition_compute_stop_pos (&cmp_it, scan, scan_byte, end, Qnil); diff --git a/src/window.c b/src/window.c index afb8f75537b..c8fcb3a607f 100644 --- a/src/window.c +++ b/src/window.c @@ -6575,9 +6575,12 @@ and redisplay normally--don't erase and redraw the frame. */) in case scroll_margin is buffer-local. */ this_scroll_margin = window_scroll_margin (w, MARGIN_IN_LINES); - /* Don't use redisplay code for initial frames, as the necessary - data structures might not be set up yet then. */ - if (!FRAME_INITIAL_P (XFRAME (w->frame))) + /* Don't use the display code for initial frames, as the necessary + data structures might not be set up yet then. Also don't use it + for buffers with very long lines, as it tremdously slows down + redisplay, especially when lines are truncated. */ + if (!FRAME_INITIAL_P (XFRAME (w->frame)) + && !current_buffer->long_line_optimizations_p) { specpdl_ref count = SPECPDL_INDEX (); diff --git a/src/xdisp.c b/src/xdisp.c index 7ee42918eb6..0248e8e53f1 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -13174,8 +13174,7 @@ mode_line_update_needed (struct window *w) { return (w->column_number_displayed != -1 && !(PT == w->last_point && !window_outdated (w)) - && (!current_buffer->long_line_optimizations_p - && w->column_number_displayed != current_column ())); + && (w->column_number_displayed != current_column ())); } /* True if window start of W is frozen and may not be changed during @@ -19331,6 +19330,16 @@ window_start_acceptable_p (Lisp_Object window, ptrdiff_t startp) return true; } +DEFUN ("long-line-optimizations-p", Flong_line_optimizations_p, Slong_line_optimizations_p, + 0, 0, 0, + doc: /* Return non-nil if long-line optimizations are in effect in current buffer. +See `long-line-threshold' and `large-hscroll-threshold' for what these +optimizations mean and when they are in effect. */) + (void) +{ + return current_buffer->long_line_optimizations_p ? Qt : Qnil; +} + /* Redisplay leaf window WINDOW. JUST_THIS_ONE_P means only selected_window is redisplayed. @@ -19606,33 +19615,36 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) ptrdiff_t it_charpos; w->optional_new_start = false; - start_display (&it, w, startp); - move_it_to (&it, PT, 0, it.last_visible_y, -1, - MOVE_TO_POS | MOVE_TO_X | MOVE_TO_Y); - /* Record IT's position now, since line_bottom_y might change - that. */ - it_charpos = IT_CHARPOS (it); - /* Make sure we set the force_start flag only if the cursor row - will be fully visible. Otherwise, the code under force_start - label below will try to move point back into view, which is - not what the code which sets optional_new_start wants. */ - if ((it.current_y == 0 || line_bottom_y (&it) < it.last_visible_y) - && !w->force_start) - { - if (it_charpos == PT) - w->force_start = true; - /* IT may overshoot PT if text at PT is invisible. */ - else if (it_charpos > PT && CHARPOS (startp) <= PT) - w->force_start = true; + if (!w->force_start) + { + start_display (&it, w, startp); + move_it_to (&it, PT, 0, it.last_visible_y, -1, + MOVE_TO_POS | MOVE_TO_X | MOVE_TO_Y); + /* Record IT's position now, since line_bottom_y might + change that. */ + it_charpos = IT_CHARPOS (it); + /* Make sure we set the force_start flag only if the cursor + row will be fully visible. Otherwise, the code under + force_start label below will try to move point back into + view, which is not what the code which sets + optional_new_start wants. */ + if (it.current_y == 0 || line_bottom_y (&it) < it.last_visible_y) + { + if (it_charpos == PT) + w->force_start = true; + /* IT may overshoot PT if text at PT is invisible. */ + else if (it_charpos > PT && CHARPOS (startp) <= PT) + w->force_start = true; #ifdef GLYPH_DEBUG - if (w->force_start) - { - if (window_frozen_p (w)) - debug_method_add (w, "set force_start from frozen window start"); - else - debug_method_add (w, "set force_start from optional_new_start"); - } + if (w->force_start) + { + if (window_frozen_p (w)) + debug_method_add (w, "set force_start from frozen window start"); + else + debug_method_add (w, "set force_start from optional_new_start"); + } #endif + } } } @@ -20358,7 +20370,6 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) || w->base_line_pos > 0 /* Column number is displayed and different from the one displayed. */ || (w->column_number_displayed != -1 - && !current_buffer->long_line_optimizations_p && (w->column_number_displayed != current_column ()))) /* This means that the window has a mode line. */ && (window_wants_mode_line (w) @@ -27878,17 +27889,6 @@ decode_mode_spec (struct window *w, register int c, int field_width, even crash emacs.) */ if (mode_line_target == MODE_LINE_TITLE) return ""; - else if (b->long_line_optimizations_p) - { - char *p = decode_mode_spec_buf; - int pad = width - 2; - while (pad-- > 0) - *p++ = ' '; - *p++ = '?'; - *p++ = '?'; - *p = '\0'; - return decode_mode_spec_buf; - } else { ptrdiff_t col = current_column (); @@ -36232,6 +36232,7 @@ be let-bound around code that needs to disable messages temporarily. */); defsubr (&Sbidi_find_overridden_directionality); defsubr (&Sdisplay__line_is_continued_p); defsubr (&Sget_display_property); + defsubr (&Slong_line_optimizations_p); DEFSYM (Qmenu_bar_update_hook, "menu-bar-update-hook"); DEFSYM (Qoverriding_terminal_local_map, "overriding-terminal-local-map"); -- cgit v1.2.1 From 096c081e51a2e4cefb3d47f8176af8797a6cd120 Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Sun, 14 Aug 2022 16:02:34 +0200 Subject: * src/Makefile.in (elnlisp): Delete autoload.eln from list. --- src/Makefile.in | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/Makefile.in b/src/Makefile.in index 92a8790efdc..a7024bda461 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -845,7 +845,6 @@ ifeq ($(HAVE_NATIVE_COMP):$(NATIVE_DISABLED),yes:) ## List of *.eln files we need to produce in addition to the preloaded ## ones in $(lisp). elnlisp := \ - emacs-lisp/autoload.eln \ emacs-lisp/byte-opt.eln \ emacs-lisp/bytecomp.eln \ emacs-lisp/cconv.eln \ -- cgit v1.2.1 From 397fdc22eb140dd80e878528fd65e74da4033fa8 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Sun, 14 Aug 2022 10:41:40 -0400 Subject: * lisp/calendar/time-date.el (seconds-to-time): Use the original arg list --- src/timefns.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/timefns.c b/src/timefns.c index 7db50ea81cc..edfd73e9b80 100644 --- a/src/timefns.c +++ b/src/timefns.c @@ -401,6 +401,10 @@ decode_float_time (double t, struct lisp_time *result) else { int scale = double_integer_scale (t); + /* FIXME: `double_integer_scale` often returns values that are + "pessimistic" (i.e. larger than necessary), so 3.5 gets converted + to (7881299347898368 . 2251799813685248) rather than (7 . 2). + On 64bit systems, this should not matter very much, tho. */ eassume (scale < flt_radix_power_size); if (scale < 0) @@ -1717,8 +1721,6 @@ DEFUN ("time-convert", Ftime_convert, Stime_convert, 1, 2, 0, doc: /* Convert TIME value to a Lisp timestamp of the given FORM. Truncate the returned value toward minus infinity. -If FORM is nil, return the same form as `current-time'. - If FORM is a positive integer, return a pair of integers (TICKS . FORM), where TICKS is the number of clock ticks and FORM is the clock frequency in ticks per second. @@ -1731,9 +1733,14 @@ If FORM is `integer', return an integer count of seconds. If FORM is `list', return an integer list (HIGH LOW USEC PSEC), where HIGH has the most significant bits of the seconds, LOW has the least significant 16 bits, and USEC and PSEC are the microsecond and -picosecond counts. */) +picosecond counts. + +If FORM is nil, the behavior depends on `current-time-list', +but new code should not rely on it. */) (Lisp_Object time, Lisp_Object form) { + /* FIXME: Any reason why we don't offer a `float` output format option as + well, since we accept it as input? */ struct lisp_time t; enum timeform input_form = decode_lisp_time (time, false, &t, 0); if (NILP (form)) -- cgit v1.2.1 From 9664b8c8e1724af159913c511203e31f0e7d845e Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sun, 14 Aug 2022 11:11:30 -0700 Subject: Fix selection preservation bug when USE_XCB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * src/xterm.c (x_preserve_selections) [USE_XCB]: Fix bug: use of wrong ‘tem’ value. Found by GCC -Wanalyzer-use-of-uninitialized-value. Move decl of local to make the (former) bug more obvious. --- src/xterm.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/xterm.c b/src/xterm.c index 6cf44e162b1..5047f3066be 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -29140,7 +29140,7 @@ void x_preserve_selections (struct x_display_info *dpyinfo, Lisp_Object lost, Lisp_Object current_owner) { - Lisp_Object tail, frame, new_owner, tem; + Lisp_Object tail, frame, new_owner; Time timestamp; Window *owners; Atom *names; @@ -29170,7 +29170,7 @@ x_preserve_selections (struct x_display_info *dpyinfo, Lisp_Object lost, FOR_EACH_TAIL_SAFE (tail) { - tem = XCAR (tail); + Lisp_Object tem = XCAR (tail); ++nowners; /* The selection is really lost (since we cannot find a new @@ -29204,7 +29204,7 @@ x_preserve_selections (struct x_display_info *dpyinfo, Lisp_Object lost, FOR_EACH_TAIL_SAFE (tail) { - tem = XCAR (tail); + Lisp_Object tem = XCAR (tail); /* Now check if we still don't own that selection, which can happen if another program set itself as the owner. */ @@ -29224,9 +29224,10 @@ x_preserve_selections (struct x_display_info *dpyinfo, Lisp_Object lost, FOR_EACH_TAIL_SAFE (tail) { + Lisp_Object tem = XCAR (tail); + reply = xcb_get_selection_owner_reply (dpyinfo->xcb_connection, cookies[nowners++], &error); - if (reply) owners[nowners - 1] = reply->owner; else @@ -29256,7 +29257,7 @@ x_preserve_selections (struct x_display_info *dpyinfo, Lisp_Object lost, FOR_EACH_TAIL_SAFE (tail) { - tem = XCAR (tail); + Lisp_Object tem = XCAR (tail); /* If the selection isn't owned by us anymore, note that the selection was lost. */ -- cgit v1.2.1 From 23561f9665609bcb4cbe89a993a70eeccbec600e Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sun, 14 Aug 2022 13:48:11 -0700 Subject: Work around Bug#57211 * src/buffer.c (Fgenerate_new_buffer_name): Allocate a bigger buffer. --- src/buffer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/buffer.c b/src/buffer.c index 6ab516d5f51..98066a2eb60 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -1160,7 +1160,8 @@ is first appended to NAME, to speed up finding a non-existent buffer. */) genbase = name; else { - char number[sizeof "-999999"]; + enum { bug_52711 = true }; /* https://bugs.gnu.org/57211 */ + char number[bug_52711 ? INT_BUFSIZE_BOUND (int) + 1 : sizeof "-999999"]; EMACS_INT r = get_random (); eassume (0 <= r); int i = r % 1000000; -- cgit v1.2.1 From 15aba5e64496414ec4659118f910516d2dc5e8b4 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sun, 14 Aug 2022 13:48:11 -0700 Subject: Decode time conses before floats * src/timefns.c (decode_lisp_time): Test for conses before floats, as conses are more common. --- src/timefns.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/timefns.c b/src/timefns.c index edfd73e9b80..b9d9a4ed976 100644 --- a/src/timefns.c +++ b/src/timefns.c @@ -822,17 +822,6 @@ decode_lisp_time (Lisp_Object specified_time, bool decode_secs_only, if (NILP (specified_time)) form = TIMEFORM_NIL; - else if (FLOATP (specified_time)) - { - double d = XFLOAT_DATA (specified_time); - if (!isfinite (d)) - time_error (isnan (d) ? EDOM : EOVERFLOW); - if (result) - decode_float_time (d, result); - else - *dresult = d; - return TIMEFORM_FLOAT; - } else if (CONSP (specified_time)) { high = XCAR (specified_time); @@ -872,6 +861,17 @@ decode_lisp_time (Lisp_Object specified_time, bool decode_secs_only, if (! INTEGERP (low)) form = TIMEFORM_INVALID; } + else if (FLOATP (specified_time)) + { + double d = XFLOAT_DATA (specified_time); + if (!isfinite (d)) + time_error (isnan (d) ? EDOM : EOVERFLOW); + if (result) + decode_float_time (d, result); + else + *dresult = d; + return TIMEFORM_FLOAT; + } int err = decode_time_components (form, high, low, usec, psec, result, dresult); -- cgit v1.2.1 From a64fe6f31987ca4a2c8ac6b96b97c6a440aeb2a2 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sun, 14 Aug 2022 13:48:11 -0700 Subject: Improve timefns speed on integers * src/timefns.c (decode_lisp_time) [FASTER_TIMEFNS]: Speed up when SPECIFIED_TIME is an integer. (time_cmp) [FASTER_TIMEFNS]: Speed up when comparing integers. --- src/timefns.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/timefns.c b/src/timefns.c index b9d9a4ed976..eed2edf1cc0 100644 --- a/src/timefns.c +++ b/src/timefns.c @@ -861,6 +861,11 @@ decode_lisp_time (Lisp_Object specified_time, bool decode_secs_only, if (! INTEGERP (low)) form = TIMEFORM_INVALID; } + else if (FASTER_TIMEFNS && INTEGERP (specified_time)) + { + decode_ticks_hz (specified_time, make_fixnum (1), result, dresult); + return form; + } else if (FLOATP (specified_time)) { double d = XFLOAT_DATA (specified_time); @@ -1206,10 +1211,16 @@ time_cmp (Lisp_Object a, Lisp_Object b) return 0; /* Compare (X . Z) to (Y . Z) quickly if X and Y are fixnums. - Do not inspect Z, as it is OK to not signal if A and B are invalid. */ - if (FASTER_TIMEFNS && CONSP (a) && CONSP (b) && BASE_EQ (XCDR (a), XCDR (b)) - && FIXNUMP (XCAR (a)) && FIXNUMP (XCAR (b))) - return XFIXNUM (XCAR (a)) - XFIXNUM (XCAR (b)); + Do not inspect Z, as it is OK to not signal if A and B are invalid. + Also, compare X to Y quickly if X and Y are fixnums. */ + if (FASTER_TIMEFNS) + { + Lisp_Object x = a, y = b; + if (CONSP (a) && CONSP (b) && BASE_EQ (XCDR (a), XCDR (b))) + x = XCAR (a), y = XCAR (b); + if (FIXNUMP (x) && FIXNUMP (y)) + return XFIXNUM (x) - XFIXNUM (y); + } /* Compare (ATICKS . AZ) to (BTICKS . BHZ) by comparing ATICKS * BHZ to BTICKS * AHZ. */ -- cgit v1.2.1