This source file includes following definitions.
- XSourcePrepare
- XSourceCheck
- XSourceDispatch
- InitializeXInput2
- FindEventTarget
- InitializeXkb
- x_source_
- GetDefaultXDisplay
- Current
- Current
- AddDispatcherForWindow
- RemoveDispatcherForWindow
- AddDispatcherForRootWindow
- RemoveDispatcherForRootWindow
- AddObserver
- RemoveObserver
- DispatchXEvents
- BlockUntilWindowMapped
- InitXSource
- ProcessXEvent
- WillProcessXEvent
- DidProcessXEvent
- GetDispatcherForXEvent
- Dispatch
#include "base/message_loop/message_pump_x11.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/message_loop.h"
namespace base {
namespace {
gboolean XSourcePrepare(GSource* source, gint* timeout_ms) {
if (XPending(MessagePumpX11::GetDefaultXDisplay()))
*timeout_ms = 0;
else
*timeout_ms = -1;
return FALSE;
}
gboolean XSourceCheck(GSource* source) {
return XPending(MessagePumpX11::GetDefaultXDisplay());
}
gboolean XSourceDispatch(GSource* source,
GSourceFunc unused_func,
gpointer data) {
MessagePumpX11* pump = static_cast<MessagePumpX11*>(data);
pump->DispatchXEvents();
return TRUE;
}
GSourceFuncs XSourceFuncs = {
XSourcePrepare,
XSourceCheck,
XSourceDispatch,
NULL
};
Display* g_xdisplay = NULL;
int g_xinput_opcode = -1;
bool InitializeXInput2() {
Display* display = MessagePumpX11::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)
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 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 InitializeXkb() {
Display* display = MessagePumpX11::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;
}
Bool supported_return;
if (!XkbSetDetectableAutoRepeat(display, True, &supported_return)) {
DVLOG(1) << "XKB not supported in the server.";
return false;
}
return true;
}
}
MessagePumpX11::MessagePumpX11() : MessagePumpGlib(),
x_source_(NULL) {
InitializeXInput2();
InitializeXkb();
InitXSource();
x_root_window_ = DefaultRootWindow(g_xdisplay);
}
MessagePumpX11::~MessagePumpX11() {
g_source_destroy(x_source_);
g_source_unref(x_source_);
XCloseDisplay(g_xdisplay);
g_xdisplay = NULL;
}
Display* MessagePumpX11::GetDefaultXDisplay() {
if (!g_xdisplay)
g_xdisplay = XOpenDisplay(NULL);
return g_xdisplay;
}
#if defined(TOOLKIT_GTK)
MessagePumpX11* MessagePumpX11::Current() {
MessageLoop* loop = MessageLoop::current();
return static_cast<MessagePumpX11*>(loop->pump_gpu());
}
#else
MessagePumpX11* MessagePumpX11::Current() {
MessageLoopForUI* loop = MessageLoopForUI::current();
return static_cast<MessagePumpX11*>(loop->pump_ui());
}
#endif
void MessagePumpX11::AddDispatcherForWindow(
MessagePumpDispatcher* dispatcher,
unsigned long xid) {
dispatchers_.insert(std::make_pair(xid, dispatcher));
}
void MessagePumpX11::RemoveDispatcherForWindow(unsigned long xid) {
dispatchers_.erase(xid);
}
void MessagePumpX11::AddDispatcherForRootWindow(
MessagePumpDispatcher* dispatcher) {
root_window_dispatchers_.AddObserver(dispatcher);
}
void MessagePumpX11::RemoveDispatcherForRootWindow(
MessagePumpDispatcher* dispatcher) {
root_window_dispatchers_.RemoveObserver(dispatcher);
}
void MessagePumpX11::AddObserver(MessagePumpObserver* observer) {
observers_.AddObserver(observer);
}
void MessagePumpX11::RemoveObserver(MessagePumpObserver* observer) {
observers_.RemoveObserver(observer);
}
void MessagePumpX11::DispatchXEvents() {
Display* display = GetDefaultXDisplay();
DCHECK(display);
MessagePumpDispatcher* dispatcher = GetDispatcher();
if (!dispatcher)
dispatcher = this;
while (XPending(display)) {
XEvent xev;
XNextEvent(display, &xev);
ProcessXEvent(dispatcher, &xev);
if (ShouldQuit())
break;
}
}
void MessagePumpX11::BlockUntilWindowMapped(unsigned long xid) {
XEvent event;
Display* display = GetDefaultXDisplay();
DCHECK(display);
MessagePumpDispatcher* dispatcher = GetDispatcher();
if (!dispatcher)
dispatcher = this;
do {
XWindowEvent(display, xid, StructureNotifyMask, &event);
ProcessXEvent(dispatcher, &event);
} while (event.type != MapNotify);
}
void MessagePumpX11::InitXSource() {
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());
}
void MessagePumpX11::ProcessXEvent(MessagePumpDispatcher* dispatcher,
XEvent* xev) {
CHECK(dispatcher);
bool have_cookie = false;
if (xev->type == GenericEvent &&
XGetEventData(xev->xgeneric.display, &xev->xcookie)) {
have_cookie = true;
}
if (!WillProcessXEvent(xev)) {
uint32_t action = dispatcher->Dispatch(xev);
bool should_quit = (action & POST_DISPATCH_QUIT_LOOP);
if (dispatcher != this && (action & POST_DISPATCH_PERFORM_DEFAULT))
action = Dispatch(xev);
if ((action & POST_DISPATCH_QUIT_LOOP) || should_quit)
Quit();
DidProcessXEvent(xev);
}
if (have_cookie)
XFreeEventData(xev->xgeneric.display, &xev->xcookie);
}
bool MessagePumpX11::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 MessagePumpX11::DidProcessXEvent(XEvent* xevent) {
FOR_EACH_OBSERVER(MessagePumpObserver, observers(), DidProcessEvent(xevent));
}
MessagePumpDispatcher* MessagePumpX11::GetDispatcherForXEvent(
const NativeEvent& xev) const {
::Window x_window = FindEventTarget(xev);
DispatchersMap::const_iterator it = dispatchers_.find(x_window);
return it != dispatchers_.end() ? it->second : NULL;
}
uint32_t MessagePumpX11::Dispatch(const NativeEvent& xev) {
if (xev->type == MappingNotify) {
for (DispatchersMap::const_iterator it = dispatchers_.begin();
it != dispatchers_.end(); ++it) {
it->second->Dispatch(xev);
}
return POST_DISPATCH_NONE;
}
if (FindEventTarget(xev) == x_root_window_) {
FOR_EACH_OBSERVER(MessagePumpDispatcher, root_window_dispatchers_,
Dispatch(xev));
return POST_DISPATCH_NONE;
}
MessagePumpDispatcher* dispatcher = GetDispatcherForXEvent(xev);
return dispatcher ? dispatcher->Dispatch(xev) : POST_DISPATCH_NONE;
}
}