- Added code to destroy the X window
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / XplatUIX11.cs
index 2b6923360ad5ee7a3c360e89881e511d3c584de8..c1197f4cac3d75982ef8f0c4623401e4d398b361 100644 (file)
 // NOT COMPLETE
 
 using System;
+using System.Threading;
 using System.Drawing;
 using System.ComponentModel;
 using System.Collections;
 using System.Diagnostics;
 using System.Runtime.InteropServices;
+using System.Net;
+using System.Net.Sockets;
+
+// Only do the poll when building with mono for now
+#if __MonoCS__
+using Mono.Posix;
+#endif
 
 /// X11 Version
 namespace System.Windows.Forms {
@@ -42,17 +50,46 @@ namespace System.Windows.Forms {
                private static bool             themes_enabled;
 
                private static IntPtr           DisplayHandle;          // X11 handle to display
+               private static int              screen_num;             // Screen number used
                private static IntPtr           root_window;            // Handle of the root window for the screen/display
                private static IntPtr           FosterParent;           // Container to hold child windows until their parent exists
                private static int              wm_protocols;           // X Atom
                private static int              wm_delete_window;       // X Atom
+               private static IntPtr           async_method;
                private static uint             default_colormap;       // X Colormap ID
                internal static Keys            key_state;
                internal static MouseButtons    mouse_state;
                internal static Point           mouse_position;
+               internal static bool            grab_confined;          // Is the current grab (if any) confined to grab_area?
+               internal static IntPtr          grab_hwnd;              // The window that is grabbed
+               internal static Rectangle       grab_area;              // The area the current grab is confined to
                internal static bool            is_visible;
 
                private static Hashtable        handle_data;
+               private Queue message_queue;
+
+               private ArrayList timer_list;
+               private Thread timer_thread;
+               private AutoResetEvent timer_wait;
+               private Socket listen;
+               private Socket wake;
+
+#if __MonoCS__
+               private pollfd [] pollfds;
+#endif
+
+               private object xlib_lock = new object ();
+
+               private static readonly EventMask  SelectInputMask = EventMask.ButtonPressMask | 
+                               EventMask.ButtonReleaseMask | 
+                               EventMask.KeyPressMask | 
+                               EventMask.KeyReleaseMask | 
+                               EventMask.EnterWindowMask | 
+                               EventMask.LeaveWindowMask |
+                               EventMask.ExposureMask |
+                               EventMask.PointerMotionMask | 
+                               EventMask.VisibilityChangeMask |
+                               EventMask.StructureNotifyMask;
 
                #endregion      // Local Variables
 
@@ -87,30 +124,55 @@ namespace System.Windows.Forms {
                }
 
                #region Constructor & Destructor
-
-
+                // This is always called from a locked context
                private XplatUIX11() {
                        // Handle singleton stuff first
                        ref_count=0;
 
+                       message_queue = new Queue ();
+                       timer_list = new ArrayList ();
+
                        // Now regular initialization
                        SetDisplay(XOpenDisplay(IntPtr.Zero));
+
+                       listen = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
+                       IPEndPoint ep = new IPEndPoint (IPAddress.Loopback, 0);
+                       listen.Bind (ep);
+                       listen.Listen (1);
+
+                       wake = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
+                       wake.Connect (listen.LocalEndPoint);
+
+#if __MonoCS__
+                       pollfds = new pollfd [2];
+                       pollfds [0] = new pollfd ();
+                       pollfds [0].fd = XConnectionNumber (DisplayHandle);
+                       pollfds [0].events = PollEvents.POLLIN;
+
+                       pollfds [1] = new pollfd ();
+                       pollfds [1].fd = wake.Handle.ToInt32 ();
+                       pollfds [1].events = PollEvents.POLLIN;
+#endif
                }
 
                ~XplatUIX11() {
-                       if (DisplayHandle!=IntPtr.Zero) {
-                               XCloseDisplay(DisplayHandle);
-                               DisplayHandle=IntPtr.Zero;
+                       lock (this) {
+                               if (DisplayHandle!=IntPtr.Zero) {
+                                       XCloseDisplay(DisplayHandle);
+                                       DisplayHandle=IntPtr.Zero;
+                               }
                        }
                }
                #endregion      // Constructor & Destructor
 
                #region Singleton Specific Code
                public static XplatUIX11 GetInstance() {
-                       if (instance==null) {
-                               instance=new XplatUIX11();
+                       lock (typeof (XplatUIX11)) {
+                               if (instance==null) {
+                                       instance=new XplatUIX11();
+                               }
+                               ref_count++;
                        }
-                       ref_count++;
                        return instance;
                }
 
@@ -121,56 +183,69 @@ namespace System.Windows.Forms {
                }
                #endregion
 
+               internal override event EventHandler Idle;
+               
                #region Public Static Methods
-               internal override IntPtr InitializeDriver() {\r
-                       if (DisplayHandle==IntPtr.Zero) {
-                               DisplayHandle=XOpenDisplay(IntPtr.Zero);
-                               key_state=Keys.None;
-                               mouse_state=MouseButtons.None;
-                               mouse_position=Point.Empty;
-                       }
-
-                       return IntPtr.Zero;\r
-               }\r
-\r
-               internal static void SetDisplay(IntPtr display_handle) {\r
-                       if (display_handle != IntPtr.Zero) {\r
+               internal override IntPtr InitializeDriver() {
+                       lock (this) {
+                               if (DisplayHandle==IntPtr.Zero) {
+                                       DisplayHandle=XOpenDisplay(IntPtr.Zero);
+                                       key_state=Keys.None;
+                                       mouse_state=MouseButtons.None;
+                                       mouse_position=Point.Empty;
+                               }
+                       }
+                       return IntPtr.Zero;
+               }
+
+               internal static void SetDisplay(IntPtr display_handle) {
+                       if (display_handle != IntPtr.Zero) {
+                               IntPtr  Screen;
+
                                if (FosterParent != IntPtr.Zero) {
                                        XDestroyWindow(DisplayHandle, FosterParent);
                                }
-                               if (DisplayHandle != IntPtr.Zero) {\r
-                                       XCloseDisplay(DisplayHandle);\r
-                               }\r
-\r
-                               DisplayHandle=display_handle;\r
-\r
-                               // Create a few things\r
+                               if (DisplayHandle != IntPtr.Zero) {
+                                       XCloseDisplay(DisplayHandle);
+                               }
+
+                               DisplayHandle=display_handle;
+
+                               // We need to tell System.Drawing our DisplayHandle. FromHdcInternal has
+                               // been hacked to do this for us.
+                               Graphics.FromHdcInternal (DisplayHandle);
+
+                               // Create a few things
                                key_state = Keys.None;
                                mouse_state = MouseButtons.None;
                                mouse_position = Point.Empty;
-                               root_window = XRootWindow(display_handle, 0);
-                               default_colormap = XDefaultColormap(display_handle, 0);
+                               Screen = XDefaultScreenOfDisplay(DisplayHandle);
+                               //screen_num = XScreenNumberOfScreen(DisplayHandle, Screen);
+                               screen_num = 0;
+                               root_window = XRootWindow(display_handle, screen_num);
+                               default_colormap = XDefaultColormap(display_handle, screen_num);
 
                                // Create the foster parent
                                FosterParent=XCreateSimpleWindow(display_handle, root_window, 0, 0, 1, 1, 4, 0, 0);
                                if (FosterParent==IntPtr.Zero) {
                                        Console.WriteLine("XplatUIX11 Constructor failed to create FosterParent");
                                }
-
                                // Prepare for shutdown
                                wm_protocols=XInternAtom(display_handle, "WM_PROTOCOLS", false);
                                wm_delete_window=XInternAtom(display_handle, "WM_DELETE_WINDOW", false);
 
                                handle_data = new Hashtable ();
-                       }\r
-               }\r
+                       }
+               }
 
-               internal override void ShutdownDriver(IntPtr token) {\r
-                       if (DisplayHandle!=IntPtr.Zero) {
-                               XCloseDisplay(DisplayHandle);
-                               DisplayHandle=IntPtr.Zero;
+               internal override void ShutdownDriver(IntPtr token) {
+                       lock (this) {
+                               if (DisplayHandle!=IntPtr.Zero) {
+                                       XCloseDisplay(DisplayHandle);
+                                       DisplayHandle=IntPtr.Zero;
+                               }
                        }
-               }\r
+               }
 
 
                internal void Version() {
@@ -181,6 +256,16 @@ namespace System.Windows.Forms {
                        Console.WriteLine("XplatUIX11.Exit");
                }
 
+               internal override void GetDisplaySize(out Size size) {
+                       XWindowAttributes       attributes=new XWindowAttributes();
+
+                       lock (xlib_lock) {
+                               XGetWindowAttributes(DisplayHandle, XRootWindow(DisplayHandle, 0), ref attributes);
+                       }
+
+                       size = new Size(attributes.width, attributes.height);
+               }
+
                internal override void EnableThemes() {
                        themes_enabled=true;
                }
@@ -206,38 +291,30 @@ namespace System.Windows.Forms {
                        if (Width<1) Width=1;
                        if (Height<1) Height=1;
 
-                       if (ParentHandle==IntPtr.Zero) {
-                               if ((cp.Style & (int)WindowStyles.WS_CHILD)!=0) {
-                                       // We need to use our foster parent window until this poor child gets it's parent assigned
-                                       ParentHandle=FosterParent;
-                               } else {
-                                       if (X<1) X=50;
-                                       if (Y<1) Y=50;
-                                       BorderWidth=4;
-                                       ParentHandle=XRootWindow(DisplayHandle, 0);
-                               }
-                       }
 
-                       WindowHandle=XCreateSimpleWindow(DisplayHandle, ParentHandle, X, Y, Width, Height, BorderWidth, 0, (this.BackColor.ToArgb() & 0x00ffffff));
-                       XMapWindow(DisplayHandle, WindowHandle);
-
-                       XSelectInput(DisplayHandle, WindowHandle, 
-                               EventMask.ButtonPressMask | 
-                               EventMask.ButtonReleaseMask | 
-                               EventMask.KeyPressMask | 
-                               EventMask.KeyReleaseMask | 
-                               EventMask.EnterWindowMask | 
-                               EventMask.LeaveWindowMask |
-                               EventMask.ExposureMask |
-                               EventMask.PointerMotionMask | 
-                               EventMask.VisibilityChangeMask |
-                               EventMask.StructureNotifyMask);
+                       lock (xlib_lock) {
+                               if (ParentHandle==IntPtr.Zero) {
+                                       if ((cp.Style & (int)WindowStyles.WS_CHILD)!=0) {
+                                               // We need to use our foster parent window until
+                                               // this poor child gets it's parent assigned
+                                               ParentHandle=FosterParent;
+                                       } else {
+                                               if (X<1) X=50;
+                                               if (Y<1) Y=50;
+                                               BorderWidth=4;
+                                               ParentHandle=XRootWindow(DisplayHandle, 0);
+                                       }
+                               }
 
-                       is_visible=true;
+                               WindowHandle=XCreateSimpleWindow(DisplayHandle, ParentHandle, X, Y, Width, Height, BorderWidth, 0, 0);
+                               XMapWindow(DisplayHandle, WindowHandle);
 
-                       protocols=wm_delete_window;
-                       XSetWMProtocols(DisplayHandle, WindowHandle, ref protocols, 1);
+                               XSelectInput(DisplayHandle, WindowHandle, SelectInputMask);
+                               is_visible=true;
 
+                               protocols=wm_delete_window;
+                               XSetWMProtocols(DisplayHandle, WindowHandle, ref protocols, 1);
+                       }
                        return(WindowHandle);
                }
 
@@ -260,13 +337,14 @@ namespace System.Windows.Forms {
                }
 
                internal override void DestroyWindow(IntPtr handle) {
-                       XDestroyWindow(DisplayHandle, handle);
-                       HandleData data = (HandleData) handle_data [handle];
-                       if (data != null) {
-                               data.Dispose ();
-                               handle_data[handle] = null;
+                       lock (this) {
+                               HandleData data = (HandleData) handle_data [handle];
+                               if (data != null) {
+                                       data.Dispose ();
+                                       handle_data [handle] = null;
+                               }
+                               XDestroyWindow(DisplayHandle, handle);
                        }
-                       return;
                }
 
                internal override void RefreshWindow(IntPtr handle) {
@@ -279,19 +357,39 @@ namespace System.Windows.Forms {
                        int     width;
                        int     height;
 
-                       // We need info about our window to generate the expose
-                       XGetGeometry(DisplayHandle, handle, out root, out x, out y, out width, out height, out border_width, out depth);
+                       lock (xlib_lock) {
+
+                               // We need info about our window to generate the expose 
+                               XGetGeometry(DisplayHandle, handle, out root, out x, out y,
+                                               out width, out height, out border_width, out depth);
+
+                               xevent.type=XEventName.Expose;
+                               xevent.ExposeEvent.display=DisplayHandle;
+                               xevent.ExposeEvent.window=handle;
+                               xevent.ExposeEvent.x=0;
+                               xevent.ExposeEvent.y=0;
+                               xevent.ExposeEvent.width=width;
+                               xevent.ExposeEvent.height=height;
+
+                               XSendEvent(DisplayHandle, handle, false, EventMask.ExposureMask, ref xevent);
+                               XFlush(DisplayHandle);
+                       }
+               }
+
+               internal override void SetWindowBackground(IntPtr handle, Color color) {
+                       XColor  xcolor;
+
+                       xcolor = new XColor();
 
-                       xevent.type=XEventName.Expose;
-                       xevent.ExposeEvent.display=DisplayHandle;
-                       xevent.ExposeEvent.window=handle;
-                       xevent.ExposeEvent.x=0;
-                       xevent.ExposeEvent.y=0;
-                       xevent.ExposeEvent.width=width;
-                       xevent.ExposeEvent.height=height;
+                       xcolor.red = (ushort)(color.R * 257);
+                       xcolor.green = (ushort)(color.G * 257);
+                       xcolor.blue = (ushort)(color.B * 257);
+                       XAllocColor(DisplayHandle, default_colormap, ref xcolor);
 
-                       XSendEvent(DisplayHandle, handle, false, EventMask.ExposureMask, ref xevent);
-                       XFlush(DisplayHandle);
+                       lock (xlib_lock) {
+                               XSetWindowBackground(DisplayHandle, handle, xcolor.pixel);
+                               XClearWindow(DisplayHandle, handle);
+                       }
                }
 
                [MonoTODO("Add support for internal table of windows/DCs for looking up paint area and cleanup")]
@@ -310,12 +408,15 @@ namespace System.Windows.Forms {
                        return paint_event;
                }
 
-               internal override void PaintEventEnd(IntPtr handle) {\r
+               internal override void PaintEventEnd(IntPtr handle) {
                        HandleData data = (HandleData) handle_data [handle];
                        if (data == null)
                                throw new Exception ("null data on PaintEventEnd");
                        data.ClearInvalidArea ();
-               }\r
+                       Graphics g = (Graphics) data.DeviceContext;
+                       g.Flush ();
+                       g.Dispose ();
+               }
 
                internal override void SetWindowPos(IntPtr handle, int x, int y, int width, int height) {
                        // X requires a sanity check for width & height; otherwise it dies
@@ -326,7 +427,9 @@ namespace System.Windows.Forms {
                        if (height < 1) {
                                height = 1;
                        }
-                       XMoveResizeWindow(DisplayHandle, handle, x, y, width, height);
+                       lock (xlib_lock) {
+                               XMoveResizeWindow(DisplayHandle, handle, x, y, width, height);
+                       }
                        return;
                }
 
@@ -335,7 +438,11 @@ namespace System.Windows.Forms {
                        int     border_width;
                        int     depth;
 
-                       XGetGeometry(DisplayHandle, handle, out root, out x, out y, out width, out height, out border_width, out depth);
+                       lock (xlib_lock) {
+                               
+                               XGetGeometry(DisplayHandle, handle, out root, out x,
+                                               out y, out width, out height, out border_width, out depth);
+                       }
 
                        client_width = width;
                        client_height = height;
@@ -343,41 +450,19 @@ namespace System.Windows.Forms {
                }
 
                internal override void Activate(IntPtr handle) {
-                       // Not sure this is the right method, but we don't use ICs either...
-                       XRaiseWindow(DisplayHandle, handle);
+
+                       lock (xlib_lock) {
+                               // Not sure this is the right method, but we don't use ICs either...    
+                               XRaiseWindow(DisplayHandle, handle);
+                       }
                        return;
                }
 
                internal override void Invalidate(IntPtr handle, Rectangle rc, bool clear) {
-                       XEvent                  xevent = new XEvent();
-
-                       xevent.type=XEventName.Expose;
-                       xevent.ExposeEvent.display=DisplayHandle;
-                       xevent.ExposeEvent.window=handle;
-                       xevent.ExposeEvent.count=0;
-
-                       if (clear) {
-                               // Need to clear the whole window, so we force a redraw for the whole window
-                               XWindowAttributes       attributes=new XWindowAttributes();
-
-                               // We need info about our window to generate the expose
-                               XGetWindowAttributes(DisplayHandle, handle, ref attributes);
-
-                               xevent.ExposeEvent.x=0;
-                               xevent.ExposeEvent.y=0;
-                               xevent.ExposeEvent.width=attributes.width;
-                               xevent.ExposeEvent.height=attributes.height;
-                       } else {
-                               xevent.ExposeEvent.x=rc.Left;
-                               xevent.ExposeEvent.y=rc.Top;
-                               xevent.ExposeEvent.width=rc.Width;
-                               xevent.ExposeEvent.height=rc.Height;
+                       // FIXME - we're not properly interpreting the clear flag, we're assuming it's always true
+                       lock (xlib_lock) {
+                               XClearArea(DisplayHandle, handle, rc.Left, rc.Top, (uint)rc.Width, (uint)rc.Height, true);
                        }
-
-                       XSendEvent(DisplayHandle, handle, false, EventMask.ExposureMask, ref xevent);
-                       // Flush is not needed, invalidate does not guarantee an immediate effect
-                       //XFlush(DisplayHandle);
-                       return;
                }
 
                internal override IntPtr DefWndProc(ref Message msg) {
@@ -425,7 +510,9 @@ namespace System.Windows.Forms {
                        int     len;
                        msg.wParam = IntPtr.Zero;
 
-                       len = XLookupString(ref xevent, buffer, 24, out keysym, IntPtr.Zero);
+                       lock (xlib_lock) {
+                               len = XLookupString(ref xevent, buffer, 24, out keysym, IntPtr.Zero);
+                       }
 
                        if ((keysym==XKeySym.XK_Control_L) || (keysym==XKeySym.XK_Control_R)) {
                                if (xevent.type==XEventName.KeyPress) {
@@ -495,13 +582,143 @@ namespace System.Windows.Forms {
                        return (IntPtr)result;
                }
 
+               private int NextTimeout (DateTime now)
+               {
+                       int timeout = Int32.MaxValue; 
+                       lock (timer_list) {
+                               foreach (Timer timer in timer_list) {
+                                       int next = (int) (timer.Expires - now).TotalMilliseconds;
+                                       if (next < 0)
+                                               return 0; // Have a timer that has already expired
+                                       if (next < timeout)
+                                               timeout = next;
+                               }
+                       }
+                       if (timeout < Timer.Minimum)
+                               timeout = Timer.Minimum;
+                       return timeout;
+               }
+
+               private void CheckTimers (DateTime now)
+               {
+                       lock (timer_list) {
+                               int count = timer_list.Count;
+                               if (count == 0)
+                                       return;
+                               for (int i = 0; i < count; i++) {
+                                        Timer timer = (Timer) timer_list [i];
+                                        if (timer.Enabled && timer.Expires <= now) {
+                                                timer.FireTick ();
+                                                timer.Update (now);
+                                        }
+                                }
+                       }
+               }
+
+               private void UpdateMessageQueue ()
+               {
+                       DateTime now = DateTime.Now;
+
+                       int pending;
+                       lock (xlib_lock) {
+                               pending = XPending (DisplayHandle);
+                       }
+                       if (pending == 0) {
+                               if (Idle != null) {
+                                       Idle (this, EventArgs.Empty);
+                               }
+                               lock (xlib_lock) {
+                                       pending = XPending (DisplayHandle);
+                               }
+                       }
+
+                       if (pending == 0) {
+                               int timeout = NextTimeout (now);
+                               if (timeout > 0) {
+#if __MonoCS__
+                                       Syscall.poll (pollfds, pollfds.Length, timeout);
+#endif
+                                       pending = XPending (DisplayHandle);
+                               }
+                       }
+
+                       CheckTimers (now);
+
+                       if (pending == 0) {
+                               lock (xlib_lock) {
+                                       pending = XPending (DisplayHandle);
+                               }
+                       }
+
+                       while (pending > 0) {
+                               XEvent xevent = new XEvent ();
+
+                               lock (xlib_lock) {
+                                       XNextEvent (DisplayHandle, ref xevent);
+                               }
+                               
+                               switch (xevent.type) {
+                               case XEventName.Expose:
+                                       HandleData data = (HandleData) handle_data [xevent.AnyEvent.window];
+                                       if (data == null) {
+                                               data = new HandleData ();
+                                               handle_data [xevent.AnyEvent.window] = data;
+                                       }
+                                  
+                                       data.AddToInvalidArea (xevent.ExposeEvent.x, xevent.ExposeEvent.y,
+                                                       xevent.ExposeEvent.width, xevent.ExposeEvent.height);
+                                  
+                                       if (!data.HasExpose) {
+                                               lock (message_queue) {
+                                                       message_queue.Enqueue (xevent);
+                                               }
+                                               data.HasExpose = true;
+                                       }
+                                       break;
+                               case XEventName.KeyPress:
+                               case XEventName.KeyRelease:
+                               case XEventName.ButtonPress:
+                               case XEventName.ButtonRelease:
+                               case XEventName.MotionNotify:
+                               case XEventName.EnterNotify:
+                               case XEventName.LeaveNotify:
+                               case XEventName.ConfigureNotify:
+                               case XEventName.DestroyNotify:
+                               case XEventName.ClientMessage:
+                                       lock (message_queue) {
+                                               message_queue.Enqueue (xevent);
+                                       }
+                                       break;
+                               }
+
+                               lock (xlib_lock) {
+                                       pending = XPending (DisplayHandle);
+                               }
+                       }
+               }
+
                internal override bool GetMessage(ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax) {
-                       XEvent  xevent = new XEvent();
+                       XEvent  xevent;
+
+                       if (message_queue.Count > 0) {
+                               xevent = (XEvent) message_queue.Dequeue ();
+                       } else {
+                               UpdateMessageQueue ();
+                               if (message_queue.Count > 0) {
+                                       xevent = (XEvent) message_queue.Dequeue ();
+                               } else {
+                                       msg.hwnd= IntPtr.Zero;
+                                       msg.message = Msg.WM_ENTERIDLE;
+                                       return true;
+                               }
+                       }
 
-               begin:
-                       XNextEvent(DisplayHandle, ref xevent);
                        msg.hwnd=xevent.AnyEvent.window;
 
+                       //
+                       // If you add a new event to this switch make sure to add it in
+                       // UpdateMessage also unless it is not coming through the X event system.
+                       //
                        switch(xevent.type) {
                                case XEventName.KeyPress: {
                                        msg.message = Msg.WM_KEYDOWN;
@@ -518,21 +735,21 @@ namespace System.Windows.Forms {
                                case XEventName.ButtonPress: {
                                        switch(xevent.ButtonEvent.button) {
                                                case 1: {
-                                                       mouse_state=MouseButtons.Left;
+                                                       mouse_state |= MouseButtons.Left;
                                                        msg.message=Msg.WM_LBUTTONDOWN;
                                                        msg.wParam=GetMousewParam(0);
                                                        break;
                                                }
 
                                                case 2: {
-                                                       mouse_state=MouseButtons.Middle;
+                                                       mouse_state |= MouseButtons.Middle;
                                                        msg.message=Msg.WM_MBUTTONDOWN;
                                                        msg.wParam=GetMousewParam(0);
                                                        break;
                                                }
 
                                                case 3: {
-                                                       mouse_state=MouseButtons.Right;
+                                                       mouse_state |= MouseButtons.Right;
                                                        msg.message=Msg.WM_RBUTTONDOWN;
                                                        msg.wParam=GetMousewParam(0);
                                                        break;
@@ -561,21 +778,21 @@ namespace System.Windows.Forms {
                                case XEventName.ButtonRelease: {
                                        switch(xevent.ButtonEvent.button) {
                                                case 1: {
-                                                       mouse_state=MouseButtons.Left;
+                                                       mouse_state &= ~MouseButtons.Left;
                                                        msg.message=Msg.WM_LBUTTONUP;
                                                        msg.wParam=GetMousewParam(0);
                                                        break;
                                                }
 
                                                case 2: {
-                                                       mouse_state=MouseButtons.Middle;
+                                                       mouse_state &= ~MouseButtons.Middle;
                                                        msg.message=Msg.WM_MBUTTONUP;
                                                        msg.wParam=GetMousewParam(0);
                                                        break;
                                                }
 
                                                case 3: {
-                                                       mouse_state=MouseButtons.Right;
+                                                       mouse_state &= ~MouseButtons.Right;
                                                        msg.message=Msg.WM_RBUTTONUP;
                                                        msg.wParam=GetMousewParam(0);
                                                        break;
@@ -606,11 +823,17 @@ namespace System.Windows.Forms {
                                }
 
                                case XEventName.EnterNotify: {
+                                       if (xevent.CrossingEvent.mode != NotifyMode.NotifyNormal) {
+                                               return true;
+                                       }
                                        msg.message=Msg.WM_MOUSE_ENTER;
                                        break;
                                }
 
                                case XEventName.LeaveNotify: {
+                                       if (xevent.CrossingEvent.mode != NotifyMode.NotifyNormal) {
+                                               return true;
+                                       }
                                        msg.message=Msg.WM_MOUSE_LEAVE;
                                        break;
                                }
@@ -624,28 +847,9 @@ namespace System.Windows.Forms {
                                }
 
                                case XEventName.Expose: {
-                                       HandleData data = (HandleData) handle_data [xevent.AnyEvent.window];
-                                       if (data == null) {
-                                               data = new HandleData ();
-                                               handle_data [xevent.AnyEvent.window] = data;
-                                       }
-
-                                       data.AddToInvalidArea (xevent.ExposeEvent.x, xevent.ExposeEvent.y,
-                                                       xevent.ExposeEvent.width, xevent.ExposeEvent.height);
-
-                                       // Only paint on the last of a series of expose events
-                                       if (xevent.ExposeEvent.count > 0) {
-                                               goto begin;
-                                       }
-
-                                       // Try combining expose events to reduce drawing
-                                       while (XCheckWindowEvent (DisplayHandle, xevent.AnyEvent.window,
-                                                           EventMask.ExposureMask, ref xevent)) {
-                                               data.AddToInvalidArea (xevent.ExposeEvent.x, xevent.ExposeEvent.y,
-                                                       xevent.ExposeEvent.width, xevent.ExposeEvent.height);
-                                       }
-
                                        msg.message=Msg.WM_PAINT;
+                                       msg.wParam=IntPtr.Zero;
+                                       msg.lParam=IntPtr.Zero;
                                        break;
                                }
 
@@ -657,19 +861,32 @@ namespace System.Windows.Forms {
                                }
 
                                case XEventName.ClientMessage: {
-                                       if (xevent.ClientMessageEvent.l0==wm_delete_window) {
+                                       if (xevent.ClientMessageEvent.message_type == async_method) {
+                                               GCHandle handle = (GCHandle)xevent.ClientMessageEvent.ptr1;
+                                               AsyncMethodData data = (AsyncMethodData) handle.Target;
+                                               AsyncMethodResult result = data.Result.Target as AsyncMethodResult;
+                                               object ret = data.Method.DynamicInvoke (data.Args);
+                                               if (result != null)
+                                                       result.Complete (ret);
+                                               handle.Free ();
+                                       } else {
                                                msg.message=Msg.WM_QUIT;
                                                msg.wParam=IntPtr.Zero;
                                                msg.lParam=IntPtr.Zero;
                                                return false;
                                        }
-                                       return true;
-                                       break;
-                               }
-                               default: {
-                                       msg.message = Msg.WM_NULL;
                                        break;
                                }
+
+                        case XEventName.TimerNotify: {
+                                xevent.TimerNotifyEvent.handler (this, EventArgs.Empty);
+                                break;
+                        }
+                               
+                       default: {
+                               msg.message = Msg.WM_NULL;
+                               break;
+                       }
                        }
 
                        NativeWindow.WndProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
@@ -685,6 +902,24 @@ namespace System.Windows.Forms {
                        return true;
                }
 
+               internal override bool SetZOrder(IntPtr hWnd, IntPtr AfterhWnd, bool Top, bool Bottom) {
+                       if (Top) {
+                               XRaiseWindow(DisplayHandle, hWnd);
+                               return true;
+                       } else if (!Bottom) {
+                               XWindowChanges  values = new XWindowChanges();
+
+                               values.sibling = AfterhWnd;
+                               values.stack_mode = StackMode.Below;
+                               XConfigureWindow(DisplayHandle, hWnd, ChangeWindowFlags.CWStackMode, ref values);
+                       } else {
+                               XLowerWindow(DisplayHandle, hWnd);
+                               return true;
+                       }
+                       return false;
+               }
+
+
                internal override bool Text(IntPtr handle, string text) {
 #if notdef
                        XTextProperty   property = new XTextProperty();
@@ -692,18 +927,40 @@ namespace System.Windows.Forms {
                        property.encoding=
                        XSetWMName(DisplayHandle, handle, ref property);
 #else
-                       XStoreName(DisplayHandle, handle, text);
+                       lock (xlib_lock) {
+                               XStoreName(DisplayHandle, handle, text);
+                       }
 #endif
                        return true;
                }
 
-               internal override bool SetVisible(IntPtr handle, bool visible) {
-                       if (visible) {
-                               XMapWindow(DisplayHandle, handle);
-                               is_visible=true;
+               internal override bool GetText(IntPtr handle, out string text) {
+                       IntPtr  textptr;
+
+                       textptr = IntPtr.Zero;
+
+                       lock (xlib_lock) {
+                               XFetchName(DisplayHandle, handle, ref textptr);
+                       }
+                       if (textptr != IntPtr.Zero) {
+                               text = Marshal.PtrToStringAnsi(textptr);
+                               XFree(textptr);
+                               return true;
                        } else {
-                               XUnmapWindow(DisplayHandle, handle);
-                               is_visible=false;
+                               text = "";
+                               return false;
+                       }
+               }
+
+               internal override bool SetVisible(IntPtr handle, bool visible) {
+                       lock (xlib_lock) {
+                               if (visible) {
+                                       XMapWindow(DisplayHandle, handle);
+                                       is_visible=true;
+                               } else {
+                                       XUnmapWindow(DisplayHandle, handle);
+                                       is_visible=false;
+                               }
                        }
                        return true;
                }
@@ -715,8 +972,10 @@ namespace System.Windows.Forms {
                internal override IntPtr SetParent(IntPtr handle, IntPtr parent) {
                        XWindowAttributes       attributes=new XWindowAttributes();
 
-                       XGetWindowAttributes(DisplayHandle, handle, ref attributes);
-                       XReparentWindow(DisplayHandle, handle, parent, attributes.x, attributes.y);
+                       lock (xlib_lock) {
+                               XGetWindowAttributes(DisplayHandle, handle, ref attributes);
+                               XReparentWindow(DisplayHandle, handle, parent, attributes.x, attributes.y);
+                       }
                        return IntPtr.Zero;
                }
 
@@ -731,20 +990,52 @@ namespace System.Windows.Forms {
                        Children=IntPtr.Zero;
                        ChildCount=0;
 
-                       XQueryTree(DisplayHandle, handle, ref Root, ref Parent, ref Children, ref ChildCount);
+                       lock (xlib_lock) {
+                               XQueryTree(DisplayHandle, handle, ref Root, ref Parent, ref Children, ref ChildCount);
+                       }
 
                        if (Children!=IntPtr.Zero) {
-                               XFree(Children);
+                               lock (xlib_lock) {
+                                       XFree(Children);
+                               }
                        }
                        return Parent;
                }
-\r
-               internal override void GrabWindow(IntPtr hWnd) {
-                       XGrabPointer(DisplayHandle, hWnd, false, EventMask.ButtonPressMask | EventMask.ButtonMotionMask | EventMask.ButtonReleaseMask | EventMask.PointerMotionMask, GrabMode.GrabModeAsync, GrabMode.GrabModeAsync, IntPtr.Zero, 0, 0);
+
+               internal override void GrabWindow(IntPtr hWnd, IntPtr confine_hwnd) {
+                       if (confine_hwnd != IntPtr.Zero) {
+                               XWindowAttributes       attributes = new XWindowAttributes();
+
+                               lock (xlib_lock) {
+                                       XGetWindowAttributes(DisplayHandle, confine_hwnd, ref attributes);
+                               }
+                               grab_area.X = attributes.x;
+                               grab_area.Y = attributes.y;
+                               grab_area.Width = attributes.width;
+                               grab_area.Height = attributes.height;
+                               grab_confined = true;
+                       }
+                       grab_hwnd = hWnd;
+                       lock (xlib_lock) {
+                               XGrabPointer(DisplayHandle, hWnd, false,
+                                       EventMask.ButtonPressMask | EventMask.ButtonMotionMask |
+                                       EventMask.ButtonReleaseMask | EventMask.PointerMotionMask,
+                                       GrabMode.GrabModeAsync, GrabMode.GrabModeAsync, confine_hwnd, 0, 0);
+                       }
+               }
+
+               internal override void GrabInfo(out IntPtr hWnd, out bool GrabConfined, out Rectangle GrabArea) {
+                       hWnd = grab_hwnd;
+                       GrabConfined = grab_confined;
+                       GrabArea = grab_area;
                }
 
                internal override void ReleaseWindow(IntPtr hWnd) {
-                       XUngrabPointer(DisplayHandle, 0);
+                       lock (xlib_lock) {
+                               XUngrabPointer(DisplayHandle, 0);
+                               grab_hwnd = IntPtr.Zero;
+                               grab_confined = false;
+                       }
                }
 
                internal override bool CalculateWindowRect(IntPtr hWnd, ref Rectangle ClientRect, int Style, bool HasMenu, out Rectangle WindowRect) {
@@ -752,6 +1043,12 @@ namespace System.Windows.Forms {
                        return true;
                }
 
+               internal override void SetCursorPos(IntPtr handle, int x, int y) {
+                       lock (xlib_lock) {
+                               XWarpPointer(DisplayHandle, IntPtr.Zero, (handle!=IntPtr.Zero) ? handle : IntPtr.Zero, 0, 0, 0, 0, x, y);
+                       }
+               }
+
                internal override void GetCursorPos(IntPtr handle, out int x, out int y) {
                        IntPtr  root;
                        IntPtr  child;
@@ -761,7 +1058,12 @@ namespace System.Windows.Forms {
                        int     win_y;
                        int     keys_buttons;
 
-                       XQueryPointer(DisplayHandle, (handle!=IntPtr.Zero) ? handle : root_window, out root, out child, out root_x, out root_y, out win_x, out win_y, out keys_buttons);
+                       lock (xlib_lock) {
+                               XQueryPointer(DisplayHandle, (handle!=IntPtr.Zero) ? handle : root_window,
+                                               out root, out child, out root_x, out root_y,
+                                               out win_x, out win_y, out keys_buttons);
+                       }
+
                        if (handle != IntPtr.Zero) {
                                x = win_x;
                                y = win_y;
@@ -777,14 +1079,69 @@ namespace System.Windows.Forms {
                        int     dest_y_return;
                        IntPtr  child;
 
-                       XTranslateCoordinates (DisplayHandle, root_window, handle, x, y, out dest_x_return, out dest_y_return, out child);
+                       lock (xlib_lock) {
+                               XTranslateCoordinates (DisplayHandle, root_window,
+                                               handle, x, y, out dest_x_return, out dest_y_return, out child);
+                       }
 
                        x = dest_x_return;
                        y = dest_y_return;
                }
 
+               internal override void ClientToScreen(IntPtr handle, ref int x, ref int y) {
+                       int     dest_x_return;
+                       int     dest_y_return;
+                       IntPtr  child;
+
+                       lock (xlib_lock) {
+                               XTranslateCoordinates (DisplayHandle, handle, root_window,
+                                       x, y, out dest_x_return, out dest_y_return, out child);
+                       }
+
+                       x = dest_x_return;
+                       y = dest_y_return;
+               }
+
+               internal override void SendAsyncMethod (AsyncMethodData method)
+               {
+                       XEvent xevent = new XEvent ();
+                       
+                       xevent.type = XEventName.ClientMessage;
+                       xevent.ClientMessageEvent.display = DisplayHandle;
+                       xevent.ClientMessageEvent.window = IntPtr.Zero;
+                       xevent.ClientMessageEvent.message_type = async_method;
+                       xevent.ClientMessageEvent.format = 32;
+                       xevent.ClientMessageEvent.ptr1 = (IntPtr) GCHandle.Alloc (method);
+
+                       lock (message_queue) {
+                               message_queue.Enqueue (xevent);
+                       }
+
+                       WakeupMain ();
+               }
+
+               private void WakeupMain ()
+               {
+                       wake.BeginSend (new byte [] { 0xFF }, 0, 1, SocketFlags.None, null, null);
+               }
+
+               internal override void SetTimer (Timer timer)
+               {
+                       lock (timer_list) {
+                               timer_list.Add (timer);
+                       }
+                       WakeupMain ();
+               }
+
+               internal override void KillTimer (Timer timer)
+               {
+                       lock (timer_list) {
+                               timer_list.Remove (timer);
+                       }
+               }
+
                // Santa's little helper
-               static void Where() \r
+               static void Where() 
                {
                        Console.WriteLine("Here: {0}", new StackTrace().ToString());
                }
@@ -851,7 +1208,13 @@ namespace System.Windows.Forms {
                [DllImport ("libX11.so", EntryPoint="XNextEvent")]
                internal extern static IntPtr XNextEvent(IntPtr display, ref XEvent xevent);
                [DllImport ("libX11.so")]
+               internal extern static int XConnectionNumber (IntPtr diplay);
+               [DllImport ("libX11.so")]
+               internal extern static int XPending (IntPtr diplay);
+               [DllImport ("libX11.so")]
                internal extern static bool XCheckWindowEvent (IntPtr display, IntPtr window, EventMask mask, ref XEvent xevent);
+               [DllImport ("libX11.so")]
+               internal extern static bool XCheckMaskEvent (IntPtr display, EventMask mask, ref XEvent xevent);
                [DllImport ("libX11.so", EntryPoint="XSelectInput")]
                internal extern static IntPtr XSelectInput(IntPtr display, IntPtr window, EventMask mask);
                [DllImport ("libX11.so", EntryPoint="XLookupString")]
@@ -880,6 +1243,9 @@ namespace System.Windows.Forms {
                [DllImport ("libX11.so", EntryPoint="XStoreName")]
                internal extern static int XStoreName(IntPtr display, IntPtr window, string window_name);
 
+               [DllImport ("libX11.so", EntryPoint="XFetchName")]
+               internal extern static int XFetchName(IntPtr display, IntPtr window, ref IntPtr window_name);
+
                [DllImport ("libX11.so", EntryPoint="XSendEvent")]
                internal extern static int XSendEvent(IntPtr display, IntPtr window, bool propagate, EventMask event_mask, ref XEvent send_event);
 
@@ -892,6 +1258,12 @@ namespace System.Windows.Forms {
                [DllImport ("libX11.so", EntryPoint="XRaiseWindow")]
                internal extern static int XRaiseWindow(IntPtr display, IntPtr window);
 
+               [DllImport ("libX11.so", EntryPoint="XLowerWindow")]
+               internal extern static uint XLowerWindow(IntPtr display, IntPtr window);
+
+               [DllImport ("libX11.so", EntryPoint="XConfigureWindow")]
+               internal extern static uint XConfigureWindow(IntPtr display, IntPtr window, ChangeWindowFlags value_mask, ref XWindowChanges values);
+
                [DllImport ("libX11.so", EntryPoint="XInternAtom")]
                internal extern static int XInternAtom(IntPtr display, string atom_name, bool only_if_exists);
 
@@ -908,34 +1280,42 @@ namespace System.Windows.Forms {
                internal extern static bool XQueryPointer(IntPtr display, IntPtr window, out IntPtr root, out IntPtr child, out int root_x, out int root_y, out int win_x, out int win_y, out int keys_buttons);
 
                [DllImport ("libX11.so", EntryPoint="XTranslateCoordinates")]
-               internal extern static bool XTranslateCoordinates (IntPtr display, IntPtr src_w, IntPtr dest_w, int src_x, int src_y, out int intdest_x_return,  out int dest_y_return, out IntPtr child_return);
+               internal extern static bool XTranslateCoordinates (IntPtr display, IntPtr src_w, IntPtr dest_w, int src_x, int src_y, out int intdest_x_return,  out int dest_y_return, out IntPtr child_return);
 
                [DllImport ("libX11.so", EntryPoint="XGetGeometry")]
                internal extern static bool XGetGeometry(IntPtr display, IntPtr window, out IntPtr root, out int x, out int y, out int width, out int height, out int border_width, out int depth);
 
-               [DllImport ("libX11.so", EntryPoint="XAllocColor")]
-               internal extern static int XAllocColor(IntPtr display, uint Colormap, ref XColor colorcell_def);
+               [DllImport ("libX11.so", EntryPoint="XWarpPointer")]
+               internal extern static uint XWarpPointer(IntPtr display, IntPtr src_w, IntPtr dest_w, int src_x, int src_y, uint src_width, uint src_height, int dest_x, int dest_y);
 
-               [DllImport ("libX11.so", EntryPoint="XGetStandardColormap")]
-               internal extern static int XGetStandardColormap(IntPtr display, IntPtr window, ref XStandardColormap cmap_info, Atom property);
+               [DllImport ("libX11.so", EntryPoint="XClearWindow")]
+               internal extern static int XClearWindow(IntPtr display, IntPtr window);
 
-               [DllImport ("libX11.so", EntryPoint="XSetRGBColormaps")]
-               internal extern static int XSetRGBColormaps(IntPtr display, IntPtr window, ref XStandardColormap cmap_info, int count, Atom property);
+               [DllImport ("libX11.so", EntryPoint="XClearArea")]
+               internal extern static int XClearArea(IntPtr display, IntPtr window, int x, int y, uint width, uint height, bool exposures);
 
-               [DllImport ("libX11.so", EntryPoint="XInstallColormap")]
-               internal extern static int XInstallColormap(IntPtr display, uint cmap);
+               // Colormaps
+               [DllImport ("libX11.so", EntryPoint="XDefaultScreenOfDisplay")]
+               internal extern static IntPtr XDefaultScreenOfDisplay(IntPtr display);
 
-               [DllImport ("libX11.so", EntryPoint="XDefaultColormap")]
-               internal extern static uint XDefaultColormap(IntPtr display, int screen_number);
+               [DllImport ("libX11.so", EntryPoint="XScreenNumberOfScreen")]
+               internal extern static int XScreenNumberOfScreen(IntPtr display, IntPtr Screen);
+
+               [DllImport ("libX11.so", EntryPoint="XDefaultVisual")]
+               internal extern static uint XDefaultVisual(IntPtr display, int screen_number);
 
                [DllImport ("libX11.so", EntryPoint="XDefaultDepth")]
                internal extern static uint XDefaultDepth(IntPtr display, int screen_number);
 
-               [DllImport ("libX11.so", EntryPoint="XDefaultVisual")]
-               internal extern static uint XDefaultVisual(IntPtr display, int screen_number);
+               [DllImport ("libX11.so", EntryPoint="XDefaultColormap")]
+               internal extern static uint XDefaultColormap(IntPtr display, int screen_number);
+
+               [DllImport ("libX11.so", EntryPoint="XLookupColor")]
+               internal extern static int XLookupColor(IntPtr display, uint Colormap, string Coloranem, ref XColor exact_def_color, ref XColor screen_def_color);
+
+               [DllImport ("libX11.so", EntryPoint="XAllocColor")]
+               internal extern static int XAllocColor(IntPtr display, uint Colormap, ref XColor colorcell_def);
 
-               [DllImport ("libX11.so", EntryPoint="XSetWindowColormap")]
-               internal extern static uint XSetWindowColormap(IntPtr display, IntPtr window, uint cmap);
 
                // Drawing
                [DllImport ("libX11.so", EntryPoint="XCreateGC")]