blob: babf74031e1eaf213ce4ea0a6d1338487589697e [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <linux/input.h>
#include <wayland-server.h>
#include <memory>
#include "base/memory/raw_ptr.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/timer/timer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/devices/device_data_manager.h"
#include "ui/events/event.h"
#include "ui/ozone/platform/wayland/host/wayland_seat.h"
#include "ui/ozone/platform/wayland/test/mock_surface.h"
#include "ui/ozone/platform/wayland/test/test_keyboard.h"
#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
#include "ui/ozone/platform/wayland/test/wayland_test.h"
#if BUILDFLAG(USE_XKBCOMMON)
#include "base/memory/free_deleter.h"
#include "base/memory/platform_shared_memory_region.h"
#include "base/memory/shared_memory_mapping.h"
#include "base/memory/unsafe_shared_memory_region.h"
#include "ui/events/keycodes/scoped_xkb.h" // nogncheck
#endif
using ::testing::_;
using ::testing::Mock;
using ::testing::NotNull;
using ::testing::Values;
namespace ui {
class WaylandKeyboardTest : public WaylandTestSimple {
public:
void SetUp() override {
WaylandTestSimple::SetUp();
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
wl_seat_send_capabilities(server->seat()->resource(),
WL_SEAT_CAPABILITY_KEYBOARD);
});
ASSERT_TRUE(connection_->seat()->keyboard());
EXPECT_EQ(1u,
DeviceDataManager::GetInstance()->GetKeyboardDevices().size());
MaybeSetUpXkb();
}
protected:
// Enters the keyboard into the surface.
void SendEnter(int32_t rate, int32_t delay) {
PostToServerAndWait(
[rate, delay, surface_id = window_->root_surface()->get_surface_id()](
wl::TestWaylandServerThread* server) {
auto* const keyboard = server->seat()->keyboard()->resource();
auto* const surface =
server->GetObject<wl::MockSurface>(surface_id)->resource();
wl::ScopedWlArray empty({});
wl_keyboard_send_enter(keyboard, server->GetNextSerial(), surface,
empty.get());
wl_keyboard_send_repeat_info(keyboard, rate, delay);
});
}
void SendEnter() {
// Set "no repeat" so that events would be "squashed" with the modifiers,
// otherwise the behaviour is not determined and we cannot set stable
// expectations.
SendEnter(0, 0);
}
};
ACTION_P(CloneEvent, ptr) {
*ptr = arg0->Clone();
}
ACTION_P(AppendEvent, ptr) {
ptr->emplace_back(arg0->Clone());
}
ACTION_P(AppendEventAndQuitLoop, ptr, event_count, closure) {
ptr->emplace_back(arg0->Clone());
if (ptr->size() == event_count)
closure.Run();
}
TEST_F(WaylandKeyboardTest, Keypress) {
SendEnter();
std::unique_ptr<Event> event;
EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event));
PostToServerAndWait([surface_id = window_->root_surface()->get_surface_id()](
wl::TestWaylandServerThread* server) {
auto* const keyboard = server->seat()->keyboard()->resource();
auto* const surface =
server->GetObject<wl::MockSurface>(surface_id)->resource();
wl_keyboard_send_key(keyboard, server->GetNextSerial(),
server->GetNextTime(), 30 /* a */,
WL_KEYBOARD_KEY_STATE_PRESSED);
wl_keyboard_send_leave(keyboard, server->GetNextSerial(), surface);
});
ASSERT_TRUE(event);
ASSERT_TRUE(event->IsKeyEvent());
auto* key_event = event->AsKeyEvent();
EXPECT_EQ(ui::VKEY_A, key_event->key_code());
EXPECT_EQ(EventType::kKeyPressed, key_event->type());
// The window no longer has keyboard focus, and the below should not result in
// receiving more events. The expectation set in the beginning of the test
// should not over-saturate.
PostToServerAndWait([surface_id = window_->root_surface()->get_surface_id()](
wl::TestWaylandServerThread* server) {
auto* const keyboard = server->seat()->keyboard()->resource();
wl_keyboard_send_key(keyboard, server->GetNextSerial(),
server->GetNextTime(), 30 /* a */,
WL_KEYBOARD_KEY_STATE_PRESSED);
});
}
TEST_F(WaylandKeyboardTest, ControlShiftModifiers) {
SendEnter();
std::vector<std::unique_ptr<Event>> events;
EXPECT_CALL(delegate_, DispatchEvent(_))
.Times(3)
.WillRepeatedly(AppendEvent(&events));
PostToServerAndWait([surface_id = window_->root_surface()->get_surface_id()](
wl::TestWaylandServerThread* server) {
auto* const keyboard = server->seat()->keyboard()->resource();
// Pressing control.
wl_keyboard_send_key(keyboard, server->GetNextSerial(),
server->GetNextTime(), 29 /* Control */,
WL_KEYBOARD_KEY_STATE_PRESSED);
wl_keyboard_send_modifiers(keyboard, server->GetNextSerial(),
4 /* mods_depressed*/, 0 /* mods_latched */,
0 /* mods_locked */, 0 /* group */);
// Pressing shift (with control key still held down).
wl_keyboard_send_key(keyboard, server->GetNextSerial(),
server->GetNextTime(), 42 /* Shift */,
WL_KEYBOARD_KEY_STATE_PRESSED);
wl_keyboard_send_modifiers(keyboard, server->GetNextSerial(),
5 /* mods_depressed*/, 0 /* mods_latched */,
0 /* mods_locked */, 0 /* group */);
// Sending a reguard keypress, eg 'a'.
wl_keyboard_send_key(keyboard, server->GetNextSerial(),
server->GetNextTime(), 30 /* a */,
WL_KEYBOARD_KEY_STATE_PRESSED);
});
auto& last_event = events[events.size() - 1];
ASSERT_TRUE(last_event);
ASSERT_TRUE(last_event->IsKeyEvent());
auto* key_event = last_event->AsKeyEvent();
EXPECT_EQ(ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN, key_event->flags());
EXPECT_EQ(ui::VKEY_A, key_event->key_code());
EXPECT_EQ(EventType::kKeyPressed, key_event->type());
}
#if BUILDFLAG(USE_XKBCOMMON)
TEST_F(WaylandKeyboardTest, CapsLockModifier) {
SendEnter();
std::vector<std::unique_ptr<Event>> events;
EXPECT_CALL(delegate_, DispatchEvent(_))
.Times(3)
.WillRepeatedly(AppendEvent(&events));
PostToServerAndWait([surface_id = window_->root_surface()->get_surface_id()](
wl::TestWaylandServerThread* server) {
auto* const keyboard = server->seat()->keyboard()->resource();
// Pressing capslock (led ON).
wl_keyboard_send_key(keyboard, server->GetNextSerial(),
server->GetNextTime(), 58 /* Capslock */,
WL_KEYBOARD_KEY_STATE_PRESSED);
wl_keyboard_send_modifiers(keyboard, server->GetNextSerial(),
2 /* mods_depressed*/, 0 /* mods_latched */,
2 /* mods_locked */, 0 /* group */);
// Releasing capslock (led ON).
wl_keyboard_send_key(keyboard, server->GetNextSerial(),
server->GetNextTime(), 58 /* Capslock */,
WL_KEYBOARD_KEY_STATE_RELEASED);
wl_keyboard_send_modifiers(keyboard, server->GetNextSerial(),
0 /* mods_depressed*/, 0 /* mods_latched */,
2 /* mods_locked */, 0 /* group */);
// Sending a reguard keypress, eg 'a'.
wl_keyboard_send_key(keyboard, server->GetNextSerial(),
server->GetNextTime(), 30 /* a */,
WL_KEYBOARD_KEY_STATE_PRESSED);
});
auto& last_event = events[events.size() - 1];
ASSERT_TRUE(last_event);
ASSERT_TRUE(last_event->IsKeyEvent());
auto* key_event = last_event->AsKeyEvent();
EXPECT_EQ(ui::EF_CAPS_LOCK_ON, key_event->flags());
EXPECT_EQ(ui::VKEY_A, key_event->key_code());
EXPECT_EQ(EventType::kKeyPressed, key_event->type());
}
#endif
TEST_F(WaylandKeyboardTest, EventAutoRepeat) {
constexpr int32_t rate = 5; // num key events per second.
constexpr int32_t delay = 60; // in milliseconds.
SendEnter(rate, delay);
// Press the key and wait for two automatically repeated events to come.
base::RunLoop run_loop;
std::vector<std::unique_ptr<Event>> events;
EXPECT_CALL(delegate_, DispatchEvent(_))
.WillRepeatedly(
AppendEventAndQuitLoop(&events, 3u, run_loop.QuitClosure()));
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
auto* const keyboard = server->seat()->keyboard()->resource();
wl_keyboard_send_key(keyboard, server->GetNextSerial(),
server->GetNextTime(), 30 /* a */,
WL_KEYBOARD_KEY_STATE_PRESSED);
});
run_loop.Run();
Mock::VerifyAndClearExpectations(&delegate_);
// Release the key.
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
auto* const keyboard = server->seat()->keyboard()->resource();
wl_keyboard_send_key(keyboard, server->GetNextSerial(),
server->GetNextTime(), 30 /* a */,
WL_KEYBOARD_KEY_STATE_RELEASED);
});
ASSERT_EQ(events.size(), 3u);
const auto press_timestamp = events[0]->time_stamp();
auto check_repeat_event = [](const Event& event) {
EXPECT_EQ(EventType::kKeyPressed, event.type());
EXPECT_TRUE(event.flags() & EF_IS_REPEAT);
EXPECT_EQ(KeyboardCode::VKEY_A, event.AsKeyEvent()->key_code());
};
// The first key repeat event happens after |delay| milliseconds.
const auto& first_repeat_event = *events[1];
check_repeat_event(first_repeat_event);
const auto first_repeat_delay =
first_repeat_event.time_stamp() - press_timestamp;
EXPECT_EQ(first_repeat_delay.InMilliseconds(), delay);
// The next key event happens after 1/|rate| seconds.
const auto& second_repeat_event = *events[2];
check_repeat_event(second_repeat_event);
const auto second_repeat_delay =
second_repeat_event.time_stamp() - press_timestamp - first_repeat_delay;
EXPECT_EQ(second_repeat_delay.InMilliseconds(), 1000 / rate);
}
TEST_F(WaylandKeyboardTest, NoEventAutoRepeatOnLeave) {
constexpr int32_t rate = 5; // num key events per second.
constexpr int32_t delay = 60; // in milliseconds.
SendEnter(rate, delay);
// Press the key and wait for one automatically repeated event to come.
base::RunLoop run_loop;
std::vector<std::unique_ptr<Event>> events;
EXPECT_CALL(delegate_, DispatchEvent(_))
.WillRepeatedly(
AppendEventAndQuitLoop(&events, 2u, run_loop.QuitClosure()));
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
auto* const keyboard = server->seat()->keyboard()->resource();
wl_keyboard_send_key(keyboard, server->GetNextSerial(),
server->GetNextTime(), 30 /* a */,
WL_KEYBOARD_KEY_STATE_PRESSED);
});
run_loop.Run();
Mock::VerifyAndClearExpectations(&delegate_);
// Then leave.
PostToServerAndWait([surface_id = window_->root_surface()->get_surface_id()](
wl::TestWaylandServerThread* server) {
auto* const keyboard = server->seat()->keyboard()->resource();
auto* const surface =
server->GetObject<wl::MockSurface>(surface_id)->resource();
wl_keyboard_send_leave(keyboard, server->GetNextSerial(), surface);
});
// Check the first key repeating event.
const auto& first_repeat_event = *events[1];
EXPECT_EQ(EventType::kKeyPressed, first_repeat_event.type());
EXPECT_TRUE(first_repeat_event.flags() & EF_IS_REPEAT);
EXPECT_EQ(KeyboardCode::VKEY_A, first_repeat_event.AsKeyEvent()->key_code());
// The window no longer has keyboard focus, and the below should not result in
// receiving more events.
EXPECT_CALL(delegate_, DispatchEvent(NotNull())).Times(0);
task_environment_.FastForwardBy(base::Milliseconds(1000));
Mock::VerifyAndClearExpectations(&delegate_);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
auto* const keyboard = server->seat()->keyboard()->resource();
wl_keyboard_send_key(keyboard, server->GetNextSerial(),
server->GetNextTime(), 30 /* a */,
WL_KEYBOARD_KEY_STATE_RELEASED);
});
}
// This test verifies the following scenario:
//
// 1/ press and hold a modifier key (in this case SHIFT, ALT, CTRL);
// 2/ ensure that no auto-repeat gets triggered;
// 3/ with the modifier key still pressed, press another
// key (in this case 'a');
// 4/ ensure that they auto-repeat logic gets started and
// the modifier key is properly handled as part of the
// event construction.
TEST_F(WaylandKeyboardTest, NoEventAutoRepeatForModifiers) {
constexpr int32_t rate = 5; // num key events per second.
constexpr int32_t delay = 60; // in milliseconds.
SendEnter(rate, delay);
const struct {
int evdev_key;
KeyboardCode key_code;
int mods_depressed;
int modifier;
} kIncomeModifiersAndExpectedResults[] = {
{42 /*shift*/, VKEY_SHIFT, 1, EF_SHIFT_DOWN},
{29 /*ctrl*/, VKEY_CONTROL, 4, EF_CONTROL_DOWN},
{56 /*alt*/, VKEY_MENU, 8, EF_ALT_DOWN},
};
for (auto values : kIncomeModifiersAndExpectedResults) {
// Press a modifier key and wait 1s, to ensure no repeated events come.
base::RunLoop run_loop;
std::vector<std::unique_ptr<Event>> events;
EXPECT_CALL(delegate_, DispatchEvent(_))
.WillRepeatedly(AppendEvent(&events));
PostToServerAndWait([&values](wl::TestWaylandServerThread* server) {
auto* const keyboard = server->seat()->keyboard()->resource();
wl_keyboard_send_key(keyboard, server->GetNextSerial(),
server->GetNextTime(), values.evdev_key,
WL_KEYBOARD_KEY_STATE_PRESSED);
wl_keyboard_send_modifiers(keyboard, server->GetNextSerial(),
values.mods_depressed /* mods_depressed*/,
0 /* mods_latched */, 0 /* mods_locked */,
0 /* group */);
});
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(1000));
run_loop.Run();
Mock::VerifyAndClearExpectations(&delegate_);
// Ensure that only the modifier key down event is processed, ie no auto
// repeat is triggered.
ASSERT_EQ(events.size(), 1u);
EXPECT_EQ(events[0]->AsKeyEvent()->key_code(), values.key_code);
// With the modifier key still held, press another key ('a' in this case).
std::vector<std::unique_ptr<Event>> events2;
base::RunLoop run_loop2;
EXPECT_CALL(delegate_, DispatchEvent(_))
.WillRepeatedly(
AppendEventAndQuitLoop(&events2, 5u, run_loop2.QuitClosure()));
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
auto* const keyboard = server->seat()->keyboard()->resource();
wl_keyboard_send_key(keyboard, server->GetNextSerial(),
server->GetNextTime(), 30 /* a */,
WL_KEYBOARD_KEY_STATE_PRESSED);
});
run_loop2.Run();
// Ensure that 4 consecutive auto-repeat key press events are dispatched,
// with the proper modifier key.
auto check_repeat_event = [&values](const Event& event) {
EXPECT_EQ(EventType::kKeyPressed, event.type());
EXPECT_TRUE(event.flags() & (EF_IS_REPEAT | values.modifier));
EXPECT_EQ(KeyboardCode::VKEY_A, event.AsKeyEvent()->key_code());
};
for (size_t i = 1; i < events2.size(); i++) {
const auto& repeat_event = *events2[i];
check_repeat_event(repeat_event);
}
// Release the keys.
Mock::VerifyAndClearExpectations(&delegate_);
PostToServerAndWait([&values](wl::TestWaylandServerThread* server) {
auto* const keyboard = server->seat()->keyboard()->resource();
wl_keyboard_send_key(keyboard, server->GetNextSerial(),
server->GetNextTime(), values.evdev_key,
WL_KEYBOARD_KEY_STATE_RELEASED);
wl_keyboard_send_key(keyboard, server->GetNextSerial(),
server->GetNextTime(), 30 /* a */,
WL_KEYBOARD_KEY_STATE_RELEASED);
});
}
}
} // namespace ui