diff --git a/app/app.go b/app/app.go
index 50f241db07..69cf2130d8 100644
--- a/app/app.go
+++ b/app/app.go
@@ -130,3 +130,9 @@ func newAppWithDriver(d fyne.Driver, id string) fyne.App {
return newApp
}
+
+// marker interface to pass system tray to supporting drivers
+type systrayDriver interface {
+ SetSystemTrayMenu(*fyne.Menu)
+ SetSystemTrayIcon(resource fyne.Resource)
+}
diff --git a/app/app_darwin.go b/app/app_darwin.go
index 61f44452c2..b8f3ab3105 100644
--- a/app/app_darwin.go
+++ b/app/app_darwin.go
@@ -37,6 +37,18 @@ func (a *fyneApp) SendNotification(n *fyne.Notification) {
fallbackNotification(n.Title, n.Content)
}
+// SetSystemTrayMenu creates a system tray item and attaches the specified menu.
+// By default this will use the application icon.
+func (a *fyneApp) SetSystemTrayMenu(menu *fyne.Menu) {
+ a.Driver().(systrayDriver).SetSystemTrayMenu(menu)
+}
+
+// SetSystemTrayIcon sets a custom image for the system tray icon.
+// You should have previously called `SetSystemTrayMenu` to initialise the menu icon.
+func (a *fyneApp) SetSystemTrayIcon(icon fyne.Resource) {
+ a.Driver().(systrayDriver).SetSystemTrayIcon(icon)
+}
+
func escapeNotificationString(in string) string {
noSlash := strings.ReplaceAll(in, "\\", "\\\\")
return strings.ReplaceAll(noSlash, "\"", "\\\"")
diff --git a/app/app_windows.go b/app/app_windows.go
index 202f371412..3e4a09b1f5 100644
--- a/app/app_windows.go
+++ b/app/app_windows.go
@@ -83,6 +83,18 @@ func (a *fyneApp) SendNotification(n *fyne.Notification) {
go runScript("notify", script)
}
+// SetSystemTrayMenu creates a system tray item and attaches the specified menu.
+// By default this will use the application icon.
+func (a *fyneApp) SetSystemTrayMenu(menu *fyne.Menu) {
+ a.Driver().(systrayDriver).SetSystemTrayMenu(menu)
+}
+
+// SetSystemTrayIcon sets a custom image for the system tray icon.
+// You should have previously called `SetSystemTrayMenu` to initialise the menu icon.
+func (a *fyneApp) SetSystemTrayIcon(icon fyne.Resource) {
+ a.Driver().(systrayDriver).SetSystemTrayIcon(icon)
+}
+
func escapeNotificationString(in string) string {
noSlash := strings.ReplaceAll(in, "`", "``")
return strings.ReplaceAll(noSlash, "\"", "`\"")
diff --git a/app/app_xdg.go b/app/app_xdg.go
index a7b6ee815c..66daf57bd4 100644
--- a/app/app_xdg.go
+++ b/app/app_xdg.go
@@ -48,6 +48,18 @@ func (a *fyneApp) SendNotification(n *fyne.Notification) {
}
}
+// SetSystemTrayMenu creates a system tray item and attaches the specified menu.
+// By default this will use the application icon.
+func (a *fyneApp) SetSystemTrayMenu(menu *fyne.Menu) {
+ a.Driver().(systrayDriver).SetSystemTrayMenu(menu)
+}
+
+// SetSystemTrayIcon sets a custom image for the system tray icon.
+// You should have previously called `SetSystemTrayMenu` to initialise the menu icon.
+func (a *fyneApp) SetSystemTrayIcon(icon fyne.Resource) {
+ a.Driver().(systrayDriver).SetSystemTrayIcon(icon)
+}
+
func rootConfigDir() string {
desktopConfig, _ := os.UserConfigDir()
return filepath.Join(desktopConfig, "fyne")
diff --git a/cmd/fyne_demo/main.go b/cmd/fyne_demo/main.go
index f3dfb10c4d..ae1744a79b 100644
--- a/cmd/fyne_demo/main.go
+++ b/cmd/fyne_demo/main.go
@@ -23,6 +23,7 @@ var topWindow fyne.Window
func main() {
a := app.NewWithID("io.fyne.demo")
a.SetIcon(theme.FyneLogo())
+ makeTray(a)
logLifecycle(a)
w := a.NewWindow("Fyne Demo")
topWindow = w
@@ -161,6 +162,16 @@ func makeMenu(a fyne.App, w fyne.Window) *fyne.MainMenu {
)
}
+func makeTray(a fyne.App) {
+ if desk, ok := a.(desktop.App); ok {
+ menu := fyne.NewMenu("Hello World",
+ fyne.NewMenuItem("Hello", func() {
+ log.Println("System tray menu tapped")
+ }))
+ desk.SetSystemTrayMenu(menu)
+ }
+}
+
func unsupportedTutorial(t tutorials.Tutorial) bool {
return !t.SupportWeb && fyne.CurrentDevice().IsBrowser()
}
diff --git a/driver/desktop/app.go b/driver/desktop/app.go
new file mode 100644
index 0000000000..e4edcec137
--- /dev/null
+++ b/driver/desktop/app.go
@@ -0,0 +1,11 @@
+package desktop
+
+import "fyne.io/fyne/v2"
+
+// App defines the desktop specific extensions to a fyne.App.
+//
+// Since: 2.2
+type App interface {
+ SetSystemTrayMenu(menu *fyne.Menu)
+ SetSystemTrayIcon(icon fyne.Resource)
+}
diff --git a/go.mod b/go.mod
index 3f8bcaafaa..18a1e64b42 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@ module fyne.io/fyne/v2
go 1.14
require (
+ fyne.io/systray v1.1.1-0.20220307102710-0121a6d9ce01
github.com/BurntSushi/toml v1.0.0
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9
github.com/akavel/rsrc v0.8.0 // indirect
diff --git a/go.sum b/go.sum
index c9fddf39ed..750f82ab3a 100644
--- a/go.sum
+++ b/go.sum
@@ -37,6 +37,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+fyne.io/systray v1.1.1-0.20220307102710-0121a6d9ce01 h1:kKqpTktxesuGT4+Dv1aYiVse688saMLeWPDRihIw+zI=
+fyne.io/systray v1.1.1-0.20220307102710-0121a6d9ce01/go.mod h1:N4ZU0i34X+n8soFRlBNkmJTunw9wD+9jIP19fSZpjSI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU=
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
diff --git a/internal/driver/glfw/driver.go b/internal/driver/glfw/driver.go
index c06c89e617..2095820371 100644
--- a/internal/driver/glfw/driver.go
+++ b/internal/driver/glfw/driver.go
@@ -3,12 +3,16 @@
package glfw
import (
+ "bytes"
+ "image"
"os"
"runtime"
"strconv"
"strings"
"sync"
+ ico "github.com/Kodeworks/golang-image-ico"
+
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/internal/animation"
intapp "fyne.io/fyne/v2/internal/app"
@@ -38,7 +42,26 @@ type gLDriver struct {
animation *animation.Runner
- drawOnMainThread bool // A workaround on Apple M1, just use 1 thread until fixed upstream
+ drawOnMainThread bool // A workaround on Apple M1, just use 1 thread until fixed upstream
+ trayStart, trayStop func() // shut down the system tray, if used
+}
+
+func toOSIcon(icon fyne.Resource) ([]byte, error) {
+ if runtime.GOOS != "windows" {
+ return icon.Content(), nil
+ }
+
+ img, _, err := image.Decode(bytes.NewReader(icon.Content()))
+ if err != nil {
+ return nil, err
+ }
+
+ buf := &bytes.Buffer{}
+ err = ico.Encode(buf, img)
+ if err != nil {
+ return nil, err
+ }
+ return buf.Bytes(), nil
}
func (d *gLDriver) RenderedTextSize(text string, textSize float32, style fyne.TextStyle) (size fyne.Size, baseline float32) {
@@ -70,6 +93,9 @@ func (d *gLDriver) Device() fyne.Device {
func (d *gLDriver) Quit() {
if curWindow != nil {
curWindow = nil
+ if d.trayStop != nil {
+ d.trayStop()
+ }
fyne.CurrentApp().Lifecycle().(*intapp.Lifecycle).TriggerExitedForeground()
}
defer func() {
diff --git a/internal/driver/glfw/driver_desktop.go b/internal/driver/glfw/driver_desktop.go
index 9a23696037..364b056972 100644
--- a/internal/driver/glfw/driver_desktop.go
+++ b/internal/driver/glfw/driver_desktop.go
@@ -1,11 +1,72 @@
-//go:build !js
-// +build !js
+//go:build !js && !wasm && !test_web_driver
+// +build !js,!wasm,!test_web_driver
package glfw
+import (
+ "fyne.io/systray"
+
+ "fyne.io/fyne/v2"
+ "fyne.io/fyne/v2/theme"
+)
+
func (d *gLDriver) Run() {
if goroutineID() != mainGoroutineID {
panic("Run() or ShowAndRun() must be called from main goroutine")
}
d.runGL()
}
+
+func (d *gLDriver) SetSystemTrayMenu(m *fyne.Menu) {
+ d.trayStart, d.trayStop = systray.RunWithExternalLoop(func() {
+ if fyne.CurrentApp().Icon() != nil {
+ img, err := toOSIcon(fyne.CurrentApp().Icon())
+ if err == nil {
+ systray.SetIcon(img)
+ }
+ } else {
+ img, err := toOSIcon(theme.FyneLogo())
+ if err == nil {
+ systray.SetIcon(img)
+ }
+ }
+
+ for _, i := range m.Items {
+ if i.IsSeparator {
+ systray.AddSeparator()
+ continue
+ }
+
+ var item *systray.MenuItem
+ fn := i.Action
+
+ if i.Checked {
+ item = systray.AddMenuItemCheckbox(i.Label, i.Label, true)
+ } else {
+ item = systray.AddMenuItem(i.Label, i.Label)
+ }
+ if i.Disabled {
+ item.Disable()
+ }
+
+ go func() {
+ for range item.ClickedCh {
+ fn()
+ }
+ }()
+ }
+
+ systray.AddSeparator()
+ quit := systray.AddMenuItem("Quit", "Quit application")
+ go func() {
+ <-quit.ClickedCh
+ d.Quit()
+ }()
+ }, func() {
+ // anything required for tear-down
+ })
+}
+
+func (d *gLDriver) SetSystemTrayIcon(resource fyne.Resource) {
+ systray.SetIcon(resource.Content())
+}
diff --git a/internal/driver/glfw/driver_mobile.go b/internal/driver/glfw/driver_mobile.go
new file mode 100644
index 0000000000..d8f56780a0
--- /dev/null
+++ b/internal/driver/glfw/driver_mobile.go
@@ -0,0 +1,10 @@
+//go:build js || wasm || test_web_driver
+// +build js wasm test_web_driver
+
+package glfw
+
+import "fyne.io/fyne/v2"
+
+func (d *gLDriver) SetSystemTrayMenu(m *fyne.Menu) {
+ // no-op for mobile apps using this driver
+}
diff --git a/internal/driver/glfw/loop.go b/internal/driver/glfw/loop.go
index 0382cb37d7..78f6164a86 100644
--- a/internal/driver/glfw/loop.go
+++ b/internal/driver/glfw/loop.go
@@ -112,6 +112,9 @@ func (d *gLDriver) runGL() {
run.cond.Broadcast()
d.initGLFW()
+ if d.trayStart != nil {
+ d.trayStart()
+ }
fyne.CurrentApp().Lifecycle().(*app.Lifecycle).TriggerStarted()
for {
select {
diff --git a/vendor/fyne.io/systray/.gitignore b/vendor/fyne.io/systray/.gitignore
new file mode 100644
index 0000000000..2e85ef697e
--- /dev/null
+++ b/vendor/fyne.io/systray/.gitignore
@@ -0,0 +1,12 @@
+example/example
+webview_example/webview_example
+*~
+*.swp
+**/*.exe
+Release
+Debug
+*.sdf
+dll/systray_unsigned.dll
+out.txt
+.vs
+on_exit*.txt
diff --git a/vendor/fyne.io/systray/CHANGELOG.md b/vendor/fyne.io/systray/CHANGELOG.md
new file mode 100644
index 0000000000..58e7fc8b33
--- /dev/null
+++ b/vendor/fyne.io/systray/CHANGELOG.md
@@ -0,0 +1,125 @@
+# Changelog
+
+## [v1.1.0](https://github.com/getlantern/systray/tree/v1.1.0) (2020-11-18)
+
+[Full Changelog](https://github.com/getlantern/systray/compare/v1.0.5...v1.1.0)
+
+**Merged pull requests:**
+
+- Add submenu support for Linux [\#183](https://github.com/getlantern/systray/pull/183) ([fbrinker](https://github.com/fbrinker))
+- Add checkbox support for Linux [\#181](https://github.com/getlantern/systray/pull/181) ([fbrinker](https://github.com/fbrinker))
+- fix SetTitle documentation [\#179](https://github.com/getlantern/systray/pull/179) ([delthas](https://github.com/delthas))
+
+## [v1.0.5](https://github.com/getlantern/systray/tree/v1.0.5) (2020-10-19)
+
+[Full Changelog](https://github.com/getlantern/systray/compare/v1.0.4...v1.0.5)
+
+**Merged pull requests:**
+
+- start menu ID with positive, and change the type to uint32 [\#173](https://github.com/getlantern/systray/pull/173) ([joesis](https://github.com/joesis))
+- Allows disabling items in submenu on macOS [\#172](https://github.com/getlantern/systray/pull/172) ([joesis](https://github.com/joesis))
+- Does not use the template icon for regular icons [\#171](https://github.com/getlantern/systray/pull/171) ([sithembiso](https://github.com/sithembiso))
+
+## [v1.0.4](https://github.com/getlantern/systray/tree/v1.0.4) (2020-07-21)
+
+[Full Changelog](https://github.com/getlantern/systray/compare/1.0.3...v1.0.4)
+
+**Merged pull requests:**
+
+- protect shared data structures with proper mutexes [\#162](https://github.com/getlantern/systray/pull/162) ([joesis](https://github.com/joesis))
+
+## [1.0.3](https://github.com/getlantern/systray/tree/1.0.3) (2020-06-11)
+
+[Full Changelog](https://github.com/getlantern/systray/compare/v1.0.3...1.0.3)
+
+## [v1.0.3](https://github.com/getlantern/systray/tree/v1.0.3) (2020-06-11)
+
+[Full Changelog](https://github.com/getlantern/systray/compare/v1.0.2...v1.0.3)
+
+**Merged pull requests:**
+
+- have a workaround to avoid crash on old macOS versions [\#153](https://github.com/getlantern/systray/pull/153) ([joesis](https://github.com/joesis))
+- Fix bug on darwin of setting icon for menu [\#147](https://github.com/getlantern/systray/pull/147) ([mangalaman93](https://github.com/mangalaman93))
+
+## [v1.0.2](https://github.com/getlantern/systray/tree/v1.0.2) (2020-05-19)
+
+[Full Changelog](https://github.com/getlantern/systray/compare/v1.0.1...v1.0.2)
+
+**Merged pull requests:**
+
+- remove unused dependencies [\#145](https://github.com/getlantern/systray/pull/145) ([joesis](https://github.com/joesis))
+
+## [v1.0.1](https://github.com/getlantern/systray/tree/v1.0.1) (2020-05-18)
+
+[Full Changelog](https://github.com/getlantern/systray/compare/1.0.1...v1.0.1)
+
+## [1.0.1](https://github.com/getlantern/systray/tree/1.0.1) (2020-05-18)
+
+[Full Changelog](https://github.com/getlantern/systray/compare/1.0.0...1.0.1)
+
+**Merged pull requests:**
+
+- Unlock menuItemsLock before changing UI [\#144](https://github.com/getlantern/systray/pull/144) ([joesis](https://github.com/joesis))
+
+## [1.0.0](https://github.com/getlantern/systray/tree/1.0.0) (2020-05-18)
+
+[Full Changelog](https://github.com/getlantern/systray/compare/v1.0.0...1.0.0)
+
+## [v1.0.0](https://github.com/getlantern/systray/tree/v1.0.0) (2020-05-18)
+
+[Full Changelog](https://github.com/getlantern/systray/compare/0.9.0...v1.0.0)
+
+**Merged pull requests:**
+
+- Check if the menu item is nil [\#137](https://github.com/getlantern/systray/pull/137) ([myleshorton](https://github.com/myleshorton))
+
+## [0.9.0](https://github.com/getlantern/systray/tree/0.9.0) (2020-03-24)
+
+[Full Changelog](https://github.com/getlantern/systray/compare/v0.9.0...0.9.0)
+
+## [v0.9.0](https://github.com/getlantern/systray/tree/v0.9.0) (2020-03-24)
+
+[Full Changelog](https://github.com/getlantern/systray/compare/8e63b37ef27d94f6db79c4ffb941608e8f0dc2f9...v0.9.0)
+
+**Merged pull requests:**
+
+- Backport all features and fixes from master [\#140](https://github.com/getlantern/systray/pull/140) ([joesis](https://github.com/joesis))
+- Nested menu windows [\#132](https://github.com/getlantern/systray/pull/132) ([joesis](https://github.com/joesis))
+- Support for nested sub-menus on OS X [\#131](https://github.com/getlantern/systray/pull/131) ([oxtoacart](https://github.com/oxtoacart))
+- Use temp directory for walk resource manager [\#129](https://github.com/getlantern/systray/pull/129) ([max-b](https://github.com/max-b))
+- Added support for template icons on macOS [\#119](https://github.com/getlantern/systray/pull/119) ([oxtoacart](https://github.com/oxtoacart))
+- When launching app window on macOS, make application a foreground app… [\#118](https://github.com/getlantern/systray/pull/118) ([oxtoacart](https://github.com/oxtoacart))
+- Include stdlib.h in systray\_browser\_linux to explicitly declare funct… [\#114](https://github.com/getlantern/systray/pull/114) ([oxtoacart](https://github.com/oxtoacart))
+- Fix panic when resources root path is not the working directory [\#112](https://github.com/getlantern/systray/pull/112) ([ksubileau](https://github.com/ksubileau))
+- Don't print close reason to console [\#111](https://github.com/getlantern/systray/pull/111) ([ksubileau](https://github.com/ksubileau))
+- Systray icon could not be changed dynamically [\#110](https://github.com/getlantern/systray/pull/110) ([ksubileau](https://github.com/ksubileau))
+- Preventing deadlock on menu item ClickeCh when no one is listening, c… [\#105](https://github.com/getlantern/systray/pull/105) ([oxtoacart](https://github.com/oxtoacart))
+- Reverted deadlock fix \(Affected other receivers\) [\#104](https://github.com/getlantern/systray/pull/104) ([ldstein](https://github.com/ldstein))
+- Fix Deadlock and item ordering in Windows [\#103](https://github.com/getlantern/systray/pull/103) ([ldstein](https://github.com/ldstein))
+- Minor README improvements \(go modules, example app, screenshot\) [\#98](https://github.com/getlantern/systray/pull/98) ([tstromberg](https://github.com/tstromberg))
+- Add support for app window [\#97](https://github.com/getlantern/systray/pull/97) ([oxtoacart](https://github.com/oxtoacart))
+- systray\_darwin.m: Compare Mac OS min version with value instead of macro [\#94](https://github.com/getlantern/systray/pull/94) ([teddywing](https://github.com/teddywing))
+- Attempt to fix https://github.com/getlantern/systray/issues/75 [\#92](https://github.com/getlantern/systray/pull/92) ([mikeschinkel](https://github.com/mikeschinkel))
+- Fix application path for MacOS in README [\#91](https://github.com/getlantern/systray/pull/91) ([zereraz](https://github.com/zereraz))
+- Document cross-platform console window details [\#81](https://github.com/getlantern/systray/pull/81) ([michaelsanford](https://github.com/michaelsanford))
+- Fix bad-looking system tray icon in Windows [\#78](https://github.com/getlantern/systray/pull/78) ([juja256](https://github.com/juja256))
+- Add the separator to the visible items [\#76](https://github.com/getlantern/systray/pull/76) ([meskio](https://github.com/meskio))
+- keep track of hidden items [\#74](https://github.com/getlantern/systray/pull/74) ([kalikaneko](https://github.com/kalikaneko))
+- Support macOS older than 10.13 [\#73](https://github.com/getlantern/systray/pull/73) ([swznd](https://github.com/swznd))
+- define ERROR\_SUCCESS as syscall.Errno [\#69](https://github.com/getlantern/systray/pull/69) ([joesis](https://github.com/joesis))
+- Bug/fix broken menuitem show [\#68](https://github.com/getlantern/systray/pull/68) ([kalikaneko](https://github.com/kalikaneko))
+- Fix mac deprecations [\#66](https://github.com/getlantern/systray/pull/66) ([jefvel](https://github.com/jefvel))
+- Made it possible to add icons to menu items on Mac [\#65](https://github.com/getlantern/systray/pull/65) ([jefvel](https://github.com/jefvel))
+- linux: delete temp files as soon as they are not needed [\#63](https://github.com/getlantern/systray/pull/63) ([meskio](https://github.com/meskio))
+- Merge changes from amkulikov to remove DLL for windows [\#56](https://github.com/getlantern/systray/pull/56) ([oxtoacart](https://github.com/oxtoacart))
+- Revert "Use templated icons for the menu bar in macOS" [\#51](https://github.com/getlantern/systray/pull/51) ([stoggi](https://github.com/stoggi))
+- Use templated icons for the menu bar in macOS [\#46](https://github.com/getlantern/systray/pull/46) ([stoggi](https://github.com/stoggi))
+- Syscalls instead of custom DLLs [\#44](https://github.com/getlantern/systray/pull/44) ([amkulikov](https://github.com/amkulikov))
+- On quit exit main loop on linux [\#41](https://github.com/getlantern/systray/pull/41) ([meskio](https://github.com/meskio))
+- Fixed hide show in linux \(\#37\) [\#39](https://github.com/getlantern/systray/pull/39) ([meskio](https://github.com/meskio))
+- fix: linux compilation warning [\#36](https://github.com/getlantern/systray/pull/36) ([novln](https://github.com/novln))
+- Added separator functionality [\#32](https://github.com/getlantern/systray/pull/32) ([oxtoacart](https://github.com/oxtoacart))
+
+
+
+\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
diff --git a/vendor/fyne.io/systray/LICENSE b/vendor/fyne.io/systray/LICENSE
new file mode 100644
index 0000000000..3ee01626e1
--- /dev/null
+++ b/vendor/fyne.io/systray/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2014 Brave New Software Project, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/fyne.io/systray/Makefile b/vendor/fyne.io/systray/Makefile
new file mode 100644
index 0000000000..12f3d221f0
--- /dev/null
+++ b/vendor/fyne.io/systray/Makefile
@@ -0,0 +1,18 @@
+tag-changelog: require-version require-gh-token
+ echo "Tagging..." && \
+ git tag -a "$$VERSION" -f --annotate -m"Tagged $$VERSION" && \
+ git push --tags -f && \
+ git checkout master && \
+ git pull && \
+ github_changelog_generator --no-issues --max-issues 100 --token "${GH_TOKEN}" --user getlantern --project systray && \
+ git add CHANGELOG.md && \
+ git commit -m "Updated changelog for $$VERSION" && \
+ git push origin HEAD && \
+ git checkout -
+
+guard-%:
+ @ if [ -z '${${*}}' ]; then echo 'Environment variable $* not set' && exit 1; fi
+
+require-version: guard-VERSION
+
+require-gh-token: guard-GH_TOKEN
diff --git a/vendor/fyne.io/systray/README.md b/vendor/fyne.io/systray/README.md
new file mode 100644
index 0000000000..a65449ea49
--- /dev/null
+++ b/vendor/fyne.io/systray/README.md
@@ -0,0 +1,122 @@
+# Systray
+
+systray is a cross-platform Go library to place an icon and menu in the notification area.
+This repository is a fork of [getlantern/systray](https://github.com/getlantern/systray)
+removing the GTK dependency and support for legacy linux system tray.
+
+## Features
+
+* Supported on Windows, macOS and Linux
+* Menu items can be checked and/or disabled
+* Methods may be called from any Goroutine
+
+## API
+
+```go
+package main
+
+import "fyne.io/systray"
+
+func main() {
+ systray.Run(onReady, onExit)
+}
+
+func onReady() {
+ systray.SetIcon(icon.Data)
+ systray.SetTitle("Awesome App")
+ systray.SetTooltip("Pretty awesome超级棒")
+ mQuit := systray.AddMenuItem("Quit", "Quit the whole app")
+
+ // Sets the icon of a menu item. Only available on Mac and Windows.
+ mQuit.SetIcon(icon.Data)
+}
+
+func onExit() {
+ // clean up here
+}
+```
+
+### Run in another toolkit
+
+Most graphical toolkits will grab the main loop so the `Run` code above is not possible.
+For this reason there is another entry point `RunWithExternalLoop`.
+This function of the library returns a start and end function that should be called
+when the application has started and will end, to loop in appropriate features.
+
+See [full API](https://pkg.go.dev/fyne.io/systray?tab=doc) as well as [CHANGELOG](https://github.com/fyne-io/systray/tree/master/CHANGELOG.md).
+
+## Try the example app!
+
+Have go v1.12+ or higher installed? Here's an example to get started on macOS:
+
+```sh
+git clone https://github.com/fyne-io/systray
+cd systray/example
+env GO111MODULE=on go build
+./example
+```
+
+On Windows, you should build like this:
+
+```
+env GO111MODULE=on go build -ldflags "-H=windowsgui"
+```
+
+The following text will then appear on the console:
+
+
+```sh
+go: finding github.com/fyne-io/systray latest
+```
+
+Now look for *Awesome App* in your menu bar!
+
+![Awesome App screenshot](example/screenshot.png)
+
+## Platform notes
+
+### Linux
+
+This implementation uses DBus to communicate through the SystemNotifier/AppIndicator spec, older tray implementations may not load the icon.
+
+### Windows
+
+* To avoid opening a console at application startup, use "fyne package" for your app or manually use these compile flags:
+
+```sh
+go build -ldflags -H=windowsgui
+```
+
+### macOS
+
+On macOS, you will need to create an application bundle to wrap the binary; simply use "fyne package" or add folders with the following minimal structure and assets:
+
+```
+SystrayApp.app/
+ Contents/
+ Info.plist
+ MacOS/
+ go-executable
+ Resources/
+ SystrayApp.icns
+```
+
+When running as an app bundle, you may want to add one or both of the following to your Info.plist:
+
+```xml
+
+ NSHighResolutionCapable
+ True
+
+
+ LSUIElement
+ 1
+```
+
+Consult the [Official Apple Documentation here](https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html#//apple_ref/doc/uid/10000123i-CH101-SW1).
+
+## Credits
+
+- https://github.com/getlantern/systray
+- https://github.com/xilp/systray
+- https://github.com/cratonica/trayhost
diff --git a/vendor/fyne.io/systray/go.mod b/vendor/fyne.io/systray/go.mod
new file mode 100644
index 0000000000..3edf71cae1
--- /dev/null
+++ b/vendor/fyne.io/systray/go.mod
@@ -0,0 +1,7 @@
+module fyne.io/systray
+
+go 1.13
+
+require golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9
+
+require github.com/godbus/dbus/v5 v5.0.4
diff --git a/vendor/fyne.io/systray/go.sum b/vendor/fyne.io/systray/go.sum
new file mode 100644
index 0000000000..ee3fb7e910
--- /dev/null
+++ b/vendor/fyne.io/systray/go.sum
@@ -0,0 +1,4 @@
+github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9 h1:YTzHMGlqJu67/uEo1lBv0n3wBXhXNeUbB1XfN2vmTm0=
+golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
diff --git a/vendor/fyne.io/systray/internal/generated/menu/dbus_menu.go b/vendor/fyne.io/systray/internal/generated/menu/dbus_menu.go
new file mode 100644
index 0000000000..1b896d3e06
--- /dev/null
+++ b/vendor/fyne.io/systray/internal/generated/menu/dbus_menu.go
@@ -0,0 +1,484 @@
+// Code generated by dbus-codegen-go DO NOT EDIT.
+package menu
+
+import (
+ "context"
+ "errors"
+ "fmt"
+
+ "github.com/godbus/dbus/v5"
+ "github.com/godbus/dbus/v5/introspect"
+)
+
+var (
+ // Introspection for com.canonical.dbusmenu
+ IntrospectDataDbusmenu = introspect.Interface{
+ Name: "com.canonical.dbusmenu",
+ Methods: []introspect.Method{{Name: "GetLayout", Args: []introspect.Arg{
+ {Name: "parentId", Type: "i", Direction: "in"},
+ {Name: "recursionDepth", Type: "i", Direction: "in"},
+ {Name: "propertyNames", Type: "as", Direction: "in"},
+ {Name: "revision", Type: "u", Direction: "out"},
+ {Name: "layout", Type: "(ia{sv}av)", Direction: "out"},
+ }},
+ {Name: "GetGroupProperties", Args: []introspect.Arg{
+ {Name: "ids", Type: "ai", Direction: "in"},
+ {Name: "propertyNames", Type: "as", Direction: "in"},
+ {Name: "properties", Type: "a(ia{sv})", Direction: "out"},
+ }},
+ {Name: "GetProperty", Args: []introspect.Arg{
+ {Name: "id", Type: "i", Direction: "in"},
+ {Name: "name", Type: "s", Direction: "in"},
+ {Name: "value", Type: "v", Direction: "out"},
+ }},
+ {Name: "Event", Args: []introspect.Arg{
+ {Name: "id", Type: "i", Direction: "in"},
+ {Name: "eventId", Type: "s", Direction: "in"},
+ {Name: "data", Type: "v", Direction: "in"},
+ {Name: "timestamp", Type: "u", Direction: "in"},
+ }},
+ {Name: "EventGroup", Args: []introspect.Arg{
+ {Name: "events", Type: "a(isvu)", Direction: "in"},
+ {Name: "idErrors", Type: "ai", Direction: "out"},
+ }},
+ {Name: "AboutToShow", Args: []introspect.Arg{
+ {Name: "id", Type: "i", Direction: "in"},
+ {Name: "needUpdate", Type: "b", Direction: "out"},
+ }},
+ {Name: "AboutToShowGroup", Args: []introspect.Arg{
+ {Name: "ids", Type: "ai", Direction: "in"},
+ {Name: "updatesNeeded", Type: "ai", Direction: "out"},
+ {Name: "idErrors", Type: "ai", Direction: "out"},
+ }},
+ },
+ Signals: []introspect.Signal{{Name: "ItemsPropertiesUpdated", Args: []introspect.Arg{
+ {Name: "updatedProps", Type: "a(ia{sv})", Direction: "out"},
+ {Name: "removedProps", Type: "a(ias)", Direction: "out"},
+ }},
+ {Name: "LayoutUpdated", Args: []introspect.Arg{
+ {Name: "revision", Type: "u", Direction: "out"},
+ {Name: "parent", Type: "i", Direction: "out"},
+ }},
+ {Name: "ItemActivationRequested", Args: []introspect.Arg{
+ {Name: "id", Type: "i", Direction: "out"},
+ {Name: "timestamp", Type: "u", Direction: "out"},
+ }},
+ },
+ Properties: []introspect.Property{{Name: "Version", Type: "u", Access: "read"},
+ {Name: "TextDirection", Type: "s", Access: "read"},
+ {Name: "Status", Type: "s", Access: "read"},
+ {Name: "IconThemePath", Type: "as", Access: "read"},
+ },
+ Annotations: []introspect.Annotation{},
+ }
+)
+
+// Signal is a common interface for all signals.
+type Signal interface {
+ Name() string
+ Interface() string
+ Sender() string
+
+ path() dbus.ObjectPath
+ values() []interface{}
+}
+
+// Emit sends the given signal to the bus.
+func Emit(conn *dbus.Conn, s Signal) error {
+ return conn.Emit(s.path(), s.Interface()+"."+s.Name(), s.values()...)
+}
+
+// ErrUnknownSignal is returned by LookupSignal when a signal cannot be resolved.
+var ErrUnknownSignal = errors.New("unknown signal")
+
+// LookupSignal converts the given raw D-Bus signal with variable body
+// into one with typed structured body or returns ErrUnknownSignal error.
+func LookupSignal(signal *dbus.Signal) (Signal, error) {
+ switch signal.Name {
+ case InterfaceDbusmenu + "." + "ItemsPropertiesUpdated":
+ v0, ok := signal.Body[0].([]struct {
+ V0 int32
+ V1 map[string]dbus.Variant
+ })
+ if !ok {
+ return nil, fmt.Errorf("prop .UpdatedProps is %T, not []struct {V0 int32;V1 map[string]dbus.Variant}", signal.Body[0])
+ }
+ v1, ok := signal.Body[1].([]struct {
+ V0 int32
+ V1 []string
+ })
+ if !ok {
+ return nil, fmt.Errorf("prop .RemovedProps is %T, not []struct {V0 int32;V1 []string}", signal.Body[1])
+ }
+ return &Dbusmenu_ItemsPropertiesUpdatedSignal{
+ sender: signal.Sender,
+ Path: signal.Path,
+ Body: &Dbusmenu_ItemsPropertiesUpdatedSignalBody{
+ UpdatedProps: v0,
+ RemovedProps: v1,
+ },
+ }, nil
+ case InterfaceDbusmenu + "." + "LayoutUpdated":
+ v0, ok := signal.Body[0].(uint32)
+ if !ok {
+ return nil, fmt.Errorf("prop .Revision is %T, not uint32", signal.Body[0])
+ }
+ v1, ok := signal.Body[1].(int32)
+ if !ok {
+ return nil, fmt.Errorf("prop .Parent is %T, not int32", signal.Body[1])
+ }
+ return &Dbusmenu_LayoutUpdatedSignal{
+ sender: signal.Sender,
+ Path: signal.Path,
+ Body: &Dbusmenu_LayoutUpdatedSignalBody{
+ Revision: v0,
+ Parent: v1,
+ },
+ }, nil
+ case InterfaceDbusmenu + "." + "ItemActivationRequested":
+ v0, ok := signal.Body[0].(int32)
+ if !ok {
+ return nil, fmt.Errorf("prop .Id is %T, not int32", signal.Body[0])
+ }
+ v1, ok := signal.Body[1].(uint32)
+ if !ok {
+ return nil, fmt.Errorf("prop .Timestamp is %T, not uint32", signal.Body[1])
+ }
+ return &Dbusmenu_ItemActivationRequestedSignal{
+ sender: signal.Sender,
+ Path: signal.Path,
+ Body: &Dbusmenu_ItemActivationRequestedSignalBody{
+ Id: v0,
+ Timestamp: v1,
+ },
+ }, nil
+ default:
+ return nil, ErrUnknownSignal
+ }
+}
+
+// AddMatchSignal registers a match rule for the given signal,
+// opts are appended to the automatically generated signal's rules.
+func AddMatchSignal(conn *dbus.Conn, s Signal, opts ...dbus.MatchOption) error {
+ return conn.AddMatchSignal(append([]dbus.MatchOption{
+ dbus.WithMatchInterface(s.Interface()),
+ dbus.WithMatchMember(s.Name()),
+ }, opts...)...)
+}
+
+// RemoveMatchSignal unregisters the previously registered subscription.
+func RemoveMatchSignal(conn *dbus.Conn, s Signal, opts ...dbus.MatchOption) error {
+ return conn.RemoveMatchSignal(append([]dbus.MatchOption{
+ dbus.WithMatchInterface(s.Interface()),
+ dbus.WithMatchMember(s.Name()),
+ }, opts...)...)
+}
+
+// Interface name constants.
+const (
+ InterfaceDbusmenu = "com.canonical.dbusmenu"
+)
+
+// Dbusmenuer is com.canonical.dbusmenu interface.
+type Dbusmenuer interface {
+ // GetLayout is com.canonical.dbusmenu.GetLayout method.
+ GetLayout(parentId int32, recursionDepth int32, propertyNames []string) (revision uint32, layout struct {
+ V0 int32
+ V1 map[string]dbus.Variant
+ V2 []dbus.Variant
+ }, err *dbus.Error)
+ // GetGroupProperties is com.canonical.dbusmenu.GetGroupProperties method.
+ GetGroupProperties(ids []int32, propertyNames []string) (properties []struct {
+ V0 int32
+ V1 map[string]dbus.Variant
+ }, err *dbus.Error)
+ // GetProperty is com.canonical.dbusmenu.GetProperty method.
+ GetProperty(id int32, name string) (value dbus.Variant, err *dbus.Error)
+ // Event is com.canonical.dbusmenu.Event method.
+ Event(id int32, eventId string, data dbus.Variant, timestamp uint32) (err *dbus.Error)
+ // EventGroup is com.canonical.dbusmenu.EventGroup method.
+ EventGroup(events []struct {
+ V0 int32
+ V1 string
+ V2 dbus.Variant
+ V3 uint32
+ }) (idErrors []int32, err *dbus.Error)
+ // AboutToShow is com.canonical.dbusmenu.AboutToShow method.
+ AboutToShow(id int32) (needUpdate bool, err *dbus.Error)
+ // AboutToShowGroup is com.canonical.dbusmenu.AboutToShowGroup method.
+ AboutToShowGroup(ids []int32) (updatesNeeded []int32, idErrors []int32, err *dbus.Error)
+}
+
+// ExportDbusmenu exports the given object that implements com.canonical.dbusmenu on the bus.
+func ExportDbusmenu(conn *dbus.Conn, path dbus.ObjectPath, v Dbusmenuer) error {
+ return conn.ExportSubtreeMethodTable(map[string]interface{}{
+ "GetLayout": v.GetLayout,
+ "GetGroupProperties": v.GetGroupProperties,
+ "GetProperty": v.GetProperty,
+ "Event": v.Event,
+ "EventGroup": v.EventGroup,
+ "AboutToShow": v.AboutToShow,
+ "AboutToShowGroup": v.AboutToShowGroup,
+ }, path, InterfaceDbusmenu)
+}
+
+// UnexportDbusmenu unexports com.canonical.dbusmenu interface on the named path.
+func UnexportDbusmenu(conn *dbus.Conn, path dbus.ObjectPath) error {
+ return conn.Export(nil, path, InterfaceDbusmenu)
+}
+
+// UnimplementedDbusmenu can be embedded to have forward compatible server implementations.
+type UnimplementedDbusmenu struct{}
+
+func (*UnimplementedDbusmenu) iface() string {
+ return InterfaceDbusmenu
+}
+
+func (*UnimplementedDbusmenu) GetLayout(parentId int32, recursionDepth int32, propertyNames []string) (revision uint32, layout struct {
+ V0 int32
+ V1 map[string]dbus.Variant
+ V2 []dbus.Variant
+}, err *dbus.Error) {
+ err = &dbus.ErrMsgUnknownMethod
+ return
+}
+
+func (*UnimplementedDbusmenu) GetGroupProperties(ids []int32, propertyNames []string) (properties []struct {
+ V0 int32
+ V1 map[string]dbus.Variant
+}, err *dbus.Error) {
+ err = &dbus.ErrMsgUnknownMethod
+ return
+}
+
+func (*UnimplementedDbusmenu) GetProperty(id int32, name string) (value dbus.Variant, err *dbus.Error) {
+ err = &dbus.ErrMsgUnknownMethod
+ return
+}
+
+func (*UnimplementedDbusmenu) Event(id int32, eventId string, data dbus.Variant, timestamp uint32) (err *dbus.Error) {
+ err = &dbus.ErrMsgUnknownMethod
+ return
+}
+
+func (*UnimplementedDbusmenu) EventGroup(events []struct {
+ V0 int32
+ V1 string
+ V2 dbus.Variant
+ V3 uint32
+}) (idErrors []int32, err *dbus.Error) {
+ err = &dbus.ErrMsgUnknownMethod
+ return
+}
+
+func (*UnimplementedDbusmenu) AboutToShow(id int32) (needUpdate bool, err *dbus.Error) {
+ err = &dbus.ErrMsgUnknownMethod
+ return
+}
+
+func (*UnimplementedDbusmenu) AboutToShowGroup(ids []int32) (updatesNeeded []int32, idErrors []int32, err *dbus.Error) {
+ err = &dbus.ErrMsgUnknownMethod
+ return
+}
+
+// NewDbusmenu creates and allocates com.canonical.dbusmenu.
+func NewDbusmenu(object dbus.BusObject) *Dbusmenu {
+ return &Dbusmenu{object}
+}
+
+// Dbusmenu implements com.canonical.dbusmenu D-Bus interface.
+type Dbusmenu struct {
+ object dbus.BusObject
+}
+
+// GetLayout calls com.canonical.dbusmenu.GetLayout method.
+func (o *Dbusmenu) GetLayout(ctx context.Context, parentId int32, recursionDepth int32, propertyNames []string) (revision uint32, layout struct {
+ V0 int32
+ V1 map[string]dbus.Variant
+ V2 []dbus.Variant
+}, err error) {
+ err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".GetLayout", 0, parentId, recursionDepth, propertyNames).Store(&revision, &layout)
+ return
+}
+
+// GetGroupProperties calls com.canonical.dbusmenu.GetGroupProperties method.
+func (o *Dbusmenu) GetGroupProperties(ctx context.Context, ids []int32, propertyNames []string) (properties []struct {
+ V0 int32
+ V1 map[string]dbus.Variant
+}, err error) {
+ err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".GetGroupProperties", 0, ids, propertyNames).Store(&properties)
+ return
+}
+
+// GetProperty calls com.canonical.dbusmenu.GetProperty method.
+func (o *Dbusmenu) GetProperty(ctx context.Context, id int32, name string) (value dbus.Variant, err error) {
+ err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".GetProperty", 0, id, name).Store(&value)
+ return
+}
+
+// Event calls com.canonical.dbusmenu.Event method.
+func (o *Dbusmenu) Event(ctx context.Context, id int32, eventId string, data dbus.Variant, timestamp uint32) (err error) {
+ err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".Event", 0, id, eventId, data, timestamp).Store()
+ return
+}
+
+// EventGroup calls com.canonical.dbusmenu.EventGroup method.
+func (o *Dbusmenu) EventGroup(ctx context.Context, events []struct {
+ V0 int32
+ V1 string
+ V2 dbus.Variant
+ V3 uint32
+}) (idErrors []int32, err error) {
+ err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".EventGroup", 0, events).Store(&idErrors)
+ return
+}
+
+// AboutToShow calls com.canonical.dbusmenu.AboutToShow method.
+func (o *Dbusmenu) AboutToShow(ctx context.Context, id int32) (needUpdate bool, err error) {
+ err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".AboutToShow", 0, id).Store(&needUpdate)
+ return
+}
+
+// AboutToShowGroup calls com.canonical.dbusmenu.AboutToShowGroup method.
+func (o *Dbusmenu) AboutToShowGroup(ctx context.Context, ids []int32) (updatesNeeded []int32, idErrors []int32, err error) {
+ err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".AboutToShowGroup", 0, ids).Store(&updatesNeeded, &idErrors)
+ return
+}
+
+// GetVersion gets com.canonical.dbusmenu.Version property.
+func (o *Dbusmenu) GetVersion(ctx context.Context) (version uint32, err error) {
+ err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceDbusmenu, "Version").Store(&version)
+ return
+}
+
+// GetTextDirection gets com.canonical.dbusmenu.TextDirection property.
+func (o *Dbusmenu) GetTextDirection(ctx context.Context) (textDirection string, err error) {
+ err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceDbusmenu, "TextDirection").Store(&textDirection)
+ return
+}
+
+// GetStatus gets com.canonical.dbusmenu.Status property.
+func (o *Dbusmenu) GetStatus(ctx context.Context) (status string, err error) {
+ err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceDbusmenu, "Status").Store(&status)
+ return
+}
+
+// GetIconThemePath gets com.canonical.dbusmenu.IconThemePath property.
+func (o *Dbusmenu) GetIconThemePath(ctx context.Context) (iconThemePath []string, err error) {
+ err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceDbusmenu, "IconThemePath").Store(&iconThemePath)
+ return
+}
+
+// Dbusmenu_ItemsPropertiesUpdatedSignal represents com.canonical.dbusmenu.ItemsPropertiesUpdated signal.
+type Dbusmenu_ItemsPropertiesUpdatedSignal struct {
+ sender string
+ Path dbus.ObjectPath
+ Body *Dbusmenu_ItemsPropertiesUpdatedSignalBody
+}
+
+// Name returns the signal's name.
+func (s *Dbusmenu_ItemsPropertiesUpdatedSignal) Name() string {
+ return "ItemsPropertiesUpdated"
+}
+
+// Interface returns the signal's interface.
+func (s *Dbusmenu_ItemsPropertiesUpdatedSignal) Interface() string {
+ return InterfaceDbusmenu
+}
+
+// Sender returns the signal's sender unique name.
+func (s *Dbusmenu_ItemsPropertiesUpdatedSignal) Sender() string {
+ return s.sender
+}
+
+func (s *Dbusmenu_ItemsPropertiesUpdatedSignal) path() dbus.ObjectPath {
+ return s.Path
+}
+
+func (s *Dbusmenu_ItemsPropertiesUpdatedSignal) values() []interface{} {
+ return []interface{}{s.Body.UpdatedProps, s.Body.RemovedProps}
+}
+
+// Dbusmenu_ItemsPropertiesUpdatedSignalBody is body container.
+type Dbusmenu_ItemsPropertiesUpdatedSignalBody struct {
+ UpdatedProps []struct {
+ V0 int32
+ V1 map[string]dbus.Variant
+ }
+ RemovedProps []struct {
+ V0 int32
+ V1 []string
+ }
+}
+
+// Dbusmenu_LayoutUpdatedSignal represents com.canonical.dbusmenu.LayoutUpdated signal.
+type Dbusmenu_LayoutUpdatedSignal struct {
+ sender string
+ Path dbus.ObjectPath
+ Body *Dbusmenu_LayoutUpdatedSignalBody
+}
+
+// Name returns the signal's name.
+func (s *Dbusmenu_LayoutUpdatedSignal) Name() string {
+ return "LayoutUpdated"
+}
+
+// Interface returns the signal's interface.
+func (s *Dbusmenu_LayoutUpdatedSignal) Interface() string {
+ return InterfaceDbusmenu
+}
+
+// Sender returns the signal's sender unique name.
+func (s *Dbusmenu_LayoutUpdatedSignal) Sender() string {
+ return s.sender
+}
+
+func (s *Dbusmenu_LayoutUpdatedSignal) path() dbus.ObjectPath {
+ return s.Path
+}
+
+func (s *Dbusmenu_LayoutUpdatedSignal) values() []interface{} {
+ return []interface{}{s.Body.Revision, s.Body.Parent}
+}
+
+// Dbusmenu_LayoutUpdatedSignalBody is body container.
+type Dbusmenu_LayoutUpdatedSignalBody struct {
+ Revision uint32
+ Parent int32
+}
+
+// Dbusmenu_ItemActivationRequestedSignal represents com.canonical.dbusmenu.ItemActivationRequested signal.
+type Dbusmenu_ItemActivationRequestedSignal struct {
+ sender string
+ Path dbus.ObjectPath
+ Body *Dbusmenu_ItemActivationRequestedSignalBody
+}
+
+// Name returns the signal's name.
+func (s *Dbusmenu_ItemActivationRequestedSignal) Name() string {
+ return "ItemActivationRequested"
+}
+
+// Interface returns the signal's interface.
+func (s *Dbusmenu_ItemActivationRequestedSignal) Interface() string {
+ return InterfaceDbusmenu
+}
+
+// Sender returns the signal's sender unique name.
+func (s *Dbusmenu_ItemActivationRequestedSignal) Sender() string {
+ return s.sender
+}
+
+func (s *Dbusmenu_ItemActivationRequestedSignal) path() dbus.ObjectPath {
+ return s.Path
+}
+
+func (s *Dbusmenu_ItemActivationRequestedSignal) values() []interface{} {
+ return []interface{}{s.Body.Id, s.Body.Timestamp}
+}
+
+// Dbusmenu_ItemActivationRequestedSignalBody is body container.
+type Dbusmenu_ItemActivationRequestedSignalBody struct {
+ Id int32
+ Timestamp uint32
+}
diff --git a/vendor/fyne.io/systray/internal/generated/notifier/status_notifier_item.go b/vendor/fyne.io/systray/internal/generated/notifier/status_notifier_item.go
new file mode 100644
index 0000000000..6afbdb0854
--- /dev/null
+++ b/vendor/fyne.io/systray/internal/generated/notifier/status_notifier_item.go
@@ -0,0 +1,612 @@
+// Code generated by dbus-codegen-go DO NOT EDIT.
+package notifier
+
+import (
+ "context"
+ "errors"
+ "fmt"
+
+ "github.com/godbus/dbus/v5"
+ "github.com/godbus/dbus/v5/introspect"
+)
+
+var (
+ // Introspection for org.kde.StatusNotifierItem
+ IntrospectDataStatusNotifierItem = introspect.Interface{
+ Name: "org.kde.StatusNotifierItem",
+ Methods: []introspect.Method{{Name: "ContextMenu", Args: []introspect.Arg{
+ {Name: "x", Type: "i", Direction: "in"},
+ {Name: "y", Type: "i", Direction: "in"},
+ }},
+ {Name: "Activate", Args: []introspect.Arg{
+ {Name: "x", Type: "i", Direction: "in"},
+ {Name: "y", Type: "i", Direction: "in"},
+ }},
+ {Name: "SecondaryActivate", Args: []introspect.Arg{
+ {Name: "x", Type: "i", Direction: "in"},
+ {Name: "y", Type: "i", Direction: "in"},
+ }},
+ {Name: "Scroll", Args: []introspect.Arg{
+ {Name: "delta", Type: "i", Direction: "in"},
+ {Name: "orientation", Type: "s", Direction: "in"},
+ }},
+ },
+ Signals: []introspect.Signal{{Name: "NewTitle"},
+ {Name: "NewIcon"},
+ {Name: "NewAttentionIcon"},
+ {Name: "NewOverlayIcon"},
+ {Name: "NewStatus", Args: []introspect.Arg{
+ {Name: "status", Type: "s", Direction: ""},
+ }},
+ {Name: "NewIconThemePath", Args: []introspect.Arg{
+ {Name: "icon_theme_path", Type: "s", Direction: "out"},
+ }},
+ {Name: "NewMenu"},
+ },
+ Properties: []introspect.Property{{Name: "Category", Type: "s", Access: "read"},
+ {Name: "Id", Type: "s", Access: "read"},
+ {Name: "Title", Type: "s", Access: "read"},
+ {Name: "Status", Type: "s", Access: "read"},
+ {Name: "WindowId", Type: "i", Access: "read"},
+ {Name: "IconThemePath", Type: "s", Access: "read"},
+ {Name: "Menu", Type: "o", Access: "read"},
+ {Name: "ItemIsMenu", Type: "b", Access: "read"},
+ {Name: "IconName", Type: "s", Access: "read"},
+ {Name: "IconPixmap", Type: "a(iiay)", Access: "read", Annotations: []introspect.Annotation{
+ {Name: "org.qtproject.QtDBus.QtTypeName", Value: "KDbusImageVector"},
+ }},
+ {Name: "OverlayIconName", Type: "s", Access: "read"},
+ {Name: "OverlayIconPixmap", Type: "a(iiay)", Access: "read", Annotations: []introspect.Annotation{
+ {Name: "org.qtproject.QtDBus.QtTypeName", Value: "KDbusImageVector"},
+ }},
+ {Name: "AttentionIconName", Type: "s", Access: "read"},
+ {Name: "AttentionIconPixmap", Type: "a(iiay)", Access: "read", Annotations: []introspect.Annotation{
+ {Name: "org.qtproject.QtDBus.QtTypeName", Value: "KDbusImageVector"},
+ }},
+ {Name: "AttentionMovieName", Type: "s", Access: "read"},
+ },
+ Annotations: []introspect.Annotation{},
+ }
+)
+
+// Signal is a common interface for all signals.
+type Signal interface {
+ Name() string
+ Interface() string
+ Sender() string
+
+ path() dbus.ObjectPath
+ values() []interface{}
+}
+
+// Emit sends the given signal to the bus.
+func Emit(conn *dbus.Conn, s Signal) error {
+ return conn.Emit(s.path(), s.Interface()+"."+s.Name(), s.values()...)
+}
+
+// ErrUnknownSignal is returned by LookupSignal when a signal cannot be resolved.
+var ErrUnknownSignal = errors.New("unknown signal")
+
+// LookupSignal converts the given raw D-Bus signal with variable body
+// into one with typed structured body or returns ErrUnknownSignal error.
+func LookupSignal(signal *dbus.Signal) (Signal, error) {
+ switch signal.Name {
+ case InterfaceStatusNotifierItem + "." + "NewTitle":
+ return &StatusNotifierItem_NewTitleSignal{
+ sender: signal.Sender,
+ Path: signal.Path,
+ Body: &StatusNotifierItem_NewTitleSignalBody{},
+ }, nil
+ case InterfaceStatusNotifierItem + "." + "NewIcon":
+ return &StatusNotifierItem_NewIconSignal{
+ sender: signal.Sender,
+ Path: signal.Path,
+ Body: &StatusNotifierItem_NewIconSignalBody{},
+ }, nil
+ case InterfaceStatusNotifierItem + "." + "NewAttentionIcon":
+ return &StatusNotifierItem_NewAttentionIconSignal{
+ sender: signal.Sender,
+ Path: signal.Path,
+ Body: &StatusNotifierItem_NewAttentionIconSignalBody{},
+ }, nil
+ case InterfaceStatusNotifierItem + "." + "NewOverlayIcon":
+ return &StatusNotifierItem_NewOverlayIconSignal{
+ sender: signal.Sender,
+ Path: signal.Path,
+ Body: &StatusNotifierItem_NewOverlayIconSignalBody{},
+ }, nil
+ case InterfaceStatusNotifierItem + "." + "NewStatus":
+ v0, ok := signal.Body[0].(string)
+ if !ok {
+ return nil, fmt.Errorf("prop .Status is %T, not string", signal.Body[0])
+ }
+ return &StatusNotifierItem_NewStatusSignal{
+ sender: signal.Sender,
+ Path: signal.Path,
+ Body: &StatusNotifierItem_NewStatusSignalBody{
+ Status: v0,
+ },
+ }, nil
+ case InterfaceStatusNotifierItem + "." + "NewIconThemePath":
+ v0, ok := signal.Body[0].(string)
+ if !ok {
+ return nil, fmt.Errorf("prop .IconThemePath is %T, not string", signal.Body[0])
+ }
+ return &StatusNotifierItem_NewIconThemePathSignal{
+ sender: signal.Sender,
+ Path: signal.Path,
+ Body: &StatusNotifierItem_NewIconThemePathSignalBody{
+ IconThemePath: v0,
+ },
+ }, nil
+ case InterfaceStatusNotifierItem + "." + "NewMenu":
+ return &StatusNotifierItem_NewMenuSignal{
+ sender: signal.Sender,
+ Path: signal.Path,
+ Body: &StatusNotifierItem_NewMenuSignalBody{},
+ }, nil
+ default:
+ return nil, ErrUnknownSignal
+ }
+}
+
+// AddMatchSignal registers a match rule for the given signal,
+// opts are appended to the automatically generated signal's rules.
+func AddMatchSignal(conn *dbus.Conn, s Signal, opts ...dbus.MatchOption) error {
+ return conn.AddMatchSignal(append([]dbus.MatchOption{
+ dbus.WithMatchInterface(s.Interface()),
+ dbus.WithMatchMember(s.Name()),
+ }, opts...)...)
+}
+
+// RemoveMatchSignal unregisters the previously registered subscription.
+func RemoveMatchSignal(conn *dbus.Conn, s Signal, opts ...dbus.MatchOption) error {
+ return conn.RemoveMatchSignal(append([]dbus.MatchOption{
+ dbus.WithMatchInterface(s.Interface()),
+ dbus.WithMatchMember(s.Name()),
+ }, opts...)...)
+}
+
+// Interface name constants.
+const (
+ InterfaceStatusNotifierItem = "org.kde.StatusNotifierItem"
+)
+
+// StatusNotifierItemer is org.kde.StatusNotifierItem interface.
+type StatusNotifierItemer interface {
+ // ContextMenu is org.kde.StatusNotifierItem.ContextMenu method.
+ ContextMenu(x int32, y int32) (err *dbus.Error)
+ // Activate is org.kde.StatusNotifierItem.Activate method.
+ Activate(x int32, y int32) (err *dbus.Error)
+ // SecondaryActivate is org.kde.StatusNotifierItem.SecondaryActivate method.
+ SecondaryActivate(x int32, y int32) (err *dbus.Error)
+ // Scroll is org.kde.StatusNotifierItem.Scroll method.
+ Scroll(delta int32, orientation string) (err *dbus.Error)
+}
+
+// ExportStatusNotifierItem exports the given object that implements org.kde.StatusNotifierItem on the bus.
+func ExportStatusNotifierItem(conn *dbus.Conn, path dbus.ObjectPath, v StatusNotifierItemer) error {
+ return conn.ExportSubtreeMethodTable(map[string]interface{}{
+ "ContextMenu": v.ContextMenu,
+ "Activate": v.Activate,
+ "SecondaryActivate": v.SecondaryActivate,
+ "Scroll": v.Scroll,
+ }, path, InterfaceStatusNotifierItem)
+}
+
+// UnexportStatusNotifierItem unexports org.kde.StatusNotifierItem interface on the named path.
+func UnexportStatusNotifierItem(conn *dbus.Conn, path dbus.ObjectPath) error {
+ return conn.Export(nil, path, InterfaceStatusNotifierItem)
+}
+
+// UnimplementedStatusNotifierItem can be embedded to have forward compatible server implementations.
+type UnimplementedStatusNotifierItem struct{}
+
+func (*UnimplementedStatusNotifierItem) iface() string {
+ return InterfaceStatusNotifierItem
+}
+
+func (*UnimplementedStatusNotifierItem) ContextMenu(x int32, y int32) (err *dbus.Error) {
+ err = &dbus.ErrMsgUnknownMethod
+ return
+}
+
+func (*UnimplementedStatusNotifierItem) Activate(x int32, y int32) (err *dbus.Error) {
+ err = &dbus.ErrMsgUnknownMethod
+ return
+}
+
+func (*UnimplementedStatusNotifierItem) SecondaryActivate(x int32, y int32) (err *dbus.Error) {
+ err = &dbus.ErrMsgUnknownMethod
+ return
+}
+
+func (*UnimplementedStatusNotifierItem) Scroll(delta int32, orientation string) (err *dbus.Error) {
+ err = &dbus.ErrMsgUnknownMethod
+ return
+}
+
+// NewStatusNotifierItem creates and allocates org.kde.StatusNotifierItem.
+func NewStatusNotifierItem(object dbus.BusObject) *StatusNotifierItem {
+ return &StatusNotifierItem{object}
+}
+
+// StatusNotifierItem implements org.kde.StatusNotifierItem D-Bus interface.
+type StatusNotifierItem struct {
+ object dbus.BusObject
+}
+
+// ContextMenu calls org.kde.StatusNotifierItem.ContextMenu method.
+func (o *StatusNotifierItem) ContextMenu(ctx context.Context, x int32, y int32) (err error) {
+ err = o.object.CallWithContext(ctx, InterfaceStatusNotifierItem+".ContextMenu", 0, x, y).Store()
+ return
+}
+
+// Activate calls org.kde.StatusNotifierItem.Activate method.
+func (o *StatusNotifierItem) Activate(ctx context.Context, x int32, y int32) (err error) {
+ err = o.object.CallWithContext(ctx, InterfaceStatusNotifierItem+".Activate", 0, x, y).Store()
+ return
+}
+
+// SecondaryActivate calls org.kde.StatusNotifierItem.SecondaryActivate method.
+func (o *StatusNotifierItem) SecondaryActivate(ctx context.Context, x int32, y int32) (err error) {
+ err = o.object.CallWithContext(ctx, InterfaceStatusNotifierItem+".SecondaryActivate", 0, x, y).Store()
+ return
+}
+
+// Scroll calls org.kde.StatusNotifierItem.Scroll method.
+func (o *StatusNotifierItem) Scroll(ctx context.Context, delta int32, orientation string) (err error) {
+ err = o.object.CallWithContext(ctx, InterfaceStatusNotifierItem+".Scroll", 0, delta, orientation).Store()
+ return
+}
+
+// GetCategory gets org.kde.StatusNotifierItem.Category property.
+func (o *StatusNotifierItem) GetCategory(ctx context.Context) (category string, err error) {
+ err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "Category").Store(&category)
+ return
+}
+
+// GetId gets org.kde.StatusNotifierItem.Id property.
+func (o *StatusNotifierItem) GetId(ctx context.Context) (id string, err error) {
+ err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "Id").Store(&id)
+ return
+}
+
+// GetTitle gets org.kde.StatusNotifierItem.Title property.
+func (o *StatusNotifierItem) GetTitle(ctx context.Context) (title string, err error) {
+ err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "Title").Store(&title)
+ return
+}
+
+// GetStatus gets org.kde.StatusNotifierItem.Status property.
+func (o *StatusNotifierItem) GetStatus(ctx context.Context) (status string, err error) {
+ err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "Status").Store(&status)
+ return
+}
+
+// GetWindowId gets org.kde.StatusNotifierItem.WindowId property.
+func (o *StatusNotifierItem) GetWindowId(ctx context.Context) (windowId int32, err error) {
+ err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "WindowId").Store(&windowId)
+ return
+}
+
+// GetIconThemePath gets org.kde.StatusNotifierItem.IconThemePath property.
+func (o *StatusNotifierItem) GetIconThemePath(ctx context.Context) (iconThemePath string, err error) {
+ err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "IconThemePath").Store(&iconThemePath)
+ return
+}
+
+// GetMenu gets org.kde.StatusNotifierItem.Menu property.
+func (o *StatusNotifierItem) GetMenu(ctx context.Context) (menu dbus.ObjectPath, err error) {
+ err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "Menu").Store(&menu)
+ return
+}
+
+// GetItemIsMenu gets org.kde.StatusNotifierItem.ItemIsMenu property.
+func (o *StatusNotifierItem) GetItemIsMenu(ctx context.Context) (itemIsMenu bool, err error) {
+ err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "ItemIsMenu").Store(&itemIsMenu)
+ return
+}
+
+// GetIconName gets org.kde.StatusNotifierItem.IconName property.
+func (o *StatusNotifierItem) GetIconName(ctx context.Context) (iconName string, err error) {
+ err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "IconName").Store(&iconName)
+ return
+}
+
+// GetIconPixmap gets org.kde.StatusNotifierItem.IconPixmap property.
+//
+// Annotations:
+// @org.qtproject.QtDBus.QtTypeName = KDbusImageVector
+func (o *StatusNotifierItem) GetIconPixmap(ctx context.Context) (iconPixmap []struct {
+ V0 int32
+ V1 int32
+ V2 []byte
+}, err error) {
+ err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "IconPixmap").Store(&iconPixmap)
+ return
+}
+
+// GetOverlayIconName gets org.kde.StatusNotifierItem.OverlayIconName property.
+func (o *StatusNotifierItem) GetOverlayIconName(ctx context.Context) (overlayIconName string, err error) {
+ err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "OverlayIconName").Store(&overlayIconName)
+ return
+}
+
+// GetOverlayIconPixmap gets org.kde.StatusNotifierItem.OverlayIconPixmap property.
+//
+// Annotations:
+// @org.qtproject.QtDBus.QtTypeName = KDbusImageVector
+func (o *StatusNotifierItem) GetOverlayIconPixmap(ctx context.Context) (overlayIconPixmap []struct {
+ V0 int32
+ V1 int32
+ V2 []byte
+}, err error) {
+ err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "OverlayIconPixmap").Store(&overlayIconPixmap)
+ return
+}
+
+// GetAttentionIconName gets org.kde.StatusNotifierItem.AttentionIconName property.
+func (o *StatusNotifierItem) GetAttentionIconName(ctx context.Context) (attentionIconName string, err error) {
+ err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "AttentionIconName").Store(&attentionIconName)
+ return
+}
+
+// GetAttentionIconPixmap gets org.kde.StatusNotifierItem.AttentionIconPixmap property.
+//
+// Annotations:
+// @org.qtproject.QtDBus.QtTypeName = KDbusImageVector
+func (o *StatusNotifierItem) GetAttentionIconPixmap(ctx context.Context) (attentionIconPixmap []struct {
+ V0 int32
+ V1 int32
+ V2 []byte
+}, err error) {
+ err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "AttentionIconPixmap").Store(&attentionIconPixmap)
+ return
+}
+
+// GetAttentionMovieName gets org.kde.StatusNotifierItem.AttentionMovieName property.
+func (o *StatusNotifierItem) GetAttentionMovieName(ctx context.Context) (attentionMovieName string, err error) {
+ err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "AttentionMovieName").Store(&attentionMovieName)
+ return
+}
+
+// StatusNotifierItem_NewTitleSignal represents org.kde.StatusNotifierItem.NewTitle signal.
+type StatusNotifierItem_NewTitleSignal struct {
+ sender string
+ Path dbus.ObjectPath
+ Body *StatusNotifierItem_NewTitleSignalBody
+}
+
+// Name returns the signal's name.
+func (s *StatusNotifierItem_NewTitleSignal) Name() string {
+ return "NewTitle"
+}
+
+// Interface returns the signal's interface.
+func (s *StatusNotifierItem_NewTitleSignal) Interface() string {
+ return InterfaceStatusNotifierItem
+}
+
+// Sender returns the signal's sender unique name.
+func (s *StatusNotifierItem_NewTitleSignal) Sender() string {
+ return s.sender
+}
+
+func (s *StatusNotifierItem_NewTitleSignal) path() dbus.ObjectPath {
+ return s.Path
+}
+
+func (s *StatusNotifierItem_NewTitleSignal) values() []interface{} {
+ return []interface{}{}
+}
+
+// StatusNotifierItem_NewTitleSignalBody is body container.
+type StatusNotifierItem_NewTitleSignalBody struct {
+}
+
+// StatusNotifierItem_NewIconSignal represents org.kde.StatusNotifierItem.NewIcon signal.
+type StatusNotifierItem_NewIconSignal struct {
+ sender string
+ Path dbus.ObjectPath
+ Body *StatusNotifierItem_NewIconSignalBody
+}
+
+// Name returns the signal's name.
+func (s *StatusNotifierItem_NewIconSignal) Name() string {
+ return "NewIcon"
+}
+
+// Interface returns the signal's interface.
+func (s *StatusNotifierItem_NewIconSignal) Interface() string {
+ return InterfaceStatusNotifierItem
+}
+
+// Sender returns the signal's sender unique name.
+func (s *StatusNotifierItem_NewIconSignal) Sender() string {
+ return s.sender
+}
+
+func (s *StatusNotifierItem_NewIconSignal) path() dbus.ObjectPath {
+ return s.Path
+}
+
+func (s *StatusNotifierItem_NewIconSignal) values() []interface{} {
+ return []interface{}{}
+}
+
+// StatusNotifierItem_NewIconSignalBody is body container.
+type StatusNotifierItem_NewIconSignalBody struct {
+}
+
+// StatusNotifierItem_NewAttentionIconSignal represents org.kde.StatusNotifierItem.NewAttentionIcon signal.
+type StatusNotifierItem_NewAttentionIconSignal struct {
+ sender string
+ Path dbus.ObjectPath
+ Body *StatusNotifierItem_NewAttentionIconSignalBody
+}
+
+// Name returns the signal's name.
+func (s *StatusNotifierItem_NewAttentionIconSignal) Name() string {
+ return "NewAttentionIcon"
+}
+
+// Interface returns the signal's interface.
+func (s *StatusNotifierItem_NewAttentionIconSignal) Interface() string {
+ return InterfaceStatusNotifierItem
+}
+
+// Sender returns the signal's sender unique name.
+func (s *StatusNotifierItem_NewAttentionIconSignal) Sender() string {
+ return s.sender
+}
+
+func (s *StatusNotifierItem_NewAttentionIconSignal) path() dbus.ObjectPath {
+ return s.Path
+}
+
+func (s *StatusNotifierItem_NewAttentionIconSignal) values() []interface{} {
+ return []interface{}{}
+}
+
+// StatusNotifierItem_NewAttentionIconSignalBody is body container.
+type StatusNotifierItem_NewAttentionIconSignalBody struct {
+}
+
+// StatusNotifierItem_NewOverlayIconSignal represents org.kde.StatusNotifierItem.NewOverlayIcon signal.
+type StatusNotifierItem_NewOverlayIconSignal struct {
+ sender string
+ Path dbus.ObjectPath
+ Body *StatusNotifierItem_NewOverlayIconSignalBody
+}
+
+// Name returns the signal's name.
+func (s *StatusNotifierItem_NewOverlayIconSignal) Name() string {
+ return "NewOverlayIcon"
+}
+
+// Interface returns the signal's interface.
+func (s *StatusNotifierItem_NewOverlayIconSignal) Interface() string {
+ return InterfaceStatusNotifierItem
+}
+
+// Sender returns the signal's sender unique name.
+func (s *StatusNotifierItem_NewOverlayIconSignal) Sender() string {
+ return s.sender
+}
+
+func (s *StatusNotifierItem_NewOverlayIconSignal) path() dbus.ObjectPath {
+ return s.Path
+}
+
+func (s *StatusNotifierItem_NewOverlayIconSignal) values() []interface{} {
+ return []interface{}{}
+}
+
+// StatusNotifierItem_NewOverlayIconSignalBody is body container.
+type StatusNotifierItem_NewOverlayIconSignalBody struct {
+}
+
+// StatusNotifierItem_NewStatusSignal represents org.kde.StatusNotifierItem.NewStatus signal.
+type StatusNotifierItem_NewStatusSignal struct {
+ sender string
+ Path dbus.ObjectPath
+ Body *StatusNotifierItem_NewStatusSignalBody
+}
+
+// Name returns the signal's name.
+func (s *StatusNotifierItem_NewStatusSignal) Name() string {
+ return "NewStatus"
+}
+
+// Interface returns the signal's interface.
+func (s *StatusNotifierItem_NewStatusSignal) Interface() string {
+ return InterfaceStatusNotifierItem
+}
+
+// Sender returns the signal's sender unique name.
+func (s *StatusNotifierItem_NewStatusSignal) Sender() string {
+ return s.sender
+}
+
+func (s *StatusNotifierItem_NewStatusSignal) path() dbus.ObjectPath {
+ return s.Path
+}
+
+func (s *StatusNotifierItem_NewStatusSignal) values() []interface{} {
+ return []interface{}{s.Body.Status}
+}
+
+// StatusNotifierItem_NewStatusSignalBody is body container.
+type StatusNotifierItem_NewStatusSignalBody struct {
+ Status string
+}
+
+// StatusNotifierItem_NewIconThemePathSignal represents org.kde.StatusNotifierItem.NewIconThemePath signal.
+type StatusNotifierItem_NewIconThemePathSignal struct {
+ sender string
+ Path dbus.ObjectPath
+ Body *StatusNotifierItem_NewIconThemePathSignalBody
+}
+
+// Name returns the signal's name.
+func (s *StatusNotifierItem_NewIconThemePathSignal) Name() string {
+ return "NewIconThemePath"
+}
+
+// Interface returns the signal's interface.
+func (s *StatusNotifierItem_NewIconThemePathSignal) Interface() string {
+ return InterfaceStatusNotifierItem
+}
+
+// Sender returns the signal's sender unique name.
+func (s *StatusNotifierItem_NewIconThemePathSignal) Sender() string {
+ return s.sender
+}
+
+func (s *StatusNotifierItem_NewIconThemePathSignal) path() dbus.ObjectPath {
+ return s.Path
+}
+
+func (s *StatusNotifierItem_NewIconThemePathSignal) values() []interface{} {
+ return []interface{}{s.Body.IconThemePath}
+}
+
+// StatusNotifierItem_NewIconThemePathSignalBody is body container.
+type StatusNotifierItem_NewIconThemePathSignalBody struct {
+ IconThemePath string
+}
+
+// StatusNotifierItem_NewMenuSignal represents org.kde.StatusNotifierItem.NewMenu signal.
+type StatusNotifierItem_NewMenuSignal struct {
+ sender string
+ Path dbus.ObjectPath
+ Body *StatusNotifierItem_NewMenuSignalBody
+}
+
+// Name returns the signal's name.
+func (s *StatusNotifierItem_NewMenuSignal) Name() string {
+ return "NewMenu"
+}
+
+// Interface returns the signal's interface.
+func (s *StatusNotifierItem_NewMenuSignal) Interface() string {
+ return InterfaceStatusNotifierItem
+}
+
+// Sender returns the signal's sender unique name.
+func (s *StatusNotifierItem_NewMenuSignal) Sender() string {
+ return s.sender
+}
+
+func (s *StatusNotifierItem_NewMenuSignal) path() dbus.ObjectPath {
+ return s.Path
+}
+
+func (s *StatusNotifierItem_NewMenuSignal) values() []interface{} {
+ return []interface{}{}
+}
+
+// StatusNotifierItem_NewMenuSignalBody is body container.
+type StatusNotifierItem_NewMenuSignalBody struct {
+}
diff --git a/vendor/fyne.io/systray/systray.go b/vendor/fyne.io/systray/systray.go
new file mode 100644
index 0000000000..903cffee8e
--- /dev/null
+++ b/vendor/fyne.io/systray/systray.go
@@ -0,0 +1,240 @@
+// Package systray is a cross-platform Go library to place an icon and menu in the notification area.
+package systray
+
+import (
+ "fmt"
+ "runtime"
+ "sync"
+ "sync/atomic"
+)
+
+var (
+ systrayReady func()
+ systrayExit func()
+ menuItems = make(map[uint32]*MenuItem)
+ menuItemsLock sync.RWMutex
+
+ currentID = uint32(0)
+ quitOnce sync.Once
+)
+
+func init() {
+ runtime.LockOSThread()
+}
+
+// MenuItem is used to keep track each menu item of systray.
+// Don't create it directly, use the one systray.AddMenuItem() returned
+type MenuItem struct {
+ // ClickedCh is the channel which will be notified when the menu item is clicked
+ ClickedCh chan struct{}
+
+ // id uniquely identify a menu item, not supposed to be modified
+ id uint32
+ // title is the text shown on menu item
+ title string
+ // tooltip is the text shown when pointing to menu item
+ tooltip string
+ // disabled menu item is grayed out and has no effect when clicked
+ disabled bool
+ // checked menu item has a tick before the title
+ checked bool
+ // has the menu item a checkbox (Linux)
+ isCheckable bool
+ // parent item, for sub menus
+ parent *MenuItem
+}
+
+func (item *MenuItem) String() string {
+ if item.parent == nil {
+ return fmt.Sprintf("MenuItem[%d, %q]", item.id, item.title)
+ }
+ return fmt.Sprintf("MenuItem[%d, parent %d, %q]", item.id, item.parent.id, item.title)
+}
+
+// newMenuItem returns a populated MenuItem object
+func newMenuItem(title string, tooltip string, parent *MenuItem) *MenuItem {
+ return &MenuItem{
+ ClickedCh: make(chan struct{}),
+ id: atomic.AddUint32(¤tID, 1),
+ title: title,
+ tooltip: tooltip,
+ disabled: false,
+ checked: false,
+ isCheckable: false,
+ parent: parent,
+ }
+}
+
+// Run initializes GUI and starts the event loop, then invokes the onReady
+// callback. It blocks until systray.Quit() is called.
+func Run(onReady, onExit func()) {
+ setInternalLoop(true)
+ Register(onReady, onExit)
+
+ nativeLoop()
+}
+
+// RunWithExternalLoop allows the systemtray module to operate with other tookits.
+// The returned start and end functions should be called by the toolkit when the application has started and will end.
+func RunWithExternalLoop(onReady, onExit func()) (start, end func()) {
+ Register(onReady, onExit)
+
+ return nativeStart, nativeEnd
+}
+
+// Register initializes GUI and registers the callbacks but relies on the
+// caller to run the event loop somewhere else. It's useful if the program
+// needs to show other UI elements, for example, webview.
+// To overcome some OS weirdness, On macOS versions before Catalina, calling
+// this does exactly the same as Run().
+func Register(onReady func(), onExit func()) {
+ if onReady == nil {
+ systrayReady = func() {}
+ } else {
+ // Run onReady on separate goroutine to avoid blocking event loop
+ readyCh := make(chan interface{})
+ go func() {
+ <-readyCh
+ onReady()
+ }()
+ systrayReady = func() {
+ close(readyCh)
+ }
+ }
+ // unlike onReady, onExit runs in the event loop to make sure it has time to
+ // finish before the process terminates
+ if onExit == nil {
+ onExit = func() {}
+ }
+ systrayExit = onExit
+ registerSystray()
+}
+
+// Quit the systray
+func Quit() {
+ quitOnce.Do(quit)
+}
+
+// AddMenuItem adds a menu item with the designated title and tooltip.
+// It can be safely invoked from different goroutines.
+// Created menu items are checkable on Windows and OSX by default. For Linux you have to use AddMenuItemCheckbox
+func AddMenuItem(title string, tooltip string) *MenuItem {
+ item := newMenuItem(title, tooltip, nil)
+ item.update()
+ return item
+}
+
+// AddMenuItemCheckbox adds a menu item with the designated title and tooltip and a checkbox for Linux.
+// It can be safely invoked from different goroutines.
+// On Windows and OSX this is the same as calling AddMenuItem
+func AddMenuItemCheckbox(title string, tooltip string, checked bool) *MenuItem {
+ item := newMenuItem(title, tooltip, nil)
+ item.isCheckable = true
+ item.checked = checked
+ item.update()
+ return item
+}
+
+// AddSeparator adds a separator bar to the menu
+func AddSeparator() {
+ addSeparator(atomic.AddUint32(¤tID, 1))
+}
+
+// AddSubMenuItem adds a nested sub-menu item with the designated title and tooltip.
+// It can be safely invoked from different goroutines.
+// Created menu items are checkable on Windows and OSX by default. For Linux you have to use AddSubMenuItemCheckbox
+func (item *MenuItem) AddSubMenuItem(title string, tooltip string) *MenuItem {
+ child := newMenuItem(title, tooltip, item)
+ child.update()
+ return child
+}
+
+// AddSubMenuItemCheckbox adds a nested sub-menu item with the designated title and tooltip and a checkbox for Linux.
+// It can be safely invoked from different goroutines.
+// On Windows and OSX this is the same as calling AddSubMenuItem
+func (item *MenuItem) AddSubMenuItemCheckbox(title string, tooltip string, checked bool) *MenuItem {
+ child := newMenuItem(title, tooltip, item)
+ child.isCheckable = true
+ child.checked = checked
+ child.update()
+ return child
+}
+
+// SetTitle set the text to display on a menu item
+func (item *MenuItem) SetTitle(title string) {
+ item.title = title
+ item.update()
+}
+
+// SetTooltip set the tooltip to show when mouse hover
+func (item *MenuItem) SetTooltip(tooltip string) {
+ item.tooltip = tooltip
+ item.update()
+}
+
+// Disabled checks if the menu item is disabled
+func (item *MenuItem) Disabled() bool {
+ return item.disabled
+}
+
+// Enable a menu item regardless if it's previously enabled or not
+func (item *MenuItem) Enable() {
+ item.disabled = false
+ item.update()
+}
+
+// Disable a menu item regardless if it's previously disabled or not
+func (item *MenuItem) Disable() {
+ item.disabled = true
+ item.update()
+}
+
+// Hide hides a menu item
+func (item *MenuItem) Hide() {
+ hideMenuItem(item)
+}
+
+// Show shows a previously hidden menu item
+func (item *MenuItem) Show() {
+ showMenuItem(item)
+}
+
+// Checked returns if the menu item has a check mark
+func (item *MenuItem) Checked() bool {
+ return item.checked
+}
+
+// Check a menu item regardless if it's previously checked or not
+func (item *MenuItem) Check() {
+ item.checked = true
+ item.update()
+}
+
+// Uncheck a menu item regardless if it's previously unchecked or not
+func (item *MenuItem) Uncheck() {
+ item.checked = false
+ item.update()
+}
+
+// update propagates changes on a menu item to systray
+func (item *MenuItem) update() {
+ menuItemsLock.Lock()
+ menuItems[item.id] = item
+ menuItemsLock.Unlock()
+ addOrUpdateMenuItem(item)
+}
+
+func systrayMenuItemSelected(id uint32) {
+ menuItemsLock.RLock()
+ item, ok := menuItems[id]
+ menuItemsLock.RUnlock()
+ if !ok {
+ fmt.Printf("No menu item with ID %v", id)
+ return
+ }
+ select {
+ case item.ClickedCh <- struct{}{}:
+ // in case no one waiting for the channel
+ default:
+ }
+}
diff --git a/vendor/fyne.io/systray/systray.h b/vendor/fyne.io/systray/systray.h
new file mode 100644
index 0000000000..9a4848eb33
--- /dev/null
+++ b/vendor/fyne.io/systray/systray.h
@@ -0,0 +1,19 @@
+#include "stdbool.h"
+
+extern void systray_ready();
+extern void systray_on_exit();
+extern void systray_menu_item_selected(int menu_id);
+void registerSystray(void);
+void nativeEnd(void);
+int nativeLoop(void);
+void nativeStart(void);
+
+void setIcon(const char* iconBytes, int length, bool template);
+void setMenuItemIcon(const char* iconBytes, int length, int menuId, bool template);
+void setTitle(char* title);
+void setTooltip(char* tooltip);
+void add_or_update_menu_item(int menuId, int parentMenuId, char* title, char* tooltip, short disabled, short checked, short isCheckable);
+void add_separator(int menuId);
+void hide_menu_item(int menuId);
+void show_menu_item(int menuId);
+void quit();
diff --git a/vendor/fyne.io/systray/systray_darwin.go b/vendor/fyne.io/systray/systray_darwin.go
new file mode 100644
index 0000000000..d80f986b71
--- /dev/null
+++ b/vendor/fyne.io/systray/systray_darwin.go
@@ -0,0 +1,143 @@
+package systray
+
+/*
+#cgo darwin CFLAGS: -DDARWIN -x objective-c -fobjc-arc
+#cgo darwin LDFLAGS: -framework Cocoa
+
+#include
+#include "systray.h"
+
+void setInternalLoop(bool);
+*/
+import "C"
+
+import (
+ "unsafe"
+)
+
+// SetTemplateIcon sets the systray icon as a template icon (on Mac), falling back
+// to a regular icon on other platforms.
+// templateIconBytes and regularIconBytes should be the content of .ico for windows and
+// .ico/.jpg/.png for other platforms.
+func SetTemplateIcon(templateIconBytes []byte, regularIconBytes []byte) {
+ cstr := (*C.char)(unsafe.Pointer(&templateIconBytes[0]))
+ C.setIcon(cstr, (C.int)(len(templateIconBytes)), true)
+}
+
+// SetIcon sets the icon of a menu item. Only works on macOS and Windows.
+// iconBytes should be the content of .ico/.jpg/.png
+func (item *MenuItem) SetIcon(iconBytes []byte) {
+ cstr := (*C.char)(unsafe.Pointer(&iconBytes[0]))
+ C.setMenuItemIcon(cstr, (C.int)(len(iconBytes)), C.int(item.id), false)
+}
+
+// SetTemplateIcon sets the icon of a menu item as a template icon (on macOS). On Windows, it
+// falls back to the regular icon bytes and on Linux it does nothing.
+// templateIconBytes and regularIconBytes should be the content of .ico for windows and
+// .ico/.jpg/.png for other platforms.
+func (item *MenuItem) SetTemplateIcon(templateIconBytes []byte, regularIconBytes []byte) {
+ cstr := (*C.char)(unsafe.Pointer(&templateIconBytes[0]))
+ C.setMenuItemIcon(cstr, (C.int)(len(templateIconBytes)), C.int(item.id), true)
+}
+
+func registerSystray() {
+ C.registerSystray()
+}
+
+func nativeLoop() {
+ C.nativeLoop()
+}
+
+func nativeEnd() {
+ C.nativeEnd()
+}
+
+func nativeStart() {
+ C.nativeStart()
+}
+
+func quit() {
+ C.quit()
+}
+
+func setInternalLoop(internal bool) {
+ C.setInternalLoop(C.bool(internal))
+}
+
+// SetIcon sets the systray icon.
+// iconBytes should be the content of .ico for windows and .ico/.jpg/.png
+// for other platforms.
+func SetIcon(iconBytes []byte) {
+ cstr := (*C.char)(unsafe.Pointer(&iconBytes[0]))
+ C.setIcon(cstr, (C.int)(len(iconBytes)), false)
+}
+
+// SetTitle sets the systray title, only available on Mac and Linux.
+func SetTitle(title string) {
+ C.setTitle(C.CString(title))
+}
+
+// SetTooltip sets the systray tooltip to display on mouse hover of the tray icon,
+// only available on Mac and Windows.
+func SetTooltip(tooltip string) {
+ C.setTooltip(C.CString(tooltip))
+}
+
+func addOrUpdateMenuItem(item *MenuItem) {
+ var disabled C.short
+ if item.disabled {
+ disabled = 1
+ }
+ var checked C.short
+ if item.checked {
+ checked = 1
+ }
+ var isCheckable C.short
+ if item.isCheckable {
+ isCheckable = 1
+ }
+ var parentID uint32 = 0
+ if item.parent != nil {
+ parentID = item.parent.id
+ }
+ C.add_or_update_menu_item(
+ C.int(item.id),
+ C.int(parentID),
+ C.CString(item.title),
+ C.CString(item.tooltip),
+ disabled,
+ checked,
+ isCheckable,
+ )
+}
+
+func addSeparator(id uint32) {
+ C.add_separator(C.int(id))
+}
+
+func hideMenuItem(item *MenuItem) {
+ C.hide_menu_item(
+ C.int(item.id),
+ )
+}
+
+func showMenuItem(item *MenuItem) {
+ C.show_menu_item(
+ C.int(item.id),
+ )
+}
+
+//export systray_ready
+func systray_ready() {
+ systrayReady()
+}
+
+//export systray_on_exit
+func systray_on_exit() {
+ systrayExit()
+}
+
+//export systray_menu_item_selected
+func systray_menu_item_selected(cID C.int) {
+ systrayMenuItemSelected(uint32(cID))
+}
diff --git a/vendor/fyne.io/systray/systray_darwin.m b/vendor/fyne.io/systray/systray_darwin.m
new file mode 100644
index 0000000000..14f6a50fb0
--- /dev/null
+++ b/vendor/fyne.io/systray/systray_darwin.m
@@ -0,0 +1,317 @@
+#import
+#include "systray.h"
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+
+ #ifndef NSControlStateValueOff
+ #define NSControlStateValueOff NSOffState
+ #endif
+
+ #ifndef NSControlStateValueOn
+ #define NSControlStateValueOn NSOnState
+ #endif
+
+#endif
+
+@interface MenuItem : NSObject
+{
+ @public
+ NSNumber* menuId;
+ NSNumber* parentMenuId;
+ NSString* title;
+ NSString* tooltip;
+ short disabled;
+ short checked;
+}
+-(id) initWithId: (int)theMenuId
+withParentMenuId: (int)theParentMenuId
+ withTitle: (const char*)theTitle
+ withTooltip: (const char*)theTooltip
+ withDisabled: (short)theDisabled
+ withChecked: (short)theChecked;
+ @end
+ @implementation MenuItem
+ -(id) initWithId: (int)theMenuId
+ withParentMenuId: (int)theParentMenuId
+ withTitle: (const char*)theTitle
+ withTooltip: (const char*)theTooltip
+ withDisabled: (short)theDisabled
+ withChecked: (short)theChecked
+{
+ menuId = [NSNumber numberWithInt:theMenuId];
+ parentMenuId = [NSNumber numberWithInt:theParentMenuId];
+ title = [[NSString alloc] initWithCString:theTitle
+ encoding:NSUTF8StringEncoding];
+ tooltip = [[NSString alloc] initWithCString:theTooltip
+ encoding:NSUTF8StringEncoding];
+ disabled = theDisabled;
+ checked = theChecked;
+ return self;
+}
+@end
+
+@interface AppDelegate: NSObject
+ - (void) add_or_update_menu_item:(MenuItem*) item;
+ - (IBAction)menuHandler:(id)sender;
+ @property (assign) IBOutlet NSWindow *window;
+ @end
+
+ @implementation AppDelegate
+{
+ NSStatusItem *statusItem;
+ NSMenu *menu;
+ NSCondition* cond;
+}
+
+@synthesize window = _window;
+
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
+{
+ self->statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
+ self->menu = [[NSMenu alloc] init];
+ [self->menu setAutoenablesItems: FALSE];
+ [self->statusItem setMenu:self->menu];
+ systray_ready();
+}
+
+- (void)applicationWillTerminate:(NSNotification *)aNotification
+{
+ systray_on_exit();
+}
+
+- (void)setIcon:(NSImage *)image {
+ statusItem.button.image = image;
+ [self updateTitleButtonStyle];
+}
+
+- (void)setTitle:(NSString *)title {
+ statusItem.button.title = title;
+ [self updateTitleButtonStyle];
+}
+
+-(void)updateTitleButtonStyle {
+ if (statusItem.button.image != nil) {
+ if ([statusItem.button.title length] == 0) {
+ statusItem.button.imagePosition = NSImageOnly;
+ } else {
+ statusItem.button.imagePosition = NSImageLeft;
+ }
+ } else {
+ statusItem.button.imagePosition = NSNoImage;
+ }
+}
+
+
+- (void)setTooltip:(NSString *)tooltip {
+ statusItem.button.toolTip = tooltip;
+}
+
+- (IBAction)menuHandler:(id)sender
+{
+ NSNumber* menuId = [sender representedObject];
+ systray_menu_item_selected(menuId.intValue);
+}
+
+- (void)add_or_update_menu_item:(MenuItem *)item {
+ NSMenu *theMenu = self->menu;
+ NSMenuItem *parentItem;
+ if ([item->parentMenuId integerValue] > 0) {
+ parentItem = find_menu_item(menu, item->parentMenuId);
+ if (parentItem.hasSubmenu) {
+ theMenu = parentItem.submenu;
+ } else {
+ theMenu = [[NSMenu alloc] init];
+ [theMenu setAutoenablesItems:NO];
+ [parentItem setSubmenu:theMenu];
+ }
+ }
+
+ NSMenuItem *menuItem;
+ menuItem = find_menu_item(theMenu, item->menuId);
+ if (menuItem == NULL) {
+ menuItem = [theMenu addItemWithTitle:item->title
+ action:@selector(menuHandler:)
+ keyEquivalent:@""];
+ [menuItem setRepresentedObject:item->menuId];
+ }
+ [menuItem setTitle:item->title];
+ [menuItem setTag:[item->menuId integerValue]];
+ [menuItem setTarget:self];
+ [menuItem setToolTip:item->tooltip];
+ if (item->disabled == 1) {
+ menuItem.enabled = FALSE;
+ } else {
+ menuItem.enabled = TRUE;
+ }
+ if (item->checked == 1) {
+ menuItem.state = NSControlStateValueOn;
+ } else {
+ menuItem.state = NSControlStateValueOff;
+ }
+}
+
+NSMenuItem *find_menu_item(NSMenu *ourMenu, NSNumber *menuId) {
+ NSMenuItem *foundItem = [ourMenu itemWithTag:[menuId integerValue]];
+ if (foundItem != NULL) {
+ return foundItem;
+ }
+ NSArray *menu_items = ourMenu.itemArray;
+ int i;
+ for (i = 0; i < [menu_items count]; i++) {
+ NSMenuItem *i_item = [menu_items objectAtIndex:i];
+ if (i_item.hasSubmenu) {
+ foundItem = find_menu_item(i_item.submenu, menuId);
+ if (foundItem != NULL) {
+ return foundItem;
+ }
+ }
+ }
+
+ return NULL;
+};
+
+- (void) add_separator:(NSNumber*) menuId
+{
+ [menu addItem: [NSMenuItem separatorItem]];
+}
+
+- (void) hide_menu_item:(NSNumber*) menuId
+{
+ NSMenuItem* menuItem = find_menu_item(menu, menuId);
+ if (menuItem != NULL) {
+ [menuItem setHidden:TRUE];
+ }
+}
+
+- (void) setMenuItemIcon:(NSArray*)imageAndMenuId {
+ NSImage* image = [imageAndMenuId objectAtIndex:0];
+ NSNumber* menuId = [imageAndMenuId objectAtIndex:1];
+
+ NSMenuItem* menuItem;
+ menuItem = find_menu_item(menu, menuId);
+ if (menuItem == NULL) {
+ return;
+ }
+ menuItem.image = image;
+}
+
+- (void) show_menu_item:(NSNumber*) menuId
+{
+ NSMenuItem* menuItem = find_menu_item(menu, menuId);
+ if (menuItem != NULL) {
+ [menuItem setHidden:FALSE];
+ }
+}
+
+- (void) quit
+{
+ [NSApp terminate:self];
+}
+
+@end
+
+bool internalLoop = false;
+AppDelegate *owner;
+
+void setInternalLoop(bool i) {
+ internalLoop = i;
+}
+
+void registerSystray(void) {
+ if (!internalLoop) { // with an external loop we don't take ownership of the app
+ return;
+ }
+
+ owner = [[AppDelegate alloc] init];
+ [[NSApplication sharedApplication] setDelegate:owner];
+
+ // A workaround to avoid crashing on macOS versions before Catalina. Somehow
+ // SIGSEGV would happen inside AppKit if [NSApp run] is called from a
+ // different function, even if that function is called right after this.
+ if (floor(NSAppKitVersionNumber) <= /*NSAppKitVersionNumber10_14*/ 1671){
+ [NSApp run];
+ }
+}
+
+void nativeEnd(void) {
+ systray_on_exit();
+}
+
+int nativeLoop(void) {
+ if (floor(NSAppKitVersionNumber) > /*NSAppKitVersionNumber10_14*/ 1671){
+ [NSApp run];
+ }
+ return EXIT_SUCCESS;
+}
+
+void nativeStart(void) {
+ owner = [[AppDelegate alloc] init];
+
+ NSNotification *launched = [NSNotification notificationWithName:NSApplicationDidFinishLaunchingNotification
+ object:[NSApplication sharedApplication]];
+ [owner applicationDidFinishLaunching:launched];
+}
+
+void runInMainThread(SEL method, id object) {
+ [owner
+ performSelectorOnMainThread:method
+ withObject:object
+ waitUntilDone: YES];
+}
+
+void setIcon(const char* iconBytes, int length, bool template) {
+ NSData* buffer = [NSData dataWithBytes: iconBytes length:length];
+ NSImage *image = [[NSImage alloc] initWithData:buffer];
+ [image setSize:NSMakeSize(16, 16)];
+ image.template = template;
+ runInMainThread(@selector(setIcon:), (id)image);
+}
+
+void setMenuItemIcon(const char* iconBytes, int length, int menuId, bool template) {
+ NSData* buffer = [NSData dataWithBytes: iconBytes length:length];
+ NSImage *image = [[NSImage alloc] initWithData:buffer];
+ [image setSize:NSMakeSize(16, 16)];
+ image.template = template;
+ NSNumber *mId = [NSNumber numberWithInt:menuId];
+ runInMainThread(@selector(setMenuItemIcon:), @[image, (id)mId]);
+}
+
+void setTitle(char* ctitle) {
+ NSString* title = [[NSString alloc] initWithCString:ctitle
+ encoding:NSUTF8StringEncoding];
+ free(ctitle);
+ runInMainThread(@selector(setTitle:), (id)title);
+}
+
+void setTooltip(char* ctooltip) {
+ NSString* tooltip = [[NSString alloc] initWithCString:ctooltip
+ encoding:NSUTF8StringEncoding];
+ free(ctooltip);
+ runInMainThread(@selector(setTooltip:), (id)tooltip);
+}
+
+void add_or_update_menu_item(int menuId, int parentMenuId, char* title, char* tooltip, short disabled, short checked, short isCheckable) {
+ MenuItem* item = [[MenuItem alloc] initWithId: menuId withParentMenuId: parentMenuId withTitle: title withTooltip: tooltip withDisabled: disabled withChecked: checked];
+ free(title);
+ free(tooltip);
+ runInMainThread(@selector(add_or_update_menu_item:), (id)item);
+}
+
+void add_separator(int menuId) {
+ NSNumber *mId = [NSNumber numberWithInt:menuId];
+ runInMainThread(@selector(add_separator:), (id)mId);
+}
+
+void hide_menu_item(int menuId) {
+ NSNumber *mId = [NSNumber numberWithInt:menuId];
+ runInMainThread(@selector(hide_menu_item:), (id)mId);
+}
+
+void show_menu_item(int menuId) {
+ NSNumber *mId = [NSNumber numberWithInt:menuId];
+ runInMainThread(@selector(show_menu_item:), (id)mId);
+}
+
+void quit() {
+ runInMainThread(@selector(quit), nil);
+}
diff --git a/vendor/fyne.io/systray/systray_linux.go b/vendor/fyne.io/systray/systray_linux.go
new file mode 100644
index 0000000000..bb1a391741
--- /dev/null
+++ b/vendor/fyne.io/systray/systray_linux.go
@@ -0,0 +1,296 @@
+//Note that you need to have github.com/knightpp/dbus-codegen-go installed from "custom" branch
+//go:generate dbus-codegen-go -prefix org.kde -package notifier -output internal/generated/notifier/status_notifier_item.go internal/StatusNotifierItem.xml
+//go:generate dbus-codegen-go -prefix com.canonical -package menu -output internal/generated/menu/dbus_menu.go internal/DbusMenu.xml
+
+package systray
+
+import (
+ "bytes"
+ "fmt"
+ "image"
+ _ "image/png"
+ "log"
+ "os"
+
+ "github.com/godbus/dbus/v5"
+ "github.com/godbus/dbus/v5/introspect"
+ "github.com/godbus/dbus/v5/prop"
+
+ "fyne.io/systray/internal/generated/menu"
+ "fyne.io/systray/internal/generated/notifier"
+)
+
+const (
+ path = "/StatusNotifierItem"
+ menuPath = "/StatusNotifierMenu"
+)
+
+var (
+ // to signal quitting the internal main loop
+ quitChan = make(chan struct{})
+
+ // icon data for the main systray icon
+ iconData []byte
+
+ // the title of our system tray icon
+ title string
+
+ // instance is the current instance of our DBus tray server
+ instance *tray
+)
+
+// SetTemplateIcon sets the systray icon as a template icon (on macOS), falling back
+// to a regular icon on other platforms.
+// templateIconBytes and iconBytes should be the content of .ico for windows and
+// .ico/.jpg/.png for other platforms.
+func SetTemplateIcon(templateIconBytes []byte, regularIconBytes []byte) {
+ // TODO handle the templateIconBytes?
+ SetIcon(regularIconBytes)
+}
+
+// SetIcon sets the systray icon.
+// iconBytes should be the content of .ico for windows and .ico/.jpg/.png
+// for other platforms.
+func SetIcon(iconBytes []byte) {
+ iconData = iconBytes
+
+ if instance != nil && instance.props != nil {
+ instance.props["org.kde.StatusNotifierItem"]["IconPixmap"].Value = []PX{convertToPixels(iconData)}
+
+ if instance.conn != nil {
+ notifier.Emit(instance.conn, ¬ifier.StatusNotifierItem_NewIconSignal{
+ Path: path,
+ Body: ¬ifier.StatusNotifierItem_NewIconSignalBody{},
+ })
+ }
+ }
+}
+
+// SetTitle sets the systray title, only available on Mac and Linux.
+func SetTitle(t string) {
+ title = t
+}
+
+// SetTooltip sets the systray tooltip to display on mouse hover of the tray icon,
+// only available on Mac and Windows.
+func SetTooltip(tooltip string) {
+}
+
+// SetTemplateIcon sets the icon of a menu item as a template icon (on macOS). On Windows, it
+// falls back to the regular icon bytes and on Linux it does nothing.
+// templateIconBytes and regularIconBytes should be the content of .ico for windows and
+// .ico/.jpg/.png for other platforms.
+func (item *MenuItem) SetTemplateIcon(templateIconBytes []byte, regularIconBytes []byte) {
+}
+
+func setInternalLoop(_ bool) {
+ // nothing to action on Linux
+}
+
+func registerSystray() {
+}
+
+func nativeLoop() int {
+ nativeStart()
+ select {
+ case <-quitChan:
+ break
+ }
+ nativeEnd()
+ return 0
+}
+
+func nativeEnd() {
+ systrayExit()
+ instance.conn.Close()
+}
+
+func quit() {
+ close(quitChan)
+}
+
+func nativeStart() {
+ systrayReady()
+ instance = &tray{menu: &menuLayout{}}
+
+ conn, _ := dbus.ConnectSessionBus()
+ instance.conn = conn
+ notifier.ExportStatusNotifierItem(conn, path, instance)
+ menu.ExportDbusmenu(conn, menuPath, instance)
+
+ name := fmt.Sprintf("org.kde.StatusNotifierItem-%d-1", os.Getpid()) // register id 1 for this process
+ _, err := conn.RequestName(name, dbus.NameFlagDoNotQueue)
+ if err != nil {
+ // fall back to existing name
+ name = conn.Names()[0]
+ }
+
+ _, err = prop.Export(conn, path, createPropSpec())
+ if err != nil {
+ log.Printf("Failed to export notifier item properties to bus")
+ return
+ }
+ _, err = prop.Export(conn, menuPath, createMenuPropSpec())
+ if err != nil {
+ log.Printf("Failed to export notifier menu properties to bus")
+ return
+ }
+
+ node := introspect.Node{
+ Name: path,
+ Interfaces: []introspect.Interface{
+ introspect.IntrospectData,
+ prop.IntrospectData,
+ notifier.IntrospectDataStatusNotifierItem,
+ },
+ }
+ err = conn.Export(introspect.NewIntrospectable(&node), path,
+ "org.freedesktop.DBus.Introspectable")
+ if err != nil {
+ log.Printf("Failed to export introspection")
+ return
+ }
+ menuNode := introspect.Node{
+ Name: menuPath,
+ Interfaces: []introspect.Interface{
+ introspect.IntrospectData,
+ prop.IntrospectData,
+ menu.IntrospectDataDbusmenu,
+ },
+ }
+ err = conn.Export(introspect.NewIntrospectable(&menuNode), menuPath,
+ "org.freedesktop.DBus.Introspectable")
+ if err != nil {
+ log.Printf("Failed to export introspection")
+ return
+ }
+
+ obj := conn.Object("org.kde.StatusNotifierWatcher", "/StatusNotifierWatcher")
+ call := obj.Call("org.kde.StatusNotifierWatcher.RegisterStatusNotifierItem", 0, path)
+ if call.Err != nil {
+ log.Printf("Failed to register our icon with the notifier watcher, maybe no tray running?")
+ }
+}
+
+// tray is a basic type that handles the dbus functionality
+type tray struct {
+ conn *dbus.Conn
+ menu *menuLayout
+ props map[string]map[string]*prop.Prop
+}
+
+// ContextMenu method is called when the user has right-clicked on our icon.
+func (t *tray) ContextMenu(x, y int32) *dbus.Error {
+ // not supported for systray lib
+ return nil
+}
+
+// Activate requests that we perform the primary action, such as showing a menu.
+func (t *tray) Activate(x, y int32) *dbus.Error {
+ // TODO show menu, or have it handled in the dbus?
+ return nil
+}
+
+// SecondaryActivate is alternative non-context click, such as middle mouse button.
+func (t *tray) SecondaryActivate(x, y int32) *dbus.Error {
+ return nil
+}
+
+// Scroll is called when the mouse wheel scrolls over the icon.
+func (t *tray) Scroll(delta int32, orient string) *dbus.Error {
+ return nil
+}
+
+type PX struct {
+ W, H int
+ Pix []byte
+}
+
+func convertToPixels(data []byte) PX {
+ if len(iconData) == 0 {
+ return PX{}
+ }
+
+ img, _, err := image.Decode(bytes.NewReader(iconData))
+ if err != nil {
+ log.Printf("Failed to read icon format %v", err)
+ return PX{}
+ }
+
+ return PX{
+ img.Bounds().Dx(), img.Bounds().Dy(),
+ argbForImage(img),
+ }
+}
+
+func argbForImage(img image.Image) []byte {
+ w, h := img.Bounds().Dx(), img.Bounds().Dy()
+ data := make([]byte, w*h*4)
+ i := 0
+ for y := 0; y < h; y++ {
+ for x := 0; x < w; x++ {
+ r, g, b, a := img.At(x, y).RGBA()
+ data[i] = byte(a)
+ data[i+1] = byte(r)
+ data[i+2] = byte(g)
+ data[i+3] = byte(b)
+ i += 4
+ }
+ }
+ return data
+}
+
+func createPropSpec() map[string]map[string]*prop.Prop {
+ instance.props = map[string]map[string]*prop.Prop{
+ "org.kde.StatusNotifierItem": {
+ "Status": {
+ "Active", // Passive, Active or NeedsAttention
+ false,
+ prop.EmitTrue,
+ nil,
+ },
+ "Title": {
+ title,
+ false,
+ prop.EmitTrue,
+ nil,
+ },
+ "Id": {
+ "1",
+ false,
+ prop.EmitTrue,
+ nil,
+ },
+ "Category": {
+ "ApplicationStatus",
+ false,
+ prop.EmitTrue,
+ nil,
+ },
+ "IconName": {
+ "",
+ false,
+ prop.EmitTrue,
+ nil,
+ },
+ "IconPixmap": {
+ []PX{convertToPixels(iconData)},
+ false,
+ prop.EmitTrue,
+ nil,
+ },
+ "IconThemePath": {
+ "",
+ false,
+ prop.EmitTrue,
+ nil,
+ },
+ "Menu": {
+ menuPath,
+ false,
+ prop.EmitTrue,
+ nil,
+ },
+ }}
+ return instance.props
+}
diff --git a/vendor/fyne.io/systray/systray_menu_linux.go b/vendor/fyne.io/systray/systray_menu_linux.go
new file mode 100644
index 0000000000..727ef23d25
--- /dev/null
+++ b/vendor/fyne.io/systray/systray_menu_linux.go
@@ -0,0 +1,207 @@
+package systray
+
+import (
+ "log"
+
+ "github.com/godbus/dbus/v5"
+ "github.com/godbus/dbus/v5/prop"
+
+ "fyne.io/systray/internal/generated/menu"
+)
+
+// SetIcon sets the icon of a menu item. Only works on macOS and Windows.
+// iconBytes should be the content of .ico/.jpg/.png
+func (item *MenuItem) SetIcon(iconBytes []byte) {
+}
+
+func (t *tray) GetLayout(parentId int32, recursionDepth int32, propertyNames []string) (revision uint32, layout menuLayout, err *dbus.Error) {
+ return 1, *instance.menu, nil
+}
+
+// GetGroupProperties is com.canonical.dbusmenu.GetGroupProperties method.
+func (t *tray) GetGroupProperties(ids []int32, propertyNames []string) (properties []struct {
+ V0 int32
+ V1 map[string]dbus.Variant
+}, err *dbus.Error) {
+ return
+}
+
+// GetProperty is com.canonical.dbusmenu.GetProperty method.
+func (t *tray) GetProperty(id int32, name string) (value dbus.Variant, err *dbus.Error) {
+ return
+}
+
+// Event is com.canonical.dbusmenu.Event method.
+func (t *tray) Event(id int32, eventId string, data dbus.Variant, timestamp uint32) (err *dbus.Error) {
+ if eventId == "clicked" {
+ item, ok := menuItems[uint32(id)]
+ if !ok {
+ log.Printf("Failed to look up clicked menu item")
+ return
+ }
+
+ item.ClickedCh <- struct{}{}
+ }
+ return
+}
+
+// EventGroup is com.canonical.dbusmenu.EventGroup method.
+func (t *tray) EventGroup(events []struct {
+ V0 int32
+ V1 string
+ V2 dbus.Variant
+ V3 uint32
+}) (idErrors []int32, err *dbus.Error) {
+ return
+}
+
+// AboutToShow is com.canonical.dbusmenu.AboutToShow method.
+func (t *tray) AboutToShow(id int32) (needUpdate bool, err *dbus.Error) {
+ return
+}
+
+// AboutToShowGroup is com.canonical.dbusmenu.AboutToShowGroup method.
+func (t *tray) AboutToShowGroup(ids []int32) (updatesNeeded []int32, idErrors []int32, err *dbus.Error) {
+ return
+}
+
+func createMenuPropSpec() map[string]map[string]*prop.Prop {
+ return map[string]map[string]*prop.Prop{
+ "com.canonical.dbusmenu": {
+ "Version": {
+ uint32(1),
+ false,
+ prop.EmitTrue,
+ nil,
+ },
+ "TextDirection": {
+ "ltr",
+ false,
+ prop.EmitTrue,
+ nil,
+ },
+ "Status": {
+ "active",
+ false,
+ prop.EmitTrue,
+ nil,
+ },
+ "IconThemePath": {
+ []string{},
+ false,
+ prop.EmitTrue,
+ nil,
+ },
+ },
+ }
+}
+
+// menuLayout is a named struct to map into generated bindings. It represents the layout of a menu item
+type menuLayout = struct {
+ V0 int32 // the unique ID of this item
+ V1 map[string]dbus.Variant // properties for this menu item layout
+ V2 []dbus.Variant // child menu item layouts
+}
+
+func addOrUpdateMenuItem(item *MenuItem) {
+ var layout *menuLayout
+ m, exists := findLayout(int32(item.id))
+ if exists {
+ layout = m
+ } else {
+ layout = &menuLayout{
+ V0: int32(item.id),
+ V1: map[string]dbus.Variant{},
+ V2: []dbus.Variant{},
+ }
+
+ parent := instance.menu
+ if item.parent != nil {
+ m, ok := findLayout(int32(item.parent.id))
+ if ok {
+ parent = m
+ parent.V1["children-display"] = dbus.MakeVariant("submenu")
+ }
+ }
+ parent.V2 = append(parent.V2, dbus.MakeVariant(layout))
+ }
+
+ applyItemToLayout(item, layout)
+ if exists {
+ refresh()
+ }
+}
+
+func addSeparator(id uint32) {
+ layout := &menuLayout{
+ V0: int32(id),
+ V1: map[string]dbus.Variant{
+ "type": dbus.MakeVariant("separator"),
+ },
+ V2: []dbus.Variant{},
+ }
+
+ instance.menu.V2 = append(instance.menu.V2, dbus.MakeVariant(layout))
+}
+
+func applyItemToLayout(in *MenuItem, out *menuLayout) {
+ out.V1["enabled"] = dbus.MakeVariant(!in.disabled)
+ out.V1["label"] = dbus.MakeVariant(in.title)
+
+ if in.isCheckable {
+ out.V1["toggle-type"] = dbus.MakeVariant("checkmark")
+ if in.checked {
+ out.V1["toggle-state"] = dbus.MakeVariant(1)
+ } else {
+ out.V1["toggle-state"] = dbus.MakeVariant(0)
+ }
+ } else {
+ out.V1["toggle-type"] = dbus.MakeVariant("")
+ out.V1["toggle-state"] = dbus.MakeVariant(0)
+ }
+}
+
+func findLayout(id int32) (*menuLayout, bool) {
+ return findSubLayout(id, instance.menu.V2)
+}
+
+func findSubLayout(id int32, vals []dbus.Variant) (*menuLayout, bool) {
+ for _, i := range vals {
+ item := i.Value().(*menuLayout)
+ if item.V0 == id {
+ return item, true
+ }
+
+ if len(item.V2) > 0 {
+ child, ok := findSubLayout(id, item.V2)
+ if ok {
+ return child, true
+ }
+ }
+ }
+
+ return nil, false
+}
+
+func hideMenuItem(item *MenuItem) {
+ m, exists := findLayout(int32(item.id))
+ if exists {
+ m.V1["visible"] = dbus.MakeVariant(false)
+ refresh()
+ }
+}
+
+func showMenuItem(item *MenuItem) {
+ m, exists := findLayout(int32(item.id))
+ if exists {
+ m.V1["visible"] = dbus.MakeVariant(true)
+ refresh()
+ }
+}
+
+func refresh() {
+ menu.Emit(instance.conn, &menu.Dbusmenu_LayoutUpdatedSignal{
+ Path: menuPath,
+ Body: &menu.Dbusmenu_LayoutUpdatedSignalBody{},
+ })
+}
diff --git a/vendor/fyne.io/systray/systray_windows.go b/vendor/fyne.io/systray/systray_windows.go
new file mode 100644
index 0000000000..ac3d722258
--- /dev/null
+++ b/vendor/fyne.io/systray/systray_windows.go
@@ -0,0 +1,957 @@
+// +build windows
+
+package systray
+
+import (
+ "crypto/md5"
+ "encoding/hex"
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+ "sort"
+ "sync"
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
+
+// Helpful sources: https://github.com/golang/exp/blob/master/shiny/driver/internal/win32
+
+var (
+ g32 = windows.NewLazySystemDLL("Gdi32.dll")
+ pCreateCompatibleBitmap = g32.NewProc("CreateCompatibleBitmap")
+ pCreateCompatibleDC = g32.NewProc("CreateCompatibleDC")
+ pDeleteDC = g32.NewProc("DeleteDC")
+ pSelectObject = g32.NewProc("SelectObject")
+
+ k32 = windows.NewLazySystemDLL("Kernel32.dll")
+ pGetModuleHandle = k32.NewProc("GetModuleHandleW")
+
+ s32 = windows.NewLazySystemDLL("Shell32.dll")
+ pShellNotifyIcon = s32.NewProc("Shell_NotifyIconW")
+
+ u32 = windows.NewLazySystemDLL("User32.dll")
+ pCreateMenu = u32.NewProc("CreateMenu")
+ pCreatePopupMenu = u32.NewProc("CreatePopupMenu")
+ pCreateWindowEx = u32.NewProc("CreateWindowExW")
+ pDefWindowProc = u32.NewProc("DefWindowProcW")
+ pDeleteMenu = u32.NewProc("DeleteMenu")
+ pDestroyWindow = u32.NewProc("DestroyWindow")
+ pDispatchMessage = u32.NewProc("DispatchMessageW")
+ pDrawIconEx = u32.NewProc("DrawIconEx")
+ pGetCursorPos = u32.NewProc("GetCursorPos")
+ pGetDC = u32.NewProc("GetDC")
+ pGetMenuItemID = u32.NewProc("GetMenuItemID")
+ pGetMessage = u32.NewProc("GetMessageW")
+ pGetSystemMetrics = u32.NewProc("GetSystemMetrics")
+ pInsertMenuItem = u32.NewProc("InsertMenuItemW")
+ pLoadCursor = u32.NewProc("LoadCursorW")
+ pLoadIcon = u32.NewProc("LoadIconW")
+ pLoadImage = u32.NewProc("LoadImageW")
+ pPostMessage = u32.NewProc("PostMessageW")
+ pPostQuitMessage = u32.NewProc("PostQuitMessage")
+ pRegisterClass = u32.NewProc("RegisterClassExW")
+ pRegisterWindowMessage = u32.NewProc("RegisterWindowMessageW")
+ pReleaseDC = u32.NewProc("ReleaseDC")
+ pSetForegroundWindow = u32.NewProc("SetForegroundWindow")
+ pSetMenuInfo = u32.NewProc("SetMenuInfo")
+ pSetMenuItemInfo = u32.NewProc("SetMenuItemInfoW")
+ pShowWindow = u32.NewProc("ShowWindow")
+ pTrackPopupMenu = u32.NewProc("TrackPopupMenu")
+ pTranslateMessage = u32.NewProc("TranslateMessage")
+ pUnregisterClass = u32.NewProc("UnregisterClassW")
+ pUpdateWindow = u32.NewProc("UpdateWindow")
+)
+
+// Contains window class information.
+// It is used with the RegisterClassEx and GetClassInfoEx functions.
+// https://msdn.microsoft.com/en-us/library/ms633577.aspx
+type wndClassEx struct {
+ Size, Style uint32
+ WndProc uintptr
+ ClsExtra, WndExtra int32
+ Instance, Icon, Cursor, Background windows.Handle
+ MenuName, ClassName *uint16
+ IconSm windows.Handle
+}
+
+// Registers a window class for subsequent use in calls to the CreateWindow or CreateWindowEx function.
+// https://msdn.microsoft.com/en-us/library/ms633587.aspx
+func (w *wndClassEx) register() error {
+ w.Size = uint32(unsafe.Sizeof(*w))
+ res, _, err := pRegisterClass.Call(uintptr(unsafe.Pointer(w)))
+ if res == 0 {
+ return err
+ }
+ return nil
+}
+
+// Unregisters a window class, freeing the memory required for the class.
+// https://msdn.microsoft.com/en-us/library/ms644899.aspx
+func (w *wndClassEx) unregister() error {
+ res, _, err := pUnregisterClass.Call(
+ uintptr(unsafe.Pointer(w.ClassName)),
+ uintptr(w.Instance),
+ )
+ if res == 0 {
+ return err
+ }
+ return nil
+}
+
+// Contains information that the system needs to display notifications in the notification area.
+// Used by Shell_NotifyIcon.
+// https://msdn.microsoft.com/en-us/library/windows/desktop/bb773352(v=vs.85).aspx
+// https://msdn.microsoft.com/en-us/library/windows/desktop/bb762159
+type notifyIconData struct {
+ Size uint32
+ Wnd windows.Handle
+ ID, Flags, CallbackMessage uint32
+ Icon windows.Handle
+ Tip [128]uint16
+ State, StateMask uint32
+ Info [256]uint16
+ Timeout, Version uint32
+ InfoTitle [64]uint16
+ InfoFlags uint32
+ GuidItem windows.GUID
+ BalloonIcon windows.Handle
+}
+
+func (nid *notifyIconData) add() error {
+ const NIM_ADD = 0x00000000
+ res, _, err := pShellNotifyIcon.Call(
+ uintptr(NIM_ADD),
+ uintptr(unsafe.Pointer(nid)),
+ )
+ if res == 0 {
+ return err
+ }
+ return nil
+}
+
+func (nid *notifyIconData) modify() error {
+ const NIM_MODIFY = 0x00000001
+ res, _, err := pShellNotifyIcon.Call(
+ uintptr(NIM_MODIFY),
+ uintptr(unsafe.Pointer(nid)),
+ )
+ if res == 0 {
+ return err
+ }
+ return nil
+}
+
+func (nid *notifyIconData) delete() error {
+ const NIM_DELETE = 0x00000002
+ res, _, err := pShellNotifyIcon.Call(
+ uintptr(NIM_DELETE),
+ uintptr(unsafe.Pointer(nid)),
+ )
+ if res == 0 {
+ return err
+ }
+ return nil
+}
+
+// Contains information about a menu item.
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ms647578(v=vs.85).aspx
+type menuItemInfo struct {
+ Size, Mask, Type, State uint32
+ ID uint32
+ SubMenu, Checked, Unchecked windows.Handle
+ ItemData uintptr
+ TypeData *uint16
+ Cch uint32
+ BMPItem windows.Handle
+}
+
+// The POINT structure defines the x- and y- coordinates of a point.
+// https://msdn.microsoft.com/en-us/library/windows/desktop/dd162805(v=vs.85).aspx
+type point struct {
+ X, Y int32
+}
+
+// Contains information about loaded resources
+type winTray struct {
+ instance,
+ icon,
+ cursor,
+ window windows.Handle
+
+ loadedImages map[string]windows.Handle
+ muLoadedImages sync.RWMutex
+ // menus keeps track of the submenus keyed by the menu item ID, plus 0
+ // which corresponds to the main popup menu.
+ menus map[uint32]windows.Handle
+ muMenus sync.RWMutex
+ // menuOf keeps track of the menu each menu item belongs to.
+ menuOf map[uint32]windows.Handle
+ muMenuOf sync.RWMutex
+ // menuItemIcons maintains the bitmap of each menu item (if applies). It's
+ // needed to show the icon correctly when showing a previously hidden menu
+ // item again.
+ menuItemIcons map[uint32]windows.Handle
+ muMenuItemIcons sync.RWMutex
+ visibleItems map[uint32][]uint32
+ muVisibleItems sync.RWMutex
+
+ nid *notifyIconData
+ muNID sync.RWMutex
+ wcex *wndClassEx
+
+ wmSystrayMessage,
+ wmTaskbarCreated uint32
+}
+
+// Loads an image from file and shows it in tray.
+// Shell_NotifyIcon: https://msdn.microsoft.com/en-us/library/windows/desktop/bb762159(v=vs.85).aspx
+func (t *winTray) setIcon(src string) error {
+ const NIF_ICON = 0x00000002
+
+ h, err := t.loadIconFrom(src)
+ if err != nil {
+ return err
+ }
+
+ t.muNID.Lock()
+ defer t.muNID.Unlock()
+ t.nid.Icon = h
+ t.nid.Flags |= NIF_ICON
+ t.nid.Size = uint32(unsafe.Sizeof(*t.nid))
+
+ return t.nid.modify()
+}
+
+// Sets tooltip on icon.
+// Shell_NotifyIcon: https://msdn.microsoft.com/en-us/library/windows/desktop/bb762159(v=vs.85).aspx
+func (t *winTray) setTooltip(src string) error {
+ const NIF_TIP = 0x00000004
+ b, err := windows.UTF16FromString(src)
+ if err != nil {
+ return err
+ }
+
+ t.muNID.Lock()
+ defer t.muNID.Unlock()
+ copy(t.nid.Tip[:], b[:])
+ t.nid.Flags |= NIF_TIP
+ t.nid.Size = uint32(unsafe.Sizeof(*t.nid))
+
+ return t.nid.modify()
+}
+
+var wt winTray
+
+// WindowProc callback function that processes messages sent to a window.
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ms633573(v=vs.85).aspx
+func (t *winTray) wndProc(hWnd windows.Handle, message uint32, wParam, lParam uintptr) (lResult uintptr) {
+ const (
+ WM_RBUTTONUP = 0x0205
+ WM_LBUTTONUP = 0x0202
+ WM_COMMAND = 0x0111
+ WM_ENDSESSION = 0x0016
+ WM_CLOSE = 0x0010
+ WM_DESTROY = 0x0002
+ WM_CREATE = 0x0001
+ )
+ switch message {
+ case WM_CREATE:
+ systrayReady()
+ case WM_COMMAND:
+ menuItemId := int32(wParam)
+ // https://docs.microsoft.com/en-us/windows/win32/menurc/wm-command#menus
+ if menuItemId != -1 {
+ systrayMenuItemSelected(uint32(wParam))
+ }
+ case WM_CLOSE:
+ pDestroyWindow.Call(uintptr(t.window))
+ t.wcex.unregister()
+ case WM_DESTROY:
+ // same as WM_ENDSESSION, but throws 0 exit code after all
+ defer pPostQuitMessage.Call(uintptr(int32(0)))
+ fallthrough
+ case WM_ENDSESSION:
+ t.muNID.Lock()
+ if t.nid != nil {
+ t.nid.delete()
+ }
+ t.muNID.Unlock()
+ systrayExit()
+ case t.wmSystrayMessage:
+ switch lParam {
+ case WM_RBUTTONUP, WM_LBUTTONUP:
+ t.showMenu()
+ }
+ case t.wmTaskbarCreated: // on explorer.exe restarts
+ t.muNID.Lock()
+ t.nid.add()
+ t.muNID.Unlock()
+ default:
+ // Calls the default window procedure to provide default processing for any window messages that an application does not process.
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms633572(v=vs.85).aspx
+ lResult, _, _ = pDefWindowProc.Call(
+ uintptr(hWnd),
+ uintptr(message),
+ uintptr(wParam),
+ uintptr(lParam),
+ )
+ }
+ return
+}
+
+func (t *winTray) initInstance() error {
+ const IDI_APPLICATION = 32512
+ const IDC_ARROW = 32512 // Standard arrow
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms633548(v=vs.85).aspx
+ const SW_HIDE = 0
+ const CW_USEDEFAULT = 0x80000000
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms632600(v=vs.85).aspx
+ const (
+ WS_CAPTION = 0x00C00000
+ WS_MAXIMIZEBOX = 0x00010000
+ WS_MINIMIZEBOX = 0x00020000
+ WS_OVERLAPPED = 0x00000000
+ WS_SYSMENU = 0x00080000
+ WS_THICKFRAME = 0x00040000
+
+ WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
+ )
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ff729176
+ const (
+ CS_HREDRAW = 0x0002
+ CS_VREDRAW = 0x0001
+ )
+ const NIF_MESSAGE = 0x00000001
+
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644931(v=vs.85).aspx
+ const WM_USER = 0x0400
+
+ const (
+ className = "SystrayClass"
+ windowName = ""
+ )
+
+ t.wmSystrayMessage = WM_USER + 1
+ t.visibleItems = make(map[uint32][]uint32)
+ t.menus = make(map[uint32]windows.Handle)
+ t.menuOf = make(map[uint32]windows.Handle)
+ t.menuItemIcons = make(map[uint32]windows.Handle)
+
+ taskbarEventNamePtr, _ := windows.UTF16PtrFromString("TaskbarCreated")
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644947
+ res, _, err := pRegisterWindowMessage.Call(
+ uintptr(unsafe.Pointer(taskbarEventNamePtr)),
+ )
+ t.wmTaskbarCreated = uint32(res)
+
+ t.loadedImages = make(map[string]windows.Handle)
+
+ instanceHandle, _, err := pGetModuleHandle.Call(0)
+ if instanceHandle == 0 {
+ return err
+ }
+ t.instance = windows.Handle(instanceHandle)
+
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms648072(v=vs.85).aspx
+ iconHandle, _, err := pLoadIcon.Call(0, uintptr(IDI_APPLICATION))
+ if iconHandle == 0 {
+ return err
+ }
+ t.icon = windows.Handle(iconHandle)
+
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms648391(v=vs.85).aspx
+ cursorHandle, _, err := pLoadCursor.Call(0, uintptr(IDC_ARROW))
+ if cursorHandle == 0 {
+ return err
+ }
+ t.cursor = windows.Handle(cursorHandle)
+
+ classNamePtr, err := windows.UTF16PtrFromString(className)
+ if err != nil {
+ return err
+ }
+
+ windowNamePtr, err := windows.UTF16PtrFromString(windowName)
+ if err != nil {
+ return err
+ }
+
+ t.wcex = &wndClassEx{
+ Style: CS_HREDRAW | CS_VREDRAW,
+ WndProc: windows.NewCallback(t.wndProc),
+ Instance: t.instance,
+ Icon: t.icon,
+ Cursor: t.cursor,
+ Background: windows.Handle(6), // (COLOR_WINDOW + 1)
+ ClassName: classNamePtr,
+ IconSm: t.icon,
+ }
+ if err := t.wcex.register(); err != nil {
+ return err
+ }
+
+ windowHandle, _, err := pCreateWindowEx.Call(
+ uintptr(0),
+ uintptr(unsafe.Pointer(classNamePtr)),
+ uintptr(unsafe.Pointer(windowNamePtr)),
+ uintptr(WS_OVERLAPPEDWINDOW),
+ uintptr(CW_USEDEFAULT),
+ uintptr(CW_USEDEFAULT),
+ uintptr(CW_USEDEFAULT),
+ uintptr(CW_USEDEFAULT),
+ uintptr(0),
+ uintptr(0),
+ uintptr(t.instance),
+ uintptr(0),
+ )
+ if windowHandle == 0 {
+ return err
+ }
+ t.window = windows.Handle(windowHandle)
+
+ pShowWindow.Call(
+ uintptr(t.window),
+ uintptr(SW_HIDE),
+ )
+
+ pUpdateWindow.Call(
+ uintptr(t.window),
+ )
+
+ t.muNID.Lock()
+ defer t.muNID.Unlock()
+ t.nid = ¬ifyIconData{
+ Wnd: windows.Handle(t.window),
+ ID: 100,
+ Flags: NIF_MESSAGE,
+ CallbackMessage: t.wmSystrayMessage,
+ }
+ t.nid.Size = uint32(unsafe.Sizeof(*t.nid))
+
+ return t.nid.add()
+}
+
+func (t *winTray) createMenu() error {
+ const MIM_APPLYTOSUBMENUS = 0x80000000 // Settings apply to the menu and all of its submenus
+
+ menuHandle, _, err := pCreatePopupMenu.Call()
+ if menuHandle == 0 {
+ return err
+ }
+ t.menus[0] = windows.Handle(menuHandle)
+
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms647575(v=vs.85).aspx
+ mi := struct {
+ Size, Mask, Style, Max uint32
+ Background windows.Handle
+ ContextHelpID uint32
+ MenuData uintptr
+ }{
+ Mask: MIM_APPLYTOSUBMENUS,
+ }
+ mi.Size = uint32(unsafe.Sizeof(mi))
+
+ res, _, err := pSetMenuInfo.Call(
+ uintptr(t.menus[0]),
+ uintptr(unsafe.Pointer(&mi)),
+ )
+ if res == 0 {
+ return err
+ }
+ return nil
+}
+
+func (t *winTray) convertToSubMenu(menuItemId uint32) (windows.Handle, error) {
+ const MIIM_SUBMENU = 0x00000004
+
+ res, _, err := pCreateMenu.Call()
+ if res == 0 {
+ return 0, err
+ }
+ menu := windows.Handle(res)
+
+ mi := menuItemInfo{Mask: MIIM_SUBMENU, SubMenu: menu}
+ mi.Size = uint32(unsafe.Sizeof(mi))
+ t.muMenuOf.RLock()
+ hMenu := t.menuOf[menuItemId]
+ t.muMenuOf.RUnlock()
+ res, _, err = pSetMenuItemInfo.Call(
+ uintptr(hMenu),
+ uintptr(menuItemId),
+ 0,
+ uintptr(unsafe.Pointer(&mi)),
+ )
+ if res == 0 {
+ return 0, err
+ }
+ t.muMenus.Lock()
+ t.menus[menuItemId] = menu
+ t.muMenus.Unlock()
+ return menu, nil
+}
+
+func (t *winTray) addOrUpdateMenuItem(menuItemId uint32, parentId uint32, title string, disabled, checked bool) error {
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms647578(v=vs.85).aspx
+ const (
+ MIIM_FTYPE = 0x00000100
+ MIIM_BITMAP = 0x00000080
+ MIIM_STRING = 0x00000040
+ MIIM_SUBMENU = 0x00000004
+ MIIM_ID = 0x00000002
+ MIIM_STATE = 0x00000001
+ )
+ const MFT_STRING = 0x00000000
+ const (
+ MFS_CHECKED = 0x00000008
+ MFS_DISABLED = 0x00000003
+ )
+ titlePtr, err := windows.UTF16PtrFromString(title)
+ if err != nil {
+ return err
+ }
+
+ mi := menuItemInfo{
+ Mask: MIIM_FTYPE | MIIM_STRING | MIIM_ID | MIIM_STATE,
+ Type: MFT_STRING,
+ ID: uint32(menuItemId),
+ TypeData: titlePtr,
+ Cch: uint32(len(title)),
+ }
+ mi.Size = uint32(unsafe.Sizeof(mi))
+ if disabled {
+ mi.State |= MFS_DISABLED
+ }
+ if checked {
+ mi.State |= MFS_CHECKED
+ }
+ t.muMenuItemIcons.RLock()
+ hIcon := t.menuItemIcons[menuItemId]
+ t.muMenuItemIcons.RUnlock()
+ if hIcon > 0 {
+ mi.Mask |= MIIM_BITMAP
+ mi.BMPItem = hIcon
+ }
+
+ var res uintptr
+ t.muMenus.RLock()
+ menu, exists := t.menus[parentId]
+ t.muMenus.RUnlock()
+ if !exists {
+ menu, err = t.convertToSubMenu(parentId)
+ if err != nil {
+ return err
+ }
+ t.muMenus.Lock()
+ t.menus[parentId] = menu
+ t.muMenus.Unlock()
+ } else if t.getVisibleItemIndex(parentId, menuItemId) != -1 {
+ // We set the menu item info based on the menuID
+ res, _, err = pSetMenuItemInfo.Call(
+ uintptr(menu),
+ uintptr(menuItemId),
+ 0,
+ uintptr(unsafe.Pointer(&mi)),
+ )
+ }
+
+ if res == 0 {
+ t.addToVisibleItems(parentId, menuItemId)
+ position := t.getVisibleItemIndex(parentId, menuItemId)
+ res, _, err = pInsertMenuItem.Call(
+ uintptr(menu),
+ uintptr(position),
+ 1,
+ uintptr(unsafe.Pointer(&mi)),
+ )
+ if res == 0 {
+ t.delFromVisibleItems(parentId, menuItemId)
+ return err
+ }
+ t.muMenuOf.Lock()
+ t.menuOf[menuItemId] = menu
+ t.muMenuOf.Unlock()
+ }
+
+ return nil
+}
+
+func (t *winTray) addSeparatorMenuItem(menuItemId, parentId uint32) error {
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms647578(v=vs.85).aspx
+ const (
+ MIIM_FTYPE = 0x00000100
+ MIIM_ID = 0x00000002
+ MIIM_STATE = 0x00000001
+ )
+ const MFT_SEPARATOR = 0x00000800
+
+ mi := menuItemInfo{
+ Mask: MIIM_FTYPE | MIIM_ID | MIIM_STATE,
+ Type: MFT_SEPARATOR,
+ ID: uint32(menuItemId),
+ }
+
+ mi.Size = uint32(unsafe.Sizeof(mi))
+
+ t.addToVisibleItems(parentId, menuItemId)
+ position := t.getVisibleItemIndex(parentId, menuItemId)
+ t.muMenus.RLock()
+ menu := uintptr(t.menus[parentId])
+ t.muMenus.RUnlock()
+ res, _, err := pInsertMenuItem.Call(
+ menu,
+ uintptr(position),
+ 1,
+ uintptr(unsafe.Pointer(&mi)),
+ )
+ if res == 0 {
+ return err
+ }
+
+ return nil
+}
+
+func (t *winTray) hideMenuItem(menuItemId, parentId uint32) error {
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms647629(v=vs.85).aspx
+ const MF_BYCOMMAND = 0x00000000
+ const ERROR_SUCCESS syscall.Errno = 0
+
+ t.muMenus.RLock()
+ menu := uintptr(t.menus[parentId])
+ t.muMenus.RUnlock()
+ res, _, err := pDeleteMenu.Call(
+ menu,
+ uintptr(menuItemId),
+ MF_BYCOMMAND,
+ )
+ if res == 0 && err.(syscall.Errno) != ERROR_SUCCESS {
+ return err
+ }
+ t.delFromVisibleItems(parentId, menuItemId)
+
+ return nil
+}
+
+func (t *winTray) showMenu() error {
+ const (
+ TPM_BOTTOMALIGN = 0x0020
+ TPM_LEFTALIGN = 0x0000
+ )
+ p := point{}
+ res, _, err := pGetCursorPos.Call(uintptr(unsafe.Pointer(&p)))
+ if res == 0 {
+ return err
+ }
+ pSetForegroundWindow.Call(uintptr(t.window))
+
+ res, _, err = pTrackPopupMenu.Call(
+ uintptr(t.menus[0]),
+ TPM_BOTTOMALIGN|TPM_LEFTALIGN,
+ uintptr(p.X),
+ uintptr(p.Y),
+ 0,
+ uintptr(t.window),
+ 0,
+ )
+ if res == 0 {
+ return err
+ }
+
+ return nil
+}
+
+func (t *winTray) delFromVisibleItems(parent, val uint32) {
+ t.muVisibleItems.Lock()
+ defer t.muVisibleItems.Unlock()
+ visibleItems := t.visibleItems[parent]
+ for i, itemval := range visibleItems {
+ if val == itemval {
+ visibleItems = append(visibleItems[:i], visibleItems[i+1:]...)
+ break
+ }
+ }
+}
+
+func (t *winTray) addToVisibleItems(parent, val uint32) {
+ t.muVisibleItems.Lock()
+ defer t.muVisibleItems.Unlock()
+ if visibleItems, exists := t.visibleItems[parent]; !exists {
+ t.visibleItems[parent] = []uint32{val}
+ } else {
+ newvisible := append(visibleItems, val)
+ sort.Slice(newvisible, func(i, j int) bool { return newvisible[i] < newvisible[j] })
+ t.visibleItems[parent] = newvisible
+ }
+}
+
+func (t *winTray) getVisibleItemIndex(parent, val uint32) int {
+ t.muVisibleItems.RLock()
+ defer t.muVisibleItems.RUnlock()
+ for i, itemval := range t.visibleItems[parent] {
+ if val == itemval {
+ return i
+ }
+ }
+ return -1
+}
+
+// Loads an image from file to be shown in tray or menu item.
+// LoadImage: https://msdn.microsoft.com/en-us/library/windows/desktop/ms648045(v=vs.85).aspx
+func (t *winTray) loadIconFrom(src string) (windows.Handle, error) {
+ const IMAGE_ICON = 1 // Loads an icon
+ const LR_LOADFROMFILE = 0x00000010 // Loads the stand-alone image from the file
+ const LR_DEFAULTSIZE = 0x00000040 // Loads default-size icon for windows(SM_CXICON x SM_CYICON) if cx, cy are set to zero
+
+ // Save and reuse handles of loaded images
+ t.muLoadedImages.RLock()
+ h, ok := t.loadedImages[src]
+ t.muLoadedImages.RUnlock()
+ if !ok {
+ srcPtr, err := windows.UTF16PtrFromString(src)
+ if err != nil {
+ return 0, err
+ }
+ res, _, err := pLoadImage.Call(
+ 0,
+ uintptr(unsafe.Pointer(srcPtr)),
+ IMAGE_ICON,
+ 0,
+ 0,
+ LR_LOADFROMFILE|LR_DEFAULTSIZE,
+ )
+ if res == 0 {
+ return 0, err
+ }
+ h = windows.Handle(res)
+ t.muLoadedImages.Lock()
+ t.loadedImages[src] = h
+ t.muLoadedImages.Unlock()
+ }
+ return h, nil
+}
+
+func (t *winTray) iconToBitmap(hIcon windows.Handle) (windows.Handle, error) {
+ const SM_CXSMICON = 49
+ const SM_CYSMICON = 50
+ const DI_NORMAL = 0x3
+ hDC, _, err := pGetDC.Call(uintptr(0))
+ if hDC == 0 {
+ return 0, err
+ }
+ defer pReleaseDC.Call(uintptr(0), hDC)
+ hMemDC, _, err := pCreateCompatibleDC.Call(hDC)
+ if hMemDC == 0 {
+ return 0, err
+ }
+ defer pDeleteDC.Call(hMemDC)
+ cx, _, _ := pGetSystemMetrics.Call(SM_CXSMICON)
+ cy, _, _ := pGetSystemMetrics.Call(SM_CYSMICON)
+ hMemBmp, _, err := pCreateCompatibleBitmap.Call(hDC, cx, cy)
+ if hMemBmp == 0 {
+ return 0, err
+ }
+ hOriginalBmp, _, _ := pSelectObject.Call(hMemDC, hMemBmp)
+ defer pSelectObject.Call(hMemDC, hOriginalBmp)
+ res, _, err := pDrawIconEx.Call(hMemDC, 0, 0, uintptr(hIcon), cx, cy, 0, uintptr(0), DI_NORMAL)
+ if res == 0 {
+ return 0, err
+ }
+ return windows.Handle(hMemBmp), nil
+}
+
+func registerSystray() {
+ if err := wt.initInstance(); err != nil {
+ log.Printf("Unable to init instance: %v", err)
+ return
+ }
+
+ if err := wt.createMenu(); err != nil {
+ log.Printf("Unable to create menu: %v", err)
+ return
+ }
+
+}
+
+var m = &struct {
+ WindowHandle windows.Handle
+ Message uint32
+ Wparam uintptr
+ Lparam uintptr
+ Time uint32
+ Pt point
+}{}
+
+func nativeLoop() {
+ for doNativeTick() {
+ }
+}
+
+func nativeEnd() {
+}
+
+func nativeStart() {
+ go func() {
+ for doNativeTick() {
+ }
+ }()
+}
+
+func doNativeTick() bool {
+ ret, _, err := pGetMessage.Call(uintptr(unsafe.Pointer(m)), 0, 0, 0)
+
+ // If the function retrieves a message other than WM_QUIT, the return value is nonzero.
+ // If the function retrieves the WM_QUIT message, the return value is zero.
+ // If there is an error, the return value is -1
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644936(v=vs.85).aspx
+ switch int32(ret) {
+ case -1:
+ log.Printf("Error at message loop: %v", err)
+ return false
+ case 0:
+ return false
+ default:
+ pTranslateMessage.Call(uintptr(unsafe.Pointer(m)))
+ pDispatchMessage.Call(uintptr(unsafe.Pointer(m)))
+ }
+ return true
+}
+
+func quit() {
+ const WM_CLOSE = 0x0010
+
+ pPostMessage.Call(
+ uintptr(wt.window),
+ WM_CLOSE,
+ 0,
+ 0,
+ )
+}
+
+func setInternalLoop(bool) {
+}
+
+func iconBytesToFilePath(iconBytes []byte) (string, error) {
+ bh := md5.Sum(iconBytes)
+ dataHash := hex.EncodeToString(bh[:])
+ iconFilePath := filepath.Join(os.TempDir(), "systray_temp_icon_"+dataHash)
+
+ if _, err := os.Stat(iconFilePath); os.IsNotExist(err) {
+ if err := ioutil.WriteFile(iconFilePath, iconBytes, 0644); err != nil {
+ return "", err
+ }
+ }
+ return iconFilePath, nil
+}
+
+// SetIcon sets the systray icon.
+// iconBytes should be the content of .ico for windows and .ico/.jpg/.png
+// for other platforms.
+func SetIcon(iconBytes []byte) {
+ iconFilePath, err := iconBytesToFilePath(iconBytes)
+ if err != nil {
+ log.Printf("Unable to write icon data to temp file: %v", err)
+ return
+ }
+ if err := wt.setIcon(iconFilePath); err != nil {
+ log.Printf("Unable to set icon: %v", err)
+ return
+ }
+}
+
+// SetTemplateIcon sets the systray icon as a template icon (on macOS), falling back
+// to a regular icon on other platforms.
+// templateIconBytes and iconBytes should be the content of .ico for windows and
+// .ico/.jpg/.png for other platforms.
+func SetTemplateIcon(templateIconBytes []byte, regularIconBytes []byte) {
+ SetIcon(regularIconBytes)
+}
+
+// SetTitle sets the systray title, only available on Mac and Linux.
+func SetTitle(title string) {
+ // do nothing
+}
+
+func (item *MenuItem) parentId() uint32 {
+ if item.parent != nil {
+ return uint32(item.parent.id)
+ }
+ return 0
+}
+
+// SetIcon sets the icon of a menu item. Only works on macOS and Windows.
+// iconBytes should be the content of .ico/.jpg/.png
+func (item *MenuItem) SetIcon(iconBytes []byte) {
+ iconFilePath, err := iconBytesToFilePath(iconBytes)
+ if err != nil {
+ log.Printf("Unable to write icon data to temp file: %v", err)
+ return
+ }
+
+ h, err := wt.loadIconFrom(iconFilePath)
+ if err != nil {
+ log.Printf("Unable to load icon from temp file: %v", err)
+ return
+ }
+
+ h, err = wt.iconToBitmap(h)
+ if err != nil {
+ log.Printf("Unable to convert icon to bitmap: %v", err)
+ return
+ }
+ wt.muMenuItemIcons.Lock()
+ wt.menuItemIcons[uint32(item.id)] = h
+ wt.muMenuItemIcons.Unlock()
+
+ err = wt.addOrUpdateMenuItem(uint32(item.id), item.parentId(), item.title, item.disabled, item.checked)
+ if err != nil {
+ log.Printf("Unable to addOrUpdateMenuItem: %v", err)
+ return
+ }
+}
+
+// SetTooltip sets the systray tooltip to display on mouse hover of the tray icon,
+// only available on Mac and Windows.
+func SetTooltip(tooltip string) {
+ if err := wt.setTooltip(tooltip); err != nil {
+ log.Printf("Unable to set tooltip: %v", err)
+ return
+ }
+}
+
+func addOrUpdateMenuItem(item *MenuItem) {
+ err := wt.addOrUpdateMenuItem(uint32(item.id), item.parentId(), item.title, item.disabled, item.checked)
+ if err != nil {
+ log.Printf("Unable to addOrUpdateMenuItem: %v", err)
+ return
+ }
+}
+
+// SetTemplateIcon sets the icon of a menu item as a template icon (on macOS). On Windows, it
+// falls back to the regular icon bytes and on Linux it does nothing.
+// templateIconBytes and regularIconBytes should be the content of .ico for windows and
+// .ico/.jpg/.png for other platforms.
+func (item *MenuItem) SetTemplateIcon(templateIconBytes []byte, regularIconBytes []byte) {
+ item.SetIcon(regularIconBytes)
+}
+
+func addSeparator(id uint32) {
+ err := wt.addSeparatorMenuItem(id, 0)
+ if err != nil {
+ log.Printf("Unable to addSeparator: %v", err)
+ return
+ }
+}
+
+func hideMenuItem(item *MenuItem) {
+ err := wt.hideMenuItem(uint32(item.id), item.parentId())
+ if err != nil {
+ log.Printf("Unable to hideMenuItem: %v", err)
+ return
+ }
+}
+
+func showMenuItem(item *MenuItem) {
+ addOrUpdateMenuItem(item)
+}
diff --git a/vendor/github.com/godbus/dbus/v5/introspect/call.go b/vendor/github.com/godbus/dbus/v5/introspect/call.go
new file mode 100644
index 0000000000..a50f3e662d
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/introspect/call.go
@@ -0,0 +1,28 @@
+package introspect
+
+import (
+ "encoding/xml"
+ "strings"
+
+ "github.com/godbus/dbus/v5"
+)
+
+// Call calls org.freedesktop.Introspectable.Introspect on a remote object
+// and returns the introspection data.
+func Call(o dbus.BusObject) (*Node, error) {
+ var xmldata string
+ var node Node
+
+ err := o.Call("org.freedesktop.DBus.Introspectable.Introspect", 0).Store(&xmldata)
+ if err != nil {
+ return nil, err
+ }
+ err = xml.NewDecoder(strings.NewReader(xmldata)).Decode(&node)
+ if err != nil {
+ return nil, err
+ }
+ if node.Name == "" {
+ node.Name = string(o.Path())
+ }
+ return &node, nil
+}
diff --git a/vendor/github.com/godbus/dbus/v5/introspect/introspect.go b/vendor/github.com/godbus/dbus/v5/introspect/introspect.go
new file mode 100644
index 0000000000..8ee61055ac
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/introspect/introspect.go
@@ -0,0 +1,86 @@
+// Package introspect provides some utilities for dealing with the DBus
+// introspection format.
+package introspect
+
+import "encoding/xml"
+
+// The introspection data for the org.freedesktop.DBus.Introspectable interface.
+var IntrospectData = Interface{
+ Name: "org.freedesktop.DBus.Introspectable",
+ Methods: []Method{
+ {
+ Name: "Introspect",
+ Args: []Arg{
+ {"out", "s", "out"},
+ },
+ },
+ },
+}
+
+// XML document type declaration of the introspection format version 1.0
+const IntrospectDeclarationString = `
+
+`
+
+// The introspection data for the org.freedesktop.DBus.Introspectable interface,
+// as a string.
+const IntrospectDataString = `
+
+
+
+
+
+`
+
+// Node is the root element of an introspection.
+type Node struct {
+ XMLName xml.Name `xml:"node"`
+ Name string `xml:"name,attr,omitempty"`
+ Interfaces []Interface `xml:"interface"`
+ Children []Node `xml:"node,omitempty"`
+}
+
+// Interface describes a DBus interface that is available on the message bus.
+type Interface struct {
+ Name string `xml:"name,attr"`
+ Methods []Method `xml:"method"`
+ Signals []Signal `xml:"signal"`
+ Properties []Property `xml:"property"`
+ Annotations []Annotation `xml:"annotation"`
+}
+
+// Method describes a Method on an Interface as returned by an introspection.
+type Method struct {
+ Name string `xml:"name,attr"`
+ Args []Arg `xml:"arg"`
+ Annotations []Annotation `xml:"annotation"`
+}
+
+// Signal describes a Signal emitted on an Interface.
+type Signal struct {
+ Name string `xml:"name,attr"`
+ Args []Arg `xml:"arg"`
+ Annotations []Annotation `xml:"annotation"`
+}
+
+// Property describes a property of an Interface.
+type Property struct {
+ Name string `xml:"name,attr"`
+ Type string `xml:"type,attr"`
+ Access string `xml:"access,attr"`
+ Annotations []Annotation `xml:"annotation"`
+}
+
+// Arg represents an argument of a method or a signal.
+type Arg struct {
+ Name string `xml:"name,attr,omitempty"`
+ Type string `xml:"type,attr"`
+ Direction string `xml:"direction,attr,omitempty"`
+}
+
+// Annotation is an annotation in the introspection format.
+type Annotation struct {
+ Name string `xml:"name,attr"`
+ Value string `xml:"value,attr"`
+}
diff --git a/vendor/github.com/godbus/dbus/v5/introspect/introspectable.go b/vendor/github.com/godbus/dbus/v5/introspect/introspectable.go
new file mode 100644
index 0000000000..a032802bbd
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/introspect/introspectable.go
@@ -0,0 +1,77 @@
+package introspect
+
+import (
+ "encoding/xml"
+ "reflect"
+ "strings"
+
+ "github.com/godbus/dbus/v5"
+)
+
+// Introspectable implements org.freedesktop.Introspectable.
+//
+// You can create it by converting the XML-formatted introspection data from a
+// string to an Introspectable or call NewIntrospectable with a Node. Then,
+// export it as org.freedesktop.Introspectable on you object.
+type Introspectable string
+
+// NewIntrospectable returns an Introspectable that returns the introspection
+// data that corresponds to the given Node. If n.Interfaces doesn't contain the
+// data for org.freedesktop.DBus.Introspectable, it is added automatically.
+func NewIntrospectable(n *Node) Introspectable {
+ found := false
+ for _, v := range n.Interfaces {
+ if v.Name == "org.freedesktop.DBus.Introspectable" {
+ found = true
+ break
+ }
+ }
+ if !found {
+ n.Interfaces = append(n.Interfaces, IntrospectData)
+ }
+ b, err := xml.Marshal(n)
+ if err != nil {
+ panic(err)
+ }
+ return Introspectable(strings.TrimSpace(IntrospectDeclarationString) + string(b))
+}
+
+// Introspect implements org.freedesktop.Introspectable.Introspect.
+func (i Introspectable) Introspect() (string, *dbus.Error) {
+ return string(i), nil
+}
+
+// Methods returns the description of the methods of v. This can be used to
+// create a Node which can be passed to NewIntrospectable.
+func Methods(v interface{}) []Method {
+ t := reflect.TypeOf(v)
+ ms := make([]Method, 0, t.NumMethod())
+ for i := 0; i < t.NumMethod(); i++ {
+ if t.Method(i).PkgPath != "" {
+ continue
+ }
+ mt := t.Method(i).Type
+ if mt.NumOut() == 0 ||
+ mt.Out(mt.NumOut()-1) != reflect.TypeOf(&dbus.Error{}) {
+
+ continue
+ }
+ var m Method
+ m.Name = t.Method(i).Name
+ m.Args = make([]Arg, 0, mt.NumIn()+mt.NumOut()-2)
+ for j := 1; j < mt.NumIn(); j++ {
+ if mt.In(j) != reflect.TypeOf((*dbus.Sender)(nil)).Elem() &&
+ mt.In(j) != reflect.TypeOf((*dbus.Message)(nil)).Elem() {
+ arg := Arg{"", dbus.SignatureOfType(mt.In(j)).String(), "in"}
+ m.Args = append(m.Args, arg)
+ }
+ }
+ for j := 0; j < mt.NumOut()-1; j++ {
+ arg := Arg{"", dbus.SignatureOfType(mt.Out(j)).String(), "out"}
+ m.Args = append(m.Args, arg)
+ }
+ m.Annotations = make([]Annotation, 0)
+ ms = append(ms, m)
+ }
+ return ms
+}
diff --git a/vendor/github.com/godbus/dbus/v5/prop/prop.go b/vendor/github.com/godbus/dbus/v5/prop/prop.go
new file mode 100644
index 0000000000..2c2a61321e
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/prop/prop.go
@@ -0,0 +1,348 @@
+// Package prop provides the Properties struct which can be used to implement
+// org.freedesktop.DBus.Properties.
+package prop
+
+import (
+ "reflect"
+ "sync"
+
+ "github.com/godbus/dbus/v5"
+ "github.com/godbus/dbus/v5/introspect"
+)
+
+// EmitType controls how org.freedesktop.DBus.Properties.PropertiesChanged is
+// emitted for a property. If it is EmitTrue, the signal is emitted. If it is
+// EmitInvalidates, the signal is also emitted, but the new value of the property
+// is not disclosed. If it is EmitConst, the property never changes value during
+// the lifetime of the object it belongs to, and hence the signal is never emitted
+// for it.
+type EmitType byte
+
+const (
+ EmitFalse EmitType = iota
+ EmitTrue
+ EmitInvalidates
+ EmitConst
+)
+
+func (e EmitType) String() (str string) {
+ switch e {
+ case EmitFalse:
+ str = "false"
+ case EmitTrue:
+ str = "true"
+ case EmitInvalidates:
+ str = "invalidates"
+ case EmitConst:
+ str = "const"
+ default:
+ panic("invalid value for EmitType")
+ }
+ return
+}
+
+// ErrIfaceNotFound is the error returned to peers who try to access properties
+// on interfaces that aren't found.
+var ErrIfaceNotFound = dbus.NewError("org.freedesktop.DBus.Properties.Error.InterfaceNotFound", nil)
+
+// ErrPropNotFound is the error returned to peers trying to access properties
+// that aren't found.
+var ErrPropNotFound = dbus.NewError("org.freedesktop.DBus.Properties.Error.PropertyNotFound", nil)
+
+// ErrReadOnly is the error returned to peers trying to set a read-only
+// property.
+var ErrReadOnly = dbus.NewError("org.freedesktop.DBus.Properties.Error.ReadOnly", nil)
+
+// ErrInvalidArg is returned to peers if the type of the property that is being
+// changed and the argument don't match.
+var ErrInvalidArg = dbus.NewError("org.freedesktop.DBus.Properties.Error.InvalidArg", nil)
+
+// The introspection data for the org.freedesktop.DBus.Properties interface.
+var IntrospectData = introspect.Interface{
+ Name: "org.freedesktop.DBus.Properties",
+ Methods: []introspect.Method{
+ {
+ Name: "Get",
+ Args: []introspect.Arg{
+ {Name: "interface", Type: "s", Direction: "in"},
+ {Name: "property", Type: "s", Direction: "in"},
+ {Name: "value", Type: "v", Direction: "out"},
+ },
+ },
+ {
+ Name: "GetAll",
+ Args: []introspect.Arg{
+ {Name: "interface", Type: "s", Direction: "in"},
+ {Name: "props", Type: "a{sv}", Direction: "out"},
+ },
+ },
+ {
+ Name: "Set",
+ Args: []introspect.Arg{
+ {Name: "interface", Type: "s", Direction: "in"},
+ {Name: "property", Type: "s", Direction: "in"},
+ {Name: "value", Type: "v", Direction: "in"},
+ },
+ },
+ },
+ Signals: []introspect.Signal{
+ {
+ Name: "PropertiesChanged",
+ Args: []introspect.Arg{
+ {Name: "interface", Type: "s", Direction: "out"},
+ {Name: "changed_properties", Type: "a{sv}", Direction: "out"},
+ {Name: "invalidates_properties", Type: "as", Direction: "out"},
+ },
+ },
+ },
+}
+
+// The introspection data for the org.freedesktop.DBus.Properties interface, as
+// a string.
+const IntrospectDataString = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`
+
+// Prop represents a single property. It is used for creating a Properties
+// value.
+type Prop struct {
+ // Initial value. Must be a DBus-representable type. This is not modified
+ // after Properties has been initialized; use Get or GetMust to access the
+ // value.
+ Value interface{}
+
+ // If true, the value can be modified by calls to Set.
+ Writable bool
+
+ // Controls how org.freedesktop.DBus.Properties.PropertiesChanged is
+ // emitted if this property changes.
+ Emit EmitType
+
+ // If not nil, anytime this property is changed by Set, this function is
+ // called with an appropriate Change as its argument. If the returned error
+ // is not nil, it is sent back to the caller of Set and the property is not
+ // changed.
+ Callback func(*Change) *dbus.Error
+}
+
+// Introspection returns the introspection data for p.
+// The "name" argument is used as the property's name in the resulting data.
+func (p *Prop) Introspection(name string) introspect.Property {
+ var result = introspect.Property{Name: name, Type: dbus.SignatureOf(p.Value).String()}
+ if p.Writable {
+ result.Access = "readwrite"
+ } else {
+ result.Access = "read"
+ }
+ result.Annotations = []introspect.Annotation{
+ {
+ Name: "org.freedesktop.DBus.Property.EmitsChangedSignal",
+ Value: p.Emit.String(),
+ },
+ }
+ return result
+}
+
+// Change represents a change of a property by a call to Set.
+type Change struct {
+ Props *Properties
+ Iface string
+ Name string
+ Value interface{}
+}
+
+// Properties is a set of values that can be made available to the message bus
+// using the org.freedesktop.DBus.Properties interface. It is safe for
+// concurrent use by multiple goroutines.
+type Properties struct {
+ m Map
+ mut sync.RWMutex
+ conn *dbus.Conn
+ path dbus.ObjectPath
+}
+
+// New falls back to Export, but it returns nil if properties export fails,
+// swallowing the error, shouldn't be used.
+//
+// Deprecated: use Export instead.
+func New(conn *dbus.Conn, path dbus.ObjectPath, props Map) *Properties {
+ p, err := Export(conn, path, props)
+ if err != nil {
+ return nil
+ }
+ return p
+}
+
+// Export returns a new Properties structure that manages the given properties.
+// The key for the first-level map of props is the name of the interface; the
+// second-level key is the name of the property. The returned structure will be
+// exported as org.freedesktop.DBus.Properties on path.
+func Export(
+ conn *dbus.Conn, path dbus.ObjectPath, props Map,
+) (*Properties, error) {
+ p := &Properties{m: copyProps(props), conn: conn, path: path}
+ if err := conn.Export(p, path, "org.freedesktop.DBus.Properties"); err != nil {
+ return nil, err
+ }
+ return p, nil
+}
+
+// Map is a helper type for supplying the configuration of properties to be handled.
+type Map = map[string]map[string]*Prop
+
+func copyProps(in Map) Map {
+ out := make(Map, len(in))
+ for intf, props := range in {
+ out[intf] = make(map[string]*Prop)
+ for name, prop := range props {
+ out[intf][name] = new(Prop)
+ *out[intf][name] = *prop
+ val := reflect.New(reflect.TypeOf(prop.Value))
+ val.Elem().Set(reflect.ValueOf(prop.Value))
+ out[intf][name].Value = val.Interface()
+ }
+ }
+ return out
+}
+
+// Get implements org.freedesktop.DBus.Properties.Get.
+func (p *Properties) Get(iface, property string) (dbus.Variant, *dbus.Error) {
+ p.mut.RLock()
+ defer p.mut.RUnlock()
+ m, ok := p.m[iface]
+ if !ok {
+ return dbus.Variant{}, ErrIfaceNotFound
+ }
+ prop, ok := m[property]
+ if !ok {
+ return dbus.Variant{}, ErrPropNotFound
+ }
+ return dbus.MakeVariant(reflect.ValueOf(prop.Value).Elem().Interface()), nil
+}
+
+// GetAll implements org.freedesktop.DBus.Properties.GetAll.
+func (p *Properties) GetAll(iface string) (map[string]dbus.Variant, *dbus.Error) {
+ p.mut.RLock()
+ defer p.mut.RUnlock()
+ m, ok := p.m[iface]
+ if !ok {
+ return nil, ErrIfaceNotFound
+ }
+ rm := make(map[string]dbus.Variant, len(m))
+ for k, v := range m {
+ rm[k] = dbus.MakeVariant(reflect.ValueOf(v.Value).Elem().Interface())
+ }
+ return rm, nil
+}
+
+// GetMust returns the value of the given property and panics if either the
+// interface or the property name are invalid.
+func (p *Properties) GetMust(iface, property string) interface{} {
+ p.mut.RLock()
+ defer p.mut.RUnlock()
+ return reflect.ValueOf(p.m[iface][property].Value).Elem().Interface()
+}
+
+// Introspection returns the introspection data that represents the properties
+// of iface.
+func (p *Properties) Introspection(iface string) []introspect.Property {
+ p.mut.RLock()
+ defer p.mut.RUnlock()
+ m := p.m[iface]
+ s := make([]introspect.Property, 0, len(m))
+ for name, prop := range m {
+ s = append(s, prop.Introspection(name))
+ }
+ return s
+}
+
+// set sets the given property and emits PropertyChanged if appropriate. p.mut
+// must already be locked.
+func (p *Properties) set(iface, property string, v interface{}) error {
+ prop := p.m[iface][property]
+ err := dbus.Store([]interface{}{v}, prop.Value)
+ if err != nil {
+ return err
+ }
+ return p.emitChange(iface, property)
+}
+
+func (p *Properties) emitChange(iface, property string) error {
+ prop := p.m[iface][property]
+ switch prop.Emit {
+ case EmitFalse:
+ return nil // do nothing
+ case EmitInvalidates:
+ return p.conn.Emit(p.path, "org.freedesktop.DBus.Properties.PropertiesChanged",
+ iface, map[string]dbus.Variant{}, []string{property})
+ case EmitTrue:
+ return p.conn.Emit(p.path, "org.freedesktop.DBus.Properties.PropertiesChanged",
+ iface, map[string]dbus.Variant{property: dbus.MakeVariant(prop.Value)},
+ []string{})
+ case EmitConst:
+ return nil
+ default:
+ panic("invalid value for EmitType")
+ }
+}
+
+// Set implements org.freedesktop.Properties.Set.
+func (p *Properties) Set(iface, property string, newv dbus.Variant) *dbus.Error {
+ p.mut.Lock()
+ defer p.mut.Unlock()
+ m, ok := p.m[iface]
+ if !ok {
+ return ErrIfaceNotFound
+ }
+ prop, ok := m[property]
+ if !ok {
+ return ErrPropNotFound
+ }
+ if !prop.Writable {
+ return ErrReadOnly
+ }
+ if newv.Signature() != dbus.SignatureOf(prop.Value) {
+ return ErrInvalidArg
+ }
+ if prop.Callback != nil {
+ err := prop.Callback(&Change{p, iface, property, newv.Value()})
+ if err != nil {
+ return err
+ }
+ }
+ if err := p.set(iface, property, newv.Value()); err != nil {
+ return dbus.MakeFailedError(err)
+ }
+ return nil
+}
+
+// SetMust sets the value of the given property and panics if the interface or
+// the property name are invalid.
+func (p *Properties) SetMust(iface, property string, v interface{}) {
+ p.mut.Lock()
+ defer p.mut.Unlock() // unlock in case of panic
+ err := p.set(iface, property, v)
+ if err != nil {
+ panic(err)
+ }
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 76bf4dbfb5..5660f86d5b 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1,3 +1,8 @@
+# fyne.io/systray v1.1.1-0.20220307102710-0121a6d9ce01
+## explicit
+fyne.io/systray
+fyne.io/systray/internal/generated/menu
+fyne.io/systray/internal/generated/notifier
# github.com/BurntSushi/toml v1.0.0
## explicit
github.com/BurntSushi/toml
@@ -50,6 +55,8 @@ github.com/go-ole/go-ole/oleutil
# github.com/godbus/dbus/v5 v5.1.0
## explicit
github.com/godbus/dbus/v5
+github.com/godbus/dbus/v5/introspect
+github.com/godbus/dbus/v5/prop
# github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff
## explicit
github.com/goki/freetype