diff --git a/modules/systray/main.go b/modules/systray/main.go index 643b1058..5552925b 100644 --- a/modules/systray/main.go +++ b/modules/systray/main.go @@ -22,17 +22,18 @@ import ( "github.com/godbus/dbus/v5/introspect" "github.com/godbus/dbus/v5/prop" - "fyne.io/fyne/v2" - "fyne.io/fyne/v2/container" - deskDriver "fyne.io/fyne/v2/driver/desktop" - "fyne.io/fyne/v2/theme" - "fyne.io/fyne/v2/widget" "fyshos.com/fynedesk" "fyshos.com/fynedesk/internal/icon" "fyshos.com/fynedesk/modules/systray/generated/menu" "fyshos.com/fynedesk/modules/systray/generated/notifier" "fyshos.com/fynedesk/modules/systray/generated/watcher" wmtheme "fyshos.com/fynedesk/theme" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/container" + deskDriver "fyne.io/fyne/v2/driver/desktop" + "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" ) const ( @@ -56,14 +57,19 @@ type tray struct { menu *menu.Dbusmenu box *fyne.Container - nodes map[dbus.Sender]*widget.Button + nodes map[dbus.Sender]*node +} + +type node struct { + ico *widget.Button + ni *notifier.StatusNotifierItem } // NewTray creates a new module that will show a system tray in the status area func NewTray() fynedesk.Module { iconSize := wmtheme.NarrowBarWidth grid := container.New(collapsingGridWrap(fyne.NewSize(iconSize, iconSize))) - t := &tray{box: grid, nodes: make(map[dbus.Sender]*widget.Button)} + t := &tray{box: grid, nodes: make(map[dbus.Sender]*node)} conn, _ := dbus.ConnectSessionBus() t.conn = conn @@ -116,6 +122,7 @@ func NewTray() fynedesk.Module { } watchErr := t.conn.AddMatchSignal(dbus.WithMatchInterface("org.freedesktop.DBus"), dbus.WithMatchObjectPath("/org/freedesktop/DBus")) + _ = t.conn.AddMatchSignal(dbus.WithMatchInterface("org.kde.StatusNotifierItem")) if watchErr != nil { fyne.LogError("Failed to monitor systray name loss", watchErr) return t @@ -125,19 +132,25 @@ func NewTray() fynedesk.Module { t.conn.Signal(c) go func() { for v := range c { - if v.Name != "org.freedesktop.DBus.NameOwnerChanged" { + switch v.Name { + case "org.freedesktop.DBus.NameOwnerChanged": + name := v.Body[0] + newOwner := v.Body[2] + if newOwner == "" { + if item, ok := t.nodes[dbus.Sender(name.(string))]; ok { + t.box.Remove(item.ico) + t.box.Refresh() + } + } + case "org.kde.StatusNotifierItem.NewIcon": + item, ok := t.nodes[dbus.Sender(v.Sender)] + if ok { + t.updateIcon(item) + } + default: log.Println("Also", v.Name) continue } - - name := v.Body[0] - newOwner := v.Body[2] - if newOwner == "" { - if item, ok := t.nodes[dbus.Sender(name.(string))]; ok { - t.box.Remove(item) - t.box.Refresh() - } - } } }() @@ -150,8 +163,9 @@ func (t *tray) Destroy() { func (t *tray) RegisterStatusNotifierItem(service string, sender dbus.Sender) (err *dbus.Error) { ni := notifier.NewStatusNotifierItem(t.conn.Object(string(sender), dbus.ObjectPath(service))) - ico, ok := t.nodes[sender] + item, ok := t.nodes[sender] if !ok { + var ico *widget.Button ico = widget.NewButton("", func() { if m, err := ni.GetMenu(t.conn.Context()); err == nil { t.showMenu(string(sender), m, ico) @@ -164,40 +178,13 @@ func (t *tray) RegisterStatusNotifierItem(service string, sender dbus.Sender) (e } }) ico.Importance = widget.LowImportance - t.nodes[sender] = ico + item = &node{ico, ni} + t.nodes[sender] = item t.box.Add(ico) } - ic, _ := ni.GetIconPixmap(t.conn.Context()) - if len(ic) > 0 { - img := pixelsToImage(ic[0]) - unique := strconv.Itoa(resourceID) + ".png" - resourceID++ - w := &bytes.Buffer{} - _ = png.Encode(w, img) - ico.SetIcon(fyne.NewStaticResource(unique, w.Bytes())) - } else { - name, _ := ni.GetIconName(t.conn.Context()) - path, _ := ni.GetIconThemePath(t.conn.Context()) - fullPath := "" - if path != "" { - fullPath = filepath.Join(path, name+".png") - if _, err := os.Stat(fullPath); err != nil { // not found, search instead - fullPath = icon.FdoLookupIconPathInTheme("64", filepath.Join(path, "hicolor"), "", name) - } - } else { - fullPath = icon.FdoLookupIconPath("", 64, name) - } - img, err := ioutil.ReadFile(fullPath) - if err != nil { - fyne.LogError("Failed to load status icon", err) - ico.SetIcon(wmtheme.BrokenImageIcon) - } else { - ico.SetIcon(fyne.NewStaticResource(name, img)) - } - } - - ico.Refresh() + t.nodes[sender].ni = ni + t.updateIcon(item) t.box.Refresh() return nil @@ -316,6 +303,37 @@ func (t *tray) showMenu(sender string, name dbus.ObjectPath, from fyne.CanvasObj fynedesk.Instance().WindowManager().ShowOverlay(w, size, pos) } +func (t *tray) updateIcon(i *node) { + ic, _ := i.ni.GetIconPixmap(t.conn.Context()) + if len(ic) > 0 { + img := pixelsToImage(ic[0]) + unique := strconv.Itoa(resourceID) + ".png" + resourceID++ + w := &bytes.Buffer{} + _ = png.Encode(w, img) + i.ico.SetIcon(fyne.NewStaticResource(unique, w.Bytes())) + } else { + name, _ := i.ni.GetIconName(t.conn.Context()) + path, _ := i.ni.GetIconThemePath(t.conn.Context()) + fullPath := "" + if path != "" { + fullPath = filepath.Join(path, name+".png") + if _, err := os.Stat(fullPath); err != nil { // not found, search instead + fullPath = icon.FdoLookupIconPathInTheme("64", filepath.Join(path, "hicolor"), "", name) + } + } else { + fullPath = icon.FdoLookupIconPath("", 64, name) + } + img, err := ioutil.ReadFile(fullPath) + if err != nil { + fyne.LogError("Failed to load status icon", err) + i.ico.SetIcon(wmtheme.BrokenImageIcon) + } else { + i.ico.SetIcon(fyne.NewStaticResource(name, img)) + } + } +} + func createPropSpec() map[string]map[string]*prop.Prop { return map[string]map[string]*prop.Prop{ "org.kde.StatusNotifierWatcher": {