mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
* Extract CEF rendered UI into a separate process and crate * Review * Review * Review * Review * Remove necessary workarounds * Block on frame copy ack * Crop and resample frames correctly * Skip blank frames * Fix deps * Fix fmt * Fix clippy warning * Review * Fix todo
252 lines
7.7 KiB
Rust
252 lines
7.7 KiB
Rust
use cef::sys::{cef_key_event_type_t, cef_mouse_button_type_t};
|
|
use cef::{Browser, ImplBrowser, ImplBrowserHost, KeyEvent, MouseEvent};
|
|
use winit::event::{ButtonSource, ElementState, MouseButton, MouseScrollDelta, WindowEvent};
|
|
|
|
mod keymap;
|
|
use keymap::{ToCharRepresentation, ToNativeKeycode, ToVKBits};
|
|
|
|
mod state;
|
|
pub(crate) use state::{CefModifiers, InputState};
|
|
|
|
use super::consts::{PINCH_ZOOM_SPEED, SCROLL_LINE_HEIGHT, SCROLL_LINE_WIDTH, SCROLL_SPEED_X, SCROLL_SPEED_Y};
|
|
|
|
/// A window input translated into the plain data CEF consumes — no winit types, so it can
|
|
/// be applied to a browser living in another process.
|
|
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
|
pub(crate) enum InputEvent {
|
|
MouseMove { data: MouseData, leave: bool },
|
|
MouseClick { data: MouseData, button: MouseButtonKind, up: bool, click_count: i32 },
|
|
MouseWheel { data: MouseData, delta_x: i32, delta_y: i32 },
|
|
Key(KeyData),
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
|
|
pub(crate) struct MouseData {
|
|
pub(crate) x: i32,
|
|
pub(crate) y: i32,
|
|
pub(crate) modifiers: u32,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
|
pub(crate) enum MouseButtonKind {
|
|
Left,
|
|
Right,
|
|
Middle,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
|
pub(crate) enum KeyEventKind {
|
|
RawKeyDown,
|
|
KeyUp,
|
|
Char,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
|
|
pub(crate) struct KeyData {
|
|
pub(crate) kind: KeyEventKind,
|
|
pub(crate) modifiers: u32,
|
|
pub(crate) windows_key_code: i32,
|
|
pub(crate) native_key_code: i32,
|
|
pub(crate) character: u16,
|
|
pub(crate) unmodified_character: u16,
|
|
}
|
|
|
|
/// Turns a winit event into zero or more [`InputEvent`]s, updating the tracked input state
|
|
/// (cursor position, click counting, modifiers) along the way.
|
|
pub(crate) fn translate(input_state: &mut InputState, event: &WindowEvent) -> Vec<InputEvent> {
|
|
match event {
|
|
WindowEvent::PointerMoved { position, .. } => {
|
|
if !input_state.cursor_move(position) {
|
|
return Vec::new();
|
|
}
|
|
vec![InputEvent::MouseMove {
|
|
data: input_state.mouse_data(),
|
|
leave: false,
|
|
}]
|
|
}
|
|
WindowEvent::PointerEntered { position, .. } => {
|
|
let _ = input_state.cursor_move(position);
|
|
vec![InputEvent::MouseMove {
|
|
data: input_state.mouse_data(),
|
|
leave: false,
|
|
}]
|
|
}
|
|
WindowEvent::PointerLeft { position, .. } => {
|
|
if let Some(position) = position {
|
|
let _ = input_state.cursor_move(position);
|
|
}
|
|
vec![InputEvent::MouseMove {
|
|
data: input_state.mouse_data(),
|
|
leave: true,
|
|
}]
|
|
}
|
|
WindowEvent::PointerButton { state, button, position, .. } => {
|
|
let mouse_button = match button {
|
|
ButtonSource::Mouse(mouse_button) => mouse_button,
|
|
_ => {
|
|
return Vec::new(); // TODO: Handle touch input
|
|
}
|
|
};
|
|
|
|
let _ = input_state.cursor_move(position);
|
|
let click_count = input_state.mouse_input(mouse_button, state).into();
|
|
let up = matches!(state, ElementState::Released);
|
|
let button = match mouse_button {
|
|
MouseButton::Left => MouseButtonKind::Left,
|
|
MouseButton::Right => MouseButtonKind::Right,
|
|
MouseButton::Middle => MouseButtonKind::Middle,
|
|
_ => return Vec::new(),
|
|
};
|
|
|
|
vec![InputEvent::MouseClick {
|
|
data: input_state.mouse_data(),
|
|
button,
|
|
up,
|
|
click_count,
|
|
}]
|
|
}
|
|
WindowEvent::MouseWheel { delta, phase: _, device_id: _, .. } => {
|
|
let (mut delta_x, mut delta_y) = match delta {
|
|
MouseScrollDelta::LineDelta(x, y) => (x * SCROLL_LINE_WIDTH as f32, y * SCROLL_LINE_HEIGHT as f32),
|
|
MouseScrollDelta::PixelDelta(physical_position) => (physical_position.x as f32, physical_position.y as f32),
|
|
};
|
|
delta_x *= SCROLL_SPEED_X;
|
|
delta_y *= SCROLL_SPEED_Y;
|
|
|
|
vec![InputEvent::MouseWheel {
|
|
data: input_state.mouse_data(),
|
|
delta_x: delta_x as i32,
|
|
delta_y: delta_y as i32,
|
|
}]
|
|
}
|
|
WindowEvent::ModifiersChanged(modifiers) => {
|
|
input_state.modifiers_changed(&modifiers.state());
|
|
Vec::new()
|
|
}
|
|
WindowEvent::KeyboardInput { device_id: _, event, is_synthetic: _ } => {
|
|
input_state.modifiers_apply_key_event(&event.logical_key, &event.state);
|
|
|
|
let mut kind = match (event.state, &event.logical_key) {
|
|
(ElementState::Pressed, winit::keyboard::Key::Character(_)) => KeyEventKind::Char,
|
|
(ElementState::Pressed, _) => KeyEventKind::RawKeyDown,
|
|
(ElementState::Released, _) => KeyEventKind::KeyUp,
|
|
};
|
|
|
|
let modifiers = input_state.cef_modifiers(&event.location, event.repeat).into();
|
|
|
|
let windows_key_code = match &event.logical_key {
|
|
winit::keyboard::Key::Named(named) => named.to_vk_bits(),
|
|
winit::keyboard::Key::Character(char) => char.chars().next().unwrap_or_default().to_vk_bits(),
|
|
_ => 0,
|
|
};
|
|
|
|
let native_key_code = event.physical_key.to_native_keycode();
|
|
|
|
let char_representation = event.logical_key.to_char_representation();
|
|
#[allow(unused_mut)]
|
|
let mut character = char_representation as u16;
|
|
|
|
if event.state == ElementState::Pressed && character != 0 {
|
|
kind = KeyEventKind::Char;
|
|
}
|
|
|
|
let unmodified_character = event.key_without_modifiers.to_char_representation() as u16;
|
|
|
|
#[cfg(target_os = "macos")] // See https://www.magpcss.org/ceforum/viewtopic.php?start=10&t=11650
|
|
if character == 0 && unmodified_character == 0 && event.text_with_all_modifiers.is_some() {
|
|
character = 1;
|
|
}
|
|
|
|
let key = KeyData {
|
|
kind,
|
|
modifiers,
|
|
windows_key_code,
|
|
native_key_code,
|
|
character,
|
|
unmodified_character,
|
|
};
|
|
|
|
if kind == KeyEventKind::Char {
|
|
// CEF expects a raw key-down before the character event it produces.
|
|
vec![
|
|
InputEvent::Key(KeyData {
|
|
kind: KeyEventKind::RawKeyDown,
|
|
..key
|
|
}),
|
|
InputEvent::Key(KeyData {
|
|
windows_key_code: char_representation as i32,
|
|
..key
|
|
}),
|
|
]
|
|
} else {
|
|
vec![InputEvent::Key(key)]
|
|
}
|
|
}
|
|
WindowEvent::PinchGesture { delta, .. } => {
|
|
if !delta.is_normal() {
|
|
return Vec::new();
|
|
}
|
|
|
|
let data = MouseData {
|
|
modifiers: CefModifiers::PINCH_MODIFIERS.into(),
|
|
..input_state.mouse_data()
|
|
};
|
|
|
|
vec![InputEvent::MouseWheel {
|
|
data,
|
|
delta_x: 0,
|
|
delta_y: (delta * PINCH_ZOOM_SPEED).round() as i32,
|
|
}]
|
|
}
|
|
_ => Vec::new(),
|
|
}
|
|
}
|
|
|
|
/// Sends a translated [`InputEvent`] to the browser. Must run on the thread owning the browser.
|
|
pub(crate) fn apply(browser: &Browser, event: &InputEvent) {
|
|
let Some(host) = browser.host() else { return };
|
|
match event {
|
|
InputEvent::MouseMove { data, leave } => {
|
|
host.send_mouse_move_event(Some(&data.into()), *leave as i32);
|
|
}
|
|
InputEvent::MouseClick { data, button, up, click_count } => {
|
|
let cef_button = cef::MouseButtonType::from(match button {
|
|
MouseButtonKind::Left => cef_mouse_button_type_t::MBT_LEFT,
|
|
MouseButtonKind::Right => cef_mouse_button_type_t::MBT_RIGHT,
|
|
MouseButtonKind::Middle => cef_mouse_button_type_t::MBT_MIDDLE,
|
|
});
|
|
host.send_mouse_click_event(Some(&data.into()), cef_button, *up as i32, *click_count);
|
|
}
|
|
InputEvent::MouseWheel { data, delta_x, delta_y } => {
|
|
host.send_mouse_wheel_event(Some(&data.into()), *delta_x, *delta_y);
|
|
}
|
|
InputEvent::Key(key) => {
|
|
let key_event = KeyEvent {
|
|
type_: match key.kind {
|
|
KeyEventKind::RawKeyDown => cef_key_event_type_t::KEYEVENT_RAWKEYDOWN,
|
|
KeyEventKind::KeyUp => cef_key_event_type_t::KEYEVENT_KEYUP,
|
|
KeyEventKind::Char => cef_key_event_type_t::KEYEVENT_CHAR,
|
|
}
|
|
.into(),
|
|
modifiers: key.modifiers,
|
|
windows_key_code: key.windows_key_code,
|
|
native_key_code: key.native_key_code,
|
|
character: key.character,
|
|
unmodified_character: key.unmodified_character,
|
|
..Default::default()
|
|
};
|
|
host.send_key_event(Some(&key_event));
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<&MouseData> for MouseEvent {
|
|
fn from(data: &MouseData) -> Self {
|
|
MouseEvent {
|
|
x: data.x,
|
|
y: data.y,
|
|
modifiers: data.modifiers,
|
|
}
|
|
}
|
|
}
|