Merge pull request #136 from Kethku/sdl2

Sdl2
macos-click-through
Keith Simmons 5 years ago committed by GitHub
commit 1f0d93ca3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -60,7 +60,7 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt-get install -y curl gnupg ca-certificates git gcc-multilib g++-multilib cmake libssl-dev pkg-config libfreetype6-dev libasound2-dev libexpat1-dev libxcb-composite0-dev libbz2-dev freeglut3-dev libxi-dev sudo apt-get install -y curl gnupg ca-certificates git gcc-multilib g++-multilib cmake libssl-dev pkg-config libfreetype6-dev libasound2-dev libexpat1-dev libxcb-composite0-dev libbz2-dev freeglut3-dev libxi-dev libsdl2-dev
- name: Build - name: Build
run: cargo build --release run: cargo build --release

1795
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -11,7 +11,8 @@ euclid = "0.20.7"
font-kit = "0.5.0" font-kit = "0.5.0"
skribo = { git = "https://github.com/linebender/skribo" } skribo = { git = "https://github.com/linebender/skribo" }
lru = "0.4.3" lru = "0.4.3"
skulpin = { git = "https://github.com/kethku/skulpin", branch = "winit_20" } skulpin = { git = "https://github.com/Kethku/skulpin", branch = "sdl2" }
# skulpin = { path = "../skulpin" }
derive-new = "0.5" derive-new = "0.5"
rmpv = "0.4.4" rmpv = "0.4.4"
rust-embed = { version = "5.2.0", features = ["debug-embed"] } rust-embed = { version = "5.2.0", features = ["debug-embed"] }
@ -26,6 +27,9 @@ flexi_logger = { version = "0.14.6", default-features = false }
anyhow = "1.0.26" anyhow = "1.0.26"
parking_lot="0.10.0" parking_lot="0.10.0"
[target.'cfg(windows)'.dependencies]
winapi = "0.3.8"
[build-dependencies] [build-dependencies]
winres = "0.1.11" winres = "0.1.11"

@ -56,11 +56,13 @@ Note: Neovide requires neovim version 0.4 or greater.
### Windows ### Windows
1. Install the latest version of Rust. I recommend <https://rustup.rs/> 1. Install the latest version of Rust. I recommend <https://rustup.rs/>
2. Ensure graphics libraries are up to date. 2. Install CMake. I use chocolatey: `choco install cmake --installargs '"ADD_CMAKE_TO_PATH=System"' -y`
3. `git clone https://github.com/Kethku/neovide` 3. Install LLVM. I use chocolatey: `choco install llvm -y`
4. `cd neovide` 4. Ensure graphics libraries are up to date.
5. `cargo build --release` 5. `git clone https://github.com/Kethku/neovide`
6. Copy `./target/release/neovide.exe` to a known location and enjoy. 6. `cd neovide`
7. `cargo build --release`
8. Copy `./target/release/neovide.exe` to a known location and enjoy.
### Mac ### Mac
@ -73,7 +75,7 @@ Note: Neovide requires neovim version 0.4 or greater.
### Linux ### Linux
Instructions to build on Ubuntu Note: Neovide has been successfully built on other destros but this reportedly works on ubuntu.
1. Install necessary dependencies 1. Install necessary dependencies

