1using System; 2using System.Collections.Generic; 3using System.Drawing; 4using System.Drawing.Drawing2D; 5using System.Drawing.Imaging; 6using System.Drawing.Text; 7using System.IO; 8using System.Linq; 9using System.Runtime.InteropServices; 10using System.Windows.Forms; 11using WindowCapture.App; 12using WindowCapture.Detection; 13using WindowCapture.Effects; 14using WindowCapture.Helpers; 15using WindowCapture.Integration; 16using WindowCapture.Models; 17using WindowCapture.Native; 18 19namespace WindowCapture.UI 20{ 21 public partial class EditorForm 22 { 23 // ===== Window chrome (settings, dispose, blur, WndProc) ===== 24 25 private void ShowSettings() 26 { 27 using (var dialog = new SettingsDialog()) 28 { 29 dialog.ShowDialog(this); 30 } 31 CreateBlurredBitmap(); 32 EnableBlurBackground(); 33 ApplyRoundedCorners(); 34 canvas.Invalidate(); 35 Invalidate(); 36 } 37 38 protected override void OnFormClosed(FormClosedEventArgs e) 39 { 40 IsOpen = false; 41 base.OnFormClosed(e); 42 } 43 44 protected override void Dispose(bool disposing) 45 { 46 if (disposing) 47 { 48 IsOpen = false; 49 D2DRenderer.Dispose(); 50 if (zoomTimer != null) { zoomTimer.Stop(); zoomTimer.Dispose(); } 51 if (glassLeaveTimer != null) { glassLeaveTimer.Stop(); glassLeaveTimer.Dispose(); } 52 if (zoomFadeTimer != null) { zoomFadeTimer.Stop(); zoomFadeTimer.Dispose(); } 53 if (fileNameFadeTimer != null) { fileNameFadeTimer.Stop(); fileNameFadeTimer.Dispose(); } 54 if (videoUpdateTimer != null) { videoUpdateTimer.Stop(); videoUpdateTimer.Dispose(); } 55 if (videoControlsFadeTimer != null) { videoControlsFadeTimer.Stop(); videoControlsFadeTimer.Dispose(); } 56 if (spaceHoldTimer != null) { spaceHoldTimer.Stop(); spaceHoldTimer.Dispose(); } 57 if (delayedNameTimer != null) { delayedNameTimer.Stop(); delayedNameTimer.Dispose(); } 58 if (wordPanelWindow != null && !wordPanelWindow.IsDisposed) wordPanelWindow.Close(); 59 // Stop GIF animation 60 if (canvas != null) canvas.StopGifAnimation(); 61 if (gifImage != null) { gifImage.Dispose(); gifImage = null; } 62 // Stop video playback 63 DsCleanup(); 64 if (videoOverlayForm != null) { videoOverlayForm.Close(); videoOverlayForm.Dispose(); videoOverlayForm = null; } 65 if (videoOverlay != null) videoOverlay.Dispose(); 66 if (dsPanel != null) dsPanel.Dispose(); 67 if (videoBrowser != null) videoBrowser.Dispose(); 68 // Dispose image cache 69 if (imageCache != null) 70 { 71 lock (imageCache) { foreach (var bmp in imageCache.Values) bmp.Dispose(); imageCache.Clear(); } 72 } 73 if (captured != null) captured.Dispose(); 74 if (blurredBitmap != null) blurredBitmap.Dispose(); 75 if (clipPath != null) clipPath.Dispose(); 76 if (canvas != null) canvas.Dispose(); 77 // Release Word COM references acquired during this session so Word.exe doesn't linger. 78 try { WordIntegration.Cleanup(); } catch { } 79 } 80 base.Dispose(disposing); 81 } 82 83 private bool isWin10RoundedCorners; 84 85 public void ApplyRoundedCorners() 86 { 87 int cornerRadius = Settings.CornerRadius; 88 if (cornerRadius <= 0) 89 { 90 Region = null; 91 isWin10RoundedCorners = false; 92 return; 93 } 94 // Try Windows 11 native rounded corners first 95 if (!WinApi.TryEnableRoundedCorners(Handle, WinApi.DWMWCP_ROUND)) 96 { 97 // Windows 10: don't use Region (DWM blur ignores Region and extends 98 // to full rectangular window). Instead, we handle corners visually 99 // in ScrollContainer_Paint by painting opaque tint in corner areas 100 // and semi-transparent tint in the rounded interior. 101 isWin10RoundedCorners = true; 102 Region = null; 103 } 104 else 105 { 106 isWin10RoundedCorners = false; 107 } 108 } 109 110 private void EnableBlurBackground() 111 { 112 BlurHelper.Apply(Handle); 113 } 114 115 protected override void OnPaintBackground(PaintEventArgs e) 116 { 117 // Clear to fully transparent (alpha=0) so DWM blur composition shows through 118 e.Graphics.Clear(Color.FromArgb(0, 0, 0, 0)); 119 } 120 121 protected override void OnPaint(PaintEventArgs e) 122 { 123 base.OnPaint(e); 124 } 125 126 // Enable window resizing for borderless form 127 protected override CreateParams CreateParams 128 { 129 get 130 { 131 CreateParams cp = base.CreateParams; 132 cp.Style |= 0x00040000 | 0x02000000; // WS_SIZEBOX | WS_CLIPCHILDREN 133 return cp; 134 } 135 } 136 137 // Constants for window resize with WndProc 138 private const int RESIZE_BORDER = 8; 139 private const int WM_NCACTIVATE = 0x0086; 140 private const int WM_NCCALCSIZE = 0x0083; 141 private const int WM_NCHITTEST = 0x0084; 142 private const int HTLEFT = 10; 143 private const int HTRIGHT = 11; 144 private const int HTTOP = 12; 145 private const int HTTOPLEFT = 13; 146 private const int HTTOPRIGHT = 14; 147 private const int HTBOTTOM = 15; 148 private const int HTBOTTOMLEFT = 16; 149 private const int HTBOTTOMRIGHT = 17; 150 151 private const int WM_ERASEBKGND = 0x0014; 152 private const int WM_GETMINMAXINFO = 0x0024; 153 154 [StructLayout(LayoutKind.Sequential)] 155 private struct MINMAXINFO 156 { 157 public Point ptReserved; 158 public Point ptMaxSize; 159 public Point ptMaxPosition; 160 public Point ptMinTrackSize; 161 public Point ptMaxTrackSize; 162 } 163 164 protected override void WndProc(ref Message m) 165 { 166 // Constrain maximized size to current monitor's working area 167 if (m.Msg == WM_GETMINMAXINFO) 168 { 169 try 170 { 171 var info = (MINMAXINFO)Marshal.PtrToStructure(m.LParam, typeof(MINMAXINFO)); 172 var screen = Screen.FromHandle(Handle); 173 var wa = screen.WorkingArea; 174 info.ptMaxPosition = new Point(wa.X - screen.Bounds.X, wa.Y - screen.Bounds.Y); 175 info.ptMaxSize = new Point(wa.Width, wa.Height); 176 Marshal.StructureToPtr(info, m.LParam, false); 177 } 178 catch { } 179 base.WndProc(ref m); 180 return; 181 } 182 183 // Suppress background erase to eliminate flicker during resize 184 if (m.Msg == WM_ERASEBKGND) 185 { 186 m.Result = (IntPtr)1; 187 return; 188 } 189 190 // Prevent white border artifacts on focus change 191 if (m.Msg == WM_NCACTIVATE) 192 { 193 m.Result = (IntPtr)1; 194 return; 195 } 196 197 // Force zero non-client area - removes the DWM-drawn 1px light border 198 // on Windows 10 when WS_SIZEBOX is set with FormBorderStyle.None 199 if (m.Msg == WM_NCCALCSIZE && m.WParam != IntPtr.Zero) 200 { 201 m.Result = IntPtr.Zero; 202 return; 203 } 204 205 if (m.Msg == WM_NCHITTEST) 206 { 207 // Handle completely ourselves - don't call base for borderless form 208 // Extract screen coordinates (signed 16-bit values) 209 int x = (short)(m.LParam.ToInt64() & 0xFFFF); 210 int y = (short)((m.LParam.ToInt64() >> 16) & 0xFFFF); 211 Point pt = PointToClient(new Point(x, y)); 212 213 bool left = pt.X < RESIZE_BORDER; 214 bool right = pt.X > ClientSize.Width - RESIZE_BORDER; 215 bool top = pt.Y < RESIZE_BORDER; 216 bool bottom = pt.Y > ClientSize.Height - RESIZE_BORDER; 217 218 if (top && left) m.Result = (IntPtr)HTTOPLEFT; 219 else if (top && right) m.Result = (IntPtr)HTTOPRIGHT; 220 else if (bottom && left) m.Result = (IntPtr)HTBOTTOMLEFT; 221 else if (bottom && right) m.Result = (IntPtr)HTBOTTOMRIGHT; 222 else if (left) m.Result = (IntPtr)HTLEFT; 223 else if (right) m.Result = (IntPtr)HTRIGHT; 224 else if (top) m.Result = (IntPtr)HTTOP; 225 else if (bottom) m.Result = (IntPtr)HTBOTTOM; 226 else m.Result = (IntPtr)1; // HTCLIENT 227 return; 228 } 229 base.WndProc(ref m); 230 } 231 232 // Public method to refresh blurred bitmap after effects 233 public void RefreshBlurredBitmap() 234 { 235 CreateBlurredBitmap(); 236 } 237 } 238}