1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com// Copyright 2014 The Chromium Authors. All rights reserved.
2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com// Use of this source code is governed by a BSD-style license that can be
3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com// found in the LICENSE file.
4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com
5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com#include "ui/events/platform/x11/x11_event_source.h"
6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com
7971d0c8049c6bfc7a58f0b41f8f59f9ec9ca077bbsalomon@google.com#include <X11/extensions/XInput2.h>
857f7abc8659f17e58fc2d1410117033ad524f9d3epoger@google.com#include <X11/X.h>
957f7abc8659f17e58fc2d1410117033ad524f9d3epoger@google.com#include <X11/Xlib.h>
1057f7abc8659f17e58fc2d1410117033ad524f9d3epoger@google.com#include <X11/XKBlib.h>
1157f7abc8659f17e58fc2d1410117033ad524f9d3epoger@google.com
1257f7abc8659f17e58fc2d1410117033ad524f9d3epoger@google.com#include "base/logging.h"
1357f7abc8659f17e58fc2d1410117033ad524f9d3epoger@google.com#include "ui/events/event_utils.h"
1457f7abc8659f17e58fc2d1410117033ad524f9d3epoger@google.com#include "ui/events/platform/platform_event_dispatcher.h"
1557f7abc8659f17e58fc2d1410117033ad524f9d3epoger@google.com#include "ui/events/x/device_data_manager_x11.h"
16b29c883fb46ac6099440d82ac57b86d25386daedbungeman@google.com#include "ui/events/x/hotplug_event_handler_x11.h"
176f6568b27eae62fea23ab8192c6da02ab892bb5eepoger@google.com#include "ui/gfx/x/x11_types.h"
1837269607334b99bf814c7dc6b426745d9b7c7e3fepoger@google.com
197bc13a62609149f0b535c2f3ff7210eb834d8b36epoger@google.comnamespace ui {
205f6a00775511b5675607c2bfdbb096c0a815025depoger@google.com
21b9b9a18ab459c2616ac4a52c9f8cc0637d284229reed@android.comnamespace {
22d9ba9a05d6f5766fdb1378b6ed84c0659009a8dascroggo@google.com
238a85d0c4938173476d037d7af0ee3b9436a1234ereed@google.comint g_xinput_opcode = -1;
244370aedf7f55af74e9ebb4ad1c2e010c08236dfajunov@google.com
25971d0c8049c6bfc7a58f0b41f8f59f9ec9ca077bbsalomon@google.combool InitializeXInput2(XDisplay* display) {
265e00989a283111cef05bed8102e45c16651e43e4commit-bot@chromium.org  if (!display)
27de96163a80167636d95837f9ee6a2e98baf9d350epoger@google.com    return false;
28c41295d1223136da4f9e4c65b7145360684ede4bcommit-bot@chromium.org
295af9b2032b552516c9223d9fb22185b022b13c62scroggo@google.com  int event, err;
308015dd83ae37147bb630d4751030868051ad0caereed@android.com
318015dd83ae37147bb630d4751030868051ad0caereed@android.com  int xiopcode;
328015dd83ae37147bb630d4751030868051ad0caereed@android.com  if (!XQueryExtension(display, "XInputExtension", &xiopcode, &event, &err)) {
33e3bb3bce3e9c1f3bc8ee779b1b3383c18e560bcecommit-bot@chromium.org    DVLOG(1) << "X Input extension not available.";
34e8ebeb1f8fde6525bbab988c6090a5d3ab19855bepoger@google.com    return false;
350dcbece326ce2fa1f9046aa69a21bb99de08714avandebo@chromium.org  }
369875dd14af6d768da8d1a4be58b98fc91ceca0ddtomhudson@google.com  g_xinput_opcode = xiopcode;
37977b9c8af3ef1b9a2fa2a0037cf3734cf2ba13d9robertphillips@google.com
385e00989a283111cef05bed8102e45c16651e43e4commit-bot@chromium.org#if defined(USE_XI2_MT)
3972c9672ce274a3b6cb40800d66374edf25b157a3scroggo@google.com  // USE_XI2_MT also defines the required XI2 minor minimum version.
40f711f320a34216fc9f64839b90b99a1492c54e09epoger@google.com  int major = 2, minor = USE_XI2_MT;
4111db6fa9b9fc30b1f504d9f53e0f6ae7149cc5c5reed@google.com#else
422a48c3adb7cf4fc754f99a41352210b4a99edf04bsalomon@google.com  int major = 2, minor = 0;
43310478e72c63e639373465216271b81f1e4a9136epoger@google.com#endif
443cb834bd27a16cc60ff30adae96659558c2dc91fjunov@chromium.org  if (XIQueryVersion(display, &major, &minor) == BadRequest) {
4572c9672ce274a3b6cb40800d66374edf25b157a3scroggo@google.com    DVLOG(1) << "XInput2 not supported in the server.";
460770044da6d61dcbc8d9673fed8dd92460faa314reed@google.com    return false;
47515dcd36032997ce335daa0163c6d67e851bcad1commit-bot@chromium.org  }
48515dcd36032997ce335daa0163c6d67e851bcad1commit-bot@chromium.org#if defined(USE_XI2_MT)
498065ec50f1937c1562810bfe2b216abfb98362b3commit-bot@chromium.org  if (major < 2 || (major == 2 && minor < USE_XI2_MT)) {
50515dcd36032997ce335daa0163c6d67e851bcad1commit-bot@chromium.org    DVLOG(1) << "XI version on server is " << major << "." << minor << ". "
51515dcd36032997ce335daa0163c6d67e851bcad1commit-bot@chromium.org            << "But 2." << USE_XI2_MT << " is required.";
52515dcd36032997ce335daa0163c6d67e851bcad1commit-bot@chromium.org    return false;
53515dcd36032997ce335daa0163c6d67e851bcad1commit-bot@chromium.org  }
54ed5eb4ef2aa1d6c705bc3ed466f9caba2a230a2bepoger@google.com#endif
55ed5eb4ef2aa1d6c705bc3ed466f9caba2a230a2bepoger@google.com
56cf8fb1f6f03fc77f9927564f9ef9abeeeec508d2bsalomon@google.com  return true;
57cf8fb1f6f03fc77f9927564f9ef9abeeeec508d2bsalomon@google.com}
58cf8fb1f6f03fc77f9927564f9ef9abeeeec508d2bsalomon@google.com
59cf8fb1f6f03fc77f9927564f9ef9abeeeec508d2bsalomon@google.combool InitializeXkb(XDisplay* display) {
606f6568b27eae62fea23ab8192c6da02ab892bb5eepoger@google.com  if (!display)
616f6568b27eae62fea23ab8192c6da02ab892bb5eepoger@google.com    return false;
626f6568b27eae62fea23ab8192c6da02ab892bb5eepoger@google.com
63cf8fb1f6f03fc77f9927564f9ef9abeeeec508d2bsalomon@google.com  int opcode, event, error;
6480724dfeb320d1152128cd33636c9024952432d3epoger@google.com  int major = XkbMajorVersion;
65cf8fb1f6f03fc77f9927564f9ef9abeeeec508d2bsalomon@google.com  int minor = XkbMinorVersion;
66123ac1d4eab757052407064623643fdc59f85363bsalomon@google.com  if (!XkbQueryExtension(display, &opcode, &event, &error, &major, &minor)) {
67cf8fb1f6f03fc77f9927564f9ef9abeeeec508d2bsalomon@google.com    DVLOG(1) << "Xkb extension not available.";
68cf8fb1f6f03fc77f9927564f9ef9abeeeec508d2bsalomon@google.com    return false;
69cf8fb1f6f03fc77f9927564f9ef9abeeeec508d2bsalomon@google.com  }
7076c913db20de7ae1baa49ae66e943bf7f40781ccepoger@google.com
7176c913db20de7ae1baa49ae66e943bf7f40781ccepoger@google.com  // Ask the server not to send KeyRelease event when the user holds down a key.
728923c6cfd580ac9accb11b909fa2a033d69553aareed@google.com  // crbug.com/138092
735e00989a283111cef05bed8102e45c16651e43e4commit-bot@chromium.org  Bool supported_return;
748923c6cfd580ac9accb11b909fa2a033d69553aareed@google.com  if (!XkbSetDetectableAutoRepeat(display, True, &supported_return)) {
750770044da6d61dcbc8d9673fed8dd92460faa314reed@google.com    DVLOG(1) << "XKB not supported in the server.";
769875dd14af6d768da8d1a4be58b98fc91ceca0ddtomhudson@google.com    return false;
779875dd14af6d768da8d1a4be58b98fc91ceca0ddtomhudson@google.com  }
780770044da6d61dcbc8d9673fed8dd92460faa314reed@google.com
7900dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.com  return true;
80e3cc2eb88fef9b2123c6ea2ed813ce53b6385926epoger@google.com}
81e3cc2eb88fef9b2123c6ea2ed813ce53b6385926epoger@google.com
82e3cc2eb88fef9b2123c6ea2ed813ce53b6385926epoger@google.com}  // namespace
83b29c883fb46ac6099440d82ac57b86d25386daedbungeman@google.com
84b29c883fb46ac6099440d82ac57b86d25386daedbungeman@google.comX11EventSource::X11EventSource(XDisplay* display)
85b29c883fb46ac6099440d82ac57b86d25386daedbungeman@google.com    : display_(display),
86b29c883fb46ac6099440d82ac57b86d25386daedbungeman@google.com      continue_stream_(true) {
8746cce91f4859b9c229938d4d649870c0a43b1806reed@google.com  CHECK(display_);
8846cce91f4859b9c229938d4d649870c0a43b1806reed@google.com  DeviceDataManagerX11::CreateInstance();
8946cce91f4859b9c229938d4d649870c0a43b1806reed@google.com  hotplug_event_handler_.reset(
9046cce91f4859b9c229938d4d649870c0a43b1806reed@google.com      new HotplugEventHandlerX11(DeviceDataManager::GetInstance()));
9100dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.com  InitializeXInput2(display_);
9200dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.com  InitializeXkb(display_);
9300dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.com
9400dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.com  // Force the initial device query to have an update list of active devices.
9500dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.com  hotplug_event_handler_->OnHotplugEvent();
963914958a49ee089ddeb04acc16373aae8bc2eaf7bsalomon@google.com}
973914958a49ee089ddeb04acc16373aae8bc2eaf7bsalomon@google.com
983914958a49ee089ddeb04acc16373aae8bc2eaf7bsalomon@google.comX11EventSource::~X11EventSource() {
993914958a49ee089ddeb04acc16373aae8bc2eaf7bsalomon@google.com}
100dd0ac281e920b01a63789893cc3e7422789658ddreed@android.com
10100dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.com// static
102d4dfd10bb6f9bf3ac6e1ebc9bc3ae22c6d06321freed@google.comX11EventSource* X11EventSource::GetInstance() {
103dd0ac281e920b01a63789893cc3e7422789658ddreed@android.com  return static_cast<X11EventSource*>(PlatformEventSource::GetInstance());
10400dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.com}
105dd0ac281e920b01a63789893cc3e7422789658ddreed@android.com
10600dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.com////////////////////////////////////////////////////////////////////////////////
107dd0ac281e920b01a63789893cc3e7422789658ddreed@android.com// X11EventSource, public
10800dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.com
10900dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.comvoid X11EventSource::DispatchXEvents() {
11000dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.com  DCHECK(display_);
111d4dfd10bb6f9bf3ac6e1ebc9bc3ae22c6d06321freed@google.com  // Handle all pending events.
11200dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.com  // It may be useful to eventually align this event dispatch with vsync, but
113dd0ac281e920b01a63789893cc3e7422789658ddreed@android.com  // not yet.
11400dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.com  continue_stream_ = true;
11500dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.com  while (XPending(display_) && continue_stream_) {
11600dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.com    XEvent xevent;
11700dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.com    XNextEvent(display_, &xevent);
11800dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.com    DispatchEvent(&xevent);
11900dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.com  }
12000dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.com}
121d4dfd10bb6f9bf3ac6e1ebc9bc3ae22c6d06321freed@google.com
12200dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.comvoid X11EventSource::BlockUntilWindowMapped(XID window) {
12300dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.com  XEvent event;
12400dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.com  do {
12500dae86f5872b60927b28a32b375bc01cd7c61c9reed@android.com    // Block until there's a message of |event_mask| type on |w|. Then remove
126ce057fec926f8ec1d60ed4722d1e51a0086e7976epoger@google.com    // it from the queue and stuff it in |event|.
127ce057fec926f8ec1d60ed4722d1e51a0086e7976epoger@google.com    XWindowEvent(display_, window, StructureNotifyMask, &event);
128ce057fec926f8ec1d60ed4722d1e51a0086e7976epoger@google.com    DispatchEvent(&event);
129ce057fec926f8ec1d60ed4722d1e51a0086e7976epoger@google.com  } while (event.type != MapNotify);
130ce057fec926f8ec1d60ed4722d1e51a0086e7976epoger@google.com}
131686abdfab0e4c45de1fd30774896c46e43a299acvandebo@chromium.org
1323cb834bd27a16cc60ff30adae96659558c2dc91fjunov@chromium.org////////////////////////////////////////////////////////////////////////////////
1333cb834bd27a16cc60ff30adae96659558c2dc91fjunov@chromium.org// X11EventSource, private
1343cb834bd27a16cc60ff30adae96659558c2dc91fjunov@chromium.org
1353cb834bd27a16cc60ff30adae96659558c2dc91fjunov@chromium.orguint32_t X11EventSource::DispatchEvent(XEvent* xevent) {
1363cb834bd27a16cc60ff30adae96659558c2dc91fjunov@chromium.org  bool have_cookie = false;
1373cb834bd27a16cc60ff30adae96659558c2dc91fjunov@chromium.org  if (xevent->type == GenericEvent &&
1383cb834bd27a16cc60ff30adae96659558c2dc91fjunov@chromium.org      XGetEventData(xevent->xgeneric.display, &xevent->xcookie)) {
1393cb834bd27a16cc60ff30adae96659558c2dc91fjunov@chromium.org    have_cookie = true;
1403cb834bd27a16cc60ff30adae96659558c2dc91fjunov@chromium.org  }
1413cb834bd27a16cc60ff30adae96659558c2dc91fjunov@chromium.org
142686abdfab0e4c45de1fd30774896c46e43a299acvandebo@chromium.org  uint32_t action = PlatformEventSource::DispatchEvent(xevent);
143686abdfab0e4c45de1fd30774896c46e43a299acvandebo@chromium.org  if (xevent->type == GenericEvent &&
1447361f54294d65a5c42ce5cf1cd56d0fd7122e268bsalomon@google.com      xevent->xgeneric.evtype == XI_HierarchyChanged) {
1457361f54294d65a5c42ce5cf1cd56d0fd7122e268bsalomon@google.com    ui::UpdateDeviceList();
1467361f54294d65a5c42ce5cf1cd56d0fd7122e268bsalomon@google.com    hotplug_event_handler_->OnHotplugEvent();
1477361f54294d65a5c42ce5cf1cd56d0fd7122e268bsalomon@google.com  }
148f28dd8ab109663a6fe67fd4ee3d66248e0dac686epoger@google.com
1497361f54294d65a5c42ce5cf1cd56d0fd7122e268bsalomon@google.com  if (have_cookie)
1507361f54294d65a5c42ce5cf1cd56d0fd7122e268bsalomon@google.com    XFreeEventData(xevent->xgeneric.display, &xevent->xcookie);
1517361f54294d65a5c42ce5cf1cd56d0fd7122e268bsalomon@google.com  return action;
1527361f54294d65a5c42ce5cf1cd56d0fd7122e268bsalomon@google.com}
1539875dd14af6d768da8d1a4be58b98fc91ceca0ddtomhudson@google.com
1547361f54294d65a5c42ce5cf1cd56d0fd7122e268bsalomon@google.comvoid X11EventSource::StopCurrentEventStream() {
1557361f54294d65a5c42ce5cf1cd56d0fd7122e268bsalomon@google.com  continue_stream_ = false;
156cf8fb1f6f03fc77f9927564f9ef9abeeeec508d2bsalomon@google.com}
1577361f54294d65a5c42ce5cf1cd56d0fd7122e268bsalomon@google.com
1587361f54294d65a5c42ce5cf1cd56d0fd7122e268bsalomon@google.com}  // namespace ui
1597361f54294d65a5c42ce5cf1cd56d0fd7122e268bsalomon@google.com