# denise-macos
[](https://crates.io/crates/denise-macos)
[](https://docs.rs/denise-macos)
[](https://github.com/bisand/denise/blob/main/LICENSE)
An embeddable Cocoa view backend for **[Denise]**, a direct-rendering UI toolkit in
Rust for embedded Linux and systems without a desktop environment.
**Not a way to ship Denise on a Mac.**
[`denise-winit`](https://crates.io/crates/denise-winit) already previews on one, and
a desktop application should use a desktop toolkit. This exists for the same reason
the Win32 control does: an existing Cocoa application that wants a Denise panel
*inside* it, next to its own views, with the host owning the window and the run
loop.
```rust
# #[cfg(target_os = "macos")]
# fn demo() -> Result<(), denise_macos::Error> {
use denise::Size;
use denise_macos::ViewSurface;
// The host has a view; Denise has a surface the size of its backing store.
let mut surface = ViewSurface::new(Size::new(800, 480), 2.0)?;
# let _ = &mut surface;
# Ok(())
# }
```
`DeniseView` is the `NSView` subclass, `ViewDelegate` is what the host implements to
drive it, and `ViewSurface` is the `denise::Surface` behind a layer-backed context.
## What is different from the bare-metal backends
- **The host owns the run loop.** There is no `run` function here. AppKit decides
when to draw and Denise answers — the opposite of the DRM backend, where Denise
decides and the display follows.
- **Damage is real.** `setNeedsDisplayInRect:` genuinely limits what gets
composited, unlike a page flip where the whole buffer goes regardless. So the
rectangles the tree produces are worth passing on rather than rounding up to the
whole view.
- **Points are not pixels.** A Retina view is 2 physical pixels per point. Denise
lays out in physical pixels throughout — the conversion happens once, at this
edge, and nothing above it needs the scale factor to hit-test.
- **There is already a cursor.** The host's window system draws one, so the
composited sprite stays off: `Ui::show_cursor(false)`, a decision that sticks
rather than one the next mouse move overrides.
## How a frame reaches the screen
Two `IOSurface`s, shown alternately. The rasteriser draws into whichever one the
compositor is not showing, and `present` swaps them; the view hands the new one
to its `CALayer` as `contents`, where CoreAnimation reads it **in place**.
Nothing is copied between the tree and the screen.
The pair is not only about tearing. Assigning the *same* object to `contents`
tells CoreAnimation nothing — the property has not changed, so it has no reason
to look at the buffer again, and the window shows its first frame for ever while
the application draws happily into memory nobody reads. Two surfaces means every
present assigns a different object, which is a change it cannot miss.
Consequences worth knowing:
- **The buffer is two frames old**, so `acquire` reports `BufferAge::Frames(2)`
and `DamageTracker` widens the repaint to match. That is the case it exists
for.
- **`present` is what publishes.** The view calls it after the delegate returns,
so a delegate that only paints keeps working — but a host driving a
`ViewSurface` itself must present, or nothing reaches the screen.
The alternative, a `CGImage` snapshot per frame, is copied whole on every commit
however little of it changed. On a 1040×720 view with one spinner: 9.2% of a
core against 0.6%.
## Platform
macOS only; elsewhere the crate compiles to almost nothing. Built on **objc2**, so
the Objective-C bridging is checked rather than hand-rolled. `unsafe` is necessarily
permitted here; every block carries a `// SAFETY:` comment.
`examples/embed.rs` is a complete host — a window, a view and the run loop:
```text
cargo run -p denise-macos --example embed
```
## Where this sits
Wraps [`denise-ui`](https://crates.io/crates/denise-ui). The Windows equivalents are
[`denise-win32`](https://crates.io/crates/denise-win32) and
[`denise-activex`](https://crates.io/crates/denise-activex).
## Status
**M5 complete**, run on real hardware. Part of [Denise][Denise] — see the
[repository README][Denise] for the whole picture.
MIT licensed.
[Denise]: https://github.com/bisand/denise