@@ -17,85 +17,27 @@ public partial class PuTTYControl : UserControlBase, IDragablzTabItem, IEmbedded
1717{
1818 #region Events
1919
20- private bool _isDpiChanging ;
21-
2220 private void WindowGrid_SizeChanged ( object sender , SizeChangedEventArgs e )
2321 {
2422 ResizeEmbeddedWindow ( ) ;
2523 }
2624
2725 private async void WindowsFormsHost_DpiChanged ( object sender , DpiChangedEventArgs e )
2826 {
29- if ( ! IsConnected || _appWin == IntPtr . Zero || _isDpiChanging )
30- return ;
31-
32- await TriggerDpiUpdateAsync ( ) ;
33- }
27+ ResizeEmbeddedWindow ( ) ;
3428
35- /// <summary>
36- /// Detaches PuTTY's window, places it visibly on the current monitor so Windows
37- /// delivers WM_DPICHANGED natively to PuTTY's message loop, then re-embeds it.
38- /// Called both on initial connect (if DPIs differ) and on every DPI change.
39- /// </summary>
40- private async Task TriggerDpiUpdateAsync ( )
41- {
42- if ( _isDpiChanging || ! IsConnected || _appWin == IntPtr . Zero )
29+ if ( ! IsConnected || _process == null || _process . HasExited )
4330 return ;
4431
45- _isDpiChanging = true ;
46-
47- try
48- {
49- // Use MonitorFromWindow for reliable physical-pixel coordinates — WinForms's
50- // Screen.FromHandle can return DPI-virtualized (wrong) coordinates on
51- // PerMonitorV2 setups.
52- var bounds = NativeMethods . GetMonitorBoundsForWindow ( WindowHost . Handle ) ;
53- int cx = bounds . left + ( bounds . right - bounds . left ) / 2 ;
54- int cy = bounds . top + ( bounds . bottom - bounds . top ) / 2 ;
55-
56- // Temporarily restore WS_POPUP so that after SetParent(null) Windows treats
57- // the window as a proper top-level popup. Without WS_POPUP (stripped during
58- // embedding), the window has no recognised top-level style and Windows
59- // skips sending WM_DPICHANGED to it entirely.
60- // Also clear WS_CHILD if SetParent happened to set it.
61- long style = NativeMethods . GetWindowLong ( _appWin , NativeMethods . GWL_STYLE ) ;
62- style &= ~ NativeMethods . WS_CHILD ;
63- style |= NativeMethods . WS_POPUP ;
64- NativeMethods . SetWindowLongPtr ( _appWin , NativeMethods . GWL_STYLE , new IntPtr ( style ) ) ;
65-
66- NativeMethods . SetParent ( _appWin , IntPtr . Zero ) ;
67-
68- // Place as an 800×600 window at the centre of the target monitor, behind
69- // all other windows (HWND_BOTTOM). SWP_SHOWWINDOW makes it visible so
70- // Windows detects the monitor's DPI and delivers WM_DPICHANGED natively.
71- // A 1×1 window is below the threshold Windows uses for DPI detection.
72- NativeMethods . SetWindowPos ( _appWin , NativeMethods . HWND_BOTTOM , cx , cy , 800 , 600 ,
73- NativeMethods . SWP_NOACTIVATE | NativeMethods . SWP_SHOWWINDOW ) ;
74-
75- // Give PuTTY's message loop time to dequeue and handle WM_DPICHANGED.
76- await Task . Delay ( 300 ) ;
77-
78- if ( ! IsConnected || _appWin == IntPtr . Zero )
79- return ;
80-
81- NativeMethods . ShowWindow ( _appWin , NativeMethods . WindowShowStyle . Hide ) ;
82- NativeMethods . SetParent ( _appWin , WindowHost . Handle ) ;
83-
84- // Remove WS_POPUP after re-embedding; keeping it on a child window can
85- // cause painting to overflow the parent panel (see issue #167).
86- style = NativeMethods . GetWindowLong ( _appWin , NativeMethods . GWL_STYLE ) ;
87- style &= ~ NativeMethods . WS_POPUP ;
88- NativeMethods . SetWindowLongPtr ( _appWin , NativeMethods . GWL_STYLE , new IntPtr ( style ) ) ;
89-
90- ResizeEmbeddedWindow ( ) ;
91- NativeMethods . ShowWindow ( _appWin , NativeMethods . WindowShowStyle . ShowNoActivate ) ;
92- }
93- finally
94- {
95- _isDpiChanging = false ;
96- }
32+ // PuTTY is a GUI application (not console-based), so the Console Font API
33+ // (AttachConsole/SetCurrentConsoleFontEx) does not apply. Instead, send
34+ // WM_DPICHANGED directly to the PuTTY window so it can rescale its fonts
35+ // and layout internally — bypassing the cross-process delivery limitation.
36+ NativeMethods . TrySendDpiChangedMessage (
37+ _appWin ,
38+ e . OldDpi . PixelsPerInchX ,
39+ e . NewDpi . PixelsPerInchX ) ;
9740 }
98-
9941 #endregion
10042
10143 #region Variables
@@ -252,8 +194,9 @@ private async Task Connect()
252194
253195 if ( ! _process . HasExited )
254196 {
255- // Capture PuTTY's DPI before embedding. If it started on a
256- // different monitor than ours, we correct below via detach/reattach.
197+ // Capture PuTTY's window DPI before embedding. The process
198+ // might have started on a different monitor than ours, so its font
199+ // may be scaled for a different DPI. We correct this after embedding.
257200 var initialWindowDpi = NativeMethods . GetDpiForWindow ( _appWin ) ;
258201
259202 // Enable mixed-DPI hosting on this thread before SetParent so that
@@ -267,11 +210,9 @@ private async Task Connect()
267210 // Show window before set style and resize
268211 NativeMethods . ShowWindow ( _appWin , NativeMethods . WindowShowStyle . Maximize ) ;
269212
270- // Remove border etc.
213+ // Remove border etc.
271214 long style = ( int ) NativeMethods . GetWindowLong ( _appWin , NativeMethods . GWL_STYLE ) ;
272- style &= ~ ( NativeMethods . WS_CAPTION | NativeMethods . WS_POPUP |
273- NativeMethods
274- . WS_THICKFRAME ) ; // NativeMethods.WS_POPUP --> Overflow? (https://github.com/BornToBeRoot/NETworkManager/issues/167)
215+ style &= ~ ( NativeMethods . WS_CAPTION | NativeMethods . WS_POPUP | NativeMethods . WS_THICKFRAME ) ;
275216 NativeMethods . SetWindowLongPtr ( _appWin , NativeMethods . GWL_STYLE , new IntPtr ( style ) ) ;
276217
277218 IsConnected = true ;
@@ -282,11 +223,13 @@ private async Task Connect()
282223
283224 ResizeEmbeddedWindow ( ) ;
284225
285- // If PuTTY started on a different DPI monitor than ours, trigger
286- // a DPI correction now so its font matches the current monitor.
226+ // If PuTTY started at a different DPI than our panel (e.g. it
227+ // spawned on a secondary monitor with a different scale factor),
228+ // send WM_DPICHANGED so PuTTY rescales its fonts to match.
287229 var currentPanelDpi = NativeMethods . GetDpiForWindow ( WindowHost . Handle ) ;
288230 if ( initialWindowDpi != currentPanelDpi )
289- await TriggerDpiUpdateAsync ( ) ;
231+ NativeMethods . TrySendDpiChangedMessage ( _appWin ,
232+ initialWindowDpi , currentPanelDpi ) ;
290233 }
291234 }
292235 }
0 commit comments