Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion src/engine/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ private static readonly List<string>
/// </summary>
private static readonly Settings SingletonInstance = InitializeGlobalSettings();

private static int oldDisplaySelectionIndex = -1;

static Settings()
{
}
Expand Down Expand Up @@ -122,6 +124,12 @@ public enum AntiAliasingMode
[JsonProperty]
public SettingValue<float> RenderScale { get; private set; } = new(1.0f);

/// <summary>
/// Selected window index for the game window.
/// </summary>
[JsonProperty]
public SettingValue<int> SelectedDisplayIndex { get; set; } = new(DisplayServer.WindowGetCurrentScreen());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really think the default should be -1 and it should mean "auto" as on non-Windows platform it's going to be annoying if the game forces itself on a specific screen.


/// <summary>
/// Upscaling method to use when the render scale is less than 1
/// </summary>
Expand Down Expand Up @@ -1026,6 +1034,32 @@ public void ApplyInputSettings()
/// </summary>
public void ApplyWindowSettings()
{
var screenId = -1;
bool isOldSelectionDifferent = false;
if (OperatingSystem.IsWindows())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this only checking for Windows? Shouldn't this work on any desktop OS?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought the original issue mentioned Windows; it would be a Windows specific operation. I think it would be good to remove it too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue originally mentions Windows, because players on Mac and Linux already have features in their operating system to fix the problem of the game on the wrong screen themselves. Only people who still use Windows have the problem, so the "fix" is mainly aimed at them. However the feature should still be quite workable on Mac and Linux as well, as long as the default behaviour is "auto" and not changing the game monitor.

{
var currentScreenId = DisplayServer.WindowGetCurrentScreen();
if (oldDisplaySelectionIndex == -1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the use for this old selection index? Cannot this just call WindowGetCurrentScreen and if the result is not what is desired, then call WindowSetCurrentScreen?

{
// If the old selection index is -1, then we have never set it before, so we set it to the current screen
oldDisplaySelectionIndex = currentScreenId;
}

screenId = SelectedDisplayIndex.Value;
isOldSelectionDifferent = screenId != currentScreenId && screenId != oldDisplaySelectionIndex;
if (isOldSelectionDifferent)
{
DisplayServer.WindowSetCurrentScreen(screenId);
}
else
{
// Game might have opened in another screen and moved to the current screen,
// so we need to update the old selection index to the current screen id
oldDisplaySelectionIndex = currentScreenId;
isOldSelectionDifferent = screenId == oldDisplaySelectionIndex;
}
}

var mode = DisplayServer.WindowGetMode();

// Treat maximized and windowed as the same thing to not reset maximized status after the user has set it
Expand Down Expand Up @@ -1059,12 +1093,24 @@ public void ApplyWindowSettings()
break;
}

if (mode != wantedMode)
var windowModeChanged = mode != wantedMode;
if (windowModeChanged)
{
GD.Print($"Switching window mode from {mode} to {wantedMode}");
DisplayServer.WindowSetMode(wantedMode);
}

// Center the window if it is in Windowed mode and the screen or window mode has changed
if (screenId > -1 && (isOldSelectionDifferent || windowModeChanged) && wantedMode == DisplayServer.WindowMode.Windowed)
{
var size = DisplayServer.ScreenGetSize(screenId);
var position = DisplayServer.ScreenGetPosition(screenId);
var windowSize = DisplayServer.WindowGetSize();
var centeredPos = position + (size - windowSize) / 2;
DisplayServer.WindowSetPosition(centeredPos, 0);
oldDisplaySelectionIndex = screenId;
}

// TODO: switch the setting to allow specifying all of the 4 possible values
DisplayServer.WindowSetVsyncMode(VSync.Value ?
DisplayServer.VSyncMode.Enabled :
Expand Down
61 changes: 61 additions & 0 deletions src/gui_common/menus/OptionsMenu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ public partial class OptionsMenu : ControlWithInput
.GetOutputDeviceList().Where(d => d != Constants.DEFAULT_AUDIO_OUTPUT_DEVICE_NAME)
.Prepend(Constants.DEFAULT_AUDIO_OUTPUT_DEVICE_NAME).ToList();

private static readonly Lazy<List<DisplayInfo>> DisplaysCache = new(() =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be static because attaching new monitors should work without having to restart the game...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hadn't thought about that. Thanks.

{
if (OperatingSystem.IsWindows())
{
return Enumerable.Range(0, DisplayServer.GetScreenCount())
.Select(i => new DisplayInfo(i, $"Monitor {i + 1}")).ToList();
}

return [];
});

#pragma warning disable CA2213
[Export]
private Button backButton = null!;
Expand Down Expand Up @@ -69,6 +80,12 @@ public partial class OptionsMenu : ControlWithInput
[Export]
private CheckButton vsync = null!;

[Export]
private VBoxContainer displayOptionContainer = null!;

[Export]
private OptionButton display = null!;

[Export]
private Label? resolution;

Expand Down Expand Up @@ -479,6 +496,7 @@ public enum OptionsTab

private static List<string> Languages => LanguagesCache.Value;
private static List<string> AudioOutputDevices => AudioOutputDevicesCache;
private static List<DisplayInfo> Displays => DisplaysCache.Value;

public override void _Ready()
{
Expand Down Expand Up @@ -523,6 +541,7 @@ public override void _EnterTree()
guiLightEffectsToggle.RegisterToolTipForControl("guiLightEffects", "options", false);
assumeHyperthreading.RegisterToolTipForControl("assumeHyperthreading", "options", false);
unsavedProgressWarningEnabled.RegisterToolTipForControl("unsavedProgressWarning", "options", false);
displayOptionContainer.Visible = OperatingSystem.IsWindows();

Localization.Instance.OnTranslationsChanged += OnTranslationsChanged;
}
Expand Down Expand Up @@ -652,6 +671,7 @@ public void ApplySettingsToControls(Settings settings)
DisplayGpuInfo();
UpdateRenderScale();
UpdateMSAAVisibility();
UpdateDisplay();

// Sound
masterVolume.Value = ConvertDbToSoundBar(settings.VolumeMaster);
Expand Down Expand Up @@ -806,6 +826,7 @@ private void InitializeOptionsSelections()

elementItemSelectionsInitialized = true;

DisplayDisplayList();
LoadLanguages();
LoadAudioOutputDevices();
LoadScreenEffects();
Expand Down Expand Up @@ -843,6 +864,18 @@ private void SwitchMode(OptionsMode mode)
}
}

