diff --git a/Dalamud.Boot/xivfixes.cpp b/Dalamud.Boot/xivfixes.cpp index c37511d83..21786a4c1 100644 --- a/Dalamud.Boot/xivfixes.cpp +++ b/Dalamud.Boot/xivfixes.cpp @@ -204,17 +204,6 @@ void xivfixes::prevent_devicechange_crashes(bool bApply) { ) 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(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); }); diff --git a/Dalamud/Configuration/Internal/DalamudConfiguration.cs b/Dalamud/Configuration/Internal/DalamudConfiguration.cs index cce922697..a8e598e08 100644 --- a/Dalamud/Configuration/Internal/DalamudConfiguration.cs +++ b/Dalamud/Configuration/Internal/DalamudConfiguration.cs @@ -346,6 +346,11 @@ internal sealed class DalamudConfiguration : IServiceType /// public List? PluginTestingOptIns { get; set; } + /// + /// Gets or sets a value indicating whether the FFXIV window should be toggled to immersive mode. + /// + public bool WindowIsImmersive { get; set; } = false; + /// /// Load a configuration from the provided path. /// diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs index 9e08d5920..2d4b979dd 100644 --- a/Dalamud/Interface/Internal/InterfaceManager.cs +++ b/Dalamud/Interface/Internal/InterfaceManager.cs @@ -435,6 +435,23 @@ internal class InterfaceManager : IDisposable, IServiceType return null; } + /// + /// Toggle Windows 11 immersive mode on the game window. + /// + /// Value. + 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) { 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; } + try + { + if (Service.Get().WindowIsImmersive) + this.SetImmersiveMode(true); + } + catch (Exception ex) + { + Log.Error(ex, "Could not enable immersive mode"); + } + this.presentHook = Hook.FromAddress(this.address.Present, this.PresentDetour); this.resizeBuffersHook = Hook.FromAddress(this.address.ResizeBuffers, this.ResizeBuffersDetour); diff --git a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabLook.cs b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabLook.cs index 8b9ef9952..c722807dd 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabLook.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabLook.cs @@ -1,10 +1,13 @@ -using System.Diagnostics.CodeAnalysis; +using System; +using System.Diagnostics.CodeAnalysis; using CheapLoc; using Dalamud.Configuration.Internal; using Dalamud.Interface.Colors; using Dalamud.Interface.Internal.Windows.Settings.Widgets; +using Dalamud.Utility; using ImGuiNET; +using Serilog; namespace Dalamud.Interface.Internal.Windows.Settings.Tabs; @@ -18,13 +21,6 @@ public class SettingsTabLook : SettingsTab { new GapSettingsEntry(5), - new ButtonSettingsEntry( - Loc.Localize("DalamudSettingsOpenStyleEditor", "Open Style Editor"), - Loc.Localize("DalamudSettingsStyleEditorHint", "Modify the look & feel of Dalamud windows."), - () => Service.Get().OpenStyleEditor()), - - new GapSettingsEntry(5), - new SettingsEntry( 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."), @@ -39,6 +35,31 @@ public class SettingsTabLook : SettingsTab new GapSettingsEntry(5, true), + new ButtonSettingsEntry( + Loc.Localize("DalamudSettingsOpenStyleEditor", "Open Style Editor"), + Loc.Localize("DalamudSettingsStyleEditorHint", "Modify the look & feel of Dalamud windows."), + () => Service.Get().OpenStyleEditor()), + + new SettingsEntry( + 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.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 GapSettingsEntry(3), diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/SettingsEntry{T}.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/SettingsEntry{T}.cs index c08eb99f6..b5fada8fd 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/SettingsEntry{T}.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/SettingsEntry{T}.cs @@ -21,7 +21,7 @@ internal sealed class SettingsEntry : SettingsEntry private object? valueBacking; - public SettingsEntry(string name, string description, LoadSettingDelegate load, SaveSettingDelegate save, Action? change = null, Func? warning = null, Func? validity = null) + public SettingsEntry(string name, string description, LoadSettingDelegate load, SaveSettingDelegate save, Action? change = null, Func? warning = null, Func? validity = null, Func? visibility = null) { this.load = load; this.save = save; @@ -30,6 +30,7 @@ internal sealed class SettingsEntry : SettingsEntry this.Description = description; this.CheckWarning = warning; this.CheckValidity = validity; + this.CheckVisibility = visibility; } public delegate T? LoadSettingDelegate(DalamudConfiguration config); diff --git a/Dalamud/NativeFunctions.cs b/Dalamud/NativeFunctions.cs index b05aecc7a..1ed05b8ba 100644 --- a/Dalamud/NativeFunctions.cs +++ b/Dalamud/NativeFunctions.cs @@ -1956,3 +1956,31 @@ internal static partial class NativeFunctions [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); } + +/// +/// Native dwmapi functions. +/// +internal static partial class NativeFunctions +{ + /// + /// Attributes for use with DwmSetWindowAttribute. + /// + public enum DWMWINDOWATTRIBUTE : int + { + /// + /// Allows the window frame for this window to be drawn in dark mode colors when the dark mode system setting is enabled. + /// + DWMWA_USE_IMMERSIVE_DARK_MODE = 20, + } + + /// + /// Sets the value of Desktop Window Manager (DWM) non-client rendering attributes for a window. + /// + /// The handle to the window for which the attribute value is to be set. + /// The attribute to be set. + /// The value of the attribute. + /// The size of the attribute. + /// HRESULT. + [DllImport("dwmapi.dll", PreserveSig = true)] + public static extern int DwmSetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE attr, ref int attrValue, int attrSize); +} diff --git a/Dalamud/Utility/Util.cs b/Dalamud/Utility/Util.cs index eba6b562f..5fb2784af 100644 --- a/Dalamud/Utility/Util.cs +++ b/Dalamud/Utility/Util.cs @@ -514,6 +514,12 @@ public static class Util return Check1() || Check2() || Check3(); } + /// + /// Heuristically determine if the Windows version is higher than Windows 11's first build. + /// + /// If Windows 11 has been detected. + public static bool IsWindows11() => Environment.OSVersion.Version.Build >= 22000; + /// /// Open a link in the default browser. ///