Merge branch 'main' into fix/cursor-min-padding

This commit is contained in:
drendog 2024-06-16 17:54:23 +02:00 committed by GitHub
commit e58589f797
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
101 changed files with 1090 additions and 1297 deletions

View File

@ -68,6 +68,11 @@ runs:
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
cmake --install build
- name: Get hyprutils-git
shell: bash
run: |
git clone https://github.com/hyprwm/hyprutils && cd hyprutils && cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -B build && cmake --build build --target hyprutils && cmake --install build
- name: Get Xorg pacman pkgs
shell: bash
if: inputs.INSTALL_XORG_PKGS == 'true'

View File

@ -113,7 +113,7 @@ pkg_check_modules(deps REQUIRED IMPORTED_TARGET
wayland-server wayland-client wayland-cursor wayland-protocols
cairo pango pangocairo pixman-1
libdrm libinput hwdata libseat libdisplay-info libliftoff libudev gbm
hyprlang>=0.3.2 hyprcursor>=0.1.7
hyprlang>=0.3.2 hyprcursor>=0.1.7 hyprutils>=0.1.1
)
find_package(hyprwayland-scanner 0.3.10 REQUIRED)

View File

@ -42,7 +42,7 @@ uninstall:
pluginenv:
@echo -en "$(MAKE) pluginenv has been deprecated.\nPlease run $(MAKE) all && sudo $(MAKE) installheaders\n"
@exit 1
installheaders:
@if [ ! -f ./src/version.h ]; then echo -en "You need to run $(MAKE) all first.\n" && exit 1; fi

View File

