1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/events/event_constants.h"
9 #include <X11/extensions/XInput.h>
10 #include <X11/extensions/XInput2.h>
13 #include "base/logging.h"
14 #include "base/memory/singleton.h"
15 #include "base/message_loop/message_pump_x11.h"
16 #include "ui/events/event_utils.h"
17 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
18 #include "ui/events/x/device_data_manager.h"
19 #include "ui/events/x/device_list_cache_x.h"
20 #include "ui/events/x/touch_factory_x11.h"
21 #include "ui/gfx/display.h"
22 #include "ui/gfx/point.h"
23 #include "ui/gfx/rect.h"
24 #include "ui/gfx/screen.h"
25 #include "ui/gfx/x/x11_atom_cache.h"
26 #include "ui/gfx/x/x11_types.h"
30 // Scroll amount for each wheelscroll event. 53 is also the value used for GTK+.
31 const int kWheelScrollAmount
= 53;
33 const int kMinWheelButton
= 4;
34 const int kMaxWheelButton
= 7;
36 // A class to track current modifier state on master device. Only track ctrl,
37 // alt, shift and caps lock keys currently. The tracked state can then be used
38 // by floating device.
39 class XModifierStateWatcher
{
41 static XModifierStateWatcher
* GetInstance() {
42 return Singleton
<XModifierStateWatcher
>::get();
45 void UpdateStateFromEvent(const base::NativeEvent
& native_event
) {
46 // Floating device can't access the modifer state from master device.
47 // We need to track the states of modifier keys in a singleton for
48 // floating devices such as touch screen. Issue 106426 is one example
49 // of why we need the modifier states for floating device.
50 state_
= native_event
->xkey
.state
;
51 // master_state is the state before key press. We need to track the
52 // state after key press for floating device. Currently only ctrl,
53 // shift, alt and caps lock keys are tracked.
54 ui::KeyboardCode keyboard_code
= ui::KeyboardCodeFromNative(native_event
);
55 unsigned int mask
= 0;
57 switch (keyboard_code
) {
58 case ui::VKEY_CONTROL
: {
62 case ui::VKEY_SHIFT
: {
70 case ui::VKEY_CAPITAL
: {
78 if (native_event
->type
== KeyPress
)
84 // Returns the current modifer state in master device. It only contains the
85 // state of ctrl, shift, alt and caps lock keys.
86 unsigned int state() { return state_
; }
89 friend struct DefaultSingletonTraits
<XModifierStateWatcher
>;
91 XModifierStateWatcher() : state_(0) { }
95 DISALLOW_COPY_AND_ASSIGN(XModifierStateWatcher
);
98 #if defined(USE_XI2_MT)
99 // Detects if a touch event is a driver-generated 'special event'.
100 // A 'special event' is a touch event with maximum radius and pressure at
102 // This needs to be done in a cleaner way: http://crbug.com/169256
103 bool TouchEventIsGeneratedHack(const base::NativeEvent
& native_event
) {
104 XIDeviceEvent
* event
=
105 static_cast<XIDeviceEvent
*>(native_event
->xcookie
.data
);
106 CHECK(event
->evtype
== XI_TouchBegin
||
107 event
->evtype
== XI_TouchUpdate
||
108 event
->evtype
== XI_TouchEnd
);
110 // Force is normalized to [0, 1].
111 if (ui::GetTouchForce(native_event
) < 1.0f
)
114 if (ui::EventLocationFromNative(native_event
) != gfx::Point())
117 // Radius is in pixels, and the valuator is the diameter in pixels.
118 double radius
= ui::GetTouchRadiusX(native_event
), min
, max
;
119 unsigned int deviceid
=
120 static_cast<XIDeviceEvent
*>(native_event
->xcookie
.data
)->sourceid
;
121 if (!ui::DeviceDataManager::GetInstance()->GetDataRange(
122 deviceid
, ui::DeviceDataManager::DT_TOUCH_MAJOR
, &min
, &max
)) {
126 return radius
* 2 == max
;
130 int GetEventFlagsFromXState(unsigned int state
) {
132 if (state
& ControlMask
)
133 flags
|= ui::EF_CONTROL_DOWN
;
134 if (state
& ShiftMask
)
135 flags
|= ui::EF_SHIFT_DOWN
;
136 if (state
& Mod1Mask
)
137 flags
|= ui::EF_ALT_DOWN
;
138 if (state
& LockMask
)
139 flags
|= ui::EF_CAPS_LOCK_DOWN
;
140 if (state
& Mod5Mask
)
141 flags
|= ui::EF_ALTGR_DOWN
;
142 if (state
& Button1Mask
)
143 flags
|= ui::EF_LEFT_MOUSE_BUTTON
;
144 if (state
& Button2Mask
)
145 flags
|= ui::EF_MIDDLE_MOUSE_BUTTON
;
146 if (state
& Button3Mask
)
147 flags
|= ui::EF_RIGHT_MOUSE_BUTTON
;
151 // Get the event flag for the button in XButtonEvent. During a ButtonPress
152 // event, |state| in XButtonEvent does not include the button that has just been
153 // pressed. Instead |state| contains flags for the buttons (if any) that had
154 // already been pressed before the current button, and |button| stores the most
155 // current pressed button. So, if you press down left mouse button, and while
156 // pressing it down, press down the right mouse button, then for the latter
157 // event, |state| would have Button1Mask set but not Button3Mask, and |button|
159 int GetEventFlagsForButton(int button
) {
162 return ui::EF_LEFT_MOUSE_BUTTON
;
164 return ui::EF_MIDDLE_MOUSE_BUTTON
;
166 return ui::EF_RIGHT_MOUSE_BUTTON
;
172 int GetButtonMaskForX2Event(XIDeviceEvent
* xievent
) {
174 for (int i
= 0; i
< 8 * xievent
->buttons
.mask_len
; i
++) {
175 if (XIMaskIsSet(xievent
->buttons
.mask
, i
)) {
176 int button
= (xievent
->sourceid
== xievent
->deviceid
) ?
177 ui::DeviceDataManager::GetInstance()->GetMappedButton(i
) : i
;
178 buttonflags
|= GetEventFlagsForButton(button
);
184 ui::EventType
GetTouchEventType(const base::NativeEvent
& native_event
) {
185 XIDeviceEvent
* event
=
186 static_cast<XIDeviceEvent
*>(native_event
->xcookie
.data
);
187 #if defined(USE_XI2_MT)
188 switch(event
->evtype
) {
190 return TouchEventIsGeneratedHack(native_event
) ? ui::ET_UNKNOWN
:
191 ui::ET_TOUCH_PRESSED
;
193 return TouchEventIsGeneratedHack(native_event
) ? ui::ET_UNKNOWN
:
196 return TouchEventIsGeneratedHack(native_event
) ? ui::ET_TOUCH_CANCELLED
:
197 ui::ET_TOUCH_RELEASED
;
199 #endif // defined(USE_XI2_MT)
201 DCHECK(ui::TouchFactory::GetInstance()->IsTouchDevice(event
->sourceid
));
202 switch (event
->evtype
) {
204 return ui::ET_TOUCH_PRESSED
;
205 case XI_ButtonRelease
:
206 return ui::ET_TOUCH_RELEASED
;
208 // Should not convert any emulated Motion event from touch device to
210 if (!(event
->flags
& XIPointerEmulated
) &&
211 GetButtonMaskForX2Event(event
))
212 return ui::ET_TOUCH_MOVED
;
213 return ui::ET_UNKNOWN
;
217 return ui::ET_UNKNOWN
;
220 double GetTouchParamFromXEvent(XEvent
* xev
,
221 ui::DeviceDataManager::DataType val
,
222 double default_value
) {
223 ui::DeviceDataManager::GetInstance()->GetEventData(
224 *xev
, val
, &default_value
);
225 return default_value
;
228 Atom
GetNoopEventAtom() {
229 return XInternAtom(gfx::GetXDisplay(), "noop", False
);
236 void UpdateDeviceList() {
237 XDisplay
* display
= gfx::GetXDisplay();
238 DeviceListCacheX::GetInstance()->UpdateDeviceList(display
);
239 TouchFactory::GetInstance()->UpdateDeviceList(display
);
240 DeviceDataManager::GetInstance()->UpdateDeviceList(display
);
243 EventType
EventTypeFromNative(const base::NativeEvent
& native_event
) {
244 switch (native_event
->type
) {
246 return ET_KEY_PRESSED
;
248 return ET_KEY_RELEASED
;
250 if (static_cast<int>(native_event
->xbutton
.button
) >= kMinWheelButton
&&
251 static_cast<int>(native_event
->xbutton
.button
) <= kMaxWheelButton
)
252 return ET_MOUSEWHEEL
;
253 return ET_MOUSE_PRESSED
;
255 // Drop wheel events; we should've already scrolled on the press.
256 if (static_cast<int>(native_event
->xbutton
.button
) >= kMinWheelButton
&&
257 static_cast<int>(native_event
->xbutton
.button
) <= kMaxWheelButton
)
259 return ET_MOUSE_RELEASED
;
261 if (native_event
->xmotion
.state
&
262 (Button1Mask
| Button2Mask
| Button3Mask
))
263 return ET_MOUSE_DRAGGED
;
264 return ET_MOUSE_MOVED
;
266 // The standard on Windows is to send a MouseMove event when the mouse
267 // first enters a window instead of sending a special mouse enter event.
268 // To be consistent we follow the same style.
269 return ET_MOUSE_MOVED
;
271 return ET_MOUSE_EXITED
;
273 TouchFactory
* factory
= TouchFactory::GetInstance();
274 if (!factory
->ShouldProcessXI2Event(native_event
))
277 XIDeviceEvent
* xievent
=
278 static_cast<XIDeviceEvent
*>(native_event
->xcookie
.data
);
280 if (factory
->IsTouchDevice(xievent
->sourceid
))
281 return GetTouchEventType(native_event
);
283 switch (xievent
->evtype
) {
284 case XI_ButtonPress
: {
285 int button
= EventButtonFromNative(native_event
);
286 if (button
>= kMinWheelButton
&& button
<= kMaxWheelButton
)
287 return ET_MOUSEWHEEL
;
288 return ET_MOUSE_PRESSED
;
290 case XI_ButtonRelease
: {
291 int button
= EventButtonFromNative(native_event
);
292 // Drop wheel events; we should've already scrolled on the press.
293 if (button
>= kMinWheelButton
&& button
<= kMaxWheelButton
)
295 return ET_MOUSE_RELEASED
;
299 if (GetFlingData(native_event
, NULL
, NULL
, NULL
, NULL
, &is_cancel
)) {
300 return is_cancel
? ET_SCROLL_FLING_CANCEL
: ET_SCROLL_FLING_START
;
301 } else if (DeviceDataManager::GetInstance()->IsScrollEvent(
303 return IsTouchpadEvent(native_event
) ? ET_SCROLL
: ET_MOUSEWHEEL
;
304 } else if (DeviceDataManager::GetInstance()->IsCMTMetricsEvent(
307 } else if (GetButtonMaskForX2Event(xievent
)) {
308 return ET_MOUSE_DRAGGED
;
310 return ET_MOUSE_MOVED
;
321 int EventFlagsFromNative(const base::NativeEvent
& native_event
) {
322 switch (native_event
->type
) {
325 XModifierStateWatcher::GetInstance()->UpdateStateFromEvent(native_event
);
326 return GetEventFlagsFromXState(native_event
->xkey
.state
);
329 case ButtonRelease
: {
330 int flags
= GetEventFlagsFromXState(native_event
->xbutton
.state
);
331 const EventType type
= EventTypeFromNative(native_event
);
332 if (type
== ET_MOUSE_PRESSED
|| type
== ET_MOUSE_RELEASED
)
333 flags
|= GetEventFlagsForButton(native_event
->xbutton
.button
);
337 return GetEventFlagsFromXState(native_event
->xmotion
.state
);
339 XIDeviceEvent
* xievent
=
340 static_cast<XIDeviceEvent
*>(native_event
->xcookie
.data
);
342 switch (xievent
->evtype
) {
343 #if defined(USE_XI2_MT)
347 return GetButtonMaskForX2Event(xievent
) |
348 GetEventFlagsFromXState(xievent
->mods
.effective
) |
349 GetEventFlagsFromXState(
350 XModifierStateWatcher::GetInstance()->state());
354 case XI_ButtonRelease
: {
356 TouchFactory::GetInstance()->IsTouchDevice(xievent
->sourceid
);
357 int flags
= GetButtonMaskForX2Event(xievent
) |
358 GetEventFlagsFromXState(xievent
->mods
.effective
);
360 flags
|= GetEventFlagsFromXState(
361 XModifierStateWatcher::GetInstance()->state());
364 const EventType type
= EventTypeFromNative(native_event
);
365 int button
= EventButtonFromNative(native_event
);
366 if ((type
== ET_MOUSE_PRESSED
|| type
== ET_MOUSE_RELEASED
) && !touch
)
367 flags
|= GetEventFlagsForButton(button
);
371 return GetButtonMaskForX2Event(xievent
) |
372 GetEventFlagsFromXState(xievent
->mods
.effective
);
379 base::TimeDelta
EventTimeFromNative(const base::NativeEvent
& native_event
) {
380 switch(native_event
->type
) {
383 return base::TimeDelta::FromMilliseconds(native_event
->xkey
.time
);
386 return base::TimeDelta::FromMilliseconds(native_event
->xbutton
.time
);
389 return base::TimeDelta::FromMilliseconds(native_event
->xmotion
.time
);
393 return base::TimeDelta::FromMilliseconds(native_event
->xcrossing
.time
);
397 double touch_timestamp
;
398 if (GetGestureTimes(native_event
, &start
, &end
)) {
399 // If the driver supports gesture times, use them.
400 return base::TimeDelta::FromMicroseconds(end
* 1000000);
401 } else if (DeviceDataManager::GetInstance()->GetEventData(*native_event
,
402 DeviceDataManager::DT_TOUCH_RAW_TIMESTAMP
, &touch_timestamp
)) {
403 return base::TimeDelta::FromMicroseconds(touch_timestamp
* 1000000);
405 XIDeviceEvent
* xide
=
406 static_cast<XIDeviceEvent
*>(native_event
->xcookie
.data
);
407 return base::TimeDelta::FromMilliseconds(xide
->time
);
413 return base::TimeDelta();
416 gfx::Point
EventLocationFromNative(const base::NativeEvent
& native_event
) {
417 switch (native_event
->type
) {
420 return gfx::Point(native_event
->xcrossing
.x
, native_event
->xcrossing
.y
);
423 return gfx::Point(native_event
->xbutton
.x
, native_event
->xbutton
.y
);
425 return gfx::Point(native_event
->xmotion
.x
, native_event
->xmotion
.y
);
427 XIDeviceEvent
* xievent
=
428 static_cast<XIDeviceEvent
*>(native_event
->xcookie
.data
);
429 return gfx::Point(round(xievent
->event_x
),
430 round(xievent
->event_y
));
436 gfx::Point
EventSystemLocationFromNative(
437 const base::NativeEvent
& native_event
) {
438 switch (native_event
->type
) {
441 return gfx::Point(native_event
->xcrossing
.x_root
,
442 native_event
->xcrossing
.y_root
);
445 case ButtonRelease
: {
446 return gfx::Point(native_event
->xbutton
.x_root
,
447 native_event
->xbutton
.y_root
);
450 return gfx::Point(native_event
->xmotion
.x_root
,
451 native_event
->xmotion
.y_root
);
454 XIDeviceEvent
* xievent
=
455 static_cast<XIDeviceEvent
*>(native_event
->xcookie
.data
);
456 return gfx::Point(xievent
->root_x
, xievent
->root_y
);
463 int EventButtonFromNative(const base::NativeEvent
& native_event
) {
464 CHECK_EQ(GenericEvent
, native_event
->type
);
465 XIDeviceEvent
* xievent
=
466 static_cast<XIDeviceEvent
*>(native_event
->xcookie
.data
);
467 int button
= xievent
->detail
;
469 return (xievent
->sourceid
== xievent
->deviceid
) ?
470 DeviceDataManager::GetInstance()->GetMappedButton(button
) : button
;
473 KeyboardCode
KeyboardCodeFromNative(const base::NativeEvent
& native_event
) {
474 return KeyboardCodeFromXKeyEvent(native_event
);
477 bool IsMouseEvent(const base::NativeEvent
& native_event
) {
478 if (native_event
->type
== EnterNotify
||
479 native_event
->type
== LeaveNotify
||
480 native_event
->type
== ButtonPress
||
481 native_event
->type
== ButtonRelease
||
482 native_event
->type
== MotionNotify
)
484 if (native_event
->type
== GenericEvent
) {
485 XIDeviceEvent
* xievent
=
486 static_cast<XIDeviceEvent
*>(native_event
->xcookie
.data
);
487 return xievent
->evtype
== XI_ButtonPress
||
488 xievent
->evtype
== XI_ButtonRelease
||
489 xievent
->evtype
== XI_Motion
;
494 int GetChangedMouseButtonFlagsFromNative(
495 const base::NativeEvent
& native_event
) {
496 switch (native_event
->type
) {
499 return GetEventFlagsFromXState(native_event
->xbutton
.state
);
501 XIDeviceEvent
* xievent
=
502 static_cast<XIDeviceEvent
*>(native_event
->xcookie
.data
);
503 switch (xievent
->evtype
) {
505 case XI_ButtonRelease
:
506 return GetEventFlagsForButton(EventButtonFromNative(native_event
));
517 gfx::Vector2d
GetMouseWheelOffset(const base::NativeEvent
& native_event
) {
518 float x_offset
, y_offset
;
519 if (GetScrollOffsets(
520 native_event
, &x_offset
, &y_offset
, NULL
, NULL
, NULL
)) {
521 return gfx::Vector2d(static_cast<int>(x_offset
),
522 static_cast<int>(y_offset
));
525 int button
= native_event
->type
== GenericEvent
?
526 EventButtonFromNative(native_event
) : native_event
->xbutton
.button
;
530 return gfx::Vector2d(0, kWheelScrollAmount
);
532 return gfx::Vector2d(0, -kWheelScrollAmount
);
534 // TODO(derat): Do something for horizontal scrolls (buttons 6 and 7)?
535 return gfx::Vector2d();
539 void ClearTouchIdIfReleased(const base::NativeEvent
& xev
) {
540 ui::EventType type
= ui::EventTypeFromNative(xev
);
541 if (type
== ui::ET_TOUCH_CANCELLED
||
542 type
== ui::ET_TOUCH_RELEASED
) {
543 ui::TouchFactory
* factory
= ui::TouchFactory::GetInstance();
544 ui::DeviceDataManager
* manager
= ui::DeviceDataManager::GetInstance();
546 if (manager
->GetEventData(
547 *xev
, ui::DeviceDataManager::DT_TOUCH_TRACKING_ID
, &tracking_id
)) {
548 factory
->ReleaseSlotForTrackingID(tracking_id
);
553 int GetTouchId(const base::NativeEvent
& xev
) {
555 ui::DeviceDataManager
* manager
= ui::DeviceDataManager::GetInstance();
557 if (!manager
->GetEventData(
558 *xev
, ui::DeviceDataManager::DT_TOUCH_TRACKING_ID
, &tracking_id
)) {
559 LOG(ERROR
) << "Could not get the tracking ID for the event. Using 0.";
561 ui::TouchFactory
* factory
= ui::TouchFactory::GetInstance();
562 slot
= factory
->GetSlotForTrackingID(tracking_id
);
567 float GetTouchRadiusX(const base::NativeEvent
& native_event
) {
568 return GetTouchParamFromXEvent(native_event
,
569 ui::DeviceDataManager::DT_TOUCH_MAJOR
, 0.0) / 2.0;
572 float GetTouchRadiusY(const base::NativeEvent
& native_event
) {
573 return GetTouchParamFromXEvent(native_event
,
574 ui::DeviceDataManager::DT_TOUCH_MINOR
, 0.0) / 2.0;
577 float GetTouchAngle(const base::NativeEvent
& native_event
) {
578 return GetTouchParamFromXEvent(native_event
,
579 ui::DeviceDataManager::DT_TOUCH_ORIENTATION
, 0.0) / 2.0;
582 float GetTouchForce(const base::NativeEvent
& native_event
) {
584 force
= GetTouchParamFromXEvent(native_event
,
585 ui::DeviceDataManager::DT_TOUCH_PRESSURE
, 0.0);
586 unsigned int deviceid
=
587 static_cast<XIDeviceEvent
*>(native_event
->xcookie
.data
)->sourceid
;
588 // Force is normalized to fall into [0, 1]
589 if (!ui::DeviceDataManager::GetInstance()->NormalizeData(
590 deviceid
, ui::DeviceDataManager::DT_TOUCH_PRESSURE
, &force
))
595 bool GetScrollOffsets(const base::NativeEvent
& native_event
,
598 float* x_offset_ordinal
,
599 float* y_offset_ordinal
,
601 if (!DeviceDataManager::GetInstance()->IsScrollEvent(native_event
))
604 // Temp values to prevent passing NULLs to DeviceDataManager.
605 float x_offset_
, y_offset_
;
606 float x_offset_ordinal_
, y_offset_ordinal_
;
609 x_offset
= &x_offset_
;
611 y_offset
= &y_offset_
;
612 if (!x_offset_ordinal
)
613 x_offset_ordinal
= &x_offset_ordinal_
;
614 if (!y_offset_ordinal
)
615 y_offset_ordinal
= &y_offset_ordinal_
;
617 finger_count
= &finger_count_
;
619 DeviceDataManager::GetInstance()->GetScrollOffsets(
622 x_offset_ordinal
, y_offset_ordinal
,
627 bool GetFlingData(const base::NativeEvent
& native_event
,
633 if (!DeviceDataManager::GetInstance()->IsFlingEvent(native_event
))
637 float vx_ordinal_
, vy_ordinal_
;
644 vx_ordinal
= &vx_ordinal_
;
646 vy_ordinal
= &vy_ordinal_
;
648 is_cancel
= &is_cancel_
;
650 DeviceDataManager::GetInstance()->GetFlingData(
651 native_event
, vx
, vy
, vx_ordinal
, vy_ordinal
, is_cancel
);
655 bool GetGestureTimes(const base::NativeEvent
& native_event
,
658 if (!DeviceDataManager::GetInstance()->HasGestureTimes(native_event
))
661 double start_time_
, end_time_
;
663 start_time
= &start_time_
;
665 end_time
= &end_time_
;
667 DeviceDataManager::GetInstance()->GetGestureTimes(
668 native_event
, start_time
, end_time
);
672 void SetNaturalScroll(bool enabled
) {
673 DeviceDataManager::GetInstance()->set_natural_scroll_enabled(enabled
);
676 bool IsNaturalScrollEnabled() {
677 return DeviceDataManager::GetInstance()->natural_scroll_enabled();
680 bool IsTouchpadEvent(const base::NativeEvent
& event
) {
681 return DeviceDataManager::GetInstance()->IsTouchpadXInputEvent(event
);
684 bool IsNoopEvent(const base::NativeEvent
& event
) {
685 return (event
->type
== ClientMessage
&&
686 event
->xclient
.message_type
== GetNoopEventAtom());
689 base::NativeEvent
CreateNoopEvent() {
690 static XEvent
* noop
= NULL
;
693 memset(noop
, 0, sizeof(XEvent
));
694 noop
->xclient
.type
= ClientMessage
;
695 noop
->xclient
.window
= None
;
696 noop
->xclient
.format
= 8;
697 DCHECK(!noop
->xclient
.display
);
699 // Make sure we use atom from current xdisplay, which may
700 // change during the test.
701 noop
->xclient
.message_type
= GetNoopEventAtom();