add icons to taskbar control buttons
This commit is contained in:
committed by
Drew Weymouth
parent
aedaa32de4
commit
041e4d3ba7
@@ -449,6 +449,12 @@ func (a *App) SetupWindowsTaskbarButtons(hwnd uintptr) {
|
|||||||
a.PlaybackManager.SeekNext()
|
a.PlaybackManager.SeekNext()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
a.PlaybackManager.OnPlaying(func() {
|
||||||
|
windows.SetTaskbarButtonIsPlaying(true)
|
||||||
|
})
|
||||||
|
f := func() { windows.SetTaskbarButtonIsPlaying(false) }
|
||||||
|
a.PlaybackManager.OnPaused(f)
|
||||||
|
a.PlaybackManager.OnStopped(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) LoginToDefaultServer() error {
|
func (a *App) LoginToDefaultServer() error {
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
DEFINE_GUID(IID_ITaskbarList3,
|
DEFINE_GUID(IID_ITaskbarList3,
|
||||||
0xEA1AFB91, 0x9E28, 0x4B86, 0x90, 0xE9, 0x9E, 0x9F, 0x8A, 0x5E, 0xEF, 0xAF);
|
0xEA1AFB91, 0x9E28, 0x4B86, 0x90, 0xE9, 0x9E, 0x9F, 0x8A, 0x5E, 0xEF, 0xAF);
|
||||||
|
|
||||||
|
#define WM_SET_PLAYING_STATE (WM_APP + 1)
|
||||||
|
|
||||||
static ITaskbarList3 *g_taskbar = NULL;
|
static ITaskbarList3 *g_taskbar = NULL;
|
||||||
static ThumbnailCallback g_callback = NULL;
|
static ThumbnailCallback g_callback = NULL;
|
||||||
static HWND g_mainHWnd = NULL;
|
static HWND g_mainHWnd = NULL;
|
||||||
@@ -16,6 +18,62 @@ static WNDPROC g_originalProc = NULL;
|
|||||||
|
|
||||||
THUMBBUTTON g_thumbButtons[3];
|
THUMBBUTTON g_thumbButtons[3];
|
||||||
|
|
||||||
|
static HICON g_prevIcon;
|
||||||
|
static HICON g_nextIcon;
|
||||||
|
static HICON g_playIcon;
|
||||||
|
static HICON g_pauseIcon;
|
||||||
|
|
||||||
|
static HICON create_icon_from_bgra(const void *bgra, int width, int height) {
|
||||||
|
HICON hIcon = NULL;
|
||||||
|
|
||||||
|
// Create mask bitmap (not used, but required)
|
||||||
|
HBITMAP hMonoMask = CreateBitmap(width, height, 1, 1, NULL);
|
||||||
|
|
||||||
|
// Create color bitmap from provided pixels
|
||||||
|
BITMAPV5HEADER bi;
|
||||||
|
ZeroMemory(&bi, sizeof(bi));
|
||||||
|
bi.bV5Size = sizeof(bi);
|
||||||
|
bi.bV5Width = width;
|
||||||
|
bi.bV5Height = -height; // top-down DIB
|
||||||
|
bi.bV5Planes = 1;
|
||||||
|
bi.bV5BitCount = 32;
|
||||||
|
bi.bV5Compression = BI_BITFIELDS;
|
||||||
|
bi.bV5RedMask = 0x00FF0000;
|
||||||
|
bi.bV5GreenMask = 0x0000FF00;
|
||||||
|
bi.bV5BlueMask = 0x000000FF;
|
||||||
|
bi.bV5AlphaMask = 0xFF000000;
|
||||||
|
|
||||||
|
void *bits = NULL;
|
||||||
|
HDC hdc = GetDC(NULL);
|
||||||
|
HBITMAP hBmp = CreateDIBSection(hdc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, &bits, NULL, 0);
|
||||||
|
ReleaseDC(NULL, hdc);
|
||||||
|
|
||||||
|
if (hBmp && bits) {
|
||||||
|
memcpy(bits, bgra, width * height * 4);
|
||||||
|
|
||||||
|
ICONINFO ii;
|
||||||
|
ZeroMemory(&ii, sizeof(ii));
|
||||||
|
ii.fIcon = TRUE;
|
||||||
|
ii.hbmMask = hMonoMask;
|
||||||
|
ii.hbmColor = hBmp;
|
||||||
|
|
||||||
|
hIcon = CreateIconIndirect(&ii);
|
||||||
|
|
||||||
|
DeleteObject(hBmp);
|
||||||
|
DeleteObject(hMonoMask);
|
||||||
|
}
|
||||||
|
return hIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
int initialize_taskbar_icons(const void *bgraPrev, const void *bgraNext, const void *bgraPlay, const void *bgraPause, int width, int height) {
|
||||||
|
g_prevIcon = create_icon_from_bgra(bgraPrev, width, height);
|
||||||
|
g_nextIcon = create_icon_from_bgra(bgraNext, width, height);
|
||||||
|
g_playIcon = create_icon_from_bgra(bgraPlay, width, height);
|
||||||
|
g_pauseIcon = create_icon_from_bgra(bgraPause, width, height);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
LRESULT CALLBACK OverrideWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
LRESULT CALLBACK OverrideWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||||
if (msg == WM_COMMAND) {
|
if (msg == WM_COMMAND) {
|
||||||
int buttonId = LOWORD(wParam);
|
int buttonId = LOWORD(wParam);
|
||||||
@@ -24,18 +82,38 @@ LRESULT CALLBACK OverrideWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPar
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (msg == WM_SET_PLAYING_STATE) {
|
||||||
|
if (g_taskbar && g_mainHWnd && g_playIcon && g_pauseIcon) {
|
||||||
|
int playing = LOWORD(wParam) ? 1 : 0;
|
||||||
|
g_thumbButtons[1].hIcon = playing ? g_pauseIcon : g_playIcon;
|
||||||
|
wcscpy_s(g_thumbButtons[1].szTip, ARRAYSIZE(g_thumbButtons[0].szTip), playing ? L"Pause" : L"Play");
|
||||||
|
g_taskbar->lpVtbl->ThumbBarUpdateButtons(
|
||||||
|
g_taskbar,
|
||||||
|
g_mainHWnd,
|
||||||
|
ARRAYSIZE(g_thumbButtons),
|
||||||
|
g_thumbButtons
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return CallWindowProc(g_originalProc, hwnd, msg, wParam, lParam);
|
return CallWindowProc(g_originalProc, hwnd, msg, wParam, lParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hook_window_proc(HWND hwnd) {
|
int set_is_playing(int playing) {
|
||||||
g_originalProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)OverrideWndProc);
|
if (g_mainHWnd) {
|
||||||
|
PostMessage(g_mainHWnd, WM_SET_PLAYING_STATE, (WPARAM)playing, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int initialize_taskbar_buttons(void *hwndPtr, ThumbnailCallback cb) {
|
int initialize_taskbar_buttons(void *hwndPtr, ThumbnailCallback cb) {
|
||||||
g_callback = cb;
|
g_callback = cb;
|
||||||
g_mainHWnd = (HWND)hwndPtr;
|
g_mainHWnd = (HWND)hwndPtr;
|
||||||
hook_window_proc(g_mainHWnd);
|
|
||||||
|
// subclass the WndProc with our OverrideWndProc
|
||||||
|
g_originalProc = (WNDPROC)SetWindowLongPtr(g_mainHWnd, GWLP_WNDPROC, (LONG_PTR)OverrideWndProc);
|
||||||
|
|
||||||
CoInitialize(NULL);
|
CoInitialize(NULL);
|
||||||
CoCreateInstance(&CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, &IID_ITaskbarList3, (void**)&g_taskbar);
|
CoCreateInstance(&CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, &IID_ITaskbarList3, (void**)&g_taskbar);
|
||||||
@@ -52,16 +130,28 @@ int initialize_taskbar_buttons(void *hwndPtr, ThumbnailCallback cb) {
|
|||||||
g_thumbButtons[0].iId = 1;
|
g_thumbButtons[0].iId = 1;
|
||||||
g_thumbButtons[0].dwFlags = THBF_ENABLED;
|
g_thumbButtons[0].dwFlags = THBF_ENABLED;
|
||||||
wcscpy_s(g_thumbButtons[0].szTip, ARRAYSIZE(g_thumbButtons[0].szTip), L"Previous");
|
wcscpy_s(g_thumbButtons[0].szTip, ARRAYSIZE(g_thumbButtons[0].szTip), L"Previous");
|
||||||
|
if (g_prevIcon) {
|
||||||
|
g_thumbButtons[0].dwMask |= THB_ICON;
|
||||||
|
g_thumbButtons[0].hIcon = g_prevIcon;
|
||||||
|
}
|
||||||
|
|
||||||
g_thumbButtons[1].dwMask = THB_FLAGS | THB_TOOLTIP;
|
g_thumbButtons[1].dwMask = THB_FLAGS | THB_TOOLTIP;
|
||||||
g_thumbButtons[1].iId = 2;
|
g_thumbButtons[1].iId = 2;
|
||||||
g_thumbButtons[1].dwFlags = THBF_ENABLED;
|
g_thumbButtons[1].dwFlags = THBF_ENABLED;
|
||||||
wcscpy_s(g_thumbButtons[1].szTip, ARRAYSIZE(g_thumbButtons[1].szTip), L"Play");
|
wcscpy_s(g_thumbButtons[1].szTip, ARRAYSIZE(g_thumbButtons[1].szTip), L"Play");
|
||||||
|
if (g_playIcon) {
|
||||||
|
g_thumbButtons[1].dwMask |= THB_ICON;
|
||||||
|
g_thumbButtons[1].hIcon = g_playIcon;
|
||||||
|
}
|
||||||
|
|
||||||
g_thumbButtons[2].dwMask = THB_FLAGS | THB_TOOLTIP;
|
g_thumbButtons[2].dwMask = THB_FLAGS | THB_TOOLTIP;
|
||||||
g_thumbButtons[2].iId = 3;
|
g_thumbButtons[2].iId = 3;
|
||||||
g_thumbButtons[2].dwFlags = THBF_ENABLED;
|
g_thumbButtons[2].dwFlags = THBF_ENABLED;
|
||||||
wcscpy_s(g_thumbButtons[2].szTip, ARRAYSIZE(g_thumbButtons[2].szTip), L"Next");
|
wcscpy_s(g_thumbButtons[2].szTip, ARRAYSIZE(g_thumbButtons[2].szTip), L"Next");
|
||||||
|
if (g_nextIcon) {
|
||||||
|
g_thumbButtons[2].dwMask |= THB_ICON;
|
||||||
|
g_thumbButtons[2].hIcon = g_nextIcon;
|
||||||
|
}
|
||||||
|
|
||||||
g_taskbar->lpVtbl->ThumbBarAddButtons(g_taskbar, g_mainHWnd, ARRAYSIZE(g_thumbButtons), g_thumbButtons);
|
g_taskbar->lpVtbl->ThumbBarAddButtons(g_taskbar, g_mainHWnd, ARRAYSIZE(g_thumbButtons), g_thumbButtons);
|
||||||
|
|
||||||
|
|||||||
@@ -10,20 +10,122 @@ extern void goButtonClicked(int);
|
|||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"image"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
var gTaskbarButtonCallback func(TaskbarButton)
|
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 {
|
func InitializeTaskbarButtons(hwnd uintptr, callback func(TaskbarButton)) error {
|
||||||
gTaskbarButtonCallback = callback
|
gTaskbarButtonCallback = callback
|
||||||
C.initialize_taskbar_buttons(unsafe.Pointer(hwnd), C.ThumbnailCallback(C.goButtonClicked))
|
C.initialize_taskbar_buttons(unsafe.Pointer(hwnd), C.ThumbnailCallback(C.goButtonClicked))
|
||||||
return nil
|
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
|
//export goButtonClicked
|
||||||
func goButtonClicked(buttonID C.int) {
|
func goButtonClicked(buttonID C.int) {
|
||||||
if gTaskbarButtonCallback != nil {
|
if gTaskbarButtonCallback != nil {
|
||||||
gTaskbarButtonCallback(TaskbarButton(buttonID))
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,8 +6,17 @@ extern "C" {
|
|||||||
|
|
||||||
typedef void (*ThumbnailCallback)(int buttonId);
|
typedef void (*ThumbnailCallback)(int buttonId);
|
||||||
|
|
||||||
|
// sets the icons that will be used for the buttons.
|
||||||
|
// the arguments are pointers to BGRA pixel data, and the dimensions (w, h) of all 4 images.
|
||||||
|
int initialize_taskbar_icons(const void *bgraPrev, const void *bgraNext, const void *bgraPlay, const void *bgraPause, int width, int height);
|
||||||
|
|
||||||
|
// adds the buttons to the window and registers the given callback to receive the button press events.
|
||||||
int initialize_taskbar_buttons(void *hwndPtr, ThumbnailCallback cb);
|
int initialize_taskbar_buttons(void *hwndPtr, ThumbnailCallback cb);
|
||||||
|
|
||||||
|
// sets whether the player is playing. controls which icon/tooltip the center button uses.
|
||||||
|
// safe to call from any thread
|
||||||
|
int set_is_playing(int playing);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -2,8 +2,21 @@
|
|||||||
|
|
||||||
package windows
|
package windows
|
||||||
|
|
||||||
import "errors"
|
import (
|
||||||
|
"errors"
|
||||||
|
"image"
|
||||||
|
)
|
||||||
|
|
||||||
|
var err = errors.New("taskbar buttons unsupported")
|
||||||
|
|
||||||
func InitializeTaskbarButtons(hwnd uintptr, callback func(TaskbarButton)) error {
|
func InitializeTaskbarButtons(hwnd uintptr, callback func(TaskbarButton)) error {
|
||||||
return errors.New("taskbar buttons unsupported")
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitializeTaskbarIcons(prev, next, play, pause image.Image) error {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetTaskbarButtonIsPlaying(isPlaying bool) error {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"image/png"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -10,7 +12,9 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/dweymouth/supersonic/backend"
|
"github.com/dweymouth/supersonic/backend"
|
||||||
|
"github.com/dweymouth/supersonic/backend/windows"
|
||||||
"github.com/dweymouth/supersonic/res"
|
"github.com/dweymouth/supersonic/res"
|
||||||
|
"github.com/dweymouth/supersonic/res/wintaskbarthumbs"
|
||||||
"github.com/dweymouth/supersonic/ui"
|
"github.com/dweymouth/supersonic/ui"
|
||||||
"github.com/dweymouth/supersonic/ui/util"
|
"github.com/dweymouth/supersonic/ui/util"
|
||||||
|
|
||||||
@@ -75,6 +79,12 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
if err := initWindowsTaskbarIcons(); err != nil {
|
||||||
|
log.Printf("Error initializing taskbar thumbnail icons: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fyneApp := app.New()
|
fyneApp := app.New()
|
||||||
fyneApp.SetIcon(res.ResAppicon256Png)
|
fyneApp.SetIcon(res.ResAppicon256Png)
|
||||||
|
|
||||||
@@ -91,14 +101,13 @@ func main() {
|
|||||||
mainWindow.Controller.DoConnectToServerWorkflow(defaultServer)
|
mainWindow.Controller.DoConnectToServerWorkflow(defaultServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
if myApp.Config.Application.EnableOSMediaPlayerAPIs {
|
if runtime.GOOS == "windows" {
|
||||||
mainWindow.Window.(driver.NativeWindow).RunNative(func(ctx any) {
|
mainWindow.Window.(driver.NativeWindow).RunNative(func(ctx any) {
|
||||||
// intialize Windows SMTC
|
hwnd := ctx.(driver.WindowsWindowContext).HWND
|
||||||
if runtime.GOOS == "windows" {
|
if myApp.Config.Application.EnableOSMediaPlayerAPIs {
|
||||||
hwnd := ctx.(driver.WindowsWindowContext).HWND
|
|
||||||
myApp.SetupWindowsSMTC(hwnd)
|
myApp.SetupWindowsSMTC(hwnd)
|
||||||
myApp.SetupWindowsTaskbarButtons(hwnd)
|
|
||||||
}
|
}
|
||||||
|
myApp.SetupWindowsTaskbarButtons(hwnd)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -117,3 +126,26 @@ func main() {
|
|||||||
log.Println("Running shutdown tasks...")
|
log.Println("Running shutdown tasks...")
|
||||||
myApp.Shutdown()
|
myApp.Shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initWindowsTaskbarIcons() error {
|
||||||
|
play, err := png.Decode(bytes.NewReader(wintaskbarthumbs.MediaPlayPNG))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pause, err := png.Decode(bytes.NewReader(wintaskbarthumbs.MediaPausePNG))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
prev, err := png.Decode(bytes.NewReader(wintaskbarthumbs.MediaSeekPreviousPNG))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
next, err := png.Decode(bytes.NewReader(wintaskbarthumbs.MediaSeekNextPNG))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
windows.InitializeTaskbarIcons(prev, next, play, pause)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package wintaskbarthumbs
|
||||||
|
|
||||||
|
import _ "embed"
|
||||||
|
|
||||||
|
//go:embed media_pause.png
|
||||||
|
var MediaPausePNG []byte
|
||||||
|
|
||||||
|
//go:embed media_play.png
|
||||||
|
var MediaPlayPNG []byte
|
||||||
|
|
||||||
|
//go:embed media_seek_next.png
|
||||||
|
var MediaSeekNextPNG []byte
|
||||||
|
|
||||||
|
//go:embed media_seek_previous.png
|
||||||
|
var MediaSeekPreviousPNG []byte
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 376 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
Reference in New Issue
Block a user