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 --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
cmake --install build 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 - name: Get Xorg pacman pkgs
shell: bash shell: bash
if: inputs.INSTALL_XORG_PKGS == 'true' 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 wayland-server wayland-client wayland-cursor wayland-protocols
cairo pango pangocairo pixman-1 cairo pango pangocairo pixman-1
libdrm libinput hwdata libseat libdisplay-info libliftoff libudev gbm 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) find_package(hyprwayland-scanner 0.3.10 REQUIRED)

View File

@ -42,7 +42,7 @@ uninstall:
pluginenv: pluginenv:
@echo -en "$(MAKE) pluginenv has been deprecated.\nPlease run $(MAKE) all && sudo $(MAKE) installheaders\n" @echo -en "$(MAKE) pluginenv has been deprecated.\nPlease run $(MAKE) all && sudo $(MAKE) installheaders\n"
@exit 1 @exit 1
installheaders: installheaders:
@if [ ! -f ./src/version.h ]; then echo -en "You need to run $(MAKE) all first.\n" && exit 1; fi @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 # See https://wiki.hyprland.org/Configuring/Master-Layout/ for more
master { master {
new_is_master = true new_status = master
} }
# https://wiki.hyprland.org/Configuring/Variables/#misc # https://wiki.hyprland.org/Configuring/Variables/#misc

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,9 @@
#include <ctype.h> #include <cctype>
#include <netdb.h> #include <netdb.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <stdio.h> #include <cstdio>
#include <stdlib.h> #include <cstdlib>
#include <string.h> #include <cstring>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
@ -12,7 +12,7 @@
#include <unistd.h> #include <unistd.h>
#include <ranges> #include <ranges>
#include <algorithm> #include <algorithm>
#include <signal.h> #include <csignal>
#include <format> #include <format>
#include <iostream> #include <iostream>
@ -22,8 +22,11 @@
#include <vector> #include <vector>
#include <deque> #include <deque>
#include <filesystem> #include <filesystem>
#include <stdarg.h> #include <cstdarg>
#include <regex> #include <regex>
#include <sys/socket.h>
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
#include "Strings.hpp" #include "Strings.hpp"
@ -98,13 +101,53 @@ std::vector<SInstanceData> instances() {
return result; 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); 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(), ' '); const auto ARGS = std::count(arg.begin(), arg.end(), ' ');
if (ARGS < minArgs) { 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; return -1;
} }
@ -139,6 +182,9 @@ int request(std::string arg, int minArgs = 0) {
return 4; return 4;
} }
if (needRoll)
return rollingRead(SERVERSOCKET);
std::string reply = ""; std::string reply = "";
char buffer[8192] = {0}; char buffer[8192] = {0};
@ -270,12 +316,6 @@ std::deque<std::string> splitArgs(int argc, char** argv) {
return result; 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) { int main(int argc, char** argv) {
bool parseArgs = true; bool parseArgs = true;
@ -288,6 +328,7 @@ int main(int argc, char** argv) {
std::string fullArgs = ""; std::string fullArgs = "";
const auto ARGS = splitArgs(argc, argv); const auto ARGS = splitArgs(argc, argv);
bool json = false; bool json = false;
bool needRoll = false;
std::string overrideInstance = ""; std::string overrideInstance = "";
for (std::size_t i = 0; i < ARGS.size(); ++i) { for (std::size_t i = 0; i < ARGS.size(); ++i) {
@ -307,6 +348,9 @@ int main(int argc, char** argv) {
fullArgs += "a"; fullArgs += "a";
} else if ((ARGS[i] == "-c" || ARGS[i] == "--config") && !fullArgs.contains("c")) { } else if ((ARGS[i] == "-c" || ARGS[i] == "--config") && !fullArgs.contains("c")) {
fullArgs += "c"; fullArgs += "c";
} else if ((ARGS[i] == "-f" || ARGS[i] == "--follow") && !fullArgs.contains("f")) {
fullArgs += "f";
needRoll = true;
} else if (ARGS[i] == "--batch") { } else if (ARGS[i] == "--batch") {
fullRequest = "--batch "; fullRequest = "--batch ";
} else if (ARGS[i] == "--instance" || ARGS[i] == "-i") { } else if (ARGS[i] == "--instance" || ARGS[i] == "-i") {
@ -366,6 +410,11 @@ int main(int argc, char** argv) {
return 0; return 0;
} }
if (needRoll && !fullRequest.contains("/rollinglog")) {
log("only 'rollinglog' command supports '--follow' option");
return 1;
}
if (overrideInstance.contains("_")) if (overrideInstance.contains("_"))
instanceSignature = overrideInstance; instanceSignature = overrideInstance;
else if (!overrideInstance.empty()) { else if (!overrideInstance.empty()) {
@ -425,6 +474,8 @@ int main(int argc, char** argv) {
exitStatus = request(fullRequest, 1); exitStatus = request(fullRequest, 1);
else if (fullRequest.contains("/--help")) else if (fullRequest.contains("/--help"))
std::cout << USAGE << std::endl; std::cout << USAGE << std::endl;
else if (fullRequest.contains("/rollinglog") && needRoll)
exitStatus = request(fullRequest, 0, true);
else { else {
exitStatus = request(fullRequest); exitStatus = request(fullRequest);
} }

View File

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

View File

@ -9,11 +9,11 @@ file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
set(CMAKE_CXX_STANDARD 23) 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}) add_executable(hyprpm ${SRCFILES})
target_link_libraries(hyprpm PUBLIC PkgConfig::tomlplusplus) target_link_libraries(hyprpm PUBLIC PkgConfig::deps)
# binary # binary
install(TARGETS hyprpm) install(TARGETS hyprpm)

View File

@ -19,30 +19,15 @@
#include <toml++/toml.hpp> #include <toml++/toml.hpp>
static std::string removeBeginEndSpacesTabs(std::string str) { #include <hyprutils/string/String.hpp>
if (str.empty()) using namespace Hyprutils::String;
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;
}
static std::string execAndGet(std::string cmd) { static std::string execAndGet(std::string cmd) {
cmd += " 2>&1"; cmd += " 2>&1";
std::array<char, 128> buffer; std::array<char, 128> buffer;
std::string result; std::string result;
const std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose); using PcloseType = int (*)(FILE*);
const std::unique_ptr<FILE, PcloseType> pipe(popen(cmd.c_str(), "r"), static_cast<PcloseType>(pclose));
if (!pipe) if (!pipe)
return ""; return "";
@ -374,7 +359,7 @@ eHeadersErrors CPluginManager::headersValid() {
if (PATH.ends_with("protocols") || PATH.ends_with("wlroots-hyprland")) if (PATH.ends_with("protocols") || PATH.ends_with("wlroots-hyprland"))
continue; continue;
verHeader = removeBeginEndSpacesTabs(PATH.substr(2)) + "/hyprland/src/version.h"; verHeader = trim(PATH.substr(2)) + "/hyprland/src/version.h";
break; 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."); 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 // let us give a bit of leg-room for shallowing
// due to timezones, etc. // due to timezones, etc.
const std::string SHALLOW_DATE = 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) if (m_bVerbose && bShallow)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "will shallow since: " + SHALLOW_DATE); 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) if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "cmake returned: " + ret); 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. // missing deps, let the user know.
std::string missing = ret.substr(ret.find("The following required packages were not found:")); std::string missing = ret.substr(ret.find("CMake Error at"));
missing = missing.substr(0, missing.find("Call Stack")); 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')); 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; return false;
} }

View File

@ -58,7 +58,8 @@ class CPluginManager {
bool hasDeps(); bool hasDeps();
bool m_bVerbose = false; bool m_bVerbose = false;
bool m_bNoShallow = false;
// will delete recursively if exists!! // will delete recursively if exists!!
bool createSafeDirectory(const std::string& path); 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 --help | -h Show this menu
--verbose | -v Enable too much logging --verbose | -v Enable too much logging
--force | -f Force an operation ignoring checks (e.g. update -f) --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; 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) { for (int i = 1; i < argc; ++i) {
if (ARGS[i].starts_with("-")) { if (ARGS[i].starts_with("-")) {
@ -52,6 +53,8 @@ int main(int argc, char** argv, char** envp) {
notify = true; notify = true;
} else if (ARGS[i] == "--verbose" || ARGS[i] == "-v") { } else if (ARGS[i] == "--verbose" || ARGS[i] == "-v") {
verbose = true; verbose = true;
} else if (ARGS[i] == "--no-shallow" || ARGS[i] == "-s") {
noShallow = true;
} else if (ARGS[i] == "--force" || ARGS[i] == "-f") { } else if (ARGS[i] == "--force" || ARGS[i] == "-f") {
force = true; force = true;
std::cout << Colors::RED << "!" << Colors::RESET << " Using --force, I hope you know what you are doing.\n"; 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; return 0;
} }
g_pPluginManager = std::make_unique<CPluginManager>(); g_pPluginManager = std::make_unique<CPluginManager>();
g_pPluginManager->m_bVerbose = verbose; g_pPluginManager->m_bVerbose = verbose;
g_pPluginManager->m_bNoShallow = noShallow;
if (command[0] == "add") { if (command[0] == "add") {
if (command.size() < 2) { if (command.size() < 2) {

View File

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

View File

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

View File

@ -23,6 +23,7 @@ in {
# Dependencies # Dependencies
inputs.hyprcursor.overlays.default inputs.hyprcursor.overlays.default
inputs.hyprlang.overlays.default inputs.hyprlang.overlays.default
inputs.hyprutils.overlays.default
inputs.hyprwayland-scanner.overlays.default inputs.hyprwayland-scanner.overlays.default
self.overlays.xwayland 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 #include <helpers/SdDaemon.hpp> // for SdNotify
#endif #endif
#include <ranges> #include <ranges>
#include "helpers/VarList.hpp" #include "helpers/varlist/VarList.hpp"
#include "protocols/FractionalScale.hpp" #include "protocols/FractionalScale.hpp"
#include "protocols/PointerConstraints.hpp" #include "protocols/PointerConstraints.hpp"
#include "protocols/LayerShell.hpp" #include "protocols/LayerShell.hpp"
@ -24,6 +24,9 @@
#include "desktop/LayerSurface.hpp" #include "desktop/LayerSurface.hpp"
#include "xwayland/XWayland.hpp" #include "xwayland/XWayland.hpp"
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
@ -348,6 +351,8 @@ void CCompositor::cleanup() {
g_pXWaylandManager.reset(); g_pXWaylandManager.reset();
g_pPointerManager.reset(); g_pPointerManager.reset();
g_pSeatManager.reset(); g_pSeatManager.reset();
g_pHyprCtl.reset();
g_pEventLoopManager.reset();
if (m_sWLRRenderer) if (m_sWLRRenderer)
wlr_renderer_destroy(m_sWLRRenderer); wlr_renderer_destroy(m_sWLRRenderer);
@ -361,6 +366,7 @@ void CCompositor::cleanup() {
if (m_critSigSource) if (m_critSigSource)
wl_event_source_remove(m_critSigSource); wl_event_source_remove(m_critSigSource);
wl_event_loop_destroy(m_sWLEventLoop);
wl_display_terminate(m_sWLDisplay); wl_display_terminate(m_sWLDisplay);
m_sWLDisplay = nullptr; 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) if (ls->fadingOut || !ls->layerSurface || (ls->layerSurface && !ls->layerSurface->surface->mapped) || ls->alpha.value() == 0.f)
continue; 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) {
if (surf->current.input.empty()) if (surf->current.input.empty())
@ -1108,6 +1114,17 @@ bool CCompositor::isWorkspaceVisible(PHLWORKSPACE w) {
return valid(w) && w->m_bVisible; 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) { PHLWORKSPACE CCompositor::getWorkspaceByID(const int& id) {
for (auto& w : m_vWorkspaces) { for (auto& w : m_vWorkspaces) {
if (w->m_iID == id && !w->inert()) if (w->m_iID == id && !w->inert())
@ -1347,6 +1364,10 @@ void CCompositor::addToFadingOutSafe(PHLLS pLS) {
m_vSurfacesFadingOut.emplace_back(pLS); m_vSurfacesFadingOut.emplace_back(pLS);
} }
void CCompositor::removeFromFadingOutSafe(PHLLS ls) {
std::erase(m_vSurfacesFadingOut, ls);
}
void CCompositor::addToFadingOutSafe(PHLWINDOW pWindow) { void CCompositor::addToFadingOutSafe(PHLWINDOW pWindow) {
const auto FOUND = std::find_if(m_vWindowsFadingOut.begin(), m_vWindowsFadingOut.end(), [&](PHLWINDOWREF& other) { return other.lock() == 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) { if (POLDMON) {
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(POLDMON->ID); g_pLayoutManager->getCurrentLayout()->recalculateMonitor(POLDMON->ID);
updateFullscreenFadeOnWorkspace(POLDMON->activeWorkspace); updateFullscreenFadeOnWorkspace(POLDMON->activeWorkspace);
updateSuspendedStates();
} }
updateFullscreenFadeOnWorkspace(pWorkspace); updateFullscreenFadeOnWorkspace(pWorkspace);
updateSuspendedStates();
// event // event
g_pEventManager->postEvent(SHyprIPCEvent{"moveworkspace", pWorkspace->m_szName + "," + pMonitor->szName}); g_pEventManager->postEvent(SHyprIPCEvent{"moveworkspace", pWorkspace->m_szName + "," + pMonitor->szName});

View File

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

View File

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

View File

@ -3,9 +3,10 @@
#include "../render/decorations/CHyprGroupBarDecoration.hpp" #include "../render/decorations/CHyprGroupBarDecoration.hpp"
#include "config/ConfigDataValues.hpp" #include "config/ConfigDataValues.hpp"
#include "helpers/VarList.hpp" #include "helpers/varlist/VarList.hpp"
#include "../protocols/LayerShell.hpp" #include "../protocols/LayerShell.hpp"
#include <cstddef>
#include <cstdint> #include <cstdint>
#include <string.h> #include <string.h>
#include <string> #include <string>
@ -13,6 +14,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <glob.h> #include <glob.h>
#include <xkbcommon/xkbcommon.h>
#include <algorithm> #include <algorithm>
#include <fstream> #include <fstream>
@ -20,7 +22,8 @@
#include <sstream> #include <sstream>
#include <ranges> #include <ranges>
#include <unordered_set> #include <unordered_set>
#include <xkbcommon/xkbcommon.h> #include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
extern "C" char** environ; extern "C" char** environ;
@ -428,8 +431,9 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("master:special_scale_factor", {1.f}); m_pConfig->addConfigValue("master:special_scale_factor", {1.f});
m_pConfig->addConfigValue("master:mfact", {0.55f}); 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: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:new_on_top", Hyprlang::INT{0});
m_pConfig->addConfigValue("master:no_gaps_when_only", Hyprlang::INT{0}); m_pConfig->addConfigValue("master:no_gaps_when_only", Hyprlang::INT{0});
m_pConfig->addConfigValue("master:orientation", {"left"}); m_pConfig->addConfigValue("master:orientation", {"left"});
@ -519,10 +523,13 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("opengl:force_introspection", Hyprlang::INT{2}); m_pConfig->addConfigValue("opengl:force_introspection", Hyprlang::INT{2});
m_pConfig->addConfigValue("cursor:no_hardware_cursors", Hyprlang::INT{0}); 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:hotspot_padding", Hyprlang::INT{0});
m_pConfig->addConfigValue("cursor:inactive_timeout", 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:no_warps", Hyprlang::INT{0});
m_pConfig->addConfigValue("cursor:persistent_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:default_monitor", {STRVAL_EMPTY});
m_pConfig->addConfigValue("cursor:zoom_factor", {1.f}); m_pConfig->addConfigValue("cursor:zoom_factor", {1.f});
m_pConfig->addConfigValue("cursor:zoom_rigid", Hyprlang::INT{0}); 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> // bind[fl]=SUPER,G,exec,dmenu_run <args>
// flags // flags
bool locked = false; bool locked = false;
bool release = false; bool release = false;
bool repeat = false; bool repeat = false;
bool mouse = false; bool mouse = false;
bool nonConsuming = false; bool nonConsuming = false;
bool transparent = false; bool transparent = false;
bool ignoreMods = false; bool ignoreMods = false;
bool multiKey = false; bool multiKey = false;
const auto BINDARGS = command.substr(4); bool hasDescription = false;
const auto BINDARGS = command.substr(4);
for (auto& arg : BINDARGS) { for (auto& arg : BINDARGS) {
if (arg == 'l') { if (arg == 'l') {
@ -1982,6 +1990,8 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
ignoreMods = true; ignoreMods = true;
} else if (arg == 's') { } else if (arg == 's') {
multiKey = true; multiKey = true;
} else if (arg == 'd') {
hasDescription = true;
} else { } else {
return "bind: invalid flag"; return "bind: invalid flag";
} }
@ -1993,11 +2003,13 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
if (mouse && (repeat || release || locked)) if (mouse && (repeat || release || locked))
return "flag m is exclusive"; 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)) if ((ARGS.size() < 3 && !mouse) || (ARGS.size() < 3 && mouse))
return "bind: too few args"; 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"; return "bind: too many args";
std::set<xkb_keysym_t> KEYSYMS; 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]; 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) if (mouse)
HANDLER = "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."; 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, g_pKeybindManager->addKeybind(SKeybind{parsedKey.key, KEYSYMS, parsedKey.keycode, parsedKey.catchAll, MOD, MODS, HANDLER, COMMAND, locked, m_szCurrentSubmap, DESCRIPTION,
repeat, mouse, nonConsuming, transparent, ignoreMods, multiKey}); release, repeat, mouse, nonConsuming, transparent, ignoreMods, multiKey, hasDescription});
} }
return {}; 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) { 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 RULE = trim(value.substr(0, value.find_first_of(',')));
const auto VALUE = removeBeginEndSpacesTabs(value.substr(value.find_first_of(',') + 1)); const auto VALUE = trim(value.substr(value.find_first_of(',') + 1));
// check rule and value // check rule and value
if (RULE.empty() || VALUE.empty()) 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) { 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 RULE = trim(value.substr(0, value.find_first_of(',')));
const auto VALUE = removeBeginEndSpacesTabs(value.substr(value.find_first_of(',') + 1)); const auto VALUE = trim(value.substr(value.find_first_of(',') + 1));
// check rule and value // check rule and value
if (RULE.empty() || VALUE.empty()) 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) { 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); const auto VALUE = value.substr(value.find_first_of(',') + 1);
if (!windowRuleValid(RULE) && RULE != "unset") { 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 = result.substr(0, min - pos);
result = removeBeginEndSpacesTabs(result); result = trim(result);
if (!result.empty() && result.back() == ',') if (!result.empty() && result.back() == ',')
result.pop_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) { std::optional<std::string> CConfigManager::handleBlurLS(const std::string& command, const std::string& value) {
if (value.starts_with("remove,")) { 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; })) if (std::erase_if(m_dBlurLSNamespaces, [&](const auto& other) { return other == TOREMOVE; }))
updateBlurredLS(TOREMOVE, false); updateBlurredLS(TOREMOVE, false);
return {}; return {};
@ -2358,7 +2372,7 @@ std::optional<std::string> CConfigManager::handleWorkspaceRules(const std::strin
const auto FIRST_DELIM = value.find_first_of(','); const auto FIRST_DELIM = value.find_first_of(',');
std::string name = ""; 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); int id = getWorkspaceIDFromString(first_ident, name);
auto rules = value.substr(FIRST_DELIM + 1); auto rules = value.substr(FIRST_DELIM + 1);

View File

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

View File

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

View File

@ -1,5 +1,6 @@
#include "HyprCtl.hpp" #include "HyprCtl.hpp"
#include <format>
#include <netinet/in.h> #include <netinet/in.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -16,6 +17,9 @@
#include <typeindex> #include <typeindex>
#include <numeric> #include <numeric>
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
#include "../config/ConfigDataValues.hpp" #include "../config/ConfigDataValues.hpp"
#include "../config/ConfigValue.hpp" #include "../config/ConfigValue.hpp"
#include "../managers/CursorManager.hpp" #include "../managers/CursorManager.hpp"
@ -24,7 +28,9 @@
#include "../devices/IKeyboard.hpp" #include "../devices/IKeyboard.hpp"
#include "../devices/ITouch.hpp" #include "../devices/ITouch.hpp"
#include "../devices/Tablet.hpp" #include "../devices/Tablet.hpp"
#include "debug/RollingLogFollow.hpp"
#include "config/ConfigManager.hpp" #include "config/ConfigManager.hpp"
#include "helpers/MiscFunctions.hpp"
static void trimTrailingComma(std::string& str) { static void trimTrailingComma(std::string& str) {
if (!str.empty() && str.back() == ',') if (!str.empty() && str.back() == ',')
@ -789,9 +795,11 @@ std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) {
ret += "e"; ret += "e";
if (kb.nonConsuming) if (kb.nonConsuming)
ret += "n"; 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, 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.keycode, kb.catchAll, kb.handler, kb.arg); kb.key, kb.keycode, kb.catchAll, kb.description, kb.handler, kb.arg);
} }
} else { } else {
// json // json
@ -805,17 +813,19 @@ std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) {
"release": {}, "release": {},
"repeat": {}, "repeat": {},
"non_consuming": {}, "non_consuming": {},
"has_description": {},
"modmask": {}, "modmask": {},
"submap": "{}", "submap": "{}",
"key": "{}", "key": "{}",
"keycode": {}, "keycode": {},
"catch_all": {}, "catch_all": {},
"description": "{}",
"dispatcher": "{}", "dispatcher": "{}",
"arg": "{}" "arg": "{}"
}},)#", }},)#",
kb.locked ? "true" : "false", kb.mouse ? "true" : "false", kb.release ? "true" : "false", kb.repeat ? "true" : "false", kb.nonConsuming ? "true" : "false", 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), kb.hasDescription ? "true" : "false", kb.modmask, escapeJSONStrings(kb.submap), escapeJSONStrings(kb.key), kb.keycode, kb.catchAll ? "true" : "false",
escapeJSONStrings(kb.arg)); escapeJSONStrings(kb.description), escapeJSONStrings(kb.handler), escapeJSONStrings(kb.arg));
} }
trimTrailingComma(ret); trimTrailingComma(ret);
ret += "]"; ret += "]";
@ -826,7 +836,7 @@ std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string versionRequest(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(), '#', ' '); std::replace(commitMsg.begin(), commitMsg.end(), '#', ' ');
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) { if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
@ -1051,7 +1061,7 @@ std::string dispatchBatch(eHyprCtlOutputFormat format, std::string request) {
request = ""; request = "";
} }
curitem = removeBeginEndSpacesTabs(curitem); curitem = trim(curitem);
}; };
nextItem(); nextItem();
@ -1305,7 +1315,7 @@ std::string dispatchGetOption(eHyprCtlOutputFormat format, std::string request)
request = ""; request = "";
} }
curitem = removeBeginEndSpacesTabs(curitem); curitem = trim(curitem);
}; };
nextItem(); nextItem();
@ -1723,6 +1733,46 @@ std::string CHyprCtl::makeDynamicCall(const std::string& input) {
return getReply(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) { int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
if (mask & WL_EVENT_ERROR || mask & WL_EVENT_HANGUP) if (mask & WL_EVENT_ERROR || mask & WL_EVENT_HANGUP)
return 0; return 0;
@ -1766,9 +1816,15 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
reply = "Err: " + std::string(e.what()); 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) if (g_pConfigManager->m_bWantsMonitorReload)
g_pConfigManager->ensureMonitorStatus(); g_pConfigManager->ensureMonitorStatus();

View File

@ -1,6 +1,7 @@
#include "Log.hpp" #include "Log.hpp"
#include "../defines.hpp" #include "../defines.hpp"
#include "../Compositor.hpp" #include "../Compositor.hpp"
#include "RollingLogFollow.hpp"
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
@ -73,6 +74,9 @@ void Debug::log(LogLevel level, std::string str) {
if (rollingLog.size() > ROLLING_LOG_SIZE) if (rollingLog.size() > ROLLING_LOG_SIZE)
rollingLog = rollingLog.substr(rollingLog.size() - ROLLING_LOG_SIZE); rollingLog = rollingLog.substr(rollingLog.size() - ROLLING_LOG_SIZE);
if (RollingLogFollow::Get().IsRunning())
RollingLogFollow::Get().AddLog(str);
if (!disableLogs || !**disableLogs) { if (!disableLogs || !**disableLogs) {
// log to a file // log to a file
std::ofstream ofs; 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() { void CLayerSurface::onMap() {
Debug::log(LOG, "LayerSurface {:x} mapped", (uintptr_t)layerSurface); Debug::log(LOG, "LayerSurface {:x} mapped", (uintptr_t)layerSurface);
mapped = true; mapped = true;
keyboardExclusive = layerSurface->current.interactivity; interactivity = layerSurface->current.interactivity;
// this layer might be re-mapped.
fadingOut = false;
g_pCompositor->removeFromFadingOutSafe(self.lock());
// fix if it changed its mon // fix if it changed its mon
const auto PMONITOR = g_pCompositor->getMonitorFromID(monitorID); const auto PMONITOR = g_pCompositor->getMonitorFromID(monitorID);
@ -133,12 +137,15 @@ void CLayerSurface::onMap() {
surface->resource()->enter(PMONITOR->self.lock()); 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); g_pInputManager->m_dExclusiveLSes.push_back(self);
const bool GRABSFOCUS = layerSurface->current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE && const bool GRABSFOCUS = ISEXCLUSIVE ||
// don't focus if constrained (layerSurface->current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE &&
(g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained()); // don't focus if constrained
(g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained()));
if (GRABSFOCUS) { if (GRABSFOCUS) {
// TODO: use the new superb really very cool grab // 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(); }); 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) { if (!g_pCompositor->getMonitorFromID(monitorID) || g_pCompositor->m_bUnsafeState) {
Debug::log(WARN, "Layersurface unmapping on invalid monitor (removed?) ignoring."); 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(); const bool WASLASTFOCUS = g_pCompositor->m_pLastFocus == surface->resource();
surface.reset();
if (!PMONITOR) if (!PMONITOR)
return; return;
// refocus if needed // refocus if needed
if (WASLASTFOCUS) { if (WASLASTFOCUS)
g_pInputManager->releaseAllMouseButtons(); g_pInputManager->refocusLastWindow(PMONITOR);
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();
}
}
CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height}; CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height};
g_pHyprRenderer->damageBox(&geomFixed); g_pHyprRenderer->damageBox(&geomFixed);
@ -246,12 +223,25 @@ void CLayerSurface::onUnmap() {
g_pHyprRenderer->damageBox(&geomFixed); g_pHyprRenderer->damageBox(&geomFixed);
g_pInputManager->sendMotionEventsToFocused(); g_pInputManager->sendMotionEventsToFocused();
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->ID);
} }
void CLayerSurface::onCommit() { void CLayerSurface::onCommit() {
if (!layerSurface) if (!layerSurface)
return; 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); const auto PMONITOR = g_pCompositor->getMonitorFromID(monitorID);
if (!PMONITOR) if (!PMONITOR)
@ -311,18 +301,35 @@ void CLayerSurface::onCommit() {
realSize.setValueAndWarp(geometry.size()); realSize.setValueAndWarp(geometry.size());
} }
if (layerSurface->current.interactivity && (g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained()) // don't focus if constrained if (mapped) {
&& !keyboardExclusive && mapped) { const bool WASLASTFOCUS = g_pCompositor->m_pLastFocus == surface->resource();
g_pCompositor->focusSurface(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); if (!WASEXCLUSIVE && ISEXCLUSIVE)
g_pSeatManager->setPointerFocus(surface->resource(), LOCAL); g_pInputManager->m_dExclusiveLSes.push_back(self);
g_pInputManager->m_bEmptyFocusCursorSet = false; else if (WASEXCLUSIVE && !ISEXCLUSIVE)
} else if (!layerSurface->current.interactivity && (g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained()) && keyboardExclusive) { std::erase_if(g_pInputManager->m_dExclusiveLSes, [this](const auto& other) { return !other.lock() || other.lock() == self.lock(); });
g_pInputManager->refocus();
// 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); g_pHyprRenderer->damageSurface(surface->resource(), position.x, position.y);
@ -512,4 +519,4 @@ int CLayerSurface::popupsCount() {
int no = -1; // we have one dummy int no = -1; // we have one dummy
popupHead->breadthfirst([](CPopup* p, void* data) { *(int*)data += 1; }, &no); popupHead->breadthfirst([](CPopup* p, void* data) { *(int*)data += 1; }, &no);
return no; return no;
} }

View File

@ -34,40 +34,41 @@ class CLayerSurface {
WP<CLayerShellResource> layerSurface; WP<CLayerShellResource> layerSurface;
wl_list link; 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; bool mapped = false;
uint32_t layer = 0; uint32_t layer = 0;
int monitorID = -1; int monitorID = -1;
bool fadingOut = false; bool fadingOut = false;
bool readyToDelete = false; bool readyToDelete = false;
bool noProcess = false; bool noProcess = false;
bool noAnimations = false; bool noAnimations = false;
bool forceBlur = false; bool forceBlur = false;
bool forceBlurPopups = false; bool forceBlurPopups = false;
int xray = -1; int xray = -1;
bool ignoreAlpha = false; bool ignoreAlpha = false;
float ignoreAlphaValue = 0.f; float ignoreAlphaValue = 0.f;
bool dimAround = false; bool dimAround = false;
std::optional<std::string> animationStyle; std::optional<std::string> animationStyle;
PHLLSREF self; PHLLSREF self;
CBox geometry = {0, 0, 0, 0}; CBox geometry = {0, 0, 0, 0};
Vector2D position; Vector2D position;
std::string szNamespace = ""; std::string szNamespace = "";
std::unique_ptr<CPopup> popupHead; std::unique_ptr<CPopup> popupHead;
void onDestroy(); void onDestroy();
void onMap(); void onMap();
void onUnmap(); void onUnmap();
void onCommit(); void onCommit();
private: private:
struct { struct {
@ -83,4 +84,4 @@ class CLayerSurface {
bool operator==(const CLayerSurface& rhs) const { bool operator==(const CLayerSurface& rhs) const {
return layerSurface == rhs.layerSurface && monitorID == rhs.monitorID; return layerSurface == rhs.layerSurface && monitorID == rhs.monitorID;
} }
}; };

View File

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

View File

@ -12,6 +12,9 @@
#include "../protocols/core/Compositor.hpp" #include "../protocols/core/Compositor.hpp"
#include "../xwayland/XWayland.hpp" #include "../xwayland/XWayland.hpp"
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
PHLWINDOW CWindow::create(SP<CXWaylandSurface> surface) { PHLWINDOW CWindow::create(SP<CXWaylandSurface> surface) {
PHLWINDOW pWindow = SP<CWindow>(new CWindow(surface)); PHLWINDOW pWindow = SP<CWindow>(new CWindow(surface));
@ -687,7 +690,7 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
CGradientValueData activeBorderGradient = {}; CGradientValueData activeBorderGradient = {};
CGradientValueData inactiveBorderGradient = {}; CGradientValueData inactiveBorderGradient = {};
bool active = true; 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 // Basic form has only two colors, everything else can be parsed as a gradient
if (colorsAndAngles.size() == 2 && !colorsAndAngles[1].contains("deg")) { if (colorsAndAngles.size() == 2 && !colorsAndAngles[1].contains("deg")) {
@ -1537,3 +1540,58 @@ void CWindow::warpCursor() {
else else
g_pCompositor->warpCursorTo(middle()); 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 fetchTitle();
std::string fetchClass(); std::string fetchClass();
void warpCursor(); void warpCursor();
PHLWINDOW getSwallower();
// listeners // listeners
void onAck(uint32_t serial); void onAck(uint32_t serial);

View File

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

View File

@ -1,6 +1,6 @@
#include "IKeyboard.hpp" #include "IKeyboard.hpp"
#include "../defines.hpp" #include "../defines.hpp"
#include "../helpers/VarList.hpp" #include "../helpers/varlist/VarList.hpp"
#include "../managers/input/InputManager.hpp" #include "../managers/input/InputManager.hpp"
uint32_t IKeyboard::getCapabilities() { 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); 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->onDisconnect(true);
pMonitor->output = nullptr; pMonitor->output = nullptr;

View File

@ -12,6 +12,9 @@
#include "../protocols/core/Compositor.hpp" #include "../protocols/core/Compositor.hpp"
#include "../xwayland/XSurface.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 PDIMSTRENGTH = CConfigValue<Hyprlang::FLOAT>("decoration:dim_strength");
static auto PSWALLOW = CConfigValue<Hyprlang::INT>("misc:enable_swallow"); static auto PSWALLOW = CConfigValue<Hyprlang::INT>("misc:enable_swallow");
static auto PSWALLOWREGEX = CConfigValue<std::string>("misc:swallow_regex"); 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 PNEWTAKESOVERFS = CConfigValue<Hyprlang::INT>("misc:new_window_takes_over_fullscreen");
static auto PINITIALWSTRACKING = CConfigValue<Hyprlang::INT>("misc:initial_workspace_tracking"); 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) { for (auto& r : PWINDOW->m_vMatchedRules) {
if (r.szRule.starts_with("monitor")) { if (r.szRule.starts_with("monitor")) {
try { try {
const auto MONITORSTR = removeBeginEndSpacesTabs(r.szRule.substr(r.szRule.find(' '))); const auto MONITORSTR = trim(r.szRule.substr(r.szRule.find(' ')));
if (MONITORSTR == "unset") { if (MONITORSTR == "unset") {
PWINDOW->m_iMonitorID = PMONITOR->ID; PWINDOW->m_iMonitorID = PMONITOR->ID;
@ -235,7 +237,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
continue; continue;
// `group` is a shorthand of `group set` // `group` is a shorthand of `group set`
if (removeBeginEndSpacesTabs(r.szRule) == "group") { if (trim(r.szRule) == "group") {
PWINDOW->m_eGroupRules |= GROUP_SET; PWINDOW->m_eGroupRules |= GROUP_SET;
continue; continue;
} }
@ -524,71 +526,17 @@ void Events::listener_mapWindow(void* owner, void* data) {
// verify swallowing // verify swallowing
if (*PSWALLOW && std::string{*PSWALLOWREGEX} != STRVAL_EMPTY) { if (*PSWALLOW && std::string{*PSWALLOWREGEX} != STRVAL_EMPTY) {
// don't swallow ourselves const auto SWALLOWER = PWINDOW->getSwallower();
std::regex rgx(*PSWALLOWREGEX);
if (!std::regex_match(PWINDOW->m_szClass, rgx)) {
// check parent
int ppid = getPPIDof(PWINDOW->getPID());
int curppid = 0; if (SWALLOWER) {
// swallow
PWINDOW->m_pSwallowed = SWALLOWER;
for (int i = 0; i < 5; ++i) { g_pLayoutManager->getCurrentLayout()->onWindowRemoved(SWALLOWER);
curppid = getPPIDof(ppid);
if (curppid < 10) { SWALLOWER->setHidden(true);
break;
}
ppid = curppid; g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->m_iMonitorID);
}
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);
}
}
}
} }
} }
@ -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 // tearing: if solitary, redraw it. This still might be a single surface window
const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); 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) { 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 (!damageBox.empty()) {
if (PMONITOR->tearingState.busy) { if (PMONITOR->tearingState.busy) {

View File

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

View File

@ -13,6 +13,8 @@
#ifdef HAS_EXECINFO #ifdef HAS_EXECINFO
#include <execinfo.h> #include <execinfo.h>
#endif #endif
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
#include <sys/sysctl.h> #include <sys/sysctl.h>
@ -195,25 +197,6 @@ std::string escapeJSONStrings(const std::string& str) {
return oss.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) { std::optional<float> getPlusMinusKeywordResult(std::string source, float relative) {
try { try {
return relative + stof(source); 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) { bool isDirection(const std::string& arg) {
return arg == "l" || arg == "r" || arg == "u" || arg == "d" || arg == "t" || arg == "b"; 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::optional<std::string> cleanCmdForWorkspace(const std::string& inWorkspaceName, std::string dirtyCmd) {
std::string cmd = removeBeginEndSpacesTabs(dirtyCmd); std::string cmd = trim(dirtyCmd);
if (!cmd.empty()) { if (!cmd.empty()) {
std::string rules; 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 // Execute a shell command and get the output
std::string execAndGet(const char* cmd) { std::string execAndGet(const char* cmd) {
std::array<char, 128> buffer; std::array<char, 128> buffer;
std::string result; std::string result;
const std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose); using PcloseType = int (*)(FILE*);
const std::unique_ptr<FILE, PcloseType> pipe(popen(cmd, "r"), static_cast<PcloseType>(pclose));
if (!pipe) { if (!pipe) {
Debug::log(ERR, "execAndGet: failed in pipe"); Debug::log(ERR, "execAndGet: failed in pipe");
return ""; return "";
@ -748,7 +707,7 @@ int64_t configStringToInt(const std::string& VALUE) {
} else if (VALUE.starts_with("rgba(") && VALUE.ends_with(')')) { } else if (VALUE.starts_with("rgba(") && VALUE.ends_with(')')) {
const auto VALUEWITHOUTFUNC = VALUE.substr(5, VALUE.length() - 6); 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()); Debug::log(WARN, "invalid length {} for rgba", VALUEWITHOUTFUNC.length());
throw std::invalid_argument("rgba() expects length of 8 characters (4 bytes)"); 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(')')) { } else if (VALUE.starts_with("rgb(") && VALUE.ends_with(')')) {
const auto VALUEWITHOUTFUNC = VALUE.substr(4, VALUE.length() - 5); 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()); Debug::log(WARN, "invalid length {} for rgb", VALUEWITHOUTFUNC.length());
throw std::invalid_argument("rgb() expects length of 6 characters (3 bytes)"); throw std::invalid_argument("rgb() expects length of 6 characters (3 bytes)");
} }
@ -822,15 +781,6 @@ double normalizeAngleRad(double ang) {
return 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> getBacktrace() {
std::vector<SCallstackFrameInfo> callstack; std::vector<SCallstackFrameInfo> callstack;
@ -865,7 +815,8 @@ bool envEnabled(const std::string& env) {
} }
std::pair<int, std::string> openExclusiveShm() { 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) { for (size_t i = 0; i < 69; ++i) {
int fd = shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600); 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 addWLSignal(wl_signal*, wl_listener*, void* pOwner, const std::string& ownerString);
void removeWLSignal(wl_listener*); void removeWLSignal(wl_listener*);
std::string escapeJSONStrings(const std::string& str); 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 std::string&);
bool isDirection(const char&); bool isDirection(const char&);
int getWorkspaceIDFromString(const std::string&, std::string&); 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); std::optional<float> getPlusMinusKeywordResult(std::string in, float relative);
void matrixProjection(float mat[9], int w, int h, wl_output_transform tr); void matrixProjection(float mat[9], int w, int h, wl_output_transform tr);
double normalizeAngleRad(double ang); double normalizeAngleRad(double ang);
std::string replaceInString(std::string subject, const std::string& search, const std::string& replace);
std::vector<SCallstackFrameInfo> getBacktrace(); std::vector<SCallstackFrameInfo> getBacktrace();
void throwError(const std::string& err); void throwError(const std::string& err);
bool envEnabled(const std::string& env); bool envEnabled(const std::string& env);

View File

@ -6,6 +6,8 @@
#include "../devices/ITouch.hpp" #include "../devices/ITouch.hpp"
#include "../protocols/LayerShell.hpp" #include "../protocols/LayerShell.hpp"
#include "../protocols/PresentationTime.hpp" #include "../protocols/PresentationTime.hpp"
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
int ratHandler(void* data) { int ratHandler(void* data) {
g_pHyprRenderer->renderMonitor((CMonitor*)data); g_pHyprRenderer->renderMonitor((CMonitor*)data);
@ -71,8 +73,7 @@ void CMonitor::onConnect(bool noRule) {
std::erase(szDescription, ','); std::erase(szDescription, ',');
// field is backwards-compatible with intended usage of `szDescription` but excludes the parenthesized DRM node name suffix // field is backwards-compatible with intended usage of `szDescription` but excludes the parenthesized DRM node name suffix
szShortDescription = szShortDescription = trim(std::format("{} {} {}", output->make ? output->make : "", output->model ? output->model : "", output->serial ? output->serial : ""));
removeBeginEndSpacesTabs(std::format("{} {} {}", output->make ? output->make : "", output->model ? output->model : "", output->serial ? output->serial : ""));
std::erase(szShortDescription, ','); std::erase(szShortDescription, ',');
if (!wlr_backend_is_drm(output->backend)) if (!wlr_backend_is_drm(output->backend))
@ -361,6 +362,24 @@ void CMonitor::addDamage(const CBox* box) {
g_pCompositor->scheduleFrameForMonitor(this); 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() { bool CMonitor::isMirror() {
return pMirrorOf != nullptr; return pMirrorOf != nullptr;
} }

View File

@ -158,6 +158,7 @@ class CMonitor {
void addDamage(const pixman_region32_t* rg); void addDamage(const pixman_region32_t* rg);
void addDamage(const CRegion* rg); void addDamage(const CRegion* rg);
void addDamage(const CBox* box); void addDamage(const CBox* box);
bool shouldSkipScheduleFrameOnMouseEvent();
void setMirror(const std::string&); void setMirror(const std::string&);
bool isMirror(); bool isMirror();
bool matchesStaticSelector(const std::string& selector) const; 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 "../desktop/Popup.hpp"
#include "AnimatedVariable.hpp" #include "AnimatedVariable.hpp"
#include "../desktop/WLSurface.hpp" #include "../desktop/WLSurface.hpp"
#include "signal/Listener.hpp" #include "signal/Signal.hpp"
#include "Region.hpp" #include "Region.hpp"
class CMonitor; 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 #pragma once
#include <functional> #include <hyprutils/signal/Signal.hpp>
#include <any>
#include <vector>
#include <memory>
#include "Listener.hpp" using namespace Hyprutils::Signal;
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;
};

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) if (pWindow->m_bIsFloating)
return; 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 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->workspaceID = pWindow->workspaceID();
PNODE->pWindow = pWindow; PNODE->pWindow = pWindow;
static auto PNEWISMASTER = CConfigValue<Hyprlang::INT>("master:new_is_master");
const auto WINDOWSONWORKSPACE = getNodesOnWorkspace(PNODE->workspaceID); const auto WINDOWSONWORKSPACE = getNodesOnWorkspace(PNODE->workspaceID);
static auto PMFACT = CConfigValue<Hyprlang::FLOAT>("master:mfact"); static auto PMFACT = CConfigValue<Hyprlang::FLOAT>("master:mfact");
float lastSplitPercent = *PMFACT; 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) || if ((BNEWISMASTER && g_pInputManager->dragMode != MBIND_MOVE) //
forceDropAsMaster) { || WINDOWSONWORKSPACE == 1 //
for (auto& nd : m_lMasterNodesData) { || (WINDOWSONWORKSPACE > 2 && !pWindow->m_bFirstMap && OPENINGON->isMaster) //
if (nd.isMaster && nd.workspaceID == PNODE->workspaceID) { || forceDropAsMaster //
nd.isMaster = false; || (*PNEWSTATUS == "inherit" && OPENINGON && OPENINGON->isMaster && g_pInputManager->dragMode != MBIND_MOVE)) {
lastSplitPercent = nd.percMaster;
break; 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() { 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) if (!g_pCompositor->m_pLastMonitor)
return {}; return {};
@ -1454,7 +1482,7 @@ Vector2D CHyprMasterLayout::predictSizeForNewWindowTiled() {
if (!MASTER) // wtf if (!MASTER) // wtf
return {}; return {};
if (*PNEWISMASTER) { if (*PNEWSTATUS == "master") {
return MASTER->size; return MASTER->size;
} else { } else {
const auto SLAVES = NODES - getMastersOnWorkspace(g_pCompositor->m_pLastMonitor->activeWorkspace->m_iID); const auto SLAVES = NODES - getMastersOnWorkspace(g_pCompositor->m_pLastMonitor->activeWorkspace->m_iID);

View File

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

View File

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

View File

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

View File

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

View File

@ -14,23 +14,25 @@ class CPluginSystem;
class IKeyboard; class IKeyboard;
struct SKeybind { struct SKeybind {
std::string key = ""; std::string key = "";
std::set<xkb_keysym_t> sMkKeys = {}; std::set<xkb_keysym_t> sMkKeys = {};
uint32_t keycode = 0; uint32_t keycode = 0;
bool catchAll = false; bool catchAll = false;
uint32_t modmask = 0; uint32_t modmask = 0;
std::set<xkb_keysym_t> sMkMods = {}; std::set<xkb_keysym_t> sMkMods = {};
std::string handler = ""; std::string handler = "";
std::string arg = ""; std::string arg = "";
bool locked = false; bool locked = false;
std::string submap = ""; std::string submap = "";
bool release = false; std::string description = "";
bool repeat = false; bool release = false;
bool mouse = false; bool repeat = false;
bool nonConsuming = false; bool mouse = false;
bool transparent = false; bool nonConsuming = false;
bool ignoreMods = false; bool transparent = false;
bool multiKey = false; bool ignoreMods = false;
bool multiKey = false;
bool hasDescription = false;
// DO NOT INITIALIZE // DO NOT INITIALIZE
bool shadowed = false; bool shadowed = false;
@ -203,6 +205,7 @@ class CKeybindManager {
static void setIgnoreGroupLock(std::string); static void setIgnoreGroupLock(std::string);
static void denyWindowFromGroup(std::string); static void denyWindowFromGroup(std::string);
static void global(std::string); static void global(std::string);
static void event(std::string);
friend class CCompositor; friend class CCompositor;
friend class CInputManager; 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) { void CPointerManager::lockSoftwareForMonitor(SP<CMonitor> mon) {
auto state = stateFor(mon); auto state = stateFor(mon);
state->softwareLocks++; state->softwareLocks++;
@ -661,7 +675,7 @@ void CPointerManager::damageIfSoftware() {
continue; continue;
if ((mw->softwareLocks > 0 || mw->hardwareFailed || *PNOHW) && b.overlaps({mw->monitor->vecPosition, mw->monitor->vecSize})) { 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; break;
} }
} }

View File

@ -43,6 +43,8 @@ class CPointerManager {
void lockSoftwareForMonitor(SP<CMonitor> pMonitor); void lockSoftwareForMonitor(SP<CMonitor> pMonitor);
void unlockSoftwareForMonitor(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 */); 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/TextInputV1.hpp"
#include "../protocols/GlobalShortcuts.hpp" #include "../protocols/GlobalShortcuts.hpp"
#include "../protocols/Screencopy.hpp" #include "../protocols/Screencopy.hpp"
#include "../helpers/memory/WeakPtr.hpp" #include "../helpers/memory/Memory.hpp"
#include "../helpers/signal/Listener.hpp" #include "../helpers/signal/Signal.hpp"
#include <unordered_map> #include <unordered_map>
class CProtocolManager { class CProtocolManager {

View File

@ -94,7 +94,7 @@ void CSeatManager::setKeyboard(SP<IKeyboard> KEEB) {
} }
void CSeatManager::updateActiveKeyboardData() { void CSeatManager::updateActiveKeyboardData() {
if (keyboard) if (keyboard && keyboard->wlr())
PROTO::seat->updateRepeatInfo(keyboard->wlr()->repeat_info.rate, keyboard->wlr()->repeat_info.delay); PROTO::seat->updateRepeatInfo(keyboard->wlr()->repeat_info.rate, keyboard->wlr()->repeat_info.delay);
PROTO::seat->updateKeymap(); PROTO::seat->updateKeymap();
} }
@ -191,6 +191,11 @@ void CSeatManager::setPointerFocus(SP<CWLSurfaceResource> surf, const Vector2D&
if (state.pointerFocus == surf) if (state.pointerFocus == surf)
return; return;
if (PROTO::data->dndActive() && surf) {
Debug::log(LOG, "[seatmgr] Refusing pointer focus during an active dnd");
return;
}
if (!mouse || !mouse->wlr()) { if (!mouse || !mouse->wlr()) {
Debug::log(ERR, "BUG THIS: setPointerFocus without a valid mouse set"); Debug::log(ERR, "BUG THIS: setPointerFocus without a valid mouse set");
return; 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) { void CSeatManager::sendTouchDown(SP<CWLSurfaceResource> surf, uint32_t timeMs, int32_t id, const Vector2D& local) {
if (state.touchFocus == surf)
return;
listeners.touchSurfaceDestroy.reset(); 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.touchFocusResource.reset();
state.touchFocus = surf; state.touchFocus = surf;
if (!surf) {
events.touchFocusChange.emit();
return;
}
auto client = surf->client(); auto client = surf->client();
for (auto& r : seatResources | std::views::reverse) { for (auto& r : seatResources | std::views::reverse) {
if (r->resource->client() != client) 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); }); 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) { 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) { void CSeatManager::sendTouchMotion(uint32_t timeMs, int32_t id, const Vector2D& local) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -155,6 +155,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
Vector2D surfacePos = Vector2D(-1337, -1337); Vector2D surfacePos = Vector2D(-1337, -1337);
PHLWINDOW pFoundWindow; PHLWINDOW pFoundWindow;
PHLLS pFoundLayerSurface; PHLLS pFoundLayerSurface;
SSessionLockSurface* pSessionLock = nullptr;
if (!g_pCompositor->m_bReadyToProcess || g_pCompositor->m_bIsShuttingDown || g_pCompositor->m_bUnsafeState) if (!g_pCompositor->m_bReadyToProcess || g_pCompositor->m_bIsShuttingDown || g_pCompositor->m_bUnsafeState)
return; return;
@ -186,7 +187,9 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
if (*PZOOMFACTOR != 1.f) if (*PZOOMFACTOR != 1.f)
g_pHyprRenderer->damageMonitor(PMONITOR); 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); g_pCompositor->scheduleFrameForMonitor(PMONITOR);
PHLWINDOW forcedFocus = m_pForcedFocus.lock(); PHLWINDOW forcedFocus = m_pForcedFocus.lock();
@ -261,12 +264,12 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
g_pCompositor->setActiveMonitor(PMONITOR); g_pCompositor->setActiveMonitor(PMONITOR);
if (g_pSessionLockManager->isSessionLocked()) { 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; return;
foundSurface = PSLS->surface->surface(); foundSurface = pSessionLock->surface->surface();
surfacePos = PMONITOR->vecPosition; surfacePos = PMONITOR->vecPosition;
} }
@ -350,6 +353,10 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
if (pFoundWindow) { if (pFoundWindow) {
if (!pFoundWindow->m_bIsX11) { if (!pFoundWindow->m_bIsX11) {
foundSurface = g_pCompositor->vectorWindowToSurface(mouseCoords, pFoundWindow, surfaceCoords); foundSurface = g_pCompositor->vectorWindowToSurface(mouseCoords, pFoundWindow, surfaceCoords);
if (!foundSurface) {
foundSurface = pFoundWindow->m_pWLSurface->resource();
surfacePos = pFoundWindow->m_vRealPosition.value();
}
} else { } else {
foundSurface = pFoundWindow->m_pWLSurface->resource(); foundSurface = pFoundWindow->m_pWLSurface->resource();
surfacePos = pFoundWindow->m_vRealPosition.value(); surfacePos = pFoundWindow->m_vRealPosition.value();
@ -365,7 +372,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
foundSurface = foundSurface =
g_pCompositor->vectorToLayerSurface(mouseCoords, &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &surfaceCoords, &pFoundLayerSurface); 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()); g_pCompositor->scheduleFrameForMonitor(g_pCompositor->m_pLastMonitor.get());
// grabs // grabs
@ -455,7 +462,9 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
restoreCursorIconToApp(); restoreCursorIconToApp();
} }
if (pFoundWindow) { if (pSessionLock != nullptr)
g_pCompositor->focusSurface(foundSurface);
else if (pFoundWindow) {
// change cursor icon if hovering over border // change cursor icon if hovering over border
if (*PRESIZEONBORDER && *PRESIZECURSORICON) { if (*PRESIZEONBORDER && *PRESIZECURSORICON) {
if (!pFoundWindow->m_bIsFullscreen && !pFoundWindow->hasPopupAt(mouseCoords)) { 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 && 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); 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; }); std::erase_if(m_vKeyboards, [pKeyboard](const auto& other) { return other == pKeyboard; });
if (m_vKeyboards.size() > 0) { if (m_vKeyboards.size() > 0) {
const auto PNEWKEYBOARD = m_vKeyboards.back(); bool found = false;
g_pSeatManager->setKeyboard(PNEWKEYBOARD); for (auto& k : m_vKeyboards | std::views::reverse) {
PNEWKEYBOARD->active = true; if (!k->wlr())
} else { continue;
g_pSeatManager->setKeyboard(k);
found = true;
break;
}
if (!found)
g_pSeatManager->setKeyboard(nullptr);
} else
g_pSeatManager->setKeyboard(nullptr); g_pSeatManager->setKeyboard(nullptr);
}
removeFromHIDs(pKeyboard); removeFromHIDs(pKeyboard);
} }
@ -1381,6 +1398,41 @@ void CInputManager::refocus() {
mouseMoveUnified(0, true); 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() { void CInputManager::unconstrainMouse() {
if (g_pSeatManager->mouse.expired()) if (g_pSeatManager->mouse.expired())
return; return;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -81,7 +81,7 @@ SP<CInputMethodV2> CInputMethodKeyboardGrabV2::getOwner() {
} }
wl_client* CInputMethodKeyboardGrabV2::client() { 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_) { 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); LOGM(LOG, "New IME with resource id {}", id);
events.newIME.emit(RESOURCE); events.newIME.emit(RESOURCE);
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -136,10 +136,12 @@ CXDGToplevelResource::CXDGToplevelResource(SP<CXdgToplevel> resource_, SP<CXDGSu
wl_array_release(&arr); wl_array_release(&arr);
} }
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_LEFT); if (resource->version() >= 2) {
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_RIGHT); pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_LEFT);
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_TOP); pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_RIGHT);
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_BOTTOM); 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) { resource->setSetTitle([this](CXdgToplevel* r, const char* t) {
state.title = t; state.title = t;
@ -261,6 +263,9 @@ uint32_t CXDGToplevelResource::setActive(bool active) {
} }
uint32_t CXDGToplevelResource::setSuspeneded(bool sus) { 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(); bool set = std::find(pendingApply.states.begin(), pendingApply.states.end(), XDG_TOPLEVEL_STATE_SUSPENDED) != pendingApply.states.end();
if (sus == set) if (sus == set)
@ -507,12 +512,12 @@ CXDGPositionerRules::CXDGPositionerRules(SP<CXDGPositionerResource> positioner)
state = positioner->state; state = positioner->state;
} }
static Vector2D pointForAnchor(const CBox& box, xdgPositionerAnchor anchor) { static Vector2D pointForAnchor(const CBox& box, const Vector2D& predictionSize, xdgPositionerAnchor anchor) {
switch (anchor) { switch (anchor) {
case XDG_POSITIONER_ANCHOR_TOP: return box.pos() + Vector2D{box.size().x / 2.F, 0}; 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, box.size().y}; 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}; 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}; 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_TOP_LEFT: return box.pos();
case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: return box.pos() + Vector2D{0, box.size().y}; 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}; 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); 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); bool success = predictedBox.inside(constraint);

View File

@ -13,13 +13,17 @@ CWLDataOfferResource::CWLDataOfferResource(SP<CWlDataOffer> resource_, SP<IDataS
return; return;
resource->setDestroy([this](CWlDataOffer* r) { resource->setDestroy([this](CWlDataOffer* r) {
if (!dead) if (!dead && (recvd || accepted))
PROTO::data->completeDrag(); PROTO::data->completeDrag();
else
PROTO::data->abortDrag();
PROTO::data->destroyResource(this); PROTO::data->destroyResource(this);
}); });
resource->setOnDestroy([this](CWlDataOffer* r) { resource->setOnDestroy([this](CWlDataOffer* r) {
if (!dead) if (!dead && (recvd || accepted))
PROTO::data->completeDrag(); PROTO::data->completeDrag();
else
PROTO::data->abortDrag();
PROTO::data->destroyResource(this); PROTO::data->destroyResource(this);
}); });
@ -85,8 +89,10 @@ void CWLDataOfferResource::sendData() {
if (!source) if (!source)
return; return;
resource->sendSourceActions(7); if (resource->version() >= 3) {
resource->sendAction(WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE); resource->sendSourceActions(7);
resource->sendAction(WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE);
}
for (auto& m : source->mimes()) { for (auto& m : source->mimes()) {
LOGM(LOG, " | offer {:x} supports mime {}", (uintptr_t)this, m); 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 // make a new offer, etc
updateDrag(); updateDrag();
} }
@ -590,15 +601,35 @@ void CWLDataDeviceProtocol::dropDrag() {
return; return;
} }
dnd.currentSource->sendDndDropPerformed(); if (!wasDragSuccessful()) {
abortDrag();
return;
}
dnd.focusedDevice->sendDrop(); dnd.focusedDevice->sendDrop();
dnd.focusedDevice->sendLeave();
resetDndState(); resetDndState();
if (dnd.overriddenCursor) if (dnd.overriddenCursor)
g_pInputManager->unsetCursorImage(); g_pInputManager->unsetCursorImage();
dnd.overriddenCursor = false; 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() { void CWLDataDeviceProtocol::completeDrag() {
@ -607,12 +638,13 @@ void CWLDataDeviceProtocol::completeDrag() {
if (!dnd.focusedDevice || !dnd.currentSource) if (!dnd.focusedDevice || !dnd.currentSource)
return; return;
dnd.currentSource->sendDndDropPerformed();
dnd.currentSource->sendDndFinished(); dnd.currentSource->sendDndFinished();
dnd.focusedDevice.reset(); dnd.focusedDevice.reset();
dnd.currentSource.reset(); dnd.currentSource.reset();
g_pSeatManager->resendEnterEvents(); g_pInputManager->simulateMouseMovement();
} }
void CWLDataDeviceProtocol::abortDrag() { void CWLDataDeviceProtocol::abortDrag() {
@ -631,7 +663,7 @@ void CWLDataDeviceProtocol::abortDrag() {
dnd.focusedDevice.reset(); dnd.focusedDevice.reset();
dnd.currentSource.reset(); dnd.currentSource.reset();
g_pSeatManager->resendEnterEvents(); g_pInputManager->simulateMouseMovement();
} }
void CWLDataDeviceProtocol::renderDND(CMonitor* pMonitor, timespec* when) { void CWLDataDeviceProtocol::renderDND(CMonitor* pMonitor, timespec* when) {
@ -650,5 +682,5 @@ void CWLDataDeviceProtocol::renderDND(CMonitor* pMonitor, timespec* when) {
} }
bool CWLDataDeviceProtocol::dndActive() { 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 dropDrag();
void completeDrag(); void completeDrag();
void resetDndState(); void resetDndState();
bool wasDragSuccessful();
// //
SP<CWLDataDeviceResource> dataDeviceForClient(wl_client*); SP<CWLDataDeviceResource> dataDeviceForClient(wl_client*);

View File

@ -9,6 +9,9 @@ CWLOutputResource::CWLOutputResource(SP<CWlOutput> resource_, SP<CMonitor> pMoni
pClient = resource->client(); pClient = resource->client();
if (!monitor)
return;
resource->setOnDestroy([this](CWlOutput* r) { resource->setOnDestroy([this](CWlOutput* r) {
if (monitor && PROTO::outputs.contains(monitor->szName)) if (monitor && PROTO::outputs.contains(monitor->szName))
PROTO::outputs.at(monitor->szName)->destroyResource(this); 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) { 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())); const auto RESOURCE = m_vOutputs.emplace_back(makeShared<CWLOutputResource>(makeShared<CWlOutput>(client, ver, id), monitor.lock()));
if (!RESOURCE->good()) { if (!RESOURCE->good()) {

View File

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

View File

@ -25,37 +25,38 @@ void CWLTouchResource::sendDown(SP<CWLSurfaceResource> surface, uint32_t timeMs,
if (!owner) if (!owner)
return; return;
if (currentSurface) {
LOGM(WARN, "requested CWLTouchResource::sendDown without sendUp first.");
sendUp(timeMs, id);
}
ASSERT(surface->client() == owner->client()); ASSERT(surface->client() == owner->client());
currentSurface = surface; currentSurface = surface;
listeners.destroySurface = surface->events.destroy.registerListener([this, timeMs, id](std::any d) { sendUp(timeMs + 10 /* hack */, id); }); 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)); 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) { void CWLTouchResource::sendUp(uint32_t timeMs, int32_t id) {
if (!owner || !currentSurface) if (!owner)
return; return;
resource->sendUp(g_pSeatManager->nextSerial(owner.lock()), timeMs, id); resource->sendUp(g_pSeatManager->nextSerial(owner.lock()), timeMs, id);
currentSurface.reset(); fingers--;
listeners.destroySurface.reset(); if (fingers <= 0) {
currentSurface.reset();
listeners.destroySurface.reset();
fingers = 0;
}
} }
void CWLTouchResource::sendMotion(uint32_t timeMs, int32_t id, const Vector2D& local) { void CWLTouchResource::sendMotion(uint32_t timeMs, int32_t id, const Vector2D& local) {
if (!owner || !currentSurface) if (!owner)
return; return;
resource->sendMotion(timeMs, id, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y)); resource->sendMotion(timeMs, id, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y));
} }
void CWLTouchResource::sendFrame() { void CWLTouchResource::sendFrame() {
if (!owner || !currentSurface) if (!owner)
return; return;
resource->sendFrame(); 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}); 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() { bool CWLPointerResource::good() {
@ -199,10 +203,16 @@ CWLKeyboardResource::CWLKeyboardResource(SP<CWlKeyboard> resource_, SP<CWLSeatRe
resource->setRelease([this](CWlKeyboard* r) { PROTO::seat->destroyResource(this); }); resource->setRelease([this](CWlKeyboard* r) { PROTO::seat->destroyResource(this); });
resource->setOnDestroy([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"); if (!g_pSeatManager->keyboard) {
static auto DELAY = CConfigValue<Hyprlang::INT>("input:repeat_delay"); LOGM(ERR, "No keyboard on bound wl_keyboard??");
return;
}
sendKeymap(g_pSeatManager->keyboard.lock()); 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() { bool CWLKeyboardResource::good() {

View File

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

View File

@ -79,20 +79,20 @@ CHyprOpenGLImpl::CHyprOpenGLImpl() {
m_tGlobalTimer.reset(); 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 // TODO: return std::expected when clang supports it
if (!m_sExts.EXT_image_dma_buf_import_modifiers) if (!m_sExts.EXT_image_dma_buf_import_modifiers)
return {}; return std::nullopt;
EGLint len = 0; EGLint len = 0;
if (!m_sProc.eglQueryDmaBufModifiersEXT(wlr_egl_get_display(g_pCompositor->m_sWLREGL), format, 0, nullptr, nullptr, &len)) { 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"); Debug::log(ERR, "EGL: Failed to query mods");
return {}; return std::nullopt;
} }
if (len <= 0) 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<uint64_t> mods;
std::vector<EGLBoolean> external; 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); m_sProc.eglQueryDmaBufModifiersEXT(wlr_egl_get_display(g_pCompositor->m_sWLREGL), format, len, mods.data(), external.data(), &len);
std::vector<uint64_t> result; std::vector<uint64_t> result;
bool linearIsExternal = false;
for (size_t i = 0; i < mods.size(); ++i) { 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; continue;
}
result.push_back(mods.at(i)); 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; return result;
} }
@ -147,15 +155,19 @@ void CHyprOpenGLImpl::initDRMFormats() {
for (auto& fmt : formats) { for (auto& fmt : formats) {
std::vector<uint64_t> mods; std::vector<uint64_t> mods;
if (!DISABLE_MODS) if (!DISABLE_MODS) {
mods = getModsForFormat(fmt); auto ret = getModsForFormat(fmt);
else if (!ret.has_value())
continue;
mods = *ret;
} else
mods = {DRM_FORMAT_MOD_LINEAR}; mods = {DRM_FORMAT_MOD_LINEAR};
m_bHasModifiers = m_bHasModifiers || mods.size() > 0; m_bHasModifiers = m_bHasModifiers || mods.size() > 0;
if (mods.size() == 0) // EGL can always do implicit modifiers.
continue; mods.push_back(DRM_FORMAT_MOD_INVALID);
dmaFormats.push_back(SDRMFormat{ dmaFormats.push_back(SDRMFormat{
.format = fmt, .format = fmt,

View File

@ -238,7 +238,9 @@ class CHyprOpenGLImpl {
void createBGTextureForMonitor(CMonitor*); void createBGTextureForMonitor(CMonitor*);
void initShaders(); void initShaders();
void initDRMFormats(); 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 // returns the out FB, can be either Mirror or MirrorSwap
CFramebuffer* blurMainFramebufferWithDamage(float a, CRegion* damage); CFramebuffer* blurMainFramebufferWithDamage(float a, CRegion* damage);

View File

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

View File

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

View File

@ -119,15 +119,15 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a) {
g_pDecorationPositioner->repositionDeco(this); g_pDecorationPositioner->repositionDeco(this);
} }
int xoff = 0; float xoff = 0;
int yoff = 0; float yoff = 0;
for (int i = 0; i < barsToDraw; ++i) { for (int i = 0; i < barsToDraw; ++i) {
const auto WINDOWINDEX = *PSTACKED ? m_dwGroupMembers.size() - i - 1 : i; const auto WINDOWINDEX = *PSTACKED ? m_dwGroupMembers.size() - i - 1 : i;
CBox rect = {ASSIGNEDBOX.x + xoff - pMonitor->vecPosition.x + m_pWindow->m_vFloatingOffset.x, CBox rect = {ASSIGNEDBOX.x + floor(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, ASSIGNEDBOX.y + ASSIGNEDBOX.h - floor(yoff) - BAR_INDICATOR_HEIGHT - BAR_PADDING_OUTER_VERT - pMonitor->vecPosition.y + m_pWindow->m_vFloatingOffset.y,
BAR_INDICATOR_HEIGHT}; m_fBarWidth, BAR_INDICATOR_HEIGHT};
if (rect.width <= 0 || rect.height <= 0) if (rect.width <= 0 || rect.height <= 0)
break; break;
@ -151,8 +151,8 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a) {
color.a *= a; color.a *= a;
g_pHyprOpenGL->renderRect(&rect, color); g_pHyprOpenGL->renderRect(&rect, color);
rect = {ASSIGNEDBOX.x + xoff - pMonitor->vecPosition.x + m_pWindow->m_vFloatingOffset.x, rect = {ASSIGNEDBOX.x + floor(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, ASSIGNEDBOX.y + ASSIGNEDBOX.h - floor(yoff) - ONEBARHEIGHT - pMonitor->vecPosition.y + m_pWindow->m_vFloatingOffset.y, m_fBarWidth,
(*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0)}; (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0)};
rect.scale(pMonitor->scale); 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(), .emplace_back(std::make_unique<CTitleTex>(m_dwGroupMembers[WINDOWINDEX].lock(),
Vector2D{m_fBarWidth * pMonitor->scale, (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale}, pMonitor->scale)) Vector2D{m_fBarWidth * pMonitor->scale, (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale}, pMonitor->scale))
.get(); .get();
rect.y += ((rect.height - pTitleTex->textHeight) / 2.0) * pMonitor->scale; rect.y += (rect.height - pTitleTex->textHeight) / 2.0;
rect.height = (pTitleTex->textHeight) * pMonitor->scale; rect.height = pTitleTex->textHeight;
rect.width = pTitleTex->textWidth * pMonitor->scale; rect.width = pTitleTex->textWidth;
rect.x += m_fBarWidth / 2.0 - (pTitleTex->textWidth / 2.0) * pMonitor->scale; rect.x += (m_fBarWidth * pMonitor->scale) / 2.0 - (pTitleTex->textWidth / 2.0);
rect.round(); rect.round();
g_pHyprOpenGL->renderTexture(pTitleTex->tex, &rect, 1.f); 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); 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() { bool CXWaylandServer::tryOpenSockets() {
for (size_t i = 0; i <= 32; ++i) { for (size_t i = 0; i <= 32; ++i) {
auto LOCK = std::format("/tmp/.X{}-lock", 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) { if (int fd = open(LOCK.c_str(), O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0444); fd >= 0) {
// we managed to open the lock // we managed to open the lock
if (!openSockets(xFDs, i)) { if (!openSockets(xFDs, i)) {
std::filesystem::remove(LOCK); safeRemove(LOCK);
close(fd); close(fd);
continue; continue;
} }
@ -170,7 +178,7 @@ bool CXWaylandServer::tryOpenSockets() {
const auto PIDSTR = std::format("{}", getpid()); const auto PIDSTR = std::format("{}", getpid());
if (write(fd, PIDSTR.c_str(), PIDSTR.length()) != (long)PIDSTR.length()) { if (write(fd, PIDSTR.c_str(), PIDSTR.length()) != (long)PIDSTR.length()) {
std::filesystem::remove(LOCK); safeRemove(LOCK);
close(fd); close(fd);
continue; continue;
} }
@ -197,7 +205,7 @@ bool CXWaylandServer::tryOpenSockets() {
} catch (...) { continue; } } catch (...) { continue; }
if (kill(pid, 0) != 0 && errno == ESRCH) { if (kill(pid, 0) != 0 && errno == ESRCH) {
if (!std::filesystem::remove(LOCK)) if (!safeRemove(LOCK))
continue; continue;
i--; i--;
@ -228,7 +236,7 @@ CXWaylandServer::~CXWaylandServer() {
close(xFDs[1]); close(xFDs[1]);
auto LOCK = std::format("/tmp/.X{}-lock", display); auto LOCK = std::format("/tmp/.X{}-lock", display);
std::filesystem::remove(LOCK); safeRemove(LOCK);
std::string path; std::string path;
#ifdef __linux__ #ifdef __linux__
@ -236,7 +244,7 @@ CXWaylandServer::~CXWaylandServer() {
#else #else
path = std::format("/tmp/.X11-unix/X{}_", display); path = std::format("/tmp/.X11-unix/X{}_", display);
#endif #endif
std::filesystem::remove(path); safeRemove(path);
} }
void CXWaylandServer::die() { void CXWaylandServer::die() {

View File

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

View File

@ -248,11 +248,11 @@ void CXWM::readProp(SP<CXWaylandSurface> XSURF, uint32_t atom, xcb_get_property_
} }
} }
} else { } else {
Debug::log(LOG, "[xwm] Unhandled prop {} -> {}", atom, propName); Debug::log(TRACE, "[xwm] Unhandled prop {} -> {}", atom, propName);
return; 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) { 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"]) { } else if (e->type == HYPRATOMS["_NET_ACTIVE_WINDOW"]) {
XSURF->events.activate.emit(); XSURF->events.activate.emit();
} else { } else {
Debug::log(LOG, "[xwm] Unhandled message prop {} -> {}", e->type, propName); Debug::log(TRACE, "[xwm] Unhandled message prop {} -> {}", e->type, propName);
return; 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) { void CXWM::handleFocusIn(xcb_focus_in_event_t* e) {
@ -394,11 +394,16 @@ void CXWM::focusWindow(SP<CXWaylandSurface> surf) {
if (surf == focusedSurface) if (surf == focusedSurface)
return; return;
auto oldSurf = focusedSurface.lock();
focusedSurface = surf; focusedSurface = surf;
if (oldSurf) // send state to all surfaces, sometimes we might lose some
sendState(oldSurf); // that could still stick with the focused atom
for (auto& s : mappedSurfaces) {
if (!s)
continue;
sendState(s.lock());
}
if (!surf) { if (!surf) {
xcb_set_input_focus_checked(connection, XCB_INPUT_FOCUS_POINTER_ROOT, XCB_NONE, XCB_CURRENT_TIME); 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) { 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; SXSelection& sel = clipboard;
if (e->property == XCB_ATOM_NONE) { if (e->property == XCB_ATOM_NONE) {
if (sel.transfer) { if (sel.transfer) {
Debug::log(ERR, "[xwm] converting selection failed"); Debug::log(TRACE, "[xwm] converting selection failed");
sel.transfer.reset(); sel.transfer.reset();
} }
} else if (e->target == HYPRATOMS["TARGETS"]) { } else if (e->target == HYPRATOMS["TARGETS"]) {
if (!focusedSurface) { 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; return;
} }
@ -519,7 +524,7 @@ bool CXWM::handleSelectionPropertyNotify(xcb_property_notify_event_t* e) {
} }
void CXWM::handleSelectionRequest(xcb_selection_request_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); e->selection);
SXSelection& sel = clipboard; 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) { 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); selectionSendNotify(e, false);
return; return;
} }
@ -585,7 +590,7 @@ void CXWM::handleSelectionRequest(xcb_selection_request_event_t* e) {
} }
bool CXWM::handleSelectionXFixesNotify(xcb_xfixes_selection_notify_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 // IMPORTANT: mind the g_pSeatManager below
SXSelection& sel = clipboard; SXSelection& sel = clipboard;
@ -868,13 +873,17 @@ void CXWM::createWMWindow() {
xcb_set_selection_owner(connection, wmWindow, HYPRATOMS["_NET_WM_CM_S0"], XCB_CURRENT_TIME); xcb_set_selection_owner(connection, wmWindow, HYPRATOMS["_NET_WM_CM_S0"], XCB_CURRENT_TIME);
} }
void CXWM::activateSurface(SP<CXWaylandSurface> surf) { void CXWM::activateSurface(SP<CXWaylandSurface> surf, bool activate) {
if (surf == focusedSurface || (surf && surf->overrideRedirect)) if ((surf == focusedSurface && activate) || (surf && surf->overrideRedirect))
return; return;
setActiveWindow(surf ? surf->xID : (uint32_t)XCB_WINDOW_NONE); if (!activate || !surf) {
setActiveWindow((uint32_t)XCB_WINDOW_NONE);
focusWindow(surf); focusWindow(nullptr);
} else {
setActiveWindow(surf ? surf->xID : (uint32_t)XCB_WINDOW_NONE);
focusWindow(surf);
}
xcb_flush(connection); xcb_flush(connection);
} }

View File

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