Compare commits

...

1 Commits

Author SHA1 Message Date
ReenigneArcher
42503ca4a8 fix(tray): run tray in main event loop enabling support for macOS
Co-Authored-By: Lukas Senionis <22381748+FrogTheFrog@users.noreply.github.com>
2025-04-30 14:32:06 -04:00
8 changed files with 66 additions and 44 deletions

View File

@@ -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"

View File

@@ -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)

View File

@@ -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?

View File

@@ -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<safe::event_t<bool>> &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<bool>(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()) {

View File

@@ -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<bool>(mail::shutdown);
auto broadcast_shutdown_event = mail::man->event<bool>(mail::broadcast_shutdown);

View File

@@ -59,6 +59,6 @@ namespace rtsp_stream {
*/
void terminate_sessions();
void rtpThread();
void start();
} // namespace rtsp_stream

View File

@@ -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() {

View File

@@ -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.