[−][src]Module penrose::core::hooks
Hook for adding additional functionality around standard WindowManager actions
Overview
Hooks are the primary way of injecting custom functionality into penrose when you want to go
beyond simply binding actions to key presses. There are multiple points in normal
WindowManager execution that will trigger the running of user defined hooks, during which you
will have complete control over the window manager state and (importantly) block the event loop
until your hook exits. For details of what hook points are available, see each of the trait
methods outlined below. Note that a single Hook can register itself to be called at multiple
hook points (all, if desired!) and that hooks are allways called in the order that they are
registered with the WindowManager on init (i.e. the order of the Vec
itself).
Implementing Hook
As an example of how to write a hook and register it, lets implement a simple hook that logs each new client that is added to a particular workspace, noting if we've seen it before or not. Completely pointless, but it will serve as a nice starting point to show what is happening.
use penrose::{ core::{ data_types::WinId, hooks::Hook, xconnection::XConn }, xcb::XcbConnection, Config, Result, WindowManager, logging_error_handler }; use std::collections::{HashMap, HashSet}; // Start with the struct itself which will contain any internal state we need to track pub struct LogAddedClients { seen: HashMap<usize, HashSet<WinId>>, } // It is idiomatic for Hooks to provide a `new` method that returns a pre-boxed struct // so that you can add it straight into your hooks Vector in your main.rs impl LogAddedClients { pub fn new() -> Box<Self> { Box::new(Self { seen: HashMap::new() }) } } // As we only care about one of the hook points, that is the only method we need to // implement: all other Hook methods for this struct will be no-ops impl<X: XConn> Hook<X> for LogAddedClients { fn client_added_to_workspace( &mut self, wm: &mut WindowManager<X>, id: WinId, wix: usize ) -> Result<()> { let clients = self.seen.entry(wix).or_insert(HashSet::new()); let msg = if clients.contains(&id) { format!("'{}' has been on '{}' before!", id, wix) } else { clients.insert(id); format!("'{}' was added to '{}' for the first time", id, wix) }; wm.log(&msg) } } // Now we simply pass our hook to the WindowManager when we create it fn main() -> penrose::Result<()> { let mut manager = WindowManager::new( Config::default(), XcbConnection::new()?, vec![LogAddedClients::new()], logging_error_handler() ); manager.init()?; // rest of your startup logic here Ok(()) }
Now, whenever a Client is added to a Workspace (either because it has been newly created,
or because it has been moved from one workspace to another) our hook will be called, and our
log message will be included in the penrose log stream. More complicated hooks can be built
that listen to multiple triggers, but most of the time you will likely only need to implement a
single method. For an example of a more complex set up, see the Scratchpad extension which
uses multiple hooks to spawn and manage a client program outside of normal WindowManager
operation.
When hooks are called
Each Hook trigger will be called as part of normal execution of WindowManager
methods at a
point that should be relatively intuitive based on the name of the method. Each method provides
a more detailed explanation of exactly what conditions it will be called under. If you would
like to see exactly which user level actions lead to specific triggers, try turning on DEBUG
logging in your logging config as part of your main.rs and lookk for the "Running
Please see the documentation on each of the individual methods for more details.
WindowManager execution with user defined Hooks
As mentioned above, each time a hook trigger point is reached the WindowManager
stops normal
execution (including responding to XEvents) and each of the registered hooks is called in
turn. If the hook implements the method associated with the trigger that has been hit, then
your logic will be run and you will have a mutable reference to the current WindowManager
state, giving you complete control over what happens next. Note that method calls on the
WindowManager
itself will (of course) resolve immediately, but that any actions which
generate XEvents will only be processed once all hooks have run and control has returned to
the manager itself.
Traits
Hook | User defined functionality triggered by WindowManager actions. |
Type Definitions
Hooks | Utility type for defining hooks in your penrose configuration. |