|
|
|
@ -1,9 +1,7 @@ |
|
|
|
using System; |
|
|
|
using System.Collections.Generic; |
|
|
|
using System.Diagnostics; |
|
|
|
using System.Runtime.InteropServices; |
|
|
|
using System.Threading; |
|
|
|
using Avalonia.Platform; |
|
|
|
using Avalonia.Threading; |
|
|
|
using Avalonia.X11.Dispatching; |
|
|
|
using static Avalonia.X11.XLib; |
|
|
|
@ -15,59 +13,59 @@ namespace Avalonia.X11 |
|
|
|
private readonly AvaloniaX11Platform _platform; |
|
|
|
private Thread _mainThread = Thread.CurrentThread; |
|
|
|
|
|
|
|
[StructLayout(LayoutKind.Explicit)] |
|
|
|
private struct epoll_data |
|
|
|
[StructLayout(LayoutKind.Sequential)] |
|
|
|
public struct fd_set |
|
|
|
{ |
|
|
|
[FieldOffset(0)] |
|
|
|
public IntPtr ptr; |
|
|
|
[FieldOffset(0)] |
|
|
|
public int fd; |
|
|
|
[FieldOffset(0)] |
|
|
|
public uint u32; |
|
|
|
[FieldOffset(0)] |
|
|
|
public ulong u64; |
|
|
|
public const int FD_SETSIZE = 1024; |
|
|
|
public fixed uint fds[FD_SETSIZE / 32]; |
|
|
|
|
|
|
|
public void Set(int fd) |
|
|
|
{ |
|
|
|
var idx = fd / 32; |
|
|
|
if (idx >= FD_SETSIZE / 32) |
|
|
|
throw new ArgumentOutOfRangeException(nameof(fd)); |
|
|
|
var bit = fd % 32; |
|
|
|
fds[idx] |= 1u << bit; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private const int EPOLLIN = 1; |
|
|
|
private const int EPOLL_CTL_ADD = 1; |
|
|
|
private const int O_NONBLOCK = 2048; |
|
|
|
|
|
|
|
[StructLayout(LayoutKind.Sequential)] |
|
|
|
private struct epoll_event |
|
|
|
public struct timeval |
|
|
|
{ |
|
|
|
public uint events; |
|
|
|
public epoll_data data; |
|
|
|
public int tv_sec; |
|
|
|
public int tv_usec; |
|
|
|
|
|
|
|
public timeval(int milliseconds) |
|
|
|
{ |
|
|
|
tv_sec = milliseconds / 1000; |
|
|
|
tv_usec = (milliseconds % 1000) * 1000; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
[DllImport("libc")] |
|
|
|
private extern static int epoll_create1(int size); |
|
|
|
|
|
|
|
[DllImport("libc")] |
|
|
|
private extern static int epoll_ctl(int epfd, int op, int fd, ref epoll_event __event); |
|
|
|
private const int O_NONBLOCK = 2048; |
|
|
|
|
|
|
|
[DllImport("libc")] |
|
|
|
private extern static int epoll_wait(int epfd, epoll_event* events, int maxevents, int timeout); |
|
|
|
[DllImport("libc", EntryPoint = "select")] |
|
|
|
private static extern int select( |
|
|
|
int nfds, |
|
|
|
ref fd_set readfds, |
|
|
|
ref fd_set writefds, |
|
|
|
ref fd_set exceptfds, |
|
|
|
timeval* timeout); |
|
|
|
|
|
|
|
[DllImport("libc")] |
|
|
|
private extern static int pipe2(int* fds, int flags); |
|
|
|
|
|
|
|
[DllImport("libc")] |
|
|
|
private extern static IntPtr write(int fd, void* buf, IntPtr count); |
|
|
|
|
|
|
|
|
|
|
|
[DllImport("libc")] |
|
|
|
private extern static IntPtr read(int fd, void* buf, IntPtr count); |
|
|
|
|
|
|
|
private enum EventCodes |
|
|
|
{ |
|
|
|
X11 = 1, |
|
|
|
Signal =2 |
|
|
|
} |
|
|
|
|
|
|
|
private int _sigread, _sigwrite; |
|
|
|
private readonly int _sigread, _sigwrite, _x11Fd; |
|
|
|
private object _lock = new object(); |
|
|
|
private bool _signaled; |
|
|
|
private bool _wakeupRequested; |
|
|
|
private long? _nextTimer; |
|
|
|
private int _epoll; |
|
|
|
private Stopwatch _clock = Stopwatch.StartNew(); |
|
|
|
private readonly X11EventDispatcher _x11Events; |
|
|
|
|
|
|
|
@ -75,30 +73,12 @@ namespace Avalonia.X11 |
|
|
|
{ |
|
|
|
_platform = platform; |
|
|
|
_x11Events = new X11EventDispatcher(platform); |
|
|
|
var ev = new epoll_event() |
|
|
|
{ |
|
|
|
events = EPOLLIN, |
|
|
|
data = {u32 = (int)EventCodes.X11} |
|
|
|
}; |
|
|
|
_epoll = epoll_create1(0); |
|
|
|
if (_epoll == -1) |
|
|
|
throw new X11Exception("epoll_create1 failed"); |
|
|
|
|
|
|
|
if (epoll_ctl(_epoll, EPOLL_CTL_ADD, _x11Events.Fd, ref ev) == -1) |
|
|
|
throw new X11Exception("Unable to attach X11 connection handle to epoll"); |
|
|
|
_x11Fd = XConnectionNumber(platform.Display); |
|
|
|
|
|
|
|
var fds = stackalloc int[2]; |
|
|
|
pipe2(fds, O_NONBLOCK); |
|
|
|
_sigread = fds[0]; |
|
|
|
_sigwrite = fds[1]; |
|
|
|
|
|
|
|
ev = new epoll_event |
|
|
|
{ |
|
|
|
events = EPOLLIN, |
|
|
|
data = {u32 = (int)EventCodes.Signal} |
|
|
|
}; |
|
|
|
if (epoll_ctl(_epoll, EPOLL_CTL_ADD, _sigread, ref ev) == -1) |
|
|
|
throw new X11Exception("Unable to attach signal pipe to epoll"); |
|
|
|
} |
|
|
|
|
|
|
|
private void CheckSignaled() |
|
|
|
@ -112,7 +92,6 @@ namespace Avalonia.X11 |
|
|
|
|
|
|
|
Signaled?.Invoke(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void RunLoop(CancellationToken cancellationToken) |
|
|
|
{ |
|
|
|
@ -126,20 +105,27 @@ namespace Avalonia.X11 |
|
|
|
|
|
|
|
if (cancellationToken.IsCancellationRequested) |
|
|
|
return; |
|
|
|
|
|
|
|
//Flush whatever requests were made to XServer
|
|
|
|
|
|
|
|
_x11Events.Flush(); |
|
|
|
epoll_event ev; |
|
|
|
|
|
|
|
if (!_x11Events.IsPending) |
|
|
|
{ |
|
|
|
now = _clock.ElapsedMilliseconds; |
|
|
|
if (_nextTimer < now) |
|
|
|
continue; |
|
|
|
|
|
|
|
var timeout = _nextTimer == null ? (int)-1 : Math.Max(1, _nextTimer.Value - now); |
|
|
|
epoll_wait(_epoll, &ev, 1, (int)Math.Min(int.MaxValue, timeout)); |
|
|
|
|
|
|
|
// Drain the signaled pipe
|
|
|
|
|
|
|
|
fd_set readFds = default, writeFds = default, exceptFds = default; |
|
|
|
readFds.Set(_x11Fd); |
|
|
|
readFds.Set(_sigread); |
|
|
|
|
|
|
|
var timeoutMs = _nextTimer == null ? (int?)null : Math.Max(1, (int)(_nextTimer.Value - now)); |
|
|
|
var timeout = timeoutMs.HasValue ? new timeval(timeoutMs.Value) : default; |
|
|
|
select(Math.Max(_x11Fd, _sigread) + 1, |
|
|
|
ref readFds, |
|
|
|
ref writeFds, |
|
|
|
ref exceptFds, |
|
|
|
timeoutMs.HasValue ? &timeout : null); |
|
|
|
|
|
|
|
int buf = 0; |
|
|
|
while (read(_sigread, &buf, new IntPtr(4)).ToInt64() > 0) |
|
|
|
{ |
|
|
|
@ -151,6 +137,7 @@ namespace Avalonia.X11 |
|
|
|
|
|
|
|
if (cancellationToken.IsCancellationRequested) |
|
|
|
return; |
|
|
|
|
|
|
|
CheckSignaled(); |
|
|
|
_x11Events.DispatchX11Events(cancellationToken); |
|
|
|
while (_platform.EventGrouperDispatchQueue.HasJobs) |
|
|
|
@ -165,7 +152,7 @@ namespace Avalonia.X11 |
|
|
|
{ |
|
|
|
lock (_lock) |
|
|
|
{ |
|
|
|
if(_wakeupRequested) |
|
|
|
if (_wakeupRequested) |
|
|
|
return; |
|
|
|
_wakeupRequested = true; |
|
|
|
int buf = 0; |
|
|
|
@ -177,7 +164,7 @@ namespace Avalonia.X11 |
|
|
|
{ |
|
|
|
lock (_lock) |
|
|
|
{ |
|
|
|
if(_signaled) |
|
|
|
if (_signaled) |
|
|
|
return; |
|
|
|
_signaled = true; |
|
|
|
Wakeup(); |
|
|
|
@ -185,7 +172,7 @@ namespace Avalonia.X11 |
|
|
|
} |
|
|
|
|
|
|
|
public bool CurrentThreadIsLoopThread => Thread.CurrentThread == _mainThread; |
|
|
|
|
|
|
|
|
|
|
|
public event Action? Signaled; |
|
|
|
public event Action? Timer; |
|
|
|
|
|
|
|
@ -196,7 +183,6 @@ namespace Avalonia.X11 |
|
|
|
Wakeup(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public long Now => _clock.ElapsedMilliseconds; |
|
|
|
public bool CanQueryPendingInput => true; |
|
|
|
|
|
|
|
|