diff --git a/CMakeLists.txt b/CMakeLists.txt index b51b074..2055edf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 2.4) project(sigstoped) -set(SRC_FILES main.cpp ) +set(SRC_FILES main.cpp process.cpp xinstance.cpp) set(LIBS -lX11) add_executable(${PROJECT_NAME} ${SRC_FILES}) diff --git a/debug.h b/debug.h new file mode 100644 index 0000000..9bc045f --- /dev/null +++ b/debug.h @@ -0,0 +1,10 @@ +#pragma once +#include +#include + +constexpr bool DEBUG = false; + +inline void debug(const std::string& in) +{ + if constexpr (DEBUG) std::cout< #include #include -#include #include #include #include -#include #include #include -#include #include #include -#include #include +#include #include +#include +#include +#include -struct Atoms +#include "xinstance.h" +#include "process.h" +#include "split.h" +#include "debug.h" + +Window intraCommesWindow; +XInstance xinstance; +volatile bool stop = false; +volatile bool configStale = false; + +void sigTerm(int dummy) { - Atom netActiveWindow = 0; - Atom netWmPid = 0; - Atom wmClientMachine = 0; -}; - -class XInstance -{ - -public: - - Atoms atoms; - - static constexpr unsigned long MAX_BYTES = 1048576; - - int screen = 0; - Display *display = nullptr; - -private: - unsigned long readProparty(Window wid, Atom atom, unsigned char** prop, int* format) - { - Atom returnedAtom; - unsigned long nitems; - unsigned long bytes_after; - - int ret = XGetWindowProperty( - display, - wid, - atom, - 0, XInstance::MAX_BYTES/4, false, - AnyPropertyType, - &returnedAtom, - format, - &nitems, - &bytes_after, - prop); - if (ret != Success) - { - std::cerr<<"XGetWindowProperty failed!\n"; - return 0; - } - else return std::min((*format)/8*nitems, XInstance::MAX_BYTES); - } - - Atom getAtom(const std::string& atomName) - { - return XInternAtom(display, atomName.c_str(), true); - } - -public: - bool open(const std::string& xDisplayName) - { - display = XOpenDisplay(xDisplayName.c_str()); - if (display == nullptr) - { - std::cerr<<"Can not open display "<(data); - XFree(data); - return wid; - } - pid_t getPid(Window wid) - { - XTextProperty xWidHostNameTextProperty; - bool ret = XGetTextProperty(display, wid, &xWidHostNameTextProperty, atoms.wmClientMachine); - if (!ret) - { - char errorString[1024]; - XGetErrorText(display, ret, errorString, 1024); - - std::cerr<<"XGetWMClientMachine failed! "<(data); - XFree(data); - - } - else - { - std::cout<<"Window "< split(const std::string& in, char delim = '\n' ) -{ - std::stringstream ss(in); - std::vector tokens; - std::string temp_str; - while(std::getline(ss, temp_str, delim)) - { - tokens.push_back(temp_str); - } - return tokens; + 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); + xinstance.flush(); } -class Process +void sigUser1(int dummy) { -public: - pid_t pid = -1; - std::string name; - - bool operator==(const Process& in) - { - return pid == in.pid; - } - bool operator!=(const Process& in) - { - return pid != in.pid; - } - Process(){} - Process(pid_t pidIn) - { - std::fstream statusFile; - std::string statusString; - statusFile.open(std::string("/proc/") + std::to_string(pidIn)+ "/status", std::fstream::in); - if(statusFile.is_open()) - { - std::string statusString((std::istreambuf_iterator(statusFile)), - std::istreambuf_iterator()); - std::vector lines = split(statusString); - if(lines.size() > 0) - { - pid = pidIn; - std::vector tokens = split(lines[0], '\t'); - if(tokens.size() > 1) - { - name = tokens[1]; - } - } - statusFile.close(); - - } - else - { - std::cout<<"cant open "<<"/proc/" + std::to_string(pidIn)+ "/status"< getBlacklist() { - XInstance xinstance; - const char* homeDir = getenv("HOME"); if(homeDir == nullptr) { std::cerr<<"HOME enviroment variable must be set.\n"; - return 1; - } - - std::fstream blacklistFile; - blacklistFile.open(std::string(homeDir)+"/.config/sigstoped/blacklist", std::fstream::in); - std::string blacklistString; - if(!blacklistFile.is_open()) - { - std::cout<(blacklistFile)), - std::istreambuf_iterator()); + const std::string configDir(std::string(homeDir)+"/.config/sigstoped/"); + std::fstream blacklistFile; + blacklistFile.open(configDir+"blacklist", std::fstream::in); + std::string blacklistString; + if(!blacklistFile.is_open()) + { + std::cout<(blacklistFile)), + std::istreambuf_iterator()); + } + return split(blacklistString); } - - std::vector applicationNames = split(blacklistString); + return std::vector(); +} + +int main(int argc, char* argv[]) +{ + std::vector applicationNames = getBlacklist(); + if(applicationNames.size() == 0) std::cout<<"WARNIG: no application names configured.\n"; struct stat sb; if( stat("/proc/version", &sb) != 0) @@ -251,14 +100,25 @@ int main(int argc, char* argv[]) return 1; } + std::list stoppedPids; + if(!xinstance.open(xDisplayName)) exit(1); - XSelectInput(xinstance.display, RootWindow(xinstance.display, xinstance.screen), PropertyChangeMask | StructureNotifyMask); + intraCommesWindow = XCreateSimpleWindow(xinstance.display, + RootWindow(xinstance.display, xinstance.screen), + 10, 10, 10, 10, 0, 0, 0); + XSelectInput(xinstance.display, intraCommesWindow, StructureNotifyMask); + XSelectInput(xinstance.display, RootWindow(xinstance.display, xinstance.screen), PropertyChangeMask | StructureNotifyMask); XEvent event; Process prevProcess; Window prevWindow = 0; - while(true) + + signal(SIGINT, sigTerm); + signal(SIGTERM, sigTerm); + signal(SIGUSR1, sigUser1); + + while(!stop) { XNextEvent(xinstance.display, &event); if (event.type == DestroyNotify) break; @@ -269,28 +129,60 @@ int main(int argc, char* argv[]) { pid_t windowPid = xinstance.getPid(wid); Process process(windowPid); + std::cout<<"Active window: "< 0) { kill(process.pid, SIGCONT); - std::cout<<"Resumeing: "< 0) { - kill(prevProcess.pid, SIGSTOP); - std::cout<<"Stoping: "< tlWindows = xinstance.getTopLevelWindows(); + for(auto& window : tlWindows) + { + if(xinstance.getPid(window) == prevProcess.pid) hasTopLevelWindow = true; + } + if(hasTopLevelWindow) + { + kill(prevProcess.pid, SIGSTOP); + std::cout<<"Stoping wid: "+std::to_string(prevWindow)+" pid: "+std::to_string(prevProcess.pid)+" name: "+prevProcess.name<<'\n'; + stoppedPids.push_back(prevProcess.pid); + } + else std::cout<<"not Stoping wid: "+std::to_string(prevWindow)+" pid: "+std::to_string(prevProcess.pid)+" name: "+prevProcess.name<<'\n'; } } } prevProcess = process; prevWindow = wid; - std::cout<<"Active window: "< +#include +#include +#include +#include "process.h" +#include "split.h" + +bool Process::operator==(const Process& in) +{ + return pid == in.pid; +} + +bool Process::operator!=(const Process& in) +{ + return pid != in.pid; +} + +Process::Process(pid_t pidIn) +{ + if(pidIn > 0) + { + std::fstream statusFile; + std::string statusString; + statusFile.open(std::string("/proc/") + std::to_string(pidIn)+ "/status", std::fstream::in); + if(statusFile.is_open()) + { + std::string statusString((std::istreambuf_iterator(statusFile)), + std::istreambuf_iterator()); + std::vector lines = split(statusString); + if(lines.size() > 0) + { + pid = pidIn; + std::vector tokens = split(lines[0], '\t'); + if(tokens.size() > 1) + { + name = tokens[1]; + } + } + statusFile.close(); + + } + else + { + std::cout<<"cant open "<<"/proc/" + std::to_string(pidIn)+ "/status"< +#include + +class Process +{ +public: + pid_t pid = -1; + std::string name; + + bool operator==(const Process& in); + bool operator!=(const Process& in); + Process(){} + Process(pid_t pidIn); +}; diff --git a/split.h b/split.h new file mode 100644 index 0000000..f4abbca --- /dev/null +++ b/split.h @@ -0,0 +1,15 @@ +#pragma once +#include +#include + +inline std::vector split(const std::string& in, char delim = '\n' ) +{ + std::stringstream ss(in); + std::vector tokens; + std::string temp_str; + while(std::getline(ss, temp_str, delim)) + { + tokens.push_back(temp_str); + } + return tokens; +} diff --git a/xinstance.cpp b/xinstance.cpp new file mode 100644 index 0000000..21fcf3c --- /dev/null +++ b/xinstance.cpp @@ -0,0 +1,154 @@ +#include "xinstance.h" +#include +#include +#include +#include +#include +#include "debug.h" + +unsigned long XInstance::readProparty(Window wid, Atom atom, unsigned char** prop, int* format) +{ + Atom returnedAtom; + unsigned long nitems; + unsigned long bytes_after; + + int ret = XGetWindowProperty( + display, + wid, + atom, + 0, XInstance::MAX_BYTES/4, false, + AnyPropertyType, + &returnedAtom, + format, + &nitems, + &bytes_after, + prop); + if (ret != Success) + { + std::cerr<<"XGetWindowProperty failed!\n"; + return 0; + } + else return std::min((*format)/8*nitems, XInstance::MAX_BYTES); +} + +Atom XInstance::getAtom(const std::string& atomName) +{ + return XInternAtom(display, atomName.c_str(), true); +} + +bool XInstance::open(const std::string& xDisplayName) +{ + display = XOpenDisplay(xDisplayName.c_str()); + if (display == nullptr) + { + std::cerr<<"Can not open display "<(data); + XFree(data); + return wid; +} + +std::vector XInstance::getTopLevelWindows() +{ + Window root_return; + Window parent_return; + 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) + { + out.push_back(windows[i]); + } + return out; +} + +void XInstance::flush() +{ + XFlush(display); +} + +pid_t XInstance::getPid(Window wid) +{ + XTextProperty xWidHostNameTextProperty; + bool ret = XGetTextProperty(display, wid, &xWidHostNameTextProperty, atoms.wmClientMachine); + if (!ret) + { + char errorString[1024]; + XGetErrorText(display, ret, errorString, 1024); + debug("XGetWMClientMachine failed! " + std::string(errorString)); + return -1; + } + char** xWidHostNameStringList; + int nStrings; + ret = XTextPropertyToStringList(&xWidHostNameTextProperty, &xWidHostNameStringList, &nStrings); + if (!ret || nStrings == 0) + { + char errorString[1024]; + XGetErrorText(display, ret, errorString, 1024); + debug("XTextPropertyToStringList failed! " + std::string(errorString)); + return -1; + } + char hostName[HOST_NAME_MAX+1]={0}; + if(gethostname(hostName, HOST_NAME_MAX) != 0) + { + debug("Can't get host name"); + return -1; + } + pid_t pid = -1; + if(strcmp(hostName, xWidHostNameStringList[0]) == 0) + { + unsigned char* data = nullptr; + int format; + unsigned long length = readProparty(wid, atoms.netWmPid, &data, &format); + if(format == 32 && length == 4) pid = *reinterpret_cast(data); + XFree(data); + + } + else + { + debug("Window "+std::to_string(wid)+" is a remote window"); + } + XFreeStringList(xWidHostNameStringList); + return pid; +} + + XInstance::~XInstance() + { + if(display) XCloseDisplay(display); + } diff --git a/xinstance.h b/xinstance.h new file mode 100644 index 0000000..c9b57f2 --- /dev/null +++ b/xinstance.h @@ -0,0 +1,37 @@ +#pragma once +#include +#include +#include + +struct Atoms +{ + Atom netActiveWindow = 0; + Atom netWmPid = 0; + Atom wmClientMachine = 0; +}; + +class XInstance +{ + +public: + + static constexpr unsigned long MAX_BYTES = 1048576; + + Atoms atoms; + int screen = 0; + Display *display = nullptr; + +private: + + unsigned long readProparty(Window wid, Atom atom, unsigned char** prop, int* format); + Atom getAtom(const std::string& atomName); + +public: + + ~XInstance(); + bool open(const std::string& xDisplayName); + Window getActiveWindow(); + pid_t getPid(Window wid); + std::vector getTopLevelWindows(); + void flush(); +};