1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-22 21:01:36 +03:00

config: cut over to wezterm-dynamic

Avoid using serde for mapping between Lua and Rust for the `Config`
struct.

This improves the build speed of the config crate by 2x; it goes down
from 30 seconds to 9 seconds on my 5950x.
This commit is contained in:
Wez Furlong 2022-05-14 08:00:03 -07:00
parent 24c6830345
commit f587cac145
37 changed files with 861 additions and 873 deletions

10
Cargo.lock generated
View File

@ -672,6 +672,7 @@ dependencies = [
"umask",
"unicode-segmentation",
"wezterm-bidi",
"wezterm-dynamic",
"wezterm-input-types",
"wezterm-ssh",
"wezterm-term",
@ -2024,6 +2025,7 @@ dependencies = [
"serde_json",
"strsim 0.10.0",
"thiserror",
"wezterm-dynamic",
]
[[package]]
@ -2287,6 +2289,7 @@ dependencies = [
"thiserror",
"unicode-segmentation",
"url",
"wezterm-dynamic",
"wezterm-ssh",
"wezterm-term",
"winapi 0.3.9",
@ -4086,6 +4089,7 @@ dependencies = [
"vtparse",
"wezterm-bidi",
"wezterm-color-types",
"wezterm-dynamic",
"winapi 0.3.9",
]
@ -4654,6 +4658,7 @@ dependencies = [
"k9",
"log",
"serde",
"wezterm-dynamic",
]
[[package]]
@ -4703,8 +4708,10 @@ dependencies = [
name = "wezterm-dynamic"
version = "0.1.0"
dependencies = [
"log",
"maplit",
"ordered-float",
"strsim 0.10.0",
"thiserror",
"wezterm-dynamic-derive",
]
@ -4817,6 +4824,7 @@ dependencies = [
"walkdir",
"wezterm-bidi",
"wezterm-client",
"wezterm-dynamic",
"wezterm-font",
"wezterm-gui-subcommands",
"wezterm-mux-server-impl",
@ -4845,6 +4853,7 @@ dependencies = [
"euclid",
"lazy_static",
"serde",
"wezterm-dynamic",
]
[[package]]
@ -4953,6 +4962,7 @@ dependencies = [
"unicode-width",
"url",
"wezterm-bidi",
"wezterm-dynamic",
]
[[package]]

View File

@ -14,6 +14,7 @@ use_serde = ["serde"]
[dependencies]
log = "0.4"
serde = {version="1.0", features = ["derive"], optional=true}
wezterm-dynamic = { path = "../wezterm-dynamic" }
[dev-dependencies]
k9 = "0.11.0"

View File

@ -3,6 +3,7 @@ use level_stack::{LevelStack, Override};
use log::trace;
use std::borrow::Cow;
use std::ops::Range;
use wezterm_dynamic::{FromDynamic, ToDynamic};
mod bidi_brackets;
mod bidi_class;
@ -20,7 +21,7 @@ use serde::{Deserialize, Serialize};
/// Placeholder codepoint index that corresponds to NO_LEVEL
const DELETED: usize = usize::max_value();
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromDynamic, ToDynamic)]
#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
pub enum ParagraphDirectionHint {
LeftToRight,

View File

@ -41,6 +41,7 @@ termwiz = { path = "../termwiz", features=["use_serde"] }
toml = "0.5"
umask = { path = "../umask" }
unicode-segmentation = "1.8"
wezterm-dynamic = { path = "../wezterm-dynamic" }
wezterm-bidi = { path = "../bidi", features=["use_serde"] }
wezterm-input-types = { path = "../wezterm-input-types" }
wezterm-ssh = { path = "../wezterm-ssh" }

View File

@ -1,7 +1,7 @@
use luahelper::impl_lua_conversion;
use serde::{Deserialize, Serialize};
use luahelper::impl_lua_conversion_dynamic;
use wezterm_dynamic::{FromDynamic, ToDynamic};
#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
#[derive(Debug, Copy, Clone, FromDynamic, ToDynamic)]
pub enum Interpolation {
Linear,
Basis,
@ -14,7 +14,7 @@ impl Default for Interpolation {
}
}
#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
#[derive(Debug, Copy, Clone, FromDynamic, ToDynamic)]
pub enum BlendMode {
Rgb,
LinearRgb,
@ -28,7 +28,7 @@ impl Default for BlendMode {
}
}
#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
#[derive(Debug, Copy, Clone, FromDynamic, ToDynamic)]
pub enum GradientOrientation {
Horizontal,
Vertical,
@ -45,7 +45,7 @@ impl Default for GradientOrientation {
}
}
#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
#[derive(Debug, Copy, Clone, FromDynamic, ToDynamic)]
pub enum GradientPreset {
Blues,
BrBg,
@ -132,34 +132,33 @@ impl GradientPreset {
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[derive(Debug, Clone, FromDynamic, ToDynamic)]
pub struct Gradient {
#[serde(default)]
#[dynamic(default)]
pub orientation: GradientOrientation,
#[serde(default)]
#[dynamic(default)]
pub colors: Vec<String>,
#[serde(default)]
#[dynamic(default)]
pub preset: Option<GradientPreset>,
#[serde(default)]
#[dynamic(default)]
pub interpolation: Interpolation,
#[serde(default)]
#[dynamic(default)]
pub blend: BlendMode,
#[serde(default)]
#[dynamic(default)]
pub segment_size: Option<usize>,
#[serde(default)]
#[dynamic(default)]
pub segment_smoothness: Option<f64>,
#[serde(default)]
#[dynamic(default)]
pub noise: Option<usize>,
}
impl_lua_conversion!(Gradient);
impl_lua_conversion_dynamic!(Gradient);
impl Gradient {
pub fn build(&self) -> anyhow::Result<colorgrad::Gradient> {

View File

@ -1,7 +1,7 @@
use crate::*;
use wezterm_dynamic::{FromDynamic, ToDynamic};
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function>
#[derive(Debug, Deserialize, Serialize, Clone, Copy)]
#[derive(Debug, Clone, Copy, FromDynamic, ToDynamic)]
pub enum EasingFunction {
Linear,
CubicBezier(f32, f32, f32, f32),
@ -11,7 +11,6 @@ pub enum EasingFunction {
EaseOut,
Constant,
}
impl_lua_conversion!(EasingFunction);
impl EasingFunction {
pub fn evaluate_at_position(&self, position: f32) -> f32 {
@ -40,27 +39,25 @@ impl Default for EasingFunction {
}
}
#[derive(Default, Debug, Deserialize, Serialize, Clone)]
#[derive(Default, Debug, Clone, FromDynamic, ToDynamic)]
pub struct VisualBell {
#[serde(default)]
#[dynamic(default)]
pub fade_in_duration_ms: u64,
#[serde(default)]
#[dynamic(default)]
pub fade_in_function: EasingFunction,
#[serde(default)]
#[dynamic(default)]
pub fade_out_duration_ms: u64,
#[serde(default)]
#[dynamic(default)]
pub fade_out_function: EasingFunction,
#[serde(default)]
#[dynamic(default)]
pub target: VisualBellTarget,
}
impl_lua_conversion!(VisualBell);
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, FromDynamic, ToDynamic)]
pub enum VisualBellTarget {
BackgroundColor,
CursorColor,
}
impl_lua_conversion!(VisualBellTarget);
impl Default for VisualBellTarget {
fn default() -> VisualBellTarget {
@ -68,12 +65,11 @@ impl Default for VisualBellTarget {
}
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Clone, FromDynamic, ToDynamic)]
pub enum AudibleBell {
SystemBeep,
Disabled,
}
impl_lua_conversion!(AudibleBell);
impl Default for AudibleBell {
fn default() -> AudibleBell {

View File

@ -1,17 +1,19 @@
use crate::lua::{format_as_escapes, FormatItem};
use crate::*;
use luahelper::impl_lua_conversion;
use luahelper::impl_lua_conversion_dynamic;
use std::convert::TryFrom;
use std::str::FromStr;
use termwiz::cell::CellAttributes;
pub use termwiz::color::{ColorSpec, RgbColor, SrgbaTuple};
use wezterm_dynamic::{FromDynamic, FromDynamicOptions, ToDynamic, Value};
#[derive(Debug, Copy, Deserialize, Serialize, Clone)]
#[derive(Debug, Copy, Clone, FromDynamic, ToDynamic)]
pub struct HsbTransform {
#[serde(default = "default_one_point_oh")]
#[dynamic(default = "default_one_point_oh")]
pub hue: f32,
#[serde(default = "default_one_point_oh")]
#[dynamic(default = "default_one_point_oh")]
pub saturation: f32,
#[serde(default = "default_one_point_oh")]
#[dynamic(default = "default_one_point_oh")]
pub brightness: f32,
}
@ -25,30 +27,56 @@ impl Default for HsbTransform {
}
}
fn de_indexed<'de, D>(deserializer: D) -> Result<HashMap<u8, RgbaColor>, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Wrap(HashMap<String, RgbaColor>);
let Wrap(map) = Wrap::deserialize(deserializer)?;
struct IndexedMap(HashMap<String, RgbaColor>);
Ok(map
.into_iter()
.filter_map(|(k, v)| match k.parse::<u8>() {
Ok(n) if n >= 16 => Some((n, v)),
_ => {
log::warn!("Ignoring invalid color key {}", k);
None
}
})
.collect())
impl ToDynamic for IndexedMap {
fn to_dynamic(&self) -> Value {
self.0.to_dynamic()
}
}
#[derive(Default, Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq, Hash)]
#[serde(try_from = "String", into = "String")]
impl FromDynamic for IndexedMap {
fn from_dynamic(
value: &Value,
options: FromDynamicOptions,
) -> Result<Self, wezterm_dynamic::Error> {
let inner = <HashMap<String, RgbaColor>>::from_dynamic(value, options)?;
Ok(Self(inner))
}
}
impl From<&HashMap<u8, RgbaColor>> for IndexedMap {
fn from(map: &HashMap<u8, RgbaColor>) -> IndexedMap {
IndexedMap(
map.iter()
.map(|(k, v)| (k.to_string(), v.clone()))
.collect(),
)
}
}
impl TryFrom<IndexedMap> for HashMap<u8, RgbaColor> {
type Error = String;
fn try_from(map: IndexedMap) -> Result<HashMap<u8, RgbaColor>, String> {
Ok(map
.0
.into_iter()
.filter_map(|(k, v)| match k.parse::<u8>() {
Ok(n) if n >= 16 => Some((n, v)),
_ => {
log::warn!("Ignoring invalid color key {}", k);
None
}
})
.collect())
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, FromDynamic, ToDynamic)]
#[dynamic(try_from = "String", into = "String")]
pub struct RgbaColor {
#[serde(flatten)]
#[dynamic(flatten)]
color: SrgbaTuple,
}
@ -67,6 +95,12 @@ impl std::ops::Deref for RgbaColor {
}
}
impl Into<String> for &RgbaColor {
fn into(self) -> String {
self.color.to_rgb_string()
}
}
impl Into<String> for RgbaColor {
fn into(self) -> String {
self.color.to_rgb_string()
@ -89,7 +123,7 @@ impl std::convert::TryFrom<String> for RgbaColor {
}
}
#[derive(Default, Debug, Deserialize, Serialize, Clone)]
#[derive(Default, Debug, Clone, FromDynamic, ToDynamic)]
pub struct Palette {
/// The text color to use when the attributes are reset to default
pub foreground: Option<RgbaColor>,
@ -109,7 +143,7 @@ pub struct Palette {
pub brights: Option<[RgbaColor; 8]>,
/// A map for setting arbitrary colors ranging from 16 to 256 in the color
/// palette
#[serde(default, deserialize_with = "de_indexed")]
#[dynamic(default, try_from = "IndexedMap", into = "IndexedMap")]
pub indexed: HashMap<u8, RgbaColor>,
/// Configure the colors and styling of the tab bar
pub tab_bar: Option<TabBarColors>,
@ -124,7 +158,7 @@ pub struct Palette {
/// The color to use for the cursor when a dead key or leader state is active
pub compose_cursor: Option<RgbaColor>,
}
impl_lua_conversion!(Palette);
impl_lua_conversion_dynamic!(Palette);
impl From<Palette> for wezterm_term::color::ColorPalette {
fn from(cfg: Palette) -> wezterm_term::color::ColorPalette {
@ -164,26 +198,25 @@ impl From<Palette> for wezterm_term::color::ColorPalette {
}
/// Specify the text styling for a tab in the tab bar
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
#[derive(Debug, Clone, Default, FromDynamic, ToDynamic)]
pub struct TabBarColor {
/// Specifies the intensity attribute for the tab title text
#[serde(default)]
#[dynamic(default)]
pub intensity: wezterm_term::Intensity,
/// Specifies the underline attribute for the tab title text
#[serde(default)]
#[dynamic(default)]
pub underline: wezterm_term::Underline,
/// Specifies the italic attribute for the tab title text
#[serde(default)]
#[dynamic(default)]
pub italic: bool,
/// Specifies the strikethrough attribute for the tab title text
#[serde(default)]
#[dynamic(default)]
pub strikethrough: bool,
/// The background color for the tab
pub bg_color: RgbColor,
/// The forgeground/text color for the tab
pub fg_color: RgbColor,
}
impl_lua_conversion!(TabBarColor);
impl TabBarColor {
pub fn as_cell_attributes(&self) -> CellAttributes {
@ -201,39 +234,38 @@ impl TabBarColor {
/// Specifies the colors to use for the tab bar portion of the UI.
/// These are not part of the terminal model and cannot be updated
/// in the same way that the dynamic color schemes are.
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Clone, FromDynamic, ToDynamic)]
pub struct TabBarColors {
/// The background color for the tab bar
#[serde(default = "default_background")]
#[dynamic(default = "default_background")]
pub background: RgbColor,
/// Styling for the active tab
#[serde(default = "default_active_tab")]
#[dynamic(default = "default_active_tab")]
pub active_tab: TabBarColor,
/// Styling for other inactive tabs
#[serde(default = "default_inactive_tab")]
#[dynamic(default = "default_inactive_tab")]
pub inactive_tab: TabBarColor,
/// Styling for an inactive tab with a mouse hovering
#[serde(default = "default_inactive_tab_hover")]
#[dynamic(default = "default_inactive_tab_hover")]
pub inactive_tab_hover: TabBarColor,
/// Styling for the new tab button
#[serde(default = "default_inactive_tab")]
#[dynamic(default = "default_inactive_tab")]
pub new_tab: TabBarColor,
/// Styling for the new tab button with a mouse hovering
#[serde(default = "default_inactive_tab_hover")]
#[dynamic(default = "default_inactive_tab_hover")]
pub new_tab_hover: TabBarColor,
#[serde(default = "default_inactive_tab_edge")]
#[dynamic(default = "default_inactive_tab_edge")]
pub inactive_tab_edge: RgbaColor,
#[serde(default = "default_inactive_tab_edge_hover")]
#[dynamic(default = "default_inactive_tab_edge_hover")]
pub inactive_tab_edge_hover: RgbaColor,
}
impl_lua_conversion!(TabBarColors);
fn default_background() -> RgbColor {
RgbColor::new_8bpc(0x33, 0x33, 0x33)
@ -285,11 +317,11 @@ impl Default for TabBarColors {
}
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Clone, FromDynamic, ToDynamic)]
pub struct TabBarStyle {
#[serde(default = "default_new_tab")]
#[dynamic(default = "default_new_tab")]
pub new_tab: String,
#[serde(default = "default_new_tab")]
#[dynamic(default = "default_new_tab")]
pub new_tab_hover: String,
}
@ -306,32 +338,32 @@ fn default_new_tab() -> String {
format_as_escapes(vec![FormatItem::Text(" + ".to_string())]).unwrap()
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Clone, FromDynamic, ToDynamic)]
pub struct WindowFrameConfig {
#[serde(default = "default_inactive_titlebar_bg")]
#[dynamic(default = "default_inactive_titlebar_bg")]
pub inactive_titlebar_bg: RgbColor,
#[serde(default = "default_active_titlebar_bg")]
#[dynamic(default = "default_active_titlebar_bg")]
pub active_titlebar_bg: RgbColor,
#[serde(default = "default_inactive_titlebar_fg")]
#[dynamic(default = "default_inactive_titlebar_fg")]
pub inactive_titlebar_fg: RgbColor,
#[serde(default = "default_active_titlebar_fg")]
#[dynamic(default = "default_active_titlebar_fg")]
pub active_titlebar_fg: RgbColor,
#[serde(default = "default_inactive_titlebar_border_bottom")]
#[dynamic(default = "default_inactive_titlebar_border_bottom")]
pub inactive_titlebar_border_bottom: RgbColor,
#[serde(default = "default_active_titlebar_border_bottom")]
#[dynamic(default = "default_active_titlebar_border_bottom")]
pub active_titlebar_border_bottom: RgbColor,
#[serde(default = "default_button_fg")]
#[dynamic(default = "default_button_fg")]
pub button_fg: RgbColor,
#[serde(default = "default_button_bg")]
#[dynamic(default = "default_button_bg")]
pub button_bg: RgbColor,
#[serde(default = "default_button_hover_fg")]
#[dynamic(default = "default_button_hover_fg")]
pub button_hover_fg: RgbColor,
#[serde(default = "default_button_hover_bg")]
#[dynamic(default = "default_button_hover_bg")]
pub button_hover_bg: RgbColor,
#[serde(default)]
#[dynamic(default)]
pub font: Option<TextStyle>,
#[serde(default)]
#[dynamic(default)]
pub font_size: Option<f64>,
}
@ -394,9 +426,20 @@ fn default_button_bg() -> RgbColor {
RgbColor::new_8bpc(0x33, 0x33, 0x33)
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Clone, FromDynamic, ToDynamic)]
pub struct ColorSchemeFile {
/// The color palette
pub colors: Palette,
}
impl_lua_conversion!(ColorSchemeFile);
impl ColorSchemeFile {
pub fn from_toml_value(value: &toml::Value) -> anyhow::Result<Self> {
Self::from_dynamic(&crate::toml_to_dynamic(value), Default::default())
.map_err(|e| anyhow::anyhow!("{}", e))
}
pub fn from_toml_str(s: &str) -> anyhow::Result<Self> {
let scheme: toml::Value = toml::from_str(s)?;
ColorSchemeFile::from_toml_value(&scheme)
}
}

View File

@ -14,18 +14,18 @@ use crate::keys::{Key, LeaderKey, Mouse};
use crate::lua::make_lua_context;
use crate::ssh::{SshBackend, SshDomain};
use crate::tls::{TlsDomainClient, TlsDomainServer};
use crate::units::{de_pixels, Dimension};
use crate::units::Dimension;
use crate::unix::UnixDomain;
use crate::wsl::WslDomain;
use crate::{
de_number, de_vec_table, default_config_with_overrides_applied, default_one_point_oh,
default_one_point_oh_f64, default_true, KeyMapPreference, LoadedConfig, CONFIG_DIR,
CONFIG_FILE_OVERRIDE, CONFIG_OVERRIDES, CONFIG_SKIP, HOME_DIR,
default_config_with_overrides_applied, default_one_point_oh, default_one_point_oh_f64,
default_true, KeyMapPreference, LoadedConfig, CONFIG_DIR, CONFIG_FILE_OVERRIDE,
CONFIG_OVERRIDES, CONFIG_SKIP, HOME_DIR,
};
use anyhow::Context;
use luahelper::impl_lua_conversion;
use luahelper::impl_lua_conversion_dynamic;
use mlua::FromLua;
use portable_pty::{CommandBuilder, PtySize};
use serde::{Deserialize, Deserializer, Serialize};
use std::collections::HashMap;
use std::ffi::OsStr;
use std::io::Read;
@ -35,61 +35,62 @@ use std::time::Duration;
use termwiz::hyperlink;
use termwiz::surface::CursorShape;
use wezterm_bidi::ParagraphDirectionHint;
use wezterm_dynamic::{FromDynamic, ToDynamic};
use wezterm_input_types::{Modifiers, WindowDecorations};
#[derive(Debug, Serialize, Deserialize, Clone)]
#[derive(Debug, Clone, FromDynamic, ToDynamic)]
pub struct Config {
/// The font size, measured in points
#[serde(default = "default_font_size", deserialize_with = "de_number")]
#[dynamic(default = "default_font_size")]
pub font_size: f64,
#[serde(default = "default_one_point_oh_f64")]
#[dynamic(default = "default_one_point_oh_f64")]
pub line_height: f64,
#[serde(default)]
#[dynamic(default)]
pub allow_square_glyphs_to_overflow_width: AllowSquareGlyphOverflow,
#[serde(default)]
#[dynamic(default)]
pub window_decorations: WindowDecorations,
/// When using FontKitXXX font systems, a set of directories to
/// search ahead of the standard font locations for fonts.
/// Relative paths are taken to be relative to the directory
/// from which the config was loaded.
#[serde(default)]
#[dynamic(default)]
pub font_dirs: Vec<PathBuf>,
#[serde(default)]
#[dynamic(default)]
pub color_scheme_dirs: Vec<PathBuf>,
/// The DPI to assume
pub dpi: Option<f64>,
/// The baseline font to use
#[serde(default)]
#[dynamic(default)]
pub font: TextStyle,
/// An optional set of style rules to select the font based
/// on the cell attributes
#[serde(default)]
#[dynamic(default)]
pub font_rules: Vec<StyleRule>,
/// When true (the default), PaletteIndex 0-7 are shifted to
/// bright when the font intensity is bold. The brightening
/// doesn't apply to text that is the default color.
#[serde(default = "default_true")]
#[dynamic(default = "default_true")]
pub bold_brightens_ansi_colors: bool,
/// The color palette
pub colors: Option<Palette>,
#[serde(default)]
#[dynamic(default)]
pub window_frame: WindowFrameConfig,
#[serde(default)]
#[dynamic(default)]
pub tab_bar_style: TabBarStyle,
#[serde(skip)]
#[dynamic(default)]
pub resolved_palette: Palette,
/// Use a named color scheme rather than the palette specified
@ -97,11 +98,11 @@ pub struct Config {
pub color_scheme: Option<String>,
/// Named color schemes
#[serde(default)]
#[dynamic(default)]
pub color_schemes: HashMap<String, Palette>,
/// How many lines of scrollback you want to retain
#[serde(default = "default_scrollback_lines")]
#[dynamic(default = "default_scrollback_lines")]
pub scrollback_lines: usize,
/// If no `prog` is specified on the command line, use this
@ -118,7 +119,7 @@ pub struct Config {
/// as the positional arguments to that command.
pub default_prog: Option<Vec<String>>,
#[serde(default = "default_gui_startup_args")]
#[dynamic(default = "default_gui_startup_args")]
pub default_gui_startup_args: Vec<String>,
/// Specifies the default current working directory if none is specified
@ -126,48 +127,48 @@ pub struct Config {
/// info!)
pub default_cwd: Option<PathBuf>,
#[serde(default)]
#[dynamic(default)]
pub exit_behavior: ExitBehavior,
#[serde(default = "default_clean_exits")]
#[dynamic(default = "default_clean_exits")]
pub clean_exit_codes: Vec<u32>,
/// Specifies a map of environment variables that should be set
/// when spawning commands in the local domain.
/// This is not used when working with remote domains.
#[serde(default)]
#[dynamic(default)]
pub set_environment_variables: HashMap<String, String>,
/// Specifies the height of a new window, expressed in character cells.
#[serde(default = "default_initial_rows")]
#[dynamic(default = "default_initial_rows")]
pub initial_rows: u16,
#[serde(default = "default_true")]
#[dynamic(default = "default_true")]
pub enable_kitty_graphics: bool,
/// Specifies the width of a new window, expressed in character cells
#[serde(default = "default_initial_cols")]
#[dynamic(default = "default_initial_cols")]
pub initial_cols: u16,
#[serde(default = "default_hyperlink_rules")]
#[dynamic(default = "default_hyperlink_rules")]
pub hyperlink_rules: Vec<hyperlink::Rule>,
/// What to set the TERM variable to
#[serde(default = "default_term")]
#[dynamic(default = "default_term")]
pub term: String,
#[serde(default)]
#[dynamic(default)]
pub font_locator: FontLocatorSelection,
#[serde(default)]
#[dynamic(default)]
pub font_rasterizer: FontRasterizerSelection,
#[serde(default)]
#[dynamic(default)]
pub font_shaper: FontShaperSelection,
#[serde(default)]
#[dynamic(default)]
pub freetype_load_target: FreeTypeLoadTarget,
#[serde(default)]
#[dynamic(default)]
pub freetype_render_target: Option<FreeTypeLoadTarget>,
#[serde(default)]
#[dynamic(default)]
pub freetype_load_flags: FreeTypeLoadFlags,
/// Selects the freetype interpret version to use.
@ -209,32 +210,32 @@ pub struct Config {
/// # when using the Fira Code font
/// harfbuzz_features = ["zero"]
/// ```
#[serde(default = "default_harfbuzz_features")]
#[dynamic(default = "default_harfbuzz_features")]
pub harfbuzz_features: Vec<String>,
#[serde(default)]
#[dynamic(default)]
pub front_end: FrontEndSelection,
#[serde(default = "WslDomain::default_domains")]
#[dynamic(default = "WslDomain::default_domains")]
pub wsl_domains: Vec<WslDomain>,
/// The set of unix domains
#[serde(default = "UnixDomain::default_unix_domains")]
#[dynamic(default = "UnixDomain::default_unix_domains")]
pub unix_domains: Vec<UnixDomain>,
#[serde(default)]
#[dynamic(default)]
pub ssh_domains: Vec<SshDomain>,
#[serde(default)]
#[dynamic(default)]
pub ssh_backend: SshBackend,
/// When running in server mode, defines configuration for
/// each of the endpoints that we'll listen for connections
#[serde(default)]
#[dynamic(default)]
pub tls_servers: Vec<TlsDomainServer>,
/// The set of tls domains that we can connect to as a client
#[serde(default)]
#[dynamic(default)]
pub tls_clients: Vec<TlsDomainClient>,
/// Constrains the rate at which the multiplexer client will
@ -242,98 +243,95 @@ pub struct Config {
/// This helps to avoid saturating the link between the client
/// and server if the server is dumping a large amount of output
/// to the client.
#[serde(default = "default_ratelimit_line_prefetches_per_second")]
#[dynamic(default = "default_ratelimit_line_prefetches_per_second")]
pub ratelimit_mux_line_prefetches_per_second: u32,
/// The buffer size used by parse_buffered_data in the mux module.
/// This should not be too large, otherwise the processing cost
/// of applying a batch of actions to the terminal will be too
/// high and the user experience will be laggy and less responsive.
#[serde(default = "default_mux_output_parser_buffer_size")]
#[dynamic(default = "default_mux_output_parser_buffer_size")]
pub mux_output_parser_buffer_size: usize,
#[serde(default = "default_mux_env_remove", deserialize_with = "de_vec_table")]
#[dynamic(default = "default_mux_env_remove")]
pub mux_env_remove: Vec<String>,
#[serde(default, deserialize_with = "de_vec_table")]
#[dynamic(default)]
pub keys: Vec<Key>,
#[serde(default)]
#[dynamic(default)]
pub key_tables: HashMap<String, Vec<Key>>,
#[serde(
default = "default_bypass_mouse_reporting_modifiers",
deserialize_with = "crate::keys::de_modifiers"
)]
#[dynamic(default = "default_bypass_mouse_reporting_modifiers")]
pub bypass_mouse_reporting_modifiers: Modifiers,
#[serde(default)]
#[dynamic(default)]
pub debug_key_events: bool,
#[serde(default)]
#[dynamic(default)]
pub disable_default_key_bindings: bool,
pub leader: Option<LeaderKey>,
#[serde(default)]
#[dynamic(default)]
pub disable_default_quick_select_patterns: bool,
#[serde(default)]
#[dynamic(default)]
pub quick_select_patterns: Vec<String>,
#[serde(default = "default_alphabet")]
#[dynamic(default = "default_alphabet")]
pub quick_select_alphabet: String,
#[serde(default)]
#[dynamic(default)]
pub mouse_bindings: Vec<Mouse>,
#[serde(default)]
#[dynamic(default)]
pub disable_default_mouse_bindings: bool,
#[serde(default)]
#[dynamic(default)]
pub daemon_options: DaemonOptions,
#[serde(default)]
#[dynamic(default)]
pub send_composed_key_when_left_alt_is_pressed: bool,
#[serde(default = "default_true")]
#[dynamic(default = "default_true")]
pub send_composed_key_when_right_alt_is_pressed: bool,
#[serde(default)]
#[dynamic(default)]
pub treat_left_ctrlalt_as_altgr: bool,
/// If true, the `Backspace` and `Delete` keys generate `Delete` and `Backspace`
/// keypresses, respectively, rather than their normal keycodes.
/// On macOS the default for this is true because its Backspace key
/// is labeled as Delete and things are backwards.
#[serde(default = "default_swap_backspace_and_delete")]
#[dynamic(default = "default_swap_backspace_and_delete")]
pub swap_backspace_and_delete: bool,
/// If true, display the tab bar UI at the top of the window.
/// The tab bar shows the titles of the tabs and which is the
/// active tab. Clicking on a tab activates it.
#[serde(default = "default_true")]
#[dynamic(default = "default_true")]
pub enable_tab_bar: bool,
#[serde(default = "default_true")]
#[dynamic(default = "default_true")]
pub use_fancy_tab_bar: bool,
#[serde(default)]
#[dynamic(default)]
pub tab_bar_at_bottom: bool,
/// If true, tab bar titles are prefixed with the tab index
#[serde(default = "default_true")]
#[dynamic(default = "default_true")]
pub show_tab_index_in_tab_bar: bool,
/// If true, show_tab_index_in_tab_bar uses a zero-based index.
/// The default is false and the tab shows a one-based index.
#[serde(default)]
#[dynamic(default)]
pub tab_and_split_indices_are_zero_based: bool,
/// Specifies the maximum width that a tab can have in the
/// tab bar. Defaults to 16 glyphs in width.
#[serde(default = "default_tab_max_width")]
#[dynamic(default = "default_tab_max_width")]
pub tab_max_width: usize,
/// If true, hide the tab bar if the window only has a single tab.
#[serde(default)]
#[dynamic(default)]
pub hide_tab_bar_if_only_one_tab: bool,
#[serde(default)]
#[dynamic(default)]
pub enable_scroll_bar: bool,
/// If false, do not try to use a Wayland protocol connection
@ -341,23 +339,23 @@ pub struct Config {
/// This option is only considered on X11/Wayland systems and
/// has no effect on macOS or Windows.
/// The default is true.
#[serde(default)]
#[dynamic(default)]
pub enable_wayland: bool,
/// Whether to prefer EGL over other GL implementations.
/// EGL on Windows has jankier resize behavior than WGL (which
/// is used if EGL is unavailable), but EGL survives graphics
/// driver updates without breaking and losing your work.
#[serde(default = "default_prefer_egl")]
#[dynamic(default = "default_prefer_egl")]
pub prefer_egl: bool,
#[serde(default = "default_true")]
#[dynamic(default = "default_true")]
pub custom_block_glyphs: bool,
#[serde(default = "default_true")]
#[dynamic(default = "default_true")]
pub anti_alias_custom_block_glyphs: bool,
/// Controls the amount of padding to use around the terminal cell area
#[serde(default)]
#[dynamic(default)]
pub window_padding: WindowPadding,
/// Specifies the path to a background image attachment file.
@ -367,13 +365,13 @@ pub struct Config {
/// of the window before any other content.
///
/// The image will be scaled to fit the window.
#[serde(default)]
#[dynamic(default)]
pub window_background_image: Option<PathBuf>,
#[serde(default)]
#[dynamic(default)]
pub window_background_gradient: Option<Gradient>,
#[serde(default)]
#[dynamic(default)]
pub window_background_image_hsb: Option<HsbTransform>,
#[serde(default)]
#[dynamic(default)]
pub foreground_text_hsb: HsbTransform,
/// Specifies the alpha value to use when rendering the background
@ -386,7 +384,7 @@ pub struct Config {
/// This only works on systems with a compositing window manager.
/// Setting opacity to a value other than 1.0 can impact render
/// performance.
#[serde(default = "default_one_point_oh")]
#[dynamic(default = "default_one_point_oh")]
pub window_background_opacity: f32,
/// inactive_pane_hue, inactive_pane_saturation and
@ -418,10 +416,10 @@ pub struct Config {
/// A subtle dimming effect can be achieved by setting:
/// inactive_pane_saturation = 0.9
/// inactive_pane_brightness = 0.8
#[serde(default = "default_inactive_pane_hsb")]
#[dynamic(default = "default_inactive_pane_hsb")]
pub inactive_pane_hsb: HsbTransform,
#[serde(default = "default_one_point_oh")]
#[dynamic(default = "default_one_point_oh")]
pub text_background_opacity: f32,
/// Specifies how often a blinking cursor transitions between visible
@ -430,17 +428,17 @@ pub struct Config {
/// Note that this value is approximate due to the way that the system
/// event loop schedulers manage timers; non-zero values will be at
/// least the interval specified with some degree of slop.
#[serde(default = "default_cursor_blink_rate")]
#[dynamic(default = "default_cursor_blink_rate")]
pub cursor_blink_rate: u64,
#[serde(default = "linear_ease")]
#[dynamic(default = "linear_ease")]
pub cursor_blink_ease_in: EasingFunction,
#[serde(default = "linear_ease")]
#[dynamic(default = "linear_ease")]
pub cursor_blink_ease_out: EasingFunction,
#[serde(default = "default_anim_fps")]
#[dynamic(default = "default_anim_fps")]
pub animation_fps: u8,
#[serde(default)]
#[dynamic(default)]
pub force_reverse_video_cursor: bool,
/// Specifies the default cursor style. various escape sequences
@ -451,7 +449,7 @@ pub struct Config {
/// Acceptable values are `SteadyBlock`, `BlinkingBlock`,
/// `SteadyUnderline`, `BlinkingUnderline`, `SteadyBar`,
/// and `BlinkingBar`.
#[serde(default)]
#[dynamic(default)]
pub default_cursor_style: DefaultCursorStyle,
/// Specifies how often blinking text (normal speed) transitions
@ -460,11 +458,11 @@ pub struct Config {
/// value is approximate due to the way that the system event loop
/// schedulers manage timers; non-zero values will be at least the
/// interval specified with some degree of slop.
#[serde(default = "default_text_blink_rate")]
#[dynamic(default = "default_text_blink_rate")]
pub text_blink_rate: u64,
#[serde(default = "linear_ease")]
#[dynamic(default = "linear_ease")]
pub text_blink_ease_in: EasingFunction,
#[serde(default = "linear_ease")]
#[dynamic(default = "linear_ease")]
pub text_blink_ease_out: EasingFunction,
/// Specifies how often blinking text (rapid speed) transitions
@ -473,176 +471,180 @@ pub struct Config {
/// value is approximate due to the way that the system event loop
/// schedulers manage timers; non-zero values will be at least the
/// interval specified with some degree of slop.
#[serde(default = "default_text_blink_rate_rapid")]
#[dynamic(default = "default_text_blink_rate_rapid")]
pub text_blink_rate_rapid: u64,
#[serde(default = "linear_ease")]
#[dynamic(default = "linear_ease")]
pub text_blink_rapid_ease_in: EasingFunction,
#[serde(default = "linear_ease")]
#[dynamic(default = "linear_ease")]
pub text_blink_rapid_ease_out: EasingFunction,
/// If non-zero, specifies the period (in seconds) at which various
/// statistics are logged. Note that there is a minimum period of
/// 10 seconds.
#[serde(default)]
#[dynamic(default)]
pub periodic_stat_logging: u64,
/// If false, do not scroll to the bottom of the terminal when
/// you send input to the terminal.
/// The default is to scroll to the bottom when you send input
/// to the terminal.
#[serde(default = "default_true")]
#[dynamic(default = "default_true")]
pub scroll_to_bottom_on_input: bool,
#[serde(default = "default_true")]
#[dynamic(default = "default_true")]
pub use_ime: bool,
#[serde(default)]
#[dynamic(default)]
pub xim_im_name: Option<String>,
#[serde(default = "default_true")]
#[dynamic(default = "default_true")]
pub use_dead_keys: bool,
#[serde(default)]
#[dynamic(default)]
pub launch_menu: Vec<SpawnCommand>,
/// When true, watch the config file and reload it automatically
/// when it is detected as changing.
#[serde(default = "default_true")]
#[dynamic(default = "default_true")]
pub automatically_reload_config: bool,
#[serde(default = "default_true")]
#[dynamic(default = "default_true")]
pub check_for_updates: bool,
#[serde(default)]
#[dynamic(default)]
pub show_update_window: bool,
#[serde(default = "default_update_interval")]
#[dynamic(default = "default_update_interval")]
pub check_for_updates_interval_seconds: u64,
/// When set to true, use the CSI-U encoding scheme as described
/// in http://www.leonerd.org.uk/hacks/fixterms/
/// This is off by default because @wez and @jsgf find the shift-space
/// mapping annoying in vim :-p
#[serde(default)]
#[dynamic(default)]
pub enable_csi_u_key_encoding: bool,
#[serde(default)]
#[dynamic(default)]
pub window_close_confirmation: WindowCloseConfirmation,
#[serde(default)]
#[dynamic(default)]
pub native_macos_fullscreen_mode: bool,
#[serde(default = "default_word_boundary")]
#[dynamic(default = "default_word_boundary")]
pub selection_word_boundary: String,
#[serde(default = "default_enq_answerback")]
#[dynamic(default = "default_enq_answerback")]
pub enq_answerback: String,
#[serde(default = "default_true")]
#[dynamic(default = "default_true")]
pub adjust_window_size_when_changing_font_size: bool,
#[serde(default)]
#[dynamic(default)]
pub use_resize_increments: bool,
#[serde(default = "default_alternate_buffer_wheel_scroll_speed")]
#[dynamic(default = "default_alternate_buffer_wheel_scroll_speed")]
pub alternate_buffer_wheel_scroll_speed: u8,
#[serde(default = "default_status_update_interval")]
#[dynamic(default = "default_status_update_interval")]
pub status_update_interval: u64,
#[serde(default)]
#[dynamic(default)]
pub experimental_pixel_positioning: bool,
#[serde(default)]
#[dynamic(default)]
pub bidi_enabled: bool,
#[serde(default)]
#[dynamic(default)]
pub bidi_direction: ParagraphDirectionHint,
#[serde(default = "default_stateless_process_list")]
#[dynamic(default = "default_stateless_process_list")]
pub skip_close_confirmation_for_processes_named: Vec<String>,
#[serde(default = "default_true")]
#[dynamic(default = "default_true")]
pub warn_about_missing_glyphs: bool,
#[serde(default)]
#[dynamic(default)]
pub sort_fallback_fonts_by_coverage: bool,
#[serde(default)]
#[dynamic(default)]
pub search_font_dirs_for_fallback: bool,
#[serde(default)]
#[dynamic(default)]
pub use_cap_height_to_scale_fallback_fonts: bool,
#[serde(default)]
#[dynamic(default)]
pub swallow_mouse_click_on_pane_focus: bool,
#[serde(default = "default_swallow_mouse_click_on_window_focus")]
#[dynamic(default = "default_swallow_mouse_click_on_window_focus")]
pub swallow_mouse_click_on_window_focus: bool,
#[serde(default)]
#[dynamic(default)]
pub pane_focus_follows_mouse: bool,
#[serde(default = "default_true")]
#[dynamic(default = "default_true")]
pub unzoom_on_switch_pane: bool,
#[serde(default = "default_max_fps")]
#[dynamic(default = "default_max_fps")]
pub max_fps: u8,
#[serde(default)]
#[dynamic(default)]
pub visual_bell: VisualBell,
#[serde(default)]
#[dynamic(default)]
pub audible_bell: AudibleBell,
#[serde(default)]
#[dynamic(default)]
pub canonicalize_pasted_newlines: Option<NewlineCanon>,
#[serde(default = "default_unicode_version")]
#[dynamic(default = "default_unicode_version")]
pub unicode_version: u8,
#[serde(default)]
#[dynamic(default)]
pub treat_east_asian_ambiguous_width_as_wide: bool,
#[serde(default = "default_true")]
#[dynamic(default = "default_true")]
pub allow_download_protocols: bool,
#[serde(default)]
#[dynamic(default)]
pub allow_win32_input_mode: bool,
#[serde(default)]
#[dynamic(default)]
pub default_domain: Option<String>,
#[serde(default)]
#[dynamic(default)]
pub default_workspace: Option<String>,
#[serde(default)]
#[dynamic(default)]
pub xcursor_theme: Option<String>,
#[serde(default)]
#[dynamic(default)]
pub xcursor_size: Option<u32>,
#[serde(default)]
#[dynamic(default)]
pub key_map_preference: KeyMapPreference,
#[serde(default)]
#[dynamic(default)]
pub quote_dropped_files: DroppedFileQuoting,
}
impl_lua_conversion!(Config);
impl_lua_conversion_dynamic!(Config);
impl Default for Config {
fn default() -> Self {
// Ask serde to provide the defaults based on the attributes
// Ask FromDynamic to provide the defaults based on the attributes
// specified in the struct so that we don't have to repeat
// the same thing in a different form down here
toml::from_str("").unwrap()
Config::from_dynamic(
&wezterm_dynamic::Value::Object(Default::default()),
Default::default(),
)
.unwrap()
}
}
impl Config {
pub fn load() -> anyhow::Result<LoadedConfig> {
Self::load_with_overrides(&serde_json::Value::default())
Self::load_with_overrides(&wezterm_dynamic::Value::default())
}
pub fn load_with_overrides(overrides: &serde_json::Value) -> anyhow::Result<LoadedConfig> {
pub fn load_with_overrides(overrides: &wezterm_dynamic::Value) -> anyhow::Result<LoadedConfig> {
// 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,
@ -707,8 +709,8 @@ 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(|| {
let config = Self::apply_overrides_obj_to(&lua, config, overrides)?;
cfg = Config::from_lua(config, &lua).with_context(|| {
format!(
"Error converting lua value returned by script {} to Config struct",
p.display()
@ -746,15 +748,17 @@ impl Config {
}
pub(crate) fn apply_overrides_obj_to<'l>(
lua: &'l mlua::Lua,
mut config: mlua::Value<'l>,
overrides: &serde_json::Value,
overrides: &wezterm_dynamic::Value,
) -> anyhow::Result<mlua::Value<'l>> {
match overrides {
serde_json::Value::Object(obj) => {
wezterm_dynamic::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)?;
let key = luahelper::dynamic_to_lua_value(lua, key.clone())?;
let value = luahelper::dynamic_to_lua_value(lua, value.clone())?;
tbl.set(key, value)?;
}
}
Ok(config)
@ -955,8 +959,7 @@ impl Config {
fn load_scheme(path: &Path) -> anyhow::Result<ColorSchemeFile> {
let s = std::fs::read_to_string(path)?;
let scheme: ColorSchemeFile = toml::from_str(&s).context("parsing TOML")?;
Ok(scheme)
ColorSchemeFile::from_toml_str(&s).context("parsing TOML")
}
for colors_dir in paths {
@ -1289,7 +1292,7 @@ fn default_inactive_pane_hsb() -> HsbTransform {
}
}
#[derive(Deserialize, Serialize, Clone, Copy, Debug)]
#[derive(FromDynamic, ToDynamic, Clone, Copy, Debug)]
pub enum DefaultCursorStyle {
BlinkingBlock,
SteadyBlock,
@ -1298,7 +1301,6 @@ pub enum DefaultCursorStyle {
BlinkingBar,
SteadyBar,
}
impl_lua_conversion!(DefaultCursorStyle);
impl Default for DefaultCursorStyle {
fn default() -> Self {
@ -1334,18 +1336,17 @@ const fn default_half_cell() -> Dimension {
Dimension::Cells(0.5)
}
#[derive(Deserialize, Serialize, Clone, Copy, Debug)]
#[derive(FromDynamic, ToDynamic, Clone, Copy, Debug)]
pub struct WindowPadding {
#[serde(deserialize_with = "de_pixels", default = "default_one_cell")]
#[dynamic(try_from = "crate::units::PixelUnit", default = "default_one_cell")]
pub left: Dimension,
#[serde(deserialize_with = "de_pixels", default = "default_half_cell")]
#[dynamic(try_from = "crate::units::PixelUnit", default = "default_half_cell")]
pub top: Dimension,
#[serde(deserialize_with = "de_pixels", default = "default_one_cell")]
#[dynamic(try_from = "crate::units::PixelUnit", default = "default_one_cell")]
pub right: Dimension,
#[serde(deserialize_with = "de_pixels", default = "default_half_cell")]
#[dynamic(try_from = "crate::units::PixelUnit", default = "default_half_cell")]
pub bottom: Dimension,
}
impl_lua_conversion!(WindowPadding);
impl Default for WindowPadding {
fn default() -> Self {
@ -1358,74 +1359,22 @@ impl Default for WindowPadding {
}
}
#[derive(Serialize, Clone, Copy, Debug, PartialEq, Eq)]
#[derive(FromDynamic, ToDynamic, Clone, Copy, Debug, PartialEq, Eq)]
pub enum NewlineCanon {
// FIXME: also allow deserialziing from bool
None,
LineFeed,
CarriageReturn,
CarriageReturnAndLineFeed,
}
impl_lua_conversion!(NewlineCanon);
impl<'de> Deserialize<'de> for NewlineCanon {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct Helper;
impl<'de> serde::de::Visitor<'de> for Helper {
type Value = NewlineCanon;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("true, false, \"None\", \"LineFeed\", \"CarriageReturnAndLineFeed\", \"CarriageReturnAndLineFeed\"")
}
fn visit_bool<E>(self, value: bool) -> Result<NewlineCanon, E>
where
E: serde::de::Error,
{
Ok(if value {
NewlineCanon::CarriageReturnAndLineFeed
} else {
NewlineCanon::LineFeed
})
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match v {
"None" => Ok(NewlineCanon::None),
"LineFeed" => Ok(NewlineCanon::LineFeed),
"CarriageReturn" => Ok(NewlineCanon::CarriageReturn),
"CarriageReturnAndLineFeed" => Ok(NewlineCanon::CarriageReturnAndLineFeed),
_ => Err(serde::de::Error::unknown_variant(
v,
&[
"None",
"LineFeed",
"CarriageReturn",
"CarriageReturnAndLineFeed",
],
)),
}
}
}
deserializer.deserialize_any(Helper)
}
}
#[derive(Deserialize, Serialize, Clone, Copy, Debug)]
#[derive(FromDynamic, ToDynamic, Clone, Copy, Debug)]
pub enum WindowCloseConfirmation {
AlwaysPrompt,
NeverPrompt,
// TODO: something smart where we see whether the
// running programs are stateful
}
impl_lua_conversion!(WindowCloseConfirmation);
impl Default for WindowCloseConfirmation {
fn default() -> Self {
@ -1453,7 +1402,7 @@ impl PathPossibility {
}
/// Behavior when the program spawned by wezterm terminates
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, FromDynamic, ToDynamic, Clone, Copy, PartialEq, Eq)]
pub enum ExitBehavior {
/// Close the associated pane
Close,
@ -1469,7 +1418,7 @@ impl Default for ExitBehavior {
}
}
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, FromDynamic, ToDynamic, Clone, Copy, PartialEq, Eq)]
pub enum DroppedFileQuoting {
/// No quoting is performed, the file name is passed through as-is
None,

View File

@ -1,14 +1,14 @@
use crate::*;
use std::fs::{File, OpenOptions};
use std::path::PathBuf;
use wezterm_dynamic::{FromDynamic, ToDynamic};
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
#[derive(Default, Debug, Clone, FromDynamic, ToDynamic)]
pub struct DaemonOptions {
pub pid_file: Option<PathBuf>,
pub stdout: Option<PathBuf>,
pub stderr: Option<PathBuf>,
}
impl_lua_conversion!(DaemonOptions);
/// Set the sticky bit on path.
/// This is used in a couple of situations where we want files that

View File

@ -2,13 +2,13 @@ use crate::color::RgbaColor;
use crate::*;
use bitflags::*;
use enum_display_derive::Display;
use luahelper::impl_lua_conversion;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use luahelper::impl_lua_conversion_dynamic;
use std::convert::TryFrom;
use std::fmt::Display;
use wezterm_dynamic::{FromDynamic, FromDynamicOptions, ToDynamic, Value};
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Hash, Display, PartialOrd, Ord,
Debug, Clone, Copy, PartialEq, Eq, Hash, Display, PartialOrd, Ord, FromDynamic, ToDynamic,
)]
pub enum FontStyle {
Normal,
@ -23,7 +23,7 @@ impl Default for FontStyle {
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash, Display, PartialOrd, Ord,
Debug, Clone, Copy, PartialEq, Eq, Hash, Display, PartialOrd, Ord, FromDynamic, ToDynamic,
)]
pub enum FontStretch {
UltraCondensed,
@ -134,56 +134,36 @@ impl FontWeight {
}
}
impl Serialize for FontWeight {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
impl ToDynamic for FontWeight {
fn to_dynamic(&self) -> Value {
match self.categorize_weight() {
FontWeightOrLabel::Weight(n) => serializer.serialize_u16(n),
FontWeightOrLabel::Label(l) => serializer.serialize_str(l),
FontWeightOrLabel::Weight(n) => Value::U64(n as u64),
FontWeightOrLabel::Label(l) => Value::String(l.to_string()),
}
}
}
impl<'de> Deserialize<'de> for FontWeight {
fn deserialize<D>(deserializer: D) -> Result<FontWeight, D::Error>
where
D: Deserializer<'de>,
{
struct V {}
impl<'de> serde::de::Visitor<'de> for V {
type Value = FontWeight;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("string or font weight value")
impl FromDynamic for FontWeight {
fn from_dynamic(
value: &Value,
_options: FromDynamicOptions,
) -> Result<Self, wezterm_dynamic::Error> {
match value {
Value::String(s) => {
Ok(Self::from_str(s).ok_or_else(|| format!("invalid font weight {}", s))?)
}
fn visit_str<E>(self, value: &str) -> Result<FontWeight, E>
where
E: serde::de::Error,
{
match FontWeight::from_str(value) {
Some(w) => Ok(w),
None => Err(E::custom(format!("invalid font weight {}", value))),
}
}
// Lua gives us an integer in this format
fn visit_i64<E>(self, value: i64) -> Result<FontWeight, E>
where
E: serde::de::Error,
{
if value > 0 && value <= u16::MAX as i64 {
Ok(FontWeight(value as u16))
Value::U64(value) => {
if *value > 0 && *value <= (u16::MAX as u64) {
Ok(FontWeight(*value as u16))
} else {
Err(E::custom(format!("invalid font weight {}", value)))
Err(format!("invalid font weight {}", value).into())
}
}
other => Err(wezterm_dynamic::Error::NoConversion {
source_type: other.variant_name().to_string(),
dest_type: "FontWeight",
}),
}
deserializer.deserialize_any(V {})
}
}
@ -235,7 +215,7 @@ impl FontWeight {
}
}
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromDynamic, ToDynamic)]
pub enum FreeTypeLoadTarget {
/// This corresponds to the default hinting algorithm, optimized
/// for standard gray-level rendering.
@ -265,8 +245,8 @@ bitflags! {
// Note that these are strongly coupled with deps/freetype/src/lib.rs,
// but we can't directly reference that from here without making config
// depend on freetype.
#[derive(Default, Deserialize, Serialize)]
#[serde(try_from="String", into="String")]
#[derive(Default, FromDynamic, ToDynamic)]
#[dynamic(try_from="String", into="String")]
pub struct FreeTypeLoadFlags: u32 {
/// FT_LOAD_DEFAULT
const DEFAULT = 0;
@ -290,6 +270,12 @@ impl Into<String> for FreeTypeLoadFlags {
}
}
impl Into<String> for &FreeTypeLoadFlags {
fn into(self) -> String {
self.to_string()
}
}
impl ToString for FreeTypeLoadFlags {
fn to_string(&self) -> String {
let mut s = vec![];
@ -339,33 +325,33 @@ impl TryFrom<String> for FreeTypeLoadFlags {
}
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, FromDynamic, ToDynamic)]
pub struct FontAttributes {
/// The font family name
pub family: String,
/// Whether the font should be a bold variant
#[serde(default)]
#[dynamic(default)]
pub weight: FontWeight,
#[serde(default)]
#[dynamic(default)]
pub stretch: FontStretch,
/// Whether the font should be an italic variant
#[serde(default)]
#[dynamic(default)]
pub style: FontStyle,
pub is_fallback: bool,
pub is_synthetic: bool,
#[serde(default)]
#[dynamic(default)]
pub harfbuzz_features: Option<Vec<String>>,
#[serde(default)]
#[dynamic(default)]
pub freetype_load_target: Option<FreeTypeLoadTarget>,
#[serde(default)]
#[dynamic(default)]
pub freetype_render_target: Option<FreeTypeLoadTarget>,
#[serde(default)]
#[dynamic(default)]
pub freetype_load_flags: Option<FreeTypeLoadFlags>,
#[serde(default)]
#[dynamic(default)]
pub scale: Option<NotNan<f64>>,
}
impl_lua_conversion!(FontAttributes);
impl_lua_conversion_dynamic!(FontAttributes);
impl std::fmt::Display for FontAttributes {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
@ -430,9 +416,9 @@ impl Default for FontAttributes {
}
/// Represents textual styling.
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, FromDynamic, ToDynamic)]
pub struct TextStyle {
#[serde(default)]
#[dynamic(default)]
pub font: Vec<FontAttributes>,
/// If set, when rendering text that is set to the default
@ -441,7 +427,7 @@ pub struct TextStyle {
/// the text color for eg: bold text.
pub foreground: Option<RgbaColor>,
}
impl_lua_conversion!(TextStyle);
impl_lua_conversion_dynamic!(TextStyle);
impl Default for TextStyle {
fn default() -> Self {
@ -604,7 +590,7 @@ impl TextStyle {
/// The above is translated as: "if the `CellAttributes` have the italic bit
/// set, then use the italic style of font rather than the default", and
/// stop processing further font rules.
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
#[derive(Debug, Default, Clone, FromDynamic, ToDynamic)]
pub struct StyleRule {
/// If present, this rule matches when CellAttributes::intensity holds
/// a value that matches this rule. Valid values are "Bold", "Normal",
@ -633,9 +619,8 @@ pub struct StyleRule {
/// When this rule matches, `font` specifies the styling to be used.
pub font: TextStyle,
}
impl_lua_conversion!(StyleRule);
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromDynamic, ToDynamic)]
pub enum AllowSquareGlyphOverflow {
Never,
Always,
@ -648,7 +633,7 @@ impl Default for AllowSquareGlyphOverflow {
}
}
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromDynamic, ToDynamic)]
pub enum FontLocatorSelection {
/// Use fontconfig APIs to resolve fonts (!macos, posix systems)
FontConfig,
@ -672,30 +657,7 @@ impl Default for FontLocatorSelection {
}
}
impl FontLocatorSelection {
pub fn variants() -> Vec<&'static str> {
vec!["FontConfig", "CoreText", "ConfigDirsOnly", "Gdi"]
}
}
impl std::str::FromStr for FontLocatorSelection {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_ref() {
"fontconfig" => Ok(Self::FontConfig),
"coretext" => Ok(Self::CoreText),
"configdirsonly" => Ok(Self::ConfigDirsOnly),
"gdi" => Ok(Self::Gdi),
_ => Err(anyhow!(
"{} is not a valid FontLocatorSelection variant, possible values are {:?}",
s,
Self::variants()
)),
}
}
}
#[derive(Debug, Deserialize, Serialize, Clone, Copy)]
#[derive(Debug, Clone, Copy, FromDynamic, ToDynamic)]
pub enum FontRasterizerSelection {
FreeType,
}
@ -706,27 +668,7 @@ impl Default for FontRasterizerSelection {
}
}
impl FontRasterizerSelection {
pub fn variants() -> Vec<&'static str> {
vec!["FreeType"]
}
}
impl std::str::FromStr for FontRasterizerSelection {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_ref() {
"freetype" => Ok(Self::FreeType),
_ => Err(anyhow!(
"{} is not a valid FontRasterizerSelection variant, possible values are {:?}",
s,
Self::variants()
)),
}
}
}
#[derive(Debug, Deserialize, Serialize, Clone, Copy)]
#[derive(Debug, Clone, Copy, FromDynamic, ToDynamic)]
pub enum FontShaperSelection {
Allsorts,
Harfbuzz,
@ -738,27 +680,6 @@ impl Default for FontShaperSelection {
}
}
impl FontShaperSelection {
pub fn variants() -> Vec<&'static str> {
vec!["Harfbuzz", "AllSorts"]
}
}
impl std::str::FromStr for FontShaperSelection {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_ref() {
"harfbuzz" => Ok(Self::Harfbuzz),
"allsorts" => Ok(Self::Allsorts),
_ => Err(anyhow!(
"{} is not a valid FontShaperSelection variant, possible values are {:?}",
s,
Self::variants()
)),
}
}
}
#[cfg(test)]
mod test {
use super::*;

View File

@ -1,36 +1,13 @@
use super::*;
use wezterm_dynamic::{FromDynamic, ToDynamic};
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromDynamic, ToDynamic)]
pub enum FrontEndSelection {
OpenGL,
Software,
}
impl_lua_conversion!(FrontEndSelection);
impl Default for FrontEndSelection {
fn default() -> Self {
FrontEndSelection::OpenGL
}
}
impl FrontEndSelection {
// TODO: find or build a proc macro for this
pub fn variants() -> Vec<&'static str> {
vec!["OpenGL", "Software"]
}
}
impl std::str::FromStr for FrontEndSelection {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_ref() {
"software" => Ok(FrontEndSelection::Software),
"opengl" => Ok(FrontEndSelection::OpenGL),
_ => Err(anyhow!(
"{} is not a valid FrontEndSelection variant, possible values are {:?}",
s,
FrontEndSelection::variants()
)),
}
}
}

View File

@ -1,23 +1,23 @@
use crate::de_notnan;
use crate::keys::KeyNoAction;
use luahelper::impl_lua_conversion;
use luahelper::impl_lua_conversion_dynamic;
use ordered_float::NotNan;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::convert::TryFrom;
use std::path::PathBuf;
use wezterm_dynamic::{FromDynamic, ToDynamic};
use wezterm_input_types::{KeyCode, Modifiers};
use wezterm_term::input::MouseButton;
#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[derive(Default, Debug, Clone, FromDynamic, ToDynamic, PartialEq, Eq)]
pub struct LauncherActionArgs {
pub flags: LauncherFlags,
pub title: Option<String>,
}
bitflags::bitflags! {
#[derive(Default, Deserialize, Serialize)]
#[serde(try_from="String", into="String")]
#[derive(Default, FromDynamic, ToDynamic)]
#[dynamic(try_from="String", into="String")]
pub struct LauncherFlags :u32 {
const ZERO = 0;
const FUZZY = 1;
@ -36,6 +36,12 @@ impl Into<String> for LauncherFlags {
}
}
impl Into<String> for &LauncherFlags {
fn into(self) -> String {
self.to_string()
}
}
impl ToString for LauncherFlags {
fn to_string(&self) -> String {
let mut s = vec![];
@ -89,7 +95,7 @@ impl TryFrom<String> for LauncherFlags {
}
}
#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, FromDynamic, ToDynamic)]
pub enum SelectionMode {
Cell,
Word,
@ -98,7 +104,7 @@ pub enum SelectionMode {
Block,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, FromDynamic, ToDynamic)]
pub enum Pattern {
CaseSensitiveString(String),
CaseInSensitiveString(String),
@ -133,7 +139,7 @@ impl std::ops::DerefMut for Pattern {
}
/// A mouse event that can trigger an action
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, FromDynamic, ToDynamic)]
pub enum MouseEventTrigger {
/// Mouse button is pressed. streak is how many times in a row
/// it was pressed.
@ -148,7 +154,7 @@ pub enum MouseEventTrigger {
/// When spawning a tab, specify which domain should be used to
/// host/spawn that tab.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, FromDynamic, ToDynamic)]
pub enum SpawnTabDomain {
/// Use the default domain
DefaultDomain,
@ -166,7 +172,7 @@ impl Default for SpawnTabDomain {
}
}
#[derive(Default, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[derive(Default, Clone, PartialEq, Eq, FromDynamic, ToDynamic)]
pub struct SpawnCommand {
/// Optional descriptive label
pub label: Option<String>,
@ -187,10 +193,10 @@ pub struct SpawnCommand {
/// Specifies a map of environment variables that should be set.
/// Whether this is used depends on the domain.
#[serde(default)]
#[dynamic(default)]
pub set_environment_variables: HashMap<String, String>,
#[serde(default)]
#[dynamic(default)]
pub domain: SpawnTabDomain,
}
@ -220,7 +226,7 @@ impl std::fmt::Display for SpawnCommand {
}
}
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromDynamic, ToDynamic)]
pub enum PaneDirection {
Up,
Down,
@ -230,7 +236,7 @@ pub enum PaneDirection {
Prev,
}
#[derive(Debug, Copy, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, FromDynamic, ToDynamic)]
pub enum ScrollbackEraseMode {
ScrollbackOnly,
ScrollbackAndViewport,
@ -242,7 +248,7 @@ impl Default for ScrollbackEraseMode {
}
}
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromDynamic, ToDynamic)]
pub enum ClipboardCopyDestination {
Clipboard,
PrimarySelection,
@ -255,7 +261,7 @@ impl Default for ClipboardCopyDestination {
}
}
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromDynamic, ToDynamic)]
pub enum ClipboardPasteSource {
Clipboard,
PrimarySelection,
@ -267,22 +273,22 @@ impl Default for ClipboardPasteSource {
}
}
#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[derive(Default, Debug, Clone, PartialEq, Eq, FromDynamic, ToDynamic)]
pub struct QuickSelectArguments {
/// Overrides the main quick_select_alphabet config
#[serde(default)]
#[dynamic(default)]
pub alphabet: String,
/// Overrides the main quick_select_patterns config
#[serde(default)]
#[dynamic(default)]
pub patterns: Vec<String>,
#[serde(default)]
#[dynamic(default)]
pub action: Option<Box<KeyAssignment>>,
/// Label to use in place of "copy" when `action` is set
#[serde(default)]
#[dynamic(default)]
pub label: String,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, FromDynamic, ToDynamic)]
pub enum KeyAssignment {
SpawnTab(SpawnTabDomain),
SpawnWindow,
@ -312,7 +318,6 @@ pub enum KeyAssignment {
ReloadConfiguration,
MoveTabRelative(isize),
MoveTab(usize),
#[serde(deserialize_with = "de_notnan")]
ScrollByPage(NotNan<f64>),
ScrollByLine(isize),
ScrollToPrompt(isize),
@ -361,11 +366,11 @@ pub enum KeyAssignment {
ActivateKeyTable {
name: String,
#[serde(default)]
#[dynamic(default)]
timeout_milliseconds: Option<u64>,
#[serde(default)]
#[dynamic(default)]
replace_current: bool,
#[serde(default = "crate::default_true")]
#[dynamic(default = "crate::default_true")]
one_shot: bool,
},
PopKeyTable,
@ -375,9 +380,9 @@ pub enum KeyAssignment {
CopyMode(CopyModeAssignment),
}
impl_lua_conversion!(KeyAssignment);
impl_lua_conversion_dynamic!(KeyAssignment);
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, FromDynamic, ToDynamic)]
pub enum CopyModeAssignment {
MoveToViewportBottom,
MoveToViewportTop,
@ -408,7 +413,6 @@ pub enum CopyModeAssignment {
EditPattern,
AcceptPattern,
}
impl_lua_conversion!(CopyModeAssignment);
pub type KeyTable = HashMap<(KeyCode, Modifiers), KeyTableEntry>;

View File

@ -1,15 +1,13 @@
use crate::keyassignment::{KeyAssignment, MouseEventTrigger};
use luahelper::impl_lua_conversion;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::convert::TryFrom;
use wezterm_dynamic::{FromDynamic, ToDynamic};
use wezterm_input_types::{KeyCode, Modifiers, PhysKeyCode};
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, FromDynamic, ToDynamic)]
pub enum KeyMapPreference {
Physical,
Mapped,
}
impl_lua_conversion!(KeyMapPreference);
impl Default for KeyMapPreference {
fn default() -> Self {
@ -17,8 +15,8 @@ impl Default for KeyMapPreference {
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[serde(into = "String", try_from = "String")]
#[derive(Debug, Clone, Eq, PartialEq, FromDynamic, ToDynamic)]
#[dynamic(into = "String", try_from = "String")]
pub enum DeferredKeyCode {
KeyCode(KeyCode),
Either {
@ -64,6 +62,15 @@ impl DeferredKeyCode {
}
}
impl Into<String> for &DeferredKeyCode {
fn into(self) -> String {
match self {
DeferredKeyCode::KeyCode(key) => key.to_string(),
DeferredKeyCode::Either { original, .. } => original.to_string(),
}
}
}
impl Into<String> for DeferredKeyCode {
fn into(self) -> String {
match self {
@ -104,89 +111,36 @@ impl TryFrom<&str> for DeferredKeyCode {
}
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, FromDynamic, ToDynamic)]
pub struct KeyNoAction {
pub key: DeferredKeyCode,
#[serde(
deserialize_with = "de_modifiers",
serialize_with = "ser_modifiers",
default
)]
#[dynamic(default)]
pub mods: Modifiers,
}
impl_lua_conversion!(KeyNoAction);
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Clone, FromDynamic, ToDynamic)]
pub struct Key {
#[serde(flatten)]
#[dynamic(flatten)]
pub key: KeyNoAction,
pub action: KeyAssignment,
}
impl_lua_conversion!(Key);
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Clone, FromDynamic, ToDynamic)]
pub struct LeaderKey {
#[serde(flatten)]
#[dynamic(flatten)]
pub key: KeyNoAction,
#[serde(default = "default_leader_timeout")]
#[dynamic(default = "default_leader_timeout")]
pub timeout_milliseconds: u64,
}
impl_lua_conversion!(LeaderKey);
fn default_leader_timeout() -> u64 {
1000
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Clone, FromDynamic, ToDynamic)]
pub struct Mouse {
pub event: MouseEventTrigger,
#[serde(
deserialize_with = "de_modifiers",
serialize_with = "ser_modifiers",
default
)]
#[dynamic(default, into = "String", try_from = "String")]
pub mods: Modifiers,
pub action: KeyAssignment,
}
impl_lua_conversion!(Mouse);
pub(crate) fn ser_modifiers<S>(mods: &Modifiers, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let s = mods.to_string();
serializer.serialize_str(&s)
}
pub(crate) fn de_modifiers<'de, D>(deserializer: D) -> Result<Modifiers, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let mut mods = Modifiers::NONE;
for ele in s.split('|') {
// Allow for whitespace; debug printing Modifiers includes spaces
// around the `|` so it is desirable to be able to reverse that
// encoding here.
let ele = ele.trim();
if ele == "SHIFT" {
mods |= Modifiers::SHIFT;
} else if ele == "ALT" || ele == "OPT" || ele == "META" {
mods |= Modifiers::ALT;
} else if ele == "CTRL" {
mods |= Modifiers::CTRL;
} else if ele == "SUPER" || ele == "CMD" || ele == "WIN" {
mods |= Modifiers::SUPER;
} else if ele == "LEADER" {
mods |= Modifiers::LEADER;
} else if ele == "NONE" || ele == "" {
mods |= Modifiers::NONE;
} else {
return Err(serde::de::Error::custom(format!(
"invalid modifier name {} in {}",
ele, s
)));
}
}
Ok(mods)
}

View File

@ -2,17 +2,14 @@
use anyhow::{anyhow, bail, Context, Error};
use lazy_static::lazy_static;
use luahelper::impl_lua_conversion;
use mlua::Lua;
use mlua::{FromLua, Lua};
use ordered_float::NotNan;
use serde::{Deserialize, Deserializer, Serialize};
use smol::channel::{Receiver, Sender};
use smol::prelude::*;
use std::cell::RefCell;
use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};
use std::ffi::OsString;
use std::fs::DirBuilder;
use std::marker::PhantomData;
#[cfg(unix)]
use std::os::unix::fs::DirBuilderExt;
use std::path::{Path, PathBuf};
@ -20,6 +17,7 @@ use std::rc::Rc;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
use std::time::Duration;
use wezterm_dynamic::{ToDynamic, Value};
mod background;
mod bell;
@ -75,11 +73,32 @@ thread_local! {
static LUA_CONFIG: RefCell<Option<LuaConfigState>> = RefCell::new(None);
}
fn toml_to_dynamic(value: &toml::Value) -> Value {
match value {
toml::Value::String(s) => s.to_dynamic(),
toml::Value::Integer(n) => n.to_dynamic(),
toml::Value::Float(n) => n.to_dynamic(),
toml::Value::Boolean(b) => b.to_dynamic(),
toml::Value::Datetime(d) => d.to_string().to_dynamic(),
toml::Value::Array(a) => a
.iter()
.map(|element| toml_to_dynamic(&element))
.collect::<Vec<_>>()
.to_dynamic(),
toml::Value::Table(t) => Value::Object(
t.iter()
.map(|(k, v)| (Value::String(k.to_string()), toml_to_dynamic(v)))
.collect::<BTreeMap<_, _>>()
.into(),
),
}
}
pub fn build_default_schemes() -> HashMap<String, Palette> {
let mut color_schemes = HashMap::new();
for (scheme_name, data) in SCHEMES.iter() {
let scheme_name = scheme_name.to_string();
let scheme: ColorSchemeFile = toml::from_str(data).unwrap();
let scheme = ColorSchemeFile::from_toml_str(data).unwrap();
color_schemes.insert(scheme_name, scheme.colors);
}
color_schemes
@ -236,7 +255,7 @@ fn default_config_with_overrides_applied() -> anyhow::Result<Config> {
let table = mlua::Value::Table(lua.create_table()?);
let config = Config::apply_overrides_to(&lua, table)?;
let cfg: Config = luahelper::from_lua_value(config)
let cfg: Config = Config::from_lua(config, &lua)
.context("Error converting lua value from overrides to Config struct")?;
// Compute but discard the key bindings here so that we raise any
// problems earlier than we use them.
@ -334,7 +353,7 @@ pub fn configuration() -> ConfigHandle {
/// 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<ConfigHandle, Error> {
pub fn overridden_config(overrides: &wezterm_dynamic::Value) -> Result<ConfigHandle, Error> {
CONFIG.overridden(overrides)
}
@ -527,7 +546,7 @@ impl ConfigInner {
self.generation += 1;
}
fn overridden(&mut self, overrides: &serde_json::Value) -> Result<ConfigHandle, Error> {
fn overridden(&mut self, overrides: &wezterm_dynamic::Value) -> Result<ConfigHandle, Error> {
let config = Config::load_with_overrides(overrides)?;
Ok(ConfigHandle {
config: Arc::new(config.config),
@ -602,7 +621,7 @@ impl Configuration {
inner.use_this_config(cfg);
}
fn overridden(&self, overrides: &serde_json::Value) -> Result<ConfigHandle, Error> {
fn overridden(&self, overrides: &wezterm_dynamic::Value) -> Result<ConfigHandle, Error> {
let mut inner = self.inner.lock().unwrap();
inner.overridden(overrides)
}
@ -659,104 +678,6 @@ impl std::ops::Deref for ConfigHandle {
}
}
pub(crate) fn de_notnan<'de, D>(deserializer: D) -> Result<NotNan<f64>, D::Error>
where
D: Deserializer<'de>,
{
let value: f64 = de_number(deserializer)?;
NotNan::new(value).map_err(|err| serde::de::Error::custom(err.to_string()))
}
/// Deserialize either an integer or a float as a float
pub(crate) fn de_number<'de, D>(deserializer: D) -> Result<f64, D::Error>
where
D: Deserializer<'de>,
{
struct Number;
impl<'de> serde::de::Visitor<'de> for Number {
type Value = f64;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("f64 or i64")
}
fn visit_f64<E>(self, value: f64) -> Result<f64, E>
where
E: serde::de::Error,
{
Ok(value)
}
fn visit_i64<E>(self, value: i64) -> Result<f64, E>
where
E: serde::de::Error,
{
Ok(value as f64)
}
}
deserializer.deserialize_any(Number)
}
/// Helper for deserializing a Vec<T> from lua code.
/// In lua, `{}` could be either an empty map or an empty vec.
/// This helper allows an empty map to be specified and treated as an empty vec.
pub fn de_vec_table<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de>,
{
struct V<T> {
phantom: PhantomData<T>,
}
impl<'de, T> serde::de::Visitor<'de> for V<T>
where
T: Deserialize<'de>,
{
type Value = Vec<T>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("Empty table or vector-like table")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let mut values = if let Some(hint) = seq.size_hint() {
Vec::with_capacity(hint)
} else {
Vec::new()
};
while let Some(ele) = seq.next_element::<T>()? {
values.push(ele);
}
Ok(values)
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
if map.next_entry::<String, T>()?.is_some() {
use serde::de::Error;
Err(A::Error::custom(
"expected empty table or vector-like table",
))
} else {
// Empty map is equivalent to empty vec
Ok(vec![])
}
}
}
deserializer.deserialize_any(V {
phantom: PhantomData,
})
}
pub struct LoadedConfig {
pub config: Config,
pub file_name: Option<PathBuf>,

View File

@ -8,7 +8,6 @@ use bstr::BString;
pub use luahelper::*;
use mlua::{FromLua, Lua, Table, ToLua, ToLuaMulti, Value, Variadic};
use ordered_float::NotNan;
use serde::*;
use smol::prelude::*;
use std::collections::HashMap;
use std::convert::TryFrom;
@ -18,6 +17,7 @@ use termwiz::color::{AnsiColor, ColorAttribute, ColorSpec, RgbColor};
use termwiz::input::Modifiers;
use termwiz::surface::change::Change;
use unicode_segmentation::UnicodeSegmentation;
use wezterm_dynamic::{FromDynamic, ToDynamic};
static LUA_REGISTRY_USER_CALLBACK_COUNT: &str = "wezterm-user-callback-count";
@ -278,12 +278,7 @@ pub fn new_wezterm_terminfo_renderer() -> TerminfoRenderer {
TerminfoRenderer::new(CAPS.clone())
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(transparent)]
struct ChangeWrap(Change);
impl_lua_conversion!(ChangeWrap);
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
#[derive(Debug, FromDynamic, ToDynamic, Clone, PartialEq, Eq)]
pub enum FormatColor {
AnsiColor(AnsiColor),
Color(String),
@ -312,14 +307,14 @@ impl Into<ColorSpec> for FormatColor {
}
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
#[derive(Debug, FromDynamic, ToDynamic, Clone, PartialEq, Eq)]
pub enum FormatItem {
Foreground(FormatColor),
Background(FormatColor),
Attribute(AttributeChange),
Text(String),
}
impl_lua_conversion!(FormatItem);
impl_lua_conversion_dynamic!(FormatItem);
impl Into<Change> for FormatItem {
fn into(self) -> Change {
@ -370,7 +365,7 @@ fn format<'lua>(_: &'lua Lua, items: Vec<FormatItem>) -> mlua::Result<String> {
format_as_escapes(items).map_err(|e| mlua::Error::external(e))
}
#[derive(Serialize, Deserialize, Debug)]
#[derive(FromDynamic, ToDynamic, Debug)]
struct BatteryInfo {
state_of_charge: f32,
vendor: String,
@ -380,7 +375,7 @@ struct BatteryInfo {
time_to_full: Option<f32>,
time_to_empty: Option<f32>,
}
impl_lua_conversion!(BatteryInfo);
impl_lua_conversion_dynamic!(BatteryInfo);
fn opt_string(s: Option<&str>) -> String {
match s {
@ -442,17 +437,17 @@ fn hostname<'lua>(_: &'lua Lua, _: ()) -> mlua::Result<String> {
}
}
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Default, FromDynamic, ToDynamic, Clone, PartialEq, Eq, Hash)]
struct TextStyleAttributes {
/// Whether the font should be a bold variant
#[serde(default)]
#[dynamic(default)]
pub bold: Option<bool>,
#[serde(default)]
#[dynamic(default)]
pub weight: Option<FontWeight>,
#[serde(default)]
#[dynamic(default)]
pub stretch: FontStretch,
/// Whether the font should be an italic variant
#[serde(default)]
#[dynamic(default)]
pub style: FontStyle,
// Ideally we'd simply use serde's aliasing functionality on the `style`
// field to support backwards compatibility, but aliases are invisible
@ -466,7 +461,7 @@ struct TextStyleAttributes {
}
impl<'lua> FromLua<'lua> for TextStyleAttributes {
fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self, mlua::Error> {
let mut attr: TextStyleAttributes = from_lua_value(value)?;
let mut attr: TextStyleAttributes = from_lua_value_dynamic(value)?;
if let Some(italic) = attr.italic.take() {
attr.style = if italic {
FontStyle::Italic
@ -478,33 +473,33 @@ impl<'lua> FromLua<'lua> for TextStyleAttributes {
}
}
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Default, FromDynamic, ToDynamic, Clone, PartialEq, Eq, Hash)]
struct LuaFontAttributes {
/// The font family name
pub family: String,
/// Whether the font should be a bold variant
#[serde(default)]
#[dynamic(default)]
pub weight: FontWeight,
#[serde(default)]
#[dynamic(default)]
pub stretch: FontStretch,
/// Whether the font should be an italic variant
#[serde(default)]
#[dynamic(default)]
pub style: FontStyle,
// Ideally we'd simply use serde's aliasing functionality on the `style`
// field to support backwards compatibility, but aliases are invisible
// to serde_lua, so we do a little fixup here ourselves in our from_lua impl.
#[serde(default)]
#[dynamic(default)]
italic: Option<bool>,
#[serde(default)]
#[dynamic(default)]
pub harfbuzz_features: Option<Vec<String>>,
#[serde(default)]
#[dynamic(default)]
pub freetype_load_target: Option<FreeTypeLoadTarget>,
#[serde(default)]
#[dynamic(default)]
pub freetype_render_target: Option<FreeTypeLoadTarget>,
#[serde(default)]
#[dynamic(default)]
pub freetype_load_flags: Option<String>,
#[serde(default)]
#[dynamic(default)]
pub scale: Option<NotNan<f64>>,
}
impl<'lua> FromLua<'lua> for LuaFontAttributes {
@ -516,7 +511,7 @@ impl<'lua> FromLua<'lua> for LuaFontAttributes {
Ok(attr)
}
v => {
let mut attr: LuaFontAttributes = from_lua_value(v)?;
let mut attr: LuaFontAttributes = from_lua_value_dynamic(v)?;
if let Some(italic) = attr.italic.take() {
attr.style = if italic {
FontStyle::Italic
@ -662,8 +657,8 @@ fn font_with_fallback<'lua>(
/// }
/// }
/// ```
fn action<'lua>(_lua: &'lua Lua, action: Table<'lua>) -> mlua::Result<KeyAssignment> {
Ok(from_lua_value(Value::Table(action))?)
fn action<'lua>(lua: &'lua Lua, action: Table<'lua>) -> mlua::Result<KeyAssignment> {
Ok(KeyAssignment::from_lua(Value::Table(action), lua)?)
}
fn action_callback<'lua>(lua: &'lua Lua, callback: mlua::Function) -> mlua::Result<KeyAssignment> {

View File

@ -1,13 +1,13 @@
use crate::*;
use std::fmt::Display;
use std::str::FromStr;
use wezterm_dynamic::{FromDynamic, ToDynamic};
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
#[derive(Debug, Clone, Copy, FromDynamic, ToDynamic)]
pub enum SshBackend {
Ssh2,
LibSsh,
}
impl_lua_conversion!(SshBackend);
impl Default for SshBackend {
fn default() -> Self {
@ -15,13 +15,12 @@ impl Default for SshBackend {
}
}
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromDynamic, ToDynamic)]
pub enum SshMultiplexing {
WezTerm,
None,
// TODO: Tmux-cc in the future?
}
impl_lua_conversion!(SshMultiplexing);
impl Default for SshMultiplexing {
fn default() -> Self {
@ -29,7 +28,7 @@ impl Default for SshMultiplexing {
}
}
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromDynamic, ToDynamic)]
pub enum Shell {
/// Unknown command shell: no assumptions can be made
Unknown,
@ -39,7 +38,6 @@ pub enum Shell {
Posix,
// TODO: Cmd, PowerShell in the future?
}
impl_lua_conversion!(Shell);
impl Default for Shell {
fn default() -> Self {
@ -47,7 +45,7 @@ impl Default for Shell {
}
}
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
#[derive(Default, Debug, Clone, FromDynamic, ToDynamic)]
pub struct SshDomain {
/// The name of this specific domain. Must be unique amongst
/// all types of domain in the configuration file.
@ -57,20 +55,20 @@ pub struct SshDomain {
pub remote_address: String,
/// Whether agent auth should be disabled
#[serde(default)]
#[dynamic(default)]
pub no_agent_auth: bool,
/// The username to use for authenticating with the remote host
pub username: Option<String>,
/// If true, connect to this domain automatically at startup
#[serde(default)]
#[dynamic(default)]
pub connect_automatically: bool,
#[serde(default = "default_read_timeout")]
#[dynamic(default = "default_read_timeout")]
pub timeout: Duration,
#[serde(default = "default_local_echo_threshold_ms")]
#[dynamic(default = "default_local_echo_threshold_ms")]
pub local_echo_threshold_ms: Option<u64>,
/// The path to the wezterm binary on the remote host
@ -82,19 +80,18 @@ pub struct SshDomain {
/// just connect directly using ssh. This doesn't require
/// that the remote host have wezterm installed, and is equivalent
/// to using `wezterm ssh` to connect.
#[serde(default)]
#[dynamic(default)]
pub multiplexing: SshMultiplexing,
/// ssh_config option values
#[serde(default)]
#[dynamic(default)]
pub ssh_option: HashMap<String, String>,
pub default_prog: Option<Vec<String>>,
#[serde(default)]
#[dynamic(default)]
pub assume_shell: Shell,
}
impl_lua_conversion!(SshDomain);
#[derive(Clone, Debug)]
pub struct SshParameters {

View File

@ -1,6 +1,7 @@
use crate::*;
use wezterm_dynamic::{FromDynamic, ToDynamic};
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
#[derive(Default, Debug, Clone, FromDynamic, ToDynamic)]
pub struct TlsDomainServer {
/// The address:port combination on which the server will listen
/// for client connections
@ -20,12 +21,11 @@ pub struct TlsDomainServer {
/// or to a PEM encoded CA file. If an entry is a directory,
/// then its contents will be loaded as CA certs and added
/// to the trust store.
#[serde(default)]
#[dynamic(default)]
pub pem_root_certs: Vec<PathBuf>,
}
impl_lua_conversion!(TlsDomainServer);
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
#[derive(Default, Debug, Clone, FromDynamic, ToDynamic)]
pub struct TlsDomainClient {
/// The name of this specific domain. Must be unique amongst
/// all types of domain in the configuration file.
@ -52,7 +52,7 @@ pub struct TlsDomainClient {
/// Each entry can be either the path to a directory or to a PEM encoded
/// CA file. If an entry is a directory, then its contents will be
/// loaded as CA certs and added to the trust store.
#[serde(default)]
#[dynamic(default)]
pub pem_root_certs: Vec<PathBuf>,
/// explicitly control whether the client checks that the certificate
@ -61,7 +61,7 @@ pub struct TlsDomainClient {
/// available for troubleshooting purposes and should not be used outside
/// of a controlled environment as it weakens the security of the TLS
/// channel.
#[serde(default)]
#[dynamic(default)]
pub accept_invalid_hostnames: bool,
/// the hostname string that we expect to match against the common name
@ -71,22 +71,21 @@ pub struct TlsDomainClient {
pub expected_cn: Option<String>,
/// If true, connect to this domain automatically at startup
#[serde(default)]
#[dynamic(default)]
pub connect_automatically: bool,
#[serde(default = "default_read_timeout")]
#[dynamic(default = "default_read_timeout")]
pub read_timeout: Duration,
#[serde(default = "default_write_timeout")]
#[dynamic(default = "default_write_timeout")]
pub write_timeout: Duration,
#[serde(default = "default_local_echo_threshold_ms")]
#[dynamic(default = "default_local_echo_threshold_ms")]
pub local_echo_threshold_ms: Option<u64>,
/// The path to the wezterm binary on the remote host
pub remote_wezterm_path: Option<String>,
}
impl_lua_conversion!(TlsDomainClient);
impl TlsDomainClient {
pub fn ssh_parameters(&self) -> Option<anyhow::Result<SshParameters>> {

View File

@ -1,4 +1,22 @@
use serde::{Deserializer, Serialize, Serializer};
use wezterm_dynamic::{FromDynamic, FromDynamicOptions, ToDynamic, Value};
#[derive(Debug, Copy, Clone)]
pub struct PixelUnit(Dimension);
impl Into<Dimension> for PixelUnit {
fn into(self) -> Dimension {
self.0
}
}
impl FromDynamic for PixelUnit {
fn from_dynamic(
value: &Value,
_options: FromDynamicOptions,
) -> Result<Self, wezterm_dynamic::Error> {
Ok(Self(DefaultUnit::Pixels.from_dynamic_impl(value)?))
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum DefaultUnit {
@ -19,63 +37,47 @@ impl DefaultUnit {
}
}
impl<'de> serde::de::Visitor<'de> for DefaultUnit {
type Value = Dimension;
impl DefaultUnit {
fn from_dynamic_impl(self, value: &Value) -> Result<Dimension, String> {
match value {
Value::F64(f) => Ok(self.to_dimension(f.into_inner() as f32)),
Value::I64(i) => Ok(self.to_dimension(*i as f32)),
Value::U64(u) => Ok(self.to_dimension(*u as f32)),
Value::String(s) => {
if let Ok(value) = s.parse::<f32>() {
Ok(self.to_dimension(value))
} else {
fn is_unit(s: &str, unit: &'static str) -> Option<f32> {
let s = s.strip_suffix(unit)?.trim();
s.parse().ok()
}
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("f64 or i64")
}
fn visit_f32<E>(self, value: f32) -> Result<Dimension, E>
where
E: serde::de::Error,
{
Ok(self.to_dimension(value))
}
fn visit_f64<E>(self, value: f64) -> Result<Dimension, E>
where
E: serde::de::Error,
{
Ok(self.to_dimension(value as f32))
}
fn visit_i64<E>(self, value: i64) -> Result<Dimension, E>
where
E: serde::de::Error,
{
Ok(self.to_dimension(value as f32))
}
fn visit_str<E>(self, s: &str) -> Result<Dimension, E>
where
E: serde::de::Error,
{
if let Ok(value) = s.parse::<f32>() {
Ok(self.to_dimension(value))
} else {
fn is_unit(s: &str, unit: &'static str) -> Option<f32> {
let s = s.strip_suffix(unit)?.trim();
s.parse().ok()
}
if let Some(v) = is_unit(s, "px") {
Ok(DefaultUnit::Pixels.to_dimension(v))
} else if let Some(v) = is_unit(s, "%") {
Ok(DefaultUnit::Percent.to_dimension(v))
} else if let Some(v) = is_unit(s, "pt") {
Ok(DefaultUnit::Points.to_dimension(v))
} else if let Some(v) = is_unit(s, "cell") {
Ok(DefaultUnit::Cells.to_dimension(v))
} else {
Err(serde::de::Error::custom(format!(
"expected either a number or a string of \
if let Some(v) = is_unit(s, "px") {
Ok(DefaultUnit::Pixels.to_dimension(v))
} else if let Some(v) = is_unit(s, "%") {
Ok(DefaultUnit::Percent.to_dimension(v))
} else if let Some(v) = is_unit(s, "pt") {
Ok(DefaultUnit::Points.to_dimension(v))
} else if let Some(v) = is_unit(s, "cell") {
Ok(DefaultUnit::Cells.to_dimension(v))
} else {
Err(format!(
"expected either a number or a string of \
the form '123px' where 'px' is a unit and \
can be one of 'px', '%', 'pt' or 'cell', \
but got {}",
s
)))
s
))
}
}
}
other => Err(format!(
"expected either a number or a string of \
the form '123px' where 'px' is a unit and \
can be one of 'px', '%', 'pt' or 'cell', \
but got {}",
other.variant_name()
)),
}
}
}
@ -115,18 +117,15 @@ impl Default for Dimension {
}
}
impl Serialize for Dimension {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
impl ToDynamic for Dimension {
fn to_dynamic(&self) -> Value {
let s = match self {
Self::Points(n) => format!("{}pt", n),
Self::Pixels(n) => format!("{}px", n),
Self::Percent(n) => format!("{}%", n * 100.),
Self::Cells(n) => format!("{}cell", n),
};
serializer.serialize_str(&s)
Value::String(s)
}
}
@ -168,38 +167,3 @@ impl Default for GeometryOrigin {
Self::ScreenCoordinateSystem
}
}
fn de_dimension<'de, D>(unit: DefaultUnit, deserializer: D) -> Result<Dimension, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(unit)
}
pub fn de_pixels<'de, D>(deserializer: D) -> Result<Dimension, D::Error>
where
D: Deserializer<'de>,
{
de_dimension(DefaultUnit::Pixels, deserializer)
}
pub fn de_points<'de, D>(deserializer: D) -> Result<Dimension, D::Error>
where
D: Deserializer<'de>,
{
de_dimension(DefaultUnit::Points, deserializer)
}
pub fn de_percent<'de, D>(deserializer: D) -> Result<Dimension, D::Error>
where
D: Deserializer<'de>,
{
de_dimension(DefaultUnit::Percent, deserializer)
}
pub fn de_cells<'de, D>(deserializer: D) -> Result<Dimension, D::Error>
where
D: Deserializer<'de>,
{
de_dimension(DefaultUnit::Cells, deserializer)
}

View File

@ -1,9 +1,10 @@
use crate::*;
use std::path::PathBuf;
use wezterm_dynamic::{FromDynamic, ToDynamic};
/// Configures an instance of a multiplexer that can be communicated
/// with via a unix domain socket
#[derive(Debug, Clone, Deserialize, Serialize)]
#[derive(Debug, Clone, FromDynamic, ToDynamic)]
pub struct UnixDomain {
/// The name of this specific domain. Must be unique amongst
/// all types of domain in the configuration file.
@ -14,12 +15,12 @@ pub struct UnixDomain {
pub socket_path: Option<PathBuf>,
/// If true, connect to this domain automatically at startup
#[serde(default)]
#[dynamic(default)]
pub connect_automatically: bool,
/// If true, do not attempt to start this server if we try and fail to
/// connect to it.
#[serde(default)]
#[dynamic(default)]
pub no_serve_automatically: bool,
/// If we decide that we need to start the server, the command to run
@ -40,20 +41,19 @@ pub struct UnixDomain {
/// system, but is useful for example when running the
/// server inside a WSL container but with the socket
/// on the host NTFS volume.
#[serde(default)]
#[dynamic(default)]
pub skip_permissions_check: bool,
#[serde(default = "default_read_timeout")]
#[dynamic(default = "default_read_timeout")]
pub read_timeout: Duration,
#[serde(default = "default_write_timeout")]
#[dynamic(default = "default_write_timeout")]
pub write_timeout: Duration,
/// Don't use default_local_echo_threshold_ms() here to
/// disable the predictive echo for Unix domains by default.
pub local_echo_threshold_ms: Option<u64>,
}
impl_lua_conversion!(UnixDomain);
impl Default for UnixDomain {
fn default() -> Self {

View File

@ -1,7 +1,9 @@
use crate::*;
use luahelper::impl_lua_conversion_dynamic;
use std::collections::HashMap;
use wezterm_dynamic::{FromDynamic, ToDynamic};
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
#[derive(Default, Debug, Clone, FromDynamic, ToDynamic)]
pub struct WslDomain {
pub name: String,
pub distribution: Option<String>,
@ -9,7 +11,7 @@ pub struct WslDomain {
pub default_cwd: Option<PathBuf>,
pub default_prog: Option<Vec<String>>,
}
impl_lua_conversion!(WslDomain);
impl_lua_conversion_dynamic!(WslDomain);
impl WslDomain {
pub fn default_domains() -> Vec<WslDomain> {
@ -33,7 +35,7 @@ impl WslDomain {
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WslDistro {
pub name: String,
pub state: String,

View File

@ -14,3 +14,4 @@ serde = {version="1.0", features = ["rc", "derive"]}
serde_json = "1.0"
strsim = "0.10"
thiserror = "1.0"
wezterm-dynamic = { path = "../wezterm-dynamic" }

View File

@ -1,9 +1,11 @@
#![macro_use]
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use wezterm_dynamic::{FromDynamic, ToDynamic, Value as DynValue};
mod serde_lua;
pub use mlua;
use mlua::{ToLua, Value as LuaValue};
pub use serde_lua::from_lua_value;
pub use serde_lua::ser::to_lua_value;
@ -36,9 +38,133 @@ macro_rules! impl_lua_conversion {
};
}
pub use serde_lua::ValueWrapper;
#[macro_export]
macro_rules! impl_lua_conversion_dynamic {
($struct:ident) => {
impl<'lua> $crate::mlua::ToLua<'lua> for $struct {
fn to_lua(
self,
lua: &'lua $crate::mlua::Lua,
) -> Result<$crate::mlua::Value<'lua>, $crate::mlua::Error> {
use wezterm_dynamic::ToDynamic;
let value = self.to_dynamic();
$crate::dynamic_to_lua_value(lua, value)
}
}
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub struct JsonLua(pub serde_json::Value);
impl_lua_conversion!(JsonLua);
impl<'lua> $crate::mlua::FromLua<'lua> for $struct {
fn from_lua(
value: $crate::mlua::Value<'lua>,
_lua: &'lua $crate::mlua::Lua,
) -> Result<Self, $crate::mlua::Error> {
use wezterm_dynamic::FromDynamic;
let lua_type = value.type_name();
let value = $crate::lua_value_to_dynamic(value)?;
$struct::from_dynamic(&value, Default::default()).map_err(|e| {
$crate::mlua::Error::FromLuaConversionError {
from: lua_type,
to: stringify!($struct),
message: Some(e.to_string()),
}
})
}
}
};
}
pub fn dynamic_to_lua_value<'lua>(
lua: &'lua mlua::Lua,
value: DynValue,
) -> mlua::Result<mlua::Value> {
Ok(match value {
DynValue::Null => LuaValue::Nil,
DynValue::Bool(b) => LuaValue::Boolean(b),
DynValue::String(s) => s.to_lua(lua)?,
DynValue::U64(u) => u.to_lua(lua)?,
DynValue::F64(u) => u.to_lua(lua)?,
DynValue::I64(u) => u.to_lua(lua)?,
DynValue::Array(array) => {
let table = lua.create_table()?;
for (idx, value) in array.into_iter().enumerate() {
table.set(idx + 1, dynamic_to_lua_value(lua, value)?)?;
}
LuaValue::Table(table)
}
DynValue::Object(object) => {
let table = lua.create_table()?;
for (key, value) in object.into_iter() {
table.set(
dynamic_to_lua_value(lua, key)?,
dynamic_to_lua_value(lua, value)?,
)?;
}
LuaValue::Table(table)
}
})
}
pub fn lua_value_to_dynamic(value: LuaValue) -> mlua::Result<DynValue> {
Ok(match value {
LuaValue::Nil => DynValue::Null,
LuaValue::String(s) => DynValue::String(s.to_str()?.to_string()),
LuaValue::Boolean(b) => DynValue::Bool(b),
LuaValue::Integer(i) => DynValue::I64(i),
LuaValue::Number(i) => DynValue::F64(i.into()),
LuaValue::LightUserData(_) | LuaValue::UserData(_) => {
return Err(mlua::Error::FromLuaConversionError {
from: "userdata",
to: "wezterm_dynamic::Value",
message: None,
})
}
LuaValue::Function(_) => {
return Err(mlua::Error::FromLuaConversionError {
from: "function",
to: "wezterm_dynamic::Value",
message: None,
})
}
LuaValue::Thread(_) => {
return Err(mlua::Error::FromLuaConversionError {
from: "thread",
to: "wezterm_dynamic::Value",
message: None,
})
}
LuaValue::Error(e) => return Err(e),
LuaValue::Table(table) => {
if let Ok(true) = table.contains_key(1) {
let mut array = vec![];
for value in table.sequence_values() {
array.push(lua_value_to_dynamic(value?)?);
}
DynValue::Array(array.into())
} else {
let mut obj = BTreeMap::default();
for pair in table.pairs::<LuaValue, LuaValue>() {
let (key, value) = pair?;
obj.insert(lua_value_to_dynamic(key)?, lua_value_to_dynamic(value)?);
}
DynValue::Object(obj.into())
}
}
})
}
pub fn from_lua_value_dynamic<T: FromDynamic>(value: LuaValue) -> mlua::Result<T> {
let type_name = value.type_name();
let value = lua_value_to_dynamic(value)?;
T::from_dynamic(&value, Default::default()).map_err(|e| mlua::Error::FromLuaConversionError {
from: type_name,
to: "Rust Type",
message: Some(e.to_string()),
})
}
#[derive(FromDynamic, ToDynamic)]
pub struct ValueLua {
pub value: wezterm_dynamic::Value,
}
impl_lua_conversion_dynamic!(ValueLua);
pub use serde_lua::ValueWrapper;

View File

@ -41,6 +41,7 @@ thiserror = "1.0"
unicode-segmentation = "1.8"
url = "2"
wezterm-ssh = { path = "../wezterm-ssh" }
wezterm-dynamic = { path = "../wezterm-dynamic" }
wezterm-term = { path = "../term", features=["use_serde"] }
flume = "0.10"

View File

@ -1,21 +1,26 @@
use luahelper::impl_lua_conversion;
use luahelper::impl_lua_conversion_dynamic;
use rangeset::RangeSet;
use serde::{Deserialize, Serialize};
use std::ops::Range;
use termwiz::surface::{SequenceNo, SEQ_ZERO};
use wezterm_dynamic::{FromDynamic, ToDynamic};
use wezterm_term::{Line, StableRowIndex, Terminal};
/// Describes the location of the cursor
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Deserialize, Serialize)]
#[derive(
Debug, Default, Copy, Clone, Eq, PartialEq, Deserialize, Serialize, FromDynamic, ToDynamic,
)]
pub struct StableCursorPosition {
pub x: usize,
pub y: StableRowIndex,
pub shape: termwiz::surface::CursorShape,
pub visibility: termwiz::surface::CursorVisibility,
}
impl_lua_conversion!(StableCursorPosition);
impl_lua_conversion_dynamic!(StableCursorPosition);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Deserialize, Serialize)]
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Default, Deserialize, Serialize, FromDynamic, ToDynamic,
)]
pub struct RenderableDimensions {
/// The viewport width
pub cols: usize,
@ -34,7 +39,7 @@ pub struct RenderableDimensions {
/// expressed as a stable index.
pub scrollback_top: StableRowIndex,
}
impl_lua_conversion!(RenderableDimensions);
impl_lua_conversion_dynamic!(RenderableDimensions);
/// Implements Pane::get_cursor_position for Terminal
pub fn terminal_get_cursor_position(term: &mut Terminal) -> StableCursorPosition {

View File

@ -9,7 +9,7 @@ edition = "2021"
libc = "0.2"
log = "0.4"
luahelper = { path = "../luahelper" }
serde = {version="1.0", features = ["rc", "derive"]}
serde = {version="1.0", features = ["derive"]}
[target."cfg(windows)".dependencies]
ntapi = "0.3"

View File

@ -31,6 +31,7 @@ unicode-segmentation = "1.8"
unicode-width = "0.1"
url = "2"
wezterm-bidi = { path = "../bidi" }
wezterm-dynamic = { path = "../wezterm-dynamic" }
[dev-dependencies]
env_logger = "0.9"

View File

@ -8,11 +8,12 @@ use super::VisibleRowIndex;
#[cfg(feature = "use_serde")]
use serde::{Deserialize, Serialize};
use std::time::{Duration, Instant};
use wezterm_dynamic::{FromDynamic, ToDynamic};
pub use termwiz::input::{KeyCode, Modifiers as KeyModifiers};
#[cfg_attr(feature = "use_serde", derive(Deserialize, Serialize))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, FromDynamic, ToDynamic)]
pub enum MouseButton {
Left,
Middle,

View File

@ -40,6 +40,7 @@ ucd-trie = "0.1"
vtparse = { version="0.6", path="../vtparse" }
wezterm-bidi = { path = "../bidi", version="0.1" }
wezterm-color-types = { path = "../color-types", version="0.1" }
wezterm-dynamic = { path = "../wezterm-dynamic" }
[features]
widgets = ["cassowary", "fnv"]

View File

@ -8,6 +8,7 @@ use crate::widechar_width::WcWidth;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::mem;
use std::sync::Arc;
use wezterm_dynamic::{FromDynamic, ToDynamic};
#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
@ -164,7 +165,7 @@ impl Default for SemanticType {
/// using an alternative color. Some terminals implement `Intensity::Half`
/// as a dimmer color variant.
#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromDynamic, ToDynamic)]
#[repr(u16)]
pub enum Intensity {
Normal = 0,
@ -180,7 +181,7 @@ impl Default for Intensity {
/// Specify just how underlined you want your `Cell` to be
#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromDynamic, ToDynamic)]
#[repr(u16)]
pub enum Underline {
/// The cell is not underlined
@ -214,7 +215,7 @@ impl Into<bool> for Underline {
/// Specify whether you want to slowly or rapidly annoy your users
#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromDynamic, ToDynamic)]
#[repr(u16)]
pub enum Blink {
None = 0,
@ -939,7 +940,7 @@ pub fn grapheme_column_width(s: &str, version: Option<UnicodeVersion>) -> usize
/// Each variant specifies one of the possible attributes; the corresponding
/// value holds the new value to be used for that attribute.
#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Eq, PartialEq)]
#[derive(Debug, Clone, Eq, PartialEq, FromDynamic, ToDynamic)]
pub enum AttributeChange {
Intensity(Intensity),
Underline(Underline),

View File

@ -6,8 +6,9 @@ use num_derive::*;
#[cfg(feature = "use_serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub use wezterm_color_types::{LinearRgba, SrgbaTuple};
use wezterm_dynamic::{FromDynamic, FromDynamicOptions, ToDynamic, Value};
#[derive(Debug, Clone, Copy, FromPrimitive, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, FromPrimitive, PartialEq, Eq, FromDynamic, ToDynamic)]
#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
#[repr(u8)]
/// These correspond to the classic ANSI color indices and are
@ -200,6 +201,23 @@ impl<'de> Deserialize<'de> for RgbColor {
}
}
impl ToDynamic for RgbColor {
fn to_dynamic(&self) -> Value {
self.to_rgb_string().to_dynamic()
}
}
impl FromDynamic for RgbColor {
fn from_dynamic(
value: &Value,
options: FromDynamicOptions,
) -> Result<Self, wezterm_dynamic::Error> {
let s = String::from_dynamic(value, options)?;
Ok(RgbColor::from_named_or_rgb_string(&s)
.ok_or_else(|| format!("unknown color name: {}", s))?)
}
}
/// An index into the fixed color palette.
pub type PaletteIndex = u8;
@ -238,7 +256,7 @@ impl From<RgbColor> for ColorSpec {
/// TrueColor value, allowing a fallback to a more traditional palette
/// index if TrueColor is not available.
#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, FromDynamic, ToDynamic)]
pub enum ColorAttribute {
/// Use RgbColor when supported, falling back to the specified PaletteIndex.
TrueColorWithPaletteFallback(RgbColor, PaletteIndex),

View File

@ -12,9 +12,10 @@ use std::collections::HashMap;
use std::fmt::{Display, Error as FmtError, Formatter};
use std::ops::Range;
use std::sync::Arc;
use wezterm_dynamic::{FromDynamic, FromDynamicOptions, ToDynamic, Value};
#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, FromDynamic, ToDynamic)]
pub struct Hyperlink {
params: HashMap<String, String>,
uri: String,
@ -124,7 +125,7 @@ impl Display for Hyperlink {
/// The Rule struct is configuration that is passed to the terminal
/// and is evaluated when processing mouse hover events.
#[cfg_attr(feature = "use_serde", derive(Deserialize, Serialize))]
#[derive(Debug, Clone)]
#[derive(Debug, Clone, FromDynamic, ToDynamic)]
pub struct Rule {
/// The compiled regex for the rule. This is used to match
/// against a line of text from the screen (typically the line
@ -136,6 +137,7 @@ pub struct Rule {
serialize_with = "serialize_regex"
)
)]
#[dynamic(into = "RegexWrap", try_from = "RegexWrap")]
regex: Regex,
/// The format string that defines how to transform the matched
/// text into a URL. For example, a format string of `$0` expands
@ -150,6 +152,36 @@ pub struct Rule {
format: String,
}
struct RegexWrap(Regex);
impl FromDynamic for RegexWrap {
fn from_dynamic(
value: &Value,
options: FromDynamicOptions,
) -> std::result::Result<RegexWrap, wezterm_dynamic::Error> {
let s = String::from_dynamic(value, options)?;
Ok(RegexWrap(Regex::new(&s).map_err(|e| e.to_string())?))
}
}
impl From<&Regex> for RegexWrap {
fn from(regex: &Regex) -> RegexWrap {
RegexWrap(regex.clone())
}
}
impl Into<Regex> for RegexWrap {
fn into(self) -> Regex {
self.0
}
}
impl ToDynamic for RegexWrap {
fn to_dynamic(&self) -> Value {
self.0.to_string().to_dynamic()
}
}
#[cfg(feature = "use_serde")]
fn deserialize_regex<'de, D>(deserializer: D) -> std::result::Result<Regex, D::Error>
where

View File

@ -7,6 +7,7 @@ use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::cmp::min;
use unicode_segmentation::UnicodeSegmentation;
use wezterm_dynamic::{FromDynamic, ToDynamic};
pub mod change;
pub mod line;
@ -31,7 +32,7 @@ pub enum Position {
}
#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromDynamic, ToDynamic)]
pub enum CursorVisibility {
Hidden,
Visible,
@ -44,7 +45,7 @@ impl Default for CursorVisibility {
}
#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromDynamic, ToDynamic)]
pub enum CursorShape {
Default,
BlinkingBlock,

View File

@ -71,6 +71,7 @@ url = "2"
walkdir = "2"
wezterm-bidi = { path = "../bidi" }
wezterm-client = { path = "../wezterm-client" }
wezterm-dynamic = { path = "../wezterm-dynamic" }
wezterm-font = { path = "../wezterm-font" }
wezterm-gui-subcommands = { path = "../wezterm-gui-subcommands" }
wezterm-mux-server-impl = { path = "../wezterm-mux-server-impl" }

View File

@ -8,7 +8,7 @@ use luahelper::*;
use mlua::{UserData, UserDataMethods};
use mux::window::WindowId as MuxWindowId;
use mux::Mux;
use serde::*;
use wezterm_dynamic::{FromDynamic, ToDynamic};
use wezterm_toast_notification::ToastNotification;
use window::{Connection, ConnectionOps, DeadKeyStatus, WindowOps, WindowState};
@ -60,14 +60,14 @@ impl UserData for GuiWin {
.map_err(|e| anyhow::anyhow!("{:#}", e))
.map_err(luaerr)?;
#[derive(Serialize, Deserialize)]
#[derive(FromDynamic, ToDynamic)]
struct Dims {
pixel_width: usize,
pixel_height: usize,
dpi: usize,
is_full_screen: bool,
}
impl_lua_conversion!(Dims);
impl_lua_conversion_dynamic!(Dims);
let dims = Dims {
pixel_width: dims.pixel_width,
@ -125,12 +125,12 @@ impl UserData for GuiWin {
.map_err(|e| anyhow::anyhow!("{:#}", e))
.map_err(luaerr)?;
let wrap = JsonLua(overrides);
let wrap = ValueLua { value: overrides };
Ok(wrap)
});
methods.add_method("set_config_overrides", |_, this, value: JsonLua| {
methods.add_method("set_config_overrides", |_, this, value: ValueLua| {
this.window
.notify(TermWindowNotif::SetConfigOverrides(value.0));
.notify(TermWindowNotif::SetConfigOverrides(value.value));
Ok(())
});
methods.add_async_method("leader_is_active", |_, this, _: ()| async move {

View File

@ -112,8 +112,8 @@ pub enum TermWindowNotif {
name: String,
again: bool,
},
GetConfigOverrides(Sender<serde_json::Value>),
SetConfigOverrides(serde_json::Value),
GetConfigOverrides(Sender<wezterm_dynamic::Value>),
SetConfigOverrides(wezterm_dynamic::Value),
CancelOverlayForPane(PaneId),
CancelOverlayForTab {
tab_id: TabId,
@ -317,7 +317,7 @@ enum EventState {
pub struct TermWindow {
pub window: Option<Window>,
pub config: ConfigHandle,
pub config_overrides: serde_json::Value,
pub config_overrides: wezterm_dynamic::Value,
os_parameters: Option<parameters::Parameters>,
/// When we most recently received keyboard focus
focused: Option<Instant>,
@ -748,7 +748,7 @@ impl TermWindow {
window: None,
window_background,
config: config.clone(),
config_overrides: serde_json::Value::default(),
config_overrides: wezterm_dynamic::Value::default(),
palette: None,
focused: None,
mux_window_id,

View File

@ -11,3 +11,4 @@ bitflags = "1.3"
euclid = "0.22"
lazy_static = "1.4"
serde = {version="1.0", features = ["rc", "derive"]}
wezterm-dynamic = {path="../wezterm-dynamic"}

View File

@ -4,6 +4,7 @@ use std::collections::HashMap;
use std::convert::TryFrom;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use wezterm_dynamic::{FromDynamic, ToDynamic};
pub struct PixelUnit;
pub struct ScreenPixelUnit;
@ -14,7 +15,19 @@ pub type ScreenPoint = euclid::Point2D<isize, ScreenPixelUnit>;
/// Which key is pressed. Not all of these are probable to appear
/// on most systems. A lot of this list is @wez trawling docs and
/// making an entry for things that might be possible in this first pass.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, Ord, PartialOrd)]
#[derive(
Debug,
Clone,
PartialEq,
Eq,
Hash,
Deserialize,
Serialize,
Ord,
PartialOrd,
FromDynamic,
ToDynamic,
)]
pub enum KeyCode {
/// The decoded unicode character
Char(char),
@ -438,7 +451,8 @@ impl ToString for KeyCode {
}
bitflags! {
#[derive(Default, Deserialize, Serialize)]
#[derive(Default, Deserialize, Serialize, FromDynamic, ToDynamic)]
#[dynamic(into="String", try_from="String")]
pub struct Modifiers: u8 {
const NONE = 0;
const SHIFT = 1<<1;
@ -451,6 +465,42 @@ bitflags! {
}
}
impl TryFrom<String> for Modifiers {
type Error = String;
fn try_from(s: String) -> Result<Modifiers, String> {
let mut mods = Modifiers::NONE;
for ele in s.split('|') {
// Allow for whitespace; debug printing Modifiers includes spaces
// around the `|` so it is desirable to be able to reverse that
// encoding here.
let ele = ele.trim();
if ele == "SHIFT" {
mods |= Modifiers::SHIFT;
} else if ele == "ALT" || ele == "OPT" || ele == "META" {
mods |= Modifiers::ALT;
} else if ele == "CTRL" {
mods |= Modifiers::CTRL;
} else if ele == "SUPER" || ele == "CMD" || ele == "WIN" {
mods |= Modifiers::SUPER;
} else if ele == "LEADER" {
mods |= Modifiers::LEADER;
} else if ele == "NONE" || ele == "" {
mods |= Modifiers::NONE;
} else {
return Err(format!("invalid modifier name {} in {}", ele, s));
}
}
Ok(mods)
}
}
impl Into<String> for &Modifiers {
fn into(self) -> String {
self.to_string()
}
}
impl ToString for Modifiers {
fn to_string(&self) -> String {
let mut s = String::new();
@ -482,7 +532,20 @@ impl ToString for Modifiers {
/// These keycodes identify keys based on their physical
/// position on an ANSI-standard US keyboard.
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Hash, Copy, Ord, PartialOrd)]
#[derive(
Debug,
Deserialize,
Serialize,
Clone,
PartialEq,
Eq,
Hash,
Copy,
Ord,
PartialOrd,
FromDynamic,
ToDynamic,
)]
pub enum PhysKeyCode {
A,
B,
@ -1136,8 +1199,9 @@ impl KeyEvent {
}
bitflags! {
#[derive(Deserialize, Serialize)]
#[derive(Deserialize, Serialize, FromDynamic, ToDynamic)]
#[serde(try_from = "String")]
#[dynamic(try_from = "String")]
pub struct WindowDecorations: u8 {
const TITLE = 1;
const RESIZE = 2;