Композитор Wayland на smithay: сессия, xdg-shell, ввод, рендеринг через OpenGL ES с отслеживанием повреждений. Протоколы xdg-decoration, viewporter и presentation-time. Окна в стиле Windows 11: заголовок с названием и кнопками, перетаскивание, изменение размера за края и углы, сворачивание и разворачивание. Панель задач с подписями окон, часами и переключателем рабочих областей. Меню «Пуск» со списком приложений и поиском в текущей раскладке. Переключение окон по Alt+Tab с подтверждением при отпускании Alt. Собственный растровщик текста на fontdb + fontdue с гамма-коррекцией покрытия: без хинтинга штрихи на мелком кегле выходят рваными. Приложения запускаются в отдельной сессии D-Bus, иначе однопользовательские программы находят уже запущенный экземпляр во внешней сессии и открывают окно не в Lark. 127 тестов, clippy без замечаний.
102 lines
4.7 KiB
Rust
102 lines
4.7 KiB
Rust
//! Тесты обработки ввода на бэкенде winit.
|
||
//!
|
||
//! Изначально композитор не собирался с ошибкой
|
||
//! `no method named key_code found for struct WinitKeyboardInputEvent`:
|
||
//! трейт `KeyboardKeyEvent` не был импортирован. Такие ошибки дешевле ловить
|
||
//! тестом, чем запуском композитора, поэтому здесь проверяется, что трейты
|
||
//! событий ввода smithay доступны и что их методы вызываются корректно.
|
||
|
||
use smithay::backend::input::{
|
||
AbsolutePositionEvent, Axis, Event, InputBackend, InputEvent, Keycode, KeyboardKeyEvent,
|
||
KeyState, PointerAxisEvent, PointerButtonEvent, PointerMotionAbsoluteEvent,
|
||
};
|
||
use smithay::backend::winit::{self, WinitInput};
|
||
|
||
/// Проверяет, что трейты событий ввода реализованы для типов бэкенда winit.
|
||
///
|
||
/// Ошибка `no method named key_code` возникает именно тогда, когда трейт не
|
||
/// импортирован, поэтому проверка идёт через обобщённые сигнатуры.
|
||
#[test]
|
||
fn input_event_traits_are_available() {
|
||
fn assert_keyboard<E: KeyboardKeyEvent<WinitInput>>() {}
|
||
fn assert_button<E: PointerButtonEvent<WinitInput>>() {}
|
||
fn assert_axis<E: PointerAxisEvent<WinitInput>>() {}
|
||
fn assert_motion<E: PointerMotionAbsoluteEvent<WinitInput>>() {}
|
||
|
||
assert_keyboard::<winit::WinitKeyboardInputEvent>();
|
||
assert_button::<winit::WinitMouseInputEvent>();
|
||
assert_axis::<winit::WinitMouseWheelEvent>();
|
||
assert_motion::<winit::WinitMouseMovedEvent>();
|
||
}
|
||
|
||
/// Проверяет, что из события клавиатуры извлекаются код, состояние и время.
|
||
///
|
||
/// Именно эти три значения композитор передаёт в `KeyboardHandle::input`.
|
||
#[test]
|
||
fn keyboard_event_exposes_code_state_and_time() {
|
||
fn read<E: KeyboardKeyEvent<WinitInput>>(event: &E) -> (Keycode, KeyState) {
|
||
(event.key_code(), event.state())
|
||
}
|
||
|
||
// Вызов компилируется, только если все методы трейта доступны.
|
||
let _ = read::<winit::WinitKeyboardInputEvent>;
|
||
}
|
||
|
||
/// Проверяет, что события указателя дают доступ к координатам, кнопкам и
|
||
/// осям прокрутки.
|
||
#[test]
|
||
fn pointer_events_expose_expected_fields() {
|
||
fn read_motion<E: PointerMotionAbsoluteEvent<WinitInput>>(event: &E) -> (f64, f64) {
|
||
(event.x(), event.y())
|
||
}
|
||
|
||
fn read_button<E: PointerButtonEvent<WinitInput>>(
|
||
event: &E,
|
||
) -> (u32, smithay::backend::input::ButtonState) {
|
||
(event.button_code(), event.state())
|
||
}
|
||
|
||
fn read_axis<E: PointerAxisEvent<WinitInput>>(event: &E) {
|
||
let _ = event.source();
|
||
let _ = event.relative_direction(Axis::Vertical);
|
||
let _ = event.amount_v120(Axis::Horizontal);
|
||
}
|
||
|
||
let _ = read_motion::<winit::WinitMouseMovedEvent>;
|
||
let _ = read_button::<winit::WinitMouseInputEvent>;
|
||
let _ = read_axis::<winit::WinitMouseWheelEvent>;
|
||
}
|
||
|
||
/// Проверяет разбор всех событий ввода, которые обрабатывает композитор.
|
||
///
|
||
/// Это защищает от ситуации, когда в `match` добавлена ветка для события,
|
||
/// доступ к полям которого требует неимпортированного трейта.
|
||
#[test]
|
||
fn compositor_handles_all_input_variants() {
|
||
fn route<B: InputBackend>(event: InputEvent<B>) {
|
||
match event {
|
||
InputEvent::Keyboard { event } => {
|
||
let code: Keycode = event.key_code();
|
||
let state: KeyState = event.state();
|
||
let _ = (code, state, event.time());
|
||
}
|
||
InputEvent::PointerMotionAbsolute { event } => {
|
||
let _ = (event.x(), event.y());
|
||
let _ = event.position_transformed((100, 100).into());
|
||
}
|
||
InputEvent::PointerButton { event } => {
|
||
let _ = (event.button_code(), event.state());
|
||
}
|
||
InputEvent::PointerAxis { event } => {
|
||
let _ = event.source();
|
||
let _ = event.amount(Axis::Vertical);
|
||
let _ = event.amount_v120(Axis::Vertical);
|
||
let _ = event.relative_direction(Axis::Vertical);
|
||
}
|
||
_ => {}
|
||
}
|
||
}
|
||
|
||
// Проверка на этапе компиляции для бэкенда winit.
|
||
let _ = route::<WinitInput>;
|
||
}
|