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 ¶
- func AnyButtonDown(wparam uintptr) bool
- func CenterOffset(avail, winExtent int) int
- func ClientCoords(px, py int, scale float64) (int, int)
- func DecodeMouseMods(wparam uintptr) (shift, ctrl bool)
- func DecodeVK(vk uint32) string
- func DefaultContentSize(workW, workH float64) (w, h int)
- func InvalidRect(r toolkit.Rect, scale float64) (x, y, w, h int)
- func LogicalFromPhysical(phys int, scale float64) int
- func MapCharDown(r rune, shift, ctrl bool) []toolkit.Event
- func MapCharUp(r rune, shift, ctrl bool) []toolkit.Event
- func MapKeyDown(vk uint32, shift, ctrl bool) []toolkit.Event
- func MapKeyUp(vk uint32, shift, ctrl bool) []toolkit.Event
- func MapMouseDown(x, y int, shift, ctrl bool) toolkit.Event
- func MapMouseMove(x, y int, buttonHeld, shift, ctrl bool) toolkit.Event
- func MapMouseUp(x, y int, shift, ctrl bool) toolkit.Event
- func MapWheel(x, y, delta int, shift, ctrl bool) toolkit.Event
- func PackBGRA(dst, src []byte)
- func PackBGRARect(dst, src []byte, width, height, x, y, w0, h0 int)
- func PhysicalFromLogical(logical int, scale float64) int
- func ScaleForDpi(dpi uint32) float64
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AnyButtonDown ¶
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 ¶
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 ¶
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 DecodeMouseMods ¶
DecodeMouseMods splits a Win32 mouse-message wParam (its low word carries the MK_* key-state bits) into the toolkit's Shift/Ctrl booleans, so a Ctrl-click or Shift-click reaches a widget with the same flags an X11/Cocoa chord would.
func DecodeVK ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
MapMouseUp turns a WM_LBUTTONUP/WM_RBUTTONUP into an EventMouseUp.
func MapWheel ¶
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 ¶
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 PhysicalFromLogical ¶
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 ScaleForDpi ¶
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.
Types ¶
This section is empty.