frus-shell 0.5.0

frus — the platform layer: window + event loop (Milestone 0)
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
//! **The application's content on the title bar's line**, on Windows (milestone 640).
//!
//! Windows draws a window's caption in the non-client area, where an application cannot
//! draw. The documented way to share that line ("Custom Window Frame Using DWM") keeps the
//! window the system's:
//!
//! - the frame is extended into the client area by the caption's height
//!   (`DwmExtendFrameIntoClientArea`), so the desktop compositor keeps its three buttons
//!   there and acts on them: it answers the hit test for them (`DwmDefWindowProc`),
//!   minimizes, maximizes and closes, and shows its snap layouts;
//! - `WM_NCCALCSIZE` gives the caption's height to the client, keeping the side and bottom
//!   borders, so the application's content starts at the top of the window;
//! - `WM_NCHITTEST` answers the rest of the line as the caption, so the system moves the
//!   window by it and maximizes it on a double click — except what the application has
//!   there (its menu bar's words), and the window's icon, which opens the window menu.
//!
//! The application **paints** the line: the compositor's buttons are under its content and
//! would not be seen, so frus paints them where the compositor has them
//! ([`buttons`]), in the state the compositor reports ([`buttons_state`]), in the colours
//! the system would draw its own caption in ([`caption_colors`], milestone 642).

use std::cell::RefCell;

use windows_sys::Win32::Foundation::{HWND, LPARAM, LRESULT, POINT, RECT, WPARAM};
use windows_sys::Win32::Graphics::Dwm::{
    DwmDefWindowProc, DwmExtendFrameIntoClientArea, DwmGetWindowAttribute, DwmSetWindowAttribute,
    DWMSBT_AUTO, DWMSBT_MAINWINDOW, DWMWA_CAPTION_BUTTON_BOUNDS, DWMWA_SYSTEMBACKDROP_TYPE,
    DWMWA_USE_IMMERSIVE_DARK_MODE,
};
use windows_sys::Win32::Graphics::Gdi::{
    ClientToScreen, FillRect, GetStockObject, GetSysColor, InvalidateRect, ScreenToClient,
    BLACK_BRUSH, COLOR_ACTIVECAPTION, COLOR_CAPTIONTEXT, COLOR_INACTIVECAPTION,
    COLOR_INACTIVECAPTIONTEXT, HDC,
};
use windows_sys::Win32::System::Registry::{RegGetValueW, HKEY_CURRENT_USER, RRF_RT_REG_DWORD};
use windows_sys::Win32::UI::Accessibility::{HCF_HIGHCONTRASTON, HIGHCONTRASTW};
use windows_sys::Win32::UI::Controls::MARGINS;
use windows_sys::Win32::UI::HiDpi::{GetDpiForWindow, GetSystemMetricsForDpi};
use windows_sys::Win32::UI::Input::KeyboardAndMouse::{
    TrackMouseEvent, TME_LEAVE, TME_NONCLIENT, TRACKMOUSEEVENT,
};
use windows_sys::Win32::UI::Shell::{DefSubclassProc, RemoveWindowSubclass, SetWindowSubclass};
use windows_sys::Win32::UI::WindowsAndMessaging::{
    GetClientRect, GetWindowRect, IsZoomed, PostMessageW, SetWindowPos, SystemParametersInfoW,
    HTCAPTION, HTCLIENT, HTCLOSE, HTMAXBUTTON, HTMINBUTTON, HTSYSMENU, HTTOP, NCCALCSIZE_PARAMS,
    SC_CLOSE, SC_MAXIMIZE, SC_MINIMIZE, SC_RESTORE, SM_CXPADDEDBORDER, SM_CYCAPTION,
    SM_CYSIZEFRAME, SPI_GETHIGHCONTRAST, SWP_FRAMECHANGED, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER,
    WM_DWMCOLORIZATIONCOLORCHANGED, WM_ERASEBKGND, WM_LBUTTONUP, WM_NCCALCSIZE, WM_NCHITTEST,
    WM_NCLBUTTONDOWN, WM_NCLBUTTONUP, WM_NCMOUSELEAVE, WM_SETTINGCHANGE, WM_SIZE, WM_SYSCOMMAND,
    WM_THEMECHANGED,
};

