remove msg, add logging, add startup commands, add no-idle command

macos-click-through
Keith Simmons 5 years ago
parent bd3967ad0a
commit 5912b82cea

1892
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -12,7 +12,6 @@ skribo = { git = "https://github.com/linebender/skribo" }
lru = "0.4.3"
skulpin = { git = "https://github.com/kethku/skulpin", branch = "winit_20" }
derive-new = "0.5"
env_logger = "0.7.1"
rmpv = "0.4.2"
rust-embed = { version = "5.2.0", features = ["debug-embed"] }
image = "0.22.3"
@ -21,9 +20,10 @@ tokio = { version = "0.2.9", features = [ "blocking", "process", "time" ] }
async-trait = "0.1.18"
lazy_static = "1.4.0"
unicode-segmentation = "1.6.0"
[target.'cfg(not(linux))'.dependencies]
msgbox = { version = "0.4.0"}
log = "0.4.8"
flexi_logger = { version = "0.14.6", default-features = false }
anyhow = "1.0.26"
crossbeam = "0.7"
[build-dependencies]
winres = "0.1.11"

@ -3,6 +3,7 @@ use nvim_rs::{Neovim, Handler, compat::tokio::Compat};
use async_trait::async_trait;
use tokio::process::ChildStdin;
use tokio::sync::mpsc::{unbounded_channel, UnboundedSender};
use log::trace;
use crate::error_handling::ResultPanicExplanation;
use crate::editor::EDITOR;
@ -32,7 +33,6 @@ impl NeovimHandler {
pub fn handle_redraw_event(&self, event: RedrawEvent) {
self.sender.send(event)
.unwrap_or_explained_panic(
"Could not process neovim event.",
"The main thread for Neovide has closed the communication channel preventing a neovim event from being processed.");
}
}
@ -43,8 +43,9 @@ impl Handler for NeovimHandler {
type Writer = Compat<ChildStdin>;
async fn handle_notify(&self, event_name: String, arguments: Vec<Value>, _neovim: Neovim<Compat<ChildStdin>>) {
trace!("Neovim notification: {:?}", &event_name);
let parsed_events = parse_neovim_event(&event_name, &arguments)
.unwrap_or_explained_panic("Could not parse event", "Could not parse event from neovim");
.unwrap_or_explained_panic("Could not parse event from neovim");
for event in parsed_events {
self.handle_redraw_event(event);
}

@ -11,13 +11,16 @@ use nvim_rs::{create::tokio as create, UiAttachOptions};
use tokio::runtime::Runtime;
use tokio::process::Command;
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
use log::{info, error, trace};
pub use events::*;
pub use keybindings::*;
pub use ui_commands::UiCommand;
use handler::NeovimHandler;
use crate::error_handling::ResultPanicExplanation;
use crate::settings::SETTINGS;
use crate::INITIAL_DIMENSIONS;
use handler::NeovimHandler;
lazy_static! {
pub static ref BRIDGE: Bridge = Bridge::new();
@ -31,8 +34,10 @@ fn set_windows_creation_flags(cmd: &mut Command) {
fn create_nvim_command() -> Command {
let mut cmd = Command::new("nvim");
let args = SETTINGS.neovim_arguments.swap(None).unwrap();
cmd.arg("--embed")
.args(std::env::args().skip(1))
.args(args.iter().skip(1))
.stderr(Stdio::inherit());
#[cfg(target_os = "windows")]
@ -56,14 +61,15 @@ async fn drain(receiver: &mut UnboundedReceiver<UiCommand>) -> Option<Vec<UiComm
async fn start_process(mut receiver: UnboundedReceiver<UiCommand>) {
let (width, height) = INITIAL_DIMENSIONS;
let (mut nvim, io_handler, _) = create::new_child_cmd(&mut create_nvim_command(), NeovimHandler::new()).await
.unwrap_or_explained_panic("Could not create nvim process", "Could not locate or start the neovim process");
.unwrap_or_explained_panic("Could not locate or start the neovim process");
tokio::spawn(async move {
info!("Close watcher started");
match io_handler.await {
Err(join_error) => eprintln!("Error joining IO loop: '{}'", join_error),
Ok(Err(error)) => {
if !error.is_channel_closed() {
eprintln!("Error: '{}'", error);
error!("Error: '{}'", error);
}
},
Ok(Ok(())) => {}
@ -73,24 +79,26 @@ async fn start_process(mut receiver: UnboundedReceiver<UiCommand>) {
if let Ok(Value::Integer(correct_version)) = nvim.eval("has(\"nvim-0.4\")").await {
if correct_version.as_i64() != Some(1) {
println!("Neovide requires version 0.4 or higher");
error!("Neovide requires version 0.4 or higher");
std::process::exit(0);
}
} else {
println!("Neovide requires version 0.4 or higher");
error!("Neovide requires version 0.4 or higher");
std::process::exit(0);
};
nvim.set_var("neovide", Value::Boolean(true)).await
.unwrap_or_explained_panic("Could not communicate.", "Could not communicate with neovim process");
.unwrap_or_explained_panic("Could not communicate with neovim process");
let mut options = UiAttachOptions::new();
options.set_linegrid_external(true);
options.set_rgb(true);
nvim.ui_attach(width as i64, height as i64, &options).await
.unwrap_or_explained_panic("Could not attach.", "Could not attach ui to neovim process");
.unwrap_or_explained_panic("Could not attach ui to neovim process");
info!("Neovim process attached");
let nvim = Arc::new(nvim);
tokio::spawn(async move {
info!("UiCommand processor started");
loop {
if let Some(commands) = drain(&mut receiver).await {
let (resize_list, other_commands): (Vec<UiCommand>, Vec<UiCommand>) = commands
@ -103,6 +111,7 @@ async fn start_process(mut receiver: UnboundedReceiver<UiCommand>) {
let nvim = nvim.clone();
tokio::spawn(async move {
trace!("Executing UiCommand: {:?}", &command);
command.execute(&nvim).await;
});
}
@ -131,9 +140,9 @@ impl Bridge {
}
pub fn queue_command(&self, command: UiCommand) {
trace!("UiCommand queued: {:?}", &command);
self.sender.send(command)
.unwrap_or_explained_panic(
"Could Not Send UI Command",
"Could not send UI command from the window system to the neovim process.");
}
}

@ -6,6 +6,7 @@ use std::sync::{Arc, Mutex};
use skulpin::skia_safe::colors;
use unicode_segmentation::UnicodeSegmentation;
use log::trace;
pub use cursor::{Cursor, CursorShape, CursorMode};
pub use style::{Colors, Style};
@ -71,9 +72,18 @@ impl Editor {
RedrawEvent::ModeInfoSet { cursor_modes } => self.cursor.mode_list = cursor_modes,
RedrawEvent::OptionSet { gui_option } => self.set_option(gui_option),
RedrawEvent::ModeChange { mode_index } => self.cursor.change_mode(mode_index, &self.defined_styles),
RedrawEvent::BusyStart => self.cursor.enabled = false,
RedrawEvent::BusyStop => self.cursor.enabled = true,
RedrawEvent::Flush => REDRAW_SCHEDULER.queue_next_frame(),
RedrawEvent::BusyStart => {
trace!("Cursor off");
self.cursor.enabled = false;
},
RedrawEvent::BusyStop => {
trace!("Cursor on");
self.cursor.enabled = true;
},
RedrawEvent::Flush => {
trace!("Image flushed");
REDRAW_SCHEDULER.queue_next_frame();
},
RedrawEvent::Resize { width, height, .. } => self.resize((width, height)),
RedrawEvent::DefaultColorsSet { colors } => self.default_style = Arc::new(Style::new(colors)),
RedrawEvent::HighlightAttributesDefine { id, style } => { self.defined_styles.insert(id, Arc::new(style)); },
@ -157,6 +167,8 @@ impl Editor {
let (width, height) = self.size;
self.dirty = vec![vec![false; width as usize]; height as usize];
self.should_clear = false;
trace!("Draw commands sent");
(draw_commands, should_clear)
}
@ -234,14 +246,17 @@ impl Editor {
}
}
}
trace!("Region scrolled");
}
fn resize(&mut self, new_size: (u64, u64)) {
trace!("Editor resized");
self.size = new_size;
self.clear();
}
fn clear(&mut self) {
trace!("Editor cleared");
let (width, height) = self.size;
self.grid = vec![vec![None; width as usize]; height as usize];
self.dirty = vec![vec![true; width as usize]; height as usize];
@ -249,6 +264,7 @@ impl Editor {
}
fn set_option(&mut self, gui_option: GuiOption) {
trace!("Option set {:?}", &gui_option);
match gui_option {
GuiOption::GuiFont(font_description) => {
let parts: Vec<&str> = font_description.split(":").collect();

@ -1,24 +1,20 @@
use msgbox::IconType;
use log::error;
fn show_error(title: &str, explanation: &str) -> ! {
if cfg!(target_os = "linux") {
panic!("{}: {}", title, explanation);
} else {
msgbox::create(title, explanation, IconType::Error);
panic!(explanation.to_string());
}
fn show_error(explanation: &str) -> ! {
error!("{}", explanation);
panic!(explanation.to_string());
}
pub trait ResultPanicExplanation<T, E: ToString> {
fn unwrap_or_explained_panic(self, title: &str, explanation: &str) -> T;
fn unwrap_or_explained_panic(self, explanation: &str) -> T;
}
impl<T, E: ToString> ResultPanicExplanation<T, E> for Result<T, E> {
fn unwrap_or_explained_panic(self, title: &str, explanation: &str) -> T {
fn unwrap_or_explained_panic(self, explanation: &str) -> T {
match self {
Err(error) => {
let explanation = format!("{}: {}", explanation, error.to_string());
show_error(title, &explanation);
show_error(&explanation);
},
Ok(content) => content
}
@ -26,14 +22,14 @@ impl<T, E: ToString> ResultPanicExplanation<T, E> for Result<T, E> {
}
pub trait OptionPanicExplanation<T> {
fn unwrap_or_explained_panic(self, title: &str, explanation: &str) -> T;
fn unwrap_or_explained_panic(self, explanation: &str) -> T;
}
impl<T> OptionPanicExplanation<T> for Option<T> {
fn unwrap_or_explained_panic(self, title: &str, explanation: &str) -> T {
fn unwrap_or_explained_panic(self, explanation: &str) -> T {
match self {
None => {
show_error(title, explanation);
show_error(explanation);
},
Some(content) => content
}

@ -6,19 +6,39 @@ mod window;
mod renderer;
mod error_handling;
mod redraw_scheduler;
mod settings;
#[macro_use] extern crate derive_new;
#[macro_use] extern crate rust_embed;
#[macro_use] extern crate lazy_static;
use std::sync::atomic::Ordering;
use lazy_static::initialize;
use flexi_logger::{Logger, Criterion, Naming, Cleanup};
use bridge::BRIDGE;
use window::ui_loop;
use settings::SETTINGS;
pub const INITIAL_DIMENSIONS: (u64, u64) = (100, 50);
fn main() {
SETTINGS.neovim_arguments.store(Some(std::env::args().filter_map(|arg| {
if arg == "--log" {
Logger::with_str("neovide")
.log_to_file()
.rotate(Criterion::Size(10_000_000), Naming::Timestamps, Cleanup::KeepLogFiles(1))
.start()
.expect("Could not start logger");
return None;
} else if arg == "--noIdle" {
SETTINGS.no_idle.store(true, Ordering::Relaxed);
return None;
}
return Some(arg.into());
}).collect::<Vec<String>>()));
initialize(&BRIDGE);
ui_loop();
}

@ -2,6 +2,8 @@ use std::sync::Mutex;
use std::sync::atomic::{AtomicU16, Ordering};
use std::time::Instant;
use log::trace;
lazy_static! {
pub static ref REDRAW_SCHEDULER: RedrawScheduler = RedrawScheduler::new();
}
@ -15,6 +17,7 @@ pub struct RedrawScheduler {
impl RedrawScheduler {
pub fn new() -> RedrawScheduler {
RedrawScheduler {
frames_queued: AtomicU16::new(1),
scheduled_frame: Mutex::new(None)
@ -22,6 +25,7 @@ impl RedrawScheduler {
}
pub fn schedule(&self, new_scheduled: Instant) {
trace!("Redraw scheduled for {:?}", new_scheduled);
let mut scheduled_frame = self.scheduled_frame.lock().unwrap();
if let Some(previous_scheduled) = scheduled_frame.clone() {
if new_scheduled < previous_scheduled {
@ -33,6 +37,7 @@ impl RedrawScheduler {
}
pub fn queue_next_frame(&self) {
trace!("Next frame queued");
self.frames_queued.store(BUFFER_FRAMES, Ordering::Relaxed);
}

@ -5,6 +5,8 @@ use skulpin::skia_safe::{TextBlob, Font as SkiaFont, Typeface, TextBlobBuilder,
use font_kit::{source::SystemSource, metrics::Metrics, properties::{Properties, Weight, Style, Stretch}, family_name::FamilyName, font::Font, };
use skribo::{LayoutSession, FontRef as SkriboFont, FontFamily, FontCollection, TextStyle};
use log::trace;
const STANDARD_CHARACTER_STRING: &'static str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
const MONOSPACE_FONT: &'static str = "Fira Code Regular Nerd Font Complete.otf";
@ -208,6 +210,7 @@ impl CachingShaper {
}
pub fn change_font(&mut self, font_name: Option<&str>, base_size: Option<f32>) {
trace!("Font changed {:?} {:?}", &font_name, &base_size);
self.font_name = font_name.map(|name| name.to_string());
self.base_size = base_size.unwrap_or(DEFAULT_FONT_SIZE);
self.font_set = FontSet::new(font_name);

@ -3,7 +3,7 @@ use std::sync::Arc;
use skulpin::CoordinateSystemHelper;
use skulpin::skia_safe::{Canvas, Paint, Surface, Budgeted, Rect, colors};
use skulpin::skia_safe::gpu::SurfaceOrigin;
use unicode_segmentation::UnicodeSegmentation;
use log::trace;
mod caching_shaper;
mod cursor_renderer;
@ -11,7 +11,7 @@ mod cursor_renderer;
pub use caching_shaper::CachingShaper;
use cursor_renderer::CursorRenderer;
use crate::editor::{EDITOR, Style, Colors};
use crate::editor::{EDITOR, Style};
pub struct Renderer {
surface: Option<Surface>,
@ -44,7 +44,7 @@ impl Renderer {
self.font_height = font_height;
}
fn compute_text_region(&self, text: &str, grid_pos: (u64, u64), cell_width: u64, size: u16) -> Rect {
fn compute_text_region(&self, grid_pos: (u64, u64), cell_width: u64, size: u16) -> Rect {
let (grid_x, grid_y) = grid_pos;
let x = grid_x as f32 * self.font_width;
let y = grid_y as f32 * self.font_height;
@ -53,8 +53,8 @@ impl Renderer {
Rect::new(x, y, x + width, y + height)
}
fn draw_background(&mut self, canvas: &mut Canvas, text: &str, grid_pos: (u64, u64), cell_width:u64, size: u16, style: &Option<Arc<Style>>, default_style: &Arc<Style>) {
let region = self.compute_text_region(text, grid_pos, cell_width, size);
fn draw_background(&mut self, canvas: &mut Canvas, grid_pos: (u64, u64), cell_width:u64, size: u16, style: &Option<Arc<Style>>, default_style: &Arc<Style>) {
let region = self.compute_text_region(grid_pos, cell_width, size);
let style = style.as_ref().unwrap_or(default_style);
self.paint.set_color(style.background(&default_style.colors).to_color());
@ -71,7 +71,7 @@ impl Renderer {
canvas.save();
let region = self.compute_text_region(text, grid_pos, cell_width, size);
let region = self.compute_text_region(grid_pos, cell_width, size);
canvas.clip_rect(region, None, Some(false));
@ -99,6 +99,7 @@ impl Renderer {
}
pub fn draw(&mut self, gpu_canvas: &mut Canvas, coordinate_system_helper: &CoordinateSystemHelper) -> bool {
trace!("Rendering");
let ((draw_commands, should_clear), default_style, cursor, font_name, font_size) = {
let mut editor = EDITOR.lock().unwrap();
(
@ -136,7 +137,7 @@ impl Renderer {
coordinate_system_helper.use_logical_coordinates(&mut canvas);
for command in draw_commands.iter() {
self.draw_background(&mut canvas, &command.text, command.grid_position.clone(), command.cell_width, command.scale, &command.style, &default_style);
self.draw_background(&mut canvas, command.grid_position.clone(), command.cell_width, command.scale, &command.style, &default_style);
}
for command in draw_commands.iter() {
self.draw_foreground(&mut canvas, &command.text, command.grid_position.clone(), command.cell_width, command.scale, &command.style, &default_style);

@ -0,0 +1,13 @@
use std::sync::atomic::AtomicBool;
use crossbeam::atomic::AtomicCell;
lazy_static! {
pub static ref SETTINGS: Settings = Settings::default();
}
#[derive(Default)]
pub struct Settings {
pub neovim_arguments: AtomicCell<Option<Vec<String>>>,
pub no_idle: AtomicBool
}

@ -1,18 +1,20 @@
use std::sync::Arc;
use std::sync::atomic::Ordering;
use std::time::{Duration, Instant};
use image::{load_from_memory, GenericImageView, Pixel};
use skulpin::{CoordinateSystem, RendererBuilder, PresentMode};
use skulpin::skia_safe::icu;
use skulpin::winit::dpi::{LogicalSize, LogicalPosition};
use skulpin::winit::dpi::LogicalSize;
use skulpin::winit::event::{ElementState, Event, MouseScrollDelta, StartCause, WindowEvent};
use skulpin::winit::event_loop::{ControlFlow, EventLoop};
use skulpin::winit::window::{Icon, WindowBuilder};
use log::{info, debug, trace, error};
use crate::bridge::{construct_keybinding_string, BRIDGE, UiCommand};
use crate::renderer::Renderer;
use crate::redraw_scheduler::REDRAW_SCHEDULER;
use crate::editor::EDITOR;
use crate::settings::SETTINGS;
use crate::INITIAL_DIMENSIONS;
#[derive(RustEmbed)]
@ -48,6 +50,7 @@ pub fn ui_loop() {
}
Icon::from_rgba(rgba, width, height).expect("Failed to create icon object")
};
info!("icon created");
let mut title = "Neovide".to_string();
let window = Arc::new(WindowBuilder::new()
@ -56,6 +59,7 @@ pub fn ui_loop() {
.with_window_icon(Some(icon))
.build(&event_loop)
.expect("Failed to create window"));
info!("window created");
let mut skulpin_renderer = RendererBuilder::new()
.prefer_integrated_gpu()
@ -64,13 +68,14 @@ pub fn ui_loop() {
.coordinate_system(CoordinateSystem::Logical)
.build(&window)
.expect("Failed to create renderer");
icu::init();
info!("renderer created");
let mut mouse_down = false;
let mut mouse_pos = (0, 0);
info!("Starting window event loop");
event_loop.run(move |event, _window_target, control_flow| {
trace!("Window Event: {:?}", event);
match event {
Event::NewEvents(StartCause::Init) |
Event::NewEvents(StartCause::ResumeTimeReached { .. }) => {
@ -183,13 +188,14 @@ pub fn ui_loop() {
window.set_title(&title);
}
if REDRAW_SCHEDULER.should_draw() {
if REDRAW_SCHEDULER.should_draw() || SETTINGS.no_idle.load(Ordering::Relaxed) {
debug!("Render Triggered");
if let Err(_) = skulpin_renderer.draw(&window, |canvas, coordinate_system_helper| {
if renderer.draw(canvas, coordinate_system_helper) {
handle_new_grid_size(window.inner_size().to_logical(window.scale_factor()), &renderer)
}
}) {
println!("Render failed. Closing");
error!("Render failed. Closing");
*control_flow = ControlFlow::Exit;
return;
}

Loading…
Cancel
Save