Add event aggregator and remove manual usage of channels. Also add redraw editor command on resume

macos-click-through
Keith Simmons 3 years ago
parent d220477b73
commit 26169f84c0

@ -1,37 +1,24 @@
use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use log::trace; use log::trace;
use nvim_rs::{Handler, Neovim}; use nvim_rs::{Handler, Neovim};
use parking_lot::Mutex;
use rmpv::Value; use rmpv::Value;
use tokio::task; use tokio::task;
use super::events::{parse_redraw_event, RedrawEvent}; use super::events::parse_redraw_event;
#[cfg(windows)] #[cfg(windows)]
use super::ui_commands::{ParallelCommand, UiCommand}; use super::ui_commands::{ParallelCommand, UiCommand};
use crate::bridge::TxWrapper; use crate::bridge::TxWrapper;
use crate::channel_utils::*; use crate::editor::EditorCommand;
use crate::error_handling::ResultPanicExplanation; use crate::error_handling::ResultPanicExplanation;
use crate::event_aggregator::EVENT_AGGREGATOR;
use crate::settings::SETTINGS; use crate::settings::SETTINGS;
#[derive(Clone)] #[derive(Clone)]
pub struct NeovimHandler { pub struct NeovimHandler {}
#[cfg(windows)]
ui_command_sender: Arc<Mutex<LoggingTx<UiCommand>>>,
redraw_event_sender: Arc<Mutex<LoggingTx<RedrawEvent>>>,
}
impl NeovimHandler { impl NeovimHandler {
pub fn new( pub fn new() -> Self {
#[cfg(windows)] ui_command_sender: LoggingTx<UiCommand>, Self {}
redraw_event_sender: LoggingTx<RedrawEvent>,
) -> NeovimHandler {
NeovimHandler {
#[cfg(windows)]
ui_command_sender: Arc::new(Mutex::new(ui_command_sender)),
redraw_event_sender: Arc::new(Mutex::new(redraw_event_sender)),
}
} }
} }
@ -47,10 +34,6 @@ impl Handler for NeovimHandler {
) { ) {
trace!("Neovim notification: {:?}", &event_name); trace!("Neovim notification: {:?}", &event_name);
#[cfg(windows)]
let ui_command_sender = self.ui_command_sender.clone();
let redraw_event_sender = self.redraw_event_sender.clone();
task::spawn_blocking(move || match event_name.as_ref() { task::spawn_blocking(move || match event_name.as_ref() {
"redraw" => { "redraw" => {
for events in arguments { for events in arguments {
@ -58,8 +41,7 @@ impl Handler for NeovimHandler {
.unwrap_or_explained_panic("Could not parse event from neovim"); .unwrap_or_explained_panic("Could not parse event from neovim");
for parsed_event in parsed_events { for parsed_event in parsed_events {
let redraw_event_sender = redraw_event_sender.lock(); EVENT_AGGREGATOR.send(EditorCommand::NeovimRedrawEvent(parsed_event));
redraw_event_sender.send(parsed_event).ok();
} }
} }
} }
@ -68,17 +50,11 @@ impl Handler for NeovimHandler {
} }
#[cfg(windows)] #[cfg(windows)]
"neovide.register_right_click" => { "neovide.register_right_click" => {
let ui_command_sender = ui_command_sender.lock(); EVENT_AGGREGATOR.send(UiCommand::Parallel(ParallelCommand::RegisterRightClick));
ui_command_sender
.send(ParallelCommand::RegisterRightClick.into())
.ok();
} }
#[cfg(windows)] #[cfg(windows)]
"neovide.unregister_right_click" => { "neovide.unregister_right_click" => {
let ui_command_sender = ui_command_sender.lock(); EVENT_AGGREGATOR.send(UiCommand::Parallel(ParallelCommand::UnregisterRightClick));
ui_command_sender
.send(ParallelCommand::UnregisterRightClick.into())
.ok();
} }
_ => {} _ => {}
}); });

@ -13,9 +13,7 @@ use nvim_rs::UiAttachOptions;
use rmpv::Value; use rmpv::Value;
use tokio::process::Command; use tokio::process::Command;
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
use tokio::sync::mpsc::UnboundedReceiver;
use crate::channel_utils::*;
use crate::running_tracker::*; use crate::running_tracker::*;
use crate::settings::*; use crate::settings::*;
use crate::{cmd_line::CmdLineSettings, error_handling::ResultPanicExplanation}; use crate::{cmd_line::CmdLineSettings, error_handling::ResultPanicExplanation};
@ -153,15 +151,8 @@ fn connection_mode() -> ConnectionMode {
} }
} }
async fn start_neovim_runtime( async fn start_neovim_runtime() {
#[cfg(windows)] ui_command_sender: LoggingTx<UiCommand>, let handler = NeovimHandler::new();
ui_command_receiver: UnboundedReceiver<UiCommand>,
redraw_event_sender: LoggingTx<RedrawEvent>,
) {
#[cfg(windows)]
let handler = NeovimHandler::new(ui_command_sender.clone(), redraw_event_sender.clone());
#[cfg(not(windows))]
let handler = NeovimHandler::new(redraw_event_sender.clone());
let (nvim, io_handler) = match connection_mode() { let (nvim, io_handler) = match connection_mode() {
ConnectionMode::Child => create::new_child_cmd(&mut create_nvim_command(), handler).await, ConnectionMode::Child => create::new_child_cmd(&mut create_nvim_command(), handler).await,
ConnectionMode::RemoteTcp(address) => create::new_tcp(address, handler).await, ConnectionMode::RemoteTcp(address) => create::new_tcp(address, handler).await,
@ -284,7 +275,7 @@ async fn start_neovim_runtime(
let nvim = Arc::new(nvim); let nvim = Arc::new(nvim);
start_ui_command_handler(ui_command_receiver, nvim.clone()); start_ui_command_handler(nvim.clone());
SETTINGS.read_initial_values(&nvim).await; SETTINGS.read_initial_values(&nvim).await;
SETTINGS.setup_changed_listeners(&nvim).await; SETTINGS.setup_changed_listeners(&nvim).await;
} }
@ -293,17 +284,8 @@ pub struct Bridge {
_runtime: Runtime, // Necessary to keep runtime running _runtime: Runtime, // Necessary to keep runtime running
} }
pub fn start_bridge( pub fn start_bridge() -> Bridge {
#[cfg(windows)] ui_command_sender: LoggingTx<UiCommand>,
ui_command_receiver: UnboundedReceiver<UiCommand>,
redraw_event_sender: LoggingTx<RedrawEvent>,
) -> Bridge {
let runtime = Runtime::new().unwrap(); let runtime = Runtime::new().unwrap();
runtime.spawn(start_neovim_runtime( runtime.spawn(start_neovim_runtime());
#[cfg(windows)]
ui_command_sender,
ui_command_receiver,
redraw_event_sender,
));
Bridge { _runtime: runtime } Bridge { _runtime: runtime }
} }

@ -5,9 +5,10 @@ use log::error;
use log::trace; use log::trace;
use nvim_rs::{call_args, rpc::model::IntoVal, Neovim}; use nvim_rs::{call_args, rpc::model::IntoVal, Neovim};
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver}; use tokio::sync::mpsc::unbounded_channel;
use crate::bridge::TxWrapper; use crate::bridge::TxWrapper;
use crate::event_aggregator::EVENT_AGGREGATOR;
use crate::running_tracker::RUNNING_TRACKER; use crate::running_tracker::RUNNING_TRACKER;
#[cfg(windows)] #[cfg(windows)]
use crate::windows_utils::{ use crate::windows_utils::{
@ -18,7 +19,7 @@ use crate::windows_utils::{
// includes keyboard and mouse input which would cause problems if sent out of order. // includes keyboard and mouse input which would cause problems if sent out of order.
// //
// When in doubt, use Parallel Commands. // When in doubt, use Parallel Commands.
#[derive(Debug, Clone)] #[derive(Clone, Debug)]
pub enum SerialCommand { pub enum SerialCommand {
Keyboard(String), Keyboard(String),
MouseButton { MouseButton {
@ -236,13 +237,11 @@ impl From<ParallelCommand> for UiCommand {
} }
} }
pub fn start_ui_command_handler( pub fn start_ui_command_handler(nvim: Arc<Neovim<TxWrapper>>) {
mut ui_command_receiver: UnboundedReceiver<UiCommand>,
nvim: Arc<Neovim<TxWrapper>>,
) {
let (serial_tx, mut serial_rx) = unbounded_channel::<SerialCommand>(); let (serial_tx, mut serial_rx) = unbounded_channel::<SerialCommand>();
let ui_command_nvim = nvim.clone(); let ui_command_nvim = nvim.clone();
tokio::spawn(async move { tokio::spawn(async move {
let mut ui_command_receiver = EVENT_AGGREGATOR.register_event::<UiCommand>();
while RUNNING_TRACKER.is_running() { while RUNNING_TRACKER.is_running() {
match ui_command_receiver.recv().await { match ui_command_receiver.recv().await {
Some(UiCommand::Serial(serial_command)) => serial_tx Some(UiCommand::Serial(serial_command)) => serial_tx

@ -1,23 +1,20 @@
use std::sync::mpsc::{channel, Receiver, SendError, Sender}; use std::sync::mpsc::{channel, Receiver, SendError, Sender};
use super::DrawCommand; use super::DrawCommand;
use crate::channel_utils::*; use crate::event_aggregator::EVENT_AGGREGATOR;
pub struct DrawCommandBatcher { pub struct DrawCommandBatcher {
window_draw_command_sender: Sender<DrawCommand>, window_draw_command_sender: Sender<DrawCommand>,
window_draw_command_receiver: Receiver<DrawCommand>, window_draw_command_receiver: Receiver<DrawCommand>,
batched_draw_command_sender: LoggingSender<Vec<DrawCommand>>,
} }
impl DrawCommandBatcher { impl DrawCommandBatcher {
pub fn new(batched_draw_command_sender: LoggingSender<Vec<DrawCommand>>) -> DrawCommandBatcher { pub fn new() -> DrawCommandBatcher {
let (sender, receiver) = channel(); let (sender, receiver) = channel();
DrawCommandBatcher { DrawCommandBatcher {
window_draw_command_sender: sender, window_draw_command_sender: sender,
window_draw_command_receiver: receiver, window_draw_command_receiver: receiver,
batched_draw_command_sender,
} }
} }
@ -25,8 +22,8 @@ impl DrawCommandBatcher {
self.window_draw_command_sender.send(draw_command) self.window_draw_command_sender.send(draw_command)
} }
pub fn send_batch(&self) -> Result<(), SendError<Vec<DrawCommand>>> { pub fn send_batch(&self) {
let batch = self.window_draw_command_receiver.try_iter().collect(); let batch: Vec<DrawCommand> = self.window_draw_command_receiver.try_iter().collect();
self.batched_draw_command_sender.send(batch) EVENT_AGGREGATOR.send(batch);
} }
} }

@ -9,11 +9,12 @@ use std::sync::Arc;
use std::thread; use std::thread;
use log::{error, trace}; use log::{error, trace};
use tokio::sync::mpsc::UnboundedReceiver;
use crate::bridge::{EditorMode, GuiOption, RedrawEvent, WindowAnchor}; use crate::bridge::{GuiOption, RedrawEvent, WindowAnchor};
use crate::channel_utils::*; use crate::event_aggregator::EVENT_AGGREGATOR;
use crate::redraw_scheduler::REDRAW_SCHEDULER; use crate::redraw_scheduler::REDRAW_SCHEDULER;
use crate::renderer::DrawCommand;
use crate::window::WindowCommand;
pub use cursor::{Cursor, CursorMode, CursorShape}; pub use cursor::{Cursor, CursorMode, CursorShape};
pub use draw_command_batcher::DrawCommandBatcher; pub use draw_command_batcher::DrawCommandBatcher;
pub use grid::CharacterGrid; pub use grid::CharacterGrid;
@ -46,24 +47,10 @@ impl WindowAnchor {
} }
} }
#[derive(Debug)] #[derive(Clone, Debug)]
pub enum DrawCommand { pub enum EditorCommand {
CloseWindow(u64), NeovimRedrawEvent(RedrawEvent),
Window { RedrawScreen,
grid_id: u64,
command: WindowDrawCommand,
},
UpdateCursor(Cursor),
FontChanged(String),
DefaultStyleChanged(Style),
ModeChanged(EditorMode),
}
#[derive(Debug)]
pub enum WindowCommand {
TitleChanged(String),
SetMouseEnabled(bool),
ListAvailableFonts,
} }
pub struct Editor { pub struct Editor {
@ -72,30 +59,24 @@ pub struct Editor {
pub defined_styles: HashMap<u64, Arc<Style>>, pub defined_styles: HashMap<u64, Arc<Style>>,
pub mode_list: Vec<CursorMode>, pub mode_list: Vec<CursorMode>,
pub draw_command_batcher: Arc<DrawCommandBatcher>, pub draw_command_batcher: Arc<DrawCommandBatcher>,
pub window_command_sender: LoggingSender<WindowCommand>,
} }
impl Editor { impl Editor {
pub fn new( pub fn new() -> Editor {
batched_draw_command_sender: LoggingSender<Vec<DrawCommand>>,
window_command_sender: LoggingSender<WindowCommand>,
) -> Editor {
Editor { Editor {
windows: HashMap::new(), windows: HashMap::new(),
cursor: Cursor::new(), cursor: Cursor::new(),
defined_styles: HashMap::new(), defined_styles: HashMap::new(),
mode_list: Vec::new(), mode_list: Vec::new(),
draw_command_batcher: Arc::new(DrawCommandBatcher::new(batched_draw_command_sender)), draw_command_batcher: Arc::new(DrawCommandBatcher::new()),
window_command_sender,
} }
} }
pub fn handle_redraw_event(&mut self, event: RedrawEvent) { pub fn handle_editor_command(&mut self, command: EditorCommand) {
match event { match command {
EditorCommand::NeovimRedrawEvent(event) => match event {
RedrawEvent::SetTitle { title } => { RedrawEvent::SetTitle { title } => {
self.window_command_sender EVENT_AGGREGATOR.send(WindowCommand::TitleChanged(title));
.send(WindowCommand::TitleChanged(title))
.ok();
} }
RedrawEvent::ModeInfoSet { cursor_modes } => self.mode_list = cursor_modes, RedrawEvent::ModeInfoSet { cursor_modes } => self.mode_list = cursor_modes,
RedrawEvent::OptionSet { gui_option } => self.set_option(gui_option), RedrawEvent::OptionSet { gui_option } => self.set_option(gui_option),
@ -108,14 +89,10 @@ impl Editor {
.ok(); .ok();
} }
RedrawEvent::MouseOn => { RedrawEvent::MouseOn => {
self.window_command_sender EVENT_AGGREGATOR.send(WindowCommand::SetMouseEnabled(true));
.send(WindowCommand::SetMouseEnabled(true))
.ok();
} }
RedrawEvent::MouseOff => { RedrawEvent::MouseOff => {
self.window_command_sender EVENT_AGGREGATOR.send(WindowCommand::SetMouseEnabled(false));
.send(WindowCommand::SetMouseEnabled(false))
.ok();
} }
RedrawEvent::BusyStart => { RedrawEvent::BusyStart => {
trace!("Cursor off"); trace!("Cursor off");
@ -128,7 +105,7 @@ impl Editor {
RedrawEvent::Flush => { RedrawEvent::Flush => {
trace!("Image flushed"); trace!("Image flushed");
self.send_cursor_info(); self.send_cursor_info();
self.draw_command_batcher.send_batch().ok(); self.draw_command_batcher.send_batch();
REDRAW_SCHEDULER.queue_next_frame(); REDRAW_SCHEDULER.queue_next_frame();
} }
RedrawEvent::DefaultColorsSet { colors } => { RedrawEvent::DefaultColorsSet { colors } => {
@ -224,6 +201,8 @@ impl Editor {
.. ..
} => self.send_updated_viewport(grid, top_line, bottom_line), } => self.send_updated_viewport(grid, top_line, bottom_line),
_ => {} _ => {}
},
EditorCommand::RedrawScreen => self.redraw_screen(),
}; };
} }
@ -423,18 +402,14 @@ impl Editor {
trace!("Option set {:?}", &gui_option); trace!("Option set {:?}", &gui_option);
if let GuiOption::GuiFont(guifont) = gui_option { if let GuiOption::GuiFont(guifont) = gui_option {
if guifont == *"*" { if guifont == *"*" {
self.window_command_sender EVENT_AGGREGATOR.send(WindowCommand::ListAvailableFonts);
.send(WindowCommand::ListAvailableFonts)
.ok();
} }
self.draw_command_batcher self.draw_command_batcher
.queue(DrawCommand::FontChanged(guifont)) .queue(DrawCommand::FontChanged(guifont))
.ok(); .ok();
for window in self.windows.values() { self.redraw_screen();
window.redraw();
}
} }
} }
@ -445,18 +420,21 @@ impl Editor {
trace!("viewport event received before window initialized"); trace!("viewport event received before window initialized");
} }
} }
fn redraw_screen(&mut self) {
for window in self.windows.values() {
window.redraw();
}
}
} }
pub fn start_editor( pub fn start_editor() {
mut redraw_event_receiver: UnboundedReceiver<RedrawEvent>,
batched_draw_command_sender: LoggingSender<Vec<DrawCommand>>,
window_command_sender: LoggingSender<WindowCommand>,
) {
thread::spawn(move || { thread::spawn(move || {
let mut editor = Editor::new(batched_draw_command_sender, window_command_sender); let mut editor = Editor::new();
while let Some(redraw_event) = redraw_event_receiver.blocking_recv() { let mut editor_command_receiver = EVENT_AGGREGATOR.register_event::<EditorCommand>();
editor.handle_redraw_event(redraw_event); while let Some(editor_command) = editor_command_receiver.blocking_recv() {
editor.handle_editor_command(editor_command);
} }
}); });
} }

@ -8,41 +8,7 @@ use super::grid::CharacterGrid;
use super::style::Style; use super::style::Style;
use super::{AnchorInfo, DrawCommand, DrawCommandBatcher}; use super::{AnchorInfo, DrawCommand, DrawCommandBatcher};
use crate::bridge::GridLineCell; use crate::bridge::GridLineCell;
use crate::renderer::{LineFragment, WindowDrawCommand};
#[derive(Clone, Debug)]
pub struct LineFragment {
pub text: String,
pub window_left: u64,
pub window_top: u64,
pub width: u64,
pub style: Option<Arc<Style>>,
}
#[derive(Clone, Debug)]
pub enum WindowDrawCommand {
Position {
grid_position: (f64, f64),
grid_size: (u64, u64),
floating_order: Option<u64>,
},
DrawLine(Vec<LineFragment>),
Scroll {
top: u64,
bottom: u64,
left: u64,
right: u64,
rows: i64,
cols: i64,
},
Clear,
Show,
Hide,
Close,
Viewport {
top_line: f64,
bottom_line: f64,
},
}
pub enum WindowType { pub enum WindowType {
Editor, Editor,

@ -0,0 +1,75 @@
use std::any::{type_name, Any, TypeId};
use std::collections::{hash_map::Entry, HashMap};
use std::fmt::Debug;
use parking_lot::{Mutex, RwLock};
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver};
use crate::channel_utils::*;
lazy_static! {
pub static ref EVENT_AGGREGATOR: EventAggregator = EventAggregator::default();
}
thread_local! {
static THREAD_SENDERS: RwLock<HashMap<TypeId, Box<dyn Any + Send>>> = RwLock::new(HashMap::new());
}
pub struct EventAggregator {
parent_senders: RwLock<HashMap<TypeId, Mutex<Box<dyn Any + Send>>>>,
unclaimed_receivers: RwLock<HashMap<TypeId, Box<dyn Any + Send + Sync>>>,
}
impl Default for EventAggregator {
fn default() -> Self {
EventAggregator {
parent_senders: RwLock::new(HashMap::new()),
unclaimed_receivers: RwLock::new(HashMap::new()),
}
}
}
impl EventAggregator {
fn get_sender<T: Any + Clone + Debug + Send>(&self) -> LoggingTx<T> {
match self.parent_senders.write().entry(TypeId::of::<T>()) {
Entry::Occupied(entry) => {
let sender = entry.get().lock();
sender.downcast_ref::<LoggingTx<T>>().unwrap().clone()
}
Entry::Vacant(entry) => {
let (sender, receiver) = unbounded_channel();
let logging_tx = LoggingTx::attach(sender, type_name::<T>().to_owned());
entry.insert(Mutex::new(Box::new(logging_tx.clone())));
self.unclaimed_receivers
.write()
.insert(TypeId::of::<T>(), Box::new(receiver));
logging_tx
}
}
}
pub fn send<T: Any + Clone + Debug + Send>(&self, event: T) {
let sender = self.get_sender::<T>();
sender.send(event).unwrap();
}
pub fn register_event<T: Any + Clone + Debug + Send>(&self) -> UnboundedReceiver<T> {
let type_id = TypeId::of::<T>();
if let Some(receiver) = self.unclaimed_receivers.write().remove(&type_id) {
*receiver.downcast::<UnboundedReceiver<T>>().unwrap()
} else {
let (sender, receiver) = unbounded_channel();
let logging_sender = LoggingTx::attach(sender, type_name::<T>().to_owned());
match self.parent_senders.write().entry(type_id) {
Entry::Occupied(_) => panic!("EventAggregator: type already registered"),
Entry::Vacant(entry) => {
entry.insert(Mutex::new(Box::new(logging_sender)));
}
}
receiver
}
}
}

@ -17,6 +17,7 @@ mod channel_utils;
mod cmd_line; mod cmd_line;
mod editor; mod editor;
mod error_handling; mod error_handling;
mod event_aggregator;
mod redraw_scheduler; mod redraw_scheduler;
mod renderer; mod renderer;
mod running_tracker; mod running_tracker;
@ -31,10 +32,8 @@ extern crate derive_new;
extern crate lazy_static; extern crate lazy_static;
use std::env::args; use std::env::args;
use std::sync::mpsc::channel;
use log::trace; use log::trace;
use tokio::sync::mpsc::unbounded_channel;
use bridge::start_bridge; use bridge::start_bridge;
use cmd_line::CmdLineSettings; use cmd_line::CmdLineSettings;
@ -44,6 +43,7 @@ use settings::SETTINGS;
use window::{create_window, KeyboardSettings, WindowSettings}; use window::{create_window, KeyboardSettings, WindowSettings};
pub use channel_utils::*; pub use channel_utils::*;
pub use event_aggregator::*;
pub use running_tracker::*; pub use running_tracker::*;
pub use windows_utils::*; pub use windows_utils::*;
@ -144,40 +144,10 @@ fn main() {
CursorSettings::register(); CursorSettings::register();
KeyboardSettings::register(); KeyboardSettings::register();
let (redraw_event_sender, redraw_event_receiver) = unbounded_channel();
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_channel();
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 // We need to keep the bridge reference around to prevent the tokio runtime from getting freed
let _bridge = start_bridge( let _bridge = start_bridge();
#[cfg(windows)] start_editor();
logging_ui_command_sender.clone(), create_window();
ui_command_receiver,
logging_redraw_event_sender,
);
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,
);
} }
#[cfg(not(test))] #[cfg(not(test))]

@ -7,19 +7,20 @@ mod rendered_window;
use crate::WindowSettings; use crate::WindowSettings;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::{hash_map::Entry, HashMap}; use std::collections::{hash_map::Entry, HashMap};
use std::sync::mpsc::Receiver;
use std::sync::Arc; use std::sync::Arc;
use log::error; use log::error;
use skia_safe::Canvas; use skia_safe::Canvas;
use tokio::sync::mpsc::UnboundedReceiver;
use crate::bridge::EditorMode; use crate::bridge::EditorMode;
use crate::editor::{DrawCommand, WindowDrawCommand}; use crate::editor::{Cursor, Style};
use crate::event_aggregator::EVENT_AGGREGATOR;
use crate::settings::*; use crate::settings::*;
use cursor_renderer::CursorRenderer; use cursor_renderer::CursorRenderer;
pub use fonts::caching_shaper::CachingShaper; pub use fonts::caching_shaper::CachingShaper;
pub use grid_renderer::GridRenderer; pub use grid_renderer::GridRenderer;
pub use rendered_window::{RenderedWindow, WindowDrawDetails}; pub use rendered_window::{LineFragment, RenderedWindow, WindowDrawCommand, WindowDrawDetails};
#[derive(SettingGroup, Clone)] #[derive(SettingGroup, Clone)]
pub struct RendererSettings { pub struct RendererSettings {
@ -42,6 +43,19 @@ impl Default for RendererSettings {
} }
} }
#[derive(Clone, Debug)]
pub enum DrawCommand {
CloseWindow(u64),
Window {
grid_id: u64,
command: WindowDrawCommand,
},
UpdateCursor(Cursor),
FontChanged(String),
DefaultStyleChanged(Style),
ModeChanged(EditorMode),
}
pub struct Renderer { pub struct Renderer {
cursor_renderer: CursorRenderer, cursor_renderer: CursorRenderer,
pub grid_renderer: GridRenderer, pub grid_renderer: GridRenderer,
@ -50,14 +64,11 @@ pub struct Renderer {
rendered_windows: HashMap<u64, RenderedWindow>, rendered_windows: HashMap<u64, RenderedWindow>,
pub window_regions: Vec<WindowDrawDetails>, pub window_regions: Vec<WindowDrawDetails>,
pub batched_draw_command_receiver: Receiver<Vec<DrawCommand>>, pub batched_draw_command_receiver: UnboundedReceiver<Vec<DrawCommand>>,
} }
impl Renderer { impl Renderer {
pub fn new( pub fn new(scale_factor: f64) -> Self {
batched_draw_command_receiver: Receiver<Vec<DrawCommand>>,
scale_factor: f64,
) -> Self {
let cursor_renderer = CursorRenderer::new(); let cursor_renderer = CursorRenderer::new();
let grid_renderer = GridRenderer::new(scale_factor); let grid_renderer = GridRenderer::new(scale_factor);
let current_mode = EditorMode::Unknown(String::from("")); let current_mode = EditorMode::Unknown(String::from(""));
@ -65,6 +76,8 @@ impl Renderer {
let rendered_windows = HashMap::new(); let rendered_windows = HashMap::new();
let window_regions = Vec::new(); let window_regions = Vec::new();
let batched_draw_command_receiver = EVENT_AGGREGATOR.register_event::<Vec<DrawCommand>>();
Renderer { Renderer {
rendered_windows, rendered_windows,
cursor_renderer, cursor_renderer,
@ -85,12 +98,11 @@ impl Renderer {
/// `bool` indicating whether or not font was changed during this frame. /// `bool` indicating whether or not font was changed during this frame.
#[allow(clippy::needless_collect)] #[allow(clippy::needless_collect)]
pub fn draw_frame(&mut self, root_canvas: &mut Canvas, dt: f32) -> bool { pub fn draw_frame(&mut self, root_canvas: &mut Canvas, dt: f32) -> bool {
let draw_commands: Vec<_> = self let mut draw_commands = Vec::new();
.batched_draw_command_receiver while let Ok(draw_command) = self.batched_draw_command_receiver.try_recv() {
.try_iter() // Iterator of Vec of DrawCommand draw_commands.extend(draw_command);
.map(|batch| batch.into_iter()) // Iterator of Iterator of DrawCommand }
.flatten() // Iterator of DrawCommand
.collect();
let mut font_changed = false; let mut font_changed = false;
for draw_command in draw_commands.into_iter() { for draw_command in draw_commands.into_iter() {

@ -1,4 +1,5 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use std::sync::Arc;
use skia_safe::canvas::{SaveLayerRec, SrcRectConstraint}; use skia_safe::canvas::{SaveLayerRec, SrcRectConstraint};
use skia_safe::gpu::SurfaceOrigin; use skia_safe::gpu::SurfaceOrigin;
@ -9,10 +10,45 @@ use skia_safe::{
use super::animation_utils::*; use super::animation_utils::*;
use super::{GridRenderer, RendererSettings}; use super::{GridRenderer, RendererSettings};
use crate::editor::{LineFragment, WindowDrawCommand}; use crate::editor::Style;
use crate::redraw_scheduler::REDRAW_SCHEDULER; use crate::redraw_scheduler::REDRAW_SCHEDULER;
use crate::utils::Dimensions; use crate::utils::Dimensions;
#[derive(Clone, Debug)]
pub struct LineFragment {
pub text: String,
pub window_left: u64,
pub window_top: u64,
pub width: u64,
pub style: Option<Arc<Style>>,
}
#[derive(Clone, Debug)]
pub enum WindowDrawCommand {
Position {
grid_position: (f64, f64),
grid_size: (u64, u64),
floating_order: Option<u64>,
},
DrawLine(Vec<LineFragment>),
Scroll {
top: u64,
bottom: u64,
left: u64,
right: u64,
rows: i64,
cols: i64,
},
Clear,
Show,
Hide,
Close,
Viewport {
top_line: f64,
bottom_line: f64,
},
}
fn build_window_surface(parent_canvas: &mut Canvas, pixel_size: (i32, i32)) -> Surface { fn build_window_surface(parent_canvas: &mut Canvas, pixel_size: (i32, i32)) -> Surface {
let mut context = parent_canvas.recording_context().unwrap(); let mut context = parent_canvas.recording_context().unwrap();
let budgeted = Budgeted::Yes; let budgeted = Budgeted::Yes;

@ -4,12 +4,11 @@ use glutin::keyboard::Key;
use glutin::platform::modifier_supplement::KeyEventExtModifierSupplement; use glutin::platform::modifier_supplement::KeyEventExtModifierSupplement;
use crate::bridge::{SerialCommand, UiCommand}; use crate::bridge::{SerialCommand, UiCommand};
use crate::channel_utils::LoggingTx; use crate::event_aggregator::EVENT_AGGREGATOR;
use crate::settings::SETTINGS; use crate::settings::SETTINGS;
use crate::window::KeyboardSettings; use crate::window::KeyboardSettings;
pub struct KeyboardManager { pub struct KeyboardManager {
command_sender: LoggingTx<UiCommand>,
shift: bool, shift: bool,
ctrl: bool, ctrl: bool,
alt: bool, alt: bool,
@ -19,9 +18,8 @@ pub struct KeyboardManager {
} }
impl KeyboardManager { impl KeyboardManager {
pub fn new(command_sender: LoggingTx<UiCommand>) -> KeyboardManager { pub fn new() -> KeyboardManager {
KeyboardManager { KeyboardManager {
command_sender,
shift: false, shift: false,
ctrl: false, ctrl: false,
alt: false, alt: false,
@ -73,9 +71,8 @@ impl KeyboardManager {
// And a key was pressed // And a key was pressed
if key_event.state == ElementState::Pressed { if key_event.state == ElementState::Pressed {
if let Some(keybinding) = self.maybe_get_keybinding(key_event) { if let Some(keybinding) = self.maybe_get_keybinding(key_event) {
self.command_sender EVENT_AGGREGATOR
.send(SerialCommand::Keyboard(keybinding).into()) .send(UiCommand::Serial(SerialCommand::Keyboard(keybinding)));
.expect("Could not send keyboard ui command");
} }
} }
} }

@ -3,10 +3,7 @@ mod mouse_manager;
mod renderer; mod renderer;
mod settings; mod settings;
use std::{ use std::time::{Duration, Instant};
sync::mpsc::Receiver,
time::{Duration, Instant},
};
use glutin::{ use glutin::{
self, self,
@ -17,16 +14,21 @@ use glutin::{
ContextBuilder, GlProfile, WindowedContext, ContextBuilder, GlProfile, WindowedContext,
}; };
use log::trace; use log::trace;
use tokio::sync::mpsc::UnboundedReceiver;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
use glutin::platform::unix::WindowBuilderExtUnix; use glutin::platform::unix::WindowBuilderExtUnix;
use image::{load_from_memory, GenericImageView, Pixel};
use keyboard_manager::KeyboardManager;
use mouse_manager::MouseManager;
use renderer::SkiaRenderer;
use crate::{ use crate::{
bridge::{ParallelCommand, UiCommand}, bridge::{ParallelCommand, UiCommand},
channel_utils::*,
cmd_line::CmdLineSettings, cmd_line::CmdLineSettings,
editor::DrawCommand, editor::EditorCommand,
editor::WindowCommand, event_aggregator::EVENT_AGGREGATOR,
redraw_scheduler::REDRAW_SCHEDULER, redraw_scheduler::REDRAW_SCHEDULER,
renderer::Renderer, renderer::Renderer,
running_tracker::*, running_tracker::*,
@ -35,11 +37,6 @@ use crate::{
}, },
utils::Dimensions, utils::Dimensions,
}; };
use image::{load_from_memory, GenericImageView, Pixel};
use keyboard_manager::KeyboardManager;
use mouse_manager::MouseManager;
use renderer::SkiaRenderer;
pub use settings::{KeyboardSettings, WindowSettings}; pub use settings::{KeyboardSettings, WindowSettings};
static ICON: &[u8] = include_bytes!("../../assets/neovide.ico"); static ICON: &[u8] = include_bytes!("../../assets/neovide.ico");
@ -47,6 +44,13 @@ static ICON: &[u8] = include_bytes!("../../assets/neovide.ico");
const MIN_WINDOW_WIDTH: u64 = 20; const MIN_WINDOW_WIDTH: u64 = 20;
const MIN_WINDOW_HEIGHT: u64 = 6; const MIN_WINDOW_HEIGHT: u64 = 6;
#[derive(Clone, Debug)]
pub enum WindowCommand {
TitleChanged(String),
SetMouseEnabled(bool),
ListAvailableFonts,
}
pub struct GlutinWindowWrapper { pub struct GlutinWindowWrapper {
windowed_context: WindowedContext<glutin::PossiblyCurrent>, windowed_context: WindowedContext<glutin::PossiblyCurrent>,
skia_renderer: SkiaRenderer, skia_renderer: SkiaRenderer,
@ -57,8 +61,7 @@ pub struct GlutinWindowWrapper {
fullscreen: bool, fullscreen: bool,
saved_inner_size: PhysicalSize<u32>, saved_inner_size: PhysicalSize<u32>,
saved_grid_size: Option<Dimensions>, saved_grid_size: Option<Dimensions>,
ui_command_sender: LoggingTx<UiCommand>, window_command_receiver: UnboundedReceiver<WindowCommand>,
window_command_receiver: Receiver<WindowCommand>,
} }
impl GlutinWindowWrapper { impl GlutinWindowWrapper {
@ -84,8 +87,7 @@ impl GlutinWindowWrapper {
#[allow(clippy::needless_collect)] #[allow(clippy::needless_collect)]
pub fn handle_window_commands(&mut self) { pub fn handle_window_commands(&mut self) {
let window_commands: Vec<WindowCommand> = self.window_command_receiver.try_iter().collect(); while let Ok(window_command) = self.window_command_receiver.try_recv() {
for window_command in window_commands.into_iter() {
match window_command { match window_command {
WindowCommand::TitleChanged(new_title) => self.handle_title_changed(new_title), WindowCommand::TitleChanged(new_title) => self.handle_title_changed(new_title),
WindowCommand::SetMouseEnabled(mouse_enabled) => { WindowCommand::SetMouseEnabled(mouse_enabled) => {
@ -103,33 +105,25 @@ impl GlutinWindowWrapper {
pub fn send_font_names(&self) { pub fn send_font_names(&self) {
let font_names = self.renderer.font_names(); let font_names = self.renderer.font_names();
self.ui_command_sender EVENT_AGGREGATOR.send(UiCommand::Parallel(ParallelCommand::DisplayAvailableFonts(
.send(UiCommand::Parallel(ParallelCommand::DisplayAvailableFonts(
font_names, font_names,
))) )));
.unwrap();
} }
pub fn handle_quit(&mut self) { pub fn handle_quit(&mut self) {
if SETTINGS.get::<CmdLineSettings>().remote_tcp.is_none() { if SETTINGS.get::<CmdLineSettings>().remote_tcp.is_none() {
self.ui_command_sender EVENT_AGGREGATOR.send(UiCommand::Parallel(ParallelCommand::Quit));
.send(ParallelCommand::Quit.into())
.expect("Could not send quit command to bridge");
} else { } else {
RUNNING_TRACKER.quit("window closed"); RUNNING_TRACKER.quit("window closed");
} }
} }
pub fn handle_focus_lost(&mut self) { pub fn handle_focus_lost(&mut self) {
self.ui_command_sender EVENT_AGGREGATOR.send(UiCommand::Parallel(ParallelCommand::FocusLost));
.send(ParallelCommand::FocusLost.into())
.ok();
} }
pub fn handle_focus_gained(&mut self) { pub fn handle_focus_gained(&mut self) {
self.ui_command_sender EVENT_AGGREGATOR.send(UiCommand::Parallel(ParallelCommand::FocusGained));
.send(ParallelCommand::FocusGained.into())
.ok();
REDRAW_SCHEDULER.queue_next_frame(); REDRAW_SCHEDULER.queue_next_frame();
} }
@ -145,6 +139,9 @@ impl GlutinWindowWrapper {
Event::LoopDestroyed => { Event::LoopDestroyed => {
self.handle_quit(); self.handle_quit();
} }
Event::Resumed => {
EVENT_AGGREGATOR.send(EditorCommand::RedrawScreen);
}
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::CloseRequested, event: WindowEvent::CloseRequested,
.. ..
@ -161,12 +158,8 @@ impl GlutinWindowWrapper {
event: WindowEvent::DroppedFile(path), event: WindowEvent::DroppedFile(path),
.. ..
} => { } => {
self.ui_command_sender let file_path = path.into_os_string().into_string().unwrap();
.send( EVENT_AGGREGATOR.send(UiCommand::Parallel(ParallelCommand::FileDrop(file_path)));
ParallelCommand::FileDrop(path.into_os_string().into_string().unwrap())
.into(),
)
.ok();
} }
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::Focused(focus), event: WindowEvent::Focused(focus),
@ -241,15 +234,10 @@ impl GlutinWindowWrapper {
return; return;
} }
self.saved_grid_size = Some(grid_size); self.saved_grid_size = Some(grid_size);
self.ui_command_sender EVENT_AGGREGATOR.send(UiCommand::Parallel(ParallelCommand::Resize {
.send(
ParallelCommand::Resize {
width: grid_size.width, width: grid_size.width,
height: grid_size.height, height: grid_size.height,
} }));
.into(),
)
.ok();
} }
fn handle_scale_factor_update(&mut self, scale_factor: f64) { fn handle_scale_factor_update(&mut self, scale_factor: f64) {
@ -259,11 +247,7 @@ impl GlutinWindowWrapper {
} }
} }
pub fn create_window( pub fn create_window() {
batched_draw_command_receiver: Receiver<Vec<DrawCommand>>,
window_command_receiver: Receiver<WindowCommand>,
ui_command_sender: LoggingTx<UiCommand>,
) {
let icon = { let icon = {
let icon = load_from_memory(ICON).expect("Failed to parse icon data"); let icon = load_from_memory(ICON).expect("Failed to parse icon data");
let (width, height) = icon.dimensions(); let (width, height) = icon.dimensions();
@ -322,11 +306,13 @@ pub fn create_window(
let window = windowed_context.window(); let window = windowed_context.window();
let scale_factor = windowed_context.window().scale_factor(); let scale_factor = windowed_context.window().scale_factor();
let renderer = Renderer::new(batched_draw_command_receiver, scale_factor); let renderer = Renderer::new(scale_factor);
let saved_inner_size = window.inner_size(); let saved_inner_size = window.inner_size();
let skia_renderer = SkiaRenderer::new(&windowed_context); let skia_renderer = SkiaRenderer::new(&windowed_context);
let window_command_receiver = EVENT_AGGREGATOR.register_event::<WindowCommand>();
log::info!( log::info!(
"window created (scale_factor: {:.4}, font_dimensions: {:?})", "window created (scale_factor: {:.4}, font_dimensions: {:?})",
scale_factor, scale_factor,
@ -337,13 +323,12 @@ pub fn create_window(
windowed_context, windowed_context,
skia_renderer, skia_renderer,
renderer, renderer,
keyboard_manager: KeyboardManager::new(ui_command_sender.clone()), keyboard_manager: KeyboardManager::new(),
mouse_manager: MouseManager::new(ui_command_sender.clone()), mouse_manager: MouseManager::new(),
title: String::from("Neovide"), title: String::from("Neovide"),
fullscreen: false, fullscreen: false,
saved_inner_size, saved_inner_size,
saved_grid_size: None, saved_grid_size: None,
ui_command_sender,
window_command_receiver, window_command_receiver,
}; };

@ -10,7 +10,7 @@ use skia_safe::Rect;
use super::keyboard_manager::KeyboardManager; use super::keyboard_manager::KeyboardManager;
use crate::bridge::{SerialCommand, UiCommand}; use crate::bridge::{SerialCommand, UiCommand};
use crate::channel_utils::LoggingTx; use crate::event_aggregator::EVENT_AGGREGATOR;
use crate::renderer::{Renderer, WindowDrawDetails}; use crate::renderer::{Renderer, WindowDrawDetails};
use crate::settings::SETTINGS; use crate::settings::SETTINGS;
use crate::window::WindowSettings; use crate::window::WindowSettings;
@ -52,8 +52,6 @@ fn mouse_button_to_button_text(mouse_button: &MouseButton) -> Option<String> {
} }
pub struct MouseManager { pub struct MouseManager {
command_sender: LoggingTx<UiCommand>,
dragging: Option<String>, dragging: Option<String>,
drag_position: PhysicalPosition<u32>, drag_position: PhysicalPosition<u32>,
@ -70,9 +68,8 @@ pub struct MouseManager {
} }
impl MouseManager { impl MouseManager {
pub fn new(command_sender: LoggingTx<UiCommand>) -> MouseManager { pub fn new() -> MouseManager {
MouseManager { MouseManager {
command_sender,
dragging: None, dragging: None,
has_moved: false, has_moved: false,
position: PhysicalPosition::new(0, 0), position: PhysicalPosition::new(0, 0),
@ -166,17 +163,12 @@ impl MouseManager {
// If dragging and we haven't already sent a position, send a drag command // If dragging and we haven't already sent a position, send a drag command
if self.dragging.is_some() && has_moved { if self.dragging.is_some() && has_moved {
self.command_sender EVENT_AGGREGATOR.send(UiCommand::Serial(SerialCommand::Drag {
.send(
SerialCommand::Drag {
button: self.dragging.as_ref().unwrap().to_owned(), button: self.dragging.as_ref().unwrap().to_owned(),
grid_id: relevant_window_details.id, grid_id: relevant_window_details.id,
position: self.drag_position.into(), position: self.drag_position.into(),
modifier_string: keyboard_manager.format_modifier_string(true), modifier_string: keyboard_manager.format_modifier_string(true),
} }));
.into(),
)
.ok();
} else { } else {
// otherwise, update the window_id_under_mouse to match the one selected // otherwise, update the window_id_under_mouse to match the one selected
self.window_details_under_mouse = Some(relevant_window_details.clone()); self.window_details_under_mouse = Some(relevant_window_details.clone());
@ -210,18 +202,13 @@ impl MouseManager {
self.relative_position self.relative_position
}; };
self.command_sender EVENT_AGGREGATOR.send(UiCommand::Serial(SerialCommand::MouseButton {
.send(
SerialCommand::MouseButton {
button: button_text.clone(), button: button_text.clone(),
action, action,
grid_id: details.id, grid_id: details.id,
position: position.into(), position: position.into(),
modifier_string: keyboard_manager.format_modifier_string(true), modifier_string: keyboard_manager.format_modifier_string(true),
} }));
.into(),
)
.ok();
} }
if down { if down {
@ -265,7 +252,7 @@ impl MouseManager {
} }
.into(); .into();
for _ in 0..(new_y - previous_y).abs() { for _ in 0..(new_y - previous_y).abs() {
self.command_sender.send(scroll_command.clone()).ok(); EVENT_AGGREGATOR.send(scroll_command.clone());
} }
} }
@ -292,7 +279,7 @@ impl MouseManager {
} }
.into(); .into();
for _ in 0..(new_x - previous_x).abs() { for _ in 0..(new_x - previous_x).abs() {
self.command_sender.send(scroll_command.clone()).ok(); EVENT_AGGREGATOR.send(scroll_command.clone());
} }
} }
} }

Loading…
Cancel
Save