@ -1,7 +1,8 @@
use log::trace; use log::trace;
use skulpin::winit::event::{ElementState, KeyboardInput, ModifiersState, VirtualKeyCode}; // use skulpin::winit::event::{ElementState, KeyboardInput, ModifiersState, VirtualKeyCode};
use skulpin::sdl2::keyboard::{Keycode, Mod};
fn parse_keycode(keycode: VirtualKeyCode) -> Option<(&'static str, bool)> { pub fn parse_keycode(keycode: Keycode) -> Option<(&'static str, bool)> {
macro_rules! unsupported_key { macro_rules! unsupported_key {
($name: ident) => {{ ($name: ident) => {{
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
@ -11,228 +12,263 @@ fn parse_keycode(keycode: VirtualKeyCode) -> Option<(&'static str, bool)> {
}}; }};
} }
// Documentation: https://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlkey.html
match keycode { match keycode {
VirtualKeyCode::Key1 => Some(("1", false)), Keycode::Backspace => Some(("BS", true)),
VirtualKeyCode::Key2 => Some(("2", false)), Keycode::Tab => Some(("Tab", true)),
VirtualKeyCode::Key3 => Some(("3", false)), Keycode::Return => Some(("Enter", true)),
VirtualKeyCode::Key4 => Some(("4", false)), Keycode::Escape => Some(("Esc", true)),
VirtualKeyCode::Key5 => Some(("5", false)), Keycode::Space => Some((" ", false)),
VirtualKeyCode::Key6 => Some(("6", false)), Keycode::Exclaim => Some(("!", false)),
VirtualKeyCode::Key7 => Some(("7", false)), Keycode::Quotedbl => Some(("\"", false)),
VirtualKeyCode::Key8 => Some(("8", false)), Keycode::Hash => Some(("#", false)),
VirtualKeyCode::Key9 => Some(("9", false)), Keycode::Dollar => Some(("$", false)),
VirtualKeyCode::Key0 => Some(("0", false)), Keycode::Percent => Some(("%", false)),
VirtualKeyCode::A => Some(("a", false)), Keycode::Ampersand => Some(("&", false)),
VirtualKeyCode::B => Some(("b", false)), Keycode::Quote => Some(("'", false)),
VirtualKeyCode::C => Some(("c", false)), Keycode::LeftParen => Some(("(", false)),
VirtualKeyCode::D => Some(("d", false)), Keycode::RightParen => Some((")", false)),
VirtualKeyCode::E => Some(("e", false)), Keycode::Asterisk => Some(("*", false)),
VirtualKeyCode::F => Some(("f", false)), Keycode::Plus => Some(("+", false)),
VirtualKeyCode::G => Some(("g", false)), Keycode::Comma => Some((",", false)),
VirtualKeyCode::H => Some(("h", false)), Keycode::Minus => Some(("-", false)),
VirtualKeyCode::I => Some(("i", false)), Keycode::Period => Some((".", false)),
VirtualKeyCode::J => Some(("j", false)), Keycode::Slash => Some(("/", false)),
VirtualKeyCode::K => Some(("k", false)), Keycode::Num0 => Some(("0", false)),
VirtualKeyCode::L => Some(("l", false)), Keycode::Num1 => Some(("1", false)),
VirtualKeyCode::M => Some(("m", false)), Keycode::Num2 => Some(("2", false)),
VirtualKeyCode::N => Some(("n", false)), Keycode::Num3 => Some(("3", false)),
VirtualKeyCode::O => Some(("o", false)), Keycode::Num4 => Some(("4", false)),
VirtualKeyCode::P => Some(("p", false)), Keycode::Num5 => Some(("5", false)),
VirtualKeyCode::Q => Some(("q", false)), Keycode::Num6 => Some(("6", false)),
VirtualKeyCode::R => Some(("r", false)), Keycode::Num7 => Some(("7", false)),
VirtualKeyCode::S => Some(("s", false)), Keycode::Num8 => Some(("8", false)),
VirtualKeyCode::T => Some(("t", false)), Keycode::Num9 => Some(("9", false)),
VirtualKeyCode::U => Some(("u", false)), Keycode::Colon => Some((":", false)),
VirtualKeyCode::V => Some(("v", false)), Keycode::Semicolon => Some((";", false)),
VirtualKeyCode::W => Some(("w", false)), Keycode::Less => Some(("lt", true)),
VirtualKeyCode::X => Some(("x", false)), Keycode::Equals => Some(("=", false)),
VirtualKeyCode::Y => Some(("y", false)), Keycode::Greater => Some(("gt", true)),
VirtualKeyCode::Z => Some(("z", false)), Keycode::Question => Some(("?", false)),
VirtualKeyCode::Escape => Some(("ESC", true)), Keycode::At => Some(("@", false)),
VirtualKeyCode::F1 => Some(("F1", true)), Keycode::LeftBracket => Some(("[", false)),
VirtualKeyCode::F2 => Some(("F2", true)), Keycode::Backslash => Some(("\\", false)),
VirtualKeyCode::F3 => Some(("F3", true)), Keycode::RightBracket => Some(("]", false)),
VirtualKeyCode::F4 => Some(("F4", true)), Keycode::Caret => Some(("^", false)),
VirtualKeyCode::F5 => Some(("F5", true)), Keycode::Underscore => Some(("_", false)),
VirtualKeyCode::F6 => Some(("F6", true)), Keycode::Backquote => Some(("`", false)),
VirtualKeyCode::F7 => Some(("F7", true)), Keycode::A => Some(("a", false)),
VirtualKeyCode::F8 => Some(("F8", true)), Keycode::B => Some(("b", false)),
VirtualKeyCode::F9 => Some(("F9", true)), Keycode::C => Some(("c", false)),
VirtualKeyCode::F10 => Some(("F10", true)), Keycode::D => Some(("d", false)),
VirtualKeyCode::F11 => Some(("F11", true)), Keycode::E => Some(("e", false)),
VirtualKeyCode::F12 => Some(("F12", true)), Keycode::F => Some(("f", false)),
VirtualKeyCode::F13 => Some(("F13", true)), Keycode::G => Some(("g", false)),
VirtualKeyCode::F14 => Some(("F14", true)), Keycode::H => Some(("h", false)),
VirtualKeyCode::F15 => Some(("F15", true)), Keycode::I => Some(("i", false)),
VirtualKeyCode::F16 => Some(("F16", true)), Keycode::J => Some(("j", false)),
VirtualKeyCode::F17 => Some(("F17", true)), Keycode::K => Some(("k", false)),
VirtualKeyCode::F18 => Some(("F18", true)), Keycode::L => Some(("l", false)),
VirtualKeyCode::F19 => Some(("F19", true)), Keycode::M => Some(("m", false)),
VirtualKeyCode::F20 => Some(("F20", true)), Keycode::N => Some(("n", false)),
VirtualKeyCode::F21 => Some(("F21", true)), Keycode::O => Some(("o", false)),
VirtualKeyCode::F22 => Some(("F22", true)), Keycode::P => Some(("p", false)),
VirtualKeyCode::F23 => Some(("F23", true)), Keycode::Q => Some(("q", false)),
VirtualKeyCode::F24 => Some(("F24", true)), Keycode::R => Some(("r", false)),
VirtualKeyCode::Snapshot => unsupported_key!(Snapshot), Keycode::S => Some(("s", false)),
VirtualKeyCode::Scroll => unsupported_key!(Scroll), Keycode::T => Some(("t", false)),
VirtualKeyCode::Pause => unsupported_key!(Pause), Keycode::U => Some(("u", false)),
VirtualKeyCode::Insert => Some(("Insert", true)), Keycode::V => Some(("v", false)),
VirtualKeyCode::Home => Some(("Home", true)), Keycode::W => Some(("w", false)),
VirtualKeyCode::Delete => Some(("Delete", true)), Keycode::X => Some(("x", false)),
VirtualKeyCode::End => Some(("End", true)), Keycode::Y => Some(("y", false)),
VirtualKeyCode::PageDown => Some(("PageDown", true)), Keycode::Z => Some(("z", false)),
VirtualKeyCode::PageUp => Some(("PageUp", true)), Keycode::Delete => Some(("Delete", true)),
VirtualKeyCode::Left => Some(("Left", true)), Keycode::CapsLock => unsupported_key!(CapsLock),
VirtualKeyCode::Up => Some(("Up", true)), Keycode::F1 => Some(("F1", true)),
VirtualKeyCode::Right => Some(("Right", true)), Keycode::F2 => Some(("F2", true)),
VirtualKeyCode::Down => Some(("Down", true)), Keycode::F3 => Some(("F3", true)),
VirtualKeyCode::Back => Some(("BS", true)), Keycode::F4 => Some(("F4", true)),
VirtualKeyCode::Return => Some(("Enter", true)), Keycode::F5 => Some(("F5", true)),
VirtualKeyCode::Space => Some(("Space", true)), Keycode::F6 => Some(("F6", true)),
VirtualKeyCode::Compose => unsupported_key!(Compose), Keycode::F7 => Some(("F7", true)),
VirtualKeyCode::Caret => Some(("^", false)), Keycode::F8 => Some(("F8", true)),
VirtualKeyCode::Numlock => unsupported_key!(Numlock), Keycode::F9 => Some(("F9", true)),
VirtualKeyCode::Numpad0 => Some(("0", false)), Keycode::F10 => Some(("F10", true)),
VirtualKeyCode::Numpad1 => Some(("1", false)), Keycode::F11 => Some(("F11", true)),
VirtualKeyCode::Numpad2 => Some(("2", false)), Keycode::F12 => Some(("F12", true)),
VirtualKeyCode::Numpad3 => Some(("3", false)), Keycode::PrintScreen => unsupported_key!(PrintScreen),
VirtualKeyCode::Numpad4 => Some(("4", false)), Keycode::ScrollLock => unsupported_key!(ScrollLock),
VirtualKeyCode::Numpad5 => Some(("5", false)), Keycode::Pause => unsupported_key!(Pause),
VirtualKeyCode::Numpad6 => Some(("6", false)), Keycode::Insert => Some(("Insert", true)),
VirtualKeyCode::Numpad7 => Some(("7", false)), Keycode::Home => Some(("Home", true)),
VirtualKeyCode::Numpad8 => Some(("8", false)), Keycode::PageUp => Some(("PageUp", true)),
VirtualKeyCode::Numpad9 => Some(("9", false)), Keycode::End => Some(("End", true)),
// These next two are for Brazillian keyboards according to Keycode::PageDown => Some(("PageDown", true)),
// https://hg.mozilla.org/integration/mozilla-inbound/rev/28039c359ce8#l2.31 Keycode::Right => Some(("Right", true)),
// Mapping both to the same thing as firefox Keycode::Left => Some(("Left", true)),
VirtualKeyCode::AbntC1 => Some(("/", false)), Keycode::Down => Some(("Down", true)),
VirtualKeyCode::AbntC2 => Some((".", false)), Keycode::Up => Some(("Up", true)),
VirtualKeyCode::Add => Some(("+", true)), Keycode::NumLockClear => unsupported_key!(NumLockClear),
VirtualKeyCode::Apostrophe => Some(("'", false)), Keycode::KpDivide => Some(("/", true)),
VirtualKeyCode::Apps => unsupported_key!(Apps), Keycode::KpMultiply => Some(("*", true)),
VirtualKeyCode::At => Some(("@", false)), Keycode::KpMinus => Some(("-", true)),
VirtualKeyCode::Ax => unsupported_key!(Ax), Keycode::KpPlus => Some(("+", true)),
VirtualKeyCode::Backslash => Some(("Bslash", true)), Keycode::KpEnter => Some(("Enter", true)),
VirtualKeyCode::Calculator => unsupported_key!(Calculator), Keycode::Kp0 => Some(("0", false)),
VirtualKeyCode::Capital => unsupported_key!(Capital), Keycode::Kp1 => Some(("1", false)),
VirtualKeyCode::Colon => Some((":", false)), Keycode::Kp2 => Some(("2", false)),
VirtualKeyCode::Comma => Some((",", false)), Keycode::Kp3 => Some(("3", false)),
VirtualKeyCode::Convert => unsupported_key!(Convert), Keycode::Kp4 => Some(("4", false)),
VirtualKeyCode::Decimal => Some((".", false)), Keycode::Kp5 => Some(("5", false)),
VirtualKeyCode::Divide => Some(("/", false)), Keycode::Kp6 => Some(("6", false)),
VirtualKeyCode::Equals => Some(("=", false)), Keycode::Kp7 => Some(("7", false)),
VirtualKeyCode::Grave => Some(("`", false)), Keycode::Kp8 => Some(("8", false)),
VirtualKeyCode::Kana => unsupported_key!(Kana), Keycode::Kp9 => Some(("9", false)),
VirtualKeyCode::Kanji => unsupported_key!(Kanji), Keycode::KpPeriod => Some((".", false)),
VirtualKeyCode::LAlt => None, // Regular modifier key Keycode::Application => unsupported_key!(Application),
VirtualKeyCode::LBracket => Some(("[", false)), Keycode::Power => unsupported_key!(Power),
VirtualKeyCode::LControl => None, // Regular modifier key Keycode::KpEquals => Some(("=", false)),
VirtualKeyCode::LShift => None, // Regular modifier key Keycode::F13 => Some(("F13", true)),
VirtualKeyCode::LWin => unsupported_key!(LWin), Keycode::F14 => Some(("F14", true)),
VirtualKeyCode::Mail => unsupported_key!(Mail), Keycode::F15 => Some(("F15", true)),
VirtualKeyCode::MediaSelect => unsupported_key!(MediaSelect), Keycode::F16 => Some(("F16", true)),
VirtualKeyCode::MediaStop => unsupported_key!(MediaStop), Keycode::F17 => Some(("F17", true)),
VirtualKeyCode::Minus => Some(("-", false)), Keycode::F18 => Some(("F18", true)),
VirtualKeyCode::Multiply => Some(("*", false)), Keycode::F19 => Some(("F19", true)),
VirtualKeyCode::Mute => unsupported_key!(Mute), Keycode::F20 => Some(("F20", true)),
VirtualKeyCode::MyComputer => unsupported_key!(MyComputer), Keycode::F21 => Some(("F21", true)),
VirtualKeyCode::NavigateForward => unsupported_key!(NavigateForward), Keycode::F22 => Some(("F22", true)),
VirtualKeyCode::NavigateBackward => unsupported_key!(NavigateBackward), Keycode::F23 => Some(("F23", true)),
VirtualKeyCode::NextTrack => unsupported_key!(NextTrack), Keycode::F24 => Some(("F24", true)),
VirtualKeyCode::NoConvert => unsupported_key!(NoConvert), Keycode::Execute => unsupported_key!(Execute),
VirtualKeyCode::NumpadComma => Some((",", false)), Keycode::Help => unsupported_key!(Help),
VirtualKeyCode::NumpadEnter => Some(("Enter", true)), Keycode::Menu => unsupported_key!(Menu),
VirtualKeyCode::NumpadEquals => Some(("=", false)), Keycode::Select => unsupported_key!(Select),
VirtualKeyCode::OEM102 => unsupported_key!(OEM102), Keycode::Stop => unsupported_key!(Stop),
VirtualKeyCode::Period => Some((".", false)), Keycode::Again => unsupported_key!(Again),
VirtualKeyCode::PlayPause => unsupported_key!(PlayPause), Keycode::Undo => unsupported_key!(Undo),
VirtualKeyCode::Power => unsupported_key!(Power), Keycode::Cut => unsupported_key!(Cut),
VirtualKeyCode::PrevTrack => unsupported_key!(PrevTrack), Keycode::Copy => unsupported_key!(Copy),
VirtualKeyCode::RAlt => None, // Regular modifier key Keycode::Paste => unsupported_key!(Paste),
VirtualKeyCode::RBracket => Some(("]", false)), Keycode::Find => unsupported_key!(Find),
VirtualKeyCode::RControl => None, // Regular modifier key Keycode::Mute => unsupported_key!(Mute),
VirtualKeyCode::RShift => None, // Regular modifier key Keycode::VolumeUp => unsupported_key!(VolumeUp),
VirtualKeyCode::RWin => unsupported_key!(RWin), Keycode::VolumeDown => unsupported_key!(VolumeDown),
VirtualKeyCode::Semicolon => Some((";", false)), Keycode::KpComma => unsupported_key!(KpComma),
VirtualKeyCode::Slash => Some(("/", false)), Keycode::KpEqualsAS400 => unsupported_key!(KpEqualsAS400),
VirtualKeyCode::Sleep => unsupported_key!(Sleep), Keycode::AltErase => unsupported_key!(AltErase),
VirtualKeyCode::Stop => unsupported_key!(Stop), Keycode::Sysreq => unsupported_key!(Sysreq),
VirtualKeyCode::Subtract => Some(("-", false)), Keycode::Cancel => unsupported_key!(Cancel),
VirtualKeyCode::Sysrq => unsupported_key!(Sysrq), Keycode::Clear => unsupported_key!(Clear),
VirtualKeyCode::Tab => Some(("Tab", true)), Keycode::Prior => unsupported_key!(Prior),
VirtualKeyCode::Underline => unsupported_key!(Underline), Keycode::Return2 => unsupported_key!(Return2),
VirtualKeyCode::Unlabeled => unsupported_key!(Unlabeled), Keycode::Separator => unsupported_key!(Separator),
VirtualKeyCode::VolumeDown => unsupported_key!(VolumeDown), Keycode::Out => unsupported_key!(Out),
VirtualKeyCode::VolumeUp => unsupported_key!(VolumeUp), Keycode::Oper => unsupported_key!(Oper),
VirtualKeyCode::Wake => unsupported_key!(Wake), Keycode::ClearAgain => unsupported_key!(ClearAgain),
VirtualKeyCode::WebBack => unsupported_key!(WebBack), Keycode::CrSel => unsupported_key!(CrSel),
VirtualKeyCode::WebFavorites => unsupported_key!(WebFavorites), Keycode::ExSel => unsupported_key!(ExSel),
VirtualKeyCode::WebForward => unsupported_key!(WebForward), Keycode::Kp00 => unsupported_key!(Kp00),
VirtualKeyCode::WebHome => unsupported_key!(WebHome), Keycode::Kp000 => unsupported_key!(Kp000),
VirtualKeyCode::WebRefresh => unsupported_key!(WebRefresh), Keycode::ThousandsSeparator => unsupported_key!(ThousandsSeparator),
VirtualKeyCode::WebSearch => unsupported_key!(WebSearch), Keycode::DecimalSeparator => unsupported_key!(DecimalSeparator),
VirtualKeyCode::WebStop => unsupported_key!(WebStop), Keycode::CurrencyUnit => unsupported_key!(CurrencyUnit),
VirtualKeyCode::Yen => Some(("¥", false)), Keycode::CurrencySubUnit => unsupported_key!(CurrencySubUnit),
VirtualKeyCode::Copy => unsupported_key!(Copy), Keycode::KpLeftParen => Some(("(", false)),
VirtualKeyCode::Paste => unsupported_key!(Paste), Keycode::KpRightParen => Some((")", false)),
VirtualKeyCode::Cut => unsupported_key!(Cut), Keycode::KpLeftBrace => Some(("[", false)),
Keycode::KpRightBrace => Some(("]", false)),
Keycode::KpTab => Some(("TAB", true)),
Keycode::KpBackspace => Some(("BS", true)),
Keycode::KpA => Some(("A", false)),
Keycode::KpB => Some(("B", false)),
Keycode::KpC => Some(("C", false)),
Keycode::KpD => Some(("D", false)),
Keycode::KpE => Some(("E", false)),
Keycode::KpF => Some(("F", false)),
Keycode::KpXor => unsupported_key!(KpXor),
Keycode::KpPower => Some(("^", false)),
Keycode::KpPercent => Some(("%", false)),
Keycode::KpLess => Some(("lt", true)),
Keycode::KpGreater => Some(("gt", true)),
Keycode::KpAmpersand => Some(("&", false)),
Keycode::KpDblAmpersand => unsupported_key!(KpDblAmpersand),
Keycode::KpVerticalBar => Some(("|", false)),
Keycode::KpDblVerticalBar => unsupported_key!(KpDblVerticalBar),
Keycode::KpColon => Some((":", false)),
Keycode::KpHash => Some(("#", false)),
Keycode::KpSpace => Some((" ", false)),
Keycode::KpAt => Some(("@", false)),
Keycode::KpExclam => Some(("!", false)),
Keycode::KpMemStore => unsupported_key!(KpMemStore),
Keycode::KpMemRecall => unsupported_key!(KpMemRecall),
Keycode::KpMemClear => unsupported_key!(KpMemClear),
Keycode::KpMemAdd => unsupported_key!(KpMemAdd),
Keycode::KpMemSubtract => unsupported_key!(KpMemSubtract),
Keycode::KpMemMultiply => unsupported_key!(KpMemMultiply),
Keycode::KpMemDivide => unsupported_key!(KpMemDivide),
Keycode::KpPlusMinus => unsupported_key!(KpPlusMinus),
Keycode::KpClear => unsupported_key!(KpClear),
Keycode::KpClearEntry => unsupported_key!(KpClearEntry),
Keycode::KpBinary => unsupported_key!(KpBinary),
Keycode::KpOctal => unsupported_key!(KpOctal),
Keycode::KpDecimal => unsupported_key!(KpDecimal),
Keycode::KpHexadecimal => unsupported_key!(KpHexadecimal),
Keycode::LCtrl => None,
Keycode::LShift => None,
Keycode::LAlt => None,
Keycode::LGui => None,
Keycode::RCtrl => None,
Keycode::RShift => None,
Keycode::RAlt => None,
Keycode::RGui => None,
Keycode::Mode => unsupported_key!(Mode),
Keycode::AudioNext => unsupported_key!(AudioNext),
Keycode::AudioPrev => unsupported_key!(AudioPrev),
Keycode::AudioStop => unsupported_key!(AudioStop),
Keycode::AudioPlay => unsupported_key!(AudioPlay),
Keycode::AudioMute => unsupported_key!(AudioMute),
Keycode::MediaSelect => unsupported_key!(MediaSelect),
Keycode::Www => unsupported_key!(Www),
Keycode::Mail => unsupported_key!(Mail),
Keycode::Calculator => unsupported_key!(Calculator),
Keycode::Computer => unsupported_key!(Computer),
Keycode::AcSearch => unsupported_key!(AcSearch),
Keycode::AcHome => unsupported_key!(AcHome),
Keycode::AcBack => unsupported_key!(AcBack),
Keycode::AcForward => unsupported_key!(AcForward),
Keycode::AcStop => unsupported_key!(AcStop),
Keycode::AcRefresh => unsupported_key!(AcRefresh),
Keycode::AcBookmarks => unsupported_key!(AcBookmarks),
Keycode::BrightnessDown => unsupported_key!(BrightnessDown),
Keycode::BrightnessUp => unsupported_key!(BrightnessUp),
Keycode::DisplaySwitch => unsupported_key!(DisplaySwitch),
Keycode::KbdIllumToggle =>unsupported_key!(KbdIllumToggle),
Keycode::KbdIllumDown => unsupported_key!(KbdIllumDown),
Keycode::KbdIllumUp => unsupported_key!(KbdIllumUp),
Keycode::Eject => unsupported_key!(Eject),
Keycode::Sleep => unsupported_key!(Sleep)
} }
} }
pub fn append_modifiers(modifiers: ModifiersState, keycode_text: &str, special: bool) -> String { pub fn append_modifiers(modifiers: Mod, keycode_text: &str, special: bool) -> String {
let mut result = keycode_text.to_string(); let mut result = keycode_text.to_string();
let mut special = special; let mut special = special;
if modifiers.shift() { if modifiers.contains(Mod::LSHIFTMOD) || modifiers.contains(Mod::RSHIFTMOD) {
result = match result.as_ref() {
"1" => "!".to_string(),
"2" => "@".to_string(),
"3" => "#".to_string(),
"4" => "$".to_string(),
"5" => "%".to_string(),
"6" => "^".to_string(),
"7" => "&".to_string(),
"8" => "*".to_string(),
"9" => "(".to_string(),
"0" => ")".to_string(),
"'" => "\"".to_string(),
"Bslash" => {
special = false;
"|".to_string()
},
"," => {
special = true;
"lt".to_string()
},
"=" => "+".to_string(),
"`" => "~".to_string(),
"[" => "{".to_string(),
"-" => "_".to_string(),
"." => ">".to_string(),
"]" => "}".to_string(),
";" => ":".to_string(),
"/" => "?".to_string(),
other => {
special = true;
format!("S-{}", other)
}
};
}
if result == "<" {
special = true; special = true;
result = "lt".to_string(); result = format!("S-{}", result);
} }
if modifiers.contains(Mod::LCTRLMOD) || modifiers.contains(Mod::RCTRLMOD) {
if modifiers.ctrl() {
special = true; special = true;
result = format!("C-{}", result); result = format!("C-{}", result);
} }
if modifiers.alt() { if modifiers.contains(Mod::LALTMOD) || modifiers.contains(Mod::RALTMOD) {
special = true; special = true;
result = format!("M-{}", result); result = format!("M-{}", result);
} }
if modifiers.logo() { if modifiers.contains(Mod::LGUIMOD) || modifiers.contains(Mod::RGUIMOD) {
special = true; special = true;
result = format!("D-{}", result); result = format!("D-{}", result);
} }
@ -243,21 +279,3 @@ pub fn append_modifiers(modifiers: ModifiersState, keycode_text: &str, special:
result result
} }
pub fn construct_keybinding_string(input: KeyboardInput) -> Option<String> {
match input {
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(keycode),
modifiers,
..
} => {
if let Some((keycode_text, special)) = parse_keycode(keycode) {
Some(append_modifiers(modifiers, keycode_text, special))
} else {
None
}
},
_ => None
}
}

