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

public SetIconFile func for Windows #82

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
76 changes: 41 additions & 35 deletions systray_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,14 @@ import (
"sort"
"syscall"
"unsafe"

"golang.org/x/sys/windows"
)

// Helpful sources: https://github.com/golang/exp/blob/master/shiny/driver/internal/win32

var (
k32 = windows.NewLazySystemDLL("Kernel32.dll")
s32 = windows.NewLazySystemDLL("Shell32.dll")
u32 = windows.NewLazySystemDLL("User32.dll")
k32 = syscall.NewLazyDLL("kernel32.dll")
s32 = syscall.NewLazyDLL("shell32.dll")
u32 = syscall.NewLazyDLL("user32.dll")
pGetModuleHandle = k32.NewProc("GetModuleHandleW")
pShellNotifyIcon = s32.NewProc("Shell_NotifyIconW")
pCreatePopupMenu = u32.NewProc("CreatePopupMenu")
Expand Down Expand Up @@ -57,9 +55,9 @@ type wndClassEx struct {
Size, Style uint32
WndProc uintptr
ClsExtra, WndExtra int32
Instance, Icon, Cursor, Background windows.Handle
Instance, Icon, Cursor, Background syscall.Handle
MenuName, ClassName *uint16
IconSm windows.Handle
IconSm syscall.Handle
}

// Registers a window class for subsequent use in calls to the CreateWindow or CreateWindowEx function.
Expand Down Expand Up @@ -92,17 +90,17 @@ func (w *wndClassEx) unregister() error {
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb762159
type notifyIconData struct {
Size uint32
Wnd windows.Handle
Wnd syscall.Handle
ID, Flags, CallbackMessage uint32
Icon windows.Handle
Icon syscall.Handle
Tip [128]uint16
State, StateMask uint32
Info [256]uint16
Timeout, Version uint32
InfoTitle [64]uint16
InfoFlags uint32
GuidItem windows.GUID
BalloonIcon windows.Handle
GuidItem syscall.GUID
BalloonIcon syscall.Handle
}

func (nid *notifyIconData) add() error {
Expand Down Expand Up @@ -146,11 +144,11 @@ func (nid *notifyIconData) delete() error {
type menuItemInfo struct {
Size, Mask, Type, State uint32
ID uint32
SubMenu, Checked, Unchecked windows.Handle
SubMenu, Checked, Unchecked syscall.Handle
ItemData uintptr
TypeData *uint16
Cch uint32
Item windows.Handle
Item syscall.Handle
}

// The POINT structure defines the x- and y- coordinates of a point.
Expand All @@ -165,9 +163,9 @@ type winTray struct {
icon,
cursor,
window,
menu windows.Handle
menu syscall.Handle

loadedImages map[string]windows.Handle
loadedImages map[string]syscall.Handle
nid *notifyIconData
wcex *wndClassEx

Expand All @@ -189,7 +187,7 @@ func (t *winTray) setIcon(src string) error {
// Save and reuse handles of loaded images
h, ok := t.loadedImages[src]
if !ok {
srcPtr, err := windows.UTF16PtrFromString(src)
srcPtr, err := syscall.UTF16PtrFromString(src)
if err != nil {
return err
}
Expand All @@ -204,7 +202,7 @@ func (t *winTray) setIcon(src string) error {
if res == 0 {
return err
}
h = windows.Handle(res)
h = syscall.Handle(res)
t.loadedImages[src] = h
}

Expand All @@ -219,7 +217,7 @@ func (t *winTray) setIcon(src string) error {
// Shell_NotifyIcon: https://msdn.microsoft.com/en-us/library/windows/desktop/bb762159(v=vs.85).aspx
func (t *winTray) setTooltip(src string) error {
const NIF_TIP = 0x00000004
b, err := windows.UTF16FromString(src)
b, err := syscall.UTF16FromString(src)
if err != nil {
return err
}
Expand All @@ -234,7 +232,7 @@ var wt winTray

// WindowProc callback function that processes messages sent to a window.
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms633573(v=vs.85).aspx
func (t *winTray) wndProc(hWnd windows.Handle, message uint32, wParam, lParam uintptr) (lResult uintptr) {
func (t *winTray) wndProc(hWnd syscall.Handle, message uint32, wParam, lParam uintptr) (lResult uintptr) {
const (
WM_COMMAND = 0x0111
WM_DESTROY = 0x0002
Expand Down Expand Up @@ -310,53 +308,53 @@ func (t *winTray) initInstance() error {
)

t.wmSystrayMessage = WM_USER + 1

taskbarEventNamePtr, _ := windows.UTF16PtrFromString("TaskbarCreated")
taskbarEventNamePtr, _ := syscall.UTF16PtrFromString("TaskbarCreated")
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms644947
res, _, err := pRegisterWindowMessage.Call(
uintptr(unsafe.Pointer(taskbarEventNamePtr)),
)
t.wmTaskbarCreated = uint32(res)

t.loadedImages = make(map[string]windows.Handle)
t.loadedImages = make(map[string]syscall.Handle)

instanceHandle, _, err := pGetModuleHandle.Call(0)
if instanceHandle == 0 {
return err
}
t.instance = windows.Handle(instanceHandle)
t.instance = syscall.Handle(instanceHandle)

// https://msdn.microsoft.com/en-us/library/windows/desktop/ms648072(v=vs.85).aspx
iconHandle, _, err := pLoadIcon.Call(0, uintptr(IDI_APPLICATION))
if iconHandle == 0 {
return err
}
t.icon = windows.Handle(iconHandle)
t.icon = syscall.Handle(iconHandle)

// https://msdn.microsoft.com/en-us/library/windows/desktop/ms648391(v=vs.85).aspx
cursorHandle, _, err := pLoadCursor.Call(0, uintptr(IDC_ARROW))
if cursorHandle == 0 {
return err
}
t.cursor = windows.Handle(cursorHandle)
t.cursor = syscall.Handle(cursorHandle)

classNamePtr, err := windows.UTF16PtrFromString(className)
classNamePtr, err := syscall.UTF16PtrFromString(className)
if err != nil {
return err
}

windowNamePtr, err := windows.UTF16PtrFromString(windowName)
windowNamePtr, err := syscall.UTF16PtrFromString(windowName)
if err != nil {
return err
}

t.wcex = &wndClassEx{
Style: CS_HREDRAW | CS_VREDRAW,
WndProc: windows.NewCallback(t.wndProc),
WndProc: syscall.NewCallback(t.wndProc),
Instance: t.instance,
Icon: t.icon,
Cursor: t.cursor,
Background: windows.Handle(6), // (COLOR_WINDOW + 1)
Background: syscall.Handle(6), // (COLOR_WINDOW + 1)
ClassName: classNamePtr,
IconSm: t.icon,
}
Expand All @@ -381,7 +379,7 @@ func (t *winTray) initInstance() error {
if windowHandle == 0 {
return err
}
t.window = windows.Handle(windowHandle)
t.window = syscall.Handle(windowHandle)

pShowWindow.Call(
uintptr(t.window),
Expand All @@ -393,7 +391,7 @@ func (t *winTray) initInstance() error {
)

t.nid = &notifyIconData{
Wnd: windows.Handle(t.window),
Wnd: syscall.Handle(t.window),
ID: 100,
Flags: NIF_MESSAGE,
CallbackMessage: t.wmSystrayMessage,
Expand All @@ -410,12 +408,12 @@ func (t *winTray) createMenu() error {
if menuHandle == 0 {
return err
}
t.menu = windows.Handle(menuHandle)
t.menu = syscall.Handle(menuHandle)

// https://msdn.microsoft.com/en-us/library/windows/desktop/ms647575(v=vs.85).aspx
mi := struct {
Size, Mask, Style, Max uint32
Background windows.Handle
Background syscall.Handle
ContextHelpID uint32
MenuData uintptr
}{
Expand Down Expand Up @@ -446,7 +444,7 @@ func (t *winTray) addOrUpdateMenuItem(menuId int32, title string, disabled, chec
MFS_CHECKED = 0x00000008
MFS_DISABLED = 0x00000003
)
titlePtr, err := windows.UTF16PtrFromString(title)
titlePtr, err := syscall.UTF16PtrFromString(title)
if err != nil {
return err
}
Expand Down Expand Up @@ -614,7 +612,7 @@ func nativeLoop() {

// Main message pump.
m := &struct {
WindowHandle windows.Handle
WindowHandle syscall.Handle
Message uint32
Wparam uintptr
Lparam uintptr
Expand Down Expand Up @@ -652,6 +650,14 @@ func quit() {
)
}


func SetIconFile(filepath string) {
if err := wt.setIcon(filepath); err != nil {
log.Errorf("Unable to set icon: %v", err)
return
}
}

// SetIcon sets the systray icon.
// iconBytes should be the content of .ico for windows and .ico/.jpg/.png
// for other platforms.
Expand Down