Skip to main content

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, &current, 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                    &params,
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;