use frus_widgets::Color;

/// The subclass's identity.
const SUBCLASS: usize = 640;
/// How thick the resizing edge along the top is, in physical pixels.
const RESIZE_EDGE: i32 = 6;

/// What the window procedure knows about the line, set by the shell each frame.
#[derive(Default)]
struct Line {
    /// The line's height, in physical pixels.
    height: i32,
    /// Where the application's own content on the line is, in client pixels.
    interactive: Vec<RECT>,
    /// Where the window's icon is, in client pixels.
    icon: Option<RECT>,
    /// The compositor's button the pointer is over, and the one pressed (hit-test codes).
    hovered: u32,
    pressed: u32,
    /// What the system says of its captions, read once and again when it says it changed.
    caption: Option<Caption>,
    /// The backdrop asked of the system for a light or a dark window, and whether it took
    /// it (milestone 643).
    backdrop: Option<(bool, bool)>,
}

/// What the system says of its captions: what [`caption_colors`] is made from.
#[derive(Clone, Copy, Debug, PartialEq)]
struct Caption {
    /// High contrast's caption colours, when it is on: active and inactive, surface and ink.
    high_contrast: Option<[Color; 4]>,
    /// The accent colour, when the person asked for it on title bars, and the one for an
    /// inactive window if the system keeps one.
    accent: Option<(Color, Option<Color>)>,
}

thread_local! {
    static LINE: RefCell<Line> = RefCell::new(Line::default());
    /// Asks the shell for a frame: a repaint message alone does not reach it.
    static WAKE: RefCell<Option<Box<dyn Fn()>>> = const { RefCell::new(None) };
}

/// What to call when the line's state changes and a frame is owed.
pub(crate) fn on_change(wake: impl Fn() + 'static) {
    set_wake(Box::new(wake));
}

/// Not generic, and not inlined: see `frus-widgets/src/component.rs` on thread-locals and hot
/// patches.
#[inline(never)]
fn set_wake(wake: Box<dyn Fn()>) {
    WAKE.with(|slot| *slot.borrow_mut() = Some(wake));
}

/// **The colours the system would draw `hwnd`'s caption in now**: its surface and its words,
/// for a light or a `dark` window, `active` or not (milestone 642).
///
/// - Under **high contrast**, the scheme's caption colours.
/// - With the **accent colour on title bars** (Settings › Personalization › Colours), the
///   accent behind an active window's caption, with white or black words, whichever reads;
///   an inactive window's is the system's own inactive accent if it keeps one, else the
///   plain caption's.
/// - Otherwise the desktop's **plain caption**: `#F3F3F3` (Windows 11; `#FFFFFF` before)
///   or `#202020` dark, black or white words, grey on an inactive window — measured on
///   Windows 11's own captions. An active window's caption there is tinted by the wallpaper
///   (Mica), which an opaque surface cannot follow: the untinted surface is the nearest.
#[inline(never)]
pub(crate) fn caption_colors(dark: bool, active: bool) -> (Color, Color) {
    let caption = LINE.with(|line| *line.borrow_mut().caption.get_or_insert_with(read_caption));
    if let Some([surface, ink, inactive_surface, inactive_ink]) = caption.high_contrast {
        return if active {
            (surface, ink)
        } else {
            (inactive_surface, inactive_ink)
        };
    }
    match caption.accent {
        Some((accent, _)) if active => {
            let words = if accent.compute_luminance() > 0.179 {
                Color::BLACK
            } else {
                Color::WHITE
            };
            return (accent, words);
        }
        Some((_, Some(inactive))) => {
            let words = if inactive.compute_luminance() > 0.179 {
                Color::rgb8(0x91, 0x91, 0x91)
            } else {
                Color::rgb8(0x79, 0x79, 0x79)
            };
            return (inactive, words);
        }
        _ => {}
    }
    match (dark, active) {
        (false, true) => (light_caption(), Color::BLACK),
        (false, false) => (light_caption(), Color::rgb8(0x91, 0x91, 0x91)),
        (true, true) => (Color::rgb8(0x20, 0x20, 0x20), Color::WHITE),
        (true, false) => (Color::rgb8(0x20, 0x20, 0x20), Color::rgb8(0x79, 0x79, 0x79)),
    }
}

