From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Lars Ingebrigtsen Newsgroups: gmane.emacs.bugs Subject: bug#43379: [PATCH] Double-click events can occur without preceding single-click events Date: Fri, 12 Nov 2021 09:22:50 +0100 Message-ID: <871r3lvl45.fsf@gnus.org> References: <87zgufj0nv.fsf@gnus.org> Mime-Version: 1.0 Content-Type: text/plain Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="19450"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/29.0.50 (gnu/linux) Cc: 43379@debbugs.gnu.org To: Daniel Koning Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Fri Nov 12 09:24:13 2021 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1mlRr2-0004oD-MO for geb-bug-gnu-emacs@m.gmane-mx.org; Fri, 12 Nov 2021 09:24:12 +0100 Original-Received: from localhost ([::1]:36880 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1mlRr0-0008A6-QO for geb-bug-gnu-emacs@m.gmane-mx.org; Fri, 12 Nov 2021 03:24:10 -0500 Original-Received: from eggs.gnu.org ([209.51.188.92]:60886) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mlRqt-00089d-23 for bug-gnu-emacs@gnu.org; Fri, 12 Nov 2021 03:24:03 -0500 Original-Received: from debbugs.gnu.org ([209.51.188.43]:60068) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1mlRqs-00025U-PD for bug-gnu-emacs@gnu.org; Fri, 12 Nov 2021 03:24:02 -0500 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1mlRqs-0008N2-LR for bug-gnu-emacs@gnu.org; Fri, 12 Nov 2021 03:24:02 -0500 X-Loop: help-debbugs@gnu.org Resent-From: Lars Ingebrigtsen Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Fri, 12 Nov 2021 08:24:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 43379 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch Original-Received: via spool by 43379-submit@debbugs.gnu.org id=B43379.163670538632075 (code B ref 43379); Fri, 12 Nov 2021 08:24:02 +0000 Original-Received: (at 43379) by debbugs.gnu.org; 12 Nov 2021 08:23:06 +0000 Original-Received: from localhost ([127.0.0.1]:43375 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1mlRpx-0008LF-PN for submit@debbugs.gnu.org; Fri, 12 Nov 2021 03:23:06 -0500 Original-Received: from quimby.gnus.org ([95.216.78.240]:34786) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1mlRpu-0008KK-R9 for 43379@debbugs.gnu.org; Fri, 12 Nov 2021 03:23:04 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnus.org; s=20200322; h=Content-Type:MIME-Version:Message-ID:In-Reply-To:Date: References:Subject:Cc:To:From:Sender:Reply-To:Content-Transfer-Encoding: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=uUKJSXt+Ak0fmmcYrbSMXsWPoLEelq/NvcBez/KGZH0=; b=cCYax+EirbgPz69nCQK1AnACa5 4G2/FP2jp1mn483ZFuMKsXdn20Nj1LU4lRPcLmeO/xWyBbeyUukl0+SLJdMm4Ltnuy4IuOaJuLqxM U6Cl5zlJHJ4Uy+lGVFA6+6naUATqrUGD+IfRtludTRjw0KiILn4/W4rvnS9gfusG9Ccs=; Original-Received: from [84.212.220.105] (helo=xo) by quimby.gnus.org with esmtpsa (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1mlRpk-0005g5-CU; Fri, 12 Nov 2021 09:22:55 +0100 X-Now-Playing: Sonic Youth's _NYC Ghosts & Flowers_: "StreamXSonik Subway" In-Reply-To: <87zgufj0nv.fsf@gnus.org> (Lars Ingebrigtsen's message of "Wed, 21 Jul 2021 14:45:24 +0200") X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.io gmane.emacs.bugs:219748 Archived-At: Lars Ingebrigtsen writes: > I've just read this thread and skimmed the patches, and they seem to > make sense to me. However, when I tried to apply them to test, they > don't apply cleanly any more (since this was almost a year ago, > unfortunately). > > Do you have updated versions of the patch sets? If so, I can test and > get them pushed to Emacs 28. I've respun the patches below -- some of the code has changed considerably, so it may not be 100% correct any more. However, I can't reproduce the original bug report (in either emacs-28 or the current trunk). Here's the test case: (let ((double-click-time 500)) (require 'cl-lib) (pop-to-buffer "*Events*") (cl-loop (let ((event (read-event))) (with-current-buffer "*Events*" (insert (replace-regexp-in-string " .*" "" (prin1-to-string event)) "\n") (goto-char (point-min)))))) I can't get a single instance of a double-mouse-x without a mouse-x happening before, even with long double-click-times. Can anybody else reproduce this problem? diff --git a/doc/emacs/custom.texi b/doc/emacs/custom.texi index d9d6a68005..92044309ea 100644 --- a/doc/emacs/custom.texi +++ b/doc/emacs/custom.texi @@ -2186,8 +2186,12 @@ Mouse Buttons The symbols for mouse events also indicate the status of the modifier keys, with the usual prefixes @samp{C-}, @samp{M-}, @samp{H-}, -@samp{s-}, @samp{A-}, and @samp{S-}. These always precede @samp{double-} -or @samp{triple-}, which always precede @samp{drag-} or @samp{down-}. +@samp{s-}, @samp{A-}, and @samp{S-}. These always precede +@samp{double-} or @samp{triple-}, which always precede @samp{drag-} or +@samp{down-}. (Two clicks with different modifier keys can never +produce a double event, no matter how close together the clicks are. +Otherwise, Emacs could get a @code{double-mouse-1} event without getting +a @code{mouse-1} event first.) A frame includes areas that don't show text from the buffer, such as the mode line and the scroll bar. You can tell whether a mouse button diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index 6ed46fa6a2..d1471e9221 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -1717,9 +1717,12 @@ Repeat Events @cindex triple-click events @cindex mouse events, repeated -If you press the same mouse button more than once in quick succession -without moving the mouse, Emacs generates special @dfn{repeat} mouse -events for the second and subsequent presses. +Emacs generates special @dfn{repeat} mouse events to represent actions +like double and triple clicks. If you press a button twice in quick +succession without moving the mouse in between, Emacs will designate the +second event as a repeat. (However, if you hold down any modifier keys +during one press and not the other, Emacs will treat the two events as +unconnected, and the second event will not be a repeat.) The most common repeat events are @dfn{double-click} events. Emacs generates a double-click event when you click a button twice; the event @@ -1793,7 +1796,10 @@ Repeat Events clicks to make a double-click. This variable is also the threshold for motion of the mouse to count -as a drag. +as a drag. (But if the mouse moves from one window to another while +the button is held down, or from one screen position to another, it +always counts as a drag, no matter the value of +@code{double-click-fuzz}.) @end defopt @defopt double-click-time diff --git a/etc/NEWS b/etc/NEWS index 0057fbdcbf..234059a781 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -138,6 +138,16 @@ LRI). The new command 'highlight-confusing-reorderings' finds and highlights segments of buffer text whose reordering for display is suspicious and could be malicious. ++++ +** Repeat events are now produced only when the modifier keys are the same. +Before, when the user pressed the same mouse button repeatedly within +the bounds specified by 'double-click-fuzz' and 'double-click-time', +it always produced a 'double-' or 'triple-' event, even if the user +was holding down modifier keys on one click and not another. This +meant that it was possible for Emacs to read a double-click event +without reading the same kind of single-click event first. Emacs now +looks at modifier keys to determine if a mouse event is a repeat. + ** Emacs server and client changes. diff --git a/src/keyboard.c b/src/keyboard.c index de9805df32..c30b980ba2 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -351,6 +351,7 @@ #define READABLE_EVENTS_IGNORE_SQUEEZABLES (1 << 2) static Lisp_Object read_char_x_menu_prompt (Lisp_Object, Lisp_Object, bool *); static Lisp_Object read_char_minibuf_menu_prompt (int, Lisp_Object); +static intmax_t mouse_repeat_count (const struct input_event *); static Lisp_Object make_lispy_event (struct input_event *); static Lisp_Object make_lispy_movement (struct frame *, Lisp_Object, enum scroll_bar_part, @@ -5062,18 +5063,6 @@ #define ISO_FUNCTION_KEY_OFFSET 0xfe00 the down mouse event. */ static Lisp_Object frame_relative_event_pos; -/* Information about the most recent up-going button event: Which - button, what location, and what time. */ - -static int last_mouse_button; -static int last_mouse_x; -static int last_mouse_y; -static Time button_down_time; - -/* The number of clicks in this multiple-click. */ - -static int double_click_count; - /* X and Y are frame-relative coordinates for a click or wheel event. Return a Lisp-style event list. */ @@ -5388,6 +5377,139 @@ make_scroll_bar_position (struct input_event *ev, Lisp_Object type) builtin_lisp_symbol (scroll_bar_parts[ev->part])); } +/* Whether the next click (or wheel event) should be treated as a + single no matter what, even if a matching prior event would make it + a repeat. This gets flipped on when a keystroke or drag event + comes in: double clicks can't extend across one of those. */ + +static bool interrupt_next_repeat_click; + +/* If EVENT is a repeat, return the number of consecutive mouse events + so far. Return 1 otherwise. In the background, keep a record of + where we are in the current sequence of repeats. + + This should only receive a pointer to an event of an applicable + kind -- namely, a button-down, button-up, or wheel event. */ + +static intmax_t +mouse_repeat_count (const struct input_event *event) +{ + /* Keep persistent information about any chain of repeat events we + might be in the middle of. */ + + static ENUM_BF (event_kind) kind; + static unsigned code; + static unsigned keys; + static unsigned wheel_dir; + + static Time timeout_start; + static EMACS_INT x, y; + + static intmax_t count; + + /* Analyze the new event that came in. */ + + unsigned new_keys = event->modifiers & CHAR_MODIFIER_MASK; + unsigned new_wheel_dir; + + Time interval; + EMACS_INT new_x, new_y, offset; + + bool is_wheel = (event->kind == WHEEL_EVENT + || event->kind == HORIZ_WHEEL_EVENT); + bool is_button = !is_wheel; + bool is_button_up = (is_button && (event->modifiers & up_modifier)); + + if (is_wheel) + new_wheel_dir = (event->modifiers & (down_modifier | up_modifier)); + else + new_wheel_dir = 0; + + /* We need to update timestamp and position after both repeat and + non-repeat events. Make the relevant comparisons in advance, + then do the update immediately. */ + + interval = event->timestamp - timeout_start; + if (!is_button_up) + timeout_start = event->timestamp; + + new_x = XFIXNUM (event->x); + new_y = XFIXNUM (event->y); + offset = max (eabs (new_x - x), eabs (new_y - y)); + x = new_x; + y = new_y; + + /* Is this a repeat? Start checking for conditions that imply + otherwise. */ + + if (interrupt_next_repeat_click && !is_button_up) + { + interrupt_next_repeat_click = false; + goto not_a_repeat; + } + + if (event->kind != kind || event->code != code || new_keys != keys + || new_wheel_dir != wheel_dir) + goto not_a_repeat; + + if (is_button_up) + /* That's it for button-up events. At this point, we declare that + it's a repeat that should inherit the count of the preceding + button-down. */ + return count; + + if (FIXNATP (Vdouble_click_time)) + { + if (interval >= XFIXNAT (Vdouble_click_time)) + goto not_a_repeat; + } + else if (!EQ (Vdouble_click_time, Qt)) + goto not_a_repeat; + + { + /* On window-system frames, use the value of 'double-click-fuzz' + as is. On other frames, interpret it as a multiple of 1/8 + characters. */ + struct frame *f; + intmax_t fuzz; + + if (WINDOWP (event->frame_or_window)) + f = XFRAME (XWINDOW (event->frame_or_window)->frame); + else if (FRAMEP (event->frame_or_window)) + f = XFRAME (event->frame_or_window); + else + emacs_abort (); + + if (FRAME_WINDOW_P (f)) + fuzz = double_click_fuzz; + else + fuzz = double_click_fuzz / 8; + + if (offset > fuzz) + goto not_a_repeat; + } + + /* We've ruled out everything that could disqualify it as a repeat, + so treat it as one. */ + count++; + return count; + + not_a_repeat: + /* Base a new repeat chain off of this event. */ + count = 1; + kind = event->kind; + code = event->code; + keys = new_keys; + wheel_dir = new_wheel_dir; + + if (is_button_up) + /* A button-up event should cut off a chain when it doesn't match + the last event, but it shouldn't start its own chain. */ + interrupt_next_repeat_click = true; + + return count; +} + /* Given a struct input_event, build the lisp event which represents it. If EVENT is 0, build a mouse movement event from the mouse movement buffer, which should have a movement event in it. @@ -5512,7 +5634,7 @@ make_lispy_event (struct input_event *event) if ((event->code) == 040 && event->modifiers & shift_modifier) c |= shift_modifier; - button_down_time = 0; + interrupt_next_repeat_click = true; XSETFASTINT (lispy_c, c); return lispy_c; } @@ -5531,7 +5653,7 @@ make_lispy_event (struct input_event *event) /* A function key. The symbol may need to have modifier prefixes tacked onto it. */ case NON_ASCII_KEYSTROKE_EVENT: - button_down_time = 0; + interrupt_next_repeat_click = true; for (i = 0; i < ARRAYELTS (lispy_accent_codes); i++) if (event->code == lispy_accent_codes[i]) @@ -5617,10 +5739,10 @@ make_lispy_event (struct input_event *event) #endif { int button = event->code; - bool is_double; Lisp_Object position; Lisp_Object *start_pos_ptr; Lisp_Object start_pos; + int repeat_count; position = Qnil; @@ -5709,57 +5831,20 @@ make_lispy_event (struct input_event *event) mouse_syms = larger_vector (mouse_syms, incr, -1); } + repeat_count = mouse_repeat_count (event); + if (repeat_count == 2) + event->modifiers |= double_modifier; + else if (repeat_count >= 3) + event->modifiers |= triple_modifier; + start_pos_ptr = aref_addr (button_down_location, button); start_pos = *start_pos_ptr; *start_pos_ptr = Qnil; - { - /* On window-system frames, use the value of - double-click-fuzz as is. On other frames, interpret it - as a multiple of 1/8 characters. */ - struct frame *f; - intmax_t fuzz; - - if (WINDOWP (event->frame_or_window)) - f = XFRAME (XWINDOW (event->frame_or_window)->frame); - else if (FRAMEP (event->frame_or_window)) - f = XFRAME (event->frame_or_window); - else - emacs_abort (); - - if (FRAME_WINDOW_P (f)) - fuzz = double_click_fuzz; - else - fuzz = double_click_fuzz / 8; - - is_double = (button == last_mouse_button - && (eabs (XFIXNUM (event->x) - last_mouse_x) <= fuzz) - && (eabs (XFIXNUM (event->y) - last_mouse_y) <= fuzz) - && button_down_time != 0 - && (EQ (Vdouble_click_time, Qt) - || (FIXNATP (Vdouble_click_time) - && (event->timestamp - button_down_time - < XFIXNAT (Vdouble_click_time))))); - } - - last_mouse_button = button; - last_mouse_x = XFIXNUM (event->x); - last_mouse_y = XFIXNUM (event->y); - /* If this is a button press, squirrel away the location, so we can decide later whether it was a click or a drag. */ if (event->modifiers & down_modifier) { - if (is_double) - { - double_click_count++; - event->modifiers |= ((double_click_count > 2) - ? triple_modifier - : double_modifier); - } - else - double_click_count = 1; - button_down_time = event->timestamp; *start_pos_ptr = Fcopy_alist (position); frame_relative_event_pos = Fcons (event->x, event->y); ignore_mouse_drag_p = false; @@ -5796,6 +5881,8 @@ make_lispy_event (struct input_event *event) && xdiff < double_click_fuzz && - double_click_fuzz < ydiff && ydiff < double_click_fuzz + /* If we jumped windows, it has to be a drag. */ + && EQ (POSN_WINDOW (start_pos), POSN_WINDOW (position)) /* Maybe the mouse has moved a lot, caused scrolling, and eventually ended up at the same screen position (but not buffer position) in which case it is a drag, not @@ -5812,7 +5899,7 @@ make_lispy_event (struct input_event *event) Fcar (position))))) /* Different window */ { /* Mouse has moved enough. */ - button_down_time = 0; + interrupt_next_repeat_click = true; click_or_drag_modifier = drag_modifier; } else if (((!EQ (Fcar (start_pos), Fcar (position))) @@ -5853,14 +5940,8 @@ make_lispy_event (struct input_event *event) } } - /* Don't check is_double; treat this as multiple if the - down-event was multiple. */ - event->modifiers - = ((event->modifiers & ~up_modifier) - | click_or_drag_modifier - | (double_click_count < 2 ? 0 - : double_click_count == 2 ? double_modifier - : triple_modifier)); + event->modifiers = ((event->modifiers & ~up_modifier) + | click_or_drag_modifier); } else /* Every mouse event should either have the down_modifier or @@ -5880,7 +5961,7 @@ make_lispy_event (struct input_event *event) if (event->modifiers & drag_modifier) return list3 (head, start_pos, position); else if (event->modifiers & (double_modifier | triple_modifier)) - return list3 (head, position, make_fixnum (double_click_count)); + return list3 (head, position, make_fixnum (repeat_count)); else return list2 (head, position); } @@ -5891,6 +5972,7 @@ make_lispy_event (struct input_event *event) { Lisp_Object position; Lisp_Object head; + int repeat_count; /* Build the position as appropriate for this mouse click. */ struct frame *f = XFRAME (event->frame_or_window); @@ -5903,38 +5985,15 @@ make_lispy_event (struct input_event *event) position = make_lispy_position (f, event->x, event->y, event->timestamp); - /* Set double or triple modifiers to indicate the wheel speed. */ { - /* On window-system frames, use the value of - double-click-fuzz as is. On other frames, interpret it - as a multiple of 1/8 characters. */ - struct frame *fr; - intmax_t fuzz; int symbol_num; - bool is_double; - - if (WINDOWP (event->frame_or_window)) - fr = XFRAME (XWINDOW (event->frame_or_window)->frame); - else if (FRAMEP (event->frame_or_window)) - fr = XFRAME (event->frame_or_window); - else - emacs_abort (); - - fuzz = FRAME_WINDOW_P (fr) - ? double_click_fuzz : double_click_fuzz / 8; if (event->modifiers & up_modifier) - { /* Emit a wheel-up event. */ - event->modifiers &= ~up_modifier; - symbol_num = 0; - } + symbol_num = 0; else if (event->modifiers & down_modifier) - { /* Emit a wheel-down event. */ - event->modifiers &= ~down_modifier; - symbol_num = 1; - } + symbol_num = 1; else /* Every wheel event should either have the down_modifier or the up_modifier set. */ @@ -5943,32 +6002,20 @@ make_lispy_event (struct input_event *event) if (event->kind == HORIZ_WHEEL_EVENT) symbol_num += 2; - is_double = (last_mouse_button == - (1 + symbol_num) - && (eabs (XFIXNUM (event->x) - last_mouse_x) <= fuzz) - && (eabs (XFIXNUM (event->y) - last_mouse_y) <= fuzz) - && button_down_time != 0 - && (EQ (Vdouble_click_time, Qt) - || (FIXNATP (Vdouble_click_time) - && (event->timestamp - button_down_time - < XFIXNAT (Vdouble_click_time))))); - if (is_double) - { - double_click_count++; - event->modifiers |= ((double_click_count > 2) - ? triple_modifier - : double_modifier); - } + /* Set double or triple modifiers to indicate the wheel + speed. */ + repeat_count = mouse_repeat_count (event); + if (repeat_count == 2) + event->modifiers |= double_modifier; + else if (repeat_count >= 3) + event->modifiers |= triple_modifier; else - { - double_click_count = 1; - event->modifiers |= click_modifier; - } + /* Non-repeat wheel events are tagged as clicks. */ + event->modifiers |= click_modifier; - button_down_time = event->timestamp; - /* Use a negative value to distinguish wheel from mouse button. */ - last_mouse_button = - (1 + symbol_num); - last_mouse_x = XFIXNUM (event->x); - last_mouse_y = XFIXNUM (event->y); + /* The Lisp side expects to see direction information in + 'symbol_num', but not in the modifier bits. */ + event->modifiers &= ~(down_modifier | up_modifier); /* Get the symbol we should use for the wheel event. */ head = modify_event_symbol (symbol_num, @@ -5981,10 +6028,10 @@ make_lispy_event (struct input_event *event) } if (NUMBERP (event->arg)) - return list4 (head, position, make_fixnum (double_click_count), + return list4 (head, position, make_fixnum (repeat_count), event->arg); else if (event->modifiers & (double_modifier | triple_modifier)) - return list3 (head, position, make_fixnum (double_click_count)); + return list3 (head, position, make_fixnum (repeat_count)); else return list2 (head, position); } diff --git a/src/nsterm.m b/src/nsterm.m index 1f17a30272..b11fcb5ed5 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -291,6 +291,7 @@ - (NSColor *)colorUsingDefaultColorSpace static NSAutoreleasePool *outerpool; static struct input_event *emacs_event = NULL; static struct input_event *q_event_ptr = NULL; +static int last_real_wheel_modifier_keys = 0; static int n_emacs_events_pending = 0; static NSMutableArray *ns_pending_files, *ns_pending_service_names, *ns_pending_service_args; @@ -6559,8 +6560,13 @@ - (void)mouseDown: (NSEvent *)theEvent int lines = 0; int scrollUp = NO; - /* FIXME: At the top or bottom of the buffer we should - * ignore momentum-phase events. */ + /* FIXME: At the top or bottom of the buffer, we should + * ignore momentum-phase events that are bound to scrolling + * the buffer down or up respectively. But since this code + * doesn't know about bindings and the keymap code doesn't + * know about wheel momentum, that doesn't seem to be + * possible yet. */ + if (! ns_use_mwheel_momentum && [theEvent momentumPhase] != NSEventPhaseNone) return; @@ -6645,6 +6651,21 @@ - (void)mouseDown: (NSEvent *)theEvent emacs_event->code = 0; emacs_event->modifiers = EV_MODIFIERS (theEvent) | (scrollUp ? up_modifier : down_modifier); + + /* If this is a momentum-phase event, ignore the modifier + * keys it arrived with. Inherit the modifier keys from the + * last non-momentum event in the sequence. The user may be + * pressing or releasing modifier keys, intending to use + * them in the next event, while the wheel events are still + * firing. */ + if ([theEvent momentumPhase] != NSEventPhaseNone) + { + emacs_event->modifiers &= ~CHAR_MODIFIER_MASK; + emacs_event->modifiers |= last_real_wheel_modifier_keys; + } + else + last_real_wheel_modifier_keys = + emacs_event->modifiers & CHAR_MODIFIER_MASK; #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 } else -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no