/// <summary>
/// Displays the list of monitors available on the system.
/// </summary>
private void DisplayDisplayList()
{
display.Clear();
foreach (var displayInfo in Displays)
{
display.AddItem(displayInfo.Name, displayInfo.Id);
}
}

/// <summary>
/// Displays the current viewport resolution
/// </summary>
Expand Down Expand Up @@ -1788,6 +1821,16 @@ Settings.Instance.AntiAliasing.Value is Settings.AntiAliasingMode.MSAA
or Settings.AntiAliasingMode.MSAAAndTemporal;
}

private void UpdateDisplay()
{
if (!OperatingSystem.IsWindows())
return;

var currentDisplay = DisplayServer.WindowGetCurrentScreen();
display.Selected = currentDisplay;
Settings.Instance.SelectedDisplayIndex.Value = currentDisplay;
}

private void OnMSAAResolutionSelected(int index)
{
Settings.Instance.MSAAResolution.Value = MSAAIndexToResolution(index);
Expand Down Expand Up @@ -1828,6 +1871,22 @@ private void UpdateRenderScale()
Localization.Translate("PERCENTAGE_VALUE").FormatSafe(Math.Round(renderScale.Value * 100));
}

private void OnMonitorSelected(int index)
{
if (index < 0 || index >= Displays.Count)
{
GD.PrintErr("Invalid monitor index selected");
return;
}

if (Settings.Instance.SelectedDisplayIndex.Value == Displays[index].Id)
return;

Settings.Instance.SelectedDisplayIndex.Value = Displays[index].Id;
Settings.Instance.ApplyWindowSettings();
UpdateResetSaveButtonState();
}

private void OnUpscalingMethodSelected(int index)
{
Settings.Instance.UpscalingMethod.Value = UpscalingMethodIndexToValue(index);
Expand Down Expand Up @@ -2736,4 +2795,6 @@ private void ConfirmResetAchievements()

AchievementsManager.Instance.Reset();
}

private record DisplayInfo(int Id, string Name);
}
30 changes: 29 additions & 1 deletion src/gui_common/menus/OptionsMenu.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ region_rect = Rect2(0, 0, 258, 1)

