diff --git a/Avalonia.sln b/Avalonia.sln index f71a94888d..388dbb6e6a 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -188,6 +188,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.UnitTests", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.OpenGL", "src\Avalonia.OpenGL\Avalonia.OpenGL.csproj", "{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.X11", "src\Avalonia.X11\Avalonia.X11.csproj", "{212D02D5-C873-469A-8E78-4A6350EC4A1A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlatformSanityChecks", "samples\PlatformSanityChecks\PlatformSanityChecks.csproj", "{8B5768BB-71F9-4E23-89B5-DDBA6458B856}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13 @@ -1689,6 +1693,54 @@ Global {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhone.Build.0 = Release|Any CPU {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.AppStore|iPhone.Build.0 = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Debug|iPhone.Build.0 = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Release|Any CPU.Build.0 = Release|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Release|iPhone.ActiveCfg = Release|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Release|iPhone.Build.0 = Release|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.AppStore|iPhone.Build.0 = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Debug|iPhone.Build.0 = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Release|Any CPU.Build.0 = Release|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Release|iPhone.ActiveCfg = Release|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Release|iPhone.Build.0 = Release|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Release|iPhoneSimulator.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1742,6 +1794,8 @@ Global {CBFD5788-567D-401B-9DFA-74E4224025A0} = {A59C4C0A-64DF-4621-B450-2BA00D6F61E2} {4ADA61C8-D191-428D-9066-EF4F0D86520F} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637} {E1240B49-7B4B-4371-A00E-068778C5CF0B} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} + {212D02D5-C873-469A-8E78-4A6350EC4A1A} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B} + {8B5768BB-71F9-4E23-89B5-DDBA6458B856} = {9B9E3891-2366-4253-A952-D08BCEB71098} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} diff --git a/samples/PlatformSanityChecks/App.xaml b/samples/PlatformSanityChecks/App.xaml new file mode 100644 index 0000000000..25bab6ae35 --- /dev/null +++ b/samples/PlatformSanityChecks/App.xaml @@ -0,0 +1,6 @@ + + + + + + diff --git a/samples/PlatformSanityChecks/App.xaml.cs b/samples/PlatformSanityChecks/App.xaml.cs new file mode 100644 index 0000000000..508fc1e34b --- /dev/null +++ b/samples/PlatformSanityChecks/App.xaml.cs @@ -0,0 +1,13 @@ +using Avalonia; +using Avalonia.Markup.Xaml; + +namespace PlatformSanityChecks +{ + public class App : Application + { + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + } +} diff --git a/samples/PlatformSanityChecks/PlatformSanityChecks.csproj b/samples/PlatformSanityChecks/PlatformSanityChecks.csproj new file mode 100644 index 0000000000..00b5b10106 --- /dev/null +++ b/samples/PlatformSanityChecks/PlatformSanityChecks.csproj @@ -0,0 +1,24 @@ + + + + Exe + netcoreapp2.0 + + + + + + + + + + + + + + + + + + + diff --git a/samples/PlatformSanityChecks/Program.cs b/samples/PlatformSanityChecks/Program.cs new file mode 100644 index 0000000000..8a3aa82981 --- /dev/null +++ b/samples/PlatformSanityChecks/Program.cs @@ -0,0 +1,140 @@ +using System; +using System.Diagnostics; +using System.Reactive.Disposables; +using System.Runtime.CompilerServices; +using System.Threading; +using Avalonia; +using Avalonia.Platform; +using Avalonia.Threading; +using Avalonia.X11; + +namespace PlatformSanityChecks +{ + public class Program + { + static Thread UiThread; + + static void Main(string[] args) + { + UiThread = Thread.CurrentThread; + AppBuilder.Configure().RuntimePlatformServicesInitializer(); + var app = new App(); + + new AvaloniaX11Platform().Initialize(); + + CheckPlatformThreading(); + + + + } + + static bool CheckAccess() => UiThread == Thread.CurrentThread; + + static void VerifyAccess() + { + if (!CheckAccess()) + Die("Call from invalid thread"); + } + + static Exception Die(string error) + { + Console.Error.WriteLine(error); + Console.Error.WriteLine(Environment.StackTrace); + Process.GetCurrentProcess().Kill(); + throw new Exception(error); + } + + static IDisposable Enter([CallerMemberName] string caller = null) + { + Console.WriteLine("Entering " + caller); + return Disposable.Create(() => { Console.WriteLine("Leaving " + caller); }); + } + + static void EnterLoop(Action cb, [CallerMemberName] string caller = null) + { + using (Enter(caller)) + { + var cts = new CancellationTokenSource(); + cb(cts); + Dispatcher.UIThread.MainLoop(cts.Token); + if (!cts.IsCancellationRequested) + Die("Unexpected loop exit"); + } + } + + static void CheckTimerOrdering() => EnterLoop(cts => + { + bool firstFired = false, secondFired = false; + DispatcherTimer.Run(() => + { + Console.WriteLine("Second tick"); + VerifyAccess(); + if (!firstFired) + throw Die("Invalid timer ordering"); + if (secondFired) + throw Die("Invocation of finished timer"); + secondFired = true; + cts.Cancel(); + return false; + }, TimeSpan.FromSeconds(2)); + DispatcherTimer.Run(() => + { + Console.WriteLine("First tick"); + VerifyAccess(); + if (secondFired) + throw Die("Invalid timer ordering"); + if (firstFired) + throw Die("Invocation of finished timer"); + firstFired = true; + return false; + }, TimeSpan.FromSeconds(1)); + }); + + static void CheckTimerTicking() => EnterLoop(cts => + { + int ticks = 0; + var st = Stopwatch.StartNew(); + DispatcherTimer.Run(() => + { + ticks++; + Console.WriteLine($"Tick {ticks} at {st.Elapsed}"); + if (ticks == 5) + { + if (st.Elapsed.TotalSeconds < 4.5) + Die("Timer is too fast"); + if (st.Elapsed.TotalSeconds > 6) + Die("Timer is too slow"); + cts.Cancel(); + return false; + } + + return true; + }, TimeSpan.FromSeconds(1)); + + + }); + + + static void CheckSignaling() => EnterLoop(cts => + { + ThreadPool.QueueUserWorkItem(_ => + { + Thread.Sleep(100); + Dispatcher.UIThread.Post(() => + { + VerifyAccess(); + cts.Cancel(); + }); + }); + }); + + static void CheckPlatformThreading() + { + CheckSignaling(); + CheckTimerOrdering(); + CheckTimerTicking(); + + } + + } +} diff --git a/src/Avalonia.X11/Avalonia.X11.csproj b/src/Avalonia.X11/Avalonia.X11.csproj new file mode 100644 index 0000000000..a035febe5b --- /dev/null +++ b/src/Avalonia.X11/Avalonia.X11.csproj @@ -0,0 +1,12 @@ + + + + netstandard2.0 + true + + + + + + + diff --git a/src/Avalonia.X11/X11Exception.cs b/src/Avalonia.X11/X11Exception.cs new file mode 100644 index 0000000000..2ac5a31d9b --- /dev/null +++ b/src/Avalonia.X11/X11Exception.cs @@ -0,0 +1,12 @@ +using System; + +namespace Avalonia.X11 +{ + public class X11Exception : Exception + { + public X11Exception(string message) : base(message) + { + + } + } +} diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs new file mode 100644 index 0000000000..c266433eef --- /dev/null +++ b/src/Avalonia.X11/X11Platform.cs @@ -0,0 +1,39 @@ +using System; +using Avalonia.Controls; +using Avalonia.Platform; +using Avalonia.X11; +using static Avalonia.X11.XLib; +namespace Avalonia.X11 +{ + public class AvaloniaX11Platform + { + public void Initialize() + { + Display = XOpenDisplay(IntPtr.Zero); + DeferredDisplay = XOpenDisplay(IntPtr.Zero); + if (Display == IntPtr.Zero) + throw new Exception("XOpenDisplay failed"); + + + AvaloniaLocator.CurrentMutable.BindToSelf(this) + .Bind().ToConstant(new X11PlatformThreading(Display)); + + } + + public IntPtr DeferredDisplay { get; set; } + public IntPtr Display { get; set; } + } +} + +namespace Avalonia +{ + public static class AvaloniaX11PlatformExtensions + { + public static T UseX11(this T builder) where T : AppBuilderBase, new() + { + builder.UseWindowingSubsystem(() => new AvaloniaX11Platform().Initialize()); + return builder; + } + } + +} diff --git a/src/Avalonia.X11/X11PlatformThreading.cs b/src/Avalonia.X11/X11PlatformThreading.cs new file mode 100644 index 0000000000..18b08085e3 --- /dev/null +++ b/src/Avalonia.X11/X11PlatformThreading.cs @@ -0,0 +1,251 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using Avalonia.Platform; +using Avalonia.Threading; +using static Avalonia.X11.XLib; + +namespace Avalonia.X11 +{ + public unsafe class X11PlatformThreading : IPlatformThreadingInterface + { + private readonly IntPtr _display; + private Thread _mainThread; + + [StructLayout(LayoutKind.Explicit)] + struct epoll_data + { + [FieldOffset(0)] + public IntPtr ptr; + [FieldOffset(0)] + public int fd; + [FieldOffset(0)] + public uint u32; + [FieldOffset(0)] + public ulong u64; + } + + private const int EPOLLIN = 1; + private const int EPOLL_CTL_ADD = 1; + private const int O_NONBLOCK = 2048; + + [StructLayout(LayoutKind.Sequential)] + struct epoll_event + { + public uint events; + public epoll_data data; + } + + [DllImport("libc")] + extern static int epoll_create1(int size); + + [DllImport("libc")] + extern static int epoll_ctl(int epfd, int op, int fd, ref epoll_event __event); + + [DllImport("libc")] + extern static int epoll_wait(int epfd, epoll_event* events, int maxevents, int timeout); + + [DllImport("libc")] + extern static int pipe2(int* fds, int flags); + [DllImport("libc")] + extern static IntPtr write(int fd, void* buf, IntPtr count); + + [DllImport("libc")] + extern static IntPtr read(int fd, void* buf, IntPtr count); + + enum EventCodes + { + X11 = 1, + Signal =2 + } + + private int _sigread, _sigwrite; + private object _lock = new object(); + private bool _signaled; + private DispatcherPriority _signaledPriority; + private int _epoll; + private Stopwatch _clock = Stopwatch.StartNew(); + + class X11Timer : IDisposable + { + private readonly X11PlatformThreading _parent; + + public X11Timer(X11PlatformThreading parent, DispatcherPriority prio, TimeSpan interval, Action tick) + { + _parent = parent; + Priority = prio; + Tick = tick; + Interval = interval; + Reschedule(); + } + + public DispatcherPriority Priority { get; } + public TimeSpan NextTick { get; private set; } + public TimeSpan Interval { get; } + public Action Tick { get; } + public bool Disposed { get; private set; } + + public void Reschedule() + { + NextTick = _parent._clock.Elapsed + Interval; + } + + public void Dispose() + { + Disposed = true; + lock (_parent._lock) + _parent._timers.Remove(this); + } + } + + List _timers = new List(); + + public X11PlatformThreading(IntPtr display) + { + _display = display; + _mainThread = Thread.CurrentThread; + var fd = XLib.XConnectionNumber(display); + 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, fd, ref ev) == -1) + throw new X11Exception("Unable to attach X11 connection handle to epoll"); + + 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"); + } + + int TimerComparer(X11Timer t1, X11Timer t2) + { + return t2.Priority - t1.Priority; + } + + public void RunLoop(CancellationToken cancellationToken) + { + var readyTimers = new List(); + while (!cancellationToken.IsCancellationRequested) + { + var now = _clock.Elapsed; + TimeSpan? nextTick = null; + readyTimers.Clear(); + lock(_timers) + foreach (var t in _timers) + { + if (nextTick == null || t.NextTick < nextTick.Value) + nextTick = t.NextTick; + if (t.NextTick < now) + readyTimers.Add(t); + } + + readyTimers.Sort(TimerComparer); + + foreach (var t in readyTimers) + { + if (cancellationToken.IsCancellationRequested) + return; + t.Tick(); + if(!t.Disposed) + { + t.Reschedule(); + if (nextTick == null || t.NextTick < nextTick.Value) + nextTick = t.NextTick; + } + } + + if (cancellationToken.IsCancellationRequested) + return; + epoll_event ev; + var len = epoll_wait(_epoll, &ev, 1, + nextTick == null ? -1 : Math.Max(1, (int)(nextTick.Value - _clock.Elapsed).TotalMilliseconds)); + if (cancellationToken.IsCancellationRequested) + return; + if (len == 0) + { + // We handle timer-related stuff at the beginning of the loop + continue; + } + else + { + if (ev.data.u32 == (int)EventCodes.Signal) + { + int buf = 0; + while (read(_sigread, &buf, new IntPtr(4)).ToInt64() > 0) + { + } + + DispatcherPriority prio; + lock (_lock) + { + _signaled = false; + prio = _signaledPriority; + _signaledPriority = DispatcherPriority.MinValue; + } + Signaled?.Invoke(prio); + } + else + { + while (XPending(_display)) + { + if (cancellationToken.IsCancellationRequested) + return; + XNextEvent(_display, out var xev); + } + } + } + } + } + + + + public void Signal(DispatcherPriority priority) + { + lock (_lock) + { + if (priority > _signaledPriority) + _signaledPriority = priority; + + if(_signaled) + return; + _signaled = true; + int buf = 0; + write(_sigwrite, &buf, new IntPtr(1)); + } + } + + public bool CurrentThreadIsLoopThread => Thread.CurrentThread == _mainThread; + public event Action Signaled; + + public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) + { + if (_mainThread != Thread.CurrentThread) + throw new InvalidOperationException("StartTimer can be only called from UI thread"); + if (interval <= TimeSpan.Zero) + throw new ArgumentException("Interval must be positive", nameof(interval)); + + // We assume that we are on the main thread and outside of epoll_wait, so there is no need for wakeup signal + + var timer = new X11Timer(this, priority, interval, tick); + lock(_timers) + _timers.Add(timer); + return timer; + } + } +} diff --git a/src/Avalonia.X11/XLib.cs b/src/Avalonia.X11/XLib.cs new file mode 100644 index 0000000000..88d36091ca --- /dev/null +++ b/src/Avalonia.X11/XLib.cs @@ -0,0 +1,105 @@ +using System; +using System.Runtime.InteropServices; + +namespace Avalonia.X11 +{ + public static class XLib + { + + [DllImport("libX11.so.6")] + public static extern IntPtr XInitThreads(); + + [DllImport("libX11.so.6")] + public static extern IntPtr XOpenDisplay(IntPtr name); + + [DllImport("libX11.so.6")] + public static extern int XConnectionNumber(IntPtr display); + + [DllImport("libX11.so.6")] + public static extern IntPtr XLockDisplay(IntPtr display); + + [DllImport("libX11.so.6")] + public static extern IntPtr XUnlockDisplay(IntPtr display); + + [DllImport("libX11.so.6")] + public static extern IntPtr XFreeGC(IntPtr display, IntPtr gc); + + [DllImport("libX11.so.6")] + public static extern IntPtr XCreateGC(IntPtr display, IntPtr drawable, ulong valuemask, IntPtr values); + + [DllImport("libX11.so.6")] + public static extern int XInitImage(ref XImage image); + + [DllImport("libX11.so.6")] + public static extern int XDestroyImage(ref XImage image); + + [DllImport("libX11.so.6")] + public static extern IntPtr XSetErrorHandler(XErrorHandler handler); + + [DllImport("libX11.so.6")] + public static extern int XSync(IntPtr display, bool discard); + + [DllImport("libX11.so.6")] + public static extern int XNextEvent(IntPtr display, out XEvent ev); + + [DllImport("libX11.so.6")] + public static extern bool XPending(IntPtr display); + + [StructLayout(LayoutKind.Sequential)] + public struct XAnyEvent + { + + public int Type; + public ulong Serial; /* # of last request processed by server */ + public bool send_event; /* true if this came from a SendEvent request */ + public IntPtr display; /* Display the event was read from */ + public IntPtr window; /* window on which event was requested in event mask */ + } + + [StructLayout(LayoutKind.Explicit)] + public unsafe struct XEvent + { + [FieldOffset(0)] public int Type; + [FieldOffset(0)] public XAnyEvent XAny; + [FieldOffset(0)] private fixed int pad[40]; + } + + public delegate int XErrorHandler(IntPtr display, ref XErrorEvent error); + + [DllImport("libX11.so.6")] + public static extern int XPutImage(IntPtr display, IntPtr drawable, IntPtr gc, ref XImage image, + int srcx, int srcy, int destx, int desty, uint width, uint height); + + [StructLayout(LayoutKind.Sequential)] + public unsafe struct XErrorEvent + { + public int type; + public IntPtr* display; /* Display the event was read from */ + public ulong serial; /* serial number of failed request */ + public byte error_code; /* error code of failed request */ + public byte request_code; /* Major op-code of failed request */ + public byte minor_code; /* Minor op-code of failed request */ + public IntPtr resourceid; /* resource id */ + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe struct XImage + { + public int width, height; /* size of image */ + public int xoffset; /* number of pixels offset in X direction */ + public int format; /* XYBitmap, XYPixmap, ZPixmap */ + public IntPtr data; /* pointer to image data */ + public int byte_order; /* data byte order, LSBFirst, MSBFirst */ + public int bitmap_unit; /* quant. of scanline 8, 16, 32 */ + public int bitmap_bit_order; /* LSBFirst, MSBFirst */ + public int bitmap_pad; /* 8, 16, 32 either XY or ZPixmap */ + public int depth; /* depth of image */ + public int bytes_per_line; /* accelerator to next scanline */ + public int bits_per_pixel; /* bits per pixel (ZPixmap) */ + public ulong red_mask; /* bits in z arrangement */ + public ulong green_mask; + public ulong blue_mask; + private fixed byte funcs[128]; + } + } +}