Skip to content

Commit bf8decf

Browse files
fix(tray): run tray in main event loop enabling support for macOS
Co-Authored-By: Lukas Senionis <[email protected]>
1 parent e9bce25 commit bf8decf

File tree

8 files changed

+68
-46
lines changed

8 files changed

+68
-46
lines changed

cmake/compile_definitions/macos.cmake

-3
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,6 @@ list(APPEND SUNSHINE_EXTERNAL_LIBRARIES
2828

2929
set(APPLE_PLIST_FILE "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/assets/Info.plist")
3030

31-
# todo - tray is not working on macos
32-
set(SUNSHINE_TRAY 0)
33-
3431
set(PLATFORM_TARGET_FILES
3532
"${CMAKE_SOURCE_DIR}/src/platform/macos/av_audio.h"
3633
"${CMAKE_SOURCE_DIR}/src/platform/macos/av_audio.m"

cmake/prep/options.cmake

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ option(BUILD_WERROR "Enable -Werror flag." OFF)
1717
# if this option is set, the build will exit after configuring special package configuration files
1818
option(SUNSHINE_CONFIGURE_ONLY "Configure special files only, then exit." OFF)
1919

20-
option(SUNSHINE_ENABLE_TRAY "Enable system tray icon. This option will be ignored on macOS." ON)
20+
option(SUNSHINE_ENABLE_TRAY "Enable system tray icon." ON)
2121

2222
option(SUNSHINE_SYSTEM_WAYLAND_PROTOCOLS "Use system installation of wayland-protocols rather than the submodule." OFF)
2323

packaging/sunshine.rb

-1
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,6 @@ def install
246246
end
247247

248248
args << "-DCUDA_FAIL_ON_MISSING=OFF" if OS.linux?
249-
args << "-DSUNSHINE_ENABLE_TRAY=OFF" if OS.mac?
250249

251250
# Handle system tray on Linux
252251
if OS.linux?

src/main.cpp

+46-11
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,43 @@ WINAPI BOOL ConsoleCtrlHandler(DWORD type) {
8989
}
9090
#endif
9191

92+
#if defined SUNSHINE_TRAY && SUNSHINE_TRAY >= 1
93+
constexpr bool tray_is_enabled = true;
94+
#else
95+
constexpr bool tray_is_enabled = false;
96+
#endif
97+
98+
void mainThreadLoop(const std::shared_ptr<safe::event_t<bool>> &shutdown_event) {
99+
bool run_loop = false;
100+
101+
// Conditions that would require the main thread event loop
102+
run_loop = tray_is_enabled;
103+
104+
if (!run_loop) {
105+
BOOST_LOG(info) << "No main thread features enabled, skipping event loop"sv;
106+
return;
107+
}
108+
109+
// Main thread event loop
110+
BOOST_LOG(info) << "Starting main loop"sv;
111+
while (true) {
112+
if (shutdown_event->peek()) {
113+
BOOST_LOG(info) << "Shutdown event detected, breaking main loop"sv;
114+
if (tray_is_enabled) {
115+
system_tray::end_tray();
116+
}
117+
break;
118+
}
119+
120+
if (tray_is_enabled) {
121+
system_tray::process_tray_events();
122+
}
123+
124+
// Sleep to avoid busy waiting
125+
std::this_thread::sleep_for(std::chrono::milliseconds(50));
126+
}
127+
}
128+
92129
int main(int argc, char *argv[]) {
93130
lifetime::argv = argv;
94131

@@ -243,11 +280,6 @@ int main(int argc, char *argv[]) {
243280

244281
task_pool.start(1);
245282

246-
#if defined SUNSHINE_TRAY && SUNSHINE_TRAY >= 1
247-
// create tray thread and detach it
248-
system_tray::run_tray();
249-
#endif
250-
251283
// Create signal handler after logging has been initialized
252284
auto shutdown_event = mail::man->event<bool>(mail::shutdown);
253285
on_signal(SIGINT, [&force_shutdown, &display_device_deinit_guard, shutdown_event]() {
@@ -346,19 +378,22 @@ int main(int argc, char *argv[]) {
346378
}
347379
#endif
348380

349-
rtsp_stream::rtpThread();
381+
std::thread rtpThread {rtsp_stream::start};
382+
383+
if (tray_is_enabled) {
384+
BOOST_LOG(info) << "Starting system tray"sv;
385+
system_tray::init_tray();
386+
}
387+
388+
mainThreadLoop(shutdown_event);
350389

351390
httpThread.join();
352391
configThread.join();
392+
rtpThread.join();
353393

354394
task_pool.stop();
355395
task_pool.join();
356396

357-
// stop system tray
358-
#if defined SUNSHINE_TRAY && SUNSHINE_TRAY >= 1
359-
system_tray::end_tray();
360-
#endif
361-
362397
#ifdef WIN32
363398
// Restore global NVIDIA control panel settings
364399
if (nvprefs_instance.owning_undo_file() && nvprefs_instance.load()) {

src/rtsp.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1088,7 +1088,7 @@ namespace rtsp_stream {
10881088
respond(sock, session, &option, 200, "OK", req->sequenceNumber, {});
10891089
}
10901090

1091-
void rtpThread() {
1091+
void start() {
10921092
auto shutdown_event = mail::man->event<bool>(mail::shutdown);
10931093
auto broadcast_shutdown_event = mail::man->event<bool>(mail::broadcast_shutdown);
10941094

src/rtsp.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,6 @@ namespace rtsp_stream {
5959
*/
6060
void terminate_sessions();
6161

62-
void rtpThread();
62+
void start();
6363

6464
} // namespace rtsp_stream

src/system_tray.cpp

+13-21
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ namespace system_tray {
124124
.allIconPaths = {TRAY_ICON, TRAY_ICON_LOCKED, TRAY_ICON_PLAYING, TRAY_ICON_PAUSING},
125125
};
126126

127-
int system_tray() {
127+
int init_tray() {
128128
#ifdef _WIN32
129129
// If we're running as SYSTEM, Explorer.exe will not have permission to open our thread handle
130130
// to monitor for thread termination. If Explorer fails to open our thread, our tray icon
@@ -195,29 +195,21 @@ namespace system_tray {
195195
}
196196

197197
tray_initialized = true;
198-
while (tray_loop(1) == 0) {
199-
BOOST_LOG(debug) << "System tray loop"sv;
200-
}
201-
202198
return 0;
203199
}
204200

205-
void run_tray() {
206-
// create the system tray
207-
#if defined(__APPLE__) || defined(__MACH__)
208-
// macOS requires that UI elements be created on the main thread
209-
// creating tray using dispatch queue does not work, although the code doesn't actually throw any (visible) errors
210-
211-
// dispatch_async(dispatch_get_main_queue(), ^{
212-
// system_tray();
213-
// });
214-
215-
BOOST_LOG(info) << "system_tray() is not yet implemented for this platform."sv;
216-
#else // Windows, Linux
217-
// create tray in separate thread
218-
std::thread tray_thread(system_tray);
219-
tray_thread.detach();
220-
#endif
201+
int process_tray_events() {
202+
if (!tray_initialized) {
203+
return 1;
204+
}
205+
206+
// Process one iteration of the tray loop with non-blocking mode (0)
207+
if (const int result = tray_loop(0); result != 0) {
208+
BOOST_LOG(warning) << "System tray loop failed"sv;
209+
return result;
210+
}
211+
212+
return 0;
221213
}
222214

223215
int end_tray() {

src/system_tray.h

+6-7
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,16 @@ namespace system_tray {
5151
void tray_quit_cb(struct tray_menu *item);
5252

5353
/**
54-
* @brief Create the system tray.
55-
* @details This function has an endless loop, so it should be run in a separate thread.
56-
* @return 1 if the system tray failed to create, otherwise 0 once the tray has been terminated.
54+
* @brief Initializes the system tray without starting a loop.
55+
* @return 0 if initialization was successful, non-zero otherwise.
5756
*/
58-
int system_tray();
57+
int init_tray();
5958

6059
/**
61-
* @brief Run the system tray with platform specific options.
62-
* @todo macOS requires that UI elements be created on the main thread, so the system tray is not currently implemented for macOS.
60+
* @brief Processes a single tray event iteration.
61+
* @return 0 if processing was successful, non-zero otherwise.
6362
*/
64-
int run_tray();
63+
int process_tray_events();
6564

6665
/**
6766
* @brief Exit the system tray.

0 commit comments

Comments
 (0)