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. 4 5 #include <cstring> 6 7 #include <X11/extensions/XInput2.h> 8 #include <X11/Xlib.h> 9 10 // Generically-named #defines from Xlib that conflict with symbols in GTest. 11 #undef Bool 12 #undef None 13 14 #include "testing/gtest/include/gtest/gtest.h" 15 #include "ui/base/events/event_constants.h" 16 #include "ui/base/events/event_utils.h" 17 #include "ui/base/touch/touch_factory_x11.h" 18 #include "ui/base/x/device_data_manager.h" 19 #include "ui/gfx/point.h" 20 21 namespace ui { 22 23 namespace { 24 25 // Initializes the passed-in Xlib event. 26 void InitButtonEvent(XEvent* event, 27 bool is_press, 28 const gfx::Point& location, 29 int button, 30 int state) { 31 memset(event, 0, sizeof(*event)); 32 33 // We don't bother setting fields that the event code doesn't use, such as 34 // x_root/y_root and window/root/subwindow. 35 XButtonEvent* button_event = &(event->xbutton); 36 button_event->type = is_press ? ButtonPress : ButtonRelease; 37 button_event->x = location.x(); 38 button_event->y = location.y(); 39 button_event->button = button; 40 button_event->state = state; 41 } 42 43 #if defined(USE_XI2_MT) 44 const int kValuatorNum = 3; 45 const int kTouchValuatorMap[kValuatorNum][4] = { 46 // { valuator_index, valuator_type, min_val, max_val } 47 { 0, DeviceDataManager::DT_TOUCH_MAJOR, 0, 1000}, 48 { 1, DeviceDataManager::DT_TOUCH_ORIENTATION, 0, 1.0}, 49 { 2, DeviceDataManager::DT_TOUCH_PRESSURE, 0, 1000}, 50 }; 51 52 struct Valuator { 53 Valuator(DeviceDataManager::DataType type, double v) 54 : data_type(type), value(v) {} 55 56 DeviceDataManager::DataType data_type; 57 double value; 58 }; 59 60 XEvent* CreateTouchEvent(int deviceid, 61 int evtype, 62 int tracking_id, 63 const gfx::Point& location, 64 const std::vector<Valuator>& valuators) { 65 XEvent* event = new XEvent; 66 memset(event, 0, sizeof(*event)); 67 event->type = GenericEvent; 68 event->xcookie.data = new XIDeviceEvent; 69 XIDeviceEvent* xiev = 70 static_cast<XIDeviceEvent*>(event->xcookie.data); 71 xiev->deviceid = deviceid; 72 xiev->sourceid = deviceid; 73 xiev->evtype = evtype; 74 xiev->detail = tracking_id; 75 xiev->event_x = location.x(); 76 xiev->event_y = location.y(); 77 78 xiev->valuators.mask_len = (valuators.size() / 8) + 1; 79 xiev->valuators.mask = new unsigned char[xiev->valuators.mask_len]; 80 memset(xiev->valuators.mask, 0, xiev->valuators.mask_len); 81 xiev->valuators.values = new double[valuators.size()]; 82 83 int val_count = 0; 84 for (int i = 0; i < kValuatorNum; i++) { 85 for(size_t j = 0; j < valuators.size(); j++) { 86 if (valuators[j].data_type == kTouchValuatorMap[i][1]) { 87 XISetMask(xiev->valuators.mask, kTouchValuatorMap[i][0]); 88 xiev->valuators.values[val_count++] = valuators[j].value; 89 } 90 } 91 } 92 93 return event; 94 } 95 96 void DestroyTouchEvent(XEvent* event) { 97 XIDeviceEvent* xiev = 98 static_cast<XIDeviceEvent*>(event->xcookie.data); 99 if (xiev) { 100 delete[] xiev->valuators.mask; 101 delete[] xiev->valuators.values; 102 delete xiev; 103 } 104 delete event; 105 } 106 107 void SetupTouchFactory(const std::vector<unsigned int>& devices) { 108 TouchFactory* factory = TouchFactory::GetInstance(); 109 factory->SetTouchDeviceForTest(devices); 110 } 111 112 void SetupDeviceDataManager(const std::vector<unsigned int>& devices) { 113 ui::DeviceDataManager* manager = ui::DeviceDataManager::GetInstance(); 114 manager->SetDeviceListForTest(devices); 115 for (size_t i = 0; i < devices.size(); i++) { 116 for (int j = 0; j < kValuatorNum; j++) { 117 manager->SetDeviceValuatorForTest( 118 devices[i], 119 kTouchValuatorMap[j][0], 120 static_cast<DeviceDataManager::DataType>(kTouchValuatorMap[j][1]), 121 kTouchValuatorMap[j][2], 122 kTouchValuatorMap[j][3]); 123 } 124 } 125 } 126 #endif 127 } // namespace 128 129 TEST(EventsXTest, ButtonEvents) { 130 XEvent event; 131 gfx::Point location(5, 10); 132 gfx::Vector2d offset; 133 134 InitButtonEvent(&event, true, location, 1, 0); 135 EXPECT_EQ(ui::ET_MOUSE_PRESSED, ui::EventTypeFromNative(&event)); 136 EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, ui::EventFlagsFromNative(&event)); 137 EXPECT_EQ(location, ui::EventLocationFromNative(&event)); 138 EXPECT_TRUE(ui::IsMouseEvent(&event)); 139 140 InitButtonEvent(&event, true, location, 2, Button1Mask | ShiftMask); 141 EXPECT_EQ(ui::ET_MOUSE_PRESSED, ui::EventTypeFromNative(&event)); 142 EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON | 143 ui::EF_SHIFT_DOWN, 144 ui::EventFlagsFromNative(&event)); 145 EXPECT_EQ(location, ui::EventLocationFromNative(&event)); 146 EXPECT_TRUE(ui::IsMouseEvent(&event)); 147 148 InitButtonEvent(&event, false, location, 3, 0); 149 EXPECT_EQ(ui::ET_MOUSE_RELEASED, ui::EventTypeFromNative(&event)); 150 EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, ui::EventFlagsFromNative(&event)); 151 EXPECT_EQ(location, ui::EventLocationFromNative(&event)); 152 EXPECT_TRUE(ui::IsMouseEvent(&event)); 153 154 // Scroll up. 155 InitButtonEvent(&event, true, location, 4, 0); 156 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(&event)); 157 EXPECT_EQ(0, ui::EventFlagsFromNative(&event)); 158 EXPECT_EQ(location, ui::EventLocationFromNative(&event)); 159 EXPECT_TRUE(ui::IsMouseEvent(&event)); 160 offset = ui::GetMouseWheelOffset(&event); 161 EXPECT_GT(offset.y(), 0); 162 EXPECT_EQ(0, offset.x()); 163 164 // Scroll down. 165 InitButtonEvent(&event, true, location, 5, 0); 166 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(&event)); 167 EXPECT_EQ(0, ui::EventFlagsFromNative(&event)); 168 EXPECT_EQ(location, ui::EventLocationFromNative(&event)); 169 EXPECT_TRUE(ui::IsMouseEvent(&event)); 170 offset = ui::GetMouseWheelOffset(&event); 171 EXPECT_LT(offset.y(), 0); 172 EXPECT_EQ(0, offset.x()); 173 174 // Scroll left, typically. 175 InitButtonEvent(&event, true, location, 6, 0); 176 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(&event)); 177 EXPECT_EQ(0, ui::EventFlagsFromNative(&event)); 178 EXPECT_EQ(location, ui::EventLocationFromNative(&event)); 179 EXPECT_TRUE(ui::IsMouseEvent(&event)); 180 offset = ui::GetMouseWheelOffset(&event); 181 EXPECT_EQ(0, offset.y()); 182 EXPECT_EQ(0, offset.x()); 183 184 // Scroll right, typically. 185 InitButtonEvent(&event, true, location, 7, 0); 186 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(&event)); 187 EXPECT_EQ(0, ui::EventFlagsFromNative(&event)); 188 EXPECT_EQ(location, ui::EventLocationFromNative(&event)); 189 EXPECT_TRUE(ui::IsMouseEvent(&event)); 190 offset = ui::GetMouseWheelOffset(&event); 191 EXPECT_EQ(0, offset.y()); 192 EXPECT_EQ(0, offset.x()); 193 194 // TODO(derat): Test XInput code. 195 } 196 197 TEST(EventsXTest, AvoidExtraEventsOnWheelRelease) { 198 XEvent event; 199 gfx::Point location(5, 10); 200 201 InitButtonEvent(&event, true, location, 4, 0); 202 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(&event)); 203 204 // We should return ET_UNKNOWN for the release event instead of returning 205 // ET_MOUSEWHEEL; otherwise we'll scroll twice for each scrollwheel step. 206 InitButtonEvent(&event, false, location, 4, 0); 207 EXPECT_EQ(ui::ET_UNKNOWN, ui::EventTypeFromNative(&event)); 208 209 // TODO(derat): Test XInput code. 210 } 211 212 TEST(EventsXTest, EnterLeaveEvent) { 213 XEvent event; 214 event.xcrossing.type = EnterNotify; 215 event.xcrossing.x = 10; 216 event.xcrossing.y = 20; 217 event.xcrossing.x_root = 110; 218 event.xcrossing.y_root = 120; 219 220 // Mouse enter events are converted to mouse move events to be consistent with 221 // the way views handle mouse enter. See comments for EnterNotify case in 222 // ui::EventTypeFromNative for more details. 223 EXPECT_EQ(ui::ET_MOUSE_MOVED, ui::EventTypeFromNative(&event)); 224 EXPECT_EQ("10,20", ui::EventLocationFromNative(&event).ToString()); 225 EXPECT_EQ("110,120", ui::EventSystemLocationFromNative(&event).ToString()); 226 227 event.xcrossing.type = LeaveNotify; 228 event.xcrossing.x = 30; 229 event.xcrossing.y = 40; 230 event.xcrossing.x_root = 230; 231 event.xcrossing.y_root = 240; 232 EXPECT_EQ(ui::ET_MOUSE_EXITED, ui::EventTypeFromNative(&event)); 233 EXPECT_EQ("30,40", ui::EventLocationFromNative(&event).ToString()); 234 EXPECT_EQ("230,240", ui::EventSystemLocationFromNative(&event).ToString()); 235 } 236 237 #if defined(USE_XI2_MT) 238 TEST(EventsXTest, TouchEventBasic) { 239 std::vector<unsigned int> devices; 240 devices.push_back(0); 241 SetupTouchFactory(devices); 242 SetupDeviceDataManager(devices); 243 XEvent* event = NULL; 244 std::vector<Valuator> valuators; 245 246 // Init touch begin with tracking id 5, touch id 0. 247 valuators.push_back(Valuator(DeviceDataManager::DT_TOUCH_MAJOR, 20)); 248 valuators.push_back(Valuator(DeviceDataManager::DT_TOUCH_ORIENTATION, 0.3f)); 249 valuators.push_back(Valuator(DeviceDataManager::DT_TOUCH_PRESSURE, 100)); 250 event = CreateTouchEvent(0, XI_TouchBegin, 5, gfx::Point(10, 10), valuators); 251 EXPECT_EQ("10,10", ui::EventLocationFromNative(event).ToString()); 252 EXPECT_EQ(GetTouchId(event), 0); 253 EXPECT_EQ(GetTouchRadiusX(event), 10); 254 EXPECT_FLOAT_EQ(GetTouchAngle(event), 0.15f); 255 EXPECT_FLOAT_EQ(GetTouchForce(event), 0.1f); 256 DestroyTouchEvent(event); 257 258 // Touch update, with new orientation info. 259 valuators.clear(); 260 valuators.push_back(Valuator(DeviceDataManager::DT_TOUCH_ORIENTATION, 0.5f)); 261 event = CreateTouchEvent(0, XI_TouchUpdate, 5, gfx::Point(20, 20), valuators); 262 EXPECT_EQ("20,20", ui::EventLocationFromNative(event).ToString()); 263 EXPECT_EQ(GetTouchId(event), 0); 264 EXPECT_EQ(GetTouchRadiusX(event), 10); 265 EXPECT_FLOAT_EQ(GetTouchAngle(event), 0.25f); 266 EXPECT_FLOAT_EQ(GetTouchForce(event), 0.1f); 267 DestroyTouchEvent(event); 268 269 // Another touch with tracking id 6, touch id 1. 270 valuators.clear(); 271 valuators.push_back(Valuator(DeviceDataManager::DT_TOUCH_MAJOR, 100)); 272 valuators.push_back(Valuator(DeviceDataManager::DT_TOUCH_ORIENTATION, 0.9f)); 273 valuators.push_back(Valuator(DeviceDataManager::DT_TOUCH_PRESSURE, 500)); 274 event = CreateTouchEvent( 275 0, XI_TouchBegin, 6, gfx::Point(200, 200), valuators); 276 EXPECT_EQ("200,200", ui::EventLocationFromNative(event).ToString()); 277 EXPECT_EQ(GetTouchId(event), 1); 278 EXPECT_EQ(GetTouchRadiusX(event), 50); 279 EXPECT_FLOAT_EQ(GetTouchAngle(event), 0.45f); 280 EXPECT_FLOAT_EQ(GetTouchForce(event), 0.5f); 281 DestroyTouchEvent(event); 282 283 // Touch with tracking id 5 should have old radius/angle value and new pressue 284 // value. 285 valuators.clear(); 286 valuators.push_back(Valuator(DeviceDataManager::DT_TOUCH_PRESSURE, 50)); 287 event = CreateTouchEvent(0, XI_TouchEnd, 5, gfx::Point(30, 30), valuators); 288 EXPECT_EQ("30,30", ui::EventLocationFromNative(event).ToString()); 289 EXPECT_EQ(GetTouchId(event), 0); 290 EXPECT_EQ(GetTouchRadiusX(event), 10); 291 EXPECT_FLOAT_EQ(GetTouchAngle(event), 0.25f); 292 EXPECT_FLOAT_EQ(GetTouchForce(event), 0.05f); 293 DestroyTouchEvent(event); 294 295 // Touch with tracking id 6 should have old angle/pressure value and new 296 // radius value. 297 valuators.clear(); 298 valuators.push_back(Valuator(DeviceDataManager::DT_TOUCH_MAJOR, 50)); 299 event = CreateTouchEvent(0, XI_TouchEnd, 6, gfx::Point(200, 200), valuators); 300 EXPECT_EQ("200,200", ui::EventLocationFromNative(event).ToString()); 301 EXPECT_EQ(GetTouchId(event), 1); 302 EXPECT_EQ(GetTouchRadiusX(event), 25); 303 EXPECT_FLOAT_EQ(GetTouchAngle(event), 0.45f); 304 EXPECT_FLOAT_EQ(GetTouchForce(event), 0.5f); 305 DestroyTouchEvent(event); 306 } 307 #endif 308 } // namespace ui 309