|
|
@@ -4,6 +4,7 @@
|
|
|
#include <X11/extensions/Xcomposite.h>
|
|
|
|
|
|
#include <unordered_set>
|
|
|
+#include <map>
|
|
|
#include <pthread.h>
|
|
|
|
|
|
#include <obs-module.h>
|
|
|
@@ -31,22 +32,6 @@ void cleanupDisplay()
|
|
|
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;
|
|
|
-}
|
|
|
-
|
|
|
// Specification for checking for ewmh support at
|
|
|
// http://standards.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472693600
|
|
|
|
|
|
@@ -181,42 +166,73 @@ std::string getWindowAtom(Window win, const char *atom)
|
|
|
return res;
|
|
|
}
|
|
|
|
|
|
-std::string getWindowCommand(Window win)
|
|
|
+static std::map<XCompcapMain *, Window> windowForSource;
|
|
|
+static std::unordered_set<XCompcapMain *> changedSources;
|
|
|
+static pthread_mutex_t changeLock = PTHREAD_MUTEX_INITIALIZER;
|
|
|
+
|
|
|
+void registerSource(XCompcapMain *source, Window win)
|
|
|
{
|
|
|
- Atom xi = XInternAtom(disp(), "WM_COMMAND", false);
|
|
|
- int n;
|
|
|
- char **list = 0;
|
|
|
- XTextProperty tp;
|
|
|
- std::string res = "error";
|
|
|
+ PLock lock(&changeLock);
|
|
|
|
|
|
- XGetTextProperty(disp(), win, &tp, xi);
|
|
|
+ blog(LOG_DEBUG, "registerSource(source=%p, win=%ld)", source, win);
|
|
|
|
|
|
- if (!tp.nitems)
|
|
|
- return std::string();
|
|
|
+ auto it = windowForSource.find(source);
|
|
|
|
|
|
- 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 (it != windowForSource.end()) {
|
|
|
+ windowForSource.erase(it);
|
|
|
}
|
|
|
|
|
|
- XFree(tp.value);
|
|
|
+ // Subscribe to Events
|
|
|
+ XSelectInput(disp(), win,
|
|
|
+ StructureNotifyMask | ExposureMask | VisibilityChangeMask);
|
|
|
+ XCompositeRedirectWindow(disp(), win, CompositeRedirectAutomatic);
|
|
|
+ XSync(disp(), 0);
|
|
|
|
|
|
- return res;
|
|
|
+ windowForSource.insert(std::make_pair(source, win));
|
|
|
}
|
|
|
|
|
|
-int getWindowPid(Window win)
|
|
|
+void unregisterSource(XCompcapMain *source)
|
|
|
{
|
|
|
- UNUSED_PARAMETER(win);
|
|
|
- return 1234; //TODO
|
|
|
+ PLock lock(&changeLock);
|
|
|
+
|
|
|
+ blog(LOG_DEBUG, "unregisterSource(source=%p)", source);
|
|
|
+ {
|
|
|
+ auto it = windowForSource.find(source);
|
|
|
+ Window win = it->second;
|
|
|
+
|
|
|
+ if (it != windowForSource.end()) {
|
|
|
+ windowForSource.erase(it);
|
|
|
+ }
|
|
|
+
|
|
|
+ // check if there are still sources listening for the same window
|
|
|
+ it = windowForSource.begin();
|
|
|
+ bool windowInUse = false;
|
|
|
+ while (it != windowForSource.end()) {
|
|
|
+ if (it->second == win) {
|
|
|
+ windowInUse = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ it++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!windowInUse) {
|
|
|
+ // Last source released, stop listening for events.
|
|
|
+ XSelectInput(disp(), win, 0);
|
|
|
+ XCompositeUnredirectWindow(disp(), win,
|
|
|
+ CompositeRedirectAutomatic);
|
|
|
+ XSync(disp(), 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ auto it = changedSources.find(source);
|
|
|
+
|
|
|
+ if (it != changedSources.end()) {
|
|
|
+ changedSources.erase(it);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-static std::unordered_set<Window> changedWindows;
|
|
|
-static pthread_mutex_t changeLock = PTHREAD_MUTEX_INITIALIZER;
|
|
|
void processEvents()
|
|
|
{
|
|
|
PLock lock(&changeLock);
|
|
|
@@ -225,36 +241,56 @@ void processEvents()
|
|
|
|
|
|
while (XEventsQueued(disp(), QueuedAfterReading) > 0) {
|
|
|
XEvent ev;
|
|
|
+ Window win = 0;
|
|
|
|
|
|
XNextEvent(disp(), &ev);
|
|
|
|
|
|
if (ev.type == ConfigureNotify)
|
|
|
- changedWindows.insert(ev.xconfigure.event);
|
|
|
+ win = ev.xconfigure.event;
|
|
|
+
|
|
|
+ else if (ev.type == MapNotify)
|
|
|
+ win = ev.xmap.event;
|
|
|
+
|
|
|
+ else if (ev.type == Expose)
|
|
|
+ win = ev.xexpose.window;
|
|
|
|
|
|
- if (ev.type == MapNotify)
|
|
|
- changedWindows.insert(ev.xmap.event);
|
|
|
+ else if (ev.type == VisibilityNotify)
|
|
|
+ win = ev.xvisibility.window;
|
|
|
|
|
|
- if (ev.type == Expose)
|
|
|
- changedWindows.insert(ev.xexpose.window);
|
|
|
+ else if (ev.type == DestroyNotify)
|
|
|
+ win = ev.xdestroywindow.event;
|
|
|
|
|
|
- if (ev.type == VisibilityNotify)
|
|
|
- changedWindows.insert(ev.xvisibility.window);
|
|
|
+ if (win != 0) {
|
|
|
+ blog(LOG_DEBUG, "processEvents(): windowChanged=%ld",
|
|
|
+ win);
|
|
|
|
|
|
- if (ev.type == DestroyNotify)
|
|
|
- changedWindows.insert(ev.xdestroywindow.event);
|
|
|
+ auto it = windowForSource.begin();
|
|
|
+
|
|
|
+ while (it != windowForSource.end()) {
|
|
|
+ if (it->second == win) {
|
|
|
+ blog(LOG_DEBUG,
|
|
|
+ "processEvents(): sourceChanged=%p",
|
|
|
+ it->first);
|
|
|
+ changedSources.insert(it->first);
|
|
|
+ }
|
|
|
+ it++;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
XUnlockDisplay(disp());
|
|
|
}
|
|
|
|
|
|
-bool windowWasReconfigured(Window win)
|
|
|
+bool sourceWasReconfigured(XCompcapMain *source)
|
|
|
{
|
|
|
PLock lock(&changeLock);
|
|
|
|
|
|
- auto it = changedWindows.find(win);
|
|
|
+ auto it = changedSources.find(source);
|
|
|
|
|
|
- if (it != changedWindows.end()) {
|
|
|
- changedWindows.erase(it);
|
|
|
+ if (it != changedSources.end()) {
|
|
|
+ changedSources.erase(it);
|
|
|
+ blog(LOG_DEBUG, "sourceWasReconfigured(source=%p)=true",
|
|
|
+ source);
|
|
|
return true;
|
|
|
}
|
|
|
|