- Added code to destroy the X window
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / XplatUIX11.cs
index 6b14ad2d34d178cf351d9948d77c3aa98be94fbd..c1197f4cac3d75982ef8f0c4623401e4d398b361 100644 (file)
 //     Peter Bartok    pbartok@novell.com
 //
 //
-// $Revision: 1.15 $
-// $Modtime: $
-// $Log: XplatUIX11.cs,v $
-// Revision 1.15  2004/08/09 22:09:47  pbartok
-// - Added handling for middle and right mousebutton
-// - Added handling for mouse wheel
-// - Added handling for key state and mouse state and position
-// - Now properly generates WM_xBUTTONx messages and WM_MOUSEWHEEL messages
-//
-// Revision 1.14  2004/08/09 20:55:59  pbartok
-// - Removed Run method, was only required for initial development
-//
-// Revision 1.13  2004/08/09 20:51:25  pbartok
-// - Implemented GrabWindow/ReleaseWindow methods to allow pointer capture
-//
-// Revision 1.12  2004/08/09 19:48:08  pbartok
-// - Fixed default sizing for child windows
-//
-// Revision 1.11  2004/08/09 18:56:55  pbartok
-// - Added generation of WM_DESTROY message
-// - Added handling of window manager induced shutdown
-//
-// Revision 1.10  2004/08/09 16:05:16  jackson
-// These properties are handled by the theme now.
-//
-// Revision 1.9  2004/08/08 21:08:10  jordi
-// fixes keyboard crash
-//
-// Revision 1.8  2004/08/06 23:46:56  pbartok
-// - Implemented GetParent
-//
-// Revision 1.7  2004/08/06 23:17:44  pbartok
-// - Fixed Refresh and Invalidate
-//
-// Revision 1.6  2004/08/06 21:30:56  pbartok
-// - Fixed recursive loop when resizing
-// - Improved/fixed redrawing on expose messages
-//
-// Revision 1.5  2004/08/06 15:53:39  jordi
-// X11 keyboard navigation
-//
-// Revision 1.4  2004/08/06 14:02:33  pbartok
-// - Fixed reparenting
-// - Fixed window border creation
-//
-// Revision 1.3  2004/08/05 21:38:02  pbartok
-// - Attempted fix for reparenting problems
-//
-// Revision 1.2  2004/08/04 20:11:24  pbartok
-// - Added Invalidate handling
-//
-// Revision 1.1  2004/07/09 05:21:25  pbartok
-// - Initial check-in
-//
-//
 
 // 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 {
-       public class XplatUIX11 : XplatUIDriver {
+       internal class XplatUIX11 : XplatUIDriver {
                #region Local Variables
                private static XplatUIX11       instance;
                private static int              ref_count;
+               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 Rectangle       paint_area;
+               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
 
                internal override Keys ModifierKeys {
@@ -137,42 +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
-                       DisplayHandle=XOpenDisplay(IntPtr.Zero);
-                       key_state=Keys.None;
-                       mouse_state=MouseButtons.None;
-                       mouse_position=Point.Empty;
-                       paint_area=new Rectangle(0, 0, 0, 0);
+                       SetDisplay(XOpenDisplay(IntPtr.Zero));
 
-                       // Create the foster parent
-                       FosterParent=XCreateSimpleWindow(DisplayHandle, XRootWindow(DisplayHandle, 0), 0, 0, 1, 1, 4, 0, 0);
-                       if (FosterParent==IntPtr.Zero) {
-                               Console.WriteLine("XplatUIX11 Constructor failed to create FosterParent");
-                       }
+                       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;
 
-                       // Prepare for shutdown
-                       wm_protocols=XInternAtom(DisplayHandle, "WM_PROTOCOLS", false);
-                       wm_delete_window=XInternAtom(DisplayHandle, "WM_DELETE_WINDOW", false);
+                       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;
                }
 
@@ -183,24 +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;
+               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) {
+                                       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;
+                               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);
 
-                       return IntPtr.Zero;\r
-               }\r
+                               handle_data = new Hashtable ();
+                       }
+               }
 
-               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() {
@@ -211,15 +256,29 @@ 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;
+               }
+
                internal override IntPtr CreateWindow(CreateParams cp) {
-                       IntPtr  WindowHandle;
-                       IntPtr  ParentHandle;
-                       int     X;
-                       int     Y;
-                       int     Width;
-                       int     Height;
-                       int     BorderWidth;
-                       int     protocols;
+                       IntPtr                  WindowHandle;
+                       IntPtr                  ParentHandle;
+                       int                     X;
+                       int                     Y;
+                       int                     Width;
+                       int                     Height;
+                       int                     BorderWidth;
+                       int                     protocols;
 
                        ParentHandle=cp.Parent;
 
@@ -232,39 +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, 0);
-                       XMapWindow(DisplayHandle, WindowHandle);
+                       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);
+                                       }
+                               }
 
-                       XSelectInput(DisplayHandle, WindowHandle, 
-                               EventMask.ButtonPressMask | 
-                               EventMask.ButtonReleaseMask | 
-                               EventMask.KeyPressMask | 
-                               EventMask.KeyReleaseMask | 
-                               EventMask.EnterWindowMask | 
-                               EventMask.LeaveWindowMask |
-                               EventMask.ExposureMask |
-                               EventMask.PointerMotionMask | 
-                               EventMask.ResizeRedirectMask |
-                               EventMask.VisibilityChangeMask |
-                               EventMask.StructureNotifyMask);
-                       XSetWindowBackground(DisplayHandle, WindowHandle, (uint)this.BackColor.ToArgb());
-                       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);
                }
 
@@ -287,99 +337,134 @@ namespace System.Windows.Forms {
                }
 
                internal override void DestroyWindow(IntPtr handle) {
-                       XDestroyWindow(DisplayHandle, handle);
-                       return;
+                       lock (this) {
+                               HandleData data = (HandleData) handle_data [handle];
+                               if (data != null) {
+                                       data.Dispose ();
+                                       handle_data [handle] = null;
+                               }
+                               XDestroyWindow(DisplayHandle, handle);
+                       }
                }
 
                internal override void RefreshWindow(IntPtr handle) {
-                       XWindowAttributes       attributes=new XWindowAttributes();
-                       XEvent                  xevent = new XEvent();
+                       XEvent  xevent = new XEvent();
+                       IntPtr  root;
+                       int     border_width;
+                       int     depth;
+                       int     x;
+                       int     y;
+                       int     width;
+                       int     height;
+
+                       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;
 
-                       // We need info about our window to generate the expose
-                       XGetWindowAttributes(DisplayHandle, handle, ref attributes);
+                       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=attributes.width;
-                       xevent.ExposeEvent.height=attributes.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")]
                internal override PaintEventArgs PaintEventStart(IntPtr handle) {
                        PaintEventArgs  paint_event;
-                       Rectangle       update_area;
 
-                       // FIXME: Assign proper values
-                       update_area = new Rectangle();
-                       paint_event = new PaintEventArgs(Graphics.FromHwnd(handle), update_area);
+                       HandleData data = (HandleData) handle_data [handle];
+                       if (data == null) {
+                               throw new Exception ("null data on paint event start: " + handle);
 
-                       return paint_event;
-               }
+                       }
 
-               internal override void PaintEventEnd(IntPtr handle) {\r
-                       // FIXME: Lookup in the internal list how to clean\r
-                       ;\r
-               }\r
+                       data.DeviceContext = Graphics.FromHwnd (handle);
+                       paint_event = new PaintEventArgs((Graphics)data.DeviceContext, data.InvalidArea);
 
-               internal override void SetWindowPos(IntPtr handle, Rectangle rc) {
-                       SetWindowPos(handle, rc.X, rc.Y, rc.Width, rc.Height);
-                       return;
+                       return paint_event;
                }
 
-               internal override bool MoveWindow(IntPtr hWnd, int x, int y, int width, int height) {
-                       XMoveResizeWindow(DisplayHandle, hWnd, x, y, width, height);
-                       return true;
+               internal override void PaintEventEnd(IntPtr handle) {
+                       HandleData data = (HandleData) handle_data [handle];
+                       if (data == null)
+                               throw new Exception ("null data on PaintEventEnd");
+                       data.ClearInvalidArea ();
+                       Graphics g = (Graphics) data.DeviceContext;
+                       g.Flush ();
+                       g.Dispose ();
                }
 
                internal override void SetWindowPos(IntPtr handle, int x, int y, int width, int height) {
-                       XMoveResizeWindow(DisplayHandle, handle, x, y, width, height);
-                       return;
-               }
+                       // X requires a sanity check for width & height; otherwise it dies
+                       if (width < 1) {
+                               width = 1;
+                       }
 
-               internal override void Activate(IntPtr handle) {
-                       // Not sure this is the right method, but we don't use ICs either...
-                       XRaiseWindow(DisplayHandle, handle);
+                       if (height < 1) {
+                               height = 1;
+                       }
+                       lock (xlib_lock) {
+                               XMoveResizeWindow(DisplayHandle, handle, x, y, width, height);
+                       }
                        return;
                }
 
-               internal override void Invalidate(IntPtr handle, Rectangle rc, bool clear) {
-                       XEvent                  xevent = new XEvent();
+               internal override void GetWindowPos(IntPtr handle, out int x, out int y, out int width, out int height, out int client_width, out int client_height) {
+                       IntPtr  root;
+                       int     border_width;
+                       int     depth;
 
-                       xevent.type=XEventName.Expose;
-                       xevent.ExposeEvent.display=DisplayHandle;
-                       xevent.ExposeEvent.window=handle;
-                       xevent.ExposeEvent.count=0;
+                       lock (xlib_lock) {
+                               
+                               XGetGeometry(DisplayHandle, handle, out root, out x,
+                                               out y, out width, out height, out border_width, out depth);
+                       }
 
-                       if (clear) {
-                               // Need to clear the whole window, so we force a redraw for the whole window
-                               XWindowAttributes       attributes=new XWindowAttributes();
+                       client_width = width;
+                       client_height = height;
+                       return;
+               }
 
-                               // We need info about our window to generate the expose
-                               XGetWindowAttributes(DisplayHandle, handle, ref attributes);
+               internal override void Activate(IntPtr handle) {
 
-                               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;
+                       lock (xlib_lock) {
+                               // Not sure this is the right method, but we don't use ICs either...    
+                               XRaiseWindow(DisplayHandle, handle);
                        }
-
-                       XSendEvent(DisplayHandle, handle, false, EventMask.ExposureMask, ref xevent);
-                       // Flush is not needed, invalidate does not guarantee an immediate effect
-                       //XFlush(DisplayHandle);
                        return;
                }
 
+               internal override void Invalidate(IntPtr handle, Rectangle rc, bool clear) {
+                       // 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);
+                       }
+               }
+
                internal override IntPtr DefWndProc(ref Message msg) {
 #if not
                        switch (msg.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) {
@@ -456,7 +543,7 @@ namespace System.Windows.Forms {
                                }                                                       
                        }
 
-                       if (msg.wParam == IntPtr.Zero) {
+                       if ((msg.wParam == IntPtr.Zero) && (keys.Length>0)) {
                                char[] keychars;                                
                                keychars=keys.ToCharArray(0, 1);
                                msg.wParam=(IntPtr)keychars[0];
@@ -495,11 +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;
+                               }
+                       }
 
-                       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;
@@ -516,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;
@@ -559,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;
@@ -603,13 +822,34 @@ namespace System.Windows.Forms {
                                        break;
                                }
 
+                               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;
+                               }
+
+                               case XEventName.ConfigureNotify: {
+                                       msg.message=Msg.WM_WINDOWPOSCHANGED;
+                                       msg.wParam=IntPtr.Zero;
+                                       msg.lParam=IntPtr.Zero;
+
+                                       break;
+                               }
+
                                case XEventName.Expose: {
-                                       // We might be able to do some optimizations by using count and combining regions
                                        msg.message=Msg.WM_PAINT;
-                                       paint_area.X=xevent.ExposeEvent.x;
-                                       paint_area.Y=xevent.ExposeEvent.y;
-                                       paint_area.Width=xevent.ExposeEvent.width;
-                                       paint_area.Height=xevent.ExposeEvent.height;
+                                       msg.wParam=IntPtr.Zero;
+                                       msg.lParam=IntPtr.Zero;
                                        break;
                                }
 
@@ -621,15 +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;
                                }
+
+                        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);
@@ -645,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();
@@ -652,20 +927,41 @@ 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;
+                               }
                        }
