wzrd

An ICCCM & EWMH compliant X11 reparenting, dynamic window manager, written in Rust
git clone git://git.deurzen.net/wzrd
Log | Files | Refs | LICENSE

main.rs (19078B)


      1 #![warn(clippy::all)]
      2 #![warn(missing_copy_implementations)]
      3 #![warn(missing_debug_implementations)]
      4 #![warn(missing_debug_implementations)]
      5 #![warn(rust_2018_idioms)]
      6 #![warn(trivial_numeric_casts)]
      7 #![warn(trivial_numeric_casts)]
      8 #![warn(unsafe_code)]
      9 #![warn(unused_extern_crates)]
     10 #![warn(unused_import_braces)]
     11 #![warn(unused_qualifications)]
     12 #![allow(dead_code)]
     13 #![recursion_limit = "256"]
     14 
     15 #[macro_use]
     16 extern crate log;
     17 
     18 #[allow(unused_imports)]
     19 use simplelog::LevelFilter;
     20 #[allow(unused_imports)]
     21 use simplelog::SimpleLogger;
     22 
     23 use winsys::geometry::Edge;
     24 use winsys::xdata::xconnection::XConnection;
     25 pub use winsys::Result;
     26 
     27 use std::collections::HashMap;
     28 use std::collections::HashSet;
     29 
     30 #[macro_use]
     31 mod macros;
     32 
     33 #[macro_use]
     34 mod defaults;
     35 
     36 mod binding;
     37 mod change;
     38 mod client;
     39 mod compare;
     40 mod consume;
     41 mod cycle;
     42 mod decoration;
     43 mod error;
     44 mod identify;
     45 mod jump;
     46 mod layout;
     47 mod model;
     48 mod partition;
     49 mod placement;
     50 mod rule;
     51 mod stack;
     52 mod util;
     53 mod workspace;
     54 mod zone;
     55 
     56 use binding::KeyBindings;
     57 use binding::MouseBindings;
     58 use change::Change;
     59 use change::Direction;
     60 use change::Toggle;
     61 use compare::MatchMethod;
     62 use jump::JumpCriterium;
     63 use layout::LayoutKind;
     64 use model::Model;
     65 use winsys::input::Button;
     66 use winsys::input::Key;
     67 use winsys::input::KeyInput;
     68 use winsys::input::Modifier;
     69 use winsys::input::MouseEventKind;
     70 use winsys::input::MouseInput;
     71 use winsys::input::MouseInputTarget;
     72 use winsys::window::Window;
     73 use workspace::ClientSelector;
     74 
     75 pub fn main() -> Result<()> {
     76     #[cfg(debug_assertions)]
     77     SimpleLogger::init(LevelFilter::Debug, simplelog::Config::default())?;
     78 
     79     let (conn, screen_num) = x11rb::connect(None)?;
     80     let (mouse_bindings, key_bindings) = init_bindings();
     81 
     82     Model::new(
     83         &mut XConnection::new(&conn, screen_num)?,
     84         &key_bindings,
     85         &mouse_bindings,
     86     )
     87     .run(key_bindings, mouse_bindings);
     88 
     89     Ok(())
     90 }
     91 
     92 fn init_bindings() -> (MouseBindings, KeyBindings) {
     93     let mut mouse_bindings = MouseBindings::new();
     94     let mut key_bindings = KeyBindings::new();
     95 
     96     mouse_bindings.insert(
     97         MouseInput {
     98             target: MouseInputTarget::Client,
     99             button: Button::Right,
    100             modifiers: hashset!(Modifier::Alt, Modifier::Ctrl),
    101         },
    102         |model: &mut Model<'_>, window: Option<Window>| -> bool {
    103             if let Some(window) = window {
    104                 model.set_floating_window(window, Toggle::Reverse);
    105             }
    106 
    107             true
    108         },
    109     );
    110 
    111     mouse_bindings.insert(
    112         MouseInput {
    113             target: MouseInputTarget::Client,
    114             button: Button::Middle,
    115             modifiers: hashset!(Modifier::Alt, Modifier::Ctrl, Modifier::Shift),
    116         },
    117         |model: &mut Model<'_>, window: Option<Window>| -> bool {
    118             if let Some(window) = window {
    119                 model.set_fullscreen_window(window, Toggle::Reverse);
    120             }
    121 
    122             true
    123         },
    124     );
    125 
    126     mouse_bindings.insert(
    127         MouseInput {
    128             target: MouseInputTarget::Client,
    129             button: Button::Middle,
    130             modifiers: hashset!(Modifier::Alt),
    131         },
    132         |model: &mut Model<'_>, window: Option<Window>| -> bool {
    133             if let Some(window) = window {
    134                 model.center_window(window);
    135             }
    136 
    137             true
    138         },
    139     );
    140 
    141     mouse_bindings.insert(
    142         MouseInput {
    143             target: MouseInputTarget::Client,
    144             button: Button::Left,
    145             modifiers: hashset!(Modifier::Alt),
    146         },
    147         |model: &mut Model<'_>, window: Option<Window>| -> bool {
    148             if let Some(window) = window {
    149                 model.start_moving(window);
    150             }
    151 
    152             true
    153         },
    154     );
    155 
    156     mouse_bindings.insert(
    157         MouseInput {
    158             target: MouseInputTarget::Client,
    159             button: Button::Right,
    160             modifiers: hashset!(Modifier::Alt),
    161         },
    162         |model: &mut Model<'_>, window: Option<Window>| -> bool {
    163             if let Some(window) = window {
    164                 model.start_resizing(window);
    165             }
    166 
    167             true
    168         },
    169     );
    170 
    171     mouse_bindings.insert(
    172         MouseInput {
    173             target: MouseInputTarget::Client,
    174             button: Button::ScrollDown,
    175             modifiers: hashset!(Modifier::Alt, Modifier::Ctrl, Modifier::Shift),
    176         },
    177         |model: &mut Model<'_>, window: Option<Window>| -> bool {
    178             if let Some(window) = window {
    179                 model.grow_ratio_window(window, -15);
    180             }
    181 
    182             true
    183         },
    184     );
    185 
    186     mouse_bindings.insert(
    187         MouseInput {
    188             target: MouseInputTarget::Client,
    189             button: Button::ScrollUp,
    190             modifiers: hashset!(Modifier::Alt, Modifier::Ctrl, Modifier::Shift),
    191         },
    192         |model: &mut Model<'_>, window: Option<Window>| -> bool {
    193             if let Some(window) = window {
    194                 model.grow_ratio_window(window, 15);
    195             }
    196 
    197             true
    198         },
    199     );
    200 
    201     mouse_bindings.insert(
    202         MouseInput {
    203             target: MouseInputTarget::Client,
    204             button: Button::ScrollUp,
    205             modifiers: hashset!(Modifier::Alt),
    206         },
    207         |model: &mut Model<'_>, _: Option<Window>| -> bool {
    208             model.cycle_focus(Direction::Backward);
    209             false
    210         }
    211     );
    212 
    213     mouse_bindings.insert(
    214         MouseInput {
    215             target: MouseInputTarget::Client,
    216             button: Button::ScrollDown,
    217             modifiers: hashset!(Modifier::Alt),
    218         },
    219         |model: &mut Model<'_>, _: Option<Window>| -> bool {
    220             model.cycle_focus(Direction::Forward);
    221             false
    222         }
    223     );
    224 
    225     mouse_bindings.insert(
    226         MouseInput {
    227             target: MouseInputTarget::Global,
    228             button: Button::ScrollUp,
    229             modifiers: hashset!(Modifier::Alt, Modifier::Shift),
    230         },
    231         |model: &mut Model<'_>, _: Option<Window>| -> bool {
    232             model.activate_next_workspace(Direction::Backward);
    233             false
    234         }
    235     );
    236 
    237     mouse_bindings.insert(
    238         MouseInput {
    239             target: MouseInputTarget::Global,
    240             button: Button::ScrollDown,
    241             modifiers: hashset!(Modifier::Alt, Modifier::Shift),
    242         },
    243         |model: &mut Model<'_>, _: Option<Window>| -> bool {
    244             model.activate_next_workspace(Direction::Forward);
    245             false
    246         }
    247     );
    248 
    249     mouse_bindings.insert(
    250         MouseInput {
    251             target: MouseInputTarget::Client,
    252             button: Button::Backward,
    253             modifiers: hashset!(Modifier::Alt),
    254         },
    255         |model: &mut Model<'_>, window: Option<Window>| -> bool {
    256             if let Some(window) = window {
    257                 model.move_window_to_next_workspace(window, Direction::Backward);
    258             }
    259 
    260             false
    261         }
    262     );
    263 
    264     mouse_bindings.insert(
    265         MouseInput {
    266             target: MouseInputTarget::Client,
    267             button: Button::Forward,
    268             modifiers: hashset!(Modifier::Alt),
    269         },
    270         |model: &mut Model<'_>, window: Option<Window>| -> bool {
    271             if let Some(window) = window {
    272                 model.move_window_to_next_workspace(window, Direction::Forward);
    273             }
    274 
    275             false
    276         }
    277     );
    278 
    279     key_bindings.insert(
    280         KeyInput {
    281             key: Key::Escape,
    282             modifiers: hashset!(Modifier::Alt, Modifier::Ctrl, Modifier::Shift),
    283         },
    284         |model: &mut Model<'_>| {
    285             model.exit();
    286         }
    287     );
    288 
    289     key_bindings.insert(
    290         KeyInput {
    291             key: Key::J,
    292             modifiers: hashset!(Modifier::Alt),
    293         },
    294         |model: &mut Model<'_>| {
    295             model.cycle_focus(Direction::Forward);
    296         }
    297     );
    298 
    299     key_bindings.insert(
    300         KeyInput {
    301             key: Key::K,
    302             modifiers: hashset!(Modifier::Alt),
    303         },
    304         |model: &mut Model<'_>| {
    305             model.cycle_focus(Direction::Backward);
    306         }
    307     );
    308 
    309     // // (kind, target, focus): "[modifiers]-button" => action
    310     // let mouse_bindings = build_mouse_bindings!(
    311     //     }),
    312 
    313     // // "[modifiers]-key" => action
    314     // let key_bindings = build_key_bindings!(
    315     //     "1-C-S-q" => do_internal!(exit),
    316 
    317     //     // client state modifiers
    318     //     "1-c" => do_internal!(kill_focus),
    319     //     "1-S-space" => do_internal!(set_floating_focus, Toggle::Reverse),
    320     //     "1-f" => do_internal!(set_fullscreen_focus, Toggle::Reverse),
    321     //     "1-x" => do_internal!(set_stick_focus, Toggle::Reverse),
    322     //     "1-2-C-f" => do_internal!(set_contained_focus, Toggle::Reverse),
    323     //     "1-2-C-i" => do_internal!(set_invincible_focus, Toggle::Reverse),
    324     //     "1-2-C-p" => do_internal!(set_producing_focus, Toggle::Reverse),
    325     //     "1-2-C-y" => do_internal!(set_iconifyable_focus, Toggle::Reverse),
    326     //     "1-y" => do_internal!(set_iconify_focus, Toggle::On),
    327     //     "1-u" => do_internal!(pop_deiconify),
    328     //     "1-2-u" => do_internal_block!(model, {
    329     //         model.deiconify_all(model.active_workspace());
    330     //     }),
    331 
    332     //     // free client arrangers
    333     //     "1-C-space" => do_internal!(center_focus),
    334     //     "1-C-h" => do_internal!(nudge_focus, Edge::Left, 15),
    335     //     "1-C-j" => do_internal!(nudge_focus, Edge::Bottom, 15),
    336     //     "1-C-k" => do_internal!(nudge_focus, Edge::Top, 15),
    337     //     "1-C-l" => do_internal!(nudge_focus, Edge::Right, 15),
    338     //     "1-C-S-h" => do_internal!(stretch_focus, Edge::Left, 15),
    339     //     "1-C-S-j" => do_internal!(stretch_focus, Edge::Bottom, 15),
    340     //     "1-C-S-k" => do_internal!(stretch_focus, Edge::Top, 15),
    341     //     "1-C-S-l" => do_internal!(stretch_focus, Edge::Right, 15),
    342     //     "1-C-S-y" => do_internal!(stretch_focus, Edge::Left, -15),
    343     //     "1-C-S-u" => do_internal!(stretch_focus, Edge::Bottom, -15),
    344     //     "1-C-S-i" => do_internal!(stretch_focus, Edge::Top, -15),
    345     //     "1-C-S-o" => do_internal!(stretch_focus, Edge::Right, -15),
    346     //     "1-C-Left" => do_internal!(snap_focus, Edge::Left),
    347     //     "1-C-Down" => do_internal!(snap_focus, Edge::Bottom),
    348     //     "1-C-Up" => do_internal!(snap_focus, Edge::Top),
    349     //     "1-C-Right" => do_internal!(snap_focus, Edge::Right),
    350 
    351     //     // client order modifiers
    352     //     "1-j" => do_internal!(cycle_focus, Direction::Forward),
    353     //     "1-k" => do_internal!(cycle_focus, Direction::Backward),
    354     //     "1-S-j" => do_internal!(drag_focus, Direction::Forward),
    355     //     "1-S-k" => do_internal!(drag_focus, Direction::Backward),
    356     //     "1-S-semicolon" => do_internal!(rotate_clients, Direction::Forward),
    357     //     "1-S-comma" => do_internal!(rotate_clients, Direction::Backward),
    358 
    359     //     // zone creators
    360     //     "1-C-Return" => do_internal!(create_layout_zone),
    361     //     "1-C-S-Return" => do_internal!(create_tab_zone),
    362     //     "1-C-C" => do_internal!(delete_zone),
    363 
    364     //     // zone order modifiers
    365     //     // "1-C-j" => do_internal!(cycle_zones, Direction::Forward),
    366     //     // "1-C-k" => do_internal!(cycle_zones, Direction::Backward),
    367 
    368     //     // active workspace layout modifiers
    369     //     "1-S-f" => do_internal!(set_layout, LayoutKind::Float),
    370     //     "1-S-l" => do_internal!(set_layout, LayoutKind::BLFloat),
    371     //     "1-z" => do_internal!(set_layout, LayoutKind::SingleFloat),
    372     //     "1-S-z" => do_internal!(set_layout, LayoutKind::BLSingleFloat),
    373     //     "1-m" => do_internal!(set_layout, LayoutKind::Monocle),
    374     //     "1-g" => do_internal!(set_layout, LayoutKind::Center),
    375     //     "1-t" => do_internal!(set_layout, LayoutKind::Stack),
    376     //     "1-S-t" => do_internal!(set_layout, LayoutKind::SStack),
    377     //     "1-C-S-p" => do_internal!(set_layout, LayoutKind::Paper),
    378     //     "1-2-C-S-p" => do_internal!(set_layout, LayoutKind::SPaper),
    379     //     "1-C-S-b" => do_internal!(set_layout, LayoutKind::BStack),
    380     //     "1-2-C-S-b" => do_internal!(set_layout, LayoutKind::SBStack),
    381     //     "1-S-y" => do_internal!(set_layout, LayoutKind::Horz),
    382     //     "1-C-y" => do_internal!(set_layout, LayoutKind::SHorz),
    383     //     "1-S-v" => do_internal!(set_layout, LayoutKind::Vert),
    384     //     "1-C-v" => do_internal!(set_layout, LayoutKind::SVert),
    385     //     "1-C-S-f" => do_internal!(apply_float_retain_region),
    386     //     "1-space" => do_internal!(toggle_layout),
    387 
    388     //     // active workspace layout data modifiers
    389     //     "1-plus" => do_internal!(change_gap_size, Change::Inc(5u32)),
    390     //     "1-minus" => do_internal!(change_gap_size, Change::Dec(5u32)),
    391     //     "1-S-equal" => do_internal!(reset_gap_size),
    392     //     "1-i" => do_internal!(change_main_count, Change::Inc(1u32)),
    393     //     "1-d" => do_internal!(change_main_count, Change::Dec(1u32)),
    394     //     "1-l" => do_internal!(change_main_factor, Change::Inc(0.05f32)),
    395     //     "1-h" => do_internal!(change_main_factor, Change::Dec(0.05f32)),
    396     //     "1-S-Left" => do_internal!(change_margin, Edge::Left, Change::Inc(5i32)),
    397     //     "1-C-S-Left" => do_internal!(change_margin, Edge::Left, Change::Dec(5i32)),
    398     //     "1-S-Up" => do_internal!(change_margin, Edge::Top, Change::Inc(5i32)),
    399     //     "1-C-S-Up" => do_internal!(change_margin, Edge::Top, Change::Dec(5i32)),
    400     //     "1-S-Down" => do_internal!(change_margin, Edge::Bottom, Change::Inc(5i32)),
    401     //     "1-C-S-Down" => do_internal!(change_margin, Edge::Bottom, Change::Dec(5i32)),
    402     //     "1-S-Right" => do_internal!(change_margin, Edge::Right, Change::Inc(5i32)),
    403     //     "1-C-S-Right" => do_internal!(change_margin, Edge::Right, Change::Dec(5i32)),
    404     //     "1-C-S-equal" => do_internal!(reset_margin),
    405     //     "1-2-C-S-l" => do_internal!(copy_prev_layout_data),
    406     //     "1-2-C-S-equal" => do_internal!(reset_layout_data),
    407 
    408     //     // workspace activators
    409     //     "1-Escape" => do_internal!(toggle_workspace),
    410     //     "1-bracketleft" => do_internal!(activate_next_workspace, Direction::Backward),
    411     //     "1-bracketright" => do_internal!(activate_next_workspace, Direction::Forward),
    412     //     "1-1" => do_internal!(activate_workspace, 0),
    413     //     "1-2" => do_internal!(activate_workspace, 1),
    414     //     "1-3" => do_internal!(activate_workspace, 2),
    415     //     "1-4" => do_internal!(activate_workspace, 3),
    416     //     "1-5" => do_internal!(activate_workspace, 4),
    417     //     "1-6" => do_internal!(activate_workspace, 5),
    418     //     "1-7" => do_internal!(activate_workspace, 6),
    419     //     "1-8" => do_internal!(activate_workspace, 7),
    420     //     "1-9" => do_internal!(activate_workspace, 8),
    421     //     "1-0" => do_internal!(activate_workspace, 9),
    422 
    423     //     // workspace client movers
    424     //     "1-S-bracketleft" => do_internal!(move_focus_to_next_workspace, Direction::Backward),
    425     //     "1-S-bracketright" => do_internal!(move_focus_to_next_workspace, Direction::Forward),
    426     //     "1-S-1" => do_internal!(move_focus_to_workspace, 0),
    427     //     "1-S-2" => do_internal!(move_focus_to_workspace, 1),
    428     //     "1-S-3" => do_internal!(move_focus_to_workspace, 2),
    429     //     "1-S-4" => do_internal!(move_focus_to_workspace, 3),
    430     //     "1-S-5" => do_internal!(move_focus_to_workspace, 4),
    431     //     "1-S-6" => do_internal!(move_focus_to_workspace, 5),
    432     //     "1-S-7" => do_internal!(move_focus_to_workspace, 6),
    433     //     "1-S-8" => do_internal!(move_focus_to_workspace, 7),
    434     //     "1-S-9" => do_internal!(move_focus_to_workspace, 8),
    435     //     "1-S-0" => do_internal!(move_focus_to_workspace, 9),
    436 
    437     //     // placeable region modifiers
    438     //     "1-v" => do_internal!(toggle_screen_struts),
    439 
    440     //     // client jump criteria
    441     //     "1-b" => do_internal!(jump_client,
    442     //         JumpCriterium::ByClass(MatchMethod::Equals("qutebrowser"))
    443     //     ),
    444     //     "1-S-b" => do_internal!(jump_client,
    445     //         JumpCriterium::ByClass(MatchMethod::Equals("Firefox"))
    446     //     ),
    447     //     "1-C-b" => do_internal!(jump_client,
    448     //         JumpCriterium::ByClass(MatchMethod::Equals("Chromium"))
    449     //     ),
    450     //     "1-2-space" => do_internal!(jump_client,
    451     //         JumpCriterium::ByClass(MatchMethod::Equals("Spotify"))
    452     //     ),
    453     //     "1-e" => do_internal_block!(model, {
    454     //         model.jump_client(JumpCriterium::ByName(
    455     //             MatchMethod::Contains("[vim]"),
    456     //         ));
    457     //     }),
    458     //     "1-slash" => do_internal_block!(model, {
    459     //         model.jump_client(JumpCriterium::OnWorkspaceBySelector(
    460     //             model.active_workspace(),
    461     //             &ClientSelector::Last,
    462     //         ));
    463     //     }),
    464     //     "1-period" => do_internal_block!(model, {
    465     //         model.jump_client(JumpCriterium::OnWorkspaceBySelector(
    466     //             model.active_workspace(),
    467     //             &ClientSelector::AtMaster,
    468     //         ));
    469     //     }),
    470     //     "1-comma" => do_internal_block!(model, {
    471     //         model.jump_client(JumpCriterium::OnWorkspaceBySelector(
    472     //             model.active_workspace(),
    473     //             &ClientSelector::First,
    474     //         ));
    475     //     }),
    476 
    477     //     // external spawn commands
    478     //     "XF86AudioPlay", "1-2-p" => spawn_external!("playerctl play-pause"),
    479     //     "XF86AudioPrev", "1-2-k" => spawn_external!("playerctl previous"),
    480     //     "XF86AudioNext", "1-2-j" => spawn_external!("playerctl next"),
    481     //     "1-2-BackSpace" => spawn_external!("playerctl stop"),
    482     //     "XF86AudioMute" => spawn_external!("amixer -D pulse sset Master toggle"),
    483     //     "XF86AudioLowerVolume" => spawn_external!("amixer -D pulse sset Master 5%-"),
    484     //     "XF86AudioRaiseVolume" => spawn_external!("amixer -D pulse sset Master 5%+"),
    485 
    486     //     "1-Return" => spawn_external!("st"),
    487     //     "1-S-Return" => spawn_external!(concat!("st -n ", WM_NAME!(), ":cf")),
    488 
    489     //     "1-p" => spawn_external!("dmenu_run"),
    490     //     "1-q" => spawn_external!("qutebrowser"),
    491     //     "1-S-q" => spawn_external!("firefox"),
    492     //     "1-C-q" => spawn_external!("chromium"),
    493 
    494     //     "1-C-e" => spawn_external!("st -g 140x42 -e zsh -i -c neomutt"),
    495     //     "1-C-s" => spawn_external!("st -g 80x42 -e zsh -i -c sncli"),
    496     //     "1-C-i" => spawn_external!("st -g 80x42 -e zsh -i -c irssi"),
    497 
    498     //     "S-XF86AudioMute" => spawn_external!("amixer -D pulse sset Capture toggle"),
    499     //     "XF86AudioMicMute" => spawn_external!("amixer -D pulse sset Capture toggle"),
    500 
    501     //     // external shell commands
    502     //     "1-S-p" => spawn_from_shell!("$HOME/bin/dmenupass"),
    503     //     "1-C-p" => spawn_from_shell!("$HOME/bin/dmenupass --copy"),
    504     //     "1-S-o" => spawn_from_shell!("$HOME/bin/dmenunotify"),
    505     //     "Print", "1-2-slash" => spawn_from_shell!(
    506     //         "maim -u -m 1 -s \
    507     //         $(date +$HOME/screenshots/scrots/SS_%Y-%h-%d_%H-%M-%S.png)"
    508     //     ),
    509     //     "S-Print", "1-2-S-slash" => spawn_from_shell!(
    510     //         "maim -u -m 1 \
    511     //         $(date +$HOME/screenshots/scrots/SS_%Y-%h-%d_%H-%M-%S.png)"
    512     //     ),
    513     // );
    514 
    515     (mouse_bindings, key_bindings)
    516 }