Fix for crash on Scene Collection change

functionNamesByPriority was cleared while in use when changing Scene Collection.
This commit is contained in:
WarmUpTill 2018-02-16 20:42:58 +01:00 committed by GitHub
parent ad052e9323
commit dd2d70bb9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 346 additions and 355 deletions

View File

@ -1,323 +1,323 @@
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <X11/extensions/scrnsaver.h>
#undef Bool
#undef CursorShape
#undef Expose
#undef KeyPress
#undef KeyRelease
#undef FocusIn
#undef FocusOut
#undef FontChange
#undef None
#undef Status
#undef Unsorted
#include <util/platform.h>
#include "advanced-scene-switcher.hpp"
using namespace std;
static Display* xdisplay = 0;
Display *disp()
{
if (!xdisplay)
xdisplay = XOpenDisplay(NULL);
return xdisplay;
}
void cleanupDisplay()
{
if (!xdisplay)
return;
XCloseDisplay(xdisplay);
xdisplay = 0;
}
static 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);
if (status == Success) {
if (num > 0) {
ewmh_window = ((Window*)data)[0];
}
if (data) {
XFree(data);
data = NULL;
}
}
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);
}
}
return ewmh_window != 0;
}
static std::vector<Window> getTopLevelWindows()
{
std::vector<Window> res;
res.resize(0);
if (!ewmhIsSupported()) {
return 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) {
continue;
}
for (unsigned long i = 0; i < num; ++i)
res.emplace_back(data[i]);
XFree(data);
}
return res;
}
static std::string GetWindowTitle(size_t i)
{
Window w = getTopLevelWindows().at(i);
std::string windowTitle;
char* name;
int status = XFetchName(disp(), w, &name);
if (status >= Success && name != nullptr)
{
std::string str(name);
windowTitle = str;
}
XFree(name);
return windowTitle;
}
void GetWindowList(vector<string> &windows)
{
windows.resize(0);
for (size_t i = 0; i < getTopLevelWindows().size(); ++i){
if (GetWindowTitle(i) != "")
windows.emplace_back(GetWindowTitle(i));
}
}
void GetCurrentWindowTitle(string &title)
{
if (!ewmhIsSupported()) {
return;
}
Atom active = XInternAtom(disp(), "_NET_ACTIVE_WINDOW", true);
Atom actualType;
int format;
unsigned long num, bytes;
Window* data = 0;
char* name;
Window rootWin = RootWindow(disp(), 0);
XGetWindowProperty(
disp(),
rootWin,
active,
0L,
~0L,
false,
AnyPropertyType,
&actualType,
&format,
&num,
&bytes,
(uint8_t**)&data);
int status = XFetchName(disp(), data[0], &name);
if (status >= Success && name != nullptr) {
std::string str(name);
title = str;
}
XFree(name);
}
pair<int, int> getCursorPos()
{
pair<int, int> pos(0, 0);
Display *dpy;
Window root;
Window ret_root;
Window ret_child;
int root_x;
int root_y;
int win_x;
int win_y;
unsigned int mask;
dpy = XOpenDisplay(NULL);
root = XDefaultRootWindow(dpy);
if(XQueryPointer(dpy, root, &ret_root, &ret_child, &root_x, &root_y,
&win_x, &win_y, &mask))
{
pos = pair<int, int> (root_x,root_y);
}
XCloseDisplay(dpy);
return pos;
}
bool isFullscreen()
{
if (!ewmhIsSupported()) {
return false;
}
Atom active = XInternAtom(disp(), "_NET_ACTIVE_WINDOW", true);
Atom actualType;
int format;
unsigned long num, bytes;
Window* data = 0;
Window rootWin = RootWindow(disp(), 0);
XGetWindowProperty(
disp(),
rootWin,
active,
0L,
~0L,
false,
AnyPropertyType,
&actualType,
&format,
&num,
&bytes,
(uint8_t**)&data);
XWindowAttributes window_attributes_return;
XWindowAttributes screen_attributes_return;
XGetWindowAttributes(disp(), rootWin, &screen_attributes_return);
XGetWindowAttributes(disp(), data[0], &window_attributes_return);
//menu bar is always 24 pixels in height
return (window_attributes_return.width >= screen_attributes_return.width &&
window_attributes_return.height + 24 >= screen_attributes_return.height) ? true : false;
}
//exe switch is not quite what is expected but it works for now
void GetProcessList(QStringList &processes)
{
processes.clear();
for (size_t i = 0; i < getTopLevelWindows().size(); ++i){
string s = GetWindowTitle(i);
if (s != "")
processes << QString::fromStdString(s);
}
}
bool isInFocus(const QString &exeToCheck)
{
string curWindow;
GetCurrentWindowTitle(curWindow);
return (QString::compare(
QString::fromStdString(curWindow),
exeToCheck,
Qt::CaseInsensitive) == 0) ? true : false;
}
int secondsSinceLastInput()
{
time_t idle_time;
static XScreenSaverInfo *mit_info;
Display *display;
int screen;
mit_info = XScreenSaverAllocInfo();
if((display=XOpenDisplay(NULL)) == NULL)
{
return(-1);
}
screen = DefaultScreen(display);
XScreenSaverQueryInfo(display, RootWindow(display,screen), mit_info);
idle_time = (mit_info->idle) / 1000;
XFree(mit_info);
XCloseDisplay(display);
return idle_time;
}
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <X11/extensions/scrnsaver.h>
#undef Bool
#undef CursorShape
#undef Expose
#undef KeyPress
#undef KeyRelease
#undef FocusIn
#undef FocusOut
#undef FontChange
#undef None
#undef Status
#undef Unsorted
#include <util/platform.h>
#include "advanced-scene-switcher.hpp"
using namespace std;
static Display* xdisplay = 0;
Display *disp()
{
if (!xdisplay)
xdisplay = XOpenDisplay(NULL);
return xdisplay;
}
void cleanupDisplay()
{
if (!xdisplay)
return;
XCloseDisplay(xdisplay);
xdisplay = 0;
}
static 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);
if (status == Success) {
if (num > 0) {
ewmh_window = ((Window*)data)[0];
}
if (data) {
XFree(data);
data = NULL;
}
}
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);
}
}
return ewmh_window != 0;
}
static std::vector<Window> getTopLevelWindows()
{
std::vector<Window> res;
res.resize(0);
if (!ewmhIsSupported()) {
return 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) {
continue;
}
for (unsigned long i = 0; i < num; ++i)
res.emplace_back(data[i]);
XFree(data);
}
return res;
}
static std::string GetWindowTitle(size_t i)
{
Window w = getTopLevelWindows().at(i);
std::string windowTitle;
char* name;
int status = XFetchName(disp(), w, &name);
if (status >= Success && name != nullptr)
{
std::string str(name);
windowTitle = str;
}
XFree(name);
return windowTitle;
}
void GetWindowList(vector<string> &windows)
{
windows.resize(0);
for (size_t i = 0; i < getTopLevelWindows().size(); ++i){
if (GetWindowTitle(i) != "")
windows.emplace_back(GetWindowTitle(i));
}
}
void GetCurrentWindowTitle(string &title)
{
if (!ewmhIsSupported()) {
return;
}
Atom active = XInternAtom(disp(), "_NET_ACTIVE_WINDOW", true);
Atom actualType;
int format;
unsigned long num, bytes;
Window* data = 0;
char* name;
Window rootWin = RootWindow(disp(), 0);
XGetWindowProperty(
disp(),
rootWin,
active,
0L,
~0L,
false,
AnyPropertyType,
&actualType,
&format,
&num,
&bytes,
(uint8_t**)&data);
int status = XFetchName(disp(), data[0], &name);
if (status >= Success && name != nullptr) {
std::string str(name);
title = str;
}
XFree(name);
}
pair<int, int> getCursorPos()
{
pair<int, int> pos(0, 0);
Display *dpy;
Window root;
Window ret_root;
Window ret_child;
int root_x;
int root_y;
int win_x;
int win_y;
unsigned int mask;
dpy = XOpenDisplay(NULL);
root = XDefaultRootWindow(dpy);
if(XQueryPointer(dpy, root, &ret_root, &ret_child, &root_x, &root_y,
&win_x, &win_y, &mask))
{
pos = pair<int, int> (root_x,root_y);
}
XCloseDisplay(dpy);
return pos;
}
bool isFullscreen()
{
if (!ewmhIsSupported()) {
return false;
}
Atom active = XInternAtom(disp(), "_NET_ACTIVE_WINDOW", true);
Atom actualType;
int format;
unsigned long num, bytes;
Window* data = 0;
Window rootWin = RootWindow(disp(), 0);
XGetWindowProperty(
disp(),
rootWin,
active,
0L,
~0L,
false,
AnyPropertyType,
&actualType,
&format,
&num,
&bytes,
(uint8_t**)&data);
XWindowAttributes window_attributes_return;
XWindowAttributes screen_attributes_return;
XGetWindowAttributes(disp(), rootWin, &screen_attributes_return);
XGetWindowAttributes(disp(), data[0], &window_attributes_return);
//menu bar is always 24 pixels in height
return (window_attributes_return.width >= screen_attributes_return.width &&
window_attributes_return.height + 24 >= screen_attributes_return.height) ? true : false;
}
//exe switch is not quite what is expected but it works for now
void GetProcessList(QStringList &processes)
{
processes.clear();
for (size_t i = 0; i < getTopLevelWindows().size(); ++i){
string s = GetWindowTitle(i);
if (s != "")
processes << QString::fromStdString(s);
}
}
bool isInFocus(const QString &exeToCheck)
{
string curWindow;
GetCurrentWindowTitle(curWindow);
return (QString::compare(
QString::fromStdString(curWindow),
exeToCheck,
Qt::CaseInsensitive) == 0) ? true : false;
}
int secondsSinceLastInput()
{
time_t idle_time;
static XScreenSaverInfo *mit_info;
Display *display;
int screen;
mit_info = XScreenSaverAllocInfo();
if((display=XOpenDisplay(NULL)) == NULL)
{
return(-1);
}
screen = DefaultScreen(display);
XScreenSaverQueryInfo(display, RootWindow(display,screen), mit_info);
idle_time = (mit_info->idle) / 1000;
XFree(mit_info);
XCloseDisplay(display);
return idle_time;
}

View File

@ -483,7 +483,7 @@ static void SaveSceneSwitcher(obs_data_t* save_data, bool saving, void*)
obs_data_set_int(obj, "interval", switcher->interval);
obs_data_set_string(obj, "non_matching_scene", nonMatchingSceneName.c_str());
obs_data_set_bool(obj, "switch_if_not_matching", switcher->switchIfNotMatching);
obs_data_set_bool(obj, "active", switcher->th.joinable());
obs_data_set_bool(obj, "active", !switcher->stop);
obs_data_set_array(obj, "switches", array);
obs_data_set_array(obj, "screenRegion", screenRegionArray);
@ -760,22 +760,20 @@ static void SaveSceneSwitcher(obs_data_t* save_data, bool saving, void*)
obs_data_set_default_string(obj, "priority4", DEFAULT_PRIORITY_4);
obs_data_set_default_string(obj, "priority5", DEFAULT_PRIORITY_5);
switcher->functionNamesByPriority.clear();
switcher->functionNamesByPriority.push_back(obs_data_get_string(obj, "priority0"));
switcher->functionNamesByPriority.push_back(obs_data_get_string(obj, "priority1"));
switcher->functionNamesByPriority.push_back(obs_data_get_string(obj, "priority2"));
switcher->functionNamesByPriority.push_back(obs_data_get_string(obj, "priority3"));
switcher->functionNamesByPriority.push_back(obs_data_get_string(obj, "priority4"));
switcher->functionNamesByPriority.push_back(obs_data_get_string(obj, "priority5"));
switcher->functionNamesByPriority[0] = (obs_data_get_string(obj, "priority0"));
switcher->functionNamesByPriority[1] = (obs_data_get_string(obj, "priority1"));
switcher->functionNamesByPriority[2] = (obs_data_get_string(obj, "priority2"));
switcher->functionNamesByPriority[3] = (obs_data_get_string(obj, "priority3"));
switcher->functionNamesByPriority[4] = (obs_data_get_string(obj, "priority4"));
switcher->functionNamesByPriority[5] = (obs_data_get_string(obj, "priority5"));
if (!switcher->prioFuncsValid())
{
switcher->functionNamesByPriority.clear();
switcher->functionNamesByPriority.push_back(DEFAULT_PRIORITY_0);
switcher->functionNamesByPriority.push_back(DEFAULT_PRIORITY_1);
switcher->functionNamesByPriority.push_back(DEFAULT_PRIORITY_2);
switcher->functionNamesByPriority.push_back(DEFAULT_PRIORITY_3);
switcher->functionNamesByPriority.push_back(DEFAULT_PRIORITY_4);
switcher->functionNamesByPriority.push_back(DEFAULT_PRIORITY_5);
switcher->functionNamesByPriority[0] = (DEFAULT_PRIORITY_0);
switcher->functionNamesByPriority[1] = (DEFAULT_PRIORITY_1);
switcher->functionNamesByPriority[2] = (DEFAULT_PRIORITY_2);
switcher->functionNamesByPriority[3] = (DEFAULT_PRIORITY_3);
switcher->functionNamesByPriority[4] = (DEFAULT_PRIORITY_4);
switcher->functionNamesByPriority[5] = (DEFAULT_PRIORITY_5);
}
obs_data_array_release(array);
@ -802,6 +800,8 @@ static void SaveSceneSwitcher(obs_data_t* save_data, bool saving, void*)
bool SwitcherData::sceneChangedDuringWait(){
bool r = false;
obs_source_t* currentSource = obs_frontend_get_current_scene();
if (!currentSource)
return true;
string curName = (obs_source_get_name(currentSource));
obs_source_release(currentSource);
if (!waitSceneName.empty() && curName != waitSceneName)
@ -850,18 +850,19 @@ void switchScene(OBSWeakSource scene, OBSWeakSource transition)
void SwitcherData::Thread()
{
//to avoid scene duplication when rapidly switching scene collection
this_thread::sleep_for(chrono::seconds(2));
while (true)
{
startLoop:
unique_lock<mutex> lock(m);
unique_lock<mutex> transitionLock(transitionMutex);
bool match = false;
OBSWeakSource scene;
OBSWeakSource transition;
chrono::milliseconds duration(interval);
switcher->Prune();
//sleep for a bit
cv.wait_for(lock, duration);
if (switcher->stop)
@ -878,7 +879,6 @@ void SwitcherData::Thread()
continue;
}
//do callbacks 'n stuff in future ...
for (string switchFuncName : functionNamesByPriority)
{
if (switchFuncName == READ_FILE_FUNC)
@ -905,7 +905,9 @@ void SwitcherData::Thread()
{
checkSceneRoundTrip(match, scene, transition, lock);
if (sceneChangedDuringWait()) //scene might have changed during the sleep
{
goto startLoop;
}
}
if (switcher->stop)
{
@ -917,7 +919,6 @@ void SwitcherData::Thread()
}
}
//switch if no match?
if (!match && switchIfNotMatching && nonMatchingScene)
{
match = true;
@ -950,11 +951,9 @@ void SwitcherData::Stop()
if (th.joinable())
{
switcher->stop = true;
switcher->stop = true;
cv.notify_one();
transitionCv.notify_one();
th.join();
cv.notify_one();
th.join();
}
}
@ -964,7 +963,6 @@ extern "C" void FreeSceneSwitcher()
switcher = nullptr;
}
//why is OBS_FRONTEND_EVENT_SCENE_CHANGED fired after OBS_FRONTEND_EVENT_TRANSITION_STOPPED?
static void OBSEvent(enum obs_frontend_event event, void* switcher)
{
switch (event){
@ -981,15 +979,9 @@ static void OBSEvent(enum obs_frontend_event event, void* switcher)
s->cv.notify_one();
break;
}
case OBS_FRONTEND_EVENT_TRANSITION_STOPPED:
{
default:
break;
}
case OBS_FRONTEND_EVENT_TRANSITION_CHANGED:
{
break;
}
}
}
void startStopHotkeyFunc(void* data, obs_hotkey_id id, obs_hotkey_t* hotkey, bool pressed);

View File

@ -156,7 +156,6 @@ struct SwitcherData
thread th;
condition_variable cv;
mutex m;
mutex transitionMutex;
bool transitionActive = false;
bool waitForTransition = false;
condition_variable transitionCv;