extract taskbar

This commit is contained in:
popaprozac 2025-04-26 18:48:27 -07:00
commit 800810f092
3 changed files with 124 additions and 96 deletions

View file

@ -9,8 +9,6 @@ import (
"image/color"
"image/png"
"os"
"syscall"
"unsafe"
"github.com/wailsapp/wails/v3/pkg/application"
"github.com/wailsapp/wails/v3/pkg/w32"
@ -19,95 +17,8 @@ import (
"golang.org/x/image/math/fixed"
)
var (
ole32 = syscall.NewLazyDLL("ole32.dll")
coCreateInstance = ole32.NewProc("CoCreateInstance")
)
const (
CLSCTX_INPROC_SERVER = 0x1
)
var (
CLSID_TaskbarList = syscall.GUID{0x56FDF344, 0xFD6D, 0x11D0, [8]byte{0x95, 0x8A, 0x00, 0x60, 0x97, 0xC9, 0xA0, 0x90}}
IID_ITaskbarList3 = syscall.GUID{0xEA1AFB91, 0x9E28, 0x4B86, [8]byte{0x90, 0xE9, 0x9E, 0x9F, 0x8A, 0x5E, 0xEF, 0xAF}}
)
type ITaskbarList3 struct {
lpVtbl *taskbarList3Vtbl
}
type taskbarList3Vtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
HrInit uintptr
AddTab uintptr
DeleteTab uintptr
ActivateTab uintptr
SetActiveAlt uintptr
MarkFullscreenWindow uintptr
SetProgressValue uintptr
SetProgressState uintptr
RegisterTab uintptr
UnregisterTab uintptr
SetTabOrder uintptr
SetTabActive uintptr
ThumbBarAddButtons uintptr
ThumbBarUpdateButtons uintptr
ThumbBarSetImageList uintptr
SetOverlayIcon uintptr
SetThumbnailTooltip uintptr
SetThumbnailClip uintptr
}
func newTaskbarList3() (*ITaskbarList3, error) {
const COINIT_APARTMENTTHREADED = 0x2
coInit := ole32.NewProc("CoInitializeEx")
if hr, _, _ := coInit.Call(0, COINIT_APARTMENTTHREADED); hr != 0 && hr != 0x1 {
return nil, syscall.Errno(hr)
}
var taskbar *ITaskbarList3
hr, _, _ := coCreateInstance.Call(
uintptr(unsafe.Pointer(&CLSID_TaskbarList)),
0,
uintptr(CLSCTX_INPROC_SERVER),
uintptr(unsafe.Pointer(&IID_ITaskbarList3)),
uintptr(unsafe.Pointer(&taskbar)),
)
if hr != 0 {
ole32.NewProc("CoUninitialize").Call()
return nil, syscall.Errno(hr)
}
if r, _, _ := syscall.SyscallN(taskbar.lpVtbl.HrInit, uintptr(unsafe.Pointer(taskbar))); r != 0 {
syscall.SyscallN(taskbar.lpVtbl.Release, uintptr(unsafe.Pointer(taskbar)))
ole32.NewProc("CoUninitialize").Call()
return nil, syscall.Errno(r)
}
return taskbar, nil
}
func (t *ITaskbarList3) SetOverlayIcon(hwnd syscall.Handle, hIcon syscall.Handle, description *uint16) error {
ret, _, _ := syscall.SyscallN(
t.lpVtbl.SetOverlayIcon,
uintptr(unsafe.Pointer(t)),
uintptr(hwnd),
uintptr(hIcon),
uintptr(unsafe.Pointer(description)),
)
if ret != 0 {
return syscall.Errno(ret)
}
return nil
}
type windowsBadge struct {
taskbar *ITaskbarList3
taskbar *w32.ITaskbarList3
badgeImg *image.RGBA
badgeSize int
fontManager *FontManager
@ -147,7 +58,7 @@ func NewWithOptions(options Options) *Service {
}
func (w *windowsBadge) Startup(ctx context.Context, options application.ServiceOptions) error {
taskbar, err := newTaskbarList3()
taskbar, err := w32.NewTaskbarList3()
if err != nil {
return err
}
@ -159,8 +70,8 @@ func (w *windowsBadge) Startup(ctx context.Context, options application.ServiceO
func (w *windowsBadge) Shutdown() error {
if w.taskbar != nil {
syscall.SyscallN(w.taskbar.lpVtbl.Release, uintptr(unsafe.Pointer(w.taskbar)))
ole32.NewProc("CoUninitialize").Call()
w.taskbar.Release()
w32.CoUninitialize()
}
return nil
@ -202,7 +113,7 @@ func (w *windowsBadge) SetBadge(label string) error {
}
defer w32.DestroyIcon(hicon)
return w.taskbar.SetOverlayIcon(syscall.Handle(hwnd), syscall.Handle(hicon), nil)
return w.taskbar.SetOverlayIcon(hwnd, hicon, nil)
}
func (w *windowsBadge) RemoveBadge() error {
@ -225,7 +136,7 @@ func (w *windowsBadge) RemoveBadge() error {
return err
}
return w.taskbar.SetOverlayIcon(syscall.Handle(hwnd), 0, nil)
return w.taskbar.SetOverlayIcon(hwnd, 0, nil)
}
func (w *windowsBadge) createBadgeIcon() (w32.HICON, error) {

View file

@ -7,9 +7,10 @@
package w32
import (
"github.com/wailsapp/go-webview2/pkg/combridge"
"syscall"
"unsafe"
"github.com/wailsapp/go-webview2/pkg/combridge"
)
var (
@ -19,6 +20,7 @@ var (
procCoInitialize = modole32.NewProc("CoInitialize")
procOleInitialize = modole32.NewProc("OleInitialize")
procCoUninitialize = modole32.NewProc("CoUninitialize")
procCoCreateInstance = modole32.NewProc("CoCreateInstance")
procCreateStreamOnHGlobal = modole32.NewProc("CreateStreamOnHGlobal")
procRegisterDragDrop = modole32.NewProc("RegisterDragDrop")
procRevokeDragDrop = modole32.NewProc("RevokeDragDrop")
@ -49,6 +51,26 @@ func CoUninitialize() {
procCoUninitialize.Call()
}
func CoCreateInstance(clsid *syscall.GUID, dwClsContext uintptr, riid *syscall.GUID, ppv uintptr) HRESULT {
ret, _, _ := procCoCreateInstance.Call(
uintptr(unsafe.Pointer(clsid)),
0,
uintptr(dwClsContext),
uintptr(unsafe.Pointer(riid)),
uintptr(ppv))
switch uint32(ret) {
case E_INVALIDARG:
panic("CoCreateInstance failed with E_INVALIDARG")
case E_OUTOFMEMORY:
panic("CoCreateInstance failed with E_OUTOFMEMORY")
case E_UNEXPECTED:
panic("CoCreateInstance failed with E_UNEXPECTED")
}
return HRESULT(ret)
}
func CreateStreamOnHGlobal(hGlobal HGLOBAL, fDeleteOnRelease bool) *IStream {
stream := new(IStream)
ret, _, _ := procCreateStreamOnHGlobal.Call(

95
v3/pkg/w32/taskbar.go Normal file
View file

@ -0,0 +1,95 @@
//go:build windows
package w32
import (
"syscall"
"unsafe"
)
var (
CLSID_TaskbarList = syscall.GUID{Data1: 0x56FDF344, Data2: 0xFD6D, Data3: 0x11D0, Data4: [8]byte{0x95, 0x8A, 0x00, 0x60, 0x97, 0xC9, 0xA0, 0x90}}
IID_ITaskbarList3 = syscall.GUID{Data1: 0xEA1AFB91, Data2: 0x9E28, Data3: 0x4B86, Data4: [8]byte{0x90, 0xE9, 0x9E, 0x9F, 0x8A, 0x5E, 0xEF, 0xAF}}
)
// ITaskbarList3 interface for Windows taskbar functionality
type ITaskbarList3 struct {
lpVtbl *taskbarList3Vtbl
}
type taskbarList3Vtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
HrInit uintptr
AddTab uintptr
DeleteTab uintptr
ActivateTab uintptr
SetActiveAlt uintptr
MarkFullscreenWindow uintptr
SetProgressValue uintptr
SetProgressState uintptr
RegisterTab uintptr
UnregisterTab uintptr
SetTabOrder uintptr
SetTabActive uintptr
ThumbBarAddButtons uintptr
ThumbBarUpdateButtons uintptr
ThumbBarSetImageList uintptr
SetOverlayIcon uintptr
SetThumbnailTooltip uintptr
SetThumbnailClip uintptr
}
// NewTaskbarList3 creates a new instance of ITaskbarList3
func NewTaskbarList3() (*ITaskbarList3, error) {
const COINIT_APARTMENTTHREADED = 0x2
if hrInit := CoInitializeEx(COINIT_APARTMENTTHREADED); hrInit != 0 && hrInit != 0x1 {
return nil, syscall.Errno(hrInit)
}
var taskbar *ITaskbarList3
hr := CoCreateInstance(
&CLSID_TaskbarList,
CLSCTX_INPROC_SERVER,
&IID_ITaskbarList3,
uintptr(unsafe.Pointer(&taskbar)),
)
if hr != 0 {
CoUninitialize()
return nil, syscall.Errno(hr)
}
if r, _, _ := syscall.SyscallN(taskbar.lpVtbl.HrInit, uintptr(unsafe.Pointer(taskbar))); r != 0 {
syscall.SyscallN(taskbar.lpVtbl.Release, uintptr(unsafe.Pointer(taskbar)))
CoUninitialize()
return nil, syscall.Errno(r)
}
return taskbar, nil
}
// SetOverlayIcon sets an overlay icon on the taskbar
func (t *ITaskbarList3) SetOverlayIcon(hwnd HWND, hIcon HICON, description *uint16) error {
ret, _, _ := syscall.SyscallN(
t.lpVtbl.SetOverlayIcon,
uintptr(unsafe.Pointer(t)),
uintptr(hwnd),
uintptr(hIcon),
uintptr(unsafe.Pointer(description)),
)
if ret != 0 {
return syscall.Errno(ret)
}
return nil
}
// Release releases the ITaskbarList3 interface
func (t *ITaskbarList3) Release() {
if t != nil {
syscall.SyscallN(t.lpVtbl.Release, uintptr(unsafe.Pointer(t)))
CoUninitialize()
}
}