/// The plain light caption: Windows 11's grey, white before it.
fn light_caption() -> Color {
    // Windows 11 is build 22000 and later of "Windows 10".
    let build = os_build();
    if build >= 22000 || build == 0 {
        Color::rgb8(0xF3, 0xF3, 0xF3)
    } else {
        Color::WHITE
    }
}

/// The system's build number, `0` if it cannot be read.
#[inline(never)]
fn os_build() -> u32 {
    type RtlGetVersion = unsafe extern "system" fn(
        *mut windows_sys::Win32::System::SystemInformation::OSVERSIONINFOW,
    ) -> i32;
    thread_local! {
        static BUILD: u32 = unsafe {
            use windows_sys::Win32::System::LibraryLoader::{GetModuleHandleA, GetProcAddress};
            let ntdll = GetModuleHandleA(c"ntdll.dll".as_ptr() as *const u8);
            match GetProcAddress(ntdll, c"RtlGetVersion".as_ptr() as *const u8) {
                Some(f) => {
                    let get: RtlGetVersion = std::mem::transmute(f);
                    let mut info: windows_sys::Win32::System::SystemInformation::OSVERSIONINFOW =
                        std::mem::zeroed();
                    info.dwOSVersionInfoSize = std::mem::size_of_val(&info) as u32;
                    if get(&mut info) >= 0 {
                        info.dwBuildNumber
                    } else {
                        0
                    }
                }
                None => 0,
            }
        };
    }
    BUILD.with(|build| *build)
}

/// Reads what the system says of its captions.
fn read_caption() -> Caption {
    // SAFETY: plain queries into structures sized for them.
    unsafe {
        let mut contrast: HIGHCONTRASTW = std::mem::zeroed();
        contrast.cbSize = std::mem::size_of::<HIGHCONTRASTW>() as u32;
        let on = SystemParametersInfoW(
            SPI_GETHIGHCONTRAST,
            contrast.cbSize,
            &mut contrast as *mut HIGHCONTRASTW as *mut core::ffi::c_void,
            0,
        ) != 0
            && contrast.dwFlags & HCF_HIGHCONTRASTON != 0;
        let high_contrast = on.then(|| {
            [
                COLOR_ACTIVECAPTION,
                COLOR_CAPTIONTEXT,
                COLOR_INACTIVECAPTION,
                COLOR_INACTIVECAPTIONTEXT,
            ]
            .map(|index| colorref(GetSysColor(index)))
        });
        let accent = (dwm_dword("ColorPrevalence") == Some(1))
            .then(|| dwm_dword("AccentColor").map(abgr))
            .flatten()
            .map(|accent| (accent, dwm_dword("AccentColorInactive").map(abgr)));
        Caption {
            high_contrast,
            accent,
        }
    }
}

/// A `DWORD` of the desktop compositor's settings for the person.
fn dwm_dword(name: &str) -> Option<u32> {
    let key: Vec<u16> = "Software\\Microsoft\\Windows\\DWM\0"
        .encode_utf16()
        .collect();
    let value: Vec<u16> = name.encode_utf16().chain(std::iter::once(0)).collect();
    let mut data: u32 = 0;
    let mut size = std::mem::size_of::<u32>() as u32;
    // SAFETY: the strings end in a nul, and the buffer is a `DWORD` as asked for.
    let status = unsafe {
        RegGetValueW(
            HKEY_CURRENT_USER,
            key.as_ptr(),
            value.as_ptr(),
            RRF_RT_REG_DWORD,
            std::ptr::null_mut(),
            &mut data as *mut u32 as *mut core::ffi::c_void,
            &mut size,
        )
    };
    (status == 0).then_some(data)
}

