replace qt event loop with epoll

This commit is contained in:
Raphael Robatsch 2021-10-27 17:24:47 +02:00
parent dfae73b1c5
commit 31c09e24d6
1 changed files with 86 additions and 48 deletions

View File

@ -5,6 +5,7 @@
#include <fcntl.h> #include <fcntl.h>
#include <math.h> #include <math.h>
#include <signal.h> #include <signal.h>
#include <sys/epoll.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/signalfd.h> #include <sys/signalfd.h>
#include <sys/stat.h> #include <sys/stat.h>
@ -14,10 +15,10 @@
#include <list> #include <list>
#include <optional> #include <optional>
#include <vector> #include <vector>
#include <QGuiApplication>
#include <QSocketNotifier>
#include <wayland-client.h> #include <wayland-client.h>
#include <wayland-cursor.h> #include <wayland-cursor.h>
#include <QGuiApplication>
#include <QString>
#include "wlr-layer-shell-unstable-v1-client-protocol.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h"
#include "xdg-shell-client-protocol.h" #include "xdg-shell-client-protocol.h"
#include "net-tapesoftware-dwl-wm-unstable-v1-client-protocol.h" #include "net-tapesoftware-dwl-wm-unstable-v1-client-protocol.h"
@ -44,10 +45,11 @@ struct Seat {
}; };
static void waylandFlush(); static void waylandFlush();
static void waylandWriteReady();
static void requireGlobal(const void *p, const char *name); static void requireGlobal(const void *p, const char *name);
static void setupStatusFifo(); static void setupStatusFifo();
static void onStatus(); static void onStatus();
[[noreturn]] static void diesys(const char *why);
[[noreturn]] static void die(const char *why);
static void cleanup(); static void cleanup();
wl_display *display; wl_display *display;
@ -65,9 +67,10 @@ static std::list<Monitor> monitors;
static std::list<Seat> seats; static std::list<Seat> seats;
static QString lastStatus; static QString lastStatus;
static std::string statusFifoName; static std::string statusFifoName;
static int epoll {-1};
static int displayFd {-1};
static int statusFifoFd {-1}; static int statusFifoFd {-1};
static int statusFifoWriter {-1}; static int statusFifoWriter {-1};
static QSocketNotifier *displayWriteNotifier;
static bool quitting {false}; static bool quitting {false};
void view(Monitor &m, const Arg &arg) void view(Monitor &m, const Arg &arg)
@ -245,28 +248,27 @@ static void setupStatusFifo()
auto result = mkfifo(path.c_str(), 0666); auto result = mkfifo(path.c_str(), 0666);
if (result == 0) { if (result == 0) {
auto fd = open(path.c_str(), O_CLOEXEC | O_NONBLOCK | O_RDONLY); auto fd = open(path.c_str(), O_CLOEXEC | O_NONBLOCK | O_RDONLY);
if (fd == -1) { if (fd < 0) {
perror("open status fifo reader"); diesys("open status fifo reader");
cleanup();
exit(1);
} }
statusFifoName = path; statusFifoName = path;
statusFifoFd = fd; statusFifoFd = fd;
fd = open(path.c_str(), O_CLOEXEC | O_WRONLY); fd = open(path.c_str(), O_CLOEXEC | O_WRONLY);
if (fd == -1) { if (fd < 0) {
perror("open status fifo writer"); diesys("open status fifo writer");
cleanup();
exit(1);
} }
statusFifoWriter = fd; statusFifoWriter = fd;
auto statusNotifier = new QSocketNotifier(statusFifoFd, QSocketNotifier::Read); epoll_event ev = {0};
statusNotifier->setEnabled(true); ev.events = EPOLLIN;
QObject::connect(statusNotifier, &QSocketNotifier::activated, onStatus); ev.data.fd = statusFifoFd;
if (epoll_ctl(epoll, EPOLL_CTL_ADD, statusFifoFd, &ev) < 0) {
diesys("epoll_ctl add status fifo");
}
return; return;
} else if (errno != EEXIST) { } else if (errno != EEXIST) {
perror("mkfifo"); diesys("mkfifo");
} }
} }
} }
@ -341,52 +343,87 @@ int main(int argc, char **argv)
sigaddset(&blockedsigs, SIGTERM); sigaddset(&blockedsigs, SIGTERM);
sigprocmask(SIG_BLOCK, &blockedsigs, nullptr); sigprocmask(SIG_BLOCK, &blockedsigs, nullptr);
QGuiApplication app(argc, argv); QGuiApplication app {argc, argv};
QCoreApplication::setOrganizationName("tape software");
QCoreApplication::setOrganizationDomain("tapesoftware.net");
QCoreApplication::setApplicationName("somebar");
epoll_event epollEv = {0};
std::array<epoll_event, 5> epollEvents;
epoll = epoll_create1(EPOLL_CLOEXEC);
if (epoll < 0) {
diesys("epoll_create1");
}
int sfd = signalfd(-1, &blockedsigs, SFD_CLOEXEC | SFD_NONBLOCK); int sfd = signalfd(-1, &blockedsigs, SFD_CLOEXEC | SFD_NONBLOCK);
if (sfd < 0) { if (sfd < 0) {
perror("signalfd"); diesys("signalfd");
cleanup(); }
exit(1); epollEv.events = EPOLLIN;
epollEv.data.fd = sfd;
if (epoll_ctl(epoll, EPOLL_CTL_ADD, sfd, &epollEv) < 0) {
diesys("epoll_ctl add signalfd");
} }
QSocketNotifier signalNotifier {sfd, QSocketNotifier::Read};
QObject::connect(&signalNotifier, &QSocketNotifier::activated, []() { quitting = true; });
display = wl_display_connect(NULL); display = wl_display_connect(nullptr);
if (!display) { if (!display) {
fprintf(stderr, "Failed to connect to Wayland display\n"); die("Failed to connect to Wayland display");
return 1;
} }
displayFd = wl_display_get_fd(display);
auto registry = wl_display_get_registry(display); auto registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, nullptr); wl_registry_add_listener(registry, &registry_listener, nullptr);
wl_display_roundtrip(display); wl_display_roundtrip(display);
onReady(); onReady();
QSocketNotifier displayReadNotifier(wl_display_get_fd(display), QSocketNotifier::Read); epollEv.events = EPOLLIN;
displayReadNotifier.setEnabled(true); epollEv.data.fd = displayFd;
QObject::connect(&displayReadNotifier, &QSocketNotifier::activated, [=]() { if (epoll_ctl(epoll, EPOLL_CTL_ADD, displayFd, &epollEv) < 0) {
auto res = wl_display_dispatch(display); diesys("epoll_ctl add wayland_display");
if (res < 0) {
perror("wl_display_dispatch");
cleanup();
exit(1);
} }
});
displayWriteNotifier = new QSocketNotifier(wl_display_get_fd(display), QSocketNotifier::Write);
displayWriteNotifier->setEnabled(false);
QObject::connect(displayWriteNotifier, &QSocketNotifier::activated, waylandWriteReady);
while (!quitting) { while (!quitting) {
waylandFlush(); waylandFlush();
app.processEvents(QEventLoop::WaitForMoreEvents); auto res = epoll_wait(epoll, epollEvents.data(), epollEvents.size(), -1);
if (res < 0) {
if (errno != EINTR) {
diesys("epoll_wait");
}
} else {
for (auto i=0; i<res; i++) {
auto &ev = epollEvents[i];
if (ev.data.fd == displayFd) {
if (ev.events & EPOLLIN) {
if (wl_display_dispatch(display) < 0) {
die("wl_display_dispatch");
}
} if (ev.events & EPOLLOUT) {
epollEv.events = EPOLLIN;
epollEv.data.fd = displayFd;
if (epoll_ctl(epoll, EPOLL_CTL_MOD, displayFd, &epollEv) < 0) {
diesys("epoll_ctl");
}
waylandFlush();
}
} else if (ev.data.fd == statusFifoFd) {
onStatus();
} else if (ev.data.fd == sfd) {
quitting = true;
}
}
}
} }
cleanup(); cleanup();
} }
void die(const char *why) {
fprintf(stderr, "%s\n", why);
cleanup();
exit(1);
}
void diesys(const char *why) {
perror(why);
cleanup();
exit(1);
}
void cleanup() { void cleanup() {
if (!statusFifoName.empty()) { if (!statusFifoName.empty()) {
unlink(statusFifoName.c_str()); unlink(statusFifoName.c_str());
@ -397,14 +434,15 @@ void waylandFlush()
{ {
wl_display_dispatch_pending(display); wl_display_dispatch_pending(display);
if (wl_display_flush(display) < 0 && errno == EAGAIN) { if (wl_display_flush(display) < 0 && errno == EAGAIN) {
displayWriteNotifier->setEnabled(true); epoll_event ev = {0};
ev.events = EPOLLIN | EPOLLOUT;
ev.data.fd = displayFd;
if (epoll_ctl(epoll, EPOLL_CTL_MOD, displayFd, &ev) < 0) {
diesys("epoll_ctl");
}
} }
} }
static void waylandWriteReady()
{
displayWriteNotifier->setEnabled(false);
waylandFlush();
}
static void requireGlobal(const void *p, const char *name) static void requireGlobal(const void *p, const char *name)
{ {
if (p) return; if (p) return;