diff --git a/UI/frontend-plugins/frontend-tools/CMakeLists.txt b/UI/frontend-plugins/frontend-tools/CMakeLists.txt index bbb9385bc..3ce774de3 100644 --- a/UI/frontend-plugins/frontend-tools/CMakeLists.txt +++ b/UI/frontend-plugins/frontend-tools/CMakeLists.txt @@ -5,16 +5,10 @@ if(APPLE) include_directories(${COCOA}) endif() -if(WIN32 OR APPLE) - set(frontend-tools_HEADERS - auto-scene-switcher.hpp - ) - set(frontend-tools_SOURCES - auto-scene-switcher.cpp - ) - set(frontend-tools_UI - forms/auto-scene-switcher.ui - ) +if(UNIX) + find_package(X11 REQUIRED) + link_libraries(${X11_LIBRARIES}) + include_directories(${X11_INCLUDE_DIR}) endif() configure_file( @@ -24,16 +18,19 @@ configure_file( set(frontend-tools_HEADERS ${frontend-tools_HEADERS} "${CMAKE_BINARY_DIR}/config/frontend-tools-config.h" + auto-scene-switcher.hpp output-timer.hpp tool-helpers.hpp ) set(frontend-tools_SOURCES ${frontend-tools_SOURCES} + auto-scene-switcher.cpp frontend-tools.c output-timer.cpp ) set(frontend-tools_UI ${frontend-tools_UI} + forms/auto-scene-switcher.ui forms/output-timer.ui ) @@ -64,6 +61,9 @@ elseif(APPLE) set(frontend-tools_PLATFORM_LIBS ${COCOA}) +else() + set(frontend-tools_PLATFORM_SOURCES + auto-scene-switcher-nix.cpp) endif() qt5_wrap_ui(frontend-tools_UI_HEADERS diff --git a/UI/frontend-plugins/frontend-tools/auto-scene-switcher-nix.cpp b/UI/frontend-plugins/frontend-tools/auto-scene-switcher-nix.cpp new file mode 100644 index 000000000..057519698 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/auto-scene-switcher-nix.cpp @@ -0,0 +1,211 @@ +#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 "auto-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); +} diff --git a/UI/frontend-plugins/frontend-tools/frontend-tools.c b/UI/frontend-plugins/frontend-tools/frontend-tools.c index a586b883e..81ee4b82c 100644 --- a/UI/frontend-plugins/frontend-tools/frontend-tools.c +++ b/UI/frontend-plugins/frontend-tools/frontend-tools.c @@ -4,10 +4,8 @@ OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("frontend-tools", "en-US") -#if defined(_WIN32) || defined(__APPLE__) void InitSceneSwitcher(); void FreeSceneSwitcher(); -#endif #if defined(_WIN32) && BUILD_CAPTIONS void InitCaptions(); @@ -19,23 +17,19 @@ void FreeOutputTimer(); bool obs_module_load(void) { -#if defined(_WIN32) || defined(__APPLE__) - InitSceneSwitcher(); -#endif #if defined(_WIN32) && BUILD_CAPTIONS InitCaptions(); #endif + InitSceneSwitcher(); InitOutputTimer(); return true; } void obs_module_unload(void) { -#if defined(_WIN32) || defined(__APPLE__) - FreeSceneSwitcher(); -#endif #if defined(_WIN32) && BUILD_CAPTIONS FreeCaptions(); #endif + FreeSceneSwitcher(); FreeOutputTimer(); }