x11_pointer_grab.cc 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. // Copyright 2015 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. #include "ui/base/x/x11_pointer_grab.h"
  5. #include "base/bind.h"
  6. #include "base/cancelable_callback.h"
  7. #include "base/check.h"
  8. #include "base/no_destructor.h"
  9. #include "base/sys_byteorder.h"
  10. #include "ui/base/x/x11_util.h"
  11. #include "ui/events/devices/x11/device_data_manager_x11.h"
  12. #include "ui/gfx/x/connection.h"
  13. #include "ui/gfx/x/xinput.h"
  14. #include "ui/gfx/x/xproto.h"
  15. namespace ui {
  16. namespace {
  17. // The grab window. None if there are no active pointer grabs.
  18. x11::Window g_grab_window = x11::Window::None;
  19. // The "owner events" parameter used to grab the pointer.
  20. bool g_owner_events = false;
  21. base::CancelableOnceCallback<void(x11::Cursor)>& GetGrabCallback() {
  22. static base::NoDestructor<base::CancelableOnceCallback<void(x11::Cursor)>>
  23. callback;
  24. return *callback;
  25. }
  26. x11::GrabStatus GrabPointerImpl(x11::Window window,
  27. bool owner_events,
  28. x11::Cursor cursor) {
  29. GetGrabCallback().Cancel();
  30. auto result = x11::GrabStatus::InvalidTime;
  31. auto* connection = x11::Connection::Get();
  32. if (ui::IsXInput2Available()) {
  33. // Do an xinput pointer grab. If there is an active xinput pointer grab
  34. // as a result of normal button press, GrabPointer() will fail.
  35. auto mask = x11::Input::XIEventMask::ButtonPress |
  36. x11::Input::XIEventMask::ButtonRelease |
  37. x11::Input::XIEventMask::Motion |
  38. x11::Input::XIEventMask::TouchBegin |
  39. x11::Input::XIEventMask::TouchUpdate |
  40. x11::Input::XIEventMask::TouchEnd;
  41. static_assert(sizeof(mask) == 4, "");
  42. for (auto master_pointer :
  43. ui::DeviceDataManagerX11::GetInstance()->master_pointers()) {
  44. x11::Input::XIGrabDeviceRequest req{
  45. .window = window,
  46. .time = x11::Time::CurrentTime,
  47. .cursor = cursor,
  48. .deviceid = master_pointer,
  49. .mode = x11::GrabMode::Async,
  50. .paired_device_mode = x11::GrabMode::Async,
  51. .owner_events = owner_events ? x11::Input::GrabOwner::Owner
  52. : x11::Input::GrabOwner::NoOwner,
  53. .mask = {base::ByteSwapToLE32(static_cast<uint32_t>(mask))},
  54. };
  55. if (auto reply = connection->xinput().XIGrabDevice(req).Sync())
  56. result = reply->status;
  57. // Assume that the grab will succeed on either all or none of the master
  58. // pointers.
  59. if (result != x11::GrabStatus::Success) {
  60. // Try core pointer grab.
  61. break;
  62. }
  63. }
  64. }
  65. if (result != x11::GrabStatus::Success) {
  66. auto mask = x11::EventMask::PointerMotion | x11::EventMask::ButtonRelease |
  67. x11::EventMask::ButtonPress;
  68. x11::GrabPointerRequest req{
  69. .owner_events = owner_events,
  70. .grab_window = window,
  71. .event_mask = mask,
  72. .pointer_mode = x11::GrabMode::Async,
  73. .keyboard_mode = x11::GrabMode::Async,
  74. .confine_to = x11::Window::None,
  75. .cursor = cursor,
  76. .time = x11::Time::CurrentTime,
  77. };
  78. if (auto reply = connection->GrabPointer(req).Sync())
  79. result = reply->status;
  80. }
  81. if (result == x11::GrabStatus::Success) {
  82. g_grab_window = window;
  83. g_owner_events = owner_events;
  84. }
  85. return result;
  86. }
  87. } // namespace
  88. x11::GrabStatus GrabPointer(x11::Window window,
  89. bool owner_events,
  90. scoped_refptr<ui::X11Cursor> cursor) {
  91. if (!cursor)
  92. return GrabPointerImpl(window, owner_events, x11::Cursor::None);
  93. if (cursor->loaded())
  94. return GrabPointerImpl(window, owner_events, cursor->xcursor());
  95. auto result = GrabPointerImpl(window, owner_events, x11::Cursor::None);
  96. GetGrabCallback().Reset(base::BindOnce(base::IgnoreResult(GrabPointerImpl),
  97. window, owner_events));
  98. cursor->OnCursorLoaded(GetGrabCallback().callback());
  99. return result;
  100. }
  101. void ChangeActivePointerGrabCursor(scoped_refptr<ui::X11Cursor> cursor) {
  102. if (g_grab_window != x11::Window::None)
  103. GrabPointer(g_grab_window, g_owner_events, cursor);
  104. }
  105. void UngrabPointer() {
  106. GetGrabCallback().Cancel();
  107. g_grab_window = x11::Window::None;
  108. auto* connection = x11::Connection::Get();
  109. if (ui::IsXInput2Available()) {
  110. for (auto master_pointer :
  111. ui::DeviceDataManagerX11::GetInstance()->master_pointers()) {
  112. connection->xinput()
  113. .XIUngrabDevice({x11::Time::CurrentTime, master_pointer})
  114. .IgnoreError();
  115. }
  116. }
  117. // Try core pointer ungrab in case the XInput2 pointer ungrab failed.
  118. connection->UngrabPointer().IgnoreError();
  119. }
  120. } // namespace ui