C+
Packages · View as Markdown

win32

Native Win32 GUI bindings for Windows — the counterpart to appkit on macOS, organized the same way. Six modules cover the surface: core (the engine), window, controls, menu, dialogs, and graphics, with an umbrella win32 facade re-exporting the flat type surface (win32::Window, win32::Button, …).

Unlike a toolkit binding, this needs nothing installed: user32, gdi32, comctl32, and comdlg32 ship with every Windows install and sit on the linker's default search path, so a consuming app builds and runs out of the box on the x86_64-pc-windows-msvc target — no MSYS2, no pkg-config, no toolkit to fetch. The bindings are direct extern fn calls into those DLLs — thin C+ structs over opaque HWND/HMENU/HDC handles. The ANSI (*A) entry points are used, so a plain NUL-terminated C+ string built with #str_ptr("...\0") is a valid LPCSTR with no UTF-16 conversion.

Events: the dispatch table

Win32 has no per-control signal objects — the parent window procedure receives everything. This binding hides that. Each control is created with a unique command id, and control.on_click(handler, user) registers handler in the parent window's dispatch table under that id (the core module heap-allocates the table and attaches it to the window with SetPropA). When the control fires a WM_COMMAND, the single routing window-procedure looks the id up and calls your function.

Handlers are plain closure-free C+ functions of shape fn(sender_hwnd, user) — C+ has no closures, so app state is threaded through the user: *u8 pointer (or a global). Paint handlers are fn(window_hwnd, hdc); wrap the device context with graphics::Painter::from_hdc(hdc) to draw.

import "win32/win32" as win32;
import "win32/controls" as controls;

fn on_click(sender: *u8, _user: *u8) {
    // handle the click; reach app state through `user` or a global.
    return;
}

fn main() -> i32 {
    let win: win32::Window = win32::Window::new(#str_ptr("C+ on Win32\0"),
                                                420 as i32, 300 as i32);
    let _lbl: controls::Label = controls::Label::new(win.raw(),
        #str_ptr("Hello from C+ via native Win32\0"),
        20 as i32, 16 as i32, 380 as i32, 22 as i32);
    let btn: controls::Button = controls::Button::new(win.raw(),
        #str_ptr("Press me\0"), 20 as i32, 48 as i32, 120 as i32, 30 as i32);
    btn.on_click(on_click, 0 as *u8);
    win.show();
    return win32::run();        // pump messages until the window is closed
}

win32::run() is the application message loop (the app.run() analogue) and returns the WM_QUIT code.

Controls

controls covers ten classes: Button, Label, Edit (plus new_multiline), CheckBox, RadioButton, ComboBox, ListBox, ProgressBar, TrackBar (a slider), and GroupBox. Each constructor takes the parent HWND and an x, y, w, h rectangle. Interactive controls expose an on_* registrar (on_click / on_change / on_toggle / on_select) alongside getters and setters: set_text, is_checked / set_checked, add_item / selected_index, set_range / set_value, and so on.

TrackBar is the exception — its notifications arrive as WM_HSCROLL rather than WM_COMMAND, so it exposes value get/set plus range instead of a command callback.

Menus and dialogs

menu builds a menu bar with Menu::new() (or a submenu with Menu::new_popup()), then add_item, add_submenu, and add_separator. Menu items route exactly like controls, so add_item takes the owning window's HWND to register its handler. Attach the bar with win.set_menu(bar.raw()).

dialogs provides message_box / message_box_ex (the alert-dialog analogue over MessageBoxA, with mb_* button-set / icon constants and id_* results) and open_file / save_file (comdlg32 file dialogs that fill a caller-provided path buffer and return whether the user confirmed).

Painting

A graphics::Painter wraps the HDC handed to a window's on_paint handler and issues GDI calls — text, rectangle, ellipse, line, fill_rect, use_pen, and text color. A Color is a COLORREF value built with Color::rgb(r, g, b).

import "win32/win32" as win32;
import "win32/graphics" as gfx;

fn on_paint(_hwnd: *u8, hdc: *u8) {
    let p: gfx::Painter = gfx::Painter::from_hdc(hdc);
    p.fill_rect(10 as i32, 10 as i32, 200 as i32, 80 as i32,
                gfx::Color::rgb(30 as u8, 120 as u8, 220 as u8));
    p.set_transparent_text();
    p.set_text_color(gfx::Color::rgb(255 as u8, 255 as u8, 255 as u8));
    p.text(24 as i32, 36 as i32, #str_ptr("painted with GDI\0"));
    return;
}

Register it with win.on_paint(on_paint, 0 as *u8), and force a repaint with win.redraw().

Ownership

HWND/HMENU/HDC are OS-owned opaque handles, so the wrapper structs hold them opaque with no drop — a window is torn down by closing it (WM_CLOSEDestroyWindow) or at process exit, and child controls are owned by their parent. The per-window dispatch table is heap-allocated and lives for the window's lifetime. This is the same conservative default as appkit's child widgets.

Platform notes

This package is Windows-only and links user32, gdi32, comctl32, and comdlg32 (declared in its Cplus.toml), all of which are present on a stock install. Build with cpc build on x86_64-pc-windows-msvc; see targets for cross-target builds. For the underlying extern fn call mechanics, see FFI; to drive a GUI app from an external agent, see the agent surface. The closure-free handler convention here is the same one builder blocks lean on elsewhere.