diff --git a/dotfiles/config/hypr/workspace-history-plugin/CMakeLists.txt b/dotfiles/config/hypr/workspace-history-plugin/CMakeLists.txt deleted file mode 100644 index 430597d6..00000000 --- a/dotfiles/config/hypr/workspace-history-plugin/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -cmake_minimum_required(VERSION 3.27) - -project(hypr-workspace-history - DESCRIPTION "Workspace history cycling plugin for Hyprland" - VERSION 0.1.0 - LANGUAGES CXX -) - -set(CMAKE_CXX_STANDARD 23) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -add_library(hypr-workspace-history SHARED src/main.cpp) - -find_package(PkgConfig REQUIRED) -pkg_check_modules(deps REQUIRED IMPORTED_TARGET - hyprland - wayland-server - xkbcommon -) - -target_link_libraries(hypr-workspace-history PRIVATE rt PkgConfig::deps) - -install(TARGETS hypr-workspace-history) diff --git a/dotfiles/config/hypr/workspace-history-plugin/src/main.cpp b/dotfiles/config/hypr/workspace-history-plugin/src/main.cpp deleted file mode 100644 index f0b99dec..00000000 --- a/dotfiles/config/hypr/workspace-history-plugin/src/main.cpp +++ /dev/null @@ -1,406 +0,0 @@ -#define WLR_USE_UNSTABLE - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -inline HANDLE PHANDLE = nullptr; - -namespace { - -constexpr int MAX_WORKSPACE = 9; - -struct SCycleState { - std::string monitorKey; - int originalWorkspace = 0; - int previewWorkspace = 0; - std::vector history; - size_t nextIndex = 0; -}; - -class CWorkspaceHistory { - public: - void seedActiveWorkspaces() { - if (!g_pCompositor) - return; - - for (const auto& monitor : g_pCompositor->m_monitors) { - if (monitor && monitor->m_activeWorkspace) - remember(monitor->m_activeWorkspace); - } - - writeDebug("seed"); - } - - void observe(PHLWORKSPACE workspace) { - const auto workspaceId = workspaceID(workspace); - if (!workspaceId) { - writeDebug("observe-skipped-non-normal-workspace"); - return; - } - - const auto key = monitorKey(workspace); - if (!m_cycle || m_cycle->monitorKey != key) { - remember(workspace); - return; - } - - if (contains(m_cycle->history, *workspaceId)) { - m_cycle->previewWorkspace = *workspaceId; - writeDebug("cycle-observe-preview"); - return; - } - - m_cycle.reset(); - remember(workspace); - writeDebug("remember-after-cycle-abandoned"); - } - - SDispatchResult cycle(int direction) { - auto* cycle = startCycle(); - if (!cycle) - return {}; - - if (cycle->history.size() < 2) { - writeDebug("cycle-skipped-short-history"); - return {}; - } - - const auto target = cycle->history[cycle->nextIndex]; - cycle->previewWorkspace = target; - const auto result = focusWorkspace(target); - if (!result.success) { - writeDebug("cycle-focus-failed"); - return result; - } - - cycle->nextIndex = wrappedIndex(cycle->nextIndex, direction, cycle->history.size()); - writeDebug(std::string("cycle-preview-") + std::to_string(target)); - return {}; - } - - SDispatchResult commit() { - if (!m_cycle) { - writeDebug("commit-skipped-no-cycle"); - return {}; - } - - auto cycle = *m_cycle; - m_cycle.reset(); - m_histories[cycle.monitorKey] = promote(cycle.history, cycle.previewWorkspace); - writeDebug("commit"); - return {}; - } - - SDispatchResult cancel() { - if (!m_cycle) { - writeDebug("cancel-skipped-no-cycle"); - return {}; - } - - const auto original = m_cycle->originalWorkspace; - m_cycle.reset(); - focusWorkspace(original); - writeDebug("cancel"); - return {}; - } - - void onKey(IKeyboard::SKeyEvent event) { - if (!m_cycle || event.state != WL_KEYBOARD_KEY_STATE_RELEASED || !g_pKeybindManager) - return; - - if (g_pKeybindManager->keycodeToModifier(event.keycode + 8) == HL_MODIFIER_META) - commit(); - } - - std::string snapshot(const std::string& reason) const { - std::stringstream out; - out << "reason=" << reason << "\n"; - - const auto monitor = Desktop::focusState() ? Desktop::focusState()->monitor() : nullptr; - const auto workspace = monitor ? monitor->m_activeWorkspace : nullptr; - out << "active_monitor=" << monitorKey(monitor) << "\n"; - out << "active_workspace=" << (workspace ? std::to_string(workspace->m_id) : "?") << "\n"; - - for (const auto& [key, history] : m_histories) { - out << "history." << key << "="; - for (size_t i = 0; i < history.size(); ++i) { - if (i > 0) - out << ","; - out << history[i]; - } - out << "\n"; - } - - if (m_cycle) { - out << "cycle.monitor=" << m_cycle->monitorKey << "\n"; - out << "cycle.original=" << m_cycle->originalWorkspace << "\n"; - out << "cycle.preview=" << m_cycle->previewWorkspace << "\n"; - out << "cycle.next_index=" << (m_cycle->nextIndex + 1) << "\n"; - out << "cycle.history="; - for (size_t i = 0; i < m_cycle->history.size(); ++i) { - if (i > 0) - out << ","; - out << m_cycle->history[i]; - } - out << "\n"; - } else { - out << "cycle=none\n"; - } - - return out.str(); - } - - void showDebug() const { - HyprlandAPI::addNotification(PHANDLE, snapshot("notification"), CHyprColor{0.4, 0.8, 1.0, 1.0}, 6000); - } - - private: - std::map> m_histories; - std::optional m_cycle; - - static std::optional workspaceID(PHLWORKSPACE workspace) { - if (!workspace || workspace->m_id < 1 || workspace->m_id > MAX_WORKSPACE) - return std::nullopt; - - return workspace->m_id; - } - - static std::string monitorKey(PHLMONITOR monitor) { - if (!monitor) - return "unknown"; - - if (!monitor->m_name.empty()) - return monitor->m_name; - - return std::to_string(monitor->m_id); - } - - static std::string monitorKey(PHLWORKSPACE workspace) { - if (!workspace || !workspace->m_monitor) - return monitorKey(Desktop::focusState() ? Desktop::focusState()->monitor() : nullptr); - - return monitorKey(workspace->m_monitor.lock()); - } - - static bool contains(const std::vector& history, int workspace) { - return std::ranges::contains(history, workspace); - } - - static std::vector promote(std::vector history, int workspace) { - std::erase(history, workspace); - history.insert(history.begin(), workspace); - return history; - } - - static size_t wrappedIndex(size_t current, int direction, size_t size) { - const auto signedSize = static_cast(size); - auto next = static_cast(current) + direction; - next = ((next % signedSize) + signedSize) % signedSize; - return static_cast(next); - } - - PHLWORKSPACE activeWorkspace() const { - const auto monitor = Desktop::focusState() ? Desktop::focusState()->monitor() : nullptr; - return monitor ? monitor->m_activeWorkspace : nullptr; - } - - void remember(PHLWORKSPACE workspace) { - const auto workspaceId = workspaceID(workspace); - if (!workspaceId) { - writeDebug("remember-skipped-non-normal-workspace"); - return; - } - - const auto key = monitorKey(workspace); - auto& history = m_histories[key]; - const bool changed = history.empty() || history.front() != *workspaceId; - history = promote(history, *workspaceId); - - if (changed) - writeDebug("remember"); - } - - SCycleState* startCycle() { - if (m_cycle) - return &*m_cycle; - - const auto workspace = activeWorkspace(); - remember(workspace); - - const auto workspaceId = workspaceID(workspace); - if (!workspaceId) { - writeDebug("cycle-start-skipped-non-normal-workspace"); - return nullptr; - } - - const auto key = monitorKey(workspace); - auto history = promote(m_histories[key], *workspaceId); - - if (history.size() < 2) { - writeDebug("cycle-start-skipped-short-history"); - return nullptr; - } - - m_cycle = SCycleState{ - .monitorKey = key, - .originalWorkspace = *workspaceId, - .previewWorkspace = *workspaceId, - .history = history, - .nextIndex = 1, - }; - writeDebug("cycle-start"); - return &*m_cycle; - } - - static SDispatchResult focusWorkspace(int workspace) { - if (!g_pKeybindManager) - return {.success = false, .error = "keybind manager is unavailable"}; - - const auto dispatcher = g_pKeybindManager->m_dispatchers.find("focusworkspaceoncurrentmonitor"); - if (dispatcher == g_pKeybindManager->m_dispatchers.end()) - return {.success = false, .error = "focusworkspaceoncurrentmonitor dispatcher is unavailable"}; - - return dispatcher->second(std::to_string(workspace)); - } - - static std::optional runtimePath(const std::string& name) { - const auto runtimeDir = std::getenv("XDG_RUNTIME_DIR"); - if (!runtimeDir) - return std::nullopt; - - return std::string(runtimeDir) + "/" + name; - } - - void writeDebug(const std::string& reason) const { - const auto statePath = runtimePath("hyprland-workspace-history-state"); - const auto logPath = runtimePath("hyprland-workspace-history.log"); - if (!statePath || !logPath) - return; - - const auto body = snapshot(reason); - std::ofstream state(*statePath, std::ios::trunc); - if (state) - state << body; - - std::ofstream log(*logPath, std::ios::app); - if (log) { - const auto now = std::time(nullptr); - log << "--- " << std::put_time(std::localtime(&now), "%Y-%m-%d %H:%M:%S") << " ---\n"; - log << body; - } - } -}; - -CWorkspaceHistory g_workspaceHistory; - -SDispatchResult dispatchCycle(std::string arg) { - int direction = 1; - if (arg == "-1" || arg == "previous" || arg == "prev" || arg == "reverse") - direction = -1; - return g_workspaceHistory.cycle(direction); -} - -SDispatchResult dispatchCommit(std::string) { - return g_workspaceHistory.commit(); -} - -SDispatchResult dispatchCancel(std::string) { - return g_workspaceHistory.cancel(); -} - -SDispatchResult dispatchDebug(std::string) { - g_workspaceHistory.showDebug(); - return {}; -} - -int luaCycle(lua_State* L) { - const auto result = g_workspaceHistory.cycle(static_cast(luaL_optinteger(L, 1, 1))); - if (!result.success) - return luaL_error(L, "%s", result.error.c_str()); - return 0; -} - -int luaCommit(lua_State* L) { - const auto result = g_workspaceHistory.commit(); - if (!result.success) - return luaL_error(L, "%s", result.error.c_str()); - return 0; -} - -int luaCancel(lua_State* L) { - const auto result = g_workspaceHistory.cancel(); - if (!result.success) - return luaL_error(L, "%s", result.error.c_str()); - return 0; -} - -int luaDebug(lua_State*) { - g_workspaceHistory.showDebug(); - return 0; -} - -void failNotification(const std::string& reason) { - HyprlandAPI::addNotification(PHANDLE, "[workspace-history] " + reason, CHyprColor{1.0, 0.2, 0.2, 1.0}, 5000); -} - -} - -APICALL EXPORT std::string PLUGIN_API_VERSION() { - return HYPRLAND_API_VERSION; -} - -APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { - PHANDLE = handle; - - const std::string hash = __hyprland_api_get_hash(); - const std::string clientHash = __hyprland_api_get_client_hash(); - if (hash != clientHash) { - failNotification("version mismatch between Hyprland headers and running Hyprland"); - throw std::runtime_error("[workspace-history] version mismatch"); - } - - static auto workspaceHook = Event::bus()->m_events.workspace.active.listen([](PHLWORKSPACE workspace) { g_workspaceHistory.observe(workspace); }); - static auto keyboardHook = Event::bus()->m_events.input.keyboard.key.listen([](IKeyboard::SKeyEvent event, Event::SCallbackInfo&) { g_workspaceHistory.onKey(event); }); - static auto startHook = Event::bus()->m_events.start.listen([] { g_workspaceHistory.seedActiveWorkspaces(); }); - static auto reloadHook = Event::bus()->m_events.config.reloaded.listen([] { g_workspaceHistory.seedActiveWorkspaces(); }); - static auto monitorHook = Event::bus()->m_events.monitor.focused.listen([](PHLMONITOR monitor) { - if (monitor && monitor->m_activeWorkspace) - g_workspaceHistory.observe(monitor->m_activeWorkspace); - }); - - HyprlandAPI::addDispatcherV2(PHANDLE, "workspacehistory:cycle", ::dispatchCycle); - HyprlandAPI::addDispatcherV2(PHANDLE, "workspacehistory:commit", ::dispatchCommit); - HyprlandAPI::addDispatcherV2(PHANDLE, "workspacehistory:cancel", ::dispatchCancel); - HyprlandAPI::addDispatcherV2(PHANDLE, "workspacehistory:debug", ::dispatchDebug); - HyprlandAPI::addLuaFunction(PHANDLE, "workspacehistory", "cycle", ::luaCycle); - HyprlandAPI::addLuaFunction(PHANDLE, "workspacehistory", "commit", ::luaCommit); - HyprlandAPI::addLuaFunction(PHANDLE, "workspacehistory", "cancel", ::luaCancel); - HyprlandAPI::addLuaFunction(PHANDLE, "workspacehistory", "debug", ::luaDebug); - - g_workspaceHistory.seedActiveWorkspaces(); - HyprlandAPI::addNotification(PHANDLE, "[workspace-history] Initialized", CHyprColor{0.2, 1.0, 0.2, 1.0}, 3000); - return {"hypr-workspace-history", "Workspace history cycling with modifier-release commits", "Ivan Malison", "0.1.0"}; -} - -APICALL EXPORT void PLUGIN_EXIT() { - g_workspaceHistory.commit(); -} diff --git a/nixos/flake.lock b/nixos/flake.lock index 6a3930e1..0fd776dd 100644 --- a/nixos/flake.lock +++ b/nixos/flake.lock @@ -1907,15 +1907,15 @@ "locked": { "lastModified": 1777525523, "narHash": "sha256-/LGaCcX6BgXRYpWnRp9CNgAgy7lmbQsubi4RwHJgnTI=", - "ref": "refs/heads/master", + "owner": "taffybar", + "repo": "taffybar", "rev": "23dbc827adca706b28df7404624ae3f5e800b04f", - "revCount": 2296, - "type": "git", - "url": "file:///home/imalison/dotfiles/dotfiles/config/taffybar/taffybar" + "type": "github" }, "original": { - "type": "git", - "url": "file:///home/imalison/dotfiles/dotfiles/config/taffybar/taffybar" + "owner": "taffybar", + "repo": "taffybar", + "type": "github" } }, "treefmt-nix": { diff --git a/nixos/flake.nix b/nixos/flake.nix index 9dc7e685..342eec48 100644 --- a/nixos/flake.nix +++ b/nixos/flake.nix @@ -114,6 +114,12 @@ inputs.hyprland.follows = "hyprland"; }; + hypr-workspace-history = { + url = "github:colonelpanic8/hypr-workspace-history"; + inputs.hyprland.follows = "hyprland"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + hyprscratch = { url = "github:colonelpanic8/hyprscratch/reapply-rules-on-toggle"; inputs.nixpkgs.follows = "nixpkgs"; @@ -476,26 +482,6 @@ containerLib = import ../org-agenda-api/container.nix { inherit pkgs system tangledConfig org-agenda-api orgApiRev dotfilesRev; }; - hyprlandPkgs = import nixpkgs { - inherit system; - overlays = [ hyprland.overlays.hyprland-packages ]; - }; - hyprWorkspaceHistory = hyprlandPkgs.hyprlandPlugins.mkHyprlandPlugin { - pluginName = "hypr-workspace-history"; - version = "0.1.0"; - src = builtins.path { - path = ../dotfiles/config/hypr/workspace-history-plugin; - name = "hypr-workspace-history-source"; - }; - - inherit (hyprland.packages.${system}.hyprland) nativeBuildInputs; - - meta = { - description = "Workspace history cycling plugin for Hyprland"; - license = lib.licenses.bsd3; - platforms = lib.platforms.linux; - }; - }; in { packages = { colonelpanic-org-agenda-api = containerLib.containers.colonelpanic; @@ -504,14 +490,14 @@ hyprNStack = inputs.hyprNStack.packages.${system}.hyprNStack; hyprexpo-lua = inputs.hyprland-plugins-lua.packages.${system}.hyprexpo; hyprwinview = inputs.hyprwinview.packages.${system}.hyprwinview; - hypr-workspace-history = hyprWorkspaceHistory; + hypr-workspace-history = inputs.hypr-workspace-history.packages.${system}.hypr-workspace-history; }; checks = lib.optionalAttrs pkgs.stdenv.isLinux { hyprNStack = inputs.hyprNStack.packages.${system}.hyprNStack; hyprexpo-lua = inputs.hyprland-plugins-lua.packages.${system}.hyprexpo; hyprwinview = inputs.hyprwinview.packages.${system}.hyprwinview; - hypr-workspace-history = hyprWorkspaceHistory; + hypr-workspace-history = inputs.hypr-workspace-history.packages.${system}.hypr-workspace-history; hyprland-config-syntax = pkgs.runCommand "hyprland-config-syntax" { nativeBuildInputs = [ pkgs.lua5_4 ]; } '' diff --git a/nixos/hyprland.nix b/nixos/hyprland.nix index 8dd764bf..d548ea27 100644 --- a/nixos/hyprland.nix +++ b/nixos/hyprland.nix @@ -13,7 +13,7 @@ let inputs.hyprNStack.packages.${system}.hyprNStack inputs.hyprland-plugins-lua.packages.${system}.hyprexpo inputs.hyprwinview.packages.${system}.hyprwinview - inputs.self.packages.${system}.hypr-workspace-history + inputs.hypr-workspace-history.packages.${system}.hypr-workspace-history ]; hyprRofiWindow = pkgs.writeShellApplication { name = "hypr_rofi_window";