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

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

@ -5,9 +5,10 @@ use log::error;
use log::trace;
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::event_aggregator::EVENT_AGGREGATOR;
use crate::running_tracker::RUNNING_TRACKER;
#[cfg(windows)]
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.
//
// When in doubt, use Parallel Commands.
#[derive(Debug, Clone)]
#[derive(Clone, Debug)]
pub enum SerialCommand {
Keyboard(String),
MouseButton {
@ -236,13 +237,11 @@ impl From<ParallelCommand> for UiCommand {
}
}
pub fn start_ui_command_handler(
mut ui_command_receiver: UnboundedReceiver<UiCommand>,
nvim: Arc<Neovim<TxWrapper>>,
) {
pub fn start_ui_command_handler(nvim: Arc<Neovim<TxWrapper>>) {
let (serial_tx, mut serial_rx) = unbounded_channel::<SerialCommand>();
let ui_command_nvim = nvim.clone();
tokio::spawn(async move {
let mut ui_command_receiver = EVENT_AGGREGATOR.register_event::<UiCommand>();
while RUNNING_TRACKER.is_running() {
match ui_command_receiver.recv().await {
Some(UiCommand::Serial(serial_command)) => serial_tx

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

@ -9,11 +9,12 @@ use std::sync::Arc;
use std::thread;
use log::{error, trace};
use tokio::sync::mpsc::UnboundedReceiver;
use crate::bridge::{EditorMode, GuiOption, RedrawEvent, WindowAnchor};
use crate::channel_utils::*;
use crate::bridge::{GuiOption, RedrawEvent, WindowAnchor};
use crate::event_aggregator::EVENT_AGGREGATOR;
use crate::redraw_scheduler::REDRAW_SCHEDULER;
use crate::renderer::DrawCommand;
use crate::window::WindowCommand;
pub use cursor::{Cursor, CursorMode, CursorShape};
pub use draw_command_batcher::DrawCommandBatcher;
pub use grid::CharacterGrid;
@ -46,24 +47,10 @@ impl WindowAnchor {
}
}
#[derive(Debug)]
pub enum DrawCommand {
CloseWindow(u64),
Window {
grid_id: u64,
command: WindowDrawCommand,
},
UpdateCursor(Cursor),
FontChanged(String),
DefaultStyleChanged(Style),
ModeChanged(EditorMode),
}
#[derive(Debug)]
pub enum WindowCommand {
TitleChanged(String),
SetMouseEnabled(bool),
ListAvailableFonts,
#[derive(Clone, Debug)]
pub enum EditorCommand {
NeovimRedrawEvent(RedrawEvent),
RedrawScreen,
}
pub struct Editor {
@ -72,158 +59,150 @@ pub struct Editor {
pub defined_styles: HashMap<u64, Arc<Style>>,
pub mode_list: Vec<CursorMode>,
pub draw_command_batcher: Arc<DrawCommandBatcher>,
pub window_command_sender: LoggingSender<WindowCommand>,
}
impl Editor {
pub fn new(
batched_draw_command_sender: LoggingSender<Vec<DrawCommand>>,
window_command_sender: LoggingSender<WindowCommand>,
) -> Editor {
pub fn new() -> Editor {
Editor {
windows: HashMap::new(),
cursor: Cursor::new(),
defined_styles: HashMap::new(),
mode_list: Vec::new(),
draw_command_batcher: Arc::new(DrawCommandBatcher::new(batched_draw_command_sender)),
window_command_sender,
draw_command_batcher: Arc::new(DrawCommandBatcher::new()),
}
}
pub fn handle_redraw_event(&mut self, event: RedrawEvent) {
match event {
RedrawEvent::SetTitle { title } => {
self.window_command_sender
.send(WindowCommand::TitleChanged(title))
.ok();
}
RedrawEvent::ModeInfoSet { cursor_modes } => self.mode_list = cursor_modes,
RedrawEvent::OptionSet { gui_option } => self.set_option(gui_option),
RedrawEvent::ModeChange { mode, mode_index } => {
if let Some(cursor_mode) = self.mode_list.get(mode_index as usize) {
self.cursor.change_mode(cursor_mode, &self.defined_styles);
pub fn handle_editor_command(&mut self, command: EditorCommand) {
match command {
EditorCommand::NeovimRedrawEvent(event) => match event {
RedrawEvent::SetTitle { title } => {
EVENT_AGGREGATOR.send(WindowCommand::TitleChanged(title));
}
self.draw_command_batcher
.queue(DrawCommand::ModeChanged(mode))
.ok();
}
RedrawEvent::MouseOn => {
self.window_command_sender
.send(WindowCommand::SetMouseEnabled(true))
.ok();
}
RedrawEvent::MouseOff => {
self.window_command_sender
.send(WindowCommand::SetMouseEnabled(false))
.ok();
}
RedrawEvent::BusyStart => {
trace!("Cursor off");
self.cursor.enabled = false;
}
RedrawEvent::BusyStop => {
trace!("Cursor on");
self.cursor.enabled = true;
}
RedrawEvent::Flush => {
trace!("Image flushed");
self.send_cursor_info();
self.draw_command_batcher.send_batch().ok();
REDRAW_SCHEDULER.queue_next_frame();
}
RedrawEvent::DefaultColorsSet { colors } => {
self.draw_command_batcher
.queue(DrawCommand::DefaultStyleChanged(Style::new(colors)))
.ok();
}
RedrawEvent::HighlightAttributesDefine { id, style } => {
self.defined_styles.insert(id, Arc::new(style));
}
RedrawEvent::CursorGoto {
grid,
column: left,
row: top,
} => self.set_cursor_position(grid, left, top),
RedrawEvent::Resize {
grid,
width,
height,
} => {
self.resize_window(grid, width, height);
}
RedrawEvent::GridLine {
grid,
row,
column_start,
cells,
} => {
let defined_styles = &self.defined_styles;
let window = self.windows.get_mut(&grid);
if let Some(window) = window {
window.draw_grid_line(row, column_start, cells, defined_styles);
RedrawEvent::ModeInfoSet { cursor_modes } => self.mode_list = cursor_modes,
RedrawEvent::OptionSet { gui_option } => self.set_option(gui_option),
RedrawEvent::ModeChange { mode, mode_index } => {
if let Some(cursor_mode) = self.mode_list.get(mode_index as usize) {
self.cursor.change_mode(cursor_mode, &self.defined_styles);
}
self.draw_command_batcher
.queue(DrawCommand::ModeChanged(mode))
.ok();
}
}
RedrawEvent::Clear { grid } => {
let window = self.windows.get_mut(&grid);
if let Some(window) = window {
window.clear();
RedrawEvent::MouseOn => {
EVENT_AGGREGATOR.send(WindowCommand::SetMouseEnabled(true));
}
}
RedrawEvent::Destroy { grid } => self.close_window(grid),
RedrawEvent::Scroll {
grid,
top,
bottom,
left,
right,
rows,
columns,
} => {
let window = self.windows.get_mut(&grid);
if let Some(window) = window {
window.scroll_region(top, bottom, left, right, rows, columns);
RedrawEvent::MouseOff => {
EVENT_AGGREGATOR.send(WindowCommand::SetMouseEnabled(false));
}
}
RedrawEvent::WindowPosition {
grid,
start_row,
start_column,
width,
height,
} => self.set_window_position(grid, start_column, start_row, width, height),
RedrawEvent::WindowFloatPosition {
grid,
anchor,
anchor_grid,
anchor_column: anchor_left,
anchor_row: anchor_top,
sort_order,
..
} => self.set_window_float_position(
grid,
anchor_grid,
anchor,
anchor_left,
anchor_top,
sort_order,
),
RedrawEvent::WindowHide { grid } => {
let window = self.windows.get(&grid);
if let Some(window) = window {
window.hide();
RedrawEvent::BusyStart => {
trace!("Cursor off");
self.cursor.enabled = false;
}
}
RedrawEvent::WindowClose { grid } => self.close_window(grid),
RedrawEvent::MessageSetPosition { grid, row, .. } => {
self.set_message_position(grid, row)
}
RedrawEvent::WindowViewport {
grid,
top_line,
bottom_line,
..
} => self.send_updated_viewport(grid, top_line, bottom_line),
_ => {}
RedrawEvent::BusyStop => {
trace!("Cursor on");
self.cursor.enabled = true;
}
RedrawEvent::Flush => {
trace!("Image flushed");
self.send_cursor_info();
self.draw_command_batcher.send_batch();
REDRAW_SCHEDULER.queue_next_frame();
}
RedrawEvent::DefaultColorsSet { colors } => {
self.draw_command_batcher
.queue(DrawCommand::DefaultStyleChanged(Style::new(colors)))
.ok();
}
RedrawEvent::HighlightAttributesDefine { id, style } => {
self.defined_styles.insert(id, Arc::new(style));
}
RedrawEvent::CursorGoto {
grid,
column: left,
row: top,
} => self.set_cursor_position(grid, left, top),
RedrawEvent::Resize {
grid,
width,
height,
} => {
self.resize_window(grid, width, height);
}
RedrawEvent::GridLine {
grid,
row,
column_start,
cells,
} => {
let defined_styles = &self.defined_styles;
let window = self.windows.get_mut(&grid);
if let Some(window) = window {
window.draw_grid_line(row, column_start, cells, defined_styles);
}
}
RedrawEvent::Clear { grid } => {
let window = self.windows.get_mut(&grid);
if let Some(window) = window {
window.clear();
}
}
RedrawEvent::Destroy { grid } => self.close_window(grid),
RedrawEvent::Scroll {
grid,
top,
bottom,
left,
right,
rows,
columns,
} => {
let window = self.windows.get_mut(&grid);
if let Some(window) = window {
window.scroll_region(top, bottom, left, right, rows, columns);
}
}
RedrawEvent::WindowPosition {
grid,
start_row,
start_column,
width,
height,
} => self.set_window_position(grid, start_column, start_row, width, height),
RedrawEvent::WindowFloatPosition {
grid,
anchor,
anchor_grid,
anchor_column: anchor_left,
anchor_row: anchor_top,
sort_order,
..
} => self.set_window_float_position(
grid,
anchor_grid,
anchor,
anchor_left,
anchor_top,
sort_order,
),
RedrawEvent::WindowHide { grid } => {
let window = self.windows.get(&grid);
if let Some(window) = window {
window.hide();
}
}
RedrawEvent::WindowClose { grid } => self.close_window(grid),
RedrawEvent::MessageSetPosition { grid, row, .. } => {
self.set_message_position(grid, row)
}
RedrawEvent::WindowViewport {
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);
if let GuiOption::GuiFont(guifont) = gui_option {
if guifont == *"*" {
self.window_command_sender
.send(WindowCommand::ListAvailableFonts)
.ok();
EVENT_AGGREGATOR.send(WindowCommand::ListAvailableFonts);
}
self.draw_command_batcher
.queue(DrawCommand::FontChanged(guifont))
.ok();
for window in self.windows.values() {
window.redraw();
}
self.redraw_screen();
}
}
@ -445,18 +420,21 @@ impl Editor {
trace!("viewport event received before window initialized");
}
}
fn redraw_screen(&mut self) {
for window in self.windows.values() {
window.redraw();
}
}
}
pub fn start_editor(
mut redraw_event_receiver: UnboundedReceiver<RedrawEvent>,
batched_draw_command_sender: LoggingSender<Vec<DrawCommand>>,
window_command_sender: LoggingSender<WindowCommand>,
) {
pub fn start_editor() {
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() {
editor.handle_redraw_event(redraw_event);
let mut editor_command_receiver = EVENT_AGGREGATOR.register_event::<EditorCommand>();
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::{AnchorInfo, DrawCommand, DrawCommandBatcher};
use crate::bridge::GridLineCell;
#[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,
},
}
use crate::renderer::{LineFragment, WindowDrawCommand};
pub enum WindowType {
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 editor;
mod error_handling;
mod event_aggregator;
mod redraw_scheduler;
mod renderer;
mod running_tracker;
@ -31,10 +32,8 @@ extern crate derive_new;
extern crate lazy_static;
use std::env::args;
use std::sync::mpsc::channel;
use log::trace;
use tokio::sync::mpsc::unbounded_channel;
use bridge::start_bridge;
use cmd_line::CmdLineSettings;
@ -44,6 +43,7 @@ use settings::SETTINGS;
use window::{create_window, KeyboardSettings, WindowSettings};
pub use channel_utils::*;
pub use event_aggregator::*;
pub use running_tracker::*;
pub use windows_utils::*;
@ -144,40 +144,10 @@ fn main() {
CursorSettings::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
let _bridge = start_bridge(
#[cfg(windows)]
logging_ui_command_sender.clone(),
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,
);
let _bridge = start_bridge();
start_editor();
create_window();
}
#[cfg(not(test))]

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

@ -1,4 +1,5 @@
use std::collections::VecDeque;
use std::sync::Arc;
use skia_safe::canvas::{SaveLayerRec, SrcRectConstraint};
use skia_safe::gpu::SurfaceOrigin;
@ -9,10 +10,45 @@ use skia_safe::{
use super::animation_utils::*;
use super::{GridRenderer, RendererSettings};
use crate::editor::{LineFragment, WindowDrawCommand};
use crate::editor::Style;
use crate::redraw_scheduler::REDRAW_SCHEDULER;
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 {
let mut context = parent_canvas.recording_context().unwrap();
let budgeted = Budgeted::Yes;

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

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

@ -10,7 +10,7 @@ use skia_safe::Rect;
use super::keyboard_manager::KeyboardManager;
use crate::bridge::{SerialCommand, UiCommand};
use crate::channel_utils::LoggingTx;
use crate::event_aggregator::EVENT_AGGREGATOR;
use crate::renderer::{Renderer, WindowDrawDetails};
use crate::settings::SETTINGS;
use crate::window::WindowSettings;
@ -52,8 +52,6 @@ fn mouse_button_to_button_text(mouse_button: &MouseButton) -> Option<String> {
}
pub struct MouseManager {
command_sender: LoggingTx<UiCommand>,
dragging: Option<String>,
drag_position: PhysicalPosition<u32>,
@ -70,9 +68,8 @@ pub struct MouseManager {
}
impl MouseManager {
pub fn new(command_sender: LoggingTx<UiCommand>) -> MouseManager {
pub fn new() -> MouseManager {
MouseManager {
command_sender,
dragging: None,
has_moved: false,
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 self.dragging.is_some() && has_moved {
self.command_sender
.send(
SerialCommand::Drag {
button: self.dragging.as_ref().unwrap().to_owned(),
grid_id: relevant_window_details.id,
position: self.drag_position.into(),
modifier_string: keyboard_manager.format_modifier_string(true),
}
.into(),
)
.ok();
EVENT_AGGREGATOR.send(UiCommand::Serial(SerialCommand::Drag {
button: self.dragging.as_ref().unwrap().to_owned(),
grid_id: relevant_window_details.id,
position: self.drag_position.into(),
modifier_string: keyboard_manager.format_modifier_string(true),
}));
} else {
// otherwise, update the window_id_under_mouse to match the one selected
self.window_details_under_mouse = Some(relevant_window_details.clone());
@ -210,18 +202,13 @@ impl MouseManager {
self.relative_position
};
self.command_sender
.send(
SerialCommand::MouseButton {
button: button_text.clone(),
action,
grid_id: details.id,
position: position.into(),
modifier_string: keyboard_manager.format_modifier_string(true),
}
.into(),
)
.ok();
EVENT_AGGREGATOR.send(UiCommand::Serial(SerialCommand::MouseButton {
button: button_text.clone(),
action,
grid_id: details.id,
position: position.into(),
modifier_string: keyboard_manager.format_modifier_string(true),
}));
}
if down {
@ -265,7 +252,7 @@ impl MouseManager {
}
.into();
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();
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