Browse Source

frontend-tools: Add automatic scene switcher for Linux

cg2121 8 years ago
parent
commit
d1de03c0be

+ 10 - 10
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

+ 211 - 0
UI/frontend-plugins/frontend-tools/auto-scene-switcher-nix.cpp

@@ -0,0 +1,211 @@
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.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 "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<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);
+}

+ 2 - 8
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();
 }