This commit is contained in:
ilusha 2025-06-06 21:29:40 +00:00
commit 1210b5b7be
18 changed files with 1661 additions and 0 deletions

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
/result
/build
/.vscode
/.cache
*build*
*rel*
.idea

29
CMakeLists.txt Normal file
View file

@ -0,0 +1,29 @@
cmake_minimum_required(VERSION 3.19)
project(Hycov VERSION "0.2")
set(CMAKE_CXX_STANDARD 23)
add_compile_definitions(WLR_USE_UNSTABLE)
add_compile_options(-g -Wall -Wextra -Wno-unused-parameter -Wno-unused-value -Wno-missing-field-initializers -Wno-narrowing -Wno-pointer-arith)
# nix workaround
if(CMAKE_EXPORT_COMPILE_COMMANDS)
set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES
${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES})
endif()
find_package(PkgConfig REQUIRED)
pkg_check_modules(DEPS REQUIRED hyprland pixman-1 libdrm pango pangocairo)
add_library(hycov SHARED
src/main.cpp
src/Plugin.cpp
src/OverviewManager.cpp
src/OverviewLayout.cpp
src/DwindleSwitcher.cpp
)
target_include_directories(hycov PRIVATE ${DEPS_INCLUDE_DIRS})
install(TARGETS hycov LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
# set(CMAKE_VERBOSE_MAKEFILE ON)
# set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 GreamMaoMao
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

352
README.md Normal file
View file

@ -0,0 +1,352 @@
# update info
**for hyprland v 0.4.0**
checkout to tag `v0.4.0`
**for hyprland v0.45.0**
checkout to tag `v0.45.0`
**for hyprland v0.45.2^**
in the `main` branch
no more than hyprland v0.46.1
# hycov
A Hyprland overview mode plugin, a new tiling WM workflow.
> [!NOTE]
> This repository only maintains the hyprland version corresponding to each hycov release, please do not bring up the issue of non-release versions, because I do not have enough time to track every commit.
### What can it do?
- Hycov can tile all of your windows in a single workspace via grid layout.
- After quitting the overview mode, hycov can perfectly recover a window's previous state (fullscreen, floating, size, positon, etc.)
- Hycov supports a variety of trigger methods, such as touch pad gestures, hot corners, and keyboard shortcuts.
- Supports multiple monitors.
- You can change the way that Hycov focuses a window, whether directional or cyclical. (single-shortcut)
https://github.com/DreamMaoMao/hycov/assets/30348075/59121362-21a8-4143-be95-72ce79ee8e95
Anyone is welcome to fork. If you end up improving the plugin, please let me know, and I'll be happy to use your fork.
### Manual Installation
> [!NOTE]
>
> 1. After Hycov is installed, you will need to logout, then log back in. This may happen automatically, but do not worry. This behaviour is normal.
> 2. Only supports hyprland source code after 2023-10-22, because the plugin requires this [commit](https://github.com/hyprwm/Hyprland/commit/a61eb7694df25a75f45502ed64b1536fda370c1d) in [hyprland](https://github.com/hyprwm/Hyprland).
> 3. Each release of hycov corresponds to each release of hyprland. If you are using a release version of hycov, but you are using the latest hyprland-git, this may not be available.
##### Using meson and ninja:
```shell
git clone https://github.com/DreamMaoMao/hycov.git
cd hycov
sudo meson setup build --prefix=/usr
sudo ninja -C build
sudo ninja -C build install # `libhycov.so` path: /usr/lib/libhycov.so
```
##### Using CMake:
```shell
git clone https://github.com/DreamMaoMao/hycov.git
cd hycov
bash install.sh # `libhycov.so` path: /usr/lib/libhycov.so
```
##### Using hyprpm:
```shell
hyprpm update
hyprpm add https://github.com/DreamMaoMao/hycov
hyprpm enable hycov
```
### Usage (hyprland.conf)
```conf
# When entering overview mode, you can use left-button to jump, right-button to kill or use keybind
# If you are installing hycov with hyprpm, you should comment out this
plugin = /usr/lib/libhycov.so
# If you are installing hycov by manually compile , you should comment out this
exec-once = hyprpm reload
# bind key to toggle overview (normal)
bind = ALT,tab,hycov:toggleoverview
# bind key to toggle overview (force mode, not affected by `only_active_workspace` or `only_active_monitor`)
bind = ALT,grave,hycov:toggleoverview,forceall #grave key is the '~' key
# bind key to toggle overview (force mode, not affected by `only_active_workspace` or `only_active_monitor`)
bind = ALT,c,hycov:toggleoverview,onlycurrentworkspace
# bind key to toggle overview (shows all windows in one monitor, not affected by `only_active_workspace` or `only_active_monitor`)
bind = ALT,g,hycov:toggleoverview,forceallinone
# The key binding for directional switch mode.
# Calculate the window closest to the direction to switch focus.
# This keybind is applicable not only to the overview, but also to the general layout.
bind=ALT,left,hycov:movefocus,l
bind=ALT,right,hycov:movefocus,r
bind=ALT,up,hycov:movefocus,u
bind=ALT,down,hycov:movefocus,d
# if you want that focusmove can cross monitor, use this
bind=ALT,left,hycov:movefocus,leftcross
bind=ALT,right,hycov:movefocus,rightcross
bind=ALT,up,hycov:movefocus,upcross
bind=ALT,down,hycov:movefocus,downcross
plugin {
hycov {
overview_gappo = 60 # gaps width from screen edge
overview_gappi = 24 # gaps width from clients
enable_hotarea = 1 # enable mouse cursor hotarea, when cursor enter hotarea, it will toggle overview
enable_click_action = 1 # enable mouse left button jump and right button kill in overview mode
hotarea_monitor = all # monitor name which hotarea is in, default is all
hotarea_pos = 1 # position of hotarea (1: bottom left, 2: bottom right, 3: top left, 4: top right)
hotarea_size = 10 # hotarea size, 10x10
swipe_fingers = 4 # finger number of gesture,move any directory
move_focus_distance = 100 # distance for movefocus,only can use 3 finger to move
enable_gesture = 0 # enable gesture
auto_exit = 1 # enable auto exit when no client in overview
auto_fullscreen = 0 # auto make active window maximize after exit overview
only_active_workspace = 0 # only overview the active workspace
only_active_monitor = 0 # only overview the active monitor
enable_alt_release_exit = 0 # alt swith mode arg,see readme for detail
alt_replace_key = Alt_L # alt swith mode arg,see readme for detail
alt_toggle_auto_next = 0 # auto focus next window when toggle overview in alt swith mode
click_in_cursor = 1 # when click to jump,the target windwo is find by cursor, not the current foucus window.
hight_of_titlebar = 0 # height deviation of title bar height
show_special = 0 # show windwos in special workspace in overview.
raise_float_to_top = 1 # raise the window that is floating before to top after leave overview mode
}
}
```
# suggested additional configuration
- when `auto_fullscreen=1` is set, you can also set the border color to mark the maximize state and bind key to control fullscreen maximize state.
```
windowrulev2 = bordercolor rgb(158833),fullscreen:1 # set bordercolor to green if window is fullscreen maximize
# toggle fullscreen maximize
bind = ALT,a,fullscreen,1
```
<details>
<summary>detail video</summary>
https://github.com/DreamMaoMao/hycov/assets/30348075/15ba36c2-1782-4ae0-8ac1-d0ca98e01e0f
</details>
- if you use the `hyprland/workspaces` module in waybar,you should change field {id} to {name}. It will let you know you are in overview mode.
```
"hyprland/workspaces": {
"format": "{name}",
"on-click":"activate",
},
```
<details>
<summary>detail picture</summary>
![image](https://github.com/DreamMaoMao/hycov/assets/30348075/332f4025-20c1-4a44-853b-1b5264df986e)
![image](https://github.com/DreamMaoMao/hycov/assets/30348075/500d9fd7-299b-48bc-ab72-146f263044a5)
</details>
# Alt switch mode
```conf
enable_alt_release_exit = 1
alt_toggle_auto_next = 0 # auto focus next window when enter overview in alt mode
# alt_replace_key = Alt_L # If your MainKey of toggleoverview is ALt, you can ignore it
```
## operation
such as `alt + tab`:
- 1.`alt + tab` will enter overview when you not in overview(please hold alt,don't make it release)
- 2.`alt + tab` will switch window focus circularly when you in overview. (please hold alt,don't make it release)
- 3.when you release `alt` , it will auto exit overview.
<details>
<summary> If you don't want to use `alt` as MainKey in alt mode</summary>
such as use `super` to repalce `alt`
- 1.bind toggleoverview
```
bind = SUPER,tab,hycov:toggleoverview
```
- 2.use `alt_replace_key` to specify what is the detection key on release.
```
# use keyname
alt_replace_key = Super_L # Alt_L,Alt_R,Super_L,Super_R,Control_L,Control_R,Shift_L,Shift_R
# use keycode
alt_replace_key = code:133 # use `xev` command to get keycode
```
</details>
### NixOS with home—manager
```nix
# flake.nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
hyprland.url = "github:hyprwm/Hyprland";
hycov={
url = "github:DreamMaoMao/hycov";
inputs.hyprland.follows = "hyprland";
};
};
outputs = { nixpkgs, home-manager, hyprland, hycov, ... }:
let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
in
{
homeConfigurations."user@hostname" = home-manager.lib.homeManagerConfiguration {
pkgs = nixpkgs.legacyPackages.x86_64-linux;
modules = [
hyprland.homeManagerModules.default
{
wayland.windowManager.hyprland = {
enable = true;
package = hyprland.packages."${pkgs.system}".hyprland;
plugins = [
hycov.packages.${pkgs.system}.hycov
];
extraConfig = ''
bind = ALT,tab,hycov:toggleoverview
bind=ALT,left,hycov:movefocus,l
bind=ALT,right,hycov:movefocus,r
bind=ALT,up,hycov:movefocus,u
bind=ALT,down,hycov:movefocus,d
plugin {
hycov {
overview_gappo = 60 #gaps width from screen
overview_gappi = 24 #gaps width from clients
hotarea_size = 10 #hotarea size in bottom left,10x10
enable_hotarea = 1 # enable mouse cursor hotarea
}
}
'' + ''
# your othor config
'';
};
}
# ...
];
};
};
}
```
## Frequently Asked Questions
- some config not work, or the plugin not work.
```
if you use install hycov at first time,please try logout and relogin again.
```
- The numbers on the waybar are confused
```
1.Please pull the latest waybar source code compilation,
this issue has been fixed in the waybar project, fix date (2023-10-27)
2.Change the {id} field in hyprland/workspace field to {name}
```
- Compilation failure
```
Please pull the latest hyprland source code to compile and install.
The plugin relies on a hyprland pr,pr submission date (2023-10-21)
```
- Unable to load
```
Check whether hyprland has been updated,
and if so, please recompile hyprcov
```
- build fail with message `No such file or directory #include <wlr/xxx>`
```
#step1
yay -R hyprland-git wlroots-git
#step2
sudo rm -rf /usr/include/hyprland
sudo rm -rf /usr/include/wlr
sudo rm -rf/usr/local/include/hyprland
sudo rm -rf /usr/local/include/wlr
#step3
yay -S wlroots-git hyprland-git
```
## Some cool use cases
<details>
<summary> hycov + [hyprland-easymotion](https://github.com/zakk4223/hyprland-easymotion) </summary>
https://github.com/DreamMaoMao/hycov/assets/30348075/486b08f1-be0d-4647-90a3-2029961402cd
```conf
bind = ALT,tab, exec, ~/.config/hypr/scripts/hycov-easymotion.sh
submap=__easymotionsubmap__
bind = ALT, Tab, exec, ~/.config/hypr/scripts/hycov-easymotion.sh
submap=reset
```
```bash
#!/bin/bash
workspace_name=$(hyprctl -j activeworkspace | jq -r '.name')
if [ "$workspace_name" = "OVERVIEW" ]; then
hyprctl dispatch hycov:leaveoverview
else
hyprctl dispatch hycov:enteroverview
hyprctl dispatch 'easymotion action:hyprctl --batch "dispatch focuswindow address:{} ; dispatch hycov:leaveoverview"'
fi
```
</details>

20
default.nix Normal file
View file

@ -0,0 +1,20 @@
{ lib
, stdenv
, hyprland
}:
stdenv.mkDerivation {
pname = "hycov";
version = "0.2";
src = ./.;
inherit (hyprland) nativeBuildInputs;
buildInputs = [ hyprland ] ++ hyprland.buildInputs;
meta = with lib; {
homepage = "https://github.com/DreamMaoMao/hycov";
description = "clients overview for hyprland plugin";
license = licenses.mit;
platforms = platforms.linux;
};
}

424
flake.lock generated Normal file
View file

@ -0,0 +1,424 @@
{
"nodes": {
"aquamarine": {
"inputs": {
"hyprutils": [
"hyprland",
"hyprutils"
],
"hyprwayland-scanner": [
"hyprland",
"hyprwayland-scanner"
],
"nixpkgs": [
"hyprland",
"nixpkgs"
],
"systems": [
"hyprland",
"systems"
]
},
"locked": {
"lastModified": 1734906446,
"narHash": "sha256-6OWluVE2A8xi+8V3jN9KA72RCgJjYdyyuLBUjxZ2q2U=",
"owner": "hyprwm",
"repo": "aquamarine",
"rev": "eecb74dc79bb6752a2a507e6edee3042390a6091",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "aquamarine",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"hyprland",
"pre-commit-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"hyprcursor": {
"inputs": {
"hyprlang": [
"hyprland",
"hyprlang"
],
"nixpkgs": [
"hyprland",
"nixpkgs"
],
"systems": [
"hyprland",
"systems"
]
},
"locked": {
"lastModified": 1734906540,
"narHash": "sha256-vQ/L9hZFezC0LquLo4TWXkyniWtYBlFHAKIsDc7PYJE=",
"owner": "hyprwm",
"repo": "hyprcursor",
"rev": "69270ba8f057d55b0e6c2dca0e165d652856e613",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprcursor",
"type": "github"
}
},
"hyprgraphics": {
"inputs": {
"hyprutils": [
"hyprland",
"hyprutils"
],
"nixpkgs": [
"hyprland",
"nixpkgs"
],
"systems": [
"hyprland",
"systems"
]
},
"locked": {
"lastModified": 1734906236,
"narHash": "sha256-vH/ysV2ONGQgYZPtcJKwc8jJivzyVxru2aaOxC20ZOE=",
"owner": "hyprwm",
"repo": "hyprgraphics",
"rev": "6dea3fba08fd704dd624b6d4b261638fb4003c9c",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprgraphics",
"type": "github"
}
},
"hyprland": {
"inputs": {
"aquamarine": "aquamarine",
"hyprcursor": "hyprcursor",
"hyprgraphics": "hyprgraphics",
"hyprland-protocols": "hyprland-protocols",
"hyprland-qtutils": "hyprland-qtutils",
"hyprlang": "hyprlang",
"hyprutils": "hyprutils",
"hyprwayland-scanner": "hyprwayland-scanner",
"nixpkgs": "nixpkgs",
"pre-commit-hooks": "pre-commit-hooks",
"systems": "systems",
"xdph": "xdph"
},
"locked": {
"lastModified": 1735585949,
"narHash": "sha256-vCGG4tGMvzCzz+ZIsiNtpoFW9+f+itYLTAVW41qk/Hk=",
"ref": "refs/heads/main",
"rev": "1989b0049f7fb714a2417dfb14d6b4f3d2a079d3",
"revCount": 5607,
"submodules": true,
"type": "git",
"url": "https://github.com/hyprwm/Hyprland"
},
"original": {
"submodules": true,
"type": "git",
"url": "https://github.com/hyprwm/Hyprland"
}
},
"hyprland-protocols": {
"inputs": {
"nixpkgs": [
"hyprland",
"nixpkgs"
],
"systems": [
"hyprland",
"systems"
]
},
"locked": {
"lastModified": 1728345020,
"narHash": "sha256-xGbkc7U/Roe0/Cv3iKlzijIaFBNguasI31ynL2IlEoM=",
"owner": "hyprwm",
"repo": "hyprland-protocols",
"rev": "a7c183800e74f337753de186522b9017a07a8cee",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprland-protocols",
"type": "github"
}
},
"hyprland-qtutils": {
"inputs": {
"hyprutils": [
"hyprland",
"hyprutils"
],
"nixpkgs": [
"hyprland",
"nixpkgs"
],
"systems": [
"hyprland",
"systems"
]
},
"locked": {
"lastModified": 1734906472,
"narHash": "sha256-pWPRv/GA/X/iAwoE6gMNUqn/ZeJX1IeLPRpZI0tTPK0=",
"owner": "hyprwm",
"repo": "hyprland-qtutils",
"rev": "c77109d7e1ddbcdb87cafd32ce411f76328ae152",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprland-qtutils",
"type": "github"
}
},
"hyprlang": {
"inputs": {
"hyprutils": [
"hyprland",
"hyprutils"
],
"nixpkgs": [
"hyprland",
"nixpkgs"
],
"systems": [
"hyprland",
"systems"
]
},
"locked": {
"lastModified": 1734906259,
"narHash": "sha256-P79t/7HbACO4/PuJBroGpTptvCWJtXTv+gWsF+sM6MI=",
"owner": "hyprwm",
"repo": "hyprlang",
"rev": "0404833ea18d543df44df935ebf1b497310eb046",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprlang",
"type": "github"
}
},
"hyprutils": {
"inputs": {
"nixpkgs": [
"hyprland",
"nixpkgs"
],
"systems": [
"hyprland",
"systems"
]
},
"locked": {
"lastModified": 1735316583,
"narHash": "sha256-AiiUwHWHfEdpFzXy7l1x3zInCUa1xcRMrbZ1XRSkzwU=",
"owner": "hyprwm",
"repo": "hyprutils",
"rev": "8f15d45b120b33712f6db477fe5ffb18034d0ea8",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprutils",
"type": "github"
}
},
"hyprwayland-scanner": {
"inputs": {
"nixpkgs": [
"hyprland",
"nixpkgs"
],
"systems": [
"hyprland",
"systems"
]
},
"locked": {
"lastModified": 1734793513,
"narHash": "sha256-rrrHcXapXJvGFqX+L/Bb0182L25jofAZ0fm1FInvrTQ=",
"owner": "hyprwm",
"repo": "hyprwayland-scanner",
"rev": "4d7367b6eee87397e2dbca2e78078dd0a4ef4c61",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprwayland-scanner",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1735291276,
"narHash": "sha256-NYVcA06+blsLG6wpAbSPTCyLvxD/92Hy4vlY9WxFI1M=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "634fd46801442d760e09493a794c4f15db2d0cbb",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1730741070,
"narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d063c1dd113c91ab27959ba540c0d9753409edf3",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": [
"hyprland",
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1734797603,
"narHash": "sha256-ulZN7ps8nBV31SE+dwkDvKIzvN6hroRY8sYOT0w+E28=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "f0f0dc4920a903c3e08f5bdb9246bb572fcae498",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"root": {
"inputs": {
"hyprland": "hyprland",
"nixpkgs": [
"hyprland",
"nixpkgs"
],
"systems": [
"hyprland",
"systems"
]
}
},
"systems": {
"locked": {
"lastModified": 1689347949,
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
"owner": "nix-systems",
"repo": "default-linux",
"rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default-linux",
"type": "github"
}
},
"xdph": {
"inputs": {
"hyprland-protocols": [
"hyprland",
"hyprland-protocols"
],
"hyprlang": [
"hyprland",
"hyprlang"
],
"hyprutils": [
"hyprland",
"hyprutils"
],
"hyprwayland-scanner": [
"hyprland",
"hyprwayland-scanner"
],
"nixpkgs": [
"hyprland",
"nixpkgs"
],
"systems": [
"hyprland",
"systems"
]
},
"locked": {
"lastModified": 1734907020,
"narHash": "sha256-p6HxwpRKVl1KIiY5xrJdjcEeK3pbmc///UOyV6QER+w=",
"owner": "hyprwm",
"repo": "xdg-desktop-portal-hyprland",
"rev": "d7f18dda5e511749fa1511185db3536208fb1a63",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "xdg-desktop-portal-hyprland",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

55
flake.nix Normal file
View file

@ -0,0 +1,55 @@
{
description = "Hyprland Plugins (Hycov)";
inputs = {
hyprland.url = "git+https://github.com/hyprwm/Hyprland?submodules=1";
nixpkgs.follows = "hyprland/nixpkgs";
systems.follows = "hyprland/systems";
};
outputs =
{ self
, hyprland
, nixpkgs
, systems
}:
let
inherit (nixpkgs) lib;
withPkgsFor = fn: lib.genAttrs (import systems) (system:
let
pkgs = import nixpkgs {
localSystem.system = system;
overlays = [
hyprland.overlays.hyprland-packages
self.overlays.default
];
};
in
fn system pkgs);
in
{
overlays = {
default = self.overlays.hycov;
hycov = final: prev: {
hyprlandPlugins = prev.hyprlandPlugins or {} // {
hycov = final.callPackage ./default.nix {
stdenv = final.gcc14Stdenv;
};
};
};
};
packages = withPkgsFor (system: pkgs: {
default = self.packages.${system}.hycov;
inherit (pkgs.hyprlandPlugins) hycov;
});
devShells = withPkgsFor (system: pkgs: {
default = pkgs.mkShell.override { stdenv = pkgs.gcc14Stdenv; } {
name = "hyprland-plugins";
# buildInputs = [ pkgs.hyprland ];
inputsFrom = [ pkgs.hycov ];
};
});
};
}

26
hyprpm.toml Normal file
View file

@ -0,0 +1,26 @@
[repository]
name = "hycov"
authors = ["DreamMaoMao"]
commit_pins = [
["03ebbe18ed8517ee22591eac82cd54322f42cb7d", "b14e35d8c08ca2e946b7249f9024c2b1787398e6"],
["cbadf3e3f31ab5ad5d192daac5f2ca930d08b8fb", "c5c314d2e4ca44011cc9f64f13ff9071eda751dd"],
["84ab8d11e8951a6551d1e1bf87796a8589da6d47", "06c3ec69171e1937a889bc20b84bbc33a70b4400"],
["13f6f0b923ff3ec94a3bec886c28b90402ceef91", "2c88820384c78b13ce92ef61fa0822369c9c6ecf"],
["1c460e98f870676b15871fe4e5bfeb1a32a3d6d8", "7d48cff8364a7b6ae52a3472ad1dfe01b9f728ae"],
["e93fbd7c4f991cb8ef03e433ccc4d43587923e15", "05fb15703d07a372b14a3260a337de13d1c16b91"],
["fe7b748eb668136dd0558b7c8279bfcd7ab4d759", "05fb15703d07a372b14a3260a337de13d1c16b91"],
["cba1ade848feac44b2eda677503900639581c3f4", "89fc3dc62d4aca3745d2b1aa6ab81dab541c232c"],
["ea2501d4556f84d3de86a4ae2f4b22a474555b9f", "7b1f51d005c5f477c763f1031b807787f2f6b449"],
["9e781040d9067c2711ec2e9f5b47b76ef70762b3","a1c4287c2ebb5f5596dc53571993725be9d63cb0"],
["918d8340afd652b011b937d29d5eea0be08467f5","a1c4287c2ebb5f5596dc53571993725be9d63cb0"]
]
[hycov]
description = "hyprland overview mode"
authors = ["DreamMaoMao"]
output = "build/libhycov.so"
build = [
"cmake -DCMAKE_BUILD_TYPE=Release -B build",
"cmake --build build -j $(nproc)"
]

5
install.sh Normal file
View file

@ -0,0 +1,5 @@
sudo rm build/* -rf
cmake -DCMAKE_BUILD_TYPE=Debug -B build
cmake --build build -j $(nproc)
sudo cp build/libhycov.so /usr/lib

171
src/DwindleSwitcher.cpp Normal file
View file

@ -0,0 +1,171 @@
#include "DwindleSwitcher.hpp"
#include <cassert>
#define private public
#include "src/managers/LayoutManager.hpp"
#undef private
#include "src/Compositor.hpp"
#include "src/helpers/Monitor.hpp"
#include <set>
struct DwindleSwitcher::PrivateContext {
struct AdditionalState {
bool fullscreen = false;
};
CHyprDwindleLayout* layout = nullptr;
std::map<PHLWINDOW, AdditionalState> additional;
std::list<SDwindleNodeData> dwindleNodes;
};
bool canRestore(std::list<SDwindleNodeData>& target, std::list<SDwindleNodeData>& backup);
auto findNodePointerByData(std::list<SDwindleNodeData>& nodes, SDwindleNodeData *node) -> SDwindleNodeData *;
void remapPointers(std::list<SDwindleNodeData>& source, std::list<SDwindleNodeData>& target);
std::string DwindleSwitcher::getName() const { return "dwindle"; }
DwindleSwitcher::DwindleSwitcher() {
context = new PrivateContext();
}
DwindleSwitcher::~DwindleSwitcher() {
delete context;
}
void DwindleSwitcher::onEnterOverviewBefore() {
context->layout = dynamic_cast<CHyprDwindleLayout*>(g_pLayoutManager->getCurrentLayout());
if (!context->layout) return;
remapPointers(context->layout->m_dwindleNodesData, context->dwindleNodes);
for (const auto& window : g_pCompositor->m_windows) {
context->additional[window] = {};
if (window->isFullscreen()) {
context->additional[window].fullscreen = true;
}
}
LayoutSwitcher::onEnterOverviewBefore();
}
void DwindleSwitcher::onEnterOverviewAfter() {}
void DwindleSwitcher::onLeaveOverviewBefore() {}
void DwindleSwitcher::onLeaveOverviewAfter() {
if (!context->layout) return;
if (canRestore(context->layout->m_dwindleNodesData, context->dwindleNodes)) {
remapPointers(context->dwindleNodes, context->layout->m_dwindleNodesData);
for (auto& node : context->layout->m_dwindleNodesData) {
context->layout->applyNodeDataToWindow(&node);
}
}
for (auto& [window, data] : context->additional) {
if (!window)
continue;
if (data.fullscreen) {
// if selected window is in the same workspace as fullscreen window then don't restore fullscreen
if (g_pCompositor->m_lastWindow != window) {
if (g_pCompositor->m_lastWindow->m_workspace == window->m_workspace) {
continue;
}
}
g_pCompositor->setWindowFullscreenState(window, {FSMODE_FULLSCREEN, FSMODE_FULLSCREEN});
}
}
context->layout = nullptr;
}
bool canRestore(std::list<SDwindleNodeData>& target, std::list<SDwindleNodeData>& backup) {
std::set<long> rootsTarget;
std::set<long> rootsBackup;
std::set<PHLWINDOW> windowsTarget;
std::set<PHLWINDOW> windowsBackup;
for (auto& node : backup) {
if (!node.isNode) {
if (node.pWindow.expired())
return false;
windowsBackup.insert(node.pWindow.lock());
}
if (!node.pParent)
rootsBackup.insert(node.workspaceID);
}
for (auto& node : target) {
if (!node.isNode) {
windowsTarget.insert(node.pWindow.lock());
}
if (!node.pParent)
rootsTarget.insert(node.workspaceID);
}
if (rootsBackup != rootsTarget) {
return false;
}
return windowsTarget == windowsBackup;
}
auto findNodePointerByData(std::list<SDwindleNodeData>& nodes, SDwindleNodeData *node) -> SDwindleNodeData * {
for (auto &iter : nodes) {
if (iter.pWindow) {
if (node->pWindow == iter.pWindow) {
return &iter;
}
} else {
if (node->children == iter.children) {
return &iter;
}
}
}
return nullptr;
}
void remapPointers(std::list<SDwindleNodeData>& source, std::list<SDwindleNodeData>& target) {
target.clear();
for (auto node : source) {
target.push_back(node);
}
std::map<SDwindleNodeData*, SDwindleNodeData*> targetToSourceMap;
for (auto& node : target) {
if (node.pParent) {
targetToSourceMap[node.pParent] = findNodePointerByData(target, node.pParent);
}
if (node.children[0]) {
targetToSourceMap[node.children[0]] = findNodePointerByData(target, node.children[0]);
}
if (node.children[1]) {
targetToSourceMap[node.children[1]] = findNodePointerByData(target, node.children[1]);
}
}
for (auto& node : target) {
if (node.pParent) {
node.pParent = targetToSourceMap[node.pParent];
}
if (node.children[0]) {
node.children[0] = targetToSourceMap[node.children[0]];
}
if (node.children[1]) {
node.children[1] = targetToSourceMap[node.children[1]];
}
}
assert(!std::any_of(targetToSourceMap.begin(), targetToSourceMap.end(), [](auto& pair){ return !pair.second;}));
}

23
src/DwindleSwitcher.hpp Normal file
View file

@ -0,0 +1,23 @@
#pragma once
#include "OverviewManager.hpp"
struct DwindleSwitcher : public LayoutSwitcher {
public:
struct PrivateContext;
public:
DwindleSwitcher();
~DwindleSwitcher();
[[nodiscard]] std::string getName() const;
void onEnterOverviewBefore() override;
void onEnterOverviewAfter() override;
void onLeaveOverviewBefore() override;
void onLeaveOverviewAfter() override;
private:
PrivateContext* context = nullptr;
};

245
src/OverviewLayout.cpp Normal file
View file

@ -0,0 +1,245 @@
#include "OverviewLayout.hpp"
#include "src/desktop/Workspace.hpp"
#include "src/helpers/Monitor.hpp"
#include "src/managers/LayoutManager.hpp"
#include "src/render/Renderer.hpp"
#include "src/Compositor.hpp"
std::string OverviewLayout::getLayoutName() { return "grid"; }
OverviewLayout::OverviewWindowNode::OverviewWindowNode(PHLWINDOW window_) {
window = std::move(window_);
savedState.monitorId = window->monitorID();
savedState.workspaceId = window->workspaceID();
savedState.workspaceName = g_pCompositor->getWorkspaceByID(savedState.workspaceId)->m_name;
savedState.pos = window->m_position;
savedState.size = window->m_size;
savedState.ratio = (float) (savedState.size.y / savedState.size.x);
savedState.posReal = window->m_realPosition->goal();
savedState.sizeReal = window->m_realSize->goal();
savedState.floating = window->m_isFloating;
if (!savedState.size.x || !savedState.size.y) {
savedState.size = savedState.sizeReal;
savedState.size.clamp({10, 10});
}
}
OverviewLayout::OverviewLayout(OverviewManager* overviewManager) {
mOverviewManager = overviewManager;
}
void OverviewLayout::onEnable() {
for (auto &window: g_pCompositor->m_windows) {
if (!window->m_isMapped || window->isHidden())
continue;
if (window->m_workspace->m_isSpecialWorkspace) {
continue;
}
onWindowCreatedTiling(window, DIRECTION_DEFAULT);
}
updateWorkspaces();
}
void OverviewLayout::updateWorkspaces() {
std::map<PHLMONITOR, std::set<PHLWORKSPACE>> activeMonitorMap;
for (auto& node : m_windowNodes) {
if (!node.second.window->m_workspace->m_isSpecialWorkspace) {
activeMonitorMap[node.second.window->m_monitor.lock()].insert(node.second.window->m_workspace);
}
}
auto underCursorWorkspace = g_pCompositor->getMonitorFromCursor()->m_activeWorkspace;
for (auto& monitor : activeMonitorMap) {
if (monitor.second.contains(underCursorWorkspace)) {
monitor.first->changeWorkspace(underCursorWorkspace);
} else {
monitor.first->changeWorkspace(*monitor.second.begin());
}
monitor.first->setSpecialWorkspace(nullptr);
}
for (auto& node : m_windowNodes) {
auto monitor = node.second.window->m_monitor;
auto workspace = monitor->m_activeWorkspace;
node.second.window->m_workspace = workspace;
node.second.window->m_monitor = monitor;
node.second.window->m_isFloating = false; // this it needed to disable minSize clamping
}
}
void OverviewLayout::onDisable() {
for (auto& [window, node] : m_windowNodes) {
auto monitor = g_pCompositor->getMonitorFromID(node.savedState.monitorId);
auto workspace = g_pCompositor->getWorkspaceByID(node.savedState.workspaceId);
if (!workspace) {
workspace = g_pCompositor->createNewWorkspace(node.savedState.workspaceId, node.savedState.monitorId, node.savedState.workspaceName);
}
window->m_workspace = workspace;
window->m_monitor = monitor;
window->m_position = node.savedState.pos;
window->m_size = node.savedState.size;
*window->m_realPosition = node.savedState.posReal;
*window->m_realSize = node.savedState.sizeReal;
window->m_isFloating = node.savedState.floating;
}
m_windowNodes.clear();
}
void OverviewLayout::onWindowRemoved(PHLWINDOW pWindow) {
onWindowRemovedTiling(pWindow);
}
void OverviewLayout::onWindowRemovedTiling(PHLWINDOW pWindow) {
m_windowNodes.erase(pWindow.get());
updateLayout();
}
void OverviewLayout::updateLayout() {
for (auto &monitor: g_pCompositor->m_monitors) {
recalculateMonitor(monitor->m_id);
}
}
void OverviewLayout::onWindowCreatedTiling(PHLWINDOW window, eDirection direction) {
if (m_windowNodes.contains(window.get())) return;
auto newNode = OverviewWindowNode(window);
m_windowNodes.insert({ window.get(), newNode });
updateLayout();
}
void OverviewLayout::recalculateMonitor(const MONITORID &monitorId) {
auto monitor = g_pCompositor->getMonitorFromID(monitorId);
if (!monitor || !monitor->m_activeWorkspace) {
return;
}
std::vector<OverviewWindowNode*> nodes;
for (auto& node : m_windowNodes) {
if (node.second.window->monitorID() == monitorId) {
nodes.push_back(&node.second);
}
}
calculateOverviewGrid(nodes, g_pCompositor->getWorkspaceByID(monitor->activeWorkspaceID()));
}
void OverviewLayout::calculateOverviewGrid(const std::vector<OverviewWindowNode*>& windows, const PHLWORKSPACE& workspace) const {
if (windows.empty()) {
return;
}
const auto monitor = g_pCompositor->getMonitorFromID(workspace->m_monitor->m_id);
auto monitorX = (int) (monitor->m_position.x + monitor->m_reservedTopLeft.x);
auto monitorY = (int) (monitor->m_position.y + monitor->m_reservedTopLeft.y);
auto monitorW = (int) (monitor->m_size.x - monitor->m_reservedTopLeft.x);
auto monitorH = (int) (monitor->m_size.y - monitor->m_reservedTopLeft.y);
auto numNodes = (int) windows.size();
// Calculate the integer part of the square root of the number of nodes
int columns = 0;
while (columns <= numNodes / 2) {
if (columns * columns >= numNodes) {
break;
}
columns++;
}
// The number of rows and columns multiplied by the number of nodes must be greater than the number of nodes to fit all the Windows
int rows = (columns && (columns - 1) * columns >= numNodes) ? columns - 1 : columns;
// If the nodes do not exactly fill all rows, the number of Windows in the unfilled rows is
int numLeftOvers = numNodes % columns;
// Calculate the width and height of the layout area based on the number of rows and columns
int height = (int) ((monitorH - 2 * (gapOut) - (rows - 1) * (gapIn)) / rows);
int width = (int) ((monitorW - 2 * (gapOut) - (columns - 1) * (gapIn)) / columns);
int leftOversOffsetX = 0;
if (numLeftOvers) {
leftOversOffsetX = (monitorW - numLeftOvers * width - (numLeftOvers - 1) * gapIn) / 2 - gapOut;
}
for (int i = 0; i < numNodes; i++) {
auto node = windows[i];
int posX = monitorX + (i % columns) * (width + gapIn) + gapOut;
int posY = monitorY + (int) (i / columns) * (height + gapIn) + gapOut;
if (i >= numNodes - numLeftOvers) {
posX += leftOversOffsetX;
}
auto cellPos = Vector2D(posX + border, posY + border);
auto cellSize = Vector2D(width - border * 2, height - border * 2);
auto windowPos = cellPos;
auto windowSize = cellSize;
if (1) {
if (cellSize.x > node->savedState.size.x && cellSize.y > node->savedState.size.y) {
windowPos = cellPos + (cellSize / 2) - (node->savedState.size / 2);
windowSize = node->savedState.size;
} else {
// apply ratio scaling
auto cellRatio = (float) (cellSize.y / cellSize.x);
if (cellRatio < node->savedState.ratio) {
windowSize.x = cellSize.y / node->savedState.ratio;
windowPos.x += (cellSize.x - windowSize.x) / 2;
} else if (cellRatio > node->savedState.ratio) {
windowSize.y = cellSize.x * node->savedState.ratio;
windowPos.y += (cellSize.y - windowSize.y) / 2;
}
}
}
node->window->m_position = windowPos;
*node->window->m_realPosition = windowPos;
node->window->m_size = windowSize;
*node->window->m_realSize = windowSize;
}
}
PHLWINDOW OverviewLayout::getNextWindowCandidate(PHLWINDOW) {
if (!m_windowNodes.empty())
return m_windowNodes.begin()->second.window;
return nullptr;
}
void OverviewLayout::onBeginDragWindow() {
mOverviewManager->leaveOverview();
}
Vector2D OverviewLayout::predictSizeForNewWindowTiled() { return {}; }
bool OverviewLayout::isWindowTiled(PHLWINDOW pWindow) { return true; }
void OverviewLayout::recalculateWindow(PHLWINDOW pWindow) {}
void OverviewLayout::resizeActiveWindow(const Vector2D &pixResize, eRectCorner corner, PHLWINDOW pWindow) {}
void OverviewLayout::fullscreenRequestForWindow( PHLWINDOW pWindow, const eFullscreenMode CURRENT_EFFECTIVE_MODE, const eFullscreenMode EFFECTIVE_MODE) {}
std::any OverviewLayout::layoutMessage(SLayoutMessageHeader header, std::string content) { return ""; }
SWindowRenderLayoutHints OverviewLayout::requestRenderHints(PHLWINDOW pWindow) { return {}; }
void OverviewLayout::switchWindows(PHLWINDOW pWindowA, PHLWINDOW pWindowB) {}
void OverviewLayout::alterSplitRatio(PHLWINDOW pWindow, float delta, bool exact) {}
void OverviewLayout::replaceWindowDataWith(PHLWINDOW from, PHLWINDOW to) {}
void OverviewLayout::moveWindowTo(PHLWINDOW, const std::string &dir, bool silent) {}

82
src/OverviewLayout.hpp Normal file
View file

@ -0,0 +1,82 @@
#pragma once
#include "OverviewManager.hpp"
#include <hyprland/src/layout/IHyprLayout.hpp>
#include <hyprland/src/SharedDefs.hpp>
#include <map>
#include <utility>
class OverviewLayout : public IHyprLayout {
struct WindowState {
Vector2D pos;
Vector2D size;
Vector2D posReal;
Vector2D sizeReal;
long monitorId = 0;
long workspaceId = 0;
std::string workspaceName;
float ratio = 1.f;
bool floating = false;
};
struct OverviewWindowNode {
explicit OverviewWindowNode(PHLWINDOW window_);
bool operator==(const OverviewWindowNode &in) const {
return window == in.window;
}
PHLWINDOW window = nullptr;
WindowState savedState;
};
public:
explicit OverviewLayout(OverviewManager* overviewManager);
void onEnable() override;
void onDisable() override;
public:
void onWindowCreatedTiling(PHLWINDOW, eDirection direction) override;
void onWindowRemovedTiling(PHLWINDOW) override;
void onWindowRemoved(PHLWINDOW) override;
void onBeginDragWindow() override;
bool isWindowTiled(PHLWINDOW) override;
PHLWINDOW getNextWindowCandidate(PHLWINDOW) override;
void recalculateMonitor(const MONITORID &) override;
void recalculateWindow(PHLWINDOW) override;
void resizeActiveWindow(const Vector2D &, eRectCorner corner, PHLWINDOW pWindow) override;
void fullscreenRequestForWindow(PHLWINDOW pWindow, eFullscreenMode CURRENT_EFFECTIVE_MODE, eFullscreenMode EFFECTIVE_MODE) override;
std::any layoutMessage(SLayoutMessageHeader, std::string) override;
SWindowRenderLayoutHints requestRenderHints(PHLWINDOW) override;
void switchWindows(PHLWINDOW, PHLWINDOW) override;
void alterSplitRatio(PHLWINDOW, float, bool) override;
std::string getLayoutName() override;
Vector2D predictSizeForNewWindowTiled() override;
void replaceWindowDataWith(PHLWINDOW from, PHLWINDOW to) override;
void moveWindowTo(PHLWINDOW, const std::string &direction, bool silent) override;
private:
void updateLayout();
void calculateOverviewGrid(const std::vector<OverviewWindowNode*>& windows, const PHLWORKSPACE& workspace) const;
void updateWorkspaces();
public:
std::map<CWindow*, OverviewWindowNode> m_windowNodes;
const int border = 5;
const int gapIn = 20;
const int gapOut = 150;
private:
OverviewManager* mOverviewManager = nullptr;
};

77
src/OverviewManager.cpp Normal file
View file

@ -0,0 +1,77 @@
#include "OverviewManager.hpp"
#include <utility>
#include "src/Compositor.hpp"
#include "src/managers/LayoutManager.hpp"
void LayoutSwitcher::onEnterOverviewBefore() {
for (auto& window : g_pCompositor->m_windows) {
g_pCompositor->setWindowFullscreenState(window, SFullscreenState());
}
}
OverviewManager::OverviewManager() {
registerSwitcher(std::make_shared<LayoutSwitcher>());
}
void OverviewManager::registerSwitcher(const std::shared_ptr<LayoutSwitcher> &switcher) {
mSwitchers[switcher->getName()] = switcher;
}
void OverviewManager::setCurrentSwitcher(const std::string& name) {
if (mSwitchers.contains(name)) {
mCurrentSwitcher = mSwitchers[name];
} else {
mCurrentSwitcher = mSwitchers["all"];
}
}
void OverviewManager::toggle() {
auto currentName = g_pLayoutManager->getCurrentLayout()->getLayoutName();
auto overviewName = mOverviewLayout->getLayoutName();
if (currentName == overviewName) {
leaveOverview();
} else {
enterOverview();
}
}
void OverviewManager::leaveOverview() {
auto currentName = g_pLayoutManager->getCurrentLayout()->getLayoutName();
auto overviewName = mOverviewLayout->getLayoutName();
if (currentName != overviewName) return;
mCurrentSwitcher->onLeaveOverviewBefore();
g_pLayoutManager->switchToLayout(mFallbackLayout->getLayoutName());
mCurrentSwitcher->onLeaveOverviewAfter();
onLastActiveWindow(g_pCompositor->m_lastWindow.lock());
}
void OverviewManager::enterOverview() {
auto currentName = g_pLayoutManager->getCurrentLayout()->getLayoutName();
auto overviewName = mOverviewLayout->getLayoutName();
if (currentName == overviewName) return;
mFallbackLayout = g_pLayoutManager->getCurrentLayout();
setCurrentSwitcher(mFallbackLayout->getLayoutName());
mCurrentSwitcher->onEnterOverviewBefore();
g_pLayoutManager->switchToLayout(mOverviewLayout->getLayoutName());
mCurrentSwitcher->onEnterOverviewAfter();
}
void OverviewManager::onLastActiveWindow(PHLWINDOW windowToFocus) {
g_pCompositor->changeWindowZOrder(windowToFocus, true);
g_pCompositor->focusWindow(std::move(windowToFocus));
}
void OverviewManager::setOverviewLayout(IHyprLayout *layout) {
mOverviewLayout = layout;
}

40
src/OverviewManager.hpp Normal file
View file

@ -0,0 +1,40 @@
#pragma once
#include "src/layout/IHyprLayout.hpp"
#include <map>
#include <memory>
struct LayoutSwitcher {
virtual std::string getName() const { return "all"; }
virtual void onEnterOverviewBefore();
virtual void onEnterOverviewAfter() {}
virtual void onLeaveOverviewBefore() {}
virtual void onLeaveOverviewAfter() {}
virtual ~LayoutSwitcher() = default;
};
class OverviewManager {
public:
OverviewManager();
void setOverviewLayout(IHyprLayout* layout);
void registerSwitcher(const std::shared_ptr<LayoutSwitcher>& switcher);
void toggle();
public:
void leaveOverview();
void enterOverview();
static void onLastActiveWindow(PHLWINDOW windowToFocus);
private:
void setCurrentSwitcher(const std::string &name);
private:
std::shared_ptr<LayoutSwitcher> mCurrentSwitcher = nullptr;
std::map<std::string, std::shared_ptr<LayoutSwitcher>> mSwitchers;
IHyprLayout* mFallbackLayout = nullptr;
IHyprLayout* mOverviewLayout = nullptr;
};

30
src/Plugin.cpp Normal file
View file

@ -0,0 +1,30 @@
#include "Plugin.hpp"
#include "DwindleSwitcher.hpp"
#include <src/managers/LayoutManager.hpp>
PluginState::PluginState(HANDLE _handle) {
handle = _handle;
manager = new OverviewManager();
layout = new OverviewLayout(manager);
}
PluginState::~PluginState() {
delete layout;
delete manager;
}
void PluginState::bind() {
manager->setOverviewLayout(layout);
manager->registerSwitcher(std::make_shared<DwindleSwitcher>());
HyprlandAPI::addLayout(handle, layout->getLayoutName(), layout);
HyprlandAPI::addDispatcherV2(handle, "overview:toggle", [this](const std::string&){
manager->toggle();
return SDispatchResult();
});
}

17
src/Plugin.hpp Normal file
View file

@ -0,0 +1,17 @@
#pragma once
#include "OverviewLayout.hpp"
#include "OverviewManager.hpp"
#include <hyprland/src/plugins/PluginAPI.hpp>
struct PluginState {
explicit PluginState(HANDLE handle);
~PluginState();
HANDLE handle = nullptr;
OverviewLayout* layout = nullptr;
OverviewManager* manager = nullptr;
void bind();
};

37
src/main.cpp Normal file
View file

@ -0,0 +1,37 @@
#include "Plugin.hpp"
#include <src/devices/IPointer.hpp>
#include <src/managers/input/InputManager.hpp>
typedef void (*CInputManager_onMouseButton)(void* , IPointer::SButtonEvent e);
static PluginState* gPluginState = nullptr;
static CFunctionHook* mouseHook = nullptr;
APICALL EXPORT std::string PLUGIN_API_VERSION() {
return HYPRLAND_API_VERSION;
}
static void mouseButtonHook(void* self, IPointer::SButtonEvent buttonEvent) {
if (buttonEvent.button == BTN_LEFT ) {
gPluginState->manager->leaveOverview();
}
(*(CInputManager_onMouseButton)mouseHook->m_original)(self, buttonEvent);
}
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
gPluginState = new PluginState(handle);
gPluginState->bind();
mouseHook = HyprlandAPI::createFunctionHook(gPluginState->handle, (void*)&CInputManager::onMouseButton, (void*)&mouseButtonHook);
mouseHook->hook();
return {"overview", "overview mode", "ilusha", "0.0"};
}
APICALL EXPORT void PLUGIN_EXIT() {
delete gPluginState;
}