| 1 |
use crate::bar::{BlockCommand, BlockConfig};
|
| 2 |
use crate::errors::ConfigError;
|
| 3 |
use crate::keyboard::handlers::Key;
|
| 4 |
use crate::keyboard::keycodes;
|
| 5 |
use crate::keyboard::{Arg, KeyAction};
|
| 6 |
use serde::Deserialize;
|
| 7 |
use std::collections::HashMap;
|
| 8 |
use x11rb::protocol::xproto::{KeyButMask, Keycode};
|
| 9 |
|
| 10 |
#[derive(Debug, Deserialize)]
|
| 11 |
pub enum ModKey {
|
| 12 |
Mod1,
|
| 13 |
Mod2,
|
| 14 |
Mod3,
|
| 15 |
Mod4,
|
| 16 |
Mod5,
|
| 17 |
Shift,
|
| 18 |
Control,
|
| 19 |
}
|
| 20 |
|
| 21 |
impl ModKey {
|
| 22 |
fn to_keybut_mask(&self) -> KeyButMask {
|
| 23 |
match self {
|
| 24 |
ModKey::Mod1 => KeyButMask::MOD1,
|
| 25 |
ModKey::Mod2 => KeyButMask::MOD2,
|
| 26 |
ModKey::Mod3 => KeyButMask::MOD3,
|
| 27 |
ModKey::Mod4 => KeyButMask::MOD4,
|
| 28 |
ModKey::Mod5 => KeyButMask::MOD5,
|
| 29 |
ModKey::Shift => KeyButMask::SHIFT,
|
| 30 |
ModKey::Control => KeyButMask::CONTROL,
|
| 31 |
}
|
| 32 |
}
|
| 33 |
}
|
| 34 |
|
| 35 |
#[rustfmt::skip]
|
| 36 |
#[derive(Debug, Deserialize)]
|
| 37 |
pub enum KeyData {
|
| 38 |
Return,
|
| 39 |
Q,
|
| 40 |
Escape,
|
| 41 |
Space,
|
| 42 |
Tab,
|
| 43 |
Backspace,
|
| 44 |
Delete,
|
| 45 |
F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
|
| 46 |
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, R, S, T, U, V, W, X, Y, Z,
|
| 47 |
Key0,
|
| 48 |
Key1,
|
| 49 |
Key2,
|
| 50 |
Key3,
|
| 51 |
Key4,
|
| 52 |
Key5,
|
| 53 |
Key6,
|
| 54 |
Key7,
|
| 55 |
Key8,
|
| 56 |
Key9,
|
| 57 |
Left,
|
| 58 |
Right,
|
| 59 |
Up,
|
| 60 |
Down,
|
| 61 |
Home,
|
| 62 |
End,
|
| 63 |
PageUp,
|
| 64 |
PageDown,
|
| 65 |
Insert,
|
| 66 |
Minus,
|
| 67 |
Equal,
|
| 68 |
BracketLeft,
|
| 69 |
BracketRight,
|
| 70 |
Semicolon,
|
| 71 |
Apostrophe,
|
| 72 |
Grave,
|
| 73 |
Backslash,
|
| 74 |
Comma,
|
| 75 |
Period,
|
| 76 |
Slash,
|
| 77 |
}
|
| 78 |
|
| 79 |
impl KeyData {
|
| 80 |
fn to_keycode(&self) -> Keycode {
|
| 81 |
match self {
|
| 82 |
KeyData::Return => keycodes::RETURN,
|
| 83 |
KeyData::Q => keycodes::Q,
|
| 84 |
KeyData::Escape => keycodes::ESCAPE,
|
| 85 |
KeyData::Space => keycodes::SPACE,
|
| 86 |
KeyData::Tab => keycodes::TAB,
|
| 87 |
KeyData::Backspace => keycodes::BACKSPACE,
|
| 88 |
KeyData::Delete => keycodes::DELETE,
|
| 89 |
KeyData::F1 => keycodes::F1,
|
| 90 |
KeyData::F2 => keycodes::F2,
|
| 91 |
KeyData::F3 => keycodes::F3,
|
| 92 |
KeyData::F4 => keycodes::F4,
|
| 93 |
KeyData::F5 => keycodes::F5,
|
| 94 |
KeyData::F6 => keycodes::F6,
|
| 95 |
KeyData::F7 => keycodes::F7,
|
| 96 |
KeyData::F8 => keycodes::F8,
|
| 97 |
KeyData::F9 => keycodes::F9,
|
| 98 |
KeyData::F10 => keycodes::F10,
|
| 99 |
KeyData::F11 => keycodes::F11,
|
| 100 |
KeyData::F12 => keycodes::F12,
|
| 101 |
KeyData::A => keycodes::A,
|
| 102 |
KeyData::B => keycodes::B,
|
| 103 |
KeyData::C => keycodes::C,
|
| 104 |
KeyData::D => keycodes::D,
|
| 105 |
KeyData::E => keycodes::E,
|
| 106 |
KeyData::F => keycodes::F,
|
| 107 |
KeyData::G => keycodes::G,
|
| 108 |
KeyData::H => keycodes::H,
|
| 109 |
KeyData::I => keycodes::I,
|
| 110 |
KeyData::J => keycodes::J,
|
| 111 |
KeyData::K => keycodes::K,
|
| 112 |
KeyData::L => keycodes::L,
|
| 113 |
KeyData::M => keycodes::M,
|
| 114 |
KeyData::N => keycodes::N,
|
| 115 |
KeyData::O => keycodes::O,
|
| 116 |
KeyData::P => keycodes::P,
|
| 117 |
KeyData::R => keycodes::R,
|
| 118 |
KeyData::S => keycodes::S,
|
| 119 |
KeyData::T => keycodes::T,
|
| 120 |
KeyData::U => keycodes::U,
|
| 121 |
KeyData::V => keycodes::V,
|
| 122 |
KeyData::W => keycodes::W,
|
| 123 |
KeyData::X => keycodes::X,
|
| 124 |
KeyData::Y => keycodes::Y,
|
| 125 |
KeyData::Z => keycodes::Z,
|
| 126 |
KeyData::Key0 => keycodes::KEY_0,
|
| 127 |
KeyData::Key1 => keycodes::KEY_1,
|
| 128 |
KeyData::Key2 => keycodes::KEY_2,
|
| 129 |
KeyData::Key3 => keycodes::KEY_3,
|
| 130 |
KeyData::Key4 => keycodes::KEY_4,
|
| 131 |
KeyData::Key5 => keycodes::KEY_5,
|
| 132 |
KeyData::Key6 => keycodes::KEY_6,
|
| 133 |
KeyData::Key7 => keycodes::KEY_7,
|
| 134 |
KeyData::Key8 => keycodes::KEY_8,
|
| 135 |
KeyData::Key9 => keycodes::KEY_9,
|
| 136 |
KeyData::Left => keycodes::LEFT,
|
| 137 |
KeyData::Right => keycodes::RIGHT,
|
| 138 |
KeyData::Up => keycodes::UP,
|
| 139 |
KeyData::Down => keycodes::DOWN,
|
| 140 |
KeyData::Home => keycodes::HOME,
|
| 141 |
KeyData::End => keycodes::END,
|
| 142 |
KeyData::PageUp => keycodes::PAGE_UP,
|
| 143 |
KeyData::PageDown => keycodes::PAGE_DOWN,
|
| 144 |
KeyData::Insert => keycodes::INSERT,
|
| 145 |
KeyData::Minus => keycodes::MINUS,
|
| 146 |
KeyData::Equal => keycodes::EQUAL,
|
| 147 |
KeyData::BracketLeft => keycodes::LEFT_BRACKET,
|
| 148 |
KeyData::BracketRight => keycodes::RIGHT_BRACKET,
|
| 149 |
KeyData::Semicolon => keycodes::SEMICOLON,
|
| 150 |
KeyData::Apostrophe => keycodes::APOSTROPHE,
|
| 151 |
KeyData::Grave => keycodes::GRAVE,
|
| 152 |
KeyData::Backslash => keycodes::BACKSLASH,
|
| 153 |
KeyData::Comma => keycodes::COMMA,
|
| 154 |
KeyData::Period => keycodes::PERIOD,
|
| 155 |
KeyData::Slash => keycodes::SLASH,
|
| 156 |
}
|
| 157 |
}
|
| 158 |
}
|
| 159 |
|
| 160 |
fn preprocess_variables(input: &str) -> Result<String, ConfigError> {
|
| 161 |
let mut variables: HashMap<String, String> = HashMap::new();
|
| 162 |
let mut result = String::new();
|
| 163 |
|
| 164 |
for line in input.lines() {
|
| 165 |
let trimmed = line.trim();
|
| 166 |
|
| 167 |
if trimmed.starts_with("#DEFINE") {
|
| 168 |
let rest = trimmed.strip_prefix("#DEFINE").unwrap().trim();
|
| 169 |
|
| 170 |
if let Some(eq_pos) = rest.find('=') {
|
| 171 |
let var_name = rest[..eq_pos].trim();
|
| 172 |
let value = rest[eq_pos + 1..].trim().trim_end_matches(',');
|
| 173 |
|
| 174 |
if !var_name.starts_with('$') {
|
| 175 |
return Err(ConfigError::InvalidVariableName(var_name.to_string()));
|
| 176 |
}
|
| 177 |
|
| 178 |
variables.insert(var_name.to_string(), value.to_string());
|
| 179 |
} else {
|
| 180 |
return Err(ConfigError::InvalidDefine(trimmed.to_string()));
|
| 181 |
}
|
| 182 |
|
| 183 |
result.push('\n');
|
| 184 |
} else {
|
| 185 |
let mut processed_line = line.to_string();
|
| 186 |
for (var_name, value) in &variables {
|
| 187 |
processed_line = processed_line.replace(var_name, value);
|
| 188 |
}
|
| 189 |
result.push_str(&processed_line);
|
| 190 |
result.push('\n');
|
| 191 |
}
|
| 192 |
}
|
| 193 |
|
| 194 |
for line in result.lines() {
|
| 195 |
if let Some(var_start) = line.find('$') {
|
| 196 |
let rest = &line[var_start..];
|
| 197 |
let var_end = rest[1..]
|
| 198 |
.find(|c: char| !c.is_alphanumeric() && c != '_')
|
| 199 |
.unwrap_or(rest.len() - 1)
|
| 200 |
+ 1;
|
| 201 |
let undefined_var = &rest[..var_end];
|
| 202 |
return Err(ConfigError::UndefinedVariable(undefined_var.to_string()));
|
| 203 |
}
|
| 204 |
}
|
| 205 |
Ok(result)
|
| 206 |
}
|
| 207 |
|
| 208 |
pub fn parse_config(input: &str) -> Result<crate::Config, ConfigError> {
|
| 209 |
let preprocessed = preprocess_variables(input)?;
|
| 210 |
let config_data: ConfigData = ron::from_str(&preprocessed)?;
|
| 211 |
config_data_to_config(config_data)
|
| 212 |
}
|
| 213 |
|
| 214 |
#[derive(Debug, Deserialize)]
|
| 215 |
struct LayoutSymbolOverrideData {
|
| 216 |
name: String,
|
| 217 |
symbol: String,
|
| 218 |
}
|
| 219 |
|
| 220 |
#[derive(Debug, Deserialize)]
|
| 221 |
struct ConfigData {
|
| 222 |
border_width: u32,
|
| 223 |
border_focused: u32,
|
| 224 |
border_unfocused: u32,
|
| 225 |
font: String,
|
| 226 |
|
| 227 |
gaps_enabled: bool,
|
| 228 |
gap_inner_horizontal: u32,
|
| 229 |
gap_inner_vertical: u32,
|
| 230 |
gap_outer_horizontal: u32,
|
| 231 |
gap_outer_vertical: u32,
|
| 232 |
|
| 233 |
terminal: String,
|
| 234 |
modkey: ModKey,
|
| 235 |
|
| 236 |
tags: Vec<String>,
|
| 237 |
#[serde(default)]
|
| 238 |
layout_symbols: Vec<LayoutSymbolOverrideData>,
|
| 239 |
keybindings: Vec<KeybindingData>,
|
| 240 |
status_blocks: Vec<StatusBlockData>,
|
| 241 |
|
| 242 |
scheme_normal: ColorSchemeData,
|
| 243 |
scheme_occupied: ColorSchemeData,
|
| 244 |
scheme_selected: ColorSchemeData,
|
| 245 |
}
|
| 246 |
|
| 247 |
#[derive(Debug, Deserialize)]
|
| 248 |
struct KeybindingData {
|
| 249 |
modifiers: Vec<ModKey>,
|
| 250 |
key: KeyData,
|
| 251 |
action: KeyAction,
|
| 252 |
#[serde(default)]
|
| 253 |
arg: ArgData,
|
| 254 |
}
|
| 255 |
|
| 256 |
#[derive(Debug, Deserialize)]
|
| 257 |
#[serde(untagged)]
|
| 258 |
enum ArgData {
|
| 259 |
None,
|
| 260 |
String(String),
|
| 261 |
Int(i32),
|
| 262 |
Array(Vec<String>),
|
| 263 |
}
|
| 264 |
|
| 265 |
impl Default for ArgData {
|
| 266 |
fn default() -> Self {
|
| 267 |
ArgData::None
|
| 268 |
}
|
| 269 |
}
|
| 270 |
|
| 271 |
#[derive(Debug, Deserialize)]
|
| 272 |
struct StatusBlockData {
|
| 273 |
format: String,
|
| 274 |
command: String,
|
| 275 |
#[serde(default)]
|
| 276 |
command_arg: Option<String>,
|
| 277 |
#[serde(default)]
|
| 278 |
battery_formats: Option<BatteryFormats>,
|
| 279 |
interval_secs: u64,
|
| 280 |
color: u32,
|
| 281 |
underline: bool,
|
| 282 |
}
|
| 283 |
|
| 284 |
#[derive(Debug, Deserialize)]
|
| 285 |
struct BatteryFormats {
|
| 286 |
charging: String,
|
| 287 |
discharging: String,
|
| 288 |
full: String,
|
| 289 |
}
|
| 290 |
|
| 291 |
#[derive(Debug, Deserialize)]
|
| 292 |
struct ColorSchemeData {
|
| 293 |
foreground: u32,
|
| 294 |
background: u32,
|
| 295 |
underline: u32,
|
| 296 |
}
|
| 297 |
|
| 298 |
fn config_data_to_config(data: ConfigData) -> Result<crate::Config, ConfigError> {
|
| 299 |
let modkey = data.modkey.to_keybut_mask();
|
| 300 |
|
| 301 |
let mut keybindings = Vec::new();
|
| 302 |
for kb_data in data.keybindings {
|
| 303 |
let modifiers = kb_data
|
| 304 |
.modifiers
|
| 305 |
.iter()
|
| 306 |
.map(|m| m.to_keybut_mask())
|
| 307 |
.collect();
|
| 308 |
|
| 309 |
let key = kb_data.key.to_keycode();
|
| 310 |
let action = kb_data.action;
|
| 311 |
let arg = arg_data_to_arg(kb_data.arg)?;
|
| 312 |
|
| 313 |
keybindings.push(Key::new(modifiers, key, action, arg));
|
| 314 |
}
|
| 315 |
|
| 316 |
let mut status_blocks = Vec::new();
|
| 317 |
for block_data in data.status_blocks {
|
| 318 |
let command = match block_data.command.as_str() {
|
| 319 |
"DateTime" => {
|
| 320 |
let fmt = block_data
|
| 321 |
.command_arg
|
| 322 |
.ok_or_else(|| ConfigError::MissingCommandArg {
|
| 323 |
command: "DateTime".to_string(),
|
| 324 |
field: "command_arg".to_string(),
|
| 325 |
})?;
|
| 326 |
BlockCommand::DateTime(fmt)
|
| 327 |
}
|
| 328 |
"Shell" => {
|
| 329 |
let cmd = block_data
|
| 330 |
.command_arg
|
| 331 |
.ok_or_else(|| ConfigError::MissingCommandArg {
|
| 332 |
command: "Shell".to_string(),
|
| 333 |
field: "command_arg".to_string(),
|
| 334 |
})?;
|
| 335 |
BlockCommand::Shell(cmd)
|
| 336 |
}
|
| 337 |
"Ram" => BlockCommand::Ram,
|
| 338 |
"Static" => {
|
| 339 |
let text = block_data.command_arg.unwrap_or_default();
|
| 340 |
BlockCommand::Static(text)
|
| 341 |
}
|
| 342 |
"Battery" => {
|
| 343 |
let formats =
|
| 344 |
block_data
|
| 345 |
.battery_formats
|
| 346 |
.ok_or_else(|| ConfigError::MissingCommandArg {
|
| 347 |
command: "Battery".to_string(),
|
| 348 |
field: "battery_formats".to_string(),
|
| 349 |
})?;
|
| 350 |
BlockCommand::Battery {
|
| 351 |
format_charging: formats.charging,
|
| 352 |
format_discharging: formats.discharging,
|
| 353 |
format_full: formats.full,
|
| 354 |
}
|
| 355 |
}
|
| 356 |
_ => return Err(ConfigError::UnknownBlockCommand(block_data.command)),
|
| 357 |
};
|
| 358 |
|
| 359 |
status_blocks.push(BlockConfig {
|
| 360 |
format: block_data.format,
|
| 361 |
command,
|
| 362 |
interval_secs: block_data.interval_secs,
|
| 363 |
color: block_data.color,
|
| 364 |
underline: block_data.underline,
|
| 365 |
});
|
| 366 |
}
|
| 367 |
|
| 368 |
let layout_symbols = data
|
| 369 |
.layout_symbols
|
| 370 |
.into_iter()
|
| 371 |
.map(|l| crate::LayoutSymbolOverride {
|
| 372 |
name: l.name,
|
| 373 |
symbol: l.symbol,
|
| 374 |
})
|
| 375 |
.collect();
|
| 376 |
|
| 377 |
Ok(crate::Config {
|
| 378 |
border_width: data.border_width,
|
| 379 |
border_focused: data.border_focused,
|
| 380 |
border_unfocused: data.border_unfocused,
|
| 381 |
font: data.font,
|
| 382 |
gaps_enabled: data.gaps_enabled,
|
| 383 |
gap_inner_horizontal: data.gap_inner_horizontal,
|
| 384 |
gap_inner_vertical: data.gap_inner_vertical,
|
| 385 |
gap_outer_horizontal: data.gap_outer_horizontal,
|
| 386 |
gap_outer_vertical: data.gap_outer_vertical,
|
| 387 |
terminal: data.terminal,
|
| 388 |
modkey,
|
| 389 |
tags: data.tags,
|
| 390 |
layout_symbols,
|
| 391 |
keybindings,
|
| 392 |
status_blocks,
|
| 393 |
scheme_normal: crate::ColorScheme {
|
| 394 |
foreground: data.scheme_normal.foreground,
|
| 395 |
background: data.scheme_normal.background,
|
| 396 |
underline: data.scheme_normal.underline,
|
| 397 |
},
|
| 398 |
scheme_occupied: crate::ColorScheme {
|
| 399 |
foreground: data.scheme_occupied.foreground,
|
| 400 |
background: data.scheme_occupied.background,
|
| 401 |
underline: data.scheme_occupied.underline,
|
| 402 |
},
|
| 403 |
scheme_selected: crate::ColorScheme {
|
| 404 |
foreground: data.scheme_selected.foreground,
|
| 405 |
background: data.scheme_selected.background,
|
| 406 |
underline: data.scheme_selected.underline,
|
| 407 |
},
|
| 408 |
})
|
| 409 |
}
|
| 410 |
|
| 411 |
fn arg_data_to_arg(data: ArgData) -> Result<Arg, ConfigError> {
|
| 412 |
match data {
|
| 413 |
ArgData::None => Ok(Arg::None),
|
| 414 |
ArgData::String(s) => Ok(Arg::Str(s)),
|
| 415 |
ArgData::Int(n) => Ok(Arg::Int(n)),
|
| 416 |
ArgData::Array(arr) => Ok(Arg::Array(arr)),
|
| 417 |
}
|
| 418 |
}
|