Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / base / message_loop / message_pump_x11.cc
1 // Copyright (c) 2012 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_loop/message_pump_x11.h"
6
7 #include <glib.h>
8 #include <X11/X.h>
9 #include <X11/extensions/XInput2.h>
10 #include <X11/XKBlib.h>
11
12 #include "base/basictypes.h"
13 #include "base/message_loop/message_loop.h"
14
15 namespace base {
16
17 namespace {
18
19 gboolean XSourcePrepare(GSource* source, gint* timeout_ms) {
20   if (XPending(MessagePumpX11::GetDefaultXDisplay()))
21     *timeout_ms = 0;
22   else
23     *timeout_ms = -1;
24   return FALSE;
25 }
26
27 gboolean XSourceCheck(GSource* source) {
28   return XPending(MessagePumpX11::GetDefaultXDisplay());
29 }
30
31 gboolean XSourceDispatch(GSource* source,
32                          GSourceFunc unused_func,
33                          gpointer data) {
34   MessagePumpX11* pump = static_cast<MessagePumpX11*>(data);
35   pump->DispatchXEvents();
36   return TRUE;
37 }
38
39 GSourceFuncs XSourceFuncs = {
40   XSourcePrepare,
41   XSourceCheck,
42   XSourceDispatch,
43   NULL
44 };
45
46 // The connection is essentially a global that's accessed through a static
47 // method and destroyed whenever ~MessagePumpX11() is called. We do this
48 // for historical reasons so user code can call
49 // MessagePumpForUI::GetDefaultXDisplay() where MessagePumpForUI is a typedef
50 // to whatever type in the current build.
51 //
52 // TODO(erg): This can be changed to something more sane like
53 // MessagePumpX11::Current()->display() once MessagePumpGtk goes away.
54 Display* g_xdisplay = NULL;
55 int g_xinput_opcode = -1;
56
57 bool InitializeXInput2() {
58   Display* display = MessagePumpX11::GetDefaultXDisplay();
59   if (!display)
60     return false;
61
62   int event, err;
63
64   int xiopcode;
65   if (!XQueryExtension(display, "XInputExtension", &xiopcode, &event, &err)) {
66     DVLOG(1) << "X Input extension not available.";
67     return false;
68   }
69   g_xinput_opcode = xiopcode;
70
71 #if defined(USE_XI2_MT)
72   // USE_XI2_MT also defines the required XI2 minor minimum version.
73   int major = 2, minor = USE_XI2_MT;
74 #else
75   int major = 2, minor = 0;
76 #endif
77   if (XIQueryVersion(display, &major, &minor) == BadRequest) {
78     DVLOG(1) << "XInput2 not supported in the server.";
79     return false;
80   }
81 #if defined(USE_XI2_MT)
82   if (major < 2 || (major == 2 && minor < USE_XI2_MT)) {
83     DVLOG(1) << "XI version on server is " << major << "." << minor << ". "
84             << "But 2." << USE_XI2_MT << " is required.";
85     return false;
86   }
87 #endif
88
89   return true;
90 }
91
92 Window FindEventTarget(const NativeEvent& xev) {
93   Window target = xev->xany.window;
94   if (xev->type == GenericEvent &&
95       static_cast<XIEvent*>(xev->xcookie.data)->extension == g_xinput_opcode) {
96     target = static_cast<XIDeviceEvent*>(xev->xcookie.data)->event;
97   }
98   return target;
99 }
100
101 bool InitializeXkb() {
102   Display* display = MessagePumpX11::GetDefaultXDisplay();
103   if (!display)
104     return false;
105
106   int opcode, event, error;
107   int major = XkbMajorVersion;
108   int minor = XkbMinorVersion;
109   if (!XkbQueryExtension(display, &opcode, &event, &error, &major, &minor)) {
110     DVLOG(1) << "Xkb extension not available.";
111     return false;
112   }
113
114   // Ask the server not to send KeyRelease event when the user holds down a key.
115   // crbug.com/138092
116   Bool supported_return;
117   if (!XkbSetDetectableAutoRepeat(display, True, &supported_return)) {
118     DVLOG(1) << "XKB not supported in the server.";
119     return false;
120   }
121
122   return true;
123 }
124
125 }  // namespace
126
127 MessagePumpX11::MessagePumpX11() : MessagePumpGlib(),
128     x_source_(NULL) {
129   InitializeXInput2();
130   InitializeXkb();
131   InitXSource();
132
133   // Can't put this in the initializer list because g_xdisplay may not exist
134   // until after InitXSource().
135   x_root_window_ = DefaultRootWindow(g_xdisplay);
136 }
137
138 MessagePumpX11::~MessagePumpX11() {
139   g_source_destroy(x_source_);
140   g_source_unref(x_source_);
141   XCloseDisplay(g_xdisplay);
142   g_xdisplay = NULL;
143 }
144
145 // static
146 Display* MessagePumpX11::GetDefaultXDisplay() {
147   if (!g_xdisplay)
148     g_xdisplay = XOpenDisplay(NULL);
149   return g_xdisplay;
150 }
151
152 #if defined(TOOLKIT_GTK)
153 // static
154 MessagePumpX11* MessagePumpX11::Current() {
155   MessageLoop* loop = MessageLoop::current();
156   return static_cast<MessagePumpX11*>(loop->pump_gpu());
157 }
158 #else
159 // static
160 MessagePumpX11* MessagePumpX11::Current() {
161   MessageLoopForUI* loop = MessageLoopForUI::current();
162   return static_cast<MessagePumpX11*>(loop->pump_ui());
163 }
164 #endif
165
166 void MessagePumpX11::AddDispatcherForWindow(
167     MessagePumpDispatcher* dispatcher,
168     unsigned long xid) {
169   dispatchers_.insert(std::make_pair(xid, dispatcher));
170 }
171
172 void MessagePumpX11::RemoveDispatcherForWindow(unsigned long xid) {
173   dispatchers_.erase(xid);
174 }
175
176 void MessagePumpX11::AddDispatcherForRootWindow(
177     MessagePumpDispatcher* dispatcher) {
178   root_window_dispatchers_.AddObserver(dispatcher);
179 }
180
181 void MessagePumpX11::RemoveDispatcherForRootWindow(
182     MessagePumpDispatcher* dispatcher) {
183   root_window_dispatchers_.RemoveObserver(dispatcher);
184 }
185
186 void MessagePumpX11::AddObserver(MessagePumpObserver* observer) {
187   observers_.AddObserver(observer);
188 }
189
190 void MessagePumpX11::RemoveObserver(MessagePumpObserver* observer) {
191   observers_.RemoveObserver(observer);
192 }
193
194 void MessagePumpX11::DispatchXEvents() {
195   Display* display = GetDefaultXDisplay();
196   DCHECK(display);
197   MessagePumpDispatcher* dispatcher = GetDispatcher();
198   if (!dispatcher)
199     dispatcher = this;
200
201   // In the general case, we want to handle all pending events before running
202   // the tasks. This is what happens in the message_pump_glib case.
203   while (XPending(display)) {
204     XEvent xev;
205     XNextEvent(display, &xev);
206     ProcessXEvent(dispatcher, &xev);
207     if (ShouldQuit())
208       break;
209   }
210 }
211
212 void MessagePumpX11::BlockUntilWindowMapped(unsigned long xid) {
213   XEvent event;
214
215   Display* display = GetDefaultXDisplay();
216   DCHECK(display);
217
218   MessagePumpDispatcher* dispatcher = GetDispatcher();
219   if (!dispatcher)
220     dispatcher = this;
221
222   do {
223     // Block until there's a message of |event_mask| type on |w|. Then remove
224     // it from the queue and stuff it in |event|.
225     XWindowEvent(display, xid, StructureNotifyMask, &event);
226     ProcessXEvent(dispatcher, &event);
227   } while (event.type != MapNotify);
228 }
229
230 void MessagePumpX11::InitXSource() {
231   // CHECKs are to help track down crbug.com/113106.
232   CHECK(!x_source_);
233   Display* display = GetDefaultXDisplay();
234   CHECK(display) << "Unable to get connection to X server";
235   x_poll_.reset(new GPollFD());
236   CHECK(x_poll_.get());
237   x_poll_->fd = ConnectionNumber(display);
238   x_poll_->events = G_IO_IN;
239
240   x_source_ = g_source_new(&XSourceFuncs, sizeof(GSource));
241   g_source_add_poll(x_source_, x_poll_.get());
242   g_source_set_can_recurse(x_source_, TRUE);
243   g_source_set_callback(x_source_, NULL, this, NULL);
244   g_source_attach(x_source_, g_main_context_default());
245 }
246
247 void MessagePumpX11::ProcessXEvent(MessagePumpDispatcher* dispatcher,
248                                    XEvent* xev) {
249   CHECK(dispatcher);
250   bool have_cookie = false;
251   if (xev->type == GenericEvent &&
252       XGetEventData(xev->xgeneric.display, &xev->xcookie)) {
253     have_cookie = true;
254   }
255
256   if (!WillProcessXEvent(xev)) {
257     uint32_t action = dispatcher->Dispatch(xev);
258     bool should_quit = (action & POST_DISPATCH_QUIT_LOOP);
259     if (dispatcher != this && (action & POST_DISPATCH_PERFORM_DEFAULT))
260       action = Dispatch(xev);
261     if ((action & POST_DISPATCH_QUIT_LOOP) || should_quit)
262       Quit();
263     DidProcessXEvent(xev);
264   }
265
266   if (have_cookie)
267     XFreeEventData(xev->xgeneric.display, &xev->xcookie);
268 }
269
270 bool MessagePumpX11::WillProcessXEvent(XEvent* xevent) {
271   if (!observers().might_have_observers())
272     return false;
273   ObserverListBase<MessagePumpObserver>::Iterator it(observers());
274   MessagePumpObserver* obs;
275   while ((obs = it.GetNext()) != NULL) {
276     if (obs->WillProcessEvent(xevent))
277       return true;
278   }
279   return false;
280 }
281
282 void MessagePumpX11::DidProcessXEvent(XEvent* xevent) {
283   FOR_EACH_OBSERVER(MessagePumpObserver, observers(), DidProcessEvent(xevent));
284 }
285
286 MessagePumpDispatcher* MessagePumpX11::GetDispatcherForXEvent(
287     const NativeEvent& xev) const {
288   ::Window x_window = FindEventTarget(xev);
289   DispatchersMap::const_iterator it = dispatchers_.find(x_window);
290   return it != dispatchers_.end() ? it->second : NULL;
291 }
292
293 uint32_t MessagePumpX11::Dispatch(const NativeEvent& xev) {
294   // MappingNotify events (meaning that the keyboard or pointer buttons have
295   // been remapped) aren't associated with a window; send them to all
296   // dispatchers.
297   if (xev->type == MappingNotify) {
298     for (DispatchersMap::const_iterator it = dispatchers_.begin();
299          it != dispatchers_.end(); ++it) {
300       it->second->Dispatch(xev);
301     }
302     return POST_DISPATCH_NONE;
303   }
304
305   if (FindEventTarget(xev) == x_root_window_) {
306     FOR_EACH_OBSERVER(MessagePumpDispatcher, root_window_dispatchers_,
307                       Dispatch(xev));
308     return POST_DISPATCH_NONE;
309   }
310   MessagePumpDispatcher* dispatcher = GetDispatcherForXEvent(xev);
311   return dispatcher ? dispatcher->Dispatch(xev) : POST_DISPATCH_NONE;
312 }
313
314 }  // namespace base