aboutsummaryrefslogtreecommitdiff
path: root/srv/notifServer.go
diff options
context:
space:
mode:
authorCaroline Larimore <caroline@larimo.re>2025-01-30 21:47:09 -0800
committerCaroline Larimore <caroline@larimo.re>2025-01-30 21:47:09 -0800
commit4b5ff500b97cb86a5fbaaf5f3c5f9a004d1334f0 (patch)
treebd1db5355acb7edb46294c67fc809036ba07c59f /srv/notifServer.go
parent784ee51b9613cac5764c9a33c343655e514e20f0 (diff)
Restructure
Diffstat (limited to 'srv/notifServer.go')
-rw-r--r--srv/notifServer.go195
1 files changed, 195 insertions, 0 deletions
diff --git a/srv/notifServer.go b/srv/notifServer.go
new file mode 100644
index 0000000..00038cc
--- /dev/null
+++ b/srv/notifServer.go
@@ -0,0 +1,195 @@
+package srv
+
+import (
+ "encoding/json"
+ "fmt"
+ "image"
+ "image/png"
+ "log"
+ "os"
+ "slices"
+ "strings"
+ "time"
+
+ "github.com/godbus/dbus/v5"
+)
+
+type notifServer struct {
+ notifications *notificationStack
+ server
+}
+
+func (s notifServer) GetCapabilities() (capabilities []string, e *dbus.Error) {
+ // log.Print("GetCapabilities called")
+ return []string{
+ "body",
+ "actions",
+ }, nil
+}
+
+func (s notifServer) GetServerInformation() (name, vendor, version, specVersion string, e *dbus.Error) {
+ // log.Print("GetServerInformation called")
+ return "corvid", "CartConnoisseur", "0.1.0", "1.2", nil
+}
+
+func (s notifServer) CloseNotification(id uint32) (e *dbus.Error) {
+ // log.Printf("CloseNotification called: %d", id)
+ s.close(id, CloseReasonClosed)
+ return nil
+}
+
+func (s notifServer) Notify(appName string, replacesId uint32, appIcon string, summary string, body string, actions []string, hints map[string]dbus.Variant, expireTimeout int32) (id uint32, e *dbus.Error) {
+ // log.Print("Notify called")
+ s.notifications.mutex.Lock()
+ defer s.notifications.mutex.Unlock()
+
+ if replacesId == 0 {
+ id = s.notifications.nextId
+ s.notifications.nextId++
+ } else {
+ id = replacesId
+ }
+
+ actionMap := make(map[string]string)
+ for i := 0; i < len(actions)-1; i += 2 {
+ actionMap[actions[i]] = actions[i+1]
+ }
+
+ hintMap := make(map[string]hint)
+ img := ""
+
+ for key, value := range hints {
+ if !value.Signature().Empty() {
+ if strings.Contains("ybnqiuxtds", string(value.Signature().String()[0])) {
+ hintMap[key] = hint{Variant: value}
+ } else if key == "image-data" {
+ raw := value.Value().([]interface{})
+
+ var i image.Image
+ if raw[3].(bool) {
+ i = &image.NRGBA{
+ Pix: raw[6].([]uint8),
+ Stride: int(raw[2].(int32)),
+ Rect: image.Rect(0, 0, int(raw[0].(int32)), int(raw[1].(int32))),
+ }
+ } else {
+ rgb := raw[6].([]uint8)
+ rgba := make([]uint8, len(rgb)/3*4)
+
+ for i := 0; i < len(rgb)-1; i += 3 {
+ rgba[i/3*4] = rgb[i]
+ rgba[i/3*4+1] = rgb[i+1]
+ rgba[i/3*4+2] = rgb[i+2]
+ rgba[i/3*4+3] = 0xff
+ }
+
+ i = &image.NRGBA{
+ Pix: rgba,
+ Stride: int(raw[2].(int32)),
+ Rect: image.Rect(0, 0, int(raw[0].(int32)), int(raw[1].(int32))),
+ }
+ }
+ _ = i
+
+ f, err := os.CreateTemp(os.TempDir(), "corvid-*.png")
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer f.Close()
+
+ png.Encode(f, i)
+
+ img = f.Name()
+ }
+ }
+ }
+
+ if expireTimeout == -1 {
+ expireTimeout = DEFAULT_EXPIRATION
+ }
+
+ notification := notification{
+ Id: id,
+ AppName: appName,
+ AppIcon: appIcon,
+ Summary: summary,
+ Body: body,
+ Actions: actionMap,
+ Hints: hintMap,
+ Timestamp: time.Now().Unix(),
+ Expiration: expireTimeout,
+ Image: img,
+ timer: nil,
+ }
+
+ if expireTimeout != 0 {
+ notification.timer = time.AfterFunc(time.Duration(expireTimeout)*time.Millisecond, func() {
+ s.close(notification.Id, CloseReasonExpire)
+ })
+ }
+
+ s.notifications.notifications[id] = notification
+ s.output()
+
+ return id, nil
+}
+
+func (s notifServer) close(id uint32, reason closeReason) {
+ s.notifications.mutex.Lock()
+ defer s.notifications.mutex.Unlock()
+
+ n, ok := s.notifications.notifications[id]
+ if !ok {
+ return
+ }
+
+ if n.timer != nil {
+ n.timer.Stop()
+ }
+
+ if n.Image != "" {
+ os.Remove(n.Image)
+ }
+
+ delete(s.notifications.notifications, n.Id)
+ s.output()
+
+ err := s.conn.Emit(s.object, s.name+".NotificationClosed", n.Id, reason)
+ if err != nil {
+ log.Print(err)
+ }
+}
+
+// TODO: relocate to cmd/corvid
+func (s notifServer) output() {
+ arr := make([]notification, len(s.notifications.notifications))
+
+ i := 0
+ for _, notification := range s.notifications.notifications {
+ arr[i] = notification
+ i++
+ }
+
+ slices.SortFunc(arr, func(a, b notification) int {
+ if a.Timestamp > b.Timestamp {
+ return SORT_DIRECTION
+ } else if a.Timestamp < b.Timestamp {
+ return -SORT_DIRECTION
+ } else {
+ if a.Id > b.Id {
+ return SORT_DIRECTION
+ } else if a.Id < b.Id {
+ return -SORT_DIRECTION
+ }
+ }
+
+ return 0
+ })
+
+ j, err := json.Marshal(arr)
+ if err != nil {
+ log.Fatalln(err)
+ }
+
+ fmt.Println(string(j))
+}