blob: 01e24c6f98aecb2541c8adc225b03a4d7ab745ea [file] [log] [blame]
// Copyright 2020 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/aura/test/ui_controls_aurax11.h"
#include "ui/gfx/x/connection.h"
#include "ui/gfx/x/keysyms/keysyms.h"
#include "ui/gfx/x/xproto.h"
namespace aura {
namespace test {
namespace {
using ui_controls::DOWN;
using ui_controls::LEFT;
using ui_controls::MIDDLE;
using ui_controls::MouseButton;
using ui_controls::RIGHT;
using ui_controls::UIControlsAura;
using ui_controls::UP;
// Mask of the buttons currently down.
unsigned button_down_mask = 0;
} // namespace
UIControlsX11::UIControlsX11(WindowTreeHost* host) : host_(host) {}
UIControlsX11::~UIControlsX11() = default;
bool UIControlsX11::SendKeyPress(gfx::NativeWindow window,
ui::KeyboardCode key,
bool control,
bool shift,
bool alt,
bool command) {
return SendKeyPressNotifyWhenDone(window, key, control, shift, alt, command,
base::OnceClosure());
}
bool UIControlsX11::SendKeyPressNotifyWhenDone(gfx::NativeWindow window,
ui::KeyboardCode key,
bool control,
bool shift,
bool alt,
bool command,
base::OnceClosure closure) {
x11::KeyEvent xevent;
xevent.detail = {};
xevent.opcode = x11::KeyEvent::Press;
if (control)
SetKeycodeAndSendThenMask(&xevent, XK_Control_L, x11::KeyButMask::Control);
if (shift)
SetKeycodeAndSendThenMask(&xevent, XK_Shift_L, x11::KeyButMask::Shift);
if (alt)
SetKeycodeAndSendThenMask(&xevent, XK_Alt_L, x11::KeyButMask::Mod1);
if (command)
SetKeycodeAndSendThenMask(&xevent, XK_Meta_L, x11::KeyButMask::Mod4);
xevent.detail = x11::Connection::Get()->KeysymToKeycode(
ui::XKeysymForWindowsKeyCode(key, shift));
PostEventToWindowTreeHost(host_, &xevent);
// Send key release events.
xevent.opcode = x11::KeyEvent::Release;
PostEventToWindowTreeHost(host_, &xevent);
if (alt)
UnmaskAndSetKeycodeThenSend(&xevent, x11::KeyButMask::Mod1, XK_Alt_L);
if (shift)
UnmaskAndSetKeycodeThenSend(&xevent, x11::KeyButMask::Shift, XK_Shift_L);
if (control)
UnmaskAndSetKeycodeThenSend(&xevent, x11::KeyButMask::Control,
XK_Control_L);
if (command)
UnmaskAndSetKeycodeThenSend(&xevent, x11::KeyButMask::Mod4, XK_Meta_L);
DCHECK_EQ(xevent.state, x11::KeyButMask{});
RunClosureAfterAllPendingUIEvents(std::move(closure));
return true;
}
bool UIControlsX11::SendMouseMove(int screen_x, int screen_y) {
return SendMouseMoveNotifyWhenDone(screen_x, screen_y, base::OnceClosure());
}
bool UIControlsX11::SendMouseMoveNotifyWhenDone(int screen_x,
int screen_y,
base::OnceClosure closure) {
gfx::Point root_location(screen_x, screen_y);
aura::client::ScreenPositionClient* screen_position_client =
aura::client::GetScreenPositionClient(host_->window());
if (screen_position_client) {
screen_position_client->ConvertPointFromScreen(host_->window(),
&root_location);
}
gfx::Point root_current_location =
QueryLatestMousePositionRequestInHost(host_);
host_->ConvertPixelsToDIP(&root_current_location);
if (root_location != root_current_location && button_down_mask == 0) {
// Move the cursor because EnterNotify/LeaveNotify are generated with the
// current mouse position as a result of XGrabPointer()
host_->window()->MoveCursorTo(root_location);
} else {
x11::MotionNotifyEvent xevent{
.event_x = root_location.x(),
.event_y = root_location.y(),
.state = static_cast<x11::KeyButMask>(button_down_mask),
.same_screen = true,
};
// WindowTreeHost will take care of other necessary fields.
PostEventToWindowTreeHost(host_, &xevent);
}
RunClosureAfterAllPendingUIEvents(std::move(closure));
return true;
}
bool UIControlsX11::SendMouseEvents(MouseButton type,
int button_state,
int accelerator_state) {
return SendMouseEventsNotifyWhenDone(type, button_state, base::OnceClosure(),
accelerator_state);
}
bool UIControlsX11::SendMouseEventsNotifyWhenDone(MouseButton type,
int button_state,
base::OnceClosure closure,
int accelerator_state) {
x11::ButtonEvent xevent;
gfx::Point mouse_loc = Env::GetInstance()->last_mouse_location();
aura::client::ScreenPositionClient* screen_position_client =
aura::client::GetScreenPositionClient(host_->window());
if (screen_position_client) {
screen_position_client->ConvertPointFromScreen(host_->window(), &mouse_loc);
}
xevent.event_x = mouse_loc.x();
xevent.event_y = mouse_loc.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;
// WindowEventDispatcher will take care of other necessary fields.
if (button_state & DOWN) {
xevent.opcode = x11::ButtonEvent::Press;
PostEventToWindowTreeHost(host_, &xevent);
button_down_mask |= static_cast<int>(xevent.state);
}
if (button_state & UP) {
xevent.opcode = x11::ButtonEvent::Release;
PostEventToWindowTreeHost(host_, &xevent);
int state = static_cast<int>(xevent.state);
button_down_mask = (button_down_mask | state) ^ state;
}
RunClosureAfterAllPendingUIEvents(std::move(closure));
return true;
}
bool UIControlsX11::SendMouseClick(MouseButton type) {
return SendMouseEvents(type, UP | DOWN, ui_controls::kNoAccelerator);
}
void UIControlsX11::RunClosureAfterAllPendingUIEvents(
base::OnceClosure closure) {
if (closure.is_null())
return;
ui::XEventWaiter::Create(
static_cast<x11::Window>(host_->GetAcceleratedWidget()),
std::move(closure));
}
void UIControlsX11::SetKeycodeAndSendThenMask(x11::KeyEvent* xevent,
uint32_t keysym,
x11::KeyButMask mask) {
xevent->detail = x11::Connection::Get()->KeysymToKeycode(keysym);
PostEventToWindowTreeHost(host_, xevent);
xevent->state = xevent->state | mask;
}
void UIControlsX11::UnmaskAndSetKeycodeThenSend(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(host_, xevent);
}
} // namespace test
} // namespace aura