feat: allow custom key for switch mode

fix: update hyprland plugin

chore: fix packaging for nix
Signed-off-by: Enrico Stemmer <enrico@h3rmt.zip>
This commit is contained in:
Enrico Stemmer 2026-01-05 02:02:17 +01:00
parent 274106dfca
commit 3d07d3ceac
No known key found for this signature in database
GPG Key ID: 104718DB4A75B70C
31 changed files with 183 additions and 125 deletions

View File

@ -1,6 +1,5 @@
use crate::flags_csv;
use crate::structs::{Config, Plugins};
use crate::util::key_to_name;
use relm4::adw::ActionRow;
use relm4::adw::gtk::SelectionMode;
use relm4::adw::prelude::*;
@ -165,13 +164,8 @@ pub fn generate_items(changes: &gtk::ListBox, config: &Config, prev_config: &Con
changes,
"Changed overview key",
format!(
"{} ({}) -> {} ({})",
prev_config.windows.overview.key,
key_to_name(&prev_config.windows.overview.key)
.unwrap_or_else(|| String::from("---")),
config.windows.overview.key,
key_to_name(&config.windows.overview.key)
.unwrap_or_else(|| String::from("---")),
"{} -> {}",
prev_config.windows.overview.key, config.windows.overview.key,
),
);
}
@ -298,6 +292,16 @@ pub fn generate_items(changes: &gtk::ListBox, config: &Config, prev_config: &Con
add_info(changes, "Enabled Switch view");
}
if prev_config.windows.switch.key != config.windows.switch.key {
add_info_subtitle(
changes,
"Changed switch key",
format!(
"{} -> {}",
prev_config.windows.switch.key, config.windows.switch.key
),
);
}
if prev_config.windows.switch.modifier != config.windows.switch.modifier {
add_info_subtitle(
changes,
@ -362,6 +366,16 @@ pub fn generate_items(changes: &gtk::ListBox, config: &Config, prev_config: &Con
add_info(changes, "Enabled Switch 2 view");
}
if prev_config.windows.switch_2.key != config.windows.switch_2.key {
add_info_subtitle(
changes,
"Changed switch 2 key",
format!(
"{} -> {}",
prev_config.windows.switch_2.key, config.windows.switch_2.key
),
);
}
if prev_config.windows.switch_2.modifier != config.windows.switch_2.modifier {
add_info_subtitle(
changes,

View File

@ -93,9 +93,9 @@ impl SimpleComponent for KeyboardShortcut {
let entry_2 = entry.clone();
let window = dialog.widgets().gtk_window_12.clone();
let send = sender.clone();
key_controller.connect_key_pressed(move |_, val, id, state| {
key_controller.connect_key_pressed(move |_, val, _, state| {
debug!("Raw key event - val: {}, state: {:?}", val, state);
match handle_key(val, state, id) {
match handle_key(val, state) {
Some((key, r#mod, label)) => {
entry.set_text(&label);
send.input(KeyboardShortcutInput::UpdateKey(key));

View File

@ -70,9 +70,9 @@ impl SimpleComponent for Switch {
#[watch]
set_sensitive: model.config.enabled,
},
_adw::ShortcutLabel::new(&mod_key_to_accelerator(model.config.modifier, &model.config.key).unwrap_or_default()) {
_adw::ShortcutLabel::new(&mod_key_to_accelerator(model.config.modifier, &model.config.key)) {
#[watch]
set_accelerator: &mod_key_to_accelerator(model.config.modifier, &model.config.key).unwrap_or_default(),
set_accelerator: &mod_key_to_accelerator(model.config.modifier, &model.config.key),
#[watch]
set_css_classes: if model.config.enabled {
if mod_key_to_accelerator(model.config.modifier, &model.config.key) == mod_key_to_accelerator(model.prev_config.modifier, &model.prev_config.key)

View File

@ -141,7 +141,7 @@ impl SimpleComponent for Windows {
let switch_2 = Switch::builder()
.launch(SwitchInit {
config: init.config.switch_2.clone(),
name: "Switch 2",
name: "Switch 2 (TODO)",
})
.forward(sender.output_sender(), WindowsOutput::Switch2);

View File

@ -68,9 +68,9 @@ impl SimpleComponent for WindowsOverview {
#[watch]
set_sensitive: model.config.enabled,
},
_adw::ShortcutLabel::new(&mod_key_to_accelerator(model.config.modifier, &model.config.key).unwrap_or_default()) {
_adw::ShortcutLabel::new(&mod_key_to_accelerator(model.config.modifier, &model.config.key)) {
#[watch]
set_accelerator: &mod_key_to_accelerator(model.config.modifier, &model.config.key).unwrap_or_default(),
set_accelerator: &mod_key_to_accelerator(model.config.modifier, &model.config.key),
#[watch]
set_css_classes: if model.config.enabled {
if mod_key_to_accelerator(model.config.modifier, &model.config.key) == mod_key_to_accelerator(model.prev_config.modifier, &model.prev_config.key)

View File

@ -248,7 +248,7 @@ impl From<Switch> for Option<config_lib::Switch> {
key: Box::from(value.key),
filter_by: vec,
switch_workspaces: value.switch_workspaces,
// TODO exclude_special_workspaces
exclude_special_workspaces: Box::from(value.exclude_special_workspaces),
})
} else {
None
@ -295,7 +295,7 @@ impl From<Overview> for Option<config_lib::Overview> {
modifier: value.modifier.into(),
filter_by: vec,
hide_filtered: false,
// TODO exclude_special_workspaces
exclude_special_workspaces: Box::from(value.exclude_special_workspaces),
})
} else {
None

View File

@ -1,6 +1,6 @@
use crate::structs::ConfigModifier;
use relm4::gtk::gdk::{Cursor, Display, Key, ModifierType};
use relm4::gtk::prelude::{Cast, DisplayExtManual, EditableExt, WidgetExt};
use relm4::gtk::gdk::{Cursor, Key, ModifierType};
use relm4::gtk::prelude::{Cast, EditableExt, WidgetExt};
use relm4::{adw, gtk};
// use relm4::tokio::time::sleep;
use tracing::{instrument, warn};
@ -87,11 +87,7 @@ impl SelectRow for gtk::ListBox {
}
}
pub fn handle_key(
val: Key,
state: ModifierType,
id: u32,
) -> Option<(String, ConfigModifier, String)> {
pub fn handle_key(val: Key, state: ModifierType) -> Option<(String, ConfigModifier, String)> {
let key_name = val.name()?;
let modifier = match val {
Key::Alt_L | Key::Alt_R => ConfigModifier::Alt,
@ -112,7 +108,7 @@ pub fn handle_key(
format!("{modifier} + {key_name}")
};
Some((format!("code:{id}"), modifier, label))
Some((key_name.to_string(), modifier, label))
}
pub fn default_config() -> config_lib::Config {
@ -123,39 +119,31 @@ pub fn default_config() -> config_lib::Config {
}
#[instrument(level = "trace", ret(level = "trace"))]
pub fn mod_key_to_accelerator(modifier: ConfigModifier, key: &str) -> Option<String> {
let key = key_to_name(key)?;
pub fn mod_key_to_accelerator(modifier: ConfigModifier, key: &str) -> String {
// correct some keys that can sometimes have wrong capitalization
let key = match &*key.to_lowercase() {
"super_l" => "Super_L",
"super_r" => "Super_R",
"alt_l" => "Alt_L",
"alt_r" => "Alt_R",
"control_l" => "Control_L",
"control_r" => "Control_R",
_ => key,
};
if modifier == ConfigModifier::None {
Some(key)
key.to_string()
} else {
Some(format!("<{modifier}>{key}"))
format!("<{modifier}>{key}")
}
}
#[instrument(level = "trace", ret(level = "trace"))]
pub fn mod_key_to_string(modifier: ConfigModifier, key: &str) -> String {
if modifier == ConfigModifier::None {
key_to_name(key).unwrap_or_else(|| "---".to_string())
key.to_string()
} else {
format!(
"{} + {}",
modifier,
key_to_name(key).unwrap_or_else(|| "---".to_string())
)
}
}
pub fn key_to_name(key: &str) -> Option<String> {
// key is keycode
if key.starts_with("code:") {
let key_id = key.split(':').nth(1)?;
let code = key_id.parse::<u32>().ok()?;
let display = &Display::default()?;
let data = display.map_keycode(code)?;
let (_, key) = data.iter().find(|(m, _k)| m.level() == 0)?;
Some(key.name()?.to_string())
} else {
Some(key.to_string())
format!("{modifier} + {key}")
}
}

View File

@ -14,5 +14,4 @@ pub use modifier::*;
pub use save::write_config;
pub use structs::*;
// TODO inc
pub const CURRENT_CONFIG_VERSION: u16 = 3;

View File

@ -42,6 +42,7 @@ impl From<old_structs::Switch> for crate::Switch {
modifier: value.modifier.into(),
key: "tab".into(),
switch_workspaces: value.show_workspaces,
exclude_special_workspaces: "".into(),
}
}
}

View File

@ -29,6 +29,7 @@ impl From<old_structs::Overview> for crate::Overview {
filter_by: value.filter_by,
hide_filtered: value.hide_filtered,
launcher: value.launcher.into(),
exclude_special_workspaces: "".into(),
}
}
}

View File

@ -45,6 +45,8 @@ pub struct Overview {
pub filter_by: Vec<FilterBy>,
#[default = false]
pub hide_filtered: bool,
#[default = ""]
pub exclude_special_workspaces: Box<str>,
}
#[derive(SmartDefault, Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
@ -188,6 +190,8 @@ pub struct Switch {
pub filter_by: Vec<FilterBy>,
#[default = false]
pub switch_workspaces: bool,
#[default = ""]
pub exclude_special_workspaces: Box<str>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]

View File

@ -13,7 +13,7 @@ use tracing::{debug, debug_span, info, trace};
static PLUGIN_COULD_BE_BUILD: OnceLock<bool> = OnceLock::new();
pub fn load_plugin(
switch: Option<Modifier>,
switch: Option<(Modifier, Box<str>)>,
overview: Option<(Modifier, Box<str>)>,
) -> anyhow::Result<()> {
let _span = debug_span!("load_plugin").entered();
@ -23,7 +23,10 @@ pub fn load_plugin(
}
let config = PluginConfig {
xkb_key_switch_mod: switch.map(|s| Box::from(mod_to_xkb_key(s))),
xkb_key_switch_mod: switch
.as_ref()
.map(|(r#mod, _)| Box::from(mod_to_xkb_key(*r#mod))),
xkb_key_switch_key: switch.map(|(_, key)| key),
xkb_key_overview_mod: overview
.as_ref()
.map(|(r#mod, _)| Box::from(r#mod.to_string())),
@ -91,7 +94,6 @@ pub const fn mod_to_xkb_key(r#mod: Modifier) -> &'static str {
Modifier::Alt => "XKB_KEY_Alt",
Modifier::Ctrl => "XKB_KEY_Control",
Modifier::Super => "XKB_KEY_Super",
// TODO
Modifier::None => "XKB_KEY_NoSymbol",
}
}

View File

@ -32,7 +32,19 @@
</configurations>
</component>
<component name="ChangeListManager">
<list default="true" id="841e5689-e8a3-4a78-b7a4-76d1dbd901c1" name="Changes" comment="fix: cancel key events when opening hyprshell overview and switch" />
<list default="true" id="841e5689-e8a3-4a78-b7a4-76d1dbd901c1" name="Changes" comment="fix: cancel key events when opening hyprshell overview and switch">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Makefile" beforeDir="false" afterPath="$PROJECT_DIR$/Makefile" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/defs-test.h" beforeDir="false" afterPath="$PROJECT_DIR$/src/defs-test.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/defs.h" beforeDir="false" afterPath="$PROJECT_DIR$/src/defs.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/globals.h" beforeDir="false" afterPath="$PROJECT_DIR$/src/globals.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/handlers.h" beforeDir="false" afterPath="$PROJECT_DIR$/src/handlers.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/init.cpp" beforeDir="false" afterPath="$PROJECT_DIR$/src/init.cpp" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/key-press.cpp" beforeDir="false" afterPath="$PROJECT_DIR$/src/key-press.cpp" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/keyboard-focus.cpp" beforeDir="false" afterPath="$PROJECT_DIR$/src/keyboard-focus.cpp" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/layer-change.cpp" beforeDir="false" afterPath="$PROJECT_DIR$/src/layer-change.cpp" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/mouse-button.cpp" beforeDir="false" afterPath="$PROJECT_DIR$/src/mouse-button.cpp" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@ -426,7 +438,7 @@
<workItem from="1757544657889" duration="7000" />
<workItem from="1757544674626" duration="5012000" />
<workItem from="1766704524077" duration="509000" />
<workItem from="1767562310610" duration="196000" />
<workItem from="1767562310610" duration="8026000" />
</task>
<task id="LOCAL-00001" summary="fix: cancel key events when opening hyprshell overview and switch">
<option name="closed" value="true" />

View File

@ -19,15 +19,14 @@ LDFLAGS := -shared --no-gnu-unique
all: build
# Build by compiling object files then linking into a shared object.
BUILD_DIR := build
OBJ := $(patsubst $(SRCS_DIR)/%.cpp,$(BUILD_DIR)/%.o,$(SRCS))
OBJ := $(patsubst $(SRCS_DIR)/%.cpp,$(PREPAREDIR)/%.o,$(SRCS))
build: $(TARGET)
$(TARGET): $(OBJ)
$(CXX) $(LDFLAGS) -o $@ $^
$(BUILD_DIR)/%.o: $(SRCS_DIR)/%.cpp
$(PREPAREDIR)/%.o: $(SRCS_DIR)/%.cpp
@mkdir -p $(dir $@)
$(CXX) $(CXXFLAGS) -c $< -o $@
@ -60,4 +59,4 @@ test-combined: build-combined
clean:
@rm -f $(TARGET)
@rm -rf $(PREPAREDIR) $(BUILD_DIR)
@rm -rf $(PREPAREDIR)

View File

@ -7,6 +7,7 @@
#define HYPRSHELL_SWTICH_XKB_MOD_L XKB_KEY_Alt_L
#define HYPRSHELL_SWTICH_XKB_MOD_R XKB_KEY_Alt_R
#define HYPRSHELL_SWITCH_KEY "tab"
#define HYPRSHELL_OVERVIEW_MOD "Super"
#define HYPRSHELL_OVERVIEW_KEY "super_l"

View File

@ -23,6 +23,8 @@ struct PluginDescriptionInfo {
#define HYPRSHELL_SWTICH_XKB_MOD_L $HYPRSHELL_SWTICH_XKB_MOD_L$
#define HYPRSHELL_SWTICH_XKB_MOD_R $HYPRSHELL_SWTICH_XKB_MOD_R$
#define HYPRSHELL_SWITCH_KEY "$HYPRSHELL_SWITCH_KEY$"
#define HYPRSHELL_OVERVIEW_MOD "$HYPRSHELL_OVERVIEW_MOD$"
#define HYPRSHELL_OVERVIEW_KEY "$HYPRSHELL_OVERVIEW_KEY$"

View File

@ -10,6 +10,7 @@ inline bool LAYER_VISIBLE = false;
inline bool CHECK_NO_MOUSE_BUTTON_PRESSED = false;
inline xkb_keysym_t OVERVIEW_KEY;
inline xkb_keysym_t SWITCH_KEY;
PluginDescriptionInfo init(HANDLE handle);

View File

@ -1,6 +1,6 @@
#pragma once
#include <hyprland/src/devices/IPointer.hpp>
#include <hyprland/src/desktop/LayerSurface.hpp>
#include <hyprland/src/desktop/view/LayerSurface.hpp>
#include <hyprland/src/plugins/PluginAPI.hpp>

View File

@ -20,6 +20,7 @@ PluginDescriptionInfo init(HANDLE handle) {
// ignore that this can return XKB_KEY_NoSymbol, it is only used to check if keysym equals
OVERVIEW_KEY = xkb_keysym_from_name(HYPRSHELL_OVERVIEW_KEY, XKB_KEYSYM_CASE_INSENSITIVE);
SWITCH_KEY = xkb_keysym_from_name(HYPRSHELL_SWITCH_KEY, XKB_KEYSYM_CASE_INSENSITIVE);
if constexpr (HYPRSHELL_PRINT_DEBUG == 1) {
const auto info = std::string("Config: ") +
HYPRSHELL_OVERVIEW_KEY + ", " +

View File

@ -24,18 +24,19 @@ void onKeyPress(const std::unordered_map<std::string, std::any> &data, SCallback
const uint32_t keycode = event.keycode + 8; // +8 because xkbcommon expects +8 from libinput
const bool release = event.state == WL_KEYBOARD_KEY_STATE_RELEASED;
const bool shiftActive = xkb_state_mod_name_is_active(state, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_EFFECTIVE) == 1;
// const bool shiftActive = xkb_state_mod_name_is_active(state, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_EFFECTIVE) == 1;
const bool ctrlActive = xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) == 1;
const bool superActive = xkb_state_mod_name_is_active(state, XKB_MOD_NAME_LOGO, XKB_STATE_MODS_EFFECTIVE) == 1;
const bool altActive = xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT, XKB_STATE_MODS_EFFECTIVE) == 1;
const xkb_keysym_t keysym = xkb_state_key_get_one_sym(keyboard->m_xkbState, keycode);
const xkb_keysym_t keysym = xkb_state_key_get_one_sym(state, keycode);
if constexpr (HYPRSHELL_PRINT_DEBUG_DEBUG == 1) {
char buffer[20];
xkb_keysym_get_name(keysym, buffer, sizeof(buffer));
const auto bigString = std::string("Name: ") + buffer +
" | KeySym: " + std::to_string(keysym) +
(shiftActive ? " | Shift: Active" : "") +
// (shiftActive ? " | Shift: Active" : "") +
(ctrlActive ? " | Control: Active" : "") +
(superActive ? " | Meta: Active" : "") +
(altActive ? " | Alt: Active" : "") +
@ -55,13 +56,22 @@ void onKeyPress(const std::unordered_map<std::string, std::any> &data, SCallback
OVERVIEW_KEY == XKB_KEY_Alt_L || OVERVIEW_KEY == XKB_KEY_Alt_R ||
OVERVIEW_KEY == XKB_KEY_Control_L || OVERVIEW_KEY == XKB_KEY_Control_R
) {
// open overview is only a modifier key
if (release && last_press_was_mod_press && CHECK_NO_MOUSE_BUTTON_PRESSED) {
if constexpr (HYPRSHELL_PRINT_DEBUG == 1) {
HyprlandAPI::addNotification(PHANDLE, "[Hyprshell Plugin] mod pressed", GREEN, 2000);
if (((OVERVIEW_KEY == XKB_KEY_Super_L || OVERVIEW_KEY == XKB_KEY_Super_R) && superActive && !ctrlActive
&& !altActive) ||
((OVERVIEW_KEY == XKB_KEY_Alt_L || OVERVIEW_KEY == XKB_KEY_Alt_R) && altActive && !ctrlActive
&& !superActive) ||
((OVERVIEW_KEY == XKB_KEY_Control_L || OVERVIEW_KEY == XKB_KEY_Control_R) && ctrlActive && !
superActive
&& !altActive)
) {
// open overview is only a modifier key
if (release && last_press_was_mod_press && CHECK_NO_MOUSE_BUTTON_PRESSED) {
if constexpr (HYPRSHELL_PRINT_DEBUG == 1) {
HyprlandAPI::addNotification(PHANDLE, "[Hyprshell Plugin] mod pressed", GREEN, 2000);
}
info.cancelled = true;
sendStringToHyprshellSocket(HYPRSHELL_OPEN_OVERVIEW);
}
info.cancelled = true;
sendStringToHyprshellSocket(HYPRSHELL_OPEN_OVERVIEW);
} else {
// between pressing and releasing the mod key, there must be
// no mouse click (dnd)
@ -72,9 +82,9 @@ void onKeyPress(const std::unordered_map<std::string, std::any> &data, SCallback
} else {
// open overview is mod + key
if (!release && (
(strcasecmp(HYPRSHELL_OVERVIEW_MOD, "Alt") == 0 && altActive) ||
(strcasecmp(HYPRSHELL_OVERVIEW_MOD, "Super") == 0 && superActive) ||
(strcasecmp(HYPRSHELL_OVERVIEW_MOD, "Ctrl") == 0 && ctrlActive))
(strcasecmp(HYPRSHELL_OVERVIEW_MOD, "Alt") == 0 && altActive && !superActive && !ctrlActive) ||
(strcasecmp(HYPRSHELL_OVERVIEW_MOD, "Super") == 0 && superActive && !altActive && !ctrlActive) ||
(strcasecmp(HYPRSHELL_OVERVIEW_MOD, "Ctrl") == 0 && ctrlActive && !superActive && !altActive))
) {
if constexpr (HYPRSHELL_PRINT_DEBUG == 1) {
HyprlandAPI::addNotification(PHANDLE, "[Hyprshell Plugin] mod + overview pressed", GREEN, 2000);
@ -90,10 +100,10 @@ void onKeyPress(const std::unordered_map<std::string, std::any> &data, SCallback
// open switch mode
if (!release && !LAYER_VISIBLE) {
if (keysym == XKB_KEY_Tab) {
if ((HYPRSHELL_SWTICH_XKB_MOD_L == XKB_KEY_Alt_L && altActive) ||
(HYPRSHELL_SWTICH_XKB_MOD_L == XKB_KEY_Super_L && superActive) ||
(HYPRSHELL_SWTICH_XKB_MOD_L == XKB_KEY_Control_L && ctrlActive)
if (keysym == SWITCH_KEY) {
if ((HYPRSHELL_SWTICH_XKB_MOD_L == XKB_KEY_Alt_L && altActive && !superActive && !ctrlActive) ||
(HYPRSHELL_SWTICH_XKB_MOD_L == XKB_KEY_Super_L && superActive && !altActive && !ctrlActive) ||
(HYPRSHELL_SWTICH_XKB_MOD_L == XKB_KEY_Control_L && ctrlActive && !superActive && !altActive)
) {
if constexpr (HYPRSHELL_PRINT_DEBUG == 1) {
HyprlandAPI::addNotification(PHANDLE, "[Hyprshell Plugin] switch open (tab) pressed", GREEN,

View File

@ -1,5 +1,5 @@
#include "globals.h"
#include <hyprland/src/desktop/LayerSurface.hpp>
#include <hyprland/src/desktop/view/LayerSurface.hpp>
void onKeyboardFocus(const SP<CWLSurfaceResource> &surface) {
if (LAYER_VISIBLE) {

View File

@ -1,5 +1,5 @@
#include <hyprland/src/plugins/PluginAPI.hpp>
#include <hyprland/src/desktop/LayerSurface.hpp>
#include <hyprland/src/desktop/view/LayerSurface.hpp>
#include "globals.h"

View File

@ -1,6 +1,4 @@
#include <hyprland/src/plugins/PluginAPI.hpp>
#include <hyprland/src/devices/IPointer.hpp>
#include <hyprland/src/desktop/LayerSurface.hpp>
#include "globals.h"
@ -9,4 +7,4 @@ void onMouseButton(const IPointer::SButtonEvent event) {
// if constexpr (HYPRSHELL_PRINT_DEBUG == 1) {
// HyprlandAPI::addNotification(PHANDLE, "[Hyprshell Plugin] Mouse button pressed", GREEN, 4000);
// }
}
}

View File

@ -11,6 +11,7 @@ use tracing::debug_span;
pub struct PluginConfig {
pub xkb_key_switch_mod: Option<Box<str>>,
pub xkb_key_switch_key: Option<Box<str>>,
pub xkb_key_overview_mod: Option<Box<str>>,
pub xkb_key_overview_key: Option<Box<str>>,
}
@ -18,8 +19,9 @@ impl Display for PluginConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}|{}|{}",
"{}|{}|{}|{}",
self.xkb_key_switch_mod.as_deref().unwrap_or(""),
self.xkb_key_switch_key.as_deref().unwrap_or(""),
self.xkb_key_overview_mod.as_deref().unwrap_or(""),
self.xkb_key_overview_key.as_deref().unwrap_or(""),
)
@ -78,6 +80,10 @@ pub fn configure(dir: &TempDir, config: &PluginConfig) -> anyhow::Result<()> {
"$HYPRSHELL_OVERVIEW_KEY$",
config.xkb_key_overview_key.as_deref().unwrap_or(""),
),
(
"$HYPRSHELL_SWITCH_KEY$",
config.xkb_key_switch_key.as_deref().unwrap_or(""),
),
(
"$HYPRSHELL_OPEN_OVERVIEW$",
&generate_transfer(&TransferType::OpenOverview),

View File

@ -8,6 +8,7 @@ mod tests {
fn build_plugin() {
let test_config = PluginConfig {
xkb_key_switch_mod: Some(Box::from("XKB_KEY_Alt")),
xkb_key_switch_key: Some(Box::from("tab")),
xkb_key_overview_mod: Some(Box::from("XKB_KEY_Super")),
xkb_key_overview_key: Some(Box::from("tab")),
};

View File

@ -44,9 +44,10 @@ pub fn create_windows_switch_window(
.default_width(10)
.build();
let s_key = Key::from_name(switch.key.to_string()).context("invalid switch key")?;
let key_controller = EventControllerKey::new();
let event_sender_2 = event_sender.clone();
key_controller.connect_key_pressed(move |_, key, _, _| handle_key(key, &event_sender_2));
key_controller.connect_key_pressed(move |_, key, _, _| handle_key(key, s_key, &event_sender_2));
let event_sender_3 = event_sender;
let r#mod = switch.modifier;
key_controller.connect_key_released(move |_, key, _, _| {
@ -92,9 +93,9 @@ fn handle_release(key: Key, modifier: Modifier, event_sender: &Sender<TransferTy
}
}
fn handle_key(key: Key, event_sender: &Sender<TransferType>) -> Propagation {
fn handle_key(key: Key, s_key: Key, event_sender: &Sender<TransferType>) -> Propagation {
match key {
Key::Tab | Key::l | Key::Right => {
k if k == s_key || k == Key::l || k == Key::Right => {
event_sender
.send_blocking(TransferType::SwitchSwitch(SwitchSwitchConfig {
direction: Direction::Right,

76
flake.lock generated
View File

@ -20,11 +20,11 @@
]
},
"locked": {
"lastModified": 1765900596,
"narHash": "sha256-+hn8v9jkkLP9m+o0Nm5SiEq10W0iWDSotH2XfjU45fA=",
"lastModified": 1767024902,
"narHash": "sha256-sMdk6QkMDhIOnvULXKUM8WW8iyi551SWw2i6KQHbrrU=",
"owner": "hyprwm",
"repo": "aquamarine",
"rev": "d83c97f8f5c0aae553c1489c7d9eff3eadcadace",
"rev": "b8a0c5ba5a9fbd2c660be7dd98bdde0ff3798556",
"type": "github"
},
"original": {
@ -35,11 +35,11 @@
},
"crane": {
"locked": {
"lastModified": 1766774972,
"narHash": "sha256-8qxEFpj4dVmIuPn9j9z6NTbU+hrcGjBOvaxTzre5HmM=",
"lastModified": 1767461147,
"narHash": "sha256-TH/xTeq/RI+DOzo+c+4F431eVuBpYVwQwBxzURe7kcI=",
"owner": "ipetkov",
"repo": "crane",
"rev": "01bc1d404a51a0a07e9d8759cd50a7903e218c82",
"rev": "7d59256814085fd9666a2ae3e774dc5ee216b630",
"type": "github"
},
"original": {
@ -51,15 +51,15 @@
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1761588595,
"narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=",
"owner": "edolstra",
"lastModified": 1767039857,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
"owner": "NixOS",
"repo": "flake-compat",
"rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github"
},
"original": {
"owner": "edolstra",
"owner": "NixOS",
"repo": "flake-compat",
"type": "github"
}
@ -113,11 +113,11 @@
]
},
"locked": {
"lastModified": 1767104570,
"narHash": "sha256-GKgwu5//R+cLdKysZjGqvUEEOGXXLdt93sNXeb2M/Lk=",
"lastModified": 1767556355,
"narHash": "sha256-RDTUBDQBi9D4eD9iJQWtUDN/13MDLX+KmE+TwwNUp2s=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "e4e78a2cbeaddd07ab7238971b16468cc1d14daf",
"rev": "f894bc4ffde179d178d8deb374fcf9855d1a82b7",
"type": "github"
},
"original": {
@ -171,11 +171,11 @@
]
},
"locked": {
"lastModified": 1763733840,
"narHash": "sha256-JnET78yl5RvpGuDQy3rCycOCkiKoLr5DN1fPhRNNMco=",
"lastModified": 1766946335,
"narHash": "sha256-MRD+Jr2bY11MzNDfenENhiK6pvN+nHygxdHoHbZ1HtE=",
"owner": "hyprwm",
"repo": "hyprgraphics",
"rev": "8f1bec691b2d198c60cccabca7a94add2df4ed1a",
"rev": "4af02a3925b454deb1c36603843da528b67ded6c",
"type": "github"
},
"original": {
@ -201,11 +201,11 @@
"xdph": "xdph"
},
"locked": {
"lastModified": 1767201430,
"narHash": "sha256-2FF66EaIbsc7CL1jKHbRFslSePDq40fzlTTbUlm5v3k=",
"lastModified": 1767523459,
"narHash": "sha256-Z3EijMLd18cg8arhXv+A/tNiknQIYaeWQVoCNUm+c4A=",
"owner": "hyprwm",
"repo": "Hyprland",
"rev": "48a024e0322bbd7c4c88126498ec478444ec4cb2",
"rev": "0b3b012817ca381e40754cb4408e5c0cd3a2c732",
"type": "github"
},
"original": {
@ -247,11 +247,11 @@
]
},
"locked": {
"lastModified": 1765643131,
"narHash": "sha256-CCGohW5EBIRy4B7vTyBMqPgsNcaNenVad/wszfddET0=",
"lastModified": 1767023960,
"narHash": "sha256-R2HgtVS1G3KSIKAQ77aOZ+Q0HituOmPgXW9nBNkpp3Q=",
"owner": "hyprwm",
"repo": "hyprland-guiutils",
"rev": "e50ae912813bdfa8372d62daf454f48d6df02297",
"rev": "c2e906261142f5dd1ee0bfc44abba23e2754c660",
"type": "github"
},
"original": {
@ -378,11 +378,11 @@
]
},
"locked": {
"lastModified": 1766160771,
"narHash": "sha256-roINUGikWRqqgKrD4iotKbGj3ZKJl3hjMz5l/SyKrHw=",
"lastModified": 1766253372,
"narHash": "sha256-1+p4Kw8HdtMoFSmJtfdwjxM4bPxDK9yg27SlvUMpzWA=",
"owner": "hyprwm",
"repo": "hyprutils",
"rev": "5ac060bfcf2f12b3a6381156ebbc13826a05b09f",
"rev": "51a4f93ce8572e7b12b7284eb9e6e8ebf16b4be9",
"type": "github"
},
"original": {
@ -432,11 +432,11 @@
]
},
"locked": {
"lastModified": 1766253200,
"narHash": "sha256-26qPwrd3od+xoYVywSB7hC2cz9ivN46VPLlrsXyGxvE=",
"lastModified": 1767473322,
"narHash": "sha256-RGOeG+wQHeJ6BKcsSB8r0ZU77g9mDvoQzoTKj2dFHwA=",
"owner": "hyprwm",
"repo": "hyprwire",
"rev": "1079777525b30a947c8d657fac158e00ae85de9d",
"rev": "d5e7d6b49fe780353c1cf9a1cf39fa8970bd9d11",
"type": "github"
},
"original": {
@ -447,11 +447,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1766070988,
"narHash": "sha256-G/WVghka6c4bAzMhTwT2vjLccg/awmHkdKSd2JrycLc=",
"lastModified": 1767379071,
"narHash": "sha256-EgE0pxsrW9jp9YFMkHL9JMXxcqi/OoumPJYwf+Okucw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c6245e83d836d0433170a16eb185cefe0572f8b8",
"rev": "fb7944c166a3b630f177938e478f0378e64ce108",
"type": "github"
},
"original": {
@ -463,11 +463,11 @@
},
"nixpkgs_2": {
"locked": {
"lastModified": 1766902085,
"narHash": "sha256-coBu0ONtFzlwwVBzmjacUQwj3G+lybcZ1oeNSQkgC0M=",
"lastModified": 1767379071,
"narHash": "sha256-EgE0pxsrW9jp9YFMkHL9JMXxcqi/OoumPJYwf+Okucw=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "c0b0e0fddf73fd517c3471e546c0df87a42d53f4",
"rev": "fb7944c166a3b630f177938e478f0378e64ce108",
"type": "github"
},
"original": {
@ -487,11 +487,11 @@
]
},
"locked": {
"lastModified": 1765911976,
"narHash": "sha256-t3T/xm8zstHRLx+pIHxVpQTiySbKqcQbK+r+01XVKc0=",
"lastModified": 1767281941,
"narHash": "sha256-6MkqajPICgugsuZ92OMoQcgSHnD6sJHwk8AxvMcIgTE=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "b68b780b69702a090c8bb1b973bab13756cc7a27",
"rev": "f0927703b7b1c8d97511c4116eb9b4ec6645a0fa",
"type": "github"
},
"original": {

View File

@ -43,6 +43,7 @@
preFixup =
buildLib.addWrapWithGccArgs
inputs.hyprland.packages.${pkgs.stdenv.hostPlatform.system}.default;
postInstall = buildLib.postInstall;
}
);
hyprshell-nixpkgs = craneLib.buildPackage (
@ -50,6 +51,7 @@
// {
cargoArtifacts = buildLib.cargoReleaseArtifacts;
preFixup = buildLib.addWrapWithGccArgs pkgs.hyprland;
postInstall = buildLib.postInstall;
}
);
hyprshell-slim = craneLib.buildPackage (
@ -60,6 +62,7 @@
preFixup =
buildLib.addWrapWithGccArgs
inputs.hyprland.packages.${pkgs.stdenv.hostPlatform.system}.default;
postInstall = buildLib.postInstall;
}
);
hyprshell-slim-nixpkgs = craneLib.buildPackage (
@ -68,6 +71,7 @@
cargoArtifacts = buildLib.cargoReleaseArtifacts;
cargoExtraArgs = "--no-default-features --features slim";
preFixup = buildLib.addWrapWithGccArgs pkgs.hyprland;
postInstall = buildLib.postInstall;
}
);
default = hyprshell;

View File

@ -78,4 +78,16 @@ rec {
cargoArtifacts = cargoFullArtifacts;
}
);
postInstall = ''
# Desktop entry
install -Dm644 packaging/hyprshell-config.desktop $out/share/applications/hyprshell.desktop
# Icon
install -Dm644 packaging/hyprshell-settings.png $out/share/pixmaps/hyprshell-settings.png
# Extract runtime data
mkdir -p $out/share/hyprshell
tar -xf packaging/usr-share.tar -C $out/share/hyprshell
'';
}

View File

@ -79,7 +79,7 @@ in
items_per_row = mkOpt "Workspaces per row" int 5;
overview = {
enable = lib.mkEnableOption "Enable overview";
key = mkOpt "Key to open overview" str "super_l";
key = mkOpt "Key to open overview" str "Super_L";
modifier = mkOpt "Modifier key" (enum [
"alt"
"ctrl"
@ -218,6 +218,7 @@ in
};
switch = {
enable = mkOpt "Enable recent window switcher" bool true;
key = mkOpt "Key to open switch" str "Tab";
modifier = mkOpt "Modifier key" (enum [
"alt"
"ctrl"

View File

@ -27,7 +27,7 @@ pub fn configure_wm(config: &Config) -> anyhow::Result<()> {
fn plugin(config: &Config) -> anyhow::Result<()> {
if let Some(windows) = &config.windows {
let switch = windows.switch.as_ref().map(|s| s.modifier);
let switch = windows.switch.as_ref().map(|s| (s.modifier, s.key.clone()));
let overview = windows
.overview
.as_ref()