mirror of
https://github.com/hyprwm/Hyprland
synced 2024-11-26 00:25:59 +01:00
Merge branch 'main' into scaling
This commit is contained in:
commit
d94391e702
63 changed files with 2006 additions and 188 deletions
4
.github/workflows/ci.yaml
vendored
4
.github/workflows/ci.yaml
vendored
|
@ -12,7 +12,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
|
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
|
||||||
pacman --noconfirm --noprogressbar -Syyu
|
pacman --noconfirm --noprogressbar -Syyu
|
||||||
pacman --noconfirm --noprogressbar -Sy glslang libepoxy libfontenc libxcvt libxfont2 libxkbfile vulkan-headers vulkan-validation-layers xcb-util-errors xcb-util-renderutil xcb-util-wm xorg-fonts-encodings xorg-server-common xorg-setxkbmap xorg-xkbcomp xorg-xwayland git cmake go clang lld libc++ pkgconf meson ninja wayland wayland-protocols libinput libxkbcommon pixman glm libdrm libglvnd cairo pango systemd scdoc base-devel seatd
|
pacman --noconfirm --noprogressbar -Sy glslang libepoxy libfontenc libxcvt libxfont2 libxkbfile vulkan-headers vulkan-validation-layers xcb-util-errors xcb-util-renderutil xcb-util-wm xorg-fonts-encodings xorg-server-common xorg-setxkbmap xorg-xkbcomp xorg-xwayland git cmake go clang lld libc++ pkgconf meson ninja wayland wayland-protocols libinput libxkbcommon pixman glm libdrm libglvnd cairo pango systemd scdoc base-devel seatd python
|
||||||
- name: Set up user
|
- name: Set up user
|
||||||
run: |
|
run: |
|
||||||
useradd -m githubuser
|
useradd -m githubuser
|
||||||
|
@ -62,7 +62,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
|
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
|
||||||
pacman --noconfirm --noprogressbar -Syyu
|
pacman --noconfirm --noprogressbar -Syyu
|
||||||
pacman --noconfirm --noprogressbar -Sy glslang libepoxy libfontenc libxcvt libxfont2 libxkbfile vulkan-headers vulkan-validation-layers git go clang lld libc++ pkgconf meson ninja wayland wayland-protocols libinput libxkbcommon pixman glm libdrm libglvnd cairo pango systemd scdoc base-devel seatd cmake jq
|
pacman --noconfirm --noprogressbar -Sy glslang libepoxy libfontenc libxcvt libxfont2 libxkbfile vulkan-headers vulkan-validation-layers git go clang lld libc++ pkgconf meson ninja wayland wayland-protocols libinput libxkbcommon pixman glm libdrm libglvnd cairo pango systemd scdoc base-devel seatd cmake jq python
|
||||||
- name: Checkout Hyprland
|
- name: Checkout Hyprland
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -20,6 +20,7 @@ result*
|
||||||
*-protocol.c
|
*-protocol.c
|
||||||
*-protocol.h
|
*-protocol.h
|
||||||
.ccls-cache
|
.ccls-cache
|
||||||
|
*.so
|
||||||
|
|
||||||
hyprctl/hyprctl
|
hyprctl/hyprctl
|
||||||
|
|
||||||
|
|
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -4,3 +4,6 @@
|
||||||
[submodule "subprojects/hyprland-protocols"]
|
[submodule "subprojects/hyprland-protocols"]
|
||||||
path = subprojects/hyprland-protocols
|
path = subprojects/hyprland-protocols
|
||||||
url = https://github.com/hyprwm/hyprland-protocols
|
url = https://github.com/hyprwm/hyprland-protocols
|
||||||
|
[submodule "subprojects/udis86"]
|
||||||
|
path = subprojects/udis86
|
||||||
|
url = https://github.com/canihavesomecoffee/udis86
|
||||||
|
|
|
@ -52,9 +52,10 @@ ENDIF(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
|
||||||
|
|
||||||
include_directories(. PRIVATE "subprojects/wlroots/include/")
|
include_directories(. PRIVATE "subprojects/wlroots/include/")
|
||||||
include_directories(. PRIVATE "subprojects/wlroots/build/include/")
|
include_directories(. PRIVATE "subprojects/wlroots/build/include/")
|
||||||
|
include_directories(. PRIVATE "subprojects/udis86/")
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
add_compile_options(-DWLR_USE_UNSTABLE)
|
add_compile_options(-DWLR_USE_UNSTABLE)
|
||||||
add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-value -Wno-missing-field-initializers -Wno-narrowing)
|
add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-value -Wno-missing-field-initializers -Wno-narrowing -Wno-pointer-arith)
|
||||||
ADD_LINK_OPTIONS( -rdynamic )
|
ADD_LINK_OPTIONS( -rdynamic )
|
||||||
SET(CMAKE_ENABLE_EXPORTS TRUE)
|
SET(CMAKE_ENABLE_EXPORTS TRUE)
|
||||||
|
|
||||||
|
@ -139,4 +140,5 @@ target_link_libraries(Hyprland
|
||||||
${CMAKE_SOURCE_DIR}/wlr-foreign-toplevel-management-unstable-v1-protocol.o
|
${CMAKE_SOURCE_DIR}/wlr-foreign-toplevel-management-unstable-v1-protocol.o
|
||||||
${CMAKE_SOURCE_DIR}/hyprland-toplevel-export-v1-protocol.o
|
${CMAKE_SOURCE_DIR}/hyprland-toplevel-export-v1-protocol.o
|
||||||
${CMAKE_SOURCE_DIR}/fractional-scale-v1-protocol.o
|
${CMAKE_SOURCE_DIR}/fractional-scale-v1-protocol.o
|
||||||
|
${CMAKE_SOURCE_DIR}/subprojects/udis86/build/libudis86/liblibudis86.a
|
||||||
)
|
)
|
||||||
|
|
14
Makefile
14
Makefile
|
@ -157,6 +157,7 @@ all:
|
||||||
make clear
|
make clear
|
||||||
make fixwlr
|
make fixwlr
|
||||||
cd ./subprojects/wlroots && meson build/ --buildtype=release && ninja -C build/ && cp ./build/libwlroots.so.12032 /usr/lib/ && cd ../..
|
cd ./subprojects/wlroots && meson build/ --buildtype=release && ninja -C build/ && cp ./build/libwlroots.so.12032 /usr/lib/ && cd ../..
|
||||||
|
cd subprojects/udis86 && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -H./ -B./build -G Ninja && cmake --build ./build --config Release --target all -j$(shell nproc)
|
||||||
make protocols
|
make protocols
|
||||||
make release
|
make release
|
||||||
cd hyprctl && make all && cd ..
|
cd hyprctl && make all && cd ..
|
||||||
|
@ -165,6 +166,7 @@ install:
|
||||||
make clear
|
make clear
|
||||||
make fixwlr
|
make fixwlr
|
||||||
cd ./subprojects/wlroots && meson build/ --buildtype=release && ninja -C build/ && cp ./build/libwlroots.so.12032 /usr/lib/ && cd ../..
|
cd ./subprojects/wlroots && meson build/ --buildtype=release && ninja -C build/ && cp ./build/libwlroots.so.12032 /usr/lib/ && cd ../..
|
||||||
|
cd subprojects/udis86 && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -H./ -B./build -G Ninja && cmake --build ./build --config Release --target all -j$(shell nproc) && cd ../..
|
||||||
make protocols
|
make protocols
|
||||||
make release
|
make release
|
||||||
cd hyprctl && make all && cd ..
|
cd hyprctl && make all && cd ..
|
||||||
|
@ -211,6 +213,18 @@ config:
|
||||||
|
|
||||||
cd subprojects/wlroots && ninja -C build/ install
|
cd subprojects/wlroots && ninja -C build/ install
|
||||||
|
|
||||||
|
cd subprojects/udis86 && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -H./ -B./build -G Ninja && cmake --build ./build --config Release --target all -j$(shell nproc)
|
||||||
|
|
||||||
|
pluginenv:
|
||||||
|
make protocols
|
||||||
|
|
||||||
|
cd subprojects/udis86 && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -H./ -B./build -G Ninja && cmake --build ./build --config Release --target all -j$(shell nproc)
|
||||||
|
|
||||||
|
make fixwlr
|
||||||
|
|
||||||
|
cd subprojects/wlroots && meson ./build --prefix=/usr --buildtype=release -Dwerror=false -Dexamples=false
|
||||||
|
cd subprojects/wlroots && ninja -C build/
|
||||||
|
|
||||||
configdebug:
|
configdebug:
|
||||||
make protocols
|
make protocols
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ Although Hyprland is pretty stable, it may have some bugs.
|
||||||
# Features
|
# Features
|
||||||
|
|
||||||
- Easily expandable and readable codebase
|
- Easily expandable and readable codebase
|
||||||
|
- Plugin support
|
||||||
- Config reloaded instantly upon saving
|
- Config reloaded instantly upon saving
|
||||||
- Custom bezier curve based animations
|
- Custom bezier curve based animations
|
||||||
- Dual Kawase blur
|
- Dual Kawase blur
|
||||||
|
|
8
example/examplePlugin/Makefile
Normal file
8
example/examplePlugin/Makefile
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# compile with HYPRLAND_HEADERS=<path_to_hl> make all
|
||||||
|
# make sure that the path above is to the root hl repo directory, NOT src/
|
||||||
|
# and that you have ran `make protocols` in the hl dir.
|
||||||
|
|
||||||
|
all:
|
||||||
|
g++ -shared -fPIC --no-gnu-unique main.cpp customLayout.cpp customDecoration.cpp -o examplePlugin.so -g -I "/usr/include/pixman-1" -I "/usr/include/libdrm" -I "${HYPRLAND_HEADERS}" -std=c++23
|
||||||
|
clean:
|
||||||
|
rm ./examplePlugin.so
|
74
example/examplePlugin/customDecoration.cpp
Normal file
74
example/examplePlugin/customDecoration.cpp
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
#include "customDecoration.hpp"
|
||||||
|
#include "../../src/Window.hpp"
|
||||||
|
#include "../../src/Compositor.hpp"
|
||||||
|
#include "globals.hpp"
|
||||||
|
|
||||||
|
CCustomDecoration::CCustomDecoration(CWindow* pWindow) {
|
||||||
|
m_pWindow = pWindow;
|
||||||
|
m_vLastWindowPos = pWindow->m_vRealPosition.vec();
|
||||||
|
m_vLastWindowSize = pWindow->m_vRealSize.vec();
|
||||||
|
}
|
||||||
|
|
||||||
|
CCustomDecoration::~CCustomDecoration() {
|
||||||
|
damageEntire();
|
||||||
|
}
|
||||||
|
|
||||||
|
SWindowDecorationExtents CCustomDecoration::getWindowDecorationExtents() {
|
||||||
|
return m_seExtents;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCustomDecoration::draw(CMonitor* pMonitor, float a, const Vector2D& offset) {
|
||||||
|
if (!g_pCompositor->windowValidMapped(m_pWindow))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!m_pWindow->m_sSpecialRenderData.decorate)
|
||||||
|
return;
|
||||||
|
|
||||||
|
static auto* const PCOLOR = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:example:border_color")->intValue;
|
||||||
|
static auto* const PROUNDING = &HyprlandAPI::getConfigValue(PHANDLE, "decoration:rounding")->intValue;
|
||||||
|
static auto* const PBORDERSIZE = &HyprlandAPI::getConfigValue(PHANDLE, "general:border_size")->intValue;
|
||||||
|
|
||||||
|
const auto ROUNDING = !m_pWindow->m_sSpecialRenderData.rounding ?
|
||||||
|
0 :
|
||||||
|
(m_pWindow->m_sAdditionalConfigData.rounding.toUnderlying() == -1 ? *PROUNDING : m_pWindow->m_sAdditionalConfigData.rounding.toUnderlying());
|
||||||
|
|
||||||
|
// draw the border
|
||||||
|
wlr_box fullBox = {(int)(m_vLastWindowPos.x - *PBORDERSIZE), (int)(m_vLastWindowPos.y - *PBORDERSIZE), (int)(m_vLastWindowSize.x + 2.0 * *PBORDERSIZE),
|
||||||
|
(int)(m_vLastWindowSize.y + 2.0 * *PBORDERSIZE)};
|
||||||
|
|
||||||
|
fullBox.x -= pMonitor->vecPosition.x;
|
||||||
|
fullBox.y -= pMonitor->vecPosition.y;
|
||||||
|
|
||||||
|
m_seExtents = {{m_vLastWindowPos.x - fullBox.x - pMonitor->vecPosition.x + 2, m_vLastWindowPos.y - fullBox.y - pMonitor->vecPosition.y + 2},
|
||||||
|
{fullBox.x + fullBox.width + pMonitor->vecPosition.x - m_vLastWindowPos.x - m_vLastWindowSize.x + 2,
|
||||||
|
fullBox.y + fullBox.height + pMonitor->vecPosition.y - m_vLastWindowPos.y - m_vLastWindowSize.y + 2}};
|
||||||
|
|
||||||
|
fullBox.x += offset.x;
|
||||||
|
fullBox.y += offset.y;
|
||||||
|
|
||||||
|
if (fullBox.width < 1 || fullBox.height < 1)
|
||||||
|
return; // don't draw invisible shadows
|
||||||
|
|
||||||
|
g_pHyprOpenGL->scissor((wlr_box*)nullptr);
|
||||||
|
|
||||||
|
scaleBox(&fullBox, pMonitor->scale);
|
||||||
|
g_pHyprOpenGL->renderBorder(&fullBox, CColor(*PCOLOR), *PROUNDING * pMonitor->scale + *PBORDERSIZE * 2, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
eDecorationType CCustomDecoration::getDecorationType() {
|
||||||
|
return DECORATION_CUSTOM;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCustomDecoration::updateWindow(CWindow* pWindow) {
|
||||||
|
|
||||||
|
m_vLastWindowPos = pWindow->m_vRealPosition.vec();
|
||||||
|
m_vLastWindowSize = pWindow->m_vRealSize.vec();
|
||||||
|
|
||||||
|
damageEntire();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCustomDecoration::damageEntire() {
|
||||||
|
wlr_box dm = {(int)(m_vLastWindowPos.x - m_seExtents.topLeft.x), (int)(m_vLastWindowPos.y - m_seExtents.topLeft.y),
|
||||||
|
(int)(m_vLastWindowSize.x + m_seExtents.topLeft.x + m_seExtents.bottomRight.x), (int)m_seExtents.topLeft.y};
|
||||||
|
g_pHyprRenderer->damageBox(&dm);
|
||||||
|
}
|
29
example/examplePlugin/customDecoration.hpp
Normal file
29
example/examplePlugin/customDecoration.hpp
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define WLR_USE_UNSTABLE
|
||||||
|
|
||||||
|
#include "../../src/render/decorations/IHyprWindowDecoration.hpp"
|
||||||
|
|
||||||
|
class CCustomDecoration : public IHyprWindowDecoration {
|
||||||
|
public:
|
||||||
|
CCustomDecoration(CWindow*);
|
||||||
|
virtual ~CCustomDecoration();
|
||||||
|
|
||||||
|
virtual SWindowDecorationExtents getWindowDecorationExtents();
|
||||||
|
|
||||||
|
virtual void draw(CMonitor*, float a, const Vector2D& offset);
|
||||||
|
|
||||||
|
virtual eDecorationType getDecorationType();
|
||||||
|
|
||||||
|
virtual void updateWindow(CWindow*);
|
||||||
|
|
||||||
|
virtual void damageEntire();
|
||||||
|
|
||||||
|
private:
|
||||||
|
SWindowDecorationExtents m_seExtents;
|
||||||
|
|
||||||
|
CWindow* m_pWindow = nullptr;
|
||||||
|
|
||||||
|
Vector2D m_vLastWindowPos;
|
||||||
|
Vector2D m_vLastWindowSize;
|
||||||
|
};
|
80
example/examplePlugin/customLayout.cpp
Normal file
80
example/examplePlugin/customLayout.cpp
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
#include "customLayout.hpp"
|
||||||
|
#include "../../src/Compositor.hpp"
|
||||||
|
#include "globals.hpp"
|
||||||
|
|
||||||
|
void CHyprCustomLayout::onWindowCreatedTiling(CWindow* pWindow) {
|
||||||
|
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
|
||||||
|
const auto SIZE = PMONITOR->vecSize;
|
||||||
|
|
||||||
|
// these are used for focus and move calculations, and are *required* to touch for moving focus to work properly.
|
||||||
|
pWindow->m_vPosition = Vector2D{(SIZE.x / 2.0) * (m_vWindowData.size() % 2), (SIZE.y / 2.0) * (int)(m_vWindowData.size() > 1)};
|
||||||
|
pWindow->m_vSize = SIZE / 2.0;
|
||||||
|
|
||||||
|
// this is the actual pos and size of the window (where it's rendered)
|
||||||
|
pWindow->m_vRealPosition = pWindow->m_vPosition + Vector2D{10, 10};
|
||||||
|
pWindow->m_vRealSize = pWindow->m_vSize - Vector2D{20, 20};
|
||||||
|
|
||||||
|
const auto PDATA = &m_vWindowData.emplace_back();
|
||||||
|
PDATA->pWindow = pWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHyprCustomLayout::onWindowRemovedTiling(CWindow* pWindow) {
|
||||||
|
std::erase_if(m_vWindowData, [&](const auto& other) { return other.pWindow == pWindow; });
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CHyprCustomLayout::isWindowTiled(CWindow* pWindow) {
|
||||||
|
return std::find_if(m_vWindowData.begin(), m_vWindowData.end(), [&](const auto& other) { return other.pWindow == pWindow; }) != m_vWindowData.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHyprCustomLayout::recalculateMonitor(const int& eIdleInhibitMode) {
|
||||||
|
; // empty
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHyprCustomLayout::recalculateWindow(CWindow* pWindow) {
|
||||||
|
; // empty
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHyprCustomLayout::resizeActiveWindow(const Vector2D& delta, CWindow* pWindow) {
|
||||||
|
; // empty
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHyprCustomLayout::fullscreenRequestForWindow(CWindow* pWindow, eFullscreenMode mode, bool on) {
|
||||||
|
; // empty
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any CHyprCustomLayout::layoutMessage(SLayoutMessageHeader header, std::string content) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
SWindowRenderLayoutHints CHyprCustomLayout::requestRenderHints(CWindow* pWindow) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHyprCustomLayout::switchWindows(CWindow* pWindowA, CWindow* pWindowB) {
|
||||||
|
; // empty
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHyprCustomLayout::alterSplitRatio(CWindow* pWindow, float delta, bool exact) {
|
||||||
|
; // empty
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CHyprCustomLayout::getLayoutName() {
|
||||||
|
return "custom";
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHyprCustomLayout::replaceWindowDataWith(CWindow* from, CWindow* to) {
|
||||||
|
; // empty
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHyprCustomLayout::onEnable() {
|
||||||
|
for (auto& w : g_pCompositor->m_vWindows) {
|
||||||
|
if (w->isHidden() || !w->m_bIsMapped || w->m_bFadingOut || w->m_bIsFloating)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
onWindowCreatedTiling(w.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHyprCustomLayout::onDisable() {
|
||||||
|
m_vWindowData.clear();
|
||||||
|
}
|
32
example/examplePlugin/customLayout.hpp
Normal file
32
example/examplePlugin/customLayout.hpp
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define WLR_USE_UNSTABLE
|
||||||
|
|
||||||
|
#include "../../src/layout/IHyprLayout.hpp"
|
||||||
|
|
||||||
|
struct SWindowData {
|
||||||
|
CWindow* pWindow = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CHyprCustomLayout : public IHyprLayout {
|
||||||
|
public:
|
||||||
|
virtual void onWindowCreatedTiling(CWindow*);
|
||||||
|
virtual void onWindowRemovedTiling(CWindow*);
|
||||||
|
virtual bool isWindowTiled(CWindow*);
|
||||||
|
virtual void recalculateMonitor(const int&);
|
||||||
|
virtual void recalculateWindow(CWindow*);
|
||||||
|
virtual void resizeActiveWindow(const Vector2D&, CWindow* pWindow = nullptr);
|
||||||
|
virtual void fullscreenRequestForWindow(CWindow*, eFullscreenMode, bool);
|
||||||
|
virtual std::any layoutMessage(SLayoutMessageHeader, std::string);
|
||||||
|
virtual SWindowRenderLayoutHints requestRenderHints(CWindow*);
|
||||||
|
virtual void switchWindows(CWindow*, CWindow*);
|
||||||
|
virtual void alterSplitRatio(CWindow*, float, bool);
|
||||||
|
virtual std::string getLayoutName();
|
||||||
|
virtual void replaceWindowDataWith(CWindow* from, CWindow* to);
|
||||||
|
|
||||||
|
virtual void onEnable();
|
||||||
|
virtual void onDisable();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<SWindowData> m_vWindowData;
|
||||||
|
};
|
5
example/examplePlugin/globals.hpp
Normal file
5
example/examplePlugin/globals.hpp
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <src/plugins/PluginAPI.hpp>
|
||||||
|
|
||||||
|
inline HANDLE PHANDLE = nullptr;
|
92
example/examplePlugin/main.cpp
Normal file
92
example/examplePlugin/main.cpp
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
#define WLR_USE_UNSTABLE
|
||||||
|
|
||||||
|
#include "globals.hpp"
|
||||||
|
|
||||||
|
#include <src/Window.hpp>
|
||||||
|
#include <src/Compositor.hpp>
|
||||||
|
#include "customLayout.hpp"
|
||||||
|
#include "customDecoration.hpp"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
// Methods
|
||||||
|
inline std::unique_ptr<CHyprCustomLayout> g_pCustomLayout;
|
||||||
|
inline CFunctionHook* g_pFocusHook = nullptr;
|
||||||
|
inline CFunctionHook* g_pMotionHook = nullptr;
|
||||||
|
inline CFunctionHook* g_pMouseDownHook = nullptr;
|
||||||
|
typedef void (*origFocusWindow)(void*, CWindow*, wlr_surface*);
|
||||||
|
typedef void (*origMotion)(wlr_seat*, uint32_t, double, double);
|
||||||
|
typedef void (*origMouseDownNormal)(void*, wlr_pointer_button_event*);
|
||||||
|
|
||||||
|
// Do NOT change this function.
|
||||||
|
APICALL EXPORT std::string PLUGIN_API_VERSION() {
|
||||||
|
return HYPRLAND_API_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onActiveWindowChange(void* self, std::any data) {
|
||||||
|
try {
|
||||||
|
auto* const PWINDOW = std::any_cast<CWindow*>(data);
|
||||||
|
|
||||||
|
HyprlandAPI::addNotification(PHANDLE, "[ExamplePlugin] Active window: " + (PWINDOW ? PWINDOW->m_szTitle : "None"), CColor{0.f, 0.5f, 1.f, 1.f}, 5000);
|
||||||
|
} catch (std::bad_any_cast& e) { HyprlandAPI::addNotification(PHANDLE, "[ExamplePlugin] Active window: None", CColor{0.f, 0.5f, 1.f, 1.f}, 5000); }
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onNewWindow(void* self, std::any data) {
|
||||||
|
auto* const PWINDOW = std::any_cast<CWindow*>(data);
|
||||||
|
|
||||||
|
HyprlandAPI::addWindowDecoration(PHANDLE, PWINDOW, new CCustomDecoration(PWINDOW));
|
||||||
|
}
|
||||||
|
|
||||||
|
void hkFocusWindow(void* thisptr, CWindow* pWindow, wlr_surface* pSurface) {
|
||||||
|
// HyprlandAPI::addNotification(PHANDLE, getFormat("FocusWindow with %lx %lx", pWindow, pSurface), CColor{0.f, 1.f, 1.f, 1.f}, 5000);
|
||||||
|
(*(origFocusWindow)g_pFocusHook->m_pOriginal)(thisptr, pWindow, pSurface);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hkNotifyMotion(wlr_seat* wlr_seat, uint32_t time_msec, double sx, double sy) {
|
||||||
|
// HyprlandAPI::addNotification(PHANDLE, getFormat("NotifyMotion with %lf %lf", sx, sy), CColor{0.f, 1.f, 1.f, 1.f}, 5000);
|
||||||
|
(*(origMotion)g_pMotionHook->m_pOriginal)(wlr_seat, time_msec, sx, sy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hkProcessMouseDownNormal(void* thisptr, wlr_pointer_button_event* e) {
|
||||||
|
// HyprlandAPI::addNotification(PHANDLE, "Mouse down normal!", CColor{0.8f, 0.2f, 0.5f, 1.0f}, 5000);
|
||||||
|
(*(origMouseDownNormal)g_pMouseDownHook->m_pOriginal)(thisptr, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
||||||
|
PHANDLE = handle;
|
||||||
|
|
||||||
|
HyprlandAPI::addNotification(PHANDLE, "Hello World from an example plugin!", CColor{0.f, 1.f, 1.f, 1.f}, 5000);
|
||||||
|
|
||||||
|
HyprlandAPI::registerCallbackDynamic(PHANDLE, "activeWindow", [&](void* self, std::any data) { onActiveWindowChange(self, data); });
|
||||||
|
HyprlandAPI::registerCallbackDynamic(PHANDLE, "openWindow", [&](void* self, std::any data) { onNewWindow(self, data); });
|
||||||
|
|
||||||
|
g_pCustomLayout = std::make_unique<CHyprCustomLayout>();
|
||||||
|
|
||||||
|
HyprlandAPI::addLayout(PHANDLE, "custom", g_pCustomLayout.get());
|
||||||
|
|
||||||
|
HyprlandAPI::addConfigValue(PHANDLE, "plugin:example:border_color", SConfigValue{.intValue = configStringToInt("rgb(44ee44)")});
|
||||||
|
|
||||||
|
HyprlandAPI::addDispatcher(PHANDLE, "example", [](std::string arg) { HyprlandAPI::addNotification(PHANDLE, "Arg passed: " + arg, CColor{0.5f, 0.5f, 0.7f, 1.0f}, 5000); });
|
||||||
|
|
||||||
|
// Hook a public member
|
||||||
|
g_pFocusHook = HyprlandAPI::createFunctionHook(PHANDLE, (void*)&CCompositor::focusWindow, (void*)&hkFocusWindow);
|
||||||
|
// Hook a public non-member
|
||||||
|
g_pMotionHook = HyprlandAPI::createFunctionHook(PHANDLE, (void*)&wlr_seat_pointer_notify_motion, (void*)&hkNotifyMotion);
|
||||||
|
// Hook a private member (!WARNING: the signature may differ in clang. This one is for gcc ONLY.)
|
||||||
|
g_pMouseDownHook = HyprlandAPI::createFunctionHook(
|
||||||
|
PHANDLE, HyprlandAPI::getFunctionAddressFromSignature(PHANDLE, "_ZN13CInputManager22processMouseDownNormalEP24wlr_pointer_button_event"), (void*)&hkProcessMouseDownNormal);
|
||||||
|
|
||||||
|
// Enable our hooks
|
||||||
|
g_pFocusHook->hook();
|
||||||
|
g_pMotionHook->hook();
|
||||||
|
g_pMouseDownHook->hook();
|
||||||
|
|
||||||
|
HyprlandAPI::reloadConfig();
|
||||||
|
|
||||||
|
return {"ExamplePlugin", "An example plugin", "Vaxry", "1.0"};
|
||||||
|
}
|
||||||
|
|
||||||
|
APICALL EXPORT void PLUGIN_EXIT() {
|
||||||
|
HyprlandAPI::invokeHyprctlCommand("seterror", "disable");
|
||||||
|
}
|
|
@ -52,11 +52,14 @@
|
||||||
version = props.version + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
|
version = props.version + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
|
||||||
wlroots = wlroots-hyprland;
|
wlroots = wlroots-hyprland;
|
||||||
inherit (inputs.hyprland-protocols.packages.${prev.stdenv.hostPlatform.system}) hyprland-protocols;
|
inherit (inputs.hyprland-protocols.packages.${prev.stdenv.hostPlatform.system}) hyprland-protocols;
|
||||||
|
inherit udis86;
|
||||||
};
|
};
|
||||||
hyprland-debug = hyprland.override {debug = true;};
|
hyprland-debug = hyprland.override {debug = true;};
|
||||||
hyprland-no-hidpi = hyprland.override {hidpiXWayland = false;};
|
hyprland-no-hidpi = hyprland.override {hidpiXWayland = false;};
|
||||||
hyprland-nvidia = hyprland.override {nvidiaPatches = true;};
|
hyprland-nvidia = hyprland.override {nvidiaPatches = true;};
|
||||||
|
|
||||||
|
udis86 = prev.callPackage ./nix/udis86.nix {};
|
||||||
|
|
||||||
waybar-hyprland = prev.waybar.overrideAttrs (oldAttrs: {
|
waybar-hyprland = prev.waybar.overrideAttrs (oldAttrs: {
|
||||||
postPatch = ''
|
postPatch = ''
|
||||||
# use hyprctl to switch workspaces
|
# use hyprctl to switch workspaces
|
||||||
|
|
|
@ -41,6 +41,7 @@ commands:
|
||||||
switchxkblayout
|
switchxkblayout
|
||||||
seterror
|
seterror
|
||||||
setprop
|
setprop
|
||||||
|
plugin
|
||||||
|
|
||||||
flags:
|
flags:
|
||||||
-j -> output in JSON
|
-j -> output in JSON
|
||||||
|
@ -349,6 +350,8 @@ int main(int argc, char** argv) {
|
||||||
request(fullRequest, 1);
|
request(fullRequest, 1);
|
||||||
else if (fullRequest.contains("/setprop"))
|
else if (fullRequest.contains("/setprop"))
|
||||||
request(fullRequest, 3);
|
request(fullRequest, 3);
|
||||||
|
else if (fullRequest.contains("/plugin"))
|
||||||
|
request(fullRequest, 1);
|
||||||
else if (fullRequest.contains("/output"))
|
else if (fullRequest.contains("/output"))
|
||||||
exitStatus = outputRequest(argc, argv);
|
exitStatus = outputRequest(argc, argv);
|
||||||
else if (fullRequest.contains("/setcursor"))
|
else if (fullRequest.contains("/setcursor"))
|
||||||
|
|
|
@ -43,6 +43,10 @@ wlroots = subproject('wlroots', default_options: ['examples=false'])
|
||||||
have_xwlr = wlroots.get_variable('features').get('xwayland')
|
have_xwlr = wlroots.get_variable('features').get('xwayland')
|
||||||
xcb_dep = dependency('xcb', required: get_option('xwayland'))
|
xcb_dep = dependency('xcb', required: get_option('xwayland'))
|
||||||
|
|
||||||
|
cmake = import('cmake')
|
||||||
|
udis = cmake.subproject('udis86')
|
||||||
|
udis86 = udis.dependency('libudis86')
|
||||||
|
|
||||||
if get_option('xwayland').enabled() and not have_xwlr
|
if get_option('xwayland').enabled() and not have_xwlr
|
||||||
error('Cannot enable Xwayland in Hyprland: wlroots has been built without Xwayland support')
|
error('Cannot enable Xwayland in Hyprland: wlroots has been built without Xwayland support')
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
mount,
|
mount,
|
||||||
pciutils,
|
pciutils,
|
||||||
systemd,
|
systemd,
|
||||||
|
udis86,
|
||||||
wayland,
|
wayland,
|
||||||
wayland-protocols,
|
wayland-protocols,
|
||||||
wayland-scanner,
|
wayland-scanner,
|
||||||
|
@ -72,6 +73,7 @@ in
|
||||||
libinput
|
libinput
|
||||||
libxkbcommon
|
libxkbcommon
|
||||||
mesa
|
mesa
|
||||||
|
udis86
|
||||||
wayland
|
wayland
|
||||||
wayland-protocols
|
wayland-protocols
|
||||||
wayland-scanner
|
wayland-scanner
|
||||||
|
|
|
@ -1,34 +1,50 @@
|
||||||
diff --git a/meson.build b/meson.build
|
diff --git a/meson.build b/meson.build
|
||||||
index 22ee4bf..5528613 100644
|
index f380255..abd7cd3 100644
|
||||||
--- a/meson.build
|
--- a/meson.build
|
||||||
+++ b/meson.build
|
+++ b/meson.build
|
||||||
@@ -2,16 +2,10 @@ project('Hyprland', 'cpp', 'c',
|
@@ -39,23 +39,8 @@ add_project_arguments(
|
||||||
version : '0.1',
|
],
|
||||||
default_options : ['warning_level=3', 'cpp_std=c++20', 'default_library=static'])
|
language: 'cpp')
|
||||||
|
|
||||||
-wlroots = subproject('wlroots', default_options: ['examples=false'])
|
-wlroots = subproject('wlroots', default_options: ['examples=false'])
|
||||||
-have_xwlr = wlroots.get_variable('features').get('xwayland')
|
-have_xwlr = wlroots.get_variable('features').get('xwayland')
|
||||||
+wlroots = dependency('wlroots', version: '>=0.16.0')
|
|
||||||
xcb_dep = dependency('xcb', required: get_option('xwayland'))
|
xcb_dep = dependency('xcb', required: get_option('xwayland'))
|
||||||
|
|
||||||
|
-cmake = import('cmake')
|
||||||
|
-udis = cmake.subproject('udis86')
|
||||||
|
-udis86 = udis.dependency('libudis86')
|
||||||
|
-
|
||||||
-if get_option('xwayland').enabled() and not have_xwlr
|
-if get_option('xwayland').enabled() and not have_xwlr
|
||||||
- error('Cannot enable Xwayland in Hyprland: wlroots has been built without Xwayland support')
|
- error('Cannot enable Xwayland in Hyprland: wlroots has been built without Xwayland support')
|
||||||
-endif
|
-endif
|
||||||
-have_xwayland = xcb_dep.found() and have_xwlr
|
-have_xwayland = xcb_dep.found() and have_xwlr
|
||||||
-
|
-
|
||||||
-if not have_xwayland
|
-if not have_xwayland
|
||||||
+if not xcb_dep.found()
|
-add_project_arguments('-DNO_XWAYLAND', language: 'cpp')
|
||||||
add_project_arguments('-DNO_XWAYLAND', language: 'cpp')
|
-endif
|
||||||
endif
|
-
|
||||||
|
backtrace_dep = cpp_compiler.find_library('execinfo', required: false)
|
||||||
|
systemd_dep = dependency('libsystemd', required: get_option('systemd'))
|
||||||
|
|
||||||
diff --git a/src/meson.build b/src/meson.build
|
diff --git a/src/meson.build b/src/meson.build
|
||||||
index 5d64188..a676333 100644
|
index 7b658d3..da8baa5 100644
|
||||||
--- a/src/meson.build
|
--- a/src/meson.build
|
||||||
+++ b/src/meson.build
|
+++ b/src/meson.build
|
||||||
@@ -7,5 +7,5 @@ executable('Hyprland', src,
|
@@ -7,7 +7,7 @@ executable('Hyprland', src,
|
||||||
server_protos,
|
server_protos,
|
||||||
dependency('wayland-server'),
|
dependency('wayland-server'),
|
||||||
dependency('wayland-client'),
|
dependency('wayland-client'),
|
||||||
- wlroots.get_variable('wlroots'),
|
- wlroots.get_variable('wlroots'),
|
||||||
+ wlroots,
|
+ dependency('wlroots'),
|
||||||
dependency('cairo'),
|
dependency('cairo'),
|
||||||
|
dependency('libdrm'),
|
||||||
|
dependency('egl'),
|
||||||
|
@@ -16,7 +16,7 @@ executable('Hyprland', src,
|
||||||
|
xcb_dep,
|
||||||
|
backtrace_dep,
|
||||||
|
systemd_dep,
|
||||||
|
- udis86,
|
||||||
|
+ dependency('udis86'),
|
||||||
|
|
||||||
|
dependency('pixman-1'),
|
||||||
|
dependency('gl', 'opengl'),
|
||||||
|
|
32
nix/udis86.nix
Normal file
32
nix/udis86.nix
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
stdenv,
|
||||||
|
fetchFromGitHub,
|
||||||
|
autoreconfHook,
|
||||||
|
python3,
|
||||||
|
}:
|
||||||
|
stdenv.mkDerivation {
|
||||||
|
pname = "udis86";
|
||||||
|
version = "unstable-2022-10-13";
|
||||||
|
|
||||||
|
src = fetchFromGitHub {
|
||||||
|
owner = "canihavesomecoffee";
|
||||||
|
repo = "udis86";
|
||||||
|
rev = "5336633af70f3917760a6d441ff02d93477b0c86";
|
||||||
|
hash = "sha256-HifdUQPGsKQKQprByeIznvRLONdOXeolOsU5nkwIv3g=";
|
||||||
|
};
|
||||||
|
|
||||||
|
nativeBuildInputs = [autoreconfHook python3];
|
||||||
|
|
||||||
|
configureFlags = ["--enable-shared"];
|
||||||
|
|
||||||
|
outputs = ["bin" "out" "dev" "lib"];
|
||||||
|
|
||||||
|
meta = with lib; {
|
||||||
|
homepage = "https://udis86.sourceforge.net";
|
||||||
|
license = licenses.bsd2;
|
||||||
|
mainProgram = "udcli";
|
||||||
|
description = "Easy-to-use, minimalistic x86 disassembler library (libudis86)";
|
||||||
|
platforms = platforms.all;
|
||||||
|
};
|
||||||
|
}
|
|
@ -18,6 +18,12 @@ int handleCritSignal(int signo, void* data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleSegv(int sig) {
|
void handleSegv(int sig) {
|
||||||
|
|
||||||
|
if (g_pHookSystem->m_bCurrentEventPlugin) {
|
||||||
|
longjmp(g_pHookSystem->m_jbHookFaultJumpBuf, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
CrashReporter::createAndSaveCrash();
|
CrashReporter::createAndSaveCrash();
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
@ -279,6 +285,10 @@ void CCompositor::cleanup() {
|
||||||
|
|
||||||
m_bIsShuttingDown = true;
|
m_bIsShuttingDown = true;
|
||||||
|
|
||||||
|
// unload all remaining plugins while the compositor is
|
||||||
|
// still in a normal working state.
|
||||||
|
g_pPluginSystem->unloadAllPlugins();
|
||||||
|
|
||||||
m_pLastFocus = nullptr;
|
m_pLastFocus = nullptr;
|
||||||
m_pLastWindow = nullptr;
|
m_pLastWindow = nullptr;
|
||||||
|
|
||||||
|
@ -364,6 +374,12 @@ void CCompositor::startCompositor() {
|
||||||
|
|
||||||
Debug::log(LOG, "Creating the HyprDebugOverlay!");
|
Debug::log(LOG, "Creating the HyprDebugOverlay!");
|
||||||
g_pDebugOverlay = std::make_unique<CHyprDebugOverlay>();
|
g_pDebugOverlay = std::make_unique<CHyprDebugOverlay>();
|
||||||
|
|
||||||
|
Debug::log(LOG, "Creating the HyprNotificationOverlay!");
|
||||||
|
g_pHyprNotificationOverlay = std::make_unique<CHyprNotificationOverlay>();
|
||||||
|
|
||||||
|
Debug::log(LOG, "Creating the PluginSystem!");
|
||||||
|
g_pPluginSystem = std::make_unique<CPluginSystem>();
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
|
||||||
|
@ -486,10 +502,6 @@ CMonitor* CCompositor::getMonitorFromVector(const Vector2D& point) {
|
||||||
|
|
||||||
void CCompositor::removeWindowFromVectorSafe(CWindow* pWindow) {
|
void CCompositor::removeWindowFromVectorSafe(CWindow* pWindow) {
|
||||||
if (windowExists(pWindow) && !pWindow->m_bFadingOut) {
|
if (windowExists(pWindow) && !pWindow->m_bFadingOut) {
|
||||||
if (pWindow->m_bIsX11 && pWindow->m_iX11Type == 2) {
|
|
||||||
std::erase_if(m_dUnmanagedX11Windows, [&](std::unique_ptr<CWindow>& el) { return el.get() == pWindow; });
|
|
||||||
}
|
|
||||||
|
|
||||||
// if X11, also check its children
|
// if X11, also check its children
|
||||||
// and delete any needed
|
// and delete any needed
|
||||||
if (pWindow->m_bIsX11) {
|
if (pWindow->m_bIsX11) {
|
||||||
|
@ -507,6 +519,10 @@ void CCompositor::removeWindowFromVectorSafe(CWindow* pWindow) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pWindow->m_bIsX11 && pWindow->m_iX11Type == 2) {
|
||||||
|
std::erase_if(m_dUnmanagedX11Windows, [&](std::unique_ptr<CWindow>& el) { return el.get() == pWindow; });
|
||||||
|
}
|
||||||
|
|
||||||
std::erase_if(m_vWindows, [&](std::unique_ptr<CWindow>& el) { return el.get() == pWindow; });
|
std::erase_if(m_vWindows, [&](std::unique_ptr<CWindow>& el) { return el.get() == pWindow; });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -517,6 +533,11 @@ bool CCompositor::windowExists(CWindow* pWindow) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto& u : m_dUnmanagedX11Windows) {
|
||||||
|
if (u.get() == pWindow)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -591,8 +612,8 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos) {
|
||||||
// special workspace
|
// special workspace
|
||||||
if (PMONITOR->specialWorkspaceID) {
|
if (PMONITOR->specialWorkspaceID) {
|
||||||
for (auto w = m_vWindows.rbegin(); w != m_vWindows.rend(); w++) {
|
for (auto w = m_vWindows.rbegin(); w != m_vWindows.rend(); w++) {
|
||||||
wlr_box box = {(*w)->m_vRealPosition.vec().x - BORDER_GRAB_AREA, (*w)->m_vRealPosition.vec().y - BORDER_GRAB_AREA, (*w)->m_vRealSize.vec().x + 2 * BORDER_GRAB_AREA,
|
const auto BB = w->get()->getWindowInputBox();
|
||||||
(*w)->m_vRealSize.vec().y + 2 * BORDER_GRAB_AREA};
|
wlr_box box = {BB.x - BORDER_GRAB_AREA, BB.y - BORDER_GRAB_AREA, BB.width + 2 * BORDER_GRAB_AREA, BB.height + 2 * BORDER_GRAB_AREA};
|
||||||
if ((*w)->m_bIsFloating && (*w)->m_iWorkspaceID == PMONITOR->specialWorkspaceID && (*w)->m_bIsMapped && wlr_box_contains_point(&box, pos.x, pos.y) &&
|
if ((*w)->m_bIsFloating && (*w)->m_iWorkspaceID == PMONITOR->specialWorkspaceID && (*w)->m_bIsMapped && wlr_box_contains_point(&box, pos.x, pos.y) &&
|
||||||
!(*w)->isHidden() && !(*w)->m_bX11ShouldntFocus)
|
!(*w)->isHidden() && !(*w)->m_bX11ShouldntFocus)
|
||||||
return (*w).get();
|
return (*w).get();
|
||||||
|
@ -608,8 +629,8 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos) {
|
||||||
|
|
||||||
// pinned windows on top of floating regardless
|
// pinned windows on top of floating regardless
|
||||||
for (auto w = m_vWindows.rbegin(); w != m_vWindows.rend(); w++) {
|
for (auto w = m_vWindows.rbegin(); w != m_vWindows.rend(); w++) {
|
||||||
wlr_box box = {(*w)->m_vRealPosition.vec().x - BORDER_GRAB_AREA, (*w)->m_vRealPosition.vec().y - BORDER_GRAB_AREA, (*w)->m_vRealSize.vec().x + 2 * BORDER_GRAB_AREA,
|
const auto BB = w->get()->getWindowInputBox();
|
||||||
(*w)->m_vRealSize.vec().y + 2 * BORDER_GRAB_AREA};
|
wlr_box box = {BB.x - BORDER_GRAB_AREA, BB.y - BORDER_GRAB_AREA, BB.width + 2 * BORDER_GRAB_AREA, BB.height + 2 * BORDER_GRAB_AREA};
|
||||||
if ((*w)->m_bIsFloating && (*w)->m_bIsMapped && !(*w)->isHidden() && !(*w)->m_bX11ShouldntFocus && (*w)->m_bPinned) {
|
if ((*w)->m_bIsFloating && (*w)->m_bIsMapped && !(*w)->isHidden() && !(*w)->m_bX11ShouldntFocus && (*w)->m_bPinned) {
|
||||||
if (wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y))
|
if (wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y))
|
||||||
return w->get();
|
return w->get();
|
||||||
|
@ -623,8 +644,8 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos) {
|
||||||
|
|
||||||
// first loop over floating cuz they're above, m_lWindows should be sorted bottom->top, for tiled it doesn't matter.
|
// first loop over floating cuz they're above, m_lWindows should be sorted bottom->top, for tiled it doesn't matter.
|
||||||
for (auto w = m_vWindows.rbegin(); w != m_vWindows.rend(); w++) {
|
for (auto w = m_vWindows.rbegin(); w != m_vWindows.rend(); w++) {
|
||||||
wlr_box box = {(*w)->m_vRealPosition.vec().x - BORDER_GRAB_AREA, (*w)->m_vRealPosition.vec().y - BORDER_GRAB_AREA, (*w)->m_vRealSize.vec().x + 2 * BORDER_GRAB_AREA,
|
const auto BB = w->get()->getWindowInputBox();
|
||||||
(*w)->m_vRealSize.vec().y + 2 * BORDER_GRAB_AREA};
|
wlr_box box = {BB.x - BORDER_GRAB_AREA, BB.y - BORDER_GRAB_AREA, BB.width + 2 * BORDER_GRAB_AREA, BB.height + 2 * BORDER_GRAB_AREA};
|
||||||
if ((*w)->m_bIsFloating && (*w)->m_bIsMapped && isWorkspaceVisible((*w)->m_iWorkspaceID) && !(*w)->isHidden() && !(*w)->m_bPinned) {
|
if ((*w)->m_bIsFloating && (*w)->m_bIsMapped && isWorkspaceVisible((*w)->m_iWorkspaceID) && !(*w)->isHidden() && !(*w)->m_bPinned) {
|
||||||
// OR windows should add focus to parent
|
// OR windows should add focus to parent
|
||||||
if ((*w)->m_bX11ShouldntFocus && (*w)->m_iX11Type != 2)
|
if ((*w)->m_bX11ShouldntFocus && (*w)->m_iX11Type != 2)
|
||||||
|
@ -1656,6 +1677,9 @@ void CCompositor::updateWindowAnimatedDecorationValues(CWindow* pWindow) {
|
||||||
} else {
|
} else {
|
||||||
pWindow->m_cRealShadowColor.setValueAndWarp(CColor(0, 0, 0, 0)); // no shadow
|
pWindow->m_cRealShadowColor.setValueAndWarp(CColor(0, 0, 0, 0)); // no shadow
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto& d : pWindow->m_dWindowDecorations)
|
||||||
|
d->updateWindow(pWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
int CCompositor::getNextAvailableMonitorID() {
|
int CCompositor::getNextAvailableMonitorID() {
|
||||||
|
@ -2012,6 +2036,9 @@ void CCompositor::scheduleFrameForMonitor(CMonitor* pMonitor) {
|
||||||
if (!pMonitor->m_bEnabled)
|
if (!pMonitor->m_bEnabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (pMonitor->renderingActive)
|
||||||
|
pMonitor->pendingFrame = true;
|
||||||
|
|
||||||
wlr_output_schedule_frame(pMonitor->output);
|
wlr_output_schedule_frame(pMonitor->output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2032,7 +2059,7 @@ CWindow* CCompositor::getWindowByRegex(const std::string& regexp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& w : g_pCompositor->m_vWindows) {
|
for (auto& w : g_pCompositor->m_vWindows) {
|
||||||
if (!w->m_bIsMapped || w->isHidden())
|
if (!w->m_bIsMapped || (w->isHidden() && !w->m_sGroupData.pNextWindow))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
|
|
|
@ -19,12 +19,14 @@
|
||||||
#include "managers/SessionLockManager.hpp"
|
#include "managers/SessionLockManager.hpp"
|
||||||
#include "managers/HookSystemManager.hpp"
|
#include "managers/HookSystemManager.hpp"
|
||||||
#include "debug/HyprDebugOverlay.hpp"
|
#include "debug/HyprDebugOverlay.hpp"
|
||||||
|
#include "debug/HyprNotificationOverlay.hpp"
|
||||||
#include "helpers/Monitor.hpp"
|
#include "helpers/Monitor.hpp"
|
||||||
#include "helpers/Workspace.hpp"
|
#include "helpers/Workspace.hpp"
|
||||||
#include "Window.hpp"
|
#include "Window.hpp"
|
||||||
#include "render/Renderer.hpp"
|
#include "render/Renderer.hpp"
|
||||||
#include "render/OpenGL.hpp"
|
#include "render/OpenGL.hpp"
|
||||||
#include "hyprerror/HyprError.hpp"
|
#include "hyprerror/HyprError.hpp"
|
||||||
|
#include "plugins/PluginSystem.hpp"
|
||||||
|
|
||||||
class CCompositor {
|
class CCompositor {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -88,6 +88,59 @@ wlr_box CWindow::getWindowIdealBoundingBoxIgnoreReserved() {
|
||||||
return wlr_box{(int)POS.x, (int)POS.y, (int)SIZE.x, (int)SIZE.y};
|
return wlr_box{(int)POS.x, (int)POS.y, (int)SIZE.x, (int)SIZE.y};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wlr_box CWindow::getWindowInputBox() {
|
||||||
|
static auto* const PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
|
||||||
|
|
||||||
|
if (m_sAdditionalConfigData.dimAround) {
|
||||||
|
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
|
||||||
|
return {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
|
||||||
|
}
|
||||||
|
|
||||||
|
SWindowDecorationExtents maxExtents = {{*PBORDERSIZE + 2, *PBORDERSIZE + 2}, {*PBORDERSIZE + 2, *PBORDERSIZE + 2}};
|
||||||
|
|
||||||
|
for (auto& wd : m_dWindowDecorations) {
|
||||||
|
|
||||||
|
if (!wd->allowsInput())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto EXTENTS = wd->getWindowDecorationExtents();
|
||||||
|
|
||||||
|
if (EXTENTS.topLeft.x > maxExtents.topLeft.x)
|
||||||
|
maxExtents.topLeft.x = EXTENTS.topLeft.x;
|
||||||
|
|
||||||
|
if (EXTENTS.topLeft.y > maxExtents.topLeft.y)
|
||||||
|
maxExtents.topLeft.y = EXTENTS.topLeft.y;
|
||||||
|
|
||||||
|
if (EXTENTS.bottomRight.x > maxExtents.bottomRight.x)
|
||||||
|
maxExtents.bottomRight.x = EXTENTS.bottomRight.x;
|
||||||
|
|
||||||
|
if (EXTENTS.bottomRight.y > maxExtents.bottomRight.y)
|
||||||
|
maxExtents.bottomRight.y = EXTENTS.bottomRight.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add extents to the real base BB and return
|
||||||
|
wlr_box finalBox = {m_vRealPosition.vec().x - maxExtents.topLeft.x, m_vRealPosition.vec().y - maxExtents.topLeft.y,
|
||||||
|
m_vRealSize.vec().x + maxExtents.topLeft.x + maxExtents.bottomRight.x, m_vRealSize.vec().y + maxExtents.topLeft.y + maxExtents.bottomRight.y};
|
||||||
|
|
||||||
|
return finalBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
SWindowDecorationExtents CWindow::getFullWindowReservedArea() {
|
||||||
|
SWindowDecorationExtents extents;
|
||||||
|
|
||||||
|
for (auto& wd : m_dWindowDecorations) {
|
||||||
|
const auto RESERVED = wd->getWindowDecorationReservedArea();
|
||||||
|
|
||||||
|
if (RESERVED.bottomRight == Vector2D{} && RESERVED.topLeft == Vector2D{})
|
||||||
|
continue;
|
||||||
|
|
||||||
|
extents.topLeft = extents.topLeft + RESERVED.topLeft;
|
||||||
|
extents.bottomRight = extents.bottomRight + RESERVED.bottomRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
return extents;
|
||||||
|
}
|
||||||
|
|
||||||
void CWindow::updateWindowDecos() {
|
void CWindow::updateWindowDecos() {
|
||||||
for (auto& wd : m_dWindowDecorations)
|
for (auto& wd : m_dWindowDecorations)
|
||||||
wd->updateWindow(this);
|
wd->updateWindow(this);
|
||||||
|
|
|
@ -155,6 +155,7 @@ class CWindow {
|
||||||
DYNLISTENER(toplevelClose);
|
DYNLISTENER(toplevelClose);
|
||||||
DYNLISTENER(toplevelActivate);
|
DYNLISTENER(toplevelActivate);
|
||||||
DYNLISTENER(toplevelFullscreen);
|
DYNLISTENER(toplevelFullscreen);
|
||||||
|
DYNLISTENER(setOverrideRedirect);
|
||||||
// DYNLISTENER(newSubsurfaceWindow);
|
// DYNLISTENER(newSubsurfaceWindow);
|
||||||
|
|
||||||
union {
|
union {
|
||||||
|
@ -286,34 +287,36 @@ class CWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
// methods
|
// methods
|
||||||
wlr_box getFullWindowBoundingBox();
|
wlr_box getFullWindowBoundingBox();
|
||||||
wlr_box getWindowIdealBoundingBoxIgnoreReserved();
|
wlr_box getWindowInputBox();
|
||||||
void updateWindowDecos();
|
wlr_box getWindowIdealBoundingBoxIgnoreReserved();
|
||||||
pid_t getPID();
|
void updateWindowDecos();
|
||||||
IHyprWindowDecoration* getDecorationByType(eDecorationType);
|
pid_t getPID();
|
||||||
void removeDecorationByType(eDecorationType);
|
IHyprWindowDecoration* getDecorationByType(eDecorationType);
|
||||||
void createToplevelHandle();
|
void removeDecorationByType(eDecorationType);
|
||||||
void destroyToplevelHandle();
|
void createToplevelHandle();
|
||||||
void updateToplevel();
|
void destroyToplevelHandle();
|
||||||
void updateSurfaceOutputs();
|
void updateToplevel();
|
||||||
void moveToWorkspace(int);
|
void updateSurfaceOutputs();
|
||||||
CWindow* X11TransientFor();
|
void moveToWorkspace(int);
|
||||||
void onUnmap();
|
CWindow* X11TransientFor();
|
||||||
void onMap();
|
void onUnmap();
|
||||||
void setHidden(bool hidden);
|
void onMap();
|
||||||
bool isHidden();
|
void setHidden(bool hidden);
|
||||||
void applyDynamicRule(const SWindowRule& r);
|
bool isHidden();
|
||||||
void updateDynamicRules();
|
void applyDynamicRule(const SWindowRule& r);
|
||||||
|
void updateDynamicRules();
|
||||||
|
SWindowDecorationExtents getFullWindowReservedArea();
|
||||||
|
|
||||||
void onBorderAngleAnimEnd(void* ptr);
|
void onBorderAngleAnimEnd(void* ptr);
|
||||||
bool isInCurvedCorner(double x, double y);
|
bool isInCurvedCorner(double x, double y);
|
||||||
bool hasPopupAt(const Vector2D& pos);
|
bool hasPopupAt(const Vector2D& pos);
|
||||||
|
|
||||||
CWindow* getGroupHead();
|
CWindow* getGroupHead();
|
||||||
CWindow* getGroupTail();
|
CWindow* getGroupTail();
|
||||||
CWindow* getGroupCurrent();
|
CWindow* getGroupCurrent();
|
||||||
void setGroupCurrent(CWindow* pWindow);
|
void setGroupCurrent(CWindow* pWindow);
|
||||||
void insertWindowToGroup(CWindow* pWindow);
|
void insertWindowToGroup(CWindow* pWindow);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// For hidden windows and stuff
|
// For hidden windows and stuff
|
||||||
|
|
|
@ -52,21 +52,22 @@ void CConfigManager::setDefaultVars() {
|
||||||
configValues["general:hover_icon_on_border"].intValue = 1;
|
configValues["general:hover_icon_on_border"].intValue = 1;
|
||||||
configValues["general:layout"].strValue = "dwindle";
|
configValues["general:layout"].strValue = "dwindle";
|
||||||
|
|
||||||
configValues["misc:disable_hyprland_logo"].intValue = 0;
|
configValues["misc:disable_hyprland_logo"].intValue = 0;
|
||||||
configValues["misc:disable_splash_rendering"].intValue = 0;
|
configValues["misc:disable_splash_rendering"].intValue = 0;
|
||||||
configValues["misc:vfr"].intValue = 1;
|
configValues["misc:vfr"].intValue = 1;
|
||||||
configValues["misc:vrr"].intValue = 0;
|
configValues["misc:vrr"].intValue = 0;
|
||||||
configValues["misc:mouse_move_enables_dpms"].intValue = 0;
|
configValues["misc:mouse_move_enables_dpms"].intValue = 0;
|
||||||
configValues["misc:always_follow_on_dnd"].intValue = 1;
|
configValues["misc:always_follow_on_dnd"].intValue = 1;
|
||||||
configValues["misc:layers_hog_keyboard_focus"].intValue = 1;
|
configValues["misc:layers_hog_keyboard_focus"].intValue = 1;
|
||||||
configValues["misc:animate_manual_resizes"].intValue = 0;
|
configValues["misc:animate_manual_resizes"].intValue = 1;
|
||||||
configValues["misc:disable_autoreload"].intValue = 0;
|
configValues["misc:animate_mouse_windowdragging"].intValue = 1;
|
||||||
configValues["misc:enable_swallow"].intValue = 0;
|
configValues["misc:disable_autoreload"].intValue = 0;
|
||||||
configValues["misc:swallow_regex"].strValue = STRVAL_EMPTY;
|
configValues["misc:enable_swallow"].intValue = 0;
|
||||||
configValues["misc:focus_on_activate"].intValue = 0;
|
configValues["misc:swallow_regex"].strValue = STRVAL_EMPTY;
|
||||||
configValues["misc:no_direct_scanout"].intValue = 0;
|
configValues["misc:focus_on_activate"].intValue = 0;
|
||||||
configValues["misc:hide_cursor_on_touch"].intValue = 1;
|
configValues["misc:no_direct_scanout"].intValue = 0;
|
||||||
configValues["misc:mouse_move_focuses_monitor"].intValue = 1;
|
configValues["misc:hide_cursor_on_touch"].intValue = 1;
|
||||||
|
configValues["misc:mouse_move_focuses_monitor"].intValue = 1;
|
||||||
|
|
||||||
configValues["debug:int"].intValue = 0;
|
configValues["debug:int"].intValue = 0;
|
||||||
configValues["debug:log_damage"].intValue = 0;
|
configValues["debug:log_damage"].intValue = 0;
|
||||||
|
@ -271,7 +272,7 @@ void CConfigManager::init() {
|
||||||
|
|
||||||
void CConfigManager::configSetValueSafe(const std::string& COMMAND, const std::string& VALUE) {
|
void CConfigManager::configSetValueSafe(const std::string& COMMAND, const std::string& VALUE) {
|
||||||
if (configValues.find(COMMAND) == configValues.end()) {
|
if (configValues.find(COMMAND) == configValues.end()) {
|
||||||
if (COMMAND.find("device:") != 0 /* devices parsed later */) {
|
if (COMMAND.find("device:") != 0 /* devices parsed later */ && COMMAND.find("plugin:") != 0 /* plugins parsed later */) {
|
||||||
if (COMMAND[0] == '$') {
|
if (COMMAND[0] == '$') {
|
||||||
// register a dynamic var
|
// register a dynamic var
|
||||||
Debug::log(LOG, "Registered dynamic var \"%s\" -> %s", COMMAND.c_str(), VALUE.c_str());
|
Debug::log(LOG, "Registered dynamic var \"%s\" -> %s", COMMAND.c_str(), VALUE.c_str());
|
||||||
|
@ -306,6 +307,18 @@ void CConfigManager::configSetValueSafe(const std::string& COMMAND, const std::s
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIGENTRY = &it->second.at(CONFIGVAR);
|
CONFIGENTRY = &it->second.at(CONFIGVAR);
|
||||||
|
} else if (COMMAND.find("plugin:") == 0) {
|
||||||
|
for (auto& [handle, pMap] : pluginConfigs) {
|
||||||
|
auto it = std::find_if(pMap->begin(), pMap->end(), [&](const auto& other) { return other.first == COMMAND; });
|
||||||
|
if (it == pMap->end()) {
|
||||||
|
return; // plugin vars do not err, so we silently ignore.
|
||||||
|
}
|
||||||
|
|
||||||
|
CONFIGENTRY = &it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CONFIGENTRY)
|
||||||
|
return; // silent ignore
|
||||||
} else {
|
} else {
|
||||||
CONFIGENTRY = &configValues.at(COMMAND);
|
CONFIGENTRY = &configValues.at(COMMAND);
|
||||||
}
|
}
|
||||||
|
@ -1649,8 +1662,17 @@ SConfigValue* CConfigManager::getConfigValuePtr(const std::string& val) {
|
||||||
SConfigValue* CConfigManager::getConfigValuePtrSafe(const std::string& val) {
|
SConfigValue* CConfigManager::getConfigValuePtrSafe(const std::string& val) {
|
||||||
const auto IT = configValues.find(val);
|
const auto IT = configValues.find(val);
|
||||||
|
|
||||||
if (IT == configValues.end())
|
if (IT == configValues.end()) {
|
||||||
|
// maybe plugin
|
||||||
|
for (auto& [pl, pMap] : pluginConfigs) {
|
||||||
|
const auto PLIT = pMap->find(val);
|
||||||
|
|
||||||
|
if (PLIT != pMap->end())
|
||||||
|
return &PLIT->second;
|
||||||
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
return &(IT->second);
|
return &(IT->second);
|
||||||
}
|
}
|
||||||
|
@ -1800,3 +1822,19 @@ ICustomConfigValueData::~ICustomConfigValueData() {
|
||||||
std::unordered_map<std::string, SAnimationPropertyConfig> CConfigManager::getAnimationConfig() {
|
std::unordered_map<std::string, SAnimationPropertyConfig> CConfigManager::getAnimationConfig() {
|
||||||
return animationConfig;
|
return animationConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CConfigManager::addPluginConfigVar(HANDLE handle, const std::string& name, const SConfigValue& value) {
|
||||||
|
auto CONFIGMAPIT = std::find_if(pluginConfigs.begin(), pluginConfigs.end(), [&](const auto& other) { return other.first == handle; });
|
||||||
|
|
||||||
|
if (CONFIGMAPIT == pluginConfigs.end()) {
|
||||||
|
pluginConfigs.emplace(
|
||||||
|
std::pair<HANDLE, std::unique_ptr<std::unordered_map<std::string, SConfigValue>>>(handle, std::make_unique<std::unordered_map<std::string, SConfigValue>>()));
|
||||||
|
CONFIGMAPIT = std::find_if(pluginConfigs.begin(), pluginConfigs.end(), [&](const auto& other) { return other.first == handle; });
|
||||||
|
}
|
||||||
|
|
||||||
|
(*CONFIGMAPIT->second)[name] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CConfigManager::removePluginConfig(HANDLE handle) {
|
||||||
|
std::erase_if(pluginConfigs, [&](const auto& other) { return other.first == handle; });
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
#define INITANIMCFG(name) animationConfig[name] = {}
|
#define INITANIMCFG(name) animationConfig[name] = {}
|
||||||
#define CREATEANIMCFG(name, parent) animationConfig[name] = {false, "", "", 0.f, -1, &animationConfig["global"], &animationConfig[parent]}
|
#define CREATEANIMCFG(name, parent) animationConfig[name] = {false, "", "", 0.f, -1, &animationConfig["global"], &animationConfig[parent]}
|
||||||
|
|
||||||
|
#define HANDLE void*
|
||||||
|
|
||||||
struct SConfigValue {
|
struct SConfigValue {
|
||||||
int64_t intValue = -INT64_MAX;
|
int64_t intValue = -INT64_MAX;
|
||||||
float floatValue = -__FLT_MAX__;
|
float floatValue = -__FLT_MAX__;
|
||||||
|
@ -159,6 +161,9 @@ class CConfigManager {
|
||||||
|
|
||||||
std::unordered_map<std::string, SAnimationPropertyConfig> getAnimationConfig();
|
std::unordered_map<std::string, SAnimationPropertyConfig> getAnimationConfig();
|
||||||
|
|
||||||
|
void addPluginConfigVar(HANDLE handle, const std::string& name, const SConfigValue& value);
|
||||||
|
void removePluginConfig(HANDLE handle);
|
||||||
|
|
||||||
// no-op when done.
|
// no-op when done.
|
||||||
void dispatchExecOnce();
|
void dispatchExecOnce();
|
||||||
|
|
||||||
|
@ -180,33 +185,35 @@ class CConfigManager {
|
||||||
std::string configCurrentPath;
|
std::string configCurrentPath;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::deque<std::string> configPaths; // stores all the config paths
|
std::deque<std::string> configPaths; // stores all the config paths
|
||||||
std::unordered_map<std::string, time_t> configModifyTimes; // stores modify times
|
std::unordered_map<std::string, time_t> configModifyTimes; // stores modify times
|
||||||
std::unordered_map<std::string, std::string> configDynamicVars; // stores dynamic vars declared by the user
|
std::unordered_map<std::string, std::string> configDynamicVars; // stores dynamic vars declared by the user
|
||||||
std::unordered_map<std::string, SConfigValue> configValues;
|
std::unordered_map<std::string, SConfigValue> configValues;
|
||||||
std::unordered_map<std::string, std::unordered_map<std::string, SConfigValue>> deviceConfigs; // stores device configs
|
std::unordered_map<std::string, std::unordered_map<std::string, SConfigValue>> deviceConfigs; // stores device configs
|
||||||
|
|
||||||
std::unordered_map<std::string, SAnimationPropertyConfig> animationConfig; // stores all the animations with their set values
|
std::unordered_map<std::string, SAnimationPropertyConfig> animationConfig; // stores all the animations with their set values
|
||||||
|
|
||||||
std::string currentCategory = ""; // For storing the category of the current item
|
std::string currentCategory = ""; // For storing the category of the current item
|
||||||
|
|
||||||
std::string parseError = ""; // For storing a parse error to display later
|
std::string parseError = ""; // For storing a parse error to display later
|
||||||
|
|
||||||
std::string m_szCurrentSubmap = ""; // For storing the current keybind submap
|
std::string m_szCurrentSubmap = ""; // For storing the current keybind submap
|
||||||
|
|
||||||
std::vector<std::pair<std::string, std::string>> boundWorkspaces;
|
std::vector<std::pair<std::string, std::string>> boundWorkspaces;
|
||||||
|
|
||||||
std::vector<SExecRequestedRule> execRequestedRules; // rules requested with exec, e.g. [workspace 2] kitty
|
std::vector<SExecRequestedRule> execRequestedRules; // rules requested with exec, e.g. [workspace 2] kitty
|
||||||
|
|
||||||
bool isFirstLaunch = true; // For exec-once
|
std::unordered_map<HANDLE, std::unique_ptr<std::unordered_map<std::string, SConfigValue>>> pluginConfigs; // stores plugin configs
|
||||||
|
|
||||||
std::deque<SMonitorRule> m_dMonitorRules;
|
bool isFirstLaunch = true; // For exec-once
|
||||||
std::deque<SWindowRule> m_dWindowRules;
|
|
||||||
std::deque<SLayerRule> m_dLayerRules;
|
|
||||||
std::deque<std::string> m_dBlurLSNamespaces;
|
|
||||||
|
|
||||||
bool firstExecDispatched = false;
|
std::deque<SMonitorRule> m_dMonitorRules;
|
||||||
std::deque<std::string> firstExecRequests;
|
std::deque<SWindowRule> m_dWindowRules;
|
||||||
|
std::deque<SLayerRule> m_dLayerRules;
|
||||||
|
std::deque<std::string> m_dBlurLSNamespaces;
|
||||||
|
|
||||||
|
bool firstExecDispatched = false;
|
||||||
|
std::deque<std::string> firstExecRequests;
|
||||||
|
|
||||||
// internal methods
|
// internal methods
|
||||||
void setDefaultVars();
|
void setDefaultVars();
|
||||||
|
|
|
@ -4,10 +4,13 @@
|
||||||
#include <execinfo.h>
|
#include <execinfo.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "../plugins/PluginSystem.hpp"
|
||||||
|
|
||||||
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
|
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
std::string getRandomMessage() {
|
std::string getRandomMessage() {
|
||||||
|
|
||||||
const std::vector<std::string> MESSAGES = {"Sorry, didn't mean to...",
|
const std::vector<std::string> MESSAGES = {"Sorry, didn't mean to...",
|
||||||
|
@ -43,6 +46,16 @@ void CrashReporter::createAndSaveCrash() {
|
||||||
|
|
||||||
finalCrashReport += "Hyprland received signal 11 (SIGSEGV): Segmentation Fault\n\n";
|
finalCrashReport += "Hyprland received signal 11 (SIGSEGV): Segmentation Fault\n\n";
|
||||||
|
|
||||||
|
if (!g_pPluginSystem->getAllPlugins().empty()) {
|
||||||
|
finalCrashReport += "Hyprland seems to be running with plugins. This crash might not be Hyprland's fault.\nPlugins:\n";
|
||||||
|
|
||||||
|
for (auto& p : g_pPluginSystem->getAllPlugins()) {
|
||||||
|
finalCrashReport += getFormat("\t%s (%s) %s\n", p->name.c_str(), p->author.c_str(), p->version.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
finalCrashReport += "\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
finalCrashReport += "System info:\n";
|
finalCrashReport += "System info:\n";
|
||||||
|
|
||||||
struct utsname unameInfo;
|
struct utsname unameInfo;
|
||||||
|
|
|
@ -108,6 +108,8 @@ static std::string getWindowData(CWindow* w, HyprCtl::eHyprCtlOutputFormat forma
|
||||||
return getFormat(
|
return getFormat(
|
||||||
R"#({
|
R"#({
|
||||||
"address": "0x%x",
|
"address": "0x%x",
|
||||||
|
"mapped": %s,
|
||||||
|
"hidden": %s,
|
||||||
"at": [%i, %i],
|
"at": [%i, %i],
|
||||||
"size": [%i, %i],
|
"size": [%i, %i],
|
||||||
"workspace": {
|
"workspace": {
|
||||||
|
@ -127,7 +129,8 @@ static std::string getWindowData(CWindow* w, HyprCtl::eHyprCtlOutputFormat forma
|
||||||
"grouped": [%s],
|
"grouped": [%s],
|
||||||
"swallowing": %s
|
"swallowing": %s
|
||||||
},)#",
|
},)#",
|
||||||
w, (int)w->m_vRealPosition.goalv().x, (int)w->m_vRealPosition.goalv().y, (int)w->m_vRealSize.goalv().x, (int)w->m_vRealSize.goalv().y, w->m_iWorkspaceID,
|
w, (w->m_bIsMapped ? "true" : "false"), (w->isHidden() ? "true" : "false"), (int)w->m_vRealPosition.goalv().x, (int)w->m_vRealPosition.goalv().y,
|
||||||
|
(int)w->m_vRealSize.goalv().x, (int)w->m_vRealSize.goalv().y, w->m_iWorkspaceID,
|
||||||
escapeJSONStrings(w->m_iWorkspaceID == -1 ? "" :
|
escapeJSONStrings(w->m_iWorkspaceID == -1 ? "" :
|
||||||
g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID) ? g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID)->m_szName :
|
g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID) ? g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID)->m_szName :
|
||||||
std::string("Invalid workspace " + std::to_string(w->m_iWorkspaceID)))
|
std::string("Invalid workspace " + std::to_string(w->m_iWorkspaceID)))
|
||||||
|
@ -139,10 +142,11 @@ static std::string getWindowData(CWindow* w, HyprCtl::eHyprCtlOutputFormat forma
|
||||||
w->m_bFakeFullscreenState ? "true" : "false", getGroupedData(w, format).c_str(), (w->m_pSwallowed ? getFormat("\"0x%x\"", w->m_pSwallowed).c_str() : "null"));
|
w->m_bFakeFullscreenState ? "true" : "false", getGroupedData(w, format).c_str(), (w->m_pSwallowed ? getFormat("\"0x%x\"", w->m_pSwallowed).c_str() : "null"));
|
||||||
} else {
|
} else {
|
||||||
return getFormat(
|
return getFormat(
|
||||||
"Window %x -> %s:\n\tat: %i,%i\n\tsize: %i,%i\n\tworkspace: %i (%s)\n\tfloating: %i\n\tmonitor: %i\n\tclass: %s\n\ttitle: %s\n\tpid: %i\n\txwayland: %i\n\tpinned: "
|
"Window %x -> %s:\n\tmapped: %i\n\thidden: %i\n\tat: %i,%i\n\tsize: %i,%i\n\tworkspace: %i (%s)\n\tfloating: %i\n\tmonitor: %i\n\tclass: %s\n\ttitle: %s\n\tpid: "
|
||||||
|
"%i\n\txwayland: %i\n\tpinned: "
|
||||||
"%i\n\tfullscreen: %i\n\tfullscreenmode: %i\n\tfakefullscreen: %i\n\tgrouped: %s\n\tswallowing: %x\n\n",
|
"%i\n\tfullscreen: %i\n\tfullscreenmode: %i\n\tfakefullscreen: %i\n\tgrouped: %s\n\tswallowing: %x\n\n",
|
||||||
w, w->m_szTitle.c_str(), (int)w->m_vRealPosition.goalv().x, (int)w->m_vRealPosition.goalv().y, (int)w->m_vRealSize.goalv().x, (int)w->m_vRealSize.goalv().y,
|
w, w->m_szTitle.c_str(), (int)w->m_bIsMapped, (int)w->isHidden(), (int)w->m_vRealPosition.goalv().x, (int)w->m_vRealPosition.goalv().y, (int)w->m_vRealSize.goalv().x,
|
||||||
w->m_iWorkspaceID,
|
(int)w->m_vRealSize.goalv().y, w->m_iWorkspaceID,
|
||||||
(w->m_iWorkspaceID == -1 ? "" :
|
(w->m_iWorkspaceID == -1 ? "" :
|
||||||
g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID) ? g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID)->m_szName.c_str() :
|
g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID) ? g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID)->m_szName.c_str() :
|
||||||
std::string("Invalid workspace " + std::to_string(w->m_iWorkspaceID)).c_str()),
|
std::string("Invalid workspace " + std::to_string(w->m_iWorkspaceID)).c_str()),
|
||||||
|
@ -159,9 +163,7 @@ std::string clientsRequest(HyprCtl::eHyprCtlOutputFormat format) {
|
||||||
result += "[";
|
result += "[";
|
||||||
|
|
||||||
for (auto& w : g_pCompositor->m_vWindows) {
|
for (auto& w : g_pCompositor->m_vWindows) {
|
||||||
if (w->m_bIsMapped) {
|
result += getWindowData(w.get(), format);
|
||||||
result += getWindowData(w.get(), format);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove trailing comma
|
// remove trailing comma
|
||||||
|
@ -171,9 +173,7 @@ std::string clientsRequest(HyprCtl::eHyprCtlOutputFormat format) {
|
||||||
result += "]";
|
result += "]";
|
||||||
} else {
|
} else {
|
||||||
for (auto& w : g_pCompositor->m_vWindows) {
|
for (auto& w : g_pCompositor->m_vWindows) {
|
||||||
if (w->m_bIsMapped) {
|
result += getWindowData(w.get(), format);
|
||||||
result += getWindowData(w.get(), format);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -1087,6 +1087,50 @@ std::string dispatchOutput(std::string request) {
|
||||||
return "ok";
|
return "ok";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string dispatchPlugin(std::string request) {
|
||||||
|
CVarList vars(request, 0, ' ');
|
||||||
|
|
||||||
|
if (vars.size() < 2)
|
||||||
|
return "not enough args";
|
||||||
|
|
||||||
|
const auto OPERATION = vars[1];
|
||||||
|
const auto PATH = vars[2];
|
||||||
|
|
||||||
|
if (OPERATION == "load") {
|
||||||
|
if (vars.size() < 3)
|
||||||
|
return "not enough args";
|
||||||
|
|
||||||
|
const auto PLUGIN = g_pPluginSystem->loadPlugin(PATH);
|
||||||
|
|
||||||
|
if (!PLUGIN)
|
||||||
|
return "error in loading plugin";
|
||||||
|
} else if (OPERATION == "unload") {
|
||||||
|
if (vars.size() < 3)
|
||||||
|
return "not enough args";
|
||||||
|
|
||||||
|
const auto PLUGIN = g_pPluginSystem->getPluginByPath(PATH);
|
||||||
|
|
||||||
|
if (!PLUGIN)
|
||||||
|
return "plugin not loaded";
|
||||||
|
|
||||||
|
g_pPluginSystem->unloadPlugin(PLUGIN);
|
||||||
|
} else if (OPERATION == "list") {
|
||||||
|
const auto PLUGINS = g_pPluginSystem->getAllPlugins();
|
||||||
|
|
||||||
|
std::string list = "";
|
||||||
|
for (auto& p : PLUGINS) {
|
||||||
|
list += getFormat("\nPlugin %s by %s:\n\tHandle: %lx\n\tVersion: %s\n\tDescription: %s\n", p->name.c_str(), p->author.c_str(), p->m_pHandle, p->version.c_str(),
|
||||||
|
p->description.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
} else {
|
||||||
|
return "unknown opt";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "ok";
|
||||||
|
}
|
||||||
|
|
||||||
std::string getReply(std::string request) {
|
std::string getReply(std::string request) {
|
||||||
auto format = HyprCtl::FORMAT_NORMAL;
|
auto format = HyprCtl::FORMAT_NORMAL;
|
||||||
|
|
||||||
|
@ -1134,6 +1178,8 @@ std::string getReply(std::string request) {
|
||||||
return bindsRequest(format);
|
return bindsRequest(format);
|
||||||
else if (request == "animations")
|
else if (request == "animations")
|
||||||
return animationsRequest(format);
|
return animationsRequest(format);
|
||||||
|
else if (request.find("plugin") == 0)
|
||||||
|
return dispatchPlugin(request);
|
||||||
else if (request.find("setprop") == 0)
|
else if (request.find("setprop") == 0)
|
||||||
return dispatchSetProp(request);
|
return dispatchSetProp(request);
|
||||||
else if (request.find("seterror") == 0)
|
else if (request.find("seterror") == 0)
|
||||||
|
@ -1156,6 +1202,10 @@ std::string getReply(std::string request) {
|
||||||
return "unknown request";
|
return "unknown request";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string HyprCtl::makeDynamicCall(const std::string& input) {
|
||||||
|
return getReply(input);
|
||||||
|
}
|
||||||
|
|
||||||
int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
|
int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
|
||||||
if (mask & WL_EVENT_ERROR || mask & WL_EVENT_HANGUP)
|
if (mask & WL_EVENT_ERROR || mask & WL_EVENT_HANGUP)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
#include "../helpers/MiscFunctions.hpp"
|
#include "../helpers/MiscFunctions.hpp"
|
||||||
|
|
||||||
namespace HyprCtl {
|
namespace HyprCtl {
|
||||||
void startHyprCtlSocket();
|
void startHyprCtlSocket();
|
||||||
|
std::string makeDynamicCall(const std::string& input);
|
||||||
|
|
||||||
// very simple thread-safe request method
|
// very simple thread-safe request method
|
||||||
inline bool requestMade = false;
|
inline bool requestMade = false;
|
||||||
|
@ -18,8 +19,7 @@ namespace HyprCtl {
|
||||||
|
|
||||||
inline int iSocketFD = -1;
|
inline int iSocketFD = -1;
|
||||||
|
|
||||||
enum eHyprCtlOutputFormat
|
enum eHyprCtlOutputFormat {
|
||||||
{
|
|
||||||
FORMAT_NORMAL = 0,
|
FORMAT_NORMAL = 0,
|
||||||
FORMAT_JSON
|
FORMAT_JSON
|
||||||
};
|
};
|
||||||
|
|
155
src/debug/HyprNotificationOverlay.cpp
Normal file
155
src/debug/HyprNotificationOverlay.cpp
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
#include "HyprNotificationOverlay.hpp"
|
||||||
|
#include "../Compositor.hpp"
|
||||||
|
|
||||||
|
CHyprNotificationOverlay::CHyprNotificationOverlay() {
|
||||||
|
g_pHookSystem->hookDynamic("focusedMon", [&](void* self, std::any param) {
|
||||||
|
if (m_dNotifications.size() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_pHyprRenderer->damageBox(&m_bLastDamage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHyprNotificationOverlay::addNotification(const std::string& text, const CColor& color, const float timeMs) {
|
||||||
|
const auto PNOTIF = m_dNotifications.emplace_back(std::make_unique<SNotification>()).get();
|
||||||
|
|
||||||
|
PNOTIF->text = text;
|
||||||
|
PNOTIF->color = color;
|
||||||
|
PNOTIF->started.reset();
|
||||||
|
PNOTIF->timeMs = timeMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
wlr_box CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) {
|
||||||
|
static constexpr auto ANIM_DURATION_MS = 600.0;
|
||||||
|
static constexpr auto ANIM_LAG_MS = 100.0;
|
||||||
|
static constexpr auto NOTIF_LEFTBAR_SIZE = 5.0;
|
||||||
|
|
||||||
|
float offsetY = 10;
|
||||||
|
float maxWidth = 0;
|
||||||
|
|
||||||
|
const auto SCALE = pMonitor->scale;
|
||||||
|
const auto FONTSIZE = std::clamp((int)(10.f * ((pMonitor->vecPixelSize.x * SCALE) / 1920.f)), 8, 40);
|
||||||
|
|
||||||
|
const auto MONSIZE = pMonitor->vecPixelSize;
|
||||||
|
|
||||||
|
cairo_select_font_face(m_pCairo, "Noto Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
|
||||||
|
cairo_set_font_size(m_pCairo, FONTSIZE);
|
||||||
|
|
||||||
|
cairo_text_extents_t cairoExtents;
|
||||||
|
|
||||||
|
const auto PBEZIER = g_pAnimationManager->getBezier("default");
|
||||||
|
|
||||||
|
for (auto& notif : m_dNotifications) {
|
||||||
|
// first rect (bg, col)
|
||||||
|
const float FIRSTRECTANIMP =
|
||||||
|
(notif->started.getMillis() > (ANIM_DURATION_MS - ANIM_LAG_MS) ?
|
||||||
|
(notif->started.getMillis() > notif->timeMs - (ANIM_DURATION_MS - ANIM_LAG_MS) ? notif->timeMs - notif->started.getMillis() : (ANIM_DURATION_MS - ANIM_LAG_MS)) :
|
||||||
|
notif->started.getMillis()) /
|
||||||
|
(ANIM_DURATION_MS - ANIM_LAG_MS);
|
||||||
|
|
||||||
|
const float FIRSTRECTPERC = FIRSTRECTANIMP >= 0.99f ? 1.f : PBEZIER->getYForPoint(FIRSTRECTANIMP);
|
||||||
|
|
||||||
|
// second rect (fg, black)
|
||||||
|
const float SECONDRECTANIMP = (notif->started.getMillis() > ANIM_DURATION_MS ?
|
||||||
|
(notif->started.getMillis() > notif->timeMs - ANIM_DURATION_MS ? notif->timeMs - notif->started.getMillis() : ANIM_DURATION_MS) :
|
||||||
|
notif->started.getMillis()) /
|
||||||
|
ANIM_DURATION_MS;
|
||||||
|
|
||||||
|
const float SECONDRECTPERC = SECONDRECTANIMP >= 0.99f ? 1.f : PBEZIER->getYForPoint(SECONDRECTANIMP);
|
||||||
|
|
||||||
|
// third rect (horiz, col)
|
||||||
|
const float THIRDRECTPERC = notif->started.getMillis() / notif->timeMs;
|
||||||
|
|
||||||
|
// get text size
|
||||||
|
cairo_text_extents(m_pCairo, notif->text.c_str(), &cairoExtents);
|
||||||
|
|
||||||
|
cairo_set_source_rgba(m_pCairo, notif->color.r, notif->color.g, notif->color.b, notif->color.a);
|
||||||
|
|
||||||
|
const auto NOTIFSIZE = Vector2D{cairoExtents.width + 20, cairoExtents.height + 10};
|
||||||
|
|
||||||
|
// draw rects
|
||||||
|
cairo_rectangle(m_pCairo, MONSIZE.x - (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC, offsetY, (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC, NOTIFSIZE.y);
|
||||||
|
cairo_fill(m_pCairo);
|
||||||
|
|
||||||
|
cairo_set_source_rgb(m_pCairo, 0.f, 0.f, 0.f);
|
||||||
|
|
||||||
|
cairo_rectangle(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC, offsetY, NOTIFSIZE.x * SECONDRECTPERC, NOTIFSIZE.y);
|
||||||
|
cairo_fill(m_pCairo);
|
||||||
|
|
||||||
|
cairo_set_source_rgba(m_pCairo, notif->color.r, notif->color.g, notif->color.b, notif->color.a);
|
||||||
|
|
||||||
|
cairo_rectangle(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + 3, offsetY + NOTIFSIZE.y - 4, THIRDRECTPERC * (NOTIFSIZE.x - 6), 2);
|
||||||
|
cairo_fill(m_pCairo);
|
||||||
|
|
||||||
|
// draw text
|
||||||
|
cairo_set_source_rgb(m_pCairo, 1.f, 1.f, 1.f);
|
||||||
|
cairo_move_to(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + NOTIF_LEFTBAR_SIZE, offsetY + FONTSIZE + (FONTSIZE / 10.0));
|
||||||
|
cairo_show_text(m_pCairo, notif->text.c_str());
|
||||||
|
|
||||||
|
// adjust offset and move on
|
||||||
|
offsetY += NOTIFSIZE.y + 10;
|
||||||
|
|
||||||
|
if (maxWidth < NOTIFSIZE.x)
|
||||||
|
maxWidth = NOTIFSIZE.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup notifs
|
||||||
|
std::erase_if(m_dNotifications, [](const auto& notif) { return notif->started.getMillis() > notif->timeMs; });
|
||||||
|
|
||||||
|
return wlr_box{(int)(pMonitor->vecPosition.x + pMonitor->vecSize.x - maxWidth - 20), (int)pMonitor->vecPosition.y, (int)maxWidth + 20, (int)offsetY + 10};
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHyprNotificationOverlay::draw(CMonitor* pMonitor) {
|
||||||
|
|
||||||
|
if (m_pLastMonitor != pMonitor || !m_pCairo || !m_pCairoSurface) {
|
||||||
|
|
||||||
|
if (m_pCairo && m_pCairoSurface) {
|
||||||
|
cairo_destroy(m_pCairo);
|
||||||
|
cairo_surface_destroy(m_pCairoSurface);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pCairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y);
|
||||||
|
m_pCairo = cairo_create(m_pCairoSurface);
|
||||||
|
m_pLastMonitor = pMonitor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the notifications
|
||||||
|
if (m_dNotifications.size() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Render to the monitor
|
||||||
|
|
||||||
|
// clear the pixmap
|
||||||
|
cairo_save(m_pCairo);
|
||||||
|
cairo_set_operator(m_pCairo, CAIRO_OPERATOR_CLEAR);
|
||||||
|
cairo_paint(m_pCairo);
|
||||||
|
cairo_restore(m_pCairo);
|
||||||
|
|
||||||
|
cairo_surface_flush(m_pCairoSurface);
|
||||||
|
|
||||||
|
wlr_box damage = drawNotifications(pMonitor);
|
||||||
|
|
||||||
|
g_pHyprRenderer->damageBox(&damage);
|
||||||
|
g_pHyprRenderer->damageBox(&m_bLastDamage);
|
||||||
|
|
||||||
|
g_pCompositor->scheduleFrameForMonitor(pMonitor);
|
||||||
|
|
||||||
|
m_bLastDamage = damage;
|
||||||
|
|
||||||
|
// copy the data to an OpenGL texture we have
|
||||||
|
const auto DATA = cairo_image_surface_get_data(m_pCairoSurface);
|
||||||
|
m_tTexture.allocate();
|
||||||
|
glBindTexture(GL_TEXTURE_2D, m_tTexture.m_iTexID);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
|
||||||
|
#ifndef GLES2
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
|
||||||
|
|
||||||
|
wlr_box pMonBox = {0, 0, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y};
|
||||||
|
g_pHyprOpenGL->renderTexture(m_tTexture, &pMonBox, 1.f);
|
||||||
|
}
|
40
src/debug/HyprNotificationOverlay.hpp
Normal file
40
src/debug/HyprNotificationOverlay.hpp
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../defines.hpp"
|
||||||
|
#include "../helpers/Timer.hpp"
|
||||||
|
#include "../helpers/Monitor.hpp"
|
||||||
|
#include "../render/Texture.hpp"
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
#include <cairo/cairo.h>
|
||||||
|
|
||||||
|
struct SNotification {
|
||||||
|
std::string text = "";
|
||||||
|
CColor color;
|
||||||
|
CTimer started;
|
||||||
|
float timeMs = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CHyprNotificationOverlay {
|
||||||
|
public:
|
||||||
|
CHyprNotificationOverlay();
|
||||||
|
|
||||||
|
void draw(CMonitor* pMonitor);
|
||||||
|
void addNotification(const std::string& text, const CColor& color, const float timeMs);
|
||||||
|
|
||||||
|
private:
|
||||||
|
wlr_box drawNotifications(CMonitor* pMonitor);
|
||||||
|
wlr_box m_bLastDamage;
|
||||||
|
|
||||||
|
std::deque<std::unique_ptr<SNotification>> m_dNotifications;
|
||||||
|
|
||||||
|
cairo_surface_t* m_pCairoSurface = nullptr;
|
||||||
|
cairo_t* m_pCairo = nullptr;
|
||||||
|
|
||||||
|
CMonitor* m_pLastMonitor = nullptr;
|
||||||
|
|
||||||
|
CTexture m_tTexture;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::unique_ptr<CHyprNotificationOverlay> g_pHyprNotificationOverlay;
|
|
@ -89,4 +89,4 @@
|
||||||
|
|
||||||
#define SPECIAL_WORKSPACE_START (-99)
|
#define SPECIAL_WORKSPACE_START (-99)
|
||||||
|
|
||||||
#define PI 3.14159265358979
|
#define PI 3.14159265358979
|
|
@ -109,10 +109,8 @@ void Events::listener_newConstraint(wl_listener* listener, void* data) {
|
||||||
if (g_pCompositor->m_pLastFocus == PCONSTRAINT->surface) {
|
if (g_pCompositor->m_pLastFocus == PCONSTRAINT->surface) {
|
||||||
g_pInputManager->constrainMouse(CONSTRAINT->pMouse, PCONSTRAINT);
|
g_pInputManager->constrainMouse(CONSTRAINT->pMouse, PCONSTRAINT);
|
||||||
|
|
||||||
if (!CONSTRAINT->hintSet) {
|
if (!CONSTRAINT->hintSet)
|
||||||
const auto PWINDOW = g_pCompositor->getConstraintWindow(g_pCompositor->m_sSeat.mouse);
|
|
||||||
CONSTRAINT->positionHint = Vector2D{-1, -1};
|
CONSTRAINT->positionHint = Vector2D{-1, -1};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@ namespace Events {
|
||||||
DYNLISTENFUNC(requestResize);
|
DYNLISTENFUNC(requestResize);
|
||||||
DYNLISTENFUNC(requestMinimize);
|
DYNLISTENFUNC(requestMinimize);
|
||||||
DYNLISTENFUNC(requestMaximize);
|
DYNLISTENFUNC(requestMaximize);
|
||||||
|
DYNLISTENFUNC(setOverrideRedirect);
|
||||||
|
|
||||||
// Window subsurfaces
|
// Window subsurfaces
|
||||||
// LISTENER(newSubsurfaceWindow);
|
// LISTENER(newSubsurfaceWindow);
|
||||||
|
|
|
@ -197,6 +197,8 @@ void Events::listener_monitorFrame(void* owner, void* data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PMONITOR->renderingActive = true;
|
||||||
|
|
||||||
// we need to cleanup fading out when rendering the appropriate context
|
// we need to cleanup fading out when rendering the appropriate context
|
||||||
g_pCompositor->cleanupFadingOut(PMONITOR->ID);
|
g_pCompositor->cleanupFadingOut(PMONITOR->ID);
|
||||||
|
|
||||||
|
@ -207,6 +209,8 @@ void Events::listener_monitorFrame(void* owner, void* data) {
|
||||||
if (*PDAMAGEBLINK || *PVFR == 0)
|
if (*PDAMAGEBLINK || *PVFR == 0)
|
||||||
g_pCompositor->scheduleFrameForMonitor(PMONITOR);
|
g_pCompositor->scheduleFrameForMonitor(PMONITOR);
|
||||||
|
|
||||||
|
PMONITOR->renderingActive = false;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,9 +260,10 @@ void Events::listener_monitorFrame(void* owner, void* data) {
|
||||||
|
|
||||||
g_pHyprRenderer->renderAllClientsForMonitor(PMONITOR->ID, &now);
|
g_pHyprRenderer->renderAllClientsForMonitor(PMONITOR->ID, &now);
|
||||||
|
|
||||||
// if correct monitor draw hyprerror
|
if (PMONITOR == g_pCompositor->m_pLastMonitor) {
|
||||||
if (PMONITOR == g_pCompositor->m_vMonitors.front().get())
|
g_pHyprNotificationOverlay->draw(PMONITOR);
|
||||||
g_pHyprError->draw();
|
g_pHyprError->draw();
|
||||||
|
}
|
||||||
|
|
||||||
// for drawing the debug overlay
|
// for drawing the debug overlay
|
||||||
if (PMONITOR == g_pCompositor->m_vMonitors.front().get() && *PDEBUGOVERLAY == 1) {
|
if (PMONITOR == g_pCompositor->m_vMonitors.front().get() && *PDEBUGOVERLAY == 1) {
|
||||||
|
@ -306,12 +311,16 @@ void Events::listener_monitorFrame(void* owner, void* data) {
|
||||||
pixman_region32_fini(&frameDamage);
|
pixman_region32_fini(&frameDamage);
|
||||||
pixman_region32_fini(&damage);
|
pixman_region32_fini(&damage);
|
||||||
|
|
||||||
|
PMONITOR->renderingActive = false;
|
||||||
|
|
||||||
if (!wlr_output_commit(PMONITOR->output))
|
if (!wlr_output_commit(PMONITOR->output))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (*PDAMAGEBLINK || *PVFR == 0)
|
if (*PDAMAGEBLINK || *PVFR == 0 || PMONITOR->pendingFrame)
|
||||||
g_pCompositor->scheduleFrameForMonitor(PMONITOR);
|
g_pCompositor->scheduleFrameForMonitor(PMONITOR);
|
||||||
|
|
||||||
|
PMONITOR->pendingFrame = false;
|
||||||
|
|
||||||
if (*PDEBUGOVERLAY == 1) {
|
if (*PDEBUGOVERLAY == 1) {
|
||||||
const float µs = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - startRender).count() / 1000.f;
|
const float µs = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - startRender).count() / 1000.f;
|
||||||
g_pDebugOverlay->renderData(PMONITOR, µs);
|
g_pDebugOverlay->renderData(PMONITOR, µs);
|
||||||
|
|
|
@ -446,7 +446,6 @@ void Events::listener_mapWindow(void* owner, void* data) {
|
||||||
PWINDOW->hyprListener_fullscreenWindow.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_fullscreen, &Events::listener_fullscreenWindow, PWINDOW,
|
PWINDOW->hyprListener_fullscreenWindow.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_fullscreen, &Events::listener_fullscreenWindow, PWINDOW,
|
||||||
"XWayland Window Late");
|
"XWayland Window Late");
|
||||||
PWINDOW->hyprListener_activateX11.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_activate, &Events::listener_activateX11, PWINDOW, "XWayland Window Late");
|
PWINDOW->hyprListener_activateX11.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_activate, &Events::listener_activateX11, PWINDOW, "XWayland Window Late");
|
||||||
PWINDOW->hyprListener_configureX11.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_configure, &Events::listener_configureX11, PWINDOW, "XWayland Window Late");
|
|
||||||
PWINDOW->hyprListener_setTitleWindow.initCallback(&PWINDOW->m_uSurface.xwayland->events.set_title, &Events::listener_setTitleWindow, PWINDOW, "XWayland Window Late");
|
PWINDOW->hyprListener_setTitleWindow.initCallback(&PWINDOW->m_uSurface.xwayland->events.set_title, &Events::listener_setTitleWindow, PWINDOW, "XWayland Window Late");
|
||||||
PWINDOW->hyprListener_requestMinimize.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_minimize, &Events::listener_requestMinimize, PWINDOW,
|
PWINDOW->hyprListener_requestMinimize.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_minimize, &Events::listener_requestMinimize, PWINDOW,
|
||||||
"Xwayland Window Late");
|
"Xwayland Window Late");
|
||||||
|
@ -596,7 +595,6 @@ void Events::listener_unmapWindow(void* owner, void* data) {
|
||||||
Debug::log(LOG, "Unregistered late callbacks XWL");
|
Debug::log(LOG, "Unregistered late callbacks XWL");
|
||||||
PWINDOW->hyprListener_fullscreenWindow.removeCallback();
|
PWINDOW->hyprListener_fullscreenWindow.removeCallback();
|
||||||
PWINDOW->hyprListener_activateX11.removeCallback();
|
PWINDOW->hyprListener_activateX11.removeCallback();
|
||||||
PWINDOW->hyprListener_configureX11.removeCallback();
|
|
||||||
PWINDOW->hyprListener_setTitleWindow.removeCallback();
|
PWINDOW->hyprListener_setTitleWindow.removeCallback();
|
||||||
PWINDOW->hyprListener_setGeometryX11U.removeCallback();
|
PWINDOW->hyprListener_setGeometryX11U.removeCallback();
|
||||||
PWINDOW->hyprListener_requestMaximize.removeCallback();
|
PWINDOW->hyprListener_requestMaximize.removeCallback();
|
||||||
|
@ -889,26 +887,24 @@ void Events::listener_activateX11(void* owner, void* data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Events::listener_configureX11(void* owner, void* data) {
|
void Events::listener_configureX11(void* owner, void* data) {
|
||||||
CWindow* PWINDOW = (CWindow*)owner;
|
CWindow* PWINDOW = (CWindow*)owner;
|
||||||
|
|
||||||
if (!g_pCompositor->windowValidMapped(PWINDOW))
|
|
||||||
return;
|
|
||||||
|
|
||||||
const auto E = (wlr_xwayland_surface_configure_event*)data;
|
const auto E = (wlr_xwayland_surface_configure_event*)data;
|
||||||
g_pHyprRenderer->damageWindow(PWINDOW);
|
|
||||||
|
|
||||||
if (!PWINDOW->m_bIsFloating || PWINDOW->m_bIsFullscreen) {
|
|
||||||
g_pXWaylandManager->setWindowSize(PWINDOW, PWINDOW->m_vRealSize.goalv(), true);
|
|
||||||
g_pInputManager->refocus();
|
|
||||||
g_pHyprRenderer->damageWindow(PWINDOW);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PWINDOW->m_uSurface.xwayland->mapped || !PWINDOW->m_bMappedX11) {
|
if (!PWINDOW->m_uSurface.xwayland->mapped || !PWINDOW->m_bMappedX11) {
|
||||||
wlr_xwayland_surface_configure(PWINDOW->m_uSurface.xwayland, E->x, E->y, E->width, E->height);
|
wlr_xwayland_surface_configure(PWINDOW->m_uSurface.xwayland, E->x, E->y, E->width, E->height);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_pHyprRenderer->damageWindow(PWINDOW);
|
||||||
|
|
||||||
|
if (!PWINDOW->m_bIsFloating || PWINDOW->m_bIsFullscreen || g_pInputManager->currentlyDraggedWindow == PWINDOW) {
|
||||||
|
g_pXWaylandManager->setWindowSize(PWINDOW, PWINDOW->m_vRealSize.goalv(), true);
|
||||||
|
g_pInputManager->refocus();
|
||||||
|
g_pHyprRenderer->damageWindow(PWINDOW);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (E->width > 1 && E->height > 1)
|
if (E->width > 1 && E->height > 1)
|
||||||
PWINDOW->setHidden(false);
|
PWINDOW->setHidden(false);
|
||||||
else
|
else
|
||||||
|
@ -974,6 +970,14 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Events::listener_setOverrideRedirect(void* owner, void* data) {
|
||||||
|
const auto PWINDOW = (CWindow*)owner;
|
||||||
|
|
||||||
|
if (!PWINDOW->m_bIsMapped && PWINDOW->m_uSurface.xwayland->mapped) {
|
||||||
|
Events::listener_mapWindow(PWINDOW, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Events::listener_surfaceXWayland(wl_listener* listener, void* data) {
|
void Events::listener_surfaceXWayland(wl_listener* listener, void* data) {
|
||||||
const auto XWSURFACE = (wlr_xwayland_surface*)data;
|
const auto XWSURFACE = (wlr_xwayland_surface*)data;
|
||||||
|
|
||||||
|
@ -993,6 +997,8 @@ void Events::listener_surfaceXWayland(wl_listener* listener, void* data) {
|
||||||
PNEWWINDOW->hyprListener_mapWindow.initCallback(&XWSURFACE->events.map, &Events::listener_mapWindow, PNEWWINDOW, "XWayland Window");
|
PNEWWINDOW->hyprListener_mapWindow.initCallback(&XWSURFACE->events.map, &Events::listener_mapWindow, PNEWWINDOW, "XWayland Window");
|
||||||
PNEWWINDOW->hyprListener_unmapWindow.initCallback(&XWSURFACE->events.unmap, &Events::listener_unmapWindow, PNEWWINDOW, "XWayland Window");
|
PNEWWINDOW->hyprListener_unmapWindow.initCallback(&XWSURFACE->events.unmap, &Events::listener_unmapWindow, PNEWWINDOW, "XWayland Window");
|
||||||
PNEWWINDOW->hyprListener_destroyWindow.initCallback(&XWSURFACE->events.destroy, &Events::listener_destroyWindow, PNEWWINDOW, "XWayland Window");
|
PNEWWINDOW->hyprListener_destroyWindow.initCallback(&XWSURFACE->events.destroy, &Events::listener_destroyWindow, PNEWWINDOW, "XWayland Window");
|
||||||
|
PNEWWINDOW->hyprListener_setOverrideRedirect.initCallback(&XWSURFACE->events.set_override_redirect, &Events::listener_setOverrideRedirect, PNEWWINDOW, "XWayland Window");
|
||||||
|
PNEWWINDOW->hyprListener_configureX11.initCallback(&XWSURFACE->events.request_configure, &Events::listener_configureX11, PNEWWINDOW, "XWayland Window");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Events::listener_newXDGSurface(wl_listener* listener, void* data) {
|
void Events::listener_newXDGSurface(wl_listener* listener, void* data) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../includes.hpp"
|
#include <cstdint>
|
||||||
|
|
||||||
class CColor {
|
class CColor {
|
||||||
public:
|
public:
|
||||||
|
@ -13,7 +13,7 @@ class CColor {
|
||||||
uint64_t getAsHex();
|
uint64_t getAsHex();
|
||||||
|
|
||||||
CColor operator-(const CColor& c2) const {
|
CColor operator-(const CColor& c2) const {
|
||||||
return CColor(r - c2.r, g - c2.g, b - c2.b, a - c2.a);
|
return CColor(r - c2.r, g - c2.g, b - c2.b, a - c2.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
CColor operator+(const CColor& c2) const {
|
CColor operator+(const CColor& c2) const {
|
||||||
|
|
|
@ -42,6 +42,9 @@ class CMonitor {
|
||||||
bool enabled10bit = false; // as above, this can be TRUE even if 10 bit failed.
|
bool enabled10bit = false; // as above, this can be TRUE even if 10 bit failed.
|
||||||
bool createdByUser = false;
|
bool createdByUser = false;
|
||||||
|
|
||||||
|
bool pendingFrame = false; // if we schedule a frame during rendering, reschedule it after
|
||||||
|
bool renderingActive = false;
|
||||||
|
|
||||||
// mirroring
|
// mirroring
|
||||||
CMonitor* pMirrorOf = nullptr;
|
CMonitor* pMirrorOf = nullptr;
|
||||||
std::vector<CMonitor*> mirrors;
|
std::vector<CMonitor*> mirrors;
|
||||||
|
|
|
@ -4,6 +4,22 @@
|
||||||
CHyprError::CHyprError() {
|
CHyprError::CHyprError() {
|
||||||
m_fFadeOpacity.create(AVARTYPE_FLOAT, g_pConfigManager->getAnimationPropertyConfig("fadeIn"), nullptr, AVARDAMAGE_NONE);
|
m_fFadeOpacity.create(AVARTYPE_FLOAT, g_pConfigManager->getAnimationPropertyConfig("fadeIn"), nullptr, AVARDAMAGE_NONE);
|
||||||
m_fFadeOpacity.registerVar();
|
m_fFadeOpacity.registerVar();
|
||||||
|
|
||||||
|
g_pHookSystem->hookDynamic("focusedMon", [&](void* self, std::any param) {
|
||||||
|
if (!m_bIsCreated)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_pHyprRenderer->damageMonitor(g_pCompositor->m_pLastMonitor);
|
||||||
|
m_bMonitorChanged = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
g_pHookSystem->hookDynamic("preRender", [&](void* self, std::any param) {
|
||||||
|
if (!m_bIsCreated)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_fFadeOpacity.isBeingAnimated() || m_bMonitorChanged)
|
||||||
|
g_pHyprRenderer->damageBox(&m_bDamageBox);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
CHyprError::~CHyprError() {
|
CHyprError::~CHyprError() {
|
||||||
|
@ -54,7 +70,7 @@ void CHyprError::createQueued() {
|
||||||
const double HEIGHT = (FONTSIZE + 2 * (FONTSIZE / 10.0)) * LINECOUNT + 3;
|
const double HEIGHT = (FONTSIZE + 2 * (FONTSIZE / 10.0)) * LINECOUNT + 3;
|
||||||
const double RADIUS = PAD > HEIGHT / 2 ? HEIGHT / 2 - 1 : PAD;
|
const double RADIUS = PAD > HEIGHT / 2 ? HEIGHT / 2 - 1 : PAD;
|
||||||
|
|
||||||
m_bDamageBox = {(int)PMONITOR->vecPosition.x, (int)PMONITOR->vecPosition.y, (int)PMONITOR->vecPixelSize.x, (int)HEIGHT + (int)PAD * 2};
|
m_bDamageBox = {0, 0, (int)PMONITOR->vecPixelSize.x, (int)HEIGHT + (int)PAD * 2};
|
||||||
|
|
||||||
cairo_new_sub_path(CAIRO);
|
cairo_new_sub_path(CAIRO);
|
||||||
cairo_arc(CAIRO, X + WIDTH - RADIUS, Y + RADIUS, RADIUS, -90 * DEGREES, 0 * DEGREES);
|
cairo_arc(CAIRO, X + WIDTH - RADIUS, Y + RADIUS, RADIUS, -90 * DEGREES, 0 * DEGREES);
|
||||||
|
@ -137,16 +153,18 @@ void CHyprError::draw() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto PMONITOR = g_pCompositor->m_vMonitors.front().get();
|
const auto PMONITOR = g_pHyprOpenGL->m_RenderData.pMonitor;
|
||||||
|
|
||||||
if (g_pHyprOpenGL->m_RenderData.pMonitor != PMONITOR)
|
wlr_box monbox = {0, 0, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y};
|
||||||
return; // wrong mon
|
|
||||||
|
|
||||||
wlr_box monbox = {0, 0, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y};
|
m_bDamageBox.x = (int)PMONITOR->vecPosition.x;
|
||||||
|
m_bDamageBox.y = (int)PMONITOR->vecPosition.y;
|
||||||
|
|
||||||
if (m_fFadeOpacity.isBeingAnimated())
|
if (m_fFadeOpacity.isBeingAnimated() || m_bMonitorChanged)
|
||||||
g_pHyprRenderer->damageBox(&m_bDamageBox);
|
g_pHyprRenderer->damageBox(&m_bDamageBox);
|
||||||
|
|
||||||
|
m_bMonitorChanged = false;
|
||||||
|
|
||||||
g_pHyprOpenGL->renderTexture(m_tTexture, &monbox, m_fFadeOpacity.fl(), 0);
|
g_pHyprOpenGL->renderTexture(m_tTexture, &monbox, m_fFadeOpacity.fl(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,8 @@ class CHyprError {
|
||||||
CTexture m_tTexture;
|
CTexture m_tTexture;
|
||||||
CAnimatedVariable m_fFadeOpacity;
|
CAnimatedVariable m_fFadeOpacity;
|
||||||
wlr_box m_bDamageBox = {0, 0, 0, 0};
|
wlr_box m_bDamageBox = {0, 0, 0, 0};
|
||||||
|
|
||||||
|
bool m_bMonitorChanged = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::unique_ptr<CHyprError> g_pHyprError; // This is a full-screen error. Treat it with respect, and there can only be one at a time.
|
inline std::unique_ptr<CHyprError> g_pHyprError; // This is a full-screen error. Treat it with respect, and there can only be one at a time.
|
|
@ -179,6 +179,10 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto RESERVED = PWINDOW->getFullWindowReservedArea();
|
||||||
|
calcPos = calcPos + RESERVED.topLeft;
|
||||||
|
calcSize = calcSize - (RESERVED.topLeft + RESERVED.bottomRight);
|
||||||
|
|
||||||
if (g_pCompositor->isWorkspaceSpecial(PWINDOW->m_iWorkspaceID)) {
|
if (g_pCompositor->isWorkspaceSpecial(PWINDOW->m_iWorkspaceID)) {
|
||||||
// if special, we adjust the coords a bit
|
// if special, we adjust the coords a bit
|
||||||
static auto* const PSCALEFACTOR = &g_pConfigManager->getConfigValuePtr("dwindle:special_scale_factor")->floatValue;
|
static auto* const PSCALEFACTOR = &g_pConfigManager->getConfigValuePtr("dwindle:special_scale_factor")->floatValue;
|
||||||
|
|
|
@ -272,7 +272,8 @@ void IHyprLayout::onMouseMove(const Vector2D& mousePos) {
|
||||||
const auto DELTA = Vector2D(mousePos.x - m_vBeginDragXY.x, mousePos.y - m_vBeginDragXY.y);
|
const auto DELTA = Vector2D(mousePos.x - m_vBeginDragXY.x, mousePos.y - m_vBeginDragXY.y);
|
||||||
const auto TICKDELTA = Vector2D(mousePos.x - m_vLastDragXY.x, mousePos.y - m_vLastDragXY.y);
|
const auto TICKDELTA = Vector2D(mousePos.x - m_vLastDragXY.x, mousePos.y - m_vLastDragXY.y);
|
||||||
|
|
||||||
const auto PANIMATE = &g_pConfigManager->getConfigValuePtr("misc:animate_manual_resizes")->intValue;
|
const auto PANIMATEMOUSE = &g_pConfigManager->getConfigValuePtr("misc:animate_mouse_windowdragging")->intValue;
|
||||||
|
const auto PANIMATE = &g_pConfigManager->getConfigValuePtr("misc:animate_manual_resizes")->intValue;
|
||||||
|
|
||||||
if (abs(TICKDELTA.x) < 1.f && abs(TICKDELTA.y) < 1.f)
|
if (abs(TICKDELTA.x) < 1.f && abs(TICKDELTA.y) < 1.f)
|
||||||
return;
|
return;
|
||||||
|
@ -283,7 +284,7 @@ void IHyprLayout::onMouseMove(const Vector2D& mousePos) {
|
||||||
|
|
||||||
if (g_pInputManager->dragMode == MBIND_MOVE) {
|
if (g_pInputManager->dragMode == MBIND_MOVE) {
|
||||||
|
|
||||||
if (*PANIMATE) {
|
if (*PANIMATEMOUSE) {
|
||||||
DRAGGINGWINDOW->m_vRealPosition = m_vBeginDragPositionXY + DELTA;
|
DRAGGINGWINDOW->m_vRealPosition = m_vBeginDragPositionXY + DELTA;
|
||||||
} else {
|
} else {
|
||||||
DRAGGINGWINDOW->m_vRealPosition.setValueAndWarp(m_vBeginDragPositionXY + DELTA);
|
DRAGGINGWINDOW->m_vRealPosition.setValueAndWarp(m_vBeginDragPositionXY + DELTA);
|
||||||
|
|
|
@ -264,7 +264,7 @@ void CHyprMasterLayout::calculateWorkspace(const int& ws) {
|
||||||
if (getNodesOnWorkspace(PWORKSPACE->m_iID) < 2 && !(ORIENTATION_CENTER == orientation)) {
|
if (getNodesOnWorkspace(PWORKSPACE->m_iID) < 2 && !(ORIENTATION_CENTER == orientation)) {
|
||||||
PMASTERNODE->position = PMONITOR->vecReservedTopLeft + PMONITOR->vecPosition;
|
PMASTERNODE->position = PMONITOR->vecReservedTopLeft + PMONITOR->vecPosition;
|
||||||
PMASTERNODE->size = Vector2D(PMONITOR->vecSize.x - PMONITOR->vecReservedTopLeft.x - PMONITOR->vecReservedBottomRight.x,
|
PMASTERNODE->size = Vector2D(PMONITOR->vecSize.x - PMONITOR->vecReservedTopLeft.x - PMONITOR->vecReservedBottomRight.x,
|
||||||
PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y - PMONITOR->vecReservedTopLeft.y);
|
PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y - PMONITOR->vecReservedTopLeft.y);
|
||||||
applyNodeDataToWindow(PMASTERNODE);
|
applyNodeDataToWindow(PMASTERNODE);
|
||||||
return;
|
return;
|
||||||
} else if (orientation == ORIENTATION_LEFT || orientation == ORIENTATION_RIGHT) {
|
} else if (orientation == ORIENTATION_LEFT || orientation == ORIENTATION_RIGHT) {
|
||||||
|
@ -512,6 +512,10 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) {
|
||||||
calcPos = calcPos + OFFSETTOPLEFT;
|
calcPos = calcPos + OFFSETTOPLEFT;
|
||||||
calcSize = calcSize - OFFSETTOPLEFT - OFFSETBOTTOMRIGHT;
|
calcSize = calcSize - OFFSETTOPLEFT - OFFSETBOTTOMRIGHT;
|
||||||
|
|
||||||
|
const auto RESERVED = PWINDOW->getFullWindowReservedArea();
|
||||||
|
calcPos = calcPos + RESERVED.topLeft;
|
||||||
|
calcSize = calcSize - (RESERVED.topLeft + RESERVED.bottomRight);
|
||||||
|
|
||||||
if (g_pCompositor->isWorkspaceSpecial(PWINDOW->m_iWorkspaceID)) {
|
if (g_pCompositor->isWorkspaceSpecial(PWINDOW->m_iWorkspaceID)) {
|
||||||
static auto* const PSCALEFACTOR = &g_pConfigManager->getConfigValuePtr("master:special_scale_factor")->floatValue;
|
static auto* const PSCALEFACTOR = &g_pConfigManager->getConfigValuePtr("master:special_scale_factor")->floatValue;
|
||||||
|
|
||||||
|
|
|
@ -1,44 +1,79 @@
|
||||||
#include "HookSystemManager.hpp"
|
#include "HookSystemManager.hpp"
|
||||||
|
|
||||||
|
#include "../plugins/PluginSystem.hpp"
|
||||||
|
|
||||||
CHookSystemManager::CHookSystemManager() {
|
CHookSystemManager::CHookSystemManager() {
|
||||||
; //
|
; //
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns the pointer to the function
|
// returns the pointer to the function
|
||||||
HOOK_CALLBACK_FN* CHookSystemManager::hookDynamic(const std::string& event, HOOK_CALLBACK_FN fn) {
|
HOOK_CALLBACK_FN* CHookSystemManager::hookDynamic(const std::string& event, HOOK_CALLBACK_FN fn, HANDLE handle) {
|
||||||
const auto PVEC = getVecForEvent(event);
|
const auto PVEC = getVecForEvent(event);
|
||||||
const auto PFN = &m_lCallbackFunctions.emplace_back(fn);
|
const auto PFN = &m_lCallbackFunctions.emplace_back(fn);
|
||||||
PVEC->emplace_back(PFN);
|
PVEC->emplace_back(SCallbackFNPtr{PFN, handle});
|
||||||
return PFN;
|
return PFN;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHookSystemManager::hookStatic(const std::string& event, HOOK_CALLBACK_FN* fn) {
|
void CHookSystemManager::hookStatic(const std::string& event, HOOK_CALLBACK_FN* fn, HANDLE handle) {
|
||||||
const auto PVEC = getVecForEvent(event);
|
const auto PVEC = getVecForEvent(event);
|
||||||
PVEC->emplace_back(fn);
|
PVEC->emplace_back(SCallbackFNPtr{fn, handle});
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHookSystemManager::unhook(HOOK_CALLBACK_FN* fn) {
|
void CHookSystemManager::unhook(HOOK_CALLBACK_FN* fn) {
|
||||||
std::erase_if(m_lCallbackFunctions, [&](const auto& other) { return &other == fn; });
|
std::erase_if(m_lCallbackFunctions, [&](const auto& other) { return &other == fn; });
|
||||||
for (auto& [k, v] : m_lpRegisteredHooks) {
|
for (auto& [k, v] : m_lpRegisteredHooks) {
|
||||||
std::erase_if(v, [&](const auto& other) { return other == fn; });
|
std::erase_if(v, [&](const auto& other) { return other.fn == fn; });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHookSystemManager::emit(const std::vector<HOOK_CALLBACK_FN*>* callbacks, std::any data) {
|
void CHookSystemManager::emit(const std::vector<SCallbackFNPtr>* callbacks, std::any data) {
|
||||||
if (callbacks->empty())
|
if (callbacks->empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (auto& cb : *callbacks)
|
std::vector<HANDLE> faultyHandles;
|
||||||
(*cb)(cb, data);
|
|
||||||
|
for (auto& cb : *callbacks) {
|
||||||
|
|
||||||
|
m_bCurrentEventPlugin = false;
|
||||||
|
|
||||||
|
if (!cb.handle) {
|
||||||
|
// we don't guard hl hooks
|
||||||
|
(*cb.fn)(cb.fn, data);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_bCurrentEventPlugin = true;
|
||||||
|
|
||||||
|
if (std::find(faultyHandles.begin(), faultyHandles.end(), cb.handle) != faultyHandles.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!setjmp(m_jbHookFaultJumpBuf))
|
||||||
|
(*cb.fn)(cb.fn, data);
|
||||||
|
else {
|
||||||
|
// this module crashed.
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
// TODO: this works only once...?
|
||||||
|
faultyHandles.push_back(cb.handle);
|
||||||
|
Debug::log(ERR, " [hookSystem] Hook from plugin %lx caused a SIGSEGV, queueing for unloading.", cb.handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!faultyHandles.empty()) {
|
||||||
|
for (auto& h : faultyHandles)
|
||||||
|
g_pPluginSystem->unloadPlugin(g_pPluginSystem->getPluginByHandle(h), true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<HOOK_CALLBACK_FN*>* CHookSystemManager::getVecForEvent(const std::string& event) {
|
std::vector<SCallbackFNPtr>* CHookSystemManager::getVecForEvent(const std::string& event) {
|
||||||
auto IT = std::find_if(m_lpRegisteredHooks.begin(), m_lpRegisteredHooks.end(), [&](const auto& other) { return other.first == event; });
|
auto IT = std::find_if(m_lpRegisteredHooks.begin(), m_lpRegisteredHooks.end(), [&](const auto& other) { return other.first == event; });
|
||||||
|
|
||||||
if (IT != m_lpRegisteredHooks.end())
|
if (IT != m_lpRegisteredHooks.end())
|
||||||
return &IT->second;
|
return &IT->second;
|
||||||
|
|
||||||
Debug::log(LOG, "[hookSystem] New hook event registered: %s", event.c_str());
|
Debug::log(LOG, " [hookSystem] New hook event registered: %s", event.c_str());
|
||||||
|
|
||||||
return &m_lpRegisteredHooks.emplace_back(std::make_pair<>(event, std::vector<HOOK_CALLBACK_FN*>{})).second;
|
return &m_lpRegisteredHooks.emplace_back(std::make_pair<>(event, std::vector<SCallbackFNPtr>{})).second;
|
||||||
}
|
}
|
|
@ -4,11 +4,21 @@
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <any>
|
#include <any>
|
||||||
|
#include <array>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
|
#include <csetjmp>
|
||||||
|
|
||||||
|
#include "../plugins/PluginAPI.hpp"
|
||||||
|
|
||||||
// global typedef for hooked functions. Passes itself as a ptr when called, and `data` additionally.
|
// global typedef for hooked functions. Passes itself as a ptr when called, and `data` additionally.
|
||||||
typedef std::function<void(void*, std::any)> HOOK_CALLBACK_FN;
|
typedef std::function<void(void*, std::any)> HOOK_CALLBACK_FN;
|
||||||
|
|
||||||
|
struct SCallbackFNPtr {
|
||||||
|
HOOK_CALLBACK_FN* fn = nullptr;
|
||||||
|
HANDLE handle = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
#define EMIT_HOOK_EVENT(name, param) \
|
#define EMIT_HOOK_EVENT(name, param) \
|
||||||
{ \
|
{ \
|
||||||
static auto* const PEVENTVEC = g_pHookSystem->getVecForEvent(name); \
|
static auto* const PEVENTVEC = g_pHookSystem->getVecForEvent(name); \
|
||||||
|
@ -20,17 +30,20 @@ class CHookSystemManager {
|
||||||
CHookSystemManager();
|
CHookSystemManager();
|
||||||
|
|
||||||
// returns the pointer to the function
|
// returns the pointer to the function
|
||||||
HOOK_CALLBACK_FN* hookDynamic(const std::string& event, HOOK_CALLBACK_FN fn);
|
HOOK_CALLBACK_FN* hookDynamic(const std::string& event, HOOK_CALLBACK_FN fn, HANDLE handle = nullptr);
|
||||||
void hookStatic(const std::string& event, HOOK_CALLBACK_FN* fn);
|
void hookStatic(const std::string& event, HOOK_CALLBACK_FN* fn, HANDLE handle = nullptr);
|
||||||
void unhook(HOOK_CALLBACK_FN* fn);
|
void unhook(HOOK_CALLBACK_FN* fn);
|
||||||
|
|
||||||
void emit(const std::vector<HOOK_CALLBACK_FN*>* callbacks, std::any data = 0);
|
void emit(const std::vector<SCallbackFNPtr>* callbacks, std::any data = 0);
|
||||||
std::vector<HOOK_CALLBACK_FN*>* getVecForEvent(const std::string& event);
|
std::vector<SCallbackFNPtr>* getVecForEvent(const std::string& event);
|
||||||
|
|
||||||
|
bool m_bCurrentEventPlugin = false;
|
||||||
|
jmp_buf m_jbHookFaultJumpBuf;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// todo: this is slow. Maybe static ptrs should be somehow allowed. unique ptr for vec?
|
// todo: this is slow. Maybe static ptrs should be somehow allowed. unique ptr for vec?
|
||||||
std::list<std::pair<std::string, std::vector<HOOK_CALLBACK_FN*>>> m_lpRegisteredHooks;
|
std::list<std::pair<std::string, std::vector<SCallbackFNPtr>>> m_lpRegisteredHooks;
|
||||||
std::list<HOOK_CALLBACK_FN> m_lCallbackFunctions;
|
std::list<HOOK_CALLBACK_FN> m_lCallbackFunctions;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::unique_ptr<CHookSystemManager> g_pHookSystem;
|
inline std::unique_ptr<CHookSystemManager> g_pHookSystem;
|
|
@ -1770,6 +1770,11 @@ void CKeybindManager::focusWindow(std::string regexp) {
|
||||||
|
|
||||||
Debug::log(LOG, "Focusing to window name: %s", PWINDOW->m_szTitle.c_str());
|
Debug::log(LOG, "Focusing to window name: %s", PWINDOW->m_szTitle.c_str());
|
||||||
|
|
||||||
|
if (PWINDOW->isHidden() && PWINDOW->m_sGroupData.pNextWindow) {
|
||||||
|
// grouped, change the current to us
|
||||||
|
PWINDOW->setGroupCurrent(PWINDOW);
|
||||||
|
}
|
||||||
|
|
||||||
g_pCompositor->focusWindow(PWINDOW);
|
g_pCompositor->focusWindow(PWINDOW);
|
||||||
|
|
||||||
const auto MIDPOINT = PWINDOW->m_vRealPosition.goalv() + PWINDOW->m_vRealSize.goalv() / 2.f;
|
const auto MIDPOINT = PWINDOW->m_vRealPosition.goalv() + PWINDOW->m_vRealSize.goalv() / 2.f;
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
class CInputManager;
|
class CInputManager;
|
||||||
class CConfigManager;
|
class CConfigManager;
|
||||||
|
class CPluginSystem;
|
||||||
|
|
||||||
struct SKeybind {
|
struct SKeybind {
|
||||||
std::string key = "";
|
std::string key = "";
|
||||||
|
|
|
@ -1,29 +1,51 @@
|
||||||
#include "LayoutManager.hpp"
|
#include "LayoutManager.hpp"
|
||||||
|
|
||||||
IHyprLayout* CLayoutManager::getCurrentLayout() {
|
CLayoutManager::CLayoutManager() {
|
||||||
switch (m_iCurrentLayoutID) {
|
m_vLayouts.emplace_back(std::make_pair<>("dwindle", &m_cDwindleLayout));
|
||||||
case LAYOUT_DWINDLE: return &m_cDwindleLayout;
|
m_vLayouts.emplace_back(std::make_pair<>("master", &m_cMasterLayout));
|
||||||
case LAYOUT_MASTER: return &m_cMasterLayout;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// fallback
|
IHyprLayout* CLayoutManager::getCurrentLayout() {
|
||||||
return &m_cDwindleLayout;
|
return m_vLayouts[m_iCurrentLayoutID].second;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLayoutManager::switchToLayout(std::string layout) {
|
void CLayoutManager::switchToLayout(std::string layout) {
|
||||||
if (layout == "dwindle") {
|
for (size_t i = 0; i < m_vLayouts.size(); ++i) {
|
||||||
if (m_iCurrentLayoutID != LAYOUT_DWINDLE) {
|
if (m_vLayouts[i].first == layout) {
|
||||||
getCurrentLayout()->onDisable();
|
getCurrentLayout()->onDisable();
|
||||||
m_iCurrentLayoutID = LAYOUT_DWINDLE;
|
m_iCurrentLayoutID = i;
|
||||||
getCurrentLayout()->onEnable();
|
getCurrentLayout()->onEnable();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} else if (layout == "master") {
|
|
||||||
if (m_iCurrentLayoutID != LAYOUT_MASTER) {
|
|
||||||
getCurrentLayout()->onDisable();
|
|
||||||
m_iCurrentLayoutID = LAYOUT_MASTER;
|
|
||||||
getCurrentLayout()->onEnable();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Debug::log(ERR, "Unknown layout %s!", layout.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Debug::log(ERR, "Unknown layout!");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLayoutManager::addLayout(const std::string& name, IHyprLayout* layout) {
|
||||||
|
if (std::find_if(m_vLayouts.begin(), m_vLayouts.end(), [&](const auto& other) { return other.first == name || other.second == layout; }) != m_vLayouts.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_vLayouts.emplace_back(std::make_pair<>(name, layout));
|
||||||
|
|
||||||
|
Debug::log(LOG, "Added new layout %s at %lx", name.c_str(), layout);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLayoutManager::removeLayout(IHyprLayout* layout) {
|
||||||
|
const auto IT = std::find_if(m_vLayouts.begin(), m_vLayouts.end(), [&](const auto& other) { return other.second == layout; });
|
||||||
|
|
||||||
|
if (IT == m_vLayouts.end() || IT->first == "dwindle" || IT->first == "master")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (m_iCurrentLayoutID == IT - m_vLayouts.begin()) {
|
||||||
|
switchToLayout("dwindle");
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug::log(LOG, "Removed a layout %s at %lx", IT->first.c_str(), layout);
|
||||||
|
|
||||||
|
std::erase(m_vLayouts, *IT);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,20 +5,28 @@
|
||||||
|
|
||||||
class CLayoutManager {
|
class CLayoutManager {
|
||||||
public:
|
public:
|
||||||
|
CLayoutManager();
|
||||||
|
|
||||||
IHyprLayout* getCurrentLayout();
|
IHyprLayout* getCurrentLayout();
|
||||||
|
|
||||||
void switchToLayout(std::string);
|
void switchToLayout(std::string);
|
||||||
|
|
||||||
|
bool addLayout(const std::string& name, IHyprLayout* layout);
|
||||||
|
bool removeLayout(IHyprLayout* layout);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum HYPRLAYOUTS {
|
enum HYPRLAYOUTS
|
||||||
|
{
|
||||||
LAYOUT_DWINDLE = 0,
|
LAYOUT_DWINDLE = 0,
|
||||||
LAYOUT_MASTER
|
LAYOUT_MASTER
|
||||||
};
|
};
|
||||||
|
|
||||||
HYPRLAYOUTS m_iCurrentLayoutID = LAYOUT_DWINDLE;
|
int m_iCurrentLayoutID = LAYOUT_DWINDLE;
|
||||||
|
|
||||||
CHyprDwindleLayout m_cDwindleLayout;
|
CHyprDwindleLayout m_cDwindleLayout;
|
||||||
CHyprMasterLayout m_cMasterLayout;
|
CHyprMasterLayout m_cMasterLayout;
|
||||||
|
|
||||||
|
std::vector<std::pair<std::string, IHyprLayout*>> m_vLayouts;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::unique_ptr<CLayoutManager> g_pLayoutManager;
|
inline std::unique_ptr<CLayoutManager> g_pLayoutManager;
|
|
@ -136,7 +136,7 @@ void CHyprXWaylandManager::sendCloseWindow(CWindow* pWindow) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHyprXWaylandManager::setWindowSize(CWindow* pWindow, const Vector2D& size, bool force) {
|
void CHyprXWaylandManager::setWindowSize(CWindow* pWindow, Vector2D size, bool force) {
|
||||||
|
|
||||||
if (!force &&
|
if (!force &&
|
||||||
((pWindow->m_vReportedSize == size && pWindow->m_vRealPosition.vec() == pWindow->m_vReportedPosition) || (pWindow->m_vReportedSize == size && !pWindow->m_bIsX11)))
|
((pWindow->m_vReportedSize == size && pWindow->m_vRealPosition.vec() == pWindow->m_vReportedPosition) || (pWindow->m_vReportedSize == size && !pWindow->m_bIsX11)))
|
||||||
|
|
|
@ -17,7 +17,7 @@ class CHyprXWaylandManager {
|
||||||
std::string getTitle(CWindow*);
|
std::string getTitle(CWindow*);
|
||||||
std::string getAppIDClass(CWindow*);
|
std::string getAppIDClass(CWindow*);
|
||||||
void sendCloseWindow(CWindow*);
|
void sendCloseWindow(CWindow*);
|
||||||
void setWindowSize(CWindow*, const Vector2D&, bool force = false);
|
void setWindowSize(CWindow*, Vector2D, bool force = false);
|
||||||
void setWindowStyleTiled(CWindow*, uint32_t);
|
void setWindowStyleTiled(CWindow*, uint32_t);
|
||||||
void setWindowFullscreen(CWindow*, bool);
|
void setWindowFullscreen(CWindow*, bool);
|
||||||
wlr_surface* surfaceAt(CWindow*, const Vector2D&, Vector2D&);
|
wlr_surface* surfaceAt(CWindow*, const Vector2D&, Vector2D&);
|
||||||
|
|
|
@ -69,11 +69,6 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
|
||||||
if (!g_pCompositor->m_bReadyToProcess || g_pCompositor->m_bIsShuttingDown)
|
if (!g_pCompositor->m_bReadyToProcess || g_pCompositor->m_bIsShuttingDown)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!g_pCompositor->m_sSeat.mouse) {
|
|
||||||
Debug::log(ERR, "BUG THIS: Mouse move on mouse nullptr!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!g_pCompositor->m_bDPMSStateON && *PMOUSEDPMS) {
|
if (!g_pCompositor->m_bDPMSStateON && *PMOUSEDPMS) {
|
||||||
// enable dpms
|
// enable dpms
|
||||||
g_pKeybindManager->dpms("on");
|
g_pKeybindManager->dpms("on");
|
||||||
|
@ -85,13 +80,15 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
|
||||||
if (MOUSECOORDSFLOORED == m_vLastCursorPosFloored && !refocus)
|
if (MOUSECOORDSFLOORED == m_vLastCursorPosFloored && !refocus)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
EMIT_HOOK_EVENT("mouseMove", MOUSECOORDSFLOORED);
|
||||||
|
|
||||||
m_vLastCursorPosFloored = MOUSECOORDSFLOORED;
|
m_vLastCursorPosFloored = MOUSECOORDSFLOORED;
|
||||||
|
|
||||||
const auto PMONITOR = g_pCompositor->getMonitorFromCursor();
|
const auto PMONITOR = g_pCompositor->getMonitorFromCursor();
|
||||||
|
|
||||||
// constraints
|
// constraints
|
||||||
// All constraints TODO: multiple mice?
|
// All constraints TODO: multiple mice?
|
||||||
if (g_pCompositor->m_sSeat.mouse->currentConstraint && !g_pCompositor->m_sSeat.exclusiveClient && !g_pSessionLockManager->isSessionLocked()) {
|
if (g_pCompositor->m_sSeat.mouse && g_pCompositor->m_sSeat.mouse->currentConstraint && !g_pCompositor->m_sSeat.exclusiveClient && !g_pSessionLockManager->isSessionLocked()) {
|
||||||
// XWayland windows sometimes issue constraints weirdly.
|
// XWayland windows sometimes issue constraints weirdly.
|
||||||
// TODO: We probably should search their parent. wlr_xwayland_surface->parent
|
// TODO: We probably should search their parent. wlr_xwayland_surface->parent
|
||||||
const auto CONSTRAINTWINDOW = g_pCompositor->getConstraintWindow(g_pCompositor->m_sSeat.mouse);
|
const auto CONSTRAINTWINDOW = g_pCompositor->getConstraintWindow(g_pCompositor->m_sSeat.mouse);
|
||||||
|
@ -311,6 +308,18 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
|
||||||
setCursorIconOnBorder(pFoundWindow);
|
setCursorIconOnBorder(pFoundWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if we're on an input deco, reset cursor. Don't on overriden
|
||||||
|
if (!m_bCursorImageOverriden) {
|
||||||
|
if (!VECINRECT(m_vLastCursorPosFloored, pFoundWindow->m_vRealPosition.vec().x, pFoundWindow->m_vRealPosition.vec().y,
|
||||||
|
pFoundWindow->m_vRealPosition.vec().x + pFoundWindow->m_vRealSize.vec().x, pFoundWindow->m_vRealPosition.vec().y + pFoundWindow->m_vRealSize.vec().y)) {
|
||||||
|
wlr_xcursor_manager_set_cursor_image(g_pCompositor->m_sWLRXCursorMgr, "left_ptr", g_pCompositor->m_sWLRCursor);
|
||||||
|
cursorSurfaceInfo.bUsed = false;
|
||||||
|
} else if (!cursorSurfaceInfo.bUsed) {
|
||||||
|
cursorSurfaceInfo.bUsed = true;
|
||||||
|
wlr_cursor_set_surface(g_pCompositor->m_sWLRCursor, cursorSurfaceInfo.pSurface, cursorSurfaceInfo.vHotspot.x, cursorSurfaceInfo.vHotspot.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (*PFOLLOWMOUSE != 1 && !refocus) {
|
if (*PFOLLOWMOUSE != 1 && !refocus) {
|
||||||
if (pFoundWindow != g_pCompositor->m_pLastWindow && g_pCompositor->m_pLastWindow &&
|
if (pFoundWindow != g_pCompositor->m_pLastWindow && g_pCompositor->m_pLastWindow &&
|
||||||
((pFoundWindow->m_bIsFloating && *PFLOATBEHAVIOR == 2) || (g_pCompositor->m_pLastWindow->m_bIsFloating != pFoundWindow->m_bIsFloating && *PFLOATBEHAVIOR != 0))) {
|
((pFoundWindow->m_bIsFloating && *PFLOATBEHAVIOR == 2) || (g_pCompositor->m_pLastWindow->m_bIsFloating != pFoundWindow->m_bIsFloating && *PFLOATBEHAVIOR != 0))) {
|
||||||
|
@ -365,6 +374,8 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
|
||||||
void CInputManager::onMouseButton(wlr_pointer_button_event* e) {
|
void CInputManager::onMouseButton(wlr_pointer_button_event* e) {
|
||||||
wlr_idle_notify_activity(g_pCompositor->m_sWLRIdle, g_pCompositor->m_sSeat.seat);
|
wlr_idle_notify_activity(g_pCompositor->m_sWLRIdle, g_pCompositor->m_sSeat.seat);
|
||||||
|
|
||||||
|
EMIT_HOOK_EVENT("mouseButton", e);
|
||||||
|
|
||||||
m_tmrLastCursorMovement.reset();
|
m_tmrLastCursorMovement.reset();
|
||||||
|
|
||||||
if (e->state == WLR_BUTTON_PRESSED) {
|
if (e->state == WLR_BUTTON_PRESSED) {
|
||||||
|
@ -401,6 +412,15 @@ void CInputManager::processMouseRequest(wlr_seat_pointer_request_set_cursor_even
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cursorSurfaceInfo.pSurface = e->surface;
|
||||||
|
|
||||||
|
if (e->surface) {
|
||||||
|
hyprListener_CursorSurfaceDestroy.removeCallback();
|
||||||
|
hyprListener_CursorSurfaceDestroy.initCallback(
|
||||||
|
&e->surface->events.destroy, [&](void* owner, void* data) { cursorSurfaceInfo.pSurface = nullptr; }, this, "InputManager");
|
||||||
|
cursorSurfaceInfo.vHotspot = {e->hotspot_x, e->hotspot_y};
|
||||||
|
}
|
||||||
|
|
||||||
if (e->seat_client == g_pCompositor->m_sSeat.seat->pointer_state.focused_client)
|
if (e->seat_client == g_pCompositor->m_sSeat.seat->pointer_state.focused_client)
|
||||||
wlr_cursor_set_surface(g_pCompositor->m_sWLRCursor, e->surface, e->hotspot_x, e->hotspot_y);
|
wlr_cursor_set_surface(g_pCompositor->m_sWLRCursor, e->surface, e->hotspot_x, e->hotspot_y);
|
||||||
}
|
}
|
||||||
|
@ -462,7 +482,7 @@ void CInputManager::processMouseDownNormal(wlr_pointer_button_event* e) {
|
||||||
if (*PFOLLOWMOUSE == 3) // don't refocus on full loose
|
if (*PFOLLOWMOUSE == 3) // don't refocus on full loose
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (!g_pCompositor->m_sSeat.mouse->currentConstraint)
|
if (!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->currentConstraint)
|
||||||
refocus();
|
refocus();
|
||||||
|
|
||||||
// if clicked on a floating window make it top
|
// if clicked on a floating window make it top
|
||||||
|
@ -1094,7 +1114,7 @@ void CInputManager::constrainMouse(SMouse* pMouse, wlr_pointer_constraint_v1* co
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInputManager::unconstrainMouse() {
|
void CInputManager::unconstrainMouse() {
|
||||||
if (!g_pCompositor->m_sSeat.mouse->currentConstraint)
|
if (!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->currentConstraint)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const auto CONSTRAINTWINDOW = g_pCompositor->getConstraintWindow(g_pCompositor->m_sSeat.mouse);
|
const auto CONSTRAINTWINDOW = g_pCompositor->getConstraintWindow(g_pCompositor->m_sSeat.mouse);
|
||||||
|
|
|
@ -7,18 +7,21 @@
|
||||||
#include "../../helpers/Timer.hpp"
|
#include "../../helpers/Timer.hpp"
|
||||||
#include "InputMethodRelay.hpp"
|
#include "InputMethodRelay.hpp"
|
||||||
|
|
||||||
enum eClickBehaviorMode {
|
enum eClickBehaviorMode
|
||||||
|
{
|
||||||
CLICKMODE_DEFAULT = 0,
|
CLICKMODE_DEFAULT = 0,
|
||||||
CLICKMODE_KILL
|
CLICKMODE_KILL
|
||||||
};
|
};
|
||||||
|
|
||||||
enum eMouseBindMode {
|
enum eMouseBindMode
|
||||||
|
{
|
||||||
MBIND_INVALID = -1,
|
MBIND_INVALID = -1,
|
||||||
MBIND_MOVE = 0,
|
MBIND_MOVE = 0,
|
||||||
MBIND_RESIZE
|
MBIND_RESIZE
|
||||||
};
|
};
|
||||||
|
|
||||||
enum eBorderIconDirection {
|
enum eBorderIconDirection
|
||||||
|
{
|
||||||
BORDERICON_NONE,
|
BORDERICON_NONE,
|
||||||
BORDERICON_UP,
|
BORDERICON_UP,
|
||||||
BORDERICON_DOWN,
|
BORDERICON_DOWN,
|
||||||
|
@ -210,6 +213,14 @@ class CInputManager {
|
||||||
void setBorderCursorIcon(eBorderIconDirection);
|
void setBorderCursorIcon(eBorderIconDirection);
|
||||||
void setCursorIconOnBorder(CWindow* w);
|
void setCursorIconOnBorder(CWindow* w);
|
||||||
|
|
||||||
|
// cursor surface
|
||||||
|
struct cursorSI {
|
||||||
|
wlr_surface* pSurface = nullptr;
|
||||||
|
Vector2D vHotspot;
|
||||||
|
bool bUsed = false;
|
||||||
|
} cursorSurfaceInfo;
|
||||||
|
DYNLISTENER(CursorSurfaceDestroy);
|
||||||
|
|
||||||
friend class CKeybindManager;
|
friend class CKeybindManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,13 @@ void CInputManager::onTouchDown(wlr_touch_down_event* e) {
|
||||||
|
|
||||||
refocus();
|
refocus();
|
||||||
|
|
||||||
|
if (m_ecbClickBehavior == CLICKMODE_KILL) {
|
||||||
|
wlr_pointer_button_event e;
|
||||||
|
e.state = WLR_BUTTON_PRESSED;
|
||||||
|
g_pInputManager->processMouseDownKill(&e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
m_bLastInputTouch = true;
|
m_bLastInputTouch = true;
|
||||||
|
|
||||||
m_sTouchData.touchFocusWindow = m_pFoundWindowToFocus;
|
m_sTouchData.touchFocusWindow = m_pFoundWindowToFocus;
|
||||||
|
|
|
@ -16,6 +16,7 @@ executable('Hyprland', src,
|
||||||
xcb_dep,
|
xcb_dep,
|
||||||
backtrace_dep,
|
backtrace_dep,
|
||||||
systemd_dep,
|
systemd_dep,
|
||||||
|
udis86,
|
||||||
|
|
||||||
dependency('pixman-1'),
|
dependency('pixman-1'),
|
||||||
dependency('gl', 'opengl'),
|
dependency('gl', 'opengl'),
|
||||||
|
|
191
src/plugins/HookSystem.cpp
Normal file
191
src/plugins/HookSystem.cpp
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
#include "HookSystem.hpp"
|
||||||
|
|
||||||
|
#define register
|
||||||
|
#include <udis86.h>
|
||||||
|
#undef register
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
CFunctionHook::CFunctionHook(HANDLE owner, void* source, void* destination) {
|
||||||
|
m_pSource = source;
|
||||||
|
m_pDestination = destination;
|
||||||
|
m_pOwner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
CFunctionHook::~CFunctionHook() {
|
||||||
|
if (m_bActive) {
|
||||||
|
unhook();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t CFunctionHook::getInstructionLenAt(void* start) {
|
||||||
|
ud_t udis;
|
||||||
|
|
||||||
|
ud_init(&udis);
|
||||||
|
ud_set_mode(&udis, 64);
|
||||||
|
ud_set_syntax(&udis, UD_SYN_INTEL);
|
||||||
|
|
||||||
|
size_t curOffset = 1;
|
||||||
|
size_t insSize = 0;
|
||||||
|
while (true) {
|
||||||
|
ud_set_input_buffer(&udis, (uint8_t*)start, curOffset);
|
||||||
|
insSize = ud_disassemble(&udis);
|
||||||
|
if (insSize != curOffset)
|
||||||
|
break;
|
||||||
|
curOffset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for RIP refs
|
||||||
|
std::string ins;
|
||||||
|
if (const auto CINS = ud_insn_asm(&udis); CINS)
|
||||||
|
ins = std::string(CINS);
|
||||||
|
|
||||||
|
if (!ins.empty() && ins.find("rip") != std::string::npos) {
|
||||||
|
// todo: support something besides call qword ptr [rip + 0xdeadbeef]
|
||||||
|
// I don't have an assembler. I don't think udis provides one. Besides, variables might be tricky.
|
||||||
|
if (((uint8_t*)start)[0] == 0xFF && ((uint8_t*)start)[1] == 0x15)
|
||||||
|
m_vTrampolineRIPUses.emplace_back(std::make_pair<>((uint64_t)start - (uint64_t)m_pSource, ins));
|
||||||
|
}
|
||||||
|
|
||||||
|
return insSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t CFunctionHook::probeMinimumJumpSize(void* start, size_t min) {
|
||||||
|
|
||||||
|
size_t size = 0;
|
||||||
|
|
||||||
|
while (size <= min) {
|
||||||
|
// find info about this instruction
|
||||||
|
size_t insLen = getInstructionLenAt(start + size);
|
||||||
|
size += insLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CFunctionHook::hook() {
|
||||||
|
|
||||||
|
// check for unsupported platforms
|
||||||
|
#if !defined(__x86_64__)
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// movabs $0,%rax | jmpq *%rax
|
||||||
|
// offset for addr: 2
|
||||||
|
static constexpr uint8_t ABSOLUTE_JMP_ADDRESS[] = {0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0};
|
||||||
|
static constexpr size_t ABSOLUTE_JMP_ADDRESS_OFFSET = 2;
|
||||||
|
// pushq %rax
|
||||||
|
static constexpr uint8_t PUSH_RAX[] = {0x50};
|
||||||
|
// popq %rax
|
||||||
|
static constexpr uint8_t POP_RAX[] = {0x58};
|
||||||
|
// nop
|
||||||
|
static constexpr uint8_t NOP = 0x90;
|
||||||
|
/*
|
||||||
|
movabs $0,%rax
|
||||||
|
callq *%rax
|
||||||
|
|
||||||
|
offset for addr: 3
|
||||||
|
*/
|
||||||
|
static constexpr uint8_t CALL_WITH_RAX[] = {0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x10};
|
||||||
|
static constexpr size_t CALL_WITH_RAX_ADDRESS_OFFSET = 2;
|
||||||
|
|
||||||
|
// get minimum size to overwrite
|
||||||
|
const auto HOOKSIZE = probeMinimumJumpSize(m_pSource, sizeof(ABSOLUTE_JMP_ADDRESS) + sizeof(PUSH_RAX) + sizeof(POP_RAX));
|
||||||
|
|
||||||
|
// alloc trampoline
|
||||||
|
const auto TRAMPOLINE_SIZE = sizeof(ABSOLUTE_JMP_ADDRESS) + HOOKSIZE + sizeof(PUSH_RAX) + m_vTrampolineRIPUses.size() * (sizeof(CALL_WITH_RAX) - 6);
|
||||||
|
m_pTrampolineAddr = mmap(NULL, TRAMPOLINE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||||
|
|
||||||
|
m_pOriginalBytes = malloc(HOOKSIZE);
|
||||||
|
memcpy(m_pOriginalBytes, m_pSource, HOOKSIZE);
|
||||||
|
|
||||||
|
// populate trampoline
|
||||||
|
memcpy(m_pTrampolineAddr, m_pSource, HOOKSIZE); // first, original func bytes
|
||||||
|
memcpy(m_pTrampolineAddr + HOOKSIZE, PUSH_RAX, sizeof(PUSH_RAX)); // then, pushq %rax
|
||||||
|
memcpy(m_pTrampolineAddr + HOOKSIZE + sizeof(PUSH_RAX), ABSOLUTE_JMP_ADDRESS, sizeof(ABSOLUTE_JMP_ADDRESS)); // then, jump to source
|
||||||
|
|
||||||
|
// fix trampoline %rip calls
|
||||||
|
for (size_t i = 0; i < m_vTrampolineRIPUses.size(); ++i) {
|
||||||
|
size_t callOffset = i * (sizeof(CALL_WITH_RAX) - 6 /* callq [rip + x] */) + m_vTrampolineRIPUses[i].first;
|
||||||
|
size_t realCallAddress = (uint64_t)m_pSource + callOffset + 6 + *((uint32_t*)(m_pSource + callOffset + 2));
|
||||||
|
|
||||||
|
memmove(m_pTrampolineAddr + callOffset + sizeof(CALL_WITH_RAX), m_pTrampolineAddr + callOffset + 6, TRAMPOLINE_SIZE - callOffset - 6);
|
||||||
|
memcpy(m_pTrampolineAddr + callOffset, CALL_WITH_RAX, sizeof(CALL_WITH_RAX));
|
||||||
|
|
||||||
|
*(uint64_t*)(m_pTrampolineAddr + callOffset + CALL_WITH_RAX_ADDRESS_OFFSET) = (uint64_t)realCallAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fixup trampoline addr
|
||||||
|
*(uint64_t*)(m_pTrampolineAddr + TRAMPOLINE_SIZE - sizeof(ABSOLUTE_JMP_ADDRESS) + ABSOLUTE_JMP_ADDRESS_OFFSET) = (uint64_t)(m_pSource + sizeof(ABSOLUTE_JMP_ADDRESS));
|
||||||
|
|
||||||
|
// make jump to hk
|
||||||
|
mprotect(m_pSource - ((uint64_t)m_pSource) % sysconf(_SC_PAGE_SIZE), sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||||
|
memcpy(m_pSource, ABSOLUTE_JMP_ADDRESS, sizeof(ABSOLUTE_JMP_ADDRESS));
|
||||||
|
|
||||||
|
// make popq %rax and NOP all remaining
|
||||||
|
memcpy(m_pSource + sizeof(ABSOLUTE_JMP_ADDRESS), POP_RAX, sizeof(POP_RAX));
|
||||||
|
size_t currentOp = sizeof(ABSOLUTE_JMP_ADDRESS) + sizeof(POP_RAX);
|
||||||
|
memset(m_pSource + currentOp, NOP, HOOKSIZE - currentOp);
|
||||||
|
|
||||||
|
// fixup jump addr
|
||||||
|
*(uint64_t*)(m_pSource + ABSOLUTE_JMP_ADDRESS_OFFSET) = (uint64_t)(m_pDestination);
|
||||||
|
|
||||||
|
// revert mprot
|
||||||
|
mprotect(m_pSource - ((uint64_t)m_pSource) % sysconf(_SC_PAGE_SIZE), sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_EXEC);
|
||||||
|
|
||||||
|
// set original addr to trampo addr
|
||||||
|
m_pOriginal = m_pTrampolineAddr;
|
||||||
|
|
||||||
|
m_bActive = true;
|
||||||
|
m_iHookLen = HOOKSIZE;
|
||||||
|
m_iTrampoLen = TRAMPOLINE_SIZE;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CFunctionHook::unhook() {
|
||||||
|
// check for unsupported platforms
|
||||||
|
#if !defined(__x86_64__)
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!m_bActive)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// allow write to src
|
||||||
|
mprotect(m_pSource - ((uint64_t)m_pSource) % sysconf(_SC_PAGE_SIZE), sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||||
|
|
||||||
|
// write back original bytes
|
||||||
|
memcpy(m_pSource, m_pOriginalBytes, m_iHookLen);
|
||||||
|
|
||||||
|
// revert mprot
|
||||||
|
mprotect(m_pSource - ((uint64_t)m_pSource) % sysconf(_SC_PAGE_SIZE), sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_EXEC);
|
||||||
|
|
||||||
|
// unmap
|
||||||
|
munmap(m_pTrampolineAddr, m_iTrampoLen);
|
||||||
|
|
||||||
|
// reset vars
|
||||||
|
m_bActive = false;
|
||||||
|
m_iHookLen = 0;
|
||||||
|
m_iTrampoLen = 0;
|
||||||
|
m_pTrampolineAddr = nullptr;
|
||||||
|
m_pOriginalBytes = nullptr;
|
||||||
|
|
||||||
|
free(m_pOriginalBytes);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CFunctionHook* CHookSystem::initHook(HANDLE owner, void* source, void* destination) {
|
||||||
|
return m_vHooks.emplace_back(std::make_unique<CFunctionHook>(owner, source, destination)).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CHookSystem::removeHook(CFunctionHook* hook) {
|
||||||
|
std::erase_if(m_vHooks, [&](const auto& other) { return other.get() == hook; });
|
||||||
|
return true; // todo: make false if not found
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHookSystem::removeAllHooksFrom(HANDLE handle) {
|
||||||
|
std::erase_if(m_vHooks, [&](const auto& other) { return other->m_pOwner == handle; });
|
||||||
|
}
|
55
src/plugins/HookSystem.hpp
Normal file
55
src/plugins/HookSystem.hpp
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#define HANDLE void*
|
||||||
|
|
||||||
|
class CFunctionHook {
|
||||||
|
public:
|
||||||
|
CFunctionHook(HANDLE owner, void* source, void* destination);
|
||||||
|
~CFunctionHook();
|
||||||
|
|
||||||
|
bool hook();
|
||||||
|
bool unhook();
|
||||||
|
|
||||||
|
CFunctionHook(const CFunctionHook&) = delete;
|
||||||
|
CFunctionHook(CFunctionHook&&) = delete;
|
||||||
|
CFunctionHook& operator=(const CFunctionHook&) = delete;
|
||||||
|
CFunctionHook& operator=(CFunctionHook&&) = delete;
|
||||||
|
|
||||||
|
void* m_pOriginal = nullptr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void* m_pSource = nullptr;
|
||||||
|
void* m_pFunctionAddr = nullptr;
|
||||||
|
void* m_pTrampolineAddr = nullptr;
|
||||||
|
void* m_pDestination = nullptr;
|
||||||
|
size_t m_iHookLen = 0;
|
||||||
|
size_t m_iTrampoLen = 0;
|
||||||
|
HANDLE m_pOwner = nullptr;
|
||||||
|
bool m_bActive = false;
|
||||||
|
|
||||||
|
std::vector<std::pair<size_t, std::string>> m_vTrampolineRIPUses;
|
||||||
|
|
||||||
|
void* m_pOriginalBytes = nullptr;
|
||||||
|
|
||||||
|
size_t probeMinimumJumpSize(void* start, size_t min);
|
||||||
|
size_t getInstructionLenAt(void* start);
|
||||||
|
|
||||||
|
friend class CHookSystem;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CHookSystem {
|
||||||
|
public:
|
||||||
|
CFunctionHook* initHook(HANDLE handle, void* source, void* destination);
|
||||||
|
bool removeHook(CFunctionHook* hook);
|
||||||
|
|
||||||
|
void removeAllHooksFrom(HANDLE handle);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::unique_ptr<CFunctionHook>> m_vHooks;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::unique_ptr<CHookSystem> g_pFunctionHookSystem;
|
193
src/plugins/PluginAPI.cpp
Normal file
193
src/plugins/PluginAPI.cpp
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
#include "PluginAPI.hpp"
|
||||||
|
#include "../Compositor.hpp"
|
||||||
|
#include "../debug/HyprCtl.hpp"
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
APICALL bool HyprlandAPI::registerCallbackStatic(HANDLE handle, const std::string& event, HOOK_CALLBACK_FN* fn) {
|
||||||
|
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
|
||||||
|
|
||||||
|
if (!PLUGIN)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
g_pHookSystem->hookStatic(event, fn, handle);
|
||||||
|
PLUGIN->registeredCallbacks.emplace_back(std::make_pair<>(event, fn));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
APICALL HOOK_CALLBACK_FN* HyprlandAPI::registerCallbackDynamic(HANDLE handle, const std::string& event, HOOK_CALLBACK_FN fn) {
|
||||||
|
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
|
||||||
|
|
||||||
|
if (!PLUGIN)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
auto* const PFN = g_pHookSystem->hookDynamic(event, fn, handle);
|
||||||
|
PLUGIN->registeredCallbacks.emplace_back(std::make_pair<>(event, PFN));
|
||||||
|
return PFN;
|
||||||
|
}
|
||||||
|
|
||||||
|
APICALL bool HyprlandAPI::unregisterCallback(HANDLE handle, HOOK_CALLBACK_FN* fn) {
|
||||||
|
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
|
||||||
|
|
||||||
|
if (!PLUGIN)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
g_pHookSystem->unhook(fn);
|
||||||
|
std::erase_if(PLUGIN->registeredCallbacks, [&](const auto& other) { return other.second == fn; });
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
APICALL std::string HyprlandAPI::invokeHyprctlCommand(const std::string& call, const std::string& args, const std::string& format) {
|
||||||
|
std::string COMMAND = format + "/" + call + " " + args;
|
||||||
|
return HyprCtl::makeDynamicCall(COMMAND);
|
||||||
|
}
|
||||||
|
|
||||||
|
APICALL bool HyprlandAPI::addLayout(HANDLE handle, const std::string& name, IHyprLayout* layout) {
|
||||||
|
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
|
||||||
|
|
||||||
|
if (!PLUGIN)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
PLUGIN->registeredLayouts.push_back(layout);
|
||||||
|
|
||||||
|
return g_pLayoutManager->addLayout(name, layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
APICALL bool HyprlandAPI::removeLayout(HANDLE handle, IHyprLayout* layout) {
|
||||||
|
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
|
||||||
|
|
||||||
|
if (!PLUGIN)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::erase(PLUGIN->registeredLayouts, layout);
|
||||||
|
|
||||||
|
return g_pLayoutManager->removeLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
APICALL bool HyprlandAPI::reloadConfig() {
|
||||||
|
g_pConfigManager->m_bForceReload = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
APICALL bool HyprlandAPI::addNotification(HANDLE handle, const std::string& text, const CColor& color, const float timeMs) {
|
||||||
|
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
|
||||||
|
|
||||||
|
if (!PLUGIN)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
g_pHyprNotificationOverlay->addNotification(text, color, timeMs);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
APICALL CFunctionHook* HyprlandAPI::createFunctionHook(HANDLE handle, const void* source, const void* destination) {
|
||||||
|
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
|
||||||
|
|
||||||
|
if (!PLUGIN)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return g_pFunctionHookSystem->initHook(handle, (void*)source, (void*)destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
APICALL bool HyprlandAPI::removeFunctionHook(HANDLE handle, CFunctionHook* hook) {
|
||||||
|
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
|
||||||
|
|
||||||
|
if (!PLUGIN)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return g_pFunctionHookSystem->removeHook(hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
APICALL bool HyprlandAPI::addWindowDecoration(HANDLE handle, CWindow* pWindow, IHyprWindowDecoration* pDecoration) {
|
||||||
|
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
|
||||||
|
|
||||||
|
if (!PLUGIN)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!g_pCompositor->windowValidMapped(pWindow))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
PLUGIN->registeredDecorations.push_back(pDecoration);
|
||||||
|
|
||||||
|
pWindow->m_dWindowDecorations.emplace_back(pDecoration);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
APICALL bool HyprlandAPI::removeWindowDecoration(HANDLE handle, IHyprWindowDecoration* pDecoration) {
|
||||||
|
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
|
||||||
|
|
||||||
|
if (!PLUGIN)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (auto& w : g_pCompositor->m_vWindows) {
|
||||||
|
for (auto& d : w->m_dWindowDecorations) {
|
||||||
|
if (d.get() == pDecoration) {
|
||||||
|
std::erase(w->m_dWindowDecorations, d);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
APICALL bool HyprlandAPI::addConfigValue(HANDLE handle, const std::string& name, const SConfigValue& value) {
|
||||||
|
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
|
||||||
|
|
||||||
|
if (!g_pPluginSystem->m_bAllowConfigVars)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!PLUGIN)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (name.find("plugin:") != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
g_pConfigManager->addPluginConfigVar(handle, name, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
APICALL SConfigValue* HyprlandAPI::getConfigValue(HANDLE handle, const std::string& name) {
|
||||||
|
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
|
||||||
|
|
||||||
|
if (!PLUGIN)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return g_pConfigManager->getConfigValuePtrSafe(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
APICALL void* HyprlandAPI::getFunctionAddressFromSignature(HANDLE handle, const std::string& sig) {
|
||||||
|
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
|
||||||
|
|
||||||
|
if (!PLUGIN)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return dlsym(nullptr, sig.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
APICALL bool HyprlandAPI::addDispatcher(HANDLE handle, const std::string& name, std::function<void(std::string)> handler) {
|
||||||
|
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
|
||||||
|
|
||||||
|
if (!PLUGIN)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
PLUGIN->registeredDispatchers.push_back(name);
|
||||||
|
|
||||||
|
g_pKeybindManager->m_mDispatchers[name] = handler;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
APICALL bool HyprlandAPI::removeDispatcher(HANDLE handle, const std::string& name) {
|
||||||
|
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
|
||||||
|
|
||||||
|
if (!PLUGIN)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::erase_if(g_pKeybindManager->m_mDispatchers, [&](const auto& other) { return other.first == name; });
|
||||||
|
std::erase_if(PLUGIN->registeredDispatchers, [&](const auto& other) { return other == name; });
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
217
src/plugins/PluginAPI.hpp
Normal file
217
src/plugins/PluginAPI.hpp
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Hyprland Plugin API.
|
||||||
|
|
||||||
|
Most documentation will be made with comments in this code, but more info can be also found on the wiki.
|
||||||
|
|
||||||
|
!WARNING!
|
||||||
|
The Hyprland API passes C++ objects over, so no ABI compatibility is guaranteed.
|
||||||
|
Make sure to compile your plugins with the same compiler as Hyprland, and ideally,
|
||||||
|
on the same machine.
|
||||||
|
|
||||||
|
See examples/examplePlugin for an example plugin
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define HYPRLAND_API_VERSION "0.1"
|
||||||
|
|
||||||
|
#include "../helpers/Color.hpp"
|
||||||
|
#include "HookSystem.hpp"
|
||||||
|
|
||||||
|
#include <any>
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
typedef std::function<void(void*, std::any)> HOOK_CALLBACK_FN;
|
||||||
|
typedef struct {
|
||||||
|
std::string name;
|
||||||
|
std::string description;
|
||||||
|
std::string author;
|
||||||
|
std::string version;
|
||||||
|
} PLUGIN_DESCRIPTION_INFO;
|
||||||
|
|
||||||
|
#define APICALL extern "C"
|
||||||
|
#define EXPORT __attribute__((visibility("default")))
|
||||||
|
#define REQUIRED
|
||||||
|
#define OPTIONAL
|
||||||
|
#define HANDLE void*
|
||||||
|
|
||||||
|
class IHyprLayout;
|
||||||
|
class CWindow;
|
||||||
|
class IHyprWindowDecoration;
|
||||||
|
struct SConfigValue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
These methods are for the plugin to implement
|
||||||
|
Methods marked with REQUIRED are required.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
called pre-plugin init.
|
||||||
|
In case of a version mismatch, will eject the .so.
|
||||||
|
|
||||||
|
This function should not be modified, see the example plugin.
|
||||||
|
*/
|
||||||
|
typedef REQUIRED std::string (*PPLUGIN_API_VERSION_FUNC)();
|
||||||
|
#define PLUGIN_API_VERSION pluginAPIVersion
|
||||||
|
#define PLUGIN_API_VERSION_FUNC_STR "pluginAPIVersion"
|
||||||
|
|
||||||
|
/*
|
||||||
|
called on plugin init. Passes a handle as the parameter, which the plugin should keep for identification later.
|
||||||
|
The plugin should return a PLUGIN_DESCRIPTION_INFO struct with information about itself.
|
||||||
|
|
||||||
|
Keep in mind this is executed synchronously, and as such any blocking calls to hyprland might hang. (e.g. system("hyprctl ..."))
|
||||||
|
*/
|
||||||
|
typedef REQUIRED PLUGIN_DESCRIPTION_INFO (*PPLUGIN_INIT_FUNC)(HANDLE);
|
||||||
|
#define PLUGIN_INIT pluginInit
|
||||||
|
#define PLUGIN_INIT_FUNC_STR "pluginInit"
|
||||||
|
|
||||||
|
/*
|
||||||
|
called on plugin unload, if that was a user action. If the plugin is being unloaded by an error,
|
||||||
|
this will not be called.
|
||||||
|
|
||||||
|
Hooks are unloaded after exit.
|
||||||
|
*/
|
||||||
|
typedef OPTIONAL void (*PPLUGIN_EXIT_FUNC)(void);
|
||||||
|
#define PLUGIN_EXIT pluginExit
|
||||||
|
#define PLUGIN_EXIT_FUNC_STR "pluginExit"
|
||||||
|
|
||||||
|
/*
|
||||||
|
End plugin methods
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace HyprlandAPI {
|
||||||
|
|
||||||
|
/*
|
||||||
|
Add a config value.
|
||||||
|
All config values MUST be in the plugin: namespace
|
||||||
|
This method may only be called in "pluginInit"
|
||||||
|
|
||||||
|
After you have registered ALL of your config values, you may call `getConfigValue`
|
||||||
|
|
||||||
|
returns: true on success, false on fail
|
||||||
|
*/
|
||||||
|
APICALL bool addConfigValue(HANDLE handle, const std::string& name, const SConfigValue& value);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get a config value.
|
||||||
|
|
||||||
|
returns: a pointer to the config value struct, which is guaranteed to be valid for the life of this plugin, unless another `addConfigValue` is called afterwards.
|
||||||
|
nullptr on error.
|
||||||
|
*/
|
||||||
|
APICALL SConfigValue* getConfigValue(HANDLE handle, const std::string& name);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Register a static (pointer) callback to a selected event.
|
||||||
|
Pointer must be kept valid until unregisterCallback() is called.
|
||||||
|
|
||||||
|
returns: true on success, false on fail
|
||||||
|
*/
|
||||||
|
APICALL bool registerCallbackStatic(HANDLE handle, const std::string& event, HOOK_CALLBACK_FN* fn);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Register a dynamic (function) callback to a selected event.
|
||||||
|
Pointer will be free'd by Hyprland on unregisterCallback().
|
||||||
|
|
||||||
|
returns: a pointer to the newly allocated function. nullptr on fail.
|
||||||
|
*/
|
||||||
|
APICALL HOOK_CALLBACK_FN* registerCallbackDynamic(HANDLE handle, const std::string& event, HOOK_CALLBACK_FN fn);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Unregisters a callback. If the callback was dynamic, frees the memory.
|
||||||
|
|
||||||
|
returns: true on success, false on fail
|
||||||
|
*/
|
||||||
|
APICALL bool unregisterCallback(HANDLE handle, HOOK_CALLBACK_FN* fn);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Calls a hyprctl command.
|
||||||
|
|
||||||
|
returns: the output (as in hyprctl)
|
||||||
|
*/
|
||||||
|
APICALL std::string invokeHyprctlCommand(const std::string& call, const std::string& args, const std::string& format = "");
|
||||||
|
|
||||||
|
/*
|
||||||
|
Adds a layout to Hyprland.
|
||||||
|
|
||||||
|
returns: true on success. False otherwise.
|
||||||
|
*/
|
||||||
|
APICALL bool addLayout(HANDLE handle, const std::string& name, IHyprLayout* layout);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Removes an added layout from Hyprland.
|
||||||
|
|
||||||
|
returns: true on success. False otherwise.
|
||||||
|
*/
|
||||||
|
APICALL bool removeLayout(HANDLE handle, IHyprLayout* layout);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Queues a config reload. Does not take effect immediately.
|
||||||
|
|
||||||
|
returns: true on success. False otherwise.
|
||||||
|
*/
|
||||||
|
APICALL bool reloadConfig();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Adds a notification.
|
||||||
|
|
||||||
|
returns: true on success. False otherwise.
|
||||||
|
*/
|
||||||
|
APICALL bool addNotification(HANDLE handle, const std::string& text, const CColor& color, const float timeMs);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Creates a trampoline function hook to an internal hl func.
|
||||||
|
|
||||||
|
returns: CFunctionHook*
|
||||||
|
|
||||||
|
!WARNING! Hooks are *not* guaranteed any API stability. Internal methods may be removed, added, or renamed. Consider preferring the API whenever possible.
|
||||||
|
*/
|
||||||
|
APICALL CFunctionHook* createFunctionHook(HANDLE handle, const void* source, const void* destination);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Removes a trampoline function hook. Will unhook if still hooked.
|
||||||
|
|
||||||
|
returns: true on success. False otherwise.
|
||||||
|
|
||||||
|
!WARNING! Hooks are *not* guaranteed any API stability. Internal methods may be removed, added, or renamed. Consider preferring the API whenever possible.
|
||||||
|
*/
|
||||||
|
APICALL bool removeFunctionHook(HANDLE handle, CFunctionHook* hook);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Gets a function address from a signature.
|
||||||
|
This is useful for hooking private functions.
|
||||||
|
|
||||||
|
returns: function address, or nullptr on fail.
|
||||||
|
*/
|
||||||
|
APICALL void* getFunctionAddressFromSignature(HANDLE handle, const std::string& sig);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Adds a window decoration to a window
|
||||||
|
|
||||||
|
returns: true on success. False otherwise.
|
||||||
|
*/
|
||||||
|
APICALL bool addWindowDecoration(HANDLE handle, CWindow* pWindow, IHyprWindowDecoration* pDecoration);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Removes a window decoration
|
||||||
|
|
||||||
|
returns: true on success. False otherwise.
|
||||||
|
*/
|
||||||
|
APICALL bool removeWindowDecoration(HANDLE handle, IHyprWindowDecoration* pDecoration);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Adds a keybind dispatcher.
|
||||||
|
|
||||||
|
returns: true on success. False otherwise.
|
||||||
|
*/
|
||||||
|
APICALL bool addDispatcher(HANDLE handle, const std::string& name, std::function<void(std::string)> handler);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Removes a keybind dispatcher.
|
||||||
|
|
||||||
|
returns: true on success. False otherwise.
|
||||||
|
*/
|
||||||
|
APICALL bool removeDispatcher(HANDLE handle, const std::string& name);
|
||||||
|
};
|
145
src/plugins/PluginSystem.cpp
Normal file
145
src/plugins/PluginSystem.cpp
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
#include "PluginSystem.hpp"
|
||||||
|
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <ranges>
|
||||||
|
#include "../Compositor.hpp"
|
||||||
|
|
||||||
|
CPluginSystem::CPluginSystem() {
|
||||||
|
g_pFunctionHookSystem = std::make_unique<CHookSystem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
CPlugin* CPluginSystem::loadPlugin(const std::string& path) {
|
||||||
|
|
||||||
|
if (getPluginByPath(path)) {
|
||||||
|
Debug::log(ERR, " [PluginSystem] Cannot load a plugin twice!");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* const PLUGIN = m_vLoadedPlugins.emplace_back(std::make_unique<CPlugin>()).get();
|
||||||
|
|
||||||
|
PLUGIN->path = path;
|
||||||
|
|
||||||
|
HANDLE MODULE = dlopen(path.c_str(), RTLD_LAZY);
|
||||||
|
|
||||||
|
if (!MODULE) {
|
||||||
|
Debug::log(ERR, " [PluginSystem] Plugin %s could not be loaded: %s", path.c_str(), dlerror());
|
||||||
|
m_vLoadedPlugins.pop_back();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
PLUGIN->m_pHandle = MODULE;
|
||||||
|
|
||||||
|
PPLUGIN_API_VERSION_FUNC apiVerFunc = (PPLUGIN_API_VERSION_FUNC)dlsym(MODULE, PLUGIN_API_VERSION_FUNC_STR);
|
||||||
|
PPLUGIN_INIT_FUNC initFunc = (PPLUGIN_INIT_FUNC)dlsym(MODULE, PLUGIN_INIT_FUNC_STR);
|
||||||
|
|
||||||
|
if (!apiVerFunc || !initFunc) {
|
||||||
|
Debug::log(ERR, " [PluginSystem] Plugin %s could not be loaded. (No apiver/init func)", path.c_str());
|
||||||
|
dlclose(MODULE);
|
||||||
|
m_vLoadedPlugins.pop_back();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string PLUGINAPIVER = apiVerFunc();
|
||||||
|
|
||||||
|
if (PLUGINAPIVER != HYPRLAND_API_VERSION) {
|
||||||
|
Debug::log(ERR, " [PluginSystem] Plugin %s could not be loaded. (API version mismatch)", path.c_str());
|
||||||
|
dlclose(MODULE);
|
||||||
|
m_vLoadedPlugins.pop_back();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
PLUGIN_DESCRIPTION_INFO PLUGINDATA;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!setjmp(m_jbPluginFaultJumpBuf)) {
|
||||||
|
m_bAllowConfigVars = true;
|
||||||
|
PLUGINDATA = initFunc(MODULE);
|
||||||
|
} else {
|
||||||
|
// this module crashed.
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
m_bAllowConfigVars = false;
|
||||||
|
Debug::log(ERR, " [PluginSystem] Plugin %s (Handle %lx) crashed in init. Unloading.", path.c_str(), MODULE);
|
||||||
|
unloadPlugin(PLUGIN, true); // Plugin could've already hooked/done something
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_bAllowConfigVars = false;
|
||||||
|
|
||||||
|
PLUGIN->author = PLUGINDATA.author;
|
||||||
|
PLUGIN->description = PLUGINDATA.description;
|
||||||
|
PLUGIN->version = PLUGINDATA.version;
|
||||||
|
PLUGIN->name = PLUGINDATA.name;
|
||||||
|
|
||||||
|
Debug::log(LOG, " [PluginSystem] Plugin %s loaded. Handle: %lx, path: \"%s\", author: \"%s\", description: \"%s\", version: \"%s\"", PLUGINDATA.name.c_str(), MODULE,
|
||||||
|
path.c_str(), PLUGINDATA.author.c_str(), PLUGINDATA.description.c_str(), PLUGINDATA.version.c_str());
|
||||||
|
|
||||||
|
return PLUGIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPluginSystem::unloadPlugin(const CPlugin* plugin, bool eject) {
|
||||||
|
if (!plugin)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!eject) {
|
||||||
|
PPLUGIN_EXIT_FUNC exitFunc = (PPLUGIN_EXIT_FUNC)dlsym(plugin->m_pHandle, PLUGIN_EXIT_FUNC_STR);
|
||||||
|
if (exitFunc)
|
||||||
|
exitFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& [k, v] : plugin->registeredCallbacks)
|
||||||
|
g_pHookSystem->unhook(v);
|
||||||
|
|
||||||
|
const auto ls = plugin->registeredLayouts;
|
||||||
|
for (auto& l : ls)
|
||||||
|
g_pLayoutManager->removeLayout(l);
|
||||||
|
|
||||||
|
g_pFunctionHookSystem->removeAllHooksFrom(plugin->m_pHandle);
|
||||||
|
|
||||||
|
const auto rd = plugin->registeredDecorations;
|
||||||
|
for (auto& d : rd)
|
||||||
|
HyprlandAPI::removeWindowDecoration(plugin->m_pHandle, d);
|
||||||
|
|
||||||
|
const auto rdi = plugin->registeredDispatchers;
|
||||||
|
for (auto& d : rdi)
|
||||||
|
HyprlandAPI::removeDispatcher(plugin->m_pHandle, d);
|
||||||
|
|
||||||
|
g_pConfigManager->removePluginConfig(plugin->m_pHandle);
|
||||||
|
|
||||||
|
dlclose(plugin->m_pHandle);
|
||||||
|
|
||||||
|
Debug::log(LOG, " [PluginSystem] Plugin %s unloaded.", plugin->name.c_str());
|
||||||
|
|
||||||
|
std::erase_if(m_vLoadedPlugins, [&](const auto& other) { return other->m_pHandle == plugin->m_pHandle; });
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPluginSystem::unloadAllPlugins() {
|
||||||
|
for (auto& p : m_vLoadedPlugins | std::views::reverse)
|
||||||
|
unloadPlugin(p.get(), false); // Unload remaining plugins gracefully
|
||||||
|
}
|
||||||
|
|
||||||
|
CPlugin* CPluginSystem::getPluginByPath(const std::string& path) {
|
||||||
|
for (auto& p : m_vLoadedPlugins) {
|
||||||
|
if (p->path == path)
|
||||||
|
return p.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
CPlugin* CPluginSystem::getPluginByHandle(HANDLE handle) {
|
||||||
|
for (auto& p : m_vLoadedPlugins) {
|
||||||
|
if (p->m_pHandle == handle)
|
||||||
|
return p.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<CPlugin*> CPluginSystem::getAllPlugins() {
|
||||||
|
std::vector<CPlugin*> results(m_vLoadedPlugins.size());
|
||||||
|
for (size_t i = 0; i < m_vLoadedPlugins.size(); ++i)
|
||||||
|
results[i] = m_vLoadedPlugins[i].get();
|
||||||
|
return results;
|
||||||
|
}
|
45
src/plugins/PluginSystem.hpp
Normal file
45
src/plugins/PluginSystem.hpp
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../defines.hpp"
|
||||||
|
#include "PluginAPI.hpp"
|
||||||
|
#include <csetjmp>
|
||||||
|
|
||||||
|
class IHyprWindowDecoration;
|
||||||
|
|
||||||
|
class CPlugin {
|
||||||
|
public:
|
||||||
|
std::string name = "";
|
||||||
|
std::string description = "";
|
||||||
|
std::string author = "";
|
||||||
|
std::string version = "";
|
||||||
|
|
||||||
|
std::string path = "";
|
||||||
|
|
||||||
|
HANDLE m_pHandle = nullptr;
|
||||||
|
|
||||||
|
std::vector<IHyprLayout*> registeredLayouts;
|
||||||
|
std::vector<IHyprWindowDecoration*> registeredDecorations;
|
||||||
|
std::vector<std::pair<std::string, HOOK_CALLBACK_FN*>> registeredCallbacks;
|
||||||
|
std::vector<std::string> registeredDispatchers;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CPluginSystem {
|
||||||
|
public:
|
||||||
|
CPluginSystem();
|
||||||
|
|
||||||
|
CPlugin* loadPlugin(const std::string& path);
|
||||||
|
void unloadPlugin(const CPlugin* plugin, bool eject = false);
|
||||||
|
void unloadAllPlugins();
|
||||||
|
CPlugin* getPluginByPath(const std::string& path);
|
||||||
|
CPlugin* getPluginByHandle(HANDLE handle);
|
||||||
|
std::vector<CPlugin*> getAllPlugins();
|
||||||
|
|
||||||
|
bool m_bAllowConfigVars = false;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::unique_ptr<CPlugin>> m_vLoadedPlugins;
|
||||||
|
|
||||||
|
jmp_buf m_jbPluginFaultJumpBuf;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::unique_ptr<CPluginSystem> g_pPluginSystem;
|
|
@ -3,3 +3,11 @@
|
||||||
#include "../../Window.hpp"
|
#include "../../Window.hpp"
|
||||||
|
|
||||||
IHyprWindowDecoration::~IHyprWindowDecoration() {}
|
IHyprWindowDecoration::~IHyprWindowDecoration() {}
|
||||||
|
|
||||||
|
SWindowDecorationExtents IHyprWindowDecoration::getWindowDecorationReservedArea() {
|
||||||
|
return SWindowDecorationExtents{};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IHyprWindowDecoration::allowsInput() {
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -6,7 +6,8 @@ enum eDecorationType
|
||||||
{
|
{
|
||||||
DECORATION_NONE = -1,
|
DECORATION_NONE = -1,
|
||||||
DECORATION_GROUPBAR,
|
DECORATION_GROUPBAR,
|
||||||
DECORATION_SHADOW
|
DECORATION_SHADOW,
|
||||||
|
DECORATION_CUSTOM
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SWindowDecorationExtents {
|
struct SWindowDecorationExtents {
|
||||||
|
@ -30,4 +31,8 @@ interface IHyprWindowDecoration {
|
||||||
virtual void updateWindow(CWindow*) = 0;
|
virtual void updateWindow(CWindow*) = 0;
|
||||||
|
|
||||||
virtual void damageEntire() = 0;
|
virtual void damageEntire() = 0;
|
||||||
|
|
||||||
|
virtual SWindowDecorationExtents getWindowDecorationReservedArea();
|
||||||
|
|
||||||
|
virtual bool allowsInput();
|
||||||
};
|
};
|
1
subprojects/udis86
Submodule
1
subprojects/udis86
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 5336633af70f3917760a6d441ff02d93477b0c86
|
Loading…
Reference in a new issue