|  | // Copyright 2013 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "ui/events/test/events_test_utils_x11.h" | 
|  |  | 
|  | #include <X11/extensions/XI2.h> | 
|  | #include <X11/keysym.h> | 
|  | #include <X11/X.h> | 
|  | #include <X11/Xlib.h> | 
|  |  | 
|  | #include "base/logging.h" | 
|  | #include "ui/events/event_constants.h" | 
|  | #include "ui/events/event_utils.h" | 
|  | #include "ui/events/keycodes/keyboard_code_conversion_x.h" | 
|  | #include "ui/events/x/touch_factory_x11.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Converts ui::EventType to state for X*Events. | 
|  | unsigned int XEventState(int flags) { | 
|  | return | 
|  | ((flags & ui::EF_SHIFT_DOWN) ? ShiftMask : 0) | | 
|  | ((flags & ui::EF_CONTROL_DOWN) ? ControlMask : 0) | | 
|  | ((flags & ui::EF_ALT_DOWN) ? Mod1Mask : 0) | | 
|  | ((flags & ui::EF_CAPS_LOCK_DOWN) ? LockMask : 0) | | 
|  | ((flags & ui::EF_ALTGR_DOWN) ? Mod5Mask : 0) | | 
|  | ((flags & ui::EF_COMMAND_DOWN) ? Mod4Mask : 0) | | 
|  | ((flags & ui::EF_MOD3_DOWN) ? Mod3Mask : 0) | | 
|  | ((flags & ui::EF_NUMPAD_KEY) ? Mod2Mask : 0) | | 
|  | ((flags & ui::EF_LEFT_MOUSE_BUTTON) ? Button1Mask: 0) | | 
|  | ((flags & ui::EF_MIDDLE_MOUSE_BUTTON) ? Button2Mask: 0) | | 
|  | ((flags & ui::EF_RIGHT_MOUSE_BUTTON) ? Button3Mask: 0); | 
|  | } | 
|  |  | 
|  | // Converts EventType to XKeyEvent type. | 
|  | int XKeyEventType(ui::EventType type) { | 
|  | switch (type) { | 
|  | case ui::ET_KEY_PRESSED: | 
|  | return KeyPress; | 
|  | case ui::ET_KEY_RELEASED: | 
|  | return KeyRelease; | 
|  | default: | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Converts EventType to XI2 event type. | 
|  | int XIKeyEventType(ui::EventType type) { | 
|  | switch (type) { | 
|  | case ui::ET_KEY_PRESSED: | 
|  | return XI_KeyPress; | 
|  | case ui::ET_KEY_RELEASED: | 
|  | return XI_KeyRelease; | 
|  | default: | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | int XIButtonEventType(ui::EventType type) { | 
|  | switch (type) { | 
|  | case ui::ET_MOUSEWHEEL: | 
|  | case ui::ET_MOUSE_PRESSED: | 
|  | // The button release X events for mouse wheels are dropped by Aura. | 
|  | return XI_ButtonPress; | 
|  | case ui::ET_MOUSE_RELEASED: | 
|  | return XI_ButtonRelease; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Converts Aura event type and flag to X button event. | 
|  | unsigned int XButtonEventButton(ui::EventType type, | 
|  | int flags) { | 
|  | // Aura events don't keep track of mouse wheel button, so just return | 
|  | // the first mouse wheel button. | 
|  | if (type == ui::ET_MOUSEWHEEL) | 
|  | return Button4; | 
|  |  | 
|  | if (flags & ui::EF_LEFT_MOUSE_BUTTON) | 
|  | return Button1; | 
|  | if (flags & ui::EF_MIDDLE_MOUSE_BUTTON) | 
|  | return Button2; | 
|  | if (flags & ui::EF_RIGHT_MOUSE_BUTTON) | 
|  | return Button3; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void InitValuatorsForXIDeviceEvent(XIDeviceEvent* xiev) { | 
|  | int valuator_count = ui::DeviceDataManagerX11::DT_LAST_ENTRY; | 
|  | xiev->valuators.mask_len = (valuator_count / 8) + 1; | 
|  | xiev->valuators.mask = new unsigned char[xiev->valuators.mask_len]; | 
|  | memset(xiev->valuators.mask, 0, xiev->valuators.mask_len); | 
|  | xiev->valuators.values = new double[valuator_count]; | 
|  | } | 
|  |  | 
|  | XEvent* CreateXInput2Event(int deviceid, | 
|  | int evtype, | 
|  | int tracking_id, | 
|  | const gfx::Point& location) { | 
|  | XEvent* event = new XEvent; | 
|  | memset(event, 0, sizeof(*event)); | 
|  | event->type = GenericEvent; | 
|  | event->xcookie.data = new XIDeviceEvent; | 
|  | XIDeviceEvent* xiev = | 
|  | static_cast<XIDeviceEvent*>(event->xcookie.data); | 
|  | memset(xiev, 0, sizeof(XIDeviceEvent)); | 
|  | xiev->deviceid = deviceid; | 
|  | xiev->sourceid = deviceid; | 
|  | xiev->evtype = evtype; | 
|  | xiev->detail = tracking_id; | 
|  | xiev->event_x = location.x(); | 
|  | xiev->event_y = location.y(); | 
|  | xiev->event = DefaultRootWindow(gfx::GetXDisplay()); | 
|  | if (evtype == XI_ButtonPress || evtype == XI_ButtonRelease) { | 
|  | xiev->buttons.mask_len = 8; | 
|  | xiev->buttons.mask = new unsigned char[xiev->buttons.mask_len]; | 
|  | memset(xiev->buttons.mask, 0, xiev->buttons.mask_len); | 
|  | } | 
|  | return event; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | namespace ui { | 
|  |  | 
|  | // XInput2 events contain additional data that need to be explicitly freed (see | 
|  | // |CreateXInput2Event()|. | 
|  | void XEventDeleter::operator()(XEvent* event) { | 
|  | if (event->type == GenericEvent) { | 
|  | XIDeviceEvent* xiev = | 
|  | static_cast<XIDeviceEvent*>(event->xcookie.data); | 
|  | if (xiev) { | 
|  | delete[] xiev->valuators.mask; | 
|  | delete[] xiev->valuators.values; | 
|  | delete[] xiev->buttons.mask; | 
|  | delete xiev; | 
|  | } | 
|  | } | 
|  | delete event; | 
|  | } | 
|  |  | 
|  | ScopedXI2Event::ScopedXI2Event() {} | 
|  | ScopedXI2Event::~ScopedXI2Event() {} | 
|  |  | 
|  | void ScopedXI2Event::InitKeyEvent(EventType type, | 
|  | KeyboardCode key_code, | 
|  | int flags) { | 
|  | XDisplay* display = gfx::GetXDisplay(); | 
|  | event_.reset(new XEvent); | 
|  | memset(event_.get(), 0, sizeof(XEvent)); | 
|  | event_->type = XKeyEventType(type); | 
|  | CHECK_NE(0, event_->type); | 
|  | event_->xkey.serial = 0; | 
|  | event_->xkey.send_event = 0; | 
|  | event_->xkey.display = display; | 
|  | event_->xkey.time = 0; | 
|  | event_->xkey.window = 0; | 
|  | event_->xkey.root = 0; | 
|  | event_->xkey.subwindow = 0; | 
|  | event_->xkey.x = 0; | 
|  | event_->xkey.y = 0; | 
|  | event_->xkey.x_root = 0; | 
|  | event_->xkey.y_root = 0; | 
|  | event_->xkey.state = XEventState(flags); | 
|  | event_->xkey.keycode = XKeyCodeForWindowsKeyCode(key_code, flags, display); | 
|  | event_->xkey.same_screen = 1; | 
|  | } | 
|  |  | 
|  | void ScopedXI2Event::InitGenericKeyEvent(int deviceid, | 
|  | int sourceid, | 
|  | EventType type, | 
|  | KeyboardCode key_code, | 
|  | int flags) { | 
|  | event_.reset( | 
|  | CreateXInput2Event(deviceid, XIKeyEventType(type), 0, gfx::Point())); | 
|  | XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(event_->xcookie.data); | 
|  | CHECK_NE(0, xievent->evtype); | 
|  | XDisplay* display = gfx::GetXDisplay(); | 
|  | event_->xgeneric.display = display; | 
|  | xievent->display = display; | 
|  | xievent->mods.effective = XEventState(flags); | 
|  | xievent->detail = XKeyCodeForWindowsKeyCode(key_code, flags, display); | 
|  | xievent->sourceid = sourceid; | 
|  | } | 
|  |  | 
|  | void ScopedXI2Event::InitGenericButtonEvent(int deviceid, | 
|  | EventType type, | 
|  | const gfx::Point& location, | 
|  | int flags) { | 
|  | event_.reset(CreateXInput2Event(deviceid, | 
|  | XIButtonEventType(type), 0, gfx::Point())); | 
|  | XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(event_->xcookie.data); | 
|  | xievent->mods.effective = XEventState(flags); | 
|  | xievent->detail = XButtonEventButton(type, flags); | 
|  | xievent->event_x = location.x(); | 
|  | xievent->event_y = location.y(); | 
|  | XISetMask(xievent->buttons.mask, xievent->detail); | 
|  | // Setup an empty valuator list for generic button events. | 
|  | SetUpValuators(std::vector<Valuator>()); | 
|  | } | 
|  |  | 
|  | void ScopedXI2Event::InitGenericMouseWheelEvent(int deviceid, | 
|  | int wheel_delta, | 
|  | int flags) { | 
|  | InitGenericButtonEvent(deviceid, ui::ET_MOUSEWHEEL, gfx::Point(), flags); | 
|  | XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(event_->xcookie.data); | 
|  | xievent->detail = wheel_delta > 0 ? Button4 : Button5; | 
|  | } | 
|  |  | 
|  | void ScopedXI2Event::InitScrollEvent(int deviceid, | 
|  | int x_offset, | 
|  | int y_offset, | 
|  | int x_offset_ordinal, | 
|  | int y_offset_ordinal, | 
|  | int finger_count) { | 
|  | event_.reset(CreateXInput2Event(deviceid, XI_Motion, 0, gfx::Point())); | 
|  |  | 
|  | Valuator valuators[] = { | 
|  | Valuator(DeviceDataManagerX11::DT_CMT_SCROLL_X, x_offset), | 
|  | Valuator(DeviceDataManagerX11::DT_CMT_SCROLL_Y, y_offset), | 
|  | Valuator(DeviceDataManagerX11::DT_CMT_ORDINAL_X, x_offset_ordinal), | 
|  | Valuator(DeviceDataManagerX11::DT_CMT_ORDINAL_Y, y_offset_ordinal), | 
|  | Valuator(DeviceDataManagerX11::DT_CMT_FINGER_COUNT, finger_count) | 
|  | }; | 
|  | SetUpValuators( | 
|  | std::vector<Valuator>(valuators, valuators + arraysize(valuators))); | 
|  | } | 
|  |  | 
|  | void ScopedXI2Event::InitFlingScrollEvent(int deviceid, | 
|  | int x_velocity, | 
|  | int y_velocity, | 
|  | int x_velocity_ordinal, | 
|  | int y_velocity_ordinal, | 
|  | bool is_cancel) { | 
|  | event_.reset(CreateXInput2Event(deviceid, XI_Motion, deviceid, gfx::Point())); | 
|  |  | 
|  | Valuator valuators[] = { | 
|  | Valuator(DeviceDataManagerX11::DT_CMT_FLING_STATE, is_cancel ? 1 : 0), | 
|  | Valuator(DeviceDataManagerX11::DT_CMT_FLING_Y, y_velocity), | 
|  | Valuator(DeviceDataManagerX11::DT_CMT_ORDINAL_Y, y_velocity_ordinal), | 
|  | Valuator(DeviceDataManagerX11::DT_CMT_FLING_X, x_velocity), | 
|  | Valuator(DeviceDataManagerX11::DT_CMT_ORDINAL_X, x_velocity_ordinal) | 
|  | }; | 
|  |  | 
|  | SetUpValuators( | 
|  | std::vector<Valuator>(valuators, valuators + arraysize(valuators))); | 
|  | } | 
|  |  | 
|  | void ScopedXI2Event::InitTouchEvent(int deviceid, | 
|  | int evtype, | 
|  | int tracking_id, | 
|  | const gfx::Point& location, | 
|  | const std::vector<Valuator>& valuators) { | 
|  | event_.reset(CreateXInput2Event(deviceid, evtype, tracking_id, location)); | 
|  |  | 
|  | // If a timestamp was specified, setup the event. | 
|  | for (size_t i = 0; i < valuators.size(); ++i) { | 
|  | if (valuators[i].data_type == | 
|  | DeviceDataManagerX11::DT_TOUCH_RAW_TIMESTAMP) { | 
|  | SetUpValuators(valuators); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | // No timestamp was specified. Use |ui::EventTimeForNow()|. | 
|  | std::vector<Valuator> valuators_with_time = valuators; | 
|  | valuators_with_time.push_back( | 
|  | Valuator(DeviceDataManagerX11::DT_TOUCH_RAW_TIMESTAMP, | 
|  | (ui::EventTimeForNow()).InMicroseconds())); | 
|  | SetUpValuators(valuators_with_time); | 
|  | } | 
|  |  | 
|  | void ScopedXI2Event::SetUpValuators(const std::vector<Valuator>& valuators) { | 
|  | CHECK(event_.get()); | 
|  | CHECK_EQ(GenericEvent, event_->type); | 
|  | XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(event_->xcookie.data); | 
|  | InitValuatorsForXIDeviceEvent(xiev); | 
|  | ui::DeviceDataManagerX11* manager = ui::DeviceDataManagerX11::GetInstance(); | 
|  | for (size_t i = 0; i < valuators.size(); ++i) { | 
|  | manager->SetValuatorDataForTest(xiev, valuators[i].data_type, | 
|  | valuators[i].value); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SetUpTouchPadForTest(unsigned int deviceid) { | 
|  | std::vector<unsigned int> device_list; | 
|  | device_list.push_back(deviceid); | 
|  |  | 
|  | TouchFactory::GetInstance()->SetPointerDeviceForTest(device_list); | 
|  | ui::DeviceDataManagerX11* manager = ui::DeviceDataManagerX11::GetInstance(); | 
|  | manager->SetDeviceListForTest(std::vector<unsigned int>(), device_list); | 
|  | } | 
|  |  | 
|  | void SetUpTouchDevicesForTest(const std::vector<unsigned int>& devices) { | 
|  | TouchFactory::GetInstance()->SetTouchDeviceForTest(devices); | 
|  | ui::DeviceDataManagerX11* manager = ui::DeviceDataManagerX11::GetInstance(); | 
|  | manager->SetDeviceListForTest(devices, std::vector<unsigned int>()); | 
|  | } | 
|  |  | 
|  | }  // namespace ui |