more work
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms.X11Internal / X11Display.cs
1 // a copy of this software and associated documentation files (the
2 // "Software"), to deal in the Software without restriction, including
3 // without limitation the rights to use, copy, modify, merge, publish,
4 // distribute, sublicense, and/or sell copies of the Software, and to
5 // permit persons to whom the Software is furnished to do so, subject to
6 // the following conditions:
7 // 
8 // The above copyright notice and this permission notice shall be
9 // included in all copies or substantial portions of the Software.
10 // 
11 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
13 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
14 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
15 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
16 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
17 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18 //
19 // Copyright (c) 2006 Novell, Inc. (http://www.novell.com)
20 //
21 //
22
23 using System;
24 using System.Collections;
25 using System.Diagnostics;
26 using System.Drawing;
27 using System.Drawing.Drawing2D;
28 using System.Drawing.Imaging;
29 using System.IO;
30 using System.Net;
31 using System.Net.Sockets;
32 using System.Reflection;
33 using System.Runtime.InteropServices;
34 using System.Text;
35 using System.Threading;
36 using System.Windows.Forms;
37
38 namespace System.Windows.Forms.X11Internal {
39
40         internal class X11Display {
41
42                 IntPtr display; /* our X handle */
43
44                 // XXX internal because X11Hwnd needs them
45                 internal IntPtr CustomVisual;    // Visual for window creation
46                 internal IntPtr CustomColormap;  // Colormap for window creation
47
48                 X11Keyboard Keyboard;
49                 internal X11Dnd Dnd; // XXX X11Hwnd needs it to enable Dnd
50                 bool detectable_key_auto_repeat;
51
52                 X11Atoms atoms;
53                 X11RootHwnd root_hwnd;
54                 X11Hwnd foster_hwnd;
55
56                 // Clipboard
57                 IntPtr          ClipMagic;
58                 ClipboardStruct Clipboard; // Our clipboard
59
60                 // Focus tracking
61                 internal X11Hwnd ActiveWindow;
62                 X11Hwnd FocusWindow;
63
64                 // Modality support
65                 internal Stack ModalWindows; // Stack of our modal windows
66
67                 // Caret
68                 CaretStruct Caret;
69
70                 // mouse hover message generation
71                 // XXX internal because X11Atoms needs to access it..
72                 internal HoverStruct HoverState;
73
74                 // double click message generation
75                 ClickStruct ClickPending;
76                 int DoubleClickInterval; // msec; max interval between clicks to count as double click
77
78                 // Support for mouse grab
79                 GrabStruct Grab;
80
81                 // State
82                 Point MousePosition;     // Last position of mouse, in screen coords
83                 MouseButtons MouseState; // Last state of mouse buttons
84
85                 XErrorHandler   ErrorHandler;           // Error handler delegate
86                 bool            ErrorExceptions;        // Throw exceptions on X errors
87
88                 Thread event_thread; // the background thread that just watches our X socket
89
90                 public X11Display (IntPtr display)
91                 {
92                         if (display == IntPtr.Zero) {
93                                 throw new ArgumentNullException("Display",
94                                                         "Could not open display (X-Server required. Check you DISPLAY environment variable)");
95                         }
96
97                         this.display = display;
98
99                         // Debugging support
100                         if (Environment.GetEnvironmentVariable ("MONO_XSYNC") != null) {
101                                 Xlib.XSynchronize (display, true);
102                         }
103
104                         if (Environment.GetEnvironmentVariable ("MONO_XEXCEPTIONS") != null) {
105                                 ErrorExceptions = true;
106                         }
107
108                         atoms = new X11Atoms (this);
109
110                         DoubleClickInterval = 500;
111
112                         HoverState.Interval = 500;
113                         HoverState.Timer = new Timer();
114                         HoverState.Timer.Enabled = false;
115                         HoverState.Timer.Interval = HoverState.Interval;
116                         HoverState.Timer.Tick += new EventHandler(MouseHover);
117                         HoverState.Size = new Size(4, 4);
118                         HoverState.X = -1;
119                         HoverState.Y = -1;
120
121                         ActiveWindow = null;
122                         FocusWindow = null;
123                         ModalWindows = new Stack(3);
124
125                         MouseState = MouseButtons.None;
126                         MousePosition = new Point(0, 0);
127
128                         Caret.Timer = new Timer();
129                         Caret.Timer.Interval = 500;             // FIXME - where should this number come from?
130                         Caret.Timer.Tick += new EventHandler(CaretCallback);
131
132                         // XXX multiscreen work here
133                         root_hwnd = new X11RootHwnd (this, Xlib.XRootWindow (display, DefaultScreen));
134
135                         // XXX do we need a per-screen foster parent?
136                         // Create the foster parent
137                         foster_hwnd = new X11Hwnd (this,
138                                                    Xlib.XCreateSimpleWindow (display, root_hwnd.WholeWindow,
139                                                                              0, 0, 1, 1, 4, UIntPtr.Zero, UIntPtr.Zero));
140
141                         Keyboard = new X11Keyboard(display, foster_hwnd.Handle);
142                         Dnd = new X11Dnd (display);
143
144                         ErrorExceptions = false;
145
146                         // Handle any upcoming errors
147                         ErrorHandler = new XErrorHandler (HandleError);
148                         Xlib.XSetErrorHandler (ErrorHandler);
149
150                         X11DesktopColors.Initialize(); // XXX we need to figure out how to make this display specific?
151
152                         // Disable keyboard autorepeat
153                         try {
154                                 Xlib.XkbSetDetectableAutoRepeat (display, true, IntPtr.Zero);
155                                 detectable_key_auto_repeat = true;
156                         } catch {
157                                 Console.Error.WriteLine ("Could not disable keyboard auto repeat, will attempt to disable manually.");
158                                 detectable_key_auto_repeat = false;
159                         }
160
161                         // we re-set our error handler here, X11DesktopColor stuff might have stolen it (gtk does)
162                         Xlib.XSetErrorHandler (ErrorHandler);
163
164                         // create our event thread (just sits on the X socket waiting for events)
165                         event_thread = new Thread (new ThreadStart (XEventThread));
166                         event_thread.IsBackground = true;
167                         event_thread.Start ();
168                 }
169
170                 #region Callbacks
171                 private void MouseHover(object sender, EventArgs e)
172                 {
173                         HoverState.Timer.Enabled = false;
174
175                         if (HoverState.Window != IntPtr.Zero) {
176                                 X11Hwnd hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (HoverState.Window);
177                                 if (hwnd != null) {
178                                         XEvent xevent = new XEvent ();
179
180                                         xevent.type = XEventName.ClientMessage;
181                                         xevent.ClientMessageEvent.display = display;
182                                         xevent.ClientMessageEvent.window = HoverState.Window;
183                                         xevent.ClientMessageEvent.message_type = HoverState.Atom;
184                                         xevent.ClientMessageEvent.format = 32;
185                                         xevent.ClientMessageEvent.ptr1 = (IntPtr) (HoverState.Y << 16 | HoverState.X);
186
187                                         hwnd.Queue.Enqueue (xevent);
188                                 }
189                         }
190                 }
191
192                 private void CaretCallback (object sender, EventArgs e)
193                 {
194                         if (Caret.Paused) {
195                                 return;
196                         }
197                         Caret.On = !Caret.On;
198
199                         Xlib.XDrawLine (display, Caret.Hwnd, Caret.gc, Caret.X, Caret.Y, Caret.X, Caret.Y + Caret.Height);
200                 }
201
202                 internal string WhereString ()
203                 {
204                         StackTrace      stack;
205                         StackFrame      frame;
206                         string          newline;
207                         string          unknown;
208                         StringBuilder   sb;
209                         MethodBase      method;
210
211                         newline = String.Format("{0}\t {1} ", Environment.NewLine, Locale.GetText("at"));
212                         unknown = Locale.GetText("<unknown method>");
213                         sb = new StringBuilder();
214                         stack = new StackTrace(true);
215
216                         for (int i = 0; i < stack.FrameCount; i++) {
217                                 frame = stack.GetFrame (i);
218                                 sb.Append(newline);
219
220                                 method = frame.GetMethod();
221                                 if (method != null) {
222                                         if (frame.GetFileLineNumber() != 0)
223                                                 sb.AppendFormat ("{0}.{1} () [{2}:{3}]",
224                                                                  method.DeclaringType.FullName, method.Name,
225                                                                  Path.GetFileName(frame.GetFileName()), frame.GetFileLineNumber());
226                                         else
227                                                 sb.AppendFormat ("{0}.{1} ()", method.DeclaringType.FullName, method.Name);
228                                 } else { 
229                                         sb.Append(unknown);
230                                 }
231                         }
232                         return sb.ToString();
233                 }
234
235                 private int HandleError (IntPtr display, ref XErrorEvent error_event)
236                 {
237                         if (ErrorExceptions)
238                                 throw new X11Exception (error_event.display, error_event.resourceid,
239                                                         error_event.serial, error_event.error_code,
240                                                         error_event.request_code, error_event.minor_code);
241                         else
242                                 Console.WriteLine ("X11 Error encountered: {0}{1}\n",
243                                                    X11Exception.GetMessage(error_event.display, error_event.resourceid,
244                                                                            error_event.serial, error_event.error_code,
245                                                                            error_event.request_code, error_event.minor_code),
246                                                    WhereString());
247                         return 0;
248                 }
249                 #endregion      // Callbacks
250
251                 private void ShowCaret()
252                 {
253                         if ((Caret.gc == IntPtr.Zero) || Caret.On) {
254                                 return;
255                         }
256                         Caret.On = true;
257
258                         Xlib.XDrawLine (display, Caret.Window, Caret.gc, Caret.X, Caret.Y, Caret.X, Caret.Y + Caret.Height);
259                 }
260
261                 private void HideCaret()
262                 {
263                         if ((Caret.gc == IntPtr.Zero) || !Caret.On) {
264                                 return;
265                         }
266                         Caret.On = false;
267
268                         Xlib.XDrawLine (display, Caret.Window, Caret.gc, Caret.X, Caret.Y, Caret.X, Caret.Y + Caret.Height);
269                 }
270
271                 public void CaretVisible (IntPtr handle, bool visible)
272                 {
273                         if (Caret.Hwnd == handle) {
274                                 if (visible) {
275                                         if (!Caret.Visible) {
276                                                 Caret.Visible = true;
277                                                 ShowCaret();
278                                                 Caret.Timer.Start();
279                                         }
280                                 } else {
281                                         Caret.Visible = false;
282                                         Caret.Timer.Stop();
283                                         HideCaret();
284                                 }
285                         }
286                 }
287
288                 public void AudibleAlert ()
289                 {
290                         Xlib.XBell (display, 0);
291                 }
292
293                 public void Flush ()
294                 {
295                         Xlib.XFlush (display);
296                 }
297
298                 public void Close ()
299                 {
300                         // XXX shut down the event_thread
301                         Xlib.XCloseDisplay (display);
302                 }
303
304                 public IntPtr XGetParent(IntPtr handle)
305                 {
306                         IntPtr  Root;
307                         IntPtr  Parent;
308                         IntPtr  Children;
309                         int     ChildCount;
310
311                         Xlib.XQueryTree (display, handle, out Root, out Parent, out Children, out ChildCount);
312
313                         if (Children!=IntPtr.Zero) {
314                                 Xlib.XFree(Children);
315                         }
316
317                         return Parent;
318                 }
319
320                 public bool SystrayAdd(IntPtr handle, string tip, Icon icon, out ToolTip tt)
321                 {
322                         IntPtr SystrayMgrWindow;
323
324                         Xlib.XGrabServer (display);
325                         SystrayMgrWindow = Xlib.XGetSelectionOwner (display, Atoms._NET_SYSTEM_TRAY_S);
326                         Xlib.XUngrabServer (display);
327
328                         if (SystrayMgrWindow != IntPtr.Zero) {
329                                 XSizeHints size_hints;
330                                 X11Hwnd hwnd;
331
332                                 hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
333 #if DriverDebug
334                                 Console.WriteLine("Adding Systray Whole:{0:X}, Client:{1:X}",
335                                                   hwnd.WholeWindow.ToInt32(), hwnd.ClientWindow.ToInt32());
336 #endif
337
338                                 // Oh boy.
339                                 if (hwnd.ClientWindow != hwnd.WholeWindow) {
340                                         Xlib.XDestroyWindow (display, hwnd.ClientWindow);
341                                         hwnd.ClientWindow = hwnd.WholeWindow;
342
343                                         /* by virtue of the way the tests are ordered when determining if it's PAINT
344                                            or NCPAINT, ClientWindow == WholeWindow will always be PAINT.  So, if we're
345                                            waiting on an nc_expose, drop it and remove the hwnd from the list (unless
346                                            there's a pending expose). */
347                                         if (hwnd.nc_expose_pending) {
348                                                 hwnd.nc_expose_pending = false;
349                                                 if (!hwnd.expose_pending)
350                                                         hwnd.Queue.RemovePaint (hwnd);
351                                         }
352                                 }
353
354                                 size_hints = new XSizeHints();
355
356                                 size_hints.flags = (IntPtr)(XSizeHintsFlags.PMinSize | XSizeHintsFlags.PMaxSize | XSizeHintsFlags.PBaseSize);
357
358                                 size_hints.min_width = 24;
359                                 size_hints.min_height = 24;
360                                 size_hints.max_width = 24;
361                                 size_hints.max_height = 24;
362                                 size_hints.base_width = 24;
363                                 size_hints.base_height = 24;
364
365                                 Xlib.XSetWMNormalHints (display, hwnd.WholeWindow, ref size_hints);
366
367                                 int[] atoms = new int[2];
368                                 atoms [0] = 1;                  // Version 1
369                                 atoms [1] = 1;                  // we want to be mapped
370
371                                 // This line cost me 3 days...
372                                 Xlib.XChangeProperty (display,
373                                                       hwnd.WholeWindow, Atoms._XEMBED_INFO, Atoms._XEMBED_INFO, 32,
374                                                       PropertyMode.Replace, atoms, 2);
375
376                                 // Need to pick some reasonable defaults
377                                 tt = new ToolTip();
378                                 tt.AutomaticDelay = 100;
379                                 tt.InitialDelay = 250;
380                                 tt.ReshowDelay = 250;
381                                 tt.ShowAlways = true;
382
383                                 if ((tip != null) && (tip != string.Empty)) {
384                                         tt.SetToolTip(Control.FromHandle(handle), tip);
385                                         tt.Active = true;
386                                 } else {
387                                         tt.Active = false;
388                                 }
389
390                                 SendNetClientMessage (SystrayMgrWindow,
391                                                       Atoms._NET_SYSTEM_TRAY_OPCODE,
392                                                       IntPtr.Zero,
393                                                       (IntPtr)SystrayRequest.SYSTEM_TRAY_REQUEST_DOCK,
394                                                       hwnd.WholeWindow);
395
396                                 return true;
397                         }
398
399                         tt = null;
400                         return false;
401                 }
402
403                 public bool SystrayChange (IntPtr handle, string tip, Icon icon, ref ToolTip tt)
404                 {
405                         Control control;
406
407                         control = Control.FromHandle(handle);
408                         if (control != null && tt != null) {
409                                 tt.SetToolTip(control, tip);
410                                 tt.Active = true;
411                                 return true;
412                         } else {
413                                 return false;
414                         }
415                 }
416
417                 public void SystrayRemove(IntPtr handle, ref ToolTip tt)
418                 {
419 #if GTKSOCKET_SUPPORTS_REPARENTING
420                         X11Hwnd hwnd;
421
422                         hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
423
424                         /* in the XEMBED spec, it mentions 3 ways for a client window to break the protocol with the embedder.
425                          * 1. The embedder can unmap the window and reparent to the root window (we should probably handle this...)
426                          * 2. The client can reparent its window out of the embedder window.
427                          * 3. The client can destroy its window.
428                          *
429                          * this call to SetParent is case 2, but in
430                          * the spec it also mentions that gtk doesn't
431                          * support this at present.  Looking at HEAD
432                          * gtksocket-x11.c jives with this statement.
433                          *
434                          * so we can't reparent.  we have to destroy.
435                          */
436                         SetParent(hwnd.WholeWindow, FosterParent);
437 #else
438                         Control control = Control.FromHandle(handle);
439                         if (control is NotifyIcon.NotifyIconWindow)
440                                 ((NotifyIcon.NotifyIconWindow)control).InternalRecreateHandle ();
441 #endif
442
443                         // The caller can now re-dock it later...
444                         if (tt != null) {
445                                 tt.Dispose();
446                                 tt = null;
447                         }
448                 }
449
450                 public void ResetMouseHover (X11Hwnd hovering)
451                 {
452                         HoverState.Timer.Enabled = hovering != null;
453                         HoverState.X = MousePosition.X;
454                         HoverState.Y = MousePosition.Y;
455                         HoverState.Window = hovering == null ? IntPtr.Zero : hovering.Handle;
456                 }
457
458                 public void ShowCursor (bool show)
459                 {
460                         ;       // FIXME - X11 doesn't 'hide' the cursor. we could create an empty cursor
461                 }
462
463                 public void SetModal (X11Hwnd hwnd, bool Modal)
464                 {
465                         if (Modal) {
466                                 ModalWindows.Push(hwnd);
467                         } else {
468                                 // XXX do we need to pop until the
469                                 // hwnd is off the stack?  or just the
470                                 // most recently pushed hwnd?
471                                 if (ModalWindows.Contains(hwnd)) {
472                                         ModalWindows.Pop();
473                                 }
474
475                                 if (ModalWindows.Count > 0) {
476                                         X11Hwnd top_hwnd = (X11Hwnd)ModalWindows.Peek();
477                                         top_hwnd.Activate();
478                                 }
479                         }
480                 }
481
482                 public bool SupportsTransparency ()
483                 {
484                         // compiz adds _NET_WM_WINDOW_OPACITY to _NET_SUPPORTED on the root window, check for that
485                         return ((IList)root_hwnd._NET_SUPPORTED).Contains (Atoms._NET_WM_WINDOW_OPACITY);
486                 }
487
488                 public void SendAsyncMethod (AsyncMethodData method)
489                 {
490                         X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(method.Handle);
491                         XEvent xevent = new XEvent ();
492
493                         xevent.type = XEventName.ClientMessage;
494                         xevent.ClientMessageEvent.display = display;
495                         xevent.ClientMessageEvent.window = method.Handle;
496                         xevent.ClientMessageEvent.message_type = Atoms.AsyncAtom;
497                         xevent.ClientMessageEvent.format = 32;
498                         xevent.ClientMessageEvent.ptr1 = (IntPtr) GCHandle.Alloc (method);
499
500                         hwnd.Queue.Enqueue (xevent);
501                 }
502
503                 delegate IntPtr WndProcDelegate (IntPtr hwnd, Msg message, IntPtr wParam, IntPtr lParam);
504
505                 public IntPtr SendMessage (IntPtr handle, Msg message, IntPtr wParam, IntPtr lParam)
506                 {
507                         X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
508                         if (hwnd == null)
509                                 return IntPtr.Zero;
510
511                         if (hwnd.Queue.Thread != Thread.CurrentThread) {
512                                 AsyncMethodResult       result;
513                                 AsyncMethodData         data;
514
515                                 result = new AsyncMethodResult ();
516                                 data = new AsyncMethodData ();
517
518                                 data.Handle = hwnd.Handle;
519                                 data.Method = new WndProcDelegate (NativeWindow.WndProc);
520                                 data.Args = new object[] { hwnd.Handle, message, wParam, lParam };
521                                 data.Result = result;
522                                 
523                                 SendAsyncMethod (data);
524 #if DriverDebug || DriverDebugThreads
525                                 Console.WriteLine ("Sending {0} message across.", message);
526 #endif
527
528                                 return IntPtr.Zero;
529                         }
530                         else {
531                                 return NativeWindow.WndProc (hwnd.Handle, message, wParam, lParam);
532                         }
533                 }
534
535                 // FIXME - I think this should just enqueue directly
536                 public bool PostMessage (IntPtr handle, Msg message, IntPtr wparam, IntPtr lparam)
537                 {
538                         XEvent xevent = new XEvent ();
539                         X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
540
541                         xevent.type = XEventName.ClientMessage;
542                         xevent.ClientMessageEvent.display = display;
543
544                         if (hwnd != null) {
545                                 xevent.ClientMessageEvent.window = hwnd.WholeWindow;
546                         } else {
547                                 xevent.ClientMessageEvent.window = IntPtr.Zero;
548                         }
549
550                         xevent.ClientMessageEvent.message_type = Atoms.PostAtom;
551                         xevent.ClientMessageEvent.format = 32;
552                         xevent.ClientMessageEvent.ptr1 = handle;
553                         xevent.ClientMessageEvent.ptr2 = (IntPtr) message;
554                         xevent.ClientMessageEvent.ptr3 = wparam;
555                         xevent.ClientMessageEvent.ptr4 = lparam;
556
557                         hwnd.Queue.Enqueue (xevent);
558
559                         return true;
560                 }
561
562                 public void SendNetWMMessage (IntPtr window, IntPtr message_type, IntPtr l0, IntPtr l1, IntPtr l2)
563                 {
564                         XEvent  xev;
565
566                         xev = new XEvent();
567                         xev.ClientMessageEvent.type = XEventName.ClientMessage;
568                         xev.ClientMessageEvent.send_event = true;
569                         xev.ClientMessageEvent.window = window;
570                         xev.ClientMessageEvent.message_type = message_type;
571                         xev.ClientMessageEvent.format = 32;
572                         xev.ClientMessageEvent.ptr1 = l0;
573                         xev.ClientMessageEvent.ptr2 = l1;
574                         xev.ClientMessageEvent.ptr3 = l2;
575
576                         Xlib.XSendEvent (display, root_hwnd.Handle, false,
577                                          new IntPtr ((int) (EventMask.SubstructureRedirectMask | EventMask.SubstructureNotifyMask)), ref xev);
578                 }
579
580                 public void SendNetClientMessage (IntPtr window, IntPtr message_type, IntPtr l0, IntPtr l1, IntPtr l2)
581                 {
582                         XEvent  xev;
583
584                         xev = new XEvent();
585                         xev.ClientMessageEvent.type = XEventName.ClientMessage;
586                         xev.ClientMessageEvent.send_event = true;
587                         xev.ClientMessageEvent.window = window;
588                         xev.ClientMessageEvent.message_type = message_type;
589                         xev.ClientMessageEvent.format = 32;
590                         xev.ClientMessageEvent.ptr1 = l0;
591                         xev.ClientMessageEvent.ptr2 = l1;
592                         xev.ClientMessageEvent.ptr3 = l2;
593
594                         Xlib.XSendEvent (display, window, false, new IntPtr ((int)EventMask.NoEventMask), ref xev);
595                 }
596
597                 public bool TranslateMessage (ref MSG msg)
598                 {
599                         return Keyboard.TranslateMessage (ref msg);
600                 }
601
602                 private void QueryPointer (IntPtr w, out IntPtr root, out IntPtr child,
603                                            out int root_x, out int root_y, out int child_x, out int child_y,
604                                            out int mask)
605                 {
606                         /* this code was written with the help of
607                            glance at gdk.  I never would have realized we
608                            needed a loop in order to traverse down in the
609                            hierarchy.  I would have assumed you'd get the
610                            most deeply nested child and have to do
611                            XQueryTree to move back up the hierarchy..
612                            stupid me, of course. */
613                         IntPtr c;
614
615                         Xlib.XGrabServer (display);
616
617                         Xlib.XQueryPointer (display, w, out root, out c,
618                                             out root_x, out root_y, out child_x, out child_y,
619                                             out mask);
620
621                         if (root != w)
622                                 c = root;
623
624                         IntPtr child_last = IntPtr.Zero;
625                         while (c != IntPtr.Zero) {
626                                 child_last = c;
627                                 Xlib.XQueryPointer (display, c, out root, out c,
628                                                     out root_x, out root_y, out child_x, out child_y,
629                                                     out mask);
630                         }
631
632                         Xlib.XUngrabServer (display);
633
634                         child = child_last;
635                 }
636
637                 public void SetCursorPos (int x, int y)
638                 {
639                         IntPtr root, child;
640                         int root_x, root_y, child_x, child_y, mask;
641
642                         /* we need to do a
643                          * QueryPointer before warping
644                          * because if the warp is on
645                          * the RootWindow, the x/y are
646                          * relative to the current
647                          * mouse position
648                          */
649                         QueryPointer (RootWindow.Handle,
650                                       out root,
651                                       out child,
652                                       out root_x, out root_y,
653                                       out child_x, out child_y,
654                                       out mask);
655
656                         Xlib.XWarpPointer (display, IntPtr.Zero, IntPtr.Zero, 0, 0, 0, 0, x - root_x, y - root_y);
657
658                         Xlib.XFlush (display);
659
660                         /* then we need to a
661                          * QueryPointer after warping
662                          * to manually generate a
663                          * motion event for the window
664                          * we move into.
665                          */
666                         QueryPointer (RootWindow.Handle,
667                                       out root,
668                                       out child,
669                                       out root_x, out root_y,
670                                       out child_x, out child_y,
671                                       out mask);
672
673                         X11Hwnd child_hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(child);
674                         if (child_hwnd == null)
675                                 return;
676
677                         XEvent xevent = new XEvent ();
678
679                         xevent.type = XEventName.MotionNotify;
680                         xevent.MotionEvent.display = display;
681                         xevent.MotionEvent.window = child_hwnd.Handle;
682                         xevent.MotionEvent.root = RootWindow.Handle;
683                         xevent.MotionEvent.x = child_x;
684                         xevent.MotionEvent.y = child_y;
685                         xevent.MotionEvent.x_root = root_x;
686                         xevent.MotionEvent.y_root = root_y;
687                         xevent.MotionEvent.state = mask;
688
689                         child_hwnd.Queue.Enqueue (xevent);
690                 }
691
692                 public void SetFocus (X11Hwnd new_focus)
693                 {
694                         if (new_focus == FocusWindow)
695                                 return;
696
697                         X11Hwnd prev_focus = FocusWindow;
698                         FocusWindow = new_focus;
699
700                         if (prev_focus != null)
701                                 SendMessage (prev_focus.Handle, Msg.WM_KILLFOCUS,
702                                              FocusWindow == null ? IntPtr.Zero : FocusWindow.Handle, IntPtr.Zero);
703                         if (FocusWindow != null)
704                                 SendMessage (FocusWindow.Handle, Msg.WM_SETFOCUS,
705                                              prev_focus == null ? IntPtr.Zero : prev_focus.Handle, IntPtr.Zero);
706
707                         //XSetInputFocus(DisplayHandle, Hwnd.ObjectFromHandle(handle).ClientWindow, RevertTo.None, IntPtr.Zero);
708                 }
709
710                 public IntPtr DefineCursor (Bitmap bitmap, Bitmap mask, Color cursor_pixel, Color mask_pixel, int xHotSpot, int yHotSpot)
711                 {
712                         IntPtr  cursor;
713                         Bitmap  cursor_bitmap;
714                         Bitmap  cursor_mask;
715                         Byte[]  cursor_bits;
716                         Byte[]  mask_bits;
717                         Color   c_pixel;
718                         Color   m_pixel;
719                         int     width;
720                         int     height;
721                         IntPtr  cursor_pixmap;
722                         IntPtr  mask_pixmap;
723                         XColor  fg;
724                         XColor  bg;
725                         bool    and;
726                         bool    xor;
727
728                         if (Xlib.XQueryBestCursor (display, RootWindow.Handle, bitmap.Width, bitmap.Height, out width, out height) == 0) {
729                                 return IntPtr.Zero;
730                         }
731
732                         // Win32 only allows creation cursors of a certain size
733                         if ((bitmap.Width != width) || (bitmap.Width != height)) {
734                                 cursor_bitmap = new Bitmap(bitmap, new Size(width, height));
735                                 cursor_mask = new Bitmap(mask, new Size(width, height));
736                         } else {
737                                 cursor_bitmap = bitmap;
738                                 cursor_mask = mask;
739                         }
740
741                         width = cursor_bitmap.Width;
742                         height = cursor_bitmap.Height;
743
744                         cursor_bits = new Byte[(width / 8) * height];
745                         mask_bits = new Byte[(width / 8) * height];
746
747                         for (int y = 0; y < height; y++) {
748                                 for (int x = 0; x < width; x++) {
749                                         c_pixel = cursor_bitmap.GetPixel(x, y);
750                                         m_pixel = cursor_mask.GetPixel(x, y);
751
752                                         and = c_pixel == cursor_pixel;
753                                         xor = m_pixel == mask_pixel;
754
755                                         if (!and && !xor) {
756                                                 // Black
757                                                 // cursor_bits[y * width / 8 + x / 8] &= (byte)~((1 << (x % 8)));       // The bit already is 0
758                                                 mask_bits[y * width / 8 + x / 8] |= (byte)(1 << (x % 8));
759                                         } else if (and && !xor) {
760                                                 // White
761                                                 cursor_bits[y * width / 8 + x / 8] |= (byte)(1 << (x % 8));
762                                                 mask_bits[y * width / 8 + x / 8] |= (byte)(1 << (x % 8));
763 #if notneeded
764                                         } else if (and && !xor) {
765                                                 // Screen
766                                         } else if (and && xor) {
767                                                 // Inverse Screen
768
769                                                 // X11 doesn't know the 'reverse screen' concept, so we'll treat them the same
770                                                 // we want both to be 0 so nothing to be done
771                                                 //cursor_bits[y * width / 8 + x / 8] &= (byte)~((1 << (x % 8)));
772                                                 //mask_bits[y * width / 8 + x / 8] |= (byte)(01 << (x % 8));
773 #endif
774                                         }
775                                 }
776                         }
777
778                         cursor_pixmap = Xlib.XCreatePixmapFromBitmapData (display, RootWindow.Handle,
779                                                                           cursor_bits, width, height, (IntPtr)1, (IntPtr)0, 1);
780                         mask_pixmap = Xlib.XCreatePixmapFromBitmapData (display, RootWindow.Handle,
781                                                                         mask_bits, width, height, (IntPtr)1, (IntPtr)0, 1);
782                         fg = new XColor();
783                         bg = new XColor();
784
785                         fg.pixel = Xlib.XWhitePixel (display, DefaultScreen);
786                         fg.red = (ushort)65535;
787                         fg.green = (ushort)65535;
788                         fg.blue = (ushort)65535;
789
790                         bg.pixel = Xlib.XBlackPixel (display, DefaultScreen);
791
792                         cursor = Xlib.XCreatePixmapCursor (display, cursor_pixmap, mask_pixmap, ref fg, ref bg, xHotSpot, yHotSpot);
793
794                         Xlib.XFreePixmap (display, cursor_pixmap);
795                         Xlib.XFreePixmap (display, mask_pixmap);
796
797                         return cursor;
798                 }
799
800                 public IntPtr DefineStdCursor (StdCursor id)
801                 {
802                         CursorFontShape shape;
803
804                         // FIXME - define missing shapes
805
806                         switch (id) {
807                         case StdCursor.AppStarting:
808                                 shape = CursorFontShape.XC_watch;
809                                 break;
810
811                         case StdCursor.Arrow:
812                                 shape = CursorFontShape.XC_top_left_arrow;
813                                 break;
814
815                         case StdCursor.Cross:
816                                 shape = CursorFontShape.XC_crosshair;
817                                 break;
818
819                         case StdCursor.Default:
820                                 shape = CursorFontShape.XC_top_left_arrow;
821                                 break;
822
823                         case StdCursor.Hand:
824                                 shape = CursorFontShape.XC_hand1;
825                                 break;
826
827                         case StdCursor.Help:
828                                 shape = CursorFontShape.XC_question_arrow;
829                                 break;
830                         
831                         case StdCursor.HSplit:
832                                 shape = CursorFontShape.XC_sb_v_double_arrow; 
833                                 break;
834
835                         case StdCursor.IBeam:
836                                 shape = CursorFontShape.XC_xterm; 
837                                 break;
838
839                         case StdCursor.No:
840                                 shape = CursorFontShape.XC_circle; 
841                                 break;
842
843                         case StdCursor.NoMove2D:
844                                 shape = CursorFontShape.XC_fleur; 
845                                 break;
846
847                         case StdCursor.NoMoveHoriz:
848                                 shape = CursorFontShape.XC_fleur; 
849                                 break;
850
851                         case StdCursor.NoMoveVert:
852                                 shape = CursorFontShape.XC_fleur; 
853                                 break;
854
855                         case StdCursor.PanEast:
856                                 shape = CursorFontShape.XC_fleur; 
857                                 break;
858
859                         case StdCursor.PanNE:
860                                 shape = CursorFontShape.XC_fleur; 
861                                 break;
862
863                         case StdCursor.PanNorth:
864                                 shape = CursorFontShape.XC_fleur; 
865                                 break;
866
867                         case StdCursor.PanNW:
868                                 shape = CursorFontShape.XC_fleur; 
869                                 break;
870
871                         case StdCursor.PanSE:
872                                 shape = CursorFontShape.XC_fleur; 
873                                 break;
874
875                         case StdCursor.PanSouth:
876                                 shape = CursorFontShape.XC_fleur; 
877                                 break;
878
879                         case StdCursor.PanSW:
880                                 shape = CursorFontShape.XC_fleur; 
881                                 break;
882
883                         case StdCursor.PanWest:
884                                 shape = CursorFontShape.XC_sizing; 
885                                 break;
886
887                         case StdCursor.SizeAll:
888                                 shape = CursorFontShape.XC_fleur; 
889                                 break;
890
891                         case StdCursor.SizeNESW:
892                                 shape = CursorFontShape.XC_top_right_corner; 
893                                 break;
894
895                         case StdCursor.SizeNS:
896                                 shape = CursorFontShape.XC_sb_v_double_arrow;
897                                 break;
898
899                         case StdCursor.SizeNWSE:
900                                 shape = CursorFontShape.XC_top_left_corner; 
901                                 break;
902
903                         case StdCursor.SizeWE:
904                                 shape = CursorFontShape.XC_sb_h_double_arrow; 
905                                 break;
906
907                         case StdCursor.UpArrow:
908                                 shape = CursorFontShape.XC_center_ptr; 
909                                 break;
910
911                         case StdCursor.VSplit:
912                                 shape = CursorFontShape.XC_sb_h_double_arrow;
913                                 break;
914
915                         case StdCursor.WaitCursor:
916                                 shape = CursorFontShape.XC_watch; 
917                                 break;
918
919                         default:
920                                 return IntPtr.Zero;
921                         }
922
923                         return Xlib.XCreateFontCursor (display, shape);
924                 }
925
926                 // XXX this should take an X11Hwnd.
927                 public void CreateCaret (IntPtr handle, int width, int height)
928                 {
929                         XGCValues gc_values;
930                         X11Hwnd hwnd;
931
932                         hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
933
934                         if (Caret.Hwnd != IntPtr.Zero)
935                                 DestroyCaret(Caret.Hwnd);
936
937                         Caret.Hwnd = handle;
938                         Caret.Window = hwnd.ClientWindow;
939                         Caret.Width = width;
940                         Caret.Height = height;
941                         Caret.Visible = false;
942                         Caret.On = false;
943
944                         gc_values = new XGCValues();
945                         gc_values.line_width = width;
946
947                         Caret.gc = Xlib.XCreateGC (display, Caret.Window, new IntPtr ((int)GCFunction.GCLineWidth), ref gc_values);
948                         if (Caret.gc == IntPtr.Zero) {
949                                 Caret.Hwnd = IntPtr.Zero;
950                                 return;
951                         }
952
953                         Xlib.XSetFunction (display, Caret.gc, GXFunction.GXinvert);
954                 }
955
956
957                 // XXX this should take an X11Hwnd.
958                 public void DestroyCaret (IntPtr handle)
959                 {
960                         if (Caret.Hwnd == handle) {
961                                 if (Caret.Visible == true) {
962                                         Caret.Timer.Stop ();
963                                 }
964                                 if (Caret.gc != IntPtr.Zero) {
965                                         Xlib.XFreeGC (display, Caret.gc);
966                                         Caret.gc = IntPtr.Zero;
967                                 }
968                                 Caret.Hwnd = IntPtr.Zero;
969                                 Caret.Visible = false;
970                                 Caret.On = false;
971                         }
972                 }
973
974                 public void SetCaretPos (IntPtr handle, int x, int y)
975                 {
976                         if (Caret.Hwnd == handle) {
977                                 Caret.Timer.Stop();
978                                 HideCaret();
979
980                                 Caret.X = x;
981                                 Caret.Y = y;
982
983                                 if (Caret.Visible == true) {
984                                         ShowCaret();
985                                         Caret.Timer.Start();
986                                 }
987                         }
988                 }
989
990                 public void DestroyCursor (IntPtr cursor)
991                 {
992                         Xlib.XFreeCursor (display, cursor);
993                 }
994
995                 private void AccumulateDestroyedHandles (Control c, ArrayList list)
996                 {
997                         if (c != null) {
998                                 Control[] controls = c.child_controls.GetAllControls ();
999
1000                                 if (c.IsHandleCreated && !c.IsDisposed) {
1001                                         X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(c.Handle);
1002
1003 #if DriverDebug || DriverDebugDestroy
1004                                         Console.WriteLine (" + adding {0} to the list of zombie windows", XplatUI.Window (hwnd.Handle));
1005                                         Console.WriteLine (" + parent X window is {0:X}", XGetParent (hwnd.WholeWindow).ToInt32());
1006 #endif
1007
1008                                         list.Add (hwnd);
1009                                         CleanupCachedWindows (hwnd);
1010                                         hwnd.zombie = true;
1011                                 }
1012
1013                                 for (int  i = 0; i < controls.Length; i ++) {
1014                                         AccumulateDestroyedHandles (controls[i], list);
1015                                 }
1016                         }
1017                         
1018                 }
1019
1020                 void CleanupCachedWindows (X11Hwnd hwnd)
1021                 {
1022                         if (ActiveWindow == hwnd) {
1023                                 SendMessage (hwnd.ClientWindow, Msg.WM_ACTIVATE, (IntPtr)WindowActiveFlags.WA_INACTIVE, IntPtr.Zero);
1024                                 ActiveWindow = null;
1025                         }
1026
1027                         if (FocusWindow == hwnd) {
1028                                 SendMessage (hwnd.ClientWindow, Msg.WM_KILLFOCUS, IntPtr.Zero, IntPtr.Zero);
1029                                 FocusWindow = null;
1030                         }
1031
1032                         if (Grab.Hwnd == hwnd.Handle) {
1033                                 Grab.Hwnd = IntPtr.Zero;
1034                                 Grab.Confined = false;
1035                         }
1036
1037                         DestroyCaret (hwnd.Handle);
1038                 }
1039
1040
1041                 public void DestroyWindow (X11Hwnd hwnd)
1042                 {
1043                         CleanupCachedWindows (hwnd);
1044
1045                         ArrayList windows = new ArrayList ();
1046
1047                         AccumulateDestroyedHandles (Control.ControlNativeWindow.ControlFromHandle(hwnd.Handle), windows);
1048
1049                         hwnd.DestroyWindow ();
1050
1051                         foreach (X11Hwnd h in windows) {
1052                                 SendMessage (h.Handle, Msg.WM_DESTROY, IntPtr.Zero, IntPtr.Zero);
1053                         }
1054                 }
1055
1056                 public X11Hwnd GetActiveWindow ()
1057                 {
1058                         IntPtr  actual_atom;
1059                         int     actual_format;
1060                         IntPtr  nitems;
1061                         IntPtr  bytes_after;
1062                         IntPtr  prop = IntPtr.Zero;
1063                         IntPtr  active = IntPtr.Zero;
1064
1065                         Xlib.XGetWindowProperty (display, RootWindow.Handle,
1066                                                  Atoms._NET_ACTIVE_WINDOW, IntPtr.Zero, new IntPtr (1), false,
1067                                                  Atoms.XA_WINDOW, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop);
1068
1069                         if (((long)nitems > 0) && (prop != IntPtr.Zero)) {
1070                                 active = (IntPtr)Marshal.ReadInt32(prop);
1071                                 Xlib.XFree(prop);
1072                         }
1073
1074                         return (X11Hwnd)Hwnd.GetObjectFromWindow(active);
1075                 }
1076
1077                 public void GetDisplaySize (out Size size)
1078                 {
1079                         XWindowAttributes attributes = new XWindowAttributes();
1080
1081                         // FIXME - use _NET_WM messages instead?
1082                         Xlib.XGetWindowAttributes (display, RootWindow.Handle, ref attributes);
1083
1084                         size = new Size(attributes.width, attributes.height);
1085                 }
1086
1087                 // XXX this method doesn't really fit well anywhere in the backend
1088                 public SizeF GetAutoScaleSize (Font font)
1089                 {
1090                         Graphics        g;
1091                         float           width;
1092                         string          magic_string = "The quick brown fox jumped over the lazy dog.";
1093                         double          magic_number = 44.549996948242189; // XXX my god, where did this number come from?
1094
1095                         g = Graphics.FromHwnd (FosterParent.Handle);
1096
1097                         width = (float) (g.MeasureString (magic_string, font).Width / magic_number);
1098                         return new SizeF(width, font.Height);
1099                 }
1100
1101                 public void GetCursorPos (X11Hwnd hwnd, out int x, out int y)
1102                 {
1103                         IntPtr  use_handle;
1104                         IntPtr  root;
1105                         IntPtr  child;
1106                         int     root_x;
1107                         int     root_y;
1108                         int     win_x;
1109                         int     win_y;
1110                         int     keys_buttons;
1111
1112                         if (hwnd != null)
1113                                 use_handle = hwnd.Handle;
1114                         else
1115                                 use_handle = RootWindow.Handle;
1116
1117                         QueryPointer (use_handle, out root, out child, out root_x, out root_y, out win_x, out win_y, out keys_buttons);
1118
1119                         if (hwnd != null) {
1120                                 x = win_x;
1121                                 y = win_y;
1122                         } else {
1123                                 x = root_x;
1124                                 y = root_y;
1125                         }
1126                 }
1127
1128                 public IntPtr GetFocus ()
1129                 {
1130                         return FocusWindow.Handle;
1131                 }
1132
1133                 public IntPtr GetMousewParam (int Delta)
1134                 {
1135                         int     result = 0;
1136
1137                         if ((MouseState & MouseButtons.Left) != 0) {
1138                                 result |= (int)MsgButtons.MK_LBUTTON;
1139                         }
1140
1141                         if ((MouseState & MouseButtons.Middle) != 0) {
1142                                 result |= (int)MsgButtons.MK_MBUTTON;
1143                         }
1144
1145                         if ((MouseState & MouseButtons.Right) != 0) {
1146                                 result |= (int)MsgButtons.MK_RBUTTON;
1147                         }
1148
1149                         Keys mods = ModifierKeys;
1150                         if ((mods & Keys.Control) != 0) {
1151                                 result |= (int)MsgButtons.MK_CONTROL;
1152                         }
1153
1154                         if ((mods & Keys.Shift) != 0) {
1155                                 result |= (int)MsgButtons.MK_SHIFT;
1156                         }
1157
1158                         result |= Delta << 16;
1159
1160                         return (IntPtr)result;
1161                 }
1162
1163                 public void GrabInfo (out IntPtr handle, out bool GrabConfined, out Rectangle GrabArea)
1164                 {
1165                         handle = Grab.Hwnd;
1166                         GrabConfined = Grab.Confined;
1167                         GrabArea = Grab.Area;
1168                 }
1169
1170                 public void GrabWindow (X11Hwnd hwnd, X11Hwnd confine_to)
1171                 {
1172                         IntPtr  confine_to_window;
1173
1174                         confine_to_window = IntPtr.Zero;
1175
1176                         if (confine_to != null) {
1177                                 XWindowAttributes attributes = new XWindowAttributes();
1178
1179                                 Xlib.XGetWindowAttributes (display, confine_to.ClientWindow, ref attributes);
1180
1181                                 Grab.Area.X = attributes.x;
1182                                 Grab.Area.Y = attributes.y;
1183                                 Grab.Area.Width = attributes.width;
1184                                 Grab.Area.Height = attributes.height;
1185                                 Grab.Confined = true;
1186                                 confine_to_window = confine_to.ClientWindow;
1187                         }
1188
1189                         Grab.Hwnd = hwnd.ClientWindow;
1190
1191                         Xlib.XGrabPointer (display, hwnd.ClientWindow, false, 
1192                                            EventMask.ButtonPressMask | EventMask.ButtonMotionMask |
1193                                            EventMask.ButtonReleaseMask | EventMask.PointerMotionMask,
1194                                            GrabMode.GrabModeAsync, GrabMode.GrabModeAsync, confine_to_window, IntPtr.Zero, IntPtr.Zero);
1195                 }
1196
1197                 public void UngrabWindow (X11Hwnd hwnd)
1198                 {
1199                         Xlib.XUngrabPointer (display, IntPtr.Zero);
1200                         Xlib.XFlush (display);
1201
1202                         // XXX make sure hwnd is what should have the grab and throw if not
1203                         Grab.Hwnd = IntPtr.Zero;
1204                         Grab.Confined = false;
1205                 }
1206
1207                 private void TranslatePropertyToClipboard (IntPtr property)
1208                 {
1209                         IntPtr actual_atom;
1210                         int actual_format;
1211                         IntPtr nitems;
1212                         IntPtr bytes_after;
1213                         IntPtr prop = IntPtr.Zero;
1214
1215                         Clipboard.Item = null;
1216
1217                         Xlib.XGetWindowProperty (display, FosterParent.Handle,
1218                                                  property, IntPtr.Zero, new IntPtr (0x7fffffff), true,
1219                                                  Atoms.AnyPropertyType, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop);
1220
1221                         if ((long)nitems > 0) {
1222                                 if (property == Atoms.XA_STRING) {
1223                                         Clipboard.Item = Marshal.PtrToStringAnsi(prop);
1224                                 } else if (property == Atoms.XA_BITMAP) {
1225                                         // FIXME - convert bitmap to image
1226                                 } else if (property == Atoms.XA_PIXMAP) {
1227                                         // FIXME - convert pixmap to image
1228                                 } else if (property == Atoms.OEMTEXT) {
1229                                         Clipboard.Item = Marshal.PtrToStringAnsi(prop);
1230                                 } else if (property == Atoms.UNICODETEXT) {
1231                                         Clipboard.Item = Marshal.PtrToStringAnsi(prop);
1232                                 }
1233
1234                                 Xlib.XFree(prop);
1235                         }
1236                 }
1237
1238                 // XXX should we be using @handle instead of Atoms.CLIPBOARD here?
1239                 public int[] ClipboardAvailableFormats (IntPtr handle)
1240                 {
1241                         // XXX deal with the updatemessagequeue stuff
1242 #if true
1243                         return new int[0];
1244 #else
1245                         DataFormats.Format f;
1246                         int[] result;
1247
1248                         f = DataFormats.Format.List;
1249
1250                         if (Xlib.XGetSelectionOwner (display, Atoms.CLIPBOARD) == IntPtr.Zero) {
1251                                 return null;
1252                         }
1253
1254                         Clipboard.Formats = new ArrayList();
1255
1256                         while (f != null) {
1257                                 Xlib.XConvertSelection (display, Atoms.CLIPBOARD, (IntPtr)f.Id, (IntPtr)f.Id, FosterParent.Handle, IntPtr.Zero);
1258
1259                                 Clipboard.Enumerating = true;
1260                                 while (Clipboard.Enumerating) {
1261                                         UpdateMessageQueue(null);
1262                                 }
1263                                 f = f.Next;
1264                         }
1265
1266                         result = new int[Clipboard.Formats.Count];
1267
1268                         for (int i = 0; i < Clipboard.Formats.Count; i++) {
1269                                 result[i] = ((IntPtr)Clipboard.Formats[i]).ToInt32 ();
1270                         }
1271
1272                         Clipboard.Formats = null;
1273                         return result;
1274 #endif
1275                 }
1276
1277                 public void ClipboardClose (IntPtr handle)
1278                 {
1279                         if (handle != ClipMagic) {
1280                                 throw new ArgumentException("handle is not a valid clipboard handle");
1281                         }
1282                         return;
1283                 }
1284
1285                 public int ClipboardGetID (IntPtr handle, string format)
1286                 {
1287                         if (handle != ClipMagic) {
1288                                 throw new ArgumentException("handle is not a valid clipboard handle");
1289                         }
1290
1291                         if (format == "Text" ) return Atoms.XA_STRING.ToInt32();
1292                         else if (format == "Bitmap" ) return Atoms.XA_BITMAP.ToInt32();
1293                         //else if (format == "MetaFilePict" ) return 3;
1294                         //else if (format == "SymbolicLink" ) return 4;
1295                         //else if (format == "DataInterchangeFormat" ) return 5;
1296                         //else if (format == "Tiff" ) return 6;
1297                         else if (format == "OEMText" ) return Atoms.OEMTEXT.ToInt32();
1298                         else if (format == "DeviceIndependentBitmap" ) return Atoms.XA_PIXMAP.ToInt32();
1299                         else if (format == "Palette" ) return Atoms.XA_COLORMAP.ToInt32();      // Useless
1300                         //else if (format == "PenData" ) return 10;
1301                         //else if (format == "RiffAudio" ) return 11;
1302                         //else if (format == "WaveAudio" ) return 12;
1303                         else if (format == "UnicodeText" ) return Atoms.UNICODETEXT.ToInt32();
1304                         //else if (format == "EnhancedMetafile" ) return 14;
1305                         //else if (format == "FileDrop" ) return 15;
1306                         //else if (format == "Locale" ) return 16;
1307
1308                         return Xlib.XInternAtom (display, format, false).ToInt32();
1309                 }
1310
1311                 public IntPtr ClipboardOpen (bool primary_selection)
1312                 {
1313                         if (!primary_selection)
1314                                 ClipMagic = Atoms.CLIPBOARD;
1315                         else
1316                                 ClipMagic = Atoms.PRIMARY;
1317
1318                         return ClipMagic;
1319                 }
1320
1321                 // XXX @converter?
1322                 public object ClipboardRetrieve (IntPtr handle, int type, XplatUI.ClipboardToObject converter)
1323                 {
1324                         // XXX deal with the UpdateMessageQueue stuff
1325 #if true
1326                         return null;
1327 #else
1328                         Xlib.XConvertSelection (display, handle, (IntPtr)type, (IntPtr)type, FosterParent, IntPtr.Zero);
1329
1330                         Clipboard.Retrieving = true;
1331                         while (Clipboard.Retrieving) {
1332                                 UpdateMessageQueue(null);
1333                         }
1334
1335                         return Clipboard.Item;
1336 #endif
1337                 }
1338
1339                 public void ClipboardStore (IntPtr handle, object obj, int type, XplatUI.ObjectToClipboard converter)
1340                 {
1341                         Clipboard.Item = obj;
1342                         Clipboard.Type = type;
1343                         Clipboard.Converter = converter;
1344
1345                         if (obj != null) {
1346                                 Xlib.XSetSelectionOwner (display, Atoms.CLIPBOARD, FosterParent.Handle, IntPtr.Zero);
1347                         } else {
1348                                 // Clearing the selection
1349                                 Xlib.XSetSelectionOwner (display, Atoms.CLIPBOARD, IntPtr.Zero, IntPtr.Zero);
1350                         }
1351                 }
1352
1353
1354                 public PaintEventArgs PaintEventStart (IntPtr handle, bool client)
1355                 {
1356                         X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
1357
1358                         if (Caret.Visible == true) {
1359                                 Caret.Paused = true;
1360                                 HideCaret();
1361                         }
1362
1363                         return hwnd.PaintEventStart (client);
1364                 }
1365
1366                 public void PaintEventEnd (IntPtr handle, bool client)
1367                 {
1368                         X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
1369
1370                         hwnd.PaintEventEnd (client);
1371
1372                         if (Caret.Visible == true) {
1373                                 ShowCaret();
1374                                 Caret.Paused = false;
1375                         }
1376                 }
1377
1378                 public DragDropEffects StartDrag (IntPtr handle, object data,
1379                                                   DragDropEffects allowed_effects)
1380                 {
1381                         X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle (handle);
1382
1383                         if (hwnd == null)
1384                                 throw new ArgumentException ("Attempt to begin drag from invalid window handle (" + handle.ToInt32 () + ").");
1385
1386                         return Dnd.StartDrag (hwnd.ClientWindow, data, allowed_effects);
1387                 }
1388
1389                 public X11Atoms Atoms {
1390                         get { return atoms; }
1391                 }
1392
1393                 public int CurrentTimestamp {
1394                         get {
1395                                 TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1));
1396
1397                                 return (int) t.TotalSeconds;
1398                         }
1399                 }
1400
1401                 public Size CursorSize {
1402                         get {
1403                                 int     x;
1404                                 int     y;
1405
1406                                 if (Xlib.XQueryBestCursor (display, RootWindow.Handle, 32, 32, out x, out y) != 0) {
1407                                         return new Size (x, y);
1408                                 } else {
1409                                         return new Size (16, 16);
1410                                 }
1411                         }
1412                 } 
1413
1414                 public IntPtr Handle {
1415                         get { return display; }
1416                 }
1417
1418                 public Size IconSize {
1419                         get {
1420                                 IntPtr          list;
1421                                 XIconSize       size;
1422                                 int             count;
1423
1424                                 if (Xlib.XGetIconSizes (display, RootWindow.Handle, out list, out count) != 0) {
1425                                         long            current;
1426                                         int             largest;
1427
1428                                         current = (long)list;
1429                                         largest = 0;
1430
1431                                         size = new XIconSize();
1432
1433                                         for (int i = 0; i < count; i++) {
1434                                                 size = (XIconSize)Marshal.PtrToStructure((IntPtr)current, size.GetType());
1435                                                 current += Marshal.SizeOf(size);
1436
1437                                                 // Look for our preferred size
1438                                                 if (size.min_width == 32) {
1439                                                         Xlib.XFree(list);
1440                                                         return new Size(32, 32);
1441                                                 }
1442
1443                                                 if (size.max_width == 32) {
1444                                                         Xlib.XFree(list);
1445                                                         return new Size(32, 32);
1446                                                 }
1447
1448                                                 if (size.min_width < 32 && size.max_width > 32) {
1449                                                         int     x;
1450
1451                                                         // check if we can fit one
1452                                                         x = size.min_width;
1453                                                         while (x < size.max_width) {
1454                                                                 x += size.width_inc;
1455                                                                 if (x == 32) {
1456                                                                         Xlib.XFree(list);
1457                                                                         return new Size(32, 32);
1458                                                                 }
1459                                                         }
1460                                                 }
1461
1462                                                 if (largest < size.max_width) {
1463                                                         largest = size.max_width;
1464                                                 }
1465                                         }
1466
1467                                         // We didn't find a match or we wouldn't be here
1468                                         return new Size(largest, largest);
1469
1470                                 } else {
1471                                         return new Size(32, 32);
1472                                 }
1473                         }
1474                 } 
1475
1476                 public int KeyboardSpeed {
1477                         get {
1478                                 //
1479                                 // A lot harder: need to do:
1480                                 // XkbQueryExtension(0x08051008, 0xbfffdf4c, 0xbfffdf50, 0xbfffdf54, 0xbfffdf58)       = 1
1481                                 // XkbAllocKeyboard(0x08051008, 0xbfffdf4c, 0xbfffdf50, 0xbfffdf54, 0xbfffdf58)        = 0x080517a8
1482                                 // XkbGetControls(0x08051008, 1, 0x080517a8, 0xbfffdf54, 0xbfffdf58)                   = 0
1483                                 //
1484                                 // And from that we can tell the repetition rate
1485                                 //
1486                                 // Notice, the values must map to:
1487                                 //   [0, 31] which maps to 2.5 to 30 repetitions per second.
1488                                 //
1489                                 return 0;
1490                         }
1491                 }
1492
1493                 public int KeyboardDelay {
1494                         get {
1495                                 //
1496                                 // Return values must range from 0 to 4, 0 meaning 250ms,
1497                                 // and 4 meaning 1000 ms.
1498                                 //
1499                                 return 1; // ie, 500 ms
1500                         }
1501                 } 
1502
1503                 public int DefaultScreen {
1504                         get { return Xlib.XDefaultScreen (display); }
1505                 }
1506
1507                 public IntPtr DefaultColormap {
1508                         // XXX multiscreen
1509                         get { return Xlib.XDefaultColormap (display, DefaultScreen); }
1510                 }
1511
1512                 public Keys ModifierKeys {
1513                         get {
1514                                 return Keyboard.ModifierKeys;
1515                         }
1516                 }
1517
1518                 public X11RootHwnd RootWindow {
1519                         get { return root_hwnd; }
1520                 }
1521
1522                 public Size SmallIconSize {
1523                         get {
1524                                 IntPtr          list;
1525                                 XIconSize       size;
1526                                 int             count;
1527
1528                                 if (Xlib.XGetIconSizes (display, RootWindow.Handle, out list, out count) != 0) {
1529                                         long            current;
1530                                         int             smallest;
1531
1532                                         current = (long)list;
1533                                         smallest = 0;
1534
1535                                         size = new XIconSize();
1536
1537                                         for (int i = 0; i < count; i++) {
1538                                                 size = (XIconSize)Marshal.PtrToStructure((IntPtr)current, size.GetType());
1539                                                 current += Marshal.SizeOf(size);
1540
1541                                                 // Look for our preferred size
1542                                                 if (size.min_width == 16) {
1543                                                         Xlib.XFree(list);
1544                                                         return new Size(16, 16);
1545                                                 }
1546
1547                                                 if (size.max_width == 16) {
1548                                                         Xlib.XFree(list);
1549                                                         return new Size(16, 16);
1550                                                 }
1551
1552                                                 if (size.min_width < 16 && size.max_width > 16) {
1553                                                         int     x;
1554
1555                                                         // check if we can fit one
1556                                                         x = size.min_width;
1557                                                         while (x < size.max_width) {
1558                                                                 x += size.width_inc;
1559                                                                 if (x == 16) {
1560                                                                         Xlib.XFree(list);
1561                                                                         return new Size(16, 16);
1562                                                                 }
1563                                                         }
1564                                                 }
1565
1566                                                 if (smallest == 0 || smallest > size.min_width) {
1567                                                         smallest = size.min_width;
1568                                                 }
1569                                         }
1570
1571                                         // We didn't find a match or we wouldn't be here
1572                                         return new Size(smallest, smallest);
1573
1574                                 } else {
1575                                         return new Size(16, 16);
1576                                 }
1577                         }
1578                 } 
1579
1580                 public X11Hwnd FosterParent {
1581                         get { return foster_hwnd; }
1582                 }
1583
1584                 public int MouseHoverTime {
1585                         get { return HoverState.Interval; }
1586                 }
1587
1588                 public Rectangle WorkingArea {
1589                         get {
1590                                 IntPtr actual_atom;
1591                                 int actual_format;
1592                                 IntPtr nitems;
1593                                 IntPtr bytes_after;
1594                                 IntPtr prop = IntPtr.Zero;
1595                                 int width;
1596                                 int height;
1597                                 int current_desktop;
1598                                 int x;
1599                                 int y;
1600
1601                                 Xlib.XGetWindowProperty (display, RootWindow.Handle, 
1602                                                          Atoms._NET_CURRENT_DESKTOP, IntPtr.Zero, new IntPtr(1), false, Atoms.XA_CARDINAL,
1603                                                          out actual_atom, out actual_format, out nitems, out bytes_after, ref prop);
1604
1605                                 if ((long)nitems < 1) {
1606                                         goto failsafe;
1607                                 }
1608
1609                                 current_desktop = Marshal.ReadIntPtr(prop, 0).ToInt32();
1610                                 Xlib.XFree(prop);
1611
1612                                 Xlib.XGetWindowProperty (display, RootWindow.Handle,
1613                                                          Atoms._NET_WORKAREA, IntPtr.Zero, new IntPtr (256), false, Atoms.XA_CARDINAL,
1614                                                          out actual_atom, out actual_format, out nitems, out bytes_after, ref prop);
1615
1616                                 if ((long)nitems < 4 * current_desktop) {
1617                                         goto failsafe;
1618                                 }
1619
1620                                 x = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop).ToInt32();
1621                                 y = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop + IntPtr.Size).ToInt32();
1622                                 width = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop + IntPtr.Size * 2).ToInt32();
1623                                 height = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop + IntPtr.Size * 3).ToInt32();
1624                                 Xlib.XFree(prop);
1625
1626                                 return new Rectangle(x, y, width, height);
1627
1628                         failsafe:
1629                                 XWindowAttributes attributes = new XWindowAttributes();
1630
1631                                 Xlib.XGetWindowAttributes (display, RootWindow.Handle, ref attributes);
1632
1633                                 return new Rectangle(0, 0, attributes.width, attributes.height);
1634                         }
1635                 }
1636
1637
1638                 private void XEventThread ()
1639                 {
1640                         bool need_idle_dispatch = true;
1641                         while (true) {
1642                                 XEvent xevent = new XEvent ();
1643
1644                                 if (need_idle_dispatch
1645                                     && Xlib.XPending (display) == 0)
1646                                 {
1647                                         // XXX can we do the event thing here?  or do we need
1648                                         // to manufacture an XEvent and do it in GetMessage?
1649                                         need_idle_dispatch = false;
1650                                         XplatUIX11_new.GetInstance().OnIdle (EventArgs.Empty);
1651                                 }
1652
1653                                 Xlib.XNextEvent (display, ref xevent);
1654
1655                                 if (xevent.AnyEvent.type == XEventName.KeyPress
1656                                     && Xlib.XFilterEvent (ref xevent, FosterParent.Handle))
1657                                         continue;
1658
1659                                 X11Hwnd hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow(xevent.AnyEvent.window);
1660                                 if (hwnd != null) {
1661                                         switch (xevent.type) {
1662                                         case XEventName.KeyPress:
1663                                         case XEventName.KeyRelease:
1664                                         case XEventName.ButtonPress:
1665                                         case XEventName.ButtonRelease:
1666                                                 need_idle_dispatch = true;
1667                                                 break;
1668                                         }
1669
1670                                         hwnd.EnqueueEvent (xevent);
1671                                 }
1672                         }
1673                 }
1674
1675                 // This is called from the non-XEventThread threads.
1676                 [MonoTODO("Implement filtering")]
1677                 public bool GetMessage (object queue_id, ref MSG msg, IntPtr handle, int wFilterMin, int wFilterMax)
1678                 {
1679                         X11ThreadQueue queue = (X11ThreadQueue)queue_id;
1680                         XEvent xevent;
1681                         bool client;
1682                         bool got_event;
1683                         X11Hwnd hwnd;
1684
1685                         ProcessNextMessage:
1686
1687                         got_event = queue.Dequeue (out xevent);
1688
1689                         queue.CheckTimers ();
1690
1691                         if (!got_event) {
1692                                 if (!queue.PostQuitState) {
1693                                         msg.hwnd= IntPtr.Zero;
1694                                         msg.message = Msg.WM_ENTERIDLE;
1695                                         return true;
1696                                 }
1697
1698                                 // We reset ourselves so GetMessage can be called again
1699                                 queue.PostQuitState = false;
1700
1701                                 return false;
1702                         }
1703
1704                         hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (xevent.AnyEvent.window);
1705
1706                         // Handle messages for windows that are already or are about to be destroyed.
1707
1708                         // we need a special block for this because unless we remove the hwnd from the paint
1709                         // queue it will always stay there (since we don't handle the expose), and we'll
1710                         // effectively loop infinitely trying to repaint a non-existant window.
1711                         if (hwnd != null && hwnd.zombie && xevent.type == XEventName.Expose) {
1712                                 hwnd.expose_pending = hwnd.nc_expose_pending = false;
1713                                 hwnd.Queue.RemovePaint (hwnd);
1714                                 goto ProcessNextMessage;
1715                         }
1716
1717                         // We need to make sure we only allow DestroyNotify events through for zombie
1718                         // hwnds, since much of the event handling code makes requests using the hwnd's
1719                         // ClientWindow, and that'll result in BadWindow errors if there's some lag
1720                         // between the XDestroyWindow call and the DestroyNotify event.
1721                         if (hwnd == null || hwnd.zombie) {
1722 #if DriverDebug || DriverDebugDestroy
1723                                 Console.WriteLine("GetMessage(): Got message {0} for non-existent or already destroyed window {1:X}",
1724                                                   xevent.type, xevent.AnyEvent.window.ToInt32());
1725 #endif
1726                                 goto ProcessNextMessage;
1727                         }
1728
1729                         client = hwnd.ClientWindow == xevent.AnyEvent.window;
1730
1731                         msg.hwnd = hwnd.Handle;
1732
1733                         switch (xevent.type) {
1734                         case XEventName.KeyPress:
1735                                 Keyboard.KeyEvent (FocusWindow.Handle, xevent, ref msg);
1736                                 break;
1737
1738                         case XEventName.KeyRelease:
1739                                 Keyboard.KeyEvent (FocusWindow.Handle, xevent, ref msg);
1740                                 break;
1741
1742                         case XEventName.ButtonPress: {
1743                                 switch(xevent.ButtonEvent.button) {
1744                                 case 1:
1745                                         MouseState |= MouseButtons.Left;
1746                                         if (client) {
1747                                                 msg.message = Msg.WM_LBUTTONDOWN;
1748                                         } else {
1749                                                 msg.message = Msg.WM_NCLBUTTONDOWN;
1750                                                 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1751                                         }
1752                                         // TODO: For WM_NCLBUTTONDOWN wParam specifies a hit-test value not the virtual keys down
1753                                         msg.wParam=GetMousewParam(0);
1754                                         break;
1755
1756                                 case 2:
1757                                         MouseState |= MouseButtons.Middle;
1758                                         if (client) {
1759                                                 msg.message = Msg.WM_MBUTTONDOWN;
1760                                         } else {
1761                                                 msg.message = Msg.WM_NCMBUTTONDOWN;
1762                                                 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1763                                         }
1764                                         msg.wParam=GetMousewParam(0);
1765                                         break;
1766
1767                                 case 3:
1768                                         MouseState |= MouseButtons.Right;
1769                                         if (client) {
1770                                                 msg.message = Msg.WM_RBUTTONDOWN;
1771                                         } else {
1772                                                 msg.message = Msg.WM_NCRBUTTONDOWN;
1773                                                 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1774                                         }
1775                                         msg.wParam=GetMousewParam(0);
1776                                         break;
1777
1778                                 case 4:
1779                                         msg.hwnd = FocusWindow.Handle;
1780                                         msg.message=Msg.WM_MOUSEWHEEL;
1781                                         msg.wParam=GetMousewParam(120);
1782                                         break;
1783
1784                                 case 5:
1785                                         msg.hwnd = FocusWindow.Handle;
1786                                         msg.message=Msg.WM_MOUSEWHEEL;
1787                                         msg.wParam=GetMousewParam(-120);
1788                                         break;
1789                                 }
1790
1791                                 msg.lParam=(IntPtr) (xevent.ButtonEvent.y << 16 | xevent.ButtonEvent.x);
1792                                 MousePosition.X = xevent.ButtonEvent.x;
1793                                 MousePosition.Y = xevent.ButtonEvent.y;
1794
1795                                 if (!hwnd.Enabled) {
1796                                         IntPtr dummy;
1797
1798                                         msg.hwnd = hwnd.EnabledHwnd;
1799                                         Xlib.XTranslateCoordinates (display, xevent.AnyEvent.window,
1800                                                                     Hwnd.ObjectFromHandle(msg.hwnd).ClientWindow,
1801                                                                     xevent.ButtonEvent.x, xevent.ButtonEvent.y,
1802                                                                     out xevent.ButtonEvent.x, out xevent.ButtonEvent.y, out dummy);
1803                                         msg.lParam = (IntPtr)(MousePosition.Y << 16 | MousePosition.X);
1804                                 }
1805
1806                                 if (Grab.Hwnd != IntPtr.Zero)
1807                                         msg.hwnd = Grab.Hwnd;
1808
1809                                 if (ClickPending.Pending &&
1810                                     ((((long)xevent.ButtonEvent.time - ClickPending.Time) < DoubleClickInterval) &&
1811                                      (msg.wParam == ClickPending.wParam) &&
1812                                      (msg.lParam == ClickPending.lParam) &&
1813                                      (msg.message == ClickPending.Message))) {
1814                                         // Looks like a genuine double click, clicked twice on the same spot with the same keys
1815                                         switch(xevent.ButtonEvent.button) {
1816                                         case 1:
1817                                                 msg.message = client ? Msg.WM_LBUTTONDBLCLK : Msg.WM_NCLBUTTONDBLCLK;
1818                                                 break;
1819
1820                                         case 2:
1821                                                 msg.message = client ? Msg.WM_MBUTTONDBLCLK : Msg.WM_NCMBUTTONDBLCLK;
1822                                                 break;
1823
1824                                         case 3:
1825                                                 msg.message = client ? Msg.WM_RBUTTONDBLCLK : Msg.WM_NCRBUTTONDBLCLK;
1826                                                 break;
1827                                         }
1828
1829                                         ClickPending.Pending = false;
1830
1831                                 }
1832                                 else {
1833                                         ClickPending.Pending = true;
1834                                         ClickPending.Hwnd = msg.hwnd;
1835                                         ClickPending.Message = msg.message;
1836                                         ClickPending.wParam = msg.wParam;
1837                                         ClickPending.lParam = msg.lParam;
1838                                         ClickPending.Time = (long)xevent.ButtonEvent.time;
1839                                 }
1840
1841                                 break;
1842                         }
1843
1844                         case XEventName.ButtonRelease:
1845                                 if (Dnd.InDrag()) {
1846                                         Dnd.HandleButtonRelease (ref xevent);
1847                                         break;
1848                                 }
1849
1850                                 switch(xevent.ButtonEvent.button) {
1851                                 case 1:
1852                                         if (client) {
1853                                                 msg.message = Msg.WM_LBUTTONUP;
1854                                         } else {
1855                                                 msg.message = Msg.WM_NCLBUTTONUP;
1856                                                 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1857                                         }
1858                                         MouseState &= ~MouseButtons.Left;
1859                                         msg.wParam=GetMousewParam(0);
1860                                         break;
1861
1862                                 case 2:
1863                                         if (client) {
1864                                                 msg.message = Msg.WM_MBUTTONUP;
1865                                         } else {
1866                                                 msg.message = Msg.WM_NCMBUTTONUP;
1867                                                 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1868                                         }
1869                                         MouseState &= ~MouseButtons.Middle;
1870                                         msg.wParam=GetMousewParam(0);
1871                                         break;
1872
1873                                 case 3:
1874                                         if (client) {
1875                                                 msg.message = Msg.WM_RBUTTONUP;
1876                                         } else {
1877                                                 msg.message = Msg.WM_NCRBUTTONUP;
1878                                                 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1879                                         }
1880                                         MouseState &= ~MouseButtons.Right;
1881                                         msg.wParam=GetMousewParam(0);
1882                                         break;
1883
1884                                 case 4:
1885                                         goto ProcessNextMessage;
1886
1887                                 case 5:
1888                                         goto ProcessNextMessage;
1889                                 }
1890
1891                                 if (!hwnd.Enabled) {
1892                                         IntPtr dummy;
1893
1894                                         msg.hwnd = hwnd.EnabledHwnd;
1895                                         Xlib.XTranslateCoordinates (display, xevent.AnyEvent.window,
1896                                                                     Hwnd.ObjectFromHandle(msg.hwnd).ClientWindow,
1897                                                                     xevent.ButtonEvent.x, xevent.ButtonEvent.y,
1898                                                                     out xevent.ButtonEvent.x, out xevent.ButtonEvent.y, out dummy);
1899                                         msg.lParam = (IntPtr)(MousePosition.Y << 16 | MousePosition.X);
1900                                 }
1901
1902                                 if (Grab.Hwnd != IntPtr.Zero)
1903                                         msg.hwnd = Grab.Hwnd;
1904
1905                                 msg.lParam=(IntPtr) (xevent.ButtonEvent.y << 16 | xevent.ButtonEvent.x);
1906                                 MousePosition.X = xevent.ButtonEvent.x;
1907                                 MousePosition.Y = xevent.ButtonEvent.y;
1908                                 break;
1909
1910                         case XEventName.MotionNotify:
1911                                 /* XXX move the compression stuff here */
1912
1913                                 if (client) {
1914 #if DriverDebugExtra
1915                                         Console.WriteLine("GetMessage(): Window {0:X} MotionNotify x={1} y={2}",
1916                                                           client ? hwnd.ClientWindow.ToInt32() : hwnd.WholeWindow.ToInt32(),
1917                                                           xevent.MotionEvent.x, xevent.MotionEvent.y);
1918 #endif
1919
1920                                         if (Dnd.HandleMotionNotify (ref xevent))
1921                                                 goto ProcessNextMessage;
1922
1923                                         if (Grab.Hwnd != IntPtr.Zero)
1924                                                 msg.hwnd = Grab.Hwnd;
1925                                         else
1926                                                 NativeWindow.WndProc(msg.hwnd, Msg.WM_SETCURSOR, msg.hwnd, (IntPtr)HitTest.HTCLIENT);
1927
1928                                         msg.message = Msg.WM_MOUSEMOVE;
1929                                         msg.wParam = GetMousewParam(0);
1930                                         msg.lParam = (IntPtr) (xevent.MotionEvent.y << 16 | xevent.MotionEvent.x & 0xFFFF);
1931
1932                                         if (!hwnd.Enabled) {
1933                                                 IntPtr dummy;
1934
1935                                                 msg.hwnd = hwnd.EnabledHwnd;
1936                                                 Xlib.XTranslateCoordinates (display, xevent.AnyEvent.window,
1937                                                                             Hwnd.ObjectFromHandle(msg.hwnd).ClientWindow,
1938                                                                             xevent.MotionEvent.x, xevent.MotionEvent.y,
1939                                                                             out xevent.MotionEvent.x, out xevent.MotionEvent.y, out dummy);
1940                                                 msg.lParam = (IntPtr)(MousePosition.Y << 16 | MousePosition.X);
1941                                         }
1942
1943                                         MousePosition.X = xevent.MotionEvent.x;
1944                                         MousePosition.Y = xevent.MotionEvent.y;
1945
1946                                         if ((HoverState.Timer.Enabled) &&
1947                                             (((MousePosition.X + HoverState.Size.Width) < HoverState.X) ||
1948                                              ((MousePosition.X - HoverState.Size.Width) > HoverState.X) ||
1949                                              ((MousePosition.Y + HoverState.Size.Height) < HoverState.Y) ||
1950                                              ((MousePosition.Y - HoverState.Size.Height) > HoverState.Y))) {
1951
1952                                                 HoverState.Timer.Stop();
1953                                                 HoverState.Timer.Start();
1954                                                 HoverState.X = MousePosition.X;
1955                                                 HoverState.Y = MousePosition.Y;
1956                                         }
1957
1958                                         break;
1959                                 }
1960                                 else {
1961                                         HitTest ht;
1962                                         IntPtr dummy;
1963                                         int screen_x;
1964                                         int screen_y;
1965
1966 #if DriverDebugExtra
1967                                         Console.WriteLine("GetMessage(): non-client area {0:X} MotionNotify x={1} y={2}",
1968                                                           client ? hwnd.ClientWindow.ToInt32() : hwnd.WholeWindow.ToInt32(),
1969                                                           xevent.MotionEvent.x, xevent.MotionEvent.y);
1970 #endif
1971                                         msg.message = Msg.WM_NCMOUSEMOVE;
1972
1973                                         if (!hwnd.Enabled) {
1974                                                 msg.hwnd = hwnd.EnabledHwnd;
1975                                                 Xlib.XTranslateCoordinates (display, xevent.AnyEvent.window,
1976                                                                             Hwnd.ObjectFromHandle(msg.hwnd).ClientWindow,
1977                                                                             xevent.MotionEvent.x, xevent.MotionEvent.y,
1978                                                                             out xevent.MotionEvent.x, out xevent.MotionEvent.y, out dummy);
1979                                                 msg.lParam = (IntPtr)(MousePosition.Y << 16 | MousePosition.X);
1980                                         }
1981
1982                                         // The hit test is sent in screen coordinates
1983                                         Xlib.XTranslateCoordinates (display, xevent.AnyEvent.window, RootWindow.Handle,
1984                                                                     xevent.MotionEvent.x, xevent.MotionEvent.y,
1985                                                                     out screen_x, out screen_y, out dummy);
1986
1987                                         msg.lParam = (IntPtr) (screen_y << 16 | screen_x & 0xFFFF);
1988                                         ht = (HitTest)NativeWindow.WndProc (hwnd.ClientWindow, Msg.WM_NCHITTEST,
1989                                                                             IntPtr.Zero, msg.lParam).ToInt32 ();
1990                                         NativeWindow.WndProc(hwnd.ClientWindow, Msg.WM_SETCURSOR, msg.hwnd, (IntPtr)ht);
1991
1992                                         MousePosition.X = xevent.MotionEvent.x;
1993                                         MousePosition.Y = xevent.MotionEvent.y;
1994                                 }
1995
1996                                 break;
1997
1998                         case XEventName.EnterNotify:
1999                                 if (!hwnd.Enabled)
2000                                         goto ProcessNextMessage;
2001
2002                                 if (xevent.CrossingEvent.mode != NotifyMode.NotifyNormal)
2003                                         goto ProcessNextMessage;
2004
2005                                 msg.message = Msg.WM_MOUSE_ENTER;
2006                                 HoverState.X = xevent.CrossingEvent.x;
2007                                 HoverState.Y = xevent.CrossingEvent.y;
2008                                 HoverState.Timer.Enabled = true;
2009                                 HoverState.Window = xevent.CrossingEvent.window;
2010                                 break;
2011
2012                         case XEventName.LeaveNotify:
2013                                 if (!hwnd.Enabled)
2014                                         goto ProcessNextMessage;
2015
2016                                 if ((xevent.CrossingEvent.mode != NotifyMode.NotifyNormal) ||
2017                                     (xevent.CrossingEvent.window != hwnd.ClientWindow))
2018                                         goto ProcessNextMessage;
2019
2020                                 msg.message=Msg.WM_MOUSE_LEAVE;
2021                                 HoverState.Timer.Enabled = false;
2022                                 HoverState.Window = IntPtr.Zero;
2023                                 break;
2024
2025                         case XEventName.ReparentNotify:
2026                                 if (hwnd.parent == null) {      // Toplevel
2027                                         if ((xevent.ReparentEvent.parent != IntPtr.Zero) && (xevent.ReparentEvent.window == hwnd.WholeWindow)) {
2028                                                 // We need to adjust x/y
2029                                                 // This sucks ass, part 2
2030                                                 // Every WM does the reparenting of toplevel windows different, so there's
2031                                                 // no standard way of getting our adjustment considering frames/decorations
2032                                                 // The code below is needed for metacity. KDE doesn't works just fine without this
2033                                                 int     dummy_int;
2034                                                 IntPtr  dummy_ptr;
2035                                                 int     new_x;
2036                                                 int     new_y;
2037                                                 int     frame_left;
2038                                                 int     frame_top;
2039
2040                                                 hwnd.Reparented = true;
2041
2042                                                 Xlib.XGetGeometry(display, XGetParent(hwnd.WholeWindow),
2043                                                                   out dummy_ptr, out new_x, out new_y,
2044                                                                   out dummy_int, out dummy_int, out dummy_int, out dummy_int);
2045                                                 hwnd.FrameExtents(out frame_left, out frame_top);
2046                                                 if ((frame_left != 0) && (frame_top != 0) && (new_x != frame_left) && (new_y != frame_top)) {
2047                                                         hwnd.x = new_x;
2048                                                         hwnd.y = new_y;
2049                                                         hwnd.whacky_wm = true;
2050                                                 }
2051
2052                                                 if (hwnd.opacity != 0xffffffff) {
2053                                                         IntPtr opacity;
2054
2055                                                         opacity = (IntPtr)(Int32)hwnd.opacity;
2056                                                         Xlib.XChangeProperty (display, XGetParent(hwnd.WholeWindow),
2057                                                                               Atoms._NET_WM_WINDOW_OPACITY, Atoms.XA_CARDINAL, 32,
2058                                                                               PropertyMode.Replace, ref opacity, 1);
2059                                                 }
2060                                                 SendMessage(msg.hwnd, Msg.WM_WINDOWPOSCHANGED, msg.wParam, msg.lParam);
2061                                                 goto ProcessNextMessage;
2062                                         } else {
2063                                                 hwnd.Reparented = false;
2064                                                 goto ProcessNextMessage;
2065                                         }
2066                                 }
2067                                 goto ProcessNextMessage;
2068
2069                         case XEventName.ConfigureNotify:
2070 #if DriverDebugExtra
2071                                 Console.WriteLine("GetMessage(): Window {0:X} ConfigureNotify x={1} y={2} width={3} height={4}",
2072                                                   hwnd.ClientWindow.ToInt32(),
2073                                                   xevent.ConfigureEvent.x, xevent.ConfigureEvent.y,
2074                                                   xevent.ConfigureEvent.width, xevent.ConfigureEvent.height);
2075 #endif
2076                                 hwnd.HandleConfigureNotify (xevent);
2077
2078                                 goto ProcessNextMessage;
2079
2080                         case XEventName.FocusIn:
2081                                 // We received focus. We use X11 focus only to know if the app window does or does not have focus
2082                                 // We do not track the actual focussed window via it. Instead, this is done via FocusWindow internally
2083                                 // Receiving focus means we've gotten activated and therefore we need to let the actual FocusWindow know 
2084                                 // about it having focus again
2085                                 if (xevent.FocusChangeEvent.detail != NotifyDetail.NotifyNonlinear)
2086                                         goto ProcessNextMessage;
2087
2088                                 if (FocusWindow.Handle == IntPtr.Zero) {
2089                                         Control c = Control.FromHandle (hwnd.ClientWindow);
2090                                         if (c == null)
2091                                                 goto ProcessNextMessage;
2092                                         Form form = c.FindForm ();
2093                                         if (form == null)
2094                                                 goto ProcessNextMessage;
2095                                         ActiveWindow = (X11Hwnd)Hwnd.ObjectFromHandle (form.Handle);
2096                                         SendMessage (ActiveWindow.Handle, Msg.WM_ACTIVATE, (IntPtr) WindowActiveFlags.WA_ACTIVE, IntPtr.Zero);
2097                                         goto ProcessNextMessage;
2098                                 }
2099                                 Keyboard.FocusIn(FocusWindow.Handle);
2100                                 SendMessage(FocusWindow.Handle, Msg.WM_SETFOCUS, IntPtr.Zero, IntPtr.Zero);
2101                                 goto ProcessNextMessage;
2102
2103                         case XEventName.FocusOut:
2104                                 // Se the comment for our FocusIn handler
2105                                 if (xevent.FocusChangeEvent.detail != NotifyDetail.NotifyNonlinear)
2106                                         goto ProcessNextMessage;
2107
2108                                 Keyboard.FocusOut(FocusWindow.Handle);
2109
2110                                 while (Keyboard.ResetKeyState(FocusWindow.Handle, ref msg))
2111                                         SendMessage(FocusWindow.Handle, msg.message, msg.wParam, msg.lParam);
2112
2113                                 SendMessage(FocusWindow.Handle, Msg.WM_KILLFOCUS, IntPtr.Zero, IntPtr.Zero);
2114                                 goto ProcessNextMessage;
2115
2116                         case XEventName.Expose:
2117                                 if (queue.PostQuitState || !hwnd.Mapped) {
2118                                         if (client)
2119                                                 hwnd.expose_pending = false;
2120                                         else
2121                                                 hwnd.nc_expose_pending = false;
2122                                         goto ProcessNextMessage;
2123                                 }
2124
2125                                 // XXX these should really be error conditions.  if we
2126                                 // have an expose event in the queue, there should be
2127                                 // one pending.
2128                                 if (client && !hwnd.expose_pending) {
2129                                         Console.WriteLine ("client expose but no expose pending");
2130                                         goto ProcessNextMessage;
2131                                 }
2132                                 else if (!client && !hwnd.nc_expose_pending) {
2133                                         Console.WriteLine ("non-client expose but no expose pending");
2134                                         goto ProcessNextMessage;
2135                                 }
2136
2137                                 if (client) {
2138                                         //#if DriverDebugExtra
2139                                         Console.WriteLine("GetMessage(): Window {0:X} Exposed area {1},{2} {3}x{4}",
2140                                                           hwnd.client_window.ToInt32(),
2141                                                           xevent.ExposeEvent.x, xevent.ExposeEvent.y,
2142                                                           xevent.ExposeEvent.width, xevent.ExposeEvent.height);
2143                                         //#endif
2144                                         msg.message = Msg.WM_PAINT;
2145                                 }
2146                                 else {
2147                                         Graphics g;
2148
2149                                         switch (hwnd.border_style) {
2150                                         case FormBorderStyle.Fixed3D:
2151                                                 g = Graphics.FromHwnd(hwnd.WholeWindow);
2152                                                 ControlPaint.DrawBorder3D(g, new Rectangle(0, 0, hwnd.Width, hwnd.Height),
2153                                                                           Border3DStyle.Sunken);
2154                                                 g.Dispose();
2155                                                 break;
2156
2157                                         case FormBorderStyle.FixedSingle:
2158                                                 g = Graphics.FromHwnd(hwnd.WholeWindow);
2159                                                 ControlPaint.DrawBorder(g, new Rectangle(0, 0, hwnd.Width, hwnd.Height),
2160                                                                         Color.Black, ButtonBorderStyle.Solid);
2161                                                 g.Dispose();
2162                                                 break;
2163                                         }
2164                                         //#if DriverDebugExtra
2165                                         Console.WriteLine("GetMessage(): Window {0:X} Exposed non-client area {1},{2} {3}x{4}",
2166                                                           hwnd.ClientWindow.ToInt32(),
2167                                                           xevent.ExposeEvent.x, xevent.ExposeEvent.y,
2168                                                           xevent.ExposeEvent.width, xevent.ExposeEvent.height);
2169                                         //#endif
2170
2171                                         Rectangle rect = new Rectangle (xevent.ExposeEvent.x, xevent.ExposeEvent.y,
2172                                                                         xevent.ExposeEvent.width, xevent.ExposeEvent.height);
2173                                         Region region = new Region (rect);
2174                                         IntPtr hrgn = region.GetHrgn (null); // Graphics object isn't needed
2175                                         msg.message = Msg.WM_NCPAINT;
2176                                         msg.wParam = hrgn == IntPtr.Zero ? (IntPtr)1 : hrgn;
2177                                         msg.refobject = region;
2178                                 }
2179                                 break;
2180
2181                         case XEventName.DestroyNotify:
2182
2183                                 // This is a bit tricky, we don't receive our own DestroyNotify, we only get those for our children
2184                                 hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(xevent.DestroyWindowEvent.window);
2185
2186                                 // We may get multiple for the same window, act only one the first (when Hwnd still knows about it)
2187                                 if ((hwnd != null) && (hwnd.ClientWindow == xevent.DestroyWindowEvent.window)) {
2188                                         CleanupCachedWindows (hwnd);
2189
2190 #if DriverDebugDestroy
2191                                         Console.WriteLine("Received X11 Destroy Notification for {0}", XplatUI.Window(hwnd.ClientWindow));
2192 #endif
2193
2194                                         msg.hwnd = hwnd.ClientWindow;
2195                                         msg.message=Msg.WM_DESTROY;
2196                                         hwnd.Dispose();
2197                                 }
2198                                 else
2199                                         goto ProcessNextMessage;
2200
2201                                 break;
2202
2203                         case XEventName.ClientMessage:
2204                                 if (Dnd.HandleClientMessage (ref xevent))
2205                                         goto ProcessNextMessage;
2206
2207                                 if (xevent.ClientMessageEvent.message_type == Atoms.AsyncAtom) {
2208                                         XplatUIDriverSupport.ExecuteClientMessage((GCHandle)xevent.ClientMessageEvent.ptr1);
2209                                         goto ProcessNextMessage;
2210                                 }
2211
2212                                 if (xevent.ClientMessageEvent.message_type == HoverState.Atom) {
2213                                         msg.message = Msg.WM_MOUSEHOVER;
2214                                         msg.wParam = GetMousewParam(0);
2215                                         msg.lParam = (IntPtr) (xevent.ClientMessageEvent.ptr1);
2216                                         return true;
2217                                 }
2218
2219                                 if (xevent.ClientMessageEvent.message_type == Atoms.PostAtom) {
2220                                         msg.hwnd = xevent.ClientMessageEvent.ptr1;
2221                                         msg.message = (Msg) xevent.ClientMessageEvent.ptr2.ToInt32 ();
2222                                         msg.wParam = xevent.ClientMessageEvent.ptr3;
2223                                         msg.lParam = xevent.ClientMessageEvent.ptr4;
2224                                         return true;
2225                                 }
2226
2227                                 if (xevent.ClientMessageEvent.message_type == Atoms._XEMBED) {
2228 #if DriverDebugXEmbed
2229                                         Console.WriteLine("GOT EMBED MESSAGE {0:X}, detail {1:X}",
2230                                                           xevent.ClientMessageEvent.ptr2.ToInt32(), xevent.ClientMessageEvent.ptr3.ToInt32());
2231 #endif
2232
2233                                         if (xevent.ClientMessageEvent.ptr2.ToInt32() == (int)XEmbedMessage.EmbeddedNotify) {
2234                                                 XSizeHints hints = new XSizeHints();
2235                                                 IntPtr dummy;
2236
2237                                                 Xlib.XGetWMNormalHints (display, hwnd.WholeWindow, ref hints, out dummy);
2238
2239                                                 hwnd.width = hints.max_width;
2240                                                 hwnd.height = hints.max_height;
2241                                                 hwnd.ClientRect = Rectangle.Empty;
2242                                                 SendMessage(msg.hwnd, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero);
2243                                         }
2244                                 }
2245
2246                                 if (xevent.ClientMessageEvent.message_type == Atoms.WM_PROTOCOLS) {
2247                                         if (xevent.ClientMessageEvent.ptr1 == Atoms.WM_DELETE_WINDOW) {
2248                                                 msg.message = Msg.WM_CLOSE;
2249                                                 return true;
2250                                         }
2251
2252                                         // We should not get this, but I'll leave the code in case we need it in the future
2253                                         if (xevent.ClientMessageEvent.ptr1 == Atoms.WM_TAKE_FOCUS) {
2254                                                 goto ProcessNextMessage;
2255                                         }
2256                                 }
2257                                 goto ProcessNextMessage;
2258
2259                         case XEventName.PropertyNotify:
2260                                 // The Hwnd's themselves handle this
2261                                 hwnd.PropertyChanged (xevent);
2262                                 goto ProcessNextMessage;
2263
2264                         default:
2265                                 goto ProcessNextMessage;
2266                         }
2267
2268                         return true;
2269                 }
2270
2271                 [MonoTODO("Implement filtering and PM_NOREMOVE")]
2272                 public bool PeekMessage (object queue_id, ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax, uint flags)
2273                 {
2274                         X11ThreadQueue queue = (X11ThreadQueue) queue_id;
2275                         bool    pending;
2276
2277                         if ((flags & (uint)PeekMessageFlags.PM_REMOVE) == 0) {
2278                                 throw new NotImplementedException("PeekMessage PM_NOREMOVE is not implemented yet");    // FIXME - Implement PM_NOREMOVE flag
2279                         }
2280
2281                         try {
2282                                 queue.Lock ();
2283                                 pending = false;
2284                                 if (queue.CountUnlocked > 0)
2285                                         pending = true;
2286                         }
2287                         catch {
2288                                 return false;
2289                         }
2290                         finally {
2291                                 queue.Unlock ();
2292                         }
2293
2294                         queue.CheckTimers ();
2295
2296                         if (!pending)
2297                                 return false;
2298
2299                         return GetMessage(queue_id, ref msg, hWnd, wFilterMin, wFilterMax);
2300                 }
2301         }
2302 }