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..cfc22b963f6a6c0b33774177c85264d86f02e6a9 |
--- /dev/null |
+++ b/ui/ozone/platform/x11/x11_event_factory.cc |
@@ -0,0 +1,318 @@ |
+// 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/X.h> |
+#include <X11/XKBlib.h> |
+#include <X11/Xlib.h> |
+#include <X11/extensions/XInput2.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/gfx/x/x11_types.h" |
+#include "ui/ozone/platform/x11/x11_surface_factory.h" |
+#include "ui/ozone/platform/x11/x11_window_host.h" |
+#include "ui/ozone/public/ozone_platform.h" |
+#include "ui/ozone/public/surface_factory_ozone.h" |
+ |
+namespace ui { |
+ |
+namespace { |
+ |
+using TimeDelta = base::TimeDelta; |
+ |
+int g_xinput_opcode = -1; |
+ |
+bool InitializeXInput2(XDisplay* display) { |
spang
2016/01/20 19:53:31
These look really easy to share with the X11 build
kylechar
2016/01/20 21:46:16
Good idea! I'll move that into a different CL.
|
+ if (!display) |
+ return false; |
+ |
+ int event, err; |
+ |
+ int xiopcode; |
+ if (!XQueryExtension(display, "XInputExtension", &xiopcode, &event, &err)) { |
+ DVLOG(1) << "X Input extension not available."; |
+ return false; |
+ } |
+ g_xinput_opcode = xiopcode; |
+ |
+ int major = 2, minor = 2; |
+ if (XIQueryVersion(display, &major, &minor) == BadRequest) { |
+ DVLOG(1) << "XInput2 not supported in the server."; |
+ return false; |
+ } |
+ if (major < 2 || (major == 2 && minor < 2)) { |
+ DVLOG(1) << "XI version on server is " << major << "." << minor << ". " |
+ << "But 2.2 is required."; |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+bool InitializeXkb(XDisplay* display) { |
+ if (!display) |
+ return false; |
+ |
+ int opcode, event, error; |
+ int major = XkbMajorVersion; |
+ int minor = XkbMinorVersion; |
+ if (!XkbQueryExtension(display, &opcode, &event, &error, &major, &minor)) { |
+ DVLOG(1) << "Xkb extension not available."; |
+ return false; |
+ } |
+ |
+ // Ask the server not to send KeyRelease event when the user holds down a key. |
+ // crbug.com/138092 |
+ Bool supported_return; |
+ if (!XkbSetDetectableAutoRepeat(display, True, &supported_return)) { |
+ DVLOG(1) << "XKB not supported in the server."; |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+// 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<X11WindowHostManager> window_manager) |
+ : display_(display), window_manager_(window_manager) { |
+ CHECK(display_); |
spang
2016/01/20 19:53:31
DCHECK for non-security assertions per chrome styl
kylechar
2016/01/20 21:46:16
Done.
|
+ DeviceDataManagerX11::CreateInstance(); |
+ InitializeXInput2(display_); |
+ InitializeXkb(display_); |
+ AddEventWatcher(); |
+} |
+ |
+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); |
+ } |
+ 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. |
+ X11WindowHost* window = window_manager_->FindWindow(xev.xany.window); |
+ if (window) |
+ window->ProcessXEvent(xev); |
+ } |
+ } |
+} |
+ |
+} // namespace ui |