Split source into multiple files
SIGUSR1 now causes reload of blacklist sigstoped now resumes all processes it stoped upon reciving SIGINT or SIGTERM sigstoped avoids stopping processes that have no top level window left TODO: is this enough?
This commit is contained in:
@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 2.4)
|
|||||||
|
|
||||||
project(sigstoped)
|
project(sigstoped)
|
||||||
|
|
||||||
set(SRC_FILES main.cpp )
|
set(SRC_FILES main.cpp process.cpp xinstance.cpp)
|
||||||
set(LIBS -lX11)
|
set(LIBS -lX11)
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME} ${SRC_FILES})
|
add_executable(${PROJECT_NAME} ${SRC_FILES})
|
||||||
|
10
debug.h
Normal file
10
debug.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
constexpr bool DEBUG = false;
|
||||||
|
|
||||||
|
inline void debug(const std::string& in)
|
||||||
|
{
|
||||||
|
if constexpr (DEBUG) std::cout<<in<<'\n';
|
||||||
|
}
|
328
main.cpp
328
main.cpp
@ -2,240 +2,89 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <X11/Xutil.h>
|
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#include <X11/extensions/dpms.h>
|
#include <X11/extensions/dpms.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <cstring>
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <list>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
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;
|
stop = true;
|
||||||
Atom netWmPid = 0;
|
XClientMessageEvent dummyEvent;
|
||||||
Atom wmClientMachine = 0;
|
memset(&dummyEvent, 0, sizeof(XClientMessageEvent));
|
||||||
};
|
dummyEvent.type = ClientMessage;
|
||||||
|
dummyEvent.window = intraCommesWindow;
|
||||||
class XInstance
|
dummyEvent.format = 32;
|
||||||
{
|
XSendEvent(xinstance.display, intraCommesWindow, 0, 0, (XEvent*)&dummyEvent);
|
||||||
|
xinstance.flush();
|
||||||
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 "<<xDisplayName<<'\n';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
screen = XDefaultScreen(display);
|
|
||||||
|
|
||||||
atoms.netActiveWindow = getAtom("_NET_ACTIVE_WINDOW");
|
|
||||||
if(atoms.netActiveWindow == 0)
|
|
||||||
{
|
|
||||||
std::cerr<<"_NET_ACTIVE_WINDOW is required\n";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
atoms.netWmPid = getAtom("_NET_WM_PID");
|
|
||||||
if(atoms.netActiveWindow == 0)
|
|
||||||
{
|
|
||||||
std::cerr<<"_NET_WM_PID is required\n";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
atoms.wmClientMachine = getAtom("WM_CLIENT_MACHINE");
|
|
||||||
if(atoms.netActiveWindow == 0)
|
|
||||||
{
|
|
||||||
std::cerr<<" is required\n";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
Window getActiveWindow()
|
|
||||||
{
|
|
||||||
unsigned char* data = nullptr;
|
|
||||||
int format;
|
|
||||||
unsigned long length = readProparty(RootWindow(display, screen), atoms.netActiveWindow, &data, &format);
|
|
||||||
Window wid = 0;
|
|
||||||
if(format == 32 && length == 4) wid = *reinterpret_cast<Window*>(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! "<<errorString<<std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
char** xWidHostNameStringList;
|
|
||||||
int nStrings;
|
|
||||||
ret = XTextPropertyToStringList(&xWidHostNameTextProperty, &xWidHostNameStringList, &nStrings);
|
|
||||||
if (!ret || nStrings == 0)
|
|
||||||
{
|
|
||||||
std::cerr<<"XTextPropertyToStringList failed!\n";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
char hostName[HOST_NAME_MAX+1]={0};
|
|
||||||
if(gethostname(hostName, HOST_NAME_MAX) != 0)
|
|
||||||
{
|
|
||||||
std::cerr<<"Can't get host name\n";
|
|
||||||
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<pid_t*>(data);
|
|
||||||
XFree(data);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::cout<<"Window "<<wid<<" is a remote window\n";
|
|
||||||
}
|
|
||||||
XFreeStringList(xWidHostNameStringList);
|
|
||||||
return pid;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<std::string> split(const std::string& in, char delim = '\n' )
|
|
||||||
{
|
|
||||||
std::stringstream ss(in);
|
|
||||||
std::vector<std::string> tokens;
|
|
||||||
std::string temp_str;
|
|
||||||
while(std::getline(ss, temp_str, delim))
|
|
||||||
{
|
|
||||||
tokens.push_back(temp_str);
|
|
||||||
}
|
|
||||||
return tokens;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Process
|
void sigUser1(int dummy)
|
||||||
{
|
{
|
||||||
public:
|
configStale = true;
|
||||||
pid_t pid = -1;
|
}
|
||||||
std::string name;
|
|
||||||
|
|
||||||
bool operator==(const Process& in)
|
std::vector<std::string> getBlacklist()
|
||||||
{
|
|
||||||
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<char>(statusFile)),
|
|
||||||
std::istreambuf_iterator<char>());
|
|
||||||
std::vector<std::string> lines = split(statusString);
|
|
||||||
if(lines.size() > 0)
|
|
||||||
{
|
|
||||||
pid = pidIn;
|
|
||||||
std::vector<std::string> 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"<<std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
|
||||||
{
|
{
|
||||||
XInstance xinstance;
|
|
||||||
|
|
||||||
const char* homeDir = getenv("HOME");
|
const char* homeDir = getenv("HOME");
|
||||||
if(homeDir == nullptr)
|
if(homeDir == nullptr)
|
||||||
{
|
{
|
||||||
std::cerr<<"HOME enviroment variable must be set.\n";
|
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<<std::string(homeDir)+"/.config/sigstoped/blacklist must exsist\n";
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
blacklistString.assign((std::istreambuf_iterator<char>(blacklistFile)),
|
const std::string configDir(std::string(homeDir)+"/.config/sigstoped/");
|
||||||
std::istreambuf_iterator<char>());
|
std::fstream blacklistFile;
|
||||||
}
|
blacklistFile.open(configDir+"blacklist", std::fstream::in);
|
||||||
|
std::string blacklistString;
|
||||||
|
if(!blacklistFile.is_open())
|
||||||
|
{
|
||||||
|
std::cout<<configDir+"blacklist dose not exist\n";
|
||||||
|
|
||||||
std::vector<std::string> applicationNames = split(blacklistString);
|
if(!std::filesystem::create_directory(configDir, homeDir))
|
||||||
|
{
|
||||||
|
std::cout<<"Can't create "<<configDir<<'\n';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
blacklistFile.open(configDir+"blacklist", std::fstream::out);
|
||||||
|
if(blacklistFile.is_open()) blacklistFile.close();
|
||||||
|
else std::cout<<"Can't create "<<configDir<<"blacklist \n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
blacklistString.assign((std::istreambuf_iterator<char>(blacklistFile)),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
}
|
||||||
|
return split(blacklistString);
|
||||||
|
}
|
||||||
|
return std::vector<std::string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
std::vector<std::string> applicationNames = getBlacklist();
|
||||||
|
if(applicationNames.size() == 0) std::cout<<"WARNIG: no application names configured.\n";
|
||||||
|
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
if( stat("/proc/version", &sb) != 0)
|
if( stat("/proc/version", &sb) != 0)
|
||||||
@ -251,14 +100,25 @@ int main(int argc, char* argv[])
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::list<pid_t> stoppedPids;
|
||||||
|
|
||||||
if(!xinstance.open(xDisplayName)) exit(1);
|
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;
|
XEvent event;
|
||||||
Process prevProcess;
|
Process prevProcess;
|
||||||
Window prevWindow = 0;
|
Window prevWindow = 0;
|
||||||
while(true)
|
|
||||||
|
signal(SIGINT, sigTerm);
|
||||||
|
signal(SIGTERM, sigTerm);
|
||||||
|
signal(SIGUSR1, sigUser1);
|
||||||
|
|
||||||
|
while(!stop)
|
||||||
{
|
{
|
||||||
XNextEvent(xinstance.display, &event);
|
XNextEvent(xinstance.display, &event);
|
||||||
if (event.type == DestroyNotify) break;
|
if (event.type == DestroyNotify) break;
|
||||||
@ -269,28 +129,60 @@ int main(int argc, char* argv[])
|
|||||||
{
|
{
|
||||||
pid_t windowPid = xinstance.getPid(wid);
|
pid_t windowPid = xinstance.getPid(wid);
|
||||||
Process process(windowPid);
|
Process process(windowPid);
|
||||||
|
std::cout<<"Active window: "<<wid<<" pid: "<<process.pid<<" name: "<<process.name<<'\n';
|
||||||
|
|
||||||
if(process != prevProcess)
|
if(process != prevProcess)
|
||||||
{
|
{
|
||||||
|
if(configStale)
|
||||||
|
{
|
||||||
|
applicationNames = getBlacklist();
|
||||||
|
configStale = false;
|
||||||
|
for(auto& pid : stoppedPids)
|
||||||
|
{
|
||||||
|
kill(pid, SIGCONT);
|
||||||
|
debug("Resumeing pid: "+std::to_string(pid));
|
||||||
|
}
|
||||||
|
stoppedPids.clear();
|
||||||
|
}
|
||||||
for(size_t i = 0; i < applicationNames.size(); ++i)
|
for(size_t i = 0; i < applicationNames.size(); ++i)
|
||||||
{
|
{
|
||||||
if(process.name == applicationNames[i] && process.name != "" && wid != 0)
|
if(process.name == applicationNames[i] && process.name != "" && wid != 0 && process.pid > 0)
|
||||||
{
|
{
|
||||||
kill(process.pid, SIGCONT);
|
kill(process.pid, SIGCONT);
|
||||||
std::cout<<"Resumeing: "<<wid<<" pid: "<<process.pid<<" name: "<<process.name<<'\n';
|
stoppedPids.remove(process.pid);
|
||||||
|
std::cout<<"Resumeing wid: "+std::to_string(wid)+" pid: "+std::to_string(process.pid)+" name: "+process.name<<'\n';
|
||||||
}
|
}
|
||||||
else if(prevProcess.name == applicationNames[i] && prevWindow != 0 && prevProcess.name != "")
|
else if(prevProcess.name == applicationNames[i] &&
|
||||||
|
prevWindow != 0 &&
|
||||||
|
prevProcess.name != "" &&
|
||||||
|
prevProcess.pid > 0)
|
||||||
{
|
{
|
||||||
kill(prevProcess.pid, SIGSTOP);
|
sleep(0.5); //give the process some time to close its other windows
|
||||||
std::cout<<"Stoping: "<<prevWindow<<" pid: "<<prevProcess.pid<<" name: "<<prevProcess.name<<'\n';
|
bool hasTopLevelWindow = false;
|
||||||
|
std::vector<Window> 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;
|
prevProcess = process;
|
||||||
prevWindow = wid;
|
prevWindow = wid;
|
||||||
std::cout<<"Active window: "<<wid<<" pid: "<<windowPid<<" name: "<<Process(windowPid).name<<'\n';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for(auto& pid : stoppedPids)
|
||||||
|
{
|
||||||
|
kill(pid, SIGCONT);
|
||||||
|
std::cout<<"Resumeing pid: "+std::to_string(pid)<<'\n';
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
47
process.cpp
Normal file
47
process.cpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#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<char>(statusFile)),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
std::vector<std::string> lines = split(statusString);
|
||||||
|
if(lines.size() > 0)
|
||||||
|
{
|
||||||
|
pid = pidIn;
|
||||||
|
std::vector<std::string> 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"<<std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
process.h
Normal file
15
process.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
};
|
15
split.h
Normal file
15
split.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
inline std::vector<std::string> split(const std::string& in, char delim = '\n' )
|
||||||
|
{
|
||||||
|
std::stringstream ss(in);
|
||||||
|
std::vector<std::string> tokens;
|
||||||
|
std::string temp_str;
|
||||||
|
while(std::getline(ss, temp_str, delim))
|
||||||
|
{
|
||||||
|
tokens.push_back(temp_str);
|
||||||
|
}
|
||||||
|
return tokens;
|
||||||
|
}
|
154
xinstance.cpp
Normal file
154
xinstance.cpp
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
#include "xinstance.h"
|
||||||
|
#include <X11/Xutil.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <unistd.h>
|
||||||
|
#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 "<<xDisplayName<<'\n';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
screen = XDefaultScreen(display);
|
||||||
|
|
||||||
|
atoms.netActiveWindow = getAtom("_NET_ACTIVE_WINDOW");
|
||||||
|
if(atoms.netActiveWindow == 0)
|
||||||
|
{
|
||||||
|
std::cerr<<"_NET_ACTIVE_WINDOW is required\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
atoms.netWmPid = getAtom("_NET_WM_PID");
|
||||||
|
if(atoms.netActiveWindow == 0)
|
||||||
|
{
|
||||||
|
std::cerr<<"_NET_WM_PID is required\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
atoms.wmClientMachine = getAtom("WM_CLIENT_MACHINE");
|
||||||
|
if(atoms.netActiveWindow == 0)
|
||||||
|
{
|
||||||
|
std::cerr<<" is required\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Window XInstance::getActiveWindow()
|
||||||
|
{
|
||||||
|
unsigned char* data = nullptr;
|
||||||
|
int format;
|
||||||
|
unsigned long length = readProparty(RootWindow(display, screen), atoms.netActiveWindow, &data, &format);
|
||||||
|
Window wid = 0;
|
||||||
|
if(format == 32 && length == 4) wid = *reinterpret_cast<Window*>(data);
|
||||||
|
XFree(data);
|
||||||
|
return wid;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Window> 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<Window> 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<pid_t*>(data);
|
||||||
|
XFree(data);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
debug("Window "+std::to_string(wid)+" is a remote window");
|
||||||
|
}
|
||||||
|
XFreeStringList(xWidHostNameStringList);
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
XInstance::~XInstance()
|
||||||
|
{
|
||||||
|
if(display) XCloseDisplay(display);
|
||||||
|
}
|
37
xinstance.h
Normal file
37
xinstance.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
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<Window> getTopLevelWindows();
|
||||||
|
void flush();
|
||||||
|
};
|
Reference in New Issue
Block a user