diff --git a/.gitignore b/.gitignore index 30de1b3..47f3855 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,3 @@ meson-out meson-info meson-logs meson-private - -Hyprspace.conf -log.txt \ No newline at end of file diff --git a/Hyprspace.lua b/Hyprspace.lua deleted file mode 100644 index 33628b8..0000000 --- a/Hyprspace.lua +++ /dev/null @@ -1,211 +0,0 @@ -local M = {} -local HOME = os.getenv("HOME") or "" - -local MATUGEN_PATH = HOME .. "/.config/matugen/generated/hyprland-colors.lua" -local pluginPath = os.getenv("HYPRSPACE_PLUGIN_PATH") - -local function file_exists(path) - if type(path) ~= "string" or path == "" then - return false - end - - local file = io.open(path, "r") - if not file then - return false - end - - file:close() - return true -end - -local function shell_quote(value) - return "'" .. tostring(value):gsub("'", "'\\''") .. "'" -end - -local function load_lua_file(path) - local env = {} - local chunk = loadfile(path, "t", env) - if not chunk then - return {} - end - - if not pcall(chunk) then - return {} - end - - return env -end - -local function rgba_to_aarrggbb(value, fallback) - if type(value) ~= "string" then - return fallback - end - - local hex = value:match("^rgba%((%x%x%x%x%x%x%x%x)%)$") - if not hex then - return fallback - end - - local rr = hex:sub(1, 2) - local gg = hex:sub(3, 4) - local bb = hex:sub(5, 6) - local aa = hex:sub(7, 8) - return tonumber("0x" .. aa .. rr .. gg .. bb) -end - -local function color_with_alpha(value, alpha, fallback) - local base = rgba_to_aarrggbb(value, fallback) - if not base then - return fallback - end - - local rgb = base & 0x00ffffff - return ((alpha & 0xff) << 24) | rgb -end - -local function matugen_colors() - return load_lua_file(MATUGEN_PATH) -end - -local function build_config() - local colors = matugen_colors() - - return { - plugin = { - hyprspace = { - -- Theme colors are derived from the current matugen palette, but - -- each slot keeps an explicit fallback so the overview remains - -- usable even when the generated file is missing. - panel_color = color_with_alpha(colors.surface_container_high or colors.surface, 0xd8, 0xd8261d20), - panel_border_color = color_with_alpha(colors.primary or colors.outline, 0x88, 0x88ffb0cf), - workspace_active_background = color_with_alpha(colors.surface_container_highest or colors.surface_container_high, 0xb8, 0xb83c3235), - workspace_inactive_background = color_with_alpha(colors.surface_container or colors.surface, 0xde, 0xde261d20), - workspace_active_border = color_with_alpha(colors.primary or colors.on_surface, 0xb0, 0xb0ffb0cf), - workspace_inactive_border = color_with_alpha(colors.outline_variant or colors.outline, 0x55, 0x55504348), - - -- Layout tuning: - -- Keep the overview compact and reserve only the Waybar strip. - -- `panel_height` is the visible overview height. - -- `reserved_area` is the top gap already occupied by Waybar. - panel_height = 220, - panel_border_width = 2, - workspace_margin = 10, - reserved_area = 35, - workspace_border_size = 1, - - adaptive_height = false, - center_aligned = true, - on_bottom = false, - hide_background_layers = false, - hide_top_layers = false, - hide_overlay_layers = false, - draw_active_workspace = true, - hide_real_layers = false, - affect_strut = false, - - override_gaps = true, - gaps_in = 20, - gaps_out = 60, - - auto_drag = true, - auto_scroll = true, - exit_on_click = true, - switch_on_drop = false, - exit_on_switch = false, - show_new_workspace = true, - show_empty_workspace = true, - show_special_workspace = false, - - disable_gestures = false, - reverse_swipe = false, - swipe_fingers = 3, - swipe_distance = 300, - swipe_force_speed = 30, - swipe_cancel_ratio = 0.5, - swipe_threshold = 10.0, - swipe_closed_padding = 10.0, - workspace_scroll_speed = 2.0, - - disable_blur = false, - override_anim_speed = 0.0, - drag_alpha = 0.2, - exit_key = "Escape", - click_release_threshold_ms = 200, - }, - }, - } -end - -local function plugin_loaded() - return hl.plugin and hl.plugin.Hyprspace and type(hl.plugin.Hyprspace.overview) == "function" -end - -local function ensure_plugin_loaded() - if plugin_loaded() then - return true - end - - -- hyprpm restores plugins itself. Only fall back to a manual binary path - -- when one was explicitly provided. - if not file_exists(pluginPath) then - return false - end - - hl.exec_cmd("hyprctl plugin load " .. shell_quote(pluginPath)) - return plugin_loaded() -end - -function M.set_plugin_path(path) - pluginPath = path -end - -function M.apply_config() - if not plugin_loaded() then - return false - end - - hl.config(build_config()) - return true -end - -function M.reload() - M.apply_config() - hl.exec_cmd("hyprctl reload") -end - -function M.overview(action) - if not plugin_loaded() then - return false - end - - M.apply_config() - hl.plugin.Hyprspace.overview(action or "toggle") - return true -end - -function M.toggle() - return M.overview("toggle") -end - -function M.setup(opts) - if type(opts) == "table" and type(opts.plugin_path) == "string" then - pluginPath = opts.plugin_path - end - - -- Startup path: - -- 1. hyprpm-managed plugins are already loaded by Hyprland - -- 2. local/manual installs can opt-in by passing `plugin_path` - -- 3. apply the latest Lua config once the plugin is available - hl.on("hyprland.start", function() - ensure_plugin_loaded() - M.apply_config() - end) - - -- Config reload path: - -- if the plugin is already present, refresh its settings immediately. - if plugin_loaded() then - M.apply_config() - end -end - -return M diff --git a/Makefile b/Makefile index 6e7276b..f9ff52c 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ CXXFLAGS=-shared -fPIC --no-gnu-unique -Wall -g -DWLR_USE_UNSTABLE -std=c++2b -O2 -INCLUDES = `pkg-config --cflags pixman-1 libdrm hyprland pangocairo libinput libudev wayland-server xkbcommon` +INCLUDES = `pkg-config --cflags pixman-1 libdrm hyprland pangocairo libinput libudev wayland-server xkbcommon lua` SRC = $(wildcard src/*.cpp) TARGET = Hyprspace.so PREFIX ?= /usr/local diff --git a/README.md b/README.md index c53693b..9201aef 100644 --- a/README.md +++ b/README.md @@ -1,319 +1,218 @@ # Hyprspace -Hyprspace is a Hyprland overview / workspace-expo plugin. It renders a top or bottom workspace strip, shows live workspace thumbnails, and lets you switch or move windows between workspaces. - -This fork is adapted for current Hyprland with Lua-based configuration instead of the old `.conf` / Hyprlang plugin config flow. - -## Features - -- Live workspace overview with window thumbnails -- Multi-monitor support -- Mouse drag/drop between workspaces -- Optional gesture support -- Lua-native configuration -- `hyprpm` install support - -## Requirements - -- Hyprland with plugin support -- Hyprland development headers / pkg-config metadata for manual builds -- A recent C++ compiler with C++23 support +A plugin for Hyprland that implements a workspace overview feature similar to that of KDE Plasma, GNOME and macOS, aimed to provide a efficient way of workspace and window management. + +> [!NOTE] +> This plugin is still maintained, by combined efforts of me and all the awesome contributors. However, I do not have as much time that I could spend on this plugin as before, and Hyprland is a rapidly changing codebase. Therefore, at this time, I could not guarantee that new issues could be resolved promptly. I appreciate your acknowledgement and support for this project! +> +> P.S.: I could recommend giving [niri](https://github.com/YaLTeR/niri) a try for anyone who is considering an alternative to Hyprland. It is a scrolling window manager that offers a great built-in overview feature that allows dragging windows in-between workspaces just like this plugin does. It also has better workspace management as it cleans up empty workspaces like GNOME does. + +https://github.com/KZDKM/Hyprspace/assets/41317840/ed1a585a-30d5-4a79-a6da-8cc0713828f9 + +### [Jump to installation](#installation) + +## Plugin Compatibility +- [x] [hyprsplit](https://github.com/shezdy/hyprsplit) (tested, explicit support) +- [x] [split-monitor-workspaces](https://github.com/Duckonaut/split-monitor-workspaces) (tested, explicit support) +- [x] [hyprexpo](https://github.com/hyprwm/hyprland-plugins/tree/main/hyprexpo) (tested, minor bugs) +- [x] Any layout plugin (except ones that override Hyprland's workspace management) + +## Roadmap +- [x] Overview interface + - [x] Workspace minimap + - [x] Workspace display +- [ ] Mouse controls + - [x] Moving window between workspaces + - [x] Creating new workspaces + - [ ] Dragging windows between workspace views +- [ ] Configurability + - [ ] Styling + - [x] Panel background + - [x] Workspace background & border + - [x] Panel on Bottom + - [ ] Vertical layout (on left / right) + - [x] Panel top padding (reserved for bar / notch) + - [x] Panel border (color / thickness) + - [ ] Unique styling for special workspaces + - [x] Behavior + - [x] Autodrag windows + - [x] Autoscroll workspaces + - [x] Responsive workspace switching + - [x] Responsive exiting + - [x] Exit on click / switch + - [x] Exit with escape key + - [x] Blacklisting workspaces + - [x] Show / hide new workspace and empty workspaces + - [x] Show / hide special workspace (#11) +- [x] Animation support +- [x] Multi-monitor support (tested) +- [x] Monitor scaling support (tested) +- [x] aarch64 support (No function hook used) +- [x] Touchpad & gesture support + - [x] Workspace swipe (#9) + - [x] Scrolling through workspace panel + - [x] Swipe to open ## Installation -### Manual install - -Build the plugin from the repo root: +### Manual -```bash -cd ~/.config/Hyprspace -make all +To build, have hyprland headers installed and under the repo directory do: ``` - -That produces: - -```text -~/.config/Hyprspace/Hyprspace.so -``` - -Load it manually: - -```bash -hyprctl plugin load ~/.config/Hyprspace/Hyprspace.so +make all ``` +Then use `hyprctl plugin load` followed by the absolute path to the `.so` file to load, you could add this to your `exec-once` to load the plugin on startup -To unload it: - -```bash -hyprctl plugin unload ~/.config/Hyprspace/Hyprspace.so +### Hyprpm ``` - -### `hyprpm` install - -If this repo is pushed to Git and the updated [hyprpm.toml](./hyprpm.toml) is included, install it with: - -```bash -hyprpm add https://github.com/0xl30/Hyprspace.git +hyprpm add https://github.com/KZDKM/Hyprspace hyprpm enable Hyprspace ``` -Examples: - -```bash -hyprpm add https://github.com/0xl30/Hyprspace.git -hyprpm enable Hyprspace +### Nix +Refer to the [Hyprland wiki](https://wiki.hyprland.org/Nix/Hyprland-on-Home-Manager/#plugins) on plugins, but your flake might look like this: +```nix +{ + inputs = { + # Hyprland is **such** eye candy + hyprland = { + type = "git"; + url = "https://github.com/hyprwm/Hyprland"; + submodules = true; + inputs.nixpkgs.follows = "nixpkgs"; + }; + Hyprspace = { + url = "github:KZDKM/Hyprspace"; + + # Hyprspace uses latest Hyprland. We declare this to keep them in sync. + inputs.hyprland.follows = "hyprland"; + }; + }; + +... # your normal setup with hyprland + + wayland.windowManager.hyprland.plugins = [ + # ... whatever + inputs.Hyprspace.packages.${pkgs.system}.Hyprspace + ]; +} ``` -Notes: - -- `hyprpm` matches your Hyprland commit against `commit_pins` in `hyprpm.toml`. -- For Hyprland `v0.55.2`, this repo already includes a matching pin. -- After updating plugin code, commit and push before expecting `hyprpm` to fetch the new version. - -## Lua setup +## Usage -### 1. Plugin helper +### Opening Overview +- Bind the `overview:toggle` or perform a workspace swipe vertically would open / close the panel. +### Interaction +- Window management: + - Click on workspace to change to it + - Click on a window to drag it + - Drag a window into a workspace would move the window to the workspace +- Exiting + - Click without dragging the window exits the overview + - Pressing ESC exits the overview +- Navigating + - When there are many workspaces open, scroll / swipe on the panel to pan through opened workspaces -Add this to start-up [plugins.lua](../plugins.lua): +## Configuration +### Dispatchers +- Use `overview:toggle` dispatcher to toggle workspace overview on current monitor +- Use `overview:close` to close the overview on current monitor if opened +- Use `overview:open` to open the overview on current monitor if closed +- Adding the `all` argument to these dispatchers would toggle / open / close overview on all monitors -```lua -require("Hyprspace.Hyprspace").setup() -``` +### Lua Configuration (Hyprland 0.55+) -This is the preferred setup for `hyprpm` installs and already-loaded plugins. +Starting with Hyprland 0.55+, the configuration format has transitioned to Lua as the default (`~/.config/hypr/hyprland.lua`). -If you want local manual loading from the Lua helper instead of loading the `.so` yourself, use: +#### 1. Setting Config Options +Plugin settings are structured nested within the `hl.config()` table under `plugin.overview`. ```lua -require("Hyprspace.Hyprspace").setup({ - plugin_path = HOME .. "/.config/Hyprspace/Hyprspace.so", +hl.config({ + plugin = { + overview = { + panelColor = "#1e1e2e", + panelBorderColor = "#cba6f7", + workspaceActiveBackground = "rgba(49, 50, 68, 0.8)", + workspaceActiveBorder = "rgb(203, 166, 247)", + onBottom = false, + autoDrag = true, + -- Add other overview settings here + } + } }) ``` -Use that only for a manual local build. Do not pass `plugin_path` when the plugin is managed by `hyprpm`. - -### 2. Keybind - -Add a keybind in [keybinds.lua](../keybinds.lua): - -```lua -hl.unbind("SUPER + A") -hl.bind("SUPER + A", function() - if hl.plugin and hl.plugin.Hyprspace and type(hl.plugin.Hyprspace.overview) == "function" then - hl.plugin.Hyprspace.overview("toggle") - end -end) -``` - -This file does not need to `require(...)` the helper directly. +#### 2. Using Lua Dispatchers +To invoke dispatchers, Hyprspace registers Lua wrapper functions in the `hl.plugin.overview` namespace. These can be bound in your configuration, run inside callbacks, or executed from the command line. -### 3. Reload +The following Lua functions are available: +* `hl.plugin.overview.toggle(arg)`: Toggles the overview on the current monitor. +* `hl.plugin.overview.open(arg)`: Opens the overview on the current monitor. +* `hl.plugin.overview.close(arg)`: Closes the overview on the current monitor. -After changing the Lua config: - -```bash -hyprctl reload -``` - -After rebuilding the plugin binary: - -```bash -cd ~/.config/Hyprspace -make all -hyprctl plugin unload ~/.config/Hyprspace/Hyprspace.so -hyprctl plugin load ~/.config/Hyprspace/Hyprspace.so -hyprctl reload -``` +#### 3. Examples -If the plugin is installed through `hyprpm`, use: - -```bash -hyprpm update -hyprpm reload -hyprctl reload -``` - -## Usage - -### Lua API - -The helper module exports: +##### Keybindings in `hyprland.lua`: +When binding Lua wrapper functions using `hl.bind`, you must wrap the call in a function definition: ```lua -local hyprspace = require("Hyprspace.Hyprspace") +-- Toggle overview on the current monitor using SUPER + TAB +hl.bind("SUPER + TAB", function() + hl.plugin.overview.toggle() +end, { description = "Toggle Hyprspace overview" }) -hyprspace.setup() -hyprspace.apply_config() -hyprspace.toggle() -hyprspace.overview("open") -hyprspace.overview("close") -hyprspace.reload() +-- Toggle overview on all monitors +hl.bind("SUPER + SHIFT + TAB", function() + hl.plugin.overview.toggle("all") +end, { description = "Toggle Hyprspace overview on all monitors" }) ``` -### Plugin Lua entrypoint - -Once the plugin is loaded, you can also call: - -```lua -hl.plugin.Hyprspace.overview("toggle") -hl.plugin.Hyprspace.overview("open") -hl.plugin.Hyprspace.overview("close") -``` - -## Configuration - -The plugin is configured from [Hyprspace.lua](./Hyprspace.lua). The helper builds a Lua config table and applies it through `hl.config(...)`. - -### Example - -```lua -plugin = { - hyprspace = { - panel_height = 220, - panel_border_width = 2, - workspace_margin = 10, - reserved_area = 35, - workspace_border_size = 1, - - center_aligned = true, - on_bottom = false, - draw_active_workspace = true, - hide_real_layers = false, - affect_strut = false, - - override_gaps = true, - gaps_in = 20, - gaps_out = 60, - - auto_drag = true, - auto_scroll = true, - exit_on_click = true, - exit_on_switch = false, - - disable_gestures = false, - swipe_fingers = 3, - swipe_distance = 300, - swipe_force_speed = 30, - swipe_cancel_ratio = 0.5, - click_release_threshold_ms = 200, - } -} -``` - -### Main options - +### Styling #### Colors - -- `panel_color` -- `panel_border_color` -- `workspace_active_background` -- `workspace_inactive_background` -- `workspace_active_border` -- `workspace_inactive_border` -- `drag_alpha` -- `disable_blur` - +- `plugin:overview:panelColor` +- `plugin:overview:panelBorderColor` +- `plugin:overview:workspaceActiveBackground` +- `plugin:overview:workspaceInactiveBackground` +- `plugin:overview:workspaceActiveBorder` +- `plugin:overview:workspaceInactiveBorder` +- `plugin:overview:dragAlpha` overrides the alpha of window when dragged in overview (0 - 1, 0 = transparent, 1 = opaque) +- `plugin:overview:disableBlur` #### Layout - -- `panel_height` -- `panel_border_width` -- `workspace_margin` -- `reserved_area` -- `workspace_border_size` -- `adaptive_height` -- `center_aligned` -- `on_bottom` -- `hide_background_layers` -- `hide_top_layers` -- `hide_overlay_layers` -- `draw_active_workspace` -- `hide_real_layers` -- `affect_strut` -- `override_gaps` -- `gaps_in` -- `gaps_out` - -#### Behavior - -- `auto_drag` -- `auto_scroll` -- `exit_on_click` -- `switch_on_drop` -- `exit_on_switch` -- `show_new_workspace` -- `show_empty_workspace` -- `show_special_workspace` -- `exit_key` - -#### Gestures and input - -- `disable_gestures` -- `reverse_swipe` -- `swipe_fingers` -- `swipe_distance` -- `swipe_force_speed` -- `swipe_cancel_ratio` -- `swipe_threshold` -- `swipe_closed_padding` -- `workspace_scroll_speed` -- `click_release_threshold_ms` - -#### Animation - -- `override_anim_speed` - -## Theming - -By default, [Hyprspace.lua](./Hyprspace.lua) reads colors from: - -```text -~/.config/matugen/generated/hyprland-colors.lua -``` - -If that file does not exist, Hyprspace falls back to built-in colors. - -The helper converts Matugen `rgba(rrggbbaa)` strings into the integer color format expected by the plugin. - -## Troubleshooting - -### The keybind does nothing - -Check that the plugin is actually loaded: - -```bash -hyprctl plugins -``` - -If you are using a manual build, load the `.so` first. - -### Lua settings are not applied - -Make sure [source/plugins.lua](../plugins.lua) calls: - -```lua -require("Hyprspace.Hyprspace").setup() -``` - -Then run: - -```bash -hyprctl reload -``` - -### `hyprpm enable` fails - -Check: - -- the repo URL is correct -- `hyprpm.toml` contains a pin for your current Hyprland commit -- the plugin builds cleanly on that commit - -### The plugin crashes on load - -Check the latest Hyprland crash report under: - -```text -~/.cache/hyprland/ -``` - -and verify the plugin was built against the same Hyprland version you are running. +- `plugin:overview:panelHeight` +- `plugin:overview:panelBorderWidth` +- `plugin:overview:onBottom` whether if panel should be on bottom instead of top +- `plugin:overview:workspaceMargin` spacing of workspaces with eachother and the edge of the panel +- `plugin:overview:reservedArea` padding on top of the panel, for Macbook camera notch +- `plugin:overview:workspaceBorderSize` +- `plugin:overview:centerAligned` whether if workspaces should be aligned at the center (KDE / macOS style) or at the left (Windows style) +- `plugin:overview:hideBackgroundLayers` do not draw background and bottom layers in overview +- `plugin:overview:hideTopLayers` do not draw top layers in overview +- `plugin:overview:hideOverlayLayers` do not draw overlay layers in overview +- `plugin:overview:hideRealLayers` whether to hide layers in actual workspace +- `plugin:overview:drawActiveWorkspace` draw the active workspace in overview as-is +- `plugin:overview:overrideGaps` whether if overview should override the layout gaps in the current workspace using the following values +- `plugin:overview:gapsIn` +- `plugin:overview:gapsOut` +- `plugin:overview:affectStrut` whether the panel should push window aside, disabling this option also disables `overrideGaps` + +### Animation +- The panel uses the `windows` curve for a slide-in animation +- Use `plugin:overview:overrideAnimSpeed` to override the animation speed + +### Behaviors +- `plugin:overview:autoDrag` mouse click always drags window when overview is open +- `plugin:overview:autoScroll` mouse scroll on active workspace area always switch workspace +- `plugin:overview:exitOnClick` mouse click without dragging exits overview +- `plugin:overview:switchOnDrop` switch to the workspace when a window is droppped into it +- `plugin:overview:exitOnSwitch` overview exits when overview is switched by clicking on workspace view or by `switchOnDrop` +- `plugin:overview:showNewWorkspace` add a new empty workspace at the end of workspaces view +- `plugin:overview:showEmptyWorkspace` show empty workspaces that are inbetween non-empty workspaces +- `plugin:overview:showSpecialWorkspace` defaults to false +- `plugin:overview:disableGestures` +- `plugin:overview:reverseSwipe` reverses the direction of swipe gesture, for macOS peeps? +- `plugin:overview:exitKey` key used to exit overview mode (default: Escape). Leave empty to disable keyboard exit. +- Touchpad gesture behavior follows Hyprland workspace swipe behavior + - `gestures:workspace_swipe_fingers` + - `gestures:workspace_swipe_cancel_ratio` + - `gestures:workspace_swipe_min_speed_to_force` diff --git a/flake.nix b/flake.nix index 24a4678..405126a 100644 --- a/flake.nix +++ b/flake.nix @@ -11,7 +11,7 @@ owner = "hyprwm"; repo = "Hyprland"; type = "github"; - ref = "v0.55.0"; + ref = "v0.56.0"; inputs.systems.follows = "systems"; }; }; diff --git a/hyprpm.toml b/hyprpm.toml index 6eb43e9..6aae4f3 100644 --- a/hyprpm.toml +++ b/hyprpm.toml @@ -19,7 +19,9 @@ commit_pins = [ ["254fc2bc6000075f660b4b8ed818a6af544d1d64", "49ef4c37c6f84dcbf7fb641bad3d60703a2bb068"], # 0.46.1 ["0bd541f2fd902dbfa04c3ea2ccf679395e316887", "cd58d2e47b575c66c2682436ba425ccdc8462998"], # 0.46.2 ["4e242d086e20b32951fdc0ebcbfb4d41b5be8dcc", "0a82e3724f929de8ad8fb04d2b7fa128493f24f7"], # 0.50.1 - ["39d7e209c79d451efab1b21151d5938289da838d", "f4b2c2537b2c02a477f17e3a684d4b542d82537b"], # 0.55.2 + ["a0136d8c04687bb36eb8a28eb9d1ff92aea99704", "3624878a7b6c00dfa77e351438fa209df99ad81d"], # 0.55.4 + ["6a8eb0490ce3ff3cbf9b00815f7a215e895d87e0", "777d73b09c7537548288128f90dff41bb8a79888"], # 0.55.4 + ["36b2e0cfe0c6094dbc47bd42a437431315bb3087", "45d65ec8cfa1a916dece34bc2f9117a01eb4b6f3"], # 0.56.0 ] [Hyprspace] diff --git a/src/Globals.hpp b/src/Globals.hpp index b13d827..336e1fa 100644 --- a/src/Globals.hpp +++ b/src/Globals.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include @@ -13,10 +12,16 @@ #include #include #include -#include +#include #include +#include +#include #include #include +#include +#include +#include +#include // Hyprland v0.54+: cancellable input uses Event::SCallbackInfo (not legacy CEvent*). using SCallbackInfo = Event::SCallbackInfo; @@ -44,58 +49,139 @@ typedef void (*tRenderWindow)(void*, PHLWINDOW, PHLMONITOR, const Time::steady_t extern void* pRenderWindow; typedef void (*tRenderLayer)(void*, PHLLS, PHLMONITOR, const Time::steady_tp&, bool, bool); extern void* pRenderLayer; -namespace Config { - extern CHyprColor panelBaseColor; - extern CHyprColor panelBorderColor; - extern CHyprColor workspaceActiveBackground; - extern CHyprColor workspaceInactiveBackground; - extern CHyprColor workspaceActiveBorder; - extern CHyprColor workspaceInactiveBorder; - - extern int panelHeight; - extern int panelBorderWidth; - extern int workspaceMargin; - extern int reservedArea; - extern int workspaceBorderSize; - extern bool adaptiveHeight; // TODO: implement - extern bool centerAligned; - extern bool onBottom; // TODO: implement - extern bool hideBackgroundLayers; - extern bool hideTopLayers; - extern bool hideOverlayLayers; - extern bool drawActiveWorkspace; - extern bool hideRealLayers; - extern bool affectStrut; - - extern bool overrideGaps; - extern int gapsIn; - extern int gapsOut; - - extern bool autoDrag; - extern bool autoScroll; - extern bool exitOnClick; - extern bool switchOnDrop; - extern bool exitOnSwitch; - extern bool showNewWorkspace; - extern bool showEmptyWorkspace; - extern bool showSpecialWorkspace; - - extern bool disableGestures; - extern bool reverseSwipe; - - extern bool disableBlur; - extern float overrideAnimSpeed; - extern float dragAlpha; - extern std::string exitKey; - - extern int clickReleaseThresholdMs; - extern int swipeFingers; - extern int swipeDistance; - extern int swipeForceSpeed; - extern float swipeCancelRatio; - extern float swipeThreshold; - extern float swipeClosedPadding; - extern float workspaceScrollSpeed; + +struct SConfig { + SP panelBaseColor; + SP panelBorderColor; + SP workspaceActiveBackground; + SP workspaceInactiveBackground; + SP workspaceActiveBorder; + SP workspaceInactiveBorder; + + SP panelHeight; + SP panelBorderWidth; + SP workspaceMargin; + SP workspaceBorderSize; + SP reservedArea; + SP adaptiveHeight; + SP centerAligned; + SP onBottom; + SP hideBackgroundLayers; + SP hideTopLayers; + SP hideOverlayLayers; + SP drawActiveWorkspace; + SP hideRealLayers; + SP affectStrut; + + SP overrideGaps; + SP gapsIn; + SP gapsOut; + + SP autoDrag; + SP autoScroll; + SP exitOnClick; + SP switchOnDrop; + SP exitOnSwitch; + SP showNewWorkspace; + SP showEmptyWorkspace; + SP showSpecialWorkspace; + + SP disableGestures; + SP reverseSwipe; + + SP disableBlur; + SP overrideAnimSpeed; + SP dragAlpha; + SP exitKey; +}; + +inline SConfig config = { + .panelBaseColor = makeShared("plugin:overview:panelColor", "description", CHyprColor(0, 0, 0, 0).getAsHex()), + .panelBorderColor = makeShared("plugin:overview:panelBorderColor", "description", CHyprColor(0, 0, 0, 0).getAsHex()), + .workspaceActiveBackground = makeShared("plugin:overview:workspaceActiveBackground", "description", CHyprColor(0, 0, 0, 0.25).getAsHex()), + .workspaceInactiveBackground = makeShared("plugin:overview:workspaceInactiveBackground", "description", CHyprColor(0, 0, 0, 0.5).getAsHex()), + .workspaceActiveBorder = makeShared("plugin:overview:workspaceActiveBorder", "description", CHyprColor(1, 1, 1, 0.25).getAsHex()), + .workspaceInactiveBorder = makeShared("plugin:overview:workspaceInactiveBorder", "description", CHyprColor(1, 1, 1, 0).getAsHex()), + + .panelHeight = makeShared("plugin:overview:panelHeight", "description", 250), + .panelBorderWidth = makeShared("plugin:overview:panelBorderWidth", "description", 2), + .workspaceMargin = makeShared("plugin:overview:workspaceMargin", "description", 12), + .workspaceBorderSize = makeShared("plugin:overview:workspaceBorderSize", "description", 1), + .reservedArea = makeShared("plugin:overview:reservedArea", "description", 0), + .adaptiveHeight = makeShared("plugin:overview:adaptiveHeight", "description", 0), // TODO: implement + .centerAligned = makeShared("plugin:overview:centerAligned", "description", 1), + .onBottom = makeShared("plugin:overview:onBottom", "description", 0), // TODO: implement + .hideBackgroundLayers = makeShared("plugin:overview:hideBackgroundLayers", "description", 0), + .hideTopLayers = makeShared("plugin:overview:hideTopLayers", "description", 0), + .hideOverlayLayers = makeShared("plugin:overview:hideOverlayLayers", "description", 0), + .drawActiveWorkspace = makeShared("plugin:overview:drawActiveWorkspace", "description", 1), + .hideRealLayers = makeShared("plugin:overview:hideRealLayers", "description", 1), + .affectStrut = makeShared("plugin:overview:affectStrut", "description", 1), + + .overrideGaps = makeShared("plugin:overview:overrideGaps", "description", 1), + .gapsIn = makeShared("plugin:overview:gapsIn", "description", 20), + .gapsOut = makeShared("plugin:overview:gapsOut", "description", 60), + + .autoDrag = makeShared("plugin:overview:autoDrag", "description", 1), + .autoScroll = makeShared("plugin:overview:autoScroll", "description", 1), + .exitOnClick = makeShared("plugin:overview:exitOnClick", "description", 1), + .switchOnDrop = makeShared("plugin:overview:switchOnDrop", "description", 0), + .exitOnSwitch = makeShared("plugin:overview:exitOnSwitch", "description", 0), + .showNewWorkspace = makeShared("plugin:overview:showNewWorkspace", "description", 1), + .showEmptyWorkspace = makeShared("plugin:overview:showEmptyWorkspace", "description", 1), + .showSpecialWorkspace = makeShared("plugin:overview:showSpecialWorkspace", "description", 0), + + .disableGestures = makeShared("plugin:overview:disableGestures", "description", 1), + .reverseSwipe = makeShared("plugin:overview:reverseSwipe", "description", 0), + + .disableBlur = makeShared("plugin:overview:disableBlur", "description", 0), + .overrideAnimSpeed = makeShared("plugin:overview:overrideAnimSpeed", "description", 0.0), + .dragAlpha = makeShared("plugin:overview:dragAlpha", "description", 0.2), + .exitKey = makeShared("plugin:overview:exitKey", "description", "Escape") +}; + +namespace HyprConfig { + inline std::optional getIntegerSafe(const std::string& name) { + auto reply = Config::mgr()->getConfigValue(name); + if (reply.dataptr && reply.type) { + if (*reply.type == typeid(Config::INTEGER)) { + return **reinterpret_cast(reply.dataptr); + } + } + return std::nullopt; + } + + inline std::optional getFloatSafe(const std::string& name) { + auto reply = Config::mgr()->getConfigValue(name); + if (reply.dataptr && reply.type) { + if (*reply.type == typeid(Config::FLOAT)) { + return **reinterpret_cast(reply.dataptr); + } + } + return std::nullopt; + } + + inline std::optional getWorkspaceSwipeDistance() { + return getIntegerSafe("gestures:workspace_swipe_distance"); + } + inline std::optional getWorkspaceSwipeMinSpeedToForce() { + return getIntegerSafe("gestures:workspace_swipe_min_speed_to_force"); + } + inline std::optional getWorkspaceSwipeCancelRatio() { + return getFloatSafe("gestures:workspace_swipe_cancel_ratio"); + } + inline std::optional getHyprsplitNumWorkspaces() { + return getIntegerSafe("plugin:hyprsplit:num_workspaces"); + } + inline std::optional getSplitMonitorWorkspacesCount() { + return getIntegerSafe("plugin:split-monitor-workspaces:count"); + } +} + +namespace Dispatchers { + SDispatchResult dispatchToggleOverview(std::string arg); + SDispatchResult dispatchOpenOverview(std::string arg); + SDispatchResult dispatchCloseOverview(std::string arg); } extern int numWorkspaces; diff --git a/src/Input.cpp b/src/Input.cpp index ba50243..0db1b50 100644 --- a/src/Input.cpp +++ b/src/Input.cpp @@ -1,32 +1,10 @@ -#include "Overview.hpp" -#include "Globals.hpp" - -#include - #include +#include +#include +#include -// 컴পাইলারের Deprecation Warning ইগনোর করার জন্য প্রাগমা যুক্ত করা হলো -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - -namespace { - -double panelTravel(PHLMONITOR owner) { - if (!owner) - return 0.; - - return (Config::panelHeight + Config::reservedArea) * owner->m_scale; -} - -double swipeClosedOffset() { - return -Config::swipeClosedPadding; -} - -double swipeVisibleThreshold() { - return Config::swipeThreshold; -} - -} +#include "Overview.hpp" +#include "Globals.hpp" bool CHyprspaceWidget::buttonEvent(bool pressed, Vector2D coords) { bool Return; @@ -39,7 +17,7 @@ bool CHyprspaceWidget::buttonEvent(bool pressed, Vector2D coords) { if (pressed) lastPressedTime = std::chrono::high_resolution_clock::now(); else - if (std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - lastPressedTime).count() < Config::clickReleaseThresholdMs) + if (std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - lastPressedTime).count() < 200) couldExit = true; int targetWorkspaceID = SPECIAL_WORKSPACE_START - 1; @@ -54,20 +32,20 @@ bool CHyprspaceWidget::buttonEvent(bool pressed, Vector2D coords) { } } - auto targetWorkspace = g_pCompositor->getWorkspaceByID(targetWorkspaceID); + auto targetWorkspace = State::workspaceState()->query().id(targetWorkspaceID).run(); // create new workspace if (targetWorkspace == nullptr && targetWorkspaceID >= SPECIAL_WORKSPACE_START) { - targetWorkspace = g_pCompositor->createNewWorkspace(targetWorkspaceID, getOwner()->m_id); + targetWorkspace = State::workspaceState()->create(targetWorkspaceID, getOwner()->m_id); } // if the cursor is hovering over workspace, clicking should switch workspace instead of starting window drag - if (Config::autoDrag && (targetWorkspace == nullptr || !pressed)) { + if (config.autoDrag->value() && (targetWorkspace == nullptr || !pressed)) { if (g_layoutManager->dragController()->target()) g_layoutManager->endDragTarget(); if (pressed) { - const auto PWINDOW = g_pCompositor->vectorToWindowUnified(coords, Desktop::View::WINDOW_ONLY, nullptr); + const auto PWINDOW = Desktop::viewState()->hitTest().windowAt(coords, Desktop::View::WINDOW_ONLY, nullptr); if (PWINDOW) { const auto LT = PWINDOW->layoutTarget(); if (LT) @@ -79,15 +57,15 @@ bool CHyprspaceWidget::buttonEvent(bool pressed, Vector2D coords) { // release window on workspace to drop it in if (targetWindow && targetWorkspace != nullptr && !pressed) { - g_pCompositor->moveWindowToWorkspaceSafe(targetWindow, targetWorkspace); + Desktop::globalWindowController()->moveWindowToWorkspace(targetWindow, targetWorkspace); if (targetWindow->m_isFloating) { auto targetPos = getOwner()->m_position + (getOwner()->m_size / 2.) - (targetWindow->m_reportedSize / 2.); - targetWindow->m_position = targetPos; - *targetWindow->m_realPosition = targetPos; + targetWindow->layoutBox().pos() = targetPos; + *targetWindow->positionAnimation() = targetPos; } - if (Config::switchOnDrop) { - g_pCompositor->getMonitorFromID(targetWorkspace->m_monitor->m_id)->changeWorkspace(targetWorkspace->m_id); - if (Config::exitOnSwitch && active) hide(); + if (config.switchOnDrop->value()) { + State::monitorState()->query().id(targetWorkspace->m_monitor->m_id).run()->changeWorkspace(targetWorkspace->m_id); + if (config.exitOnSwitch->value() && active) hide(); } updateLayout(); } @@ -96,12 +74,12 @@ bool CHyprspaceWidget::buttonEvent(bool pressed, Vector2D coords) { if (targetWorkspace->m_isSpecialWorkspace) getOwner()->activeSpecialWorkspaceID() == targetWorkspaceID ? getOwner()->setSpecialWorkspace(nullptr) : getOwner()->setSpecialWorkspace(targetWorkspaceID); else { - g_pCompositor->getMonitorFromID(targetWorkspace->m_monitor->m_id)->changeWorkspace(targetWorkspace->m_id); + State::monitorState()->query().id(targetWorkspace->m_monitor->m_id).run()->changeWorkspace(targetWorkspace->m_id); } - if (Config::exitOnSwitch && active) hide(); + if (config.exitOnSwitch->value() && active) hide(); } // click elsewhere to exit overview - else if (Config::exitOnClick && targetWorkspace == nullptr && active && couldExit && !pressed) hide(); + else if (config.exitOnClick->value() && targetWorkspace == nullptr && active && couldExit && !pressed) hide(); return Return; } @@ -109,31 +87,29 @@ bool CHyprspaceWidget::buttonEvent(bool pressed, Vector2D coords) { bool CHyprspaceWidget::axisEvent(double delta, wl_pointer_axis axis, Vector2D coords) { const auto owner = getOwner(); - const auto travel = panelTravel(owner); - CBox widgetBox = {owner->m_position.x, owner->m_position.y - curYOffset->value(), owner->m_transformedSize.x, travel}; - if (Config::onBottom) - widgetBox = {owner->m_position.x, owner->m_position.y + owner->m_transformedSize.y - travel + curYOffset->value(), owner->m_transformedSize.x, travel}; + CBox widgetBox = {owner->m_position.x, owner->m_position.y - curYOffset->value(), owner->m_transformedSize.x, (config.panelHeight->value() + config.reservedArea->value()) * owner->m_scale}; + if (config.onBottom->value()) widgetBox = {owner->m_position.x, owner->m_position.y + owner->m_transformedSize.y - ((config.panelHeight->value() + config.reservedArea->value()) * owner->m_scale) + curYOffset->value(), owner->m_transformedSize.x, (config.panelHeight->value() + config.reservedArea->value()) * owner->m_scale}; // scroll through panel if cursor is on it if (widgetBox.containsPoint(coords * getOwner()->m_scale)) { // only horizontal scroll pans the panel; ignore vertical scroll here if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) - *workspaceScrollOffset = workspaceScrollOffset->goal() - delta * Config::workspaceScrollSpeed; + *workspaceScrollOffset = workspaceScrollOffset->goal() - delta * 2; } // otherwise, scroll to switch active workspace (vertical scroll only) - else if (Config::autoScroll && axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { + else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { if (delta < 0) { SWorkspaceIDName wsIDName = getWorkspaceIDNameFromString("r-1"); - if (g_pCompositor->getWorkspaceByID(wsIDName.id) == nullptr) { - auto newWorkspace = g_pCompositor->createNewWorkspace(wsIDName.id, ownerID); + if (State::workspaceState()->query().id(wsIDName.id).run() == nullptr) { + auto newWorkspace = State::workspaceState()->create(wsIDName.id, ownerID); (void)newWorkspace; } getOwner()->changeWorkspace(wsIDName.id); } else { SWorkspaceIDName wsIDName = getWorkspaceIDNameFromString("r+1"); - if (g_pCompositor->getWorkspaceByID(wsIDName.id) == nullptr) { - auto newWorkspace = g_pCompositor->createNewWorkspace(wsIDName.id, ownerID); + if (State::workspaceState()->query().id(wsIDName.id).run() == nullptr) { + auto newWorkspace = State::workspaceState()->create(wsIDName.id, ownerID); (void)newWorkspace; } getOwner()->changeWorkspace(wsIDName.id); @@ -156,49 +132,38 @@ bool CHyprspaceWidget::beginSwipe(IPointer::SSwipeBeginEvent e) { } bool CHyprspaceWidget::updateSwipe(IPointer::SSwipeUpdateEvent e) { - // restrict swipe to a axis with the most significant movement to prevent misinput - const auto absY = std::abs(e.delta.y); - const auto verticalSwipe = absY > 0.0 && (std::abs(e.delta.x) / absY) < 1.0; - - if (verticalSwipe) { - if (swiping && e.fingers == static_cast(Config::swipeFingers)) { + constexpr int fingers = 3; + const auto distance = HyprConfig::getWorkspaceSwipeDistance().value(); - const auto owner = getOwner(); - if (!owner) - return true; + // restrict swipe to a axis with the most significant movement to prevent misinput + if (abs(e.delta.x) / abs(e.delta.y) < 1) { + if (swiping && e.fingers == (uint32_t)fingers) { - const auto distance = std::max(Config::swipeDistance, 1); - const auto currentScaling = owner->m_size.x / static_cast(distance); - const auto travel = panelTravel(owner); + float currentScaling = State::monitorState()->query().vec(g_pInputManager->getMouseCoordsInternal()).run()->m_size.x / distance; - double scrollDifferential = e.delta.y * (Config::reverseSwipe ? -1 : 1) * (Config::onBottom ? -1 : 1) * currentScaling; + double scrollDifferential = e.delta.y * (config.reverseSwipe->value() ? -1 : 1) * (config.onBottom->value() ? -1 : 1) * currentScaling; curSwipeOffset += scrollDifferential; - curSwipeOffset = std::clamp(curSwipeOffset, swipeClosedOffset(), travel); + curSwipeOffset = std::clamp(curSwipeOffset, -10, ((config.panelHeight->value() + config.reservedArea->value()) * getOwner()->m_scale)); avgSwipeSpeed = (avgSwipeSpeed * swipePoints + scrollDifferential) / (swipePoints + 1); - swipePoints++; - curYOffset->setValueAndWarp(travel - curSwipeOffset); + curYOffset->setValueAndWarp(((config.panelHeight->value() + config.reservedArea->value()) * getOwner()->m_scale) - curSwipeOffset); - if (curSwipeOffset < swipeVisibleThreshold() && active) - hide(); - else if (curSwipeOffset > swipeVisibleThreshold() && !active) - show(); + if (curSwipeOffset < 10 && active) hide(); + else if (curSwipeOffset > 10 && !active) show(); return false; } } else { // scroll through panel - if (e.fingers == static_cast(Config::swipeFingers) && active) { + if (e.fingers == (uint32_t)fingers && active) { const auto owner = getOwner(); - const auto travel = panelTravel(owner); - CBox widgetBox = {owner->m_position.x, owner->m_position.y - curYOffset->value(), owner->m_transformedSize.x, travel}; - if (Config::onBottom) - widgetBox = {owner->m_position.x, owner->m_position.y + owner->m_transformedSize.y - travel + curYOffset->value(), owner->m_transformedSize.x, travel}; + CBox widgetBox = {owner->m_position.x, owner->m_position.y - curYOffset->value(), owner->m_transformedSize.x, (config.panelHeight->value() + config.reservedArea->value()) * owner->m_scale}; + if (config.onBottom->value()) widgetBox = {owner->m_position.x, owner->m_position.y + owner->m_transformedSize.y - ((config.panelHeight->value() + config.reservedArea->value()) * owner->m_scale) + curYOffset->value(), owner->m_transformedSize.x, (config.panelHeight->value() + config.reservedArea->value()) * owner->m_scale}; if (widgetBox.containsPoint(g_pInputManager->getMouseCoordsInternal() * getOwner()->m_scale)) { - workspaceScrollOffset->setValueAndWarp(workspaceScrollOffset->goal() + e.delta.x * Config::workspaceScrollSpeed); + workspaceScrollOffset->setValueAndWarp(workspaceScrollOffset->goal() + e.delta.x * 2); return false; } } @@ -210,50 +175,46 @@ bool CHyprspaceWidget::updateSwipe(IPointer::SSwipeUpdateEvent e) { // janky asf bool CHyprspaceWidget::endSwipe(IPointer::SSwipeEndEvent e) { swiping = false; - const auto owner = getOwner(); - const auto travel = panelTravel(owner); // force cancel swipe if (e.cancelled) { - if (active) - hide(); - curSwipeOffset = swipeClosedOffset(); + if (active) hide(); + curSwipeOffset = -10.; } else { + const auto swipeForceSpeed = HyprConfig::getWorkspaceSwipeMinSpeedToForce().value(); + const auto cancelRatio = HyprConfig::getWorkspaceSwipeCancelRatio().value(); + double swipeTravel = (config.panelHeight->value() + config.reservedArea->value()) * getOwner()->m_scale; if (activeBeforeSwipe) { - if ((curSwipeOffset < travel * Config::swipeCancelRatio) || avgSwipeSpeed < -Config::swipeForceSpeed) { - if (active) - hide(); + if ((curSwipeOffset < swipeTravel * cancelRatio) || avgSwipeSpeed < -swipeForceSpeed) { + if (active) hide(); else { - *curYOffset = travel; - curSwipeOffset = swipeClosedOffset(); + *curYOffset = (config.panelHeight->value() + config.reservedArea->value()) * getOwner()->m_scale; + curSwipeOffset = -10.; } } else { // cancel - if (!active) - show(); + if (!active) show(); else { - *curYOffset = 0; - curSwipeOffset = travel; + *curYOffset = 0; + curSwipeOffset = (config.panelHeight->value() + config.reservedArea->value()) * getOwner()->m_scale; } } } else { - if ((curSwipeOffset > travel * (1.F - Config::swipeCancelRatio)) || avgSwipeSpeed > Config::swipeForceSpeed) { - if (!active) - show(); + if ((curSwipeOffset > swipeTravel * (1.f - cancelRatio)) || avgSwipeSpeed > swipeForceSpeed) { + if (!active) show(); else { - *curYOffset = 0; - curSwipeOffset = travel; + *curYOffset = 0; + curSwipeOffset = (config.panelHeight->value() + config.reservedArea->value()) * getOwner()->m_scale; } } else { // cancel - if (active) - hide(); + if (active) hide(); else { - *curYOffset = travel; - curSwipeOffset = swipeClosedOffset(); + *curYOffset = (config.panelHeight->value() + config.reservedArea->value()) * getOwner()->m_scale; + curSwipeOffset = -10.; } } } diff --git a/src/Layout.cpp b/src/Layout.cpp index ab32878..9bd6819 100644 --- a/src/Layout.cpp +++ b/src/Layout.cpp @@ -1,13 +1,14 @@ #include "Overview.hpp" #include "Globals.hpp" #include +#include // FIXME: preserve original workspace rules void CHyprspaceWidget::updateLayout() { - if (!Config::affectStrut) return; + if (!config.affectStrut->value()) return; - const auto currentHeight = Config::panelHeight + Config::reservedArea; + const auto currentHeight = config.panelHeight->value() + config.reservedArea->value(); const auto pMonitor = getOwner(); if (!pMonitor) return; @@ -23,7 +24,7 @@ void CHyprspaceWidget::updateLayout() { auto* const PGAPSOUT = static_cast(PGAPSOUTBASE); if (active) { - if (!Config::onBottom) + if (!config.onBottom->value()) pMonitor->m_reservedArea = Desktop::CReservedArea(currentHeight, 0, 0, 0); else pMonitor->m_reservedArea = Desktop::CReservedArea(0, 0, currentHeight, 0); @@ -42,11 +43,11 @@ void CHyprspaceWidget::updateLayout() { const auto oActiveWorkspace = pMonitor->m_activeWorkspace; if (!oActiveWorkspace) return; - for (auto& ws : g_pCompositor->getWorkspaces()) { // HACK: recalculate other workspaces without reserved area + for (auto& ws : State::workspaceState()->workspaces()) { // HACK: recalculate other workspaces without reserved area if (ws && ws->m_monitor && ws->m_monitor->m_id == ownerID && ws->m_id != oActiveWorkspace->m_id) { pMonitor->m_activeWorkspace = ws.lock(); const auto curRules = std::to_string(pMonitor->activeWorkspaceID()) + ", gapsin:" + PGAPSIN->toString() + ", gapsout:" + PGAPSOUT->toString(); - if (Config::overrideGaps) { + if (config.overrideGaps->value()) { if (const auto legacy = Config::Legacy::mgr().lock()) legacy->handleWorkspaceRules("", curRules); } @@ -55,8 +56,8 @@ void CHyprspaceWidget::updateLayout() { } pMonitor->m_activeWorkspace = oActiveWorkspace; - const auto curRules = std::to_string(pMonitor->activeWorkspaceID()) + ", gapsin:" + std::to_string(Config::gapsIn) + ", gapsout:" + std::to_string(Config::gapsOut); - if (Config::overrideGaps) { + const auto curRules = std::to_string(pMonitor->activeWorkspaceID()) + ", gapsin:" + std::to_string(config.gapsIn->value()) + ", gapsout:" + std::to_string(config.gapsOut->value()); + if (config.overrideGaps->value()) { if (const auto legacy = Config::Legacy::mgr().lock()) legacy->handleWorkspaceRules("", curRules); } @@ -64,10 +65,10 @@ void CHyprspaceWidget::updateLayout() { } else { - for (auto& ws : g_pCompositor->getWorkspaces()) { + for (auto& ws : State::workspaceState()->workspaces()) { if (ws && ws->m_monitor && ws->m_monitor->m_id == ownerID) { const auto curRules = std::to_string(ws->m_id) + ", gapsin:" + PGAPSIN->toString() + ", gapsout:" + PGAPSOUT->toString(); - if (Config::overrideGaps) { + if (config.overrideGaps->value()) { if (const auto legacy = Config::Legacy::mgr().lock()) legacy->handleWorkspaceRules("", curRules); } diff --git a/src/Lua.cpp b/src/Lua.cpp new file mode 100644 index 0000000..c52f636 --- /dev/null +++ b/src/Lua.cpp @@ -0,0 +1,28 @@ +#include "Lua.hpp" +#include "Globals.hpp" +#include + +extern "C" { + #include + #include +} + +template +int luaDispatcherWrapper(lua_State* L) { + const std::string arg = luaL_optstring(L, 1, ""); + SDispatchResult result = DispatcherFunc(arg); + lua_pushboolean(L, result.success); + if (!result.success) { + lua_pushstring(L, result.error.c_str()); + return 2; + } + return 1; +} + +void registerLuaBindings(HANDLE handle) { + if (Config::mgr() && Config::mgr()->type() == Config::CONFIG_LUA) { + HyprlandAPI::addLuaFunction(handle, "overview", "toggle", luaDispatcherWrapper); + HyprlandAPI::addLuaFunction(handle, "overview", "open", luaDispatcherWrapper); + HyprlandAPI::addLuaFunction(handle, "overview", "close", luaDispatcherWrapper); + } +} \ No newline at end of file diff --git a/src/Lua.hpp b/src/Lua.hpp new file mode 100644 index 0000000..f12c240 --- /dev/null +++ b/src/Lua.hpp @@ -0,0 +1,4 @@ +#pragma once +#include + +void registerLuaBindings(HANDLE handle); \ No newline at end of file diff --git a/src/Overview.cpp b/src/Overview.cpp index 2c1c5a6..b02b914 100644 --- a/src/Overview.cpp +++ b/src/Overview.cpp @@ -1,173 +1,145 @@ #include "Overview.hpp" #include "Globals.hpp" - -#include - #include +#include +#include -namespace { - -double panelTravelForMonitor(PHLMONITOR owner) { - if (!owner) - return 0.; - - return (Config::panelHeight + Config::reservedArea) * owner->m_scale; -} - -double closedSwipeOffset() { - return -Config::swipeClosedPadding; -} - -double shownSwipeOffset(PHLMONITOR owner) { - return panelTravelForMonitor(owner); -} - -} // namespace - -CHyprspaceWidget::CHyprspaceWidget(uint64_t inOwnerID) : ownerID(inOwnerID) { - resetAnimationState(getOwner()); -} - -CHyprspaceWidget::~CHyprspaceWidget() { - cleanup(getOwner()); - curYOffset.reset(); - workspaceScrollOffset.reset(); -} - -void CHyprspaceWidget::restoreHiddenLayers() { - for (const auto& [layer, alpha] : oLayerAlpha) { - if (!layer || layer->m_readyToDelete || !layer->m_mapped) - continue; - - layer->m_fadingOut = false; - *layer->m_alpha = alpha; - } - - oLayerAlpha.clear(); -} - -void CHyprspaceWidget::restoreFullscreenWindows() { - for (const auto& [windowRef, fullscreenMode] : prevFullscreen) { - const auto window = windowRef.lock(); - if (!window) - continue; - - g_pCompositor->setWindowFullscreenState(window, Desktop::View::SFullscreenState(fullscreenMode)); - if (fullscreenMode == FSMODE_FULLSCREEN) - window->m_wantsInitialFullscreen = false; - } - - prevFullscreen.clear(); -} +CHyprspaceWidget::CHyprspaceWidget(uint64_t inOwnerID) { + ownerID = inOwnerID; -void CHyprspaceWidget::resetAnimationState(PHLMONITOR owner) { curAnimationConfig = *Config::animationTree()->getAnimationPropertyConfig("windows"); - curAnimation = *curAnimationConfig.pValues.lock(); - *curAnimationConfig.pValues.lock() = curAnimation; - if (Config::overrideAnimSpeed > 0) - curAnimation.internalSpeed = Config::overrideAnimSpeed; + // the fuck is pValues??? + curAnimation = *curAnimationConfig.pValues.lock(); + *curAnimationConfig.pValues.lock() = curAnimation; - g_pAnimationManager->createAnimation(0.F, curYOffset, curAnimationConfig.pValues.lock(), AVARDAMAGE_ENTIRE); - g_pAnimationManager->createAnimation(0.F, workspaceScrollOffset, curAnimationConfig.pValues.lock(), AVARDAMAGE_ENTIRE); + if (config.overrideAnimSpeed->value() > 0) + curAnimation.internalSpeed = config.overrideAnimSpeed->value(); - const auto hiddenOffset = panelTravelForMonitor(owner); - curYOffset->setValueAndWarp(active ? 0.F : hiddenOffset); - workspaceScrollOffset->setValueAndWarp(0.F); - curSwipeOffset = active ? shownSwipeOffset(owner) : closedSwipeOffset(); + Animation::mgr()->createAnimation(0.F, curYOffset, curAnimationConfig.pValues.lock(), AVARDAMAGE_ENTIRE); + Animation::mgr()->createAnimation(0.F, workspaceScrollOffset, curAnimationConfig.pValues.lock(), AVARDAMAGE_ENTIRE); + curYOffset->setValueAndWarp(config.panelHeight->value()); + workspaceScrollOffset->setValueAndWarp(0); } -void CHyprspaceWidget::cleanup(PHLMONITOR owner) { - restoreHiddenLayers(); - restoreFullscreenWindows(); - workspaceBoxes.clear(); - swiping = false; - activeBeforeSwipe = false; - avgSwipeSpeed = 0.; - swipePoints = 0; - active = false; - - if (owner) { - owner->m_reservedArea = Desktop::CReservedArea(); - g_pHyprRenderer->arrangeLayersForMonitor(ownerID); - g_layoutManager->recalculateMonitor(owner); - g_pCompositor->scheduleFrameForMonitor(owner); - } -} +// TODO: implement deconstructor and delete widget on monitor unplug +CHyprspaceWidget::~CHyprspaceWidget() {} PHLMONITOR CHyprspaceWidget::getOwner() { - return g_pCompositor->getMonitorFromID(ownerID); + return State::monitorState()->query().id(ownerID).run(); } void CHyprspaceWidget::show() { auto owner = getOwner(); - if (!owner) - return; + if (!owner) return; if (prevFullscreen.empty()) { - for (auto& ws : g_pCompositor->getWorkspaces()) { - if (!ws || !ws->m_monitor || ws->m_monitor->m_id != ownerID) - continue; - - const auto window = ws->getFullscreenWindow(); - if (!window || ws->m_fullscreenMode == FSMODE_NONE) - continue; - - if (ws->m_fullscreenMode == FSMODE_FULLSCREEN) - window->m_wantsInitialFullscreen = true; - - prevFullscreen.emplace_back(PHLWINDOWREF(window), ws->m_fullscreenMode); - g_pCompositor->setWindowFullscreenState(window, Desktop::View::SFullscreenState{.internal = FSMODE_NONE, .client = FSMODE_NONE}); + // unfullscreen all windows + for (auto& ws : State::workspaceState()->workspaces()) { + if (ws && ws->m_monitor && ws->m_monitor->m_id == ownerID) { + const auto pWorkspace = ws.lock(); + const auto w = Fullscreen::controller()->getFullscreenWindow(pWorkspace); + const auto modes = Fullscreen::controller()->getFullscreenModes(pWorkspace); + if (w != nullptr && modes.internal != Fullscreen::FSMODE_NONE) { + // use fakefullscreenstate to preserve client's internal state + // fixes youtube fullscreen not restoring properly + if (modes.internal == Fullscreen::FSMODE_FULLSCREEN) w->m_wantsInitialFullscreen = true; + prevFullscreen.emplace_back(std::make_tuple(PHLWINDOWREF(w), modes)); + Fullscreen::controller()->setFullscreenMode(w, Fullscreen::FSMODE_NONE, Fullscreen::FSMODE_NONE); + } + } } } - if (oLayerAlpha.empty() && Config::hideRealLayers) { - for (int layerIdx : {2, 3}) { - for (auto& layerRef : owner->m_layerSurfaceLayers[layerIdx]) { - const auto layer = layerRef.lock(); - if (!layer) - continue; - - oLayerAlpha.emplace_back(layer, layer->m_alpha->goal()); - *layer->m_alpha = 0.F; - layer->m_fadingOut = true; - } + // hide top and overlay layers + // FIXME: ensure input is disabled for hidden layers + if (oLayerAlpha.empty() && config.hideRealLayers->value()) { + for (auto& ls : owner->m_layerSurfaceLayers[2]) { + //ls->startAnimation(false); + oLayerAlpha.emplace_back(std::make_tuple(ls.lock(), ls->alpha()[Desktop::View::LS_ALPHA_FADE]->goal())); + *ls->alpha()[Desktop::View::LS_ALPHA_FADE] = 0.f; + } + for (auto& ls : owner->m_layerSurfaceLayers[3]) { + //ls->startAnimation(false); + oLayerAlpha.emplace_back(std::make_tuple(ls.lock(), ls->alpha()[Desktop::View::LS_ALPHA_FADE]->goal())); + *ls->alpha()[Desktop::View::LS_ALPHA_FADE] = 0.f; } } active = true; + // panel offset should be handled by swipe event when swiping if (!swiping) { - *curYOffset = 0.F; - curSwipeOffset = shownSwipeOffset(owner); + *curYOffset = 0; + curSwipeOffset = (config.panelHeight->value() + config.reservedArea->value()) * owner->m_scale; } updateLayout(); g_pHyprRenderer->damageMonitor(owner); - g_pCompositor->scheduleFrameForMonitor(owner); + owner->scheduleFrame(); } void CHyprspaceWidget::hide() { auto owner = getOwner(); - if (!owner) - return; + if (!owner) return; + + // restore layer state + for (auto& ls : owner->m_layerSurfaceLayers[2]) { + if (ls->m_mapped) { + auto oAlpha = std::find_if(oLayerAlpha.begin(), oLayerAlpha.end(), [&] (const auto& tuple) {return std::get<0>(tuple) == ls;}); + if (oAlpha != oLayerAlpha.end()) { + *ls->alpha()[Desktop::View::LS_ALPHA_FADE] = std::get<1>(*oAlpha); + } + //ls->startAnimation(true); + } + } + for (auto& ls : owner->m_layerSurfaceLayers[3]) { + if (ls->m_mapped) { + auto oAlpha = std::find_if(oLayerAlpha.begin(), oLayerAlpha.end(), [&] (const auto& tuple) {return std::get<0>(tuple) == ls;}); + if (oAlpha != oLayerAlpha.end()) { + *ls->alpha()[Desktop::View::LS_ALPHA_FADE] = std::get<1>(*oAlpha); + } + //ls->startAnimation(true); + } + } + oLayerAlpha.clear(); - restoreHiddenLayers(); - restoreFullscreenWindows(); + // restore fullscreen state + for (auto& fs : prevFullscreen) { + const auto w = std::get<0>(fs).lock(); + if (!w) continue; + const auto modes = std::get<1>(fs); + Fullscreen::controller()->setFullscreenMode(w, modes.internal, modes.client); + if (modes.internal == Fullscreen::FSMODE_FULLSCREEN) w->m_wantsInitialFullscreen = false; + } + prevFullscreen.clear(); active = false; + // panel offset should be handled by swipe event when swiping if (!swiping) { - *curYOffset = shownSwipeOffset(owner); - curSwipeOffset = closedSwipeOffset(); + *curYOffset = (config.panelHeight->value() + config.reservedArea->value()) * owner->m_scale; + curSwipeOffset = -10.; } updateLayout(); - g_pCompositor->scheduleFrameForMonitor(owner); + owner->scheduleFrame(); } void CHyprspaceWidget::updateConfig() { - resetAnimationState(getOwner()); + curAnimationConfig = *Config::animationTree()->getAnimationPropertyConfig("windows"); + + // the fuck is pValues??? + curAnimation = *curAnimationConfig.pValues.lock(); + *curAnimationConfig.pValues.lock() = curAnimation; + + if (config.overrideAnimSpeed->value() > 0) + curAnimation.internalSpeed = config.overrideAnimSpeed->value(); + + Animation::mgr()->createAnimation(0.F, curYOffset, curAnimationConfig.pValues.lock(), AVARDAMAGE_ENTIRE); + Animation::mgr()->createAnimation(0.F, workspaceScrollOffset, curAnimationConfig.pValues.lock(), AVARDAMAGE_ENTIRE); + curYOffset->setValueAndWarp(config.panelHeight->value()); + workspaceScrollOffset->setValueAndWarp(0); } bool CHyprspaceWidget::isActive() { diff --git a/src/Overview.hpp b/src/Overview.hpp index f0b28a3..68a6aea 100644 --- a/src/Overview.hpp +++ b/src/Overview.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include class CHyprspaceWidget { @@ -17,7 +18,7 @@ class CHyprspaceWidget { std::vector> workspaceBoxes; // for storing the fullscreen state of windows prior to overview activation (which unfullscreens all windows) - std::vector> prevFullscreen; + std::vector> prevFullscreen; // for storing the layer alpha values prior to overview activation (which sets all panel to transparent when configured) std::vector> oLayerAlpha; @@ -31,15 +32,11 @@ class CHyprspaceWidget { double avgSwipeSpeed = 0.; // number of swiping speed frames recorded int swipePoints = 0; - // Swipe travel in monitor-scaled pixels, where 0 is hidden and panelTravel() is fully shown. - double curSwipeOffset = 0.; + // on second thought, this seems redundant as we could just write to curYOffset while swiping + double curSwipeOffset = 10.; PHLANIMVAR workspaceScrollOffset; - void restoreHiddenLayers(); - void restoreFullscreenWindows(); - void resetAnimationState(PHLMONITOR owner); - public: // for slide-in animation and swiping @@ -55,7 +52,6 @@ class CHyprspaceWidget { void hide(); void updateConfig(); - void cleanup(PHLMONITOR owner = nullptr); // should be called active or not void draw(); diff --git a/src/Render.cpp b/src/Render.cpp index 52fb0ef..3329659 100644 --- a/src/Render.cpp +++ b/src/Render.cpp @@ -1,11 +1,14 @@ #include "Overview.hpp" #include "Globals.hpp" +#include #include #include #include #include #include #include +#include +#include #include #include #include @@ -43,8 +46,8 @@ void renderWindowStub(PHLWINDOW pWindow, PHLMONITOR pMonitor, PHLWORKSPACE pWork Render::SRenderModifData renderModif; - const auto oRealPosition = pWindow->m_realPosition->value(); - const auto oSize = pWindow->m_realSize->value(); + const auto oRealPosition = pWindow->position(Desktop::View::IGeometric::GEOMETRIC_CURRENT); + const auto oSize = pWindow->size(Desktop::View::IGeometric::GEOMETRIC_CURRENT); const float logicalW = std::max((float)oSize.x, 5.F); const float scaleMod = rectOverride.w / std::max(logicalW * pMonitor->m_scale, 5.F); if (!(scaleMod > 0.F) || !(rectOverride.w > 0 && rectOverride.h > 0)) return; @@ -69,9 +72,9 @@ void renderWindowStub(PHLWINDOW pWindow, PHLMONITOR pMonitor, PHLWORKSPACE pWork renderdata.w = std::max(oSize.x, 5.0); renderdata.h = std::max(oSize.y, 5.0); renderdata.surface = pWindow->wlSurface()->resource(); - renderdata.dontRound = pWindow->isEffectiveInternalFSMode(FSMODE_FULLSCREEN); + renderdata.dontRound = Fullscreen::controller()->isFullscreen(pWindow, Fullscreen::FSMODE_FULLSCREEN); renderdata.fadeAlpha = 1.F; - renderdata.alpha = 1.F; + renderdata.alpha = 0.999F; renderdata.decorate = false; renderdata.rounding = renderdata.dontRound ? 0 : pWindow->rounding() * scaleMod * pMonitor->m_scale; renderdata.roundingPower = renderdata.dontRound ? 2.0F : pWindow->roundingPower(); @@ -103,10 +106,10 @@ void renderWindowStub(PHLWINDOW pWindow, PHLMONITOR pMonitor, PHLWORKSPACE pWork void renderLayerStub(PHLLS pLayer, PHLMONITOR pMonitor, CBox rectOverride, CBox clipBox, const Time::steady_tp& time) { if (!pLayer || !pMonitor) return; - if (!pLayer->m_mapped || pLayer->m_readyToDelete || !pLayer->m_layerSurface || !pLayer->wlSurface() || !pLayer->wlSurface()->resource()) return; + if (!pLayer->m_mapped || !pLayer->m_layerSurface || !pLayer->wlSurface() || !pLayer->wlSurface()->resource()) return; - Vector2D oRealPosition = pLayer->m_realPosition->value(); - Vector2D oSize = pLayer->m_realSize->value(); + Vector2D oRealPosition = pLayer->position(Desktop::View::IGeometric::GEOMETRIC_CURRENT); + Vector2D oSize = pLayer->size(Desktop::View::IGeometric::GEOMETRIC_CURRENT); const float curScaling = rectOverride.w / (oSize.x); if (!(curScaling > 0.F) || !(rectOverride.w > 0 && rectOverride.h > 0)) return; @@ -124,7 +127,7 @@ void renderLayerStub(PHLLS pLayer, PHLMONITOR pMonitor, CBox rectOverride, CBox CSurfacePassElement::SRenderData renderdata = {pMonitor, time, oRealPosition}; renderdata.fadeAlpha = 1.F; - renderdata.alpha = 1.F; + renderdata.alpha = 0.999F; renderdata.blur = false; renderdata.surface = pLayer->wlSurface()->resource(); renderdata.decorate = false; @@ -172,10 +175,10 @@ void CHyprspaceWidget::draw() { owner->m_blurFBShouldRender = true; int bottomInvert = 1; - if (Config::onBottom) bottomInvert = -1; + if (config.onBottom->value()) bottomInvert = -1; // Background box - CBox widgetBox = {owner->m_position.x, owner->m_position.y + (Config::onBottom * (owner->m_transformedSize.y - ((Config::panelHeight + Config::reservedArea) * owner->m_scale))) - (bottomInvert * curYOffset->value()), owner->m_transformedSize.x, (Config::panelHeight + Config::reservedArea) * owner->m_scale}; //TODO: update size on monitor change + CBox widgetBox = {owner->m_position.x, owner->m_position.y + (config.onBottom->value() * (owner->m_transformedSize.y - ((config.panelHeight->value() + config.reservedArea->value()) * owner->m_scale))) - (bottomInvert * curYOffset->value()), owner->m_transformedSize.x, (config.panelHeight->value() + config.reservedArea->value()) * owner->m_scale}; //TODO: update size on monitor change // set widgetBox relative to current monitor for rendering panel widgetBox.x -= owner->m_position.x; @@ -183,20 +186,20 @@ void CHyprspaceWidget::draw() { g_pHyprRenderer->m_renderData.clipBox = monitorClip; - if (!Config::disableBlur) { - renderRectWithBlur(widgetBox, Config::panelBaseColor); + if (!config.disableBlur->value()) { + renderRectWithBlur(widgetBox, config.panelBaseColor->value()); } else { - renderRect(widgetBox, Config::panelBaseColor); + renderRect(widgetBox, config.panelBaseColor->value()); } // Panel Border - if (Config::panelBorderWidth > 0) { + if (config.panelBorderWidth->value() > 0) { // Border box - CBox borderBox = {widgetBox.x, owner->m_position.y + (Config::onBottom * owner->m_transformedSize.y) + (Config::panelHeight + Config::reservedArea - curYOffset->value() * owner->m_scale) * bottomInvert, owner->m_transformedSize.x, static_cast(Config::panelBorderWidth)}; + CBox borderBox = {widgetBox.x, owner->m_position.y + (config.onBottom->value() * owner->m_transformedSize.y) + (config.panelHeight->value() + config.reservedArea->value() - curYOffset->value() * owner->m_scale) * bottomInvert, owner->m_transformedSize.x, static_cast(config.panelBorderWidth->value())}; borderBox.y -= owner->m_position.y; - renderRect(borderBox, Config::panelBorderColor); + renderRect(borderBox, config.panelBorderColor->value()); } @@ -212,14 +215,14 @@ void CHyprspaceWidget::draw() { // the list of workspaces to show std::vector workspaces; - if (Config::showSpecialWorkspace) { + if (config.showSpecialWorkspace->value()) { workspaces.push_back(SPECIAL_WORKSPACE_START); } // find the lowest and highest workspace id to determine which empty workspaces to insert int lowestID = INT_MAX; int highestID = 1; - for (auto& ws : g_pCompositor->getWorkspaces()) { + for (auto& ws : State::workspaceState()->workspaces()) { if (!ws) continue; // normal workspaces start from 1, special workspaces ends on -2 if (ws->m_id < 1) continue; @@ -231,7 +234,7 @@ void CHyprspaceWidget::draw() { } // include empty workspaces that are between non-empty ones - if (Config::showEmptyWorkspace) { + if (config.showEmptyWorkspace->value()) { int wsIDStart = 1; int wsIDEnd = highestID; @@ -243,16 +246,16 @@ void CHyprspaceWidget::draw() { for (int i = wsIDStart; i <= wsIDEnd; i++) { if (i == owner->activeSpecialWorkspaceID()) continue; - const auto pWorkspace = g_pCompositor->getWorkspaceByID(i); + const auto pWorkspace = State::workspaceState()->query().id(i).run(); if (pWorkspace == nullptr) workspaces.push_back(i); } } // add a new empty workspace at last - if (Config::showNewWorkspace) { + if (config.showNewWorkspace->value()) { // get the lowest empty workspce id after the highest id of current workspace - while (g_pCompositor->getWorkspaceByID(highestID) != nullptr) highestID++; + while (State::workspaceState()->query().id(highestID).run() != nullptr) highestID++; workspaces.push_back(highestID); } @@ -260,67 +263,67 @@ void CHyprspaceWidget::draw() { // render workspace boxes int wsCount = workspaces.size(); - double monitorSizeScaleFactor = ((Config::panelHeight - 2 * Config::workspaceMargin) / (owner->m_transformedSize.y)) * owner->m_scale; // scale box with panel height + double monitorSizeScaleFactor = ((config.panelHeight->value() - 2 * config.workspaceMargin->value()) / (owner->m_transformedSize.y)) * owner->m_scale; // scale box with panel height double workspaceBoxW = owner->m_transformedSize.x * monitorSizeScaleFactor; double workspaceBoxH = owner->m_transformedSize.y * monitorSizeScaleFactor; - double workspaceGroupWidth = workspaceBoxW * wsCount + (Config::workspaceMargin * owner->m_scale) * (wsCount - 1); - double curWorkspaceRectOffsetX = Config::centerAligned ? workspaceScrollOffset->value() + (widgetBox.w / 2.) - (workspaceGroupWidth / 2.) : workspaceScrollOffset->value() + Config::workspaceMargin; - double curWorkspaceRectOffsetY = !Config::onBottom ? (((Config::reservedArea + Config::workspaceMargin) * owner->m_scale) - curYOffset->value()) : (owner->m_transformedSize.y - ((Config::reservedArea + Config::workspaceMargin) * owner->m_scale) - workspaceBoxH + curYOffset->value()); - double workspaceOverflowSize = std::max(((workspaceGroupWidth - widgetBox.w) / 2) + (Config::workspaceMargin * owner->m_scale), 0); + double workspaceGroupWidth = workspaceBoxW * wsCount + (config.workspaceMargin->value() * owner->m_scale) * (wsCount - 1); + double curWorkspaceRectOffsetX = config.centerAligned->value() ? workspaceScrollOffset->value() + (widgetBox.w / 2.) - (workspaceGroupWidth / 2.) : workspaceScrollOffset->value() + config.workspaceMargin->value(); + double curWorkspaceRectOffsetY = !config.onBottom->value() ? (((config.reservedArea->value() + config.workspaceMargin->value()) * owner->m_scale) - curYOffset->value()) : (owner->m_transformedSize.y - ((config.reservedArea->value() + config.workspaceMargin->value()) * owner->m_scale) - workspaceBoxH + curYOffset->value()); + double workspaceOverflowSize = std::max(((workspaceGroupWidth - widgetBox.w) / 2) + (config.workspaceMargin->value() * owner->m_scale), 0); *workspaceScrollOffset = std::clamp(workspaceScrollOffset->goal(), -workspaceOverflowSize, workspaceOverflowSize); if (!(workspaceBoxW > 0 && workspaceBoxH > 0)) return; for (auto wsID : workspaces) { - const auto ws = g_pCompositor->getWorkspaceByID(wsID); + const auto ws = State::workspaceState()->query().id(wsID).run(); CBox curWorkspaceBox = {curWorkspaceRectOffsetX, curWorkspaceRectOffsetY, workspaceBoxW, workspaceBoxH}; // workspace background rect (NOT background layer) and border if (ws == owner->m_activeWorkspace) { - if (Config::workspaceBorderSize >= 1 && Config::workspaceActiveBorder.a > 0) { - renderBorder(curWorkspaceBox, Config::CGradientValueData(Config::workspaceActiveBorder), Config::workspaceBorderSize); + if (config.workspaceBorderSize->value() >= 1 && CHyprColor(config.workspaceActiveBorder->value()).a > 0) { + renderBorder(curWorkspaceBox, Config::CGradientValueData(config.workspaceActiveBorder->value()), config.workspaceBorderSize->value()); } - if (!Config::disableBlur) { - renderRectWithBlur(curWorkspaceBox, Config::workspaceActiveBackground); // cant really round it until I find a proper way to clip windows to a rounded rect + if (!config.disableBlur->value()) { + renderRectWithBlur(curWorkspaceBox, config.workspaceActiveBackground->value()); // cant really round it until I find a proper way to clip windows to a rounded rect } else { - renderRect(curWorkspaceBox, Config::workspaceActiveBackground); + renderRect(curWorkspaceBox, config.workspaceActiveBackground->value()); } - if (!Config::drawActiveWorkspace) { - curWorkspaceRectOffsetX += workspaceBoxW + (Config::workspaceMargin * owner->m_scale); + if (!config.drawActiveWorkspace->value()) { + curWorkspaceRectOffsetX += workspaceBoxW + (config.workspaceMargin->value() * owner->m_scale); continue; } } else { - if (Config::workspaceBorderSize >= 1 && Config::workspaceInactiveBorder.a > 0) { - renderBorder(curWorkspaceBox, Config::CGradientValueData(Config::workspaceInactiveBorder), Config::workspaceBorderSize); + if (config.workspaceBorderSize->value() >= 1 && CHyprColor(config.workspaceInactiveBorder->value()).a > 0) { + renderBorder(curWorkspaceBox, Config::CGradientValueData(config.workspaceInactiveBorder->value()), config.workspaceBorderSize->value()); } - if (!Config::disableBlur) { - renderRectWithBlur(curWorkspaceBox, Config::workspaceInactiveBackground); + if (!config.disableBlur->value()) { + renderRectWithBlur(curWorkspaceBox, config.workspaceInactiveBackground->value()); } else { - renderRect(curWorkspaceBox, Config::workspaceInactiveBackground); + renderRect(curWorkspaceBox, config.workspaceInactiveBackground->value()); } } // background and bottom layers - if (!Config::hideBackgroundLayers) { + if (!config.hideBackgroundLayers->value()) { for (auto& ls : owner->m_layerSurfaceLayers[0]) { - CBox layerBox = {curWorkspaceBox.pos() + (ls->m_realPosition->value() - owner->m_position) * monitorSizeScaleFactor, ls->m_realSize->value() * monitorSizeScaleFactor}; + CBox layerBox = {curWorkspaceBox.pos() + (ls->position(Desktop::View::IGeometric::GEOMETRIC_CURRENT) - owner->m_position) * monitorSizeScaleFactor, ls->size(Desktop::View::IGeometric::GEOMETRIC_CURRENT) * monitorSizeScaleFactor}; renderLayerStub(ls.lock(), owner, layerBox, curWorkspaceBox, time); } for (auto& ls : owner->m_layerSurfaceLayers[1]) { - CBox layerBox = {curWorkspaceBox.pos() + (ls->m_realPosition->value() - owner->m_position) * monitorSizeScaleFactor, ls->m_realSize->value() * monitorSizeScaleFactor}; + CBox layerBox = {curWorkspaceBox.pos() + (ls->position(Desktop::View::IGeometric::GEOMETRIC_CURRENT) - owner->m_position) * monitorSizeScaleFactor, ls->size(Desktop::View::IGeometric::GEOMETRIC_CURRENT) * monitorSizeScaleFactor}; renderLayerStub(ls.lock(), owner, layerBox, curWorkspaceBox, time); } } // the mini panel to cover the awkward empty space reserved by the panel - if (owner->m_activeWorkspace == ws && Config::affectStrut) { + if (owner->m_activeWorkspace == ws && config.affectStrut->value()) { CBox miniPanelBox = {curWorkspaceRectOffsetX, curWorkspaceRectOffsetY, widgetBox.w * monitorSizeScaleFactor, widgetBox.h * monitorSizeScaleFactor}; - if (Config::onBottom) miniPanelBox = {curWorkspaceRectOffsetX, curWorkspaceRectOffsetY + workspaceBoxH - widgetBox.h * monitorSizeScaleFactor, widgetBox.w * monitorSizeScaleFactor, widgetBox.h * monitorSizeScaleFactor}; + if (config.onBottom->value()) miniPanelBox = {curWorkspaceRectOffsetX, curWorkspaceRectOffsetY + workspaceBoxH - widgetBox.h * monitorSizeScaleFactor, widgetBox.w * monitorSizeScaleFactor, widgetBox.h * monitorSizeScaleFactor}; - if (!Config::disableBlur) { + if (!config.disableBlur->value()) { renderRectWithBlur(miniPanelBox, CHyprColor(0, 0, 0, 0)); } else { @@ -332,13 +335,13 @@ void CHyprspaceWidget::draw() { if (ws != nullptr) { // draw tiled windows - for (auto& w : g_pCompositor->m_windows) { + for (auto& w : Desktop::windowState()->windows()) { if (!w) continue; if (w->m_workspace == ws && !w->m_isFloating) { - double wX = curWorkspaceRectOffsetX + ((w->m_realPosition->value().x - owner->m_position.x) * monitorSizeScaleFactor * owner->m_scale); - double wY = curWorkspaceRectOffsetY + ((w->m_realPosition->value().y - owner->m_position.y) * monitorSizeScaleFactor * owner->m_scale); - double wW = w->m_realSize->value().x * monitorSizeScaleFactor * owner->m_scale; - double wH = w->m_realSize->value().y * monitorSizeScaleFactor * owner->m_scale; + double wX = curWorkspaceRectOffsetX + ((w->position(Desktop::View::IGeometric::GEOMETRIC_CURRENT).x - owner->m_position.x) * monitorSizeScaleFactor * owner->m_scale); + double wY = curWorkspaceRectOffsetY + ((w->position(Desktop::View::IGeometric::GEOMETRIC_CURRENT).y - owner->m_position.y) * monitorSizeScaleFactor * owner->m_scale); + double wW = w->size(Desktop::View::IGeometric::GEOMETRIC_CURRENT).x * monitorSizeScaleFactor * owner->m_scale; + double wH = w->size(Desktop::View::IGeometric::GEOMETRIC_CURRENT).y * monitorSizeScaleFactor * owner->m_scale; if (!(wW > 0 && wH > 0)) continue; CBox curWindowBox = {wX, wY, wW, wH}; //g_pHyprOpenGL->renderRectWithBlur(&curWindowBox, CHyprColor(0, 0, 0, 0)); @@ -346,13 +349,13 @@ void CHyprspaceWidget::draw() { } } // draw floating windows - for (auto& w : g_pCompositor->m_windows) { + for (auto& w : Desktop::windowState()->windows()) { if (!w) continue; if (w->m_workspace == ws && w->m_isFloating && ws->getLastFocusedWindow() != w) { - double wX = curWorkspaceRectOffsetX + ((w->m_realPosition->value().x - owner->m_position.x) * monitorSizeScaleFactor * owner->m_scale); - double wY = curWorkspaceRectOffsetY + ((w->m_realPosition->value().y - owner->m_position.y) * monitorSizeScaleFactor * owner->m_scale); - double wW = w->m_realSize->value().x * monitorSizeScaleFactor * owner->m_scale; - double wH = w->m_realSize->value().y * monitorSizeScaleFactor * owner->m_scale; + double wX = curWorkspaceRectOffsetX + ((w->position(Desktop::View::IGeometric::GEOMETRIC_CURRENT).x - owner->m_position.x) * monitorSizeScaleFactor * owner->m_scale); + double wY = curWorkspaceRectOffsetY + ((w->position(Desktop::View::IGeometric::GEOMETRIC_CURRENT).y - owner->m_position.y) * monitorSizeScaleFactor * owner->m_scale); + double wW = w->size(Desktop::View::IGeometric::GEOMETRIC_CURRENT).x * monitorSizeScaleFactor * owner->m_scale; + double wH = w->size(Desktop::View::IGeometric::GEOMETRIC_CURRENT).y * monitorSizeScaleFactor * owner->m_scale; if (!(wW > 0 && wH > 0)) continue; CBox curWindowBox = {wX, wY, wW, wH}; //g_pHyprOpenGL->renderRectWithBlur(&curWindowBox, CHyprColor(0, 0, 0, 0)); @@ -363,10 +366,10 @@ void CHyprspaceWidget::draw() { if (ws->getLastFocusedWindow()) if (ws->getLastFocusedWindow()->m_isFloating) { const auto w = ws->getLastFocusedWindow(); - double wX = curWorkspaceRectOffsetX + ((w->m_realPosition->value().x - owner->m_position.x) * monitorSizeScaleFactor * owner->m_scale); - double wY = curWorkspaceRectOffsetY + ((w->m_realPosition->value().y - owner->m_position.y) * monitorSizeScaleFactor * owner->m_scale); - double wW = w->m_realSize->value().x * monitorSizeScaleFactor * owner->m_scale; - double wH = w->m_realSize->value().y * monitorSizeScaleFactor * owner->m_scale; + double wX = curWorkspaceRectOffsetX + ((w->position(Desktop::View::IGeometric::GEOMETRIC_CURRENT).x - owner->m_position.x) * monitorSizeScaleFactor * owner->m_scale); + double wY = curWorkspaceRectOffsetY + ((w->position(Desktop::View::IGeometric::GEOMETRIC_CURRENT).y - owner->m_position.y) * monitorSizeScaleFactor * owner->m_scale); + double wW = w->size(Desktop::View::IGeometric::GEOMETRIC_CURRENT).x * monitorSizeScaleFactor * owner->m_scale; + double wH = w->size(Desktop::View::IGeometric::GEOMETRIC_CURRENT).y * monitorSizeScaleFactor * owner->m_scale; if (!(wW > 0 && wH > 0)) continue; CBox curWindowBox = {wX, wY, wW, wH}; //g_pHyprOpenGL->renderRectWithBlur(&curWindowBox, CHyprColor(0, 0, 0, 0)); @@ -374,17 +377,17 @@ void CHyprspaceWidget::draw() { } } - if (owner->m_activeWorkspace != ws || !Config::hideRealLayers) { + if (owner->m_activeWorkspace != ws || !config.hideRealLayers->value()) { // this layer is hidden for real workspace when panel is displayed - if (!Config::hideTopLayers) + if (!config.hideTopLayers->value()) for (auto& ls : owner->m_layerSurfaceLayers[2]) { - CBox layerBox = {curWorkspaceBox.pos() + (ls->m_realPosition->value() - owner->m_position) * monitorSizeScaleFactor, ls->m_realSize->value() * monitorSizeScaleFactor}; + CBox layerBox = {curWorkspaceBox.pos() + (ls->position(Desktop::View::IGeometric::GEOMETRIC_CURRENT) - owner->m_position) * monitorSizeScaleFactor, ls->size(Desktop::View::IGeometric::GEOMETRIC_CURRENT) * monitorSizeScaleFactor}; renderLayerStub(ls.lock(), owner, layerBox, curWorkspaceBox, time); } - if (!Config::hideOverlayLayers) + if (!config.hideOverlayLayers->value()) for (auto& ls : owner->m_layerSurfaceLayers[3]) { - CBox layerBox = {curWorkspaceBox.pos() + (ls->m_realPosition->value() - owner->m_position) * monitorSizeScaleFactor, ls->m_realSize->value() * monitorSizeScaleFactor}; + CBox layerBox = {curWorkspaceBox.pos() + (ls->position(Desktop::View::IGeometric::GEOMETRIC_CURRENT) - owner->m_position) * monitorSizeScaleFactor, ls->size(Desktop::View::IGeometric::GEOMETRIC_CURRENT) * monitorSizeScaleFactor}; renderLayerStub(ls.lock(), owner, layerBox, curWorkspaceBox, time); } } @@ -403,7 +406,7 @@ void CHyprspaceWidget::draw() { workspaceBoxes.emplace_back(std::make_tuple(wsID, curWorkspaceBox)); // set the current position to the next workspace box - curWorkspaceRectOffsetX += workspaceBoxW + Config::workspaceMargin * owner->m_scale; + curWorkspaceRectOffsetX += workspaceBoxW + config.workspaceMargin->value() * owner->m_scale; } g_pHyprRenderer->m_renderData.clipBox = monitorClip; diff --git a/src/main.cpp b/src/main.cpp index 664ef29..22d7086 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,84 +1,27 @@ -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include #include +#include #include +#include #include -#include -#include +#include +#include +#include #include - -extern "C" { -#include -#include -} - -#include "Globals.hpp" +#include #include "Overview.hpp" +#include "Globals.hpp" +#include "Lua.hpp" void* pRenderWindow; void* pRenderLayer; std::vector> g_overviewWidgets; -CHyprColor Config::panelBaseColor = CHyprColor(0xCC111111); -CHyprColor Config::panelBorderColor = CHyprColor(0x66FF99CC); -CHyprColor Config::workspaceActiveBackground = CHyprColor(0x66111111); -CHyprColor Config::workspaceInactiveBackground = CHyprColor(0xAA111111); -CHyprColor Config::workspaceActiveBorder = CHyprColor(1, 1, 1, 0.3); -CHyprColor Config::workspaceInactiveBorder = CHyprColor(1, 1, 1, 0); - -int Config::panelHeight = 250; -int Config::panelBorderWidth = 2; -int Config::workspaceMargin = 12; -int Config::reservedArea = 0; -int Config::workspaceBorderSize = 1; -bool Config::adaptiveHeight = false; -bool Config::centerAligned = true; -bool Config::onBottom = false; -bool Config::hideBackgroundLayers = false; -bool Config::hideTopLayers = false; -bool Config::hideOverlayLayers = false; -bool Config::drawActiveWorkspace = true; -bool Config::hideRealLayers = false; -bool Config::affectStrut = false; -bool Config::overrideGaps = true; -int Config::gapsIn = 20; -int Config::gapsOut = 60; -bool Config::autoDrag = true; -bool Config::autoScroll = true; -bool Config::exitOnClick = true; -bool Config::switchOnDrop = false; -bool Config::exitOnSwitch = false; -bool Config::showNewWorkspace = true; -bool Config::showEmptyWorkspace = true; -bool Config::showSpecialWorkspace = false; -bool Config::disableGestures = false; -bool Config::reverseSwipe = false; -bool Config::disableBlur = false; -float Config::overrideAnimSpeed = 0.F; -float Config::dragAlpha = 0.2F; -std::string Config::exitKey = "Escape"; -int Config::clickReleaseThresholdMs = 200; -int Config::swipeFingers = 3; -int Config::swipeDistance = 300; -int Config::swipeForceSpeed = 30; -float Config::swipeCancelRatio = 0.5F; -float Config::swipeThreshold = 10.F; -float Config::swipeClosedPadding = 10.F; -float Config::workspaceScrollSpeed = 2.F; - -int numWorkspaces = -1; +int numWorkspaces = -1; //hyprsplit/split-monitor-workspaces support +// Event listener handles (auto-unregister when destroyed) CHyprSignalListener g_pRenderHook; CHyprSignalListener g_pConfigReloadHook; CHyprSignalListener g_pOpenLayerHook; @@ -94,311 +37,206 @@ CHyprSignalListener g_pSwipeEndHook; CHyprSignalListener g_pKeyPressHook; CHyprSignalListener g_pSwitchWorkspaceHook; CHyprSignalListener g_pAddMonitorHook; -CHyprSignalListener g_pRemoveMonitorHook; CHyprSignalListener g_pStartHook; -void reloadConfig(); - -namespace { - -using Config::Values::CBoolValue; -using Config::Values::CColorValue; -using Config::Values::CFloatValue; -using Config::Values::CIntValue; -using Config::Values::CStringValue; -using Config::Values::IValue; - -constexpr const char* PLUGIN_PREFIX = "plugin:hyprspace:"; - -struct SPluginConfigValues { - SP panelColor, panelBorderColor, workspaceActiveBackground, workspaceInactiveBackground, workspaceActiveBorder, workspaceInactiveBorder; - SP panelHeight, panelBorderWidth, workspaceMargin, reservedArea, workspaceBorderSize; - SP adaptiveHeight, centerAligned, onBottom, hideBackgroundLayers, hideTopLayers, hideOverlayLayers, drawActiveWorkspace, hideRealLayers, affectStrut; - SP overrideGaps, autoDrag, autoScroll, exitOnClick, switchOnDrop, exitOnSwitch, showNewWorkspace, showEmptyWorkspace, showSpecialWorkspace; - SP gapsIn, gapsOut, swipeFingers, swipeDistance, swipeForceSpeed, clickReleaseThresholdMs; - SP disableGestures, reverseSwipe, disableBlur; - SP swipeCancelRatio, swipeThreshold, swipeClosedPadding, workspaceScrollSpeed, overrideAnimSpeed, dragAlpha; - SP exitKey; -}; - -SPluginConfigValues g_pluginConfigValues; - -template -SP registerPluginValue(const SP& value) { - if (HyprlandAPI::addConfigValueV2(pHandle, value)) - return value; - - Log::logger->log(Log::ERR, "Hyprspace: failed to register config value {}", value->name()); - return nullptr; -} - -int readIntValue(const SP& value, int fallback) { - return value ? static_cast(value->value()) : fallback; -} - -bool readBoolValue(const SP& value, bool fallback) { - return value ? value->value() : fallback; -} - -float readFloatValue(const SP& value, float fallback) { - return value ? value->value() : fallback; -} - -std::string readStringValue(const SP& value, const std::string& fallback) { - return value ? value->value() : fallback; -} - -CHyprColor readColorValue(const SP& value, const CHyprColor& fallback) { - return value ? CHyprColor(static_cast(value->value())) : fallback; -} - -void endSwipeIfNeeded(const std::shared_ptr& widget) { - if (!widget || !widget->isSwiping()) - return; - - IPointer::SSwipeEndEvent cancelled; - cancelled.cancelled = true; - widget->endSwipe(cancelled); -} - -void removeMonitorWidget(PHLMONITOR monitor) { - if (!monitor) - return; - - g_overviewWidgets.erase(std::remove_if(g_overviewWidgets.begin(), g_overviewWidgets.end(), - [monitor](const std::shared_ptr& widget) { - if (!widget) - return true; - - const auto owner = widget->getOwner(); - if (owner && owner != monitor) - return false; - - widget->cleanup(monitor); - return owner == monitor || !owner; - }), - g_overviewWidgets.end()); -} - -void registerConfigValues() { - g_pluginConfigValues.panelColor = registerPluginValue(SP(new CColorValue("plugin:hyprspace:panel_color", "Overview panel fill color", Config::panelBaseColor.getAsHex()))); - g_pluginConfigValues.panelBorderColor = registerPluginValue(SP(new CColorValue("plugin:hyprspace:panel_border_color", "Overview panel border color", Config::panelBorderColor.getAsHex()))); - g_pluginConfigValues.workspaceActiveBackground = registerPluginValue(SP(new CColorValue("plugin:hyprspace:workspace_active_background", "Active workspace preview background", Config::workspaceActiveBackground.getAsHex()))); - g_pluginConfigValues.workspaceInactiveBackground = registerPluginValue(SP(new CColorValue("plugin:hyprspace:workspace_inactive_background", "Inactive workspace preview background", Config::workspaceInactiveBackground.getAsHex()))); - g_pluginConfigValues.workspaceActiveBorder = registerPluginValue(SP(new CColorValue("plugin:hyprspace:workspace_active_border", "Active workspace preview border", Config::workspaceActiveBorder.getAsHex()))); - g_pluginConfigValues.workspaceInactiveBorder = registerPluginValue(SP(new CColorValue("plugin:hyprspace:workspace_inactive_border", "Inactive workspace preview border", Config::workspaceInactiveBorder.getAsHex()))); - - g_pluginConfigValues.panelHeight = registerPluginValue(SP(new CIntValue("plugin:hyprspace:panel_height", "Overview panel height in logical pixels", Config::panelHeight))); - g_pluginConfigValues.panelBorderWidth = registerPluginValue(SP(new CIntValue("plugin:hyprspace:panel_border_width", "Overview panel border width in pixels", Config::panelBorderWidth))); - g_pluginConfigValues.workspaceMargin = registerPluginValue(SP(new CIntValue("plugin:hyprspace:workspace_margin", "Spacing between workspace previews", Config::workspaceMargin))); - g_pluginConfigValues.reservedArea = registerPluginValue(SP(new CIntValue("plugin:hyprspace:reserved_area", "Padding reserved inside the overview panel", Config::reservedArea))); - g_pluginConfigValues.workspaceBorderSize = registerPluginValue(SP(new CIntValue("plugin:hyprspace:workspace_border_size", "Workspace preview border width", Config::workspaceBorderSize))); - - g_pluginConfigValues.adaptiveHeight = registerPluginValue(SP(new CBoolValue("plugin:hyprspace:adaptive_height", "Reserved for future adaptive overview sizing", Config::adaptiveHeight))); - g_pluginConfigValues.centerAligned = registerPluginValue(SP(new CBoolValue("plugin:hyprspace:center_aligned", "Center workspace previews when they fit", Config::centerAligned))); - g_pluginConfigValues.onBottom = registerPluginValue(SP(new CBoolValue("plugin:hyprspace:on_bottom", "Render the overview panel at the bottom edge", Config::onBottom))); - g_pluginConfigValues.hideBackgroundLayers = registerPluginValue(SP(new CBoolValue("plugin:hyprspace:hide_background_layers", "Skip background and bottom layer previews", Config::hideBackgroundLayers))); - g_pluginConfigValues.hideTopLayers = registerPluginValue(SP(new CBoolValue("plugin:hyprspace:hide_top_layers", "Skip top layer previews", Config::hideTopLayers))); - g_pluginConfigValues.hideOverlayLayers = registerPluginValue(SP(new CBoolValue("plugin:hyprspace:hide_overlay_layers", "Skip overlay layer previews", Config::hideOverlayLayers))); - g_pluginConfigValues.drawActiveWorkspace = registerPluginValue(SP(new CBoolValue("plugin:hyprspace:draw_active_workspace", "Draw the active workspace contents in the overview", Config::drawActiveWorkspace))); - g_pluginConfigValues.hideRealLayers = registerPluginValue(SP(new CBoolValue("plugin:hyprspace:hide_real_layers", "Temporarily hide real top and overlay layers while overview is active", Config::hideRealLayers))); - g_pluginConfigValues.affectStrut = registerPluginValue(SP(new CBoolValue("plugin:hyprspace:affect_strut", "Reserve monitor space while the overview is visible", Config::affectStrut))); - - g_pluginConfigValues.overrideGaps = registerPluginValue(SP(new CBoolValue("plugin:hyprspace:override_gaps", "Override workspace gaps while the overview is visible", Config::overrideGaps))); - g_pluginConfigValues.gapsIn = registerPluginValue(SP(new CIntValue("plugin:hyprspace:gaps_in", "Inner gaps to use while overview is active", Config::gapsIn))); - g_pluginConfigValues.gapsOut = registerPluginValue(SP(new CIntValue("plugin:hyprspace:gaps_out", "Outer gaps to use while overview is active", Config::gapsOut))); - - g_pluginConfigValues.autoDrag = registerPluginValue(SP(new CBoolValue("plugin:hyprspace:auto_drag", "Start dragging a hovered window on press", Config::autoDrag))); - g_pluginConfigValues.autoScroll = registerPluginValue(SP(new CBoolValue("plugin:hyprspace:auto_scroll", "Allow wheel scrolling outside the panel to switch workspaces", Config::autoScroll))); - g_pluginConfigValues.exitOnClick = registerPluginValue(SP(new CBoolValue("plugin:hyprspace:exit_on_click", "Close the overview when clicking outside a workspace", Config::exitOnClick))); - g_pluginConfigValues.switchOnDrop = registerPluginValue(SP(new CBoolValue("plugin:hyprspace:switch_on_drop", "Switch to a workspace after dropping a window into it", Config::switchOnDrop))); - g_pluginConfigValues.exitOnSwitch = registerPluginValue(SP(new CBoolValue("plugin:hyprspace:exit_on_switch", "Close the overview after a workspace switch", Config::exitOnSwitch))); - g_pluginConfigValues.showNewWorkspace = registerPluginValue(SP(new CBoolValue("plugin:hyprspace:show_new_workspace", "Append a synthetic empty workspace preview at the end", Config::showNewWorkspace))); - g_pluginConfigValues.showEmptyWorkspace = registerPluginValue(SP(new CBoolValue("plugin:hyprspace:show_empty_workspace", "Display empty workspaces between populated ones", Config::showEmptyWorkspace))); - g_pluginConfigValues.showSpecialWorkspace = registerPluginValue(SP(new CBoolValue("plugin:hyprspace:show_special_workspace", "Display the special workspace preview", Config::showSpecialWorkspace))); - - g_pluginConfigValues.disableGestures = registerPluginValue(SP(new CBoolValue("plugin:hyprspace:disable_gestures", "Disable overview gesture handling", Config::disableGestures))); - g_pluginConfigValues.reverseSwipe = registerPluginValue(SP(new CBoolValue("plugin:hyprspace:reverse_swipe", "Reverse overview swipe direction", Config::reverseSwipe))); - g_pluginConfigValues.swipeFingers = registerPluginValue(SP(new CIntValue("plugin:hyprspace:swipe_fingers", "Number of fingers required for overview swipes", Config::swipeFingers, {.min = 1}))); - g_pluginConfigValues.swipeDistance = registerPluginValue(SP(new CIntValue("plugin:hyprspace:swipe_distance", "Gesture distance divisor for overview swipes", Config::swipeDistance, {.min = 1}))); - g_pluginConfigValues.swipeForceSpeed = registerPluginValue(SP(new CIntValue("plugin:hyprspace:swipe_force_speed", "Swipe speed required to force open or close", Config::swipeForceSpeed, {.min = 0}))); - g_pluginConfigValues.swipeCancelRatio = registerPluginValue(SP(new CFloatValue("plugin:hyprspace:swipe_cancel_ratio", "Swipe travel ratio needed to complete the gesture", Config::swipeCancelRatio, {.min = 0.F, .max = 1.F}))); - g_pluginConfigValues.swipeThreshold = registerPluginValue(SP(new CFloatValue("plugin:hyprspace:swipe_threshold", "Swipe offset threshold that toggles visible state", Config::swipeThreshold, {.min = 0.F}))); - g_pluginConfigValues.swipeClosedPadding = registerPluginValue(SP(new CFloatValue("plugin:hyprspace:swipe_closed_padding", "Closed-state overshoot padding for swipe gestures", Config::swipeClosedPadding, {.min = 0.F}))); - g_pluginConfigValues.workspaceScrollSpeed = registerPluginValue(SP(new CFloatValue("plugin:hyprspace:workspace_scroll_speed", "Horizontal panel scroll multiplier for wheel and swipe input", Config::workspaceScrollSpeed, {.min = 0.F}))); - - g_pluginConfigValues.disableBlur = registerPluginValue(SP(new CBoolValue("plugin:hyprspace:disable_blur", "Disable blur while rendering the overview", Config::disableBlur))); - g_pluginConfigValues.overrideAnimSpeed = registerPluginValue(SP(new CFloatValue("plugin:hyprspace:override_anim_speed", "Override the window animation speed used by the overview", Config::overrideAnimSpeed, {.min = 0.F}))); - g_pluginConfigValues.dragAlpha = registerPluginValue(SP(new CFloatValue("plugin:hyprspace:drag_alpha", "Temporary alpha applied to dragged windows inside the overview", Config::dragAlpha, {.min = 0.F, .max = 1.F}))); - g_pluginConfigValues.exitKey = registerPluginValue(SP(new CStringValue("plugin:hyprspace:exit_key", "Keysyms used to close the overview, empty disables it", Config::exitKey))); - g_pluginConfigValues.clickReleaseThresholdMs = registerPluginValue(SP(new CIntValue("plugin:hyprspace:click_release_threshold_ms", "Maximum press duration that still counts as a click", Config::clickReleaseThresholdMs, {.min = 0}))); -} - -} // namespace - APICALL EXPORT std::string PLUGIN_API_VERSION() { return HYPRLAND_API_VERSION; } -std::shared_ptr getWidgetForMonitor(PHLMONITORREF monitor) { +std::shared_ptr getWidgetForMonitor(PHLMONITORREF pMonitor) { for (auto& widget : g_overviewWidgets) { - if (!widget) - continue; - - if (widget->getOwner() == monitor) + if (!widget) continue; + if (!widget->getOwner()) continue; + if (widget->getOwner() == pMonitor) { return widget; + } } - return nullptr; } +// used to enforce the layout void refreshWidgets() { for (auto& widget : g_overviewWidgets) { - if (widget && widget->isActive()) - widget->show(); + if (widget != nullptr) + if (widget->isActive()) + widget->show(); } } -bool g_layoutNeedsRefresh = true; -float g_oAlpha = -1; +bool g_layoutNeedsRefresh = true; + +// for restroing dragged window's alpha value +float g_oAlpha = -1; void onRender(eRenderStage renderStage) { + + // refresh layout after scheduled recalculation on monitors were carried out in renderMonitor if (renderStage == eRenderStage::RENDER_PRE) { if (g_layoutNeedsRefresh) { refreshWidgets(); g_layoutNeedsRefresh = false; } - } else if (renderStage == eRenderStage::RENDER_PRE_WINDOWS) { - const auto widget = getWidgetForMonitor(g_pHyprRenderer->m_renderData.pMonitor); - if (widget && widget->getOwner()) { - const auto dragTarget = g_layoutManager->dragController()->target(); - const auto curWindow = dragTarget ? dragTarget->window() : nullptr; - if (curWindow && widget->isActive()) { - g_oAlpha = curWindow->alpha(Desktop::View::WINDOW_ALPHA_ACTIVE)->goal(); - curWindow->alpha(Desktop::View::WINDOW_ALPHA_ACTIVE)->setValueAndWarp(0); - } else - g_oAlpha = -1; - } else - g_oAlpha = -1; - } else if (renderStage == eRenderStage::RENDER_POST_WINDOWS) { + } + else if (renderStage == eRenderStage::RENDER_PRE_WINDOWS) { + + const auto widget = getWidgetForMonitor(g_pHyprRenderer->m_renderData.pMonitor); - if (!widget || !widget->getOwner()) { - g_oAlpha = -1; - return; - } + if (widget != nullptr) + if (widget->getOwner()) { + //widget->draw(); + const auto dragTarget = g_layoutManager->dragController()->target(); + const auto curWindow = dragTarget ? dragTarget->window() : nullptr; + if (curWindow) { + if (widget->isActive()) { + g_oAlpha = curWindow->alpha(Desktop::View::WINDOW_ALPHA_ACTIVE)->goal(); + curWindow->alpha(Desktop::View::WINDOW_ALPHA_ACTIVE)->setValueAndWarp(0); // HACK: hide dragged window for the actual pass + } + } + else g_oAlpha = -1; + } + else g_oAlpha = -1; + else g_oAlpha = -1; - widget->draw(); + } + else if (renderStage == eRenderStage::RENDER_POST_WINDOWS) { - if (g_oAlpha == -1) - return; + const auto widget = getWidgetForMonitor(g_pHyprRenderer->m_renderData.pMonitor); - const auto dragTarget = g_layoutManager->dragController()->target(); - const auto curWindow = dragTarget ? dragTarget->window() : nullptr; - if (!curWindow) { - g_oAlpha = -1; - return; - } + if (widget != nullptr) + if (widget->getOwner()) { + widget->draw(); + if (g_oAlpha != -1) { + const auto dragTarget = g_layoutManager->dragController()->target(); + const auto curWindow = dragTarget ? dragTarget->window() : nullptr; + if (curWindow) { + curWindow->alpha(Desktop::View::WINDOW_ALPHA_ACTIVE)->setValueAndWarp(config.dragAlpha->value()); + curWindow->m_ruleApplicator->noBlur().unset(Desktop::Types::PRIORITY_SET_PROP); + const auto time = Time::steadyNow(); + (*(tRenderWindow)pRenderWindow)(g_pHyprRenderer.get(), curWindow, widget->getOwner(), time, true, Render::RENDER_PASS_MAIN, false, false); + curWindow->m_ruleApplicator->noBlur().unset(Desktop::Types::PRIORITY_SET_PROP); + curWindow->alpha(Desktop::View::WINDOW_ALPHA_ACTIVE)->setValueAndWarp(g_oAlpha); + } + } + g_oAlpha = -1; + } - curWindow->alpha(Desktop::View::WINDOW_ALPHA_ACTIVE)->setValueAndWarp(Config::dragAlpha); - curWindow->m_ruleApplicator->noBlur().unset(Desktop::Types::PRIORITY_SET_PROP); - const auto time = Time::steadyNow(); - (*(tRenderWindow)pRenderWindow)(g_pHyprRenderer.get(), curWindow, widget->getOwner(), time, true, Render::RENDER_PASS_MAIN, false, false); - curWindow->m_ruleApplicator->noBlur().unset(Desktop::Types::PRIORITY_SET_PROP); - curWindow->alpha(Desktop::View::WINDOW_ALPHA_ACTIVE)->setValueAndWarp(g_oAlpha); - g_oAlpha = -1; } } -void onWorkspaceChange(PHLWORKSPACE workspace) { - if (!workspace) - return; +// event hook, currently this is only here to re-hide top layer panels on workspace change +void onWorkspaceChange(PHLWORKSPACE pWorkspace) { - const auto widget = getWidgetForMonitor(g_pCompositor->getMonitorFromID(workspace->m_monitor->m_id)); - if (widget && widget->isActive()) - widget->show(); + if (!pWorkspace) return; + + auto widget = getWidgetForMonitor(State::monitorState()->query().id(pWorkspace->m_monitor->m_id).run()); + if (widget != nullptr) + if (widget->isActive()) + widget->show(); } +// event hook for click and drag interaction void onMouseButton(const IPointer::SButtonEvent& event, SCallbackInfo& info) { const SP pointer = g_pSeatManager->m_mouse.lock(); - if (!pointer || event.button != BTN_LEFT) + if (!pointer) return; - const auto monitor = g_pCompositor->getMonitorFromCursor(); - if (!monitor) - return; + if (event.button != BTN_LEFT) return; + + const auto pressed = event.state == WL_POINTER_BUTTON_STATE_PRESSED; + const auto pMonitor = State::monitorState()->query().vec(g_pInputManager->getMouseCoordsInternal()).run(); + if (pMonitor) { + const auto widget = getWidgetForMonitor(pMonitor); + if (widget) { + if (widget->isActive()) { + info.cancelled = !widget->buttonEvent(pressed, g_pInputManager->getMouseCoordsInternal()); + } + } + } - const auto widget = getWidgetForMonitor(monitor); - if (widget && widget->isActive()) - info.cancelled = !widget->buttonEvent(event.state == WL_POINTER_BUTTON_STATE_PRESSED, g_pInputManager->getMouseCoordsInternal()); } +// event hook for scrolling through panel and workspaces void onMouseAxis(const IPointer::SAxisEvent& event, SCallbackInfo& info) { - const auto monitor = g_pCompositor->getMonitorFromCursor(); - if (!monitor) - return; - const auto widget = getWidgetForMonitor(monitor); - if (widget && widget->isActive()) - info.cancelled = !widget->axisEvent(event.delta, event.axis, g_pInputManager->getMouseCoordsInternal()); + const auto pMonitor = State::monitorState()->query().vec(g_pInputManager->getMouseCoordsInternal()).run(); + if (pMonitor) { + const auto widget = getWidgetForMonitor(pMonitor); + if (widget) { + if (widget->isActive()) { + info.cancelled = !widget->axisEvent(event.delta, event.axis, g_pInputManager->getMouseCoordsInternal()); + } + } + } + } +// event hook for swipe void onSwipeBegin(const IPointer::SSwipeBeginEvent& event, SCallbackInfo& info) { - if (Config::disableGestures) - return; - const auto widget = getWidgetForMonitor(g_pCompositor->getMonitorFromCursor()); - if (widget) + if (config.disableGestures->value()) return; + + const auto widget = getWidgetForMonitor(State::monitorState()->query().vec(g_pInputManager->getMouseCoordsInternal()).run()); + if (widget != nullptr) widget->beginSwipe(event); - for (auto& other : g_overviewWidgets) { - if (other && other != widget && other->isSwiping()) { - IPointer::SSwipeEndEvent cancelled; - cancelled.cancelled = true; - other->endSwipe(cancelled); + // end other widget swipe + for (auto& w : g_overviewWidgets) { + if (w != widget && w->isSwiping()) { + IPointer::SSwipeEndEvent dummy; + dummy.cancelled = true; + w->endSwipe(dummy); } } } +// event hook for update swipe, most of the swiping mechanics are here void onSwipeUpdate(const IPointer::SSwipeUpdateEvent& event, SCallbackInfo& info) { - if (Config::disableGestures) - return; - const auto widget = getWidgetForMonitor(g_pCompositor->getMonitorFromCursor()); - if (widget) + if (config.disableGestures->value()) return; + + const auto widget = getWidgetForMonitor(State::monitorState()->query().vec(g_pInputManager->getMouseCoordsInternal()).run()); + if (widget != nullptr) info.cancelled = !widget->updateSwipe(event); } +// event hook for end swipe void onSwipeEnd(const IPointer::SSwipeEndEvent& event, SCallbackInfo& info) { - if (Config::disableGestures) - return; - const auto widget = getWidgetForMonitor(g_pCompositor->getMonitorFromCursor()); - if (widget) + if (config.disableGestures->value()) return; + + const auto widget = getWidgetForMonitor(State::monitorState()->query().vec(g_pInputManager->getMouseCoordsInternal()).run()); + if (widget != nullptr) widget->endSwipe(event); } +// Close overview with configurable key void onKeyPress(const IKeyboard::SKeyEvent& event, SCallbackInfo& info) { const SP keyboard = g_pSeatManager->m_keyboard.lock(); - if (!keyboard || !keyboard->m_xkbSymState || Config::exitKey.empty()) + if (!keyboard || !keyboard->m_xkbSymState) return; - const auto keycode = event.keycode + 8; - const xkb_keysym_t keysym = xkb_state_key_get_one_sym(keyboard->m_xkbSymState, keycode); - const xkb_keysym_t configured = xkb_keysym_from_name(Config::exitKey.c_str(), XKB_KEYSYM_CASE_INSENSITIVE); + const auto keycode = event.keycode + 8; // Because to xkbcommon it's +8 from libinput + const xkb_keysym_t keysym = xkb_state_key_get_one_sym(keyboard->m_xkbSymState, keycode); - if (configured == XKB_KEY_NoSymbol || keysym != configured) + const std::string cfgExitKey = config.exitKey->value(); + if (cfgExitKey.empty()) return; - bool overviewActive = false; - for (auto& widget : g_overviewWidgets) { - if (widget && widget->isActive()) { - widget->hide(); - overviewActive = true; + const xkb_keysym_t cfgExitKeysym = xkb_keysym_from_name(cfgExitKey.c_str(), XKB_KEYSYM_CASE_INSENSITIVE); + + if (keysym == cfgExitKeysym) { + // close all panels + bool overviewActive = false; + for (auto& widget : g_overviewWidgets) { + if (widget != nullptr && widget->isActive()) { + widget->hide(); + overviewActive = true; + } } + // Only cancel event if overview was active and closed + if (overviewActive) + info.cancelled = true; } - - if (overviewActive) - info.cancelled = true; } PHLMONITOR g_pTouchedMonitor; @@ -407,244 +245,230 @@ void onTouchDown(const ITouch::SDownEvent& event, SCallbackInfo& info) { if (!event.device) return; - auto targetMonitor = g_pCompositor->getMonitorFromName(!event.device->m_boundOutput.empty() ? event.device->m_boundOutput : ""); - targetMonitor = targetMonitor ? targetMonitor : g_pCompositor->getMonitorFromCursor(); + auto targetMonitor = State::monitorState()->query().name(!event.device->m_boundOutput.empty() ? event.device->m_boundOutput : "").run(); + targetMonitor = targetMonitor ? targetMonitor : State::monitorState()->query().vec(g_pInputManager->getMouseCoordsInternal()).run(); const auto widget = getWidgetForMonitor(targetMonitor); - if (widget && targetMonitor && widget->isActive()) { - const Vector2D pos = targetMonitor->m_position + event.pos * targetMonitor->m_size; - info.cancelled = !widget->buttonEvent(true, pos); - if (info.cancelled) { - g_pTouchedMonitor = targetMonitor; - g_pCompositor->warpCursorTo(pos); - g_pInputManager->refocus(); + if (widget != nullptr && targetMonitor != nullptr) { + if (widget->isActive()) { + Vector2D pos = targetMonitor->m_position + event.pos * targetMonitor->m_size; + info.cancelled = !widget->buttonEvent(true, pos); + if (info.cancelled) { + g_pTouchedMonitor = targetMonitor; + Pointer::pointerController()->warpTo(pos); + g_pInputManager->refocus(); + } } } } void onTouchMove(const ITouch::SMotionEvent& event, SCallbackInfo& info) { - if (!g_pTouchedMonitor) - return; + if (g_pTouchedMonitor == nullptr) return; - g_pCompositor->warpCursorTo(g_pTouchedMonitor->m_position + g_pTouchedMonitor->m_size * event.pos); + Pointer::pointerController()->warpTo(g_pTouchedMonitor->m_position + g_pTouchedMonitor->m_size * event.pos); g_pInputManager->simulateMouseMovement(); } void onTouchUp(const ITouch::SUpEvent& event, SCallbackInfo& info) { const auto widget = getWidgetForMonitor(g_pTouchedMonitor); - if (widget && g_pTouchedMonitor && widget->isActive()) - info.cancelled = !widget->buttonEvent(false, g_pInputManager->getMouseCoordsInternal()); + if (widget != nullptr && g_pTouchedMonitor != nullptr) + if (widget->isActive()) + info.cancelled = !widget->buttonEvent(false, g_pInputManager->getMouseCoordsInternal()); g_pTouchedMonitor = nullptr; } -static SDispatchResult dispatchToggleOverview(std::string arg) { - const auto currentMonitor = g_pCompositor->getMonitorFromCursor(); - const auto widget = getWidgetForMonitor(currentMonitor); - if (!widget) - return {}; - - if (arg.contains("all")) { - const bool anyActive = widget->isActive(); - for (auto& overviewWidget : g_overviewWidgets) { - if (!overviewWidget) - continue; - - if (anyActive && overviewWidget->isActive()) - overviewWidget->hide(); - else if (!anyActive && !overviewWidget->isActive()) - overviewWidget->show(); +SDispatchResult Dispatchers::dispatchToggleOverview(std::string arg) { + auto currentMonitor = State::monitorState()->query().vec(g_pInputManager->getMouseCoordsInternal()).run(); + auto widget = getWidgetForMonitor(currentMonitor); + if (widget) { + if (arg.contains("all")) { + if (widget->isActive()) { + for (auto& widget : g_overviewWidgets) { + if (widget != nullptr) + if (widget->isActive()) + widget->hide(); + } + } + else { + for (auto& widget : g_overviewWidgets) { + if (widget != nullptr) + if (!widget->isActive()) + widget->show(); + } + } } - } else - widget->isActive() ? widget->hide() : widget->show(); - - return {}; + else + widget->isActive() ? widget->hide() : widget->show(); + } + return SDispatchResult{}; } -static SDispatchResult dispatchOpenOverview(std::string arg) { +SDispatchResult Dispatchers::dispatchOpenOverview(std::string arg) { if (arg.contains("all")) { for (auto& widget : g_overviewWidgets) { - if (widget && !widget->isActive()) - widget->show(); + if (!widget->isActive()) widget->show(); } - } else { - const auto widget = getWidgetForMonitor(g_pCompositor->getMonitorFromCursor()); - if (widget && !widget->isActive()) - widget->show(); } - - return {}; + else { + auto currentMonitor = State::monitorState()->query().vec(g_pInputManager->getMouseCoordsInternal()).run(); + auto widget = getWidgetForMonitor(currentMonitor); + if (widget) + if (!widget->isActive()) widget->show(); + } + return SDispatchResult{}; } -static SDispatchResult dispatchCloseOverview(std::string arg) { +SDispatchResult Dispatchers::dispatchCloseOverview(std::string arg) { if (arg.contains("all")) { for (auto& widget : g_overviewWidgets) { - if (widget && widget->isActive()) - widget->hide(); + if (widget->isActive()) widget->hide(); } - } else { - const auto widget = getWidgetForMonitor(g_pCompositor->getMonitorFromCursor()); - if (widget && widget->isActive()) - widget->hide(); } - - return {}; -} - -int luaOverview(lua_State* L) { - const char* arg = luaL_optstring(L, 1, "toggle"); - const std::string command = arg ? arg : "toggle"; - - // Pull the latest Lua-side config into the runtime snapshot before changing overview state. - reloadConfig(); - - if (command == "toggle") - dispatchToggleOverview(""); - else if (command == "open") - dispatchOpenOverview(""); - else if (command == "close") - dispatchCloseOverview(""); - else if (command == "toggle_all") - dispatchToggleOverview("all"); - else if (command == "open_all") - dispatchOpenOverview("all"); - else if (command == "close_all") - dispatchCloseOverview("all"); - else - return luaL_error(L, "Hyprspace.overview: expected toggle, open, close, toggle_all, open_all, or close_all"); - - return 0; + else { + auto currentMonitor = State::monitorState()->query().vec(g_pInputManager->getMouseCoordsInternal()).run(); + auto widget = getWidgetForMonitor(currentMonitor); + if (widget) + if (widget->isActive()) widget->hide(); + } + return SDispatchResult{}; } void* findFunctionBySymbol(HANDLE inHandle, const std::string func, const std::string sym) { - for (const auto& match : HyprlandAPI::findFunctionsByName(inHandle, func)) { - if (match.demangled.contains(sym)) - return match.address; + // should return all functions + auto funcSearch = HyprlandAPI::findFunctionsByName(inHandle, func); + for (auto f : funcSearch) { + if (f.demangled.contains(sym)) + return f.address; } - return nullptr; } void reloadConfig() { - Config::panelBaseColor = readColorValue(g_pluginConfigValues.panelColor, Config::panelBaseColor); - Config::panelBorderColor = readColorValue(g_pluginConfigValues.panelBorderColor, Config::panelBorderColor); - Config::workspaceActiveBackground = readColorValue(g_pluginConfigValues.workspaceActiveBackground, Config::workspaceActiveBackground); - Config::workspaceInactiveBackground = readColorValue(g_pluginConfigValues.workspaceInactiveBackground, Config::workspaceInactiveBackground); - Config::workspaceActiveBorder = readColorValue(g_pluginConfigValues.workspaceActiveBorder, Config::workspaceActiveBorder); - Config::workspaceInactiveBorder = readColorValue(g_pluginConfigValues.workspaceInactiveBorder, Config::workspaceInactiveBorder); - - Config::panelHeight = readIntValue(g_pluginConfigValues.panelHeight, Config::panelHeight); - Config::panelBorderWidth = readIntValue(g_pluginConfigValues.panelBorderWidth, Config::panelBorderWidth); - Config::workspaceMargin = readIntValue(g_pluginConfigValues.workspaceMargin, Config::workspaceMargin); - Config::reservedArea = readIntValue(g_pluginConfigValues.reservedArea, Config::reservedArea); - Config::workspaceBorderSize = readIntValue(g_pluginConfigValues.workspaceBorderSize, Config::workspaceBorderSize); - Config::adaptiveHeight = readBoolValue(g_pluginConfigValues.adaptiveHeight, Config::adaptiveHeight); - Config::centerAligned = readBoolValue(g_pluginConfigValues.centerAligned, Config::centerAligned); - Config::onBottom = readBoolValue(g_pluginConfigValues.onBottom, Config::onBottom); - Config::hideBackgroundLayers = readBoolValue(g_pluginConfigValues.hideBackgroundLayers, Config::hideBackgroundLayers); - Config::hideTopLayers = readBoolValue(g_pluginConfigValues.hideTopLayers, Config::hideTopLayers); - Config::hideOverlayLayers = readBoolValue(g_pluginConfigValues.hideOverlayLayers, Config::hideOverlayLayers); - Config::drawActiveWorkspace = readBoolValue(g_pluginConfigValues.drawActiveWorkspace, Config::drawActiveWorkspace); - Config::hideRealLayers = readBoolValue(g_pluginConfigValues.hideRealLayers, Config::hideRealLayers); - Config::affectStrut = readBoolValue(g_pluginConfigValues.affectStrut, Config::affectStrut); - - Config::overrideGaps = readBoolValue(g_pluginConfigValues.overrideGaps, Config::overrideGaps); - Config::gapsIn = readIntValue(g_pluginConfigValues.gapsIn, Config::gapsIn); - Config::gapsOut = readIntValue(g_pluginConfigValues.gapsOut, Config::gapsOut); - - Config::autoDrag = readBoolValue(g_pluginConfigValues.autoDrag, Config::autoDrag); - Config::autoScroll = readBoolValue(g_pluginConfigValues.autoScroll, Config::autoScroll); - Config::exitOnClick = readBoolValue(g_pluginConfigValues.exitOnClick, Config::exitOnClick); - Config::switchOnDrop = readBoolValue(g_pluginConfigValues.switchOnDrop, Config::switchOnDrop); - Config::exitOnSwitch = readBoolValue(g_pluginConfigValues.exitOnSwitch, Config::exitOnSwitch); - Config::showNewWorkspace = readBoolValue(g_pluginConfigValues.showNewWorkspace, Config::showNewWorkspace); - Config::showEmptyWorkspace = readBoolValue(g_pluginConfigValues.showEmptyWorkspace, Config::showEmptyWorkspace); - Config::showSpecialWorkspace = readBoolValue(g_pluginConfigValues.showSpecialWorkspace, Config::showSpecialWorkspace); - - Config::disableGestures = readBoolValue(g_pluginConfigValues.disableGestures, Config::disableGestures); - Config::reverseSwipe = readBoolValue(g_pluginConfigValues.reverseSwipe, Config::reverseSwipe); - Config::swipeFingers = readIntValue(g_pluginConfigValues.swipeFingers, Config::swipeFingers); - Config::swipeDistance = readIntValue(g_pluginConfigValues.swipeDistance, Config::swipeDistance); - Config::swipeForceSpeed = readIntValue(g_pluginConfigValues.swipeForceSpeed, Config::swipeForceSpeed); - Config::swipeCancelRatio = readFloatValue(g_pluginConfigValues.swipeCancelRatio, Config::swipeCancelRatio); - Config::swipeThreshold = readFloatValue(g_pluginConfigValues.swipeThreshold, Config::swipeThreshold); - Config::swipeClosedPadding = readFloatValue(g_pluginConfigValues.swipeClosedPadding, Config::swipeClosedPadding); - Config::workspaceScrollSpeed = readFloatValue(g_pluginConfigValues.workspaceScrollSpeed, Config::workspaceScrollSpeed); - - Config::disableBlur = readBoolValue(g_pluginConfigValues.disableBlur, Config::disableBlur); - Config::overrideAnimSpeed = readFloatValue(g_pluginConfigValues.overrideAnimSpeed, Config::overrideAnimSpeed); - Config::dragAlpha = readFloatValue(g_pluginConfigValues.dragAlpha, Config::dragAlpha); - Config::exitKey = readStringValue(g_pluginConfigValues.exitKey, Config::exitKey); - Config::clickReleaseThresholdMs = readIntValue(g_pluginConfigValues.clickReleaseThresholdMs, Config::clickReleaseThresholdMs); - - numWorkspaces = -1; - for (auto& widget : g_overviewWidgets) { - if (!widget) - continue; - - if (widget->isActive()) - widget->hide(); - endSwipeIfNeeded(widget); widget->updateConfig(); + if (widget->isActive() || widget->isSwiping()) { + widget->hide(); + IPointer::SSwipeEndEvent dummy; + dummy.cancelled = true; + widget->endSwipe(dummy); + } } + + // get number of workspaces from hyprsplit or split-monitor-workspaces plugin config + numWorkspaces = -1; + auto numWorkspacesOpt = HyprConfig::getHyprsplitNumWorkspaces(); + if (!numWorkspacesOpt.has_value()) + numWorkspacesOpt = HyprConfig::getSplitMonitorWorkspacesCount(); + if (numWorkspacesOpt.has_value()) + numWorkspaces = numWorkspacesOpt.value(); + + // TODO: schedule frame for monitor? } void registerMonitors() { - for (auto& monitor : g_pCompositor->m_monitors) { - if (getWidgetForMonitor(monitor)) - continue; - - g_overviewWidgets.emplace_back(std::make_shared(monitor->m_id)); + // create a widget for each monitor + for (auto& m : State::monitorState()->monitors()) { + if (getWidgetForMonitor(m) != nullptr) continue; + CHyprspaceWidget* widget = new CHyprspaceWidget(m->m_id); + g_overviewWidgets.emplace_back(widget); } } APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE inHandle) { pHandle = inHandle; - Log::logger->log(Log::DEBUG, "Loading Hyprspace"); - - registerConfigValues(); - HyprlandAPI::addLuaFunction(pHandle, "Hyprspace", "overview", ::luaOverview); + Log::logger->log(Log::DEBUG, "Loading overview plugin"); + + HyprlandAPI::addConfigValueV2(pHandle, config.panelBaseColor); + HyprlandAPI::addConfigValueV2(pHandle, config.panelBorderColor); + HyprlandAPI::addConfigValueV2(pHandle, config.workspaceActiveBackground); + HyprlandAPI::addConfigValueV2(pHandle, config.workspaceInactiveBackground); + HyprlandAPI::addConfigValueV2(pHandle, config.workspaceActiveBorder); + HyprlandAPI::addConfigValueV2(pHandle, config.workspaceInactiveBorder); + + HyprlandAPI::addConfigValueV2(pHandle, config.panelHeight); + HyprlandAPI::addConfigValueV2(pHandle, config.panelBorderWidth); + HyprlandAPI::addConfigValueV2(pHandle, config.workspaceMargin); + HyprlandAPI::addConfigValueV2(pHandle, config.workspaceBorderSize); + HyprlandAPI::addConfigValueV2(pHandle, config.reservedArea); + HyprlandAPI::addConfigValueV2(pHandle, config.adaptiveHeight); + HyprlandAPI::addConfigValueV2(pHandle, config.centerAligned); + HyprlandAPI::addConfigValueV2(pHandle, config.onBottom); + HyprlandAPI::addConfigValueV2(pHandle, config.hideBackgroundLayers); + HyprlandAPI::addConfigValueV2(pHandle, config.hideTopLayers); + HyprlandAPI::addConfigValueV2(pHandle, config.hideOverlayLayers); + HyprlandAPI::addConfigValueV2(pHandle, config.drawActiveWorkspace); + HyprlandAPI::addConfigValueV2(pHandle, config.hideRealLayers); + HyprlandAPI::addConfigValueV2(pHandle, config.affectStrut); + + HyprlandAPI::addConfigValueV2(pHandle, config.overrideGaps); + HyprlandAPI::addConfigValueV2(pHandle, config.gapsIn); + HyprlandAPI::addConfigValueV2(pHandle, config.gapsOut); + + HyprlandAPI::addConfigValueV2(pHandle, config.autoDrag); + HyprlandAPI::addConfigValueV2(pHandle, config.autoScroll); + HyprlandAPI::addConfigValueV2(pHandle, config.exitOnClick); + HyprlandAPI::addConfigValueV2(pHandle, config.switchOnDrop); + HyprlandAPI::addConfigValueV2(pHandle, config.exitOnSwitch); + HyprlandAPI::addConfigValueV2(pHandle, config.showNewWorkspace); + HyprlandAPI::addConfigValueV2(pHandle, config.showEmptyWorkspace); + HyprlandAPI::addConfigValueV2(pHandle, config.showSpecialWorkspace); + + HyprlandAPI::addConfigValueV2(pHandle, config.disableGestures); + HyprlandAPI::addConfigValueV2(pHandle, config.reverseSwipe); + + HyprlandAPI::addConfigValueV2(pHandle, config.disableBlur); + HyprlandAPI::addConfigValueV2(pHandle, config.overrideAnimSpeed); + HyprlandAPI::addConfigValueV2(pHandle, config.dragAlpha); + HyprlandAPI::addConfigValueV2(pHandle, config.exitKey); g_pConfigReloadHook = Event::bus()->m_events.config.reloaded.listen([]() { reloadConfig(); }); - g_pStartHook = Event::bus()->m_events.start.listen([]() { + g_pStartHook = Event::bus()->m_events.start.listen([]() { reloadConfig(); registerMonitors(); }); HyprlandAPI::reloadConfig(); - HyprlandAPI::addDispatcherV2(pHandle, "overview:toggle", ::dispatchToggleOverview); - HyprlandAPI::addDispatcherV2(pHandle, "overview:open", ::dispatchOpenOverview); - HyprlandAPI::addDispatcherV2(pHandle, "overview:close", ::dispatchCloseOverview); - - g_pRenderHook = Event::bus()->m_events.render.stage.listen([](eRenderStage stage) { onRender(stage); }); - g_pOpenLayerHook = Event::bus()->m_events.layer.opened.listen([](PHLLS) { g_layoutNeedsRefresh = true; }); - g_pCloseLayerHook = Event::bus()->m_events.layer.closed.listen([](PHLLS) { g_layoutNeedsRefresh = true; }); - g_pMouseButtonHook = listenCancellable(Event::bus()->m_events.input.mouse.button, onMouseButton); - g_pMouseAxisHook = listenCancellable(Event::bus()->m_events.input.mouse.axis, onMouseAxis); - g_pTouchDownHook = listenCancellable(Event::bus()->m_events.input.touch.down, onTouchDown); - g_pTouchMoveHook = listenCancellable(Event::bus()->m_events.input.touch.motion, onTouchMove); - g_pTouchUpHook = listenCancellable(Event::bus()->m_events.input.touch.up, onTouchUp); - g_pSwipeBeginHook = listenCancellable(Event::bus()->m_events.gesture.swipe.begin, onSwipeBegin); - g_pSwipeUpdateHook = listenCancellable(Event::bus()->m_events.gesture.swipe.update, onSwipeUpdate); - g_pSwipeEndHook = listenCancellable(Event::bus()->m_events.gesture.swipe.end, onSwipeEnd); - g_pKeyPressHook = listenCancellable(Event::bus()->m_events.input.keyboard.key, onKeyPress); + HyprlandAPI::addDispatcherV2(pHandle, "overview:toggle", Dispatchers::dispatchToggleOverview); + HyprlandAPI::addDispatcherV2(pHandle, "overview:open", Dispatchers::dispatchOpenOverview); + HyprlandAPI::addDispatcherV2(pHandle, "overview:close", Dispatchers::dispatchCloseOverview); + + registerLuaBindings(pHandle); + + g_pRenderHook = Event::bus()->m_events.render.stage.listen([](eRenderStage stage) { onRender(stage); }); + + // refresh on layer change + g_pOpenLayerHook = Event::bus()->m_events.layer.opened.listen([](PHLLS) { g_layoutNeedsRefresh = true; }); + g_pCloseLayerHook = Event::bus()->m_events.layer.closed.listen([](PHLLS) { g_layoutNeedsRefresh = true; }); + + + g_pMouseButtonHook = listenCancellable(Event::bus()->m_events.input.mouse.button, onMouseButton); + g_pMouseAxisHook = listenCancellable(Event::bus()->m_events.input.mouse.axis, onMouseAxis); + + g_pTouchDownHook = listenCancellable(Event::bus()->m_events.input.touch.down, onTouchDown); + g_pTouchMoveHook = listenCancellable(Event::bus()->m_events.input.touch.motion, onTouchMove); + g_pTouchUpHook = listenCancellable(Event::bus()->m_events.input.touch.up, onTouchUp); + + g_pSwipeBeginHook = listenCancellable(Event::bus()->m_events.gesture.swipe.begin, onSwipeBegin); + g_pSwipeUpdateHook = listenCancellable(Event::bus()->m_events.gesture.swipe.update, onSwipeUpdate); + g_pSwipeEndHook = listenCancellable(Event::bus()->m_events.gesture.swipe.end, onSwipeEnd); + + g_pKeyPressHook = listenCancellable(Event::bus()->m_events.input.keyboard.key, onKeyPress); + g_pSwitchWorkspaceHook = Event::bus()->m_events.workspace.active.listen(onWorkspaceChange); pRenderWindow = findFunctionBySymbol(pHandle, "renderWindow", "IHyprRenderer::renderWindow"); if (!pRenderWindow) pRenderWindow = findFunctionBySymbol(pHandle, "renderWindow", "CHyprRenderer::renderWindow"); - pRenderLayer = findFunctionBySymbol(pHandle, "renderLayer", "IHyprRenderer::renderLayer"); if (!pRenderLayer) pRenderLayer = findFunctionBySymbol(pHandle, "renderLayer", "CHyprRenderer::renderLayer"); registerMonitors(); - g_pAddMonitorHook = Event::bus()->m_events.monitor.added.listen([](PHLMONITOR) { registerMonitors(); }); - g_pRemoveMonitorHook = Event::bus()->m_events.monitor.removed.listen([](PHLMONITOR monitor) { removeMonitorWidget(monitor); }); + g_pAddMonitorHook = Event::bus()->m_events.monitor.added.listen([](PHLMONITOR) { registerMonitors(); }); - return {"Hyprspace", "Workspace overview", "KZdkm", "0.2"}; + return {"Hyprspace", "Workspace overview", "KZdkm", "0.1"}; } APICALL EXPORT void PLUGIN_EXIT() { @@ -663,16 +487,11 @@ APICALL EXPORT void PLUGIN_EXIT() { g_pKeyPressHook.reset(); g_pSwitchWorkspaceHook.reset(); g_pAddMonitorHook.reset(); - g_pRemoveMonitorHook.reset(); g_pStartHook.reset(); - for (auto& widget : g_overviewWidgets) { - if (widget) - widget->cleanup(widget->getOwner()); - } g_overviewWidgets.clear(); pRenderWindow = nullptr; - pRenderLayer = nullptr; - pHandle = nullptr; + pRenderLayer = nullptr; + pHandle = nullptr; }