macos-click-through
AnhQuan Nguyen 5 years ago
parent b36581b25b
commit a530171aea

@ -1,13 +1,13 @@
use std::convert::TryInto;
use std::error; use std::error;
use std::fmt; use std::fmt;
use std::convert::TryInto;
use rmpv::Value; use rmpv::Value;
use skulpin::skia_safe::Color4f; use skulpin::skia_safe::Color4f;
use crate::editor::EDITOR; use crate::editor::EDITOR;
use crate::editor::{Colors, CursorMode, CursorShape, Style};
use crate::error_handling::ResultPanicExplanation; use crate::error_handling::ResultPanicExplanation;
use crate::editor::{Colors, Style, CursorMode, CursorShape};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum EventParseError { pub enum EventParseError {
@ -18,7 +18,7 @@ pub enum EventParseError {
InvalidI64(Value), InvalidI64(Value),
InvalidBool(Value), InvalidBool(Value),
InvalidWindowAnchor(Value), InvalidWindowAnchor(Value),
InvalidEventFormat InvalidEventFormat,
} }
type Result<T> = std::result::Result<T, EventParseError>; type Result<T> = std::result::Result<T, EventParseError>;
@ -31,8 +31,10 @@ impl fmt::Display for EventParseError {
EventParseError::InvalidU64(value) => write!(f, "invalid u64 format {}", value), EventParseError::InvalidU64(value) => write!(f, "invalid u64 format {}", value),
EventParseError::InvalidI64(value) => write!(f, "invalid i64 format {}", value), EventParseError::InvalidI64(value) => write!(f, "invalid i64 format {}", value),
EventParseError::InvalidBool(value) => write!(f, "invalid bool format {}", value), EventParseError::InvalidBool(value) => write!(f, "invalid bool format {}", value),
EventParseError::InvalidWindowAnchor(value) => write!(f, "invalid window anchor format {}", value), EventParseError::InvalidWindowAnchor(value) => {
EventParseError::InvalidEventFormat => write!(f, "invalid event format") write!(f, "invalid window anchor format {}", value)
}
EventParseError::InvalidEventFormat => write!(f, "invalid event format"),
} }
} }
} }
@ -47,7 +49,7 @@ impl error::Error for EventParseError {
pub struct GridLineCell { pub struct GridLineCell {
pub text: String, pub text: String,
pub highlight_id: Option<u64>, pub highlight_id: Option<u64>,
pub repeat: Option<u64> pub repeat: Option<u64>,
} }
pub type StyledContent = Vec<(u64, String)>; pub type StyledContent = Vec<(u64, String)>;
@ -66,7 +68,7 @@ pub enum MessageKind {
ReturnPrompt, ReturnPrompt,
QuickFix, QuickFix,
SearchCount, SearchCount,
Warning Warning,
} }
impl MessageKind { impl MessageKind {
@ -84,7 +86,7 @@ impl MessageKind {
"quickfix" => MessageKind::QuickFix, "quickfix" => MessageKind::QuickFix,
"search_count" => MessageKind::SearchCount, "search_count" => MessageKind::SearchCount,
"wmsg" => MessageKind::Warning, "wmsg" => MessageKind::Warning,
_ => MessageKind::Unknown _ => MessageKind::Unknown,
} }
} }
} }
@ -101,7 +103,7 @@ pub enum GuiOption {
Pumblend(u64), Pumblend(u64),
ShowTabLine(u64), ShowTabLine(u64),
TermGuiColors(bool), TermGuiColors(bool),
Unknown(String, Value) Unknown(String, Value),
} }
#[derive(Debug)] #[derive(Debug)]
@ -109,44 +111,137 @@ pub enum WindowAnchor {
NorthWest, NorthWest,
NorthEast, NorthEast,
SouthWest, SouthWest,
SouthEast SouthEast,
} }
#[derive(Debug)] #[derive(Debug)]
pub enum RedrawEvent { pub enum RedrawEvent {
SetTitle { title: String }, SetTitle {
ModeInfoSet { cursor_modes: Vec<CursorMode> }, title: String,
OptionSet { gui_option: GuiOption }, },
ModeChange { mode_index: u64 }, ModeInfoSet {
cursor_modes: Vec<CursorMode>,
},
OptionSet {
gui_option: GuiOption,
},
ModeChange {
mode_index: u64,
},
BusyStart, BusyStart,
BusyStop, BusyStop,
Flush, Flush,
Resize { grid: u64, width: u64, height: u64 }, Resize {
DefaultColorsSet { colors: Colors }, grid: u64,
HighlightAttributesDefine { id: u64, style: Style }, width: u64,
GridLine { grid: u64, row: u64, column_start: u64, cells: Vec<GridLineCell> }, height: u64,
Clear { grid: u64 }, },
CursorGoto { grid: u64, row: u64, column: u64 }, DefaultColorsSet {
Scroll { grid: u64, top: u64, bottom: u64, left: u64, right: u64, rows: i64, columns: i64 }, colors: Colors,
WindowPosition { grid: u64, window: u64, start_row: u64, start_column: u64, width: u64, height: u64 }, },
WindowFloatPosition { grid: u64, window: u64, anchor: WindowAnchor, anchor_grid: u64, anchor_row: u64, anchor_column: u64, focusable: bool }, HighlightAttributesDefine {
WindowExternalPosition { grid: u64, window: u64 }, id: u64,
WindowHide { grid: u64 }, style: Style,
WindowClose { grid: u64 }, },
MessageSetPosition { grid: u64, row: u64, scrolled: bool, separator_character: String }, GridLine {
CommandLineShow { content: StyledContent, position: u64, first_character: String, prompt: String, indent: u64, level: u64 }, grid: u64,
CommandLinePosition { position: u64, level: u64 }, row: u64,
CommandLineSpecialCharacter { character: String, shift: bool, level: u64 }, column_start: u64,
cells: Vec<GridLineCell>,
},
Clear {
grid: u64,
},
CursorGoto {
grid: u64,
row: u64,
column: u64,
},
Scroll {
grid: u64,
top: u64,
bottom: u64,
left: u64,
right: u64,
rows: i64,
columns: i64,
},
WindowPosition {
grid: u64,
window: u64,
start_row: u64,
start_column: u64,
width: u64,
height: u64,
},
WindowFloatPosition {
grid: u64,
window: u64,
anchor: WindowAnchor,
anchor_grid: u64,
anchor_row: u64,
anchor_column: u64,
focusable: bool,
},
WindowExternalPosition {
grid: u64,
window: u64,
},
WindowHide {
grid: u64,
},
WindowClose {
grid: u64,
},
MessageSetPosition {
grid: u64,
row: u64,
scrolled: bool,
separator_character: String,
},
CommandLineShow {
content: StyledContent,
position: u64,
first_character: String,
prompt: String,
indent: u64,
level: u64,
},
CommandLinePosition {
position: u64,
level: u64,
},
CommandLineSpecialCharacter {
character: String,
shift: bool,
level: u64,
},
CommandLineHide, CommandLineHide,
CommandLineBlockShow { lines: Vec<StyledContent> }, CommandLineBlockShow {
CommandLineBlockAppend { line: StyledContent }, lines: Vec<StyledContent>,
},
CommandLineBlockAppend {
line: StyledContent,
},
CommandLineBlockHide, CommandLineBlockHide,
MessageShow { kind: MessageKind, content: StyledContent, replace_last: bool }, MessageShow {
kind: MessageKind,
content: StyledContent,
replace_last: bool,
},
MessageClear, MessageClear,
MessageShowMode { content: StyledContent }, MessageShowMode {
MessageShowCommand { content: StyledContent }, content: StyledContent,
MessageRuler { content: StyledContent }, },
MessageHistoryShow { entries: Vec<(MessageKind, StyledContent)>} MessageShowCommand {
content: StyledContent,
},
MessageRuler {
content: StyledContent,
},
MessageHistoryShow {
entries: Vec<(MessageKind, StyledContent)>,
},
} }
fn unpack_color(packed_color: u64) -> Color4f { fn unpack_color(packed_color: u64) -> Color4f {
@ -158,7 +253,7 @@ fn unpack_color(packed_color: u64) -> Color4f {
r: r / 255.0, r: r / 255.0,
g: g / 255.0, g: g / 255.0,
b: b / 255.0, b: b / 255.0,
a: 1.0 a: 1.0,
} }
} }
@ -177,7 +272,9 @@ fn extract_values<Arr: AsMut<[Value]>>(values: Vec<Value>, mut arr: Arr) -> Resu
} }
fn parse_array(array_value: Value) -> Result<Vec<Value>> { fn parse_array(array_value: Value) -> Result<Vec<Value>> {
array_value.try_into().map_err(EventParseError::InvalidArray) array_value
.try_into()
.map_err(EventParseError::InvalidArray)
} }
fn parse_map(map_value: Value) -> Result<Vec<(Value, Value)>> { fn parse_map(map_value: Value) -> Result<Vec<(Value, Value)>> {
@ -185,7 +282,9 @@ fn parse_map(map_value: Value) -> Result<Vec<(Value, Value)>> {
} }
fn parse_string(string_value: Value) -> Result<String> { fn parse_string(string_value: Value) -> Result<String> {
string_value.try_into().map_err(EventParseError::InvalidString) string_value
.try_into()
.map_err(EventParseError::InvalidString)
} }
fn parse_u64(u64_value: Value) -> Result<u64> { fn parse_u64(u64_value: Value) -> Result<u64> {
@ -204,12 +303,13 @@ fn parse_set_title(set_title_arguments: Vec<Value>) -> Result<RedrawEvent> {
let [title] = extract_values(set_title_arguments, [Value::Nil])?; let [title] = extract_values(set_title_arguments, [Value::Nil])?;
Ok(RedrawEvent::SetTitle { Ok(RedrawEvent::SetTitle {
title: parse_string(title)? title: parse_string(title)?,
}) })
} }
fn parse_mode_info_set(mode_info_set_arguments: Vec<Value>) -> Result<RedrawEvent> { fn parse_mode_info_set(mode_info_set_arguments: Vec<Value>) -> Result<RedrawEvent> {
let [_cursor_style_enabled, mode_info] = extract_values(mode_info_set_arguments, [Value::Nil, Value::Nil])?; let [_cursor_style_enabled, mode_info] =
extract_values(mode_info_set_arguments, [Value::Nil, Value::Nil])?;
let mode_info_values = parse_array(mode_info)?; let mode_info_values = parse_array(mode_info)?;
let mut cursor_modes = Vec::with_capacity(mode_info_values.len()); let mut cursor_modes = Vec::with_capacity(mode_info_values.len());
@ -222,22 +322,22 @@ fn parse_mode_info_set(mode_info_set_arguments: Vec<Value>) -> Result<RedrawEven
match parse_string(name)?.as_str() { match parse_string(name)?.as_str() {
"cursor_shape" => { "cursor_shape" => {
mode_info.shape = CursorShape::from_type_name(&parse_string(value)?); mode_info.shape = CursorShape::from_type_name(&parse_string(value)?);
}, }
"cell_percentage" => { "cell_percentage" => {
mode_info.cell_percentage = Some(parse_u64(value)? as f32 / 100.0); mode_info.cell_percentage = Some(parse_u64(value)? as f32 / 100.0);
}, }
"blinkwait" => { "blinkwait" => {
mode_info.blinkwait = Some(parse_u64(value)?); mode_info.blinkwait = Some(parse_u64(value)?);
}, }
"blinkon" => { "blinkon" => {
mode_info.blinkon = Some(parse_u64(value)?); mode_info.blinkon = Some(parse_u64(value)?);
}, }
"blinkoff" => { "blinkoff" => {
mode_info.blinkoff = Some(parse_u64(value)?); mode_info.blinkoff = Some(parse_u64(value)?);
} }
"attr_id" => { "attr_id" => {
mode_info.style_id = Some(parse_u64(value)?); mode_info.style_id = Some(parse_u64(value)?);
}, }
_ => {} _ => {}
} }
} }
@ -245,9 +345,7 @@ fn parse_mode_info_set(mode_info_set_arguments: Vec<Value>) -> Result<RedrawEven
cursor_modes.push(mode_info); cursor_modes.push(mode_info);
} }
Ok(RedrawEvent::ModeInfoSet { Ok(RedrawEvent::ModeInfoSet { cursor_modes })
cursor_modes
})
} }
fn parse_option_set(option_set_arguments: Vec<Value>) -> Result<RedrawEvent> { fn parse_option_set(option_set_arguments: Vec<Value>) -> Result<RedrawEvent> {
@ -267,8 +365,8 @@ fn parse_option_set(option_set_arguments: Vec<Value>) -> Result<RedrawEvent> {
"pumblend" => GuiOption::Pumblend(parse_u64(value)?), "pumblend" => GuiOption::Pumblend(parse_u64(value)?),
"showtabline" => GuiOption::ShowTabLine(parse_u64(value)?), "showtabline" => GuiOption::ShowTabLine(parse_u64(value)?),
"termguicolors" => GuiOption::TermGuiColors(parse_bool(value)?), "termguicolors" => GuiOption::TermGuiColors(parse_bool(value)?),
_ => GuiOption::Unknown(name, value) _ => GuiOption::Unknown(name, value),
} },
}) })
} }
@ -276,30 +374,32 @@ fn parse_mode_change(mode_change_arguments: Vec<Value>) -> Result<RedrawEvent> {
let [_mode, mode_index] = extract_values(mode_change_arguments, [Value::Nil, Value::Nil])?; let [_mode, mode_index] = extract_values(mode_change_arguments, [Value::Nil, Value::Nil])?;
Ok(RedrawEvent::ModeChange { Ok(RedrawEvent::ModeChange {
mode_index: parse_u64(mode_index)? mode_index: parse_u64(mode_index)?,
}) })
} }
fn parse_grid_resize(grid_resize_arguments: Vec<Value>) -> Result<RedrawEvent> { fn parse_grid_resize(grid_resize_arguments: Vec<Value>) -> Result<RedrawEvent> {
let [grid_id, width, height] = extract_values(grid_resize_arguments, [Value::Nil, Value::Nil, Value::Nil])?; let [grid_id, width, height] =
extract_values(grid_resize_arguments, [Value::Nil, Value::Nil, Value::Nil])?;
Ok(RedrawEvent::Resize { Ok(RedrawEvent::Resize {
grid: parse_u64(grid_id)?, grid: parse_u64(grid_id)?,
width: parse_u64(width)?, width: parse_u64(width)?,
height: parse_u64(height)? height: parse_u64(height)?,
}) })
} }
fn parse_default_colors(default_colors_arguments: Vec<Value>) -> Result<RedrawEvent> { fn parse_default_colors(default_colors_arguments: Vec<Value>) -> Result<RedrawEvent> {
let values = [Value::Nil, Value::Nil, Value::Nil, Value::Nil, Value::Nil]; let values = [Value::Nil, Value::Nil, Value::Nil, Value::Nil, Value::Nil];
let [foreground, background, special, _term_foreground, _term_background] = extract_values(default_colors_arguments, values)?; let [foreground, background, special, _term_foreground, _term_background] =
extract_values(default_colors_arguments, values)?;
Ok(RedrawEvent::DefaultColorsSet { Ok(RedrawEvent::DefaultColorsSet {
colors: Colors { colors: Colors {
foreground: Some(unpack_color(parse_u64(foreground)?)), foreground: Some(unpack_color(parse_u64(foreground)?)),
background: Some(unpack_color(parse_u64(background)?)), background: Some(unpack_color(parse_u64(background)?)),
special: Some(unpack_color(parse_u64(special)?)), special: Some(unpack_color(parse_u64(special)?)),
} },
}) })
} }
@ -311,17 +411,25 @@ fn parse_style(style_map: Value) -> Result<Style> {
for attribute in attributes { for attribute in attributes {
if let (Value::String(name), value) = attribute { if let (Value::String(name), value) = attribute {
match (name.as_str().unwrap(), value) { match (name.as_str().unwrap(), value) {
("foreground", Value::Integer(packed_color)) => style.colors.foreground = Some(unpack_color(packed_color.as_u64().unwrap())), ("foreground", Value::Integer(packed_color)) => {
("background", Value::Integer(packed_color)) => style.colors.background = Some(unpack_color(packed_color.as_u64().unwrap())), style.colors.foreground = Some(unpack_color(packed_color.as_u64().unwrap()))
("special", Value::Integer(packed_color)) => style.colors.special = Some(unpack_color(packed_color.as_u64().unwrap())), }
("background", Value::Integer(packed_color)) => {
style.colors.background = Some(unpack_color(packed_color.as_u64().unwrap()))
}
("special", Value::Integer(packed_color)) => {
style.colors.special = Some(unpack_color(packed_color.as_u64().unwrap()))
}
("reverse", Value::Boolean(reverse)) => style.reverse = reverse, ("reverse", Value::Boolean(reverse)) => style.reverse = reverse,
("italic", Value::Boolean(italic)) => style.italic = italic, ("italic", Value::Boolean(italic)) => style.italic = italic,
("bold", Value::Boolean(bold)) => style.bold = bold, ("bold", Value::Boolean(bold)) => style.bold = bold,
("strikethrough", Value::Boolean(strikethrough)) => style.strikethrough = strikethrough, ("strikethrough", Value::Boolean(strikethrough)) => {
style.strikethrough = strikethrough
}
("underline", Value::Boolean(underline)) => style.underline = underline, ("underline", Value::Boolean(underline)) => style.underline = underline,
("undercurl", Value::Boolean(undercurl)) => style.undercurl = undercurl, ("undercurl", Value::Boolean(undercurl)) => style.undercurl = undercurl,
("blend", Value::Integer(blend)) => style.blend = blend.as_u64().unwrap() as u8, ("blend", Value::Integer(blend)) => style.blend = blend.as_u64().unwrap() as u8,
_ => println!("Ignored style attribute: {}", name) _ => println!("Ignored style attribute: {}", name),
} }
} else { } else {
println!("Invalid attribute format"); println!("Invalid attribute format");
@ -333,10 +441,14 @@ fn parse_style(style_map: Value) -> Result<Style> {
fn parse_hl_attr_define(hl_attr_define_arguments: Vec<Value>) -> Result<RedrawEvent> { fn parse_hl_attr_define(hl_attr_define_arguments: Vec<Value>) -> Result<RedrawEvent> {
let values = [Value::Nil, Value::Nil, Value::Nil, Value::Nil]; let values = [Value::Nil, Value::Nil, Value::Nil, Value::Nil];
let [id, attributes, _terminal_attributes, _info] = extract_values(hl_attr_define_arguments, values)?; let [id, attributes, _terminal_attributes, _info] =
extract_values(hl_attr_define_arguments, values)?;
let style = parse_style(attributes)?; let style = parse_style(attributes)?;
Ok(RedrawEvent::HighlightAttributesDefine { id: parse_u64(id)?, style }) Ok(RedrawEvent::HighlightAttributesDefine {
id: parse_u64(id)?,
style,
})
} }
fn parse_grid_line_cell(grid_line_cell: Value) -> Result<GridLineCell> { fn parse_grid_line_cell(grid_line_cell: Value) -> Result<GridLineCell> {
@ -346,17 +458,26 @@ fn parse_grid_line_cell(grid_line_cell: Value) -> Result<GridLineCell> {
let mut cell_contents = parse_array(grid_line_cell)?; let mut cell_contents = parse_array(grid_line_cell)?;
let text_value = cell_contents.first_mut() let text_value = cell_contents
.first_mut()
.map(|v| take_value(v)) .map(|v| take_value(v))
.ok_or(EventParseError::InvalidEventFormat)?; .ok_or(EventParseError::InvalidEventFormat)?;
let highlight_id = cell_contents.get_mut(1).map(|v| take_value(v)).map(parse_u64).transpose()?; let highlight_id = cell_contents
let repeat = cell_contents.get_mut(2).map(|v| take_value(v)).map(parse_u64).transpose()?; .get_mut(1)
.map(|v| take_value(v))
.map(parse_u64)
.transpose()?;
let repeat = cell_contents
.get_mut(2)
.map(|v| take_value(v))
.map(parse_u64)
.transpose()?;
Ok(GridLineCell { Ok(GridLineCell {
text: parse_string(text_value)?, text: parse_string(text_value)?,
highlight_id, highlight_id,
repeat repeat,
}) })
} }
@ -371,40 +492,63 @@ fn parse_grid_line(grid_line_arguments: Vec<Value>) -> Result<RedrawEvent> {
cells: parse_array(cells)? cells: parse_array(cells)?
.into_iter() .into_iter()
.map(parse_grid_line_cell) .map(parse_grid_line_cell)
.collect::<Result<Vec<GridLineCell>>>()? .collect::<Result<Vec<GridLineCell>>>()?,
}) })
} }
fn parse_clear(clear_arguments: Vec<Value>) -> Result<RedrawEvent> { fn parse_clear(clear_arguments: Vec<Value>) -> Result<RedrawEvent> {
let [grid_id] = extract_values(clear_arguments, [Value::Nil])?; let [grid_id] = extract_values(clear_arguments, [Value::Nil])?;
Ok(RedrawEvent::Clear { grid: parse_u64(grid_id)? }) Ok(RedrawEvent::Clear {
grid: parse_u64(grid_id)?,
})
} }
fn parse_cursor_goto(cursor_goto_arguments: Vec<Value>) -> Result<RedrawEvent> { fn parse_cursor_goto(cursor_goto_arguments: Vec<Value>) -> Result<RedrawEvent> {
let [grid_id, column, row] = extract_values(cursor_goto_arguments, [Value::Nil, Value::Nil, Value::Nil])?; let [grid_id, column, row] =
extract_values(cursor_goto_arguments, [Value::Nil, Value::Nil, Value::Nil])?;
Ok(RedrawEvent::CursorGoto { Ok(RedrawEvent::CursorGoto {
grid: parse_u64(grid_id)?, grid: parse_u64(grid_id)?,
row: parse_u64(row)?, row: parse_u64(row)?,
column: parse_u64(column)? column: parse_u64(column)?,
}) })
} }
fn parse_grid_scroll(grid_scroll_arguments: Vec<Value>) -> Result<RedrawEvent> { fn parse_grid_scroll(grid_scroll_arguments: Vec<Value>) -> Result<RedrawEvent> {
let values = [Value::Nil, Value::Nil, Value::Nil, Value::Nil, Value::Nil, Value::Nil, Value::Nil]; let values = [
let [grid_id, top, bottom, left, right, rows, columns] = extract_values(grid_scroll_arguments, values)?; Value::Nil,
Value::Nil,
Value::Nil,
Value::Nil,
Value::Nil,
Value::Nil,
Value::Nil,
];
let [grid_id, top, bottom, left, right, rows, columns] =
extract_values(grid_scroll_arguments, values)?;
Ok(RedrawEvent::Scroll { Ok(RedrawEvent::Scroll {
grid: parse_u64(grid_id)?, grid: parse_u64(grid_id)?,
top: parse_u64(top)?, bottom: parse_u64(bottom)?, top: parse_u64(top)?,
left: parse_u64(left)?, right: parse_u64(right)?, bottom: parse_u64(bottom)?,
rows: parse_i64(rows)?, columns: parse_i64(columns)? left: parse_u64(left)?,
right: parse_u64(right)?,
rows: parse_i64(rows)?,
columns: parse_i64(columns)?,
}) })
} }
fn parse_win_pos(win_pos_arguments: Vec<Value>) -> Result<RedrawEvent> { fn parse_win_pos(win_pos_arguments: Vec<Value>) -> Result<RedrawEvent> {
let values = [Value::Nil, Value::Nil, Value::Nil, Value::Nil, Value::Nil, Value::Nil]; let values = [
let [grid, window, start_row, start_column, width, height] = extract_values(win_pos_arguments, values)?; Value::Nil,
Value::Nil,
Value::Nil,
Value::Nil,
Value::Nil,
Value::Nil,
];
let [grid, window, start_row, start_column, width, height] =
extract_values(win_pos_arguments, values)?;
Ok(RedrawEvent::WindowPosition { Ok(RedrawEvent::WindowPosition {
grid: parse_u64(grid)?, grid: parse_u64(grid)?,
@ -412,7 +556,7 @@ fn parse_win_pos(win_pos_arguments: Vec<Value>) -> Result<RedrawEvent> {
start_row: parse_u64(start_row)?, start_row: parse_u64(start_row)?,
start_column: parse_u64(start_column)?, start_column: parse_u64(start_column)?,
width: parse_u64(width)?, width: parse_u64(width)?,
height: parse_u64(height)? height: parse_u64(height)?,
}) })
} }
@ -423,13 +567,22 @@ fn parse_window_anchor(value: Value) -> Result<WindowAnchor> {
"NE" => Ok(WindowAnchor::NorthEast), "NE" => Ok(WindowAnchor::NorthEast),
"SW" => Ok(WindowAnchor::SouthWest), "SW" => Ok(WindowAnchor::SouthWest),
"SE" => Ok(WindowAnchor::SouthEast), "SE" => Ok(WindowAnchor::SouthEast),
_ => Err(EventParseError::InvalidWindowAnchor(value_str.into())) _ => Err(EventParseError::InvalidWindowAnchor(value_str.into())),
} }
} }
fn parse_win_float_pos(win_float_pos_arguments: Vec<Value>) -> Result<RedrawEvent> { fn parse_win_float_pos(win_float_pos_arguments: Vec<Value>) -> Result<RedrawEvent> {
let values = [Value::Nil, Value::Nil, Value::Nil, Value::Nil, Value::Nil, Value::Nil, Value::Nil]; let values = [
let [grid, window, anchor, anchor_grid, anchor_row, anchor_column, focusable] = extract_values(win_float_pos_arguments, values)?; Value::Nil,
Value::Nil,
Value::Nil,
Value::Nil,
Value::Nil,
Value::Nil,
Value::Nil,
];
let [grid, window, anchor, anchor_grid, anchor_row, anchor_column, focusable] =
extract_values(win_float_pos_arguments, values)?;
Ok(RedrawEvent::WindowFloatPosition { Ok(RedrawEvent::WindowFloatPosition {
grid: parse_u64(grid)?, grid: parse_u64(grid)?,
@ -438,7 +591,7 @@ fn parse_win_float_pos(win_float_pos_arguments: Vec<Value>) -> Result<RedrawEven
anchor_grid: parse_u64(anchor_grid)?, anchor_grid: parse_u64(anchor_grid)?,
anchor_row: parse_u64(anchor_row)?, anchor_row: parse_u64(anchor_row)?,
anchor_column: parse_u64(anchor_column)?, anchor_column: parse_u64(anchor_column)?,
focusable: parse_bool(focusable)? focusable: parse_bool(focusable)?,
}) })
} }
@ -447,7 +600,7 @@ fn parse_win_external_pos(win_external_pos_arguments: Vec<Value>) -> Result<Redr
Ok(RedrawEvent::WindowExternalPosition { Ok(RedrawEvent::WindowExternalPosition {
grid: parse_u64(grid)?, grid: parse_u64(grid)?,
window: parse_u64(window)? window: parse_u64(window)?,
}) })
} }
@ -455,7 +608,7 @@ fn parse_win_hide(win_hide_arguments: Vec<Value>) -> Result<RedrawEvent> {
let [grid] = extract_values(win_hide_arguments, [Value::Nil])?; let [grid] = extract_values(win_hide_arguments, [Value::Nil])?;
Ok(RedrawEvent::WindowHide { Ok(RedrawEvent::WindowHide {
grid: parse_u64(grid)? grid: parse_u64(grid)?,
}) })
} }
@ -463,7 +616,7 @@ fn parse_win_close(win_close_arguments: Vec<Value>) -> Result<RedrawEvent> {
let [grid] = extract_values(win_close_arguments, [Value::Nil])?; let [grid] = extract_values(win_close_arguments, [Value::Nil])?;
Ok(RedrawEvent::WindowClose { Ok(RedrawEvent::WindowClose {
grid: parse_u64(grid)? grid: parse_u64(grid)?,
}) })
} }
@ -475,21 +628,32 @@ fn parse_msg_set_pos(msg_set_pos_arguments: Vec<Value>) -> Result<RedrawEvent> {
grid: parse_u64(grid)?, grid: parse_u64(grid)?,
row: parse_u64(row)?, row: parse_u64(row)?,
scrolled: parse_bool(scrolled)?, scrolled: parse_bool(scrolled)?,
separator_character: parse_string(separator_character)? separator_character: parse_string(separator_character)?,
}) })
} }
fn parse_styled_content(line: Value) -> Result<StyledContent> { fn parse_styled_content(line: Value) -> Result<StyledContent> {
parse_array(line)?.into_iter().map(|tuple| { parse_array(line)?
let [style_id, text] = extract_values(parse_array(tuple)?, [Value::Nil, Value::Nil])?; .into_iter()
.map(|tuple| {
let [style_id, text] = extract_values(parse_array(tuple)?, [Value::Nil, Value::Nil])?;
Ok((parse_u64(style_id)?, parse_string(text)?)) Ok((parse_u64(style_id)?, parse_string(text)?))
}).collect() })
.collect()
} }
fn parse_cmdline_show(cmdline_show_arguments: Vec<Value>) -> Result<RedrawEvent> { fn parse_cmdline_show(cmdline_show_arguments: Vec<Value>) -> Result<RedrawEvent> {
let values = [Value::Nil, Value::Nil, Value::Nil, Value::Nil, Value::Nil, Value::Nil]; let values = [
let [content, position, first_character, prompt, indent, level] = extract_values(cmdline_show_arguments, values)?; Value::Nil,
Value::Nil,
Value::Nil,
Value::Nil,
Value::Nil,
Value::Nil,
];
let [content, position, first_character, prompt, indent, level] =
extract_values(cmdline_show_arguments, values)?;
Ok(RedrawEvent::CommandLineShow { Ok(RedrawEvent::CommandLineShow {
content: parse_styled_content(content)?, content: parse_styled_content(content)?,
@ -497,7 +661,7 @@ fn parse_cmdline_show(cmdline_show_arguments: Vec<Value>) -> Result<RedrawEvent>
first_character: parse_string(first_character)?, first_character: parse_string(first_character)?,
prompt: parse_string(prompt)?, prompt: parse_string(prompt)?,
indent: parse_u64(indent)?, indent: parse_u64(indent)?,
level: parse_u64(level)? level: parse_u64(level)?,
}) })
} }
@ -506,17 +670,20 @@ fn parse_cmdline_pos(cmdline_pos_arguments: Vec<Value>) -> Result<RedrawEvent> {
Ok(RedrawEvent::CommandLinePosition { Ok(RedrawEvent::CommandLinePosition {
position: parse_u64(position)?, position: parse_u64(position)?,
level: parse_u64(level)? level: parse_u64(level)?,
}) })
} }
fn parse_cmdline_special_char(cmdline_special_char_arguments: Vec<Value>) -> Result<RedrawEvent> { fn parse_cmdline_special_char(cmdline_special_char_arguments: Vec<Value>) -> Result<RedrawEvent> {
let [character, shift, level] = extract_values(cmdline_special_char_arguments, [Value::Nil, Value::Nil, Value::Nil])?; let [character, shift, level] = extract_values(
cmdline_special_char_arguments,
[Value::Nil, Value::Nil, Value::Nil],
)?;
Ok(RedrawEvent::CommandLineSpecialCharacter { Ok(RedrawEvent::CommandLineSpecialCharacter {
character: parse_string(character)?, character: parse_string(character)?,
shift: parse_bool(shift)?, shift: parse_bool(shift)?,
level: parse_u64(level)? level: parse_u64(level)?,
}) })
} }
@ -527,7 +694,7 @@ fn parse_cmdline_block_show(cmdline_block_show_arguments: Vec<Value>) -> Result<
lines: parse_array(lines)? lines: parse_array(lines)?
.into_iter() .into_iter()
.map(parse_styled_content) .map(parse_styled_content)
.collect::<Result<_>>()? .collect::<Result<_>>()?,
}) })
} }
@ -535,17 +702,18 @@ fn parse_cmdline_block_append(cmdline_block_append_arguments: Vec<Value>) -> Res
let [line] = extract_values(cmdline_block_append_arguments, [Value::Nil])?; let [line] = extract_values(cmdline_block_append_arguments, [Value::Nil])?;
Ok(RedrawEvent::CommandLineBlockAppend { Ok(RedrawEvent::CommandLineBlockAppend {
line: parse_styled_content(line)? line: parse_styled_content(line)?,
}) })
} }
fn parse_msg_show(msg_show_arguments: Vec<Value>) -> Result<RedrawEvent> { fn parse_msg_show(msg_show_arguments: Vec<Value>) -> Result<RedrawEvent> {
let [kind, content, replace_last] = extract_values(msg_show_arguments, [Value::Nil, Value::Nil, Value::Nil])?; let [kind, content, replace_last] =
extract_values(msg_show_arguments, [Value::Nil, Value::Nil, Value::Nil])?;
Ok(RedrawEvent::MessageShow { Ok(RedrawEvent::MessageShow {
kind: MessageKind::parse(&parse_string(kind)?), kind: MessageKind::parse(&parse_string(kind)?),
content: parse_styled_content(content)?, content: parse_styled_content(content)?,
replace_last: parse_bool(replace_last)? replace_last: parse_bool(replace_last)?,
}) })
} }
@ -578,7 +746,7 @@ fn parse_msg_history_entry(entry: Value) -> Result<(MessageKind, StyledContent)>
Ok(( Ok((
MessageKind::parse(&parse_string(kind)?), MessageKind::parse(&parse_string(kind)?),
parse_styled_content(content)? parse_styled_content(content)?,
)) ))
} }
@ -589,13 +757,14 @@ fn parse_msg_history_show(msg_history_show_arguments: Vec<Value>) -> Result<Redr
entries: parse_array(entries)? entries: parse_array(entries)?
.into_iter() .into_iter()
.map(parse_msg_history_entry) .map(parse_msg_history_entry)
.collect::<Result<_>>()? .collect::<Result<_>>()?,
}) })
} }
pub fn parse_redraw_event(event_value: Value) -> Result<Vec<RedrawEvent>> { pub fn parse_redraw_event(event_value: Value) -> Result<Vec<RedrawEvent>> {
let mut event_contents = parse_array(event_value)?.into_iter(); let mut event_contents = parse_array(event_value)?.into_iter();
let event_name = event_contents.next() let event_name = event_contents
.next()
.ok_or(EventParseError::InvalidEventFormat) .ok_or(EventParseError::InvalidEventFormat)
.and_then(parse_string)?; .and_then(parse_string)?;
@ -639,7 +808,7 @@ pub fn parse_redraw_event(event_value: Value) -> Result<Vec<RedrawEvent>> {
"msg_showcmd" => Some(parse_msg_showcmd(event_parameters)?), "msg_showcmd" => Some(parse_msg_showcmd(event_parameters)?),
"msg_ruler" => Some(parse_msg_ruler(event_parameters)?), "msg_ruler" => Some(parse_msg_ruler(event_parameters)?),
"msg_history_show" => Some(parse_msg_history_show(event_parameters)?), "msg_history_show" => Some(parse_msg_history_show(event_parameters)?),
_ => None _ => None,
}; };
if let Some(parsed_event) = possible_parsed_event { if let Some(parsed_event) = possible_parsed_event {
@ -650,7 +819,7 @@ pub fn parse_redraw_event(event_value: Value) -> Result<Vec<RedrawEvent>> {
Ok(parsed_events) Ok(parsed_events)
} }
pub(in super) fn handle_redraw_event_group(arguments: Vec<Value>) { pub(super) fn handle_redraw_event_group(arguments: Vec<Value>) {
for events in arguments { for events in arguments {
let parsed_events = parse_redraw_event(events) let parsed_events = parse_redraw_event(events)
.unwrap_or_explained_panic("Could not parse event from neovim"); .unwrap_or_explained_panic("Could not parse event from neovim");
@ -661,9 +830,3 @@ pub(in super) fn handle_redraw_event_group(arguments: Vec<Value>) {
} }
} }
} }

@ -1,12 +1,12 @@
use rmpv::Value;
use nvim_rs::{Neovim, Handler, compat::tokio::Compat};
use async_trait::async_trait; use async_trait::async_trait;
use log::trace;
use nvim_rs::{compat::tokio::Compat, Handler, Neovim};
use rmpv::Value;
use tokio::process::ChildStdin; use tokio::process::ChildStdin;
use tokio::task; use tokio::task;
use log::trace;
use crate::settings::SETTINGS;
use super::events::handle_redraw_event_group; use super::events::handle_redraw_event_group;
use crate::settings::SETTINGS;
#[derive(Clone)] #[derive(Clone)]
pub struct NeovimHandler(); pub struct NeovimHandler();
@ -15,18 +15,23 @@ pub struct NeovimHandler();
impl Handler for NeovimHandler { impl Handler for NeovimHandler {
type Writer = Compat<ChildStdin>; type Writer = Compat<ChildStdin>;
async fn handle_notify(&self, event_name: String, arguments: Vec<Value>, _neovim: Neovim<Compat<ChildStdin>>) { async fn handle_notify(
&self,
event_name: String,
arguments: Vec<Value>,
_neovim: Neovim<Compat<ChildStdin>>,
) {
trace!("Neovim notification: {:?}", &event_name); trace!("Neovim notification: {:?}", &event_name);
task::spawn_blocking(move || { task::spawn_blocking(move || match event_name.as_ref() {
match event_name.as_ref() { "redraw" => {
"redraw" => { handle_redraw_event_group(arguments);
handle_redraw_event_group(arguments); }
}, "setting_changed" => {
"setting_changed" => { SETTINGS.handle_changed_notification(arguments);
SETTINGS.handle_changed_notification(arguments);
},
_ => {}
} }
}).await.ok(); _ => {}
})
.await
.ok();
} }
} }

@ -1,9 +1,9 @@
mod qwerty; mod qwerty;
use log::{trace, error}; use log::{error, trace};
use skulpin::sdl2::keyboard::{Keycode, Mod}; use skulpin::sdl2::keyboard::{Keycode, Mod};
use crate::settings::{SETTINGS, FromValue, Value}; use crate::settings::{FromValue, Value, SETTINGS};
use qwerty::*; use qwerty::*;
@ -14,14 +14,17 @@ pub fn unsupported_key<R>(keycode: Keycode) -> Option<R> {
#[derive(Clone)] #[derive(Clone)]
pub enum KeyboardLayout { pub enum KeyboardLayout {
Qwerty Qwerty,
} }
impl FromValue for KeyboardLayout { impl FromValue for KeyboardLayout {
fn from_value(&mut self, value: Value) { fn from_value(&mut self, value: Value) {
match value.as_str() { match value.as_str() {
Some("qwerty") => *self = KeyboardLayout::Qwerty, Some("qwerty") => *self = KeyboardLayout::Qwerty,
_ => error!("keyboard_layout setting expected a known keyboard layout name, but received: {}", value) _ => error!(
"keyboard_layout setting expected a known keyboard layout name, but received: {}",
value
),
} }
} }
} }
@ -29,25 +32,32 @@ impl FromValue for KeyboardLayout {
impl From<KeyboardLayout> for Value { impl From<KeyboardLayout> for Value {
fn from(layout: KeyboardLayout) -> Self { fn from(layout: KeyboardLayout) -> Self {
match layout { match layout {
KeyboardLayout::Qwerty => "qwerty".into() KeyboardLayout::Qwerty => "qwerty".into(),
} }
} }
} }
#[derive(Clone)] #[derive(Clone)]
struct KeyboardSettings { struct KeyboardSettings {
layout: KeyboardLayout layout: KeyboardLayout,
} }
pub fn initialize_settings() { pub fn initialize_settings() {
SETTINGS.set(&KeyboardSettings { SETTINGS.set(&KeyboardSettings {
layout: KeyboardLayout::Qwerty layout: KeyboardLayout::Qwerty,
}); });
register_nvim_setting!("keyboard_layout", KeyboardSettings::layout); register_nvim_setting!("keyboard_layout", KeyboardSettings::layout);
} }
fn append_modifiers(keycode_text: &str, special: bool, shift: bool, ctrl: bool, alt: bool, gui: bool) -> String { fn append_modifiers(
keycode_text: &str,
special: bool,
shift: bool,
ctrl: bool,
alt: bool,
gui: bool,
) -> String {
let mut result = keycode_text.to_string(); let mut result = keycode_text.to_string();
let mut special = special; let mut special = special;
@ -80,7 +90,11 @@ fn append_modifiers(keycode_text: &str, special: bool, shift: bool, ctrl: bool,
result result
} }
pub fn produce_neovim_keybinding_string(keycode: Option<Keycode>, keytext: Option<String>, modifiers: Mod) -> Option<String> { pub fn produce_neovim_keybinding_string(
keycode: Option<Keycode>,
keytext: Option<String>,
modifiers: Mod,
) -> Option<String> {
let shift = modifiers.contains(Mod::LSHIFTMOD) || modifiers.contains(Mod::RSHIFTMOD); let shift = modifiers.contains(Mod::LSHIFTMOD) || modifiers.contains(Mod::RSHIFTMOD);
let ctrl = modifiers.contains(Mod::LCTRLMOD) || modifiers.contains(Mod::RCTRLMOD); let ctrl = modifiers.contains(Mod::LCTRLMOD) || modifiers.contains(Mod::RCTRLMOD);
let alt = modifiers.contains(Mod::LALTMOD) || modifiers.contains(Mod::RALTMOD); let alt = modifiers.contains(Mod::LALTMOD) || modifiers.contains(Mod::RALTMOD);
@ -90,7 +104,10 @@ pub fn produce_neovim_keybinding_string(keycode: Option<Keycode>, keytext: Optio
} else if let Some(keycode) = keycode { } else if let Some(keycode) = keycode {
(match SETTINGS.get::<KeyboardSettings>().layout { (match SETTINGS.get::<KeyboardSettings>().layout {
KeyboardLayout::Qwerty => handle_qwerty_layout(keycode, shift, ctrl, alt), KeyboardLayout::Qwerty => handle_qwerty_layout(keycode, shift, ctrl, alt),
}).map(|(transformed_text, special, shift, ctrl, alt)| append_modifiers(transformed_text, special, shift, ctrl, alt, gui)) })
.map(|(transformed_text, special, shift, ctrl, alt)| {
append_modifiers(transformed_text, special, shift, ctrl, alt, gui)
})
} else { } else {
None None
} }

@ -2,7 +2,12 @@ use super::unsupported_key;
use skulpin::sdl2::keyboard::Keycode; use skulpin::sdl2::keyboard::Keycode;
pub fn handle_qwerty_layout(keycode: Keycode, shift: bool, ctrl: bool, alt: bool) -> Option<(&'static str, bool, bool, bool, bool)> { pub fn handle_qwerty_layout(
keycode: Keycode,
shift: bool,
ctrl: bool,
alt: bool,
) -> Option<(&'static str, bool, bool, bool, bool)> {
match (keycode, shift, ctrl, alt) { match (keycode, shift, ctrl, alt) {
(Keycode::Backspace, shift, ctrl, alt) => Some(("BS", true, shift, ctrl, alt)), (Keycode::Backspace, shift, ctrl, alt) => Some(("BS", true, shift, ctrl, alt)),
(Keycode::Tab, shift, ctrl, alt) => Some(("Tab", true, shift, ctrl, alt)), (Keycode::Tab, shift, ctrl, alt) => Some(("Tab", true, shift, ctrl, alt)),
@ -255,10 +260,10 @@ pub fn handle_qwerty_layout(keycode: Keycode, shift: bool, ctrl: bool, alt: bool
(Keycode::BrightnessDown, _, _, _) => unsupported_key(Keycode::BrightnessDown), (Keycode::BrightnessDown, _, _, _) => unsupported_key(Keycode::BrightnessDown),
(Keycode::BrightnessUp, _, _, _) => unsupported_key(Keycode::BrightnessUp), (Keycode::BrightnessUp, _, _, _) => unsupported_key(Keycode::BrightnessUp),
(Keycode::DisplaySwitch, _, _, _) => unsupported_key(Keycode::DisplaySwitch), (Keycode::DisplaySwitch, _, _, _) => unsupported_key(Keycode::DisplaySwitch),
(Keycode::KbdIllumToggle, _, _, _) =>unsupported_key(Keycode::KbdIllumToggle), (Keycode::KbdIllumToggle, _, _, _) => unsupported_key(Keycode::KbdIllumToggle),
(Keycode::KbdIllumDown, _, _, _) => unsupported_key(Keycode::KbdIllumDown), (Keycode::KbdIllumDown, _, _, _) => unsupported_key(Keycode::KbdIllumDown),
(Keycode::KbdIllumUp, _, _, _) => unsupported_key(Keycode::KbdIllumUp), (Keycode::KbdIllumUp, _, _, _) => unsupported_key(Keycode::KbdIllumUp),
(Keycode::Eject, _, _, _) => unsupported_key(Keycode::Eject), (Keycode::Eject, _, _, _) => unsupported_key(Keycode::Eject),
(Keycode::Sleep, _, _, _) => unsupported_key(Keycode::Sleep) (Keycode::Sleep, _, _, _) => unsupported_key(Keycode::Sleep),
} }
} }

@ -5,24 +5,23 @@ mod events;
mod handler; mod handler;
mod ui_commands; mod ui_commands;
use std::sync::Arc;
use std::process::Stdio; use std::process::Stdio;
use std::sync::Arc;
use rmpv::Value; use log::{error, info, trace};
use nvim_rs::{create::tokio as create, UiAttachOptions}; use nvim_rs::{create::tokio as create, UiAttachOptions};
use tokio::runtime::Runtime; use rmpv::Value;
use tokio::process::Command; use tokio::process::Command;
use tokio::runtime::Runtime;
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
use log::{info, error, trace};
use crate::error_handling::ResultPanicExplanation;
use crate::settings::*;
use crate::INITIAL_DIMENSIONS;
pub use events::*; pub use events::*;
use handler::NeovimHandler;
pub use layouts::*; pub use layouts::*;
use crate::settings::*;
pub use ui_commands::UiCommand; pub use ui_commands::UiCommand;
use handler::NeovimHandler;
use crate::error_handling::ResultPanicExplanation;
use crate::INITIAL_DIMENSIONS;
lazy_static! { lazy_static! {
pub static ref BRIDGE: Bridge = Bridge::new(); pub static ref BRIDGE: Bridge = Bridge::new();
@ -60,8 +59,10 @@ async fn drain(receiver: &mut UnboundedReceiver<UiCommand>) -> Option<Vec<UiComm
async fn start_process(mut receiver: UnboundedReceiver<UiCommand>) { async fn start_process(mut receiver: UnboundedReceiver<UiCommand>) {
let (width, height) = INITIAL_DIMENSIONS; let (width, height) = INITIAL_DIMENSIONS;
let (mut nvim, io_handler, _) = create::new_child_cmd(&mut create_nvim_command(), NeovimHandler()).await let (mut nvim, io_handler, _) =
.unwrap_or_explained_panic("Could not locate or start the neovim process"); create::new_child_cmd(&mut create_nvim_command(), NeovimHandler())
.await
.unwrap_or_explained_panic("Could not locate or start the neovim process");
tokio::spawn(async move { tokio::spawn(async move {
info!("Close watcher started"); info!("Close watcher started");
@ -71,7 +72,7 @@ async fn start_process(mut receiver: UnboundedReceiver<UiCommand>) {
if !error.is_channel_closed() { if !error.is_channel_closed() {
error!("Error: '{}'", error); error!("Error: '{}'", error);
} }
}, }
Ok(Ok(())) => {} Ok(Ok(())) => {}
}; };
std::process::exit(0); std::process::exit(0);
@ -87,15 +88,22 @@ async fn start_process(mut receiver: UnboundedReceiver<UiCommand>) {
std::process::exit(0); std::process::exit(0);
}; };
nvim.set_var("neovide", Value::Boolean(true)).await nvim.set_var("neovide", Value::Boolean(true))
.await
.unwrap_or_explained_panic("Could not communicate with neovim process"); .unwrap_or_explained_panic("Could not communicate with neovim process");
let mut options = UiAttachOptions::new(); let mut options = UiAttachOptions::new();
options.set_linegrid_external(true); options.set_linegrid_external(true);
options.set_rgb(true); options.set_rgb(true);
nvim.ui_attach(width as i64, height as i64, &options).await nvim.ui_attach(width as i64, height as i64, &options)
.await
.unwrap_or_explained_panic("Could not attach ui to neovim process"); .unwrap_or_explained_panic("Could not attach ui to neovim process");
if let Err(command_error) = nvim.command("runtime! ginit.vim").await { if let Err(command_error) = nvim.command("runtime! ginit.vim").await {
nvim.command(&format!("echomsg \"error encountered in ginit.vim {:?}\"", command_error)).await.ok(); nvim.command(&format!(
"echomsg \"error encountered in ginit.vim {:?}\"",
command_error
))
.await
.ok();
} }
info!("Neovim process attached"); info!("Neovim process attached");
@ -109,9 +117,11 @@ async fn start_process(mut receiver: UnboundedReceiver<UiCommand>) {
.partition(|command| command.is_resize()); .partition(|command| command.is_resize());
for command in resize_list for command in resize_list
.into_iter().last().into_iter() .into_iter()
.chain(other_commands.into_iter()) { .last()
.into_iter()
.chain(other_commands.into_iter())
{
let input_nvim = input_nvim.clone(); let input_nvim = input_nvim.clone();
tokio::spawn(async move { tokio::spawn(async move {
trace!("Executing UiCommand: {:?}", &command); trace!("Executing UiCommand: {:?}", &command);
@ -124,13 +134,14 @@ async fn start_process(mut receiver: UnboundedReceiver<UiCommand>) {
SETTINGS.read_initial_values(&nvim).await; SETTINGS.read_initial_values(&nvim).await;
SETTINGS.setup_changed_listeners(&nvim).await; SETTINGS.setup_changed_listeners(&nvim).await;
nvim.set_option("lazyredraw", Value::Boolean(false)).await nvim.set_option("lazyredraw", Value::Boolean(false))
.await
.ok(); .ok();
} }
pub struct Bridge { pub struct Bridge {
_runtime: Runtime, // Necessary to keep runtime running _runtime: Runtime, // Necessary to keep runtime running
sender: UnboundedSender<UiCommand> sender: UnboundedSender<UiCommand>,
} }
impl Bridge { impl Bridge {
@ -142,13 +153,16 @@ impl Bridge {
start_process(receiver).await; start_process(receiver).await;
}); });
Bridge { _runtime: runtime, sender } Bridge {
_runtime: runtime,
sender,
}
} }
pub fn queue_command(&self, command: UiCommand) { pub fn queue_command(&self, command: UiCommand) {
trace!("UiCommand queued: {:?}", &command); trace!("UiCommand queued: {:?}", &command);
self.sender.send(command) self.sender.send(command).unwrap_or_explained_panic(
.unwrap_or_explained_panic( "Could not send UI command from the window system to the neovim process.",
"Could not send UI command from the window system to the neovim process."); );
} }
} }

@ -1,54 +1,72 @@
use nvim_rs::Neovim; use log::trace;
use nvim_rs::compat::tokio::Compat; use nvim_rs::compat::tokio::Compat;
use nvim_rs::Neovim;
use tokio::process::ChildStdin; use tokio::process::ChildStdin;
use log::trace;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum UiCommand { pub enum UiCommand {
Resize { width: u32, height: u32 }, Resize {
width: u32,
height: u32,
},
Keyboard(String), Keyboard(String),
MouseButton { action: String, position: (u32, u32) }, MouseButton {
Scroll { direction: String, position: (u32, u32) }, action: String,
position: (u32, u32),
},
Scroll {
direction: String,
position: (u32, u32),
},
Drag(u32, u32), Drag(u32, u32),
FocusLost, FocusLost,
FocusGained FocusGained,
} }
impl UiCommand { impl UiCommand {
pub async fn execute(self, nvim: &Neovim<Compat<ChildStdin>>) { pub async fn execute(self, nvim: &Neovim<Compat<ChildStdin>>) {
match self { match self {
UiCommand::Resize { width, height } => UiCommand::Resize { width, height } => nvim
nvim.ui_try_resize(width.max(10) as i64, height.max(3) as i64).await .ui_try_resize(width.max(10) as i64, height.max(3) as i64)
.expect("Resize failed"), .await
UiCommand::Keyboard(input_command) => { .expect("Resize failed"),
UiCommand::Keyboard(input_command) => {
trace!("Keyboard Input Sent: {}", input_command); trace!("Keyboard Input Sent: {}", input_command);
nvim.input(&input_command).await nvim.input(&input_command).await.expect("Input failed");
.expect("Input failed"); }
}, UiCommand::MouseButton {
UiCommand::MouseButton { action, position: (grid_x, grid_y) } => action,
nvim.input_mouse("left", &action, "", 0, grid_y as i64, grid_x as i64).await position: (grid_x, grid_y),
.expect("Mouse Input Failed"), } => nvim
UiCommand::Scroll { direction, position: (grid_x, grid_y) } => .input_mouse("left", &action, "", 0, grid_y as i64, grid_x as i64)
nvim.input_mouse("wheel", &direction, "", 0, grid_y as i64, grid_x as i64).await .await
.expect("Mouse Scroll Failed"), .expect("Mouse Input Failed"),
UiCommand::Drag(grid_x, grid_y) => UiCommand::Scroll {
nvim.input_mouse("left", "drag", "", 0, grid_y as i64, grid_x as i64).await direction,
.expect("Mouse Drag Failed"), position: (grid_x, grid_y),
UiCommand::FocusLost => { } => nvim
nvim.command("if exists('#FocusLost') | doautocmd <nomodeline> FocusLost | endif").await .input_mouse("wheel", &direction, "", 0, grid_y as i64, grid_x as i64)
.expect("Focus Lost Failed") .await
}, .expect("Mouse Scroll Failed"),
UiCommand::FocusGained => { UiCommand::Drag(grid_x, grid_y) => nvim
nvim.command("if exists('#FocusGained') | doautocmd <nomodeline> FocusGained | endif").await .input_mouse("left", "drag", "", 0, grid_y as i64, grid_x as i64)
.expect("Focus Gained Failed") .await
}, .expect("Mouse Drag Failed"),
UiCommand::FocusLost => nvim
.command("if exists('#FocusLost') | doautocmd <nomodeline> FocusLost | endif")
.await
.expect("Focus Lost Failed"),
UiCommand::FocusGained => nvim
.command("if exists('#FocusGained') | doautocmd <nomodeline> FocusGained | endif")
.await
.expect("Focus Gained Failed"),
} }
} }
pub fn is_resize(&self) -> bool { pub fn is_resize(&self) -> bool {
match self { match self {
UiCommand::Resize { .. } => true, UiCommand::Resize { .. } => true,
_ => false _ => false,
} }
} }
} }

@ -1,98 +1,112 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use skulpin::skia_safe::Color4f; use skulpin::skia_safe::Color4f;
use super::style::{Style, Colors}; use super::style::{Colors, Style};
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum CursorShape { pub enum CursorShape {
Block, Block,
Horizontal, Horizontal,
Vertical Vertical,
} }
impl CursorShape { impl CursorShape {
pub fn from_type_name(name: &str) -> Option<CursorShape> { pub fn from_type_name(name: &str) -> Option<CursorShape> {
match name { match name {
"block" => Some(CursorShape::Block), "block" => Some(CursorShape::Block),
"horizontal" => Some(CursorShape::Horizontal), "horizontal" => Some(CursorShape::Horizontal),
"vertical" => Some(CursorShape::Vertical), "vertical" => Some(CursorShape::Vertical),
_ => None _ => None,
} }
} }
} }
#[derive(Default, Debug, Clone, PartialEq)] #[derive(Default, Debug, Clone, PartialEq)]
pub struct CursorMode { pub struct CursorMode {
pub shape: Option<CursorShape>, pub shape: Option<CursorShape>,
pub style_id: Option<u64>, pub style_id: Option<u64>,
pub cell_percentage: Option<f32>, pub cell_percentage: Option<f32>,
pub blinkwait: Option<u64>, pub blinkwait: Option<u64>,
pub blinkon: Option<u64>, pub blinkon: Option<u64>,
pub blinkoff: Option<u64>, pub blinkoff: Option<u64>,
} }
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub struct Cursor { pub struct Cursor {
pub position: (u64, u64), pub position: (u64, u64),
pub shape: CursorShape, pub shape: CursorShape,
pub cell_percentage: Option<f32>, pub cell_percentage: Option<f32>,
pub blinkwait: Option<u64>, pub blinkwait: Option<u64>,
pub blinkon: Option<u64>, pub blinkon: Option<u64>,
pub blinkoff: Option<u64>, pub blinkoff: Option<u64>,
pub style: Option<Arc<Style>>, pub style: Option<Arc<Style>>,
pub enabled: bool, pub enabled: bool,
pub mode_list: Vec<CursorMode> pub mode_list: Vec<CursorMode>,
} }
impl Cursor { impl Cursor {
pub fn new() -> Cursor { pub fn new() -> Cursor {
Cursor { Cursor {
position: (0, 0), position: (0, 0),
shape: CursorShape::Block, shape: CursorShape::Block,
style: None, style: None,
cell_percentage: None, cell_percentage: None,
blinkwait: None, blinkwait: None,
blinkon: None, blinkon: None,
blinkoff: None, blinkoff: None,
enabled: true, enabled: true,
mode_list: Vec::new() mode_list: Vec::new(),
} }
} }
pub fn foreground(&self, default_colors: &Colors) -> Color4f { pub fn foreground(&self, default_colors: &Colors) -> Color4f {
if let Some(style) = &self.style { if let Some(style) = &self.style {
style.colors.foreground.clone().unwrap_or_else(||default_colors.background.clone().unwrap()) style
} else { .colors
default_colors.background.clone().unwrap() .foreground
} .clone()
} .unwrap_or_else(|| default_colors.background.clone().unwrap())
} else {
pub fn background(&self, default_colors: &Colors) -> Color4f { default_colors.background.clone().unwrap()
if let Some(style) = &self.style { }
style.colors.background.clone().unwrap_or_else(||default_colors.foreground.clone().unwrap()) }
} else {
default_colors.foreground.clone().unwrap() pub fn background(&self, default_colors: &Colors) -> Color4f {
} if let Some(style) = &self.style {
} style
.colors
pub fn change_mode(&mut self, mode_index: u64, styles: &HashMap<u64, Arc<Style>>) { .background
if let Some(CursorMode { shape, style_id, cell_percentage, blinkwait, blinkon, blinkoff }) = self.mode_list.get(mode_index as usize) { .clone()
if let Some(shape) = shape { .unwrap_or_else(|| default_colors.foreground.clone().unwrap())
self.shape = shape.clone(); } else {
} default_colors.foreground.clone().unwrap()
}
if let Some(style_id) = style_id { }
self.style = styles
.get(style_id) pub fn change_mode(&mut self, mode_index: u64, styles: &HashMap<u64, Arc<Style>>) {
.cloned(); if let Some(CursorMode {
} shape,
style_id,
self.cell_percentage = *cell_percentage; cell_percentage,
self.blinkwait = *blinkwait; blinkwait,
self.blinkon = *blinkon; blinkon,
self.blinkoff = *blinkoff; blinkoff,
} }) = self.mode_list.get(mode_index as usize)
} {
} if let Some(shape) = shape {
self.shape = shape.clone();
}
if let Some(style_id) = style_id {
self.style = styles.get(style_id).cloned();
}
self.cell_percentage = *cell_percentage;
self.blinkwait = *blinkwait;
self.blinkon = *blinkon;
self.blinkoff = *blinkoff;
}
}
}

@ -1,5 +1,5 @@
use std::sync::Arc;
use log::trace; use log::trace;
use std::sync::Arc;
use super::style::Style; use super::style::Style;
@ -55,11 +55,12 @@ impl CharacterGrid {
} }
pub fn get_cell(&self, x: u64, y: u64) -> Option<&GridCell> { pub fn get_cell(&self, x: u64, y: u64) -> Option<&GridCell> {
self.cell_index(x,y).map(|idx| &self.characters[idx]) self.cell_index(x, y).map(|idx| &self.characters[idx])
} }
pub fn get_cell_mut(&mut self, x: u64, y: u64) -> Option<&mut GridCell> { pub fn get_cell_mut(&mut self, x: u64, y: u64) -> Option<&mut GridCell> {
self.cell_index(x,y).map(move |idx| &mut self.characters[idx]) self.cell_index(x, y)
.map(move |idx| &mut self.characters[idx])
} }
pub fn is_dirty_cell(&self, x: u64, y: u64) -> bool { pub fn is_dirty_cell(&self, x: u64, y: u64) -> bool {
@ -78,13 +79,13 @@ impl CharacterGrid {
pub fn set_dirty_all(&mut self, value: bool) { pub fn set_dirty_all(&mut self, value: bool) {
self.dirty.clear(); self.dirty.clear();
self.dirty.resize_with((self.width * self.height) as usize, || value); self.dirty
.resize_with((self.width * self.height) as usize, || value);
} }
pub fn rows(&self) -> impl Iterator<Item=&[GridCell]> { pub fn rows(&self) -> impl Iterator<Item = &[GridCell]> {
(0..self.height) (0..self.height).map(move |row| {
.map(move |row| { &self.characters[(row * self.width) as usize..((row + 1) * self.width) as usize]
&self.characters[(row * self.width) as usize..((row + 1) * self.width) as usize] })
})
} }
} }

@ -1,21 +1,21 @@
mod cursor; mod cursor;
mod style;
mod grid; mod grid;
mod style;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use log::trace;
use parking_lot::Mutex; use parking_lot::Mutex;
use skulpin::skia_safe::colors; use skulpin::skia_safe::colors;
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
use log::trace;
pub use cursor::{Cursor, CursorShape, CursorMode};
pub use style::{Colors, Style};
pub use grid::CharacterGrid;
use crate::bridge::{GridLineCell, GuiOption, RedrawEvent}; use crate::bridge::{GridLineCell, GuiOption, RedrawEvent};
use crate::redraw_scheduler::REDRAW_SCHEDULER; use crate::redraw_scheduler::REDRAW_SCHEDULER;
use crate::INITIAL_DIMENSIONS; use crate::INITIAL_DIMENSIONS;
pub use cursor::{Cursor, CursorMode, CursorShape};
pub use grid::CharacterGrid;
pub use style::{Colors, Style};
lazy_static! { lazy_static! {
pub static ref EDITOR: Arc<Mutex<Editor>> = Arc::new(Mutex::new(Editor::new())); pub static ref EDITOR: Arc<Mutex<Editor>> = Arc::new(Mutex::new(Editor::new()));
@ -26,7 +26,7 @@ pub struct DrawCommand {
pub text: String, pub text: String,
pub cell_width: u64, pub cell_width: u64,
pub grid_position: (u64, u64), pub grid_position: (u64, u64),
pub style: Option<Arc<Style>> pub style: Option<Arc<Style>>,
} }
pub struct Editor { pub struct Editor {
@ -37,7 +37,7 @@ pub struct Editor {
pub cursor: Cursor, pub cursor: Cursor,
pub default_style: Arc<Style>, pub default_style: Arc<Style>,
pub defined_styles: HashMap<u64, Arc<Style>>, pub defined_styles: HashMap<u64, Arc<Style>>,
pub previous_style: Option<Arc<Style>> pub previous_style: Option<Arc<Style>>,
} }
impl Editor { impl Editor {
@ -48,41 +48,63 @@ impl Editor {
font_name: None, font_name: None,
font_size: None, font_size: None,
cursor: Cursor::new(), cursor: Cursor::new(),
default_style: Arc::new(Style::new(Colors::new(Some(colors::WHITE), Some(colors::BLACK), Some(colors::GREY)))), default_style: Arc::new(Style::new(Colors::new(
Some(colors::WHITE),
Some(colors::BLACK),
Some(colors::GREY),
))),
defined_styles: HashMap::new(), defined_styles: HashMap::new(),
previous_style: None previous_style: None,
}; };
editor.grid.clear(); editor.grid.clear();
editor editor
} }
pub fn handle_redraw_event(&mut self, event: RedrawEvent) { pub fn handle_redraw_event(&mut self, event: RedrawEvent) {
match event { match event {
RedrawEvent::SetTitle { title } => self.title = title, RedrawEvent::SetTitle { title } => self.title = title,
RedrawEvent::ModeInfoSet { cursor_modes } => self.cursor.mode_list = cursor_modes, RedrawEvent::ModeInfoSet { cursor_modes } => self.cursor.mode_list = cursor_modes,
RedrawEvent::OptionSet { gui_option } => self.set_option(gui_option), RedrawEvent::OptionSet { gui_option } => self.set_option(gui_option),
RedrawEvent::ModeChange { mode_index } => self.cursor.change_mode(mode_index, &self.defined_styles), RedrawEvent::ModeChange { mode_index } => {
self.cursor.change_mode(mode_index, &self.defined_styles)
}
RedrawEvent::BusyStart => { RedrawEvent::BusyStart => {
trace!("Cursor off"); trace!("Cursor off");
self.cursor.enabled = false; self.cursor.enabled = false;
}, }
RedrawEvent::BusyStop => { RedrawEvent::BusyStop => {
trace!("Cursor on"); trace!("Cursor on");
self.cursor.enabled = true; self.cursor.enabled = true;
}, }
RedrawEvent::Flush => { RedrawEvent::Flush => {
trace!("Image flushed"); trace!("Image flushed");
REDRAW_SCHEDULER.queue_next_frame(); REDRAW_SCHEDULER.queue_next_frame();
}, }
RedrawEvent::Resize { width, height, .. } => self.grid.resize(width, height), RedrawEvent::Resize { width, height, .. } => self.grid.resize(width, height),
RedrawEvent::DefaultColorsSet { colors } => self.default_style = Arc::new(Style::new(colors)), RedrawEvent::DefaultColorsSet { colors } => {
RedrawEvent::HighlightAttributesDefine { id, style } => { self.defined_styles.insert(id, Arc::new(style)); }, self.default_style = Arc::new(Style::new(colors))
RedrawEvent::GridLine { row, column_start, cells, .. } => self.draw_grid_line(row, column_start, cells), }
RedrawEvent::HighlightAttributesDefine { id, style } => {
self.defined_styles.insert(id, Arc::new(style));
}
RedrawEvent::GridLine {
row,
column_start,
cells,
..
} => self.draw_grid_line(row, column_start, cells),
RedrawEvent::Clear { .. } => self.grid.clear(), RedrawEvent::Clear { .. } => self.grid.clear(),
RedrawEvent::CursorGoto { row, column, .. } => self.cursor.position = (row, column), RedrawEvent::CursorGoto { row, column, .. } => self.cursor.position = (row, column),
RedrawEvent::Scroll { top, bottom, left, right, rows, columns, .. } => self.scroll_region(top, bottom, left, right, rows, columns), RedrawEvent::Scroll {
top,
bottom,
left,
right,
rows,
columns,
..
} => self.scroll_region(top, bottom, left, right, rows, columns),
_ => {} _ => {}
}; };
} }
@ -101,18 +123,29 @@ impl Editor {
fn command_matches(command: &Option<DrawCommand>, style: &Option<Arc<Style>>) -> bool { fn command_matches(command: &Option<DrawCommand>, style: &Option<Arc<Style>>) -> bool {
match command { match command {
Some(command) => &command.style == style, Some(command) => &command.style == style,
None => true None => true,
} }
} }
fn add_character(command: &mut Option<DrawCommand>, character: &str, row_index: u64, col_index: u64, style: Option<Arc<Style>>) { fn add_character(
command: &mut Option<DrawCommand>,
character: &str,
row_index: u64,
col_index: u64,
style: Option<Arc<Style>>,
) {
match command { match command {
Some(command) => { Some(command) => {
command.text.push_str(character); command.text.push_str(character);
command.cell_width += 1; command.cell_width += 1;
}, }
None => { None => {
command.replace(DrawCommand::new(character.to_string(), 1, (col_index, row_index), style)); command.replace(DrawCommand::new(
character.to_string(),
1,
(col_index, row_index),
style,
));
} }
} }
} }
@ -120,7 +153,13 @@ impl Editor {
for (col_index, cell) in row.iter().enumerate() { for (col_index, cell) in row.iter().enumerate() {
if let Some((character, style)) = cell { if let Some((character, style)) = cell {
if character.is_empty() { if character.is_empty() {
add_character(&mut command, &" ", row_index as u64, col_index as u64, style.clone()); add_character(
&mut command,
&" ",
row_index as u64,
col_index as u64,
style.clone(),
);
add_command(&mut draw_commands, command); add_command(&mut draw_commands, command);
command = None; command = None;
} else { } else {
@ -128,7 +167,13 @@ impl Editor {
add_command(&mut draw_commands, command); add_command(&mut draw_commands, command);
command = None; command = None;
} }
add_character(&mut command, &character, row_index as u64, col_index as u64, style.clone()); add_character(
&mut command,
&character,
row_index as u64,
col_index as u64,
style.clone(),
);
} }
} else { } else {
if !command_matches(&command, &None) { if !command_matches(&command, &None) {
@ -141,19 +186,22 @@ impl Editor {
add_command(&mut draw_commands, command); add_command(&mut draw_commands, command);
} }
let should_clear = self.grid.should_clear; let should_clear = self.grid.should_clear;
let draw_commands = draw_commands.into_iter().filter(|command| {
let (x, y) = command.grid_position;
let min = (x as i64 - 1).max(0) as u64; let draw_commands = draw_commands
let max = (x + command.cell_width + 1).min(self.grid.width); .into_iter()
for char_index in min..max { .filter(|command| {
if self.grid.is_dirty_cell(char_index, y) { let (x, y) = command.grid_position;
return true;
let min = (x as i64 - 1).max(0) as u64;
let max = (x + command.cell_width + 1).min(self.grid.width);
for char_index in min..max {
if self.grid.is_dirty_cell(char_index, y) {
return true;
}
} }
} false
false })
}).collect::<Vec<DrawCommand>>(); .collect::<Vec<DrawCommand>>();
self.grid.set_dirty_all(false); self.grid.set_dirty_all(false);
self.grid.should_clear = false; self.grid.should_clear = false;
@ -166,7 +214,7 @@ impl Editor {
let style = match cell.highlight_id { let style = match cell.highlight_id {
Some(0) => None, Some(0) => None,
Some(style_id) => self.defined_styles.get(&style_id).cloned(), Some(style_id) => self.defined_styles.get(&style_id).cloned(),
None => self.previous_style.clone() None => self.previous_style.clone(),
}; };
let mut text = cell.text; let mut text = cell.text;
@ -205,28 +253,29 @@ impl Editor {
} }
fn scroll_region(&mut self, top: u64, bot: u64, left: u64, right: u64, rows: i64, cols: i64) { fn scroll_region(&mut self, top: u64, bot: u64, left: u64, right: u64, rows: i64, cols: i64) {
let y_iter : Box<dyn Iterator<Item=i64>> = if rows > 0 { let y_iter: Box<dyn Iterator<Item = i64>> = if rows > 0 {
Box::new((top as i64 + rows).. bot as i64) Box::new((top as i64 + rows)..bot as i64)
} else { } else {
Box::new((top as i64 .. (bot as i64 + rows)).rev()) Box::new((top as i64..(bot as i64 + rows)).rev())
}; };
for y in y_iter { for y in y_iter {
let dest_y = y - rows; let dest_y = y - rows;
if dest_y >= 0 && dest_y < self.grid.height as i64 { if dest_y >= 0 && dest_y < self.grid.height as i64 {
let x_iter: Box<dyn Iterator<Item = i64>> = if cols > 0 {
let x_iter : Box<dyn Iterator<Item=i64>> = if cols > 0 { Box::new((left as i64 + cols)..right as i64)
Box::new((left as i64 + cols) .. right as i64)
} else { } else {
Box::new((left as i64 .. (right as i64 + cols)).rev()) Box::new((left as i64..(right as i64 + cols)).rev())
}; };
for x in x_iter { for x in x_iter {
let dest_x = x - cols; let dest_x = x - cols;
let cell_data = self.grid.get_cell(x as u64, y as u64).cloned(); let cell_data = self.grid.get_cell(x as u64, y as u64).cloned();
if let Some(cell_data) = cell_data { if let Some(cell_data) = cell_data {
if let Some(dest_cell) = self.grid.get_cell_mut(dest_x as u64, dest_y as u64) { if let Some(dest_cell) =
self.grid.get_cell_mut(dest_x as u64, dest_y as u64)
{
*dest_cell = cell_data; *dest_cell = cell_data;
self.grid.set_dirty_cell(dest_x as u64, dest_y as u64); self.grid.set_dirty_cell(dest_x as u64, dest_y as u64);
} }
@ -248,9 +297,8 @@ impl Editor {
self.font_size = part[1..].parse::<f32>().ok(); self.font_size = part[1..].parse::<f32>().ok();
} }
} }
}, }
_ => {} _ => {}
} }
} }
} }

@ -1,49 +1,64 @@
use skulpin::skia_safe::Color4f; use skulpin::skia_safe::Color4f;
#[derive(new, PartialEq, Debug, Clone)] #[derive(new, PartialEq, Debug, Clone)]
pub struct Colors { pub struct Colors {
pub foreground: Option<Color4f>, pub foreground: Option<Color4f>,
pub background: Option<Color4f>, pub background: Option<Color4f>,
pub special: Option<Color4f> pub special: Option<Color4f>,
} }
#[derive(new, Debug, Clone, PartialEq)] #[derive(new, Debug, Clone, PartialEq)]
pub struct Style { pub struct Style {
pub colors: Colors, pub colors: Colors,
#[new(default)] #[new(default)]
pub reverse: bool, pub reverse: bool,
#[new(default)] #[new(default)]
pub italic: bool, pub italic: bool,
#[new(default)] #[new(default)]
pub bold: bool, pub bold: bool,
#[new(default)] #[new(default)]
pub strikethrough: bool, pub strikethrough: bool,
#[new(default)] #[new(default)]
pub underline: bool, pub underline: bool,
#[new(default)] #[new(default)]
pub undercurl: bool, pub undercurl: bool,
#[new(default)] #[new(default)]
pub blend: u8 pub blend: u8,
} }
impl Style { impl Style {
pub fn foreground(&self, default_colors: &Colors) -> Color4f { pub fn foreground(&self, default_colors: &Colors) -> Color4f {
if self.reverse { if self.reverse {
self.colors.background.clone().unwrap_or_else(||default_colors.background.clone().unwrap()) self.colors
} else { .background
self.colors.foreground.clone().unwrap_or_else(||default_colors.foreground.clone().unwrap()) .clone()
} .unwrap_or_else(|| default_colors.background.clone().unwrap())
} } else {
self.colors
pub fn background(&self, default_colors: &Colors) -> Color4f { .foreground
if self.reverse { .clone()
self.colors.foreground.clone().unwrap_or_else(||default_colors.foreground.clone().unwrap()) .unwrap_or_else(|| default_colors.foreground.clone().unwrap())
} else { }
self.colors.background.clone().unwrap_or_else(||default_colors.background.clone().unwrap()) }
}
} pub fn background(&self, default_colors: &Colors) -> Color4f {
if self.reverse {
pub fn special(&self, default_colors: &Colors) -> Color4f { self.colors
self.colors.special.clone().unwrap_or_else(||default_colors.special.clone().unwrap()) .foreground
} .clone()
} .unwrap_or_else(|| default_colors.foreground.clone().unwrap())
} else {
self.colors
.background
.clone()
.unwrap_or_else(|| default_colors.background.clone().unwrap())
}
}
pub fn special(&self, default_colors: &Colors) -> Color4f {
self.colors
.special
.clone()
.unwrap_or_else(|| default_colors.special.clone().unwrap())
}
}

@ -1,37 +1,37 @@
use log::error; use log::error;
fn show_error(explanation: &str) -> ! { fn show_error(explanation: &str) -> ! {
error!("{}", explanation); error!("{}", explanation);
panic!(explanation.to_string()); panic!(explanation.to_string());
} }
pub trait ResultPanicExplanation<T, E: ToString> { pub trait ResultPanicExplanation<T, E: ToString> {
fn unwrap_or_explained_panic(self, explanation: &str) -> T; fn unwrap_or_explained_panic(self, explanation: &str) -> T;
} }
impl<T, E: ToString> ResultPanicExplanation<T, E> for Result<T, E> { impl<T, E: ToString> ResultPanicExplanation<T, E> for Result<T, E> {
fn unwrap_or_explained_panic(self, explanation: &str) -> T { fn unwrap_or_explained_panic(self, explanation: &str) -> T {
match self { match self {
Err(error) => { Err(error) => {
let explanation = format!("{}: {}", explanation, error.to_string()); let explanation = format!("{}: {}", explanation, error.to_string());
show_error(&explanation); show_error(&explanation);
}, }
Ok(content) => content Ok(content) => content,
} }
} }
} }
pub trait OptionPanicExplanation<T> { pub trait OptionPanicExplanation<T> {
fn unwrap_or_explained_panic(self, explanation: &str) -> T; fn unwrap_or_explained_panic(self, explanation: &str) -> T;
} }
impl<T> OptionPanicExplanation<T> for Option<T> { impl<T> OptionPanicExplanation<T> for Option<T> {
fn unwrap_or_explained_panic(self, explanation: &str) -> T { fn unwrap_or_explained_panic(self, explanation: &str) -> T {
match self { match self {
None => { None => {
show_error(explanation); show_error(explanation);
}, }
Some(content) => content Some(content) => content,
} }
} }
} }

@ -5,14 +5,17 @@ mod settings;
mod bridge; mod bridge;
mod editor; mod editor;
mod window;
mod renderer;
mod error_handling; mod error_handling;
mod redraw_scheduler; mod redraw_scheduler;
mod renderer;
mod window;
#[macro_use] extern crate derive_new; #[macro_use]
#[macro_use] extern crate rust_embed; extern crate derive_new;
#[macro_use] extern crate lazy_static; #[macro_use]
extern crate rust_embed;
#[macro_use]
extern crate lazy_static;
use lazy_static::initialize; use lazy_static::initialize;

@ -1,83 +1,87 @@
use std::sync::Mutex; use std::sync::atomic::{AtomicU16, Ordering};
use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::Mutex;
use std::time::Instant; use std::time::Instant;
use log::trace; use log::trace;
use crate::settings::*; use crate::settings::*;
lazy_static! { lazy_static! {
pub static ref REDRAW_SCHEDULER: RedrawScheduler = RedrawScheduler::new(); pub static ref REDRAW_SCHEDULER: RedrawScheduler = RedrawScheduler::new();
} }
#[derive(Clone)] #[derive(Clone)]
struct RedrawSettings { struct RedrawSettings {
extra_buffer_frames: u64, extra_buffer_frames: u64,
} }
pub fn initialize_settings() { pub fn initialize_settings() {
let buffer_frames = if SETTINGS
let buffer_frames = if SETTINGS.neovim_arguments.contains(&String::from("--extraBufferFrames")) { .neovim_arguments
60 .contains(&String::from("--extraBufferFrames"))
}else{ {
1 60
}; } else {
1
SETTINGS.set(&RedrawSettings { };
extra_buffer_frames: buffer_frames,
}); SETTINGS.set(&RedrawSettings {
extra_buffer_frames: buffer_frames,
register_nvim_setting!("extra_buffer_frames", RedrawSettings::extra_buffer_frames); });
}
register_nvim_setting!("extra_buffer_frames", RedrawSettings::extra_buffer_frames);
pub struct RedrawScheduler { }
frames_queued: AtomicU16,
scheduled_frame: Mutex<Option<Instant>> pub struct RedrawScheduler {
} frames_queued: AtomicU16,
scheduled_frame: Mutex<Option<Instant>>,
impl RedrawScheduler { }
pub fn new() -> RedrawScheduler {
RedrawScheduler { impl RedrawScheduler {
frames_queued: AtomicU16::new(1), pub fn new() -> RedrawScheduler {
scheduled_frame: Mutex::new(None) RedrawScheduler {
} frames_queued: AtomicU16::new(1),
} scheduled_frame: Mutex::new(None),
}
pub fn schedule(&self, new_scheduled: Instant) { }
trace!("Redraw scheduled for {:?}", new_scheduled);
let mut scheduled_frame = self.scheduled_frame.lock().unwrap(); pub fn schedule(&self, new_scheduled: Instant) {
if let Some(previous_scheduled) = *scheduled_frame { trace!("Redraw scheduled for {:?}", new_scheduled);
if new_scheduled < previous_scheduled { let mut scheduled_frame = self.scheduled_frame.lock().unwrap();
*scheduled_frame = Some(new_scheduled); if let Some(previous_scheduled) = *scheduled_frame {
} if new_scheduled < previous_scheduled {
} else { *scheduled_frame = Some(new_scheduled);
*scheduled_frame = Some(new_scheduled); }
} } else {
} *scheduled_frame = Some(new_scheduled);
}
pub fn queue_next_frame(&self) { }
trace!("Next frame queued");
let buffer_frames = SETTINGS.get::<RedrawSettings>().extra_buffer_frames; pub fn queue_next_frame(&self) {
self.frames_queued.store(buffer_frames as u16, Ordering::Relaxed); trace!("Next frame queued");
} let buffer_frames = SETTINGS.get::<RedrawSettings>().extra_buffer_frames;
self.frames_queued
pub fn should_draw(&self) -> bool { .store(buffer_frames as u16, Ordering::Relaxed);
let frames_queued = self.frames_queued.load(Ordering::Relaxed); }
if frames_queued > 0 {
self.frames_queued.store(frames_queued - 1, Ordering::Relaxed); pub fn should_draw(&self) -> bool {
true let frames_queued = self.frames_queued.load(Ordering::Relaxed);
} else { if frames_queued > 0 {
let mut next_scheduled_frame = self.scheduled_frame.lock().unwrap(); self.frames_queued
if let Some(scheduled_frame) = *next_scheduled_frame { .store(frames_queued - 1, Ordering::Relaxed);
if scheduled_frame < Instant::now() { true
*next_scheduled_frame = None; } else {
true let mut next_scheduled_frame = self.scheduled_frame.lock().unwrap();
} else { if let Some(scheduled_frame) = *next_scheduled_frame {
false if scheduled_frame < Instant::now() {
} *next_scheduled_frame = None;
} else { true
false } else {
} false
} }
} } else {
} false
}
}
}
}

@ -1,13 +1,20 @@
use std::collections::HashMap; use std::collections::HashMap;
use font_kit::{
family_name::FamilyName,
font::Font,
metrics::Metrics,
properties::{Properties, Stretch, Style, Weight},
source::SystemSource,
};
use lru::LruCache; use lru::LruCache;
use skulpin::skia_safe::{TextBlob, Font as SkiaFont, Typeface, TextBlobBuilder, Data}; use skribo::{FontCollection, FontFamily, FontRef as SkriboFont, LayoutSession, TextStyle};
use font_kit::{source::SystemSource, metrics::Metrics, properties::{Properties, Weight, Style, Stretch}, family_name::FamilyName, font::Font }; use skulpin::skia_safe::{Data, Font as SkiaFont, TextBlob, TextBlobBuilder, Typeface};
use skribo::{LayoutSession, FontRef as SkriboFont, FontFamily, FontCollection, TextStyle};
use log::{trace, info, warn}; use log::{info, trace, warn};
const STANDARD_CHARACTER_STRING: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; const STANDARD_CHARACTER_STRING: &str =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
const MONOSPACE_FONT: &str = "Fira Code Regular Nerd Font Complete.otf"; const MONOSPACE_FONT: &str = "Fira Code Regular Nerd Font Complete.otf";
const MONOSPACE_BOLD_FONT: &str = "Fira Code Bold Nerd Font Complete.otf"; const MONOSPACE_BOLD_FONT: &str = "Fira Code Bold Nerd Font Complete.otf";
@ -45,11 +52,17 @@ const DEFAULT_FONT_SIZE: f32 = 14.0;
struct ShapeKey { struct ShapeKey {
pub text: String, pub text: String,
pub bold: bool, pub bold: bool,
pub italic: bool pub italic: bool,
} }
pub fn add_font_to_collection_by_name(name: &str, source: &SystemSource, collection: &mut FontCollection) -> Option<()> { pub fn add_font_to_collection_by_name(
source.select_family_by_name(name).ok() name: &str,
source: &SystemSource,
collection: &mut FontCollection,
) -> Option<()> {
source
.select_family_by_name(name)
.ok()
.and_then(|matching_fonts| matching_fonts.fonts()[0].load().ok()) .and_then(|matching_fonts| matching_fonts.fonts()[0].load().ok())
.map(|font| collection.add_family(FontFamily::new_from_font(font))) .map(|font| collection.add_family(FontFamily::new_from_font(font)))
} }
@ -61,30 +74,34 @@ pub fn add_asset_font_to_collection(name: &str, collection: &mut FontCollection)
.map(|font| collection.add_family(FontFamily::new_from_font(font))) .map(|font| collection.add_family(FontFamily::new_from_font(font)))
} }
pub fn build_collection_by_font_name(font_name: Option<&str>, bold: bool, italic: bool) -> FontCollection { pub fn build_collection_by_font_name(
font_name: Option<&str>,
bold: bool,
italic: bool,
) -> FontCollection {
let source = SystemSource::new(); let source = SystemSource::new();
let mut collection = FontCollection::new(); let mut collection = FontCollection::new();
if let Some(font_name) = font_name { if let Some(font_name) = font_name {
let weight = if bold { let weight = if bold { Weight::BOLD } else { Weight::NORMAL };
Weight::BOLD
} else {
Weight::NORMAL
};
let style = if italic { let style = if italic { Style::Italic } else { Style::Normal };
Style::Italic
} else {
Style::Normal
};
let properties = Properties { let properties = Properties {
weight, style, stretch: Stretch::NORMAL weight,
style,
stretch: Stretch::NORMAL,
}; };
if let Ok(custom) = source.select_best_match(&[FamilyName::Title(font_name.to_string())], &properties) { if let Ok(custom) =
custom.load().ok() source.select_best_match(&[FamilyName::Title(font_name.to_string())], &properties)
.map(|matching_font| collection.add_family(FontFamily::new_from_font(matching_font))) {
custom
.load()
.ok()
.map(|matching_font| {
collection.add_family(FontFamily::new_from_font(matching_font))
})
.unwrap_or_else(|| warn!("Could not load gui font")); .unwrap_or_else(|| warn!("Could not load gui font"));
} }
} }
@ -104,7 +121,9 @@ pub fn build_collection_by_font_name(font_name: Option<&str>, bold: bool, italic
if add_font_to_collection_by_name(SYSTEM_EMOJI_FONT, &source, &mut collection).is_none() { if add_font_to_collection_by_name(SYSTEM_EMOJI_FONT, &source, &mut collection).is_none() {
#[cfg(feature = "embed-fonts")] #[cfg(feature = "embed-fonts")]
{ {
if cfg!(not(target_os = "macos")) && add_asset_font_to_collection(EMOJI_FONT, &mut collection).is_some() { if cfg!(not(target_os = "macos"))
&& add_asset_font_to_collection(EMOJI_FONT, &mut collection).is_some()
{
info!("Fell back to embedded emoji font"); info!("Fell back to embedded emoji font");
} else { } else {
warn!("Could not load emoji font"); warn!("Could not load emoji font");
@ -115,14 +134,9 @@ pub fn build_collection_by_font_name(font_name: Option<&str>, bold: bool, italic
add_font_to_collection_by_name(SYSTEM_SYMBOL_FONT, &source, &mut collection) add_font_to_collection_by_name(SYSTEM_SYMBOL_FONT, &source, &mut collection)
.unwrap_or_else(|| warn!("Could not load system symbol font")); .unwrap_or_else(|| warn!("Could not load system symbol font"));
#[cfg(feature = "embed-fonts")] #[cfg(feature = "embed-fonts")]
{ {
let wide_style = if bold { let wide_style = if bold { WIDE_BOLD_FONT } else { WIDE_FONT };
WIDE_BOLD_FONT
} else {
WIDE_FONT
};
add_asset_font_to_collection(wide_style, &mut collection) add_asset_font_to_collection(wide_style, &mut collection)
.unwrap_or_else(|| warn!("Could not load embedded wide font")); .unwrap_or_else(|| warn!("Could not load embedded wide font"));
@ -163,7 +177,7 @@ pub struct CachingShaper {
pub base_size: f32, pub base_size: f32,
font_set: FontSet, font_set: FontSet,
font_cache: LruCache<String, SkiaFont>, font_cache: LruCache<String, SkiaFont>,
blob_cache: LruCache<ShapeKey, Vec<TextBlob>> blob_cache: LruCache<ShapeKey, Vec<TextBlob>>,
} }
fn build_skia_font_from_skribo_font(skribo_font: &SkriboFont, base_size: f32) -> Option<SkiaFont> { fn build_skia_font_from_skribo_font(skribo_font: &SkriboFont, base_size: f32) -> Option<SkiaFont> {
@ -196,11 +210,20 @@ impl CachingShaper {
} }
fn metrics(&self) -> Metrics { fn metrics(&self) -> Metrics {
self.font_set.normal.itemize("a").next().unwrap().1.font.metrics() self.font_set
.normal
.itemize("a")
.next()
.unwrap()
.1
.font
.metrics()
} }
pub fn shape(&mut self, text: &str, bold: bool, italic: bool) -> Vec<TextBlob> { pub fn shape(&mut self, text: &str, bold: bool, italic: bool) -> Vec<TextBlob> {
let style = TextStyle { size: self.base_size }; let style = TextStyle {
size: self.base_size,
};
let session = LayoutSession::create(text, &style, &self.font_set.get(bold, italic)); let session = LayoutSession::create(text, &style, &self.font_set.get(bold, italic));
@ -215,7 +238,8 @@ impl CachingShaper {
let mut blob_builder = TextBlobBuilder::new(); let mut blob_builder = TextBlobBuilder::new();
let count = layout_run.glyphs().count(); let count = layout_run.glyphs().count();
let (glyphs, positions) = blob_builder.alloc_run_pos_h(&skia_font, count, ascent, None); let (glyphs, positions) =
blob_builder.alloc_run_pos_h(&skia_font, count, ascent, None);
for (i, glyph) in layout_run.glyphs().enumerate() { for (i, glyph) in layout_run.glyphs().enumerate() {
glyphs[i] = glyph.glyph_id as u16; glyphs[i] = glyph.glyph_id as u16;
@ -251,18 +275,26 @@ impl CachingShaper {
pub fn font_base_dimensions(&mut self) -> (f32, f32) { pub fn font_base_dimensions(&mut self) -> (f32, f32) {
let metrics = self.metrics(); let metrics = self.metrics();
let font_height = (metrics.ascent - metrics.descent) * self.base_size / metrics.units_per_em as f32; let font_height =
(metrics.ascent - metrics.descent) * self.base_size / metrics.units_per_em as f32;
let style = TextStyle { size: self.base_size }; let style = TextStyle {
let session = LayoutSession::create(STANDARD_CHARACTER_STRING, &style, &self.font_set.normal); size: self.base_size,
};
let session =
LayoutSession::create(STANDARD_CHARACTER_STRING, &style, &self.font_set.normal);
let layout_run = session.iter_all().next().unwrap(); let layout_run = session.iter_all().next().unwrap();
let glyph_offsets: Vec<f32> = layout_run.glyphs().map(|glyph| glyph.offset.x).collect(); let glyph_offsets: Vec<f32> = layout_run.glyphs().map(|glyph| glyph.offset.x).collect();
let glyph_advances: Vec<f32> = glyph_offsets.windows(2).map(|pair| pair[1] - pair[0]).collect(); let glyph_advances: Vec<f32> = glyph_offsets
.windows(2)
.map(|pair| pair[1] - pair[0])
.collect();
let mut amounts = HashMap::new(); let mut amounts = HashMap::new();
for advance in glyph_advances.iter() { for advance in glyph_advances.iter() {
amounts.entry(advance.to_string()) amounts
.entry(advance.to_string())
.and_modify(|e| *e += 1) .and_modify(|e| *e += 1)
.or_insert(1); .or_insert(1);
} }
@ -286,7 +318,12 @@ mod tests {
#[test] #[test]
fn unmatched_font_returns_nothing() { fn unmatched_font_returns_nothing() {
assert!(add_font_to_collection_by_name("Foobar", &SystemSource::new(), &mut FontCollection::new()).is_none()); assert!(add_font_to_collection_by_name(
"Foobar",
&SystemSource::new(),
&mut FontCollection::new()
)
.is_none());
} }
#[test] #[test]

@ -6,13 +6,13 @@ use crate::redraw_scheduler::REDRAW_SCHEDULER;
pub enum BlinkState { pub enum BlinkState {
Waiting, Waiting,
On, On,
Off Off,
} }
pub struct BlinkStatus { pub struct BlinkStatus {
state: BlinkState, state: BlinkState,
last_transition: Instant, last_transition: Instant,
previous_cursor: Option<Cursor> previous_cursor: Option<Cursor>,
} }
impl BlinkStatus { impl BlinkStatus {
@ -20,7 +20,7 @@ impl BlinkStatus {
BlinkStatus { BlinkStatus {
state: BlinkState::Waiting, state: BlinkState::Waiting,
last_transition: Instant::now(), last_transition: Instant::now(),
previous_cursor: None previous_cursor: None,
} }
} }
@ -33,25 +33,31 @@ impl BlinkStatus {
} else { } else {
self.state = BlinkState::On; self.state = BlinkState::On;
} }
} }
if new_cursor.blinkwait == Some(0) || if new_cursor.blinkwait == Some(0)
new_cursor.blinkoff == Some(0) || || new_cursor.blinkoff == Some(0)
new_cursor.blinkon == Some(0) { || new_cursor.blinkon == Some(0)
{
return true; return true;
} }
let delay = match self.state { let delay = match self.state {
BlinkState::Waiting => new_cursor.blinkwait, BlinkState::Waiting => new_cursor.blinkwait,
BlinkState::Off => new_cursor.blinkoff, BlinkState::Off => new_cursor.blinkoff,
BlinkState::On => new_cursor.blinkon BlinkState::On => new_cursor.blinkon,
}.filter(|millis| *millis > 0).map(Duration::from_millis); }
.filter(|millis| *millis > 0)
.map(Duration::from_millis);
if delay.map(|delay| self.last_transition + delay < Instant::now()).unwrap_or(false) { if delay
.map(|delay| self.last_transition + delay < Instant::now())
.unwrap_or(false)
{
self.state = match self.state { self.state = match self.state {
BlinkState::Waiting => BlinkState::On, BlinkState::Waiting => BlinkState::On,
BlinkState::On => BlinkState::Off, BlinkState::On => BlinkState::Off,
BlinkState::Off => BlinkState::On BlinkState::Off => BlinkState::On,
}; };
self.last_transition = Instant::now(); self.last_transition = Instant::now();
} }
@ -59,8 +65,9 @@ impl BlinkStatus {
let scheduled_frame = (match self.state { let scheduled_frame = (match self.state {
BlinkState::Waiting => new_cursor.blinkwait, BlinkState::Waiting => new_cursor.blinkwait,
BlinkState::Off => new_cursor.blinkoff, BlinkState::Off => new_cursor.blinkoff,
BlinkState::On => new_cursor.blinkon BlinkState::On => new_cursor.blinkon,
}).map(|delay| self.last_transition + Duration::from_millis(delay)); })
.map(|delay| self.last_transition + Duration::from_millis(delay));
if let Some(scheduled_frame) = scheduled_frame { if let Some(scheduled_frame) = scheduled_frame {
REDRAW_SCHEDULER.schedule(scheduled_frame); REDRAW_SCHEDULER.schedule(scheduled_frame);
@ -68,7 +75,7 @@ impl BlinkStatus {
match self.state { match self.state {
BlinkState::Waiting | BlinkState::Off => false, BlinkState::Waiting | BlinkState::Off => false,
BlinkState::On => true BlinkState::On => true,
} }
} }
} }

@ -1,322 +1,362 @@
mod animation_utils; mod animation_utils;
mod cursor_vfx; mod blink;
mod blink; mod cursor_vfx;
use skulpin::skia_safe::{Canvas, Paint, Path, Point}; use skulpin::skia_safe::{Canvas, Paint, Path, Point};
use crate::settings::*; use crate::editor::{Colors, Cursor, CursorShape, EDITOR};
use crate::renderer::CachingShaper; use crate::redraw_scheduler::REDRAW_SCHEDULER;
use crate::editor::{EDITOR, Colors, Cursor, CursorShape}; use crate::renderer::CachingShaper;
use crate::redraw_scheduler::REDRAW_SCHEDULER; use crate::settings::*;
use animation_utils::*; use animation_utils::*;
use blink::*; use blink::*;
const COMMAND_LINE_DELAY_FRAMES: u64 = 5;
const COMMAND_LINE_DELAY_FRAMES: u64 = 5; const DEFAULT_CELL_PERCENTAGE: f32 = 1.0 / 8.0;
const DEFAULT_CELL_PERCENTAGE: f32 = 1.0 / 8.0;
const STANDARD_CORNERS: &[(f32, f32); 4] = &[(-0.5, -0.5), (0.5, -0.5), (0.5, 0.5), (-0.5, 0.5)];
const STANDARD_CORNERS: &[(f32, f32); 4] = &[(-0.5, -0.5), (0.5, -0.5), (0.5, 0.5), (-0.5, 0.5)];
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
#[derive(Clone)]
#[derive(Clone)] pub struct CursorSettings {
pub struct CursorSettings { animation_length: f32,
animation_length: f32, trail_size: f32,
trail_size: f32, vfx_mode: cursor_vfx::VfxMode,
vfx_mode: cursor_vfx::VfxMode, vfx_opacity: f32,
vfx_opacity: f32, vfx_particle_lifetime: f32,
vfx_particle_lifetime: f32, vfx_particle_density: f32,
vfx_particle_density: f32, vfx_particle_speed: f32,
vfx_particle_speed: f32, vfx_particle_phase: f32,
vfx_particle_phase: f32, vfx_particle_curl: f32,
vfx_particle_curl: f32, }
}
pub fn initialize_settings() {
pub fn initialize_settings() { SETTINGS.set(&CursorSettings {
SETTINGS.set(&CursorSettings { animation_length: 0.13,
animation_length: 0.13, trail_size: 0.7,
trail_size: 0.7, vfx_mode: cursor_vfx::VfxMode::Disabled,
vfx_mode: cursor_vfx::VfxMode::Disabled, vfx_opacity: 200.0,
vfx_opacity: 200.0, vfx_particle_lifetime: 1.2,
vfx_particle_lifetime: 1.2, vfx_particle_density: 7.0,
vfx_particle_density: 7.0, vfx_particle_speed: 10.0,
vfx_particle_speed: 10.0, vfx_particle_phase: 1.5,
vfx_particle_phase: 1.5, vfx_particle_curl: 1.0,
vfx_particle_curl: 1.0, });
});
register_nvim_setting!("cursor_animation_length", CursorSettings::animation_length);
register_nvim_setting!("cursor_animation_length", CursorSettings::animation_length); register_nvim_setting!("cursor_trail_size", CursorSettings::trail_size);
register_nvim_setting!("cursor_trail_size", CursorSettings::trail_size); register_nvim_setting!("cursor_vfx_mode", CursorSettings::vfx_mode);
register_nvim_setting!("cursor_vfx_mode", CursorSettings::vfx_mode); register_nvim_setting!("cursor_vfx_opacity", CursorSettings::vfx_opacity);
register_nvim_setting!("cursor_vfx_opacity", CursorSettings::vfx_opacity); register_nvim_setting!(
register_nvim_setting!("cursor_vfx_particle_lifetime", CursorSettings::vfx_particle_lifetime); "cursor_vfx_particle_lifetime",
register_nvim_setting!("cursor_vfx_particle_density", CursorSettings::vfx_particle_density); CursorSettings::vfx_particle_lifetime
register_nvim_setting!("cursor_vfx_particle_speed", CursorSettings::vfx_particle_speed); );
register_nvim_setting!("cursor_vfx_particle_phase", CursorSettings::vfx_particle_phase); register_nvim_setting!(
register_nvim_setting!("cursor_vfx_particle_curl", CursorSettings::vfx_particle_curl); "cursor_vfx_particle_density",
} CursorSettings::vfx_particle_density
);
// ---------------------------------------------------------------------------- register_nvim_setting!(
"cursor_vfx_particle_speed",
#[derive(Debug, Clone)] CursorSettings::vfx_particle_speed
pub struct Corner { );
start_position: Point, register_nvim_setting!(
current_position: Point, "cursor_vfx_particle_phase",
relative_position: Point, CursorSettings::vfx_particle_phase
previous_destination: Point, );
t: f32, register_nvim_setting!(
} "cursor_vfx_particle_curl",
CursorSettings::vfx_particle_curl
impl Corner { );
pub fn new() -> Corner { }
Corner {
start_position: Point::new(0.0, 0.0), // ----------------------------------------------------------------------------
current_position: Point::new(0.0, 0.0),
relative_position: Point::new(0.0, 0.0), #[derive(Debug, Clone)]
previous_destination: Point::new(-1000.0, -1000.0), pub struct Corner {
t: 0.0, start_position: Point,
} current_position: Point,
} relative_position: Point,
previous_destination: Point,
pub fn update(&mut self, settings: &CursorSettings, font_dimensions: Point, destination: Point, dt: f32) -> bool { t: f32,
// Update destination if needed }
let mut immediate_movement = false;
if destination != self.previous_destination { impl Corner {
let travel_distance = destination - self.previous_destination; pub fn new() -> Corner {
let chars_travel_x = travel_distance.x / font_dimensions.x; Corner {
if travel_distance.y == 0.0 && (chars_travel_x - 1.0).abs() < 0.1 { start_position: Point::new(0.0, 0.0),
// We're moving one character to the right. Make movement immediate to avoid lag current_position: Point::new(0.0, 0.0),
// while typing relative_position: Point::new(0.0, 0.0),
immediate_movement = true; previous_destination: Point::new(-1000.0, -1000.0),
} t: 0.0,
self.t = 0.0; }
self.start_position = self.current_position; }
self.previous_destination = destination;
} pub fn update(
&mut self,
// Check first if animation's over settings: &CursorSettings,
if self.t > 1.0 { font_dimensions: Point,
return false; destination: Point,
} dt: f32,
) -> bool {
// Calculate window-space destination for corner // Update destination if needed
let relative_scaled_position: Point = ( let mut immediate_movement = false;
self.relative_position.x * font_dimensions.x, if destination != self.previous_destination {
self.relative_position.y * font_dimensions.y, let travel_distance = destination - self.previous_destination;
).into(); let chars_travel_x = travel_distance.x / font_dimensions.x;
if travel_distance.y == 0.0 && (chars_travel_x - 1.0).abs() < 0.1 {
let corner_destination = destination + relative_scaled_position; // We're moving one character to the right. Make movement immediate to avoid lag
// while typing
if immediate_movement { immediate_movement = true;
self.t = 1.0; }
self.current_position = corner_destination; self.t = 0.0;
return true; self.start_position = self.current_position;
} self.previous_destination = destination;
}
// Calculate how much a corner will be lagging behind based on how much it's aligned
// with the direction of motion. Corners in front will move faster than corners in the // Check first if animation's over
// back if self.t > 1.0 {
let travel_direction = { return false;
let mut d = destination - self.current_position; }
d.normalize();
d // Calculate window-space destination for corner
}; let relative_scaled_position: Point = (
self.relative_position.x * font_dimensions.x,
let corner_direction = { self.relative_position.y * font_dimensions.y,
let mut d = self.relative_position; )
d.normalize(); .into();
d
}; let corner_destination = destination + relative_scaled_position;
let direction_alignment = travel_direction.dot(corner_direction); if immediate_movement {
self.t = 1.0;
self.current_position = corner_destination;
if self.t == 1.0 { return true;
// We are at destination, move t out of 0-1 range to stop the animation }
self.t = 2.0;
} else { // Calculate how much a corner will be lagging behind based on how much it's aligned
let corner_dt = dt * lerp(1.0, 1.0 - settings.trail_size, -direction_alignment); // with the direction of motion. Corners in front will move faster than corners in the
self.t = (self.t + corner_dt / settings.animation_length).min(1.0) // back
} let travel_direction = {
let mut d = destination - self.current_position;
self.current_position = d.normalize();
ease_point(ease_out_expo, self.start_position, corner_destination, self.t); d
};
true
} let corner_direction = {
} let mut d = self.relative_position;
d.normalize();
pub struct CursorRenderer { d
pub corners: Vec<Corner>, };
pub previous_position: (u64, u64),
pub command_line_delay: u64, let direction_alignment = travel_direction.dot(corner_direction);
blink_status: BlinkStatus,
previous_cursor_shape: Option<CursorShape>, if self.t == 1.0 {
cursor_vfx: Option<Box<dyn cursor_vfx::CursorVfx>>, // We are at destination, move t out of 0-1 range to stop the animation
previous_vfx_mode: cursor_vfx::VfxMode, self.t = 2.0;
} } else {
let corner_dt = dt * lerp(1.0, 1.0 - settings.trail_size, -direction_alignment);
impl CursorRenderer { self.t = (self.t + corner_dt / settings.animation_length).min(1.0)
pub fn new() -> CursorRenderer { }
let mut renderer = CursorRenderer {
corners: vec![Corner::new(); 4], self.current_position = ease_point(
previous_position: (0, 0), ease_out_expo,
command_line_delay: 0, self.start_position,
blink_status: BlinkStatus::new(), corner_destination,
previous_cursor_shape: None, self.t,
//cursor_vfx: Box::new(PointHighlight::new(Point{x:0.0, y:0.0}, HighlightMode::Ripple)), );
cursor_vfx: None,
previous_vfx_mode: cursor_vfx::VfxMode::Disabled, true
}; }
renderer.set_cursor_shape(&CursorShape::Block, DEFAULT_CELL_PERCENTAGE); }
renderer
} pub struct CursorRenderer {
pub corners: Vec<Corner>,
fn set_cursor_shape(&mut self, cursor_shape: &CursorShape, cell_percentage: f32) { pub previous_position: (u64, u64),
self.corners = self.corners pub command_line_delay: u64,
.clone() blink_status: BlinkStatus,
.into_iter().enumerate() previous_cursor_shape: Option<CursorShape>,
.map(|(i, corner)| { cursor_vfx: Option<Box<dyn cursor_vfx::CursorVfx>>,
let (x, y) = STANDARD_CORNERS[i]; previous_vfx_mode: cursor_vfx::VfxMode,
Corner { }
relative_position: match cursor_shape {
CursorShape::Block => (x, y).into(), impl CursorRenderer {
// Transform the x position so that the right side is translated over to pub fn new() -> CursorRenderer {
// the BAR_WIDTH position let mut renderer = CursorRenderer {
CursorShape::Vertical => ((x + 0.5) * cell_percentage - 0.5, y).into(), corners: vec![Corner::new(); 4],
// Do the same as above, but flip the y coordinate and then flip the result previous_position: (0, 0),
// so that the horizontal bar is at the bottom of the character space command_line_delay: 0,
// instead of the top. blink_status: BlinkStatus::new(),
CursorShape::Horizontal => (x, -((-y + 0.5) * cell_percentage - 0.5)).into() previous_cursor_shape: None,
}, //cursor_vfx: Box::new(PointHighlight::new(Point{x:0.0, y:0.0}, HighlightMode::Ripple)),
t: 0.0, cursor_vfx: None,
start_position: corner.current_position, previous_vfx_mode: cursor_vfx::VfxMode::Disabled,
.. corner };
renderer.set_cursor_shape(&CursorShape::Block, DEFAULT_CELL_PERCENTAGE);
} renderer
}) }
.collect::<Vec<Corner>>();
} fn set_cursor_shape(&mut self, cursor_shape: &CursorShape, cell_percentage: f32) {
self.corners = self
pub fn draw(&mut self, .corners
cursor: Cursor, default_colors: &Colors, .clone()
font_width: f32, font_height: f32, .into_iter()
shaper: &mut CachingShaper, canvas: &mut Canvas, .enumerate()
dt: f32) { .map(|(i, corner)| {
let render = self.blink_status.update_status(&cursor); let (x, y) = STANDARD_CORNERS[i];
Corner {
let settings = SETTINGS.get::<CursorSettings>(); relative_position: match cursor_shape {
CursorShape::Block => (x, y).into(),
if settings.vfx_mode != self.previous_vfx_mode { // Transform the x position so that the right side is translated over to
self.cursor_vfx = cursor_vfx::new_cursor_vfx(&settings.vfx_mode); // the BAR_WIDTH position
self.previous_vfx_mode = settings.vfx_mode.clone(); CursorShape::Vertical => ((x + 0.5) * cell_percentage - 0.5, y).into(),
} // Do the same as above, but flip the y coordinate and then flip the result
// so that the horizontal bar is at the bottom of the character space
let mut paint = Paint::new(skulpin::skia_safe::colors::WHITE, None); // instead of the top.
paint.set_anti_alias(true); CursorShape::Horizontal => {
(x, -((-y + 0.5) * cell_percentage - 0.5)).into()
self.previous_position = { }
let editor = EDITOR.lock(); },
let (_, grid_y) = cursor.position; t: 0.0,
let (_, previous_y) = self.previous_position; start_position: corner.current_position,
if grid_y == editor.grid.height - 1 && previous_y != grid_y { ..corner
self.command_line_delay += 1; }
if self.command_line_delay < COMMAND_LINE_DELAY_FRAMES { })
self.previous_position .collect::<Vec<Corner>>();
} else { }
self.command_line_delay = 0;
cursor.position pub fn draw(
} &mut self,
} else { cursor: Cursor,
self.command_line_delay = 0; default_colors: &Colors,
cursor.position font_width: f32,
} font_height: f32,
}; shaper: &mut CachingShaper,
canvas: &mut Canvas,
dt: f32,
let (grid_x, grid_y) = self.previous_position; ) {
let render = self.blink_status.update_status(&cursor);
let (character, font_dimensions): (String, Point) = {
let editor = EDITOR.lock(); let settings = SETTINGS.get::<CursorSettings>();
let character = match editor.grid.get_cell(grid_x, grid_y) {
Some(Some((character, _))) => character.clone(), if settings.vfx_mode != self.previous_vfx_mode {
_ => ' '.to_string(), self.cursor_vfx = cursor_vfx::new_cursor_vfx(&settings.vfx_mode);
}; self.previous_vfx_mode = settings.vfx_mode.clone();
}
let is_double = match editor.grid.get_cell(grid_x + 1, grid_y) {
Some(Some((character, _))) => character.is_empty(), let mut paint = Paint::new(skulpin::skia_safe::colors::WHITE, None);
_ => false, paint.set_anti_alias(true);
};
self.previous_position = {
let font_width = match (is_double, &cursor.shape) { let editor = EDITOR.lock();
(true, CursorShape::Block) => font_width * 2.0, let (_, grid_y) = cursor.position;
_ => font_width let (_, previous_y) = self.previous_position;
}; if grid_y == editor.grid.height - 1 && previous_y != grid_y {
(character, (font_width, font_height).into()) self.command_line_delay += 1;
}; if self.command_line_delay < COMMAND_LINE_DELAY_FRAMES {
let destination: Point = (grid_x as f32 * font_width, grid_y as f32 * font_height).into(); self.previous_position
let center_destination = destination + font_dimensions * 0.5; } else {
self.command_line_delay = 0;
let new_cursor = Some(cursor.shape.clone()); cursor.position
}
if self.previous_cursor_shape != new_cursor { } else {
self.previous_cursor_shape = new_cursor; self.command_line_delay = 0;
self.set_cursor_shape(&cursor.shape, cursor.cell_percentage.unwrap_or(DEFAULT_CELL_PERCENTAGE)); cursor.position
}
if let Some(vfx) = self.cursor_vfx.as_mut() { };
vfx.restart(center_destination);
} let (grid_x, grid_y) = self.previous_position;
}
let (character, font_dimensions): (String, Point) = {
let mut animating = false; let editor = EDITOR.lock();
if !center_destination.is_zero() { let character = match editor.grid.get_cell(grid_x, grid_y) {
for corner in self.corners.iter_mut() { Some(Some((character, _))) => character.clone(),
let corner_animating = corner.update(&settings, font_dimensions, center_destination, dt); _ => ' '.to_string(),
animating |= corner_animating; };
}
let is_double = match editor.grid.get_cell(grid_x + 1, grid_y) {
let vfx_animating = if let Some(vfx) = self.cursor_vfx.as_mut() { Some(Some((character, _))) => character.is_empty(),
vfx.update(&settings, center_destination, (font_width, font_height), dt) _ => false,
}else{ };
false
}; let font_width = match (is_double, &cursor.shape) {
(true, CursorShape::Block) => font_width * 2.0,
animating |= vfx_animating; _ => font_width,
} };
(character, (font_width, font_height).into())
if animating || self.command_line_delay != 0 { };
REDRAW_SCHEDULER.queue_next_frame(); let destination: Point = (grid_x as f32 * font_width, grid_y as f32 * font_height).into();
} let center_destination = destination + font_dimensions * 0.5;
if cursor.enabled && render { let new_cursor = Some(cursor.shape.clone());
// Draw Background
paint.set_color(cursor.background(&default_colors).to_color()); if self.previous_cursor_shape != new_cursor {
self.previous_cursor_shape = new_cursor;
// The cursor is made up of four points, so I create a path with each of the four self.set_cursor_shape(
// corners. &cursor.shape,
let mut path = Path::new(); cursor.cell_percentage.unwrap_or(DEFAULT_CELL_PERCENTAGE),
path.move_to(self.corners[0].current_position); );
path.line_to(self.corners[1].current_position);
path.line_to(self.corners[2].current_position); if let Some(vfx) = self.cursor_vfx.as_mut() {
path.line_to(self.corners[3].current_position); vfx.restart(center_destination);
path.close(); }
canvas.draw_path(&path, &paint); }
// Draw foreground let mut animating = false;
paint.set_color(cursor.foreground(&default_colors).to_color()); if !center_destination.is_zero() {
canvas.save(); for corner in self.corners.iter_mut() {
canvas.clip_path(&path, None, Some(false)); let corner_animating =
corner.update(&settings, font_dimensions, center_destination, dt);
let blobs = &shaper.shape_cached(&character, false, false); animating |= corner_animating;
for blob in blobs.iter() { }
canvas.draw_text_blob(&blob, destination, &paint);
} let vfx_animating = if let Some(vfx) = self.cursor_vfx.as_mut() {
canvas.restore(); vfx.update(&settings, center_destination, (font_width, font_height), dt)
if let Some(vfx) = self.cursor_vfx.as_ref() { } else {
vfx.render(&settings, canvas, &cursor, &default_colors, (font_width, font_height)); false
} };
} animating |= vfx_animating;
} }
}
if animating || self.command_line_delay != 0 {
REDRAW_SCHEDULER.queue_next_frame();
}
if cursor.enabled && render {
// Draw Background
paint.set_color(cursor.background(&default_colors).to_color());
// The cursor is made up of four points, so I create a path with each of the four
// corners.
let mut path = Path::new();
path.move_to(self.corners[0].current_position);
path.line_to(self.corners[1].current_position);
path.line_to(self.corners[2].current_position);
path.line_to(self.corners[3].current_position);
path.close();
canvas.draw_path(&path, &paint);
// Draw foreground
paint.set_color(cursor.foreground(&default_colors).to_color());
canvas.save();
canvas.clip_path(&path, None, Some(false));
let blobs = &shaper.shape_cached(&character, false, false);
for blob in blobs.iter() {
canvas.draw_text_blob(&blob, destination, &paint);
}
canvas.restore();
if let Some(vfx) = self.cursor_vfx.as_ref() {
vfx.render(
&settings,
canvas,
&cursor,
&default_colors,
(font_width, font_height),
);
}
}
}
}

@ -1,18 +1,17 @@
use std::sync::Arc; use std::sync::Arc;
use skulpin::CoordinateSystemHelper;
use skulpin::skia_safe::{Canvas, Paint, Surface, Budgeted, Rect, colors, dash_path_effect};
use skulpin::skia_safe::gpu::SurfaceOrigin;
use log::trace; use log::trace;
use skulpin::skia_safe::gpu::SurfaceOrigin;
use skulpin::skia_safe::{colors, dash_path_effect, Budgeted, Canvas, Paint, Rect, Surface};
use skulpin::CoordinateSystemHelper;
mod caching_shaper; mod caching_shaper;
pub mod cursor_renderer; pub mod cursor_renderer;
pub use caching_shaper::CachingShaper; pub use caching_shaper::CachingShaper;
use crate::editor::{Style, EDITOR};
use cursor_renderer::CursorRenderer; use cursor_renderer::CursorRenderer;
use crate::editor::{EDITOR, Style};
pub struct Renderer { pub struct Renderer {
surface: Option<Surface>, surface: Option<Surface>,
@ -29,13 +28,20 @@ impl Renderer {
let surface = None; let surface = None;
let mut paint = Paint::new(colors::WHITE, None); let mut paint = Paint::new(colors::WHITE, None);
paint.set_anti_alias(false); paint.set_anti_alias(false);
let mut shaper = CachingShaper::new(); let mut shaper = CachingShaper::new();
let (font_width, font_height) = shaper.font_base_dimensions(); let (font_width, font_height) = shaper.font_base_dimensions();
let cursor_renderer = CursorRenderer::new(); let cursor_renderer = CursorRenderer::new();
Renderer { surface, paint, shaper, font_width, font_height, cursor_renderer } Renderer {
surface,
paint,
shaper,
font_width,
font_height,
cursor_renderer,
}
} }
fn set_font(&mut self, name: Option<&str>, size: Option<f32>) { fn set_font(&mut self, name: Option<&str>, size: Option<f32>) {
@ -54,15 +60,31 @@ impl Renderer {
Rect::new(x, y, x + width, y + height) Rect::new(x, y, x + width, y + height)
} }
fn draw_background(&mut self, canvas: &mut Canvas, grid_pos: (u64, u64), cell_width:u64, style: &Option<Arc<Style>>, default_style: &Arc<Style>) { fn draw_background(
&mut self,
canvas: &mut Canvas,
grid_pos: (u64, u64),
cell_width: u64,
style: &Option<Arc<Style>>,
default_style: &Arc<Style>,
) {
let region = self.compute_text_region(grid_pos, cell_width); let region = self.compute_text_region(grid_pos, cell_width);
let style = style.as_ref().unwrap_or(default_style); let style = style.as_ref().unwrap_or(default_style);
self.paint.set_color(style.background(&default_style.colors).to_color()); self.paint
.set_color(style.background(&default_style.colors).to_color());
canvas.draw_rect(region, &self.paint); canvas.draw_rect(region, &self.paint);
} }
fn draw_foreground(&mut self, canvas: &mut Canvas, text: &str, grid_pos: (u64, u64), cell_width: u64, style: &Option<Arc<Style>>, default_style: &Arc<Style>) { fn draw_foreground(
&mut self,
canvas: &mut Canvas,
text: &str,
grid_pos: (u64, u64),
cell_width: u64,
style: &Option<Arc<Style>>,
default_style: &Arc<Style>,
) {
let (grid_x, grid_y) = grid_pos; let (grid_x, grid_y) = grid_pos;
let x = grid_x as f32 * self.font_width; let x = grid_x as f32 * self.font_width;
let y = grid_y as f32 * self.font_height; let y = grid_y as f32 * self.font_height;
@ -79,53 +101,71 @@ impl Renderer {
if style.underline || style.undercurl { if style.underline || style.undercurl {
let line_position = self.shaper.underline_position(); let line_position = self.shaper.underline_position();
let stroke_width = self.shaper.base_size / 10.0; let stroke_width = self.shaper.base_size / 10.0;
self.paint.set_color(style.special(&default_style.colors).to_color()); self.paint
.set_color(style.special(&default_style.colors).to_color());
self.paint.set_stroke_width(stroke_width); self.paint.set_stroke_width(stroke_width);
if style.undercurl { if style.undercurl {
self.paint.set_path_effect(dash_path_effect::new(&[stroke_width * 2.0, stroke_width * 2.0], 0.0)); self.paint.set_path_effect(dash_path_effect::new(
&[stroke_width * 2.0, stroke_width * 2.0],
0.0,
));
} else { } else {
self.paint.set_path_effect(None); self.paint.set_path_effect(None);
} }
canvas.draw_line((x, y - line_position + self.font_height), (x + width, y - line_position + self.font_height), &self.paint); canvas.draw_line(
(x, y - line_position + self.font_height),
(x + width, y - line_position + self.font_height),
&self.paint,
);
} }
self.paint.set_color(style.foreground(&default_style.colors).to_color()); self.paint
.set_color(style.foreground(&default_style.colors).to_color());
let text = text.trim_end(); let text = text.trim_end();
if !text.is_empty() { if !text.is_empty() {
for blob in self.shaper.shape_cached(text, style.bold, style.italic).iter() { for blob in self
.shaper
.shape_cached(text, style.bold, style.italic)
.iter()
{
canvas.draw_text_blob(blob, (x, y), &self.paint); canvas.draw_text_blob(blob, (x, y), &self.paint);
} }
} }
if style.strikethrough { if style.strikethrough {
let line_position = region.center_y(); let line_position = region.center_y();
self.paint.set_color(style.special(&default_style.colors).to_color()); self.paint
.set_color(style.special(&default_style.colors).to_color());
canvas.draw_line((x, line_position), (x + width, line_position), &self.paint); canvas.draw_line((x, line_position), (x + width, line_position), &self.paint);
} }
canvas.restore(); canvas.restore();
} }
pub fn draw(&mut self, gpu_canvas: &mut Canvas, pub fn draw(
&mut self,
gpu_canvas: &mut Canvas,
coordinate_system_helper: &CoordinateSystemHelper, coordinate_system_helper: &CoordinateSystemHelper,
dt: f32) -> bool { dt: f32,
) -> bool {
trace!("Rendering"); trace!("Rendering");
let ((draw_commands, should_clear), default_style, cursor, font_name, font_size) = { let ((draw_commands, should_clear), default_style, cursor, font_name, font_size) = {
let mut editor = EDITOR.lock(); let mut editor = EDITOR.lock();
( (
editor.build_draw_commands(), editor.build_draw_commands(),
editor.default_style.clone(), editor.default_style.clone(),
editor.cursor.clone(), editor.cursor.clone(),
editor.font_name.clone(), editor.font_name.clone(),
editor.font_size editor.font_size,
) )
}; };
let font_changed = let font_changed = font_name != self.shaper.font_name
font_name != self.shaper.font_name || || font_size
font_size.map(|new_size| (new_size - self.shaper.base_size).abs() > std::f32::EPSILON).unwrap_or(false); .map(|new_size| (new_size - self.shaper.base_size).abs() > std::f32::EPSILON)
.unwrap_or(false);
if font_changed { if font_changed {
self.set_font(font_name.as_deref(), font_size); self.set_font(font_name.as_deref(), font_size);
} }
@ -139,7 +179,16 @@ impl Renderer {
let budgeted = Budgeted::YES; let budgeted = Budgeted::YES;
let image_info = gpu_canvas.image_info(); let image_info = gpu_canvas.image_info();
let surface_origin = SurfaceOrigin::TopLeft; let surface_origin = SurfaceOrigin::TopLeft;
let mut surface = Surface::new_render_target(&mut context, budgeted, &image_info, None, surface_origin, None, None).expect("Could not create surface"); let mut surface = Surface::new_render_target(
&mut context,
budgeted,
&image_info,
None,
surface_origin,
None,
None,
)
.expect("Could not create surface");
let canvas = surface.canvas(); let canvas = surface.canvas();
canvas.clear(default_style.colors.background.clone().unwrap().to_color()); canvas.clear(default_style.colors.background.clone().unwrap().to_color());
surface surface
@ -149,23 +198,46 @@ impl Renderer {
coordinate_system_helper.use_logical_coordinates(&mut canvas); coordinate_system_helper.use_logical_coordinates(&mut canvas);
for command in draw_commands.iter() { for command in draw_commands.iter() {
self.draw_background(&mut canvas, command.grid_position.clone(), command.cell_width, &command.style, &default_style); self.draw_background(
&mut canvas,
command.grid_position.clone(),
command.cell_width,
&command.style,
&default_style,
);
} }
for command in draw_commands.iter() { for command in draw_commands.iter() {
self.draw_foreground(&mut canvas, &command.text, command.grid_position.clone(), command.cell_width, &command.style, &default_style); self.draw_foreground(
&mut canvas,
&command.text,
command.grid_position.clone(),
command.cell_width,
&command.style,
&default_style,
);
} }
let image = surface.image_snapshot(); let image = surface.image_snapshot();
let window_size = coordinate_system_helper.window_logical_size(); let window_size = coordinate_system_helper.window_logical_size();
let image_destination = Rect::new(0.0, 0.0, window_size.width as f32, window_size.height as f32); let image_destination = Rect::new(
0.0,
0.0,
window_size.width as f32,
window_size.height as f32,
);
gpu_canvas.draw_image_rect(image, None, &image_destination, &self.paint); gpu_canvas.draw_image_rect(image, None, &image_destination, &self.paint);
self.surface = Some(surface); self.surface = Some(surface);
self.cursor_renderer.draw( self.cursor_renderer.draw(
cursor, &default_style.colors, cursor,
self.font_width, self.font_height, &default_style.colors,
&mut self.shaper, gpu_canvas, dt); self.font_width,
self.font_height,
&mut self.shaper,
gpu_canvas,
dt,
);
font_changed font_changed
} }

@ -1,14 +1,14 @@
use std::any::{Any, TypeId};
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryInto; use std::convert::TryInto;
use std::any::{Any, TypeId};
pub use rmpv::Value; use flexi_logger::{Cleanup, Criterion, Duplicate, Logger, Naming};
use nvim_rs::Neovim; use log::{error, warn};
use nvim_rs::compat::tokio::Compat; use nvim_rs::compat::tokio::Compat;
use flexi_logger::{Logger, Criterion, Naming, Cleanup, Duplicate}; use nvim_rs::Neovim;
use tokio::process::ChildStdin;
use parking_lot::RwLock; use parking_lot::RwLock;
use log::{error,warn}; pub use rmpv::Value;
use tokio::process::ChildStdin;
use crate::error_handling::ResultPanicExplanation; use crate::error_handling::ResultPanicExplanation;
@ -27,12 +27,12 @@ pub trait FromValue {
impl FromValue for f32 { impl FromValue for f32 {
fn from_value(&mut self, value: Value) { fn from_value(&mut self, value: Value) {
if value.is_f64() { if value.is_f64() {
*self = value.as_f64().unwrap() as f32; *self = value.as_f64().unwrap() as f32;
}else if value.is_i64() { } else if value.is_i64() {
*self = value.as_i64().unwrap() as f32; *self = value.as_i64().unwrap() as f32;
}else if value.is_u64() { } else if value.is_u64() {
*self = value.as_u64().unwrap() as f32; *self = value.as_u64().unwrap() as f32;
}else{ } else {
error!("Setting expected an f32, but received {:?}", value); error!("Setting expected an f32, but received {:?}", value);
} }
} }
@ -41,19 +41,18 @@ impl FromValue for f32 {
impl FromValue for u64 { impl FromValue for u64 {
fn from_value(&mut self, value: Value) { fn from_value(&mut self, value: Value) {
if value.is_u64() { if value.is_u64() {
*self = value.as_u64().unwrap(); *self = value.as_u64().unwrap();
}else{ } else {
error!("Setting expected a u64, but received {:?}", value); error!("Setting expected a u64, but received {:?}", value);
} }
} }
} }
impl FromValue for u32 { impl FromValue for u32 {
fn from_value(&mut self, value: Value) { fn from_value(&mut self, value: Value) {
if value.is_u64() { if value.is_u64() {
*self = value.as_u64().unwrap() as u32; *self = value.as_u64().unwrap() as u32;
}else{ } else {
error!("Setting expected a u32, but received {:?}", value); error!("Setting expected a u32, but received {:?}", value);
} }
} }
@ -62,8 +61,8 @@ impl FromValue for u32 {
impl FromValue for i32 { impl FromValue for i32 {
fn from_value(&mut self, value: Value) { fn from_value(&mut self, value: Value) {
if value.is_i64() { if value.is_i64() {
*self = value.as_i64().unwrap() as i32; *self = value.as_i64().unwrap() as i32;
}else{ } else {
error!("Setting expected an i32, but received {:?}", value); error!("Setting expected an i32, but received {:?}", value);
} }
} }
@ -73,10 +72,9 @@ impl FromValue for String {
fn from_value(&mut self, value: Value) { fn from_value(&mut self, value: Value) {
if value.is_str() { if value.is_str() {
*self = String::from(value.as_str().unwrap()); *self = String::from(value.as_str().unwrap());
}else{ } else {
error!("Setting expected a string, but received {:?}", value); error!("Setting expected a string, but received {:?}", value);
} }
} }
} }
@ -84,9 +82,9 @@ impl FromValue for bool {
fn from_value(&mut self, value: Value) { fn from_value(&mut self, value: Value) {
if value.is_bool() { if value.is_bool() {
*self = value.as_bool().unwrap(); *self = value.as_bool().unwrap();
}else if value.is_u64() { } else if value.is_u64() {
*self = value.as_u64().unwrap() != 0; *self = value.as_u64().unwrap() != 0;
}else{ } else {
error!("Setting expected a string, but received {:?}", value); error!("Setting expected a string, but received {:?}", value);
} }
} }
@ -116,8 +114,7 @@ macro_rules! register_nvim_setting {
// Function types to handle settings updates // Function types to handle settings updates
type UpdateHandlerFunc = fn(Value); type UpdateHandlerFunc = fn(Value);
type ReaderFunc = fn()->Value; type ReaderFunc = fn() -> Value;
// The Settings struct acts as a global container where each of Neovide's subsystems can store // The Settings struct acts as a global container where each of Neovide's subsystems can store
// their own settings. It will also coordinate updates between Neovide and nvim to make sure the // their own settings. It will also coordinate updates between Neovide and nvim to make sure the
@ -134,23 +131,28 @@ pub struct Settings {
} }
impl Settings { impl Settings {
fn new() -> Settings { fn new() -> Settings {
let mut log_to_file = false; let mut log_to_file = false;
let neovim_arguments = std::env::args().filter(|arg| { let neovim_arguments = std::env::args()
if arg == "--log" { .filter(|arg| {
log_to_file = true; if arg == "--log" {
false log_to_file = true;
} else { false
true } else {
} true
}).collect::<Vec<String>>(); }
})
.collect::<Vec<String>>();
if log_to_file { if log_to_file {
Logger::with_env_or_str("neovide") Logger::with_env_or_str("neovide")
.duplicate_to_stderr(Duplicate::Error) .duplicate_to_stderr(Duplicate::Error)
.log_to_file() .log_to_file()
.rotate(Criterion::Size(10_000_000), Naming::Timestamps, Cleanup::KeepLogFiles(1)) .rotate(
Criterion::Size(10_000_000),
Naming::Timestamps,
Cleanup::KeepLogFiles(1),
)
.start() .start()
.expect("Could not start logger"); .expect("Could not start logger");
} else { } else {
@ -159,7 +161,7 @@ impl Settings {
.expect("Could not start logger"); .expect("Could not start logger");
} }
Settings{ Settings {
neovim_arguments, neovim_arguments,
settings: RwLock::new(HashMap::new()), settings: RwLock::new(HashMap::new()),
listeners: RwLock::new(HashMap::new()), listeners: RwLock::new(HashMap::new()),
@ -167,32 +169,45 @@ impl Settings {
} }
} }
pub fn set_setting_handlers(&self, property_name: &str, update_func: UpdateHandlerFunc, reader_func: ReaderFunc) { pub fn set_setting_handlers(
self.listeners.write().insert(String::from(property_name), update_func); &self,
self.readers.write().insert(String::from(property_name), reader_func); property_name: &str,
update_func: UpdateHandlerFunc,
reader_func: ReaderFunc,
) {
self.listeners
.write()
.insert(String::from(property_name), update_func);
self.readers
.write()
.insert(String::from(property_name), reader_func);
} }
pub fn set<T: Clone + Send + Sync + 'static >(&self, t: &T) { pub fn set<T: Clone + Send + Sync + 'static>(&self, t: &T) {
let type_id : TypeId = TypeId::of::<T>(); let type_id: TypeId = TypeId::of::<T>();
let t : T = (*t).clone(); let t: T = (*t).clone();
self.settings.write().insert(type_id, Box::new(t)); self.settings.write().insert(type_id, Box::new(t));
} }
pub fn get<'a, T: Clone + Send + Sync + 'static>(&'a self) -> T { pub fn get<'a, T: Clone + Send + Sync + 'static>(&'a self) -> T {
let read_lock = self.settings.read(); let read_lock = self.settings.read();
let boxed = &read_lock.get(&TypeId::of::<T>()).expect("Trying to retrieve a settings object that doesn't exist"); let boxed = &read_lock
let value: &T = boxed.downcast_ref::<T>().expect("Attempted to extract a settings object of the wrong type"); .get(&TypeId::of::<T>())
.expect("Trying to retrieve a settings object that doesn't exist");
let value: &T = boxed
.downcast_ref::<T>()
.expect("Attempted to extract a settings object of the wrong type");
(*value).clone() (*value).clone()
} }
pub async fn read_initial_values(&self, nvim: &Neovim<Compat<ChildStdin>>) { pub async fn read_initial_values(&self, nvim: &Neovim<Compat<ChildStdin>>) {
let keys : Vec<String> = self.listeners.read().keys().cloned().collect(); let keys: Vec<String> = self.listeners.read().keys().cloned().collect();
for name in keys { for name in keys {
let variable_name = format!("neovide_{}", name.to_string()); let variable_name = format!("neovide_{}", name.to_string());
match nvim.get_var(&variable_name).await { match nvim.get_var(&variable_name).await {
Ok(value) => { Ok(value) => {
self.listeners.read().get(&name).unwrap()(value); self.listeners.read().get(&name).unwrap()(value);
}, }
Err(error) => { Err(error) => {
warn!("Initial value load failed for {}: {}", name, error); warn!("Initial value load failed for {}: {}", name, error);
let setting = self.readers.read().get(&name).unwrap()(); let setting = self.readers.read().get(&name).unwrap()();
@ -203,27 +218,32 @@ impl Settings {
} }
pub async fn setup_changed_listeners(&self, nvim: &Neovim<Compat<ChildStdin>>) { pub async fn setup_changed_listeners(&self, nvim: &Neovim<Compat<ChildStdin>>) {
let keys : Vec<String> = self.listeners.read().keys().cloned().collect(); let keys: Vec<String> = self.listeners.read().keys().cloned().collect();
for name in keys { for name in keys {
let vimscript = format!( let vimscript = format!(
concat!( concat!(
"exe \"", "exe \"",
"fun! NeovideNotify{0}Changed(d, k, z)\n", "fun! NeovideNotify{0}Changed(d, k, z)\n",
"call rpcnotify(1, 'setting_changed', '{0}', g:neovide_{0})\n", "call rpcnotify(1, 'setting_changed', '{0}', g:neovide_{0})\n",
"endf\n", "endf\n",
"call dictwatcheradd(g:, 'neovide_{0}', 'NeovideNotify{0}Changed')\"", "call dictwatcheradd(g:, 'neovide_{0}', 'NeovideNotify{0}Changed')\"",
) ),
, name); name
nvim.command(&vimscript).await );
.unwrap_or_explained_panic(&format!("Could not setup setting notifier for {}", name)); nvim.command(&vimscript)
.await
.unwrap_or_explained_panic(&format!(
"Could not setup setting notifier for {}",
name
));
} }
} }
pub fn handle_changed_notification(&self, arguments: Vec<Value>) { pub fn handle_changed_notification(&self, arguments: Vec<Value>) {
let mut arguments = arguments.into_iter(); let mut arguments = arguments.into_iter();
let (name, value) = (arguments.next().unwrap(), arguments.next().unwrap()); let (name, value) = (arguments.next().unwrap(), arguments.next().unwrap());
let name: Result<String, _>= name.try_into(); let name: Result<String, _> = name.try_into();
let name = name.unwrap(); let name = name.unwrap();
self.listeners.read().get(&name).unwrap()(value); self.listeners.read().get(&name).unwrap()(value);

@ -1,20 +1,20 @@
use std::time::{Duration, Instant};
use std::thread::sleep; use std::thread::sleep;
use std::time::{Duration, Instant};
use log::{info, trace, debug, error}; use log::{debug, error, info, trace};
use skulpin::{LogicalSize, PhysicalSize};
use skulpin::sdl2; use skulpin::sdl2;
use skulpin::sdl2::Sdl;
use skulpin::sdl2::video::{Window, FullscreenType};
use skulpin::sdl2::event::{Event, WindowEvent}; use skulpin::sdl2::event::{Event, WindowEvent};
use skulpin::sdl2::keyboard::Keycode; use skulpin::sdl2::keyboard::Keycode;
use skulpin::{RendererBuilder, Renderer as SkulpinRenderer, PresentMode, CoordinateSystem, dpis}; use skulpin::sdl2::video::{FullscreenType, Window};
use skulpin::sdl2::Sdl;
use skulpin::{dpis, CoordinateSystem, PresentMode, Renderer as SkulpinRenderer, RendererBuilder};
use skulpin::{LogicalSize, PhysicalSize};
use crate::settings::*; use crate::bridge::{produce_neovim_keybinding_string, UiCommand, BRIDGE};
use crate::bridge::{produce_neovim_keybinding_string, BRIDGE, UiCommand};
use crate::renderer::Renderer;
use crate::redraw_scheduler::REDRAW_SCHEDULER;
use crate::editor::EDITOR; use crate::editor::EDITOR;
use crate::redraw_scheduler::REDRAW_SCHEDULER;
use crate::renderer::Renderer;
use crate::settings::*;
use crate::INITIAL_DIMENSIONS; use crate::INITIAL_DIMENSIONS;
#[derive(RustEmbed)] #[derive(RustEmbed)]
@ -23,8 +23,8 @@ struct Asset;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
fn windows_fix_dpi() { fn windows_fix_dpi() {
use winapi::um::winuser::SetProcessDpiAwarenessContext;
use winapi::shared::windef::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2; use winapi::shared::windef::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
use winapi::um::winuser::SetProcessDpiAwarenessContext;
unsafe { unsafe {
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
} }
@ -35,7 +35,10 @@ fn handle_new_grid_size(new_size: LogicalSize, renderer: &Renderer) {
let new_width = ((new_size.width + 1) as f32 / renderer.font_width) as u32; let new_width = ((new_size.width + 1) as f32 / renderer.font_width) as u32;
let new_height = ((new_size.height + 1) as f32 / renderer.font_height) as u32; let new_height = ((new_size.height + 1) as f32 / renderer.font_height) as u32;
// Add 1 here to make sure resizing doesn't change the grid size on startup // Add 1 here to make sure resizing doesn't change the grid size on startup
BRIDGE.queue_command(UiCommand::Resize { width: new_width, height: new_height }); BRIDGE.queue_command(UiCommand::Resize {
width: new_width,
height: new_height,
});
} }
} }
@ -50,23 +53,25 @@ struct WindowWrapper {
previous_size: LogicalSize, previous_size: LogicalSize,
previous_dpis: (f32, f32), previous_dpis: (f32, f32),
transparency: f32, transparency: f32,
fullscreen: bool fullscreen: bool,
} }
impl WindowWrapper { impl WindowWrapper {
pub fn new() -> WindowWrapper { pub fn new() -> WindowWrapper {
let context = sdl2::init().expect("Failed to initialize sdl2"); let context = sdl2::init().expect("Failed to initialize sdl2");
let video_subsystem = context.video().expect("Failed to create sdl video subsystem"); let video_subsystem = context
.video()
.expect("Failed to create sdl video subsystem");
video_subsystem.text_input().start(); video_subsystem.text_input().start();
let (width, height) = INITIAL_DIMENSIONS; let (width, height) = INITIAL_DIMENSIONS;
let renderer = Renderer::new(); let renderer = Renderer::new();
let logical_size = LogicalSize { let logical_size = LogicalSize {
width: (width as f32 * renderer.font_width) as u32, width: (width as f32 * renderer.font_width) as u32,
height: (height as f32 * renderer.font_height + 1.0) as u32 height: (height as f32 * renderer.font_height + 1.0) as u32,
}; };
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
windows_fix_dpi(); windows_fix_dpi();
sdl2::hint::set("SDL_MOUSE_FOCUS_CLICKTHROUGH", "1"); sdl2::hint::set("SDL_MOUSE_FOCUS_CLICKTHROUGH", "1");
@ -83,13 +88,14 @@ impl WindowWrapper {
// }; // };
// info!("icon created"); // info!("icon created");
let window = video_subsystem.window("Neovide", logical_size.width, logical_size.height) let window = video_subsystem
.position_centered() .window("Neovide", logical_size.width, logical_size.height)
.allow_highdpi() .position_centered()
.resizable() .allow_highdpi()
.vulkan() .resizable()
.build() .vulkan()
.expect("Failed to create window"); .build()
.expect("Failed to create window");
info!("window created"); info!("window created");
let skulpin_renderer = RendererBuilder::new() let skulpin_renderer = RendererBuilder::new()
@ -104,18 +110,21 @@ impl WindowWrapper {
let previous_size = LogicalSize::new(&window).unwrap(); let previous_size = LogicalSize::new(&window).unwrap();
let previous_dpis = dpis(&window).unwrap(); let previous_dpis = dpis(&window).unwrap();
WindowWrapper { WindowWrapper {
context, window, skulpin_renderer, renderer, context,
window,
skulpin_renderer,
renderer,
mouse_down: false, mouse_down: false,
mouse_position: LogicalSize { mouse_position: LogicalSize {
width: 0, width: 0,
height: 0 height: 0,
}, },
title: String::from("Neovide"), title: String::from("Neovide"),
previous_size, previous_size,
previous_dpis, previous_dpis,
transparency: 1.0, transparency: 1.0,
fullscreen: false fullscreen: false,
} }
} }
@ -123,7 +132,9 @@ impl WindowWrapper {
let editor_title = { EDITOR.lock().title.clone() }; let editor_title = { EDITOR.lock().title.clone() };
if self.title != editor_title { if self.title != editor_title {
self.title = editor_title; self.title = editor_title;
self.window.set_title(&self.title).expect("Could not set title"); self.window
.set_title(&self.title)
.expect("Could not set title");
} }
let transparency = { SETTINGS.get::<WindowSettings>().transparency }; let transparency = { SETTINGS.get::<WindowSettings>().transparency };
@ -138,7 +149,7 @@ impl WindowWrapper {
if self.fullscreen != fullscreen { if self.fullscreen != fullscreen {
let state = match fullscreen { let state = match fullscreen {
true => FullscreenType::Desktop, true => FullscreenType::Desktop,
false => FullscreenType::Off false => FullscreenType::Off,
}; };
self.window.set_fullscreen(state).ok(); self.window.set_fullscreen(state).ok();
self.fullscreen = fullscreen; self.fullscreen = fullscreen;
@ -149,41 +160,51 @@ impl WindowWrapper {
let modifiers = self.context.keyboard().mod_state(); let modifiers = self.context.keyboard().mod_state();
if keycode.is_some() || text.is_some() { if keycode.is_some() || text.is_some() {
trace!("Keyboard Input Received: keycode-{:?} modifiers-{:?} text-{:?}", keycode, modifiers, text); trace!(
"Keyboard Input Received: keycode-{:?} modifiers-{:?} text-{:?}",
keycode,
modifiers,
text
);
} }
if let Some(keybinding_string) = produce_neovim_keybinding_string(keycode, text, modifiers) { if let Some(keybinding_string) = produce_neovim_keybinding_string(keycode, text, modifiers)
{
BRIDGE.queue_command(UiCommand::Keyboard(keybinding_string)); BRIDGE.queue_command(UiCommand::Keyboard(keybinding_string));
} }
} }
pub fn handle_pointer_motion(&mut self, x: i32, y: i32) { pub fn handle_pointer_motion(&mut self, x: i32, y: i32) {
let previous_position = self.mouse_position; let previous_position = self.mouse_position;
if let Ok(new_mouse_position) = LogicalSize::from_physical_size_tuple(( if let Ok(new_mouse_position) = LogicalSize::from_physical_size_tuple(
(
(x as f32 / self.renderer.font_width) as u32, (x as f32 / self.renderer.font_width) as u32,
(y as f32 / self.renderer.font_height) as u32 (y as f32 / self.renderer.font_height) as u32,
), ),
&self.window &self.window,
) { ) {
self.mouse_position = new_mouse_position; self.mouse_position = new_mouse_position;
if self.mouse_down && previous_position != self.mouse_position { if self.mouse_down && previous_position != self.mouse_position {
BRIDGE.queue_command(UiCommand::Drag(self.mouse_position.width, self.mouse_position.height)); BRIDGE.queue_command(UiCommand::Drag(
self.mouse_position.width,
self.mouse_position.height,
));
} }
} }
} }
pub fn handle_pointer_down(&mut self) { pub fn handle_pointer_down(&mut self) {
BRIDGE.queue_command(UiCommand::MouseButton { BRIDGE.queue_command(UiCommand::MouseButton {
action: String::from("press"), action: String::from("press"),
position: (self.mouse_position.width, self.mouse_position.height) position: (self.mouse_position.width, self.mouse_position.height),
}); });
self.mouse_down = true; self.mouse_down = true;
} }
pub fn handle_pointer_up(&mut self) { pub fn handle_pointer_up(&mut self) {
BRIDGE.queue_command(UiCommand::MouseButton { BRIDGE.queue_command(UiCommand::MouseButton {
action: String::from("release"), action: String::from("release"),
position: (self.mouse_position.width, self.mouse_position.height) position: (self.mouse_position.width, self.mouse_position.height),
}); });
self.mouse_down = false; self.mouse_down = false;
} }
@ -198,9 +219,9 @@ impl WindowWrapper {
}; };
if let Some(input_type) = vertical_input_type { if let Some(input_type) = vertical_input_type {
BRIDGE.queue_command(UiCommand::Scroll { BRIDGE.queue_command(UiCommand::Scroll {
direction: input_type.to_string(), direction: input_type.to_string(),
position: (self.mouse_position.width, self.mouse_position.height) position: (self.mouse_position.width, self.mouse_position.height),
}); });
} }
@ -213,9 +234,9 @@ impl WindowWrapper {
}; };
if let Some(input_type) = horizontal_input_type { if let Some(input_type) = horizontal_input_type {
BRIDGE.queue_command(UiCommand::Scroll { BRIDGE.queue_command(UiCommand::Scroll {
direction: input_type.to_string(), direction: input_type.to_string(),
position: (self.mouse_position.width, self.mouse_position.height) position: (self.mouse_position.width, self.mouse_position.height),
}); });
} }
} }
@ -240,9 +261,12 @@ impl WindowWrapper {
if let Ok(new_dpis) = dpis(&self.window) { if let Ok(new_dpis) = dpis(&self.window) {
if self.previous_dpis != new_dpis { if self.previous_dpis != new_dpis {
let physical_size = PhysicalSize::new(&self.window); let physical_size = PhysicalSize::new(&self.window);
self.window.set_size( self.window
(physical_size.width as f32 * new_dpis.0 / self.previous_dpis.0) as u32, .set_size(
(physical_size.height as f32 * new_dpis.1 / self.previous_dpis.1) as u32).unwrap(); (physical_size.width as f32 * new_dpis.0 / self.previous_dpis.0) as u32,
(physical_size.height as f32 * new_dpis.1 / self.previous_dpis.1) as u32,
)
.unwrap();
self.previous_dpis = new_dpis; self.previous_dpis = new_dpis;
} }
} }
@ -251,13 +275,17 @@ impl WindowWrapper {
let current_size = self.previous_size; let current_size = self.previous_size;
if REDRAW_SCHEDULER.should_draw() || SETTINGS.get::<WindowSettings>().no_idle { if REDRAW_SCHEDULER.should_draw() || SETTINGS.get::<WindowSettings>().no_idle {
let renderer = &mut self.renderer; let renderer = &mut self.renderer;
if self.skulpin_renderer.draw(&self.window, |canvas, coordinate_system_helper| { if self
let dt = 1.0 / (SETTINGS.get::<WindowSettings>().refresh_rate as f32); .skulpin_renderer
.draw(&self.window, |canvas, coordinate_system_helper| {
if renderer.draw(canvas, coordinate_system_helper, dt) { let dt = 1.0 / (SETTINGS.get::<WindowSettings>().refresh_rate as f32);
handle_new_grid_size(current_size, &renderer)
} if renderer.draw(canvas, coordinate_system_helper, dt) {
}).is_err() { handle_new_grid_size(current_size, &renderer)
}
})
.is_err()
{
error!("Render failed. Closing"); error!("Render failed. Closing");
return false; return false;
} }
@ -271,19 +299,21 @@ struct WindowSettings {
refresh_rate: u64, refresh_rate: u64,
transparency: f32, transparency: f32,
no_idle: bool, no_idle: bool,
fullscreen: bool fullscreen: bool,
} }
pub fn initialize_settings() { pub fn initialize_settings() {
let no_idle = SETTINGS.neovim_arguments.contains(&String::from("--noIdle")); let no_idle = SETTINGS
.neovim_arguments
.contains(&String::from("--noIdle"));
SETTINGS.set(&WindowSettings { SETTINGS.set(&WindowSettings {
refresh_rate: 60, refresh_rate: 60,
transparency: 1.0, transparency: 1.0,
no_idle, no_idle,
fullscreen: false fullscreen: false,
}); });
register_nvim_setting!("refresh_rate", WindowSettings::refresh_rate); register_nvim_setting!("refresh_rate", WindowSettings::refresh_rate);
register_nvim_setting!("transparency", WindowSettings::transparency); register_nvim_setting!("transparency", WindowSettings::transparency);
register_nvim_setting!("no_idle", WindowSettings::no_idle); register_nvim_setting!("no_idle", WindowSettings::no_idle);
@ -294,7 +324,10 @@ pub fn ui_loop() {
let mut window = WindowWrapper::new(); let mut window = WindowWrapper::new();
info!("Starting window event loop"); info!("Starting window event loop");
let mut event_pump = window.context.event_pump().expect("Could not create sdl event pump"); let mut event_pump = window
.context
.event_pump()
.expect("Could not create sdl event pump");
'running: loop { 'running: loop {
let frame_start = Instant::now(); let frame_start = Instant::now();
@ -306,20 +339,29 @@ pub fn ui_loop() {
for event in event_pump.poll_iter() { for event in event_pump.poll_iter() {
match event { match event {
Event::Quit {..} => break 'running, Event::Quit { .. } => break 'running,
Event::KeyDown { keycode: Some(received_keycode), .. } => { Event::KeyDown {
keycode: Some(received_keycode),
..
} => {
keycode = Some(received_keycode); keycode = Some(received_keycode);
}, }
Event::TextInput { text, .. } => keytext = Some(text), Event::TextInput { text, .. } => keytext = Some(text),
Event::MouseMotion { x, y, .. } => window.handle_pointer_motion(x, y), Event::MouseMotion { x, y, .. } => window.handle_pointer_motion(x, y),
Event::MouseButtonDown { .. } => window.handle_pointer_down(), Event::MouseButtonDown { .. } => window.handle_pointer_down(),
Event::MouseButtonUp { .. } => window.handle_pointer_up(), Event::MouseButtonUp { .. } => window.handle_pointer_up(),
Event::MouseWheel { x, y, .. } => window.handle_mouse_wheel(x, y), Event::MouseWheel { x, y, .. } => window.handle_mouse_wheel(x, y),
Event::Window { win_event: WindowEvent::FocusLost, .. } => window.handle_focus_lost(), Event::Window {
Event::Window { win_event: WindowEvent::FocusGained, .. } => { win_event: WindowEvent::FocusLost,
..
} => window.handle_focus_lost(),
Event::Window {
win_event: WindowEvent::FocusGained,
..
} => {
ignore_text_this_frame = true; // Ignore any text events on the first frame when focus is regained. https://github.com/Kethku/neovide/issues/193 ignore_text_this_frame = true; // Ignore any text events on the first frame when focus is regained. https://github.com/Kethku/neovide/issues/193
window.handle_focus_gained(); window.handle_focus_gained();
}, }
Event::Window { .. } => REDRAW_SCHEDULER.queue_next_frame(), Event::Window { .. } => REDRAW_SCHEDULER.queue_next_frame(),
_ => {} _ => {}
} }

Loading…
Cancel
Save