//go:build windows package notify import ( "fmt" "sync" "golang.org/x/sys/windows/registry" ) // appIDKey is where Windows looks up the identity (name, icon) of an // unpackaged program that shows toasts: HKCU, so no admin rights. const appIDKey = `Software\Classes\AppUserModelId\` + AppID var ( identityMu sync.Mutex identityKnown bool identityOK bool ) // UseIcon registers talk's own toast identity for the current user: the // AppUserModelID AppID with display name "talk" and the icon at iconPath. // Toasts then appear as coming from talk instead of Windows PowerShell. // Idempotent; rewritten on every call so it follows the icon's location. func UseIcon(iconPath string) error { setIconPath(iconPath) k, _, err := registry.CreateKey(registry.CURRENT_USER, appIDKey, registry.SET_VALUE) if err != nil { return fmt.Errorf("registering toast identity: %w", err) } defer k.Close() if err := k.SetStringValue("DisplayName", Title); err != nil { return fmt.Errorf("registering toast identity: %w", err) } if err := k.SetStringValue("IconUri", iconPath); err != nil { return fmt.Errorf("registering toast identity: %w", err) } identityMu.Lock() identityKnown, identityOK = true, true identityMu.Unlock() return nil } // ownIdentity reports whether AppID is registered for this user (by this // process or an earlier daemon start); checked once per process. func ownIdentity() bool { identityMu.Lock() defer identityMu.Unlock() if !identityKnown { identityKnown = true identityOK = false if k, err := registry.OpenKey(registry.CURRENT_USER, appIDKey, registry.QUERY_VALUE); err == nil { _, _, err := k.GetStringValue("IconUri") identityOK = err == nil k.Close() } } return identityOK }