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