feat: make Windows 11 immersive mode configurable

This commit is contained in:
goat 2023-02-19 13:27:58 +01:00
parent 9c321b2c05
commit 1e40cabdc6
No known key found for this signature in database
GPG key ID: 49E2AA8C6A76498B
7 changed files with 97 additions and 20 deletions

View file

@ -204,17 +204,6 @@ void xivfixes::prevent_devicechange_crashes(bool bApply) {
) )
return ShowWindow(hWnd, SW_MAXIMIZE); return ShowWindow(hWnd, SW_MAXIMIZE);
if (uMsg == WM_CREATE) {
// While at it, also apply dark mode title bar to the game.
if (const auto dwmapi = LoadLibraryW(L"dwmapi.dll")) {
if (const auto fn = reinterpret_cast<decltype(&DwmSetWindowAttribute)>(GetProcAddress(dwmapi, "DwmSetWindowAttribute"))) {
const BOOL trueValue = TRUE;
fn(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &trueValue, sizeof trueValue);
}
FreeLibrary(dwmapi);
}
}
return s_pfnGameWndProc(hWnd, uMsg, wParam, lParam); return s_pfnGameWndProc(hWnd, uMsg, wParam, lParam);
}); });

View file

@ -346,6 +346,11 @@ internal sealed class DalamudConfiguration : IServiceType
/// </summary> /// </summary>
public List<PluginTestingOptIn>? PluginTestingOptIns { get; set; } public List<PluginTestingOptIn>? PluginTestingOptIns { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the FFXIV window should be toggled to immersive mode.
/// </summary>
public bool WindowIsImmersive { get; set; } = false;
/// <summary> /// <summary>
/// Load a configuration from the provided path. /// Load a configuration from the provided path.
/// </summary> /// </summary>

View file

@ -435,6 +435,23 @@ internal class InterfaceManager : IDisposable, IServiceType
return null; return null;
} }
/// <summary>
/// Toggle Windows 11 immersive mode on the game window.
/// </summary>
/// <param name="enabled">Value.</param>
internal void SetImmersiveMode(bool enabled)
{
if (this.GameWindowHandle == nint.Zero)
return;
int value = enabled ? 1 : 0;
var hr = NativeFunctions.DwmSetWindowAttribute(
this.GameWindowHandle,
NativeFunctions.DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE,
ref value,
sizeof(int));
}
private static void ShowFontError(string path) private static void ShowFontError(string path)
{ {
Util.Fatal($"One or more files required by XIVLauncher were not found.\nPlease restart and report this error if it occurs again.\n\n{path}", "Error"); Util.Fatal($"One or more files required by XIVLauncher were not found.\nPlease restart and report this error if it occurs again.\n\n{path}", "Error");
@ -974,6 +991,16 @@ internal class InterfaceManager : IDisposable, IServiceType
break; break;
} }
try
{
if (Service<DalamudConfiguration>.Get().WindowIsImmersive)
this.SetImmersiveMode(true);
}
catch (Exception ex)
{
Log.Error(ex, "Could not enable immersive mode");
}
this.presentHook = Hook<PresentDelegate>.FromAddress(this.address.Present, this.PresentDetour); this.presentHook = Hook<PresentDelegate>.FromAddress(this.address.Present, this.PresentDetour);
this.resizeBuffersHook = Hook<ResizeBuffersDelegate>.FromAddress(this.address.ResizeBuffers, this.ResizeBuffersDetour); this.resizeBuffersHook = Hook<ResizeBuffersDelegate>.FromAddress(this.address.ResizeBuffers, this.ResizeBuffersDetour);

View file

