~kennylevinsen/clayrs

fdb2d71ca29c1b084f75023d839d84f6af62ac2d — Kenny Levinsen 6 months ago c7af2e9
More configurability
M src/config.rs => src/config.rs +12 -9
@@ 1,9 1,5 @@
use serde::Deserialize;
use std::{
    default::Default,
    fs::File,
    path::PathBuf,
};
use std::{default::Default, fs::File, path::PathBuf};

#[derive(Default, Deserialize, Debug)]
pub struct Keyboard {


@@ 26,7 22,6 @@ pub struct General {
    pub start_command: String,
}


#[derive(Clone, Copy, Deserialize, Debug, Eq, PartialEq)]
pub enum Visibility {
    Shown,


@@ 54,7 49,7 @@ pub enum BindingAction {
    OutputLayoutIncPrimaries(i8),
    UI(Visibility),
    #[default]
    Nothing
    Nothing,
}

#[derive(Default, Deserialize, Debug)]


@@ 69,6 64,13 @@ pub struct UI {
    pub font_path: String,
    pub font_size: f32,
    pub trigger: String,
    pub color_bg: u32,
    pub color_focus: u32,
    pub color_selection: u32,
    pub color_drag: u32,
    pub font_color_used: u32,
    pub font_color_available: u32,
    pub font_color_active: u32,
}

#[derive(Default, Deserialize, Debug)]


@@ 88,7 90,9 @@ pub fn get_config_path() -> Result<PathBuf, String> {
        p
    } else {
        let xdg_dirs = xdg::BaseDirectories::with_prefix("clay").map_err(|e| e.to_string())?;
        xdg_dirs.find_config_file("config.ron").ok_or("No config file found".to_string())?
        xdg_dirs
            .find_config_file("config.ron")
            .ok_or("No config file found".to_string())?
    };
    Ok(config_path)
}


@@ 97,4 101,3 @@ pub fn read_config(config_path: &PathBuf) -> Result<Config, String> {
    let file = File::open(config_path).map_err(|e| e.to_string())?;
    ron::de::from_reader(&file).map_err(|e| e.to_string())
}


M src/draw/color.rs => src/draw/color.rs +9 -4
@@ 2,10 2,6 @@
pub struct Color(pub u32);

impl Color {
    pub const WHITE: Color = Color(0xFFFFFFFF);
    pub const GREY50: Color = Color(0xFF7F7F7F);
    pub const DARKORANGE: Color = Color(0xFFFF7F00);

    #[allow(unused)]
    pub fn new(red: u8, green: u8, blue: u8, opacity: u8) -> Color {
        Color((opacity as u32) << 24 | (red as u32) << 16 | (green as u32) << 8 | (blue as u32))


@@ 30,4 26,13 @@ impl Color {
                | ((self.blue() * alpha) >> 8),
        )
    }

