From ff4e749ce6460f1093f17f66e1521e16c31b98c9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 22 Feb 2026 05:22:19 +0000 Subject: [PATCH] Initial plan: Fix Windows-specific bugs from discussion #5001 Co-authored-by: leaanthony <1943904+leaanthony@users.noreply.github.com> --- v3/pkg/application/event_manager.go | 174 + v3/pkg/application/webview_window_windows.go | 2479 ++++++++++++ v3/pkg/w32/actions.go | 27 + v3/pkg/w32/clipboard.go | 143 + v3/pkg/w32/com.go | 55 + v3/pkg/w32/comctl32.go | 112 + v3/pkg/w32/comdlg32.go | 40 + v3/pkg/w32/constants.go | 3731 ++++++++++++++++++ v3/pkg/w32/consts.go | 102 + v3/pkg/w32/dialogs.go | 28 + v3/pkg/w32/dwmapi.go | 46 + v3/pkg/w32/gdi32.go | 581 +++ v3/pkg/w32/gdiplus.go | 177 + v3/pkg/w32/guid.go | 225 ++ v3/pkg/w32/icon.go | 255 ++ v3/pkg/w32/idataobject.go | 168 + v3/pkg/w32/idispatch.go | 45 + v3/pkg/w32/idroptarget.go | 140 + v3/pkg/w32/image.go | 55 + v3/pkg/w32/istream.go | 33 + v3/pkg/w32/kernel32.go | 337 ++ v3/pkg/w32/menubar.go | 980 +++++ v3/pkg/w32/ole32.go | 119 + v3/pkg/w32/oleaut32.go | 50 + v3/pkg/w32/popupmenu.go | 96 + v3/pkg/w32/screen.go | 143 + v3/pkg/w32/shcore.go | 71 + v3/pkg/w32/shell32.go | 414 ++ v3/pkg/w32/shlwapi.go | 26 + v3/pkg/w32/taskbar.go | 95 + v3/pkg/w32/theme.go | 345 ++ v3/pkg/w32/timer.go | 19 + v3/pkg/w32/toolbar.go | 216 + v3/pkg/w32/typedef.go | 1110 ++++++ v3/pkg/w32/user32.go | 1510 +++++++ v3/pkg/w32/utils.go | 637 +++ v3/pkg/w32/vars.go | 16 + v3/pkg/w32/wda.go | 21 + v3/pkg/w32/window.go | 377 ++ 39 files changed, 15198 insertions(+) create mode 100644 v3/pkg/application/event_manager.go create mode 100644 v3/pkg/application/webview_window_windows.go create mode 100644 v3/pkg/w32/actions.go create mode 100644 v3/pkg/w32/clipboard.go create mode 100644 v3/pkg/w32/com.go create mode 100644 v3/pkg/w32/comctl32.go create mode 100644 v3/pkg/w32/comdlg32.go create mode 100644 v3/pkg/w32/constants.go create mode 100644 v3/pkg/w32/consts.go create mode 100644 v3/pkg/w32/dialogs.go create mode 100644 v3/pkg/w32/dwmapi.go create mode 100644 v3/pkg/w32/gdi32.go create mode 100644 v3/pkg/w32/gdiplus.go create mode 100644 v3/pkg/w32/guid.go create mode 100644 v3/pkg/w32/icon.go create mode 100644 v3/pkg/w32/idataobject.go create mode 100644 v3/pkg/w32/idispatch.go create mode 100644 v3/pkg/w32/idroptarget.go create mode 100644 v3/pkg/w32/image.go create mode 100644 v3/pkg/w32/istream.go create mode 100644 v3/pkg/w32/kernel32.go create mode 100644 v3/pkg/w32/menubar.go create mode 100644 v3/pkg/w32/ole32.go create mode 100644 v3/pkg/w32/oleaut32.go create mode 100644 v3/pkg/w32/popupmenu.go create mode 100644 v3/pkg/w32/screen.go create mode 100644 v3/pkg/w32/shcore.go create mode 100644 v3/pkg/w32/shell32.go create mode 100644 v3/pkg/w32/shlwapi.go create mode 100644 v3/pkg/w32/taskbar.go create mode 100644 v3/pkg/w32/theme.go create mode 100644 v3/pkg/w32/timer.go create mode 100644 v3/pkg/w32/toolbar.go create mode 100644 v3/pkg/w32/typedef.go create mode 100644 v3/pkg/w32/user32.go create mode 100644 v3/pkg/w32/utils.go create mode 100644 v3/pkg/w32/vars.go create mode 100644 v3/pkg/w32/wda.go create mode 100644 v3/pkg/w32/window.go diff --git a/v3/pkg/application/event_manager.go b/v3/pkg/application/event_manager.go new file mode 100644 index 000000000..7555ff685 --- /dev/null +++ b/v3/pkg/application/event_manager.go @@ -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) + }() + } +} diff --git a/v3/pkg/application/webview_window_windows.go b/v3/pkg/application/webview_window_windows.go new file mode 100644 index 000000000..16ba08f0b --- /dev/null +++ b/v3/pkg/application/webview_window_windows.go @@ -0,0 +1,2479 @@ +//go:build windows + +package application + +import ( + "errors" + "fmt" + "net/url" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + "unsafe" + + "github.com/bep/debounce" + "github.com/wailsapp/go-webview2/webviewloader" + "github.com/wailsapp/wails/v3/internal/assetserver" + "github.com/wailsapp/wails/v3/internal/assetserver/webview" + "github.com/wailsapp/wails/v3/internal/capabilities" + "github.com/wailsapp/wails/v3/internal/runtime" + "github.com/wailsapp/wails/v3/internal/sliceutil" + + "github.com/wailsapp/go-webview2/pkg/edge" + "github.com/wailsapp/wails/v3/pkg/events" + "github.com/wailsapp/wails/v3/pkg/w32" +) + +var edgeMap = map[string]uintptr{ + "n-resize": w32.HTTOP, + "ne-resize": w32.HTTOPRIGHT, + "e-resize": w32.HTRIGHT, + "se-resize": w32.HTBOTTOMRIGHT, + "s-resize": w32.HTBOTTOM, + "sw-resize": w32.HTBOTTOMLEFT, + "w-resize": w32.HTLEFT, + "nw-resize": w32.HTTOPLEFT, +} + +type windowsWebviewWindow struct { + windowImpl unsafe.Pointer + parent *WebviewWindow + hwnd w32.HWND + menu *Win32Menu + currentlyOpenContextMenu *Win32Menu + ignoreDPIChangeResizing bool + + // Fullscreen flags + isCurrentlyFullscreen bool + previousWindowStyle uint32 + previousWindowExStyle uint32 + previousWindowPlacement w32.WINDOWPLACEMENT + + // Webview + chromium *edge.Chromium + webviewNavigationCompleted bool + + // Window visibility management - robust fallback for issue #2861 + showRequested bool // Track if show() was called before navigation completed + visibilityTimeout *time.Timer // Timeout to show window if navigation is delayed + windowShown bool // Track if window container has been shown + // Track whether content protection has been applied to the native window yet + contentProtectionApplied bool + + // resizeBorder* is the width/height of the resize border in pixels. + resizeBorderWidth int32 + resizeBorderHeight int32 + focusingChromium bool + onceDo sync.Once + + // Window move debouncer + moveDebouncer func(func()) + resizeDebouncer func(func()) + + // isMinimizing indicates whether the window is currently being minimized + // Used to prevent unnecessary redraws during minimize/restore operations + isMinimizing bool + + // menubarTheme is the theme for the menubar + menubarTheme *w32.MenuBarTheme +} + +func (w *windowsWebviewWindow) setMenu(menu *Menu) { + menu.Update() + w.menu = NewApplicationMenu(w, menu) + w.menu.parentWindow = w + w32.SetMenu(w.hwnd, w.menu.menu) + + // Set menu background if theme is active + if w.menubarTheme != nil { + globalApplication.debug("Applying menubar theme in setMenu", "window", w.parent.id) + w.menubarTheme.SetMenuBackground(w.menu.menu) + w32.DrawMenuBar(w.hwnd) + // Force a repaint of the menu area + w32.InvalidateRect(w.hwnd, nil, true) + } else { + globalApplication.debug("No menubar theme to apply in setMenu", "window", w.parent.id) + } + + // Check if using translucent background with Mica - this makes menubars invisible + if w.parent.options.BackgroundType == BackgroundTypeTranslucent && + (w.parent.options.Windows.BackdropType == Mica || + w.parent.options.Windows.BackdropType == Acrylic || + w.parent.options.Windows.BackdropType == Tabbed) { + // Log warning about menubar visibility issue + globalApplication.debug("Warning: Menubars may be invisible when using translucent backgrounds with Mica/Acrylic/Tabbed effects", "window", w.parent.id) + } +} + +func (w *windowsWebviewWindow) cut() { + w.execJS("document.execCommand('cut')") +} + +func (w *windowsWebviewWindow) paste() { + w.execJS(` + (async () => { + try { + // Try to read all available formats + const clipboardItems = await navigator.clipboard.read(); + + for (const clipboardItem of clipboardItems) { + // Check for image types + for (const type of clipboardItem.types) { + if (type.startsWith('image/')) { + const blob = await clipboardItem.getType(type); + const url = URL.createObjectURL(blob); + document.execCommand('insertHTML', false, ''); + return; + } + } + + // If no image found, try text + if (clipboardItem.types.includes('text/plain')) { + const text = await navigator.clipboard.readText(); + document.execCommand('insertText', false, text); + return; + } + } + } catch(err) { + // Fallback to text-only paste if clipboard access fails + try { + const text = await navigator.clipboard.readText(); + document.execCommand('insertText', false, text); + } catch(fallbackErr) { + console.error('Failed to paste:', err, fallbackErr); + } + } + })() + `) +} + +func (w *windowsWebviewWindow) copy() { + w.execJS(` + (async () => { + try { + const selection = window.getSelection(); + if (!selection.rangeCount) return; + + const range = selection.getRangeAt(0); + const container = document.createElement('div'); + container.appendChild(range.cloneContents()); + + // Check if we have any images in the selection + const images = container.getElementsByTagName('img'); + if (images.length > 0) { + // Handle image copy + const img = images[0]; // Take the first image + const response = await fetch(img.src); + const blob = await response.blob(); + await navigator.clipboard.write([ + new ClipboardItem({ + [blob.type]: blob + }) + ]); + } else { + // Handle text copy + const text = selection.toString(); + if (text) { + await navigator.clipboard.writeText(text); + } + } + } catch(err) { + console.error('Failed to copy:', err); + } + })() + `) +} + +func (w *windowsWebviewWindow) selectAll() { + w.execJS("document.execCommand('selectAll')") +} + +func (w *windowsWebviewWindow) undo() { + w.execJS("document.execCommand('undo')") +} + +func (w *windowsWebviewWindow) redo() { + w.execJS("document.execCommand('redo')") +} + +func (w *windowsWebviewWindow) delete() { + w.execJS("document.execCommand('delete')") +} + +func (w *windowsWebviewWindow) handleKeyEvent(_ string) { + // Unused on windows +} + +func (w *windowsWebviewWindow) setEnabled(enabled bool) { + w32.EnableWindow(w.hwnd, enabled) +} + +func (w *windowsWebviewWindow) print() error { + w.execJS("window.print();") + return nil +} + +func (w *windowsWebviewWindow) startResize(border string) error { + if !w32.ReleaseCapture() { + return errors.New("unable to release mouse capture") + } + // Use PostMessage because we don't want to block the caller until resizing has been finished. + w32.PostMessage(w.hwnd, w32.WM_NCLBUTTONDOWN, edgeMap[border], 0) + return nil +} + +func (w *windowsWebviewWindow) startDrag() error { + if !w32.ReleaseCapture() { + return errors.New("unable to release mouse capture") + } + // Use PostMessage because we don't want to block the caller until dragging has been finished. + w32.PostMessage(w.hwnd, w32.WM_NCLBUTTONDOWN, w32.HTCAPTION, 0) + return nil +} + +func (w *windowsWebviewWindow) nativeWindow() unsafe.Pointer { + return unsafe.Pointer(w.hwnd) +} + +func (w *windowsWebviewWindow) setTitle(title string) { + w32.SetWindowText(w.hwnd, title) +} + +func (w *windowsWebviewWindow) setAlwaysOnTop(alwaysOnTop bool) { + var hwndInsertAfter uintptr + if alwaysOnTop { + hwndInsertAfter = w32.HWND_TOPMOST + } else { + hwndInsertAfter = w32.HWND_NOTOPMOST + } + w32.SetWindowPos(w.hwnd, + hwndInsertAfter, + 0, + 0, + 0, + 0, + uint(w32.SWP_NOMOVE|w32.SWP_NOSIZE)) +} + +func (w *windowsWebviewWindow) setURL(url string) { + // Navigate to the given URL in the webview + w.webviewNavigationCompleted = false + w.chromium.Navigate(url) +} + +func (w *windowsWebviewWindow) setResizable(resizable bool) { + w.setStyle(resizable, w32.WS_THICKFRAME) + w.execJS(fmt.Sprintf("window._wails.setResizable(%v);", resizable)) +} + +func (w *windowsWebviewWindow) setMinSize(width, height int) { + w.parent.options.MinWidth = width + w.parent.options.MinHeight = height +} + +func (w *windowsWebviewWindow) setMaxSize(width, height int) { + w.parent.options.MaxWidth = width + w.parent.options.MaxHeight = height +} + +func (w *windowsWebviewWindow) execJS(js string) { + if w.chromium == nil { + return + } + globalApplication.dispatchOnMainThread(func() { + w.chromium.Eval(js) + }) +} + +func (w *windowsWebviewWindow) setBackgroundColour(color RGBA) { + switch w.parent.options.BackgroundType { + case BackgroundTypeSolid: + w32.SetBackgroundColour(w.hwnd, color.Red, color.Green, color.Blue) + w.chromium.SetBackgroundColour(color.Red, color.Green, color.Blue, color.Alpha) + case BackgroundTypeTransparent, BackgroundTypeTranslucent: + w.chromium.SetBackgroundColour(0, 0, 0, 0) + } +} + +func (w *windowsWebviewWindow) framelessWithDecorations() bool { + return w.parent.options.Frameless && !w.parent.options.Windows.DisableFramelessWindowDecorations +} + +func (w *windowsWebviewWindow) run() { + + options := w.parent.options + + // Initialize showRequested based on whether window should be hidden + // Non-hidden windows should be shown by default + w.showRequested = !options.Hidden + + w.chromium = edge.NewChromium() + if globalApplication.options.ErrorHandler != nil { + w.chromium.SetErrorCallback(globalApplication.options.ErrorHandler) + } + + exStyle := w32.WS_EX_CONTROLPARENT + if options.BackgroundType != BackgroundTypeSolid { + if (options.Frameless && options.BackgroundType == BackgroundTypeTransparent) || + w.parent.options.IgnoreMouseEvents { + // Always if transparent and frameless + exStyle |= w32.WS_EX_TRANSPARENT | w32.WS_EX_LAYERED + } else { + // Only WS_EX_NOREDIRECTIONBITMAP if not (and not solid) + exStyle |= w32.WS_EX_NOREDIRECTIONBITMAP + } + } + if options.AlwaysOnTop { + exStyle |= w32.WS_EX_TOPMOST + } + // If we're frameless, we need to add the WS_EX_TOOLWINDOW style to hide the window from the taskbar + if options.Windows.HiddenOnTaskbar { + //exStyle |= w32.WS_EX_TOOLWINDOW + exStyle |= w32.WS_EX_NOACTIVATE + } else { + exStyle |= w32.WS_EX_APPWINDOW + } + + if options.Windows.ExStyle != 0 { + exStyle = options.Windows.ExStyle + } + + bounds := Rect{ + X: options.X, + Y: options.Y, + Width: options.Width, + Height: options.Height, + } + initialScreen := ScreenNearestDipRect(bounds) + physicalBounds := initialScreen.dipToPhysicalRect(bounds) + + // Default window position applied by the system + // TODO: provide a way to set (0,0) as an initial position? + if options.X == 0 && options.Y == 0 { + physicalBounds.X = w32.CW_USEDEFAULT + physicalBounds.Y = w32.CW_USEDEFAULT + } + + var appMenu w32.HMENU + + // Process Menu + if !options.Frameless { + userMenu := w.parent.options.Windows.Menu + if userMenu != nil { + // Explicit window menu takes priority + userMenu.Update() + w.menu = NewApplicationMenu(w, userMenu) + w.menu.parentWindow = w + appMenu = w.menu.menu + } else if options.UseApplicationMenu && globalApplication.applicationMenu != nil { + // Use the global application menu if opted in + globalApplication.applicationMenu.Update() + w.menu = NewApplicationMenu(w, globalApplication.applicationMenu) + w.menu.parentWindow = w + appMenu = w.menu.menu + } + } + + var parent w32.HWND + + var style uint = w32.WS_OVERLAPPEDWINDOW + // If the window should be hidden initially, exclude WS_VISIBLE from the style + // This prevents the white window flash reported in issue #4611 + if options.Hidden { + style = style &^ uint(w32.WS_VISIBLE) + } + + w.hwnd = w32.CreateWindowEx( + uint(exStyle), + w32.MustStringToUTF16Ptr(globalApplication.options.Windows.WndClass), + w32.MustStringToUTF16Ptr(options.Title), + style, + physicalBounds.X, + physicalBounds.Y, + physicalBounds.Width, + physicalBounds.Height, + parent, + appMenu, + w32.GetModuleHandle(""), + nil) + + if w.hwnd == 0 { + globalApplication.fatal("unable to create window") + } + + // Ensure correct window size in case the scale factor of current screen is different from the initial one. + // This could happen when using the default window position and the window launches on a secondary monitor. + currentScreen, _ := w.getScreen() + if currentScreen.ScaleFactor != initialScreen.ScaleFactor { + w.setSize(options.Width, options.Height) + } + + w.setupChromium() + + if options.Windows.WindowDidMoveDebounceMS == 0 { + options.Windows.WindowDidMoveDebounceMS = 50 + } + w.moveDebouncer = debounce.New( + time.Duration(options.Windows.WindowDidMoveDebounceMS) * time.Millisecond, + ) + + if options.Windows.ResizeDebounceMS > 0 { + w.resizeDebouncer = debounce.New( + time.Duration(options.Windows.ResizeDebounceMS) * time.Millisecond, + ) + } + + // Initialise the window buttons + w.setMinimiseButtonState(options.MinimiseButtonState) + w.setMaximiseButtonState(options.MaximiseButtonState) + w.setCloseButtonState(options.CloseButtonState) + + // Register the window with the application + getNativeApplication().registerWindow(w) + + w.setResizable(!options.DisableResize) + + w.setIgnoreMouseEvents(options.IgnoreMouseEvents) + + if options.Frameless { + // Inform the application of the frame change this is needed to trigger the WM_NCCALCSIZE event. + // => https://learn.microsoft.com/en-us/windows/win32/dwm/customframe#removing-the-standard-frame + // This is normally done in WM_CREATE but we can't handle that there because that is emitted during CreateWindowEx + // and at that time we can't yet register the window for calling our WndProc method. + // This must be called after setResizable above! + rcClient := w32.GetWindowRect(w.hwnd) + w32.SetWindowPos(w.hwnd, + 0, + int(rcClient.Left), + int(rcClient.Top), + int(rcClient.Right-rcClient.Left), + int(rcClient.Bottom-rcClient.Top), + w32.SWP_FRAMECHANGED) + } + + // Icon + if !options.Windows.DisableIcon { + // App icon ID is 3 + icon, err := NewIconFromResource(w32.GetModuleHandle(""), uint16(3)) + if err != nil { + // Try loading from the given icon + if globalApplication.options.Icon != nil { + icon, _ = w32.CreateLargeHIconFromImage(globalApplication.options.Icon) + } + } + if icon != 0 { + w.setIcon(icon) + } + } else { + w.disableIcon() + } + + // Process the theme + switch options.Windows.Theme { + case SystemDefault: + isDark := w32.IsCurrentlyDarkMode() + if isDark { + w32.AllowDarkModeForWindow(w.hwnd, true) + } + w.updateTheme(isDark) + // Don't initialize default dark theme here if custom theme might be set + // The updateTheme call above will handle both default and custom themes + w.parent.onApplicationEvent(events.Windows.SystemThemeChanged, func(*ApplicationEvent) { + InvokeAsync(func() { + w.updateTheme(w32.IsCurrentlyDarkMode()) + }) + }) + case Light: + w.updateTheme(false) + case Dark: + w32.AllowDarkModeForWindow(w.hwnd, true) + w.updateTheme(true) + // Don't initialize default dark theme here if custom theme might be set + // The updateTheme call above will handle custom themes + } + + w.setBackgroundColour(options.BackgroundColour) + if options.BackgroundType == BackgroundTypeTranslucent { + w.setBackdropType(w.parent.options.Windows.BackdropType) + } + + // Process StartState + switch options.StartState { + case WindowStateMaximised: + if w.parent.Resizable() { + w.maximise() + } + case WindowStateMinimised: + w.minimise() + case WindowStateFullscreen: + w.fullscreen() + case WindowStateNormal: + } + + // Process window mask + if options.Windows.WindowMask != nil { + w.setWindowMask(options.Windows.WindowMask) + } + + if options.InitialPosition == WindowCentered { + w.center() + } else { + w.setPosition(options.X, options.Y) + } + + if options.Frameless { + // Trigger a resize to ensure the window is sized correctly + w.chromium.Resize() + } +} + +func (w *windowsWebviewWindow) center() { + w32.CenterWindow(w.hwnd) +} + +func (w *windowsWebviewWindow) disableSizeConstraints() { + w.setMaxSize(0, 0) + w.setMinSize(0, 0) +} + +func (w *windowsWebviewWindow) enableSizeConstraints() { + options := w.parent.options + if options.MinWidth > 0 || options.MinHeight > 0 { + w.setMinSize(options.MinWidth, options.MinHeight) + } + if options.MaxWidth > 0 || options.MaxHeight > 0 { + w.setMaxSize(options.MaxWidth, options.MaxHeight) + } +} + +func (w *windowsWebviewWindow) update() { + w32.UpdateWindow(w.hwnd) +} + +// getBorderSizes returns the extended border size for the window +func (w *windowsWebviewWindow) getBorderSizes() *LRTB { + var result LRTB + var frame w32.RECT + w32.DwmGetWindowAttribute( + w.hwnd, + w32.DWMWA_EXTENDED_FRAME_BOUNDS, + unsafe.Pointer(&frame), + unsafe.Sizeof(frame), + ) + rect := w32.GetWindowRect(w.hwnd) + result.Left = int(frame.Left - rect.Left) + result.Top = int(frame.Top - rect.Top) + result.Right = int(rect.Right - frame.Right) + result.Bottom = int(rect.Bottom - frame.Bottom) + return &result +} + +// convertWindowToWebviewCoordinates converts window-relative coordinates to webview-relative coordinates +func (w *windowsWebviewWindow) convertWindowToWebviewCoordinates(windowX, windowY int) (int, int) { + // Get the client area of the window (this excludes borders, title bar, etc.) + clientRect := w32.GetClientRect(w.hwnd) + if clientRect == nil { + // Fallback: return coordinates as-is if we can't get client rect + globalApplication.debug("[DragDropDebug] convertWindowToWebviewCoordinates: Failed to get client rect, returning original coordinates", "windowX", windowX, "windowY", windowY) + return windowX, windowY + } + + // Get the window rect to calculate the offset + windowRect := w32.GetWindowRect(w.hwnd) + + globalApplication.debug("[DragDropDebug] convertWindowToWebviewCoordinates: Input window coordinates", "windowX", windowX, "windowY", windowY) + globalApplication.debug("[DragDropDebug] convertWindowToWebviewCoordinates: Window rect", + "left", windowRect.Left, "top", windowRect.Top, "right", windowRect.Right, "bottom", windowRect.Bottom, + "width", windowRect.Right-windowRect.Left, "height", windowRect.Bottom-windowRect.Top) + globalApplication.debug("[DragDropDebug] convertWindowToWebviewCoordinates: Client rect", + "left", clientRect.Left, "top", clientRect.Top, "right", clientRect.Right, "bottom", clientRect.Bottom, + "width", clientRect.Right-clientRect.Left, "height", clientRect.Bottom-clientRect.Top) + + // Convert client (0,0) to screen coordinates to find where the client area starts + var point w32.POINT + point.X = 0 + point.Y = 0 + + // Convert client (0,0) to screen coordinates + clientX, clientY := w32.ClientToScreen(w.hwnd, int(point.X), int(point.Y)) + + // The window coordinates from drag drop are relative to the window's top-left + // But we need them relative to the client area's top-left + // So we need to subtract the difference between window origin and client origin + windowOriginX := int(windowRect.Left) + windowOriginY := int(windowRect.Top) + + globalApplication.debug("[DragDropDebug] convertWindowToWebviewCoordinates: Client (0,0) in screen coordinates", "clientX", clientX, "clientY", clientY) + globalApplication.debug("[DragDropDebug] convertWindowToWebviewCoordinates: Window origin in screen coordinates", "windowOriginX", windowOriginX, "windowOriginY", windowOriginY) + + // Calculate the offset from window origin to client origin + offsetX := clientX - windowOriginX + offsetY := clientY - windowOriginY + + globalApplication.debug("[DragDropDebug] convertWindowToWebviewCoordinates: Calculated offset", "offsetX", offsetX, "offsetY", offsetY) + + // Convert window-relative coordinates to webview-relative coordinates + webviewX := windowX - offsetX + webviewY := windowY - offsetY + + globalApplication.debug("[DragDropDebug] convertWindowToWebviewCoordinates: Final webview coordinates", "webviewX", webviewX, "webviewY", webviewY) + + return webviewX, webviewY +} + +func (w *windowsWebviewWindow) physicalBounds() Rect { + // var rect w32.RECT + // // Get the extended frame bounds instead of the window rect to offset the invisible borders in Windows 10 + // w32.DwmGetWindowAttribute(w.hwnd, w32.DWMWA_EXTENDED_FRAME_BOUNDS, unsafe.Pointer(&rect), unsafe.Sizeof(rect)) + rect := w32.GetWindowRect(w.hwnd) + return Rect{ + X: int(rect.Left), + Y: int(rect.Top), + Width: int(rect.Right - rect.Left), + Height: int(rect.Bottom - rect.Top), + } +} + +func (w *windowsWebviewWindow) setPhysicalBounds(physicalBounds Rect) { + // // Offset invisible borders + // borderSize := w.getBorderSizes() + // physicalBounds.X -= borderSize.Left + // physicalBounds.Y -= borderSize.Top + // physicalBounds.Width += borderSize.Left + borderSize.Right + // physicalBounds.Height += borderSize.Top + borderSize.Bottom + + // Set flag to ignore resizing the window with DPI change because we already calculated the correct size + // for the target position, this prevents double resizing issue when the window is moved between screens + previousFlag := w.ignoreDPIChangeResizing + w.ignoreDPIChangeResizing = true + w32.SetWindowPos( + w.hwnd, + 0, + physicalBounds.X, + physicalBounds.Y, + physicalBounds.Width, + physicalBounds.Height, + w32.SWP_NOZORDER|w32.SWP_NOACTIVATE, + ) + w.ignoreDPIChangeResizing = previousFlag +} + +// Get window dip bounds +func (w *windowsWebviewWindow) bounds() Rect { + return PhysicalToDipRect(w.physicalBounds()) +} + +// Set window dip bounds +func (w *windowsWebviewWindow) setBounds(bounds Rect) { + w.setPhysicalBounds(DipToPhysicalRect(bounds)) +} + +func (w *windowsWebviewWindow) size() (int, int) { + bounds := w.bounds() + return bounds.Width, bounds.Height +} + +func (w *windowsWebviewWindow) width() int { + return w.bounds().Width +} + +func (w *windowsWebviewWindow) height() int { + return w.bounds().Height +} + +func (w *windowsWebviewWindow) setSize(width, height int) { + bounds := w.bounds() + bounds.Width = width + bounds.Height = height + + w.setBounds(bounds) +} + +func (w *windowsWebviewWindow) position() (int, int) { + bounds := w.bounds() + return bounds.X, bounds.Y +} + +func (w *windowsWebviewWindow) setPosition(x int, y int) { + bounds := w.bounds() + bounds.X = x + bounds.Y = y + + w.setBounds(bounds) +} + +// Get window position relative to the screen WorkArea on which it is +func (w *windowsWebviewWindow) relativePosition() (int, int) { + screen, _ := w.getScreen() + pos := screen.absoluteToRelativeDipPoint(w.bounds().Origin()) + // Relative to WorkArea origin + pos.X -= (screen.WorkArea.X - screen.X) + pos.Y -= (screen.WorkArea.Y - screen.Y) + return pos.X, pos.Y +} + +// Set window position relative to the screen WorkArea on which it is +func (w *windowsWebviewWindow) setRelativePosition(x int, y int) { + screen, _ := w.getScreen() + pos := screen.relativeToAbsoluteDipPoint(Point{X: x, Y: y}) + // Relative to WorkArea origin + pos.X += (screen.WorkArea.X - screen.X) + pos.Y += (screen.WorkArea.Y - screen.Y) + w.setPosition(pos.X, pos.Y) +} + +func (w *windowsWebviewWindow) destroy() { + w.parent.markAsDestroyed() + // destroy the window + w32.DestroyWindow(w.hwnd) +} + +func (w *windowsWebviewWindow) reload() { + w.execJS("window.location.reload();") +} + +func (w *windowsWebviewWindow) forceReload() { + // noop +} + +func (w *windowsWebviewWindow) zoomReset() { + w.setZoom(1.0) +} + +func (w *windowsWebviewWindow) zoomIn() { + // Increase the zoom level by 0.05 + currentZoom := w.getZoom() + if currentZoom == -1 { + return + } + w.setZoom(currentZoom + 0.05) +} + +func (w *windowsWebviewWindow) zoomOut() { + // Decrease the zoom level by 0.05 + currentZoom := w.getZoom() + if currentZoom == -1 { + return + } + if currentZoom > 1.05 { + // Decrease the zoom level by 0.05 + w.setZoom(currentZoom - 0.05) + } else { + // Set the zoom level to 1.0 + w.setZoom(1.0) + } +} + +func (w *windowsWebviewWindow) getZoom() float64 { + controller := w.chromium.GetController() + factor, err := controller.GetZoomFactor() + if err != nil { + return -1 + } + return factor +} + +func (w *windowsWebviewWindow) setZoom(zoom float64) { + w.chromium.PutZoomFactor(zoom) +} + +func (w *windowsWebviewWindow) close() { + // Send WM_CLOSE message to trigger the same flow as clicking the X button + w32.SendMessage(w.hwnd, w32.WM_CLOSE, 0, 0) +} + +func (w *windowsWebviewWindow) zoom() { + // Noop +} + +func (w *windowsWebviewWindow) setHTML(html string) { + // Render the given HTML in the webview window + w.execJS(fmt.Sprintf("document.documentElement.innerHTML = %q;", html)) +} + +// on is used to indicate that a particular event should be listened for +func (w *windowsWebviewWindow) on(_ uint) { + // We don't need to worry about this in Windows as we do not need + // to optimise cgo calls +} + +func (w *windowsWebviewWindow) minimise() { + w32.ShowWindow(w.hwnd, w32.SW_MINIMIZE) +} + +func (w *windowsWebviewWindow) unminimise() { + w.restore() +} + +func (w *windowsWebviewWindow) maximise() { + w32.ShowWindow(w.hwnd, w32.SW_MAXIMIZE) + w.chromium.Focus() +} + +func (w *windowsWebviewWindow) unmaximise() { + w.restore() + w.parent.emit(events.Windows.WindowUnMaximise) +} + +func (w *windowsWebviewWindow) restore() { + w32.ShowWindow(w.hwnd, w32.SW_RESTORE) + w.chromium.Focus() +} + +func (w *windowsWebviewWindow) fullscreen() { + if w.isFullscreen() { + return + } + if w.framelessWithDecorations() { + err := w32.ExtendFrameIntoClientArea(w.hwnd, false) + if err != nil { + globalApplication.handleFatalError(err) + } + } + w.disableSizeConstraints() + w.previousWindowStyle = uint32(w32.GetWindowLongPtr(w.hwnd, w32.GWL_STYLE)) + w.previousWindowExStyle = uint32(w32.GetWindowLong(w.hwnd, w32.GWL_EXSTYLE)) + monitor := w32.MonitorFromWindow(w.hwnd, w32.MONITOR_DEFAULTTOPRIMARY) + var monitorInfo w32.MONITORINFO + monitorInfo.CbSize = uint32(unsafe.Sizeof(monitorInfo)) + if !w32.GetMonitorInfo(monitor, &monitorInfo) { + return + } + if !w32.GetWindowPlacement(w.hwnd, &w.previousWindowPlacement) { + return + } + // According to https://devblogs.microsoft.com/oldnewthing/20050505-04/?p=35703 one should use w32.WS_POPUP | w32.WS_VISIBLE + w32.SetWindowLong( + w.hwnd, + w32.GWL_STYLE, + w.previousWindowStyle & ^uint32(w32.WS_OVERLAPPEDWINDOW) | (w32.WS_POPUP|w32.WS_VISIBLE), + ) + w32.SetWindowLong( + w.hwnd, + w32.GWL_EXSTYLE, + w.previousWindowExStyle & ^uint32(w32.WS_EX_DLGMODALFRAME), + ) + w.isCurrentlyFullscreen = true + w32.SetWindowPos(w.hwnd, w32.HWND_TOP, + int(monitorInfo.RcMonitor.Left), + int(monitorInfo.RcMonitor.Top), + int(monitorInfo.RcMonitor.Right-monitorInfo.RcMonitor.Left), + int(monitorInfo.RcMonitor.Bottom-monitorInfo.RcMonitor.Top), + w32.SWP_NOOWNERZORDER|w32.SWP_FRAMECHANGED) + + // Hide the menubar in fullscreen mode + w32.SetMenu(w.hwnd, 0) + + w.chromium.Focus() + w.parent.emit(events.Windows.WindowFullscreen) +} + +func (w *windowsWebviewWindow) unfullscreen() { + if !w.isFullscreen() { + return + } + if w.framelessWithDecorations() { + err := w32.ExtendFrameIntoClientArea(w.hwnd, true) + if err != nil { + globalApplication.handleFatalError(err) + } + } + w32.SetWindowLong(w.hwnd, w32.GWL_STYLE, w.previousWindowStyle) + w32.SetWindowLong(w.hwnd, w32.GWL_EXSTYLE, w.previousWindowExStyle) + w32.SetWindowPlacement(w.hwnd, &w.previousWindowPlacement) + w.isCurrentlyFullscreen = false + + // Restore the menubar when exiting fullscreen + if w.menu != nil { + w32.SetMenu(w.hwnd, w.menu.menu) + } + + w32.SetWindowPos(w.hwnd, 0, 0, 0, 0, 0, + w32.SWP_NOMOVE|w32.SWP_NOSIZE|w32.SWP_NOZORDER|w32.SWP_NOOWNERZORDER|w32.SWP_FRAMECHANGED) + w.enableSizeConstraints() + w.parent.emit(events.Windows.WindowUnFullscreen) +} + +func (w *windowsWebviewWindow) isMinimised() bool { + style := uint32(w32.GetWindowLong(w.hwnd, w32.GWL_STYLE)) + return style&w32.WS_MINIMIZE != 0 +} + +func (w *windowsWebviewWindow) isMaximised() bool { + style := uint32(w32.GetWindowLong(w.hwnd, w32.GWL_STYLE)) + return style&w32.WS_MAXIMIZE != 0 +} + +func (w *windowsWebviewWindow) isFocused() bool { + // Returns true if the window is currently focused + return w32.GetForegroundWindow() == w.hwnd +} + +func (w *windowsWebviewWindow) isFullscreen() bool { + // TODO: Actually calculate this based on size of window against screen size + // => stffabi: This flag is essential since it indicates that we are in fullscreen mode even before the native properties + // reflect this, e.g. when needing to know if we are in fullscreen during a wndproc message. + // That's also why this flag is set before SetWindowPos in v2 in fullscreen/unfullscreen. + return w.isCurrentlyFullscreen +} + +func (w *windowsWebviewWindow) isNormal() bool { + return !w.isMinimised() && !w.isMaximised() && !w.isFullscreen() +} + +func (w *windowsWebviewWindow) isVisible() bool { + style := uint32(w32.GetWindowLong(w.hwnd, w32.GWL_STYLE)) + return style&w32.WS_VISIBLE != 0 +} + +func (w *windowsWebviewWindow) focus() { + w32.SetForegroundWindow(w.hwnd) + + if w.isDisabled() { + return + } + if w.isMinimised() { + w.unminimise() + } + + w.focusingChromium = true + w.chromium.Focus() + w.focusingChromium = false +} + +// printStyle takes a windows style and prints it in a human-readable format +// This is for debugging window style issues +func (w *windowsWebviewWindow) printStyle() { + style := uint32(w32.GetWindowLong(w.hwnd, w32.GWL_STYLE)) + fmt.Printf("Style: ") + if style&w32.WS_BORDER != 0 { + fmt.Printf("WS_BORDER ") + } + if style&w32.WS_CAPTION != 0 { + fmt.Printf("WS_CAPTION ") + } + if style&w32.WS_CHILD != 0 { + fmt.Printf("WS_CHILD ") + } + if style&w32.WS_CLIPCHILDREN != 0 { + fmt.Printf("WS_CLIPCHILDREN ") + } + if style&w32.WS_CLIPSIBLINGS != 0 { + fmt.Printf("WS_CLIPSIBLINGS ") + } + if style&w32.WS_DISABLED != 0 { + fmt.Printf("WS_DISABLED ") + } + if style&w32.WS_DLGFRAME != 0 { + fmt.Printf("WS_DLGFRAME ") + } + if style&w32.WS_GROUP != 0 { + fmt.Printf("WS_GROUP ") + } + if style&w32.WS_HSCROLL != 0 { + fmt.Printf("WS_HSCROLL ") + } + if style&w32.WS_MAXIMIZE != 0 { + fmt.Printf("WS_MAXIMIZE ") + } + if style&w32.WS_MAXIMIZEBOX != 0 { + fmt.Printf("WS_MAXIMIZEBOX ") + } + if style&w32.WS_MINIMIZE != 0 { + fmt.Printf("WS_MINIMIZE ") + } + if style&w32.WS_MINIMIZEBOX != 0 { + fmt.Printf("WS_MINIMIZEBOX ") + } + if style&w32.WS_OVERLAPPED != 0 { + fmt.Printf("WS_OVERLAPPED ") + } + if style&w32.WS_POPUP != 0 { + fmt.Printf("WS_POPUP ") + } + if style&w32.WS_SYSMENU != 0 { + fmt.Printf("WS_SYSMENU ") + } + if style&w32.WS_TABSTOP != 0 { + fmt.Printf("WS_TABSTOP ") + } + if style&w32.WS_THICKFRAME != 0 { + fmt.Printf("WS_THICKFRAME ") + } + if style&w32.WS_VISIBLE != 0 { + fmt.Printf("WS_VISIBLE ") + } + if style&w32.WS_VSCROLL != 0 { + fmt.Printf("WS_VSCROLL ") + } + fmt.Printf("\n") + + // Do the same for the extended style + extendedStyle := uint32(w32.GetWindowLong(w.hwnd, w32.GWL_EXSTYLE)) + fmt.Printf("Extended Style: ") + if extendedStyle&w32.WS_EX_ACCEPTFILES != 0 { + fmt.Printf("WS_EX_ACCEPTFILES ") + } + if extendedStyle&w32.WS_EX_APPWINDOW != 0 { + fmt.Printf("WS_EX_APPWINDOW ") + } + if extendedStyle&w32.WS_EX_CLIENTEDGE != 0 { + fmt.Printf("WS_EX_CLIENTEDGE ") + } + if extendedStyle&w32.WS_EX_COMPOSITED != 0 { + fmt.Printf("WS_EX_COMPOSITED ") + } + if extendedStyle&w32.WS_EX_CONTEXTHELP != 0 { + fmt.Printf("WS_EX_CONTEXTHELP ") + } + if extendedStyle&w32.WS_EX_CONTROLPARENT != 0 { + fmt.Printf("WS_EX_CONTROLPARENT ") + } + if extendedStyle&w32.WS_EX_DLGMODALFRAME != 0 { + fmt.Printf("WS_EX_DLGMODALFRAME ") + } + if extendedStyle&w32.WS_EX_LAYERED != 0 { + fmt.Printf("WS_EX_LAYERED ") + } + if extendedStyle&w32.WS_EX_LAYOUTRTL != 0 { + fmt.Printf("WS_EX_LAYOUTRTL ") + } + if extendedStyle&w32.WS_EX_LEFT != 0 { + fmt.Printf("WS_EX_LEFT ") + } + if extendedStyle&w32.WS_EX_LEFTSCROLLBAR != 0 { + fmt.Printf("WS_EX_LEFTSCROLLBAR ") + } + if extendedStyle&w32.WS_EX_LTRREADING != 0 { + fmt.Printf("WS_EX_LTRREADING ") + } + if extendedStyle&w32.WS_EX_MDICHILD != 0 { + fmt.Printf("WS_EX_MDICHILD ") + } + if extendedStyle&w32.WS_EX_NOACTIVATE != 0 { + fmt.Printf("WS_EX_NOACTIVATE ") + } + if extendedStyle&w32.WS_EX_NOINHERITLAYOUT != 0 { + fmt.Printf("WS_EX_NOINHERITLAYOUT ") + } + if extendedStyle&w32.WS_EX_NOPARENTNOTIFY != 0 { + fmt.Printf("WS_EX_NOPARENTNOTIFY ") + } + if extendedStyle&w32.WS_EX_NOREDIRECTIONBITMAP != 0 { + fmt.Printf("WS_EX_NOREDIRECTIONBITMAP ") + } + if extendedStyle&w32.WS_EX_OVERLAPPEDWINDOW != 0 { + fmt.Printf("WS_EX_OVERLAPPEDWINDOW ") + } + if extendedStyle&w32.WS_EX_PALETTEWINDOW != 0 { + fmt.Printf("WS_EX_PALETTEWINDOW ") + } + if extendedStyle&w32.WS_EX_RIGHT != 0 { + fmt.Printf("WS_EX_RIGHT ") + } + if extendedStyle&w32.WS_EX_RIGHTSCROLLBAR != 0 { + fmt.Printf("WS_EX_RIGHTSCROLLBAR ") + } + if extendedStyle&w32.WS_EX_RTLREADING != 0 { + fmt.Printf("WS_EX_RTLREADING ") + } + if extendedStyle&w32.WS_EX_STATICEDGE != 0 { + fmt.Printf("WS_EX_STATICEDGE ") + } + if extendedStyle&w32.WS_EX_TOOLWINDOW != 0 { + fmt.Printf("WS_EX_TOOLWINDOW ") + } + if extendedStyle&w32.WS_EX_TOPMOST != 0 { + fmt.Printf("WS_EX_TOPMOST ") + } + if extendedStyle&w32.WS_EX_TRANSPARENT != 0 { + fmt.Printf("WS_EX_TRANSPARENT ") + } + if extendedStyle&w32.WS_EX_WINDOWEDGE != 0 { + fmt.Printf("WS_EX_WINDOWEDGE ") + } + fmt.Printf("\n") + +} + +func (w *windowsWebviewWindow) show() { + // Always show the window container immediately (decouple from WebView state) + // This fixes issue #2861 where efficiency mode prevents window visibility + w32.ShowWindow(w.hwnd, w32.SW_SHOW) + w.windowShown = true + w.showRequested = true + w.updateContentProtection() + + // Show WebView if navigation has completed + if w.webviewNavigationCompleted { + w.chromium.Show() + // Cancel timeout since we can show immediately + if w.visibilityTimeout != nil { + w.visibilityTimeout.Stop() + w.visibilityTimeout = nil + } + } else { + // Start timeout to show WebView if navigation is delayed (fallback for efficiency mode) + if w.visibilityTimeout == nil { + w.visibilityTimeout = time.AfterFunc(3*time.Second, func() { + // Show WebView even if navigation hasn't completed + // This prevents permanent invisibility in efficiency mode + if !w.webviewNavigationCompleted && w.chromium != nil { + w.chromium.Show() + } + w.visibilityTimeout = nil + }) + } + } +} + +func (w *windowsWebviewWindow) hide() { + w32.ShowWindow(w.hwnd, w32.SW_HIDE) + w.windowShown = false + w.showRequested = false + + // Cancel any pending visibility timeout + if w.visibilityTimeout != nil { + w.visibilityTimeout.Stop() + w.visibilityTimeout = nil + } +} + +// Get the screen for the current window +func (w *windowsWebviewWindow) getScreen() (*Screen, error) { + return getScreenForWindow(w) +} + +func (w *windowsWebviewWindow) setFrameless(b bool) { + // Remove or add the frame + if b { + w32.SetWindowLong(w.hwnd, w32.GWL_STYLE, w32.WS_VISIBLE|w32.WS_POPUP) + } else { + w32.SetWindowLong(w.hwnd, w32.GWL_STYLE, w32.WS_VISIBLE|w32.WS_OVERLAPPEDWINDOW) + } + w32.SetWindowPos( + w.hwnd, + 0, + 0, + 0, + 0, + 0, + w32.SWP_NOMOVE|w32.SWP_NOSIZE|w32.SWP_NOZORDER|w32.SWP_FRAMECHANGED, + ) +} + +func newWindowImpl(parent *WebviewWindow) *windowsWebviewWindow { + result := &windowsWebviewWindow{ + parent: parent, + resizeBorderWidth: int32(w32.GetSystemMetrics(w32.SM_CXSIZEFRAME)), + resizeBorderHeight: int32(w32.GetSystemMetrics(w32.SM_CYSIZEFRAME)), + // Initialize visibility tracking fields + showRequested: false, + visibilityTimeout: nil, + windowShown: false, + } + + return result +} + +func (w *windowsWebviewWindow) openContextMenu(menu *Menu, _ *ContextMenuData) { + // Destroy previous context menu if it exists to prevent memory leak + if w.currentlyOpenContextMenu != nil { + w.currentlyOpenContextMenu.Destroy() + } + // Create the menu from current Go-side menu state + thisMenu := NewPopupMenu(w.hwnd, menu) + thisMenu.Update() + w.currentlyOpenContextMenu = thisMenu + thisMenu.ShowAtCursor() +} + +func (w *windowsWebviewWindow) setStyle(b bool, style int) { + currentStyle := int(w32.GetWindowLongPtr(w.hwnd, w32.GWL_STYLE)) + if currentStyle != 0 { + if b { + currentStyle = currentStyle | style + } else { + currentStyle = currentStyle &^ style + } + w32.SetWindowLongPtr(w.hwnd, w32.GWL_STYLE, uintptr(currentStyle)) + } +} +func (w *windowsWebviewWindow) setExStyle(b bool, style int) { + currentStyle := int(w32.GetWindowLongPtr(w.hwnd, w32.GWL_EXSTYLE)) + if currentStyle != 0 { + if b { + currentStyle = currentStyle | style + } else { + currentStyle = currentStyle &^ style + } + w32.SetWindowLongPtr(w.hwnd, w32.GWL_EXSTYLE, uintptr(currentStyle)) + } +} + +func (w *windowsWebviewWindow) setBackdropType(backdropType BackdropType) { + if !w32.SupportsBackdropTypes() { + var accent = w32.ACCENT_POLICY{ + AccentState: w32.ACCENT_ENABLE_BLURBEHIND, + } + var data w32.WINDOWCOMPOSITIONATTRIBDATA + data.Attrib = w32.WCA_ACCENT_POLICY + data.PvData = w32.PVOID(&accent) + data.CbData = unsafe.Sizeof(accent) + + w32.SetWindowCompositionAttribute(w.hwnd, &data) + } else { + w32.EnableTranslucency(w.hwnd, uint32(backdropType)) + } +} + +func (w *windowsWebviewWindow) setIcon(icon w32.HICON) { + w32.SendMessage(w.hwnd, w32.WM_SETICON, w32.ICON_BIG, icon) +} + +func (w *windowsWebviewWindow) disableIcon() { + + // TODO: If frameless, return + exStyle := w32.GetWindowLong(w.hwnd, w32.GWL_EXSTYLE) + w32.SetWindowLong(w.hwnd, w32.GWL_EXSTYLE, uint32(exStyle|w32.WS_EX_DLGMODALFRAME)) + w32.SetWindowPos(w.hwnd, 0, 0, 0, 0, 0, + uint( + w32.SWP_FRAMECHANGED| + w32.SWP_NOMOVE| + w32.SWP_NOSIZE| + w32.SWP_NOZORDER), + ) +} + +func (w *windowsWebviewWindow) processThemeColour(fn func(w32.HWND, uint32), value *uint32) { + if value == nil { + return + } + fn(w.hwnd, *value) +} + +func (w *windowsWebviewWindow) isDisabled() bool { + style := uint32(w32.GetWindowLong(w.hwnd, w32.GWL_STYLE)) + return style&w32.WS_DISABLED != 0 +} + +func (w *windowsWebviewWindow) updateTheme(isDarkMode bool) { + + if w32.IsCurrentlyHighContrastMode() { + return + } + + if !w32.SupportsThemes() { + return + } + + w32.SetTheme(w.hwnd, isDarkMode) + + // Clear any existing theme first + if w.menubarTheme != nil && !isDarkMode { + // Reset menu to default Windows theme when switching to light mode + w.menubarTheme = nil + if w.menu != nil { + // Clear the menu background by setting it to default + var mi w32.MENUINFO + mi.CbSize = uint32(unsafe.Sizeof(mi)) + mi.FMask = w32.MIIM_BACKGROUND | w32.MIIM_APPLYTOSUBMENUS + mi.HbrBack = 0 // NULL brush resets to default + w32.SetMenuInfo(w.menu.menu, &mi) + } + } + + // Custom theme processing + customTheme := w.parent.options.Windows.CustomTheme + // Custom theme + if w32.SupportsCustomThemes() { + var userTheme *MenuBarTheme + if isDarkMode { + userTheme = customTheme.DarkModeMenuBar + } else { + userTheme = customTheme.LightModeMenuBar + } + + if userTheme != nil { + modeStr := "light" + if isDarkMode { + modeStr = "dark" + } + globalApplication.debug("Setting custom "+modeStr+" menubar theme", "window", w.parent.id) + w.menubarTheme = &w32.MenuBarTheme{ + TitleBarBackground: userTheme.Default.Background, + TitleBarText: userTheme.Default.Text, + MenuBarBackground: userTheme.Default.Background, // Use default background for menubar + MenuHoverBackground: userTheme.Hover.Background, + MenuHoverText: userTheme.Hover.Text, + MenuSelectedBackground: userTheme.Selected.Background, + MenuSelectedText: userTheme.Selected.Text, + } + w.menubarTheme.Init() + + // If menu is already set, update it + if w.menu != nil { + w.menubarTheme.SetMenuBackground(w.menu.menu) + w32.DrawMenuBar(w.hwnd) + w32.InvalidateRect(w.hwnd, nil, true) + } + } else if userTheme == nil && isDarkMode { + // Use default dark theme if no custom theme provided + globalApplication.debug("Setting default dark menubar theme", "window", w.parent.id) + w.menubarTheme = &w32.MenuBarTheme{ + TitleBarBackground: w32.RGBptr(45, 45, 45), // Dark titlebar + TitleBarText: w32.RGBptr(222, 222, 222), // Slightly muted white + MenuBarBackground: w32.RGBptr(33, 33, 33), // Standard dark mode (#212121) + MenuHoverBackground: w32.RGBptr(48, 48, 48), // Slightly lighter for hover (#303030) + MenuHoverText: w32.RGBptr(222, 222, 222), // Slightly muted white + MenuSelectedBackground: w32.RGBptr(48, 48, 48), // Same as hover + MenuSelectedText: w32.RGBptr(222, 222, 222), // Slightly muted white + } + w.menubarTheme.Init() + + // If menu is already set, update it + if w.menu != nil { + w.menubarTheme.SetMenuBackground(w.menu.menu) + w32.DrawMenuBar(w.hwnd) + w32.InvalidateRect(w.hwnd, nil, true) + } + } else if userTheme == nil && !isDarkMode && w.menu != nil { + // No custom theme for light mode - ensure menu is reset to default + globalApplication.debug("Resetting menu to default light theme", "window", w.parent.id) + var mi w32.MENUINFO + mi.CbSize = uint32(unsafe.Sizeof(mi)) + mi.FMask = w32.MIIM_BACKGROUND | w32.MIIM_APPLYTOSUBMENUS + mi.HbrBack = 0 // NULL brush resets to default + w32.SetMenuInfo(w.menu.menu, &mi) + w32.DrawMenuBar(w.hwnd) + w32.InvalidateRect(w.hwnd, nil, true) + } + // Define a map for theme selection + themeMap := map[bool]map[bool]*WindowTheme{ + true: { // Window is active + true: customTheme.DarkModeActive, // Dark mode + false: customTheme.LightModeActive, // Light mode + }, + false: { // Window is inactive + true: customTheme.DarkModeInactive, // Dark mode + false: customTheme.LightModeInactive, // Light mode + }, + } + + // Select the appropriate theme + theme := themeMap[w.isActive()][isDarkMode] + + // Apply theme colors + if theme != nil { + w.processThemeColour(w32.SetTitleBarColour, theme.TitleBarColour) + w.processThemeColour(w32.SetTitleTextColour, theme.TitleTextColour) + w.processThemeColour(w32.SetBorderColour, theme.BorderColour) + } + } +} + +func (w *windowsWebviewWindow) isActive() bool { + return w32.GetForegroundWindow() == w.hwnd +} + +var resizePending int32 + +func (w *windowsWebviewWindow) WndProc(msg uint32, wparam, lparam uintptr) uintptr { + + // Use the original implementation that works perfectly for maximized + processed, code := w32.MenuBarWndProc(w.hwnd, msg, wparam, lparam, w.menubarTheme) + if processed { + return code + } + + switch msg { + case w32.WM_ACTIVATE: + if int(wparam&0xffff) == w32.WA_INACTIVE { + w.parent.emit(events.Windows.WindowInactive) + } + if wparam == w32.WA_ACTIVE { + getNativeApplication().currentWindowID = w.parent.id + w.parent.emit(events.Windows.WindowActive) + } + if wparam == w32.WA_CLICKACTIVE { + getNativeApplication().currentWindowID = w.parent.id + w.parent.emit(events.Windows.WindowClickActive) + } + // If we want to have a frameless window but with the default frame decorations, extend the DWM client area. + // This Option is not affected by returning 0 in WM_NCCALCSIZE. + // As a result we have hidden the titlebar but still have the default window frame styling. + // See: https://docs.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmextendframeintoclientarea#remarks + if w.framelessWithDecorations() { + err := w32.ExtendFrameIntoClientArea(w.hwnd, true) + if err != nil { + globalApplication.handleFatalError(err) + } + } + case w32.WM_CLOSE: + + if atomic.LoadUint32(&w.parent.unconditionallyClose) == 0 { + // We were called by `Close()` or pressing the close button on the window + w.parent.emit(events.Windows.WindowClosing) + return 0 + } + + defer func() { + windowsApp := globalApplication.impl.(*windowsApp) + windowsApp.unregisterWindow(w) + + }() + + // Now do the actual close + w.chromium.ShuttingDown() + return w32.DefWindowProc(w.hwnd, w32.WM_CLOSE, 0, 0) + + case w32.WM_KILLFOCUS: + if w.focusingChromium { + return 0 + } + w.parent.emit(events.Windows.WindowKillFocus) + case w32.WM_ENTERSIZEMOVE: + // This is needed to close open dropdowns when moving the window https://github.com/MicrosoftEdge/WebView2Feedback/issues/2290 + w32.SetFocus(w.hwnd) + if int(w32.GetKeyState(w32.VK_LBUTTON))&(0x8000) != 0 { + // Left mouse button is down - window is being moved + w.parent.emit(events.Windows.WindowStartMove) + } else { + // Window is being resized + w.parent.emit(events.Windows.WindowStartResize) + } + case w32.WM_EXITSIZEMOVE: + if int(w32.GetKeyState(w32.VK_LBUTTON))&0x8000 != 0 { + w.parent.emit(events.Windows.WindowEndMove) + } else { + w.parent.emit(events.Windows.WindowEndResize) + } + case w32.WM_SETFOCUS: + w.focus() + w.parent.emit(events.Windows.WindowSetFocus) + case w32.WM_MOVE, w32.WM_MOVING: + _ = w.chromium.NotifyParentWindowPositionChanged() + w.moveDebouncer(func() { + w.parent.emit(events.Windows.WindowDidMove) + }) + case w32.WM_SHOWWINDOW: + if wparam == 1 { + w.parent.emit(events.Windows.WindowShow) + w.updateContentProtection() + } else { + w.parent.emit(events.Windows.WindowHide) + } + case w32.WM_WINDOWPOSCHANGED: + windowPos := (*w32.WINDOWPOS)(unsafe.Pointer(lparam)) + if windowPos.Flags&w32.SWP_NOZORDER == 0 { + w.parent.emit(events.Windows.WindowZOrderChanged) + } + case w32.WM_PAINT: + w.parent.emit(events.Windows.WindowPaint) + case w32.WM_ERASEBKGND: + w.parent.emit(events.Windows.WindowBackgroundErase) + return 1 // Let WebView2 handle background erasing + // WM_UAHDRAWMENUITEM is handled by MenuBarWndProc at the top of this function + // Check for keypress + case w32.WM_SYSCOMMAND: + switch wparam { + case w32.SC_KEYMENU: + if lparam == 0 { + // F10 or plain Alt key + if w.processKeyBinding(w32.VK_F10) { + return 0 + } + } else { + // Alt + key combination + // The character code is in the low word of lparam + char := byte(lparam & 0xFF) + // Convert ASCII to virtual key code if needed + vkey := w32.VkKeyScan(uint16(char)) + if w.processKeyBinding(uint(vkey)) { + return 0 + } + } + } + case w32.WM_SYSKEYDOWN: + globalApplication.debug("w32.WM_SYSKEYDOWN", "wparam", uint(wparam)) + w.parent.emit(events.Windows.WindowKeyDown) + if w.processKeyBinding(uint(wparam)) { + return 0 + } + case w32.WM_SYSKEYUP: + w.parent.emit(events.Windows.WindowKeyUp) + case w32.WM_KEYDOWN: + w.parent.emit(events.Windows.WindowKeyDown) + w.processKeyBinding(uint(wparam)) + case w32.WM_KEYUP: + w.parent.emit(events.Windows.WindowKeyUp) + case w32.WM_SIZE: + switch wparam { + case w32.SIZE_MAXIMIZED: + if w.isMinimizing { + w.parent.emit(events.Windows.WindowUnMinimise) + } + w.isMinimizing = false + w.parent.emit(events.Windows.WindowMaximise) + // Force complete redraw when maximized + if w.menu != nil && w.menubarTheme != nil { + // Invalidate the entire window to force complete redraw + w32.RedrawWindow(w.hwnd, nil, 0, w32.RDW_FRAME|w32.RDW_INVALIDATE|w32.RDW_UPDATENOW) + } + case w32.SIZE_RESTORED: + if w.isMinimizing { + w.parent.emit(events.Windows.WindowUnMinimise) + } + w.isMinimizing = false + w.parent.emit(events.Windows.WindowRestore) + case w32.SIZE_MINIMIZED: + w.isMinimizing = true + w.parent.emit(events.Windows.WindowMinimise) + } + + doResize := func() { + // Get the new size from lparam + width := int32(lparam & 0xFFFF) + height := int32((lparam >> 16) & 0xFFFF) + bounds := &edge.Rect{ + Left: 0, + Top: 0, + Right: width, + Bottom: height, + } + InvokeSync(func() { + time.Sleep(1 * time.Nanosecond) + w.chromium.ResizeWithBounds(bounds) + atomic.StoreInt32(&resizePending, 0) + w.parent.emit(events.Windows.WindowDidResize) + }) + } + + if w.parent.options.Frameless && wparam == w32.SIZE_MINIMIZED { + // If the window is frameless, and we are minimizing, then we need to suppress the Resize on the + // WebView2. If we don't do this, restoring does not work as expected and first restores with some wrong + // size during the restore animation and only fully renders when the animation is done. This highly + // depends on the content in the WebView, see https://github.com/MicrosoftEdge/WebView2Feedback/issues/2549 + } else if w.resizeDebouncer != nil { + w.resizeDebouncer(doResize) + } else { + if atomic.CompareAndSwapInt32(&resizePending, 0, 1) { + doResize() + } + } + return 0 + + case w32.WM_GETMINMAXINFO: + mmi := (*w32.MINMAXINFO)(unsafe.Pointer(lparam)) + hasConstraints := false + options := w.parent.options + // Using ScreenManager to get the closest screen and scale according to its DPI is problematic + // here because in multi-monitor setup, when dragging the window between monitors with the mouse + // on the side with the higher DPI, the DPI change point is offset beyond the mid point, causing + // wrong scaling and unwanted resizing when using the monitor DPI. To avoid this issue, we use + // scaleWithWindowDPI() instead which retrieves the correct DPI with GetDpiForWindow(). + if options.MinWidth > 0 || options.MinHeight > 0 { + hasConstraints = true + + width, height := w.scaleWithWindowDPI(options.MinWidth, options.MinHeight) + if width > 0 { + mmi.PtMinTrackSize.X = int32(width) + } + if height > 0 { + mmi.PtMinTrackSize.Y = int32(height) + } + } + if options.MaxWidth > 0 || options.MaxHeight > 0 { + hasConstraints = true + + width, height := w.scaleWithWindowDPI(options.MaxWidth, options.MaxHeight) + if width > 0 { + mmi.PtMaxTrackSize.X = int32(width) + } + if height > 0 { + mmi.PtMaxTrackSize.Y = int32(height) + } + } + if hasConstraints { + return 0 + } + + case w32.WM_DPICHANGED: + if !w.ignoreDPIChangeResizing { + newWindowRect := (*w32.RECT)(unsafe.Pointer(lparam)) + w32.SetWindowPos(w.hwnd, + uintptr(0), + int(newWindowRect.Left), + int(newWindowRect.Top), + int(newWindowRect.Right-newWindowRect.Left), + int(newWindowRect.Bottom-newWindowRect.Top), + w32.SWP_NOZORDER|w32.SWP_NOACTIVATE) + } + w.parent.emit(events.Windows.WindowDPIChanged) + } + + if w.parent.options.Windows.WindowMask != nil { + switch msg { + case w32.WM_NCHITTEST: + if w.parent.options.Windows.WindowMaskDraggable { + return w32.HTCAPTION + } + w.parent.emit(events.Windows.WindowNonClientHit) + return w32.HTCLIENT + case w32.WM_NCLBUTTONDOWN: + w.parent.emit(events.Windows.WindowNonClientMouseDown) + case w32.WM_NCLBUTTONUP: + w.parent.emit(events.Windows.WindowNonClientMouseUp) + case w32.WM_NCMOUSEMOVE: + w.parent.emit(events.Windows.WindowNonClientMouseMove) + case w32.WM_NCMOUSELEAVE: + w.parent.emit(events.Windows.WindowNonClientMouseLeave) + } + } + + if w.menu != nil || w.currentlyOpenContextMenu != nil { + switch msg { + case w32.WM_COMMAND: + cmdMsgID := int(wparam & 0xffff) + switch cmdMsgID { + default: + var processed bool + if w.currentlyOpenContextMenu != nil { + processed = w.currentlyOpenContextMenu.ProcessCommand(cmdMsgID) + w.currentlyOpenContextMenu = nil + + } + if !processed && w.menu != nil { + processed = w.menu.ProcessCommand(cmdMsgID) + } + } + } + } + + if options := w.parent.options; options.Frameless { + switch msg { + case w32.WM_ACTIVATE: + // If we want to have a frameless window but with the default frame decorations, extend the DWM client area. + // This Option is not affected by returning 0 in WM_NCCALCSIZE. + // As a result we have hidden the titlebar but still have the default window frame styling. + // See: https://docs.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmextendframeintoclientarea#remarks + if w.framelessWithDecorations() { + err := w32.ExtendFrameIntoClientArea(w.hwnd, true) + if err != nil { + globalApplication.handleFatalError(err) + } + } + + case w32.WM_NCCALCSIZE: + // Disable the standard frame by allowing the client area to take the full + // window size. + // See: https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-nccalcsize#remarks + // This hides the titlebar and also disables the resizing from user interaction because the standard frame is not + // shown. We still need the WS_THICKFRAME style to enable resizing from the frontend. + if wparam != 0 { + rgrc := (*w32.RECT)(unsafe.Pointer(lparam)) + if w.isCurrentlyFullscreen { + // In Full-Screen mode we don't need to adjust anything + // It essential we have the flag here, that is set before SetWindowPos in fullscreen/unfullscreen + // because the native size might not yet reflect we are in fullscreen during this event! + w.setPadding(edge.Rect{}) + } else if w.isMaximised() { + // If the window is maximized we must adjust the client area to the work area of the monitor. Otherwise + // some content goes beyond the visible part of the monitor. + // Make sure to use the provided RECT to get the monitor, because during maximizig there might be + // a wrong monitor returned in multiscreen mode when using MonitorFromWindow. + // See: https://github.com/MicrosoftEdge/WebView2Feedback/issues/2549 + screen := ScreenNearestPhysicalRect(Rect{ + X: int(rgrc.Left), + Y: int(rgrc.Top), + Width: int(rgrc.Right - rgrc.Left), + Height: int(rgrc.Bottom - rgrc.Top), + }) + + rect := screen.PhysicalWorkArea + + maxWidth := options.MaxWidth + maxHeight := options.MaxHeight + + if maxWidth > 0 { + maxWidth = screen.scale(maxWidth, false) + if rect.Width > maxWidth { + rect.Width = maxWidth + } + } + + if maxHeight > 0 { + maxHeight = screen.scale(maxHeight, false) + if rect.Height > maxHeight { + rect.Height = maxHeight + } + } + + *rgrc = w32.RECT{ + Left: int32(rect.X), + Top: int32(rect.Y), + Right: int32(rect.X + rect.Width), + Bottom: int32(rect.Y + rect.Height), + } + w.setPadding(edge.Rect{}) + } else { + // This is needed to work around the resize flickering in frameless mode with WindowDecorations + // See: https://stackoverflow.com/a/6558508 + // The workaround from the SO answer suggests to reduce the bottom of the window by 1px. + // However, this would result in losing 1px of the WebView content. + // Increasing the bottom also worksaround the flickering, but we would lose 1px of the WebView content + // therefore let's pad the content with 1px at the bottom. + rgrc.Bottom += 1 + w.setPadding(edge.Rect{Bottom: 1}) + } + return 0 + } + } + } + return w32.DefWindowProc(w.hwnd, msg, wparam, lparam) +} + +func (w *windowsWebviewWindow) DPI() (w32.UINT, w32.UINT) { + if w32.HasGetDpiForWindowFunc() { + // GetDpiForWindow is supported beginning with Windows 10, 1607 and is the most accurate + // one, especially it is consistent with the WM_DPICHANGED event. + dpi := w32.GetDpiForWindow(w.hwnd) + return dpi, dpi + } + + if w32.HasGetDPIForMonitorFunc() { + // GetDpiForWindow is supported beginning with Windows 8.1 + monitor := w32.MonitorFromWindow(w.hwnd, w32.MONITOR_DEFAULTTONEAREST) + if monitor == 0 { + return 0, 0 + } + var dpiX, dpiY w32.UINT + w32.GetDPIForMonitor(monitor, w32.MDT_EFFECTIVE_DPI, &dpiX, &dpiY) + return dpiX, dpiY + } + + // If none of the above is supported fallback to the System DPI. + screen := w32.GetDC(0) + x := w32.GetDeviceCaps(screen, w32.LOGPIXELSX) + y := w32.GetDeviceCaps(screen, w32.LOGPIXELSY) + w32.ReleaseDC(0, screen) + return w32.UINT(x), w32.UINT(y) +} + +func (w *windowsWebviewWindow) scaleWithWindowDPI(width, height int) (int, int) { + dpix, dpiy := w.DPI() + scaledWidth := ScaleWithDPI(width, dpix) + scaledHeight := ScaleWithDPI(height, dpiy) + + return scaledWidth, scaledHeight +} + +func ScaleWithDPI(pixels int, dpi uint) int { + return (pixels * int(dpi)) / 96 +} + +func (w *windowsWebviewWindow) setWindowMask(imageData []byte) { + + // Set the window to a WS_EX_LAYERED window + newStyle := w32.GetWindowLong(w.hwnd, w32.GWL_EXSTYLE) | w32.WS_EX_LAYERED + + if w.isAlwaysOnTop() { + newStyle |= w32.WS_EX_TOPMOST + } + // Save the current window style + w.previousWindowExStyle = uint32(w32.GetWindowLong(w.hwnd, w32.GWL_EXSTYLE)) + + w32.SetWindowLong(w.hwnd, w32.GWL_EXSTYLE, uint32(newStyle)) + + data, err := pngToImage(imageData) + if err != nil { + globalApplication.fatal("fatal error in callback setWindowMask: %w", err) + } + + bitmap, err := w32.CreateHBITMAPFromImage(data) + hdc := w32.CreateCompatibleDC(0) + defer w32.DeleteDC(hdc) + + oldBitmap := w32.SelectObject(hdc, bitmap) + defer w32.SelectObject(hdc, oldBitmap) + + screenDC := w32.GetDC(0) + defer w32.ReleaseDC(0, screenDC) + + size := w32.SIZE{CX: int32(data.Bounds().Dx()), CY: int32(data.Bounds().Dy())} + ptSrc := w32.POINT{X: 0, Y: 0} + ptDst := w32.POINT{X: int32(w.width()), Y: int32(w.height())} + blend := w32.BLENDFUNCTION{ + BlendOp: w32.AC_SRC_OVER, + BlendFlags: 0, + SourceConstantAlpha: 255, + AlphaFormat: w32.AC_SRC_ALPHA, + } + w32.UpdateLayeredWindow(w.hwnd, screenDC, &ptDst, &size, hdc, &ptSrc, 0, &blend, w32.ULW_ALPHA) +} + +func (w *windowsWebviewWindow) isAlwaysOnTop() bool { + return w32.GetWindowLong(w.hwnd, w32.GWL_EXSTYLE)&w32.WS_EX_TOPMOST != 0 +} + +// processMessage is given a message sent from JS via the postMessage API +// We put it on the global window message buffer to be processed centrally +func (w *windowsWebviewWindow) processMessage(message string, sender *edge.ICoreWebView2, args *edge.ICoreWebView2WebMessageReceivedEventArgs) { + topSource, err := sender.GetSource() + if err != nil { + globalApplication.error("Unable to get source from sender: %s", err.Error()) + topSource = "" + } + + senderSource, err := args.GetSource() + if err != nil { + globalApplication.error("Unable to get source from args: %s", err.Error()) + senderSource = "" + } + + // We send all messages to the centralised window message buffer + windowMessageBuffer <- &windowMessage{ + windowId: w.parent.id, + message: message, + originInfo: &OriginInfo{ + Origin: senderSource, + TopOrigin: topSource, + }, + } +} + +func (w *windowsWebviewWindow) processRequest( + req *edge.ICoreWebView2WebResourceRequest, + args *edge.ICoreWebView2WebResourceRequestedEventArgs, +) { + + // Setting the UserAgent on the CoreWebView2Settings clears the whole default UserAgent of the Edge browser, but + // we want to just append our ApplicationIdentifier. So we adjust the UserAgent for every request. + if reqHeaders, err := req.GetHeaders(); err == nil { + useragent, _ := reqHeaders.GetHeader(assetserver.HeaderUserAgent) + useragent = strings.Join([]string{useragent, assetserver.WailsUserAgentValue}, " ") + err = reqHeaders.SetHeader(assetserver.HeaderUserAgent, useragent) + if err != nil { + globalApplication.fatal("error setting UserAgent header: %w", err) + } + err = reqHeaders.SetHeader( + webViewRequestHeaderWindowId, + strconv.FormatUint(uint64(w.parent.id), 10), + ) + if err != nil { + globalApplication.fatal("error setting WindowId header: %w", err) + } + err = reqHeaders.Release() + if err != nil { + globalApplication.fatal("error releasing headers: %w", err) + } + } + + if globalApplication.assets == nil { + // We are using the devServer let the WebView2 handle the request with its default handler + return + } + + //Get the request + uri, _ := req.GetUri() + reqUri, err := url.ParseRequestURI(uri) + if err != nil { + globalApplication.error("unable to parse request uri: uri='%s' error='%w'", uri, err) + return + } + + if reqUri.Scheme != "http" { + // Let the WebView2 handle the request with its default handler + return + } else if !strings.HasPrefix(reqUri.Host, "wails.localhost") { + // Let the WebView2 handle the request with its default handler + return + } + + webviewRequest, err := webview.NewRequest( + w.chromium.Environment(), + args, + func(fn func()) { + InvokeSync(fn) + }) + if err != nil { + globalApplication.error("%s: NewRequest failed: %w", uri, err) + return + } + + webviewRequests <- &webViewAssetRequest{ + Request: webviewRequest, + windowId: w.parent.id, + windowName: w.parent.options.Name, + } +} + +func (w *windowsWebviewWindow) setupChromium() { + chromium := w.chromium + debugMode := globalApplication.isDebugMode + + opts := w.parent.options.Windows + + webview2version, err := webviewloader.GetAvailableCoreWebView2BrowserVersionString( + globalApplication.options.Windows.WebviewBrowserPath, + ) + if err != nil { + globalApplication.error("error getting WebView2 version: %w", err) + return + } + globalApplication.capabilities = capabilities.NewCapabilities(webview2version) + + // Browser flags apply globally to the shared WebView2 environment + // Use application-level options, not per-window options + appOpts := globalApplication.options.Windows + + // We disable this by default. Can be overridden with the `EnableFraudulentWebsiteWarnings` option + disabledFeatures := append([]string{"msSmartScreenProtection"}, appOpts.DisabledFeatures...) + + if len(disabledFeatures) > 0 { + disabledFeatures = sliceutil.Unique(disabledFeatures) + arg := fmt.Sprintf("--disable-features=%s", strings.Join(disabledFeatures, ",")) + chromium.AdditionalBrowserArgs = append(chromium.AdditionalBrowserArgs, arg) + } + + if len(appOpts.EnabledFeatures) > 0 { + enabledFeatures := sliceutil.Unique(appOpts.EnabledFeatures) + arg := fmt.Sprintf("--enable-features=%s", strings.Join(enabledFeatures, ",")) + chromium.AdditionalBrowserArgs = append(chromium.AdditionalBrowserArgs, arg) + } + + if len(appOpts.AdditionalBrowserArgs) > 0 { + chromium.AdditionalBrowserArgs = append(chromium.AdditionalBrowserArgs, appOpts.AdditionalBrowserArgs...) + } + + chromium.DataPath = globalApplication.options.Windows.WebviewUserDataPath + chromium.BrowserPath = globalApplication.options.Windows.WebviewBrowserPath + + if opts.Permissions != nil { + for permission, state := range opts.Permissions { + chromium.SetPermission(edge.CoreWebView2PermissionKind(permission), + edge.CoreWebView2PermissionState(state)) + } + } + + chromium.MessageCallback = w.processMessage + chromium.MessageWithAdditionalObjectsCallback = w.processMessageWithAdditionalObjects + chromium.WebResourceRequestedCallback = w.processRequest + chromium.ContainsFullScreenElementChangedCallback = w.fullscreenChanged + chromium.NavigationCompletedCallback = w.navigationCompleted + chromium.AcceleratorKeyCallback = w.processKeyBinding + + chromium.Embed(w.hwnd) + + // Prevent efficiency mode by keeping WebView2 visible (fixes issue #2861) + // Microsoft recommendation: keep IsVisible = true to avoid efficiency mode + // See: https://github.com/MicrosoftEdge/WebView2Feedback/discussions/4021 + // TODO: Re-enable when PutIsVisible method is available in go-webview2 package + // err := chromium.PutIsVisible(true) + // if err != nil { + // globalApplication.error("Failed to set WebView2 visibility for efficiency mode prevention: %v", err) + // } + + if chromium.HasCapability(edge.SwipeNavigation) { + err := chromium.PutIsSwipeNavigationEnabled(opts.EnableSwipeGestures) + if err != nil { + globalApplication.handleFatalError(err) + } + } + + // File drop handling on Windows: + // WebView2's AllowExternalDrop controls ALL drag-and-drop (both external file drops + // AND internal HTML5 drag-and-drop). We cannot disable it without breaking HTML5 DnD. + // + // When EnableFileDrop is true: + // - JS dragenter/dragover/drop events fire for external file drags + // - JS calls preventDefault() to stop the browser from navigating to the file + // - JS uses chrome.webview.postMessageWithAdditionalObjects to send file paths to Go + // - Go receives paths via processMessageWithAdditionalObjects + // + // When EnableFileDrop is false: + // - We cannot use AllowExternalDrag(false) as it breaks HTML5 internal drag-and-drop + // - JS runtime checks window._wails.flags.enableFileDrop and shows "no drop" cursor + // - The enableFileDrop flag is injected in navigationCompleted callback + + err = chromium.PutIsGeneralAutofillEnabled(opts.GeneralAutofillEnabled) + if err != nil { + if errors.Is(err, edge.UnsupportedCapabilityError) { + globalApplication.warning("unsupported capability: GeneralAutofillEnabled") + } else { + globalApplication.handleFatalError(err) + } + } + + err = chromium.PutIsPasswordAutosaveEnabled(opts.PasswordAutosaveEnabled) + if err != nil { + if errors.Is(err, edge.UnsupportedCapabilityError) { + globalApplication.warning("unsupported capability: PasswordAutosaveEnabled") + } else { + globalApplication.handleFatalError(err) + } + } + + chromium.Resize() + settings, err := chromium.GetSettings() + if err != nil { + globalApplication.handleFatalError(err) + } + if settings == nil { + globalApplication.fatal("error getting settings") + } + err = settings.PutAreDefaultContextMenusEnabled( + debugMode || !w.parent.options.DefaultContextMenuDisabled, + ) + if err != nil { + globalApplication.handleFatalError(err) + } + + w.enableDevTools(settings) + + if w.parent.options.Zoom > 0.0 { + chromium.PutZoomFactor(w.parent.options.Zoom) + } + err = settings.PutIsZoomControlEnabled(w.parent.options.ZoomControlEnabled) + if err != nil { + globalApplication.handleFatalError(err) + } + + err = settings.PutIsStatusBarEnabled(false) + if err != nil { + globalApplication.handleFatalError(err) + } + err = settings.PutAreBrowserAcceleratorKeysEnabled(false) + if err != nil { + globalApplication.handleFatalError(err) + } + err = settings.PutIsSwipeNavigationEnabled(false) + if err != nil { + globalApplication.handleFatalError(err) + } + + if debugMode && w.parent.options.OpenInspectorOnStartup { + chromium.OpenDevToolsWindow() + } + + // Set background colour + w.setBackgroundColour(w.parent.options.BackgroundColour) + chromium.SetBackgroundColour( + w.parent.options.BackgroundColour.Red, + w.parent.options.BackgroundColour.Green, + w.parent.options.BackgroundColour.Blue, + w.parent.options.BackgroundColour.Alpha, + ) + + chromium.SetGlobalPermission(edge.CoreWebView2PermissionStateAllow) + chromium.AddWebResourceRequestedFilter("*", edge.COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL) + + if w.parent.options.HTML != "" { + var script string + if w.parent.options.JS != "" { + script = w.parent.options.JS + } + if w.parent.options.CSS != "" { + script += fmt.Sprintf( + "; addEventListener(\"DOMContentLoaded\", (event) => { document.head.appendChild(document.createElement('style')).innerHTML=\"%s\"; });", + strings.ReplaceAll(w.parent.options.CSS, `"`, `\"`), + ) + } + if script != "" { + chromium.Init(script) + } + chromium.NavigateToString(w.parent.options.HTML) + } else { + startURL, err := assetserver.GetStartURL(w.parent.options.URL) + if err != nil { + globalApplication.handleFatalError(err) + } + w.webviewNavigationCompleted = false + chromium.Navigate(startURL) + } + +} + +func (w *windowsWebviewWindow) fullscreenChanged( + sender *edge.ICoreWebView2, + _ *edge.ICoreWebView2ContainsFullScreenElementChangedEventArgs, +) { + isFullscreen, err := sender.GetContainsFullScreenElement() + if err != nil { + globalApplication.fatal("fatal error in callback fullscreenChanged: %w", err) + } + if isFullscreen { + w.fullscreen() + } else { + w.unfullscreen() + } +} + +func (w *windowsWebviewWindow) flash(enabled bool) { + w32.FlashWindow(w.hwnd, enabled) +} + +func (w *windowsWebviewWindow) navigationCompleted( + sender *edge.ICoreWebView2, + args *edge.ICoreWebView2NavigationCompletedEventArgs, +) { + + // Install the runtime core + w.execJS(runtime.Core(globalApplication.impl.GetFlags(globalApplication.options))) + + // Set the EnableFileDrop flag for this window (Windows-specific) + // The JS runtime checks this before processing file drops + w.execJS(fmt.Sprintf("window._wails.flags.enableFileDrop = %v;", w.parent.options.EnableFileDrop)) + + // EmitEvent DomReady ApplicationEvent + windowEvents <- &windowEvent{EventID: uint(events.Windows.WebViewNavigationCompleted), WindowID: w.parent.id} + + if w.webviewNavigationCompleted { + // NavigationCompleted is triggered for every Load. If an application uses reloads the Hide/Show will trigger + // a flickering of the window with every reload. So we only do this once for the first NavigationCompleted. + return + } + w.webviewNavigationCompleted = true + + // Cancel any pending visibility timeout since navigation completed + if w.visibilityTimeout != nil { + w.visibilityTimeout.Stop() + w.visibilityTimeout = nil + } + + wasFocused := w.isFocused() + // Hack to make it visible: https://github.com/MicrosoftEdge/WebView2Feedback/issues/1077#issuecomment-825375026 + err := w.chromium.Hide() + if err != nil { + globalApplication.handleFatalError(err) + } + err = w.chromium.Show() + if err != nil { + globalApplication.handleFatalError(err) + } + if wasFocused { + w.focus() + } + + // Only call parent.Show() if not hidden and show was requested but window wasn't shown yet + // The new robust show() method handles window visibility independently + if !w.parent.options.Hidden { + if w.showRequested && !w.windowShown { + w.parent.Show() + } + w.update() + } +} + +func (w *windowsWebviewWindow) processKeyBinding(vkey uint) bool { + + globalApplication.debug("Processing key binding", "vkey", vkey) + + // Get the keyboard state and convert to an accelerator + var keyState [256]byte + if !w32.GetKeyboardState(keyState[:]) { + globalApplication.error("error getting keyboard state") + return false + } + + var acc accelerator + // Check if CTRL is pressed + if keyState[w32.VK_CONTROL]&0x80 != 0 { + acc.Modifiers = append(acc.Modifiers, ControlKey) + } + // Check if ALT is pressed + if keyState[w32.VK_MENU]&0x80 != 0 { + acc.Modifiers = append(acc.Modifiers, OptionOrAltKey) + } + // Check if SHIFT is pressed + if keyState[w32.VK_SHIFT]&0x80 != 0 { + acc.Modifiers = append(acc.Modifiers, ShiftKey) + } + // Check if WIN is pressed + if keyState[w32.VK_LWIN]&0x80 != 0 || keyState[w32.VK_RWIN]&0x80 != 0 { + acc.Modifiers = append(acc.Modifiers, SuperKey) + } + + if vkey != w32.VK_CONTROL && vkey != w32.VK_MENU && vkey != w32.VK_SHIFT && + vkey != w32.VK_LWIN && + vkey != w32.VK_RWIN { + // Convert the vkey to a string + accKey, ok := VirtualKeyCodes[vkey] + if !ok { + return false + } + acc.Key = accKey + } + + accKey := acc.String() + globalApplication.debug("Processing key binding", "vkey", vkey, "acc", accKey) + + // Process the key binding + if w.parent.processKeyBinding(accKey) { + return true + } + + if accKey == "alt+f4" { + w32.PostMessage(w.hwnd, w32.WM_CLOSE, 0, 0) + return true + } + + return false +} + +func (w *windowsWebviewWindow) processMessageWithAdditionalObjects( + message string, + sender *edge.ICoreWebView2, + args *edge.ICoreWebView2WebMessageReceivedEventArgs, +) { + if strings.HasPrefix(message, "file:drop:") { + objs, err := args.GetAdditionalObjects() + if err != nil { + globalApplication.handleError(err) + return + } + + defer func() { + err = objs.Release() + if err != nil { + globalApplication.error("error releasing objects: %w", err) + } + }() + + count, err := objs.GetCount() + if err != nil { + globalApplication.error("cannot get count: %w", err) + return + } + + var filenames []string + for i := uint32(0); i < count; i++ { + _file, err := objs.GetValueAtIndex(i) + if err != nil { + globalApplication.error("cannot get value at %d: %w", i, err) + return + } + + file := (*edge.ICoreWebView2File)(unsafe.Pointer(_file)) + + // TODO: Fix this + defer file.Release() + + filepath, err := file.GetPath() + if err != nil { + globalApplication.error("cannot get path for object at %d: %w", i, err) + return + } + + filenames = append(filenames, filepath) + } + + // Extract X/Y coordinates from message - format is "file:drop:x:y" + var x, y int + parts := strings.Split(message, ":") + if len(parts) >= 4 { + if parsedX, err := strconv.Atoi(parts[2]); err == nil { + x = parsedX + } + if parsedY, err := strconv.Atoi(parts[3]); err == nil { + y = parsedY + } + } + + globalApplication.debug("[DragDropDebug] processMessageWithAdditionalObjects: Raw WebView2 coordinates", "x", x, "y", y) + + // Convert webview-relative coordinates to window-relative coordinates, then to webview-relative coordinates + // Note: The coordinates from WebView2 are already webview-relative, but let's log them for debugging + webviewX, webviewY := x, y + + globalApplication.debug("[DragDropDebug] processMessageWithAdditionalObjects: Using coordinates as-is (already webview-relative)", "webviewX", webviewX, "webviewY", webviewY) + + w.parent.InitiateFrontendDropProcessing(filenames, webviewX, webviewY) + return + } +} + +func (w *windowsWebviewWindow) setMaximiseButtonEnabled(enabled bool) { + w.setStyle(enabled, w32.WS_MAXIMIZEBOX) +} + +func (w *windowsWebviewWindow) setMinimiseButtonEnabled(enabled bool) { + w.setStyle(enabled, w32.WS_MINIMIZEBOX) +} + +func (w *windowsWebviewWindow) toggleMenuBar() { + if w.menu != nil { + if w32.GetMenu(w.hwnd) == 0 { + w32.SetMenu(w.hwnd, w.menu.menu) + } else { + w32.SetMenu(w.hwnd, 0) + } + + // Get the bounds of the client area + //bounds := w32.GetClientRect(w.hwnd) + + // Resize the webview + w.chromium.Resize() + + // Update size of webview + w.update() + // Restore focus to the webview after toggling menu + w.focus() + } +} + +func (w *windowsWebviewWindow) enableRedraw() { + w32.SendMessage(w.hwnd, w32.WM_SETREDRAW, 1, 0) + w32.RedrawWindow( + w.hwnd, + nil, + 0, + w32.RDW_ERASE|w32.RDW_FRAME|w32.RDW_INVALIDATE|w32.RDW_ALLCHILDREN, + ) +} + +func (w *windowsWebviewWindow) disableRedraw() { + w32.SendMessage(w.hwnd, w32.WM_SETREDRAW, 0, 0) +} + +func (w *windowsWebviewWindow) disableRedrawWithCallback(callback func()) { + w.disableRedraw() + callback() + w.enableRedraw() + +} + +func NewIconFromResource(instance w32.HINSTANCE, resId uint16) (w32.HICON, error) { + var err error + var result w32.HICON + if result = w32.LoadIconWithResourceID(instance, resId); result == 0 { + err = fmt.Errorf("cannot load icon from resource with id %v", resId) + } + return result, err +} + +func (w *windowsWebviewWindow) setMinimiseButtonState(state ButtonState) { + switch state { + case ButtonDisabled, ButtonHidden: + w.setStyle(false, w32.WS_MINIMIZEBOX) + case ButtonEnabled: + w.setStyle(true, w32.WS_SYSMENU) + w.setStyle(true, w32.WS_MINIMIZEBOX) + + } +} + +func (w *windowsWebviewWindow) setMaximiseButtonState(state ButtonState) { + switch state { + case ButtonDisabled, ButtonHidden: + w.setStyle(false, w32.WS_MAXIMIZEBOX) + case ButtonEnabled: + w.setStyle(true, w32.WS_SYSMENU) + w.setStyle(true, w32.WS_MAXIMIZEBOX) + } +} + +func (w *windowsWebviewWindow) setCloseButtonState(state ButtonState) { + switch state { + case ButtonEnabled: + w.setStyle(true, w32.WS_SYSMENU) + _ = w32.EnableCloseButton(w.hwnd) + case ButtonDisabled: + w.setStyle(true, w32.WS_SYSMENU) + _ = w32.DisableCloseButton(w.hwnd) + case ButtonHidden: + w.setStyle(false, w32.WS_SYSMENU) + } +} + +func (w *windowsWebviewWindow) setGWLStyle(style int) { + w32.SetWindowLong(w.hwnd, w32.GWL_STYLE, uint32(style)) +} + +func (w *windowsWebviewWindow) isIgnoreMouseEvents() bool { + exStyle := w32.GetWindowLong(w.hwnd, w32.GWL_EXSTYLE) + return exStyle&w32.WS_EX_TRANSPARENT != 0 +} + +func (w *windowsWebviewWindow) setIgnoreMouseEvents(ignore bool) { + exStyle := w32.GetWindowLong(w.hwnd, w32.GWL_EXSTYLE) + if ignore { + exStyle |= w32.WS_EX_LAYERED | w32.WS_EX_TRANSPARENT + } else { + exStyle &^= w32.WS_EX_TRANSPARENT + } + w32.SetWindowLong(w.hwnd, w32.GWL_EXSTYLE, uint32(exStyle)) +} + +func (w *windowsWebviewWindow) setPadding(padding edge.Rect) { + // Skip SetPadding if window is being minimized to prevent flickering + if w.isMinimizing { + return + } + w.chromium.SetPadding(padding) +} + +func (w *windowsWebviewWindow) showMenuBar() { + if w.menu != nil { + w32.SetMenu(w.hwnd, w.menu.menu) + } +} + +func (w *windowsWebviewWindow) hideMenuBar() { + if w.menu != nil { + w32.SetMenu(w.hwnd, 0) + } +} + +func (w *windowsWebviewWindow) snapAssist() { + // Simulate Win+Z key combination to trigger Snap Assist + // Press Windows key + w32.KeybdEvent(byte(w32.VK_LWIN), 0, 0, 0) + // Press Z key + w32.KeybdEvent(byte('Z'), 0, 0, 0) + // Release Z key + w32.KeybdEvent(byte('Z'), 0, w32.KEYEVENTF_KEYUP, 0) + // Release Windows key + w32.KeybdEvent(byte(w32.VK_LWIN), 0, w32.KEYEVENTF_KEYUP, 0) +} + +func (w *windowsWebviewWindow) setContentProtection(enabled bool) { + // Ensure the option reflects the requested state for future show() calls + w.parent.options.ContentProtectionEnabled = enabled + w.updateContentProtection() +} + +func (w *windowsWebviewWindow) updateContentProtection() { + if w.hwnd == 0 { + return + } + + if !w.isVisible() { + // Defer updates until the window is visible to avoid affinity glitches. + return + } + + desired := w.parent.options.ContentProtectionEnabled + + if desired { + if w.applyDisplayAffinity(w32.WDA_EXCLUDEFROMCAPTURE) { + w.contentProtectionApplied = true + } + return + } + + if w.applyDisplayAffinity(w32.WDA_NONE) { + w.contentProtectionApplied = false + } +} + +func (w *windowsWebviewWindow) applyDisplayAffinity(affinity uint32) bool { + if ok := w32.SetWindowDisplayAffinity(w.hwnd, affinity); !ok { + // Note: wrapper already falls back to WDA_MONITOR on older Windows. + globalApplication.warning("SetWindowDisplayAffinity failed: window=%v, affinity=%v", w.parent.id, affinity) + return false + } + return true +} diff --git a/v3/pkg/w32/actions.go b/v3/pkg/w32/actions.go new file mode 100644 index 000000000..2d376c7f4 --- /dev/null +++ b/v3/pkg/w32/actions.go @@ -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) +} diff --git a/v3/pkg/w32/clipboard.go b/v3/pkg/w32/clipboard.go new file mode 100644 index 000000000..89334c0a4 --- /dev/null +++ b/v3/pkg/w32/clipboard.go @@ -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 +} diff --git a/v3/pkg/w32/com.go b/v3/pkg/w32/com.go new file mode 100644 index 000000000..35b79e535 --- /dev/null +++ b/v3/pkg/w32/com.go @@ -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...) +} diff --git a/v3/pkg/w32/comctl32.go b/v3/pkg/w32/comctl32.go new file mode 100644 index 000000000..b66709f5f --- /dev/null +++ b/v3/pkg/w32/comctl32.go @@ -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 +} diff --git a/v3/pkg/w32/comdlg32.go b/v3/pkg/w32/comdlg32.go new file mode 100644 index 000000000..d28922c33 --- /dev/null +++ b/v3/pkg/w32/comdlg32.go @@ -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) +} diff --git a/v3/pkg/w32/constants.go b/v3/pkg/w32/constants.go new file mode 100644 index 000000000..50085a6e6 --- /dev/null +++ b/v3/pkg/w32/constants.go @@ -0,0 +1,3731 @@ +//go:build windows + +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved. + */ +package w32 + +const ( + FALSE = 0 + TRUE = 1 +) + +const ( + NO_ERROR = 0 + ERROR_SUCCESS = 0 + ERROR_FILE_NOT_FOUND = 2 + ERROR_PATH_NOT_FOUND = 3 + ERROR_ACCESS_DENIED = 5 + ERROR_INVALID_HANDLE = 6 + ERROR_BAD_FORMAT = 11 + ERROR_INVALID_NAME = 123 + ERROR_MORE_DATA = 234 + ERROR_NO_MORE_ITEMS = 259 + ERROR_INVALID_SERVICE_CONTROL = 1052 + ERROR_SERVICE_REQUEST_TIMEOUT = 1053 + ERROR_SERVICE_NO_THREAD = 1054 + ERROR_SERVICE_DATABASE_LOCKED = 1055 + ERROR_SERVICE_ALREADY_RUNNING = 1056 + ERROR_SERVICE_DISABLED = 1058 + ERROR_SERVICE_DOES_NOT_EXIST = 1060 + ERROR_SERVICE_CANNOT_ACCEPT_CTRL = 1061 + ERROR_SERVICE_NOT_ACTIVE = 1062 + ERROR_DATABASE_DOES_NOT_EXIST = 1065 + ERROR_SERVICE_DEPENDENCY_FAIL = 1068 + ERROR_SERVICE_LOGON_FAILED = 1069 + ERROR_SERVICE_MARKED_FOR_DELETE = 1072 + ERROR_SERVICE_DEPENDENCY_DELETED = 1075 +) + +const ( + SE_ERR_FNF = 2 + SE_ERR_PNF = 3 + SE_ERR_ACCESSDENIED = 5 + SE_ERR_OOM = 8 + SE_ERR_DLLNOTFOUND = 32 + SE_ERR_SHARE = 26 + SE_ERR_ASSOCINCOMPLETE = 27 + SE_ERR_DDETIMEOUT = 28 + SE_ERR_DDEFAIL = 29 + SE_ERR_DDEBUSY = 30 + SE_ERR_NOASSOC = 31 +) + +const ( + EDS_ROTATEDMODE = 0x00000001 + EDS_RAWMODE = 0x00000002 + DMDO_DEFAULT = 0 + DMDO_90 = 1 + DMDO_180 = 2 + DMDO_270 = 3 +) + +const ( + CW_USEDEFAULT = ^0x7fffffff +) + +const ( + IMAGE_BITMAP = 0 + IMAGE_ICON = 1 + IMAGE_CURSOR = 2 + IMAGE_ENHMETAFILE = 3 +) + +// SetProcessDpiAwareness constants +const ( + PROCESS_DPI_UNAWARE = 0 + PROCESS_SYSTEM_DPI_AWARE = 1 + PROCESS_PER_MONITOR_DPI_AWARE = 2 +) + +// SetProcessDpiAwarenessContext constants +// Credit: https://github.com/ncruces/zenity +const ( + DPI_AWARENESS_CONTEXT_UNAWARE = ^uintptr(1) + 1 + DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = ^uintptr(2) + 1 + DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = ^uintptr(3) + 1 + DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = ^uintptr(4) + 1 + DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED = ^uintptr(5) + 1 +) + +// ShowWindow constants +const ( + SW_HIDE = 0 + SW_NORMAL = 1 + SW_SHOWNORMAL = 1 + SW_SHOWMINIMIZED = 2 + SW_MAXIMIZE = 3 + SW_SHOWMAXIMIZED = 3 + SW_SHOWNOACTIVATE = 4 + SW_SHOW = 5 + SW_MINIMIZE = 6 + SW_SHOWMINNOACTIVE = 7 + SW_SHOWNA = 8 + SW_RESTORE = 9 + SW_SHOWDEFAULT = 10 + SW_FORCEMINIMIZE = 11 +) + +// Window class styles +const ( + CS_VREDRAW = 0x00000001 + CS_HREDRAW = 0x00000002 + CS_KEYCVTWINDOW = 0x00000004 + CS_DBLCLKS = 0x00000008 + CS_OWNDC = 0x00000020 + CS_CLASSDC = 0x00000040 + CS_PARENTDC = 0x00000080 + CS_NOKEYCVT = 0x00000100 + CS_NOCLOSE = 0x00000200 + CS_SAVEBITS = 0x00000800 + CS_BYTEALIGNCLIENT = 0x00001000 + CS_BYTEALIGNWINDOW = 0x00002000 + CS_GLOBALCLASS = 0x00004000 + CS_IME = 0x00010000 + CS_DROPSHADOW = 0x00020000 +) + +// Predefined cursor constants +const ( + IDC_ARROW = 32512 + IDC_IBEAM = 32513 + IDC_WAIT = 32514 + IDC_CROSS = 32515 + IDC_UPARROW = 32516 + IDC_SIZENWSE = 32642 + IDC_SIZENESW = 32643 + IDC_SIZEWE = 32644 + IDC_SIZENS = 32645 + IDC_SIZEALL = 32646 + IDC_NO = 32648 + IDC_HAND = 32649 + IDC_APPSTARTING = 32650 + IDC_HELP = 32651 + IDC_ICON = 32641 + IDC_SIZE = 32640 +) + +// Predefined icon constants +const ( + IDI_APPLICATION = 32512 + IDI_HAND = 32513 + IDI_QUESTION = 32514 + IDI_EXCLAMATION = 32515 + IDI_ASTERISK = 32516 + IDI_WINLOGO = 32517 + IDI_WARNING = IDI_EXCLAMATION + IDI_ERROR = IDI_HAND + IDI_INFORMATION = IDI_ASTERISK +) + +const ( + RT_CURSOR = 1 // win32.MAKEINTRESOURCE(1) + RT_BITMAP = 2 + RT_ICON = 3 + RT_MENU = 4 + RT_DIALOG = 5 + RT_STRING = 6 + RT_FONTDIR = 7 + RT_FONT = 8 + RT_ACCELERATOR = 9 + RT_RCDATA = 10 + RT_MESSAGETABLE = 11 + RT_GROUP_CURSOR = 12 + RT_GROUP_ICON = 14 + RT_VERSION = 16 + RT_DLGINCLUDE = 17 + RT_PLUGPLAY = 19 + RT_VXD = 20 + RT_ANICURSOR = 21 + RT_ANIICON = 22 + RT_HTML = 23 + RT_MANIFEST = 24 +) + +// Button style constants +const ( + BS_3STATE = 5 + BS_AUTO3STATE = 6 + BS_AUTOCHECKBOX = 3 + BS_AUTORADIOBUTTON = 9 + BS_BITMAP = 128 + BS_BOTTOM = 0x800 + BS_CENTER = 0x300 + BS_CHECKBOX = 2 + BS_DEFPUSHBUTTON = 1 + BS_GROUPBOX = 7 + BS_ICON = 64 + BS_LEFT = 256 + BS_LEFTTEXT = 32 + BS_MULTILINE = 0x2000 + BS_NOTIFY = 0x4000 + BS_OWNERDRAW = 0xB + BS_PUSHBUTTON = 0 + BS_PUSHLIKE = 4096 + BS_RADIOBUTTON = 4 + BS_RIGHT = 512 + BS_RIGHTBUTTON = 32 + BS_TEXT = 0 + BS_TOP = 0x400 + BS_USERBUTTON = 8 + BS_VCENTER = 0xC00 + BS_FLAT = 0x8000 + BS_SPLITBUTTON = 0x000C // >= Vista + BS_DEFSPLITBUTTON = 0x000D // >= Vista +) + +// Button state constants +const ( + BST_CHECKED = 1 + BST_INDETERMINATE = 2 + BST_UNCHECKED = 0 + BST_FOCUS = 8 + BST_PUSHED = 4 +) + +// Predefined brushes constants +const ( + COLOR_3DDKSHADOW = 21 + COLOR_3DFACE = 15 + COLOR_3DHILIGHT = 20 + COLOR_3DHIGHLIGHT = 20 + COLOR_3DLIGHT = 22 + COLOR_BTNHILIGHT = 20 + COLOR_3DSHADOW = 16 + COLOR_ACTIVEBORDER = 10 + COLOR_ACTIVECAPTION = 2 + COLOR_APPWORKSPACE = 12 + COLOR_BACKGROUND = 1 + COLOR_DESKTOP = 1 + COLOR_BTNFACE = 15 + COLOR_BTNHIGHLIGHT = 20 + COLOR_BTNSHADOW = 16 + COLOR_BTNTEXT = 18 + COLOR_CAPTIONTEXT = 9 + COLOR_GRAYTEXT = 17 + COLOR_HIGHLIGHT = 13 + COLOR_HIGHLIGHTTEXT = 14 + COLOR_INACTIVEBORDER = 11 + COLOR_INACTIVECAPTION = 3 + COLOR_INACTIVECAPTIONTEXT = 19 + COLOR_INFOBK = 24 + COLOR_INFOTEXT = 23 + COLOR_MENU = 4 + COLOR_MENUTEXT = 7 + COLOR_SCROLLBAR = 0 + COLOR_WINDOW = 5 + COLOR_WINDOWFRAME = 6 + COLOR_WINDOWTEXT = 8 + COLOR_HOTLIGHT = 26 + COLOR_GRADIENTACTIVECAPTION = 27 + COLOR_GRADIENTINACTIVECAPTION = 28 +) + +// Button message constants +const ( + BM_CLICK = 245 + BM_GETCHECK = 240 + BM_GETIMAGE = 246 + BM_GETSTATE = 242 + BM_SETCHECK = 241 + BM_SETIMAGE = 247 + BM_SETSTATE = 243 + BM_SETSTYLE = 244 +) + +// Button notifications +const ( + BN_CLICKED = 0 + BN_PAINT = 1 + BN_HILITE = 2 + BN_PUSHED = BN_HILITE + BN_UNHILITE = 3 + BN_UNPUSHED = BN_UNHILITE + BN_DISABLE = 4 + BN_DOUBLECLICKED = 5 + BN_DBLCLK = BN_DOUBLECLICKED + BN_SETFOCUS = 6 + BN_KILLFOCUS = 7 +) + +// TrackPopupMenu[Ex] flags +const ( + TPM_CENTERALIGN = 0x0004 + TPM_LEFTALIGN = 0x0000 + TPM_RIGHTALIGN = 0x0008 + TPM_BOTTOMALIGN = 0x0020 + TPM_TOPALIGN = 0x0000 + TPM_VCENTERALIGN = 0x0010 + TPM_NONOTIFY = 0x0080 + TPM_RETURNCMD = 0x0100 + TPM_LEFTBUTTON = 0x0000 + TPM_RIGHTBUTTON = 0x0002 + TPM_HORNEGANIMATION = 0x0800 + TPM_HORPOSANIMATION = 0x0400 + TPM_NOANIMATION = 0x4000 + TPM_VERNEGANIMATION = 0x2000 + TPM_VERPOSANIMATION = 0x1000 + TPM_HORIZONTAL = 0x0000 + TPM_VERTICAL = 0x0040 +) + +// GetWindowLong and GetWindowLongPtr constants +const ( + GWL_EXSTYLE = -20 + GWL_STYLE = -16 + GWL_WNDPROC = -4 + GWLP_WNDPROC = -4 + GWL_HINSTANCE = -6 + GWLP_HINSTANCE = -6 + GWL_HWNDPARENT = -8 + GWLP_HWNDPARENT = -8 + GWL_ID = -12 + GWLP_ID = -12 + GWL_USERDATA = -21 + GWLP_USERDATA = -21 +) + +const ( + GW_HWNDFIRST = 0 + GW_HWNDLAST = 1 + GW_HWNDNEXT = 2 + GW_HWNDPREV = 3 + GW_OWNER = 4 + GW_CHILD = 5 + GW_ENABLEDPOPUP = 6 +) + +// Window style constants +const ( + WS_OVERLAPPED = 0x00000000 + WS_POPUP = 0x80000000 + WS_CHILD = 0x40000000 + WS_MINIMIZE = 0x20000000 + WS_VISIBLE = 0x10000000 + WS_DISABLED = 0x08000000 + WS_CLIPSIBLINGS = 0x04000000 + WS_CLIPCHILDREN = 0x02000000 + WS_MAXIMIZE = 0x01000000 + WS_CAPTION = 0x00C00000 + WS_BORDER = 0x00800000 + WS_DLGFRAME = 0x00400000 + WS_VSCROLL = 0x00200000 + WS_HSCROLL = 0x00100000 + WS_SYSMENU = 0x00080000 + WS_THICKFRAME = 0x00040000 + WS_GROUP = 0x00020000 + WS_TABSTOP = 0x00010000 + WS_MINIMIZEBOX = 0x00020000 + WS_MAXIMIZEBOX = 0x00010000 + WS_TILED = 0x00000000 + WS_ICONIC = 0x20000000 + WS_SIZEBOX = 0x00040000 + WS_OVERLAPPEDWINDOW = 0x00000000 | 0x00C00000 | 0x00080000 | 0x00040000 | 0x00020000 | 0x00010000 + WS_POPUPWINDOW = 0x80000000 | 0x00800000 | 0x00080000 + WS_CHILDWINDOW = 0x40000000 +) + +// Extended window style constants +const ( + WS_EX_DLGMODALFRAME = 0x00000001 + WS_EX_NOPARENTNOTIFY = 0x00000004 + WS_EX_TOPMOST = 0x00000008 + WS_EX_ACCEPTFILES = 0x00000010 + WS_EX_TRANSPARENT = 0x00000020 + WS_EX_MDICHILD = 0x00000040 + WS_EX_TOOLWINDOW = 0x00000080 + WS_EX_WINDOWEDGE = 0x00000100 + WS_EX_CLIENTEDGE = 0x00000200 + WS_EX_CONTEXTHELP = 0x00000400 + WS_EX_COMPOSITED = 0x02000000 + WS_EX_RIGHT = 0x00001000 + WS_EX_LEFT = 0x00000000 + WS_EX_RTLREADING = 0x00002000 + WS_EX_LTRREADING = 0x00000000 + WS_EX_LEFTSCROLLBAR = 0x00004000 + WS_EX_RIGHTSCROLLBAR = 0x00000000 + WS_EX_CONTROLPARENT = 0x00010000 + WS_EX_STATICEDGE = 0x00020000 + WS_EX_APPWINDOW = 0x00040000 + WS_EX_OVERLAPPEDWINDOW = 0x00000100 | 0x00000200 + WS_EX_PALETTEWINDOW = 0x00000100 | 0x00000080 | 0x00000008 + WS_EX_LAYERED = 0x00080000 + WS_EX_NOINHERITLAYOUT = 0x00100000 + WS_EX_NOREDIRECTIONBITMAP = 0x00200000 + WS_EX_LAYOUTRTL = 0x00400000 + WS_EX_NOACTIVATE = 0x08000000 +) + +// Window message constants +const ( + WM_APP = 32768 + WM_ACTIVATE = 6 + WM_ACTIVATEAPP = 28 + WM_AFXFIRST = 864 + WM_AFXLAST = 895 + WM_ASKCBFORMATNAME = 780 + WM_CANCELJOURNAL = 75 + WM_CANCELMODE = 31 + WM_CAPTURECHANGED = 533 + WM_CHANGECBCHAIN = 781 + WM_CHAR = 258 + WM_CHARTOITEM = 47 + WM_CHILDACTIVATE = 34 + WM_CLEAR = 771 + WM_CLOSE = 16 + WM_COMMAND = 273 + WM_COMMNOTIFY = 68 /* OBSOLETE */ + WM_COMPACTING = 65 + WM_COMPAREITEM = 57 + WM_CONTEXTMENU = 123 + WM_COPY = 769 + WM_COPYDATA = 74 + WM_CREATE = 1 + WM_CTLCOLORBTN = 309 + WM_CTLCOLORDLG = 310 + WM_CTLCOLOREDIT = 307 + WM_CTLCOLORLISTBOX = 308 + WM_CTLCOLORMSGBOX = 306 + WM_CTLCOLORSCROLLBAR = 311 + WM_CTLCOLORSTATIC = 312 + WM_CUT = 768 + WM_DEADCHAR = 259 + WM_DELETEITEM = 45 + WM_DESTROY = 2 + WM_DESTROYCLIPBOARD = 775 + WM_DEVICECHANGE = 537 + WM_DEVMODECHANGE = 27 + WM_DISPLAYCHANGE = 126 + WM_DRAWCLIPBOARD = 776 + WM_DRAWITEM = 43 + WM_DROPFILES = 563 + WM_ENABLE = 10 + WM_ENDSESSION = 22 + WM_ENTERIDLE = 289 + WM_ENTERMENULOOP = 529 + WM_ENTERSIZEMOVE = 561 + WM_ERASEBKGND = 20 + WM_EXITMENULOOP = 530 + WM_EXITSIZEMOVE = 562 + WM_FONTCHANGE = 29 + WM_GETDLGCODE = 135 + WM_GETFONT = 49 + WM_GETHOTKEY = 51 + WM_GETICON = 127 + WM_GETMINMAXINFO = 36 + WM_GETTEXT = 13 + WM_GETTEXTLENGTH = 14 + WM_HANDHELDFIRST = 856 + WM_HANDHELDLAST = 863 + WM_HELP = 83 + WM_HOTKEY = 786 + WM_HSCROLL = 276 + WM_HSCROLLCLIPBOARD = 782 + WM_ICONERASEBKGND = 39 + WM_INITDIALOG = 272 + WM_INITMENU = 278 + WM_INITMENUPOPUP = 279 + WM_INPUT = 0x00FF + WM_INPUTLANGCHANGE = 81 + WM_INPUTLANGCHANGEREQUEST = 80 + WM_KEYDOWN = 256 + WM_KEYUP = 257 + WM_KILLFOCUS = 8 + WM_MDIACTIVATE = 546 + WM_MDICASCADE = 551 + WM_MDICREATE = 544 + WM_MDIDESTROY = 545 + WM_MDIGETACTIVE = 553 + WM_MDIICONARRANGE = 552 + WM_MDIMAXIMIZE = 549 + WM_MDINEXT = 548 + WM_MDIREFRESHMENU = 564 + WM_MDIRESTORE = 547 + WM_MDISETMENU = 560 + WM_MDITILE = 550 + WM_MEASUREITEM = 44 + WM_GETOBJECT = 0x003D + WM_CHANGEUISTATE = 0x0127 + WM_UPDATEUISTATE = 0x0128 + WM_QUERYUISTATE = 0x0129 + WM_UNINITMENUPOPUP = 0x0125 + WM_MENURBUTTONUP = 290 + WM_MENUCOMMAND = 0x0126 + WM_MENUGETOBJECT = 0x0124 + WM_MENUDRAG = 0x0123 + WM_APPCOMMAND = 0x0319 + WM_MENUCHAR = 288 + WM_MENUSELECT = 287 + WM_MOVE = 3 + WM_MOVING = 534 + WM_NCACTIVATE = 134 + WM_NCCALCSIZE = 131 + WM_NCCREATE = 129 + WM_NCDESTROY = 130 + WM_NCHITTEST = 132 + WM_NCLBUTTONDBLCLK = 163 + WM_NCLBUTTONDOWN = 161 + WM_NCLBUTTONUP = 162 + WM_NCMBUTTONDBLCLK = 169 + WM_NCMBUTTONDOWN = 167 + WM_NCMBUTTONUP = 168 + WM_NCXBUTTONDOWN = 171 + WM_NCXBUTTONUP = 172 + WM_NCXBUTTONDBLCLK = 173 + WM_NCMOUSEHOVER = 0x02A0 + WM_NCMOUSELEAVE = 0x02A2 + WM_NCMOUSEMOVE = 160 + WM_NCPAINT = 133 + WM_NCRBUTTONDBLCLK = 166 + WM_NCRBUTTONDOWN = 164 + WM_NCRBUTTONUP = 165 + WM_NEXTDLGCTL = 40 + WM_NEXTMENU = 531 + WM_NOTIFY = 78 + WM_NOTIFYFORMAT = 85 + WM_NULL = 0 + WM_PAINT = 15 + WM_PAINTCLIPBOARD = 777 + WM_PAINTICON = 38 + WM_UAHDRAWMENU = 0x0091 + WM_UAHDRAWMENUITEM = 0x0092 + WM_UAHMEASUREMENUITEM = 0x0094 + WM_PALETTECHANGED = 785 + WM_PALETTEISCHANGING = 784 + WM_PARENTNOTIFY = 528 + WM_PASTE = 770 + WM_PENWINFIRST = 896 + WM_PENWINLAST = 911 + WM_POWER = 72 + WM_POWERBROADCAST = 536 + WM_PRINT = 791 + WM_PRINTCLIENT = 792 + WM_QUERYDRAGICON = 55 + WM_QUERYENDSESSION = 17 + WM_QUERYNEWPALETTE = 783 + WM_QUERYOPEN = 19 + WM_QUEUESYNC = 35 + WM_QUIT = 18 + WM_RENDERALLFORMATS = 774 + WM_RENDERFORMAT = 773 + WM_SETCURSOR = 32 + WM_SETFOCUS = 7 + WM_SETFONT = 48 + WM_SETHOTKEY = 50 + WM_SETICON = 128 + WM_SETREDRAW = 11 + WM_SETTEXT = 12 + WM_SETTINGCHANGE = 26 + WM_SHOWWINDOW = 24 + WM_SIZE = 5 + WM_SIZECLIPBOARD = 779 + WM_SIZING = 532 + WM_SPOOLERSTATUS = 42 + WM_STYLECHANGED = 125 + WM_STYLECHANGING = 124 + WM_SYSCHAR = 262 + WM_SYSCOLORCHANGE = 21 + WM_SYSCOMMAND = 274 + WM_SYSDEADCHAR = 263 + WM_SYSKEYDOWN = 260 + WM_SYSKEYUP = 261 + WM_TCARD = 82 + WM_THEMECHANGED = 794 + WM_TIMECHANGE = 30 + WM_TIMER = 275 + WM_UNDO = 772 + WM_USER = 1024 + WM_USERCHANGED = 84 + WM_VKEYTOITEM = 46 + WM_VSCROLL = 277 + WM_VSCROLLCLIPBOARD = 778 + WM_WINDOWPOSCHANGED = 71 + WM_WINDOWPOSCHANGING = 70 + WM_SELECTALL = 0x00B1 + WM_WININICHANGE = 26 + WM_KEYFIRST = 256 + WM_KEYLAST = 264 + WM_SYNCPAINT = 136 + WM_MOUSEACTIVATE = 33 + WM_MOUSEMOVE = 512 + WM_LBUTTONDOWN = 513 + WM_LBUTTONUP = 514 + WM_LBUTTONDBLCLK = 515 + WM_RBUTTONDOWN = 516 + WM_RBUTTONUP = 517 + WM_RBUTTONDBLCLK = 518 + WM_MBUTTONDOWN = 519 + WM_MBUTTONUP = 520 + WM_MBUTTONDBLCLK = 521 + WM_MOUSEWHEEL = 522 + WM_MOUSEHWHEEL = 526 + WM_MOUSEFIRST = 512 + WM_XBUTTONDOWN = 523 + WM_XBUTTONUP = 524 + WM_XBUTTONDBLCLK = 525 + WM_MOUSELAST = 525 + WM_MOUSEHOVER = 0x2A1 + WM_MOUSELEAVE = 0x2A3 + WM_CLIPBOARDUPDATE = 0x031D + WM_DPICHANGED = 0x02E0 +) + +const ( + SC_SIZE = 0xF000 // Resize the window + SC_MOVE = 0xF010 // Move the window + SC_MINIMIZE = 0xF020 // Minimize the window + SC_MAXIMIZE = 0xF030 // Maximize the window + SC_NEXTWINDOW = 0xF040 // Move to next window + SC_PREVWINDOW = 0xF050 // Move to previous window + SC_CLOSE = 0xF060 // Close the window + SC_VSCROLL = 0xF070 // Vertical scroll + SC_HSCROLL = 0xF080 // Horizontal scroll + SC_MOUSEMENU = 0xF090 // Mouse menu + SC_KEYMENU = 0xF100 // Key menu (triggered by Alt or F10) + SC_ARRANGE = 0xF110 // Arrange windows + SC_RESTORE = 0xF120 // Restore window from minimized/maximized + SC_TASKLIST = 0xF130 // Task list + SC_SCREENSAVE = 0xF140 // Screen saver + SC_HOTKEY = 0xF150 // Hotkey + SC_DEFAULT = 0xF160 // Default command + SC_MONITORPOWER = 0xF170 // Monitor power + SC_CONTEXTHELP = 0xF180 // Context help + SC_SEPARATOR = 0xF00F // Separator +) + +const ( + // Remove the Close option from the window menu + SC_MASK_CLOSE = ^uint16(SC_CLOSE) + // Mask for extracting the system command + SC_MASK_CMD = 0xFFF0 +) + +// WM_ACTIVATE +const ( + WA_INACTIVE = 0 + WA_ACTIVE = 1 + WA_CLICKACTIVE = 2 +) + +const LF_FACESIZE = 32 + +// Font weight constants +const ( + FW_DONTCARE = 0 + FW_THIN = 100 + FW_EXTRALIGHT = 200 + FW_ULTRALIGHT = FW_EXTRALIGHT + FW_LIGHT = 300 + FW_NORMAL = 400 + FW_REGULAR = 400 + FW_MEDIUM = 500 + FW_SEMIBOLD = 600 + FW_DEMIBOLD = FW_SEMIBOLD + FW_BOLD = 700 + FW_EXTRABOLD = 800 + FW_ULTRABOLD = FW_EXTRABOLD + FW_HEAVY = 900 + FW_BLACK = FW_HEAVY +) + +// Charset constants +const ( + ANSI_CHARSET = 0 + DEFAULT_CHARSET = 1 + SYMBOL_CHARSET = 2 + SHIFTJIS_CHARSET = 128 + HANGEUL_CHARSET = 129 + HANGUL_CHARSET = 129 + GB2312_CHARSET = 134 + CHINESEBIG5_CHARSET = 136 + GREEK_CHARSET = 161 + TURKISH_CHARSET = 162 + HEBREW_CHARSET = 177 + ARABIC_CHARSET = 178 + BALTIC_CHARSET = 186 + RUSSIAN_CHARSET = 204 + THAI_CHARSET = 222 + EASTEUROPE_CHARSET = 238 + OEM_CHARSET = 255 + JOHAB_CHARSET = 130 + VIETNAMESE_CHARSET = 163 + MAC_CHARSET = 77 +) + +const ( + // PBT_APMPOWERSTATUSCHANGE - Power status has changed. + PBT_APMPOWERSTATUSCHANGE = 10 + + // PBT_APMRESUMEAUTOMATIC -Operation is resuming automatically from a low-power state. This message is sent every time the system resumes. + PBT_APMRESUMEAUTOMATIC = 18 + + // PBT_APMRESUMESUSPEND - Operation is resuming from a low-power state. This message is sent after PBT_APMRESUMEAUTOMATIC if the resume is triggered by user input, such as pressing a key. + PBT_APMRESUMESUSPEND = 7 + + // PBT_APMSUSPEND - System is suspending operation. + PBT_APMSUSPEND = 4 + + // PBT_POWERSETTINGCHANGE - A power setting change event has been received. + PBT_POWERSETTINGCHANGE = 32787 +) + +// Font output precision constants +const ( + OUT_DEFAULT_PRECIS = 0 + OUT_STRING_PRECIS = 1 + OUT_CHARACTER_PRECIS = 2 + OUT_STROKE_PRECIS = 3 + OUT_TT_PRECIS = 4 + OUT_DEVICE_PRECIS = 5 + OUT_RASTER_PRECIS = 6 + OUT_TT_ONLY_PRECIS = 7 + OUT_OUTLINE_PRECIS = 8 + OUT_PS_ONLY_PRECIS = 10 +) + +// Font clipping precision constants +const ( + CLIP_DEFAULT_PRECIS = 0 + CLIP_CHARACTER_PRECIS = 1 + CLIP_STROKE_PRECIS = 2 + CLIP_MASK = 15 + CLIP_LH_ANGLES = 16 + CLIP_TT_ALWAYS = 32 + CLIP_EMBEDDED = 128 +) + +// Font output quality constants +const ( + DEFAULT_QUALITY = 0 + DRAFT_QUALITY = 1 + PROOF_QUALITY = 2 + NONANTIALIASED_QUALITY = 3 + ANTIALIASED_QUALITY = 4 + CLEARTYPE_QUALITY = 5 +) + +// Font pitch constants +const ( + DEFAULT_PITCH = 0 + FIXED_PITCH = 1 + VARIABLE_PITCH = 2 +) + +// Font family constants +const ( + FF_DECORATIVE = 80 + FF_DONTCARE = 0 + FF_MODERN = 48 + FF_ROMAN = 16 + FF_SCRIPT = 64 + FF_SWISS = 32 +) + +// DeviceCapabilities capabilities +const ( + DC_FIELDS = 1 + DC_PAPERS = 2 + DC_PAPERSIZE = 3 + DC_MINEXTENT = 4 + DC_MAXEXTENT = 5 + DC_BINS = 6 + DC_DUPLEX = 7 + DC_SIZE = 8 + DC_EXTRA = 9 + DC_VERSION = 10 + DC_DRIVER = 11 + DC_BINNAMES = 12 + DC_ENUMRESOLUTIONS = 13 + DC_FILEDEPENDENCIES = 14 + DC_TRUETYPE = 15 + DC_PAPERNAMES = 16 + DC_ORIENTATION = 17 + DC_COPIES = 18 + DC_BINADJUST = 19 + DC_EMF_COMPLIANT = 20 + DC_DATATYPE_PRODUCED = 21 + DC_COLLATE = 22 + DC_MANUFACTURER = 23 + DC_MODEL = 24 + DC_PERSONALITY = 25 + DC_PRINTRATE = 26 + DC_PRINTRATEUNIT = 27 + DC_PRINTERMEM = 28 + DC_MEDIAREADY = 29 + DC_STAPLE = 30 + DC_PRINTRATEPPM = 31 + DC_COLORDEVICE = 32 + DC_NUP = 33 + DC_MEDIATYPENAMES = 34 + DC_MEDIATYPES = 35 +) + +// GetDeviceCaps index constants +const ( + DRIVERVERSION = 0 + TECHNOLOGY = 2 + HORZSIZE = 4 + VERTSIZE = 6 + HORZRES = 8 + VERTRES = 10 + LOGPIXELSX = 88 + LOGPIXELSY = 90 + BITSPIXEL = 12 + PLANES = 14 + NUMBRUSHES = 16 + NUMPENS = 18 + NUMFONTS = 22 + NUMCOLORS = 24 + NUMMARKERS = 20 + ASPECTX = 40 + ASPECTY = 42 + ASPECTXY = 44 + PDEVICESIZE = 26 + CLIPCAPS = 36 + SIZEPALETTE = 104 + NUMRESERVED = 106 + COLORRES = 108 + PHYSICALWIDTH = 110 + PHYSICALHEIGHT = 111 + PHYSICALOFFSETX = 112 + PHYSICALOFFSETY = 113 + SCALINGFACTORX = 114 + SCALINGFACTORY = 115 + VREFRESH = 116 + DESKTOPHORZRES = 118 + DESKTOPVERTRES = 117 + BLTALIGNMENT = 119 + SHADEBLENDCAPS = 120 + COLORMGMTCAPS = 121 + RASTERCAPS = 38 + CURVECAPS = 28 + LINECAPS = 30 + POLYGONALCAPS = 32 + TEXTCAPS = 34 +) + +// GetDeviceCaps TECHNOLOGY constants +const ( + DT_PLOTTER = 0 + DT_RASDISPLAY = 1 + DT_RASPRINTER = 2 + DT_RASCAMERA = 3 + DT_CHARSTREAM = 4 + DT_METAFILE = 5 + DT_DISPFILE = 6 +) + +// GetDeviceCaps SHADEBLENDCAPS constants +const ( + SB_NONE = 0x00 + SB_CONST_ALPHA = 0x01 + SB_PIXEL_ALPHA = 0x02 + SB_PREMULT_ALPHA = 0x04 + SB_GRAD_RECT = 0x10 + SB_GRAD_TRI = 0x20 +) + +// GetDeviceCaps COLORMGMTCAPS constants +const ( + CM_NONE = 0x00 + CM_DEVICE_ICM = 0x01 + CM_GAMMA_RAMP = 0x02 + CM_CMYK_COLOR = 0x04 +) + +// GetDeviceCaps RASTERCAPS constants +const ( + RC_BANDING = 2 + RC_BITBLT = 1 + RC_BITMAP64 = 8 + RC_DI_BITMAP = 128 + RC_DIBTODEV = 512 + RC_FLOODFILL = 4096 + RC_GDI20_OUTPUT = 16 + RC_PALETTE = 256 + RC_SCALING = 4 + RC_STRETCHBLT = 2048 + RC_STRETCHDIB = 8192 + RC_DEVBITS = 0x8000 + RC_OP_DX_OUTPUT = 0x4000 +) + +// GetDeviceCaps CURVECAPS constants +const ( + CC_NONE = 0 + CC_CIRCLES = 1 + CC_PIE = 2 + CC_CHORD = 4 + CC_ELLIPSES = 8 + CC_WIDE = 16 + CC_STYLED = 32 + CC_WIDESTYLED = 64 + CC_INTERIORS = 128 + CC_ROUNDRECT = 256 +) + +// GetDeviceCaps LINECAPS constants +const ( + LC_NONE = 0 + LC_POLYLINE = 2 + LC_MARKER = 4 + LC_POLYMARKER = 8 + LC_WIDE = 16 + LC_STYLED = 32 + LC_WIDESTYLED = 64 + LC_INTERIORS = 128 +) + +// GetDeviceCaps POLYGONALCAPS constants +const ( + PC_NONE = 0 + PC_POLYGON = 1 + PC_POLYPOLYGON = 256 + PC_PATHS = 512 + PC_RECTANGLE = 2 + PC_WINDPOLYGON = 4 + PC_SCANLINE = 8 + PC_TRAPEZOID = 4 + PC_WIDE = 16 + PC_STYLED = 32 + PC_WIDESTYLED = 64 + PC_INTERIORS = 128 +) + +// GetDeviceCaps TEXTCAPS constants +const ( + TC_OP_CHARACTER = 1 + TC_OP_STROKE = 2 + TC_CP_STROKE = 4 + TC_CR_90 = 8 + TC_CR_ANY = 16 + TC_SF_X_YINDEP = 32 + TC_SA_DOUBLE = 64 + TC_SA_INTEGER = 128 + TC_SA_CONTIN = 256 + TC_EA_DOUBLE = 512 + TC_IA_ABLE = 1024 + TC_UA_ABLE = 2048 + TC_SO_ABLE = 4096 + TC_RA_ABLE = 8192 + TC_VA_ABLE = 16384 + TC_RESERVED = 32768 + TC_SCROLLBLT = 65536 +) + +// Static control styles +const ( + SS_BITMAP = 14 + SS_BLACKFRAME = 7 + SS_BLACKRECT = 4 + SS_CENTER = 1 + SS_CENTERIMAGE = 512 + SS_EDITCONTROL = 0x2000 + SS_ENHMETAFILE = 15 + SS_ETCHEDFRAME = 18 + SS_ETCHEDHORZ = 16 + SS_ETCHEDVERT = 17 + SS_GRAYFRAME = 8 + SS_GRAYRECT = 5 + SS_ICON = 3 + SS_LEFT = 0 + SS_LEFTNOWORDWRAP = 0xc + SS_NOPREFIX = 128 + SS_NOTIFY = 256 + SS_OWNERDRAW = 0xd + SS_REALSIZECONTROL = 0x040 + SS_REALSIZEIMAGE = 0x800 + SS_RIGHT = 2 + SS_RIGHTJUST = 0x400 + SS_SIMPLE = 11 + SS_SUNKEN = 4096 + SS_WHITEFRAME = 9 + SS_WHITERECT = 6 + SS_USERITEM = 10 + SS_TYPEMASK = 0x0000001F + SS_ENDELLIPSIS = 0x00004000 + SS_PATHELLIPSIS = 0x00008000 + SS_WORDELLIPSIS = 0x0000C000 + SS_ELLIPSISMASK = 0x0000C000 +) + +const ( + FLASHW_STOP = 0 // Stop flashing. The system restores the window to its original state. + FLASHW_CAPTION = 1 // Flash the window caption. + FLASHW_TRAY = 2 // Flash the taskbar button. + FLASHW_ALL = 3 // Flash both the window caption and taskbar button. This is equivalent to setting the FLASHW_CAPTION | FLASHW_TRAY flags. + FLASHW_TIMER = 4 // Flash continuously, until the FLASHW_STOP flag is set. + FLASHW_TIMERNOFG = 12 // Flash continuously until the window comes to the foreground. +) + +// Edit styles +const ( + ES_LEFT = 0x0000 + ES_CENTER = 0x0001 + ES_RIGHT = 0x0002 + ES_MULTILINE = 0x0004 + ES_UPPERCASE = 0x0008 + ES_LOWERCASE = 0x0010 + ES_PASSWORD = 0x0020 + ES_AUTOVSCROLL = 0x0040 + ES_AUTOHSCROLL = 0x0080 + ES_NOHIDESEL = 0x0100 + ES_OEMCONVERT = 0x0400 + ES_READONLY = 0x0800 + ES_WANTRETURN = 0x1000 + ES_NUMBER = 0x2000 +) + +// Edit notifications +const ( + EN_SETFOCUS = 0x0100 + EN_KILLFOCUS = 0x0200 + EN_CHANGE = 0x0300 + EN_UPDATE = 0x0400 + EN_ERRSPACE = 0x0500 + EN_MAXTEXT = 0x0501 + EN_HSCROLL = 0x0601 + EN_VSCROLL = 0x0602 + EN_ALIGN_LTR_EC = 0x0700 + EN_ALIGN_RTL_EC = 0x0701 +) + +// Edit messages +const ( + EM_GETSEL = 0x00B0 + EM_SETSEL = 0x00B1 + EM_GETRECT = 0x00B2 + EM_SETRECT = 0x00B3 + EM_SETRECTNP = 0x00B4 + EM_SCROLL = 0x00B5 + EM_LINESCROLL = 0x00B6 + EM_SCROLLCARET = 0x00B7 + EM_GETMODIFY = 0x00B8 + EM_SETMODIFY = 0x00B9 + EM_GETLINECOUNT = 0x00BA + EM_LINEINDEX = 0x00BB + EM_SETHANDLE = 0x00BC + EM_GETHANDLE = 0x00BD + EM_GETTHUMB = 0x00BE + EM_LINELENGTH = 0x00C1 + EM_REPLACESEL = 0x00C2 + EM_GETLINE = 0x00C4 + EM_LIMITTEXT = 0x00C5 + EM_CANUNDO = 0x00C6 + EM_UNDO = 0x00C7 + EM_FMTLINES = 0x00C8 + EM_LINEFROMCHAR = 0x00C9 + EM_SETTABSTOPS = 0x00CB + EM_SETPASSWORDCHAR = 0x00CC + EM_EMPTYUNDOBUFFER = 0x00CD + EM_GETFIRSTVISIBLELINE = 0x00CE + EM_SETREADONLY = 0x00CF + EM_SETWORDBREAKPROC = 0x00D0 + EM_GETWORDBREAKPROC = 0x00D1 + EM_GETPASSWORDCHAR = 0x00D2 + EM_SETMARGINS = 0x00D3 + EM_GETMARGINS = 0x00D4 + EM_SETLIMITTEXT = EM_LIMITTEXT + EM_GETLIMITTEXT = 0x00D5 + EM_POSFROMCHAR = 0x00D6 + EM_CHARFROMPOS = 0x00D7 + EM_SETIMESTATUS = 0x00D8 + EM_GETIMESTATUS = 0x00D9 + EM_SETCUEBANNER = 0x1501 + EM_GETCUEBANNER = 0x1502 +) + +const ( + CCM_FIRST = 0x2000 + CCM_LAST = CCM_FIRST + 0x200 + CCM_SETBKCOLOR = 8193 + CCM_SETCOLORSCHEME = 8194 + CCM_GETCOLORSCHEME = 8195 + CCM_GETDROPTARGET = 8196 + CCM_SETUNICODEFORMAT = 8197 + CCM_GETUNICODEFORMAT = 8198 + CCM_SETVERSION = 0x2007 + CCM_GETVERSION = 0x2008 + CCM_SETNOTIFYWINDOW = 0x2009 + CCM_SETWINDOWTHEME = 0x200b + CCM_DPISCALE = 0x200c +) + +// Common controls styles +const ( + CCS_TOP = 1 + CCS_NOMOVEY = 2 + CCS_BOTTOM = 3 + CCS_NORESIZE = 4 + CCS_NOPARENTALIGN = 8 + CCS_ADJUSTABLE = 32 + CCS_NODIVIDER = 64 + CCS_VERT = 128 + CCS_LEFT = 129 + CCS_NOMOVEX = 130 + CCS_RIGHT = 131 +) + +// ProgressBar messages +const ( + PROGRESS_CLASS = "msctls_progress32" + PBM_SETPOS = WM_USER + 2 + PBM_DELTAPOS = WM_USER + 3 + PBM_SETSTEP = WM_USER + 4 + PBM_STEPIT = WM_USER + 5 + PBM_SETRANGE32 = 1030 + PBM_GETRANGE = 1031 + PBM_GETPOS = 1032 + PBM_SETBARCOLOR = 1033 + PBM_SETBKCOLOR = CCM_SETBKCOLOR + PBS_SMOOTH = 1 + PBS_VERTICAL = 4 +) + +// Trackbar messages and constants +const ( + TBS_AUTOTICKS = 1 + TBS_VERT = 2 + TBS_HORZ = 0 + TBS_TOP = 4 + TBS_BOTTOM = 0 + TBS_LEFT = 4 + TBS_RIGHT = 0 + TBS_BOTH = 8 + TBS_NOTICKS = 16 + TBS_ENABLESELRANGE = 32 + TBS_FIXEDLENGTH = 64 + TBS_NOTHUMB = 128 + TBS_TOOLTIPS = 0x0100 +) + +const ( + TBM_GETPOS = WM_USER + TBM_GETRANGEMIN = WM_USER + 1 + TBM_GETRANGEMAX = WM_USER + 2 + TBM_GETTIC = WM_USER + 3 + TBM_SETTIC = WM_USER + 4 + TBM_SETPOS = WM_USER + 5 + TBM_SETRANGE = WM_USER + 6 + TBM_SETRANGEMIN = WM_USER + 7 + TBM_SETRANGEMAX = WM_USER + 8 + TBM_CLEARTICS = WM_USER + 9 + TBM_SETSEL = WM_USER + 10 + TBM_SETSELSTART = WM_USER + 11 + TBM_SETSELEND = WM_USER + 12 + TBM_GETPTICS = WM_USER + 14 + TBM_GETTICPOS = WM_USER + 15 + TBM_GETNUMTICS = WM_USER + 16 + TBM_GETSELSTART = WM_USER + 17 + TBM_GETSELEND = WM_USER + 18 + TBM_CLEARSEL = WM_USER + 19 + TBM_SETTICFREQ = WM_USER + 20 + TBM_SETPAGESIZE = WM_USER + 21 + TBM_GETPAGESIZE = WM_USER + 22 + TBM_SETLINESIZE = WM_USER + 23 + TBM_GETLINESIZE = WM_USER + 24 + TBM_GETTHUMBRECT = WM_USER + 25 + TBM_GETCHANNELRECT = WM_USER + 26 + TBM_SETTHUMBLENGTH = WM_USER + 27 + TBM_GETTHUMBLENGTH = WM_USER + 28 + TBM_SETTOOLTIPS = WM_USER + 29 + TBM_GETTOOLTIPS = WM_USER + 30 + TBM_SETTIPSIDE = WM_USER + 31 + TBM_SETBUDDY = WM_USER + 32 + TBM_GETBUDDY = WM_USER + 33 +) + +const ( + TB_LINEUP = 0 + TB_LINEDOWN = 1 + TB_PAGEUP = 2 + TB_PAGEDOWN = 3 + TB_THUMBPOSITION = 4 + TB_THUMBTRACK = 5 + TB_TOP = 6 + TB_BOTTOM = 7 + TB_ENDTRACK = 8 +) + +// GetOpenFileName and GetSaveFileName extended flags +const ( + OFN_EX_NOPLACESBAR = 0x00000001 +) + +// GetOpenFileName and GetSaveFileName flags +const ( + OFN_ALLOWMULTISELECT = 0x00000200 + OFN_CREATEPROMPT = 0x00002000 + OFN_DONTADDTORECENT = 0x02000000 + OFN_ENABLEHOOK = 0x00000020 + OFN_ENABLEINCLUDENOTIFY = 0x00400000 + OFN_ENABLESIZING = 0x00800000 + OFN_ENABLETEMPLATE = 0x00000040 + OFN_ENABLETEMPLATEHANDLE = 0x00000080 + OFN_EXPLORER = 0x00080000 + OFN_EXTENSIONDIFFERENT = 0x00000400 + OFN_FILEMUSTEXIST = 0x00001000 + OFN_FORCESHOWHIDDEN = 0x10000000 + OFN_HIDEREADONLY = 0x00000004 + OFN_LONGNAMES = 0x00200000 + OFN_NOCHANGEDIR = 0x00000008 + OFN_NODEREFERENCELINKS = 0x00100000 + OFN_NOLONGNAMES = 0x00040000 + OFN_NONETWORKBUTTON = 0x00020000 + OFN_NOREADONLYRETURN = 0x00008000 + OFN_NOTESTFILECREATE = 0x00010000 + OFN_NOVALIDATE = 0x00000100 + OFN_OVERWRITEPROMPT = 0x00000002 + OFN_PATHMUSTEXIST = 0x00000800 + OFN_READONLY = 0x00000001 + OFN_SHAREAWARE = 0x00004000 + OFN_SHOWHELP = 0x00000010 +) + +// SHBrowseForFolder flags +const ( + BIF_RETURNONLYFSDIRS = 0x00000001 + BIF_DONTGOBELOWDOMAIN = 0x00000002 + BIF_STATUSTEXT = 0x00000004 + BIF_RETURNFSANCESTORS = 0x00000008 + BIF_EDITBOX = 0x00000010 + BIF_VALIDATE = 0x00000020 + BIF_NEWDIALOGSTYLE = 0x00000040 + BIF_BROWSEINCLUDEURLS = 0x00000080 + BIF_USENEWUI = BIF_EDITBOX | BIF_NEWDIALOGSTYLE + BIF_UAHINT = 0x00000100 + BIF_NONEWFOLDERBUTTON = 0x00000200 + BIF_NOTRANSLATETARGETS = 0x00000400 + BIF_BROWSEFORCOMPUTER = 0x00001000 + BIF_BROWSEFORPRINTER = 0x00002000 + BIF_BROWSEINCLUDEFILES = 0x00004000 + BIF_SHAREABLE = 0x00008000 + BIF_BROWSEFILEJUNCTIONS = 0x00010000 +) + +// MessageBox flags +const ( + MB_OK = 0x00000000 + MB_OKCANCEL = 0x00000001 + MB_ABORTRETRYIGNORE = 0x00000002 + MB_YESNOCANCEL = 0x00000003 + MB_YESNO = 0x00000004 + MB_RETRYCANCEL = 0x00000005 + MB_CANCELTRYCONTINUE = 0x00000006 + MB_ICONHAND = 0x00000010 + MB_ICONQUESTION = 0x00000020 + MB_ICONEXCLAMATION = 0x00000030 + MB_ICONASTERISK = 0x00000040 + MB_USERICON = 0x00000080 + MB_ICONWARNING = MB_ICONEXCLAMATION + MB_ICONERROR = MB_ICONHAND + MB_ICONINFORMATION = MB_ICONASTERISK + MB_ICONSTOP = MB_ICONHAND + MB_DEFBUTTON1 = 0x00000000 + MB_DEFBUTTON2 = 0x00000100 + MB_DEFBUTTON3 = 0x00000200 + MB_DEFBUTTON4 = 0x00000300 +) + +// COM +const ( + E_INVALIDARG = 0x80070057 + E_OUTOFMEMORY = 0x8007000E + E_UNEXPECTED = 0x8000FFFF +) + +const ( + S_OK = 0 + S_FALSE = 0x0001 + RPC_E_CHANGED_MODE = 0x80010106 +) + +// GetSystemMetrics constants +const ( + SM_CXSCREEN = 0 + SM_CYSCREEN = 1 + SM_CXVSCROLL = 2 + SM_CYHSCROLL = 3 + SM_CYCAPTION = 4 + SM_CXBORDER = 5 + SM_CYBORDER = 6 + SM_CXDLGFRAME = 7 + SM_CYDLGFRAME = 8 + SM_CYVTHUMB = 9 + SM_CXHTHUMB = 10 + SM_CXICON = 11 + SM_CYICON = 12 + SM_CXCURSOR = 13 + SM_CYCURSOR = 14 + SM_CYMENU = 15 + SM_CXFULLSCREEN = 16 + SM_CYFULLSCREEN = 17 + SM_CYKANJIWINDOW = 18 + SM_MOUSEPRESENT = 19 + SM_CYVSCROLL = 20 + SM_CXHSCROLL = 21 + SM_DEBUG = 22 + SM_SWAPBUTTON = 23 + SM_RESERVED1 = 24 + SM_RESERVED2 = 25 + SM_RESERVED3 = 26 + SM_RESERVED4 = 27 + SM_CXMIN = 28 + SM_CYMIN = 29 + SM_CXSIZE = 30 + SM_CYSIZE = 31 + SM_CXFRAME = 32 + SM_CYFRAME = 33 + SM_CXMINTRACK = 34 + SM_CYMINTRACK = 35 + SM_CXDOUBLECLK = 36 + SM_CYDOUBLECLK = 37 + SM_CXICONSPACING = 38 + SM_CYICONSPACING = 39 + SM_MENUDROPALIGNMENT = 40 + SM_PENWINDOWS = 41 + SM_DBCSENABLED = 42 + SM_CMOUSEBUTTONS = 43 + SM_CXFIXEDFRAME = SM_CXDLGFRAME + SM_CYFIXEDFRAME = SM_CYDLGFRAME + SM_CXSIZEFRAME = SM_CXFRAME + SM_CYSIZEFRAME = SM_CYFRAME + SM_SECURE = 44 + SM_CXEDGE = 45 + SM_CYEDGE = 46 + SM_CXMINSPACING = 47 + SM_CYMINSPACING = 48 + SM_CXSMICON = 49 + SM_CYSMICON = 50 + SM_CYSMCAPTION = 51 + SM_CXSMSIZE = 52 + SM_CYSMSIZE = 53 + SM_CXMENUSIZE = 54 + SM_CYMENUSIZE = 55 + SM_ARRANGE = 56 + SM_CXMINIMIZED = 57 + SM_CYMINIMIZED = 58 + SM_CXMAXTRACK = 59 + SM_CYMAXTRACK = 60 + SM_CXMAXIMIZED = 61 + SM_CYMAXIMIZED = 62 + SM_NETWORK = 63 + SM_CLEANBOOT = 67 + SM_CXDRAG = 68 + SM_CYDRAG = 69 + SM_SHOWSOUNDS = 70 + SM_CXMENUCHECK = 71 + SM_CYMENUCHECK = 72 + SM_SLOWMACHINE = 73 + SM_MIDEASTENABLED = 74 + SM_MOUSEWHEELPRESENT = 75 + SM_XVIRTUALSCREEN = 76 + SM_YVIRTUALSCREEN = 77 + SM_CXVIRTUALSCREEN = 78 + SM_CYVIRTUALSCREEN = 79 + SM_CMONITORS = 80 + SM_SAMEDISPLAYFORMAT = 81 + SM_IMMENABLED = 82 + SM_CXFOCUSBORDER = 83 + SM_CYFOCUSBORDER = 84 + SM_TABLETPC = 86 + SM_MEDIACENTER = 87 + SM_STARTER = 88 + SM_SERVERR2 = 89 + SM_CMETRICS = 91 + SM_CXPADDEDBORDER = 92 + SM_REMOTESESSION = 0x1000 + SM_SHUTTINGDOWN = 0x2000 + SM_REMOTECONTROL = 0x2001 + SM_CARETBLINKINGENABLED = 0x2002 +) + +const ( + CLSCTX_INPROC_SERVER = 1 + CLSCTX_INPROC_HANDLER = 2 + CLSCTX_LOCAL_SERVER = 4 + CLSCTX_INPROC_SERVER16 = 8 + CLSCTX_REMOTE_SERVER = 16 + CLSCTX_ALL = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER + CLSCTX_INPROC = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER + CLSCTX_SERVER = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER +) + +const ( + COINIT_APARTMENTTHREADED = 0x2 + COINIT_MULTITHREADED = 0x0 + COINIT_DISABLE_OLE1DDE = 0x4 + COINIT_SPEED_OVER_MEMORY = 0x8 +) + +const ( + DISPATCH_METHOD = 1 + DISPATCH_PROPERTYGET = 2 + DISPATCH_PROPERTYPUT = 4 + DISPATCH_PROPERTYPUTREF = 8 +) + +const ( + CC_FASTCALL = iota + CC_CDECL + CC_MSCPASCAL + CC_PASCAL = CC_MSCPASCAL + CC_MACPASCAL + CC_STDCALL + CC_FPFASTCALL + CC_SYSCALL + CC_MPWCDECL + CC_MPWPASCAL + CC_MAX = CC_MPWPASCAL +) + +const ( + VT_EMPTY = 0x0 + VT_NULL = 0x1 + VT_I2 = 0x2 + VT_I4 = 0x3 + VT_R4 = 0x4 + VT_R8 = 0x5 + VT_CY = 0x6 + VT_DATE = 0x7 + VT_BSTR = 0x8 + VT_DISPATCH = 0x9 + VT_ERROR = 0xa + VT_BOOL = 0xb + VT_VARIANT = 0xc + VT_UNKNOWN = 0xd + VT_DECIMAL = 0xe + VT_I1 = 0x10 + VT_UI1 = 0x11 + VT_UI2 = 0x12 + VT_UI4 = 0x13 + VT_I8 = 0x14 + VT_UI8 = 0x15 + VT_INT = 0x16 + VT_UINT = 0x17 + VT_VOID = 0x18 + VT_HRESULT = 0x19 + VT_PTR = 0x1a + VT_SAFEARRAY = 0x1b + VT_CARRAY = 0x1c + VT_USERDEFINED = 0x1d + VT_LPSTR = 0x1e + VT_LPWSTR = 0x1f + VT_RECORD = 0x24 + VT_INT_PTR = 0x25 + VT_UINT_PTR = 0x26 + VT_FILETIME = 0x40 + VT_BLOB = 0x41 + VT_STREAM = 0x42 + VT_STORAGE = 0x43 + VT_STREAMED_OBJECT = 0x44 + VT_STORED_OBJECT = 0x45 + VT_BLOB_OBJECT = 0x46 + VT_CF = 0x47 + VT_CLSID = 0x48 + VT_BSTR_BLOB = 0xfff + VT_VECTOR = 0x1000 + VT_ARRAY = 0x2000 + VT_BYREF = 0x4000 + VT_RESERVED = 0x8000 + VT_ILLEGAL = 0xffff + VT_ILLEGALMASKED = 0xfff + VT_TYPEMASK = 0xfff +) + +const ( + DISPID_UNKNOWN = -1 + DISPID_VALUE = 0 + DISPID_PROPERTYPUT = -3 + DISPID_NEWENUM = -4 + DISPID_EVALUATE = -5 + DISPID_CONSTRUCTOR = -6 + DISPID_DESTRUCTOR = -7 + DISPID_COLLECT = -8 +) + +const ( + MONITOR_DEFAULTTONULL = 0x00000000 + MONITOR_DEFAULTTOPRIMARY = 0x00000001 + MONITOR_DEFAULTTONEAREST = 0x00000002 + + MONITORINFOF_PRIMARY = 0x00000001 +) + +const ( + CCHDEVICENAME = 32 + CCHFORMNAME = 32 +) + +const ( + IDOK = 1 + IDCANCEL = 2 + IDABORT = 3 + IDRETRY = 4 + IDIGNORE = 5 + IDYES = 6 + IDNO = 7 + IDCLOSE = 8 + IDHELP = 9 + IDTRYAGAIN = 10 + IDCONTINUE = 11 + IDTIMEOUT = 32000 +) + +// Generic WM_NOTIFY notification codes +const ( + NM_FIRST = 0 + NM_OUTOFMEMORY = NM_FIRST - 1 + NM_CLICK = NM_FIRST - 2 + NM_DBLCLK = NM_FIRST - 3 + NM_RETURN = NM_FIRST - 4 + NM_RCLICK = NM_FIRST - 5 + NM_RDBLCLK = NM_FIRST - 6 + NM_SETFOCUS = NM_FIRST - 7 + NM_KILLFOCUS = NM_FIRST - 8 + NM_CUSTOMDRAW = NM_FIRST - 12 + NM_HOVER = NM_FIRST - 13 + NM_NCHITTEST = NM_FIRST - 14 + NM_KEYDOWN = NM_FIRST - 15 + NM_RELEASEDCAPTURE = NM_FIRST - 16 + NM_SETCURSOR = NM_FIRST - 17 + NM_CHAR = NM_FIRST - 18 + NM_TOOLTIPSCREATED = NM_FIRST - 19 + NM_LAST = NM_FIRST - 99 +) + +// ListView messages +const ( + LVM_FIRST = 0x1000 + LVM_GETITEMCOUNT = LVM_FIRST + 4 + LVM_SETIMAGELIST = LVM_FIRST + 3 + LVM_GETIMAGELIST = LVM_FIRST + 2 + LVM_GETITEM = LVM_FIRST + 75 + LVM_SETITEM = LVM_FIRST + 76 + LVM_INSERTITEM = LVM_FIRST + 77 + LVM_DELETEITEM = LVM_FIRST + 8 + LVM_DELETEALLITEMS = LVM_FIRST + 9 + LVM_GETCALLBACKMASK = LVM_FIRST + 10 + LVM_SETCALLBACKMASK = LVM_FIRST + 11 + LVM_SETUNICODEFORMAT = CCM_SETUNICODEFORMAT + LVM_GETNEXTITEM = LVM_FIRST + 12 + LVM_FINDITEM = LVM_FIRST + 83 + LVM_GETITEMRECT = LVM_FIRST + 14 + LVM_GETSTRINGWIDTH = LVM_FIRST + 87 + LVM_HITTEST = LVM_FIRST + 18 + LVM_ENSUREVISIBLE = LVM_FIRST + 19 + LVM_SCROLL = LVM_FIRST + 20 + LVM_REDRAWITEMS = LVM_FIRST + 21 + LVM_ARRANGE = LVM_FIRST + 22 + LVM_EDITLABEL = LVM_FIRST + 118 + LVM_GETEDITCONTROL = LVM_FIRST + 24 + LVM_GETCOLUMN = LVM_FIRST + 95 + LVM_SETCOLUMN = LVM_FIRST + 96 + LVM_INSERTCOLUMN = LVM_FIRST + 97 + LVM_DELETECOLUMN = LVM_FIRST + 28 + LVM_GETCOLUMNWIDTH = LVM_FIRST + 29 + LVM_SETCOLUMNWIDTH = LVM_FIRST + 30 + LVM_GETHEADER = LVM_FIRST + 31 + LVM_CREATEDRAGIMAGE = LVM_FIRST + 33 + LVM_GETVIEWRECT = LVM_FIRST + 34 + LVM_GETTEXTCOLOR = LVM_FIRST + 35 + LVM_SETTEXTCOLOR = LVM_FIRST + 36 + LVM_GETTEXTBKCOLOR = LVM_FIRST + 37 + LVM_SETTEXTBKCOLOR = LVM_FIRST + 38 + LVM_GETTOPINDEX = LVM_FIRST + 39 + LVM_GETCOUNTPERPAGE = LVM_FIRST + 40 + LVM_GETORIGIN = LVM_FIRST + 41 + LVM_UPDATE = LVM_FIRST + 42 + LVM_SETITEMSTATE = LVM_FIRST + 43 + LVM_GETITEMSTATE = LVM_FIRST + 44 + LVM_GETITEMTEXT = LVM_FIRST + 115 + LVM_SETITEMTEXT = LVM_FIRST + 116 + LVM_SETITEMCOUNT = LVM_FIRST + 47 + LVM_SORTITEMS = LVM_FIRST + 48 + LVM_SETITEMPOSITION32 = LVM_FIRST + 49 + LVM_GETSELECTEDCOUNT = LVM_FIRST + 50 + LVM_GETITEMSPACING = LVM_FIRST + 51 + LVM_GETISEARCHSTRING = LVM_FIRST + 117 + LVM_SETICONSPACING = LVM_FIRST + 53 + LVM_SETEXTENDEDLISTVIEWSTYLE = LVM_FIRST + 54 + LVM_GETEXTENDEDLISTVIEWSTYLE = LVM_FIRST + 55 + LVM_GETSUBITEMRECT = LVM_FIRST + 56 + LVM_SUBITEMHITTEST = LVM_FIRST + 57 + LVM_SETCOLUMNORDERARRAY = LVM_FIRST + 58 + LVM_GETCOLUMNORDERARRAY = LVM_FIRST + 59 + LVM_SETHOTITEM = LVM_FIRST + 60 + LVM_GETHOTITEM = LVM_FIRST + 61 + LVM_SETHOTCURSOR = LVM_FIRST + 62 + LVM_GETHOTCURSOR = LVM_FIRST + 63 + LVM_APPROXIMATEVIEWRECT = LVM_FIRST + 64 + LVM_SETWORKAREAS = LVM_FIRST + 65 + LVM_GETWORKAREAS = LVM_FIRST + 70 + LVM_GETNUMBEROFWORKAREAS = LVM_FIRST + 73 + LVM_GETSELECTIONMARK = LVM_FIRST + 66 + LVM_SETSELECTIONMARK = LVM_FIRST + 67 + LVM_SETHOVERTIME = LVM_FIRST + 71 + LVM_GETHOVERTIME = LVM_FIRST + 72 + LVM_SETTOOLTIPS = LVM_FIRST + 74 + LVM_GETTOOLTIPS = LVM_FIRST + 78 + LVM_SORTITEMSEX = LVM_FIRST + 81 + LVM_SETBKIMAGE = LVM_FIRST + 138 + LVM_GETBKIMAGE = LVM_FIRST + 139 + LVM_SETSELECTEDCOLUMN = LVM_FIRST + 140 + LVM_SETVIEW = LVM_FIRST + 142 + LVM_GETVIEW = LVM_FIRST + 143 + LVM_INSERTGROUP = LVM_FIRST + 145 + LVM_SETGROUPINFO = LVM_FIRST + 147 + LVM_GETGROUPINFO = LVM_FIRST + 149 + LVM_REMOVEGROUP = LVM_FIRST + 150 + LVM_MOVEGROUP = LVM_FIRST + 151 + LVM_GETGROUPCOUNT = LVM_FIRST + 152 + LVM_GETGROUPINFOBYINDEX = LVM_FIRST + 153 + LVM_MOVEITEMTOGROUP = LVM_FIRST + 154 + LVM_GETGROUPRECT = LVM_FIRST + 98 + LVM_SETGROUPMETRICS = LVM_FIRST + 155 + LVM_GETGROUPMETRICS = LVM_FIRST + 156 + LVM_ENABLEGROUPVIEW = LVM_FIRST + 157 + LVM_SORTGROUPS = LVM_FIRST + 158 + LVM_INSERTGROUPSORTED = LVM_FIRST + 159 + LVM_REMOVEALLGROUPS = LVM_FIRST + 160 + LVM_HASGROUP = LVM_FIRST + 161 + LVM_GETGROUPSTATE = LVM_FIRST + 92 + LVM_GETFOCUSEDGROUP = LVM_FIRST + 93 + LVM_SETTILEVIEWINFO = LVM_FIRST + 162 + LVM_GETTILEVIEWINFO = LVM_FIRST + 163 + LVM_SETTILEINFO = LVM_FIRST + 164 + LVM_GETTILEINFO = LVM_FIRST + 165 + LVM_SETINSERTMARK = LVM_FIRST + 166 + LVM_GETINSERTMARK = LVM_FIRST + 167 + LVM_INSERTMARKHITTEST = LVM_FIRST + 168 + LVM_GETINSERTMARKRECT = LVM_FIRST + 169 + LVM_SETINSERTMARKCOLOR = LVM_FIRST + 170 + LVM_GETINSERTMARKCOLOR = LVM_FIRST + 171 + LVM_SETINFOTIP = LVM_FIRST + 173 + LVM_GETSELECTEDCOLUMN = LVM_FIRST + 174 + LVM_ISGROUPVIEWENABLED = LVM_FIRST + 175 + LVM_GETOUTLINECOLOR = LVM_FIRST + 176 + LVM_SETOUTLINECOLOR = LVM_FIRST + 177 + LVM_CANCELEDITLABEL = LVM_FIRST + 179 + LVM_MAPINDEXTOID = LVM_FIRST + 180 + LVM_MAPIDTOINDEX = LVM_FIRST + 181 + LVM_ISITEMVISIBLE = LVM_FIRST + 182 + LVM_GETNEXTITEMINDEX = LVM_FIRST + 211 +) + +// ListView notifications +const ( + LVN_FIRST = -100 + + LVN_ITEMCHANGING = LVN_FIRST - 0 + LVN_ITEMCHANGED = LVN_FIRST - 1 + LVN_INSERTITEM = LVN_FIRST - 2 + LVN_DELETEITEM = LVN_FIRST - 3 + LVN_DELETEALLITEMS = LVN_FIRST - 4 + LVN_BEGINLABELEDITA = LVN_FIRST - 5 + LVN_BEGINLABELEDITW = LVN_FIRST - 75 + LVN_ENDLABELEDITA = LVN_FIRST - 6 + LVN_ENDLABELEDITW = LVN_FIRST - 76 + LVN_COLUMNCLICK = LVN_FIRST - 8 + LVN_BEGINDRAG = LVN_FIRST - 9 + LVN_BEGINRDRAG = LVN_FIRST - 11 + LVN_ODCACHEHINT = LVN_FIRST - 13 + LVN_ODFINDITEMA = LVN_FIRST - 52 + LVN_ODFINDITEMW = LVN_FIRST - 79 + LVN_ITEMACTIVATE = LVN_FIRST - 14 + LVN_ODSTATECHANGED = LVN_FIRST - 15 + LVN_HOTTRACK = LVN_FIRST - 21 + LVN_GETDISPINFO = LVN_FIRST - 77 + LVN_SETDISPINFO = LVN_FIRST - 78 + LVN_KEYDOWN = LVN_FIRST - 55 + LVN_MARQUEEBEGIN = LVN_FIRST - 56 + LVN_GETINFOTIP = LVN_FIRST - 58 + LVN_INCREMENTALSEARCH = LVN_FIRST - 63 + LVN_BEGINSCROLL = LVN_FIRST - 80 + LVN_ENDSCROLL = LVN_FIRST - 81 +) + +const ( + LVSCW_AUTOSIZE = ^uintptr(0) + LVSCW_AUTOSIZE_USEHEADER = ^uintptr(1) +) + +// ListView LVNI constants +const ( + LVNI_ALL = 0 + LVNI_FOCUSED = 1 + LVNI_SELECTED = 2 + LVNI_CUT = 4 + LVNI_DROPHILITED = 8 + LVNI_ABOVE = 256 + LVNI_BELOW = 512 + LVNI_TOLEFT = 1024 + LVNI_TORIGHT = 2048 +) + +// ListView styles +const ( + LVS_ICON = 0x0000 + LVS_REPORT = 0x0001 + LVS_SMALLICON = 0x0002 + LVS_LIST = 0x0003 + LVS_TYPEMASK = 0x0003 + LVS_SINGLESEL = 0x0004 + LVS_SHOWSELALWAYS = 0x0008 + LVS_SORTASCENDING = 0x0010 + LVS_SORTDESCENDING = 0x0020 + LVS_SHAREIMAGELISTS = 0x0040 + LVS_NOLABELWRAP = 0x0080 + LVS_AUTOARRANGE = 0x0100 + LVS_EDITLABELS = 0x0200 + LVS_OWNERDATA = 0x1000 + LVS_NOSCROLL = 0x2000 + LVS_TYPESTYLEMASK = 0xfc00 + LVS_ALIGNTOP = 0x0000 + LVS_ALIGNLEFT = 0x0800 + LVS_ALIGNMASK = 0x0c00 + LVS_OWNERDRAWFIXED = 0x0400 + LVS_NOCOLUMNHEADER = 0x4000 + LVS_NOSORTHEADER = 0x8000 +) + +// ListView extended styles +const ( + LVS_EX_GRIDLINES = 0x00000001 + LVS_EX_SUBITEMIMAGES = 0x00000002 + LVS_EX_CHECKBOXES = 0x00000004 + LVS_EX_TRACKSELECT = 0x00000008 + LVS_EX_HEADERDRAGDROP = 0x00000010 + LVS_EX_FULLROWSELECT = 0x00000020 + LVS_EX_ONECLICKACTIVATE = 0x00000040 + LVS_EX_TWOCLICKACTIVATE = 0x00000080 + LVS_EX_FLATSB = 0x00000100 + LVS_EX_REGIONAL = 0x00000200 + LVS_EX_INFOTIP = 0x00000400 + LVS_EX_UNDERLINEHOT = 0x00000800 + LVS_EX_UNDERLINECOLD = 0x00001000 + LVS_EX_MULTIWORKAREAS = 0x00002000 + LVS_EX_LABELTIP = 0x00004000 + LVS_EX_BORDERSELECT = 0x00008000 + LVS_EX_DOUBLEBUFFER = 0x00010000 + LVS_EX_HIDELABELS = 0x00020000 + LVS_EX_SINGLEROW = 0x00040000 + LVS_EX_SNAPTOGRID = 0x00080000 + LVS_EX_SIMPLESELECT = 0x00100000 +) + +// ListView column flags +const ( + LVCF_FMT = 0x0001 + LVCF_WIDTH = 0x0002 + LVCF_TEXT = 0x0004 + LVCF_SUBITEM = 0x0008 + LVCF_IMAGE = 0x0010 + LVCF_ORDER = 0x0020 +) + +// ListView column format constants +const ( + LVCFMT_LEFT = 0x0000 + LVCFMT_RIGHT = 0x0001 + LVCFMT_CENTER = 0x0002 + LVCFMT_JUSTIFYMASK = 0x0003 + LVCFMT_IMAGE = 0x0800 + LVCFMT_BITMAP_ON_RIGHT = 0x1000 + LVCFMT_COL_HAS_IMAGES = 0x8000 +) + +// ListView item flags +const ( + LVIF_TEXT = 0x00000001 + LVIF_IMAGE = 0x00000002 + LVIF_PARAM = 0x00000004 + LVIF_STATE = 0x00000008 + LVIF_INDENT = 0x00000010 + LVIF_NORECOMPUTE = 0x00000800 + LVIF_GROUPID = 0x00000100 + LVIF_COLUMNS = 0x00000200 +) + +const LVFI_PARAM = 0x0001 + +// ListView item states +const ( + LVIS_FOCUSED = 1 + LVIS_SELECTED = 2 + LVIS_CUT = 4 + LVIS_DROPHILITED = 8 + LVIS_OVERLAYMASK = 0xF00 + LVIS_STATEIMAGEMASK = 0xF000 +) + +// ListView hit test constants +const ( + LVHT_NOWHERE = 0x00000001 + LVHT_ONITEMICON = 0x00000002 + LVHT_ONITEMLABEL = 0x00000004 + LVHT_ONITEMSTATEICON = 0x00000008 + LVHT_ONITEM = LVHT_ONITEMICON | LVHT_ONITEMLABEL | LVHT_ONITEMSTATEICON + + LVHT_ABOVE = 0x00000008 + LVHT_BELOW = 0x00000010 + LVHT_TORIGHT = 0x00000020 + LVHT_TOLEFT = 0x00000040 +) + +// ListView image list types +const ( + LVSIL_NORMAL = 0 + LVSIL_SMALL = 1 + LVSIL_STATE = 2 + LVSIL_GROUPHEADER = 3 +) + +// InitCommonControlsEx flags +const ( + ICC_LISTVIEW_CLASSES = 1 + ICC_TREEVIEW_CLASSES = 2 + ICC_BAR_CLASSES = 4 + ICC_TAB_CLASSES = 8 + ICC_UPDOWN_CLASS = 16 + ICC_PROGRESS_CLASS = 32 + ICC_HOTKEY_CLASS = 64 + ICC_ANIMATE_CLASS = 128 + ICC_WIN95_CLASSES = 255 + ICC_DATE_CLASSES = 256 + ICC_USEREX_CLASSES = 512 + ICC_COOL_CLASSES = 1024 + ICC_INTERNET_CLASSES = 2048 + ICC_PAGESCROLLER_CLASS = 4096 + ICC_NATIVEFNTCTL_CLASS = 8192 + INFOTIPSIZE = 1024 + ICC_STANDARD_CLASSES = 0x00004000 + ICC_LINK_CLASS = 0x00008000 +) + +// Dialog Codes +const ( + DLGC_WANTARROWS = 0x0001 + DLGC_WANTTAB = 0x0002 + DLGC_WANTALLKEYS = 0x0004 + DLGC_WANTMESSAGE = 0x0004 + DLGC_HASSETSEL = 0x0008 + DLGC_DEFPUSHBUTTON = 0x0010 + DLGC_UNDEFPUSHBUTTON = 0x0020 + DLGC_RADIOBUTTON = 0x0040 + DLGC_WANTCHARS = 0x0080 + DLGC_STATIC = 0x0100 + DLGC_BUTTON = 0x2000 +) + +// Get/SetWindowWord/Long offsets for use with WC_DIALOG windows +const ( + DWL_MSGRESULT = 0 + DWL_DLGPROC = 4 + DWL_USER = 8 +) + +// Registry predefined keys +const ( + HKEY_CLASSES_ROOT HKEY = 0x80000000 + HKEY_CURRENT_USER HKEY = 0x80000001 + HKEY_LOCAL_MACHINE HKEY = 0x80000002 + HKEY_USERS HKEY = 0x80000003 + HKEY_PERFORMANCE_DATA HKEY = 0x80000004 + HKEY_CURRENT_CONFIG HKEY = 0x80000005 + HKEY_DYN_DATA HKEY = 0x80000006 +) + +// Registry Key Security and Access Rights +const ( + KEY_ALL_ACCESS = 0xF003F + KEY_CREATE_SUB_KEY = 0x0004 + KEY_ENUMERATE_SUB_KEYS = 0x0008 + KEY_NOTIFY = 0x0010 + KEY_QUERY_VALUE = 0x0001 + KEY_SET_VALUE = 0x0002 + KEY_READ = 0x20019 + KEY_WRITE = 0x20006 +) + +const ( + NFR_ANSI = 1 + NFR_UNICODE = 2 + NF_QUERY = 3 + NF_REQUERY = 4 +) + +// Registry value types +const ( + RRF_RT_REG_NONE = 0x00000001 + RRF_RT_REG_SZ = 0x00000002 + RRF_RT_REG_EXPAND_SZ = 0x00000004 + RRF_RT_REG_BINARY = 0x00000008 + RRF_RT_REG_DWORD = 0x00000010 + RRF_RT_REG_MULTI_SZ = 0x00000020 + RRF_RT_REG_QWORD = 0x00000040 + RRF_RT_DWORD = RRF_RT_REG_BINARY | RRF_RT_REG_DWORD + RRF_RT_QWORD = RRF_RT_REG_BINARY | RRF_RT_REG_QWORD + RRF_RT_ANY = 0x0000ffff + RRF_NOEXPAND = 0x10000000 + RRF_ZEROONFAILURE = 0x20000000 + REG_PROCESS_APPKEY = 0x00000001 + REG_MUI_STRING_TRUNCATE = 0x00000001 +) + +// PeekMessage wRemoveMsg value +const ( + PM_NOREMOVE = 0x000 + PM_REMOVE = 0x001 + PM_NOYIELD = 0x002 +) + +// ImageList flags +const ( + ILC_MASK = 0x00000001 + ILC_COLOR = 0x00000000 + ILC_COLORDDB = 0x000000FE + ILC_COLOR4 = 0x00000004 + ILC_COLOR8 = 0x00000008 + ILC_COLOR16 = 0x00000010 + ILC_COLOR24 = 0x00000018 + ILC_COLOR32 = 0x00000020 + ILC_PALETTE = 0x00000800 + ILC_MIRROR = 0x00002000 + ILC_PERITEMMIRROR = 0x00008000 + ILC_ORIGINALSIZE = 0x00010000 + ILC_HIGHQUALITYSCALE = 0x00020000 +) + +// Keystroke Message Flags +const ( + KF_EXTENDED = 0x0100 + KF_DLGMODE = 0x0800 + KF_MENUMODE = 0x1000 + KF_ALTDOWN = 0x2000 + KF_REPEAT = 0x4000 + KF_UP = 0x8000 +) + +// Virtual-Key Codes +/* +const ( + VK_LBUTTON = 0x01 + VK_RBUTTON = 0x02 + VK_CANCEL = 0x03 + VK_MBUTTON = 0x04 + VK_XBUTTON1 = 0x05 + VK_XBUTTON2 = 0x06 + VK_BACK = 0x08 + VK_TAB = 0x09 + VK_CLEAR = 0x0C + VK_RETURN = 0x0D + VK_SHIFT = 0x10 + VK_CONTROL = 0x11 + VK_MENU = 0x12 + VK_PAUSE = 0x13 + VK_CAPITAL = 0x14 + VK_KANA = 0x15 + VK_HANGEUL = 0x15 + VK_HANGUL = 0x15 + VK_JUNJA = 0x17 + VK_FINAL = 0x18 + VK_HANJA = 0x19 + VK_KANJI = 0x19 + VK_ESCAPE = 0x1B + VK_CONVERT = 0x1C + VK_NONCONVERT = 0x1D + VK_ACCEPT = 0x1E + VK_MODECHANGE = 0x1F + VK_SPACE = 0x20 + VK_PRIOR = 0x21 + VK_NEXT = 0x22 + VK_END = 0x23 + VK_HOME = 0x24 + VK_LEFT = 0x25 + VK_UP = 0x26 + VK_RIGHT = 0x27 + VK_DOWN = 0x28 + VK_SELECT = 0x29 + VK_PRINT = 0x2A + VK_EXECUTE = 0x2B + VK_SNAPSHOT = 0x2C + VK_INSERT = 0x2D + VK_DELETE = 0x2E + VK_HELP = 0x2F + VK_LWIN = 0x5B + VK_RWIN = 0x5C + VK_APPS = 0x5D + VK_SLEEP = 0x5F + VK_NUMPAD0 = 0x60 + VK_NUMPAD1 = 0x61 + VK_NUMPAD2 = 0x62 + VK_NUMPAD3 = 0x63 + VK_NUMPAD4 = 0x64 + VK_NUMPAD5 = 0x65 + VK_NUMPAD6 = 0x66 + VK_NUMPAD7 = 0x67 + VK_NUMPAD8 = 0x68 + VK_NUMPAD9 = 0x69 + VK_MULTIPLY = 0x6A + VK_ADD = 0x6B + VK_SEPARATOR = 0x6C + VK_SUBTRACT = 0x6D + VK_DECIMAL = 0x6E + VK_DIVIDE = 0x6F + VK_F1 = 0x70 + VK_F2 = 0x71 + VK_F3 = 0x72 + VK_F4 = 0x73 + VK_F5 = 0x74 + VK_F6 = 0x75 + VK_F7 = 0x76 + VK_F8 = 0x77 + VK_F9 = 0x78 + VK_F10 = 0x79 + VK_F11 = 0x7A + VK_F12 = 0x7B + VK_F13 = 0x7C + VK_F14 = 0x7D + VK_F15 = 0x7E + VK_F16 = 0x7F + VK_F17 = 0x80 + VK_F18 = 0x81 + VK_F19 = 0x82 + VK_F20 = 0x83 + VK_F21 = 0x84 + VK_F22 = 0x85 + VK_F23 = 0x86 + VK_F24 = 0x87 + VK_NUMLOCK = 0x90 + VK_SCROLL = 0x91 + VK_OEM_NEC_EQUAL = 0x92 + VK_OEM_FJ_JISHO = 0x92 + VK_OEM_FJ_MASSHOU = 0x93 + VK_OEM_FJ_TOUROKU = 0x94 + VK_OEM_FJ_LOYA = 0x95 + VK_OEM_FJ_ROYA = 0x96 + VK_LSHIFT = 0xA0 + VK_RSHIFT = 0xA1 + VK_LCONTROL = 0xA2 + VK_RCONTROL = 0xA3 + VK_LMENU = 0xA4 + VK_RMENU = 0xA5 + VK_BROWSER_BACK = 0xA6 + VK_BROWSER_FORWARD = 0xA7 + VK_BROWSER_REFRESH = 0xA8 + VK_BROWSER_STOP = 0xA9 + VK_BROWSER_SEARCH = 0xAA + VK_BROWSER_FAVORITES = 0xAB + VK_BROWSER_HOME = 0xAC + VK_VOLUME_MUTE = 0xAD + VK_VOLUME_DOWN = 0xAE + VK_VOLUME_UP = 0xAF + VK_MEDIA_NEXT_TRACK = 0xB0 + VK_MEDIA_PREV_TRACK = 0xB1 + VK_MEDIA_STOP = 0xB2 + VK_MEDIA_PLAY_PAUSE = 0xB3 + VK_LAUNCH_MAIL = 0xB4 + VK_LAUNCH_MEDIA_SELECT = 0xB5 + VK_LAUNCH_APP1 = 0xB6 + VK_LAUNCH_APP2 = 0xB7 + VK_OEM_1 = 0xBA + VK_OEM_PLUS = 0xBB + VK_OEM_COMMA = 0xBC + VK_OEM_MINUS = 0xBD + VK_OEM_PERIOD = 0xBE + VK_OEM_2 = 0xBF + VK_OEM_3 = 0xC0 + VK_OEM_4 = 0xDB + VK_OEM_5 = 0xDC + VK_OEM_6 = 0xDD + VK_OEM_7 = 0xDE + VK_OEM_8 = 0xDF + VK_OEM_AX = 0xE1 + VK_OEM_102 = 0xE2 + VK_ICO_HELP = 0xE3 + VK_ICO_00 = 0xE4 + VK_PROCESSKEY = 0xE5 + VK_ICO_CLEAR = 0xE6 + VK_OEM_RESET = 0xE9 + VK_OEM_JUMP = 0xEA + VK_OEM_PA1 = 0xEB + VK_OEM_PA2 = 0xEC + VK_OEM_PA3 = 0xED + VK_OEM_WSCTRL = 0xEE + VK_OEM_CUSEL = 0xEF + VK_OEM_ATTN = 0xF0 + VK_OEM_FINISH = 0xF1 + VK_OEM_COPY = 0xF2 + VK_OEM_AUTO = 0xF3 + VK_OEM_ENLW = 0xF4 + VK_OEM_BACKTAB = 0xF5 + VK_ATTN = 0xF6 + VK_CRSEL = 0xF7 + VK_EXSEL = 0xF8 + VK_EREOF = 0xF9 + VK_PLAY = 0xFA + VK_ZOOM = 0xFB + VK_NONAME = 0xFC + VK_PA1 = 0xFD + VK_OEM_CLEAR = 0xFE +)*/ + +// Registry Value Types +const ( + REG_NONE = 0 + REG_SZ = 1 + REG_EXPAND_SZ = 2 + REG_BINARY = 3 + REG_DWORD = 4 + REG_DWORD_LITTLE_ENDIAN = 4 + REG_DWORD_BIG_ENDIAN = 5 + REG_LINK = 6 + REG_MULTI_SZ = 7 + REG_RESOURCE_LIST = 8 + REG_FULL_RESOURCE_DESCRIPTOR = 9 + REG_RESOURCE_REQUIREMENTS_LIST = 10 + REG_QWORD = 11 + REG_QWORD_LITTLE_ENDIAN = 11 +) + +// Tooltip styles +const ( + TTS_ALWAYSTIP = 0x01 + TTS_NOPREFIX = 0x02 + TTS_NOANIMATE = 0x10 + TTS_NOFADE = 0x20 + TTS_BALLOON = 0x40 + TTS_CLOSE = 0x80 + TTS_USEVISUALSTYLE = 0x100 +) + +// Tooltip messages +const ( + TTM_ACTIVATE = WM_USER + 1 + TTM_SETDELAYTIME = WM_USER + 3 + TTM_ADDTOOL = WM_USER + 50 + TTM_DELTOOL = WM_USER + 51 + TTM_NEWTOOLRECT = WM_USER + 52 + TTM_RELAYEVENT = WM_USER + 7 + TTM_GETTOOLINFO = WM_USER + 53 + TTM_SETTOOLINFO = WM_USER + 54 + TTM_HITTEST = WM_USER + 55 + TTM_GETTEXT = WM_USER + 56 + TTM_UPDATETIPTEXT = WM_USER + 57 + TTM_GETTOOLCOUNT = WM_USER + 13 + TTM_ENUMTOOLS = WM_USER + 58 + TTM_GETCURRENTTOOL = WM_USER + 59 + TTM_WINDOWFROMPOINT = WM_USER + 16 + TTM_TRACKACTIVATE = WM_USER + 17 + TTM_TRACKPOSITION = WM_USER + 18 + TTM_SETTIPBKCOLOR = WM_USER + 19 + TTM_SETTIPTEXTCOLOR = WM_USER + 20 + TTM_GETDELAYTIME = WM_USER + 21 + TTM_GETTIPBKCOLOR = WM_USER + 22 + TTM_GETTIPTEXTCOLOR = WM_USER + 23 + TTM_SETMAXTIPWIDTH = WM_USER + 24 + TTM_GETMAXTIPWIDTH = WM_USER + 25 + TTM_SETMARGIN = WM_USER + 26 + TTM_GETMARGIN = WM_USER + 27 + TTM_POP = WM_USER + 28 + TTM_UPDATE = WM_USER + 29 + TTM_GETBUBBLESIZE = WM_USER + 30 + TTM_ADJUSTRECT = WM_USER + 31 + TTM_SETTITLE = WM_USER + 33 + TTM_POPUP = WM_USER + 34 + TTM_GETTITLE = WM_USER + 35 +) + +// Tooltip icons +const ( + TTI_NONE = 0 + TTI_INFO = 1 + TTI_WARNING = 2 + TTI_ERROR = 3 + TTI_INFO_LARGE = 4 + TTI_WARNING_LARGE = 5 + TTI_ERROR_LARGE = 6 +) + +// Tooltip notifications +const ( + TTN_FIRST = -520 + TTN_LAST = -549 + TTN_GETDISPINFO = TTN_FIRST - 10 + TTN_SHOW = TTN_FIRST - 1 + TTN_POP = TTN_FIRST - 2 + TTN_LINKCLICK = TTN_FIRST - 3 + TTN_NEEDTEXT = TTN_GETDISPINFO +) + +const ( + TTF_IDISHWND = 0x0001 + TTF_CENTERTIP = 0x0002 + TTF_RTLREADING = 0x0004 + TTF_SUBCLASS = 0x0010 + TTF_TRACK = 0x0020 + TTF_ABSOLUTE = 0x0080 + TTF_TRANSPARENT = 0x0100 + TTF_PARSELINKS = 0x1000 + TTF_DI_SETITEM = 0x8000 +) + +const ( + SWP_NOSIZE = 0x0001 + SWP_NOMOVE = 0x0002 + SWP_NOZORDER = 0x0004 + SWP_NOREDRAW = 0x0008 + SWP_NOACTIVATE = 0x0010 + SWP_FRAMECHANGED = 0x0020 + SWP_SHOWWINDOW = 0x0040 + SWP_HIDEWINDOW = 0x0080 + SWP_NOCOPYBITS = 0x0100 + SWP_NOOWNERZORDER = 0x0200 + SWP_NOSENDCHANGING = 0x0400 + SWP_DRAWFRAME = SWP_FRAMECHANGED + SWP_NOREPOSITION = SWP_NOOWNERZORDER + SWP_DEFERERASE = 0x2000 + SWP_ASYNCWINDOWPOS = 0x4000 +) + +// Predefined window handles +const ( + HWND_BROADCAST = HWND(0xFFFF) + HWND_BOTTOM = HWND(1) + HWND_NOTOPMOST = ^HWND(1) // -2 + HWND_TOP = HWND(0) + HWND_TOPMOST = ^HWND(0) // -1 + HWND_DESKTOP = HWND(0) + HWND_MESSAGE = ^HWND(2) // -3 +) + +// Pen types +const ( + PS_COSMETIC = 0x00000000 + PS_GEOMETRIC = 0x00010000 + PS_TYPE_MASK = 0x000F0000 +) + +// Pen styles +const ( + PS_SOLID = 0 + PS_DASH = 1 + PS_DOT = 2 + PS_DASHDOT = 3 + PS_DASHDOTDOT = 4 + PS_NULL = 5 + PS_INSIDEFRAME = 6 + PS_USERSTYLE = 7 + PS_ALTERNATE = 8 + PS_STYLE_MASK = 0x0000000F +) + +// Pen cap types +const ( + PS_ENDCAP_ROUND = 0x00000000 + PS_ENDCAP_SQUARE = 0x00000100 + PS_ENDCAP_FLAT = 0x00000200 + PS_ENDCAP_MASK = 0x00000F00 +) + +// Pen join types +const ( + PS_JOIN_ROUND = 0x00000000 + PS_JOIN_BEVEL = 0x00001000 + PS_JOIN_MITER = 0x00002000 + PS_JOIN_MASK = 0x0000F000 +) + +// Hatch styles +const ( + HS_HORIZONTAL = 0 + HS_VERTICAL = 1 + HS_FDIAGONAL = 2 + HS_BDIAGONAL = 3 + HS_CROSS = 4 + HS_DIAGCROSS = 5 +) + +// Stock Logical Objects +const ( + WHITE_BRUSH = 0 + LTGRAY_BRUSH = 1 + GRAY_BRUSH = 2 + DKGRAY_BRUSH = 3 + BLACK_BRUSH = 4 + NULL_BRUSH = 5 + HOLLOW_BRUSH = NULL_BRUSH + WHITE_PEN = 6 + BLACK_PEN = 7 + NULL_PEN = 8 + OEM_FIXED_FONT = 10 + ANSI_FIXED_FONT = 11 + ANSI_VAR_FONT = 12 + SYSTEM_FONT = 13 + DEVICE_DEFAULT_FONT = 14 + DEFAULT_PALETTE = 15 + SYSTEM_FIXED_FONT = 16 + DEFAULT_GUI_FONT = 17 + DC_BRUSH = 18 + DC_PEN = 19 +) + +// Brush styles +const ( + BS_SOLID = 0 + BS_NULL = 1 + BS_HOLLOW = BS_NULL + BS_HATCHED = 2 + BS_PATTERN = 3 + BS_INDEXED = 4 + BS_DIBPATTERN = 5 + BS_DIBPATTERNPT = 6 + BS_PATTERN8X8 = 7 + BS_DIBPATTERN8X8 = 8 + BS_MONOPATTERN = 9 +) + +// TRACKMOUSEEVENT flags +const ( + TME_HOVER = 0x00000001 + TME_LEAVE = 0x00000002 + TME_NONCLIENT = 0x00000010 + TME_QUERY = 0x40000000 + TME_CANCEL = 0x80000000 + + HOVER_DEFAULT = 0xFFFFFFFF +) + +// WM_NCHITTEST and MOUSEHOOKSTRUCT Mouse Position Codes +const ( + HTERROR = -2 + HTTRANSPARENT = -1 + HTNOWHERE = 0 + HTCLIENT = 1 + HTCAPTION = 2 + HTSYSMENU = 3 + HTGROWBOX = 4 + HTSIZE = HTGROWBOX + HTMENU = 5 + HTHSCROLL = 6 + HTVSCROLL = 7 + HTMINBUTTON = 8 + HTMAXBUTTON = 9 + HTLEFT = 10 + HTRIGHT = 11 + HTTOP = 12 + HTTOPLEFT = 13 + HTTOPRIGHT = 14 + HTBOTTOM = 15 + HTBOTTOMLEFT = 16 + HTBOTTOMRIGHT = 17 + HTBORDER = 18 + HTREDUCE = HTMINBUTTON + HTZOOM = HTMAXBUTTON + HTSIZEFIRST = HTLEFT + HTSIZELAST = HTBOTTOMRIGHT + HTOBJECT = 19 + HTCLOSE = 20 + HTHELP = 21 +) + +// DrawText[Ex] format flags +const ( + DT_TOP = 0x00000000 + DT_LEFT = 0x00000000 + DT_CENTER = 0x00000001 + DT_RIGHT = 0x00000002 + DT_VCENTER = 0x00000004 + DT_BOTTOM = 0x00000008 + DT_WORDBREAK = 0x00000010 + DT_SINGLELINE = 0x00000020 + DT_EXPANDTABS = 0x00000040 + DT_TABSTOP = 0x00000080 + DT_NOCLIP = 0x00000100 + DT_EXTERNALLEADING = 0x00000200 + DT_CALCRECT = 0x00000400 + DT_NOPREFIX = 0x00000800 + DT_INTERNAL = 0x00001000 + DT_EDITCONTROL = 0x00002000 + DT_PATH_ELLIPSIS = 0x00004000 + DT_END_ELLIPSIS = 0x00008000 + DT_MODIFYSTRING = 0x00010000 + DT_RTLREADING = 0x00020000 + DT_WORD_ELLIPSIS = 0x00040000 + DT_NOFULLWIDTHCHARBREAK = 0x00080000 + DT_HIDEPREFIX = 0x00100000 + DT_PREFIXONLY = 0x00200000 +) + +const CLR_INVALID = 0xFFFFFFFF + +// Background Modes +const ( + TRANSPARENT = 1 + OPAQUE = 2 + BKMODE_LAST = 2 +) + +// Global Memory Flags +const ( + GMEM_FIXED = 0x0000 + GMEM_MOVEABLE = 0x0002 + GMEM_NOCOMPACT = 0x0010 + GMEM_NODISCARD = 0x0020 + GMEM_ZEROINIT = 0x0040 + GMEM_MODIFY = 0x0080 + GMEM_DISCARDABLE = 0x0100 + GMEM_NOT_BANKED = 0x1000 + GMEM_SHARE = 0x2000 + GMEM_DDESHARE = 0x2000 + GMEM_NOTIFY = 0x4000 + GMEM_LOWER = GMEM_NOT_BANKED + GMEM_VALID_FLAGS = 0x7F72 + GMEM_INVALID_HANDLE = 0x8000 + GHND = GMEM_MOVEABLE | GMEM_ZEROINIT + GPTR = GMEM_FIXED | GMEM_ZEROINIT +) + +// Ternary raster operations +const ( + SRCCOPY = 0x00CC0020 + SRCPAINT = 0x00EE0086 + SRCAND = 0x008800C6 + SRCINVERT = 0x00660046 + SRCERASE = 0x00440328 + NOTSRCCOPY = 0x00330008 + NOTSRCERASE = 0x001100A6 + MERGECOPY = 0x00C000CA + MERGEPAINT = 0x00BB0226 + PATCOPY = 0x00F00021 + PATPAINT = 0x00FB0A09 + PATINVERT = 0x005A0049 + DSTINVERT = 0x00550009 + BLACKNESS = 0x00000042 + WHITENESS = 0x00FF0062 + NOMIRRORBITMAP = 0x80000000 + CAPTUREBLT = 0x40000000 +) + +// Clipboard formats +const ( + CF_TEXT = 1 + CF_BITMAP = 2 + CF_METAFILEPICT = 3 + CF_SYLK = 4 + CF_DIF = 5 + CF_TIFF = 6 + CF_OEMTEXT = 7 + CF_DIB = 8 + CF_PALETTE = 9 + CF_PENDATA = 10 + CF_RIFF = 11 + CF_WAVE = 12 + CF_UNICODETEXT = 13 + CF_ENHMETAFILE = 14 + CF_HDROP = 15 + CF_LOCALE = 16 + CF_DIBV5 = 17 + CF_MAX = 18 + CF_OWNERDISPLAY = 0x0080 + CF_DSPTEXT = 0x0081 + CF_DSPBITMAP = 0x0082 + CF_DSPMETAFILEPICT = 0x0083 + CF_DSPENHMETAFILE = 0x008E + CF_PRIVATEFIRST = 0x0200 + CF_PRIVATELAST = 0x02FF + CF_GDIOBJFIRST = 0x0300 + CF_GDIOBJLAST = 0x03FF +) + +// Bitmap compression formats +const ( + BI_RGB = 0 + BI_RLE8 = 1 + BI_RLE4 = 2 + BI_BITFIELDS = 3 + BI_JPEG = 4 + BI_PNG = 5 +) + +// SetDIBitsToDevice fuColorUse +const ( + DIB_PAL_COLORS = 1 + DIB_RGB_COLORS = 0 +) + +const ( + STANDARD_RIGHTS_REQUIRED = 0x000F +) + +// Service Control Manager object specific access types +const ( + SC_MANAGER_CONNECT = 0x0001 + SC_MANAGER_CREATE_SERVICE = 0x0002 + SC_MANAGER_ENUMERATE_SERVICE = 0x0004 + SC_MANAGER_LOCK = 0x0008 + SC_MANAGER_QUERY_LOCK_STATUS = 0x0010 + SC_MANAGER_MODIFY_BOOT_CONFIG = 0x0020 + SC_MANAGER_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE | SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_LOCK | SC_MANAGER_QUERY_LOCK_STATUS | SC_MANAGER_MODIFY_BOOT_CONFIG +) + +// Service Types (Bit Mask) +const ( + SERVICE_KERNEL_DRIVER = 0x00000001 + SERVICE_FILE_SYSTEM_DRIVER = 0x00000002 + SERVICE_ADAPTER = 0x00000004 + SERVICE_RECOGNIZER_DRIVER = 0x00000008 + SERVICE_DRIVER = SERVICE_KERNEL_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_RECOGNIZER_DRIVER + SERVICE_WIN32_OWN_PROCESS = 0x00000010 + SERVICE_WIN32_SHARE_PROCESS = 0x00000020 + SERVICE_WIN32 = SERVICE_WIN32_OWN_PROCESS | SERVICE_WIN32_SHARE_PROCESS + SERVICE_INTERACTIVE_PROCESS = 0x00000100 + SERVICE_TYPE_ALL = SERVICE_WIN32 | SERVICE_ADAPTER | SERVICE_DRIVER | SERVICE_INTERACTIVE_PROCESS +) + +// Service State -- for CurrentState +const ( + SERVICE_STOPPED = 0x00000001 + SERVICE_START_PENDING = 0x00000002 + SERVICE_STOP_PENDING = 0x00000003 + SERVICE_RUNNING = 0x00000004 + SERVICE_CONTINUE_PENDING = 0x00000005 + SERVICE_PAUSE_PENDING = 0x00000006 + SERVICE_PAUSED = 0x00000007 +) + +// Controls Accepted (Bit Mask) +const ( + SERVICE_ACCEPT_STOP = 0x00000001 + SERVICE_ACCEPT_PAUSE_CONTINUE = 0x00000002 + SERVICE_ACCEPT_SHUTDOWN = 0x00000004 + SERVICE_ACCEPT_PARAMCHANGE = 0x00000008 + SERVICE_ACCEPT_NETBINDCHANGE = 0x00000010 + SERVICE_ACCEPT_HARDWAREPROFILECHANGE = 0x00000020 + SERVICE_ACCEPT_POWEREVENT = 0x00000040 + SERVICE_ACCEPT_SESSIONCHANGE = 0x00000080 + SERVICE_ACCEPT_PRESHUTDOWN = 0x00000100 + SERVICE_ACCEPT_TIMECHANGE = 0x00000200 + SERVICE_ACCEPT_TRIGGEREVENT = 0x00000400 +) + +// Service object specific access type +const ( + SERVICE_QUERY_CONFIG = 0x0001 + SERVICE_CHANGE_CONFIG = 0x0002 + SERVICE_QUERY_STATUS = 0x0004 + SERVICE_ENUMERATE_DEPENDENTS = 0x0008 + SERVICE_START = 0x0010 + SERVICE_STOP = 0x0020 + SERVICE_PAUSE_CONTINUE = 0x0040 + SERVICE_INTERROGATE = 0x0080 + SERVICE_USER_DEFINED_CONTROL = 0x0100 + + SERVICE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | + SERVICE_QUERY_CONFIG | + SERVICE_CHANGE_CONFIG | + SERVICE_QUERY_STATUS | + SERVICE_ENUMERATE_DEPENDENTS | + SERVICE_START | + SERVICE_STOP | + SERVICE_PAUSE_CONTINUE | + SERVICE_INTERROGATE | + SERVICE_USER_DEFINED_CONTROL +) + +// MapVirtualKey maptypes +const ( + MAPVK_VK_TO_CHAR = 2 + MAPVK_VK_TO_VSC = 0 + MAPVK_VSC_TO_VK = 1 + MAPVK_VSC_TO_VK_EX = 3 +) + +// ReadEventLog Flags +const ( + EVENTLOG_SEEK_READ = 0x0002 + EVENTLOG_SEQUENTIAL_READ = 0x0001 + EVENTLOG_FORWARDS_READ = 0x0004 + EVENTLOG_BACKWARDS_READ = 0x0008 +) + +// CreateToolhelp32Snapshot flags +const ( + TH32CS_SNAPHEAPLIST = 0x00000001 + TH32CS_SNAPPROCESS = 0x00000002 + TH32CS_SNAPTHREAD = 0x00000004 + TH32CS_SNAPMODULE = 0x00000008 + TH32CS_SNAPMODULE32 = 0x00000010 + TH32CS_INHERIT = 0x80000000 + TH32CS_SNAPALL = TH32CS_SNAPHEAPLIST | TH32CS_SNAPMODULE | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD +) + +const ( + MAX_MODULE_NAME32 = 255 + MAX_PATH = 260 +) + +const ( + NOTIFYICON_VERSION = 3 + NOTIFYICON_VERSION_4 = 4 +) + +const ( + NIM_ADD = 0x00000000 + NIM_MODIFY = 0x00000001 + NIM_DELETE = 0x00000002 + NIM_SETVERSION = 0x00000004 + + NIF_MESSAGE = 0x00000001 + NIF_ICON = 0x00000002 + NIF_TIP = 0x00000004 + NIF_STATE = 0x00000008 + NIF_INFO = 0x00000010 + NIF_GUID = 0x00000020 + NIF_REALTIME = 0x00000040 + NIF_SHOWTIP = 0x00000080 + + NIS_HIDDEN = 0x00000001 + + NIN_POPUPOPEN = WM_USER + 6 + NIN_POPUPCLOSE = WM_USER + 7 + + NIIF_NONE = 0x00000000 + NIIF_INFO = 0x00000001 + NIIF_WARNING = 0x00000002 + NIIF_ERROR = 0x00000003 + NIIF_USER = 0x00000004 + NIIF_NOSOUND = 0x00000010 + NIIF_LARGE_ICON = 0x00000020 + NIIF_RESPECT_QUIET_TIME = 0x00000080 + NIIF_ICON_MASK = 0x0000000F +) + +const ( + FOREGROUND_BLUE = 0x0001 + FOREGROUND_GREEN = 0x0002 + FOREGROUND_RED = 0x0004 + FOREGROUND_INTENSITY = 0x0008 + BACKGROUND_BLUE = 0x0010 + BACKGROUND_GREEN = 0x0020 + BACKGROUND_RED = 0x0040 + BACKGROUND_INTENSITY = 0x0080 + COMMON_LVB_LEADING_BYTE = 0x0100 + COMMON_LVB_TRAILING_BYTE = 0x0200 + COMMON_LVB_GRID_HORIZONTAL = 0x0400 + COMMON_LVB_GRID_LVERTICAL = 0x0800 + COMMON_LVB_GRID_RVERTICAL = 0x1000 + COMMON_LVB_REVERSE_VIDEO = 0x4000 + COMMON_LVB_UNDERSCORE = 0x8000 +) + +// Flags used by the DWM_BLURBEHIND structure to indicate +// which of its members contain valid information. +const ( + DWM_BB_ENABLE = 0x00000001 // A value for the fEnable member has been specified. + DWM_BB_BLURREGION = 0x00000002 // A value for the hRgnBlur member has been specified. + DWM_BB_TRANSITIONONMAXIMIZED = 0x00000004 // A value for the fTransitionOnMaximized member has been specified. +) + +// Flags used by the DwmEnableComposition function +// to change the state of Desktop Window Manager (DWM) composition. +const ( + DWM_EC_DISABLECOMPOSITION = 0 // Disable composition + DWM_EC_ENABLECOMPOSITION = 1 // Enable composition +) + +// enum-lite implementation for the following constant structure +type DWM_SHOWCONTACT int32 + +const ( + DWMSC_DOWN = 0x00000001 + DWMSC_UP = 0x00000002 + DWMSC_DRAG = 0x00000004 + DWMSC_HOLD = 0x00000008 + DWMSC_PENBARREL = 0x00000010 + DWMSC_NONE = 0x00000000 + DWMSC_ALL = 0xFFFFFFFF +) + +// enum-lite implementation for the following constant structure +type DWM_SOURCE_FRAME_SAMPLING int32 + +// TODO: need to verify this construction +// Flags used by the DwmSetPresentParameters function +// to specify the frame sampling type +const ( + DWM_SOURCE_FRAME_SAMPLING_POINT = iota + 1 + DWM_SOURCE_FRAME_SAMPLING_COVERAGE + DWM_SOURCE_FRAME_SAMPLING_LAST +) + +// Flags used by the DWM_THUMBNAIL_PROPERTIES structure to +// indicate which of its members contain valid information. +const ( + DWM_TNP_RECTDESTINATION = 0x00000001 // A value for the rcDestination member has been specified + DWM_TNP_RECTSOURCE = 0x00000002 // A value for the rcSource member has been specified + DWM_TNP_OPACITY = 0x00000004 // A value for the opacity member has been specified + DWM_TNP_VISIBLE = 0x00000008 // A value for the fVisible member has been specified + DWM_TNP_SOURCECLIENTAREAONLY = 0x00000010 // A value for the fSourceClientAreaOnly member has been specified +) + +// enum-lite implementation for the following constant structure +type DWMFLIP3DWINDOWPOLICY int32 + +// TODO: need to verify this construction +// Flags used by the DwmSetWindowAttribute function +// to specify the Flip3D window policy +const ( + DWMFLIP3D_DEFAULT = iota + 1 + DWMFLIP3D_EXCLUDEBELOW + DWMFLIP3D_EXCLUDEABOVE + DWMFLIP3D_LAST +) + +// enum-lite implementation for the following constant structure +type DWMNCRENDERINGPOLICY int32 + +// TODO: need to verify this construction +// Flags used by the DwmSetWindowAttribute function +// to specify the non-client area rendering policy +const ( + DWMNCRP_USEWINDOWSTYLE = iota + 1 + DWMNCRP_DISABLED + DWMNCRP_ENABLED + DWMNCRP_LAST +) + +// enum-lite implementation for the following constant structure +type DWMTRANSITION_OWNEDWINDOW_TARGET int32 + +const ( + DWMTRANSITION_OWNEDWINDOW_NULL = -1 + DWMTRANSITION_OWNEDWINDOW_REPOSITION = 0 +) + +// TODO: need to verify this construction +// Flags used by the DwmGetWindowAttribute and DwmSetWindowAttribute functions +// to specify window attributes for non-client rendering +const ( + DWMWA_NCRENDERING_ENABLED = iota + 1 + DWMWA_NCRENDERING_POLICY + DWMWA_TRANSITIONS_FORCEDISABLED + DWMWA_ALLOW_NCPAINT + DWMWA_CAPTION_BUTTON_BOUNDS + DWMWA_NONCLIENT_RTL_LAYOUT + DWMWA_FORCE_ICONIC_REPRESENTATION + DWMWA_FLIP3D_POLICY + DWMWA_EXTENDED_FRAME_BOUNDS + DWMWA_HAS_ICONIC_BITMAP + DWMWA_DISALLOW_PEEK + DWMWA_EXCLUDED_FROM_PEEK + DWMWA_CLOAK + DWMWA_CLOAKED + DWMWA_FREEZE_REPRESENTATION + DWMWA_LAST +) + +// enum-lite implementation for the following constant structure +type GESTURE_TYPE int32 + +// TODO: use iota? +// Identifies the gesture type +const ( + GT_PEN_TAP = 0 + GT_PEN_DOUBLETAP = 1 + GT_PEN_RIGHTTAP = 2 + GT_PEN_PRESSANDHOLD = 3 + GT_PEN_PRESSANDHOLDABORT = 4 + GT_TOUCH_TAP = 5 + GT_TOUCH_DOUBLETAP = 6 + GT_TOUCH_RIGHTTAP = 7 + GT_TOUCH_PRESSANDHOLD = 8 + GT_TOUCH_PRESSANDHOLDABORT = 9 + GT_TOUCH_PRESSANDTAP = 10 +) + +// Icons +const ( + ICON_SMALL = 0 + ICON_BIG = 1 + ICON_SMALL2 = 2 +) + +const ( + SIZE_RESTORED = 0 + SIZE_MINIMIZED = 1 + SIZE_MAXIMIZED = 2 + SIZE_MAXSHOW = 3 + SIZE_MAXHIDE = 4 +) + +// XButton values +const ( + XBUTTON1 = 1 + XBUTTON2 = 2 +) + +const ( + LR_LOADFROMFILE = 0x00000010 + LR_DEFAULTSIZE = 0x00000040 +) + +// Devmode +const ( + DM_SPECVERSION = 0x0401 + + DM_ORIENTATION = 0x00000001 + DM_PAPERSIZE = 0x00000002 + DM_PAPERLENGTH = 0x00000004 + DM_PAPERWIDTH = 0x00000008 + DM_SCALE = 0x00000010 + DM_POSITION = 0x00000020 + DM_NUP = 0x00000040 + DM_DISPLAYORIENTATION = 0x00000080 + DM_COPIES = 0x00000100 + DM_DEFAULTSOURCE = 0x00000200 + DM_PRINTQUALITY = 0x00000400 + DM_COLOR = 0x00000800 + DM_DUPLEX = 0x00001000 + DM_YRESOLUTION = 0x00002000 + DM_TTOPTION = 0x00004000 + DM_COLLATE = 0x00008000 + DM_FORMNAME = 0x00010000 + DM_LOGPIXELS = 0x00020000 + DM_BITSPERPEL = 0x00040000 + DM_PELSWIDTH = 0x00080000 + DM_PELSHEIGHT = 0x00100000 + DM_DISPLAYFLAGS = 0x00200000 + DM_DISPLAYFREQUENCY = 0x00400000 + DM_ICMMETHOD = 0x00800000 + DM_ICMINTENT = 0x01000000 + DM_MEDIATYPE = 0x02000000 + DM_DITHERTYPE = 0x04000000 + DM_PANNINGWIDTH = 0x08000000 + DM_PANNINGHEIGHT = 0x10000000 + DM_DISPLAYFIXEDOUTPUT = 0x20000000 +) + +// ChangeDisplaySettings +const ( + CDS_UPDATEREGISTRY = 0x00000001 + CDS_TEST = 0x00000002 + CDS_FULLSCREEN = 0x00000004 + CDS_GLOBAL = 0x00000008 + CDS_SET_PRIMARY = 0x00000010 + CDS_VIDEOPARAMETERS = 0x00000020 + CDS_RESET = 0x40000000 + CDS_NORESET = 0x10000000 + + DISP_CHANGE_SUCCESSFUL = 0 + DISP_CHANGE_RESTART = 1 + DISP_CHANGE_FAILED = -1 + DISP_CHANGE_BADMODE = -2 + DISP_CHANGE_NOTUPDATED = -3 + DISP_CHANGE_BADFLAGS = -4 + DISP_CHANGE_BADPARAM = -5 + DISP_CHANGE_BADDUALVIEW = -6 +) + +const ( + ENUM_CURRENT_SETTINGS = 0xFFFFFFFF + ENUM_REGISTRY_SETTINGS = 0xFFFFFFFE +) + +// PIXELFORMATDESCRIPTOR +const ( + PFD_TYPE_RGBA = 0 + PFD_TYPE_COLORINDEX = 1 + + PFD_MAIN_PLANE = 0 + PFD_OVERLAY_PLANE = 1 + PFD_UNDERLAY_PLANE = -1 + + PFD_DOUBLEBUFFER = 0x00000001 + PFD_STEREO = 0x00000002 + PFD_DRAW_TO_WINDOW = 0x00000004 + PFD_DRAW_TO_BITMAP = 0x00000008 + PFD_SUPPORT_GDI = 0x00000010 + PFD_SUPPORT_OPENGL = 0x00000020 + PFD_GENERIC_FORMAT = 0x00000040 + PFD_NEED_PALETTE = 0x00000080 + PFD_NEED_SYSTEM_PALETTE = 0x00000100 + PFD_SWAP_EXCHANGE = 0x00000200 + PFD_SWAP_COPY = 0x00000400 + PFD_SWAP_LAYER_BUFFERS = 0x00000800 + PFD_GENERIC_ACCELERATED = 0x00001000 + PFD_SUPPORT_DIRECTDRAW = 0x00002000 + PFD_DIRECT3D_ACCELERATED = 0x00004000 + PFD_SUPPORT_COMPOSITION = 0x00008000 + + PFD_DEPTH_DONTCARE = 0x20000000 + PFD_DOUBLEBUFFER_DONTCARE = 0x40000000 + PFD_STEREO_DONTCARE = 0x80000000 +) + +const ( + INPUT_MOUSE = 0 + INPUT_KEYBOARD = 1 + INPUT_HARDWARE = 2 +) + +const ( + KEYEVENTF_EXTENDEDKEY = 0x0001 + KEYEVENTF_KEYUP = 0x0002 + KEYEVENTF_SCANCODE = 0x0008 + KEYEVENTF_UNICODE = 0x0004 +) + +const ( + MOUSEEVENTF_ABSOLUTE = 0x8000 + MOUSEEVENTF_HWHEEL = 0x01000 + MOUSEEVENTF_MOVE = 0x0001 + MOUSEEVENTF_MOVE_NOCOALESCE = 0x2000 + MOUSEEVENTF_LEFTDOWN = 0x0002 + MOUSEEVENTF_LEFTUP = 0x0004 + MOUSEEVENTF_RIGHTDOWN = 0x0008 + MOUSEEVENTF_RIGHTUP = 0x0010 + MOUSEEVENTF_MIDDLEDOWN = 0x0020 + MOUSEEVENTF_MIDDLEUP = 0x0040 + MOUSEEVENTF_VIRTUALDESK = 0x4000 + MOUSEEVENTF_WHEEL = 0x0800 + MOUSEEVENTF_XDOWN = 0x0080 + MOUSEEVENTF_XUP = 0x0100 +) + +// Windows Hooks (WH_*) +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms644990(v=vs.85).aspx +const ( + WH_CALLWNDPROC = 4 + WH_CALLWNDPROCRET = 12 + WH_CBT = 5 + WH_DEBUG = 9 + WH_FOREGROUNDIDLE = 11 + WH_GETMESSAGE = 3 + WH_JOURNALPLAYBACK = 1 + WH_JOURNALRECORD = 0 + WH_KEYBOARD = 2 + WH_KEYBOARD_LL = 13 + WH_MOUSE = 7 + WH_MOUSE_LL = 14 + WH_MSGFILTER = -1 + WH_SHELL = 10 + WH_SYSMSGFILTER = 6 +) + +// ComboBox return values +const ( + CB_OKAY = 0 + CB_ERR = ^uintptr(0) // -1 + CB_ERRSPACE = ^uintptr(1) // -2 +) + +// ComboBox notifications +const ( + CBN_ERRSPACE = -1 + CBN_SELCHANGE = 1 + CBN_DBLCLK = 2 + CBN_SETFOCUS = 3 + CBN_KILLFOCUS = 4 + CBN_EDITCHANGE = 5 + CBN_EDITUPDATE = 6 + CBN_DROPDOWN = 7 + CBN_CLOSEUP = 8 + CBN_SELENDOK = 9 + CBN_SELENDCANCEL = 10 +) + +// ComboBox styles +const ( + CBS_SIMPLE = 0x0001 + CBS_DROPDOWN = 0x0002 + CBS_DROPDOWNLIST = 0x0003 + CBS_OWNERDRAWFIXED = 0x0010 + CBS_OWNERDRAWVARIABLE = 0x0020 + CBS_AUTOHSCROLL = 0x0040 + CBS_OEMCONVERT = 0x0080 + CBS_SORT = 0x0100 + CBS_HASSTRINGS = 0x0200 + CBS_NOINTEGRALHEIGHT = 0x0400 + CBS_DISABLENOSCROLL = 0x0800 + CBS_UPPERCASE = 0x2000 + CBS_LOWERCASE = 0x4000 +) + +// ComboBox messages +const ( + CB_GETEDITSEL = 0x0140 + CB_LIMITTEXT = 0x0141 + CB_SETEDITSEL = 0x0142 + CB_ADDSTRING = 0x0143 + CB_DELETESTRING = 0x0144 + CB_DIR = 0x0145 + CB_GETCOUNT = 0x0146 + CB_GETCURSEL = 0x0147 + CB_GETLBTEXT = 0x0148 + CB_GETLBTEXTLEN = 0x0149 + CB_INSERTSTRING = 0x014A + CB_RESETCONTENT = 0x014B + CB_FINDSTRING = 0x014C + CB_SELECTSTRING = 0x014D + CB_SETCURSEL = 0x014E + CB_SHOWDROPDOWN = 0x014F + CB_GETITEMDATA = 0x0150 + CB_SETITEMDATA = 0x0151 + CB_GETDROPPEDCONTROLRECT = 0x0152 + CB_SETITEMHEIGHT = 0x0153 + CB_GETITEMHEIGHT = 0x0154 + CB_SETEXTENDEDUI = 0x0155 + CB_GETEXTENDEDUI = 0x0156 + CB_GETDROPPEDSTATE = 0x0157 + CB_FINDSTRINGEXACT = 0x0158 + CB_SETLOCALE = 0x0159 + CB_GETLOCALE = 0x015A + CB_GETTOPINDEX = 0x015b + CB_SETTOPINDEX = 0x015c + CB_GETHORIZONTALEXTENT = 0x015d + CB_SETHORIZONTALEXTENT = 0x015e + CB_GETDROPPEDWIDTH = 0x015f + CB_SETDROPPEDWIDTH = 0x0160 + CB_INITSTORAGE = 0x0161 + CB_MULTIPLEADDSTRING = 0x0163 + CB_GETCOMBOBOXINFO = 0x0164 +) + +// TreeView styles +const ( + TVS_HASBUTTONS = 0x0001 + TVS_HASLINES = 0x0002 + TVS_LINESATROOT = 0x0004 + TVS_EDITLABELS = 0x0008 + TVS_DISABLEDRAGDROP = 0x0010 + TVS_SHOWSELALWAYS = 0x0020 + TVS_RTLREADING = 0x0040 + TVS_NOTOOLTIPS = 0x0080 + TVS_CHECKBOXES = 0x0100 + TVS_TRACKSELECT = 0x0200 + TVS_SINGLEEXPAND = 0x0400 + TVS_INFOTIP = 0x0800 + TVS_FULLROWSELECT = 0x1000 + TVS_NOSCROLL = 0x2000 + TVS_NONEVENHEIGHT = 0x4000 + TVS_NOHSCROLL = 0x8000 +) + +const ( + TVS_EX_NOSINGLECOLLAPSE = 0x0001 + TVS_EX_MULTISELECT = 0x0002 + TVS_EX_DOUBLEBUFFER = 0x0004 + TVS_EX_NOINDENTSTATE = 0x0008 + TVS_EX_RICHTOOLTIP = 0x0010 + TVS_EX_AUTOHSCROLL = 0x0020 + TVS_EX_FADEINOUTEXPANDOS = 0x0040 + TVS_EX_PARTIALCHECKBOXES = 0x0080 + TVS_EX_EXCLUSIONCHECKBOXES = 0x0100 + TVS_EX_DIMMEDCHECKBOXES = 0x0200 + TVS_EX_DRAWIMAGEASYNC = 0x0400 +) + +const ( + TVIF_TEXT = 0x0001 + TVIF_IMAGE = 0x0002 + TVIF_PARAM = 0x0004 + TVIF_STATE = 0x0008 + TVIF_HANDLE = 0x0010 + TVIF_SELECTEDIMAGE = 0x0020 + TVIF_CHILDREN = 0x0040 + TVIF_INTEGRAL = 0x0080 + TVIF_STATEEX = 0x0100 + TVIF_EXPANDEDIMAGE = 0x0200 +) + +const ( + TVIS_SELECTED = 0x0002 + TVIS_CUT = 0x0004 + TVIS_DROPHILITED = 0x0008 + TVIS_BOLD = 0x0010 + TVIS_EXPANDED = 0x0020 + TVIS_EXPANDEDONCE = 0x0040 + TVIS_EXPANDPARTIAL = 0x0080 + TVIS_OVERLAYMASK = 0x0F00 + TVIS_STATEIMAGEMASK = 0xF000 + TVIS_USERMASK = 0xF000 +) + +const ( + TVIS_EX_FLAT = 0x0001 + TVIS_EX_DISABLED = 0x0002 + TVIS_EX_ALL = 0x0002 +) + +const ( + TVI_ROOT = ^HTREEITEM(0xffff) + TVI_FIRST = ^HTREEITEM(0xfffe) + TVI_LAST = ^HTREEITEM(0xfffd) + TVI_SORT = ^HTREEITEM(0xfffc) +) + +// TVM_EXPAND action flags +const ( + TVE_COLLAPSE = 0x0001 + TVE_EXPAND = 0x0002 + TVE_TOGGLE = 0x0003 + TVE_EXPANDPARTIAL = 0x4000 + TVE_COLLAPSERESET = 0x8000 +) + +const ( + TVGN_CARET = 9 +) + +// TreeView messages +const ( + TV_FIRST = 0x1100 + + TVM_INSERTITEM = TV_FIRST + 50 + TVM_DELETEITEM = TV_FIRST + 1 + TVM_EXPAND = TV_FIRST + 2 + TVM_GETITEMRECT = TV_FIRST + 4 + TVM_GETCOUNT = TV_FIRST + 5 + TVM_GETINDENT = TV_FIRST + 6 + TVM_SETINDENT = TV_FIRST + 7 + TVM_GETIMAGELIST = TV_FIRST + 8 + TVM_SETIMAGELIST = TV_FIRST + 9 + TVM_GETNEXTITEM = TV_FIRST + 10 + TVM_SELECTITEM = TV_FIRST + 11 + TVM_GETITEM = TV_FIRST + 62 + TVM_SETITEM = TV_FIRST + 63 + TVM_EDITLABEL = TV_FIRST + 65 + TVM_GETEDITCONTROL = TV_FIRST + 15 + TVM_GETVISIBLECOUNT = TV_FIRST + 16 + TVM_HITTEST = TV_FIRST + 17 + TVM_CREATEDRAGIMAGE = TV_FIRST + 18 + TVM_SORTCHILDREN = TV_FIRST + 19 + TVM_ENSUREVISIBLE = TV_FIRST + 20 + TVM_SORTCHILDRENCB = TV_FIRST + 21 + TVM_ENDEDITLABELNOW = TV_FIRST + 22 + TVM_GETISEARCHSTRING = TV_FIRST + 64 + TVM_SETTOOLTIPS = TV_FIRST + 24 + TVM_GETTOOLTIPS = TV_FIRST + 25 + TVM_SETINSERTMARK = TV_FIRST + 26 + TVM_SETUNICODEFORMAT = CCM_SETUNICODEFORMAT + TVM_GETUNICODEFORMAT = CCM_GETUNICODEFORMAT + TVM_SETITEMHEIGHT = TV_FIRST + 27 + TVM_GETITEMHEIGHT = TV_FIRST + 28 + TVM_SETBKCOLOR = TV_FIRST + 29 + TVM_SETTEXTCOLOR = TV_FIRST + 30 + TVM_GETBKCOLOR = TV_FIRST + 31 + TVM_GETTEXTCOLOR = TV_FIRST + 32 + TVM_SETSCROLLTIME = TV_FIRST + 33 + TVM_GETSCROLLTIME = TV_FIRST + 34 + TVM_SETINSERTMARKCOLOR = TV_FIRST + 37 + TVM_GETINSERTMARKCOLOR = TV_FIRST + 38 + TVM_GETITEMSTATE = TV_FIRST + 39 + TVM_SETLINECOLOR = TV_FIRST + 40 + TVM_GETLINECOLOR = TV_FIRST + 41 + TVM_MAPACCIDTOHTREEITEM = TV_FIRST + 42 + TVM_MAPHTREEITEMTOACCID = TV_FIRST + 43 + TVM_SETEXTENDEDSTYLE = TV_FIRST + 44 + TVM_GETEXTENDEDSTYLE = TV_FIRST + 45 + TVM_SETAUTOSCROLLINFO = TV_FIRST + 59 +) + +// TreeView notifications +const ( + TVN_FIRST = ^uint32(399) + + TVN_SELCHANGING = TVN_FIRST - 50 + TVN_SELCHANGED = TVN_FIRST - 51 + TVN_GETDISPINFO = TVN_FIRST - 52 + TVN_ITEMEXPANDING = TVN_FIRST - 54 + TVN_ITEMEXPANDED = TVN_FIRST - 55 + TVN_BEGINDRAG = TVN_FIRST - 56 + TVN_BEGINRDRAG = TVN_FIRST - 57 + TVN_DELETEITEM = TVN_FIRST - 58 + TVN_BEGINLABELEDIT = TVN_FIRST - 59 + TVN_ENDLABELEDIT = TVN_FIRST - 60 + TVN_KEYDOWN = TVN_FIRST - 12 + TVN_GETINFOTIP = TVN_FIRST - 14 + TVN_SINGLEEXPAND = TVN_FIRST - 15 + TVN_ITEMCHANGING = TVN_FIRST - 17 + TVN_ITEMCHANGED = TVN_FIRST - 19 + TVN_ASYNCDRAW = TVN_FIRST - 20 +) + +// TreeView hit test constants +const ( + TVHT_NOWHERE = 1 + TVHT_ONITEMICON = 2 + TVHT_ONITEMLABEL = 4 + TVHT_ONITEM = TVHT_ONITEMICON | TVHT_ONITEMLABEL | TVHT_ONITEMSTATEICON + TVHT_ONITEMINDENT = 8 + TVHT_ONITEMBUTTON = 16 + TVHT_ONITEMRIGHT = 32 + TVHT_ONITEMSTATEICON = 64 + TVHT_ABOVE = 256 + TVHT_BELOW = 512 + TVHT_TORIGHT = 1024 + TVHT_TOLEFT = 2048 +) + +type HTREEITEM HANDLE + +type TVITEM struct { + Mask uint32 + HItem HTREEITEM + State uint32 + StateMask uint32 + PszText uintptr + CchTextMax int32 + IImage int32 + ISelectedImage int32 + CChildren int32 + LParam uintptr +} + +/*type TVITEMEX struct { + mask UINT + hItem HTREEITEM + state UINT + stateMask UINT + pszText LPWSTR + cchTextMax int + iImage int + iSelectedImage int + cChildren int + lParam LPARAM + iIntegral int + uStateEx UINT + hwnd HWND + iExpandedImage int +}*/ + +type TVINSERTSTRUCT struct { + HParent HTREEITEM + HInsertAfter HTREEITEM + Item TVITEM + // itemex TVITEMEX +} + +type NMTREEVIEW struct { + Hdr NMHDR + Action uint32 + ItemOld TVITEM + ItemNew TVITEM + PtDrag POINT +} + +type NMTVDISPINFO struct { + Hdr NMHDR + Item TVITEM +} + +type NMTVKEYDOWN struct { + Hdr NMHDR + WVKey uint16 + Flags uint32 +} + +type TVHITTESTINFO struct { + Pt POINT + Flags uint32 + HItem HTREEITEM +} + +// TabPage support + +const TCM_FIRST = 0x1300 +const TCN_FIRST = -550 + +const ( + TCS_SCROLLOPPOSITE = 0x0001 + TCS_BOTTOM = 0x0002 + TCS_RIGHT = 0x0002 + TCS_MULTISELECT = 0x0004 + TCS_FLATBUTTONS = 0x0008 + TCS_FORCEICONLEFT = 0x0010 + TCS_FORCELABELLEFT = 0x0020 + TCS_HOTTRACK = 0x0040 + TCS_VERTICAL = 0x0080 + TCS_TABS = 0x0000 + TCS_BUTTONS = 0x0100 + TCS_SINGLELINE = 0x0000 + TCS_MULTILINE = 0x0200 + TCS_RIGHTJUSTIFY = 0x0000 + TCS_FIXEDWIDTH = 0x0400 + TCS_RAGGEDRIGHT = 0x0800 + TCS_FOCUSONBUTTONDOWN = 0x1000 + TCS_OWNERDRAWFIXED = 0x2000 + TCS_TOOLTIPS = 0x4000 + TCS_FOCUSNEVER = 0x8000 +) + +const ( + TCS_EX_FLATSEPARATORS = 0x00000001 + TCS_EX_REGISTERDROP = 0x00000002 +) + +const ( + TCM_GETIMAGELIST = TCM_FIRST + 2 + TCM_SETIMAGELIST = TCM_FIRST + 3 + TCM_GETITEMCOUNT = TCM_FIRST + 4 + TCM_GETITEM = TCM_FIRST + 60 + TCM_SETITEM = TCM_FIRST + 61 + TCM_INSERTITEM = TCM_FIRST + 62 + TCM_DELETEITEM = TCM_FIRST + 8 + TCM_DELETEALLITEMS = TCM_FIRST + 9 + TCM_GETITEMRECT = TCM_FIRST + 10 + TCM_GETCURSEL = TCM_FIRST + 11 + TCM_SETCURSEL = TCM_FIRST + 12 + TCM_HITTEST = TCM_FIRST + 13 + TCM_SETITEMEXTRA = TCM_FIRST + 14 + TCM_ADJUSTRECT = TCM_FIRST + 40 + TCM_SETITEMSIZE = TCM_FIRST + 41 + TCM_REMOVEIMAGE = TCM_FIRST + 42 + TCM_SETPADDING = TCM_FIRST + 43 + TCM_GETROWCOUNT = TCM_FIRST + 44 + TCM_GETTOOLTIPS = TCM_FIRST + 45 + TCM_SETTOOLTIPS = TCM_FIRST + 46 + TCM_GETCURFOCUS = TCM_FIRST + 47 + TCM_SETCURFOCUS = TCM_FIRST + 48 + TCM_SETMINTABWIDTH = TCM_FIRST + 49 + TCM_DESELECTALL = TCM_FIRST + 50 + TCM_HIGHLIGHTITEM = TCM_FIRST + 51 + TCM_SETEXTENDEDSTYLE = TCM_FIRST + 52 + TCM_GETEXTENDEDSTYLE = TCM_FIRST + 53 + TCM_SETUNICODEFORMAT = CCM_SETUNICODEFORMAT + TCM_GETUNICODEFORMAT = CCM_GETUNICODEFORMAT +) + +const ( + TCIF_TEXT = 0x0001 + TCIF_IMAGE = 0x0002 + TCIF_RTLREADING = 0x0004 + TCIF_PARAM = 0x0008 + TCIF_STATE = 0x0010 +) + +const ( + TCIS_BUTTONPRESSED = 0x0001 + TCIS_HIGHLIGHTED = 0x0002 +) + +const ( + TCHT_NOWHERE = 0x0001 + TCHT_ONITEMICON = 0x0002 + TCHT_ONITEMLABEL = 0x0004 + TCHT_ONITEM = TCHT_ONITEMICON | TCHT_ONITEMLABEL +) + +const ( + TCN_KEYDOWN = TCN_FIRST - 0 + TCN_SELCHANGE = TCN_FIRST - 1 + TCN_SELCHANGING = TCN_FIRST - 2 + TCN_GETOBJECT = TCN_FIRST - 3 + TCN_FOCUSCHANGE = TCN_FIRST - 4 +) + +type TCITEMHEADER struct { + Mask uint32 + LpReserved1 uint32 + LpReserved2 uint32 + PszText *uint16 + CchTextMax int32 + IImage int32 +} + +type TCITEM struct { + Mask uint32 + DwState uint32 + DwStateMask uint32 + PszText *uint16 + CchTextMax int32 + IImage int32 + LParam uintptr +} + +type TCHITTESTINFO struct { + Pt POINT + flags uint32 +} + +type NMTCKEYDOWN struct { + Hdr NMHDR + WVKey uint16 + Flags uint32 +} + +// Menu support constants + +// Constants for MENUITEMINFO.fMask +const ( + MIIM_STATE = 1 + MIIM_ID = 2 + MIIM_SUBMENU = 4 + MIIM_CHECKMARKS = 8 + MIIM_TYPE = 16 + MIIM_DATA = 32 + MIIM_STRING = 64 + MIIM_BITMAP = 128 + MIIM_FTYPE = 256 +) + +// Constants for MENUITEMINFO.fType +const ( + MFT_BITMAP = 4 + MFT_MENUBARBREAK = 32 + MFT_MENUBREAK = 64 + MFT_OWNERDRAW = 256 + MFT_RADIOCHECK = 512 + MFT_RIGHTJUSTIFY = 0x4000 + MFT_SEPARATOR = 0x800 + MFT_RIGHTORDER = 0x2000 + MFT_STRING = 0 +) + +// Constants for MENUITEMINFO.fState +const ( + MFS_CHECKED = 8 + MFS_DEFAULT = 4096 + MFS_DISABLED = 3 + MFS_ENABLED = 0 + MFS_GRAYED = 3 + MFS_HILITE = 128 + MFS_UNCHECKED = 0 + MFS_UNHILITE = 0 +) + +// Constants for MENUITEMINFO.hbmp* +const ( + HBMMENU_CALLBACK = -1 + HBMMENU_SYSTEM = 1 + HBMMENU_MBAR_RESTORE = 2 + HBMMENU_MBAR_MINIMIZE = 3 + HBMMENU_MBAR_CLOSE = 5 + HBMMENU_MBAR_CLOSE_D = 6 + HBMMENU_MBAR_MINIMIZE_D = 7 + HBMMENU_POPUP_CLOSE = 8 + HBMMENU_POPUP_RESTORE = 9 + HBMMENU_POPUP_MAXIMIZE = 10 + HBMMENU_POPUP_MINIMIZE = 11 +) + +// MENUINFO mask constants +const ( + MIM_APPLYTOSUBMENUS = 0x80000000 + MIM_BACKGROUND = 0x00000002 + MIM_HELPID = 0x00000004 + MIM_MAXHEIGHT = 0x00000001 + MIM_MENUDATA = 0x00000008 + MIM_STYLE = 0x00000010 +) + +// MENUINFO style constants +const ( + MNS_AUTODISMISS = 0x10000000 + MNS_CHECKORBMP = 0x04000000 + MNS_DRAGDROP = 0x20000000 + MNS_MODELESS = 0x40000000 + MNS_NOCHECK = 0x80000000 + MNS_NOTIFYBYPOS = 0x08000000 +) + +const ( + MF_BYCOMMAND = 0x00000000 + MF_BYPOSITION = 0x00000400 + MF_BITMAP = 0x00000004 + MF_CHECKED = 0x00000008 + MF_DISABLED = 0x00000002 + MF_ENABLED = 0x00000000 + MF_GRAYED = 0x00000001 + MF_MENUBARBREAK = 0x00000020 + MF_MENUBREAK = 0x00000040 + MF_OWNERDRAW = 0x00000100 + MF_POPUP = 0x00000010 + MF_SEPARATOR = 0x00000800 + MF_STRING = 0x00000000 + MF_UNCHECKED = 0x00000000 +) + +type MENUITEMINFO struct { + CbSize uint32 + FMask uint32 + FType uint32 + FState uint32 + WID uint32 + HSubMenu HMENU + HbmpChecked HBITMAP + HbmpUnchecked HBITMAP + DwItemData uintptr + DwTypeData *uint16 + Cch uint32 + HbmpItem HBITMAP +} + +type MENUINFO struct { + CbSize uint32 + FMask uint32 + DwStyle uint32 + CyMax uint32 + HbrBack HBRUSH + DwContextHelpID uint32 + DwMenuData uintptr +} + +// UI state constants +const ( + UIS_SET = 1 + UIS_CLEAR = 2 + UIS_INITIALIZE = 3 +) + +// UI state constants +const ( + UISF_HIDEFOCUS = 0x1 + UISF_HIDEACCEL = 0x2 + UISF_ACTIVE = 0x4 +) + +// Virtual key codes +const ( + VK_LBUTTON = 1 + VK_RBUTTON = 2 + VK_CANCEL = 3 + VK_MBUTTON = 4 + VK_XBUTTON1 = 5 + VK_XBUTTON2 = 6 + VK_BACK = 8 + VK_TAB = 9 + VK_CLEAR = 12 + VK_RETURN = 13 + VK_SHIFT = 16 + VK_CONTROL = 17 + VK_MENU = 18 + VK_PAUSE = 19 + VK_CAPITAL = 20 + VK_KANA = 0x15 + VK_HANGEUL = 0x15 + VK_HANGUL = 0x15 + VK_JUNJA = 0x17 + VK_FINAL = 0x18 + VK_HANJA = 0x19 + VK_KANJI = 0x19 + VK_ESCAPE = 0x1B + VK_CONVERT = 0x1C + VK_NONCONVERT = 0x1D + VK_ACCEPT = 0x1E + VK_MODECHANGE = 0x1F + VK_SPACE = 32 + VK_PRIOR = 33 + VK_NEXT = 34 + VK_END = 35 + VK_HOME = 36 + VK_LEFT = 37 + VK_UP = 38 + VK_RIGHT = 39 + VK_DOWN = 40 + VK_SELECT = 41 + VK_PRINT = 42 + VK_EXECUTE = 43 + VK_SNAPSHOT = 44 + VK_INSERT = 45 + VK_DELETE = 46 + VK_HELP = 47 + VK_LWIN = 0x5B + VK_RWIN = 0x5C + VK_APPS = 0x5D + VK_SLEEP = 0x5F + VK_NUMPAD0 = 0x60 + VK_NUMPAD1 = 0x61 + VK_NUMPAD2 = 0x62 + VK_NUMPAD3 = 0x63 + VK_NUMPAD4 = 0x64 + VK_NUMPAD5 = 0x65 + VK_NUMPAD6 = 0x66 + VK_NUMPAD7 = 0x67 + VK_NUMPAD8 = 0x68 + VK_NUMPAD9 = 0x69 + VK_MULTIPLY = 0x6A + VK_ADD = 0x6B + VK_SEPARATOR = 0x6C + VK_SUBTRACT = 0x6D + VK_DECIMAL = 0x6E + VK_DIVIDE = 0x6F + VK_F1 = 0x70 + VK_F2 = 0x71 + VK_F3 = 0x72 + VK_F4 = 0x73 + VK_F5 = 0x74 + VK_F6 = 0x75 + VK_F7 = 0x76 + VK_F8 = 0x77 + VK_F9 = 0x78 + VK_F10 = 0x79 + VK_F11 = 0x7A + VK_F12 = 0x7B + VK_F13 = 0x7C + VK_F14 = 0x7D + VK_F15 = 0x7E + VK_F16 = 0x7F + VK_F17 = 0x80 + VK_F18 = 0x81 + VK_F19 = 0x82 + VK_F20 = 0x83 + VK_F21 = 0x84 + VK_F22 = 0x85 + VK_F23 = 0x86 + VK_F24 = 0x87 + VK_NUMLOCK = 0x90 + VK_SCROLL = 0x91 + VK_LSHIFT = 0xA0 + VK_RSHIFT = 0xA1 + VK_LCONTROL = 0xA2 + VK_RCONTROL = 0xA3 + VK_LMENU = 0xA4 + VK_RMENU = 0xA5 + VK_BROWSER_BACK = 0xA6 + VK_BROWSER_FORWARD = 0xA7 + VK_BROWSER_REFRESH = 0xA8 + VK_BROWSER_STOP = 0xA9 + VK_BROWSER_SEARCH = 0xAA + VK_BROWSER_FAVORITES = 0xAB + VK_BROWSER_HOME = 0xAC + VK_VOLUME_MUTE = 0xAD + VK_VOLUME_DOWN = 0xAE + VK_VOLUME_UP = 0xAF + VK_MEDIA_NEXT_TRACK = 0xB0 + VK_MEDIA_PREV_TRACK = 0xB1 + VK_MEDIA_STOP = 0xB2 + VK_MEDIA_PLAY_PAUSE = 0xB3 + VK_LAUNCH_MAIL = 0xB4 + VK_LAUNCH_MEDIA_SELECT = 0xB5 + VK_LAUNCH_APP1 = 0xB6 + VK_LAUNCH_APP2 = 0xB7 + VK_OEM_1 = 0xBA + VK_OEM_PLUS = 0xBB + VK_OEM_COMMA = 0xBC + VK_OEM_MINUS = 0xBD + VK_OEM_PERIOD = 0xBE + VK_OEM_2 = 0xBF + VK_OEM_3 = 0xC0 + VK_OEM_4 = 0xDB + VK_OEM_5 = 0xDC + VK_OEM_6 = 0xDD + VK_OEM_7 = 0xDE + VK_OEM_8 = 0xDF + VK_OEM_102 = 0xE2 + VK_PROCESSKEY = 0xE5 + VK_PACKET = 0xE7 + VK_ATTN = 0xF6 + VK_CRSEL = 0xF7 + VK_EXSEL = 0xF8 + VK_EREOF = 0xF9 + VK_PLAY = 0xFA + VK_ZOOM = 0xFB + VK_NONAME = 0xFC + VK_PA1 = 0xFD + VK_OEM_CLEAR = 0xFE +) + +// ScrollBar constants +const ( + SB_HORZ = 0 + SB_VERT = 1 + SB_CTL = 2 + SB_BOTH = 3 +) + +// ScrollBar commands +const ( + SB_LINEUP = 0 + SB_LINELEFT = 0 + SB_LINEDOWN = 1 + SB_LINERIGHT = 1 + SB_PAGEUP = 2 + SB_PAGELEFT = 2 + SB_PAGEDOWN = 3 + SB_PAGERIGHT = 3 + SB_THUMBPOSITION = 4 + SB_THUMBTRACK = 5 + SB_TOP = 6 + SB_LEFT = 6 + SB_BOTTOM = 7 + SB_RIGHT = 7 + SB_ENDSCROLL = 8 +) + +// [Get|Set]ScrollInfo mask constants +const ( + SIF_RANGE = 1 + SIF_PAGE = 2 + SIF_POS = 4 + SIF_DISABLENOSCROLL = 8 + SIF_TRACKPOS = 16 + SIF_ALL = SIF_RANGE + SIF_PAGE + SIF_POS + SIF_TRACKPOS +) + +const AC_SRC_OVER = 0 +const AC_SRC_ALPHA = 1 + +const ULW_COLORKEY = 1 +const ULW_ALPHA = 2 +const ULW_OPAQUE = 4 +const ULW_EX_NORESIZE = 8 + +// RedrawWindow flags +const ( + RDW_INVALIDATE = 0x0001 + RDW_INTERNALPAINT = 0x0002 + RDW_ERASE = 0x0004 + RDW_VALIDATE = 0x0008 + RDW_NOINTERNALPAINT = 0x0010 + RDW_NOERASE = 0x0020 + RDW_NOCHILDREN = 0x0040 + RDW_ALLCHILDREN = 0x0080 + RDW_UPDATENOW = 0x0100 + RDW_ERASENOW = 0x0200 + RDW_FRAME = 0x0400 + RDW_NOFRAME = 0x0800 +) diff --git a/v3/pkg/w32/consts.go b/v3/pkg/w32/consts.go new file mode 100644 index 000000000..b8cdf0358 --- /dev/null +++ b/v3/pkg/w32/consts.go @@ -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 +} diff --git a/v3/pkg/w32/dialogs.go b/v3/pkg/w32/dialogs.go new file mode 100644 index 000000000..d327187c0 --- /dev/null +++ b/v3/pkg/w32/dialogs.go @@ -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 +} diff --git a/v3/pkg/w32/dwmapi.go b/v3/pkg/w32/dwmapi.go new file mode 100644 index 000000000..1b911efc3 --- /dev/null +++ b/v3/pkg/w32/dwmapi.go @@ -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 +} diff --git a/v3/pkg/w32/gdi32.go b/v3/pkg/w32/gdi32.go new file mode 100644 index 000000000..04d11ca14 --- /dev/null +++ b/v3/pkg/w32/gdi32.go @@ -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 +} diff --git a/v3/pkg/w32/gdiplus.go b/v3/pkg/w32/gdiplus.go new file mode 100644 index 000000000..2591ed71b --- /dev/null +++ b/v3/pkg/w32/gdiplus.go @@ -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))) + } +} diff --git a/v3/pkg/w32/guid.go b/v3/pkg/w32/guid.go new file mode 100644 index 000000000..b6e557f01 --- /dev/null +++ b/v3/pkg/w32/guid.go @@ -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, + +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] +} diff --git a/v3/pkg/w32/icon.go b/v3/pkg/w32/icon.go new file mode 100644 index 000000000..97d4ad854 --- /dev/null +++ b/v3/pkg/w32/icon.go @@ -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) +} diff --git a/v3/pkg/w32/idataobject.go b/v3/pkg/w32/idataobject.go new file mode 100644 index 000000000..cf56d0934 --- /dev/null +++ b/v3/pkg/w32/idataobject.go @@ -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{} diff --git a/v3/pkg/w32/idispatch.go b/v3/pkg/w32/idispatch.go new file mode 100644 index 000000000..4f610f3ff --- /dev/null +++ b/v3/pkg/w32/idispatch.go @@ -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...) +} diff --git a/v3/pkg/w32/idroptarget.go b/v3/pkg/w32/idroptarget.go new file mode 100644 index 000000000..abcc931db --- /dev/null +++ b/v3/pkg/w32/idroptarget.go @@ -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) +} diff --git a/v3/pkg/w32/image.go b/v3/pkg/w32/image.go new file mode 100644 index 000000000..1c7520f36 --- /dev/null +++ b/v3/pkg/w32/image.go @@ -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 +} diff --git a/v3/pkg/w32/istream.go b/v3/pkg/w32/istream.go new file mode 100644 index 000000000..a47fbbce1 --- /dev/null +++ b/v3/pkg/w32/istream.go @@ -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))) +} diff --git a/v3/pkg/w32/kernel32.go b/v3/pkg/w32/kernel32.go new file mode 100644 index 000000000..affd497cd --- /dev/null +++ b/v3/pkg/w32/kernel32.go @@ -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) +} diff --git a/v3/pkg/w32/menubar.go b/v3/pkg/w32/menubar.go new file mode 100644 index 000000000..6b11c1e02 --- /dev/null +++ b/v3/pkg/w32/menubar.go @@ -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) + } + } +} diff --git a/v3/pkg/w32/ole32.go b/v3/pkg/w32/ole32.go new file mode 100644 index 000000000..a7d204912 --- /dev/null +++ b/v3/pkg/w32/ole32.go @@ -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 +} diff --git a/v3/pkg/w32/oleaut32.go b/v3/pkg/w32/oleaut32.go new file mode 100644 index 000000000..0bb8ef7da --- /dev/null +++ b/v3/pkg/w32/oleaut32.go @@ -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) +} diff --git a/v3/pkg/w32/popupmenu.go b/v3/pkg/w32/popupmenu.go new file mode 100644 index 000000000..670eabf9f --- /dev/null +++ b/v3/pkg/w32/popupmenu.go @@ -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 +} diff --git a/v3/pkg/w32/screen.go b/v3/pkg/w32/screen.go new file mode 100644 index 000000000..113b1d4cc --- /dev/null +++ b/v3/pkg/w32/screen.go @@ -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 +} diff --git a/v3/pkg/w32/shcore.go b/v3/pkg/w32/shcore.go new file mode 100644 index 000000000..f83062eb3 --- /dev/null +++ b/v3/pkg/w32/shcore.go @@ -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 +} diff --git a/v3/pkg/w32/shell32.go b/v3/pkg/w32/shell32.go new file mode 100644 index 000000000..e8f3f8f08 --- /dev/null +++ b/v3/pkg/w32/shell32.go @@ -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 +} diff --git a/v3/pkg/w32/shlwapi.go b/v3/pkg/w32/shlwapi.go new file mode 100644 index 000000000..89d17ce6f --- /dev/null +++ b/v3/pkg/w32/shlwapi.go @@ -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 +} diff --git a/v3/pkg/w32/taskbar.go b/v3/pkg/w32/taskbar.go new file mode 100644 index 000000000..7f2a697d6 --- /dev/null +++ b/v3/pkg/w32/taskbar.go @@ -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() + } +} diff --git a/v3/pkg/w32/theme.go b/v3/pkg/w32/theme.go new file mode 100644 index 000000000..0e8a8ef17 --- /dev/null +++ b/v3/pkg/w32/theme.go @@ -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 +} diff --git a/v3/pkg/w32/timer.go b/v3/pkg/w32/timer.go new file mode 100644 index 000000000..c95253e53 --- /dev/null +++ b/v3/pkg/w32/timer.go @@ -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 +} diff --git a/v3/pkg/w32/toolbar.go b/v3/pkg/w32/toolbar.go new file mode 100644 index 000000000..ac9261fc4 --- /dev/null +++ b/v3/pkg/w32/toolbar.go @@ -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 +} diff --git a/v3/pkg/w32/typedef.go b/v3/pkg/w32/typedef.go new file mode 100644 index 000000000..cf1c13c09 --- /dev/null +++ b/v3/pkg/w32/typedef.go @@ -0,0 +1,1110 @@ +//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" + "unsafe" + + "golang.org/x/sys/windows" +) + +// From MSDN: Windows Data Types +// http://msdn.microsoft.com/en-us/library/s3f49ktz.aspx +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa383751.aspx +// ATOM WORD +// BOOL int32 +// BOOLEAN byte +// BYTE byte +// CCHAR int8 +// CHAR int8 +// COLORREF DWORD +// DWORD uint32 +// DWORDLONG ULONGLONG +// DWORD_PTR ULONG_PTR +// DWORD32 uint32 +// DWORD64 uint64 +// FLOAT float32 +// HACCEL HANDLE +// HALF_PTR struct{} // ??? +// HANDLE PVOID +// HBITMAP HANDLE +// HBRUSH HANDLE +// HCOLORSPACE HANDLE +// HCONV HANDLE +// HCONVLIST HANDLE +// HCURSOR HANDLE +// HDC HANDLE +// HDDEDATA HANDLE +// HDESK HANDLE +// HDROP HANDLE +// HDWP HANDLE +// HENHMETAFILE HANDLE +// HFILE HANDLE +// HFONT HANDLE +// HGDIOBJ HANDLE +// HGLOBAL HANDLE +// HHOOK HANDLE +// HICON HANDLE +// HINSTANCE HANDLE +// HKEY HANDLE +// HKL HANDLE +// HLOCAL HANDLE +// HMENU HANDLE +// HMETAFILE HANDLE +// HMODULE HANDLE +// HPALETTE HANDLE +// HPEN HANDLE +// HRESULT int32 +// HRGN HANDLE +// HSZ HANDLE +// HWINSTA HANDLE +// HWND HANDLE +// INT int32 +// INT_PTR uintptr +// INT8 int8 +// INT16 int16 +// INT32 int32 +// INT64 int64 +// LANGID WORD +// LCID DWORD +// LCTYPE DWORD +// LGRPID DWORD +// LONG int32 +// LONGLONG int64 +// LONG_PTR uintptr +// LONG32 int32 +// LONG64 int64 +// LPARAM LONG_PTR +// LPBOOL *BOOL +// LPBYTE *BYTE +// LPCOLORREF *COLORREF +// LPCSTR *int8 +// LPCTSTR LPCWSTR +// LPCVOID unsafe.Pointer +// LPCWSTR *WCHAR +// LPDWORD *DWORD +// LPHANDLE *HANDLE +// LPINT *INT +// LPLONG *LONG +// LPSTR *CHAR +// LPTSTR LPWSTR +// LPVOID unsafe.Pointer +// LPWORD *WORD +// LPWSTR *WCHAR +// LRESULT LONG_PTR +// PBOOL *BOOL +// PBOOLEAN *BOOLEAN +// PBYTE *BYTE +// PCHAR *CHAR +// PCSTR *CHAR +// PCTSTR PCWSTR +// PCWSTR *WCHAR +// PDWORD *DWORD +// PDWORDLONG *DWORDLONG +// PDWORD_PTR *DWORD_PTR +// PDWORD32 *DWORD32 +// PDWORD64 *DWORD64 +// PFLOAT *FLOAT +// PHALF_PTR *HALF_PTR +// PHANDLE *HANDLE +// PHKEY *HKEY +// PINT_PTR *INT_PTR +// PINT8 *INT8 +// PINT16 *INT16 +// PINT32 *INT32 +// PINT64 *INT64 +// PLCID *LCID +// PLONG *LONG +// PLONGLONG *LONGLONG +// PLONG_PTR *LONG_PTR +// PLONG32 *LONG32 +// PLONG64 *LONG64 +// POINTER_32 struct{} // ??? +// POINTER_64 struct{} // ??? +// POINTER_SIGNED uintptr +// POINTER_UNSIGNED uintptr +// PSHORT *SHORT +// PSIZE_T *SIZE_T +// PSSIZE_T *SSIZE_T +// PSTR *CHAR +// PTBYTE *TBYTE +// PTCHAR *TCHAR +// PTSTR PWSTR +// PUCHAR *UCHAR +// PUHALF_PTR *UHALF_PTR +// PUINT *UINT +// PUINT_PTR *UINT_PTR +// PUINT8 *UINT8 +// PUINT16 *UINT16 +// PUINT32 *UINT32 +// PUINT64 *UINT64 +// PULONG *ULONG +// PULONGLONG *ULONGLONG +// PULONG_PTR *ULONG_PTR +// PULONG32 *ULONG32 +// PULONG64 *ULONG64 +// PUSHORT *USHORT +// PVOID unsafe.Pointer +// PWCHAR *WCHAR +// PWORD *WORD +// PWSTR *WCHAR +// QWORD uint64 +// SC_HANDLE HANDLE +// SC_LOCK LPVOID +// SERVICE_STATUS_HANDLE HANDLE +// SHORT int16 +// SIZE_T ULONG_PTR +// SSIZE_T LONG_PTR +// TBYTE WCHAR +// TCHAR WCHAR +// UCHAR uint8 +// UHALF_PTR struct{} // ??? +// UINT uint32 +// UINT_PTR uintptr +// UINT8 uint8 +// UINT16 uint16 +// UINT32 uint32 +// UINT64 uint64 +// ULONG uint32 +// ULONGLONG uint64 +// ULONG_PTR uintptr +// ULONG32 uint32 +// ULONG64 uint64 +// USHORT uint16 +// USN LONGLONG +// WCHAR uint16 +// WORD uint16 +// WPARAM UINT_PTR +type ( + ATOM = uint16 + BOOL = int32 + COLORREF = uint32 + DWM_FRAME_COUNT = uint64 + WORD = uint16 + DWORD = uint32 + HACCEL = HANDLE + HANDLE = uintptr + HBITMAP = HANDLE + HBRUSH = HANDLE + HCURSOR = HANDLE + HDC = HANDLE + HDROP = HANDLE + HDWP = HANDLE + HENHMETAFILE = HANDLE + HFONT = HANDLE + HGDIOBJ = HANDLE + HGLOBAL = HANDLE + HGLRC = HANDLE + HHOOK = HANDLE + HICON = HANDLE + HIMAGELIST = HANDLE + HINSTANCE = HANDLE + HKEY = HANDLE + HKL = HANDLE + HMENU = HANDLE + HMODULE = HANDLE + HMONITOR = HANDLE + HPEN = HANDLE + HRESULT = int32 + HRGN = HANDLE + HRSRC = HANDLE + HTHUMBNAIL = HANDLE + HWND = HANDLE + LPARAM = uintptr + LPCVOID = unsafe.Pointer + LRESULT = uintptr + PVOID = unsafe.Pointer + QPC_TIME = uint64 + ULONG_PTR = uintptr + SIZE_T = ULONG_PTR + WPARAM = uintptr + UINT = uint +) + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd162805.aspx +type POINT struct { + X, Y int32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd162897.aspx +type RECT struct { + Left, Top, Right, Bottom int32 +} + +func (r *RECT) String() string { + return fmt.Sprintf("RECT (%p): Left: %d, Top: %d, Right: %d, Bottom: %d", r, r.Left, r.Top, r.Right, r.Bottom) +} + +func RectToPoints(rect *RECT) []POINT { + return []POINT{ + {X: rect.Left, Y: rect.Top}, // Top-left + {X: rect.Right, Y: rect.Bottom}, // Bottom-right + } +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms633577.aspx +type WNDCLASSEX struct { + Size uint32 + Style uint32 + WndProc uintptr + ClsExtra int32 + WndExtra int32 + Instance HINSTANCE + Icon HICON + Cursor HCURSOR + Background HBRUSH + MenuName *uint16 + ClassName *uint16 + IconSm HICON +} + +type TPMPARAMS struct { + CbSize uint32 + RcExclude RECT +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms644958.aspx +type MSG struct { + Hwnd HWND + Message uint32 + WParam uintptr + LParam uintptr + Time uint32 + Pt POINT +} + +// https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-minmaxinfo +type MINMAXINFO struct { + PtReserved POINT + PtMaxSize POINT + PtMaxPosition POINT + PtMinTrackSize POINT + PtMaxTrackSize POINT +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd145037.aspx +type LOGFONT struct { + Height int32 + Width int32 + Escapement int32 + Orientation int32 + Weight int32 + Italic byte + Underline byte + StrikeOut byte + CharSet byte + OutPrecision byte + ClipPrecision byte + Quality byte + PitchAndFamily byte + FaceName [LF_FACESIZE]uint16 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646839.aspx +type OPENFILENAME struct { + StructSize uint32 + Owner HWND + Instance HINSTANCE + Filter *uint16 + CustomFilter *uint16 + MaxCustomFilter uint32 + FilterIndex uint32 + File *uint16 + MaxFile uint32 + FileTitle *uint16 + MaxFileTitle uint32 + InitialDir *uint16 + Title *uint16 + Flags uint32 + FileOffset uint16 + FileExtension uint16 + DefExt *uint16 + CustData uintptr + FnHook uintptr + TemplateName *uint16 + PvReserved unsafe.Pointer + DwReserved uint32 + FlagsEx uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb773205.aspx +type BROWSEINFO struct { + Owner HWND + Root *uint16 + DisplayName *uint16 + Title *uint16 + Flags uint32 + CallbackFunc uintptr + LParam uintptr + Image int32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms221627.aspx +type VARIANT struct { + VT uint16 // 2 + WReserved1 uint16 // 4 + WReserved2 uint16 // 6 + WReserved3 uint16 // 8 + Val int64 // 16 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms221416.aspx +type DISPPARAMS struct { + Rgvarg uintptr + RgdispidNamedArgs uintptr + CArgs uint32 + CNamedArgs uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms221133.aspx +type EXCEPINFO struct { + WCode uint16 + WReserved uint16 + BstrSource *uint16 + BstrDescription *uint16 + BstrHelpFile *uint16 + DwHelpContext uint32 + PvReserved uintptr + PfnDeferredFillIn uintptr + Scode int32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd145035.aspx +type LOGBRUSH struct { + LbStyle uint32 + LbColor COLORREF + LbHatch uintptr +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd183565.aspx +type DEVMODE struct { + DmDeviceName [CCHDEVICENAME]uint16 + DmSpecVersion uint16 + DmDriverVersion uint16 + DmSize uint16 + DmDriverExtra uint16 + DmFields uint32 + DmOrientation int16 + DmPaperSize int16 + DmPaperLength int16 + DmPaperWidth int16 + DmScale int16 + DmCopies int16 + DmDefaultSource int16 + DmPrintQuality int16 + DmColor int16 + DmDuplex int16 + DmYResolution int16 + DmTTOption int16 + DmCollate int16 + DmFormName [CCHFORMNAME]uint16 + DmLogPixels uint16 + DmBitsPerPel uint32 + DmPelsWidth uint32 + DmPelsHeight uint32 + DmDisplayFlags uint32 + DmDisplayFrequency uint32 + DmICMMethod uint32 + DmICMIntent uint32 + DmMediaType uint32 + DmDitherType uint32 + DmReserved1 uint32 + DmReserved2 uint32 + DmPanningWidth uint32 + DmPanningHeight uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd183376.aspx +type BITMAPINFOHEADER struct { + BiSize uint32 + BiWidth int32 + BiHeight int32 + BiPlanes uint16 + BiBitCount uint16 + BiCompression uint32 + BiSizeImage uint32 + BiXPelsPerMeter int32 + BiYPelsPerMeter int32 + BiClrUsed uint32 + BiClrImportant uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd162938.aspx +type RGBQUAD struct { + RgbBlue byte + RgbGreen byte + RgbRed byte + RgbReserved byte +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd183375.aspx +type BITMAPINFO struct { + BmiHeader BITMAPINFOHEADER + BmiColors *RGBQUAD +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd183371.aspx +type BITMAP struct { + BmType int32 + BmWidth int32 + BmHeight int32 + BmWidthBytes int32 + BmPlanes uint16 + BmBitsPixel uint16 + BmBits unsafe.Pointer +} + +type BLENDFUNCTION struct { + BlendOp byte + BlendFlags byte + SourceConstantAlpha byte + AlphaFormat byte +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd183567.aspx +type DIBSECTION struct { + DsBm BITMAP + DsBmih BITMAPINFOHEADER + DsBitfields [3]uint32 + DshSection HANDLE + DsOffset uint32 +} + +type MSGBOXPARAMS struct { + cbSize uint32 + hwndOwner HWND + hInstance HANDLE + lpszText *uint16 + lpszCaption *uint16 + dwStyle uint32 + lpszIcon *uint16 + dwContextHelp uintptr + lpfnMsgBoxCallback uintptr + dwLanguageId uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd162607.aspx +type ENHMETAHEADER struct { + IType uint32 + NSize uint32 + RclBounds RECT + RclFrame RECT + DSignature uint32 + NVersion uint32 + NBytes uint32 + NRecords uint32 + NHandles uint16 + SReserved uint16 + NDescription uint32 + OffDescription uint32 + NPalEntries uint32 + SzlDevice SIZE + SzlMillimeters SIZE + CbPixelFormat uint32 + OffPixelFormat uint32 + BOpenGL uint32 + SzlMicrometers SIZE +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd145106.aspx +type SIZE struct { + CX, CY int32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd145132.aspx +type TEXTMETRIC struct { + TmHeight int32 + TmAscent int32 + TmDescent int32 + TmInternalLeading int32 + TmExternalLeading int32 + TmAveCharWidth int32 + TmMaxCharWidth int32 + TmWeight int32 + TmOverhang int32 + TmDigitizedAspectX int32 + TmDigitizedAspectY int32 + TmFirstChar uint16 + TmLastChar uint16 + TmDefaultChar uint16 + TmBreakChar uint16 + TmItalic byte + TmUnderlined byte + TmStruckOut byte + TmPitchAndFamily byte + TmCharSet byte +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd183574.aspx +type DOCINFO struct { + CbSize int32 + LpszDocName *uint16 + LpszOutput *uint16 + LpszDatatype *uint16 + FwType uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb775514.aspx +type NMHDR struct { + HwndFrom HWND + IdFrom uintptr + Code uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb774743.aspx +type LVCOLUMN struct { + Mask uint32 + Fmt int32 + Cx int32 + PszText *uint16 + CchTextMax int32 + ISubItem int32 + IImage int32 + IOrder int32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb774760.aspx +type LVITEM struct { + Mask uint32 + IItem int32 + ISubItem int32 + State uint32 + StateMask uint32 + PszText *uint16 + CchTextMax int32 + IImage int32 + LParam uintptr + IIndent int32 + IGroupId int32 + CColumns uint32 + PuColumns uint32 +} + +type LVFINDINFO struct { + Flags uint32 + PszText *uint16 + LParam uintptr + Pt POINT + VkDirection uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb774754.aspx +type LVHITTESTINFO struct { + Pt POINT + Flags uint32 + IItem int32 + ISubItem int32 + IGroup int32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb774771.aspx +type NMITEMACTIVATE struct { + Hdr NMHDR + IItem int32 + ISubItem int32 + UNewState uint32 + UOldState uint32 + UChanged uint32 + PtAction POINT + LParam uintptr + UKeyFlags uint32 +} + +type NMLVKEYDOWN struct { + Hdr NMHDR + WVKey uint16 + Flags uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb774773.aspx +type NMLISTVIEW struct { + Hdr NMHDR + IItem int32 + ISubItem int32 + UNewState uint32 + UOldState uint32 + UChanged uint32 + PtAction POINT + LParam uintptr +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb774780.aspx +type NMLVDISPINFO struct { + Hdr NMHDR + Item LVITEM +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb775507.aspx +type INITCOMMONCONTROLSEX struct { + DwSize uint32 + DwICC uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb760256.aspx +type TOOLINFO struct { + CbSize uint32 + UFlags uint32 + Hwnd HWND + UId uintptr + Rect RECT + Hinst HINSTANCE + LpszText *uint16 + LParam uintptr + LpReserved unsafe.Pointer +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms645604.aspx +type TRACKMOUSEEVENT struct { + CbSize uint32 + DwFlags uint32 + HwndTrack HWND + DwHoverTime uint32 +} + +type NOTIFYICONDATA struct { + CbSize uint32 + HWnd HWND + UID uint32 + UFlags uint32 + UCallbackMessage uint32 + HIcon HICON + SzTip [128]uint16 + DwState uint32 + DwStateMask uint32 + SzInfo [256]uint16 + UVersion uint32 + SzInfoTitle [64]uint16 + DwInfoFlags uint32 + GuidItem windows.GUID + HBalloonIcon HICON +} + +const SPI_GETNOTIFYWINDOWRECT = 0x0040 +const SPI_SETWORKAREA = 0x002F + +// Taskbar constants +const ABM_GETTASKBARPOS = 0x00000005 +const ABM_GETSTATE = 0x00000004 +const ABM_GETAUTOHIDEBAR = 0x00000007 +const ABM_SETSTATE = 0x0000000a +const ABM_SETAUTOHIDEBAR = 0x00000008 +const ABM_WINDOWPOSCHANGED = 0x00000009 +const ABM_SETPOS = 0x00000003 + +const ABE_LEFT = 0 +const ABE_TOP = 1 +const ABE_RIGHT = 2 +const ABE_BOTTOM = 3 + +type NOTIFYICONIDENTIFIER struct { + CbSize uint32 + HWnd HWND + UId uint32 + GuidItem windows.GUID +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms534067.aspx +type GdiplusStartupInput struct { + GdiplusVersion uint32 + DebugEventCallback uintptr + SuppressBackgroundThread BOOL + SuppressExternalCodecs BOOL +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms534068.aspx +type GdiplusStartupOutput struct { + NotificationHook uintptr + NotificationUnhook uintptr +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd162768.aspx +type PAINTSTRUCT struct { + Hdc HDC + FErase BOOL + RcPaint RECT + FRestore BOOL + FIncUpdate BOOL + RgbReserved [32]byte +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa363646.aspx +type EVENTLOGRECORD struct { + Length uint32 + Reserved uint32 + RecordNumber uint32 + TimeGenerated uint32 + TimeWritten uint32 + EventID uint32 + EventType uint16 + NumStrings uint16 + EventCategory uint16 + ReservedFlags uint16 + ClosingRecordNumber uint32 + StringOffset uint32 + UserSidLength uint32 + UserSidOffset uint32 + DataLength uint32 + DataOffset uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms685996.aspx +type SERVICE_STATUS struct { + DwServiceType uint32 + DwCurrentState uint32 + DwControlsAccepted uint32 + DwWin32ExitCode uint32 + DwServiceSpecificExitCode uint32 + DwCheckPoint uint32 + DwWaitHint uint32 +} + +/* ------------------------- + Undocumented API +------------------------- */ + +type ACCENT_STATE DWORD + +const ( + ACCENT_DISABLED ACCENT_STATE = 0 + ACCENT_ENABLE_GRADIENT ACCENT_STATE = 1 + ACCENT_ENABLE_TRANSPARENTGRADIENT ACCENT_STATE = 2 + ACCENT_ENABLE_BLURBEHIND ACCENT_STATE = 3 + ACCENT_ENABLE_ACRYLICBLURBEHIND ACCENT_STATE = 4 // RS4 1803 + ACCENT_ENABLE_HOSTBACKDROP ACCENT_STATE = 5 // RS5 1809 + ACCENT_INVALID_STATE ACCENT_STATE = 6 +) + +type ACCENT_POLICY struct { + AccentState ACCENT_STATE + AccentFlags DWORD + GradientColor DWORD + AnimationId DWORD +} + +// ------------------------- + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms684225.aspx +type MODULEENTRY32 struct { + Size uint32 + ModuleID uint32 + ProcessID uint32 + GlblcntUsage uint32 + ProccntUsage uint32 + ModBaseAddr *uint8 + ModBaseSize uint32 + HModule HMODULE + SzModule [MAX_MODULE_NAME32 + 1]uint16 + SzExePath [MAX_PATH]uint16 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms724284.aspx +type FILETIME struct { + DwLowDateTime uint32 + DwHighDateTime uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms682119.aspx +type COORD struct { + X, Y int16 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms686311.aspx +type SMALL_RECT struct { + Left, Top, Right, Bottom int16 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms682093.aspx +type CONSOLE_SCREEN_BUFFER_INFO struct { + DwSize COORD + DwCursorPosition COORD + WAttributes uint16 + SrWindow SMALL_RECT + DwMaximumWindowSize COORD +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb773244.aspx +type MARGINS struct { + CxLeftWidth, CxRightWidth, CyTopHeight, CyBottomHeight int32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa969500.aspx +type DWM_BLURBEHIND struct { + DwFlags uint32 + fEnable BOOL + hRgnBlur HRGN + fTransitionOnMaximized BOOL +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa969501.aspx +type DWM_PRESENT_PARAMETERS struct { + cbSize uint32 + fQueue BOOL + cRefreshStart DWM_FRAME_COUNT + cBuffer uint32 + fUseSourceRate BOOL + rateSource UNSIGNED_RATIO + cRefreshesPerFrame uint32 + eSampling DWM_SOURCE_FRAME_SAMPLING +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa969502.aspx +type DWM_THUMBNAIL_PROPERTIES struct { + dwFlags uint32 + rcDestination RECT + rcSource RECT + opacity byte + fVisible BOOL + fSourceClientAreaOnly BOOL +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa969503.aspx +type DWM_TIMING_INFO struct { + cbSize uint32 + rateRefresh UNSIGNED_RATIO + qpcRefreshPeriod QPC_TIME + rateCompose UNSIGNED_RATIO + qpcVBlank QPC_TIME + cRefresh DWM_FRAME_COUNT + cDXRefresh uint32 + qpcCompose QPC_TIME + cFrame DWM_FRAME_COUNT + cDXPresent uint32 + cRefreshFrame DWM_FRAME_COUNT + cFrameSubmitted DWM_FRAME_COUNT + cDXPresentSubmitted uint32 + cFrameConfirmed DWM_FRAME_COUNT + cDXPresentConfirmed uint32 + cRefreshConfirmed DWM_FRAME_COUNT + cDXRefreshConfirmed uint32 + cFramesLate DWM_FRAME_COUNT + cFramesOutstanding uint32 + cFrameDisplayed DWM_FRAME_COUNT + qpcFrameDisplayed QPC_TIME + cRefreshFrameDisplayed DWM_FRAME_COUNT + cFrameComplete DWM_FRAME_COUNT + qpcFrameComplete QPC_TIME + cFramePending DWM_FRAME_COUNT + qpcFramePending QPC_TIME + cFramesDisplayed DWM_FRAME_COUNT + cFramesComplete DWM_FRAME_COUNT + cFramesPending DWM_FRAME_COUNT + cFramesAvailable DWM_FRAME_COUNT + cFramesDropped DWM_FRAME_COUNT + cFramesMissed DWM_FRAME_COUNT + cRefreshNextDisplayed DWM_FRAME_COUNT + cRefreshNextPresented DWM_FRAME_COUNT + cRefreshesDisplayed DWM_FRAME_COUNT + cRefreshesPresented DWM_FRAME_COUNT + cRefreshStarted DWM_FRAME_COUNT + cPixelsReceived uint64 + cPixelsDrawn uint64 + cBuffersEmpty DWM_FRAME_COUNT +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd389402.aspx +type MilMatrix3x2D struct { + S_11, S_12, S_21, S_22 float64 + DX, DY float64 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa969505.aspx +type UNSIGNED_RATIO struct { + uiNumerator uint32 + uiDenominator uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms632603.aspx +type CREATESTRUCT struct { + CreateParams uintptr + Instance HINSTANCE + Menu HMENU + Parent HWND + Cy, Cx int32 + Y, X int32 + Style int32 + Name *uint16 + Class *uint16 + dwExStyle uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd145065.aspx +type MONITORINFO struct { + CbSize uint32 + RcMonitor RECT + RcWork RECT + DwFlags uint32 +} + +type WINDOWINFO struct { + CbSize DWORD + RcWindow RECT + RcClient RECT + DwStyle DWORD + DwExStyle DWORD + DwWindowStatus DWORD + CxWindowBorders UINT + CyWindowBorders UINT + AtomWindowType ATOM + WCreatorVersion WORD +} + +type MONITOR_DPI_TYPE int32 + +const ( + MDT_EFFECTIVE_DPI MONITOR_DPI_TYPE = 0 + MDT_ANGULAR_DPI MONITOR_DPI_TYPE = 1 + MDT_RAW_DPI MONITOR_DPI_TYPE = 2 + MDT_DEFAULT MONITOR_DPI_TYPE = 0 +) + +func (w *WINDOWINFO) isStyle(style DWORD) bool { + return w.DwStyle&style == style +} + +func (w *WINDOWINFO) IsPopup() bool { + return w.isStyle(WS_POPUP) +} + +func (m *MONITORINFO) Dump() { + fmt.Printf("MONITORINFO (%p)\n", m) + fmt.Printf(" CbSize : %d\n", m.CbSize) + fmt.Printf(" RcMonitor: %s\n", &m.RcMonitor) + fmt.Printf(" RcWork : %s\n", &m.RcWork) + fmt.Printf(" DwFlags : %d\n", m.DwFlags) +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd145066.aspx +type MONITORINFOEX struct { + MONITORINFO + SzDevice [CCHDEVICENAME]uint16 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd368826.aspx +type PIXELFORMATDESCRIPTOR struct { + Size uint16 + Version uint16 + DwFlags uint32 + IPixelType byte + ColorBits byte + RedBits, RedShift byte + GreenBits, GreenShift byte + BlueBits, BlueShift byte + AlphaBits, AlphaShift byte + AccumBits byte + AccumRedBits byte + AccumGreenBits byte + AccumBlueBits byte + AccumAlphaBits byte + DepthBits, StencilBits byte + AuxBuffers byte + ILayerType byte + Reserved byte + DwLayerMask uint32 + DwVisibleMask uint32 + DwDamageMask uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646270(v=vs.85).aspx +type INPUT struct { + Type uint32 + Mi MOUSEINPUT + Ki KEYBDINPUT + Hi HARDWAREINPUT +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646273(v=vs.85).aspx +type MOUSEINPUT struct { + Dx int32 + Dy int32 + MouseData uint32 + DwFlags uint32 + Time uint32 + DwExtraInfo uintptr +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646271(v=vs.85).aspx +type KEYBDINPUT struct { + WVk uint16 + WScan uint16 + DwFlags uint32 + Time uint32 + DwExtraInfo uintptr +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646269(v=vs.85).aspx +type HARDWAREINPUT struct { + UMsg uint32 + WParamL uint16 + WParamH uint16 +} + +type KbdInput struct { + typ uint32 + ki KEYBDINPUT +} + +type MouseInput struct { + typ uint32 + mi MOUSEINPUT +} + +type HardwareInput struct { + typ uint32 + hi HARDWAREINPUT +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms724950(v=vs.85).aspx +type SYSTEMTIME struct { + Year uint16 + Month uint16 + DayOfWeek uint16 + Day uint16 + Hour uint16 + Minute uint16 + Second uint16 + Milliseconds uint16 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms644967(v=vs.85).aspx +type KBDLLHOOKSTRUCT struct { + VkCode DWORD + ScanCode DWORD + Flags DWORD + Time DWORD + DwExtraInfo ULONG_PTR +} + +type HOOKPROC func(int, WPARAM, LPARAM) LRESULT + +type WINDOWPLACEMENT struct { + Length uint32 + Flags uint32 + ShowCmd uint32 + PtMinPosition POINT + PtMaxPosition POINT + RcNormalPosition RECT +} + +type SCROLLINFO struct { + CbSize uint32 + FMask uint32 + NMin int32 + NMax int32 + NPage uint32 + NPos int32 + NTrackPos int32 +} + +type FLASHWINFO struct { + CbSize uint32 + Hwnd HWND + DwFlags DWORD + UCount uint32 + DwTimeout DWORD +} diff --git a/v3/pkg/w32/user32.go b/v3/pkg/w32/user32.go new file mode 100644 index 000000000..95463d86b --- /dev/null +++ b/v3/pkg/w32/user32.go @@ -0,0 +1,1510 @@ +//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" + "runtime" + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var ( + moduser32 = syscall.NewLazyDLL("user32.dll") + + procRegisterClassEx = moduser32.NewProc("RegisterClassExW") + procGetClassName = moduser32.NewProc("GetClassNameW") + procLoadIcon = moduser32.NewProc("LoadIconW") + procLoadCursor = moduser32.NewProc("LoadCursorW") + procShowWindow = moduser32.NewProc("ShowWindow") + procGetDesktopWindow = moduser32.NewProc("GetDesktopWindow") + procShowWindowAsync = moduser32.NewProc("ShowWindowAsync") + procIsZoomed = moduser32.NewProc("IsZoomed") + procUpdateWindow = moduser32.NewProc("UpdateWindow") + procCreateWindowEx = moduser32.NewProc("CreateWindowExW") + procAdjustWindowRect = moduser32.NewProc("AdjustWindowRect") + procAdjustWindowRectEx = moduser32.NewProc("AdjustWindowRectEx") + procDestroyWindow = moduser32.NewProc("DestroyWindow") + procDefWindowProc = moduser32.NewProc("DefWindowProcW") + procDefDlgProc = moduser32.NewProc("DefDlgProcW") + procPostQuitMessage = moduser32.NewProc("PostQuitMessage") + procGetMessage = moduser32.NewProc("GetMessageW") + procTranslateMessage = moduser32.NewProc("TranslateMessage") + procDispatchMessage = moduser32.NewProc("DispatchMessageW") + procSendMessage = moduser32.NewProc("SendMessageW") + procPostMessage = moduser32.NewProc("PostMessageW") + procWaitMessage = moduser32.NewProc("WaitMessage") + procSetWindowText = moduser32.NewProc("SetWindowTextW") + procGetWindowTextLength = moduser32.NewProc("GetWindowTextLengthW") + procGetWindowText = moduser32.NewProc("GetWindowTextW") + procGetWindowRect = moduser32.NewProc("GetWindowRect") + procGetWindowInfo = moduser32.NewProc("GetWindowInfo") + procGetWindow = moduser32.NewProc("GetWindow") + procSetWindowCompositionAttribute = moduser32.NewProc("SetWindowCompositionAttribute") + procMoveWindow = moduser32.NewProc("MoveWindow") + procScreenToClient = moduser32.NewProc("ScreenToClient") + procCallWindowProc = moduser32.NewProc("CallWindowProcW") + procSetWindowLong = moduser32.NewProc("SetWindowLongW") + procSetWindowLongPtr = moduser32.NewProc("SetWindowLongW") + procGetWindowLong = moduser32.NewProc("GetWindowLongW") + procGetWindowLongPtr = moduser32.NewProc("GetWindowLongW") + procEnableWindow = moduser32.NewProc("EnableWindow") + procIsWindowEnabled = moduser32.NewProc("IsWindowEnabled") + procIsWindowVisible = moduser32.NewProc("IsWindowVisible") + procSetFocus = moduser32.NewProc("SetFocus") + procGetFocus = moduser32.NewProc("GetFocus") + procSetActiveWindow = moduser32.NewProc("SetActiveWindow") + procSetForegroundWindow = moduser32.NewProc("SetForegroundWindow") + procBringWindowToTop = moduser32.NewProc("BringWindowToTop") + procInvalidateRect = moduser32.NewProc("InvalidateRect") + procGetClientRect = moduser32.NewProc("GetClientRect") + procGetDC = moduser32.NewProc("GetDC") + procReleaseDC = moduser32.NewProc("ReleaseDC") + procSetCapture = moduser32.NewProc("SetCapture") + procReleaseCapture = moduser32.NewProc("ReleaseCapture") + procGetWindowThreadProcessId = moduser32.NewProc("GetWindowThreadProcessId") + procMessageBox = moduser32.NewProc("MessageBoxW") + procMessageBoxIndirect = moduser32.NewProc("MessageBoxIndirectW") + procGetSystemMetrics = moduser32.NewProc("GetSystemMetrics") + procPostThreadMessageW = moduser32.NewProc("PostThreadMessageW") + procRegisterWindowMessageA = moduser32.NewProc("RegisterWindowMessageA") + procCopyRect = moduser32.NewProc("CopyRect") + procEqualRect = moduser32.NewProc("EqualRect") + procInflateRect = moduser32.NewProc("InflateRect") + procIntersectRect = moduser32.NewProc("IntersectRect") + procIsRectEmpty = moduser32.NewProc("IsRectEmpty") + procOffsetRect = moduser32.NewProc("OffsetRect") + procPtInRect = moduser32.NewProc("PtInRect") + procSetRect = moduser32.NewProc("SetRect") + procSetRectEmpty = moduser32.NewProc("SetRectEmpty") + procSubtractRect = moduser32.NewProc("SubtractRect") + procUnionRect = moduser32.NewProc("UnionRect") + procCreateDialogParam = moduser32.NewProc("CreateDialogParamW") + procDialogBoxParam = moduser32.NewProc("DialogBoxParamW") + procGetDlgItem = moduser32.NewProc("GetDlgItem") + procDrawIcon = moduser32.NewProc("DrawIcon") + procCreateMenu = moduser32.NewProc("CreateMenu") + procRemoveMenu = moduser32.NewProc("RemoveMenu") + procGetMenuItemPosition = moduser32.NewProc("GetMenuItemPosition") + procDestroyMenu = moduser32.NewProc("DestroyMenu") + procCreatePopupMenu = moduser32.NewProc("CreatePopupMenu") + procCheckMenuRadioItem = moduser32.NewProc("CheckMenuRadioItem") + procCreateIconFromResourceEx = moduser32.NewProc("CreateIconFromResourceEx") + procInsertMenuItem = moduser32.NewProc("InsertMenuItemW") + procCheckMenuItem = moduser32.NewProc("CheckMenuItem") + procClientToScreen = moduser32.NewProc("ClientToScreen") + procIsDialogMessage = moduser32.NewProc("IsDialogMessageW") + procIsWindow = moduser32.NewProc("IsWindow") + procEndDialog = moduser32.NewProc("EndDialog") + procPeekMessage = moduser32.NewProc("PeekMessageW") + procTranslateAccelerator = moduser32.NewProc("TranslateAcceleratorW") + procSetWindowPos = moduser32.NewProc("SetWindowPos") + procFillRect = moduser32.NewProc("FillRect") + procFrameRect = moduser32.NewProc("FrameRect") + procDrawText = moduser32.NewProc("DrawTextW") + procAddClipboardFormatListener = moduser32.NewProc("AddClipboardFormatListener") + procRemoveClipboardFormatListener = moduser32.NewProc("RemoveClipboardFormatListener") + procOpenClipboard = moduser32.NewProc("OpenClipboard") + procCloseClipboard = moduser32.NewProc("CloseClipboard") + procEnumClipboardFormats = moduser32.NewProc("EnumClipboardFormats") + procGetClipboardData = moduser32.NewProc("GetClipboardData") + procSetClipboardData = moduser32.NewProc("SetClipboardData") + procEmptyClipboard = moduser32.NewProc("EmptyClipboard") + procGetClipboardFormatName = moduser32.NewProc("GetClipboardFormatNameW") + procIsClipboardFormatAvailable = moduser32.NewProc("IsClipboardFormatAvailable") + procBeginPaint = moduser32.NewProc("BeginPaint") + procEndPaint = moduser32.NewProc("EndPaint") + procGetKeyboardState = moduser32.NewProc("GetKeyboardState") + procMapVirtualKey = moduser32.NewProc("MapVirtualKeyW") + procMapVirtualKeyEx = moduser32.NewProc("MapVirtualKeyExW") + procGetAsyncKeyState = moduser32.NewProc("GetAsyncKeyState") + procToAscii = moduser32.NewProc("ToAscii") + procSwapMouseButton = moduser32.NewProc("SwapMouseButton") + procGetCursorPos = moduser32.NewProc("GetCursorPos") + procSetCursorPos = moduser32.NewProc("SetCursorPos") + procSetCursor = moduser32.NewProc("SetCursor") + procCreateIcon = moduser32.NewProc("CreateIcon") + procDestroyIcon = moduser32.NewProc("DestroyIcon") + procMonitorFromPoint = moduser32.NewProc("MonitorFromPoint") + procMonitorFromRect = moduser32.NewProc("MonitorFromRect") + procMonitorFromWindow = moduser32.NewProc("MonitorFromWindow") + procGetMonitorInfo = moduser32.NewProc("GetMonitorInfoW") + procGetDpiForSystem = moduser32.NewProc("GetDpiForSystem") + procGetDpiForWindow = moduser32.NewProc("GetDpiForWindow") + procSetProcessDPIAware = moduser32.NewProc("SetProcessDPIAware") + procSetProcessDpiAwarenessContext = moduser32.NewProc("SetProcessDpiAwarenessContext") + procEnumDisplayMonitors = moduser32.NewProc("EnumDisplayMonitors") + procEnumDisplayDevices = moduser32.NewProc("EnumDisplayDevicesW") + procEnumDisplaySettings = moduser32.NewProc("EnumDisplaySettingsW") + procEnumDisplaySettingsEx = moduser32.NewProc("EnumDisplaySettingsExW") + procEnumWindows = moduser32.NewProc("EnumWindows") + procEnumChildWindows = moduser32.NewProc("EnumChildWindows") + procChangeDisplaySettingsEx = moduser32.NewProc("ChangeDisplaySettingsExW") + procSetTimer = moduser32.NewProc("SetTimer") + procKillTimer = moduser32.NewProc("KillTimer") + procKeybdEvent = moduser32.NewProc("keybd_event") + procSendInput = moduser32.NewProc("SendInput") + procSetWindowsHookEx = moduser32.NewProc("SetWindowsHookExW") + procUnhookWindowsHookEx = moduser32.NewProc("UnhookWindowsHookEx") + procCallNextHookEx = moduser32.NewProc("CallNextHookEx") + procGetForegroundWindow = moduser32.NewProc("GetForegroundWindow") + procUpdateLayeredWindow = moduser32.NewProc("UpdateLayeredWindow") + getDisplayConfig = moduser32.NewProc("GetDisplayConfigBufferSizes") + queryDisplayConfig = moduser32.NewProc("QueryDisplayConfig") + + procSystemParametersInfo = moduser32.NewProc("SystemParametersInfoW") + procSetClassLong = moduser32.NewProc("SetClassLongW") + procSetClassLongPtr = moduser32.NewProc("SetClassLongPtrW") + + procSetMenu = moduser32.NewProc("SetMenu") + procAppendMenu = moduser32.NewProc("AppendMenuW") + procSetMenuItemInfo = moduser32.NewProc("SetMenuItemInfoW") + procDrawMenuBar = moduser32.NewProc("DrawMenuBar") + procTrackPopupMenu = moduser32.NewProc("TrackPopupMenu") + procTrackPopupMenuEx = moduser32.NewProc("TrackPopupMenuEx") + procGetMenuBarInfo = moduser32.NewProc("GetMenuBarInfo") + procMapWindowPoints = moduser32.NewProc("MapWindowPoints") + procGetKeyState = moduser32.NewProc("GetKeyState") + procGetSysColorBrush = moduser32.NewProc("GetSysColorBrush") + procGetSysColor = moduser32.NewProc("GetSysColor") + + procGetWindowPlacement = moduser32.NewProc("GetWindowPlacement") + procSetWindowPlacement = moduser32.NewProc("SetWindowPlacement") + procGetWindowDC = moduser32.NewProc("GetWindowDC") + + procGetScrollInfo = moduser32.NewProc("GetScrollInfo") + procSetScrollInfo = moduser32.NewProc("SetScrollInfo") + + procFlashWindowEx = moduser32.NewProc("FlashWindowEx") + + procSetMenuItemBitmaps = moduser32.NewProc("SetMenuItemBitmaps") + + procRedrawWindow = moduser32.NewProc("RedrawWindow") + + procRegisterWindowMessageW = moduser32.NewProc("RegisterWindowMessageW") + procSetWindowDisplayAffinity = moduser32.NewProc("SetWindowDisplayAffinity") + + mainThread HANDLE +) + +func init() { + runtime.LockOSThread() + mainThread = GetCurrentThreadId() +} + +func GetWindowDC(hwnd HWND) HDC { + ret, _, _ := procGetWindowDC.Call(hwnd) + return ret +} + +func GET_X_LPARAM(lp uintptr) int32 { + return int32(int16(LOWORD(uint32(lp)))) +} + +func GET_Y_LPARAM(lp uintptr) int32 { + return int32(int16(HIWORD(uint32(lp)))) +} + +func RegisterClassEx(wndClassEx *WNDCLASSEX) ATOM { + ret, _, _ := procRegisterClassEx.Call(uintptr(unsafe.Pointer(wndClassEx))) + return ATOM(ret) +} + +func SetMenuItemBitmaps(hMenu HMENU, uPosition, uFlags uint32, hBitmapUnchecked HBITMAP, hBitmapChecked HBITMAP) error { + ret, _, _ := procSetMenuItemBitmaps.Call( + hMenu, + uintptr(uPosition), + uintptr(uFlags), + hBitmapUnchecked, + hBitmapChecked) + + if ret == 0 { + return windows.GetLastError() + } + return nil +} + +func GetDesktopWindow() HWND { + ret, _, _ := procGetDesktopWindow.Call() + return ret +} + +func LoadIcon(instance HINSTANCE, iconName *uint16) HICON { + ret, _, _ := procLoadIcon.Call( + uintptr(instance), + uintptr(unsafe.Pointer(iconName))) + + return HICON(ret) +} + +func LoadIconWithResourceID(instance HINSTANCE, res uint16) HICON { + ret, _, _ := procLoadIcon.Call( + uintptr(instance), + uintptr(res)) + + return HICON(ret) +} + +func LoadCursor(instance HINSTANCE, cursorName *uint16) HCURSOR { + ret, _, _ := procLoadCursor.Call( + uintptr(instance), + uintptr(unsafe.Pointer(cursorName))) + + return HCURSOR(ret) +} + +func LoadCursorWithResourceID(instance HINSTANCE, res uint16) HCURSOR { + ret, _, _ := procLoadCursor.Call( + uintptr(instance), + uintptr(res)) + + return HCURSOR(ret) +} + +func MessageBoxIndirect(msgbox *MSGBOXPARAMS) int32 { + ret, _, _ := procMessageBoxIndirect.Call( + uintptr(unsafe.Pointer(msgbox))) + + return int32(ret) +} + +func ShowWindow(hwnd HWND, cmdshow int) bool { + ret, _, _ := procShowWindow.Call( + uintptr(hwnd), + uintptr(cmdshow)) + + return ret != 0 +} + +func IsZoomed(hwnd HWND) bool { + ret, _, _ := procIsZoomed.Call(uintptr(hwnd)) + return ret != 0 +} + +func ShowWindowAsync(hwnd HWND, cmdshow int) bool { + ret, _, _ := procShowWindowAsync.Call( + uintptr(hwnd), + uintptr(cmdshow)) + + return ret != 0 +} + +func UpdateWindow(hwnd HWND) bool { + ret, _, _ := procUpdateWindow.Call( + uintptr(hwnd)) + return ret != 0 +} + +func UpdateLayeredWindow(hwnd HWND, hdcDst HDC, pptDst *POINT, psize *SIZE, + hdcSrc HDC, pptSrc *POINT, crKey COLORREF, pblend *BLENDFUNCTION, dwFlags DWORD) bool { + ret, _, _ := procUpdateLayeredWindow.Call( + hwnd, + hdcDst, + uintptr(unsafe.Pointer(pptDst)), + uintptr(unsafe.Pointer(psize)), + hdcSrc, + uintptr(unsafe.Pointer(pptSrc)), + uintptr(crKey), + uintptr(unsafe.Pointer(pblend)), + uintptr(dwFlags)) + return ret != 0 +} + +func PostThreadMessage(threadID HANDLE, msg int, wp, lp uintptr) { + procPostThreadMessageW.Call(threadID, uintptr(msg), wp, lp) +} + +func RegisterWindowMessage(name *uint16) uint32 { + ret, _, _ := procRegisterWindowMessageW.Call(uintptr(unsafe.Pointer(name))) + return uint32(ret) +} + +func PostMainThreadMessage(msg uint32, wp, lp uintptr) bool { + ret, _, _ := procPostThreadMessageW.Call(mainThread, uintptr(msg), wp, lp) + return ret != 0 +} + +func CreateWindowEx(exStyle uint, className, windowName *uint16, + style uint, x, y, width, height int, parent HWND, menu HMENU, + instance HINSTANCE, param unsafe.Pointer) HWND { + ret, _, _ := procCreateWindowEx.Call( + uintptr(exStyle), + uintptr(unsafe.Pointer(className)), + uintptr(unsafe.Pointer(windowName)), + uintptr(style), + uintptr(x), + uintptr(y), + uintptr(width), + uintptr(height), + uintptr(parent), + uintptr(menu), + uintptr(instance), + uintptr(param)) + + return HWND(ret) +} + +func AdjustWindowRectEx(rect *RECT, style uint, menu bool, exStyle uint) bool { + ret, _, _ := procAdjustWindowRectEx.Call( + uintptr(unsafe.Pointer(rect)), + uintptr(style), + uintptr(BoolToBOOL(menu)), + uintptr(exStyle)) + + return ret != 0 +} + +func AdjustWindowRect(rect *RECT, style uint, menu bool) bool { + ret, _, _ := procAdjustWindowRect.Call( + uintptr(unsafe.Pointer(rect)), + uintptr(style), + uintptr(BoolToBOOL(menu))) + + return ret != 0 +} + +func DestroyWindow(hwnd HWND) bool { + ret, _, _ := procDestroyWindow.Call(hwnd) + return ret != 0 +} + +func HasGetDpiForWindowFunc() bool { + err := procGetDpiForWindow.Find() + return err == nil +} + +func GetDpiForWindow(hwnd HWND) UINT { + dpi, _, _ := procGetDpiForWindow.Call(hwnd) + return uint(dpi) +} + +func HasSetProcessDPIAwareFunc() bool { + err := procSetProcessDPIAware.Find() + return err == nil +} + +func GetClassName(hwnd HWND) string { + var buf [256]uint16 + procGetClassName.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(len(buf))) + + return syscall.UTF16ToString(buf[:]) +} + +func SetProcessDPIAware() error { + status, r, err := procSetProcessDPIAware.Call() + if status == 0 { + return fmt.Errorf("SetProcessDPIAware failed %d: %v %v", status, r, err) + } + return nil +} + +func HasSetProcessDpiAwarenessContextFunc() bool { + err := procSetProcessDpiAwarenessContext.Find() + return err == nil +} + +func SetProcessDpiAwarenessContext(ctx uintptr) error { + status, r, err := procSetProcessDpiAwarenessContext.Call(ctx) + if status == 0 { + return fmt.Errorf("SetProcessDpiAwarenessContext failed %d: %v %v", status, r, err) + } + return nil +} + +func GetForegroundWindow() HWND { + ret, _, _ := procGetForegroundWindow.Call() + return HWND(ret) +} + +func SetWindowCompositionAttribute(hwnd HWND, data *WINDOWCOMPOSITIONATTRIBDATA) bool { + if procSetWindowCompositionAttribute != nil { + ret, _, _ := procSetWindowCompositionAttribute.Call( + hwnd, + uintptr(unsafe.Pointer(data)), + ) + return ret != 0 + } + return false +} + +func DefWindowProc(hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr { + ret, _, _ := procDefWindowProc.Call( + uintptr(hwnd), + uintptr(msg), + wParam, + lParam) + + return ret +} + +func DefDlgProc(hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr { + ret, _, _ := procDefDlgProc.Call( + uintptr(hwnd), + uintptr(msg), + wParam, + lParam) + + return ret +} + +func PostQuitMessage(exitCode int) { + procPostQuitMessage.Call( + uintptr(exitCode)) +} + +func GetMessage(msg *MSG, hwnd HWND, msgFilterMin, msgFilterMax uint32) int { + ret, _, _ := procGetMessage.Call( + uintptr(unsafe.Pointer(msg)), + uintptr(hwnd), + uintptr(msgFilterMin), + uintptr(msgFilterMax)) + + return int(ret) +} + +func TranslateMessage(msg *MSG) bool { + ret, _, _ := procTranslateMessage.Call( + uintptr(unsafe.Pointer(msg))) + + return ret != 0 + +} + +func DispatchMessage(msg *MSG) uintptr { + ret, _, _ := procDispatchMessage.Call( + uintptr(unsafe.Pointer(msg))) + + return ret + +} + +func SendMessage(hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr { + ret, _, _ := procSendMessage.Call( + uintptr(hwnd), + uintptr(msg), + wParam, + lParam) + + return ret +} + +func PostMessage(hwnd HWND, msg uint32, wParam, lParam uintptr) bool { + ret, _, _ := procPostMessage.Call( + uintptr(hwnd), + uintptr(msg), + wParam, + lParam) + + return ret != 0 +} + +func WaitMessage() bool { + ret, _, _ := procWaitMessage.Call() + return ret != 0 +} + +func SetWindowText(hwnd HWND, text string) { + procSetWindowText.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text)))) +} + +func GetWindowTextLength(hwnd HWND) int { + ret, _, _ := procGetWindowTextLength.Call( + uintptr(hwnd)) + + return int(ret) +} + +func GetWindowInfo(hwnd HWND, info *WINDOWINFO) int { + ret, _, _ := procGetWindowInfo.Call( + hwnd, + uintptr(unsafe.Pointer(info)), + ) + return int(ret) +} + +func GetWindow(hwnd HWND, cmd uint32) HWND { + ret, _, _ := procGetWindow.Call( + hwnd, + uintptr(cmd), + ) + return HWND(ret) +} + +func GetWindowText(hwnd HWND) string { + textLen := GetWindowTextLength(hwnd) + 1 + + buf := make([]uint16, textLen) + procGetWindowText.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(textLen)) + + return syscall.UTF16ToString(buf) +} + +func GetWindowRect(hwnd HWND) *RECT { + var rect RECT + procGetWindowRect.Call( + hwnd, + uintptr(unsafe.Pointer(&rect))) + + return &rect +} + +func MoveWindow(hwnd HWND, x, y, width, height int, repaint bool) bool { + ret, _, _ := procMoveWindow.Call( + uintptr(hwnd), + uintptr(x), + uintptr(y), + uintptr(width), + uintptr(height), + uintptr(BoolToBOOL(repaint))) + + return ret != 0 + +} + +func ScreenToClient(hwnd HWND, x, y int) (X, Y int, ok bool) { + pt := POINT{X: int32(x), Y: int32(y)} + ret, _, _ := procScreenToClient.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(&pt))) + + return int(pt.X), int(pt.Y), ret != 0 +} + +func CallWindowProc(preWndProc uintptr, hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr { + ret, _, _ := procCallWindowProc.Call( + preWndProc, + uintptr(hwnd), + uintptr(msg), + wParam, + lParam) + + return ret +} + +func SetWindowLong(hwnd HWND, index int, value uint32) uint32 { + ret, _, _ := procSetWindowLong.Call( + uintptr(hwnd), + uintptr(index), + uintptr(value)) + + return uint32(ret) +} + +func SetWindowLongPtr(hwnd HWND, index int, value uintptr) uintptr { + ret, _, _ := procSetWindowLongPtr.Call( + uintptr(hwnd), + uintptr(index), + value) + + return ret +} + +func GetWindowLong(hwnd HWND, index int) int32 { + ret, _, _ := procGetWindowLong.Call( + uintptr(hwnd), + uintptr(index)) + + return int32(ret) +} + +func GetWindowLongPtr(hwnd HWND, index int) uintptr { + ret, _, _ := procGetWindowLongPtr.Call( + uintptr(hwnd), + uintptr(index)) + + return ret +} + +func EnableWindow(hwnd HWND, b bool) bool { + ret, _, _ := procEnableWindow.Call( + uintptr(hwnd), + uintptr(BoolToBOOL(b))) + return ret != 0 +} + +func IsWindowEnabled(hwnd HWND) bool { + ret, _, _ := procIsWindowEnabled.Call( + uintptr(hwnd)) + + return ret != 0 +} + +func IsWindowVisible(hwnd HWND) bool { + ret, _, _ := procIsWindowVisible.Call( + uintptr(hwnd)) + + return ret != 0 +} + +func SetFocus(hwnd HWND) HWND { + ret, _, _ := procSetFocus.Call( + uintptr(hwnd)) + + return HWND(ret) +} + +func SetActiveWindow(hwnd HWND) HWND { + ret, _, _ := procSetActiveWindow.Call( + uintptr(hwnd)) + + return HWND(ret) +} + +func BringWindowToTop(hwnd HWND) bool { + ret, _, _ := procBringWindowToTop.Call(uintptr(hwnd)) + return ret != 0 +} + +func SetForegroundWindow(hwnd HWND) HWND { + ret, _, _ := procSetForegroundWindow.Call( + uintptr(hwnd)) + + return HWND(ret) +} + +func GetFocus() HWND { + ret, _, _ := procGetFocus.Call() + return HWND(ret) +} + +func InvalidateRect(hwnd HWND, rect *RECT, erase bool) bool { + ret, _, _ := procInvalidateRect.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(rect)), + uintptr(BoolToBOOL(erase))) + + return ret != 0 +} + +func GetClientRect(hwnd HWND) *RECT { + var rect RECT + ret, _, _ := procGetClientRect.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(&rect))) + + if ret == 0 { + panic(fmt.Sprintf("GetClientRect(%d) failed", hwnd)) + } + + return &rect +} + +func GetDC(hwnd HWND) HDC { + ret, _, _ := procGetDC.Call( + uintptr(hwnd)) + + return HDC(ret) +} + +func ReleaseDC(hwnd HWND, hDC HDC) bool { + ret, _, _ := procReleaseDC.Call( + uintptr(hwnd), + uintptr(hDC)) + + return ret != 0 +} + +func SetCapture(hwnd HWND) HWND { + ret, _, _ := procSetCapture.Call( + uintptr(hwnd)) + + return HWND(ret) +} + +func ReleaseCapture() bool { + ret, _, _ := procReleaseCapture.Call() + + return ret != 0 +} + +func EnumWindows(enumFunc uintptr, lparam uintptr) bool { + ret, _, _ := procEnumWindows.Call( + enumFunc, + lparam) + + return ret != 0 +} + +func GetWindowThreadProcessId(hwnd HWND) (HANDLE, int) { + var processId int + ret, _, _ := procGetWindowThreadProcessId.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(&processId))) + + return HANDLE(ret), processId +} + +func MessageBox(hwnd HWND, title, caption string, flags uint) int { + ret, _, _ := procMessageBox.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(title))), + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(caption))), + uintptr(flags)) + + return int(ret) +} + +func GetSystemMetrics(index int) int { + ret, _, _ := procGetSystemMetrics.Call( + uintptr(index)) + + return int(ret) +} + +func GetSysColorBrush(nIndex int) HBRUSH { + ret, _, _ := procGetSysColorBrush.Call(1, + uintptr(nIndex), + 0, + 0) + + return HBRUSH(ret) +} + +func GetSysColor(nIndex int) COLORREF { + ret, _, _ := procGetSysColor.Call( + uintptr(nIndex)) + + return COLORREF(ret) +} + +func CopyRect(dst, src *RECT) bool { + ret, _, _ := procCopyRect.Call( + uintptr(unsafe.Pointer(dst)), + uintptr(unsafe.Pointer(src))) + + return ret != 0 +} + +func EqualRect(rect1, rect2 *RECT) bool { + ret, _, _ := procEqualRect.Call( + uintptr(unsafe.Pointer(rect1)), + uintptr(unsafe.Pointer(rect2))) + + return ret != 0 +} + +func InflateRect(rect *RECT, dx, dy int) bool { + ret, _, _ := procInflateRect.Call( + uintptr(unsafe.Pointer(rect)), + uintptr(dx), + uintptr(dy)) + + return ret != 0 +} + +func IntersectRect(dst, src1, src2 *RECT) bool { + ret, _, _ := procIntersectRect.Call( + uintptr(unsafe.Pointer(dst)), + uintptr(unsafe.Pointer(src1)), + uintptr(unsafe.Pointer(src2))) + + return ret != 0 +} + +func IsRectEmpty(rect *RECT) bool { + ret, _, _ := procIsRectEmpty.Call( + uintptr(unsafe.Pointer(rect))) + + return ret != 0 +} + +func OffsetRect(rect *RECT, dx, dy int) bool { + ret, _, _ := procOffsetRect.Call( + uintptr(unsafe.Pointer(rect)), + uintptr(dx), + uintptr(dy)) + + return ret != 0 +} + +func PtInRect(rect *RECT, x, y int) bool { + pt := POINT{X: int32(x), Y: int32(y)} + ret, _, _ := procPtInRect.Call( + uintptr(unsafe.Pointer(rect)), + uintptr(unsafe.Pointer(&pt))) + + return ret != 0 +} + +func RectInRect(rect1, rect2 *RECT) bool { + return rect1.Left >= rect2.Left && rect1.Right <= rect2.Right && + rect1.Top >= rect2.Top && rect1.Bottom <= rect2.Bottom +} + +func SetRect(rect *RECT, left, top, right, bottom int) bool { + ret, _, _ := procSetRect.Call( + uintptr(unsafe.Pointer(rect)), + uintptr(left), + uintptr(top), + uintptr(right), + uintptr(bottom)) + + return ret != 0 +} + +func SetRectEmpty(rect *RECT) bool { + ret, _, _ := procSetRectEmpty.Call( + uintptr(unsafe.Pointer(rect))) + + return ret != 0 +} + +func SubtractRect(dst, src1, src2 *RECT) bool { + ret, _, _ := procSubtractRect.Call( + uintptr(unsafe.Pointer(dst)), + uintptr(unsafe.Pointer(src1)), + uintptr(unsafe.Pointer(src2))) + + return ret != 0 +} + +func UnionRect(dst, src1, src2 *RECT) bool { + ret, _, _ := procUnionRect.Call( + uintptr(unsafe.Pointer(dst)), + uintptr(unsafe.Pointer(src1)), + uintptr(unsafe.Pointer(src2))) + + return ret != 0 +} + +func CreateDialog(hInstance HINSTANCE, lpTemplate *uint16, hWndParent HWND, lpDialogProc uintptr) HWND { + ret, _, _ := procCreateDialogParam.Call( + uintptr(hInstance), + uintptr(unsafe.Pointer(lpTemplate)), + uintptr(hWndParent), + lpDialogProc, + 0) + + return HWND(ret) +} + +func DialogBox(hInstance HINSTANCE, lpTemplateName *uint16, hWndParent HWND, lpDialogProc uintptr) int { + ret, _, _ := procDialogBoxParam.Call( + uintptr(hInstance), + uintptr(unsafe.Pointer(lpTemplateName)), + uintptr(hWndParent), + lpDialogProc, + 0) + + return int(ret) +} + +func GetDlgItem(hDlg HWND, nIDDlgItem int) HWND { + ret, _, _ := procGetDlgItem.Call( + uintptr(unsafe.Pointer(hDlg)), + uintptr(nIDDlgItem)) + + return HWND(ret) +} + +func DrawIcon(hDC HDC, x, y int, hIcon HICON) bool { + ret, _, _ := procDrawIcon.Call( + uintptr(unsafe.Pointer(hDC)), + uintptr(x), + uintptr(y), + uintptr(unsafe.Pointer(hIcon))) + + return ret != 0 +} + +func CreateMenu() HMENU { + ret, _, _ := procCreateMenu.Call(0, + 0, + 0, + 0) + + return HMENU(ret) +} + +func SetMenu(hWnd HWND, hMenu HMENU) bool { + + ret, _, _ := procSetMenu.Call(hWnd, hMenu) + return ret != 0 +} + +func AppendMenu(hMenu HMENU, uFlags uint32, uIDNewItem uintptr, lpNewItem *uint16) bool { + ret, _, _ := procAppendMenu.Call( + hMenu, + uintptr(uFlags), + uIDNewItem, + uintptr(unsafe.Pointer(lpNewItem))) + + return ret != 0 +} + +// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-checkmenuradioitem +func SelectRadioMenuItem(menuID uint16, startID uint16, endID uint16, hwnd HWND) bool { + ret, _, _ := procCheckMenuRadioItem.Call( + hwnd, + uintptr(startID), + uintptr(endID), + uintptr(menuID), + MF_BYCOMMAND) + return ret != 0 + +} + +func CreatePopupMenu() PopupMenu { + ret, _, _ := procCreatePopupMenu.Call(0, + 0, + 0, + 0) + + return PopupMenu(ret) +} + +func TrackPopupMenuEx(hMenu HMENU, fuFlags uint32, x, y int32, hWnd HWND, lptpm *TPMPARAMS) bool { + + ret, _, _ := procTrackPopupMenuEx.Call( + hMenu, + uintptr(fuFlags), + uintptr(x), + uintptr(y), + hWnd, + uintptr(unsafe.Pointer(lptpm))) + + return ret != 0 +} + +func DrawMenuBar(hWnd HWND) bool { + ret, _, _ := procDrawMenuBar.Call(hWnd, 0, 0) + return ret != 0 +} + +func InsertMenuItem(hMenu HMENU, uItem uint32, fByPosition bool, lpmii *MENUITEMINFO) bool { + ret, _, _ := procInsertMenuItem.Call( + hMenu, + uintptr(uItem), + uintptr(BoolToBOOL(fByPosition)), + uintptr(unsafe.Pointer(lpmii)), + 0, + 0) + + return ret != 0 +} + +func SetMenuItemInfo(hMenu HMENU, uItem uint32, fByPosition bool, lpmii *MENUITEMINFO) bool { + ret, _, _ := procSetMenuItemInfo.Call( + hMenu, + uintptr(uItem), + uintptr(BoolToBOOL(fByPosition)), + uintptr(unsafe.Pointer(lpmii)), + 0, + 0) + + return ret != 0 +} + +func ClientToScreen(hwnd HWND, x, y int) (int, int) { + pt := POINT{X: int32(x), Y: int32(y)} + + procClientToScreen.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(&pt))) + + return int(pt.X), int(pt.Y) +} + +func IsDialogMessage(hwnd HWND, msg *MSG) bool { + ret, _, _ := procIsDialogMessage.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(msg))) + + return ret != 0 +} + +func IsWindow(hwnd HWND) bool { + ret, _, _ := procIsWindow.Call( + uintptr(hwnd)) + + return ret != 0 +} + +func EndDialog(hwnd HWND, nResult uintptr) bool { + ret, _, _ := procEndDialog.Call( + uintptr(hwnd), + nResult) + + return ret != 0 +} + +func PeekMessage(lpMsg *MSG, hwnd HWND, wMsgFilterMin, wMsgFilterMax, wRemoveMsg uint32) bool { + ret, _, _ := procPeekMessage.Call( + uintptr(unsafe.Pointer(lpMsg)), + uintptr(hwnd), + uintptr(wMsgFilterMin), + uintptr(wMsgFilterMax), + uintptr(wRemoveMsg)) + + return ret != 0 +} + +func TranslateAccelerator(hwnd HWND, hAccTable HACCEL, lpMsg *MSG) bool { + ret, _, _ := procTranslateMessage.Call( + uintptr(hwnd), + uintptr(hAccTable), + uintptr(unsafe.Pointer(lpMsg))) + + return ret != 0 +} + +func SetWindowPos(hwnd, hWndInsertAfter HWND, x, y, cx, cy int, uFlags uint) bool { + ret, _, _ := procSetWindowPos.Call( + uintptr(hwnd), + uintptr(hWndInsertAfter), + uintptr(x), + uintptr(y), + uintptr(cx), + uintptr(cy), + uintptr(uFlags)) + + return ret != 0 +} + +func FillRect(hDC HDC, lprc *RECT, hbr HBRUSH) bool { + ret, _, _ := procFillRect.Call( + uintptr(hDC), + uintptr(unsafe.Pointer(lprc)), + uintptr(hbr)) + + return ret != 0 +} + +func DrawText(hDC HDC, text []uint16, uCount int, lpRect *RECT, uFormat uint32) int { + + // Convert the string to a UTF16 pointer + // This is necessary because the DrawText function expects a UTF16 pointer + + ret, _, _ := procDrawText.Call( + uintptr(hDC), + uintptr(unsafe.Pointer(&text[0])), + uintptr(uCount), + uintptr(unsafe.Pointer(lpRect)), + uintptr(uFormat)) + + return int(ret) +} + +func AddClipboardFormatListener(hwnd HWND) bool { + ret, _, _ := procAddClipboardFormatListener.Call( + uintptr(hwnd)) + return ret != 0 +} + +func RemoveClipboardFormatListener(hwnd HWND) bool { + ret, _, _ := procRemoveClipboardFormatListener.Call( + uintptr(hwnd)) + return ret != 0 +} + +func OpenClipboard(hWndNewOwner HWND) bool { + ret, _, _ := procOpenClipboard.Call( + uintptr(hWndNewOwner)) + return ret != 0 +} + +func CloseClipboard() bool { + ret, _, _ := procCloseClipboard.Call() + return ret != 0 +} + +func EnumClipboardFormats(format uint) uint { + ret, _, _ := procEnumClipboardFormats.Call( + uintptr(format)) + return uint(ret) +} + +func GetClipboardData(uFormat uint) HANDLE { + ret, _, _ := procGetClipboardData.Call( + uintptr(uFormat)) + return HANDLE(ret) +} + +func SetClipboardData(uFormat uint, hMem HANDLE) HANDLE { + ret, _, _ := procSetClipboardData.Call( + uintptr(uFormat), + uintptr(hMem)) + return HANDLE(ret) +} + +func EmptyClipboard() bool { + ret, _, _ := procEmptyClipboard.Call() + return ret != 0 +} + +func GetClipboardFormatName(format uint) (string, bool) { + cchMaxCount := 255 + buf := make([]uint16, cchMaxCount) + ret, _, _ := procGetClipboardFormatName.Call( + uintptr(format), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(cchMaxCount)) + + if ret > 0 { + return syscall.UTF16ToString(buf), true + } + + return "Requested format does not exist or is predefined", false +} + +func IsClipboardFormatAvailable(format uint) bool { + ret, _, _ := procIsClipboardFormatAvailable.Call(uintptr(format)) + return ret != 0 +} + +func BeginPaint(hwnd HWND, paint *PAINTSTRUCT) HDC { + ret, _, _ := procBeginPaint.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(paint))) + return HDC(ret) +} + +func EndPaint(hwnd HWND, paint *PAINTSTRUCT) { + procEndPaint.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(paint))) +} + +func GetKeyboardState(keyState []byte) bool { + if len(keyState) != 256 { + panic("keyState slice must have a size of 256 bytes") + } + ret, _, _ := procGetKeyboardState.Call(uintptr(unsafe.Pointer(&keyState[0]))) + return ret != 0 +} + +func MapVirtualKeyEx(uCode, uMapType uint, dwhkl HKL) uint { + ret, _, _ := procMapVirtualKeyEx.Call( + uintptr(uCode), + uintptr(uMapType), + uintptr(dwhkl)) + return uint(ret) +} + +func MapVirtualKey(uCode uint, uMapType uint) uint { + ret, _, _ := procMapVirtualKey.Call(uintptr(uCode), uintptr(uMapType)) + return uint(ret) +} + +func GetAsyncKeyState(vKey int) uint16 { + ret, _, _ := procGetAsyncKeyState.Call(uintptr(vKey)) + return uint16(ret) +} + +func ToAscii(uVirtKey, uScanCode uint, lpKeyState *byte, lpChar *uint16, uFlags uint) int { + ret, _, _ := procToAscii.Call( + uintptr(uVirtKey), + uintptr(uScanCode), + uintptr(unsafe.Pointer(lpKeyState)), + uintptr(unsafe.Pointer(lpChar)), + uintptr(uFlags)) + return int(ret) +} + +func SwapMouseButton(fSwap bool) bool { + ret, _, _ := procSwapMouseButton.Call( + uintptr(BoolToBOOL(fSwap))) + return ret != 0 +} + +func GetCursorPos() (x, y int, ok bool) { + pt := POINT{} + ret, _, _ := procGetCursorPos.Call(uintptr(unsafe.Pointer(&pt))) + return int(pt.X), int(pt.Y), ret != 0 +} + +func SetCursorPos(x, y int) bool { + ret, _, _ := procSetCursorPos.Call( + uintptr(x), + uintptr(y), + ) + return ret != 0 +} + +func SetCursor(cursor HCURSOR) HCURSOR { + ret, _, _ := procSetCursor.Call( + uintptr(cursor), + ) + return HCURSOR(ret) +} + +func CreateIcon(instance HINSTANCE, nWidth, nHeight int, cPlanes, cBitsPerPixel byte, ANDbits, XORbits *byte) HICON { + ret, _, _ := procCreateIcon.Call( + uintptr(instance), + uintptr(nWidth), + uintptr(nHeight), + uintptr(cPlanes), + uintptr(cBitsPerPixel), + uintptr(unsafe.Pointer(ANDbits)), + uintptr(unsafe.Pointer(XORbits)), + ) + return HICON(ret) +} + +func DestroyIcon(icon HICON) bool { + ret, _, _ := procDestroyIcon.Call( + uintptr(icon), + ) + return ret != 0 +} + +func MonitorFromPoint(x, y int, dwFlags uint32) HMONITOR { + ret, _, _ := procMonitorFromPoint.Call( + uintptr(x), + uintptr(y), + uintptr(dwFlags), + ) + return HMONITOR(ret) +} + +func MonitorFromRect(rc *RECT, dwFlags uint32) HMONITOR { + ret, _, _ := procMonitorFromRect.Call( + uintptr(unsafe.Pointer(rc)), + uintptr(dwFlags), + ) + return HMONITOR(ret) +} + +func MonitorFromWindow(hwnd HWND, dwFlags uint32) HMONITOR { + ret, _, _ := procMonitorFromWindow.Call( + uintptr(hwnd), + uintptr(dwFlags), + ) + return HMONITOR(ret) +} + +func GetMonitorInfo(hMonitor HMONITOR, lmpi *MONITORINFO) bool { + ret, _, _ := procGetMonitorInfo.Call( + uintptr(hMonitor), + uintptr(unsafe.Pointer(lmpi)), + ) + return ret != 0 +} + +func GetMonitorInfoEx(hMonitor HMONITOR, lmpi *MONITORINFOEX) bool { + ret, _, _ := procGetMonitorInfo.Call( + uintptr(hMonitor), + uintptr(unsafe.Pointer(lmpi)), + ) + return ret != 0 +} + +func EnumDisplayMonitors(hdc HDC, clip *RECT, fnEnum uintptr, dwData unsafe.Pointer) bool { + ret, _, _ := procEnumDisplayMonitors.Call( + hdc, + uintptr(unsafe.Pointer(clip)), + fnEnum, + uintptr(dwData), + ) + return ret != 0 +} + +func EnumDisplaySettingsEx(szDeviceName *uint16, iModeNum uint32, devMode *DEVMODE, dwFlags uint32) bool { + ret, _, _ := procEnumDisplaySettingsEx.Call( + uintptr(unsafe.Pointer(szDeviceName)), + uintptr(iModeNum), + uintptr(unsafe.Pointer(devMode)), + uintptr(dwFlags), + ) + return ret != 0 +} + +func ChangeDisplaySettingsEx(szDeviceName *uint16, devMode *DEVMODE, hwnd HWND, dwFlags uint32, lParam uintptr) int32 { + ret, _, _ := procChangeDisplaySettingsEx.Call( + uintptr(unsafe.Pointer(szDeviceName)), + uintptr(unsafe.Pointer(devMode)), + uintptr(hwnd), + uintptr(dwFlags), + lParam, + ) + return int32(ret) +} + +/* +func SendInput(inputs []INPUT) uint32 { + var validInputs []C.INPUT + + for _, oneInput := range inputs { + input := C.INPUT{_type: C.DWORD(oneInput.Type)} + + switch oneInput.Type { + case INPUT_MOUSE: + (*MouseInput)(unsafe.Pointer(&input)).mi = oneInput.Mi + case INPUT_KEYBOARD: + (*KbdInput)(unsafe.Pointer(&input)).ki = oneInput.Ki + case INPUT_HARDWARE: + (*HardwareInput)(unsafe.Pointer(&input)).hi = oneInput.Hi + default: + panic("unkown type") + } + + validInputs = append(validInputs, input) + } + + ret, _, _ := procSendInput.Call( + uintptr(len(validInputs)), + uintptr(unsafe.Pointer(&validInputs[0])), + uintptr(unsafe.Sizeof(C.INPUT{})), + ) + return uint32(ret) +}*/ + +func SetWindowsHookEx(idHook int, lpfn HOOKPROC, hMod HINSTANCE, dwThreadId DWORD) HHOOK { + ret, _, _ := procSetWindowsHookEx.Call( + uintptr(idHook), + uintptr(syscall.NewCallback(lpfn)), + uintptr(hMod), + uintptr(dwThreadId), + ) + return HHOOK(ret) +} + +func UnhookWindowsHookEx(hhk HHOOK) bool { + ret, _, _ := procUnhookWindowsHookEx.Call( + uintptr(hhk), + ) + return ret != 0 +} + +func CallNextHookEx(hhk HHOOK, nCode int, wParam WPARAM, lParam LPARAM) LRESULT { + ret, _, _ := procCallNextHookEx.Call( + uintptr(hhk), + uintptr(nCode), + uintptr(wParam), + uintptr(lParam), + ) + return LRESULT(ret) +} + +func GetKeyState(nVirtKey int32) int16 { + ret, _, _ := procGetKeyState.Call( + uintptr(nVirtKey), + 0, + 0) + + return int16(ret) +} + +func DestroyMenu(hMenu HMENU) bool { + ret, _, _ := procDestroyMenu.Call(1, + uintptr(hMenu), + 0, + 0) + + return ret != 0 +} + +func GetWindowPlacement(hWnd HWND, lpwndpl *WINDOWPLACEMENT) bool { + ret, _, _ := procGetWindowPlacement.Call( + uintptr(hWnd), + uintptr(unsafe.Pointer(lpwndpl)), + 0) + + return ret != 0 +} + +func SetWindowPlacement(hWnd HWND, lpwndpl *WINDOWPLACEMENT) bool { + ret, _, _ := procSetWindowPlacement.Call( + uintptr(hWnd), + uintptr(unsafe.Pointer(lpwndpl)), + 0) + + return ret != 0 +} + +func SetScrollInfo(hwnd HWND, fnBar int32, lpsi *SCROLLINFO, fRedraw bool) int32 { + ret, _, _ := procSetScrollInfo.Call( + hwnd, + uintptr(fnBar), + uintptr(unsafe.Pointer(lpsi)), + uintptr(BoolToBOOL(fRedraw)), + 0, + 0) + + return int32(ret) +} + +func GetScrollInfo(hwnd HWND, fnBar int32, lpsi *SCROLLINFO) bool { + ret, _, _ := procGetScrollInfo.Call( + hwnd, + uintptr(fnBar), + uintptr(unsafe.Pointer(lpsi))) + + return ret != 0 +} + +func RedrawWindow(hwnd HWND, lprcUpdate *RECT, hrgnUpdate HRGN, flags uint32) bool { + ret, _, _ := procRedrawWindow.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(lprcUpdate)), + uintptr(hrgnUpdate), + uintptr(flags)) + return ret != 0 +} + +func FrameRect(hDC HDC, lprc *RECT, hbr HBRUSH) int { + ret, _, _ := procFrameRect.Call( + uintptr(hDC), + uintptr(unsafe.Pointer(lprc)), + uintptr(hbr)) + return int(ret) +} + +func GetMenuBarInfo(hwnd HWND, idObject int32, idItem uint32, pmbi *MENUBARINFO) bool { + ret, _, _ := procGetMenuBarInfo.Call( + uintptr(hwnd), + uintptr(idObject), + uintptr(idItem), + uintptr(unsafe.Pointer(pmbi))) + return ret != 0 +} + +func MapWindowPoints(hwndFrom, hwndTo HWND, lpPoints *POINT, cPoints uint) int { + ret, _, _ := procMapWindowPoints.Call( + uintptr(hwndFrom), + uintptr(hwndTo), + uintptr(unsafe.Pointer(lpPoints)), + uintptr(cPoints)) + return int(ret) +} + +func TrackPopupMenu(hmenu HMENU, flags uint32, x, y int32, reserved int32, hwnd HWND, prcRect *RECT) bool { + ret, _, _ := procTrackPopupMenu.Call( + uintptr(hmenu), + uintptr(flags), + uintptr(x), + uintptr(y), + uintptr(reserved), + uintptr(hwnd), + uintptr(unsafe.Pointer(prcRect))) + return ret != 0 +} + +// KeybdEvent synthesizes a keystroke. The system can use such a synthesized keystroke to generate a WM_KEYUP or WM_KEYDOWN message. +// bVk: Virtual-key code +// bScan: Hardware scan code +// dwFlags: Controls various aspects of function operation (KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP) +// dwExtraInfo: Additional value associated with the keystroke +func KeybdEvent(bVk byte, bScan byte, dwFlags uint32, dwExtraInfo uintptr) { + procKeybdEvent.Call( + uintptr(bVk), + uintptr(bScan), + uintptr(dwFlags), + dwExtraInfo, + ) +} diff --git a/v3/pkg/w32/utils.go b/v3/pkg/w32/utils.go new file mode 100644 index 000000000..f4e99dbaf --- /dev/null +++ b/v3/pkg/w32/utils.go @@ -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) + } +} diff --git a/v3/pkg/w32/vars.go b/v3/pkg/w32/vars.go new file mode 100644 index 000000000..cb69f9d19 --- /dev/null +++ b/v3/pkg/w32/vars.go @@ -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}} +) diff --git a/v3/pkg/w32/wda.go b/v3/pkg/w32/wda.go new file mode 100644 index 000000000..0faf91a82 --- /dev/null +++ b/v3/pkg/w32/wda.go @@ -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 +} diff --git a/v3/pkg/w32/window.go b/v3/pkg/w32/window.go new file mode 100644 index 000000000..c26ec414d --- /dev/null +++ b/v3/pkg/w32/window.go @@ -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 +} \ No newline at end of file