blob: 78c179933d00a7e5a70d0d94c3675fe7b9dd1379 [file] [log] [blame]
[email protected]71ad9c6f2010-10-22 16:17:471// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/message_pump_glib_x.h"
6
7#include <gdk/gdkx.h>
[email protected]130eeb32010-11-12 17:42:308#if defined(HAVE_XINPUT2)
9#include <X11/extensions/XInput2.h>
10#else
[email protected]71ad9c6f2010-10-22 16:17:4711#include <X11/Xlib.h>
[email protected]130eeb32010-11-12 17:42:3012#endif
[email protected]71ad9c6f2010-10-22 16:17:4713
[email protected]b2f7ac42010-10-26 18:43:1814#include "base/message_pump_glib_x_dispatch.h"
15
[email protected]71ad9c6f2010-10-22 16:17:4716namespace {
17
18gboolean PlaceholderDispatch(GSource* source,
19 GSourceFunc cb,
20 gpointer data) {
21 return TRUE;
22}
23
[email protected]130eeb32010-11-12 17:42:3024#if defined(HAVE_XINPUT2)
25
26// Setup XInput2 select for the GtkWidget.
27gboolean GtkWidgetRealizeCallback(GSignalInvocationHint* hint, guint nparams,
28 const GValue* pvalues, gpointer data) {
29 GtkWidget* widget = GTK_WIDGET(g_value_get_object(pvalues));
30 GdkWindow* window = widget->window;
31 base::MessagePumpGlibX* msgpump = static_cast<base::MessagePumpGlibX*>(data);
32
33 DCHECK(window); // TODO(sad): Remove once determined if necessary.
34
35 // TODO(sad): Do we need to set a flag on |window| to make sure we don't
36 // select for the same GdkWindow multiple times? Does it matter?
37 msgpump->SetupXInput2ForXWindow(GDK_WINDOW_XID(window));
38
39 return true;
40}
41
42// We need to capture all the GDK windows that get created, and start
43// listening for XInput2 events. So we setup a callback to the 'realize'
44// signal for GTK+ widgets, so that whenever the signal triggers for any
45// GtkWidget, which means the GtkWidget should now have a GdkWindow, we can
46// setup XInput2 events for the GdkWindow.
47void SetupGtkWidgetRealizeNotifier(base::MessagePumpGlibX* msgpump) {
48 guint signal_id;
49 gpointer klass = g_type_class_ref(GTK_TYPE_WIDGET);
50
51 g_signal_parse_name("realize", GTK_TYPE_WIDGET, &signal_id, NULL, FALSE);
52 g_signal_add_emission_hook(signal_id, 0, GtkWidgetRealizeCallback,
53 static_cast<gpointer>(msgpump), NULL);
54
55 g_type_class_unref(klass);
56}
57
58#endif // HAVE_XINPUT2
59
[email protected]71ad9c6f2010-10-22 16:17:4760} // namespace
61
62namespace base {
63
64MessagePumpGlibX::MessagePumpGlibX() : base::MessagePumpForUI(),
[email protected]130eeb32010-11-12 17:42:3065#if defined(HAVE_XINPUT2)
66 xiopcode_(-1),
67 masters_(),
68 slaves_(),
69#endif
[email protected]71ad9c6f2010-10-22 16:17:4770 gdksource_(NULL),
71 dispatching_event_(false),
72 capture_x_events_(0),
73 capture_gdk_events_(0) {
74 gdk_event_handler_set(&EventDispatcherX, this, NULL);
75
[email protected]130eeb32010-11-12 17:42:3076#if defined(HAVE_XINPUT2)
77 InitializeXInput2();
78#endif
[email protected]71ad9c6f2010-10-22 16:17:4779 InitializeEventsToCapture();
80}
81
82MessagePumpGlibX::~MessagePumpGlibX() {
83}
84
85bool MessagePumpGlibX::RunOnce(GMainContext* context, bool block) {
86 GdkDisplay* gdisp = gdk_display_get_default();
87 Display* display = GDK_DISPLAY_XDISPLAY(gdisp);
88 if (XPending(display)) {
89 XEvent xev;
90 XPeekEvent(display, &xev);
[email protected]130eeb32010-11-12 17:42:3091 if (capture_x_events_[xev.type]
92#if defined(HAVE_XINPUT2)
93 && (xev.type != GenericEvent || xev.xcookie.extension == xiopcode_)
94#endif
95 ) {
[email protected]71ad9c6f2010-10-22 16:17:4796 XNextEvent(display, &xev);
97
[email protected]b2f7ac42010-10-26 18:43:1898 bool processed = static_cast<MessagePumpGlibXDispatcher*>
99 (GetDispatcher())->Dispatch(&xev);
[email protected]71ad9c6f2010-10-22 16:17:47100
[email protected]b2f7ac42010-10-26 18:43:18101 if (!processed) {
102 DLOG(WARNING) << "Event (" << xev.type << ") not handled.";
[email protected]130eeb32010-11-12 17:42:30103
104 // TODO(sad): It is necessary to put back the event so that the default
105 // GDK events handler can take care of it. Without this, it is
106 // impossible to use the omnibox at the moment. However, this will
107 // eventually be removed once the omnibox code is updated for touchui.
108 XPutBackEvent(display, &xev);
109 g_main_context_iteration(context, FALSE);
[email protected]b2f7ac42010-10-26 18:43:18110 }
[email protected]71ad9c6f2010-10-22 16:17:47111 } else {
[email protected]b2f7ac42010-10-26 18:43:18112 // TODO(sad): A couple of extra events can still sneak in during this.
113 // Those should be sent back to the X queue from the dispatcher
114 // EventDispatcherX.
[email protected]71ad9c6f2010-10-22 16:17:47115 g_main_context_iteration(context, FALSE);
116 }
117 }
118
119 bool retvalue;
120 if (gdksource_) {
121 // Replace the dispatch callback of the GDK event source temporarily so that
122 // it doesn't read events from X.
123 gboolean (*cb)(GSource*, GSourceFunc, void*) =
124 gdksource_->source_funcs->dispatch;
125 gdksource_->source_funcs->dispatch = PlaceholderDispatch;
126
127 dispatching_event_ = true;
128 retvalue = g_main_context_iteration(context, block);
129 dispatching_event_ = false;
130
131 gdksource_->source_funcs->dispatch = cb;
132 } else {
133 retvalue = g_main_context_iteration(context, block);
134 }
135
136 return retvalue;
137}
138
139void MessagePumpGlibX::InitializeEventsToCapture(void) {
140 // TODO(sad): Decide which events we want to capture and update the tables
141 // accordingly.
142 capture_x_events_[KeyPress] = true;
143 capture_gdk_events_[GDK_KEY_PRESS] = true;
144
145 capture_x_events_[KeyRelease] = true;
146 capture_gdk_events_[GDK_KEY_RELEASE] = true;
[email protected]b2f7ac42010-10-26 18:43:18147
148 capture_x_events_[ButtonPress] = true;
149 capture_gdk_events_[GDK_BUTTON_PRESS] = true;
150
151 capture_x_events_[ButtonRelease] = true;
152 capture_gdk_events_[GDK_BUTTON_RELEASE] = true;
153
154 capture_x_events_[MotionNotify] = true;
155 capture_gdk_events_[GDK_MOTION_NOTIFY] = true;
[email protected]130eeb32010-11-12 17:42:30156
157#if defined(HAVE_XINPUT2)
158 capture_x_events_[GenericEvent] = true;
159#endif
[email protected]71ad9c6f2010-10-22 16:17:47160}
161
[email protected]130eeb32010-11-12 17:42:30162#if defined(HAVE_XINPUT2)
163void MessagePumpGlibX::InitializeXInput2(void) {
164 GdkDisplay* display = gdk_display_get_default();
165 Display* xdisplay = GDK_DISPLAY_XDISPLAY(display);
166 int event, err;
167
168 if (!XQueryExtension(xdisplay, "XInputExtension", &xiopcode_, &event, &err)) {
169 DLOG(WARNING) << "X Input extension not available.";
170 xiopcode_ = -1;
171 return;
172 }
173
174 int major = 2, minor = 0;
175 if (XIQueryVersion(xdisplay, &major, &minor) == BadRequest) {
176 DLOG(WARNING) << "XInput2 not supported in the server.";
177 xiopcode_ = -1;
178 return;
179 }
180
181 SetupGtkWidgetRealizeNotifier(this);
182
183 // Instead of asking X for the list of devices all the time, let's maintain a
184 // list of slave (physical) and master (virtual) pointer devices.
185 int count = 0;
186 XIDeviceInfo* devices = XIQueryDevice(xdisplay, XIAllDevices, &count);
187 for (int i = 0; i < count; i++) {
188 XIDeviceInfo* devinfo = devices + i;
189 if (devinfo->use == XISlavePointer) {
190 slaves_.insert(devinfo->deviceid);
191 } else if (devinfo->use == XIMasterPointer) {
192 masters_.insert(devinfo->deviceid);
193 }
194 // We do not need to care about XIFloatingSlave, because the callback for
195 // XI_HierarchyChanged event will take care of it.
196 }
197 XIFreeDeviceInfo(devices);
198
199 // TODO(sad): Select on root for XI_HierarchyChanged so that slaves_ and
200 // masters_ can be kept up-to-date. This is a relatively rare event, so we can
201 // put it off for a later time.
202 // Note: It is not necessary to listen for XI_DeviceChanged events.
203}
204
205void MessagePumpGlibX::SetupXInput2ForXWindow(Window xwindow) {
206 Display* xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
207
208 // Setup mask for mouse events.
209 unsigned char mask[(XI_LASTEVENT + 7)/8];
210 memset(mask, 0, sizeof(mask));
211
212 XISetMask(mask, XI_ButtonPress);
213 XISetMask(mask, XI_ButtonRelease);
214 XISetMask(mask, XI_Motion);
215
216 // It is necessary to select only for the master devices. XInput2 provides
217 // enough information to the event callback to decide which slave device
218 // triggered the event, thus decide whether the 'pointer event' is a 'mouse
219 // event' or a 'touch event'. So it is not necessary to select for the slave
220 // devices here.
221 XIEventMask evmasks[masters_.size()];
222 int count = 0;
223 for (std::set<int>::const_iterator iter = masters_.begin();
224 iter != masters_.end();
225 ++iter, ++count) {
226 evmasks[count].deviceid = *iter;
227 evmasks[count].mask_len = sizeof(mask);
228 evmasks[count].mask = mask;
229 }
230
231 XISelectEvents(xdisplay, xwindow, evmasks, masters_.size());
232
233 // TODO(sad): Setup masks for keyboard events.
234
235 XFlush(xdisplay);
236}
237
238#endif // HAVE_XINPUT2
239
[email protected]71ad9c6f2010-10-22 16:17:47240void MessagePumpGlibX::EventDispatcherX(GdkEvent* event, gpointer data) {
241 MessagePumpGlibX* pump_x = reinterpret_cast<MessagePumpGlibX*>(data);
242
243 if (!pump_x->gdksource_) {
244 pump_x->gdksource_ = g_main_current_source();
245 } else if (!pump_x->IsDispatchingEvent()) {
246 if (event->type != GDK_NOTHING &&
247 pump_x->capture_gdk_events_[event->type]) {
248 // TODO(sad): An X event is caught by the GDK handler. Put it back in the
249 // X queue so that we catch it in the next iteration. When done, the
250 // following DLOG statement will be removed.
[email protected]b2f7ac42010-10-26 18:43:18251 DLOG(WARNING) << "GDK received an event it shouldn't have";
[email protected]71ad9c6f2010-10-22 16:17:47252 }
253 }
254
255 pump_x->DispatchEvents(event);
256}
257
258} // namespace base