Index: dataobj/environment.cc =================================================================== --- dataobj/environment.cc (revision 10359) +++ dataobj/environment.cc (working copy) @@ -18,6 +18,7 @@ sint16 env_t::menupos = MENU_TOP; sint16 env_t::fullscreen = WINDOWED; +sint16 env_t::display_scale_percent = 100; bool env_t::reselect_closes_tool = true; sint8 env_t::pak_tile_height_step = 16; @@ -571,6 +572,10 @@ file->rdwr_short( fullscreen ); } + if( file->is_version_atleast(123, 1) ) { + file->rdwr_short(display_scale_percent); + } + // server settings are not saved, since they are server specific // and could be different on different servers on the same computers } Index: dataobj/environment.h =================================================================== --- dataobj/environment.h (revision 10359) +++ dataobj/environment.h (working copy) @@ -39,6 +39,9 @@ static sint16 fullscreen; + /// Controls size of the virtual display + static sint16 display_scale_percent; + static bool reselect_closes_tool; /// points to the current user directory for loading and saving Index: gui/display_settings.cc =================================================================== --- gui/display_settings.cc (revision 10359) +++ gui/display_settings.cc (working copy) @@ -82,18 +82,22 @@ gui_settings_t::gui_settings_t() { - set_table_layout( 1, 0 ); + set_table_layout( 3, 0 ); + // Show thememanager buttons[ IDBTN_SHOW_THEMEMANAGER ].init( button_t::roundbox_state | button_t::flexible, "Select a theme for display" ); - add_component( buttons + IDBTN_SHOW_THEMEMANAGER ); + add_component( buttons + IDBTN_SHOW_THEMEMANAGER, 3 ); // Change font buttons[ IDBTN_CHANGE_FONT ].init( button_t::roundbox_state | button_t::flexible, "Select display font" ); - add_component( buttons + IDBTN_CHANGE_FONT ); + add_component( buttons + IDBTN_CHANGE_FONT, 3 ); - // add controls to info container - add_table(2,4); - set_alignment(ALIGN_LEFT); + // screen scale number input + new_component("Screen scale: "); + screen_scale_numinp.init(dr_get_screen_scale(), 25, 400, 25, false); + screen_scale_numinp.add_listener(this); + add_component(&screen_scale_numinp); + new_component(); // position of menu new_component("Toolbar position:"); @@ -103,43 +107,42 @@ case MENU_BOTTOM: toolbar_pos.init(button_t::arrowdown, NULL); break; case MENU_RIGHT: toolbar_pos.init(button_t::arrowright, NULL); break; } - add_component(&toolbar_pos); + add_component(&toolbar_pos,2); fullscreen.init( button_t::square_state, "Fullscreen (changed after restart)" ); fullscreen.pressed = ( dr_get_fullscreen() == FULLSCREEN ); fullscreen.enable(dr_has_fullscreen()); - add_component( &fullscreen, 2 ); + add_component( &fullscreen, 3 ); borderless.init( button_t::square_state, "Borderless (disabled on fullscreen)" ); borderless.enable ( dr_get_fullscreen() != FULLSCREEN ); borderless.pressed = ( dr_get_fullscreen() == BORDERLESS ); - add_component( &borderless, 2 ); + add_component( &borderless, 3 ); reselect_closes_tool.init( button_t::square_state, "Reselect closes tools" ); reselect_closes_tool.pressed = env_t::reselect_closes_tool; - add_component( &reselect_closes_tool, 2 ); + add_component( &reselect_closes_tool, 3 ); // Frame time label new_component("Frame time:"); frame_time_value_label.buf().printf(" 9999 ms"); frame_time_value_label.update(); - add_component( &frame_time_value_label ); + add_component( &frame_time_value_label, 2 ); // Idle time label new_component("Idle:"); idle_time_value_label.buf().printf(" 9999 ms"); idle_time_value_label.update(); - add_component( &idle_time_value_label ); + add_component( &idle_time_value_label, 2 ); // FPS label new_component("FPS:"); fps_value_label.buf().printf(" 99.9 fps"); fps_value_label.update(); - add_component( &fps_value_label ); + add_component( &fps_value_label, 2 ); // Simloops label new_component("Sim:"); simloops_value_label.buf().printf(" 999.9"); simloops_value_label.update(); - add_component( &simloops_value_label ); - end_table(); + add_component( &simloops_value_label, 2 ); } void gui_settings_t::draw(scr_coord offset) @@ -181,10 +184,21 @@ } +bool gui_settings_t::action_triggered(gui_action_creator_t *comp, value_t) +{ + if (comp == &screen_scale_numinp) { + const sint16 new_value = screen_scale_numinp.get_value(); + dr_set_screen_scale(new_value); + } + + return true; +} + + map_settings_t::map_settings_t() { - set_table_layout( 1, 0 ); - add_table( 2, 0 ); + set_table_layout( 2, 0 ); + // Show grid checkbox buttons[ IDBTN_SHOW_GRID ].init( button_t::square_state, "show grid" ); add_component( buttons + IDBTN_SHOW_GRID, 2 ); @@ -253,8 +267,6 @@ time_setting.set_selection( old_show_month ); add_component( &time_setting ); time_setting.add_listener( this ); - - end_table(); } bool map_settings_t::action_triggered( gui_action_creator_t *comp, value_t v ) @@ -285,8 +297,7 @@ transparency_settings_t::transparency_settings_t() { - set_table_layout( 1, 0 ); - add_table( 2, 0 ); + set_table_layout( 2, 0 ); // Transparent instead of hidden checkbox buttons[ IDBTN_TRANSPARENT_INSTEAD_OF_HIDDEN ].init( button_t::square_state, "hide transparent" ); @@ -324,8 +335,6 @@ factory_tooltip.set_selection( env_t::show_factory_storage_bar ); add_component( &factory_tooltip ); factory_tooltip.add_listener( this ); - - end_table(); } bool transparency_settings_t::action_triggered( gui_action_creator_t *comp, value_t v ) @@ -359,8 +368,7 @@ station_settings_t::station_settings_t() { - set_table_layout( 1, 0 ); - add_table( 2, 0 ); + set_table_layout( 2, 0 ); // Transparent station coverage buttons[ IDBTN_TRANSPARENT_STATION_COVERAGE ].init( button_t::square_state, "transparent station coverage" ); @@ -386,14 +394,11 @@ buttons[ IDBTN_SHOW_WAITING_BARS ].init( button_t::square_state, "show waiting bars" ); buttons[ IDBTN_SHOW_WAITING_BARS ].pressed = env_t::show_names & 2; add_component( buttons + IDBTN_SHOW_WAITING_BARS, 2 ); - - end_table(); } traffic_settings_t::traffic_settings_t() { - set_table_layout( 1, 0 ); - add_table( 2, 0 ); + set_table_layout( 2, 0 ); // Pedestrians in towns checkbox buttons[IDBTN_PEDESTRIANS_IN_TOWNS].init(button_t::square_state, "6LIGHT_CHOOSE"); @@ -447,8 +452,6 @@ money_booking.set_selection( env_t::show_money_message ); add_component(&money_booking, 2); money_booking.add_listener(this); - - end_table(); } bool traffic_settings_t::action_triggered( gui_action_creator_t *comp, value_t v ) Index: gui/display_settings.h =================================================================== --- gui/display_settings.h (revision 10359) +++ gui/display_settings.h (working copy) @@ -16,12 +16,15 @@ #include "components/gui_combobox.h" #include "components/gui_tab_panel.h" + /** * Menu with display settings */ -class gui_settings_t : public gui_aligned_container_t +class gui_settings_t : public gui_aligned_container_t, public action_listener_t { private: + gui_numberinput_t screen_scale_numinp; + gui_label_buf_t frame_time_value_label, idle_time_value_label, @@ -32,9 +35,12 @@ button_t toolbar_pos, reselect_closes_tool, fullscreen, borderless; gui_settings_t(); + void draw( scr_coord offset ) OVERRIDE; + bool action_triggered( gui_action_creator_t *comp, value_t v) OVERRIDE; }; + class map_settings_t : public gui_aligned_container_t, public action_listener_t { private: Index: simmain.cc =================================================================== --- simmain.cc (revision 10359) +++ simmain.cc (working copy) @@ -393,7 +393,9 @@ " -server [PORT] starts program as server (for network game)\n" " without port specified uses 13353\n" " -announce Enable server announcements\n" - " -autodpi Scale for high DPI screens\n" + " -autodpi Automatic screen scaling for high DPI screens\n" + " -screen_scale N Manual screen scaling to N percent (0=off)\n" + " Ignored when -autodpi is specified\n" " -server_dns FQDN/IP FQDN or IP address of server for announcements\n" " -server_name NAME Name of server for announcements\n" " -server_admin_pw PW password for server administration\n" @@ -803,8 +805,13 @@ } if( args.has_arg("-autodpi") ) { - dr_auto_scale( true ); + dr_set_screen_scale( -1 ); } + else if (const char *scaling = args.gimme_arg("-screen_scale", 1)) { + if (scaling[0] >= '0' && scaling[0] <= '9') { + dr_set_screen_scale(atoi(scaling)); + } + } int parameter[2]; parameter[0] = args.has_arg("-async"); Index: simutrans/history.txt =================================================================== --- simutrans/history.txt (revision 10359) +++ simutrans/history.txt (working copy) @@ -1,10 +1,11 @@ FIX: up/down arrows in schedule broken FIX: Simulation speeds up uncontrollably in some cases if New World dialogue is open - CHG: lang files are laoded if their name is *XX.tab or XX*.tab The first is preferred to avoid confusion by name like ja-taken.tab + CHG: lang files are loaded if their name is *XX.tab or XX*.tab. The first is preferred to avoid confusion by name like ja-taken.tab ADD: selected convoi in minimap now magenta. Also network display properly updated when activating or closing windows + ADD: Option to adjust screen scaling manually (either via display settings or '-screen_scale' command line option) -Release of 123.0 (r10137 on 30-Dec-2021): +Release of 123.0 (r10317 on 30-Dec-2021): KNOWN BUG: ugly movement of pedestrians on diagonals/curves FIX: map enlargement crashes with new climate generator FIX: Wrong tooltip when building something with the finance window opened Index: sys/simsys.h =================================================================== --- sys/simsys.h (revision 10359) +++ sys/simsys.h (working copy) @@ -71,9 +71,19 @@ extern char const PATH_SEPARATOR[]; -// scale according to dpi setting -bool dr_auto_scale(bool); +/// @param scale_percent +/// Possible values: +/// -1: auto (scale according to screen DPI setting) +/// 0: off (default 1:1 scale) +/// other: specific scaling, in percent +/// < -1: invalid +bool dr_set_screen_scale(sint16 scale_percent); + +/// @returns Relative size of the virtual display, in percent +sint16 dr_get_screen_scale(); + + bool dr_os_init(int const* parameter); /* maximum size possible (if there) */ Index: sys/simsys_posix.cc =================================================================== --- sys/simsys_posix.cc (revision 10359) +++ sys/simsys_posix.cc (working copy) @@ -29,12 +29,19 @@ #error "Posix only compiles with color depth=0" #endif -// no autoscaling as we have no display ... -bool dr_auto_scale(bool) +bool dr_set_screen_scale(sint16) { + // no autoscaling as we have no display ... return false; } + +sint16 dr_get_screen_scale() +{ + return 100; +} + + bool dr_os_init(const int*) { // prepare for next event Index: sys/simsys_s.cc =================================================================== --- sys/simsys_s.cc (revision 10359) +++ sys/simsys_s.cc (working copy) @@ -146,7 +146,7 @@ #define SCREEN_RESCALE_Y(y) (((y) * SCALE_NEUTRAL_Y) / y_scale) // no autoscaling yet -bool dr_auto_scale(bool) +bool dr_set_screen_scale(sint16 /*scale_percent*/) { #ifdef __ANDROID__ // SDL 1.2 does not support scaling, but the libSDL Android rendering layer @@ -179,6 +179,13 @@ #endif } + +sint16 dr_get_screen_scale() +{ + return 100; +} + + /* * Hier sind die Basisfunktionen zur Initialisierung der * Schnittstelle untergebracht Index: sys/simsys_s2.cc =================================================================== --- sys/simsys_s2.cc (revision 10359) +++ sys/simsys_s2.cc (working copy) @@ -152,11 +152,13 @@ bool has_soft_keyboard = false; -// no autoscaling yet -bool dr_auto_scale(bool on_off ) +bool dr_set_screen_scale(sint16 scale_percent) { + const sint32 old_x_scale = x_scale; + const sint32 old_y_scale = y_scale; + + if( scale_percent == -1 ) { #if SDL_VERSION_ATLEAST(2,0,4) - if( on_off ) { float hdpi, vdpi; SDL_Init( SDL_INIT_VIDEO ); SDL_DisplayMode mode; @@ -178,22 +180,48 @@ y_scale = (y_scale * current_y) / MIN_SCALE_HEIGHT; DBG_MESSAGE("new scaling", "x=%i, y=%i", x_scale, y_scale); } - - return x_scale==SCALE_NEUTRAL_X && y_scale==SCALE_NEUTRAL_Y; - } - else #else #pragma message "SDL version must be at least 2.0.4 to support autoscaling." + // 1.5 scale up by default + x_scale = (150*SCALE_NEUTRAL_X)/100; + y_scale = (150*SCALE_NEUTRAL_Y)/100; #endif - { - // 1.5 scale up by default - x_scale = (3*SCALE_NEUTRAL_X)/2; - y_scale = (3*SCALE_NEUTRAL_Y)/2; - (void)on_off; - return false; } + else if (scale_percent == 0) { + x_scale = SCALE_NEUTRAL_X; + y_scale = SCALE_NEUTRAL_Y; + } + else { + x_scale = (scale_percent*SCALE_NEUTRAL_X)/100; + y_scale = (scale_percent*SCALE_NEUTRAL_Y)/100; + } + + if (x_scale != old_x_scale || y_scale != old_y_scale) { + // force window resize + int w, h; + SDL_GetWindowSize(window, &w, &h); + + SDL_Event ev; + ev.type = SDL_WINDOWEVENT; + ev.window.event = SDL_WINDOWEVENT_SIZE_CHANGED; + ev.window.data1 = w; + ev.window.data2 = h; + + if (SDL_PushEvent(&ev) != 1) { + return false; + } + } + + return true; } + +sint16 dr_get_screen_scale() +{ + return (x_scale*100)/SCALE_NEUTRAL_X; +} + + static int SDLCALL my_event_filter(void* /*userdata*/, SDL_Event* event) { DBG_MESSAGE("my_event_filter", "%i", event->type); @@ -369,6 +397,10 @@ DBG_DEBUG( "internal_create_surfaces(SDL2)", "Using: Renderer: %s, Max_w: %d, Max_h: %d, Flags: %d, Formats: %d, %s", ri.name, ri.max_texture_width, ri.max_texture_height, ri.flags, ri.num_texture_formats, SDL_GetPixelFormatName(pixel_format) ); + // Non-integer scaling -> enable bilinear filtering (must be done before texture creation) + const bool integer_scaling = (x_scale & (SCALE_NEUTRAL_X - 1)) == 0 && (y_scale & (SCALE_NEUTRAL_Y - 1)) == 0; + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, integer_scaling ? "0" : "1" ); // 0=none, 1=bilinear, 2=anisotropic (DirectX only) + screen_tx = SDL_CreateTexture( renderer, pixel_format, SDL_TEXTUREACCESS_STREAMING, tex_width, tex_height ); if( screen_tx == NULL ) { dbg->error( "internal_create_surfaces(SDL2)", "Couldn't create texture: %s", SDL_GetError() ); @@ -402,8 +434,8 @@ { // scale up resolution res = dr_query_screen_resolution(); - const int tex_w = min( res.w, SCREEN_TO_TEX_X(screen_width) ); - const int tex_h = min( res.h, SCREEN_TO_TEX_Y(screen_height) ); + const int tex_w = clamp( res.w, 1, SCREEN_TO_TEX_X(screen_width) ); + const int tex_h = clamp( res.h, 1, SCREEN_TO_TEX_Y(screen_height) ); DBG_MESSAGE("dr_os_open()", "Screen requested %i,%i, available max %i,%i", tex_w, tex_h, res.w, res.h); @@ -411,7 +443,7 @@ // some cards need those alignments // especially 64bit want a border of 8bytes - const int tex_pitch = max((tex_w + 15) & 0x7FF0, 16); + const int tex_pitch = (tex_w + 15) & 0x7FF0; // SDL2 only works with borderless fullscreen (SDL_WINDOW_FULLSCREEN_DESKTOP) Uint32 flags = fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_RESIZABLE; @@ -423,11 +455,6 @@ return 0; } - // Non-integer scaling -> enable bilinear filtering (must be done before texture creation) - if ((x_scale & (SCALE_NEUTRAL_X - 1)) != 0 || (y_scale & (SCALE_NEUTRAL_Y - 1)) != 0) { - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); // 0=none, 1=bilinear, 2=anisotropic (DirectX only) - } - if( !internal_create_surfaces( tex_pitch, tex_h ) ) { return 0; } @@ -648,8 +675,8 @@ case SDL_WINDOWEVENT: if( event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED ) { - sys_event.new_window_size_w = SCREEN_TO_TEX_X(event.window.data1); - sys_event.new_window_size_h = SCREEN_TO_TEX_Y(event.window.data2); + sys_event.new_window_size_w = max(1, SCREEN_TO_TEX_X(event.window.data1)); + sys_event.new_window_size_h = max(1, SCREEN_TO_TEX_Y(event.window.data2)); sys_event.type = SIM_SYSTEM; sys_event.code = SYSTEM_RESIZE; } Index: sys/simsys_w.cc =================================================================== --- sys/simsys_w.cc (revision 10359) +++ sys/simsys_w.cc (working copy) @@ -86,26 +86,48 @@ // scale automatically -bool dr_auto_scale(bool on_off) +bool dr_set_screen_scale(sint16 percent) { - if( on_off ) { - HDC hdc = GetDC(NULL); - if (hdc) { - x_scale = (GetDeviceCaps(hdc, LOGPIXELSX)*32)/96; - y_scale = (GetDeviceCaps(hdc, LOGPIXELSY)*32)/96; - ReleaseDC(NULL, hdc); - } - return true; + bool scale_ok = false; + long old_scale_x = x_scale, old_scale_y= y_scale; + + HDC hdc = GetDC(NULL); + if( percent == -1 && hdc ) { + x_scale = (GetDeviceCaps(hdc, LOGPIXELSX)*32)/96; + y_scale = (GetDeviceCaps(hdc, LOGPIXELSY)*32)/96; + scale_ok = true; } - else { - x_scale = 32; - y_scale = 32; + else if (percent == 0) { + percent = 100; + scale_ok = true; + } + else if (percent < 0) { + dbg->error("dr_set_screen_scale(Win32)", "Invalid screen scaling %d (Must be >= -1)", percent); return false; } + else { + x_scale = (percent * 32) / 100; + y_scale = (percent * 32) / 100; + } + ReleaseDC(NULL, hdc); + + if( (x_scale!=old_scale_x || y_scale!=old_scale_y) && hwnd) { + // force window size update + RECT TempRect; + GetWindowRect(hwnd, &TempRect); + const LRESULT res = PostMessage(hwnd, WM_SIZE, SIZE_RESTORED, MAKELPARAM(TempRect.right - TempRect.left, TempRect.bottom - TempRect.top)); + } + return true; } +sint16 dr_get_screen_scale() +{ + return (x_scale * 100) / 32; +} + + /* * Hier sind die Basisfunktionen zur Initialisierung der * Schnittstelle untergebracht