From cf9d94eb1256881d48b1a3dc63db6caf32bfb841 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Mon, 17 Dec 2018 19:06:07 +0000 Subject: [PATCH] Add color option to visual bell This adds the option to specify the color of the visual bell using the `visual_bell.color` configuration setting. This is done by rendering a big quad over the entire screen, which also opens up options to draw other arbitrary rectangles on the screen in the future. --- CHANGELOG.md | 4 + Cargo.lock | 38 ++-- alacritty.yml | 1 + alacritty_macos.yml | 15 +- alacritty_windows.yml | 1 + res/rect.f.glsl | 22 +++ res/rect.v.glsl | 25 +++ res/text.f.glsl | 3 +- res/text.v.glsl | 4 +- src/config.rs | 24 ++- src/display.rs | 22 ++- src/renderer/mod.rs | 449 +++++++++++++++++++++++++++++------------- 12 files changed, 426 insertions(+), 182 deletions(-) create mode 100644 res/rect.f.glsl create mode 100644 res/rect.v.glsl diff --git a/CHANGELOG.md b/CHANGELOG.md index 88a0e263..8ef9fcc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- New configuration field `visual_bell.color` allows changing the visual bell color + ### Fixed - Fix color issue in ncurses programs by updating terminfo pairs from 0x10000 to 0x7FFF diff --git a/Cargo.lock b/Cargo.lock index 473d6292..97e8238e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -122,7 +122,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "arrayvec" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -152,7 +152,7 @@ dependencies = [ "backtrace-sys 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -220,7 +220,7 @@ name = "blake2-rfc" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -519,10 +519,10 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-epoch 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -531,7 +531,7 @@ name = "crossbeam-epoch" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -542,10 +542,10 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1535,12 +1535,12 @@ dependencies = [ "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.39 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "openssl-sys" -version = "0.9.39" +version = "0.9.40" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1938,7 +1938,7 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1954,7 +1954,7 @@ name = "rusttype" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "ordered-float 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "stb_truetype 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1965,7 +1965,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "approx 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "arrayvec 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "ordered-float 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "stb_truetype 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2440,7 +2440,7 @@ name = "tokio-threadpool" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-deque 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2886,7 +2886,7 @@ dependencies = [ "checksum approx 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3c57ff1a5b00753647aebbbcf4ea67fa1e711a65ea7a30eb90dbf07de2485aee" "checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392" "checksum arraydeque 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e300327073b806ffc81fccb228b2d4131ac7ef1b1a015f7b0c399c7f886cacc6" -"checksum arrayvec 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f405cc4c21cd8b784f6c8fc2adf9bc00f59558f0049b5ec21517f875963040cc" +"checksum arrayvec 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d18513977c2d8261c448511c5c53dc66b26dfccbc3d4446672dea1e71a7d8a26" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e5f34df7a019573fb8bdc7e24a2bfebe51a2a1d6bfdbaeccedb3c41fc574727" "checksum backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b5b493b66e03090ebc4343eb02f94ff944e0cbc9ac6571491d170ba026741eb5" @@ -2931,9 +2931,9 @@ dependencies = [ "checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" "checksum crc32fast 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e91d5240c6975ef33aeb5f148f35275c25eda8e8a5f95abe421978b05b8bf192" "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" -"checksum crossbeam-deque 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4fe1b6f945f824c7a25afe44f62e25d714c0cc523f8e99d8db5cd1026e1269d3" +"checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" -"checksum crossbeam-epoch 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2449aaa4ec7ef96e5fb24db16024b935df718e9ae1cec0a1e68feeca2efca7b8" +"checksum crossbeam-epoch 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f10a4f8f409aaac4b16a5474fb233624238fcdeefb9ba50d5ea059aab63ba31c" "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" "checksum crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "41ee4864f4797060e52044376f7d107429ce1fb43460021b126424b7180ee21a" "checksum deflate 0.7.19 (registry+https://github.com/rust-lang/crates.io-index)" = "8a6abb26e16e8d419b5c78662aa9f82857c2386a073da266840e474d5055ec86" @@ -3042,7 +3042,7 @@ dependencies = [ "checksum objc-foundation 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" "checksum objc_id 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" "checksum openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "a3605c298474a3aa69de92d21139fb5e2a81688d308262359d85cdd0d12a7985" -"checksum openssl-sys 0.9.39 (registry+https://github.com/rust-lang/crates.io-index)" = "278c1ad40a89aa1e741a1eed089a2f60b18fab8089c3139b542140fc7d674106" +"checksum openssl-sys 0.9.40 (registry+https://github.com/rust-lang/crates.io-index)" = "1bb974e77de925ef426b6bc82fce15fd45bdcbeb5728bffcfc7cdeeb7ce1c2d6" "checksum ordered-float 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7eb5259643245d3f292c7a146b2df53bba24d7eab159410e648eb73dc164669d" "checksum ordered-float 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2f0015e9e8e28ee20c581cfbfe47c650cedeb9ed0721090e0b7ebb10b9cdbcc2" "checksum osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" @@ -3086,7 +3086,7 @@ dependencies = [ "checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum reqwest 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)" = "738769ec83daf6c1929dc9dae7d69ed3779b55ae5c356e989dcd3aa677d8486e" -"checksum rustc-demangle 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "82ae957aa1b3055d8e086486723c0ccd3d7b8fa190ae8fa2e35543b6171c810e" +"checksum rustc-demangle 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "01b90379b8664dd83460d59bdc5dd1fd3172b8913788db483ed1325171eab2f7" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rusttype 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "11ff03da02f6d340bbee5ec55eed03ff9abd6ea013b93bc7c35973cc28f65999" "checksum rusttype 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "436c67ae0d0d24f14e1177c3ed96780ee16db82b405f0fba1bb80b46c9a30625" diff --git a/alacritty.yml b/alacritty.yml index fdd7f4f3..2978f1dc 100644 --- a/alacritty.yml +++ b/alacritty.yml @@ -223,6 +223,7 @@ colors: visual_bell: animation: EaseOutExpo duration: 0 + color: '0xffffff' # Background opacity # diff --git a/alacritty_macos.yml b/alacritty_macos.yml index acfc4d16..4c8e26ed 100644 --- a/alacritty_macos.yml +++ b/alacritty_macos.yml @@ -234,6 +234,7 @@ colors: visual_bell: animation: EaseOutExpo duration: 0 + color: '0xffffff' # Background opacity # @@ -244,15 +245,15 @@ background_opacity: 1.0 # Mouse bindings # # Available fields: -# - mouse -# - action -# - mods (optional) +# - mouse +# - action +# - mods (optional) # # Values for `mouse`: -# - Middle -# - Left -# - Right -# - Numeric identifier such as `5` +# - Middle +# - Left +# - Right +# - Numeric identifier such as `5` # # All available `mods` and `action` values are documented in the key binding # section. diff --git a/alacritty_windows.yml b/alacritty_windows.yml index b7ca1259..59de4099 100644 --- a/alacritty_windows.yml +++ b/alacritty_windows.yml @@ -204,6 +204,7 @@ colors: visual_bell: animation: EaseOutExpo duration: 0 + color: '0xffffff' # Background opacity # diff --git a/res/rect.f.glsl b/res/rect.f.glsl new file mode 100644 index 00000000..907ee858 --- /dev/null +++ b/res/rect.f.glsl @@ -0,0 +1,22 @@ +// Copyright 2016 Joe Wilm, The Alacritty Project Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#version 330 core +in vec4 color; + +out vec4 FragColor; + +void main() +{ + FragColor = color; +} diff --git a/res/rect.v.glsl b/res/rect.v.glsl new file mode 100644 index 00000000..a353b6b0 --- /dev/null +++ b/res/rect.v.glsl @@ -0,0 +1,25 @@ +// Copyright 2016 Joe Wilm, The Alacritty Project Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#version 330 core +layout (location = 0) in vec3 aPos; + +out vec4 color; + +uniform vec4 col; + +void main() +{ + gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0); + color = col; +} diff --git a/res/text.f.glsl b/res/text.f.glsl index 5915960f..532cf929 100644 --- a/res/text.f.glsl +++ b/res/text.f.glsl @@ -15,7 +15,6 @@ in vec2 TexCoords; in vec3 fg; in vec4 bg; -flat in float vb; flat in int background; layout(location = 0, index = 0) out vec4 color; @@ -31,7 +30,7 @@ void main() discard; alphaMask = vec4(1.0); - color = vec4(bg.rgb + vb, 1.0); + color = vec4(bg.rgb, 1.0); } else { vec3 textColor = texture(mask, TexCoords).rgb; alphaMask = vec4(textColor, textColor.r); diff --git a/res/text.v.glsl b/res/text.v.glsl index 9580c71f..32aee5ac 100644 --- a/res/text.v.glsl +++ b/res/text.v.glsl @@ -25,6 +25,7 @@ layout (location = 3) in vec4 uv; // text fg color layout (location = 4) in vec3 textColor; + // Background color layout (location = 5) in vec4 backgroundColor; @@ -36,12 +37,10 @@ out vec4 bg; uniform vec2 termDim; uniform vec2 cellDim; -uniform float visualBell; uniform int backgroundPass; // Orthographic projection uniform mat4 projection; -flat out float vb; flat out int background; void main() @@ -73,7 +72,6 @@ void main() TexCoords = uvOffset + vec2(position.x, 1 - position.y) * uvSize; } - vb = visualBell; background = backgroundPass; bg = vec4(backgroundColor.rgb / 255.0, backgroundColor.a); fg = textColor / vec3(255.0, 255.0, 255.0); diff --git a/src/config.rs b/src/config.rs index 39a5c024..d5eadcf8 100644 --- a/src/config.rs +++ b/src/config.rs @@ -157,13 +157,12 @@ pub struct VisualBellConfig { animation: VisualBellAnimation, /// Visual bell duration in milliseconds - #[serde(deserialize_with = "deserialize_visual_bell_duration")] - #[serde(default="default_visual_bell_duration")] + #[serde(default, deserialize_with = "deserialize_visual_bell_duration")] duration: u16, -} -fn default_visual_bell_duration() -> u16 { - 150 + /// Visual bell flash color + #[serde(default="default_visual_bell_color", deserialize_with = "rgb_from_hex")] + color: Rgb, } fn deserialize_visual_bell_duration<'a, D>(deserializer: D) -> ::std::result::Result @@ -173,11 +172,15 @@ fn deserialize_visual_bell_duration<'a, D>(deserializer: D) -> ::std::result::Re Ok(duration) => Ok(duration), Err(err) => { error!("problem with config: {}; Using default value", err); - Ok(default_visual_bell_duration()) + Ok(0) }, } } +fn default_visual_bell_color() -> Rgb { + Rgb { r: 255, g: 255, b: 255 } +} + impl VisualBellConfig { /// Visual bell animation #[inline] @@ -190,13 +193,20 @@ impl VisualBellConfig { pub fn duration(&self) -> Duration { Duration::from_millis(u64::from(self.duration)) } + + /// Visual bell flash color + #[inline] + pub fn color(&self) -> Rgb { + self.color + } } impl Default for VisualBellConfig { fn default() -> VisualBellConfig { VisualBellConfig { animation: VisualBellAnimation::default(), - duration: default_visual_bell_duration(), + color: default_visual_bell_color(), + duration: 0, } } } diff --git a/src/display.rs b/src/display.rs index 99889c67..120cd1dc 100644 --- a/src/display.rs +++ b/src/display.rs @@ -158,8 +158,8 @@ impl Display { let dimensions = options.dimensions() .unwrap_or_else(|| config.dimensions()); - let mut padding_x = (f64::from(config.padding().x) * dpr).floor(); - let mut padding_y = (f64::from(config.padding().y) * dpr).floor(); + let mut padding_x = f64::from(config.padding().x) * dpr; + let mut padding_y = f64::from(config.padding().y) * dpr; if dimensions.columns_u32() > 0 && dimensions.lines_u32() > 0 @@ -168,6 +168,8 @@ impl Display { // Calculate new size based on cols/lines specified in config let width = cell_width as u32 * dimensions.columns_u32(); let height = cell_height as u32 * dimensions.lines_u32(); + padding_x = padding_x.floor(); + padding_y = padding_y.floor(); viewport_size = PhysicalSize::new( f64::from(width) + 2. * padding_x, @@ -211,7 +213,6 @@ impl Display { renderer.with_api( config, &size_info, - 0., /* visual bell intensity */ |api| { api.clear(background_color); }, @@ -325,8 +326,6 @@ impl Display { self.update_glyph_cache(config); } - // Receive any resize events; only call gl::Viewport on last - // available if let Some(psize) = new_size.take() { let width = psize.width as f32; let height = psize.height as f32; @@ -405,7 +404,7 @@ impl Display { // handling and rendering. drop(terminal); - self.renderer.with_api(config, &size_info, visual_bell_intensity, |api| { + self.renderer.with_api(config, &size_info, |api| { api.clear(background_color); }); @@ -416,12 +415,15 @@ impl Display { { let _sampler = self.meter.sampler(); - self.renderer.with_api(config, &size_info, visual_bell_intensity, |mut api| { + self.renderer.with_api(config, &size_info, |mut api| { // Draw the grid api.render_cells(grid_cells.iter(), glyph_cache); }); } + // Draw rectangles + self.renderer.draw_rects(config, &size_info, visual_bell_intensity); + // Draw render timer if self.render_timer { let timing = format!("{:.3} usec", self.meter.average()); @@ -430,7 +432,7 @@ impl Display { g: 0x4e, b: 0x53, }; - self.renderer.with_api(config, &size_info, visual_bell_intensity, |mut api| { + self.renderer.with_api(config, &size_info, |mut api| { api.render_string(&timing[..], size_info.lines() - 2, glyph_cache, color); }); } @@ -446,7 +448,7 @@ impl Display { g: 0x00, b: 0x00, }; - self.renderer.with_api(config, &size_info, visual_bell_intensity, |mut api| { + self.renderer.with_api(config, &size_info, |mut api| { api.render_string(&msg, size_info.lines() - 1, glyph_cache, color); }); } else if self.logger_proxy.warnings() { @@ -459,7 +461,7 @@ impl Display { g: 0xff, b: 0x00, }; - self.renderer.with_api(config, &size_info, visual_bell_intensity, |mut api| { + self.renderer.with_api(config, &size_info, |mut api| { api.render_string(&msg, size_info.lines() - 1, glyph_cache, color); }); } diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 5d7a661f..c05301cc 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -37,12 +37,18 @@ use glutin::dpi::PhysicalSize; // Shader paths for live reload 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"); +static RECT_SHADER_F_PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/res/rect.f.glsl"); +static RECT_SHADER_V_PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/res/rect.v.glsl"); // Shader source which is used when live-shader-reload feature is disable static TEXT_SHADER_F: &'static str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.f.glsl")); static TEXT_SHADER_V: &'static str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.v.glsl")); +static RECT_SHADER_F: &'static str = + include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/res/rect.f.glsl")); +static RECT_SHADER_V: &'static str = + include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/res/rect.v.glsl")); /// `LoadGlyph` allows for copying a rasterized glyph into graphics memory pub trait LoadGlyph { @@ -98,7 +104,7 @@ impl From for Error { /// /// Uniforms are prefixed with "u", and vertex attributes are prefixed with "a". #[derive(Debug)] -pub struct ShaderProgram { +pub struct TextShaderProgram { // Program id id: GLuint, @@ -111,15 +117,23 @@ pub struct ShaderProgram { /// Cell dimensions (pixels) u_cell_dim: GLint, - /// Visual bell - u_visual_bell: GLint, - /// Background pass flag /// /// Rendering is split into two passes; 1 for backgrounds, and one for text u_background: GLint, } +/// Rectangle drawing program +/// +/// Uniforms are prefixed with "u" +#[derive(Debug)] +pub struct RectShaderProgram { + // Program id + id: GLuint, + /// Rectangle color + u_col: GLint, +} + #[derive(Copy, Debug, Clone)] pub struct Glyph { tex_id: GLuint, @@ -351,11 +365,13 @@ struct InstanceData { #[derive(Debug)] pub struct QuadRenderer { - program: ShaderProgram, + program: TextShaderProgram, + rect_program: RectShaderProgram, vao: GLuint, - vbo: GLuint, ebo: GLuint, vbo_instance: GLuint, + rect_vao: GLuint, + rect_vbo: GLuint, atlas: Vec, current_atlas: usize, active_tex: GLuint, @@ -369,9 +385,8 @@ pub struct RenderApi<'a> { batch: &'a mut Batch, atlas: &'a mut Vec, current_atlas: &'a mut usize, - program: &'a mut ShaderProgram, + program: &'a mut TextShaderProgram, config: &'a Config, - visual_bell_intensity: f32, } #[derive(Debug)] @@ -470,7 +485,8 @@ const ATLAS_SIZE: i32 = 1024; impl QuadRenderer { // TODO should probably hand this a transform instead of width/height pub fn new(size: PhysicalSize) -> Result { - let program = ShaderProgram::new(size)?; + let program = TextShaderProgram::new(size)?; + let rect_program = RectShaderProgram::new()?; let mut vao: GLuint = 0; let mut vbo: GLuint = 0; @@ -478,6 +494,10 @@ impl QuadRenderer { let mut vbo_instance: GLuint = 0; + let mut rect_vao: GLuint = 0; + let mut rect_vbo: GLuint = 0; + let mut rect_ebo: GLuint = 0; + unsafe { gl::Enable(gl::BLEND); gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR); @@ -598,8 +618,27 @@ impl QuadRenderer { gl::EnableVertexAttribArray(5); gl::VertexAttribDivisor(5, 1); + // Rectangle setup + gl::GenVertexArrays(1, &mut rect_vao); + gl::GenBuffers(1, &mut rect_vbo); + gl::GenBuffers(1, &mut rect_ebo); + gl::BindVertexArray(rect_vao); + let indices: [i32; 6] = [ + 0, 1, 3, + 1, 2, 3, + ]; + gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, rect_ebo); + gl::BufferData( + gl::ELEMENT_ARRAY_BUFFER, + (size_of::() * indices.len()) as _, + indices.as_ptr() as *const _, + gl::STATIC_DRAW + ); + + // Cleanup gl::BindVertexArray(0); gl::BindBuffer(gl::ARRAY_BUFFER, 0); + gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, 0); } let (msg_tx, msg_rx) = mpsc::channel(); @@ -635,10 +674,12 @@ impl QuadRenderer { let mut renderer = QuadRenderer { program, + rect_program, vao, - vbo, ebo, vbo_instance, + rect_vao, + rect_vbo, atlas: Vec::new(), current_atlas: 0, active_tex: 0, @@ -652,28 +693,77 @@ impl QuadRenderer { Ok(renderer) } - pub fn with_api( + // Draw all rectangles simultaneously to prevent excessive program swaps + pub fn draw_rects( &mut self, config: &Config, props: &term::SizeInfo, visual_bell_intensity: f64, + ) { + // Swap to rectangle rendering program + unsafe { + // Swap program + gl::UseProgram(self.rect_program.id); + + // Remove padding from viewport + gl::Viewport(0, 0, props.width as i32, props.height as i32); + + // Change blending strategy + gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); + + // Setup data and buffers + gl::BindVertexArray(self.rect_vao); + gl::BindBuffer(gl::ARRAY_BUFFER, self.rect_vbo); + + // Position + gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, (size_of::() * 3) as _, ptr::null()); + gl::EnableVertexAttribArray(0); + } + + // Draw visual bell + let color = config.visual_bell().color(); + let rect = Rect::new(0., 0., props.width, props.height); + self.render_rect(&rect, color, visual_bell_intensity as f32, props); + + // Deactivate rectangle program again + unsafe { + // Reset blending strategy + gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR); + + // Reset data and buffers + gl::BindBuffer(gl::ARRAY_BUFFER, 0); + gl::BindVertexArray(0); + + let padding_x = props.padding_x as i32; + let padding_y = props.padding_y as i32; + let width = props.width as i32; + let height = props.height as i32; + gl::Viewport(padding_x, padding_y, width - 2 * padding_x, height - 2 * padding_y); + + // Disable program + gl::UseProgram(0); + } + } + + pub fn with_api( + &mut self, + config: &Config, + props: &term::SizeInfo, func: F, ) -> T where F: FnOnce(RenderApi<'_>) -> T, { - while let Ok(msg) = self.rx.try_recv() { - match msg { - Msg::ShaderReload => { - self.reload_shaders(PhysicalSize::new(f64::from(props.width), f64::from(props.height))); - } - } + // Flush message queue + if let Ok(Msg::ShaderReload) = self.rx.try_recv() { + let size = PhysicalSize::new(f64::from(props.width), f64::from(props.height)); + self.reload_shaders(size); } + while let Ok(_) = self.rx.try_recv() {} unsafe { - self.program.activate(); + gl::UseProgram(self.program.id); self.program.set_term_uniforms(props); - self.program.set_visual_bell(visual_bell_intensity as _); gl::BindVertexArray(self.vao); gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo); @@ -687,7 +777,6 @@ impl QuadRenderer { atlas: &mut self.atlas, current_atlas: &mut self.current_atlas, program: &mut self.program, - visual_bell_intensity: visual_bell_intensity as _, config, }); @@ -696,7 +785,7 @@ impl QuadRenderer { gl::BindBuffer(gl::ARRAY_BUFFER, 0); gl::BindVertexArray(0); - self.program.deactivate(); + gl::UseProgram(0); } res @@ -719,12 +808,13 @@ impl QuadRenderer { pub fn reload_shaders(&mut self, size: PhysicalSize) { warn!("Reloading shaders ..."); - let program = match ShaderProgram::new(size) { - Ok(program) => { + let result = (TextShaderProgram::new(size), RectShaderProgram::new()); + let (program, rect_program) = match result { + (Ok(program), Ok(rect_program)) => { warn!(" ... OK"); - program + (program, rect_program) } - Err(err) => { + (Err(err), _) | (_, Err(err)) => { match err { ShaderCreationError::Io(err) => { error!("Error reading shader file: {}", err); @@ -743,6 +833,7 @@ impl QuadRenderer { self.active_tex = 0; self.program = program; + self.rect_program = rect_program; } pub fn resize(&mut self, size: PhysicalSize, padding_x: f32, padding_y: f32) { @@ -755,12 +846,72 @@ impl QuadRenderer { let padding_x = padding_x as i32; let padding_y = padding_y as i32; gl::Viewport(padding_x, padding_y, width - 2 * padding_x, height - 2 * padding_y); + + // update projection + gl::UseProgram(self.program.id); + self.program.update_projection( + width as f32, + height as f32, + padding_x as f32, + padding_y as f32, + ); + gl::UseProgram(0); + } + } + + // Render a rectangle + // + // This requires the rectangle program to be activated + fn render_rect(&mut self, rect: &Rect, color: Rgb, alpha: f32, size: &term::SizeInfo) { + // Do nothing when alpha is fully transparent + if alpha == 0. { + return; } - // update projection - self.program.activate(); - self.program.update_projection(width as f32, height as f32, padding_x, padding_y); - self.program.deactivate(); + // Calculate rectangle position + let center_x = size.width / 2.; + let center_y = size.height / 2.; + let x = (rect.x - center_x) / center_x; + let y = -(rect.y - center_y) / center_y; + let width = rect.width / center_x; + let height = rect.height / center_y; + + unsafe { + // Setup vertices + let vertices: [f32; 12] = [ + x + width, y , 0.0, + x + width, y - height, 0.0, + x , y - height, 0.0, + x , y , 0.0, + ]; + + // Load vertex data into array buffer + gl::BufferData( + gl::ARRAY_BUFFER, + (size_of::() * vertices.len()) as _, + vertices.as_ptr() as *const _, + gl::STATIC_DRAW + ); + + // Color + self.rect_program.set_color(color, alpha); + + // Draw the rectangle + gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, ptr::null()); + } + } +} + +struct Rect { + x: T, + y: T, + width: T, + height: T, +} + +impl Rect { + fn new(x: T, y: T, width: T, height: T) -> Self { + Rect { x, y, width, height } } } @@ -769,11 +920,11 @@ impl<'a> RenderApi<'a> { let alpha = self.config.background_opacity().get(); unsafe { gl::ClearColor( - (self.visual_bell_intensity + f32::from(color.r) / 255.0).min(1.0) * alpha, - (self.visual_bell_intensity + f32::from(color.g) / 255.0).min(1.0) * alpha, - (self.visual_bell_intensity + f32::from(color.b) / 255.0).min(1.0) * alpha, - alpha - ); + (f32::from(color.r) / 255.0).min(1.0) * alpha, + (f32::from(color.g) / 255.0).min(1.0) * alpha, + (f32::from(color.b) / 255.0).min(1.0) * alpha, + alpha, + ); gl::Clear(gl::COLOR_BUFFER_BIT); } } @@ -1012,39 +1163,20 @@ impl<'a> Drop for RenderApi<'a> { } } -impl ShaderProgram { - pub fn activate(&self) { - unsafe { - gl::UseProgram(self.id); - } - } - - pub fn deactivate(&self) { - unsafe { - gl::UseProgram(0); - } - } - - pub fn new(size: PhysicalSize) -> Result { - let vertex_source = if cfg!(feature = "live-shader-reload") { - None +impl TextShaderProgram { + pub fn new(size: PhysicalSize) -> Result { + let (vertex_src, fragment_src) = if cfg!(feature = "live-shader-reload") { + (None, None) } else { - Some(TEXT_SHADER_V) + (Some(TEXT_SHADER_V), Some(TEXT_SHADER_F)) }; - let vertex_shader = - ShaderProgram::create_shader(TEXT_SHADER_V_PATH, gl::VERTEX_SHADER, vertex_source)?; - let frag_source = if cfg!(feature = "live-shader-reload") { - None - } else { - Some(TEXT_SHADER_F) - }; - let fragment_shader = - ShaderProgram::create_shader(TEXT_SHADER_F_PATH, gl::FRAGMENT_SHADER, frag_source)?; - let program = ShaderProgram::create_program(vertex_shader, fragment_shader)?; + let vertex_shader = create_shader(TEXT_SHADER_V_PATH, gl::VERTEX_SHADER, vertex_src)?; + let fragment_shader = create_shader(TEXT_SHADER_F_PATH, gl::FRAGMENT_SHADER, fragment_src)?; + let program = create_program(vertex_shader, fragment_shader)?; unsafe { - gl::DeleteShader(vertex_shader); gl::DeleteShader(fragment_shader); + gl::DeleteShader(vertex_shader); gl::UseProgram(program); } @@ -1063,30 +1195,28 @@ impl ShaderProgram { } // get uniform locations - let (projection, term_dim, cell_dim, visual_bell, background) = unsafe { + let (projection, term_dim, cell_dim, background) = unsafe { ( gl::GetUniformLocation(program, cptr!(b"projection\0")), gl::GetUniformLocation(program, cptr!(b"termDim\0")), gl::GetUniformLocation(program, cptr!(b"cellDim\0")), - gl::GetUniformLocation(program, cptr!(b"visualBell\0")), gl::GetUniformLocation(program, cptr!(b"backgroundPass\0")), ) }; assert_uniform_valid!(projection, term_dim, cell_dim); - let shader = ShaderProgram { + let shader = TextShaderProgram { id: program, u_projection: projection, u_term_dim: term_dim, u_cell_dim: cell_dim, - u_visual_bell: visual_bell, u_background: background, }; shader.update_projection(size.width as f32, size.height as f32, 0., 0.); - shader.deactivate(); + unsafe { gl::UseProgram(0); } Ok(shader) } @@ -1133,12 +1263,6 @@ impl ShaderProgram { } } - fn set_visual_bell(&self, visual_bell: f32) { - unsafe { - gl::Uniform1f(self.u_visual_bell, visual_bell); - } - } - fn set_background_pass(&self, background_pass: bool) { let value = if background_pass { 1 } else { 0 }; @@ -1146,69 +1270,9 @@ impl ShaderProgram { gl::Uniform1i(self.u_background, value); } } - - fn create_program(vertex: GLuint, fragment: GLuint) -> Result { - unsafe { - let program = gl::CreateProgram(); - gl::AttachShader(program, vertex); - gl::AttachShader(program, fragment); - gl::LinkProgram(program); - - let mut success: GLint = 0; - gl::GetProgramiv(program, gl::LINK_STATUS, &mut success); - - if success == i32::from(gl::TRUE) { - Ok(program) - } else { - Err(ShaderCreationError::Link(get_program_info_log(program))) - } - } - } - - fn create_shader( - path: &str, - kind: GLenum, - source: Option<&'static str>, - ) -> Result { - let from_disk; - let source = if let Some(src) = source { - src - } else { - from_disk = read_file(path)?; - &from_disk[..] - }; - - let len: [GLint; 1] = [source.len() as GLint]; - - let shader = unsafe { - let shader = gl::CreateShader(kind); - gl::ShaderSource(shader, 1, &(source.as_ptr() as *const _), len.as_ptr()); - gl::CompileShader(shader); - shader - }; - - let mut success: GLint = 0; - unsafe { - gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut success); - } - - if success == GLint::from(gl::TRUE) { - Ok(shader) - } else { - // Read log - let log = get_shader_info_log(shader); - - // Cleanup - unsafe { - gl::DeleteShader(shader); - } - - Err(ShaderCreationError::Compile(PathBuf::from(path), log)) - } - } } -impl Drop for ShaderProgram { +impl Drop for TextShaderProgram { fn drop(&mut self) { unsafe { gl::DeleteProgram(self.id); @@ -1216,6 +1280,123 @@ impl Drop for ShaderProgram { } } +impl RectShaderProgram { + pub fn new() -> Result { + let (vertex_src, fragment_src) = if cfg!(feature = "live-shader-reload") { + (None, None) + } else { + (Some(RECT_SHADER_V), Some(RECT_SHADER_F)) + }; + let vertex_shader = create_shader( + RECT_SHADER_V_PATH, + gl::VERTEX_SHADER, + vertex_src + )?; + let fragment_shader = create_shader( + RECT_SHADER_F_PATH, + gl::FRAGMENT_SHADER, + fragment_src + )?; + let program = create_program(vertex_shader, fragment_shader)?; + + unsafe { + gl::DeleteShader(fragment_shader); + gl::DeleteShader(vertex_shader); + gl::UseProgram(program); + } + + // get uniform locations + let u_col = unsafe { + gl::GetUniformLocation(program, b"col\0".as_ptr() as *const _) + }; + + let shader = RectShaderProgram { + id: program, + u_col, + }; + + unsafe { gl::UseProgram(0) } + + Ok(shader) + } + + fn set_color(&self, color: Rgb, alpha: f32) { + unsafe { + gl::Uniform4f( + self.u_col, + f32::from(color.r) / 255., + f32::from(color.g) / 255., + f32::from(color.b) / 255., + alpha, + ); + } + } +} + +impl Drop for RectShaderProgram { + fn drop(&mut self) { + unsafe { + gl::DeleteProgram(self.id); + } + } +} + +fn create_program(vertex: GLuint, fragment: GLuint) -> Result { + unsafe { + let program = gl::CreateProgram(); + gl::AttachShader(program, vertex); + gl::AttachShader(program, fragment); + gl::LinkProgram(program); + + let mut success: GLint = 0; + gl::GetProgramiv(program, gl::LINK_STATUS, &mut success); + + if success == i32::from(gl::TRUE) { + Ok(program) + } else { + Err(ShaderCreationError::Link(get_program_info_log(program))) + } + } +} + +fn create_shader(path: &str, kind: GLenum, source: Option<&'static str>) + -> Result +{ + let from_disk; + let source = if let Some(src) = source { + src + } else { + from_disk = read_file(path)?; + &from_disk[..] + }; + + let len: [GLint; 1] = [source.len() as GLint]; + + let shader = unsafe { + let shader = gl::CreateShader(kind); + gl::ShaderSource(shader, 1, &(source.as_ptr() as *const _), len.as_ptr()); + gl::CompileShader(shader); + shader + }; + + let mut success: GLint = 0; + unsafe { + gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut success); + } + + if success == GLint::from(gl::TRUE) { + Ok(shader) + } else { + // Read log + let log = get_shader_info_log(shader); + + // Cleanup + unsafe { gl::DeleteShader(shader); } + + Err(ShaderCreationError::Compile(PathBuf::from(path), log)) + } +} + fn get_program_info_log(program: GLuint) -> String { // Get expected log length let mut max_length: GLint = 0;