| // Copyright (c) 2012 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 "base/message_pump_aurax11.h" |
| |
| #include <glib.h> |
| #include <X11/X.h> |
| #include <X11/extensions/XInput2.h> |
| #include <X11/XKBlib.h> |
| |
| #include "base/basictypes.h" |
| #include "base/message_loop.h" |
| |
| namespace { |
| |
| gboolean XSourcePrepare(GSource* source, gint* timeout_ms) { |
| if (XPending(base::MessagePumpAuraX11::GetDefaultXDisplay())) |
| *timeout_ms = 0; |
| else |
| *timeout_ms = -1; |
| return FALSE; |
| } |
| |
| gboolean XSourceCheck(GSource* source) { |
| return XPending(base::MessagePumpAuraX11::GetDefaultXDisplay()); |
| } |
| |
| gboolean XSourceDispatch(GSource* source, |
| GSourceFunc unused_func, |
| gpointer data) { |
| base::MessagePumpAuraX11* pump = static_cast<base::MessagePumpAuraX11*>(data); |
| return pump->DispatchXEvents(); |
| } |
| |
| GSourceFuncs XSourceFuncs = { |
| XSourcePrepare, |
| XSourceCheck, |
| XSourceDispatch, |
| NULL |
| }; |
| |
| // The connection is essentially a global that's accessed through a static |
| // method and destroyed whenever ~MessagePumpAuraX11() is called. We do this |
| // for historical reasons so user code can call |
| // MessagePumpForUI::GetDefaultXDisplay() where MessagePumpForUI is a typedef |
| // to whatever type in the current build. |
| // |
| // TODO(erg): This can be changed to something more sane like |
| // MessagePumpAuraX11::Current()->display() once MessagePumpGtk goes away. |
| Display* g_xdisplay = NULL; |
| int g_xinput_opcode = -1; |
| |
| bool InitializeXInput2Internal() { |
| Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay(); |
| 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; |
| |
| #if defined(USE_XI2_MT) |
| // USE_XI2_MT also defines the required XI2 minor minimum version. |
| int major = 2, minor = USE_XI2_MT; |
| #else |
| int major = 2, minor = 0; |
| #endif |
| if (XIQueryVersion(display, &major, &minor) == BadRequest) { |
| DVLOG(1) << "XInput2 not supported in the server."; |
| return false; |
| } |
| #if defined(USE_XI2_MT) |
| if (major < 2 || (major == 2 && minor < USE_XI2_MT)) { |
| DVLOG(1) << "XI version on server is " << major << "." << minor << ". " |
| << "But 2." << USE_XI2_MT << " is required."; |
| return false; |
| } |
| #endif |
| |
| return true; |
| } |
| |
| Window FindEventTarget(const base::NativeEvent& xev) { |
| Window target = xev->xany.window; |
| if (xev->type == GenericEvent && |
| static_cast<XIEvent*>(xev->xcookie.data)->extension == g_xinput_opcode) { |
| target = static_cast<XIDeviceEvent*>(xev->xcookie.data)->event; |
| } |
| return target; |
| } |
| |
| bool InitializeXInput2() { |
| static bool xinput2_supported = InitializeXInput2Internal(); |
| return xinput2_supported; |
| } |
| |
| bool InitializeXkb() { |
| Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay(); |
| 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; |
| } |
| |
| } // namespace |
| |
| namespace base { |
| |
| MessagePumpAuraX11::MessagePumpAuraX11() : MessagePumpGlib(), |
| x_source_(NULL) { |
| InitializeXInput2(); |
| InitializeXkb(); |
| InitXSource(); |
| |
| // Can't put this in the initializer list because g_xdisplay may not exist |
| // until after InitXSource(). |
| x_root_window_ = DefaultRootWindow(g_xdisplay); |
| } |
| |
| // static |
| Display* MessagePumpAuraX11::GetDefaultXDisplay() { |
| if (!g_xdisplay) |
| g_xdisplay = XOpenDisplay(NULL); |
| return g_xdisplay; |
| } |
| |
| // static |
| bool MessagePumpAuraX11::HasXInput2() { |
| return InitializeXInput2(); |
| } |
| |
| // static |
| MessagePumpAuraX11* MessagePumpAuraX11::Current() { |
| MessageLoopForUI* loop = MessageLoopForUI::current(); |
| return static_cast<MessagePumpAuraX11*>(loop->pump_ui()); |
| } |
| |
| void MessagePumpAuraX11::AddDispatcherForWindow( |
| MessagePumpDispatcher* dispatcher, |
| unsigned long xid) { |
| dispatchers_.insert(std::make_pair(xid, dispatcher)); |
| } |
| |
| void MessagePumpAuraX11::RemoveDispatcherForWindow(unsigned long xid) { |
| dispatchers_.erase(xid); |
| } |
| |
| void MessagePumpAuraX11::AddDispatcherForRootWindow( |
| MessagePumpDispatcher* dispatcher) { |
| root_window_dispatchers_.AddObserver(dispatcher); |
| } |
| |
| void MessagePumpAuraX11::RemoveDispatcherForRootWindow( |
| MessagePumpDispatcher* dispatcher) { |
| root_window_dispatchers_.RemoveObserver(dispatcher); |
| } |
| |
| bool MessagePumpAuraX11::DispatchXEvents() { |
| Display* display = GetDefaultXDisplay(); |
| DCHECK(display); |
| MessagePumpDispatcher* dispatcher = |
| GetDispatcher() ? GetDispatcher() : this; |
| |
| // In the general case, we want to handle all pending events before running |
| // the tasks. This is what happens in the message_pump_glib case. |
| while (XPending(display)) { |
| XEvent xev; |
| XNextEvent(display, &xev); |
| if (dispatcher && ProcessXEvent(dispatcher, &xev)) |
| return TRUE; |
| } |
| return TRUE; |
| } |
| |
| void MessagePumpAuraX11::BlockUntilWindowMapped(unsigned long xid) { |
| XEvent event; |
| |
| Display* display = GetDefaultXDisplay(); |
| DCHECK(display); |
| |
| MessagePumpDispatcher* dispatcher = |
| GetDispatcher() ? GetDispatcher() : this; |
| |
| 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, xid, StructureNotifyMask, &event); |
| ProcessXEvent(dispatcher, &event); |
| } while (event.type != MapNotify); |
| } |
| |
| MessagePumpAuraX11::~MessagePumpAuraX11() { |
| g_source_destroy(x_source_); |
| g_source_unref(x_source_); |
| XCloseDisplay(g_xdisplay); |
| g_xdisplay = NULL; |
| } |
| |
| void MessagePumpAuraX11::InitXSource() { |
| // CHECKs are to help track down crbug.com/113106. |
| CHECK(!x_source_); |
| Display* display = GetDefaultXDisplay(); |
| CHECK(display) << "Unable to get connection to X server"; |
| x_poll_.reset(new GPollFD()); |
| CHECK(x_poll_.get()); |
| x_poll_->fd = ConnectionNumber(display); |
| x_poll_->events = G_IO_IN; |
| |
| x_source_ = g_source_new(&XSourceFuncs, sizeof(GSource)); |
| g_source_add_poll(x_source_, x_poll_.get()); |
| g_source_set_can_recurse(x_source_, TRUE); |
| g_source_set_callback(x_source_, NULL, this, NULL); |
| g_source_attach(x_source_, g_main_context_default()); |
| } |
| |
| bool MessagePumpAuraX11::ProcessXEvent(MessagePumpDispatcher* dispatcher, |
| XEvent* xev) { |
| bool should_quit = false; |
| |
| bool have_cookie = false; |
| if (xev->type == GenericEvent && |
| XGetEventData(xev->xgeneric.display, &xev->xcookie)) { |
| have_cookie = true; |
| } |
| |
| if (!WillProcessXEvent(xev)) { |
| if (!dispatcher->Dispatch(xev)) { |
| should_quit = true; |
| Quit(); |
| } |
| DidProcessXEvent(xev); |
| } |
| |
| if (have_cookie) { |
| XFreeEventData(xev->xgeneric.display, &xev->xcookie); |
| } |
| |
| return should_quit; |
| } |
| |
| bool MessagePumpAuraX11::WillProcessXEvent(XEvent* xevent) { |
| if (!observers().might_have_observers()) |
| return false; |
| ObserverListBase<MessagePumpObserver>::Iterator it(observers()); |
| MessagePumpObserver* obs; |
| while ((obs = it.GetNext()) != NULL) { |
| if (obs->WillProcessEvent(xevent)) |
| return true; |
| } |
| return false; |
| } |
| |
| void MessagePumpAuraX11::DidProcessXEvent(XEvent* xevent) { |
| FOR_EACH_OBSERVER(MessagePumpObserver, observers(), DidProcessEvent(xevent)); |
| } |
| |
| MessagePumpDispatcher* MessagePumpAuraX11::GetDispatcherForXEvent( |
| const base::NativeEvent& xev) const { |
| ::Window x_window = FindEventTarget(xev); |
| DispatchersMap::const_iterator it = dispatchers_.find(x_window); |
| return it != dispatchers_.end() ? it->second : NULL; |
| } |
| |
| bool MessagePumpAuraX11::Dispatch(const base::NativeEvent& xev) { |
| // MappingNotify events (meaning that the keyboard or pointer buttons have |
| // been remapped) aren't associated with a window; send them to all |
| // dispatchers. |
| if (xev->type == MappingNotify) { |
| for (DispatchersMap::const_iterator it = dispatchers_.begin(); |
| it != dispatchers_.end(); ++it) { |
| it->second->Dispatch(xev); |
| } |
| return true; |
| } |
| |
| if (FindEventTarget(xev) == x_root_window_) { |
| FOR_EACH_OBSERVER(MessagePumpDispatcher, root_window_dispatchers_, |
| Dispatch(xev)); |
| return true; |
| } |
| MessagePumpDispatcher* dispatcher = GetDispatcherForXEvent(xev); |
| return dispatcher ? dispatcher->Dispatch(xev) : true; |
| } |
| |
| } // namespace base |