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

Wrap setIcon NSImage ops within autoreleasepool pattern #240

Open
wants to merge 3 commits 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
10 changes: 6 additions & 4 deletions systray_darwin.m
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,12 @@ void runInMainThread(SEL method, id object) {

void setIcon(const char* iconBytes, int length, bool template) {
NSData* buffer = [NSData dataWithBytes: iconBytes length:length];
NSImage *image = [[NSImage alloc] initWithData:buffer];
[image setSize:NSMakeSize(16, 16)];
image.template = template;
runInMainThread(@selector(setIcon:), (id)image);
@autoreleasepool {
NSImage *image = [[NSImage alloc] initWithData:buffer];
[image setSize:NSMakeSize(16, 16)];
image.template = template;
runInMainThread(@selector(setIcon:), (id)image);
}
}

void setMenuItemIcon(const char* iconBytes, int length, int menuId, bool template) {
Expand Down
64 changes: 58 additions & 6 deletions systray_windows.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:build windows
// +build windows

package systray
Expand All @@ -22,6 +23,7 @@ var (
g32 = windows.NewLazySystemDLL("Gdi32.dll")
pCreateCompatibleBitmap = g32.NewProc("CreateCompatibleBitmap")
pCreateCompatibleDC = g32.NewProc("CreateCompatibleDC")
pCreateDIBSection = g32.NewProc("CreateDIBSection")
pDeleteDC = g32.NewProc("DeleteDC")
pSelectObject = g32.NewProc("SelectObject")

Expand Down Expand Up @@ -172,6 +174,30 @@ type point struct {
X, Y int32
}

// The BITMAPINFO structure defines the dimensions and color information for a DIB.
// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfo
type bitmapInfo struct {
BmiHeader bitmapInfoHeader
BmiColors windows.Handle
}

// The BITMAPINFOHEADER structure contains information about the dimensions and color format of a device-independent bitmap (DIB).
// https://learn.microsoft.com/en-us/previous-versions/dd183376(v=vs.85)
// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
type bitmapInfoHeader struct {
BiSize uint32
BiWidth int32
BiHeight int32
BiPlanes uint16
BiBitCount uint16
BiCompression uint32
BiSizeImage uint32
BiXPelsPerMeter int32
BiYPelsPerMeter int32
BiClrUsed uint32
BiClrImportant uint32
}

// Contains information about loaded resources
type winTray struct {
instance,
Expand Down Expand Up @@ -735,7 +761,7 @@ func (t *winTray) loadIconFrom(src string) (windows.Handle, error) {
return h, nil
}

func (t *winTray) iconToBitmap(hIcon windows.Handle) (windows.Handle, error) {
func iconToBitmap(hIcon windows.Handle) (windows.Handle, error) {
const SM_CXSMICON = 49
const SM_CYSMICON = 50
const DI_NORMAL = 0x3
Expand All @@ -751,10 +777,7 @@ func (t *winTray) iconToBitmap(hIcon windows.Handle) (windows.Handle, error) {
defer pDeleteDC.Call(hMemDC)
cx, _, _ := pGetSystemMetrics.Call(SM_CXSMICON)
cy, _, _ := pGetSystemMetrics.Call(SM_CYSMICON)
hMemBmp, _, err := pCreateCompatibleBitmap.Call(hDC, cx, cy)
if hMemBmp == 0 {
return 0, err
}
hMemBmp, err := create32BitHBitmap(hMemDC, int32(cx), int32(cy))
hOriginalBmp, _, _ := pSelectObject.Call(hMemDC, hMemBmp)
defer pSelectObject.Call(hMemDC, hOriginalBmp)
res, _, err := pDrawIconEx.Call(hMemDC, 0, 0, uintptr(hIcon), cx, cy, 0, uintptr(0), DI_NORMAL)
Expand All @@ -764,6 +787,35 @@ func (t *winTray) iconToBitmap(hIcon windows.Handle) (windows.Handle, error) {
return windows.Handle(hMemBmp), nil
}

// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createdibsection
func create32BitHBitmap(hDC uintptr, cx, cy int32) (uintptr, error) {
const BI_RGB uint32 = 0
const DIB_RGB_COLORS = 0
bmi := bitmapInfo{
BmiHeader: bitmapInfoHeader{
BiPlanes: 1,
BiCompression: BI_RGB,
BiWidth: cx,
BiHeight: cy,
BiBitCount: 32,
},
}
bmi.BmiHeader.BiSize = uint32(unsafe.Sizeof(bmi.BmiHeader))
var bits uintptr
hBitmap, _, err := pCreateDIBSection.Call(
hDC,
uintptr(unsafe.Pointer(&bmi)),
DIB_RGB_COLORS,
uintptr(unsafe.Pointer(&bits)),
uintptr(0),
0,
)
if hBitmap == 0 {
return 0, err
}
return hBitmap, nil
}

func registerSystray() {
if err := wt.initInstance(); err != nil {
log.Errorf("Unable to init instance: %v", err)
Expand Down Expand Up @@ -882,7 +934,7 @@ func (item *MenuItem) SetIcon(iconBytes []byte) {
return
}

h, err = wt.iconToBitmap(h)
h, err = iconToBitmap(h)
if err != nil {
log.Errorf("Unable to convert icon to bitmap: %v", err)
return
Expand Down