2 changed files with 157 additions and 3 deletions
@ -0,0 +1,153 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Diagnostics; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using System.Threading.Tasks; |
||||
|
using Avalonia.Controls.Platform; |
||||
|
using Avalonia.FreeDesktop; |
||||
|
using Avalonia.Input; |
||||
|
using Avalonia.Input.Platform; |
||||
|
using Avalonia.OpenGL; |
||||
|
using Avalonia.Platform; |
||||
|
using Avalonia.Rendering; |
||||
|
using Avalonia.X11.Glx; |
||||
|
using Avalonia.X11.NativeDialogs; |
||||
|
using static Avalonia.X11.XLib; |
||||
|
|
||||
|
namespace Avalonia.X11 |
||||
|
{ |
||||
|
internal class LinuxHighResolutionTimer : IDisposable |
||||
|
{ |
||||
|
[DllImport("libc", SetLastError = true)] |
||||
|
static extern int nanosleep(ref TimeSpec duration, ref TimeSpec remaining); |
||||
|
|
||||
|
[DllImport("libc", SetLastError = true)] |
||||
|
static extern int clock_gettime(uint clk_id, ref TimeSpec tp); |
||||
|
|
||||
|
const uint CLOCK_MONOTONIC = 1; |
||||
|
|
||||
|
struct TimeSpec |
||||
|
{ |
||||
|
public long tv_sec; |
||||
|
public long tv_nsec; |
||||
|
|
||||
|
public TimeSpec(int tv_sec, long tv_nsec) : this() |
||||
|
{ |
||||
|
this.tv_sec = tv_sec; |
||||
|
this.tv_nsec = tv_nsec; |
||||
|
} |
||||
|
|
||||
|
public TimeSpan ToTimeSpan() |
||||
|
=> TimeSpan.FromSeconds(tv_sec) + TimeSpan.FromTicks(tv_nsec / 100); |
||||
|
|
||||
|
public static TimeSpec ConvertToTimeSpec(TimeSpan timeSpan) |
||||
|
=> new TimeSpec(timeSpan.Seconds, timeSpan.Ticks * 100); |
||||
|
}; |
||||
|
|
||||
|
TimeSpec GetElapsedTimeSpec(ref TimeSpec start, ref TimeSpec stop) |
||||
|
{ |
||||
|
var elapsed_time = new TimeSpec(); |
||||
|
if ((stop.tv_nsec - start.tv_nsec) < 0) |
||||
|
{ |
||||
|
elapsed_time.tv_sec = stop.tv_sec - start.tv_sec - 1; |
||||
|
elapsed_time.tv_nsec = stop.tv_nsec - start.tv_nsec + 1000000000; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
elapsed_time.tv_sec = stop.tv_sec - start.tv_sec; |
||||
|
elapsed_time.tv_nsec = stop.tv_nsec - start.tv_nsec; |
||||
|
} |
||||
|
return elapsed_time; |
||||
|
} |
||||
|
|
||||
|
public LinuxHighResolutionTimer(double seconds) |
||||
|
{ |
||||
|
totalInterval = TimeSpan.FromSeconds(seconds); |
||||
|
} |
||||
|
|
||||
|
private volatile bool shouldStop = false; |
||||
|
private TimeSpan elapsedTotal = TimeSpan.Zero; |
||||
|
private IDisposable _diposable1; |
||||
|
private TimeSpan totalInterval; |
||||
|
|
||||
|
private void TickTock(Action<TimeSpan> observer) |
||||
|
{ |
||||
|
var start = new TimeSpec(); |
||||
|
var frameStop = new TimeSpec(); |
||||
|
var sleepStop = new TimeSpec(); |
||||
|
var remaining = new TimeSpec(); |
||||
|
|
||||
|
TimeSpan frameTime, totalTime; |
||||
|
|
||||
|
while (!shouldStop) |
||||
|
{ |
||||
|
clock_gettime(CLOCK_MONOTONIC, ref start); |
||||
|
|
||||
|
observer(elapsedTotal); |
||||
|
|
||||
|
clock_gettime(CLOCK_MONOTONIC, ref frameStop); |
||||
|
|
||||
|
frameTime = GetElapsedTimeSpec(ref start, ref frameStop).ToTimeSpan(); |
||||
|
|
||||
|
var calc = (totalInterval - frameTime); |
||||
|
|
||||
|
if (calc < TimeSpan.Zero) |
||||
|
calc = totalInterval; |
||||
|
|
||||
|
var finDur = TimeSpec.ConvertToTimeSpec(calc); |
||||
|
|
||||
|
nanosleep(ref finDur, ref remaining); |
||||
|
|
||||
|
clock_gettime(CLOCK_MONOTONIC, ref sleepStop); |
||||
|
|
||||
|
totalTime = GetElapsedTimeSpec(ref start, ref sleepStop).ToTimeSpan(); |
||||
|
|
||||
|
elapsedTotal += totalTime; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
shouldStop = true; |
||||
|
_diposable1?.Dispose(); |
||||
|
} |
||||
|
|
||||
|
public IDisposable Subscribe(Action<TimeSpan> observer) |
||||
|
{ |
||||
|
_diposable1 = Task.Run(() => TickTock(observer)); |
||||
|
return this; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
internal class X11HighResRenderTimer : DefaultRenderTimer |
||||
|
{ |
||||
|
private double intervalMills; |
||||
|
|
||||
|
public X11HighResRenderTimer(int framesPerSecond) : base(framesPerSecond) |
||||
|
{ |
||||
|
this.intervalMills = 1d / (double)framesPerSecond; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public override bool Equals(object obj) |
||||
|
{ |
||||
|
return base.Equals(obj); |
||||
|
} |
||||
|
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
return base.GetHashCode(); |
||||
|
} |
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return base.ToString(); |
||||
|
} |
||||
|
|
||||
|
protected override IDisposable StartCore(Action<TimeSpan> tick) |
||||
|
{ |
||||
|
return new LinuxHighResolutionTimer(intervalMills).Subscribe(tick); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue