From 191a83bb7b75b9c79c4d1dfc7acd9c83f19cef45 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Tue, 12 May 2026 20:41:25 -0700 Subject: [PATCH] Add Hyprspace to Hyprland --- dotfiles/config/hypr/hyprland/binds.lua | 4 +- dotfiles/config/hypr/hyprland/core.lua | 19 +++++ dotfiles/config/hypr/hyprland/settings.lua | 3 + dotfiles/config/hypr/hyprland/state.lua | 1 + nixos/flake.lock | 24 ++++++ nixos/flake.nix | 6 ++ nixos/hyprland.nix | 16 ++++ nixos/packages/hyprspace-lua-api.patch | 87 ++++++++++++++++++++++ 8 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 nixos/packages/hyprspace-lua-api.patch diff --git a/dotfiles/config/hypr/hyprland/binds.lua b/dotfiles/config/hypr/hyprland/binds.lua index da7ae112..57c259eb 100644 --- a/dotfiles/config/hypr/hyprland/binds.lua +++ b/dotfiles/config/hypr/hyprland/binds.lua @@ -24,8 +24,8 @@ function M.setup(ctx) default_action = "bring", }), overview_bind_opts) bind(main_mod .. " + SHIFT + slash", hyprwinview({ action = "toggle-filter" }), overview_bind_opts) - bind("ALT + Tab", hyprexpo("open"), overview_bind_opts) - bind("ALT + SHIFT + Tab", hyprexpo("bring"), overview_bind_opts) + bind("ALT + Tab", hyprspace("toggle"), overview_bind_opts) + bind("ALT + SHIFT + Tab", hyprexpo("open"), overview_bind_opts) bind(main_mod .. " + G", hyprwinview({ action = "show", start_in_filter_mode = true, diff --git a/dotfiles/config/hypr/hyprland/core.lua b/dotfiles/config/hypr/hyprland/core.lua index a1a30bc7..db199d4e 100644 --- a/dotfiles/config/hypr/hyprland/core.lua +++ b/dotfiles/config/hypr/hyprland/core.lua @@ -70,6 +70,24 @@ function M.setup(ctx) end end + local function hyprspace(action) + action = action or "toggle" + return function() + overview_trace("hyprspace " .. tostring(action)) + if hl.plugin and hl.plugin.hyprspace and hl.plugin.hyprspace.overview then + hl.plugin.hyprspace.overview(action) + else + hl.notification.create({ + text = "Hyprspace is not loaded", + duration = 1800, + icon = notification_icons.warning, + color = "rgba(edb443ff)", + font_size = 13, + }) + end + end + end + local function hyprwinview(action) return function() local label = "hyprwinview" @@ -496,6 +514,7 @@ function M.setup(ctx) ctx.overview_trace = overview_trace ctx.window_selector = window_selector ctx.hyprexpo = hyprexpo + ctx.hyprspace = hyprspace ctx.hyprwinview = hyprwinview ctx.workspacehistory = workspacehistory ctx.apply_nstack_config = apply_nstack_config diff --git a/dotfiles/config/hypr/hyprland/settings.lua b/dotfiles/config/hypr/hyprland/settings.lua index 3b02c4fc..b27d8320 100644 --- a/dotfiles/config/hypr/hyprland/settings.lua +++ b/dotfiles/config/hypr/hyprland/settings.lua @@ -8,6 +8,9 @@ function M.setup(ctx) if enable_hyprexpo and not verify_config then hl.plugin.load("/run/current-system/sw/lib/libhyprexpo.so") end + if enable_hyprspace and not verify_config then + hl.plugin.load("/run/current-system/sw/lib/libHyprspace.so") + end if enable_hyprwinview and not verify_config then hl.plugin.load("/run/current-system/sw/lib/libhyprwinview.so") end diff --git a/dotfiles/config/hypr/hyprland/state.lua b/dotfiles/config/hypr/hyprland/state.lua index f671cdd4..5ee4d5ab 100644 --- a/dotfiles/config/hypr/hyprland/state.lua +++ b/dotfiles/config/hypr/hyprland/state.lua @@ -46,6 +46,7 @@ return { current_layout = columns_layout, enable_nstack = true, enable_hyprexpo = true, + enable_hyprspace = true, enable_hyprwinview = true, enable_workspace_history = true, configure_nstack_plugin_from_lua = false, diff --git a/nixos/flake.lock b/nixos/flake.lock index 89bf9ebe..415be43e 100644 --- a/nixos/flake.lock +++ b/nixos/flake.lock @@ -1,5 +1,28 @@ { "nodes": { + "Hyprspace": { + "inputs": { + "hyprland": [ + "hyprland" + ], + "systems": [ + "systems" + ] + }, + "locked": { + "lastModified": 1775071961, + "narHash": "sha256-LXkeeH9Blr6ohS1LAsVZbkJ/EAkkMDATh5qu45hC7Zo=", + "owner": "KZDKM", + "repo": "Hyprspace", + "rev": "12ddde04f8584bf7de3151e6169918e0dda9f822", + "type": "github" + }, + "original": { + "owner": "KZDKM", + "repo": "Hyprspace", + "type": "github" + } + }, "agenix": { "inputs": { "darwin": "darwin", @@ -1705,6 +1728,7 @@ }, "root": { "inputs": { + "Hyprspace": "Hyprspace", "agenix": "agenix", "claude-code-nix": "claude-code-nix", "codex-cli-nix": "codex-cli-nix", diff --git a/nixos/flake.nix b/nixos/flake.nix index a91ed36b..f7b5c92e 100644 --- a/nixos/flake.nix +++ b/nixos/flake.nix @@ -109,6 +109,12 @@ inputs.hyprland.follows = "hyprland"; }; + Hyprspace = { + url = "github:KZDKM/Hyprspace"; + inputs.hyprland.follows = "hyprland"; + inputs.systems.follows = "systems"; + }; + hyprwinview = { url = "github:colonelpanic8/hyprwinview"; inputs.hyprland.follows = "hyprland"; diff --git a/nixos/hyprland.nix b/nixos/hyprland.nix index 6cee82a8..e1e05372 100644 --- a/nixos/hyprland.nix +++ b/nixos/hyprland.nix @@ -89,9 +89,25 @@ overrideAttrs = f: makeHyprlandLuaPackage (package.overrideAttrs f); }; hyprlandPackage = makeHyprlandLuaPackage baseHyprlandPackage; + hyprspace = inputs.Hyprspace.packages.${system}.Hyprspace.overrideAttrs (old: { + version = "${old.version}-pr230"; + __intentionallyOverridingVersion = true; + patches = + (old.patches or []) + ++ [ + # Hyprspace main has not caught up with Hyprland 0.55 yet. + # https://github.com/KZDKM/Hyprspace/pull/230 + (pkgs.fetchpatch { + url = "https://github.com/KZDKM/Hyprspace/pull/230.patch"; + hash = "sha256-jwSuIyhUi8KMIpgBxqyDZvcRHxHjiji8GMipDX4Dot8="; + }) + ./packages/hyprspace-lua-api.patch + ]; + }); hyprlandPluginPackages = [ inputs.hyprNStack.packages.${system}.hyprNStack inputs.hyprland-plugins-lua.packages.${system}.hyprexpo + hyprspace inputs.hyprwinview.packages.${system}.hyprwinview inputs.hypr-workspace-history.packages.${system}.hypr-workspace-history ]; diff --git a/nixos/packages/hyprspace-lua-api.patch b/nixos/packages/hyprspace-lua-api.patch new file mode 100644 index 00000000..a300d4a4 --- /dev/null +++ b/nixos/packages/hyprspace-lua-api.patch @@ -0,0 +1,87 @@ +diff --git a/src/main.cpp b/src/main.cpp +index 9b75f8a..b69b459 100644 +--- a/src/main.cpp ++++ b/src/main.cpp +@@ -5,6 +5,7 @@ + #include + #include + #include ++#include + #include + #include "Overview.hpp" + #include "Globals.hpp" +@@ -377,6 +378,65 @@ static SDispatchResult dispatchCloseOverview(std::string arg) { + return SDispatchResult{}; + } + ++static int luaOverview(lua_State* L) { ++ std::string action = "toggle"; ++ std::string arg; ++ ++ if (lua_gettop(L) >= 1 && !lua_isnil(L, 1)) { ++ if (lua_istable(L, 1)) { ++ lua_getfield(L, 1, "action"); ++ if (lua_isstring(L, -1)) ++ action = lua_tostring(L, -1); ++ else if (!lua_isnil(L, -1)) ++ return luaL_error(L, "hyprspace.overview: field \"action\" must be a string"); ++ lua_pop(L, 1); ++ ++ lua_getfield(L, 1, "all"); ++ if (lua_isboolean(L, -1) && lua_toboolean(L, -1)) ++ arg = "all"; ++ else if (!lua_isnil(L, -1) && !lua_isboolean(L, -1)) ++ return luaL_error(L, "hyprspace.overview: field \"all\" must be a boolean"); ++ lua_pop(L, 1); ++ } else if (lua_isstring(L, 1)) { ++ action = lua_tostring(L, 1); ++ } else { ++ return luaL_error(L, "hyprspace.overview: argument must be a string or table"); ++ } ++ } ++ ++ if (action == "toggle") { ++ dispatchToggleOverview(arg); ++ return 0; ++ } ++ ++ if (action == "open") { ++ dispatchOpenOverview(arg); ++ return 0; ++ } ++ ++ if (action == "close") { ++ dispatchCloseOverview(arg); ++ return 0; ++ } ++ ++ if (action == "toggle all") { ++ dispatchToggleOverview("all"); ++ return 0; ++ } ++ ++ if (action == "open all") { ++ dispatchOpenOverview("all"); ++ return 0; ++ } ++ ++ if (action == "close all") { ++ dispatchCloseOverview("all"); ++ return 0; ++ } ++ ++ return luaL_error(L, "hyprspace.overview: unknown action \"%s\"", action.c_str()); ++} ++ + void* findFunctionBySymbol(HANDLE inHandle, const std::string func, const std::string sym) { + // should return all functions + auto funcSearch = HyprlandAPI::findFunctionsByName(inHandle, func); +@@ -516,6 +566,8 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE inHandle) { + HyprlandAPI::addDispatcherV2(pHandle, "overview:open", ::dispatchOpenOverview); + HyprlandAPI::addDispatcherV2(pHandle, "overview:close", ::dispatchCloseOverview); + ++ HyprlandAPI::addLuaFunction(pHandle, "hyprspace", "overview", ::luaOverview); ++ + g_pRenderHook = Event::bus()->m_events.render.stage.listen([](eRenderStage stage) { onRender(stage); }); + + // refresh on layer change