10 changed files with 656 additions and 0 deletions
@ -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> |
||||
@ -0,0 +1,13 @@ |
|||||
|
using Avalonia; |
||||
|
using Avalonia.Markup.Xaml; |
||||
|
|
||||
|
namespace PlatformSanityChecks |
||||
|
{ |
||||
|
public class App : Application |
||||
|
{ |
||||
|
public override void Initialize() |
||||
|
{ |
||||
|
AvaloniaXamlLoader.Load(this); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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> |
||||
@ -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(); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -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> |
||||
@ -0,0 +1,12 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Avalonia.X11 |
||||
|
{ |
||||
|
public class X11Exception : Exception |
||||
|
{ |
||||
|
public X11Exception(string message) : base(message) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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…
Reference in new issue