hyprshell_exec_lib/
plugin.rs1use anyhow::{Context, bail};
2use config_lib::Modifier;
3use hyprland::ctl::plugin;
4use hyprland_plugin::PluginConfig;
5use std::path::Path;
6use std::sync::OnceLock;
7use tracing::{debug, debug_span, info, trace};
8
9static PLUGIN_COULD_BE_BUILD: OnceLock<bool> = OnceLock::new();
14
15pub fn load_plugin(
16 switch: Option<Modifier>,
17 overview: Option<(Modifier, Box<str>)>,
18) -> anyhow::Result<()> {
19 let _span = debug_span!("load_plugin").entered();
20
21 if PLUGIN_COULD_BE_BUILD.get() == Some(&false) {
22 bail!("plugin could not be built last, skipping to prevent reload loop");
23 }
24
25 let config = PluginConfig {
26 xkb_key_switch_mod: switch.map(|s| Box::from(mod_to_xkb_key(s))),
27 xkb_key_overview_mod: overview
28 .as_ref()
29 .map(|(r#mod, _)| Box::from(r#mod.to_string())),
30 xkb_key_overview_key: overview.map(|(_, key)| key),
31 };
32
33 if check_new_plugin_needed(&config) {
34 unload().context("unable to unload old plugin")?;
35 info!("building plugin, this may take a while, please wait");
36 hyprland_plugin::generate(&config).context("unable to generate plugin")?;
37 trace!(
38 "generated plugin at {:?}",
39 hyprland_plugin::PLUGIN_OUTPUT_PATH
40 );
41 if let Err(err) = plugin::load(Path::new(hyprland_plugin::PLUGIN_OUTPUT_PATH)) {
42 PLUGIN_COULD_BE_BUILD.get_or_init(|| false);
43 trace!("plugin failed to load, disabling plugin");
44 bail!("unable to load plugin: {err:?}")
45 }
46 trace!("loaded plugin");
47 } else {
48 debug!("plugin already loaded, skipping");
49 }
50
51 Ok(())
52}
53
54pub fn check_new_plugin_needed(config: &PluginConfig) -> bool {
55 let plugins = plugin::list().unwrap_or_default();
56 trace!("plugins: {plugins:?}");
57 for plugin in plugins {
58 if plugin.name == hyprland_plugin::PLUGIN_NAME {
59 let Some(desc) = plugin.description.split(" - ").last() else {
60 continue;
61 };
62 if desc == config.to_string() {
63 return false;
65 }
66 }
67 }
68 true
69}
70
71pub fn unload() -> anyhow::Result<()> {
72 let plugins = plugin::list().unwrap_or_default();
73 for plugin in plugins {
74 if plugin.name == hyprland_plugin::PLUGIN_NAME {
75 debug!("plugin loaded, unloading it");
76 plugin::unload(Path::new(hyprland_plugin::PLUGIN_OUTPUT_PATH)).with_context(|| {
77 format!(
78 "unable to unload old plugin at: {}",
79 hyprland_plugin::PLUGIN_OUTPUT_PATH
80 )
81 })?;
82 debug!("plugin unloaded");
83 }
84 }
85 Ok(())
86}
87
88#[allow(clippy::must_use_candidate)]
89pub const fn mod_to_xkb_key(r#mod: Modifier) -> &'static str {
90 match r#mod {
91 Modifier::Alt => "XKB_KEY_Alt",
92 Modifier::Ctrl => "XKB_KEY_Control",
93 Modifier::Super => "XKB_KEY_Super",
94 }
95}