@@ -606,6 +606,10 @@ static int env_flag_default_true(const char *name) {
606606 return env_flag_enabled (name );
607607}
608608
609+ static int env_flag_default_false (const char * name ) {
610+ return env_flag_enabled (name );
611+ }
612+
609613static const char * apple_hp_getenv_first (const char * a , const char * b ) {
610614 const char * s = NULL ;
611615 if (a ) {
@@ -907,10 +911,11 @@ static uint32_t apple_hp_visible_content_pad(void) {
907911}
908912
909913static int apple_hp_visible_crop_enabled (void ) {
910- /* Dynamic-resolution backing surfaces can carry trailing black padding
911- * after the server downsizes the virtual display. Crop detection is
912- * therefore on by default for the HP live-view path, with an env opt-out. */
913- return env_flag_default_true ("VNC_APPLE_HP_VISIBLE_CROP" );
914+ /* HP backing/layout sizes are authoritative for rendering. Heuristic crop
915+ * detection can misclassify legitimate dark regions during startup and after
916+ * layout changes, which distorts the live-view aspect. Keep cropping opt-in
917+ * for debugging and edge cases instead of enabling it by default. */
918+ return env_flag_default_false ("VNC_APPLE_HP_VISIBLE_CROP" );
914919}
915920
916921static int apple_hp_dynamic_target_materially_diff (uint16_t a_w , uint16_t a_h ,
@@ -1210,8 +1215,37 @@ static int send_set_display_message(rfbClient *client) {
12101215 return send_blob (client , & msg , sizeof (msg ), "apple-hp post-rekey hello" );
12111216}
12121217
1218+ static double apple_hp_runtime_scale_factor (void ) {
1219+ #if defined(APPLEHPDEBUG_HAS_SDL )
1220+ int window_w = 0 ;
1221+ int window_h = 0 ;
1222+ int output_w = 0 ;
1223+ int output_h = 0 ;
1224+ double scale_x ;
1225+ double scale_y ;
1226+ double scale ;
1227+
1228+ if (!g_runtime .live_view || !g_live .window || !g_live .renderer ) return 1.0 ;
1229+ SDL_GetWindowSize (g_live .window , & window_w , & window_h );
1230+ if (window_w <= 0 || window_h <= 0 ) return 1.0 ;
1231+ if (SDL_GetRendererOutputSize (g_live .renderer , & output_w , & output_h ) < 0 ) return 1.0 ;
1232+ if (output_w <= 0 || output_h <= 0 ) return 1.0 ;
1233+ scale_x = (double )output_w / (double )window_w ;
1234+ scale_y = (double )output_h / (double )window_h ;
1235+ scale = ((scale_x > scale_y ) ? scale_x : scale_y ) * 0.5 ;
1236+ if (scale > 0.95 && scale < 1.05 ) scale = 1.0 ;
1237+ if (scale < 0.5 ) scale = 0.5 ;
1238+ if (scale > 2.0 ) scale = 2.0 ;
1239+ return scale ;
1240+ #else
1241+ return 1.0 ;
1242+ #endif
1243+ }
1244+
12131245static int send_native_scale_factor_message (rfbClient * client ) {
1214- struct apple_hp_scale_factor_message msg = apple_hp_make_native_scale_factor_message ();
1246+ double scale = apple_hp_runtime_scale_factor ();
1247+ struct apple_hp_scale_factor_message msg = apple_hp_make_scale_factor_message (scale );
1248+ rfbClientLog ("apple-hp: using scale factor %.6f\n" , scale );
12151249 return send_blob (client , & msg , sizeof (msg ), "apple-hp scale factor" );
12161250}
12171251
@@ -1964,34 +1998,52 @@ static int live_view_runtime_display_size(rfbClient *client, uint16_t *out_w, ui
19641998#if defined(APPLEHPDEBUG_HAS_SDL )
19651999 int window_w = 0 ;
19662000 int window_h = 0 ;
2001+ int output_w = 0 ;
2002+ int output_h = 0 ;
19672003 int display_index = 0 ;
19682004 int w = 0 ;
19692005 int h = 0 ;
19702006 Uint32 window_flags = 0 ;
19712007 SDL_Rect bounds ;
19722008
19732009 if (!client || !out_w || !out_h ) return 0 ;
1974- if (g_live .runtime_size_valid &&
1975- g_live .cached_runtime_w > 0 &&
1976- g_live .cached_runtime_h > 0 ) {
1977- * out_w = g_live .cached_runtime_w ;
1978- * out_h = g_live .cached_runtime_h ;
1979- return 1 ;
1980- }
19812010 memset (& bounds , 0 , sizeof (bounds ));
19822011 if (g_live .window ) {
19832012 window_flags = SDL_GetWindowFlags (g_live .window );
19842013 display_index = SDL_GetWindowDisplayIndex (g_live .window );
19852014 if (display_index < 0 ) display_index = 0 ;
19862015 SDL_GetWindowSize (g_live .window , & window_w , & window_h );
19872016 }
2017+ if (g_live .renderer ) {
2018+ if (SDL_GetRendererOutputSize (g_live .renderer , & output_w , & output_h ) < 0 ) {
2019+ output_w = 0 ;
2020+ output_h = 0 ;
2021+ }
2022+ }
2023+ if (output_w > 0 && output_h > 0 ) {
2024+ int hidpi_scale = 1 ;
2025+ if (window_w > 0 ) {
2026+ int inferred = (output_w + (window_w / 2 )) / window_w ;
2027+ if (inferred > hidpi_scale ) hidpi_scale = inferred ;
2028+ }
2029+ if (window_h > 0 ) {
2030+ int inferred = (output_h + (window_h / 2 )) / window_h ;
2031+ if (inferred > hidpi_scale ) hidpi_scale = inferred ;
2032+ }
2033+ if (hidpi_scale < 1 ) hidpi_scale = 1 ;
2034+ w = output_w / hidpi_scale ;
2035+ h = output_h / hidpi_scale ;
2036+ }
19882037 if ((window_flags & SDL_WINDOW_FULLSCREEN_DESKTOP ) != 0 ||
19892038 (window_flags & SDL_WINDOW_FULLSCREEN ) != 0 ) {
19902039 /* Dynamic-resolution requests should follow the logical fullscreen window
19912040 * size in points, not the HiDPI drawable size in pixels. On macOS the
19922041 * actual fullscreen window size tracks the drawable area more reliably
19932042 * than display bounds, which can include extra space and produce bars. */
1994- if (window_w > 0 && window_h > 0 ) {
2043+ if (w > 0 && h > 0 ) {
2044+ /* Prefer normalized drawable size when available; it reflects the
2045+ * actual fullscreen content area more accurately than window chrome. */
2046+ } else if (window_w > 0 && window_h > 0 ) {
19952047 w = window_w ;
19962048 h = window_h ;
19972049 } else if (SDL_GetDisplayUsableBounds (display_index , & bounds ) == 0 &&
@@ -2235,17 +2287,13 @@ static int compute_live_view_geometry(rfbClient *client, struct apple_hp_live_vi
22352287 geom -> src .h = client -> height > 0 ? client -> height : 1 ;
22362288 }
22372289 if (g_runtime .apple_hp_mode && geom -> src .w > 0 && geom -> src .h > 0 ) {
2238- if (g_hp .visible_content_valid &&
2239- geom -> src .x == g_hp .visible_content_x &&
2240- geom -> src .y == g_hp .visible_content_y &&
2241- geom -> src .w == g_hp .visible_content_w &&
2242- geom -> src .h == g_hp .visible_content_h ) {
2243- fit_src_w = geom -> src .w ;
2244- fit_src_h = geom -> src .h ;
2245- } else {
2246- fit_src_w = display_w > 0 ? display_w : geom -> src .w ;
2247- fit_src_h = display_h > 0 ? display_h : geom -> src .h ;
2248- }
2290+ /* Fit using the actual sampled backing/source rectangle. Apple HP often
2291+ * reports logical display dimensions that differ slightly from the backing
2292+ * it actually returns (for example 1616x1010 vs 3211x2007). Using the
2293+ * logical size for presentation introduces small bars and makes the source
2294+ * overlay appear larger than the destination box. */
2295+ fit_src_w = geom -> src .w ;
2296+ fit_src_h = geom -> src .h ;
22492297 fit_w = geom -> output_w ;
22502298 fit_h = (int )(((long long )fit_w * (long long )fit_src_h ) / (long long )fit_src_w );
22512299 if (fit_h > geom -> output_h ) {
@@ -3229,6 +3277,21 @@ static rfbBool handle_hp_probe_encoding(rfbClient *client, rfbFramebufferUpdateR
32293277 }
32303278 g_hp .initial_full_refresh_done = 0 ;
32313279 g_hp .initial_full_refresh_retries = 0 ;
3280+ if (!g_live .window_user_sized ) {
3281+ uint16_t startup_target_w = 0 ;
3282+ uint16_t startup_target_h = 0 ;
3283+ if (apple_hp_compute_dynamic_resolution_target (client , & startup_target_w , & startup_target_h ) &&
3284+ startup_target_w != 0 && startup_target_h != 0 &&
3285+ apple_hp_dynamic_target_materially_diff (startup_target_w , startup_target_h ,
3286+ scaled_w , scaled_h )) {
3287+ g_live .last_runtime_w = startup_target_w ;
3288+ g_live .last_runtime_h = startup_target_h ;
3289+ if (!maybe_send_dynamic_resolution_update (client , "startup-layout" , TRUE)) {
3290+ free (payload );
3291+ return FALSE;
3292+ }
3293+ }
3294+ }
32323295 }
32333296 if (client && scaled_w != 0 && scaled_h != 0 &&
32343297 (scaled_w != prev_display_w || scaled_h != prev_display_h ||
0 commit comments