|
| 1 | +<image width="50px" src="https://raw.githubusercontent.com/sminez/penrose/develop/icon.svg" align="left"></image> |
| 2 | +# Actions |
| 3 | + |
| 4 | +To start with we're going to assume that when we talk about running an `Action` we're talking about |
| 5 | +executing some custom code in response to a key binding bein pressed. With that in mind, lets take |
| 6 | +a look at the [KeyEventHandler][0] trait found in `penrose::core::bindings`: |
| 7 | + |
| 8 | +```rust |
| 9 | +pub trait KeyEventHandler<X: XConn> { |
| 10 | + fn call(&mut self, state: &mut State<X>, x: &X) -> Result<()>; |
| 11 | +} |
| 12 | +``` |
| 13 | + |
| 14 | +There's not much to it: you are given mutable access to the window manager `State` and a reference to |
| 15 | +the X connection. From there you can do pretty much whatever you like other than return data (we'll |
| 16 | +take a look at how you can persist and manage your own state in a bit!) |
| 17 | + |
| 18 | +To make things easier to work with (and to avoid having to implement this trait for every piece of |
| 19 | +custom logic you want to run) there are several helper functions provided for wrapping free functions |
| 20 | +of the right signature. |
| 21 | + |
| 22 | +> **NOTE**: In any case where you do not need to manage any additional state, it is _strongly_ |
| 23 | +> recommended that you make use of these helpers to write your actions as simple functions rather |
| 24 | +> than structs that implement the `KeyEventHandler` trait. |
| 25 | +
|
| 26 | + |
| 27 | +## Built-in helpers |
| 28 | + |
| 29 | +In the [penrose::builtin::actions][1] module you will find a number of helper functions for writing |
| 30 | +actions. The most general of these being `key_handler` which simply handles plumbing through the |
| 31 | +required type information for Rust to generate the `KeyEventHandler` trait implementation for you. |
| 32 | + |
| 33 | +### An example |
| 34 | +As a real example of how this can be used, here is the power menu helper I have in my own set up |
| 35 | +which makes use of the dmenu based helpers in [penrose::extensions::util::dmenu][2] to prompt the |
| 36 | +user for a selection before executing the selected action: |
| 37 | +```rust |
| 38 | +use penrose::{ |
| 39 | + builtin::actions::key_handler, |
| 40 | + core::bindings::KeyEventHandler, |
| 41 | + custom_error, |
| 42 | + extensions::util::dmenu::{DMenu, DMenuConfig, MenuMatch}, |
| 43 | + util::spawn, |
| 44 | +}; |
| 45 | +use std::process::exit; |
| 46 | + |
| 47 | +pub fn power_menu<X: XConn>() -> KeyEventHandler<X> { |
| 48 | + key_handler(|state, _| { |
| 49 | + let options = vec!["lock", "logout", "restart-wm", "shutdown", "reboot"]; |
| 50 | + let menu = DMenu::new(">>> ", options, DMenuConfig::default()); |
| 51 | + let screen_index = state.client_set.current_screen().index(); |
| 52 | + |
| 53 | + if let Ok(MenuMatch::Line(_, choice)) = menu.run(screen_index) { |
| 54 | + match choice.as_ref() { |
| 55 | + "lock" => spawn("xflock4"), |
| 56 | + "logout" => spawn("pkill -fi penrose"), |
| 57 | + "shutdown" => spawn("sudo shutdown -h now"), |
| 58 | + "reboot" => spawn("sudo reboot"), |
| 59 | + "restart-wm" => exit(0), // Wrapper script then handles restarting us |
| 60 | + _ => unimplemented!(), |
| 61 | + } |
| 62 | + } else { |
| 63 | + Ok(()) |
| 64 | + } |
| 65 | + }) |
| 66 | +} |
| 67 | +``` |
| 68 | + |
| 69 | +The window manager state is used to determine the current screen (where we want to open dmenu) |
| 70 | +but other than that we're running completely arbitrary code in response to a keypress. The main |
| 71 | +thing to keep in mind is that penrose is _single threaded_ so anything you do in an action must |
| 72 | +complete in order for the event loop to continue running. |
| 73 | + |
| 74 | +### StackSet manipulation |
| 75 | + |
| 76 | +The most common set of actions you'll want to perform are modifications to the `StackSet` in |
| 77 | +to reposition and select windows on the screen. There are [a large number of methods][3] available |
| 78 | +for modifying the current state of your windows and the [modify_with][4] helper gives you an |
| 79 | +easy way to call them directly. If you think back to the minimal example window manager we covered |
| 80 | +in the "getting started" section, we saw this in use for most of the key bindings. Paraphrasing |
| 81 | +a little, it looks like this: |
| 82 | +```rust |
| 83 | +use penrose::builtin::actions::modify_with; |
| 84 | + |
| 85 | +// Select the next available layout algorithm |
| 86 | +modify_with(|cs| cs.next_layout()); |
| 87 | + |
| 88 | +// Close the currently focused window |
| 89 | +modify_with(|cs| cs.kill_focused()); |
| 90 | +``` |
| 91 | + |
| 92 | + [0]: https://sminez.github.io/penrose/rustdoc/penrose/core/bindings/trait.KeyEventHandler.html |
| 93 | + [1]: https://sminez.github.io/penrose/rustdoc/penrose/builtin/actions/index.html |
| 94 | + [2]: https://sminez.github.io/penrose/rustdoc/penrose/extensions/util/dmenu/index.html |
| 95 | + [3]: https://sminez.github.io/penrose/rustdoc/penrose/pure/struct.StackSet.html |
| 96 | + [4]: https://sminez.github.io/penrose/rustdoc/penrose/builtin/actions/fn.modify_with.html |
0 commit comments