diff options
author | Dmitry Gutov <dgutov@yandex.ru> | 2022-08-15 02:22:59 +0300 |
---|---|---|
committer | Dmitry Gutov <dgutov@yandex.ru> | 2022-08-15 02:22:59 +0300 |
commit | ee3a674c7c9e39fe7ff296ce1f9830fc45520de8 (patch) | |
tree | e8ba1e7be54314f208454e80e3d31044c913f3eb /src | |
parent | fe0e53d963899a16e0dd1bbc1ba10a6b59f7989e (diff) | |
parent | 0a8e88fd83db5398d36064a7f87cff5b57da7284 (diff) | |
download | emacs-scratch/font_lock_large_files.tar.gz |
Merge branch 'master' into scratch/font_lock_large_filesscratch/font_lock_large_files
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.in | 1 | ||||
-rw-r--r-- | src/buffer.c | 21 | ||||
-rw-r--r-- | src/eval.c | 59 | ||||
-rw-r--r-- | src/ftcrfont.c | 6 | ||||
-rw-r--r-- | src/indent.c | 60 | ||||
-rw-r--r-- | src/keyboard.c | 25 | ||||
-rw-r--r-- | src/lisp.h | 2 | ||||
-rw-r--r-- | src/timefns.c | 71 | ||||
-rw-r--r-- | src/window.c | 9 | ||||
-rw-r--r-- | src/xdisp.c | 203 | ||||
-rw-r--r-- | src/xfns.c | 19 | ||||
-rw-r--r-- | src/xterm.c | 250 |
12 files changed, 595 insertions, 131 deletions
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 \ diff --git a/src/buffer.c b/src/buffer.c index e5601af5051..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; @@ -6442,6 +6443,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/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/ftcrfont.c b/src/ftcrfont.c index 119ec284094..e089f9dea85 100644 --- a/src/ftcrfont.c +++ b/src/ftcrfont.c @@ -677,7 +677,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; 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/keyboard.c b/src/keyboard.c index 4ad6e4e6bd1..8a2b7d58c4b 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. */ @@ -12638,10 +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', unless this is set to the symbol +`dont-save'. */); Vdeactivate_mark = Qnil; Fmake_variable_buffer_local (Qdeactivate_mark); 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/timefns.c b/src/timefns.c index 1112f174763..eed2edf1cc0 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) @@ -818,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); @@ -868,6 +861,22 @@ 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); + 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); @@ -1202,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. */ @@ -1714,21 +1729,29 @@ 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 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. + +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)) 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 1fcff2c2e3b..1e8f70b2db9 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -11040,6 +11040,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; @@ -11063,6 +11072,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 @@ -13106,8 +13160,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 @@ -15776,7 +15829,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 @@ -15789,7 +15855,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; @@ -16727,9 +16800,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), @@ -18119,8 +18206,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. */ @@ -19229,6 +19316,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. @@ -19504,33 +19601,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 + } } } @@ -20256,7 +20356,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) @@ -24496,8 +24595,26 @@ 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); + move_result = fast_move_it_horizontally (it, chars_to_skip); + + 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); + } + 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 @@ -27758,17 +27875,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 (); @@ -36112,6 +36218,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"); diff --git a/src/xfns.c b/src/xfns.c index 2845ecca6a9..6ed93ee42ca 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_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_DISPLAY_INFO (f)); } else #endif diff --git a/src/xterm.c b/src/xterm.c index ab43a8ec517..5047f3066be 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -1398,6 +1398,15 @@ 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; + +/* 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 drag-and-drop operations. */ struct x_client_list_window @@ -4705,6 +4714,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) { @@ -11909,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 (); @@ -12089,6 +12162,33 @@ 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); + + 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 + #ifdef HAVE_XKB x_dnd_keyboard_state = 0; @@ -12882,6 +12982,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 +14271,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; @@ -17316,6 +17425,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 +17458,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. */ @@ -17870,6 +17986,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; @@ -17878,6 +18000,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; @@ -17904,6 +18029,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; @@ -18446,6 +18574,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); @@ -20280,6 +20413,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; @@ -20876,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)) @@ -20968,6 +21112,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; @@ -21270,7 +21415,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; } @@ -21294,6 +21447,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); @@ -21333,6 +21487,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 +21737,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 +21769,6 @@ handle_one_xevent (struct x_display_info *dpyinfo, } #endif - device = xi_device_from_id (dpyinfo, xev->deviceid); - if (!device) goto XI_OTHER; @@ -22162,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; @@ -22426,11 +22592,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; @@ -22440,8 +22609,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, @@ -22492,9 +22670,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; } @@ -23182,6 +23363,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); } @@ -25947,27 +26134,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. */ @@ -28955,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; @@ -28985,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 @@ -29019,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. */ @@ -29039,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 @@ -29071,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. */ |