1// Copyright 2013 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/test/events_test_utils_x11.h"
6
7#include <X11/extensions/XI2.h>
8#include <X11/keysym.h>
9#include <X11/X.h>
10#include <X11/Xlib.h>
11
12#include "base/logging.h"
13#include "ui/events/event_constants.h"
14#include "ui/events/event_utils.h"
15#include "ui/events/keycodes/keyboard_code_conversion_x.h"
16#include "ui/events/x/touch_factory_x11.h"
17
18namespace {
19
20// Converts ui::EventType to state for X*Events.
21unsigned int XEventState(int flags) {
22  return
23      ((flags & ui::EF_SHIFT_DOWN) ? ShiftMask : 0) |
24      ((flags & ui::EF_CONTROL_DOWN) ? ControlMask : 0) |
25      ((flags & ui::EF_ALT_DOWN) ? Mod1Mask : 0) |
26      ((flags & ui::EF_CAPS_LOCK_DOWN) ? LockMask : 0) |
27      ((flags & ui::EF_ALTGR_DOWN) ? Mod5Mask : 0) |
28      ((flags & ui::EF_COMMAND_DOWN) ? Mod4Mask : 0) |
29      ((flags & ui::EF_MOD3_DOWN) ? Mod3Mask : 0) |
30      ((flags & ui::EF_NUMPAD_KEY) ? Mod2Mask : 0) |
31      ((flags & ui::EF_LEFT_MOUSE_BUTTON) ? Button1Mask: 0) |
32      ((flags & ui::EF_MIDDLE_MOUSE_BUTTON) ? Button2Mask: 0) |
33      ((flags & ui::EF_RIGHT_MOUSE_BUTTON) ? Button3Mask: 0);
34}
35
36// Converts EventType to XKeyEvent type.
37int XKeyEventType(ui::EventType type) {
38  switch (type) {
39    case ui::ET_KEY_PRESSED:
40      return KeyPress;
41    case ui::ET_KEY_RELEASED:
42      return KeyRelease;
43    default:
44      return 0;
45  }
46}
47
48// Converts EventType to XI2 event type.
49int XIKeyEventType(ui::EventType type) {
50  switch (type) {
51    case ui::ET_KEY_PRESSED:
52      return XI_KeyPress;
53    case ui::ET_KEY_RELEASED:
54      return XI_KeyRelease;
55    default:
56      return 0;
57  }
58}
59
60int XIButtonEventType(ui::EventType type) {
61  switch (type) {
62    case ui::ET_MOUSEWHEEL:
63    case ui::ET_MOUSE_PRESSED:
64      // The button release X events for mouse wheels are dropped by Aura.
65      return XI_ButtonPress;
66    case ui::ET_MOUSE_RELEASED:
67      return XI_ButtonRelease;
68    default:
69      NOTREACHED();
70      return 0;
71  }
72}
73
74// Converts Aura event type and flag to X button event.
75unsigned int XButtonEventButton(ui::EventType type,
76                                int flags) {
77  // Aura events don't keep track of mouse wheel button, so just return
78  // the first mouse wheel button.
79  if (type == ui::ET_MOUSEWHEEL)
80    return Button4;
81
82  if (flags & ui::EF_LEFT_MOUSE_BUTTON)
83    return Button1;
84  if (flags & ui::EF_MIDDLE_MOUSE_BUTTON)
85    return Button2;
86  if (flags & ui::EF_RIGHT_MOUSE_BUTTON)
87    return Button3;
88
89  return 0;
90}
91
92void InitValuatorsForXIDeviceEvent(XIDeviceEvent* xiev) {
93  int valuator_count = ui::DeviceDataManagerX11::DT_LAST_ENTRY;
94  xiev->valuators.mask_len = (valuator_count / 8) + 1;
95  xiev->valuators.mask = new unsigned char[xiev->valuators.mask_len];
96  memset(xiev->valuators.mask, 0, xiev->valuators.mask_len);
97  xiev->valuators.values = new double[valuator_count];
98}
99
100XEvent* CreateXInput2Event(int deviceid,
101                           int evtype,
102                           int tracking_id,
103                           const gfx::Point& location) {
104  XEvent* event = new XEvent;
105  memset(event, 0, sizeof(*event));
106  event->type = GenericEvent;
107  event->xcookie.data = new XIDeviceEvent;
108  XIDeviceEvent* xiev =
109      static_cast<XIDeviceEvent*>(event->xcookie.data);
110  memset(xiev, 0, sizeof(XIDeviceEvent));
111  xiev->deviceid = deviceid;
112  xiev->sourceid = deviceid;
113  xiev->evtype = evtype;
114  xiev->detail = tracking_id;
115  xiev->event_x = location.x();
116  xiev->event_y = location.y();
117  xiev->event = DefaultRootWindow(gfx::GetXDisplay());
118  if (evtype == XI_ButtonPress || evtype == XI_ButtonRelease) {
119    xiev->buttons.mask_len = 8;
120    xiev->buttons.mask = new unsigned char[xiev->buttons.mask_len];
121    memset(xiev->buttons.mask, 0, xiev->buttons.mask_len);
122  }
123  return event;
124}
125
126}  // namespace
127
128namespace ui {
129
130// XInput2 events contain additional data that need to be explicitly freed (see
131// |CreateXInput2Event()|.
132void XEventDeleter::operator()(XEvent* event) {
133  if (event->type == GenericEvent) {
134    XIDeviceEvent* xiev =
135        static_cast<XIDeviceEvent*>(event->xcookie.data);
136    if (xiev) {
137      delete[] xiev->valuators.mask;
138      delete[] xiev->valuators.values;
139      delete[] xiev->buttons.mask;
140      delete xiev;
141    }
142  }
143  delete event;
144}
145
146ScopedXI2Event::ScopedXI2Event() {}
147ScopedXI2Event::~ScopedXI2Event() {}
148
149void ScopedXI2Event::InitKeyEvent(EventType type,
150                                  KeyboardCode key_code,
151                                  int flags) {
152  XDisplay* display = gfx::GetXDisplay();
153  event_.reset(new XEvent);
154  memset(event_.get(), 0, sizeof(XEvent));
155  event_->type = XKeyEventType(type);
156  CHECK_NE(0, event_->type);
157  event_->xkey.serial = 0;
158  event_->xkey.send_event = 0;
159  event_->xkey.display = display;
160  event_->xkey.time = 0;
161  event_->xkey.window = 0;
162  event_->xkey.root = 0;
163  event_->xkey.subwindow = 0;
164  event_->xkey.x = 0;
165  event_->xkey.y = 0;
166  event_->xkey.x_root = 0;
167  event_->xkey.y_root = 0;
168  event_->xkey.state = XEventState(flags);
169  event_->xkey.keycode = XKeyCodeForWindowsKeyCode(key_code, flags, display);
170  event_->xkey.same_screen = 1;
171}
172
173void ScopedXI2Event::InitGenericKeyEvent(int deviceid,
174                                         int sourceid,
175                                         EventType type,
176                                         KeyboardCode key_code,
177                                         int flags) {
178  event_.reset(
179      CreateXInput2Event(deviceid, XIKeyEventType(type), 0, gfx::Point()));
180  XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(event_->xcookie.data);
181  CHECK_NE(0, xievent->evtype);
182  XDisplay* display = gfx::GetXDisplay();
183  event_->xgeneric.display = display;
184  xievent->display = display;
185  xievent->mods.effective = XEventState(flags);
186  xievent->detail = XKeyCodeForWindowsKeyCode(key_code, flags, display);
187  xievent->sourceid = sourceid;
188}
189
190void ScopedXI2Event::InitGenericButtonEvent(int deviceid,
191                                            EventType type,
192                                            const gfx::Point& location,
193                                            int flags) {
194  event_.reset(CreateXInput2Event(deviceid,
195                                  XIButtonEventType(type), 0, gfx::Point()));
196  XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(event_->xcookie.data);
197  xievent->mods.effective = XEventState(flags);
198  xievent->detail = XButtonEventButton(type, flags);
199  xievent->event_x = location.x();
200  xievent->event_y = location.y();
201  XISetMask(xievent->buttons.mask, xievent->detail);
202  // Setup an empty valuator list for generic button events.
203  SetUpValuators(std::vector<Valuator>());
204}
205
206void ScopedXI2Event::InitGenericMouseWheelEvent(int deviceid,
207                                                int wheel_delta,
208                                                int flags) {
209  InitGenericButtonEvent(deviceid, ui::ET_MOUSEWHEEL, gfx::Point(), flags);
210  XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(event_->xcookie.data);
211  xievent->detail = wheel_delta > 0 ? Button4 : Button5;
212}
213
214void ScopedXI2Event::InitScrollEvent(int deviceid,
215                                     int x_offset,
216                                     int y_offset,
217                                     int x_offset_ordinal,
218                                     int y_offset_ordinal,
219                                     int finger_count) {
220  event_.reset(CreateXInput2Event(deviceid, XI_Motion, 0, gfx::Point()));
221
222  Valuator valuators[] = {
223    Valuator(DeviceDataManagerX11::DT_CMT_SCROLL_X, x_offset),
224    Valuator(DeviceDataManagerX11::DT_CMT_SCROLL_Y, y_offset),
225    Valuator(DeviceDataManagerX11::DT_CMT_ORDINAL_X, x_offset_ordinal),
226    Valuator(DeviceDataManagerX11::DT_CMT_ORDINAL_Y, y_offset_ordinal),
227    Valuator(DeviceDataManagerX11::DT_CMT_FINGER_COUNT, finger_count)
228  };
229  SetUpValuators(
230      std::vector<Valuator>(valuators, valuators + arraysize(valuators)));
231}
232
233void ScopedXI2Event::InitFlingScrollEvent(int deviceid,
234                                          int x_velocity,
235                                          int y_velocity,
236                                          int x_velocity_ordinal,
237                                          int y_velocity_ordinal,
238                                          bool is_cancel) {
239  event_.reset(CreateXInput2Event(deviceid, XI_Motion, deviceid, gfx::Point()));
240
241  Valuator valuators[] = {
242    Valuator(DeviceDataManagerX11::DT_CMT_FLING_STATE, is_cancel ? 1 : 0),
243    Valuator(DeviceDataManagerX11::DT_CMT_FLING_Y, y_velocity),
244    Valuator(DeviceDataManagerX11::DT_CMT_ORDINAL_Y, y_velocity_ordinal),
245    Valuator(DeviceDataManagerX11::DT_CMT_FLING_X, x_velocity),
246    Valuator(DeviceDataManagerX11::DT_CMT_ORDINAL_X, x_velocity_ordinal)
247  };
248
249  SetUpValuators(
250      std::vector<Valuator>(valuators, valuators + arraysize(valuators)));
251}
252
253void ScopedXI2Event::InitTouchEvent(int deviceid,
254                                    int evtype,
255                                    int tracking_id,
256                                    const gfx::Point& location,
257                                    const std::vector<Valuator>& valuators) {
258  event_.reset(CreateXInput2Event(deviceid, evtype, tracking_id, location));
259
260  // If a timestamp was specified, setup the event.
261  for (size_t i = 0; i < valuators.size(); ++i) {
262    if (valuators[i].data_type ==
263        DeviceDataManagerX11::DT_TOUCH_RAW_TIMESTAMP) {
264      SetUpValuators(valuators);
265      return;
266    }
267  }
268
269  // No timestamp was specified. Use |ui::EventTimeForNow()|.
270  std::vector<Valuator> valuators_with_time = valuators;
271  valuators_with_time.push_back(
272      Valuator(DeviceDataManagerX11::DT_TOUCH_RAW_TIMESTAMP,
273               (ui::EventTimeForNow()).InMicroseconds()));
274  SetUpValuators(valuators_with_time);
275}
276
277void ScopedXI2Event::SetUpValuators(const std::vector<Valuator>& valuators) {
278  CHECK(event_.get());
279  CHECK_EQ(GenericEvent, event_->type);
280  XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(event_->xcookie.data);
281  InitValuatorsForXIDeviceEvent(xiev);
282  ui::DeviceDataManagerX11* manager = ui::DeviceDataManagerX11::GetInstance();
283  for (size_t i = 0; i < valuators.size(); ++i) {
284    manager->SetValuatorDataForTest(xiev, valuators[i].data_type,
285                                    valuators[i].value);
286  }
287}
288
289void SetUpTouchPadForTest(unsigned int deviceid) {
290  std::vector<unsigned int> device_list;
291  device_list.push_back(deviceid);
292
293  TouchFactory::GetInstance()->SetPointerDeviceForTest(device_list);
294  ui::DeviceDataManagerX11* manager = ui::DeviceDataManagerX11::GetInstance();
295  manager->SetDeviceListForTest(std::vector<unsigned int>(), device_list);
296}
297
298void SetUpTouchDevicesForTest(const std::vector<unsigned int>& devices) {
299  TouchFactory::GetInstance()->SetTouchDeviceForTest(devices);
300  ui::DeviceDataManagerX11* manager = ui::DeviceDataManagerX11::GetInstance();
301  manager->SetDeviceListForTest(devices, std::vector<unsigned int>());
302}
303
304}  // namespace ui
305