@ -4,31 +4,31 @@ use tokio::process::ChildStdin;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum UiCommand { pub enum UiCommand {
Resize { width: i64, height: i64 }, Resize { width: u32, height: u32 },
Keyboard(String), Keyboard(String),
MouseButton { action: String, position: (i64, i64) }, MouseButton { action: String, position: (u32, u32) },
Scroll { direction: String, position: (i64, i64) }, Scroll { direction: String, position: (u32, u32) },
Drag(i64, i64) Drag(u32, u32)
} }
impl UiCommand { impl UiCommand {
pub async fn execute(self, nvim: &Neovim<Compat<ChildStdin>>) { pub async fn execute(self, nvim: &Neovim<Compat<ChildStdin>>) {
match self { match self {
UiCommand::Resize { width, height } => UiCommand::Resize { width, height } =>
nvim.ui_try_resize(width.max(10), height.max(3)).await nvim.ui_try_resize(width.max(10) as i64, height.max(3) as i64).await
.expect("Resize failed"), .expect("Resize failed"),
UiCommand::Keyboard(input_command) => { UiCommand::Keyboard(input_command) => {
nvim.input(&input_command).await nvim.input(&input_command).await
.expect("Input failed"); .expect("Input failed");
}, },
UiCommand::MouseButton { action, position: (grid_x, grid_y) } => UiCommand::MouseButton { action, position: (grid_x, grid_y) } =>
nvim.input_mouse("left", &action, "", 0, grid_x, grid_y).await nvim.input_mouse("left", &action, "", 0, grid_y as i64, grid_x as i64).await
.expect("Mouse Input Failed"), .expect("Mouse Input Failed"),
UiCommand::Scroll { direction, position: (grid_x, grid_y) } => UiCommand::Scroll { direction, position: (grid_x, grid_y) } =>
nvim.input_mouse("wheel", &direction, "", 0, grid_x, grid_y).await nvim.input_mouse("wheel", &direction, "", 0, grid_y as i64, grid_x as i64).await
.expect("Mouse Scroll Failed"), .expect("Mouse Scroll Failed"),
UiCommand::Drag(grid_x, grid_y) => UiCommand::Drag(grid_x, grid_y) =>
nvim.input_mouse("left", "drag", "", 0, grid_x, grid_y).await nvim.input_mouse("left", "drag", "", 0, grid_y as i64, grid_x as i64).await
.expect("Mouse Drag Failed") .expect("Mouse Drag Failed")
} }
} }

@ -7,6 +7,7 @@ use nvim_rs::compat::tokio::Compat;
use flexi_logger::{Logger, Criterion, Naming, Cleanup}; use flexi_logger::{Logger, Criterion, Naming, Cleanup};
use tokio::process::ChildStdin; use tokio::process::ChildStdin;
use parking_lot::Mutex; use parking_lot::Mutex;
use log::warn;
use crate::error_handling::ResultPanicExplanation; use crate::error_handling::ResultPanicExplanation;
@ -14,6 +15,7 @@ lazy_static! {
pub static ref SETTINGS: Settings = Settings::new(); pub static ref SETTINGS: Settings = Settings::new();
} }
#[derive(Debug)]
pub enum Setting { pub enum Setting {
Bool(bool), Bool(bool),
U16(u16), U16(u16),
@ -111,20 +113,22 @@ pub struct Settings {
impl Settings { impl Settings {
pub async fn read_initial_values(&self, nvim: &Neovim<Compat<ChildStdin>>) { pub async fn read_initial_values(&self, nvim: &Neovim<Compat<ChildStdin>>) {
let keys : Vec<String>= self.settings.lock().keys().cloned().collect(); let keys : Vec<String> = self.settings.lock().keys().cloned().collect();
for name in keys { for name in keys {
let variable_name = format!("g:neovide_{}", name.to_string()); let variable_name = format!("neovide_{}", name.to_string());
if let Ok(value) = nvim.get_var(&variable_name).await { match nvim.get_var(&variable_name).await {
self.settings.lock().get_mut(&name).unwrap().parse(value); Ok(value) => self.settings.lock().get_mut(&name).unwrap().parse(value),
} else { Err(error) => {
let setting = self.get(&name); warn!("Initial value load failed for {}: {}", name, error);
nvim.set_var(&variable_name, setting.unparse()).await.ok(); let setting = self.get(&name);
nvim.set_var(&variable_name, setting.unparse()).await.ok();
}
} }
} }
} }
pub async fn setup_changed_listeners(&self, nvim: &Neovim<Compat<ChildStdin>>) { pub async fn setup_changed_listeners(&self, nvim: &Neovim<Compat<ChildStdin>>) {
let keys : Vec<String>= self.settings.lock().keys().cloned().collect(); let keys : Vec<String> = self.settings.lock().keys().cloned().collect();
for name in keys { for name in keys {
let vimscript = let vimscript =
format!("function NeovideNotify{}Changed(d, k, z)\n", name) + format!("function NeovideNotify{}Changed(d, k, z)\n", name) +
@ -139,7 +143,6 @@ impl Settings {
pub fn handle_changed_notification(&self, arguments: Vec<Value>) { pub fn handle_changed_notification(&self, arguments: Vec<Value>) {
let mut arguments = arguments.into_iter(); let mut arguments = arguments.into_iter();
let (name, value) = (arguments.next().unwrap(), arguments.next().unwrap()); let (name, value) = (arguments.next().unwrap(), arguments.next().unwrap());
dbg!(&name, &value);
let name: Result<String, _>= name.try_into(); let name: Result<String, _>= name.try_into();
let name = name.unwrap(); let name = name.unwrap();
@ -180,6 +183,7 @@ impl Settings {
settings.insert("no_idle".to_string(), Setting::new_bool(no_idle)); settings.insert("no_idle".to_string(), Setting::new_bool(no_idle));
settings.insert("extra_buffer_frames".to_string(), Setting::new_u16(buffer_frames)); settings.insert("extra_buffer_frames".to_string(), Setting::new_u16(buffer_frames));
settings.insert("refresh_rate".to_string(), Setting::new_u16(60));
Settings { neovim_arguments, settings: Mutex::new(settings) } Settings { neovim_arguments, settings: Mutex::new(settings) }
} }

@ -1,16 +1,14 @@
use std::sync::Arc;
use std::sync::atomic::Ordering;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use std::thread::sleep;
use image::{load_from_memory, GenericImageView, Pixel}; use log::{info, debug, error};
use skulpin::{CoordinateSystem, RendererBuilder, PresentMode}; use skulpin::{LogicalSize, PhysicalSize};
use skulpin::winit::dpi::{LogicalSize, LogicalPosition}; use skulpin::sdl2;
use skulpin::winit::event::{ElementState, Event, MouseScrollDelta, StartCause, WindowEvent, ModifiersState}; use skulpin::sdl2::event::Event;
use skulpin::winit::event_loop::{ControlFlow, EventLoop}; use skulpin::sdl2::keyboard::Mod;
use skulpin::winit::window::{Icon, WindowBuilder}; use skulpin::{RendererBuilder, PresentMode, CoordinateSystem, dpis};
use log::{info, debug, trace, error};
use crate::bridge::{construct_keybinding_string, BRIDGE, UiCommand}; use crate::bridge::{parse_keycode, append_modifiers, BRIDGE, UiCommand};
use crate::renderer::Renderer; use crate::renderer::Renderer;
use crate::redraw_scheduler::REDRAW_SCHEDULER; use crate::redraw_scheduler::REDRAW_SCHEDULER;
use crate::editor::EDITOR; use crate::editor::EDITOR;
@ -21,216 +19,206 @@ use crate::INITIAL_DIMENSIONS;
#[folder = "assets/"] #[folder = "assets/"]
struct Asset; struct Asset;
fn handle_new_grid_size(new_size: LogicalSize<f32>, renderer: &Renderer) { #[cfg(target_os = "windows")]
if new_size.width > 0.0 && new_size.height > 0.0 { fn windows_fix_dpi() {
let new_width = ((new_size.width + 1.0) / renderer.font_width) as i64; use winapi::um::winuser::SetProcessDpiAwarenessContext;
let new_height = ((new_size.height + 1.0) / renderer.font_height) as i64; use winapi::shared::windef::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
unsafe {
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
}
}
fn handle_new_grid_size(new_size: LogicalSize, renderer: &Renderer) {
if new_size.width > 0 && new_size.height > 0 {
let new_width = ((new_size.width + 1) as f32 / renderer.font_width) as u32;
let new_height = ((new_size.height + 1) as f32 / renderer.font_height) as u32;
// Add 1 here to make sure resizing doesn't change the grid size on startup // Add 1 here to make sure resizing doesn't change the grid size on startup
BRIDGE.queue_command(UiCommand::Resize { width: new_width as i64, height: new_height as i64 }); BRIDGE.queue_command(UiCommand::Resize { width: new_width, height: new_height });
} }
} }
pub fn ui_loop() { pub fn ui_loop() {
let event_loop = EventLoop::<()>::with_user_event(); let sdl_context = sdl2::init().expect("Failed to initialize sdl2");
let video_subsystem = sdl_context.video().expect("Failed to create sdl video subsystem");
let mut renderer = Renderer::new();
let (width, height) = INITIAL_DIMENSIONS; let (width, height) = INITIAL_DIMENSIONS;
let logical_size = LogicalSize::new(
(width as f32 * renderer.font_width) as f64, let mut renderer = Renderer::new();
(height as f32 * renderer.font_height + 1.0) as f64 let logical_size = LogicalSize {
); width: (width as f32 * renderer.font_width) as u32,
height: (height as f32 * renderer.font_height + 1.0) as u32
let icon = {
let icon_data = Asset::get("nvim.ico").expect("Failed to read icon data");
let icon = load_from_memory(&icon_data).expect("Failed to parse icon data");
let (width, height) = icon.dimensions();
let mut rgba = Vec::with_capacity((width * height) as usize * 4);
for (_, _, pixel) in icon.pixels() {
rgba.extend_from_slice(&pixel.to_rgba().0);
}
Icon::from_rgba(rgba, width, height).expect("Failed to create icon object")
}; };
info!("icon created");
let mut title = "Neovide".to_string(); // let icon = {
let window = Arc::new(WindowBuilder::new() // let icon_data = Asset::get("nvim.ico").expect("Failed to read icon data");
.with_title(&title) // let icon = load_from_memory(&icon_data).expect("Failed to parse icon data");
.with_inner_size(logical_size) // let (width, height) = icon.dimensions();
.with_window_icon(Some(icon)) // let mut rgba = Vec::with_capacity((width * height) as usize * 4);
.build(&event_loop) // for (_, _, pixel) in icon.pixels() {
.expect("Failed to create window")); // rgba.extend_from_slice(&pixel.to_rgba().0);
// }
// Icon::from_rgba(rgba, width, height).expect("Failed to create icon object")
// };
// info!("icon created");
#[cfg(target_os = "windows")]
windows_fix_dpi();
sdl2::hint::set("SDL_MOUSE_FOCUS_CLICKTHROUGH", "1");
let mut window = video_subsystem.window("Neovide", logical_size.width, logical_size.height)
.position_centered()
.allow_highdpi()
.resizable()
.vulkan()
.build()
.expect("Failed to create window");
info!("window created"); info!("window created");
let mut skulpin_renderer = RendererBuilder::new() let mut skulpin_renderer = RendererBuilder::new()
.prefer_integrated_gpu() .prefer_integrated_gpu()
.use_vulkan_debug_layer(true) .use_vulkan_debug_layer(true)
.present_mode_priority(vec![PresentMode::Mailbox, PresentMode::Immediate]) .present_mode_priority(vec![PresentMode::Immediate])
.coordinate_system(CoordinateSystem::Logical) .coordinate_system(CoordinateSystem::Logical)
.build(&window) .build(&window)
.expect("Failed to create renderer"); .expect("Failed to create renderer");
info!("renderer created"); info!("renderer created");
let mut mouse_down = false; let mut mouse_down = false;
let mut mouse_pos = (0, 0); let mut mouse_position = LogicalSize {
width: 0,
height: 0
};
let mut allow_next_char = false; let mut title = "Neovide".to_string();
let mut next_char_modifiers = ModifiersState::empty(); let mut previous_size = LogicalSize::new(&window).unwrap();
let mut previous_dpis = dpis(&window).unwrap();
info!("Starting window event loop"); info!("Starting window event loop");
event_loop.run(move |event, _window_target, control_flow| { let mut event_pump = sdl_context.event_pump().expect("Could not create sdl event pump");
trace!("Window Event: {:?}", event); 'running: loop {
match event { let frame_start = Instant::now();
Event::NewEvents(StartCause::Init) |
Event::NewEvents(StartCause::ResumeTimeReached { .. }) => { let editor_title = { EDITOR.lock().title.clone() };
window.request_redraw() if title != editor_title {
}, title = editor_title;
window.set_title(&title).expect("Could not set title");
Event::WindowEvent { }
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
Event::WindowEvent {
event: WindowEvent::Resized(new_size),
..
} => {
handle_new_grid_size(new_size.to_logical(window.scale_factor()), &renderer)
},
Event::WindowEvent {
event: WindowEvent::KeyboardInput {
input,
..
},
..
} => {
// Only interpret 'char' events when we get a previous event without a virtual
// keycode (which we ignore for KeyboardInput events).
// This is a hack so we don't lose a bunch of input events on Linux
if input.virtual_keycode == None {
allow_next_char = true;
}else {
allow_next_char = false;
}
next_char_modifiers = input.modifiers;
if let Some(keybinding_string) = construct_keybinding_string(input) let mut ignore_text_input = false;
.map(UiCommand::Keyboard) { for event in event_pump.poll_iter() {
BRIDGE.queue_command(keybinding_string); match event {
} Event::Quit {..} => break 'running,
}, Event::Window {..} => REDRAW_SCHEDULER.queue_next_frame(),
Event::KeyDown { keycode: Some(keycode), keymod: modifiers, .. } => {
Event::WindowEvent { if let Some((key_text, special)) = parse_keycode(keycode) {
event: WindowEvent::ReceivedCharacter(c), let will_text_input =
..
} => { !modifiers.contains(Mod::LCTRLMOD) &&
if allow_next_char { !modifiers.contains(Mod::RCTRLMOD) &&
next_char_modifiers.remove(ModifiersState::SHIFT); !modifiers.contains(Mod::LALTMOD) &&
let keybinding = super::bridge::append_modifiers(next_char_modifiers, &c.to_string(), false); !modifiers.contains(Mod::RALTMOD) &&
BRIDGE.queue_command(UiCommand::Keyboard(keybinding)); !modifiers.contains(Mod::LGUIMOD) &&
} !modifiers.contains(Mod::RGUIMOD);
}, if will_text_input && !special {
break;
}
Event::WindowEvent { BRIDGE.queue_command(UiCommand::Keyboard(append_modifiers(modifiers, key_text, special)));
event: WindowEvent::CursorMoved { ignore_text_input = true;
position, }
..
}, },
.. Event::TextInput { text, .. } => {
} => { if ignore_text_input {
let position: LogicalPosition<f64> = position.to_logical(window.scale_factor()); ignore_text_input = false;
let grid_y = (position.x / renderer.font_width as f64) as i64; } else {
let grid_x = (position.y / renderer.font_height as f64) as i64; let text = if text == "<" {
let (old_x, old_y) = mouse_pos; String::from("<lt>")
mouse_pos = (grid_x, grid_y); } else {
if mouse_down && (old_x != grid_x || old_y != grid_y) { text
BRIDGE.queue_command(UiCommand::Drag(grid_x, grid_y)); };
} BRIDGE.queue_command(UiCommand::Keyboard(text))
} }
Event::WindowEvent {
event: WindowEvent::MouseInput {
state,
..
}, },
.. Event::MouseMotion { x, y, .. } => {
} => { let previous_position = mouse_position;
let input_type = match (state, mouse_down) { mouse_position = LogicalSize::from_physical_size_tuple((
(ElementState::Pressed, false) => { (x as f32 / renderer.font_width) as u32,
mouse_down = true; (y as f32 / renderer.font_height) as u32
Some("press") ),
}, &window
(ElementState::Released, true) => { ).expect("Could not calculate logical mouse position");
mouse_down = false; if mouse_down && previous_position != mouse_position {
Some("release") BRIDGE.queue_command(UiCommand::Drag(mouse_position.width, mouse_position.height));
}, }
_ => None
};
if let Some(input_type) = input_type {
let (grid_x, grid_y) = mouse_pos;
BRIDGE.queue_command(UiCommand::MouseButton { action: input_type.to_string(), position: (grid_x, grid_y) });
}
}
Event::WindowEvent {
event: WindowEvent::MouseWheel {
delta: MouseScrollDelta::LineDelta(horizontal, vertical),
..
}, },
.. Event::MouseButtonDown { .. } => {
} => { BRIDGE.queue_command(UiCommand::MouseButton { action: String::from("press"), position: (mouse_position.width, mouse_position.height) });
let vertical_input_type = if vertical > 0.0 { mouse_down = true;
Some("up") },
} else if vertical < 0.0 { Event::MouseButtonUp { .. } => {
Some("down") BRIDGE.queue_command(UiCommand::MouseButton { action: String::from("release"), position: (mouse_position.width, mouse_position.height) });
} else { mouse_down = false;
None },
}; Event::MouseWheel { x, y, .. } => {
let vertical_input_type = if y > 0 {
if let Some(input_type) = vertical_input_type { Some("up")
BRIDGE.queue_command(UiCommand::Scroll { direction: input_type.to_string(), position: mouse_pos }); } else if y < 0 {
} Some("down")
} else {
None
};
if let Some(input_type) = vertical_input_type {
BRIDGE.queue_command(UiCommand::Scroll { direction: input_type.to_string(), position: (mouse_position.width, mouse_position.height) });
}
let horizontal_input_type = if horizontal > 0.0 { let horizontal_input_type = if x > 0 {
Some("right") Some("right")
} else if horizontal < 0.0 { } else if x < 0 {
Some("left") Some("left")
} else { } else {
None None
}; };
if let Some(input_type) = horizontal_input_type { if let Some(input_type) = horizontal_input_type {
BRIDGE.queue_command(UiCommand::Scroll { direction: input_type.to_string(), position: mouse_pos }); BRIDGE.queue_command(UiCommand::Scroll { direction: input_type.to_string(), position: (mouse_position.width, mouse_position.height) });
} }
},
_ => {}
} }
}
Event::RedrawRequested { .. } => { let new_size = LogicalSize::new(&window).unwrap();
let frame_start = Instant::now(); if previous_size != new_size {
handle_new_grid_size(new_size, &renderer);
previous_size = new_size;
}
let editor_title = { EDITOR.lock().title.clone() }; let new_dpis = dpis(&window).unwrap();
if title != editor_title { if previous_dpis != new_dpis {
title = editor_title; let physical_size = PhysicalSize::new(&window);
window.set_title(&title); window.set_size(
} (physical_size.width as f32 * new_dpis.0 / previous_dpis.0) as u32,
(physical_size.height as f32 * new_dpis.1 / previous_dpis.1) as u32).unwrap();
previous_dpis = new_dpis;
}
if REDRAW_SCHEDULER.should_draw() || SETTINGS.get("no_idle").read_bool() { debug!("Render Triggered");
debug!("Render Triggered"); if REDRAW_SCHEDULER.should_draw() || SETTINGS.get("no_idle").read_bool() {
if skulpin_renderer.draw(&window, |canvas, coordinate_system_helper| { if skulpin_renderer.draw(&window, |canvas, coordinate_system_helper| {
if renderer.draw(canvas, coordinate_system_helper) { if renderer.draw(canvas, coordinate_system_helper) {
handle_new_grid_size(window.inner_size().to_logical(window.scale_factor()), &renderer) handle_new_grid_size(new_size, &renderer)
}
}).is_err() {
error!("Render failed. Closing");
*control_flow = ControlFlow::Exit;
return;
}
} }
}).is_err() {
error!("Render failed. Closing");
break;
}
}
*control_flow = ControlFlow::WaitUntil(frame_start + Duration::from_secs_f32(1.0 / 60.0)); let elapsed = frame_start.elapsed();
}, let refresh_rate = SETTINGS.get("refresh_rate").read_u16() as f32;
let frame_length = Duration::from_secs_f32(1.0 / refresh_rate);
_ => {} if elapsed < frame_length {
sleep(frame_length - elapsed);
} }
}) }
} }

Loading…
Cancel
Save