denise_activex/control.rs
1//! The OLE control object: what a container actually holds.
2//!
3//! A windowed, inside-out, activate-when-visible control, which is the shape a
4//! VB6 form or an MFC dialog expects. The container calls `SetClientSite`, then
5//! `DoVerb(OLEIVERB_INPLACEACTIVATE)`, and at that point this creates a
6//! [`DeniseControl`] child window inside the container's own. From then on
7//! Windows delivers input straight to it and the container is out of the path.
8//!
9//! # Why `RefCell`
10//!
11//! COM methods take `&self`, and every one of these has to mutate something. The
12//! class is registered `ThreadingModel=Apartment`, so the object only ever runs
13//! on the thread that created it — which is also the thread its window procedure
14//! runs on. That is what makes a `RefCell` the right tool rather than a lock.
15
16use std::cell::RefCell;
17use std::panic::{AssertUnwindSafe, catch_unwind};
18use std::time::Instant;
19
20use denise::{InputEvent, Rect, Role, Size, Surface, Theme};
21use denise_ui::widgets::{Button, Label, Panel, TextInput};
22use denise_ui::{NodeId, Ui};
23use denise_win32::{ControlDelegate, DeniseControl, DibSurface};
24use windows::Win32::Foundation::{
25 DV_E_DVASPECT, E_FAIL, E_NOINTERFACE, E_NOTIMPL, E_POINTER, HWND, RECT, RECTL, SIZE,
26};
27use windows::Win32::Graphics::Gdi::{
28 HALFTONE, HDC, LOGPALETTE, RestoreDC, SRCCOPY, SaveDC, SetBrushOrgEx, SetStretchBltMode,
29 StretchBlt,
30};
31use windows::Win32::System::Com::{
32 ADVF_ONLYONCE, CoTaskMemAlloc, DISPATCH_METHOD, DISPPARAMS, DVASPECT, DVASPECT_CONTENT,
33 DVTARGETDEVICE, IAdviseSink, IDataObject, IDispatch, IEnumSTATDATA, IMoniker, IPersist,
34 IPersist_Impl, IPersistStreamInit, IPersistStreamInit_Impl, IStream, ITypeInfo,
35};
36use windows::Win32::System::Diagnostics::Debug::IObjectSafety_Impl;
37use windows::Win32::System::Ole::{
38 IEnumOLEVERB, IOleClientSite, IOleControl_Impl, IOleInPlaceObject_Impl, IOleInPlaceSite,
39 IOleObject_Impl, IOleWindow_Impl, IViewObject_Impl, IViewObject2_Impl, OLECLOSE, OLEGETMONIKER,
40 OLEIVERB_HIDE, OLEIVERB_INPLACEACTIVATE, OLEIVERB_SHOW, OLEIVERB_UIACTIVATE, OLEMISC,
41 OLEWHICHMK, USERCLASSTYPE,
42};
43use windows::Win32::UI::WindowsAndMessaging::{DestroyWindow, SWP_NOZORDER, SetWindowPos};
44use windows_core::{BOOL, GUID, HRESULT, Interface, OutRef, Ref, implement};
45
46use crate::dispatch;
47use crate::himetric::{himetric_to_pixels, pixels_to_himetric};
48use crate::model::{Model, Shared};
49use crate::registry::MISC_STATUS;
50use crate::safety;
51use crate::server::CLSID_DENISE_PANEL;
52use crate::view;
53
54/// The message the panel's button emits. One button and one message, because an
55/// automation surface without a type library is late-bound: every addition is
56/// something a host has to discover by reading documentation rather than by
57/// pressing `.`.
58const MSG_ACTIVATED: u32 = 1;
59
60/// The panel a container embeds.
61#[implement(
62 windows::Win32::System::Ole::IOleObject,
63 windows::Win32::System::Ole::IOleInPlaceObject,
64 windows::Win32::System::Ole::IOleControl,
65 windows::Win32::System::Com::IPersistStreamInit,
66 windows::Win32::System::Com::IDispatch,
67 windows::Win32::System::Com::IConnectionPointContainer,
68 windows::Win32::System::Com::IConnectionPoint,
69 windows::Win32::System::Ole::IViewObject2,
70 windows::Win32::System::Diagnostics::Debug::IObjectSafety
71)]
72pub struct DenisePanel {
73 pub(crate) state: RefCell<PanelState>,
74 /// The scriptable half, shared with the tree inside the child window. A
75 /// separate cell from `state` on purpose: a property put reaches both, and
76 /// one lock over the two would deadlock against itself the first time an
77 /// event handler assigned to a property.
78 pub(crate) model: Shared,
79}
80
81pub(crate) struct PanelState {
82 /// The container's site, from `SetClientSite`. `None` before it arrives and
83 /// after `Close`.
84 site: Option<IOleClientSite>,
85 /// The child window, once activated in place.
86 control: Option<DeniseControl>,
87 /// Extent in HIMETRIC units, which is what OLE asks for and reports.
88 extent: SIZE,
89 /// Where the container put us, in its client coordinates.
90 position: RECT,
91 /// Whether persisted state has been initialised, for `IPersistStreamInit`.
92 initialised: bool,
93 /// The sink from `IViewObject::SetAdvise`, told when the picture changes.
94 ///
95 /// This is how a form editor learns that a design-time property was assigned:
96 /// there is no window to invalidate, so the only way its drawing ever
97 /// refreshes is if the control says so.
98 view_sink: Option<IAdviseSink>,
99 /// The aspects that sink asked about, echoed back in the notification.
100 view_aspects: u32,
101 /// The `ADVF_` flags it registered with. Only `ONLYONCE` changes anything.
102 view_advf: u32,
103 /// The description handed to `IDispatch::GetTypeInfo`, loaded on first use.
104 pub(crate) type_info: Option<ITypeInfo>,
105}
106
107impl Default for DenisePanel {
108 fn default() -> Self {
109 Self::new()
110 }
111}
112
113impl DenisePanel {
114 /// A control with no site and no window, which is what the class factory
115 /// hands back. Everything else happens when the container calls in.
116 pub fn new() -> Self {
117 Self {
118 state: RefCell::new(PanelState {
119 site: None,
120 control: None,
121 // 200x120 pixels at 96 DPI: a reasonable footprint for a control
122 // dropped on a form, and the container overrides it anyway.
123 extent: SIZE {
124 cx: pixels_to_himetric(200),
125 cy: pixels_to_himetric(120),
126 },
127 position: RECT::default(),
128 initialised: false,
129 view_sink: None,
130 view_aspects: 0,
131 view_advf: 0,
132 type_info: None,
133 }),
134 model: Model::new(),
135 }
136 }
137}
138
139impl DenisePanel_Impl {
140 /// Creates the child window inside the container's, if it does not exist.
141 ///
142 /// The container's `HWND` comes from its `IOleInPlaceSite`, which is the only
143 /// way an in-process control learns where it lives.
144 fn activate(&self) -> windows_core::Result<()> {
145 if self.state.borrow().control.is_some() {
146 return Ok(());
147 }
148 let site = self
149 .state
150 .borrow()
151 .site
152 .clone()
153 .ok_or_else(|| windows_core::Error::from(E_FAIL))?;
154 let in_place: IOleInPlaceSite = site.cast()?;
155 // SAFETY: `in_place` is the container's own object; these are its
156 // documented calls and take no arguments needing validation.
157 let parent: HWND = unsafe { in_place.GetWindow() }?;
158 // SAFETY: telling the container we are activating, which the protocol
159 // requires before putting a window in its client area.
160 unsafe { in_place.OnInPlaceActivate() }?;
161
162 // A container that activates before calling `SetObjectRects` leaves the
163 // position empty, and a 1x1 control is indistinguishable from a broken
164 // one. The extent is what it told us in `SetExtent`, in HIMETRIC, so fall
165 // back to that rather than to nothing.
166 let (position, extent) = {
167 let state = self.state.borrow();
168 (state.position, state.extent)
169 };
170 let width = position.right - position.left;
171 let height = position.bottom - position.top;
172 let bounds = if width > 0 && height > 0 {
173 Rect::new(position.left, position.top, width, height)
174 } else {
175 Rect::new(
176 position.left,
177 position.top,
178 himetric_to_pixels(extent.cx).max(1),
179 himetric_to_pixels(extent.cy).max(1),
180 )
181 };
182 let size = Size::new(bounds.width as u32, bounds.height as u32);
183 let tree = Tree::new(size, self.model.clone());
184 let control = DeniseControl::new(parent, bounds, 1.0, Box::new(tree))
185 .map_err(|_| windows_core::Error::from(E_FAIL))?;
186
187 self.state.borrow_mut().control = Some(control);
188 control.update();
189 Ok(())
190 }
191
192 /// Destroys the child window, if there is one.
193 fn deactivate(&self) {
194 let control = self.state.borrow_mut().control.take();
195 if let Some(control) = control {
196 // SAFETY: the window was created by `activate` and is destroyed once.
197 unsafe {
198 let _ = DestroyWindow(control.hwnd());
199 }
200 }
201 }
202
203 /// Pushes a property change into the live tree.
204 ///
205 /// Called from every property put, which arrives from two places that look
206 /// identical from here: a script, and an event handler the tree itself is in
207 /// the middle of calling. The second must not reach the tree.
208 /// [`DeniseControl::update`] holds the control's own `RefCell` across the
209 /// whole delegate call, so running it again from inside would borrow it twice
210 /// and panic out through a COM method into the host. The delegate holds the
211 /// flag for its entire pass — including while an event is being raised — and
212 /// applies whatever a handler left behind before it returns.
213 pub(crate) fn sync(&self) {
214 if self.model.borrow().inside {
215 return;
216 }
217 // Copied out before the call: `update` runs the tree, which can raise an
218 // event, whose handler can land straight back in this object.
219 let control = self.state.borrow().control;
220 match control {
221 // A panic in the tree must not unwind into a host's script engine,
222 // which is what a `catch_unwind` at this boundary buys. The window
223 // procedure has the same wrapper for input; this is the other door in.
224 Some(control) => {
225 let _ = catch_unwind(AssertUnwindSafe(|| control.update()));
226 }
227 // No window, so nothing to repaint — but somebody may be drawing the
228 // control themselves through `IViewObject::Draw`, and a picture is
229 // only as current as the last time its owner was told to redraw it.
230 None => self.notify_view(),
231 }
232 }
233
234 /// Tells the view sink, if there is one, that what it drew is now stale.
235 fn notify_view(&self) {
236 let (sink, aspects, once) = {
237 let state = self.state.borrow();
238 let once = state.view_advf & ADVF_ONLYONCE.0 as u32 != 0;
239 (state.view_sink.clone(), state.view_aspects, once)
240 };
241 let Some(sink) = sink else {
242 return;
243 };
244 // Dropped before the call rather than after: `OnViewChange` is entitled
245 // to draw, and drawing lands back in this object.
246 if once {
247 self.state.borrow_mut().view_sink = None;
248 }
249 // SAFETY: `sink` is the container's object, kept alive by the clone.
250 // `-1` is every index, which for a control with one view is the only one.
251 unsafe { sink.OnViewChange(aspects, -1) };
252 }
253
254 /// Draws the control as it currently stands onto somebody else's device
255 /// context.
256 ///
257 /// The whole point is that this works with no window and no site: a form
258 /// editor asks for the picture before the control is ever activated, and a
259 /// control that cannot answer is a blank rectangle on the form. So the tree
260 /// is built from the model, painted once into a surface of its own, and
261 /// blitted — none of which touches the live control, if there even is one.
262 fn render(&self, target: HDC, bounds: *const RECTL) -> windows_core::Result<()> {
263 if bounds.is_null() {
264 return Err(E_POINTER.into());
265 }
266 // SAFETY: the container promises a readable RECTL.
267 let bounds = unsafe { *bounds };
268 let Some(plan) = view::plan(bounds.left, bounds.top, bounds.right, bounds.bottom) else {
269 // Nothing asked for, so nothing drawn. Not a failure: see `view`.
270 return Ok(());
271 };
272
273 let size = Size::new(plan.source_width, plan.source_height);
274 let mut surface =
275 DibSurface::new(size, 1.0).map_err(|_| windows_core::Error::from(E_FAIL))?;
276 let (mut ui, _nodes) = build(size, &self.model);
277 // No input, no elapsed time and nothing focused, so this is the control
278 // at rest: no hover, no pressed button and no caret. Which is what a
279 // design-time picture should be.
280 ui.tick(0);
281 ui.invalidate_all();
282 {
283 let mut frame = surface
284 .acquire()
285 .map_err(|_| windows_core::Error::from(E_FAIL))?;
286 ui.paint(&mut frame);
287 }
288
289 // SAFETY: `target` is the container's device context, live for the call;
290 // the source DC holds the DIB just painted and the source rectangle is
291 // the whole of it.
292 //
293 // The device context belongs to the container, so its state is put back
294 // afterwards. Changing a stretch mode and leaving it changed is the kind
295 // of thing that makes somebody else's later drawing wrong for no reason
296 // they can trace. `HALFTONE` is documented as requiring the brush origin
297 // to be set after it, which is the only reason that call is here — and
298 // neither is touched at all unless the blit actually scales.
299 let drawn = unsafe {
300 let saved = SaveDC(target);
301 if plan.stretches() {
302 SetStretchBltMode(target, HALFTONE);
303 let _ = SetBrushOrgEx(target, 0, 0, None);
304 }
305 let drawn = StretchBlt(
306 target,
307 plan.x,
308 plan.y,
309 plan.width,
310 plan.height,
311 Some(surface.dc()),
312 0,
313 0,
314 size.width as i32,
315 size.height as i32,
316 SRCCOPY,
317 );
318 if saved != 0 {
319 let _ = RestoreDC(target, saved);
320 }
321 drawn
322 };
323 if drawn.as_bool() {
324 Ok(())
325 } else {
326 Err(E_FAIL.into())
327 }
328 }
329}
330
331// ------------------------------------------------------------------ IOleObject
332
333impl IOleObject_Impl for DenisePanel_Impl {
334 fn SetClientSite(&self, site: Ref<'_, IOleClientSite>) -> windows_core::Result<()> {
335 self.state.borrow_mut().site = site.cloned();
336 Ok(())
337 }
338
339 fn GetClientSite(&self) -> windows_core::Result<IOleClientSite> {
340 self.state
341 .borrow()
342 .site
343 .clone()
344 .ok_or_else(|| windows_core::Error::from(E_FAIL))
345 }
346
347 fn SetHostNames(
348 &self,
349 _container: &windows_core::PCWSTR,
350 _object: &windows_core::PCWSTR,
351 ) -> windows_core::Result<()> {
352 // The names are for a title bar this control does not have.
353 Ok(())
354 }
355
356 fn Close(&self, _save: &OLECLOSE) -> windows_core::Result<()> {
357 self.deactivate();
358 {
359 let mut state = self.state.borrow_mut();
360 state.site = None;
361 // The same cycle as the event sinks below, one interface along.
362 state.view_sink = None;
363 }
364 // A sink holds the control and the control holds the sink. A container
365 // that unadvises has already broken that cycle; one that forgot has just
366 // said it is finished, and this is the last chance to break it for them.
367 self.model.borrow_mut().clear_sinks();
368 Ok(())
369 }
370
371 fn SetMoniker(
372 &self,
373 _which: &OLEWHICHMK,
374 _moniker: Ref<'_, IMoniker>,
375 ) -> windows_core::Result<()> {
376 // Linking, which this control is registered as not supporting —
377 // OLEMISC_CANTLINKINSIDE.
378 Err(E_FAIL.into())
379 }
380
381 fn GetMoniker(
382 &self,
383 _assign: &OLEGETMONIKER,
384 _which: &OLEWHICHMK,
385 ) -> windows_core::Result<IMoniker> {
386 Err(E_FAIL.into())
387 }
388
389 fn InitFromData(
390 &self,
391 _data: Ref<'_, IDataObject>,
392 _creation: BOOL,
393 _reserved: u32,
394 ) -> windows_core::Result<()> {
395 Err(E_FAIL.into())
396 }
397
398 fn GetClipboardData(&self, _reserved: u32) -> windows_core::Result<IDataObject> {
399 Err(E_FAIL.into())
400 }
401
402 fn DoVerb(
403 &self,
404 verb: i32,
405 _message: *const windows::Win32::UI::WindowsAndMessaging::MSG,
406 _site: Ref<'_, IOleClientSite>,
407 _index: i32,
408 _parent: HWND,
409 position: *const RECT,
410 ) -> windows_core::Result<()> {
411 if !position.is_null() {
412 // SAFETY: the container promises a readable RECT when it passes one.
413 self.state.borrow_mut().position = unsafe { *position };
414 }
415 // SHOW, INPLACEACTIVATE and UIACTIVATE all mean the same thing for an
416 // inside-out control: put a live window on screen. Treating them
417 // differently is how a control ends up visible but inert.
418 if verb == OLEIVERB_SHOW.0
419 || verb == OLEIVERB_INPLACEACTIVATE.0
420 || verb == OLEIVERB_UIACTIVATE.0
421 {
422 self.activate()
423 } else if verb == OLEIVERB_HIDE.0 {
424 self.deactivate();
425 Ok(())
426 } else {
427 Err(E_FAIL.into())
428 }
429 }
430
431 fn EnumVerbs(&self) -> windows_core::Result<IEnumOLEVERB> {
432 // The container falls back to the registry's verb list, which for a
433 // control with only "show" is the right amount of ceremony.
434 Err(E_FAIL.into())
435 }
436
437 fn Update(&self) -> windows_core::Result<()> {
438 // Through `sync` rather than straight to the control: a container is
439 // entitled to call this from inside an event handler, and that is the one
440 // moment the tree must not be run again.
441 self.sync();
442 Ok(())
443 }
444
445 fn IsUpToDate(&self) -> windows_core::Result<()> {
446 Ok(())
447 }
448
449 fn GetUserClassID(&self) -> windows_core::Result<GUID> {
450 Ok(CLSID_DENISE_PANEL)
451 }
452
453 fn GetUserType(&self, _form: &USERCLASSTYPE) -> windows_core::Result<windows_core::PWSTR> {
454 // The caller frees this with `CoTaskMemFree`, so it has to come from
455 // `CoTaskMemAlloc` and not from Rust's allocator.
456 let wide: Vec<u16> = crate::registry::FRIENDLY_NAME
457 .encode_utf16()
458 .chain(core::iter::once(0))
459 .collect();
460 let bytes = core::mem::size_of_val(wide.as_slice());
461 // SAFETY: allocating `bytes` and then writing exactly that many.
462 let buffer = unsafe { CoTaskMemAlloc(bytes) } as *mut u16;
463 if buffer.is_null() {
464 return Err(windows::Win32::Foundation::E_OUTOFMEMORY.into());
465 }
466 // SAFETY: `buffer` is a fresh allocation of `bytes`, and `wide` holds
467 // exactly that many bytes and cannot overlap it.
468 unsafe { core::ptr::copy_nonoverlapping(wide.as_ptr(), buffer, wide.len()) };
469 Ok(windows_core::PWSTR(buffer))
470 }
471
472 fn SetExtent(&self, _aspect: DVASPECT, size: *const SIZE) -> windows_core::Result<()> {
473 if size.is_null() {
474 return Err(E_POINTER.into());
475 }
476 // SAFETY: the container promises a readable SIZE.
477 self.state.borrow_mut().extent = unsafe { *size };
478 Ok(())
479 }
480
481 fn GetExtent(&self, _aspect: DVASPECT) -> windows_core::Result<SIZE> {
482 Ok(self.state.borrow().extent)
483 }
484
485 fn Advise(&self, _sink: Ref<'_, IAdviseSink>) -> windows_core::Result<u32> {
486 // No advisory connections: nothing here changes behind the container's
487 // back, so there is nothing to notify about.
488 Ok(0)
489 }
490
491 fn Unadvise(&self, _token: u32) -> windows_core::Result<()> {
492 Ok(())
493 }
494
495 fn EnumAdvise(&self) -> windows_core::Result<IEnumSTATDATA> {
496 Err(E_FAIL.into())
497 }
498
499 fn GetMiscStatus(&self, _aspect: DVASPECT) -> windows_core::Result<OLEMISC> {
500 // The same flags the registry carries. A container may ask either way,
501 // and the two disagreeing is a control that behaves differently
502 // depending on which one it happened to read.
503 Ok(OLEMISC(MISC_STATUS as i32))
504 }
505
506 fn SetColorScheme(
507 &self,
508 _palette: *const windows::Win32::Graphics::Gdi::LOGPALETTE,
509 ) -> windows_core::Result<()> {
510 Ok(())
511 }
512}
513
514// ------------------------------------------------------------------ IOleWindow
515
516impl IOleWindow_Impl for DenisePanel_Impl {
517 fn GetWindow(&self) -> windows_core::Result<HWND> {
518 self.state
519 .borrow()
520 .control
521 .map(|c| c.hwnd())
522 .ok_or_else(|| windows_core::Error::from(E_FAIL))
523 }
524
525 fn ContextSensitiveHelp(&self, _entering: BOOL) -> windows_core::Result<()> {
526 Ok(())
527 }
528}
529
530// ----------------------------------------------------------- IOleInPlaceObject
531
532impl IOleInPlaceObject_Impl for DenisePanel_Impl {
533 fn InPlaceDeactivate(&self) -> windows_core::Result<()> {
534 self.deactivate();
535 Ok(())
536 }
537
538 fn UIDeactivate(&self) -> windows_core::Result<()> {
539 // Nothing to hand back: this control merges no menus, no toolbars and no
540 // accelerators into the container's.
541 Ok(())
542 }
543
544 fn SetObjectRects(
545 &self,
546 position: *const RECT,
547 _clip: *const RECT,
548 ) -> windows_core::Result<()> {
549 if position.is_null() {
550 return Err(E_POINTER.into());
551 }
552 // SAFETY: the container promises a readable RECT.
553 let position = unsafe { *position };
554 self.state.borrow_mut().position = position;
555
556 let control = self.state.borrow().control;
557 if let Some(control) = control {
558 // SAFETY: the control's window is live while `control` is `Some`.
559 unsafe {
560 let _ = SetWindowPos(
561 control.hwnd(),
562 None,
563 position.left,
564 position.top,
565 position.right - position.left,
566 position.bottom - position.top,
567 SWP_NOZORDER,
568 );
569 }
570 }
571 // The clip rectangle is the container's business: a child window is
572 // already clipped to its parent, which is what makes it a child window.
573 Ok(())
574 }
575
576 fn ReactivateAndUndo(&self) -> windows_core::Result<()> {
577 Err(E_FAIL.into())
578 }
579}
580
581// --------------------------------------------------------------- IOleControl
582
583impl IOleControl_Impl for DenisePanel_Impl {
584 fn GetControlInfo(
585 &self,
586 _info: *mut windows::Win32::System::Ole::CONTROLINFO,
587 ) -> windows_core::Result<()> {
588 // No mnemonics to register with the container.
589 Err(E_FAIL.into())
590 }
591
592 fn OnMnemonic(
593 &self,
594 _message: *const windows::Win32::UI::WindowsAndMessaging::MSG,
595 ) -> windows_core::Result<()> {
596 Err(E_FAIL.into())
597 }
598
599 fn OnAmbientPropertyChange(&self, _dispid: i32) -> windows_core::Result<()> {
600 Ok(())
601 }
602
603 fn FreezeEvents(&self, _freeze: BOOL) -> windows_core::Result<()> {
604 Ok(())
605 }
606}
607
608// ------------------------------------------------- IViewObject / IViewObject2
609
610/// Drawing without a window.
611///
612/// Everything else in this file assumes a live control: the container sites it,
613/// activates it, and Windows delivers input to a real `HWND`. A form editor does
614/// none of that. It drops the control on a design surface, sets properties on it,
615/// and asks for a picture — and a control with no answer is the blank rectangle
616/// that this interface exists to avoid.
617///
618/// The container's device context is the whole interface, which also makes this
619/// the path a print preview and a copy-to-metafile take.
620impl IViewObject_Impl for DenisePanel_Impl {
621 fn Draw(
622 &self,
623 aspect: DVASPECT,
624 _index: i32,
625 _aspect_info: *mut core::ffi::c_void,
626 _target_device: *const DVTARGETDEVICE,
627 _target_dc: HDC,
628 draw_dc: HDC,
629 bounds: *const RECTL,
630 _window_bounds: *const RECTL,
631 _continue_fn: isize,
632 _continue_arg: usize,
633 ) -> windows_core::Result<()> {
634 // CONTENT is the control itself. THUMBNAIL, ICON and DOCPRINT are the
635 // other three, and answering one of them with the content is worse than
636 // declining: a container asked for a 32x32 icon and would scale a panel
637 // into it rather than fall back to the class's registered default.
638 if aspect != DVASPECT_CONTENT {
639 return Err(DV_E_DVASPECT.into());
640 }
641 // A panic must not unwind out of a COM method into a form editor, and
642 // this one runs the tree — the same boundary `sync` guards.
643 catch_unwind(AssertUnwindSafe(|| self.render(draw_dc, bounds)))
644 .unwrap_or_else(|_| Err(E_FAIL.into()))
645 }
646
647 fn GetColorSet(
648 &self,
649 _aspect: DVASPECT,
650 _index: i32,
651 _aspect_info: *mut core::ffi::c_void,
652 _target_device: *const DVTARGETDEVICE,
653 _target_dc: HDC,
654 _colours: *mut *mut LOGPALETTE,
655 ) -> windows_core::Result<()> {
656 // The surface is 32-bit RGB, so there is no palette to negotiate. A
657 // container on a palettised display would want one; there are none left.
658 Err(E_NOTIMPL.into())
659 }
660
661 fn Freeze(
662 &self,
663 _aspect: DVASPECT,
664 _index: i32,
665 _aspect_info: *mut core::ffi::c_void,
666 _token: *mut u32,
667 ) -> windows_core::Result<()> {
668 // Freezing pins a view so a container can draw it repeatedly and know it
669 // has not changed underneath. Every `Draw` here renders from the model on
670 // the spot, so there is no cached view to pin and nothing honest to
671 // promise.
672 Err(E_NOTIMPL.into())
673 }
674
675 fn Unfreeze(&self, _token: u32) -> windows_core::Result<()> {
676 Err(E_NOTIMPL.into())
677 }
678
679 fn SetAdvise(
680 &self,
681 aspects: DVASPECT,
682 advf: u32,
683 sink: Ref<'_, IAdviseSink>,
684 ) -> windows_core::Result<()> {
685 // One sink, replaced rather than added to: `SetAdvise` is documented as
686 // supporting exactly one, which is what distinguishes it from the
687 // `IOleObject::Advise` list.
688 let mut state = self.state.borrow_mut();
689 state.view_sink = sink.cloned();
690 state.view_aspects = aspects.0;
691 state.view_advf = advf;
692 Ok(())
693 }
694
695 fn GetAdvise(
696 &self,
697 aspects: *mut u32,
698 advf: *mut u32,
699 sink: OutRef<'_, IAdviseSink>,
700 ) -> windows_core::Result<()> {
701 // Each of the three is optional, and a container that only wants one
702 // passes null for the others. Writing through those is the crash.
703 let state = self.state.borrow();
704 if !aspects.is_null() {
705 // SAFETY: non-null, and the caller owns a `u32` behind it.
706 unsafe { *aspects = state.view_aspects };
707 }
708 if !advf.is_null() {
709 // SAFETY: as above.
710 unsafe { *advf = state.view_advf };
711 }
712 if !sink.is_null() {
713 sink.write(state.view_sink.clone())?;
714 }
715 Ok(())
716 }
717}
718
719impl IViewObject2_Impl for DenisePanel_Impl {
720 fn GetExtent(
721 &self,
722 _aspect: DVASPECT,
723 _index: i32,
724 _target_device: *const DVTARGETDEVICE,
725 ) -> windows_core::Result<SIZE> {
726 // Deliberately the same value `IOleObject::GetExtent` reports. The whole
727 // reason `IViewObject2` exists is to save a container a `QueryInterface`
728 // for that answer, so the two disagreeing would be a control that changes
729 // size depending on which interface was asked.
730 Ok(self.state.borrow().extent)
731 }
732}
733
734// -------------------------------------------------------------- IObjectSafety
735
736/// The claim, answered per interface.
737///
738/// What is being claimed, and why it is true of this control, is in
739/// [`safety`](crate::safety) — the short version is that the entire scriptable
740/// surface is two strings, a boolean and a repaint. The mapping from an
741/// interface id to a question is here because it needs the ids; the answer to
742/// each question is there, where it can be tested anywhere.
743impl IObjectSafety_Impl for DenisePanel_Impl {
744 fn GetInterfaceSafetyOptions(
745 &self,
746 riid: *const GUID,
747 supported: *mut u32,
748 enabled: *mut u32,
749 ) -> windows_core::Result<()> {
750 if supported.is_null() || enabled.is_null() {
751 return Err(E_POINTER.into());
752 }
753 let options = safety::supported(asked_about(riid));
754 if options == 0 {
755 // No claim about that interface, which is not the same as claiming
756 // it is unsafe — the host asked about something this control has
757 // nothing to say about.
758 return Err(E_NOINTERFACE.into());
759 }
760 // Supported and enabled are the same value on purpose. There is no mode
761 // to switch into: the control is safe because of what its members do,
762 // not because a host asked it to behave.
763 // SAFETY: both pointers were checked non-null above, and the caller owns
764 // a `u32` behind each.
765 unsafe {
766 *supported = options;
767 *enabled = options;
768 }
769 Ok(())
770 }
771
772 fn SetInterfaceSafetyOptions(
773 &self,
774 riid: *const GUID,
775 mask: u32,
776 _enabled: u32,
777 ) -> windows_core::Result<()> {
778 let options = safety::supported(asked_about(riid));
779 if options == 0 {
780 return Err(E_NOINTERFACE.into());
781 }
782 // The requested values are ignored, and only the mask is checked. A host
783 // switching a guarantee off cannot make this control unsafe, and one
784 // asking for a guarantee that was never offered must be told no rather
785 // than quietly agreed with.
786 if safety::accepts(options, mask) {
787 Ok(())
788 } else {
789 Err(E_FAIL.into())
790 }
791 }
792}
793
794/// Which of the two questions an interface id is asking.
795///
796/// `IDispatchEx` is named without being implemented: a host that asks about it
797/// is asking about scripting, and the honest answer to "is script safe here" does
798/// not depend on which scripting interface it came through. The `IPersist`
799/// family is the other question — untrusted *data* rather than untrusted callers
800/// — and `IPersistStreamInit` is the one this control actually has.
801fn asked_about(riid: *const GUID) -> safety::Asked {
802 if riid.is_null() {
803 return safety::Asked::Other;
804 }
805 // SAFETY: non-null, and the caller promises a readable GUID.
806 let riid = unsafe { *riid };
807 if riid == IDispatch::IID || riid == IID_IDISPATCHEX {
808 safety::Asked::Automation
809 } else if riid == IPersistStreamInit::IID || riid == IPersist::IID {
810 safety::Asked::Persistence
811 } else {
812 safety::Asked::Other
813 }
814}
815
816/// `IID_IDispatchEx`, which this control does not implement.
817///
818/// Written out because the interface is not in the crate's enabled features and
819/// pulling in a whole module for one identity is worse than a constant. A host
820/// that asks about it is asking the scripting question either way.
821const IID_IDISPATCHEX: GUID = GUID::from_u128(0xa6ef9860_c720_11d0_9337_00a0c90dcaa9);
822
823// ---------------------------------------------------- IPersist / StreamInit
824
825impl IPersist_Impl for DenisePanel_Impl {
826 fn GetClassID(&self) -> windows_core::Result<GUID> {
827 Ok(CLSID_DENISE_PANEL)
828 }
829}
830
831impl IPersistStreamInit_Impl for DenisePanel_Impl {
832 fn IsDirty(&self) -> HRESULT {
833 // Nothing is persisted yet, so nothing is ever unsaved. `S_FALSE` is
834 // "clean"; returning `S_OK` would make a container prompt to save a
835 // control that has no state.
836 windows::Win32::Foundation::S_FALSE
837 }
838
839 fn Load(&self, _stream: Ref<'_, IStream>) -> windows_core::Result<()> {
840 // No properties yet, so a saved form has nothing for this to read. It
841 // must still succeed: VB6 calls it on every load and treats a failure as
842 // a broken control.
843 self.state.borrow_mut().initialised = true;
844 Ok(())
845 }
846
847 fn Save(&self, _stream: Ref<'_, IStream>, _clear_dirty: BOOL) -> windows_core::Result<()> {
848 Ok(())
849 }
850
851 fn GetSizeMax(&self) -> windows_core::Result<u64> {
852 Ok(0)
853 }
854
855 fn InitNew(&self) -> windows_core::Result<()> {
856 self.state.borrow_mut().initialised = true;
857 Ok(())
858 }
859}
860
861/// The nodes a script can reach, once they exist.
862///
863/// `Option` because [`Ui::add`] can refuse, and a control whose tree failed to
864/// build should draw nothing rather than panic inside a host's message loop.
865#[derive(Clone, Copy, Default)]
866struct Nodes {
867 label: Option<NodeId>,
868 input: Option<NodeId>,
869 button: Option<NodeId>,
870}
871
872/// What a container sees, and what a script drives: a heading, a field and a
873/// button.
874struct Tree {
875 ui: Ui<u32>,
876 nodes: Nodes,
877 model: Shared,
878 started: Instant,
879}
880
881impl Tree {
882 fn new(size: Size, model: Shared) -> Self {
883 let (ui, nodes) = build(size, &model);
884 Self {
885 ui,
886 nodes,
887 model,
888 started: Instant::now(),
889 }
890 }
891
892 /// Writes anything a script assigned into the widgets.
893 ///
894 /// Runs with `inside` already set, so a property put from an event handler
895 /// cannot reach back in behind it.
896 fn apply(&mut self) {
897 let (text, caption, enabled, dirty, refresh) = {
898 let mut model = self.model.borrow_mut();
899 let pending = (
900 model.text.clone(),
901 model.caption.clone(),
902 model.enabled,
903 model.dirty,
904 model.refresh,
905 );
906 model.dirty = false;
907 model.refresh = false;
908 pending
909 };
910
911 if refresh {
912 self.ui.invalidate_all();
913 }
914 if !dirty {
915 return;
916 }
917
918 if let Some(label) = self
919 .nodes
920 .label
921 .and_then(|id| self.ui.widget_mut::<Label>(id))
922 {
923 label.set_text(caption);
924 }
925
926 // Only when it differs. `set_text` puts the caret at the end, so writing
927 // the same string on every pass would move the caret out from under
928 // anyone editing in the middle of a word.
929 let stale = self.nodes.input.filter(|id| {
930 self.ui
931 .widget::<TextInput<u32>>(*id)
932 .is_some_and(|field| field.text() != text)
933 });
934 if let Some(field) = stale.and_then(|id| self.ui.widget_mut::<TextInput<u32>>(id)) {
935 field.set_text(text);
936 }
937
938 for id in [self.nodes.input, self.nodes.button].into_iter().flatten() {
939 self.ui.set_enabled(id, enabled);
940 }
941 }
942
943 /// Mirrors the field back into the model and decides what to raise.
944 fn collect(&mut self) -> Vec<i32> {
945 // `any` short-circuits, and the queue is still emptied: a `Drain` removes
946 // its whole range when it is dropped, whether or not it was consumed. The
947 // queue growing without bound is the failure this has to avoid.
948 let clicked = self
949 .ui
950 .drain_messages()
951 .any(|message| message == MSG_ACTIVATED);
952
953 let current = self
954 .nodes
955 .input
956 .and_then(|id| self.ui.widget::<TextInput<u32>>(id))
957 .map(|field| field.text().to_string());
958
959 match current {
960 Some(current) => {
961 let raised = dispatch::events_raised(&self.model.borrow().text, ¤t, clicked);
962 self.model.borrow_mut().text = current;
963 raised
964 }
965 // No field to read, so nothing can have changed in one.
966 None => dispatch::events_raised("", "", clicked),
967 }
968 }
969
970 /// Calls every advised sink, with no borrows held.
971 fn raise(&self, dispid: i32) {
972 let sinks = self.model.borrow().sinks();
973 let params = DISPPARAMS::default();
974 for sink in sinks {
975 // SAFETY: `sink` is the host's object, kept alive by the clone;
976 // `params` is a live local describing no arguments. Neither event
977 // takes any or returns anything, so there is nothing to marshal.
978 //
979 // The result is dropped on purpose: a handler that fails is the
980 // host's problem, and it is not a reason for the panel to stop
981 // drawing.
982 unsafe {
983 let _ = sink.Invoke(
984 dispid,
985 &GUID::zeroed(),
986 0,
987 DISPATCH_METHOD,
988 ¶ms,
989 None,
990 None,
991 None,
992 );
993 }
994 }
995 }
996}
997
998/// Marks the tree as running for as long as it exists.
999///
1000/// A guard rather than two assignments, because the flag has to come down even
1001/// when the pass ends badly. The window procedure turns a panic into
1002/// `DefWindowProc` and carries on, and a flag left standing after one would
1003/// silently stop every later property put from ever reaching the tree — a control
1004/// that quietly stops responding to script, with nothing in the logs.
1005struct Running(Shared);
1006
1007impl Running {
1008 /// Holds an `Rc` rather than a borrow so the caller keeps its `&mut self`.
1009 fn enter(model: &Shared) -> Self {
1010 model.borrow_mut().inside = true;
1011 Self(model.clone())
1012 }
1013}
1014
1015impl Drop for Running {
1016 fn drop(&mut self) {
1017 self.0.borrow_mut().inside = false;
1018 }
1019}
1020
1021/// Builds the tree from whatever the model currently holds.
1022///
1023/// Reads the model rather than hard-coding the strings, so a resize — which
1024/// rebuilds everything — does not throw away what a script assigned.
1025fn build(size: Size, model: &Shared) -> (Ui<u32>, Nodes) {
1026 let (text, caption, enabled) = {
1027 let model = model.borrow();
1028 (model.text.clone(), model.caption.clone(), model.enabled)
1029 };
1030
1031 let mut ui: Ui<u32> = Ui::new(size, Theme::DARK);
1032 // The container's window system draws a pointer already.
1033 ui.show_cursor(false);
1034 let root = ui.root();
1035 let width = size.width as i32;
1036 let height = size.height as i32;
1037 let mut nodes = Nodes::default();
1038
1039 if let Some(card) = ui.add(
1040 root,
1041 Panel::default(),
1042 Rect::new(8, 8, (width - 16).max(1), (height - 16).max(1)),
1043 ) {
1044 nodes.label = ui.add(
1045 card,
1046 Label::new(caption),
1047 Rect::new(12, 10, (width - 40).max(1), 22),
1048 );
1049 let mut field = TextInput::<u32>::new().with_placeholder("Tekst");
1050 field.set_text(text);
1051 nodes.input = ui.add(card, field, Rect::new(12, 38, (width - 40).max(1), 32));
1052 nodes.button = ui.add(
1053 card,
1054 Button::new("OK", MSG_ACTIVATED).with_role(Role::Primary),
1055 Rect::new(12, 78, 96, 30),
1056 );
1057 }
1058
1059 for id in [nodes.input, nodes.button].into_iter().flatten() {
1060 ui.set_enabled(id, enabled);
1061 }
1062
1063 (ui, nodes)
1064}
1065
1066impl ControlDelegate for Tree {
1067 fn update(&mut self, surface: &mut DibSurface, events: &[InputEvent], damage: &mut Vec<Rect>) {
1068 if surface.size() != self.ui.size() {
1069 let (ui, nodes) = build(surface.size(), &self.model);
1070 self.ui = ui;
1071 self.nodes = nodes;
1072 self.ui.invalidate_all();
1073 }
1074
1075 // Held for the whole pass, raising included. `DeniseControl::update` has
1076 // the control's `RefCell` borrowed around this call, so anything a handler
1077 // does that would run the tree again has to be turned away here rather
1078 // than panic there.
1079 let _running = Running::enter(&self.model);
1080
1081 self.apply();
1082 self.ui.handle(events);
1083 self.ui.tick(self.started.elapsed().as_millis() as u64);
1084
1085 for dispid in self.collect() {
1086 self.raise(dispid);
1087 }
1088
1089 // A handler is allowed to assign to a property, and could not reach the
1090 // tree while it ran. One further pass, deliberately not a loop: a handler
1091 // that assigns on every event would otherwise never hand control back.
1092 self.apply();
1093
1094 // Painted last, so a handler that set `Caption` sees it drawn in the same
1095 // frame as the click that called it.
1096 if !self.ui.needs_paint() {
1097 return;
1098 }
1099 if let Ok(mut frame) = surface.acquire() {
1100 self.ui.paint(&mut frame);
1101 drop(frame);
1102 damage.extend_from_slice(self.ui.damage());
1103 self.ui.presented();
1104 }
1105 }
1106
1107 fn next_wake_ms(&self) -> Option<u64> {
1108 self.ui.next_wake_ms()
1109 }
1110}
1111
1112/// Referenced so the flags cannot drift from the registry's copy.
1113const _: u32 = MISC_STATUS;