10 changed files with 212 additions and 64 deletions
@ -0,0 +1,20 @@ |
|||
using System; |
|||
using static Avalonia.X11.XLib; |
|||
|
|||
namespace Avalonia.X11.Clipboard; |
|||
|
|||
internal static class ClipboardReadSessionFactory |
|||
{ |
|||
public static SelectionReadSession CreateSession(AvaloniaX11Platform platform) |
|||
{ |
|||
var window = new EventStreamWindow(platform); |
|||
XSelectInput(platform.Display, window.Handle, new IntPtr((int)XEventMask.PropertyChangeMask)); |
|||
|
|||
return new SelectionReadSession( |
|||
platform.Display, |
|||
window.Handle, |
|||
platform.Info.Atoms.CLIPBOARD, |
|||
window, |
|||
platform.Info.Atoms); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Avalonia.X11.DragDrop; |
|||
|
|||
internal interface IEventWaiter : IDisposable |
|||
{ |
|||
Task<XEvent?> WaitForEventAsync(Func<XEvent, bool> predicate, TimeSpan timeout); |
|||
} |
|||
@ -0,0 +1,120 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using System.Runtime.InteropServices; |
|||
using System.Threading.Tasks; |
|||
using static Avalonia.X11.XLib; |
|||
|
|||
namespace Avalonia.X11.DragDrop; |
|||
|
|||
internal sealed partial class SynchronousEventWaiter(IntPtr display) : IEventWaiter |
|||
{ |
|||
private const short POLLIN = 0x0001; |
|||
|
|||
public XEvent? WaitForEvent(Func<XEvent, bool> predicate, TimeSpan timeout) |
|||
{ |
|||
var startingTimestamp = Stopwatch.GetTimestamp(); |
|||
TimeSpan elapsed; |
|||
List<XEvent>? stashedEvents = null; |
|||
var fd = 0; |
|||
|
|||
try { |
|||
while (true) |
|||
{ |
|||
if (!CheckTimeout()) |
|||
return null; |
|||
|
|||
// First, wait until there's some event in the queue
|
|||
while (XPending(display) == 0) |
|||
{ |
|||
if (fd == 0) |
|||
fd = XConnectionNumber(display); |
|||
|
|||
if (!CheckTimeout()) |
|||
return null; |
|||
|
|||
var remainingTimeout = timeout - elapsed; |
|||
|
|||
var pollfd = new PollFd |
|||
{ |
|||
fd = fd, |
|||
events = POLLIN |
|||
}; |
|||
|
|||
unsafe |
|||
{ |
|||
if (poll(&pollfd, 1, (int)remainingTimeout.TotalMilliseconds) != 1) |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
if (!CheckTimeout()) |
|||
return null; |
|||
|
|||
// Second, check if there's an event we're interested in.
|
|||
// Use XNextEvent and stash the unrelated events, then put them back onto the queue.
|
|||
// We can't use XIfEvent because it could block indefinitely.
|
|||
// We can't use XCheckIfEvent either: by leaving the event in the queue, the next poll()
|
|||
// returns immediately, eating CPU, and we don't want to add arbitrary thread sleeps.
|
|||
XNextEvent(display, out var evt); |
|||
|
|||
if (evt.type == XEventName.GenericEvent) |
|||
{ |
|||
unsafe |
|||
{ |
|||
XGetEventData(display, &evt.GenericEventCookie); |
|||
} |
|||
} |
|||
|
|||
if (predicate(evt)) |
|||
return evt; |
|||
|
|||
stashedEvents ??= []; |
|||
stashedEvents.Add(evt); |
|||
} |
|||
} |
|||
finally |
|||
{ |
|||
if (stashedEvents is not null) |
|||
{ |
|||
foreach (var evt in stashedEvents) |
|||
{ |
|||
XPutBackEvent(display, evt); |
|||
|
|||
if (evt.type == XEventName.GenericEvent) |
|||
{ |
|||
unsafe |
|||
{ |
|||
if (evt.GenericEventCookie.data != null) |
|||
XFreeEventData(display, &evt.GenericEventCookie); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
bool CheckTimeout() |
|||
{ |
|||
elapsed = Stopwatch.GetElapsedTime(startingTimestamp); |
|||
return elapsed < timeout; |
|||
} |
|||
} |
|||
|
|||
Task<XEvent?> IEventWaiter.WaitForEventAsync(Func<XEvent, bool> predicate, TimeSpan timeout) |
|||
=> Task.FromResult(WaitForEvent(predicate, timeout)); |
|||
|
|||
void IDisposable.Dispose() |
|||
{ |
|||
} |
|||
|
|||
[StructLayout(LayoutKind.Sequential)] |
|||
private struct PollFd |
|||
{ |
|||
public int fd; |
|||
public short events; |
|||
public short revents; |
|||
} |
|||
|
|||
[LibraryImport("libc", SetLastError = true)] |
|||
private static unsafe partial int poll(PollFd* fds, nint nfds, int timeout); |
|||
} |
|||
Loading…
Reference in new issue