add tray icon (#1035)

This commit is contained in:
ReenigneArcher 2023-03-15 16:30:18 -04:00 committed by GitHub
parent 237f21573b
commit 014d693112
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 314 additions and 2 deletions

View File

@ -319,6 +319,7 @@ jobs:
build-essential \
gcc-10 \
g++-10 \
libappindicator3-dev \
libavdevice-dev \
libcap-dev \
libcurl4-openssl-dev \

4
.gitmodules vendored
View File

@ -46,3 +46,7 @@
path = third-party/nanors
url = https://github.com/sleepybishop/nanors.git
branch = master
[submodule "third-party/tray"]
path = third-party/tray
url = https://github.com/dmikushin/tray
branch = master

View File

@ -142,6 +142,9 @@ find_package(Boost COMPONENTS locale log filesystem program_options REQUIRED)
list(APPEND SUNSHINE_COMPILE_OPTIONS -Wall -Wno-missing-braces -Wno-maybe-uninitialized -Wno-sign-compare)
# enable system tray, we will disable this later if we cannot find the required package config on linux
set(SUNSHINE_TRAY 1)
if(WIN32)
enable_language(RC)
set(CMAKE_RC_COMPILER windres)
@ -168,6 +171,7 @@ if(WIN32)
src/platform/windows/display_vram.cpp
src/platform/windows/display_ram.cpp
src/platform/windows/audio.cpp
third-party/tray/tray_windows.c
third-party/ViGEmClient/src/ViGEmClient.cpp
third-party/ViGEmClient/include/ViGEm/Client.h
third-party/ViGEmClient/include/ViGEm/Common.h
@ -207,6 +211,7 @@ elseif(APPLE)
FIND_LIBRARY(APP_SERVICES_LIBRARY ApplicationServices )
FIND_LIBRARY(AV_FOUNDATION_LIBRARY AVFoundation )
FIND_LIBRARY(COCOA Cocoa REQUIRED ) # tray icon
FIND_LIBRARY(CORE_MEDIA_LIBRARY CoreMedia )
FIND_LIBRARY(CORE_VIDEO_LIBRARY CoreVideo )
FIND_LIBRARY(VIDEO_TOOLBOX_LIBRARY VideoToolbox )
@ -214,6 +219,7 @@ elseif(APPLE)
list(APPEND SUNSHINE_EXTERNAL_LIBRARIES
${APP_SERVICES_LIBRARY}
${AV_FOUNDATION_LIBRARY}
${COCOA}
${CORE_MEDIA_LIBRARY}
${CORE_VIDEO_LIBRARY}
${VIDEO_TOOLBOX_LIBRARY}
@ -240,6 +246,7 @@ elseif(APPLE)
src/platform/macos/publish.cpp
third-party/TPCircularBuffer/TPCircularBuffer.c
third-party/TPCircularBuffer/TPCircularBuffer.h
third-party/tray/tray_darwin.m
${APPLE_PLIST_FILE})
else()
add_compile_definitions(SUNSHINE_PLATFORM="linux")
@ -248,6 +255,7 @@ else()
option(SUNSHINE_ENABLE_X11 "Enable X11 grab if available" ON)
option(SUNSHINE_ENABLE_WAYLAND "Enable building wayland specific code" ON)
option(SUNSHINE_ENABLE_CUDA "Enable cuda specific code" ON)
option(SUNSHINE_ENABLE_TRAY "Enable tray icon" ON)
if(${SUNSHINE_ENABLE_X11})
find_package(X11)
@ -413,10 +421,27 @@ ${CMAKE_BINARY_DIR}/generated-src/${filename}.h")
src/platform/linux/wlgrab.cpp
src/platform/linux/wayland.cpp)
endif()
if(NOT ${X11_FOUND} AND NOT (${LIBDRM_FOUND} AND ${LIBCAP_FOUND}) AND NOT ${WAYLAND_FOUND} AND NOT ${})
if(NOT ${X11_FOUND} AND NOT (${LIBDRM_FOUND} AND ${LIBCAP_FOUND}) AND NOT ${WAYLAND_FOUND})
message(FATAL_ERROR "Couldn't find either x11, wayland, cuda or (libdrm and libcap)")
endif()
# tray icon
if(${SUNSHINE_ENABLE_TRAY})
pkg_check_modules(APPINDICATOR appindicator3-0.1)
if(NOT APPINDICATOR_FOUND)
message(WARNING "Couldn't find appindicator, disabling tray icon")
set(SUNSHINE_TRAY 0)
else()
include_directories(${APPINDICATOR_INCLUDE_DIRS})
link_directories(${APPINDICATOR_LIBRARY_DIRS})
list(APPEND PLATFORM_TARGET_FILES third-party/tray/tray_linux.c)
list(APPEND SUNSHINE_EXTERNAL_LIBRARIES ${APPINDICATOR_LIBRARIES})
endif()
else()
set(SUNSHINE_TRAY 0)
endif()
list(APPEND PLATFORM_TARGET_FILES
src/platform/linux/publish.cpp
src/platform/linux/vaapi.h
@ -466,6 +491,7 @@ set(SUNSHINE_TARGET_FILES
third-party/moonlight-common-c/src/Rtsp.h
third-party/moonlight-common-c/src/RtspParser.c
third-party/moonlight-common-c/src/Video.h
third-party/tray/tray.h
src/upnp.cpp
src/upnp.h
src/cbs.cpp
@ -499,6 +525,8 @@ set(SUNSHINE_TARGET_FILES
src/network.cpp
src/network.h
src/move_by_copy.h
src/system_tray.cpp
src/system_tray.h
src/task_pool.h
src/thread_pool.h
src/thread_safe.h
@ -511,6 +539,8 @@ set_source_files_properties(src/upnp.cpp PROPERTIES COMPILE_FLAGS -Wno-pedantic)
set_source_files_properties(third-party/nanors/rs.c
PROPERTIES COMPILE_FLAGS "-include deps/obl/autoshim.h -ftree-vectorize")
list(APPEND SUNSHINE_DEFINITIONS SUNSHINE_TRAY=${SUNSHINE_TRAY})
# Pre-compiled binaries
if(WIN32)
set(FFMPEG_PREPARED_BINARIES "${CMAKE_CURRENT_SOURCE_DIR}/third-party/ffmpeg-windows-x86_64")
@ -890,6 +920,18 @@ elseif(UNIX)
pulseaudio-libs >= 10.0")
# This should automatically figure out dependencies, doesn't work with the current config
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS OFF)
if(${SUNSHINE_TRAY} STREQUAL 1)
install(FILES "${CMAKE_SOURCE_DIR}/sunshine.svg"
DESTINATION "/usr/share/icons")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "\
${CPACK_DEBIAN_PACKAGE_DEPENDS}, \
libappindicator3-1")
set(CPACK_RPM_PACKAGE_REQUIRES "\
${CPACK_RPM_PACKAGE_REQUIRES}, \
libappindicator-gtk3 >= 12.10.0")
endif()
endif()
endif()

View File

@ -35,6 +35,7 @@ dnf -y install \
gcc-12.0.1* \
gcc-c++-12.0.1* \
git-2.39.2* \
libappindicator-gtk3-devel-12.10.0* \
libcap-devel-2.48* \
libcurl-devel-7.82.0* \
libdrm-devel-2.4.110* \

View File

@ -35,6 +35,7 @@ dnf -y install \
gcc-12.2.1* \
gcc-c++-12.2.1* \
git-2.39.2* \
libappindicator-gtk3-devel-12.10.1* \
libcap-devel-2.48* \
libcurl-devel-7.85.0* \
libdrm-devel-2.4.112* \

View File

@ -34,6 +34,7 @@ apt-get install -y --no-install-recommends \
gcc-10=10.3.0* \
g++-10=10.3.0* \
git=1:2.25.1* \
libappindicator3-dev=12.10.1* \
libavdevice-dev=7:4.2.* \
libboost-filesystem-dev=1.71.0* \
libboost-locale-dev=1.71.0* \

View File

@ -33,6 +33,7 @@ apt-get install -y --no-install-recommends \
build-essential=12.9* \
cmake=3.22.1* \
git=1:2.34.1* \
libappindicator3-dev=12.10.1* \
libavdevice-dev=7:4.4.* \
libboost-filesystem-dev=1.74.0* \
libboost-locale-dev=1.74.0* \

View File

@ -59,6 +59,7 @@ Install Requirements
gcc \
gcc-c++ \
intel-mediasdk-devel \ # x86_64 only
libappindicator-gtk3-devel \
libcap-devel \
libcurl-devel \
libdrm-devel \
@ -94,6 +95,7 @@ Install Requirements
build-essential \
cmake \
g++-10 \
libappindicator3-dev \
libavdevice-dev \
libboost-filesystem-dev \
libboost-locale-dev \
@ -142,6 +144,7 @@ Install Requirements
sudo apt update && sudo apt install \
build-essential \
cmake \
libappindicator3-dev \
libavdevice-dev \
libboost-filesystem-dev \
libboost-locale-dev \

View File

@ -75,6 +75,7 @@ Code
src/rtsp
src/stream
src/sync
src/system_tray
src/task_pool
src/thread_pool
src/thread_safe

View File

@ -0,0 +1,4 @@
system_tray
===========
.. doxygenfile:: system_tray.h

View File

@ -9,7 +9,7 @@ arch=('x86_64' 'aarch64')
url=@PROJECT_HOMEPAGE_URL@
license=('GPL3')
depends=('avahi' 'boost-libs' 'curl' 'libevdev' 'libmfx' 'libpulse' 'libva' 'libvdpau' 'libx11' 'libxcb' 'libxfixes' 'libxrandr' 'libxtst' 'numactl' 'openssl' 'opus' 'udev')
depends=('avahi' 'boost-libs' 'curl' 'libappindicator-gtk3' 'libevdev' 'libmfx' 'libpulse' 'libva' 'libvdpau' 'libx11' 'libxcb' 'libxfixes' 'libxrandr' 'libxtst' 'numactl' 'openssl' 'opus' 'udev')
makedepends=('boost' 'cmake' 'git' 'make' 'nodejs' 'npm')
optdepends=('cuda: NvFBC capture support'
'libcap'

View File

@ -25,6 +25,7 @@
#include "platform/common.h"
#include "process.h"
#include "rtsp.h"
#include "system_tray.h"
#include "thread_pool.h"
#include "upnp.h"
#include "version.h"
@ -301,6 +302,11 @@ int main(int argc, char *argv[]) {
BOOST_LOG(info) << PROJECT_NAME << " version: " << PROJECT_VER << std::endl;
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, shutdown_event]() {
@ -371,6 +377,11 @@ int main(int argc, char *argv[]) {
task_pool.stop();
task_pool.join();
// stop system tray
#if defined SUNSHINE_TRAY && SUNSHINE_TRAY >= 1
system_tray::end_tray();
#endif
return 0;
}

View File

@ -30,6 +30,13 @@ extern boost::log::sources::severity_logger<int> fatal;
// functions
int main(int argc, char *argv[]);
void log_flush();
void open_url(const std::string &url);
void tray_open_ui_cb(struct tray_menu *item);
void tray_donate_github_cb(struct tray_menu *item);
void tray_donate_mee6_cb(struct tray_menu *item);
void tray_donate_patreon_cb(struct tray_menu *item);
void tray_donate_paypal_cb(struct tray_menu *item);
void tray_quit_cb(struct tray_menu *item);
void print_help(const char *name);
std::string read_file(const char *path);
int write_file(const char *path, const std::string_view &contents);

184
src/system_tray.cpp Normal file
View File

@ -0,0 +1,184 @@
/**
* @file system_tray.cpp
*/
// macros
#if defined SUNSHINE_TRAY && SUNSHINE_TRAY >= 1
#if defined(_WIN32) || defined(_WIN64)
#define TRAY_ICON WEB_DIR "images/favicon.ico"
#include <windows.h>
#elif defined(__linux__) || defined(linux) || defined(__linux)
#define TRAY_ICON "sunshine"
#elif defined(__APPLE__) || defined(__MACH__)
#define TRAY_ICON WEB_DIR "images/logo-sunshine-16.png"
#include <dispatch/dispatch.h>
#endif
// standard includes
#include <csignal>
#include <string>
// lib includes
#include "tray/tray.h"
// local includes
#include "confighttp.h"
#include "main.h"
// local includes
//#include "platform/common.h"
//#include "process.h"
using namespace std::literals;
// system_tray namespace
namespace system_tray {
/**
* @brief Open a url in the default web browser.
* @param url The url to open.
*/
void open_url(const std::string &url) {
// if windows
#if defined(_WIN32) || defined(_WIN64)
ShellExecuteA(nullptr, "open", url.c_str(), nullptr, nullptr, SW_SHOWNORMAL);
#else // if unix
system(("open "s + url).c_str());
#endif
}
/**
* @brief Callback for opening the UI from the system tray.
* @param item The tray menu item.
*/
void tray_open_ui_cb(struct tray_menu *item) {
BOOST_LOG(info) << "Opening UI from system tray"sv;
// create the url with the port
std::string url = "https://localhost:" + std::to_string(map_port(confighttp::PORT_HTTPS));
// open the url in the default web browser
open_url(url);
}
/**
* @brief Callback for opening GitHub Sponsors from the system tray.
* @param item The tray menu item.
*/
void tray_donate_github_cb(struct tray_menu *item) {
open_url("https://github.com/sponsors/LizardByte");
}
/**
* @brief Callback for opening MEE6 donation from the system tray.
* @param item The tray menu item.
*/
void tray_donate_mee6_cb(struct tray_menu *item) {
open_url("https://mee6.xyz/m/804382334370578482");
}
/**
* @brief Callback for opening Patreon from the system tray.
* @param item The tray menu item.
*/
void tray_donate_patreon_cb(struct tray_menu *item) {
open_url("https://www.patreon.com/LizardByte");
}
/**
* @brief Callback for opening PayPal donation from the system tray.
* @param item The tray menu item.
*/
void tray_donate_paypal_cb(struct tray_menu *item) {
open_url("https://www.paypal.com/paypalme/ReenigneArcher");
}
/**
* @brief Callback for exiting Sunshine from the system tray.
* @param item The tray menu item.
*/
void tray_quit_cb(struct tray_menu *item) {
BOOST_LOG(info) << "Quiting from system tray"sv;
std::raise(SIGINT);
}
// Tray menu
static struct tray tray = {
.icon = TRAY_ICON,
#if defined(_WIN32) || defined(_WIN64)
.tooltip = const_cast<char *>("Sunshine"), // cast the string literal to a non-const char* pointer
#endif
.menu =
(struct tray_menu[]) {
// todo - use boost/locale to translate menu strings
{ .text = "Open Sunshine", .cb = tray_open_ui_cb },
{ .text = "-" },
{ .text = "Donate",
.submenu =
(struct tray_menu[]) {
{ .text = "GitHub Sponsors", .cb = tray_donate_github_cb },
{ .text = "MEE6", .cb = tray_donate_mee6_cb },
{ .text = "Patreon", .cb = tray_donate_patreon_cb },
{ .text = "PayPal", .cb = tray_donate_paypal_cb },
{ .text = nullptr } } },
{ .text = "-" },
{ .text = "Quit", .cb = tray_quit_cb },
{ .text = nullptr } },
};
/**
* @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.
*/
int system_tray() {
if(tray_init(&tray) < 0) {
BOOST_LOG(warning) << "Failed to create system tray"sv;
return 1;
}
else {
BOOST_LOG(info) << "System tray created"sv;
}
while(tray_loop(1) == 0) {
BOOST_LOG(debug) << "System tray loop"sv;
}
return 0;
}
/**
* @brief Run the system tray with platform specific options.
* @note macOS requires that UI elements be created on the main thread, so the system tray is not implemented for macOS.
*/
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
// dispatch_async(dispatch_get_main_queue(), ^{
// system_tray();
// });
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
}
/**
* @brief Exit the system tray.
* @return 0 after exiting the system tray.
*/
int end_tray() {
tray_exit();
return 0;
}
} // namespace system_tray
#endif

23
src/system_tray.h Normal file
View File

@ -0,0 +1,23 @@
/**
* @file system_tray.h
*/
// macros
#if defined SUNSHINE_TRAY && SUNSHINE_TRAY >= 1
// system_tray namespace
namespace system_tray {
void open_url(const std::string &url);
void tray_open_ui_cb(struct tray_menu *item);
void tray_donate_github_cb(struct tray_menu *item);
void tray_donate_mee6_cb(struct tray_menu *item);
void tray_donate_patreon_cb(struct tray_menu *item);
void tray_donate_paypal_cb(struct tray_menu *item);
void tray_quit_cb(struct tray_menu *item);
int system_tray();
int run_tray();
int end_tray();
} // namespace system_tray
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 777 B

27
sunshine.svg Normal file
View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Sunshine_Logo" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" width="256px" height="256px" viewBox="0 0 256 256" style="enable-background:new 0 0 256 256;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FDD107;}
.st1{fill:#F89A1C;}
.st2{fill:#EF3E23;}
.st3{fill:#F26222;}
</style>
<path class="st0" d="M118.7688675,20.7120476c0,0-63.8333359,26-74.3333359,83.8333282s37.1666718,91.5,86.3333282,75.3333282
s70.3333435-51,81.8333435-86.9999924c0,0-9.3333435,100.4999924-96.1666718,115.4999924s-118.1666641-50-82.1666641-119.8333282
C44.2688675,67.0453796,80.5188675,29.6287155,118.7688675,20.7120476z"/>
<path class="st1" d="M118.7688675,20.7120476c0,0-41.125,3.6666679-83.25,61.0416679s-28.125,139.125,34.25,149.375
s115.8749924-44.875,133.5-82.375s15.1666718-61.4583282,9.75-77.8749924c0,0,0.6666718,36.4166641-13.3333282,59.6666641
s-29.75,46.3333282-65.0833282,62.1666718s-74.1666718,13.75-95.4166718-19.25s-5.9166641-76.0833359-0.2916641-85.3333359
S72.3938675,33.7953796,118.7688675,20.7120476z"/>
<path class="st2" d="M73.0188675,39.6287155c0,0,38.125-28.125,76.8749924-28.125s63,28.25,68.5,52.25s6,54.125-11.5,87.6249924
s-37.375,56-79.1249924,76.125s-84.625,2.75-84.625,2.75s25.9769745,25.8750153,71.0509872,16.5
c45.0740051-9.375,82.2406769-40.875,98.4073486-69.5s28.7916565-57.3749924,27.6666565-92.2499924s-23.75-54.5-31.25-60.25
s-23.1875-17.8125-58.1875-16.5625S86.4563675,29.8162155,73.0188675,39.6287155z"/>
<path class="st3" d="M73.0188675,39.6287155c0,0,35-32.8125,82.4374924-32.8125s69.1875,24.8125,78.875,44.6875
s21.8125,70-12.1875,122.9999924s-74.625,67.375-93.625,71.625s-42.4311447,4.269165-59.1114044-1.2299957
c0,0,35.1947479,8.3966675,66.7780762-7.4366608s51.6666718-32.1666718,74.0833282-68.8333435
s25.9166718-72.7499924,22.1666718-93.9166565s-12.1666718-42.4166718-36.5-56.3333359s-56.7291718-10.531251-74.4791641-4.531251
S91.9876175,26.4099655,73.0188675,39.6287155z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

1
third-party/tray vendored Submodule

@ -0,0 +1 @@
Subproject commit 1c2fd286a513cc7315ca5b59ae9a8a140e795c31