win32

package
v0.25.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 12, 2026 License: BSD-3-Clause Imports: 4 Imported by: 0

Documentation

Overview

Package win32 is the pure-Go (CGO-free) Windows Win32/GDI windowing backend for the go-widgets toolkit. It registers a window class and creates a real top-level HWND, blits the toolkit's RGBA framebuffer into it through a top-down 32bpp DIB with StretchDIBits, and routes native WM_* mouse/wheel/key input into toolkit.Event, so a go-widgets widget tree runs on a Windows desktop exactly as it does on X11, Wayland, macOS or in the browser/wasm host.

The Win32 API is reached through the process' own kernel32/user32/gdi32 DLLs via syscall.NewLazyDLL + syscall.SyscallN and a syscall.NewCallback WNDPROC — no cgo — so the whole module builds and links with CGO_ENABLED=0.

This file is the SOVEREIGN, OS-INDEPENDENT half: the WM_*→toolkit.Event mapping (virtual-key decode, modifier decode, button/wheel mapping), the RGBA→BGRA DIB packing, the Per-Monitor-V2 DPI/size maths and the damage-rect→InvalidateRect conversion, all expressed over plain Go values with a single toolkit dependency (the event model). It carries NO syscall/unsafe/windows dependency, so it builds — and is unit-tested to 100% — on every GOOS, mirroring internal/cocoa's mapping.go and internal/wasmbox's protocol.go. The thin Win32 glue that actually creates the HWND, presents the DIB and pumps the message loop lives in win32_windows.go (//go:build windows) and drives everything here.

Index

Constants

View Source
const (
	CtButton   = 50000
	CtEdit     = 50004
	CtImage    = 50006
	CtListItem = 50007
	CtList     = 50008
	CtMenu     = 50009
	CtMenuBar  = 50010
	CtProgress = 50012
	CtSlider   = 50015
	CtSpinner  = 50016
	CtTab      = 50018
	CtText     = 50020
	CtToolBar  = 50021
	CtTree     = 50023
	CtGroup    = 50026
	CtDataGrid = 50028
	CtDocument = 50030
	CtWindow   = 50032
	CtPane     = 50033
	CtCheckBox = 50002
	CtComboBox = 50003
	CtRadio    = 50013
)

UI Automation control type identifiers. A wrong number renames every element for the user, so these are the documented values rather than a guess.

Variables

This section is empty.

Functions

func A11yNodes added in v0.8.0

func A11yNodes(root toolkit.Widget) []toolkit.A11yNode

A11yNodes is the tree to publish for a widget root: every meaningful element, in visual order, with the ones no reader could use already removed.

func A11ySkip added in v0.8.0

func A11ySkip(n toolkit.A11yNode) bool

A11ySkip reports whether a node should be left out of the published tree. An element with no name says nothing a screen reader could announce, and one with no area cannot be pointed at; either is a stop the user has to skip past for nothing.

func AnyButtonDown

func AnyButtonDown(wparam uintptr) bool

AnyButtonDown reports whether any mouse button is held per a WM_MOUSEMOVE wParam, which is how the backend distinguishes a drag from a plain hover move (the X11/Wayland backends read the same button mask; Cocoa gets it from the distinct -mouseDragged: selector).

func CenterOffset

func CenterOffset(avail, winExtent int) int

CenterOffset returns the top-left offset at which a window of outer size winExtent is centred within an axis of usable extent avail: (avail-winExtent)/2, clamped to 0 so a window larger than the work area is pinned to the origin rather than pushed off-screen. The caller adds the work-area origin (left/top) to place the window absolutely.

func ClientCoords

func ClientCoords(px, py int, scale float64) (int, int)

ClientCoords converts a mouse position in physical client pixels (top-left origin, the space every WM_MOUSE* message reports) into framebuffer pixels — the LOGICAL-point coordinate space the toolkit framebuffer and every toolkit.Event uses. At the default render scale of 1 the two spaces coincide; at 150% a click at physical (300,150) maps to framebuffer (200,100), so a widget is hit where the user sees it. The result is clamped to be non-negative.

func DecodeVK

func DecodeVK(vk uint32) string

DecodeVK maps a Win32 virtual-key code to the toolkit's symbolic key NAME (DOM-style: "Enter", "ArrowLeft", …, exactly the names the toolkit widgets match and the X11/Cocoa/wasmbox backends emit). It returns "" for every key that is not one of the named editing/navigation keys — those carry a printable rune instead, delivered via the WM_CHAR path (MapCharDown).

func DefaultContentSize

func DefaultContentSize(workW, workH float64) (w, h int)

DefaultContentSize picks a readable default window content size, in LOGICAL points, from the monitor work area (workW×workH, also in logical points). It takes defaultScreenFraction of the work area and clamps each axis to the [min,max] readability band, then to the work extent so the window never exceeds the usable screen. When the work area is unknown (workW or workH ≤ 0) it returns the fixed fallback. The result is always ≥ 1×1 and ≤ the work area, so a defaulted window is legible without manual sizing. It mirrors the Cocoa backend's DefaultContentSize exactly.

func InvalidRect

func InvalidRect(r toolkit.Rect, scale float64) (x, y, w, h int)

InvalidRect converts a damage rectangle in framebuffer (LOGICAL) pixels — the space RenderDamaged reports and the framebuffer uses — into a rectangle in physical client pixels, ready for InvalidateRect / the WM_PAINT update region. Because the framebuffer is logical and the client area is physical, the rect is multiplied by the render scale (the inverse of the Cocoa DirtyRect, which divides a device-pixel rect back to points). The rectangle is clamped to be non-negative and expanded to whole pixels (floor origin, ceil far edge) so a sub-pixel damage rect never leaves a seam on screen.

func LogicalFromPhysical

func LogicalFromPhysical(phys int, scale float64) int

LogicalFromPhysical converts a physical client extent (WM_SIZE reports the client area in device pixels) back to the logical-point size the framebuffer is rendered at, rounded to the nearest whole point and never below 1. It is the inverse of PhysicalFromLogical: at 150% a 1200-pixel client becomes an 800-point framebuffer, keeping the UI readable as the window resizes.

func MapCharDown

func MapCharDown(r rune, m Mods) []toolkit.Event

MapCharDown turns a committed printable rune (from WM_CHAR on key press) into the EventKeyDown+EventChar pair the toolkit expects for text input — the same press/char split the X11 and Cocoa backends perform. A non-printable code (a control code such as the ^M/^I/^[ that WM_CHAR also delivers for Enter/Tab/Escape, or DEL) yields nothing, because those keys are already delivered as named keys through MapKeyDown.

func MapCharUp

func MapCharUp(r rune, m Mods) []toolkit.Event

MapCharUp turns a printable rune (the glue translates the WM_KEYUP virtual key to its character) into the single EventKeyUp the toolkit expects on release. A non-printable rune yields nothing.

func MapKeyDown

func MapKeyDown(vk uint32, m Mods) []toolkit.Event

MapKeyDown turns a WM_KEYDOWN virtual key into the toolkit event(s) it produces. A NAMED key (Enter, ArrowLeft, …) yields a single EventKeyDown carrying the name in Code. A key that is not named yields nothing here: on Windows the printable character is not known until the OS translates the keystroke into the following WM_CHAR, so MapCharDown emits the KeyDown+Char pair for printables — keeping the toolkit's press/char split identical to the X11 and Cocoa backends.

func MapKeyUp

func MapKeyUp(vk uint32, m Mods) []toolkit.Event

MapKeyUp turns a WM_KEYUP virtual key into the toolkit event(s) it produces. A NAMED key yields a single EventKeyUp carrying the name; a non-named (printable) key's release is delivered via MapCharUp (the glue translates the virtual key to its rune), so a printable key produces EventKeyUp{rune} exactly as the X11 backend does.

func MapMouseDown

func MapMouseDown(x, y int, m Mods) toolkit.Event

MapMouseDown turns a WM_LBUTTONDOWN/WM_RBUTTONDOWN at the given framebuffer pixel into an EventClick, mirroring the X11 ButtonPress (buttons 1–3 → click) and Cocoa mapping. Windows delivers a distinct message per button; the backend routes all of them here, button-agnostically, exactly as the toolkit's click model expects.

func MapMouseMove

func MapMouseMove(x, y int, buttonHeld bool, m Mods) toolkit.Event

MapMouseMove turns a WM_MOUSEMOVE into a drag (a button held) or a plain hover move (no button), per buttonHeld — the same drag-vs-move split the X11/Wayland backends derive from the event's button-state mask.

func MapMouseUp

func MapMouseUp(x, y int, m Mods) toolkit.Event

MapMouseUp turns a WM_LBUTTONUP/WM_RBUTTONUP into an EventMouseUp.

func MapWheel

func MapWheel(x, y, delta int, m Mods) toolkit.Event

MapWheel turns a WM_MOUSEWHEEL delta into an EventScroll whose Delta is normalised to the toolkit's ±1 row step. Windows reports a POSITIVE delta when the wheel is rotated forward/away from the user (a scroll-up gesture); the toolkit's Delta is POSITIVE to scroll down/forward, so the sign is inverted — matching the browser/wheel convention the X11 and wasmbox backends use. A zero delta yields a Delta-0 EventScroll (harmless; scrollable widgets clamp it), so the mapping is total.

func PackBGRA

func PackBGRA(dst, src []byte)

PackBGRA converts the whole RGBA framebuffer src (the toolkit's byte order: R,G,B,A) into the BGRA byte order a Win32 top-down 32bpp BI_RGB DIB requires (B,G,R,A), writing into dst. It swaps the R and B channels of every pixel and preserves G and A. dst and src must be the same length and a multiple of 4; any tail shorter than a whole pixel is left untouched. This is the pure core of the StretchDIBits present path.

func PackBGRARect

func PackBGRARect(dst, src []byte, width, height, x, y, w0, h0 int)

PackBGRARect converts only the w0×h0 sub-rectangle at (x,y) of the RGBA framebuffer src into the BGRA DIB dst, both laid out as width×height 32bpp rows (stride = 4*width bytes). It is the damage-region packer: after an incremental repaint only the changed rectangle is re-packed before the damaged sub-blit, instead of re-packing the whole surface. The rectangle is clamped to the surface, so an out-of-bounds rect packs only its visible part (and an empty intersection packs nothing).

func ParsePressPoint added in v0.8.0

func ParsePressPoint(s string) (x, y int, ok bool)

ParsePressPoint reads back what PressPoint wrote. A malformed value REFUSES rather than defaulting to (0,0), which is a real and usually clickable place.

func PhysicalFromLogical

func PhysicalFromLogical(logical int, scale float64) int

PhysicalFromLogical converts a logical-point extent to physical device pixels at the given render scale, rounded to the nearest whole pixel and never below 1 (a visible window always has a positive pixel size). It sizes the window's physical client area from the toolkit's logical layout: at 150% a 800-point content width becomes a 1200-pixel client area, so the OS shows the logical UI at a readable physical size.

func PressPoint added in v0.8.0

func PressPoint(n toolkit.A11yNode) string

PressPoint encodes an element's centre in the LOGICAL points the input path speaks, as the string carried on its AutomationId.

Carrying the point ON the element beats a Go-side table keyed by index: the tree is rebuilt whenever the frame changes, and an index would go stale the moment content scrolled under a screen-reader user's cursor — invoking would then activate whatever moved into that slot.

func ScaleForDpi

func ScaleForDpi(dpi uint32) float64

ScaleForDpi converts a Win32 DPI value (as returned by GetDpiForWindow / WM_DPICHANGED) into the framebuffer render scale: physical device pixels per logical point. 96 DPI (100%) → 1.0, 144 (150%) → 1.5, 192 (200%) → 2.0. A zero or negative DPI (an unavailable monitor) defaults to 1.0 so the maths never divides by zero downstream.

func ScreenRect added in v0.8.0

func ScreenRect(n toolkit.A11yNode, scale float64, originX, originY int) (x, y, w, h float64)

ScreenRect converts a node's rectangle from the LOGICAL points the toolkit lays out in to the PHYSICAL screen pixels UI Automation reports, given the window's DPI scale and the screen position of its client area.

Both halves matter and both are invisible when wrong: forgetting the scale puts every element at a fraction of its true size on a high-DPI display, and forgetting the origin reports each one relative to the window while the client places it on the desktop — so a screen reader's focus ring lands in the top-left corner of the screen instead of on the control.

func UIAControlType added in v0.8.0

func UIAControlType(r toolkit.Role) int32

UIAControlType maps a toolkit role to its UI Automation control type. Anything unrecognised becomes a group — UIA's "a thing containing things", the honest answer for a role this table does not know, and never a wrong announcement.

Types

type Mods added in v0.12.0

type Mods struct{ Shift, Ctrl, Alt, Meta bool }

Mods is the decoded modifier state carried on every toolkit event the Win32 backend emits: Shift, Ctrl, Alt and Meta (the ⊞ Windows/logo key).

func DecodeMouseMods

func DecodeMouseMods(wparam uintptr) Mods

DecodeMouseMods splits a Win32 mouse-message wParam (its low word carries the MK_* key-state bits) into the toolkit modifiers, so a Ctrl-click or Shift-click reaches a widget with the same flags an X11/Cocoa chord would. The mouse wParam carries no Alt or Windows-key bit, so Alt/Meta are left false for pointer events (they are read from the keyboard state for key events).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL