Skip to content

Commit

Permalink
Console part one: stdin
Browse files Browse the repository at this point in the history
Cf. issue #4
  • Loading branch information
ctrlcctrlv committed Sep 14, 2020
1 parent 32defa4 commit 76db331
Show file tree
Hide file tree
Showing 11 changed files with 280 additions and 23 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/target
Cargo.lock
*.swp
16 changes: 15 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ authors = ["Fredrick Brennan <[email protected]>"]
# For display
# We might need to vendorize these some day. See aclysma/skulpin#66:
# https://github.com/aclysma/skulpin/issues/66#issuecomment-689244118
# Commented versions I occasionally use in development to stick extra debug!'s in Skulpin.
#skulpin = { version = "0.10.0", default-features = false, features = ["skia_complete", "skulpin_winit"] }
#skulpin = { path = "../skulpin", default-features = false, features = ["skia_complete", "skulpin_winit"] }
#skulpin-plugin-imgui = { path = "../skulpin/skulpin-plugin-imgui" }
skulpin = { version = "0.10.0", default-features = false, features = ["skia_complete", "skulpin_winit"] }
skulpin-plugin-imgui = "0.5.0"
skulpin-plugin-imgui = { version = "0.5.0" }
imgui-winit-support = "0.4.0"

# For choosing font for toolbox
Expand All @@ -30,12 +34,22 @@ enum-iterator = "=0.6.0"
backtrace = "0.3.0"
colored = "2.0.0"

# Right now only for pasting into console
clipboard = "0.5.0"

# Logging
log = "0.4.11"
env_logger = "0.7.1"

# Gives us derive(Display)
derive_more = "0.99.10"

## Our crates
# parses .glif files and gives us a place to put arbitrary data
glifparser = { git = "https://github.com/mfeq/glifparser" }
#glifparser = { path = "../glifparser" } # for development
mfeq-ipc = { git = "https://github.com/mfeq/ipc" }

# See src/util/mod.rs::set_codepage_utf8
[target.'cfg(windows)'.dependencies]
winapi = "0.3"
57 changes: 57 additions & 0 deletions src/events/console.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Console
use crate::CONSOLE;

use clipboard::{ClipboardContext, ClipboardProvider};

use winit::event::{ModifiersState, VirtualKeyCode};
// Only called if ElementState::Pressed
pub fn set_state(vk: VirtualKeyCode, m: ModifiersState) {
CONSOLE.with(|c| match vk {
VirtualKeyCode::Semicolon => {
if !m.shift() {
return;
}
c.borrow_mut().active(true);
}
VirtualKeyCode::Escape => {
c.borrow_mut().active(false);
}
VirtualKeyCode::Return => {
c.borrow_mut().active(false);
run_command(&mut c.borrow_mut());
}
_ => {}
});
}

const CHAR_BACKSPACE: char = '\x08';

use state::RendererConsole;
impl RendererConsole {
///! Handle chars which will not trigger events (so, not :, Escape or Return)
pub fn handle_ch(&mut self, ch: char) {
debug_event!("Got ch: {:x}", ch as u8);
if ch != CHAR_BACKSPACE {
self.stdin.push(ch);
} else {
if self.stdin.len() > 1 {
// don't delete `:`
self.stdin.pop();
}
}
}

///! String from clipboard
pub fn handle_clipboard(&mut self) {
let mut ctx: ClipboardContext =
ClipboardProvider::new().expect("Failed to set up clipboard provider");
if let Ok(s) = ctx.get_contents() {
self.stdin.push_str(&s);
}
}
}

pub fn run_command(c: &mut RendererConsole) {
debug!("Command requested to be run: {}", &c.stdin);
c.stdin.clear()
}
4 changes: 3 additions & 1 deletion src/events.rs → src/events/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::renderer::constants::*;
use crate::renderer::points::calc::*;
use crate::state;
use crate::{PEN_DATA, STATE};
use crate::{CONSOLE, PEN_DATA, STATE};
use state::{Mode, PenData, PointData};

use skulpin::skia_safe::{Canvas, Matrix};
Expand All @@ -13,6 +13,8 @@ use skulpin::winit::window::Window;
use std::cell::RefCell;
use std::mem;

