mirror of https://github.com/sgoudham/neovide.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
235 lines
8.0 KiB
Rust
235 lines
8.0 KiB
Rust
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
|
|
|
#[cfg(not(test))]
|
|
use flexi_logger::{Cleanup, Criterion, Duplicate, Logger, Naming};
|
|
|
|
// Test naming occasionally uses camelCase with underscores to separate sections of
|
|
// the test name.
|
|
#[cfg_attr(test, allow(non_snake_case))]
|
|
#[macro_use]
|
|
extern crate neovide_derive;
|
|
|
|
#[macro_use]
|
|
extern crate clap;
|
|
|
|
mod bridge;
|
|
mod channel_utils;
|
|
mod cmd_line;
|
|
mod editor;
|
|
mod error_handling;
|
|
mod redraw_scheduler;
|
|
mod renderer;
|
|
mod settings;
|
|
mod window;
|
|
pub mod windows_utils;
|
|
|
|
#[macro_use]
|
|
extern crate derive_new;
|
|
#[macro_use]
|
|
extern crate rust_embed;
|
|
#[macro_use]
|
|
extern crate lazy_static;
|
|
|
|
use std::sync::{atomic::AtomicBool, mpsc::channel, Arc};
|
|
|
|
use crossfire::mpsc::unbounded_future;
|
|
|
|
use bridge::start_bridge;
|
|
#[cfg(not(test))]
|
|
use cmd_line::CmdLineSettings;
|
|
use editor::start_editor;
|
|
use renderer::{cursor_renderer::CursorSettings, RendererSettings};
|
|
#[cfg(not(test))]
|
|
use settings::SETTINGS;
|
|
use window::{create_window, window_geometry, WindowSettings};
|
|
|
|
pub use channel_utils::*;
|
|
pub const INITIAL_DIMENSIONS: (u64, u64) = (100, 50);
|
|
|
|
fn main() {
|
|
// -----------
|
|
// | DATA FLOW |
|
|
// -----------
|
|
//
|
|
// Data flows in a circular motion via channels. This allows each component to handle and
|
|
// process data on their own thread while not blocking the other components from processing.
|
|
//
|
|
// This way Neovim continues to produce events, the window doesn't freeze and queues up ui
|
|
// commands, and the editor can do the processing necessary to handle the UI events
|
|
// effectively.
|
|
//
|
|
// BRIDGE
|
|
// V REDRAW EVENT
|
|
// EDITOR
|
|
// V DRAW COMMAND
|
|
// WINDOW
|
|
// V UI COMMAND
|
|
// BRIDGE
|
|
//
|
|
// BRIDGE:
|
|
// The bridge is responsible for the connection to the neovim process itself. It is in charge
|
|
// of starting and communicating to and from the process.
|
|
//
|
|
// REDRAW EVENT:
|
|
// Redraw events are direct events from the neovim process meant to specify how the editor
|
|
// should be drawn to the screen. They also include other things such as whether the mouse is
|
|
// enabled. The bridge takes these events, filters out some of them meant only for
|
|
// filtering, and forwards them to the editor.
|
|
//
|
|
// EDITOR:
|
|
// The editor is responsible for processing and transforming redraw events into something
|
|
// more readily renderable. Ligature support and multi window management requires some
|
|
// significant preprocessing of the redraw events in order to capture what exactly should get
|
|
// drawn where. Futher this step takes a bit of processing power to accomplish, so it is done
|
|
// on it's own thread. Ideally heavily computationally expensive tasks should be done in the
|
|
// editor.
|
|
//
|
|
// DRAW COMMAND:
|
|
// The draw commands are distilled render information describing actions to be done at the
|
|
// window by window level.
|
|
//
|
|
// WINDOW:
|
|
// The window is responsible for rendering and gathering input events from the user. This
|
|
// inncludes taking the draw commands from the editor and turning them into pixels on the
|
|
// screen. The ui commands are then forwarded back to the BRIDGE to convert them into
|
|
// commands for neovim to handle properly.
|
|
//
|
|
// UI COMMAND:
|
|
// The ui commands are things like text input/key bindings, outer window resizes, and mouse
|
|
// inputs.
|
|
//
|
|
// ------------------
|
|
// | Other Components |
|
|
// ------------------
|
|
//
|
|
// Neovide also includes some other systems which are globally available via lazy static
|
|
// instantiations.
|
|
//
|
|
// SETTINGS:
|
|
// The settings system is live updated from global variables in neovim with the prefix
|
|
// "neovide". They allow us to configure and manage the functionality of neovide from neovim
|
|
// init scripts and variables.
|
|
//
|
|
// REDRAW SCHEDULER:
|
|
// The redraw scheduler is a simple system in charge of deciding if the renderer should draw
|
|
// another frame next frame, or if it can safely skip drawing to save battery and cpu power.
|
|
// Multiple other parts of the app "queue_next_frame" function to ensure animations continue
|
|
// properly or updates to the graphics are pushed to the screen.
|
|
|
|
cmd_line::handle_command_line_arguments(); //Will exit if -h or -v
|
|
|
|
if let Err(err) = window_geometry() {
|
|
eprintln!("{}", err);
|
|
return;
|
|
}
|
|
|
|
#[cfg(not(test))]
|
|
init_logger();
|
|
|
|
#[cfg(target_os = "macos")]
|
|
{
|
|
// incase of app bundle, we can just pass --disowned option straight away to bypass this check
|
|
#[cfg(not(debug_assertions))]
|
|
if !SETTINGS.get::<CmdLineSettings>().disowned {
|
|
if let Ok(curr_exe) = std::env::current_exe() {
|
|
assert!(std::process::Command::new(curr_exe)
|
|
.args(std::env::args().skip(1))
|
|
.arg("--disowned")
|
|
.spawn()
|
|
.is_ok());
|
|
return;
|
|
} else {
|
|
eprintln!("error in disowning process, cannot obtain the path for the current executable, continuing without disowning...");
|
|
}
|
|
}
|
|
|
|
use std::env;
|
|
if env::var_os("TERM").is_none() {
|
|
let mut profile_path = dirs::home_dir().unwrap();
|
|
profile_path.push(".profile");
|
|
let shell = env::var("SHELL").unwrap();
|
|
let cmd = format!(
|
|
"(source /etc/profile && source {} && echo $PATH)",
|
|
profile_path.to_str().unwrap()
|
|
);
|
|
if let Ok(path) = std::process::Command::new(shell)
|
|
.arg("-c")
|
|
.arg(cmd)
|
|
.output()
|
|
{
|
|
env::set_var("PATH", std::str::from_utf8(&path.stdout).unwrap());
|
|
}
|
|
}
|
|
}
|
|
|
|
WindowSettings::register();
|
|
RendererSettings::register();
|
|
CursorSettings::register();
|
|
|
|
let running = Arc::new(AtomicBool::new(true));
|
|
|
|
let (redraw_event_sender, redraw_event_receiver) = unbounded_future();
|
|
let logging_redraw_event_sender =
|
|
LoggingTx::attach(redraw_event_sender, "redraw_event".to_owned());
|
|
|
|
let (batched_draw_command_sender, batched_draw_command_receiver) = channel();
|
|
let logging_batched_draw_command_sender = LoggingSender::attach(
|
|
batched_draw_command_sender,
|
|
"batched_draw_command".to_owned(),
|
|
);
|
|
|
|
let (ui_command_sender, ui_command_receiver) = unbounded_future();
|
|
let logging_ui_command_sender = LoggingTx::attach(ui_command_sender, "ui_command".to_owned());
|
|
|
|
let (window_command_sender, window_command_receiver) = channel();
|
|
let logging_window_command_sender =
|
|
LoggingSender::attach(window_command_sender, "window_command".to_owned());
|
|
|
|
// We need to keep the bridge reference around to prevent the tokio runtime from getting freed
|
|
let _bridge = start_bridge(
|
|
logging_ui_command_sender.clone(),
|
|
ui_command_receiver,
|
|
logging_redraw_event_sender,
|
|
running.clone(),
|
|
);
|
|
start_editor(
|
|
redraw_event_receiver,
|
|
logging_batched_draw_command_sender,
|
|
logging_window_command_sender,
|
|
);
|
|
create_window(
|
|
batched_draw_command_receiver,
|
|
window_command_receiver,
|
|
logging_ui_command_sender,
|
|
running,
|
|
);
|
|
}
|
|
|
|
#[cfg(not(test))]
|
|
pub fn init_logger() {
|
|
let verbosity = match SETTINGS.get::<CmdLineSettings>().verbosity {
|
|
0 => "warn",
|
|
1 => "info",
|
|
2 => "debug",
|
|
_ => "trace",
|
|
};
|
|
let log_to_file = SETTINGS.get::<CmdLineSettings>().log_to_file;
|
|
|
|
if log_to_file {
|
|
Logger::with_env_or_str("neovide")
|
|
.duplicate_to_stderr(Duplicate::Error)
|
|
.log_to_file()
|
|
.rotate(
|
|
Criterion::Size(10_000_000),
|
|
Naming::Timestamps,
|
|
Cleanup::KeepLogFiles(1),
|
|
)
|
|
.start()
|
|
.expect("Could not start logger");
|
|
} else {
|
|
Logger::with_env_or_str(format!("neovide = {}", verbosity))
|
|
.start()
|
|
.expect("Could not start logger");
|
|
}
|
|
}
|