@ -125,7 +125,7 @@ dwindle {
# See https://wiki.hyprland.org/Configuring/Master-Layout/ for more
master {
new_is_master = true
new_status = master
}
# https://wiki.hyprland.org/Configuring/Variables/#misc

View File

@ -13,11 +13,11 @@
]
},
"locked": {
"lastModified": 1717181720,
"narHash": "sha256-yv+QZWsusu/NWjydkxixHC2g+tIJ9v+xkE2EiVpJj6g=",
"lastModified": 1718368322,
"narHash": "sha256-VfMg3RsnRLQzbq0hFIh1dCM09b5C/F/qPFUOgU/CRi0=",
"owner": "hyprwm",
"repo": "hyprcursor",
"rev": "9e27a2c2ceb1e0b85bd55b0afefad196056fe87c",
"rev": "dd3a853c8239d1c3f3f37de7d2b8ae4b4f3840df",
"type": "github"
},
"original": {
@ -53,6 +53,9 @@
},
"hyprlang": {
"inputs": {
"hyprutils": [
"hyprutils"
],
"nixpkgs": [
"nixpkgs"
],
@ -61,11 +64,11 @@
]
},
"locked": {
"lastModified": 1716473782,
"narHash": "sha256-+qLn4lsHU6iL3+HTo1gTQ1tWzet8K9h+IfVemzEQZj8=",
"lastModified": 1717881852,
"narHash": "sha256-XeeVoKHQgfKuXoP6q90sUqKyl7EYy3ol2dVZGM+Jj94=",
"owner": "hyprwm",
"repo": "hyprlang",
"rev": "87d5d984109c839482b88b4795db073eb9ed446f",
"rev": "ec6938c66253429192274d612912649a0cfe4d28",
"type": "github"
},
"original": {
@ -74,6 +77,29 @@
"type": "github"
}
},
"hyprutils": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"systems": [
"systems"
]
},
"locked": {
"lastModified": 1718271409,
"narHash": "sha256-8KvVqtApNt4FWTdn1TqVvw00rpqyG9UuUPA2ilPVD1U=",
"owner": "hyprwm",
"repo": "hyprutils",
"rev": "8e10e0626fb26a14b859b3811b6ed7932400c86e",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprutils",
"type": "github"
}
},
"hyprwayland-scanner": {
"inputs": {
"nixpkgs": [
@ -84,11 +110,11 @@
]
},
"locked": {
"lastModified": 1717784906,
"narHash": "sha256-YxmfxHfWed1fosaa7fC1u7XoKp1anEZU+7Lh/ojRKoM=",
"lastModified": 1718119275,
"narHash": "sha256-nqDYXATNkyGXVmNMkT19fT4sjtSPBDS1LLOxa3Fueo4=",
"owner": "hyprwm",
"repo": "hyprwayland-scanner",
"rev": "0f30f9eca6e404130988554accbb64d1c9ec877d",
"rev": "1419520d5f7f38d35e05504da5c1b38212a38525",
"type": "github"
},
"original": {
@ -99,11 +125,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1717602782,
"narHash": "sha256-pL9jeus5QpX5R+9rsp3hhZ+uplVHscNJh8n8VpqscM0=",
"lastModified": 1718318537,
"narHash": "sha256-4Zu0RYRcAY/VWuu6awwq4opuiD//ahpc2aFHg2CWqFY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e8057b67ebf307f01bdcc8fba94d94f75039d1f6",
"rev": "e9ee548d90ff586a6471b4ae80ae9cfcbceb3420",
"type": "github"
},
"original": {
@ -117,6 +143,7 @@
"inputs": {
"hyprcursor": "hyprcursor",
"hyprlang": "hyprlang",
"hyprutils": "hyprutils",
"hyprwayland-scanner": "hyprwayland-scanner",
"nixpkgs": "nixpkgs",
"systems": "systems",
@ -152,11 +179,11 @@
]
},
"locked": {
"lastModified": 1716290197,
"narHash": "sha256-1u9Exrc7yx9qtES2brDh7/DDZ8w8ap1nboIOAtCgeuM=",
"lastModified": 1718272114,
"narHash": "sha256-KsX7sAwkEFpXiwyjt0HGTnnrUU58wW1jlzj5IA/LRz8=",
"owner": "hyprwm",
"repo": "xdg-desktop-portal-hyprland",
"rev": "91e48d6acd8a5a611d26f925e51559ab743bc438",
"rev": "24be4a26f0706e456fca1b61b8c79f7486a9e86d",
"type": "github"
},
"original": {

View File

@ -18,6 +18,13 @@
url = "github:hyprwm/hyprlang";
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
inputs.hyprutils.follows = "hyprutils";
};
hyprutils = {
url = "github:hyprwm/hyprutils";
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
};
hyprwayland-scanner = {

View File

@ -5,8 +5,12 @@ project(
DESCRIPTION "Control utility for Hyprland"
)
pkg_check_modules(deps REQUIRED IMPORTED_TARGET hyprutils>=0.1.1)
add_executable(hyprctl "main.cpp")
target_link_libraries(hyprctl PUBLIC PkgConfig::deps)
# binary
install(TARGETS hyprctl)

View File

@ -38,7 +38,8 @@ commands:
plugin ... Issue a plugin request
reload [config-only] Issue a reload to force reload the config. Pass
'config-only' to disable monitor reload
rollinglog Prints tail of the log
rollinglog Prints tail of the log. Also supports -f/--follow
option
setcursor <theme> <size> Sets the cursor theme and reloads the cursor
manager
seterror <color> <message...> Sets the hyprctl error string. Color has
@ -112,7 +113,7 @@ create <backend>:
remove <name>:
Removes virtual output. Pass the output's name, as found in
'hyprctl monitors'
flags:
See 'hyprctl --help')#";

View File

@ -1,9 +1,9 @@
#include <ctype.h>
#include <cctype>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
@ -12,7 +12,7 @@
#include <unistd.h>
#include <ranges>
#include <algorithm>
#include <signal.h>
#include <csignal>
#include <format>
#include <iostream>
@ -22,8 +22,11 @@
#include <vector>
#include <deque>
#include <filesystem>
#include <stdarg.h>
#include <cstdarg>
#include <regex>
#include <sys/socket.h>
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
#include "Strings.hpp"
@ -98,13 +101,53 @@ std::vector<SInstanceData> instances() {
return result;
}
int request(std::string arg, int minArgs = 0) {
static volatile bool sigintReceived = false;
void intHandler(int sig) {
sigintReceived = true;
std::cout << "[hyprctl] SIGINT received, closing connection" << std::endl;
}
int rollingRead(const int socket) {
sigintReceived = false;
signal(SIGINT, intHandler);
constexpr size_t BUFFER_SIZE = 8192;
std::array<char, BUFFER_SIZE> buffer = {0};
int sizeWritten = 0;
std::cout << "[hyprctl] reading from socket following up log:" << std::endl;
while (!sigintReceived) {
sizeWritten = read(socket, buffer.data(), BUFFER_SIZE);
if (sizeWritten < 0 && errno != EAGAIN) {
if (errno != EINTR)
std::cout << "Couldn't read (5) " << strerror(errno) << ":" << errno << std::endl;
close(socket);
return 5;
}
if (sizeWritten == 0)
break;
if (sizeWritten > 0) {
std::cout << std::string(buffer.data(), sizeWritten);
buffer.fill('\0');
}
usleep(100000);
}
close(socket);
return 0;
}
int request(std::string arg, int minArgs = 0, bool needRoll = false) {
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
auto t = timeval{.tv_sec = 0, .tv_usec = 100000};
setsockopt(SERVERSOCKET, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(struct timeval));
const auto ARGS = std::count(arg.begin(), arg.end(), ' ');
if (ARGS < minArgs) {
log("Not enough arguments, expected at least " + minArgs);
log(std::format("Not enough arguments in '{}', expected at least {}", arg, minArgs));
return -1;
}
@ -139,6 +182,9 @@ int request(std::string arg, int minArgs = 0) {
return 4;
}
if (needRoll)
return rollingRead(SERVERSOCKET);
std::string reply = "";
char buffer[8192] = {0};
@ -270,12 +316,6 @@ std::deque<std::string> splitArgs(int argc, char** argv) {
return result;
}
bool isNumber(const std::string& str, bool allowfloat) {
if (str.empty())
return false;
return std::ranges::all_of(str.begin(), str.end(), [&](char c) { return isdigit(c) != 0 || c == '-' || (allowfloat && c == '.'); });
}
int main(int argc, char** argv) {
bool parseArgs = true;
@ -288,6 +328,7 @@ int main(int argc, char** argv) {
std::string fullArgs = "";
const auto ARGS = splitArgs(argc, argv);
bool json = false;
bool needRoll = false;
std::string overrideInstance = "";
for (std::size_t i = 0; i < ARGS.size(); ++i) {
@ -307,6 +348,9 @@ int main(int argc, char** argv) {
fullArgs += "a";
} else if ((ARGS[i] == "-c" || ARGS[i] == "--config") && !fullArgs.contains("c")) {
fullArgs += "c";
} else if ((ARGS[i] == "-f" || ARGS[i] == "--follow") && !fullArgs.contains("f")) {
fullArgs += "f";
needRoll = true;
} else if (ARGS[i] == "--batch") {
fullRequest = "--batch ";
} else if (ARGS[i] == "--instance" || ARGS[i] == "-i") {
@ -366,6 +410,11 @@ int main(int argc, char** argv) {
return 0;
}
if (needRoll && !fullRequest.contains("/rollinglog")) {
log("only 'rollinglog' command supports '--follow' option");
return 1;
}
if (overrideInstance.contains("_"))
instanceSignature = overrideInstance;
else if (!overrideInstance.empty()) {
@ -425,6 +474,8 @@ int main(int argc, char** argv) {
exitStatus = request(fullRequest, 1);
else if (fullRequest.contains("/--help"))
std::cout << USAGE << std::endl;
else if (fullRequest.contains("/rollinglog") && needRoll)
exitStatus = request(fullRequest, 0, true);
else {
exitStatus = request(fullRequest);
}

View File

@ -1,4 +1,7 @@
executable('hyprctl', 'main.cpp',
dependencies: [
dependency('hyprutils', version: '>= 0.1.1'),
],
install: true
)

View File

@ -9,11 +9,11 @@ file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
set(CMAKE_CXX_STANDARD 23)
pkg_check_modules(tomlplusplus REQUIRED IMPORTED_TARGET tomlplusplus)
pkg_check_modules(deps REQUIRED IMPORTED_TARGET tomlplusplus hyprutils>=0.1.1)
add_executable(hyprpm ${SRCFILES})
target_link_libraries(hyprpm PUBLIC PkgConfig::tomlplusplus)
target_link_libraries(hyprpm PUBLIC PkgConfig::deps)
# binary
install(TARGETS hyprpm)

View File

@ -19,30 +19,15 @@
#include <toml++/toml.hpp>
static std::string removeBeginEndSpacesTabs(std::string str) {
if (str.empty())
return str;
int countBefore = 0;
while (str[countBefore] == ' ' || str[countBefore] == '\t') {
countBefore++;
}
int countAfter = 0;
while ((int)str.length() - countAfter - 1 >= 0 && (str[str.length() - countAfter - 1] == ' ' || str[str.length() - 1 - countAfter] == '\t')) {
countAfter++;
}
str = str.substr(countBefore, str.length() - countBefore - countAfter);
return str;
}
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
static std::string execAndGet(std::string cmd) {
cmd += " 2>&1";
std::array<char, 128> buffer;
std::string result;
const std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
std::array<char, 128> buffer;
std::string result;
using PcloseType = int (*)(FILE*);
const std::unique_ptr<FILE, PcloseType> pipe(popen(cmd.c_str(), "r"), static_cast<PcloseType>(pclose));
if (!pipe)
return "";
@ -374,7 +359,7 @@ eHeadersErrors CPluginManager::headersValid() {
if (PATH.ends_with("protocols") || PATH.ends_with("wlroots-hyprland"))
continue;
verHeader = removeBeginEndSpacesTabs(PATH.substr(2)) + "/hyprland/src/version.h";
verHeader = trim(PATH.substr(2)) + "/hyprland/src/version.h";
break;
}
@ -442,12 +427,12 @@ bool CPluginManager::updateHeaders(bool force) {
progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " Cloning https://github.com/hyprwm/hyprland, this might take a moment.");
const bool bShallow = HLVER.branch == "main" || HLVER.branch == "";
const bool bShallow = (HLVER.branch == "main" || HLVER.branch == "") && !m_bNoShallow;
// let us give a bit of leg-room for shallowing
// due to timezones, etc.
const std::string SHALLOW_DATE =
removeBeginEndSpacesTabs(HLVER.date).empty() ? "" : execAndGet("LC_TIME=\"en_US.UTF-8\" date --date='" + HLVER.date + " - 1 weeks' '+\%a \%b \%d \%H:\%M:\%S \%Y'");
trim(HLVER.date).empty() ? "" : execAndGet("LC_TIME=\"en_US.UTF-8\" date --date='" + HLVER.date + " - 1 weeks' '+\%a \%b \%d \%H:\%M:\%S \%Y'");
if (m_bVerbose && bShallow)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "will shallow since: " + SHALLOW_DATE);
@ -495,13 +480,16 @@ bool CPluginManager::updateHeaders(bool force) {
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "cmake returned: " + ret);
if (ret.contains("required packages were not found")) {
if (ret.contains("CMake Error at")) {
// missing deps, let the user know.
std::string missing = ret.substr(ret.find("The following required packages were not found:"));
missing = missing.substr(0, missing.find("Call Stack"));
std::string missing = ret.substr(ret.find("CMake Error at"));
missing = ret.substr(ret.find_first_of('\n') + 1);
missing = missing.substr(0, missing.find("-- Configuring incomplete"));
missing = missing.substr(0, missing.find_last_of('\n'));
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not configure the hyprland source, cmake complained:\n" << missing << "\n";
std::cerr << "\n"
<< Colors::RED << "" << Colors::RESET << " Could not configure the hyprland source, cmake complained:\n"
<< missing << "\n\nThis likely means that you are missing the above dependencies or they are out of date.\n";
return false;
}

View File

@ -58,7 +58,8 @@ class CPluginManager {
bool hasDeps();
bool m_bVerbose = false;
bool m_bVerbose = false;
bool m_bNoShallow = false;
// will delete recursively if exists!!
bool createSafeDirectory(const std::string& path);

View File

@ -26,6 +26,7 @@ const std::string HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
--help | -h Show this menu
--verbose | -v Enable too much logging
--force | -f Force an operation ignoring checks (e.g. update -f)
--no-shallow | -s Disable shallow cloning of Hyprland sources
)#";
@ -41,7 +42,7 @@ int main(int argc, char** argv, char** envp) {
}
std::vector<std::string> command;
bool notify = false, verbose = false, force = false;
bool notify = false, verbose = false, force = false, noShallow = false;
for (int i = 1; i < argc; ++i) {
if (ARGS[i].starts_with("-")) {
@ -52,6 +53,8 @@ int main(int argc, char** argv, char** envp) {
notify = true;
} else if (ARGS[i] == "--verbose" || ARGS[i] == "-v") {
verbose = true;
} else if (ARGS[i] == "--no-shallow" || ARGS[i] == "-s") {
noShallow = true;
} else if (ARGS[i] == "--force" || ARGS[i] == "-f") {
force = true;
std::cout << Colors::RED << "!" << Colors::RESET << " Using --force, I hope you know what you are doing.\n";
@ -69,8 +72,9 @@ int main(int argc, char** argv, char** envp) {
return 0;
}
g_pPluginManager = std::make_unique<CPluginManager>();
g_pPluginManager->m_bVerbose = verbose;
g_pPluginManager = std::make_unique<CPluginManager>();
g_pPluginManager->m_bVerbose = verbose;
g_pPluginManager->m_bNoShallow = noShallow;
if (command[0] == "add") {
if (command.size() < 2) {

View File

@ -3,6 +3,7 @@ src = globber.stdout().strip().split('\n')
executable('hyprpm', src,
dependencies: [
dependency('hyprutils', version: '>= 0.1.1'),
dependency('threads'),
dependency('tomlplusplus')
],

View File

@ -13,6 +13,7 @@
git,
hyprcursor,
hyprlang,
hyprutils,
hyprwayland-scanner,
jq,
libGL,
@ -110,6 +111,7 @@ assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been remov
git
hyprcursor.dev
hyprlang
hyprutils
libGL
libdrm
libdatrie

View File

@ -23,6 +23,7 @@ in {
# Dependencies
inputs.hyprcursor.overlays.default
inputs.hyprlang.overlays.default
inputs.hyprutils.overlays.default
inputs.hyprwayland-scanner.overlays.default
self.overlays.xwayland

View File

@ -1,3 +1,3 @@
{
"version": "0.41.0"
"version": "0.41.1"
}

View File

@ -14,7 +14,7 @@
#include <helpers/SdDaemon.hpp> // for SdNotify
#endif
#include <ranges>
#include "helpers/VarList.hpp"
#include "helpers/varlist/VarList.hpp"
#include "protocols/FractionalScale.hpp"
#include "protocols/PointerConstraints.hpp"
#include "protocols/LayerShell.hpp"
@ -24,6 +24,9 @@
#include "desktop/LayerSurface.hpp"
#include "xwayland/XWayland.hpp"
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
#include <sys/types.h>
#include <sys/stat.h>
@ -348,6 +351,8 @@ void CCompositor::cleanup() {
g_pXWaylandManager.reset();
g_pPointerManager.reset();
g_pSeatManager.reset();
g_pHyprCtl.reset();
g_pEventLoopManager.reset();
if (m_sWLRRenderer)
wlr_renderer_destroy(m_sWLRRenderer);
@ -361,6 +366,7 @@ void CCompositor::cleanup() {
if (m_critSigSource)
wl_event_source_remove(m_critSigSource);
wl_event_loop_destroy(m_sWLEventLoop);
wl_display_terminate(m_sWLDisplay);
m_sWLDisplay = nullptr;
@ -1056,7 +1062,7 @@ SP<CWLSurfaceResource> CCompositor::vectorToLayerSurface(const Vector2D& pos, st
if (ls->fadingOut || !ls->layerSurface || (ls->layerSurface && !ls->layerSurface->surface->mapped) || ls->alpha.value() == 0.f)
continue;
auto [surf, local] = ls->layerSurface->surface->at(pos - ls->geometry.pos());
auto [surf, local] = ls->layerSurface->surface->at(pos - ls->geometry.pos(), true);
if (surf) {
if (surf->current.input.empty())
@ -1108,6 +1114,17 @@ bool CCompositor::isWorkspaceVisible(PHLWORKSPACE w) {
return valid(w) && w->m_bVisible;
}
bool CCompositor::isWorkspaceVisibleNotCovered(PHLWORKSPACE w) {
if (!valid(w))
return false;
const auto PMONITOR = getMonitorFromID(w->m_iMonitorID);
if (PMONITOR->activeSpecialWorkspace)
return PMONITOR->activeSpecialWorkspace->m_iID == w->m_iID;
return PMONITOR->activeWorkspace->m_iID == w->m_iID;
}
PHLWORKSPACE CCompositor::getWorkspaceByID(const int& id) {
for (auto& w : m_vWorkspaces) {
if (w->m_iID == id && !w->inert())
@ -1347,6 +1364,10 @@ void CCompositor::addToFadingOutSafe(PHLLS pLS) {
m_vSurfacesFadingOut.emplace_back(pLS);
}
void CCompositor::removeFromFadingOutSafe(PHLLS ls) {
std::erase(m_vSurfacesFadingOut, ls);
}
void CCompositor::addToFadingOutSafe(PHLWINDOW pWindow) {
const auto FOUND = std::find_if(m_vWindowsFadingOut.begin(), m_vWindowsFadingOut.end(), [&](PHLWINDOWREF& other) { return other.lock() == pWindow; });
@ -2084,9 +2105,11 @@ void CCompositor::moveWorkspaceToMonitor(PHLWORKSPACE pWorkspace, CMonitor* pMon
if (POLDMON) {
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(POLDMON->ID);
updateFullscreenFadeOnWorkspace(POLDMON->activeWorkspace);
updateSuspendedStates();
}
updateFullscreenFadeOnWorkspace(pWorkspace);
updateSuspendedStates();
// event
g_pEventManager->postEvent(SHyprIPCEvent{"moveworkspace", pWorkspace->m_szName + "," + pMonitor->szName});

View File

@ -118,6 +118,7 @@ class CCompositor {
PHLWINDOW getWindowFromSurface(SP<CWLSurfaceResource>);
PHLWINDOW getWindowFromHandle(uint32_t);
bool isWorkspaceVisible(PHLWORKSPACE);
bool isWorkspaceVisibleNotCovered(PHLWORKSPACE);
PHLWORKSPACE getWorkspaceByID(const int&);
PHLWORKSPACE getWorkspaceByName(const std::string&);
PHLWORKSPACE getWorkspaceByString(const std::string&);
@ -155,6 +156,7 @@ class CCompositor {
PHLWINDOW getX11Parent(PHLWINDOW);
void scheduleFrameForMonitor(CMonitor*);
void addToFadingOutSafe(PHLLS);
void removeFromFadingOutSafe(PHLLS);
void addToFadingOutSafe(PHLWINDOW);
PHLWINDOW getWindowByRegex(const std::string&);
void warpCursorTo(const Vector2D&, bool force = false);

View File

@ -1,6 +1,6 @@
#pragma once
#include "../defines.hpp"
#include "../helpers/VarList.hpp"
#include "../helpers/varlist/VarList.hpp"
#include <vector>
enum eConfigValueDataTypes {

View File

@ -3,9 +3,10 @@
#include "../render/decorations/CHyprGroupBarDecoration.hpp"
#include "config/ConfigDataValues.hpp"
#include "helpers/VarList.hpp"
#include "helpers/varlist/VarList.hpp"
#include "../protocols/LayerShell.hpp"
#include <cstddef>
#include <cstdint>
#include <string.h>
#include <string>
@ -13,6 +14,7 @@
#include <sys/types.h>
#include <unistd.h>
#include <glob.h>
#include <xkbcommon/xkbcommon.h>
#include <algorithm>
#include <fstream>
@ -20,7 +22,8 @@
#include <sstream>
#include <ranges>
#include <unordered_set>
#include <xkbcommon/xkbcommon.h>
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
extern "C" char** environ;
@ -428,8 +431,9 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("master:special_scale_factor", {1.f});
m_pConfig->addConfigValue("master:mfact", {0.55f});
m_pConfig->addConfigValue("master:new_is_master", Hyprlang::INT{1});
m_pConfig->addConfigValue("master:new_status", {"slave"});
m_pConfig->addConfigValue("master:always_center_master", Hyprlang::INT{0});
m_pConfig->addConfigValue("master:new_on_active", {"none"});
m_pConfig->addConfigValue("master:new_on_top", Hyprlang::INT{0});
m_pConfig->addConfigValue("master:no_gaps_when_only", Hyprlang::INT{0});
m_pConfig->addConfigValue("master:orientation", {"left"});
@ -519,10 +523,13 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("opengl:force_introspection", Hyprlang::INT{2});
m_pConfig->addConfigValue("cursor:no_hardware_cursors", Hyprlang::INT{0});
m_pConfig->addConfigValue("cursor:no_break_fs_vrr", Hyprlang::INT{0});
m_pConfig->addConfigValue("cursor:min_refresh_rate", Hyprlang::INT{24});
m_pConfig->addConfigValue("cursor:hotspot_padding", Hyprlang::INT{0});
m_pConfig->addConfigValue("cursor:inactive_timeout", Hyprlang::INT{0});
m_pConfig->addConfigValue("cursor:no_warps", Hyprlang::INT{0});
m_pConfig->addConfigValue("cursor:persistent_warps", Hyprlang::INT{0});
m_pConfig->addConfigValue("cursor:warp_on_change_workspace", Hyprlang::INT{0});
m_pConfig->addConfigValue("cursor:default_monitor", {STRVAL_EMPTY});
m_pConfig->addConfigValue("cursor:zoom_factor", {1.f});
m_pConfig->addConfigValue("cursor:zoom_rigid", Hyprlang::INT{0});
@ -1955,15 +1962,16 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
// bind[fl]=SUPER,G,exec,dmenu_run <args>
// flags
bool locked = false;
bool release = false;
bool repeat = false;
bool mouse = false;
bool nonConsuming = false;
bool transparent = false;
bool ignoreMods = false;
bool multiKey = false;
const auto BINDARGS = command.substr(4);
bool locked = false;
bool release = false;
bool repeat = false;
bool mouse = false;
bool nonConsuming = false;
bool transparent = false;
bool ignoreMods = false;
bool multiKey = false;
bool hasDescription = false;
const auto BINDARGS = command.substr(4);
for (auto& arg : BINDARGS) {
if (arg == 'l') {
@ -1982,6 +1990,8 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
ignoreMods = true;
} else if (arg == 's') {
multiKey = true;
} else if (arg == 'd') {
hasDescription = true;
} else {
return "bind: invalid flag";
}
@ -1993,11 +2003,13 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
if (mouse && (repeat || release || locked))
return "flag m is exclusive";
const auto ARGS = CVarList(value, 4);
const int numbArgs = hasDescription ? 5 : 4;
const auto ARGS = CVarList(value, numbArgs);
const int DESCR_OFFSET = hasDescription ? 1 : 0;
if ((ARGS.size() < 3 && !mouse) || (ARGS.size() < 3 && mouse))
return "bind: too few args";
else if ((ARGS.size() > 4 && !mouse) || (ARGS.size() > 3 && mouse))
else if ((ARGS.size() > (size_t)4 + DESCR_OFFSET && !mouse) || (ARGS.size() > (size_t)3 + DESCR_OFFSET && mouse))
return "bind: too many args";
std::set<xkb_keysym_t> KEYSYMS;
@ -2016,9 +2028,11 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
const auto KEY = multiKey ? "" : ARGS[1];
auto HANDLER = ARGS[2];
const auto DESCRIPTION = hasDescription ? ARGS[2] : "";
const auto COMMAND = mouse ? HANDLER : ARGS[3];
auto HANDLER = ARGS[2 + DESCR_OFFSET];
const auto COMMAND = mouse ? HANDLER : ARGS[3 + DESCR_OFFSET];
if (mouse)
HANDLER = "mouse";
@ -2046,8 +2060,8 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
return "Invalid catchall, catchall keybinds are only allowed in submaps.";
}
g_pKeybindManager->addKeybind(SKeybind{parsedKey.key, KEYSYMS, parsedKey.keycode, parsedKey.catchAll, MOD, MODS, HANDLER, COMMAND, locked, m_szCurrentSubmap, release,
repeat, mouse, nonConsuming, transparent, ignoreMods, multiKey});
g_pKeybindManager->addKeybind(SKeybind{parsedKey.key, KEYSYMS, parsedKey.keycode, parsedKey.catchAll, MOD, MODS, HANDLER, COMMAND, locked, m_szCurrentSubmap, DESCRIPTION,
release, repeat, mouse, nonConsuming, transparent, ignoreMods, multiKey, hasDescription});
}
return {};
@ -2087,8 +2101,8 @@ bool layerRuleValid(const std::string& RULE) {
}
std::optional<std::string> CConfigManager::handleWindowRule(const std::string& command, const std::string& value) {
const auto RULE = removeBeginEndSpacesTabs(value.substr(0, value.find_first_of(',')));
const auto VALUE = removeBeginEndSpacesTabs(value.substr(value.find_first_of(',') + 1));
const auto RULE = trim(value.substr(0, value.find_first_of(',')));
const auto VALUE = trim(value.substr(value.find_first_of(',') + 1));
// check rule and value
if (RULE.empty() || VALUE.empty())
@ -2114,8 +2128,8 @@ std::optional<std::string> CConfigManager::handleWindowRule(const std::string& c
}
std::optional<std::string> CConfigManager::handleLayerRule(const std::string& command, const std::string& value) {
const auto RULE = removeBeginEndSpacesTabs(value.substr(0, value.find_first_of(',')));
const auto VALUE = removeBeginEndSpacesTabs(value.substr(value.find_first_of(',') + 1));
const auto RULE = trim(value.substr(0, value.find_first_of(',')));
const auto VALUE = trim(value.substr(value.find_first_of(',') + 1));
// check rule and value
if (RULE.empty() || VALUE.empty())
@ -2142,7 +2156,7 @@ std::optional<std::string> CConfigManager::handleLayerRule(const std::string& co
}
std::optional<std::string> CConfigManager::handleWindowRuleV2(const std::string& command, const std::string& value) {
const auto RULE = removeBeginEndSpacesTabs(value.substr(0, value.find_first_of(',')));
const auto RULE = trim(value.substr(0, value.find_first_of(',')));
const auto VALUE = value.substr(value.find_first_of(',') + 1);
if (!windowRuleValid(RULE) && RULE != "unset") {
@ -2219,7 +2233,7 @@ std::optional<std::string> CConfigManager::handleWindowRuleV2(const std::string&
result = result.substr(0, min - pos);
result = removeBeginEndSpacesTabs(result);
result = trim(result);
if (!result.empty() && result.back() == ',')
result.pop_back();
@ -2341,7 +2355,7 @@ void CConfigManager::updateBlurredLS(const std::string& name, const bool forceBl
std::optional<std::string> CConfigManager::handleBlurLS(const std::string& command, const std::string& value) {
if (value.starts_with("remove,")) {
const auto TOREMOVE = removeBeginEndSpacesTabs(value.substr(7));
const auto TOREMOVE = trim(value.substr(7));
if (std::erase_if(m_dBlurLSNamespaces, [&](const auto& other) { return other == TOREMOVE; }))
updateBlurredLS(TOREMOVE, false);
return {};
@ -2358,7 +2372,7 @@ std::optional<std::string> CConfigManager::handleWorkspaceRules(const std::strin
const auto FIRST_DELIM = value.find_first_of(',');
std::string name = "";
auto first_ident = removeBeginEndSpacesTabs(value.substr(0, FIRST_DELIM));
auto first_ident = trim(value.substr(0, FIRST_DELIM));
int id = getWorkspaceIDFromString(first_ident, name);
auto rules = value.substr(FIRST_DELIM + 1);

View File

@ -15,7 +15,7 @@
#include <xf86drmMode.h>
#include "../helpers/WLClasses.hpp"
#include "../helpers/Monitor.hpp"
#include "../helpers/VarList.hpp"
#include "../helpers/varlist/VarList.hpp"
#include "../desktop/Window.hpp"
#include "../desktop/LayerSurface.hpp"

View File

@ -138,7 +138,7 @@ dwindle {
# See https://wiki.hyprland.org/Configuring/Master-Layout/ for more
master {
new_is_master = true
new_status = master
}
# https://wiki.hyprland.org/Configuring/Variables/#misc

View File

@ -1,5 +1,6 @@
#include "HyprCtl.hpp"
#include <format>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
@ -16,6 +17,9 @@
#include <typeindex>
#include <numeric>
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
#include "../config/ConfigDataValues.hpp"
#include "../config/ConfigValue.hpp"
#include "../managers/CursorManager.hpp"
@ -24,7 +28,9 @@
#include "../devices/IKeyboard.hpp"
#include "../devices/ITouch.hpp"
#include "../devices/Tablet.hpp"
#include "debug/RollingLogFollow.hpp"
#include "config/ConfigManager.hpp"
#include "helpers/MiscFunctions.hpp"
static void trimTrailingComma(std::string& str) {
if (!str.empty() && str.back() == ',')
@ -789,9 +795,11 @@ std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) {
ret += "e";
if (kb.nonConsuming)
ret += "n";
if (kb.hasDescription)
ret += "d";
ret += std::format("\n\tmodmask: {}\n\tsubmap: {}\n\tkey: {}\n\tkeycode: {}\n\tcatchall: {}\n\tdispatcher: {}\n\targ: {}\n\n", kb.modmask, kb.submap, kb.key,
kb.keycode, kb.catchAll, kb.handler, kb.arg);
ret += std::format("\n\tmodmask: {}\n\tsubmap: {}\n\tkey: {}\n\tkeycode: {}\n\tcatchall: {}\n\tdescription: {}\n\tdispatcher: {}\n\targ: {}\n\n", kb.modmask, kb.submap,
kb.key, kb.keycode, kb.catchAll, kb.description, kb.handler, kb.arg);
}
} else {
// json
@ -805,17 +813,19 @@ std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) {
"release": {},
"repeat": {},
"non_consuming": {},
"has_description": {},
"modmask": {},
"submap": "{}",
"key": "{}",
"keycode": {},
"catch_all": {},
"description": "{}",
"dispatcher": "{}",
"arg": "{}"
}},)#",
kb.locked ? "true" : "false", kb.mouse ? "true" : "false", kb.release ? "true" : "false", kb.repeat ? "true" : "false", kb.nonConsuming ? "true" : "false",
kb.modmask, escapeJSONStrings(kb.submap), escapeJSONStrings(kb.key), kb.keycode, kb.catchAll ? "true" : "false", escapeJSONStrings(kb.handler),
escapeJSONStrings(kb.arg));
kb.hasDescription ? "true" : "false", kb.modmask, escapeJSONStrings(kb.submap), escapeJSONStrings(kb.key), kb.keycode, kb.catchAll ? "true" : "false",
escapeJSONStrings(kb.description), escapeJSONStrings(kb.handler), escapeJSONStrings(kb.arg));
}
trimTrailingComma(ret);
ret += "]";
@ -826,7 +836,7 @@ std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
auto commitMsg = removeBeginEndSpacesTabs(GIT_COMMIT_MESSAGE);
auto commitMsg = trim(GIT_COMMIT_MESSAGE);
std::replace(commitMsg.begin(), commitMsg.end(), '#', ' ');
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
@ -1051,7 +1061,7 @@ std::string dispatchBatch(eHyprCtlOutputFormat format, std::string request) {
request = "";
}
curitem = removeBeginEndSpacesTabs(curitem);
curitem = trim(curitem);
};
nextItem();
@ -1305,7 +1315,7 @@ std::string dispatchGetOption(eHyprCtlOutputFormat format, std::string request)
request = "";
}
curitem = removeBeginEndSpacesTabs(curitem);
curitem = trim(curitem);
};
nextItem();
@ -1723,6 +1733,46 @@ std::string CHyprCtl::makeDynamicCall(const std::string& input) {
return getReply(input);
}
bool successWrite(int fd, const std::string& data, bool needLog = true) {
if (write(fd, data.c_str(), data.length()) > 0)
return true;
if (errno == EAGAIN)
return true;
if (needLog)
Debug::log(ERR, "Couldn't write to socket. Error: " + std::string(strerror(errno)));
return false;
}
void runWritingDebugLogThread(const int conn) {
using namespace std::chrono_literals;
Debug::log(LOG, "In followlog thread, got connection, start writing: {}", conn);
//will be finished, when reading side close connection
std::thread([conn]() {
while (Debug::RollingLogFollow::Get().IsRunning()) {
if (Debug::RollingLogFollow::Get().isEmpty(conn)) {
std::this_thread::sleep_for(1000ms);
continue;
}
auto line = Debug::RollingLogFollow::Get().GetLog(conn);
if (!successWrite(conn, line))
// We cannot write, when connection is closed. So thread will successfully exit by itself
break;
std::this_thread::sleep_for(100ms);
}
close(conn);
Debug::RollingLogFollow::Get().StopFor(conn);
}).detach();
}
bool isFollowUpRollingLogRequest(const std::string& request) {
return request.contains("rollinglog") && request.contains("f");
}
int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
if (mask & WL_EVENT_ERROR || mask & WL_EVENT_HANGUP)
return 0;
@ -1766,9 +1816,15 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
reply = "Err: " + std::string(e.what());
}
write(ACCEPTEDCONNECTION, reply.c_str(), reply.length());
successWrite(ACCEPTEDCONNECTION, reply);
close(ACCEPTEDCONNECTION);
if (isFollowUpRollingLogRequest(request)) {
Debug::log(LOG, "Followup rollinglog request received. Starting thread to write to socket.");
Debug::RollingLogFollow::Get().StartFor(ACCEPTEDCONNECTION);
runWritingDebugLogThread(ACCEPTEDCONNECTION);
Debug::log(LOG, Debug::RollingLogFollow::Get().DebugInfo());
} else
close(ACCEPTEDCONNECTION);
if (g_pConfigManager->m_bWantsMonitorReload)
g_pConfigManager->ensureMonitorStatus();

View File

@ -1,6 +1,7 @@
#include "Log.hpp"
#include "../defines.hpp"
#include "../Compositor.hpp"
#include "RollingLogFollow.hpp"
#include <fstream>
#include <iostream>
@ -73,6 +74,9 @@ void Debug::log(LogLevel level, std::string str) {
if (rollingLog.size() > ROLLING_LOG_SIZE)
rollingLog = rollingLog.substr(rollingLog.size() - ROLLING_LOG_SIZE);
if (RollingLogFollow::Get().IsRunning())
RollingLogFollow::Get().AddLog(str);
if (!disableLogs || !**disableLogs) {
// log to a file
std::ofstream ofs;

View File

@ -0,0 +1,65 @@
#pragma once
#include <shared_mutex>
namespace Debug {
struct RollingLogFollow {
std::unordered_map<int, std::string> socketToRollingLogFollowQueue;
std::shared_mutex m;
bool running = false;
static constexpr size_t ROLLING_LOG_FOLLOW_TOO_BIG = 8192;
// Returns true if the queue is empty for the given socket
bool isEmpty(int socket) {
std::shared_lock<std::shared_mutex> r(m);
return socketToRollingLogFollowQueue[socket].empty();
}
std::string DebugInfo() {
std::shared_lock<std::shared_mutex> r(m);
return std::format("RollingLogFollow, got {} connections", socketToRollingLogFollowQueue.size());
}
std::string GetLog(int socket) {
std::unique_lock<std::shared_mutex> w(m);
const std::string ret = socketToRollingLogFollowQueue[socket];
socketToRollingLogFollowQueue[socket] = "";
return ret;
};
void AddLog(std::string log) {
std::unique_lock<std::shared_mutex> w(m);
running = true;
std::vector<int> to_erase;
for (const auto& p : socketToRollingLogFollowQueue)
socketToRollingLogFollowQueue[p.first] += log + "\n";
}
bool IsRunning() {
std::shared_lock<std::shared_mutex> r(m);
return running;
}
void StopFor(int socket) {
std::unique_lock<std::shared_mutex> w(m);
socketToRollingLogFollowQueue.erase(socket);
if (socketToRollingLogFollowQueue.empty())
running = false;
}
void StartFor(int socket) {
std::unique_lock<std::shared_mutex> w(m);
socketToRollingLogFollowQueue[socket] = std::format("[LOG] Following log to socket: {} started\n", socket);
running = true;
}
static RollingLogFollow& Get() {
static RollingLogFollow instance;
static std::mutex gm;
std::lock_guard<std::mutex> lock(gm);
return instance;
};
};
}

View File

@ -116,8 +116,12 @@ void CLayerSurface::onDestroy() {
void CLayerSurface::onMap() {
Debug::log(LOG, "LayerSurface {:x} mapped", (uintptr_t)layerSurface);
mapped = true;
keyboardExclusive = layerSurface->current.interactivity;
mapped = true;
interactivity = layerSurface->current.interactivity;
// this layer might be re-mapped.
fadingOut = false;
g_pCompositor->removeFromFadingOutSafe(self.lock());
// fix if it changed its mon
const auto PMONITOR = g_pCompositor->getMonitorFromID(monitorID);
@ -133,12 +137,15 @@ void CLayerSurface::onMap() {
surface->resource()->enter(PMONITOR->self.lock());
if (layerSurface->current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE)
const bool ISEXCLUSIVE = layerSurface->current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
if (ISEXCLUSIVE)
g_pInputManager->m_dExclusiveLSes.push_back(self);
const bool GRABSFOCUS = layerSurface->current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE &&
// don't focus if constrained
(g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained());
const bool GRABSFOCUS = ISEXCLUSIVE ||
(layerSurface->current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE &&
// don't focus if constrained
(g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained()));
if (GRABSFOCUS) {
// TODO: use the new superb really very cool grab
@ -177,9 +184,6 @@ void CLayerSurface::onUnmap() {
std::erase_if(g_pInputManager->m_dExclusiveLSes, [this](const auto& other) { return !other.lock() || other.lock() == self.lock(); });
if (!g_pInputManager->m_dExclusiveLSes.empty())
g_pCompositor->focusSurface(g_pInputManager->m_dExclusiveLSes[0]->surface->resource());
if (!g_pCompositor->getMonitorFromID(monitorID) || g_pCompositor->m_bUnsafeState) {
Debug::log(WARN, "Layersurface unmapping on invalid monitor (removed?) ignoring.");
@ -204,39 +208,12 @@ void CLayerSurface::onUnmap() {
const bool WASLASTFOCUS = g_pCompositor->m_pLastFocus == surface->resource();
surface.reset();
if (!PMONITOR)
return;
// refocus if needed
if (WASLASTFOCUS) {
g_pInputManager->releaseAllMouseButtons();
Vector2D surfaceCoords;
PHLLS pFoundLayerSurface;
SP<CWLSurfaceResource> foundSurface = nullptr;
g_pCompositor->m_pLastFocus.reset();
// find LS-es to focus
foundSurface = g_pCompositor->vectorToLayerSurface(g_pInputManager->getMouseCoordsInternal(), &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
&surfaceCoords, &pFoundLayerSurface);
if (!foundSurface)
foundSurface = g_pCompositor->vectorToLayerSurface(g_pInputManager->getMouseCoordsInternal(), &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
&surfaceCoords, &pFoundLayerSurface);
if (!foundSurface && g_pCompositor->m_pLastWindow.lock() && g_pCompositor->isWorkspaceVisible(g_pCompositor->m_pLastWindow->m_pWorkspace)) {
// if there isn't any, focus the last window
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
g_pCompositor->focusWindow(nullptr);
g_pCompositor->focusWindow(PLASTWINDOW);
} else {
// otherwise, full refocus
g_pInputManager->refocus();
}
}
if (WASLASTFOCUS)
g_pInputManager->refocusLastWindow(PMONITOR);
CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height};
g_pHyprRenderer->damageBox(&geomFixed);
@ -246,12 +223,25 @@ void CLayerSurface::onUnmap() {
g_pHyprRenderer->damageBox(&geomFixed);
g_pInputManager->sendMotionEventsToFocused();
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->ID);
}
void CLayerSurface::onCommit() {
if (!layerSurface)
return;
if (!mapped) {
// we're re-mapping if this is the case
if (layerSurface->surface && !layerSurface->surface->current.buffer) {
fadingOut = false;
geometry = {};
g_pHyprRenderer->arrangeLayersForMonitor(monitorID);
}
return;
}
const auto PMONITOR = g_pCompositor->getMonitorFromID(monitorID);
if (!PMONITOR)
@ -311,18 +301,35 @@ void CLayerSurface::onCommit() {
realSize.setValueAndWarp(geometry.size());
}
if (layerSurface->current.interactivity && (g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained()) // don't focus if constrained
&& !keyboardExclusive && mapped) {
g_pCompositor->focusSurface(surface->resource());
if (mapped) {
const bool WASLASTFOCUS = g_pCompositor->m_pLastFocus == surface->resource();
const bool WASEXCLUSIVE = interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
const bool ISEXCLUSIVE = layerSurface->current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y);
g_pSeatManager->setPointerFocus(surface->resource(), LOCAL);
g_pInputManager->m_bEmptyFocusCursorSet = false;
} else if (!layerSurface->current.interactivity && (g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained()) && keyboardExclusive) {
g_pInputManager->refocus();
if (!WASEXCLUSIVE && ISEXCLUSIVE)
g_pInputManager->m_dExclusiveLSes.push_back(self);
else if (WASEXCLUSIVE && !ISEXCLUSIVE)
std::erase_if(g_pInputManager->m_dExclusiveLSes, [this](const auto& other) { return !other.lock() || other.lock() == self.lock(); });
// if the surface was focused and interactive but now isn't, refocus
if (WASLASTFOCUS && !layerSurface->current.interactivity) {
// moveMouseUnified won't focus non interactive layers but it won't unfocus them either,
// so unfocus the surface here.
g_pCompositor->focusSurface(nullptr);
g_pInputManager->refocusLastWindow(g_pCompositor->getMonitorFromID(monitorID));
} else if (!WASEXCLUSIVE && ISEXCLUSIVE) {
// if now exclusive and not previously
g_pSeatManager->setGrab(nullptr);
g_pInputManager->releaseAllMouseButtons();
g_pCompositor->focusSurface(surface->resource());
const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y);
g_pSeatManager->setPointerFocus(surface->resource(), LOCAL);
g_pInputManager->m_bEmptyFocusCursorSet = false;
}
}
keyboardExclusive = layerSurface->current.interactivity;
interactivity = layerSurface->current.interactivity;
g_pHyprRenderer->damageSurface(surface->resource(), position.x, position.y);
@ -512,4 +519,4 @@ int CLayerSurface::popupsCount() {
int no = -1; // we have one dummy
popupHead->breadthfirst([](CPopup* p, void* data) { *(int*)data += 1; }, &no);
return no;
}
}

View File

@ -34,40 +34,41 @@ class CLayerSurface {
WP<CLayerShellResource> layerSurface;
wl_list link;
bool keyboardExclusive = false;
// the header providing the enum type cannot be imported here
int interactivity = 0;
SP<CWLSurface> surface;
SP<CWLSurface> surface;
bool mapped = false;
uint32_t layer = 0;
bool mapped = false;
uint32_t layer = 0;
int monitorID = -1;
int monitorID = -1;
bool fadingOut = false;
bool readyToDelete = false;
bool noProcess = false;
bool noAnimations = false;
bool fadingOut = false;
bool readyToDelete = false;
bool noProcess = false;
bool noAnimations = false;
bool forceBlur = false;
bool forceBlurPopups = false;
int xray = -1;
bool ignoreAlpha = false;
float ignoreAlphaValue = 0.f;
bool dimAround = false;
bool forceBlur = false;
bool forceBlurPopups = false;
int xray = -1;
bool ignoreAlpha = false;
float ignoreAlphaValue = 0.f;
bool dimAround = false;
std::optional<std::string> animationStyle;
std::optional<std::string> animationStyle;
PHLLSREF self;
PHLLSREF self;
CBox geometry = {0, 0, 0, 0};
Vector2D position;
std::string szNamespace = "";
std::unique_ptr<CPopup> popupHead;
CBox geometry = {0, 0, 0, 0};
Vector2D position;
std::string szNamespace = "";
std::unique_ptr<CPopup> popupHead;
void onDestroy();
void onMap();
void onUnmap();
void onCommit();
void onDestroy();
void onMap();
void onUnmap();
void onCommit();
private:
struct {
@ -83,4 +84,4 @@ class CLayerSurface {
bool operator==(const CLayerSurface& rhs) const {
return layerSurface == rhs.layerSurface && monitorID == rhs.monitorID;
}
};
};

View File

@ -3,7 +3,7 @@
#include <vector>
#include <memory>
#include "Subsurface.hpp"
#include "../helpers/signal/Listener.hpp"
#include "../helpers/signal/Signal.hpp"
class CXDGPopupResource;

View File

@ -12,6 +12,9 @@
#include "../protocols/core/Compositor.hpp"
#include "../xwayland/XWayland.hpp"
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
PHLWINDOW CWindow::create(SP<CXWaylandSurface> surface) {
PHLWINDOW pWindow = SP<CWindow>(new CWindow(surface));
@ -687,7 +690,7 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
CGradientValueData activeBorderGradient = {};
CGradientValueData inactiveBorderGradient = {};
bool active = true;
CVarList colorsAndAngles = CVarList(removeBeginEndSpacesTabs(r.szRule.substr(r.szRule.find_first_of(' ') + 1)), 0, 's', true);
CVarList colorsAndAngles = CVarList(trim(r.szRule.substr(r.szRule.find_first_of(' ') + 1)), 0, 's', true);
// Basic form has only two colors, everything else can be parsed as a gradient
if (colorsAndAngles.size() == 2 && !colorsAndAngles[1].contains("deg")) {
@ -1537,3 +1540,58 @@ void CWindow::warpCursor() {
else
g_pCompositor->warpCursorTo(middle());
}
PHLWINDOW CWindow::getSwallower() {
static auto PSWALLOWREGEX = CConfigValue<std::string>("misc:swallow_regex");
static auto PSWALLOWEXREGEX = CConfigValue<std::string>("misc:swallow_exception_regex");
static auto PSWALLOW = CConfigValue<Hyprlang::INT>("misc:enable_swallow");
if (!*PSWALLOW || (*PSWALLOWREGEX).empty())
return nullptr;
// check parent
std::vector<PHLWINDOW> candidates;
pid_t currentPid = getPID();
// walk up the tree until we find someone, 25 iterations max.
for (size_t i = 0; i < 25; ++i) {
currentPid = getPPIDof(currentPid);
if (!currentPid)
break;
for (auto& w : g_pCompositor->m_vWindows) {
if (!w->m_bIsMapped || w->isHidden())
continue;
if (w->getPID() == currentPid)
candidates.push_back(w);
}
}
if (!(*PSWALLOWREGEX).empty())
std::erase_if(candidates, [&](const auto& other) { return !std::regex_match(other->m_szClass, std::regex(*PSWALLOWREGEX)); });
if (candidates.size() <= 0)
return nullptr;
if (!(*PSWALLOWEXREGEX).empty())
std::erase_if(candidates, [&](const auto& other) { return std::regex_match(other->m_szTitle, std::regex(*PSWALLOWEXREGEX)); });
if (candidates.size() <= 0)
return nullptr;
if (candidates.size() == 1)
return candidates.at(0);
// walk up the focus history and find the last focused
for (auto& w : g_pCompositor->m_vWindowFocusHistory) {
if (!w)
continue;
if (std::find(candidates.begin(), candidates.end(), w.lock()) != candidates.end())
return w.lock();
}
// if none are found (??) then just return the first one
return candidates.at(0);
}

View File

@ -450,6 +450,7 @@ class CWindow {
std::string fetchTitle();
std::string fetchClass();
void warpCursor();
PHLWINDOW getSwallower();
// listeners
void onAck(uint32_t serial);

View File

@ -2,6 +2,9 @@
#include "../Compositor.hpp"
#include "../config/ConfigValue.hpp"
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
PHLWORKSPACE CWorkspace::create(int id, int monitorID, std::string name, bool special, bool isEmtpy) {
PHLWORKSPACE workspace = makeShared<CWorkspace>(id, monitorID, name, special, isEmtpy);
workspace->init(workspace);
@ -219,7 +222,7 @@ std::string CWorkspace::getConfigName() {
}
bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
auto selector = removeBeginEndSpacesTabs(selector_);
auto selector = trim(selector_);
if (selector.empty())
return true;

View File

@ -1,6 +1,6 @@
#include "IKeyboard.hpp"
#include "../defines.hpp"
#include "../helpers/VarList.hpp"
#include "../helpers/varlist/VarList.hpp"
#include "../managers/input/InputManager.hpp"
uint32_t IKeyboard::getCapabilities() {

View File

@ -188,6 +188,9 @@ void Events::listener_monitorDestroy(void* owner, void* data) {
Debug::log(LOG, "Destroy called for monitor {}", pMonitor->output->name);
if (pMonitor->output->idle_frame)
wl_event_source_remove(pMonitor->output->idle_frame);
pMonitor->onDisconnect(true);
pMonitor->output = nullptr;

View File

@ -12,6 +12,9 @@
#include "../protocols/core/Compositor.hpp"
#include "../xwayland/XSurface.hpp"
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
// ------------------------------------------------------------ //
// __ _______ _ _ _____ ______ _______ //
// \ \ / /_ _| \ | | __ \ / __ \ \ / / ____| //
@ -41,7 +44,6 @@ void Events::listener_mapWindow(void* owner, void* data) {
static auto PDIMSTRENGTH = CConfigValue<Hyprlang::FLOAT>("decoration:dim_strength");
static auto PSWALLOW = CConfigValue<Hyprlang::INT>("misc:enable_swallow");
static auto PSWALLOWREGEX = CConfigValue<std::string>("misc:swallow_regex");
static auto PSWALLOWEXREGEX = CConfigValue<std::string>("misc:swallow_exception_regex");
static auto PNEWTAKESOVERFS = CConfigValue<Hyprlang::INT>("misc:new_window_takes_over_fullscreen");
static auto PINITIALWSTRACKING = CConfigValue<Hyprlang::INT>("misc:initial_workspace_tracking");
@ -140,7 +142,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
for (auto& r : PWINDOW->m_vMatchedRules) {
if (r.szRule.starts_with("monitor")) {
try {
const auto MONITORSTR = removeBeginEndSpacesTabs(r.szRule.substr(r.szRule.find(' ')));
const auto MONITORSTR = trim(r.szRule.substr(r.szRule.find(' ')));
if (MONITORSTR == "unset") {
PWINDOW->m_iMonitorID = PMONITOR->ID;
@ -235,7 +237,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
continue;
// `group` is a shorthand of `group set`
if (removeBeginEndSpacesTabs(r.szRule) == "group") {
if (trim(r.szRule) == "group") {
PWINDOW->m_eGroupRules |= GROUP_SET;
continue;
}
@ -524,71 +526,17 @@ void Events::listener_mapWindow(void* owner, void* data) {
// verify swallowing
if (*PSWALLOW && std::string{*PSWALLOWREGEX} != STRVAL_EMPTY) {
// don't swallow ourselves
std::regex rgx(*PSWALLOWREGEX);
if (!std::regex_match(PWINDOW->m_szClass, rgx)) {
// check parent
int ppid = getPPIDof(PWINDOW->getPID());
const auto SWALLOWER = PWINDOW->getSwallower();
int curppid = 0;
if (SWALLOWER) {
// swallow
PWINDOW->m_pSwallowed = SWALLOWER;
for (int i = 0; i < 5; ++i) {
curppid = getPPIDof(ppid);
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(SWALLOWER);
if (curppid < 10) {
break;
}
SWALLOWER->setHidden(true);
ppid = curppid;
}
if (ppid) {
// get window by pid
std::vector<PHLWINDOW> found;
PHLWINDOW finalFound;
for (auto& w : g_pCompositor->m_vWindows) {
if (!w->m_bIsMapped || w->isHidden())
continue;
if (w->getPID() == ppid) {
found.push_back(w);
}
}
if (found.size() > 1) {
for (auto& w : found) {
// try get the focus, otherwise we'll ignore to avoid swallowing incorrect windows
if (w == PFOCUSEDWINDOWPREV) {
finalFound = w;
break;
}
}
} else if (found.size() == 1) {
finalFound = found[0];
}
if (finalFound) {
bool valid = std::regex_match(PWINDOW->m_szClass, rgx);
if (std::string{*PSWALLOWEXREGEX} != STRVAL_EMPTY) {
std::regex exc(*PSWALLOWEXREGEX);
valid = valid && !std::regex_match(PWINDOW->m_szTitle, exc);
}
// check if it's the window we want & not exempt from getting swallowed
if (valid) {
// swallow
PWINDOW->m_pSwallowed = finalFound;
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(finalFound);
finalFound->setHidden(true);
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->m_iMonitorID);
}
}
}
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->m_iMonitorID);
}
}
@ -800,7 +748,7 @@ void Events::listener_commitWindow(void* owner, void* data) {
// tearing: if solitary, redraw it. This still might be a single surface window
const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
if (PMONITOR && PMONITOR->solitaryClient.lock() == PWINDOW && PWINDOW->canBeTorn() && PMONITOR->tearingState.canTear && PWINDOW->m_pWLSurface->resource()->current.buffer) {
CRegion damageBox{PWINDOW->m_pWLSurface->resource()->current.bufferDamage};
CRegion damageBox{PWINDOW->m_pWLSurface->resource()->accumulateCurrentBufferDamage()};
if (!damageBox.empty()) {
if (PMONITOR->tearingState.busy) {

View File

@ -11,18 +11,26 @@
*/
inline const std::vector<SPixelFormat> GLES3_FORMATS = {
{
.drmFormat = DRM_FORMAT_ARGB8888,
.flipRB = true,
.glFormat = GL_RGBA,
.drmFormat = DRM_FORMAT_ARGB8888,
.flipRB = true,
#ifndef GLES2
.glFormat = GL_RGBA,
#else
.glFormat = GL_BGRA_EXT,
#endif
.glType = GL_UNSIGNED_BYTE,
.withAlpha = true,
.alphaStripped = DRM_FORMAT_XRGB8888,
.bytesPerBlock = 4,
},
{
.drmFormat = DRM_FORMAT_XRGB8888,
.flipRB = true,
.glFormat = GL_RGBA,
.drmFormat = DRM_FORMAT_XRGB8888,
.flipRB = true,
#ifndef GLES2
.glFormat = GL_RGBA,
#else
.glFormat = GL_BGRA_EXT,
#endif
.glType = GL_UNSIGNED_BYTE,
.withAlpha = false,
.alphaStripped = DRM_FORMAT_XRGB8888,
@ -93,70 +101,100 @@ inline const std::vector<SPixelFormat> GLES3_FORMATS = {
.bytesPerBlock = 2,
},
{
.drmFormat = DRM_FORMAT_XBGR2101010,
.glFormat = GL_RGBA,
.glType = GL_UNSIGNED_INT_2_10_10_10_REV,
.drmFormat = DRM_FORMAT_XBGR2101010,
.glFormat = GL_RGBA,
#ifndef GLES2
.glType = GL_UNSIGNED_INT_2_10_10_10_REV,
#else
.glType = GL_UNSIGNED_INT_2_10_10_10_REV_EXT,
#endif
.withAlpha = false,
.alphaStripped = DRM_FORMAT_XBGR2101010,
.bytesPerBlock = 4,
},
{
.drmFormat = DRM_FORMAT_ABGR2101010,
.glFormat = GL_RGBA,
.glType = GL_UNSIGNED_INT_2_10_10_10_REV,
.drmFormat = DRM_FORMAT_ABGR2101010,
.glFormat = GL_RGBA,
#ifndef GLES2
.glType = GL_UNSIGNED_INT_2_10_10_10_REV,
#else
.glType = GL_UNSIGNED_INT_2_10_10_10_REV_EXT,
#endif
.withAlpha = true,
.alphaStripped = DRM_FORMAT_XBGR2101010,
.bytesPerBlock = 4,
},
{
.drmFormat = DRM_FORMAT_XRGB2101010,
.glFormat = GL_RGBA,
.glType = GL_UNSIGNED_INT_2_10_10_10_REV,
.drmFormat = DRM_FORMAT_XRGB2101010,
.glFormat = GL_RGBA,
#ifndef GLES2
.glType = GL_UNSIGNED_INT_2_10_10_10_REV,
#else
.glType = GL_UNSIGNED_INT_2_10_10_10_REV_EXT,
#endif
.withAlpha = false,
.alphaStripped = DRM_FORMAT_XRGB2101010,
.bytesPerBlock = 4,
},
{
.drmFormat = DRM_FORMAT_ARGB2101010,
.glFormat = GL_RGBA,
.glType = GL_UNSIGNED_INT_2_10_10_10_REV,
.drmFormat = DRM_FORMAT_ARGB2101010,
.glFormat = GL_RGBA,
#ifndef GLES2
.glType = GL_UNSIGNED_INT_2_10_10_10_REV,
#else
.glType = GL_UNSIGNED_INT_2_10_10_10_REV_EXT,
#endif
.withAlpha = true,
.alphaStripped = DRM_FORMAT_XRGB2101010,
.bytesPerBlock = 4,
},
{
.drmFormat = DRM_FORMAT_XBGR16161616F,
.glFormat = GL_RGBA,
.glType = GL_HALF_FLOAT,
.drmFormat = DRM_FORMAT_XBGR16161616F,
.glFormat = GL_RGBA,
#ifndef GLES2
.glType = GL_HALF_FLOAT,
#else
.glType = GL_HALF_FLOAT_OES,
#endif
.withAlpha = false,
.alphaStripped = DRM_FORMAT_XBGR16161616F,
.bytesPerBlock = 8,
},
{
.drmFormat = DRM_FORMAT_ABGR16161616F,
.glFormat = GL_RGBA,
.glType = GL_HALF_FLOAT,
.drmFormat = DRM_FORMAT_ABGR16161616F,
.glFormat = GL_RGBA,
#ifndef GLES2
.glType = GL_HALF_FLOAT,
#else
.glType = GL_HALF_FLOAT_OES,
#endif
.withAlpha = true,
.alphaStripped = DRM_FORMAT_XBGR16161616F,
.bytesPerBlock = 8,
},
{
.drmFormat = DRM_FORMAT_XBGR16161616,
.glInternalFormat = GL_RGBA16UI,
.glFormat = GL_RGBA,
.glType = GL_UNSIGNED_SHORT,
.withAlpha = false,
.alphaStripped = DRM_FORMAT_XBGR16161616,
.bytesPerBlock = 8,
.drmFormat = DRM_FORMAT_XBGR16161616,
#ifndef GLES2
.glFormat = GL_RGBA16UI,
#else
.glFormat = GL_RGBA16_EXT,
#endif
.glType = GL_UNSIGNED_SHORT,
.withAlpha = false,
.alphaStripped = DRM_FORMAT_XBGR16161616,
.bytesPerBlock = 8,
},
{
.drmFormat = DRM_FORMAT_ABGR16161616,
.glInternalFormat = GL_RGBA16UI,
.glFormat = GL_RGBA,
.glType = GL_UNSIGNED_SHORT,
.withAlpha = true,
.alphaStripped = DRM_FORMAT_XBGR16161616,
.bytesPerBlock = 8,
.drmFormat = DRM_FORMAT_ABGR16161616,
#ifndef GLES2
.glFormat = GL_RGBA16UI,
#else
.glFormat = GL_RGBA16_EXT,
#endif
.glType = GL_UNSIGNED_SHORT,
.withAlpha = true,
.alphaStripped = DRM_FORMAT_XBGR16161616,
.bytesPerBlock = 8,
},
{
.drmFormat = DRM_FORMAT_YVYU,

View File

@ -13,6 +13,8 @@
#ifdef HAS_EXECINFO
#include <execinfo.h>
#endif
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
#include <sys/sysctl.h>
@ -195,25 +197,6 @@ std::string escapeJSONStrings(const std::string& str) {
return oss.str();
}
std::string removeBeginEndSpacesTabs(std::string str) {
if (str.empty())
return str;
int countBefore = 0;
while (str[countBefore] == ' ' || str[countBefore] == '\t') {
countBefore++;
}
int countAfter = 0;
while ((int)str.length() - countAfter - 1 >= 0 && (str[str.length() - countAfter - 1] == ' ' || str[str.length() - 1 - countAfter] == '\t')) {
countAfter++;
}
str = str.substr(countBefore, str.length() - countBefore - countAfter);
return str;
}
std::optional<float> getPlusMinusKeywordResult(std::string source, float relative) {
try {
return relative + stof(source);
@ -223,31 +206,6 @@ std::optional<float> getPlusMinusKeywordResult(std::string source, float relativ
}
}
bool isNumber(const std::string& str, bool allowfloat) {
std::string copy = str;
if (*copy.begin() == '-')
copy = copy.substr(1);
if (copy.empty())
return false;
bool point = !allowfloat;
for (auto& c : copy) {
if (c == '.') {
if (point)
return false;
point = true;
continue;
}
if (!std::isdigit(c))
return false;
}
return true;
}
bool isDirection(const std::string& arg) {
return arg == "l" || arg == "r" || arg == "u" || arg == "d" || arg == "t" || arg == "b";
}
@ -579,7 +537,7 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
std::optional<std::string> cleanCmdForWorkspace(const std::string& inWorkspaceName, std::string dirtyCmd) {
std::string cmd = removeBeginEndSpacesTabs(dirtyCmd);
std::string cmd = trim(dirtyCmd);
if (!cmd.empty()) {
std::string rules;
@ -622,9 +580,10 @@ float vecToRectDistanceSquared(const Vector2D& vec, const Vector2D& p1, const Ve
// Execute a shell command and get the output
std::string execAndGet(const char* cmd) {
std::array<char, 128> buffer;
std::string result;
const std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
std::array<char, 128> buffer;
std::string result;
using PcloseType = int (*)(FILE*);
const std::unique_ptr<FILE, PcloseType> pipe(popen(cmd, "r"), static_cast<PcloseType>(pclose));
if (!pipe) {
Debug::log(ERR, "execAndGet: failed in pipe");
return "";
@ -748,7 +707,7 @@ int64_t configStringToInt(const std::string& VALUE) {
} else if (VALUE.starts_with("rgba(") && VALUE.ends_with(')')) {
const auto VALUEWITHOUTFUNC = VALUE.substr(5, VALUE.length() - 6);
if (removeBeginEndSpacesTabs(VALUEWITHOUTFUNC).length() != 8) {
if (trim(VALUEWITHOUTFUNC).length() != 8) {
Debug::log(WARN, "invalid length {} for rgba", VALUEWITHOUTFUNC.length());
throw std::invalid_argument("rgba() expects length of 8 characters (4 bytes)");
}
@ -760,7 +719,7 @@ int64_t configStringToInt(const std::string& VALUE) {
} else if (VALUE.starts_with("rgb(") && VALUE.ends_with(')')) {
const auto VALUEWITHOUTFUNC = VALUE.substr(4, VALUE.length() - 5);
if (removeBeginEndSpacesTabs(VALUEWITHOUTFUNC).length() != 6) {
if (trim(VALUEWITHOUTFUNC).length() != 6) {
Debug::log(WARN, "invalid length {} for rgb", VALUEWITHOUTFUNC.length());
throw std::invalid_argument("rgb() expects length of 6 characters (3 bytes)");
}
@ -822,15 +781,6 @@ double normalizeAngleRad(double ang) {
return ang;
}
std::string replaceInString(std::string subject, const std::string& search, const std::string& replace) {
size_t pos = 0;
while ((pos = subject.find(search, pos)) != std::string::npos) {
subject.replace(pos, search.length(), replace);
pos += replace.length();
}
return subject;
}
std::vector<SCallstackFrameInfo> getBacktrace() {
std::vector<SCallstackFrameInfo> callstack;
@ -865,7 +815,8 @@ bool envEnabled(const std::string& env) {
}
std::pair<int, std::string> openExclusiveShm() {
std::string name = g_pTokenManager->getRandomUUID();
// Only absolute paths can be shared across different shm_open() calls
std::string name = "/" + g_pTokenManager->getRandomUUID();
for (size_t i = 0; i < 69; ++i) {
int fd = shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600);

View File

@ -17,8 +17,6 @@ std::string absolutePath(const std::string&, const std::str
void addWLSignal(wl_signal*, wl_listener*, void* pOwner, const std::string& ownerString);
void removeWLSignal(wl_listener*);
std::string escapeJSONStrings(const std::string& str);
std::string removeBeginEndSpacesTabs(std::string);
bool isNumber(const std::string&, bool allowfloat = false);
bool isDirection(const std::string&);
bool isDirection(const char&);
int getWorkspaceIDFromString(const std::string&, std::string&);
@ -32,7 +30,6 @@ Vector2D configStringToVector2D(const std::string&);
std::optional<float> getPlusMinusKeywordResult(std::string in, float relative);
void matrixProjection(float mat[9], int w, int h, wl_output_transform tr);
double normalizeAngleRad(double ang);
std::string replaceInString(std::string subject, const std::string& search, const std::string& replace);
std::vector<SCallstackFrameInfo> getBacktrace();
void throwError(const std::string& err);
bool envEnabled(const std::string& env);

View File

@ -6,6 +6,8 @@
#include "../devices/ITouch.hpp"
#include "../protocols/LayerShell.hpp"
#include "../protocols/PresentationTime.hpp"
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
int ratHandler(void* data) {
g_pHyprRenderer->renderMonitor((CMonitor*)data);
@ -71,8 +73,7 @@ void CMonitor::onConnect(bool noRule) {
std::erase(szDescription, ',');
// field is backwards-compatible with intended usage of `szDescription` but excludes the parenthesized DRM node name suffix
szShortDescription =
removeBeginEndSpacesTabs(std::format("{} {} {}", output->make ? output->make : "", output->model ? output->model : "", output->serial ? output->serial : ""));
szShortDescription = trim(std::format("{} {} {}", output->make ? output->make : "", output->model ? output->model : "", output->serial ? output->serial : ""));
std::erase(szShortDescription, ',');
if (!wlr_backend_is_drm(output->backend))
@ -361,6 +362,24 @@ void CMonitor::addDamage(const CBox* box) {
g_pCompositor->scheduleFrameForMonitor(this);
}
bool CMonitor::shouldSkipScheduleFrameOnMouseEvent() {
static auto PNOBREAK = CConfigValue<Hyprlang::INT>("cursor:no_break_fs_vrr");
static auto PMINRR = CConfigValue<Hyprlang::INT>("cursor:min_refresh_rate");
// skip scheduling extra frames for fullsreen apps with vrr
bool shouldSkip = *PNOBREAK && output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED && activeWorkspace && activeWorkspace->m_bHasFullscreenWindow &&
activeWorkspace->m_efFullscreenMode == FULLSCREEN_FULL;
// keep requested minimum refresh rate
if (shouldSkip && *PMINRR && lastPresentationTimer.getMillis() > 1000 / *PMINRR) {
// damage whole screen because some previous cursor box damages were skipped
wlr_damage_ring_add_whole(&damage);
return false;
}
return shouldSkip;
}
bool CMonitor::isMirror() {
return pMirrorOf != nullptr;
}

View File

@ -158,6 +158,7 @@ class CMonitor {
void addDamage(const pixman_region32_t* rg);
void addDamage(const CRegion* rg);
void addDamage(const CBox* box);
bool shouldSkipScheduleFrameOnMouseEvent();
void setMirror(const std::string&);
bool isMirror();
bool matchesStaticSelector(const std::string& selector) const;

View File

@ -1,37 +0,0 @@
#include "MiscFunctions.hpp"
#include "VarList.hpp"
#include <ranges>
#include <algorithm>
CVarList::CVarList(const std::string& in, const size_t lastArgNo, const char delim, const bool removeEmpty) {
if (in.empty())
m_vArgs.emplace_back("");
std::string args{in};
size_t idx = 0;
size_t pos = 0;
std::ranges::replace_if(
args, [&](const char& c) { return delim == 's' ? std::isspace(c) : c == delim; }, 0);
for (const auto& s : args | std::views::split(0)) {
if (removeEmpty && s.empty())
continue;
if (++idx == lastArgNo) {
m_vArgs.emplace_back(removeBeginEndSpacesTabs(in.substr(pos)));
break;
}
pos += s.size() + 1;
m_vArgs.emplace_back(removeBeginEndSpacesTabs(std::string_view{s}.data()));
}
}
std::string CVarList::join(const std::string& joiner, size_t from, size_t to) const {
size_t last = to == 0 ? size() : to;
std::string rolling;
for (size_t i = from; i < last; ++i) {
rolling += m_vArgs[i] + (i + 1 < last ? joiner : "");
}
return rolling;
}

View File

@ -1,64 +0,0 @@
#pragma once
#include <functional>
#include <vector>
#include <string>
#include "../macros.hpp"
class CVarList {
public:
/** Split string into arg list
@param lastArgNo stop splitting after argv reaches maximum size, last arg will contain rest of unsplit args
@param delim if delimiter is 's', use std::isspace
@param removeEmpty remove empty args from argv
*/
CVarList(const std::string& in, const size_t maxSize = 0, const char delim = ',', const bool removeEmpty = false);
~CVarList() = default;
size_t size() const {
return m_vArgs.size();
}
std::string join(const std::string& joiner, size_t from = 0, size_t to = 0) const;
void map(std::function<void(std::string&)> func) {
for (auto& s : m_vArgs)
func(s);
}
void append(const std::string arg) {
m_vArgs.emplace_back(arg);
}
std::string operator[](const size_t& idx) const {
if (idx >= m_vArgs.size())
return "";
return m_vArgs[idx];
}
// for range-based loops
std::vector<std::string>::iterator begin() {
return m_vArgs.begin();
}
std::vector<std::string>::const_iterator begin() const {
return m_vArgs.begin();
}
std::vector<std::string>::iterator end() {
return m_vArgs.end();
}
std::vector<std::string>::const_iterator end() const {
return m_vArgs.end();
}
bool contains(const std::string& el) {
for (auto& a : m_vArgs) {
if (a == el)
return true;
}
return false;
}
private:
std::vector<std::string> m_vArgs;
};

View File

@ -7,7 +7,7 @@
#include "../desktop/Popup.hpp"
#include "AnimatedVariable.hpp"
#include "../desktop/WLSurface.hpp"
#include "signal/Listener.hpp"
#include "signal/Signal.hpp"
#include "Region.hpp"
class CMonitor;

View File

@ -0,0 +1,9 @@
#pragma once
#include <hyprutils/memory/WeakPtr.hpp>
using namespace Hyprutils::Memory;
#define SP Hyprutils::Memory::CSharedPointer
#define WP Hyprutils::Memory::CWeakPointer
#define UP std::unique_ptr

View File

@ -1,302 +0,0 @@
#pragma once
#include <typeinfo>
#include <typeindex>
#include <cstddef>
#include <cstdint>
#include <memory>
#define SP CSharedPointer
/*
This is a custom impl of std::shared_ptr.
It is not thread-safe like the STL one,
but Hyprland is single-threaded anyways.
It differs a bit from how the STL one works,
namely in the fact that it keeps the T* inside the
control block, and that you can still make a CWeakPtr
or deref an existing one inside the destructor.
*/
namespace CSharedPointer_ {
class impl_base {
public:
virtual ~impl_base(){};
virtual void inc() noexcept = 0;
virtual void dec() noexcept = 0;
virtual void incWeak() noexcept = 0;
virtual void decWeak() noexcept = 0;
virtual unsigned int ref() noexcept = 0;
virtual unsigned int wref() noexcept = 0;
virtual void destroy() noexcept = 0;
virtual bool destroying() noexcept = 0;
virtual bool dataNonNull() noexcept = 0;
};
template <typename T>
class impl : public impl_base {
public:
impl(T* data) noexcept : _data(data) {
;
}
/* strong refcount */
unsigned int _ref = 0;
/* weak refcount */
unsigned int _weak = 0;
T* _data = nullptr;
friend void swap(impl*& a, impl*& b) {
impl* tmp = a;
a = b;
b = tmp;
}
/* if the destructor was called,
creating shared_ptrs is no longer valid */
bool _destroying = false;
void _destroy() {
if (!_data || _destroying)
return;
// first, we destroy the data, but keep the pointer.
// this way, weak pointers will still be able to
// reference and use, but no longer create shared ones.
_destroying = true;
__deleter(_data);
// now, we can reset the data and call it a day.
_data = nullptr;
_destroying = false;
}
std::default_delete<T> __deleter{};
//
virtual void inc() noexcept {
_ref++;
}
virtual void dec() noexcept {
_ref--;
}
virtual void incWeak() noexcept {
_weak++;
}
virtual void decWeak() noexcept {
_weak--;
}
virtual unsigned int ref() noexcept {
return _ref;
}
virtual unsigned int wref() noexcept {
return _weak;
}
virtual void destroy() noexcept {
_destroy();
}
virtual bool destroying() noexcept {
return _destroying;
}
virtual bool dataNonNull() noexcept {
return _data;
}
virtual ~impl() {
destroy();
}
};
};
template <typename T>
class CSharedPointer {
public:
template <typename X>
using validHierarchy = typename std::enable_if<std::is_assignable<CSharedPointer<T>&, X>::value, CSharedPointer&>::type;
template <typename X>
using isConstructible = typename std::enable_if<std::is_constructible<T&, X&>::value>::type;
/* creates a new shared pointer managing a resource
avoid calling. Could duplicate ownership. Prefer makeShared */
explicit CSharedPointer(T* object) noexcept {
impl_ = new CSharedPointer_::impl<T>(object);
increment();
}
/* creates a shared pointer from a reference */
template <typename U, typename = isConstructible<U>>
CSharedPointer(const CSharedPointer<U>& ref) noexcept {
impl_ = ref.impl_;
increment();
}
CSharedPointer(const CSharedPointer& ref) noexcept {
impl_ = ref.impl_;
increment();
}
template <typename U, typename = isConstructible<U>>
CSharedPointer(CSharedPointer<U>&& ref) noexcept {
std::swap(impl_, ref.impl_);
}
CSharedPointer(CSharedPointer&& ref) noexcept {
std::swap(impl_, ref.impl_);
}
/* allows weakPointer to create from an impl */
CSharedPointer(CSharedPointer_::impl_base* implementation) noexcept {
impl_ = implementation;
increment();
}
/* creates an empty shared pointer with no implementation */
CSharedPointer() noexcept {
; // empty
}
/* creates an empty shared pointer with no implementation */
CSharedPointer(std::nullptr_t) noexcept {
; // empty
}
~CSharedPointer() {
// we do not decrement here,
// because we want to preserve the pointer
// in case this is the last owner.
if (impl_ && impl_->ref() == 1)
destroyImpl();
else
decrement();
}
template <typename U>
validHierarchy<const CSharedPointer<U>&> operator=(const CSharedPointer<U>& rhs) {
if (impl_ == rhs.impl_)
return *this;
decrement();
impl_ = rhs.impl_;
increment();
return *this;
}
CSharedPointer& operator=(const CSharedPointer& rhs) {
if (impl_ == rhs.impl_)
return *this;
decrement();
impl_ = rhs.impl_;
increment();
return *this;
}
template <typename U>
validHierarchy<const CSharedPointer<U>&> operator=(CSharedPointer<U>&& rhs) {
std::swap(impl_, rhs.impl_);
return *this;
}
CSharedPointer& operator=(CSharedPointer&& rhs) {
std::swap(impl_, rhs.impl_);
return *this;
}
operator bool() const {
return impl_ && impl_->dataNonNull();
}
bool operator==(const CSharedPointer& rhs) const {
return impl_ == rhs.impl_;
}
bool operator()(const CSharedPointer& lhs, const CSharedPointer& rhs) const {
return (uintptr_t)lhs.impl_ < (uintptr_t)rhs.impl_;
}
bool operator<(const CSharedPointer& rhs) const {
return (uintptr_t)impl_ < (uintptr_t)rhs.impl_;
}
T* operator->() const {
return get();
}
T& operator*() const {
return *get();
}
void reset() {
decrement();
impl_ = nullptr;
}
T* get() const {
return (T*)(impl_ ? static_cast<CSharedPointer_::impl<T>*>(impl_)->_data : nullptr);
}
unsigned int strongRef() const {
return impl_ ? impl_->ref() : 0;
}
CSharedPointer_::impl_base* impl_ = nullptr;
private:
/*
no-op if there is no impl_
may delete the stored object if ref == 0
may delete and reset impl_ if ref == 0 and weak == 0
*/
void decrement() {
if (!impl_)
return;
impl_->dec();
// if ref == 0, we can destroy impl
if (impl_->ref() == 0)
destroyImpl();
}
/* no-op if there is no impl_ */
void increment() {
if (!impl_)
return;
impl_->inc();
}
/* destroy the pointed-to object
if able, will also destroy impl */
void destroyImpl() {
// destroy the impl contents
impl_->destroy();
// check for weak refs, if zero, we can also delete impl_
if (impl_->wref() == 0) {
delete impl_;
impl_ = nullptr;
}
}
};
template <typename U, typename... Args>
static CSharedPointer<U> makeShared(Args&&... args) {
return CSharedPointer<U>(new U(std::forward<Args>(args)...));
}
template <typename T>
struct std::hash<CSharedPointer<T>> {
std::size_t operator()(const CSharedPointer<T>& p) const noexcept {
return std::hash<void*>{}(p.impl_);
}
};

View File

@ -1,190 +0,0 @@
#pragma once
#include "SharedPtr.hpp"
#define WP CWeakPointer
/*
This is a Hyprland implementation of std::weak_ptr.
See SharedPtr.hpp for more info on how it's different.
*/
template <typename T>
class CWeakPointer {
public:
template <typename X>
using validHierarchy = typename std::enable_if<std::is_assignable<CWeakPointer<T>&, X>::value, CWeakPointer&>::type;
template <typename X>
using isConstructible = typename std::enable_if<std::is_constructible<T&, X&>::value>::type;
/* create a weak ptr from a reference */
template <typename U, typename = isConstructible<U>>
CWeakPointer(const CSharedPointer<U>& ref) noexcept {
if (!ref.impl_)
return;
impl_ = ref.impl_;
incrementWeak();
}
/* create a weak ptr from another weak ptr */
template <typename U, typename = isConstructible<U>>
CWeakPointer(const CWeakPointer<U>& ref) noexcept {
if (!ref.impl_)
return;
impl_ = ref.impl_;
incrementWeak();
}
CWeakPointer(const CWeakPointer& ref) noexcept {
if (!ref.impl_)
return;
impl_ = ref.impl_;
incrementWeak();
}
template <typename U, typename = isConstructible<U>>
CWeakPointer(CWeakPointer<U>&& ref) noexcept {
std::swap(impl_, ref.impl_);
}
CWeakPointer(CWeakPointer&& ref) noexcept {
std::swap(impl_, ref.impl_);
}
/* create a weak ptr from another weak ptr with assignment */
template <typename U>
validHierarchy<const CWeakPointer<U>&> operator=(const CWeakPointer<U>& rhs) {
if (impl_ == rhs.impl_)
return *this;
decrementWeak();
impl_ = rhs.impl_;
incrementWeak();
return *this;
}
CWeakPointer<T>& operator=(const CWeakPointer& rhs) {
if (impl_ == rhs.impl_)
return *this;
decrementWeak();
impl_ = rhs.impl_;
incrementWeak();
return *this;
}
/* create a weak ptr from a shared ptr with assignment */
template <typename U>
validHierarchy<const CWeakPointer<U>&> operator=(const CSharedPointer<U>& rhs) {
if ((uintptr_t)impl_ == (uintptr_t)rhs.impl_)
return *this;
decrementWeak();
impl_ = rhs.impl_;
incrementWeak();
return *this;
}
/* create an empty weak ptr */
CWeakPointer() {
;
}
~CWeakPointer() {
decrementWeak();
}
/* expired MAY return true even if the pointer is still stored.
the situation would be e.g. self-weak pointer in a destructor.
for pointer validity, use valid() */
bool expired() const {
return !impl_ || !impl_->dataNonNull() || impl_->destroying();
}
/* this means the pointed-to object is not yet deleted and can still be
referenced, but it might be in the process of being deleted.
check !expired() if you want to check whether it's valid and
assignable to a SP. */
bool valid() const {
return impl_ && impl_->dataNonNull();
}
void reset() {
decrementWeak();
impl_ = nullptr;
}
CSharedPointer<T> lock() const {
if (!impl_ || !impl_->dataNonNull() || impl_->destroying())
return {};
return CSharedPointer<T>(impl_);
}
/* this returns valid() */
operator bool() const {
return valid();
}
bool operator==(const CWeakPointer<T>& rhs) const {
return impl_ == rhs.impl_;
}
bool operator==(const CSharedPointer<T>& rhs) const {
return impl_ == rhs.impl_;
}
bool operator()(const CWeakPointer& lhs, const CWeakPointer& rhs) const {
return (uintptr_t)lhs.impl_ < (uintptr_t)rhs.impl_;
}
bool operator<(const CWeakPointer& rhs) const {
return (uintptr_t)impl_ < (uintptr_t)rhs.impl_;
}
T* get() const {
return (T*)(impl_ ? static_cast<CSharedPointer_::impl<T>*>(impl_)->_data : nullptr);
}
T* operator->() const {
return get();
}
CSharedPointer_::impl_base* impl_ = nullptr;
private:
/* no-op if there is no impl_ */
void decrementWeak() {
if (!impl_)
return;
impl_->decWeak();
// we need to check for ->destroying,
// because otherwise we could destroy here
// and have a shared_ptr destroy the same thing
// later (in situations where we have a weak_ptr to self)
if (impl_->wref() == 0 && impl_->ref() == 0 && !impl_->destroying()) {
delete impl_;
impl_ = nullptr;
}
}
/* no-op if there is no impl_ */
void incrementWeak() {
if (!impl_)
return;
impl_->incWeak();
}
};
template <typename T>
struct std::hash<CWeakPointer<T>> {
std::size_t operator()(const CWeakPointer<T>& p) const noexcept {
return std::hash<void*>{}(p.impl_);
}
};

View File

@ -1,21 +0,0 @@
#include "Listener.hpp"
#include "Signal.hpp"
CSignalListener::CSignalListener(std::function<void(std::any)> handler) : m_fHandler(handler) {
;
}
void CSignalListener::emit(std::any data) {
if (!m_fHandler)
return;
m_fHandler(data);
}
CStaticSignalListener::CStaticSignalListener(std::function<void(void*, std::any)> handler, void* owner) : m_pOwner(owner), m_fHandler(handler) {
;
}
void CStaticSignalListener::emit(std::any data) {
m_fHandler(m_pOwner, data);
}

View File

@ -1,41 +0,0 @@
#pragma once
#include <any>
#include <memory>
#include <functional>
#include "../../macros.hpp"
class CSignal;
class CSignalListener {
public:
CSignalListener(std::function<void(std::any)> handler);
CSignalListener(CSignalListener&&) = delete;
CSignalListener(CSignalListener&) = delete;
CSignalListener(const CSignalListener&) = delete;
CSignalListener(const CSignalListener&&) = delete;
void emit(std::any data);
private:
std::function<void(std::any)> m_fHandler;
};
typedef SP<CSignalListener> CHyprSignalListener;
class CStaticSignalListener {
public:
CStaticSignalListener(std::function<void(void*, std::any)> handler, void* owner);
CStaticSignalListener(CStaticSignalListener&&) = delete;
CStaticSignalListener(CStaticSignalListener&) = delete;
CStaticSignalListener(const CStaticSignalListener&) = delete;
CStaticSignalListener(const CStaticSignalListener&&) = delete;
void emit(std::any data);
private:
void* m_pOwner = nullptr;
std::function<void(void*, std::any)> m_fHandler;
};

View File

@ -1,51 +0,0 @@
#include "Signal.hpp"
#include <algorithm>
void CSignal::emit(std::any data) {
bool dirty = false;
std::vector<SP<CSignalListener>> listeners;
for (auto& l : m_vListeners) {
if (l.expired()) {
dirty = true;
continue;
}
listeners.emplace_back(l.lock());
}
std::vector<CStaticSignalListener*> statics;
for (auto& l : m_vStaticListeners) {
statics.emplace_back(l.get());
}
for (auto& l : listeners) {
// if there is only one lock, it means the event is only held by the listeners
// vector and was removed during our iteration
if (l.strongRef() == 1) {
dirty = true;
continue;
}
l->emit(data);
}
for (auto& l : statics) {
l->emit(data);
}
// release SPs
listeners.clear();
if (dirty)
std::erase_if(m_vListeners, [](const auto& other) { return other.expired(); });
}
CHyprSignalListener CSignal::registerListener(std::function<void(std::any)> handler) {
CHyprSignalListener listener = makeShared<CSignalListener>(handler);
m_vListeners.emplace_back(WP<CSignalListener>(listener));
return listener;
}
void CSignal::registerStaticListener(std::function<void(void*, std::any)> handler, void* owner) {
m_vStaticListeners.emplace_back(std::make_unique<CStaticSignalListener>(handler, owner));
}

View File

@ -1,24 +1,5 @@
#pragma once
#include <functional>
#include <any>
#include <vector>
#include <memory>
#include <hyprutils/signal/Signal.hpp>
#include "Listener.hpp"
class CSignal {
public:
void emit(std::any data = {});
//
[[nodiscard("Listener is unregistered when the ptr is lost")]] CHyprSignalListener registerListener(std::function<void(std::any)> handler);
// this is for static listeners. They die with this signal.
// TODO: can we somehow rid of the void* data and make it a custom this?
void registerStaticListener(std::function<void(void*, std::any)> handler, void* owner);
private:
std::vector<WP<CSignalListener>> m_vListeners;
std::vector<std::unique_ptr<CStaticSignalListener>> m_vStaticListeners;
};
using namespace Hyprutils::Signal;

View File

@ -0,0 +1,5 @@
#pragma once
#include <hyprutils/string/VarList.hpp>
using namespace Hyprutils::String;

View File

@ -76,17 +76,31 @@ void CHyprMasterLayout::onWindowCreatedTiling(PHLWINDOW pWindow, eDirection dire
if (pWindow->m_bIsFloating)
return;
static auto PNEWTOP = CConfigValue<Hyprlang::INT>("master:new_on_top");
static auto PNEWONACTIVE = CConfigValue<std::string>("master:new_on_active");
static auto PNEWONTOP = CConfigValue<Hyprlang::INT>("master:new_on_top");
static auto PNEWSTATUS = CConfigValue<std::string>("master:new_status");
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
const auto PNODE = *PNEWTOP ? &m_lMasterNodesData.emplace_front() : &m_lMasterNodesData.emplace_back();
const bool BNEWBEFOREACTIVE = *PNEWONACTIVE == "before";
const bool BNEWISMASTER = *PNEWSTATUS == "master";
const auto PNODE = [&]() {
if (*PNEWONACTIVE != "none" && !BNEWISMASTER) {
const auto pLastNode = getNodeFromWindow(g_pCompositor->m_pLastWindow.lock());
if (pLastNode && !(pLastNode->isMaster && (getMastersOnWorkspace(pWindow->workspaceID()) == 1 || *PNEWSTATUS == "slave"))) {
auto it = std::find(m_lMasterNodesData.begin(), m_lMasterNodesData.end(), *pLastNode);
if (!BNEWBEFOREACTIVE)
++it;
return &(*m_lMasterNodesData.emplace(it));
}
}
return *PNEWONTOP ? &m_lMasterNodesData.emplace_front() : &m_lMasterNodesData.emplace_back();
}();
PNODE->workspaceID = pWindow->workspaceID();
PNODE->pWindow = pWindow;
static auto PNEWISMASTER = CConfigValue<Hyprlang::INT>("master:new_is_master");
const auto WINDOWSONWORKSPACE = getNodesOnWorkspace(PNODE->workspaceID);
static auto PMFACT = CConfigValue<Hyprlang::FLOAT>("master:mfact");
float lastSplitPercent = *PMFACT;
@ -186,13 +200,27 @@ void CHyprMasterLayout::onWindowCreatedTiling(PHLWINDOW pWindow, eDirection dire
}
}
if ((*PNEWISMASTER && g_pInputManager->dragMode != MBIND_MOVE) || WINDOWSONWORKSPACE == 1 || (WINDOWSONWORKSPACE > 2 && !pWindow->m_bFirstMap && OPENINGON->isMaster) ||
forceDropAsMaster) {
for (auto& nd : m_lMasterNodesData) {
if (nd.isMaster && nd.workspaceID == PNODE->workspaceID) {
nd.isMaster = false;
lastSplitPercent = nd.percMaster;
break;
if ((BNEWISMASTER && g_pInputManager->dragMode != MBIND_MOVE) //
|| WINDOWSONWORKSPACE == 1 //
|| (WINDOWSONWORKSPACE > 2 && !pWindow->m_bFirstMap && OPENINGON->isMaster) //
|| forceDropAsMaster //
|| (*PNEWSTATUS == "inherit" && OPENINGON && OPENINGON->isMaster && g_pInputManager->dragMode != MBIND_MOVE)) {
if (BNEWBEFOREACTIVE) {
for (auto& nd : m_lMasterNodesData | std::views::reverse) {
if (nd.isMaster && nd.workspaceID == PNODE->workspaceID) {
nd.isMaster = false;
lastSplitPercent = nd.percMaster;
break;
}
}
} else {
for (auto& nd : m_lMasterNodesData) {
if (nd.isMaster && nd.workspaceID == PNODE->workspaceID) {
nd.isMaster = false;
lastSplitPercent = nd.percMaster;
break;
}
}
}
@ -1440,7 +1468,7 @@ void CHyprMasterLayout::replaceWindowDataWith(PHLWINDOW from, PHLWINDOW to) {
}
Vector2D CHyprMasterLayout::predictSizeForNewWindowTiled() {
static auto PNEWISMASTER = CConfigValue<Hyprlang::INT>("master:new_is_master");
static auto PNEWSTATUS = CConfigValue<std::string>("master:new_status");
if (!g_pCompositor->m_pLastMonitor)
return {};
@ -1454,7 +1482,7 @@ Vector2D CHyprMasterLayout::predictSizeForNewWindowTiled() {
if (!MASTER) // wtf
return {};
if (*PNEWISMASTER) {
if (*PNEWSTATUS == "master") {
return MASTER->size;
} else {
const auto SLAVES = NODES - getMastersOnWorkspace(g_pCompositor->m_pLastMonitor->activeWorkspace->m_iID);

View File

@ -4,9 +4,7 @@
#include <csignal>
#include <utility>
#include "helpers/memory/WeakPtr.hpp"
#define UP std::unique_ptr
#include "helpers/memory/Memory.hpp"
#ifndef NDEBUG
#ifdef HYPRLAND_DEBUG

View File

@ -6,6 +6,7 @@
#include "../desktop/Window.hpp"
#include "../desktop/LayerSurface.hpp"
#include "eventLoop/EventLoopManager.hpp"
#include "../helpers/varlist/VarList.hpp"
int wlTick(SP<CEventLoopTimer> self, void* data) {
if (g_pAnimationManager)
@ -396,6 +397,8 @@ void CAnimationManager::onWindowPostCreateClose(PHLWINDOW pWindow, bool close) {
auto ANIMSTYLE = pWindow->m_vRealPosition.m_pConfig->pValues->internalStyle;
transform(ANIMSTYLE.begin(), ANIMSTYLE.end(), ANIMSTYLE.begin(), ::tolower);
CVarList animList(ANIMSTYLE, 0, 's');
// if the window is not being animated, that means the layout set a fixed size for it, don't animate.
if (!pWindow->m_vRealPosition.isBeingAnimated() && !pWindow->m_vRealSize.isBeingAnimated())
return;
@ -407,12 +410,8 @@ void CAnimationManager::onWindowPostCreateClose(PHLWINDOW pWindow, bool close) {
if (pWindow->m_sAdditionalConfigData.animationStyle != "") {
// the window has config'd special anim
if (pWindow->m_sAdditionalConfigData.animationStyle.starts_with("slide")) {
if (pWindow->m_sAdditionalConfigData.animationStyle.contains(' ')) {
// has a direction
animationSlide(pWindow, pWindow->m_sAdditionalConfigData.animationStyle.substr(pWindow->m_sAdditionalConfigData.animationStyle.find(' ') + 1), close);
} else {
animationSlide(pWindow, "", close);
}
CVarList animList2(pWindow->m_sAdditionalConfigData.animationStyle, 0, 's');
animationSlide(pWindow, animList2[1], close);
} else {
// anim popin, fallback
@ -429,9 +428,9 @@ void CAnimationManager::onWindowPostCreateClose(PHLWINDOW pWindow, bool close) {
animationPopin(pWindow, close, minPerc / 100.f);
}
} else {
if (ANIMSTYLE == "slide") {
animationSlide(pWindow, "", close);
} else {
if (animList[0] == "slide")
animationSlide(pWindow, animList[1], close);
else {
// anim popin, fallback
float minPerc = 0.f;
@ -451,9 +450,9 @@ void CAnimationManager::onWindowPostCreateClose(PHLWINDOW pWindow, bool close) {
std::string CAnimationManager::styleValidInConfigVar(const std::string& config, const std::string& style) {
if (config.starts_with("window")) {
if (style == "slide") {
if (style.starts_with("slide"))
return "";
} else if (style.starts_with("popin")) {
else if (style.starts_with("popin")) {
// try parsing
float minPerc = 0.f;
if (style.find("%") != std::string::npos) {

View File

@ -3,7 +3,7 @@
#include <vector>
#include "../defines.hpp"
#include "../helpers/memory/SharedPtr.hpp"
#include "../helpers/memory/Memory.hpp"
struct SHyprIPCEvent {
std::string event;

View File

@ -1,18 +1,22 @@
#include "../config/ConfigValue.hpp"
#include "../devices/IKeyboard.hpp"
#include "../managers/SeatManager.hpp"
#include "../protocols/LayerShell.hpp"
#include "../protocols/ShortcutsInhibit.hpp"
#include "../render/decorations/CHyprGroupBarDecoration.hpp"
#include "KeybindManager.hpp"
#include "TokenManager.hpp"
#include "debug/Log.hpp"
#include "helpers/VarList.hpp"
#include "helpers/varlist/VarList.hpp"
#include <optional>
#include <iterator>
#include <string>
#include <string_view>
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
#include <sys/ioctl.h>
#include <fcntl.h>
#include <vector>
@ -111,6 +115,7 @@ CKeybindManager::CKeybindManager() {
m_mDispatchers["movewindoworgroup"] = moveWindowOrGroup;
m_mDispatchers["setignoregrouplock"] = setIgnoreGroupLock;
m_mDispatchers["denywindowfromgroup"] = denyWindowFromGroup;
m_mDispatchers["event"] = event;
m_mDispatchers["global"] = global;
m_tScrollTimer.reset();
@ -836,7 +841,7 @@ bool CKeybindManager::handleInternalKeybinds(xkb_keysym_t keysym) {
void CKeybindManager::spawn(std::string args) {
args = removeBeginEndSpacesTabs(args);
args = trim(args);
std::string RULES = "";
@ -1083,6 +1088,8 @@ void CKeybindManager::changeworkspace(std::string args) {
if (!PMONITORWORKSPACEOWNER)
return;
updateRelativeCursorCoords();
g_pCompositor->setActiveMonitor(PMONITORWORKSPACEOWNER);
if (BISWORKSPACECURRENT) {
@ -1111,6 +1118,16 @@ void CKeybindManager::changeworkspace(std::string args) {
else
g_pInputManager->simulateMouseMovement();
}
const static auto PWARPONWORKSPACECHANGE = CConfigValue<Hyprlang::INT>("cursor:warp_on_change_workspace");
if (*PWARPONWORKSPACECHANGE) {
auto PLAST = pWorkspaceToChangeTo->getLastFocusedWindow();
auto HLSurface = CWLSurface::fromResource(g_pSeatManager->state.pointerFocus.lock());
if (PLAST && (!HLSurface || HLSurface->getWindow()))
PLAST->warpCursor();
}
}
void CKeybindManager::fullscreenActive(std::string args) {
@ -2677,3 +2694,7 @@ void CKeybindManager::moveGroupWindow(std::string args) {
PLASTWINDOW->updateWindowDecos();
}
void CKeybindManager::event(std::string args) {
g_pEventManager->postEvent(SHyprIPCEvent{"custom", args});
}

View File

@ -14,23 +14,25 @@ class CPluginSystem;
class IKeyboard;
struct SKeybind {
std::string key = "";
std::set<xkb_keysym_t> sMkKeys = {};
uint32_t keycode = 0;
bool catchAll = false;
uint32_t modmask = 0;
std::set<xkb_keysym_t> sMkMods = {};
std::string handler = "";
std::string arg = "";
bool locked = false;
std::string submap = "";
bool release = false;
bool repeat = false;
bool mouse = false;
bool nonConsuming = false;
bool transparent = false;
bool ignoreMods = false;
bool multiKey = false;
std::string key = "";
std::set<xkb_keysym_t> sMkKeys = {};
uint32_t keycode = 0;
bool catchAll = false;
uint32_t modmask = 0;
std::set<xkb_keysym_t> sMkMods = {};
std::string handler = "";
std::string arg = "";
bool locked = false;
std::string submap = "";
std::string description = "";
bool release = false;
bool repeat = false;
bool mouse = false;
bool nonConsuming = false;
bool transparent = false;
bool ignoreMods = false;
bool multiKey = false;
bool hasDescription = false;
// DO NOT INITIALIZE
bool shadowed = false;
@ -203,6 +205,7 @@ class CKeybindManager {
static void setIgnoreGroupLock(std::string);
static void denyWindowFromGroup(std::string);
static void global(std::string);
static void event(std::string);
friend class CCompositor;
friend class CInputManager;

View File

@ -150,6 +150,20 @@ CPointerManager::CPointerManager() {
});
}
void CPointerManager::lockSoftwareAll() {
for (auto& state : monitorStates)
state->softwareLocks++;
updateCursorBackend();
}
void CPointerManager::unlockSoftwareAll() {
for (auto& state : monitorStates)
state->softwareLocks--;
updateCursorBackend();
}
void CPointerManager::lockSoftwareForMonitor(SP<CMonitor> mon) {
auto state = stateFor(mon);
state->softwareLocks++;
@ -661,7 +675,7 @@ void CPointerManager::damageIfSoftware() {
continue;
if ((mw->softwareLocks > 0 || mw->hardwareFailed || *PNOHW) && b.overlaps({mw->monitor->vecPosition, mw->monitor->vecSize})) {
g_pHyprRenderer->damageBox(&b);
g_pHyprRenderer->damageBox(&b, mw->monitor->shouldSkipScheduleFrameOnMouseEvent());
break;
}
}

View File

@ -43,6 +43,8 @@ class CPointerManager {
void lockSoftwareForMonitor(SP<CMonitor> pMonitor);
void unlockSoftwareForMonitor(SP<CMonitor> pMonitor);
void lockSoftwareAll();
void unlockSoftwareAll();
void renderSoftwareCursorsFor(SP<CMonitor> pMonitor, timespec* now, CRegion& damage /* logical */, std::optional<Vector2D> overridePos = {} /* monitor-local */);

View File

@ -5,8 +5,8 @@
#include "../protocols/TextInputV1.hpp"
#include "../protocols/GlobalShortcuts.hpp"
#include "../protocols/Screencopy.hpp"
#include "../helpers/memory/WeakPtr.hpp"
#include "../helpers/signal/Listener.hpp"
#include "../helpers/memory/Memory.hpp"
#include "../helpers/signal/Signal.hpp"
#include <unordered_map>
class CProtocolManager {

View File

@ -94,7 +94,7 @@ void CSeatManager::setKeyboard(SP<IKeyboard> KEEB) {
}
void CSeatManager::updateActiveKeyboardData() {
if (keyboard)
if (keyboard && keyboard->wlr())
PROTO::seat->updateRepeatInfo(keyboard->wlr()->repeat_info.rate, keyboard->wlr()->repeat_info.delay);
PROTO::seat->updateKeymap();
}
@ -191,6 +191,11 @@ void CSeatManager::setPointerFocus(SP<CWLSurfaceResource> surf, const Vector2D&
if (state.pointerFocus == surf)
return;
if (PROTO::data->dndActive() && surf) {
Debug::log(LOG, "[seatmgr] Refusing pointer focus during an active dnd");
return;
}
if (!mouse || !mouse->wlr()) {
Debug::log(ERR, "BUG THIS: setPointerFocus without a valid mouse set");
return;
@ -337,34 +342,11 @@ void CSeatManager::sendPointerAxis(uint32_t timeMs, wl_pointer_axis axis, double
}
void CSeatManager::sendTouchDown(SP<CWLSurfaceResource> surf, uint32_t timeMs, int32_t id, const Vector2D& local) {
if (state.touchFocus == surf)
return;
listeners.touchSurfaceDestroy.reset();
if (state.touchFocusResource) {
auto client = state.touchFocusResource->client();
for (auto& s : seatResources) {
if (s->resource->client() != client)
continue;
for (auto& t : s->resource->touches) {
if (!t)
continue;
t->sendUp(timeMs, id);
}
}
}
state.touchFocusResource.reset();
state.touchFocus = surf;
if (!surf) {
events.touchFocusChange.emit();
return;
}
auto client = surf->client();
for (auto& r : seatResources | std::views::reverse) {
if (r->resource->client() != client)
@ -381,11 +363,34 @@ void CSeatManager::sendTouchDown(SP<CWLSurfaceResource> surf, uint32_t timeMs, i
listeners.touchSurfaceDestroy = surf->events.destroy.registerListener([this, timeMs, id](std::any d) { sendTouchUp(timeMs + 10, id); });
events.touchFocusChange.emit();
touchLocks++;
if (touchLocks <= 1)
events.touchFocusChange.emit();
}
void CSeatManager::sendTouchUp(uint32_t timeMs, int32_t id) {
sendTouchDown(nullptr, timeMs, id, {});
if (!state.touchFocusResource || touchLocks <= 0)
return;
auto client = state.touchFocusResource->client();
for (auto& r : seatResources | std::views::reverse) {
if (r->resource->client() != client)
continue;
state.touchFocusResource = r->resource;
for (auto& t : r->resource->touches) {
if (!t)
continue;
t->sendUp(timeMs, id);
}
}
touchLocks--;
if (touchLocks <= 0)
events.touchFocusChange.emit();
}
void CSeatManager::sendTouchMotion(uint32_t timeMs, int32_t id, const Vector2D& local) {

View File

@ -155,6 +155,7 @@ class CSeatManager {
} listeners;
Vector2D lastLocalCoords;
int touchLocks = 0; // we assume there aint like 20 touch devices at once...
friend struct SSeatResourceContainer;
friend class CSeatGrab;

View File

@ -2,7 +2,7 @@
#include "../defines.hpp"
#include "../helpers/Timer.hpp"
#include "../helpers/signal/Listener.hpp"
#include "../helpers/signal/Signal.hpp"
#include <cstdint>
#include <unordered_map>

View File

@ -25,5 +25,6 @@ CThreadManager::CThreadManager() {
}
CThreadManager::~CThreadManager() {
//
if (m_esConfigTimer)
wl_event_source_remove(m_esConfigTimer);
}

View File

@ -5,7 +5,7 @@
#include <unordered_map>
#include <string>
#include "../helpers/memory/SharedPtr.hpp"
#include "../helpers/memory/Memory.hpp"
class CUUIDToken {
public:

View File

@ -4,7 +4,7 @@
#include <functional>
#include <optional>
#include "../../helpers/memory/SharedPtr.hpp"
#include "../../helpers/memory/Memory.hpp"
class CEventLoopTimer {
public:

View File

@ -155,6 +155,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
Vector2D surfacePos = Vector2D(-1337, -1337);
PHLWINDOW pFoundWindow;
PHLLS pFoundLayerSurface;
SSessionLockSurface* pSessionLock = nullptr;
if (!g_pCompositor->m_bReadyToProcess || g_pCompositor->m_bIsShuttingDown || g_pCompositor->m_bUnsafeState)
return;
@ -186,7 +187,9 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
if (*PZOOMFACTOR != 1.f)
g_pHyprRenderer->damageMonitor(PMONITOR);
if (!PMONITOR->solitaryClient.lock() && g_pHyprRenderer->shouldRenderCursor() && PMONITOR->output->software_cursor_locks > 0)
bool skipFrameSchedule = PMONITOR->shouldSkipScheduleFrameOnMouseEvent();
if (!PMONITOR->solitaryClient.lock() && g_pHyprRenderer->shouldRenderCursor() && PMONITOR->output->software_cursor_locks > 0 && !skipFrameSchedule)
g_pCompositor->scheduleFrameForMonitor(PMONITOR);
PHLWINDOW forcedFocus = m_pForcedFocus.lock();
@ -261,12 +264,12 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
g_pCompositor->setActiveMonitor(PMONITOR);
if (g_pSessionLockManager->isSessionLocked()) {
const auto PSLS = PMONITOR ? g_pSessionLockManager->getSessionLockSurfaceForMonitor(PMONITOR->ID) : nullptr;
pSessionLock = PMONITOR ? g_pSessionLockManager->getSessionLockSurfaceForMonitor(PMONITOR->ID) : nullptr;
if (!PSLS)
if (!pSessionLock)
return;
foundSurface = PSLS->surface->surface();
foundSurface = pSessionLock->surface->surface();
surfacePos = PMONITOR->vecPosition;
}
@ -350,6 +353,10 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
if (pFoundWindow) {
if (!pFoundWindow->m_bIsX11) {
foundSurface = g_pCompositor->vectorWindowToSurface(mouseCoords, pFoundWindow, surfaceCoords);
if (!foundSurface) {
foundSurface = pFoundWindow->m_pWLSurface->resource();
surfacePos = pFoundWindow->m_vRealPosition.value();
}
} else {
foundSurface = pFoundWindow->m_pWLSurface->resource();
surfacePos = pFoundWindow->m_vRealPosition.value();
@ -365,7 +372,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
foundSurface =
g_pCompositor->vectorToLayerSurface(mouseCoords, &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &surfaceCoords, &pFoundLayerSurface);
if (g_pCompositor->m_pLastMonitor->output->software_cursor_locks > 0)
if (g_pCompositor->m_pLastMonitor->output->software_cursor_locks > 0 && !skipFrameSchedule)
g_pCompositor->scheduleFrameForMonitor(g_pCompositor->m_pLastMonitor.get());
// grabs
@ -455,7 +462,9 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
restoreCursorIconToApp();
}
if (pFoundWindow) {
if (pSessionLock != nullptr)
g_pCompositor->focusSurface(foundSurface);
else if (pFoundWindow) {
// change cursor icon if hovering over border
if (*PRESIZEONBORDER && *PRESIZECURSORICON) {
if (!pFoundWindow->m_bIsFullscreen && !pFoundWindow->hasPopupAt(mouseCoords)) {
@ -509,7 +518,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
}
if (pFoundLayerSurface && (pFoundLayerSurface->layerSurface->current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE) && FOLLOWMOUSE != 3 &&
allowKeyboardRefocus) {
(allowKeyboardRefocus || pFoundLayerSurface->layerSurface->current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE)) {
g_pCompositor->focusSurface(foundSurface);
}
@ -1223,12 +1232,20 @@ void CInputManager::destroyKeyboard(SP<IKeyboard> pKeyboard) {
std::erase_if(m_vKeyboards, [pKeyboard](const auto& other) { return other == pKeyboard; });
if (m_vKeyboards.size() > 0) {
const auto PNEWKEYBOARD = m_vKeyboards.back();
g_pSeatManager->setKeyboard(PNEWKEYBOARD);
PNEWKEYBOARD->active = true;
} else {
bool found = false;
for (auto& k : m_vKeyboards | std::views::reverse) {
if (!k->wlr())
continue;
g_pSeatManager->setKeyboard(k);
found = true;
break;
}
if (!found)
g_pSeatManager->setKeyboard(nullptr);
} else
g_pSeatManager->setKeyboard(nullptr);
}
removeFromHIDs(pKeyboard);
}
@ -1381,6 +1398,41 @@ void CInputManager::refocus() {
mouseMoveUnified(0, true);
}
void CInputManager::refocusLastWindow(CMonitor* pMonitor) {
if (!pMonitor) {
refocus();
return;
}
Vector2D surfaceCoords;
PHLLS pFoundLayerSurface;
SP<CWLSurfaceResource> foundSurface = nullptr;
g_pInputManager->releaseAllMouseButtons();
// first try for an exclusive layer
if (!m_dExclusiveLSes.empty())
foundSurface = m_dExclusiveLSes[m_dExclusiveLSes.size() - 1]->surface->resource();
// then any surfaces above windows on the same monitor
if (!foundSurface)
foundSurface = g_pCompositor->vectorToLayerSurface(g_pInputManager->getMouseCoordsInternal(), &pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
&surfaceCoords, &pFoundLayerSurface);
if (!foundSurface)
foundSurface = g_pCompositor->vectorToLayerSurface(g_pInputManager->getMouseCoordsInternal(), &pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
&surfaceCoords, &pFoundLayerSurface);
if (!foundSurface && g_pCompositor->m_pLastWindow.lock() && g_pCompositor->isWorkspaceVisibleNotCovered(g_pCompositor->m_pLastWindow->m_pWorkspace)) {
// then the last focused window if we're on the same workspace as it
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
g_pCompositor->focusWindow(PLASTWINDOW);
} else {
// otherwise fall back to a normal refocus.
refocus();
}
}
void CInputManager::unconstrainMouse() {
if (g_pSeatManager->mouse.expired())
return;

View File

@ -6,7 +6,7 @@
#include "../../helpers/WLClasses.hpp"
#include "../../helpers/Timer.hpp"
#include "InputMethodRelay.hpp"
#include "../../helpers/signal/Listener.hpp"
#include "../../helpers/signal/Signal.hpp"
#include "../../devices/IPointer.hpp"
#include "../../devices/ITouch.hpp"
#include "../../devices/Tablet.hpp"
@ -104,6 +104,7 @@ class CInputManager {
Vector2D getMouseCoordsInternal();
void refocus();
void refocusLastWindow(CMonitor* pMonitor);
void simulateMouseMovement();
void sendMotionEventsToFocused();

View File

@ -4,7 +4,7 @@
#include "../../desktop/WLSurface.hpp"
#include "../../macros.hpp"
#include "../../helpers/Box.hpp"
#include "../../helpers/signal/Listener.hpp"
#include "../../helpers/signal/Signal.hpp"
class CInputMethodPopupV2;

View File

@ -3,7 +3,7 @@
#include <list>
#include "../../defines.hpp"
#include "../../helpers/WLClasses.hpp"
#include "../../helpers/signal/Listener.hpp"
#include "../../helpers/signal/Signal.hpp"
#include "TextInput.hpp"
#include "InputMethodPopup.hpp"
#include <any>

View File

@ -3,7 +3,7 @@
#include "../../helpers/WLListener.hpp"
#include "../../macros.hpp"
#include "../../helpers/Box.hpp"
#include "../../helpers/signal/Listener.hpp"
#include "../../helpers/signal/Signal.hpp"
#include <memory>
struct wl_client;

View File

@ -13,6 +13,7 @@ executable('Hyprland', src,
dependency('cairo'),
dependency('hyprcursor'),
dependency('hyprlang', version: '>= 0.3.2'),
dependency('hyprutils', version: '>= 0.1.1'),
dependency('libdrm'),
dependency('egl'),
dependency('xkbcommon'),

View File

@ -1,6 +1,6 @@
#include "HookSystem.hpp"
#include "../debug/Log.hpp"
#include "../helpers/VarList.hpp"
#include "../helpers/varlist/VarList.hpp"
#include "../managers/TokenManager.hpp"
#include "../Compositor.hpp"

View File

@ -5,7 +5,7 @@
#include <unordered_map>
#include "WaylandProtocol.hpp"
#include "alpha-modifier-v1.hpp"
#include "../helpers/signal/Listener.hpp"
#include "../helpers/signal/Signal.hpp"
class CWLSurfaceResource;

View File

@ -6,7 +6,7 @@
#include <cstdint>
#include <unordered_map>
#include <vector>
#include "../helpers/signal/Listener.hpp"
#include "../helpers/signal/Signal.hpp"
class CFocusGrab;
class CSeatGrab;

View File

@ -122,25 +122,19 @@ void CForeignToplevelHandleWlr::sendMonitor(CMonitor* pMonitor) {
if (lastMonitorID == (int64_t)pMonitor->ID)
return;
const auto CLIENT = resource->client();
struct wl_resource* outputResource;
const auto CLIENT = resource->client();
if (const auto PLASTMONITOR = g_pCompositor->getMonitorFromID(lastMonitorID); PLASTMONITOR) {
wl_resource_for_each(outputResource, &PLASTMONITOR->output->resources) {
if (wl_resource_get_client(outputResource) != CLIENT)
continue;
const auto OLDRESOURCE = PROTO::outputs.at(PLASTMONITOR->szName)->outputResourceFrom(CLIENT);
resource->sendOutputLeave(outputResource);
}
if (OLDRESOURCE)
resource->sendOutputLeave(OLDRESOURCE->getResource()->resource());
}
wl_resource_for_each(outputResource, &pMonitor->output->resources) {
if (wl_resource_get_client(outputResource) != CLIENT)
continue;
const auto NEWRESOURCE = PROTO::outputs.at(pMonitor->szName)->outputResourceFrom(CLIENT);
resource->sendOutputEnter(outputResource);
}
if (NEWRESOURCE)
resource->sendOutputEnter(NEWRESOURCE->getResource()->resource());
lastMonitorID = pMonitor->ID;
}

View File

@ -5,7 +5,7 @@
#include <cstdint>
#include "WaylandProtocol.hpp"
#include "wlr-gamma-control-unstable-v1.hpp"
#include "../helpers/signal/Listener.hpp"
#include "../helpers/signal/Signal.hpp"
class CMonitor;

View File

@ -81,7 +81,7 @@ SP<CInputMethodV2> CInputMethodKeyboardGrabV2::getOwner() {
}
wl_client* CInputMethodKeyboardGrabV2::client() {
return resource->client();
return resource->resource() ? resource->client() : nullptr;
}
CInputMethodPopupV2::CInputMethodPopupV2(SP<CZwpInputPopupSurfaceV2> resource_, SP<CInputMethodV2> owner_, SP<CWLSurfaceResource> surface) : resource(resource_), owner(owner_) {
@ -373,4 +373,4 @@ void CInputMethodV2Protocol::onGetIME(CZwpInputMethodManagerV2* mgr, wl_resource
LOGM(LOG, "New IME with resource id {}", id);
events.newIME.emit(RESOURCE);
}
}

View File

@ -5,7 +5,7 @@
#include <cstdint>
#include "WaylandProtocol.hpp"
#include "wlr-output-management-unstable-v1.hpp"
#include "../helpers/signal/Listener.hpp"
#include "../helpers/signal/Signal.hpp"
class CMonitor;

View File

@ -5,7 +5,7 @@
#include <cstdint>
#include "WaylandProtocol.hpp"
#include "wlr-output-power-management-unstable-v1.hpp"
#include "../helpers/signal/Listener.hpp"
#include "../helpers/signal/Signal.hpp"
class CMonitor;

View File

@ -9,7 +9,7 @@
#include "pointer-constraints-unstable-v1.hpp"
#include "../helpers/Vector2D.hpp"
#include "../helpers/Region.hpp"
#include "../helpers/signal/Listener.hpp"
#include "../helpers/signal/Signal.hpp"
class CWLSurface;
class CWLSurfaceResource;

View File

@ -114,9 +114,11 @@ CSessionLock::CSessionLock(SP<CExtSessionLockV1> resource_) : resource(resource_
return;
}
events.unlockAndDestroy.emit();
inert = true;
PROTO::sessionLock->locked = false;
events.unlockAndDestroy.emit();
inert = true;
PROTO::sessionLock->destroyResource(this);
});
}

View File

@ -100,7 +100,7 @@ wlr_keyboard* CVirtualKeyboardV1Resource::wlr() {
}
wl_client* CVirtualKeyboardV1Resource::client() {
return resource->client();
return resource->resource() ? resource->client() : nullptr;
}
void CVirtualKeyboardV1Resource::releasePressed() {
@ -151,4 +151,4 @@ void CVirtualKeyboardProtocol::onCreateKeeb(CZwpVirtualKeyboardManagerV1* pMgr,
LOGM(LOG, "New VKeyboard at id {}", id);
events.newKeyboard.emit(RESOURCE);
}
}

View File

@ -136,10 +136,12 @@ CXDGToplevelResource::CXDGToplevelResource(SP<CXdgToplevel> resource_, SP<CXDGSu
wl_array_release(&arr);
}
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_LEFT);
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_RIGHT);
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_TOP);
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_BOTTOM);
if (resource->version() >= 2) {
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_LEFT);
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_RIGHT);
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_TOP);
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_BOTTOM);
}
resource->setSetTitle([this](CXdgToplevel* r, const char* t) {
state.title = t;
@ -261,6 +263,9 @@ uint32_t CXDGToplevelResource::setActive(bool active) {
}
uint32_t CXDGToplevelResource::setSuspeneded(bool sus) {
if (resource->version() < 6)
return owner->scheduleConfigure(); // SUSPENDED is since 6
bool set = std::find(pendingApply.states.begin(), pendingApply.states.end(), XDG_TOPLEVEL_STATE_SUSPENDED) != pendingApply.states.end();
if (sus == set)
@ -507,12 +512,12 @@ CXDGPositionerRules::CXDGPositionerRules(SP<CXDGPositionerResource> positioner)
state = positioner->state;
}
static Vector2D pointForAnchor(const CBox& box, xdgPositionerAnchor anchor) {
static Vector2D pointForAnchor(const CBox& box, const Vector2D& predictionSize, xdgPositionerAnchor anchor) {
switch (anchor) {
case XDG_POSITIONER_ANCHOR_TOP: return box.pos() + Vector2D{box.size().x / 2.F, 0};
case XDG_POSITIONER_ANCHOR_BOTTOM: return box.pos() + Vector2D{box.size().x / 2.F, box.size().y};
case XDG_POSITIONER_ANCHOR_LEFT: return box.pos() + Vector2D{0, box.size().y / 2.F};
case XDG_POSITIONER_ANCHOR_RIGHT: return box.pos() + Vector2D{box.size().x, box.size().y / 2.F};
case XDG_POSITIONER_ANCHOR_TOP: return box.pos() + Vector2D{box.size().x / 2.F - predictionSize.x / 2.F, 0};
case XDG_POSITIONER_ANCHOR_BOTTOM: return box.pos() + Vector2D{box.size().x / 2.F - predictionSize.x / 2.F, box.size().y};
case XDG_POSITIONER_ANCHOR_LEFT: return box.pos() + Vector2D{0, box.size().y / 2.F - predictionSize.y / 2.F};
case XDG_POSITIONER_ANCHOR_RIGHT: return box.pos() + Vector2D{box.size().x, box.size().y / 2.F - predictionSize.y / 2.F};
case XDG_POSITIONER_ANCHOR_TOP_LEFT: return box.pos();
case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: return box.pos() + Vector2D{0, box.size().y};
case XDG_POSITIONER_ANCHOR_TOP_RIGHT: return box.pos() + Vector2D{box.size().x, 0};
@ -527,7 +532,7 @@ CBox CXDGPositionerRules::getPosition(const CBox& constraint, const Vector2D& pa
Debug::log(LOG, "GetPosition with constraint {} {} and parent {}", constraint.pos(), constraint.size(), parentCoord);
CBox predictedBox = {parentCoord + constraint.pos() + pointForAnchor(state.anchorRect, state.anchor) + state.offset, state.requestedSize};
CBox predictedBox = {parentCoord + constraint.pos() + pointForAnchor(state.anchorRect, state.requestedSize, state.anchor) + state.offset, state.requestedSize};
bool success = predictedBox.inside(constraint);

View File

@ -13,13 +13,17 @@ CWLDataOfferResource::CWLDataOfferResource(SP<CWlDataOffer> resource_, SP<IDataS
return;
resource->setDestroy([this](CWlDataOffer* r) {
if (!dead)
if (!dead && (recvd || accepted))
PROTO::data->completeDrag();
else
PROTO::data->abortDrag();
PROTO::data->destroyResource(this);
});
resource->setOnDestroy([this](CWlDataOffer* r) {
if (!dead)
if (!dead && (recvd || accepted))
PROTO::data->completeDrag();
else
PROTO::data->abortDrag();
PROTO::data->destroyResource(this);
});
@ -85,8 +89,10 @@ void CWLDataOfferResource::sendData() {
if (!source)
return;
resource->sendSourceActions(7);
resource->sendAction(WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE);
if (resource->version() >= 3) {
resource->sendSourceActions(7);
resource->sendAction(WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE);
}
for (auto& m : source->mimes()) {
LOGM(LOG, " | offer {:x} supports mime {}", (uintptr_t)this, m);
@ -536,6 +542,11 @@ void CWLDataDeviceProtocol::initiateDrag(WP<CWLDataSourceResource> currentSource
}
});
// unfocus the pointer from the surface, this is part of """standard""" wayland procedure and gtk will freak out if this isn't happening.
// BTW, the spec does NOT require this explicitly...
// Fuck you gtk.
g_pSeatManager->setPointerFocus(nullptr, {});
// make a new offer, etc
updateDrag();
}
@ -590,15 +601,35 @@ void CWLDataDeviceProtocol::dropDrag() {
return;
}
dnd.currentSource->sendDndDropPerformed();
if (!wasDragSuccessful()) {
abortDrag();
return;
}
dnd.focusedDevice->sendDrop();
dnd.focusedDevice->sendLeave();
resetDndState();
if (dnd.overriddenCursor)
g_pInputManager->unsetCursorImage();
dnd.overriddenCursor = false;
g_pInputManager->simulateMouseMovement();
}
bool CWLDataDeviceProtocol::wasDragSuccessful() {
if (!dnd.focusedDevice || !dnd.currentSource)
return false;
for (auto& o : m_vOffers) {
if (o->dead || !o->source || !o->source->hasDnd())
continue;
if (o->recvd || o->accepted)
return true;
}
return false;
}
void CWLDataDeviceProtocol::completeDrag() {
@ -607,12 +638,13 @@ void CWLDataDeviceProtocol::completeDrag() {
if (!dnd.focusedDevice || !dnd.currentSource)
return;
dnd.currentSource->sendDndDropPerformed();
dnd.currentSource->sendDndFinished();
dnd.focusedDevice.reset();
dnd.currentSource.reset();
g_pSeatManager->resendEnterEvents();
g_pInputManager->simulateMouseMovement();
}
void CWLDataDeviceProtocol::abortDrag() {
@ -631,7 +663,7 @@ void CWLDataDeviceProtocol::abortDrag() {
dnd.focusedDevice.reset();
dnd.currentSource.reset();
g_pSeatManager->resendEnterEvents();
g_pInputManager->simulateMouseMovement();
}
void CWLDataDeviceProtocol::renderDND(CMonitor* pMonitor, timespec* when) {
@ -650,5 +682,5 @@ void CWLDataDeviceProtocol::renderDND(CMonitor* pMonitor, timespec* when) {
}
bool CWLDataDeviceProtocol::dndActive() {
return dnd.currentSource;
return dnd.currentSource && dnd.mouseButton /* test a member of the state to ensure it's also present */;
}

View File

@ -175,6 +175,7 @@ class CWLDataDeviceProtocol : public IWaylandProtocol {
void dropDrag();
void completeDrag();
void resetDndState();
bool wasDragSuccessful();
//
SP<CWLDataDeviceResource> dataDeviceForClient(wl_client*);

View File

@ -9,6 +9,9 @@ CWLOutputResource::CWLOutputResource(SP<CWlOutput> resource_, SP<CMonitor> pMoni
pClient = resource->client();
if (!monitor)
return;
resource->setOnDestroy([this](CWlOutput* r) {
if (monitor && PROTO::outputs.contains(monitor->szName))
PROTO::outputs.at(monitor->szName)->destroyResource(this);
@ -69,6 +72,9 @@ CWLOutputProtocol::CWLOutputProtocol(const wl_interface* iface, const int& ver,
}
void CWLOutputProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
if (defunct)
Debug::log(WARN, "[wl_output] Binding a wl_output that's inert?? Possible client bug.");
const auto RESOURCE = m_vOutputs.emplace_back(makeShared<CWLOutputResource>(makeShared<CWlOutput>(client, ver, id), monitor.lock()));
if (!RESOURCE->good()) {

View File

@ -5,7 +5,7 @@
#include <cstdint>
#include "../WaylandProtocol.hpp"
#include "wayland.hpp"
#include "../../helpers/signal/Listener.hpp"
#include "../../helpers/signal/Signal.hpp"
class CMonitor;

View File

@ -25,37 +25,38 @@ void CWLTouchResource::sendDown(SP<CWLSurfaceResource> surface, uint32_t timeMs,
if (!owner)
return;
if (currentSurface) {
LOGM(WARN, "requested CWLTouchResource::sendDown without sendUp first.");
sendUp(timeMs, id);
}
ASSERT(surface->client() == owner->client());
currentSurface = surface;
listeners.destroySurface = surface->events.destroy.registerListener([this, timeMs, id](std::any d) { sendUp(timeMs + 10 /* hack */, id); });
resource->sendDown(g_pSeatManager->nextSerial(owner.lock()), timeMs, surface->getResource().get(), id, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y));
fingers++;
}
void CWLTouchResource::sendUp(uint32_t timeMs, int32_t id) {
if (!owner || !currentSurface)
if (!owner)
return;
resource->sendUp(g_pSeatManager->nextSerial(owner.lock()), timeMs, id);
currentSurface.reset();
listeners.destroySurface.reset();
fingers--;
if (fingers <= 0) {
currentSurface.reset();
listeners.destroySurface.reset();
fingers = 0;
}
}
void CWLTouchResource::sendMotion(uint32_t timeMs, int32_t id, const Vector2D& local) {
if (!owner || !currentSurface)
if (!owner)
return;
resource->sendMotion(timeMs, id, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y));
}
void CWLTouchResource::sendFrame() {
if (!owner || !currentSurface)
if (!owner)
return;
resource->sendFrame();
@ -97,6 +98,9 @@ CWLPointerResource::CWLPointerResource(SP<CWlPointer> resource_, SP<CWLSeatResou
g_pSeatManager->onSetCursor(owner.lock(), serial, surf ? CWLSurfaceResource::fromResource(surf) : nullptr, {hotX, hotY});
});
if (g_pSeatManager->state.pointerFocus && g_pSeatManager->state.pointerFocus->client() == resource->client())
sendEnter(g_pSeatManager->state.pointerFocus.lock(), {-1, -1} /* Coords don't really matter that much, they will be updated next move */);
}
bool CWLPointerResource::good() {
@ -199,10 +203,16 @@ CWLKeyboardResource::CWLKeyboardResource(SP<CWlKeyboard> resource_, SP<CWLSeatRe
resource->setRelease([this](CWlKeyboard* r) { PROTO::seat->destroyResource(this); });
resource->setOnDestroy([this](CWlKeyboard* r) { PROTO::seat->destroyResource(this); });
static auto REPEAT = CConfigValue<Hyprlang::INT>("input:repeat_rate");
static auto DELAY = CConfigValue<Hyprlang::INT>("input:repeat_delay");
if (!g_pSeatManager->keyboard) {
LOGM(ERR, "No keyboard on bound wl_keyboard??");
return;
}
sendKeymap(g_pSeatManager->keyboard.lock());
repeatInfo(*REPEAT, *DELAY);
repeatInfo(g_pSeatManager->keyboard->repeatRate, g_pSeatManager->keyboard->repeatDelay);
if (g_pSeatManager->state.keyboardFocus && g_pSeatManager->state.keyboardFocus->client() == resource->client())
sendEnter(g_pSeatManager->state.keyboardFocus.lock());
}
bool CWLKeyboardResource::good() {

View File

@ -46,6 +46,8 @@ class CWLTouchResource {
SP<CWlTouch> resource;
WP<CWLSurfaceResource> currentSurface;
int fingers = 0;
struct {
CHyprSignalListener destroySurface;
} listeners;

View File

@ -79,20 +79,20 @@ CHyprOpenGLImpl::CHyprOpenGLImpl() {
m_tGlobalTimer.reset();
}
std::vector<uint64_t> CHyprOpenGLImpl::getModsForFormat(EGLint format) {
std::optional<std::vector<uint64_t>> CHyprOpenGLImpl::getModsForFormat(EGLint format) {
// TODO: return std::expected when clang supports it
if (!m_sExts.EXT_image_dma_buf_import_modifiers)
return {};
return std::nullopt;
EGLint len = 0;
if (!m_sProc.eglQueryDmaBufModifiersEXT(wlr_egl_get_display(g_pCompositor->m_sWLREGL), format, 0, nullptr, nullptr, &len)) {
Debug::log(ERR, "EGL: Failed to query mods");
return {};
return std::nullopt;
}
if (len <= 0)
return {DRM_FORMAT_MOD_LINEAR, DRM_FORMAT_MOD_INVALID}; // assume the driver can do linear and implicit.
return std::vector<uint64_t>{};
std::vector<uint64_t> mods;
std::vector<EGLBoolean> external;
@ -103,13 +103,21 @@ std::vector<uint64_t> CHyprOpenGLImpl::getModsForFormat(EGLint format) {
m_sProc.eglQueryDmaBufModifiersEXT(wlr_egl_get_display(g_pCompositor->m_sWLREGL), format, len, mods.data(), external.data(), &len);
std::vector<uint64_t> result;
bool linearIsExternal = false;
for (size_t i = 0; i < mods.size(); ++i) {
if (external.at(i))
if (external.at(i)) {
if (mods.at(i) == DRM_FORMAT_MOD_LINEAR)
linearIsExternal = true;
continue;
}
result.push_back(mods.at(i));
}
// if the driver doesn't mark linear as external, add it. It's allowed unless the driver says otherwise. (e.g. nvidia)
if (!linearIsExternal && std::find(mods.begin(), mods.end(), DRM_FORMAT_MOD_LINEAR) == mods.end() && mods.size() == 0)
mods.push_back(DRM_FORMAT_MOD_LINEAR);
return result;
}
@ -147,15 +155,19 @@ void CHyprOpenGLImpl::initDRMFormats() {
for (auto& fmt : formats) {
std::vector<uint64_t> mods;
if (!DISABLE_MODS)
mods = getModsForFormat(fmt);
else
if (!DISABLE_MODS) {
auto ret = getModsForFormat(fmt);
if (!ret.has_value())
continue;
mods = *ret;
} else
mods = {DRM_FORMAT_MOD_LINEAR};
m_bHasModifiers = m_bHasModifiers || mods.size() > 0;
if (mods.size() == 0)
continue;
// EGL can always do implicit modifiers.
mods.push_back(DRM_FORMAT_MOD_INVALID);
dmaFormats.push_back(SDRMFormat{
.format = fmt,

View File

@ -238,7 +238,9 @@ class CHyprOpenGLImpl {
void createBGTextureForMonitor(CMonitor*);
void initShaders();
void initDRMFormats();
std::vector<uint64_t> getModsForFormat(EGLint format);
//
std::optional<std::vector<uint64_t>> getModsForFormat(EGLint format);
// returns the out FB, can be either Mirror or MirrorSwap
CFramebuffer* blurMainFramebufferWithDamage(float a, CRegion* damage);

View File

@ -90,6 +90,11 @@ CHyprRenderer::CHyprRenderer() {
wl_event_source_timer_update(m_pCursorTicker, 500);
}
CHyprRenderer::~CHyprRenderer() {
if (m_pCursorTicker)
wl_event_source_remove(m_pCursorTicker);
}
static void renderSurface(SP<CWLSurfaceResource> surface, int x, int y, void* data) {
if (!surface->current.buffer || !surface->current.buffer->texture)
return;
@ -1253,6 +1258,15 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
TRACY_GPU_ZONE("Render");
static bool zoomLock = false;
if (zoomLock && *PZOOMFACTOR == 1.f) {
g_pPointerManager->unlockSoftwareAll();
zoomLock = false;
} else if (!zoomLock && *PZOOMFACTOR != 1.f) {
g_pPointerManager->lockSoftwareAll();
zoomLock = true;
}
if (pMonitor == g_pCompositor->getMonitorFromCursor())
g_pHyprOpenGL->m_RenderData.mouseZoomFactor = std::clamp(*PZOOMFACTOR, 1.f, INFINITY);
else
@ -1265,10 +1279,6 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
pMonitor->forceFullFrames = 10;
}
bool lockSoftware = pMonitor == g_pCompositor->getMonitorFromCursor() && *PZOOMFACTOR != 1.f;
if (lockSoftware)
g_pPointerManager->lockSoftwareForMonitor(pMonitor->self.lock());
CRegion damage, finalDamage;
if (!beginRender(pMonitor, damage, RENDER_MODE_NORMAL)) {
Debug::log(ERR, "renderer: couldn't beginRender()!");
@ -1370,9 +1380,6 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
endRender();
if (lockSoftware)
g_pPointerManager->unlockSoftwareForMonitor(pMonitor->self.lock());
TRACY_GPU_COLLECT;
if (!pMonitor->mirrors.empty()) {
@ -1756,7 +1763,7 @@ void CHyprRenderer::damageMonitor(CMonitor* pMonitor) {
Debug::log(LOG, "Damage: Monitor {}", pMonitor->szName);
}
void CHyprRenderer::damageBox(CBox* pBox) {
void CHyprRenderer::damageBox(CBox* pBox, bool skipFrameSchedule) {
if (g_pCompositor->m_bUnsafeState)
return;
@ -1766,7 +1773,8 @@ void CHyprRenderer::damageBox(CBox* pBox) {
CBox damageBox = {pBox->x - m->vecPosition.x, pBox->y - m->vecPosition.y, pBox->width, pBox->height};
damageBox.scale(m->scale);
m->addDamage(&damageBox);
if (!skipFrameSchedule)
m->addDamage(&damageBox);
}
static auto PLOGDAMAGE = CConfigValue<Hyprlang::INT>("debug:log_damage");

View File

@ -42,12 +42,13 @@ struct SSessionLockSurface;
class CHyprRenderer {
public:
CHyprRenderer();
~CHyprRenderer();
void renderMonitor(CMonitor* pMonitor);
void arrangeLayersForMonitor(const int&);
void damageSurface(SP<CWLSurfaceResource>, double, double, double scale = 1.0);
void damageWindow(PHLWINDOW, bool forceFull = false);
void damageBox(CBox*);
void damageBox(CBox*, bool skipFrameSchedule = false);
void damageBox(const int& x, const int& y, const int& w, const int& h);
void damageRegion(const CRegion&);
void damageMonitor(CMonitor*);

View File

@ -119,15 +119,15 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a) {
g_pDecorationPositioner->repositionDeco(this);
}
int xoff = 0;
int yoff = 0;
float xoff = 0;
float yoff = 0;
for (int i = 0; i < barsToDraw; ++i) {
const auto WINDOWINDEX = *PSTACKED ? m_dwGroupMembers.size() - i - 1 : i;
CBox rect = {ASSIGNEDBOX.x + xoff - pMonitor->vecPosition.x + m_pWindow->m_vFloatingOffset.x,
ASSIGNEDBOX.y + ASSIGNEDBOX.h - yoff - BAR_INDICATOR_HEIGHT - BAR_PADDING_OUTER_VERT - pMonitor->vecPosition.y + m_pWindow->m_vFloatingOffset.y, m_fBarWidth,
BAR_INDICATOR_HEIGHT};
CBox rect = {ASSIGNEDBOX.x + floor(xoff) - pMonitor->vecPosition.x + m_pWindow->m_vFloatingOffset.x,
ASSIGNEDBOX.y + ASSIGNEDBOX.h - floor(yoff) - BAR_INDICATOR_HEIGHT - BAR_PADDING_OUTER_VERT - pMonitor->vecPosition.y + m_pWindow->m_vFloatingOffset.y,
m_fBarWidth, BAR_INDICATOR_HEIGHT};
if (rect.width <= 0 || rect.height <= 0)
break;
@ -151,8 +151,8 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a) {
color.a *= a;
g_pHyprOpenGL->renderRect(&rect, color);
rect = {ASSIGNEDBOX.x + xoff - pMonitor->vecPosition.x + m_pWindow->m_vFloatingOffset.x,
ASSIGNEDBOX.y + ASSIGNEDBOX.h - yoff - ONEBARHEIGHT - pMonitor->vecPosition.y + m_pWindow->m_vFloatingOffset.y, m_fBarWidth,
rect = {ASSIGNEDBOX.x + floor(xoff) - pMonitor->vecPosition.x + m_pWindow->m_vFloatingOffset.x,
ASSIGNEDBOX.y + ASSIGNEDBOX.h - floor(yoff) - ONEBARHEIGHT - pMonitor->vecPosition.y + m_pWindow->m_vFloatingOffset.y, m_fBarWidth,
(*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0)};
rect.scale(pMonitor->scale);
@ -172,10 +172,10 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a) {
.emplace_back(std::make_unique<CTitleTex>(m_dwGroupMembers[WINDOWINDEX].lock(),
Vector2D{m_fBarWidth * pMonitor->scale, (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale}, pMonitor->scale))
.get();
rect.y += ((rect.height - pTitleTex->textHeight) / 2.0) * pMonitor->scale;
rect.height = (pTitleTex->textHeight) * pMonitor->scale;
rect.width = pTitleTex->textWidth * pMonitor->scale;
rect.x += m_fBarWidth / 2.0 - (pTitleTex->textWidth / 2.0) * pMonitor->scale;
rect.y += (rect.height - pTitleTex->textHeight) / 2.0;
rect.height = pTitleTex->textHeight;
rect.width = pTitleTex->textWidth;
rect.x += (m_fBarWidth * pMonitor->scale) / 2.0 - (pTitleTex->textWidth / 2.0);
rect.round();
g_pHyprOpenGL->renderTexture(pTitleTex->tex, &rect, 1.f);

View File

@ -155,6 +155,14 @@ static int xwaylandReady(int fd, uint32_t mask, void* data) {
return g_pXWayland->pServer->ready(fd, mask);
}
static bool safeRemove(const std::string& path) {
try {
return std::filesystem::remove(path);
} catch (std::exception& e) { Debug::log(ERR, "[XWayland] failed to remove {}", path); }
return false;
}
bool CXWaylandServer::tryOpenSockets() {
for (size_t i = 0; i <= 32; ++i) {
auto LOCK = std::format("/tmp/.X{}-lock", i);
@ -162,7 +170,7 @@ bool CXWaylandServer::tryOpenSockets() {
if (int fd = open(LOCK.c_str(), O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0444); fd >= 0) {
// we managed to open the lock
if (!openSockets(xFDs, i)) {
std::filesystem::remove(LOCK);
safeRemove(LOCK);
close(fd);
continue;
}
@ -170,7 +178,7 @@ bool CXWaylandServer::tryOpenSockets() {
const auto PIDSTR = std::format("{}", getpid());
if (write(fd, PIDSTR.c_str(), PIDSTR.length()) != (long)PIDSTR.length()) {
std::filesystem::remove(LOCK);
safeRemove(LOCK);
close(fd);
continue;
}
@ -197,7 +205,7 @@ bool CXWaylandServer::tryOpenSockets() {
} catch (...) { continue; }
if (kill(pid, 0) != 0 && errno == ESRCH) {
if (!std::filesystem::remove(LOCK))
if (!safeRemove(LOCK))
continue;
i--;
@ -228,7 +236,7 @@ CXWaylandServer::~CXWaylandServer() {
close(xFDs[1]);
auto LOCK = std::format("/tmp/.X{}-lock", display);
std::filesystem::remove(LOCK);
safeRemove(LOCK);
std::string path;
#ifdef __linux__
@ -236,7 +244,7 @@ CXWaylandServer::~CXWaylandServer() {
#else
path = std::format("/tmp/.X11-unix/X{}_", display);
#endif
std::filesystem::remove(path);
safeRemove(path);
}
void CXWaylandServer::die() {

View File

@ -179,7 +179,7 @@ void CXWaylandSurface::configure(const CBox& box) {
void CXWaylandSurface::activate(bool activate) {
if (overrideRedirect && !activate)
return;
g_pXWayland->pWM->activateSurface(self.lock());
g_pXWayland->pWM->activateSurface(self.lock(), activate);
}
void CXWaylandSurface::setFullscreen(bool fs) {

View File

@ -248,11 +248,11 @@ void CXWM::readProp(SP<CXWaylandSurface> XSURF, uint32_t atom, xcb_get_property_
}
}
} else {
Debug::log(LOG, "[xwm] Unhandled prop {} -> {}", atom, propName);
Debug::log(TRACE, "[xwm] Unhandled prop {} -> {}", atom, propName);
return;
}
Debug::log(LOG, "[xwm] Handled prop {} -> {}", atom, propName);
Debug::log(TRACE, "[xwm] Handled prop {} -> {}", atom, propName);
}
void CXWM::handlePropertyNotify(xcb_property_notify_event_t* e) {
@ -354,11 +354,11 @@ void CXWM::handleClientMessage(xcb_client_message_event_t* e) {
} else if (e->type == HYPRATOMS["_NET_ACTIVE_WINDOW"]) {
XSURF->events.activate.emit();
} else {
Debug::log(LOG, "[xwm] Unhandled message prop {} -> {}", e->type, propName);
Debug::log(TRACE, "[xwm] Unhandled message prop {} -> {}", e->type, propName);
return;
}
Debug::log(LOG, "[xwm] Handled message prop {} -> {}", e->type, propName);
Debug::log(TRACE, "[xwm] Handled message prop {} -> {}", e->type, propName);
}
void CXWM::handleFocusIn(xcb_focus_in_event_t* e) {
@ -394,11 +394,16 @@ void CXWM::focusWindow(SP<CXWaylandSurface> surf) {
if (surf == focusedSurface)
return;
auto oldSurf = focusedSurface.lock();
focusedSurface = surf;
if (oldSurf)
sendState(oldSurf);
// send state to all surfaces, sometimes we might lose some
// that could still stick with the focused atom
for (auto& s : mappedSurfaces) {
if (!s)
continue;
sendState(s.lock());
}
if (!surf) {
xcb_set_input_focus_checked(connection, XCB_INPUT_FOCUS_POINTER_ROOT, XCB_NONE, XCB_CURRENT_TIME);
@ -490,18 +495,18 @@ std::string CXWM::mimeFromAtom(xcb_atom_t atom) {
}
void CXWM::handleSelectionNotify(xcb_selection_notify_event_t* e) {
Debug::log(LOG, "[xwm] Selection notify for {} prop {} target {}", e->selection, e->property, e->target);
Debug::log(TRACE, "[xwm] Selection notify for {} prop {} target {}", e->selection, e->property, e->target);
SXSelection& sel = clipboard;
if (e->property == XCB_ATOM_NONE) {
if (sel.transfer) {
Debug::log(ERR, "[xwm] converting selection failed");
Debug::log(TRACE, "[xwm] converting selection failed");
sel.transfer.reset();
}
} else if (e->target == HYPRATOMS["TARGETS"]) {
if (!focusedSurface) {
Debug::log(LOG, "[xwm] denying access to write to clipboard because no X client is in focus");
Debug::log(TRACE, "[xwm] denying access to write to clipboard because no X client is in focus");
return;
}
@ -519,7 +524,7 @@ bool CXWM::handleSelectionPropertyNotify(xcb_property_notify_event_t* e) {
}
void CXWM::handleSelectionRequest(xcb_selection_request_event_t* e) {
Debug::log(LOG, "[xwm] Selection request for {} prop {} target {} time {} requestor {} selection {}", e->selection, e->property, e->target, e->time, e->requestor,
Debug::log(TRACE, "[xwm] Selection request for {} prop {} target {} time {} requestor {} selection {}", e->selection, e->property, e->target, e->time, e->requestor,
e->selection);
SXSelection& sel = clipboard;
@ -542,7 +547,7 @@ void CXWM::handleSelectionRequest(xcb_selection_request_event_t* e) {
}
if (!g_pSeatManager->state.keyboardFocusResource || g_pSeatManager->state.keyboardFocusResource->client() != g_pXWayland->pServer->xwaylandClient) {
Debug::log(LOG, "[xwm] Ignoring clipboard access: xwayland not in focus");
Debug::log(TRACE, "[xwm] Ignoring clipboard access: xwayland not in focus");
selectionSendNotify(e, false);
return;
}
@ -585,7 +590,7 @@ void CXWM::handleSelectionRequest(xcb_selection_request_event_t* e) {
}
bool CXWM::handleSelectionXFixesNotify(xcb_xfixes_selection_notify_event_t* e) {
Debug::log(LOG, "[xwm] Selection xfixes notify for {}", e->selection);
Debug::log(TRACE, "[xwm] Selection xfixes notify for {}", e->selection);
// IMPORTANT: mind the g_pSeatManager below
SXSelection& sel = clipboard;
@ -868,13 +873,17 @@ void CXWM::createWMWindow() {
xcb_set_selection_owner(connection, wmWindow, HYPRATOMS["_NET_WM_CM_S0"], XCB_CURRENT_TIME);
}
void CXWM::activateSurface(SP<CXWaylandSurface> surf) {
if (surf == focusedSurface || (surf && surf->overrideRedirect))
void CXWM::activateSurface(SP<CXWaylandSurface> surf, bool activate) {
if ((surf == focusedSurface && activate) || (surf && surf->overrideRedirect))
return;
setActiveWindow(surf ? surf->xID : (uint32_t)XCB_WINDOW_NONE);
focusWindow(surf);
if (!activate || !surf) {
setActiveWindow((uint32_t)XCB_WINDOW_NONE);
focusWindow(nullptr);
} else {
setActiveWindow(surf ? surf->xID : (uint32_t)XCB_WINDOW_NONE);
focusWindow(surf);
}
xcb_flush(connection);
}

View File

@ -1,6 +1,6 @@
#pragma once
#include "../helpers/signal/Listener.hpp"
#include "../helpers/signal/Signal.hpp"
#include "../helpers/WLListener.hpp"
#include "../macros.hpp"
@ -79,7 +79,7 @@ class CXWM {
void setActiveWindow(xcb_window_t window);
void sendState(SP<CXWaylandSurface> surf);
void focusWindow(SP<CXWaylandSurface> surf);
void activateSurface(SP<CXWaylandSurface> surf);
void activateSurface(SP<CXWaylandSurface> surf, bool activate);
bool isWMWindow(xcb_window_t w);
void sendWMMessage(SP<CXWaylandSurface> surf, xcb_client_message_data_t* data, uint32_t mask);

Some files were not shown because too many files have changed in this diff Show More