Skip to main content

river_rs/
config.rs

1//! # Config
2//!
3//! The heart of river-rs library
4use crate::colors::Colors;
5use crate::layout::Layout;
6use std::io::stdout;
7use std::io::Result;
8use std::io::Write;
9use std::process::Command;
10
11/// Struct for info of modifier, keymap and associated command.
12///
13/// You should not write Keybinds by yourself, that's why `set_keybind` and `set_keybinds` are for.
14#[derive(Clone, Debug)]
15pub struct Keybind {
16    modifier: String,
17    keymap: String,
18    command: String,
19}
20/// Struct for setting xkb settings. After creating struct, apply settings with `Config.set_keyboard_layout`.
21pub struct KeyboardLayout<T> {
22    pub rules: Option<T>,
23    pub model: Option<T>,
24    pub variant: Option<T>,
25    pub options: Option<T>,
26    pub layout: Option<T>,
27}
28/// The heart of the River configuration, holding values for default and mouse-related keybinds, colors and modifier
29///
30/// Each value can only be changed with methods, because it is Rust, baby!
31/// For more info, check associated structs and implemented methods.
32#[derive(Debug)]
33pub struct Config {
34    keybinds: Vec<Keybind>,
35    colors: Colors,
36    layout: Layout,
37    modifier: String,
38}
39
40// Util functions
41impl Config {
42    /// Convenient function to simplify writing config from the end users perspective
43    fn serialize_to_owned(&self, arr: &Vec<[&str; 2]>) -> Vec<Vec<String>> {
44        let mut new_arr: Vec<Vec<String>> = Vec::new();
45
46        for keybind in arr {
47            new_arr.push(vec![String::from(keybind[0]), String::from(keybind[1])])
48        }
49
50        new_arr
51    }
52
53    pub fn print_keybindings(&self) {
54        let mut writer = stdout();
55        write!(writer, "{:?}", self.keybinds).unwrap();
56    }
57}
58
59// Keybinds
60impl Config {
61    /// Sets the single keybind
62    ///
63    /// # Example
64    /// ```
65    /// use river_rs::config::Config;
66    ///
67    /// let mut config = Config::default();
68    /// let key = String::from("Q");
69    /// let command = String::from("spanw foo");
70    /// config.set_keybind(&key, &command);
71    /// ```
72    pub fn set_keybind(&mut self, keys: &str, command: &str) -> &mut Self {
73        let keybind = Keybind {
74            modifier: self.modifier.clone(),
75            keymap: String::from(keys),
76            command: String::from(command),
77        };
78
79        self.keybinds.push(keybind);
80        self
81    }
82    /// Sets keybinds based on the vector of lists with 2 values
83    ///
84    /// Second command can be written with spaces, no need to define every argument separatly.
85    ///  
86    /// Takes the ownership of the vector and modifies it to supply `riverctl`
87    ///
88    /// # Examples
89    /// ```
90    /// use river_rs::config::Config;
91    ///
92    /// let mut config = Config::default();
93    /// let keybinds = vec![
94    ///   ["Q", "spawn foo"],
95    ///   ["E", "exit"],
96    ///   ["M", "spawn bruh"]
97    /// ];
98    /// config.set_keybinds(keybinds);
99    /// ```
100    pub fn set_keybinds(&mut self, keybinds: Vec<[&str; 2]>) -> &mut Self {
101        let keybinds = self.serialize_to_owned(&keybinds);
102
103        for keybind in keybinds {
104            self.set_keybind(&keybind[0], &keybind[1]);
105        }
106
107        self
108    }
109
110    /// Every keybind is optional, so you can just provide it with `None` keyword
111    ///
112    /// # Example
113    /// ```
114    /// use river_rs::config::Config;
115    ///
116    /// let mut config = Config::default();
117    /// config.set_mouse_keybinds(Some("move-view"), Some("resize-view"), None);
118    /// ```
119    ///
120    pub fn set_mouse_keybinds(
121        &mut self,
122        left: Option<&str>,
123        right: Option<&str>,
124        middle: Option<&str>,
125    ) -> &mut Self {
126        if let Some(left_command) = left {
127            self.apply_mouse_keybind("left", left_command);
128        }
129        if let Some(right_command) = right {
130            self.apply_mouse_keybind("right", right_command);
131        }
132        if let Some(middle_command) = middle {
133            self.apply_mouse_keybind("middle", middle_command);
134        }
135        self
136    }
137
138    fn apply_mouse_keybind(&self, position: &str, command: &str) {
139        let pos: &str = match position {
140            "left" => "BTN_LEFT",
141
142            "right" => "BTN_RIGHT",
143
144            "middle" => "BTN_MIDDLE",
145
146            _ => "BTN_LEFT",
147        };
148        Command::new("riverctl")
149            .args([
150                "map-pointer",
151                "normal",
152                self.modifier.as_str(),
153                pos,
154                command,
155            ])
156            .spawn()
157            .expect("Can't set the mouse keybind")
158            .wait()
159            .unwrap();
160    }
161
162    fn apply_keybind(&self, keybind: Keybind) {
163        let command: Vec<&str> = keybind.command.split_whitespace().collect();
164        match command.len() {
165            1 => {
166                Command::new("riverctl")
167                    .args([
168                        "map",
169                        "normal",
170                        keybind.modifier.as_str(),
171                        keybind.keymap.as_str(),
172                        command[0],
173                    ])
174                    .spawn()
175                    .expect("Can't set the keybind\n")
176                    .wait()
177                    .unwrap();
178            }
179            2 => {
180                let args = [
181                    "map",
182                    "normal",
183                    keybind.modifier.as_str(),
184                    keybind.keymap.as_str(),
185                    command[0],
186                    command[1],
187                ];
188                Command::new("riverctl")
189                    .args(args)
190                    .spawn()
191                    .expect("Can't set the keybind\n")
192                    .wait()
193                    .unwrap();
194            }
195            0 => {
196                panic!("There are no commands provided for the riverctl!\n")
197            }
198            _ => {
199                let args: Vec<&str> = [
200                    "map",
201                    "normal",
202                    keybind.modifier.as_str(),
203                    keybind.keymap.as_str(),
204                ]
205                .iter()
206                .chain(&command)
207                .copied()
208                .collect();
209                Command::new("riverctl")
210                    .args(args)
211                    .spawn()
212                    .expect("Can't set the keybind\n")
213                    .wait()
214                    .unwrap();
215            }
216        }
217    }
218}
219
220impl Default for Config {
221    /// Creates empty config with no keybinds.
222    ///
223    /// The default modifier is `Super`.
224    /// To check the default colors visit Colors struct.
225    fn default() -> Self {
226        Config {
227            keybinds: vec![],
228            colors: Colors::default(),
229            layout: Layout::default(),
230            modifier: String::from("Super"),
231        }
232    }
233}
234
235// Basics
236impl Config {
237    /// Sets xkb settings related to repeat_rate and repeat_delay.
238    ///
239    /// The typematic delay indicates the amount of time (typically in milliseconds) a key needs to be pressed and held in order for the repeating process to begin.
240    /// After the repeating process has been triggered, the character will be repeated with a certain frequency (usually given in Hz) specified by the typematic rate.
241    ///`(Taken from the Arch Wiki)`
242    pub fn set_repeat(&mut self, repeat_rate: u32, repeat_delay: u32) -> &mut Self {
243        Command::new("riverctl")
244            .args([
245                "set-repeat",
246                repeat_rate.to_string().as_str(),
247                repeat_delay.to_string().as_str(),
248            ])
249            .spawn()
250            .expect("Can't set xkb settings")
251            .wait()
252            .unwrap();
253
254        self
255    }
256
257    pub fn set_layout_generator(&mut self, layout: Layout) -> &mut Self {
258        self.layout = layout;
259        self
260    }
261    /// Set xkb settings for window manager. To check available settings, look at `KeyboardLayout`
262    /// struct.
263    pub fn set_keyboard_layout(&mut self, layout: KeyboardLayout<&str>) -> &mut Self {
264        let rules = layout.rules.unwrap_or("");
265        let model = layout.model.unwrap_or("");
266        let variant = layout.variant.unwrap_or("");
267        let options = layout.options.unwrap_or("");
268
269        let layout = match layout.layout {
270            Some(layout) => layout,
271            None => panic!("Keyboard layout is not set"),
272        };
273
274        Command::new("riverctl")
275            .args([
276                "keyboard-layout",
277                "-rules",
278                rules,
279                "-model",
280                model,
281                "-variant",
282                variant,
283                "-options",
284                options,
285                layout,
286            ])
287            .spawn()
288            .expect("Can't set the keyboard layout!\n")
289            .wait()
290            .unwrap();
291        self
292    }
293
294    /// Changes the River Modifier key.
295    ///
296    /// Useful when chaining `set_keybinds` with different modifiers.
297    ///
298    /// # Example
299    /// ```
300    /// use river_rs::config::Config;
301    ///
302    /// let mut config = Config::default();
303    ///
304    /// let keybinds = vec![
305    ///     ["C", "close"],
306    ///     ["J", "focus-view next"],
307    ///     ["K", "focus-view previous"],
308    /// ];
309    /// let shift_keybinds = vec![
310    ///     ["E", "exit"],
311    ///     ["J", "swap next"],
312    ///     ["K", "swap previous"],
313    /// ];
314    /// config
315    ///     .set_keybinds(keybinds)
316    ///     .change_super("Super+Shift")
317    ///     .set_keybinds(shift_keybinds)
318    ///     .apply()
319    ///     .unwrap();
320    /// ```
321    pub fn change_super(&mut self, key: &str) -> &mut Self {
322        self.modifier = String::from(key);
323        self
324    }
325
326    /// Sets tags from 1 to 9 based on passed modifiers
327    pub fn set_tags(&mut self, modifier: &str, switch_modifier: &str) -> &mut Self {
328        let tags: Vec<u32> = (0..9).collect();
329        let tag_ids: Vec<u32> = tags.iter().map(|x| 2_u32.pow(*x)).collect();
330        let mut keybinds: Vec<Keybind> = Vec::new();
331        let mut idx = 0;
332        while idx < tags.len() {
333            keybinds.push(Keybind {
334                modifier: String::from(modifier),
335                keymap: (tags[idx] + 1).to_string(),
336                command: String::from("set-focused-tags ") + tag_ids[idx].to_string().as_str(),
337            });
338            keybinds.push(Keybind {
339                modifier: String::from(switch_modifier),
340                keymap: (tags[idx] + 1).to_string(),
341                command: String::from("set-view-tags ") + tag_ids[idx].to_string().as_str(),
342            });
343            idx += 1;
344        }
345        for keybind in keybinds {
346            self.keybinds.push(keybind);
347        }
348        self
349    }
350
351    /// Set your autostart programs to spawn on launching River
352    /// # Example
353    /// ```
354    /// use crate::river_rs::config::Config;
355    /// let autostart = vec![
356    ///     "firefox",
357    ///     "kitty"
358    /// ];
359    ///
360    /// let mut config = Config::default();
361    /// config.autostart(autostart);
362    /// ```
363    pub fn autostart(&mut self, applications: Vec<&str>) -> &mut Self {
364        for app in applications {
365            Command::new("riverctl")
366                .args(["spawn", app])
367                .spawn()
368                .expect("Can't spawn autostart programs")
369                .wait()
370                .unwrap();
371        }
372        self
373    }
374
375    fn apply_colors(&mut self) -> &mut Self {
376        let background_color = format!("{:#X}", self.colors.background_color);
377        let border_color_focused = format!("{:#X}", self.colors.border_color_focused);
378        let border_color_unfocused = format!("{:#X}", self.colors.border_color_unfocused);
379
380        let commands = vec![
381            ["background-color", background_color.as_str()],
382            ["border-color-focused", border_color_focused.as_str()],
383            ["border-color-unfocused", border_color_unfocused.as_str()],
384        ];
385
386        for command in commands {
387            Command::new("riverctl")
388                .args(command)
389                .spawn()
390                .expect("Can't set colors with riverctl\n")
391                .wait()
392                .unwrap();
393        }
394
395        self
396    }
397
398    /// Finish setting up the config.
399    ///
400    /// Needs to be run at the end of setup via chaining.
401    pub fn apply(&mut self) -> Result<()> {
402        for keybind in &self.keybinds {
403            let keybind = keybind.clone();
404            self.apply_keybind(keybind);
405        }
406        self.layout.spawn();
407        Ok(())
408    }
409}