hyprshell_exec_lib/
plugin.rs

1use anyhow::Context;
2use config_lib::Modifier;
3use hyprland::ctl::plugin;
4use hyprland_plugin::PluginConfig;
5use std::path::Path;
6use tracing::{debug, debug_span, trace};
7
8pub fn load_plugin(
9    switch: Option<Modifier>,
10    overview: Option<(Modifier, Box<str>)>,
11) -> anyhow::Result<()> {
12    let _span = debug_span!("load_plugin").entered();
13    let config = PluginConfig {
14        xkb_key_switch_mod: switch.map(|s| Box::from(mod_to_xkb_key(s))),
15        xkb_key_overview_mod: overview
16            .as_ref()
17            .map(|(r#mod, _)| Box::from(r#mod.to_string())),
18        xkb_key_overview_key: overview.map(|(_, key)| key),
19    };
20
21    if check_new_plugin_needed(&config) {
22        unload().context("unable to unload old plugin")?;
23        hyprland_plugin::generate(&config).context("unable to generate plugin")?;
24        trace!(
25            "generated plugin at {:?}",
26            hyprland_plugin::PLUGIN_OUTPUT_PATH
27        );
28        plugin::load(Path::new(hyprland_plugin::PLUGIN_OUTPUT_PATH))
29            .context("unable to load plugin")?;
30        trace!("loaded plugin");
31    } else {
32        debug!("plugin already loaded, skipping");
33    }
34
35    Ok(())
36}
37
38pub fn check_new_plugin_needed(config: &PluginConfig) -> bool {
39    let plugins = plugin::list().unwrap_or_default();
40    trace!("plugins: {plugins:?}");
41    for plugin in plugins {
42        if plugin.name == hyprland_plugin::PLUGIN_NAME {
43            let Some(desc) = plugin.description.split(" - ").last() else {
44                continue;
45            };
46            if desc == config.to_string() {
47                // config didn't change, no need to reload
48                return false;
49            }
50        }
51    }
52    true
53}
54
55pub fn unload() -> anyhow::Result<()> {
56    let plugins = plugin::list().unwrap_or_default();
57    for plugin in plugins {
58        if plugin.name == hyprland_plugin::PLUGIN_NAME {
59            debug!("plugin loaded, unloading it");
60            plugin::unload(Path::new(hyprland_plugin::PLUGIN_OUTPUT_PATH)).with_context(|| {
61                format!(
62                    "unable to unload old plugin at: {}",
63                    hyprland_plugin::PLUGIN_OUTPUT_PATH
64                )
65            })?;
66            debug!("plugin unloaded");
67        }
68    }
69    Ok(())
70}
71
72#[allow(clippy::must_use_candidate)]
73pub const fn mod_to_xkb_key(r#mod: Modifier) -> &'static str {
74    match r#mod {
75        Modifier::Alt => "XKB_KEY_Alt",
76        Modifier::Ctrl => "XKB_KEY_Control",
77        Modifier::Super => "XKB_KEY_Super",
78    }
79}