/// A `COLORREF` (`0x00BBGGRR`).
fn colorref(c: u32) -> Color {
    Color::rgb8(c as u8, (c >> 8) as u8, (c >> 16) as u8)
}

/// The compositor's accent (`0xAABBGGRR`), opaque.
#[inline(never)]
fn abgr(c: u32) -> Color {
    colorref(c & 0x00FF_FFFF)
}

/// The system's caption height for `hwnd`'s screen, in physical pixels: the caption and
/// the resizing border above it — less that border when the window is maximized, since it
/// is off the screen then and the system's buttons sit in the caption alone.
unsafe fn caption_height(hwnd: HWND) -> i32 {
    let dpi = GetDpiForWindow(hwnd);
    let border = if IsZoomed(hwnd) != 0 {
        0
    } else {
        GetSystemMetricsForDpi(SM_CYSIZEFRAME, dpi) + GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi)
    };
    GetSystemMetricsForDpi(SM_CYCAPTION, dpi) + border
}

/// Extends the frame into `hwnd` by the caption's height, as it is now, and records it.
///
/// Maximized, the line is the caption alone: extended by the border too, the system centred
/// its buttons in a line taller than the one on the screen, and they sat lower than the
/// words beside them (milestone 643).
unsafe fn extend_frame(hwnd: HWND) {
    let height = caption_height(hwnd);
    LINE.with(|line| line.borrow_mut().height = height);
    let margins = MARGINS {
        cxLeftWidth: 0,
        cxRightWidth: 0,
        cyTopHeight: height,
        cyBottomHeight: 0,
    };
    DwmExtendFrameIntoClientArea(hwnd, &margins);
}

/// Shares `hwnd`'s title bar line with its content.
///
/// # Safety
///
/// `hwnd` must be a live top-level window owned by this thread.
pub(crate) unsafe fn enable(hwnd: HWND) {
    extend_frame(hwnd);
    SetWindowSubclass(hwnd, Some(subclass), SUBCLASS, 0);
    frame_changed(hwnd);
    // The window's own surface, under a see-through frame, is painted black: see
    // `WM_ERASEBKGND`.
    InvalidateRect(hwnd, std::ptr::null(), 1);
}

/// **Asks the system to paint its caption's backdrop over the frame extended into `hwnd`**,
/// for a light or a `dark` window — on Windows 11 the wallpaper-tinted one (Mica) — and
/// says whether it took it (milestone 643). Where the application leaves the title bar's
/// line transparent, that backdrop shows, and the system's own buttons over it.
///
/// Without it the extended frame is black. Windows 11 22H2 and later take it as the main
/// window's backdrop; the first Windows 11 by an attribute of its own; Windows 10 not at
/// all, and the application then paints the line itself.
///
/// # Safety
///
/// As [`enable`].
pub(crate) unsafe fn system_backdrop(hwnd: HWND, dark: bool) -> bool {
    if let Some((was, took)) = LINE.with(|line| line.borrow().backdrop) {
        if was == dark {
            return took;
        }
    }
    let set = |attribute: i32, value: i32| {
        DwmSetWindowAttribute(
            hwnd,
            attribute as u32,
            &value as *const i32 as *const core::ffi::c_void,
            std::mem::size_of::<i32>() as u32,
        ) == 0
    };
    // Light or dark as the window is, which the backdrop and the buttons follow — what the
    // reference's runner asks of the system too.
    set(DWMWA_USE_IMMERSIVE_DARK_MODE, i32::from(dark));
    /// The first Windows 11's own attribute for the same backdrop, before it had a name.
    const DWMWA_MICA_EFFECT: i32 = 1029;
    let took = set(DWMWA_SYSTEMBACKDROP_TYPE, DWMSBT_MAINWINDOW) || set(DWMWA_MICA_EFFECT, 1);
    LINE.with(|line| line.borrow_mut().backdrop = Some((dark, took)));
    took
}

