Merge pull request #197 from j4qfrost/rustfmt

rust fmt
macos-click-through
Keith Simmons 5 years ago committed by GitHub
commit 2815a28e68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -9,10 +9,16 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: "Check formatting"
run: |
cargo fmt --all -- --check
- name: Build
run: |
cargo test
cargo build --release
- uses: actions/upload-artifact@v1
with:
name: neovide-windows.exe
@ -26,12 +32,18 @@ jobs:
- uses: actions/checkout@v2
- name: Install Vulkan SDK
run: brew cask install apenngrace/vulkan/vulkan-sdk
- name: "Check formatting"
run: |
cargo fmt --all -- --check
- name: Build
run: |
cargo test
cargo build --release
cargo install cargo-bundle
cargo bundle --release
- name: Update Bundle
run: |
cd target/release/bundle/osx/Neovide.app/Contents
@ -66,6 +78,10 @@ jobs:
run: |
sudo apt-get install -y curl gnupg ca-certificates git gcc-multilib g++-multilib cmake libssl-dev pkg-config libfreetype6-dev libasound2-dev libexpat1-dev libxcb-composite0-dev libbz2-dev freeglut3-dev libxi-dev libsdl2-dev
- name: "Check formatting"
run: |
cargo fmt --all -- --check
- name: Build
run: |
cargo test

@ -1,13 +1,13 @@
use std::convert::TryInto;
use std::error;
use std::fmt;
use std::convert::TryInto;
use rmpv::Value;
use skulpin::skia_safe::Color4f;
use crate::editor::EDITOR;
use crate::editor::{Colors, CursorMode, CursorShape, Style};
use crate::error_handling::ResultPanicExplanation;
use crate::editor::{Colors, Style, CursorMode, CursorShape};
#[derive(Debug, Clone)]
pub enum EventParseError {
@ -18,7 +18,7 @@ pub enum EventParseError {
InvalidI64(Value),
InvalidBool(Value),
InvalidWindowAnchor(Value),
InvalidEventFormat
InvalidEventFormat,
}
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::InvalidI64(value) => write!(f, "invalid i64 format {}", value),
EventParseError::InvalidBool(value) => write!(f, "invalid bool format {}", value),
EventParseError::InvalidWindowAnchor(value) => write!(f, "invalid window anchor format {}", value),
EventParseError::InvalidEventFormat => write!(f, "invalid event format")
EventParseError::InvalidWindowAnchor(value) => {
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 text: String,
pub highlight_id: Option<u64>,
pub repeat: Option<u64>
pub repeat: Option<u64>,
}
pub type StyledContent = Vec<(u64, String)>;
@ -66,7 +68,7 @@ pub enum MessageKind {
ReturnPrompt,
QuickFix,
SearchCount,
Warning
Warning,
}
impl MessageKind {
@ -84,7 +86,7 @@ impl MessageKind {
"quickfix" => MessageKind::QuickFix,
"search_count" => MessageKind::SearchCount,
"wmsg" => MessageKind::Warning,
_ => MessageKind::Unknown
_ => MessageKind::Unknown,
}
}
}
@ -101,7 +103,7 @@ pub enum GuiOption {
Pumblend(u64),
ShowTabLine(u64),
TermGuiColors(bool),
Unknown(String, Value)
Unknown(String, Value),
}
#[derive(Debug)]
@ -109,44 +111,137 @@ pub enum WindowAnchor {
NorthWest,
NorthEast,
SouthWest,
SouthEast
SouthEast,
}
#[derive(Debug)]
pub enum RedrawEvent {
SetTitle { title: String },
ModeInfoSet { cursor_modes: Vec<CursorMode> },
OptionSet { gui_option: GuiOption },
ModeChange { mode_index: u64 },
SetTitle {
title: String,
},
ModeInfoSet {
cursor_modes: Vec<CursorMode>,
},
OptionSet {
gui_option: GuiOption,
},
ModeChange {
mode_index: u64,
},
BusyStart,
BusyStop,
Flush,
Resize { grid: u64, width: u64, height: u64 },
DefaultColorsSet { colors: Colors },
HighlightAttributesDefine { id: u64, style: Style },
GridLine { grid: u64, row: 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 },
Resize {
grid: u64,
width: u64,
height: u64,
},
DefaultColorsSet {
colors: Colors,
},
HighlightAttributesDefine {
id: u64,
style: Style,
},
GridLine {
grid: u64,
row: 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,
CommandLineBlockShow { lines: Vec<StyledContent> },
CommandLineBlockAppend { line: StyledContent },
CommandLineBlockShow {
lines: Vec<StyledContent>,
},
CommandLineBlockAppend {
line: StyledContent,
},
CommandLineBlockHide,
MessageShow { kind: MessageKind, content: StyledContent, replace_last: bool },
MessageShow {
kind: MessageKind,
content: StyledContent,
replace_last: bool,
},
MessageClear,
MessageShowMode { content: StyledContent },
MessageShowCommand { content: StyledContent },
MessageRuler { content: StyledContent },
MessageHistoryShow { entries: Vec<(MessageKind, StyledContent)>}
MessageShowMode {
content: StyledContent,
},
MessageShowCommand {
content: StyledContent,
},
MessageRuler {
content: StyledContent,
},
MessageHistoryShow {
entries: Vec<(MessageKind, StyledContent)>,
},
}
fn unpack_color(packed_color: u64) -> Color4f {
@ -158,7 +253,7 @@ fn unpack_color(packed_color: u64) -> Color4f {
r: r / 255.0,
g: g / 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>> {
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)>> {
@ -185,7 +282,9 @@ fn parse_map(map_value: Value) -> Result<Vec<(Value, Value)>> {
}
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> {
@ -204,12 +303,13 @@ fn parse_set_title(set_title_arguments: Vec<Value>) -> Result<RedrawEvent> {
let [title] = extract_values(set_title_arguments, [Value::Nil])?;
Ok(RedrawEvent::SetTitle {
title: parse_string(title)?
title: parse_string(title)?,
})
}
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 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() {
"cursor_shape" => {
mode_info.shape = CursorShape::from_type_name(&parse_string(value)?);
},
}
"cell_percentage" => {
mode_info.cell_percentage = Some(parse_u64(value)? as f32 / 100.0);
},
}
"blinkwait" => {
mode_info.blinkwait = Some(parse_u64(value)?);
},
}
"blinkon" => {
mode_info.blinkon = Some(parse_u64(value)?);
},
}
"blinkoff" => {
mode_info.blinkoff = Some(parse_u64(value)?);
}
"attr_id" => {
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);
}
Ok(RedrawEvent::ModeInfoSet {
cursor_modes
})
Ok(RedrawEvent::ModeInfoSet { cursor_modes })
}
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)?),
"showtabline" => GuiOption::ShowTabLine(parse_u64(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])?;
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> {
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 {
grid: parse_u64(grid_id)?,
width: parse_u64(width)?,
height: parse_u64(height)?
height: parse_u64(height)?,
})
}
fn parse_default_colors(default_colors_arguments: Vec<Value>) -> Result<RedrawEvent> {
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 {
colors: Colors {
foreground: Some(unpack_color(parse_u64(foreground)?)),
background: Some(unpack_color(parse_u64(background)?)),
special: Some(unpack_color(parse_u64(special)?)),
}
},
})
}
@ -311,17 +411,25 @@ fn parse_style(style_map: Value) -> Result<Style> {
for attribute in attributes {
if let (Value::String(name), value) = attribute {
match (name.as_str().unwrap(), value) {
("foreground", Value::Integer(packed_color)) => style.colors.foreground = 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())),
("foreground", Value::Integer(packed_color)) => {
style.colors.foreground = 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,
("italic", Value::Boolean(italic)) => style.italic = italic,
("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,
("undercurl", Value::Boolean(undercurl)) => style.undercurl = undercurl,
("blend", Value::Integer(blend)) => style.blend = blend.as_u64().unwrap() as u8,
_ => println!("Ignored style attribute: {}", name)
_ => println!("Ignored style attribute: {}", name),
}
} else {
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> {
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)?;
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> {
@ -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 text_value = cell_contents.first_mut()
let text_value = cell_contents
.first_mut()
.map(|v| take_value(v))
.ok_or(EventParseError::InvalidEventFormat)?;
let highlight_id = cell_contents.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()?;
let highlight_id = cell_contents
.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 {
text: parse_string(text_value)?,
highlight_id,
repeat
repeat,
})
}
@ -371,40 +492,63 @@ fn parse_grid_line(grid_line_arguments: Vec<Value>) -> Result<RedrawEvent> {
cells: parse_array(cells)?
.into_iter()
.map(parse_grid_line_cell)
.collect::<Result<Vec<GridLineCell>>>()?
.collect::<Result<Vec<GridLineCell>>>()?,
})
}
fn parse_clear(clear_arguments: Vec<Value>) -> Result<RedrawEvent> {
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> {
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 {
grid: parse_u64(grid_id)?,
row: parse_u64(row)?,
column: parse_u64(column)?
column: parse_u64(column)?,
})
}
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 [grid_id, top, bottom, left, right, rows, columns] = extract_values(grid_scroll_arguments, values)?;
let 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 {
grid: parse_u64(grid_id)?,
top: parse_u64(top)?, bottom: parse_u64(bottom)?,
left: parse_u64(left)?, right: parse_u64(right)?,
rows: parse_i64(rows)?, columns: parse_i64(columns)?
top: parse_u64(top)?,
bottom: parse_u64(bottom)?,
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> {
let 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)?;
let 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 {
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_column: parse_u64(start_column)?,
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),
"SW" => Ok(WindowAnchor::SouthWest),
"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> {
let 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)?;
let 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 {
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_row: parse_u64(anchor_row)?,
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 {
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])?;
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])?;
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)?,
row: parse_u64(row)?,
scrolled: parse_bool(scrolled)?,
separator_character: parse_string(separator_character)?
separator_character: parse_string(separator_character)?,
})
}
fn parse_styled_content(line: Value) -> Result<StyledContent> {
parse_array(line)?.into_iter().map(|tuple| {
let [style_id, text] = extract_values(parse_array(tuple)?, [Value::Nil, Value::Nil])?;
parse_array(line)?
.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)?))
}).collect()
Ok((parse_u64(style_id)?, parse_string(text)?))
})
.collect()
}
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 [content, position, first_character, prompt, indent, level] = extract_values(cmdline_show_arguments, values)?;
let 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 {
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)?,
prompt: parse_string(prompt)?,
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 {
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> {
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 {
character: parse_string(character)?,
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)?
.into_iter()
.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])?;
Ok(RedrawEvent::CommandLineBlockAppend {
line: parse_styled_content(line)?
line: parse_styled_content(line)?,
})
}
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 {
kind: MessageKind::parse(&parse_string(kind)?),
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((
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)?
.into_iter()
.map(parse_msg_history_entry)
.collect::<Result<_>>()?
.collect::<Result<_>>()?,
})
}
pub fn parse_redraw_event(event_value: Value) -> Result<Vec<RedrawEvent>> {
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)
.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_ruler" => Some(parse_msg_ruler(event_parameters)?),
"msg_history_show" => Some(parse_msg_history_show(event_parameters)?),
_ => None
_ => None,
};
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)
}
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 {
let parsed_events = parse_redraw_event(events)
.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 log::trace;
use nvim_rs::{compat::tokio::Compat, Handler, Neovim};
use rmpv::Value;
use tokio::process::ChildStdin;
use tokio::task;
use log::trace;
use crate::settings::SETTINGS;
use super::events::handle_redraw_event_group;
use crate::settings::SETTINGS;
#[derive(Clone)]
pub struct NeovimHandler();
@ -15,18 +15,23 @@ pub struct NeovimHandler();
impl Handler for NeovimHandler {
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);
task::spawn_blocking(move || {
match event_name.as_ref() {
"redraw" => {
handle_redraw_event_group(arguments);
},
"setting_changed" => {
SETTINGS.handle_changed_notification(arguments);
},
_ => {}
task::spawn_blocking(move || match event_name.as_ref() {
"redraw" => {
handle_redraw_event_group(arguments);
}
"setting_changed" => {
SETTINGS.handle_changed_notification(arguments);
}
}).await.ok();
_ => {}
})
.await
.ok();
}
}

