Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added SignalNotify to get action and close signals #4

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 96 additions & 1 deletion notify.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
// specification.
package notify

import "github.com/godbus/dbus"
import (
"context"

"github.com/godbus/dbus"
)

// Notification object paths and interfaces.
const (
Expand Down Expand Up @@ -287,3 +291,94 @@ func CloseNotification(id uint32) (err error) {
err = call.Err
return
}

// CloseReason is the reason why the notification was closed.
type CloseReason uint32

func (x CloseReason) String() string {
switch x {
case NotClosed:
return "not closed"
case CloseReasonExpired:
return "expired"
case CloseReasonUserDismissed:
return "user dismissed"
case CloseReasonCloseCalled:
return "close called"
default:
return "undefined"
}
}

// https://developer.gnome.org/notification-spec/#signals
const (
NotClosed CloseReason = 0
CloseReasonExpired CloseReason = 1
CloseReasonUserDismissed CloseReason = 2
CloseReasonCloseCalled CloseReason = 3
CloseReasonUndefined CloseReason = 4
)

type Signal struct {
Name string
ID uint32
ActionKey string
CloseReason CloseReason
}

// SignalNotify sends notification signals to the channel, such as close or action.
// Note that you can receive other signals, check the ID.
// Does not return until the context is done.
// Do not close the channel until after this function returns.
func SignalNotify(ctx context.Context, ch chan<- Signal) error {
conn, err := dbus.SessionBus()
if err != nil {
return err
}
call := conn.BusObject().Call(
"org.freedesktop.DBus.AddMatch",
0,
"type='signal',path='"+DbusObjectPath+"',interface='"+DbusInterfacePath+"'")
if call.Err != nil {
return call.Err
}
chinput := make(chan *dbus.Signal)
conn.Signal(chinput)
defer func() {
conn.BusObject().Call(
"org.freedesktop.DBus.RemoveMatch",
0,
"type='signal',path='"+DbusObjectPath+"',interface='"+DbusInterfacePath+"'")
conn.RemoveSignal(chinput)
close(chinput)
}()
for {
select {
case <-ctx.Done():
return ctx.Err()
case dsig := <-chinput:
var sig Signal
switch dsig.Name {
case SignalNotificationClosed:
sig = Signal{
Name: dsig.Name,
ID: dsig.Body[0].(uint32),
CloseReason: CloseReason(dsig.Body[1].(uint32)),
}
case SignalActionInvoked:
sig = Signal{
Name: dsig.Name,
ID: dsig.Body[0].(uint32),
ActionKey: dsig.Body[1].(string),
}
default:
sig = Signal{Name: dsig.Name}
}
select {
case ch <- sig:
case <-ctx.Done():
return ctx.Err()
}
}
}
}
53 changes: 52 additions & 1 deletion notify_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package notify

import "testing"
import (
"context"
"log"
"sync"
"testing"
"time"
)

func TestGetCapabilities(t *testing.T) {
c, err := GetCapabilities()
Expand Down Expand Up @@ -70,3 +76,48 @@ func TestUrgencyNotification(t *testing.T) {
t.Fatal(err)
}
}

func TestSignalNotify(t *testing.T) {
msg := "Just a test\n\n<b>you can click things!</b>\n\n(use the -short test switch to skip this)"
ntf := NewNotification("Notification Test", msg)
if testing.Short() {
ntf.Timeout = 500
log.Print("Using short timeout because of -short")
} else {
ntf.Timeout = 5000
log.Printf("Using %vms timeout, click the notification or use -short to go faster", ntf.Timeout)
}
ntf.Actions = []string{"my-something", "Something", "default", "Default"}
id, err := ntf.Show()
if err != nil {
t.Fatal(err)
}

ctx, cancel := context.WithTimeout(context.Background(), 7*time.Second)
defer cancel()

ch := make(chan Signal, 2)
wg := &sync.WaitGroup{}

wg.Add(1)
go func() {
defer wg.Done()
for sig := range ch {
log.Printf("Signal: %+v", sig)
if sig.CloseReason == NotClosed {
CloseNotification(id)
} else {
cancel()
break
}
}
}()

err = SignalNotify(ctx, ch)
if err != nil && err != context.Canceled {
t.Fatal(err)
}

close(ch)
wg.Wait()
}