diff --git a/Cargo.toml b/Cargo.toml index 14a43f8..51c2bb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,8 +7,9 @@ build = "build.rs" description = "A simple GUI for Neovim." [features] -default = [] +default = ["sdl2"] embed-fonts = [] +sdl2 = ["skulpin/skulpin_sdl2"] [dependencies] euclid = "0.20.7" diff --git a/src/bridge/mod.rs b/src/bridge/mod.rs index c420ae4..659ebd5 100644 --- a/src/bridge/mod.rs +++ b/src/bridge/mod.rs @@ -237,8 +237,7 @@ pub fn start_bridge( } _ => {} }, - Err(error) => { - error!("Notification channel closed: {}", error); + Err(_) => { notification_running.store(false, Ordering::Relaxed); break; } @@ -255,8 +254,7 @@ pub fn start_bridge( Ok(ui_command) => { ui_command.execute(&mut nvim); } - Err(error) => { - error!("Ui command channel closed: {}", error); + Err(_) => { ui_command_running.store(false, Ordering::Relaxed); break; } diff --git a/src/bridge/ui_commands.rs b/src/bridge/ui_commands.rs index 1f5b43e..fbbf894 100644 --- a/src/bridge/ui_commands.rs +++ b/src/bridge/ui_commands.rs @@ -30,7 +30,6 @@ pub enum UiCommand { FileDrop(String), FocusLost, FocusGained, - Quit, #[cfg(windows)] RegisterRightClick, #[cfg(windows)] @@ -97,9 +96,6 @@ impl UiCommand { UiCommand::FocusGained => nvim .command("if exists('#FocusGained') | doautocmd FocusGained | endif") .expect("Focus Gained Failed"), - UiCommand::Quit => { - nvim.command("qa!").ok(); // Ignoring result as it won't succeed since the app closed. - } UiCommand::FileDrop(path) => { nvim.command(format!("e {}", path).as_str()).ok(); } diff --git a/src/editor/mod.rs b/src/editor/mod.rs index 4452fd2..c83250a 100644 --- a/src/editor/mod.rs +++ b/src/editor/mod.rs @@ -313,11 +313,18 @@ impl Editor { .map(|parent| parent.get_width()) .unwrap_or(1); + let anchor_info = AnchorInfo { + anchor_grid_id: 1, // Base Grid + anchor_type: WindowAnchor::NorthWest, + anchor_left: 0.0, + anchor_top: 0.0 + }; + if let Some(window) = self.windows.get_mut(&grid) { window.position( parent_width, window.get_height(), - None, + Some(anchor_info), 0.0, grid_top as f64, ); @@ -327,7 +334,7 @@ impl Editor { grid, parent_width, 1, - None, + Some(anchor_info), 0.0, grid_top as f64, self.draw_command_batcher.clone(), diff --git a/src/main.rs b/src/main.rs index f5f02b8..952aa56 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,7 +27,7 @@ use window::window_geometry; use bridge::start_bridge; use editor::start_editor; -use window::start_window; +use window::create_window; pub const INITIAL_DIMENSIONS: (u64, u64) = (100, 50); @@ -147,7 +147,7 @@ fn main() { batched_draw_command_sender, window_command_sender, ); - start_window( + create_window( batched_draw_command_receiver, window_command_receiver, ui_command_sender, diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index e98838d..eb40cba 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -285,6 +285,13 @@ impl Renderer { .to_color(), ); + root_canvas.save(); + + if let Some(root_window) = self.rendered_windows.get(&1) { + let clip_rect = root_window.pixel_region(self.font_width, self.font_height); + root_canvas.clip_rect(&clip_rect, None, Some(false)); + } + coordinate_system_helper.use_logical_coordinates(root_canvas); let windows: Vec<&mut RenderedWindow> = { @@ -324,6 +331,8 @@ impl Renderer { dt, ); + root_canvas.restore(); + font_changed } } diff --git a/src/renderer/rendered_window.rs b/src/renderer/rendered_window.rs index 3707117..3db30f3 100644 --- a/src/renderer/rendered_window.rs +++ b/src/renderer/rendered_window.rs @@ -29,7 +29,7 @@ fn build_window_surface( parent_image_info.color_space(), ); let surface_origin = SurfaceOrigin::TopLeft; - let mut surface = Surface::new_render_target( + Surface::new_render_target( &mut context, budgeted, &image_info, @@ -38,8 +38,7 @@ fn build_window_surface( None, None, ) - .expect("Could not create surface"); - surface + .expect("Could not create surface") } fn build_background_window_surface( @@ -109,6 +108,18 @@ impl RenderedWindow { } } + pub fn pixel_region(&self, font_width: f32, font_height: f32) -> Rect { + let current_pixel_position = Point::new( + self.grid_current_position.x * font_width, + self.grid_current_position.y * font_height, + ); + + let image_width = (self.grid_width as f32 * font_width) as i32; + let image_height = (self.grid_height as f32 * font_height) as i32; + + Rect::from_point_and_size(current_pixel_position, (image_width, image_height)) + } + pub fn update(&mut self, settings: &RendererSettings, dt: f32) -> bool { if (self.t - 1.0).abs() < std::f32::EPSILON { return false; @@ -139,13 +150,7 @@ impl RenderedWindow { font_height: f32, dt: f32, ) -> (u64, Rect) { - let current_pixel_position = Point::new( - self.grid_current_position.x * font_width, - self.grid_current_position.y * font_height, - ); - - let image_width = (self.grid_width as f32 * font_width) as i32; - let image_height = (self.grid_height as f32 * font_height) as i32; + let pixel_region = self.pixel_region(font_width, font_height); if self.update(settings, dt) { REDRAW_SCHEDULER.queue_next_frame(); @@ -153,12 +158,13 @@ impl RenderedWindow { root_canvas.save(); - let region = Rect::from_point_and_size(current_pixel_position, (image_width, image_height)); - root_canvas.clip_rect(®ion, None, Some(false)); + root_canvas.clip_rect(&pixel_region, None, Some(false)); if self.floating && settings.floating_blur { let blur = blur((2.0, 2.0), None, None, None).unwrap(); - let save_layer_rec = SaveLayerRec::default().backdrop(&blur).bounds(®ion); + let save_layer_rec = SaveLayerRec::default() + .backdrop(&blur) + .bounds(&pixel_region); root_canvas.save_layer(&save_layer_rec); } @@ -172,7 +178,7 @@ impl RenderedWindow { self.background_surface.draw( root_canvas.as_mut(), - (current_pixel_position.x, current_pixel_position.y), + (pixel_region.left(), pixel_region.top()), Some(&paint), ); @@ -181,7 +187,7 @@ impl RenderedWindow { self.foreground_surface.draw( root_canvas.as_mut(), - (current_pixel_position.x, current_pixel_position.y), + (pixel_region.left(), pixel_region.top()), Some(&paint), ); @@ -191,12 +197,7 @@ impl RenderedWindow { root_canvas.restore(); - let window_position = current_pixel_position.clone(); - - ( - self.id, - Rect::from_point_and_size(window_position, (image_width as f32, image_height as f32)), - ) + (self.id, pixel_region) } pub fn handle_window_draw_command( diff --git a/src/window/mod.rs b/src/window/mod.rs new file mode 100644 index 0000000..fb8543d --- /dev/null +++ b/src/window/mod.rs @@ -0,0 +1,113 @@ +#[cfg_attr(feature = "sdl2", path = "sdl2/mod.rs")] +mod window_wrapper; +mod settings; + +use std::sync::Arc; +use std::sync::atomic::AtomicBool; +use std::sync::mpsc::{Sender, Receiver}; + +use skulpin::LogicalSize; + +use crate::INITIAL_DIMENSIONS; +use crate::bridge::UiCommand; +use crate::editor::{DrawCommand, WindowCommand}; +use crate::renderer::Renderer; + +#[cfg(feature = "sdl2")] +pub use window_wrapper::start_loop; + +pub use settings::*; + +pub fn window_geometry() -> Result<(u64, u64), String> { + let prefix = "--geometry="; + + std::env::args() + .find(|arg| arg.starts_with(prefix)) + .map_or(Ok(INITIAL_DIMENSIONS), |arg| { + let input = &arg[prefix.len()..]; + let invalid_parse_err = format!( + "Invalid geometry: {}\nValid format: x", + input + ); + + input + .split('x') + .map(|dimension| { + dimension + .parse::() + .map_err(|_| invalid_parse_err.as_str()) + .and_then(|dimension| { + if dimension > 0 { + Ok(dimension) + } else { + Err("Invalid geometry: Window dimensions should be greater than 0.") + } + }) + }) + .collect::, &str>>() + .and_then(|dimensions| { + if let [width, height] = dimensions[..] { + Ok((width, height)) + } else { + Err(invalid_parse_err.as_str()) + } + }) + .map_err(|msg| msg.to_owned()) + }) +} + +pub fn window_geometry_or_default() -> (u64, u64) { + window_geometry().unwrap_or(INITIAL_DIMENSIONS) +} + +#[cfg(target_os = "windows")] +fn windows_fix_dpi() { + use winapi::shared::windef::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2; + use winapi::um::winuser::SetProcessDpiAwarenessContext; + unsafe { + SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + } +} + +fn handle_new_grid_size( + new_size: LogicalSize, + renderer: &Renderer, + ui_command_sender: &Sender, +) { + if new_size.width > 0 && new_size.height > 0 { + // Add 1 here to make sure resizing doesn't change the grid size on startup + 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; + ui_command_sender + .send(UiCommand::Resize { + width: new_width, + height: new_height, + }) + .ok(); + } +} + +pub fn create_window( + batched_draw_command_receiver: Receiver>, + window_command_receiver: Receiver, + ui_command_sender: Sender, + running: Arc, +) { + let (width, height) = window_geometry_or_default(); + + let renderer = Renderer::new(batched_draw_command_receiver); + let logical_size = LogicalSize { + width: (width as f32 * renderer.font_width) as u32, + height: (height as f32 * renderer.font_height + 1.0) as u32, + }; + + #[cfg(target_os = "windows")] + windows_fix_dpi(); + + start_loop( + window_command_receiver, + ui_command_sender, + running, + logical_size, + renderer); +} diff --git a/src/window.rs b/src/window/sdl2/mod.rs similarity index 65% rename from src/window.rs rename to src/window/sdl2/mod.rs index 23110a9..fa768af 100644 --- a/src/window.rs +++ b/src/window/sdl2/mod.rs @@ -4,9 +4,10 @@ use std::sync::Arc; use std::thread::sleep; use std::time::{Duration, Instant}; -use log::{debug, error, info, trace}; +use log::{debug, error, trace}; use skulpin::ash::prelude::VkResult; use skulpin::sdl2; +use skulpin::sdl2::EventPump; use skulpin::sdl2::event::{Event, WindowEvent}; use skulpin::sdl2::keyboard::Keycode; use skulpin::sdl2::video::FullscreenType; @@ -16,49 +17,24 @@ use skulpin::{ RendererBuilder, Sdl2Window, Window, }; +use super::handle_new_grid_size; +use super::settings::*; use crate::bridge::{produce_neovim_keybinding_string, UiCommand}; -use crate::editor::{DrawCommand, WindowCommand}; +use crate::editor::WindowCommand; use crate::error_handling::ResultPanicExplanation; use crate::redraw_scheduler::REDRAW_SCHEDULER; use crate::renderer::Renderer; use crate::settings::*; -use crate::INITIAL_DIMENSIONS; #[derive(RustEmbed)] #[folder = "assets/"] struct Asset; -#[cfg(target_os = "windows")] -fn windows_fix_dpi() { - use winapi::shared::windef::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2; - use winapi::um::winuser::SetProcessDpiAwarenessContext; - unsafe { - SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); - } -} - -fn handle_new_grid_size( - new_size: LogicalSize, - renderer: &Renderer, - ui_command_sender: &Sender, -) { - 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 - ui_command_sender - .send(UiCommand::Resize { - width: new_width, - height: new_height, - }) - .ok(); - } -} - -struct WindowWrapper { +pub struct Sdl2WindowWrapper { context: Sdl, window: sdl2::video::Window, skulpin_renderer: SkulpinRenderer, + event_pump: EventPump, renderer: Renderer, mouse_down: bool, mouse_position: LogicalSize, @@ -71,121 +47,11 @@ struct WindowWrapper { cached_size: (u32, u32), cached_position: (i32, i32), ui_command_sender: Sender, + window_command_receiver: Receiver, running: Arc, } -pub fn window_geometry() -> Result<(u64, u64), String> { - let prefix = "--geometry="; - - std::env::args() - .find(|arg| arg.starts_with(prefix)) - .map_or(Ok(INITIAL_DIMENSIONS), |arg| { - let input = &arg[prefix.len()..]; - let invalid_parse_err = format!( - "Invalid geometry: {}\nValid format: x", - input - ); - - input - .split('x') - .map(|dimension| { - dimension - .parse::() - .map_err(|_| invalid_parse_err.as_str()) - .and_then(|dimension| { - if dimension > 0 { - Ok(dimension) - } else { - Err("Invalid geometry: Window dimensions should be greater than 0.") - } - }) - }) - .collect::, &str>>() - .and_then(|dimensions| { - if let [width, height] = dimensions[..] { - Ok((width, height)) - } else { - Err(invalid_parse_err.as_str()) - } - }) - .map_err(|msg| msg.to_owned()) - }) -} - -pub fn window_geometry_or_default() -> (u64, u64) { - window_geometry().unwrap_or(INITIAL_DIMENSIONS) -} - -impl WindowWrapper { - pub fn new( - ui_command_sender: Sender, - batched_draw_command_receiver: Receiver>, - running: Arc, - ) -> WindowWrapper { - let context = sdl2::init().expect("Failed to initialize sdl2"); - let video_subsystem = context - .video() - .expect("Failed to create sdl video subsystem"); - video_subsystem.text_input().start(); - - let (width, height) = window_geometry_or_default(); - - let renderer = Renderer::new(batched_draw_command_receiver); - let logical_size = LogicalSize { - width: (width as f32 * renderer.font_width) as u32, - height: (height as f32 * renderer.font_height + 1.0) as u32, - }; - - #[cfg(target_os = "windows")] - windows_fix_dpi(); - sdl2::hint::set("SDL_MOUSE_FOCUS_CLICKTHROUGH", "1"); - - let sdl_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"); - - let skulpin_renderer = { - let sdl_window_wrapper = Sdl2Window::new(&sdl_window); - RendererBuilder::new() - .prefer_integrated_gpu() - .use_vulkan_debug_layer(false) - .present_mode_priority(vec![PresentMode::Immediate]) - .coordinate_system(CoordinateSystem::Logical) - .build(&sdl_window_wrapper) - .expect("Failed to create renderer") - }; - - info!("renderer created"); - - WindowWrapper { - context, - window: sdl_window, - skulpin_renderer, - renderer, - mouse_down: false, - mouse_position: LogicalSize { - width: 0, - height: 0, - }, - mouse_enabled: true, - grid_id_under_mouse: 0, - title: String::from("Neovide"), - previous_size: logical_size, - transparency: 1.0, - fullscreen: false, - cached_size: (0, 0), - cached_position: (0, 0), - ui_command_sender, - running, - } - } - +impl Sdl2WindowWrapper { pub fn toggle_fullscreen(&mut self) { if self.fullscreen { if cfg!(target_os = "windows") { @@ -411,7 +277,63 @@ impl WindowWrapper { REDRAW_SCHEDULER.queue_next_frame(); } - pub fn draw_frame(&mut self, dt: f32) -> VkResult { + fn handle_events(&mut self) { + self.synchronize_settings(); + + let mut keycode = None; + let mut keytext = None; + let mut ignore_text_this_frame = false; + + let window_events: Vec = self.event_pump.poll_iter().collect(); + for event in window_events.into_iter() { + match event { + Event::Quit { .. } => self.handle_quit(), + Event::DropFile { filename, .. } => { + self.ui_command_sender.send(UiCommand::FileDrop(filename)).ok(); + } + Event::KeyDown { + keycode: received_keycode, + .. + } => { + keycode = received_keycode; + } + Event::TextInput { text, .. } => keytext = Some(text), + Event::MouseMotion { x, y, .. } => self.handle_pointer_motion(x, y), + Event::MouseButtonDown { .. } => self.handle_pointer_down(), + Event::MouseButtonUp { .. } => self.handle_pointer_up(), + Event::MouseWheel { x, y, .. } => self.handle_mouse_wheel(x, y), + Event::Window { + win_event: WindowEvent::FocusLost, + .. + } => self.handle_focus_lost(), + Event::Window { + win_event: WindowEvent::FocusGained, + .. + } => { + ignore_text_this_frame = true; // Ignore any text events on the first frame when focus is regained. https://github.com/Kethku/neovide/issues/193 + self.handle_focus_gained(); + } + Event::Window { .. } => REDRAW_SCHEDULER.queue_next_frame(), + _ => {} + } + } + + if !ignore_text_this_frame { + self.handle_keyboard_input(keycode, keytext); + } + + let window_commands: Vec = self.window_command_receiver.try_iter().collect(); + for window_command in window_commands.into_iter() { + match window_command { + WindowCommand::TitleChanged(new_title) => self.handle_title_changed(new_title), + WindowCommand::SetMouseEnabled(mouse_enabled) => { + self.mouse_enabled = mouse_enabled + } + } + } + } + + fn draw_frame(&mut self, dt: f32) -> VkResult { let sdl_window_wrapper = Sdl2Window::new(&self.window); let new_size = sdl_window_wrapper.logical_size(); if self.previous_size != new_size { @@ -419,12 +341,12 @@ impl WindowWrapper { self.previous_size = new_size; } - debug!("Render Triggered"); - let current_size = self.previous_size; let ui_command_sender = self.ui_command_sender.clone(); if REDRAW_SCHEDULER.should_draw() || SETTINGS.get::().no_idle { + debug!("Render Triggered"); + let renderer = &mut self.renderer; self.skulpin_renderer.draw( &sdl_window_wrapper, @@ -442,49 +364,68 @@ impl WindowWrapper { } } -#[derive(Clone)] -struct WindowSettings { - refresh_rate: u64, - transparency: f32, - no_idle: bool, - fullscreen: bool, -} - -pub fn initialize_settings() { - let no_idle = SETTINGS - .neovim_arguments - .contains(&String::from("--noIdle")); +pub fn start_loop( + window_command_receiver: Receiver, + ui_command_sender: Sender, + running: Arc, + logical_size: LogicalSize, + renderer: Renderer + ) { + sdl2::hint::set("SDL_MOUSE_FOCUS_CLICKTHROUGH", "1"); + + let context = sdl2::init().expect("Failed to initialize sdl2"); + let video_subsystem = context + .video() + .expect("Failed to create sdl video subsystem"); + video_subsystem.text_input().start(); + + let sdl_window = video_subsystem + .window("Neovide", logical_size.width, logical_size.height) + .position_centered() + .allow_highdpi() + .resizable() + .vulkan() + .build() + .expect("Failed to create window"); + + let skulpin_renderer = { + let sdl_window_wrapper = Sdl2Window::new(&sdl_window); + RendererBuilder::new() + .prefer_integrated_gpu() + .use_vulkan_debug_layer(false) + .present_mode_priority(vec![PresentMode::Immediate]) + .coordinate_system(CoordinateSystem::Logical) + .build(&sdl_window_wrapper) + .expect("Failed to create renderer") + }; + + let event_pump = context + .event_pump() + .expect("Could not create sdl event pump"); - SETTINGS.set(&WindowSettings { - refresh_rate: 60, + let mut window_wrapper = Sdl2WindowWrapper { + context, + window: sdl_window, + skulpin_renderer, + renderer, + event_pump, + mouse_down: false, + mouse_position: LogicalSize { + width: 0, + height: 0, + }, + mouse_enabled: true, + grid_id_under_mouse: 0, + title: String::from("Neovide"), + previous_size: logical_size, transparency: 1.0, - no_idle, fullscreen: false, - }); - - register_nvim_setting!("refresh_rate", WindowSettings::refresh_rate); - register_nvim_setting!("transparency", WindowSettings::transparency); - register_nvim_setting!("no_idle", WindowSettings::no_idle); - register_nvim_setting!("fullscreen", WindowSettings::fullscreen); -} - -pub fn start_window( - batched_draw_command_receiver: Receiver>, - window_command_receiver: Receiver, - ui_command_sender: Sender, - running: Arc, -) { - let mut window = WindowWrapper::new( - ui_command_sender.clone(), - batched_draw_command_receiver, - running.clone(), - ); - - info!("Starting window event loop"); - let mut event_pump = window - .context - .event_pump() - .expect("Could not create sdl event pump"); + cached_size: (0, 0), + cached_position: (0, 0), + ui_command_sender, + window_command_receiver, + running: running.clone(), + }; let mut was_animating = false; let mut previous_frame_start = Instant::now(); @@ -495,49 +436,6 @@ pub fn start_window( let frame_start = Instant::now(); - window.synchronize_settings(); - - let mut keycode = None; - let mut keytext = None; - let mut ignore_text_this_frame = false; - - for event in event_pump.poll_iter() { - match event { - Event::Quit { .. } => window.handle_quit(), - Event::DropFile { filename, .. } => { - ui_command_sender.send(UiCommand::FileDrop(filename)).ok(); - } - Event::KeyDown { - keycode: received_keycode, - .. - } => { - keycode = received_keycode; - } - Event::TextInput { text, .. } => keytext = Some(text), - Event::MouseMotion { x, y, .. } => window.handle_pointer_motion(x, y), - Event::MouseButtonDown { .. } => window.handle_pointer_down(), - Event::MouseButtonUp { .. } => window.handle_pointer_up(), - Event::MouseWheel { x, y, .. } => window.handle_mouse_wheel(x, y), - Event::Window { - win_event: WindowEvent::FocusLost, - .. - } => window.handle_focus_lost(), - Event::Window { - win_event: WindowEvent::FocusGained, - .. - } => { - ignore_text_this_frame = true; // Ignore any text events on the first frame when focus is regained. https://github.com/Kethku/neovide/issues/193 - window.handle_focus_gained(); - } - Event::Window { .. } => REDRAW_SCHEDULER.queue_next_frame(), - _ => {} - } - } - - if !ignore_text_this_frame { - window.handle_keyboard_input(keycode, keytext); - } - let refresh_rate = { SETTINGS.get::().refresh_rate as f32 }; let dt = if was_animating { previous_frame_start.elapsed().as_secs_f32() @@ -545,16 +443,9 @@ pub fn start_window( 1.0 / refresh_rate }; - for window_command in window_command_receiver.try_iter() { - match window_command { - WindowCommand::TitleChanged(new_title) => window.handle_title_changed(new_title), - WindowCommand::SetMouseEnabled(mouse_enabled) => { - window.mouse_enabled = mouse_enabled - } - } - } + window_wrapper.handle_events(); - match window.draw_frame(dt) { + match window_wrapper.draw_frame(dt) { Ok(animating) => { was_animating = animating; } diff --git a/src/window/settings.rs b/src/window/settings.rs new file mode 100644 index 0000000..62d36b1 --- /dev/null +++ b/src/window/settings.rs @@ -0,0 +1,27 @@ +use crate::settings::*; + +#[derive(Clone)] +pub struct WindowSettings { + pub refresh_rate: u64, + pub transparency: f32, + pub no_idle: bool, + pub fullscreen: bool, +} + +pub fn initialize_settings() { + let no_idle = SETTINGS + .neovim_arguments + .contains(&String::from("--noIdle")); + + SETTINGS.set(&WindowSettings { + refresh_rate: 60, + transparency: 1.0, + no_idle, + fullscreen: false, + }); + + register_nvim_setting!("refresh_rate", WindowSettings::refresh_rate); + register_nvim_setting!("transparency", WindowSettings::transparency); + register_nvim_setting!("no_idle", WindowSettings::no_idle); + register_nvim_setting!("fullscreen", WindowSettings::fullscreen); +}