@ -1,9 +1,9 @@
mod qwerty;
use log::{trace, error};
use log::{error, trace};
use skulpin::sdl2::keyboard::{Keycode, Mod};
use crate::settings::{SETTINGS, FromValue, Value};
use crate::settings::{FromValue, Value, SETTINGS};
use qwerty::*;
@ -14,14 +14,17 @@ pub fn unsupported_key<R>(keycode: Keycode) -> Option<R> {
#[derive(Clone)]
pub enum KeyboardLayout {
Qwerty
Qwerty,
}
impl FromValue for KeyboardLayout {
fn from_value(&mut self, value: Value) {
match value.as_str() {
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 {
fn from(layout: KeyboardLayout) -> Self {
match layout {
KeyboardLayout::Qwerty => "qwerty".into()
KeyboardLayout::Qwerty => "qwerty".into(),
}
}
}
#[derive(Clone)]
struct KeyboardSettings {
layout: KeyboardLayout
layout: KeyboardLayout,
}
pub fn initialize_settings() {
SETTINGS.set(&KeyboardSettings {
layout: KeyboardLayout::Qwerty
layout: KeyboardLayout::Qwerty,
});
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 special = special;
@ -80,7 +90,11 @@ fn append_modifiers(keycode_text: &str, special: bool, shift: bool, ctrl: bool,
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 ctrl = modifiers.contains(Mod::LCTRLMOD) || modifiers.contains(Mod::RCTRLMOD);
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 {
(match SETTINGS.get::<KeyboardSettings>().layout {
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 {
None
}

@ -2,7 +2,12 @@ use super::unsupported_key;
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) {
(Keycode::Backspace, shift, ctrl, alt) => Some(("BS", 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::BrightnessUp, _, _, _) => unsupported_key(Keycode::BrightnessUp),
(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::KbdIllumUp, _, _, _) => unsupported_key(Keycode::KbdIllumUp),
(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 ui_commands;
use std::sync::Arc;
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 tokio::runtime::Runtime;
use rmpv::Value;
use tokio::process::Command;
use tokio::runtime::Runtime;
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::*;
use handler::NeovimHandler;
pub use layouts::*;
use crate::settings::*;
pub use ui_commands::UiCommand;
use handler::NeovimHandler;
use crate::error_handling::ResultPanicExplanation;
use crate::INITIAL_DIMENSIONS;
lazy_static! {
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>) {
let (width, height) = INITIAL_DIMENSIONS;
let (mut nvim, io_handler, _) = create::new_child_cmd(&mut create_nvim_command(), NeovimHandler()).await
.unwrap_or_explained_panic("Could not locate or start the neovim process");
let (mut nvim, io_handler, _) =
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 {
info!("Close watcher started");
@ -71,7 +72,7 @@ async fn start_process(mut receiver: UnboundedReceiver<UiCommand>) {
if !error.is_channel_closed() {
error!("Error: '{}'", error);
}
},
}
Ok(Ok(())) => {}
};
std::process::exit(0);
@ -87,15 +88,22 @@ async fn start_process(mut receiver: UnboundedReceiver<UiCommand>) {
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");
let mut options = UiAttachOptions::new();
options.set_linegrid_external(true);
options.set_rgb(true);
nvim.ui_attach(width as i64, height as i64, &options).await
nvim.ui_attach(width as i64, height as i64, &options)
.await
.unwrap_or_explained_panic("Could not attach ui to neovim process");
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");
@ -109,9 +117,11 @@ async fn start_process(mut receiver: UnboundedReceiver<UiCommand>) {
.partition(|command| command.is_resize());
for command in resize_list
.into_iter().last().into_iter()
.chain(other_commands.into_iter()) {
.into_iter()
.last()
.into_iter()
.chain(other_commands.into_iter())
{
let input_nvim = input_nvim.clone();
tokio::spawn(async move {
trace!("Executing UiCommand: {:?}", &command);
@ -124,13 +134,14 @@ async fn start_process(mut receiver: UnboundedReceiver<UiCommand>) {
SETTINGS.read_initial_values(&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();
}
pub struct Bridge {
_runtime: Runtime, // Necessary to keep runtime running
sender: UnboundedSender<UiCommand>
sender: UnboundedSender<UiCommand>,
}
impl Bridge {
@ -142,13 +153,16 @@ impl Bridge {
start_process(receiver).await;
});
Bridge { _runtime: runtime, sender }
Bridge {
_runtime: runtime,
sender,
}
}
pub fn queue_command(&self, command: UiCommand) {
trace!("UiCommand queued: {:?}", &command);
self.sender.send(command)
.unwrap_or_explained_panic(
"Could not send UI command from the window system to the neovim process.");
self.sender.send(command).unwrap_or_explained_panic(
"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::Neovim;
use tokio::process::ChildStdin;
use log::trace;
#[derive(Debug, Clone)]
pub enum UiCommand {
Resize { width: u32, height: u32 },
Resize {
width: u32,
height: u32,
},
Keyboard(String),
MouseButton { action: String, position: (u32, u32) },
Scroll { direction: String, position: (u32, u32) },
MouseButton {
action: String,
position: (u32, u32),
},
Scroll {
direction: String,
position: (u32, u32),
},
Drag(u32, u32),
FocusLost,
FocusGained
FocusGained,
}
impl UiCommand {
pub async fn execute(self, nvim: &Neovim<Compat<ChildStdin>>) {
match self {
UiCommand::Resize { width, height } =>
nvim.ui_try_resize(width.max(10) as i64, height.max(3) as i64).await
.expect("Resize failed"),
UiCommand::Keyboard(input_command) => {
UiCommand::Resize { width, height } => nvim
.ui_try_resize(width.max(10) as i64, height.max(3) as i64)
.await
.expect("Resize failed"),
UiCommand::Keyboard(input_command) => {
trace!("Keyboard Input Sent: {}", input_command);
nvim.input(&input_command).await
.expect("Input failed");
},
UiCommand::MouseButton { action, position: (grid_x, grid_y) } =>
nvim.input_mouse("left", &action, "", 0, grid_y as i64, grid_x as i64).await
.expect("Mouse Input Failed"),
UiCommand::Scroll { direction, position: (grid_x, grid_y) } =>
nvim.input_mouse("wheel", &direction, "", 0, grid_y as i64, grid_x as i64).await
.expect("Mouse Scroll Failed"),
UiCommand::Drag(grid_x, grid_y) =>
nvim.input_mouse("left", "drag", "", 0, grid_y as i64, grid_x as i64).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")
},
nvim.input(&input_command).await.expect("Input failed");
}
UiCommand::MouseButton {
action,
position: (grid_x, grid_y),
} => nvim
.input_mouse("left", &action, "", 0, grid_y as i64, grid_x as i64)
.await
.expect("Mouse Input Failed"),
UiCommand::Scroll {
direction,
position: (grid_x, grid_y),
} => nvim
.input_mouse("wheel", &direction, "", 0, grid_y as i64, grid_x as i64)
.await
.expect("Mouse Scroll Failed"),
UiCommand::Drag(grid_x, grid_y) => nvim
.input_mouse("left", "drag", "", 0, grid_y as i64, grid_x as i64)
.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 {
match self {
UiCommand::Resize { .. } => true,
_ => false
_ => false,
}
}
}

@ -1,98 +1,112 @@
use std::collections::HashMap;
use std::sync::Arc;
use skulpin::skia_safe::Color4f;
use super::style::{Style, Colors};
#[derive(Debug, Clone, PartialEq)]
pub enum CursorShape {
Block,
Horizontal,
Vertical
}
impl CursorShape {
pub fn from_type_name(name: &str) -> Option<CursorShape> {
match name {
"block" => Some(CursorShape::Block),
"horizontal" => Some(CursorShape::Horizontal),
"vertical" => Some(CursorShape::Vertical),
_ => None
}
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct CursorMode {
pub shape: Option<CursorShape>,
pub style_id: Option<u64>,
pub cell_percentage: Option<f32>,
pub blinkwait: Option<u64>,
pub blinkon: Option<u64>,
pub blinkoff: Option<u64>,
}
#[derive(Clone, PartialEq)]
pub struct Cursor {
pub position: (u64, u64),
pub shape: CursorShape,
pub cell_percentage: Option<f32>,
pub blinkwait: Option<u64>,
pub blinkon: Option<u64>,
pub blinkoff: Option<u64>,
pub style: Option<Arc<Style>>,
pub enabled: bool,
pub mode_list: Vec<CursorMode>
}
impl Cursor {
pub fn new() -> Cursor {
Cursor {
position: (0, 0),
shape: CursorShape::Block,
style: None,
cell_percentage: None,
blinkwait: None,
blinkon: None,
blinkoff: None,
enabled: true,
mode_list: Vec::new()
}
}
pub fn foreground(&self, default_colors: &Colors) -> Color4f {
if let Some(style) = &self.style {
style.colors.foreground.clone().unwrap_or_else(||default_colors.background.clone().unwrap())
} else {
default_colors.background.clone().unwrap()
}
}
pub fn background(&self, default_colors: &Colors) -> Color4f {
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 change_mode(&mut self, mode_index: u64, styles: &HashMap<u64, Arc<Style>>) {
if let Some(CursorMode { shape, style_id, cell_percentage, blinkwait, blinkon, 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;
}
}
}
use std::collections::HashMap;
use std::sync::Arc;
use skulpin::skia_safe::Color4f;
use super::style::{Colors, Style};
#[derive(Debug, Clone, PartialEq)]
pub enum CursorShape {
Block,
Horizontal,
Vertical,
}
impl CursorShape {
pub fn from_type_name(name: &str) -> Option<CursorShape> {
match name {
"block" => Some(CursorShape::Block),
"horizontal" => Some(CursorShape::Horizontal),
"vertical" => Some(CursorShape::Vertical),
_ => None,
}
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct CursorMode {
pub shape: Option<CursorShape>,
pub style_id: Option<u64>,
pub cell_percentage: Option<f32>,
pub blinkwait: Option<u64>,
pub blinkon: Option<u64>,
pub blinkoff: Option<u64>,
}
#[derive(Clone, PartialEq)]
pub struct Cursor {
pub position: (u64, u64),
pub shape: CursorShape,
pub cell_percentage: Option<f32>,
pub blinkwait: Option<u64>,
pub blinkon: Option<u64>,
pub blinkoff: Option<u64>,
pub style: Option<Arc<Style>>,
pub enabled: bool,
pub mode_list: Vec<CursorMode>,
}
impl Cursor {
pub fn new() -> Cursor {
Cursor {
position: (0, 0),
shape: CursorShape::Block,
style: None,
cell_percentage: None,
blinkwait: None,
blinkon: None,
blinkoff: None,
enabled: true,
mode_list: Vec::new(),
}
}
pub fn foreground(&self, default_colors: &Colors) -> Color4f {
if let Some(style) = &self.style {
style
.colors
.foreground
.clone()
.unwrap_or_else(|| default_colors.background.clone().unwrap())
} else {
default_colors.background.clone().unwrap()
}
}
pub fn background(&self, default_colors: &Colors) -> Color4f {
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 change_mode(&mut self, mode_index: u64, styles: &HashMap<u64, Arc<Style>>) {
if let Some(CursorMode {
shape,
style_id,
cell_percentage,
blinkwait,
blinkon,
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 std::sync::Arc;
use super::style::Style;
@ -55,11 +55,12 @@ impl CharacterGrid {
}
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> {
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 {
@ -78,13 +79,13 @@ impl CharacterGrid {
pub fn set_dirty_all(&mut self, value: bool) {
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]> {
(0..self.height)
.map(move |row| {
&self.characters[(row * self.width) as usize..((row + 1) * self.width) as usize]
})
pub fn rows(&self) -> impl Iterator<Item = &[GridCell]> {
(0..self.height).map(move |row| {
&self.characters[(row * self.width) as usize..((row + 1) * self.width) as usize]
})
}
}

@ -1,21 +1,21 @@
mod cursor;
mod style;
mod grid;
mod style;
use std::collections::HashMap;
use std::sync::Arc;
use log::trace;
use parking_lot::Mutex;
use skulpin::skia_safe::colors;
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::redraw_scheduler::REDRAW_SCHEDULER;
use crate::INITIAL_DIMENSIONS;
pub use cursor::{Cursor, CursorMode, CursorShape};
pub use grid::CharacterGrid;
pub use style::{Colors, Style};
lazy_static! {
pub static ref EDITOR: Arc<Mutex<Editor>> = Arc::new(Mutex::new(Editor::new()));
@ -26,7 +26,7 @@ pub struct DrawCommand {
pub text: String,
pub cell_width: u64,
pub grid_position: (u64, u64),
pub style: Option<Arc<Style>>
pub style: Option<Arc<Style>>,
}
pub struct Editor {
@ -37,7 +37,7 @@ pub struct Editor {
pub cursor: Cursor,
pub default_style: Arc<Style>,
pub defined_styles: HashMap<u64, Arc<Style>>,
pub previous_style: Option<Arc<Style>>
pub previous_style: Option<Arc<Style>>,
}
impl Editor {
@ -48,41 +48,63 @@ impl Editor {
font_name: None,
font_size: None,
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(),
previous_style: None
previous_style: None,
};
editor.grid.clear();
editor
}
pub fn handle_redraw_event(&mut self, event: RedrawEvent) {
match event {
RedrawEvent::SetTitle { title } => self.title = title,
RedrawEvent::ModeInfoSet { cursor_modes } => self.cursor.mode_list = cursor_modes,
RedrawEvent::OptionSet { gui_option } => self.set_option(gui_option),
RedrawEvent::ModeChange { mode_index } => self.cursor.change_mode(mode_index, &self.defined_styles),
RedrawEvent::ModeChange { mode_index } => {
self.cursor.change_mode(mode_index, &self.defined_styles)
}
RedrawEvent::BusyStart => {
trace!("Cursor off");
self.cursor.enabled = false;
},
}
RedrawEvent::BusyStop => {
trace!("Cursor on");
self.cursor.enabled = true;
},
}
RedrawEvent::Flush => {
trace!("Image flushed");
REDRAW_SCHEDULER.queue_next_frame();
},
}
RedrawEvent::Resize { width, height, .. } => self.grid.resize(width, height),
RedrawEvent::DefaultColorsSet { colors } => self.default_style = Arc::new(Style::new(colors)),
RedrawEvent::HighlightAttributesDefine { id, style } => { self.defined_styles.insert(id, Arc::new(style)); },
RedrawEvent::GridLine { row, column_start, cells, .. } => self.draw_grid_line(row, column_start, cells),
RedrawEvent::DefaultColorsSet { colors } => {
self.default_style = Arc::new(Style::new(colors))
}
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::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 {
match command {
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 {
Some(command) => {
command.text.push_str(character);
command.cell_width += 1;
},
}
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() {
if let Some((character, style)) = cell {
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);
command = None;
} else {
@ -128,7 +167,13 @@ impl Editor {
add_command(&mut draw_commands, command);
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 {
if !command_matches(&command, &None) {
@ -141,19 +186,22 @@ impl Editor {
add_command(&mut draw_commands, command);
}
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 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;
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 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
}).collect::<Vec<DrawCommand>>();
false
})
.collect::<Vec<DrawCommand>>();
self.grid.set_dirty_all(false);
self.grid.should_clear = false;
@ -166,7 +214,7 @@ impl Editor {
let style = match cell.highlight_id {
Some(0) => None,
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;
@ -205,28 +253,29 @@ impl Editor {
}
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 {
Box::new((top as i64 + rows).. bot as i64)
let y_iter: Box<dyn Iterator<Item = i64>> = if rows > 0 {
Box::new((top as i64 + rows)..bot as i64)
} 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 {
let dest_y = y - rows;
if dest_y >= 0 && dest_y < self.grid.height as i64 {
let x_iter : Box<dyn Iterator<Item=i64>> = if cols > 0 {
Box::new((left as i64 + cols) .. right as i64)
let x_iter: Box<dyn Iterator<Item = i64>> = if cols > 0 {
Box::new((left as i64 + cols)..right as i64)
} 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 {
let dest_x = x - cols;
let cell_data = self.grid.get_cell(x as u64, y as u64).cloned();
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(cell_data) = cell_data {
if let Some(dest_cell) =
self.grid.get_cell_mut(dest_x as u64, dest_y as u64)
{
*dest_cell = cell_data;
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();
}
}
},
}
_ => {}
}
}
}

@ -1,49 +1,64 @@
use skulpin::skia_safe::Color4f;
#[derive(new, PartialEq, Debug, Clone)]
pub struct Colors {
pub foreground: Option<Color4f>,
pub background: Option<Color4f>,
pub special: Option<Color4f>
}
#[derive(new, Debug, Clone, PartialEq)]
pub struct Style {
pub colors: Colors,
#[new(default)]
pub reverse: bool,
#[new(default)]
pub italic: bool,
#[new(default)]
pub bold: bool,
#[new(default)]
pub strikethrough: bool,
#[new(default)]
pub underline: bool,
#[new(default)]
pub undercurl: bool,
#[new(default)]
pub blend: u8
}
impl Style {
pub fn foreground(&self, default_colors: &Colors) -> Color4f {
if self.reverse {
self.colors.background.clone().unwrap_or_else(||default_colors.background.clone().unwrap())
} else {
self.colors.foreground.clone().unwrap_or_else(||default_colors.foreground.clone().unwrap())
}
}
pub fn background(&self, default_colors: &Colors) -> Color4f {
if self.reverse {
self.colors.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())
}
}
use skulpin::skia_safe::Color4f;
#[derive(new, PartialEq, Debug, Clone)]
pub struct Colors {
pub foreground: Option<Color4f>,
pub background: Option<Color4f>,
pub special: Option<Color4f>,
}
#[derive(new, Debug, Clone, PartialEq)]
pub struct Style {
pub colors: Colors,
#[new(default)]
pub reverse: bool,
#[new(default)]
pub italic: bool,
#[new(default)]
pub bold: bool,
#[new(default)]
pub strikethrough: bool,
#[new(default)]
pub underline: bool,
#[new(default)]
pub undercurl: bool,
#[new(default)]
pub blend: u8,
}
impl Style {
pub fn foreground(&self, default_colors: &Colors) -> Color4f {
if self.reverse {
self.colors
.background
.clone()
.unwrap_or_else(|| default_colors.background.clone().unwrap())
} else {
self.colors
.foreground
.clone()
.unwrap_or_else(|| default_colors.foreground.clone().unwrap())
}
}
pub fn background(&self, default_colors: &Colors) -> Color4f {
if self.reverse {
self.colors
.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;
fn show_error(explanation: &str) -> ! {
error!("{}", explanation);
panic!(explanation.to_string());
}
pub trait ResultPanicExplanation<T, E: ToString> {
fn unwrap_or_explained_panic(self, explanation: &str) -> T;
}
impl<T, E: ToString> ResultPanicExplanation<T, E> for Result<T, E> {
fn unwrap_or_explained_panic(self, explanation: &str) -> T {
match self {
Err(error) => {
let explanation = format!("{}: {}", explanation, error.to_string());
show_error(&explanation);
},
Ok(content) => content
}
}
}
pub trait OptionPanicExplanation<T> {
fn unwrap_or_explained_panic(self, explanation: &str) -> T;
}
impl<T> OptionPanicExplanation<T> for Option<T> {
fn unwrap_or_explained_panic(self, explanation: &str) -> T {
match self {
None => {
show_error(explanation);
},
Some(content) => content
}
}
}
use log::error;
fn show_error(explanation: &str) -> ! {
error!("{}", explanation);
panic!(explanation.to_string());
}
pub trait ResultPanicExplanation<T, E: ToString> {
fn unwrap_or_explained_panic(self, explanation: &str) -> T;
}
impl<T, E: ToString> ResultPanicExplanation<T, E> for Result<T, E> {
fn unwrap_or_explained_panic(self, explanation: &str) -> T {
match self {
Err(error) => {
let explanation = format!("{}: {}", explanation, error.to_string());
show_error(&explanation);
}
Ok(content) => content,
}
}
}
pub trait OptionPanicExplanation<T> {
fn unwrap_or_explained_panic(self, explanation: &str) -> T;
}
impl<T> OptionPanicExplanation<T> for Option<T> {
fn unwrap_or_explained_panic(self, explanation: &str) -> T {
match self {
None => {
show_error(explanation);
}
Some(content) => content,
}
}
}

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

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

@ -1,13 +1,20 @@
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 skulpin::skia_safe::{TextBlob, Font as SkiaFont, Typeface, TextBlobBuilder, Data};
use font_kit::{source::SystemSource, metrics::Metrics, properties::{Properties, Weight, Style, Stretch}, family_name::FamilyName, font::Font };
use skribo::{LayoutSession, FontRef as SkriboFont, FontFamily, FontCollection, TextStyle};
use skribo::{FontCollection, FontFamily, FontRef as SkriboFont, LayoutSession, TextStyle};
use skulpin::skia_safe::{Data, Font as SkiaFont, TextBlob, TextBlobBuilder, Typeface};
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_BOLD_FONT: &str = "Fira Code Bold Nerd Font Complete.otf";
@ -45,11 +52,17 @@ const DEFAULT_FONT_SIZE: f32 = 14.0;
struct ShapeKey {
pub text: String,
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<()> {
source.select_family_by_name(name).ok()
pub fn add_font_to_collection_by_name(
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())
.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)))
}
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 mut collection = FontCollection::new();
if let Some(font_name) = font_name {
let weight = if bold {
Weight::BOLD
} else {
Weight::NORMAL
};
let weight = if bold { Weight::BOLD } else { Weight::NORMAL };
let style = if italic {
Style::Italic
} else {
Style::Normal
};
let style = if italic { Style::Italic } else { Style::Normal };
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) {
custom.load().ok()
.map(|matching_font| collection.add_family(FontFamily::new_from_font(matching_font)))
if let Ok(custom) =
source.select_best_match(&[FamilyName::Title(font_name.to_string())], &properties)
{
custom
.load()
.ok()
.map(|matching_font| {
collection.add_family(FontFamily::new_from_font(matching_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() {
#[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");
} else {
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)
.unwrap_or_else(|| warn!("Could not load system symbol font"));
#[cfg(feature = "embed-fonts")]
{
let wide_style = if bold {
WIDE_BOLD_FONT
} else {
WIDE_FONT
};
let wide_style = if bold { WIDE_BOLD_FONT } else { WIDE_FONT };
add_asset_font_to_collection(wide_style, &mut collection)
.unwrap_or_else(|| warn!("Could not load embedded wide font"));
@ -163,7 +177,7 @@ pub struct CachingShaper {
pub base_size: f32,
font_set: FontSet,
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> {
@ -196,11 +210,20 @@ impl CachingShaper {
}
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> {
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));
@ -215,7 +238,8 @@ impl CachingShaper {
let mut blob_builder = TextBlobBuilder::new();
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() {
glyphs[i] = glyph.glyph_id as u16;
@ -251,18 +275,26 @@ impl CachingShaper {
pub fn font_base_dimensions(&mut self) -> (f32, f32) {
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 session = LayoutSession::create(STANDARD_CHARACTER_STRING, &style, &self.font_set.normal);
let style = TextStyle {
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 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();
for advance in glyph_advances.iter() {
amounts.entry(advance.to_string())
amounts
.entry(advance.to_string())
.and_modify(|e| *e += 1)
.or_insert(1);
}
@ -286,7 +318,12 @@ mod tests {
#[test]
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]

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

@ -1,322 +1,362 @@
mod animation_utils;
mod cursor_vfx;
mod blink;
use skulpin::skia_safe::{Canvas, Paint, Path, Point};
use crate::settings::*;
use crate::renderer::CachingShaper;
use crate::editor::{EDITOR, Colors, Cursor, CursorShape};
use crate::redraw_scheduler::REDRAW_SCHEDULER;
use animation_utils::*;
use blink::*;
const COMMAND_LINE_DELAY_FRAMES: u64 = 5;
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)];
// ----------------------------------------------------------------------------
#[derive(Clone)]
pub struct CursorSettings {
animation_length: f32,
trail_size: f32,
vfx_mode: cursor_vfx::VfxMode,
vfx_opacity: f32,
vfx_particle_lifetime: f32,
vfx_particle_density: f32,
vfx_particle_speed: f32,
vfx_particle_phase: f32,
vfx_particle_curl: f32,
}
pub fn initialize_settings() {
SETTINGS.set(&CursorSettings {
animation_length: 0.13,
trail_size: 0.7,
vfx_mode: cursor_vfx::VfxMode::Disabled,
vfx_opacity: 200.0,
vfx_particle_lifetime: 1.2,
vfx_particle_density: 7.0,
vfx_particle_speed: 10.0,
vfx_particle_phase: 1.5,
vfx_particle_curl: 1.0,
});
register_nvim_setting!("cursor_animation_length", CursorSettings::animation_length);
register_nvim_setting!("cursor_trail_size", CursorSettings::trail_size);
register_nvim_setting!("cursor_vfx_mode", CursorSettings::vfx_mode);
register_nvim_setting!("cursor_vfx_opacity", CursorSettings::vfx_opacity);
register_nvim_setting!("cursor_vfx_particle_lifetime", CursorSettings::vfx_particle_lifetime);
register_nvim_setting!("cursor_vfx_particle_density", CursorSettings::vfx_particle_density);
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!("cursor_vfx_particle_curl", CursorSettings::vfx_particle_curl);
}
// ----------------------------------------------------------------------------
#[derive(Debug, Clone)]
pub struct Corner {
start_position: Point,
current_position: Point,
relative_position: Point,
previous_destination: Point,
t: f32,
}
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),
previous_destination: Point::new(-1000.0, -1000.0),
t: 0.0,
}
}
pub fn update(&mut self, settings: &CursorSettings, font_dimensions: Point, destination: Point, dt: f32) -> bool {
// Update destination if needed
let mut immediate_movement = false;
if destination != self.previous_destination {
let travel_distance = destination - self.previous_destination;
let chars_travel_x = travel_distance.x / font_dimensions.x;
if travel_distance.y == 0.0 && (chars_travel_x - 1.0).abs() < 0.1 {
// We're moving one character to the right. Make movement immediate to avoid lag
// while typing
immediate_movement = true;
}
self.t = 0.0;
self.start_position = self.current_position;
self.previous_destination = destination;
}
// Check first if animation's over
if self.t > 1.0 {
return false;
}
// Calculate window-space destination for corner
let relative_scaled_position: Point = (
self.relative_position.x * font_dimensions.x,
self.relative_position.y * font_dimensions.y,
).into();
let corner_destination = destination + relative_scaled_position;
if immediate_movement {
self.t = 1.0;
self.current_position = corner_destination;
return true;
}
// 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
// back
let travel_direction = {
let mut d = destination - self.current_position;
d.normalize();
d
};
let corner_direction = {
let mut d = self.relative_position;
d.normalize();
d
};
let direction_alignment = travel_direction.dot(corner_direction);
if self.t == 1.0 {
// We are at destination, move t out of 0-1 range to stop the animation
self.t = 2.0;
} else {
let corner_dt = dt * lerp(1.0, 1.0 - settings.trail_size, -direction_alignment);
self.t = (self.t + corner_dt / settings.animation_length).min(1.0)
}
self.current_position =
ease_point(ease_out_expo, self.start_position, corner_destination, self.t);
true
}
}
pub struct CursorRenderer {
pub corners: Vec<Corner>,
pub previous_position: (u64, u64),
pub command_line_delay: u64,
blink_status: BlinkStatus,
previous_cursor_shape: Option<CursorShape>,
cursor_vfx: Option<Box<dyn cursor_vfx::CursorVfx>>,
previous_vfx_mode: cursor_vfx::VfxMode,
}
impl CursorRenderer {
pub fn new() -> CursorRenderer {
let mut renderer = CursorRenderer {
corners: vec![Corner::new(); 4],
previous_position: (0, 0),
command_line_delay: 0,
blink_status: BlinkStatus::new(),
previous_cursor_shape: None,
//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,
};
renderer.set_cursor_shape(&CursorShape::Block, DEFAULT_CELL_PERCENTAGE);
renderer
}
fn set_cursor_shape(&mut self, cursor_shape: &CursorShape, cell_percentage: f32) {
self.corners = self.corners
.clone()
.into_iter().enumerate()
.map(|(i, corner)| {
let (x, y) = STANDARD_CORNERS[i];
Corner {
relative_position: match cursor_shape {
CursorShape::Block => (x, y).into(),
// Transform the x position so that the right side is translated over to
// the BAR_WIDTH position
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
// instead of the top.
CursorShape::Horizontal => (x, -((-y + 0.5) * cell_percentage - 0.5)).into()
},
t: 0.0,
start_position: corner.current_position,
.. corner
}
})
.collect::<Vec<Corner>>();
}
pub fn draw(&mut self,
cursor: Cursor, default_colors: &Colors,
font_width: f32, font_height: f32,
shaper: &mut CachingShaper, canvas: &mut Canvas,
dt: f32) {
let render = self.blink_status.update_status(&cursor);
let settings = SETTINGS.get::<CursorSettings>();
if settings.vfx_mode != self.previous_vfx_mode {
self.cursor_vfx = cursor_vfx::new_cursor_vfx(&settings.vfx_mode);
self.previous_vfx_mode = settings.vfx_mode.clone();
}
let mut paint = Paint::new(skulpin::skia_safe::colors::WHITE, None);
paint.set_anti_alias(true);
self.previous_position = {
let editor = EDITOR.lock();
let (_, grid_y) = cursor.position;
let (_, previous_y) = self.previous_position;
if grid_y == editor.grid.height - 1 && previous_y != grid_y {
self.command_line_delay += 1;
if self.command_line_delay < COMMAND_LINE_DELAY_FRAMES {
self.previous_position
} else {
self.command_line_delay = 0;
cursor.position
}
} else {
self.command_line_delay = 0;
cursor.position
}
};
let (grid_x, grid_y) = self.previous_position;
let (character, font_dimensions): (String, Point) = {
let editor = EDITOR.lock();
let character = match editor.grid.get_cell(grid_x, grid_y) {
Some(Some((character, _))) => character.clone(),
_ => ' '.to_string(),
};
let is_double = match editor.grid.get_cell(grid_x + 1, grid_y) {
Some(Some((character, _))) => character.is_empty(),
_ => false,
};
let font_width = match (is_double, &cursor.shape) {
(true, CursorShape::Block) => font_width * 2.0,
_ => font_width
};
(character, (font_width, font_height).into())
};
let destination: Point = (grid_x as f32 * font_width, grid_y as f32 * font_height).into();
let center_destination = destination + font_dimensions * 0.5;
let new_cursor = Some(cursor.shape.clone());
if self.previous_cursor_shape != new_cursor {
self.previous_cursor_shape = new_cursor;
self.set_cursor_shape(&cursor.shape, cursor.cell_percentage.unwrap_or(DEFAULT_CELL_PERCENTAGE));
if let Some(vfx) = self.cursor_vfx.as_mut() {
vfx.restart(center_destination);
}
}
let mut animating = false;
if !center_destination.is_zero() {
for corner in self.corners.iter_mut() {
let corner_animating = corner.update(&settings, font_dimensions, center_destination, dt);
animating |= corner_animating;
}
let vfx_animating = if let Some(vfx) = self.cursor_vfx.as_mut() {
vfx.update(&settings, center_destination, (font_width, font_height), dt)
}else{
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));
}
}
}
}
mod animation_utils;
mod blink;
mod cursor_vfx;
use skulpin::skia_safe::{Canvas, Paint, Path, Point};
use crate::editor::{Colors, Cursor, CursorShape, EDITOR};
use crate::redraw_scheduler::REDRAW_SCHEDULER;
use crate::renderer::CachingShaper;
use crate::settings::*;
use animation_utils::*;
use blink::*;
const COMMAND_LINE_DELAY_FRAMES: u64 = 5;
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)];
// ----------------------------------------------------------------------------
#[derive(Clone)]
pub struct CursorSettings {
animation_length: f32,
trail_size: f32,
vfx_mode: cursor_vfx::VfxMode,
vfx_opacity: f32,
vfx_particle_lifetime: f32,
vfx_particle_density: f32,
vfx_particle_speed: f32,
vfx_particle_phase: f32,
vfx_particle_curl: f32,
}
pub fn initialize_settings() {
SETTINGS.set(&CursorSettings {
animation_length: 0.13,
trail_size: 0.7,
vfx_mode: cursor_vfx::VfxMode::Disabled,
vfx_opacity: 200.0,
vfx_particle_lifetime: 1.2,
vfx_particle_density: 7.0,
vfx_particle_speed: 10.0,
vfx_particle_phase: 1.5,
vfx_particle_curl: 1.0,
});
register_nvim_setting!("cursor_animation_length", CursorSettings::animation_length);
register_nvim_setting!("cursor_trail_size", CursorSettings::trail_size);
register_nvim_setting!("cursor_vfx_mode", CursorSettings::vfx_mode);
register_nvim_setting!("cursor_vfx_opacity", CursorSettings::vfx_opacity);
register_nvim_setting!(
"cursor_vfx_particle_lifetime",
CursorSettings::vfx_particle_lifetime
);
register_nvim_setting!(
"cursor_vfx_particle_density",
CursorSettings::vfx_particle_density
);
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!(
"cursor_vfx_particle_curl",
CursorSettings::vfx_particle_curl
);
}
// ----------------------------------------------------------------------------
#[derive(Debug, Clone)]
pub struct Corner {
start_position: Point,
current_position: Point,
relative_position: Point,
previous_destination: Point,
t: f32,
}
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),
previous_destination: Point::new(-1000.0, -1000.0),
t: 0.0,
}
}
pub fn update(
&mut self,
settings: &CursorSettings,
font_dimensions: Point,
destination: Point,
dt: f32,
) -> bool {
// Update destination if needed
let mut immediate_movement = false;
if destination != self.previous_destination {
let travel_distance = destination - self.previous_destination;
let chars_travel_x = travel_distance.x / font_dimensions.x;
if travel_distance.y == 0.0 && (chars_travel_x - 1.0).abs() < 0.1 {
// We're moving one character to the right. Make movement immediate to avoid lag
// while typing
immediate_movement = true;
}
self.t = 0.0;
self.start_position = self.current_position;
self.previous_destination = destination;
}
// Check first if animation's over
if self.t > 1.0 {
return false;
}
// Calculate window-space destination for corner
let relative_scaled_position: Point = (
self.relative_position.x * font_dimensions.x,
self.relative_position.y * font_dimensions.y,
)
.into();
let corner_destination = destination + relative_scaled_position;
if immediate_movement {
self.t = 1.0;
self.current_position = corner_destination;
return true;
}
// 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
// back
let travel_direction = {
let mut d = destination - self.current_position;
d.normalize();
d
};
let corner_direction = {
let mut d = self.relative_position;
d.normalize();
d
};
let direction_alignment = travel_direction.dot(corner_direction);
if self.t == 1.0 {
// We are at destination, move t out of 0-1 range to stop the animation
self.t = 2.0;
} else {
let corner_dt = dt * lerp(1.0, 1.0 - settings.trail_size, -direction_alignment);
self.t = (self.t + corner_dt / settings.animation_length).min(1.0)
}
self.current_position = ease_point(
ease_out_expo,
self.start_position,
corner_destination,
self.t,
);
true
}
}
pub struct CursorRenderer {
pub corners: Vec<Corner>,
pub previous_position: (u64, u64),
pub command_line_delay: u64,
blink_status: BlinkStatus,
previous_cursor_shape: Option<CursorShape>,
cursor_vfx: Option<Box<dyn cursor_vfx::CursorVfx>>,
previous_vfx_mode: cursor_vfx::VfxMode,
}
impl CursorRenderer {
pub fn new() -> CursorRenderer {
let mut renderer = CursorRenderer {
corners: vec![Corner::new(); 4],
previous_position: (0, 0),
command_line_delay: 0,
blink_status: BlinkStatus::new(),
previous_cursor_shape: None,
//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,
};
renderer.set_cursor_shape(&CursorShape::Block, DEFAULT_CELL_PERCENTAGE);
renderer
}
fn set_cursor_shape(&mut self, cursor_shape: &CursorShape, cell_percentage: f32) {
self.corners = self
.corners
.clone()
.into_iter()
.enumerate()
.map(|(i, corner)| {
let (x, y) = STANDARD_CORNERS[i];
Corner {
relative_position: match cursor_shape {
CursorShape::Block => (x, y).into(),
// Transform the x position so that the right side is translated over to
// the BAR_WIDTH position
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
// instead of the top.
CursorShape::Horizontal => {
(x, -((-y + 0.5) * cell_percentage - 0.5)).into()
}
},
t: 0.0,
start_position: corner.current_position,
..corner
}
})
.collect::<Vec<Corner>>();
}
pub fn draw(
&mut self,
cursor: Cursor,
default_colors: &Colors,
font_width: f32,
font_height: f32,
shaper: &mut CachingShaper,
canvas: &mut Canvas,
dt: f32,
) {
let render = self.blink_status.update_status(&cursor);
let settings = SETTINGS.get::<CursorSettings>();
if settings.vfx_mode != self.previous_vfx_mode {
self.cursor_vfx = cursor_vfx::new_cursor_vfx(&settings.vfx_mode);
self.previous_vfx_mode = settings.vfx_mode.clone();
}
let mut paint = Paint::new(skulpin::skia_safe::colors::WHITE, None);
paint.set_anti_alias(true);
self.previous_position = {
let editor = EDITOR.lock();
let (_, grid_y) = cursor.position;
let (_, previous_y) = self.previous_position;
if grid_y == editor.grid.height - 1 && previous_y != grid_y {
self.command_line_delay += 1;
if self.command_line_delay < COMMAND_LINE_DELAY_FRAMES {
self.previous_position
} else {
self.command_line_delay = 0;
cursor.position
}
} else {
self.command_line_delay = 0;
cursor.position
}
};
let (grid_x, grid_y) = self.previous_position;
let (character, font_dimensions): (String, Point) = {
let editor = EDITOR.lock();
let character = match editor.grid.get_cell(grid_x, grid_y) {
Some(Some((character, _))) => character.clone(),
_ => ' '.to_string(),
};
let is_double = match editor.grid.get_cell(grid_x + 1, grid_y) {
Some(Some((character, _))) => character.is_empty(),
_ => false,
};
let font_width = match (is_double, &cursor.shape) {
(true, CursorShape::Block) => font_width * 2.0,
_ => font_width,
};
(character, (font_width, font_height).into())
};
let destination: Point = (grid_x as f32 * font_width, grid_y as f32 * font_height).into();
let center_destination = destination + font_dimensions * 0.5;
let new_cursor = Some(cursor.shape.clone());
if self.previous_cursor_shape != new_cursor {
self.previous_cursor_shape = new_cursor;
self.set_cursor_shape(
&cursor.shape,
cursor.cell_percentage.unwrap_or(DEFAULT_CELL_PERCENTAGE),
);
if let Some(vfx) = self.cursor_vfx.as_mut() {
vfx.restart(center_destination);
}
}
let mut animating = false;
if !center_destination.is_zero() {
for corner in self.corners.iter_mut() {
let corner_animating =
corner.update(&settings, font_dimensions, center_destination, dt);
animating |= corner_animating;
}
let vfx_animating = if let Some(vfx) = self.cursor_vfx.as_mut() {
vfx.update(&settings, center_destination, (font_width, font_height), dt)
} else {
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 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 skulpin::skia_safe::gpu::SurfaceOrigin;
use skulpin::skia_safe::{colors, dash_path_effect, Budgeted, Canvas, Paint, Rect, Surface};
use skulpin::CoordinateSystemHelper;
mod caching_shaper;
pub mod cursor_renderer;
pub use caching_shaper::CachingShaper;
use crate::editor::{Style, EDITOR};
use cursor_renderer::CursorRenderer;
use crate::editor::{EDITOR, Style};
pub struct Renderer {
surface: Option<Surface>,
@ -29,13 +28,20 @@ impl Renderer {
let surface = None;
let mut paint = Paint::new(colors::WHITE, None);
paint.set_anti_alias(false);
let mut shaper = CachingShaper::new();
let (font_width, font_height) = shaper.font_base_dimensions();
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>) {
@ -54,15 +60,31 @@ impl Renderer {
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 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);
}
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 x = grid_x as f32 * self.font_width;
let y = grid_y as f32 * self.font_height;
@ -79,53 +101,71 @@ impl Renderer {
if style.underline || style.undercurl {
let line_position = self.shaper.underline_position();
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);
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 {
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();
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);
}
}
if style.strikethrough {
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.restore();
}
pub fn draw(&mut self, gpu_canvas: &mut Canvas,
pub fn draw(
&mut self,
gpu_canvas: &mut Canvas,
coordinate_system_helper: &CoordinateSystemHelper,
dt: f32) -> bool {
dt: f32,
) -> bool {
trace!("Rendering");
let ((draw_commands, should_clear), default_style, cursor, font_name, font_size) = {
let mut editor = EDITOR.lock();
(
editor.build_draw_commands(),
editor.default_style.clone(),
editor.build_draw_commands(),
editor.default_style.clone(),
editor.cursor.clone(),
editor.font_name.clone(),
editor.font_size
editor.font_size,
)
};
let font_changed =
font_name != self.shaper.font_name ||
font_size.map(|new_size| (new_size - self.shaper.base_size).abs() > std::f32::EPSILON).unwrap_or(false);
let font_changed = font_name != self.shaper.font_name
|| font_size
.map(|new_size| (new_size - self.shaper.base_size).abs() > std::f32::EPSILON)
.unwrap_or(false);
if font_changed {
self.set_font(font_name.as_deref(), font_size);
}
@ -139,7 +179,16 @@ impl Renderer {
let budgeted = Budgeted::YES;
let image_info = gpu_canvas.image_info();
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();
canvas.clear(default_style.colors.background.clone().unwrap().to_color());
surface
@ -149,23 +198,46 @@ impl Renderer {
coordinate_system_helper.use_logical_coordinates(&mut canvas);
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() {
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 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);
self.surface = Some(surface);
self.cursor_renderer.draw(
cursor, &default_style.colors,
self.font_width, self.font_height,
&mut self.shaper, gpu_canvas, dt);
cursor,
&default_style.colors,
self.font_width,
self.font_height,
&mut self.shaper,
gpu_canvas,
dt,
);
font_changed
}

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

@ -1,20 +1,20 @@
use std::time::{Duration, Instant};
use std::thread::sleep;
use std::time::{Duration, Instant};
use log::{info, trace, debug, error};
use skulpin::{LogicalSize, PhysicalSize};
use log::{debug, error, info, trace};
use skulpin::sdl2;
use skulpin::sdl2::Sdl;
use skulpin::sdl2::video::{Window, FullscreenType};
use skulpin::sdl2::event::{Event, WindowEvent};
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, BRIDGE, UiCommand};
use crate::renderer::Renderer;
use crate::redraw_scheduler::REDRAW_SCHEDULER;
use crate::bridge::{produce_neovim_keybinding_string, UiCommand, BRIDGE};
use crate::editor::EDITOR;
use crate::redraw_scheduler::REDRAW_SCHEDULER;
use crate::renderer::Renderer;
use crate::settings::*;
use crate::INITIAL_DIMENSIONS;
#[derive(RustEmbed)]
@ -23,8 +23,8 @@ struct Asset;
#[cfg(target_os = "windows")]
fn windows_fix_dpi() {
use winapi::um::winuser::SetProcessDpiAwarenessContext;
use winapi::shared::windef::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
use winapi::um::winuser::SetProcessDpiAwarenessContext;
unsafe {
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_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
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_dpis: (f32, f32),
transparency: f32,
fullscreen: bool
fullscreen: bool,
}
impl WindowWrapper {
pub fn new() -> WindowWrapper {
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();
let (width, height) = INITIAL_DIMENSIONS;
let renderer = Renderer::new();
let logical_size = LogicalSize {
width: (width as f32 * renderer.font_width) as u32,
height: (height as f32 * renderer.font_height + 1.0) as u32
width: (width as f32 * renderer.font_width) as u32,
height: (height as f32 * renderer.font_height + 1.0) as u32,
};
#[cfg(target_os = "windows")]
windows_fix_dpi();
sdl2::hint::set("SDL_MOUSE_FOCUS_CLICKTHROUGH", "1");
@ -83,13 +88,14 @@ impl WindowWrapper {
// };
// info!("icon created");
let window = video_subsystem.window("Neovide", logical_size.width, logical_size.height)
.position_centered()
.allow_highdpi()
.resizable()
.vulkan()
.build()
.expect("Failed to create window");
let window = video_subsystem
.window("Neovide", logical_size.width, logical_size.height)
.position_centered()
.allow_highdpi()
.resizable()
.vulkan()
.build()
.expect("Failed to create window");
info!("window created");
let skulpin_renderer = RendererBuilder::new()
@ -104,18 +110,21 @@ impl WindowWrapper {
let previous_size = LogicalSize::new(&window).unwrap();
let previous_dpis = dpis(&window).unwrap();
WindowWrapper {
context, window, skulpin_renderer, renderer,
WindowWrapper {
context,
window,
skulpin_renderer,
renderer,
mouse_down: false,
mouse_position: LogicalSize {
width: 0,
height: 0
height: 0,
},
title: String::from("Neovide"),
previous_size,
previous_dpis,
transparency: 1.0,
fullscreen: false
fullscreen: false,
}
}
@ -123,7 +132,9 @@ impl WindowWrapper {
let editor_title = { EDITOR.lock().title.clone() };
if 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 };
@ -138,7 +149,7 @@ impl WindowWrapper {
if self.fullscreen != fullscreen {
let state = match fullscreen {
true => FullscreenType::Desktop,
false => FullscreenType::Off
false => FullscreenType::Off,
};
self.window.set_fullscreen(state).ok();
self.fullscreen = fullscreen;
@ -149,41 +160,51 @@ impl WindowWrapper {
let modifiers = self.context.keyboard().mod_state();
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));
}
}
pub fn handle_pointer_motion(&mut self, x: i32, y: i32) {
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,
(y as f32 / self.renderer.font_height) as u32
),
&self.window
(y as f32 / self.renderer.font_height) as u32,
),
&self.window,
) {
self.mouse_position = new_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) {
BRIDGE.queue_command(UiCommand::MouseButton {
action: String::from("press"),
position: (self.mouse_position.width, self.mouse_position.height)
BRIDGE.queue_command(UiCommand::MouseButton {
action: String::from("press"),
position: (self.mouse_position.width, self.mouse_position.height),
});
self.mouse_down = true;
}
pub fn handle_pointer_up(&mut self) {
BRIDGE.queue_command(UiCommand::MouseButton {
action: String::from("release"),
position: (self.mouse_position.width, self.mouse_position.height)
pub fn handle_pointer_up(&mut self) {
BRIDGE.queue_command(UiCommand::MouseButton {
action: String::from("release"),
position: (self.mouse_position.width, self.mouse_position.height),
});
self.mouse_down = false;
}
@ -198,9 +219,9 @@ impl WindowWrapper {
};
if let Some(input_type) = vertical_input_type {
BRIDGE.queue_command(UiCommand::Scroll {
direction: input_type.to_string(),
position: (self.mouse_position.width, self.mouse_position.height)
BRIDGE.queue_command(UiCommand::Scroll {
direction: input_type.to_string(),
position: (self.mouse_position.width, self.mouse_position.height),
});
}
@ -213,9 +234,9 @@ impl WindowWrapper {
};
if let Some(input_type) = horizontal_input_type {
BRIDGE.queue_command(UiCommand::Scroll {
direction: input_type.to_string(),
position: (self.mouse_position.width, self.mouse_position.height)
BRIDGE.queue_command(UiCommand::Scroll {
direction: input_type.to_string(),
position: (self.mouse_position.width, self.mouse_position.height),
});
}
}
@ -240,9 +261,12 @@ impl WindowWrapper {
if let Ok(new_dpis) = dpis(&self.window) {
if self.previous_dpis != new_dpis {
let physical_size = PhysicalSize::new(&self.window);
self.window.set_size(
(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.window
.set_size(
(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;
}
}
@ -251,13 +275,17 @@ impl WindowWrapper {
let current_size = self.previous_size;
if REDRAW_SCHEDULER.should_draw() || SETTINGS.get::<WindowSettings>().no_idle {
let renderer = &mut self.renderer;
if self.skulpin_renderer.draw(&self.window, |canvas, coordinate_system_helper| {
let dt = 1.0 / (SETTINGS.get::<WindowSettings>().refresh_rate as f32);
if renderer.draw(canvas, coordinate_system_helper, dt) {
handle_new_grid_size(current_size, &renderer)
}
}).is_err() {
if self
.skulpin_renderer
.draw(&self.window, |canvas, coordinate_system_helper| {
let dt = 1.0 / (SETTINGS.get::<WindowSettings>().refresh_rate as f32);
if renderer.draw(canvas, coordinate_system_helper, dt) {
handle_new_grid_size(current_size, &renderer)
}
})
.is_err()
{
error!("Render failed. Closing");
return false;
}
@ -271,19 +299,21 @@ struct WindowSettings {
refresh_rate: u64,
transparency: f32,
no_idle: bool,
fullscreen: bool
fullscreen: bool,
}
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 {
refresh_rate: 60,
transparency: 1.0,
no_idle,
fullscreen: false
fullscreen: false,
});
register_nvim_setting!("refresh_rate", WindowSettings::refresh_rate);
register_nvim_setting!("transparency", WindowSettings::transparency);
register_nvim_setting!("no_idle", WindowSettings::no_idle);
@ -294,7 +324,10 @@ pub fn ui_loop() {
let mut window = WindowWrapper::new();
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 {
let frame_start = Instant::now();
@ -306,20 +339,29 @@ pub fn ui_loop() {
for event in event_pump.poll_iter() {
match event {
Event::Quit {..} => break 'running,
Event::KeyDown { keycode: Some(received_keycode), .. } => {
Event::Quit { .. } => break 'running,
Event::KeyDown {
keycode: Some(received_keycode),
..
} => {
keycode = Some(received_keycode);
},
}
Event::TextInput { text, .. } => keytext = Some(text),
Event::MouseMotion { x, y, .. } => window.handle_pointer_motion(x, y),
Event::MouseButtonDown { .. } => window.handle_pointer_down(),
Event::MouseButtonUp { .. } => window.handle_pointer_up(),
Event::MouseWheel { x, y, .. } => window.handle_mouse_wheel(x, y),
Event::Window { win_event: WindowEvent::FocusLost, .. } => window.handle_focus_lost(),
Event::Window { win_event: WindowEvent::FocusGained, .. } => {
Event::Window {
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
window.handle_focus_gained();
},
}
Event::Window { .. } => REDRAW_SCHEDULER.queue_next_frame(),
_ => {}
}

Loading…
Cancel
Save