[sub_resource type="StyleBoxEmpty" id="7"]

[node name="OptionsMenu" type="Control" node_paths=PackedStringArray("backButton", "resetButton", "saveButton", "tabButtons", "graphicsButton", "soundButton", "performanceButton", "inputsButton", "miscButton", "graphicsTab", "vsync", "resolution", "displayMode", "antiAliasingMode", "msaaSection", "msaaResolution", "renderScale", "renderScaleLabel", "upscalingMethod", "upscalingSharpening", "maxFramesPerSecond", "colourblindSetting", "chromaticAberrationToggle", "chromaticAberrationSlider", "controllerPromptType", "displayAbilitiesHotBarToggle", "anisotropicFilterLevel", "damageEffect", "strainVisibility", "displayBackgroundParticlesToggle", "guiLightEffectsToggle", "displayPartNamesToggle", "displayMenu3DBackgroundsToggle", "displayMicrobeBackgroundDistortionToggle", "lowQualityBackgroundBlurToggle", "bloomEffectToggle", "bloomSlider", "blurSlider", "gpuName", "usedRendererName", "videoMemory", "soundTab", "masterVolume", "masterMuted", "musicVolume", "musicMuted", "ambianceVolume", "ambianceMuted", "sfxVolume", "sfxMuted", "guiVolume", "guiMuted", "audioOutputDeviceSelection", "languageSelection", "resetLanguageButton", "languageProgressLabel", "performanceTab", "cloudInterval", "cloudResolutionTitle", "cloudResolution", "runAutoEvoDuringGameplay", "runGameSimulationMultithreaded", "detectedCPUCount", "activeThreadCount", "assumeHyperthreading", "useManualThreadCount", "threadCountSlider", "useManualNativeThreadCount", "nativeThreadCountSlider", "maxSpawnedEntities", "useDiskCaching", "maxCacheSizeSlider", "maxCacheSizeLabel", "currentCacheSize", "maxMemoryCacheTimeSlider", "maxMemoryCacheTimeLabel", "maxDiskCacheTimeSlider", "maxDiskCacheTimeLabel", "maxMemoryItemsSlider", "maxMemoryItemsLabel", "maxMemoryOnlyCacheTimeSlider", "maxMemoryOnlyCacheTimeLabel", "inputsTab", "mouseAxisSensitivitiesBound", "mouseHorizontalSensitivity", "mouseHorizontalInverted", "mouseVerticalSensitivity", "mouseVerticalInverted", "mouseWindowSizeScaling", "mouseWindowSizeScalingWithLogicalSize", "controllerAxisSensitivitiesBound", "controllerHorizontalSensitivity", "controllerHorizontalInverted", "controllerVerticalSensitivity", "controllerVerticalInverted", "twoDimensionalMovement", "threeDimensionalMovement", "mouseEdgePanEnabled", "mouseEdgePanSensitivity", "inputGroupList", "deadzoneConfigurationPopup", "miscTab", "playIntro", "playMicrobeIntro", "cheats", "tutorialsEnabledOnNewGame", "autoSave", "maxAutoSaves", "maxQuickSaves", "customUsernameEnabled", "customUsername", "webFeedsEnabled", "microbeRippleEffect", "microbeCameraTilt", "showNewPatchNotes", "dismissedNoticeCount", "jsonDebugMode", "screenEffectSelect", "commitLabel", "builtAtLabel", "tutorialsEnabled", "tutorialsEnabledContainer", "unsavedProgressWarningEnabled", "resetAchievementsButton", "screenshotDirectoryWarningBox", "backConfirmationBox", "defaultsConfirmationBox", "confirmAchievementsReset", "errorAcceptBox", "patchNotesBox", "patchNotesDisplayer")]
[node name="OptionsMenu" type="Control" node_paths=PackedStringArray("backButton", "resetButton", "saveButton", "tabButtons", "graphicsButton", "soundButton", "performanceButton", "inputsButton", "miscButton", "graphicsTab", "vsync", "displayOptionContainer", "display", "resolution", "displayMode", "antiAliasingMode", "msaaSection", "msaaResolution", "renderScale", "renderScaleLabel", "upscalingMethod", "upscalingSharpening", "maxFramesPerSecond", "colourblindSetting", "chromaticAberrationToggle", "chromaticAberrationSlider", "controllerPromptType", "displayAbilitiesHotBarToggle", "anisotropicFilterLevel", "damageEffect", "strainVisibility", "displayBackgroundParticlesToggle", "guiLightEffectsToggle", "displayPartNamesToggle", "displayMenu3DBackgroundsToggle", "displayMicrobeBackgroundDistortionToggle", "lowQualityBackgroundBlurToggle", "bloomEffectToggle", "bloomSlider", "blurSlider", "gpuName", "usedRendererName", "videoMemory", "soundTab", "masterVolume", "masterMuted", "musicVolume", "musicMuted", "ambianceVolume", "ambianceMuted", "sfxVolume", "sfxMuted", "guiVolume", "guiMuted", "audioOutputDeviceSelection", "languageSelection", "resetLanguageButton", "languageProgressLabel", "performanceTab", "cloudInterval", "cloudResolutionTitle", "cloudResolution", "runAutoEvoDuringGameplay", "runGameSimulationMultithreaded", "detectedCPUCount", "activeThreadCount", "assumeHyperthreading", "useManualThreadCount", "threadCountSlider", "useManualNativeThreadCount", "nativeThreadCountSlider", "maxSpawnedEntities", "useDiskCaching", "maxCacheSizeSlider", "maxCacheSizeLabel", "currentCacheSize", "maxMemoryCacheTimeSlider", "maxMemoryCacheTimeLabel", "maxDiskCacheTimeSlider", "maxDiskCacheTimeLabel", "maxMemoryItemsSlider", "maxMemoryItemsLabel", "maxMemoryOnlyCacheTimeSlider", "maxMemoryOnlyCacheTimeLabel", "inputsTab", "mouseAxisSensitivitiesBound", "mouseHorizontalSensitivity", "mouseHorizontalInverted", "mouseVerticalSensitivity", "mouseVerticalInverted", "mouseWindowSizeScaling", "mouseWindowSizeScalingWithLogicalSize", "controllerAxisSensitivitiesBound", "controllerHorizontalSensitivity", "controllerHorizontalInverted", "controllerVerticalSensitivity", "controllerVerticalInverted", "twoDimensionalMovement", "threeDimensionalMovement", "mouseEdgePanEnabled", "mouseEdgePanSensitivity", "inputGroupList", "deadzoneConfigurationPopup", "miscTab", "playIntro", "playMicrobeIntro", "cheats", "tutorialsEnabledOnNewGame", "autoSave", "maxAutoSaves", "maxQuickSaves", "customUsernameEnabled", "customUsername", "webFeedsEnabled", "microbeRippleEffect", "microbeCameraTilt", "showNewPatchNotes", "dismissedNoticeCount", "jsonDebugMode", "screenEffectSelect", "commitLabel", "builtAtLabel", "tutorialsEnabled", "tutorialsEnabledContainer", "unsavedProgressWarningEnabled", "resetAchievementsButton", "screenshotDirectoryWarningBox", "backConfirmationBox", "defaultsConfirmationBox", "confirmAchievementsReset", "errorAcceptBox", "patchNotesBox", "patchNotesDisplayer")]
process_mode = 3
layout_mode = 3
anchors_preset = 15
Expand All @@ -57,6 +57,8 @@ inputsButton = NodePath("CenterContainer/VBoxContainer/TabButtons/InputsButton")
miscButton = NodePath("CenterContainer/VBoxContainer/TabButtons/MiscButton")
graphicsTab = NodePath("CenterContainer/VBoxContainer/VBoxContainer/VBoxContainer/PanelContainer/Graphics")
vsync = NodePath("CenterContainer/VBoxContainer/VBoxContainer/VBoxContainer/PanelContainer/Graphics/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer2/HBoxContainer/Vsync")
displayOptionContainer = NodePath("CenterContainer/VBoxContainer/VBoxContainer/VBoxContainer/PanelContainer/Graphics/ScrollContainer/MarginContainer/VBoxContainer/DisplayOptionContainer")
display = NodePath("CenterContainer/VBoxContainer/VBoxContainer/VBoxContainer/PanelContainer/Graphics/ScrollContainer/MarginContainer/VBoxContainer/DisplayOptionContainer/HBoxContainer/Display")
resolution = NodePath("CenterContainer/VBoxContainer/VBoxContainer/VBoxContainer/PanelContainer/Graphics/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer7/HBoxContainer/Resolution")
displayMode = NodePath("CenterContainer/VBoxContainer/VBoxContainer/VBoxContainer/PanelContainer/Graphics/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer3/HBoxContainer/DisplayMode")
antiAliasingMode = NodePath("CenterContainer/VBoxContainer/VBoxContainer/VBoxContainer/PanelContainer/Graphics/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer4/HBoxContainer/AntiAliasingMode")
Expand Down Expand Up @@ -263,6 +265,7 @@ layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
follow_focus = true
scroll_vertical = 100
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one line chagne seems suspicious to me...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know. Whenever I open the OptionsMenu scene, Godot just changes this line for me. I always revert it but looks like I missed this one.


