Browse Source

[X11] Platform threading interface and platform sanity checks

pull/2011/head
Nikita Tsukanov 7 years ago
parent
commit
16ea1f3762
  1. 54
      Avalonia.sln
  2. 6
      samples/PlatformSanityChecks/App.xaml
  3. 13
      samples/PlatformSanityChecks/App.xaml.cs
  4. 24
      samples/PlatformSanityChecks/PlatformSanityChecks.csproj
  5. 140
      samples/PlatformSanityChecks/Program.cs
  6. 12
      src/Avalonia.X11/Avalonia.X11.csproj
  7. 12
      src/Avalonia.X11/X11Exception.cs
  8. 39
      src/Avalonia.X11/X11Platform.cs
  9. 251
      src/Avalonia.X11/X11PlatformThreading.cs
  10. 105
      src/Avalonia.X11/XLib.cs

54
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}

6
samples/PlatformSanityChecks/App.xaml

@ -0,0 +1,6 @@
<Application xmlns="https://github.com/avaloniaui">
<Application.Styles>
<StyleInclude Source="resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"/>
</Application.Styles>
</Application>

13
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);
}
}
}

24
samples/PlatformSanityChecks/PlatformSanityChecks.csproj

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj" />
<ProjectReference Include="..\..\src\Avalonia.DotNetCoreRuntime\Avalonia.DotNetCoreRuntime.csproj" />
<ProjectReference Include="..\..\src\Avalonia.X11\Avalonia.X11.csproj" />
<ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Layout\Avalonia.Layout.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
</ItemGroup>
</Project>

140
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<App>().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<CancellationTokenSource> 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();
}
}
}

12
src/Avalonia.X11/Avalonia.X11.csproj

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" />
</ItemGroup>
</Project>

12
src/Avalonia.X11/X11Exception.cs

@ -0,0 +1,12 @@
using System;
namespace Avalonia.X11
{
public class X11Exception : Exception
{
public X11Exception(string message) : base(message)
{
}
}
}

39
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<IPlatformThreadingInterface>().ToConstant(new X11PlatformThreading(Display));
}
public IntPtr DeferredDisplay { get; set; }
public IntPtr Display { get; set; }
}
}
namespace Avalonia
{
public static class AvaloniaX11PlatformExtensions
{
public static T UseX11<T>(this T builder) where T : AppBuilderBase<T>, new()
{
builder.UseWindowingSubsystem(() => new AvaloniaX11Platform().Initialize());
return builder;
}
}
}

251
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<X11Timer> _timers = new List<X11Timer>();
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<X11Timer>();
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<DispatcherPriority?> 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;
}
}
}

105
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];
}
}
}
Loading…
Cancel
Save