obs-studio/plugins/linux-xcomposite/xcompcap-helper.cpp

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();
}