123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ui/base/x/x11_whole_screen_move_loop.h"
- #include <stddef.h>
- #include <memory>
- #include <utility>
- #include "base/bind.h"
- #include "base/logging.h"
- #include "base/run_loop.h"
- #include "base/task/current_thread.h"
- #include "base/task/single_thread_task_runner.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "ui/base/x/x11_pointer_grab.h"
- #include "ui/base/x/x11_util.h"
- #include "ui/events/event.h"
- #include "ui/events/event_utils.h"
- #include "ui/events/keycodes/keyboard_code_conversion_x.h"
- #include "ui/events/platform/platform_event_source.h"
- #include "ui/events/platform/scoped_event_dispatcher.h"
- #include "ui/events/x/events_x_utils.h"
- #include "ui/events/x/x11_event_translation.h"
- #include "ui/gfx/x/connection.h"
- #include "ui/gfx/x/keysyms/keysyms.h"
- #include "ui/gfx/x/x11_window_event_manager.h"
- #include "ui/gfx/x/xproto.h"
- namespace ui {
- namespace {
- // XGrabKey requires the modifier mask to explicitly be specified.
- constexpr x11::ModMask kModifiersMasks[] = {
- {}, // No additional modifier.
- x11::ModMask::c_2, // Num lock
- x11::ModMask::Lock, // Caps lock
- x11::ModMask::c_5, // Scroll lock
- x11::ModMask::c_2 | x11::ModMask::Lock,
- x11::ModMask::c_2 | x11::ModMask::c_5,
- x11::ModMask::Lock | x11::ModMask::c_5,
- x11::ModMask::c_2 | x11::ModMask::Lock | x11::ModMask::c_5,
- };
- const char* GrabStatusToString(x11::GrabStatus grab_status) {
- switch (grab_status) {
- case x11::GrabStatus::Success:
- return "Success";
- case x11::GrabStatus::AlreadyGrabbed:
- return "AlreadyGrabbed";
- case x11::GrabStatus::InvalidTime:
- return "InvalidTime";
- case x11::GrabStatus::NotViewable:
- return "NotViewable";
- case x11::GrabStatus::Frozen:
- return "Frozen";
- }
- NOTREACHED();
- return "";
- }
- } // namespace
- X11WholeScreenMoveLoop::X11WholeScreenMoveLoop(X11MoveLoopDelegate* delegate)
- : delegate_(delegate),
- in_move_loop_(false),
- grab_input_window_(x11::Window::None),
- grabbed_pointer_(false),
- canceled_(false) {}
- X11WholeScreenMoveLoop::~X11WholeScreenMoveLoop() {
- EndMoveLoop();
- }
- void X11WholeScreenMoveLoop::PostDispatchIfNeeded(const ui::MouseEvent& event) {
- }
- ////////////////////////////////////////////////////////////////////////////////
- // DesktopWindowTreeHostLinux, ui::PlatformEventDispatcher implementation:
- bool X11WholeScreenMoveLoop::CanDispatchEvent(const ui::PlatformEvent& event) {
- return in_move_loop_;
- }
- uint32_t X11WholeScreenMoveLoop::DispatchEvent(const ui::PlatformEvent& event) {
- DCHECK(base::CurrentUIThread::IsSet());
- // This method processes all events while the move loop is active.
- if (!in_move_loop_)
- return ui::POST_DISPATCH_PERFORM_DEFAULT;
- switch (event->type()) {
- case ui::ET_MOUSE_MOVED:
- case ui::ET_MOUSE_DRAGGED: {
- auto& current_xevent = *x11::Connection::Get()->dispatching_event();
- x11::Event last_xevent;
- std::unique_ptr<ui::Event> last_motion;
- auto* mouse_event = event->AsMouseEvent();
- if ((current_xevent.As<x11::MotionNotifyEvent>() ||
- current_xevent.As<x11::Input::DeviceEvent>()) &&
- ui::CoalescePendingMotionEvents(current_xevent, &last_xevent)) {
- last_motion = ui::BuildEventFromXEvent(last_xevent);
- mouse_event = last_motion->AsMouseEvent();
- }
- delegate_->OnMouseMovement(mouse_event->root_location(),
- mouse_event->flags(),
- mouse_event->time_stamp());
- return ui::POST_DISPATCH_NONE;
- }
- case ui::ET_MOUSE_RELEASED: {
- if (event->AsMouseEvent()->IsLeftMouseButton()) {
- // Assume that drags are being done with the left mouse button. Only
- // break the drag if the left mouse button was released.
- delegate_->OnMouseReleased();
- if (!grabbed_pointer_) {
- // If the source widget had capture prior to the move loop starting,
- // it may be relying on views::Widget getting the mouse release and
- // releasing capture in Widget::OnMouseEvent().
- return ui::POST_DISPATCH_PERFORM_DEFAULT;
- }
- }
- return ui::POST_DISPATCH_NONE;
- }
- case ui::ET_KEY_PRESSED:
- if (event->AsKeyEvent()->key_code() == ui::VKEY_ESCAPE) {
- canceled_ = true;
- EndMoveLoop();
- return ui::POST_DISPATCH_NONE;
- }
- break;
- default:
- break;
- }
- return ui::POST_DISPATCH_PERFORM_DEFAULT;
- }
- bool X11WholeScreenMoveLoop::RunMoveLoop(
- bool can_grab_pointer,
- scoped_refptr<ui::X11Cursor> old_cursor,
- scoped_refptr<ui::X11Cursor> new_cursor) {
- DCHECK(!in_move_loop_); // Can only handle one nested loop at a time.
- // Query the mouse cursor prior to the move loop starting so that it can be
- // restored when the move loop finishes.
- initial_cursor_ = old_cursor;
- auto* connection = x11::Connection::Get();
- CreateDragInputWindow(connection);
- // Only grab mouse capture of |grab_input_window_| if |can_grab_pointer| is
- // true aka the source that initiated the move loop doesn't have explicit
- // grab.
- // - The caller may intend to transfer capture to a different X11Window
- // when the move loop ends and not release capture.
- // - Releasing capture and X window destruction are both asynchronous. We drop
- // events targeted at |grab_input_window_| in the time between the move
- // loop ends and |grab_input_window_| loses capture.
- grabbed_pointer_ = false;
- if (can_grab_pointer) {
- grabbed_pointer_ = GrabPointer(new_cursor);
- if (!grabbed_pointer_) {
- x11::Connection::Get()->DestroyWindow({grab_input_window_});
- return false;
- }
- }
- GrabEscKey();
- std::unique_ptr<ui::ScopedEventDispatcher> old_dispatcher =
- std::move(nested_dispatcher_);
- nested_dispatcher_ =
- ui::PlatformEventSource::GetInstance()->OverrideDispatcher(this);
- base::WeakPtr<X11WholeScreenMoveLoop> alive(weak_factory_.GetWeakPtr());
- in_move_loop_ = true;
- canceled_ = false;
- base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
- quit_closure_ = run_loop.QuitClosure();
- run_loop.Run();
- if (!alive)
- return false;
- nested_dispatcher_ = std::move(old_dispatcher);
- return !canceled_;
- }
- void X11WholeScreenMoveLoop::UpdateCursor(scoped_refptr<ui::X11Cursor> cursor) {
- if (in_move_loop_)
- ui::ChangeActivePointerGrabCursor(cursor);
- }
- void X11WholeScreenMoveLoop::EndMoveLoop() {
- if (!in_move_loop_)
- return;
- // TODO(erg): Is this ungrab the cause of having to click to give input focus
- // on drawn out windows? Not ungrabbing here screws the X server until I kill
- // the chrome process.
- // Ungrab before we let go of the window.
- if (grabbed_pointer_)
- ui::UngrabPointer();
- else
- UpdateCursor(initial_cursor_);
- auto* connection = x11::Connection::Get();
- auto esc_keycode = connection->KeysymToKeycode(XK_Escape);
- for (auto mask : kModifiersMasks)
- connection->UngrabKey({esc_keycode, grab_input_window_, mask});
- // Restore the previous dispatcher.
- nested_dispatcher_.reset();
- delegate_->OnMoveLoopEnded();
- grab_input_window_events_.reset();
- connection->DestroyWindow({grab_input_window_});
- grab_input_window_ = x11::Window::None;
- in_move_loop_ = false;
- std::move(quit_closure_).Run();
- }
- bool X11WholeScreenMoveLoop::GrabPointer(scoped_refptr<X11Cursor> cursor) {
- auto* connection = x11::Connection::Get();
- // Pass "owner_events" as false so that X sends all mouse events to
- // |grab_input_window_|.
- auto ret = ui::GrabPointer(grab_input_window_, false, cursor);
- if (ret != x11::GrabStatus::Success) {
- DLOG(ERROR) << "Grabbing pointer for dragging failed: "
- << GrabStatusToString(ret);
- }
- connection->Flush();
- return ret == x11::GrabStatus::Success;
- }
- void X11WholeScreenMoveLoop::GrabEscKey() {
- auto* connection = x11::Connection::Get();
- auto esc_keycode = connection->KeysymToKeycode(XK_Escape);
- for (auto mask : kModifiersMasks) {
- connection->GrabKey({false, grab_input_window_, mask, esc_keycode,
- x11::GrabMode::Async, x11::GrabMode::Async});
- }
- }
- void X11WholeScreenMoveLoop::CreateDragInputWindow(
- x11::Connection* connection) {
- grab_input_window_ = connection->GenerateId<x11::Window>();
- connection->CreateWindow(x11::CreateWindowRequest{
- .wid = grab_input_window_,
- .parent = connection->default_root(),
- .x = -100,
- .y = -100,
- .width = 10,
- .height = 10,
- .c_class = x11::WindowClass::InputOnly,
- .override_redirect = x11::Bool32(true),
- });
- auto event_mask =
- x11::EventMask::ButtonPress | x11::EventMask::ButtonRelease |
- x11::EventMask::PointerMotion | x11::EventMask::KeyPress |
- x11::EventMask::KeyRelease | x11::EventMask::StructureNotify;
- grab_input_window_events_ = std::make_unique<x11::XScopedEventSelector>(
- grab_input_window_, event_mask);
- connection->MapWindow({grab_input_window_});
- RaiseWindow(grab_input_window_);
- }
- } // namespace ui
|