/// Gives `hwnd`'s title bar line back to the system.
///
/// # Safety
///
/// As [`enable`].
pub(crate) unsafe fn disable(hwnd: HWND) {
    RemoveWindowSubclass(hwnd, Some(subclass), SUBCLASS);
    // The backdrop asked for the line goes with it.
    if LINE.with(|line| line.borrow().backdrop.is_some_and(|(_, took)| took)) {
        let auto = DWMSBT_AUTO;
        DwmSetWindowAttribute(
            hwnd,
            DWMWA_SYSTEMBACKDROP_TYPE as u32,
            &auto as *const i32 as *const core::ffi::c_void,
            std::mem::size_of::<i32>() as u32,
        );
    }
    let margins = MARGINS {
        cxLeftWidth: 0,
        cxRightWidth: 0,
        cyTopHeight: 0,
        cyBottomHeight: 0,
    };
    DwmExtendFrameIntoClientArea(hwnd, &margins);
    LINE.with(|line| *line.borrow_mut() = Line::default());
    frame_changed(hwnd);
}

unsafe fn frame_changed(hwnd: HWND) {
    SetWindowPos(
        hwnd,
        std::ptr::null_mut(),
        0,
        0,
        0,
        0,
        SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER,
    );
}

/// The line's height, in physical pixels.
#[inline(never)]
pub(crate) fn height() -> i32 {
    LINE.with(|line| line.borrow().height)
}

/// Where the compositor keeps its three buttons, in client pixels.
///
/// # Safety
///
/// As [`enable`].
pub(crate) unsafe fn buttons(hwnd: HWND) -> Option<RECT> {
    let mut bounds = RECT {
        left: 0,
        top: 0,
        right: 0,
        bottom: 0,
    };
    let got = DwmGetWindowAttribute(
        hwnd,
        DWMWA_CAPTION_BUTTON_BOUNDS as u32,
        &mut bounds as *mut RECT as *mut core::ffi::c_void,
        std::mem::size_of::<RECT>() as u32,
    );
    if got != 0 || bounds.right <= bounds.left {
        return None;
    }
    // The bounds are the window's; the client starts past its left border.
    let mut window = RECT {
        left: 0,
        top: 0,
        right: 0,
        bottom: 0,
    };
    GetWindowRect(hwnd, &mut window);
    let mut origin = POINT { x: 0, y: 0 };
    ClientToScreen(hwnd, &mut origin);
    let (dx, dy) = (origin.x - window.left, origin.y - window.top);
    Some(RECT {
        left: bounds.left - dx,
        top: (bounds.top - dy).max(0),
        right: bounds.right - dx,
        bottom: bounds.bottom - dy,
    })
}

/// What the application has on the line, and where the window's icon is, in client
/// pixels: told every frame.
#[inline(never)]
pub(crate) fn publish(interactive: Vec<RECT>, icon: Option<RECT>) {
    LINE.with(|line| {
        let mut line = line.borrow_mut();
        line.interactive = interactive;
        line.icon = icon;
    });
}

/// The compositor's button under the pointer and the one pressed, as hit-test codes (`0`
/// for none).
#[inline(never)]
pub(crate) fn buttons_state() -> (u32, u32) {
    LINE.with(|line| {
        let line = line.borrow();
        (line.hovered, line.pressed)
    })
}

/// Records a new hover or press and asks for a frame if it changed.
unsafe fn set_state(hwnd: HWND, hovered: Option<u32>, pressed: Option<u32>) {
    let changed = LINE.with(|line| {
        let mut line = line.borrow_mut();
        let before = (line.hovered, line.pressed);
        if let Some(h) = hovered {
            line.hovered = h;
        }
        if let Some(p) = pressed {
            line.pressed = p;
        }
        before != (line.hovered, line.pressed)
    });
    if changed {
        InvalidateRect(hwnd, std::ptr::null(), 0);
        WAKE.with(|slot| {
            if let Some(wake) = slot.borrow().as_ref() {
                wake();
            }
        });
    }
}

