summaryrefslogtreecommitdiffstats
path: root/base/message_pump_aurax11.cc
diff options
context:
space:
mode:
Diffstat (limited to 'base/message_pump_aurax11.cc')
-rw-r--r--base/message_pump_aurax11.cc307
1 files changed, 307 insertions, 0 deletions
diff --git a/base/message_pump_aurax11.cc b/base/message_pump_aurax11.cc
new file mode 100644
index 0000000..9ba7989
--- /dev/null
+++ b/base/message_pump_aurax11.cc
@@ -0,0 +1,307 @@
+// 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