@@ -1018,12 +1018,20 @@ STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_TOOL
STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_AUTO :(auto-detect)
STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_NORMAL :Normal
STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_2X_ZOOM :Double size
STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_4X_ZOOM :Quad size
STR_GAME_OPTIONS_GRAPHICS :{BLACK}Graphics
STR_GAME_OPTIONS_REFRESH_RATE :{BLACK}Display refresh rate
STR_GAME_OPTIONS_REFRESH_RATE_TOOLTIP :{BLACK}Select the screen refresh rate to use
STR_GAME_OPTIONS_REFRESH_RATE_OTHER :other
STR_GAME_OPTIONS_REFRESH_RATE_ITEM :{NUM}Hz
STR_GAME_OPTIONS_REFRESH_RATE_WARNING :{WHITE}Refresh rates higher than 60Hz might impact performance.
STR_GAME_OPTIONS_BASE_GRF :{BLACK}Base graphics set
STR_GAME_OPTIONS_BASE_GRF_TOOLTIP :{BLACK}Select the base graphics set to use
STR_GAME_OPTIONS_BASE_GRF_STATUS :{RED}{NUM} missing/corrupted file{P "" s}
STR_GAME_OPTIONS_BASE_GRF_DESCRIPTION_TOOLTIP :{BLACK}Additional information about the base graphics set
STR_GAME_OPTIONS_BASE_SFX :{BLACK}Base sounds set
@@ -35,14 +35,16 @@
#include "querystring_gui.h"
#include "fontcache.h"
#include "zoom_func.h"
#include "video/video_driver.hpp"
#include <vector>
#include <iterator>
#include "safeguards.h"
static const StringID _autosave_dropdown[] = {
STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_OFF,
STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_1_MONTH,
STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_3_MONTHS,
@@ -137,21 +139,39 @@ template <class TBaseSet>
void ShowBaseSetTextfileWindow(TextfileType file_type, const TBaseSet* baseset, StringID content_type)
{
DeleteWindowById(WC_TEXTFILE, file_type);
new BaseSetTextfileWindow<TBaseSet>(file_type, baseset, content_type);
}
std::set<int> _refresh_rates = { 30, 60, 75, 90, 100, 120, 144, 240 };
/**
* Add the refresh rate from the config and the refresh rates from all the monitors to
* our list of refresh rates shown in the GUI.
*/
static void AddRefreshRatesAndSelect()
/* Add the refresh rate as selected in the config. */
_refresh_rates.insert(_settings_client.gui.refresh_rate);
/* Add all the refresh rates of all monitors connected to the machine. */
std::vector<int> monitorRates = VideoDriver::GetInstance()->GetListOfMonitorRefreshRates();
std::copy(monitorRates.begin(), monitorRates.end(), std::inserter(_refresh_rates, _refresh_rates.end()));
struct GameOptionsWindow : Window {
GameSettings *opt;
bool reload;
GameOptionsWindow(WindowDesc *desc) : Window(desc)
this->opt = &GetGameSettings();
this->reload = false;
AddRefreshRatesAndSelect();
this->InitNested(WN_GAME_OPTIONS_GAME_OPTIONS);
this->OnInvalidateData(0);
~GameOptionsWindow()
@@ -212,12 +232,22 @@ struct GameOptionsWindow : Window {
*selected_index = GetCurRes();
for (uint i = 0; i < _resolutions.size(); i++) {
list.emplace_back(new DropDownListStringItem(SPECSTR_RESOLUTION_START + i, i, false));
break;
case WID_GO_REFRESH_RATE_DROPDOWN: // Setup refresh rate dropdown
for (auto it = _refresh_rates.begin(); it != _refresh_rates.end(); it++) {
auto i = std::distance(_refresh_rates.begin(), it);
if (*it == _settings_client.gui.refresh_rate) *selected_index = i;
auto item = new DropDownListParamStringItem(STR_GAME_OPTIONS_REFRESH_RATE_ITEM, i, false);
item->SetParam(0, *it);
list.emplace_back(item);
case WID_GO_GUI_ZOOM_DROPDOWN: {
*selected_index = _gui_zoom_cfg != ZOOM_LVL_CFG_AUTO ? ZOOM_LVL_OUT_4X - _gui_zoom + 1 : 0;
const StringID *items = _gui_zoom_dropdown;
for (int i = 0; *items != INVALID_STRING_ID; items++, i++) {
list.emplace_back(new DropDownListStringItem(*items, i, i != 0 && _settings_client.gui.zoom_min > ZOOM_LVL_OUT_4X - i + 1));
@@ -249,23 +279,24 @@ struct GameOptionsWindow : Window {
return list;
void SetStringParameters(int widget) const override
switch (widget) {
case WID_GO_CURRENCY_DROPDOWN: SetDParam(0, _currency_specs[this->opt->locale.currency].name); break;
case WID_GO_AUTOSAVE_DROPDOWN: SetDParam(0, _autosave_dropdown[_settings_client.gui.autosave]); break;
case WID_GO_LANG_DROPDOWN: SetDParamStr(0, _current_language->own_name); break;
case WID_GO_RESOLUTION_DROPDOWN: SetDParam(0, GetCurRes() == _resolutions.size() ? STR_GAME_OPTIONS_RESOLUTION_OTHER : SPECSTR_RESOLUTION_START + GetCurRes()); break;
case WID_GO_GUI_ZOOM_DROPDOWN: SetDParam(0, _gui_zoom_dropdown[_gui_zoom_cfg != ZOOM_LVL_CFG_AUTO ? ZOOM_LVL_OUT_4X - _gui_zoom_cfg + 1 : 0]); break;
case WID_GO_FONT_ZOOM_DROPDOWN: SetDParam(0, _font_zoom_dropdown[_font_zoom_cfg != ZOOM_LVL_CFG_AUTO ? ZOOM_LVL_OUT_4X - _font_zoom_cfg + 1 : 0]); break;
case WID_GO_BASE_GRF_DROPDOWN: SetDParamStr(0, BaseGraphics::GetUsedSet()->name.c_str()); break;
case WID_GO_BASE_GRF_STATUS: SetDParam(0, BaseGraphics::GetUsedSet()->GetNumInvalid()); break;
case WID_GO_BASE_SFX_DROPDOWN: SetDParamStr(0, BaseSounds::GetUsedSet()->name.c_str()); break;
case WID_GO_BASE_MUSIC_DROPDOWN: SetDParamStr(0, BaseMusic::GetUsedSet()->name.c_str()); break;
case WID_GO_BASE_MUSIC_STATUS: SetDParam(0, BaseMusic::GetUsedSet()->GetNumInvalid()); break;
case WID_GO_REFRESH_RATE_DROPDOWN: SetDParam(0, _settings_client.gui.refresh_rate); break;
void DrawWidget(const Rect &r, int widget) const override
@@ -448,12 +479,22 @@ struct GameOptionsWindow : Window {
case WID_GO_RESOLUTION_DROPDOWN: // Change resolution
if ((uint)index < _resolutions.size() && ChangeResInGame(_resolutions[index].width, _resolutions[index].height)) {
this->SetDirty();
case WID_GO_REFRESH_RATE_DROPDOWN: {
_settings_client.gui.refresh_rate = *std::next(_refresh_rates.begin(), index);
if (_settings_client.gui.refresh_rate > 60) {
/* Show warning to the user that this refresh rate might not be suitable on
* larger maps with many NewGRFs and vehicles. */
ShowErrorMessage(STR_GAME_OPTIONS_REFRESH_RATE_WARNING, INVALID_STRING_ID, WL_INFO);
int8 new_zoom = index > 0 ? ZOOM_LVL_OUT_4X - index + 1 : ZOOM_LVL_CFG_AUTO;
if (new_zoom != _gui_zoom_cfg) {
GfxClearSpriteCache();
_gui_zoom_cfg = new_zoom;
UpdateGUIZoom();
@@ -525,42 +566,53 @@ static const NWidgetPart _nested_game_op
NWidget(WWT_PANEL, COLOUR_GREY, WID_GO_BACKGROUND), SetPIP(6, 6, 10),
NWidget(NWID_HORIZONTAL), SetPIP(10, 10, 10),
NWidget(NWID_VERTICAL), SetPIP(0, 6, 0),
NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_AUTOSAVE_FRAME, STR_NULL),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_AUTOSAVE_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_STRING, STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_TOOLTIP), SetFill(1, 0),
EndContainer(),
NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_RESOLUTION, STR_NULL),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_RESOLUTION_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_STRING, STR_GAME_OPTIONS_RESOLUTION_TOOLTIP), SetFill(1, 0), SetPadding(0, 0, 3, 0),
NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0), SetPadding(0, 0, 3, 0),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_FULLSCREEN, STR_NULL),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_GO_FULLSCREEN_BUTTON), SetMinimalSize(21, 9), SetDataTip(STR_EMPTY, STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP),
NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_VIDEO_ACCELERATION, STR_NULL),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_GO_VIDEO_ACCEL_BUTTON), SetMinimalSize(21, 9), SetDataTip(STR_EMPTY, STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP),
NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_GUI_ZOOM_FRAME, STR_NULL),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_GUI_ZOOM_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_STRING, STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP), SetFill(1, 0),
NWidget(NWID_SPACER), SetMinimalSize(0, 0), SetFill(0, 1),
NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME, STR_NULL),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_CURRENCY_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_STRING, STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP), SetFill(1, 0),
NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_LANGUAGE, STR_NULL),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_LANG_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_RAW_STRING, STR_GAME_OPTIONS_LANGUAGE_TOOLTIP), SetFill(1, 0),
NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_FONT_ZOOM, STR_NULL),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_FONT_ZOOM_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_STRING, STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_TOOLTIP), SetFill(1, 0),
NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_GRAPHICS, STR_NULL), SetPadding(0, 10, 0, 10),
NWidget(NWID_HORIZONTAL),
NWidget(NWID_VERTICAL),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_RESOLUTION, STR_NULL), SetPadding(0, 0, 2, 0),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_REFRESH_RATE, STR_NULL), SetPadding(0, 0, 2, 0),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_FULLSCREEN, STR_NULL), SetPadding(0, 0, 2, 0),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_RESOLUTION_DROPDOWN), SetMinimalSize(100, 12), SetDataTip(STR_BLACK_STRING, STR_GAME_OPTIONS_RESOLUTION_TOOLTIP), SetFill(1, 0), SetPadding(0, 0, 2, 0),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_REFRESH_RATE_DROPDOWN), SetMinimalSize(100, 12), SetDataTip(STR_GAME_OPTIONS_REFRESH_RATE_ITEM, STR_GAME_OPTIONS_REFRESH_RATE_TOOLTIP), SetFill(1, 0), SetPadding(0, 0, 2, 0),
NWidget(NWID_HORIZONTAL), SetPadding(0, 0, 2, 0),
NWidget(NWID_SPACER), SetMinimalSize(1, 0), SetFill(1, 0),
NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_BASE_GRF, STR_NULL), SetPadding(0, 10, 0, 10),
NWidget(NWID_HORIZONTAL), SetPIP(0, 30, 0),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_BASE_GRF_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_RAW_STRING, STR_GAME_OPTIONS_BASE_GRF_TOOLTIP),
NWidget(WWT_TEXT, COLOUR_GREY, WID_GO_BASE_GRF_STATUS), SetMinimalSize(150, 12), SetDataTip(STR_EMPTY, STR_NULL), SetFill(1, 0),
NWidget(WWT_TEXT, COLOUR_GREY, WID_GO_BASE_GRF_DESCRIPTION), SetMinimalSize(330, 0), SetDataTip(STR_EMPTY, STR_GAME_OPTIONS_BASE_GRF_DESCRIPTION_TOOLTIP), SetFill(1, 0), SetPadding(6, 0, 6, 0),
@@ -233,12 +233,22 @@ bool VideoDriver_Allegro::ClaimMousePoin
select_mouse_cursor(MOUSE_CURSOR_NONE);
show_mouse(nullptr);
disable_hardware_cursor();
return true;
std::vector<int> VideoDriver_Allegro::GetListOfMonitorRefreshRates()
std::vector<int> rates = {};
int refresh_rate = get_refresh_rate();
if (refresh_rate != 0) rates.push_back(refresh_rate);
return rates;
struct AllegroVkMapping {
uint16 vk_from;
byte vk_count;
byte map_to;
};
@@ -28,12 +28,14 @@ public:
bool ToggleFullscreen(bool fullscreen) override;
bool AfterBlitterChange() override;
bool ClaimMousePointer() override;
std::vector<int> GetListOfMonitorRefreshRates() override;
const char *GetName() const override { return "allegro"; }
protected:
void InputLoop() override;
void Paint() override;
void CheckPaletteAnim() override;
@@ -44,12 +44,14 @@ public:
bool ChangeResolution(int w, int h) override;
void EditBoxLostFocus() override;
/* --- The following methods should be private, but can't be due to Obj-C limitations. --- */
void MainLoopReal();
virtual void AllocateBackingStore(bool force = false) = 0;
@@ -40,12 +40,13 @@
#include "../../settings_type.h"
#include "../../window_func.h"
#include "../../window_gui.h"
#import <sys/param.h> /* for MAXPATHLEN */
#import <sys/time.h> /* gettimeofday */
#include <array>
* Important notice regarding all modifications!!!!!!!
* There are certain limitations because the file is objective C++.
* gdb has limitations.
* C++ and objective C code can't be joined in all cases (classes stuff).
@@ -226,12 +227,36 @@ void VideoDriver_Cocoa::EditBoxLostFocus
[ [ this->cocoaview inputContext ] discardMarkedText ];
/* Clear any marked string from the current edit box. */
HandleTextInput(nullptr, true);
* Get refresh rates of all connected monitors.
std::vector<int> VideoDriver_Cocoa::GetListOfMonitorRefreshRates()
std::vector<int> rates{};
if (MacOSVersionIsAtLeast(10, 6, 0)) {
std::array<CGDirectDisplayID, 16> displays;
uint32_t count = 0;
CGGetActiveDisplayList(displays.size(), displays.data(), &count);
for (uint32_t i = 0; i < count; i++) {
CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displays[i]);
int rate = (int)CGDisplayModeGetRefreshRate(mode);
if (rate > 0) rates.push_back(rate);
CGDisplayModeRelease(mode);
* Get the resolution of the main screen.
Dimension VideoDriver_Cocoa::GetScreenSize() const
NSRect frame = [ [ NSScreen mainScreen ] frame ];
return { static_cast<uint>(NSWidth(frame)), static_cast<uint>(NSHeight(frame)) };
@@ -234,12 +234,23 @@ void VideoDriver_SDL_Base::EditBoxLostFo
if (this->edit_box_focused) {
SDL_StopTextInput();
this->edit_box_focused = false;
std::vector<int> VideoDriver_SDL_Base::GetListOfMonitorRefreshRates()
for (int i = 0; i < SDL_GetNumVideoDisplays(); i++) {
SDL_DisplayMode mode = {};
if (SDL_GetDisplayMode(i, 0, &mode) != 0) continue;
if (mode.refresh_rate != 0) rates.push_back(mode.refresh_rate);
struct SDLVkMapping {
SDL_Keycode vk_from;
bool unprintable;
@@ -36,12 +36,14 @@ public:
void EditBoxGainedFocus() override;
const char *GetName() const override { return "sdl"; }
struct SDL_Window *sdl_window; ///< Main SDL window.
Palette local_palette; ///< Copy of _cur_palette.
bool buffer_locked; ///< Video buffer was locked by the main thread.
@@ -147,12 +147,21 @@ public:
* An edit box gained the input focus
virtual void EditBoxGainedFocus() {}
* Get a list of refresh rates of each available monitor.
* @return A vector of the refresh rates of all available monitors.
virtual std::vector<int> GetListOfMonitorRefreshRates()
return {};
* Get a suggested default GUI zoom taking screen DPI into account.
virtual ZoomLevel GetSuggestedUIZoom()
float dpi_scale = this->GetDPIScale();
@@ -913,12 +913,33 @@ void VideoDriver_Win32Base::EditBoxLostF
CancelIMEComposition(this->main_wnd);
SetCompositionPos(this->main_wnd);
SetCandidatePos(this->main_wnd);
std::vector<int> VideoDriver_Win32Base::GetListOfMonitorRefreshRates()
EnumDisplayMonitors(nullptr, nullptr, [](HMONITOR hMonitor, HDC hDC, LPRECT rc, LPARAM data) -> BOOL {
auto &list = *reinterpret_cast<std::vector<int>*>(data);
MONITORINFOEX monitorInfo = {};
monitorInfo.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(hMonitor, &monitorInfo);
DEVMODE devMode = {};
devMode.dmSize = sizeof(DEVMODE);
devMode.dmDriverExtra = 0;
EnumDisplaySettings(monitorInfo.szDevice, ENUM_CURRENT_SETTINGS, &devMode);
if (devMode.dmDisplayFrequency != 0) list.push_back(devMode.dmDisplayFrequency);
}, reinterpret_cast<LPARAM>(&rates));
Dimension VideoDriver_Win32Base::GetScreenSize() const
return { static_cast<uint>(GetSystemMetrics(SM_CXSCREEN)), static_cast<uint>(GetSystemMetrics(SM_CYSCREEN)) };
float VideoDriver_Win32Base::GetDPIScale()
@@ -30,12 +30,14 @@ public:
HWND main_wnd; ///< Handle to system window.
bool fullscreen; ///< Whether to use (true) fullscreen mode.
bool has_focus = false; ///< Does our window have system focus?
Rect dirty_rect; ///< Region of the screen that needs redrawing.
int width = 0; ///< Width in pixels of our display surface.
@@ -30,12 +30,13 @@ enum GameOptionsWidgets {
WID_GO_BASE_MUSIC_DROPDOWN, ///< Use to select a base music set.
WID_GO_BASE_MUSIC_STATUS, ///< Info about corrupted files etc.
WID_GO_BASE_MUSIC_TEXTFILE, ///< Open base music readme, changelog (+1) or license (+2).
WID_GO_BASE_MUSIC_DESCRIPTION = WID_GO_BASE_MUSIC_TEXTFILE + TFT_END, ///< Description of selected base music set.
WID_GO_FONT_ZOOM_DROPDOWN, ///< Dropdown for the font zoom level.
WID_GO_VIDEO_ACCEL_BUTTON, ///< Toggle for video acceleration.
WID_GO_REFRESH_RATE_DROPDOWN, ///< Dropdown for all available refresh rates.
/** Widgets of the #GameSettingsWindow class. */
enum GameSettingsWidgets {
WID_GS_FILTER, ///< Text filter.
WID_GS_OPTIONSPANEL, ///< Panel widget containing the option lists.
Status change: