use std::sync::Arc;
use std::sync::Mutex;
use super::EguiManager;
use rafx::api::RafxResult;
use sdl2::event::Event;
use sdl2::mouse::MouseButton;
use sdl2::video::Window;
struct CursorHandler {
system_cursor: Option<sdl2::mouse::SystemCursor>,
cursor: Option<sdl2::mouse::Cursor>,
mouse: sdl2::mouse::MouseUtil,
}
impl CursorHandler {
fn new(mouse: sdl2::mouse::MouseUtil) -> Self {
CursorHandler {
system_cursor: None,
cursor: None,
mouse,
}
}
fn set_cursor(
&mut self,
system_cursor: Option<sdl2::mouse::SystemCursor>,
) {
if system_cursor != self.system_cursor {
if system_cursor.is_none() {
self.system_cursor = None;
self.cursor = None;
self.mouse.show_cursor(false);
} else {
let cursor = sdl2::mouse::Cursor::from_system(system_cursor.unwrap()).unwrap();
cursor.set();
self.system_cursor = system_cursor;
self.cursor = Some(cursor);
self.mouse.show_cursor(true);
}
}
}
}
struct Sdl2EguiManagerInner {
clipboard: sdl2::clipboard::ClipboardUtil,
video_subsystem: sdl2::VideoSubsystem,
cursor: CursorHandler,
}
unsafe impl Send for Sdl2EguiManagerInner {}
#[derive(Clone)]
pub struct Sdl2EguiManager {
egui_manager: EguiManager,
inner: Arc<Mutex<Sdl2EguiManagerInner>>,
}
impl Sdl2EguiManager {
pub fn egui_manager(&self) -> EguiManager {
self.egui_manager.clone()
}
pub fn new(
sdl2_video_subsystem: &sdl2::VideoSubsystem,
sdl2_mouse: sdl2::mouse::MouseUtil,
) -> Self {
let egui_manager = EguiManager::new();
let inner = Sdl2EguiManagerInner {
clipboard: sdl2_video_subsystem.clipboard(),
video_subsystem: sdl2_video_subsystem.clone(),
cursor: CursorHandler::new(sdl2_mouse),
};
Sdl2EguiManager {
egui_manager,
inner: Arc::new(Mutex::new(inner)),
}
}
#[profiling::function]
pub fn handle_event(
&self,
event: &sdl2::event::Event,
) {
self.egui_manager.with_context_and_input(|_, input| {
match event {
Event::KeyDown {
keycode, keymod, ..
} => {
Self::handle_key_press(
input,
*keycode,
*keymod,
&self.inner.lock().unwrap().clipboard,
true,
);
}
Event::KeyUp {
keycode, keymod, ..
} => {
Self::handle_key_press(
input,
*keycode,
*keymod,
&self.inner.lock().unwrap().clipboard,
false,
);
}
Event::TextInput { text, .. } => {
input.events.push(egui::Event::Text(text.clone()));
}
Event::MouseMotion { x, y, .. } => {
let dpi = input.pixels_per_point.unwrap_or(1.0);
input.events.push(egui::Event::PointerMoved(egui::Pos2::new(
*x as f32 / dpi,
*y as f32 / dpi,
)));
}
Event::MouseButtonDown {
mouse_btn, x, y, ..
} => {
Self::handle_mouse_press(input, *x, *y, *mouse_btn, true);
}
Event::MouseButtonUp {
mouse_btn, x, y, ..
} => {
Self::handle_mouse_press(input, *x, *y, *mouse_btn, false);
}
Event::MouseWheel { x, y, .. } => {
input.scroll_delta.x += *x as f32;
input.scroll_delta.y += *y as f32;
}
_ => {}
}
});
}
fn handle_key_press(
input: &mut egui::RawInput,
keycode: Option<sdl2::keyboard::Keycode>,
keymod: sdl2::keyboard::Mod,
clipboard: &sdl2::clipboard::ClipboardUtil,
pressed: bool,
) {
use sdl2::keyboard::Mod;
input.modifiers.alt = keymod.intersects(Mod::LALTMOD | Mod::RALTMOD);
input.modifiers.ctrl = keymod.intersects(Mod::LCTRLMOD | Mod::RCTRLMOD);
input.modifiers.shift = keymod.intersects(Mod::LSHIFTMOD | Mod::RSHIFTMOD);
if cfg!(target_os = "macos") {
input.modifiers.mac_cmd = keymod.intersects(Mod::LGUIMOD | Mod::RGUIMOD);
input.modifiers.command = input.modifiers.mac_cmd;
} else {
input.modifiers.mac_cmd = false;
input.modifiers.command = input.modifiers.ctrl;
}
if let Some(sdl2_keycode) = keycode {
if let Some(key) = Self::egui_key(sdl2_keycode) {
if pressed {
if input.modifiers.command {
match key {
egui::Key::X => {
input.events.push(egui::Event::Cut);
}
egui::Key::C => {
input.events.push(egui::Event::Copy);
}
egui::Key::V => {
if let Ok(text) = clipboard.clipboard_text() {
input.events.push(egui::Event::Text(text));
}
}
_ => {}
}
}
}
input.events.push(egui::Event::Key {
key,
pressed,
modifiers: input.modifiers,
});
}
}
}
fn handle_mouse_press(
input: &mut egui::RawInput,
x: i32,
y: i32,
mouse_btn: sdl2::mouse::MouseButton,
pressed: bool,
) {
if let Some(button) = Self::egui_mouse_button(mouse_btn) {
input.events.push(egui::Event::PointerButton {
pos: egui::Pos2::new(x as _, y as _),
button,
pressed,
modifiers: input.modifiers,
});
}
}
pub fn ignore_event(
&self,
event: &sdl2::event::Event,
) -> bool {
let mut ignore = false;
self.egui_manager.with_context(|ctx| {
ignore = match event {
Event::KeyDown { .. } => ctx.wants_keyboard_input(),
Event::KeyUp { .. } => ctx.wants_keyboard_input(),
Event::TextInput { .. } => ctx.wants_keyboard_input(),
Event::MouseMotion { .. } => ctx.wants_pointer_input(),
Event::MouseButtonDown { .. } => ctx.wants_pointer_input(),
Event::MouseButtonUp { .. } => ctx.wants_pointer_input(),
Event::MouseWheel { .. } => ctx.wants_pointer_input(),
_ => false,
};
});
ignore
}
#[profiling::function]
pub fn begin_frame(
&self,
window: &Window,
) -> RafxResult<()> {
let (physical_width, physical_height) = window.vulkan_drawable_size();
let pixels_per_point = if cfg!(target_os = "windows") {
let display_index = window.display_index()?;
let (_, display_dpi, _) = window.subsystem().display_dpi(display_index)?;
display_dpi / 96.0
} else {
let (logical_width, _logical_height) = window.size();
if logical_width > 0 {
physical_width as f32 / logical_width as f32
} else {
1.0
}
};
self.egui_manager
.begin_frame(physical_width, physical_height, pixels_per_point);
Ok(())
}
#[profiling::function]
pub fn end_frame(&self) {
let mut inner = self.inner.lock().unwrap();
let output = self.egui_manager.end_frame();
if !output.copied_text.is_empty() {
inner
.clipboard
.set_clipboard_text(&output.copied_text)
.unwrap();
}
let cursor = Self::sdl2_mouse_cursor(output.cursor_icon);
inner.cursor.set_cursor(cursor);
}
fn egui_mouse_button(mouse_button: sdl2::mouse::MouseButton) -> Option<egui::PointerButton> {
match mouse_button {
MouseButton::Left => Some(egui::PointerButton::Primary),
MouseButton::Middle => Some(egui::PointerButton::Middle),
MouseButton::Right => Some(egui::PointerButton::Secondary),
_ => None,
}
}
fn egui_key(key: sdl2::keyboard::Keycode) -> Option<egui::Key> {
use egui::Key;
use sdl2::keyboard::Keycode;
Some(match key {
Keycode::Down => Key::ArrowDown,
Keycode::Left => Key::ArrowLeft,
Keycode::Right => Key::ArrowRight,
Keycode::Up => Key::ArrowUp,
Keycode::Escape => Key::Escape,
Keycode::Tab => Key::Tab,
Keycode::Backspace => Key::Backspace,
Keycode::Return => Key::Enter,
Keycode::Space => Key::Space,
Keycode::Insert => Key::Insert,
Keycode::Delete => Key::Delete,
Keycode::Home => Key::Home,
Keycode::End => Key::End,
Keycode::PageUp => Key::PageUp,
Keycode::PageDown => Key::PageDown,
Keycode::Num0 | Keycode::Kp0 => Key::Num0,
Keycode::Num1 | Keycode::Kp1 => Key::Num1,
Keycode::Num2 | Keycode::Kp2 => Key::Num2,
Keycode::Num3 | Keycode::Kp3 => Key::Num3,
Keycode::Num4 | Keycode::Kp4 => Key::Num4,
Keycode::Num5 | Keycode::Kp5 => Key::Num5,
Keycode::Num6 | Keycode::Kp6 => Key::Num6,
Keycode::Num7 | Keycode::Kp7 => Key::Num7,
Keycode::Num8 | Keycode::Kp8 => Key::Num8,
Keycode::Num9 | Keycode::Kp9 => Key::Num9,
Keycode::A => Key::A,
Keycode::B => Key::B,
Keycode::C => Key::C,
Keycode::D => Key::D,
Keycode::E => Key::E,
Keycode::F => Key::F,
Keycode::G => Key::G,
Keycode::H => Key::H,
Keycode::I => Key::I,
Keycode::J => Key::J,
Keycode::K => Key::K,
Keycode::L => Key::L,
Keycode::M => Key::M,
Keycode::N => Key::N,
Keycode::O => Key::O,
Keycode::P => Key::P,
Keycode::Q => Key::Q,
Keycode::R => Key::R,
Keycode::S => Key::S,
Keycode::T => Key::T,
Keycode::U => Key::U,
Keycode::V => Key::V,
Keycode::W => Key::W,
Keycode::X => Key::X,
Keycode::Y => Key::Y,
Keycode::Z => Key::Z,
_ => return None,
})
}
fn sdl2_mouse_cursor(egui_cursor: egui::CursorIcon) -> Option<sdl2::mouse::SystemCursor> {
use egui::CursorIcon;
use sdl2::mouse::SystemCursor;
Some(match egui_cursor {
CursorIcon::None => return None,
CursorIcon::PointingHand => SystemCursor::Hand,
CursorIcon::Progress => SystemCursor::Wait,
CursorIcon::Wait => SystemCursor::Wait,
CursorIcon::Crosshair => SystemCursor::Crosshair,
CursorIcon::Text => SystemCursor::IBeam,
CursorIcon::VerticalText => SystemCursor::IBeam,
CursorIcon::NoDrop => SystemCursor::No,
CursorIcon::NotAllowed => SystemCursor::No,
CursorIcon::Grab => SystemCursor::Hand,
CursorIcon::Grabbing => SystemCursor::Hand,
CursorIcon::ResizeHorizontal => SystemCursor::SizeWE,
CursorIcon::ResizeNeSw => SystemCursor::SizeNESW,
CursorIcon::ResizeNwSe => SystemCursor::SizeNWSE,
CursorIcon::ResizeVertical => SystemCursor::SizeNS,
_ => SystemCursor::Arrow,
})
}
}