diff --git a/cmake/compile_definitions/macos.cmake b/cmake/compile_definitions/macos.cmake index fb33d3bf..448ad65e 100644 --- a/cmake/compile_definitions/macos.cmake +++ b/cmake/compile_definitions/macos.cmake @@ -28,9 +28,6 @@ list(APPEND SUNSHINE_EXTERNAL_LIBRARIES set(APPLE_PLIST_FILE "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/assets/Info.plist") -# todo - tray is not working on macos -set(SUNSHINE_TRAY 0) - set(PLATFORM_TARGET_FILES "${CMAKE_SOURCE_DIR}/src/platform/macos/av_audio.h" "${CMAKE_SOURCE_DIR}/src/platform/macos/av_audio.m" diff --git a/cmake/prep/options.cmake b/cmake/prep/options.cmake index 0043b137..2a92b1c6 100644 --- a/cmake/prep/options.cmake +++ b/cmake/prep/options.cmake @@ -17,7 +17,7 @@ option(BUILD_WERROR "Enable -Werror flag." OFF) # if this option is set, the build will exit after configuring special package configuration files option(SUNSHINE_CONFIGURE_ONLY "Configure special files only, then exit." OFF) -option(SUNSHINE_ENABLE_TRAY "Enable system tray icon. This option will be ignored on macOS." ON) +option(SUNSHINE_ENABLE_TRAY "Enable system tray icon." ON) option(SUNSHINE_SYSTEM_WAYLAND_PROTOCOLS "Use system installation of wayland-protocols rather than the submodule." OFF) diff --git a/packaging/sunshine.rb b/packaging/sunshine.rb index a61a4295..a17718bb 100644 --- a/packaging/sunshine.rb +++ b/packaging/sunshine.rb @@ -246,7 +246,6 @@ index 5b3638d..aca9481 100644 end args << "-DCUDA_FAIL_ON_MISSING=OFF" if OS.linux? - args << "-DSUNSHINE_ENABLE_TRAY=OFF" if OS.mac? # Handle system tray on Linux if OS.linux? diff --git a/src/main.cpp b/src/main.cpp index b91dedce..aa2aba01 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -89,6 +89,43 @@ WINAPI BOOL ConsoleCtrlHandler(DWORD type) { } #endif +#if defined SUNSHINE_TRAY && SUNSHINE_TRAY >= 1 +constexpr bool tray_is_enabled = true; +#else +constexpr bool tray_is_enabled = false; +#endif + +void mainThreadLoop(const std::shared_ptr> &shutdown_event) { + bool run_loop = false; + + // Conditions that would require the main thread event loop + run_loop = tray_is_enabled; + + if (!run_loop) { + BOOST_LOG(info) << "No main thread features enabled, skipping event loop"sv; + return; + } + + // Main thread event loop + BOOST_LOG(info) << "Starting main loop"sv; + while (true) { + if (shutdown_event->peek()) { + BOOST_LOG(info) << "Shutdown event detected, breaking main loop"sv; + if (tray_is_enabled) { + system_tray::end_tray(); + } + break; + } + + if (tray_is_enabled) { + system_tray::process_tray_events(); + } + + // Sleep to avoid busy waiting + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } +} + int main(int argc, char *argv[]) { lifetime::argv = argv; @@ -243,11 +280,6 @@ int main(int argc, char *argv[]) { task_pool.start(1); -#if defined SUNSHINE_TRAY && SUNSHINE_TRAY >= 1 - // create tray thread and detach it - system_tray::run_tray(); -#endif - // Create signal handler after logging has been initialized auto shutdown_event = mail::man->event(mail::shutdown); on_signal(SIGINT, [&force_shutdown, &display_device_deinit_guard, shutdown_event]() { @@ -346,19 +378,22 @@ int main(int argc, char *argv[]) { } #endif - rtsp_stream::rtpThread(); + std::thread rtpThread {rtsp_stream::start}; + + if (tray_is_enabled) { + BOOST_LOG(info) << "Starting system tray"sv; + system_tray::init_tray(); + } + + mainThreadLoop(shutdown_event); httpThread.join(); configThread.join(); + rtpThread.join(); task_pool.stop(); task_pool.join(); - // stop system tray -#if defined SUNSHINE_TRAY && SUNSHINE_TRAY >= 1 - system_tray::end_tray(); -#endif - #ifdef WIN32 // Restore global NVIDIA control panel settings if (nvprefs_instance.owning_undo_file() && nvprefs_instance.load()) { diff --git a/src/rtsp.cpp b/src/rtsp.cpp index d6f6fbbb..19009eba 100644 --- a/src/rtsp.cpp +++ b/src/rtsp.cpp @@ -1088,7 +1088,7 @@ namespace rtsp_stream { respond(sock, session, &option, 200, "OK", req->sequenceNumber, {}); } - void rtpThread() { + void start() { auto shutdown_event = mail::man->event(mail::shutdown); auto broadcast_shutdown_event = mail::man->event(mail::broadcast_shutdown); diff --git a/src/rtsp.h b/src/rtsp.h index 7e715677..2326eb9e 100644 --- a/src/rtsp.h +++ b/src/rtsp.h @@ -59,6 +59,6 @@ namespace rtsp_stream { */ void terminate_sessions(); - void rtpThread(); + void start(); } // namespace rtsp_stream diff --git a/src/system_tray.cpp b/src/system_tray.cpp index 621a809c..cbd5c197 100644 --- a/src/system_tray.cpp +++ b/src/system_tray.cpp @@ -124,7 +124,7 @@ namespace system_tray { .allIconPaths = {TRAY_ICON, TRAY_ICON_LOCKED, TRAY_ICON_PLAYING, TRAY_ICON_PAUSING}, }; - int system_tray() { + int init_tray() { #ifdef _WIN32 // If we're running as SYSTEM, Explorer.exe will not have permission to open our thread handle // to monitor for thread termination. If Explorer fails to open our thread, our tray icon @@ -195,29 +195,21 @@ namespace system_tray { } tray_initialized = true; - while (tray_loop(1) == 0) { - BOOST_LOG(debug) << "System tray loop"sv; - } - return 0; } - void run_tray() { - // create the system tray - #if defined(__APPLE__) || defined(__MACH__) - // macOS requires that UI elements be created on the main thread - // creating tray using dispatch queue does not work, although the code doesn't actually throw any (visible) errors + int process_tray_events() { + if (!tray_initialized) { + return 1; + } - // dispatch_async(dispatch_get_main_queue(), ^{ - // system_tray(); - // }); + // Process one iteration of the tray loop with non-blocking mode (0) + if (const int result = tray_loop(0); result != 0) { + BOOST_LOG(warning) << "System tray loop failed"sv; + return result; + } - BOOST_LOG(info) << "system_tray() is not yet implemented for this platform."sv; - #else // Windows, Linux - // create tray in separate thread - std::thread tray_thread(system_tray); - tray_thread.detach(); - #endif + return 0; } int end_tray() { diff --git a/src/system_tray.h b/src/system_tray.h index 00a17f92..164e057c 100644 --- a/src/system_tray.h +++ b/src/system_tray.h @@ -51,17 +51,16 @@ namespace system_tray { void tray_quit_cb(struct tray_menu *item); /** - * @brief Create the system tray. - * @details This function has an endless loop, so it should be run in a separate thread. - * @return 1 if the system tray failed to create, otherwise 0 once the tray has been terminated. + * @brief Initializes the system tray without starting a loop. + * @return 0 if initialization was successful, non-zero otherwise. */ - int system_tray(); + int init_tray(); /** - * @brief Run the system tray with platform specific options. - * @todo macOS requires that UI elements be created on the main thread, so the system tray is not currently implemented for macOS. + * @brief Processes a single tray event iteration. + * @return 0 if processing was successful, non-zero otherwise. */ - int run_tray(); + int process_tray_events(); /** * @brief Exit the system tray.