├── .clang_format ├── .clangd ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.md ├── flake.lock ├── flake.nix ├── flash.gif ├── hyprload.toml ├── hyprpm.toml ├── shrink.gif └── src ├── Flash.cpp ├── Flash.hpp ├── Globals.hpp ├── IFocusAnimation.cpp ├── IFocusAnimation.hpp ├── Log.hpp ├── Shrink.cpp ├── Shrink.hpp └── main.cpp /.clang_format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: LLVM 4 | 5 | AccessModifierOffset: -2 6 | AlignAfterOpenBracket: Align 7 | AlignConsecutiveMacros: true 8 | AlignConsecutiveAssignments: true 9 | AlignEscapedNewlines: Right 10 | AlignOperands: false 11 | AlignTrailingComments: true 12 | AllowAllArgumentsOnNextLine: true 13 | AllowAllConstructorInitializersOnNextLine: true 14 | AllowAllParametersOfDeclarationOnNextLine: true 15 | AllowShortBlocksOnASingleLine: true 16 | AllowShortCaseLabelsOnASingleLine: true 17 | AllowShortFunctionsOnASingleLine: Empty 18 | AllowShortIfStatementsOnASingleLine: Never 19 | AllowShortLambdasOnASingleLine: All 20 | AllowShortLoopsOnASingleLine: false 21 | AlwaysBreakAfterDefinitionReturnType: None 22 | AlwaysBreakAfterReturnType: None 23 | AlwaysBreakBeforeMultilineStrings: false 24 | AlwaysBreakTemplateDeclarations: Yes 25 | BreakBeforeBraces: Attach 26 | BreakBeforeTernaryOperators: false 27 | BreakConstructorInitializers: AfterColon 28 | ColumnLimit: 180 29 | CompactNamespaces: false 30 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 31 | ExperimentalAutoDetectBinPacking: false 32 | FixNamespaceComments: false 33 | IncludeBlocks: Preserve 34 | IndentCaseLabels: true 35 | IndentWidth: 4 36 | PointerAlignment: Left 37 | ReflowComments: false 38 | SortIncludes: false 39 | SortUsingDeclarations: false 40 | SpaceAfterCStyleCast: false 41 | SpaceAfterLogicalNot: false 42 | SpaceAfterTemplateKeyword: true 43 | SpaceBeforeCtorInitializerColon: true 44 | SpaceBeforeInheritanceColon: true 45 | SpaceBeforeParens: ControlStatements 46 | SpaceBeforeRangeBasedForLoopColon: true 47 | SpaceInEmptyParentheses: false 48 | SpacesBeforeTrailingComments: 1 49 | SpacesInAngles: false 50 | SpacesInCStyleCastParentheses: false 51 | SpacesInContainerLiterals: false 52 | SpacesInParentheses: false 53 | SpacesInSquareBrackets: false 54 | Standard: Auto 55 | TabWidth: 4 56 | UseTab: Never 57 | 58 | AllowShortEnumsOnASingleLine: false 59 | 60 | BraceWrapping: 61 | AfterEnum: false 62 | 63 | AlignConsecutiveDeclarations: AcrossEmptyLines 64 | 65 | NamespaceIndentation: All 66 | -------------------------------------------------------------------------------- /.clangd: -------------------------------------------------------------------------------- 1 | CompileFlags: 2 | CompilationDatabase: "cmake" 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | .vscode/ 35 | .cache/ 36 | 37 | # hyprland 38 | hyprland/ 39 | 40 | # Build directories 41 | cmake/ 42 | build/ 43 | 44 | # Misc 45 | compile_commands.json 46 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.27) 2 | 3 | project( 4 | hyprfocus 5 | DESCRIPTION 6 | "focus animation plugin for Hyprland inspired by Flashfocus (pyt0xic fork)" 7 | VERSION 0.1) 8 | 9 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 10 | 11 | set(CMAKE_CXX_STANDARD 23) 12 | 13 | file(GLOB_RECURSE SRC "src/*.cpp") 14 | 15 | add_library(hyprfocus SHARED ${SRC}) 16 | 17 | find_package(PkgConfig REQUIRED) 18 | pkg_check_modules( 19 | deps 20 | REQUIRED 21 | IMPORTED_TARGET 22 | hyprland 23 | libdrm 24 | libinput 25 | libudev 26 | pangocairo 27 | pixman-1 28 | wayland-server 29 | xkbcommon) 30 | target_link_libraries(hyprfocus PRIVATE rt PkgConfig::deps) 31 | 32 | install(TARGETS hyprfocus) 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2023, Vortex 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SOURCE_FILES=$(wildcard src/*.cpp) 2 | 3 | all: 4 | $(CXX) -shared -fPIC --no-gnu-unique $(SOURCE_FILES) -o hyprfocus.so -g `pkg-config --cflags pixman-1 libdrm hyprland pangocairo libinput libudev wayland-server xkbcommon` -std=c++2b -O2 5 | strip hyprfocus.so 6 | 7 | debug: 8 | $(CXX) -shared -fPIC --no-gnu-unique $(SOURCE_FILES) -o hyprfocus.so -g `pkg-config --cflags pixman-1 libdrm hyprland pangocairo libinput libudev wayland-server xkbcommon` -std=c++2b -O2 9 | 10 | clean: 11 | rm ./hyprfocus.so 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hyprfocus 2 | 3 | a plugin which provides focus animations for us borderless folks, originally inspired by [flashfocus](https://github.com/fennerm/flashfocus)! 4 | Modified to work with the latest Hyprland and support hyprpm as well as some other improvements. 5 | 6 | ## animations 7 | 8 | flash 9 | 10 | ![preview](flash.gif) 11 | 12 | shrink 13 | 14 | ![preview](shrink.gif) 15 | 16 | ## installation 17 | 18 | instructions based on [the official wiki](https://wiki.hyprland.org/Plugins/Using-Plugins/#compiling-official-plugins) 19 | 20 | ``` 21 | hyprpm add https://github.com/pyt0xic/hyprfocus 22 | ``` 23 | 24 | ## getting started 25 | 26 | to start using hyprfocus, add this to your hyprland config: 27 | 28 | ``` 29 | hyprfocus { 30 | enabled = yes 31 | animate_floating = yes 32 | animate_workspacechange = yes 33 | focus_animation = shrink 34 | # Beziers for focus animations 35 | bezier = bezIn, 0.5,0.0,1.0,0.5 36 | bezier = bezOut, 0.0,0.5,0.5,1.0 37 | bezier = overshot, 0.05, 0.9, 0.1, 1.05 38 | bezier = smoothOut, 0.36, 0, 0.66, -0.56 39 | bezier = smoothIn, 0.25, 1, 0.5, 1 40 | bezier = realsmooth, 0.28,0.29,.69,1.08 41 | # Flash settings 42 | flash { 43 | flash_opacity = 0.95 44 | in_bezier = realsmooth 45 | in_speed = 0.5 46 | out_bezier = realsmooth 47 | out_speed = 3 48 | } 49 | # Shrink settings 50 | shrink { 51 | shrink_percentage = 0.95 52 | in_bezier = realsmooth 53 | in_speed = 1 54 | out_bezier = realsmooth 55 | out_speed = 2 56 | } 57 | } 58 | ``` 59 | 60 | ### plugin configuration 61 | 62 | `enabled` (yes/no) -> enable or disable the plugin 63 | 64 | `focus_animation` (flash/shrink/none) -> animation for keyboard-driven focus 65 | 66 | `animate_workspacechange` (yes/no) -> Whether to trigger the focus animation when changing workspaces 67 | 68 | `animate_floating` (yes/no) -> Whether to trigger the focus animation for floating windows 69 | 70 | ### animations configuration 71 | 72 | animations can be configured within the plugin scope with the following syntax: 73 | 74 | ``` 75 | plugin:hyprfocus { 76 | { 77 | 78 | } 79 | } 80 | ``` 81 | 82 | ### shared animation variables 83 | 84 | `in_bezier` (bezier) -> bezier curve towards the animation apex 85 | 86 | `out_bezier` (bezier) -> bezier curve towards the default window state 87 | 88 | `in_speed` (float) -> speed for the 'in' bezier 89 | 90 | `out_speed` (float) -> speed for the 'out' bezier 91 | 92 | ### flash 93 | 94 | `flash_opacity` (float) -> opacity to during the flash's apex 95 | 96 | ### shrink 97 | 98 | `shrink_percentage` (float) -> the amount a window has shrunk during the animation's apex 99 | 100 | ### dispatching 101 | 102 | hyprfocus can also flash the currently focused window through the `animatefocused` dispatcher: 103 | 104 | ``` 105 | bind = $mod, space, animatefocused 106 | ``` 107 | 108 | # Special thanks 109 | 110 | - [flashfocus](https://github.com/fennerm/flashfocus): An earlier project of similar nature 111 | - [Original repo](https://github.com/VortexCoyote/hyprfocus.git) 112 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "hyprland": { 4 | "inputs": { 5 | "hyprland-protocols": "hyprland-protocols", 6 | "nixpkgs": "nixpkgs", 7 | "systems": "systems", 8 | "wlroots": "wlroots", 9 | "xdph": "xdph" 10 | }, 11 | "locked": { 12 | "lastModified": 1698018813, 13 | "narHash": "sha256-JMg+HRyTOZK3W8pRNyJTp7AOWYkbs+LaKqAFc+cScyM=", 14 | "owner": "hyprwm", 15 | "repo": "Hyprland", 16 | "rev": "015664eb4cde5ab93cfacbfd8c2e831eeb876634", 17 | "type": "github" 18 | }, 19 | "original": { 20 | "owner": "hyprwm", 21 | "repo": "Hyprland", 22 | "type": "github" 23 | } 24 | }, 25 | "hyprland-protocols": { 26 | "inputs": { 27 | "nixpkgs": [ 28 | "hyprland", 29 | "nixpkgs" 30 | ], 31 | "systems": [ 32 | "hyprland", 33 | "systems" 34 | ] 35 | }, 36 | "locked": { 37 | "lastModified": 1691753796, 38 | "narHash": "sha256-zOEwiWoXk3j3+EoF3ySUJmberFewWlagvewDRuWYAso=", 39 | "owner": "hyprwm", 40 | "repo": "hyprland-protocols", 41 | "rev": "0c2ce70625cb30aef199cb388f99e19a61a6ce03", 42 | "type": "github" 43 | }, 44 | "original": { 45 | "owner": "hyprwm", 46 | "repo": "hyprland-protocols", 47 | "type": "github" 48 | } 49 | }, 50 | "nix-filter": { 51 | "locked": { 52 | "lastModified": 1694857738, 53 | "narHash": "sha256-bxxNyLHjhu0N8T3REINXQ2ZkJco0ABFPn6PIe2QUfqo=", 54 | "owner": "numtide", 55 | "repo": "nix-filter", 56 | "rev": "41fd48e00c22b4ced525af521ead8792402de0ea", 57 | "type": "github" 58 | }, 59 | "original": { 60 | "owner": "numtide", 61 | "repo": "nix-filter", 62 | "type": "github" 63 | } 64 | }, 65 | "nixpkgs": { 66 | "locked": { 67 | "lastModified": 1694767346, 68 | "narHash": "sha256-5uH27SiVFUwsTsqC5rs3kS7pBoNhtoy9QfTP9BmknGk=", 69 | "owner": "NixOS", 70 | "repo": "nixpkgs", 71 | "rev": "ace5093e36ab1e95cb9463863491bee90d5a4183", 72 | "type": "github" 73 | }, 74 | "original": { 75 | "owner": "NixOS", 76 | "ref": "nixos-unstable", 77 | "repo": "nixpkgs", 78 | "type": "github" 79 | } 80 | }, 81 | "root": { 82 | "inputs": { 83 | "hyprland": "hyprland", 84 | "nix-filter": "nix-filter" 85 | } 86 | }, 87 | "systems": { 88 | "locked": { 89 | "lastModified": 1689347949, 90 | "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", 91 | "owner": "nix-systems", 92 | "repo": "default-linux", 93 | "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68", 94 | "type": "github" 95 | }, 96 | "original": { 97 | "owner": "nix-systems", 98 | "repo": "default-linux", 99 | "type": "github" 100 | } 101 | }, 102 | "wlroots": { 103 | "flake": false, 104 | "locked": { 105 | "host": "gitlab.freedesktop.org", 106 | "lastModified": 1696410538, 107 | "narHash": "sha256-ecDhdYLXWHsxMv+EWG36mCNDvzRbu9qfjH7dLxL7aGM=", 108 | "owner": "wlroots", 109 | "repo": "wlroots", 110 | "rev": "3406c1b17a4a7e6d4e2a7d9c1176affa72bce1bc", 111 | "type": "gitlab" 112 | }, 113 | "original": { 114 | "host": "gitlab.freedesktop.org", 115 | "owner": "wlroots", 116 | "repo": "wlroots", 117 | "rev": "3406c1b17a4a7e6d4e2a7d9c1176affa72bce1bc", 118 | "type": "gitlab" 119 | } 120 | }, 121 | "xdph": { 122 | "inputs": { 123 | "hyprland-protocols": [ 124 | "hyprland", 125 | "hyprland-protocols" 126 | ], 127 | "nixpkgs": [ 128 | "hyprland", 129 | "nixpkgs" 130 | ], 131 | "systems": [ 132 | "hyprland", 133 | "systems" 134 | ] 135 | }, 136 | "locked": { 137 | "lastModified": 1694628480, 138 | "narHash": "sha256-Qg9hstRw0pvjGu5hStkr2UX1D73RYcQ9Ns/KnZMIm9w=", 139 | "owner": "hyprwm", 140 | "repo": "xdg-desktop-portal-hyprland", 141 | "rev": "8f45a6435069b9e24ebd3160eda736d7a391cbf2", 142 | "type": "github" 143 | }, 144 | "original": { 145 | "owner": "hyprwm", 146 | "repo": "xdg-desktop-portal-hyprland", 147 | "type": "github" 148 | } 149 | } 150 | }, 151 | "root": "root", 152 | "version": 7 153 | } 154 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | hyprland.url = "github:hyprwm/Hyprland"; 4 | nix-filter.url = "github:numtide/nix-filter"; 5 | }; 6 | outputs = { self, hyprland, nix-filter, ... }: 7 | let 8 | inherit (hyprland.inputs) nixpkgs; 9 | forHyprlandSystems = fn: nixpkgs.lib.genAttrs (builtins.attrNames hyprland.packages) (system: fn system nixpkgs.legacyPackages.${system}); 10 | in 11 | { 12 | packages = forHyprlandSystems 13 | (system: pkgs: rec { 14 | hyprfocus = pkgs.gcc13Stdenv.mkDerivation { 15 | pname = "hyprfocus"; 16 | version = "0.1"; 17 | src = nix-filter.lib { 18 | root = ./.; 19 | include = [ 20 | "src" 21 | ./Makefile 22 | ]; 23 | }; 24 | 25 | 26 | nativeBuildInputs = with pkgs; [ pkg-config ]; 27 | 28 | buildInputs = with pkgs; [ 29 | hyprland.packages.${system}.hyprland.dev 30 | ] 31 | ++ hyprland.packages.${system}.hyprland.buildInputs; 32 | 33 | installPhase = '' 34 | mkdir -p $out/lib 35 | install ./hyprfocus.so $out/lib/libhyprfocus.so 36 | ''; 37 | 38 | meta = with pkgs.lib; { 39 | homepage = "https://github.com/VortexCoyote/hyprfocus"; 40 | description = "a focus animation plugin for Hyprland inspired by Flashfocus"; 41 | license = licenses.bsd3; 42 | platforms = platforms.linux; 43 | }; 44 | 45 | 46 | }; 47 | default = hyprfocus; 48 | }); 49 | }; 50 | } 51 | 52 | -------------------------------------------------------------------------------- /flash.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyt0xic/hyprfocus/de6eaf5846b970b697bdf0e20e731b9fbe08654d/flash.gif -------------------------------------------------------------------------------- /hyprload.toml: -------------------------------------------------------------------------------- 1 | [hyprfocus] 2 | description = "a plugin which provides focus animations for us borderless folks, originally inspired by flashfocus" 3 | version = "2.0" 4 | author = "Vortex" 5 | 6 | [hyprfocus.build] 7 | output = "hyprfocus.so" 8 | steps = [ 9 | "make all", 10 | ] 11 | -------------------------------------------------------------------------------- /hyprpm.toml: -------------------------------------------------------------------------------- 1 | [repository] 2 | name = "hyprfocus" 3 | authors = ["VortexCoyote, OakleyCord, vaxerski, DRAGONTOS, pyt0xic"] 4 | 5 | [hyprfocus] 6 | description = "a focus animation plugin for Hyprland inspired by Flashfocus (pyt0xic fork)" 7 | authors = ["VortexCoyote, OakleyCord, vaxerski, DRAGONTOS, pyt0xic"] 8 | output = "hyprfocus.so" 9 | commit_pins = [ 10 | ["1c460e98f870676b15871fe4e5bfeb1a32a3d6d8", "2af0c1e87e3cdcef3c8f9c7def918982312d0b75"], 11 | ["c5e28ebcfe00a510922779b2c568cfa52a317445", "c5e28ebcfe00a510922779b2c568cfa52a317445"], 12 | ["0c513ba91bd73106be99e35b1a020d24e5ae874a", "e44b956dd4b7507489219bd2b02a3886d547b7e2"], 13 | ["cba1ade848feac44b2eda677503900639581c3f4", "49dbe19e08f963e01ac4ba17b42719956d390ec7"], 14 | ] 15 | build = [ 16 | "make all" 17 | ] 18 | -------------------------------------------------------------------------------- /shrink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyt0xic/hyprfocus/de6eaf5846b970b697bdf0e20e731b9fbe08654d/shrink.gif -------------------------------------------------------------------------------- /src/Flash.cpp: -------------------------------------------------------------------------------- 1 | #include "Flash.hpp" 2 | #include "Globals.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | void CFlash::init(HANDLE pHandle, std::string animationName) { 10 | IFocusAnimation::init(pHandle, animationName); 11 | addConfigValue(pHandle, "flash_opacity", Hyprlang::FLOAT{0.5f}); 12 | } 13 | 14 | void CFlash::setup(HANDLE pHandle, std::string animationName) { 15 | // IFocusAnimation::setup(pHandle, animationName); 16 | // static const auto *flash_opacity = 17 | // (Hyprlang::FLOAT *const *)(getConfigValue(pHandle, "flash_opacity") 18 | // ->getDataStaticPtr()); 19 | // g_fFlashOpacity = **flash_opacity; 20 | // hyprfocus_log(LOG, "Flash opacity: {}", g_fFlashOpacity); 21 | // static const auto *active_opacity = 22 | // (Hyprlang::FLOAT *const *)(HyprlandAPI::getConfigValue( 23 | // pHandle, "decoration:active_opacity") 24 | // ->getDataStaticPtr()); 25 | // g_fActiveOpacity = **active_opacity; 26 | // hyprfocus_log(LOG, "Active opacity: {}", g_fActiveOpacity); 27 | } 28 | 29 | void CFlash::onWindowFocus(PHLWINDOW pWindow, HANDLE pHandle) { 30 | hyprfocus_log(LOG, "Flash onWindowFocus start"); 31 | IFocusAnimation::onWindowFocus(pWindow, pHandle); 32 | 33 | static const auto *flash_opacity = 34 | (Hyprlang::FLOAT *const *)(getConfigValue(pHandle, "flash_opacity") 35 | ->getDataStaticPtr()); 36 | pWindow->m_fAlpha = **flash_opacity; 37 | // pWindow->m_fAlpha = g_fFlashOpacity; 38 | pWindow->m_fAlpha.setConfig(&m_sFocusInAnimConfig); 39 | pWindow->m_fAlpha.setCallbackOnEnd([this, pWindow, pHandle](void *) { 40 | static const auto *active_opacity = 41 | (Hyprlang::FLOAT *const *)(HyprlandAPI::getConfigValue( 42 | pHandle, "decoration:active_opacity") 43 | ->getDataStaticPtr()); 44 | // Make sure we restore to the active window opacity 45 | pWindow->m_fAlpha = **active_opacity; 46 | // pWindow->m_fAlpha = g_fActiveOpacity; 47 | pWindow->m_fAlpha.setConfig(&m_sFocusOutAnimConfig); 48 | }); 49 | } 50 | -------------------------------------------------------------------------------- /src/Flash.hpp: -------------------------------------------------------------------------------- 1 | #include "IFocusAnimation.hpp" 2 | 3 | class CFlash : public IFocusAnimation { 4 | public: 5 | void onWindowFocus(PHLWINDOW pWindow, HANDLE pHandle) override; 6 | void init(HANDLE pHandle, std::string animationName) override; 7 | void setup(HANDLE pHandle, std::string animationName) override; 8 | }; 9 | -------------------------------------------------------------------------------- /src/Globals.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Log.hpp" 4 | #include 5 | #include 6 | 7 | // inline std::string g_sInBezier; 8 | // inline float g_fInSpeed; 9 | // inline std::string g_sOutBezier; 10 | // inline float g_fOutSpeed; 11 | 12 | // General 13 | inline HANDLE PHANDLE = nullptr; 14 | 15 | // inline std::string g_sAnimationType; 16 | 17 | // Flash 18 | // inline float g_fFlashOpacity; 19 | // inline float g_fActiveOpacity; 20 | 21 | // Shrink 22 | // inline float g_fShrinkPercentage; 23 | -------------------------------------------------------------------------------- /src/IFocusAnimation.cpp: -------------------------------------------------------------------------------- 1 | #include "IFocusAnimation.hpp" 2 | #include "Globals.hpp" 3 | 4 | #include 5 | #include 6 | 7 | void IFocusAnimation::init(HANDLE pHandle, std::string animationName) { 8 | m_szAnimationName = animationName; 9 | hyprfocus_log(LOG, "Initializing focus animation: {}", animationName); 10 | 11 | HyprlandAPI::addConfigValue(pHandle, configPrefix() + "in_bezier", 12 | Hyprlang::STRING{"default"}); 13 | HyprlandAPI::addConfigValue(pHandle, configPrefix() + "out_bezier", 14 | Hyprlang::STRING{"default"}); 15 | 16 | HyprlandAPI::addConfigValue(pHandle, configPrefix() + "in_speed", 17 | Hyprlang::FLOAT{1.f}); 18 | HyprlandAPI::addConfigValue(pHandle, configPrefix() + "out_speed", 19 | Hyprlang::FLOAT{5.f}); 20 | 21 | m_sFocusInAnimConfig = 22 | *(g_pConfigManager->getAnimationPropertyConfig("global")); 23 | m_sFocusInAnimConfig.internalEnabled = 1; 24 | m_sFocusInAnimConfig.internalStyle = 25 | std::string("hyprfocus_") + animationName + std::string("_in"); 26 | m_sFocusInAnimConfig.pValues = &m_sFocusInAnimConfig; 27 | 28 | m_sFocusOutAnimConfig = 29 | *(g_pConfigManager->getAnimationPropertyConfig("global")); 30 | m_sFocusOutAnimConfig.internalEnabled = 1; 31 | m_sFocusOutAnimConfig.internalStyle = 32 | std::string("hyprfocus_") + animationName + std::string("_out"); 33 | m_sFocusOutAnimConfig.pValues = &m_sFocusOutAnimConfig; 34 | } 35 | 36 | void IFocusAnimation::setup(HANDLE pHandle, std::string animationName) { 37 | // hyprfocus_log(LOG, "Setting up focus animation: {}", animationName); 38 | } 39 | 40 | void IFocusAnimation::onWindowFocus(PHLWINDOW pWindow, HANDLE pHandle) { 41 | hyprfocus_log(LOG, "Base callback for animation: {}", m_szAnimationName); 42 | static const auto *inBezier = 43 | (Hyprlang::STRING const *)(HyprlandAPI::getConfigValue( 44 | pHandle, configPrefix() + "in_bezier") 45 | ->getDataStaticPtr()); 46 | static const auto *inSpeed = 47 | (Hyprlang::FLOAT *const *)(HyprlandAPI::getConfigValue( 48 | pHandle, configPrefix() + "in_speed") 49 | ->getDataStaticPtr()); 50 | static const auto *outBezier = 51 | (Hyprlang::STRING const *)(HyprlandAPI::getConfigValue( 52 | pHandle, configPrefix() + "out_bezier") 53 | ->getDataStaticPtr()); 54 | static const auto *outSpeed = 55 | (Hyprlang::FLOAT *const *)(HyprlandAPI::getConfigValue( 56 | pHandle, configPrefix() + "out_speed") 57 | ->getDataStaticPtr()); 58 | m_sFocusInAnimConfig.internalBezier = *inBezier; 59 | m_sFocusInAnimConfig.internalSpeed = **inSpeed; 60 | 61 | m_sFocusOutAnimConfig.internalBezier = *outBezier; 62 | m_sFocusOutAnimConfig.internalSpeed = **outSpeed; 63 | 64 | hyprfocus_log(LOG, "In bezier: {} In speed: {} Out bezier: {} Out speed: {}", 65 | m_sFocusInAnimConfig.internalBezier, 66 | m_sFocusInAnimConfig.internalSpeed, 67 | m_sFocusOutAnimConfig.internalBezier, 68 | m_sFocusOutAnimConfig.internalSpeed); 69 | } 70 | 71 | void IFocusAnimation::addConfigValue(HANDLE pHandle, std::string name, 72 | Hyprlang::CConfigValue sValue) { 73 | HyprlandAPI::addConfigValue(pHandle, configPrefix() + name, sValue); 74 | hyprfocus_log(LOG, "Added config value: {}", configPrefix() + name); 75 | } 76 | 77 | Hyprlang::CConfigValue *IFocusAnimation::getConfigValue(HANDLE pHandle, 78 | std::string name) { 79 | hyprfocus_log(LOG, "Getting config value: {}", configPrefix() + name); 80 | return HyprlandAPI::getConfigValue(pHandle, configPrefix() + name); 81 | } 82 | -------------------------------------------------------------------------------- /src/IFocusAnimation.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #define WLR_USE_UNSTABLE 5 | 6 | #include 7 | 8 | class IFocusAnimation { 9 | public: 10 | virtual void onWindowFocus(PHLWINDOW pWindow, HANDLE pHandle); 11 | virtual void init(HANDLE pHandle, std::string animationName); 12 | virtual void setup(HANDLE pHandle, std::string animationName); 13 | 14 | void addConfigValue(HANDLE pHandle, std::string name, 15 | Hyprlang::CConfigValue sValue); 16 | Hyprlang::CConfigValue *getConfigValue(HANDLE pHandle, std::string name); 17 | 18 | public: 19 | SAnimationPropertyConfig m_sFocusInAnimConfig; 20 | SAnimationPropertyConfig m_sFocusOutAnimConfig; 21 | 22 | std::string m_szAnimationName; 23 | 24 | std::string configPrefix() { 25 | return std::string("plugin:hyprfocus:") + m_szAnimationName + ":"; 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /src/Log.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define WLR_USE_UNSTABLE 3 | #include 4 | 5 | template 6 | void hyprfocus_log(eLogLevel level, std::format_string fmt, 7 | Args &&...args) { 8 | auto msg = std::vformat(fmt.get(), std::make_format_args(args...)); 9 | Debug::log(level, "[hyprfocus] {}", msg); 10 | } 11 | -------------------------------------------------------------------------------- /src/Shrink.cpp: -------------------------------------------------------------------------------- 1 | #include "Shrink.hpp" 2 | 3 | #include "Log.hpp" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | void CShrink::init(HANDLE pHandle, std::string animationName) { 10 | IFocusAnimation::init(pHandle, "shrink"); 11 | addConfigValue(pHandle, "shrink_percentage", Hyprlang::FLOAT{0.5f}); 12 | } 13 | 14 | void CShrink::setup(HANDLE pHandle, std::string animationName) { 15 | // IFocusAnimation::setup(pHandle, animationName); 16 | // static const auto *shrinkPercentage = 17 | // (Hyprlang::FLOAT *const *)(getConfigValue(pHandle, "shrink_percentage") 18 | // ->getDataStaticPtr()); 19 | // g_fShrinkPercentage = **shrinkPercentage; 20 | // hyprfocus_log(LOG, "Shrink percentage: {}", g_fShrinkPercentage); 21 | } 22 | 23 | void CShrink::onWindowFocus(PHLWINDOW pWindow, HANDLE pHandle) { 24 | std::string currentAnimStyle = 25 | pWindow->m_vRealSize.getConfig()->internalStyle; 26 | hyprfocus_log(LOG, "Current animation style: {}", currentAnimStyle); 27 | if ((currentAnimStyle == "popout" || currentAnimStyle == "popin") && 28 | pWindow->m_vRealSize.isBeingAnimated()) { 29 | hyprfocus_log(LOG, "Shrink: Window is already being animated, skipping"); 30 | return; 31 | } 32 | 33 | IFocusAnimation::onWindowFocus(pWindow, pHandle); 34 | 35 | pWindow->m_vRealSize.setConfig(&m_sFocusOutAnimConfig); 36 | pWindow->m_vRealPosition.setConfig(&m_sFocusOutAnimConfig); 37 | 38 | m_sShrinkAnimation.registerVar(); 39 | m_sShrinkAnimation.create(1.0f, &m_sFocusInAnimConfig, AVARDAMAGE_ENTIRE); 40 | static const auto *shrinkPercentage = 41 | (Hyprlang::FLOAT *const *)(getConfigValue(pHandle, "shrink_percentage") 42 | ->getDataStaticPtr()); 43 | hyprfocus_log(LOG, "Shrink percentage: {}", **shrinkPercentage); 44 | m_sShrinkAnimation = **shrinkPercentage; 45 | 46 | m_sShrinkAnimation.setUpdateCallback([this, pWindow](void *pShrinkAnimation) { 47 | const auto GOALPOS = pWindow->m_vRealPosition.goal(); 48 | const auto GOALSIZE = pWindow->m_vRealSize.goal(); 49 | 50 | const auto *PANIMATION = (CAnimatedVariable *)pShrinkAnimation; 51 | 52 | pWindow->m_vRealSize.setValue(GOALSIZE * PANIMATION->value()); 53 | pWindow->m_vRealPosition.setValue(GOALPOS + GOALSIZE / 2.f - 54 | pWindow->m_vRealSize.value() / 2.f); 55 | }); 56 | 57 | m_sShrinkAnimation.setCallbackOnEnd([this, pWindow](void *pShrinkAnimation) { 58 | ((CAnimatedVariable *)pShrinkAnimation)->resetAllCallbacks(); 59 | }); 60 | } 61 | -------------------------------------------------------------------------------- /src/Shrink.hpp: -------------------------------------------------------------------------------- 1 | #include "IFocusAnimation.hpp" 2 | 3 | class CShrink : public IFocusAnimation { 4 | public: 5 | void onWindowFocus(PHLWINDOW pWindow, HANDLE pHandle) override; 6 | void init(HANDLE pHandle, std::string animationName) override; 7 | void setup(HANDLE pHandle, std::string animationName) override; 8 | 9 | CAnimatedVariable m_sShrinkAnimation; 10 | }; 11 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "Log.hpp" 2 | #include 3 | #include 4 | 5 | #include "Globals.hpp" 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "Flash.hpp" 15 | #include "Shrink.hpp" 16 | 17 | PHLWINDOW g_pPreviouslyFocusedWindow = nullptr; 18 | bool g_bMouseWasPressed = false; 19 | bool g_bWorkspaceChanged = false; 20 | 21 | std::unordered_map> g_mAnimations; 22 | 23 | static bool OnSameWorkspace(PHLWINDOW pWindow1, PHLWINDOW pWindow2) { 24 | if (pWindow1 == pWindow2) { 25 | return true; 26 | } else if (pWindow1 == nullptr || pWindow2 == nullptr) { 27 | return false; 28 | } else if (pWindow1->workspaceID() == pWindow2->workspaceID()) { 29 | return true; 30 | } else { 31 | return false; 32 | } 33 | } 34 | 35 | void flashWindow(PHLWINDOW pWindow) { 36 | // static const Hyprlang::STRING *focusAnimation = nullptr; 37 | // if (g_bMouseWasPressed == true) { 38 | // hyprfocus_log(LOG, "Mouse was pressed"); 39 | // focusAnimation = 40 | // (Hyprlang::STRING const *)(HyprlandAPI::getConfigValue( 41 | // PHANDLE, 42 | // "plugin:hyprfocus:mouse_focus_animation") 43 | // ->getDataStaticPtr()); 44 | // } else { 45 | // focusAnimation = 46 | // (Hyprlang::STRING const 47 | // *)(HyprlandAPI::getConfigValue( 48 | // PHANDLE, "plugin:hyprfocus:keyboard_focus_animation") 49 | // ->getDataStaticPtr()); 50 | // } 51 | static const Hyprlang::STRING *focusAnimation = 52 | (Hyprlang::STRING const *)(HyprlandAPI::getConfigValue( 53 | PHANDLE, 54 | "plugin:hyprfocus:focus_animation") 55 | ->getDataStaticPtr()); 56 | hyprfocus_log(LOG, "Flashing window"); 57 | hyprfocus_log(LOG, "Animation: {}", *focusAnimation); 58 | 59 | auto it = g_mAnimations.find(*focusAnimation); 60 | if (it != g_mAnimations.end()) { 61 | hyprfocus_log(LOG, "Calling onWindowFocus for animation {}", 62 | *focusAnimation); 63 | it->second->onWindowFocus(pWindow, PHANDLE); 64 | } 65 | } 66 | 67 | void flashCurrentWindow(std::string) { 68 | hyprfocus_log(LOG, "Flashing current window"); 69 | static auto *const PHYPRFOCUSENABLED = 70 | (Hyprlang::INT *const *)HyprlandAPI::getConfigValue( 71 | PHANDLE, "plugin:hyprfocus:enabled") 72 | ->getDataStaticPtr(); 73 | if (!*PHYPRFOCUSENABLED) { 74 | hyprfocus_log(LOG, "HyprFocus is disabled"); 75 | return; 76 | } 77 | 78 | if (g_pPreviouslyFocusedWindow == nullptr) { 79 | hyprfocus_log(LOG, "No previously focused window"); 80 | return; 81 | } 82 | 83 | flashWindow(g_pPreviouslyFocusedWindow); 84 | } 85 | 86 | static void onActiveWindowChange(void *self, std::any data) { 87 | try { 88 | hyprfocus_log(LOG, "Active window changed"); 89 | auto const PWINDOW = std::any_cast(data); 90 | static auto *const PHYPRFOCUSENABLED = 91 | (Hyprlang::INT *const *)HyprlandAPI::getConfigValue( 92 | PHANDLE, "plugin:hyprfocus:enabled") 93 | ->getDataStaticPtr(); 94 | 95 | static auto *const PANIMATEFLOATING = 96 | (Hyprlang::INT *const *)HyprlandAPI::getConfigValue( 97 | PHANDLE, "plugin:hyprfocus:animate_floating") 98 | ->getDataStaticPtr(); 99 | 100 | static auto *const PANIMATEWORKSPACECHANGE = 101 | (Hyprlang::INT *const *)HyprlandAPI::getConfigValue( 102 | PHANDLE, "plugin:hyprfocus:animate_workspacechange") 103 | ->getDataStaticPtr(); 104 | 105 | if (!**PHYPRFOCUSENABLED) { 106 | hyprfocus_log(LOG, "HyprFocus is disabled"); 107 | return; 108 | } 109 | 110 | if (PWINDOW == nullptr) { 111 | hyprfocus_log(LOG, "Window is null"); 112 | return; 113 | } 114 | 115 | if (PWINDOW == g_pPreviouslyFocusedWindow) { 116 | hyprfocus_log(LOG, "Window is the same as the previous one"); 117 | return; 118 | } 119 | 120 | if (PWINDOW->m_bIsFloating && !**PANIMATEFLOATING) { 121 | hyprfocus_log(LOG, "Floating window, not animating"); 122 | g_pPreviouslyFocusedWindow = PWINDOW; 123 | return; 124 | } 125 | 126 | if (!**PANIMATEWORKSPACECHANGE && 127 | !OnSameWorkspace(PWINDOW, g_pPreviouslyFocusedWindow)) { 128 | hyprfocus_log(LOG, "Workspace changed, not animating"); 129 | g_pPreviouslyFocusedWindow = PWINDOW; 130 | return; 131 | } 132 | 133 | flashWindow(PWINDOW); 134 | g_pPreviouslyFocusedWindow = PWINDOW; 135 | 136 | } catch (std::bad_any_cast &e) { 137 | hyprfocus_log(ERR, "Cast Error: {}", e.what()); 138 | } catch (std::exception &e) { 139 | hyprfocus_log(ERR, "Error: {}", e.what()); 140 | } 141 | } 142 | 143 | static void onMouseButton(void *self, std::any data) { 144 | try { 145 | auto const PWLRMOUSEBUTTONEVENT = 146 | std::any_cast(data); 147 | hyprfocus_log(LOG, "Mouse button state: {}", 148 | (int)PWLRMOUSEBUTTONEVENT.state); 149 | g_bMouseWasPressed = (int)PWLRMOUSEBUTTONEVENT.state == 1; 150 | 151 | } catch (std::bad_any_cast &e) { 152 | hyprfocus_log(ERR, "Cast Error: {}", e.what()); 153 | } catch (std::exception &e) { 154 | hyprfocus_log(ERR, "Error: {}", e.what()); 155 | } 156 | } 157 | 158 | // Do NOT change this function. 159 | APICALL EXPORT std::string PLUGIN_API_VERSION() { return HYPRLAND_API_VERSION; } 160 | 161 | APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { 162 | PHANDLE = handle; 163 | 164 | HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprfocus:enabled", 165 | Hyprlang::INT{0}); 166 | HyprlandAPI::addConfigValue( 167 | PHANDLE, "plugin:hyprfocus:animate_workspacechange", Hyprlang::INT{1}); 168 | HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprfocus:animate_floating", 169 | Hyprlang::INT{1}); 170 | HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprfocus:focus_animation", 171 | Hyprlang::STRING("flash")); 172 | HyprlandAPI::addDispatcher(PHANDLE, "animatefocused", &flashCurrentWindow); 173 | 174 | g_mAnimations["flash"] = std::make_unique(); 175 | g_mAnimations["shrink"] = std::make_unique(); 176 | g_mAnimations["none"] = std::make_unique(); 177 | 178 | for (auto &[name, pAnimation] : g_mAnimations) { 179 | pAnimation->init(PHANDLE, name); 180 | hyprfocus_log(LOG, "Registered animation: {}", name); 181 | } 182 | 183 | HyprlandAPI::reloadConfig(); 184 | g_pConfigManager->tick(); 185 | hyprfocus_log(LOG, "Reloaded config"); 186 | 187 | // Register callbacks 188 | static auto P1 = HyprlandAPI::registerCallbackDynamic( 189 | PHANDLE, "activeWindow", 190 | [&](void *self, SCallbackInfo &info, std::any data) { 191 | onActiveWindowChange(self, data); 192 | }); 193 | hyprfocus_log(LOG, "Registered active window change callback"); 194 | 195 | static auto P2 = HyprlandAPI::registerCallbackDynamic( 196 | PHANDLE, "mouseButton", 197 | [&](void *self, SCallbackInfo &info, std::any data) { 198 | onMouseButton(self, data); 199 | }); 200 | hyprfocus_log(LOG, "Registered mouse button callback"); 201 | 202 | HyprlandAPI::reloadConfig(); 203 | 204 | return {"hyprfocus", "animate windows on focus", "Vortex", "2.0"}; 205 | } 206 | 207 | APICALL EXPORT void PLUGIN_EXIT() {} 208 | --------------------------------------------------------------------------------