[node name="MarginContainer" type="MarginContainer" parent="CenterContainer/VBoxContainer/VBoxContainer/VBoxContainer/PanelContainer/Graphics/ScrollContainer"]
layout_mode = 2
Expand Down Expand Up @@ -466,6 +469,30 @@ layout_mode = 2
custom_minimum_size = Vector2(0, 5)
layout_mode = 2

[node name="DisplayOptionContainer" type="VBoxContainer" parent="CenterContainer/VBoxContainer/VBoxContainer/VBoxContainer/PanelContainer/Graphics/ScrollContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3

[node name="HBoxContainer" type="HBoxContainer" parent="CenterContainer/VBoxContainer/VBoxContainer/VBoxContainer/PanelContainer/Graphics/ScrollContainer/MarginContainer/VBoxContainer/DisplayOptionContainer"]
layout_mode = 2

[node name="Label" type="Label" parent="CenterContainer/VBoxContainer/VBoxContainer/VBoxContainer/PanelContainer/Graphics/ScrollContainer/MarginContainer/VBoxContainer/DisplayOptionContainer/HBoxContainer"]
custom_minimum_size = Vector2(250, 0)
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
size_flags_stretch_ratio = 3.0
mouse_filter = 0
text = "DISPLAY"
autowrap_mode = 2

[node name="Display" type="OptionButton" parent="CenterContainer/VBoxContainer/VBoxContainer/VBoxContainer/PanelContainer/Graphics/ScrollContainer/MarginContainer/VBoxContainer/DisplayOptionContainer/HBoxContainer"]
custom_minimum_size = Vector2(200, 25)
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
autowrap_mode = 2

[node name="VBoxContainer7" type="VBoxContainer" parent="CenterContainer/VBoxContainer/VBoxContainer/VBoxContainer/PanelContainer/Graphics/ScrollContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
Expand Down Expand Up @@ -3393,6 +3420,7 @@ grow_vertical = 1
[connection signal="item_selected" from="CenterContainer/VBoxContainer/VBoxContainer/VBoxContainer/PanelContainer/Graphics/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer4/HBoxContainer/AntiAliasingMode" to="." method="OnAntiAliasingModeSelected"]
[connection signal="item_selected" from="CenterContainer/VBoxContainer/VBoxContainer/VBoxContainer/PanelContainer/Graphics/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer5/HBoxContainer/MSAAResolution" to="." method="OnMSAAResolutionSelected"]
[connection signal="item_selected" from="CenterContainer/VBoxContainer/VBoxContainer/VBoxContainer/PanelContainer/Graphics/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer6/HBoxContainer/AnisotropicFilterLevel" to="." method="OnAnisotropicFilteringSelected"]
[connection signal="item_selected" from="CenterContainer/VBoxContainer/VBoxContainer/VBoxContainer/PanelContainer/Graphics/ScrollContainer/MarginContainer/VBoxContainer/DisplayOptionContainer/HBoxContainer/Display" to="." method="OnMonitorSelected"]
[connection signal="value_changed" from="CenterContainer/VBoxContainer/VBoxContainer/VBoxContainer/PanelContainer/Graphics/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer8/HBoxContainer/RenderScale" to="." method="OnRenderScaleChanged"]
[connection signal="item_selected" from="CenterContainer/VBoxContainer/VBoxContainer/VBoxContainer/PanelContainer/Graphics/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer9/HBoxContainer/UpscaleMethod" to="." method="OnUpscalingMethodSelected"]
[connection signal="value_changed" from="CenterContainer/VBoxContainer/VBoxContainer/VBoxContainer/PanelContainer/Graphics/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer10/HBoxContainer/Sharpening" to="." method="OnUpscalingSharpeningChanged"]
Expand Down