compositor: implement wayland socket handover (#6930)

* compositor: implement wayland socket handover

This commit implements the compositor side of the Wayland socket
handover protocol as described in the [KDE Wiki]. The CLI options are
chosen so that they are compatible with Kwin.

[KDE Wiki]: https://invent.kde.org/plasma/kwin/-/wikis/Restarting

* main: verify that --wayland-fd is a valid file descriptor

* main: fail if only one of --socket and --wayland-fd is passed
This commit is contained in:
Ferdinand Bachmann 2024-07-20 00:37:20 +02:00 committed by GitHub
parent 8e15f91c24
commit efccf25fcc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 74 additions and 12 deletions

View file

@ -32,6 +32,10 @@ Show command usage.
.TP .TP
\f[B]-c\f[R], \f[B]--config\f[R] \f[B]-c\f[R], \f[B]--config\f[R]
Specify config file to use. Specify config file to use.
\f[B]--socket\f[R]
Sets the Wayland socket name (for Wayland socket handover)
\f[B]--wayland-fd\f[R]
Sets the Wayland socket file descriptor (for Wayland socket handover)
.SH BUGS .SH BUGS
.TP .TP
Submit bug reports and request features online at: Submit bug reports and request features online at:

View file

@ -41,6 +41,12 @@ OPTIONS
**-c**, **--config** **-c**, **--config**
Specify config file to use. Specify config file to use.
**--socket**
Sets the Wayland socket name (for Wayland socket handover)
**--wayland-fd**
Sets the Wayland socket file descriptor (for Wayland socket handover)
BUGS BUGS
==== ====

View file

@ -27,6 +27,7 @@
#include <hyprutils/string/String.hpp> #include <hyprutils/string/String.hpp>
using namespace Hyprutils::String; using namespace Hyprutils::String;
#include <fcntl.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/resource.h> #include <sys/resource.h>
@ -533,19 +534,28 @@ void CCompositor::prepareFallbackOutput() {
wlr_headless_add_output(headless, 1920, 1080); wlr_headless_add_output(headless, 1920, 1080);
} }
void CCompositor::startCompositor() { void CCompositor::startCompositor(std::string socketName, int socketFd) {
initAllSignals(); initAllSignals();
// get socket, avoid using 0 if (!socketName.empty() && socketFd != -1) {
for (int candidate = 1; candidate <= 32; candidate++) { fcntl(socketFd, F_SETFD, FD_CLOEXEC);
const auto CANDIDATESTR = ("wayland-" + std::to_string(candidate)); const auto RETVAL = wl_display_add_socket_fd(m_sWLDisplay, socketFd);
const auto RETVAL = wl_display_add_socket(m_sWLDisplay, CANDIDATESTR.c_str());
if (RETVAL >= 0) { if (RETVAL >= 0) {
m_szWLDisplaySocket = CANDIDATESTR; m_szWLDisplaySocket = socketName;
Debug::log(LOG, "wl_display_add_socket for {} succeeded with {}", CANDIDATESTR, RETVAL); Debug::log(LOG, "wl_display_add_socket_fd for {} succeeded with {}", socketName, RETVAL);
break; } else
} else { Debug::log(WARN, "wl_display_add_socket_fd for {} returned {}: skipping", socketName, RETVAL);
Debug::log(WARN, "wl_display_add_socket for {} returned {}: skipping candidate {}", CANDIDATESTR, RETVAL, candidate); } else {
// get socket, avoid using 0
for (int candidate = 1; candidate <= 32; candidate++) {
const auto CANDIDATESTR = ("wayland-" + std::to_string(candidate));
const auto RETVAL = wl_display_add_socket(m_sWLDisplay, CANDIDATESTR.c_str());
if (RETVAL >= 0) {
m_szWLDisplaySocket = CANDIDATESTR;
Debug::log(LOG, "wl_display_add_socket for {} succeeded with {}", CANDIDATESTR, RETVAL);
break;
} else
Debug::log(WARN, "wl_display_add_socket for {} returned {}: skipping candidate {}", CANDIDATESTR, RETVAL, candidate);
} }
} }

View file

@ -78,7 +78,7 @@ class CCompositor {
std::unordered_map<std::string, uint64_t> m_mMonitorIDMap; std::unordered_map<std::string, uint64_t> m_mMonitorIDMap;
void initServer(); void initServer();
void startCompositor(); void startCompositor(std::string socketName, int socketFd);
void cleanup(); void cleanup();
void createLockFile(); void createLockFile();
void removeLockFile(); void removeLockFile();

View file

@ -4,6 +4,7 @@
#include "config/ConfigManager.hpp" #include "config/ConfigManager.hpp"
#include "init/initHelpers.hpp" #include "init/initHelpers.hpp"
#include <fcntl.h>
#include <iostream> #include <iostream>
#include <iterator> #include <iterator>
#include <vector> #include <vector>
@ -16,6 +17,8 @@ void help() {
std::cout << "\nArguments:\n"; std::cout << "\nArguments:\n";
std::cout << " --help -h - Show this message again\n"; std::cout << " --help -h - Show this message again\n";
std::cout << " --config FILE -c FILE - Specify config file to use\n"; std::cout << " --config FILE -c FILE - Specify config file to use\n";
std::cout << " --socket NAME - Sets the Wayland socket name (for Wayland socket handover)\n";
std::cout << " --wayland-fd FD - Sets the Wayland socket fd (for Wayland socket handover)\n";
std::cout << " --i-am-really-stupid - Omits root user privileges check (why would you do that?)\n"; std::cout << " --i-am-really-stupid - Omits root user privileges check (why would you do that?)\n";
} }
@ -37,6 +40,8 @@ int main(int argc, char** argv) {
// parse some args // parse some args
std::string configPath; std::string configPath;
std::string socketName;
int socketFd = -1;
bool ignoreSudo = false; bool ignoreSudo = false;
std::vector<std::string> args{argv + 1, argv + argc}; std::vector<std::string> args{argv + 1, argv + argc};
@ -46,6 +51,36 @@ int main(int argc, char** argv) {
std::cout << "[ WARNING ] Running Hyprland with superuser privileges might damage your system\n"; std::cout << "[ WARNING ] Running Hyprland with superuser privileges might damage your system\n";
ignoreSudo = true; ignoreSudo = true;
} else if (it->compare("--socket") == 0) {
if (std::next(it) == args.end()) {
help();
return 1;
}
socketName = *std::next(it);
it++;
} else if (it->compare("--wayland-fd") == 0) {
if (std::next(it) == args.end()) {
help();
return 1;
}
try {
socketFd = std::stoi(std::next(it)->c_str());
// check if socketFd is a valid file descriptor
if (fcntl(socketFd, F_GETFD) == -1)
throw std::exception();
} catch (...) {
std::cerr << "[ ERROR ] Invalid Wayland FD!\n";
help();
return 1;
}
it++;
} else if (it->compare("-c") == 0 || it->compare("--config") == 0) { } else if (it->compare("-c") == 0 || it->compare("--config") == 0) {
if (std::next(it) == args.end()) { if (std::next(it) == args.end()) {
help(); help();
@ -93,6 +128,13 @@ int main(int argc, char** argv) {
std::cout << "Superuser privileges check is omitted. I hope you know what you're doing.\n"; std::cout << "Superuser privileges check is omitted. I hope you know what you're doing.\n";
} }
if (socketName.empty() ^ (socketFd == -1)) {
std::cerr << "[ ERROR ] Hyprland was launched with only one of --socket and --wayland-fd.\n";
std::cerr << " Hint: Pass both --socket and --wayland-fd to perform Wayland socket handover.\n";
return 1;
}
std::cout << "Welcome to Hyprland!\n"; std::cout << "Welcome to Hyprland!\n";
// let's init the compositor. // let's init the compositor.
@ -113,7 +155,7 @@ int main(int argc, char** argv) {
Debug::log(LOG, "Hyprland init finished."); Debug::log(LOG, "Hyprland init finished.");
// If all's good to go, start. // If all's good to go, start.
g_pCompositor->startCompositor(); g_pCompositor->startCompositor(socketName, socketFd);
g_pCompositor->m_bIsShuttingDown = true; g_pCompositor->m_bIsShuttingDown = true;