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/keycodes/keyboard_code_conversion_x.h"
15#include "ui/events/x/touch_factory_x11.h"
16
17namespace {
18
19// Converts ui::EventType to state for X*Events.
20unsigned int XEventState(int flags) {
21  return
22      ((flags & ui::EF_SHIFT_DOWN) ? ShiftMask : 0) |
23      ((flags & ui::EF_CONTROL_DOWN) ? ControlMask : 0) |
24      ((flags & ui::EF_ALT_DOWN) ? Mod1Mask : 0) |
25      ((flags & ui::EF_CAPS_LOCK_DOWN) ? LockMask : 0) |
26      ((flags & ui::EF_LEFT_MOUSE_BUTTON) ? Button1Mask: 0) |
27      ((flags & ui::EF_MIDDLE_MOUSE_BUTTON) ? Button2Mask: 0) |
28      ((flags & ui::EF_RIGHT_MOUSE_BUTTON) ? Button3Mask: 0);
29}
30
31// Converts EventType to XKeyEvent type.
32int XKeyEventType(ui::EventType type) {
33  switch (type) {
34    case ui::ET_KEY_PRESSED:
35      return KeyPress;
36    case ui::ET_KEY_RELEASED:
37      return KeyRelease;
38    default:
39      return 0;
40  }
41}
42
43// Converts EventType to XButtonEvent type.
44int XButtonEventType(ui::EventType type) {
45  switch (type) {
46    case ui::ET_MOUSEWHEEL:
47    case ui::ET_MOUSE_PRESSED:
48      // The button release X events for mouse wheels are dropped by Aura.
49      return ButtonPress;
50    case ui::ET_MOUSE_RELEASED:
51      return ButtonRelease;
52    default:
53      return 0;
54  }
55}
56
57// Converts KeyboardCode to XKeyEvent keycode.
58unsigned int XKeyEventKeyCode(ui::KeyboardCode key_code,
59                              int flags,
60                              XDisplay* display) {
61  const int keysym = XKeysymForWindowsKeyCode(key_code,
62                                              flags & ui::EF_SHIFT_DOWN);
63  // Tests assume the keycode for XK_less is equal to the one of XK_comma,
64  // but XKeysymToKeycode returns 94 for XK_less while it returns 59 for
65  // XK_comma. Here we convert the value for XK_less to the value for XK_comma.
66  return (keysym == XK_less) ? 59 : XKeysymToKeycode(display, keysym);
67}
68
69// Converts Aura event type and flag to X button event.
70unsigned int XButtonEventButton(ui::EventType type,
71                                int flags) {
72  // Aura events don't keep track of mouse wheel button, so just return
73  // the first mouse wheel button.
74  if (type == ui::ET_MOUSEWHEEL)
75    return Button4;
76
77  switch (flags) {
78    case ui::EF_LEFT_MOUSE_BUTTON:
79      return Button1;
80    case ui::EF_MIDDLE_MOUSE_BUTTON:
81      return Button2;
82    case ui::EF_RIGHT_MOUSE_BUTTON:
83      return Button3;
84  }
85
86  return 0;
87}
88
89void InitValuatorsForXIDeviceEvent(XIDeviceEvent* xiev) {
90  int valuator_count = ui::DeviceDataManager::DT_LAST_ENTRY;
91  xiev->valuators.mask_len = (valuator_count / 8) + 1;
92  xiev->valuators.mask = new unsigned char[xiev->valuators.mask_len];
93  memset(xiev->valuators.mask, 0, xiev->valuators.mask_len);
94  xiev->valuators.values = new double[valuator_count];
95}
96
97XEvent* CreateXInput2Event(int deviceid,
98                           int evtype,
99                           int tracking_id,
100                           const gfx::Point& location) {
101  XEvent* event = new XEvent;
102  memset(event, 0, sizeof(*event));
103  event->type = GenericEvent;
104  event->xcookie.data = new XIDeviceEvent;
105  XIDeviceEvent* xiev =
106      static_cast<XIDeviceEvent*>(event->xcookie.data);
107  memset(xiev, 0, sizeof(XIDeviceEvent));
108  xiev->deviceid = deviceid;
109  xiev->sourceid = deviceid;
110  xiev->evtype = evtype;
111  xiev->detail = tracking_id;
112  xiev->event_x = location.x();
113  xiev->event_y = location.y();
114
115  return event;
116}
117
118}  // namespace
119
120namespace ui {
121
122ScopedXI2Event::ScopedXI2Event() {}
123ScopedXI2Event::~ScopedXI2Event() {
124  Cleanup();
125}
126
127void ScopedXI2Event::InitKeyEvent(EventType type,
128                                  KeyboardCode key_code,
129                                  int flags) {
130  Cleanup();
131  XDisplay* display = gfx::GetXDisplay();
132  event_.reset(new XEvent);
133  memset(event_.get(), 0, sizeof(XEvent));
134  event_->type = XKeyEventType(type);
135  CHECK_NE(0, event_->type);
136  event_->xkey.serial = 0;
137  event_->xkey.send_event = 0;
138  event_->xkey.display = display;
139  event_->xkey.time = 0;
140  event_->xkey.window = 0;
141  event_->xkey.root = 0;
142  event_->xkey.subwindow = 0;
143  event_->xkey.x = 0;
144  event_->xkey.y = 0;
145  event_->xkey.x_root = 0;
146  event_->xkey.y_root = 0;
147  event_->xkey.state = XEventState(flags);
148  event_->xkey.keycode = XKeyEventKeyCode(key_code, flags, display);
149  event_->xkey.same_screen = 1;
150}
151
152void ScopedXI2Event::InitButtonEvent(EventType type,
153                                     int flags) {
154  Cleanup();
155  event_.reset(new XEvent);
156  memset(event_.get(), 0, sizeof(XEvent));
157  event_->type = XButtonEventType(type);
158  CHECK_NE(0, event_->type);
159  event_->xbutton.serial = 0;
160  event_->xbutton.send_event = 0;
161  event_->xbutton.display = gfx::GetXDisplay();
162  event_->xbutton.time = 0;
163  event_->xbutton.window = 0;
164  event_->xbutton.root = 0;
165  event_->xbutton.subwindow = 0;
166  event_->xbutton.x = 0;
167  event_->xbutton.y = 0;
168  event_->xbutton.x_root = 0;
169  event_->xbutton.y_root = 0;
170  event_->xbutton.state = XEventState(flags);
171  event_->xbutton.button = XButtonEventButton(type, flags);
172  event_->xbutton.same_screen = 1;
173}
174
175void ScopedXI2Event::InitMouseWheelEvent(int wheel_delta,
176                                         int flags) {
177  InitButtonEvent(ui::ET_MOUSEWHEEL, flags);
178  // MouseWheelEvents are not taking horizontal scrolls into account
179  // at the moment.
180  event_->xbutton.button = wheel_delta > 0 ? Button4 : Button5;
181}
182
183void ScopedXI2Event::InitScrollEvent(int deviceid,
184                                     int x_offset,
185                                     int y_offset,
186                                     int x_offset_ordinal,
187                                     int y_offset_ordinal,
188                                     int finger_count) {
189  Cleanup();
190  event_.reset(CreateXInput2Event(deviceid, XI_Motion, 0, gfx::Point()));
191
192  Valuator valuators[] = {
193    Valuator(DeviceDataManager::DT_CMT_SCROLL_X, x_offset),
194    Valuator(DeviceDataManager::DT_CMT_SCROLL_Y, y_offset),
195    Valuator(DeviceDataManager::DT_CMT_ORDINAL_X, x_offset_ordinal),
196    Valuator(DeviceDataManager::DT_CMT_ORDINAL_Y, y_offset_ordinal),
197    Valuator(DeviceDataManager::DT_CMT_FINGER_COUNT, finger_count)
198  };
199  SetUpValuators(
200      std::vector<Valuator>(valuators, valuators + arraysize(valuators)));
201}
202
203void ScopedXI2Event::InitFlingScrollEvent(int deviceid,
204                                          int x_velocity,
205                                          int y_velocity,
206                                          int x_velocity_ordinal,
207                                          int y_velocity_ordinal,
208                                          bool is_cancel) {
209  Cleanup();
210  event_.reset(CreateXInput2Event(deviceid, XI_Motion, deviceid, gfx::Point()));
211
212  Valuator valuators[] = {
213    Valuator(DeviceDataManager::DT_CMT_FLING_STATE, is_cancel ? 1 : 0),
214    Valuator(DeviceDataManager::DT_CMT_FLING_Y, y_velocity),
215    Valuator(DeviceDataManager::DT_CMT_ORDINAL_Y, y_velocity_ordinal),
216    Valuator(DeviceDataManager::DT_CMT_FLING_X, x_velocity),
217    Valuator(DeviceDataManager::DT_CMT_ORDINAL_X, x_velocity_ordinal)
218  };
219
220  SetUpValuators(
221      std::vector<Valuator>(valuators, valuators + arraysize(valuators)));
222}
223
224void ScopedXI2Event::InitTouchEvent(int deviceid,
225                                    int evtype,
226                                    int tracking_id,
227                                    const gfx::Point& location,
228                                    const std::vector<Valuator>& valuators) {
229  Cleanup();
230  event_.reset(CreateXInput2Event(deviceid, evtype, tracking_id, location));
231  SetUpValuators(valuators);
232}
233
234void ScopedXI2Event::Cleanup() {
235  if (event_.get() && event_->type == GenericEvent) {
236    XIDeviceEvent* xiev =
237        static_cast<XIDeviceEvent*>(event_->xcookie.data);
238    if (xiev) {
239      delete[] xiev->valuators.mask;
240      delete[] xiev->valuators.values;
241      delete xiev;
242    }
243  }
244  event_.reset();
245}
246
247void ScopedXI2Event::SetUpValuators(const std::vector<Valuator>& valuators) {
248  CHECK(event_.get());
249  CHECK_EQ(GenericEvent, event_->type);
250  XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(event_->xcookie.data);
251  InitValuatorsForXIDeviceEvent(xiev);
252  ui::DeviceDataManager* manager = ui::DeviceDataManager::GetInstance();
253  for (size_t i = 0; i < valuators.size(); ++i) {
254    manager->SetValuatorDataForTest(xiev, valuators[i].data_type,
255                                    valuators[i].value);
256  }
257}
258
259void SetUpScrollDeviceForTest(unsigned int deviceid) {
260  std::vector<unsigned int> device_list;
261  device_list.push_back(deviceid);
262
263  TouchFactory::GetInstance()->SetPointerDeviceForTest(device_list);
264  ui::DeviceDataManager* manager = ui::DeviceDataManager::GetInstance();
265  manager->SetDeviceListForTest(std::vector<unsigned int>(), device_list);
266}
267
268void SetUpTouchDevicesForTest(const std::vector<unsigned int>& devices) {
269  TouchFactory::GetInstance()->SetTouchDeviceForTest(devices);
270  ui::DeviceDataManager* manager = ui::DeviceDataManager::GetInstance();
271  manager->SetDeviceListForTest(devices, std::vector<unsigned int>());
272}
273
274}  // namespace ui
275