Merge pull request #409 from Alkarex/patch-1
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms.X11Internal / X11Display.cs
index 8ead2c4c8856b1d88ec1b78705c5d708ecb9a7de..43b39dcd4fc9a9d78ccc3fd62494fcc426ea1d42 100644 (file)
@@ -159,7 +159,7 @@ namespace System.Windows.Forms.X11Internal {
 #endif
 
                        Keyboard = new X11Keyboard(display, foster_hwnd.Handle);
-                       Dnd = new X11Dnd (display);
+                       Dnd = new X11Dnd (display, Keyboard);
 
                        ErrorExceptions = false;
 
@@ -555,6 +555,35 @@ namespace System.Windows.Forms.X11Internal {
                        }
                }
 
+               public int SendInput (IntPtr handle, Queue keys) {
+                       if (handle == IntPtr.Zero)
+                               return 0;
+
+                       int count = keys.Count;
+                       Hwnd hwnd = Hwnd.ObjectFromHandle(handle);
+
+                       while (keys.Count > 0) {
+                       
+                               MSG msg = (MSG)keys.Dequeue();
+
+                               XEvent xevent = new XEvent ();
+
+                               xevent.type = (msg.message == Msg.WM_KEYUP ? XEventName.KeyRelease : XEventName.KeyPress);
+                               xevent.KeyEvent.display = display;
+
+                               if (hwnd != null) {
+                                       xevent.KeyEvent.window = hwnd.whole_window;
+                               } else {
+                                       xevent.KeyEvent.window = IntPtr.Zero;
+                               }
+
+                               xevent.KeyEvent.keycode = Keyboard.ToKeycode((int)msg.wParam);
+
+                               hwnd.Queue.EnqueueLocked (xevent);
+                       }
+                       return count;
+               }
+
                // FIXME - I think this should just enqueue directly
                public bool PostMessage (IntPtr handle, Msg message, IntPtr wparam, IntPtr lparam)
                {
@@ -640,7 +669,7 @@ namespace System.Windows.Forms.X11Internal {
                           stupid me, of course. */
                        IntPtr c;
 
-                       Xlib.XGrabServer (display);
+                       //                      Xlib.XGrabServer (display);
 
                        Xlib.XQueryPointer (display, w, out root, out c,
                                            out root_x, out root_y, out child_x, out child_y,
@@ -657,7 +686,7 @@ namespace System.Windows.Forms.X11Internal {
                                                    out mask);
                        }
 
-                       Xlib.XUngrabServer (display);
+                       //                      Xlib.XUngrabServer (display);
 
                        child = child_last;
                }
@@ -824,7 +853,64 @@ namespace System.Windows.Forms.X11Internal {
 
                        return cursor;
                }
+               
+               public Bitmap DefineStdCursorBitmap (StdCursor id)
+               {
+                       CursorFontShape shape;
+                       string name;
+                       IntPtr theme;
+                       int size;
+                       Bitmap bmp = null;
+
+                       try {
+                               shape = XplatUIX11.StdCursorToFontShape (id);
+                               name = shape.ToString ().Replace ("XC_", string.Empty);
+                               size = XplatUIX11.XcursorGetDefaultSize (Handle);
+                               theme = XplatUIX11.XcursorGetTheme (Handle);
+                               IntPtr images_ptr = XplatUIX11.XcursorLibraryLoadImages (name, theme, size);
+#if debug
+                               Console.WriteLine ("DefineStdCursorBitmap, id={0}, #id={1}, name{2}, size={3}, theme: {4}, images_ptr={5}", id, (int) id, name, size, Marshal.PtrToStringAnsi (theme), images_ptr);
+#endif
+
+                               if (images_ptr == IntPtr.Zero) {
+                                       return null;
+                               }
 
+                               XcursorImages images = (XcursorImages)Marshal.PtrToStructure (images_ptr, typeof (XcursorImages));
+#if debug
+                               Console.WriteLine ("DefineStdCursorBitmap, cursor has {0} images", images.nimage);
+#endif
+
+                               if (images.nimage > 0) {
+                                       // We only care about the first image.
+                                       XcursorImage image = (XcursorImage)Marshal.PtrToStructure (Marshal.ReadIntPtr (images.images), typeof (XcursorImage));
+
+#if debug
+                                       Console.WriteLine ("DefineStdCursorBitmap, loaded image <size={0}, height={1}, width={2}, xhot={3}, yhot={4}, pixels={5}", image.size, image.height, image.width, image.xhot, image.yhot, image.pixels);
+#endif
+                                       // A sanity check
+                                       if (image.width <= short.MaxValue && image.height <= short.MaxValue) {
+                                               int [] pixels = new int [image.width * image.height];
+                                               Marshal.Copy (image.pixels, pixels, 0, pixels.Length);
+                                               bmp = new Bitmap (image.width, image.height);
+                                               for (int w = 0; w < image.width; w++) {
+                                                       for (int h = 0; h < image.height; h++) {
+                                                               bmp.SetPixel (w, h, Color.FromArgb (pixels [h * image.width + w]));
+                                                       }
+                                               }
+                                       }
+                               }
+
+                               XplatUIX11.XcursorImagesDestroy (images_ptr);
+
+                       } catch (DllNotFoundException ex) {
+                               Console.WriteLine ("Could not load libXcursor: " + ex.Message + " (" + ex.GetType ().Name + ")");
+                               return null;
+                       }
+
+                       return bmp;
+               }
+               
                public IntPtr DefineStdCursor (StdCursor id)
                {
                        CursorFontShape shape;
@@ -1070,6 +1156,8 @@ namespace System.Windows.Forms.X11Internal {
                {
                        CleanupCachedWindows (hwnd);
 
+                       hwnd.SendParentNotify (Msg.WM_DESTROY, int.MaxValue, int.MaxValue);
+
                        ArrayList windows = new ArrayList ();
 
                        AccumulateDestroyedHandles (Control.ControlNativeWindow.ControlFromHandle(hwnd.Handle), windows);
@@ -1121,7 +1209,7 @@ namespace System.Windows.Forms.X11Internal {
                                // of ours but not the modal one, switch back to the modal window
 
                                if (ActiveWindow != null &&
-                                   NativeWindow.FindWindow (ActiveWindow.Handle) != null) {
+                                   NativeWindow.FromHandle (ActiveWindow.Handle) != null) {
                                        if (ActiveWindow != (X11Hwnd)ModalWindows.Peek())
                                                ((X11Hwnd)ModalWindows.Peek()).Activate ();
                                }
@@ -1409,7 +1497,7 @@ namespace System.Windows.Forms.X11Internal {
                }
 
 
-               public PaintEventArgs PaintEventStart (IntPtr handle, bool client)
+               public PaintEventArgs PaintEventStart (ref Message m, IntPtr handle, bool client)
                {
                        X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
 
@@ -1418,14 +1506,14 @@ namespace System.Windows.Forms.X11Internal {
                                HideCaret();
                        }
 
-                       return hwnd.PaintEventStart (client);
+                       return hwnd.PaintEventStart (ref m, client);
                }
 
-               public void PaintEventEnd (IntPtr handle, bool client)
+               public void PaintEventEnd (ref Message m, IntPtr handle, bool client)
                {
                        X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
 
-                       hwnd.PaintEventEnd (client);
+                       hwnd.PaintEventEnd (ref m, client);
 
                        if (Caret.Visible == true) {
                                ShowCaret();
@@ -1597,7 +1685,19 @@ namespace System.Windows.Forms.X11Internal {
 
                public IntPtr OverrideCursor {
                        get { return OverrideCursorHandle; }
-                       set { OverrideCursorHandle = value; }
+                       set {
+                               if (Grab.Hwnd != IntPtr.Zero) {
+                                       Xlib.XChangeActivePointerGrab (display,
+                                                                      EventMask.ButtonMotionMask |
+                                                                      EventMask.PointerMotionMask |
+                                                                      EventMask.ButtonPressMask |
+                                                                      EventMask.ButtonReleaseMask,
+                                                                      value, IntPtr.Zero);
+                                       return;
+                               }
+
+                               OverrideCursorHandle = value;
+                       }
                }
 
                public X11RootHwnd RootWindow {
@@ -1670,6 +1770,38 @@ namespace System.Windows.Forms.X11Internal {
                        get { return HoverState.Interval; }
                }
 
+               public Rectangle VirtualScreen {
+                       get {
+                               IntPtr actual_atom;
+                               int actual_format;
+                               IntPtr nitems;
+                               IntPtr bytes_after;
+                               IntPtr prop = IntPtr.Zero;
+                               int width;
+                               int height;
+
+                               Xlib.XGetWindowProperty (display, RootWindow.Handle,
+                                                        Atoms._NET_DESKTOP_GEOMETRY, IntPtr.Zero, new IntPtr (256), false, Atoms.XA_CARDINAL,
+                                                        out actual_atom, out actual_format, out nitems, out bytes_after, ref prop);
+
+                               if ((long)nitems < 2)
+                                       goto failsafe;
+
+                               width = Marshal.ReadIntPtr(prop, 0).ToInt32();
+                               height = Marshal.ReadIntPtr(prop, IntPtr.Size).ToInt32();
+                               Xlib.XFree(prop);
+
+                               return new Rectangle(0, 0, width, height);
+
+                       failsafe:
+                               XWindowAttributes attributes = new XWindowAttributes();
+
+                               Xlib.XGetWindowAttributes (display, RootWindow.Handle, ref attributes);
+
+                               return new Rectangle(0, 0, attributes.width, attributes.height);
+                       }
+               }
+
                public Rectangle WorkingArea {
                        get {
                                IntPtr actual_atom;
@@ -1780,602 +1912,605 @@ namespace System.Windows.Forms.X11Internal {
                        X11ThreadQueue queue = (X11ThreadQueue)queue_id;
                        XEvent xevent;
                        bool client;
-                       bool got_xevent;
+                       bool got_xevent = false;
+
                        X11Hwnd hwnd;
 
+               ProcessNextMessage:
                        do {
-                       ProcessNextMessage:
                                got_xevent = queue.Dequeue (out xevent);
 
-                               if (got_xevent) {
-                                       hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (xevent.AnyEvent.window);
-
-                                       // Handle messages for windows that are already or are about to be destroyed.
-
-                                       // we need a special block for this because unless we remove the hwnd from the paint
-                                       // queue it will always stay there (since we don't handle the expose), and we'll
-                                       // effectively loop infinitely trying to repaint a non-existant window.
-                                       if (hwnd != null && hwnd.zombie && xevent.type == XEventName.Expose) {
-                                               hwnd.PendingExpose = hwnd.PendingNCExpose = false;
-                                               goto ProcessNextMessage;
-                                       }
+                               if (!got_xevent) {
+#if spew
+                                       Console.WriteLine (">");
+                                       Console.Out.Flush ();
+#endif
+                                       break;
+                               }
 
-                                       // We need to make sure we only allow DestroyNotify events through for zombie
-                                       // hwnds, since much of the event handling code makes requests using the hwnd's
-                                       // ClientWindow, and that'll result in BadWindow errors if there's some lag
-                                       // between the XDestroyWindow call and the DestroyNotify event.
-                                       if (hwnd == null || hwnd.zombie) {
-                                               #if DriverDebug || DriverDebugDestroy
-                                               Console.WriteLine("GetMessage(): Got message {0} for non-existent or already destroyed window {1:X}",
-                                                                 xevent.type, xevent.AnyEvent.window.ToInt32());
-                                               #endif
-                                               goto ProcessNextMessage;
-                                       }
+#if spew
+                               Console.Write ("-");
+                               Console.Out.Flush ();
+#endif
 
-                                       client = hwnd.ClientWindow == xevent.AnyEvent.window;
+                               hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (xevent.AnyEvent.window);
 
-                                       msg.hwnd = hwnd.Handle;
+                               // Handle messages for windows that are already or are about to be destroyed.
 
-                                       switch (xevent.type) {
-                                       case XEventName.KeyPress:
-                                               Keyboard.KeyEvent (FocusWindow.Handle, xevent, ref msg);
-                                               return true;
+                               // we need a special block for this because unless we remove the hwnd from the paint
+                               // queue it will always stay there (since we don't handle the expose), and we'll
+                               // effectively loop infinitely trying to repaint a non-existant window.
+                               if (hwnd != null && hwnd.zombie && xevent.type == XEventName.Expose) {
+                                       hwnd.PendingExpose = hwnd.PendingNCExpose = false;
+                                       goto ProcessNextMessage;
+                               }
 
-                                       case XEventName.KeyRelease:
-                                               Keyboard.KeyEvent (FocusWindow.Handle, xevent, ref msg);
-                                               return true;
+                               // We need to make sure we only allow DestroyNotify events through for zombie
+                               // hwnds, since much of the event handling code makes requests using the hwnd's
+                               // ClientWindow, and that'll result in BadWindow errors if there's some lag
+                               // between the XDestroyWindow call and the DestroyNotify event.
+                               if (hwnd == null || hwnd.zombie) {
+#if DriverDebug || DriverDebugDestroy
+                                       Console.WriteLine("GetMessage(): Got message {0} for non-existent or already destroyed window {1:X}",
+                                                         xevent.type, xevent.AnyEvent.window.ToInt32());
+#endif
+                                       goto ProcessNextMessage;
+                               }
 
-                                       case XEventName.ButtonPress: {
-                                               switch(xevent.ButtonEvent.button) {
-                                               case 1:
-                                                       MouseState |= MouseButtons.Left;
-                                                       if (client) {
-                                                               msg.message = Msg.WM_LBUTTONDOWN;
-                                                       } else {
-                                                               msg.message = Msg.WM_NCLBUTTONDOWN;
-                                                               hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
-                                                       }
-                                                       // TODO: For WM_NCLBUTTONDOWN wParam specifies a hit-test value not the virtual keys down
-                                                       msg.wParam=GetMousewParam(0);
-                                                       break;
+                               client = hwnd.ClientWindow == xevent.AnyEvent.window;
 
-                                               case 2:
-                                                       MouseState |= MouseButtons.Middle;
-                                                       if (client) {
-                                                               msg.message = Msg.WM_MBUTTONDOWN;
-                                                       } else {
-                                                               msg.message = Msg.WM_NCMBUTTONDOWN;
-                                                               hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
-                                                       }
-                                                       msg.wParam=GetMousewParam(0);
-                                                       break;
+                               msg.hwnd = hwnd.Handle;
 
-                                               case 3:
-                                                       MouseState |= MouseButtons.Right;
-                                                       if (client) {
-                                                               msg.message = Msg.WM_RBUTTONDOWN;
-                                                       } else {
-                                                               msg.message = Msg.WM_NCRBUTTONDOWN;
-                                                               hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
-                                                       }
-                                                       msg.wParam=GetMousewParam(0);
-                                                       break;
+                               switch (xevent.type) {
+                               case XEventName.KeyPress:
+                                       Keyboard.KeyEvent (FocusWindow.Handle, xevent, ref msg);
+                                       return true;
 
-                                               case 4:
-                                                       msg.hwnd = FocusWindow.Handle;
-                                                       msg.message=Msg.WM_MOUSEWHEEL;
-                                                       msg.wParam=GetMousewParam(120);
-                                                       break;
+                               case XEventName.KeyRelease:
+                                       Keyboard.KeyEvent (FocusWindow.Handle, xevent, ref msg);
+                                       return true;
 
-                                               case 5:
-                                                       msg.hwnd = FocusWindow.Handle;
-                                                       msg.message=Msg.WM_MOUSEWHEEL;
-                                                       msg.wParam=GetMousewParam(-120);
-                                                       break;
+                               case XEventName.ButtonPress: {
+                                       switch(xevent.ButtonEvent.button) {
+                                       case 1:
+                                               MouseState |= MouseButtons.Left;
+                                               if (client) {
+                                                       msg.message = Msg.WM_LBUTTONDOWN;
+                                               } else {
+                                                       msg.message = Msg.WM_NCLBUTTONDOWN;
+                                                       hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
                                                }
+                                               // TODO: For WM_NCLBUTTONDOWN wParam specifies a hit-test value not the virtual keys down
+                                               msg.wParam=GetMousewParam(0);
+                                               break;
 
-                                               msg.lParam=(IntPtr) (xevent.ButtonEvent.y << 16 | xevent.ButtonEvent.x);
-                                               MousePosition.X = xevent.ButtonEvent.x;
-                                               MousePosition.Y = xevent.ButtonEvent.y;
-
-                                               if (!hwnd.Enabled) {
-                                                       RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window,
-                                                                                     ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
+                                       case 2:
+                                               MouseState |= MouseButtons.Middle;
+                                               if (client) {
+                                                       msg.message = Msg.WM_MBUTTONDOWN;
+                                               } else {
+                                                       msg.message = Msg.WM_NCMBUTTONDOWN;
+                                                       hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
                                                }
+                                               msg.wParam=GetMousewParam(0);
+                                               break;
 
-                                               if (Grab.Hwnd != IntPtr.Zero)
-                                                       msg.hwnd = Grab.Hwnd;
+                                       case 3:
+                                               MouseState |= MouseButtons.Right;
+                                               if (client) {
+                                                       msg.message = Msg.WM_RBUTTONDOWN;
+                                               } else {
+                                                       msg.message = Msg.WM_NCRBUTTONDOWN;
+                                                       hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
+                                               }
+                                               msg.wParam=GetMousewParam(0);
+                                               break;
 
-                                               if (ClickPending.Pending &&
-                                                   ((((long)xevent.ButtonEvent.time - ClickPending.Time) < DoubleClickInterval) &&
-                                                    (msg.wParam == ClickPending.wParam) &&
-                                                    (msg.lParam == ClickPending.lParam) &&
-                                                    (msg.message == ClickPending.Message))) {
-                                                       // Looks like a genuine double click, clicked twice on the same spot with the same keys
-                                                       switch(xevent.ButtonEvent.button) {
-                                                       case 1:
-                                                               msg.message = client ? Msg.WM_LBUTTONDBLCLK : Msg.WM_NCLBUTTONDBLCLK;
-                                                               break;
-
-                                                       case 2:
-                                                               msg.message = client ? Msg.WM_MBUTTONDBLCLK : Msg.WM_NCMBUTTONDBLCLK;
-                                                               break;
-
-                                                       case 3:
-                                                               msg.message = client ? Msg.WM_RBUTTONDBLCLK : Msg.WM_NCRBUTTONDBLCLK;
-                                                               break;
-                                                       }
+                                       case 4:
+                                               msg.hwnd = FocusWindow.Handle;
+                                               msg.message=Msg.WM_MOUSEWHEEL;
+                                               msg.wParam=GetMousewParam(120);
+                                               break;
 
-                                                       ClickPending.Pending = false;
+                                       case 5:
+                                               msg.hwnd = FocusWindow.Handle;
+                                               msg.message=Msg.WM_MOUSEWHEEL;
+                                               msg.wParam=GetMousewParam(-120);
+                                               break;
+                                       }
 
-                                               }
-                                               else {
-                                                       ClickPending.Pending = true;
-                                                       ClickPending.Hwnd = msg.hwnd;
-                                                       ClickPending.Message = msg.message;
-                                                       ClickPending.wParam = msg.wParam;
-                                                       ClickPending.lParam = msg.lParam;
-                                                       ClickPending.Time = (long)xevent.ButtonEvent.time;
-                                               }
+                                       msg.lParam=(IntPtr) (xevent.ButtonEvent.y << 16 | xevent.ButtonEvent.x);
+                                       MousePosition.X = xevent.ButtonEvent.x;
+                                       MousePosition.Y = xevent.ButtonEvent.y;
 
-                                               return true;
+                                       if (!hwnd.Enabled) {
+                                               RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window,
+                                                                             ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
                                        }
 
-                                       case XEventName.ButtonRelease:
-                                               if (Dnd.InDrag()) {
-                                                       Dnd.HandleButtonRelease (ref xevent);
-                                                       return true;
-                                               }
+                                       if (Grab.Hwnd != IntPtr.Zero)
+                                               msg.hwnd = Grab.Hwnd;
 
+                                       if (ClickPending.Pending &&
+                                           ((((long)xevent.ButtonEvent.time - ClickPending.Time) < DoubleClickInterval) &&
+                                            (msg.wParam == ClickPending.wParam) &&
+                                            (msg.lParam == ClickPending.lParam) &&
+                                            (msg.message == ClickPending.Message))) {
+                                               // Looks like a genuine double click, clicked twice on the same spot with the same keys
                                                switch(xevent.ButtonEvent.button) {
                                                case 1:
-                                                       if (client) {
-                                                               msg.message = Msg.WM_LBUTTONUP;
-                                                       } else {
-                                                               msg.message = Msg.WM_NCLBUTTONUP;
-                                                               hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
-                                                       }
-                                                       MouseState &= ~MouseButtons.Left;
-                                                       msg.wParam=GetMousewParam(0);
+                                                       msg.message = client ? Msg.WM_LBUTTONDBLCLK : Msg.WM_NCLBUTTONDBLCLK;
                                                        break;
 
                                                case 2:
-                                                       if (client) {
-                                                               msg.message = Msg.WM_MBUTTONUP;
-                                                       } else {
-                                                               msg.message = Msg.WM_NCMBUTTONUP;
-                                                               hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
-                                                       }
-                                                       MouseState &= ~MouseButtons.Middle;
-                                                       msg.wParam=GetMousewParam(0);
+                                                       msg.message = client ? Msg.WM_MBUTTONDBLCLK : Msg.WM_NCMBUTTONDBLCLK;
                                                        break;
 
                                                case 3:
-                                                       if (client) {
-                                                               msg.message = Msg.WM_RBUTTONUP;
-                                                       } else {
-                                                               msg.message = Msg.WM_NCRBUTTONUP;
-                                                               hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
-                                                       }
-                                                       MouseState &= ~MouseButtons.Right;
-                                                       msg.wParam=GetMousewParam(0);
+                                                       msg.message = client ? Msg.WM_RBUTTONDBLCLK : Msg.WM_NCRBUTTONDBLCLK;
                                                        break;
+                                               }
 
-                                               case 4:
-                                                       goto ProcessNextMessage;
+                                               ClickPending.Pending = false;
 
-                                               case 5:
-                                                       goto ProcessNextMessage;
+                                       }
+                                       else {
+                                               ClickPending.Pending = true;
+                                               ClickPending.Hwnd = msg.hwnd;
+                                               ClickPending.Message = msg.message;
+                                               ClickPending.wParam = msg.wParam;
+                                               ClickPending.lParam = msg.lParam;
+                                               ClickPending.Time = (long)xevent.ButtonEvent.time;
+                                       }
+
+                                       if (msg.message == Msg.WM_LBUTTONDOWN || msg.message == Msg.WM_MBUTTONDOWN || msg.message == Msg.WM_RBUTTONDOWN) {
+                                               hwnd.SendParentNotify (msg.message, MousePosition.X, MousePosition.Y);
+
+                                               // Win32 splurts MouseMove events all over the place, regardless of whether the mouse is actually moving or
+                                               // not, especially after mousedown and mouseup. To support apps relying on mousemove events between and after 
+                                               // mouse clicks to repaint or whatever, we generate a mousemove event here. *sigh*
+                                               XEvent motionEvent = new XEvent ();
+                                               motionEvent.type = XEventName.MotionNotify;
+                                               motionEvent.MotionEvent.display = display;
+                                               motionEvent.MotionEvent.window = xevent.ButtonEvent.window;
+                                               motionEvent.MotionEvent.x = xevent.ButtonEvent.x;
+                                               motionEvent.MotionEvent.y = xevent.ButtonEvent.y;
+                                               hwnd.Queue.Enqueue (motionEvent);
+                                       }
+
+                                       return true;
+                               }
+
+                               case XEventName.ButtonRelease:
+                                       switch(xevent.ButtonEvent.button) {
+                                       case 1:
+                                               if (client) {
+                                                       msg.message = Msg.WM_LBUTTONUP;
+                                               } else {
+                                                       msg.message = Msg.WM_NCLBUTTONUP;
+                                                       hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
                                                }
+                                               MouseState &= ~MouseButtons.Left;
+                                               msg.wParam=GetMousewParam(0);
+                                               break;
 
-                                               if (!hwnd.Enabled) {
-                                                       RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window,
-                                                                                     ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
+                                       case 2:
+                                               if (client) {
+                                                       msg.message = Msg.WM_MBUTTONUP;
+                                               } else {
+                                                       msg.message = Msg.WM_NCMBUTTONUP;
+                                                       hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
                                                }
+                                               MouseState &= ~MouseButtons.Middle;
+                                               msg.wParam=GetMousewParam(0);
+                                               break;
 
-                                               if (Grab.Hwnd != IntPtr.Zero)
-                                                       msg.hwnd = Grab.Hwnd;
+                                       case 3:
+                                               if (client) {
+                                                       msg.message = Msg.WM_RBUTTONUP;
+                                               } else {
+                                                       msg.message = Msg.WM_NCRBUTTONUP;
+                                                       hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
+                                               }
+                                               MouseState &= ~MouseButtons.Right;
+                                               msg.wParam=GetMousewParam(0);
+                                               break;
 
-                                               msg.lParam=(IntPtr) (xevent.ButtonEvent.y << 16 | xevent.ButtonEvent.x);
-                                               MousePosition.X = xevent.ButtonEvent.x;
-                                               MousePosition.Y = xevent.ButtonEvent.y;
-                                               return true;
+                                       case 4:
+                                               goto ProcessNextMessage;
 
-                                       case XEventName.MotionNotify:
-                                               /* XXX move the compression stuff here */
+                                       case 5:
+                                               goto ProcessNextMessage;
+                                       }
 
-                                               if (client) {
-                                                       #if DriverDebugExtra
-                                                       Console.WriteLine("GetMessage(): Window {0:X} MotionNotify x={1} y={2}",
-                                                                         client ? hwnd.ClientWindow.ToInt32() : hwnd.WholeWindow.ToInt32(),
-                                                                         xevent.MotionEvent.x, xevent.MotionEvent.y);
-                                                       #endif
-
-                                                       if (Dnd.HandleMotionNotify (ref xevent))
-                                                               goto ProcessNextMessage;
-
-                                                       if (Grab.Hwnd != IntPtr.Zero)
-                                                               msg.hwnd = Grab.Hwnd;
-                                                       else
-                                                               NativeWindow.WndProc(msg.hwnd, Msg.WM_SETCURSOR, msg.hwnd, (IntPtr)HitTest.HTCLIENT);
-
-                                                       msg.message = Msg.WM_MOUSEMOVE;
-                                                       msg.wParam = GetMousewParam(0);
-                                                       msg.lParam = (IntPtr) (xevent.MotionEvent.y << 16 | xevent.MotionEvent.x & 0xFFFF);
-
-                                                       if (!hwnd.Enabled) {
-                                                               RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window,
-                                                                                             ref xevent.MotionEvent.x, ref xevent.MotionEvent.y);
-                                                       }
+                                       if (!hwnd.Enabled) {
+                                               RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window,
+                                                                             ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
+                                       }
 
-                                                       MousePosition.X = xevent.MotionEvent.x;
-                                                       MousePosition.Y = xevent.MotionEvent.y;
+                                       if (Grab.Hwnd != IntPtr.Zero)
+                                               msg.hwnd = Grab.Hwnd;
+
+                                       msg.lParam=(IntPtr) (xevent.ButtonEvent.y << 16 | xevent.ButtonEvent.x);
+                                       MousePosition.X = xevent.ButtonEvent.x;
+                                       MousePosition.Y = xevent.ButtonEvent.y;
+
+                                               // Win32 splurts MouseMove events all over the place, regardless of whether the mouse is actually moving or
+                                               // not, especially after mousedown and mouseup. To support apps relying on mousemove events between and after 
+                                               // mouse clicks to repaint or whatever, we generate a mousemove event here. *sigh*
+                                       if (msg.message == Msg.WM_LBUTTONUP || msg.message == Msg.WM_MBUTTONUP || msg.message == Msg.WM_RBUTTONUP) {
+                                               XEvent motionEvent = new XEvent ();
+                                               motionEvent.type = XEventName.MotionNotify;
+                                               motionEvent.MotionEvent.display = display;
+                                               motionEvent.MotionEvent.window = xevent.ButtonEvent.window;
+                                               motionEvent.MotionEvent.x = xevent.ButtonEvent.x;
+                                               motionEvent.MotionEvent.y = xevent.ButtonEvent.y;
+                                               hwnd.Queue.Enqueue (motionEvent);
+                                       }
+                                       return true;
 
-                                                       if ((HoverState.Timer.Enabled) &&
-                                                           (((MousePosition.X + HoverState.Size.Width) < HoverState.X) ||
-                                                            ((MousePosition.X - HoverState.Size.Width) > HoverState.X) ||
-                                                            ((MousePosition.Y + HoverState.Size.Height) < HoverState.Y) ||
-                                                            ((MousePosition.Y - HoverState.Size.Height) > HoverState.Y))) {
+                               case XEventName.MotionNotify:
+                                       /* XXX move the compression stuff here */
 
-                                                               HoverState.Timer.Stop();
-                                                               HoverState.Timer.Start();
-                                                               HoverState.X = MousePosition.X;
-                                                               HoverState.Y = MousePosition.Y;
-                                                       }
-                                               }
-                                               else {
-                                                       HitTest ht;
-                                                       IntPtr dummy;
-                                                       int screen_x;
-                                                       int screen_y;
-
-                                                       #if DriverDebugExtra
-                                                       Console.WriteLine("GetMessage(): non-client area {0:X} MotionNotify x={1} y={2}",
-                                                                         client ? hwnd.ClientWindow.ToInt32() : hwnd.WholeWindow.ToInt32(),
-                                                                         xevent.MotionEvent.x, xevent.MotionEvent.y);
-                                                       #endif
-                                                       msg.message = Msg.WM_NCMOUSEMOVE;
-
-                                                       if (!hwnd.Enabled) {
-                                                               RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window,
-                                                                                             ref xevent.MotionEvent.x, ref xevent.MotionEvent.y);
-                                                       }
+                                       if (client) {
+#if DriverDebugExtra
+                                               Console.WriteLine("GetMessage(): Window {0:X} MotionNotify x={1} y={2}",
+                                                                 client ? hwnd.ClientWindow.ToInt32() : hwnd.WholeWindow.ToInt32(),
+                                                                 xevent.MotionEvent.x, xevent.MotionEvent.y);
+#endif
 
-                                                       // The hit test is sent in screen coordinates
-                                                       Xlib.XTranslateCoordinates (display, xevent.AnyEvent.window, RootWindow.Handle,
-                                                                                   xevent.MotionEvent.x, xevent.MotionEvent.y,
-                                                                                   out screen_x, out screen_y, out dummy);
+                                               if (Grab.Hwnd != IntPtr.Zero)
+                                                       msg.hwnd = Grab.Hwnd;
+                                               else
+                                                       NativeWindow.WndProc(msg.hwnd, Msg.WM_SETCURSOR, msg.hwnd, (IntPtr)HitTest.HTCLIENT);
 
-                                                       msg.lParam = (IntPtr) (screen_y << 16 | screen_x & 0xFFFF);
-                                                       ht = (HitTest)NativeWindow.WndProc (hwnd.ClientWindow, Msg.WM_NCHITTEST,
-                                                                                           IntPtr.Zero, msg.lParam).ToInt32 ();
-                                                       NativeWindow.WndProc(hwnd.ClientWindow, Msg.WM_SETCURSOR, msg.hwnd, (IntPtr)ht);
+                                               msg.message = Msg.WM_MOUSEMOVE;
+                                               msg.wParam = GetMousewParam(0);
+                                               msg.lParam = (IntPtr) (xevent.MotionEvent.y << 16 | xevent.MotionEvent.x & 0xFFFF);
 
-                                                       MousePosition.X = xevent.MotionEvent.x;
-                                                       MousePosition.Y = xevent.MotionEvent.y;
+                                               if (!hwnd.Enabled) {
+                                                       RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window,
+                                                                                     ref xevent.MotionEvent.x, ref xevent.MotionEvent.y);
                                                }
 
-                                               return true;
+                                               MousePosition.X = xevent.MotionEvent.x;
+                                               MousePosition.Y = xevent.MotionEvent.y;
 
-                                       case XEventName.EnterNotify:
-                                               if (!hwnd.Enabled)
-                                                       goto ProcessNextMessage;
+                                               if ((HoverState.Timer.Enabled) &&
+                                                   (((MousePosition.X + HoverState.Size.Width) < HoverState.X) ||
+                                                    ((MousePosition.X - HoverState.Size.Width) > HoverState.X) ||
+                                                    ((MousePosition.Y + HoverState.Size.Height) < HoverState.Y) ||
+                                                    ((MousePosition.Y - HoverState.Size.Height) > HoverState.Y))) {
 
-                                               if (xevent.CrossingEvent.mode != NotifyMode.NotifyNormal)
-                                                       goto ProcessNextMessage;
+                                                       HoverState.Timer.Stop();
+                                                       HoverState.Timer.Start();
+                                                       HoverState.X = MousePosition.X;
+                                                       HoverState.Y = MousePosition.Y;
+                                               }
+                                       }
+                                       else {
+                                               HitTest ht;
+                                               IntPtr dummy;
+                                               int screen_x;
+                                               int screen_y;
+
+                                               #if DriverDebugExtra
+                                               Console.WriteLine("GetMessage(): non-client area {0:X} MotionNotify x={1} y={2}",
+                                                                 client ? hwnd.ClientWindow.ToInt32() : hwnd.WholeWindow.ToInt32(),
+                                                                 xevent.MotionEvent.x, xevent.MotionEvent.y);
+                                               #endif
+                                               msg.message = Msg.WM_NCMOUSEMOVE;
 
-                                               msg.message = Msg.WM_MOUSE_ENTER;
-                                               HoverState.X = xevent.CrossingEvent.x;
-                                               HoverState.Y = xevent.CrossingEvent.y;
-                                               HoverState.Timer.Enabled = true;
-                                               HoverState.Window = xevent.CrossingEvent.window;
+                                               if (!hwnd.Enabled) {
+                                                       RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window,
+                                                                                     ref xevent.MotionEvent.x, ref xevent.MotionEvent.y);
+                                               }
 
-                                               return true;
+                                               // The hit test is sent in screen coordinates
+                                               Xlib.XTranslateCoordinates (display, xevent.AnyEvent.window, RootWindow.Handle,
+                                                                           xevent.MotionEvent.x, xevent.MotionEvent.y,
+                                                                           out screen_x, out screen_y, out dummy);
 
-                                       case XEventName.LeaveNotify:
-                                               if (!hwnd.Enabled)
-                                                       goto ProcessNextMessage;
+                                               msg.lParam = (IntPtr) (screen_y << 16 | screen_x & 0xFFFF);
+                                               ht = (HitTest)NativeWindow.WndProc (hwnd.ClientWindow, Msg.WM_NCHITTEST,
+                                                                                   IntPtr.Zero, msg.lParam).ToInt32 ();
+                                               NativeWindow.WndProc(hwnd.ClientWindow, Msg.WM_SETCURSOR, msg.hwnd, (IntPtr)ht);
 
-                                               if ((xevent.CrossingEvent.mode != NotifyMode.NotifyNormal) ||
-                                                   (xevent.CrossingEvent.window != hwnd.ClientWindow))
-                                                       goto ProcessNextMessage;
+                                               MousePosition.X = xevent.MotionEvent.x;
+                                               MousePosition.Y = xevent.MotionEvent.y;
+                                       }
 
-                                               msg.message=Msg.WM_MOUSE_LEAVE;
-                                               HoverState.Timer.Enabled = false;
-                                               HoverState.Window = IntPtr.Zero;
+                                       return true;
 
-                                               return true;
+                               case XEventName.EnterNotify:
+                                       if (!hwnd.Enabled)
+                                               goto ProcessNextMessage;
 
-                                       case XEventName.ReparentNotify:
-                                               if (hwnd.parent == null) {      // Toplevel
-                                                       if ((xevent.ReparentEvent.parent != IntPtr.Zero) && (xevent.ReparentEvent.window == hwnd.WholeWindow)) {
-                                                               // We need to adjust x/y
-                                                               // This sucks ass, part 2
-                                                               // Every WM does the reparenting of toplevel windows different, so there's
-                                                               // no standard way of getting our adjustment considering frames/decorations
-                                                               // The code below is needed for metacity. KDE doesn't works just fine without this
-                                                               int     dummy_int;
-                                                               IntPtr  dummy_ptr;
-                                                               int     new_x;
-                                                               int     new_y;
-                                                               int     frame_left;
-                                                               int     frame_top;
-
-                                                               hwnd.Reparented = true;
-
-                                                               Xlib.XGetGeometry(display, XGetParent(hwnd.WholeWindow),
-                                                                                 out dummy_ptr, out new_x, out new_y,
-                                                                                 out dummy_int, out dummy_int, out dummy_int, out dummy_int);
-                                                               hwnd.FrameExtents(out frame_left, out frame_top);
-                                                               if ((frame_left != 0) && (frame_top != 0) && (new_x != frame_left) && (new_y != frame_top)) {
-                                                                       hwnd.x = new_x;
-                                                                       hwnd.y = new_y;
-                                                                       hwnd.whacky_wm = true;
-                                                               }
+                                       if (xevent.CrossingEvent.mode != NotifyMode.NotifyNormal)
+                                               goto ProcessNextMessage;
 
-                                                               if (hwnd.opacity != 0xffffffff) {
-                                                                       IntPtr opacity;
+                                       msg.message = Msg.WM_MOUSE_ENTER;
+                                       HoverState.X = xevent.CrossingEvent.x;
+                                       HoverState.Y = xevent.CrossingEvent.y;
+                                       HoverState.Timer.Enabled = true;
+                                       HoverState.Window = xevent.CrossingEvent.window;
 
-                                                                       opacity = (IntPtr)(Int32)hwnd.opacity;
-                                                                       Xlib.XChangeProperty (display, XGetParent(hwnd.WholeWindow),
-                                                                                             Atoms._NET_WM_WINDOW_OPACITY, Atoms.XA_CARDINAL, 32,
-                                                                                             PropertyMode.Replace, ref opacity, 1);
-                                                               }
-                                                               SendMessage(msg.hwnd, Msg.WM_WINDOWPOSCHANGED, msg.wParam, msg.lParam);
-                                                               goto ProcessNextMessage;
-                                                       } else {
-                                                               hwnd.Reparented = false;
-                                                               goto ProcessNextMessage;
-                                                       }
-                                               }
+                                       return true;
+
+                               case XEventName.LeaveNotify:
+                                       if (!hwnd.Enabled)
                                                goto ProcessNextMessage;
 
-                                       case XEventName.ConfigureNotify:
-                                               hwnd.AddConfigureNotify (xevent);
+                                       if ((xevent.CrossingEvent.mode != NotifyMode.NotifyNormal) ||
+                                           (xevent.CrossingEvent.window != hwnd.ClientWindow))
                                                goto ProcessNextMessage;
 
-                                       case XEventName.FocusIn:
-                                               // We received focus. We use X11 focus only to know if the app window does or does not have focus
-                                               // We do not track the actual focussed window via it. Instead, this is done via FocusWindow internally
-                                               // Receiving focus means we've gotten activated and therefore we need to let the actual FocusWindow know 
-                                               // about it having focus again
-                                               if (xevent.FocusChangeEvent.detail != NotifyDetail.NotifyNonlinear)
-                                                       goto ProcessNextMessage;
+                                       msg.message=Msg.WM_MOUSELEAVE;
+                                       HoverState.Timer.Enabled = false;
+                                       HoverState.Window = IntPtr.Zero;
+
+                                       return true;
+
+                               case XEventName.ReparentNotify:
+                                       if (hwnd.parent == null) {      // Toplevel
+                                               if ((xevent.ReparentEvent.parent != IntPtr.Zero) && (xevent.ReparentEvent.window == hwnd.WholeWindow)) {
+                                                       // We need to adjust x/y
+                                                       // This sucks ass, part 2
+                                                       // Every WM does the reparenting of toplevel windows different, so there's
+                                                       // no standard way of getting our adjustment considering frames/decorations
+                                                       // The code below is needed for metacity. KDE doesn't works just fine without this
+                                                       int     dummy_int;
+                                                       IntPtr  dummy_ptr;
+                                                       int     new_x;
+                                                       int     new_y;
+                                                       int     frame_left;
+                                                       int     frame_top;
+
+                                                       hwnd.Reparented = true;
+
+                                                       Xlib.XGetGeometry(display, XGetParent(hwnd.WholeWindow),
+                                                                         out dummy_ptr, out new_x, out new_y,
+                                                                         out dummy_int, out dummy_int, out dummy_int, out dummy_int);
+                                                       hwnd.FrameExtents(out frame_left, out frame_top);
+                                                       if ((frame_left != 0) && (frame_top != 0) && (new_x != frame_left) && (new_y != frame_top)) {
+                                                               hwnd.x = new_x;
+                                                               hwnd.y = new_y;
+                                                               hwnd.whacky_wm = true;
+                                                       }
 
-                                               if (FocusWindow == null) {
-                                                       Control c = Control.FromHandle (hwnd.ClientWindow);
-                                                       if (c == null)
-                                                               goto ProcessNextMessage;
-                                                       Form form = c.FindForm ();
-                                                       if (form == null)
-                                                               goto ProcessNextMessage;
-                                                       ActiveWindow = (X11Hwnd)Hwnd.ObjectFromHandle (form.Handle);
-                                                       SendMessage (ActiveWindow.Handle, Msg.WM_ACTIVATE, (IntPtr) WindowActiveFlags.WA_ACTIVE, IntPtr.Zero);
+                                                       if (hwnd.opacity != 0xffffffff) {
+                                                               IntPtr opacity;
+
+                                                               opacity = (IntPtr)(Int32)hwnd.opacity;
+                                                               Xlib.XChangeProperty (display, XGetParent(hwnd.WholeWindow),
+                                                                                     Atoms._NET_WM_WINDOW_OPACITY, Atoms.XA_CARDINAL, 32,
+                                                                                     PropertyMode.Replace, ref opacity, 1);
+                                                       }
+                                                       SendMessage(msg.hwnd, Msg.WM_WINDOWPOSCHANGED, msg.wParam, msg.lParam);
+                                                       goto ProcessNextMessage;
+                                               } else {
+                                                       hwnd.Reparented = false;
                                                        goto ProcessNextMessage;
                                                }
-                                               Keyboard.FocusIn(FocusWindow.Handle);
-                                               SendMessage(FocusWindow.Handle, Msg.WM_SETFOCUS, IntPtr.Zero, IntPtr.Zero);
+                                       }
+                                       goto ProcessNextMessage;
+
+                               case XEventName.ConfigureNotify:
+                                       hwnd.HandleConfigureNotify (xevent);
+                                       goto ProcessNextMessage;
+
+                               case XEventName.MapNotify: {
+                                       if (client && (xevent.ConfigureEvent.xevent == xevent.ConfigureEvent.window)) { // Ignore events for children (SubstructureNotify) and client areas
+                                               hwnd.Mapped = true;
+                                               msg.message = Msg.WM_SHOWWINDOW;
+                                               msg.wParam = (IntPtr) 1;
+                                               // XXX we're missing the lParam..
+                                               break;
+                                       }
+                                       goto ProcessNextMessage;
+                               }
+
+                               case XEventName.UnmapNotify: {
+                                       if (client && (xevent.ConfigureEvent.xevent == xevent.ConfigureEvent.window)) { // Ignore events for children (SubstructureNotify) and client areas
+                                               hwnd.Mapped = false;
+                                               msg.message = Msg.WM_SHOWWINDOW;
+                                               msg.wParam = (IntPtr) 0;
+                                               // XXX we're missing the lParam..
+                                               break;
+                                       }
+                                       goto ProcessNextMessage;
+                               }
+
+                               case XEventName.FocusIn:
+                                       // We received focus. We use X11 focus only to know if the app window does or does not have focus
+                                       // We do not track the actual focussed window via it. Instead, this is done via FocusWindow internally
+                                       // Receiving focus means we've gotten activated and therefore we need to let the actual FocusWindow know 
+                                       // about it having focus again
+                                       if (xevent.FocusChangeEvent.detail != NotifyDetail.NotifyNonlinear)
                                                goto ProcessNextMessage;
 
-                                       case XEventName.FocusOut:
-                                               // Se the comment for our FocusIn handler
-                                               if (xevent.FocusChangeEvent.detail != NotifyDetail.NotifyNonlinear)
+                                       if (FocusWindow == null) {
+                                               Control c = Control.FromHandle (hwnd.ClientWindow);
+                                               if (c == null)
                                                        goto ProcessNextMessage;
-
-                                               if (FocusWindow == null)
+                                               Form form = c.FindForm ();
+                                               if (form == null)
                                                        goto ProcessNextMessage;
+                                               X11Hwnd new_active = (X11Hwnd)Hwnd.ObjectFromHandle (form.Handle);
+                                               if (ActiveWindow != new_active) {
+                                                       ActiveWindow = new_active;
+                                                       SendMessage (ActiveWindow.Handle, Msg.WM_ACTIVATE, (IntPtr) WindowActiveFlags.WA_ACTIVE, IntPtr.Zero);
+                                               }
+                                               goto ProcessNextMessage;
+                                       }
+                                       Keyboard.FocusIn(FocusWindow.Handle);
+                                       SendMessage(FocusWindow.Handle, Msg.WM_SETFOCUS, IntPtr.Zero, IntPtr.Zero);
+                                       goto ProcessNextMessage;
 
-                                               Keyboard.FocusOut(FocusWindow.Handle);
-
-                                               while (Keyboard.ResetKeyState(FocusWindow.Handle, ref msg))
-                                                       SendMessage(FocusWindow.Handle, msg.message, msg.wParam, msg.lParam);
-
-                                               SendMessage(FocusWindow.Handle, Msg.WM_KILLFOCUS, IntPtr.Zero, IntPtr.Zero);
+                               case XEventName.FocusOut:
+                                       // Se the comment for our FocusIn handler
+                                       if (xevent.FocusChangeEvent.detail != NotifyDetail.NotifyNonlinear)
                                                goto ProcessNextMessage;
 
-                                       case XEventName.Expose:
-                                               hwnd.AddExpose (client,
-                                                               xevent.ExposeEvent.x, xevent.ExposeEvent.y,
-                                                               xevent.ExposeEvent.width, xevent.ExposeEvent.height);
+                                       if (FocusWindow == null)
                                                goto ProcessNextMessage;
-                                               
-                                       case XEventName.DestroyNotify:
 
-                                               // This is a bit tricky, we don't receive our own DestroyNotify, we only get those for our children
-                                               hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(xevent.DestroyWindowEvent.window);
+                                       Keyboard.FocusOut(FocusWindow.Handle);
 
-                                               // We may get multiple for the same window, act only one the first (when Hwnd still knows about it)
-                                               if ((hwnd != null) && (hwnd.ClientWindow == xevent.DestroyWindowEvent.window)) {
-                                                       CleanupCachedWindows (hwnd);
+                                       while (Keyboard.ResetKeyState(FocusWindow.Handle, ref msg))
+                                               SendMessage(FocusWindow.Handle, msg.message, msg.wParam, msg.lParam);
 
-                                                       #if DriverDebugDestroy
-                                                       Console.WriteLine("Received X11 Destroy Notification for {0}", XplatUI.Window(hwnd.ClientWindow));
-                                                       #endif
+                                       SendMessage(FocusWindow.Handle, Msg.WM_KILLFOCUS, IntPtr.Zero, IntPtr.Zero);
+                                       goto ProcessNextMessage;
 
-                                                       msg.hwnd = hwnd.ClientWindow;
-                                                       msg.message=Msg.WM_DESTROY;
-                                                       hwnd.Dispose();
-                                               }
-                                               else
-                                                       goto ProcessNextMessage;
+                               case XEventName.Expose:
+                                       if (!hwnd.Mapped) {
+                                               hwnd.PendingExpose = hwnd.PendingNCExpose = false;
+                                               continue;
+                                       }
 
-                                               return true;
+                                       msg.hwnd = hwnd.Handle;
 
-                                       case XEventName.ClientMessage:
-                                               if (Dnd.HandleClientMessage (ref xevent))
-                                                       goto ProcessNextMessage;
+                                       if (client) {
+#if DriverDebugExtra
+                                               Console.WriteLine("GetMessage(): Window {0:X} Exposed area {1},{2} {3}x{4}",
+                                                                 hwnd.client_window.ToInt32(),
+                                                                 xevent.ExposeEvent.x, xevent.ExposeEvent.y,
+                                                                 xevent.ExposeEvent.width, xevent.ExposeEvent.height);
+#endif
+                                               msg.message = Msg.WM_PAINT;
+                                       }
+                                       else {
+                                               Graphics g;
+
+                                               switch (hwnd.border_style) {
+                                               case FormBorderStyle.Fixed3D:
+                                                       g = Graphics.FromHwnd(hwnd.WholeWindow);
+                                                       ControlPaint.DrawBorder3D(g, new Rectangle(0, 0, hwnd.Width, hwnd.Height),
+                                                                                 Border3DStyle.Sunken);
+                                                       g.Dispose();
+                                                       break;
 
-                                               if (xevent.ClientMessageEvent.message_type == Atoms.AsyncAtom) {
-                                                       XplatUIDriverSupport.ExecuteClientMessage((GCHandle)xevent.ClientMessageEvent.ptr1);
-                                                       goto ProcessNextMessage;
+                                               case FormBorderStyle.FixedSingle:
+                                                       g = Graphics.FromHwnd(hwnd.WholeWindow);
+                                                       ControlPaint.DrawBorder(g, new Rectangle(0, 0, hwnd.Width, hwnd.Height),
+                                                                               Color.Black, ButtonBorderStyle.Solid);
+                                                       g.Dispose();
+                                               break;
                                                }
+#if DriverDebugExtra
+                                               Console.WriteLine("GetMessage(): Window {0:X} Exposed non-client area {1},{2} {3}x{4}",
+                                                                 hwnd.ClientWindow.ToInt32(),
+                                                                 xevent.ExposeEvent.x, xevent.ExposeEvent.y,
+                                                                 xevent.ExposeEvent.width, xevent.ExposeEvent.height);
+#endif
 
-                                               if (xevent.ClientMessageEvent.message_type == HoverState.Atom) {
-                                                       msg.message = Msg.WM_MOUSEHOVER;
-                                                       msg.wParam = GetMousewParam(0);
-                                                       msg.lParam = (IntPtr) (xevent.ClientMessageEvent.ptr1);
-                                                       return true;
-                                               }
+                                               Rectangle rect = new Rectangle (xevent.ExposeEvent.x, xevent.ExposeEvent.y,
+                                                                               xevent.ExposeEvent.width, xevent.ExposeEvent.height);
+                                               Region region = new Region (rect);
+                                               IntPtr hrgn = region.GetHrgn (null); // Graphics object isn't needed
+                                               msg.message = Msg.WM_NCPAINT;
+                                               msg.wParam = hrgn == IntPtr.Zero ? (IntPtr)1 : hrgn;
+                                               msg.refobject = region;
+                                       }
 
-                                               if (xevent.ClientMessageEvent.message_type == Atoms.PostAtom) {
-                                                       msg.hwnd = xevent.ClientMessageEvent.ptr1;
-                                                       msg.message = (Msg) xevent.ClientMessageEvent.ptr2.ToInt32 ();
-                                                       msg.wParam = xevent.ClientMessageEvent.ptr3;
-                                                       msg.lParam = xevent.ClientMessageEvent.ptr4;
-                                                       return true;
-                                               }
+                                       return true;
+                                               
+                               case XEventName.DestroyNotify:
 
-                                               if (xevent.ClientMessageEvent.message_type == Atoms._XEMBED) {
-                                                       #if DriverDebugXEmbed
-                                                       Console.WriteLine("GOT EMBED MESSAGE {0:X}, detail {1:X}",
-                                                                         xevent.ClientMessageEvent.ptr2.ToInt32(), xevent.ClientMessageEvent.ptr3.ToInt32());
-                                                       #endif
+                                       // This is a bit tricky, we don't receive our own DestroyNotify, we only get those for our children
+                                       hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(xevent.DestroyWindowEvent.window);
 
-                                                       if (xevent.ClientMessageEvent.ptr2.ToInt32() == (int)XEmbedMessage.EmbeddedNotify) {
-                                                               XSizeHints hints = new XSizeHints();
-                                                               IntPtr dummy;
+                                       // We may get multiple for the same window, act only one the first (when Hwnd still knows about it)
+                                       if ((hwnd != null) && (hwnd.ClientWindow == xevent.DestroyWindowEvent.window)) {
+                                               CleanupCachedWindows (hwnd);
 
-                                                               Xlib.XGetWMNormalHints (display, hwnd.WholeWindow, ref hints, out dummy);
+                                               #if DriverDebugDestroy
+                                               Console.WriteLine("Received X11 Destroy Notification for {0}", XplatUI.Window(hwnd.ClientWindow));
+                                               #endif
 
-                                                               hwnd.width = hints.max_width;
-                                                               hwnd.height = hints.max_height;
-                                                               hwnd.ClientRect = Rectangle.Empty;
-                                                               SendMessage(msg.hwnd, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero);
-                                                       }
-                                               }
+                                               msg.hwnd = hwnd.ClientWindow;
+                                               msg.message=Msg.WM_DESTROY;
+                                               hwnd.Dispose();
+                                       }
+                                       else
+                                               goto ProcessNextMessage;
 
-                                               if (xevent.ClientMessageEvent.message_type == Atoms.WM_PROTOCOLS) {
-                                                       if (xevent.ClientMessageEvent.ptr1 == Atoms.WM_DELETE_WINDOW) {
-                                                               msg.message = Msg.WM_CLOSE;
-                                                               return true;
-                                                       }
+                                       return true;
 
-                                                       // We should not get this, but I'll leave the code in case we need it in the future
-                                                       if (xevent.ClientMessageEvent.ptr1 == Atoms.WM_TAKE_FOCUS) {
-                                                               goto ProcessNextMessage;
-                                                       }
-                                               }
+                               case XEventName.ClientMessage:
+                                       if (Dnd.HandleClientMessage (ref xevent))
                                                goto ProcessNextMessage;
 
-                                       case XEventName.PropertyNotify:
-                                               // The Hwnd's themselves handle this
-                                               hwnd.PropertyChanged (xevent);
+                                       if (xevent.ClientMessageEvent.message_type == Atoms.AsyncAtom) {
+                                               XplatUIDriverSupport.ExecuteClientMessage((GCHandle)xevent.ClientMessageEvent.ptr1);
                                                goto ProcessNextMessage;
                                        }
-                               }
-
-                       } while (got_xevent);
-
-                       queue.CheckTimers ();
 
-                       while (queue.Configure.Count > 0) {
-                               xevent = queue.Configure.Dequeue ();
-
-                               hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (xevent.AnyEvent.window);
-                               if (hwnd == null || hwnd.zombie)
-                                       continue;
+                                       if (xevent.ClientMessageEvent.message_type == HoverState.Atom) {
+                                               msg.message = Msg.WM_MOUSEHOVER;
+                                               msg.wParam = GetMousewParam(0);
+                                               msg.lParam = (IntPtr) (xevent.ClientMessageEvent.ptr1);
+                                               return true;
+                                       }
 
-                               hwnd.HandleConfigureNotify (xevent);
-                       }
+                                       if (xevent.ClientMessageEvent.message_type == Atoms.PostAtom) {
+                                               msg.hwnd = xevent.ClientMessageEvent.ptr1;
+                                               msg.message = (Msg) xevent.ClientMessageEvent.ptr2.ToInt32 ();
+                                               msg.wParam = xevent.ClientMessageEvent.ptr3;
+                                               msg.lParam = xevent.ClientMessageEvent.ptr4;
 
-                       /* now that we've handled X events, handle any pending paint and configure events */
-                       
-                       while (queue.Paint.Count > 0) {
-                               xevent = queue.Paint.Dequeue ();
+                                               // if we posted a WM_QUIT message, make sure we return
+                                               // false here as well.
+                                               if (msg.message == (Msg)Msg.WM_QUIT)
+                                                       return false;
+                                               else
+                                                       return true;
+                                       }
 
-                               hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (xevent.AnyEvent.window);
-                               if (hwnd == null || hwnd.zombie)
-                                       continue;
+                                       if (xevent.ClientMessageEvent.message_type == Atoms._XEMBED) {
+#if DriverDebugXEmbed
+                                               Console.WriteLine("GOT EMBED MESSAGE {0:X}, detail {1:X}",
+                                                                 xevent.ClientMessageEvent.ptr2.ToInt32(), xevent.ClientMessageEvent.ptr3.ToInt32());
+#endif
 
-                               client = hwnd.ClientWindow == xevent.AnyEvent.window;
+                                               if (xevent.ClientMessageEvent.ptr2.ToInt32() == (int)XEmbedMessage.EmbeddedNotify) {
+                                                       XSizeHints hints = new XSizeHints();
+                                                       IntPtr dummy;
 
-                               if (queue.PostQuitState || !hwnd.Mapped) {
-                                       try {
-                                               hwnd.Queue.Lock ();
-                                               hwnd.PendingExpose = hwnd.PendingNCExpose = false;
-                                               continue;
-                                       }
-                                       finally {
-                                               hwnd.Queue.Unlock ();
-                                       }
-                               }
+                                                       Xlib.XGetWMNormalHints (display, hwnd.WholeWindow, ref hints, out dummy);
 
-                               // XXX these should really be error conditions.  if we
-                               // have an expose event in the queue, there should be
-                               // one pending.
-                               try {
-                                       hwnd.Queue.Lock ();
-                                       if (client && !hwnd.PendingExpose) {
-                                               Console.WriteLine ("client expose but no expose pending");
-                                               continue;
-                                       }
-                                       else if (!client && !hwnd.PendingNCExpose) {
-                                               Console.WriteLine ("non-client expose but no expose pending");
-                                               continue;
+                                                       hwnd.width = hints.max_width;
+                                                       hwnd.height = hints.max_height;
+                                                       hwnd.ClientRect = Rectangle.Empty;
+                                                       SendMessage(msg.hwnd, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero);
+                                               }
                                        }
-                               }
-                               finally {
-                                       hwnd.Queue.Unlock ();
-                               }
 
-                               msg.hwnd = hwnd.Handle;
-
-                               if (client) {
-#if DriverDebugExtra
-                                       Console.WriteLine("GetMessage(): Window {0:X} Exposed area {1},{2} {3}x{4}",
-                                                         hwnd.client_window.ToInt32(),
-                                                         xevent.ExposeEvent.x, xevent.ExposeEvent.y,
-                                                         xevent.ExposeEvent.width, xevent.ExposeEvent.height);
-#endif
-                                       msg.message = Msg.WM_PAINT;
-                               }
-                               else {
-                                       Graphics g;
-
-                                       switch (hwnd.border_style) {
-                                       case FormBorderStyle.Fixed3D:
-                                               g = Graphics.FromHwnd(hwnd.WholeWindow);
-                                               ControlPaint.DrawBorder3D(g, new Rectangle(0, 0, hwnd.Width, hwnd.Height),
-                                                                         Border3DStyle.Sunken);
-                                               g.Dispose();
-                                               break;
+                                       if (xevent.ClientMessageEvent.message_type == Atoms.WM_PROTOCOLS) {
+                                               if (xevent.ClientMessageEvent.ptr1 == Atoms.WM_DELETE_WINDOW) {
+                                                       msg.message = Msg.WM_CLOSE;
+                                                       return true;
+                                               }
 
-                                       case FormBorderStyle.FixedSingle:
-                                               g = Graphics.FromHwnd(hwnd.WholeWindow);
-                                               ControlPaint.DrawBorder(g, new Rectangle(0, 0, hwnd.Width, hwnd.Height),
-                                                                       Color.Black, ButtonBorderStyle.Solid);
-                                               g.Dispose();
-                                               break;
+                                               // We should not get this, but I'll leave the code in case we need it in the future
+                                               if (xevent.ClientMessageEvent.ptr1 == Atoms.WM_TAKE_FOCUS) {
+                                                       goto ProcessNextMessage;
+                                               }
                                        }
-                                       #if DriverDebugExtra
-                                       Console.WriteLine("GetMessage(): Window {0:X} Exposed non-client area {1},{2} {3}x{4}",
-                                                         hwnd.ClientWindow.ToInt32(),
-                                                         xevent.ExposeEvent.x, xevent.ExposeEvent.y,
-                                                         xevent.ExposeEvent.width, xevent.ExposeEvent.height);
-                                       #endif
-
-                                       Rectangle rect = new Rectangle (xevent.ExposeEvent.x, xevent.ExposeEvent.y,
-                                                                               xevent.ExposeEvent.width, xevent.ExposeEvent.height);
-                                       Region region = new Region (rect);
-                                       IntPtr hrgn = region.GetHrgn (null); // Graphics object isn't needed
-                                       msg.message = Msg.WM_NCPAINT;
-                                       msg.wParam = hrgn == IntPtr.Zero ? (IntPtr)1 : hrgn;
-                                       msg.refobject = region;
-                               }
 
-                               return true;
-                       }
+                                       goto ProcessNextMessage;
 
-                       if (!queue.PostQuitState) {
-                               msg.hwnd= IntPtr.Zero;
-                               msg.message = Msg.WM_ENTERIDLE;
-                               return true;
-                       }
-
-                       // We reset ourselves so GetMessage can be called again
-                       queue.PostQuitState = false;
+                               case XEventName.PropertyNotify:
+                                       // The Hwnd's themselves handle this
+                                       hwnd.PropertyChanged (xevent);
+                                       goto ProcessNextMessage;
+                               }
+                       } while (true);
 
-                       return false;
+                       msg.hwnd= IntPtr.Zero;
+                       msg.message = Msg.WM_ENTERIDLE;
+                       return true;
                }
 
                [MonoTODO("Implement filtering and PM_NOREMOVE")]
@@ -2425,5 +2560,151 @@ namespace System.Windows.Forms.X11Internal {
 
                        queue.DispatchIdle = true;
                }
+
+               // double buffering support
+               public void CreateOffscreenDrawable (IntPtr handle,
+                                                    int width, int height,
+                                                    out object offscreen_drawable)
+               {
+                       IntPtr root_out;
+                       int x_out, y_out, width_out, height_out, border_width_out, depth_out;
+
+                       Xlib.XGetGeometry (display, handle,
+                                          out root_out,
+                                          out x_out, out y_out,
+                                          out width_out, out height_out,
+                                          out border_width_out, out depth_out);
+
+                       IntPtr pixmap = Xlib.XCreatePixmap (display, handle, width, height, depth_out);
+
+                       offscreen_drawable = pixmap;
+               }
+
+               public void DestroyOffscreenDrawable (object offscreen_drawable)
+               {
+                       Xlib.XFreePixmap (display, (IntPtr)offscreen_drawable);
+               }
+
+               public Graphics GetOffscreenGraphics (object offscreen_drawable)
+               {
+                       return Graphics.FromHwnd ((IntPtr) offscreen_drawable);
+               }
+
+               public void BlitFromOffscreen (IntPtr dest_handle,
+                                              Graphics dest_dc,
+                                              object offscreen_drawable,
+                                              Graphics offscreen_dc,
+                                              Rectangle r)
+               {
+                       XGCValues gc_values;
+                       IntPtr gc;
+
+                       gc_values = new XGCValues();
+
+                       gc = Xlib.XCreateGC (display, dest_handle, IntPtr.Zero, ref gc_values);
+
+                       Xlib.XCopyArea (display, (IntPtr)offscreen_drawable, dest_handle,
+                                       gc, r.X, r.Y, r.Width, r.Height, r.X, r.Y);
+
+                       Xlib.XFreeGC (display, gc);
+               }
+
+
+               // reversible screen-level drawing
+               IntPtr GetReversibleScreenGC (Color backColor)
+               {
+                       XGCValues       gc_values;
+                       IntPtr          gc;
+                       uint pixel;
+
+                       XColor xcolor = new XColor();
+                       xcolor.red = (ushort)(backColor.R * 257);
+                       xcolor.green = (ushort)(backColor.G * 257);
+                       xcolor.blue = (ushort)(backColor.B * 257);
+                       Xlib.XAllocColor (display, DefaultColormap, ref xcolor);
+                       pixel = (uint)xcolor.pixel.ToInt32();
+
+
+                       gc_values = new XGCValues();
+
+                       gc_values.subwindow_mode = GCSubwindowMode.IncludeInferiors;
+                       gc_values.foreground = (IntPtr)pixel;
+
+                       gc = Xlib.XCreateGC (display, RootWindow.Handle, new IntPtr ((int) (GCFunction.GCSubwindowMode | GCFunction.GCForeground)), ref gc_values);
+                       Xlib.XSetForeground (display, gc, (UIntPtr)pixel);
+                       Xlib.XSetFunction (display,   gc, GXFunction.GXxor);
+
+                       return gc;
+               }
+
+               public void DrawReversibleLine (Point start, Point end, Color backColor)
+               {
+                       if (backColor.GetBrightness() < 0.5)
+                               backColor = Color.FromArgb(255 - backColor.R, 255 - backColor.G, 255 - backColor.B);
+
+                       IntPtr gc = GetReversibleScreenGC (backColor);
+
+                       Xlib.XDrawLine (display, RootWindow.Handle, gc, start.X, start.Y, end.X, end.Y);
+
+                       Xlib.XFreeGC (display, gc);
+               }
+
+               public void FillReversibleRectangle (Rectangle rectangle, Color backColor)
+               {
+                       if (backColor.GetBrightness() < 0.5)
+                               backColor = Color.FromArgb(255 - backColor.R, 255 - backColor.G, 255 - backColor.B);
+
+                       IntPtr gc = GetReversibleScreenGC (backColor);
+
+                       if (rectangle.Width < 0) {
+                               rectangle.X += rectangle.Width;
+                               rectangle.Width = -rectangle.Width;
+                       }
+                       if (rectangle.Height < 0) {
+                               rectangle.Y += rectangle.Height;
+                               rectangle.Height = -rectangle.Height;
+                       }
+
+                       Xlib.XFillRectangle (display, RootWindow.Handle, gc, rectangle.Left, rectangle.Top, rectangle.Width, rectangle.Height);
+
+                       Xlib.XFreeGC (display, gc);
+               }
+
+               public void DrawReversibleFrame (Rectangle rectangle, Color backColor, FrameStyle style)
+               {
+                       if (backColor.GetBrightness() < 0.5)
+                               backColor = Color.FromArgb(255 - backColor.R, 255 - backColor.G, 255 - backColor.B);
+
+                       IntPtr gc = GetReversibleScreenGC (backColor);
+
+                       if (rectangle.Width < 0) {
+                               rectangle.X += rectangle.Width;
+                               rectangle.Width = -rectangle.Width;
+                       }
+                       if (rectangle.Height < 0) {
+                               rectangle.Y += rectangle.Height;
+                               rectangle.Height = -rectangle.Height;
+                       }
+
+                       int line_width = 1;
+                       GCLineStyle line_style = GCLineStyle.LineSolid;
+                       GCCapStyle cap_style = GCCapStyle.CapButt;
+                       GCJoinStyle join_style = GCJoinStyle.JoinMiter;
+
+                       switch (style) {
+                       case FrameStyle.Dashed:
+                               line_style = GCLineStyle.LineOnOffDash;
+                               break;
+                       case FrameStyle.Thick:
+                               line_width = 2;
+                               break;
+                       }
+
+                       Xlib.XSetLineAttributes (display, gc, line_width, line_style, cap_style, join_style);
+
+                       Xlib.XDrawRectangle (display, RootWindow.Handle, gc, rectangle.Left, rectangle.Top, rectangle.Width, rectangle.Height);
+
+                       Xlib.XFreeGC (display, gc);
+               }
        }
 }