2020-08-18 03:49:37 +02:00
using Ryujinx.Common.Logging ;
using System ;
using System.Runtime.CompilerServices ;
using System.Runtime.InteropServices ;
2021-12-05 00:02:30 +01:00
using System.Runtime.Versioning ;
2020-08-18 03:49:37 +02:00
namespace Ryujinx.Common.System
{
/// <summary>
/// Handle Windows Multimedia timer resolution.
/// </summary>
2021-12-05 00:02:30 +01:00
[SupportedOSPlatform("windows")]
2020-08-18 03:49:37 +02:00
public class WindowsMultimediaTimerResolution : IDisposable
{
[StructLayout(LayoutKind.Sequential)]
public struct TimeCaps
{
public uint wPeriodMin ;
public uint wPeriodMax ;
} ;
2021-01-08 09:14:13 +01:00
[DllImport("winmm.dll", EntryPoint = "timeGetDevCaps", SetLastError = true)]
private static extern uint TimeGetDevCaps ( ref TimeCaps timeCaps , uint sizeTimeCaps ) ;
2020-08-18 03:49:37 +02:00
2021-01-08 09:14:13 +01:00
[DllImport("winmm.dll", EntryPoint = "timeBeginPeriod")]
private static extern uint TimeBeginPeriod ( uint uMilliseconds ) ;
2020-08-18 03:49:37 +02:00
2021-01-08 09:14:13 +01:00
[DllImport("winmm.dll", EntryPoint = "timeEndPeriod")]
private static extern uint TimeEndPeriod ( uint uMilliseconds ) ;
2020-08-18 03:49:37 +02:00
private uint _targetResolutionInMilliseconds ;
private bool _isActive ;
/// <summary>
/// Create a new <see cref="WindowsMultimediaTimerResolution"/> and activate the given resolution.
/// </summary>
/// <param name="targetResolutionInMilliseconds"></param>
public WindowsMultimediaTimerResolution ( uint targetResolutionInMilliseconds )
{
_targetResolutionInMilliseconds = targetResolutionInMilliseconds ;
EnsureResolutionSupport ( ) ;
Activate ( ) ;
}
private void EnsureResolutionSupport ( )
{
TimeCaps timeCaps = default ;
2021-01-08 09:14:13 +01:00
uint result = TimeGetDevCaps ( ref timeCaps , ( uint ) Unsafe . SizeOf < TimeCaps > ( ) ) ;
2020-08-18 03:49:37 +02:00
if ( result ! = 0 )
{
Logger . Notice . Print ( LogClass . Application , $"timeGetDevCaps failed with result: {result}" ) ;
}
else
{
uint supportedTargetResolutionInMilliseconds = Math . Min ( Math . Max ( timeCaps . wPeriodMin , _targetResolutionInMilliseconds ) , timeCaps . wPeriodMax ) ;
if ( supportedTargetResolutionInMilliseconds ! = _targetResolutionInMilliseconds )
{
Logger . Notice . Print ( LogClass . Application , $"Target resolution isn't supported by OS, using closest resolution: {supportedTargetResolutionInMilliseconds}ms" ) ;
_targetResolutionInMilliseconds = supportedTargetResolutionInMilliseconds ;
}
}
}
private void Activate ( )
{
2021-01-08 09:14:13 +01:00
uint result = TimeBeginPeriod ( _targetResolutionInMilliseconds ) ;
2020-08-18 03:49:37 +02:00
if ( result ! = 0 )
{
Logger . Notice . Print ( LogClass . Application , $"timeBeginPeriod failed with result: {result}" ) ;
}
else
{
_isActive = true ;
}
}
private void Disable ( )
{
if ( _isActive )
{
2021-01-08 09:14:13 +01:00
uint result = TimeEndPeriod ( _targetResolutionInMilliseconds ) ;
2020-08-18 03:49:37 +02:00
if ( result ! = 0 )
{
Logger . Notice . Print ( LogClass . Application , $"timeEndPeriod failed with result: {result}" ) ;
}
else
{
_isActive = false ;
}
}
}
public void Dispose ( )
{
Dispose ( true ) ;
2021-01-08 09:14:13 +01:00
GC . SuppressFinalize ( this ) ;
2020-08-18 03:49:37 +02:00
}
protected virtual void Dispose ( bool disposing )
{
if ( disposing )
{
Disable ( ) ;
}
}
}
2021-01-08 09:14:13 +01:00
}