Skip to main content

Crate denise_win32

Crate denise_win32 

Source
Expand description

A Denise panel in a Win32 child window.

The oldest of the reasons this project exists. CoreCanvas shipped inside Windows applications that were not going to be rewritten — MFC, WinForms, VB6 through the ActiveX shim — and the thing they all need is a control they can put in a dialog next to the ones they already have.

So: DeniseControl registers a window class and creates a child HWND. The host owns the window, the message loop and the parent; Denise owns the pixels inside one rectangle and nothing else.

§What is different from the bare-metal backends

  • The host owns the message loop. There is no run function here. Windows decides when to paint and Denise answers, which is the opposite of the DRM backend where Denise decides and the display follows.
  • Damage is real bandwidth. BitBlt moves only the rectangles it is given, unlike a DRM page flip where the whole buffer goes regardless. The tree’s damage is worth passing on rather than rounding up to the client area.
  • The pixel format already matches. A 32-bit BI_RGB DIB section is 0xAARRGGBB in a little-endian DWORD, which is exactly what the rasteriser writes. No conversion pass anywhere.
  • There is already a cursor. Windows draws one, so the composited sprite stays off: Ui::show_cursor(false), which since M5 is a decision that sticks rather than one the next mouse move overrides.

§No console window

A Rust binary defaults to the console subsystem, so Windows allocates a console and it sits behind the app looking like a mistake. The .exe that hosts the control — not this library — decides that, with a crate-level attribute:

ⓘ
#![windows_subsystem = "windows"]

The cost is that stdout and stderr go nowhere, including panic messages and anything printed before a window exists. So the usual form keeps the console while developing and drops it for the build that ships:

ⓘ
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

It is ignored on non-Windows targets, so it needs no cfg(windows) of its own. A host that wants both — no console, and somewhere for diagnostics to go — should log to a file or OutputDebugString rather than to a stream nothing is reading.

§Testing

keymap is platform-independent and its tests run everywhere. That is not tidiness: it is a table of a hundred numbers, it is the part that breaks, and discovering that on a CI runner rather than locally is a slow way to discover it — which is exactly what happened the first time. Only the window and the DIB section are gated to Windows, the same split denise-drm and denise-evdev make.

§Status

Run on real hardware — Windows 11 ARM64, where Tab reaches the control and AltGr and the dead keys compose — and built and tested by CI on every push. Still unverified: focus behaviour inside a real dialog, and DPI changes. For the latter, the toolkit’s answer exists and is documented in docs/design.md: the host rebuilds with theme.scaled(factor) (or denise_ui_new_scaled over the C ABI) when WM_DPICHANGED arrives — what is missing is a host that has actually done it.

Re-exports§

pub use keymap::key_code;

Modules§

keymap
Win32 virtual key codes to KeyCode.

Structs§

DeniseControl
A Denise panel in a child window.
DibSurface
A pixel buffer a window blits from.

Enums§

Error
Failures from this backend.

Traits§

ControlDelegate
What the application implements to put something in the control.