Permalink
Browse files

Merge pull request #138 from thefiddler/xi2exitfix

[X11] Fix hang when exiting XI2 input thread
  • Loading branch information...
2 parents f889835 + ea5f1ad commit f0fd13a23ee4b01395a5dd513ff81d28b314b2be @thefiddler thefiddler committed Jun 18, 2014
@@ -117,24 +117,6 @@ static void CurrentDomain_ProcessExit(object sender, EventArgs e)
#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 @@ internal static partial class Functions
/// <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 @@ public static IntPtr XOpenDisplay(IntPtr display)
[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 @@ public static IntPtr XOpenDisplay(IntPtr display)
[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 @@ internal sealed class X11GLNative : NativeWindowBase
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 @@ static XI2MouseKeyboard()
//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,16 +129,25 @@ public XI2MouseKeyboard()
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);
}
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 @@ public XI2MouseKeyboard()
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 @@ void ProcessEvents()
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 @@ unsafe static double ReadRawValue(ref XIRawEvent raw, int bit)
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 @@ static bool IsBitSet(IntPtr mask, int bit)
}
}
+ 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 @@ void Dispose(bool disposing)
{
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();
}
}

0 comments on commit f0fd13a

Please sign in to comment.