@ -1,10 +1,13 @@
using System.Diagnostics.CodeAnalysis; using System;
using System.Diagnostics.CodeAnalysis;
using CheapLoc; using CheapLoc;
using Dalamud.Configuration.Internal; using Dalamud.Configuration.Internal;
using Dalamud.Interface.Colors; using Dalamud.Interface.Colors;
using Dalamud.Interface.Internal.Windows.Settings.Widgets; using Dalamud.Interface.Internal.Windows.Settings.Widgets;
using Dalamud.Utility;
using ImGuiNET; using ImGuiNET;
using Serilog;
namespace Dalamud.Interface.Internal.Windows.Settings.Tabs; namespace Dalamud.Interface.Internal.Windows.Settings.Tabs;
@ -18,13 +21,6 @@ public class SettingsTabLook : SettingsTab
{ {
new GapSettingsEntry(5), new GapSettingsEntry(5),
new ButtonSettingsEntry(
Loc.Localize("DalamudSettingsOpenStyleEditor", "Open Style Editor"),
Loc.Localize("DalamudSettingsStyleEditorHint", "Modify the look & feel of Dalamud windows."),
() => Service<DalamudInterface>.Get().OpenStyleEditor()),
new GapSettingsEntry(5),
new SettingsEntry<bool>( new SettingsEntry<bool>(
Loc.Localize("DalamudSettingToggleAxisFonts", "Use AXIS fonts as default Dalamud font"), Loc.Localize("DalamudSettingToggleAxisFonts", "Use AXIS fonts as default Dalamud font"),
Loc.Localize("DalamudSettingToggleUiAxisFontsHint", "Use AXIS fonts (the game's main UI fonts) as default Dalamud font."), Loc.Localize("DalamudSettingToggleUiAxisFontsHint", "Use AXIS fonts (the game's main UI fonts) as default Dalamud font."),
@ -39,6 +35,31 @@ public class SettingsTabLook : SettingsTab
new GapSettingsEntry(5, true), new GapSettingsEntry(5, true),
new ButtonSettingsEntry(
Loc.Localize("DalamudSettingsOpenStyleEditor", "Open Style Editor"),
Loc.Localize("DalamudSettingsStyleEditorHint", "Modify the look & feel of Dalamud windows."),
() => Service<DalamudInterface>.Get().OpenStyleEditor()),
new SettingsEntry<bool>(
Loc.Localize("DalamudSettingsUseDarkMode", "Use Windows immersive/dark mode"),
Loc.Localize("DalamudSettingsUseDarkModeHint", "This will cause the FFXIV window title bar to follow your preferred Windows color settings, and switch to dark mode if enabled."),
c => c.WindowIsImmersive,
(v, c) => c.WindowIsImmersive = v,
b =>
{
try
{
Service<InterfaceManager>.GetNullable()?.SetImmersiveMode(b);
}
catch (Exception ex)
{
Log.Error(ex, "Could not toggle immersive mode");
}
},
visibility: Util.IsWindows11),
new GapSettingsEntry(5, true),
new HintSettingsEntry(Loc.Localize("DalamudSettingToggleUiHideOptOutNote", "Plugins may independently opt out of the settings below.")), new HintSettingsEntry(Loc.Localize("DalamudSettingToggleUiHideOptOutNote", "Plugins may independently opt out of the settings below.")),
new GapSettingsEntry(3), new GapSettingsEntry(3),

View file

@ -21,7 +21,7 @@ internal sealed class SettingsEntry<T> : SettingsEntry
private object? valueBacking; private object? valueBacking;
public SettingsEntry(string name, string description, LoadSettingDelegate load, SaveSettingDelegate save, Action<T?>? change = null, Func<T?, string?>? warning = null, Func<T?, string?>? validity = null) public SettingsEntry(string name, string description, LoadSettingDelegate load, SaveSettingDelegate save, Action<T?>? change = null, Func<T?, string?>? warning = null, Func<T?, string?>? validity = null, Func<bool>? visibility = null)
{ {
this.load = load; this.load = load;
this.save = save; this.save = save;
@ -30,6 +30,7 @@ internal sealed class SettingsEntry<T> : SettingsEntry
this.Description = description; this.Description = description;
this.CheckWarning = warning; this.CheckWarning = warning;
this.CheckValidity = validity; this.CheckValidity = validity;
this.CheckVisibility = visibility;
} }
public delegate T? LoadSettingDelegate(DalamudConfiguration config); public delegate T? LoadSettingDelegate(DalamudConfiguration config);

View file

@ -1956,3 +1956,31 @@ internal static partial class NativeFunctions
[DllImport("ws2_32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "setsockopt")] [DllImport("ws2_32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "setsockopt")]
public static extern int SetSockOpt(IntPtr socket, SocketOptionLevel level, SocketOptionName optName, ref IntPtr optVal, int optLen); public static extern int SetSockOpt(IntPtr socket, SocketOptionLevel level, SocketOptionName optName, ref IntPtr optVal, int optLen);
} }
/// <summary>
/// Native dwmapi functions.
/// </summary>
internal static partial class NativeFunctions
{
/// <summary>
/// Attributes for use with DwmSetWindowAttribute.
/// </summary>
public enum DWMWINDOWATTRIBUTE : int
{
/// <summary>
/// Allows the window frame for this window to be drawn in dark mode colors when the dark mode system setting is enabled.
/// </summary>
DWMWA_USE_IMMERSIVE_DARK_MODE = 20,
}
/// <summary>
/// Sets the value of Desktop Window Manager (DWM) non-client rendering attributes for a window.
/// </summary>
/// <param name="hwnd">The handle to the window for which the attribute value is to be set.</param>
/// <param name="attr">The attribute to be set.</param>
/// <param name="attrValue">The value of the attribute.</param>
/// <param name="attrSize">The size of the attribute.</param>
/// <returns>HRESULT.</returns>
[DllImport("dwmapi.dll", PreserveSig = true)]
public static extern int DwmSetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE attr, ref int attrValue, int attrSize);
}

View file

@ -514,6 +514,12 @@ public static class Util
return Check1() || Check2() || Check3(); return Check1() || Check2() || Check3();
} }
/// <summary>
/// Heuristically determine if the Windows version is higher than Windows 11's first build.
/// </summary>
/// <returns>If Windows 11 has been detected.</returns>
public static bool IsWindows11() => Environment.OSVersion.Version.Build >= 22000;
/// <summary> /// <summary>
/// Open a link in the default browser. /// Open a link in the default browser.
/// </summary> /// </summary>