diff --git a/data/language/english_uk.txt b/data/language/english_uk.txt index 8460f2a6ee..5cacdaf8bb 100644 --- a/data/language/english_uk.txt +++ b/data/language/english_uk.txt @@ -3917,9 +3917,10 @@ STR_5575 :Max Players: STR_5576 :Port: STR_5577 :South Korean Won (W) STR_5578 :Russian Rouble (R) +STR_5579 :Window scale factor: ##################### -# Rides/attractions # +# Rides/attractions # ##################### #WW diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 37a315ef8e..0b54cb2da6 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -28,7 +28,7 @@ - Feature: Custom user data path specified by command line argument. - Feature: Full UTF-8 language support. - Feature: TTF font integration for non-Latin languages. -- Feature: Added support for Traditional Chinese, Simplified Chinese, Korean, Russian, Finnish and Brazilian Portuguese. +- Feature: Added support for Traditional Chinese, Simplified Chinese, Korean, Russian, Finnish and Brazilian Portuguese. - Feature: Added South Korean Won and Russian Rouble as currencies. - Feature: Allow different date formats. - Feature: Option to automatically pause the game on minimise from fullscreen. @@ -38,6 +38,7 @@ - Feature: Option to automatically place staff after hire. - Feature: Option to enable 'mow grass' by default for handymen (RCT1 style) - Feature: Option to ignore invalid checksums on loaded parks. +- Feature: Option to scale game display for better compatibility with high DPI screens. - Alteration: Autosave is now measured in real-time rather than in-game date. - Technical: DirectDraw, DirectInput, DirectPlay and DirectSound dependencies are no longer used. - Removed: Six Flags branding and limitations. diff --git a/src/config.c b/src/config.c index 647f59e4f7..916466eb60 100644 --- a/src/config.c +++ b/src/config.c @@ -197,6 +197,7 @@ config_property_definition _generalDefinitions[] = { { offsetof(general_configuration, upper_case_banners), "upper_case_banners", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL }, { offsetof(general_configuration, allow_loading_with_incorrect_checksum),"allow_loading_with_incorrect_checksum", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL }, { offsetof(general_configuration, steam_overlay_pause), "steam_overlay_pause", CONFIG_VALUE_TYPE_BOOLEAN, true, NULL }, + { offsetof(general_configuration, window_scale), "window_scale", CONFIG_VALUE_TYPE_FLOAT, { .value_float = 1.0f }, NULL }, }; config_property_definition _interfaceDefinitions[] = { diff --git a/src/config.h b/src/config.h index 58dae6b559..951727ca21 100644 --- a/src/config.h +++ b/src/config.h @@ -167,6 +167,7 @@ typedef struct { uint8 upper_case_banners; uint8 allow_loading_with_incorrect_checksum; uint8 steam_overlay_pause; + float window_scale; } general_configuration; typedef struct { diff --git a/src/localisation/string_ids.h b/src/localisation/string_ids.h index 1091f29339..6d6ddcf95d 100644 --- a/src/localisation/string_ids.h +++ b/src/localisation/string_ids.h @@ -2169,6 +2169,8 @@ enum { STR_WON = 5577, STR_ROUBLE = 5578, + STR_UI_SCALING_DESC = 5579, + // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working STR_COUNT = 32768 }; diff --git a/src/platform/platform.h b/src/platform/platform.h index fcd94a06b9..a8c4b260d8 100644 --- a/src/platform/platform.h +++ b/src/platform/platform.h @@ -100,6 +100,7 @@ void platform_get_closest_resolution(int inWidth, int inHeight, int *outWidth, i void platform_init(); void platform_draw(); void platform_free(); +void platform_trigger_resize(); void platform_update_palette(const uint8 *colours, int start_index, int num_colours); void platform_set_fullscreen_mode(int mode); void platform_set_cursor(char cursor); diff --git a/src/platform/shared.c b/src/platform/shared.c index 71bbc88033..bc92131204 100644 --- a/src/platform/shared.c +++ b/src/platform/shared.c @@ -65,8 +65,9 @@ bool gHardwareDisplay; bool gSteamOverlayActive = false; -static SDL_Surface *_surface; -static SDL_Palette *_palette; +static SDL_Surface *_surface = NULL; +static SDL_Surface *_RGBASurface = NULL; +static SDL_Palette *_palette = NULL; static void *_screenBuffer; static int _screenBufferSize; @@ -285,9 +286,24 @@ void platform_draw() SDL_UnlockSurface(_surface); // Copy the surface to the window - if (SDL_BlitSurface(_surface, NULL, SDL_GetWindowSurface(gWindow), NULL)) { - log_fatal("SDL_BlitSurface %s", SDL_GetError()); - exit(1); + if (gConfigGeneral.window_scale == 1 || gConfigGeneral.window_scale <= 0) + { + if (SDL_BlitSurface(_surface, NULL, SDL_GetWindowSurface(gWindow), NULL)) { + log_fatal("SDL_BlitSurface %s", SDL_GetError()); + exit(1); + } + } else { + // first blit to rgba surface to change the pixel format + if (SDL_BlitSurface(_surface, NULL, _RGBASurface, NULL)) { + log_fatal("SDL_BlitSurface %s", SDL_GetError()); + exit(1); + } + // then scale to window size. Without changing to RGBA first, SDL complains + // about blit configurations being incompatible. + if (SDL_BlitScaled(_RGBASurface, NULL, SDL_GetWindowSurface(gWindow), NULL)) { + log_fatal("SDL_BlitScaled %s", SDL_GetError()); + exit(1); + } } if (SDL_UpdateWindowSurface(gWindow)) { log_fatal("SDL_UpdateWindowSurface %s", SDL_GetError()); @@ -300,17 +316,19 @@ void platform_draw() static void platform_resize(int width, int height) { uint32 flags; + int dst_w = (int)(width / gConfigGeneral.window_scale); + int dst_h = (int)(height / gConfigGeneral.window_scale); - RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16) = width; - RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, uint16) = height; + RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16) = dst_w; + RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, uint16) = dst_h; platform_refresh_video(); flags = SDL_GetWindowFlags(gWindow); if ((flags & SDL_WINDOW_MINIMIZED) == 0) { - window_resize_gui(width, height); - window_relocate_windows(width, height); + window_resize_gui(dst_w, dst_h); + window_relocate_windows(dst_w, dst_h); } title_fix_location(); @@ -329,6 +347,13 @@ static void platform_resize(int width, int height) } } +void platform_trigger_resize() +{ + int w, h; + SDL_GetWindowSize(gWindow, &w, &h); + platform_resize(w, h); +} + static uint8 soft_light(uint8 a, uint8 b) { float fa = a / 255.0f; @@ -421,11 +446,11 @@ void platform_process_messages() } break; case SDL_MOUSEMOTION: - RCT2_GLOBAL(0x0142406C, int) = e.motion.x; - RCT2_GLOBAL(0x01424070, int) = e.motion.y; + RCT2_GLOBAL(0x0142406C, int) = (int)(e.motion.x / gConfigGeneral.window_scale); + RCT2_GLOBAL(0x01424070, int) = (int)(e.motion.y / gConfigGeneral.window_scale); - gCursorState.x = e.motion.x; - gCursorState.y = e.motion.y; + gCursorState.x = (int)(e.motion.x / gConfigGeneral.window_scale); + gCursorState.y = (int)(e.motion.y / gConfigGeneral.window_scale); break; case SDL_MOUSEWHEEL: if (gConsoleOpen) { @@ -435,8 +460,8 @@ void platform_process_messages() gCursorState.wheel += e.wheel.y * 128; break; case SDL_MOUSEBUTTONDOWN: - RCT2_GLOBAL(0x01424318, int) = e.button.x; - RCT2_GLOBAL(0x0142431C, int) = e.button.y; + RCT2_GLOBAL(0x01424318, int) = (int)(e.button.x / gConfigGeneral.window_scale); + RCT2_GLOBAL(0x0142431C, int) = (int)(e.button.y / gConfigGeneral.window_scale); switch (e.button.button) { case SDL_BUTTON_LEFT: store_mouse_input(1); @@ -454,8 +479,8 @@ void platform_process_messages() } break; case SDL_MOUSEBUTTONUP: - RCT2_GLOBAL(0x01424318, int) = e.button.x; - RCT2_GLOBAL(0x0142431C, int) = e.button.y; + RCT2_GLOBAL(0x01424318, int) = (int)(e.button.x / gConfigGeneral.window_scale); + RCT2_GLOBAL(0x0142431C, int) = (int)(e.button.y / gConfigGeneral.window_scale); switch (e.button.button) { case SDL_BUTTON_LEFT: store_mouse_input(2); @@ -644,6 +669,8 @@ static void platform_close_window() SDL_FreeSurface(_surface); if (_palette != NULL) SDL_FreePalette(_palette); + if (_RGBASurface != NULL) + SDL_FreeSurface(_RGBASurface); platform_unload_cursors(); } @@ -916,14 +943,18 @@ void platform_refresh_video() } else { if (_surface != NULL) SDL_FreeSurface(_surface); + if (_RGBASurface != NULL) + SDL_FreeSurface(_RGBASurface); if (_palette != NULL) SDL_FreePalette(_palette); _surface = SDL_CreateRGBSurface(0, width, height, 8, 0, 0, 0, 0); + _RGBASurface = SDL_CreateRGBSurface(0, width, height, 32, 0, 0, 0, 0); + SDL_SetSurfaceBlendMode(_RGBASurface, SDL_BLENDMODE_NONE); _palette = SDL_AllocPalette(256); - if (!_surface || !_palette) { - log_fatal("%p || %p == NULL %s", _surface, _palette, SDL_GetError()); + if (!_surface || !_palette || !_RGBASurface) { + log_fatal("%p || %p || %p == NULL %s", _surface, _palette, _RGBASurface, SDL_GetError()); exit(-1); } diff --git a/src/windows/options.c b/src/windows/options.c index 4609c5bfe1..660d152380 100644 --- a/src/windows/options.c +++ b/src/windows/options.c @@ -82,6 +82,9 @@ enum WINDOW_OPTIONS_WIDGET_IDX { WIDX_UNCAP_FPS_CHECKBOX, WIDX_MINIMIZE_FOCUS_LOSS, WIDX_STEAM_OVERLAY_PAUSE, + WIDX_SCALE, + WIDX_SCALE_UP, + WIDX_SCALE_DOWN, WIDX_RENDERING_GROUP, WIDX_TILE_SMOOTHING_CHECKBOX, WIDX_GRIDLINES_CHECKBOX, @@ -156,7 +159,7 @@ enum WINDOW_OPTIONS_WIDGET_IDX { }; #define WW 310 -#define WH 265 +#define WH 280 #define MAIN_OPTIONS_WIDGETS \ { WWT_FRAME, 0, 0, WW-1, 0, WH-1, STR_NONE, STR_NONE }, \ @@ -172,7 +175,7 @@ enum WINDOW_OPTIONS_WIDGET_IDX { static rct_widget window_options_display_widgets[] = { MAIN_OPTIONS_WIDGETS, - { WWT_GROUPBOX, 1, 5, 304, 53, 160, STR_HARDWARE_GROUP, STR_NONE }, // Hardware group + { WWT_GROUPBOX, 1, 5, 304, 53, 175, STR_HARDWARE_GROUP, STR_NONE }, // Hardware group { WWT_DROPDOWN, 1, 155, 299, 68, 79, STR_RESOLUTION_X_BY_Y, STR_NONE }, // resolution { WWT_DROPDOWN_BUTTON, 1, 288, 298, 69, 78, STR_DROPDOWN_GLYPH, STR_NONE }, { WWT_DROPDOWN, 1, 155, 299, 83, 94, 871, STR_NONE }, // fullscreen @@ -181,14 +184,17 @@ static rct_widget window_options_display_widgets[] = { { WWT_CHECKBOX, 1, 10, 290, 114, 125, STR_UNCAP_FPS, STR_NONE }, // uncap fps { WWT_CHECKBOX, 1, 10, 290, 129, 140, STR_MININISE_FULL_SCREEN_ON_FOCUS_LOSS, STR_NONE }, // minimise fullscreen focus loss { WWT_CHECKBOX, 1, 10, 290, 144, 155, STR_STEAM_OVERLAY_PAUSE, STR_NONE }, // minimise fullscreen focus loss + { WWT_SPINNER, 1, 155, 299, 159, 170, STR_NONE, STR_NONE }, // scale spinner + { WWT_DROPDOWN_BUTTON, 1, 288, 298, 160, 164, STR_NUMERIC_UP, STR_NONE }, // scale spinner up + { WWT_DROPDOWN_BUTTON, 1, 288, 298, 165, 169, STR_NUMERIC_DOWN, STR_NONE }, // scale spinner down - { WWT_GROUPBOX, 1, 5, 304, 164, 255, STR_RENDERING_GROUP, STR_NONE }, // Rendering group - { WWT_CHECKBOX, 1, 10, 290, 179, 190, STR_TILE_SMOOTHING, STR_TILE_SMOOTHING_TIP }, // landscape smoothing - { WWT_CHECKBOX, 1, 10, 290, 194, 205, STR_GRIDLINES, STR_GRIDLINES_TIP }, // gridlines - { WWT_DROPDOWN, 1, 155, 299, 208, 219, STR_NONE, STR_NONE }, // construction marker - { WWT_DROPDOWN_BUTTON, 1, 288, 298, 209, 218, STR_DROPDOWN_GLYPH, STR_NONE }, - { WWT_CHECKBOX, 1, 10, 290, 224, 235, STR_CYCLE_DAY_NIGHT, STR_NONE }, // cycle day-night - { WWT_CHECKBOX, 1, 10, 290, 239, 250, STR_UPPER_CASE_BANNERS, STR_NONE }, // upper case banners + { WWT_GROUPBOX, 1, 5, 304, 179, 270, STR_RENDERING_GROUP, STR_NONE }, // Rendering group + { WWT_CHECKBOX, 1, 10, 290, 194, 205, STR_TILE_SMOOTHING, STR_TILE_SMOOTHING_TIP }, // landscape smoothing + { WWT_CHECKBOX, 1, 10, 290, 209, 220, STR_GRIDLINES, STR_GRIDLINES_TIP }, // gridlines + { WWT_DROPDOWN, 1, 155, 299, 224, 234, STR_NONE, STR_NONE }, // construction marker + { WWT_DROPDOWN_BUTTON, 1, 288, 298, 225, 233, STR_DROPDOWN_GLYPH, STR_NONE }, + { WWT_CHECKBOX, 1, 10, 290, 239, 250, STR_CYCLE_DAY_NIGHT, STR_NONE }, // cycle day-night + { WWT_CHECKBOX, 1, 10, 290, 254, 265, STR_UPPER_CASE_BANNERS, STR_NONE }, // upper case banners { WIDGETS_END }, }; @@ -362,6 +368,9 @@ static uint32 window_options_page_enabled_widgets[] = { (1 << WIDX_UNCAP_FPS_CHECKBOX) | (1 << WIDX_MINIMIZE_FOCUS_LOSS) | (1 << WIDX_STEAM_OVERLAY_PAUSE) | + (1 << WIDX_SCALE) | + (1 << WIDX_SCALE_UP) | + (1 << WIDX_SCALE_DOWN) | (1 << WIDX_CONSTRUCTION_MARKER) | (1 << WIDX_CONSTRUCTION_MARKER_DROPDOWN) | (1 << WIDX_DAY_NIGHT_CHECKBOX) | @@ -761,6 +770,19 @@ static void window_options_mousedown(int widgetIndex, rct_window*w, rct_widget* dropdown_set_checked(gConfigGeneral.construction_marker_colour, true); break; + case WIDX_SCALE_UP: + gConfigGeneral.window_scale += 0.25f; + config_save_default(); + gfx_invalidate_screen(); + platform_trigger_resize(); + break; + case WIDX_SCALE_DOWN: + gConfigGeneral.window_scale -= 0.25f; + gConfigGeneral.window_scale = max(0.5f, gConfigGeneral.window_scale); + config_save_default(); + gfx_invalidate_screen(); + platform_trigger_resize(); + break; } break; @@ -1363,6 +1385,10 @@ static void window_options_paint(rct_window *w, rct_drawpixelinfo *dpi) gfx_draw_string_left(dpi, STR_DISPLAY_RESOLUTION, w, w->colours[1], w->x + 10, w->y + window_options_display_widgets[WIDX_RESOLUTION].top + 1); gfx_draw_string_left(dpi, STR_FULLSCREEN_MODE, w, w->colours[1], w->x + 10, w->y + window_options_display_widgets[WIDX_FULLSCREEN].top + 1); gfx_draw_string_left(dpi, STR_CONSTRUCTION_MARKER, w, w->colours[1], w->x + 10, w->y + window_options_display_widgets[WIDX_CONSTRUCTION_MARKER].top + 1); + gfx_draw_string_left(dpi, STR_UI_SCALING_DESC, w, w->colours[1], w->x + 10, w->y + window_options_display_widgets[WIDX_SCALE].top + 1); + + int scale = (int)(gConfigGeneral.window_scale * 100); + gfx_draw_string_left(dpi, 3311, &scale, w->colours[1], w->x + w->widgets[WIDX_SCALE].left + 1, w->y + w->widgets[WIDX_SCALE].top + 1); break; case WINDOW_OPTIONS_PAGE_CULTURE: gfx_draw_string_left(dpi, 2776, w, w->colours[1], w->x + 10, w->y + window_options_culture_widgets[WIDX_LANGUAGE].top + 1); @@ -1380,7 +1406,7 @@ static void window_options_paint(rct_window *w, rct_drawpixelinfo *dpi) gfx_draw_string_left(dpi, STR_DISTANCE_AND_SPEED, w, w->colours[1], w->x + 10, w->y + window_options_culture_widgets[WIDX_DISTANCE].top + 1); gfx_draw_string_left(dpi, STR_TEMPERATURE, w, w->colours[1], w->x + 10, w->y + window_options_culture_widgets[WIDX_TEMPERATURE].top + 1); gfx_draw_string_left(dpi, STR_HEIGHT_LABELS, w, w->colours[1], w->x + 10, w->y + window_options_culture_widgets[WIDX_HEIGHT_LABELS].top + 1); - gfx_draw_string_left(dpi, 5161, w, w->colours[1], w->x + 10, w->y + window_options_culture_widgets[WIDX_DATE_FORMAT].top + 1); + gfx_draw_string_left(dpi, STR_DATE_FORMAT, w, w->colours[1], w->x + 10, w->y + window_options_culture_widgets[WIDX_DATE_FORMAT].top + 1); gfx_draw_string_left( dpi, DateFormatStringIds[gConfigGeneral.date_format],