Merge pull request #138 from thefiddler/xi2exitfix
[X11] Fix hang when exiting XI2 input thread
This commit is contained in:
commit
f0fd13a23e
4 changed files with 114 additions and 77 deletions
|
@ -117,24 +117,6 @@ namespace OpenTK.Platform.X11
|
|||
|
||||
#region Window handling
|
||||
|
||||
[Obsolete("Use XCreateWindow instead")]
|
||||
[DllImport(_dll_name, EntryPoint = "XCreateWindow")]
|
||||
public extern static Window CreateWindow(
|
||||
Display display,
|
||||
Window parent,
|
||||
int x, int y,
|
||||
//uint width, uint height,
|
||||
int width, int height,
|
||||
//uint border_width,
|
||||
int border_width,
|
||||
int depth,
|
||||
//uint @class,
|
||||
int @class,
|
||||
IntPtr visual,
|
||||
[MarshalAs(UnmanagedType.SysUInt)] CreateWindowMask valuemask,
|
||||
SetWindowAttributes attributes
|
||||
);
|
||||
|
||||
[DllImport(_dll_name, EntryPoint = "XCreateSimpleWindow")]
|
||||
public extern static Window CreateSimpleWindow(
|
||||
Display display,
|
||||
|
@ -1462,10 +1444,26 @@ XF86VidModeGetGammaRampSize(
|
|||
/// <para>The XCreateSimpleWindow function creates an unmapped InputOutput subwindow for a specified parent window, returns the window ID of the created window, and causes the X server to generate a CreateNotify event. The created window is placed on top in the stacking order with respect to siblings. Any part of the window that extends outside its parent window is clipped. The border_width for an InputOnly window must be zero, or a BadMatch error results. XCreateSimpleWindow inherits its depth, class, and visual from its parent. All other window attributes, except background and border, have their default values. </para>
|
||||
/// <para>XCreateSimpleWindow can generate BadAlloc, BadMatch, BadValue, and BadWindow errors.</para>
|
||||
/// </remarks>
|
||||
[DllImport(X11Library, EntryPoint = "XCreateWindow")]//, CLSCompliant(false)]
|
||||
public extern static Window XCreateWindow(Display display, Window parent,
|
||||
public static Window XCreateWindow(Display display, Window parent,
|
||||
int x, int y, int width, int height, int border_width, int depth,
|
||||
int @class, IntPtr visual, UIntPtr valuemask, ref XSetWindowAttributes attributes);
|
||||
CreateWindowArgs @class, IntPtr visual, SetWindowValuemask valuemask,
|
||||
XSetWindowAttributes? attributes)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
if (attributes.HasValue)
|
||||
{
|
||||
XSetWindowAttributes attr = attributes.Value;
|
||||
return XCreateWindow(display, parent, x, y, width, height, border_width, depth,
|
||||
(int)@class, visual, (IntPtr)valuemask, &attr);
|
||||
}
|
||||
else
|
||||
{
|
||||
return XCreateWindow(display, parent, x, y, width, height, border_width, depth,
|
||||
(int)@class, visual, (IntPtr)valuemask, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
|
|
@ -71,10 +71,8 @@ namespace OpenTK.Platform.X11
|
|||
[DllImport("libX11", EntryPoint = "XSynchronize")]
|
||||
public extern static IntPtr XSynchronize(IntPtr display, bool onoff);
|
||||
|
||||
//[DllImport("libX11", EntryPoint = "XCreateWindow"), CLSCompliant(false)]
|
||||
//public extern static IntPtr XCreateWindow(IntPtr display, IntPtr parent, int x, int y, int width, int height, int border_width, int depth, int xclass, IntPtr visual, UIntPtr valuemask, ref XSetWindowAttributes attributes);
|
||||
[DllImport("libX11", EntryPoint = "XCreateWindow")]
|
||||
public extern static IntPtr XCreateWindow(IntPtr display, IntPtr parent, int x, int y, int width, int height, int border_width, int depth, int xclass, IntPtr visual, IntPtr valuemask, ref XSetWindowAttributes attributes);
|
||||
public unsafe extern static IntPtr XCreateWindow(IntPtr display, IntPtr parent, int x, int y, int width, int height, int border_width, int depth, int xclass, IntPtr visual, IntPtr valuemask, XSetWindowAttributes* attributes);
|
||||
|
||||
[DllImport("libX11", EntryPoint = "XCreateSimpleWindow")]//, CLSCompliant(false)]
|
||||
public extern static IntPtr XCreateSimpleWindow(IntPtr display, IntPtr parent, int x, int y, int width, int height, int border_width, UIntPtr border, UIntPtr background);
|
||||
|
@ -103,7 +101,8 @@ namespace OpenTK.Platform.X11
|
|||
[DllImport("libX11")]
|
||||
public extern static Bool XCheckTypedEvent(Display display, XEventName event_type, out XEvent event_return);
|
||||
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public delegate Bool EventPredicate(IntPtr display, ref XEvent e, IntPtr arg);
|
||||
[DllImport("libX11")]
|
||||
public extern static Bool XIfEvent(Display display, ref XEvent e, IntPtr predicate, IntPtr arg );
|
||||
|
|
|
@ -181,12 +181,13 @@ namespace OpenTK.Platform.X11
|
|||
EventMask.PropertyChangeMask;
|
||||
attributes.event_mask = (IntPtr)window.EventMask;
|
||||
|
||||
uint mask = (uint)SetWindowValuemask.ColorMap | (uint)SetWindowValuemask.EventMask |
|
||||
(uint)SetWindowValuemask.BackPixel | (uint)SetWindowValuemask.BorderPixel;
|
||||
SetWindowValuemask mask =
|
||||
SetWindowValuemask.ColorMap | SetWindowValuemask.EventMask |
|
||||
SetWindowValuemask.BackPixel | SetWindowValuemask.BorderPixel;
|
||||
|
||||
window.Handle = Functions.XCreateWindow(window.Display, window.RootWindow,
|
||||
x, y, width, height, 0, window.VisualInfo.Depth/*(int)CreateWindowArgs.CopyFromParent*/,
|
||||
(int)CreateWindowArgs.InputOutput, window.VisualInfo.Visual, (UIntPtr)mask, ref attributes);
|
||||
CreateWindowArgs.InputOutput, window.VisualInfo.Visual, mask, attributes);
|
||||
|
||||
if (window.Handle == IntPtr.Zero)
|
||||
throw new ApplicationException("XCreateWindow call failed (returned 0).");
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace OpenTK.Platform.X11
|
|||
{
|
||||
sealed class XI2MouseKeyboard : IKeyboardDriver2, IMouseDriver2, IDisposable
|
||||
{
|
||||
const XEventName ExitEvent = XEventName.LASTEvent + 1;
|
||||
static readonly IntPtr ExitAtom;
|
||||
readonly object Sync = new object();
|
||||
readonly Thread ProcessingThread;
|
||||
readonly X11KeyMap KeyMap;
|
||||
|
@ -116,6 +116,9 @@ namespace OpenTK.Platform.X11
|
|||
//TouchPressure = Functions.XInternAtom(API.DefaultDisplay, "Abs MT Pressure", false);
|
||||
//TouchId = Functions.XInternAtom(API.DefaultDisplay, "Abs MT Tracking ID", false);
|
||||
//TouchMaxContacts = Functions.XInternAtom(API.DefaultDisplay, "Max Contacts", false);
|
||||
|
||||
// Custom
|
||||
ExitAtom = Functions.XInternAtom(API.DefaultDisplay, "Exit Input Thread Message", false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,9 +129,14 @@ namespace OpenTK.Platform.X11
|
|||
window.Display = Functions.XOpenDisplay(IntPtr.Zero);
|
||||
using (new XLock(window.Display))
|
||||
{
|
||||
XSetWindowAttributes attr = new XSetWindowAttributes();
|
||||
|
||||
window.Screen = Functions.XDefaultScreen(window.Display);
|
||||
window.RootWindow = Functions.XRootWindow(window.Display, window.Screen);
|
||||
window.Handle = window.RootWindow;
|
||||
window.Handle = Functions.XCreateWindow(window.Display, window.RootWindow,
|
||||
0, 0, 1, 1, 0, 0,
|
||||
CreateWindowArgs.InputOnly, IntPtr.Zero,
|
||||
SetWindowValuemask.Nothing, attr);
|
||||
|
||||
KeyMap = new X11KeyMap(window.Display);
|
||||
}
|
||||
|
@ -136,6 +144,10 @@ namespace OpenTK.Platform.X11
|
|||
if (!IsSupported(window.Display))
|
||||
throw new NotSupportedException("XInput2 not supported.");
|
||||
|
||||
// Enable XI2 mouse/keyboard events
|
||||
// Note: the input event loop blocks waiting for these events
|
||||
// *or* a custom ClientMessage event that instructs us to exit.
|
||||
// See SendExitEvent() below.
|
||||
using (new XLock(window.Display))
|
||||
using (XIEventMask mask = new XIEventMask(1,
|
||||
XIEventMasks.RawKeyPressMask |
|
||||
|
@ -144,10 +156,9 @@ namespace OpenTK.Platform.X11
|
|||
XIEventMasks.RawButtonReleaseMask |
|
||||
XIEventMasks.RawMotionMask |
|
||||
XIEventMasks.MotionMask |
|
||||
XIEventMasks.DeviceChangedMask |
|
||||
(XIEventMasks)(1 << (int)ExitEvent)))
|
||||
XIEventMasks.DeviceChangedMask))
|
||||
{
|
||||
XI.SelectEvents(window.Display, window.Handle, mask);
|
||||
XI.SelectEvents(window.Display, window.RootWindow, mask);
|
||||
UpdateDevices();
|
||||
}
|
||||
|
||||
|
@ -423,45 +434,52 @@ namespace OpenTK.Platform.X11
|
|||
using (new XLock(window.Display))
|
||||
{
|
||||
Functions.XIfEvent(window.Display, ref e, Predicate, new IntPtr(XIOpCode));
|
||||
if (e.type == ExitEvent)
|
||||
|
||||
if (e.type == XEventName.ClientMessage && e.ClientMessageEvent.ptr1 == ExitAtom)
|
||||
{
|
||||
return;
|
||||
Functions.XDestroyWindow(window.Display, window.Handle);
|
||||
window.Handle = IntPtr.Zero;
|
||||
break;
|
||||
}
|
||||
|
||||
IntPtr dummy;
|
||||
int x, y, dummy2;
|
||||
Functions.XQueryPointer(window.Display, window.RootWindow,
|
||||
out dummy, out dummy, out x, out y,
|
||||
out dummy2, out dummy2, out dummy2);
|
||||
Interlocked.Exchange(ref cursor_x, (long)x);
|
||||
Interlocked.Exchange(ref cursor_y, (long)y);
|
||||
|
||||
cookie = e.GenericEventCookie;
|
||||
if (Functions.XGetEventData(window.Display, ref cookie) != 0)
|
||||
if (e.type == XEventName.GenericEvent && e.GenericEvent.extension == XIOpCode)
|
||||
{
|
||||
switch ((XIEventType)cookie.evtype)
|
||||
IntPtr dummy;
|
||||
int x, y, dummy2;
|
||||
Functions.XQueryPointer(window.Display, window.RootWindow,
|
||||
out dummy, out dummy, out x, out y,
|
||||
out dummy2, out dummy2, out dummy2);
|
||||
Interlocked.Exchange(ref cursor_x, (long)x);
|
||||
Interlocked.Exchange(ref cursor_y, (long)y);
|
||||
|
||||
cookie = e.GenericEventCookie;
|
||||
if (Functions.XGetEventData(window.Display, ref cookie) != 0)
|
||||
{
|
||||
case XIEventType.Motion:
|
||||
switch ((XIEventType)cookie.evtype)
|
||||
{
|
||||
case XIEventType.Motion:
|
||||
// Nothing to do
|
||||
break;
|
||||
break;
|
||||
|
||||
case XIEventType.RawKeyPress:
|
||||
case XIEventType.RawKeyRelease:
|
||||
case XIEventType.RawMotion:
|
||||
case XIEventType.RawButtonPress:
|
||||
case XIEventType.RawButtonRelease:
|
||||
case XIEventType.RawKeyPress:
|
||||
case XIEventType.RawKeyRelease:
|
||||
case XIEventType.RawMotion:
|
||||
case XIEventType.RawButtonPress:
|
||||
case XIEventType.RawButtonRelease:
|
||||
// Delivered to all XIMouse instances
|
||||
ProcessRawEvent(ref cookie);
|
||||
break;
|
||||
ProcessRawEvent(ref cookie);
|
||||
break;
|
||||
|
||||
case XIEventType.DeviceChanged:
|
||||
UpdateDevices();
|
||||
break;
|
||||
case XIEventType.DeviceChanged:
|
||||
UpdateDevices();
|
||||
break;
|
||||
}
|
||||
}
|
||||
Functions.XFreeEventData(window.Display, ref cookie);
|
||||
}
|
||||
Functions.XFreeEventData(window.Display, ref cookie);
|
||||
}
|
||||
}
|
||||
Debug.WriteLine("[X11] Exiting input event loop.");
|
||||
}
|
||||
|
||||
void ProcessRawEvent(ref XGenericEventCookie cookie)
|
||||
|
@ -575,16 +593,29 @@ namespace OpenTK.Platform.X11
|
|||
static bool IsEventValid(IntPtr display, ref XEvent e, IntPtr arg)
|
||||
{
|
||||
bool valid = false;
|
||||
if ((long)e.GenericEventCookie.extension == arg.ToInt64())
|
||||
switch (e.type)
|
||||
{
|
||||
valid |= e.GenericEventCookie.evtype == (int)XIEventType.RawKeyPress;
|
||||
valid |= e.GenericEventCookie.evtype == (int)XIEventType.RawKeyRelease;
|
||||
valid |= e.GenericEventCookie.evtype == (int)XIEventType.RawMotion;
|
||||
valid |= e.GenericEventCookie.evtype == (int)XIEventType.RawButtonPress;
|
||||
valid |= e.GenericEventCookie.evtype == (int)XIEventType.RawButtonRelease;
|
||||
valid |= e.GenericEventCookie.evtype == (int)XIEventType.DeviceChanged;
|
||||
case XEventName.GenericEvent:
|
||||
{
|
||||
long extension = (long)e.GenericEventCookie.extension;
|
||||
valid =
|
||||
extension == arg.ToInt64() &&
|
||||
(e.GenericEventCookie.evtype == (int)XIEventType.RawKeyPress ||
|
||||
e.GenericEventCookie.evtype == (int)XIEventType.RawKeyRelease ||
|
||||
e.GenericEventCookie.evtype == (int)XIEventType.RawMotion ||
|
||||
e.GenericEventCookie.evtype == (int)XIEventType.RawButtonPress ||
|
||||
e.GenericEventCookie.evtype == (int)XIEventType.RawButtonRelease ||
|
||||
e.GenericEventCookie.evtype == (int)XIEventType.DeviceChanged);
|
||||
valid |= extension == 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case XEventName.ClientMessage:
|
||||
valid =
|
||||
e.ClientMessageEvent.ptr1 == ExitAtom;
|
||||
break;
|
||||
}
|
||||
valid |= e.AnyEvent.type == ExitEvent;
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
|
@ -596,6 +627,23 @@ namespace OpenTK.Platform.X11
|
|||
}
|
||||
}
|
||||
|
||||
void SendExitEvent()
|
||||
{
|
||||
// Send a ClientMessage event to unblock XIfEvent
|
||||
// and exit the input event loop.
|
||||
using (new XLock(API.DefaultDisplay))
|
||||
{
|
||||
XEvent ev = new XEvent();
|
||||
ev.type = XEventName.ClientMessage;
|
||||
ev.ClientMessageEvent.display = window.Display;
|
||||
ev.ClientMessageEvent.window = window.Handle;
|
||||
ev.ClientMessageEvent.format = 32;
|
||||
ev.ClientMessageEvent.ptr1 = ExitAtom;
|
||||
Functions.XSendEvent(API.DefaultDisplay, window.Handle, false, 0, ref ev);
|
||||
Functions.XFlush(API.DefaultDisplay);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable Members
|
||||
|
@ -610,17 +658,8 @@ namespace OpenTK.Platform.X11
|
|||
{
|
||||
if (!disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
using (new XLock(API.DefaultDisplay))
|
||||
{
|
||||
XEvent e = new XEvent();
|
||||
e.type = ExitEvent;
|
||||
Functions.XSendEvent(API.DefaultDisplay, window.Handle, false, IntPtr.Zero, ref e);
|
||||
Functions.XFlush(API.DefaultDisplay);
|
||||
}
|
||||
}
|
||||
disposed = true;
|
||||
SendExitEvent();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue