Migrate notifier from portmaster-ui to cmds/notifier, remove some duplicated code, move assets to assets/data and add a small go package in assets to allow embedding icons
This commit is contained in:
154
cmds/notifier/notify_linux.go
Normal file
154
cmds/notifier/notify_linux.go
Normal file
@@ -0,0 +1,154 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
notify "github.com/dhaavi/go-notify"
|
||||
"github.com/safing/portbase/log"
|
||||
)
|
||||
|
||||
type NotificationID uint32
|
||||
|
||||
var (
|
||||
capabilities notify.Capabilities
|
||||
notifsByID sync.Map
|
||||
)
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
capabilities, err = notify.GetCapabilities()
|
||||
if err != nil {
|
||||
log.Errorf("failed to get notification system capabilities: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func handleActions(ctx context.Context, actions chan notify.Signal) {
|
||||
mainWg.Add(1)
|
||||
defer mainWg.Done()
|
||||
|
||||
listenForNotifications:
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case sig := <-actions:
|
||||
if sig.Name != "org.freedesktop.Notifications.ActionInvoked" {
|
||||
// we don't care for anything else (dismissed, closed)
|
||||
continue listenForNotifications
|
||||
}
|
||||
|
||||
// get notification by system ID
|
||||
n, ok := notifsByID.LoadAndDelete(NotificationID(sig.ID))
|
||||
|
||||
if !ok {
|
||||
continue listenForNotifications
|
||||
}
|
||||
|
||||
notification := n.(*Notification)
|
||||
|
||||
log.Tracef("notify: received signal: %+v", sig)
|
||||
if sig.ActionKey != "" {
|
||||
// send action
|
||||
if ok {
|
||||
notification.Lock()
|
||||
notification.SelectAction(sig.ActionKey)
|
||||
notification.Unlock()
|
||||
}
|
||||
} else {
|
||||
log.Tracef("notify: notification clicked: %+v", sig)
|
||||
// Global action invoked, start the app
|
||||
launchApp()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func actionListener() {
|
||||
actions := make(chan notify.Signal, 100)
|
||||
|
||||
go handleActions(mainCtx, actions)
|
||||
|
||||
err := notify.SignalNotify(mainCtx, actions)
|
||||
if err != nil && err != context.Canceled {
|
||||
log.Errorf("notify: signal listener failed: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Show shows the notification.
|
||||
func (n *Notification) Show() {
|
||||
sysN := notify.NewNotification("Portmaster", n.Message)
|
||||
// see https://developer.gnome.org/notification-spec/
|
||||
|
||||
// The optional name of the application sending the notification.
|
||||
// Can be blank.
|
||||
sysN.AppName = "Portmaster"
|
||||
|
||||
// The optional notification ID that this notification replaces.
|
||||
sysN.ReplacesID = uint32(n.systemID)
|
||||
|
||||
// The optional program icon of the calling application.
|
||||
// sysN.AppIcon string
|
||||
|
||||
// The summary text briefly describing the notification.
|
||||
// Summary string (arg 1)
|
||||
|
||||
// The optional detailed body text.
|
||||
// Body string (arg 2)
|
||||
|
||||
// The actions send a request message back to the notification client
|
||||
// when invoked.
|
||||
// sysN.Actions []string
|
||||
if capabilities.Actions {
|
||||
sysN.Actions = make([]string, 0, len(n.AvailableActions)*2)
|
||||
for _, action := range n.AvailableActions {
|
||||
if IsSupportedAction(*action) {
|
||||
sysN.Actions = append(sysN.Actions, action.ID)
|
||||
sysN.Actions = append(sysN.Actions, action.Text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set Portmaster icon.
|
||||
iconLocation, err := ensureAppIcon()
|
||||
if err != nil {
|
||||
log.Warningf("notify: failed to write icon: %s", err)
|
||||
}
|
||||
sysN.AppIcon = iconLocation
|
||||
|
||||
// TODO: Use hints to display icon of affected app.
|
||||
// Hints are a way to provide extra data to a notification server.
|
||||
// sysN.Hints = make(map[string]interface{})
|
||||
|
||||
// The timeout time in milliseconds since the display of the
|
||||
// notification at which the notification should automatically close.
|
||||
// sysN.Timeout int32
|
||||
|
||||
newID, err := sysN.Show()
|
||||
if err != nil {
|
||||
log.Warningf("notify: failed to show notification %s", n.EventID)
|
||||
return
|
||||
}
|
||||
|
||||
notifsByID.Store(NotificationID(newID), n)
|
||||
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
n.systemID = NotificationID(newID)
|
||||
}
|
||||
|
||||
// Cancel cancels the notification.
|
||||
func (n *Notification) Cancel() {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
// TODO: could a ID of 0 be valid?
|
||||
if n.systemID != 0 {
|
||||
err := notify.CloseNotification(uint32(n.systemID))
|
||||
if err != nil {
|
||||
log.Warningf("notify: failed to close notification %s/%d", n.EventID, n.systemID)
|
||||
}
|
||||
notifsByID.Delete(n.systemID)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user