zenity/notify_windows.go
2022-06-18 12:58:50 +01:00

127 lines
2.8 KiB
Go

package zenity
import (
"math/rand"
"runtime"
"syscall"
"time"
"unsafe"
"github.com/ncruces/zenity/internal/win"
"github.com/ncruces/zenity/internal/zenutil"
)
var (
rtlGetNtVersionNumbers = ntdll.NewProc("RtlGetNtVersionNumbers")
wtsSendMessage = wtsapi32.NewProc("WTSSendMessageW")
)
func notify(text string, opts options) error {
if opts.ctx != nil && opts.ctx.Err() != nil {
return opts.ctx.Err()
}
var args win.NOTIFYICONDATA
args.StructSize = uint32(unsafe.Sizeof(args))
args.ID = rand.Uint32()
args.Flags = 0x00000010 // NIF_INFO
args.State = 0x00000001 // NIS_HIDDEN
info := syscall.StringToUTF16(text)
copy(args.Info[:len(args.Info)-1], info)
if opts.title != nil {
title := syscall.StringToUTF16(*opts.title)
copy(args.InfoTitle[:len(args.InfoTitle)-1], title)
}
switch opts.icon {
case InfoIcon, QuestionIcon:
args.InfoFlags |= 0x1 // NIIF_INFO
case WarningIcon:
args.InfoFlags |= 0x2 // NIIF_WARNING
case ErrorIcon:
args.InfoFlags |= 0x3 // NIIF_ERROR
default:
icon := getIcon(opts.icon)
if icon.handle != 0 {
defer icon.delete()
args.Icon = win.Handle(icon.handle)
args.Flags |= 0x00000002 // NIF_ICON
args.InfoFlags |= 0x4 // NIIF_USER
}
}
runtime.LockOSThread()
defer runtime.UnlockOSThread()
s, err := win.ShellNotifyIcon(win.NIM_ADD, &args)
if s == 0 {
if errno, ok := err.(syscall.Errno); ok && errno == 0 {
return wtsMessage(text, opts)
}
return err
}
var major, minor, build uint32
rtlGetNtVersionNumbers.Call(
uintptr(unsafe.Pointer(&major)),
uintptr(unsafe.Pointer(&minor)),
uintptr(unsafe.Pointer(&build)))
// On Windows 7 (6.1) and lower, wait up to 10 seconds to clean up.
if major < 6 || major == 6 && minor < 2 {
if opts.ctx != nil {
select {
case <-opts.ctx.Done():
case <-time.After(10 * time.Second):
}
} else {
time.Sleep(10 * time.Second)
}
}
win.ShellNotifyIcon(win.NIM_DELETE, &args)
return nil
}
func wtsMessage(text string, opts options) error {
var flags uintptr
switch opts.icon {
case ErrorIcon:
flags |= win.MB_ICONERROR
case QuestionIcon:
flags |= win.MB_ICONQUESTION
case WarningIcon:
flags |= win.MB_ICONWARNING
case InfoIcon:
flags |= win.MB_ICONINFORMATION
}
title := opts.title
if title == nil {
title = stringPtr("Notification")
}
timeout := zenutil.Timeout
if timeout == 0 {
timeout = 10
}
ptext := syscall.StringToUTF16(text)
ptitle := syscall.StringToUTF16(*title)
var res uint32
s, _, err := wtsSendMessage.Call(
0, // WTS_CURRENT_SERVER_HANDLE
0xffffffff, // WTS_CURRENT_SESSION
uintptr(unsafe.Pointer(&ptitle[0])), uintptr(2*len(ptitle)),
uintptr(unsafe.Pointer(&ptext[0])), uintptr(2*len(ptext)),
flags, uintptr(timeout), uintptr(unsafe.Pointer(&res)), 0)
if s == 0 {
return err
}
return nil
}