mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 14:45:49 +01:00
Initial plan: Fix Windows-specific bugs from discussion #5001
Co-authored-by: leaanthony <1943904+leaanthony@users.noreply.github.com>
This commit is contained in:
parent
ed95232a47
commit
ff4e749ce6
39 changed files with 15198 additions and 0 deletions
174
v3/pkg/application/event_manager.go
Normal file
174
v3/pkg/application/event_manager.go
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
package application
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/events"
|
||||
)
|
||||
|
||||
// EventManager manages event-related operations
|
||||
type EventManager struct {
|
||||
app *App
|
||||
}
|
||||
|
||||
// newEventManager creates a new EventManager instance
|
||||
func newEventManager(app *App) *EventManager {
|
||||
return &EventManager{
|
||||
app: app,
|
||||
}
|
||||
}
|
||||
|
||||
// Emit emits a custom event with the specified name and associated data.
|
||||
// It returns a boolean indicating whether the event was cancelled by a hook.
|
||||
//
|
||||
// If no data argument is provided, Emit emits an event with nil data.
|
||||
// When there is exactly one data argument, it will be used as the custom event's data field.
|
||||
// When more than one argument is provided, the event's data field will be set to the argument slice.
|
||||
//
|
||||
// If the given event name is registered, Emit validates the data parameter
|
||||
// against the expected data type. In case of a mismatch, Emit reports an error
|
||||
// to the registered error handler for the application and cancels the event.
|
||||
func (em *EventManager) Emit(name string, data ...any) bool {
|
||||
event := &CustomEvent{Name: name}
|
||||
|
||||
if len(data) == 1 {
|
||||
event.Data = data[0]
|
||||
} else if len(data) > 1 {
|
||||
event.Data = data
|
||||
}
|
||||
|
||||
if err := em.app.customEventProcessor.Emit(event); err != nil {
|
||||
globalApplication.handleError(err)
|
||||
}
|
||||
|
||||
return event.IsCancelled()
|
||||
}
|
||||
|
||||
// EmitEvent emits a custom event object (internal use)
|
||||
// It returns a boolean indicating whether the event was cancelled by a hook.
|
||||
//
|
||||
// If the given event name is registered, emitEvent validates the data parameter
|
||||
// against the expected data type. In case of a mismatch, emitEvent reports an error
|
||||
// to the registered error handler for the application and cancels the event.
|
||||
func (em *EventManager) EmitEvent(event *CustomEvent) bool {
|
||||
if err := em.app.customEventProcessor.Emit(event); err != nil {
|
||||
globalApplication.handleError(err)
|
||||
}
|
||||
|
||||
return event.IsCancelled()
|
||||
}
|
||||
|
||||
// On registers a listener for custom events
|
||||
func (em *EventManager) On(name string, callback func(event *CustomEvent)) func() {
|
||||
return em.app.customEventProcessor.On(name, callback)
|
||||
}
|
||||
|
||||
// Off removes all listeners for a custom event
|
||||
func (em *EventManager) Off(name string) {
|
||||
em.app.customEventProcessor.Off(name)
|
||||
}
|
||||
|
||||
// OnMultiple registers a listener for custom events that will be called N times
|
||||
func (em *EventManager) OnMultiple(name string, callback func(event *CustomEvent), counter int) {
|
||||
em.app.customEventProcessor.OnMultiple(name, callback, counter)
|
||||
}
|
||||
|
||||
// Reset removes all custom event listeners
|
||||
func (em *EventManager) Reset() {
|
||||
em.app.customEventProcessor.OffAll()
|
||||
}
|
||||
|
||||
// OnApplicationEvent registers a listener for application events
|
||||
func (em *EventManager) OnApplicationEvent(eventType events.ApplicationEventType, callback func(event *ApplicationEvent)) func() {
|
||||
eventID := uint(eventType)
|
||||
em.app.applicationEventListenersLock.Lock()
|
||||
defer em.app.applicationEventListenersLock.Unlock()
|
||||
listener := &EventListener{
|
||||
callback: callback,
|
||||
}
|
||||
em.app.applicationEventListeners[eventID] = append(em.app.applicationEventListeners[eventID], listener)
|
||||
if em.app.impl != nil {
|
||||
go func() {
|
||||
defer handlePanic()
|
||||
em.app.impl.on(eventID)
|
||||
}()
|
||||
}
|
||||
|
||||
return func() {
|
||||
// lock the map
|
||||
em.app.applicationEventListenersLock.Lock()
|
||||
defer em.app.applicationEventListenersLock.Unlock()
|
||||
// Remove listener
|
||||
em.app.applicationEventListeners[eventID] = slices.DeleteFunc(em.app.applicationEventListeners[eventID], func(l *EventListener) bool {
|
||||
return l == listener
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterApplicationEventHook registers an application event hook
|
||||
func (em *EventManager) RegisterApplicationEventHook(eventType events.ApplicationEventType, callback func(event *ApplicationEvent)) func() {
|
||||
eventID := uint(eventType)
|
||||
em.app.applicationEventHooksLock.Lock()
|
||||
defer em.app.applicationEventHooksLock.Unlock()
|
||||
thisHook := &eventHook{
|
||||
callback: callback,
|
||||
}
|
||||
em.app.applicationEventHooks[eventID] = append(em.app.applicationEventHooks[eventID], thisHook)
|
||||
|
||||
return func() {
|
||||
em.app.applicationEventHooksLock.Lock()
|
||||
em.app.applicationEventHooks[eventID] = slices.DeleteFunc(em.app.applicationEventHooks[eventID], func(h *eventHook) bool {
|
||||
return h == thisHook
|
||||
})
|
||||
em.app.applicationEventHooksLock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch dispatches an event to listeners (internal use)
|
||||
func (em *EventManager) dispatch(event *CustomEvent) {
|
||||
// Snapshot listeners under Lock
|
||||
em.app.wailsEventListenerLock.Lock()
|
||||
listeners := slices.Clone(em.app.wailsEventListeners)
|
||||
em.app.wailsEventListenerLock.Unlock()
|
||||
|
||||
for _, listener := range listeners {
|
||||
if event.IsCancelled() {
|
||||
return
|
||||
}
|
||||
listener.DispatchWailsEvent(event)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleApplicationEvent handles application events (internal use)
|
||||
func (em *EventManager) handleApplicationEvent(event *ApplicationEvent) {
|
||||
defer handlePanic()
|
||||
em.app.applicationEventListenersLock.RLock()
|
||||
listeners, ok := em.app.applicationEventListeners[event.Id]
|
||||
em.app.applicationEventListenersLock.RUnlock()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Process Hooks
|
||||
em.app.applicationEventHooksLock.RLock()
|
||||
hooks, ok := em.app.applicationEventHooks[event.Id]
|
||||
em.app.applicationEventHooksLock.RUnlock()
|
||||
if ok {
|
||||
for _, thisHook := range hooks {
|
||||
thisHook.callback(event)
|
||||
if event.IsCancelled() {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, listener := range listeners {
|
||||
go func() {
|
||||
if event.IsCancelled() {
|
||||
return
|
||||
}
|
||||
defer handlePanic()
|
||||
listener.callback(event)
|
||||
}()
|
||||
}
|
||||
}
|
||||
2479
v3/pkg/application/webview_window_windows.go
Normal file
2479
v3/pkg/application/webview_window_windows.go
Normal file
File diff suppressed because it is too large
Load diff
27
v3/pkg/w32/actions.go
Normal file
27
v3/pkg/w32/actions.go
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
//go:build windows
|
||||
|
||||
package w32
|
||||
|
||||
func Undo(hwnd HWND) {
|
||||
SendMessage(hwnd, WM_UNDO, 0, 0)
|
||||
}
|
||||
|
||||
func Cut(hwnd HWND) {
|
||||
SendMessage(hwnd, WM_CUT, 0, 0)
|
||||
}
|
||||
|
||||
func Copy(hwnd HWND) {
|
||||
SendMessage(hwnd, WM_COPY, 0, 0)
|
||||
}
|
||||
|
||||
func Paste(hwnd HWND) {
|
||||
SendMessage(hwnd, WM_PASTE, 0, 0)
|
||||
}
|
||||
|
||||
func Delete(hwnd HWND) {
|
||||
SendMessage(hwnd, WM_CLEAR, 0, 0)
|
||||
}
|
||||
|
||||
func SelectAll(hwnd HWND) {
|
||||
SendMessage(hwnd, WM_SELECTALL, 0, 0)
|
||||
}
|
||||
143
v3/pkg/w32/clipboard.go
Normal file
143
v3/pkg/w32/clipboard.go
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
//go:build windows
|
||||
|
||||
/*
|
||||
* Based on code originally from https://github.com/atotto/clipboard. Copyright (c) 2013 Ato Araki. All rights reserved.
|
||||
*/
|
||||
|
||||
package w32
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
cfUnicodetext = 13
|
||||
gmemMoveable = 0x0002
|
||||
)
|
||||
|
||||
// waitOpenClipboard opens the clipboard, waiting for up to a second to do so.
|
||||
func waitOpenClipboard() error {
|
||||
started := time.Now()
|
||||
limit := started.Add(time.Second)
|
||||
var r uintptr
|
||||
var err error
|
||||
for time.Now().Before(limit) {
|
||||
r, _, err = procOpenClipboard.Call(0)
|
||||
if r != 0 {
|
||||
return nil
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func GetClipboardText() (string, error) {
|
||||
// LockOSThread ensure that the whole method will keep executing on the same thread from begin to end (it actually locks the goroutine thread attribution).
|
||||
// Otherwise if the goroutine switch thread during execution (which is a common practice), the OpenClipboard and CloseClipboard will happen on two different threads, and it will result in a clipboard deadlock.
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
if formatAvailable, _, err := procIsClipboardFormatAvailable.Call(cfUnicodetext); formatAvailable == 0 {
|
||||
return "", err
|
||||
}
|
||||
err := waitOpenClipboard()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
h, _, err := procGetClipboardData.Call(cfUnicodetext)
|
||||
if h == 0 {
|
||||
_, _, _ = procCloseClipboard.Call()
|
||||
return "", err
|
||||
}
|
||||
|
||||
l, _, err := kernelGlobalLock.Call(h)
|
||||
if l == 0 {
|
||||
_, _, _ = procCloseClipboard.Call()
|
||||
return "", err
|
||||
}
|
||||
|
||||
text := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(l))[:])
|
||||
|
||||
r, _, err := kernelGlobalUnlock.Call(h)
|
||||
if r == 0 {
|
||||
_, _, _ = procCloseClipboard.Call()
|
||||
return "", err
|
||||
}
|
||||
|
||||
closed, _, err := procCloseClipboard.Call()
|
||||
if closed == 0 {
|
||||
return "", err
|
||||
}
|
||||
return text, nil
|
||||
}
|
||||
|
||||
func SetClipboardText(text string) error {
|
||||
// LockOSThread ensure that the whole method will keep executing on the same thread from begin to end (it actually locks the goroutine thread attribution).
|
||||
// Otherwise if the goroutine switch thread during execution (which is a common practice), the OpenClipboard and CloseClipboard will happen on two different threads, and it will result in a clipboard deadlock.
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
err := waitOpenClipboard()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r, _, err := procEmptyClipboard.Call(0)
|
||||
if r == 0 {
|
||||
_, _, _ = procCloseClipboard.Call()
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := syscall.UTF16FromString(text)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// "If the hMem parameter identifies a memory object, the object must have
|
||||
// been allocated using the function with the GMEM_MOVEABLE flag."
|
||||
h, _, err := kernelGlobalAlloc.Call(gmemMoveable, uintptr(len(data)*int(unsafe.Sizeof(data[0]))))
|
||||
if h == 0 {
|
||||
_, _, _ = procCloseClipboard.Call()
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if h != 0 {
|
||||
kernelGlobalFree.Call(h)
|
||||
}
|
||||
}()
|
||||
|
||||
l, _, err := kernelGlobalLock.Call(h)
|
||||
if l == 0 {
|
||||
_, _, _ = procCloseClipboard.Call()
|
||||
return err
|
||||
}
|
||||
|
||||
r, _, err = kernelLstrcpy.Call(l, uintptr(unsafe.Pointer(&data[0])))
|
||||
if r == 0 {
|
||||
_, _, _ = procCloseClipboard.Call()
|
||||
return err
|
||||
}
|
||||
|
||||
r, _, err = kernelGlobalUnlock.Call(h)
|
||||
if r == 0 {
|
||||
if err.(syscall.Errno) != 0 {
|
||||
_, _, _ = procCloseClipboard.Call()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
r, _, err = procSetClipboardData.Call(cfUnicodetext, h)
|
||||
if r == 0 {
|
||||
_, _, _ = procCloseClipboard.Call()
|
||||
return err
|
||||
}
|
||||
h = 0 // suppress deferred cleanup
|
||||
closed, _, err := procCloseClipboard.Call()
|
||||
if closed == 0 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
55
v3/pkg/w32/com.go
Normal file
55
v3/pkg/w32/com.go
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
//go:build windows
|
||||
|
||||
package w32
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ComProc stores a COM procedure.
|
||||
type ComProc uintptr
|
||||
|
||||
// NewComProc creates a new COM proc from a Go function.
|
||||
func NewComProc(fn interface{}) ComProc {
|
||||
return ComProc(windows.NewCallback(fn))
|
||||
}
|
||||
|
||||
type EventRegistrationToken struct {
|
||||
value int64
|
||||
}
|
||||
|
||||
// IUnknown
|
||||
type IUnknown struct {
|
||||
Vtbl *IUnknownVtbl
|
||||
}
|
||||
|
||||
type IUnknownVtbl struct {
|
||||
QueryInterface ComProc
|
||||
AddRef ComProc
|
||||
Release ComProc
|
||||
}
|
||||
|
||||
func (i *IUnknownVtbl) CallRelease(this unsafe.Pointer) error {
|
||||
_, _, err := i.Release.Call(
|
||||
uintptr(this),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type IUnknownImpl interface {
|
||||
QueryInterface(refiid, object uintptr) uintptr
|
||||
AddRef() uintptr
|
||||
Release() uintptr
|
||||
}
|
||||
|
||||
// Call calls a COM procedure.
|
||||
//
|
||||
//go:uintptrescapes
|
||||
func (p ComProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
|
||||
return syscall.SyscallN(uintptr(p), a...)
|
||||
}
|
||||
112
v3/pkg/w32/comctl32.go
Normal file
112
v3/pkg/w32/comctl32.go
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
//go:build windows
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package w32
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
modcomctl32 = syscall.NewLazyDLL("comctl32.dll")
|
||||
|
||||
procInitCommonControlsEx = modcomctl32.NewProc("InitCommonControlsEx")
|
||||
procImageList_Create = modcomctl32.NewProc("ImageList_Create")
|
||||
procImageList_Destroy = modcomctl32.NewProc("ImageList_Destroy")
|
||||
procImageList_GetImageCount = modcomctl32.NewProc("ImageList_GetImageCount")
|
||||
procImageList_SetImageCount = modcomctl32.NewProc("ImageList_SetImageCount")
|
||||
procImageList_Add = modcomctl32.NewProc("ImageList_Add")
|
||||
procImageList_ReplaceIcon = modcomctl32.NewProc("ImageList_ReplaceIcon")
|
||||
procImageList_Remove = modcomctl32.NewProc("ImageList_Remove")
|
||||
procTrackMouseEvent = modcomctl32.NewProc("_TrackMouseEvent")
|
||||
)
|
||||
|
||||
func InitCommonControlsEx(lpInitCtrls *INITCOMMONCONTROLSEX) bool {
|
||||
ret, _, _ := procInitCommonControlsEx.Call(
|
||||
uintptr(unsafe.Pointer(lpInitCtrls)))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func ImageList_Create(cx, cy int, flags uint, cInitial, cGrow int) HIMAGELIST {
|
||||
ret, _, _ := procImageList_Create.Call(
|
||||
uintptr(cx),
|
||||
uintptr(cy),
|
||||
uintptr(flags),
|
||||
uintptr(cInitial),
|
||||
uintptr(cGrow))
|
||||
|
||||
if ret == 0 {
|
||||
panic("Create image list failed")
|
||||
}
|
||||
|
||||
return HIMAGELIST(ret)
|
||||
}
|
||||
|
||||
func ImageList_Destroy(himl HIMAGELIST) bool {
|
||||
ret, _, _ := procImageList_Destroy.Call(
|
||||
uintptr(himl))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func ImageList_GetImageCount(himl HIMAGELIST) int {
|
||||
ret, _, _ := procImageList_GetImageCount.Call(
|
||||
uintptr(himl))
|
||||
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func ImageList_SetImageCount(himl HIMAGELIST, uNewCount uint) bool {
|
||||
ret, _, _ := procImageList_SetImageCount.Call(
|
||||
uintptr(himl),
|
||||
uintptr(uNewCount))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func ImageList_Add(himl HIMAGELIST, hbmImage, hbmMask HBITMAP) int {
|
||||
ret, _, _ := procImageList_Add.Call(
|
||||
uintptr(himl),
|
||||
uintptr(hbmImage),
|
||||
uintptr(hbmMask))
|
||||
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func ImageList_ReplaceIcon(himl HIMAGELIST, i int, hicon HICON) int {
|
||||
ret, _, _ := procImageList_ReplaceIcon.Call(
|
||||
uintptr(himl),
|
||||
uintptr(i),
|
||||
uintptr(hicon))
|
||||
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func ImageList_AddIcon(himl HIMAGELIST, hicon HICON) int {
|
||||
return ImageList_ReplaceIcon(himl, -1, hicon)
|
||||
}
|
||||
|
||||
func ImageList_Remove(himl HIMAGELIST, i int) bool {
|
||||
ret, _, _ := procImageList_Remove.Call(
|
||||
uintptr(himl),
|
||||
uintptr(i))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func ImageList_RemoveAll(himl HIMAGELIST) bool {
|
||||
return ImageList_Remove(himl, -1)
|
||||
}
|
||||
|
||||
func TrackMouseEvent(tme *TRACKMOUSEEVENT) bool {
|
||||
ret, _, _ := procTrackMouseEvent.Call(
|
||||
uintptr(unsafe.Pointer(tme)))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
40
v3/pkg/w32/comdlg32.go
Normal file
40
v3/pkg/w32/comdlg32.go
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
//go:build windows
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved.
|
||||
*/
|
||||
package w32
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
modcomdlg32 = syscall.NewLazyDLL("comdlg32.dll")
|
||||
|
||||
procGetSaveFileName = modcomdlg32.NewProc("GetSaveFileNameW")
|
||||
procGetOpenFileName = modcomdlg32.NewProc("GetOpenFileNameW")
|
||||
procCommDlgExtendedError = modcomdlg32.NewProc("CommDlgExtendedError")
|
||||
)
|
||||
|
||||
func GetOpenFileName(ofn *OPENFILENAME) bool {
|
||||
ret, _, _ := procGetOpenFileName.Call(
|
||||
uintptr(unsafe.Pointer(ofn)))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func GetSaveFileName(ofn *OPENFILENAME) bool {
|
||||
ret, _, _ := procGetSaveFileName.Call(
|
||||
uintptr(unsafe.Pointer(ofn)))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func CommDlgExtendedError() uint {
|
||||
ret, _, _ := procCommDlgExtendedError.Call()
|
||||
|
||||
return uint(ret)
|
||||
}
|
||||
3731
v3/pkg/w32/constants.go
Normal file
3731
v3/pkg/w32/constants.go
Normal file
File diff suppressed because it is too large
Load diff
102
v3/pkg/w32/consts.go
Normal file
102
v3/pkg/w32/consts.go
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
//go:build windows
|
||||
|
||||
package w32
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
modwingdi = syscall.NewLazyDLL("gdi32.dll")
|
||||
procCreateSolidBrush = modwingdi.NewProc("CreateSolidBrush")
|
||||
)
|
||||
var (
|
||||
kernel32 = syscall.NewLazyDLL("kernel32")
|
||||
kernelGlobalAlloc = kernel32.NewProc("GlobalAlloc")
|
||||
kernelGlobalFree = kernel32.NewProc("GlobalFree")
|
||||
kernelGlobalLock = kernel32.NewProc("GlobalLock")
|
||||
kernelGlobalUnlock = kernel32.NewProc("GlobalUnlock")
|
||||
kernelLstrcpy = kernel32.NewProc("lstrcpyW")
|
||||
)
|
||||
var (
|
||||
modBranding = syscall.NewLazyDLL("winbrand.dll")
|
||||
brandingFormatString = modBranding.NewProc("BrandingFormatString")
|
||||
)
|
||||
|
||||
var windowsVersion, _ = GetWindowsVersionInfo()
|
||||
|
||||
func IsWindowsVersionAtLeast(major, minor, buildNumber int) bool {
|
||||
return windowsVersion.Major >= major &&
|
||||
windowsVersion.Minor >= minor &&
|
||||
windowsVersion.Build >= buildNumber
|
||||
}
|
||||
|
||||
type WindowsVersionInfo struct {
|
||||
Major int
|
||||
Minor int
|
||||
Build int
|
||||
DisplayVersion string
|
||||
}
|
||||
|
||||
func (w *WindowsVersionInfo) String() string {
|
||||
return fmt.Sprintf("%d.%d.%d (%s)", w.Major, w.Minor, w.Build, w.DisplayVersion)
|
||||
}
|
||||
|
||||
func (w *WindowsVersionInfo) IsWindowsVersionAtLeast(major, minor, buildNumber int) bool {
|
||||
return w.Major >= major && w.Minor >= minor && w.Build >= buildNumber
|
||||
}
|
||||
|
||||
func GetBranding() string {
|
||||
windowsLong := MustStringToUTF16Ptr("%WINDOWS_LONG%\x00")
|
||||
ret, _, _ := brandingFormatString.Call(
|
||||
uintptr(unsafe.Pointer(windowsLong)),
|
||||
)
|
||||
return windows.UTF16PtrToString((*uint16)(unsafe.Pointer(ret)))
|
||||
}
|
||||
|
||||
func GetWindowsVersionInfo() (*WindowsVersionInfo, error) {
|
||||
key, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &WindowsVersionInfo{
|
||||
Major: regDWORDKeyAsInt(key, "CurrentMajorVersionNumber"),
|
||||
Minor: regDWORDKeyAsInt(key, "CurrentMinorVersionNumber"),
|
||||
Build: regStringKeyAsInt(key, "CurrentBuildNumber"),
|
||||
DisplayVersion: regKeyAsString(key, "DisplayVersion"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func regDWORDKeyAsInt(key registry.Key, name string) int {
|
||||
result, _, err := key.GetIntegerValue(name)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return int(result)
|
||||
}
|
||||
|
||||
func regStringKeyAsInt(key registry.Key, name string) int {
|
||||
resultStr, _, err := key.GetStringValue(name)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
result, err := strconv.Atoi(resultStr)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func regKeyAsString(key registry.Key, name string) string {
|
||||
resultStr, _, err := key.GetStringValue(name)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return resultStr
|
||||
}
|
||||
28
v3/pkg/w32/dialogs.go
Normal file
28
v3/pkg/w32/dialogs.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
//go:build windows
|
||||
|
||||
package w32
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func MessageBoxWithIcon(hwnd HWND, text *uint16, caption *uint16, iconID int, flags uint32) (int32, error) {
|
||||
|
||||
params := MSGBOXPARAMS{
|
||||
cbSize: uint32(unsafe.Sizeof(MSGBOXPARAMS{})),
|
||||
hwndOwner: hwnd,
|
||||
hInstance: GetApplicationHandle(),
|
||||
lpszText: text,
|
||||
lpszCaption: caption,
|
||||
dwStyle: flags,
|
||||
lpszIcon: (*uint16)(unsafe.Pointer(uintptr(iconID))),
|
||||
}
|
||||
|
||||
r, _, err := procMessageBoxIndirect.Call(
|
||||
uintptr(unsafe.Pointer(¶ms)),
|
||||
)
|
||||
if r == 0 {
|
||||
return 0, err
|
||||
}
|
||||
return int32(r), nil
|
||||
}
|
||||
46
v3/pkg/w32/dwmapi.go
Normal file
46
v3/pkg/w32/dwmapi.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
//go:build windows
|
||||
|
||||
package w32
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
moddwmapi = syscall.NewLazyDLL("dwmapi.dll")
|
||||
|
||||
procDwmSetWindowAttribute = moddwmapi.NewProc("DwmSetWindowAttribute")
|
||||
procDwmGetWindowAttribute = moddwmapi.NewProc("DwmGetWindowAttribute")
|
||||
procDwmExtendFrameIntoClientArea = moddwmapi.NewProc("DwmExtendFrameIntoClientArea")
|
||||
)
|
||||
|
||||
func DwmSetWindowAttribute(hwnd HWND, dwAttribute DWMWINDOWATTRIBUTE, pvAttribute unsafe.Pointer, cbAttribute uintptr) HRESULT {
|
||||
ret, _, _ := procDwmSetWindowAttribute.Call(
|
||||
hwnd,
|
||||
uintptr(dwAttribute),
|
||||
uintptr(pvAttribute),
|
||||
cbAttribute)
|
||||
return HRESULT(ret)
|
||||
}
|
||||
|
||||
func DwmGetWindowAttribute(hwnd HWND, dwAttribute DWMWINDOWATTRIBUTE, pvAttribute unsafe.Pointer, cbAttribute uintptr) HRESULT {
|
||||
ret, _, _ := procDwmGetWindowAttribute.Call(
|
||||
hwnd,
|
||||
uintptr(dwAttribute),
|
||||
uintptr(pvAttribute),
|
||||
cbAttribute)
|
||||
return HRESULT(ret)
|
||||
}
|
||||
|
||||
func dwmExtendFrameIntoClientArea(hwnd uintptr, margins *MARGINS) error {
|
||||
ret, _, _ := procDwmExtendFrameIntoClientArea.Call(
|
||||
hwnd,
|
||||
uintptr(unsafe.Pointer(margins)))
|
||||
|
||||
if ret != 0 {
|
||||
return syscall.GetLastError()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
581
v3/pkg/w32/gdi32.go
Normal file
581
v3/pkg/w32/gdi32.go
Normal file
|
|
@ -0,0 +1,581 @@
|
|||
//go:build windows
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved.
|
||||
*/
|
||||
package w32
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
modgdi32 = syscall.NewLazyDLL("gdi32.dll")
|
||||
|
||||
procGetDeviceCaps = modgdi32.NewProc("GetDeviceCaps")
|
||||
procDeleteObject = modgdi32.NewProc("DeleteObject")
|
||||
procCreateFontIndirect = modgdi32.NewProc("CreateFontIndirectW")
|
||||
procAbortDoc = modgdi32.NewProc("AbortDoc")
|
||||
procBitBlt = modgdi32.NewProc("BitBlt")
|
||||
procPatBlt = modgdi32.NewProc("PatBlt")
|
||||
procCloseEnhMetaFile = modgdi32.NewProc("CloseEnhMetaFile")
|
||||
procCopyEnhMetaFile = modgdi32.NewProc("CopyEnhMetaFileW")
|
||||
procCreateBrushIndirect = modgdi32.NewProc("CreateBrushIndirect")
|
||||
procCreateCompatibleDC = modgdi32.NewProc("CreateCompatibleDC")
|
||||
procCreateDC = modgdi32.NewProc("CreateDCW")
|
||||
procCreateDIBSection = modgdi32.NewProc("CreateDIBSection")
|
||||
procCreateEnhMetaFile = modgdi32.NewProc("CreateEnhMetaFileW")
|
||||
procCreateIC = modgdi32.NewProc("CreateICW")
|
||||
procDeleteDC = modgdi32.NewProc("DeleteDC")
|
||||
procDeleteEnhMetaFile = modgdi32.NewProc("DeleteEnhMetaFile")
|
||||
procEllipse = modgdi32.NewProc("Ellipse")
|
||||
procEndDoc = modgdi32.NewProc("EndDoc")
|
||||
procEndPage = modgdi32.NewProc("EndPage")
|
||||
procExtCreatePen = modgdi32.NewProc("ExtCreatePen")
|
||||
procGetEnhMetaFile = modgdi32.NewProc("GetEnhMetaFileW")
|
||||
procGetEnhMetaFileHeader = modgdi32.NewProc("GetEnhMetaFileHeader")
|
||||
procGetObject = modgdi32.NewProc("GetObjectW")
|
||||
procGetStockObject = modgdi32.NewProc("GetStockObject")
|
||||
procGetTextExtentExPoint = modgdi32.NewProc("GetTextExtentExPointW")
|
||||
procGetTextExtentPoint32 = modgdi32.NewProc("GetTextExtentPoint32W")
|
||||
procGetTextMetrics = modgdi32.NewProc("GetTextMetricsW")
|
||||
procLineTo = modgdi32.NewProc("LineTo")
|
||||
procMoveToEx = modgdi32.NewProc("MoveToEx")
|
||||
procPlayEnhMetaFile = modgdi32.NewProc("PlayEnhMetaFile")
|
||||
procRectangle = modgdi32.NewProc("Rectangle")
|
||||
procResetDC = modgdi32.NewProc("ResetDCW")
|
||||
procSelectObject = modgdi32.NewProc("SelectObject")
|
||||
procSetBkMode = modgdi32.NewProc("SetBkMode")
|
||||
procSetBrushOrgEx = modgdi32.NewProc("SetBrushOrgEx")
|
||||
procSetStretchBltMode = modgdi32.NewProc("SetStretchBltMode")
|
||||
procSetTextColor = modgdi32.NewProc("SetTextColor")
|
||||
procSetBkColor = modgdi32.NewProc("SetBkColor")
|
||||
procStartDoc = modgdi32.NewProc("StartDocW")
|
||||
procStartPage = modgdi32.NewProc("StartPage")
|
||||
procStretchBlt = modgdi32.NewProc("StretchBlt")
|
||||
procSetDIBitsToDevice = modgdi32.NewProc("SetDIBitsToDevice")
|
||||
procChoosePixelFormat = modgdi32.NewProc("ChoosePixelFormat")
|
||||
procDescribePixelFormat = modgdi32.NewProc("DescribePixelFormat")
|
||||
procGetEnhMetaFilePixelFormat = modgdi32.NewProc("GetEnhMetaFilePixelFormat")
|
||||
procGetPixelFormat = modgdi32.NewProc("GetPixelFormat")
|
||||
procSetPixelFormat = modgdi32.NewProc("SetPixelFormat")
|
||||
procSwapBuffers = modgdi32.NewProc("SwapBuffers")
|
||||
procSaveDC = modgdi32.NewProc("SaveDC")
|
||||
procRestoreDC = modgdi32.NewProc("RestoreDC")
|
||||
procSelectClipRgn = modgdi32.NewProc("SelectClipRgn")
|
||||
procExcludeClipRect = modgdi32.NewProc("ExcludeClipRect")
|
||||
procExtTextOut = modgdi32.NewProc("ExtTextOutW")
|
||||
)
|
||||
|
||||
func GetDeviceCaps(hdc HDC, index int) int {
|
||||
ret, _, _ := procGetDeviceCaps.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(index))
|
||||
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func DeleteObject(hObject HGDIOBJ) bool {
|
||||
ret, _, _ := procDeleteObject.Call(
|
||||
uintptr(hObject))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func CreateFontIndirect(logFont *LOGFONT) HFONT {
|
||||
ret, _, _ := procCreateFontIndirect.Call(
|
||||
uintptr(unsafe.Pointer(logFont)))
|
||||
|
||||
return HFONT(ret)
|
||||
}
|
||||
|
||||
func AbortDoc(hdc HDC) int {
|
||||
ret, _, _ := procAbortDoc.Call(
|
||||
uintptr(hdc))
|
||||
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func BitBlt(hdcDest HDC, nXDest, nYDest, nWidth, nHeight int, hdcSrc HDC, nXSrc, nYSrc int, dwRop uint) {
|
||||
ret, _, _ := procBitBlt.Call(
|
||||
uintptr(hdcDest),
|
||||
uintptr(nXDest),
|
||||
uintptr(nYDest),
|
||||
uintptr(nWidth),
|
||||
uintptr(nHeight),
|
||||
uintptr(hdcSrc),
|
||||
uintptr(nXSrc),
|
||||
uintptr(nYSrc),
|
||||
uintptr(dwRop))
|
||||
|
||||
if ret == 0 {
|
||||
panic("BitBlt failed")
|
||||
}
|
||||
}
|
||||
|
||||
func PatBlt(hdc HDC, nXLeft, nYLeft, nWidth, nHeight int, dwRop uint) {
|
||||
ret, _, _ := procPatBlt.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(nXLeft),
|
||||
uintptr(nYLeft),
|
||||
uintptr(nWidth),
|
||||
uintptr(nHeight),
|
||||
uintptr(dwRop))
|
||||
|
||||
if ret == 0 {
|
||||
panic("PatBlt failed")
|
||||
}
|
||||
}
|
||||
|
||||
func CloseEnhMetaFile(hdc HDC) HENHMETAFILE {
|
||||
ret, _, _ := procCloseEnhMetaFile.Call(
|
||||
uintptr(hdc))
|
||||
|
||||
return HENHMETAFILE(ret)
|
||||
}
|
||||
|
||||
func CopyEnhMetaFile(hemfSrc HENHMETAFILE, lpszFile *uint16) HENHMETAFILE {
|
||||
ret, _, _ := procCopyEnhMetaFile.Call(
|
||||
uintptr(hemfSrc),
|
||||
uintptr(unsafe.Pointer(lpszFile)))
|
||||
|
||||
return HENHMETAFILE(ret)
|
||||
}
|
||||
|
||||
func CreateBrushIndirect(lplb *LOGBRUSH) HBRUSH {
|
||||
ret, _, _ := procCreateBrushIndirect.Call(
|
||||
uintptr(unsafe.Pointer(lplb)))
|
||||
|
||||
return HBRUSH(ret)
|
||||
}
|
||||
|
||||
func CreateCompatibleDC(hdc HDC) HDC {
|
||||
ret, _, _ := procCreateCompatibleDC.Call(
|
||||
uintptr(hdc))
|
||||
|
||||
if ret == 0 {
|
||||
panic("Create compatible DC failed")
|
||||
}
|
||||
|
||||
return HDC(ret)
|
||||
}
|
||||
|
||||
func CreateDC(lpszDriver, lpszDevice, lpszOutput *uint16, lpInitData *DEVMODE) HDC {
|
||||
ret, _, _ := procCreateDC.Call(
|
||||
uintptr(unsafe.Pointer(lpszDriver)),
|
||||
uintptr(unsafe.Pointer(lpszDevice)),
|
||||
uintptr(unsafe.Pointer(lpszOutput)),
|
||||
uintptr(unsafe.Pointer(lpInitData)))
|
||||
|
||||
return HDC(ret)
|
||||
}
|
||||
|
||||
func CreateDIBSection(hdc HDC, pbmi *BITMAPINFO, iUsage uint, ppvBits *unsafe.Pointer, hSection HANDLE, dwOffset uint) HBITMAP {
|
||||
ret, _, _ := procCreateDIBSection.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(unsafe.Pointer(pbmi)),
|
||||
uintptr(iUsage),
|
||||
uintptr(unsafe.Pointer(ppvBits)),
|
||||
uintptr(hSection),
|
||||
uintptr(dwOffset))
|
||||
|
||||
return HBITMAP(ret)
|
||||
}
|
||||
|
||||
func CreateEnhMetaFile(hdcRef HDC, lpFilename *uint16, lpRect *RECT, lpDescription *uint16) HDC {
|
||||
ret, _, _ := procCreateEnhMetaFile.Call(
|
||||
uintptr(hdcRef),
|
||||
uintptr(unsafe.Pointer(lpFilename)),
|
||||
uintptr(unsafe.Pointer(lpRect)),
|
||||
uintptr(unsafe.Pointer(lpDescription)))
|
||||
|
||||
return HDC(ret)
|
||||
}
|
||||
|
||||
func CreateIC(lpszDriver, lpszDevice, lpszOutput *uint16, lpdvmInit *DEVMODE) HDC {
|
||||
ret, _, _ := procCreateIC.Call(
|
||||
uintptr(unsafe.Pointer(lpszDriver)),
|
||||
uintptr(unsafe.Pointer(lpszDevice)),
|
||||
uintptr(unsafe.Pointer(lpszOutput)),
|
||||
uintptr(unsafe.Pointer(lpdvmInit)))
|
||||
|
||||
return HDC(ret)
|
||||
}
|
||||
|
||||
func DeleteDC(hdc HDC) bool {
|
||||
ret, _, _ := procDeleteDC.Call(
|
||||
uintptr(hdc))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func DeleteEnhMetaFile(hemf HENHMETAFILE) bool {
|
||||
ret, _, _ := procDeleteEnhMetaFile.Call(
|
||||
uintptr(hemf))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func Ellipse(hdc HDC, nLeftRect, nTopRect, nRightRect, nBottomRect int32) bool {
|
||||
ret, _, _ := procEllipse.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(nLeftRect),
|
||||
uintptr(nTopRect),
|
||||
uintptr(nRightRect),
|
||||
uintptr(nBottomRect))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func EndDoc(hdc HDC) int {
|
||||
ret, _, _ := procEndDoc.Call(
|
||||
uintptr(hdc))
|
||||
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func EndPage(hdc HDC) int {
|
||||
ret, _, _ := procEndPage.Call(
|
||||
uintptr(hdc))
|
||||
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func ExtCreatePen(dwPenStyle, dwWidth uint, lplb *LOGBRUSH, dwStyleCount uint, lpStyle *uint) HPEN {
|
||||
ret, _, _ := procExtCreatePen.Call(
|
||||
uintptr(dwPenStyle),
|
||||
uintptr(dwWidth),
|
||||
uintptr(unsafe.Pointer(lplb)),
|
||||
uintptr(dwStyleCount),
|
||||
uintptr(unsafe.Pointer(lpStyle)))
|
||||
|
||||
return HPEN(ret)
|
||||
}
|
||||
|
||||
func GetEnhMetaFile(lpszMetaFile *uint16) HENHMETAFILE {
|
||||
ret, _, _ := procGetEnhMetaFile.Call(
|
||||
uintptr(unsafe.Pointer(lpszMetaFile)))
|
||||
|
||||
return HENHMETAFILE(ret)
|
||||
}
|
||||
|
||||
func GetEnhMetaFileHeader(hemf HENHMETAFILE, cbBuffer uint, lpemh *ENHMETAHEADER) uint {
|
||||
ret, _, _ := procGetEnhMetaFileHeader.Call(
|
||||
uintptr(hemf),
|
||||
uintptr(cbBuffer),
|
||||
uintptr(unsafe.Pointer(lpemh)))
|
||||
|
||||
return uint(ret)
|
||||
}
|
||||
|
||||
func GetObject(hgdiobj HGDIOBJ, cbBuffer uintptr, lpvObject unsafe.Pointer) int {
|
||||
ret, _, _ := procGetObject.Call(
|
||||
uintptr(hgdiobj),
|
||||
uintptr(cbBuffer),
|
||||
uintptr(lpvObject))
|
||||
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func GetStockObject(fnObject int) HGDIOBJ {
|
||||
ret, _, _ := procGetDeviceCaps.Call(
|
||||
uintptr(fnObject))
|
||||
|
||||
return HGDIOBJ(ret)
|
||||
}
|
||||
|
||||
func GetTextExtentExPoint(hdc HDC, lpszStr *uint16, cchString, nMaxExtent int, lpnFit, alpDx *int, lpSize *SIZE) bool {
|
||||
ret, _, _ := procGetTextExtentExPoint.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(unsafe.Pointer(lpszStr)),
|
||||
uintptr(cchString),
|
||||
uintptr(nMaxExtent),
|
||||
uintptr(unsafe.Pointer(lpnFit)),
|
||||
uintptr(unsafe.Pointer(alpDx)),
|
||||
uintptr(unsafe.Pointer(lpSize)))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func GetTextExtentPoint32(hdc HDC, lpString *uint16, c int, lpSize *SIZE) bool {
|
||||
ret, _, _ := procGetTextExtentPoint32.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(unsafe.Pointer(lpString)),
|
||||
uintptr(c),
|
||||
uintptr(unsafe.Pointer(lpSize)))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func GetTextMetrics(hdc HDC, lptm *TEXTMETRIC) bool {
|
||||
ret, _, _ := procGetTextMetrics.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(unsafe.Pointer(lptm)))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func LineTo(hdc HDC, nXEnd, nYEnd int32) bool {
|
||||
ret, _, _ := procLineTo.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(nXEnd),
|
||||
uintptr(nYEnd))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func MoveToEx(hdc HDC, x, y int, lpPoint *POINT) bool {
|
||||
ret, _, _ := procMoveToEx.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(x),
|
||||
uintptr(y),
|
||||
uintptr(unsafe.Pointer(lpPoint)))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func PlayEnhMetaFile(hdc HDC, hemf HENHMETAFILE, lpRect *RECT) bool {
|
||||
ret, _, _ := procPlayEnhMetaFile.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(hemf),
|
||||
uintptr(unsafe.Pointer(lpRect)))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func Rectangle(hdc HDC, nLeftRect, nTopRect, nRightRect, nBottomRect int32) bool {
|
||||
ret, _, _ := procRectangle.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(nLeftRect),
|
||||
uintptr(nTopRect),
|
||||
uintptr(nRightRect),
|
||||
uintptr(nBottomRect))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func ResetDC(hdc HDC, lpInitData *DEVMODE) HDC {
|
||||
ret, _, _ := procResetDC.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(unsafe.Pointer(lpInitData)))
|
||||
|
||||
return HDC(ret)
|
||||
}
|
||||
|
||||
func SelectObject(hdc HDC, hgdiobj HGDIOBJ) HGDIOBJ {
|
||||
ret, _, _ := procSelectObject.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(hgdiobj))
|
||||
|
||||
if ret == 0 {
|
||||
panic("SelectObject failed")
|
||||
}
|
||||
|
||||
return HGDIOBJ(ret)
|
||||
}
|
||||
|
||||
func SetBkMode(hdc HDC, iBkMode int) int {
|
||||
ret, _, _ := procSetBkMode.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(iBkMode))
|
||||
|
||||
if ret == 0 {
|
||||
panic("SetBkMode failed")
|
||||
}
|
||||
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func SetBrushOrgEx(hdc HDC, nXOrg, nYOrg int, lppt *POINT) bool {
|
||||
ret, _, _ := procSetBrushOrgEx.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(nXOrg),
|
||||
uintptr(nYOrg),
|
||||
uintptr(unsafe.Pointer(lppt)))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func SetStretchBltMode(hdc HDC, iStretchMode int) int {
|
||||
ret, _, _ := procSetStretchBltMode.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(iStretchMode))
|
||||
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func SetTextColor(hdc HDC, crColor COLORREF) COLORREF {
|
||||
ret, _, _ := procSetTextColor.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(crColor))
|
||||
|
||||
if ret == CLR_INVALID {
|
||||
panic("SetTextColor failed")
|
||||
}
|
||||
|
||||
return COLORREF(ret)
|
||||
}
|
||||
|
||||
func SetBkColor(hdc HDC, crColor COLORREF) COLORREF {
|
||||
ret, _, _ := procSetBkColor.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(crColor))
|
||||
|
||||
if ret == CLR_INVALID {
|
||||
panic("SetBkColor failed")
|
||||
}
|
||||
|
||||
return COLORREF(ret)
|
||||
}
|
||||
|
||||
func StartDoc(hdc HDC, lpdi *DOCINFO) int {
|
||||
ret, _, _ := procStartDoc.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(unsafe.Pointer(lpdi)))
|
||||
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func StartPage(hdc HDC) int {
|
||||
ret, _, _ := procStartPage.Call(
|
||||
uintptr(hdc))
|
||||
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func StretchBlt(hdcDest HDC, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest int, hdcSrc HDC, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc int, dwRop uint) {
|
||||
ret, _, _ := procStretchBlt.Call(
|
||||
uintptr(hdcDest),
|
||||
uintptr(nXOriginDest),
|
||||
uintptr(nYOriginDest),
|
||||
uintptr(nWidthDest),
|
||||
uintptr(nHeightDest),
|
||||
uintptr(hdcSrc),
|
||||
uintptr(nXOriginSrc),
|
||||
uintptr(nYOriginSrc),
|
||||
uintptr(nWidthSrc),
|
||||
uintptr(nHeightSrc),
|
||||
uintptr(dwRop))
|
||||
|
||||
if ret == 0 {
|
||||
panic("StretchBlt failed")
|
||||
}
|
||||
}
|
||||
|
||||
func SetDIBitsToDevice(hdc HDC, xDest, yDest, dwWidth, dwHeight, xSrc, ySrc int, uStartScan, cScanLines uint, lpvBits []byte, lpbmi *BITMAPINFO, fuColorUse uint) int {
|
||||
ret, _, _ := procSetDIBitsToDevice.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(xDest),
|
||||
uintptr(yDest),
|
||||
uintptr(dwWidth),
|
||||
uintptr(dwHeight),
|
||||
uintptr(xSrc),
|
||||
uintptr(ySrc),
|
||||
uintptr(uStartScan),
|
||||
uintptr(cScanLines),
|
||||
uintptr(unsafe.Pointer(&lpvBits[0])),
|
||||
uintptr(unsafe.Pointer(lpbmi)),
|
||||
uintptr(fuColorUse))
|
||||
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func ChoosePixelFormat(hdc HDC, pfd *PIXELFORMATDESCRIPTOR) int {
|
||||
ret, _, _ := procChoosePixelFormat.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(unsafe.Pointer(pfd)),
|
||||
)
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func DescribePixelFormat(hdc HDC, iPixelFormat int, nBytes uint, pfd *PIXELFORMATDESCRIPTOR) int {
|
||||
ret, _, _ := procDescribePixelFormat.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(iPixelFormat),
|
||||
uintptr(nBytes),
|
||||
uintptr(unsafe.Pointer(pfd)),
|
||||
)
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func GetEnhMetaFilePixelFormat(hemf HENHMETAFILE, cbBuffer uint32, pfd *PIXELFORMATDESCRIPTOR) uint {
|
||||
ret, _, _ := procGetEnhMetaFilePixelFormat.Call(
|
||||
uintptr(hemf),
|
||||
uintptr(cbBuffer),
|
||||
uintptr(unsafe.Pointer(pfd)),
|
||||
)
|
||||
return uint(ret)
|
||||
}
|
||||
|
||||
func GetPixelFormat(hdc HDC) int {
|
||||
ret, _, _ := procGetPixelFormat.Call(
|
||||
uintptr(hdc),
|
||||
)
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func SetPixelFormat(hdc HDC, iPixelFormat int, pfd *PIXELFORMATDESCRIPTOR) bool {
|
||||
ret, _, _ := procSetPixelFormat.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(iPixelFormat),
|
||||
uintptr(unsafe.Pointer(pfd)),
|
||||
)
|
||||
return ret == TRUE
|
||||
}
|
||||
|
||||
func SwapBuffers(hdc HDC) bool {
|
||||
ret, _, _ := procSwapBuffers.Call(uintptr(hdc))
|
||||
return ret == TRUE
|
||||
}
|
||||
|
||||
func SaveDC(hdc HDC) int {
|
||||
ret, _, _ := procSaveDC.Call(uintptr(hdc))
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func RestoreDC(hdc HDC, nSavedDC int) bool {
|
||||
ret, _, _ := procRestoreDC.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(nSavedDC))
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func SelectClipRgn(hdc HDC, hrgn HRGN) int {
|
||||
ret, _, _ := procSelectClipRgn.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(hrgn))
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func ExcludeClipRect(hdc HDC, left, top, right, bottom int32) int {
|
||||
ret, _, _ := procExcludeClipRect.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(left),
|
||||
uintptr(top),
|
||||
uintptr(right),
|
||||
uintptr(bottom))
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func ExtTextOut(hdc HDC, x, y int32, fuOptions uint32, lprc *RECT, lpString *uint16, cbCount uint32, lpDx *int) bool {
|
||||
var rectPtr uintptr
|
||||
if lprc != nil {
|
||||
rectPtr = uintptr(unsafe.Pointer(lprc))
|
||||
}
|
||||
var dxPtr uintptr
|
||||
if lpDx != nil {
|
||||
dxPtr = uintptr(unsafe.Pointer(lpDx))
|
||||
}
|
||||
ret, _, _ := procExtTextOut.Call(
|
||||
uintptr(hdc),
|
||||
uintptr(x),
|
||||
uintptr(y),
|
||||
uintptr(fuOptions),
|
||||
rectPtr,
|
||||
uintptr(unsafe.Pointer(lpString)),
|
||||
uintptr(cbCount),
|
||||
dxPtr)
|
||||
return ret != 0
|
||||
}
|
||||
177
v3/pkg/w32/gdiplus.go
Normal file
177
v3/pkg/w32/gdiplus.go
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
//go:build windows
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved.
|
||||
*/
|
||||
package w32
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
Ok = 0
|
||||
GenericError = 1
|
||||
InvalidParameter = 2
|
||||
OutOfMemory = 3
|
||||
ObjectBusy = 4
|
||||
InsufficientBuffer = 5
|
||||
NotImplemented = 6
|
||||
Win32Error = 7
|
||||
WrongState = 8
|
||||
Aborted = 9
|
||||
FileNotFound = 10
|
||||
ValueOverflow = 11
|
||||
AccessDenied = 12
|
||||
UnknownImageFormat = 13
|
||||
FontFamilyNotFound = 14
|
||||
FontStyleNotFound = 15
|
||||
NotTrueTypeFont = 16
|
||||
UnsupportedGdiplusVersion = 17
|
||||
GdiplusNotInitialized = 18
|
||||
PropertyNotFound = 19
|
||||
PropertyNotSupported = 20
|
||||
ProfileNotFound = 21
|
||||
)
|
||||
|
||||
func GetGpStatus(s int32) string {
|
||||
switch s {
|
||||
case Ok:
|
||||
return "Ok"
|
||||
case GenericError:
|
||||
return "GenericError"
|
||||
case InvalidParameter:
|
||||
return "InvalidParameter"
|
||||
case OutOfMemory:
|
||||
return "OutOfMemory"
|
||||
case ObjectBusy:
|
||||
return "ObjectBusy"
|
||||
case InsufficientBuffer:
|
||||
return "InsufficientBuffer"
|
||||
case NotImplemented:
|
||||
return "NotImplemented"
|
||||
case Win32Error:
|
||||
return "Win32Error"
|
||||
case WrongState:
|
||||
return "WrongState"
|
||||
case Aborted:
|
||||
return "Aborted"
|
||||
case FileNotFound:
|
||||
return "FileNotFound"
|
||||
case ValueOverflow:
|
||||
return "ValueOverflow"
|
||||
case AccessDenied:
|
||||
return "AccessDenied"
|
||||
case UnknownImageFormat:
|
||||
return "UnknownImageFormat"
|
||||
case FontFamilyNotFound:
|
||||
return "FontFamilyNotFound"
|
||||
case FontStyleNotFound:
|
||||
return "FontStyleNotFound"
|
||||
case NotTrueTypeFont:
|
||||
return "NotTrueTypeFont"
|
||||
case UnsupportedGdiplusVersion:
|
||||
return "UnsupportedGdiplusVersion"
|
||||
case GdiplusNotInitialized:
|
||||
return "GdiplusNotInitialized"
|
||||
case PropertyNotFound:
|
||||
return "PropertyNotFound"
|
||||
case PropertyNotSupported:
|
||||
return "PropertyNotSupported"
|
||||
case ProfileNotFound:
|
||||
return "ProfileNotFound"
|
||||
}
|
||||
return "Unknown Status Value"
|
||||
}
|
||||
|
||||
var (
|
||||
token uintptr
|
||||
|
||||
modgdiplus = syscall.NewLazyDLL("gdiplus.dll")
|
||||
|
||||
procGdipCreateBitmapFromFile = modgdiplus.NewProc("GdipCreateBitmapFromFile")
|
||||
procGdipCreateBitmapFromHBITMAP = modgdiplus.NewProc("GdipCreateBitmapFromHBITMAP")
|
||||
procGdipCreateHBITMAPFromBitmap = modgdiplus.NewProc("GdipCreateHBITMAPFromBitmap")
|
||||
procGdipCreateBitmapFromResource = modgdiplus.NewProc("GdipCreateBitmapFromResource")
|
||||
procGdipCreateBitmapFromStream = modgdiplus.NewProc("GdipCreateBitmapFromStream")
|
||||
procGdipDisposeImage = modgdiplus.NewProc("GdipDisposeImage")
|
||||
procGdiplusShutdown = modgdiplus.NewProc("GdiplusShutdown")
|
||||
procGdiplusStartup = modgdiplus.NewProc("GdiplusStartup")
|
||||
)
|
||||
|
||||
func GdipCreateBitmapFromFile(filename string) (*uintptr, error) {
|
||||
var bitmap *uintptr
|
||||
ret, _, _ := procGdipCreateBitmapFromFile.Call(
|
||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(filename))),
|
||||
uintptr(unsafe.Pointer(&bitmap)))
|
||||
|
||||
if ret != Ok {
|
||||
return nil, errors.New(fmt.Sprintf("GdipCreateBitmapFromFile failed with status '%s' for file '%s'", GetGpStatus(int32(ret)), filename))
|
||||
}
|
||||
|
||||
return bitmap, nil
|
||||
}
|
||||
|
||||
func GdipCreateBitmapFromResource(instance HINSTANCE, resId *uint16) (*uintptr, error) {
|
||||
var bitmap *uintptr
|
||||
ret, _, _ := procGdipCreateBitmapFromResource.Call(
|
||||
uintptr(instance),
|
||||
uintptr(unsafe.Pointer(resId)),
|
||||
uintptr(unsafe.Pointer(&bitmap)))
|
||||
|
||||
if ret != Ok {
|
||||
return nil, errors.New(fmt.Sprintf("GdiCreateBitmapFromResource failed with status '%s'", GetGpStatus(int32(ret))))
|
||||
}
|
||||
|
||||
return bitmap, nil
|
||||
}
|
||||
|
||||
func GdipCreateBitmapFromStream(stream *IStream) (*uintptr, error) {
|
||||
var bitmap *uintptr
|
||||
ret, _, _ := procGdipCreateBitmapFromStream.Call(
|
||||
uintptr(unsafe.Pointer(stream)),
|
||||
uintptr(unsafe.Pointer(&bitmap)))
|
||||
|
||||
if ret != Ok {
|
||||
return nil, errors.New(fmt.Sprintf("GdipCreateBitmapFromStream failed with status '%s'", GetGpStatus(int32(ret))))
|
||||
}
|
||||
|
||||
return bitmap, nil
|
||||
}
|
||||
|
||||
func GdipCreateHBITMAPFromBitmap(bitmap *uintptr, background uint32) (HBITMAP, error) {
|
||||
var hbitmap HBITMAP
|
||||
ret, _, _ := procGdipCreateHBITMAPFromBitmap.Call(
|
||||
uintptr(unsafe.Pointer(bitmap)),
|
||||
uintptr(unsafe.Pointer(&hbitmap)),
|
||||
uintptr(background))
|
||||
|
||||
if ret != Ok {
|
||||
return 0, errors.New(fmt.Sprintf("GdipCreateHBITMAPFromBitmap failed with status '%s'", GetGpStatus(int32(ret))))
|
||||
}
|
||||
|
||||
return hbitmap, nil
|
||||
}
|
||||
|
||||
func GdipDisposeImage(image *uintptr) {
|
||||
procGdipDisposeImage.Call(uintptr(unsafe.Pointer(image)))
|
||||
}
|
||||
|
||||
func GdiplusShutdown() {
|
||||
procGdiplusShutdown.Call(token)
|
||||
}
|
||||
|
||||
func GdiplusStartup(input *GdiplusStartupInput, output *GdiplusStartupOutput) {
|
||||
ret, _, _ := procGdiplusStartup.Call(
|
||||
uintptr(unsafe.Pointer(&token)),
|
||||
uintptr(unsafe.Pointer(input)),
|
||||
uintptr(unsafe.Pointer(output)))
|
||||
|
||||
if ret != Ok {
|
||||
panic("GdiplusStartup failed with status " + GetGpStatus(int32(ret)))
|
||||
}
|
||||
}
|
||||
225
v3/pkg/w32/guid.go
Normal file
225
v3/pkg/w32/guid.go
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
//go:build windows
|
||||
|
||||
package w32
|
||||
|
||||
// This code has been adapted from: https://github.com/go-ole/go-ole
|
||||
|
||||
/*
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright © 2013-2017 Yasuhiro Matsumoto, <mattn.jp@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the “Software”), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
const hextable = "0123456789ABCDEF"
|
||||
const emptyGUID = "{00000000-0000-0000-0000-000000000000}"
|
||||
|
||||
// GUID is Windows API specific GUID type.
|
||||
//
|
||||
// This exists to match Windows GUID type for direct passing for COM.
|
||||
// Format is in xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx.
|
||||
type GUID struct {
|
||||
Data1 uint32
|
||||
Data2 uint16
|
||||
Data3 uint16
|
||||
Data4 [8]byte
|
||||
}
|
||||
|
||||
// NewGUID converts the given string into a globally unique identifier that is
|
||||
// compliant with the Windows API.
|
||||
//
|
||||
// The supplied string may be in any of these formats:
|
||||
//
|
||||
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
|
||||
// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
|
||||
//
|
||||
// The conversion of the supplied string is not case-sensitive.
|
||||
func NewGUID(guid string) *GUID {
|
||||
d := []byte(guid)
|
||||
var d1, d2, d3, d4a, d4b []byte
|
||||
|
||||
switch len(d) {
|
||||
case 38:
|
||||
if d[0] != '{' || d[37] != '}' {
|
||||
return nil
|
||||
}
|
||||
d = d[1:37]
|
||||
fallthrough
|
||||
case 36:
|
||||
if d[8] != '-' || d[13] != '-' || d[18] != '-' || d[23] != '-' {
|
||||
return nil
|
||||
}
|
||||
d1 = d[0:8]
|
||||
d2 = d[9:13]
|
||||
d3 = d[14:18]
|
||||
d4a = d[19:23]
|
||||
d4b = d[24:36]
|
||||
case 32:
|
||||
d1 = d[0:8]
|
||||
d2 = d[8:12]
|
||||
d3 = d[12:16]
|
||||
d4a = d[16:20]
|
||||
d4b = d[20:32]
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
var g GUID
|
||||
var ok1, ok2, ok3, ok4 bool
|
||||
g.Data1, ok1 = decodeHexUint32(d1)
|
||||
g.Data2, ok2 = decodeHexUint16(d2)
|
||||
g.Data3, ok3 = decodeHexUint16(d3)
|
||||
g.Data4, ok4 = decodeHexByte64(d4a, d4b)
|
||||
if ok1 && ok2 && ok3 && ok4 {
|
||||
return &g
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeHexUint32(src []byte) (value uint32, ok bool) {
|
||||
var b1, b2, b3, b4 byte
|
||||
var ok1, ok2, ok3, ok4 bool
|
||||
b1, ok1 = decodeHexByte(src[0], src[1])
|
||||
b2, ok2 = decodeHexByte(src[2], src[3])
|
||||
b3, ok3 = decodeHexByte(src[4], src[5])
|
||||
b4, ok4 = decodeHexByte(src[6], src[7])
|
||||
value = (uint32(b1) << 24) | (uint32(b2) << 16) | (uint32(b3) << 8) | uint32(b4)
|
||||
ok = ok1 && ok2 && ok3 && ok4
|
||||
return
|
||||
}
|
||||
|
||||
func decodeHexUint16(src []byte) (value uint16, ok bool) {
|
||||
var b1, b2 byte
|
||||
var ok1, ok2 bool
|
||||
b1, ok1 = decodeHexByte(src[0], src[1])
|
||||
b2, ok2 = decodeHexByte(src[2], src[3])
|
||||
value = (uint16(b1) << 8) | uint16(b2)
|
||||
ok = ok1 && ok2
|
||||
return
|
||||
}
|
||||
|
||||
func decodeHexByte64(s1 []byte, s2 []byte) (value [8]byte, ok bool) {
|
||||
var ok1, ok2, ok3, ok4, ok5, ok6, ok7, ok8 bool
|
||||
value[0], ok1 = decodeHexByte(s1[0], s1[1])
|
||||
value[1], ok2 = decodeHexByte(s1[2], s1[3])
|
||||
value[2], ok3 = decodeHexByte(s2[0], s2[1])
|
||||
value[3], ok4 = decodeHexByte(s2[2], s2[3])
|
||||
value[4], ok5 = decodeHexByte(s2[4], s2[5])
|
||||
value[5], ok6 = decodeHexByte(s2[6], s2[7])
|
||||
value[6], ok7 = decodeHexByte(s2[8], s2[9])
|
||||
value[7], ok8 = decodeHexByte(s2[10], s2[11])
|
||||
ok = ok1 && ok2 && ok3 && ok4 && ok5 && ok6 && ok7 && ok8
|
||||
return
|
||||
}
|
||||
|
||||
func decodeHexByte(c1, c2 byte) (value byte, ok bool) {
|
||||
var n1, n2 byte
|
||||
var ok1, ok2 bool
|
||||
n1, ok1 = decodeHexChar(c1)
|
||||
n2, ok2 = decodeHexChar(c2)
|
||||
value = (n1 << 4) | n2
|
||||
ok = ok1 && ok2
|
||||
return
|
||||
}
|
||||
|
||||
func decodeHexChar(c byte) (byte, bool) {
|
||||
switch {
|
||||
case '0' <= c && c <= '9':
|
||||
return c - '0', true
|
||||
case 'a' <= c && c <= 'f':
|
||||
return c - 'a' + 10, true
|
||||
case 'A' <= c && c <= 'F':
|
||||
return c - 'A' + 10, true
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// String converts the GUID to string form. It will adhere to this pattern:
|
||||
//
|
||||
// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
|
||||
//
|
||||
// If the GUID is nil, the string representation of an empty GUID is returned:
|
||||
//
|
||||
// {00000000-0000-0000-0000-000000000000}
|
||||
func (guid *GUID) String() string {
|
||||
if guid == nil {
|
||||
return emptyGUID
|
||||
}
|
||||
|
||||
var c [38]byte
|
||||
c[0] = '{'
|
||||
putUint32Hex(c[1:9], guid.Data1)
|
||||
c[9] = '-'
|
||||
putUint16Hex(c[10:14], guid.Data2)
|
||||
c[14] = '-'
|
||||
putUint16Hex(c[15:19], guid.Data3)
|
||||
c[19] = '-'
|
||||
putByteHex(c[20:24], guid.Data4[0:2])
|
||||
c[24] = '-'
|
||||
putByteHex(c[25:37], guid.Data4[2:8])
|
||||
c[37] = '}'
|
||||
return string(c[:])
|
||||
}
|
||||
|
||||
func putUint32Hex(b []byte, v uint32) {
|
||||
b[0] = hextable[byte(v>>24)>>4]
|
||||
b[1] = hextable[byte(v>>24)&0x0f]
|
||||
b[2] = hextable[byte(v>>16)>>4]
|
||||
b[3] = hextable[byte(v>>16)&0x0f]
|
||||
b[4] = hextable[byte(v>>8)>>4]
|
||||
b[5] = hextable[byte(v>>8)&0x0f]
|
||||
b[6] = hextable[byte(v)>>4]
|
||||
b[7] = hextable[byte(v)&0x0f]
|
||||
}
|
||||
|
||||
func putUint16Hex(b []byte, v uint16) {
|
||||
b[0] = hextable[byte(v>>8)>>4]
|
||||
b[1] = hextable[byte(v>>8)&0x0f]
|
||||
b[2] = hextable[byte(v)>>4]
|
||||
b[3] = hextable[byte(v)&0x0f]
|
||||
}
|
||||
|
||||
func putByteHex(dst, src []byte) {
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i*2] = hextable[src[i]>>4]
|
||||
dst[i*2+1] = hextable[src[i]&0x0f]
|
||||
}
|
||||
}
|
||||
|
||||
// IsEqualGUID compares two GUID.
|
||||
//
|
||||
// Not constant time comparison.
|
||||
func IsEqualGUID(guid1 *GUID, guid2 *GUID) bool {
|
||||
return guid1.Data1 == guid2.Data1 &&
|
||||
guid1.Data2 == guid2.Data2 &&
|
||||
guid1.Data3 == guid2.Data3 &&
|
||||
guid1.Data4[0] == guid2.Data4[0] &&
|
||||
guid1.Data4[1] == guid2.Data4[1] &&
|
||||
guid1.Data4[2] == guid2.Data4[2] &&
|
||||
guid1.Data4[3] == guid2.Data4[3] &&
|
||||
guid1.Data4[4] == guid2.Data4[4] &&
|
||||
guid1.Data4[5] == guid2.Data4[5] &&
|
||||
guid1.Data4[6] == guid2.Data4[6] &&
|
||||
guid1.Data4[7] == guid2.Data4[7]
|
||||
}
|
||||
255
v3/pkg/w32/icon.go
Normal file
255
v3/pkg/w32/icon.go
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
//go:build windows
|
||||
|
||||
package w32
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"image/png"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func CreateIconFromResourceEx(presbits uintptr, dwResSize uint32, isIcon bool, version uint32, cxDesired int, cyDesired int, flags uint) (uintptr, error) {
|
||||
icon := 0
|
||||
if isIcon {
|
||||
icon = 1
|
||||
}
|
||||
r, _, err := procCreateIconFromResourceEx.Call(
|
||||
presbits,
|
||||
uintptr(dwResSize),
|
||||
uintptr(icon),
|
||||
uintptr(version),
|
||||
uintptr(cxDesired),
|
||||
uintptr(cyDesired),
|
||||
uintptr(flags),
|
||||
)
|
||||
|
||||
if r == 0 {
|
||||
return 0, err
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func isPNG(fileData []byte) bool {
|
||||
if len(fileData) < 4 {
|
||||
return false
|
||||
}
|
||||
return string(fileData[:4]) == "\x89PNG"
|
||||
}
|
||||
|
||||
func isICO(fileData []byte) bool {
|
||||
if len(fileData) < 4 {
|
||||
return false
|
||||
}
|
||||
return string(fileData[:4]) == "\x00\x00\x01\x00"
|
||||
}
|
||||
|
||||
// CreateSmallHIconFromImage creates a HICON from a PNG or ICO file
|
||||
func CreateSmallHIconFromImage(fileData []byte) (HICON, error) {
|
||||
if len(fileData) < 8 {
|
||||
return 0, fmt.Errorf("invalid file format")
|
||||
}
|
||||
|
||||
if !isPNG(fileData) && !isICO(fileData) {
|
||||
return 0, fmt.Errorf("unsupported file format")
|
||||
}
|
||||
iconWidth := GetSystemMetrics(SM_CXSMICON)
|
||||
iconHeight := GetSystemMetrics(SM_CYSMICON)
|
||||
icon, err := CreateIconFromResourceEx(
|
||||
uintptr(unsafe.Pointer(&fileData[0])),
|
||||
uint32(len(fileData)),
|
||||
true,
|
||||
0x00030000,
|
||||
iconWidth,
|
||||
iconHeight,
|
||||
LR_DEFAULTSIZE)
|
||||
return HICON(icon), err
|
||||
}
|
||||
|
||||
// CreateLargeHIconFromImage creates a HICON from a PNG or ICO file
|
||||
func CreateLargeHIconFromImage(fileData []byte) (HICON, error) {
|
||||
if len(fileData) < 8 {
|
||||
return 0, fmt.Errorf("invalid file format")
|
||||
}
|
||||
|
||||
if !isPNG(fileData) && !isICO(fileData) {
|
||||
return 0, fmt.Errorf("unsupported file format")
|
||||
}
|
||||
iconWidth := GetSystemMetrics(SM_CXICON)
|
||||
iconHeight := GetSystemMetrics(SM_CXICON)
|
||||
icon, err := CreateIconFromResourceEx(
|
||||
uintptr(unsafe.Pointer(&fileData[0])),
|
||||
uint32(len(fileData)),
|
||||
true,
|
||||
0x00030000,
|
||||
iconWidth,
|
||||
iconHeight,
|
||||
LR_DEFAULTSIZE)
|
||||
return HICON(icon), err
|
||||
}
|
||||
|
||||
type ICONINFO struct {
|
||||
FIcon int32
|
||||
XHotspot int32
|
||||
YHotspot int32
|
||||
HbmMask syscall.Handle
|
||||
HbmColor syscall.Handle
|
||||
}
|
||||
|
||||
func SaveHIconAsPNG(hIcon HICON, filePath string) error {
|
||||
// Load necessary DLLs
|
||||
user32 := syscall.NewLazyDLL("user32.dll")
|
||||
gdi32 := syscall.NewLazyDLL("gdi32.dll")
|
||||
|
||||
// Get procedures
|
||||
getIconInfo := user32.NewProc("GetIconInfo")
|
||||
getObject := gdi32.NewProc("GetObjectW")
|
||||
createCompatibleDC := gdi32.NewProc("CreateCompatibleDC")
|
||||
selectObject := gdi32.NewProc("SelectObject")
|
||||
getDIBits := gdi32.NewProc("GetDIBits")
|
||||
deleteObject := gdi32.NewProc("DeleteObject")
|
||||
deleteDC := gdi32.NewProc("DeleteDC")
|
||||
|
||||
// Get icon info
|
||||
var iconInfo ICONINFO
|
||||
ret, _, err := getIconInfo.Call(
|
||||
uintptr(hIcon),
|
||||
uintptr(unsafe.Pointer(&iconInfo)),
|
||||
)
|
||||
if ret == 0 {
|
||||
return err
|
||||
}
|
||||
defer deleteObject.Call(uintptr(iconInfo.HbmMask))
|
||||
defer deleteObject.Call(uintptr(iconInfo.HbmColor))
|
||||
|
||||
// Get bitmap info
|
||||
var bmp BITMAP
|
||||
ret, _, err = getObject.Call(
|
||||
uintptr(iconInfo.HbmColor),
|
||||
unsafe.Sizeof(bmp),
|
||||
uintptr(unsafe.Pointer(&bmp)),
|
||||
)
|
||||
if ret == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create DC
|
||||
hdc, _, _ := createCompatibleDC.Call(0)
|
||||
if hdc == 0 {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
defer deleteDC.Call(hdc)
|
||||
|
||||
// Select bitmap into DC
|
||||
oldBitmap, _, _ := selectObject.Call(hdc, uintptr(iconInfo.HbmColor))
|
||||
defer selectObject.Call(hdc, oldBitmap)
|
||||
|
||||
// Prepare bitmap info header
|
||||
var bi BITMAPINFO
|
||||
bi.BmiHeader.BiSize = uint32(unsafe.Sizeof(bi.BmiHeader))
|
||||
bi.BmiHeader.BiWidth = bmp.BmWidth
|
||||
bi.BmiHeader.BiHeight = bmp.BmHeight
|
||||
bi.BmiHeader.BiPlanes = 1
|
||||
bi.BmiHeader.BiBitCount = 32
|
||||
bi.BmiHeader.BiCompression = BI_RGB
|
||||
|
||||
// Allocate memory for bitmap bits
|
||||
width, height := int(bmp.BmWidth), int(bmp.BmHeight)
|
||||
bufferSize := width * height * 4
|
||||
bits := make([]byte, bufferSize)
|
||||
|
||||
// Get bitmap bits
|
||||
ret, _, err = getDIBits.Call(
|
||||
hdc,
|
||||
uintptr(iconInfo.HbmColor),
|
||||
0,
|
||||
uintptr(bmp.BmHeight),
|
||||
uintptr(unsafe.Pointer(&bits[0])),
|
||||
uintptr(unsafe.Pointer(&bi)),
|
||||
DIB_RGB_COLORS,
|
||||
)
|
||||
if ret == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create Go image
|
||||
img := image.NewRGBA(image.Rect(0, 0, width, height))
|
||||
|
||||
// Convert DIB to RGBA
|
||||
for y := 0; y < height; y++ {
|
||||
for x := 0; x < width; x++ {
|
||||
// DIB is bottom-up, so we need to invert Y
|
||||
dibIndex := ((height-1-y)*width + x) * 4
|
||||
|
||||
// BGRA to RGBA
|
||||
b := bits[dibIndex]
|
||||
g := bits[dibIndex+1]
|
||||
r := bits[dibIndex+2]
|
||||
a := bits[dibIndex+3]
|
||||
|
||||
// Set pixel in the image
|
||||
img.Set(x, y, color.RGBA{R: r, G: g, B: b, A: a})
|
||||
}
|
||||
}
|
||||
|
||||
// Create output file
|
||||
outFile, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer outFile.Close()
|
||||
|
||||
// Encode and save the image
|
||||
return png.Encode(outFile, img)
|
||||
}
|
||||
|
||||
func SetWindowIcon(hwnd HWND, icon HICON) {
|
||||
SendMessage(hwnd, WM_SETICON, ICON_SMALL, uintptr(icon))
|
||||
}
|
||||
|
||||
func pngToImage(data []byte) (*image.RGBA, error) {
|
||||
img, err := png.Decode(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bounds := img.Bounds()
|
||||
rgba := image.NewRGBA(bounds)
|
||||
draw.Draw(rgba, bounds, img, bounds.Min, draw.Src)
|
||||
return rgba, nil
|
||||
}
|
||||
|
||||
func SetMenuIcons(parentMenu HMENU, itemID int, unchecked []byte, checked []byte) error {
|
||||
if unchecked == nil {
|
||||
return fmt.Errorf("invalid unchecked bitmap")
|
||||
}
|
||||
var err error
|
||||
var uncheckedIcon, checkedIcon HBITMAP
|
||||
var uncheckedImage, checkedImage *image.RGBA
|
||||
uncheckedImage, err = pngToImage(unchecked)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uncheckedIcon, err = CreateHBITMAPFromImage(uncheckedImage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if checked != nil {
|
||||
checkedImage, err = pngToImage(checked)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
checkedIcon, err = CreateHBITMAPFromImage(checkedImage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
checkedIcon = uncheckedIcon
|
||||
}
|
||||
return SetMenuItemBitmaps(parentMenu, uint32(itemID), MF_BYCOMMAND, checkedIcon, uncheckedIcon)
|
||||
}
|
||||
168
v3/pkg/w32/idataobject.go
Normal file
168
v3/pkg/w32/idataobject.go
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
//go:build windows
|
||||
|
||||
package w32
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type IDataObjectVtbl struct {
|
||||
IUnknownVtbl
|
||||
GetData ComProc
|
||||
GetDataHere ComProc
|
||||
QueryGetData ComProc
|
||||
GetCanonicalFormatEtc ComProc
|
||||
SetData ComProc
|
||||
EnumFormatEtc ComProc
|
||||
DAdvise ComProc
|
||||
}
|
||||
|
||||
type IDataObject struct {
|
||||
Vtbl *IDataObjectVtbl
|
||||
}
|
||||
|
||||
func (i *IDataObject) AddRef() uintptr {
|
||||
refCounter, _, _ := i.Vtbl.AddRef.Call(uintptr(unsafe.Pointer(i)))
|
||||
return refCounter
|
||||
}
|
||||
|
||||
func (i *IDataObject) GetData(formatEtc *FORMATETC, medium *STGMEDIUM) error {
|
||||
hr, _, err := i.Vtbl.GetData.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(formatEtc)),
|
||||
uintptr(unsafe.Pointer(medium)),
|
||||
)
|
||||
if windows.Handle(hr) != windows.S_OK {
|
||||
return syscall.Errno(hr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (i *IDataObject) GetDataHere(formatEtc *FORMATETC, medium *STGMEDIUM) error {
|
||||
hr, _, err := i.Vtbl.GetDataHere.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(formatEtc)),
|
||||
uintptr(unsafe.Pointer(medium)),
|
||||
)
|
||||
if windows.Handle(hr) != windows.S_OK {
|
||||
return syscall.Errno(hr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (i *IDataObject) QueryGetData(formatEtc *FORMATETC) error {
|
||||
hr, _, err := i.Vtbl.QueryGetData.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(formatEtc)),
|
||||
)
|
||||
if windows.Handle(hr) != windows.S_OK {
|
||||
return syscall.Errno(hr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (i *IDataObject) GetCanonicalFormatEtc(inputFormatEtc *FORMATETC, outputFormatEtc *FORMATETC) error {
|
||||
hr, _, err := i.Vtbl.GetCanonicalFormatEtc.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(inputFormatEtc)),
|
||||
uintptr(unsafe.Pointer(outputFormatEtc)),
|
||||
)
|
||||
if windows.Handle(hr) != windows.S_OK {
|
||||
return syscall.Errno(hr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (i *IDataObject) SetData(formatEtc *FORMATETC, medium *STGMEDIUM, release bool) error {
|
||||
hr, _, err := i.Vtbl.SetData.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(formatEtc)),
|
||||
uintptr(unsafe.Pointer(medium)),
|
||||
uintptr(BoolToBOOL(release)),
|
||||
)
|
||||
if windows.Handle(hr) != windows.S_OK {
|
||||
return syscall.Errno(hr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (i *IDataObject) EnumFormatEtc(dwDirection uint32, enumFormatEtc **IEnumFORMATETC) error {
|
||||
hr, _, err := i.Vtbl.EnumFormatEtc.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(dwDirection),
|
||||
uintptr(unsafe.Pointer(enumFormatEtc)),
|
||||
)
|
||||
if windows.Handle(hr) != windows.S_OK {
|
||||
return syscall.Errno(hr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (i *IDataObject) DAdvise(formatEtc *FORMATETC, advf uint32, adviseSink *IAdviseSink, pdwConnection *uint32) error {
|
||||
hr, _, err := i.Vtbl.DAdvise.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(formatEtc)),
|
||||
uintptr(advf),
|
||||
uintptr(unsafe.Pointer(adviseSink)),
|
||||
uintptr(unsafe.Pointer(pdwConnection)),
|
||||
)
|
||||
if windows.Handle(hr) != windows.S_OK {
|
||||
return syscall.Errno(hr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type DVTargetDevice struct {
|
||||
TdSize uint32
|
||||
TdDriverNameOffset uint16
|
||||
TdDeviceNameOffset uint16
|
||||
TdPortNameOffset uint16
|
||||
TdExtDevmodeOffset uint16
|
||||
TdData [1]byte
|
||||
}
|
||||
|
||||
type FORMATETC struct {
|
||||
CfFormat uint16
|
||||
Ptd *DVTargetDevice
|
||||
DwAspect uint32
|
||||
Lindex int32
|
||||
Tymed Tymed
|
||||
}
|
||||
|
||||
type Tymed uint32
|
||||
|
||||
const (
|
||||
TYMED_HGLOBAL Tymed = 1
|
||||
TYMED_FILE Tymed = 2
|
||||
TYMED_ISTREAM Tymed = 4
|
||||
TYMED_ISTORAGE Tymed = 8
|
||||
TYMED_GDI Tymed = 16
|
||||
TYMED_MFPICT Tymed = 32
|
||||
TYMED_ENHMF Tymed = 64
|
||||
TYMED_NULL Tymed = 0
|
||||
)
|
||||
|
||||
type STGMEDIUM struct {
|
||||
Tymed Tymed
|
||||
Union uintptr
|
||||
PUnkForRelease IUnknownImpl
|
||||
}
|
||||
|
||||
func (s STGMEDIUM) FileName() string {
|
||||
if s.Tymed != TYMED_FILE {
|
||||
return ""
|
||||
}
|
||||
return windows.UTF16PtrToString((*uint16)(unsafe.Pointer(s.Union)))
|
||||
}
|
||||
|
||||
func (s STGMEDIUM) Release() {
|
||||
if s.PUnkForRelease != nil {
|
||||
s.PUnkForRelease.Release()
|
||||
}
|
||||
}
|
||||
|
||||
type IEnumFORMATETC struct{}
|
||||
type IAdviseSink struct{}
|
||||
type IEnumStatData struct{}
|
||||
45
v3/pkg/w32/idispatch.go
Normal file
45
v3/pkg/w32/idispatch.go
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
//go:build windows
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved.
|
||||
*/
|
||||
package w32
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type pIDispatchVtbl struct {
|
||||
pQueryInterface uintptr
|
||||
pAddRef uintptr
|
||||
pRelease uintptr
|
||||
pGetTypeInfoCount uintptr
|
||||
pGetTypeInfo uintptr
|
||||
pGetIDsOfNames uintptr
|
||||
pInvoke uintptr
|
||||
}
|
||||
|
||||
type IDispatch struct {
|
||||
lpVtbl *pIDispatchVtbl
|
||||
}
|
||||
|
||||
func (this *IDispatch) QueryInterface(id *GUID) *IDispatch {
|
||||
return ComQueryInterface((*IUnknown)(unsafe.Pointer(this)), id)
|
||||
}
|
||||
|
||||
func (this *IDispatch) AddRef() int32 {
|
||||
return ComAddRef((*IUnknown)(unsafe.Pointer(this)))
|
||||
}
|
||||
|
||||
func (this *IDispatch) Release() int32 {
|
||||
return ComRelease((*IUnknown)(unsafe.Pointer(this)))
|
||||
}
|
||||
|
||||
func (this *IDispatch) GetIDsOfName(names []string) []int32 {
|
||||
return ComGetIDsOfName(this, names)
|
||||
}
|
||||
|
||||
func (this *IDispatch) Invoke(dispid int32, dispatch int16, params ...interface{}) *VARIANT {
|
||||
return ComInvoke(this, dispid, dispatch, params...)
|
||||
}
|
||||
140
v3/pkg/w32/idroptarget.go
Normal file
140
v3/pkg/w32/idroptarget.go
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
//go:build windows
|
||||
|
||||
package w32
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/go-webview2/pkg/combridge"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
DROPEFFECT_NONE DWORD = 0
|
||||
DROPEFFECT_COPY DWORD = 1
|
||||
DROPEFFECT_MOVE DWORD = 2
|
||||
DROPEFFECT_LINK DWORD = 4
|
||||
)
|
||||
|
||||
const (
|
||||
DRAGDROP_E_ALREADYREGISTERED = 0x80040101
|
||||
DRAGDROP_E_INVALIDHWND = 0x80040102
|
||||
)
|
||||
|
||||
func _NOP(_ uintptr) uintptr {
|
||||
return uintptr(windows.S_FALSE)
|
||||
}
|
||||
|
||||
func init() {
|
||||
combridge.RegisterVTable[combridge.IUnknown, iDropTarget](
|
||||
"{00000122-0000-0000-C000-000000000046}",
|
||||
_iDropTargetDragEnter,
|
||||
_iDropTargetDragOver,
|
||||
_iDropTargetDragLeave,
|
||||
_iDropTargetDrop,
|
||||
)
|
||||
}
|
||||
|
||||
func _iDropTargetDragEnter(
|
||||
this uintptr,
|
||||
dataObject *IDataObject,
|
||||
grfKeyState DWORD,
|
||||
point POINT,
|
||||
pdfEffect *DWORD,
|
||||
) uintptr {
|
||||
return combridge.Resolve[iDropTarget](this).DragEnter(dataObject, grfKeyState, point, pdfEffect)
|
||||
}
|
||||
|
||||
func _iDropTargetDragOver(this uintptr, grfKeyState DWORD, point POINT, pdfEffect *DWORD) uintptr {
|
||||
return combridge.Resolve[iDropTarget](this).DragOver(grfKeyState, point, pdfEffect)
|
||||
}
|
||||
|
||||
func _iDropTargetDragLeave(this uintptr) uintptr {
|
||||
return combridge.Resolve[iDropTarget](this).DragLeave()
|
||||
}
|
||||
|
||||
func _iDropTargetDrop(this uintptr, dataObject *IDataObject, grfKeyState DWORD, point POINT, pdfEffect *DWORD) uintptr {
|
||||
return combridge.Resolve[iDropTarget](this).Drop(dataObject, grfKeyState, point, pdfEffect)
|
||||
}
|
||||
|
||||
type iDropTarget interface {
|
||||
combridge.IUnknown
|
||||
|
||||
DragEnter(dataObject *IDataObject, grfKeyState DWORD, point POINT, pdfEffect *DWORD) uintptr
|
||||
DragOver(grfKeyState DWORD, point POINT, pdfEffect *DWORD) uintptr
|
||||
DragLeave() uintptr
|
||||
Drop(dataObject *IDataObject, grfKeyState DWORD, point POINT, pdfEffect *DWORD) uintptr
|
||||
}
|
||||
|
||||
var _ iDropTarget = &DropTarget{}
|
||||
|
||||
type DropTarget struct {
|
||||
combridge.IUnknownImpl
|
||||
OnEnterEffect DWORD
|
||||
OnOverEffect DWORD
|
||||
OnEnter func()
|
||||
OnLeave func()
|
||||
OnOver func()
|
||||
OnDrop func(filenames []string, x int, y int)
|
||||
}
|
||||
|
||||
func NewDropTarget() *DropTarget {
|
||||
result := &DropTarget{
|
||||
OnEnterEffect: DROPEFFECT_COPY,
|
||||
OnOverEffect: DROPEFFECT_COPY,
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (d *DropTarget) DragEnter(dataObject *IDataObject, grfKeyState DWORD, point POINT, pdfEffect *DWORD) uintptr {
|
||||
*pdfEffect = d.OnEnterEffect
|
||||
if d.OnEnter != nil {
|
||||
d.OnEnter()
|
||||
}
|
||||
return uintptr(windows.S_OK)
|
||||
}
|
||||
|
||||
func (d *DropTarget) DragOver(grfKeyState DWORD, point POINT, pdfEffect *DWORD) uintptr {
|
||||
*pdfEffect = d.OnOverEffect
|
||||
if d.OnOver != nil {
|
||||
d.OnOver()
|
||||
}
|
||||
return uintptr(windows.S_OK)
|
||||
}
|
||||
|
||||
func (d *DropTarget) DragLeave() uintptr {
|
||||
if d.OnLeave != nil {
|
||||
d.OnLeave()
|
||||
}
|
||||
return uintptr(windows.S_OK)
|
||||
}
|
||||
|
||||
func (d *DropTarget) Drop(dataObject *IDataObject, grfKeyState DWORD, point POINT, pdfEffect *DWORD) uintptr {
|
||||
|
||||
if d.OnDrop == nil {
|
||||
return uintptr(windows.S_OK)
|
||||
}
|
||||
|
||||
// Extract filenames from dataObject
|
||||
var filenames []string
|
||||
var formatETC = FORMATETC{
|
||||
CfFormat: CF_HDROP,
|
||||
Tymed: TYMED_HGLOBAL,
|
||||
}
|
||||
|
||||
var stgMedium STGMEDIUM
|
||||
|
||||
err := dataObject.GetData(&formatETC, &stgMedium)
|
||||
if err != nil && err != windows.ERROR_SUCCESS {
|
||||
return uintptr(windows.S_FALSE)
|
||||
}
|
||||
defer stgMedium.Release()
|
||||
hDrop := stgMedium.Union
|
||||
_, numFiles := DragQueryFile(hDrop, 0xFFFFFFFF)
|
||||
for i := uint(0); i < numFiles; i++ {
|
||||
filename, _ := DragQueryFile(hDrop, i)
|
||||
filenames = append(filenames, filename)
|
||||
}
|
||||
|
||||
d.OnDrop(filenames, int(point.X), int(point.Y))
|
||||
|
||||
return uintptr(windows.S_OK)
|
||||
}
|
||||
55
v3/pkg/w32/image.go
Normal file
55
v3/pkg/w32/image.go
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
//go:build windows
|
||||
|
||||
package w32
|
||||
|
||||
import (
|
||||
"image"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func CreateHBITMAPFromImage(img *image.RGBA) (HBITMAP, error) {
|
||||
bounds := img.Bounds()
|
||||
width, height := bounds.Dx(), bounds.Dy()
|
||||
|
||||
// Create a BITMAPINFO structure for the DIB
|
||||
bmi := BITMAPINFO{
|
||||
BmiHeader: BITMAPINFOHEADER{
|
||||
BiSize: uint32(unsafe.Sizeof(BITMAPINFOHEADER{})),
|
||||
BiWidth: int32(width),
|
||||
BiHeight: int32(-height), // negative to indicate top-down bitmap
|
||||
BiPlanes: 1,
|
||||
BiBitCount: 32,
|
||||
BiCompression: BI_RGB,
|
||||
BiSizeImage: uint32(width * height * 4), // RGBA = 4 bytes
|
||||
},
|
||||
}
|
||||
|
||||
// Create the DIB section
|
||||
var bits unsafe.Pointer
|
||||
|
||||
hbmp := CreateDIBSection(0, &bmi, DIB_RGB_COLORS, &bits, 0, 0)
|
||||
if hbmp == 0 {
|
||||
return 0, syscall.GetLastError()
|
||||
}
|
||||
|
||||
// Copy the pixel data from the Go image to the DIB section
|
||||
for y := 0; y < height; y++ {
|
||||
for x := 0; x < width; x++ {
|
||||
i := img.PixOffset(x, y)
|
||||
r := img.Pix[i+0]
|
||||
g := img.Pix[i+1]
|
||||
b := img.Pix[i+2]
|
||||
a := img.Pix[i+3]
|
||||
|
||||
// Write the RGBA pixel data to the DIB section (BGR order)
|
||||
offset := y*width*4 + x*4
|
||||
*((*uint8)(unsafe.Pointer(uintptr(bits) + uintptr(offset) + 0))) = b
|
||||
*((*uint8)(unsafe.Pointer(uintptr(bits) + uintptr(offset) + 1))) = g
|
||||
*((*uint8)(unsafe.Pointer(uintptr(bits) + uintptr(offset) + 2))) = r
|
||||
*((*uint8)(unsafe.Pointer(uintptr(bits) + uintptr(offset) + 3))) = a
|
||||
}
|
||||
}
|
||||
|
||||
return hbmp, nil
|
||||
}
|
||||
33
v3/pkg/w32/istream.go
Normal file
33
v3/pkg/w32/istream.go
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
//go:build windows
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Tad Vizbaras. All Rights Reserved.
|
||||
* Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved.
|
||||
*/
|
||||
package w32
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type pIStreamVtbl struct {
|
||||
pQueryInterface uintptr
|
||||
pAddRef uintptr
|
||||
pRelease uintptr
|
||||
}
|
||||
|
||||
type IStream struct {
|
||||
lpVtbl *pIStreamVtbl
|
||||
}
|
||||
|
||||
func (this *IStream) QueryInterface(id *GUID) *IDispatch {
|
||||
return ComQueryInterface((*IUnknown)(unsafe.Pointer(this)), id)
|
||||
}
|
||||
|
||||
func (this *IStream) AddRef() int32 {
|
||||
return ComAddRef((*IUnknown)(unsafe.Pointer(this)))
|
||||
}
|
||||
|
||||
func (this *IStream) Release() int32 {
|
||||
return ComRelease((*IUnknown)(unsafe.Pointer(this)))
|
||||
}
|
||||
337
v3/pkg/w32/kernel32.go
Normal file
337
v3/pkg/w32/kernel32.go
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
//go:build windows
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Tad Vizbaras. All Rights Reserved.
|
||||
* Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved.
|
||||
*/
|
||||
package w32
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
procGetModuleHandle = modkernel32.NewProc("GetModuleHandleW")
|
||||
procMulDiv = modkernel32.NewProc("MulDiv")
|
||||
procGetConsoleWindow = modkernel32.NewProc("GetConsoleWindow")
|
||||
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
|
||||
procGetCurrentThreadId = modkernel32.NewProc("GetCurrentThreadId")
|
||||
procGetLogicalDrives = modkernel32.NewProc("GetLogicalDrives")
|
||||
procGetLogicalDriveStrings = modkernel32.NewProc("GetLogicalDriveStringsW")
|
||||
procGetUserDefaultLCID = modkernel32.NewProc("GetUserDefaultLCID")
|
||||
procLstrlen = modkernel32.NewProc("lstrlenW")
|
||||
procLstrcpy = modkernel32.NewProc("lstrcpyW")
|
||||
procGlobalAlloc = modkernel32.NewProc("GlobalAlloc")
|
||||
procGlobalFree = modkernel32.NewProc("GlobalFree")
|
||||
procGlobalLock = modkernel32.NewProc("GlobalLock")
|
||||
procGlobalUnlock = modkernel32.NewProc("GlobalUnlock")
|
||||
procMoveMemory = modkernel32.NewProc("RtlMoveMemory")
|
||||
procFindResource = modkernel32.NewProc("FindResourceW")
|
||||
procSizeofResource = modkernel32.NewProc("SizeofResource")
|
||||
procLockResource = modkernel32.NewProc("LockResource")
|
||||
procLoadResource = modkernel32.NewProc("LoadResource")
|
||||
procGetLastError = modkernel32.NewProc("GetLastError")
|
||||
procOpenProcess = modkernel32.NewProc("OpenProcess")
|
||||
procTerminateProcess = modkernel32.NewProc("TerminateProcess")
|
||||
procCloseHandle = modkernel32.NewProc("CloseHandle")
|
||||
procCreateToolhelp32Snapshot = modkernel32.NewProc("CreateToolhelp32Snapshot")
|
||||
procModule32First = modkernel32.NewProc("Module32FirstW")
|
||||
procModule32Next = modkernel32.NewProc("Module32NextW")
|
||||
procGetSystemTimes = modkernel32.NewProc("GetSystemTimes")
|
||||
procGetConsoleScreenBufferInfo = modkernel32.NewProc("GetConsoleScreenBufferInfo")
|
||||
procSetConsoleTextAttribute = modkernel32.NewProc("SetConsoleTextAttribute")
|
||||
procGetDiskFreeSpaceEx = modkernel32.NewProc("GetDiskFreeSpaceExW")
|
||||
procGetProcessTimes = modkernel32.NewProc("GetProcessTimes")
|
||||
procSetSystemTime = modkernel32.NewProc("SetSystemTime")
|
||||
procGetSystemTime = modkernel32.NewProc("GetSystemTime")
|
||||
)
|
||||
|
||||
func GetModuleHandle(modulename string) HINSTANCE {
|
||||
var mn uintptr
|
||||
if modulename == "" {
|
||||
mn = 0
|
||||
} else {
|
||||
mn = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(modulename)))
|
||||
}
|
||||
ret, _, _ := procGetModuleHandle.Call(mn)
|
||||
return HINSTANCE(ret)
|
||||
}
|
||||
|
||||
func GetApplicationHandle() HINSTANCE {
|
||||
ret, _, _ := procGetModuleHandle.Call(0)
|
||||
return ret
|
||||
}
|
||||
|
||||
func MulDiv(number, numerator, denominator int) int {
|
||||
ret, _, _ := procMulDiv.Call(
|
||||
uintptr(number),
|
||||
uintptr(numerator),
|
||||
uintptr(denominator))
|
||||
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func GetConsoleWindow() HWND {
|
||||
ret, _, _ := procGetConsoleWindow.Call()
|
||||
|
||||
return HWND(ret)
|
||||
}
|
||||
|
||||
func GetCurrentThread() HANDLE {
|
||||
ret, _, _ := procGetCurrentThread.Call()
|
||||
|
||||
return HANDLE(ret)
|
||||
}
|
||||
|
||||
func GetCurrentThreadId() HANDLE {
|
||||
ret, _, _ := procGetCurrentThreadId.Call()
|
||||
|
||||
return HANDLE(ret)
|
||||
}
|
||||
|
||||
func GetLogicalDrives() uint32 {
|
||||
ret, _, _ := procGetLogicalDrives.Call()
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
func GetUserDefaultLCID() uint32 {
|
||||
ret, _, _ := procGetUserDefaultLCID.Call()
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
func Lstrlen(lpString *uint16) int {
|
||||
ret, _, _ := procLstrlen.Call(uintptr(unsafe.Pointer(lpString)))
|
||||
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func Lstrcpy(buf []uint16, lpString *uint16) {
|
||||
procLstrcpy.Call(
|
||||
uintptr(unsafe.Pointer(&buf[0])),
|
||||
uintptr(unsafe.Pointer(lpString)))
|
||||
}
|
||||
|
||||
func GlobalAlloc(uFlags uint, dwBytes uint32) HGLOBAL {
|
||||
ret, _, _ := procGlobalAlloc.Call(
|
||||
uintptr(uFlags),
|
||||
uintptr(dwBytes))
|
||||
|
||||
if ret == 0 {
|
||||
panic("GlobalAlloc failed")
|
||||
}
|
||||
|
||||
return HGLOBAL(ret)
|
||||
}
|
||||
|
||||
func GlobalFree(hMem HGLOBAL) {
|
||||
ret, _, _ := procGlobalFree.Call(uintptr(hMem))
|
||||
|
||||
if ret != 0 {
|
||||
panic("GlobalFree failed")
|
||||
}
|
||||
}
|
||||
|
||||
func GlobalLock(hMem HGLOBAL) unsafe.Pointer {
|
||||
ret, _, _ := procGlobalLock.Call(uintptr(hMem))
|
||||
|
||||
if ret == 0 {
|
||||
panic("GlobalLock failed")
|
||||
}
|
||||
|
||||
return unsafe.Pointer(ret)
|
||||
}
|
||||
|
||||
func GlobalUnlock(hMem HGLOBAL) bool {
|
||||
ret, _, _ := procGlobalUnlock.Call(uintptr(hMem))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func MoveMemory(destination, source unsafe.Pointer, length uint32) {
|
||||
procMoveMemory.Call(
|
||||
uintptr(unsafe.Pointer(destination)),
|
||||
uintptr(source),
|
||||
uintptr(length))
|
||||
}
|
||||
|
||||
func FindResource(hModule HMODULE, lpName, lpType *uint16) (HRSRC, error) {
|
||||
ret, _, _ := procFindResource.Call(
|
||||
uintptr(hModule),
|
||||
uintptr(unsafe.Pointer(lpName)),
|
||||
uintptr(unsafe.Pointer(lpType)))
|
||||
|
||||
if ret == 0 {
|
||||
return 0, syscall.GetLastError()
|
||||
}
|
||||
|
||||
return HRSRC(ret), nil
|
||||
}
|
||||
|
||||
func SizeofResource(hModule HMODULE, hResInfo HRSRC) uint32 {
|
||||
ret, _, _ := procSizeofResource.Call(
|
||||
uintptr(hModule),
|
||||
uintptr(hResInfo))
|
||||
|
||||
if ret == 0 {
|
||||
panic("SizeofResource failed")
|
||||
}
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
func LockResource(hResData HGLOBAL) unsafe.Pointer {
|
||||
ret, _, _ := procLockResource.Call(uintptr(hResData))
|
||||
|
||||
if ret == 0 {
|
||||
panic("LockResource failed")
|
||||
}
|
||||
|
||||
return unsafe.Pointer(ret)
|
||||
}
|
||||
|
||||
func LoadResource(hModule HMODULE, hResInfo HRSRC) HGLOBAL {
|
||||
ret, _, _ := procLoadResource.Call(
|
||||
uintptr(hModule),
|
||||
uintptr(hResInfo))
|
||||
|
||||
if ret == 0 {
|
||||
panic("LoadResource failed")
|
||||
}
|
||||
|
||||
return HGLOBAL(ret)
|
||||
}
|
||||
|
||||
func GetLastError() uint32 {
|
||||
ret, _, _ := procGetLastError.Call()
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
func OpenProcess(desiredAccess uint32, inheritHandle bool, processId uint32) HANDLE {
|
||||
inherit := 0
|
||||
if inheritHandle {
|
||||
inherit = 1
|
||||
}
|
||||
|
||||
ret, _, _ := procOpenProcess.Call(
|
||||
uintptr(desiredAccess),
|
||||
uintptr(inherit),
|
||||
uintptr(processId))
|
||||
return HANDLE(ret)
|
||||
}
|
||||
|
||||
func TerminateProcess(hProcess HANDLE, uExitCode uint) bool {
|
||||
ret, _, _ := procTerminateProcess.Call(
|
||||
uintptr(hProcess),
|
||||
uintptr(uExitCode))
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func CloseHandle(object HANDLE) bool {
|
||||
ret, _, _ := procCloseHandle.Call(
|
||||
uintptr(object))
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func CreateToolhelp32Snapshot(flags, processId uint32) HANDLE {
|
||||
ret, _, _ := procCreateToolhelp32Snapshot.Call(
|
||||
uintptr(flags),
|
||||
uintptr(processId))
|
||||
|
||||
if ret <= 0 {
|
||||
return HANDLE(0)
|
||||
}
|
||||
|
||||
return HANDLE(ret)
|
||||
}
|
||||
|
||||
func Module32First(snapshot HANDLE, me *MODULEENTRY32) bool {
|
||||
ret, _, _ := procModule32First.Call(
|
||||
uintptr(snapshot),
|
||||
uintptr(unsafe.Pointer(me)))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func Module32Next(snapshot HANDLE, me *MODULEENTRY32) bool {
|
||||
ret, _, _ := procModule32Next.Call(
|
||||
uintptr(snapshot),
|
||||
uintptr(unsafe.Pointer(me)))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func GetSystemTimes(lpIdleTime, lpKernelTime, lpUserTime *FILETIME) bool {
|
||||
ret, _, _ := procGetSystemTimes.Call(
|
||||
uintptr(unsafe.Pointer(lpIdleTime)),
|
||||
uintptr(unsafe.Pointer(lpKernelTime)),
|
||||
uintptr(unsafe.Pointer(lpUserTime)))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func GetProcessTimes(hProcess HANDLE, lpCreationTime, lpExitTime, lpKernelTime, lpUserTime *FILETIME) bool {
|
||||
ret, _, _ := procGetProcessTimes.Call(
|
||||
uintptr(hProcess),
|
||||
uintptr(unsafe.Pointer(lpCreationTime)),
|
||||
uintptr(unsafe.Pointer(lpExitTime)),
|
||||
uintptr(unsafe.Pointer(lpKernelTime)),
|
||||
uintptr(unsafe.Pointer(lpUserTime)))
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func GetConsoleScreenBufferInfo(hConsoleOutput HANDLE) *CONSOLE_SCREEN_BUFFER_INFO {
|
||||
var csbi CONSOLE_SCREEN_BUFFER_INFO
|
||||
ret, _, _ := procGetConsoleScreenBufferInfo.Call(
|
||||
uintptr(hConsoleOutput),
|
||||
uintptr(unsafe.Pointer(&csbi)))
|
||||
if ret == 0 {
|
||||
return nil
|
||||
}
|
||||
return &csbi
|
||||
}
|
||||
|
||||
func SetConsoleTextAttribute(hConsoleOutput HANDLE, wAttributes uint16) bool {
|
||||
ret, _, _ := procSetConsoleTextAttribute.Call(
|
||||
uintptr(hConsoleOutput),
|
||||
uintptr(wAttributes))
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func GetDiskFreeSpaceEx(dirName string) (r bool,
|
||||
freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes uint64) {
|
||||
ret, _, _ := procGetDiskFreeSpaceEx.Call(
|
||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(dirName))),
|
||||
uintptr(unsafe.Pointer(&freeBytesAvailable)),
|
||||
uintptr(unsafe.Pointer(&totalNumberOfBytes)),
|
||||
uintptr(unsafe.Pointer(&totalNumberOfFreeBytes)))
|
||||
return ret != 0,
|
||||
freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes
|
||||
}
|
||||
|
||||
func GetSystemTime() *SYSTEMTIME {
|
||||
var time SYSTEMTIME
|
||||
procGetSystemTime.Call(
|
||||
uintptr(unsafe.Pointer(&time)))
|
||||
return &time
|
||||
}
|
||||
|
||||
func SetSystemTime(time *SYSTEMTIME) bool {
|
||||
ret, _, _ := procSetSystemTime.Call(
|
||||
uintptr(unsafe.Pointer(time)))
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func GetLogicalDriveStrings(nBufferLength uint32, lpBuffer *uint16) uint32 {
|
||||
ret, _, _ := procGetLogicalDriveStrings.Call(
|
||||
uintptr(nBufferLength),
|
||||
uintptr(unsafe.Pointer(lpBuffer)),
|
||||
0)
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
980
v3/pkg/w32/menubar.go
Normal file
980
v3/pkg/w32/menubar.go
Normal file
|
|
@ -0,0 +1,980 @@
|
|||
//go:build windows
|
||||
|
||||
package w32
|
||||
|
||||
import (
|
||||
"os"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
OBJID_MENU = -3
|
||||
ODT_MENU = 1
|
||||
// Menu info flags
|
||||
MIIM_BACKGROUND = 0x00000002
|
||||
MIIM_APPLYTOSUBMENUS = 0x80000000
|
||||
)
|
||||
|
||||
var (
|
||||
menuTheme HTHEME
|
||||
procSetMenuInfo = moduser32.NewProc("SetMenuInfo")
|
||||
)
|
||||
|
||||
type DTTOPTS struct {
|
||||
DwSize uint32
|
||||
DwFlags uint32
|
||||
CrText uint32
|
||||
CrBorder uint32
|
||||
CrShadow uint32
|
||||
ITextShadowType int32
|
||||
PtShadowOffset POINT
|
||||
iBorderSize int32
|
||||
iFontPropId int32
|
||||
IColorPropId int32
|
||||
IStateId int32
|
||||
FApplyOverlay int32
|
||||
IGlowSize int32
|
||||
PfnDrawTextCallback uintptr
|
||||
LParam uintptr
|
||||
}
|
||||
|
||||
const (
|
||||
MENU_POPUPITEM = 14
|
||||
MENU_BARITEM = 8 // Menu bar item part ID for theme drawing
|
||||
DTT_TEXTCOLOR = 1
|
||||
)
|
||||
|
||||
// Menu item states
|
||||
const (
|
||||
ODS_SELECTED = 0x0001
|
||||
ODS_GRAYED = 0x0002
|
||||
ODS_DISABLED = 0x0004
|
||||
ODS_CHECKED = 0x0008
|
||||
ODS_FOCUS = 0x0010
|
||||
ODS_DEFAULT = 0x0020
|
||||
ODS_HOTLIGHT = 0x0040
|
||||
ODS_INACTIVE = 0x0080
|
||||
ODS_NOACCEL = 0x0100
|
||||
ODS_NOFOCUSRECT = 0x0200
|
||||
)
|
||||
|
||||
// Menu Button Image states
|
||||
const (
|
||||
MBI_NORMAL = 1
|
||||
MBI_HOT = 2
|
||||
MBI_PUSHED = 3
|
||||
MBI_DISABLED = 4
|
||||
)
|
||||
|
||||
var (
|
||||
procGetMenuItemInfo = moduser32.NewProc("GetMenuItemInfoW")
|
||||
procGetMenuItemCount = moduser32.NewProc("GetMenuItemCount")
|
||||
procGetMenuItemRect = moduser32.NewProc("GetMenuItemRect")
|
||||
)
|
||||
|
||||
func GetMenuItemInfo(hmenu HMENU, item uint32, fByPosition bool, lpmii *MENUITEMINFO) bool {
|
||||
ret, _, _ := procGetMenuItemInfo.Call(
|
||||
uintptr(hmenu),
|
||||
uintptr(item),
|
||||
uintptr(boolToUint(fByPosition)),
|
||||
uintptr(unsafe.Pointer(lpmii)),
|
||||
)
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func GetMenuItemCount(hmenu HMENU) int {
|
||||
ret, _, _ := procGetMenuItemCount.Call(uintptr(hmenu))
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func GetMenuItemRect(hwnd HWND, hmenu HMENU, item uint32, rect *RECT) bool {
|
||||
ret, _, _ := procGetMenuItemRect.Call(
|
||||
uintptr(hwnd),
|
||||
uintptr(hmenu),
|
||||
uintptr(item),
|
||||
uintptr(unsafe.Pointer(rect)),
|
||||
)
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
// Helper function to convert bool to uint
|
||||
func boolToUint(b bool) uint {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type UAHMENU struct {
|
||||
Hmenu HMENU
|
||||
Hdc HDC
|
||||
DwFlags uint32
|
||||
}
|
||||
|
||||
type MENUBARINFO struct {
|
||||
CbSize uint32
|
||||
Bar RECT
|
||||
Menu HMENU
|
||||
Window HWND
|
||||
BarFocused int32
|
||||
Focused int32
|
||||
}
|
||||
|
||||
type DRAWITEMSTRUCT struct {
|
||||
ControlType uint32
|
||||
ControlID uint32
|
||||
ItemID uint32
|
||||
ItemAction uint32
|
||||
ItemState uint32
|
||||
HWNDItem HWND
|
||||
HDC HDC
|
||||
RcItem RECT
|
||||
ItemData uintptr
|
||||
}
|
||||
|
||||
type UAHDRAWMENUITEM struct {
|
||||
DIS DRAWITEMSTRUCT
|
||||
UM UAHMENU
|
||||
UAMI UAHMENUITEM
|
||||
}
|
||||
|
||||
type UAHMENUITEM struct {
|
||||
Position int
|
||||
Umim UAHMENUITEMMETRICS
|
||||
Umpm UAHMENUPOPUPMETRICS
|
||||
}
|
||||
type UAHMENUITEMMETRICS struct {
|
||||
data [32]byte // Total size of the union in bytes (4 DWORDs * 4 bytes each * 2 arrays)
|
||||
}
|
||||
|
||||
func (u *UAHMENUITEMMETRICS) RgsizeBar() *[2]struct{ cx, cy uint32 } {
|
||||
return (*[2]struct{ cx, cy uint32 })(unsafe.Pointer(&u.data))
|
||||
}
|
||||
|
||||
func (u *UAHMENUITEMMETRICS) RgsizePopup() *[4]struct{ cx, cy uint32 } {
|
||||
return (*[4]struct{ cx, cy uint32 })(unsafe.Pointer(&u.data))
|
||||
}
|
||||
|
||||
type UAHMEASUREMENUITEM struct {
|
||||
UM UAHMENU
|
||||
UAMI UAHMENUITEM
|
||||
Mis MEASUREITEMSTRUCT
|
||||
}
|
||||
|
||||
type MEASUREITEMSTRUCT struct {
|
||||
CtlType uint32
|
||||
CtlID uint32
|
||||
ItemID uint32
|
||||
ItemWidth uint32
|
||||
ItemHeight uint32
|
||||
ItemData uintptr
|
||||
}
|
||||
|
||||
type UAHMENUPOPUPMETRICS struct {
|
||||
Rgcx [4]uint32 // Array of 4 DWORDs
|
||||
FUpdateMaxWidths uint32 // Bit-field represented as a uint32
|
||||
}
|
||||
|
||||
// Helper function to get the value of the fUpdateMaxWidths bit-field
|
||||
func (u *UAHMENUPOPUPMETRICS) GetFUpdateMaxWidths() uint32 {
|
||||
return u.FUpdateMaxWidths & 0x3 // Mask to get the first 2 bits
|
||||
}
|
||||
|
||||
// Helper function to set the value of the fUpdateMaxWidths bit-field
|
||||
func (u *UAHMENUPOPUPMETRICS) SetFUpdateMaxWidths(value uint32) {
|
||||
u.FUpdateMaxWidths = (u.FUpdateMaxWidths &^ 0x3) | (value & 0x3) // Clear and set the first 2 bits
|
||||
}
|
||||
|
||||
type MenuBarTheme struct {
|
||||
TitleBarBackground *uint32
|
||||
TitleBarText *uint32
|
||||
MenuBarBackground *uint32 // Separate color for menubar
|
||||
MenuHoverBackground *uint32
|
||||
MenuHoverText *uint32
|
||||
MenuSelectedBackground *uint32
|
||||
MenuSelectedText *uint32
|
||||
|
||||
// private brushes
|
||||
titleBarBackgroundBrush HBRUSH
|
||||
menuBarBackgroundBrush HBRUSH // Separate brush for menubar
|
||||
menuHoverBackgroundBrush HBRUSH
|
||||
menuSelectedBackgroundBrush HBRUSH
|
||||
}
|
||||
|
||||
func createColourWithDefaultColor(color *uint32, def uint32) *uint32 {
|
||||
if color == nil {
|
||||
return &def
|
||||
}
|
||||
return color
|
||||
}
|
||||
|
||||
func (d *MenuBarTheme) Init() {
|
||||
d.TitleBarBackground = createColourWithDefaultColor(d.TitleBarBackground, RGB(25, 25, 26))
|
||||
d.TitleBarText = createColourWithDefaultColor(d.TitleBarText, RGB(222, 222, 222))
|
||||
d.MenuBarBackground = createColourWithDefaultColor(d.MenuBarBackground, RGB(33, 33, 33))
|
||||
d.MenuSelectedText = createColourWithDefaultColor(d.MenuSelectedText, RGB(222, 222, 222))
|
||||
d.MenuSelectedBackground = createColourWithDefaultColor(d.MenuSelectedBackground, RGB(48, 48, 48))
|
||||
d.MenuHoverText = createColourWithDefaultColor(d.MenuHoverText, RGB(222, 222, 222))
|
||||
d.MenuHoverBackground = createColourWithDefaultColor(d.MenuHoverBackground, RGB(48, 48, 48))
|
||||
// Create brushes
|
||||
d.titleBarBackgroundBrush = CreateSolidBrush(*d.TitleBarBackground)
|
||||
d.menuBarBackgroundBrush = CreateSolidBrush(*d.MenuBarBackground)
|
||||
d.menuHoverBackgroundBrush = CreateSolidBrush(*d.MenuHoverBackground)
|
||||
d.menuSelectedBackgroundBrush = CreateSolidBrush(*d.MenuSelectedBackground)
|
||||
}
|
||||
|
||||
// SetMenuBackground sets the menu background brush directly
|
||||
func (d *MenuBarTheme) SetMenuBackground(hmenu HMENU) {
|
||||
var mi MENUINFO
|
||||
mi.CbSize = uint32(unsafe.Sizeof(mi))
|
||||
mi.FMask = MIIM_BACKGROUND | MIIM_APPLYTOSUBMENUS
|
||||
mi.HbrBack = d.menuBarBackgroundBrush // Use separate menubar brush
|
||||
SetMenuInfo(hmenu, &mi)
|
||||
}
|
||||
|
||||
// SetMenuInfo wrapper function
|
||||
func SetMenuInfo(hmenu HMENU, lpcmi *MENUINFO) bool {
|
||||
ret, _, _ := procSetMenuInfo.Call(
|
||||
uintptr(hmenu),
|
||||
uintptr(unsafe.Pointer(lpcmi)))
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func CreateSolidBrush(color COLORREF) HBRUSH {
|
||||
ret, _, _ := procCreateSolidBrush.Call(
|
||||
uintptr(color),
|
||||
)
|
||||
return HBRUSH(ret)
|
||||
}
|
||||
|
||||
func RGB(r, g, b byte) uint32 {
|
||||
return uint32(r) | uint32(g)<<8 | uint32(b)<<16
|
||||
}
|
||||
|
||||
func RGBptr(r, g, b byte) *uint32 {
|
||||
result := uint32(r) | uint32(g)<<8 | uint32(b)<<16
|
||||
return &result
|
||||
}
|
||||
|
||||
// Track hover state for menubar items when maximized
|
||||
var (
|
||||
currentHoverItem int = -1
|
||||
menuIsOpen bool = false // Track if a dropdown menu is open
|
||||
)
|
||||
|
||||
func MenuBarWndProc(hwnd HWND, msg uint32, wParam WPARAM, lParam LPARAM, theme *MenuBarTheme) (bool, LRESULT) {
|
||||
// Only proceed if we have a theme (either for dark or light mode)
|
||||
if theme == nil {
|
||||
return false, 0
|
||||
}
|
||||
switch msg {
|
||||
case WM_UAHDRAWMENU:
|
||||
udm := (*UAHMENU)(unsafe.Pointer(lParam))
|
||||
|
||||
// Check if maximized first
|
||||
isMaximized := IsZoomed(hwnd)
|
||||
|
||||
// get the menubar rect
|
||||
var menuBarInfo MENUBARINFO
|
||||
menuBarInfo.CbSize = uint32(unsafe.Sizeof(menuBarInfo))
|
||||
if !GetMenuBarInfo(hwnd, OBJID_MENU, 0, &menuBarInfo) {
|
||||
return false, 0
|
||||
}
|
||||
|
||||
winRect := GetWindowRect(hwnd)
|
||||
|
||||
// the rcBar is offset by the window rect
|
||||
rc := menuBarInfo.Bar
|
||||
OffsetRect(&rc, int(-winRect.Left), int(-winRect.Top))
|
||||
|
||||
// DEBUG: Log the coordinates
|
||||
// println("WM_UAHDRAWMENU: maximized=", isMaximized)
|
||||
// println(" menubar screen rect: L=", menuBarInfo.Bar.Left, "T=", menuBarInfo.Bar.Top,
|
||||
// "R=", menuBarInfo.Bar.Right, "B=", menuBarInfo.Bar.Bottom)
|
||||
// println(" window rect: L=", winRect.Left, "T=", winRect.Top,
|
||||
// "R=", winRect.Right, "B=", winRect.Bottom)
|
||||
// println(" converted rect: L=", rc.Left, "T=", rc.Top,
|
||||
// "R=", rc.Right, "B=", rc.Bottom)
|
||||
|
||||
// When maximized, Windows extends the window beyond the visible area
|
||||
// We need to adjust the menubar rect to ensure it's fully visible
|
||||
if isMaximized {
|
||||
// Get the frame size - this is how much the window extends beyond visible area when maximized
|
||||
frameY := GetSystemMetrics(SM_CYSIZEFRAME)
|
||||
paddedBorder := GetSystemMetrics(SM_CXPADDEDBORDER)
|
||||
|
||||
// In Windows 10/11, the actual border is frame + padding
|
||||
borderSize := frameY + paddedBorder
|
||||
|
||||
// println(" Frame metrics: frameY=", frameY, "paddedBorder=", paddedBorder, "borderSize=", borderSize)
|
||||
|
||||
// First, fill the area from the top of the visible area to the menubar
|
||||
topFillRect := RECT{
|
||||
Left: rc.Left,
|
||||
Top: int32(borderSize), // Start of visible area in window coordinates
|
||||
Right: rc.Right,
|
||||
Bottom: rc.Top, // Up to where the menubar starts
|
||||
}
|
||||
FillRect(udm.Hdc, &topFillRect, theme.menuBarBackgroundBrush)
|
||||
}
|
||||
|
||||
// Fill the entire menubar background with dark color
|
||||
FillRect(udm.Hdc, &rc, theme.menuBarBackgroundBrush)
|
||||
|
||||
// Paint over the menubar border explicitly
|
||||
// The border is typically 1-2 pixels at the bottom
|
||||
borderRect := rc
|
||||
borderRect.Top = borderRect.Bottom - 1
|
||||
borderRect.Bottom = borderRect.Bottom + 2
|
||||
FillRect(udm.Hdc, &borderRect, theme.menuBarBackgroundBrush)
|
||||
|
||||
// When maximized, we still need to handle the drawing ourselves
|
||||
// Some projects found that returning false here causes issues
|
||||
|
||||
// When maximized, manually draw all menu items here
|
||||
if isMaximized {
|
||||
// Draw each menu item manually
|
||||
itemCount := GetMenuItemCount(menuBarInfo.Menu)
|
||||
for i := 0; i < itemCount; i++ {
|
||||
var itemRect RECT
|
||||
if GetMenuItemRect(hwnd, menuBarInfo.Menu, uint32(i), &itemRect) {
|
||||
// Convert to window coordinates
|
||||
OffsetRect(&itemRect, int(-winRect.Left), int(-winRect.Top))
|
||||
|
||||
// Check if this item is hovered
|
||||
if i == currentHoverItem {
|
||||
// Fill with hover background
|
||||
FillRect(udm.Hdc, &itemRect, theme.menuHoverBackgroundBrush)
|
||||
}
|
||||
|
||||
// Get menu text
|
||||
menuString := make([]uint16, 256)
|
||||
mii := MENUITEMINFO{
|
||||
CbSize: uint32(unsafe.Sizeof(MENUITEMINFO{})),
|
||||
FMask: MIIM_STRING,
|
||||
DwTypeData: &menuString[0],
|
||||
Cch: uint32(len(menuString) - 1),
|
||||
}
|
||||
|
||||
if GetMenuItemInfo(menuBarInfo.Menu, uint32(i), true, &mii) {
|
||||
// Draw the text
|
||||
if i == currentHoverItem {
|
||||
SetTextColor(udm.Hdc, COLORREF(*theme.MenuHoverText))
|
||||
} else {
|
||||
SetTextColor(udm.Hdc, COLORREF(*theme.TitleBarText))
|
||||
}
|
||||
SetBkMode(udm.Hdc, TRANSPARENT)
|
||||
DrawText(udm.Hdc, menuString, -1, &itemRect, DT_CENTER|DT_SINGLELINE|DT_VCENTER)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the original HDC so Windows can draw the menu text
|
||||
return true, LRESULT(udm.Hdc)
|
||||
case WM_DRAWITEM:
|
||||
// Handle owner-drawn menu items
|
||||
dis := (*DRAWITEMSTRUCT)(unsafe.Pointer(lParam))
|
||||
|
||||
// Check if this is a menu item
|
||||
if dis.ControlType == ODT_MENU {
|
||||
// Draw the menu item background
|
||||
var bgBrush HBRUSH
|
||||
var textColor uint32
|
||||
|
||||
if dis.ItemState&ODS_SELECTED != 0 {
|
||||
// Selected state
|
||||
bgBrush = theme.menuSelectedBackgroundBrush
|
||||
textColor = *theme.MenuSelectedText
|
||||
} else {
|
||||
// Normal state
|
||||
bgBrush = theme.titleBarBackgroundBrush
|
||||
textColor = *theme.TitleBarText
|
||||
}
|
||||
|
||||
// Fill background
|
||||
FillRect(dis.HDC, &dis.RcItem, bgBrush)
|
||||
|
||||
// Draw text if we have item data
|
||||
if dis.ItemData != 0 {
|
||||
text := (*uint16)(unsafe.Pointer(dis.ItemData))
|
||||
if text != nil {
|
||||
// Set text color and draw
|
||||
SetTextColor(dis.HDC, COLORREF(textColor))
|
||||
SetBkMode(dis.HDC, TRANSPARENT)
|
||||
DrawText(dis.HDC, (*[256]uint16)(unsafe.Pointer(text))[:], -1, &dis.RcItem, DT_CENTER|DT_SINGLELINE|DT_VCENTER)
|
||||
}
|
||||
}
|
||||
|
||||
return true, 1
|
||||
}
|
||||
case WM_UAHDRAWMENUITEM:
|
||||
udmi := (*UAHDRAWMENUITEM)(unsafe.Pointer(lParam))
|
||||
|
||||
// Check if we're getting menu item draw messages when maximized or fullscreen
|
||||
isMaximized := IsZoomed(hwnd)
|
||||
|
||||
// Create buffer for menu text
|
||||
menuString := make([]uint16, 256)
|
||||
|
||||
// Setup menu item info structure
|
||||
mii := MENUITEMINFO{
|
||||
CbSize: uint32(unsafe.Sizeof(MENUITEMINFO{})),
|
||||
FMask: MIIM_STRING | MIIM_SUBMENU,
|
||||
DwTypeData: &menuString[0],
|
||||
Cch: uint32(len(menuString) - 1),
|
||||
}
|
||||
|
||||
if !GetMenuItemInfo(udmi.UM.Hmenu, uint32(udmi.UAMI.Position), true, &mii) {
|
||||
// Failed to get menu item info, let default handler process
|
||||
return false, 0
|
||||
}
|
||||
|
||||
// Remove automatic popup on hover - menus should only open on click
|
||||
// This was causing the menu to appear at wrong coordinates
|
||||
dwFlags := uint32(DT_CENTER | DT_SINGLELINE | DT_VCENTER)
|
||||
|
||||
// When maximized/fullscreen, try without VCENTER to see if text appears
|
||||
if isMaximized && os.Getenv("WAILS_TEST_NO_VCENTER") == "1" {
|
||||
dwFlags = uint32(DT_CENTER | DT_SINGLELINE)
|
||||
println(" Using dwFlags without VCENTER")
|
||||
}
|
||||
|
||||
// Check if this is a menubar item
|
||||
// When dwFlags has 0x0A00 (2560) it's a menubar item
|
||||
isMenuBarItem := (udmi.UM.DwFlags&0x0A00) == 0x0A00 || udmi.UM.DwFlags == 0
|
||||
|
||||
// Use different colors for menubar vs popup items
|
||||
var bgBrush HBRUSH
|
||||
var textColor uint32
|
||||
|
||||
if udmi.DIS.ItemState&ODS_HOTLIGHT != 0 {
|
||||
// Hot state - use a specific color for hover
|
||||
bgBrush = theme.menuHoverBackgroundBrush
|
||||
textColor = *theme.MenuHoverText
|
||||
} else if udmi.DIS.ItemState&ODS_SELECTED != 0 {
|
||||
// Selected state
|
||||
bgBrush = theme.menuSelectedBackgroundBrush
|
||||
textColor = *theme.MenuSelectedText
|
||||
} else {
|
||||
// Normal state
|
||||
if isMenuBarItem {
|
||||
// Menubar items in normal state
|
||||
bgBrush = theme.menuBarBackgroundBrush
|
||||
textColor = *theme.TitleBarText
|
||||
} else {
|
||||
// Popup menu items in normal state - use same color as menubar
|
||||
bgBrush = theme.menuBarBackgroundBrush
|
||||
textColor = *theme.TitleBarText
|
||||
}
|
||||
}
|
||||
|
||||
// Fill background
|
||||
if bgBrush != 0 {
|
||||
FillRect(udmi.UM.Hdc, &udmi.DIS.RcItem, bgBrush)
|
||||
}
|
||||
|
||||
// Draw text
|
||||
SetTextColor(udmi.UM.Hdc, COLORREF(textColor))
|
||||
SetBkMode(udmi.UM.Hdc, TRANSPARENT)
|
||||
|
||||
// When maximized/fullscreen and menubar item, use the same font settings as drawMenuBarText
|
||||
if isMaximized && isMenuBarItem {
|
||||
// Create a non-bold font explicitly
|
||||
menuFont := LOGFONT{
|
||||
Height: -12, // Standard Windows menu font height (9pt)
|
||||
Weight: 400, // FW_NORMAL (not bold)
|
||||
CharSet: 1, // DEFAULT_CHARSET
|
||||
Quality: 5, // CLEARTYPE_QUALITY
|
||||
PitchAndFamily: 0, // DEFAULT_PITCH
|
||||
}
|
||||
// Set font face name to "Segoe UI" (Windows default)
|
||||
fontName := []uint16{'S', 'e', 'g', 'o', 'e', ' ', 'U', 'I', 0}
|
||||
copy(menuFont.FaceName[:], fontName)
|
||||
|
||||
hFont := CreateFontIndirect(&menuFont)
|
||||
if hFont != 0 {
|
||||
oldFont := SelectObject(udmi.UM.Hdc, HGDIOBJ(hFont))
|
||||
DrawText(udmi.UM.Hdc, menuString, -1, &udmi.DIS.RcItem, dwFlags)
|
||||
SelectObject(udmi.UM.Hdc, oldFont)
|
||||
DeleteObject(HGDIOBJ(hFont))
|
||||
} else {
|
||||
DrawText(udmi.UM.Hdc, menuString, -1, &udmi.DIS.RcItem, dwFlags)
|
||||
}
|
||||
return true, 4 // CDRF_SKIPDEFAULT
|
||||
} else {
|
||||
DrawText(udmi.UM.Hdc, menuString, -1, &udmi.DIS.RcItem, dwFlags)
|
||||
}
|
||||
|
||||
// Return appropriate value based on whether we're in maximized/fullscreen
|
||||
// For maximized, we need to ensure Windows doesn't override our drawing
|
||||
if isMaximized {
|
||||
// Skip default processing to prevent Windows from overriding our colors
|
||||
return true, 4 // CDRF_SKIPDEFAULT
|
||||
}
|
||||
// Return 1 to indicate we've handled the drawing
|
||||
return true, 1
|
||||
case WM_UAHMEASUREMENUITEM:
|
||||
// Let the default window procedure handle the menu item measurement
|
||||
// We're not modifying the default sizing anymore
|
||||
result := DefWindowProc(hwnd, msg, wParam, lParam)
|
||||
|
||||
return true, result
|
||||
case WM_NCPAINT:
|
||||
// Paint our custom menubar first
|
||||
paintDarkMenuBar(hwnd, theme)
|
||||
|
||||
// Then let Windows do its default painting
|
||||
result := DefWindowProc(hwnd, msg, wParam, lParam)
|
||||
|
||||
// Paint again to ensure our painting is on top
|
||||
paintDarkMenuBar(hwnd, theme)
|
||||
|
||||
return true, result
|
||||
case WM_NCACTIVATE:
|
||||
result := DefWindowProc(hwnd, msg, wParam, lParam)
|
||||
|
||||
// Force paint the menubar with dark background
|
||||
paintDarkMenuBar(hwnd, theme)
|
||||
|
||||
return false, result
|
||||
case WM_PAINT:
|
||||
// Let Windows paint first
|
||||
result := DefWindowProc(hwnd, msg, wParam, lParam)
|
||||
|
||||
// Then paint our menubar
|
||||
paintDarkMenuBar(hwnd, theme)
|
||||
|
||||
return false, result
|
||||
case WM_ACTIVATEAPP, WM_ACTIVATE:
|
||||
// Handle app activation/deactivation
|
||||
result := DefWindowProc(hwnd, msg, wParam, lParam)
|
||||
|
||||
// Repaint menubar
|
||||
paintDarkMenuBar(hwnd, theme)
|
||||
|
||||
return false, result
|
||||
case WM_SIZE, WM_WINDOWPOSCHANGED:
|
||||
// Handle window size changes
|
||||
result := DefWindowProc(hwnd, msg, wParam, lParam)
|
||||
|
||||
// Repaint menubar after size change
|
||||
paintDarkMenuBar(hwnd, theme)
|
||||
|
||||
// CRITICAL: Force complete menubar redraw when maximized
|
||||
if msg == WM_SIZE && wParam == SIZE_MAXIMIZED {
|
||||
// Invalidate the entire menubar area to force redraw
|
||||
var mbi MENUBARINFO
|
||||
mbi.CbSize = uint32(unsafe.Sizeof(mbi))
|
||||
if GetMenuBarInfo(hwnd, OBJID_MENU, 0, &mbi) {
|
||||
InvalidateRect(hwnd, &mbi.Bar, true)
|
||||
DrawMenuBar(hwnd)
|
||||
}
|
||||
}
|
||||
|
||||
return false, result
|
||||
case WM_SETFOCUS, WM_KILLFOCUS:
|
||||
// Handle focus changes (e.g., when inspector opens)
|
||||
result := DefWindowProc(hwnd, msg, wParam, lParam)
|
||||
|
||||
// Repaint menubar after focus change
|
||||
paintDarkMenuBar(hwnd, theme)
|
||||
|
||||
return false, result
|
||||
case WM_ERASEBKGND:
|
||||
// When maximized, draw menubar text here
|
||||
if IsZoomed(hwnd) {
|
||||
var menuBarInfo MENUBARINFO
|
||||
menuBarInfo.CbSize = uint32(unsafe.Sizeof(menuBarInfo))
|
||||
if GetMenuBarInfo(hwnd, OBJID_MENU, 0, &menuBarInfo) {
|
||||
hdc := HDC(wParam)
|
||||
drawMenuBarText(hwnd, hdc, &menuBarInfo, theme)
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
case WM_NCMOUSEMOVE, WM_MOUSEMOVE:
|
||||
// Track mouse movement for hover effects when maximized
|
||||
if IsZoomed(hwnd) {
|
||||
// Don't process hover changes while menu is open
|
||||
if menuIsOpen {
|
||||
return false, 0
|
||||
}
|
||||
|
||||
var screenX, screenY int32
|
||||
if msg == WM_NCMOUSEMOVE {
|
||||
// For NC messages, lParam contains screen coordinates
|
||||
screenX = int32(LOWORD(uint32(lParam)))
|
||||
screenY = int32(HIWORD(uint32(lParam)))
|
||||
} else {
|
||||
// For regular MOUSEMOVE, convert client to screen coordinates
|
||||
clientX := int32(LOWORD(uint32(lParam)))
|
||||
clientY := int32(HIWORD(uint32(lParam)))
|
||||
sx, sy := ClientToScreen(hwnd, int(clientX), int(clientY))
|
||||
screenX = int32(sx)
|
||||
screenY = int32(sy)
|
||||
}
|
||||
|
||||
// Check if we're over the menubar
|
||||
var menuBarInfo MENUBARINFO
|
||||
menuBarInfo.CbSize = uint32(unsafe.Sizeof(menuBarInfo))
|
||||
if GetMenuBarInfo(hwnd, OBJID_MENU, 0, &menuBarInfo) {
|
||||
// menuBarInfo.Bar already contains screen coordinates
|
||||
// Check if mouse is over menubar using screen coordinates
|
||||
if screenX >= menuBarInfo.Bar.Left && screenX <= menuBarInfo.Bar.Right &&
|
||||
screenY >= menuBarInfo.Bar.Top && screenY <= menuBarInfo.Bar.Bottom {
|
||||
|
||||
// Always re-request mouse tracking to ensure we get leave messages
|
||||
TrackMouseEvent(&TRACKMOUSEEVENT{
|
||||
CbSize: uint32(unsafe.Sizeof(TRACKMOUSEEVENT{})),
|
||||
DwFlags: TME_LEAVE | TME_NONCLIENT,
|
||||
HwndTrack: hwnd,
|
||||
DwHoverTime: 0,
|
||||
})
|
||||
// Find which menu item we're over
|
||||
itemCount := GetMenuItemCount(menuBarInfo.Menu)
|
||||
newHoverItem := -1
|
||||
|
||||
for i := 0; i < itemCount; i++ {
|
||||
var itemRect RECT
|
||||
if GetMenuItemRect(hwnd, menuBarInfo.Menu, uint32(i), &itemRect) {
|
||||
// itemRect is already in screen coordinates from GetMenuItemRect
|
||||
// Check using screen coordinates
|
||||
if screenX >= itemRect.Left && screenX <= itemRect.Right &&
|
||||
screenY >= itemRect.Top && screenY <= itemRect.Bottom {
|
||||
newHoverItem = i
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If hover item changed, update and redraw just the menubar
|
||||
if newHoverItem != currentHoverItem {
|
||||
currentHoverItem = newHoverItem
|
||||
// Get the actual menubar rect for precise invalidation
|
||||
winRect := GetWindowRect(hwnd)
|
||||
menubarRect := menuBarInfo.Bar
|
||||
// Convert to window coordinates
|
||||
menubarRect.Left -= winRect.Left
|
||||
menubarRect.Top -= winRect.Top
|
||||
menubarRect.Right -= winRect.Left
|
||||
menubarRect.Bottom -= winRect.Top
|
||||
// Invalidate only the menubar
|
||||
InvalidateRect(hwnd, &menubarRect, false)
|
||||
}
|
||||
} else {
|
||||
// Mouse left menubar
|
||||
if currentHoverItem != -1 {
|
||||
currentHoverItem = -1
|
||||
// Get the actual menubar rect
|
||||
winRect := GetWindowRect(hwnd)
|
||||
menubarRect := menuBarInfo.Bar
|
||||
// Convert to window coordinates
|
||||
menubarRect.Left -= winRect.Left
|
||||
menubarRect.Top -= winRect.Top
|
||||
menubarRect.Right -= winRect.Left
|
||||
menubarRect.Bottom -= winRect.Top
|
||||
InvalidateRect(hwnd, &menubarRect, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
case WM_NCLBUTTONDOWN:
|
||||
// When clicking on menubar, clear hover state immediately
|
||||
if IsZoomed(hwnd) && currentHoverItem != -1 {
|
||||
// Check if click is on menubar
|
||||
var menuBarInfo MENUBARINFO
|
||||
menuBarInfo.CbSize = uint32(unsafe.Sizeof(menuBarInfo))
|
||||
if GetMenuBarInfo(hwnd, OBJID_MENU, 0, &menuBarInfo) {
|
||||
// Get click position (screen coordinates)
|
||||
clickX := int32(LOWORD(uint32(lParam)))
|
||||
clickY := int32(HIWORD(uint32(lParam)))
|
||||
|
||||
if clickX >= menuBarInfo.Bar.Left && clickX <= menuBarInfo.Bar.Right &&
|
||||
clickY >= menuBarInfo.Bar.Top && clickY <= menuBarInfo.Bar.Bottom {
|
||||
// Click is on menubar - clear hover
|
||||
currentHoverItem = -1
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
case WM_NCMOUSELEAVE, WM_MOUSELEAVE:
|
||||
// Clear hover state when mouse leaves (but not if menu is open)
|
||||
if IsZoomed(hwnd) && currentHoverItem != -1 && !menuIsOpen {
|
||||
currentHoverItem = -1
|
||||
// Get menubar info for precise invalidation
|
||||
var menuBarInfo MENUBARINFO
|
||||
menuBarInfo.CbSize = uint32(unsafe.Sizeof(menuBarInfo))
|
||||
if GetMenuBarInfo(hwnd, OBJID_MENU, 0, &menuBarInfo) {
|
||||
winRect := GetWindowRect(hwnd)
|
||||
menubarRect := menuBarInfo.Bar
|
||||
menubarRect.Left -= winRect.Left
|
||||
menubarRect.Top -= winRect.Top
|
||||
menubarRect.Right -= winRect.Left
|
||||
menubarRect.Bottom -= winRect.Top
|
||||
InvalidateRect(hwnd, &menubarRect, false)
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
case WM_ENTERMENULOOP:
|
||||
// Menu is being opened - clear hover state
|
||||
menuIsOpen = true
|
||||
if IsZoomed(hwnd) && currentHoverItem != -1 {
|
||||
oldHoverItem := currentHoverItem
|
||||
currentHoverItem = -1
|
||||
// Redraw the previously hovered item to remove hover effect
|
||||
var menuBarInfo MENUBARINFO
|
||||
menuBarInfo.CbSize = uint32(unsafe.Sizeof(menuBarInfo))
|
||||
if GetMenuBarInfo(hwnd, OBJID_MENU, 0, &menuBarInfo) {
|
||||
var itemRect RECT
|
||||
if GetMenuItemRect(hwnd, menuBarInfo.Menu, uint32(oldHoverItem), &itemRect) {
|
||||
winRect := GetWindowRect(hwnd)
|
||||
// Convert to window coordinates
|
||||
itemRect.Left -= winRect.Left
|
||||
itemRect.Top -= winRect.Top
|
||||
itemRect.Right -= winRect.Left
|
||||
itemRect.Bottom -= winRect.Top
|
||||
// Add some padding
|
||||
itemRect.Left -= 5
|
||||
itemRect.Right += 5
|
||||
itemRect.Top -= 5
|
||||
itemRect.Bottom += 5
|
||||
InvalidateRect(hwnd, &itemRect, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
case WM_EXITMENULOOP:
|
||||
// Menu has been closed
|
||||
menuIsOpen = false
|
||||
// Clear any existing hover state first
|
||||
currentHoverItem = -1
|
||||
// Force a complete menubar redraw
|
||||
if IsZoomed(hwnd) {
|
||||
var menuBarInfo MENUBARINFO
|
||||
menuBarInfo.CbSize = uint32(unsafe.Sizeof(menuBarInfo))
|
||||
if GetMenuBarInfo(hwnd, OBJID_MENU, 0, &menuBarInfo) {
|
||||
winRect := GetWindowRect(hwnd)
|
||||
menubarRect := menuBarInfo.Bar
|
||||
menubarRect.Left -= winRect.Left
|
||||
menubarRect.Top -= winRect.Top
|
||||
menubarRect.Right -= winRect.Left
|
||||
menubarRect.Bottom -= winRect.Top
|
||||
InvalidateRect(hwnd, &menubarRect, false)
|
||||
}
|
||||
// Force a timer to restart mouse tracking
|
||||
SetTimer(hwnd, 1001, 50, 0)
|
||||
}
|
||||
return false, 0
|
||||
case WM_TIMER:
|
||||
// Handle our mouse tracking restart timer
|
||||
if wParam == 1001 {
|
||||
KillTimer(hwnd, 1001)
|
||||
if IsZoomed(hwnd) {
|
||||
// Get current mouse position and simulate a mouse move
|
||||
x, y, _ := GetCursorPos()
|
||||
// Check if mouse is over the window
|
||||
winRect := GetWindowRect(hwnd)
|
||||
if x >= int(winRect.Left) && x <= int(winRect.Right) &&
|
||||
y >= int(winRect.Top) && y <= int(winRect.Bottom) {
|
||||
// Check if we're over the menubar specifically
|
||||
var menuBarInfo MENUBARINFO
|
||||
menuBarInfo.CbSize = uint32(unsafe.Sizeof(menuBarInfo))
|
||||
if GetMenuBarInfo(hwnd, OBJID_MENU, 0, &menuBarInfo) {
|
||||
if int32(x) >= menuBarInfo.Bar.Left && int32(x) <= menuBarInfo.Bar.Right &&
|
||||
int32(y) >= menuBarInfo.Bar.Top && int32(y) <= menuBarInfo.Bar.Bottom {
|
||||
// Post a non-client mouse move to restart tracking
|
||||
PostMessage(hwnd, WM_NCMOUSEMOVE, 0, uintptr(y)<<16|uintptr(x)&0xFFFF)
|
||||
} else {
|
||||
// Convert to client coordinates for regular mouse move
|
||||
clientX, clientY, _ := ScreenToClient(hwnd, x, y)
|
||||
// Post a mouse move message to restart tracking
|
||||
PostMessage(hwnd, WM_MOUSEMOVE, 0, uintptr(clientY)<<16|uintptr(clientX)&0xFFFF)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true, 0
|
||||
}
|
||||
return false, 0
|
||||
}
|
||||
return false, 0
|
||||
}
|
||||
|
||||
// paintDarkMenuBar paints the menubar with dark background
|
||||
func paintDarkMenuBar(hwnd HWND, theme *MenuBarTheme) {
|
||||
// Get menubar info
|
||||
var menuBarInfo MENUBARINFO
|
||||
menuBarInfo.CbSize = uint32(unsafe.Sizeof(menuBarInfo))
|
||||
if !GetMenuBarInfo(hwnd, OBJID_MENU, 0, &menuBarInfo) {
|
||||
return
|
||||
}
|
||||
|
||||
// Get window DC
|
||||
hdc := GetWindowDC(hwnd)
|
||||
if hdc == 0 {
|
||||
return
|
||||
}
|
||||
defer ReleaseDC(hwnd, hdc)
|
||||
|
||||
// Check if window is maximized or fullscreen
|
||||
isMaximized := IsZoomed(hwnd)
|
||||
isFullscreen := false
|
||||
|
||||
// Check if window is in fullscreen by checking if it covers the monitor
|
||||
windowRect := GetWindowRect(hwnd)
|
||||
monitor := MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY)
|
||||
var monitorInfo MONITORINFO
|
||||
monitorInfo.CbSize = uint32(unsafe.Sizeof(monitorInfo))
|
||||
if GetMonitorInfo(monitor, &monitorInfo) {
|
||||
// If window matches monitor bounds, it's fullscreen
|
||||
if windowRect.Left == monitorInfo.RcMonitor.Left &&
|
||||
windowRect.Top == monitorInfo.RcMonitor.Top &&
|
||||
windowRect.Right == monitorInfo.RcMonitor.Right &&
|
||||
windowRect.Bottom == monitorInfo.RcMonitor.Bottom {
|
||||
isFullscreen = true
|
||||
}
|
||||
}
|
||||
|
||||
// When maximized or fullscreen, we need to handle the special case
|
||||
if isMaximized || isFullscreen {
|
||||
// Convert menubar rect from screen to window coordinates
|
||||
menubarRect := menuBarInfo.Bar
|
||||
menubarRect.Left -= windowRect.Left
|
||||
menubarRect.Top -= windowRect.Top
|
||||
menubarRect.Right -= windowRect.Left
|
||||
menubarRect.Bottom -= windowRect.Top
|
||||
|
||||
if isMaximized && !isFullscreen {
|
||||
// Get the frame size (only for maximized, not fullscreen)
|
||||
frameY := GetSystemMetrics(SM_CYSIZEFRAME)
|
||||
paddedBorder := GetSystemMetrics(SM_CXPADDEDBORDER)
|
||||
borderSize := frameY + paddedBorder
|
||||
|
||||
// Fill from visible area top to menubar
|
||||
topFillRect := RECT{
|
||||
Left: menubarRect.Left,
|
||||
Top: int32(borderSize), // Start of visible area
|
||||
Right: menubarRect.Right,
|
||||
Bottom: menubarRect.Top,
|
||||
}
|
||||
FillRect(hdc, &topFillRect, theme.menuBarBackgroundBrush)
|
||||
} else if isFullscreen {
|
||||
// In fullscreen, fill from the very top
|
||||
topFillRect := RECT{
|
||||
Left: menubarRect.Left,
|
||||
Top: 0, // Start from top in fullscreen
|
||||
Right: menubarRect.Right,
|
||||
Bottom: menubarRect.Top,
|
||||
}
|
||||
FillRect(hdc, &topFillRect, theme.menuBarBackgroundBrush)
|
||||
}
|
||||
|
||||
// Fill the menubar itself
|
||||
FillRect(hdc, &menubarRect, theme.menuBarBackgroundBrush)
|
||||
} else {
|
||||
// Paint the menubar background with dark color
|
||||
FillRect(hdc, &menuBarInfo.Bar, theme.menuBarBackgroundBrush)
|
||||
}
|
||||
|
||||
// Get window and client rects to find the non-client area
|
||||
clientRect := GetClientRect(hwnd)
|
||||
|
||||
// Convert client rect top-left to screen coordinates
|
||||
_, screenY := ClientToScreen(hwnd, int(clientRect.Left), int(clientRect.Top))
|
||||
|
||||
// Paint the entire area between menubar and client area
|
||||
// This should cover any borders
|
||||
borderRect := RECT{
|
||||
Left: 0,
|
||||
Top: menuBarInfo.Bar.Bottom - windowRect.Top,
|
||||
Right: windowRect.Right - windowRect.Left,
|
||||
Bottom: int32(screenY) - windowRect.Top,
|
||||
}
|
||||
FillRect(hdc, &borderRect, theme.menuBarBackgroundBrush)
|
||||
|
||||
// When maximized or fullscreen, also draw menubar text
|
||||
if isMaximized || isFullscreen {
|
||||
drawMenuBarText(hwnd, hdc, &menuBarInfo, theme)
|
||||
}
|
||||
}
|
||||
|
||||
func drawMenuBarText(hwnd HWND, hdc HDC, menuBarInfo *MENUBARINFO, theme *MenuBarTheme) {
|
||||
// Get the menu handle
|
||||
hmenu := menuBarInfo.Menu
|
||||
if hmenu == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Get the number of menu items
|
||||
itemCount := GetMenuItemCount(hmenu)
|
||||
if itemCount <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Create a non-bold font explicitly
|
||||
menuFont := LOGFONT{
|
||||
Height: -12, // Standard Windows menu font height (9pt)
|
||||
Weight: 400, // FW_NORMAL (not bold)
|
||||
CharSet: 1, // DEFAULT_CHARSET
|
||||
Quality: 5, // CLEARTYPE_QUALITY
|
||||
PitchAndFamily: 0, // DEFAULT_PITCH
|
||||
}
|
||||
// Set font face name to "Segoe UI" (Windows default)
|
||||
fontName := []uint16{'S', 'e', 'g', 'o', 'e', ' ', 'U', 'I', 0}
|
||||
copy(menuFont.FaceName[:], fontName)
|
||||
|
||||
hFont := CreateFontIndirect(&menuFont)
|
||||
if hFont != 0 {
|
||||
oldFont := SelectObject(hdc, HGDIOBJ(hFont))
|
||||
defer func() {
|
||||
SelectObject(hdc, oldFont)
|
||||
DeleteObject(HGDIOBJ(hFont))
|
||||
}()
|
||||
}
|
||||
|
||||
// Set text color and background mode
|
||||
SetTextColor(hdc, COLORREF(*theme.TitleBarText))
|
||||
SetBkMode(hdc, TRANSPARENT)
|
||||
|
||||
// Get the window rect for coordinate conversion
|
||||
winRect := GetWindowRect(hwnd)
|
||||
|
||||
// Iterate through each menu item
|
||||
for i := 0; i < itemCount; i++ {
|
||||
// Get the menu item rect
|
||||
var itemRect RECT
|
||||
if !GetMenuItemRect(hwnd, hmenu, uint32(i), &itemRect) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Convert to window coordinates
|
||||
OffsetRect(&itemRect, int(-winRect.Left), int(-winRect.Top))
|
||||
|
||||
// Check if this item is hovered
|
||||
if i == currentHoverItem {
|
||||
// Fill with hover background
|
||||
FillRect(hdc, &itemRect, theme.menuHoverBackgroundBrush)
|
||||
}
|
||||
|
||||
// Get the menu item text
|
||||
menuString := make([]uint16, 256)
|
||||
mii := MENUITEMINFO{
|
||||
CbSize: uint32(unsafe.Sizeof(MENUITEMINFO{})),
|
||||
FMask: MIIM_STRING,
|
||||
DwTypeData: &menuString[0],
|
||||
Cch: uint32(len(menuString) - 1),
|
||||
}
|
||||
|
||||
if GetMenuItemInfo(hmenu, uint32(i), true, &mii) {
|
||||
// Set text color based on hover state
|
||||
if i == currentHoverItem {
|
||||
SetTextColor(hdc, COLORREF(*theme.MenuHoverText))
|
||||
} else {
|
||||
SetTextColor(hdc, COLORREF(*theme.TitleBarText))
|
||||
}
|
||||
// Draw the text
|
||||
DrawText(hdc, menuString, -1, &itemRect, DT_CENTER|DT_SINGLELINE|DT_VCENTER)
|
||||
}
|
||||
}
|
||||
}
|
||||
119
v3/pkg/w32/ole32.go
Normal file
119
v3/pkg/w32/ole32.go
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
//go:build windows
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Tad Vizbaras. All Rights Reserved.
|
||||
* Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved.
|
||||
*/
|
||||
package w32
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/go-webview2/pkg/combridge"
|
||||
)
|
||||
|
||||
var (
|
||||
modole32 = syscall.NewLazyDLL("ole32.dll")
|
||||
|
||||
procCoInitializeEx = modole32.NewProc("CoInitializeEx")
|
||||
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")
|
||||
)
|
||||
|
||||
func CoInitializeEx(coInit uintptr) HRESULT {
|
||||
ret, _, _ := procCoInitializeEx.Call(
|
||||
0,
|
||||
coInit)
|
||||
|
||||
switch uint32(ret) {
|
||||
case E_INVALIDARG:
|
||||
panic("CoInitializeEx failed with E_INVALIDARG")
|
||||
case E_OUTOFMEMORY:
|
||||
panic("CoInitializeEx failed with E_OUTOFMEMORY")
|
||||
case E_UNEXPECTED:
|
||||
panic("CoInitializeEx failed with E_UNEXPECTED")
|
||||
}
|
||||
|
||||
return HRESULT(ret)
|
||||
}
|
||||
|
||||
func CoInitialize() {
|
||||
procCoInitialize.Call(0)
|
||||
}
|
||||
|
||||
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(
|
||||
uintptr(hGlobal),
|
||||
uintptr(BoolToBOOL(fDeleteOnRelease)),
|
||||
uintptr(unsafe.Pointer(&stream)))
|
||||
|
||||
switch uint32(ret) {
|
||||
case E_INVALIDARG:
|
||||
panic("CreateStreamOnHGlobal failed with E_INVALIDARG")
|
||||
case E_OUTOFMEMORY:
|
||||
panic("CreateStreamOnHGlobal failed with E_OUTOFMEMORY")
|
||||
case E_UNEXPECTED:
|
||||
panic("CreateStreamOnHGlobal failed with E_UNEXPECTED")
|
||||
}
|
||||
|
||||
return stream
|
||||
}
|
||||
func OleInitialise() {
|
||||
procOleInitialize.Call()
|
||||
}
|
||||
|
||||
func RegisterDragDrop(hwnd HWND, dropTarget *DropTarget) error {
|
||||
|
||||
dt := combridge.New[iDropTarget](dropTarget)
|
||||
hr, _, _ := procRegisterDragDrop.Call(
|
||||
hwnd,
|
||||
dt.Ref(),
|
||||
)
|
||||
|
||||
if hr != S_OK {
|
||||
return syscall.Errno(hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func RevokeDragDrop(hwnd HWND) error {
|
||||
hr, _, _ := procRevokeDragDrop.Call(
|
||||
hwnd,
|
||||
)
|
||||
|
||||
if hr != S_OK {
|
||||
return syscall.Errno(hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
50
v3/pkg/w32/oleaut32.go
Normal file
50
v3/pkg/w32/oleaut32.go
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
//go:build windows
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Tad Vizbaras. All Rights Reserved.
|
||||
* Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved.
|
||||
*/
|
||||
package w32
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
modoleaut32 = syscall.NewLazyDLL("oleaut32")
|
||||
|
||||
procVariantInit = modoleaut32.NewProc("VariantInit")
|
||||
procSysAllocString = modoleaut32.NewProc("SysAllocString")
|
||||
procSysFreeString = modoleaut32.NewProc("SysFreeString")
|
||||
procSysStringLen = modoleaut32.NewProc("SysStringLen")
|
||||
procCreateDispTypeInfo = modoleaut32.NewProc("CreateDispTypeInfo")
|
||||
procCreateStdDispatch = modoleaut32.NewProc("CreateStdDispatch")
|
||||
)
|
||||
|
||||
func VariantInit(v *VARIANT) {
|
||||
hr, _, _ := procVariantInit.Call(uintptr(unsafe.Pointer(v)))
|
||||
if hr != 0 {
|
||||
panic("Invoke VariantInit error.")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SysAllocString(v string) (ss *int16) {
|
||||
pss, _, _ := procSysAllocString.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(v))))
|
||||
ss = (*int16)(unsafe.Pointer(pss))
|
||||
return
|
||||
}
|
||||
|
||||
func SysFreeString(v *int16) {
|
||||
hr, _, _ := procSysFreeString.Call(uintptr(unsafe.Pointer(v)))
|
||||
if hr != 0 {
|
||||
panic("Invoke SysFreeString error.")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SysStringLen(v *int16) uint {
|
||||
l, _, _ := procSysStringLen.Call(uintptr(unsafe.Pointer(v)))
|
||||
return uint(l)
|
||||
}
|
||||
96
v3/pkg/w32/popupmenu.go
Normal file
96
v3/pkg/w32/popupmenu.go
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
//go:build windows
|
||||
|
||||
package w32
|
||||
|
||||
type Menu HMENU
|
||||
type PopupMenu Menu
|
||||
|
||||
func (m Menu) destroy() bool {
|
||||
ret, _, _ := procDestroyMenu.Call(uintptr(m))
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func (p PopupMenu) destroy() bool {
|
||||
return Menu(p).destroy()
|
||||
}
|
||||
|
||||
func (p PopupMenu) Track(hwnd HWND, flags uint32, x, y int32) bool {
|
||||
return TrackPopupMenuEx(
|
||||
HMENU(p),
|
||||
flags,
|
||||
x,
|
||||
y,
|
||||
hwnd,
|
||||
nil)
|
||||
}
|
||||
|
||||
func RemoveMenu(m HMENU, pos, flags int) bool {
|
||||
ret, _, _ := procRemoveMenu.Call(
|
||||
uintptr(m),
|
||||
uintptr(pos),
|
||||
uintptr(flags))
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func (p PopupMenu) Append(flags uint32, id uintptr, text string) bool {
|
||||
return Menu(p).Append(flags, id, text)
|
||||
}
|
||||
|
||||
func (m Menu) Append(flags uint32, id uintptr, text string) bool {
|
||||
return AppendMenu(HMENU(m), flags, id, MustStringToUTF16Ptr(text))
|
||||
}
|
||||
|
||||
func (p PopupMenu) Check(id uintptr, checked bool) bool {
|
||||
return Menu(p).Check(id, checked)
|
||||
}
|
||||
|
||||
func (m Menu) Check(id uintptr, check bool) bool {
|
||||
var checkState uint = MF_UNCHECKED
|
||||
if check {
|
||||
checkState = MF_CHECKED
|
||||
}
|
||||
return CheckMenuItem(HMENU(m), id, checkState) != 0
|
||||
}
|
||||
|
||||
func CheckRadio(m HMENU, startID int, endID int, selectedID int) bool {
|
||||
ret, _, _ := procCheckMenuRadioItem.Call(
|
||||
m,
|
||||
uintptr(startID),
|
||||
uintptr(endID),
|
||||
uintptr(selectedID),
|
||||
MF_BYCOMMAND)
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func (m Menu) CheckRadio(startID int, endID int, selectedID int) bool {
|
||||
ret, _, _ := procCheckMenuRadioItem.Call(
|
||||
uintptr(m),
|
||||
uintptr(startID),
|
||||
uintptr(endID),
|
||||
uintptr(selectedID),
|
||||
MF_BYCOMMAND)
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func CheckMenuItem(menu HMENU, id uintptr, flags uint) uint {
|
||||
ret, _, _ := procCheckMenuItem.Call(
|
||||
menu,
|
||||
id,
|
||||
uintptr(flags),
|
||||
)
|
||||
return uint(ret)
|
||||
}
|
||||
|
||||
func (p PopupMenu) CheckRadio(startID, endID, selectedID int) bool {
|
||||
return Menu(p).CheckRadio(startID, endID, selectedID)
|
||||
}
|
||||
|
||||
func NewMenu() HMENU {
|
||||
ret, _, _ := procCreateMenu.Call()
|
||||
return HMENU(ret)
|
||||
}
|
||||
|
||||
func NewPopupMenu() HMENU {
|
||||
ret, _, _ := procCreatePopupMenu.Call()
|
||||
return ret
|
||||
}
|
||||
143
v3/pkg/w32/screen.go
Normal file
143
v3/pkg/w32/screen.go
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
//go:build windows
|
||||
|
||||
package w32
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type Screen struct {
|
||||
MONITORINFOEX
|
||||
HMonitor uintptr
|
||||
Name string
|
||||
IsPrimary bool
|
||||
IsCurrent bool
|
||||
ScaleFactor float32
|
||||
Rotation float32
|
||||
}
|
||||
|
||||
type DISPLAY_DEVICE struct {
|
||||
cb uint32
|
||||
DeviceName [32]uint16
|
||||
DeviceString [128]uint16
|
||||
StateFlags uint32
|
||||
DeviceID [128]uint16
|
||||
DeviceKey [128]uint16
|
||||
}
|
||||
|
||||
func getMonitorName(deviceName string) (string, error) {
|
||||
var device DISPLAY_DEVICE
|
||||
device.cb = uint32(unsafe.Sizeof(device))
|
||||
i := uint32(0)
|
||||
for {
|
||||
res, _, _ := procEnumDisplayDevices.Call(uintptr(unsafe.Pointer(MustStringToUTF16Ptr(deviceName))), uintptr(i), uintptr(unsafe.Pointer(&device)), 0)
|
||||
if res == 0 {
|
||||
break
|
||||
}
|
||||
if device.StateFlags&0x1 != 0 {
|
||||
return syscall.UTF16ToString(device.DeviceString[:]), nil
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("monitor name not found for device: %s", deviceName)
|
||||
}
|
||||
|
||||
// I'm not convinced this works properly
|
||||
func GetRotationForMonitor(displayName [32]uint16) (float32, error) {
|
||||
var devMode DEVMODE
|
||||
devMode.DmSize = uint16(unsafe.Sizeof(devMode))
|
||||
resp, _, _ := procEnumDisplaySettings.Call(uintptr(unsafe.Pointer(&displayName[0])), ENUM_CURRENT_SETTINGS, uintptr(unsafe.Pointer(&devMode)))
|
||||
if resp == 0 {
|
||||
return 0, fmt.Errorf("EnumDisplaySettings failed")
|
||||
}
|
||||
|
||||
if (devMode.DmFields & DM_DISPLAYORIENTATION) == 0 {
|
||||
return 0, fmt.Errorf("DM_DISPLAYORIENTATION not set")
|
||||
}
|
||||
|
||||
switch devMode.DmOrientation {
|
||||
case DMDO_DEFAULT:
|
||||
return 0, nil
|
||||
case DMDO_90:
|
||||
return 90, nil
|
||||
case DMDO_180:
|
||||
return 180, nil
|
||||
case DMDO_270:
|
||||
return 270, nil
|
||||
}
|
||||
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func GetAllScreens() ([]*Screen, error) {
|
||||
var result []*Screen
|
||||
var errMessage string
|
||||
|
||||
// Get cursor position to determine the current monitor
|
||||
var cursor POINT
|
||||
ret, _, _ := procGetCursorPos.Call(uintptr(unsafe.Pointer(&cursor)))
|
||||
if ret == 0 {
|
||||
return nil, fmt.Errorf("GetCursorPos failed")
|
||||
}
|
||||
|
||||
// Enumerate the monitors
|
||||
enumFunc := func(hMonitor uintptr, hdc uintptr, lprcMonitor *RECT, lParam uintptr) uintptr {
|
||||
monitor := MONITORINFOEX{
|
||||
MONITORINFO: MONITORINFO{
|
||||
CbSize: uint32(unsafe.Sizeof(MONITORINFOEX{})),
|
||||
},
|
||||
SzDevice: [32]uint16{},
|
||||
}
|
||||
ret, _, _ := procGetMonitorInfo.Call(hMonitor, uintptr(unsafe.Pointer(&monitor)))
|
||||
if ret == 0 {
|
||||
errMessage = "GetMonitorInfo failed"
|
||||
return 0 // Stop enumeration
|
||||
}
|
||||
|
||||
screen := &Screen{
|
||||
MONITORINFOEX: monitor,
|
||||
HMonitor: hMonitor,
|
||||
IsPrimary: monitor.DwFlags == MONITORINFOF_PRIMARY,
|
||||
IsCurrent: rectContainsPoint(monitor.RcMonitor, cursor),
|
||||
}
|
||||
|
||||
// Get monitor name
|
||||
name, err := getMonitorName(syscall.UTF16ToString(monitor.SzDevice[:]))
|
||||
if err == nil {
|
||||
screen.Name = name
|
||||
}
|
||||
|
||||
// Get DPI for monitor
|
||||
var dpiX, dpiY uint
|
||||
ret = GetDPIForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY)
|
||||
if ret != S_OK {
|
||||
errMessage = "GetDpiForMonitor failed"
|
||||
return 0 // Stop enumeration
|
||||
}
|
||||
// Convert to scale factor
|
||||
screen.ScaleFactor = float32(dpiX) / 96.0
|
||||
|
||||
// Get rotation of monitor
|
||||
rot, err := GetRotationForMonitor(monitor.SzDevice)
|
||||
if err == nil {
|
||||
screen.Rotation = rot
|
||||
}
|
||||
|
||||
result = append(result, screen)
|
||||
return 1 // Continue enumeration
|
||||
}
|
||||
|
||||
ret, _, _ = procEnumDisplayMonitors.Call(0, 0, syscall.NewCallback(enumFunc), 0)
|
||||
if ret == 0 {
|
||||
return nil, fmt.Errorf("EnumDisplayMonitors failed: %s", errMessage)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func rectContainsPoint(r RECT, p POINT) bool {
|
||||
return p.X >= r.Left && p.X < r.Right && p.Y >= r.Top && p.Y < r.Bottom
|
||||
}
|
||||
71
v3/pkg/w32/shcore.go
Normal file
71
v3/pkg/w32/shcore.go
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
//go:build windows
|
||||
|
||||
package w32
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
modshcore = syscall.NewLazyDLL("shcore.dll")
|
||||
|
||||
procGetDpiForMonitor = modshcore.NewProc("GetDpiForMonitor")
|
||||
procGetProcessDpiAwareness = modshcore.NewProc("GetProcessDpiAwareness")
|
||||
procSetProcessDpiAwareness = modshcore.NewProc("SetProcessDpiAwareness")
|
||||
)
|
||||
|
||||
func HasGetProcessDpiAwarenessFunc() bool {
|
||||
err := procGetProcessDpiAwareness.Find()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// GetProcessDpiAwareness retrieves the DPI awareness of the current process.
|
||||
// Returns one of: PROCESS_DPI_UNAWARE, PROCESS_SYSTEM_DPI_AWARE, or PROCESS_PER_MONITOR_DPI_AWARE.
|
||||
func GetProcessDpiAwareness() (uint, error) {
|
||||
var awareness uint
|
||||
status, _, err := procGetProcessDpiAwareness.Call(0, uintptr(unsafe.Pointer(&awareness)))
|
||||
if status != S_OK {
|
||||
return 0, fmt.Errorf("GetProcessDpiAwareness failed: %v", err)
|
||||
}
|
||||
return awareness, nil
|
||||
}
|
||||
|
||||
func HasSetProcessDpiAwarenessFunc() bool {
|
||||
err := procSetProcessDpiAwareness.Find()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func SetProcessDpiAwareness(val uint) error {
|
||||
status, r, err := procSetProcessDpiAwareness.Call(uintptr(val))
|
||||
if status != S_OK {
|
||||
return fmt.Errorf("procSetProcessDpiAwareness failed %d: %v %v", status, r, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func HasGetDPIForMonitorFunc() bool {
|
||||
err := procGetDpiForMonitor.Find()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func GetDPIForMonitor(hmonitor HMONITOR, dpiType MONITOR_DPI_TYPE, dpiX *UINT, dpiY *UINT) uintptr {
|
||||
ret, _, _ := procGetDpiForMonitor.Call(
|
||||
hmonitor,
|
||||
uintptr(dpiType),
|
||||
uintptr(unsafe.Pointer(dpiX)),
|
||||
uintptr(unsafe.Pointer(dpiY)))
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func GetNotificationFlyoutBounds() (*RECT, error) {
|
||||
var rect RECT
|
||||
res, _, err := procSystemParametersInfo.Call(SPI_GETNOTIFYWINDOWRECT, 0, uintptr(unsafe.Pointer(&rect)), 0)
|
||||
if res == 0 {
|
||||
_ = err
|
||||
return nil, err
|
||||
}
|
||||
return &rect, nil
|
||||
}
|
||||
414
v3/pkg/w32/shell32.go
Normal file
414
v3/pkg/w32/shell32.go
Normal file
|
|
@ -0,0 +1,414 @@
|
|||
//go:build windows
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Tad Vizbaras. All Rights Reserved.
|
||||
* Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved.
|
||||
*/
|
||||
package w32
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type CSIDL uint32
|
||||
|
||||
const (
|
||||
CSIDL_DESKTOP = 0x00
|
||||
CSIDL_INTERNET = 0x01
|
||||
CSIDL_PROGRAMS = 0x02
|
||||
CSIDL_CONTROLS = 0x03
|
||||
CSIDL_PRINTERS = 0x04
|
||||
CSIDL_PERSONAL = 0x05
|
||||
CSIDL_FAVORITES = 0x06
|
||||
CSIDL_STARTUP = 0x07
|
||||
CSIDL_RECENT = 0x08
|
||||
CSIDL_SENDTO = 0x09
|
||||
CSIDL_BITBUCKET = 0x0A
|
||||
CSIDL_STARTMENU = 0x0B
|
||||
CSIDL_MYDOCUMENTS = 0x0C
|
||||
CSIDL_MYMUSIC = 0x0D
|
||||
CSIDL_MYVIDEO = 0x0E
|
||||
CSIDL_DESKTOPDIRECTORY = 0x10
|
||||
CSIDL_DRIVES = 0x11
|
||||
CSIDL_NETWORK = 0x12
|
||||
CSIDL_NETHOOD = 0x13
|
||||
CSIDL_FONTS = 0x14
|
||||
CSIDL_TEMPLATES = 0x15
|
||||
CSIDL_COMMON_STARTMENU = 0x16
|
||||
CSIDL_COMMON_PROGRAMS = 0x17
|
||||
CSIDL_COMMON_STARTUP = 0x18
|
||||
CSIDL_COMMON_DESKTOPDIRECTORY = 0x19
|
||||
CSIDL_APPDATA = 0x1A
|
||||
CSIDL_PRINTHOOD = 0x1B
|
||||
CSIDL_LOCAL_APPDATA = 0x1C
|
||||
CSIDL_ALTSTARTUP = 0x1D
|
||||
CSIDL_COMMON_ALTSTARTUP = 0x1E
|
||||
CSIDL_COMMON_FAVORITES = 0x1F
|
||||
CSIDL_INTERNET_CACHE = 0x20
|
||||
CSIDL_COOKIES = 0x21
|
||||
CSIDL_HISTORY = 0x22
|
||||
CSIDL_COMMON_APPDATA = 0x23
|
||||
CSIDL_WINDOWS = 0x24
|
||||
CSIDL_SYSTEM = 0x25
|
||||
CSIDL_PROGRAM_FILES = 0x26
|
||||
CSIDL_MYPICTURES = 0x27
|
||||
CSIDL_PROFILE = 0x28
|
||||
CSIDL_SYSTEMX86 = 0x29
|
||||
CSIDL_PROGRAM_FILESX86 = 0x2A
|
||||
CSIDL_PROGRAM_FILES_COMMON = 0x2B
|
||||
CSIDL_PROGRAM_FILES_COMMONX86 = 0x2C
|
||||
CSIDL_COMMON_TEMPLATES = 0x2D
|
||||
CSIDL_COMMON_DOCUMENTS = 0x2E
|
||||
CSIDL_COMMON_ADMINTOOLS = 0x2F
|
||||
CSIDL_ADMINTOOLS = 0x30
|
||||
CSIDL_CONNECTIONS = 0x31
|
||||
CSIDL_COMMON_MUSIC = 0x35
|
||||
CSIDL_COMMON_PICTURES = 0x36
|
||||
CSIDL_COMMON_VIDEO = 0x37
|
||||
CSIDL_RESOURCES = 0x38
|
||||
CSIDL_RESOURCES_LOCALIZED = 0x39
|
||||
CSIDL_COMMON_OEM_LINKS = 0x3A
|
||||
CSIDL_CDBURN_AREA = 0x3B
|
||||
CSIDL_COMPUTERSNEARME = 0x3D
|
||||
CSIDL_FLAG_CREATE = 0x8000
|
||||
CSIDL_FLAG_DONT_VERIFY = 0x4000
|
||||
CSIDL_FLAG_NO_ALIAS = 0x1000
|
||||
CSIDL_FLAG_PER_USER_INIT = 0x8000
|
||||
CSIDL_FLAG_MASK = 0xFF00
|
||||
)
|
||||
|
||||
var (
|
||||
FOLDERID_AccountPictures = NewGUID("{008CA0B1-55B4-4C56-B8A8-4DE4B299D3BE}")
|
||||
FOLDERID_AddNewPrograms = NewGUID("{DE61D971-5EBC-4F02-A3A9-6C82895E5C04}")
|
||||
FOLDERID_AdminTools = NewGUID("{724EF170-A42D-4FEF-9F26-B60E846FBA4F}")
|
||||
FOLDERID_ApplicationShortcuts = NewGUID("{A3918781-E5F2-4890-B3D9-A7E54332328C}")
|
||||
FOLDERID_AppsFolder = NewGUID("{1E87508D-89C2-42F0-8A7E-645A0F50CA58}")
|
||||
FOLDERID_AppUpdates = NewGUID("{A305CE99-F527-492B-8B1A-7E76FA98D6E4}")
|
||||
FOLDERID_CDBurning = NewGUID("{9E52AB10-F80D-49DF-ACB8-4330F5687855}")
|
||||
FOLDERID_ChangeRemovePrograms = NewGUID("{DF7266AC-9274-4867-8D55-3BD661DE872D}")
|
||||
FOLDERID_CommonAdminTools = NewGUID("{D0384E7D-BAC3-4797-8F14-CBA229B392B5}")
|
||||
FOLDERID_CommonOEMLinks = NewGUID("{C1BAE2D0-10DF-4334-BEDD-7AA20B227A9D}")
|
||||
FOLDERID_CommonPrograms = NewGUID("{0139D44E-6AFE-49F2-8690-3DAFCAE6FFB8}")
|
||||
FOLDERID_CommonStartMenu = NewGUID("{A4115719-D62E-491D-AA7C-E74B8BE3B067}")
|
||||
FOLDERID_CommonStartup = NewGUID("{82A5EA35-D9CD-47C5-9629-E15D2F714E6E}")
|
||||
FOLDERID_CommonTemplates = NewGUID("{B94237E7-57AC-4347-9151-B08C6C32D1F7}")
|
||||
FOLDERID_ComputerFolder = NewGUID("{0AC0837C-BBF8-452A-850D-79D08E667CA7}")
|
||||
FOLDERID_ConflictFolder = NewGUID("{4BFEFB45-347D-4006-A5BE-AC0CB0567192}")
|
||||
FOLDERID_ConnectionsFolder = NewGUID("{6F0CD92B-2E97-45D1-88FF-B0D186B8DEDD}")
|
||||
FOLDERID_Contacts = NewGUID("{56784854-C6CB-462B-8169-88E350ACB882}")
|
||||
FOLDERID_ControlPanelFolder = NewGUID("{82A74AEB-AEB4-465C-A014-D097EE346D63}")
|
||||
FOLDERID_Cookies = NewGUID("{2B0F765D-C0E9-4171-908E-08A611B84FF6}")
|
||||
FOLDERID_Desktop = NewGUID("{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}")
|
||||
FOLDERID_DeviceMetadataStore = NewGUID("{5CE4A5E9-E4EB-479D-B89F-130C02886155}")
|
||||
FOLDERID_Documents = NewGUID("{FDD39AD0-238F-46AF-ADB4-6C85480369C7}")
|
||||
FOLDERID_DocumentsLibrary = NewGUID("{7B0DB17D-9CD2-4A93-9733-46CC89022E7C}")
|
||||
FOLDERID_Downloads = NewGUID("{374DE290-123F-4565-9164-39C4925E467B}")
|
||||
FOLDERID_Favorites = NewGUID("{1777F761-68AD-4D8A-87BD-30B759FA33DD}")
|
||||
FOLDERID_Fonts = NewGUID("{FD228CB7-AE11-4AE3-864C-16F3910AB8FE}")
|
||||
FOLDERID_Games = NewGUID("{CAC52C1A-B53D-4EDC-92D7-6B2E8AC19434}")
|
||||
FOLDERID_GameTasks = NewGUID("{054FAE61-4DD8-4787-80B6-090220C4B700}")
|
||||
FOLDERID_History = NewGUID("{D9DC8A3B-B784-432E-A781-5A1130A75963}")
|
||||
FOLDERID_HomeGroup = NewGUID("{52528A6B-B9E3-4ADD-B60D-588C2DBA842D}")
|
||||
FOLDERID_HomeGroupCurrentUser = NewGUID("{9B74B6A3-0DFD-4F11-9E78-5F7800F2E772}")
|
||||
FOLDERID_ImplicitAppShortcuts = NewGUID("{BCB5256F-79F6-4CEE-B725-DC34E402FD46}")
|
||||
FOLDERID_InternetCache = NewGUID("{352481E8-33BE-4251-BA85-6007CAEDCF9D}")
|
||||
FOLDERID_InternetFolder = NewGUID("{4D9F7874-4E0C-4904-967B-40B0D20C3E4B}")
|
||||
FOLDERID_Libraries = NewGUID("{1B3EA5DC-B587-4786-B4EF-BD1DC332AEAE}")
|
||||
FOLDERID_Links = NewGUID("{BFB9D5E0-C6A9-404C-B2B2-AE6DB6AF4968}")
|
||||
FOLDERID_LocalAppData = NewGUID("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}")
|
||||
FOLDERID_LocalAppDataLow = NewGUID("{A520A1A4-1780-4FF6-BD18-167343C5AF16}")
|
||||
FOLDERID_LocalizedResourcesDir = NewGUID("{2A00375E-224C-49DE-B8D1-440DF7EF3DDC}")
|
||||
FOLDERID_Music = NewGUID("{4BD8D571-6D19-48D3-BE97-422220080E43}")
|
||||
FOLDERID_MusicLibrary = NewGUID("{2112AB0A-C86A-4FFE-A368-0DE96E47012E}")
|
||||
FOLDERID_NetHood = NewGUID("{C5ABBF53-E17F-4121-8900-86626FC2C973}")
|
||||
FOLDERID_NetworkFolder = NewGUID("{D20BEEC4-5CA8-4905-AE3B-BF251EA09B53}")
|
||||
FOLDERID_OriginalImages = NewGUID("{2C36C0AA-5812-4B87-BFD0-4CD0DFB19B39}")
|
||||
FOLDERID_PhotoAlbums = NewGUID("{69D2CF90-FC33-4FB7-9A0C-EBB0F0FCB43C}")
|
||||
FOLDERID_Pictures = NewGUID("{33E28130-4E1E-4676-835A-98395C3BC3BB}")
|
||||
FOLDERID_PicturesLibrary = NewGUID("{A990AE9F-A03B-4E80-94BC-9912D7504104}")
|
||||
FOLDERID_Playlists = NewGUID("{DE92C1C7-837F-4F69-A3BB-86E631204A23}")
|
||||
FOLDERID_PrintersFolder = NewGUID("{76FC4E2D-D6AD-4519-A663-37BD56068185}")
|
||||
FOLDERID_PrintHood = NewGUID("{9274BD8D-CFD1-41C3-B35E-B13F55A758F4}")
|
||||
FOLDERID_Profile = NewGUID("{5E6C858F-0E22-4760-9AFE-EA3317B67173}")
|
||||
FOLDERID_ProgramData = NewGUID("{62AB5D82-FDC1-4DC3-A9DD-070D1D495D97}")
|
||||
FOLDERID_ProgramFiles = NewGUID("{905E63B6-C1BF-494E-B29C-65B732D3D21A}")
|
||||
FOLDERID_ProgramFilesCommon = NewGUID("{F7F1ED05-9F6D-47A2-AAAE-29D317C6F066}")
|
||||
FOLDERID_ProgramFilesCommonX64 = NewGUID("{6365D5A7-0F0D-45E5-87F6-0DA56B6A4F7D}")
|
||||
FOLDERID_ProgramFilesCommonX86 = NewGUID("{DE974D24-D9C6-4D3E-BF91-F4455120B917}")
|
||||
FOLDERID_ProgramFilesX64 = NewGUID("{6D809377-6AF0-444B-8957-A3773F02200E}")
|
||||
FOLDERID_ProgramFilesX86 = NewGUID("{7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}")
|
||||
FOLDERID_Programs = NewGUID("{A77F5D77-2E2B-44C3-A6A2-ABA601054A51}")
|
||||
FOLDERID_Public = NewGUID("{DFDF76A2-C82A-4D63-906A-5644AC457385}")
|
||||
FOLDERID_PublicDesktop = NewGUID("{C4AA340D-F20F-4863-AFEF-1F769F2BE730}")
|
||||
FOLDERID_PublicDocuments = NewGUID("{ED4824AF-DCE4-45A8-81E2-FC7965083634}")
|
||||
FOLDERID_PublicDownloads = NewGUID("{3D644C9B-1FB8-4F30-9B45-F670235F79C0}")
|
||||
FOLDERID_PublicGameTasks = NewGUID("{DEBF2536-E1A8-4C59-B6A2-414586476AEA}")
|
||||
FOLDERID_PublicLibraries = NewGUID("{48DAF80B-E6CF-4F4E-B800-0E69D84EE384}")
|
||||
FOLDERID_PublicMusic = NewGUID("{3214FAB5-9757-4298-BB61-92A9DEAA44FF}")
|
||||
FOLDERID_PublicPictures = NewGUID("{B6EBFB86-6907-413C-9AF7-4FC2ABF07CC5}")
|
||||
FOLDERID_PublicRingtones = NewGUID("{E555AB60-153B-4D17-9F04-A5FE99FC15EC}")
|
||||
FOLDERID_PublicUserTiles = NewGUID("{0482af6c-08f1-4c34-8c90-e17ec98b1e17}")
|
||||
FOLDERID_PublicVideos = NewGUID("{2400183A-6185-49FB-A2D8-4A392A602BA3}")
|
||||
FOLDERID_QuickLaunch = NewGUID("{52a4f021-7b75-48a9-9f6b-4b87a210bc8f}")
|
||||
FOLDERID_Recent = NewGUID("{AE50C081-EBD2-438A-8655-8A092E34987A}")
|
||||
FOLDERID_RecordedTVLibrary = NewGUID("{1A6FDBA2-F42D-4358-A798-B74D745926C5}")
|
||||
FOLDERID_RecycleBinFolder = NewGUID("{B7534046-3ECB-4C18-BE4E-64CD4CB7D6AC}")
|
||||
FOLDERID_ResourceDir = NewGUID("{8AD10C31-2ADB-4296-A8F7-E4701232C972}")
|
||||
FOLDERID_Ringtones = NewGUID("{C870044B-F49E-4126-A9C3-B52A1FF411E8}")
|
||||
FOLDERID_RoamingAppData = NewGUID("{3EB685DB-65F9-4CF6-A03A-E3EF65729F3D}")
|
||||
FOLDERID_RoamingTiles = NewGUID("{AAA8D5A5-F1D6-4259-BAA8-78E7EF60835E}")
|
||||
FOLDERID_SampleMusic = NewGUID("{B250C668-F57D-4EE1-A63C-290EE7D1AA1F}")
|
||||
FOLDERID_SamplePictures = NewGUID("{C4900540-2379-4C75-844B-64E6FAF8716B}")
|
||||
FOLDERID_SamplePlaylists = NewGUID("{15CA69B3-30EE-49C1-ACE1-6B5EC372AFB5}")
|
||||
FOLDERID_SampleVideos = NewGUID("{859EAD94-2E85-48AD-A71A-0969CB56A6CD}")
|
||||
FOLDERID_SavedGames = NewGUID("{4C5C32FF-BB9D-43B0-B5B4-2D72E54EAAA4}")
|
||||
FOLDERID_SavedPictures = NewGUID("{3B193882-D3AD-4EAB-965A-69829D1FB59F}")
|
||||
FOLDERID_SavedPicturesLibrary = NewGUID("{E25B5812-BE88-4BD9-94B0-29233477B6C3}")
|
||||
FOLDERID_SavedSearches = NewGUID("{7D1D3A04-DEBB-4115-95CF-2F29DA2920DA}")
|
||||
FOLDERID_SEARCH_CSC = NewGUID("{ee32e446-31ca-4aba-814f-a5ebd2fd6d5e}")
|
||||
FOLDERID_SEARCH_MAPI = NewGUID("{98ec0e18-2098-4d44-8644-66979315a281}")
|
||||
FOLDERID_SearchHome = NewGUID("{190337d1-b8ca-4121-a639-6d472d16972a}")
|
||||
FOLDERID_SendTo = NewGUID("{8983036C-27C0-404B-8F08-102D10DCFD74}")
|
||||
FOLDERID_SidebarDefaultParts = NewGUID("{7B396E54-9EC5-4300-BE0A-2482EBAE1A26}")
|
||||
FOLDERID_SidebarParts = NewGUID("{A75D362E-50FC-4fb7-AC2C-A8BEAA314493}")
|
||||
FOLDERID_SkyDrive = NewGUID("{A52BBA46-E9E1-435f-B3D9-28DAA648C0F6}")
|
||||
FOLDERID_SkyDriveCameraRoll = NewGUID("{767E6811-49CB-4273-87C2-20F355E1085B}")
|
||||
FOLDERID_SkyDriveDocuments = NewGUID("{24D89E24-2F19-4534-9DDE-6A6671FBB8FE}")
|
||||
FOLDERID_SkyDriveMusic = NewGUID("{C3F2459E-80D6-45DC-BFEF-1F769F2BE730}")
|
||||
FOLDERID_SkyDrivePictures = NewGUID("{339719B5-8C47-4894-94C2-D8F77ADD44A6}")
|
||||
FOLDERID_StartMenu = NewGUID("{625B53C3-AB48-4EC1-BA1F-A1EF4146FC19}")
|
||||
FOLDERID_Startup = NewGUID("{B97D20BB-F46A-4C97-BA10-5E3608430854}")
|
||||
FOLDERID_SyncManagerFolder = NewGUID("{43668BF8-C14E-49B2-97C9-747784D784B7}")
|
||||
FOLDERID_SyncResultsFolder = NewGUID("{289a9a43-be44-4057-a41b-587a76d7e7f9}")
|
||||
FOLDERID_SyncSetupFolder = NewGUID("{0F214138-B1D3-4a90-BBA9-27CBC0C5389A}")
|
||||
FOLDERID_System = NewGUID("{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}")
|
||||
FOLDERID_SystemX86 = NewGUID("{D65231B0-B2F1-4857-A4CE-A8E7C6EA7D27}")
|
||||
FOLDERID_Templates = NewGUID("{A63293E8-664E-48DB-A079-DF759E0509F7}")
|
||||
FOLDERID_UserPinned = NewGUID("{9E3995AB-1F9C-4F13-B827-48B24B6C7174}")
|
||||
FOLDERID_UserProfiles = NewGUID("{0762D272-C50A-4BB0-A382-697DCD729B80}")
|
||||
FOLDERID_UserProgramFiles = NewGUID("{5CD7AEE2-2219-4A67-B85D-6C9CE15660CB}")
|
||||
FOLDERID_UserProgramFilesCommon = NewGUID("{BCBD3057-CA5C-4622-B42D-BC56DB0AE516}")
|
||||
FOLDERID_UsersFiles = NewGUID("{F3CE0F7C-4901-4ACC-8648-D5D44B04EF8F}")
|
||||
FOLDERID_UsersLibraries = NewGUID("{A302545D-DEFF-464b-ABE8-61C8648D939B}")
|
||||
FOLDERID_Videos = NewGUID("{18989B1D-99B5-455B-841C-AB7C74E4DDFC}")
|
||||
FOLDERID_VideosLibrary = NewGUID("{491E922F-5643-4AF4-A7EB-4E7A138D8174}")
|
||||
FOLDERID_Windows = NewGUID("{F38BF404-1D43-42F2-9305-67DE0B28FC23}")
|
||||
)
|
||||
|
||||
var (
|
||||
modshell32 = syscall.NewLazyDLL("shell32.dll")
|
||||
|
||||
procSHBrowseForFolder = modshell32.NewProc("SHBrowseForFolderW")
|
||||
procSHGetPathFromIDList = modshell32.NewProc("SHGetPathFromIDListW")
|
||||
procDragAcceptFiles = modshell32.NewProc("DragAcceptFiles")
|
||||
procDragQueryFile = modshell32.NewProc("DragQueryFileW")
|
||||
procDragQueryPoint = modshell32.NewProc("DragQueryPoint")
|
||||
procDragFinish = modshell32.NewProc("DragFinish")
|
||||
procShellExecute = modshell32.NewProc("ShellExecuteW")
|
||||
procExtractIcon = modshell32.NewProc("ExtractIconW")
|
||||
procGetSpecialFolderPath = modshell32.NewProc("SHGetSpecialFolderPathW")
|
||||
procShellNotifyIcon = modshell32.NewProc("Shell_NotifyIconW")
|
||||
procShellNotifyIconGetRect = modshell32.NewProc("Shell_NotifyIconGetRect")
|
||||
procSHGetKnownFolderPath = modshell32.NewProc("SHGetKnownFolderPath")
|
||||
procSHAppBarMessage = modshell32.NewProc("SHAppBarMessage")
|
||||
)
|
||||
|
||||
type APPBARDATA struct {
|
||||
CbSize uint32
|
||||
HWnd HWND
|
||||
UCallbackMessage uint32
|
||||
UEdge uint32
|
||||
Rc RECT
|
||||
LParam uintptr
|
||||
}
|
||||
|
||||
func ShellNotifyIcon(cmd uintptr, nid *NOTIFYICONDATA) bool {
|
||||
ret, _, _ := procShellNotifyIcon.Call(cmd, uintptr(unsafe.Pointer(nid)))
|
||||
return ret == 1
|
||||
}
|
||||
|
||||
func SHBrowseForFolder(bi *BROWSEINFO) uintptr {
|
||||
ret, _, _ := procSHBrowseForFolder.Call(uintptr(unsafe.Pointer(bi)))
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func SHGetKnownFolderPath(rfid *GUID, dwFlags uint32, hToken HANDLE) (string, error) {
|
||||
var path *uint16
|
||||
ret, _, _ := procSHGetKnownFolderPath.Call(uintptr(unsafe.Pointer(rfid)), uintptr(dwFlags), hToken, uintptr(unsafe.Pointer(path)))
|
||||
if ret != uintptr(windows.S_OK) {
|
||||
return "", fmt.Errorf("SHGetKnownFolderPath failed: %v", ret)
|
||||
}
|
||||
return windows.UTF16PtrToString(path), nil
|
||||
}
|
||||
|
||||
func SHGetPathFromIDList(idl uintptr) string {
|
||||
buf := make([]uint16, 1024)
|
||||
procSHGetPathFromIDList.Call(
|
||||
idl,
|
||||
uintptr(unsafe.Pointer(&buf[0])))
|
||||
|
||||
return syscall.UTF16ToString(buf)
|
||||
}
|
||||
|
||||
func DragAcceptFiles(hwnd HWND, accept bool) {
|
||||
procDragAcceptFiles.Call(
|
||||
hwnd,
|
||||
uintptr(BoolToBOOL(accept)))
|
||||
}
|
||||
|
||||
func DragQueryFile(hDrop HDROP, iFile uint) (fileName string, fileCount uint) {
|
||||
ret, _, _ := procDragQueryFile.Call(
|
||||
hDrop,
|
||||
uintptr(iFile),
|
||||
0,
|
||||
0)
|
||||
|
||||
fileCount = uint(ret)
|
||||
|
||||
if iFile != 0xFFFFFFFF {
|
||||
buf := make([]uint16, fileCount+1)
|
||||
|
||||
ret, _, _ := procDragQueryFile.Call(
|
||||
hDrop,
|
||||
uintptr(iFile),
|
||||
uintptr(unsafe.Pointer(&buf[0])),
|
||||
uintptr(fileCount+1))
|
||||
|
||||
if ret == 0 {
|
||||
panic("Invoke DragQueryFile error.")
|
||||
}
|
||||
|
||||
fileName = syscall.UTF16ToString(buf)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func DragQueryPoint(hDrop HDROP) (x, y int, isClientArea bool) {
|
||||
var pt POINT
|
||||
ret, _, _ := procDragQueryPoint.Call(
|
||||
uintptr(hDrop),
|
||||
uintptr(unsafe.Pointer(&pt)))
|
||||
|
||||
return int(pt.X), int(pt.Y), (ret == 1)
|
||||
}
|
||||
|
||||
func DragFinish(hDrop HDROP) {
|
||||
procDragFinish.Call(uintptr(hDrop))
|
||||
}
|
||||
|
||||
func ShellExecute(hwnd HWND, lpOperation, lpFile, lpParameters, lpDirectory string, nShowCmd int) error {
|
||||
var op, param, directory uintptr
|
||||
if len(lpOperation) != 0 {
|
||||
op = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpOperation)))
|
||||
}
|
||||
if len(lpParameters) != 0 {
|
||||
param = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpParameters)))
|
||||
}
|
||||
if len(lpDirectory) != 0 {
|
||||
directory = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpDirectory)))
|
||||
}
|
||||
|
||||
ret, _, _ := procShellExecute.Call(
|
||||
uintptr(hwnd),
|
||||
op,
|
||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpFile))),
|
||||
param,
|
||||
directory,
|
||||
uintptr(nShowCmd))
|
||||
|
||||
errorMsg := ""
|
||||
if ret != 0 && ret <= 32 {
|
||||
switch int(ret) {
|
||||
case ERROR_FILE_NOT_FOUND:
|
||||
errorMsg = "The specified file was not found."
|
||||
case ERROR_PATH_NOT_FOUND:
|
||||
errorMsg = "The specified path was not found."
|
||||
case ERROR_BAD_FORMAT:
|
||||
errorMsg = "The .exe file is invalid (non-Win32 .exe or error in .exe image)."
|
||||
case SE_ERR_ACCESSDENIED:
|
||||
errorMsg = "The operating system denied access to the specified file."
|
||||
case SE_ERR_ASSOCINCOMPLETE:
|
||||
errorMsg = "The file name association is incomplete or invalid."
|
||||
case SE_ERR_DDEBUSY:
|
||||
errorMsg = "The DDE transaction could not be completed because other DDE transactions were being processed."
|
||||
case SE_ERR_DDEFAIL:
|
||||
errorMsg = "The DDE transaction failed."
|
||||
case SE_ERR_DDETIMEOUT:
|
||||
errorMsg = "The DDE transaction could not be completed because the request timed out."
|
||||
case SE_ERR_DLLNOTFOUND:
|
||||
errorMsg = "The specified DLL was not found."
|
||||
case SE_ERR_NOASSOC:
|
||||
errorMsg = "There is no application associated with the given file name extension. This error will also be returned if you attempt to print a file that is not printable."
|
||||
case SE_ERR_OOM:
|
||||
errorMsg = "There was not enough memory to complete the operation."
|
||||
case SE_ERR_SHARE:
|
||||
errorMsg = "A sharing violation occurred."
|
||||
default:
|
||||
errorMsg = fmt.Sprintf("Unknown error occurred with error code %v", ret)
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New(errorMsg)
|
||||
}
|
||||
|
||||
func ExtractIcon(lpszExeFileName string, nIconIndex int) HICON {
|
||||
ret, _, _ := procExtractIcon.Call(
|
||||
0,
|
||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpszExeFileName))),
|
||||
uintptr(nIconIndex))
|
||||
|
||||
return HICON(ret)
|
||||
}
|
||||
|
||||
func SHGetSpecialFolderPath(hwndOwner HWND, lpszPath *uint16, csidl CSIDL, fCreate bool) bool {
|
||||
ret, _, _ := procGetSpecialFolderPath.Call(
|
||||
uintptr(hwndOwner),
|
||||
uintptr(unsafe.Pointer(lpszPath)),
|
||||
uintptr(csidl),
|
||||
uintptr(BoolToBOOL(fCreate)),
|
||||
0,
|
||||
0)
|
||||
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func GetSystrayBounds(hwnd HWND, uid uint32) (*RECT, error) {
|
||||
var rect RECT
|
||||
identifier := NOTIFYICONIDENTIFIER{
|
||||
CbSize: uint32(unsafe.Sizeof(NOTIFYICONIDENTIFIER{})),
|
||||
HWnd: hwnd,
|
||||
UId: uid,
|
||||
}
|
||||
ret, _, _ := procShellNotifyIconGetRect.Call(
|
||||
uintptr(unsafe.Pointer(&identifier)),
|
||||
uintptr(unsafe.Pointer(&rect)))
|
||||
|
||||
if ret != S_OK {
|
||||
return nil, syscall.GetLastError()
|
||||
}
|
||||
|
||||
return &rect, nil
|
||||
}
|
||||
|
||||
// GetTaskbarPosition returns the location of the taskbar.
|
||||
func GetTaskbarPosition() *APPBARDATA {
|
||||
var result APPBARDATA
|
||||
result.CbSize = uint32(unsafe.Sizeof(APPBARDATA{}))
|
||||
ret, _, _ := procSHAppBarMessage.Call(
|
||||
ABM_GETTASKBARPOS,
|
||||
uintptr(unsafe.Pointer(&result)))
|
||||
if ret == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &result
|
||||
}
|
||||
26
v3/pkg/w32/shlwapi.go
Normal file
26
v3/pkg/w32/shlwapi.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
//go:build windows
|
||||
|
||||
package w32
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
modshlwapi = syscall.NewLazyDLL("shlwapi.dll")
|
||||
|
||||
procSHCreateMemStream = modshlwapi.NewProc("SHCreateMemStream")
|
||||
)
|
||||
|
||||
func SHCreateMemStream(data []byte) (uintptr, error) {
|
||||
ret, _, err := procSHCreateMemStream.Call(
|
||||
uintptr(unsafe.Pointer(&data[0])),
|
||||
uintptr(len(data)),
|
||||
)
|
||||
if ret == 0 {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
95
v3/pkg/w32/taskbar.go
Normal file
95
v3/pkg/w32/taskbar.go
Normal 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()
|
||||
}
|
||||
}
|
||||
345
v3/pkg/w32/theme.go
Normal file
345
v3/pkg/w32/theme.go
Normal file
|
|
@ -0,0 +1,345 @@
|
|||
//go:build windows
|
||||
|
||||
package w32
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
type DWMWINDOWATTRIBUTE int32
|
||||
|
||||
const DwmwaUseImmersiveDarkModeBefore20h1 DWMWINDOWATTRIBUTE = 19
|
||||
const DwmwaUseImmersiveDarkMode DWMWINDOWATTRIBUTE = 20
|
||||
const DwmwaBorderColor DWMWINDOWATTRIBUTE = 34
|
||||
const DwmwaCaptionColor DWMWINDOWATTRIBUTE = 35
|
||||
const DwmwaTextColor DWMWINDOWATTRIBUTE = 36
|
||||
const DwmwaSystemBackdropType DWMWINDOWATTRIBUTE = 38
|
||||
|
||||
const SPI_GETHIGHCONTRAST = 0x0042
|
||||
const HCF_HIGHCONTRASTON = 0x00000001
|
||||
|
||||
type WINDOWCOMPOSITIONATTRIB DWORD
|
||||
|
||||
type HTHEME HANDLE
|
||||
|
||||
const (
|
||||
WCA_UNDEFINED WINDOWCOMPOSITIONATTRIB = 0
|
||||
WCA_NCRENDERING_ENABLED WINDOWCOMPOSITIONATTRIB = 1
|
||||
WCA_NCRENDERING_POLICY WINDOWCOMPOSITIONATTRIB = 2
|
||||
WCA_TRANSITIONS_FORCEDISABLED WINDOWCOMPOSITIONATTRIB = 3
|
||||
WCA_ALLOW_NCPAINT WINDOWCOMPOSITIONATTRIB = 4
|
||||
WCA_CAPTION_BUTTON_BOUNDS WINDOWCOMPOSITIONATTRIB = 5
|
||||
WCA_NONCLIENT_RTL_LAYOUT WINDOWCOMPOSITIONATTRIB = 6
|
||||
WCA_FORCE_ICONIC_REPRESENTATION WINDOWCOMPOSITIONATTRIB = 7
|
||||
WCA_EXTENDED_FRAME_BOUNDS WINDOWCOMPOSITIONATTRIB = 8
|
||||
WCA_HAS_ICONIC_BITMAP WINDOWCOMPOSITIONATTRIB = 9
|
||||
WCA_THEME_ATTRIBUTES WINDOWCOMPOSITIONATTRIB = 10
|
||||
WCA_NCRENDERING_EXILED WINDOWCOMPOSITIONATTRIB = 11
|
||||
WCA_NCADORNMENTINFO WINDOWCOMPOSITIONATTRIB = 12
|
||||
WCA_EXCLUDED_FROM_LIVEPREVIEW WINDOWCOMPOSITIONATTRIB = 13
|
||||
WCA_VIDEO_OVERLAY_ACTIVE WINDOWCOMPOSITIONATTRIB = 14
|
||||
WCA_FORCE_ACTIVEWINDOW_APPEARANCE WINDOWCOMPOSITIONATTRIB = 15
|
||||
WCA_DISALLOW_PEEK WINDOWCOMPOSITIONATTRIB = 16
|
||||
WCA_CLOAK WINDOWCOMPOSITIONATTRIB = 17
|
||||
WCA_CLOAKED WINDOWCOMPOSITIONATTRIB = 18
|
||||
WCA_ACCENT_POLICY WINDOWCOMPOSITIONATTRIB = 19
|
||||
WCA_FREEZE_REPRESENTATION WINDOWCOMPOSITIONATTRIB = 20
|
||||
WCA_EVER_UNCLOAKED WINDOWCOMPOSITIONATTRIB = 21
|
||||
WCA_VISUAL_OWNER WINDOWCOMPOSITIONATTRIB = 22
|
||||
WCA_HOLOGRAPHIC WINDOWCOMPOSITIONATTRIB = 23
|
||||
WCA_EXCLUDED_FROM_DDA WINDOWCOMPOSITIONATTRIB = 24
|
||||
WCA_PASSIVEUPDATEMODE WINDOWCOMPOSITIONATTRIB = 25
|
||||
WCA_USEDARKMODECOLORS WINDOWCOMPOSITIONATTRIB = 26
|
||||
WCA_CORNER_STYLE WINDOWCOMPOSITIONATTRIB = 27
|
||||
WCA_PART_COLOR WINDOWCOMPOSITIONATTRIB = 28
|
||||
WCA_DISABLE_MOVESIZE_FEEDBACK WINDOWCOMPOSITIONATTRIB = 29
|
||||
WCA_LAST WINDOWCOMPOSITIONATTRIB = 30
|
||||
)
|
||||
|
||||
type WINDOWCOMPOSITIONATTRIBDATA struct {
|
||||
Attrib WINDOWCOMPOSITIONATTRIB
|
||||
PvData unsafe.Pointer
|
||||
CbData uintptr
|
||||
}
|
||||
|
||||
var (
|
||||
uxtheme = syscall.NewLazyDLL("uxtheme.dll")
|
||||
procSetWindowTheme = uxtheme.NewProc("SetWindowTheme")
|
||||
procOpenThemeData = uxtheme.NewProc("OpenThemeData")
|
||||
procCloseThemeData = uxtheme.NewProc("CloseThemeData")
|
||||
procDrawThemeBackground = uxtheme.NewProc("DrawThemeBackground")
|
||||
procAllowDarkModeForApplication = uxtheme.NewProc("AllowDarkModeForApp")
|
||||
procDrawThemeTextEx = uxtheme.NewProc("DrawThemeTextEx")
|
||||
)
|
||||
|
||||
type PreferredAppMode = int32
|
||||
|
||||
const (
|
||||
PreferredAppModeDefault PreferredAppMode = iota
|
||||
PreferredAppModeAllowDark
|
||||
PreferredAppModeForceDark
|
||||
PreferredAppModeForceLight
|
||||
PreferredAppModeMax
|
||||
)
|
||||
|
||||
var (
|
||||
AllowDarkModeForWindow func(hwnd HWND, allow bool) uintptr
|
||||
SetPreferredAppMode func(mode int32) uintptr
|
||||
FlushMenuThemes func()
|
||||
RefreshImmersiveColorPolicyState func()
|
||||
ShouldAppsUseDarkMode func() bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
if IsWindowsVersionAtLeast(10, 0, 18334) {
|
||||
// AllowDarkModeForWindow is only available on Windows 10+
|
||||
localUXTheme, err := windows.LoadLibrary("uxtheme.dll")
|
||||
if err == nil {
|
||||
procAllowDarkModeForWindow, err := windows.GetProcAddressByOrdinal(localUXTheme, uintptr(133))
|
||||
if err == nil {
|
||||
AllowDarkModeForWindow = func(hwnd HWND, allow bool) uintptr {
|
||||
var allowInt int32
|
||||
if allow {
|
||||
allowInt = 1
|
||||
}
|
||||
ret, _, _ := syscall.SyscallN(procAllowDarkModeForWindow, uintptr(allowInt))
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
// Add ShouldAppsUseDarkMode
|
||||
procShouldAppsUseDarkMode, err := windows.GetProcAddressByOrdinal(localUXTheme, uintptr(132))
|
||||
if err == nil {
|
||||
ShouldAppsUseDarkMode = func() bool {
|
||||
ret, _, _ := syscall.SyscallN(procShouldAppsUseDarkMode)
|
||||
return ret != 0
|
||||
}
|
||||
}
|
||||
|
||||
// SetPreferredAppMode is only available on Windows 10+
|
||||
procSetPreferredAppMode, err := windows.GetProcAddressByOrdinal(localUXTheme, uintptr(135))
|
||||
if err == nil {
|
||||
SetPreferredAppMode = func(mode int32) uintptr {
|
||||
ret, _, _ := syscall.SyscallN(procSetPreferredAppMode, uintptr(mode))
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
// Add FlushMenuThemes
|
||||
procFlushMenuThemesAddr, err := windows.GetProcAddressByOrdinal(localUXTheme, uintptr(136))
|
||||
if err == nil {
|
||||
FlushMenuThemes = func() {
|
||||
syscall.SyscallN(procFlushMenuThemesAddr)
|
||||
}
|
||||
}
|
||||
|
||||
// Add RefreshImmersiveColorPolicyState
|
||||
procRefreshImmersiveColorPolicyStateAddr, err := windows.GetProcAddressByOrdinal(localUXTheme, uintptr(104))
|
||||
if err == nil {
|
||||
RefreshImmersiveColorPolicyState = func() {
|
||||
syscall.SyscallN(procRefreshImmersiveColorPolicyStateAddr)
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize dark mode
|
||||
if SetPreferredAppMode != nil {
|
||||
SetPreferredAppMode(PreferredAppModeAllowDark)
|
||||
if RefreshImmersiveColorPolicyState != nil {
|
||||
RefreshImmersiveColorPolicyState()
|
||||
}
|
||||
}
|
||||
|
||||
windows.FreeLibrary(localUXTheme)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func dwmSetWindowAttribute(hwnd uintptr, dwAttribute DWMWINDOWATTRIBUTE, pvAttribute unsafe.Pointer, cbAttribute uintptr) {
|
||||
ret, _, err := procDwmSetWindowAttribute.Call(
|
||||
hwnd,
|
||||
uintptr(dwAttribute),
|
||||
uintptr(pvAttribute),
|
||||
cbAttribute)
|
||||
if ret != 0 {
|
||||
_ = err
|
||||
// println(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func SupportsThemes() bool {
|
||||
// We can't support Windows versions before 17763
|
||||
return IsWindowsVersionAtLeast(10, 0, 17763)
|
||||
}
|
||||
|
||||
func SupportsCustomThemes() bool {
|
||||
return IsWindowsVersionAtLeast(10, 0, 17763)
|
||||
}
|
||||
|
||||
func SupportsBackdropTypes() bool {
|
||||
return IsWindowsVersionAtLeast(10, 0, 22621)
|
||||
}
|
||||
|
||||
func SupportsImmersiveDarkMode() bool {
|
||||
return IsWindowsVersionAtLeast(10, 0, 18985)
|
||||
}
|
||||
|
||||
func SetMenuTheme(hwnd uintptr, useDarkMode bool) {
|
||||
if !SupportsThemes() {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if dark mode is supported and enabled
|
||||
if useDarkMode && ShouldAppsUseDarkMode != nil && !ShouldAppsUseDarkMode() {
|
||||
useDarkMode = false
|
||||
}
|
||||
|
||||
// Set the window theme
|
||||
themeName := "Explorer"
|
||||
if useDarkMode {
|
||||
themeName = "DarkMode_Explorer"
|
||||
}
|
||||
procSetWindowTheme.Call(HWND(hwnd), uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(themeName))), 0)
|
||||
|
||||
// Update the theme state
|
||||
if RefreshImmersiveColorPolicyState != nil {
|
||||
RefreshImmersiveColorPolicyState()
|
||||
}
|
||||
|
||||
// Flush menu themes to force a refresh
|
||||
if FlushMenuThemes != nil {
|
||||
FlushMenuThemes()
|
||||
}
|
||||
|
||||
// Set dark mode for the window
|
||||
if AllowDarkModeForWindow != nil {
|
||||
AllowDarkModeForWindow(HWND(hwnd), useDarkMode)
|
||||
}
|
||||
|
||||
// Force a redraw
|
||||
InvalidateRect(HWND(hwnd), nil, true)
|
||||
}
|
||||
|
||||
func SetTheme(hwnd uintptr, useDarkMode bool) {
|
||||
if SupportsThemes() {
|
||||
attr := DwmwaUseImmersiveDarkModeBefore20h1
|
||||
if SupportsImmersiveDarkMode() {
|
||||
attr = DwmwaUseImmersiveDarkMode
|
||||
}
|
||||
var winDark int32
|
||||
if useDarkMode {
|
||||
winDark = 1
|
||||
}
|
||||
dwmSetWindowAttribute(hwnd, attr, unsafe.Pointer(&winDark), unsafe.Sizeof(winDark))
|
||||
SetMenuTheme(hwnd, useDarkMode)
|
||||
}
|
||||
}
|
||||
|
||||
func EnableTranslucency(hwnd uintptr, backdrop uint32) {
|
||||
dwmSetWindowAttribute(hwnd, DwmwaSystemBackdropType, unsafe.Pointer(&backdrop), unsafe.Sizeof(backdrop))
|
||||
}
|
||||
|
||||
func SetTitleBarColour(hwnd uintptr, titleBarColour uint32) {
|
||||
// Debug: Print the color value being set
|
||||
// fmt.Printf("Setting titlebar color to: 0x%08X (RGB: %d, %d, %d)\n", titleBarColour, titleBarColour&0xFF, (titleBarColour>>8)&0xFF, (titleBarColour>>16)&0xFF)
|
||||
dwmSetWindowAttribute(hwnd, DwmwaCaptionColor, unsafe.Pointer(&titleBarColour), unsafe.Sizeof(titleBarColour))
|
||||
}
|
||||
|
||||
func SetTitleTextColour(hwnd uintptr, titleTextColour uint32) {
|
||||
dwmSetWindowAttribute(hwnd, DwmwaTextColor, unsafe.Pointer(&titleTextColour), unsafe.Sizeof(titleTextColour))
|
||||
}
|
||||
|
||||
func SetBorderColour(hwnd uintptr, titleBorderColour uint32) {
|
||||
dwmSetWindowAttribute(hwnd, DwmwaBorderColor, unsafe.Pointer(&titleBorderColour), unsafe.Sizeof(titleBorderColour))
|
||||
}
|
||||
|
||||
func IsCurrentlyDarkMode() bool {
|
||||
key, err := registry.OpenKey(registry.CURRENT_USER, `SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize`, registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer key.Close()
|
||||
|
||||
AppsUseLightTheme, _, err := key.GetIntegerValue("AppsUseLightTheme")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return AppsUseLightTheme == 0
|
||||
}
|
||||
|
||||
type highContrast struct {
|
||||
CbSize uint32
|
||||
DwFlags uint32
|
||||
LpszDefaultScheme *int16
|
||||
}
|
||||
|
||||
func IsCurrentlyHighContrastMode() bool {
|
||||
var result highContrast
|
||||
result.CbSize = uint32(unsafe.Sizeof(result))
|
||||
res, _, err := procSystemParametersInfo.Call(SPI_GETHIGHCONTRAST, uintptr(result.CbSize), uintptr(unsafe.Pointer(&result)), 0)
|
||||
if res == 0 {
|
||||
_ = err
|
||||
return false
|
||||
}
|
||||
r := result.DwFlags&HCF_HIGHCONTRASTON == HCF_HIGHCONTRASTON
|
||||
return r
|
||||
}
|
||||
|
||||
func GetAccentColor() (string, error) {
|
||||
key, err := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\DWM`, registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer key.Close()
|
||||
|
||||
accentColor, _, err := key.GetIntegerValue("AccentColor")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Extract RGB components from ABGR format (Alpha, Blue, Green, Red)
|
||||
red := uint8(accentColor & 0xFF)
|
||||
green := uint8((accentColor >> 8) & 0xFF)
|
||||
blue := uint8((accentColor >> 16) & 0xFF)
|
||||
|
||||
return fmt.Sprintf("rgb(%d,%d,%d)", red, green, blue), nil
|
||||
}
|
||||
|
||||
// OpenThemeData opens theme data for a window and its class
|
||||
func OpenThemeData(hwnd HWND, pszClassList string) HTHEME {
|
||||
ret, _, _ := procOpenThemeData.Call(
|
||||
uintptr(hwnd),
|
||||
uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(pszClassList))))
|
||||
return HTHEME(ret)
|
||||
}
|
||||
|
||||
// CloseThemeData closes theme data handle
|
||||
func CloseThemeData(hTheme HTHEME) error {
|
||||
ret, _, err := procCloseThemeData.Call(uintptr(hTheme))
|
||||
if ret != 0 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DrawThemeTextEx draws theme text with extended options
|
||||
func DrawThemeTextEx(hTheme HTHEME, hdc HDC, iPartId int32, iStateId int32, pszText []uint16, cchText int32, dwTextFlags uint32, pRect *RECT, pOptions *DTTOPTS) error {
|
||||
ret, _, err := procDrawThemeTextEx.Call(
|
||||
uintptr(hTheme),
|
||||
uintptr(hdc),
|
||||
uintptr(iPartId),
|
||||
uintptr(iStateId),
|
||||
uintptr(unsafe.Pointer(&pszText[0])),
|
||||
uintptr(cchText),
|
||||
uintptr(dwTextFlags),
|
||||
uintptr(unsafe.Pointer(pRect)),
|
||||
uintptr(unsafe.Pointer(pOptions)))
|
||||
if ret != 0 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
19
v3/pkg/w32/timer.go
Normal file
19
v3/pkg/w32/timer.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
//go:build windows
|
||||
|
||||
package w32
|
||||
|
||||
func SetTimer(hwnd HWND, nIDEvent uintptr, uElapse uint32, lpTimerFunc uintptr) uintptr {
|
||||
ret, _, _ := procSetTimer.Call(
|
||||
uintptr(hwnd),
|
||||
nIDEvent,
|
||||
uintptr(uElapse),
|
||||
lpTimerFunc)
|
||||
return ret
|
||||
}
|
||||
|
||||
func KillTimer(hwnd HWND, nIDEvent uintptr) bool {
|
||||
ret, _, _ := procKillTimer.Call(
|
||||
uintptr(hwnd),
|
||||
nIDEvent)
|
||||
return ret != 0
|
||||
}
|
||||
216
v3/pkg/w32/toolbar.go
Normal file
216
v3/pkg/w32/toolbar.go
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
//go:build windows
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Tad Vizbaras. All Rights Reserved.
|
||||
* Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package w32
|
||||
|
||||
// ToolBar messages
|
||||
const (
|
||||
TB_ENABLEBUTTON = WM_USER + 1
|
||||
TB_CHECKBUTTON = WM_USER + 2
|
||||
TB_PRESSBUTTON = WM_USER + 3
|
||||
TB_HIDEBUTTON = WM_USER + 4
|
||||
TB_INDETERMINATE = WM_USER + 5
|
||||
TB_MARKBUTTON = WM_USER + 6
|
||||
TB_ISBUTTONENABLED = WM_USER + 9
|
||||
TB_ISBUTTONCHECKED = WM_USER + 10
|
||||
TB_ISBUTTONPRESSED = WM_USER + 11
|
||||
TB_ISBUTTONHIDDEN = WM_USER + 12
|
||||
TB_ISBUTTONINDETERMINATE = WM_USER + 13
|
||||
TB_ISBUTTONHIGHLIGHTED = WM_USER + 14
|
||||
TB_SETSTATE = WM_USER + 17
|
||||
TB_GETSTATE = WM_USER + 18
|
||||
TB_ADDBITMAP = WM_USER + 19
|
||||
TB_DELETEBUTTON = WM_USER + 22
|
||||
TB_GETBUTTON = WM_USER + 23
|
||||
TB_BUTTONCOUNT = WM_USER + 24
|
||||
TB_COMMANDTOINDEX = WM_USER + 25
|
||||
TB_SAVERESTORE = WM_USER + 76
|
||||
TB_CUSTOMIZE = WM_USER + 27
|
||||
TB_ADDSTRING = WM_USER + 77
|
||||
TB_GETITEMRECT = WM_USER + 29
|
||||
TB_BUTTONSTRUCTSIZE = WM_USER + 30
|
||||
TB_SETBUTTONSIZE = WM_USER + 31
|
||||
TB_SETBITMAPSIZE = WM_USER + 32
|
||||
TB_AUTOSIZE = WM_USER + 33
|
||||
TB_GETTOOLTIPS = WM_USER + 35
|
||||
TB_SETTOOLTIPS = WM_USER + 36
|
||||
TB_SETPARENT = WM_USER + 37
|
||||
TB_SETROWS = WM_USER + 39
|
||||
TB_GETROWS = WM_USER + 40
|
||||
TB_GETBITMAPFLAGS = WM_USER + 41
|
||||
TB_SETCMDID = WM_USER + 42
|
||||
TB_CHANGEBITMAP = WM_USER + 43
|
||||
TB_GETBITMAP = WM_USER + 44
|
||||
TB_GETBUTTONTEXT = WM_USER + 75
|
||||
TB_REPLACEBITMAP = WM_USER + 46
|
||||
TB_GETBUTTONSIZE = WM_USER + 58
|
||||
TB_SETBUTTONWIDTH = WM_USER + 59
|
||||
TB_SETINDENT = WM_USER + 47
|
||||
TB_SETIMAGELIST = WM_USER + 48
|
||||
TB_GETIMAGELIST = WM_USER + 49
|
||||
TB_LOADIMAGES = WM_USER + 50
|
||||
TB_GETRECT = WM_USER + 51
|
||||
TB_SETHOTIMAGELIST = WM_USER + 52
|
||||
TB_GETHOTIMAGELIST = WM_USER + 53
|
||||
TB_SETDISABLEDIMAGELIST = WM_USER + 54
|
||||
TB_GETDISABLEDIMAGELIST = WM_USER + 55
|
||||
TB_SETSTYLE = WM_USER + 56
|
||||
TB_GETSTYLE = WM_USER + 57
|
||||
TB_SETMAXTEXTROWS = WM_USER + 60
|
||||
TB_GETTEXTROWS = WM_USER + 61
|
||||
TB_GETOBJECT = WM_USER + 62
|
||||
TB_GETBUTTONINFO = WM_USER + 63
|
||||
TB_SETBUTTONINFO = WM_USER + 64
|
||||
TB_INSERTBUTTON = WM_USER + 67
|
||||
TB_ADDBUTTONS = WM_USER + 68
|
||||
TB_HITTEST = WM_USER + 69
|
||||
TB_SETDRAWTEXTFLAGS = WM_USER + 70
|
||||
TB_GETHOTITEM = WM_USER + 71
|
||||
TB_SETHOTITEM = WM_USER + 72
|
||||
TB_SETANCHORHIGHLIGHT = WM_USER + 73
|
||||
TB_GETANCHORHIGHLIGHT = WM_USER + 74
|
||||
TB_GETINSERTMARK = WM_USER + 79
|
||||
TB_SETINSERTMARK = WM_USER + 80
|
||||
TB_INSERTMARKHITTEST = WM_USER + 81
|
||||
TB_MOVEBUTTON = WM_USER + 82
|
||||
TB_GETMAXSIZE = WM_USER + 83
|
||||
TB_SETEXTENDEDSTYLE = WM_USER + 84
|
||||
TB_GETEXTENDEDSTYLE = WM_USER + 85
|
||||
TB_GETPADDING = WM_USER + 86
|
||||
TB_SETPADDING = WM_USER + 87
|
||||
TB_SETINSERTMARKCOLOR = WM_USER + 88
|
||||
TB_GETINSERTMARKCOLOR = WM_USER + 89
|
||||
TB_MAPACCELERATOR = WM_USER + 90
|
||||
TB_GETSTRING = WM_USER + 91
|
||||
TB_SETCOLORSCHEME = CCM_SETCOLORSCHEME
|
||||
TB_GETCOLORSCHEME = CCM_GETCOLORSCHEME
|
||||
TB_SETUNICODEFORMAT = CCM_SETUNICODEFORMAT
|
||||
TB_GETUNICODEFORMAT = CCM_GETUNICODEFORMAT
|
||||
)
|
||||
|
||||
// ToolBar notifications
|
||||
const (
|
||||
TBN_FIRST = -700
|
||||
TBN_DROPDOWN = TBN_FIRST - 10
|
||||
)
|
||||
|
||||
// TBN_DROPDOWN return codes
|
||||
const (
|
||||
TBDDRET_DEFAULT = 0
|
||||
TBDDRET_NODEFAULT = 1
|
||||
TBDDRET_TREATPRESSED = 2
|
||||
)
|
||||
|
||||
// ToolBar state constants
|
||||
const (
|
||||
TBSTATE_CHECKED = 1
|
||||
TBSTATE_PRESSED = 2
|
||||
TBSTATE_ENABLED = 4
|
||||
TBSTATE_HIDDEN = 8
|
||||
TBSTATE_INDETERMINATE = 16
|
||||
TBSTATE_WRAP = 32
|
||||
TBSTATE_ELLIPSES = 0x40
|
||||
TBSTATE_MARKED = 0x0080
|
||||
)
|
||||
|
||||
// ToolBar style constants
|
||||
const (
|
||||
TBSTYLE_BUTTON = 0
|
||||
TBSTYLE_SEP = 1
|
||||
TBSTYLE_CHECK = 2
|
||||
TBSTYLE_GROUP = 4
|
||||
TBSTYLE_CHECKGROUP = TBSTYLE_GROUP | TBSTYLE_CHECK
|
||||
TBSTYLE_DROPDOWN = 8
|
||||
TBSTYLE_AUTOSIZE = 16
|
||||
TBSTYLE_NOPREFIX = 32
|
||||
TBSTYLE_TOOLTIPS = 256
|
||||
TBSTYLE_WRAPABLE = 512
|
||||
TBSTYLE_ALTDRAG = 1024
|
||||
TBSTYLE_FLAT = 2048
|
||||
TBSTYLE_LIST = 4096
|
||||
TBSTYLE_CUSTOMERASE = 8192
|
||||
TBSTYLE_REGISTERDROP = 0x4000
|
||||
TBSTYLE_TRANSPARENT = 0x8000
|
||||
)
|
||||
|
||||
// ToolBar extended style constants
|
||||
const (
|
||||
TBSTYLE_EX_DRAWDDARROWS = 0x00000001
|
||||
TBSTYLE_EX_MIXEDBUTTONS = 8
|
||||
TBSTYLE_EX_HIDECLIPPEDBUTTONS = 16
|
||||
TBSTYLE_EX_DOUBLEBUFFER = 0x80
|
||||
)
|
||||
|
||||
// ToolBar button style constants
|
||||
const (
|
||||
BTNS_BUTTON = TBSTYLE_BUTTON
|
||||
BTNS_SEP = TBSTYLE_SEP
|
||||
BTNS_CHECK = TBSTYLE_CHECK
|
||||
BTNS_GROUP = TBSTYLE_GROUP
|
||||
BTNS_CHECKGROUP = TBSTYLE_CHECKGROUP
|
||||
BTNS_DROPDOWN = TBSTYLE_DROPDOWN
|
||||
BTNS_AUTOSIZE = TBSTYLE_AUTOSIZE
|
||||
BTNS_NOPREFIX = TBSTYLE_NOPREFIX
|
||||
BTNS_WHOLEDROPDOWN = 0x0080
|
||||
BTNS_SHOWTEXT = 0x0040
|
||||
)
|
||||
|
||||
// TBBUTTONINFO mask flags
|
||||
const (
|
||||
TBIF_IMAGE = 0x00000001
|
||||
TBIF_TEXT = 0x00000002
|
||||
TBIF_STATE = 0x00000004
|
||||
TBIF_STYLE = 0x00000008
|
||||
TBIF_LPARAM = 0x00000010
|
||||
TBIF_COMMAND = 0x00000020
|
||||
TBIF_SIZE = 0x00000040
|
||||
TBIF_BYINDEX = 0x80000000
|
||||
)
|
||||
|
||||
type NMMOUSE struct {
|
||||
Hdr NMHDR
|
||||
DwItemSpec uintptr
|
||||
DwItemData uintptr
|
||||
Pt POINT
|
||||
DwHitInfo uintptr
|
||||
}
|
||||
|
||||
type NMTOOLBAR struct {
|
||||
Hdr NMHDR
|
||||
IItem int32
|
||||
TbButton TBBUTTON
|
||||
CchText int32
|
||||
PszText *uint16
|
||||
RcButton RECT
|
||||
}
|
||||
|
||||
type TBBUTTON struct {
|
||||
IBitmap int32
|
||||
IdCommand int32
|
||||
FsState byte
|
||||
FsStyle byte
|
||||
//#ifdef _WIN64
|
||||
// BYTE bReserved[6] // padding for alignment
|
||||
//#elif defined(_WIN32)
|
||||
BReserved [2]byte // padding for alignment
|
||||
//#endif
|
||||
DwData uintptr
|
||||
IString uintptr
|
||||
}
|
||||
|
||||
type TBBUTTONINFO struct {
|
||||
CbSize uint32
|
||||
DwMask uint32
|
||||
IdCommand int32
|
||||
IImage int32
|
||||
FsState byte
|
||||
FsStyle byte
|
||||
Cx uint16
|
||||
LParam uintptr
|
||||
PszText uintptr
|
||||
CchText int32
|
||||
}
|
||||
1110
v3/pkg/w32/typedef.go
Normal file
1110
v3/pkg/w32/typedef.go
Normal file
File diff suppressed because it is too large
Load diff
1510
v3/pkg/w32/user32.go
Normal file
1510
v3/pkg/w32/user32.go
Normal file
File diff suppressed because it is too large
Load diff
637
v3/pkg/w32/utils.go
Normal file
637
v3/pkg/w32/utils.go
Normal file
|
|
@ -0,0 +1,637 @@
|
|||
//go:build windows
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Tad Vizbaras. All Rights Reserved.
|
||||
* Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package w32
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func MustLoadLibrary(name string) uintptr {
|
||||
lib, err := syscall.LoadLibrary(name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return uintptr(lib)
|
||||
}
|
||||
|
||||
func MustGetProcAddress(lib uintptr, name string) uintptr {
|
||||
addr, err := syscall.GetProcAddress(syscall.Handle(lib), name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return uintptr(addr)
|
||||
}
|
||||
|
||||
func SUCCEEDED(hr HRESULT) bool {
|
||||
return hr >= 0
|
||||
}
|
||||
|
||||
func FAILED(hr HRESULT) bool {
|
||||
return hr < 0
|
||||
}
|
||||
|
||||
func LOWORD(dw uint32) uint16 {
|
||||
return uint16(dw)
|
||||
}
|
||||
|
||||
func HIWORD(dw uint32) uint16 {
|
||||
return uint16(dw >> 16 & 0xffff)
|
||||
}
|
||||
|
||||
func MAKELONG(lo, hi uint16) uint32 {
|
||||
return uint32(uint32(lo) | ((uint32(hi)) << 16))
|
||||
}
|
||||
|
||||
func BoolToBOOL(value bool) BOOL {
|
||||
if value {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func UTF16PtrToString(cstr *uint16) string {
|
||||
if cstr != nil {
|
||||
us := make([]uint16, 0, 256)
|
||||
for p := uintptr(unsafe.Pointer(cstr)); ; p += 2 {
|
||||
u := *(*uint16)(unsafe.Pointer(p))
|
||||
if u == 0 {
|
||||
return string(utf16.Decode(us))
|
||||
}
|
||||
us = append(us, u)
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func ComAddRef(unknown *IUnknown) int32 {
|
||||
ret, _, _ := syscall.SyscallN(uintptr(unknown.Vtbl.AddRef),
|
||||
uintptr(unsafe.Pointer(unknown)),
|
||||
0,
|
||||
0)
|
||||
return int32(ret)
|
||||
}
|
||||
|
||||
func ComRelease(unknown *IUnknown) int32 {
|
||||
ret, _, _ := syscall.SyscallN(uintptr(unknown.Vtbl.Release),
|
||||
uintptr(unsafe.Pointer(unknown)),
|
||||
0,
|
||||
0)
|
||||
return int32(ret)
|
||||
}
|
||||
|
||||
func ComQueryInterface(unknown *IUnknown, id *GUID) *IDispatch {
|
||||
var disp *IDispatch
|
||||
hr, _, _ := syscall.SyscallN(uintptr(unknown.Vtbl.QueryInterface),
|
||||
uintptr(unsafe.Pointer(unknown)),
|
||||
uintptr(unsafe.Pointer(id)),
|
||||
uintptr(unsafe.Pointer(&disp)))
|
||||
if hr != 0 {
|
||||
panic("Invoke QieryInterface error.")
|
||||
}
|
||||
return disp
|
||||
}
|
||||
|
||||
func ComGetIDsOfName(disp *IDispatch, names []string) []int32 {
|
||||
wnames := make([]*uint16, len(names))
|
||||
dispid := make([]int32, len(names))
|
||||
for i := 0; i < len(names); i++ {
|
||||
wnames[i] = syscall.StringToUTF16Ptr(names[i])
|
||||
}
|
||||
hr, _, _ := syscall.SyscallN(disp.lpVtbl.pGetIDsOfNames,
|
||||
uintptr(unsafe.Pointer(disp)),
|
||||
uintptr(unsafe.Pointer(IID_NULL)),
|
||||
uintptr(unsafe.Pointer(&wnames[0])),
|
||||
uintptr(len(names)),
|
||||
uintptr(GetUserDefaultLCID()),
|
||||
uintptr(unsafe.Pointer(&dispid[0])))
|
||||
if hr != 0 {
|
||||
panic("Invoke GetIDsOfName error.")
|
||||
}
|
||||
return dispid
|
||||
}
|
||||
|
||||
func ComInvoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{}) (result *VARIANT) {
|
||||
var dispparams DISPPARAMS
|
||||
|
||||
if dispatch&DISPATCH_PROPERTYPUT != 0 {
|
||||
dispnames := [1]int32{DISPID_PROPERTYPUT}
|
||||
dispparams.RgdispidNamedArgs = uintptr(unsafe.Pointer(&dispnames[0]))
|
||||
dispparams.CNamedArgs = 1
|
||||
}
|
||||
var vargs []VARIANT
|
||||
if len(params) > 0 {
|
||||
vargs = make([]VARIANT, len(params))
|
||||
for i, v := range params {
|
||||
//n := len(params)-i-1
|
||||
n := len(params) - i - 1
|
||||
VariantInit(&vargs[n])
|
||||
switch v.(type) {
|
||||
case bool:
|
||||
if v.(bool) {
|
||||
vargs[n] = VARIANT{VT_BOOL, 0, 0, 0, 0xffff}
|
||||
} else {
|
||||
vargs[n] = VARIANT{VT_BOOL, 0, 0, 0, 0}
|
||||
}
|
||||
case *bool:
|
||||
vargs[n] = VARIANT{VT_BOOL | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*bool))))}
|
||||
case byte:
|
||||
vargs[n] = VARIANT{VT_I1, 0, 0, 0, int64(v.(byte))}
|
||||
case *byte:
|
||||
vargs[n] = VARIANT{VT_I1 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*byte))))}
|
||||
case int16:
|
||||
vargs[n] = VARIANT{VT_I2, 0, 0, 0, int64(v.(int16))}
|
||||
case *int16:
|
||||
vargs[n] = VARIANT{VT_I2 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*int16))))}
|
||||
case uint16:
|
||||
vargs[n] = VARIANT{VT_UI2, 0, 0, 0, int64(v.(int16))}
|
||||
case *uint16:
|
||||
vargs[n] = VARIANT{VT_UI2 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*uint16))))}
|
||||
case int, int32:
|
||||
vargs[n] = VARIANT{VT_UI4, 0, 0, 0, int64(v.(int))}
|
||||
case *int, *int32:
|
||||
vargs[n] = VARIANT{VT_I4 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*int))))}
|
||||
case uint, uint32:
|
||||
vargs[n] = VARIANT{VT_UI4, 0, 0, 0, int64(v.(uint))}
|
||||
case *uint, *uint32:
|
||||
vargs[n] = VARIANT{VT_UI4 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*uint))))}
|
||||
case int64:
|
||||
vargs[n] = VARIANT{VT_I8, 0, 0, 0, v.(int64)}
|
||||
case *int64:
|
||||
vargs[n] = VARIANT{VT_I8 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*int64))))}
|
||||
case uint64:
|
||||
vargs[n] = VARIANT{VT_UI8, 0, 0, 0, int64(v.(uint64))}
|
||||
case *uint64:
|
||||
vargs[n] = VARIANT{VT_UI8 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*uint64))))}
|
||||
case float32:
|
||||
vargs[n] = VARIANT{VT_R4, 0, 0, 0, int64(v.(float32))}
|
||||
case *float32:
|
||||
vargs[n] = VARIANT{VT_R4 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*float32))))}
|
||||
case float64:
|
||||
vargs[n] = VARIANT{VT_R8, 0, 0, 0, int64(v.(float64))}
|
||||
case *float64:
|
||||
vargs[n] = VARIANT{VT_R8 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*float64))))}
|
||||
case string:
|
||||
vargs[n] = VARIANT{VT_BSTR, 0, 0, 0, int64(uintptr(unsafe.Pointer(SysAllocString(v.(string)))))}
|
||||
case *string:
|
||||
vargs[n] = VARIANT{VT_BSTR | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*string))))}
|
||||
case *IDispatch:
|
||||
vargs[n] = VARIANT{VT_DISPATCH, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*IDispatch))))}
|
||||
case **IDispatch:
|
||||
vargs[n] = VARIANT{VT_DISPATCH | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(**IDispatch))))}
|
||||
case nil:
|
||||
vargs[n] = VARIANT{VT_NULL, 0, 0, 0, 0}
|
||||
case *VARIANT:
|
||||
vargs[n] = VARIANT{VT_VARIANT | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*VARIANT))))}
|
||||
default:
|
||||
panic("unknown type")
|
||||
}
|
||||
}
|
||||
dispparams.Rgvarg = uintptr(unsafe.Pointer(&vargs[0]))
|
||||
dispparams.CArgs = uint32(len(params))
|
||||
}
|
||||
|
||||
var ret VARIANT
|
||||
var excepInfo EXCEPINFO
|
||||
VariantInit(&ret)
|
||||
hr, _, _ := syscall.SyscallN(disp.lpVtbl.pInvoke,
|
||||
uintptr(unsafe.Pointer(disp)),
|
||||
uintptr(dispid),
|
||||
uintptr(unsafe.Pointer(IID_NULL)),
|
||||
uintptr(GetUserDefaultLCID()),
|
||||
uintptr(dispatch),
|
||||
uintptr(unsafe.Pointer(&dispparams)),
|
||||
uintptr(unsafe.Pointer(&ret)),
|
||||
uintptr(unsafe.Pointer(&excepInfo)),
|
||||
0)
|
||||
if hr != 0 {
|
||||
if excepInfo.BstrDescription != nil {
|
||||
bs := UTF16PtrToString(excepInfo.BstrDescription)
|
||||
panic(bs)
|
||||
}
|
||||
}
|
||||
for _, varg := range vargs {
|
||||
if varg.VT == VT_BSTR && varg.Val != 0 {
|
||||
SysFreeString(((*int16)(unsafe.Pointer(uintptr(varg.Val)))))
|
||||
}
|
||||
}
|
||||
result = &ret
|
||||
return
|
||||
}
|
||||
|
||||
func WMMessageToString(msg uintptr) string {
|
||||
// Convert windows message to string
|
||||
switch msg {
|
||||
case WM_APP:
|
||||
return "WM_APP"
|
||||
case WM_ACTIVATE:
|
||||
return "WM_ACTIVATE"
|
||||
case WM_ACTIVATEAPP:
|
||||
return "WM_ACTIVATEAPP"
|
||||
case WM_AFXFIRST:
|
||||
return "WM_AFXFIRST"
|
||||
case WM_AFXLAST:
|
||||
return "WM_AFXLAST"
|
||||
case WM_ASKCBFORMATNAME:
|
||||
return "WM_ASKCBFORMATNAME"
|
||||
case WM_CANCELJOURNAL:
|
||||
return "WM_CANCELJOURNAL"
|
||||
case WM_CANCELMODE:
|
||||
return "WM_CANCELMODE"
|
||||
case WM_CAPTURECHANGED:
|
||||
return "WM_CAPTURECHANGED"
|
||||
case WM_CHANGECBCHAIN:
|
||||
return "WM_CHANGECBCHAIN"
|
||||
case WM_CHAR:
|
||||
return "WM_CHAR"
|
||||
case WM_CHARTOITEM:
|
||||
return "WM_CHARTOITEM"
|
||||
case WM_CHILDACTIVATE:
|
||||
return "WM_CHILDACTIVATE"
|
||||
case WM_CLEAR:
|
||||
return "WM_CLEAR"
|
||||
case WM_CLOSE:
|
||||
return "WM_CLOSE"
|
||||
case WM_COMMAND:
|
||||
return "WM_COMMAND"
|
||||
case WM_COMMNOTIFY /* OBSOLETE */ :
|
||||
return "WM_COMMNOTIFY"
|
||||
case WM_COMPACTING:
|
||||
return "WM_COMPACTING"
|
||||
case WM_COMPAREITEM:
|
||||
return "WM_COMPAREITEM"
|
||||
case WM_CONTEXTMENU:
|
||||
return "WM_CONTEXTMENU"
|
||||
case WM_COPY:
|
||||
return "WM_COPY"
|
||||
case WM_COPYDATA:
|
||||
return "WM_COPYDATA"
|
||||
case WM_CREATE:
|
||||
return "WM_CREATE"
|
||||
case WM_CTLCOLORBTN:
|
||||
return "WM_CTLCOLORBTN"
|
||||
case WM_CTLCOLORDLG:
|
||||
return "WM_CTLCOLORDLG"
|
||||
case WM_CTLCOLOREDIT:
|
||||
return "WM_CTLCOLOREDIT"
|
||||
case WM_CTLCOLORLISTBOX:
|
||||
return "WM_CTLCOLORLISTBOX"
|
||||
case WM_CTLCOLORMSGBOX:
|
||||
return "WM_CTLCOLORMSGBOX"
|
||||
case WM_CTLCOLORSCROLLBAR:
|
||||
return "WM_CTLCOLORSCROLLBAR"
|
||||
case WM_CTLCOLORSTATIC:
|
||||
return "WM_CTLCOLORSTATIC"
|
||||
case WM_CUT:
|
||||
return "WM_CUT"
|
||||
case WM_DEADCHAR:
|
||||
return "WM_DEADCHAR"
|
||||
case WM_DELETEITEM:
|
||||
return "WM_DELETEITEM"
|
||||
case WM_DESTROY:
|
||||
return "WM_DESTROY"
|
||||
case WM_DESTROYCLIPBOARD:
|
||||
return "WM_DESTROYCLIPBOARD"
|
||||
case WM_DEVICECHANGE:
|
||||
return "WM_DEVICECHANGE"
|
||||
case WM_DEVMODECHANGE:
|
||||
return "WM_DEVMODECHANGE"
|
||||
case WM_DISPLAYCHANGE:
|
||||
return "WM_DISPLAYCHANGE"
|
||||
case WM_DRAWCLIPBOARD:
|
||||
return "WM_DRAWCLIPBOARD"
|
||||
case WM_DRAWITEM:
|
||||
return "WM_DRAWITEM"
|
||||
case WM_DROPFILES:
|
||||
return "WM_DROPFILES"
|
||||
case WM_ENABLE:
|
||||
return "WM_ENABLE"
|
||||
case WM_ENDSESSION:
|
||||
return "WM_ENDSESSION"
|
||||
case WM_ENTERIDLE:
|
||||
return "WM_ENTERIDLE"
|
||||
case WM_ENTERMENULOOP:
|
||||
return "WM_ENTERMENULOOP"
|
||||
case WM_ENTERSIZEMOVE:
|
||||
return "WM_ENTERSIZEMOVE"
|
||||
case WM_ERASEBKGND:
|
||||
return "WM_ERASEBKGND"
|
||||
case WM_EXITMENULOOP:
|
||||
return "WM_EXITMENULOOP"
|
||||
case WM_EXITSIZEMOVE:
|
||||
return "WM_EXITSIZEMOVE"
|
||||
case WM_FONTCHANGE:
|
||||
return "WM_FONTCHANGE"
|
||||
case WM_GETDLGCODE:
|
||||
return "WM_GETDLGCODE"
|
||||
case WM_GETFONT:
|
||||
return "WM_GETFONT"
|
||||
case WM_GETHOTKEY:
|
||||
return "WM_GETHOTKEY"
|
||||
case WM_GETICON:
|
||||
return "WM_GETICON"
|
||||
case WM_GETMINMAXINFO:
|
||||
return "WM_GETMINMAXINFO"
|
||||
case WM_GETTEXT:
|
||||
return "WM_GETTEXT"
|
||||
case WM_GETTEXTLENGTH:
|
||||
return "WM_GETTEXTLENGTH"
|
||||
case WM_HANDHELDFIRST:
|
||||
return "WM_HANDHELDFIRST"
|
||||
case WM_HANDHELDLAST:
|
||||
return "WM_HANDHELDLAST"
|
||||
case WM_HELP:
|
||||
return "WM_HELP"
|
||||
case WM_HOTKEY:
|
||||
return "WM_HOTKEY"
|
||||
case WM_HSCROLL:
|
||||
return "WM_HSCROLL"
|
||||
case WM_HSCROLLCLIPBOARD:
|
||||
return "WM_HSCROLLCLIPBOARD"
|
||||
case WM_ICONERASEBKGND:
|
||||
return "WM_ICONERASEBKGND"
|
||||
case WM_INITDIALOG:
|
||||
return "WM_INITDIALOG"
|
||||
case WM_INITMENU:
|
||||
return "WM_INITMENU"
|
||||
case WM_INITMENUPOPUP:
|
||||
return "WM_INITMENUPOPUP"
|
||||
case WM_INPUT:
|
||||
return "WM_INPUT"
|
||||
case WM_INPUTLANGCHANGE:
|
||||
return "WM_INPUTLANGCHANGE"
|
||||
case WM_INPUTLANGCHANGEREQUEST:
|
||||
return "WM_INPUTLANGCHANGEREQUEST"
|
||||
case WM_KEYDOWN:
|
||||
return "WM_KEYDOWN"
|
||||
case WM_KEYUP:
|
||||
return "WM_KEYUP"
|
||||
case WM_KILLFOCUS:
|
||||
return "WM_KILLFOCUS"
|
||||
case WM_MDIACTIVATE:
|
||||
return "WM_MDIACTIVATE"
|
||||
case WM_MDICASCADE:
|
||||
return "WM_MDICASCADE"
|
||||
case WM_MDICREATE:
|
||||
return "WM_MDICREATE"
|
||||
case WM_MDIDESTROY:
|
||||
return "WM_MDIDESTROY"
|
||||
case WM_MDIGETACTIVE:
|
||||
return "WM_MDIGETACTIVE"
|
||||
case WM_MDIICONARRANGE:
|
||||
return "WM_MDIICONARRANGE"
|
||||
case WM_MDIMAXIMIZE:
|
||||
return "WM_MDIMAXIMIZE"
|
||||
case WM_MDINEXT:
|
||||
return "WM_MDINEXT"
|
||||
case WM_MDIREFRESHMENU:
|
||||
return "WM_MDIREFRESHMENU"
|
||||
case WM_MDIRESTORE:
|
||||
return "WM_MDIRESTORE"
|
||||
case WM_MDISETMENU:
|
||||
return "WM_MDISETMENU"
|
||||
case WM_MDITILE:
|
||||
return "WM_MDITILE"
|
||||
case WM_MEASUREITEM:
|
||||
return "WM_MEASUREITEM"
|
||||
case WM_GETOBJECT:
|
||||
return "WM_GETOBJECT"
|
||||
case WM_CHANGEUISTATE:
|
||||
return "WM_CHANGEUISTATE"
|
||||
case WM_UPDATEUISTATE:
|
||||
return "WM_UPDATEUISTATE"
|
||||
case WM_QUERYUISTATE:
|
||||
return "WM_QUERYUISTATE"
|
||||
case WM_UNINITMENUPOPUP:
|
||||
return "WM_UNINITMENUPOPUP"
|
||||
case WM_MENURBUTTONUP:
|
||||
return "WM_MENURBUTTONUP"
|
||||
case WM_MENUCOMMAND:
|
||||
return "WM_MENUCOMMAND"
|
||||
case WM_MENUGETOBJECT:
|
||||
return "WM_MENUGETOBJECT"
|
||||
case WM_MENUDRAG:
|
||||
return "WM_MENUDRAG"
|
||||
case WM_APPCOMMAND:
|
||||
return "WM_APPCOMMAND"
|
||||
case WM_MENUCHAR:
|
||||
return "WM_MENUCHAR"
|
||||
case WM_MENUSELECT:
|
||||
return "WM_MENUSELECT"
|
||||
case WM_MOVE:
|
||||
return "WM_MOVE"
|
||||
case WM_MOVING:
|
||||
return "WM_MOVING"
|
||||
case WM_NCACTIVATE:
|
||||
return "WM_NCACTIVATE"
|
||||
case WM_NCCALCSIZE:
|
||||
return "WM_NCCALCSIZE"
|
||||
case WM_NCCREATE:
|
||||
return "WM_NCCREATE"
|
||||
case WM_NCDESTROY:
|
||||
return "WM_NCDESTROY"
|
||||
case WM_NCHITTEST:
|
||||
return "WM_NCHITTEST"
|
||||
case WM_NCLBUTTONDBLCLK:
|
||||
return "WM_NCLBUTTONDBLCLK"
|
||||
case WM_NCLBUTTONDOWN:
|
||||
return "WM_NCLBUTTONDOWN"
|
||||
case WM_NCLBUTTONUP:
|
||||
return "WM_NCLBUTTONUP"
|
||||
case WM_NCMBUTTONDBLCLK:
|
||||
return "WM_NCMBUTTONDBLCLK"
|
||||
case WM_NCMBUTTONDOWN:
|
||||
return "WM_NCMBUTTONDOWN"
|
||||
case WM_NCMBUTTONUP:
|
||||
return "WM_NCMBUTTONUP"
|
||||
case WM_NCXBUTTONDOWN:
|
||||
return "WM_NCXBUTTONDOWN"
|
||||
case WM_NCXBUTTONUP:
|
||||
return "WM_NCXBUTTONUP"
|
||||
case WM_NCXBUTTONDBLCLK:
|
||||
return "WM_NCXBUTTONDBLCLK"
|
||||
case WM_NCMOUSEHOVER:
|
||||
return "WM_NCMOUSEHOVER"
|
||||
case WM_NCMOUSELEAVE:
|
||||
return "WM_NCMOUSELEAVE"
|
||||
case WM_NCMOUSEMOVE:
|
||||
return "WM_NCMOUSEMOVE"
|
||||
case WM_NCPAINT:
|
||||
return "WM_NCPAINT"
|
||||
case WM_NCRBUTTONDBLCLK:
|
||||
return "WM_NCRBUTTONDBLCLK"
|
||||
case WM_NCRBUTTONDOWN:
|
||||
return "WM_NCRBUTTONDOWN"
|
||||
case WM_NCRBUTTONUP:
|
||||
return "WM_NCRBUTTONUP"
|
||||
case WM_NEXTDLGCTL:
|
||||
return "WM_NEXTDLGCTL"
|
||||
case WM_NEXTMENU:
|
||||
return "WM_NEXTMENU"
|
||||
case WM_NOTIFY:
|
||||
return "WM_NOTIFY"
|
||||
case WM_NOTIFYFORMAT:
|
||||
return "WM_NOTIFYFORMAT"
|
||||
case WM_NULL:
|
||||
return "WM_NULL"
|
||||
case WM_PAINT:
|
||||
return "WM_PAINT"
|
||||
case WM_PAINTCLIPBOARD:
|
||||
return "WM_PAINTCLIPBOARD"
|
||||
case WM_PAINTICON:
|
||||
return "WM_PAINTICON"
|
||||
case WM_PALETTECHANGED:
|
||||
return "WM_PALETTECHANGED"
|
||||
case WM_PALETTEISCHANGING:
|
||||
return "WM_PALETTEISCHANGING"
|
||||
case WM_PARENTNOTIFY:
|
||||
return "WM_PARENTNOTIFY"
|
||||
case WM_PASTE:
|
||||
return "WM_PASTE"
|
||||
case WM_PENWINFIRST:
|
||||
return "WM_PENWINFIRST"
|
||||
case WM_PENWINLAST:
|
||||
return "WM_PENWINLAST"
|
||||
case WM_POWER:
|
||||
return "WM_POWER"
|
||||
case WM_PRINT:
|
||||
return "WM_PRINT"
|
||||
case WM_PRINTCLIENT:
|
||||
return "WM_PRINTCLIENT"
|
||||
case WM_QUERYDRAGICON:
|
||||
return "WM_QUERYDRAGICON"
|
||||
case WM_QUERYENDSESSION:
|
||||
return "WM_QUERYENDSESSION"
|
||||
case WM_QUERYNEWPALETTE:
|
||||
return "WM_QUERYNEWPALETTE"
|
||||
case WM_QUERYOPEN:
|
||||
return "WM_QUERYOPEN"
|
||||
case WM_QUEUESYNC:
|
||||
return "WM_QUEUESYNC"
|
||||
case WM_QUIT:
|
||||
return "WM_QUIT"
|
||||
case WM_RENDERALLFORMATS:
|
||||
return "WM_RENDERALLFORMATS"
|
||||
case WM_RENDERFORMAT:
|
||||
return "WM_RENDERFORMAT"
|
||||
case WM_SETCURSOR:
|
||||
return "WM_SETCURSOR"
|
||||
case WM_SETFOCUS:
|
||||
return "WM_SETFOCUS"
|
||||
case WM_SETFONT:
|
||||
return "WM_SETFONT"
|
||||
case WM_SETHOTKEY:
|
||||
return "WM_SETHOTKEY"
|
||||
case WM_SETICON:
|
||||
return "WM_SETICON"
|
||||
case WM_SETREDRAW:
|
||||
return "WM_SETREDRAW"
|
||||
case WM_SETTEXT:
|
||||
return "WM_SETTEXT"
|
||||
case WM_SETTINGCHANGE:
|
||||
return "WM_SETTINGCHANGE"
|
||||
case WM_SHOWWINDOW:
|
||||
return "WM_SHOWWINDOW"
|
||||
case WM_SIZE:
|
||||
return "WM_SIZE"
|
||||
case WM_SIZECLIPBOARD:
|
||||
return "WM_SIZECLIPBOARD"
|
||||
case WM_SIZING:
|
||||
return "WM_SIZING"
|
||||
case WM_SPOOLERSTATUS:
|
||||
return "WM_SPOOLERSTATUS"
|
||||
case WM_STYLECHANGED:
|
||||
return "WM_STYLECHANGED"
|
||||
case WM_STYLECHANGING:
|
||||
return "WM_STYLECHANGING"
|
||||
case WM_SYSCHAR:
|
||||
return "WM_SYSCHAR"
|
||||
case WM_SYSCOLORCHANGE:
|
||||
return "WM_SYSCOLORCHANGE"
|
||||
case WM_SYSCOMMAND:
|
||||
return "WM_SYSCOMMAND"
|
||||
case WM_SYSDEADCHAR:
|
||||
return "WM_SYSDEADCHAR"
|
||||
case WM_SYSKEYDOWN:
|
||||
return "WM_SYSKEYDOWN"
|
||||
case WM_SYSKEYUP:
|
||||
return "WM_SYSKEYUP"
|
||||
case WM_TCARD:
|
||||
return "WM_TCARD"
|
||||
case WM_THEMECHANGED:
|
||||
return "WM_THEMECHANGED"
|
||||
case WM_TIMECHANGE:
|
||||
return "WM_TIMECHANGE"
|
||||
case WM_TIMER:
|
||||
return "WM_TIMER"
|
||||
case WM_UNDO:
|
||||
return "WM_UNDO"
|
||||
case WM_USER:
|
||||
return "WM_USER"
|
||||
case WM_USERCHANGED:
|
||||
return "WM_USERCHANGED"
|
||||
case WM_VKEYTOITEM:
|
||||
return "WM_VKEYTOITEM"
|
||||
case WM_VSCROLL:
|
||||
return "WM_VSCROLL"
|
||||
case WM_VSCROLLCLIPBOARD:
|
||||
return "WM_VSCROLLCLIPBOARD"
|
||||
case WM_WINDOWPOSCHANGED:
|
||||
return "WM_WINDOWPOSCHANGED"
|
||||
case WM_WINDOWPOSCHANGING:
|
||||
return "WM_WINDOWPOSCHANGING"
|
||||
case WM_KEYLAST:
|
||||
return "WM_KEYLAST"
|
||||
case WM_SYNCPAINT:
|
||||
return "WM_SYNCPAINT"
|
||||
case WM_MOUSEACTIVATE:
|
||||
return "WM_MOUSEACTIVATE"
|
||||
case WM_MOUSEMOVE:
|
||||
return "WM_MOUSEMOVE"
|
||||
case WM_LBUTTONDOWN:
|
||||
return "WM_LBUTTONDOWN"
|
||||
case WM_LBUTTONUP:
|
||||
return "WM_LBUTTONUP"
|
||||
case WM_LBUTTONDBLCLK:
|
||||
return "WM_LBUTTONDBLCLK"
|
||||
case WM_RBUTTONDOWN:
|
||||
return "WM_RBUTTONDOWN"
|
||||
case WM_RBUTTONUP:
|
||||
return "WM_RBUTTONUP"
|
||||
case WM_RBUTTONDBLCLK:
|
||||
return "WM_RBUTTONDBLCLK"
|
||||
case WM_MBUTTONDOWN:
|
||||
return "WM_MBUTTONDOWN"
|
||||
case WM_MBUTTONUP:
|
||||
return "WM_MBUTTONUP"
|
||||
case WM_MBUTTONDBLCLK:
|
||||
return "WM_MBUTTONDBLCLK"
|
||||
case WM_MOUSEWHEEL:
|
||||
return "WM_MOUSEWHEEL"
|
||||
case WM_XBUTTONDOWN:
|
||||
return "WM_XBUTTONDOWN"
|
||||
case WM_XBUTTONUP:
|
||||
return "WM_XBUTTONUP"
|
||||
case WM_MOUSELAST:
|
||||
return "WM_MOUSELAST"
|
||||
case WM_MOUSEHOVER:
|
||||
return "WM_MOUSEHOVER"
|
||||
case WM_MOUSELEAVE:
|
||||
return "WM_MOUSELEAVE"
|
||||
case WM_CLIPBOARDUPDATE:
|
||||
return "WM_CLIPBOARDUPDATE"
|
||||
default:
|
||||
return fmt.Sprintf("0x%08x", msg)
|
||||
}
|
||||
}
|
||||
16
v3/pkg/w32/vars.go
Normal file
16
v3/pkg/w32/vars.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
//go:build windows
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Tad Vizbaras. All Rights Reserved.
|
||||
* Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package w32
|
||||
|
||||
var (
|
||||
IID_NULL = &GUID{0x00000000, 0x0000, 0x0000, [8]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}
|
||||
IID_IUnknown = &GUID{0x00000000, 0x0000, 0x0000, [8]byte{0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}
|
||||
IID_IDispatch = &GUID{0x00020400, 0x0000, 0x0000, [8]byte{0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}
|
||||
IID_IConnectionPointContainer = &GUID{0xB196B284, 0xBAB4, 0x101A, [8]byte{0xB6, 0x9C, 0x00, 0xAA, 0x00, 0x34, 0x1D, 0x07}}
|
||||
IID_IConnectionPoint = &GUID{0xB196B286, 0xBAB4, 0x101A, [8]byte{0xB6, 0x9C, 0x00, 0xAA, 0x00, 0x34, 0x1D, 0x07}}
|
||||
)
|
||||
21
v3/pkg/w32/wda.go
Normal file
21
v3/pkg/w32/wda.go
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
//go:build windows
|
||||
|
||||
package w32
|
||||
|
||||
const (
|
||||
WDA_NONE = 0x00000000
|
||||
WDA_MONITOR = 0x00000001
|
||||
WDA_EXCLUDEFROMCAPTURE = 0x00000011 // windows 10 2004+
|
||||
)
|
||||
|
||||
func SetWindowDisplayAffinity(hwnd uintptr, affinity uint32) bool {
|
||||
if affinity == WDA_EXCLUDEFROMCAPTURE && !IsWindowsVersionAtLeast(10, 0, 19041) {
|
||||
// for older windows versions, use WDA_MONITOR
|
||||
affinity = WDA_MONITOR
|
||||
}
|
||||
ret, _, _ := procSetWindowDisplayAffinity.Call(
|
||||
hwnd,
|
||||
uintptr(affinity),
|
||||
)
|
||||
return ret != 0
|
||||
}
|
||||
377
v3/pkg/w32/window.go
Normal file
377
v3/pkg/w32/window.go
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
//go:build windows
|
||||
|
||||
package w32
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
user32 = syscall.NewLazyDLL("user32.dll")
|
||||
getSystemMenu = user32.NewProc("GetSystemMenu")
|
||||
getMenuProc = user32.NewProc("GetMenu")
|
||||
enableMenuItem = user32.NewProc("EnableMenuItem")
|
||||
findWindow = user32.NewProc("FindWindowW")
|
||||
sendMessage = user32.NewProc("SendMessageW")
|
||||
vkKeyScan = user32.NewProc("VkKeyScanW") // Use W version for Unicode
|
||||
)
|
||||
|
||||
func VkKeyScan(ch uint16) uint16 {
|
||||
ret, _, _ := syscall.SyscallN(
|
||||
vkKeyScan.Addr(),
|
||||
uintptr(ch),
|
||||
)
|
||||
return uint16(ret)
|
||||
}
|
||||
|
||||
const (
|
||||
WMCOPYDATA_SINGLE_INSTANCE_DATA = 1542
|
||||
)
|
||||
|
||||
type COPYDATASTRUCT struct {
|
||||
DwData uintptr
|
||||
CbData uint32
|
||||
LpData uintptr
|
||||
}
|
||||
|
||||
var Fatal func(error)
|
||||
|
||||
const (
|
||||
GCLP_HBRBACKGROUND int32 = -10
|
||||
GCLP_HICON int32 = -14
|
||||
)
|
||||
|
||||
type WINDOWPOS struct {
|
||||
HwndInsertAfter HWND
|
||||
X int32
|
||||
Y int32
|
||||
Cx int32
|
||||
Cy int32
|
||||
Flags uint32
|
||||
}
|
||||
|
||||
func ExtendFrameIntoClientArea(hwnd uintptr, extend bool) error {
|
||||
// -1: Adds the default frame styling (aero shadow and e.g. rounded corners on Windows 11)
|
||||
// Also shows the caption buttons if transparent ant translucent but they don't work.
|
||||
// 0: Adds the default frame styling but no aero shadow, does not show the caption buttons.
|
||||
// 1: Adds the default frame styling (aero shadow and e.g. rounded corners on Windows 11) but no caption buttons
|
||||
// are shown if transparent ant translucent.
|
||||
var margins MARGINS
|
||||
if extend {
|
||||
margins = MARGINS{1, 1, 1, 1} // Only extend 1 pixel to have the default frame styling but no caption buttons
|
||||
}
|
||||
if err := dwmExtendFrameIntoClientArea(hwnd, &margins); err != nil {
|
||||
return fmt.Errorf("DwmExtendFrameIntoClientArea failed: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsVisible(hwnd uintptr) bool {
|
||||
ret, _, _ := procIsWindowVisible.Call(hwnd)
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func IsWindowFullScreen(hwnd uintptr) bool {
|
||||
wRect := GetWindowRect(hwnd)
|
||||
m := MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY)
|
||||
var mi MONITORINFO
|
||||
mi.CbSize = uint32(unsafe.Sizeof(mi))
|
||||
if !GetMonitorInfo(m, &mi) {
|
||||
return false
|
||||
}
|
||||
return wRect.Left == mi.RcMonitor.Left &&
|
||||
wRect.Top == mi.RcMonitor.Top &&
|
||||
wRect.Right == mi.RcMonitor.Right &&
|
||||
wRect.Bottom == mi.RcMonitor.Bottom
|
||||
}
|
||||
|
||||
func IsWindowMaximised(hwnd uintptr) bool {
|
||||
style := uint32(getWindowLong(hwnd, GWL_STYLE))
|
||||
return style&WS_MAXIMIZE != 0
|
||||
}
|
||||
func IsWindowMinimised(hwnd uintptr) bool {
|
||||
style := uint32(getWindowLong(hwnd, GWL_STYLE))
|
||||
return style&WS_MINIMIZE != 0
|
||||
}
|
||||
|
||||
func RestoreWindow(hwnd uintptr) {
|
||||
showWindow(hwnd, SW_RESTORE)
|
||||
}
|
||||
|
||||
func ShowWindowMaximised(hwnd uintptr) {
|
||||
showWindow(hwnd, SW_MAXIMIZE)
|
||||
}
|
||||
func ShowWindowMinimised(hwnd uintptr) {
|
||||
showWindow(hwnd, SW_MINIMIZE)
|
||||
}
|
||||
|
||||
func SetApplicationIcon(hwnd uintptr, icon HICON) {
|
||||
setClassLongPtr(hwnd, GCLP_HICON, icon)
|
||||
}
|
||||
|
||||
func SetBackgroundColour(hwnd uintptr, r, g, b uint8) {
|
||||
col := uint32(r) | uint32(g)<<8 | uint32(b)<<16
|
||||
hbrush, _, _ := procCreateSolidBrush.Call(uintptr(col))
|
||||
setClassLongPtr(hwnd, GCLP_HBRBACKGROUND, hbrush)
|
||||
}
|
||||
|
||||
func IsWindowNormal(hwnd uintptr) bool {
|
||||
return !IsWindowMaximised(hwnd) && !IsWindowMinimised(hwnd) && !IsWindowFullScreen(hwnd)
|
||||
}
|
||||
|
||||
func setClassLongPtr(hwnd uintptr, param int32, val uintptr) bool {
|
||||
proc := procSetClassLongPtr
|
||||
if strconv.IntSize == 32 {
|
||||
/*
|
||||
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setclasslongptrw
|
||||
Note: To write code that is compatible with both 32-bit and 64-bit Windows, use SetClassLongPtr.
|
||||
When compiling for 32-bit Windows, SetClassLongPtr is defined as a call to the SetClassLong function
|
||||
|
||||
=> We have to do this dynamically when directly calling the DLL procedures
|
||||
*/
|
||||
proc = procSetClassLong
|
||||
}
|
||||
|
||||
ret, _, _ := proc.Call(
|
||||
hwnd,
|
||||
uintptr(param),
|
||||
val,
|
||||
)
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func getWindowLong(hwnd uintptr, index int) int32 {
|
||||
ret, _, _ := procGetWindowLong.Call(
|
||||
hwnd,
|
||||
uintptr(index))
|
||||
|
||||
return int32(ret)
|
||||
}
|
||||
|
||||
func showWindow(hwnd uintptr, cmdshow int) bool {
|
||||
ret, _, _ := procShowWindow.Call(
|
||||
hwnd,
|
||||
uintptr(cmdshow))
|
||||
return ret != 0
|
||||
}
|
||||
|
||||
func stripNulls(str string) string {
|
||||
// Split the string into substrings at each null character
|
||||
substrings := strings.Split(str, "\x00")
|
||||
|
||||
// Join the substrings back into a single string
|
||||
strippedStr := strings.Join(substrings, "")
|
||||
|
||||
return strippedStr
|
||||
}
|
||||
|
||||
func MustStringToUTF16Ptr(input string) *uint16 {
|
||||
input = stripNulls(input)
|
||||
result, err := syscall.UTF16PtrFromString(input)
|
||||
if err != nil {
|
||||
Fatal(err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// MustStringToUTF16uintptr converts input to a NUL-terminated UTF-16 buffer and returns its pointer as a uintptr.
|
||||
// It first removes any internal NUL characters from input, then converts the result to a UTF-16 pointer.
|
||||
// The function panics if the conversion fails.
|
||||
func MustStringToUTF16uintptr(input string) uintptr {
|
||||
input = stripNulls(input)
|
||||
ret, err := syscall.UTF16PtrFromString(input)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return uintptr(unsafe.Pointer(ret))
|
||||
}
|
||||
|
||||
// MustStringToUTF16 converts s to UTF-16 encoding, stripping any embedded NULs and panicking on error.
|
||||
//
|
||||
// The returned slice is suitable for Windows API calls that expect a UTF-16 encoded string.
|
||||
func MustStringToUTF16(input string) []uint16 {
|
||||
input = stripNulls(input)
|
||||
ret, err := syscall.UTF16FromString(input)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// StringToUTF16 converts input to a UTF-16 encoded, NUL-terminated []uint16 suitable for Windows API calls.
|
||||
// It first removes any embedded NUL ('\x00') characters from input. The returned slice is NUL-terminated;
|
||||
// an error is returned if the conversion fails.
|
||||
func StringToUTF16(input string) ([]uint16, error) {
|
||||
input = stripNulls(input)
|
||||
return syscall.UTF16FromString(input)
|
||||
}
|
||||
|
||||
func CenterWindow(hwnd HWND) {
|
||||
windowInfo := getWindowInfo(hwnd)
|
||||
frameless := windowInfo.IsPopup()
|
||||
|
||||
info := GetMonitorInfoForWindow(hwnd)
|
||||
workRect := info.RcWork
|
||||
screenMiddleW := workRect.Left + (workRect.Right-workRect.Left)/2
|
||||
screenMiddleH := workRect.Top + (workRect.Bottom-workRect.Top)/2
|
||||
var winRect *RECT
|
||||
if !frameless {
|
||||
winRect = GetWindowRect(hwnd)
|
||||
} else {
|
||||
winRect = GetClientRect(hwnd)
|
||||
}
|
||||
winWidth := winRect.Right - winRect.Left
|
||||
winHeight := winRect.Bottom - winRect.Top
|
||||
windowX := screenMiddleW - (winWidth / 2)
|
||||
windowY := screenMiddleH - (winHeight / 2)
|
||||
SetWindowPos(hwnd, HWND_TOP, int(windowX), int(windowY), int(winWidth), int(winHeight), SWP_NOSIZE)
|
||||
}
|
||||
|
||||
func getWindowInfo(hwnd HWND) *WINDOWINFO {
|
||||
var info WINDOWINFO
|
||||
info.CbSize = uint32(unsafe.Sizeof(info))
|
||||
GetWindowInfo(hwnd, &info)
|
||||
return &info
|
||||
}
|
||||
|
||||
func GetMonitorInfoForWindow(hwnd HWND) *MONITORINFO {
|
||||
currentMonitor := MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST)
|
||||
var info MONITORINFO
|
||||
info.CbSize = uint32(unsafe.Sizeof(info))
|
||||
GetMonitorInfo(currentMonitor, &info)
|
||||
return &info
|
||||
}
|
||||
|
||||
type WindowProc func(hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr
|
||||
|
||||
var windowClasses = make(map[string]HINSTANCE)
|
||||
var windowClassesLock sync.Mutex
|
||||
|
||||
func getWindowClass(name string) (HINSTANCE, bool) {
|
||||
windowClassesLock.Lock()
|
||||
defer windowClassesLock.Unlock()
|
||||
result, exists := windowClasses[name]
|
||||
return result, exists
|
||||
}
|
||||
|
||||
func setWindowClass(name string, instance HINSTANCE) {
|
||||
windowClassesLock.Lock()
|
||||
defer windowClassesLock.Unlock()
|
||||
windowClasses[name] = instance
|
||||
}
|
||||
|
||||
func RegisterWindow(name string, proc WindowProc) (HINSTANCE, error) {
|
||||
classInstance, exists := getWindowClass(name)
|
||||
if exists {
|
||||
return classInstance, nil
|
||||
}
|
||||
applicationInstance := GetModuleHandle("")
|
||||
if applicationInstance == 0 {
|
||||
return 0, fmt.Errorf("get module handle failed")
|
||||
}
|
||||
|
||||
var wc WNDCLASSEX
|
||||
wc.Size = uint32(unsafe.Sizeof(wc))
|
||||
wc.WndProc = syscall.NewCallback(proc)
|
||||
wc.Instance = applicationInstance
|
||||
wc.Icon = LoadIconWithResourceID(0, uint16(IDI_APPLICATION))
|
||||
wc.Cursor = LoadCursorWithResourceID(0, uint16(IDC_ARROW))
|
||||
wc.Background = COLOR_BTNFACE + 1
|
||||
wc.ClassName = MustStringToUTF16Ptr(name)
|
||||
|
||||
atom := RegisterClassEx(&wc)
|
||||
if atom == 0 {
|
||||
panic(syscall.GetLastError())
|
||||
}
|
||||
|
||||
setWindowClass(name, applicationInstance)
|
||||
|
||||
return applicationInstance, nil
|
||||
}
|
||||
|
||||
func FlashWindow(hwnd HWND, enabled bool) {
|
||||
var flashInfo FLASHWINFO
|
||||
flashInfo.CbSize = uint32(unsafe.Sizeof(flashInfo))
|
||||
flashInfo.Hwnd = hwnd
|
||||
if enabled {
|
||||
flashInfo.DwFlags = FLASHW_ALL | FLASHW_TIMERNOFG
|
||||
} else {
|
||||
flashInfo.DwFlags = FLASHW_STOP
|
||||
}
|
||||
_, _, _ = procFlashWindowEx.Call(uintptr(unsafe.Pointer(&flashInfo)))
|
||||
}
|
||||
|
||||
func EnumChildWindows(hwnd HWND, callback func(hwnd HWND, lparam LPARAM) LRESULT) LRESULT {
|
||||
r, _, _ := procEnumChildWindows.Call(hwnd, syscall.NewCallback(callback), 0)
|
||||
return r
|
||||
}
|
||||
|
||||
func DisableCloseButton(hwnd HWND) error {
|
||||
hSysMenu, _, err := getSystemMenu.Call(hwnd, 0)
|
||||
if hSysMenu == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
r1, _, err := enableMenuItem.Call(hSysMenu, SC_CLOSE, MF_BYCOMMAND|MF_DISABLED|MF_GRAYED)
|
||||
if r1 == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func EnableCloseButton(hwnd HWND) error {
|
||||
hSysMenu, _, err := getSystemMenu.Call(hwnd, 0)
|
||||
if hSysMenu == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
r1, _, err := enableMenuItem.Call(hSysMenu, SC_CLOSE, MF_BYCOMMAND|MF_ENABLED)
|
||||
if r1 == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func FindWindowW(className, windowName *uint16) HWND {
|
||||
ret, _, _ := findWindow.Call(
|
||||
uintptr(unsafe.Pointer(className)),
|
||||
uintptr(unsafe.Pointer(windowName)),
|
||||
)
|
||||
return HWND(ret)
|
||||
}
|
||||
|
||||
func SendMessageToWindow(hwnd HWND, msg string) {
|
||||
// Convert data to UTF16 string
|
||||
dataUTF16, err := StringToUTF16(msg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Prepare COPYDATASTRUCT
|
||||
cds := COPYDATASTRUCT{
|
||||
DwData: WMCOPYDATA_SINGLE_INSTANCE_DATA,
|
||||
CbData: uint32((len(dataUTF16) * 2) + 1), // +1 for null terminator
|
||||
LpData: uintptr(unsafe.Pointer(&dataUTF16[0])),
|
||||
}
|
||||
|
||||
// Send message to first instance
|
||||
_, _, _ = procSendMessage.Call(
|
||||
hwnd,
|
||||
WM_COPYDATA,
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&cds)),
|
||||
)
|
||||
}
|
||||
|
||||
// GetMenu retrieves a handle to the menu assigned to the specified window
|
||||
func GetMenu(hwnd HWND) HMENU {
|
||||
ret, _, _ := getMenuProc.Call(hwnd)
|
||||
return ret
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue