blob: 88872d9b22641f6d956c5ad603d80b078b719a6b [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2015 The Chromium Authors
dtapuskaedc082c2015-09-17 12:17:012// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/*
6 * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
7 * Copyright (C) 2006-2009 Google Inc.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
22 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
26 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
Kartar Singh746606522024-05-29 09:34:3031#include "components/input/web_input_event_builders_mac.h"
dtapuskaedc082c2015-09-17 12:17:0132
33#import <ApplicationServices/ApplicationServices.h>
34#import <Cocoa/Cocoa.h>
35
avib533f5d2015-12-25 03:11:1536#include <stdint.h>
37
Avi Drissman9306606f2023-05-26 22:43:5038#include "base/apple/owned_objc.h"
James Howarda07e1492020-06-26 15:34:0839#include "base/mac/mac_util.h"
Annie Sullivan14243de2020-01-31 21:47:5740#include "base/metrics/histogram_macros.h"
dtapuskaedc082c2015-09-17 12:17:0141#include "base/strings/string_util.h"
Daniel Cheng224569ee2018-04-25 05:45:0642#include "base/time/time.h"
Dave Tapuska129cef82019-12-19 16:36:4843#include "third_party/blink/public/common/input/web_input_event.h"
Daniel Cheng224569ee2018-04-25 05:45:0644#include "ui/events/base_event_utils.h"
jonross2addcb72016-08-18 21:48:5445#include "ui/events/blink/blink_event_util.h"
tapteda3d3da132016-08-11 06:04:4046#import "ui/events/cocoa/cocoa_event_utils.h"
Annie Sullivan14243de2020-01-31 21:47:5747#include "ui/events/event_utils.h"
dtapuskaedc082c2015-09-17 12:17:0148#include "ui/events/keycodes/keyboard_code_conversion.h"
49#include "ui/events/keycodes/keyboard_code_conversion_mac.h"
50
Kartar Singh5c8e0b22024-05-30 10:32:1451namespace input {
dtapuskaedc082c2015-09-17 12:17:0152
53namespace {
54
chongzc8bec732015-12-21 21:35:0555inline NSString* FilterSpecialCharacter(NSString* str) {
56 if ([str length] != 1)
57 return str;
58 unichar c = [str characterAtIndex:0];
59 NSString* result = str;
60 if (c == 0x7F) {
61 // Backspace should be 8
62 result = @"\x8";
63 } else if (c >= 0xF700 && c <= 0xF7FF) {
64 // Mac private use characters should be @"\0" (@"" won't work)
65 // NSDeleteFunctionKey will also go into here
66 // Use the range 0xF700~0xF7FF to match
67 // http://www.opensource.apple.com/source/WebCore/WebCore-7601.1.55/platform/mac/KeyEventMac.mm
68 result = @"\0";
69 }
70 return result;
71}
72
dtapuskaedc082c2015-09-17 12:17:0173inline NSString* TextFromEvent(NSEvent* event) {
Avi Drissman8be81112022-05-11 01:12:4274 if ([event type] == NSEventTypeFlagsChanged)
dtapuskaedc082c2015-09-17 12:17:0175 return @"";
chongzc8bec732015-12-21 21:35:0576 return FilterSpecialCharacter([event characters]);
dtapuskaedc082c2015-09-17 12:17:0177}
78
79inline NSString* UnmodifiedTextFromEvent(NSEvent* event) {
Avi Drissman8be81112022-05-11 01:12:4280 if ([event type] == NSEventTypeFlagsChanged)
dtapuskaedc082c2015-09-17 12:17:0181 return @"";
chongzc8bec732015-12-21 21:35:0582 return FilterSpecialCharacter([event charactersIgnoringModifiers]);
dtapuskaedc082c2015-09-17 12:17:0183}
84
dtapuskaedc082c2015-09-17 12:17:0185// End Apple code.
86// ----------------------------------------------------------------------------
87
88int ModifiersFromEvent(NSEvent* event) {
89 int modifiers = 0;
Jayson Adams085b2b412022-06-23 16:47:2790 NSEventModifierFlags modifier_flags = [event modifierFlags];
dtapuskaedc082c2015-09-17 12:17:0191
Jayson Adams085b2b412022-06-23 16:47:2792 if (modifier_flags & NSEventModifierFlagControl)
Blink Reformat1c4d759e2017-04-09 16:34:5493 modifiers |= blink::WebInputEvent::kControlKey;
Jayson Adams085b2b412022-06-23 16:47:2794 if (modifier_flags & NSEventModifierFlagShift)
Blink Reformat1c4d759e2017-04-09 16:34:5495 modifiers |= blink::WebInputEvent::kShiftKey;
Jayson Adams085b2b412022-06-23 16:47:2796 if (modifier_flags & NSEventModifierFlagOption)
Blink Reformat1c4d759e2017-04-09 16:34:5497 modifiers |= blink::WebInputEvent::kAltKey;
Jayson Adams085b2b412022-06-23 16:47:2798 if (modifier_flags & NSEventModifierFlagCommand)
Blink Reformat1c4d759e2017-04-09 16:34:5499 modifiers |= blink::WebInputEvent::kMetaKey;
Jayson Adams085b2b412022-06-23 16:47:27100 if (modifier_flags & NSEventModifierFlagCapsLock)
Blink Reformat1c4d759e2017-04-09 16:34:54101 modifiers |= blink::WebInputEvent::kCapsLockOn;
dtapuskaedc082c2015-09-17 12:17:01102
103 // The return value of 1 << 0 corresponds to the left mouse button,
104 // 1 << 1 corresponds to the right mouse button,
105 // 1 << n, n >= 2 correspond to other mouse buttons.
106 NSUInteger pressed_buttons = [NSEvent pressedMouseButtons];
107
108 if (pressed_buttons & (1 << 0))
Blink Reformat1c4d759e2017-04-09 16:34:54109 modifiers |= blink::WebInputEvent::kLeftButtonDown;
dtapuskaedc082c2015-09-17 12:17:01110 if (pressed_buttons & (1 << 1))
Blink Reformat1c4d759e2017-04-09 16:34:54111 modifiers |= blink::WebInputEvent::kRightButtonDown;
dtapuskaedc082c2015-09-17 12:17:01112 if (pressed_buttons & (1 << 2))
Blink Reformat1c4d759e2017-04-09 16:34:54113 modifiers |= blink::WebInputEvent::kMiddleButtonDown;
Lan Wei2bddb8c2019-08-09 15:39:26114 if (pressed_buttons & (1 << 3))
115 modifiers |= blink::WebInputEvent::kBackButtonDown;
116 if (pressed_buttons & (1 << 4))
117 modifiers |= blink::WebInputEvent::kForwardButtonDown;
dtapuskaedc082c2015-09-17 12:17:01118
119 return modifiers;
120}
121
122void SetWebEventLocationFromEventInView(blink::WebMouseEvent* result,
123 NSEvent* event,
James Howarda07e1492020-06-26 15:34:08124 NSView* view,
125 bool unacceleratedMovement = false) {
Avi Drissmanceeb0f22023-03-17 16:44:44126 NSPoint screen_local =
127 [view.window convertPointToScreen:event.locationInWindow];
dtapuskaedc082c2015-09-17 12:17:01128 NSScreen* primary_screen = ([[NSScreen screens] count] > 0)
ccameron741debc2015-10-07 19:42:18129 ? [[NSScreen screens] firstObject]
dtapuskaedc082c2015-09-17 12:17:01130 : nil;
Avi Drissmanceeb0f22023-03-17 16:44:44131
mustaqc51f3aab2017-04-05 15:43:11132 // Flip y conditionally.
Blink Reformat1c4d759e2017-04-09 16:34:54133 result->SetPositionInScreen(
mustaqc51f3aab2017-04-05 15:43:11134 screen_local.x, primary_screen
135 ? [primary_screen frame].size.height - screen_local.y
136 : screen_local.y);
dtapuskaedc082c2015-09-17 12:17:01137
erikchen51f7d2442016-03-11 05:19:17138 NSPoint content_local =
139 [view convertPoint:[event locationInWindow] fromView:nil];
mustaqc51f3aab2017-04-05 15:43:11140 // Flip y.
Blink Reformat1c4d759e2017-04-09 16:34:54141 result->SetPositionInWidget(content_local.x,
mustaqc51f3aab2017-04-05 15:43:11142 [view frame].size.height - content_local.y);
dtapuskaedc082c2015-09-17 12:17:01143
James Howarda07e1492020-06-26 15:34:08144 CGEventRef cgEvent = nullptr;
145 if (unacceleratedMovement && (cgEvent = [event CGEvent]) != nullptr) {
James Howarda07e1492020-06-26 15:34:08146 result->movement_x = CGEventGetIntegerValueField(
147 cgEvent, kCGEventUnacceleratedPointerMovementX);
148 result->movement_y = CGEventGetIntegerValueField(
149 cgEvent, kCGEventUnacceleratedPointerMovementY);
150 result->is_raw_movement_event = true;
151 } else {
152 result->movement_x = [event deltaX];
153 result->movement_y = [event deltaY];
154 }
dtapuskaedc082c2015-09-17 12:17:01155}
156
157bool IsSystemKeyEvent(const blink::WebKeyboardEvent& event) {
158 // Windows and Linux set |isSystemKey| if alt is down. Blink looks at this
159 // flag to decide if it should handle a key or not. E.g. alt-left/right
160 // shouldn't be used by Blink to scroll the current page, because we want
161 // to get that key back for it to do history navigation. Hence, the
162 // corresponding situation on OS X is to set this for cmd key presses.
joleksy914cede2015-09-29 19:40:00163
dtapuskaedc082c2015-09-17 12:17:01164 // cmd-b and and cmd-i are system wide key bindings that OS X doesn't
165 // handle for us, so the editor handles them.
Blink Reformat1c4d759e2017-04-09 16:34:54166 int modifiers = event.GetModifiers() & blink::WebInputEvent::kInputModifiers;
167 if (modifiers == blink::WebInputEvent::kMetaKey &&
168 event.windows_key_code == ui::VKEY_B)
joleksy914cede2015-09-29 19:40:00169 return false;
Blink Reformat1c4d759e2017-04-09 16:34:54170 if (modifiers == blink::WebInputEvent::kMetaKey &&
171 event.windows_key_code == ui::VKEY_I)
joleksy914cede2015-09-29 19:40:00172 return false;
173
Blink Reformat1c4d759e2017-04-09 16:34:54174 return event.GetModifiers() & blink::WebInputEvent::kMetaKey;
dtapuskaedc082c2015-09-17 12:17:01175}
176
177blink::WebMouseWheelEvent::Phase PhaseForNSEventPhase(
178 NSEventPhase event_phase) {
Blink Reformat1c4d759e2017-04-09 16:34:54179 uint32_t phase = blink::WebMouseWheelEvent::kPhaseNone;
dtapuskaedc082c2015-09-17 12:17:01180 if (event_phase & NSEventPhaseBegan)
Blink Reformat1c4d759e2017-04-09 16:34:54181 phase |= blink::WebMouseWheelEvent::kPhaseBegan;
dtapuskaedc082c2015-09-17 12:17:01182 if (event_phase & NSEventPhaseStationary)
Blink Reformat1c4d759e2017-04-09 16:34:54183 phase |= blink::WebMouseWheelEvent::kPhaseStationary;
dtapuskaedc082c2015-09-17 12:17:01184 if (event_phase & NSEventPhaseChanged)
Blink Reformat1c4d759e2017-04-09 16:34:54185 phase |= blink::WebMouseWheelEvent::kPhaseChanged;
dtapuskaedc082c2015-09-17 12:17:01186 if (event_phase & NSEventPhaseEnded)
Blink Reformat1c4d759e2017-04-09 16:34:54187 phase |= blink::WebMouseWheelEvent::kPhaseEnded;
dtapuskaedc082c2015-09-17 12:17:01188 if (event_phase & NSEventPhaseCancelled)
Blink Reformat1c4d759e2017-04-09 16:34:54189 phase |= blink::WebMouseWheelEvent::kPhaseCancelled;
dtapuskaedc082c2015-09-17 12:17:01190 if (event_phase & NSEventPhaseMayBegin)
Blink Reformat1c4d759e2017-04-09 16:34:54191 phase |= blink::WebMouseWheelEvent::kPhaseMayBegin;
dtapuskaedc082c2015-09-17 12:17:01192 return static_cast<blink::WebMouseWheelEvent::Phase>(phase);
193}
194
195blink::WebMouseWheelEvent::Phase PhaseForEvent(NSEvent* event) {
dtapuskaedc082c2015-09-17 12:17:01196 NSEventPhase event_phase = [event phase];
197 return PhaseForNSEventPhase(event_phase);
198}
199
200blink::WebMouseWheelEvent::Phase MomentumPhaseForEvent(NSEvent* event) {
dtapuskaedc082c2015-09-17 12:17:01201 NSEventPhase event_momentum_phase = [event momentumPhase];
202 return PhaseForNSEventPhase(event_momentum_phase);
203}
204
dtapuskadee64e42015-09-24 02:47:07205ui::DomKey DomKeyFromEvent(NSEvent* event) {
206 ui::DomKey key = ui::DomKeyFromNSEvent(event);
207 if (key != ui::DomKey::NONE)
208 return key;
209 return ui::DomKey::UNIDENTIFIED;
210}
211
Ella Gef2599af2017-09-06 13:27:17212blink::WebMouseEvent::Button ButtonFromPressedMouseButtons() {
213 NSUInteger pressed_buttons = [NSEvent pressedMouseButtons];
214
215 if (pressed_buttons & (1 << 0))
216 return blink::WebMouseEvent::Button::kLeft;
217 if (pressed_buttons & (1 << 1))
218 return blink::WebMouseEvent::Button::kRight;
219 if (pressed_buttons & (1 << 2))
220 return blink::WebMouseEvent::Button::kMiddle;
Lan Wei2bddb8c2019-08-09 15:39:26221 if (pressed_buttons & (1 << 3))
222 return blink::WebMouseEvent::Button::kBack;
223 if (pressed_buttons & (1 << 4))
224 return blink::WebMouseEvent::Button::kForward;
225 return blink::WebMouseEvent::Button::kNoButton;
226}
227blink::WebMouseEvent::Button ButtonFromButtonNumber(NSEvent* event) {
228 NSUInteger button_number = [event buttonNumber];
229
230 if (button_number == 1)
231 return blink::WebMouseEvent::Button::kRight;
232 if (button_number == 2)
233 return blink::WebMouseEvent::Button::kMiddle;
234 if (button_number == 3)
235 return blink::WebMouseEvent::Button::kBack;
236 if (button_number == 4)
237 return blink::WebMouseEvent::Button::kForward;
Ella Gef2599af2017-09-06 13:27:17238 return blink::WebMouseEvent::Button::kNoButton;
239}
240
dtapuskaedc082c2015-09-17 12:17:01241} // namespace
242
Joe Mason74bec922022-03-16 09:25:19243blink::WebKeyboardEvent WebKeyboardEventBuilder::Build(NSEvent* event) {
Avi Drissman9306606f2023-05-26 22:43:50244 ui::ComputeEventLatencyOS(base::apple::OwnedNSEvent(event));
David Bokan87b556b12020-07-21 19:14:04245
dtapuska8c4dae12017-01-13 00:23:06246 ui::DomCode dom_code = ui::DomCodeFromNSEvent(event);
247 int modifiers =
248 ModifiersFromEvent(event) | ui::DomCodeToWebInputEventModifiers(dom_code);
dtapuskaedc082c2015-09-17 12:17:01249
Avi Drissman9306606f2023-05-26 22:43:50250 if ((event.type != NSEventTypeFlagsChanged) && event.ARepeat) {
Blink Reformat1c4d759e2017-04-09 16:34:54251 modifiers |= blink::WebInputEvent::kIsAutoRepeat;
Avi Drissman9306606f2023-05-26 22:43:50252 }
dtapuskaedc082c2015-09-17 12:17:01253
Daniel Cheng224569ee2018-04-25 05:45:06254 blink::WebKeyboardEvent result(
Dave Tapuskab5a72eb2020-04-21 22:12:56255 ui::IsKeyUpEvent(event) ? blink::WebInputEvent::Type::kKeyUp
256 : blink::WebInputEvent::Type::kRawKeyDown,
Avi Drissman9306606f2023-05-26 22:43:50257 modifiers, ui::EventTimeStampFromSeconds(event.timestamp));
Jayson Adamse1325a1e2022-08-08 17:36:19258
259 // Some keys have the same meaning but different locations on the keyboard:
260 // the left and right shift keys; the numeric keypad keys and their
261 // counterparts in the number row; etc. A "located" keyboard code lets us
262 // distinguish between keys with the same meaning. For example, VKEY_LSHIFT
263 // and VKEY_RSHIFT are located keyboard codes and VKEY_SHIFT is their non-
264 // located representation.
265 //
266 // When determining the windows_key_code, we want to use the non-located code
267 // for some keys (Shift, etc.). We call ui::LocatedToNonLocatedKeyboardCode()
268 // to perform this conversion. However, ui::LocatedToNonLocatedKeyboardCode()
269 // converts more keys than we'd like. In particular, it returns the
270 // non-located representations of number pad key codes. If we use these as
271 // windows key codes, key presses in the number row and the number pad will be
272 // indistinguishable (see https://crbug.com/1282730). To avoid this, when we
273 // encounter a number pad key, we'll use the located key_code itself rather
274 // than its non-located counterpart.
275 ui::KeyboardCode key_code = ui::KeyboardCodeFromNSEvent(event);
276 bool is_numeric_keypad_keycode =
277 key_code >= ui::VKEY_NUMPAD0 && key_code <= ui::VKEY_NUMPAD9;
278 result.windows_key_code = is_numeric_keypad_keycode
279 ? key_code
280 : ui::LocatedToNonLocatedKeyboardCode(key_code);
281
Blink Reformat1c4d759e2017-04-09 16:34:54282 result.native_key_code = [event keyCode];
283 result.dom_code = static_cast<int>(dom_code);
284 result.dom_key = DomKeyFromEvent(event);
dtapuskaedc082c2015-09-17 12:17:01285 NSString* text_str = TextFromEvent(event);
286 NSString* unmodified_str = UnmodifiedTextFromEvent(event);
dtapuskaedc082c2015-09-17 12:17:01287
288 // Begin Apple code, copied from KeyEventMac.mm
289
290 // Always use 13 for Enter/Return -- we don't want to use AppKit's
291 // different character for Enter.
Blink Reformat1c4d759e2017-04-09 16:34:54292 if (result.windows_key_code == '\r') {
dtapuskaedc082c2015-09-17 12:17:01293 text_str = @"\r";
294 unmodified_str = @"\r";
295 }
296
dtapuskaedc082c2015-09-17 12:17:01297 // Always use 9 for tab -- we don't want to use AppKit's different character
298 // for shift-tab.
Blink Reformat1c4d759e2017-04-09 16:34:54299 if (result.windows_key_code == 9) {
dtapuskaedc082c2015-09-17 12:17:01300 text_str = @"\x9";
301 unmodified_str = @"\x9";
302 }
303
304 // End Apple code.
305
Blink Reformat1c4d759e2017-04-09 16:34:54306 if ([text_str length] < blink::WebKeyboardEvent::kTextLengthCap &&
307 [unmodified_str length] < blink::WebKeyboardEvent::kTextLengthCap) {
Jan Wilken Dörrief5b33f92020-12-17 22:19:20308 [text_str getCharacters:reinterpret_cast<UniChar*>(&result.text[0])];
309 [unmodified_str
310 getCharacters:reinterpret_cast<UniChar*>(&result.unmodified_text[0])];
dtapuskaedc082c2015-09-17 12:17:01311 } else
312 NOTIMPLEMENTED();
313
Blink Reformat1c4d759e2017-04-09 16:34:54314 result.is_system_key = IsSystemKeyEvent(result);
dtapuskaedc082c2015-09-17 12:17:01315
316 return result;
317}
318
319// WebMouseEvent --------------------------------------------------------------
320
lanweie97eb5292017-02-07 21:59:42321blink::WebMouseEvent WebMouseEventBuilder::Build(
322 NSEvent* event,
323 NSView* view,
James Howarda07e1492020-06-26 15:34:08324 blink::WebPointerProperties::PointerType pointerType,
325 bool unacceleratedMovement) {
Avi Drissman9306606f2023-05-26 22:43:50326 ui::ComputeEventLatencyOS(base::apple::OwnedNSEvent(event));
Blink Reformat1c4d759e2017-04-09 16:34:54327 blink::WebInputEvent::Type event_type =
328 blink::WebInputEvent::Type::kUndefined;
dtapuska8c4dae12017-01-13 00:23:06329 int click_count = 0;
Blink Reformat1c4d759e2017-04-09 16:34:54330 blink::WebMouseEvent::Button button = blink::WebMouseEvent::Button::kNoButton;
dtapuskaedc082c2015-09-17 12:17:01331
lanwei74a646d2016-06-08 19:16:55332 NSEventType type = [event type];
333 switch (type) {
Avi Drissman8be81112022-05-11 01:12:42334 case NSEventTypeMouseExited:
Dave Tapuskab5a72eb2020-04-21 22:12:56335 event_type = blink::WebInputEvent::Type::kMouseLeave;
dtapuskaedc082c2015-09-17 12:17:01336 break;
Avi Drissman8be81112022-05-11 01:12:42337 case NSEventTypeLeftMouseDown:
Dave Tapuskab5a72eb2020-04-21 22:12:56338 event_type = blink::WebInputEvent::Type::kMouseDown;
dtapuska8c4dae12017-01-13 00:23:06339 click_count = [event clickCount];
Blink Reformat1c4d759e2017-04-09 16:34:54340 button = blink::WebMouseEvent::Button::kLeft;
dtapuskaedc082c2015-09-17 12:17:01341 break;
Avi Drissman8be81112022-05-11 01:12:42342 case NSEventTypeOtherMouseDown:
Dave Tapuskab5a72eb2020-04-21 22:12:56343 event_type = blink::WebInputEvent::Type::kMouseDown;
dtapuska8c4dae12017-01-13 00:23:06344 click_count = [event clickCount];
Lan Wei2bddb8c2019-08-09 15:39:26345 button = ButtonFromButtonNumber(event);
dtapuskaedc082c2015-09-17 12:17:01346 break;
Avi Drissman8be81112022-05-11 01:12:42347 case NSEventTypeRightMouseDown:
Dave Tapuskab5a72eb2020-04-21 22:12:56348 event_type = blink::WebInputEvent::Type::kMouseDown;
dtapuska8c4dae12017-01-13 00:23:06349 click_count = [event clickCount];
Blink Reformat1c4d759e2017-04-09 16:34:54350 button = blink::WebMouseEvent::Button::kRight;
dtapuskaedc082c2015-09-17 12:17:01351 break;
Avi Drissman8be81112022-05-11 01:12:42352 case NSEventTypeLeftMouseUp:
Dave Tapuskab5a72eb2020-04-21 22:12:56353 event_type = blink::WebInputEvent::Type::kMouseUp;
dtapuska8c4dae12017-01-13 00:23:06354 click_count = [event clickCount];
Blink Reformat1c4d759e2017-04-09 16:34:54355 button = blink::WebMouseEvent::Button::kLeft;
dtapuskaedc082c2015-09-17 12:17:01356 break;
Avi Drissman8be81112022-05-11 01:12:42357 case NSEventTypeOtherMouseUp:
Dave Tapuskab5a72eb2020-04-21 22:12:56358 event_type = blink::WebInputEvent::Type::kMouseUp;
dtapuska8c4dae12017-01-13 00:23:06359 click_count = [event clickCount];
Lan Wei2bddb8c2019-08-09 15:39:26360 button = ButtonFromButtonNumber(event);
dtapuskaedc082c2015-09-17 12:17:01361 break;
Avi Drissman8be81112022-05-11 01:12:42362 case NSEventTypeRightMouseUp:
Dave Tapuskab5a72eb2020-04-21 22:12:56363 event_type = blink::WebInputEvent::Type::kMouseUp;
dtapuska8c4dae12017-01-13 00:23:06364 click_count = [event clickCount];
Blink Reformat1c4d759e2017-04-09 16:34:54365 button = blink::WebMouseEvent::Button::kRight;
dtapuskaedc082c2015-09-17 12:17:01366 break;
Avi Drissman8be81112022-05-11 01:12:42367 case NSEventTypeMouseMoved:
368 case NSEventTypeMouseEntered:
Dave Tapuskab5a72eb2020-04-21 22:12:56369 event_type = blink::WebInputEvent::Type::kMouseMove;
Ella Gef2599af2017-09-06 13:27:17370 button = ButtonFromPressedMouseButtons();
dtapuskaedc082c2015-09-17 12:17:01371 break;
Avi Drissman8be81112022-05-11 01:12:42372 case NSEventTypeLeftMouseDragged:
Dave Tapuskab5a72eb2020-04-21 22:12:56373 event_type = blink::WebInputEvent::Type::kMouseMove;
Blink Reformat1c4d759e2017-04-09 16:34:54374 button = blink::WebMouseEvent::Button::kLeft;
dtapuskaedc082c2015-09-17 12:17:01375 break;
Avi Drissman8be81112022-05-11 01:12:42376 case NSEventTypeOtherMouseDragged:
Dave Tapuskab5a72eb2020-04-21 22:12:56377 event_type = blink::WebInputEvent::Type::kMouseMove;
Blink Reformat1c4d759e2017-04-09 16:34:54378 button = blink::WebMouseEvent::Button::kMiddle;
dtapuskaedc082c2015-09-17 12:17:01379 break;
Avi Drissman8be81112022-05-11 01:12:42380 case NSEventTypeRightMouseDragged:
Dave Tapuskab5a72eb2020-04-21 22:12:56381 event_type = blink::WebInputEvent::Type::kMouseMove;
Blink Reformat1c4d759e2017-04-09 16:34:54382 button = blink::WebMouseEvent::Button::kRight;
dtapuskaedc082c2015-09-17 12:17:01383 break;
384 default:
385 NOTIMPLEMENTED();
386 }
387
Ella Ge60380272017-08-25 17:59:27388 // Set id = 0 for all mouse events, disable multi-pen on mac for now.
Avi Drissman8be81112022-05-11 01:12:42389 // NSEventTypeMouseExited and NSEventTypeMouseEntered events don't have
390 // deviceID. Therefore pen exit and enter events can't get correct id.
dtapuska8c4dae12017-01-13 00:23:06391 blink::WebMouseEvent result(event_type, ModifiersFromEvent(event),
Daniel Cheng224569ee2018-04-25 05:45:06392 ui::EventTimeStampFromSeconds([event timestamp]),
393 0);
Blink Reformat1c4d759e2017-04-09 16:34:54394 result.click_count = click_count;
dtapuska8c4dae12017-01-13 00:23:06395 result.button = button;
James Howarda07e1492020-06-26 15:34:08396 SetWebEventLocationFromEventInView(&result, event, view,
397 unacceleratedMovement);
dtapuskaedc082c2015-09-17 12:17:01398
Blink Reformat1c4d759e2017-04-09 16:34:54399 result.pointer_type = pointerType;
Avi Drissman8be81112022-05-11 01:12:42400 if ((type == NSEventTypeMouseExited || type == NSEventTypeMouseEntered) ||
401 ([event subtype] != NSEventSubtypeTabletPoint &&
402 [event subtype] != NSEventSubtypeTabletProximity)) {
lanweid0d3d882016-09-21 20:56:59403 return result;
lanwei74a646d2016-06-08 19:16:55404 }
lanweid0d3d882016-09-21 20:56:59405
406 // Set stylus properties for events with a subtype of
Avi Drissman8be81112022-05-11 01:12:42407 // NSEventSubtypeTabletPoint.
lanwei45def6b2017-03-23 19:01:11408 NSEventSubtype subtype = [event subtype];
Avi Drissman8be81112022-05-11 01:12:42409 if (subtype == NSEventSubtypeTabletPoint) {
lanweid0d3d882016-09-21 20:56:59410 result.force = [event pressure];
411 NSPoint tilt = [event tilt];
Randolf Jung52ec4742023-10-11 18:18:01412 result.tilt_x = tilt.x * 90.0f;
Liviu Tinta12a1ad202020-08-11 20:33:50413 // Pointer Events specification states that tiltY is positive when the
414 // pen is tilted towards the user.
415 // By default, in MacOS, the Y coordinate increases going up,
416 // while in Chromium the Y coordinate increases going down.
417 // https://developer.apple.com/library/archive/documentation/General/Conceptual/Devpedia-CocoaApp/CoordinateSystem.html
418 // In this case (if the coordinate system is not flipped) tiltY needs to
419 // be reversed to match Chromium's expectation that tiltY is positive
420 // towards the user
Randolf Jung52ec4742023-10-11 18:18:01421 result.tilt_y = ([view isFlipped] ? 1.0 : (-1.0)) * tilt.y * 90.0f;
Blink Reformat1c4d759e2017-04-09 16:34:54422 result.tangential_pressure = [event tangentialPressure];
lanweidcc95342017-01-19 18:28:28423 // NSEvent spec doesn't specify the range of rotation, we make sure that
424 // this value is in the range of [0,359].
425 int twist = (int)[event rotation];
426 twist = twist % 360;
427 if (twist < 0)
428 twist += 360;
429 result.twist = twist;
lanweie97eb5292017-02-07 21:59:42430 } else {
431 event_type = [event isEnteringProximity]
Dave Tapuskab5a72eb2020-04-21 22:12:56432 ? blink::WebInputEvent::Type::kMouseMove
433 : blink::WebInputEvent::Type::kMouseLeave;
Blink Reformat1c4d759e2017-04-09 16:34:54434 result.SetType(event_type);
lanweid0d3d882016-09-21 20:56:59435 }
dtapuskaedc082c2015-09-17 12:17:01436 return result;
437}
438
439// WebMouseWheelEvent ---------------------------------------------------------
440
441blink::WebMouseWheelEvent WebMouseWheelEventBuilder::Build(
442 NSEvent* event,
wjmaclean0e5881c2016-08-11 00:27:30443 NSView* view) {
Avi Drissman9306606f2023-05-26 22:43:50444 ui::ComputeEventLatencyOS(base::apple::OwnedNSEvent(event));
Daniel Cheng224569ee2018-04-25 05:45:06445 blink::WebMouseWheelEvent result(
Dave Tapuskab5a72eb2020-04-21 22:12:56446 blink::WebInputEvent::Type::kMouseWheel, ModifiersFromEvent(event),
Daniel Cheng224569ee2018-04-25 05:45:06447 ui::EventTimeStampFromSeconds([event timestamp]));
Blink Reformat1c4d759e2017-04-09 16:34:54448 result.button = blink::WebMouseEvent::Button::kNoButton;
dtapuskaedc082c2015-09-17 12:17:01449
dtapuskaedc082c2015-09-17 12:17:01450 SetWebEventLocationFromEventInView(&result, event, view);
451
dtapuskaedc082c2015-09-17 12:17:01452 // Of Mice and Men
453 // ---------------
454 //
455 // There are three types of scroll data available on a scroll wheel CGEvent.
456 // Apple's documentation ([1]) is rather vague in their differences, and not
457 // terribly helpful in deciding which to use. This is what's really going on.
458 //
459 // First, these events behave very differently depending on whether a standard
460 // wheel mouse is used (one that scrolls in discrete units) or a
461 // trackpad/Mighty Mouse is used (which both provide continuous scrolling).
462 // You must check to see which was used for the event by testing the
463 // kCGScrollWheelEventIsContinuous field.
464 //
465 // Second, these events refer to "axes". Axis 1 is the y-axis, and axis 2 is
466 // the x-axis.
467 //
468 // Third, there is a concept of mouse acceleration. Scrolling the same amount
469 // of physical distance will give you different results logically depending on
470 // whether you scrolled a little at a time or in one continuous motion. Some
471 // fields account for this while others do not.
472 //
473 // Fourth, for trackpads there is a concept of chunkiness. When scrolling
474 // continuously, events can be delivered in chunks. That is to say, lots of
475 // scroll events with delta 0 will be delivered, and every so often an event
476 // with a non-zero delta will be delivered, containing the accumulated deltas
477 // from all the intermediate moves. [2]
478 //
479 // For notchy wheel mice (kCGScrollWheelEventIsContinuous == 0)
480 // ------------------------------------------------------------
481 //
482 // kCGScrollWheelEventDeltaAxis*
483 // This is the rawest of raw events. For each mouse notch you get a value of
484 // +1/-1. This does not take acceleration into account and thus is less
485 // useful for building UIs.
486 //
487 // kCGScrollWheelEventPointDeltaAxis*
488 // This is smarter. In general, for each mouse notch you get a value of
489 // +1/-1, but this _does_ take acceleration into account, so you will get
490 // larger values on longer scrolls. This field would be ideal for building
491 // UIs except for one nasty bug: when the shift key is pressed, this set of
492 // fields fails to move the value into the axis2 field (the other two types
493 // of data do). This wouldn't be so bad except for the fact that while the
494 // number of axes is used in the creation of a CGScrollWheelEvent, there is
495 // no way to get that information out of the event once created.
496 //
497 // kCGScrollWheelEventFixedPtDeltaAxis*
498 // This is a fixed value, and for each mouse notch you get a value of
499 // +0.1/-0.1 (but, like above, scaled appropriately for acceleration). This
500 // value takes acceleration into account, and in fact is identical to the
501 // results you get from -[NSEvent delta*]. (That is, if you linked on Tiger
502 // or greater; see [2] for details.)
503 //
504 // A note about continuous devices
505 // -------------------------------
506 //
507 // There are two devices that provide continuous scrolling events (trackpads
508 // and Mighty Mouses) and they behave rather differently. The Mighty Mouse
509 // behaves a lot like a regular mouse. There is no chunking, and the
510 // FixedPtDelta values are the PointDelta values multiplied by 0.1. With the
511 // trackpad, though, there is chunking. While the FixedPtDelta values are
512 // reasonable (they occur about every fifth event but have values five times
513 // larger than usual) the Delta values are unreasonable. They don't appear to
514 // accumulate properly.
515 //
516 // For continuous devices (kCGScrollWheelEventIsContinuous != 0)
517 // -------------------------------------------------------------
518 //
519 // kCGScrollWheelEventDeltaAxis*
520 // This provides values with no acceleration. With a trackpad, these values
521 // are chunked but each non-zero value does not appear to be cumulative.
522 // This seems to be a bug.
523 //
524 // kCGScrollWheelEventPointDeltaAxis*
525 // This provides values with acceleration. With a trackpad, these values are
526 // not chunked and are highly accurate.
527 //
528 // kCGScrollWheelEventFixedPtDeltaAxis*
529 // This provides values with acceleration. With a trackpad, these values are
530 // chunked but unlike Delta events are properly cumulative.
531 //
532 // Summary
533 // -------
534 //
535 // In general the best approach to take is: determine if the event is
536 // continuous. If it is not, then use the FixedPtDelta events (or just stick
537 // with Cocoa events). They provide both acceleration and proper horizontal
538 // scrolling. If the event is continuous, then doing pixel scrolling with the
539 // PointDelta is the way to go. In general, avoid the Delta events. They're
540 // the oldest (dating back to 10.4, before CGEvents were public) but they lack
541 // acceleration and precision, making them useful only in specific edge cases.
542 //
543 // References
544 // ----------
545 //
546 // [1]
547 // <http://developer.apple.com/documentation/Carbon/Reference/QuartzEventServicesRef/Reference/reference.html>
548 // [2] <http://developer.apple.com/releasenotes/Cocoa/AppKitOlderNotes.html>
Avi Drissman8be81112022-05-11 01:12:42549 // Scroll to the section headed "NSEventTypeScrollWheel events".
dtapuskaedc082c2015-09-17 12:17:01550 //
551 // P.S. The "smooth scrolling" option in the system preferences is utterly
552 // unrelated to any of this.
553
554 CGEventRef cg_event = [event CGEvent];
555 DCHECK(cg_event);
556
557 // Wheel ticks are supposed to be raw, unaccelerated values, one per physical
558 // mouse wheel notch. The delta event is perfect for this (being a good
559 // "specific edge case" as mentioned above). Trackpads, unfortunately, do
560 // event chunking, and sending mousewheel events with 0 ticks causes some
561 // websites to malfunction. Therefore, for all continuous input devices we use
562 // the point delta data instead, since we cannot distinguish trackpad data
563 // from data from any other continuous device.
564
dtapuskaedc082c2015-09-17 12:17:01565 if (CGEventGetIntegerValueField(cg_event, kCGScrollWheelEventIsContinuous)) {
Mohsen Izadiffcbc61f12020-02-09 06:31:27566 result.delta_units = ui::ScrollGranularity::kScrollByPrecisePixel;
Blink Reformat1c4d759e2017-04-09 16:34:54567 result.delta_x = CGEventGetIntegerValueField(
dtapuskaedc082c2015-09-17 12:17:01568 cg_event, kCGScrollWheelEventPointDeltaAxis2);
Blink Reformat1c4d759e2017-04-09 16:34:54569 result.delta_y = CGEventGetIntegerValueField(
dtapuskaedc082c2015-09-17 12:17:01570 cg_event, kCGScrollWheelEventPointDeltaAxis1);
Blink Reformat1c4d759e2017-04-09 16:34:54571 result.wheel_ticks_x = result.delta_x / ui::kScrollbarPixelsPerCocoaTick;
572 result.wheel_ticks_y = result.delta_y / ui::kScrollbarPixelsPerCocoaTick;
dtapuskaedc082c2015-09-17 12:17:01573 } else {
Blink Reformat1c4d759e2017-04-09 16:34:54574 result.delta_x = [event deltaX] * ui::kScrollbarPixelsPerCocoaTick;
575 result.delta_y = [event deltaY] * ui::kScrollbarPixelsPerCocoaTick;
576 result.wheel_ticks_y =
dtapuskaedc082c2015-09-17 12:17:01577 CGEventGetIntegerValueField(cg_event, kCGScrollWheelEventDeltaAxis1);
Blink Reformat1c4d759e2017-04-09 16:34:54578 result.wheel_ticks_x =
dtapuskaedc082c2015-09-17 12:17:01579 CGEventGetIntegerValueField(cg_event, kCGScrollWheelEventDeltaAxis2);
580 }
581
dtapuskaedc082c2015-09-17 12:17:01582 result.phase = PhaseForEvent(event);
Blink Reformat1c4d759e2017-04-09 16:34:54583 result.momentum_phase = MomentumPhaseForEvent(event);
dtapuskaedc082c2015-09-17 12:17:01584
585 return result;
586}
587
588blink::WebGestureEvent WebGestureEventBuilder::Build(NSEvent* event,
589 NSView* view) {
590 blink::WebGestureEvent result;
591
592 // Use a temporary WebMouseEvent to get the location.
593 blink::WebMouseEvent temp;
594
595 SetWebEventLocationFromEventInView(&temp, event, view);
Ella Ge1116059d2018-03-21 02:06:13596 result.SetPositionInWidget(temp.PositionInWidget());
597 result.SetPositionInScreen(temp.PositionInScreen());
dtapuskaedc082c2015-09-17 12:17:01598
Blink Reformat1c4d759e2017-04-09 16:34:54599 result.SetModifiers(ModifiersFromEvent(event));
Daniel Cheng224569ee2018-04-25 05:45:06600 result.SetTimeStamp(ui::EventTimeStampFromSeconds([event timestamp]));
dtapuskaedc082c2015-09-17 12:17:01601
Daniel Cheng7f9ec902019-04-18 05:07:00602 result.SetSourceDevice(blink::WebGestureDevice::kTouchpad);
Ella Ge1116059d2018-03-21 02:06:13603
dtapuskaedc082c2015-09-17 12:17:01604 switch ([event type]) {
605 case NSEventTypeMagnify:
Kevin McNee297980d2018-06-04 21:32:14606 // We don't need to set the type based on |[event phase]| as the caller
607 // must set the begin and end types in order to support older Mac
608 // versions.
Dave Tapuskab5a72eb2020-04-21 22:12:56609 result.SetType(blink::WebInputEvent::Type::kGesturePinchUpdate);
Blink Reformat1c4d759e2017-04-09 16:34:54610 result.data.pinch_update.scale = [event magnification] + 1.0;
Kevin McNee297980d2018-06-04 21:32:14611 result.SetNeedsWheelEvent(true);
dtapuskaedc082c2015-09-17 12:17:01612 break;
613 case NSEventTypeSmartMagnify:
614 // Map the Cocoa "double-tap with two fingers" zoom gesture to regular
615 // GestureDoubleTap, because the effect is similar to single-finger
616 // double-tap zoom on mobile platforms. Note that tapCount is set to 1
617 // because the gesture type already encodes that information.
Dave Tapuskab5a72eb2020-04-21 22:12:56618 result.SetType(blink::WebInputEvent::Type::kGestureDoubleTap);
Blink Reformat1c4d759e2017-04-09 16:34:54619 result.data.tap.tap_count = 1;
Kevin McNee1a7d30962018-10-12 15:30:46620 result.SetNeedsWheelEvent(true);
dtapuskaedc082c2015-09-17 12:17:01621 break;
622 case NSEventTypeBeginGesture:
623 case NSEventTypeEndGesture:
624 // The specific type of a gesture is not defined when the gesture begin
625 // and end NSEvents come in. Leave them undefined. The caller will need
626 // to specify them when the gesture is differentiated.
dtapuskaedc082c2015-09-17 12:17:01627 break;
Erik Chenf253afd2017-11-02 19:46:13628 case NSEventTypeScrollWheel:
629 // When building against the 10.11 SDK or later, and running on macOS
630 // 10.11+, Cocoa no longer sends separate Begin/End gestures for scroll
631 // events. However, it's convenient to use the same path as the older
632 // OSes, to avoid logic duplication. We just need to support building a
633 // dummy WebGestureEvent.
634 break;
dtapuskaedc082c2015-09-17 12:17:01635 default:
636 NOTIMPLEMENTED();
dtapuskaedc082c2015-09-17 12:17:01637 }
638
639 return result;
640}
641
Lan Wei404244f2018-08-01 14:10:22642// WebTouchEvent --------------------------------------------------------------
643
644blink::WebTouchEvent WebTouchEventBuilder::Build(NSEvent* event, NSView* view) {
645 blink::WebInputEvent::Type event_type =
646 blink::WebInputEvent::Type::kUndefined;
647 NSEventType type = [event type];
Dave Tapuska221241572020-04-27 15:59:47648 blink::WebTouchPoint::State state =
649 blink::WebTouchPoint::State::kStateUndefined;
Lan Wei404244f2018-08-01 14:10:22650 switch (type) {
Avi Drissman8be81112022-05-11 01:12:42651 case NSEventTypeLeftMouseDown:
Dave Tapuskab5a72eb2020-04-21 22:12:56652 event_type = blink::WebInputEvent::Type::kTouchStart;
Dave Tapuska221241572020-04-27 15:59:47653 state = blink::WebTouchPoint::State::kStatePressed;
Lan Wei404244f2018-08-01 14:10:22654 break;
Avi Drissman8be81112022-05-11 01:12:42655 case NSEventTypeLeftMouseUp:
Dave Tapuskab5a72eb2020-04-21 22:12:56656 event_type = blink::WebInputEvent::Type::kTouchEnd;
Dave Tapuska221241572020-04-27 15:59:47657 state = blink::WebTouchPoint::State::kStateReleased;
Lan Wei404244f2018-08-01 14:10:22658 break;
Avi Drissman8be81112022-05-11 01:12:42659 case NSEventTypeLeftMouseDragged:
660 case NSEventTypeRightMouseDragged:
661 case NSEventTypeOtherMouseDragged:
662 case NSEventTypeMouseMoved:
663 case NSEventTypeRightMouseDown:
664 case NSEventTypeOtherMouseDown:
665 case NSEventTypeRightMouseUp:
666 case NSEventTypeOtherMouseUp:
Dave Tapuskab5a72eb2020-04-21 22:12:56667 event_type = blink::WebInputEvent::Type::kTouchMove;
Dave Tapuska221241572020-04-27 15:59:47668 state = blink::WebTouchPoint::State::kStateMoved;
Lan Wei404244f2018-08-01 14:10:22669 break;
670 default:
Peter Boström0af5ffa12024-11-08 11:47:04671 NOTREACHED() << "Invalid types for touch events." << type;
Lan Wei404244f2018-08-01 14:10:22672 }
673
674 blink::WebTouchEvent result(event_type, ModifiersFromEvent(event),
675 ui::EventTimeStampFromSeconds([event timestamp]));
Avi Drissman9306606f2023-05-26 22:43:50676 ui::ComputeEventLatencyOS(base::apple::OwnedNSEvent(event));
Dave Tapuskab5a72eb2020-04-21 22:12:56677 result.hovering = event_type == blink::WebInputEvent::Type::kTouchEnd;
Lan Wei404244f2018-08-01 14:10:22678 result.unique_touch_event_id = ui::GetNextTouchEventId();
679 result.touches_length = 1;
680
681 // Use a temporary WebMouseEvent to get the location.
682 blink::WebMouseEvent temp;
683 SetWebEventLocationFromEventInView(&temp, event, view);
684 result.touches[0].SetPositionInWidget(temp.PositionInWidget());
685 result.touches[0].SetPositionInScreen(temp.PositionInScreen());
686 result.touches[0].movement_x = temp.movement_x;
687 result.touches[0].movement_y = temp.movement_y;
688
689 result.touches[0].state = state;
690 result.touches[0].pointer_type =
691 blink::WebPointerProperties::PointerType::kPen;
692 result.touches[0].id = [event pointingDeviceID];
693 result.touches[0].force = [event pressure];
694 NSPoint tilt = [event tilt];
Randolf Jung52ec4742023-10-11 18:18:01695 result.touches[0].tilt_x = tilt.x * 90.0f;
696 result.touches[0].tilt_y = tilt.y * 90.0f;
Lan Wei404244f2018-08-01 14:10:22697 result.touches[0].tangential_pressure = [event tangentialPressure];
698 // NSEvent spec doesn't specify the range of rotation, we make sure that
699 // this value is in the range of [0,359].
700 int twist = (int)[event rotation];
701 twist = twist % 360;
702 if (twist < 0)
703 twist += 360;
704 result.touches[0].twist = twist;
705 float rotation_angle = twist % 180;
706 if (rotation_angle > 90)
707 rotation_angle = 180.f - rotation_angle;
708 result.touches[0].rotation_angle = rotation_angle;
709 return result;
710}
711
Kartar Singh5c8e0b22024-05-30 10:32:14712} // namespace input