pub mod console;

// Generic events
pub fn center_cursor(winit_window: &Window) -> Result<(), winit::error::ExternalError> {
let mut center = winit_window.outer_size();
Expand Down
12 changes: 7 additions & 5 deletions src/imgui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use crate::events;
use crate::state::Mode;
use crate::STATE;

use renderer::constants::CONSOLE_FONTS;

pub mod icons;
pub mod support;

Expand Down Expand Up @@ -77,9 +79,9 @@ use font_kit::{
source::SystemSource,
};

struct Font {
data: Vec<u8>,
path: Option<PathBuf>,
pub struct Font {
pub data: Vec<u8>,
pub path: Option<PathBuf>,
}

fn load_font(family: &[FKFamilyName]) -> Font {
Expand All @@ -100,11 +102,11 @@ fn load_font(family: &[FKFamilyName]) -> Font {
}

lazy_static! {
static ref SYSTEMSANS: Font = load_font(&[
pub static ref SYSTEMSANS: Font = load_font(&[
FKFamilyName::Title("Segoe UI".to_string()),
FKFamilyName::SansSerif
]);
static ref SYSTEMMONO: Font = load_font(&[FKFamilyName::Monospace]);
pub static ref SYSTEMMONO: Font = load_font(CONSOLE_FONTS.as_slice());
}

use skulpin::skia_safe::Rect;
Expand Down
58 changes: 46 additions & 12 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ extern crate font_kit;
extern crate skulpin;
#[macro_use]
extern crate skulpin_plugin_imgui;
extern crate clipboard;
extern crate imgui_winit_support;
// Our crates
extern crate glifparser;
Expand All @@ -51,17 +52,20 @@ pub use skulpin_plugin_imgui::imgui as imgui_rs;
mod util;
mod io;
mod ipc;
// Provides a thread-local global `STATE` variable
// Provides thread-local global variables.
mod state;
use state::{Glyph, PointLabels};
pub use state::{PEN_DATA, STATE};
pub use state::{CONSOLE, PEN_DATA, STATE};
mod events;
mod imgui;
mod renderer;

use renderer::constants::*;

fn main() {
#[cfg(target_family = "windows")]
util::set_codepage_utf8();

env_logger::init();
util::set_panic_hook();

Expand Down Expand Up @@ -128,7 +132,7 @@ fn main() {

event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
debug_events!("{:?}", event);
debug_event!("{:?}", event);

// Without this, the program will crash if it launches with the cursor over the window, as
// the mouse event occurs before the redraw, which means that it uses an uninitialized
Expand All @@ -153,12 +157,18 @@ fn main() {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(VirtualKeyCode::Escape | VirtualKeyCode::Q),
virtual_keycode: Some(VirtualKeyCode::Q),
..
},
..
} => {
if !CONSOLE.with(|c| c.borrow().active) {
*control_flow = ControlFlow::Exit;
}
}
WindowEvent::CloseRequested => {
*control_flow = ControlFlow::Exit;
}
| WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput {
input:
KeyboardInput {
Expand All @@ -172,6 +182,23 @@ fn main() {
if kstate != ElementState::Pressed {
return;
}

if let Some(vk) = virtual_keycode {
events::console::set_state(vk, modifiers);
}

// We write to the Console in ReceivedCharacter, not here.
CONSOLE.with(|c| {
if c.borrow().active {
if let Some(VirtualKeyCode::V) = virtual_keycode {
if modifiers.ctrl() {
c.borrow_mut().handle_clipboard();
}
}
return;
}
});

STATE.with(|v| {
let mode = v.borrow().mode;
let mut newmode = mode;
Expand Down Expand Up @@ -228,19 +255,26 @@ fn main() {
if mode != newmode {
v.borrow_mut().mode = newmode;
events::mode_switched(mode, newmode);
}

debug!(
"Scale factor now {}; offset {:?}; mode {:?}",
v.borrow().factor,
v.borrow().offset,
v.borrow().mode
);
debug!(
"Scale factor now {}; offset {:+}{:+}; mode {:?}",
v.borrow().factor,
v.borrow().offset.0,
v.borrow().offset.1,
v.borrow().mode
);
}

v.borrow_mut().offset = offset;
v.borrow_mut().factor = scale;
});
}
WindowEvent::ReceivedCharacter(ch) => {
if !CONSOLE.with(|c| c.borrow().active) {
return;
}
CONSOLE.with(|c| c.borrow_mut().handle_ch(ch));
}
WindowEvent::CursorMoved { position, .. } => {
STATE.with(|v| {
let mode = v.borrow().mode;
Expand Down
112 changes: 112 additions & 0 deletions src/renderer/console.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
//! Our Console is not a normal Unix console. stdin is always one line long, and stdout can be any
//! number of lines but disappears often and is not possible to be scrolled back. (It is always the
//! output of a single Command.) That's because this is a *Renderer Console*, not supposed to
//! represent the Console itself, but rather just what we show the user on the screen. We output to
//! the normal stdout as well, that's the persistent stdout.
pub struct Console {
pub stdin: String,
pub stdout: String,
// Pressing `:` activates the console, like in Vim.
pub active: bool,
}

impl Default for Console {
fn default() -> Self {
Console {
stdin: String::new(),
stdout: String::new(),
active: false,
}
}
}

impl Console {
pub fn active(&mut self, b: bool) {
self.active = b;
}
}

use imgui;
use skulpin::skia_safe::{Data, Font, FontStyle, Typeface};
use std::fs;
lazy_static! {
static ref MONO_FONT_BYTES: Option<Vec<u8>> = {
match imgui::SYSTEMMONO.path {
Some(_) => Some(imgui::SYSTEMMONO.data.clone()),
_ => None,
}
};
}

use std::cell::RefCell;
lazy_static! {
static ref CONSOLE_TYPEFACE: Typeface = {
match &*MONO_FONT_BYTES {
Some(ref bytes) => {
Typeface::from_data(unsafe { Data::new_bytes(bytes.as_slice()) }, None)
.expect("Failed to load mono font from memory")
}
None => Typeface::from_name("monospace", FontStyle::bold())
.expect("Failed to load mono font"),
}
};
}

use super::constants::*;
use crate::STATE;
use skulpin::skia_safe::{Canvas, Paint, PaintStyle, Path, Rect, TextBlob};
impl Console {
pub fn draw(&mut self, canvas: &mut Canvas) {
if !self.active {
return;
}
STATE.with(|v| {
let factor = v.borrow().factor;
let offset = v.borrow().offset;
let font =
Font::from_typeface_with_params(&*CONSOLE_TYPEFACE, 14. * (1. / factor), 1., 0.0);
let winsize = v.borrow().winsize;
let mut topleft = (offset.0, (winsize.height as f32) * (1. / factor));
let mut size = ((winsize.width as f32) * (1. / factor), offset.1);

let (_, trect) = font.measure_str("Q", None);
topleft.1 -= (CONSOLE_PADDING_Y_TOP + CONSOLE_PADDING_Y_BOTTOM) * (1. / factor);
topleft.1 -= trect.height(); // premultiplied by font
size.1 += (CONSOLE_PADDING_Y_TOP + CONSOLE_PADDING_Y_BOTTOM) * (1. / factor);
size.1 += trect.height(); // premultiplied by font

// Draw background
let console_rect = Rect::from_point_and_size(topleft, size);
let mut paint = Paint::default();
let mut path = Path::new();
paint.set_style(PaintStyle::Fill);
paint.set_color(CONSOLE_FILL);
path.add_rect(console_rect, None);
path.close();

canvas.draw_path(&path, &paint);

// Draw text
let blob = TextBlob::from_str(&self.stdin, &font)
.expect(&format!("Failed to shape {}", &self.stdin));

paint.set_color(CONSOLE_TEXT_FILL);
topleft.0 += CONSOLE_PADDING_X * (1. / factor);
topleft.1 += CONSOLE_PADDING_Y_BOTTOM * (1. / factor);
topleft.1 += trect.height(); // premultiplied by font

canvas.draw_text_blob(&blob, topleft, &paint);
});
}
}

enum Return {
OK,
Failure(String),
}

struct Command {
name: String,
args: Vec<String>,
run: Fn() -> Return,
}
Loading

0 comments on commit 76db331

Please sign in to comment.