initial
This commit is contained in:
commit
1210b5b7be
18 changed files with 1661 additions and 0 deletions
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
/result
|
||||
/build
|
||||
/.vscode
|
||||
/.cache
|
||||
*build*
|
||||
*rel*
|
||||
.idea
|
||||
29
CMakeLists.txt
Normal file
29
CMakeLists.txt
Normal 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
21
LICENSE
Normal 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
352
README.md
Normal 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>
|
||||
|
||||

|
||||

|
||||
|
||||
</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
20
default.nix
Normal 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
424
flake.lock
generated
Normal 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
55
flake.nix
Normal 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
26
hyprpm.toml
Normal 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
5
install.sh
Normal 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
171
src/DwindleSwitcher.cpp
Normal 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
23
src/DwindleSwitcher.hpp
Normal 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
245
src/OverviewLayout.cpp
Normal 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
82
src/OverviewLayout.hpp
Normal 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
77
src/OverviewManager.cpp
Normal 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
40
src/OverviewManager.hpp
Normal 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
30
src/Plugin.cpp
Normal 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
17
src/Plugin.hpp
Normal 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
37
src/main.cpp
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue