diff --git a/v3/examples/systray/main.go b/v3/examples/systray/main.go
index d1587de1..2767c43e 100644
--- a/v3/examples/systray/main.go
+++ b/v3/examples/systray/main.go
@@ -2,6 +2,7 @@ package main
import (
_ "embed"
+ "fmt"
"log"
"runtime"
@@ -71,6 +72,19 @@ func main() {
println("Click me!")
ctx.ClickedMenuItem().SetLabel("Disabled!").SetEnabled(false)
})
+ myMenu.AddSeparator()
+ // Callbacks can be shared. This is useful for radio groups
+ radioCallback := func(ctx *application.Context) {
+ menuItem := ctx.ClickedMenuItem()
+ menuItem.SetLabel(menuItem.Label() + "!")
+ fmt.Println("radioCallback: ")
+ }
+
+ // Radio groups are created implicitly by placing radio items next to each other in a menu
+ myMenu.AddRadio("Radio 1", true).OnClick(radioCallback)
+ myMenu.AddRadio("Radio 2", false).OnClick(radioCallback)
+ myMenu.AddRadio("Radio 3", false).OnClick(radioCallback)
+
myMenu.AddSeparator()
myMenu.Add("Quit").OnClick(func(ctx *application.Context) {
app.Quit()
diff --git a/v3/go.mod b/v3/go.mod
index 4ae5c50c..9936b7cd 100644
--- a/v3/go.mod
+++ b/v3/go.mod
@@ -8,6 +8,7 @@ require (
github.com/go-git/go-git/v5 v5.3.0
github.com/go-ole/go-ole v1.2.6
github.com/go-task/task/v3 v3.31.0
+ github.com/godbus/dbus/v5 v5.1.0
github.com/google/go-cmp v0.5.9
github.com/google/uuid v1.3.0
github.com/gorilla/pat v1.0.1
diff --git a/v3/go.sum b/v3/go.sum
index 4e1fb37c..ec645207 100644
--- a/v3/go.sum
+++ b/v3/go.sum
@@ -119,6 +119,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZ
github.com/go-task/task/v3 v3.31.0 h1:o6iyj9gPJXxvxPi/u/l8e025PmM2BqKgtLNPS2i7hV4=
github.com/go-task/task/v3 v3.31.0/go.mod h1:/CPDAu9nS3+soqY/e1tTrSo/zxk76lnljEV9aBTeKrg=
github.com/goccy/go-json v0.9.6/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
+github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
diff --git a/v3/internal/dbus/DbusMenu.xml b/v3/internal/dbus/DbusMenu.xml
new file mode 100644
index 00000000..db695984
--- /dev/null
+++ b/v3/internal/dbus/DbusMenu.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/v3/internal/dbus/README.md b/v3/internal/dbus/README.md
new file mode 100644
index 00000000..d2cfce10
--- /dev/null
+++ b/v3/internal/dbus/README.md
@@ -0,0 +1,5 @@
+//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
+
diff --git a/v3/internal/dbus/StatusNotifierItem.xml b/v3/internal/dbus/StatusNotifierItem.xml
new file mode 100644
index 00000000..1093d3d1
--- /dev/null
+++ b/v3/internal/dbus/StatusNotifierItem.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/v3/internal/dbus/dbus.go b/v3/internal/dbus/dbus.go
new file mode 100644
index 00000000..4e4a3adb
--- /dev/null
+++ b/v3/internal/dbus/dbus.go
@@ -0,0 +1,4 @@
+package dbus
+
+//go:generate dbus-codegen-go -prefix org.kde -package notifier -output notifier/status_notifier_item.go StatusNotifierItem.xml
+//go:generate dbus-codegen-go -prefix com.canonical -package menu -output menu/dbus_menu.go DbusMenu.xml
diff --git a/v3/internal/dbus/generate.sh b/v3/internal/dbus/generate.sh
new file mode 100644
index 00000000..03716e52
--- /dev/null
+++ b/v3/internal/dbus/generate.sh
@@ -0,0 +1,2 @@
+dbus-codegen-go -prefix com.canonical -package menu -output dbus_menu.go DbusMenu.xml
+dbus-codegen-go -prefix org.kde -package notifier -output status_notifier_item.go StatusNotifierItem.xml
diff --git a/v3/internal/dbus/menu/.keep b/v3/internal/dbus/menu/.keep
new file mode 100644
index 00000000..e69de29b
diff --git a/v3/internal/dbus/menu/dbus_menu.go b/v3/internal/dbus/menu/dbus_menu.go
new file mode 100644
index 00000000..cbf4fd32
--- /dev/null
+++ b/v3/internal/dbus/menu/dbus_menu.go
@@ -0,0 +1,483 @@
+// 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/v3/internal/dbus/notifier/.keep b/v3/internal/dbus/notifier/.keep
new file mode 100644
index 00000000..e69de29b
diff --git a/v3/internal/dbus/notifier/status_notifier_item.go b/v3/internal/dbus/notifier/status_notifier_item.go
new file mode 100644
index 00000000..99891644
--- /dev/null
+++ b/v3/internal/dbus/notifier/status_notifier_item.go
@@ -0,0 +1,636 @@
+// 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"},
+ {Name: "ToolTip", Type: "(sa(iiay)ss)", Access: "read", Annotations: []introspect.Annotation{
+ {Name: "org.qtproject.QtDBus.QtTypeName", Value: "KDbusToolTipStruct"},
+ }},
+ },
+ 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
+}
+
+// GetToolTip gets org.kde.StatusNotifierItem.ToolTip property.
+//
+// Annotations:
+//
+// @org.qtproject.QtDBus.QtTypeName = KDbusToolTipStruct
+func (o *StatusNotifierItem) GetToolTip(ctx context.Context) (toolTip struct {
+ V0 string
+ V1 []struct {
+ V0 int32
+ V1 int32
+ V2 []byte
+ }
+ V2 string
+ V3 string
+}, err error) {
+ err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "ToolTip").Store(&toolTip)
+ 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/v3/pkg/application/image.go b/v3/pkg/application/image.go
index fd77e06f..81c51973 100644
--- a/v3/pkg/application/image.go
+++ b/v3/pkg/application/image.go
@@ -18,3 +18,20 @@ func pngToImage(data []byte) (*image.RGBA, error) {
draw.Draw(rgba, bounds, img, bounds.Min, draw.Src)
return rgba, nil
}
+
+func ToARGB(img *image.RGBA) (int, int, []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 w, h, data
+}
diff --git a/v3/pkg/application/menuitem.go b/v3/pkg/application/menuitem.go
index bdbd7367..762d2ca6 100644
--- a/v3/pkg/application/menuitem.go
+++ b/v3/pkg/application/menuitem.go
@@ -72,6 +72,7 @@ func newMenuItem(label string) *MenuItem {
func newMenuItemSeparator() *MenuItem {
result := &MenuItem{
+ id: uint(atomic.AddUintptr(&menuItemID, 1)),
itemType: separator,
}
return result
@@ -199,7 +200,9 @@ func (m *MenuItem) handleClick() {
if m.itemType == checkbox {
m.checked = !m.checked
ctx.withChecked(m.checked)
- m.impl.setChecked(m.checked)
+ if m.impl != nil {
+ m.impl.setChecked(m.checked)
+ }
}
if m.itemType == radio {
for _, member := range m.radioGroupMembers {
@@ -210,7 +213,9 @@ func (m *MenuItem) handleClick() {
}
m.checked = true
ctx.withChecked(true)
- m.impl.setChecked(true)
+ if m.impl != nil {
+ m.impl.setChecked(true)
+ }
}
if m.callback != nil {
go m.callback(ctx)
diff --git a/v3/pkg/application/systemtray_linux.go b/v3/pkg/application/systemtray_linux.go
index f22bd7b5..ac9b2981 100644
--- a/v3/pkg/application/systemtray_linux.go
+++ b/v3/pkg/application/systemtray_linux.go
@@ -1,7 +1,28 @@
//go:build linux
+/*
+Portions of this code are derived from the project:
+- https://github.com/fyne-io/systray
+*/
package application
+import (
+ "fmt"
+ "os"
+
+ "github.com/godbus/dbus/v5"
+ "github.com/godbus/dbus/v5/introspect"
+ "github.com/godbus/dbus/v5/prop"
+ "github.com/wailsapp/wails/v3/internal/dbus/menu"
+ "github.com/wailsapp/wails/v3/internal/dbus/notifier"
+ "github.com/wailsapp/wails/v3/pkg/icons"
+)
+
+const (
+ itemPath = "/StatusNotifierItem"
+ menuPath = "/StatusNotifierMenu"
+)
+
type linuxSystemTray struct {
id uint
label string
@@ -10,13 +31,161 @@ type linuxSystemTray struct {
iconPosition int
isTemplateIcon bool
+
+ quitChan chan struct{}
+ conn *dbus.Conn
+ props *prop.Properties
+ menuProps *prop.Properties
+
+ menuVersion uint32 // need to bump this anytime we change anything
+ itemMap map[int32]*systrayMenuItem
+}
+
+// dbusMenu is a named struct to map into generated bindings.
+// It represents the layout of a menu item
+type dbusMenu = struct {
+ V0 int32 // items' unique id
+ V1 map[string]dbus.Variant // layout properties
+ V2 []dbus.Variant // child menu(s)
+}
+
+// systrayMenuItem is an implementation of the menuItemImpl interface
+type systrayMenuItem struct {
+ sysTray *linuxSystemTray
+ menuItem *MenuItem
+ dbusItem *dbusMenu
+}
+
+func (s *systrayMenuItem) setTooltip(v string) {
+ s.dbusItem.V1["tooltip"] = dbus.MakeVariant(v)
+ s.sysTray.update(s)
+}
+
+func (s *systrayMenuItem) setLabel(v string) {
+ s.dbusItem.V1["label"] = dbus.MakeVariant(v)
+ s.sysTray.update(s)
+}
+
+func (s *systrayMenuItem) setDisabled(disabled bool) {
+ v := dbus.MakeVariant(!disabled)
+ if s.dbusItem.V1["toggle-state"] != v {
+ s.dbusItem.V1["enabled"] = v
+ s.sysTray.update(s)
+ }
+}
+
+func (s *systrayMenuItem) setChecked(checked bool) {
+ v := dbus.MakeVariant(0)
+ if checked {
+ v = dbus.MakeVariant(1)
+ }
+ if s.dbusItem.V1["toggle-state"] != v {
+ s.dbusItem.V1["toggle-state"] = v
+ s.sysTray.update(s)
+ }
+}
+
+func (s *systrayMenuItem) setAccelerator(accelerator *accelerator) {}
+func (s *systrayMenuItem) setHidden(hidden bool) {
+ s.dbusItem.V1["visible"] = dbus.MakeVariant(!hidden)
+ s.sysTray.update(s)
+}
+
+func (i systrayMenuItem) dbus() *dbusMenu {
+ item := &dbusMenu{
+ V0: int32(i.menuItem.id),
+ V1: map[string]dbus.Variant{},
+ V2: []dbus.Variant{},
+ }
+ return item
}
func (s *linuxSystemTray) setIconPosition(position int) {
s.iconPosition = position
}
+func (s *linuxSystemTray) processMenu(menu *Menu, parentId int32) {
+ parentItem, ok := s.itemMap[int32(parentId)]
+ if !ok {
+ return
+ }
+ parent := parentItem.dbusItem
+
+ for _, item := range menu.items {
+ menuItem := &dbusMenu{
+ V0: int32(item.id),
+ V1: map[string]dbus.Variant{},
+ V2: []dbus.Variant{},
+ }
+ item.impl = &systrayMenuItem{
+ sysTray: s,
+ menuItem: item,
+ dbusItem: menuItem,
+ }
+ s.itemMap[int32(item.id)] = item.impl.(*systrayMenuItem)
+
+ menuItem.V1["enabled"] = dbus.MakeVariant(!item.disabled)
+ menuItem.V1["visible"] = dbus.MakeVariant(!item.hidden)
+ if item.label != "" {
+ menuItem.V1["label"] = dbus.MakeVariant(item.label)
+ }
+ switch item.itemType {
+ case checkbox:
+ menuItem.V1["toggle-type"] = dbus.MakeVariant("checkmark")
+ v := dbus.MakeVariant(0)
+ if item.checked {
+ v = dbus.MakeVariant(1)
+ }
+ menuItem.V1["toggle-state"] = v
+ case submenu:
+ menuItem.V1["children-display"] = dbus.MakeVariant("submenu")
+ s.processMenu(item.submenu, int32(item.id))
+ case text:
+ case radio:
+ menuItem.V1["toggle-type"] = dbus.MakeVariant("radio")
+ v := dbus.MakeVariant(0)
+ if item.checked {
+ v = dbus.MakeVariant(1)
+ }
+ menuItem.V1["toggle-state"] = v
+ case separator:
+ menuItem.V1["type"] = dbus.MakeVariant("separator")
+ }
+
+ parent.V2 = append(parent.V2, dbus.MakeVariant(menuItem))
+ }
+}
+
+func (s *linuxSystemTray) refresh() {
+ s.menuVersion++
+ if err := s.menuProps.Set("com.canonical.dbusmenu", "Version",
+ dbus.MakeVariant(s.menuVersion)); err != nil {
+ globalApplication.error("systray error: failed to update menu version: %v", err)
+ return
+ }
+ if err := menu.Emit(s.conn, &menu.Dbusmenu_LayoutUpdatedSignal{
+ Path: menuPath,
+ Body: &menu.Dbusmenu_LayoutUpdatedSignalBody{
+ Revision: s.menuVersion,
+ },
+ }); err != nil {
+ globalApplication.error("systray error: failed to emit layout updated signal: %v", err)
+ }
+}
+
func (s *linuxSystemTray) setMenu(menu *Menu) {
+ s.itemMap = map[int32]*systrayMenuItem{}
+ // our root menu element
+ s.itemMap[0] = &systrayMenuItem{
+ menuItem: nil,
+ dbusItem: &dbusMenu{
+ V0: int32(0),
+ V1: map[string]dbus.Variant{},
+ V2: []dbus.Variant{},
+ },
+ }
+ menu.processRadioGroups()
+ s.processMenu(menu, 0)
s.menu = menu
}
@@ -25,6 +194,7 @@ func (s *linuxSystemTray) positionWindow(window *WebviewWindow, offset int) erro
}
func (s *linuxSystemTray) getScreen() (*Screen, error) {
+ // FIXME: How do we get the screen we are on?
return &Screen{}, nil
}
@@ -33,40 +203,446 @@ func (s *linuxSystemTray) bounds() (*Rect, error) {
}
func (s *linuxSystemTray) run() {
- globalApplication.error("linuxSystemTray.run() - not implemented")
+ conn, err := dbus.SessionBus()
+ if err != nil {
+ globalApplication.error("systray error: failed to connect to DBus: %v\n", err)
+ return
+ }
+ err = notifier.ExportStatusNotifierItem(conn, itemPath, s)
+ if err != nil {
+ globalApplication.error("systray error: failed to export status notifier item: %v\n", err)
+ }
+
+ err = menu.ExportDbusmenu(conn, menuPath, s)
+ if err != nil {
+ globalApplication.error("systray error: failed to export status notifier menu: %v", err)
+ return
+ }
+
+ 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 {
+ globalApplication.error("systray error: failed to request name: %s\n", err)
+ // it's not critical error: continue
+ }
+ props, err := prop.Export(conn, itemPath, s.createPropSpec())
+ if err != nil {
+ globalApplication.error("systray error: failed to export notifier item properties to bus: %s\n", err)
+ return
+ }
+ menuProps, err := prop.Export(conn, menuPath, s.createMenuPropSpec())
+ if err != nil {
+ globalApplication.error("systray error: failed to export notifier menu properties to bus: %s\n", err)
+ return
+ }
+
+ s.conn = conn
+ s.props = props
+ s.menuProps = menuProps
+
+ node := introspect.Node{
+ Name: itemPath,
+ Interfaces: []introspect.Interface{
+ introspect.IntrospectData,
+ prop.IntrospectData,
+ notifier.IntrospectDataStatusNotifierItem,
+ },
+ }
+ err = conn.Export(introspect.NewIntrospectable(&node), itemPath, "org.freedesktop.DBus.Introspectable")
+ if err != nil {
+ globalApplication.error("systray error: failed to export node introspection: %s\n", err)
+ 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 {
+ globalApplication.error("systray error: failed to export menu node introspection: %s\n", err)
+ return
+ }
+ s.setLabel(s.label)
+ go func() {
+ s.register()
+
+ if err := conn.AddMatchSignal(
+ dbus.WithMatchObjectPath("/org/freedesktop/DBus"),
+ dbus.WithMatchInterface("org.freedesktop.DBus"),
+ dbus.WithMatchSender("org.freedesktop.DBus"),
+ dbus.WithMatchMember("NameOwnerChanged"),
+ dbus.WithMatchArg(0, "org.kde.StatusNotifierWatcher"),
+ ); err != nil {
+ globalApplication.error("systray error: failed to register signal matching: %v\n", err)
+ return
+ }
+
+ sc := make(chan *dbus.Signal, 10)
+ conn.Signal(sc)
+
+ for {
+ select {
+ case sig := <-sc:
+ if sig == nil {
+ return // We get a nil signal when closing the window.
+ }
+ // sig.Body has the args, which are [name old_owner new_owner]
+ if sig.Body[2] != "" {
+ s.register()
+ }
+
+ case <-s.quitChan:
+ return
+ }
+ }
+ }()
+ s.setMenu(s.menu)
}
func (s *linuxSystemTray) setIcon(icon []byte) {
+
s.icon = icon
+
+ iconPx, err := iconToPX(icon)
+ if err != nil {
+ globalApplication.error("systray error: failed to convert icon to PX: %s\n", err)
+ return
+ }
+ s.props.SetMust("org.kde.StatusNotifierItem", "IconPixmap", iconPx)
+
+ if s.conn == nil {
+ return
+ }
+
+ err = notifier.Emit(s.conn, ¬ifier.StatusNotifierItem_NewIconSignal{
+ Path: itemPath,
+ Body: ¬ifier.StatusNotifierItem_NewIconSignalBody{},
+ })
+ if err != nil {
+ globalApplication.error("systray error: failed to emit new icon signal: %s\n", err)
+ return
+ }
}
func (s *linuxSystemTray) setDarkModeIcon(icon []byte) {
- s.icon = icon
+ s.setIcon(icon)
}
func (s *linuxSystemTray) setTemplateIcon(icon []byte) {
s.icon = icon
s.isTemplateIcon = true
+ s.setIcon(icon)
}
func newSystemTrayImpl(s *SystemTray) systemTrayImpl {
+ label := s.label
+ if label == "" {
+ label = "Wails"
+ }
+
return &linuxSystemTray{
id: s.id,
- label: s.label,
+ label: label,
icon: s.icon,
menu: s.menu,
iconPosition: s.iconPosition,
isTemplateIcon: s.isTemplateIcon,
+ quitChan: make(chan struct{}),
+ menuVersion: 1,
}
}
func (s *linuxSystemTray) openMenu() {
+ // FIXME: Use DBUS to open?
+ globalApplication.info("systray error: openMenu not implemented on Linux")
}
func (s *linuxSystemTray) setLabel(label string) {
s.label = label
+
+ if err := s.props.Set("org.kde.StatusNotifierItem", "Title", dbus.MakeVariant(label)); err != nil {
+ globalApplication.error("systray error: failed to set Title prop: %s\n", err)
+ return
+ }
+
+ if s.conn == nil {
+ return
+ }
+
+ if err := notifier.Emit(s.conn, ¬ifier.StatusNotifierItem_NewTitleSignal{
+ Path: itemPath,
+ Body: ¬ifier.StatusNotifierItem_NewTitleSignalBody{},
+ }); err != nil {
+ globalApplication.error("systray error: failed to emit new title signal: %s", err)
+ return
+ }
+
}
func (s *linuxSystemTray) destroy() {
- // Remove the status item from the status bar and its associated menu
+ close(s.quitChan)
+}
+
+func (s *linuxSystemTray) createMenuPropSpec() map[string]map[string]*prop.Prop {
+ return map[string]map[string]*prop.Prop{
+ "com.canonical.dbusmenu": {
+ // update version each time we change something
+ "Version": {
+ Value: s.menuVersion,
+ Writable: true,
+ Emit: prop.EmitTrue,
+ Callback: nil,
+ },
+ "TextDirection": {
+ Value: "ltr",
+ Writable: false,
+ Emit: prop.EmitTrue,
+ Callback: nil,
+ },
+ "Status": {
+ Value: "normal",
+ Writable: false,
+ Emit: prop.EmitTrue,
+ Callback: nil,
+ },
+ "IconThemePath": {
+ Value: []string{},
+ Writable: false,
+ Emit: prop.EmitTrue,
+ Callback: nil,
+ },
+ },
+ }
+}
+
+func (s *linuxSystemTray) createPropSpec() map[string]map[string]*prop.Prop {
+ props := map[string]*prop.Prop{
+ "Status": {
+ Value: "Active", // Passive, Active or NeedsAttention
+ Writable: false,
+ Emit: prop.EmitTrue,
+ Callback: nil,
+ },
+ "Title": {
+ Value: s.label,
+ Writable: true,
+ Emit: prop.EmitTrue,
+ Callback: nil,
+ },
+ "Id": {
+ Value: s.label,
+ Writable: false,
+ Emit: prop.EmitTrue,
+ Callback: nil,
+ },
+ "Category": {
+ Value: "ApplicationStatus",
+ Writable: false,
+ Emit: prop.EmitTrue,
+ Callback: nil,
+ },
+ "IconName": {
+ Value: "",
+ Writable: false,
+ Emit: prop.EmitTrue,
+ Callback: nil,
+ },
+ "IconThemePath": {
+ Value: "",
+ Writable: false,
+ Emit: prop.EmitTrue,
+ Callback: nil,
+ },
+ "ItemIsMenu": {
+ Value: true,
+ Writable: false,
+ Emit: prop.EmitTrue,
+ Callback: nil,
+ },
+ "Menu": {
+ Value: dbus.ObjectPath(menuPath),
+ Writable: true,
+ Emit: prop.EmitTrue,
+ Callback: nil,
+ },
+ "ToolTip": {
+ Value: tooltip{V2: s.label},
+ Writable: true,
+ Emit: prop.EmitTrue,
+ Callback: nil,
+ },
+ }
+
+ if s.icon == nil {
+ // set a basic default one if one isn't set
+ s.icon = icons.WailsLogoWhiteTransparent
+ }
+ if iconPx, err := iconToPX(s.icon); err == nil {
+ props["IconPixmap"] = &prop.Prop{
+ Value: []PX{iconPx},
+ Writable: true,
+ Emit: prop.EmitTrue,
+ Callback: nil,
+ }
+ }
+
+ return map[string]map[string]*prop.Prop{
+ "org.kde.StatusNotifierItem": props,
+ }
+}
+
+func (s *linuxSystemTray) update(i *systrayMenuItem) {
+ s.itemMap[int32(i.menuItem.id)] = i
+ s.refresh()
+}
+
+func (s *linuxSystemTray) register() bool {
+ obj := s.conn.Object("org.kde.StatusNotifierWatcher", "/StatusNotifierWatcher")
+ call := obj.Call("org.kde.StatusNotifierWatcher.RegisterStatusNotifierItem", 0, itemPath)
+ if call.Err != nil {
+ globalApplication.error("systray error: failed to register: %v\n", call.Err)
+ return false
+ }
+
+ return true
+}
+
+type PX struct {
+ W, H int
+ Pix []byte
+}
+
+func iconToPX(icon []byte) (PX, error) {
+ img, err := pngToImage(icon)
+ if err != nil {
+ return PX{}, err
+ }
+ w, h, bytes := ToARGB(img)
+ return PX{
+ W: w,
+ H: h,
+ Pix: bytes,
+ }, nil
+}
+
+// AboutToShow is an implementation of the com.canonical.dbusmenu.AboutToShow method.
+func (s *linuxSystemTray) AboutToShow(id int32) (needUpdate bool, err *dbus.Error) {
+ return
+}
+
+// AboutToShowGroup is an implementation of the com.canonical.dbusmenu.AboutToShowGroup method.
+func (s *linuxSystemTray) AboutToShowGroup(ids []int32) (updatesNeeded []int32, idErrors []int32, err *dbus.Error) {
+ return
+}
+
+// GetProperty is an implementation of the com.canonical.dbusmenu.GetProperty method.
+func (s *linuxSystemTray) GetProperty(id int32, name string) (value dbus.Variant, err *dbus.Error) {
+ if item, ok := s.itemMap[id]; ok {
+ if p, ok := item.dbusItem.V1[name]; ok {
+ return p, nil
+ }
+ }
+ return
+}
+
+// Event is com.canonical.dbusmenu.Event method.
+func (s *linuxSystemTray) Event(id int32, eventID string, data dbus.Variant, timestamp uint32) (err *dbus.Error) {
+ if eventID == "clicked" {
+ if item, ok := s.itemMap[id]; ok {
+ InvokeAsync(item.menuItem.handleClick)
+ }
+ }
+ return
+}
+
+// EventGroup is an implementation of the com.canonical.dbusmenu.EventGroup method.
+func (s *linuxSystemTray) EventGroup(events []struct {
+ V0 int32
+ V1 string
+ V2 dbus.Variant
+ V3 uint32
+}) (idErrors []int32, err *dbus.Error) {
+ for _, event := range events {
+ if event.V1 == "clicked" {
+ item, ok := s.itemMap[event.V0]
+ if ok {
+ InvokeAsync(item.menuItem.handleClick)
+ }
+ }
+ }
+ return
+}
+
+// GetGroupProperties is an implementation of the com.canonical.dbusmenu.GetGroupProperties method.
+func (s *linuxSystemTray) GetGroupProperties(ids []int32, propertyNames []string) (properties []struct {
+ V0 int32
+ V1 map[string]dbus.Variant
+}, err *dbus.Error) {
+ // FIXME: RLock?
+ /* instance.menuLock.Lock()
+ defer instance.menuLock.Unlock()
+ */
+ for _, id := range ids {
+ if m, ok := s.itemMap[id]; ok {
+ p := struct {
+ V0 int32
+ V1 map[string]dbus.Variant
+ }{
+ V0: m.dbusItem.V0,
+ V1: make(map[string]dbus.Variant, len(m.dbusItem.V1)),
+ }
+ for k, v := range m.dbusItem.V1 {
+ p.V1[k] = v
+ }
+ properties = append(properties, p)
+ }
+ }
+ return properties, nil
+}
+
+// GetLayout is an implementation of the com.canonical.dbusmenu.GetLayout method.
+func (s *linuxSystemTray) GetLayout(parentID int32, recursionDepth int32, propertyNames []string) (revision uint32, layout dbusMenu, err *dbus.Error) {
+ // FIXME: RLock?
+ if m, ok := s.itemMap[parentID]; ok {
+ return s.menuVersion, *m.dbusItem, nil
+ }
+
+ return
+}
+
+// Activate implements org.kde.StatusNotifierItem.Activate method.
+func (s *linuxSystemTray) Activate(x int32, y int32) (err *dbus.Error) {
+ fmt.Println("Activate", x, y)
+ return
+}
+
+// ContextMenu is org.kde.StatusNotifierItem.ContextMenu method
+func (s *linuxSystemTray) ContextMenu(x int32, y int32) (err *dbus.Error) {
+ fmt.Println("ContextMenu", x, y)
+ return
+}
+
+func (s *linuxSystemTray) Scroll(delta int32, orientation string) (err *dbus.Error) {
+ fmt.Println("Scroll", delta, orientation)
+ return
+}
+
+// SecondaryActivate implements org.kde.StatusNotifierItem.SecondaryActivate method.
+func (s *linuxSystemTray) SecondaryActivate(x int32, y int32) (err *dbus.Error) {
+ fmt.Println("SecondaryActivate", x, y)
+ return
+}
+
+// tooltip is our data for a tooltip property.
+// Param names need to match the generated code...
+type tooltip = struct {
+ V0 string // name
+ V1 []PX // icons
+ V2 string // title
+ V3 string // description
}