/// **The compositor's button under `at`** (client pixels), by the bounds it reports for
/// them: minimize, maximize and close, a third each.
///
/// The compositor answers the hit test for its buttons itself (`DwmDefWindowProc`) — except
/// when the window is maximized: then it reports the buttons where they are and answers
/// nothing there, and a press on one was taken for the caption, which moves the window and
/// does nothing else. This answers in its place, from the same bounds.
unsafe fn button_at(hwnd: HWND, at: &POINT) -> Option<u32> {
    let bounds = buttons(hwnd)?;
    if !contains(&bounds, at) {
        return None;
    }
    let third = (bounds.right - bounds.left).max(1) as f32 / 3.0;
    Some(match ((at.x - bounds.left) as f32 / third) as i32 {
        0 => HTMINBUTTON,
        1 => HTMAXBUTTON,
        _ => HTCLOSE,
    })
}

/// What the system does for a release on button `code`.
unsafe fn command_for(hwnd: HWND, code: u32) -> u32 {
    match code {
        HTMINBUTTON => SC_MINIMIZE,
        HTMAXBUTTON if IsZoomed(hwnd) != 0 => SC_RESTORE,
        HTMAXBUTTON => SC_MAXIMIZE,
        _ => SC_CLOSE,
    }
}

fn is_button(code: u32) -> bool {
    matches!(code, HTMINBUTTON | HTMAXBUTTON | HTCLOSE)
}

#[inline(never)]
fn contains(r: &RECT, at: &POINT) -> bool {
    at.x >= r.left && at.x < r.right && at.y >= r.top && at.y < r.bottom
}