-//                     Console.WriteLine("Setting window visibility: {0}", visible);
                        return true;
                }
 
@@ -676,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;
                }
 
@@ -692,24 +990,159 @@ 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) {
+                       WindowRect = new Rectangle(ClientRect.Left, ClientRect.Top, ClientRect.Width, ClientRect.Height);
+                       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;
+                       int     root_x;
+                       int     root_y;
+                       int     win_x;
+                       int     win_y;
+                       int     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;
+                       } else {
+                               x = root_x;
+                               y = root_y;
+                       }
+               }
+
+               internal override void ScreenToClient(IntPtr handle, ref int x, ref int y)
+               {
+                       int     dest_x_return;
+                       int     dest_y_return;
+                       IntPtr  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() {
+               static void Where() 
+               {
                        Console.WriteLine("Here: {0}", new StackTrace().ToString());
                }
                #endregion      // Public Static Methods
@@ -774,6 +1207,14 @@ namespace System.Windows.Forms {
                internal extern static IntPtr XRootWindow(IntPtr display, int screen_number);
                [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")]
@@ -787,6 +1228,9 @@ namespace System.Windows.Forms {
                [DllImport ("libX11.so", EntryPoint="XMoveResizeWindow")]
                internal extern static int XMoveResizeWindow(IntPtr display, IntPtr window, int x, int y, int width, int height);
 
+               [DllImport ("libX11.so", EntryPoint="XResizeWindow")]
+               internal extern static int XResizeWindow(IntPtr display, IntPtr window, int width, int height);
+
                [DllImport ("libX11.so", EntryPoint="XGetWindowAttributes")]
                internal extern static int XGetWindowAttributes(IntPtr display, IntPtr window, ref XWindowAttributes attributes);
 
@@ -799,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);
 
@@ -811,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);
 
@@ -823,6 +1276,47 @@ namespace System.Windows.Forms {
                [DllImport ("libX11.so", EntryPoint="XUngrabPointer")]
                internal extern static int XUngrabPointer(IntPtr display, uint timestamp);
 
+               [DllImport ("libX11.so", EntryPoint="XQueryPointer")]
+               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);
+
+               [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="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="XClearWindow")]
+               internal extern static int XClearWindow(IntPtr display, IntPtr window);
+
+               [DllImport ("libX11.so", EntryPoint="XClearArea")]
+               internal extern static int XClearArea(IntPtr display, IntPtr window, int x, int y, uint width, uint height, bool exposures);
+
+               // Colormaps
+               [DllImport ("libX11.so", EntryPoint="XDefaultScreenOfDisplay")]
+               internal extern static IntPtr XDefaultScreenOfDisplay(IntPtr display);
+
+               [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="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);
+
+
                // Drawing
                [DllImport ("libX11.so", EntryPoint="XCreateGC")]
                internal extern static IntPtr XCreateGC(IntPtr display, IntPtr window, int valuemask, IntPtr values);