x11_whole_screen_move_loop.cc 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  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. #include "ui/base/x/x11_whole_screen_move_loop.h"
  5. #include <stddef.h>
  6. #include <memory>
  7. #include <utility>
  8. #include "base/bind.h"
  9. #include "base/logging.h"
  10. #include "base/run_loop.h"
  11. #include "base/task/current_thread.h"
  12. #include "base/task/single_thread_task_runner.h"
  13. #include "base/threading/thread_task_runner_handle.h"
  14. #include "ui/base/x/x11_pointer_grab.h"
  15. #include "ui/base/x/x11_util.h"
  16. #include "ui/events/event.h"
  17. #include "ui/events/event_utils.h"
  18. #include "ui/events/keycodes/keyboard_code_conversion_x.h"
  19. #include "ui/events/platform/platform_event_source.h"
  20. #include "ui/events/platform/scoped_event_dispatcher.h"
  21. #include "ui/events/x/events_x_utils.h"
  22. #include "ui/events/x/x11_event_translation.h"
  23. #include "ui/gfx/x/connection.h"
  24. #include "ui/gfx/x/keysyms/keysyms.h"
  25. #include "ui/gfx/x/x11_window_event_manager.h"
  26. #include "ui/gfx/x/xproto.h"
  27. namespace ui {
  28. namespace {
  29. // XGrabKey requires the modifier mask to explicitly be specified.
  30. constexpr x11::ModMask kModifiersMasks[] = {
  31. {}, // No additional modifier.
  32. x11::ModMask::c_2, // Num lock
  33. x11::ModMask::Lock, // Caps lock
  34. x11::ModMask::c_5, // Scroll lock
  35. x11::ModMask::c_2 | x11::ModMask::Lock,
  36. x11::ModMask::c_2 | x11::ModMask::c_5,
  37. x11::ModMask::Lock | x11::ModMask::c_5,
  38. x11::ModMask::c_2 | x11::ModMask::Lock | x11::ModMask::c_5,
  39. };
  40. const char* GrabStatusToString(x11::GrabStatus grab_status) {
  41. switch (grab_status) {
  42. case x11::GrabStatus::Success:
  43. return "Success";
  44. case x11::GrabStatus::AlreadyGrabbed:
  45. return "AlreadyGrabbed";
  46. case x11::GrabStatus::InvalidTime:
  47. return "InvalidTime";
  48. case x11::GrabStatus::NotViewable:
  49. return "NotViewable";
  50. case x11::GrabStatus::Frozen:
  51. return "Frozen";
  52. }
  53. NOTREACHED();
  54. return "";
  55. }
  56. } // namespace
  57. X11WholeScreenMoveLoop::X11WholeScreenMoveLoop(X11MoveLoopDelegate* delegate)
  58. : delegate_(delegate),
  59. in_move_loop_(false),
  60. grab_input_window_(x11::Window::None),
  61. grabbed_pointer_(false),
  62. canceled_(false) {}
  63. X11WholeScreenMoveLoop::~X11WholeScreenMoveLoop() {
  64. EndMoveLoop();
  65. }
  66. void X11WholeScreenMoveLoop::PostDispatchIfNeeded(const ui::MouseEvent& event) {
  67. }
  68. ////////////////////////////////////////////////////////////////////////////////
  69. // DesktopWindowTreeHostLinux, ui::PlatformEventDispatcher implementation:
  70. bool X11WholeScreenMoveLoop::CanDispatchEvent(const ui::PlatformEvent& event) {
  71. return in_move_loop_;
  72. }
  73. uint32_t X11WholeScreenMoveLoop::DispatchEvent(const ui::PlatformEvent& event) {
  74. DCHECK(base::CurrentUIThread::IsSet());
  75. // This method processes all events while the move loop is active.
  76. if (!in_move_loop_)
  77. return ui::POST_DISPATCH_PERFORM_DEFAULT;
  78. switch (event->type()) {
  79. case ui::ET_MOUSE_MOVED:
  80. case ui::ET_MOUSE_DRAGGED: {
  81. auto& current_xevent = *x11::Connection::Get()->dispatching_event();
  82. x11::Event last_xevent;
  83. std::unique_ptr<ui::Event> last_motion;
  84. auto* mouse_event = event->AsMouseEvent();
  85. if ((current_xevent.As<x11::MotionNotifyEvent>() ||
  86. current_xevent.As<x11::Input::DeviceEvent>()) &&
  87. ui::CoalescePendingMotionEvents(current_xevent, &last_xevent)) {
  88. last_motion = ui::BuildEventFromXEvent(last_xevent);
  89. mouse_event = last_motion->AsMouseEvent();
  90. }
  91. delegate_->OnMouseMovement(mouse_event->root_location(),
  92. mouse_event->flags(),
  93. mouse_event->time_stamp());
  94. return ui::POST_DISPATCH_NONE;
  95. }
  96. case ui::ET_MOUSE_RELEASED: {
  97. if (event->AsMouseEvent()->IsLeftMouseButton()) {
  98. // Assume that drags are being done with the left mouse button. Only
  99. // break the drag if the left mouse button was released.
  100. delegate_->OnMouseReleased();
  101. if (!grabbed_pointer_) {
  102. // If the source widget had capture prior to the move loop starting,
  103. // it may be relying on views::Widget getting the mouse release and
  104. // releasing capture in Widget::OnMouseEvent().
  105. return ui::POST_DISPATCH_PERFORM_DEFAULT;
  106. }
  107. }
  108. return ui::POST_DISPATCH_NONE;
  109. }
  110. case ui::ET_KEY_PRESSED:
  111. if (event->AsKeyEvent()->key_code() == ui::VKEY_ESCAPE) {
  112. canceled_ = true;
  113. EndMoveLoop();
  114. return ui::POST_DISPATCH_NONE;
  115. }
  116. break;
  117. default:
  118. break;
  119. }
  120. return ui::POST_DISPATCH_PERFORM_DEFAULT;
  121. }
  122. bool X11WholeScreenMoveLoop::RunMoveLoop(
  123. bool can_grab_pointer,
  124. scoped_refptr<ui::X11Cursor> old_cursor,
  125. scoped_refptr<ui::X11Cursor> new_cursor) {
  126. DCHECK(!in_move_loop_); // Can only handle one nested loop at a time.
  127. // Query the mouse cursor prior to the move loop starting so that it can be
  128. // restored when the move loop finishes.
  129. initial_cursor_ = old_cursor;
  130. auto* connection = x11::Connection::Get();
  131. CreateDragInputWindow(connection);
  132. // Only grab mouse capture of |grab_input_window_| if |can_grab_pointer| is
  133. // true aka the source that initiated the move loop doesn't have explicit
  134. // grab.
  135. // - The caller may intend to transfer capture to a different X11Window
  136. // when the move loop ends and not release capture.
  137. // - Releasing capture and X window destruction are both asynchronous. We drop
  138. // events targeted at |grab_input_window_| in the time between the move
  139. // loop ends and |grab_input_window_| loses capture.
  140. grabbed_pointer_ = false;
  141. if (can_grab_pointer) {
  142. grabbed_pointer_ = GrabPointer(new_cursor);
  143. if (!grabbed_pointer_) {
  144. x11::Connection::Get()->DestroyWindow({grab_input_window_});
  145. return false;
  146. }
  147. }
  148. GrabEscKey();
  149. std::unique_ptr<ui::ScopedEventDispatcher> old_dispatcher =
  150. std::move(nested_dispatcher_);
  151. nested_dispatcher_ =
  152. ui::PlatformEventSource::GetInstance()->OverrideDispatcher(this);
  153. base::WeakPtr<X11WholeScreenMoveLoop> alive(weak_factory_.GetWeakPtr());
  154. in_move_loop_ = true;
  155. canceled_ = false;
  156. base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
  157. quit_closure_ = run_loop.QuitClosure();
  158. run_loop.Run();
  159. if (!alive)
  160. return false;
  161. nested_dispatcher_ = std::move(old_dispatcher);
  162. return !canceled_;
  163. }
  164. void X11WholeScreenMoveLoop::UpdateCursor(scoped_refptr<ui::X11Cursor> cursor) {
  165. if (in_move_loop_)
  166. ui::ChangeActivePointerGrabCursor(cursor);
  167. }
  168. void X11WholeScreenMoveLoop::EndMoveLoop() {
  169. if (!in_move_loop_)
  170. return;
  171. // TODO(erg): Is this ungrab the cause of having to click to give input focus
  172. // on drawn out windows? Not ungrabbing here screws the X server until I kill
  173. // the chrome process.
  174. // Ungrab before we let go of the window.
  175. if (grabbed_pointer_)
  176. ui::UngrabPointer();
  177. else
  178. UpdateCursor(initial_cursor_);
  179. auto* connection = x11::Connection::Get();
  180. auto esc_keycode = connection->KeysymToKeycode(XK_Escape);
  181. for (auto mask : kModifiersMasks)
  182. connection->UngrabKey({esc_keycode, grab_input_window_, mask});
  183. // Restore the previous dispatcher.
  184. nested_dispatcher_.reset();
  185. delegate_->OnMoveLoopEnded();
  186. grab_input_window_events_.reset();
  187. connection->DestroyWindow({grab_input_window_});
  188. grab_input_window_ = x11::Window::None;
  189. in_move_loop_ = false;
  190. std::move(quit_closure_).Run();
  191. }
  192. bool X11WholeScreenMoveLoop::GrabPointer(scoped_refptr<X11Cursor> cursor) {
  193. auto* connection = x11::Connection::Get();
  194. // Pass "owner_events" as false so that X sends all mouse events to
  195. // |grab_input_window_|.
  196. auto ret = ui::GrabPointer(grab_input_window_, false, cursor);
  197. if (ret != x11::GrabStatus::Success) {
  198. DLOG(ERROR) << "Grabbing pointer for dragging failed: "
  199. << GrabStatusToString(ret);
  200. }
  201. connection->Flush();
  202. return ret == x11::GrabStatus::Success;
  203. }
  204. void X11WholeScreenMoveLoop::GrabEscKey() {
  205. auto* connection = x11::Connection::Get();
  206. auto esc_keycode = connection->KeysymToKeycode(XK_Escape);
  207. for (auto mask : kModifiersMasks) {
  208. connection->GrabKey({false, grab_input_window_, mask, esc_keycode,
  209. x11::GrabMode::Async, x11::GrabMode::Async});
  210. }
  211. }
  212. void X11WholeScreenMoveLoop::CreateDragInputWindow(
  213. x11::Connection* connection) {
  214. grab_input_window_ = connection->GenerateId<x11::Window>();
  215. connection->CreateWindow(x11::CreateWindowRequest{
  216. .wid = grab_input_window_,
  217. .parent = connection->default_root(),
  218. .x = -100,
  219. .y = -100,
  220. .width = 10,
  221. .height = 10,
  222. .c_class = x11::WindowClass::InputOnly,
  223. .override_redirect = x11::Bool32(true),
  224. });
  225. auto event_mask =
  226. x11::EventMask::ButtonPress | x11::EventMask::ButtonRelease |
  227. x11::EventMask::PointerMotion | x11::EventMask::KeyPress |
  228. x11::EventMask::KeyRelease | x11::EventMask::StructureNotify;
  229. grab_input_window_events_ = std::make_unique<x11::XScopedEventSelector>(
  230. grab_input_window_, event_mask);
  231. connection->MapWindow({grab_input_window_});
  232. RaiseWindow(grab_input_window_);
  233. }
  234. } // namespace ui