unsafe extern "system" fn subclass(
    hwnd: HWND,
    msg: u32,
    wparam: WPARAM,
    lparam: LPARAM,
    _id: usize,
    _data: usize,
) -> LRESULT {
    // The button a press began on, before the release below clears it.
    let pressed_before = LINE.with(|line| line.borrow().pressed);
    match msg {
        WM_NCLBUTTONDOWN if is_button(wparam as u32) => {
            set_state(hwnd, None, Some(wparam as u32));
        }
        WM_NCLBUTTONUP | WM_LBUTTONUP => set_state(hwnd, None, Some(0)),
        WM_NCMOUSELEAVE => set_state(hwnd, Some(0), Some(0)),
        // Maximized or restored: the line is as tall as the caption on the screen.
        WM_SIZE => extend_frame(hwnd),
        // The person changed the colours, the accent's place, the contrast or the theme:
        // the caption is read again, and a frame asked for (milestone 642).
        WM_SETTINGCHANGE | WM_DWMCOLORIZATIONCOLORCHANGED | WM_THEMECHANGED => {
            LINE.with(|line| line.borrow_mut().caption = None);
            WAKE.with(|slot| {
                if let Some(wake) = slot.borrow().as_ref() {
                    wake();
                }
            });
        }
        _ => {}
    }
    // The compositor answers for its own buttons first.
    let mut answered: LRESULT = 0;
    if DwmDefWindowProc(hwnd, msg, wparam, lparam, &mut answered) != 0 {
        if msg == WM_NCHITTEST {
            let code = answered as u32;
            set_state(hwnd, Some(if is_button(code) { code } else { 0 }), None);
            if is_button(code) {
                // To hear the pointer leave the line.
                let mut track = TRACKMOUSEEVENT {
                    cbSize: std::mem::size_of::<TRACKMOUSEEVENT>() as u32,
                    dwFlags: TME_LEAVE | TME_NONCLIENT,
                    hwndTrack: hwnd,
                    dwHoverTime: 0,
                };
                TrackMouseEvent(&mut track);
            }
        }
        return answered;
    }
    match msg {
        // A button the compositor did not answer for (a maximized window): the press is
        // shown, and the release on the same button is the system's command, as the
        // compositor would have sent it. Left to the default, a press would draw the old
        // caption's button over the content.
        WM_NCLBUTTONDOWN if is_button(wparam as u32) => 0,
        WM_NCLBUTTONUP if is_button(wparam as u32) => {
            if pressed_before == wparam as u32 {
                PostMessageW(
                    hwnd,
                    WM_SYSCOMMAND,
                    command_for(hwnd, wparam as u32) as WPARAM,
                    lparam,
                );
            }
            0
        }
        // **The window's own surface is black** (milestone 643). It is the surface the
        // system keeps for what is drawn with its older drawing calls, and it lies under a
        // see-through frame: where the frame leaves the title bar's line transparent, it
        // shows — white where the window last was, black where it grew. Over the frame the
        // system extended into the window, black is what lets the frame show: its
        // backdrop and its buttons ("Custom Window Frame Using DWM"). Elsewhere the frame
        // covers it.
        WM_ERASEBKGND => {
            let mut client = RECT {
                left: 0,
                top: 0,
                right: 0,
                bottom: 0,
            };
            GetClientRect(hwnd, &mut client);
            FillRect(wparam as HDC, &client, GetStockObject(BLACK_BRUSH) as _);
            1
        }
        WM_NCCALCSIZE if wparam != 0 => {
            let params = &mut *(lparam as *mut NCCALCSIZE_PARAMS);
            // The window's new rectangle, as proposed: what the client is cut from.
            let proposed = params.rgrc[0];
            let result = DefSubclassProc(hwnd, msg, wparam, lparam);
            // The caption goes to the client; the sides and the bottom keep their borders.
            // Maximized, the window hangs past the screen by its frame, so the client starts
            // that far down — the frame being how far the client's left edge is from the
            // proposed window's. Measured from the window's rectangle instead, which is still
            // the restored one while this message is handled, it came out negative, the
            // client started off the screen, and the top of the line with it (milestone 643).
            params.rgrc[0].top = proposed.top;
            if IsZoomed(hwnd) != 0 {
                params.rgrc[0].top += (params.rgrc[0].left - proposed.left).max(0);
            }
            result
        }
        WM_NCHITTEST => {
            set_state(hwnd, Some(0), None);
            let hit = DefSubclassProc(hwnd, msg, wparam, lparam);
            if hit != HTCLIENT as LRESULT {
                return hit;
            }
            let mut at = POINT {
                x: (lparam & 0xffff) as i16 as i32,
                y: ((lparam >> 16) & 0xffff) as i16 as i32,
            };
            ScreenToClient(hwnd, &mut at);
            if at.y < RESIZE_EDGE && IsZoomed(hwnd) == 0 {
                return HTTOP as LRESULT;
            }
            // The compositor's buttons, where it did not answer for them itself.
            if let Some(code) = button_at(hwnd, &at) {
                set_state(hwnd, Some(code), None);
                let mut track = TRACKMOUSEEVENT {
                    cbSize: std::mem::size_of::<TRACKMOUSEEVENT>() as u32,
                    dwFlags: TME_LEAVE | TME_NONCLIENT,
                    hwndTrack: hwnd,
                    dwHoverTime: 0,
                };
                TrackMouseEvent(&mut track);
                return code as LRESULT;
            }
            LINE.with(|line| {
                let line = line.borrow();
                if at.y >= line.height {
                    hit
                } else if line.icon.as_ref().is_some_and(|r| contains(r, &at)) {
                    HTSYSMENU as LRESULT
                } else if line.interactive.iter().any(|r| contains(r, &at)) {
                    hit
                } else {
                    HTCAPTION as LRESULT
                }
            })
        }
        _ => DefSubclassProc(hwnd, msg, wparam, lparam),
    }
}