blob: 96216a5ecb7227c0bd027645a0eb97dd6aed805e [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 "ui/base/x/x11_util.h"
#include "ui/events/devices/x11/device_data_manager_x11.h"
#include "ui/gfx/x/x11.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;
}
int GrabPointerImpl(x11::Window window, bool owner_events, x11::Cursor cursor) {
GetGrabCallback().Cancel();
int result = GrabInvalidTime;
if (ui::IsXInput2Available()) {
// Do an XInput2 pointer grab. If there is an active XInput2 pointer grab
// as a result of normal button press, XGrabPointer() will fail.
unsigned char mask[XIMaskLen(XI_LASTEVENT)];
memset(mask, 0, sizeof(mask));
XISetMask(mask, XI_ButtonPress);
XISetMask(mask, XI_ButtonRelease);
XISetMask(mask, XI_Motion);
XISetMask(mask, XI_TouchBegin);
XISetMask(mask, XI_TouchUpdate);
XISetMask(mask, XI_TouchEnd);
XIEventMask evmask;
evmask.mask_len = sizeof(mask);
evmask.mask = mask;
const std::vector<int>& master_pointers =
ui::DeviceDataManagerX11::GetInstance()->master_pointers();
for (int master_pointer : master_pointers) {
evmask.deviceid = master_pointer;
result = XIGrabDevice(gfx::GetXDisplay(), master_pointer,
static_cast<uint32_t>(window), x11::CurrentTime,
static_cast<uint32_t>(cursor), GrabModeAsync,
GrabModeAsync, owner_events, &evmask);
// Assume that the grab will succeed on either all or none of the master
// pointers.
if (result != GrabSuccess) {
// Try core pointer grab.
break;
}
}
}
if (result != GrabSuccess) {
int event_mask = PointerMotionMask | ButtonReleaseMask | ButtonPressMask;
result = XGrabPointer(gfx::GetXDisplay(), static_cast<uint32_t>(window),
owner_events, event_mask, GrabModeAsync,
GrabModeAsync, x11::None,
static_cast<uint32_t>(cursor), x11::CurrentTime);
}
if (result == GrabSuccess) {
g_grab_window = window;
g_owner_events = owner_events;
}
return result;
}
} // namespace
int 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());
int 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;
if (ui::IsXInput2Available()) {
const std::vector<int>& master_pointers =
ui::DeviceDataManagerX11::GetInstance()->master_pointers();
for (int master_pointer : master_pointers)
XIUngrabDevice(gfx::GetXDisplay(), master_pointer, x11::CurrentTime);
}
// Try core pointer ungrab in case the XInput2 pointer ungrab failed.
XUngrabPointer(gfx::GetXDisplay(), x11::CurrentTime);
}
} // namespace ui