diff --git a/PowerControl/External/RTSS.cs b/PowerControl/External/RTSS.cs new file mode 100644 index 0000000..5e4d658 --- /dev/null +++ b/PowerControl/External/RTSS.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace PowerControl.External +{ + internal static class RTSS + { + public static bool GetProfileProperty(string propertyName, out T value) + { + var bytes = new byte[Marshal.SizeOf()]; + var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); + value = default(T); + try + { + if (!GetProfileProperty(propertyName, handle.AddrOfPinnedObject(), (uint)bytes.Length)) + return false; + + value = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject()); + return true; + } + catch + { + return false; + } + finally + { + handle.Free(); + } + } + + public static bool SetProfileProperty(string propertyName, T value) + { + var bytes = new byte[Marshal.SizeOf()]; + var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); + try + { + Marshal.StructureToPtr(value, handle.AddrOfPinnedObject(), false); + return SetProfileProperty(propertyName, handle.AddrOfPinnedObject(), (uint)bytes.Length); + } + catch + { + return false; + } + finally + { + handle.Free(); + } + } + + [DllImport("kernel32.dll", EntryPoint = "GetModuleHandleW", SetLastError = true)] + public static extern IntPtr GetModuleHandle(string moduleName); + + [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)] + static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName); + + [return: MarshalAs(UnmanagedType.Bool)] + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll", SetLastError = true)] + static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + + [DllImport("C:\\Program Files (x86)\\RivaTuner Statistics Server\\RTSSHooks64.dll")] + public static extern uint SetFlags(uint dwAND, uint dwXOR); + + [DllImport("C:\\Program Files (x86)\\RivaTuner Statistics Server\\RTSSHooks64.dll", CharSet = CharSet.Ansi)] + public static extern void LoadProfile(string profile = GLOBAL_PROFILE); + + [DllImport("C:\\Program Files (x86)\\RivaTuner Statistics Server\\RTSSHooks64.dll", CharSet = CharSet.Ansi)] + public static extern void SaveProfile(string profile = GLOBAL_PROFILE); + + [DllImport("C:\\Program Files (x86)\\RivaTuner Statistics Server\\RTSSHooks64.dll", CharSet = CharSet.Ansi)] + public static extern void DeleteProfile(string profile = GLOBAL_PROFILE); + + [DllImport("C:\\Program Files (x86)\\RivaTuner Statistics Server\\RTSSHooks64.dll", CharSet = CharSet.Ansi)] + public static extern bool GetProfileProperty(string propertyName, IntPtr value, uint size); + + [DllImport("C:\\Program Files (x86)\\RivaTuner Statistics Server\\RTSSHooks64.dll", CharSet = CharSet.Ansi)] + public static extern bool SetProfileProperty(string propertyName, IntPtr value, uint size); + + [DllImport("C:\\Program Files (x86)\\RivaTuner Statistics Server\\RTSSHooks64.dll", CharSet = CharSet.Ansi)] + public static extern void ResetProfile(string profile = GLOBAL_PROFILE); + + [DllImport("C:\\Program Files (x86)\\RivaTuner Statistics Server\\RTSSHooks64.dll", CharSet = CharSet.Ansi)] + public static extern void UpdateProfiles(); + + private static void PostMessage(uint Msg, IntPtr wParam, IntPtr lParam) + { + var hWnd = FindWindow(null, "RTSS"); + if (hWnd == IntPtr.Zero) + hWnd = FindWindow(null, "RivaTuner Statistics Server"); + + if (hWnd != IntPtr.Zero) + PostMessage(hWnd, Msg, wParam, lParam); + } + + public static uint EnableFlag(uint flag, bool status) + { + var current = SetFlags(~flag, status ? flag : 0); + UpdateSettings(); + return current; + } + + public static void UpdateSettings() + { + PostMessage(WM_RTSS_UPDATESETTINGS, IntPtr.Zero, IntPtr.Zero); + } + + public const uint WM_APP = 0x8000; + public const uint WM_RTSS_UPDATESETTINGS = WM_APP + 100; + public const uint WM_RTSS_SHOW_PROPERTIES = WM_APP + 102; + + public const uint RTSSHOOKSFLAG_OSD_VISIBLE = 1; + public const uint RTSSHOOKSFLAG_LIMITER_DISABLED = 4; + public const string GLOBAL_PROFILE = ""; + } +} diff --git a/PowerControl/MenuStack.cs b/PowerControl/MenuStack.cs index 7bafdfc..803d617 100644 --- a/PowerControl/MenuStack.cs +++ b/PowerControl/MenuStack.cs @@ -1,4 +1,5 @@ using CommonHelpers; +using PowerControl.External; using System; using System.Collections.Generic; using System.Diagnostics; @@ -63,10 +64,59 @@ namespace PowerControl ApplyValue = delegate(object selected) { Helpers.PhysicalMonitorBrightnessController.SetRefreshRate((int)selected); + Root["FPS Limit"].Update(); // force refresh FPS limit return Helpers.PhysicalMonitorBrightnessController.GetRefreshRate(); } }, new Menu.MenuItemWithOptions() + { + Name = "FPS Limit", + ApplyDelay = 500, + OptionsValues = delegate() + { + var refreshRate = Helpers.PhysicalMonitorBrightnessController.GetRefreshRate(); + return new object[] + { + "Off", refreshRate, refreshRate / 2, refreshRate / 4 + }; + }, + CurrentValue = delegate() + { + try + { + RTSS.LoadProfile(); + if (RTSS.GetProfileProperty("FramerateLimit", out int framerate)) + return (framerate == 0) ? "Off" : framerate; + } + catch + { + } + return null; + }, + ApplyValue = delegate(object selected) + { + try + { + int framerate = 0; + if (selected != null && selected.ToString() != "Off") + framerate = (int)selected; + + RTSS.LoadProfile(); + if (!RTSS.SetProfileProperty("FramerateLimit", framerate)) + return null; + if (!RTSS.GetProfileProperty("FramerateLimit", out framerate)) + return null; + RTSS.SaveProfile(); + RTSS.UpdateProfiles(); + return (framerate == 0) ? "Off" : framerate; + } + catch + { + } + return null; + } + }, + new Menu.MenuItemWithOptions() { Name = "TDP", Options = { "Auto", "3W", "4W", "5W", "6W", "7W", "8W", "10W", "12W", "15W" }, diff --git a/images/power_control_2.png b/images/power_control_2.png new file mode 100644 index 0000000..4ffc313 Binary files /dev/null and b/images/power_control_2.png differ