//go:build (windows || darwin) && cgo package desktop import ( "strings" "github.com/kermieisinthehouse/systray" "golang.org/x/text/cases" "golang.org/x/text/language" "github.com/stashapp/stash/internal/manager/config" "github.com/stashapp/stash/pkg/logger" ) // MUST be run on the main goroutine or will have no effect on macOS func startSystray(exit chan int, faviconProvider FaviconProvider) { // Shows a small notification to inform that Stash will no longer show a terminal window, // and instead will be available in the tray. Will only show the first time a pre-desktop integration // system is started from a non-terminal method, e.g. double-clicking an icon. c := config.GetInstance() if c.GetShowOneTimeMovedNotification() { SendNotification("Stash has moved!", "Stash now runs in your tray, instead of a terminal window.") c.SetBool(config.ShowOneTimeMovedNotification, false) if err := c.Write(); err != nil { logger.Errorf("Error while writing configuration file: %v", err) } } // Listen for changes to rerender systray // TODO: This is disabled for now. The systray package does not clean up all of its resources when Quit() is called. // TODO: This results in this only working once, or changes being ignored. Our fork of systray fixes a crash(!) on macOS here. // go func() { // for { // <-config.GetInstance().GetConfigUpdatesChannel() // systray.Quit() // } // }() // "intercept" an exit code to quit the systray, allowing the call to systray.Run() below to return. go func() { exitCode := <-exit systray.Quit() exit <- exitCode }() systray.Run(func() { systrayInitialize(exit, faviconProvider) }, nil) } func systrayInitialize(exit chan<- int, faviconProvider FaviconProvider) { favicon := faviconProvider.GetFavicon() systray.SetTemplateIcon(favicon, favicon) systray.SetTooltip("🟢 Stash is Running.") openStashButton := systray.AddMenuItem("Open Stash", "Open a browser window to Stash") var menuItems []string systray.AddSeparator() c := config.GetInstance() if !c.IsNewSystem() { menuItems = c.GetMenuItems() for _, item := range menuItems { c := cases.Title(language.Und) titleCaseItem := c.String(strings.ToLower(item)) curr := systray.AddMenuItem(titleCaseItem, "Open to "+titleCaseItem) go func(item string) { for { <-curr.ClickedCh if item == "markers" { item = "scenes/markers" } openURLInBrowser(item) } }(item) } systray.AddSeparator() // TODO - Some ideas for future expansions // systray.AddMenuItem("Start a Scan", "Scan all libraries with default settings") // systray.AddMenuItem("Start Auto Tagging", "Auto Tag all libraries") // systray.AddMenuItem("Check for updates", "Check for a new Stash release") // systray.AddSeparator() } quitStashButton := systray.AddMenuItem("Quit Stash Server", "Quits the Stash server") go func() { for { select { case <-openStashButton.ClickedCh: openURLInBrowser("") case <-quitStashButton.ClickedCh: exit <- 0 return } } }() }