minor refactoring

macos-click-through
keith 4 years ago
parent 4643a24da1
commit 3d1f797688

@ -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"

@ -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;
}

@ -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 <nomodeline> 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();
}

@ -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(),

@ -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,

@ -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
}
}

@ -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(&region, 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(&region);
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(

@ -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: <width>x<height>",
input
);
input
.split('x')
.map(|dimension| {
dimension
.parse::<u64>()
.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::<Result<Vec<_>, &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<UiCommand>,
) {
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<Vec<DrawCommand>>,
window_command_receiver: Receiver<WindowCommand>,
ui_command_sender: Sender<UiCommand>,
running: Arc<AtomicBool>,
) {
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);
}

@ -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<UiCommand>,
) {
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<UiCommand>,
window_command_receiver: Receiver<WindowCommand>,
running: Arc<AtomicBool>,
}
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: <width>x<height>",
input
);
input
.split('x')
.map(|dimension| {
dimension
.parse::<u64>()
.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::<Result<Vec<_>, &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<UiCommand>,
batched_draw_command_receiver: Receiver<Vec<DrawCommand>>,
running: Arc<AtomicBool>,
) -> 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<bool> {
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<Event> = 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<WindowCommand> = 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<bool> {
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::<WindowSettings>().no_idle {
debug!("Render Triggered");
let renderer = &mut self.renderer;
self.skulpin_renderer.draw(
&sdl_window_wrapper,
@ -442,50 +364,69 @@ 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"));
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);
}
pub fn start_window(
batched_draw_command_receiver: Receiver<Vec<DrawCommand>>,
pub fn start_loop(
window_command_receiver: Receiver<WindowCommand>,
ui_command_sender: Sender<UiCommand>,
running: Arc<AtomicBool>,
) {
let mut window = WindowWrapper::new(
ui_command_sender.clone(),
batched_draw_command_receiver,
running.clone(),
);
logical_size: LogicalSize,
renderer: Renderer
) {
sdl2::hint::set("SDL_MOUSE_FOCUS_CLICKTHROUGH", "1");
info!("Starting window event loop");
let mut event_pump = window
.context
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");
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,
fullscreen: false,
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();
loop {
@ -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::<WindowSettings>().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;
}

@ -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);
}
Loading…
Cancel
Save