diff --git a/app/app_darwin.go b/app/app_darwin.go index 94389fae89..b8f3ab3105 100644 --- a/app/app_darwin.go +++ b/app/app_darwin.go @@ -37,10 +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 aa7dad193e..3e4a09b1f5 100644 --- a/app/app_windows.go +++ b/app/app_windows.go @@ -83,10 +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/go.mod b/go.mod index 11d411234b..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 @@ -10,7 +11,6 @@ require ( github.com/fsnotify/fsnotify v1.5.1 github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504 - github.com/fyne-io/systray v1.1.1-0.20220306152635-ed046166dd6b github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211213063430-748e38ca8aec github.com/go-ole/go-ole v1.2.6 diff --git a/go.sum b/go.sum index 22b5493318..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= @@ -84,8 +86,6 @@ github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe h1:A/wiwvQ0CAjPkuJyt github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe/go.mod h1:d4clgH0/GrRwWjRzJJQXxT/h1TyuNSfF/X64zb/3Ggg= github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504 h1:+31CdF/okdokeFNoy9L/2PccG3JFidQT3ev64/r4pYU= github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504/go.mod h1:gLRWYfYnMA9TONeppRSikMdXlHQ97xVsPojddUv3b/E= -github.com/fyne-io/systray v1.1.1-0.20220306152635-ed046166dd6b h1:7kfhvRMwRg3t/gWnBbqdi6+4OwQntdQBGRb3mMTHB9M= -github.com/fyne-io/systray v1.1.1-0.20220306152635-ed046166dd6b/go.mod h1:s+heIP7MbDGp0rLQhw1yEpMEaf0d3y3XQ75K68Esobs= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk= github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= diff --git a/internal/driver/glfw/driver_desktop.go b/internal/driver/glfw/driver_desktop.go index 53ada46760..364b056972 100644 --- a/internal/driver/glfw/driver_desktop.go +++ b/internal/driver/glfw/driver_desktop.go @@ -4,7 +4,7 @@ package glfw import ( - "github.com/fyne-io/systray" + "fyne.io/systray" "fyne.io/fyne/v2" "fyne.io/fyne/v2/theme" diff --git a/vendor/github.com/fyne-io/systray/.gitignore b/vendor/fyne.io/systray/.gitignore similarity index 100% rename from vendor/github.com/fyne-io/systray/.gitignore rename to vendor/fyne.io/systray/.gitignore diff --git a/vendor/github.com/fyne-io/systray/CHANGELOG.md b/vendor/fyne.io/systray/CHANGELOG.md similarity index 100% rename from vendor/github.com/fyne-io/systray/CHANGELOG.md rename to vendor/fyne.io/systray/CHANGELOG.md diff --git a/vendor/github.com/fyne-io/systray/LICENSE b/vendor/fyne.io/systray/LICENSE similarity index 100% rename from vendor/github.com/fyne-io/systray/LICENSE rename to vendor/fyne.io/systray/LICENSE diff --git a/vendor/github.com/fyne-io/systray/Makefile b/vendor/fyne.io/systray/Makefile similarity index 100% rename from vendor/github.com/fyne-io/systray/Makefile rename to vendor/fyne.io/systray/Makefile diff --git a/vendor/github.com/fyne-io/systray/README.md b/vendor/fyne.io/systray/README.md similarity index 87% rename from vendor/github.com/fyne-io/systray/README.md rename to vendor/fyne.io/systray/README.md index 079a64492d..a65449ea49 100644 --- a/vendor/github.com/fyne-io/systray/README.md +++ b/vendor/fyne.io/systray/README.md @@ -6,13 +6,17 @@ removing the GTK dependency and support for legacy linux system tray. ## Features -* Supported on Windows, macOS (cgo-free Linux is a work in progress) +* 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) } @@ -39,7 +43,7 @@ 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/github.com/fyne-io/systray?tab=doc) as well as [CHANGELOG](https://github.com/fyne-io/systray/tree/master/CHANGELOG.md). +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! @@ -73,7 +77,7 @@ Now look for *Awesome App* in your menu bar! ### Linux -> This is a work in progress and currently broken / non-functionali +This implementation uses DBus to communicate through the SystemNotifier/AppIndicator spec, older tray implementations may not load the icon. ### Windows @@ -113,5 +117,6 @@ Consult the [Official Apple Documentation here](https://developer.apple.com/libr ## Credits +- https://github.com/getlantern/systray - https://github.com/xilp/systray - https://github.com/cratonica/trayhost diff --git a/vendor/github.com/fyne-io/systray/go.mod b/vendor/fyne.io/systray/go.mod similarity index 76% rename from vendor/github.com/fyne-io/systray/go.mod rename to vendor/fyne.io/systray/go.mod index 291da134e3..3edf71cae1 100644 --- a/vendor/github.com/fyne-io/systray/go.mod +++ b/vendor/fyne.io/systray/go.mod @@ -1,4 +1,4 @@ -module github.com/fyne-io/systray +module fyne.io/systray go 1.13 diff --git a/vendor/github.com/fyne-io/systray/go.sum b/vendor/fyne.io/systray/go.sum similarity index 100% rename from vendor/github.com/fyne-io/systray/go.sum rename to vendor/fyne.io/systray/go.sum diff --git a/vendor/github.com/fyne-io/systray/internal/generated/menu/dbus_menu.go b/vendor/fyne.io/systray/internal/generated/menu/dbus_menu.go similarity index 100% rename from vendor/github.com/fyne-io/systray/internal/generated/menu/dbus_menu.go rename to vendor/fyne.io/systray/internal/generated/menu/dbus_menu.go diff --git a/vendor/github.com/fyne-io/systray/internal/generated/notifier/status_notifier_item.go b/vendor/fyne.io/systray/internal/generated/notifier/status_notifier_item.go similarity index 100% rename from vendor/github.com/fyne-io/systray/internal/generated/notifier/status_notifier_item.go rename to vendor/fyne.io/systray/internal/generated/notifier/status_notifier_item.go diff --git a/vendor/github.com/fyne-io/systray/systray.go b/vendor/fyne.io/systray/systray.go similarity index 100% rename from vendor/github.com/fyne-io/systray/systray.go rename to vendor/fyne.io/systray/systray.go diff --git a/vendor/github.com/fyne-io/systray/systray.h b/vendor/fyne.io/systray/systray.h similarity index 100% rename from vendor/github.com/fyne-io/systray/systray.h rename to vendor/fyne.io/systray/systray.h diff --git a/vendor/github.com/fyne-io/systray/systray_darwin.go b/vendor/fyne.io/systray/systray_darwin.go similarity index 100% rename from vendor/github.com/fyne-io/systray/systray_darwin.go rename to vendor/fyne.io/systray/systray_darwin.go diff --git a/vendor/github.com/fyne-io/systray/systray_darwin.m b/vendor/fyne.io/systray/systray_darwin.m similarity index 97% rename from vendor/github.com/fyne-io/systray/systray_darwin.m rename to vendor/fyne.io/systray/systray_darwin.m index 777bf8a435..14f6a50fb0 100644 --- a/vendor/github.com/fyne-io/systray/systray_darwin.m +++ b/vendor/fyne.io/systray/systray_darwin.m @@ -246,7 +246,10 @@ int nativeLoop(void) { void nativeStart(void) { owner = [[AppDelegate alloc] init]; - [owner applicationDidFinishLaunching:NULL]; + + NSNotification *launched = [NSNotification notificationWithName:NSApplicationDidFinishLaunchingNotification + object:[NSApplication sharedApplication]]; + [owner applicationDidFinishLaunching:launched]; } void runInMainThread(SEL method, id object) { diff --git a/vendor/github.com/fyne-io/systray/systray_linux.go b/vendor/fyne.io/systray/systray_linux.go similarity index 91% rename from vendor/github.com/fyne-io/systray/systray_linux.go rename to vendor/fyne.io/systray/systray_linux.go index 013560ee87..bb1a391741 100644 --- a/vendor/github.com/fyne-io/systray/systray_linux.go +++ b/vendor/fyne.io/systray/systray_linux.go @@ -12,11 +12,12 @@ import ( "log" "os" - "github.com/fyne-io/systray/internal/generated/menu" - "github.com/fyne-io/systray/internal/generated/notifier" "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 ( @@ -44,7 +45,7 @@ var ( // .ico/.jpg/.png for other platforms. func SetTemplateIcon(templateIconBytes []byte, regularIconBytes []byte) { // TODO handle the templateIconBytes? - iconData = regularIconBytes + SetIcon(regularIconBytes) } // SetIcon sets the systray icon. @@ -52,6 +53,17 @@ func SetTemplateIcon(templateIconBytes []byte, regularIconBytes []byte) { // 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. @@ -154,7 +166,7 @@ func nativeStart() { } obj := conn.Object("org.kde.StatusNotifierWatcher", "/StatusNotifierWatcher") - call := obj.Call("org.kde.StatusNotifierWatcher.RegisterStatusNotifierItem", 0, name) + 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?") } @@ -164,6 +176,7 @@ func nativeStart() { 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. @@ -228,7 +241,7 @@ func argbForImage(img image.Image) []byte { } func createPropSpec() map[string]map[string]*prop.Prop { - return map[string]map[string]*prop.Prop{ + instance.props = map[string]map[string]*prop.Prop{ "org.kde.StatusNotifierItem": { "Status": { "Active", // Passive, Active or NeedsAttention @@ -279,4 +292,5 @@ func createPropSpec() map[string]map[string]*prop.Prop { nil, }, }} + return instance.props } diff --git a/vendor/github.com/fyne-io/systray/systray_menu_linux.go b/vendor/fyne.io/systray/systray_menu_linux.go similarity index 98% rename from vendor/github.com/fyne-io/systray/systray_menu_linux.go rename to vendor/fyne.io/systray/systray_menu_linux.go index 6c27ea90e1..727ef23d25 100644 --- a/vendor/github.com/fyne-io/systray/systray_menu_linux.go +++ b/vendor/fyne.io/systray/systray_menu_linux.go @@ -3,9 +3,10 @@ package systray import ( "log" - "github.com/fyne-io/systray/internal/generated/menu" "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. diff --git a/vendor/github.com/fyne-io/systray/systray_windows.go b/vendor/fyne.io/systray/systray_windows.go similarity index 100% rename from vendor/github.com/fyne-io/systray/systray_windows.go rename to vendor/fyne.io/systray/systray_windows.go diff --git a/vendor/github.com/godbus/dbus/v5/introspect/introspect.go b/vendor/github.com/godbus/dbus/v5/introspect/introspect.go index b06c3f1cf2..8ee61055ac 100644 --- a/vendor/github.com/godbus/dbus/v5/introspect/introspect.go +++ b/vendor/github.com/godbus/dbus/v5/introspect/introspect.go @@ -50,7 +50,7 @@ type Interface struct { Annotations []Annotation `xml:"annotation"` } -// Method describes a Method on an Interface as retured by an introspection. +// Method describes a Method on an Interface as returned by an introspection. type Method struct { Name string `xml:"name,attr"` Args []Arg `xml:"arg"` diff --git a/vendor/github.com/godbus/dbus/v5/prop/prop.go b/vendor/github.com/godbus/dbus/v5/prop/prop.go index 84c7ce1407..2c2a61321e 100644 --- a/vendor/github.com/godbus/dbus/v5/prop/prop.go +++ b/vendor/github.com/godbus/dbus/v5/prop/prop.go @@ -126,7 +126,9 @@ 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. + // 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. @@ -143,6 +145,24 @@ type Prop struct { 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 @@ -155,7 +175,7 @@ type Change struct { // using the org.freedesktop.DBus.Properties interface. It is safe for // concurrent use by multiple goroutines. type Properties struct { - m map[string]map[string]*Prop + m Map mut sync.RWMutex conn *dbus.Conn path dbus.ObjectPath @@ -165,7 +185,7 @@ type Properties struct { // swallowing the error, shouldn't be used. // // Deprecated: use Export instead. -func New(conn *dbus.Conn, path dbus.ObjectPath, props map[string]map[string]*Prop) *Properties { +func New(conn *dbus.Conn, path dbus.ObjectPath, props Map) *Properties { p, err := Export(conn, path, props) if err != nil { return nil @@ -178,15 +198,33 @@ func New(conn *dbus.Conn, path dbus.ObjectPath, props map[string]map[string]*Pro // 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[string]map[string]*Prop, + conn *dbus.Conn, path dbus.ObjectPath, props Map, ) (*Properties, error) { - p := &Properties{m: props, conn: conn, path: path} + 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() @@ -199,7 +237,7 @@ func (p *Properties) Get(iface, property string) (dbus.Variant, *dbus.Error) { if !ok { return dbus.Variant{}, ErrPropNotFound } - return dbus.MakeVariant(prop.Value), nil + return dbus.MakeVariant(reflect.ValueOf(prop.Value).Elem().Interface()), nil } // GetAll implements org.freedesktop.DBus.Properties.GetAll. @@ -212,7 +250,7 @@ func (p *Properties) GetAll(iface string) (map[string]dbus.Variant, *dbus.Error) } rm := make(map[string]dbus.Variant, len(m)) for k, v := range m { - rm[k] = dbus.MakeVariant(v.Value) + rm[k] = dbus.MakeVariant(reflect.ValueOf(v.Value).Elem().Interface()) } return rm, nil } @@ -222,7 +260,7 @@ func (p *Properties) GetAll(iface string) (map[string]dbus.Variant, *dbus.Error) func (p *Properties) GetMust(iface, property string) interface{} { p.mut.RLock() defer p.mut.RUnlock() - return p.m[iface][property].Value + return reflect.ValueOf(p.m[iface][property].Value).Elem().Interface() } // Introspection returns the introspection data that represents the properties @@ -232,20 +270,8 @@ func (p *Properties) Introspection(iface string) []introspect.Property { defer p.mut.RUnlock() m := p.m[iface] s := make([]introspect.Property, 0, len(m)) - for k, v := range m { - p := introspect.Property{Name: k, Type: dbus.SignatureOf(v.Value).String()} - if v.Writable { - p.Access = "readwrite" - } else { - p.Access = "read" - } - p.Annotations = []introspect.Annotation{ - { - Name: "org.freedesktop.DBus.Property.EmitsChangedSignal", - Value: v.Emit.String(), - }, - } - s = append(s, p) + for name, prop := range m { + s = append(s, prop.Introspection(name)) } return s } @@ -254,9 +280,6 @@ func (p *Properties) Introspection(iface string) []introspect.Property { // must already be locked. func (p *Properties) set(iface, property string, v interface{}) error { prop := p.m[iface][property] - if reflect.ValueOf(prop.Value).Kind() != reflect.Ptr { - prop.Value = reflect.New(reflect.TypeOf(prop.Value)).Interface() - } err := dbus.Store([]interface{}{v}, prop.Value) if err != nil { return err @@ -318,9 +341,8 @@ func (p *Properties) Set(iface, property string, newv dbus.Variant) *dbus.Error func (p *Properties) SetMust(iface, property string, v interface{}) { p.mut.Lock() defer p.mut.Unlock() // unlock in case of panic - prop := p.m[iface][property] - prop.Value = v - if err := p.emitChange(iface, property); err != nil { + err := p.set(iface, property, v) + if err != nil { panic(err) } } diff --git a/vendor/modules.txt b/vendor/modules.txt index a5aca76f72..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 @@ -26,11 +31,6 @@ github.com/fyne-io/gl-js # github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504 ## explicit github.com/fyne-io/glfw-js -# github.com/fyne-io/systray v1.1.1-0.20220306152635-ed046166dd6b -## explicit -github.com/fyne-io/systray -github.com/fyne-io/systray/internal/generated/menu -github.com/fyne-io/systray/internal/generated/notifier # github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 ## explicit github.com/go-gl/gl/v2.1/gl