345 lines
5.5 KiB
C++
345 lines
5.5 KiB
C++
#include <X11/Xlib.h>
|
|
#include <X11/Xatom.h>
|
|
#include <X11/Xutil.h>
|
|
#include <X11/extensions/Xcomposite.h>
|
|
|
|
#include <unordered_set>
|
|
#include <pthread.h>
|
|
|
|
#include <obs-module.h>
|
|
|
|
#include "xcompcap-helper.h"
|
|
|
|
namespace XCompcap
|
|
{
|
|
static Display* xdisplay = 0;
|
|
|
|
Display *disp()
|
|
{
|
|
if (!xdisplay)
|
|
xdisplay = XOpenDisplay(NULL);
|
|
|
|
return xdisplay;
|
|
}
|
|
|
|
void cleanupDisplay()
|
|
{
|
|
if (!xdisplay)
|
|
return;
|
|
|
|
XCloseDisplay(xdisplay);
|
|
xdisplay = 0;
|
|
}
|
|
|
|
static void getAllWindows(Window parent, std::list<Window>& windows)
|
|
{
|
|
UNUSED_PARAMETER(parent);
|
|
UNUSED_PARAMETER(windows);
|
|
}
|
|
|
|
std::list<Window> getAllWindows()
|
|
{
|
|
std::list<Window> res;
|
|
|
|
for (int i = 0; i < ScreenCount(disp()); ++i)
|
|
getAllWindows(RootWindow(disp(), i), res);
|
|
|
|
return res;
|
|
}
|
|
|
|
std::list<Window> getTopLevelWindows()
|
|
{
|
|
std::list<Window> res;
|
|
|
|
Atom netClList = XInternAtom(disp(), "_NET_CLIENT_LIST", true);
|
|
Atom actualType;
|
|
int format;
|
|
unsigned long num, bytes;
|
|
Window* data = 0;
|
|
|
|
for (int i = 0; i < ScreenCount(disp()); ++i) {
|
|
Window rootWin = RootWindow(disp(), i);
|
|
|
|
int status = XGetWindowProperty(
|
|
disp(),
|
|
rootWin,
|
|
netClList,
|
|
0L,
|
|
~0L,
|
|
false,
|
|
AnyPropertyType,
|
|
&actualType,
|
|
&format,
|
|
&num,
|
|
&bytes,
|
|
(uint8_t**)&data);
|
|
|
|
if (status != Success) {
|
|
blog(LOG_WARNING, "Failed getting root "
|
|
"window properties");
|
|
continue;
|
|
}
|
|
|
|
for (unsigned long i = 0; i < num; ++i)
|
|
res.push_back(data[i]);
|
|
|
|
XFree(data);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
int getRootWindowScreen(Window root)
|
|
{
|
|
XWindowAttributes attr;
|
|
|
|
if (!XGetWindowAttributes(disp(), root, &attr))
|
|
return DefaultScreen(disp());
|
|
|
|
return XScreenNumberOfScreen(attr.screen);
|
|
}
|
|
|
|
std::string getWindowName(Window win)
|
|
{
|
|
Atom netWmName = XInternAtom(disp(), "_NET_WM_NAME", false);
|
|
int n;
|
|
char **list = 0;
|
|
XTextProperty tp;
|
|
std::string res = "unknown";
|
|
|
|
XGetTextProperty(disp(), win, &tp, netWmName);
|
|
|
|
if (!tp.nitems)
|
|
XGetWMName(disp(), win, &tp);
|
|
|
|
if (!tp.nitems)
|
|
return "error";
|
|
|
|
if (tp.encoding == XA_STRING) {
|
|
res = (char*)tp.value;
|
|
} else {
|
|
int ret = XmbTextPropertyToTextList(disp(), &tp, &list,
|
|
&n);
|
|
|
|
if (ret >= Success && n > 0 && *list) {
|
|
res = *list;
|
|
XFreeStringList(list);
|
|
}
|
|
}
|
|
|
|
XFree(tp.value);
|
|
|
|
return res;
|
|
}
|
|
|
|
std::string getWindowCommand(Window win)
|
|
{
|
|
Atom xi = XInternAtom(disp(), "WM_COMMAND", false);
|
|
int n;
|
|
char **list = 0;
|
|
XTextProperty tp;
|
|
std::string res = "error";
|
|
|
|
XGetTextProperty(disp(), win, &tp, xi);
|
|
|
|
if (!tp.nitems)
|
|
return std::string();
|
|
|
|
if (tp.encoding == XA_STRING) {
|
|
res = (char*)tp.value;
|
|
} else {
|
|
int ret = XmbTextPropertyToTextList(disp(), &tp, &list,
|
|
&n);
|
|
if (ret >= Success && n > 0 && *list) {
|
|
res = *list;
|
|
XFreeStringList(list);
|
|
}
|
|
}
|
|
|
|
XFree(tp.value);
|
|
|
|
return res;
|
|
}
|
|
|
|
int getWindowPid(Window win)
|
|
{
|
|
UNUSED_PARAMETER(win);
|
|
return 1234; //TODO
|
|
}
|
|
|
|
static std::unordered_set<Window> changedWindows;
|
|
static pthread_mutex_t changeLock = PTHREAD_MUTEX_INITIALIZER;
|
|
void processEvents()
|
|
{
|
|
PLock lock(&changeLock);
|
|
|
|
XLockDisplay(disp());
|
|
|
|
while (XEventsQueued(disp(), QueuedAfterReading) > 0) {
|
|
XEvent ev;
|
|
|
|
XNextEvent(disp(), &ev);
|
|
|
|
if (ev.type == ConfigureNotify)
|
|
changedWindows.insert(ev.xconfigure.event);
|
|
|
|
if (ev.type == MapNotify)
|
|
changedWindows.insert(ev.xmap.event);
|
|
|
|
if (ev.type == DestroyNotify)
|
|
changedWindows.insert(ev.xdestroywindow.event);
|
|
}
|
|
|
|
XUnlockDisplay(disp());
|
|
}
|
|
|
|
bool windowWasReconfigured(Window win)
|
|
{
|
|
PLock lock(&changeLock);
|
|
|
|
auto it = changedWindows.find(win);
|
|
|
|
if (it != changedWindows.end()) {
|
|
changedWindows.erase(it);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
PLock::PLock(pthread_mutex_t* mtx, bool trylock)
|
|
:m(mtx)
|
|
{
|
|
if (trylock)
|
|
islock = mtx && pthread_mutex_trylock(mtx) == 0;
|
|
else
|
|
islock = mtx && pthread_mutex_lock(mtx) == 0;
|
|
}
|
|
|
|
PLock::~PLock()
|
|
{
|
|
if (islock) {
|
|
pthread_mutex_unlock(m);
|
|
}
|
|
}
|
|
|
|
bool PLock::isLocked()
|
|
{
|
|
return islock;
|
|
}
|
|
|
|
void PLock::unlock()
|
|
{
|
|
if (islock) {
|
|
pthread_mutex_unlock(m);
|
|
islock = false;
|
|
}
|
|
}
|
|
|
|
void PLock::lock()
|
|
{
|
|
if (!islock) {
|
|
pthread_mutex_lock(m);
|
|
islock = true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static bool* curErrorTarget = 0;
|
|
static char curErrorText[200];
|
|
static int xerrorlock_handler(Display* disp, XErrorEvent* err)
|
|
{
|
|
|
|
if (curErrorTarget)
|
|
*curErrorTarget = true;
|
|
|
|
XGetErrorText(disp, err->error_code, curErrorText, 200);
|
|
|
|
return 0;
|
|
}
|
|
|
|
XErrorLock::XErrorLock()
|
|
{
|
|
goterr = false;
|
|
islock = false;
|
|
prevhandler = 0;
|
|
|
|
lock();
|
|
}
|
|
|
|
XErrorLock::~XErrorLock()
|
|
{
|
|
unlock();
|
|
}
|
|
|
|
bool XErrorLock::isLocked()
|
|
{
|
|
return islock;
|
|
}
|
|
|
|
void XErrorLock::lock()
|
|
{
|
|
if (!islock) {
|
|
XLockDisplay(XCompcap::disp());
|
|
XSync(XCompcap::disp(), 0);
|
|
|
|
curErrorTarget = &goterr;
|
|
curErrorText[0] = 0;
|
|
prevhandler = XSetErrorHandler(xerrorlock_handler);
|
|
|
|
islock = true;
|
|
}
|
|
}
|
|
|
|
void XErrorLock::unlock()
|
|
{
|
|
if (islock) {
|
|
curErrorTarget = 0;
|
|
XSetErrorHandler(prevhandler);
|
|
prevhandler = 0;
|
|
XUnlockDisplay(XCompcap::disp());
|
|
islock = false;
|
|
}
|
|
}
|
|
|
|
bool XErrorLock::gotError()
|
|
{
|
|
if (!islock)
|
|
return false;
|
|
|
|
XSync(XCompcap::disp(), 0);
|
|
|
|
bool res = goterr;
|
|
goterr = false;
|
|
return res;
|
|
}
|
|
|
|
std::string XErrorLock::getErrorText()
|
|
{
|
|
return curErrorText;
|
|
}
|
|
|
|
void XErrorLock::resetError()
|
|
{
|
|
if (islock)
|
|
XSync(XCompcap::disp(), 0);
|
|
|
|
goterr = false;
|
|
curErrorText[0] = 0;
|
|
}
|
|
|
|
|
|
ObsGsContextHolder::ObsGsContextHolder()
|
|
{
|
|
gs_entercontext(obs_graphics());
|
|
}
|
|
|
|
ObsGsContextHolder::~ObsGsContextHolder()
|
|
{
|
|
gs_leavecontext();
|
|
}
|