WindowsMultimediaTimerResolution.cs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. using Ryujinx.Common.Logging;
  2. using System;
  3. using System.Runtime.CompilerServices;
  4. using System.Runtime.InteropServices;
  5. using System.Runtime.Versioning;
  6. namespace Ryujinx.Common.SystemInterop
  7. {
  8. /// <summary>
  9. /// Handle Windows Multimedia timer resolution.
  10. /// </summary>
  11. [SupportedOSPlatform("windows")]
  12. public partial class WindowsMultimediaTimerResolution : IDisposable
  13. {
  14. [StructLayout(LayoutKind.Sequential)]
  15. public struct TimeCaps
  16. {
  17. public uint wPeriodMin;
  18. public uint wPeriodMax;
  19. };
  20. [LibraryImport("winmm.dll", EntryPoint = "timeGetDevCaps", SetLastError = true)]
  21. private static partial uint TimeGetDevCaps(ref TimeCaps timeCaps, uint sizeTimeCaps);
  22. [LibraryImport("winmm.dll", EntryPoint = "timeBeginPeriod")]
  23. private static partial uint TimeBeginPeriod(uint uMilliseconds);
  24. [LibraryImport("winmm.dll", EntryPoint = "timeEndPeriod")]
  25. private static partial uint TimeEndPeriod(uint uMilliseconds);
  26. private uint _targetResolutionInMilliseconds;
  27. private bool _isActive;
  28. /// <summary>
  29. /// Create a new <see cref="WindowsMultimediaTimerResolution"/> and activate the given resolution.
  30. /// </summary>
  31. /// <param name="targetResolutionInMilliseconds"></param>
  32. public WindowsMultimediaTimerResolution(uint targetResolutionInMilliseconds)
  33. {
  34. _targetResolutionInMilliseconds = targetResolutionInMilliseconds;
  35. EnsureResolutionSupport();
  36. Activate();
  37. }
  38. private void EnsureResolutionSupport()
  39. {
  40. TimeCaps timeCaps = default;
  41. uint result = TimeGetDevCaps(ref timeCaps, (uint)Unsafe.SizeOf<TimeCaps>());
  42. if (result != 0)
  43. {
  44. Logger.Notice.Print(LogClass.Application, $"timeGetDevCaps failed with result: {result}");
  45. }
  46. else
  47. {
  48. uint supportedTargetResolutionInMilliseconds = Math.Min(Math.Max(timeCaps.wPeriodMin, _targetResolutionInMilliseconds), timeCaps.wPeriodMax);
  49. if (supportedTargetResolutionInMilliseconds != _targetResolutionInMilliseconds)
  50. {
  51. Logger.Notice.Print(LogClass.Application, $"Target resolution isn't supported by OS, using closest resolution: {supportedTargetResolutionInMilliseconds}ms");
  52. _targetResolutionInMilliseconds = supportedTargetResolutionInMilliseconds;
  53. }
  54. }
  55. }
  56. private void Activate()
  57. {
  58. uint result = TimeBeginPeriod(_targetResolutionInMilliseconds);
  59. if (result != 0)
  60. {
  61. Logger.Notice.Print(LogClass.Application, $"timeBeginPeriod failed with result: {result}");
  62. }
  63. else
  64. {
  65. _isActive = true;
  66. }
  67. }
  68. private void Disable()
  69. {
  70. if (_isActive)
  71. {
  72. uint result = TimeEndPeriod(_targetResolutionInMilliseconds);
  73. if (result != 0)
  74. {
  75. Logger.Notice.Print(LogClass.Application, $"timeEndPeriod failed with result: {result}");
  76. }
  77. else
  78. {
  79. _isActive = false;
  80. }
  81. }
  82. }
  83. public void Dispose()
  84. {
  85. Dispose(true);
  86. GC.SuppressFinalize(this);
  87. }
  88. protected virtual void Dispose(bool disposing)
  89. {
  90. if (disposing)
  91. {
  92. Disable();
  93. }
  94. }
  95. }
  96. }