aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock150
-rw-r--r--Cargo.toml1
-rw-r--r--src/main.rs21
-rw-r--r--src/renderer/mod.rs214
4 files changed, 339 insertions, 47 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 16457c20..565684bc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -8,6 +8,7 @@ dependencies = [
"gl_generator 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glutin 0.4.9 (git+https://github.com/jwilm/glutin?rev=c95e6973ace3cbf321123a64588b27f032675be9)",
"libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "notify 2.5.5 (git+https://github.com/jwilm/rsnotify?branch=add-ignore-op)",
"servo-fontconfig 0.2.0 (git+https://github.com/jwilm/rust-fontconfig)",
]
@@ -23,6 +24,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "bitflags"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -37,6 +43,16 @@ version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "bytes"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "cfg-if"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "cgl"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -143,6 +159,14 @@ dependencies = [
]
[[package]]
+name = "filetime"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "freetype-rs"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -173,6 +197,24 @@ dependencies = [
]
[[package]]
+name = "fsevent"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fsevent-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "fsevent-sys"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "gcc"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -243,6 +285,14 @@ dependencies = [
]
[[package]]
+name = "inotify"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -321,6 +371,73 @@ dependencies = [
]
[[package]]
+name = "mio"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "miow 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
+ "nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "miow"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "net2"
+version = "0.2.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "nix"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "notify"
+version = "2.5.5"
+source = "git+https://github.com/jwilm/rsnotify?branch=add-ignore-op#0ca41a4807c427e6cf47d7e75735df62d2e86708"
+dependencies = [
+ "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fsevent 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fsevent-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "inotify 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
+ "walkdir 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "num"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -478,6 +595,11 @@ dependencies = [
]
[[package]]
+name = "slab"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "tempfile"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -490,6 +612,16 @@ dependencies = [
]
[[package]]
+name = "time"
+version = "0.1.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "user32-sys"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -499,6 +631,15 @@ dependencies = [
]
[[package]]
+name = "walkdir"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "wayland-client"
version = "0.5.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -561,6 +702,15 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "ws2_32-sys"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "x11-dl"
version = "2.4.0"
source = "git+https://github.com/jwilm/x11-rs?rev=40f08df7b4408980b922b3c6e258c9c6765c2c24#40f08df7b4408980b922b3c6e258c9c6765c2c24"
diff --git a/Cargo.toml b/Cargo.toml
index ef2cdf32..de8c09cc 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,6 +12,7 @@ freetype-rs = "0.9.0"
libc = "*"
cgmath = "0.7"
euclid = "0.6"
+notify = { git = "https://github.com/jwilm/rsnotify", branch = "add-ignore-op" }
[build-dependencies]
gl_generator = "0.5"
diff --git a/src/main.rs b/src/main.rs
index b6a36330..e5f9e0b3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -11,6 +11,7 @@ extern crate libc;
extern crate glutin;
extern crate cgmath;
extern crate euclid;
+extern crate notify;
#[macro_use]
mod macros;
@@ -61,6 +62,7 @@ struct TermProps {
cell_height: f32,
sep_y: f32,
height: f32,
+ width: f32,
}
fn main() {
@@ -178,17 +180,18 @@ fn main() {
gl::Clear(gl::COLOR_BUFFER_BIT);
}
+ let props = TermProps {
+ cell_width: cell_width as f32,
+ sep_x: sep_x as f32,
+ cell_height: cell_height as f32,
+ sep_y: sep_y as f32,
+ height: height as f32,
+ width: width as f32,
+ };
+
{
let _sampler = meter.sampler();
- let props = TermProps {
- cell_width: cell_width as f32,
- sep_x: sep_x as f32,
- cell_height: cell_height as f32,
- sep_y: sep_y as f32,
- height: height as f32,
- };
-
// Draw the grid
renderer.render_grid(terminal.grid(), &glyph_cache, &props);
@@ -199,7 +202,7 @@ fn main() {
// Draw render timer
let timing = format!("{:.3} usec", meter.average());
let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 };
- renderer.render_string(&timing[..], &glyph_cache, cell_width, &color);
+ renderer.render_string(&timing[..], &glyph_cache, &props, &color);
window.swap_buffers().unwrap();
}
diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs
index 2f364482..6161191f 100644
--- a/src/renderer/mod.rs
+++ b/src/renderer/mod.rs
@@ -1,23 +1,32 @@
use std::ffi::CString;
+use std::fs::File;
+use std::io::{self, Read};
use std::mem::size_of;
+use std::path::{PathBuf, Path};
use std::ptr;
+use std::sync::Arc;
+use std::sync::atomic::{Ordering, AtomicBool};
use cgmath::{self, Matrix};
use euclid::{Rect, Size2D, Point2D};
use gl::types::*;
use gl;
-
+use notify::{Watcher as WatcherApi, RecommendedWatcher as Watcher, op};
use text::RasterizedGlyph;
use grid::Grid;
use term;
use super::{Rgb, TermProps, GlyphCache};
-static TEXT_SHADER_V: &'static str = include_str!("../../res/text.v.glsl");
-static TEXT_SHADER_F: &'static str = include_str!("../../res/text.f.glsl");
+// static TEXT_SHADER_V: &'static str = include_str!("../../res/text.v.glsl");
+// static TEXT_SHADER_F: &'static str = include_str!("../../res/text.f.glsl");
+
+static TEXT_SHADER_F_PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.f.glsl");
+static TEXT_SHADER_V_PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.v.glsl");
pub struct QuadRenderer {
program: ShaderProgram,
+ should_reload: Arc<AtomicBool>,
vao: GLuint,
vbo: GLuint,
ebo: GLuint,
@@ -39,7 +48,7 @@ pub struct PackedVertex {
impl QuadRenderer {
// TODO should probably hand this a transform instead of width/height
pub fn new(width: u32, height: u32) -> QuadRenderer {
- let program = ShaderProgram::new(width, height);
+ let program = ShaderProgram::new(width, height).unwrap();
let mut vao: GLuint = 0;
let mut vbo: GLuint = 0;
@@ -88,8 +97,41 @@ impl QuadRenderer {
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, 0);
}
+ let should_reload = Arc::new(AtomicBool::new(false));
+ let should_reload2 = should_reload.clone();
+
+ ::std::thread::spawn(move || {
+ let (tx, rx) = ::std::sync::mpsc::channel();
+ let mut watcher = Watcher::new(tx).unwrap();
+ watcher.watch(TEXT_SHADER_F_PATH).expect("watch fragment shader");
+ watcher.watch(TEXT_SHADER_V_PATH).expect("watch vertex shader");
+
+ loop {
+ let event = rx.recv().expect("watcher event");
+ let ::notify::Event { path, op } = event;
+
+ if let Ok(op) = op {
+ if op.contains(op::RENAME) {
+ continue;
+ }
+
+ if op.contains(op::IGNORED) {
+ if let Some(path) = path.as_ref() {
+ if let Err(err) = watcher.watch(path) {
+ println!("failed to establish watch on {:?}: {:?}", path, err);
+ }
+ }
+
+ // This is last event we see after saving in vim
+ should_reload2.store(true, Ordering::Relaxed);
+ }
+ }
+ }
+ });
+
let mut renderer = QuadRenderer {
program: program,
+ should_reload: should_reload,
vao: vao,
vbo: vbo,
ebo: ebo,
@@ -109,10 +151,10 @@ impl QuadRenderer {
pub fn render_string(&mut self,
s: &str,
glyph_cache: &GlyphCache,
- cell_width: u32,
+ props: &TermProps,
color: &Rgb)
{
- self.prepare_render();
+ self.prepare_render(props);
let (mut x, mut y) = (800f32, 20f32);
@@ -121,7 +163,7 @@ impl QuadRenderer {
self.render(glyph, x, y, color);
}
- x += cell_width as f32 + 2f32;
+ x += props.cell_width as f32 + 2f32;
}
self.finish_render();
@@ -132,7 +174,7 @@ impl QuadRenderer {
glyph_cache: &GlyphCache,
props: &TermProps)
{
- self.prepare_render();
+ self.prepare_render(props);
if let Some(glyph) = glyph_cache.get(&term::CURSOR_SHAPE) {
let y = (props.cell_height + props.sep_y) * (cursor.y as f32);
let x = (props.cell_width + props.sep_x) * (cursor.x as f32);
@@ -146,7 +188,7 @@ impl QuadRenderer {
}
pub fn render_grid(&mut self, grid: &Grid, glyph_cache: &GlyphCache, props: &TermProps) {
- self.prepare_render();
+ self.prepare_render(props);
for i in 0..grid.rows() {
let row = &grid[i];
for j in 0..row.cols() {
@@ -166,7 +208,7 @@ impl QuadRenderer {
self.finish_render();
}
- fn prepare_render(&self) {
+ fn prepare_render(&mut self, props: &TermProps) {
unsafe {
self.program.activate();
@@ -175,6 +217,10 @@ impl QuadRenderer {
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo);
gl::ActiveTexture(gl::TEXTURE0);
}
+
+ if self.should_reload.load(Ordering::Relaxed) {
+ self.reload_shaders(props.width as u32, props.height as u32);
+ }
}
fn finish_render(&mut self) {
@@ -187,6 +233,30 @@ impl QuadRenderer {
}
}
+ pub fn reload_shaders(&mut self, width: u32, height: u32) {
+ self.should_reload.store(false, Ordering::Relaxed);
+ let program = match ShaderProgram::new(width, height) {
+ Ok(program) => program,
+ Err(err) => {
+ match err {
+ ShaderCreationError::Io(err) => {
+ println!("Error reading shader file: {}", err);
+ },
+ ShaderCreationError::Compile(path, log) => {
+ println!("Error compiling shader at {:?}", path);
+ io::copy(&mut log.as_bytes(), &mut io::stdout()).unwrap();
+ }
+ }
+
+ return;
+ }
+ };
+
+ self.active_tex = 0;
+ self.active_color = Rgb { r: 0, g: 0, b: 0 };
+ self.program = program;
+ }
+
fn render(&mut self, glyph: &Glyph, x: f32, y: f32, color: &Rgb) {
if &self.active_color != color {
unsafe {
@@ -308,9 +378,10 @@ impl ShaderProgram {
}
}
- pub fn new(width: u32, height: u32) -> ShaderProgram {
- let vertex_shader = ShaderProgram::create_vertex_shader();
- let fragment_shader = ShaderProgram::create_fragment_shader();
+ pub fn new(width: u32, height: u32) -> Result<ShaderProgram, ShaderCreationError> {
+ let vertex_shader = ShaderProgram::create_shader(TEXT_SHADER_V_PATH, gl::VERTEX_SHADER)?;
+ let fragment_shader = ShaderProgram::create_shader(TEXT_SHADER_F_PATH,
+ gl::FRAGMENT_SHADER)?;
let program = ShaderProgram::create_program(vertex_shader, fragment_shader);
unsafe {
@@ -358,11 +429,10 @@ impl ShaderProgram {
}
shader.deactivate();
- shader
+ Ok(shader)
}
fn create_program(vertex: GLuint, fragment: GLuint) -> GLuint {
-
unsafe {
let program = gl::CreateProgram();
gl::AttachShader(program, vertex);
@@ -379,41 +449,109 @@ impl ShaderProgram {
}
}
- fn create_fragment_shader() -> GLuint {
+
+ fn create_shader(path: &str, kind: GLenum) -> Result<GLuint, ShaderCreationError> {
+ let source = CString::new(read_file(path)?).unwrap();
+ let shader = unsafe {
+ let shader = gl::CreateShader(kind);
+ gl::ShaderSource(shader, 1, &source.as_ptr(), ptr::null());
+ gl::CompileShader(shader);
+ shader
+ };
+
+ let mut success: GLint = 0;
unsafe {
- let fragment_shader = gl::CreateShader(gl::FRAGMENT_SHADER);
- let fragment_source = CString::new(TEXT_SHADER_F).unwrap();
- gl::ShaderSource(fragment_shader, 1, &fragment_source.as_ptr(), ptr::null());
- gl::CompileShader(fragment_shader);
+ gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut success);
+ }
- let mut success: GLint = 0;
- gl::GetShaderiv(fragment_shader, gl::COMPILE_STATUS, &mut success);
+ if success == (gl::TRUE as GLint) {
+ Ok(shader)
+ } else {
+ // Read log
+ let log = get_shader_info_log(shader);
- if success != (gl::TRUE as GLint) {
- panic!("failed to compiler fragment shader");
- }
- fragment_shader
+ // Cleanup
+ unsafe { gl::DeleteShader(shader); }
+
+ Err(ShaderCreationError::Compile(PathBuf::from(path), log))
}
}
+}
- fn create_vertex_shader() -> GLuint {
- unsafe {
- let vertex_shader = gl::CreateShader(gl::VERTEX_SHADER);
- let vertex_source = CString::new(TEXT_SHADER_V).unwrap();
- gl::ShaderSource(vertex_shader, 1, &vertex_source.as_ptr(), ptr::null());
- gl::CompileShader(vertex_shader);
+fn get_shader_info_log(shader: GLuint) -> String {
+ // Get expected log length
+ let mut max_length: GLint = 0;
+ unsafe {
+ gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut max_length);
+ }
- let mut success: GLint = 0;
- gl::GetShaderiv(vertex_shader, gl::COMPILE_STATUS, &mut success);
+ // Read the info log
+ let mut actual_length: GLint = 0;
+ let mut buf: Vec<u8> = Vec::with_capacity(max_length as usize);
+ unsafe {
+ gl::GetShaderInfoLog(shader, max_length, &mut actual_length, buf.as_mut_ptr() as *mut _);
+ }
- if success != (gl::TRUE as GLint) {
- panic!("failed to compiler vertex shader");
- }
- vertex_shader
+ // Build a string
+ unsafe {
+ buf.set_len(actual_length as usize);
+ }
+
+ // XXX should we expect opengl to return garbage?
+ String::from_utf8(buf).unwrap()
+}
+
+fn read_file(path: &str) -> Result<String, io::Error> {
+ let mut f = File::open(path)?;
+ let mut buf = String::new();
+ f.read_to_string(&mut buf)?;
+
+ Ok(buf)
+}
+
+#[derive(Debug)]
+pub enum ShaderCreationError {
+ /// Error reading file
+ Io(io::Error),
+
+ /// Error compiling shader
+ Compile(PathBuf, String),
+}
+
+impl ::std::error::Error for ShaderCreationError {
+ fn cause(&self) -> Option<&::std::error::Error> {
+ match *self {
+ ShaderCreationError::Io(ref err) => Some(err),
+ ShaderCreationError::Compile(_, _) => None,
+ }
+ }
+
+ fn description(&self) -> &str {
+ match *self {
+ ShaderCreationError::Io(ref err) => err.description(),
+ ShaderCreationError::Compile(ref _path, ref s) => s.as_str(),
}
}
}
+impl ::std::fmt::Display for ShaderCreationError {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ match *self {
+ ShaderCreationError::Io(ref err) => write!(f, "Error creating shader: {}", err),
+ ShaderCreationError::Compile(ref _path, ref s) => {
+ write!(f, "Error compiling shader: {}", s)
+ },
+ }
+ }
+}
+
+impl From<io::Error> for ShaderCreationError {
+ fn from(val: io::Error) -> ShaderCreationError {
+ ShaderCreationError::Io(val)
+ }
+}
+
+
/// Manages a single texture atlas
///
/// The strategy for filling an atlas looks roughly like this: