summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDmitry Gutov <dgutov@yandex.ru>2022-08-15 02:22:59 +0300
committerDmitry Gutov <dgutov@yandex.ru>2022-08-15 02:22:59 +0300
commitee3a674c7c9e39fe7ff296ce1f9830fc45520de8 (patch)
treee8ba1e7be54314f208454e80e3d31044c913f3eb /src
parentfe0e53d963899a16e0dd1bbc1ba10a6b59f7989e (diff)
parent0a8e88fd83db5398d36064a7f87cff5b57da7284 (diff)
downloademacs-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.in1
-rw-r--r--src/buffer.c21
-rw-r--r--src/eval.c59
-rw-r--r--src/ftcrfont.c6
-rw-r--r--src/indent.c60
-rw-r--r--src/keyboard.c25
-rw-r--r--src/lisp.h2
-rw-r--r--src/timefns.c71
-rw-r--r--src/window.c9
-rw-r--r--src/xdisp.c203
-rw-r--r--src/xfns.c19
-rw-r--r--src/xterm.c250
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. */