Advertisement
Guest User

message_pump_glib_x.cc

a guest
Aug 7th, 2014
279
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.20 KB | None | 0 0
  1. // 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>
  8. #if defined(HAVE_XINPUT2)
  9. #include <X11/extensions/XInput2.h>
  10. #else
  11. #include <X11/Xlib.h>
  12. #endif
  13.  
  14. #include "base/message_pump_glib_x_dispatch.h"
  15.  
  16. namespace {
  17.  
  18. gboolean PlaceholderDispatch(GSource* source,
  19. GSourceFunc cb,
  20. gpointer data) {
  21. return TRUE;
  22. }
  23.  
  24. #if defined(HAVE_XINPUT2)
  25.  
  26. // Setup XInput2 select for the GtkWidget.
  27. gboolean 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. if (GDK_WINDOW_TYPE(window) != GDK_WINDOW_TOPLEVEL &&
  36. GDK_WINDOW_TYPE(window) != GDK_WINDOW_CHILD &&
  37. GDK_WINDOW_TYPE(window) != GDK_WINDOW_DIALOG)
  38. return true;
  39.  
  40. // TODO(sad): Do we need to set a flag on |window| to make sure we don't
  41. // select for the same GdkWindow multiple times? Does it matter?
  42. msgpump->SetupXInput2ForXWindow(GDK_WINDOW_XID(window));
  43.  
  44. return true;
  45. }
  46.  
  47. // We need to capture all the GDK windows that get created, and start
  48. // listening for XInput2 events. So we setup a callback to the 'realize'
  49. // signal for GTK+ widgets, so that whenever the signal triggers for any
  50. // GtkWidget, which means the GtkWidget should now have a GdkWindow, we can
  51. // setup XInput2 events for the GdkWindow.
  52. static guint realize_signal_id = 0;
  53. static guint realize_hook_id = 0;
  54.  
  55. void SetupGtkWidgetRealizeNotifier(base::MessagePumpGlibX* msgpump) {
  56. gpointer klass = g_type_class_ref(GTK_TYPE_WIDGET);
  57.  
  58. g_signal_parse_name("realize", GTK_TYPE_WIDGET,
  59. &realize_signal_id, NULL, FALSE);
  60. realize_hook_id = g_signal_add_emission_hook(realize_signal_id, 0,
  61. GtkWidgetRealizeCallback, static_cast<gpointer>(msgpump), NULL);
  62.  
  63. g_type_class_unref(klass);
  64. }
  65.  
  66. void RemoveGtkWidgetRealizeNotifier() {
  67. if (realize_signal_id != 0)
  68. g_signal_remove_emission_hook(realize_signal_id, realize_hook_id);
  69. realize_signal_id = 0;
  70. realize_hook_id = 0;
  71. }
  72.  
  73. #endif // HAVE_XINPUT2
  74.  
  75. } // namespace
  76.  
  77. namespace base {
  78.  
  79. MessagePumpGlibX::MessagePumpGlibX() : base::MessagePumpForUI(),
  80. #if defined(HAVE_XINPUT2)
  81. xiopcode_(-1),
  82. masters_(),
  83. slaves_(),
  84. #endif
  85. gdksource_(NULL),
  86. dispatching_event_(false),
  87. capture_x_events_(0),
  88. capture_gdk_events_(0) {
  89. gdk_event_handler_set(&EventDispatcherX, this, NULL);
  90.  
  91. #if defined(HAVE_XINPUT2)
  92. InitializeXInput2();
  93. #endif
  94. InitializeEventsToCapture();
  95. }
  96.  
  97. MessagePumpGlibX::~MessagePumpGlibX() {
  98. #if defined(HAVE_XINPUT2)
  99. RemoveGtkWidgetRealizeNotifier();
  100. #endif
  101. }
  102.  
  103. #if defined(HAVE_XINPUT2)
  104. void MessagePumpGlibX::SetupXInput2ForXWindow(Window xwindow) {
  105. Display* xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
  106.  
  107. // Setup mask for mouse events.
  108. unsigned char mask[(XI_LASTEVENT + 7)/8];
  109. memset(mask, 0, sizeof(mask));
  110.  
  111. XISetMask(mask, XI_ButtonPress);
  112. XISetMask(mask, XI_ButtonRelease);
  113. XISetMask(mask, XI_Motion);
  114.  
  115. // It is necessary to select only for the master devices. XInput2 provides
  116. // enough information to the event callback to decide which slave device
  117. // triggered the event, thus decide whether the 'pointer event' is a 'mouse
  118. // event' or a 'touch event'. So it is not necessary to select for the slave
  119. // devices here.
  120. XIEventMask evmasks[masters_.size()];
  121. int count = 0;
  122. for (std::set<int>::const_iterator iter = masters_.begin();
  123. iter != masters_.end();
  124. ++iter, ++count) {
  125. evmasks[count].deviceid = *iter;
  126. evmasks[count].mask_len = sizeof(mask);
  127. evmasks[count].mask = mask;
  128. }
  129.  
  130. XISelectEvents(xdisplay, xwindow, evmasks, masters_.size());
  131.  
  132. // TODO(sad): Setup masks for keyboard events.
  133.  
  134. XFlush(xdisplay);
  135. }
  136. #endif // HAVE_XINPUT2
  137.  
  138. bool MessagePumpGlibX::RunOnce(GMainContext* context, bool block) {
  139. GdkDisplay* gdisp = gdk_display_get_default();
  140. if (!gdisp || !GetDispatcher())
  141. return MessagePumpForUI::RunOnce(context, block);
  142.  
  143. Display* display = GDK_DISPLAY_XDISPLAY(gdisp);
  144. bool should_quit = false;
  145.  
  146. if (XPending(display)) {
  147. XEvent xev;
  148. XPeekEvent(display, &xev);
  149. if (capture_x_events_[xev.type]
  150. #if defined(HAVE_XINPUT2)
  151. && (xev.type != GenericEvent || xev.xcookie.extension == xiopcode_)
  152. #endif
  153. ) {
  154. XNextEvent(display, &xev);
  155.  
  156. #if defined(HAVE_XINPUT2)
  157. bool have_cookie = false;
  158. if (xev.type == GenericEvent &&
  159. XGetEventData(xev.xgeneric.display, &xev.xcookie)) {
  160. have_cookie = true;
  161. }
  162. #endif
  163.  
  164. MessagePumpGlibXDispatcher::DispatchStatus status =
  165. static_cast<MessagePumpGlibXDispatcher*>
  166. (GetDispatcher())->DispatchX(&xev);
  167.  
  168. if (status == MessagePumpGlibXDispatcher::EVENT_QUIT) {
  169. should_quit = true;
  170. Quit();
  171. } else if (status == MessagePumpGlibXDispatcher::EVENT_IGNORED) {
  172. DLOG(WARNING) << "Event (" << xev.type << ") not handled.";
  173.  
  174. // TODO(sad): It is necessary to put back the event so that the default
  175. // GDK events handler can take care of it. Without this, it is
  176. // impossible to use the omnibox at the moment. However, this will
  177. // eventually be removed once the omnibox code is updated for touchui.
  178. XPutBackEvent(display, &xev);
  179. //if (gdksource_)
  180. // gdksource_->source_funcs->dispatch = gdkdispatcher_;
  181. //g_main_context_iteration(context, FALSE);
  182. }
  183.  
  184. #if defined(HAVE_XINPUT2)
  185. if (have_cookie) {
  186. XFreeEventData(xev.xgeneric.display, &xev.xcookie);
  187. }
  188. #endif
  189. } else {
  190. // TODO(sad): A couple of extra events can still sneak in during this.
  191. // Those should be sent back to the X queue from the dispatcher
  192. // EventDispatcherX.
  193. if (gdksource_)
  194. gdksource_->source_funcs->dispatch = gdkdispatcher_;
  195. g_main_context_iteration(context, FALSE);
  196. }
  197. }
  198.  
  199. if (should_quit)
  200. return true;
  201.  
  202. bool retvalue;
  203. if (gdksource_) {
  204. // Replace the dispatch callback of the GDK event source temporarily so that
  205. // it doesn't read events from X.
  206. gboolean (*cb)(GSource*, GSourceFunc, void*) =
  207. gdksource_->source_funcs->dispatch;
  208. gdksource_->source_funcs->dispatch = PlaceholderDispatch;
  209.  
  210. dispatching_event_ = true;
  211. retvalue = g_main_context_iteration(context, block);
  212. dispatching_event_ = false;
  213.  
  214. gdksource_->source_funcs->dispatch = cb;
  215. } else {
  216. retvalue = g_main_context_iteration(context, block);
  217. }
  218.  
  219. return retvalue;
  220. }
  221.  
  222. void MessagePumpGlibX::EventDispatcherX(GdkEvent* event, gpointer data) {
  223. MessagePumpGlibX* pump_x = reinterpret_cast<MessagePumpGlibX*>(data);
  224.  
  225. if (!pump_x->gdksource_) {
  226. pump_x->gdksource_ = g_main_current_source();
  227. pump_x->gdkdispatcher_ = pump_x->gdksource_->source_funcs->dispatch;
  228. } else if (!pump_x->IsDispatchingEvent()) {
  229. if (event->type != GDK_NOTHING &&
  230. pump_x->capture_gdk_events_[event->type]) {
  231. // TODO(sad): An X event is caught by the GDK handler. Put it back in the
  232. // X queue so that we catch it in the next iteration. When done, the
  233. // following DLOG statement will be removed.
  234. DLOG(WARNING) << "GDK received an event it shouldn't have";
  235. }
  236. }
  237.  
  238. pump_x->DispatchEvents(event);
  239. }
  240.  
  241. void MessagePumpGlibX::InitializeEventsToCapture(void) {
  242. // TODO(sad): Decide which events we want to capture and update the tables
  243. // accordingly.
  244. capture_x_events_[KeyPress] = true;
  245. capture_gdk_events_[GDK_KEY_PRESS] = true;
  246.  
  247. capture_x_events_[KeyRelease] = true;
  248. capture_gdk_events_[GDK_KEY_RELEASE] = true;
  249.  
  250. capture_x_events_[ButtonPress] = true;
  251. capture_gdk_events_[GDK_BUTTON_PRESS] = true;
  252.  
  253. capture_x_events_[ButtonRelease] = true;
  254. capture_gdk_events_[GDK_BUTTON_RELEASE] = true;
  255.  
  256. capture_x_events_[MotionNotify] = true;
  257. capture_gdk_events_[GDK_MOTION_NOTIFY] = true;
  258.  
  259. #if defined(HAVE_XINPUT2)
  260. capture_x_events_[GenericEvent] = true;
  261. #endif
  262. }
  263.  
  264. #if defined(HAVE_XINPUT2)
  265. void MessagePumpGlibX::InitializeXInput2(void) {
  266. GdkDisplay* display = gdk_display_get_default();
  267. if (!display)
  268. return;
  269.  
  270. Display* xdisplay = GDK_DISPLAY_XDISPLAY(display);
  271. int event, err;
  272.  
  273. if (!XQueryExtension(xdisplay, "XInputExtension", &xiopcode_, &event, &err)) {
  274. DLOG(WARNING) << "X Input extension not available.";
  275. xiopcode_ = -1;
  276. return;
  277. }
  278.  
  279. int major = 2, minor = 0;
  280. if (XIQueryVersion(xdisplay, &major, &minor) == BadRequest) {
  281. DLOG(WARNING) << "XInput2 not supported in the server.";
  282. xiopcode_ = -1;
  283. return;
  284. }
  285.  
  286. // TODO(sad): Here, we only setup so that the X windows created by GTK+ are
  287. // setup for XInput2 events. We need a way to listen for XInput2 events for X
  288. // windows created by other means (e.g. for context menus).
  289. SetupGtkWidgetRealizeNotifier(this);
  290.  
  291. // Instead of asking X for the list of devices all the time, let's maintain a
  292. // list of slave (physical) and master (virtual) pointer devices.
  293. int count = 0;
  294. XIDeviceInfo* devices = XIQueryDevice(xdisplay, XIAllDevices, &count);
  295. for (int i = 0; i < count; i++) {
  296. XIDeviceInfo* devinfo = devices + i;
  297. if (devinfo->use == XISlavePointer) {
  298. slaves_.insert(devinfo->deviceid);
  299. } else if (devinfo->use == XIMasterPointer) {
  300. masters_.insert(devinfo->deviceid);
  301. }
  302. // We do not need to care about XIFloatingSlave, because the callback for
  303. // XI_HierarchyChanged event will take care of it.
  304. }
  305. XIFreeDeviceInfo(devices);
  306.  
  307. // TODO(sad): Select on root for XI_HierarchyChanged so that slaves_ and
  308. // masters_ can be kept up-to-date. This is a relatively rare event, so we can
  309. // put it off for a later time.
  310. // Note: It is not necessary to listen for XI_DeviceChanged events.
  311. }
  312. #endif // HAVE_XINPUT2
  313.  
  314. } // namespace base
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement