blob: c9e583ee872865b61e1b8b7c6e317dced74eaa68 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/base/x/test/x11_ui_controls_test_helper.h"
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "ui/base/x/x11_util.h"
#include "ui/events/keycodes/keyboard_code_conversion_x.h"
#include "ui/events/test/x11_event_waiter.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/x/keysyms/keysyms.h"
#include "ui/gfx/x/xproto.h"
namespace ui {
namespace {
using ui_controls::DOWN;
using ui_controls::LEFT;
using ui_controls::MIDDLE;
using ui_controls::MouseButton;
using ui_controls::RIGHT;
using ui_controls::UP;
// Mask of the buttons currently down.
unsigned button_down_mask = 0;
// The root and time fields of |xevent| may be modified.
template <typename T>
void PostEventToWindowTreeHost(gfx::AcceleratedWidget widget, T* xevent) {
x11::Window xwindow = static_cast<x11::Window>(widget);
xevent->event = xwindow;
xevent->root = x11::Connection::Get()->default_root();
xevent->time = x11::Time::CurrentTime;
x11::Connection::Get()->SendEvent(*xevent, xwindow, x11::EventMask::NoEvent);
x11::Connection::Get()->Flush();
}
} // namespace
X11UIControlsTestHelper::X11UIControlsTestHelper()
: connection_(*x11::Connection::Get()),
x_root_window_(ui::GetX11RootWindow()),
x_window_(connection_->CreateDummyWindow(
"Chromium X11UIControlsTestHelper Window")) {}
X11UIControlsTestHelper::~X11UIControlsTestHelper() {
connection_->DestroyWindow({x_window_});
}
unsigned X11UIControlsTestHelper::ButtonDownMask() const {
return button_down_mask;
}
void X11UIControlsTestHelper::SendKeyEvents(gfx::AcceleratedWidget widget,
ui::KeyboardCode key,
int key_event_types,
int accelerator_state,
base::OnceClosure closure) {
bool press = key_event_types & ui_controls::kKeyPress;
bool release = key_event_types & ui_controls::kKeyRelease;
bool control = accelerator_state & ui_controls::kControl;
bool shift = accelerator_state & ui_controls::kShift;
bool alt = accelerator_state & ui_controls::kAlt;
x11::KeyEvent xevent;
// Send key press events.
if (press) {
xevent.opcode = x11::KeyEvent::Press;
if (control) {
SetKeycodeAndSendThenMask(widget, &xevent, XK_Control_L,
x11::KeyButMask::Control);
}
if (shift) {
SetKeycodeAndSendThenMask(widget, &xevent, XK_Shift_L,
x11::KeyButMask::Shift);
}
if (alt) {
SetKeycodeAndSendThenMask(widget, &xevent, XK_Alt_L,
x11::KeyButMask::Mod1);
}
xevent.detail = x11::Connection::Get()->KeysymToKeycode(
ui::XKeysymForWindowsKeyCode(key, shift));
PostEventToWindowTreeHost(widget, &xevent);
}
// Send key release events.
if (release) {
xevent.opcode = x11::KeyEvent::Release;
PostEventToWindowTreeHost(widget, &xevent);
if (alt) {
UnmaskAndSetKeycodeThenSend(widget, &xevent, x11::KeyButMask::Mod1,
XK_Alt_L);
}
if (shift) {
UnmaskAndSetKeycodeThenSend(widget, &xevent, x11::KeyButMask::Shift,
XK_Shift_L);
}
if (control) {
UnmaskAndSetKeycodeThenSend(widget, &xevent, x11::KeyButMask::Control,
XK_Control_L);
}
}
RunClosureAfterAllPendingUIEvents(std::move(closure));
return;
}
void X11UIControlsTestHelper::SendMouseMotionNotifyEvent(
gfx::AcceleratedWidget widget,
const gfx::Point& mouse_loc,
const gfx::Point& mouse_loc_in_connection_px,
base::OnceClosure closure) {
x11::MotionNotifyEvent xevent{
.root_x = static_cast<int16_t>(mouse_loc_in_connection_px.x()),
.root_y = static_cast<int16_t>(mouse_loc_in_connection_px.y()),
.event_x = static_cast<int16_t>(mouse_loc.x()),
.event_y = static_cast<int16_t>(mouse_loc.y()),
.state = static_cast<x11::KeyButMask>(button_down_mask),
.same_screen = true,
};
// RootWindow will take care of other necessary fields.
PostEventToWindowTreeHost(widget, &xevent);
RunClosureAfterAllPendingUIEvents(std::move(closure));
return;
}
void X11UIControlsTestHelper::SendMouseEvent(
gfx::AcceleratedWidget widget,
MouseButton type,
int button_state,
int accelerator_state,
const gfx::Point& mouse_loc,
const gfx::Point& mouse_loc_in_connection_px,
base::OnceClosure closure) {
x11::ButtonEvent xevent;
xevent.event_x = mouse_loc.x();
xevent.event_y = mouse_loc.y();
xevent.root_x = mouse_loc_in_connection_px.x();
xevent.root_y = mouse_loc_in_connection_px.y();
switch (type) {
case LEFT:
xevent.detail = static_cast<x11::Button>(1);
xevent.state = x11::KeyButMask::Button1;
break;
case MIDDLE:
xevent.detail = static_cast<x11::Button>(2);
xevent.state = x11::KeyButMask::Button2;
break;
case RIGHT:
xevent.detail = static_cast<x11::Button>(3);
xevent.state = x11::KeyButMask::Button3;
break;
}
// Process accelerator key state.
if (accelerator_state & ui_controls::kShift)
xevent.state = xevent.state | x11::KeyButMask::Shift;
if (accelerator_state & ui_controls::kControl)
xevent.state = xevent.state | x11::KeyButMask::Control;
if (accelerator_state & ui_controls::kAlt)
xevent.state = xevent.state | x11::KeyButMask::Mod1;
if (accelerator_state & ui_controls::kCommand)
xevent.state = xevent.state | x11::KeyButMask::Mod4;
// RootWindow will take care of other necessary fields.
if (button_state & DOWN) {
xevent.opcode = x11::ButtonEvent::Press;
PostEventToWindowTreeHost(widget, &xevent);
button_down_mask |= static_cast<int>(xevent.state);
}
if (button_state & UP) {
xevent.opcode = x11::ButtonEvent::Release;
PostEventToWindowTreeHost(widget, &xevent);
int state = static_cast<int>(xevent.state);
button_down_mask = (button_down_mask | state) ^ state;
}
RunClosureAfterAllPendingUIEvents(std::move(closure));
return;
}
void X11UIControlsTestHelper::RunClosureAfterAllPendingUIEvents(
base::OnceClosure closure) {
if (closure.is_null())
return;
ui::XEventWaiter::Create(x_window_, std::move(closure));
}
void X11UIControlsTestHelper::SetKeycodeAndSendThenMask(
gfx::AcceleratedWidget widget,
x11::KeyEvent* xevent,
uint32_t keysym,
x11::KeyButMask mask) {
xevent->detail = x11::Connection::Get()->KeysymToKeycode(keysym);
PostEventToWindowTreeHost(widget, xevent);
xevent->state = xevent->state | mask;
}
void X11UIControlsTestHelper::UnmaskAndSetKeycodeThenSend(
gfx::AcceleratedWidget widget,
x11::KeyEvent* xevent,
x11::KeyButMask mask,
uint32_t keysym) {
xevent->state = static_cast<x11::KeyButMask>(
static_cast<uint32_t>(xevent->state) ^ static_cast<uint32_t>(mask));
xevent->detail = x11::Connection::Get()->KeysymToKeycode(keysym);
PostEventToWindowTreeHost(widget, xevent);
}
} // namespace ui