mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-09 03:59:22 +08:00
refactor/simplify linux
This commit is contained in:
parent
9adeef2bdf
commit
5dc7bd97f2
@ -17,12 +17,36 @@ import (
|
||||
)
|
||||
|
||||
type linuxNotifier struct {
|
||||
categories map[string]NotificationCategory
|
||||
categoriesLock sync.RWMutex
|
||||
appName string
|
||||
internal *internalNotifier
|
||||
notificationInitErr error
|
||||
initOnce sync.Once
|
||||
// Categories
|
||||
categories map[string]NotificationCategory
|
||||
categoriesLock sync.RWMutex
|
||||
|
||||
// App info
|
||||
appName string
|
||||
|
||||
// Notification system
|
||||
sync.Mutex
|
||||
method string
|
||||
dbusConn *dbus.Conn
|
||||
sendPath string
|
||||
activeNotifs map[string]uint32 // Maps our notification IDs to system IDs
|
||||
contexts map[string]*notificationContext // Stores notification contexts by our ID
|
||||
|
||||
// Listener management
|
||||
listenerCtx context.Context
|
||||
listenerCancel context.CancelFunc
|
||||
listenerRunning bool
|
||||
|
||||
// Initialization
|
||||
initOnce sync.Once
|
||||
initialized bool
|
||||
}
|
||||
|
||||
type notificationContext struct {
|
||||
ID string
|
||||
SystemID uint32
|
||||
Actions map[string]string // Maps action keys to display labels
|
||||
UserData map[string]interface{} // The original user data
|
||||
}
|
||||
|
||||
const (
|
||||
@ -35,38 +59,19 @@ const (
|
||||
|
||||
MethodNotifySend = "notify-send"
|
||||
MethodDbus = "dbus"
|
||||
MethodKdialog = "kdialog"
|
||||
|
||||
notifyChannelBufferSize = 25
|
||||
)
|
||||
|
||||
type closedReason uint32
|
||||
|
||||
// internalNotifier handles the actual notification sending via dbus or command line
|
||||
type notificationContext struct {
|
||||
ID string
|
||||
SystemID uint32
|
||||
Actions map[string]string // Maps action keys to display labels
|
||||
UserData map[string]interface{} // The original user data
|
||||
}
|
||||
|
||||
type internalNotifier struct {
|
||||
sync.Mutex
|
||||
method string
|
||||
dbusConn *dbus.Conn
|
||||
sendPath string
|
||||
activeNotifs map[string]uint32 // Maps our notification IDs to system IDs
|
||||
contexts map[string]*notificationContext // Stores notification contexts by our ID
|
||||
listenerCtx context.Context
|
||||
listenerCancel context.CancelFunc
|
||||
listenerRunning bool
|
||||
}
|
||||
|
||||
// New creates a new Notifications Service
|
||||
func New() *Service {
|
||||
notificationServiceOnce.Do(func() {
|
||||
impl := &linuxNotifier{
|
||||
categories: make(map[string]NotificationCategory),
|
||||
categories: make(map[string]NotificationCategory),
|
||||
activeNotifs: make(map[string]uint32),
|
||||
contexts: make(map[string]*notificationContext),
|
||||
}
|
||||
|
||||
NotificationService = &Service{
|
||||
@ -77,159 +82,167 @@ func New() *Service {
|
||||
}
|
||||
|
||||
// Startup is called when the service is loaded
|
||||
func (ls *linuxNotifier) Startup(ctx context.Context) error {
|
||||
ls.appName = application.Get().Config().Name
|
||||
func (ln *linuxNotifier) Startup(ctx context.Context) error {
|
||||
ln.appName = application.Get().Config().Name
|
||||
|
||||
if err := ls.loadCategories(); err != nil {
|
||||
if err := ln.loadCategories(); err != nil {
|
||||
fmt.Printf("Failed to load notification categories: %v\n", err)
|
||||
}
|
||||
|
||||
// Initialize the internal notifier
|
||||
ls.internal = &internalNotifier{
|
||||
activeNotifs: make(map[string]uint32),
|
||||
contexts: make(map[string]*notificationContext),
|
||||
}
|
||||
|
||||
var err error
|
||||
ls.initOnce.Do(func() {
|
||||
// Initialize notification system
|
||||
err = ls.initNotificationSystem()
|
||||
ln.initOnce.Do(func() {
|
||||
err = ln.initNotificationSystem()
|
||||
ln.initialized = err == nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Shutdown is called when the service is unloaded
|
||||
func (ls *linuxNotifier) Shutdown() error {
|
||||
if ls.internal != nil {
|
||||
ls.internal.Lock()
|
||||
defer ls.internal.Unlock()
|
||||
// initNotificationSystem initializes the notification system
|
||||
func (ln *linuxNotifier) initNotificationSystem() error {
|
||||
ln.Lock()
|
||||
defer ln.Unlock()
|
||||
|
||||
// Cancel the listener context if it's running
|
||||
if ls.internal.listenerCancel != nil {
|
||||
ls.internal.listenerCancel()
|
||||
ls.internal.listenerCancel = nil
|
||||
}
|
||||
|
||||
// Close the connection
|
||||
if ls.internal.dbusConn != nil {
|
||||
ls.internal.dbusConn.Close()
|
||||
ls.internal.dbusConn = nil
|
||||
}
|
||||
|
||||
// Clear state
|
||||
ls.internal.activeNotifs = make(map[string]uint32)
|
||||
ls.internal.contexts = make(map[string]*notificationContext)
|
||||
ls.internal.method = "none"
|
||||
ls.internal.sendPath = ""
|
||||
}
|
||||
|
||||
return ls.saveCategories()
|
||||
}
|
||||
|
||||
// initNotificationSystem initializes the notification system, choosing the best available method
|
||||
func (ls *linuxNotifier) initNotificationSystem() error {
|
||||
var err error
|
||||
|
||||
// Cancel any existing listener before starting a new one
|
||||
if ls.internal.listenerCancel != nil {
|
||||
ls.internal.listenerCancel()
|
||||
// Cancel any existing listener
|
||||
if ln.listenerCancel != nil {
|
||||
ln.listenerCancel()
|
||||
ln.listenerCancel = nil
|
||||
}
|
||||
|
||||
// Create a new context for the listener
|
||||
ls.internal.listenerCtx, ls.internal.listenerCancel = context.WithCancel(context.Background())
|
||||
ln.listenerCtx, ln.listenerCancel = context.WithCancel(context.Background())
|
||||
|
||||
// Reset state
|
||||
ls.internal.activeNotifs = make(map[string]uint32)
|
||||
ls.internal.contexts = make(map[string]*notificationContext)
|
||||
ls.internal.listenerRunning = false
|
||||
|
||||
checkDbus := func() (*dbus.Conn, error) {
|
||||
conn, err := dbus.SessionBusPrivate()
|
||||
if err != nil {
|
||||
return conn, err
|
||||
}
|
||||
|
||||
if err = conn.Auth(nil); err != nil {
|
||||
return conn, err
|
||||
}
|
||||
|
||||
if err = conn.Hello(); err != nil {
|
||||
return conn, err
|
||||
}
|
||||
|
||||
obj := conn.Object(dbusNotificationsInterface, dbusObjectPath)
|
||||
call := obj.Call(callGetCapabilities, 0)
|
||||
if call.Err != nil {
|
||||
return conn, call.Err
|
||||
}
|
||||
|
||||
var ret []string
|
||||
err = call.Store(&ret)
|
||||
if err != nil {
|
||||
return conn, err
|
||||
}
|
||||
|
||||
// Add a listener for notification signals
|
||||
err = conn.AddMatchSignal(
|
||||
dbus.WithMatchObjectPath(dbusObjectPath),
|
||||
dbus.WithMatchInterface(dbusNotificationsInterface),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
ln.activeNotifs = make(map[string]uint32)
|
||||
ln.contexts = make(map[string]*notificationContext)
|
||||
ln.listenerRunning = false
|
||||
|
||||
// Try dbus first
|
||||
ls.internal.dbusConn, err = checkDbus()
|
||||
dbusConn, err := ln.initDBus()
|
||||
if err == nil {
|
||||
ls.internal.method = MethodDbus
|
||||
// Start the dbus signal listener with context
|
||||
go ls.startDBusListener(ls.internal.listenerCtx)
|
||||
ls.internal.listenerRunning = true
|
||||
return nil
|
||||
}
|
||||
if ls.internal.dbusConn != nil {
|
||||
ls.internal.dbusConn.Close()
|
||||
ls.internal.dbusConn = nil
|
||||
}
|
||||
ln.dbusConn = dbusConn
|
||||
ln.method = MethodDbus
|
||||
|
||||
// Try notify-send
|
||||
send, err := exec.LookPath("notify-send")
|
||||
if err == nil {
|
||||
ls.internal.sendPath = send
|
||||
ls.internal.method = MethodNotifySend
|
||||
// Start the dbus signal listener
|
||||
go ln.startDBusListener(ln.listenerCtx)
|
||||
ln.listenerRunning = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Try sw-notify-send
|
||||
send, err = exec.LookPath("sw-notify-send")
|
||||
// Try notify-send as fallback
|
||||
sendPath, err := ln.initNotifySend()
|
||||
if err == nil {
|
||||
ls.internal.sendPath = send
|
||||
ls.internal.method = MethodNotifySend
|
||||
ln.sendPath = sendPath
|
||||
ln.method = MethodNotifySend
|
||||
return nil
|
||||
}
|
||||
|
||||
// No method available
|
||||
ls.internal.method = "none"
|
||||
ls.internal.sendPath = ""
|
||||
|
||||
ln.method = ""
|
||||
ln.sendPath = ""
|
||||
return errors.New("no notification method is available")
|
||||
}
|
||||
|
||||
// initDBus attempts to initialize D-Bus notifications
|
||||
func (ln *linuxNotifier) initDBus() (*dbus.Conn, error) {
|
||||
conn, err := dbus.SessionBusPrivate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = conn.Auth(nil); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = conn.Hello(); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
obj := conn.Object(dbusNotificationsInterface, dbusObjectPath)
|
||||
call := obj.Call(callGetCapabilities, 0)
|
||||
if call.Err != nil {
|
||||
conn.Close()
|
||||
return nil, call.Err
|
||||
}
|
||||
|
||||
var ret []string
|
||||
err = call.Store(&ret)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Add a listener for notification signals
|
||||
err = conn.AddMatchSignal(
|
||||
dbus.WithMatchObjectPath(dbusObjectPath),
|
||||
dbus.WithMatchInterface(dbusNotificationsInterface),
|
||||
)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// initNotifySend attempts to find notify-send binary
|
||||
func (ln *linuxNotifier) initNotifySend() (string, error) {
|
||||
// Try standard notify-send
|
||||
send, err := exec.LookPath("notify-send")
|
||||
if err == nil {
|
||||
return send, nil
|
||||
}
|
||||
|
||||
// Try sw-notify-send (in some distros)
|
||||
send, err = exec.LookPath("sw-notify-send")
|
||||
if err == nil {
|
||||
return send, nil
|
||||
}
|
||||
|
||||
return "", errors.New("notify-send not found")
|
||||
}
|
||||
|
||||
// Shutdown is called when the service is unloaded
|
||||
func (ln *linuxNotifier) Shutdown() error {
|
||||
ln.Lock()
|
||||
|
||||
// Cancel the listener context if it's running
|
||||
if ln.listenerCancel != nil {
|
||||
ln.listenerCancel()
|
||||
ln.listenerCancel = nil
|
||||
}
|
||||
|
||||
// Close the connection
|
||||
if ln.dbusConn != nil {
|
||||
ln.dbusConn.Close()
|
||||
ln.dbusConn = nil
|
||||
}
|
||||
|
||||
// Clear state
|
||||
ln.activeNotifs = make(map[string]uint32)
|
||||
ln.contexts = make(map[string]*notificationContext)
|
||||
ln.method = ""
|
||||
ln.sendPath = ""
|
||||
ln.initialized = false
|
||||
|
||||
ln.Unlock()
|
||||
|
||||
return ln.saveCategories()
|
||||
}
|
||||
|
||||
// startDBusListener listens for DBus signals for notification actions and closures
|
||||
func (ls *linuxNotifier) startDBusListener(ctx context.Context) {
|
||||
func (ln *linuxNotifier) startDBusListener(ctx context.Context) {
|
||||
signal := make(chan *dbus.Signal, notifyChannelBufferSize)
|
||||
ls.internal.dbusConn.Signal(signal)
|
||||
ln.dbusConn.Signal(signal)
|
||||
|
||||
defer func() {
|
||||
ls.internal.Lock()
|
||||
ls.internal.listenerRunning = false
|
||||
ls.internal.Unlock()
|
||||
ls.internal.dbusConn.RemoveSignal(signal) // Remove signal handler
|
||||
close(signal) // Clean up channel
|
||||
ln.Lock()
|
||||
ln.listenerRunning = false
|
||||
ln.Unlock()
|
||||
ln.dbusConn.RemoveSignal(signal) // Remove signal handler
|
||||
close(signal) // Clean up channel
|
||||
}()
|
||||
|
||||
for {
|
||||
@ -252,34 +265,34 @@ func (ls *linuxNotifier) startDBusListener(ctx context.Context) {
|
||||
case signalNotificationClosed:
|
||||
systemID := s.Body[0].(uint32)
|
||||
reason := closedReason(s.Body[1].(uint32)).string()
|
||||
ls.handleNotificationClosed(systemID, reason)
|
||||
ln.handleNotificationClosed(systemID, reason)
|
||||
case signalActionInvoked:
|
||||
systemID := s.Body[0].(uint32)
|
||||
actionKey := s.Body[1].(string)
|
||||
ls.handleActionInvoked(systemID, actionKey)
|
||||
ln.handleActionInvoked(systemID, actionKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handleNotificationClosed processes notification closed signals
|
||||
func (ls *linuxNotifier) handleNotificationClosed(systemID uint32, reason string) {
|
||||
func (ln *linuxNotifier) handleNotificationClosed(systemID uint32, reason string) {
|
||||
// Find our notification ID for this system ID
|
||||
var notifID string
|
||||
var userData map[string]interface{}
|
||||
|
||||
ls.internal.Lock()
|
||||
for id, sysID := range ls.internal.activeNotifs {
|
||||
ln.Lock()
|
||||
for id, sysID := range ln.activeNotifs {
|
||||
if sysID == systemID {
|
||||
notifID = id
|
||||
// Get the user data from context if available
|
||||
if ctx, exists := ls.internal.contexts[id]; exists {
|
||||
if ctx, exists := ln.contexts[id]; exists {
|
||||
userData = ctx.UserData
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
ls.internal.Unlock()
|
||||
ln.Unlock()
|
||||
|
||||
if notifID != "" {
|
||||
response := NotificationResponse{
|
||||
@ -304,28 +317,28 @@ func (ls *linuxNotifier) handleNotificationClosed(systemID uint32, reason string
|
||||
}
|
||||
|
||||
// Clean up the context
|
||||
ls.internal.Lock()
|
||||
delete(ls.internal.contexts, notifID)
|
||||
delete(ls.internal.activeNotifs, notifID)
|
||||
ls.internal.Unlock()
|
||||
ln.Lock()
|
||||
delete(ln.contexts, notifID)
|
||||
delete(ln.activeNotifs, notifID)
|
||||
ln.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// handleActionInvoked processes action invoked signals
|
||||
func (ls *linuxNotifier) handleActionInvoked(systemID uint32, actionKey string) {
|
||||
func (ln *linuxNotifier) handleActionInvoked(systemID uint32, actionKey string) {
|
||||
// Find our notification ID and context for this system ID
|
||||
var notifID string
|
||||
var ctx *notificationContext
|
||||
|
||||
ls.internal.Lock()
|
||||
for id, sysID := range ls.internal.activeNotifs {
|
||||
ln.Lock()
|
||||
for id, sysID := range ln.activeNotifs {
|
||||
if sysID == systemID {
|
||||
notifID = id
|
||||
ctx = ls.internal.contexts[id]
|
||||
ctx = ln.contexts[id]
|
||||
break
|
||||
}
|
||||
}
|
||||
ls.internal.Unlock()
|
||||
ln.Unlock()
|
||||
|
||||
if notifID != "" {
|
||||
if actionKey == "default" {
|
||||
@ -372,62 +385,58 @@ func (ls *linuxNotifier) handleActionInvoked(systemID uint32, actionKey string)
|
||||
}
|
||||
|
||||
// Clean up the context
|
||||
ls.internal.Lock()
|
||||
delete(ls.internal.contexts, notifID)
|
||||
delete(ls.internal.activeNotifs, notifID)
|
||||
ls.internal.Unlock()
|
||||
ln.Lock()
|
||||
delete(ln.contexts, notifID)
|
||||
delete(ln.activeNotifs, notifID)
|
||||
ln.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// CheckBundleIdentifier is a Linux stub that always returns true.
|
||||
// (bundle identifiers are macOS-specific)
|
||||
func (ls *linuxNotifier) CheckBundleIdentifier() bool {
|
||||
func (ln *linuxNotifier) CheckBundleIdentifier() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// RequestNotificationAuthorization is a Linux stub that always returns true.
|
||||
// (user authorization is macOS-specific)
|
||||
func (ls *linuxNotifier) RequestNotificationAuthorization() (bool, error) {
|
||||
func (ln *linuxNotifier) RequestNotificationAuthorization() (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// CheckNotificationAuthorization is a Linux stub that always returns true.
|
||||
// (user authorization is macOS-specific)
|
||||
func (ls *linuxNotifier) CheckNotificationAuthorization() (bool, error) {
|
||||
func (ln *linuxNotifier) CheckNotificationAuthorization() (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// SendNotification sends a basic notification with a unique identifier, title, subtitle, and body.
|
||||
func (ls *linuxNotifier) SendNotification(options NotificationOptions) error {
|
||||
if ls.internal == nil {
|
||||
func (ln *linuxNotifier) SendNotification(options NotificationOptions) error {
|
||||
if !ln.initialized {
|
||||
return errors.New("notification service not initialized")
|
||||
}
|
||||
|
||||
if ls.internal.method == "" || (ls.internal.method == MethodDbus && ls.internal.dbusConn == nil) ||
|
||||
(ls.internal.method == MethodNotifySend && ls.internal.sendPath == "") {
|
||||
return errors.New("notification system not properly initialized")
|
||||
if err := validateNotificationOptions(options); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ls.internal.Lock()
|
||||
defer ls.internal.Unlock()
|
||||
ln.Lock()
|
||||
defer ln.Unlock()
|
||||
|
||||
var (
|
||||
systemID uint32
|
||||
err error
|
||||
)
|
||||
|
||||
switch ls.internal.method {
|
||||
switch ln.method {
|
||||
case MethodDbus:
|
||||
systemID, err = ls.sendViaDbus(options, nil)
|
||||
systemID, err = ln.sendViaDbus(options, nil)
|
||||
case MethodNotifySend:
|
||||
systemID, err = ls.sendViaNotifySend(options)
|
||||
systemID, err = ln.sendViaNotifySend(options)
|
||||
default:
|
||||
err = errors.New("no notification method is available")
|
||||
}
|
||||
|
||||
if err == nil && systemID > 0 {
|
||||
// Store the system ID mapping
|
||||
ls.internal.activeNotifs[options.ID] = systemID
|
||||
ln.activeNotifs[options.ID] = systemID
|
||||
|
||||
// Create and store the notification context
|
||||
ctx := ¬ificationContext{
|
||||
@ -435,52 +444,51 @@ func (ls *linuxNotifier) SendNotification(options NotificationOptions) error {
|
||||
SystemID: systemID,
|
||||
UserData: options.Data,
|
||||
}
|
||||
ls.internal.contexts[options.ID] = ctx
|
||||
ln.contexts[options.ID] = ctx
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// SendNotificationWithActions sends a notification with additional actions.
|
||||
func (ls *linuxNotifier) SendNotificationWithActions(options NotificationOptions) error {
|
||||
if ls.internal == nil {
|
||||
func (ln *linuxNotifier) SendNotificationWithActions(options NotificationOptions) error {
|
||||
if !ln.initialized {
|
||||
return errors.New("notification service not initialized")
|
||||
}
|
||||
|
||||
if ls.internal.method == "" || (ls.internal.method == MethodDbus && ls.internal.dbusConn == nil) ||
|
||||
(ls.internal.method == MethodNotifySend && ls.internal.sendPath == "") {
|
||||
return errors.New("notification system not properly initialized")
|
||||
if err := validateNotificationOptions(options); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ls.categoriesLock.RLock()
|
||||
category, exists := ls.categories[options.CategoryID]
|
||||
ls.categoriesLock.RUnlock()
|
||||
ln.categoriesLock.RLock()
|
||||
category, exists := ln.categories[options.CategoryID]
|
||||
ln.categoriesLock.RUnlock()
|
||||
|
||||
if !exists {
|
||||
return ls.SendNotification(options)
|
||||
return ln.SendNotification(options)
|
||||
}
|
||||
|
||||
ls.internal.Lock()
|
||||
defer ls.internal.Unlock()
|
||||
ln.Lock()
|
||||
defer ln.Unlock()
|
||||
|
||||
var (
|
||||
systemID uint32
|
||||
err error
|
||||
)
|
||||
|
||||
switch ls.internal.method {
|
||||
switch ln.method {
|
||||
case MethodDbus:
|
||||
systemID, err = ls.sendViaDbus(options, &category)
|
||||
systemID, err = ln.sendViaDbus(options, &category)
|
||||
case MethodNotifySend:
|
||||
// notify-send doesn't support actions, fall back to basic notification
|
||||
systemID, err = ls.sendViaNotifySend(options)
|
||||
systemID, err = ln.sendViaNotifySend(options)
|
||||
default:
|
||||
err = errors.New("no notification method is available")
|
||||
}
|
||||
|
||||
if err == nil && systemID > 0 {
|
||||
// Store the system ID mapping
|
||||
ls.internal.activeNotifs[options.ID] = systemID
|
||||
ln.activeNotifs[options.ID] = systemID
|
||||
|
||||
// Create and store the notification context with actions
|
||||
ctx := ¬ificationContext{
|
||||
@ -497,14 +505,14 @@ func (ls *linuxNotifier) SendNotificationWithActions(options NotificationOptions
|
||||
}
|
||||
}
|
||||
|
||||
ls.internal.contexts[options.ID] = ctx
|
||||
ln.contexts[options.ID] = ctx
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// sendViaDbus sends a notification via dbus
|
||||
func (ls *linuxNotifier) sendViaDbus(options NotificationOptions, category *NotificationCategory) (result uint32, err error) {
|
||||
func (ln *linuxNotifier) sendViaDbus(options NotificationOptions, category *NotificationCategory) (result uint32, err error) {
|
||||
// Prepare actions
|
||||
var actions []string
|
||||
if category != nil {
|
||||
@ -530,9 +538,9 @@ func (ls *linuxNotifier) sendViaDbus(options NotificationOptions, category *Noti
|
||||
}
|
||||
|
||||
// Send the notification
|
||||
obj := ls.internal.dbusConn.Object(dbusNotificationsInterface, dbusObjectPath)
|
||||
obj := ln.dbusConn.Object(dbusNotificationsInterface, dbusObjectPath)
|
||||
dbusArgs := []interface{}{
|
||||
ls.appName, // App name
|
||||
ln.appName, // App name
|
||||
uint32(0), // Replaces ID (0 means new notification)
|
||||
"", // App icon (empty for now)
|
||||
options.Title, // Title
|
||||
@ -556,20 +564,15 @@ func (ls *linuxNotifier) sendViaDbus(options NotificationOptions, category *Noti
|
||||
}
|
||||
|
||||
// sendViaNotifySend sends a notification via notify-send command
|
||||
func (ls *linuxNotifier) sendViaNotifySend(options NotificationOptions) (uint32, error) {
|
||||
func (ln *linuxNotifier) sendViaNotifySend(options NotificationOptions) (uint32, error) {
|
||||
args := []string{
|
||||
options.Title,
|
||||
options.Body,
|
||||
"--urgency=normal",
|
||||
}
|
||||
|
||||
// Add icon if eventually supported
|
||||
// if options.Icon != "" { ... }
|
||||
|
||||
// Add urgency (normal by default)
|
||||
args = append(args, "--urgency=normal")
|
||||
|
||||
// Execute the command
|
||||
cmd := exec.Command(ls.internal.sendPath, args...)
|
||||
cmd := exec.Command(ln.sendPath, args...)
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("notify-send error: %v", err)
|
||||
@ -579,78 +582,74 @@ func (ls *linuxNotifier) sendViaNotifySend(options NotificationOptions) (uint32,
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// RegisterNotificationCategory registers a new NotificationCategory to be used with SendNotificationWithActions.
|
||||
func (ls *linuxNotifier) RegisterNotificationCategory(category NotificationCategory) error {
|
||||
ls.categoriesLock.Lock()
|
||||
ls.categories[category.ID] = category
|
||||
ls.categoriesLock.Unlock()
|
||||
// RegisterNotificationCategory registers a new NotificationCategory
|
||||
func (ln *linuxNotifier) RegisterNotificationCategory(category NotificationCategory) error {
|
||||
ln.categoriesLock.Lock()
|
||||
ln.categories[category.ID] = category
|
||||
ln.categoriesLock.Unlock()
|
||||
|
||||
return ls.saveCategories()
|
||||
return ln.saveCategories()
|
||||
}
|
||||
|
||||
// RemoveNotificationCategory removes a previously registered NotificationCategory.
|
||||
func (ls *linuxNotifier) RemoveNotificationCategory(categoryId string) error {
|
||||
ls.categoriesLock.Lock()
|
||||
delete(ls.categories, categoryId)
|
||||
ls.categoriesLock.Unlock()
|
||||
// RemoveNotificationCategory removes a previously registered NotificationCategory
|
||||
func (ln *linuxNotifier) RemoveNotificationCategory(categoryId string) error {
|
||||
ln.categoriesLock.Lock()
|
||||
delete(ln.categories, categoryId)
|
||||
ln.categoriesLock.Unlock()
|
||||
|
||||
return ls.saveCategories()
|
||||
return ln.saveCategories()
|
||||
}
|
||||
|
||||
// RemoveAllPendingNotifications is a Linux stub that always returns nil.
|
||||
// (macOS-specific)
|
||||
func (ls *linuxNotifier) RemoveAllPendingNotifications() error {
|
||||
// RemoveAllPendingNotifications is a Linux stub that always returns nil
|
||||
func (ln *linuxNotifier) RemoveAllPendingNotifications() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemovePendingNotification is a Linux stub that always returns nil.
|
||||
// (macOS-specific)
|
||||
func (ls *linuxNotifier) RemovePendingNotification(_ string) error {
|
||||
// RemovePendingNotification is a Linux stub that always returns nil
|
||||
func (ln *linuxNotifier) RemovePendingNotification(_ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveAllDeliveredNotifications is a Linux stub that always returns nil.
|
||||
// (macOS-specific)
|
||||
func (ls *linuxNotifier) RemoveAllDeliveredNotifications() error {
|
||||
// RemoveAllDeliveredNotifications is a Linux stub that always returns nil
|
||||
func (ln *linuxNotifier) RemoveAllDeliveredNotifications() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveDeliveredNotification is a Linux stub that always returns nil.
|
||||
// (macOS-specific)
|
||||
func (ls *linuxNotifier) RemoveDeliveredNotification(_ string) error {
|
||||
// RemoveDeliveredNotification is a Linux stub that always returns nil
|
||||
func (ln *linuxNotifier) RemoveDeliveredNotification(_ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveNotification removes a notification by ID (Linux-specific)
|
||||
func (ls *linuxNotifier) RemoveNotification(identifier string) error {
|
||||
if ls.internal == nil || ls.internal.method != MethodDbus || ls.internal.dbusConn == nil {
|
||||
func (ln *linuxNotifier) RemoveNotification(identifier string) error {
|
||||
if !ln.initialized || ln.method != MethodDbus || ln.dbusConn == nil {
|
||||
return errors.New("dbus not available for closing notifications")
|
||||
}
|
||||
|
||||
// Get the system ID for this notification
|
||||
ls.internal.Lock()
|
||||
systemID, exists := ls.internal.activeNotifs[identifier]
|
||||
ls.internal.Unlock()
|
||||
ln.Lock()
|
||||
systemID, exists := ln.activeNotifs[identifier]
|
||||
ln.Unlock()
|
||||
|
||||
if !exists {
|
||||
return nil // Already closed or unknown
|
||||
}
|
||||
|
||||
// Call CloseNotification on dbus
|
||||
obj := ls.internal.dbusConn.Object(dbusNotificationsInterface, dbusObjectPath)
|
||||
obj := ln.dbusConn.Object(dbusNotificationsInterface, dbusObjectPath)
|
||||
call := obj.Call(callCloseNotification, 0, systemID)
|
||||
|
||||
return call.Err
|
||||
}
|
||||
|
||||
// getConfigFilePath returns the path to the configuration file for storing notification categories
|
||||
func (ls *linuxNotifier) getConfigFilePath() (string, error) {
|
||||
// getConfigFilePath returns the path to the configuration file
|
||||
func (ln *linuxNotifier) getConfigFilePath() (string, error) {
|
||||
configDir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get user config directory: %v", err)
|
||||
}
|
||||
|
||||
appConfigDir := filepath.Join(configDir, ls.appName)
|
||||
appConfigDir := filepath.Join(configDir, ln.appName)
|
||||
if err := os.MkdirAll(appConfigDir, 0755); err != nil {
|
||||
return "", fmt.Errorf("failed to create config directory: %v", err)
|
||||
}
|
||||
@ -658,16 +657,16 @@ func (ls *linuxNotifier) getConfigFilePath() (string, error) {
|
||||
return filepath.Join(appConfigDir, "notification-categories.json"), nil
|
||||
}
|
||||
|
||||
// saveCategories saves the notification categories to a file.
|
||||
func (ls *linuxNotifier) saveCategories() error {
|
||||
filePath, err := ls.getConfigFilePath()
|
||||
// saveCategories saves the notification categories to a file
|
||||
func (ln *linuxNotifier) saveCategories() error {
|
||||
filePath, err := ln.getConfigFilePath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ls.categoriesLock.RLock()
|
||||
data, err := json.Marshal(ls.categories)
|
||||
ls.categoriesLock.RUnlock()
|
||||
ln.categoriesLock.RLock()
|
||||
data, err := json.Marshal(ln.categories)
|
||||
ln.categoriesLock.RUnlock()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal notification categories: %v", err)
|
||||
@ -680,9 +679,9 @@ func (ls *linuxNotifier) saveCategories() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadCategories loads notification categories from a file.
|
||||
func (ls *linuxNotifier) loadCategories() error {
|
||||
filePath, err := ls.getConfigFilePath()
|
||||
// loadCategories loads notification categories from a file
|
||||
func (ln *linuxNotifier) loadCategories() error {
|
||||
filePath, err := ln.getConfigFilePath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -705,9 +704,9 @@ func (ls *linuxNotifier) loadCategories() error {
|
||||
return fmt.Errorf("failed to unmarshal notification categories: %v", err)
|
||||
}
|
||||
|
||||
ls.categoriesLock.Lock()
|
||||
ls.categories = categories
|
||||
ls.categoriesLock.Unlock()
|
||||
ln.categoriesLock.Lock()
|
||||
ln.categories = categories
|
||||
ln.categoriesLock.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user