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
runfunction 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.
BitBltmoves 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_RGBDIB section is0xAARRGGBBin a little-endianDWORD, 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§
Structs§
- Denise
Control - A Denise panel in a child window.
- DibSurface
- A pixel buffer a window blits from.
Enums§
- Error
- Failures from this backend.
Traits§
- Control
Delegate - What the application implements to put something in the control.