From db08b8c1dcd81b5d9a54cf3341dc85159fa0163a Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Sat, 27 Feb 2021 14:53:19 -0800 Subject: [PATCH] add window:set_config_overrides lua method This commit expands on the prior commits to introduce the concept of per-window configuration overrides. Each TermWindow maintains json compatible object value holding a map of config key -> config value overrides. When the window notices that the config has changed, the config file is loaded, the CLI overrides (if any) are applied, and then finally the per-window overrides, before attempting to coerce the resultant lua value into a Config object. This mechanism has some important constraints: * Only data can be assigned to the overrides. Closures or special lua userdata object handles are not permitted. This is because the lifetime of those objects is tied to the lua context in which they were parsed, which doesn't really exist in the context of the window. * Only simple keys are supported for the per-window overrides. That means that trying to override a very specific field of a deeply structured value (eg: something like `font_rules[1].italic = false` isn't able to be expressed in this scheme. Instead, you would need to assign the entire `font_rules` key. I don't anticipate this being a common desire at this time; if more advance manipulations are required, then I have some thoughts on an event where arbitrary lua modifications can be applied. The implementation details are fairly straight-forward, but in testing the two examplary use cases I noticed that some hangovers from supporting overrides for a couple of font related options meant that the window-specific config wasn't being honored. I've removed the code that handled those overrides in favor of the newer more general CLI option override support, and threaded the config through to the font code. closes: #469 closes: #329 --- Cargo.lock | 2 + config/Cargo.toml | 1 + config/src/font.rs | 42 ----------- config/src/lib.rs | 44 +++++++++++- docs/changelog.md | 3 +- docs/config/lua/window/effective_config.md | 2 + .../config/lua/window/get_config_overrides.md | 9 +++ .../config/lua/window/set_config_overrides.md | 61 ++++++++++++++++ luahelper/Cargo.toml | 1 + luahelper/src/lib.rs | 11 ++- wezterm-font/src/lib.rs | 71 +++++++++++-------- wezterm-font/src/shaper/allsorts.rs | 2 +- wezterm-font/src/shaper/harfbuzz.rs | 12 ++-- wezterm-font/src/shaper/mod.rs | 12 ++-- wezterm-gui-subcommands/src/lib.rs | 24 ------- wezterm-gui/src/gui/shapecache.rs | 2 +- wezterm-gui/src/gui/termwindow.rs | 29 ++++++-- wezterm-gui/src/main.rs | 8 --- wezterm-gui/src/scripting/guiwin.rs | 19 +++++ 19 files changed, 235 insertions(+), 120 deletions(-) create mode 100644 docs/config/lua/window/get_config_overrides.md create mode 100644 docs/config/lua/window/set_config_overrides.md diff --git a/Cargo.lock b/Cargo.lock index 92d64f745..fc967d9f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -687,6 +687,7 @@ dependencies = [ "pretty_env_logger", "promise", "serde", + "serde_json", "smol", "termwiz", "toml", @@ -1929,6 +1930,7 @@ dependencies = [ "log", "mlua", "serde", + "serde_json", "strsim 0.10.0", "thiserror", ] diff --git a/config/Cargo.toml b/config/Cargo.toml index ec91755d6..e462f2602 100644 --- a/config/Cargo.toml +++ b/config/Cargo.toml @@ -30,6 +30,7 @@ notify = "4.0" portable-pty = { path = "../pty", features = ["serde_support"]} promise = { path = "../promise" } serde = {version="1.0", features = ["rc", "derive"]} +serde_json = "1.0" smol = "1.2" termwiz = { path = "../termwiz" } toml = "0.5" diff --git a/config/src/font.rs b/config/src/font.rs index d273d190c..36318d0b6 100644 --- a/config/src/font.rs +++ b/config/src/font.rs @@ -369,10 +369,6 @@ pub enum FontLocatorSelection { ConfigDirsOnly, } -lazy_static::lazy_static! { - static ref DEFAULT_LOCATOR: Mutex = Mutex::new(Default::default()); -} - impl Default for FontLocatorSelection { fn default() -> Self { if cfg!(windows) { @@ -386,16 +382,6 @@ impl Default for FontLocatorSelection { } impl FontLocatorSelection { - pub fn set_default(self) { - let mut def = DEFAULT_LOCATOR.lock().unwrap(); - *def = self; - } - - pub fn get_default() -> Self { - let def = DEFAULT_LOCATOR.lock().unwrap(); - *def - } - pub fn variants() -> Vec<&'static str> { vec!["FontConfig", "CoreText", "ConfigDirsOnly", "Gdi"] } @@ -423,10 +409,6 @@ pub enum FontRasterizerSelection { FreeType, } -lazy_static::lazy_static! { - static ref DEFAULT_RASTER: Mutex = Mutex::new(Default::default()); -} - impl Default for FontRasterizerSelection { fn default() -> Self { FontRasterizerSelection::FreeType @@ -434,16 +416,6 @@ impl Default for FontRasterizerSelection { } impl FontRasterizerSelection { - pub fn set_default(self) { - let mut def = DEFAULT_RASTER.lock().unwrap(); - *def = self; - } - - pub fn get_default() -> Self { - let def = DEFAULT_RASTER.lock().unwrap(); - *def - } - pub fn variants() -> Vec<&'static str> { vec!["FreeType"] } @@ -469,10 +441,6 @@ pub enum FontShaperSelection { Harfbuzz, } -lazy_static::lazy_static! { - static ref DEFAULT_SHAPER: Mutex = Mutex::new(Default::default()); -} - impl Default for FontShaperSelection { fn default() -> Self { FontShaperSelection::Harfbuzz @@ -480,16 +448,6 @@ impl Default for FontShaperSelection { } impl FontShaperSelection { - pub fn set_default(self) { - let mut def = DEFAULT_SHAPER.lock().unwrap(); - *def = self; - } - - pub fn get_default() -> Self { - let def = DEFAULT_SHAPER.lock().unwrap(); - *def - } - pub fn variants() -> Vec<&'static str> { vec!["Harfbuzz", "AllSorts"] } diff --git a/config/src/lib.rs b/config/src/lib.rs index ec47e6994..8b46f21cc 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -310,6 +310,12 @@ pub fn configuration() -> ConfigHandle { CONFIG.get() } +/// Returns a version of the config (loaded from the config file) +/// with some field overridden based on the supplied overrides object. +pub fn overridden_config(overrides: &serde_json::Value) -> Result { + CONFIG.overridden(overrides) +} + pub fn reload() { CONFIG.reload(); } @@ -447,9 +453,17 @@ impl ConfigInner { self.generation += 1; } + fn overridden(&mut self, overrides: &serde_json::Value) -> Result { + let config = Config::load_with_overrides(overrides)?; + Ok(ConfigHandle { + config: Arc::new(config.config), + generation: self.generation, + }) + } + fn use_test(&mut self) { - FontLocatorSelection::ConfigDirsOnly.set_default(); let mut config = Config::default_config(); + config.font_locator = FontLocatorSelection::ConfigDirsOnly; // Specify the same DPI used on non-mac systems so // that we have consistent values regardless of the // operating system that we're running tests on @@ -491,6 +505,11 @@ impl Configuration { inner.use_this_config(cfg); } + fn overridden(&self, overrides: &serde_json::Value) -> Result { + let mut inner = self.inner.lock().unwrap(); + inner.overridden(overrides) + } + /// Use a config that doesn't depend on the user's /// environment and is suitable for unit testing pub fn use_test(&self) { @@ -1136,6 +1155,10 @@ impl PathPossibility { impl Config { pub fn load() -> Result { + Self::load_with_overrides(&serde_json::Value::default()) + } + + pub fn load_with_overrides(overrides: &serde_json::Value) -> Result { // Note that the directories crate has methods for locating project // specific config directories, but only returns one of them, not // multiple. In addition, it spawns a lot of subprocesses, @@ -1193,6 +1216,7 @@ impl Config { .eval_async(), )?; let config = Self::apply_overrides_to(&lua, config)?; + let config = Self::apply_overrides_obj_to(config, overrides)?; cfg = luahelper::from_lua_value(config).with_context(|| { format!( "Error converting lua value returned by script {} to Config struct", @@ -1222,6 +1246,24 @@ impl Config { }) } + fn apply_overrides_obj_to<'l>( + mut config: mlua::Value<'l>, + overrides: &serde_json::Value, + ) -> anyhow::Result> { + match overrides { + serde_json::Value::Object(obj) => { + if let mlua::Value::Table(tbl) = &mut config { + for (key, value) in obj { + let value = luahelper::JsonLua(value.clone()); + tbl.set(key.as_str(), value)?; + } + } + Ok(config) + } + _ => Ok(config), + } + } + fn apply_overrides_to<'l>( lua: &'l mlua::Lua, mut config: mlua::Value<'l>, diff --git a/docs/changelog.md b/docs/changelog.md index 74d4371d0..b311e3803 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -33,7 +33,8 @@ brief notes about them may accumulate here. * Windows: Fixed [ToggleFullScreen](config/lua/keyassignment/ToggleFullScreen.md) so that it once again toggles between full screen and normal placement. [#177](https://github.com/wez/wezterm/issues/177) * New: [exit_behavior](config/lua/config/exit_behavior.md) config option to keep panes open after the program has completed. [#499](https://github.com/wez/wezterm/issues/499) * Closing the configuration error window no longer requires confirmation -* New: added `--config name=value` options to `wezterm`, `wezterm-gui` and `wezterm-mux-server` +* New: added `--config name=value` options to `wezterm`, `wezterm-gui` and `wezterm-mux-server`. The `--font-locator`, `--font-rasterizer` and `--font-shaper` CLI options have been removed in favor of this new mechanism. +* New: [window:set_config_overrides](config/lua/window/set_config_overrides.md) method that can be used to override GUI related configuration options on a per-window basis. Click through to see examples of dynamically toggling ligatures and window opacity. [#469](https://github.com/wez/wezterm/issues/469) [#329](https://github.com/wez/wezterm/issues/329) ### 20210203-095643-70a364eb diff --git a/docs/config/lua/window/effective_config.md b/docs/config/lua/window/effective_config.md index ec8556a1d..0adc97618 100644 --- a/docs/config/lua/window/effective_config.md +++ b/docs/config/lua/window/effective_config.md @@ -10,6 +10,8 @@ configuration, including any CLI or per-window configuration overrides. Note: changing the config table will NOT change the effective window config; it is just a copy of that information. +If you want to change the configuration in a window, look at [set_config_overrides](set_config_overrides.md). + This example will log the configured font size when `CTRL-SHIFT-E` is pressed: ```lua diff --git a/docs/config/lua/window/get_config_overrides.md b/docs/config/lua/window/get_config_overrides.md new file mode 100644 index 000000000..571b6f360 --- /dev/null +++ b/docs/config/lua/window/get_config_overrides.md @@ -0,0 +1,9 @@ +# `window:get_config_overrides()` + +*Since: nightly* + +Returns a copy of the current set of configuration overrides that is in effect +for the window. + +See [set_config_overrides](set_config_overrides.md) for examples! + diff --git a/docs/config/lua/window/set_config_overrides.md b/docs/config/lua/window/set_config_overrides.md new file mode 100644 index 000000000..b19a2ce92 --- /dev/null +++ b/docs/config/lua/window/set_config_overrides.md @@ -0,0 +1,61 @@ +# `window:set_config_overrides(overrides)` + +*Since: nightly* + +Changes the set of configuration overrides for the window. +The config file is re-evaluated and any CLI overrides are +applied, followed by the keys and values from the `overrides` +parameter. + +This can be used to override configuration on a per-window basis; +this is only useful for options that apply to the GUI window, such +as rendering the GUI. + +In this example, a key assignment (`CTRL-SHIFT-E`) is used to toggle the use of +ligatures in the current window: + +```lua +local wezterm = require 'wezterm' + +wezterm.on("toggle-ligature", function(window, pane) + local overrides = window:get_config_overrides() or {} + if not overrides.harfbuzz_features then + -- If we haven't overriden it yet, then override with ligatures disabled + overrides.harfbuzz_features = {"calt=0", "clig=0", "liga=0"} + else + -- else we did already, and we should disable out override now + overrides.harfbuzz_features = nil + end + window:set_config_overrides(overrides) +end) + +return { + keys = { + {key="E", mods="CTRL", action=wezterm.action{EmitEvent="toggle-ligature"}}, + }, +} +``` + +In this example, a key assignment (`CTRL-SHIFT-B`) is used to toggle opacity +for the window: + +```lua +local wezterm = require 'wezterm' + +wezterm.on("toggle-opacity", function(window, pane) + local overrides = window:get_config_overrides() or {} + if not overrides.window_background_opacity then + overrides.window_background_opacity = 0.5; + else + overrides.window_background_opacity = nil + end + window:set_config_overrides(overrides) +end) + +return { + keys = { + {key="B", mods="CTRL", action=wezterm.action{EmitEvent="toggle-opacity"}}, + }, +} +``` + diff --git a/luahelper/Cargo.toml b/luahelper/Cargo.toml index a96010828..8aae2aed0 100644 --- a/luahelper/Cargo.toml +++ b/luahelper/Cargo.toml @@ -11,5 +11,6 @@ bstr = "0.2" log = "0.4" mlua = "0.5" serde = {version="1.0", features = ["rc", "derive"]} +serde_json = "1.0" strsim = "0.10" thiserror = "1.0" diff --git a/luahelper/src/lib.rs b/luahelper/src/lib.rs index 32415f1fa..cc2d8cc3c 100644 --- a/luahelper/src/lib.rs +++ b/luahelper/src/lib.rs @@ -1,5 +1,7 @@ #![macro_use] +use serde::{Deserialize, Serialize}; + mod serde_lua; pub use mlua; pub use serde_lua::from_lua_value; @@ -19,7 +21,7 @@ macro_rules! impl_lua_conversion { self, lua: &'lua $crate::mlua::Lua, ) -> Result<$crate::mlua::Value<'lua>, $crate::mlua::Error> { - Ok(luahelper::to_lua_value(lua, self)?) + Ok($crate::to_lua_value(lua, self)?) } } @@ -28,8 +30,13 @@ macro_rules! impl_lua_conversion { value: $crate::mlua::Value<'lua>, _lua: &'lua $crate::mlua::Lua, ) -> Result { - Ok(luahelper::from_lua_value(value)?) + Ok($crate::from_lua_value(value)?) } } }; } + +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +pub struct JsonLua(pub serde_json::Value); +impl_lua_conversion!(JsonLua); diff --git a/wezterm-font/src/lib.rs b/wezterm-font/src/lib.rs index 1b63a12d1..eb1f7f108 100644 --- a/wezterm-font/src/lib.rs +++ b/wezterm-font/src/lib.rs @@ -1,7 +1,7 @@ use crate::db::FontDatabase; -use crate::locator::{new_locator, FontDataHandle, FontLocator, FontLocatorSelection}; +use crate::locator::{new_locator, FontDataHandle, FontLocator}; use crate::rasterizer::{new_rasterizer, FontRasterizer}; -use crate::shaper::{new_shaper, FontShaper, FontShaperSelection}; +use crate::shaper::{new_shaper, FontShaper}; use anyhow::{Context, Error}; use config::{configuration, ConfigHandle, FontRasterizerSelection, TextStyle}; use std::cell::RefCell; @@ -60,8 +60,10 @@ impl LoadedFont { } } if loaded { - *self.shaper.borrow_mut() = - new_shaper(FontShaperSelection::get_default(), &self.handles.borrow())?; + if let Some(font_config) = self.font_config.upgrade() { + *self.shaper.borrow_mut() = + new_shaper(&*font_config.config.borrow(), &self.handles.borrow())?; + } } Ok(loaded) } @@ -152,10 +154,13 @@ impl LoadedFont { if let Some(raster) = rasterizers.get(&fallback) { raster.rasterize_glyph(glyph_pos, self.font_size, self.dpi) } else { - let raster = new_rasterizer( - FontRasterizerSelection::get_default(), - &(self.handles.borrow())[fallback], - )?; + let raster_selection = self + .font_config + .upgrade() + .map_or(FontRasterizerSelection::default(), |c| { + c.config.borrow().font_rasterizer + }); + let raster = new_rasterizer(raster_selection, &(self.handles.borrow())[fallback])?; let result = raster.rasterize_glyph(glyph_pos, self.font_size, self.dpi); rasterizers.insert(fallback, raster); result @@ -168,7 +173,7 @@ struct FontConfigInner { metrics: RefCell>, dpi_scale: RefCell, font_scale: RefCell, - config_generation: RefCell, + config: RefCell, locator: Box, font_dirs: RefCell, built_in: RefCell, @@ -181,35 +186,42 @@ pub struct FontConfiguration { impl FontConfigInner { /// Create a new empty configuration - pub fn new() -> anyhow::Result { - let locator = new_locator(FontLocatorSelection::get_default()); - let config = configuration(); + pub fn new(config: Option) -> anyhow::Result { + let config = config.unwrap_or_else(|| configuration()); + let locator = new_locator(config.font_locator); Ok(Self { fonts: RefCell::new(HashMap::new()), locator, metrics: RefCell::new(None), font_scale: RefCell::new(1.0), dpi_scale: RefCell::new(1.0), - config_generation: RefCell::new(config.generation()), + config: RefCell::new(config.clone()), font_dirs: RefCell::new(FontDatabase::with_font_dirs(&config)?), built_in: RefCell::new(FontDatabase::with_built_in()?), }) } + fn config_changed(&self, config: &ConfigHandle) -> anyhow::Result<()> { + let mut fonts = self.fonts.borrow_mut(); + *self.config.borrow_mut() = config.clone(); + // Config was reloaded, invalidate our caches + fonts.clear(); + self.metrics.borrow_mut().take(); + *self.font_dirs.borrow_mut() = FontDatabase::with_font_dirs(config)?; + Ok(()) + } + /// Given a text style, load (with caching) the font that best /// matches according to the fontconfig pattern. fn resolve_font(&self, myself: &Rc, style: &TextStyle) -> anyhow::Result> { - let mut fonts = self.fonts.borrow_mut(); - - let config = configuration(); - let current_generation = config.generation(); - if current_generation != *self.config_generation.borrow() { - // Config was reloaded, invalidate our caches - fonts.clear(); - self.metrics.borrow_mut().take(); - *self.font_dirs.borrow_mut() = FontDatabase::with_font_dirs(&config)?; - *self.config_generation.borrow_mut() = current_generation; + let global_config = configuration(); + let current_generation = global_config.generation(); + if current_generation != self.config.borrow().generation() { + self.config_changed(&global_config)?; } + let config = self.config.borrow(); + + let mut fonts = self.fonts.borrow_mut(); if let Some(entry) = fonts.get(style) { return Ok(Rc::clone(entry)); @@ -284,9 +296,8 @@ impl FontConfigInner { } } - let shaper = new_shaper(FontShaperSelection::get_default(), &handles)?; + let shaper = new_shaper(&*config, &handles)?; - let config = configuration(); let font_size = config.font_size * *self.font_scale.borrow(); let dpi = *self.dpi_scale.borrow() as u32 * config.dpi.unwrap_or(::window::DEFAULT_DPI) as u32; @@ -326,7 +337,7 @@ impl FontConfigInner { /// Returns the baseline font specified in the configuration pub fn default_font(&self, myself: &Rc) -> anyhow::Result> { - self.resolve_font(myself, &configuration().font) + self.resolve_font(myself, &self.config.borrow().font) } pub fn get_font_scale(&self) -> f64 { @@ -392,11 +403,15 @@ impl FontConfigInner { impl FontConfiguration { /// Create a new empty configuration - pub fn new() -> anyhow::Result { - let inner = Rc::new(FontConfigInner::new()?); + pub fn new(config: Option) -> anyhow::Result { + let inner = Rc::new(FontConfigInner::new(config)?); Ok(Self { inner }) } + pub fn config_changed(&self, config: &ConfigHandle) -> anyhow::Result<()> { + self.inner.config_changed(config) + } + /// Given a text style, load (with caching) the font that best /// matches according to the fontconfig pattern. pub fn resolve_font(&self, style: &TextStyle) -> anyhow::Result> { diff --git a/wezterm-font/src/shaper/allsorts.rs b/wezterm-font/src/shaper/allsorts.rs index ae796ae94..0c873d818 100644 --- a/wezterm-font/src/shaper/allsorts.rs +++ b/wezterm-font/src/shaper/allsorts.rs @@ -452,7 +452,7 @@ pub struct AllsortsShaper { } impl AllsortsShaper { - pub fn new(handles: &[FontDataHandle]) -> anyhow::Result { + pub fn new(_: &config::ConfigHandle, handles: &[FontDataHandle]) -> anyhow::Result { let mut fonts = vec![]; let mut success = false; for handle in handles { diff --git a/wezterm-font/src/shaper/harfbuzz.rs b/wezterm-font/src/shaper/harfbuzz.rs index 4116625cb..385891ec8 100644 --- a/wezterm-font/src/shaper/harfbuzz.rs +++ b/wezterm-font/src/shaper/harfbuzz.rs @@ -4,7 +4,7 @@ use crate::locator::FontDataHandle; use crate::shaper::{FallbackIdx, FontMetrics, FontShaper, GlyphInfo}; use crate::units::*; use anyhow::anyhow; -use config::configuration; +use config::ConfigHandle; use log::error; use ordered_float::NotNan; use std::cell::{RefCell, RefMut}; @@ -66,6 +66,7 @@ pub struct HarfbuzzShaper { fonts: Vec>>, lib: ftwrap::Library, metrics: RefCell>, + config: ConfigHandle, } #[derive(Error, Debug)] @@ -102,7 +103,7 @@ fn is_question_string(s: &str) -> bool { } impl HarfbuzzShaper { - pub fn new(handles: &[FontDataHandle]) -> anyhow::Result { + pub fn new(config: &ConfigHandle, handles: &[FontDataHandle]) -> anyhow::Result { let lib = ftwrap::Library::new()?; let handles = handles.to_vec(); let mut fonts = vec![]; @@ -114,6 +115,7 @@ impl HarfbuzzShaper { handles, lib, metrics: RefCell::new(HashMap::new()), + config: config.clone(), }) } @@ -149,7 +151,7 @@ impl HarfbuzzShaper { dpi: u32, no_glyphs: &mut Vec, ) -> anyhow::Result> { - let config = configuration(); + let config = &self.config; let features: Vec = config .harfbuzz_features .iter() @@ -451,7 +453,9 @@ mod test { .unwrap() .clone(); - let shaper = HarfbuzzShaper::new(&[handle]).unwrap(); + let config = config::configuration(); + + let shaper = HarfbuzzShaper::new(&config, &[handle]).unwrap(); { let mut no_glyphs = vec![]; let info = shaper.shape("abc", 10., 72, &mut no_glyphs).unwrap(); diff --git a/wezterm-font/src/shaper/mod.rs b/wezterm-font/src/shaper/mod.rs index c5f875351..7b1b5f303 100644 --- a/wezterm-font/src/shaper/mod.rs +++ b/wezterm-font/src/shaper/mod.rs @@ -74,11 +74,15 @@ pub trait FontShaper { pub use config::FontShaperSelection; pub fn new_shaper( - shaper: FontShaperSelection, + config: &config::ConfigHandle, handles: &[FontDataHandle], ) -> anyhow::Result> { - match shaper { - FontShaperSelection::Harfbuzz => Ok(Box::new(harfbuzz::HarfbuzzShaper::new(handles)?)), - FontShaperSelection::Allsorts => Ok(Box::new(allsorts::AllsortsShaper::new(handles)?)), + match config.font_shaper { + FontShaperSelection::Harfbuzz => { + Ok(Box::new(harfbuzz::HarfbuzzShaper::new(config, handles)?)) + } + FontShaperSelection::Allsorts => { + Ok(Box::new(allsorts::AllsortsShaper::new(config, handles)?)) + } } } diff --git a/wezterm-gui-subcommands/src/lib.rs b/wezterm-gui-subcommands/src/lib.rs index fc7abab04..d56e47f80 100644 --- a/wezterm-gui-subcommands/src/lib.rs +++ b/wezterm-gui-subcommands/src/lib.rs @@ -1,6 +1,3 @@ -use config::FontLocatorSelection; -use config::FontRasterizerSelection; -use config::FontShaperSelection; use config::{FrontEndSelection, SshParameters}; use std::ffi::OsString; use structopt::StructOpt; @@ -32,27 +29,6 @@ pub struct StartCommand { )] pub front_end: Option, - #[structopt( - long = "font-locator", - possible_values = &FontLocatorSelection::variants(), - case_insensitive = true - )] - pub font_locator: Option, - - #[structopt( - long = "font-rasterizer", - possible_values = &FontRasterizerSelection::variants(), - case_insensitive = true - )] - pub font_rasterizer: Option, - - #[structopt( - long = "font-shaper", - possible_values = &FontShaperSelection::variants(), - case_insensitive = true - )] - pub font_shaper: Option, - /// If true, do not connect to domains marked as connect_automatically /// in your wezterm.toml configuration file. #[structopt(long = "no-auto-connect")] diff --git a/wezterm-gui/src/gui/shapecache.rs b/wezterm-gui/src/gui/shapecache.rs index bc10a3981..096be9053 100644 --- a/wezterm-gui/src/gui/shapecache.rs +++ b/wezterm-gui/src/gui/shapecache.rs @@ -270,7 +270,7 @@ mod test { .filter_level(log::LevelFilter::Trace) .try_init(); - let fonts = Rc::new(FontConfiguration::new().unwrap()); + let fonts = Rc::new(FontConfiguration::new(None).unwrap()); let render_metrics = RenderMetrics::new(&fonts).unwrap(); let mut glyph_cache = GlyphCache::new_in_memory(&fonts, 128, &render_metrics).unwrap(); diff --git a/wezterm-gui/src/gui/termwindow.rs b/wezterm-gui/src/gui/termwindow.rs index 6072d3647..a01a54d45 100644 --- a/wezterm-gui/src/gui/termwindow.rs +++ b/wezterm-gui/src/gui/termwindow.rs @@ -222,6 +222,7 @@ pub struct TabState { pub struct TermWindow { pub window: Option, pub config: ConfigHandle, + pub config_overrides: serde_json::Value, /// When we most recently received keyboard focus focused: Option, fonts: Rc, @@ -720,6 +721,7 @@ impl WindowCallbacks for TermWindow { let guts = Box::new(Self { window: None, config: self.config.clone(), + config_overrides: self.config_overrides.clone(), window_background: self.window_background.clone(), palette: None, focused: None, @@ -935,7 +937,7 @@ impl TermWindow { let window_background = load_background_image(&config); - let fontconfig = Rc::new(FontConfiguration::new()?); + let fontconfig = Rc::new(FontConfiguration::new(Some(config.clone()))?); let mux = Mux::get().expect("to be main thread with mux running"); let size = match mux.get_active_tab_for_window(mux_window_id) { Some(tab) => tab.get_size(), @@ -994,6 +996,7 @@ impl TermWindow { window: None, window_background, config: config.clone(), + config_overrides: serde_json::Value::default(), palette: None, focused: None, mux_window_id, @@ -1304,8 +1307,22 @@ impl TermWindow { self.palette.as_ref().unwrap() } - fn config_was_reloaded(&mut self) { - let config = configuration(); + pub fn config_was_reloaded(&mut self) { + log::debug!( + "config was reloaded, overrides: {:?}", + self.config_overrides + ); + let config = match config::overridden_config(&self.config_overrides) { + Ok(config) => config, + Err(err) => { + log::error!( + "Failed to apply config overrides to window: {:#}: {:?}", + err, + self.config_overrides + ); + configuration() + } + }; self.config = config.clone(); self.palette.take(); @@ -1328,6 +1345,10 @@ impl TermWindow { self.leader_is_down = None; let dimensions = self.dimensions; let cell_dims = self.current_cell_dimensions(); + + if let Err(err) = self.fonts.config_changed(&config) { + log::error!("Failed to load font configuration: {:#}", err); + } self.apply_scale_change(&dimensions, self.fonts.get_font_scale()); self.apply_dimensions(&dimensions, Some(cell_dims)); if let Some(window) = self.window.as_ref() { @@ -2412,7 +2433,7 @@ impl TermWindow { fn reset_font_and_window_size(&mut self) -> anyhow::Result<()> { let config = &self.config; let size = config.initial_size(); - let fontconfig = Rc::new(FontConfiguration::new()?); + let fontconfig = Rc::new(FontConfiguration::new(Some(config.clone()))?); let render_metrics = RenderMetrics::new(&fontconfig)?; let terminal_size = PtySize { diff --git a/wezterm-gui/src/main.rs b/wezterm-gui/src/main.rs index 2ce40faeb..1698213bc 100644 --- a/wezterm-gui/src/main.rs +++ b/wezterm-gui/src/main.rs @@ -289,14 +289,6 @@ fn run_terminal_gui(config: config::ConfigHandle, opts: StartCommand) -> anyhow: } let run = move || -> anyhow::Result<()> { - opts.font_locator - .unwrap_or(config.font_locator) - .set_default(); - opts.font_shaper.unwrap_or(config.font_shaper).set_default(); - opts.font_rasterizer - .unwrap_or(config.font_rasterizer) - .set_default(); - let need_builder = !opts.prog.is_empty() || opts.cwd.is_some(); let cmd = if need_builder { diff --git a/wezterm-gui/src/scripting/guiwin.rs b/wezterm-gui/src/scripting/guiwin.rs index 154260362..decd44043 100644 --- a/wezterm-gui/src/scripting/guiwin.rs +++ b/wezterm-gui/src/scripting/guiwin.rs @@ -4,6 +4,7 @@ use super::pane::PaneObject; use crate::gui::TermWindow; use anyhow::anyhow; use config::keyassignment::KeyAssignment; +use luahelper::JsonLua; use mlua::{UserData, UserDataMethods}; use mux::window::WindowId as MuxWindowId; use window::WindowOps; @@ -59,5 +60,23 @@ impl UserData for GuiWin { this.with_term_window(move |term_window, _ops| Ok((*term_window.config).clone())) .await }); + methods.add_async_method("get_config_overrides", |_, this, _: ()| async move { + this.with_term_window(move |term_window, _ops| { + let wrap = JsonLua(term_window.config_overrides.clone()); + Ok(wrap) + }) + .await + }); + methods.add_async_method( + "set_config_overrides", + |_, this, value: JsonLua| async move { + this.with_term_window(move |term_window, _ops| { + term_window.config_overrides = value.0.clone(); + term_window.config_was_reloaded(); + Ok(()) + }) + .await + }, + ); } }