New upstream version 24.0.1+dfsg1
This commit is contained in:
parent
b14f9eae6d
commit
5a730d6ec3
842 changed files with 42245 additions and 33385 deletions
|
|
@ -2,7 +2,7 @@ X11SharedMemoryScreenInput="Bildschirmaufnahme (XSHM)"
|
|||
Screen="Bildschirm"
|
||||
CaptureCursor="Mauszeiger aufnehmen"
|
||||
AdvancedSettings="Erweiterte Einstellungen"
|
||||
XServer="X-Server"
|
||||
XServer="X‐Server"
|
||||
XCCapture="Fensteraufnahme (Xcomposite)"
|
||||
Window="Fenster"
|
||||
CropTop="Oben abschneiden (Pixel)"
|
||||
|
|
@ -10,7 +10,7 @@ CropLeft="Links abschneiden (Pixel)"
|
|||
CropRight="Rechts abschneiden (Pixel)"
|
||||
CropBottom="Unten abschneiden (Pixel)"
|
||||
SwapRedBlue="Rot und Blau tauschen"
|
||||
LockX="X-Server während der Aufnahme sperren"
|
||||
IncludeXBorder="X-Rahmen anzeigen"
|
||||
ExcludeAlpha="Alphaloses Texturformat verwenden (Mesa-Problemumgehung)"
|
||||
LockX="X‐Server während der Aufnahme sperren"
|
||||
IncludeXBorder="X‐Rahmen anzeigen"
|
||||
ExcludeAlpha="Alphaloses Texturformat verwenden (Mesa‐Problemumgehung)"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
X11SharedMemoryScreenInput="Captura de pantalla (XSHM)"
|
||||
Screen="Pantalla"
|
||||
CaptureCursor="Captura de cursor"
|
||||
CaptureCursor="Capturar o cursor"
|
||||
AdvancedSettings="Axustes avanzados"
|
||||
XServer="X Server"
|
||||
XServer="Servidor das X"
|
||||
XCCapture="Captura de xanela (Xcomposite)"
|
||||
Window="Xanela"
|
||||
CropTop="Recortar por arriba (píxeles)"
|
||||
CropLeft="Recortar pola esquerda (píxeles)"
|
||||
CropRight="Recortar pola dereita (píxeles)"
|
||||
CropBottom="Recortar por abaixo (píxeles)"
|
||||
SwapRedBlue="Trocar vermello e azul"
|
||||
LockX="Bloquear o servidor X durante a captura"
|
||||
CropTop="Recortar por riba (píxeis)"
|
||||
CropLeft="Recortar pola esquerda (píxeis)"
|
||||
CropRight="Recortar pola dereita (píxeis)"
|
||||
CropBottom="Recortar por baixo (píxeis)"
|
||||
SwapRedBlue="Intercambiar vermello e azul"
|
||||
LockX="Bloquear o servidor das X durante a captura"
|
||||
IncludeXBorder="Incluír o bordo da xanela X"
|
||||
ExcludeAlpha="Empregar o formato de textura sen alfa (solución Mesa)"
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ CropLeft="Trunchiază stânga (pixeli)"
|
|||
CropRight="Trunchiază dreapta (pixeli)"
|
||||
CropBottom="Trunchiază partea inferioară (pixeli)"
|
||||
SwapRedBlue="Schimbă roșu cu albastru"
|
||||
LockX="Blochează serverul X atunci când se capturează"
|
||||
LockX="Blochează X server când se capturează"
|
||||
IncludeXBorder="Include marginea cu X"
|
||||
ExcludeAlpha="Folosește formatul de texturi fără alpha (soluție de evitare pentru Mesa)"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
X11SharedMemoryScreenInput="Zajemanje zaslona (XSHM)"
|
||||
Screen="Zaslon"
|
||||
CaptureCursor="Zajemaj kazalec"
|
||||
CaptureCursor="Zajemi kazalec"
|
||||
AdvancedSettings="Napredne nastavitve"
|
||||
XServer="X Server"
|
||||
XCCapture="Zajemanje okna (Xcomposite)"
|
||||
|
|
|
|||
|
|
@ -11,291 +11,259 @@
|
|||
|
||||
#include "xcompcap-helper.hpp"
|
||||
|
||||
namespace XCompcap
|
||||
namespace XCompcap {
|
||||
static Display *xdisplay = 0;
|
||||
|
||||
Display *disp()
|
||||
{
|
||||
static Display* xdisplay = 0;
|
||||
if (!xdisplay)
|
||||
xdisplay = XOpenDisplay(NULL);
|
||||
|
||||
Display *disp()
|
||||
{
|
||||
if (!xdisplay)
|
||||
xdisplay = XOpenDisplay(NULL);
|
||||
return xdisplay;
|
||||
}
|
||||
|
||||
return xdisplay;
|
||||
}
|
||||
void cleanupDisplay()
|
||||
{
|
||||
if (!xdisplay)
|
||||
return;
|
||||
|
||||
void cleanupDisplay()
|
||||
{
|
||||
if (!xdisplay)
|
||||
return;
|
||||
XCloseDisplay(xdisplay);
|
||||
xdisplay = 0;
|
||||
}
|
||||
|
||||
XCloseDisplay(xdisplay);
|
||||
xdisplay = 0;
|
||||
}
|
||||
static void getAllWindows(Window parent, std::list<Window> &windows)
|
||||
{
|
||||
UNUSED_PARAMETER(parent);
|
||||
UNUSED_PARAMETER(windows);
|
||||
}
|
||||
|
||||
static void getAllWindows(Window parent, std::list<Window>& windows)
|
||||
{
|
||||
UNUSED_PARAMETER(parent);
|
||||
UNUSED_PARAMETER(windows);
|
||||
}
|
||||
std::list<Window> getAllWindows()
|
||||
{
|
||||
std::list<Window> res;
|
||||
|
||||
std::list<Window> getAllWindows()
|
||||
{
|
||||
std::list<Window> res;
|
||||
for (int i = 0; i < ScreenCount(disp()); ++i)
|
||||
getAllWindows(RootWindow(disp(), i), res);
|
||||
|
||||
for (int i = 0; i < ScreenCount(disp()); ++i)
|
||||
getAllWindows(RootWindow(disp(), i), res);
|
||||
return res;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
// Specification for checking for ewmh support at
|
||||
// http://standards.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472693600
|
||||
|
||||
// Specification for checking for ewmh support at
|
||||
// http://standards.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472693600
|
||||
bool ewmhIsSupported()
|
||||
{
|
||||
Display *display = disp();
|
||||
Atom netSupportingWmCheck =
|
||||
XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", true);
|
||||
Atom actualType;
|
||||
int format = 0;
|
||||
unsigned long num = 0, bytes = 0;
|
||||
unsigned char *data = NULL;
|
||||
Window ewmh_window = 0;
|
||||
|
||||
bool ewmhIsSupported()
|
||||
{
|
||||
Display *display = disp();
|
||||
Atom netSupportingWmCheck = XInternAtom(display,
|
||||
"_NET_SUPPORTING_WM_CHECK", true);
|
||||
Atom actualType;
|
||||
int format = 0;
|
||||
unsigned long num = 0, bytes = 0;
|
||||
unsigned char *data = NULL;
|
||||
Window ewmh_window = 0;
|
||||
int status = XGetWindowProperty(display, DefaultRootWindow(display),
|
||||
netSupportingWmCheck, 0L, 1L, false,
|
||||
XA_WINDOW, &actualType, &format, &num,
|
||||
&bytes, &data);
|
||||
|
||||
int status = XGetWindowProperty(
|
||||
display,
|
||||
DefaultRootWindow(display),
|
||||
netSupportingWmCheck,
|
||||
0L,
|
||||
1L,
|
||||
false,
|
||||
XA_WINDOW,
|
||||
&actualType,
|
||||
&format,
|
||||
&num,
|
||||
&bytes,
|
||||
&data);
|
||||
|
||||
if (status == Success) {
|
||||
if (num > 0) {
|
||||
ewmh_window = ((Window*)data)[0];
|
||||
}
|
||||
if (data) {
|
||||
XFree(data);
|
||||
data = NULL;
|
||||
}
|
||||
if (status == Success) {
|
||||
if (num > 0) {
|
||||
ewmh_window = ((Window *)data)[0];
|
||||
}
|
||||
|
||||
if (ewmh_window) {
|
||||
status = XGetWindowProperty(
|
||||
display,
|
||||
ewmh_window,
|
||||
netSupportingWmCheck,
|
||||
0L,
|
||||
1L,
|
||||
false,
|
||||
XA_WINDOW,
|
||||
&actualType,
|
||||
&format,
|
||||
&num,
|
||||
&bytes,
|
||||
&data);
|
||||
if (status != Success || num == 0 ||
|
||||
ewmh_window != ((Window*)data)[0]) {
|
||||
ewmh_window = 0;
|
||||
}
|
||||
if (status == Success && data) {
|
||||
XFree(data);
|
||||
}
|
||||
if (data) {
|
||||
XFree(data);
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
return ewmh_window != 0;
|
||||
}
|
||||
|
||||
std::list<Window> getTopLevelWindows()
|
||||
{
|
||||
std::list<Window> res;
|
||||
|
||||
if (!ewmhIsSupported()) {
|
||||
blog(LOG_WARNING, "Unable to query window list "
|
||||
"because window manager "
|
||||
"does not support extended "
|
||||
"window manager Hints");
|
||||
return res;
|
||||
if (ewmh_window) {
|
||||
status = XGetWindowProperty(display, ewmh_window,
|
||||
netSupportingWmCheck, 0L, 1L, false,
|
||||
XA_WINDOW, &actualType, &format,
|
||||
&num, &bytes, &data);
|
||||
if (status != Success || num == 0 ||
|
||||
ewmh_window != ((Window *)data)[0]) {
|
||||
ewmh_window = 0;
|
||||
}
|
||||
|
||||
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]);
|
||||
|
||||
if (status == Success && data) {
|
||||
XFree(data);
|
||||
}
|
||||
}
|
||||
|
||||
return ewmh_window != 0;
|
||||
}
|
||||
|
||||
std::list<Window> getTopLevelWindows()
|
||||
{
|
||||
std::list<Window> res;
|
||||
|
||||
if (!ewmhIsSupported()) {
|
||||
blog(LOG_WARNING, "Unable to query window list "
|
||||
"because window manager "
|
||||
"does not support extended "
|
||||
"window manager Hints");
|
||||
return res;
|
||||
}
|
||||
|
||||
int getRootWindowScreen(Window root)
|
||||
{
|
||||
XWindowAttributes attr;
|
||||
Atom netClList = XInternAtom(disp(), "_NET_CLIENT_LIST", true);
|
||||
Atom actualType;
|
||||
int format;
|
||||
unsigned long num, bytes;
|
||||
Window *data = 0;
|
||||
|
||||
if (!XGetWindowAttributes(disp(), root, &attr))
|
||||
return DefaultScreen(disp());
|
||||
for (int i = 0; i < ScreenCount(disp()); ++i) {
|
||||
Window rootWin = RootWindow(disp(), i);
|
||||
|
||||
return XScreenNumberOfScreen(attr.screen);
|
||||
}
|
||||
int status = XGetWindowProperty(disp(), rootWin, netClList, 0L,
|
||||
~0L, false, AnyPropertyType,
|
||||
&actualType, &format, &num,
|
||||
&bytes, (uint8_t **)&data);
|
||||
|
||||
std::string getWindowAtom(Window win, const char *atom)
|
||||
{
|
||||
Atom netWmName = XInternAtom(disp(), atom, 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);
|
||||
}
|
||||
if (status != Success) {
|
||||
blog(LOG_WARNING, "Failed getting root "
|
||||
"window properties");
|
||||
continue;
|
||||
}
|
||||
|
||||
char *conv = nullptr;
|
||||
if (os_mbs_to_utf8_ptr(res.c_str(), 0, &conv))
|
||||
res = conv;
|
||||
bfree(conv);
|
||||
for (unsigned long i = 0; i < num; ++i)
|
||||
res.push_back(data[i]);
|
||||
|
||||
XFree(tp.value);
|
||||
|
||||
return res;
|
||||
XFree(data);
|
||||
}
|
||||
|
||||
std::string getWindowCommand(Window win)
|
||||
{
|
||||
Atom xi = XInternAtom(disp(), "WM_COMMAND", false);
|
||||
int n;
|
||||
char **list = 0;
|
||||
XTextProperty tp;
|
||||
std::string res = "error";
|
||||
return res;
|
||||
}
|
||||
|
||||
XGetTextProperty(disp(), win, &tp, xi);
|
||||
int getRootWindowScreen(Window root)
|
||||
{
|
||||
XWindowAttributes attr;
|
||||
|
||||
if (!tp.nitems)
|
||||
return std::string();
|
||||
if (!XGetWindowAttributes(disp(), root, &attr))
|
||||
return DefaultScreen(disp());
|
||||
|
||||
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);
|
||||
}
|
||||
return XScreenNumberOfScreen(attr.screen);
|
||||
}
|
||||
|
||||
std::string getWindowAtom(Window win, const char *atom)
|
||||
{
|
||||
Atom netWmName = XInternAtom(disp(), atom, 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;
|
||||
}
|
||||
|
||||
int getWindowPid(Window win)
|
||||
{
|
||||
UNUSED_PARAMETER(win);
|
||||
return 1234; //TODO
|
||||
}
|
||||
char *conv = nullptr;
|
||||
if (os_mbs_to_utf8_ptr(res.c_str(), 0, &conv))
|
||||
res = conv;
|
||||
bfree(conv);
|
||||
|
||||
static std::unordered_set<Window> changedWindows;
|
||||
static pthread_mutex_t changeLock = PTHREAD_MUTEX_INITIALIZER;
|
||||
void processEvents()
|
||||
{
|
||||
PLock lock(&changeLock);
|
||||
XFree(tp.value);
|
||||
|
||||
XLockDisplay(disp());
|
||||
return res;
|
||||
}
|
||||
|
||||
while (XEventsQueued(disp(), QueuedAfterReading) > 0) {
|
||||
XEvent ev;
|
||||
std::string getWindowCommand(Window win)
|
||||
{
|
||||
Atom xi = XInternAtom(disp(), "WM_COMMAND", false);
|
||||
int n;
|
||||
char **list = 0;
|
||||
XTextProperty tp;
|
||||
std::string res = "error";
|
||||
|
||||
XNextEvent(disp(), &ev);
|
||||
XGetTextProperty(disp(), win, &tp, xi);
|
||||
|
||||
if (ev.type == ConfigureNotify)
|
||||
changedWindows.insert(ev.xconfigure.event);
|
||||
if (!tp.nitems)
|
||||
return std::string();
|
||||
|
||||
if (ev.type == MapNotify)
|
||||
changedWindows.insert(ev.xmap.event);
|
||||
|
||||
if (ev.type == Expose)
|
||||
changedWindows.insert(ev.xexpose.window);
|
||||
|
||||
if (ev.type == VisibilityNotify)
|
||||
changedWindows.insert(ev.xvisibility.window);
|
||||
|
||||
if (ev.type == DestroyNotify)
|
||||
changedWindows.insert(ev.xdestroywindow.event);
|
||||
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);
|
||||
}
|
||||
|
||||
XUnlockDisplay(disp());
|
||||
}
|
||||
|
||||
bool windowWasReconfigured(Window win)
|
||||
{
|
||||
PLock lock(&changeLock);
|
||||
XFree(tp.value);
|
||||
|
||||
auto it = changedWindows.find(win);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (it != changedWindows.end()) {
|
||||
changedWindows.erase(it);
|
||||
return true;
|
||||
}
|
||||
int getWindowPid(Window win)
|
||||
{
|
||||
UNUSED_PARAMETER(win);
|
||||
return 1234; //TODO
|
||||
}
|
||||
|
||||
return false;
|
||||
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 == Expose)
|
||||
changedWindows.insert(ev.xexpose.window);
|
||||
|
||||
if (ev.type == VisibilityNotify)
|
||||
changedWindows.insert(ev.xvisibility.window);
|
||||
|
||||
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)
|
||||
PLock::PLock(pthread_mutex_t *mtx, bool trylock) : m(mtx)
|
||||
{
|
||||
if (trylock)
|
||||
islock = mtx && pthread_mutex_trylock(mtx) == 0;
|
||||
|
|
@ -331,11 +299,9 @@ void PLock::lock()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool* curErrorTarget = 0;
|
||||
static bool *curErrorTarget = 0;
|
||||
static char curErrorText[200];
|
||||
static int xerrorlock_handler(Display* disp, XErrorEvent* err)
|
||||
static int xerrorlock_handler(Display *disp, XErrorEvent *err)
|
||||
{
|
||||
|
||||
if (curErrorTarget)
|
||||
|
|
@ -451,7 +417,6 @@ void XDisplayLock::unlock()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
ObsGsContextHolder::ObsGsContextHolder()
|
||||
{
|
||||
obs_enter_graphics();
|
||||
|
|
|
|||
|
|
@ -5,17 +5,15 @@
|
|||
|
||||
#define blog(level, msg, ...) blog(level, "xcompcap: " msg, ##__VA_ARGS__)
|
||||
|
||||
|
||||
class PLock
|
||||
{
|
||||
class PLock {
|
||||
pthread_mutex_t *m;
|
||||
bool islock;
|
||||
|
||||
public:
|
||||
PLock(const PLock&) = delete;
|
||||
PLock& operator=(const PLock&) = delete;
|
||||
public:
|
||||
PLock(const PLock &) = delete;
|
||||
PLock &operator=(const PLock &) = delete;
|
||||
|
||||
PLock(pthread_mutex_t* mtx, bool trylock = false);
|
||||
PLock(pthread_mutex_t *mtx, bool trylock = false);
|
||||
|
||||
~PLock();
|
||||
|
||||
|
|
@ -25,15 +23,14 @@ class PLock
|
|||
void lock();
|
||||
};
|
||||
|
||||
class XErrorLock
|
||||
{
|
||||
class XErrorLock {
|
||||
bool islock;
|
||||
bool goterr;
|
||||
XErrorHandler prevhandler;
|
||||
|
||||
public:
|
||||
XErrorLock(const XErrorLock&) = delete;
|
||||
XErrorLock& operator=(const XErrorLock&) = delete;
|
||||
public:
|
||||
XErrorLock(const XErrorLock &) = delete;
|
||||
XErrorLock &operator=(const XErrorLock &) = delete;
|
||||
|
||||
XErrorLock();
|
||||
~XErrorLock();
|
||||
|
|
@ -48,13 +45,12 @@ class XErrorLock
|
|||
void resetError();
|
||||
};
|
||||
|
||||
class XDisplayLock
|
||||
{
|
||||
class XDisplayLock {
|
||||
bool islock;
|
||||
|
||||
public:
|
||||
XDisplayLock(const XDisplayLock&) = delete;
|
||||
XDisplayLock& operator=(const XDisplayLock&) = delete;
|
||||
public:
|
||||
XDisplayLock(const XDisplayLock &) = delete;
|
||||
XDisplayLock &operator=(const XDisplayLock &) = delete;
|
||||
|
||||
XDisplayLock();
|
||||
~XDisplayLock();
|
||||
|
|
@ -65,39 +61,37 @@ class XDisplayLock
|
|||
void lock();
|
||||
};
|
||||
|
||||
class ObsGsContextHolder
|
||||
{
|
||||
public:
|
||||
ObsGsContextHolder(const ObsGsContextHolder&) = delete;
|
||||
ObsGsContextHolder& operator=(const ObsGsContextHolder&) = delete;
|
||||
class ObsGsContextHolder {
|
||||
public:
|
||||
ObsGsContextHolder(const ObsGsContextHolder &) = delete;
|
||||
ObsGsContextHolder &operator=(const ObsGsContextHolder &) = delete;
|
||||
|
||||
ObsGsContextHolder();
|
||||
~ObsGsContextHolder();
|
||||
};
|
||||
|
||||
namespace XCompcap
|
||||
namespace XCompcap {
|
||||
Display *disp();
|
||||
void cleanupDisplay();
|
||||
|
||||
std::string getWindowCommand(Window win);
|
||||
int getRootWindowScreen(Window root);
|
||||
std::string getWindowAtom(Window win, const char *atom);
|
||||
int getWindowPid(Window win);
|
||||
bool ewmhIsSupported();
|
||||
std::list<Window> getTopLevelWindows();
|
||||
std::list<Window> getAllWindows();
|
||||
|
||||
inline std::string getWindowName(Window win)
|
||||
{
|
||||
Display* disp();
|
||||
void cleanupDisplay();
|
||||
|
||||
std::string getWindowCommand(Window win);
|
||||
int getRootWindowScreen(Window root);
|
||||
std::string getWindowAtom(Window win, const char *atom);
|
||||
int getWindowPid(Window win);
|
||||
bool ewmhIsSupported();
|
||||
std::list<Window> getTopLevelWindows();
|
||||
std::list<Window> getAllWindows();
|
||||
|
||||
inline std::string getWindowName(Window win)
|
||||
{
|
||||
return getWindowAtom(win, "_NET_WM_NAME");
|
||||
}
|
||||
|
||||
inline std::string getWindowClass(Window win)
|
||||
{
|
||||
return getWindowAtom(win, "WM_CLASS");
|
||||
}
|
||||
|
||||
void processEvents();
|
||||
bool windowWasReconfigured(Window win);
|
||||
return getWindowAtom(win, "_NET_WM_NAME");
|
||||
}
|
||||
|
||||
inline std::string getWindowClass(Window win)
|
||||
{
|
||||
return getWindowAtom(win, "WM_CLASS");
|
||||
}
|
||||
|
||||
void processEvents();
|
||||
bool windowWasReconfigured(Window win);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ bool XCompcapMain::init()
|
|||
|
||||
if (major == 0 && minor < 2) {
|
||||
blog(LOG_ERROR, "Xcomposite extension is too old: %d.%d < 0.2",
|
||||
major, minor);
|
||||
major, minor);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -51,43 +51,41 @@ obs_properties_t *XCompcapMain::properties()
|
|||
{
|
||||
obs_properties_t *props = obs_properties_create();
|
||||
|
||||
obs_property_t *wins = obs_properties_add_list(props, "capture_window",
|
||||
obs_module_text("Window"),
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
|
||||
obs_property_t *wins = obs_properties_add_list(
|
||||
props, "capture_window", obs_module_text("Window"),
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
|
||||
|
||||
for (Window win: XCompcap::getTopLevelWindows()) {
|
||||
for (Window win : XCompcap::getTopLevelWindows()) {
|
||||
std::string wname = XCompcap::getWindowName(win);
|
||||
std::string cls = XCompcap::getWindowClass(win);
|
||||
std::string winid = std::to_string((long long)win);
|
||||
std::string desc =
|
||||
(winid + WIN_STRING_DIV + wname +
|
||||
WIN_STRING_DIV + cls);
|
||||
(winid + WIN_STRING_DIV + wname + WIN_STRING_DIV + cls);
|
||||
|
||||
obs_property_list_add_string(wins, wname.c_str(),
|
||||
desc.c_str());
|
||||
obs_property_list_add_string(wins, wname.c_str(), desc.c_str());
|
||||
}
|
||||
|
||||
obs_properties_add_int(props, "cut_top", obs_module_text("CropTop"),
|
||||
0, 4096, 1);
|
||||
obs_properties_add_int(props, "cut_top", obs_module_text("CropTop"), 0,
|
||||
4096, 1);
|
||||
obs_properties_add_int(props, "cut_left", obs_module_text("CropLeft"),
|
||||
0, 4096, 1);
|
||||
0, 4096, 1);
|
||||
obs_properties_add_int(props, "cut_right", obs_module_text("CropRight"),
|
||||
0, 4096, 1);
|
||||
0, 4096, 1);
|
||||
obs_properties_add_int(props, "cut_bot", obs_module_text("CropBottom"),
|
||||
0, 4096, 1);
|
||||
0, 4096, 1);
|
||||
|
||||
obs_properties_add_bool(props, "swap_redblue",
|
||||
obs_module_text("SwapRedBlue"));
|
||||
obs_module_text("SwapRedBlue"));
|
||||
obs_properties_add_bool(props, "lock_x", obs_module_text("LockX"));
|
||||
|
||||
obs_properties_add_bool(props, "show_cursor",
|
||||
obs_module_text("CaptureCursor"));
|
||||
obs_module_text("CaptureCursor"));
|
||||
|
||||
obs_properties_add_bool(props, "include_border",
|
||||
obs_module_text("IncludeXBorder"));
|
||||
obs_module_text("IncludeXBorder"));
|
||||
|
||||
obs_properties_add_bool(props, "exclude_alpha",
|
||||
obs_module_text("ExcludeAlpha"));
|
||||
obs_module_text("ExcludeAlpha"));
|
||||
|
||||
return props;
|
||||
}
|
||||
|
|
@ -108,20 +106,24 @@ void XCompcapMain::defaults(obs_data_t *settings)
|
|||
|
||||
#define FIND_WINDOW_INTERVAL 2.0
|
||||
|
||||
struct XCompcapMain_private
|
||||
{
|
||||
struct XCompcapMain_private {
|
||||
XCompcapMain_private()
|
||||
:win(0)
|
||||
,cut_top(0), cur_cut_top(0)
|
||||
,cut_left(0), cur_cut_left(0)
|
||||
,cut_right(0), cur_cut_right(0)
|
||||
,cut_bot(0), cur_cut_bot(0)
|
||||
,inverted(false)
|
||||
,width(0),height(0)
|
||||
,pixmap(0)
|
||||
,glxpixmap(0)
|
||||
,tex(0)
|
||||
,gltex(0)
|
||||
: win(0),
|
||||
cut_top(0),
|
||||
cur_cut_top(0),
|
||||
cut_left(0),
|
||||
cur_cut_left(0),
|
||||
cut_right(0),
|
||||
cur_cut_right(0),
|
||||
cut_bot(0),
|
||||
cur_cut_bot(0),
|
||||
inverted(false),
|
||||
width(0),
|
||||
height(0),
|
||||
pixmap(0),
|
||||
glxpixmap(0),
|
||||
tex(0),
|
||||
gltex(0)
|
||||
{
|
||||
pthread_mutexattr_init(&lockattr);
|
||||
pthread_mutexattr_settype(&lockattr, PTHREAD_MUTEX_RECURSIVE);
|
||||
|
|
@ -169,7 +171,6 @@ struct XCompcapMain_private
|
|||
xcursor_t *cursor = nullptr;
|
||||
};
|
||||
|
||||
|
||||
XCompcapMain::XCompcapMain(obs_data_t *settings, obs_source_t *source)
|
||||
{
|
||||
p = new XCompcapMain_private;
|
||||
|
|
@ -231,7 +232,7 @@ static Window getWindowFromString(std::string wstr)
|
|||
std::string wcls = wstr.substr(lastMark + markSize);
|
||||
|
||||
Window matchedNameWin = wid;
|
||||
for (Window cwin: XCompcap::getTopLevelWindows()) {
|
||||
for (Window cwin : XCompcap::getTopLevelWindows()) {
|
||||
std::string cwinname = XCompcap::getWindowName(cwin);
|
||||
std::string ccls = XCompcap::getWindowClass(cwin);
|
||||
|
||||
|
|
@ -249,38 +250,103 @@ static Window getWindowFromString(std::string wstr)
|
|||
static void xcc_cleanup(XCompcapMain_private *p)
|
||||
{
|
||||
PLock lock(&p->lock);
|
||||
XDisplayLock xlock;
|
||||
XErrorLock xlock;
|
||||
|
||||
if (p->gltex) {
|
||||
GLuint gltex = *(GLuint*)gs_texture_get_obj(p->gltex);
|
||||
GLuint gltex = *(GLuint *)gs_texture_get_obj(p->gltex);
|
||||
glBindTexture(GL_TEXTURE_2D, gltex);
|
||||
glXReleaseTexImageEXT(xdisp, p->glxpixmap, GLX_FRONT_LEFT_EXT);
|
||||
if (p->glxpixmap) {
|
||||
glXReleaseTexImageEXT(xdisp, p->glxpixmap,
|
||||
GLX_FRONT_LEFT_EXT);
|
||||
if (xlock.gotError()) {
|
||||
blog(LOG_ERROR,
|
||||
"cleanup glXReleaseTexImageEXT failed: %s",
|
||||
xlock.getErrorText().c_str());
|
||||
xlock.resetError();
|
||||
}
|
||||
glXDestroyPixmap(xdisp, p->glxpixmap);
|
||||
if (xlock.gotError()) {
|
||||
blog(LOG_ERROR,
|
||||
"cleanup glXDestroyPixmap failed: %s",
|
||||
xlock.getErrorText().c_str());
|
||||
xlock.resetError();
|
||||
}
|
||||
p->glxpixmap = 0;
|
||||
}
|
||||
gs_texture_destroy(p->gltex);
|
||||
p->gltex = 0;
|
||||
}
|
||||
|
||||
if (p->glxpixmap) {
|
||||
glXDestroyPixmap(xdisp, p->glxpixmap);
|
||||
p->glxpixmap = 0;
|
||||
}
|
||||
|
||||
if (p->pixmap) {
|
||||
XFreePixmap(xdisp, p->pixmap);
|
||||
if (xlock.gotError()) {
|
||||
blog(LOG_ERROR, "cleanup glXDestroyPixmap failed: %s",
|
||||
xlock.getErrorText().c_str());
|
||||
xlock.resetError();
|
||||
}
|
||||
p->pixmap = 0;
|
||||
}
|
||||
|
||||
if (p->win) {
|
||||
XCompositeUnredirectWindow(xdisp, p->win,
|
||||
CompositeRedirectAutomatic);
|
||||
CompositeRedirectAutomatic);
|
||||
XSelectInput(xdisp, p->win, 0);
|
||||
p->win = 0;
|
||||
}
|
||||
|
||||
if (p->tex) {
|
||||
gs_texture_destroy(p->tex);
|
||||
p->tex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static gs_color_format gs_format_from_tex()
|
||||
{
|
||||
GLint iformat = 0;
|
||||
// consider GL_ARB_internalformat_query
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT,
|
||||
&iformat);
|
||||
|
||||
// These formats are known to be wrong on Intel platforms. We intentionally
|
||||
// use swapped internal formats here to preserve historic behavior which
|
||||
// swapped colors accidentally and because D3D11 would not support a
|
||||
// GS_RGBX format
|
||||
switch (iformat) {
|
||||
case GL_RGB:
|
||||
return GS_BGRX;
|
||||
case GL_RGBA:
|
||||
return GS_RGBA;
|
||||
default:
|
||||
return GS_RGBA;
|
||||
}
|
||||
}
|
||||
|
||||
// from libobs-opengl/gl-subsystem.h because we need to handle GLX modifying textures outside libobs.
|
||||
struct fb_info;
|
||||
|
||||
struct gs_texture {
|
||||
gs_device_t *device;
|
||||
enum gs_texture_type type;
|
||||
enum gs_color_format format;
|
||||
GLenum gl_format;
|
||||
GLenum gl_target;
|
||||
GLenum gl_internal_format;
|
||||
GLenum gl_type;
|
||||
GLuint texture;
|
||||
uint32_t levels;
|
||||
bool is_dynamic;
|
||||
bool is_render_target;
|
||||
bool is_dummy;
|
||||
bool gen_mipmaps;
|
||||
|
||||
gs_samplerstate_t *cur_sampler;
|
||||
struct fbo_info *fbo;
|
||||
};
|
||||
// End shitty hack.
|
||||
|
||||
void XCompcapMain::updateSettings(obs_data_t *settings)
|
||||
{
|
||||
PLock lock(&p->lock);
|
||||
XErrorLock xlock;
|
||||
ObsGsContextHolder obsctx;
|
||||
|
||||
blog(LOG_DEBUG, "Settings updating");
|
||||
|
|
@ -290,8 +356,8 @@ void XCompcapMain::updateSettings(obs_data_t *settings)
|
|||
xcc_cleanup(p);
|
||||
|
||||
if (settings) {
|
||||
const char *windowName = obs_data_get_string(settings,
|
||||
"capture_window");
|
||||
const char *windowName =
|
||||
obs_data_get_string(settings, "capture_window");
|
||||
|
||||
p->windowName = windowName;
|
||||
p->win = getWindowFromString(windowName);
|
||||
|
|
@ -303,30 +369,28 @@ void XCompcapMain::updateSettings(obs_data_t *settings)
|
|||
p->lockX = obs_data_get_bool(settings, "lock_x");
|
||||
p->swapRedBlue = obs_data_get_bool(settings, "swap_redblue");
|
||||
p->show_cursor = obs_data_get_bool(settings, "show_cursor");
|
||||
p->include_border = obs_data_get_bool(settings, "include_border");
|
||||
p->include_border =
|
||||
obs_data_get_bool(settings, "include_border");
|
||||
p->exclude_alpha = obs_data_get_bool(settings, "exclude_alpha");
|
||||
p->draw_opaque = false;
|
||||
} else {
|
||||
p->win = prevWin;
|
||||
}
|
||||
|
||||
xlock.resetError();
|
||||
|
||||
XErrorLock xlock;
|
||||
if (p->win)
|
||||
XCompositeRedirectWindow(xdisp, p->win,
|
||||
CompositeRedirectAutomatic);
|
||||
|
||||
CompositeRedirectAutomatic);
|
||||
if (xlock.gotError()) {
|
||||
blog(LOG_ERROR, "XCompositeRedirectWindow failed: %s",
|
||||
xlock.getErrorText().c_str());
|
||||
xlock.getErrorText().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (p->win)
|
||||
XSelectInput(xdisp, p->win,
|
||||
StructureNotifyMask
|
||||
| ExposureMask
|
||||
| VisibilityChangeMask);
|
||||
StructureNotifyMask | ExposureMask |
|
||||
VisibilityChangeMask);
|
||||
XSync(xdisp, 0);
|
||||
|
||||
XWindowAttributes attr;
|
||||
|
|
@ -342,75 +406,26 @@ void XCompcapMain::updateSettings(obs_data_t *settings)
|
|||
int x, y;
|
||||
|
||||
XTranslateCoordinates(xdisp, p->win, attr.root, 0, 0, &x, &y,
|
||||
&child);
|
||||
&child);
|
||||
xcursor_offset(p->cursor, x, y);
|
||||
}
|
||||
|
||||
gs_color_format cf = GS_RGBA;
|
||||
|
||||
if (p->exclude_alpha) {
|
||||
cf = GS_BGRX;
|
||||
}
|
||||
|
||||
bool has_alpha = true;
|
||||
|
||||
const int attrs[] =
|
||||
{
|
||||
GLX_BIND_TO_TEXTURE_RGBA_EXT, GL_TRUE,
|
||||
GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
|
||||
GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
|
||||
GLX_ALPHA_SIZE, 8,
|
||||
GLX_DOUBLEBUFFER, GL_FALSE,
|
||||
None
|
||||
};
|
||||
|
||||
const int config_attrs[] = {GLX_BIND_TO_TEXTURE_RGBA_EXT,
|
||||
GL_TRUE,
|
||||
GLX_DRAWABLE_TYPE,
|
||||
GLX_PIXMAP_BIT,
|
||||
GLX_BIND_TO_TEXTURE_TARGETS_EXT,
|
||||
GLX_TEXTURE_2D_BIT_EXT,
|
||||
GLX_DOUBLEBUFFER,
|
||||
GL_FALSE,
|
||||
None};
|
||||
int nelem = 0;
|
||||
GLXFBConfig *configs = glXGetFBConfigs(xdisp,
|
||||
XCompcap::getRootWindowScreen(attr.root),
|
||||
&nelem);
|
||||
GLXFBConfig *configs = glXChooseFBConfig(
|
||||
xdisp, XCompcap::getRootWindowScreen(attr.root), config_attrs,
|
||||
&nelem);
|
||||
|
||||
if (nelem <= 0) {
|
||||
blog(LOG_ERROR, "no fb configs available");
|
||||
p->win = 0;
|
||||
p->height = 0;
|
||||
p->width = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
GLXFBConfig config;
|
||||
for (int i = 0; i < nelem; i++) {
|
||||
config = configs[i];
|
||||
XVisualInfo *visual = glXGetVisualFromFBConfig(xdisp, config);
|
||||
if (!visual)
|
||||
continue;
|
||||
|
||||
if (attr.visual->visualid != visual->visualid) {
|
||||
XFree(visual);
|
||||
continue;
|
||||
}
|
||||
XFree(visual);
|
||||
|
||||
int value;
|
||||
glXGetFBConfigAttrib(xdisp, config, GLX_ALPHA_SIZE, &value);
|
||||
if (value != 8)
|
||||
has_alpha = false;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
XFree(configs);
|
||||
configs = glXChooseFBConfig(xdisp,
|
||||
XCompcap::getRootWindowScreen(attr.root),
|
||||
attrs, &nelem);
|
||||
|
||||
if (nelem <= 0) {
|
||||
blog(LOG_ERROR, "no matching fb config found");
|
||||
p->win = 0;
|
||||
p->height = 0;
|
||||
p->width = 0;
|
||||
return;
|
||||
}
|
||||
bool found = false;
|
||||
GLXFBConfig config;
|
||||
for (int i = 0; i < nelem; i++) {
|
||||
config = configs[i];
|
||||
XVisualInfo *visual = glXGetVisualFromFBConfig(xdisp, config);
|
||||
|
|
@ -425,10 +440,16 @@ void XCompcapMain::updateSettings(obs_data_t *settings)
|
|||
found = true;
|
||||
break;
|
||||
}
|
||||
if (!found)
|
||||
config = configs[0];
|
||||
if (!found) {
|
||||
blog(LOG_ERROR, "no matching fb config found");
|
||||
p->win = 0;
|
||||
p->height = 0;
|
||||
p->width = 0;
|
||||
XFree(configs);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cf == GS_BGRX || !has_alpha) {
|
||||
if (p->exclude_alpha || attr.depth != 32) {
|
||||
p->draw_opaque = true;
|
||||
}
|
||||
|
||||
|
|
@ -462,93 +483,83 @@ void XCompcapMain::updateSettings(obs_data_t *settings)
|
|||
p->cur_cut_right = 0;
|
||||
}
|
||||
|
||||
if (p->tex)
|
||||
gs_texture_destroy(p->tex);
|
||||
|
||||
uint8_t *texData = new uint8_t[width() * height() * 4];
|
||||
|
||||
memset(texData, 0, width() * height() * 4);
|
||||
|
||||
const uint8_t* texDataArr[] = { texData, 0 };
|
||||
|
||||
p->tex = gs_texture_create(width(), height(), cf, 1,
|
||||
texDataArr, 0);
|
||||
|
||||
delete[] texData;
|
||||
|
||||
if (p->swapRedBlue) {
|
||||
GLuint tex = *(GLuint*)gs_texture_get_obj(p->tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
// Precautionary since we dont error check every GLX call above.
|
||||
xlock.resetError();
|
||||
|
||||
p->pixmap = XCompositeNameWindowPixmap(xdisp, p->win);
|
||||
|
||||
if (xlock.gotError()) {
|
||||
blog(LOG_ERROR, "XCompositeNameWindowPixmap failed: %s",
|
||||
xlock.getErrorText().c_str());
|
||||
xlock.getErrorText().c_str());
|
||||
p->pixmap = 0;
|
||||
XFree(configs);
|
||||
return;
|
||||
}
|
||||
|
||||
const int attribs_alpha[] =
|
||||
{
|
||||
GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
|
||||
GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT,
|
||||
None
|
||||
};
|
||||
|
||||
const int attribs_no_alpha[] =
|
||||
{
|
||||
GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
|
||||
GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGB_EXT,
|
||||
None
|
||||
};
|
||||
|
||||
const int *attribs = cf == GS_RGBA ? attribs_alpha : attribs_no_alpha;
|
||||
|
||||
p->glxpixmap = glXCreatePixmap(xdisp, config, p->pixmap, attribs);
|
||||
// Should be consistent format with config we are using. Since we searched on RGBA lets use RGBA here.
|
||||
const int pixmap_attrs[] = {GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
|
||||
GLX_TEXTURE_FORMAT_EXT,
|
||||
GLX_TEXTURE_FORMAT_RGBA_EXT, None};
|
||||
|
||||
p->glxpixmap = glXCreatePixmap(xdisp, config, p->pixmap, pixmap_attrs);
|
||||
if (xlock.gotError()) {
|
||||
blog(LOG_ERROR, "glXCreatePixmap failed: %s",
|
||||
xlock.getErrorText().c_str());
|
||||
xlock.getErrorText().c_str());
|
||||
XFreePixmap(xdisp, p->pixmap);
|
||||
XFree(configs);
|
||||
p->pixmap = 0;
|
||||
p->glxpixmap = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
XFree(configs);
|
||||
|
||||
p->gltex = gs_texture_create(p->width, p->height, cf, 1, 0,
|
||||
GS_GL_DUMMYTEX);
|
||||
|
||||
GLuint gltex = *(GLuint*)gs_texture_get_obj(p->gltex);
|
||||
// Build an OBS texture to bind the pixmap to.
|
||||
p->gltex = gs_texture_create(p->width, p->height, GS_RGBA, 1, 0,
|
||||
GS_GL_DUMMYTEX);
|
||||
GLuint gltex = *(GLuint *)gs_texture_get_obj(p->gltex);
|
||||
glBindTexture(GL_TEXTURE_2D, gltex);
|
||||
glXBindTexImageEXT(xdisp, p->glxpixmap, GLX_FRONT_LEFT_EXT, NULL);
|
||||
if (xlock.gotError()) {
|
||||
blog(LOG_ERROR, "glXBindTexImageEXT failed: %s",
|
||||
xlock.getErrorText().c_str());
|
||||
XFreePixmap(xdisp, p->pixmap);
|
||||
XFree(configs);
|
||||
p->pixmap = 0;
|
||||
p->glxpixmap = 0;
|
||||
return;
|
||||
}
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
// glxBindTexImageEXT might modify the textures format.
|
||||
gs_color_format format = gs_format_from_tex();
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
// sync OBS texture format based on any glxBindTexImageEXT changes
|
||||
p->gltex->format = format;
|
||||
|
||||
// Create a pure OBS texture to use for rendering. Using the same
|
||||
// format so we can copy instead of drawing from the source gltex.
|
||||
if (p->tex)
|
||||
gs_texture_destroy(p->tex);
|
||||
p->tex = gs_texture_create(width(), height(), format, 1, 0,
|
||||
GS_GL_DUMMYTEX);
|
||||
if (p->swapRedBlue) {
|
||||
GLuint tex = *(GLuint *)gs_texture_get_obj(p->tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
if (!p->windowName.empty()) {
|
||||
blog(LOG_INFO, "[window-capture: '%s'] update settings:\n"
|
||||
"\ttitle: %s\n"
|
||||
"\tclass: %s\n"
|
||||
"\tHas alpha: %s\n"
|
||||
"\tFound proper GLXFBConfig: %s\n",
|
||||
obs_source_get_name(p->source),
|
||||
XCompcap::getWindowName(p->win).c_str(),
|
||||
XCompcap::getWindowClass(p->win).c_str(),
|
||||
has_alpha ? "yes" : "no",
|
||||
found ? "yes" : "no");
|
||||
blog(LOG_DEBUG, "\n"
|
||||
"\tid: %s",
|
||||
std::to_string((long long)p->win).c_str());
|
||||
blog(LOG_INFO,
|
||||
"[window-capture: '%s'] update settings:\n"
|
||||
"\ttitle: %s\n"
|
||||
"\tclass: %s\n"
|
||||
"\tBit depth: %i\n"
|
||||
"\tFound proper GLXFBConfig (in %i): %s\n",
|
||||
obs_source_get_name(p->source),
|
||||
XCompcap::getWindowName(p->win).c_str(),
|
||||
XCompcap::getWindowClass(p->win).c_str(), attr.depth,
|
||||
nelem, found ? "yes" : "no");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -596,33 +607,28 @@ void XCompcapMain::tick(float seconds)
|
|||
obs_enter_graphics();
|
||||
|
||||
if (p->lockX) {
|
||||
// XDisplayLock is still live so we should already be locked.
|
||||
XLockDisplay(xdisp);
|
||||
XSync(xdisp, 0);
|
||||
}
|
||||
|
||||
if (p->include_border) {
|
||||
gs_copy_texture_region(
|
||||
p->tex, 0, 0,
|
||||
p->gltex,
|
||||
p->cur_cut_left,
|
||||
p->cur_cut_top,
|
||||
width(), height());
|
||||
gs_copy_texture_region(p->tex, 0, 0, p->gltex, p->cur_cut_left,
|
||||
p->cur_cut_top, width(), height());
|
||||
} else {
|
||||
gs_copy_texture_region(
|
||||
p->tex, 0, 0,
|
||||
p->gltex,
|
||||
p->cur_cut_left + p->border,
|
||||
p->cur_cut_top + p->border,
|
||||
width(), height());
|
||||
gs_copy_texture_region(p->tex, 0, 0, p->gltex,
|
||||
p->cur_cut_left + p->border,
|
||||
p->cur_cut_top + p->border, width(),
|
||||
height());
|
||||
}
|
||||
|
||||
if (p->cursor && p->show_cursor) {
|
||||
xcursor_tick(p->cursor);
|
||||
|
||||
p->cursor_outside =
|
||||
p->cursor->x < p->cur_cut_left ||
|
||||
p->cursor->y < p->cur_cut_top ||
|
||||
p->cursor->x > int(p->width - p->cur_cut_right) ||
|
||||
p->cursor->x < p->cur_cut_left ||
|
||||
p->cursor->y < p->cur_cut_top ||
|
||||
p->cursor->x > int(p->width - p->cur_cut_right) ||
|
||||
p->cursor->y > int(p->height - p->cur_cut_bot);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@
|
|||
|
||||
struct XCompcapMain_private;
|
||||
|
||||
class XCompcapMain
|
||||
{
|
||||
public:
|
||||
class XCompcapMain {
|
||||
public:
|
||||
static bool init();
|
||||
static void deinit();
|
||||
|
||||
|
|
@ -22,6 +21,6 @@ class XCompcapMain
|
|||
uint32_t width();
|
||||
uint32_t height();
|
||||
|
||||
private:
|
||||
private:
|
||||
XCompcapMain_private *p;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,38 +2,38 @@
|
|||
|
||||
#include "xcompcap-main.hpp"
|
||||
|
||||
static void* xcompcap_create(obs_data_t *settings, obs_source_t *source)
|
||||
static void *xcompcap_create(obs_data_t *settings, obs_source_t *source)
|
||||
{
|
||||
return new XCompcapMain(settings, source);
|
||||
}
|
||||
|
||||
static void xcompcap_destroy(void *data)
|
||||
{
|
||||
XCompcapMain* cc = (XCompcapMain*)data;
|
||||
XCompcapMain *cc = (XCompcapMain *)data;
|
||||
delete cc;
|
||||
}
|
||||
|
||||
static void xcompcap_video_tick(void* data, float seconds)
|
||||
static void xcompcap_video_tick(void *data, float seconds)
|
||||
{
|
||||
XCompcapMain* cc = (XCompcapMain*)data;
|
||||
XCompcapMain *cc = (XCompcapMain *)data;
|
||||
cc->tick(seconds);
|
||||
}
|
||||
|
||||
static void xcompcap_video_render(void* data, gs_effect_t *effect)
|
||||
static void xcompcap_video_render(void *data, gs_effect_t *effect)
|
||||
{
|
||||
XCompcapMain* cc = (XCompcapMain*)data;
|
||||
XCompcapMain *cc = (XCompcapMain *)data;
|
||||
cc->render(effect);
|
||||
}
|
||||
|
||||
static uint32_t xcompcap_getwidth(void* data)
|
||||
static uint32_t xcompcap_getwidth(void *data)
|
||||
{
|
||||
XCompcapMain* cc = (XCompcapMain*)data;
|
||||
XCompcapMain *cc = (XCompcapMain *)data;
|
||||
return cc->width();
|
||||
}
|
||||
|
||||
static uint32_t xcompcap_getheight(void* data)
|
||||
static uint32_t xcompcap_getheight(void *data)
|
||||
{
|
||||
XCompcapMain* cc = (XCompcapMain*)data;
|
||||
XCompcapMain *cc = (XCompcapMain *)data;
|
||||
return cc->height();
|
||||
}
|
||||
|
||||
|
|
@ -51,11 +51,11 @@ void xcompcap_defaults(obs_data_t *settings)
|
|||
|
||||
void xcompcap_update(void *data, obs_data_t *settings)
|
||||
{
|
||||
XCompcapMain* cc = (XCompcapMain*)data;
|
||||
XCompcapMain *cc = (XCompcapMain *)data;
|
||||
cc->updateSettings(settings);
|
||||
}
|
||||
|
||||
static const char* xcompcap_getname(void*)
|
||||
static const char *xcompcap_getname(void *)
|
||||
{
|
||||
return obs_module_text("XCCapture");
|
||||
}
|
||||
|
|
@ -69,20 +69,19 @@ extern "C" void xcomposite_load(void)
|
|||
memset(&sinfo, 0, sizeof(obs_source_info));
|
||||
|
||||
sinfo.id = "xcomposite_input";
|
||||
sinfo.output_flags = OBS_SOURCE_VIDEO |
|
||||
OBS_SOURCE_CUSTOM_DRAW |
|
||||
OBS_SOURCE_DO_NOT_DUPLICATE;
|
||||
sinfo.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW |
|
||||
OBS_SOURCE_DO_NOT_DUPLICATE;
|
||||
|
||||
sinfo.get_name = xcompcap_getname;
|
||||
sinfo.create = xcompcap_create;
|
||||
sinfo.destroy = xcompcap_destroy;
|
||||
sinfo.get_name = xcompcap_getname;
|
||||
sinfo.create = xcompcap_create;
|
||||
sinfo.destroy = xcompcap_destroy;
|
||||
sinfo.get_properties = xcompcap_props;
|
||||
sinfo.get_defaults = xcompcap_defaults;
|
||||
sinfo.update = xcompcap_update;
|
||||
sinfo.video_tick = xcompcap_video_tick;
|
||||
sinfo.video_render = xcompcap_video_render;
|
||||
sinfo.get_width = xcompcap_getwidth;
|
||||
sinfo.get_height = xcompcap_getheight;
|
||||
sinfo.get_defaults = xcompcap_defaults;
|
||||
sinfo.update = xcompcap_update;
|
||||
sinfo.video_tick = xcompcap_video_tick;
|
||||
sinfo.video_render = xcompcap_video_render;
|
||||
sinfo.get_width = xcompcap_getwidth;
|
||||
sinfo.get_height = xcompcap_getheight;
|
||||
|
||||
obs_register_source(&sinfo);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,26 +26,27 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
* size or by creating a new texture if the size is different
|
||||
*/
|
||||
static void xcb_xcursor_create(xcb_xcursor_t *data,
|
||||
xcb_xfixes_get_cursor_image_reply_t *xc)
|
||||
xcb_xfixes_get_cursor_image_reply_t *xc)
|
||||
{
|
||||
uint32_t *pixels = xcb_xfixes_get_cursor_image_cursor_image(xc);
|
||||
if (!pixels)
|
||||
return;
|
||||
|
||||
if (data->tex && data->last_height == xc->width &&
|
||||
data->last_width == xc->height) {
|
||||
gs_texture_set_image(data->tex, (const uint8_t *) pixels,
|
||||
xc->width * sizeof(uint32_t), false);
|
||||
data->last_width == xc->height) {
|
||||
gs_texture_set_image(data->tex, (const uint8_t *)pixels,
|
||||
xc->width * sizeof(uint32_t), false);
|
||||
} else {
|
||||
if (data->tex)
|
||||
gs_texture_destroy(data->tex);
|
||||
|
||||
data->tex = gs_texture_create(xc->width, xc->height,
|
||||
GS_BGRA, 1, (const uint8_t **) &pixels, GS_DYNAMIC);
|
||||
data->tex = gs_texture_create(xc->width, xc->height, GS_BGRA, 1,
|
||||
(const uint8_t **)&pixels,
|
||||
GS_DYNAMIC);
|
||||
}
|
||||
|
||||
data->last_serial = xc->cursor_serial;
|
||||
data->last_width = xc->width;
|
||||
data->last_width = xc->width;
|
||||
data->last_height = xc->height;
|
||||
}
|
||||
|
||||
|
|
@ -58,8 +59,8 @@ xcb_xcursor_t *xcb_xcursor_init(xcb_connection_t *xcb)
|
|||
|
||||
xcb_xfixes_query_version_cookie_t xfix_c;
|
||||
|
||||
xfix_c = xcb_xfixes_query_version_unchecked(xcb,
|
||||
XCB_XFIXES_MAJOR_VERSION, XCB_XFIXES_MINOR_VERSION);
|
||||
xfix_c = xcb_xfixes_query_version_unchecked(
|
||||
xcb, XCB_XFIXES_MAJOR_VERSION, XCB_XFIXES_MINOR_VERSION);
|
||||
free(xcb_xfixes_query_version_reply(xcb, xfix_c, NULL));
|
||||
|
||||
return data;
|
||||
|
|
@ -73,7 +74,7 @@ void xcb_xcursor_destroy(xcb_xcursor_t *data)
|
|||
}
|
||||
|
||||
void xcb_xcursor_update(xcb_xcursor_t *data,
|
||||
xcb_xfixes_get_cursor_image_reply_t *xc)
|
||||
xcb_xfixes_get_cursor_image_reply_t *xc)
|
||||
{
|
||||
if (!data || !xc)
|
||||
return;
|
||||
|
|
@ -81,8 +82,8 @@ void xcb_xcursor_update(xcb_xcursor_t *data,
|
|||
if (!data->tex || data->last_serial != xc->cursor_serial)
|
||||
xcb_xcursor_create(data, xc);
|
||||
|
||||
data->x = xc->x - data->x_org;
|
||||
data->y = xc->y - data->y_org;
|
||||
data->x = xc->x - data->x_org;
|
||||
data->y = xc->y - data->y_org;
|
||||
data->x_render = data->x - xc->xhot;
|
||||
data->y_render = data->y - xc->yhot;
|
||||
}
|
||||
|
|
@ -92,7 +93,7 @@ void xcb_xcursor_render(xcb_xcursor_t *data)
|
|||
if (!data->tex)
|
||||
return;
|
||||
|
||||
gs_effect_t *effect = gs_get_effect();
|
||||
gs_effect_t *effect = gs_get_effect();
|
||||
gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
|
||||
gs_effect_set_texture(image, data->tex);
|
||||
|
||||
|
|
@ -109,9 +110,8 @@ void xcb_xcursor_render(xcb_xcursor_t *data)
|
|||
gs_blend_state_pop();
|
||||
}
|
||||
|
||||
void xcb_xcursor_offset(xcb_xcursor_t* data, const int x_org, const int y_org)
|
||||
void xcb_xcursor_offset(xcb_xcursor_t *data, const int x_org, const int y_org)
|
||||
{
|
||||
data->x_org = x_org;
|
||||
data->y_org = y_org;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,12 +30,12 @@ typedef struct {
|
|||
unsigned int last_height;
|
||||
gs_texture_t *tex;
|
||||
|
||||
int x;
|
||||
int y;
|
||||
int x_org;
|
||||
int y_org;
|
||||
float x_render;
|
||||
float y_render;
|
||||
int x;
|
||||
int y;
|
||||
int x_org;
|
||||
int y_org;
|
||||
float x_render;
|
||||
float y_render;
|
||||
} xcb_xcursor_t;
|
||||
|
||||
/**
|
||||
|
|
@ -60,7 +60,7 @@ void xcb_xcursor_destroy(xcb_xcursor_t *data);
|
|||
*
|
||||
*/
|
||||
void xcb_xcursor_update(xcb_xcursor_t *data,
|
||||
xcb_xfixes_get_cursor_image_reply_t *xc);
|
||||
xcb_xfixes_get_cursor_image_reply_t *xc);
|
||||
|
||||
/**
|
||||
* Draw the cursor
|
||||
|
|
|
|||
|
|
@ -28,12 +28,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
* Theres a lot of talk about this in other implementation and they tend to
|
||||
* be really complicated, but this naive approach seems to work fine ...
|
||||
*/
|
||||
static uint32_t *xcursor_pixels(XFixesCursorImage *xc) {
|
||||
static uint32_t *xcursor_pixels(XFixesCursorImage *xc)
|
||||
{
|
||||
uint_fast32_t size = xc->width * xc->height;
|
||||
uint32_t *pixels = bmalloc(size * sizeof(uint32_t));
|
||||
|
||||
for (uint_fast32_t i = 0; i < size; ++i)
|
||||
pixels[i] = (uint32_t) xc->pixels[i];
|
||||
pixels[i] = (uint32_t)xc->pixels[i];
|
||||
|
||||
return pixels;
|
||||
}
|
||||
|
|
@ -42,22 +43,23 @@ static uint32_t *xcursor_pixels(XFixesCursorImage *xc) {
|
|||
* Create the cursor texture, either by updating if the new cursor has the same
|
||||
* size or by creating a new texture if the size is different
|
||||
*/
|
||||
static void xcursor_create(xcursor_t *data, XFixesCursorImage *xc) {
|
||||
static void xcursor_create(xcursor_t *data, XFixesCursorImage *xc)
|
||||
{
|
||||
uint32_t *pixels = xcursor_pixels(xc);
|
||||
if (!pixels)
|
||||
return;
|
||||
|
||||
if (data->tex
|
||||
&& data->last_height == xc->width
|
||||
&& data->last_width == xc->height) {
|
||||
gs_texture_set_image(data->tex, (const uint8_t *) pixels,
|
||||
xc->width * sizeof(uint32_t), False);
|
||||
if (data->tex && data->last_height == xc->width &&
|
||||
data->last_width == xc->height) {
|
||||
gs_texture_set_image(data->tex, (const uint8_t *)pixels,
|
||||
xc->width * sizeof(uint32_t), False);
|
||||
} else {
|
||||
if (data->tex)
|
||||
gs_texture_destroy(data->tex);
|
||||
|
||||
data->tex = gs_texture_create(xc->width, xc->height,
|
||||
GS_BGRA, 1, (const uint8_t **) &pixels, GS_DYNAMIC);
|
||||
data->tex = gs_texture_create(xc->width, xc->height, GS_BGRA, 1,
|
||||
(const uint8_t **)&pixels,
|
||||
GS_DYNAMIC);
|
||||
}
|
||||
|
||||
bfree(pixels);
|
||||
|
|
@ -67,7 +69,8 @@ static void xcursor_create(xcursor_t *data, XFixesCursorImage *xc) {
|
|||
data->last_height = xc->height;
|
||||
}
|
||||
|
||||
xcursor_t *xcursor_init(Display *dpy) {
|
||||
xcursor_t *xcursor_init(Display *dpy)
|
||||
{
|
||||
xcursor_t *data = bzalloc(sizeof(xcursor_t));
|
||||
|
||||
data->dpy = dpy;
|
||||
|
|
@ -76,13 +79,15 @@ xcursor_t *xcursor_init(Display *dpy) {
|
|||
return data;
|
||||
}
|
||||
|
||||
void xcursor_destroy(xcursor_t *data) {
|
||||
void xcursor_destroy(xcursor_t *data)
|
||||
{
|
||||
if (data->tex)
|
||||
gs_texture_destroy(data->tex);
|
||||
bfree(data);
|
||||
}
|
||||
|
||||
void xcursor_tick(xcursor_t *data) {
|
||||
void xcursor_tick(xcursor_t *data)
|
||||
{
|
||||
XFixesCursorImage *xc = XFixesGetCursorImage(data->dpy);
|
||||
if (!xc)
|
||||
return;
|
||||
|
|
@ -98,11 +103,12 @@ void xcursor_tick(xcursor_t *data) {
|
|||
XFree(xc);
|
||||
}
|
||||
|
||||
void xcursor_render(xcursor_t *data) {
|
||||
void xcursor_render(xcursor_t *data)
|
||||
{
|
||||
if (!data->tex)
|
||||
return;
|
||||
|
||||
gs_effect_t *effect = gs_get_effect();
|
||||
gs_effect_t *effect = gs_get_effect();
|
||||
gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
|
||||
gs_effect_set_texture(image, data->tex);
|
||||
|
||||
|
|
@ -119,9 +125,8 @@ void xcursor_render(xcursor_t *data) {
|
|||
gs_blend_state_pop();
|
||||
}
|
||||
|
||||
void xcursor_offset(xcursor_t* data, int_fast32_t x_org, int_fast32_t y_org)
|
||||
void xcursor_offset(xcursor_t *data, int_fast32_t x_org, int_fast32_t y_org)
|
||||
{
|
||||
data->x_org = x_org;
|
||||
data->y_org = y_org;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ bool xinerama_is_active(xcb_connection_t *xcb)
|
|||
|
||||
bool active = true;
|
||||
xcb_xinerama_is_active_cookie_t xnr_c;
|
||||
xcb_xinerama_is_active_reply_t *xnr_r;
|
||||
xcb_xinerama_is_active_reply_t *xnr_r;
|
||||
|
||||
xnr_c = xcb_xinerama_is_active_unchecked(xcb);
|
||||
xnr_r = xcb_xinerama_is_active_reply(xcb, xnr_c, NULL);
|
||||
|
|
@ -48,7 +48,7 @@ int xinerama_screen_count(xcb_connection_t *xcb)
|
|||
|
||||
int screens = 0;
|
||||
xcb_xinerama_query_screens_cookie_t scr_c;
|
||||
xcb_xinerama_query_screens_reply_t *scr_r;
|
||||
xcb_xinerama_query_screens_reply_t *scr_r;
|
||||
|
||||
scr_c = xcb_xinerama_query_screens_unchecked(xcb);
|
||||
scr_r = xcb_xinerama_query_screens_reply(xcb, scr_c, NULL);
|
||||
|
|
@ -60,15 +60,15 @@ int xinerama_screen_count(xcb_connection_t *xcb)
|
|||
}
|
||||
|
||||
int xinerama_screen_geo(xcb_connection_t *xcb, int_fast32_t screen,
|
||||
int_fast32_t *x, int_fast32_t *y,
|
||||
int_fast32_t *w, int_fast32_t *h)
|
||||
int_fast32_t *x, int_fast32_t *y, int_fast32_t *w,
|
||||
int_fast32_t *h)
|
||||
{
|
||||
if (!xcb)
|
||||
goto fail;
|
||||
|
||||
bool success = false;
|
||||
xcb_xinerama_query_screens_cookie_t scr_c;
|
||||
xcb_xinerama_query_screens_reply_t *scr_r;
|
||||
xcb_xinerama_query_screens_reply_t *scr_r;
|
||||
xcb_xinerama_screen_info_iterator_t iter;
|
||||
|
||||
scr_c = xcb_xinerama_query_screens_unchecked(xcb);
|
||||
|
|
@ -113,7 +113,7 @@ int randr_screen_count(xcb_connection_t *xcb)
|
|||
screen = xcb_setup_roots_iterator(xcb_get_setup(xcb)).data;
|
||||
|
||||
xcb_randr_get_screen_resources_cookie_t res_c;
|
||||
xcb_randr_get_screen_resources_reply_t* res_r;
|
||||
xcb_randr_get_screen_resources_reply_t *res_r;
|
||||
|
||||
res_c = xcb_randr_get_screen_resources(xcb, screen->root);
|
||||
res_r = xcb_randr_get_screen_resources_reply(xcb, res_c, 0);
|
||||
|
|
@ -124,15 +124,14 @@ int randr_screen_count(xcb_connection_t *xcb)
|
|||
}
|
||||
|
||||
int randr_screen_geo(xcb_connection_t *xcb, int_fast32_t screen,
|
||||
int_fast32_t *x, int_fast32_t *y,
|
||||
int_fast32_t *w, int_fast32_t *h,
|
||||
xcb_screen_t **rscreen)
|
||||
int_fast32_t *x, int_fast32_t *y, int_fast32_t *w,
|
||||
int_fast32_t *h, xcb_screen_t **rscreen)
|
||||
{
|
||||
xcb_screen_t *xscreen;
|
||||
xscreen = xcb_setup_roots_iterator(xcb_get_setup(xcb)).data;
|
||||
|
||||
xcb_randr_get_screen_resources_cookie_t res_c;
|
||||
xcb_randr_get_screen_resources_reply_t* res_r;
|
||||
xcb_randr_get_screen_resources_reply_t *res_r;
|
||||
|
||||
res_c = xcb_randr_get_screen_resources(xcb, xscreen->root);
|
||||
res_r = xcb_randr_get_screen_resources_reply(xcb, res_c, 0);
|
||||
|
|
@ -168,8 +167,8 @@ fail:
|
|||
return -1;
|
||||
}
|
||||
|
||||
int x11_screen_geo(xcb_connection_t *xcb, int_fast32_t screen,
|
||||
int_fast32_t *w, int_fast32_t *h)
|
||||
int x11_screen_geo(xcb_connection_t *xcb, int_fast32_t screen, int_fast32_t *w,
|
||||
int_fast32_t *h)
|
||||
{
|
||||
if (!xcb)
|
||||
goto fail;
|
||||
|
|
@ -194,14 +193,14 @@ fail:
|
|||
return -1;
|
||||
}
|
||||
|
||||
xcb_shm_t* xshm_xcb_attach(xcb_connection_t *xcb, const int w, const int h)
|
||||
xcb_shm_t *xshm_xcb_attach(xcb_connection_t *xcb, const int w, const int h)
|
||||
{
|
||||
if (!xcb)
|
||||
return NULL;
|
||||
|
||||
xcb_shm_t *shm = bzalloc(sizeof(xcb_shm_t));
|
||||
shm->xcb = xcb;
|
||||
shm->seg = xcb_generate_id(shm->xcb);
|
||||
shm->xcb = xcb;
|
||||
shm->seg = xcb_generate_id(shm->xcb);
|
||||
|
||||
shm->shmid = shmget(IPC_PRIVATE, w * h * 4, IPC_CREAT | 0777);
|
||||
if (shm->shmid == -1)
|
||||
|
|
@ -224,7 +223,7 @@ void xshm_xcb_detach(xcb_shm_t *shm)
|
|||
|
||||
xcb_shm_detach(shm->xcb, shm->seg);
|
||||
|
||||
if ((char *) shm->data != (char *) -1)
|
||||
if ((char *)shm->data != (char *)-1)
|
||||
shmdt(shm->data);
|
||||
|
||||
if (shm->shmid != -1)
|
||||
|
|
|
|||
|
|
@ -27,9 +27,9 @@ extern "C" {
|
|||
|
||||
typedef struct {
|
||||
xcb_connection_t *xcb;
|
||||
xcb_shm_seg_t seg;
|
||||
int shmid;
|
||||
uint8_t *data;
|
||||
xcb_shm_seg_t seg;
|
||||
int shmid;
|
||||
uint8_t *data;
|
||||
} xcb_shm_t;
|
||||
|
||||
/**
|
||||
|
|
@ -61,8 +61,8 @@ int xinerama_screen_count(xcb_connection_t *xcb);
|
|||
* @return < 0 on error
|
||||
*/
|
||||
int xinerama_screen_geo(xcb_connection_t *xcb, int_fast32_t screen,
|
||||
int_fast32_t *x, int_fast32_t *y,
|
||||
int_fast32_t *w, int_fast32_t *h);
|
||||
int_fast32_t *x, int_fast32_t *y, int_fast32_t *w,
|
||||
int_fast32_t *h);
|
||||
|
||||
/**
|
||||
* Check for Randr extension
|
||||
|
|
@ -93,9 +93,8 @@ int randr_screen_count(xcb_connection_t *xcb);
|
|||
* @return < 0 on error
|
||||
*/
|
||||
int randr_screen_geo(xcb_connection_t *xcb, int_fast32_t screen,
|
||||
int_fast32_t *x, int_fast32_t *y,
|
||||
int_fast32_t *w, int_fast32_t *h,
|
||||
xcb_screen_t **rscreen);
|
||||
int_fast32_t *x, int_fast32_t *y, int_fast32_t *w,
|
||||
int_fast32_t *h, xcb_screen_t **rscreen);
|
||||
|
||||
/**
|
||||
* Get screen geometry for a X11 screen
|
||||
|
|
@ -109,8 +108,8 @@ int randr_screen_geo(xcb_connection_t *xcb, int_fast32_t screen,
|
|||
*
|
||||
* @return < 0 on error
|
||||
*/
|
||||
int x11_screen_geo(xcb_connection_t *xcb, int_fast32_t screen,
|
||||
int_fast32_t *w, int_fast32_t *h);
|
||||
int x11_screen_geo(xcb_connection_t *xcb, int_fast32_t screen, int_fast32_t *w,
|
||||
int_fast32_t *h);
|
||||
|
||||
/**
|
||||
* Attach a shared memory segment to the X-Server
|
||||
|
|
|
|||
|
|
@ -33,26 +33,26 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#define blog(level, msg, ...) blog(level, "xshm-input: " msg, ##__VA_ARGS__)
|
||||
|
||||
struct xshm_data {
|
||||
obs_source_t *source;
|
||||
obs_source_t *source;
|
||||
|
||||
xcb_connection_t *xcb;
|
||||
xcb_screen_t *xcb_screen;
|
||||
xcb_shm_t *xshm;
|
||||
xcb_xcursor_t *cursor;
|
||||
xcb_screen_t *xcb_screen;
|
||||
xcb_shm_t *xshm;
|
||||
xcb_xcursor_t *cursor;
|
||||
|
||||
char *server;
|
||||
uint_fast32_t screen_id;
|
||||
int_fast32_t x_org;
|
||||
int_fast32_t y_org;
|
||||
int_fast32_t width;
|
||||
int_fast32_t height;
|
||||
char *server;
|
||||
uint_fast32_t screen_id;
|
||||
int_fast32_t x_org;
|
||||
int_fast32_t y_org;
|
||||
int_fast32_t width;
|
||||
int_fast32_t height;
|
||||
|
||||
gs_texture_t *texture;
|
||||
gs_texture_t *texture;
|
||||
|
||||
bool show_cursor;
|
||||
bool use_xinerama;
|
||||
bool use_randr;
|
||||
bool advanced;
|
||||
bool show_cursor;
|
||||
bool use_xinerama;
|
||||
bool use_randr;
|
||||
bool advanced;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -66,8 +66,8 @@ static inline void xshm_resize_texture(struct xshm_data *data)
|
|||
{
|
||||
if (data->texture)
|
||||
gs_texture_destroy(data->texture);
|
||||
data->texture = gs_texture_create(data->width, data->height,
|
||||
GS_BGRA, 1, NULL, GS_DYNAMIC);
|
||||
data->texture = gs_texture_create(data->width, data->height, GS_BGRA, 1,
|
||||
NULL, GS_DYNAMIC);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -102,26 +102,23 @@ static int_fast32_t xshm_update_geometry(struct xshm_data *data)
|
|||
int_fast32_t old_height = data->height;
|
||||
|
||||
if (data->use_randr) {
|
||||
if (randr_screen_geo(data->xcb, data->screen_id,
|
||||
&data->x_org, &data->y_org,
|
||||
&data->width, &data->height,
|
||||
&data->xcb_screen) < 0) {
|
||||
if (randr_screen_geo(data->xcb, data->screen_id, &data->x_org,
|
||||
&data->y_org, &data->width, &data->height,
|
||||
&data->xcb_screen) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if (data->use_xinerama) {
|
||||
} else if (data->use_xinerama) {
|
||||
if (xinerama_screen_geo(data->xcb, data->screen_id,
|
||||
&data->x_org, &data->y_org,
|
||||
&data->width, &data->height) < 0) {
|
||||
&data->x_org, &data->y_org,
|
||||
&data->width, &data->height) < 0) {
|
||||
return -1;
|
||||
}
|
||||
data->xcb_screen = xcb_get_screen(data->xcb, 0);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
data->x_org = 0;
|
||||
data->y_org = 0;
|
||||
if (x11_screen_geo(data->xcb, data->screen_id,
|
||||
&data->width, &data->height) < 0) {
|
||||
if (x11_screen_geo(data->xcb, data->screen_id, &data->width,
|
||||
&data->height) < 0) {
|
||||
return -1;
|
||||
}
|
||||
data->xcb_screen = xcb_get_screen(data->xcb, data->screen_id);
|
||||
|
|
@ -132,9 +129,10 @@ static int_fast32_t xshm_update_geometry(struct xshm_data *data)
|
|||
return -1;
|
||||
}
|
||||
|
||||
blog(LOG_INFO, "Geometry %"PRIdFAST32"x%"PRIdFAST32
|
||||
" @ %"PRIdFAST32",%"PRIdFAST32,
|
||||
data->width, data->height, data->x_org, data->y_org);
|
||||
blog(LOG_INFO,
|
||||
"Geometry %" PRIdFAST32 "x%" PRIdFAST32 " @ %" PRIdFAST32
|
||||
",%" PRIdFAST32,
|
||||
data->width, data->height, data->x_org, data->y_org);
|
||||
|
||||
if (old_width == data->width && old_height == data->height)
|
||||
return 0;
|
||||
|
|
@ -145,7 +143,7 @@ static int_fast32_t xshm_update_geometry(struct xshm_data *data)
|
|||
/**
|
||||
* Returns the name of the plugin
|
||||
*/
|
||||
static const char* xshm_getname(void *unused)
|
||||
static const char *xshm_getname(void *unused)
|
||||
{
|
||||
UNUSED_PARAMETER(unused);
|
||||
return obs_module_text("X11SharedMemoryScreenInput");
|
||||
|
|
@ -190,8 +188,8 @@ static void xshm_capture_stop(struct xshm_data *data)
|
|||
*/
|
||||
static void xshm_capture_start(struct xshm_data *data)
|
||||
{
|
||||
const char *server = (data->advanced && *data->server)
|
||||
? data->server : NULL;
|
||||
const char *server = (data->advanced && *data->server) ? data->server
|
||||
: NULL;
|
||||
|
||||
data->xcb = xcb_connect(server, NULL);
|
||||
if (!data->xcb || xcb_connection_has_error(data->xcb)) {
|
||||
|
|
@ -239,10 +237,10 @@ static void xshm_update(void *vptr, obs_data_t *settings)
|
|||
|
||||
xshm_capture_stop(data);
|
||||
|
||||
data->screen_id = obs_data_get_int(settings, "screen");
|
||||
data->screen_id = obs_data_get_int(settings, "screen");
|
||||
data->show_cursor = obs_data_get_bool(settings, "show_cursor");
|
||||
data->advanced = obs_data_get_bool(settings, "advanced");
|
||||
data->server = bstrdup(obs_data_get_string(settings, "server"));
|
||||
data->advanced = obs_data_get_bool(settings, "advanced");
|
||||
data->server = bstrdup(obs_data_get_string(settings, "server"));
|
||||
|
||||
xshm_capture_start(data);
|
||||
}
|
||||
|
|
@ -260,11 +258,11 @@ static void xshm_defaults(obs_data_t *defaults)
|
|||
/**
|
||||
* Toggle visibility of advanced settings
|
||||
*/
|
||||
static bool xshm_toggle_advanced(obs_properties_t *props,
|
||||
obs_property_t *p, obs_data_t *settings)
|
||||
static bool xshm_toggle_advanced(obs_properties_t *props, obs_property_t *p,
|
||||
obs_data_t *settings)
|
||||
{
|
||||
UNUSED_PARAMETER(p);
|
||||
const bool visible = obs_data_get_bool(settings, "advanced");
|
||||
const bool visible = obs_data_get_bool(settings, "advanced");
|
||||
obs_property_t *server = obs_properties_get(props, "server");
|
||||
|
||||
obs_property_set_visible(server, visible);
|
||||
|
|
@ -278,14 +276,14 @@ static bool xshm_toggle_advanced(obs_properties_t *props,
|
|||
/**
|
||||
* The x server was changed
|
||||
*/
|
||||
static bool xshm_server_changed(obs_properties_t *props,
|
||||
obs_property_t *p, obs_data_t *settings)
|
||||
static bool xshm_server_changed(obs_properties_t *props, obs_property_t *p,
|
||||
obs_data_t *settings)
|
||||
{
|
||||
UNUSED_PARAMETER(p);
|
||||
|
||||
bool advanced = obs_data_get_bool(settings, "advanced");
|
||||
bool advanced = obs_data_get_bool(settings, "advanced");
|
||||
int_fast32_t old_screen = obs_data_get_int(settings, "screen");
|
||||
const char *server = obs_data_get_string(settings, "server");
|
||||
const char *server = obs_data_get_string(settings, "server");
|
||||
obs_property_t *screens = obs_properties_get(props, "screen");
|
||||
|
||||
/* we want a real NULL here in case there is no string here */
|
||||
|
|
@ -303,11 +301,11 @@ static bool xshm_server_changed(obs_properties_t *props,
|
|||
dstr_init(&screen_info);
|
||||
bool randr = randr_is_active(xcb);
|
||||
bool xinerama = xinerama_is_active(xcb);
|
||||
int_fast32_t count = (randr) ?
|
||||
randr_screen_count(xcb) :
|
||||
(xinerama) ?
|
||||
xinerama_screen_count(xcb) :
|
||||
xcb_setup_roots_length(xcb_get_setup(xcb));
|
||||
int_fast32_t count =
|
||||
(randr) ? randr_screen_count(xcb)
|
||||
: (xinerama)
|
||||
? xinerama_screen_count(xcb)
|
||||
: xcb_setup_roots_length(xcb_get_setup(xcb));
|
||||
|
||||
for (int_fast32_t i = 0; i < count; ++i) {
|
||||
int_fast32_t x, y, w, h;
|
||||
|
|
@ -320,21 +318,22 @@ static bool xshm_server_changed(obs_properties_t *props,
|
|||
else
|
||||
x11_screen_geo(xcb, i, &w, &h);
|
||||
|
||||
dstr_printf(&screen_info, "Screen %"PRIuFAST32" (%"PRIuFAST32
|
||||
"x%"PRIuFAST32" @ %"PRIuFAST32
|
||||
",%"PRIuFAST32")", i, w, h, x, y);
|
||||
dstr_printf(&screen_info,
|
||||
"Screen %" PRIuFAST32 " (%" PRIuFAST32
|
||||
"x%" PRIuFAST32 " @ %" PRIuFAST32 ",%" PRIuFAST32
|
||||
")",
|
||||
i, w, h, x, y);
|
||||
|
||||
obs_property_list_add_int(screens, screen_info.array, i);
|
||||
}
|
||||
|
||||
/* handle missing screen */
|
||||
if (old_screen + 1 > count) {
|
||||
dstr_printf(&screen_info, "Screen %"PRIuFAST32" (not found)",
|
||||
old_screen);
|
||||
size_t index = obs_property_list_add_int(screens,
|
||||
screen_info.array, old_screen);
|
||||
dstr_printf(&screen_info, "Screen %" PRIuFAST32 " (not found)",
|
||||
old_screen);
|
||||
size_t index = obs_property_list_add_int(
|
||||
screens, screen_info.array, old_screen);
|
||||
obs_property_list_item_disable(screens, index, true);
|
||||
|
||||
}
|
||||
|
||||
dstr_free(&screen_info);
|
||||
|
|
@ -355,13 +354,13 @@ static obs_properties_t *xshm_properties(void *vptr)
|
|||
obs_properties_t *props = obs_properties_create();
|
||||
|
||||
obs_properties_add_list(props, "screen", obs_module_text("Screen"),
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
obs_properties_add_bool(props, "show_cursor",
|
||||
obs_module_text("CaptureCursor"));
|
||||
obs_property_t *advanced = obs_properties_add_bool(props, "advanced",
|
||||
obs_module_text("AdvancedSettings"));
|
||||
obs_property_t *server = obs_properties_add_text(props, "server",
|
||||
obs_module_text("XServer"), OBS_TEXT_DEFAULT);
|
||||
obs_module_text("CaptureCursor"));
|
||||
obs_property_t *advanced = obs_properties_add_bool(
|
||||
props, "advanced", obs_module_text("AdvancedSettings"));
|
||||
obs_property_t *server = obs_properties_add_text(
|
||||
props, "server", obs_module_text("XServer"), OBS_TEXT_DEFAULT);
|
||||
|
||||
obs_property_set_modified_callback(advanced, xshm_toggle_advanced);
|
||||
obs_property_set_modified_callback(server, xshm_server_changed);
|
||||
|
|
@ -415,14 +414,16 @@ static void xshm_video_tick(void *vptr, float seconds)
|
|||
if (!obs_source_showing(data->source))
|
||||
return;
|
||||
|
||||
xcb_shm_get_image_cookie_t img_c;
|
||||
xcb_shm_get_image_reply_t *img_r;
|
||||
xcb_shm_get_image_cookie_t img_c;
|
||||
xcb_shm_get_image_reply_t *img_r;
|
||||
xcb_xfixes_get_cursor_image_cookie_t cur_c;
|
||||
xcb_xfixes_get_cursor_image_reply_t *cur_r;
|
||||
xcb_xfixes_get_cursor_image_reply_t *cur_r;
|
||||
|
||||
img_c = xcb_shm_get_image_unchecked(data->xcb, data->xcb_screen->root,
|
||||
data->x_org, data->y_org, data->width, data->height,
|
||||
~0, XCB_IMAGE_FORMAT_Z_PIXMAP, data->xshm->seg, 0);
|
||||
data->x_org, data->y_org,
|
||||
data->width, data->height, ~0,
|
||||
XCB_IMAGE_FORMAT_Z_PIXMAP,
|
||||
data->xshm->seg, 0);
|
||||
cur_c = xcb_xfixes_get_cursor_image_unchecked(data->xcb);
|
||||
|
||||
img_r = xcb_shm_get_image_reply(data->xcb, img_c, NULL);
|
||||
|
|
@ -433,8 +434,8 @@ static void xshm_video_tick(void *vptr, float seconds)
|
|||
|
||||
obs_enter_graphics();
|
||||
|
||||
gs_texture_set_image(data->texture, (void *) data->xshm->data,
|
||||
data->width * 4, false);
|
||||
gs_texture_set_image(data->texture, (void *)data->xshm->data,
|
||||
data->width * 4, false);
|
||||
xcb_xcursor_update(data->cursor, cur_r);
|
||||
|
||||
obs_leave_graphics();
|
||||
|
|
@ -491,19 +492,18 @@ static uint32_t xshm_getheight(void *vptr)
|
|||
}
|
||||
|
||||
struct obs_source_info xshm_input = {
|
||||
.id = "xshm_input",
|
||||
.type = OBS_SOURCE_TYPE_INPUT,
|
||||
.output_flags = OBS_SOURCE_VIDEO |
|
||||
OBS_SOURCE_CUSTOM_DRAW |
|
||||
OBS_SOURCE_DO_NOT_DUPLICATE,
|
||||
.get_name = xshm_getname,
|
||||
.create = xshm_create,
|
||||
.destroy = xshm_destroy,
|
||||
.update = xshm_update,
|
||||
.get_defaults = xshm_defaults,
|
||||
.id = "xshm_input",
|
||||
.type = OBS_SOURCE_TYPE_INPUT,
|
||||
.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW |
|
||||
OBS_SOURCE_DO_NOT_DUPLICATE,
|
||||
.get_name = xshm_getname,
|
||||
.create = xshm_create,
|
||||
.destroy = xshm_destroy,
|
||||
.update = xshm_update,
|
||||
.get_defaults = xshm_defaults,
|
||||
.get_properties = xshm_properties,
|
||||
.video_tick = xshm_video_tick,
|
||||
.video_render = xshm_video_render,
|
||||
.get_width = xshm_getwidth,
|
||||
.get_height = xshm_getheight
|
||||
.video_tick = xshm_video_tick,
|
||||
.video_render = xshm_video_render,
|
||||
.get_width = xshm_getwidth,
|
||||
.get_height = xshm_getheight,
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue