blob: b90f9e3c613b48e79694b60d41e26f88d02be840 [file] [log] [blame]
// Copyright 2015 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_pointer_grab.h"
#include "base/bind.h"
#include "base/cancelable_callback.h"
#include "base/check.h"
#include "base/no_destructor.h"
#include "base/sys_byteorder.h"
#include "ui/base/x/x11_util.h"
#include "ui/events/devices/x11/device_data_manager_x11.h"
#include "ui/gfx/x/connection.h"
#include "ui/gfx/x/xinput.h"
#include "ui/gfx/x/xproto.h"
namespace ui {
namespace {
// The grab window. None if there are no active pointer grabs.
x11::Window g_grab_window = x11::Window::None;
// The "owner events" parameter used to grab the pointer.
bool g_owner_events = false;
base::CancelableOnceCallback<void(x11::Cursor)>& GetGrabCallback() {
static base::NoDestructor<base::CancelableOnceCallback<void(x11::Cursor)>>
callback;
return *callback;
}
x11::GrabStatus GrabPointerImpl(x11::Window window,
bool owner_events,
x11::Cursor cursor) {
GetGrabCallback().Cancel();
auto result = x11::GrabStatus::InvalidTime;
auto* connection = x11::Connection::Get();
if (ui::IsXInput2Available()) {
// Do an xinput pointer grab. If there is an active xinput pointer grab
// as a result of normal button press, GrabPointer() will fail.
auto mask = x11::Input::XIEventMask::ButtonPress |
x11::Input::XIEventMask::ButtonRelease |
x11::Input::XIEventMask::Motion |
x11::Input::XIEventMask::TouchBegin |
x11::Input::XIEventMask::TouchUpdate |
x11::Input::XIEventMask::TouchEnd;
static_assert(sizeof(mask) == 4, "");
for (auto master_pointer :
ui::DeviceDataManagerX11::GetInstance()->master_pointers()) {
x11::Input::XIGrabDeviceRequest req{
.window = window,
.time = x11::Time::CurrentTime,
.cursor = cursor,
.deviceid = master_pointer,
.mode = x11::GrabMode::Async,
.paired_device_mode = x11::GrabMode::Async,
.owner_events = owner_events ? x11::Input::GrabOwner::Owner
: x11::Input::GrabOwner::NoOwner,
.mask = {base::ByteSwapToLE32(static_cast<uint32_t>(mask))},
};
if (auto reply = connection->xinput().XIGrabDevice(req).Sync())
result = reply->status;
// Assume that the grab will succeed on either all or none of the master
// pointers.
if (result != x11::GrabStatus::Success) {
// Try core pointer grab.
break;
}
}
}
if (result != x11::GrabStatus::Success) {
auto mask = x11::EventMask::PointerMotion | x11::EventMask::ButtonRelease |
x11::EventMask::ButtonPress;
x11::GrabPointerRequest req{
.owner_events = owner_events,
.grab_window = window,
.event_mask = mask,
.pointer_mode = x11::GrabMode::Async,
.keyboard_mode = x11::GrabMode::Async,
.confine_to = x11::Window::None,
.cursor = cursor,
.time = x11::Time::CurrentTime,
};
if (auto reply = connection->GrabPointer(req).Sync())
result = reply->status;
}
if (result == x11::GrabStatus::Success) {
g_grab_window = window;
g_owner_events = owner_events;
}
return result;
}
} // namespace
x11::GrabStatus GrabPointer(x11::Window window,
bool owner_events,
scoped_refptr<ui::X11Cursor> cursor) {
if (!cursor)
return GrabPointerImpl(window, owner_events, x11::Cursor::None);
if (cursor->loaded())
return GrabPointerImpl(window, owner_events, cursor->xcursor());
auto result = GrabPointerImpl(window, owner_events, x11::Cursor::None);
GetGrabCallback().Reset(base::BindOnce(base::IgnoreResult(GrabPointerImpl),
window, owner_events));
cursor->OnCursorLoaded(GetGrabCallback().callback());
return result;
}
void ChangeActivePointerGrabCursor(scoped_refptr<ui::X11Cursor> cursor) {
DCHECK(g_grab_window != x11::Window::None);
GrabPointer(g_grab_window, g_owner_events, cursor);
}
void UngrabPointer() {
GetGrabCallback().Cancel();
g_grab_window = x11::Window::None;
auto* connection = x11::Connection::Get();
if (ui::IsXInput2Available()) {
for (auto master_pointer :
ui::DeviceDataManagerX11::GetInstance()->master_pointers()) {
connection->xinput()
.XIUngrabDevice({x11::Time::CurrentTime, master_pointer})
.IgnoreError();
}
}
// Try core pointer ungrab in case the XInput2 pointer ungrab failed.
connection->UngrabPointer().IgnoreError();
}
} // namespace ui