Chromium Code Reviews| Index: ui/ozone/platform/x11/x11_event_factory.cc |
| diff --git a/ui/ozone/platform/x11/x11_event_factory.cc b/ui/ozone/platform/x11/x11_event_factory.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..acf38ffed454d80f0eccc99740c34a284752beb4 |
| --- /dev/null |
| +++ b/ui/ozone/platform/x11/x11_event_factory.cc |
| @@ -0,0 +1,264 @@ |
| +// Copyright 2016 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/ozone/platform/x11/x11_event_factory.h" |
| + |
| +#include <X11/Xlib.h> |
| + |
| +#include "base/logging.h" |
| +#include "base/macros.h" |
| +#include "ui/events/devices/x11/device_data_manager_x11.h" |
| +#include "ui/events/event.h" |
| +#include "ui/events/event_utils.h" |
| +#include "ui/events/keycodes/keyboard_code_conversion_x.h" |
| +#include "ui/events/platform/platform_event_dispatcher.h" |
| +#include "ui/events/x/events_x_utils.h" |
| +#include "ui/events/x/input_x.h" |
| +#include "ui/gfx/x/x11_types.h" |
| +#include "ui/ozone/platform/x11/x11_surface_factory.h" |
| +#include "ui/ozone/platform/x11/x11_window_ozone.h" |
| +#include "ui/ozone/public/ozone_platform.h" |
| +#include "ui/ozone/public/surface_factory_ozone.h" |
| + |
| +namespace ui { |
| + |
| +namespace { |
| + |
| +using TimeDelta = base::TimeDelta; |
| + |
| +// Translates XI2 XEvent into a ui::Event. |
| +ui::Event* TranslateXI2EventToEvent(const XEvent& xev) { |
| + XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev.xcookie.data); |
| + EventType event_type = EventTypeFromXEvent(xev); |
| + gfx::Point location = gfx::Point(xievent->event_x, xievent->event_y); |
| + gfx::Point root_location = gfx::Point(xievent->root_x, xievent->root_y); |
| + int flags = EventFlagsFromXEvent(xev); |
| + switch (event_type) { |
| + case ET_KEY_PRESSED: |
| + case ET_KEY_RELEASED: |
| + return new KeyEvent(event_type, KeyboardCodeFromXKeyEvent(&xev), flags); |
| + case ET_MOUSE_PRESSED: |
| + case ET_MOUSE_MOVED: |
| + case ET_MOUSE_DRAGGED: |
| + case ET_MOUSE_RELEASED: |
| + return new MouseEvent(event_type, location, root_location, TimeDelta(), |
| + flags, GetChangedMouseButtonFlagsFromXEvent(xev)); |
| + case ET_MOUSEWHEEL: |
| + return new MouseWheelEvent(GetMouseWheelOffsetFromXEvent(xev), location, |
| + root_location, TimeDelta(), flags, |
| + GetChangedMouseButtonFlagsFromXEvent(xev)); |
| + case ET_SCROLL_FLING_START: |
| + case ET_SCROLL_FLING_CANCEL: { |
| + float x_offset, y_offset, x_offset_ordinal, y_offset_ordinal; |
| + GetFlingDataFromXEvent(xev, &x_offset, &y_offset, &x_offset_ordinal, |
| + &y_offset_ordinal, nullptr); |
| + return new ScrollEvent(event_type, location, EventTimeFromXEvent(xev), |
| + flags, x_offset, y_offset, x_offset_ordinal, |
| + y_offset_ordinal, 0); |
| + } |
| + case ET_SCROLL: { |
| + float x_offset, y_offset, x_offset_ordinal, y_offset_ordinal; |
| + int finger_count; |
| + GetScrollOffsetsFromXEvent(xev, &x_offset, &y_offset, &x_offset_ordinal, |
| + &y_offset_ordinal, &finger_count); |
| + return new ScrollEvent(event_type, location, EventTimeFromXEvent(xev), |
| + flags, x_offset, y_offset, x_offset_ordinal, |
| + y_offset_ordinal, finger_count); |
| + } |
| + case ET_TOUCH_MOVED: |
| + case ET_TOUCH_PRESSED: |
| + case ET_TOUCH_CANCELLED: |
| + case ET_TOUCH_RELEASED: |
| + return new TouchEvent(event_type, location, GetTouchIdFromXEvent(xev), |
| + EventTimeFromXEvent(xev)); |
| + case ET_UNKNOWN: |
| + return nullptr; |
| + default: |
| + break; |
| + } |
| + return nullptr; |
| +} |
| + |
| +// Translates a XEvent into a ui::Event. |
| +ui::Event* TranslateXEventToEvent(const XEvent& xev) { |
| + int flags = EventFlagsFromXEvent(xev); |
| + switch (xev.type) { |
| + case LeaveNotify: |
| + case EnterNotify: |
| + // EnterNotify creates ET_MOUSE_MOVED. Mark as synthesized as this is |
| + // not real mouse move event. |
| + // int flags = GetEventFlagsFromXState(xev.xcrossing.state); |
| + if (xev.type == EnterNotify) |
| + flags |= EF_IS_SYNTHESIZED; |
| + return new MouseEvent( |
| + ET_MOUSE_MOVED, gfx::Point(xev.xcrossing.x, xev.xcrossing.y), |
| + gfx::Point(xev.xcrossing.x_root, xev.xcrossing.y_root), TimeDelta(), |
| + flags, 0); |
| + |
| + case KeyPress: |
| + case KeyRelease: |
| + return new KeyEvent(EventTypeFromXEvent(xev), |
| + KeyboardCodeFromXKeyEvent(&xev), flags); |
| + |
| + case ButtonPress: |
| + case ButtonRelease: { |
| + gfx::Point location = gfx::Point(xev.xbutton.x, xev.xbutton.y); |
| + gfx::Point root_location = |
| + gfx::Point(xev.xbutton.x_root, xev.xbutton.y_root); |
| + switch (EventTypeFromXEvent(xev)) { |
| + case ET_MOUSEWHEEL: |
| + return new MouseWheelEvent(GetMouseWheelOffsetFromXEvent(xev), |
| + location, root_location, TimeDelta(), |
| + flags, 0); |
| + case ET_MOUSE_PRESSED: |
| + case ET_MOUSE_RELEASED: |
| + return new MouseEvent(EventTypeFromXEvent(xev), location, |
| + root_location, TimeDelta(), flags, |
| + GetChangedMouseButtonFlagsFromXEvent(xev)); |
| + case ET_UNKNOWN: |
| + // No event is created for X11-release events for mouse-wheel |
| + // buttons. |
| + break; |
| + default: |
| + NOTREACHED(); |
| + } |
| + break; |
| + } |
| + |
| + case GenericEvent: |
| + return TranslateXI2EventToEvent(xev); |
| + } |
| + return nullptr; |
| +} |
| + |
| +} // namespace |
| + |
| +X11EventFactory::X11EventFactory( |
| + XDisplay* display, |
| + scoped_refptr<X11WindowManagerOzone> window_manager) |
| + : display_(display), window_manager_(window_manager) { |
| + DCHECK(display_); |
| + DeviceDataManagerX11::CreateInstance(); |
| + InitializeXInput2(display_); |
| + InitializeXkb(display_); |
| + AddEventWatcher(); |
|
spang
2016/01/20 22:32:53
Does it need to be called here since it's called i
kylechar
2016/01/21 14:14:52
I'm not sure. It seems like it shouldn't need to b
sadrul
2016/01/21 14:53:01
I think in some tests, the message-loop is created
|
| +} |
| + |
| +X11EventFactory::~X11EventFactory() {} |
| + |
| +// static |
| +X11EventFactory* X11EventFactory::GetInstance() { |
| + return static_cast<X11EventFactory*>(PlatformEventSource::GetInstance()); |
| +} |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// X11EventFactory, public |
| + |
| +void X11EventFactory::DispatchXEvents() { |
| + DCHECK(display_); |
| + // Handle all pending events. |
| + // It may be useful to eventually align this event dispatch with vsync, but |
| + // not yet. |
| + continue_stream_ = true; |
| + while (XPending(display_) && continue_stream_) { |
| + XEvent xevent; |
| + XNextEvent(display_, &xevent); |
| + ExtractCookieDataDispatchEvent(&xevent); |
| + } |
| +} |
| + |
| +void X11EventFactory::BlockUntilWindowMapped(XID window) { |
| + XEvent event; |
| + do { |
| + // Block until there's a message of |event_mask| type on |w|. Then remove |
| + // it from the queue and stuff it in |event|. |
| + XWindowEvent(display_, window, StructureNotifyMask, &event); |
| + ExtractCookieDataDispatchEvent(&event); |
| + } while (event.type != MapNotify); |
| +} |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// X11EventFactory, private |
| + |
| +void X11EventFactory::AddEventWatcher() { |
| + if (initialized_) |
| + return; |
| + if (!base::MessageLoop::current()) |
| + return; |
| + |
| + int fd = ConnectionNumber(display_); |
| + base::MessageLoopForUI::current()->WatchFileDescriptor( |
| + fd, true, base::MessagePumpLibevent::WATCH_READ, &watcher_controller_, |
| + this); |
| + initialized_ = true; |
| +} |
| + |
| +uint32_t X11EventFactory::ExtractCookieDataDispatchEvent(XEvent* xevent) { |
| + bool have_cookie = false; |
| + if (xevent->type == GenericEvent && |
| + XGetEventData(xevent->xgeneric.display, &xevent->xcookie)) { |
| + have_cookie = true; |
| + } |
| + uint32_t action = DispatchEvent(xevent); |
| + if (have_cookie) |
| + XFreeEventData(xevent->xgeneric.display, &xevent->xcookie); |
| + return action; |
| +} |
| + |
| +uint32_t X11EventFactory::DispatchEvent(base::NativeEvent event) { |
| + XEvent* xevent = static_cast<XEvent*>(event); |
| + uint32_t action = POST_DISPATCH_STOP_PROPAGATION; |
| + base::NativeEvent translated_event = TranslateXEventToEvent(*xevent); |
| + if (translated_event) { |
| + action = PlatformEventSource::DispatchEvent(translated_event); |
| + } else { |
| + DispatchXEventToXWindow(*xevent); |
|
spang
2016/01/20 22:32:53
Someday I hope we will be able to take this branch
|
| + } |
| + if (xevent->type == GenericEvent && |
| + xevent->xgeneric.evtype == XI_HierarchyChanged) { |
| + // TODO(kylechar): UpdateDeviceList() isn't calling X11 functions. |
| + ui::UpdateDeviceList(); |
| + } |
| + |
| + if (xevent->type == EnterNotify && |
| + xevent->xcrossing.detail != NotifyInferior && |
| + xevent->xcrossing.mode != NotifyUngrab) { |
| + // Clear stored scroll data |
| + DeviceDataManagerX11::GetInstance()->InvalidateScrollClasses(); |
| + } |
| + return action; |
| +} |
| + |
| +void X11EventFactory::StopCurrentEventStream() { |
| + continue_stream_ = false; |
| +} |
| + |
| +void X11EventFactory::OnDispatcherListChanged() { |
| + AddEventWatcher(); |
| +} |
| + |
| +void X11EventFactory::OnFileCanReadWithoutBlocking(int fd) { |
| + DispatchXEvents(); |
| +} |
| + |
| +void X11EventFactory::OnFileCanWriteWithoutBlocking(int fd) { |
| + NOTREACHED(); |
| +} |
| + |
| +void X11EventFactory::DispatchXEventToXWindow(const XEvent& xev) { |
| + switch (xev.type) { |
| + case FocusOut: |
| + case Expose: |
| + case ConfigureNotify: |
| + case ClientMessage: { |
| + // This is a windowing message that needs to be passed to the platform |
| + // window directly. |
| + X11WindowOzone* window = window_manager_->FindWindow(xev.xany.window); |
| + if (window) |
| + window->ProcessXEvent(xev); |
| + } |
| + } |
| +} |
| + |
| +} // namespace ui |