132 lines
3.2 KiB
Go
132 lines
3.2 KiB
Go
//go:build windows
|
|
|
|
package windows
|
|
|
|
/*
|
|
#cgo LDFLAGS: -lole32
|
|
#include "taskbar_buttons.h"
|
|
|
|
extern void goButtonClicked(int);
|
|
*/
|
|
import "C"
|
|
import (
|
|
"errors"
|
|
"image"
|
|
"unsafe"
|
|
)
|
|
|
|
var gTaskbarButtonCallback func(TaskbarButton)
|
|
|
|
// InitializeTaskbarIcons supplies the image icons that will be used for the taskbar buttons.
|
|
// They must all have the same pixel dimensions.
|
|
// This should be called before InitializeTaskbarButtons or the buttons will not have icons.
|
|
func InitializeTaskbarIcons(prev, next, play, pause image.Image) error {
|
|
pB := imageToBGRA(prev)
|
|
nB := imageToBGRA(next)
|
|
plB := imageToBGRA(play)
|
|
paB := imageToBGRA(pause)
|
|
bnds := prev.Bounds()
|
|
|
|
C.initialize_taskbar_icons(unsafe.Pointer(&pB[0]), unsafe.Pointer(&nB[0]), unsafe.Pointer(&plB[0]), unsafe.Pointer(&paB[0]), C.int(bnds.Dx()), C.int(bnds.Dy()))
|
|
return nil
|
|
}
|
|
|
|
func InitializeTaskbarButtons(hwnd uintptr, callback func(TaskbarButton)) error {
|
|
gTaskbarButtonCallback = callback
|
|
C.initialize_taskbar_buttons(unsafe.Pointer(hwnd), C.ThumbnailCallback(C.goButtonClicked))
|
|
return nil
|
|
}
|
|
|
|
func SetTaskbarButtonIsPlaying(isPlaying bool) error {
|
|
i := C.int(0)
|
|
if isPlaying {
|
|
i = C.int(1)
|
|
}
|
|
if ret := int(C.set_is_playing(i)); ret == 0 {
|
|
return nil
|
|
}
|
|
return errors.New("failed to set taskbar button is playing state")
|
|
}
|
|
|
|
//export goButtonClicked
|
|
func goButtonClicked(buttonID C.int) {
|
|
if gTaskbarButtonCallback != nil {
|
|
gTaskbarButtonCallback(TaskbarButton(buttonID))
|
|
}
|
|
}
|
|
|
|
// imageToBGRA converts an image.Image to a BGRA byte slice.
|
|
// For *image.RGBA, it un-premultiplies the colors to straight alpha.
|
|
func imageToBGRA(img image.Image) []byte {
|
|
bounds := img.Bounds()
|
|
w, h := bounds.Dx(), bounds.Dy()
|
|
bgra := make([]byte, w*h*4)
|
|
|
|
i := 0
|
|
switch src := img.(type) {
|
|
case *image.NRGBA:
|
|
// Straight alpha, just swap channels
|
|
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
|
off := src.PixOffset(bounds.Min.X, y)
|
|
for x := 0; x < w; x++ {
|
|
r := src.Pix[off+0]
|
|
g := src.Pix[off+1]
|
|
b := src.Pix[off+2]
|
|
a := src.Pix[off+3]
|
|
bgra[i+0] = b
|
|
bgra[i+1] = g
|
|
bgra[i+2] = r
|
|
bgra[i+3] = a
|
|
off += 4
|
|
i += 4
|
|
}
|
|
}
|
|
|
|
case *image.RGBA:
|
|
// Premultiplied alpha, un-premultiply to straight alpha
|
|
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
|
off := src.PixOffset(bounds.Min.X, y)
|
|
for x := 0; x < w; x++ {
|
|
r := src.Pix[off+0]
|
|
g := src.Pix[off+1]
|
|
b := src.Pix[off+2]
|
|
a := src.Pix[off+3]
|
|
|
|
if a != 0 {
|
|
// Convert from premultiplied to straight alpha
|
|
r = uint8((uint16(r) * 0xFF) / uint16(a))
|
|
g = uint8((uint16(g) * 0xFF) / uint16(a))
|
|
b = uint8((uint16(b) * 0xFF) / uint16(a))
|
|
}
|
|
|
|
bgra[i+0] = b
|
|
bgra[i+1] = g
|
|
bgra[i+2] = r
|
|
bgra[i+3] = a
|
|
off += 4
|
|
i += 4
|
|
}
|
|
}
|
|
|
|
default:
|
|
// Fallback: handle any other image.Image type via At()
|
|
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
|
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
|
r16, g16, b16, a16 := img.At(x, y).RGBA()
|
|
// Convert from 16-bit [0, 65535] to 8-bit [0, 255]
|
|
r := uint8(r16 >> 8)
|
|
g := uint8(g16 >> 8)
|
|
b := uint8(b16 >> 8)
|
|
a := uint8(a16 >> 8)
|
|
bgra[i+0] = b
|
|
bgra[i+1] = g
|
|
bgra[i+2] = r
|
|
bgra[i+3] = a
|
|
i += 4
|
|
}
|
|
}
|
|
}
|
|
|
|
return bgra
|
|
}
|