瀏覽代碼

Added library support (#480)

* Added library support

- Changed CMakeLists.txt to build a lib then an app instead of only
building an app.
- Added a friend class forward definition for the WallpaperApplication
  in order to be used by third party apps.
- Fixed a segfault (library runtime issue?) inside CPass destructor.

* Get rid of friend class + fixed issues with building

* Fixed path of wlr protocols using SOURCE_DIR instead of CURRENT_SOURCE_DIR

* Fixed redundant blank line
Estorc 7 月之前
父節點
當前提交
acf30b025d

+ 1 - 0
.gitignore

@@ -1,5 +1,6 @@
 cmake-build-*
 .idea
+.cache/
 build/
 .vscode/
 third_party/

+ 43 - 17
CMakeLists.txt

@@ -37,6 +37,7 @@ find_package(PulseAudio REQUIRED)
 
 set(ENABLE_OPT OFF)
 set(SPIRV_CROSS_FORCE_PIC ON)
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
 set(KISSFFT_TEST OFF)
 set(KISSFFT_TOOLS OFF)
 
@@ -156,13 +157,13 @@ if(WAYLAND_SUPPORT_FOUND)
 
     message(STATUS "Building protocols...")
     add_custom_command(OUTPUT ${WAYLAND_OUTPUT_DIR}/wlr-foreign-toplevel-management-unstable-v1-protocol.h
-        COMMAND ${WaylandScanner} client-header ${CMAKE_SOURCE_DIR}/protocols/wlr-foreign-toplevel-management-unstable-v1.xml ${WAYLAND_OUTPUT_DIR}/wlr-foreign-toplevel-management-unstable-v1-protocol.h)
+        COMMAND ${WaylandScanner} client-header ${CMAKE_CURRENT_SOURCE_DIR}/protocols/wlr-foreign-toplevel-management-unstable-v1.xml ${WAYLAND_OUTPUT_DIR}/wlr-foreign-toplevel-management-unstable-v1-protocol.h)
     add_custom_command(OUTPUT ${WAYLAND_OUTPUT_DIR}/wlr-foreign-toplevel-management-unstable-v1-protocol.c
-        COMMAND ${WaylandScanner} private-code ${CMAKE_SOURCE_DIR}/protocols/wlr-foreign-toplevel-management-unstable-v1.xml ${WAYLAND_OUTPUT_DIR}/wlr-foreign-toplevel-management-unstable-v1-protocol.c)
+        COMMAND ${WaylandScanner} private-code ${CMAKE_CURRENT_SOURCE_DIR}/protocols/wlr-foreign-toplevel-management-unstable-v1.xml ${WAYLAND_OUTPUT_DIR}/wlr-foreign-toplevel-management-unstable-v1-protocol.c)
     add_custom_command(OUTPUT ${WAYLAND_OUTPUT_DIR}/wlr-layer-shell-unstable-v1-protocol.h
-        COMMAND ${WaylandScanner} client-header ${CMAKE_SOURCE_DIR}/protocols/wlr-layer-shell-unstable-v1.xml ${WAYLAND_OUTPUT_DIR}/wlr-layer-shell-unstable-v1-protocol.h)
+        COMMAND ${WaylandScanner} client-header ${CMAKE_CURRENT_SOURCE_DIR}/protocols/wlr-layer-shell-unstable-v1.xml ${WAYLAND_OUTPUT_DIR}/wlr-layer-shell-unstable-v1-protocol.h)
     add_custom_command(OUTPUT ${WAYLAND_OUTPUT_DIR}/wlr-layer-shell-unstable-v1-protocol.c
-        COMMAND ${WaylandScanner} private-code ${CMAKE_SOURCE_DIR}/protocols/wlr-layer-shell-unstable-v1.xml ${WAYLAND_OUTPUT_DIR}/wlr-layer-shell-unstable-v1-protocol.c)
+        COMMAND ${WaylandScanner} private-code ${CMAKE_CURRENT_SOURCE_DIR}/protocols/wlr-layer-shell-unstable-v1.xml ${WAYLAND_OUTPUT_DIR}/wlr-layer-shell-unstable-v1-protocol.c)
     add_custom_command(OUTPUT ${WAYLAND_OUTPUT_DIR}/xdg-shell-protocol.h
         COMMAND ${WaylandScanner} client-header ${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml ${WAYLAND_OUTPUT_DIR}/xdg-shell-protocol.h)
     add_custom_command(OUTPUT ${WAYLAND_OUTPUT_DIR}/xdg-shell-protocol.c
@@ -485,16 +486,15 @@ if(BUILD_TESTING)
         src/WallpaperEngine/Testing/Cases/MouseCoordinates.cpp)
 endif()
 
-add_executable(
-    linux-wallpaperengine
-    src/main.cpp
+add_library(
+    linux-wallpaperengine-lib SHARED
 
     ${COMMON_SOURCES}
     ${WAYLAND_SOURCES}
     ${X11_SOURCES}
     ${DEMOMODE_SOURCES})
 
-target_link_libraries (linux-wallpaperengine PUBLIC
+target_link_libraries (linux-wallpaperengine-lib PUBLIC
     ${OPENGL_LIBRARIES}
     GLEW::GLEW
     ${GLUT_LIBRARIES}
@@ -539,8 +539,8 @@ if (BUILD_TESTING)
         argparse)
 endif()
 
-target_compile_definitions(linux-wallpaperengine PUBLIC ERRORONLY=${ERRORONLY})
-target_compile_definitions(linux-wallpaperengine PUBLIC DEMOMODE=${DEMOMODE})
+target_compile_definitions(linux-wallpaperengine-lib PUBLIC ERRORONLY=${ERRORONLY})
+target_compile_definitions(linux-wallpaperengine-lib PUBLIC DEMOMODE=${DEMOMODE})
 
 if (BUILD_TESTING)
     # tests should give as much output as possible
@@ -549,28 +549,54 @@ if (BUILD_TESTING)
 endif()
 
 if(X11_SUPPORT_FOUND)
-    target_compile_definitions(linux-wallpaperengine PUBLIC ENABLE_X11)
+    target_compile_definitions(linux-wallpaperengine-lib PUBLIC ENABLE_X11)
 
     # make sure some of the X11 functions we'll use are available
     check_function_exists(XSetIOErrorExitHandler HAVE_XSETIOERROREXITHANDLER)
 
     if(HAVE_XSETIOERROREXITHANDLER)
-        target_compile_definitions(linux-wallpaperengine PUBLIC HAVE_XSETIOERROREXITHANDLER=1)
+        target_compile_definitions(linux-wallpaperengine-lib PUBLIC HAVE_XSETIOERROREXITHANDLER=1)
     endif()
 endif()
 
 if(WAYLAND_SUPPORT_FOUND)
-    target_compile_definitions(linux-wallpaperengine PUBLIC ENABLE_WAYLAND)
+    target_compile_definitions(linux-wallpaperengine-lib PUBLIC ENABLE_WAYLAND)
 endif()
 
-COPY_FILES(linux-wallpaperengine "${CEF_BINARY_FILES}" "${CEF_BINARY_DIR}" "${TARGET_OUTPUT_DIRECTORY}")
-COPY_FILES(linux-wallpaperengine "${CEF_RESOURCE_FILES}" "${CEF_RESOURCE_DIR}" "${TARGET_OUTPUT_DIRECTORY}")
+target_include_directories(linux-wallpaperengine-lib PUBLIC
+    src
+    src/External/glslang-WallpaperEngine
+    src/External/SPIRV-Cross-WallpaperEngine
+    src/External/MimeTypes
+    src/External/json/include
+    src/External/stb
+    src/External/kissfft
+    ${MPV_INCLUDE_DIR}
+    ${GLEW_INCLUDE_DIR}
+    ${LZ4_INCLUDE_DIR}
+    ${SDL2_INCLUDE_DIRS}
+    ${FFMPEG_INCLUDE_DIR}
+    ${PULSEAUDIO_INCLUDE_DIR}
+    ${CEF_INCLUDE_PATH}
+    ${CMAKE_SOURCE_DIR}
+    ${X11_INCLUDES}
+    ${WAYLAND_OUTPUT_DIR})
+
+COPY_FILES(linux-wallpaperengine-lib "${CEF_BINARY_FILES}" "${CEF_BINARY_DIR}" "${TARGET_OUTPUT_DIRECTORY}")
+COPY_FILES(linux-wallpaperengine-lib "${CEF_RESOURCE_FILES}" "${CEF_RESOURCE_DIR}" "${TARGET_OUTPUT_DIRECTORY}")
 # remove the vulkan lib as chromium includes a broken libvulkan.so.1 with it
 file(REMOVE "${CEF_BINARY_DIR}/libvulkan.so.1")
+set_target_properties(linux-wallpaperengine-lib PROPERTIES
+    LIBRARY_OUTPUT_DIRECTORY ${TARGET_OUTPUT_DIRECTORY})
 
-add_dependencies(linux-wallpaperengine libcef_dll_wrapper glslang spirv-cross-core spirv-cross-glsl)
+add_dependencies(linux-wallpaperengine-lib libcef_dll_wrapper glslang spirv-cross-core spirv-cross-glsl)
 # custom version of cef's SET_EXECUTABLE_TARGET_PROPERTIES that fixes C files being compiled with the wrong parameters
-REPLACED_SET_EXECUTABLE_TARGET_PROPERTIES(linux-wallpaperengine)
+REPLACED_SET_EXECUTABLE_TARGET_PROPERTIES(linux-wallpaperengine-lib)
+
+add_executable(linux-wallpaperengine
+    src/main.cpp)
+
+target_link_libraries (linux-wallpaperengine PUBLIC linux-wallpaperengine-lib)
 
 # set some install parameters if not in debug mode
 install(DIRECTORY ${TARGET_OUTPUT_DIRECTORY}/ DESTINATION .)

+ 96 - 69
src/WallpaperEngine/Application/WallpaperApplication.cpp

@@ -719,15 +719,12 @@ void WallpaperApplication::setupOpenGLDebugging () {
 #endif
 }
 
-void WallpaperApplication::show () {
+void WallpaperApplication::setup () {
     this->setupOutput ();
     this->setupAudio ();
     this->prepareOutputs ();
     this->setupOpenGLDebugging ();
 
-    static time_t seconds;
-    static struct tm* timeinfo;
-
     if (this->m_context.settings.general.dumpStructure) {
 	auto prettyPrinter = Data::Dumpers::StringPrinter ();
 
@@ -750,66 +747,17 @@ void WallpaperApplication::show () {
     bool initialized = false;
     int frame = 0;
 #endif /* DEMOMODE */
+}
 
-    while (this->m_context.state.general.keepRunning) {
-	// update g_Daytime
-	time (&seconds);
-	timeinfo = localtime (&seconds);
-	g_Daytime = ((timeinfo->tm_hour * 60.0f) + timeinfo->tm_min) / (24.0f * 60.0f);
-
-	// keep track of the previous frame's time
-	g_TimeLast = g_Time;
-	// calculate the current time value
-	g_Time = m_videoDriver->getRenderTime ();
-	// update audio recorder
-	m_audioDriver->update ();
-	// update input information
-	m_videoDriver->getInputContext ().update ();
-	// process driver events
-	m_videoDriver->dispatchEventQueue ();
-
-	if (m_videoDriver->closeRequested ()) {
-	    sLog.out ("Stop requested by driver");
-	    this->m_context.state.general.keepRunning = false;
-	}
-
-#if DEMOMODE
-	// wait for a full render cycle before actually starting
-	// this gives some extra time for video and web decoders to set themselves up
-	// because of size changes
-	if (m_videoDriver->getFrameCounter () > (uint32_t)this->m_context.settings.render.maximumFPS) {
-	    if (!initialized) {
-		width = this->m_renderContext->getWallpapers ().begin ()->second->getWidth ();
-		height = this->m_renderContext->getWallpapers ().begin ()->second->getHeight ();
-		pixels.reserve (width * height * 3);
-		init_encoder ("output.webm", width, height);
-		initialized = true;
-	    }
-
-	    glBindFramebuffer (
-		GL_FRAMEBUFFER, this->m_renderContext->getWallpapers ().begin ()->second->getWallpaperFramebuffer ()
-	    );
-
-	    glPixelStorei (GL_PACK_ALIGNMENT, 1);
-	    glReadPixels (0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels.data ());
-	    write_video_frame (pixels.data ());
-	    frame++;
-
-	    // stop after the given framecount
-	    if (frame >= FRAME_COUNT) {
-		this->m_context.state.general.keepRunning = false;
-	    }
-	}
-#endif /* DEMOMODE */
-	// check for fullscreen windows and wait until there's none fullscreen
-	if (this->m_fullScreenDetector->anythingFullscreen () && this->m_context.state.general.keepRunning) {
-	    this->m_isPaused = true;
-	    this->m_pauseStart = std::chrono::steady_clock::now ();
+void WallpaperApplication::render () {
+    static time_t seconds;
+    static struct tm* timeinfo;
 
-	    m_renderContext->setPause (true);
-	    while (this->m_fullScreenDetector->anythingFullscreen () && this->m_context.state.general.keepRunning) {
+	if (this->m_isPaused) {
 		usleep (FULLSCREEN_CHECK_WAIT_TIME);
-	    }
+		if (this->m_fullScreenDetector->anythingFullscreen () && this->m_context.state.general.keepRunning) {
+			return;
+		}
 	    m_renderContext->setPause (false);
 
 	    // account for paused duration in playlist timers
@@ -824,29 +772,96 @@ void WallpaperApplication::show () {
 	    }
 
 	    this->m_isPaused = false;
+	} else {
+		// update g_Daytime
+		time (&seconds);
+		timeinfo = localtime (&seconds);
+		g_Daytime = static_cast<float>((timeinfo->tm_hour * 60) + timeinfo->tm_min) / (24.0f * 60.0f);
+
+		// keep track of the previous frame's time
+		g_TimeLast = g_Time;
+		// calculate the current time value
+		g_Time = m_videoDriver->getRenderTime ();
+		// update audio recorder
+		m_audioDriver->update ();
+		// update input information
+		m_videoDriver->getInputContext ().update ();
+		// process driver events
+		m_videoDriver->dispatchEventQueue ();
+
+		if (m_videoDriver->closeRequested ()) {
+			sLog.out ("Stop requested by driver");
+			this->m_context.state.general.keepRunning = false;
+		}
+
+#if DEMOMODE
+		// wait for a full render cycle before actually starting
+		// this gives some extra time for video and web decoders to set themselves up
+		// because of size changes
+		if (m_videoDriver->getFrameCounter () > (uint32_t)this->m_context.settings.render.maximumFPS) {
+			if (!initialized) {
+			width = this->m_renderContext->getWallpapers ().begin ()->second->getWidth ();
+			height = this->m_renderContext->getWallpapers ().begin ()->second->getHeight ();
+			pixels.reserve (width * height * 3);
+			init_encoder ("output.webm", width, height);
+			initialized = true;
+			}
+
+			glBindFramebuffer (
+			GL_FRAMEBUFFER, this->m_renderContext->getWallpapers ().begin ()->second->getWallpaperFramebuffer ()
+			);
+
+			glPixelStorei (GL_PACK_ALIGNMENT, 1);
+			glReadPixels (0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels.data ());
+			write_video_frame (pixels.data ());
+			frame++;
+
+			// stop after the given framecount
+			if (frame >= FRAME_COUNT) {
+			this->m_context.state.general.keepRunning = false;
+			}
+		}
+#endif /* DEMOMODE */
+		// check for fullscreen windows and wait until there's none fullscreen
+		if (this->m_fullScreenDetector->anythingFullscreen () && this->m_context.state.general.keepRunning) {
+			this->m_isPaused = true;
+			this->m_pauseStart = std::chrono::steady_clock::now ();
+
+			m_renderContext->setPause (true);
+			return;
+		}
 	}
 
 	this->updatePlaylists ();
 
 	if (!this->m_context.settings.screenshot.take || this->m_screenShotTaken == true) {
-	    continue;
+	    return;
 	}
 
 	if (this->m_videoDriver->getFrameCounter () < this->m_nextFrameScreenshot) {
-	    continue;
+	    return;
 	}
 
 	this->takeScreenshot (this->m_context.settings.screenshot.path);
 	this->m_screenShotTaken = true;
-    }
+}
 
-    sLog.out ("Stopping");
+void WallpaperApplication::cleanup () {
+	sLog.out ("Stopping");
 
-#if DEMOMODE
-    close_encoder ();
-#endif /* DEMOMODE */
+	#if DEMOMODE
+		close_encoder ();
+	#endif /* DEMOMODE */
 
-    SDL_Quit ();
+		SDL_Quit ();
+}
+
+void WallpaperApplication::show () {
+	setup();
+    while (this->m_context.state.general.keepRunning) {
+		render();
+    }
+    cleanup();
 }
 
 void WallpaperApplication::update (Render::Drivers::Output::OutputViewport* viewport) {
@@ -868,3 +883,15 @@ ApplicationContext& WallpaperApplication::getContext () const { return this->m_c
 const WallpaperEngine::Render::Drivers::Output::Output& WallpaperApplication::getOutput () const {
     return this->m_renderContext->getOutput ();
 }
+
+void WallpaperApplication::setDestinationFramebuffer (GLuint framebuffer) {
+	this->m_destinationFramebuffer = framebuffer;
+	// Update all wallpapers with the new destination framebuffer
+	for (const auto& [screen, wallpaper] : this->m_renderContext->getWallpapers ()) {
+		wallpaper->setDestinationFramebuffer (framebuffer);
+	};
+}
+
+GLuint WallpaperApplication::getDestinationFramebuffer () const { 
+	return this->m_destinationFramebuffer;
+}

+ 23 - 0
src/WallpaperEngine/Application/WallpaperApplication.h

@@ -22,6 +22,7 @@
 #include <set>
 
 namespace WallpaperEngine::Application {
+
 using namespace WallpaperEngine::Assets;
 using namespace WallpaperEngine::Data::Model;
 /**
@@ -31,6 +32,18 @@ class WallpaperApplication {
 public:
     explicit WallpaperApplication (ApplicationContext& context);
 
+    /**
+     * Prepares the application for rendering.
+     */
+    void setup();
+    /**
+     * Renders a frame of the application.
+     */
+    void render();
+    /**
+     * Cleans up all the resources used by the application.
+     */
+    static void cleanup();
     /**
      * Shows the application until it's closed
      */
@@ -57,6 +70,15 @@ public:
      * Gets the output
      */
     [[nodiscard]] const WallpaperEngine::Render::Drivers::Output::Output& getOutput () const;
+    /**
+     * Sets the destination framebuffer for rendering. If not called, the default framebuffer will be used.
+     */
+    void setDestinationFramebuffer (GLuint framebuffer);
+
+    /**
+     * Gets the currently set destination framebuffer for rendering. If not set, returns 0 (the default framebuffer).
+     */
+    [[nodiscard]] GLuint getDestinationFramebuffer () const;
 
 private:
     /**
@@ -152,5 +174,6 @@ private:
     bool m_screenShotTaken = false;
     uint32_t m_nextFrameScreenshot = 0;
     std::chrono::steady_clock::time_point m_pauseStart {};
+    GLuint m_destinationFramebuffer = 0;
 };
 } // namespace WallpaperEngine::Application

+ 12 - 7
src/WallpaperEngine/Render/Objects/Effects/CPass.cpp

@@ -47,17 +47,22 @@ CPass::CPass (
 
 CPass::~CPass () {
     // destroy shader programs
-    GLuint attachedShaders[2];
-    GLsizei attachedCount;
+    if (!glIsProgram(this->m_programID)) return; // program already invalid or deleted
 
-    // destroy shaders (we only attach 2 to each program)
-    glGetAttachedShaders (this->m_programID, 2, &attachedCount, attachedShaders);
+    GLint shaderCount = 0;
+    glGetProgramiv(this->m_programID, GL_ATTACHED_SHADERS, &shaderCount);
 
-    for (auto i = 0; i < attachedCount; i++) {
-	glDeleteShader (attachedShaders[i]);
+    if (shaderCount > 0) {
+        std::vector<GLuint> attachedShaders(shaderCount);
+        glGetAttachedShaders(this->m_programID, shaderCount, nullptr, attachedShaders.data());
+
+        for (GLuint s : attachedShaders) {
+            if (glIsShader(s)) glDeleteShader(s);
+        }
     }
 
-    glDeleteProgram (this->m_programID);
+    glDeleteProgram(this->m_programID);
+    this->m_programID = 0;
 }
 
 std::shared_ptr<const TextureProvider> CPass::resolveTexture (

+ 1 - 0
src/WallpaperEngine/Render/RenderContext.cpp

@@ -35,6 +35,7 @@ void RenderContext::render (Drivers::Output::OutputViewport* viewport) {
 }
 
 void RenderContext::setWallpaper (const std::string& display, std::shared_ptr<CWallpaper> wallpaper) {
+    wallpaper->setDestinationFramebuffer (this->m_app.getDestinationFramebuffer ());
     this->m_wallpapers.insert_or_assign (display, wallpaper);
 }