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:
8 // The above copyright notice and this permission notice shall be
9 // included in all copies or substantial portions of the Software.
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.
19 // Copyright (c) 2006 Novell, Inc. (http://www.novell.com)
24 using System.Collections;
25 using System.Diagnostics;
27 using System.Drawing.Drawing2D;
28 using System.Drawing.Imaging;
31 using System.Net.Sockets;
32 using System.Reflection;
33 using System.Runtime.InteropServices;
35 using System.Threading;
36 using System.Windows.Forms;
38 namespace System.Windows.Forms.X11Internal {
40 internal class X11Display {
42 IntPtr display; /* our X handle */
44 // XXX internal because X11Hwnd needs them
45 internal IntPtr CustomVisual; // Visual for window creation
46 internal IntPtr CustomColormap; // Colormap for window creation
49 internal X11Dnd Dnd; // XXX X11Hwnd needs it to enable Dnd
50 bool detectable_key_auto_repeat;
53 X11RootHwnd root_hwnd;
58 ClipboardStruct Clipboard; // Our clipboard
61 internal X11Hwnd ActiveWindow;
65 internal Stack ModalWindows; // Stack of our modal windows
70 // mouse hover message generation
71 // XXX internal because X11Atoms needs to access it..
72 internal HoverStruct HoverState;
74 // double click message generation
75 ClickStruct ClickPending;
76 int DoubleClickInterval; // msec; max interval between clicks to count as double click
78 // Support for mouse grab
82 Point MousePosition; // Last position of mouse, in screen coords
83 MouseButtons MouseState; // Last state of mouse buttons
85 XErrorHandler ErrorHandler; // Error handler delegate
86 bool ErrorExceptions; // Throw exceptions on X errors
88 Thread event_thread; // the background thread that just watches our X socket
90 public X11Display (IntPtr display)
92 if (display == IntPtr.Zero) {
93 throw new ArgumentNullException("Display",
94 "Could not open display (X-Server required. Check you DISPLAY environment variable)");
97 this.display = display;
100 if (Environment.GetEnvironmentVariable ("MONO_XSYNC") != null) {
101 Xlib.XSynchronize (display, true);
104 if (Environment.GetEnvironmentVariable ("MONO_XEXCEPTIONS") != null) {
105 ErrorExceptions = true;
108 atoms = new X11Atoms (this);
110 DoubleClickInterval = 500;
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);
123 ModalWindows = new Stack(3);
125 MouseState = MouseButtons.None;
126 MousePosition = new Point(0, 0);
128 Caret.Timer = new Timer();
129 Caret.Timer.Interval = 500; // FIXME - where should this number come from?
130 Caret.Timer.Tick += new EventHandler(CaretCallback);
132 // XXX multiscreen work here
133 root_hwnd = new X11RootHwnd (this, Xlib.XRootWindow (display, DefaultScreen));
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));
141 Keyboard = new X11Keyboard(display, foster_hwnd.Handle);
142 Dnd = new X11Dnd (display);
144 ErrorExceptions = false;
146 // Handle any upcoming errors
147 ErrorHandler = new XErrorHandler (HandleError);
148 Xlib.XSetErrorHandler (ErrorHandler);
150 X11DesktopColors.Initialize(); // XXX we need to figure out how to make this display specific?
152 // Disable keyboard autorepeat
154 Xlib.XkbSetDetectableAutoRepeat (display, true, IntPtr.Zero);
155 detectable_key_auto_repeat = true;
157 Console.Error.WriteLine ("Could not disable keyboard auto repeat, will attempt to disable manually.");
158 detectable_key_auto_repeat = false;
161 // we re-set our error handler here, X11DesktopColor stuff might have stolen it (gtk does)
162 Xlib.XSetErrorHandler (ErrorHandler);
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 ();
171 private void MouseHover(object sender, EventArgs e)
173 HoverState.Timer.Enabled = false;
175 if (HoverState.Window != IntPtr.Zero) {
176 X11Hwnd hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (HoverState.Window);
178 XEvent xevent = new XEvent ();
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);
187 hwnd.Queue.Enqueue (xevent);
192 private void CaretCallback (object sender, EventArgs e)
197 Caret.On = !Caret.On;
199 Xlib.XDrawLine (display, Caret.Hwnd, Caret.gc, Caret.X, Caret.Y, Caret.X, Caret.Y + Caret.Height);
202 internal string WhereString ()
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);
216 for (int i = 0; i < stack.FrameCount; i++) {
217 frame = stack.GetFrame (i);
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());
227 sb.AppendFormat ("{0}.{1} ()", method.DeclaringType.FullName, method.Name);
232 return sb.ToString();
235 private int HandleError (IntPtr display, ref XErrorEvent error_event)
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);
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),
249 #endregion // Callbacks
251 private void ShowCaret()
253 if ((Caret.gc == IntPtr.Zero) || Caret.On) {
258 Xlib.XDrawLine (display, Caret.Window, Caret.gc, Caret.X, Caret.Y, Caret.X, Caret.Y + Caret.Height);
261 private void HideCaret()
263 if ((Caret.gc == IntPtr.Zero) || !Caret.On) {
268 Xlib.XDrawLine (display, Caret.Window, Caret.gc, Caret.X, Caret.Y, Caret.X, Caret.Y + Caret.Height);
271 public void CaretVisible (IntPtr handle, bool visible)
273 if (Caret.Hwnd == handle) {
275 if (!Caret.Visible) {
276 Caret.Visible = true;
281 Caret.Visible = false;
288 public void AudibleAlert ()
290 Xlib.XBell (display, 0);
295 Xlib.XFlush (display);
300 // XXX shut down the event_thread
301 Xlib.XCloseDisplay (display);
304 public IntPtr XGetParent(IntPtr handle)
311 Xlib.XQueryTree (display, handle, out Root, out Parent, out Children, out ChildCount);
313 if (Children!=IntPtr.Zero) {
314 Xlib.XFree(Children);
320 public bool SystrayAdd(IntPtr handle, string tip, Icon icon, out ToolTip tt)
322 IntPtr SystrayMgrWindow;
324 Xlib.XGrabServer (display);
325 SystrayMgrWindow = Xlib.XGetSelectionOwner (display, Atoms._NET_SYSTEM_TRAY_S);
326 Xlib.XUngrabServer (display);
328 if (SystrayMgrWindow != IntPtr.Zero) {
329 XSizeHints size_hints;
332 hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
334 Console.WriteLine("Adding Systray Whole:{0:X}, Client:{1:X}",
335 hwnd.WholeWindow.ToInt32(), hwnd.ClientWindow.ToInt32());
339 if (hwnd.ClientWindow != hwnd.WholeWindow) {
340 Xlib.XDestroyWindow (display, hwnd.ClientWindow);
341 hwnd.ClientWindow = hwnd.WholeWindow;
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);
354 size_hints = new XSizeHints();
356 size_hints.flags = (IntPtr)(XSizeHintsFlags.PMinSize | XSizeHintsFlags.PMaxSize | XSizeHintsFlags.PBaseSize);
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;
365 Xlib.XSetWMNormalHints (display, hwnd.WholeWindow, ref size_hints);
367 int[] atoms = new int[2];
368 atoms [0] = 1; // Version 1
369 atoms [1] = 1; // we want to be mapped
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);
376 // Need to pick some reasonable defaults
378 tt.AutomaticDelay = 100;
379 tt.InitialDelay = 250;
380 tt.ReshowDelay = 250;
381 tt.ShowAlways = true;
383 if ((tip != null) && (tip != string.Empty)) {
384 tt.SetToolTip(Control.FromHandle(handle), tip);
390 SendNetClientMessage (SystrayMgrWindow,
391 Atoms._NET_SYSTEM_TRAY_OPCODE,
393 (IntPtr)SystrayRequest.SYSTEM_TRAY_REQUEST_DOCK,
403 public bool SystrayChange (IntPtr handle, string tip, Icon icon, ref ToolTip tt)
407 control = Control.FromHandle(handle);
408 if (control != null && tt != null) {
409 tt.SetToolTip(control, tip);
417 public void SystrayRemove(IntPtr handle, ref ToolTip tt)
419 #if GTKSOCKET_SUPPORTS_REPARENTING
422 hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
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.
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.
434 * so we can't reparent. we have to destroy.
436 SetParent(hwnd.WholeWindow, FosterParent);
438 Control control = Control.FromHandle(handle);
439 if (control is NotifyIcon.NotifyIconWindow)
440 ((NotifyIcon.NotifyIconWindow)control).InternalRecreateHandle ();
443 // The caller can now re-dock it later...
450 public void ResetMouseHover (X11Hwnd hovering)
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;
458 public void ShowCursor (bool show)
460 ; // FIXME - X11 doesn't 'hide' the cursor. we could create an empty cursor
463 public void SetModal (X11Hwnd hwnd, bool Modal)
466 ModalWindows.Push(hwnd);
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)) {
475 if (ModalWindows.Count > 0) {
476 X11Hwnd top_hwnd = (X11Hwnd)ModalWindows.Peek();
482 public bool SupportsTransparency ()
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);
488 public void SendAsyncMethod (AsyncMethodData method)
490 X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(method.Handle);
491 XEvent xevent = new XEvent ();
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);
500 hwnd.Queue.Enqueue (xevent);
503 delegate IntPtr WndProcDelegate (IntPtr hwnd, Msg message, IntPtr wParam, IntPtr lParam);
505 public IntPtr SendMessage (IntPtr handle, Msg message, IntPtr wParam, IntPtr lParam)
507 X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
511 if (hwnd.Queue.Thread != Thread.CurrentThread) {
512 AsyncMethodResult result;
513 AsyncMethodData data;
515 result = new AsyncMethodResult ();
516 data = new AsyncMethodData ();
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;
523 SendAsyncMethod (data);
524 #if DriverDebug || DriverDebugThreads
525 Console.WriteLine ("Sending {0} message across.", message);
531 return NativeWindow.WndProc (hwnd.Handle, message, wParam, lParam);
535 // FIXME - I think this should just enqueue directly
536 public bool PostMessage (IntPtr handle, Msg message, IntPtr wparam, IntPtr lparam)
538 XEvent xevent = new XEvent ();
539 X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
541 xevent.type = XEventName.ClientMessage;
542 xevent.ClientMessageEvent.display = display;
545 xevent.ClientMessageEvent.window = hwnd.WholeWindow;
547 xevent.ClientMessageEvent.window = IntPtr.Zero;
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;
557 hwnd.Queue.Enqueue (xevent);
562 public void SendNetWMMessage (IntPtr window, IntPtr message_type, IntPtr l0, IntPtr l1, IntPtr l2)
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;
576 Xlib.XSendEvent (display, root_hwnd.Handle, false,
577 new IntPtr ((int) (EventMask.SubstructureRedirectMask | EventMask.SubstructureNotifyMask)), ref xev);
580 public void SendNetClientMessage (IntPtr window, IntPtr message_type, IntPtr l0, IntPtr l1, IntPtr l2)
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;
594 Xlib.XSendEvent (display, window, false, new IntPtr ((int)EventMask.NoEventMask), ref xev);
597 public bool TranslateMessage (ref MSG msg)
599 return Keyboard.TranslateMessage (ref msg);
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,
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. */
615 Xlib.XGrabServer (display);
617 Xlib.XQueryPointer (display, w, out root, out c,
618 out root_x, out root_y, out child_x, out child_y,
624 IntPtr child_last = IntPtr.Zero;
625 while (c != IntPtr.Zero) {
627 Xlib.XQueryPointer (display, c, out root, out c,
628 out root_x, out root_y, out child_x, out child_y,
632 Xlib.XUngrabServer (display);
637 public void SetCursorPos (int x, int y)
640 int root_x, root_y, child_x, child_y, mask;
643 * QueryPointer before warping
644 * because if the warp is on
645 * the RootWindow, the x/y are
646 * relative to the current
649 QueryPointer (RootWindow.Handle,
652 out root_x, out root_y,
653 out child_x, out child_y,
656 Xlib.XWarpPointer (display, IntPtr.Zero, IntPtr.Zero, 0, 0, 0, 0, x - root_x, y - root_y);
658 Xlib.XFlush (display);
661 * QueryPointer after warping
662 * to manually generate a
663 * motion event for the window
666 QueryPointer (RootWindow.Handle,
669 out root_x, out root_y,
670 out child_x, out child_y,
673 X11Hwnd child_hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(child);
674 if (child_hwnd == null)
677 XEvent xevent = new XEvent ();
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;
689 child_hwnd.Queue.Enqueue (xevent);
692 public void SetFocus (X11Hwnd new_focus)
694 if (new_focus == FocusWindow)
697 X11Hwnd prev_focus = FocusWindow;
698 FocusWindow = new_focus;
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);
707 //XSetInputFocus(DisplayHandle, Hwnd.ObjectFromHandle(handle).ClientWindow, RevertTo.None, IntPtr.Zero);
710 public IntPtr DefineCursor (Bitmap bitmap, Bitmap mask, Color cursor_pixel, Color mask_pixel, int xHotSpot, int yHotSpot)
713 Bitmap cursor_bitmap;
721 IntPtr cursor_pixmap;
728 if (Xlib.XQueryBestCursor (display, RootWindow.Handle, bitmap.Width, bitmap.Height, out width, out height) == 0) {
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));
737 cursor_bitmap = bitmap;
741 width = cursor_bitmap.Width;
742 height = cursor_bitmap.Height;
744 cursor_bits = new Byte[(width / 8) * height];
745 mask_bits = new Byte[(width / 8) * height];
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);
752 and = c_pixel == cursor_pixel;
753 xor = m_pixel == mask_pixel;
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) {
761 cursor_bits[y * width / 8 + x / 8] |= (byte)(1 << (x % 8));
762 mask_bits[y * width / 8 + x / 8] |= (byte)(1 << (x % 8));
764 } else if (and && !xor) {
766 } else if (and && xor) {
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));
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);
785 fg.pixel = Xlib.XWhitePixel (display, DefaultScreen);
786 fg.red = (ushort)65535;
787 fg.green = (ushort)65535;
788 fg.blue = (ushort)65535;
790 bg.pixel = Xlib.XBlackPixel (display, DefaultScreen);
792 cursor = Xlib.XCreatePixmapCursor (display, cursor_pixmap, mask_pixmap, ref fg, ref bg, xHotSpot, yHotSpot);
794 Xlib.XFreePixmap (display, cursor_pixmap);
795 Xlib.XFreePixmap (display, mask_pixmap);
800 public IntPtr DefineStdCursor (StdCursor id)
802 CursorFontShape shape;
804 // FIXME - define missing shapes
807 case StdCursor.AppStarting:
808 shape = CursorFontShape.XC_watch;
811 case StdCursor.Arrow:
812 shape = CursorFontShape.XC_top_left_arrow;
815 case StdCursor.Cross:
816 shape = CursorFontShape.XC_crosshair;
819 case StdCursor.Default:
820 shape = CursorFontShape.XC_top_left_arrow;
824 shape = CursorFontShape.XC_hand1;
828 shape = CursorFontShape.XC_question_arrow;
831 case StdCursor.HSplit:
832 shape = CursorFontShape.XC_sb_v_double_arrow;
835 case StdCursor.IBeam:
836 shape = CursorFontShape.XC_xterm;
840 shape = CursorFontShape.XC_circle;
843 case StdCursor.NoMove2D:
844 shape = CursorFontShape.XC_fleur;
847 case StdCursor.NoMoveHoriz:
848 shape = CursorFontShape.XC_fleur;
851 case StdCursor.NoMoveVert:
852 shape = CursorFontShape.XC_fleur;
855 case StdCursor.PanEast:
856 shape = CursorFontShape.XC_fleur;
859 case StdCursor.PanNE:
860 shape = CursorFontShape.XC_fleur;
863 case StdCursor.PanNorth:
864 shape = CursorFontShape.XC_fleur;
867 case StdCursor.PanNW:
868 shape = CursorFontShape.XC_fleur;
871 case StdCursor.PanSE:
872 shape = CursorFontShape.XC_fleur;
875 case StdCursor.PanSouth:
876 shape = CursorFontShape.XC_fleur;
879 case StdCursor.PanSW:
880 shape = CursorFontShape.XC_fleur;
883 case StdCursor.PanWest:
884 shape = CursorFontShape.XC_sizing;
887 case StdCursor.SizeAll:
888 shape = CursorFontShape.XC_fleur;
891 case StdCursor.SizeNESW:
892 shape = CursorFontShape.XC_top_right_corner;
895 case StdCursor.SizeNS:
896 shape = CursorFontShape.XC_sb_v_double_arrow;
899 case StdCursor.SizeNWSE:
900 shape = CursorFontShape.XC_top_left_corner;
903 case StdCursor.SizeWE:
904 shape = CursorFontShape.XC_sb_h_double_arrow;
907 case StdCursor.UpArrow:
908 shape = CursorFontShape.XC_center_ptr;
911 case StdCursor.VSplit:
912 shape = CursorFontShape.XC_sb_h_double_arrow;
915 case StdCursor.WaitCursor:
916 shape = CursorFontShape.XC_watch;
923 return Xlib.XCreateFontCursor (display, shape);
926 // XXX this should take an X11Hwnd.
927 public void CreateCaret (IntPtr handle, int width, int height)
932 hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
934 if (Caret.Hwnd != IntPtr.Zero)
935 DestroyCaret(Caret.Hwnd);
938 Caret.Window = hwnd.ClientWindow;
940 Caret.Height = height;
941 Caret.Visible = false;
944 gc_values = new XGCValues();
945 gc_values.line_width = width;
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;
953 Xlib.XSetFunction (display, Caret.gc, GXFunction.GXinvert);
957 // XXX this should take an X11Hwnd.
958 public void DestroyCaret (IntPtr handle)
960 if (Caret.Hwnd == handle) {
961 if (Caret.Visible == true) {
964 if (Caret.gc != IntPtr.Zero) {
965 Xlib.XFreeGC (display, Caret.gc);
966 Caret.gc = IntPtr.Zero;
968 Caret.Hwnd = IntPtr.Zero;
969 Caret.Visible = false;
974 public void SetCaretPos (IntPtr handle, int x, int y)
976 if (Caret.Hwnd == handle) {
983 if (Caret.Visible == true) {
990 public void DestroyCursor (IntPtr cursor)
992 Xlib.XFreeCursor (display, cursor);
995 private void AccumulateDestroyedHandles (Control c, ArrayList list)
998 Control[] controls = c.child_controls.GetAllControls ();
1000 if (c.IsHandleCreated && !c.IsDisposed) {
1001 X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(c.Handle);
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());
1009 CleanupCachedWindows (hwnd);
1013 for (int i = 0; i < controls.Length; i ++) {
1014 AccumulateDestroyedHandles (controls[i], list);
1020 void CleanupCachedWindows (X11Hwnd hwnd)
1022 if (ActiveWindow == hwnd) {
1023 SendMessage (hwnd.ClientWindow, Msg.WM_ACTIVATE, (IntPtr)WindowActiveFlags.WA_INACTIVE, IntPtr.Zero);
1024 ActiveWindow = null;
1027 if (FocusWindow == hwnd) {
1028 SendMessage (hwnd.ClientWindow, Msg.WM_KILLFOCUS, IntPtr.Zero, IntPtr.Zero);
1032 if (Grab.Hwnd == hwnd.Handle) {
1033 Grab.Hwnd = IntPtr.Zero;
1034 Grab.Confined = false;
1037 DestroyCaret (hwnd.Handle);
1041 public void DestroyWindow (X11Hwnd hwnd)
1043 CleanupCachedWindows (hwnd);
1045 ArrayList windows = new ArrayList ();
1047 AccumulateDestroyedHandles (Control.ControlNativeWindow.ControlFromHandle(hwnd.Handle), windows);
1049 hwnd.DestroyWindow ();
1051 foreach (X11Hwnd h in windows) {
1052 SendMessage (h.Handle, Msg.WM_DESTROY, IntPtr.Zero, IntPtr.Zero);
1056 public X11Hwnd GetActiveWindow ()
1062 IntPtr prop = IntPtr.Zero;
1063 IntPtr active = IntPtr.Zero;
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);
1069 if (((long)nitems > 0) && (prop != IntPtr.Zero)) {
1070 active = (IntPtr)Marshal.ReadInt32(prop);
1074 return (X11Hwnd)Hwnd.GetObjectFromWindow(active);
1077 public void GetDisplaySize (out Size size)
1079 XWindowAttributes attributes = new XWindowAttributes();
1081 // FIXME - use _NET_WM messages instead?
1082 Xlib.XGetWindowAttributes (display, RootWindow.Handle, ref attributes);
1084 size = new Size(attributes.width, attributes.height);
1087 // XXX this method doesn't really fit well anywhere in the backend
1088 public SizeF GetAutoScaleSize (Font font)
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?
1095 g = Graphics.FromHwnd (FosterParent.Handle);
1097 width = (float) (g.MeasureString (magic_string, font).Width / magic_number);
1098 return new SizeF(width, font.Height);
1101 public void GetCursorPos (X11Hwnd hwnd, out int x, out int y)
1113 use_handle = hwnd.Handle;
1115 use_handle = RootWindow.Handle;
1117 QueryPointer (use_handle, out root, out child, out root_x, out root_y, out win_x, out win_y, out keys_buttons);
1128 public IntPtr GetFocus ()
1130 return FocusWindow.Handle;
1133 public IntPtr GetMousewParam (int Delta)
1137 if ((MouseState & MouseButtons.Left) != 0) {
1138 result |= (int)MsgButtons.MK_LBUTTON;
1141 if ((MouseState & MouseButtons.Middle) != 0) {
1142 result |= (int)MsgButtons.MK_MBUTTON;
1145 if ((MouseState & MouseButtons.Right) != 0) {
1146 result |= (int)MsgButtons.MK_RBUTTON;
1149 Keys mods = ModifierKeys;
1150 if ((mods & Keys.Control) != 0) {
1151 result |= (int)MsgButtons.MK_CONTROL;
1154 if ((mods & Keys.Shift) != 0) {
1155 result |= (int)MsgButtons.MK_SHIFT;
1158 result |= Delta << 16;
1160 return (IntPtr)result;
1163 public void GrabInfo (out IntPtr handle, out bool GrabConfined, out Rectangle GrabArea)
1166 GrabConfined = Grab.Confined;
1167 GrabArea = Grab.Area;
1170 public void GrabWindow (X11Hwnd hwnd, X11Hwnd confine_to)
1172 IntPtr confine_to_window;
1174 confine_to_window = IntPtr.Zero;
1176 if (confine_to != null) {
1177 XWindowAttributes attributes = new XWindowAttributes();
1179 Xlib.XGetWindowAttributes (display, confine_to.ClientWindow, ref attributes);
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;
1189 Grab.Hwnd = hwnd.ClientWindow;
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);
1197 public void UngrabWindow (X11Hwnd hwnd)
1199 Xlib.XUngrabPointer (display, IntPtr.Zero);
1200 Xlib.XFlush (display);
1202 // XXX make sure hwnd is what should have the grab and throw if not
1203 Grab.Hwnd = IntPtr.Zero;
1204 Grab.Confined = false;
1207 private void TranslatePropertyToClipboard (IntPtr property)
1213 IntPtr prop = IntPtr.Zero;
1215 Clipboard.Item = null;
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);
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);
1238 // XXX should we be using @handle instead of Atoms.CLIPBOARD here?
1239 public int[] ClipboardAvailableFormats (IntPtr handle)
1241 // XXX deal with the updatemessagequeue stuff
1245 DataFormats.Format f;
1248 f = DataFormats.Format.List;
1250 if (Xlib.XGetSelectionOwner (display, Atoms.CLIPBOARD) == IntPtr.Zero) {
1254 Clipboard.Formats = new ArrayList();
1257 Xlib.XConvertSelection (display, Atoms.CLIPBOARD, (IntPtr)f.Id, (IntPtr)f.Id, FosterParent.Handle, IntPtr.Zero);
1259 Clipboard.Enumerating = true;
1260 while (Clipboard.Enumerating) {
1261 UpdateMessageQueue(null);
1266 result = new int[Clipboard.Formats.Count];
1268 for (int i = 0; i < Clipboard.Formats.Count; i++) {
1269 result[i] = ((IntPtr)Clipboard.Formats[i]).ToInt32 ();
1272 Clipboard.Formats = null;
1277 public void ClipboardClose (IntPtr handle)
1279 if (handle != ClipMagic) {
1280 throw new ArgumentException("handle is not a valid clipboard handle");
1285 public int ClipboardGetID (IntPtr handle, string format)
1287 if (handle != ClipMagic) {
1288 throw new ArgumentException("handle is not a valid clipboard handle");
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;
1308 return Xlib.XInternAtom (display, format, false).ToInt32();
1311 public IntPtr ClipboardOpen (bool primary_selection)
1313 if (!primary_selection)
1314 ClipMagic = Atoms.CLIPBOARD;
1316 ClipMagic = Atoms.PRIMARY;
1322 public object ClipboardRetrieve (IntPtr handle, int type, XplatUI.ClipboardToObject converter)
1324 // XXX deal with the UpdateMessageQueue stuff
1328 Xlib.XConvertSelection (display, handle, (IntPtr)type, (IntPtr)type, FosterParent, IntPtr.Zero);
1330 Clipboard.Retrieving = true;
1331 while (Clipboard.Retrieving) {
1332 UpdateMessageQueue(null);
1335 return Clipboard.Item;
1339 public void ClipboardStore (IntPtr handle, object obj, int type, XplatUI.ObjectToClipboard converter)
1341 Clipboard.Item = obj;
1342 Clipboard.Type = type;
1343 Clipboard.Converter = converter;
1346 Xlib.XSetSelectionOwner (display, Atoms.CLIPBOARD, FosterParent.Handle, IntPtr.Zero);
1348 // Clearing the selection
1349 Xlib.XSetSelectionOwner (display, Atoms.CLIPBOARD, IntPtr.Zero, IntPtr.Zero);
1354 public PaintEventArgs PaintEventStart (IntPtr handle, bool client)
1356 X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
1358 if (Caret.Visible == true) {
1359 Caret.Paused = true;
1363 return hwnd.PaintEventStart (client);
1366 public void PaintEventEnd (IntPtr handle, bool client)
1368 X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
1370 hwnd.PaintEventEnd (client);
1372 if (Caret.Visible == true) {
1374 Caret.Paused = false;
1378 public DragDropEffects StartDrag (IntPtr handle, object data,
1379 DragDropEffects allowed_effects)
1381 X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle (handle);
1384 throw new ArgumentException ("Attempt to begin drag from invalid window handle (" + handle.ToInt32 () + ").");
1386 return Dnd.StartDrag (hwnd.ClientWindow, data, allowed_effects);
1389 public X11Atoms Atoms {
1390 get { return atoms; }
1393 public int CurrentTimestamp {
1395 TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1));
1397 return (int) t.TotalSeconds;
1401 public Size CursorSize {
1406 if (Xlib.XQueryBestCursor (display, RootWindow.Handle, 32, 32, out x, out y) != 0) {
1407 return new Size (x, y);
1409 return new Size (16, 16);
1414 public IntPtr Handle {
1415 get { return display; }
1418 public Size IconSize {
1424 if (Xlib.XGetIconSizes (display, RootWindow.Handle, out list, out count) != 0) {
1428 current = (long)list;
1431 size = new XIconSize();
1433 for (int i = 0; i < count; i++) {
1434 size = (XIconSize)Marshal.PtrToStructure((IntPtr)current, size.GetType());
1435 current += Marshal.SizeOf(size);
1437 // Look for our preferred size
1438 if (size.min_width == 32) {
1440 return new Size(32, 32);
1443 if (size.max_width == 32) {
1445 return new Size(32, 32);
1448 if (size.min_width < 32 && size.max_width > 32) {
1451 // check if we can fit one
1453 while (x < size.max_width) {
1454 x += size.width_inc;
1457 return new Size(32, 32);
1462 if (largest < size.max_width) {
1463 largest = size.max_width;
1467 // We didn't find a match or we wouldn't be here
1468 return new Size(largest, largest);
1471 return new Size(32, 32);
1476 public int KeyboardSpeed {
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
1484 // And from that we can tell the repetition rate
1486 // Notice, the values must map to:
1487 // [0, 31] which maps to 2.5 to 30 repetitions per second.
1493 public int KeyboardDelay {
1496 // Return values must range from 0 to 4, 0 meaning 250ms,
1497 // and 4 meaning 1000 ms.
1499 return 1; // ie, 500 ms
1503 public int DefaultScreen {
1504 get { return Xlib.XDefaultScreen (display); }
1507 public IntPtr DefaultColormap {
1509 get { return Xlib.XDefaultColormap (display, DefaultScreen); }
1512 public Keys ModifierKeys {
1514 return Keyboard.ModifierKeys;
1518 public X11RootHwnd RootWindow {
1519 get { return root_hwnd; }
1522 public Size SmallIconSize {
1528 if (Xlib.XGetIconSizes (display, RootWindow.Handle, out list, out count) != 0) {
1532 current = (long)list;
1535 size = new XIconSize();
1537 for (int i = 0; i < count; i++) {
1538 size = (XIconSize)Marshal.PtrToStructure((IntPtr)current, size.GetType());
1539 current += Marshal.SizeOf(size);
1541 // Look for our preferred size
1542 if (size.min_width == 16) {
1544 return new Size(16, 16);
1547 if (size.max_width == 16) {
1549 return new Size(16, 16);
1552 if (size.min_width < 16 && size.max_width > 16) {
1555 // check if we can fit one
1557 while (x < size.max_width) {
1558 x += size.width_inc;
1561 return new Size(16, 16);
1566 if (smallest == 0 || smallest > size.min_width) {
1567 smallest = size.min_width;
1571 // We didn't find a match or we wouldn't be here
1572 return new Size(smallest, smallest);
1575 return new Size(16, 16);
1580 public X11Hwnd FosterParent {
1581 get { return foster_hwnd; }
1584 public int MouseHoverTime {
1585 get { return HoverState.Interval; }
1588 public Rectangle WorkingArea {
1594 IntPtr prop = IntPtr.Zero;
1597 int current_desktop;
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);
1605 if ((long)nitems < 1) {
1609 current_desktop = Marshal.ReadIntPtr(prop, 0).ToInt32();
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);
1616 if ((long)nitems < 4 * current_desktop) {
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();
1626 return new Rectangle(x, y, width, height);
1629 XWindowAttributes attributes = new XWindowAttributes();
1631 Xlib.XGetWindowAttributes (display, RootWindow.Handle, ref attributes);
1633 return new Rectangle(0, 0, attributes.width, attributes.height);
1638 private void XEventThread ()
1640 bool need_idle_dispatch = true;
1642 XEvent xevent = new XEvent ();
1644 if (need_idle_dispatch
1645 && Xlib.XPending (display) == 0)
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);
1653 Xlib.XNextEvent (display, ref xevent);
1655 if (xevent.AnyEvent.type == XEventName.KeyPress
1656 && Xlib.XFilterEvent (ref xevent, FosterParent.Handle))
1659 X11Hwnd hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow(xevent.AnyEvent.window);
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;
1670 hwnd.EnqueueEvent (xevent);
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)
1679 X11ThreadQueue queue = (X11ThreadQueue)queue_id;
1687 got_event = queue.Dequeue (out xevent);
1689 queue.CheckTimers ();
1692 if (!queue.PostQuitState) {
1693 msg.hwnd= IntPtr.Zero;
1694 msg.message = Msg.WM_ENTERIDLE;
1698 // We reset ourselves so GetMessage can be called again
1699 queue.PostQuitState = false;
1704 hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (xevent.AnyEvent.window);
1706 // Handle messages for windows that are already or are about to be destroyed.
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;
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());
1726 goto ProcessNextMessage;
1729 client = hwnd.ClientWindow == xevent.AnyEvent.window;
1731 msg.hwnd = hwnd.Handle;
1733 switch (xevent.type) {
1734 case XEventName.KeyPress:
1735 Keyboard.KeyEvent (FocusWindow.Handle, xevent, ref msg);
1738 case XEventName.KeyRelease:
1739 Keyboard.KeyEvent (FocusWindow.Handle, xevent, ref msg);
1742 case XEventName.ButtonPress: {
1743 switch(xevent.ButtonEvent.button) {
1745 MouseState |= MouseButtons.Left;
1747 msg.message = Msg.WM_LBUTTONDOWN;
1749 msg.message = Msg.WM_NCLBUTTONDOWN;
1750 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1752 // TODO: For WM_NCLBUTTONDOWN wParam specifies a hit-test value not the virtual keys down
1753 msg.wParam=GetMousewParam(0);
1757 MouseState |= MouseButtons.Middle;
1759 msg.message = Msg.WM_MBUTTONDOWN;
1761 msg.message = Msg.WM_NCMBUTTONDOWN;
1762 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1764 msg.wParam=GetMousewParam(0);
1768 MouseState |= MouseButtons.Right;
1770 msg.message = Msg.WM_RBUTTONDOWN;
1772 msg.message = Msg.WM_NCRBUTTONDOWN;
1773 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1775 msg.wParam=GetMousewParam(0);
1779 msg.hwnd = FocusWindow.Handle;
1780 msg.message=Msg.WM_MOUSEWHEEL;
1781 msg.wParam=GetMousewParam(120);
1785 msg.hwnd = FocusWindow.Handle;
1786 msg.message=Msg.WM_MOUSEWHEEL;
1787 msg.wParam=GetMousewParam(-120);
1791 msg.lParam=(IntPtr) (xevent.ButtonEvent.y << 16 | xevent.ButtonEvent.x);
1792 MousePosition.X = xevent.ButtonEvent.x;
1793 MousePosition.Y = xevent.ButtonEvent.y;
1795 if (!hwnd.Enabled) {
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);
1806 if (Grab.Hwnd != IntPtr.Zero)
1807 msg.hwnd = Grab.Hwnd;
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) {
1817 msg.message = client ? Msg.WM_LBUTTONDBLCLK : Msg.WM_NCLBUTTONDBLCLK;
1821 msg.message = client ? Msg.WM_MBUTTONDBLCLK : Msg.WM_NCMBUTTONDBLCLK;
1825 msg.message = client ? Msg.WM_RBUTTONDBLCLK : Msg.WM_NCRBUTTONDBLCLK;
1829 ClickPending.Pending = false;
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;
1844 case XEventName.ButtonRelease:
1846 Dnd.HandleButtonRelease (ref xevent);
1850 switch(xevent.ButtonEvent.button) {
1853 msg.message = Msg.WM_LBUTTONUP;
1855 msg.message = Msg.WM_NCLBUTTONUP;
1856 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1858 MouseState &= ~MouseButtons.Left;
1859 msg.wParam=GetMousewParam(0);
1864 msg.message = Msg.WM_MBUTTONUP;
1866 msg.message = Msg.WM_NCMBUTTONUP;
1867 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1869 MouseState &= ~MouseButtons.Middle;
1870 msg.wParam=GetMousewParam(0);
1875 msg.message = Msg.WM_RBUTTONUP;
1877 msg.message = Msg.WM_NCRBUTTONUP;
1878 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1880 MouseState &= ~MouseButtons.Right;
1881 msg.wParam=GetMousewParam(0);
1885 goto ProcessNextMessage;
1888 goto ProcessNextMessage;
1891 if (!hwnd.Enabled) {
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);
1902 if (Grab.Hwnd != IntPtr.Zero)
1903 msg.hwnd = Grab.Hwnd;
1905 msg.lParam=(IntPtr) (xevent.ButtonEvent.y << 16 | xevent.ButtonEvent.x);
1906 MousePosition.X = xevent.ButtonEvent.x;
1907 MousePosition.Y = xevent.ButtonEvent.y;
1910 case XEventName.MotionNotify:
1911 /* XXX move the compression stuff here */
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);
1920 if (Dnd.HandleMotionNotify (ref xevent))
1921 goto ProcessNextMessage;
1923 if (Grab.Hwnd != IntPtr.Zero)
1924 msg.hwnd = Grab.Hwnd;
1926 NativeWindow.WndProc(msg.hwnd, Msg.WM_SETCURSOR, msg.hwnd, (IntPtr)HitTest.HTCLIENT);
1928 msg.message = Msg.WM_MOUSEMOVE;
1929 msg.wParam = GetMousewParam(0);
1930 msg.lParam = (IntPtr) (xevent.MotionEvent.y << 16 | xevent.MotionEvent.x & 0xFFFF);
1932 if (!hwnd.Enabled) {
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);
1943 MousePosition.X = xevent.MotionEvent.x;
1944 MousePosition.Y = xevent.MotionEvent.y;
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))) {
1952 HoverState.Timer.Stop();
1953 HoverState.Timer.Start();
1954 HoverState.X = MousePosition.X;
1955 HoverState.Y = MousePosition.Y;
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);
1971 msg.message = Msg.WM_NCMOUSEMOVE;
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);
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);
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);
1992 MousePosition.X = xevent.MotionEvent.x;
1993 MousePosition.Y = xevent.MotionEvent.y;
1998 case XEventName.EnterNotify:
2000 goto ProcessNextMessage;
2002 if (xevent.CrossingEvent.mode != NotifyMode.NotifyNormal)
2003 goto ProcessNextMessage;
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;
2012 case XEventName.LeaveNotify:
2014 goto ProcessNextMessage;
2016 if ((xevent.CrossingEvent.mode != NotifyMode.NotifyNormal) ||
2017 (xevent.CrossingEvent.window != hwnd.ClientWindow))
2018 goto ProcessNextMessage;
2020 msg.message=Msg.WM_MOUSE_LEAVE;
2021 HoverState.Timer.Enabled = false;
2022 HoverState.Window = IntPtr.Zero;
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
2040 hwnd.Reparented = true;
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)) {
2049 hwnd.whacky_wm = true;
2052 if (hwnd.opacity != 0xffffffff) {
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);
2060 SendMessage(msg.hwnd, Msg.WM_WINDOWPOSCHANGED, msg.wParam, msg.lParam);
2061 goto ProcessNextMessage;
2063 hwnd.Reparented = false;
2064 goto ProcessNextMessage;
2067 goto ProcessNextMessage;
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);
2076 hwnd.HandleConfigureNotify (xevent);
2078 goto ProcessNextMessage;
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;
2088 if (FocusWindow.Handle == IntPtr.Zero) {
2089 Control c = Control.FromHandle (hwnd.ClientWindow);
2091 goto ProcessNextMessage;
2092 Form form = c.FindForm ();
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;
2099 Keyboard.FocusIn(FocusWindow.Handle);
2100 SendMessage(FocusWindow.Handle, Msg.WM_SETFOCUS, IntPtr.Zero, IntPtr.Zero);
2101 goto ProcessNextMessage;
2103 case XEventName.FocusOut:
2104 // Se the comment for our FocusIn handler
2105 if (xevent.FocusChangeEvent.detail != NotifyDetail.NotifyNonlinear)
2106 goto ProcessNextMessage;
2108 Keyboard.FocusOut(FocusWindow.Handle);
2110 while (Keyboard.ResetKeyState(FocusWindow.Handle, ref msg))
2111 SendMessage(FocusWindow.Handle, msg.message, msg.wParam, msg.lParam);
2113 SendMessage(FocusWindow.Handle, Msg.WM_KILLFOCUS, IntPtr.Zero, IntPtr.Zero);
2114 goto ProcessNextMessage;
2116 case XEventName.Expose:
2117 if (queue.PostQuitState || !hwnd.Mapped) {
2119 hwnd.expose_pending = false;
2121 hwnd.nc_expose_pending = false;
2122 goto ProcessNextMessage;
2125 // XXX these should really be error conditions. if we
2126 // have an expose event in the queue, there should be
2128 if (client && !hwnd.expose_pending) {
2129 Console.WriteLine ("client expose but no expose pending");
2130 goto ProcessNextMessage;
2132 else if (!client && !hwnd.nc_expose_pending) {
2133 Console.WriteLine ("non-client expose but no expose pending");
2134 goto ProcessNextMessage;
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);
2144 msg.message = Msg.WM_PAINT;
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);
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);
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);
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;
2181 case XEventName.DestroyNotify:
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);
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);
2190 #if DriverDebugDestroy
2191 Console.WriteLine("Received X11 Destroy Notification for {0}", XplatUI.Window(hwnd.ClientWindow));
2194 msg.hwnd = hwnd.ClientWindow;
2195 msg.message=Msg.WM_DESTROY;
2199 goto ProcessNextMessage;
2203 case XEventName.ClientMessage:
2204 if (Dnd.HandleClientMessage (ref xevent))
2205 goto ProcessNextMessage;
2207 if (xevent.ClientMessageEvent.message_type == Atoms.AsyncAtom) {
2208 XplatUIDriverSupport.ExecuteClientMessage((GCHandle)xevent.ClientMessageEvent.ptr1);
2209 goto ProcessNextMessage;
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);
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;
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());
2233 if (xevent.ClientMessageEvent.ptr2.ToInt32() == (int)XEmbedMessage.EmbeddedNotify) {
2234 XSizeHints hints = new XSizeHints();
2237 Xlib.XGetWMNormalHints (display, hwnd.WholeWindow, ref hints, out dummy);
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);
2246 if (xevent.ClientMessageEvent.message_type == Atoms.WM_PROTOCOLS) {
2247 if (xevent.ClientMessageEvent.ptr1 == Atoms.WM_DELETE_WINDOW) {
2248 msg.message = Msg.WM_CLOSE;
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;
2257 goto ProcessNextMessage;
2259 case XEventName.PropertyNotify:
2260 // The Hwnd's themselves handle this
2261 hwnd.PropertyChanged (xevent);
2262 goto ProcessNextMessage;
2265 goto ProcessNextMessage;
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)
2274 X11ThreadQueue queue = (X11ThreadQueue) queue_id;
2277 if ((flags & (uint)PeekMessageFlags.PM_REMOVE) == 0) {
2278 throw new NotImplementedException("PeekMessage PM_NOREMOVE is not implemented yet"); // FIXME - Implement PM_NOREMOVE flag
2284 if (queue.CountUnlocked > 0)
2294 queue.CheckTimers ();
2299 return GetMessage(queue_id, ref msg, hWnd, wFilterMin, wFilterMax);