    pub fn as_f32arr(&self) -> [f32; 4] {
        [
            ((self.0 >> 24) & 0xFF) as f32 / 255.,
            ((self.0 >> 16) & 0xFF) as f32 / 255.,
            ((self.0 >> 8) & 0xFF) as f32 / 255.,
            (self.0 & 0xFF) as f32 / 255.,
        ]
    }
}

M src/ffi/iface.rs => src/ffi/iface.rs +4 -3
@@ 71,7 71,7 @@ unsafe extern "C" fn view_focused(raw_view: *mut types::clay_view, data: *mut c_

impl types::Clay {
    pub fn run(&mut self, mgr: &mut mgr::Mgr) {
        let mut c_mgr =  types::claymgr {
        let mut c_mgr = types::claymgr {
            user_data: mgr as *mut mgr::Mgr as *mut c_void,
            input: types::claymgr_input {
                key_event: Some(key_event),


@@ 90,7 90,8 @@ impl types::Clay {
                focused: Some(view_focused),
            },
        };
        unsafe { types::clay_server_run(self.server, &mut c_mgr); }
        unsafe {
            types::clay_server_run(self.server, &mut c_mgr);
        }
    }
}


M src/ffi/types.rs => src/ffi/types.rs +22 -25
@@ 5,11 5,8 @@

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

use crate::{
    draw::BufferView,
    config::Config,
};
use std::ffi::{CString, c_char, CStr};
use crate::{config::Config, draw::BufferView};
use std::ffi::{c_char, CStr, CString};

pub struct ClayShmView<'a> {
    pub clay_view: ClayView,


@@ 24,15 21,11 @@ pub struct Clay {

impl Clay {
    pub fn create() -> Option<Clay> {
        let raw = unsafe {
            clay_server_create()
        };
        let raw = unsafe { clay_server_create() };
        if raw.is_null() {
            None
        } else {
            Some(Clay{
                server: raw,
            })
            Some(Clay { server: raw })
        }
    }



@@ 43,11 36,21 @@ impl Clay {
    pub fn set_config(&self, config: &Config) {
        let mut cfg = clay_config {
            keyboard: clay_config_keyboard {
                xkb_rules: CString::new(config.input.keyboard.xkb_rules.as_str()).unwrap().into_raw(),
                xkb_model: CString::new(config.input.keyboard.xkb_model.as_str()).unwrap().into_raw(),
                xkb_layout: CString::new(config.input.keyboard.xkb_layout.as_str()).unwrap().into_raw(),
                xkb_variant: CString::new(config.input.keyboard.xkb_variant.as_str()).unwrap().into_raw(),
                xkb_options: CString::new(config.input.keyboard.xkb_options.as_str()).unwrap().into_raw(),
                xkb_rules: CString::new(config.input.keyboard.xkb_rules.as_str())
                    .unwrap()
                    .into_raw(),
                xkb_model: CString::new(config.input.keyboard.xkb_model.as_str())
                    .unwrap()
                    .into_raw(),
                xkb_layout: CString::new(config.input.keyboard.xkb_layout.as_str())
                    .unwrap()
                    .into_raw(),
                xkb_variant: CString::new(config.input.keyboard.xkb_variant.as_str())
                    .unwrap()
                    .into_raw(),
                xkb_options: CString::new(config.input.keyboard.xkb_options.as_str())
                    .unwrap()
                    .into_raw(),
                repeat_rate: config.input.keyboard.repeat_rate,
                repeat_delay: config.input.keyboard.repeat_delay,
            },


@@ 78,9 81,7 @@ impl Clay {

    pub fn draw_create_shm_view(&self, dim: (u32, u32)) -> ClayShmView {
        let mut data: *mut u32 = std::ptr::null_mut();
        let raw = unsafe {
            clay_draw_create_shm_view(self.server, dim.0, dim.1, &mut data)
        };
        let raw = unsafe { clay_draw_create_shm_view(self.server, dim.0, dim.1, &mut data) };
        let view = BufferView::from_raw(data, dim);
        ClayShmView {
            clay_view: unsafe { ClayView::from_raw(raw) },


@@ 117,9 118,7 @@ impl std::hash::Hash for ClayView {

impl ClayView {
    pub unsafe fn from_raw(raw: *mut clay_view) -> ClayView {
        ClayView {
            raw,
        }
        ClayView { raw }
    }

    pub fn set_enabled(&self, enabled: bool) {


@@ 208,8 207,6 @@ impl std::hash::Hash for ClayOutput {

impl ClayOutput {
    pub unsafe fn from_raw(raw: *mut clay_output) -> ClayOutput {
        ClayOutput {
            raw,
        }
        ClayOutput { raw }
    }
}

M src/layout/mod.rs => src/layout/mod.rs +3 -16
@@ 17,20 17,11 @@ pub enum LayoutControl {
}

pub trait Layout {
    fn layout(
        &self,
        layout: &Vec<Uuid>,
        containers: &mut HashMap<Uuid, Container>,
        geo: &clay_box,
    );
    fn layout(&self, layout: &Vec<Uuid>, containers: &mut HashMap<Uuid, Container>, geo: &clay_box);
    fn control(&mut self, _ctrl: LayoutControl) {}
}

fn layout_horizontal(
    layout: &[Uuid],
    containers: &mut HashMap<Uuid, Container>,
    geo: &clay_box,
) {
fn layout_horizontal(layout: &[Uuid], containers: &mut HashMap<Uuid, Container>, geo: &clay_box) {
    if geo.width == 0 || geo.height == 0 {
        return;
    }


@@ 51,11 42,7 @@ fn layout_horizontal(
    }
}

fn layout_vertical(
    layout: &[Uuid],
    containers: &mut HashMap<Uuid, Container>,
    geo: &clay_box,
) {
fn layout_vertical(layout: &[Uuid], containers: &mut HashMap<Uuid, Container>, geo: &clay_box) {
    if geo.width == 0 || geo.height == 0 {
        return;
    }

M src/main.rs => src/main.rs +2 -4
@@ 1,14 1,14 @@
#![feature(test)]

mod config;
mod draw;
mod ffi;
mod layout;
mod mgr;
mod utils;
mod config;

use mgr::Mgr;
use ffi::types::Clay;
use mgr::Mgr;

pub fn main() {
    let config = config::get_config_path().expect("unable to find configuration");


@@ 20,7 20,6 @@ pub fn main() {
        }
    };


    let mut mgr = Mgr::new(server, config);

    std::env::remove_var("LD_LIBRARY_PATH");


@@ 36,7 35,6 @@ pub fn main() {
            cmd.arg("-c");
            cmd.arg(&start);
            let _ = cmd.spawn();

        });
    }
    server.run(&mut mgr);

M src/mgr/binding.rs => src/mgr/binding.rs +376 -70
@@ 21,55 21,343 @@ fn mod_name_to_mod(v: &str) -> u32 {
fn sym_name_to_sym(m: Modifiers, v: &str) -> u32 {
    use xkbcommon::xkb::keysyms;
    match v {
        "a" => if m.shift() { keysyms::KEY_A } else { keysyms::KEY_a }
        "b" => if m.shift() { keysyms::KEY_B } else { keysyms::KEY_b }
        "c" => if m.shift() { keysyms::KEY_C } else { keysyms::KEY_c }
        "d" => if m.shift() { keysyms::KEY_D } else { keysyms::KEY_d }
        "e" => if m.shift() { keysyms::KEY_E } else { keysyms::KEY_e }
        "f" => if m.shift() { keysyms::KEY_F } else { keysyms::KEY_f }
        "g" => if m.shift() { keysyms::KEY_G } else { keysyms::KEY_g }
        "h" => if m.shift() { keysyms::KEY_H } else { keysyms::KEY_h }
        "i" => if m.shift() { keysyms::KEY_I } else { keysyms::KEY_i }
        "j" => if m.shift() { keysyms::KEY_J } else { keysyms::KEY_j }
        "k" => if m.shift() { keysyms::KEY_K } else { keysyms::KEY_k }
        "l" => if m.shift() { keysyms::KEY_L } else { keysyms::KEY_l }
        "m" => if m.shift() { keysyms::KEY_M } else { keysyms::KEY_m }
        "n" => if m.shift() { keysyms::KEY_N } else { keysyms::KEY_n }
        "o" => if m.shift() { keysyms::KEY_O } else { keysyms::KEY_o }
        "p" => if m.shift() { keysyms::KEY_P } else { keysyms::KEY_p }
        "q" => if m.shift() { keysyms::KEY_Q } else { keysyms::KEY_q }
        "r" => if m.shift() { keysyms::KEY_R } else { keysyms::KEY_r }
        "s" => if m.shift() { keysyms::KEY_S } else { keysyms::KEY_s }
        "t" => if m.shift() { keysyms::KEY_T } else { keysyms::KEY_t }
        "u" => if m.shift() { keysyms::KEY_U } else { keysyms::KEY_u }
        "v" => if m.shift() { keysyms::KEY_V } else { keysyms::KEY_v }
        "w" => if m.shift() { keysyms::KEY_W } else { keysyms::KEY_w }
        "x" => if m.shift() { keysyms::KEY_X } else { keysyms::KEY_x }
        "y" => if m.shift() { keysyms::KEY_Y } else { keysyms::KEY_y }
        "z" => if m.shift() { keysyms::KEY_Z } else { keysyms::KEY_z }
        "1" => if !m.shift() { keysyms::KEY_1 } else { keysyms::KEY_exclam },
        "2" => if !m.shift() { keysyms::KEY_2 } else { keysyms::KEY_at },
        "3" => if !m.shift() { keysyms::KEY_3 } else { keysyms::KEY_numbersign },
        "4" => if !m.shift() { keysyms::KEY_4 } else { keysyms::KEY_dollar },
        "5" => if !m.shift() { keysyms::KEY_5 } else { keysyms::KEY_percent },
        "6" => if !m.shift() { keysyms::KEY_6 } else { keysyms::KEY_asciicircum },
        "7" => if !m.shift() { keysyms::KEY_7 } else { keysyms::KEY_ampersand },
        "8" => if !m.shift() { keysyms::KEY_8 } else { keysyms::KEY_asterisk },
        "9" => if !m.shift() { keysyms::KEY_9 } else { keysyms::KEY_parenleft },
        "0" => if !m.shift() { keysyms::KEY_9 } else { keysyms::KEY_parenright },
        ":" => if m.shift() { keysyms::KEY_colon } else { keysyms::KEY_semicolon },
        "'" => if m.shift() { keysyms::KEY_quotedbl } else { keysyms::KEY_apostrophe },
        "[" => if m.shift() { keysyms::KEY_braceleft } else { keysyms::KEY_bracketleft },
        "]" => if m.shift() { keysyms::KEY_braceright } else { keysyms::KEY_bracketright },
        "\\" => if m.shift() { keysyms::KEY_bar } else { keysyms::KEY_backslash },
        "," => if m.shift() { keysyms::KEY_less } else { keysyms::KEY_comma },
        "." => if m.shift() { keysyms::KEY_greater } else { keysyms::KEY_period },
        "/" => if m.shift() { keysyms::KEY_question } else { keysyms::KEY_slash },
        "`" => if m.shift() { keysyms::KEY_asciitilde } else { keysyms::KEY_grave },
        "-" => if m.shift() { keysyms::KEY_underscore } else { keysyms::KEY_minus },
        "=" => if m.shift() { keysyms::KEY_plus } else { keysyms::KEY_equal },
        "a" => {
            if m.shift() {
                keysyms::KEY_A
            } else {
                keysyms::KEY_a
            }
        }
        "b" => {
            if m.shift() {
                keysyms::KEY_B
            } else {
                keysyms::KEY_b
            }
        }
        "c" => {
            if m.shift() {
                keysyms::KEY_C
            } else {
                keysyms::KEY_c
            }
        }
        "d" => {
            if m.shift() {
                keysyms::KEY_D
            } else {
                keysyms::KEY_d
            }
        }
        "e" => {
            if m.shift() {
                keysyms::KEY_E
            } else {
                keysyms::KEY_e
            }
        }
        "f" => {
            if m.shift() {
                keysyms::KEY_F
            } else {
                keysyms::KEY_f
            }
        }
        "g" => {
            if m.shift() {
                keysyms::KEY_G
            } else {
                keysyms::KEY_g
            }
        }
        "h" => {
            if m.shift() {
                keysyms::KEY_H
            } else {
                keysyms::KEY_h
            }
        }
        "i" => {
            if m.shift() {
                keysyms::KEY_I
            } else {
                keysyms::KEY_i
            }
        }
        "j" => {
            if m.shift() {
                keysyms::KEY_J
            } else {
                keysyms::KEY_j
            }
        }
        "k" => {
            if m.shift() {
                keysyms::KEY_K
            } else {
                keysyms::KEY_k
            }
        }
        "l" => {
            if m.shift() {
                keysyms::KEY_L
            } else {
                keysyms::KEY_l
            }
        }
        "m" => {
            if m.shift() {
                keysyms::KEY_M
            } else {
                keysyms::KEY_m
            }
        }
        "n" => {
            if m.shift() {
                keysyms::KEY_N
            } else {
                keysyms::KEY_n
            }
        }
        "o" => {
            if m.shift() {
                keysyms::KEY_O
            } else {
                keysyms::KEY_o
            }
        }
        "p" => {
            if m.shift() {
                keysyms::KEY_P
            } else {
                keysyms::KEY_p
            }
        }
        "q" => {
            if m.shift() {
                keysyms::KEY_Q
            } else {
                keysyms::KEY_q
            }
        }
        "r" => {
            if m.shift() {
                keysyms::KEY_R
            } else {
                keysyms::KEY_r
            }
        }
        "s" => {
            if m.shift() {
                keysyms::KEY_S
            } else {
                keysyms::KEY_s
            }
        }
        "t" => {
            if m.shift() {
                keysyms::KEY_T
            } else {
                keysyms::KEY_t
            }
        }
        "u" => {
            if m.shift() {
                keysyms::KEY_U
            } else {
                keysyms::KEY_u
            }
        }
        "v" => {
            if m.shift() {
                keysyms::KEY_V
            } else {
                keysyms::KEY_v
            }
        }
        "w" => {
            if m.shift() {
                keysyms::KEY_W
            } else {
                keysyms::KEY_w
            }
        }
        "x" => {
            if m.shift() {
                keysyms::KEY_X
            } else {
                keysyms::KEY_x
            }
        }
        "y" => {
            if m.shift() {
                keysyms::KEY_Y
            } else {
                keysyms::KEY_y
            }
        }
        "z" => {
            if m.shift() {
                keysyms::KEY_Z
            } else {
                keysyms::KEY_z
            }
        }
        "1" => {
            if !m.shift() {
                keysyms::KEY_1
            } else {
                keysyms::KEY_exclam
            }
        }
        "2" => {
            if !m.shift() {
                keysyms::KEY_2
            } else {
                keysyms::KEY_at
            }
        }
        "3" => {
            if !m.shift() {
                keysyms::KEY_3
            } else {
                keysyms::KEY_numbersign
            }
        }
        "4" => {
            if !m.shift() {
                keysyms::KEY_4
            } else {
                keysyms::KEY_dollar
            }
        }
        "5" => {
            if !m.shift() {
                keysyms::KEY_5
            } else {
                keysyms::KEY_percent
            }
        }
        "6" => {
            if !m.shift() {
                keysyms::KEY_6
            } else {
                keysyms::KEY_asciicircum
            }
        }
        "7" => {
            if !m.shift() {
                keysyms::KEY_7
            } else {
                keysyms::KEY_ampersand
            }
        }
        "8" => {
            if !m.shift() {
                keysyms::KEY_8
            } else {
                keysyms::KEY_asterisk
            }
        }
        "9" => {
            if !m.shift() {
                keysyms::KEY_9
            } else {
                keysyms::KEY_parenleft
            }
        }
        "0" => {
            if !m.shift() {
                keysyms::KEY_9
            } else {
                keysyms::KEY_parenright
            }
        }
        ":" => {
            if m.shift() {
                keysyms::KEY_colon
            } else {
                keysyms::KEY_semicolon
            }
        }
        "'" => {
            if m.shift() {
                keysyms::KEY_quotedbl
            } else {
                keysyms::KEY_apostrophe
            }
        }
        "[" => {
            if m.shift() {
                keysyms::KEY_braceleft
            } else {
                keysyms::KEY_bracketleft
            }
        }
        "]" => {
            if m.shift() {
                keysyms::KEY_braceright
            } else {
                keysyms::KEY_bracketright
            }
        }
        "\\" => {
            if m.shift() {
                keysyms::KEY_bar
            } else {
                keysyms::KEY_backslash
            }
        }
        "," => {
            if m.shift() {
                keysyms::KEY_less
            } else {
                keysyms::KEY_comma
            }
        }
        "." => {
            if m.shift() {
                keysyms::KEY_greater
            } else {
                keysyms::KEY_period
            }
        }
        "/" => {
            if m.shift() {
                keysyms::KEY_question
            } else {
                keysyms::KEY_slash
            }
        }
        "`" => {
            if m.shift() {
                keysyms::KEY_asciitilde
            } else {
                keysyms::KEY_grave
            }
        }
        "-" => {
            if m.shift() {
                keysyms::KEY_underscore
            } else {
                keysyms::KEY_minus
            }
        }
        "=" => {
            if m.shift() {
                keysyms::KEY_plus
            } else {
                keysyms::KEY_equal
            }
        }

        "tab" => if m.shift() { keysyms::KEY_ISO_Left_Tab } else { keysyms::KEY_Tab },
        "tab" => {
            if m.shift() {
                keysyms::KEY_ISO_Left_Tab
            } else {
                keysyms::KEY_Tab
            }
        }
        "caps" => keysyms::KEY_Caps_Lock,
        "shift" => keysyms::KEY_Shift_L,
        "ctrl" => keysyms::KEY_Control_L,


@@ 82,6 370,7 @@ fn sym_name_to_sym(m: Modifiers, v: &str) -> u32 {
        "shift_r" => keysyms::KEY_Shift_R,
        "return" => keysyms::KEY_Return,
        "backspace" => keysyms::KEY_BackSpace,
        "escape" | "esc" => keysyms::KEY_Escape,

        "XF86MonBrightnessUp" => keysyms::KEY_XF86MonBrightnessUp,
        "XF86MonBrightnessDown" => keysyms::KEY_XF86MonBrightnessDown,


@@ 136,7 425,7 @@ pub struct BindingManager {

impl BindingManager {
    pub fn new(cfg: &config::Config) -> BindingManager {
        let mut bindings = BindingManager{
        let mut bindings = BindingManager {
            bindings: Vec::new(),
            ui_trigger: 0,
        };


@@ 153,7 442,13 @@ impl BindingManager {
    }

    fn register(&mut self, cfg: &config::Binding) {
        let modifiers = Modifiers(cfg.modifiers.iter().map(|x| mod_name_to_mod(x)).reduce(|a, b| a | b).unwrap_or(0));
        let modifiers = Modifiers(
            cfg.modifiers
                .iter()
                .map(|x| mod_name_to_mod(x))
                .reduce(|a, b| a | b)
                .unwrap_or(0),
        );
        let sym = sym_name_to_sym(modifiers, &cfg.symbol);
        for m in &mut self.bindings {
            if m.0 == modifiers.0 {


@@ 171,7 466,7 @@ impl BindingManager {
            return match state {
                1 => &config::BindingAction::UI(config::Visibility::Shown),
                _ => &config::BindingAction::UI(config::Visibility::Hidden),
            }
            };
        }

        if state == 1 {


@@ 191,9 486,15 @@ mod tests {
    use test::Bencher;

    fn register_few_bindings(mgr: &mut BindingManager) {
        for m in [vec!["logo"], vec!["logo", "ctrl"], vec!["logo", "shift"], vec!["logo", "alt"], vec!["logo", "shift", "alt"]] {
        for m in [
            vec!["logo"],
            vec!["logo", "ctrl"],
            vec!["logo", "shift"],
            vec!["logo", "alt"],
            vec!["logo", "shift", "alt"],
        ] {
            for ch in "abcdefg".chars() {
                let b = config::Binding{
                let b = config::Binding {
                    modifiers: m.iter().map(|x| x.to_string()).collect(),
                    symbol: ch.to_string(),
                    action: config::BindingAction::Exit,


@@ 201,7 502,7 @@ mod tests {
                mgr.register(&b);
            }
        }
        let b = config::Binding{
        let b = config::Binding {
            modifiers: vec![],
            symbol: "a".to_string(),
            action: config::BindingAction::Exit,


@@ 210,9 511,15 @@ mod tests {
    }

    fn register_many_bindings(mgr: &mut BindingManager) {
        for m in [vec!["logo"], vec!["logo", "ctrl"], vec!["logo", "shift"], vec!["logo", "alt"], vec!["logo", "shift", "alt"]] {
        for m in [
            vec!["logo"],
            vec!["logo", "ctrl"],
            vec!["logo", "shift"],
            vec!["logo", "alt"],
            vec!["logo", "shift", "alt"],
        ] {
            for ch in "abcdefghijklmnopqrstuvwxyz".chars() {
                let b = config::Binding{
                let b = config::Binding {
                    modifiers: m.iter().map(|x| x.to_string()).collect(),
                    symbol: ch.to_string(),
                    action: config::BindingAction::Exit,


@@ 220,7 527,7 @@ mod tests {
                mgr.register(&b);
            }
        }
        let b = config::Binding{
        let b = config::Binding {
            modifiers: vec![],
            symbol: "a".to_string(),
            action: config::BindingAction::Exit,


@@ 244,9 551,9 @@ mod tests {
    fn bench_binding_many_miss_sym(b: &mut Bencher) {
        let mut bindings = BindingManager::new(&config::Config::default());
        register_many_bindings(&mut bindings);
        let sym = sym_name_to_sym(Modifiers(1<<6), "-");
        let sym = sym_name_to_sym(Modifiers(1 << 6), "-");
        b.iter(|| {
            let modifier = test::black_box(1<<6);
            let modifier = test::black_box(1 << 6);
            let sym = test::black_box(sym);
            let state = test::black_box(1);
            bindings.action(modifier, sym, state);


@@ 257,9 564,9 @@ mod tests {
    fn bench_binding_many_miss_mod(b: &mut Bencher) {
        let mut bindings = BindingManager::new(&config::Config::default());
        register_many_bindings(&mut bindings);
        let sym = sym_name_to_sym(Modifiers(1<<7), "a");
        let sym = sym_name_to_sym(Modifiers(1 << 7), "a");
        b.iter(|| {
            let modifier = test::black_box(1<<7);
            let modifier = test::black_box(1 << 7);
            let sym = test::black_box(sym);
            let state = test::black_box(1);
            bindings.action(modifier, sym, state);


@@ 270,9 577,9 @@ mod tests {
    fn bench_binding_many_match_sym(b: &mut Bencher) {
        let mut bindings = BindingManager::new(&config::Config::default());
        register_many_bindings(&mut bindings);
        let sym = sym_name_to_sym(Modifiers(1<<6), "z");
        let sym = sym_name_to_sym(Modifiers(1 << 6), "z");
        b.iter(|| {
            let modifier = test::black_box(1<<6);
            let modifier = test::black_box(1 << 6);
            let sym = test::black_box(sym);
            let state = test::black_box(1);
            bindings.action(modifier, sym, state);


@@ 321,9 628,9 @@ mod tests {
    fn bench_binding_few_miss_sym(b: &mut Bencher) {
        let mut bindings = BindingManager::new(&config::Config::default());
        register_few_bindings(&mut bindings);
        let sym = sym_name_to_sym(Modifiers(1<<6), "-");
        let sym = sym_name_to_sym(Modifiers(1 << 6), "-");
        b.iter(|| {
            let modifier = test::black_box(1<<6);
            let modifier = test::black_box(1 << 6);
            let sym = test::black_box(sym);
            let state = test::black_box(1);
            bindings.action(modifier, sym, state);


@@ 334,9 641,9 @@ mod tests {
    fn bench_binding_few_miss_mod(b: &mut Bencher) {
        let mut bindings = BindingManager::new(&config::Config::default());
        register_few_bindings(&mut bindings);
        let sym = sym_name_to_sym(Modifiers(1<<7), "a");
        let sym = sym_name_to_sym(Modifiers(1 << 7), "a");
        b.iter(|| {
            let modifier = test::black_box(1<<7);
            let modifier = test::black_box(1 << 7);
            let sym = test::black_box(sym);
            let state = test::black_box(1);
            bindings.action(modifier, sym, state);


@@ 347,9 654,9 @@ mod tests {
    fn bench_binding_few_match_sym(b: &mut Bencher) {
        let mut bindings = BindingManager::new(&config::Config::default());
        register_few_bindings(&mut bindings);
        let sym = sym_name_to_sym(Modifiers(1<<6), "z");
        let sym = sym_name_to_sym(Modifiers(1 << 6), "z");
        b.iter(|| {
            let modifier = test::black_box(1<<6);
            let modifier = test::black_box(1 << 6);
            let sym = test::black_box(sym);
            let state = test::black_box(1);
            bindings.action(modifier, sym, state);


@@ 381,5 688,4 @@ mod tests {
            bindings.action(modifier, sym, state);
        });
    }

}

M src/mgr/mod.rs => src/mgr/mod.rs +16 -12
@@ 1,15 1,16 @@
mod binding;
pub mod container;
mod output;
mod presentation;
mod view;
mod binding;

use uuid::Uuid;

use crate::{
    config,
    draw::fonts::{ASCII, Font},
    draw::fonts::{Font, ASCII},
    ffi::types::{clay_box, Clay, ClayOutput, ClayView},
    layout::LayoutControl,
    mgr::{
        binding::BindingManager,
        container::Container,


@@ 17,13 18,9 @@ use crate::{
        presentation::{Presentation, PresentationAction},
        view::View,
    },
    layout::LayoutControl,
};

use std::{
    collections::HashMap,
    path::PathBuf,
};
use std::{collections::HashMap, path::PathBuf};

const BTN_LEFT: u32 = 0x110;



@@ 135,7 132,9 @@ impl Mgr {
    pub fn key_event(&mut self, sym: u32, modmask: u32, state: i32) -> bool {
        let action = self.bindings.action(modmask, sym, state);
        match action {
            config::BindingAction::Run(cmd, args) => self.run(cmd, &args.iter().map(|x| x.as_str()).collect::<Vec<&str>>()),
            config::BindingAction::Run(cmd, args) => {
                self.run(cmd, &args.iter().map(|x| x.as_str()).collect::<Vec<&str>>())
            }
            config::BindingAction::ReloadConfig => self.reload_config(),
            config::BindingAction::UI(config::Visibility::Shown) => self.present(),
            config::BindingAction::UI(config::Visibility::Hidden) => self.presentation = None,


@@ 147,7 146,9 @@ impl Mgr {
            config::BindingAction::OutputLayoutAdvance(dir) => self.output_layout_advance(*dir),
            config::BindingAction::OutputLayoutModeNext => self.output_layout_next(),
            config::BindingAction::OutputLayoutIncSplit(s) => self.output_layout_inc_split(*s),
            config::BindingAction::OutputLayoutIncPrimaries(p) => self.output_layout_inc_primaries(*p),
            config::BindingAction::OutputLayoutIncPrimaries(p) => {
                self.output_layout_inc_primaries(*p)
            }
            config::BindingAction::Nothing => return false,
        }



@@ 235,7 236,7 @@ impl Mgr {
                &mut self.font,
            );
        } else {
            let mut p = Presentation::new(self.clay);
            let mut p = Presentation::new(self.clay, &self.config.ui);
            p.all_change(
                &mut self.views,
                &mut self.containers,


@@ 309,14 310,17 @@ impl Mgr {
            return;
        }

        let current = self.views.iter().position(|v| v.current_container == active_container);
        let current = self
            .views
            .iter()
            .position(|v| v.current_container == active_container);
        let (start, end) = match (current, dir) {
            (Some(idx), config::Direction::Forward) => (idx, self.views.len()),
            (Some(idx), config::Direction::Backward) => (0, idx),
            (None, _) => (0, self.views.len()),
        };

        let it: Box<dyn Iterator<Item=&mut View>> = if dir == config::Direction::Backward {
        let it: Box<dyn Iterator<Item = &mut View>> = if dir == config::Direction::Backward {
            Box::new(self.views[start..end].iter_mut().rev())
        } else {
            Box::new(self.views[start..end].iter_mut())

M src/mgr/presentation.rs => src/mgr/presentation.rs +45 -28
@@ 1,16 1,14 @@
pub use crate::{
use crate::{
    config,
    draw::color::Color,
    draw::fonts::{Font, FontLayout, PreparedText},
    ffi::types::{clay_box, Clay, ClayOutput, ClayView},
    ffi::types::{Clay, ClayView},
    mgr::{container::Container, output::Output, view::View},
    utils::Modifiers,
};

use uuid::Uuid;

use std::{
    collections::HashMap,
};
use std::collections::HashMap;

struct CenterText<'a> {
    position: (i32, i32),


@@ 31,10 29,7 @@ impl<'a> CenterText<'a> {
        };
        let position = (position.0, position.1 + y_offset as i32);

        Some(CenterText {
            position,
            text,
        })
        Some(CenterText { position, text })
    }

    fn draw_line(&self, clay: &Clay, line: usize, c: Color) -> (ClayView, (i32, i32), (u32, u32)) {


@@ 78,6 73,7 @@ struct ContainerPresentation {
    view_texts: Vec<ViewPresentation>,
    view_idx: Option<usize>,
    color: [f32; 4],
    colors: Colors,
    position: (i32, i32),
    size: (u32, u32),
    focused: bool,


@@ 103,7 99,7 @@ impl ContainerPresentation {
            font.reset(FontLayout::Center(self.size.0));
            font.append("No views");
            if let Some(c) = CenterText::new(font.prepare(), self.position, self.size) {
                let (view, position, size) = c.draw_line(clay, 0, Color::WHITE);
                let (view, position, size) = c.draw_line(clay, 0, self.colors.font_available);
                self.view_texts.push(ViewPresentation {
                    clay_view: view,
                    view_idx: None,


@@ 125,7 121,7 @@ impl ContainerPresentation {
            font.append(name);
            font.append("\n");
            if let Some(c) = CenterText::new(font.prepare(), self.position, self.size) {
                let (view, position, size) = c.draw_line(clay, 0, Color::WHITE);
                let (view, position, size) = c.draw_line(clay, 0, self.colors.font_available);
                self.view_texts.push(ViewPresentation {
                    view_idx: Some(name_idx),
                    clay_view: view,


@@ 148,11 144,11 @@ impl ContainerPresentation {
                let ViewInfo { bound, .. } = &views[idx];

                let color = if idx == name_idx {
                    Color::DARKORANGE
                    self.colors.font_active
                } else if *bound {
                    Color::GREY50
                    self.colors.font_used
                } else {
                    Color::WHITE
                    self.colors.font_available
                };

                let (view, position, size) = c.draw_line(clay, idx, color);


@@ 177,8 173,22 @@ struct Selection {
    view: Option<usize>,
}

#[derive(Clone)]
struct Colors {
    bg: [f32; 4],
    focus: [f32; 4],
    selection: [f32; 4],
    drag: [f32; 4],
    font_used: Color,
    font_available: Color,
    font_active: Color,
}

pub struct Presentation {
    clay: Clay,

    colors: Colors,

    output_boxes: Vec<ClayView>,
    containers: HashMap<Uuid, ContainerPresentation>,
    views: Vec<ViewInfo>,


@@ 195,9 205,19 @@ impl Drop for Presentation {
}

impl Presentation {
    pub fn new(clay: Clay) -> Presentation {
    pub fn new(clay: Clay, cfg: &config::UI) -> Presentation {
        let colors = Colors {
            bg: Color(cfg.color_bg).as_f32arr(),
            focus: Color(cfg.color_focus).as_f32arr(),
            selection: Color(cfg.color_selection).as_f32arr(),
            drag: Color(cfg.color_drag).as_f32arr(),
            font_used: Color(cfg.font_color_used),
            font_available: Color(cfg.font_color_available),
            font_active: Color(cfg.font_color_active),
        };
        Presentation {
            clay,
            colors,
            output_boxes: Vec::new(),
            containers: HashMap::new(),
            views: Vec::new(),


@@ 232,7 252,7 @@ impl Presentation {
                        output.usable_area.width as u32,
                        output.usable_area.height as u32,
                    ),
                    [0., 0., 0., 0.66],
                    self.colors.bg,
                );
                output_rect.set_position((output.usable_area.x, output.usable_area.y));
                output_rect.set_enabled(true);


@@ 246,7 266,7 @@ impl Presentation {
                font.append("No containers");

                if let Some(c) = CenterText::new(font.prepare(), position, size) {
                    let (view, _, _) = c.draw_line(&self.clay, 0, Color::WHITE);
                    let (view, _, _) = c.draw_line(&self.clay, 0, self.colors.font_available);
                    self.output_boxes.push(view);
                }
            }


@@ 263,9 283,7 @@ impl Presentation {
        self.containers.clear();
        for (id, con) in containers {
            // Focus capture device
            let container_rect = self
                .clay
                .draw_create_rect_view(con.size, [0., 0., 0., 0.66]);
            let container_rect = self.clay.draw_create_rect_view(con.size, self.colors.bg);
            container_rect.set_position(con.position);
            container_rect.set_enabled(true);



@@ 277,7 295,8 @@ impl Presentation {
                    view_texts: Vec::new(),
                    view_idx: None,
                    position: con.position,
                    color: [0., 0., 0., 0.],
                    color: self.colors.bg,
                    colors: self.colors.clone(),
                    size: con.size,
                    focused: false,
                },


@@ 292,13 311,11 @@ impl Presentation {

            let color = match (focus, &self.selection) {
                (true, Some(Selection { container, .. })) if container == id => {
                    [0.05, 0.05, 0.075, 0.66]
                }
                (false, Some(Selection { container, .. })) if container == id => {
                    [0.025, 0.025, 0.05, 0.66]
                    self.colors.selection
                }
                (true, _) => [0.05, 0.05, 0.05, 0.66],
                (false, _) => [0., 0., 0., 0.66],
                (false, Some(Selection { container, .. })) if container == id => self.colors.drag,
                (true, _) => self.colors.focus,
                (false, _) => self.colors.bg,
            };

            if con.color != color {

M src/utils/mod.rs => src/utils/mod.rs +1 -2
@@ 1,5 1,4 @@

#[allow(dead_code,non_snake_case)]
#[allow(dead_code, non_snake_case)]
pub mod Modifiers {
    pub const SHIFT: u32 = 1 << 0;
    pub const CAPS: u32 = 1 << 1;