xwayland: move to hyprland impl (#6086)
This commit is contained in:
parent
a71207434c
commit
addd3e7f1a
36 changed files with 2956 additions and 707 deletions
427
src/xwayland/Server.cpp
Normal file
427
src/xwayland/Server.cpp
Normal file
|
|
@ -0,0 +1,427 @@
|
|||
#ifndef NO_XWAYLAND
|
||||
|
||||
#include "Server.hpp"
|
||||
#include "../defines.hpp"
|
||||
#include "../Compositor.hpp"
|
||||
#include "../managers/CursorManager.hpp"
|
||||
#include "XWayland.hpp"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// TODO: cleanup
|
||||
static bool set_cloexec(int fd, bool cloexec) {
|
||||
int flags = fcntl(fd, F_GETFD);
|
||||
if (flags == -1) {
|
||||
wlr_log_errno(WLR_ERROR, "fcntl failed");
|
||||
return false;
|
||||
}
|
||||
if (cloexec) {
|
||||
flags = flags | FD_CLOEXEC;
|
||||
} else {
|
||||
flags = flags & ~FD_CLOEXEC;
|
||||
}
|
||||
if (fcntl(fd, F_SETFD, flags) == -1) {
|
||||
wlr_log_errno(WLR_ERROR, "fcntl failed");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int openSocket(struct sockaddr_un* addr, size_t path_size) {
|
||||
int fd, rc;
|
||||
socklen_t size = offsetof(struct sockaddr_un, sun_path) + path_size + 1;
|
||||
|
||||
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd < 0) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to create socket %c%s", addr->sun_path[0] ? addr->sun_path[0] : '@', addr->sun_path + 1);
|
||||
return -1;
|
||||
}
|
||||
if (!set_cloexec(fd, true)) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addr->sun_path[0]) {
|
||||
unlink(addr->sun_path);
|
||||
}
|
||||
if (bind(fd, (struct sockaddr*)addr, size) < 0) {
|
||||
rc = errno;
|
||||
wlr_log_errno(WLR_ERROR, "Failed to bind socket %c%s", addr->sun_path[0] ? addr->sun_path[0] : '@', addr->sun_path + 1);
|
||||
goto cleanup;
|
||||
}
|
||||
if (listen(fd, 1) < 0) {
|
||||
rc = errno;
|
||||
wlr_log_errno(WLR_ERROR, "Failed to listen to socket %c%s", addr->sun_path[0] ? addr->sun_path[0] : '@', addr->sun_path + 1);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
return fd;
|
||||
|
||||
cleanup:
|
||||
close(fd);
|
||||
if (addr->sun_path[0]) {
|
||||
unlink(addr->sun_path);
|
||||
}
|
||||
errno = rc;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool checkPermissionsForSocketDir(void) {
|
||||
struct stat buf;
|
||||
|
||||
if (lstat("/tmp/.X11-unix", &buf)) {
|
||||
Debug::log(ERR, "Failed statting X11 socket dir");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(buf.st_mode & S_IFDIR)) {
|
||||
Debug::log(ERR, "X11 socket dir is not a dir");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!((buf.st_uid == 0) || (buf.st_uid == getuid()))) {
|
||||
Debug::log(ERR, "X11 socket dir is not ours");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(buf.st_mode & S_ISVTX)) {
|
||||
if ((buf.st_mode & (S_IWGRP | S_IWOTH))) {
|
||||
Debug::log(ERR, "X11 socket dir is sticky by others");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool openSockets(std::array<int, 2>& sockets, int display) {
|
||||
auto ret = mkdir("/tmp/.X11-unix", 755);
|
||||
|
||||
if (ret != 0) {
|
||||
if (errno == EEXIST) {
|
||||
if (!checkPermissionsForSocketDir())
|
||||
return false;
|
||||
} else {
|
||||
Debug::log(ERR, "XWayland: couldn't create socket dir");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string path;
|
||||
sockaddr_un addr = {.sun_family = AF_UNIX};
|
||||
|
||||
#ifdef __linux__
|
||||
// cursed...
|
||||
addr.sun_path[0] = 0;
|
||||
path = std::format("/tmp/.X11-unix/X{}", display);
|
||||
strncpy(addr.sun_path + 1, path.c_str(), path.length() + 1);
|
||||
#else
|
||||
path = std::format("/tmp/.X11-unix/X{}_", display);
|
||||
strncpy(addr.sun_path, path.c_str(), path.length() + 1);
|
||||
#endif
|
||||
sockets[0] = openSocket(&addr, path.length());
|
||||
if (sockets[0] < 0)
|
||||
return false;
|
||||
|
||||
path = std::format("/tmp/.X11-unix/X{}", display);
|
||||
strncpy(addr.sun_path, path.c_str(), path.length() + 1);
|
||||
sockets[1] = openSocket(&addr, path.length());
|
||||
if (sockets[1] < 0) {
|
||||
close(sockets[0]);
|
||||
sockets[0] = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void startServer(void* data) {
|
||||
if (!g_pXWayland->pServer->start())
|
||||
Debug::log(ERR, "The XWayland server could not start! XWayland will not work...");
|
||||
}
|
||||
|
||||
static int xwaylandReady(int fd, uint32_t mask, void* data) {
|
||||
return g_pXWayland->pServer->ready(fd, mask);
|
||||
}
|
||||
|
||||
bool CXWaylandServer::tryOpenSockets() {
|
||||
for (size_t i = 0; i <= 32; ++i) {
|
||||
auto LOCK = std::format("/tmp/.X{}-lock", i);
|
||||
|
||||
if (int fd = open(LOCK.c_str(), O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0444); fd >= 0) {
|
||||
// we managed to open the lock
|
||||
if (!openSockets(xFDs, i)) {
|
||||
std::filesystem::remove(LOCK);
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto PIDSTR = std::format("{}", getpid());
|
||||
|
||||
if (write(fd, PIDSTR.c_str(), PIDSTR.length()) != (long)PIDSTR.length()) {
|
||||
std::filesystem::remove(LOCK);
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
display = i;
|
||||
displayName = std::format(":{}", display);
|
||||
break;
|
||||
}
|
||||
|
||||
int fd = open(LOCK.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
|
||||
if (fd < 0)
|
||||
continue;
|
||||
|
||||
char pidstr[12] = {0};
|
||||
read(fd, pidstr, sizeof(pidstr) - 1);
|
||||
close(fd);
|
||||
|
||||
uint64_t pid = 0;
|
||||
try {
|
||||
pid = std::stoi(std::string{pidstr, 11});
|
||||
} catch (...) { continue; }
|
||||
|
||||
if (kill(pid, 0) != 0 && errno == ESRCH) {
|
||||
if (!std::filesystem::remove(LOCK))
|
||||
continue;
|
||||
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
if (display < 0) {
|
||||
Debug::log(ERR, "Failed to find a suitable socket for xwayland");
|
||||
return false;
|
||||
}
|
||||
|
||||
Debug::log(LOG, "XWayland found a suitable display socket at DISPLAY: {}", displayName);
|
||||
return true;
|
||||
}
|
||||
|
||||
CXWaylandServer::CXWaylandServer() {
|
||||
;
|
||||
}
|
||||
|
||||
CXWaylandServer::~CXWaylandServer() {
|
||||
die();
|
||||
if (display < 0)
|
||||
return;
|
||||
|
||||
if (xFDs[0])
|
||||
close(xFDs[0]);
|
||||
if (xFDs[1])
|
||||
close(xFDs[1]);
|
||||
|
||||
auto LOCK = std::format("/tmp/.X{}-lock", display);
|
||||
std::filesystem::remove(LOCK);
|
||||
|
||||
std::string path;
|
||||
#ifdef __linux__
|
||||
path = std::format("/tmp/.X11-unix/X{}", display);
|
||||
#else
|
||||
path = std::format("/tmp/.X11-unix/X{}_", display);
|
||||
#endif
|
||||
std::filesystem::remove(path);
|
||||
}
|
||||
|
||||
void CXWaylandServer::die() {
|
||||
if (display < 0)
|
||||
return;
|
||||
|
||||
if (xFDReadEvents[0]) {
|
||||
wl_event_source_remove(xFDReadEvents[0]);
|
||||
wl_event_source_remove(xFDReadEvents[1]);
|
||||
|
||||
xFDReadEvents = {nullptr, nullptr};
|
||||
}
|
||||
|
||||
if (pipeSource)
|
||||
wl_event_source_remove(pipeSource);
|
||||
|
||||
if (waylandFDs[0])
|
||||
close(waylandFDs[0]);
|
||||
if (waylandFDs[1])
|
||||
close(waylandFDs[1]);
|
||||
if (xwmFDs[0])
|
||||
close(xwmFDs[0]);
|
||||
if (xwmFDs[1])
|
||||
close(xwmFDs[1]);
|
||||
|
||||
if (xwaylandClient)
|
||||
wl_client_destroy(xwaylandClient);
|
||||
|
||||
xwaylandClient = nullptr;
|
||||
waylandFDs = {-1, -1};
|
||||
xwmFDs = {-1, -1};
|
||||
}
|
||||
|
||||
bool CXWaylandServer::create() {
|
||||
if (!tryOpenSockets())
|
||||
return false;
|
||||
|
||||
setenv("DISPLAY", displayName.c_str(), true);
|
||||
|
||||
// TODO: lazy mode
|
||||
|
||||
idleSource = wl_event_loop_add_idle(g_pCompositor->m_sWLEventLoop, ::startServer, nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CXWaylandServer::runXWayland(int notifyFD) {
|
||||
if (!set_cloexec(xFDs[0], false) || !set_cloexec(xFDs[1], false) || !set_cloexec(waylandFDs[1], false) || !set_cloexec(xwmFDs[1], false)) {
|
||||
Debug::log(ERR, "Failed to unset cloexec on fds");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
auto cmd = std::format("Xwayland {} -rootless -core -listenfd {} -listenfd {} -displayfd {} -wm {}", displayName, xFDs[0], xFDs[1], notifyFD, xwmFDs[1]);
|
||||
|
||||
auto waylandSocket = std::format("{}", waylandFDs[1]);
|
||||
setenv("WAYLAND_SOCKET", waylandSocket.c_str(), true);
|
||||
|
||||
Debug::log(LOG, "Starting XWayland with \"{}\", bon voyage!", cmd);
|
||||
|
||||
execl("/bin/sh", "/bin/sh", "-c", cmd.c_str(), nullptr);
|
||||
|
||||
Debug::log(ERR, "XWayland failed to open");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
bool CXWaylandServer::start() {
|
||||
idleSource = nullptr;
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, waylandFDs.data()) != 0) {
|
||||
Debug::log(ERR, "socketpair failed (1)");
|
||||
die();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!set_cloexec(waylandFDs[0], true) || !set_cloexec(waylandFDs[1], true)) {
|
||||
Debug::log(ERR, "set_cloexec failed (1)");
|
||||
die();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, xwmFDs.data()) != 0) {
|
||||
Debug::log(ERR, "socketpair failed (2)");
|
||||
die();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!set_cloexec(xwmFDs[0], true) || !set_cloexec(xwmFDs[1], true)) {
|
||||
Debug::log(ERR, "set_cloexec failed (2)");
|
||||
die();
|
||||
return false;
|
||||
}
|
||||
|
||||
xwaylandClient = wl_client_create(g_pCompositor->m_sWLDisplay, waylandFDs[0]);
|
||||
if (!xwaylandClient) {
|
||||
Debug::log(ERR, "wl_client_create failed");
|
||||
die();
|
||||
return false;
|
||||
}
|
||||
|
||||
waylandFDs[0] = -1;
|
||||
|
||||
int notify[2] = {-1, -1};
|
||||
if (pipe(notify) < 0) {
|
||||
Debug::log(ERR, "pipe failed");
|
||||
die();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!set_cloexec(notify[0], true)) {
|
||||
Debug::log(ERR, "set_cloexec failed (3)");
|
||||
close(notify[0]);
|
||||
close(notify[1]);
|
||||
die();
|
||||
return false;
|
||||
}
|
||||
|
||||
pipeSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, notify[0], WL_EVENT_READABLE, ::xwaylandReady, nullptr);
|
||||
|
||||
serverPID = fork();
|
||||
if (serverPID < 0) {
|
||||
Debug::log(ERR, "fork failed");
|
||||
close(notify[0]);
|
||||
close(notify[1]);
|
||||
die();
|
||||
return false;
|
||||
} else if (serverPID == 0) {
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
Debug::log(ERR, "second fork failed");
|
||||
_exit(1);
|
||||
} else if (pid == 0) {
|
||||
runXWayland(notify[1]);
|
||||
}
|
||||
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
close(notify[1]);
|
||||
close(waylandFDs[1]);
|
||||
close(xwmFDs[1]);
|
||||
waylandFDs[1] = -1;
|
||||
xwmFDs[1] = -1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int CXWaylandServer::ready(int fd, uint32_t mask) {
|
||||
if (mask & WL_EVENT_READABLE) {
|
||||
// xwayland writes twice
|
||||
char buf[64];
|
||||
ssize_t n = read(fd, buf, sizeof(buf));
|
||||
if (n < 0 && errno != EINTR) {
|
||||
Debug::log(ERR, "Xwayland: read from displayFd failed");
|
||||
mask = 0;
|
||||
} else if (n <= 0 || buf[n - 1] != '\n')
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (waitpid(serverPID, nullptr, 0) < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
Debug::log(ERR, "Xwayland: waitpid for fork failed");
|
||||
g_pXWayland->pServer.reset();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// if we don't have readable here, it failed
|
||||
if (!(mask & WL_EVENT_READABLE)) {
|
||||
Debug::log(ERR, "Xwayland: startup failed, not setting up xwm");
|
||||
g_pXWayland->pServer.reset();
|
||||
return 1;
|
||||
}
|
||||
|
||||
Debug::log(LOG, "XWayland is ready");
|
||||
|
||||
close(fd);
|
||||
wl_event_source_remove(pipeSource);
|
||||
pipeSource = nullptr;
|
||||
|
||||
// start the wm
|
||||
g_pXWayland->pWM = std::make_unique<CXWM>();
|
||||
|
||||
g_pCursorManager->setXWaylandCursor();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
48
src/xwayland/Server.hpp
Normal file
48
src/xwayland/Server.hpp
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "../helpers/signal/Signal.hpp"
|
||||
|
||||
struct wl_event_source;
|
||||
struct wl_client;
|
||||
|
||||
// TODO: add lazy mode
|
||||
class CXWaylandServer {
|
||||
public:
|
||||
CXWaylandServer();
|
||||
~CXWaylandServer();
|
||||
|
||||
// create the server.
|
||||
bool create();
|
||||
|
||||
// starts the server, meant to be called by CXWaylandServer.
|
||||
bool start();
|
||||
|
||||
// called on ready
|
||||
int ready(int fd, uint32_t mask);
|
||||
|
||||
void die();
|
||||
|
||||
struct {
|
||||
CSignal ready;
|
||||
} events;
|
||||
|
||||
wl_client* xwaylandClient = nullptr;
|
||||
|
||||
private:
|
||||
bool tryOpenSockets();
|
||||
void runXWayland(int notifyFD);
|
||||
|
||||
pid_t serverPID = 0;
|
||||
|
||||
std::string displayName;
|
||||
int display = -1;
|
||||
std::array<int, 2> xFDs = {-1, -1};
|
||||
std::array<wl_event_source*, 2> xFDReadEvents = {nullptr, nullptr};
|
||||
wl_event_source* idleSource = nullptr;
|
||||
wl_event_source* pipeSource = nullptr;
|
||||
std::array<int, 2> xwmFDs = {-1, -1};
|
||||
std::array<int, 2> waylandFDs = {-1, -1};
|
||||
|
||||
friend class CXWM;
|
||||
};
|
||||
97
src/xwayland/XDataSource.cpp
Normal file
97
src/xwayland/XDataSource.cpp
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
#ifndef NO_XWAYLAND
|
||||
|
||||
#include "XDataSource.hpp"
|
||||
#include "XWayland.hpp"
|
||||
#include "../defines.hpp"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
CXDataSource::CXDataSource(SXSelection& sel_) : selection(sel_) {
|
||||
xcb_get_property_cookie_t cookie = xcb_get_property(g_pXWayland->pWM->connection,
|
||||
1, // delete
|
||||
selection.window, HYPRATOMS["_WL_SELECTION"], XCB_GET_PROPERTY_TYPE_ANY, 0, 4096);
|
||||
|
||||
xcb_get_property_reply_t* reply = xcb_get_property_reply(g_pXWayland->pWM->connection, cookie, NULL);
|
||||
if (!reply)
|
||||
return;
|
||||
|
||||
if (reply->type != XCB_ATOM_ATOM) {
|
||||
free(reply);
|
||||
return;
|
||||
}
|
||||
|
||||
auto value = (xcb_atom_t*)xcb_get_property_value(reply);
|
||||
for (uint32_t i = 0; i < reply->value_len; i++) {
|
||||
if (value[i] == HYPRATOMS["UTF8_STRING"])
|
||||
mimeTypes.push_back("text/plain;charset=utf-8");
|
||||
else if (value[i] == HYPRATOMS["TEXT"])
|
||||
mimeTypes.push_back("text/plain");
|
||||
else if (value[i] != HYPRATOMS["TARGETS"] && value[i] != HYPRATOMS["TIMESTAMP"]) {
|
||||
|
||||
auto type = g_pXWayland->pWM->mimeFromAtom(value[i]);
|
||||
|
||||
if (type == "INVALID")
|
||||
continue;
|
||||
|
||||
mimeTypes.push_back(type);
|
||||
}
|
||||
|
||||
mimeAtoms.push_back(value[i]);
|
||||
}
|
||||
|
||||
free(reply);
|
||||
}
|
||||
|
||||
std::vector<std::string> CXDataSource::mimes() {
|
||||
return mimeTypes;
|
||||
}
|
||||
|
||||
void CXDataSource::send(const std::string& mime, uint32_t fd) {
|
||||
xcb_atom_t mimeAtom = 0;
|
||||
|
||||
for (size_t i = 0; i < mimeTypes.size(); ++i) {
|
||||
if (mimeTypes.at(i) == mime) {
|
||||
mimeAtom = mimeAtoms.at(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mimeAtom) {
|
||||
Debug::log(ERR, "[XDataSource] mime atom not found");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
Debug::log(LOG, "[XDataSource] send with mime {} to fd {}", mime, fd);
|
||||
|
||||
selection.transfer = std::make_unique<SXTransfer>(selection);
|
||||
selection.transfer->incomingWindow = xcb_generate_id(g_pXWayland->pWM->connection);
|
||||
const uint32_t MASK = XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE;
|
||||
xcb_create_window(g_pXWayland->pWM->connection, XCB_COPY_FROM_PARENT, selection.transfer->incomingWindow, g_pXWayland->pWM->screen->root, 0, 0, 10, 10, 0,
|
||||
XCB_WINDOW_CLASS_INPUT_OUTPUT, g_pXWayland->pWM->screen->root_visual, XCB_CW_EVENT_MASK, &MASK);
|
||||
|
||||
xcb_convert_selection(g_pXWayland->pWM->connection, selection.transfer->incomingWindow, HYPRATOMS["CLIPBOARD"], mimeAtom, HYPRATOMS["_WL_SELECTION"], XCB_TIME_CURRENT_TIME);
|
||||
|
||||
xcb_flush(g_pXWayland->pWM->connection);
|
||||
|
||||
fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK);
|
||||
selection.transfer->wlFD = fd;
|
||||
}
|
||||
|
||||
void CXDataSource::accepted(const std::string& mime) {
|
||||
Debug::log(LOG, "[XDataSource] accepted is a stub");
|
||||
}
|
||||
|
||||
void CXDataSource::cancelled() {
|
||||
Debug::log(LOG, "[XDataSource] cancelled is a stub");
|
||||
}
|
||||
|
||||
void CXDataSource::error(uint32_t code, const std::string& msg) {
|
||||
Debug::log(LOG, "[XDataSource] error is a stub: err {}: {}", code, msg);
|
||||
}
|
||||
|
||||
eDataSourceType CXDataSource::type() {
|
||||
return DATA_SOURCE_TYPE_X11;
|
||||
}
|
||||
|
||||
#endif
|
||||
22
src/xwayland/XDataSource.hpp
Normal file
22
src/xwayland/XDataSource.hpp
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include "../protocols/types/DataDevice.hpp"
|
||||
|
||||
struct SXSelection;
|
||||
|
||||
class CXDataSource : public IDataSource {
|
||||
public:
|
||||
CXDataSource(SXSelection&);
|
||||
|
||||
virtual std::vector<std::string> mimes();
|
||||
virtual void send(const std::string& mime, uint32_t fd);
|
||||
virtual void accepted(const std::string& mime);
|
||||
virtual void cancelled();
|
||||
virtual void error(uint32_t code, const std::string& msg);
|
||||
virtual eDataSourceType type();
|
||||
|
||||
private:
|
||||
SXSelection& selection;
|
||||
std::vector<std::string> mimeTypes; // these two have shared idx
|
||||
std::vector<uint32_t> mimeAtoms; //
|
||||
};
|
||||
291
src/xwayland/XSurface.cpp
Normal file
291
src/xwayland/XSurface.cpp
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
#include "XSurface.hpp"
|
||||
#include "XWayland.hpp"
|
||||
#include "../protocols/XWaylandShell.hpp"
|
||||
|
||||
#ifndef NO_XWAYLAND
|
||||
|
||||
#include "../Compositor.hpp"
|
||||
#include <ranges>
|
||||
|
||||
CXWaylandSurface::CXWaylandSurface(uint32_t xID_, CBox geometry_, bool OR) : xID(xID_), geometry(geometry_), overrideRedirect(OR) {
|
||||
xcb_res_query_client_ids_cookie_t client_id_cookie = {0};
|
||||
if (g_pXWayland->pWM->xres) {
|
||||
xcb_res_client_id_spec_t spec = {.client = xID, .mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID};
|
||||
client_id_cookie = xcb_res_query_client_ids(g_pXWayland->pWM->connection, 1, &spec);
|
||||
}
|
||||
|
||||
uint32_t values[1];
|
||||
values[0] = XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_PROPERTY_CHANGE;
|
||||
xcb_change_window_attributes(g_pXWayland->pWM->connection, xID, XCB_CW_EVENT_MASK, values);
|
||||
|
||||
if (g_pXWayland->pWM->xres) {
|
||||
xcb_res_query_client_ids_reply_t* reply = xcb_res_query_client_ids_reply(g_pXWayland->pWM->connection, client_id_cookie, nullptr);
|
||||
if (!reply)
|
||||
return;
|
||||
|
||||
uint32_t* ppid = nullptr;
|
||||
xcb_res_client_id_value_iterator_t iter = xcb_res_query_client_ids_ids_iterator(reply);
|
||||
while (iter.rem > 0) {
|
||||
if (iter.data->spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID && xcb_res_client_id_value_value_length(iter.data) > 0) {
|
||||
ppid = xcb_res_client_id_value_value(iter.data);
|
||||
break;
|
||||
}
|
||||
xcb_res_client_id_value_next(&iter);
|
||||
}
|
||||
if (ppid == NULL) {
|
||||
free(reply);
|
||||
return;
|
||||
}
|
||||
pid = *ppid;
|
||||
free(reply);
|
||||
}
|
||||
|
||||
events.resourceChange.registerStaticListener([this](void* data, std::any d) { ensureListeners(); }, nullptr);
|
||||
}
|
||||
|
||||
void CXWaylandSurface::ensureListeners() {
|
||||
bool connected = hyprListener_surfaceDestroy.isConnected();
|
||||
|
||||
if (connected && !surface) {
|
||||
hyprListener_surfaceDestroy.removeCallback();
|
||||
hyprListener_surfaceCommit.removeCallback();
|
||||
} else if (!connected && surface) {
|
||||
hyprListener_surfaceDestroy.initCallback(
|
||||
&surface->events.destroy,
|
||||
[this](void* owner, void* data) {
|
||||
if (mapped)
|
||||
unmap();
|
||||
|
||||
surface = nullptr;
|
||||
hyprListener_surfaceDestroy.removeCallback();
|
||||
hyprListener_surfaceCommit.removeCallback();
|
||||
events.resourceChange.emit();
|
||||
},
|
||||
nullptr, "CXWaylandSurface");
|
||||
hyprListener_surfaceCommit.initCallback(
|
||||
&surface->events.commit,
|
||||
[this](void* owner, void* data) {
|
||||
if (surface->pending.buffer_width > 0 && surface->pending.buffer_height > 0 && !mapped) {
|
||||
map();
|
||||
return;
|
||||
}
|
||||
|
||||
if (surface->pending.buffer_width <= 0 && surface->pending.buffer_height <= 0 && mapped) {
|
||||
unmap();
|
||||
return;
|
||||
}
|
||||
|
||||
events.commit.emit();
|
||||
},
|
||||
nullptr, "CXWaylandSurface");
|
||||
}
|
||||
|
||||
if (resource) {
|
||||
listeners.destroyResource = resource->events.destroy.registerListener([this](std::any d) {
|
||||
unmap();
|
||||
surface = nullptr;
|
||||
events.resourceChange.emit();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void CXWaylandSurface::map() {
|
||||
if (mapped)
|
||||
return;
|
||||
|
||||
ASSERT(surface);
|
||||
|
||||
g_pXWayland->pWM->mappedSurfaces.emplace_back(self);
|
||||
g_pXWayland->pWM->mappedSurfacesStacking.emplace_back(self);
|
||||
|
||||
mapped = true;
|
||||
wlr_surface_map(surface);
|
||||
|
||||
Debug::log(LOG, "XWayland surface {:x} mapping", (uintptr_t)this);
|
||||
|
||||
events.map.emit();
|
||||
|
||||
g_pXWayland->pWM->updateClientList();
|
||||
}
|
||||
|
||||
void CXWaylandSurface::unmap() {
|
||||
if (!mapped)
|
||||
return;
|
||||
|
||||
ASSERT(surface);
|
||||
|
||||
std::erase(g_pXWayland->pWM->mappedSurfaces, self);
|
||||
std::erase(g_pXWayland->pWM->mappedSurfacesStacking, self);
|
||||
|
||||
mapped = false;
|
||||
wlr_surface_unmap(surface);
|
||||
|
||||
Debug::log(LOG, "XWayland surface {:x} unmapping", (uintptr_t)this);
|
||||
|
||||
events.unmap.emit();
|
||||
|
||||
g_pXWayland->pWM->updateClientList();
|
||||
}
|
||||
|
||||
void CXWaylandSurface::considerMap() {
|
||||
if (mapped)
|
||||
return;
|
||||
|
||||
if (!surface) {
|
||||
Debug::log(LOG, "XWayland surface: considerMap, nope, no surface");
|
||||
return;
|
||||
}
|
||||
|
||||
if (surface->pending.buffer_width > 0 && surface->pending.buffer_height > 0) {
|
||||
Debug::log(LOG, "XWayland surface: considerMap, sure, we have a buffer");
|
||||
map();
|
||||
return;
|
||||
}
|
||||
|
||||
Debug::log(LOG, "XWayland surface: considerMap, nope, we don't have a buffer");
|
||||
}
|
||||
|
||||
bool CXWaylandSurface::wantsFocus() {
|
||||
if (atoms.empty())
|
||||
return true;
|
||||
|
||||
const std::array<uint32_t, 10> search = {
|
||||
HYPRATOMS["_NET_WM_WINDOW_TYPE_COMBO"], HYPRATOMS["_NET_WM_WINDOW_TYPE_DND"], HYPRATOMS["_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"],
|
||||
HYPRATOMS["_NET_WM_WINDOW_TYPE_MENU"], HYPRATOMS["_NET_WM_WINDOW_TYPE_NOTIFICATION"], HYPRATOMS["_NET_WM_WINDOW_TYPE_POPUP_MENU"],
|
||||
HYPRATOMS["_NET_WM_WINDOW_TYPE_SPLASH"], HYPRATOMS["_NET_WM_WINDOW_TYPE_DESKTOP"], HYPRATOMS["_NET_WM_WINDOW_TYPE_TOOLTIP"],
|
||||
HYPRATOMS["_NET_WM_WINDOW_TYPE_UTILITY"],
|
||||
};
|
||||
|
||||
for (auto& searched : search) {
|
||||
for (auto& a : atoms) {
|
||||
if (a == searched)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CXWaylandSurface::configure(const CBox& box) {
|
||||
Vector2D oldSize = geometry.size();
|
||||
|
||||
geometry = box;
|
||||
|
||||
uint32_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT | XCB_CONFIG_WINDOW_BORDER_WIDTH;
|
||||
uint32_t values[] = {box.x, box.y, box.width, box.height, 0};
|
||||
xcb_configure_window(g_pXWayland->pWM->connection, xID, mask, values);
|
||||
|
||||
g_pXWayland->pWM->updateClientList();
|
||||
|
||||
xcb_flush(g_pXWayland->pWM->connection);
|
||||
}
|
||||
|
||||
void CXWaylandSurface::activate(bool activate) {
|
||||
if (overrideRedirect && !activate)
|
||||
return;
|
||||
g_pXWayland->pWM->activateSurface(self.lock());
|
||||
}
|
||||
|
||||
void CXWaylandSurface::setFullscreen(bool fs) {
|
||||
fullscreen = fs;
|
||||
g_pXWayland->pWM->sendState(self.lock());
|
||||
}
|
||||
|
||||
void CXWaylandSurface::setMinimized(bool mz) {
|
||||
minimized = mz;
|
||||
g_pXWayland->pWM->sendState(self.lock());
|
||||
}
|
||||
|
||||
void CXWaylandSurface::restackToTop() {
|
||||
uint32_t values[1] = {XCB_STACK_MODE_ABOVE};
|
||||
|
||||
xcb_configure_window(g_pXWayland->pWM->connection, xID, XCB_CONFIG_WINDOW_STACK_MODE, values);
|
||||
|
||||
for (auto it = g_pXWayland->pWM->mappedSurfacesStacking.begin(); it != g_pXWayland->pWM->mappedSurfacesStacking.end(); ++it) {
|
||||
if (*it == self) {
|
||||
std::rotate(it, it + 1, g_pXWayland->pWM->mappedSurfacesStacking.end());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_pXWayland->pWM->updateClientList();
|
||||
|
||||
xcb_flush(g_pXWayland->pWM->connection);
|
||||
}
|
||||
|
||||
void CXWaylandSurface::close() {
|
||||
xcb_client_message_data_t msg = {0};
|
||||
msg.data32[0] = HYPRATOMS["WM_DELETE_WINDOW"];
|
||||
msg.data32[1] = XCB_CURRENT_TIME;
|
||||
g_pXWayland->pWM->sendWMMessage(self.lock(), &msg, XCB_EVENT_MASK_NO_EVENT);
|
||||
}
|
||||
|
||||
void CXWaylandSurface::setWithdrawn(bool withdrawn_) {
|
||||
withdrawn = withdrawn_;
|
||||
std::vector<uint32_t> props = {XCB_ICCCM_WM_STATE_NORMAL, XCB_WINDOW_NONE};
|
||||
|
||||
if (withdrawn)
|
||||
props[0] = XCB_ICCCM_WM_STATE_WITHDRAWN;
|
||||
else if (minimized)
|
||||
props[0] = XCB_ICCCM_WM_STATE_ICONIC;
|
||||
else
|
||||
props[0] = XCB_ICCCM_WM_STATE_NORMAL;
|
||||
|
||||
xcb_change_property(g_pXWayland->pWM->connection, XCB_PROP_MODE_REPLACE, xID, HYPRATOMS["WM_STATE"], HYPRATOMS["WM_STATE"], 32, props.size(), props.data());
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
CXWaylandSurface::CXWaylandSurface(uint32_t xID_, CBox geometry_, bool OR) : xID(xID_), geometry(geometry_), overrideRedirect(OR) {
|
||||
;
|
||||
}
|
||||
|
||||
void CXWaylandSurface::ensureListeners() {
|
||||
;
|
||||
}
|
||||
|
||||
void CXWaylandSurface::map() {
|
||||
;
|
||||
}
|
||||
|
||||
void CXWaylandSurface::unmap() {
|
||||
;
|
||||
}
|
||||
|
||||
bool CXWaylandSurface::wantsFocus() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void CXWaylandSurface::configure(const CBox& box) {
|
||||
;
|
||||
}
|
||||
|
||||
void CXWaylandSurface::activate(bool activate) {
|
||||
;
|
||||
}
|
||||
|
||||
void CXWaylandSurface::setFullscreen(bool fs) {
|
||||
;
|
||||
}
|
||||
|
||||
void CXWaylandSurface::setMinimized(bool mz) {
|
||||
;
|
||||
}
|
||||
|
||||
void CXWaylandSurface::restackToTop() {
|
||||
;
|
||||
}
|
||||
|
||||
void CXWaylandSurface::close() {
|
||||
;
|
||||
}
|
||||
|
||||
void CXWaylandSurface::considerMap() {
|
||||
;
|
||||
}
|
||||
|
||||
void CXWaylandSurface::setWithdrawn(bool withdrawn) {
|
||||
;
|
||||
}
|
||||
|
||||
#endif
|
||||
120
src/xwayland/XSurface.hpp
Normal file
120
src/xwayland/XSurface.hpp
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
#pragma once
|
||||
|
||||
#include "../helpers/WLListener.hpp"
|
||||
#include "../helpers/signal/Signal.hpp"
|
||||
#include "../helpers/Box.hpp"
|
||||
#include <vector>
|
||||
|
||||
struct wlr_surface;
|
||||
class CXWaylandSurfaceResource;
|
||||
|
||||
#ifdef NO_XWAYLAND
|
||||
typedef uint32_t xcb_pixmap_t;
|
||||
typedef uint32_t xcb_window_t;
|
||||
typedef struct {
|
||||
int32_t flags;
|
||||
uint32_t input;
|
||||
int32_t initial_state;
|
||||
xcb_pixmap_t icon_pixmap;
|
||||
xcb_window_t icon_window;
|
||||
int32_t icon_x, icon_y;
|
||||
xcb_pixmap_t icon_mask;
|
||||
xcb_window_t window_group;
|
||||
} xcb_icccm_wm_hints_t;
|
||||
typedef struct {
|
||||
uint32_t flags;
|
||||
int32_t x, y;
|
||||
int32_t width, height;
|
||||
int32_t min_width, min_height;
|
||||
int32_t max_width, max_height;
|
||||
int32_t width_inc, height_inc;
|
||||
int32_t min_aspect_num, min_aspect_den;
|
||||
int32_t max_aspect_num, max_aspect_den;
|
||||
int32_t base_width, base_height;
|
||||
uint32_t win_gravity;
|
||||
} xcb_size_hints_t;
|
||||
#else
|
||||
#include <xcb/xcb_icccm.h>
|
||||
#endif
|
||||
|
||||
class CXWaylandSurface {
|
||||
public:
|
||||
wlr_surface* surface = nullptr;
|
||||
WP<CXWaylandSurfaceResource> resource;
|
||||
|
||||
struct {
|
||||
CSignal stateChanged; // maximized, fs, minimized, etc.
|
||||
CSignal metadataChanged; // title, appid
|
||||
CSignal destroy;
|
||||
|
||||
CSignal resourceChange; // associated / dissociated
|
||||
|
||||
CSignal setGeometry;
|
||||
CSignal configure; // CBox
|
||||
|
||||
CSignal map;
|
||||
CSignal unmap;
|
||||
CSignal commit;
|
||||
|
||||
CSignal activate;
|
||||
} events;
|
||||
|
||||
struct {
|
||||
std::string title;
|
||||
std::string appid;
|
||||
|
||||
// volatile state: is reset after the stateChanged signal fires
|
||||
std::optional<bool> requestsMaximize;
|
||||
std::optional<bool> requestsFullscreen;
|
||||
std::optional<bool> requestsMinimize;
|
||||
} state;
|
||||
|
||||
uint32_t xID = 0;
|
||||
uint64_t wlID = 0;
|
||||
uint64_t wlSerial = 0;
|
||||
pid_t pid = 0;
|
||||
CBox geometry;
|
||||
bool overrideRedirect = false;
|
||||
bool withdrawn = false;
|
||||
bool fullscreen = false;
|
||||
bool maximized = false;
|
||||
bool minimized = false;
|
||||
bool mapped = false;
|
||||
bool modal = false;
|
||||
|
||||
WP<CXWaylandSurface> parent;
|
||||
WP<CXWaylandSurface> self;
|
||||
std::vector<WP<CXWaylandSurface>> children;
|
||||
|
||||
UP<xcb_icccm_wm_hints_t> hints;
|
||||
UP<xcb_size_hints_t> sizeHints;
|
||||
std::vector<uint32_t> atoms;
|
||||
std::string role = "";
|
||||
bool transient = false;
|
||||
|
||||
bool wantsFocus();
|
||||
void configure(const CBox& box);
|
||||
void activate(bool activate);
|
||||
void setFullscreen(bool fs);
|
||||
void setMinimized(bool mz);
|
||||
void restackToTop();
|
||||
void close();
|
||||
|
||||
private:
|
||||
CXWaylandSurface(uint32_t xID, CBox geometry, bool OR);
|
||||
|
||||
void ensureListeners();
|
||||
void map();
|
||||
void unmap();
|
||||
void considerMap();
|
||||
void setWithdrawn(bool withdrawn);
|
||||
|
||||
DYNLISTENER(surfaceDestroy);
|
||||
DYNLISTENER(surfaceCommit);
|
||||
|
||||
struct {
|
||||
CHyprSignalListener destroyResource;
|
||||
} listeners;
|
||||
|
||||
friend class CXWM;
|
||||
};
|
||||
1210
src/xwayland/XWM.cpp
Normal file
1210
src/xwayland/XWM.cpp
Normal file
File diff suppressed because it is too large
Load diff
160
src/xwayland/XWM.hpp
Normal file
160
src/xwayland/XWM.hpp
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
#pragma once
|
||||
|
||||
#include "../helpers/signal/Listener.hpp"
|
||||
#include "../helpers/WLListener.hpp"
|
||||
#include "../macros.hpp"
|
||||
|
||||
#include "XDataSource.hpp"
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_errors.h>
|
||||
#include <xcb/composite.h>
|
||||
#include <xcb/xfixes.h>
|
||||
#include <xcb/res.h>
|
||||
|
||||
struct wl_event_source;
|
||||
class CXWaylandSurfaceResource;
|
||||
struct SXSelection;
|
||||
|
||||
struct SXTransfer {
|
||||
~SXTransfer();
|
||||
|
||||
SXSelection& selection;
|
||||
bool out = true;
|
||||
|
||||
bool incremental = false;
|
||||
bool flushOnDelete = false;
|
||||
bool propertySet = false;
|
||||
|
||||
int wlFD = -1;
|
||||
wl_event_source* eventSource = nullptr;
|
||||
|
||||
std::vector<uint8_t> data;
|
||||
|
||||
xcb_selection_request_event_t request;
|
||||
|
||||
int propertyStart;
|
||||
xcb_get_property_reply_t* propertyReply;
|
||||
xcb_window_t incomingWindow;
|
||||
|
||||
bool getIncomingSelectionProp(bool erase);
|
||||
};
|
||||
|
||||
struct SXSelection {
|
||||
xcb_window_t window = 0;
|
||||
xcb_window_t owner = 0;
|
||||
xcb_timestamp_t timestamp = 0;
|
||||
SP<CXDataSource> dataSource;
|
||||
|
||||
void onSelection();
|
||||
bool sendData(xcb_selection_request_event_t* e, std::string mime);
|
||||
int onRead(int fd, uint32_t mask);
|
||||
|
||||
struct {
|
||||
CHyprSignalListener setSelection;
|
||||
} listeners;
|
||||
|
||||
std::unique_ptr<SXTransfer> transfer;
|
||||
};
|
||||
|
||||
class CXWM {
|
||||
public:
|
||||
CXWM();
|
||||
|
||||
int onEvent(int fd, uint32_t mask);
|
||||
|
||||
private:
|
||||
void setCursor(unsigned char* pixData, uint32_t stride, const Vector2D& size, const Vector2D& hotspot);
|
||||
|
||||
void gatherResources();
|
||||
void getVisual();
|
||||
void getRenderFormat();
|
||||
void createWMWindow();
|
||||
void initSelection();
|
||||
|
||||
void onNewSurface(wlr_surface* surf);
|
||||
void onNewResource(SP<CXWaylandSurfaceResource> resource);
|
||||
|
||||
void setActiveWindow(xcb_window_t window);
|
||||
void sendState(SP<CXWaylandSurface> surf);
|
||||
void focusWindow(SP<CXWaylandSurface> surf);
|
||||
void activateSurface(SP<CXWaylandSurface> surf);
|
||||
bool isWMWindow(xcb_window_t w);
|
||||
|
||||
void sendWMMessage(SP<CXWaylandSurface> surf, xcb_client_message_data_t* data, uint32_t mask);
|
||||
|
||||
SP<CXWaylandSurface> windowForXID(xcb_window_t wid);
|
||||
|
||||
void readWindowData(SP<CXWaylandSurface> surf);
|
||||
void associate(SP<CXWaylandSurface> surf, wlr_surface* wlSurf);
|
||||
void dissociate(SP<CXWaylandSurface> surf);
|
||||
|
||||
void updateClientList();
|
||||
|
||||
// event handlers
|
||||
void handleCreate(xcb_create_notify_event_t* e);
|
||||
void handleDestroy(xcb_destroy_notify_event_t* e);
|
||||
void handleConfigure(xcb_configure_request_event_t* e);
|
||||
void handleConfigureNotify(xcb_configure_notify_event_t* e);
|
||||
void handleMapRequest(xcb_map_request_event_t* e);
|
||||
void handleMapNotify(xcb_map_notify_event_t* e);
|
||||
void handleUnmapNotify(xcb_unmap_notify_event_t* e);
|
||||
void handlePropertyNotify(xcb_property_notify_event_t* e);
|
||||
void handleClientMessage(xcb_client_message_event_t* e);
|
||||
void handleFocusIn(xcb_focus_in_event_t* e);
|
||||
void handleError(xcb_value_error_t* e);
|
||||
|
||||
bool handleSelectionEvent(xcb_generic_event_t* e);
|
||||
void handleSelectionNotify(xcb_selection_notify_event_t* e);
|
||||
bool handleSelectionPropertyNotify(xcb_property_notify_event_t* e);
|
||||
void handleSelectionRequest(xcb_selection_request_event_t* e);
|
||||
bool handleSelectionXFixesNotify(xcb_xfixes_selection_notify_event_t* e);
|
||||
|
||||
void selectionSendNotify(xcb_selection_request_event_t* e, bool success);
|
||||
xcb_atom_t mimeToAtom(const std::string& mime);
|
||||
std::string mimeFromAtom(xcb_atom_t atom);
|
||||
void setClipboardToWayland(SXSelection& sel);
|
||||
void getTransferData(SXSelection& sel);
|
||||
void readProp(SP<CXWaylandSurface> XSURF, uint32_t atom, xcb_get_property_reply_t* reply);
|
||||
|
||||
//
|
||||
xcb_connection_t* connection = nullptr;
|
||||
xcb_errors_context_t* errors = nullptr;
|
||||
xcb_screen_t* screen = nullptr;
|
||||
|
||||
xcb_window_t wmWindow;
|
||||
|
||||
wl_event_source* eventSource = nullptr;
|
||||
|
||||
const xcb_query_extension_reply_t* xfixes = nullptr;
|
||||
const xcb_query_extension_reply_t* xres = nullptr;
|
||||
int xfixesMajor = 0;
|
||||
|
||||
xcb_visualid_t visual_id;
|
||||
xcb_colormap_t colormap;
|
||||
uint32_t cursorXID = 0;
|
||||
|
||||
xcb_render_pictformat_t render_format_id;
|
||||
|
||||
std::vector<WP<CXWaylandSurfaceResource>> shellResources;
|
||||
std::vector<SP<CXWaylandSurface>> surfaces;
|
||||
std::vector<WP<CXWaylandSurface>> mappedSurfaces; // ordered by map time
|
||||
std::vector<WP<CXWaylandSurface>> mappedSurfacesStacking; // ordered by stacking
|
||||
|
||||
WP<CXWaylandSurface> focusedSurface;
|
||||
uint64_t lastFocusSeq = 0;
|
||||
|
||||
SXSelection clipboard;
|
||||
|
||||
DYNLISTENER(newSurface);
|
||||
|
||||
struct {
|
||||
CHyprSignalListener newXShellSurface;
|
||||
} listeners;
|
||||
|
||||
friend class CXWaylandSurface;
|
||||
friend class CXWayland;
|
||||
friend class CXDataSource;
|
||||
friend struct SXSelection;
|
||||
friend struct SXTransfer;
|
||||
};
|
||||
28
src/xwayland/XWayland.cpp
Normal file
28
src/xwayland/XWayland.cpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#include "XWayland.hpp"
|
||||
#include "../debug/Log.hpp"
|
||||
|
||||
CXWayland::CXWayland() {
|
||||
#ifndef NO_XWAYLAND
|
||||
Debug::log(LOG, "Starting up the XWayland server");
|
||||
|
||||
pServer = std::make_unique<CXWaylandServer>();
|
||||
|
||||
if (!pServer->create()) {
|
||||
Debug::log(ERR, "XWayland failed to start: it will not work.");
|
||||
return;
|
||||
}
|
||||
#else
|
||||
Debug::log(LOG, "Not starting XWayland: disabled at compile time");
|
||||
#endif
|
||||
}
|
||||
|
||||
void CXWayland::setCursor(unsigned char* pixData, uint32_t stride, const Vector2D& size, const Vector2D& hotspot) {
|
||||
#ifndef NO_XWAYLAND
|
||||
if (!pWM) {
|
||||
Debug::log(ERR, "Couldn't set XCursor: no XWM yet");
|
||||
return;
|
||||
}
|
||||
|
||||
pWM->setCursor(pixData, stride, size, hotspot);
|
||||
#endif
|
||||
}
|
||||
129
src/xwayland/XWayland.hpp
Normal file
129
src/xwayland/XWayland.hpp
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "../helpers/signal/Signal.hpp"
|
||||
|
||||
#include "XSurface.hpp"
|
||||
|
||||
#ifndef NO_XWAYLAND
|
||||
#include "Server.hpp"
|
||||
#include "XWM.hpp"
|
||||
#else
|
||||
class CXWaylandServer;
|
||||
class CXWM;
|
||||
#endif
|
||||
|
||||
class CXWayland {
|
||||
public:
|
||||
CXWayland();
|
||||
|
||||
#ifndef NO_XWAYLAND
|
||||
std::unique_ptr<CXWaylandServer> pServer;
|
||||
std::unique_ptr<CXWM> pWM;
|
||||
#endif
|
||||
|
||||
void setCursor(unsigned char* pixData, uint32_t stride, const Vector2D& size, const Vector2D& hotspot);
|
||||
|
||||
struct {
|
||||
CSignal newSurface;
|
||||
} events;
|
||||
};
|
||||
|
||||
inline std::unique_ptr<CXWayland> g_pXWayland;
|
||||
|
||||
#define HYPRATOM(name) \
|
||||
{ name, 0 }
|
||||
inline std::unordered_map<std::string, uint32_t> HYPRATOMS = {
|
||||
HYPRATOM("_NET_SUPPORTED"),
|
||||
HYPRATOM("_NET_SUPPORTING_WM_CHECK"),
|
||||
HYPRATOM("_NET_WM_NAME"),
|
||||
HYPRATOM("_NET_WM_VISIBLE_NAME"),
|
||||
HYPRATOM("_NET_WM_MOVERESIZE"),
|
||||
HYPRATOM("_NET_WM_STATE_STICKY"),
|
||||
HYPRATOM("_NET_WM_STATE_FULLSCREEN"),
|
||||
HYPRATOM("_NET_WM_STATE_DEMANDS_ATTENTION"),
|
||||
HYPRATOM("_NET_WM_STATE_MODAL"),
|
||||
HYPRATOM("_NET_WM_STATE_HIDDEN"),
|
||||
HYPRATOM("_NET_WM_STATE_FOCUSED"),
|
||||
HYPRATOM("_NET_WM_STATE"),
|
||||
HYPRATOM("_NET_WM_WINDOW_TYPE"),
|
||||
HYPRATOM("_NET_WM_WINDOW_TYPE_NORMAL"),
|
||||
HYPRATOM("_NET_WM_WINDOW_TYPE_DOCK"),
|
||||
HYPRATOM("_NET_WM_WINDOW_TYPE_DIALOG"),
|
||||
HYPRATOM("_NET_WM_WINDOW_TYPE_UTILITY"),
|
||||
HYPRATOM("_NET_WM_WINDOW_TYPE_TOOLBAR"),
|
||||
HYPRATOM("_NET_WM_WINDOW_TYPE_SPLASH"),
|
||||
HYPRATOM("_NET_WM_WINDOW_TYPE_MENU"),
|
||||
HYPRATOM("_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"),
|
||||
HYPRATOM("_NET_WM_WINDOW_TYPE_POPUP_MENU"),
|
||||
HYPRATOM("_NET_WM_WINDOW_TYPE_TOOLTIP"),
|
||||
HYPRATOM("_NET_WM_WINDOW_TYPE_NOTIFICATION"),
|
||||
HYPRATOM("_NET_WM_WINDOW_TYPE_COMBO"),
|
||||
HYPRATOM("_NET_WM_WINDOW_TYPE_DND"),
|
||||
HYPRATOM("_NET_WM_WINDOW_TYPE_DESKTOP"),
|
||||
HYPRATOM("_NET_WM_STATE_MAXIMIZED_HORZ"),
|
||||
HYPRATOM("_NET_WM_STATE_MAXIMIZED_VERT"),
|
||||
HYPRATOM("_NET_WM_DESKTOP"),
|
||||
HYPRATOM("_NET_WM_STRUT_PARTIAL"),
|
||||
HYPRATOM("_NET_CLIENT_LIST"),
|
||||
HYPRATOM("_NET_CLIENT_LIST_STACKING"),
|
||||
HYPRATOM("_NET_CURRENT_DESKTOP"),
|
||||
HYPRATOM("_NET_NUMBER_OF_DESKTOPS"),
|
||||
HYPRATOM("_NET_DESKTOP_NAMES"),
|
||||
HYPRATOM("_NET_DESKTOP_VIEWPORT"),
|
||||
HYPRATOM("_NET_ACTIVE_WINDOW"),
|
||||
HYPRATOM("_NET_CLOSE_WINDOW"),
|
||||
HYPRATOM("_NET_MOVERESIZE_WINDOW"),
|
||||
HYPRATOM("_NET_WM_USER_TIME"),
|
||||
HYPRATOM("_NET_STARTUP_ID"),
|
||||
HYPRATOM("_NET_WORKAREA"),
|
||||
HYPRATOM("_NET_WM_ICON"),
|
||||
HYPRATOM("_NET_WM_CM_S0"),
|
||||
HYPRATOM("WM_PROTOCOLS"),
|
||||
HYPRATOM("WM_HINTS"),
|
||||
HYPRATOM("WM_DELETE_WINDOW"),
|
||||
HYPRATOM("UTF8_STRING"),
|
||||
HYPRATOM("WM_STATE"),
|
||||
HYPRATOM("WM_CLIENT_LEADER"),
|
||||
HYPRATOM("WM_TAKE_FOCUS"),
|
||||
HYPRATOM("WM_NORMAL_HINTS"),
|
||||
HYPRATOM("WM_SIZE_HINTS"),
|
||||
HYPRATOM("WM_WINDOW_ROLE"),
|
||||
HYPRATOM("_NET_REQUEST_FRAME_EXTENTS"),
|
||||
HYPRATOM("_NET_FRAME_EXTENTS"),
|
||||
HYPRATOM("_MOTIF_WM_HINTS"),
|
||||
HYPRATOM("WM_CHANGE_STATE"),
|
||||
HYPRATOM("_NET_SYSTEM_TRAY_OPCODE"),
|
||||
HYPRATOM("_NET_SYSTEM_TRAY_COLORS"),
|
||||
HYPRATOM("_NET_SYSTEM_TRAY_VISUAL"),
|
||||
HYPRATOM("_NET_SYSTEM_TRAY_ORIENTATION"),
|
||||
HYPRATOM("_XEMBED_INFO"),
|
||||
HYPRATOM("MANAGER"),
|
||||
HYPRATOM("XdndSelection"),
|
||||
HYPRATOM("XdndAware"),
|
||||
HYPRATOM("XdndStatus"),
|
||||
HYPRATOM("XdndPosition"),
|
||||
HYPRATOM("XdndEnter"),
|
||||
HYPRATOM("XdndLeave"),
|
||||
HYPRATOM("XdndDrop"),
|
||||
HYPRATOM("XdndFinished"),
|
||||
HYPRATOM("XdndProxy"),
|
||||
HYPRATOM("XdndTypeList"),
|
||||
HYPRATOM("XdndActionMove"),
|
||||
HYPRATOM("XdndActionCopy"),
|
||||
HYPRATOM("XdndActionAsk"),
|
||||
HYPRATOM("XdndActionPrivate"),
|
||||
HYPRATOM("CLIPBOARD"),
|
||||
HYPRATOM("PRIMARY"),
|
||||
HYPRATOM("_WL_SELECTION"),
|
||||
HYPRATOM("CLIPBOARD_MANAGER"),
|
||||
HYPRATOM("WINDOW"),
|
||||
HYPRATOM("WM_S0"),
|
||||
HYPRATOM("WL_SURFACE_ID"),
|
||||
HYPRATOM("WL_SURFACE_SERIAL"),
|
||||
HYPRATOM("TARGETS"),
|
||||
HYPRATOM("TIMESTAMP"),
|
||||
HYPRATOM("DELETE"),
|
||||
HYPRATOM("TEXT"),
|
||||
HYPRATOM("INCR"),
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue