From dd2d70bb9c481b06c4684ade3487f71683a30734 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Fri, 16 Feb 2018 20:42:58 +0100 Subject: [PATCH] Fix for crash on Scene Collection change functionNamesByPriority was cleared while in use when changing Scene Collection. --- advanced-scene-switcher-nix.cpp | 646 ++++++++++++++++---------------- advanced-scene-switcher.cpp | 54 ++- switcher-data-structs.hpp | 1 - 3 files changed, 346 insertions(+), 355 deletions(-) diff --git a/advanced-scene-switcher-nix.cpp b/advanced-scene-switcher-nix.cpp index 2fba35f5..cbfc231b 100644 --- a/advanced-scene-switcher-nix.cpp +++ b/advanced-scene-switcher-nix.cpp @@ -1,323 +1,323 @@ -#include -#include -#include -#include -#undef Bool -#undef CursorShape -#undef Expose -#undef KeyPress -#undef KeyRelease -#undef FocusIn -#undef FocusOut -#undef FontChange -#undef None -#undef Status -#undef Unsorted -#include -#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 getTopLevelWindows() -{ - std::vector 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 &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 getCursorPos() -{ - pair 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 (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 +#include +#include +#include +#undef Bool +#undef CursorShape +#undef Expose +#undef KeyPress +#undef KeyRelease +#undef FocusIn +#undef FocusOut +#undef FontChange +#undef None +#undef Status +#undef Unsorted +#include +#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 getTopLevelWindows() +{ + std::vector 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 &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 getCursorPos() +{ + pair 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 (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; +} + + diff --git a/advanced-scene-switcher.cpp b/advanced-scene-switcher.cpp index 884c3a8d..2dcc8d71 100644 --- a/advanced-scene-switcher.cpp +++ b/advanced-scene-switcher.cpp @@ -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 lock(m); - unique_lock 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); diff --git a/switcher-data-structs.hpp b/switcher-data-structs.hpp index 1d1b13f6..4ed1f037 100644 --- a/switcher-data-structs.hpp +++ b/switcher-data-structs.hpp @@ -156,7 +156,6 @@ struct SwitcherData thread th; condition_variable cv; mutex m; - mutex transitionMutex; bool transitionActive = false; bool waitForTransition = false; condition_variable transitionCv;