EPoll.h
author František Kučera <franta-hg@frantovo.cz>
Tue, 05 Dec 2023 22:55:25 +0100
branchv_0
changeset 20 0899e966993e
parent 0 bb715a82a8f1
permissions -rw-r--r--
process all X11 event waiting in the queue in the same cycle avoid so called glitches after continuous key press or X11 tunelled through SSH

/**
 * ShaderShark
 * Copyright © 2023 František Kučera (Frantovo.cz, GlobalCode.info)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <stdexcept>
#include <string>

#include <signal.h>
#include <sys/signalfd.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>

#include <stdexcept>
#include <string>

class EPoll {
private:
	int epollEventCount;
	epoll_event* epollEvents;
	int epollFD;

	void static log(const std::string& message) {
		std::cout << message << std::endl;
	}
public:

	class Exception : public std::logic_error {
	public:

		Exception(const std::string& msg) : logic_error(msg) {
		}

		Exception(const std::string& msg, int errNo) :
				logic_error(msg + strerror(errNo)) {
		}

	};

	EPoll(int eventCount = 5) : epollEventCount(eventCount) {
		epollEvents = (epoll_event*) malloc(sizeof (*epollEvents) * eventCount);
		epollFD = epoll_create1(0);
		log("EPoll(): epoll_create() = " + std::to_string(epollFD));
	}

	virtual ~EPoll() {
		::close(epollFD);
		free(epollEvents);
	}

	EPoll(const EPoll&) = delete;
	EPoll& operator=(const EPoll&) = delete;

	static void processSignal(int signal) {
		log("got signal: " + std::to_string(signal));
	}

	int addSignals() {
		::signal(SIGTERM, EPoll::processSignal);
		::signal(SIGINT, EPoll::processSignal);

		sigset_t mask;
		::sigemptyset(&mask);
		::sigaddset(&mask, SIGTERM);
		::sigaddset(&mask, SIGINT);
		int signalFD = ::signalfd(-1, &mask, 0);
		add(signalFD, EPOLLIN | EPOLLET | EPOLLHUP);
		log("signalFD=" + std::to_string(signalFD));
		return signalFD;
	}

	void add(int fd, uint32_t events = EPOLL_EVENTS::EPOLLIN) {
		struct epoll_event ev;
		ev.data.fd = fd;
		ev.events = events;
		int err = epoll_ctl(epollFD, EPOLL_CTL_ADD, ev.data.fd, &ev);
		if (err) throw Exception("EPoll::add() failed (a file?):", errno);
		// Regular files can not be monitored this way, thus:
		//   cat Makefile | opengl-pdf      # works
		//   opengl-pdf < Makefile          # does not work
	}

	void del(int fd) {
		int err = epoll_ctl(epollFD, EPOLL_CTL_DEL, fd, nullptr);
		if (err) throw Exception("EPoll::del() failed: ", errno);
	}

	int wait(int timeout = 1000) {
		int count = epoll_wait(epollFD, epollEvents, epollEventCount, timeout);
		if (count >= 0) return count;
		else throw Exception("EPoll::wait() failed: ", errno);
	}

	const epoll_event& operator[](std::size_t index) const {
		if (index < epollEventCount) return epollEvents[index];
		else throw std::out_of_range("invalid index of epollEvents");
	}

};