|
| 1 | +# Real-pointer check of Window::SetMaximizeButtonBounds (#70) on Windows 11: |
| 2 | +# resting the cursor on an app-drawn maximize button opens the snap layouts, |
| 3 | +# and the content window under it still receives the moves. Hover only - no |
| 4 | +# clicks, no keyboard. Build core/tests/window_maximize_button_test first. |
| 5 | +$ErrorActionPreference = 'Stop' |
| 6 | +. "$PSScriptRoot\env.ps1" |
| 7 | +. "$PSScriptRoot\winput.ps1" |
| 8 | +. "$PSScriptRoot\guiapp.ps1" |
| 9 | +Add-Type -AssemblyName System.Drawing |
| 10 | +Add-Type -AssemblyName System.Windows.Forms |
| 11 | + |
| 12 | +$exe = $env:CORE_SNAP_LAYOUT_TEST_EXE |
| 13 | +if (-not $exe) { $exe = "$RemoteScratch\core-build\tests\Debug\window_maximize_button_test.exe" } |
| 14 | +if (-not (Test-Path $exe)) { throw "Missing executable: $exe" } |
| 15 | +Start-Result "$RemoteScratch\core_window_snap_layout_test.result.txt" |
| 16 | +$build = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion').CurrentBuild |
| 17 | +$flyout = (Get-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced' -ErrorAction SilentlyContinue).EnableSnapAssistFlyout |
| 18 | +Say "Windows build $build, EnableSnapAssistFlyout=$(if ($null -eq $flyout) { 'default (on)' } else { $flyout })" |
| 19 | +Assert-Idle |
| 20 | + |
| 21 | +$info = New-Object System.Diagnostics.ProcessStartInfo $exe |
| 22 | +$info.Arguments = '--serve' |
| 23 | +$info.UseShellExecute = $false |
| 24 | +$info.CreateNoWindow = $true |
| 25 | +$info.RedirectStandardOutput = $true |
| 26 | +$proc = [System.Diagnostics.Process]::Start($info) |
| 27 | +$lines = New-Object System.Collections.Generic.List[string] |
| 28 | +$script:read = $proc.StandardOutput.ReadLineAsync() |
| 29 | +function Read-AppLines { |
| 30 | + while ($script:read.IsCompleted) { |
| 31 | + $line = $script:read.Result |
| 32 | + if ($null -eq $line) { break } |
| 33 | + $lines.Add($line) |
| 34 | + $script:read = $proc.StandardOutput.ReadLineAsync() |
| 35 | + } |
| 36 | +} |
| 37 | +# Waits while draining the app's output: a full pipe would block its message loop. |
| 38 | +function Wait-Reading([double]$Seconds) { |
| 39 | + $end = (Get-Date).AddSeconds($Seconds) |
| 40 | + do { Read-AppLines; Start-Sleep -Milliseconds 20 } while ((Get-Date) -lt $end) |
| 41 | + Read-AppLines |
| 42 | +} |
| 43 | +$app = @{ Proc = $proc; Log = $null; Stdout = $null } |
| 44 | +$rest = $null |
| 45 | +try { |
| 46 | + $deadline = (Get-Date).AddSeconds(20) |
| 47 | + do { Pause 0.1; Read-AppLines } until (@($lines | ? { $_ -like 'READY *' -or $_ -like 'FAIL*' }).Count -gt 0 -or (Get-Date) -gt $deadline) |
| 48 | + $ready = $lines | ? { $_ -like 'READY *' } | Select-Object -First 1 |
| 49 | + if (-not $ready) { throw "The app did not get ready: $($lines -join '; ')" } |
| 50 | + $b = $ready.Split(' ')[1..4] | % { [int]$_ } |
| 51 | + $center = @(($b[0] + [int]($b[2] / 2)), ($b[1] + [int]($b[3] / 2))) |
| 52 | + $win = Get-Win $app 'nativeapi snap layout' |
| 53 | + if (-not $win) { throw 'The window is missing' } |
| 54 | + Raise-AppWindows $proc.Id |
| 55 | + # Approach over the window's own content, below the title band. |
| 56 | + $approach = @(($win.Left + 120), ($win.Top + 200)) |
| 57 | + # Leave sideways along the title band, away from the flyout below the button. |
| 58 | + $rest = $null |
| 59 | + Assert-Owner $proc.Id $approach[0] $approach[1] |
| 60 | + Move-Cursor $approach 400 |
| 61 | + Wait-Reading 0.5 |
| 62 | + # The flyout opens below the button, over this window. The window under |
| 63 | + # that point tells: ours before, the shell's while the flyout is up. |
| 64 | + $probe = @($center[0], ($center[1] + [int]($b[3] * 2))) |
| 65 | + $ownerBefore = [WInput]::Owner($probe[0], $probe[1]) |
| 66 | + Assert-Owner $proc.Id $center[0] $center[1] |
| 67 | + $countBefore = $lines.Count |
| 68 | + Move-Cursor $center 500 |
| 69 | + Wait-Reading 2.0 # The flyout opens after a short rest. |
| 70 | + $ownerDuring = [WInput]::Owner($probe[0], $probe[1]) |
| 71 | + $process = (Get-Process -Id $ownerDuring -ErrorAction SilentlyContinue).ProcessName |
| 72 | + # Evidence: the screen around the button while the flyout is up. |
| 73 | + $bmp = New-Object System.Drawing.Bitmap 900, 500 |
| 74 | + $g = [System.Drawing.Graphics]::FromImage($bmp) |
| 75 | + $g.CopyFromScreen([Math]::Max(0, $center[0] - 600), [Math]::Max(0, $center[1] - 60), 0, 0, $bmp.Size) |
| 76 | + $bmp.Save("$RemoteScratch\core_window_snap_layout_test.png", [System.Drawing.Imaging.ImageFormat]::Png) |
| 77 | + $g.Dispose(); $bmp.Dispose() |
| 78 | + Check 'below the button is this window before the hover' ($ownerBefore -eq $proc.Id) "pid $ownerBefore" |
| 79 | + Check 'resting on the button opens a window of another process over it (the snap layouts)' ` |
| 80 | + ($ownerDuring -ne $proc.Id -and $ownerDuring -ne 0) "pid $ownerDuring ($process)" |
| 81 | + # Natively the content gets no move over the button (it steps aside in the |
| 82 | + # hit test), so a move it logs inside the button was forwarded. |
| 83 | + $c = ($lines | ? { $_ -like 'BUTTON_CONTENT *' } | Select-Object -First 1).Split(' ')[1..4] | % { [int]$_ } |
| 84 | + $moves = @($lines | Select-Object -Skip $countBefore | ? { $_ -like 'CONTENT WM_MOUSEMOVE*' } | ? { |
| 85 | + $f = $_.Split(' '); $x = [int]$f[2]; $y = [int]$f[3] |
| 86 | + $x -ge $c[0] -and $x -lt $c[0] + $c[2] -and $y -ge $c[1] -and $y -lt $c[1] + $c[3] |
| 87 | + }) |
| 88 | + Check 'the content window received the moves over the button' ($moves.Count -gt 0) "$($moves.Count) moves, last: $($moves | Select-Object -Last 1)" |
| 89 | + # Leave the button: the flyout closes and the content hears it. |
| 90 | + $rest = @(($center[0] - [int]($b[2] * 5)), $center[1]) |
| 91 | + Assert-Owner $proc.Id $rest[0] $rest[1] |
| 92 | + Move-Cursor $rest 400 |
| 93 | + Wait-Reading 1.5 |
| 94 | + $ownerAfter = [WInput]::Owner($probe[0], $probe[1]) |
| 95 | + Check 'the snap layouts closed after leaving' ($ownerAfter -eq $proc.Id) "pid $ownerAfter" |
| 96 | + Check 'the content window heard the pointer leave the button' (@($lines | ? { $_ -like 'CONTENT WM_MOUSELEAVE*' }).Count -gt 0) |
| 97 | + Check 'no click was sent' (@($lines | ? { $_ -like 'CONTENT WM_LBUTTON*' }).Count -eq 0) |
| 98 | +} finally { |
| 99 | + if (-not $proc.HasExited) { Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue } |
| 100 | + $lines | Set-Content "$RemoteScratch\core_window_snap_layout_test.app.log" -Encoding utf8 |
| 101 | +} |
0 commit comments