1 // Copyright 2019 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.
4
5 #include "ui/events/x/x11_event_translation.h"
6
7 #include <vector>
8
9 #include "base/check.h"
10 #include "base/notreached.h"
11 #include "base/time/time.h"
12 #include "ui/events/devices/x11/touch_factory_x11.h"
13 #include "ui/events/event.h"
14 #include "ui/events/event_utils.h"
15 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
16 #include "ui/events/pointer_details.h"
17 #include "ui/events/types/event_type.h"
18 #include "ui/events/x/events_x_utils.h"
19 #include "ui/gfx/geometry/point.h"
20 #include "ui/gfx/x/xproto.h"
21
22 #if defined(USE_OZONE)
23 #include "ui/base/ui_base_features.h"
24 #endif
25
26 namespace ui {
27
28 namespace {
29
XkbGroupForCoreState(int state)30 int XkbGroupForCoreState(int state) {
31 return (state >> 13) & 0x3;
32 }
33
34 // In X11 touch events, a new tracking_id/slot mapping is set up for each new
35 // event (see |ui::GetTouchIdFromXEvent| function), which needs to be cleared
36 // at destruction time for corresponding release/cancel events. In this
37 // particular case, ui::TouchEvent class is extended so that dtor can be
38 // overridden in order to implement this platform-specific behavior.
39 class TouchEventX11 : public ui::TouchEvent {
40 public:
TouchEventX11(EventType type,gfx::Point location,base::TimeTicks timestamp,const PointerDetails & pointer_details)41 TouchEventX11(EventType type,
42 gfx::Point location,
43 base::TimeTicks timestamp,
44 const PointerDetails& pointer_details)
45 : TouchEvent(type, location, timestamp, pointer_details) {}
46
~TouchEventX11()47 ~TouchEventX11() override {
48 if (type() == ET_TOUCH_RELEASED || type() == ET_TOUCH_CANCELLED)
49 TouchFactory::GetInstance()->ReleaseSlot(pointer_details().id);
50 }
51 };
52
GetEventPropertiesFromXEvent(EventType type,const x11::Event & x11_event)53 Event::Properties GetEventPropertiesFromXEvent(EventType type,
54 const x11::Event& x11_event) {
55 using Values = std::vector<uint8_t>;
56 Event::Properties properties;
57 if (type == ET_KEY_PRESSED || type == ET_KEY_RELEASED) {
58 auto* key = x11_event.As<x11::KeyEvent>();
59
60 // Keyboard group
61 auto state = static_cast<uint32_t>(key->state);
62 uint8_t group = XkbGroupForCoreState(state);
63 properties.emplace(kPropertyKeyboardGroup, Values{group});
64
65 // Hardware keycode
66 uint8_t hw_keycode = static_cast<uint8_t>(key->detail);
67 properties.emplace(kPropertyKeyboardHwKeyCode, Values{hw_keycode});
68
69 // IBus-gtk specific flags
70 uint8_t ibus_flags = (state >> kPropertyKeyboardIBusFlagOffset) &
71 kPropertyKeyboardIBusFlagMask;
72 if (ibus_flags)
73 properties.emplace(kPropertyKeyboardIBusFlag, Values{ibus_flags});
74
75 } else if (type == ET_MOUSE_EXITED) {
76 // NotifyVirtual events are created for intermediate windows that the
77 // pointer crosses through. These occur when middle clicking.
78 // Change these into mouse move events.
79 auto* crossing = x11_event.As<x11::CrossingEvent>();
80 bool crossing_intermediate_window =
81 crossing->detail == x11::NotifyDetail::Virtual;
82 if (crossing_intermediate_window) {
83 properties.emplace(kPropertyMouseCrossedIntermediateWindow,
84 crossing_intermediate_window);
85 }
86 }
87 return properties;
88 }
89
CreateKeyEvent(EventType event_type,const x11::Event & x11_event)90 std::unique_ptr<KeyEvent> CreateKeyEvent(EventType event_type,
91 const x11::Event& x11_event) {
92 KeyboardCode key_code = KeyboardCodeFromXKeyEvent(x11_event);
93 int event_flags = EventFlagsFromXEvent(x11_event);
94
95 // In Ozone builds, keep DomCode/DomKey unset, so they are extracted lazily
96 // in KeyEvent::ApplyLayout() which makes it possible for CrOS/Linux, for
97 // example, to support host system keyboard layouts.
98 std::unique_ptr<KeyEvent> event;
99 #if defined(USE_OZONE)
100 if (features::IsUsingOzonePlatform()) {
101 event = std::make_unique<KeyEvent>(event_type, key_code, event_flags,
102 EventTimeFromXEvent(x11_event));
103 }
104 #endif
105 #if defined(USE_X11)
106 if (!event) {
107 event = std::make_unique<KeyEvent>(
108 event_type, key_code, CodeFromXEvent(x11_event), event_flags,
109 GetDomKeyFromXEvent(x11_event), EventTimeFromXEvent(x11_event));
110 }
111 #endif
112
113 DCHECK(event);
114 event->SetProperties(GetEventPropertiesFromXEvent(event_type, x11_event));
115 event->InitializeNative();
116 return event;
117 }
118
SetEventSourceDeviceId(MouseEvent * event,const x11::Event & xev)119 void SetEventSourceDeviceId(MouseEvent* event, const x11::Event& xev) {
120 DCHECK(event);
121 if (auto* xiev = xev.As<x11::Input::DeviceEvent>())
122 event->set_source_device_id(static_cast<uint16_t>(xiev->sourceid));
123 }
124
CreateMouseEvent(EventType type,const x11::Event & x11_event)125 std::unique_ptr<MouseEvent> CreateMouseEvent(EventType type,
126 const x11::Event& x11_event) {
127 // Ignore EventNotify and LeaveNotify events from children of |xwindow_|.
128 // NativeViewGLSurfaceGLX adds a child to |xwindow_|.
129 // https://crbug.com/792322
130 auto* crossing = x11_event.As<x11::CrossingEvent>();
131 if (crossing && crossing->detail == x11::NotifyDetail::Inferior)
132 return nullptr;
133
134 PointerDetails details{EventPointerType::kMouse};
135 auto event = std::make_unique<MouseEvent>(
136 type, EventLocationFromXEvent(x11_event),
137 EventSystemLocationFromXEvent(x11_event), EventTimeFromXEvent(x11_event),
138 EventFlagsFromXEvent(x11_event),
139 GetChangedMouseButtonFlagsFromXEvent(x11_event), details);
140
141 DCHECK(event);
142 SetEventSourceDeviceId(event.get(), x11_event);
143 event->SetProperties(GetEventPropertiesFromXEvent(type, x11_event));
144 event->InitializeNative();
145 return event;
146 }
147
CreateMouseWheelEvent(const x11::Event & x11_event)148 std::unique_ptr<MouseWheelEvent> CreateMouseWheelEvent(
149 const x11::Event& x11_event) {
150 int button_flags = x11_event.As<x11::Input::DeviceEvent>()
151 ? GetChangedMouseButtonFlagsFromXEvent(x11_event)
152 : 0;
153 auto event = std::make_unique<MouseWheelEvent>(
154 GetMouseWheelOffsetFromXEvent(x11_event),
155 EventLocationFromXEvent(x11_event),
156 EventSystemLocationFromXEvent(x11_event), EventTimeFromXEvent(x11_event),
157 EventFlagsFromXEvent(x11_event), button_flags);
158
159 DCHECK(event);
160 event->InitializeNative();
161 return event;
162 }
163
CreateTouchEvent(EventType type,const x11::Event & xev)164 std::unique_ptr<TouchEvent> CreateTouchEvent(EventType type,
165 const x11::Event& xev) {
166 auto event = std::make_unique<TouchEventX11>(
167 type, EventLocationFromXEvent(xev), EventTimeFromXEvent(xev),
168 GetTouchPointerDetailsFromXEvent(xev));
169 #if defined(USE_OZONE)
170 if (features::IsUsingOzonePlatform()) {
171 // Touch events don't usually have |root_location| set differently than
172 // |location|, since there is a touch device to display association, but
173 // this doesn't happen in Ozone X11.
174 event->set_root_location(EventSystemLocationFromXEvent(xev));
175 }
176 #endif
177 return event;
178 }
179
CreateScrollEvent(EventType type,const x11::Event & xev)180 std::unique_ptr<ScrollEvent> CreateScrollEvent(EventType type,
181 const x11::Event& xev) {
182 float x_offset, y_offset, x_offset_ordinal, y_offset_ordinal;
183 int finger_count = 0;
184
185 if (type == ET_SCROLL) {
186 GetScrollOffsetsFromXEvent(xev, &x_offset, &y_offset, &x_offset_ordinal,
187 &y_offset_ordinal, &finger_count);
188 } else {
189 GetFlingDataFromXEvent(xev, &x_offset, &y_offset, &x_offset_ordinal,
190 &y_offset_ordinal, nullptr);
191 }
192 auto event = std::make_unique<ScrollEvent>(
193 type, EventLocationFromXEvent(xev), EventTimeFromXEvent(xev),
194 EventFlagsFromXEvent(xev), x_offset, y_offset, x_offset_ordinal,
195 y_offset_ordinal, finger_count);
196
197 DCHECK(event);
198 // We need to filter zero scroll offset here. Because MouseWheelEventQueue
199 // assumes we'll never get a zero scroll offset event and we need delta to
200 // determine which element to scroll on phaseBegan.
201 return (event->x_offset() != 0.0 || event->y_offset() != 0.0)
202 ? std::move(event)
203 : nullptr;
204 }
205
206 // Translates XI2 XEvent into a ui::Event.
TranslateFromXI2Event(const x11::Event & xev,EventType event_type)207 std::unique_ptr<ui::Event> TranslateFromXI2Event(const x11::Event& xev,
208 EventType event_type) {
209 switch (event_type) {
210 case ET_KEY_PRESSED:
211 case ET_KEY_RELEASED:
212 return CreateKeyEvent(event_type, xev);
213 case ET_MOUSE_PRESSED:
214 case ET_MOUSE_RELEASED:
215 case ET_MOUSE_MOVED:
216 case ET_MOUSE_DRAGGED:
217 case ET_MOUSE_ENTERED:
218 case ET_MOUSE_EXITED:
219 return CreateMouseEvent(event_type, xev);
220 case ET_MOUSEWHEEL:
221 return CreateMouseWheelEvent(xev);
222 case ET_SCROLL_FLING_START:
223 case ET_SCROLL_FLING_CANCEL:
224 case ET_SCROLL:
225 return CreateScrollEvent(event_type, xev);
226 case ET_TOUCH_MOVED:
227 case ET_TOUCH_PRESSED:
228 case ET_TOUCH_CANCELLED:
229 case ET_TOUCH_RELEASED:
230 return CreateTouchEvent(event_type, xev);
231 case ET_UNKNOWN:
232 return nullptr;
233 default:
234 break;
235 }
236 return nullptr;
237 }
238
TranslateFromXEvent(const x11::Event & xev)239 std::unique_ptr<Event> TranslateFromXEvent(const x11::Event& xev) {
240 EventType event_type = EventTypeFromXEvent(xev);
241 if (xev.As<x11::CrossingEvent>() || xev.As<x11::MotionNotifyEvent>())
242 return CreateMouseEvent(event_type, xev);
243 if (xev.As<x11::KeyEvent>())
244 return CreateKeyEvent(event_type, xev);
245 if (xev.As<x11::ButtonEvent>()) {
246 switch (event_type) {
247 case ET_MOUSEWHEEL:
248 return CreateMouseWheelEvent(xev);
249 case ET_MOUSE_PRESSED:
250 case ET_MOUSE_RELEASED:
251 return CreateMouseEvent(event_type, xev);
252 case ET_UNKNOWN:
253 // No event is created for X11-release events for mouse-wheel
254 // buttons.
255 break;
256 default:
257 NOTREACHED();
258 }
259 }
260 if (xev.As<x11::Input::DeviceEvent>())
261 return TranslateFromXI2Event(xev, event_type);
262 return nullptr;
263 }
264
265 } // namespace
266
267 // Translates a XEvent into a ui::Event.
BuildEventFromXEvent(const x11::Event & xev)268 std::unique_ptr<Event> BuildEventFromXEvent(const x11::Event& xev) {
269 auto event = TranslateFromXEvent(xev);
270 if (event)
271 ui::ComputeEventLatencyOS(event.get());
272 return event;
273 }
274
275 // Convenience function that translates XEvent into ui::KeyEvent
BuildKeyEventFromXEvent(const x11::Event & xev)276 std::unique_ptr<KeyEvent> BuildKeyEventFromXEvent(const x11::Event& xev) {
277 auto event = BuildEventFromXEvent(xev);
278 if (!event || !event->IsKeyEvent())
279 return nullptr;
280 return std::unique_ptr<KeyEvent>{event.release()->AsKeyEvent()};
281 }
282
283 // Convenience function that translates XEvent into ui::MouseEvent
BuildMouseEventFromXEvent(const x11::Event & xev)284 std::unique_ptr<MouseEvent> BuildMouseEventFromXEvent(const x11::Event& xev) {
285 auto event = BuildEventFromXEvent(xev);
286 if (!event || !event->IsMouseEvent())
287 return nullptr;
288 return std::unique_ptr<MouseEvent>{event.release()->AsMouseEvent()};
289 }
290
291 // Convenience function that translates XEvent into ui::TouchEvent
BuildTouchEventFromXEvent(const x11::Event & xev)292 std::unique_ptr<TouchEvent> BuildTouchEventFromXEvent(const x11::Event& xev) {
293 auto event = BuildEventFromXEvent(xev);
294 if (!event || !event->IsTouchEvent())
295 return nullptr;
296 return std::unique_ptr<TouchEvent>{event.release()->AsTouchEvent()};
297 }
298
299 // Convenience function that translates XEvent into ui::MouseWheelEvent
BuildMouseWheelEventFromXEvent(const x11::Event & xev)300 std::unique_ptr<MouseWheelEvent> BuildMouseWheelEventFromXEvent(
301 const x11::Event& xev) {
302 auto event = BuildEventFromXEvent(xev);
303 if (!event || !event->IsMouseWheelEvent())
304 return nullptr;
305 return std::unique_ptr<MouseWheelEvent>{event.release()->AsMouseWheelEvent()};
306 }
307
308 } // namespace ui
309