From 6cbf940f8d89887a1e550a1aeec90c9145ce4df4 Mon Sep 17 00:00:00 2001 From: Carl Klemm Date: Mon, 15 Jun 2020 16:39:12 +0200 Subject: [PATCH 01/10] version bump --- debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index f098a16..6596d44 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,3 @@ -sigstoped (1:0.73-1) unstable; urgency=medium +sigstoped (1:1.0.2-1) unstable; urgency=medium Inital version -- Uvos Mon, 10 Jun 2020 15:00:00 +0100 From e1253286e40a79e6890d5a149830b05f739d8598 Mon Sep 17 00:00:00 2001 From: Carl Klemm Date: Mon, 15 Jun 2020 23:43:56 +0200 Subject: [PATCH 02/10] Ignore BadWindow errors caused by faulty __NET_ACTIVE_WINDOW events --- xinstance.cpp | 27 ++++++++++++++++++++++++--- xinstance.h | 2 ++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/xinstance.cpp b/xinstance.cpp index c9701e0..836eb64 100644 --- a/xinstance.cpp +++ b/xinstance.cpp @@ -124,6 +124,7 @@ void XInstance::flush() pid_t XInstance::getPid(Window wid) { + defaultHandler = XSetErrorHandler(ignoreErrorHandler); XTextProperty xWidHostNameTextProperty; bool ret; ret = XGetTextProperty(display, wid, &xWidHostNameTextProperty, atoms.wmClientMachine); @@ -132,7 +133,11 @@ pid_t XInstance::getPid(Window wid) char errorString[1024]; XGetErrorText(display, ret, errorString, 1024); debug("XGetWMClientMachine failed! " + std::string(errorString)); - if(!ignoreClientMachine) return -1; + if(!ignoreClientMachine) + { + XSetErrorHandler(defaultHandler); + return -1; + } } char** xWidHostNameStringList = nullptr; int nStrings; @@ -142,13 +147,21 @@ pid_t XInstance::getPid(Window wid) char errorString[1024]; XGetErrorText(display, ret, errorString, 1024); debug("XTextPropertyToStringList failed! " + std::string(errorString)); - if(!ignoreClientMachine) return -1; + if(!ignoreClientMachine) + { + XSetErrorHandler(defaultHandler); + return -1; + } } char hostName[HOST_NAME_MAX+1]={0}; if(gethostname(hostName, HOST_NAME_MAX) != 0) { debug("Can't get host name"); - if(!ignoreClientMachine) return -1; + if(!ignoreClientMachine) + { + XSetErrorHandler(defaultHandler); + return -1; + } } pid_t pid = -1; if(ignoreClientMachine || strcmp(hostName, xWidHostNameStringList[0]) == 0 ) @@ -165,9 +178,17 @@ pid_t XInstance::getPid(Window wid) debug("Window "+std::to_string(wid)+" is a remote window"); } if(xWidHostNameStringList) XFreeStringList(xWidHostNameStringList); + XSetErrorHandler(defaultHandler); return pid; } +int XInstance::ignoreErrorHandler(Display* display, XErrorEvent* xerror) +{ + std::cerr<<"Ignoring: error code"<error_code<<" request code "<request_code<<'\n' + <<"this error most likely occured because of a bug in your WM\n"; + return 0; +} + XInstance::~XInstance() { if(display) XCloseDisplay(display); diff --git a/xinstance.h b/xinstance.h index 5bced7f..dc69403 100644 --- a/xinstance.h +++ b/xinstance.h @@ -34,6 +34,7 @@ class XInstance public: + inline static XErrorHandler defaultHandler; static constexpr unsigned long MAX_BYTES = 1048576; inline static bool ignoreClientMachine = false; @@ -46,6 +47,7 @@ private: unsigned long readProparty(Window wid, Atom atom, unsigned char** prop, int* format); Atom getAtom(const std::string& atomName); + static int ignoreErrorHandler(Display* display, XErrorEvent* xerror); public: From bdcd27eb953bc71ec30b1ff67e769da2c76512a6 Mon Sep 17 00:00:00 2001 From: Carl Klemm Date: Mon, 15 Jun 2020 23:49:00 +0200 Subject: [PATCH 03/10] version bump --- argpopt.h | 2 +- debian/changelog | 4 ++++ main.cpp | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/argpopt.h b/argpopt.h index 0e924ad..9be6d2c 100644 --- a/argpopt.h +++ b/argpopt.h @@ -25,7 +25,7 @@ struct Config bool ignoreClientMachine = false; }; -const char *argp_program_version = "1.0.2"; +const char *argp_program_version = "1.0.3"; const char *argp_program_bug_address = ""; static char doc[] = "Deamon that stops programms via SIGSTOP when their X11 windows lose focus."; static char args_doc[] = ""; diff --git a/debian/changelog b/debian/changelog index 7a7aaee..80f8af4 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,7 @@ +sigstoped (1.0.3) unstable; urgency=medium + Ignore BadWindow errors caused by faulty __NET_ACTIVE_WINDOW events + -- Uvos Mon, 15 Jun 2020 23:47:00 +0100 + sigstoped (1.0.2) unstable; urgency=medium Inital version -- Uvos Mon, 10 Jun 2020 15:00:00 +0100 diff --git a/main.cpp b/main.cpp index ff8a5f2..a14b2ef 100644 --- a/main.cpp +++ b/main.cpp @@ -159,7 +159,7 @@ int main(int argc, char* argv[]) std::string confDir = getConfdir(); if(confDir.size() == 0) return 1; - if(!createPidFile(confDir+"pidfile")); + if(!createPidFile(confDir+"pidfile")) return 1; std::vector applicationNames = getApplicationlist(confDir+"blacklist"); From 8c69a02e5d4c0624e7c4c4d45441e469ae999c21 Mon Sep 17 00:00:00 2001 From: Carl Klemm Date: Tue, 16 Jun 2020 09:54:22 +0200 Subject: [PATCH 04/10] Fix memory leak in XInstance::getTopLevelWindows --- argpopt.h | 2 +- debian/changelog | 4 ++++ xinstance.cpp | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/argpopt.h b/argpopt.h index 9be6d2c..4ab2569 100644 --- a/argpopt.h +++ b/argpopt.h @@ -25,7 +25,7 @@ struct Config bool ignoreClientMachine = false; }; -const char *argp_program_version = "1.0.3"; +const char *argp_program_version = "1.0.4"; const char *argp_program_bug_address = ""; static char doc[] = "Deamon that stops programms via SIGSTOP when their X11 windows lose focus."; static char args_doc[] = ""; diff --git a/debian/changelog b/debian/changelog index 80f8af4..c9fa8bc 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,7 @@ +sigstoped (1.0.4) unstable; urgency=medium + Fix memory leak in XInstance::getTopLevelWindows() + -- Uvos Mon, 16 Jun 2020 09:47:00 +0100 + sigstoped (1.0.3) unstable; urgency=medium Ignore BadWindow errors caused by faulty __NET_ACTIVE_WINDOW events -- Uvos Mon, 15 Jun 2020 23:47:00 +0100 diff --git a/xinstance.cpp b/xinstance.cpp index 836eb64..c785a97 100644 --- a/xinstance.cpp +++ b/xinstance.cpp @@ -107,13 +107,13 @@ std::vector XInstance::getTopLevelWindows() Window* windows = nullptr; unsigned int nwindows; XQueryTree(display, RootWindow(display, screen), &root_return, &parent_return, &windows, &nwindows); - std::vector out; out.reserve(nwindows); - for(unsigned int i; i < nwindows; ++i) + for(unsigned int i = 0; i < nwindows; ++i) { out.push_back(windows[i]); } + if(windows != nullptr) XFree(windows); return out; } From 21d5c9475b674d91cf0f9df9d7715cf28c785f1a Mon Sep 17 00:00:00 2001 From: uvos Date: Wed, 1 Jul 2020 19:21:27 +0200 Subject: [PATCH 05/10] Configureable timout for applications to close their windows. Fix 5sec stall when switching from an application to be stopped to an application that was stopped before. Make Xinstance thread safe --- CMakeLists.txt | 4 +- argpopt.h | 5 ++ main.cpp | 122 ++++++++++++++++++++++++++++++++++--------------- process.cpp | 11 +++-- xinstance.cpp | 25 ++++++++-- 5 files changed, 120 insertions(+), 47 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 54eff76..a873ff0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 2.4) project(sigstoped) -set(SRC_FILES main.cpp process.cpp xinstance.cpp) -set(LIBS -lX11) +set(SRC_FILES main.cpp process.cpp xinstance.cpp CppTimer.cpp) +set(LIBS -lX11 -lrt) add_executable(${PROJECT_NAME} ${SRC_FILES}) diff --git a/argpopt.h b/argpopt.h index 4ab2569..25788c4 100644 --- a/argpopt.h +++ b/argpopt.h @@ -23,6 +23,7 @@ struct Config { bool ignoreClientMachine = false; + int timeoutSecs = 10; }; const char *argp_program_version = "1.0.4"; @@ -33,6 +34,7 @@ static char args_doc[] = ""; static struct argp_option options[] = { {"ignore-client-machine", 'i', 0, 0, "Also stop programs associated with windows that fail to set WM_CLIENT_MACHINE" }, + {"timout", 't', "seconds", 0, "Timeout to give program to close its last window before stoping it" }, { 0 } }; @@ -45,6 +47,9 @@ error_t parse_opt (int key, char *arg, struct argp_state *state) case 'i': config->ignoreClientMachine = true; break; + case 't': + config->timeoutSecs = atol(arg); + break; default: return ARGP_ERR_UNKNOWN; } diff --git a/main.cpp b/main.cpp index a14b2ef..7404894 100644 --- a/main.cpp +++ b/main.cpp @@ -39,23 +39,27 @@ #include "split.h" #include "debug.h" #include "argpopt.h" +#include "CppTimer.h" Window intraCommesWindow; -XInstance xinstance; -volatile bool stop = false; volatile bool configStale = false; +XInstance xinstance; constexpr char configPrefix[] = "/.config/sigstoped/"; +constexpr char STOP_EVENT = 65; +constexpr char PROC_STOP_EVENT = 1; void sigTerm(int dummy) { - stop = true; - XClientMessageEvent dummyEvent; - memset(&dummyEvent, 0, sizeof(XClientMessageEvent)); - dummyEvent.type = ClientMessage; - dummyEvent.window = intraCommesWindow; - dummyEvent.format = 32; - XSendEvent(xinstance.display, intraCommesWindow, 0, 0, (XEvent*)&dummyEvent); + XClientMessageEvent event; + memset(&event, 0, sizeof(XClientMessageEvent)); + event.type = ClientMessage; + event.window = intraCommesWindow; + event.format = 8; + event.data.b[0] = STOP_EVENT; + XLockDisplay(xinstance.display); + XSendEvent(xinstance.display, intraCommesWindow, 0, 0, (XEvent*)&event); + XUnlockDisplay(xinstance.display); xinstance.flush(); } @@ -121,7 +125,7 @@ bool createPidFile(const std::string& fileName) { std::cerr<<"Only one " < tlWindows = xinstance->getTopLevelWindows(); + for(auto& window : tlWindows) + { + if(xinstance->getPid(window) == process.getPid()) hasTopLevelWindow = true; + } + if(hasTopLevelWindow) + { + process.stop(true); + std::cout<<"Stoping pid: "+std::to_string(process.getPid())+" name: "+process.getName()<<'\n'; + return true; + } + else + { + std::cout<<"not Stoping pid: "+std::to_string(process.getPid())+" name: "+process.getName()<<'\n'; + return false; + } +} + int main(int argc, char* argv[]) { + char* xDisplayName = std::getenv( "DISPLAY" ); + if(xDisplayName == nullptr) + { + std::cerr<<"DISPLAY enviroment variable must be set.\n"; + return 1; + } + + if(!xinstance.open(xDisplayName)) exit(1); + Config config; argp_parse(&argp, argc, argv, 0, 0, &config); - + if(config.ignoreClientMachine) { std::cout<<"WARNING: Ignoring WM_CLIENT_MACHINE is dangerous and may cause sigstoped to stop random pids if remote windows are present"< stoppedProcs; - if(!xinstance.open(xDisplayName)) exit(1); - intraCommesWindow = XCreateSimpleWindow(xinstance.display, RootWindow(xinstance.display, xinstance.screen), 10, 10, 10, 10, 0, 0, 0); @@ -190,6 +230,7 @@ int main(int argc, char* argv[]) XEvent event; Process prevProcess; + Process qeuedToStop; Window prevWindow = 0; signal(SIGINT, sigTerm); @@ -197,11 +238,13 @@ int main(int argc, char* argv[]) signal(SIGHUP, sigTerm); signal(SIGUSR1, sigUser1); - while(!stop) + CppTimer timer; + + while(true) { XNextEvent(xinstance.display, &event); if (event.type == DestroyNotify) break; - if (event.type == PropertyNotify && event.xproperty.atom == xinstance.atoms.netActiveWindow) + else if (event.type == PropertyNotify && event.xproperty.atom == xinstance.atoms.netActiveWindow) { Window wid = xinstance.getActiveWindow(); if(wid != 0 && wid != prevWindow) @@ -223,6 +266,12 @@ int main(int argc, char* argv[]) { if(process.getName() == applicationNames[i] && wid != 0 && process.getPid() > 0 && process.getName() != "") { + if(process == qeuedToStop) + { + std::cout<<"Canceling stop of wid: "+std::to_string(wid)+" pid: "+std::to_string(process.getPid())+" name: "+process.getName()<<'\n'; + timer.stop(); + qeuedToStop = Process(); + } process.resume(true); stoppedProcs.remove(process); std::cout<<"Resumeing wid: "+std::to_string(wid)+" pid: "+std::to_string(process.getPid())+" name: "+process.getName()<<'\n'; @@ -232,20 +281,11 @@ int main(int argc, char* argv[]) prevProcess.getName() != "" && prevProcess.getPid() > 0) { - sleep(5); //give the process some time to close its other windows - bool hasTopLevelWindow = false; - std::vector tlWindows = xinstance.getTopLevelWindows(); - for(auto& window : tlWindows) - { - if(xinstance.getPid(window) == prevProcess.getPid()) hasTopLevelWindow = true; - } - if(hasTopLevelWindow) - { - prevProcess.stop(true); - std::cout<<"Stoping wid: "+std::to_string(prevWindow)+" pid: "+std::to_string(prevProcess.getPid())+" name: "+prevProcess.getName()<<'\n'; - stoppedProcs.push_back(prevProcess); - } - else std::cout<<"not Stoping wid: "+std::to_string(prevWindow)+" pid: "+std::to_string(prevProcess.getPid())+" name: "+prevProcess.getName()<<'\n'; + timer.block(); + std::cout<<"Will stop pid: "<window == intraCommesWindow) + { + XClientMessageEvent* clientEvent = (XClientMessageEvent*)&event; + if (clientEvent->data.b[0] == STOP_EVENT) break; + if (clientEvent->data.b[0] == PROC_STOP_EVENT) + { + stopProcess(qeuedToStop, &xinstance); + qeuedToStop = Process(); + } + } } for(auto& process : stoppedProcs) process.resume(true); std::filesystem::remove(confDir+"pidfile"); diff --git a/process.cpp b/process.cpp index 1dbc56c..2c17bff 100644 --- a/process.cpp +++ b/process.cpp @@ -42,11 +42,14 @@ pid_t Process::getPid() void Process::stop(bool children) { - kill(pid_, SIGSTOP); - if(children) + if(pid_ > 0) { - std::vector children = getChildren(); - for(auto& child : children) child.stop(true); + kill(pid_, SIGSTOP); + if(children) + { + std::vector children = getChildren(); + for(auto& child : children) child.stop(true); + } } } diff --git a/xinstance.cpp b/xinstance.cpp index c785a97..8c024a7 100644 --- a/xinstance.cpp +++ b/xinstance.cpp @@ -30,7 +30,8 @@ unsigned long XInstance::readProparty(Window wid, Atom atom, unsigned char** pro Atom returnedAtom; unsigned long nitems; unsigned long bytes_after; - + + XLockDisplay(display); int ret = XGetWindowProperty( display, wid, @@ -42,6 +43,7 @@ unsigned long XInstance::readProparty(Window wid, Atom atom, unsigned char** pro &nitems, &bytes_after, prop); + XUnlockDisplay(display); if (ret != Success) { std::cerr<<"XGetWindowProperty failed!\n"; @@ -52,11 +54,12 @@ unsigned long XInstance::readProparty(Window wid, Atom atom, unsigned char** pro Atom XInstance::getAtom(const std::string& atomName) { - return XInternAtom(display, atomName.c_str(), true); + return XInternAtom(display, atomName.c_str(), true);; } bool XInstance::open(const std::string& xDisplayName) { + XInitThreads(); display = XOpenDisplay(xDisplayName.c_str()); if (display == nullptr) { @@ -96,7 +99,9 @@ Window XInstance::getActiveWindow() unsigned long length = readProparty(RootWindow(display, screen), atoms.netActiveWindow, &data, &format); Window wid = 0; if(format == 32 && length == 4) wid = *reinterpret_cast(data); + XLockDisplay(display); XFree(data); + XUnlockDisplay(display); return wid; } @@ -106,20 +111,26 @@ std::vector XInstance::getTopLevelWindows() Window parent_return; Window* windows = nullptr; unsigned int nwindows; + XLockDisplay(display); XQueryTree(display, RootWindow(display, screen), &root_return, &parent_return, &windows, &nwindows); + XUnlockDisplay(display); std::vector out; out.reserve(nwindows); for(unsigned int i = 0; i < nwindows; ++i) { out.push_back(windows[i]); } - if(windows != nullptr) XFree(windows); + XLockDisplay(display); + if(windows != nullptr) XFree(windows); + XUnlockDisplay(display); return out; } void XInstance::flush() { - XFlush(display); + XLockDisplay(display); + XFlush(display); + XUnlockDisplay(display); } pid_t XInstance::getPid(Window wid) @@ -127,7 +138,9 @@ pid_t XInstance::getPid(Window wid) defaultHandler = XSetErrorHandler(ignoreErrorHandler); XTextProperty xWidHostNameTextProperty; bool ret; - ret = XGetTextProperty(display, wid, &xWidHostNameTextProperty, atoms.wmClientMachine); + XLockDisplay(display); + ret = XGetTextProperty(display, wid, &xWidHostNameTextProperty, atoms.wmClientMachine); + XUnlockDisplay(display); if (!ret) { char errorString[1024]; @@ -191,5 +204,7 @@ int XInstance::ignoreErrorHandler(Display* display, XErrorEvent* xerror) XInstance::~XInstance() { + XLockDisplay(display); if(display) XCloseDisplay(display); + XUnlockDisplay(display); } From a0429b865bed0ed86eb1adf8abd03d6b107b3ab2 Mon Sep 17 00:00:00 2001 From: uvos Date: Wed, 1 Jul 2020 19:25:51 +0200 Subject: [PATCH 06/10] version bump --- argpopt.h | 2 +- debian/changelog | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/argpopt.h b/argpopt.h index 25788c4..ab3118d 100644 --- a/argpopt.h +++ b/argpopt.h @@ -26,7 +26,7 @@ struct Config int timeoutSecs = 10; }; -const char *argp_program_version = "1.0.4"; +const char *argp_program_version = "1.0.5"; const char *argp_program_bug_address = ""; static char doc[] = "Deamon that stops programms via SIGSTOP when their X11 windows lose focus."; static char args_doc[] = ""; diff --git a/debian/changelog b/debian/changelog index c9fa8bc..07b432a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +sigstoped (1.0.5) unstable; urgency=medium + Configureable timout for applications to close their windows. + Fix 5sec stall when switching from an application to be stopped to an application that was stopped before. + Make Xinstance thread safe + -- Uvos Wed, 01 Jul Jun 2020 09:47:00 +0100 + sigstoped (1.0.4) unstable; urgency=medium Fix memory leak in XInstance::getTopLevelWindows() -- Uvos Mon, 16 Jun 2020 09:47:00 +0100 From e8e469729a90189b3f0bd6db1f439209374ea3a7 Mon Sep 17 00:00:00 2001 From: uvos Date: Wed, 1 Jul 2020 19:45:25 +0200 Subject: [PATCH 07/10] add missing files to git --- CppTimer.cpp | 97 ++++++++++++++++++++++++++++++++++++++++++++++++ CppTimer.h | 85 ++++++++++++++++++++++++++++++++++++++++++ debian/changelog | 8 ++-- 3 files changed, 186 insertions(+), 4 deletions(-) create mode 100644 CppTimer.cpp create mode 100644 CppTimer.h diff --git a/CppTimer.cpp b/CppTimer.cpp new file mode 100644 index 0000000..a8c75d0 --- /dev/null +++ b/CppTimer.cpp @@ -0,0 +1,97 @@ +#include "CppTimer.h" +#include + +/** + * GNU GENERAL PUBLIC LICENSE + * Version 3, 29 June 2007 + * + * (C) 2020, Bernd Porr + * + * This is inspired by the timer_create man page. + **/ + +CppTimer::CppTimer() { + // We create a static handler catches the signal SIG + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = handler; + sigemptyset(&sa.sa_mask); + if (sigaction(SIG, &sa, NULL) == -1) + throw("Could not create signal handler"); + + if(pipe(pipeFd) < 0) + throw("Could not create pipe"); + + // Create the timer + sev.sigev_notify = SIGEV_SIGNAL; + sev.sigev_signo = SIG; + // Cruical is that the signal carries the pointer to this class instance here + // because the handler just handles anything that comes in! + sev.sigev_value.sival_ptr = this; + // create the timer + if (timer_create(CLOCKID, &sev, &timerid) == -1) + throw("Could not create timer"); +}; + +void CppTimer::start(long nanosecs,std::function callbackIn, int type) { + switch(type){ + case(PERIODIC): + //starts after specified period of nanoseconds + its.it_value.tv_sec = nanosecs / 1000000000; + its.it_value.tv_nsec = nanosecs % 1000000000; + its.it_interval.tv_sec = nanosecs / 1000000000; + its.it_interval.tv_nsec = nanosecs % 1000000000; + break; + case(ONESHOT): + //fires once after specified period of nanoseconds + its.it_value.tv_sec = nanosecs / 1000000000; + its.it_value.tv_nsec = nanosecs % 1000000000; + its.it_interval.tv_sec = 0; + its.it_interval.tv_nsec = 0; + break; + } + callback = callbackIn; + if (timer_settime(timerid, 0, &its, NULL) == -1) + throw("Could not start timer"); + discardPipe(); + running = true; +} + +void CppTimer::discardPipe() { + char buf; + fcntl(pipeFd[0], F_SETFL, O_NONBLOCK); + while(read(pipeFd[0], &buf, 1) > 0); + fcntl(pipeFd[0], F_SETFL, 0); +} + +void CppTimer::block() +{ + if(running) + { + char buf; + read(pipeFd[0], &buf, 1); + } +} + +void CppTimer::stop() { + // disarm + struct itimerspec itsnew; + itsnew.it_value.tv_sec = 0; + itsnew.it_value.tv_nsec = 0; + itsnew.it_interval.tv_sec = 0; + itsnew.it_interval.tv_nsec = 0; + timer_settime(timerid, 0, &itsnew, &its); + running = false; +} + +bool CppTimer::isRunning() +{ + return running; +} + +CppTimer::~CppTimer() { + stop(); + // delete the timer + timer_delete(timerid); + // default action for signal handling + signal(SIG, SIG_IGN); +} diff --git a/CppTimer.h b/CppTimer.h new file mode 100644 index 0000000..0465465 --- /dev/null +++ b/CppTimer.h @@ -0,0 +1,85 @@ +#ifndef __CPP_TIMER_H_ +#define __CPP_TIMER_H_ + +/** + * GNU GENERAL PUBLIC LICENSE + * Version 3, 29 June 2007 + * + * (C) 2020, Bernd Porr + * + * This is inspired by the timer_create man page. + **/ + +#include +#include +#include +#include +#include +#include +#include + +#define CLOCKID CLOCK_MONOTONIC +#define SIG SIGRTMIN + +/** + * Timer class which repeatedly fires. It's wrapper around the + * POSIX per-process timer. + **/ +class CppTimer { + +public: + static constexpr int PERIODIC = 0; + static constexpr int ONESHOT = 1; + static constexpr long MS_TO_NS = 1000000; + /** + * Creates an instance of the timer and connects the + * signal handler to the timer. + **/ + CppTimer(); + + /** + * Starts the timer. The timer fires first after + * the specified time in nanoseconds and then at + * that interval in PERIODIC mode. In ONESHOT mode + * the timer fires once after the specified time in + * nanoseconds. + **/ + virtual void start(long nanosecs, std::function callbackIn, int type = PERIODIC); + + /** + * Stops the timer by disarming it. It can be re-started + * with start(). + **/ + virtual void stop(); + + /** + * Destructor disarms the timer, deletes it and + * disconnect the signal handler. + **/ + virtual ~CppTimer(); + + bool isRunning(); + + void block(); + +private: + timer_t timerid = 0; + struct sigevent sev; + struct sigaction sa; + struct itimerspec its; + int pipeFd[2]; + std::atomic running = false; + std::function callback; + void discardPipe(); + + static void handler(int sig, siginfo_t *si, void *uc ) { + CppTimer *timer = reinterpret_cast (si->si_value.sival_ptr); + timer->callback(); + char buf = '\n'; + write(timer->pipeFd[1], &buf, 1); + timer->running = false; + } +}; + + +#endif diff --git a/debian/changelog b/debian/changelog index 07b432a..3a3e737 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,8 @@ sigstoped (1.0.5) unstable; urgency=medium - Configureable timout for applications to close their windows. - Fix 5sec stall when switching from an application to be stopped to an application that was stopped before. - Make Xinstance thread safe - -- Uvos Wed, 01 Jul Jun 2020 09:47:00 +0100 + Configureable timout for applications to close their windows. + Fix 5sec stall when switching from an application to be stopped to an application that was stopped before. + Make Xinstance thread safe +-- Uvos Mon, 16 Jun 2020 09:47:00 +0100 sigstoped (1.0.4) unstable; urgency=medium Fix memory leak in XInstance::getTopLevelWindows() From eb49f93e993eafee1277011966162846d0a6b518 Mon Sep 17 00:00:00 2001 From: uvos Date: Sat, 4 Jul 2020 11:50:46 +0200 Subject: [PATCH 08/10] prevent timer overflow on 32 bit systems --- CppTimer.cpp | 14 +++++++------- CppTimer.h | 2 +- main.cpp | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CppTimer.cpp b/CppTimer.cpp index a8c75d0..13cefb3 100644 --- a/CppTimer.cpp +++ b/CppTimer.cpp @@ -32,19 +32,19 @@ CppTimer::CppTimer() { throw("Could not create timer"); }; -void CppTimer::start(long nanosecs,std::function callbackIn, int type) { +void CppTimer::start(long secs, long nanosecs,std::function callbackIn, int type) { switch(type){ case(PERIODIC): //starts after specified period of nanoseconds - its.it_value.tv_sec = nanosecs / 1000000000; - its.it_value.tv_nsec = nanosecs % 1000000000; - its.it_interval.tv_sec = nanosecs / 1000000000; - its.it_interval.tv_nsec = nanosecs % 1000000000; + its.it_value.tv_sec = secs; + its.it_value.tv_nsec = nanosecs; + its.it_interval.tv_sec = secs; + its.it_interval.tv_nsec = nanosecs; break; case(ONESHOT): //fires once after specified period of nanoseconds - its.it_value.tv_sec = nanosecs / 1000000000; - its.it_value.tv_nsec = nanosecs % 1000000000; + its.it_value.tv_sec = secs; + its.it_value.tv_nsec = nanosecs; its.it_interval.tv_sec = 0; its.it_interval.tv_nsec = 0; break; diff --git a/CppTimer.h b/CppTimer.h index 0465465..c8cd191 100644 --- a/CppTimer.h +++ b/CppTimer.h @@ -44,7 +44,7 @@ public: * the timer fires once after the specified time in * nanoseconds. **/ - virtual void start(long nanosecs, std::function callbackIn, int type = PERIODIC); + virtual void start(long secs, long nanosecs, std::function callbackIn, int type = PERIODIC); /** * Stops the timer by disarming it. It can be re-started diff --git a/main.cpp b/main.cpp index 7404894..93f3611 100644 --- a/main.cpp +++ b/main.cpp @@ -284,7 +284,7 @@ int main(int argc, char* argv[]) timer.block(); std::cout<<"Will stop pid: "< Date: Sat, 4 Jul 2020 12:01:17 +0200 Subject: [PATCH 09/10] Version Bump --- argpopt.h | 2 +- debian/changelog | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/argpopt.h b/argpopt.h index ab3118d..571dae4 100644 --- a/argpopt.h +++ b/argpopt.h @@ -26,7 +26,7 @@ struct Config int timeoutSecs = 10; }; -const char *argp_program_version = "1.0.5"; +const char *argp_program_version = "1.0.6"; const char *argp_program_bug_address = ""; static char doc[] = "Deamon that stops programms via SIGSTOP when their X11 windows lose focus."; static char args_doc[] = ""; diff --git a/debian/changelog b/debian/changelog index 3a3e737..e1551a8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,7 +1,11 @@ +sigstoped (1.0.6) unstable; urgency=medium + Fix Timer overflow on 32bit devices +-- Uvos Sat, 04 Jul 2020 11:47:00 +0100 + sigstoped (1.0.5) unstable; urgency=medium - Configureable timout for applications to close their windows. - Fix 5sec stall when switching from an application to be stopped to an application that was stopped before. - Make Xinstance thread safe + Configureable timout for applications to close their windows. + Fix 5sec stall when switching from an application to be stopped to an application that was stopped before. + Make Xinstance thread safe -- Uvos Mon, 16 Jun 2020 09:47:00 +0100 sigstoped (1.0.4) unstable; urgency=medium From b74d9d84932397732aeacf1516d52b8b6bdf2f0b Mon Sep 17 00:00:00 2001 From: uvos Date: Thu, 20 Apr 2023 23:45:52 +0200 Subject: [PATCH 10/10] add copywrite --- debian/copywrite | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 debian/copywrite diff --git a/debian/copywrite b/debian/copywrite new file mode 100644 index 0000000..61721d5 --- /dev/null +++ b/debian/copywrite @@ -0,0 +1,2 @@ +Unless stated otherwise, all files are: +Copyright 2020 Carl Klemm and are licensed under the GPLv3