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;
37 // Only do the poll when building with mono for now
39 using Mono.Unix.Native;
42 namespace System.Windows.Forms.X11Internal {
44 internal class X11Display {
46 IntPtr display; /* our X handle */
48 // XXX internal because X11Hwnd needs them
49 internal IntPtr CustomVisual; // Visual for window creation
50 internal IntPtr CustomColormap; // Colormap for window creation
53 internal X11Dnd Dnd; // XXX X11Hwnd needs it to enable Dnd
54 bool detectable_key_auto_repeat;
57 X11RootHwnd root_hwnd;
62 ClipboardStruct Clipboard; // Our clipboard
65 internal X11Hwnd ActiveWindow;
69 Stack ModalWindows; // Stack of our modal windows
74 // mouse hover message generation
75 // XXX internal because X11Atoms needs to access it..
76 internal HoverStruct HoverState;
78 // double click message generation
79 ClickStruct ClickPending;
80 int DoubleClickInterval; // msec; max interval between clicks to count as double click
82 // Support for mouse grab
86 IntPtr LastCursorWindow; // The last window we set the cursor on
87 IntPtr LastCursorHandle; // The handle that was last set on LastCursorWindow
88 IntPtr OverrideCursorHandle; // The cursor that is set to override any other cursors
91 Point MousePosition; // Last position of mouse, in screen coords
92 MouseButtons MouseState; // Last state of mouse buttons
94 XErrorHandler ErrorHandler; // Error handler delegate
95 bool ErrorExceptions; // Throw exceptions on X errors
97 Thread event_thread; // the background thread that just watches our X socket
103 public X11Display (IntPtr display)
105 if (display == IntPtr.Zero) {
106 throw new ArgumentNullException("Display",
107 "Could not open display (X-Server required. Check you DISPLAY environment variable)");
110 this.display = display;
113 if (Environment.GetEnvironmentVariable ("MONO_XSYNC") != null) {
114 Xlib.XSynchronize (display, true);
117 if (Environment.GetEnvironmentVariable ("MONO_XEXCEPTIONS") != null) {
118 ErrorExceptions = true;
121 atoms = new X11Atoms (this);
123 DoubleClickInterval = 500;
125 HoverState.Interval = 500;
126 HoverState.Timer = new Timer();
127 HoverState.Timer.Enabled = false;
128 HoverState.Timer.Interval = HoverState.Interval;
129 HoverState.Timer.Tick += new EventHandler(MouseHover);
130 HoverState.Size = new Size(4, 4);
136 ModalWindows = new Stack(3);
138 MouseState = MouseButtons.None;
139 MousePosition = new Point(0, 0);
141 Caret.Timer = new Timer();
142 Caret.Timer.Interval = 500; // FIXME - where should this number come from?
143 Caret.Timer.Tick += new EventHandler(CaretCallback);
145 // XXX multiscreen work here
146 root_hwnd = new X11RootHwnd (this, Xlib.XRootWindow (display, DefaultScreen));
148 // XXX do we need a per-screen foster parent?
149 // Create the foster parent
150 foster_hwnd = new X11Hwnd (this,
151 Xlib.XCreateSimpleWindow (display, root_hwnd.WholeWindow,
152 0, 0, 1, 1, 4, UIntPtr.Zero, UIntPtr.Zero));
155 pollfds = new Pollfd [1];
156 pollfds [0] = new Pollfd ();
157 pollfds [0].fd = Xlib.XConnectionNumber (display);
158 pollfds [0].events = PollEvents.POLLIN;
161 Keyboard = new X11Keyboard(display, foster_hwnd.Handle);
162 Dnd = new X11Dnd (display);
164 ErrorExceptions = false;
166 // Handle any upcoming errors
167 ErrorHandler = new XErrorHandler (HandleError);
168 Xlib.XSetErrorHandler (ErrorHandler);
170 X11DesktopColors.Initialize(); // XXX we need to figure out how to make this display specific?
172 // Disable keyboard autorepeat
174 Xlib.XkbSetDetectableAutoRepeat (display, true, IntPtr.Zero);
175 detectable_key_auto_repeat = true;
177 Console.Error.WriteLine ("Could not disable keyboard auto repeat, will attempt to disable manually.");
178 detectable_key_auto_repeat = false;
181 // we re-set our error handler here, X11DesktopColor stuff might have stolen it (gtk does)
182 Xlib.XSetErrorHandler (ErrorHandler);
184 // create our event thread (just sits on the X socket waiting for events)
185 event_thread = new Thread (new ThreadStart (XEventThread));
186 event_thread.IsBackground = true;
187 event_thread.Start ();
191 private void MouseHover(object sender, EventArgs e)
193 HoverState.Timer.Enabled = false;
195 if (HoverState.Window != IntPtr.Zero) {
196 X11Hwnd hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (HoverState.Window);
198 XEvent xevent = new XEvent ();
200 xevent.type = XEventName.ClientMessage;
201 xevent.ClientMessageEvent.display = display;
202 xevent.ClientMessageEvent.window = HoverState.Window;
203 xevent.ClientMessageEvent.message_type = HoverState.Atom;
204 xevent.ClientMessageEvent.format = 32;
205 xevent.ClientMessageEvent.ptr1 = (IntPtr) (HoverState.Y << 16 | HoverState.X);
207 hwnd.Queue.Enqueue (xevent);
212 private void CaretCallback (object sender, EventArgs e)
217 Caret.On = !Caret.On;
219 Xlib.XDrawLine (display, Caret.Hwnd, Caret.gc, Caret.X, Caret.Y, Caret.X, Caret.Y + Caret.Height);
222 internal string WhereString ()
231 newline = String.Format("{0}\t {1} ", Environment.NewLine, Locale.GetText("at"));
232 unknown = Locale.GetText("<unknown method>");
233 sb = new StringBuilder();
234 stack = new StackTrace(true);
236 for (int i = 0; i < stack.FrameCount; i++) {
237 frame = stack.GetFrame (i);
240 method = frame.GetMethod();
241 if (method != null) {
242 if (frame.GetFileLineNumber() != 0)
243 sb.AppendFormat ("{0}.{1} () [{2}:{3}]",
244 method.DeclaringType.FullName, method.Name,
245 Path.GetFileName(frame.GetFileName()), frame.GetFileLineNumber());
247 sb.AppendFormat ("{0}.{1} ()", method.DeclaringType.FullName, method.Name);
252 return sb.ToString();
255 private int HandleError (IntPtr display, ref XErrorEvent error_event)
258 throw new X11Exception (error_event.display, error_event.resourceid,
259 error_event.serial, error_event.error_code,
260 error_event.request_code, error_event.minor_code);
262 Console.WriteLine ("X11 Error encountered: {0}{1}\n",
263 X11Exception.GetMessage(error_event.display, error_event.resourceid,
264 error_event.serial, error_event.error_code,
265 error_event.request_code, error_event.minor_code),
269 #endregion // Callbacks
271 private void ShowCaret()
273 if ((Caret.gc == IntPtr.Zero) || Caret.On) {
278 Xlib.XDrawLine (display, Caret.Window, Caret.gc, Caret.X, Caret.Y, Caret.X, Caret.Y + Caret.Height);
281 private void HideCaret()
283 if ((Caret.gc == IntPtr.Zero) || !Caret.On) {
288 Xlib.XDrawLine (display, Caret.Window, Caret.gc, Caret.X, Caret.Y, Caret.X, Caret.Y + Caret.Height);
291 public void CaretVisible (IntPtr handle, bool visible)
293 if (Caret.Hwnd == handle) {
295 if (!Caret.Visible) {
296 Caret.Visible = true;
301 Caret.Visible = false;
308 public void AudibleAlert ()
310 Xlib.XBell (display, 0);
315 Xlib.XFlush (display);
320 // XXX shut down the event_thread
321 Xlib.XCloseDisplay (display);
324 public IntPtr XGetParent(IntPtr handle)
331 Xlib.XQueryTree (display, handle, out Root, out Parent, out Children, out ChildCount);
333 if (Children!=IntPtr.Zero) {
334 Xlib.XFree(Children);
340 public bool SystrayAdd(IntPtr handle, string tip, Icon icon, out ToolTip tt)
342 IntPtr SystrayMgrWindow;
344 Xlib.XGrabServer (display);
345 SystrayMgrWindow = Xlib.XGetSelectionOwner (display, Atoms._NET_SYSTEM_TRAY_S);
346 Xlib.XUngrabServer (display);
348 if (SystrayMgrWindow != IntPtr.Zero) {
349 XSizeHints size_hints;
352 hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
354 Console.WriteLine("Adding Systray Whole:{0:X}, Client:{1:X}",
355 hwnd.WholeWindow.ToInt32(), hwnd.ClientWindow.ToInt32());
359 if (hwnd.ClientWindow != hwnd.WholeWindow) {
360 Xlib.XDestroyWindow (display, hwnd.ClientWindow);
361 hwnd.ClientWindow = hwnd.WholeWindow;
366 /* by virtue of the way the tests are ordered when determining if it's PAINT
367 or NCPAINT, ClientWindow == WholeWindow will always be PAINT. So, if we're
368 waiting on an nc_expose, drop it and remove the hwnd from the list (unless
369 there's a pending expose). */
370 hwnd.PendingNCExpose = false;
373 hwnd.Queue.Unlock ();
377 size_hints = new XSizeHints();
379 size_hints.flags = (IntPtr)(XSizeHintsFlags.PMinSize | XSizeHintsFlags.PMaxSize | XSizeHintsFlags.PBaseSize);
381 size_hints.min_width = 24;
382 size_hints.min_height = 24;
383 size_hints.max_width = 24;
384 size_hints.max_height = 24;
385 size_hints.base_width = 24;
386 size_hints.base_height = 24;
388 Xlib.XSetWMNormalHints (display, hwnd.WholeWindow, ref size_hints);
390 int[] atoms = new int[2];
391 atoms [0] = 1; // Version 1
392 atoms [1] = 1; // we want to be mapped
394 // This line cost me 3 days...
395 Xlib.XChangeProperty (display,
396 hwnd.WholeWindow, Atoms._XEMBED_INFO, Atoms._XEMBED_INFO, 32,
397 PropertyMode.Replace, atoms, 2);
399 // Need to pick some reasonable defaults
401 tt.AutomaticDelay = 100;
402 tt.InitialDelay = 250;
403 tt.ReshowDelay = 250;
404 tt.ShowAlways = true;
406 if ((tip != null) && (tip != string.Empty)) {
407 tt.SetToolTip(Control.FromHandle(handle), tip);
413 SendNetClientMessage (SystrayMgrWindow,
414 Atoms._NET_SYSTEM_TRAY_OPCODE,
416 (IntPtr)SystrayRequest.SYSTEM_TRAY_REQUEST_DOCK,
426 public bool SystrayChange (IntPtr handle, string tip, Icon icon, ref ToolTip tt)
430 control = Control.FromHandle(handle);
431 if (control != null && tt != null) {
432 tt.SetToolTip(control, tip);
440 public void SystrayRemove(IntPtr handle, ref ToolTip tt)
442 #if GTKSOCKET_SUPPORTS_REPARENTING
445 hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
447 /* in the XEMBED spec, it mentions 3 ways for a client window to break the protocol with the embedder.
448 * 1. The embedder can unmap the window and reparent to the root window (we should probably handle this...)
449 * 2. The client can reparent its window out of the embedder window.
450 * 3. The client can destroy its window.
452 * this call to SetParent is case 2, but in
453 * the spec it also mentions that gtk doesn't
454 * support this at present. Looking at HEAD
455 * gtksocket-x11.c jives with this statement.
457 * so we can't reparent. we have to destroy.
459 SetParent(hwnd.WholeWindow, FosterParent);
461 Control control = Control.FromHandle(handle);
462 if (control is NotifyIcon.NotifyIconWindow)
463 ((NotifyIcon.NotifyIconWindow)control).InternalRecreateHandle ();
466 // The caller can now re-dock it later...
473 public void ResetMouseHover (X11Hwnd hovering)
475 HoverState.Timer.Enabled = hovering != null;
476 HoverState.X = MousePosition.X;
477 HoverState.Y = MousePosition.Y;
478 HoverState.Window = hovering == null ? IntPtr.Zero : hovering.Handle;
481 public void ShowCursor (bool show)
483 ; // FIXME - X11 doesn't 'hide' the cursor. we could create an empty cursor
486 public void SetModal (X11Hwnd hwnd, bool Modal)
489 ModalWindows.Push(hwnd);
491 // XXX do we need to pop until the
492 // hwnd is off the stack? or just the
493 // most recently pushed hwnd?
494 if (ModalWindows.Contains(hwnd)) {
498 if (ModalWindows.Count > 0) {
499 X11Hwnd top_hwnd = (X11Hwnd)ModalWindows.Peek();
505 public bool SupportsTransparency ()
507 // compiz adds _NET_WM_WINDOW_OPACITY to _NET_SUPPORTED on the root window, check for that
508 return ((IList)root_hwnd._NET_SUPPORTED).Contains (Atoms._NET_WM_WINDOW_OPACITY);
511 public void SendAsyncMethod (AsyncMethodData method)
513 X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(method.Handle);
514 XEvent xevent = new XEvent ();
516 xevent.type = XEventName.ClientMessage;
517 xevent.ClientMessageEvent.display = display;
518 xevent.ClientMessageEvent.window = method.Handle;
519 xevent.ClientMessageEvent.message_type = Atoms.AsyncAtom;
520 xevent.ClientMessageEvent.format = 32;
521 xevent.ClientMessageEvent.ptr1 = (IntPtr) GCHandle.Alloc (method);
523 hwnd.Queue.Enqueue (xevent);
526 delegate IntPtr WndProcDelegate (IntPtr hwnd, Msg message, IntPtr wParam, IntPtr lParam);
528 public IntPtr SendMessage (IntPtr handle, Msg message, IntPtr wParam, IntPtr lParam)
530 X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
534 if (hwnd.Queue.Thread != Thread.CurrentThread) {
535 AsyncMethodResult result;
536 AsyncMethodData data;
538 result = new AsyncMethodResult ();
539 data = new AsyncMethodData ();
541 data.Handle = hwnd.Handle;
542 data.Method = new WndProcDelegate (NativeWindow.WndProc);
543 data.Args = new object[] { hwnd.Handle, message, wParam, lParam };
544 data.Result = result;
546 SendAsyncMethod (data);
547 #if DriverDebug || DriverDebugThreads
548 Console.WriteLine ("Sending {0} message across.", message);
554 return NativeWindow.WndProc (hwnd.Handle, message, wParam, lParam);
558 // FIXME - I think this should just enqueue directly
559 public bool PostMessage (IntPtr handle, Msg message, IntPtr wparam, IntPtr lparam)
561 XEvent xevent = new XEvent ();
562 X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
564 xevent.type = XEventName.ClientMessage;
565 xevent.ClientMessageEvent.display = display;
568 xevent.ClientMessageEvent.window = hwnd.WholeWindow;
570 xevent.ClientMessageEvent.window = IntPtr.Zero;
573 xevent.ClientMessageEvent.message_type = Atoms.PostAtom;
574 xevent.ClientMessageEvent.format = 32;
575 xevent.ClientMessageEvent.ptr1 = handle;
576 xevent.ClientMessageEvent.ptr2 = (IntPtr) message;
577 xevent.ClientMessageEvent.ptr3 = wparam;
578 xevent.ClientMessageEvent.ptr4 = lparam;
580 hwnd.Queue.Enqueue (xevent);
585 public void SendNetWMMessage (IntPtr window, IntPtr message_type, IntPtr l0, IntPtr l1, IntPtr l2)
590 xev.ClientMessageEvent.type = XEventName.ClientMessage;
591 xev.ClientMessageEvent.send_event = true;
592 xev.ClientMessageEvent.window = window;
593 xev.ClientMessageEvent.message_type = message_type;
594 xev.ClientMessageEvent.format = 32;
595 xev.ClientMessageEvent.ptr1 = l0;
596 xev.ClientMessageEvent.ptr2 = l1;
597 xev.ClientMessageEvent.ptr3 = l2;
599 Xlib.XSendEvent (display, root_hwnd.Handle, false,
600 new IntPtr ((int) (EventMask.SubstructureRedirectMask | EventMask.SubstructureNotifyMask)), ref xev);
603 public void SendNetClientMessage (IntPtr window, IntPtr message_type, IntPtr l0, IntPtr l1, IntPtr l2)
608 xev.ClientMessageEvent.type = XEventName.ClientMessage;
609 xev.ClientMessageEvent.send_event = true;
610 xev.ClientMessageEvent.window = window;
611 xev.ClientMessageEvent.message_type = message_type;
612 xev.ClientMessageEvent.format = 32;
613 xev.ClientMessageEvent.ptr1 = l0;
614 xev.ClientMessageEvent.ptr2 = l1;
615 xev.ClientMessageEvent.ptr3 = l2;
617 Xlib.XSendEvent (display, window, false, new IntPtr ((int)EventMask.NoEventMask), ref xev);
620 public bool TranslateMessage (ref MSG msg)
622 return Keyboard.TranslateMessage (ref msg);
625 public IntPtr DispatchMessage (ref MSG msg)
627 return NativeWindow.WndProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
630 private void QueryPointer (IntPtr w, out IntPtr root, out IntPtr child,
631 out int root_x, out int root_y, out int child_x, out int child_y,
634 /* this code was written with the help of
635 glance at gdk. I never would have realized we
636 needed a loop in order to traverse down in the
637 hierarchy. I would have assumed you'd get the
638 most deeply nested child and have to do
639 XQueryTree to move back up the hierarchy..
640 stupid me, of course. */
643 Xlib.XGrabServer (display);
645 Xlib.XQueryPointer (display, w, out root, out c,
646 out root_x, out root_y, out child_x, out child_y,
652 IntPtr child_last = IntPtr.Zero;
653 while (c != IntPtr.Zero) {
655 Xlib.XQueryPointer (display, c, out root, out c,
656 out root_x, out root_y, out child_x, out child_y,
660 Xlib.XUngrabServer (display);
665 public void SetCursorPos (int x, int y)
668 int root_x, root_y, child_x, child_y, mask;
671 * QueryPointer before warping
672 * because if the warp is on
673 * the RootWindow, the x/y are
674 * relative to the current
677 QueryPointer (RootWindow.Handle,
680 out root_x, out root_y,
681 out child_x, out child_y,
684 Xlib.XWarpPointer (display, IntPtr.Zero, IntPtr.Zero, 0, 0, 0, 0, x - root_x, y - root_y);
686 Xlib.XFlush (display);
689 * QueryPointer after warping
690 * to manually generate a
691 * motion event for the window
694 QueryPointer (RootWindow.Handle,
697 out root_x, out root_y,
698 out child_x, out child_y,
701 X11Hwnd child_hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(child);
702 if (child_hwnd == null)
705 XEvent xevent = new XEvent ();
707 xevent.type = XEventName.MotionNotify;
708 xevent.MotionEvent.display = display;
709 xevent.MotionEvent.window = child_hwnd.Handle;
710 xevent.MotionEvent.root = RootWindow.Handle;
711 xevent.MotionEvent.x = child_x;
712 xevent.MotionEvent.y = child_y;
713 xevent.MotionEvent.x_root = root_x;
714 xevent.MotionEvent.y_root = root_y;
715 xevent.MotionEvent.state = mask;
717 child_hwnd.Queue.Enqueue (xevent);
720 public void SetFocus (X11Hwnd new_focus)
722 if (new_focus == FocusWindow)
725 X11Hwnd prev_focus = FocusWindow;
726 FocusWindow = new_focus;
728 if (prev_focus != null)
729 SendMessage (prev_focus.Handle, Msg.WM_KILLFOCUS,
730 FocusWindow == null ? IntPtr.Zero : FocusWindow.Handle, IntPtr.Zero);
731 if (FocusWindow != null)
732 SendMessage (FocusWindow.Handle, Msg.WM_SETFOCUS,
733 prev_focus == null ? IntPtr.Zero : prev_focus.Handle, IntPtr.Zero);
735 //XSetInputFocus(DisplayHandle, Hwnd.ObjectFromHandle(handle).ClientWindow, RevertTo.None, IntPtr.Zero);
738 public IntPtr DefineCursor (Bitmap bitmap, Bitmap mask, Color cursor_pixel, Color mask_pixel, int xHotSpot, int yHotSpot)
741 Bitmap cursor_bitmap;
749 IntPtr cursor_pixmap;
756 if (Xlib.XQueryBestCursor (display, RootWindow.Handle, bitmap.Width, bitmap.Height, out width, out height) == 0) {
760 // Win32 only allows creation cursors of a certain size
761 if ((bitmap.Width != width) || (bitmap.Width != height)) {
762 cursor_bitmap = new Bitmap(bitmap, new Size(width, height));
763 cursor_mask = new Bitmap(mask, new Size(width, height));
765 cursor_bitmap = bitmap;
769 width = cursor_bitmap.Width;
770 height = cursor_bitmap.Height;
772 cursor_bits = new Byte[(width / 8) * height];
773 mask_bits = new Byte[(width / 8) * height];
775 for (int y = 0; y < height; y++) {
776 for (int x = 0; x < width; x++) {
777 c_pixel = cursor_bitmap.GetPixel(x, y);
778 m_pixel = cursor_mask.GetPixel(x, y);
780 and = c_pixel == cursor_pixel;
781 xor = m_pixel == mask_pixel;
785 // cursor_bits[y * width / 8 + x / 8] &= (byte)~((1 << (x % 8))); // The bit already is 0
786 mask_bits[y * width / 8 + x / 8] |= (byte)(1 << (x % 8));
787 } else if (and && !xor) {
789 cursor_bits[y * width / 8 + x / 8] |= (byte)(1 << (x % 8));
790 mask_bits[y * width / 8 + x / 8] |= (byte)(1 << (x % 8));
792 } else if (and && !xor) {
794 } else if (and && xor) {
797 // X11 doesn't know the 'reverse screen' concept, so we'll treat them the same
798 // we want both to be 0 so nothing to be done
799 //cursor_bits[y * width / 8 + x / 8] &= (byte)~((1 << (x % 8)));
800 //mask_bits[y * width / 8 + x / 8] |= (byte)(01 << (x % 8));
806 cursor_pixmap = Xlib.XCreatePixmapFromBitmapData (display, RootWindow.Handle,
807 cursor_bits, width, height, (IntPtr)1, (IntPtr)0, 1);
808 mask_pixmap = Xlib.XCreatePixmapFromBitmapData (display, RootWindow.Handle,
809 mask_bits, width, height, (IntPtr)1, (IntPtr)0, 1);
813 fg.pixel = Xlib.XWhitePixel (display, DefaultScreen);
814 fg.red = (ushort)65535;
815 fg.green = (ushort)65535;
816 fg.blue = (ushort)65535;
818 bg.pixel = Xlib.XBlackPixel (display, DefaultScreen);
820 cursor = Xlib.XCreatePixmapCursor (display, cursor_pixmap, mask_pixmap, ref fg, ref bg, xHotSpot, yHotSpot);
822 Xlib.XFreePixmap (display, cursor_pixmap);
823 Xlib.XFreePixmap (display, mask_pixmap);
828 public IntPtr DefineStdCursor (StdCursor id)
830 CursorFontShape shape;
832 // FIXME - define missing shapes
835 case StdCursor.AppStarting:
836 shape = CursorFontShape.XC_watch;
839 case StdCursor.Arrow:
840 shape = CursorFontShape.XC_top_left_arrow;
843 case StdCursor.Cross:
844 shape = CursorFontShape.XC_crosshair;
847 case StdCursor.Default:
848 shape = CursorFontShape.XC_top_left_arrow;
852 shape = CursorFontShape.XC_hand1;
856 shape = CursorFontShape.XC_question_arrow;
859 case StdCursor.HSplit:
860 shape = CursorFontShape.XC_sb_v_double_arrow;
863 case StdCursor.IBeam:
864 shape = CursorFontShape.XC_xterm;
868 shape = CursorFontShape.XC_circle;
871 case StdCursor.NoMove2D:
872 shape = CursorFontShape.XC_fleur;
875 case StdCursor.NoMoveHoriz:
876 shape = CursorFontShape.XC_fleur;
879 case StdCursor.NoMoveVert:
880 shape = CursorFontShape.XC_fleur;
883 case StdCursor.PanEast:
884 shape = CursorFontShape.XC_fleur;
887 case StdCursor.PanNE:
888 shape = CursorFontShape.XC_fleur;
891 case StdCursor.PanNorth:
892 shape = CursorFontShape.XC_fleur;
895 case StdCursor.PanNW:
896 shape = CursorFontShape.XC_fleur;
899 case StdCursor.PanSE:
900 shape = CursorFontShape.XC_fleur;
903 case StdCursor.PanSouth:
904 shape = CursorFontShape.XC_fleur;
907 case StdCursor.PanSW:
908 shape = CursorFontShape.XC_fleur;
911 case StdCursor.PanWest:
912 shape = CursorFontShape.XC_sizing;
915 case StdCursor.SizeAll:
916 shape = CursorFontShape.XC_fleur;
919 case StdCursor.SizeNESW:
920 shape = CursorFontShape.XC_top_right_corner;
923 case StdCursor.SizeNS:
924 shape = CursorFontShape.XC_sb_v_double_arrow;
927 case StdCursor.SizeNWSE:
928 shape = CursorFontShape.XC_top_left_corner;
931 case StdCursor.SizeWE:
932 shape = CursorFontShape.XC_sb_h_double_arrow;
935 case StdCursor.UpArrow:
936 shape = CursorFontShape.XC_center_ptr;
939 case StdCursor.VSplit:
940 shape = CursorFontShape.XC_sb_h_double_arrow;
943 case StdCursor.WaitCursor:
944 shape = CursorFontShape.XC_watch;
951 return Xlib.XCreateFontCursor (display, shape);
954 // XXX this should take an X11Hwnd.
955 public void CreateCaret (IntPtr handle, int width, int height)
960 hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
962 if (Caret.Hwnd != IntPtr.Zero)
963 DestroyCaret(Caret.Hwnd);
966 Caret.Window = hwnd.ClientWindow;
968 Caret.Height = height;
969 Caret.Visible = false;
972 gc_values = new XGCValues();
973 gc_values.line_width = width;
975 Caret.gc = Xlib.XCreateGC (display, Caret.Window, new IntPtr ((int)GCFunction.GCLineWidth), ref gc_values);
976 if (Caret.gc == IntPtr.Zero) {
977 Caret.Hwnd = IntPtr.Zero;
981 Xlib.XSetFunction (display, Caret.gc, GXFunction.GXinvert);
985 // XXX this should take an X11Hwnd.
986 public void DestroyCaret (IntPtr handle)
988 if (Caret.Hwnd == handle) {
989 if (Caret.Visible == true) {
992 if (Caret.gc != IntPtr.Zero) {
993 Xlib.XFreeGC (display, Caret.gc);
994 Caret.gc = IntPtr.Zero;
996 Caret.Hwnd = IntPtr.Zero;
997 Caret.Visible = false;
1002 public void SetCaretPos (IntPtr handle, int x, int y)
1004 if (Caret.Hwnd == handle) {
1011 if (Caret.Visible == true) {
1013 Caret.Timer.Start();
1018 public void DestroyCursor (IntPtr cursor)
1020 Xlib.XFreeCursor (display, cursor);
1023 private void AccumulateDestroyedHandles (Control c, ArrayList list)
1026 Control[] controls = c.child_controls.GetAllControls ();
1028 if (c.IsHandleCreated && !c.IsDisposed) {
1029 X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(c.Handle);
1031 #if DriverDebug || DriverDebugDestroy
1032 Console.WriteLine (" + adding {0} to the list of zombie windows", XplatUI.Window (hwnd.Handle));
1033 Console.WriteLine (" + parent X window is {0:X}", XGetParent (hwnd.WholeWindow).ToInt32());
1037 CleanupCachedWindows (hwnd);
1041 for (int i = 0; i < controls.Length; i ++) {
1042 AccumulateDestroyedHandles (controls[i], list);
1048 void CleanupCachedWindows (X11Hwnd hwnd)
1050 if (ActiveWindow == hwnd) {
1051 SendMessage (hwnd.ClientWindow, Msg.WM_ACTIVATE, (IntPtr)WindowActiveFlags.WA_INACTIVE, IntPtr.Zero);
1052 ActiveWindow = null;
1055 if (FocusWindow == hwnd) {
1056 SendMessage (hwnd.ClientWindow, Msg.WM_KILLFOCUS, IntPtr.Zero, IntPtr.Zero);
1060 if (Grab.Hwnd == hwnd.Handle) {
1061 Grab.Hwnd = IntPtr.Zero;
1062 Grab.Confined = false;
1065 DestroyCaret (hwnd.Handle);
1069 public void DestroyWindow (X11Hwnd hwnd)
1071 CleanupCachedWindows (hwnd);
1073 ArrayList windows = new ArrayList ();
1075 AccumulateDestroyedHandles (Control.ControlNativeWindow.ControlFromHandle(hwnd.Handle), windows);
1077 hwnd.DestroyWindow ();
1079 foreach (X11Hwnd h in windows) {
1080 SendMessage (h.Handle, Msg.WM_DESTROY, IntPtr.Zero, IntPtr.Zero);
1084 public X11Hwnd GetActiveWindow ()
1090 IntPtr prop = IntPtr.Zero;
1091 IntPtr active = IntPtr.Zero;
1093 Xlib.XGetWindowProperty (display, RootWindow.Handle,
1094 Atoms._NET_ACTIVE_WINDOW, IntPtr.Zero, new IntPtr (1), false,
1095 Atoms.XA_WINDOW, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop);
1097 if (((long)nitems > 0) && (prop != IntPtr.Zero)) {
1098 active = (IntPtr)Marshal.ReadInt32(prop);
1102 return (X11Hwnd)Hwnd.GetObjectFromWindow(active);
1105 public void SetActiveWindow (X11Hwnd new_active_window)
1107 if (new_active_window != ActiveWindow) {
1108 if (ActiveWindow != null)
1109 PostMessage (ActiveWindow.Handle, Msg.WM_ACTIVATE,
1110 (IntPtr)WindowActiveFlags.WA_INACTIVE, IntPtr.Zero);
1112 ActiveWindow = new_active_window;
1114 if (ActiveWindow != null)
1115 PostMessage (ActiveWindow.Handle, Msg.WM_ACTIVATE,
1116 (IntPtr)WindowActiveFlags.WA_ACTIVE, IntPtr.Zero);
1119 if (ModalWindows.Count > 0) {
1120 // Modality handling, if we are modal and the new active window is one
1121 // of ours but not the modal one, switch back to the modal window
1123 if (ActiveWindow != null &&
1124 NativeWindow.FindWindow (ActiveWindow.Handle) != null) {
1125 if (ActiveWindow != (X11Hwnd)ModalWindows.Peek())
1126 ((X11Hwnd)ModalWindows.Peek()).Activate ();
1131 public void GetDisplaySize (out Size size)
1133 XWindowAttributes attributes = new XWindowAttributes();
1135 // FIXME - use _NET_WM messages instead?
1136 Xlib.XGetWindowAttributes (display, RootWindow.Handle, ref attributes);
1138 size = new Size(attributes.width, attributes.height);
1141 // XXX this method doesn't really fit well anywhere in the backend
1142 public SizeF GetAutoScaleSize (Font font)
1146 string magic_string = "The quick brown fox jumped over the lazy dog.";
1147 double magic_number = 44.549996948242189; // XXX my god, where did this number come from?
1149 g = Graphics.FromHwnd (FosterParent.Handle);
1151 width = (float) (g.MeasureString (magic_string, font).Width / magic_number);
1152 return new SizeF(width, font.Height);
1155 public void GetCursorPos (X11Hwnd hwnd, out int x, out int y)
1167 use_handle = hwnd.Handle;
1169 use_handle = RootWindow.Handle;
1171 QueryPointer (use_handle, out root, out child, out root_x, out root_y, out win_x, out win_y, out keys_buttons);
1182 public IntPtr GetFocus ()
1184 return FocusWindow.Handle;
1187 public IntPtr GetMousewParam (int Delta)
1191 if ((MouseState & MouseButtons.Left) != 0) {
1192 result |= (int)MsgButtons.MK_LBUTTON;
1195 if ((MouseState & MouseButtons.Middle) != 0) {
1196 result |= (int)MsgButtons.MK_MBUTTON;
1199 if ((MouseState & MouseButtons.Right) != 0) {
1200 result |= (int)MsgButtons.MK_RBUTTON;
1203 Keys mods = ModifierKeys;
1204 if ((mods & Keys.Control) != 0) {
1205 result |= (int)MsgButtons.MK_CONTROL;
1208 if ((mods & Keys.Shift) != 0) {
1209 result |= (int)MsgButtons.MK_SHIFT;
1212 result |= Delta << 16;
1214 return (IntPtr)result;
1217 public void GrabInfo (out IntPtr handle, out bool GrabConfined, out Rectangle GrabArea)
1220 GrabConfined = Grab.Confined;
1221 GrabArea = Grab.Area;
1224 public void GrabWindow (X11Hwnd hwnd, X11Hwnd confine_to)
1226 IntPtr confine_to_window;
1228 confine_to_window = IntPtr.Zero;
1230 if (confine_to != null) {
1231 Console.WriteLine (Environment.StackTrace);
1233 XWindowAttributes attributes = new XWindowAttributes();
1235 Xlib.XGetWindowAttributes (display, confine_to.ClientWindow, ref attributes);
1237 Grab.Area.X = attributes.x;
1238 Grab.Area.Y = attributes.y;
1239 Grab.Area.Width = attributes.width;
1240 Grab.Area.Height = attributes.height;
1241 Grab.Confined = true;
1242 confine_to_window = confine_to.ClientWindow;
1245 Grab.Hwnd = hwnd.ClientWindow;
1247 Xlib.XGrabPointer (display, hwnd.ClientWindow, false,
1248 EventMask.ButtonPressMask | EventMask.ButtonMotionMask |
1249 EventMask.ButtonReleaseMask | EventMask.PointerMotionMask,
1250 GrabMode.GrabModeAsync, GrabMode.GrabModeAsync, confine_to_window, IntPtr.Zero, IntPtr.Zero);
1253 public void UngrabWindow (X11Hwnd hwnd)
1255 Xlib.XUngrabPointer (display, IntPtr.Zero);
1256 Xlib.XFlush (display);
1258 // XXX make sure hwnd is what should have the grab and throw if not
1259 Grab.Hwnd = IntPtr.Zero;
1260 Grab.Confined = false;
1263 private void TranslatePropertyToClipboard (IntPtr property)
1269 IntPtr prop = IntPtr.Zero;
1271 Clipboard.Item = null;
1273 Xlib.XGetWindowProperty (display, FosterParent.Handle,
1274 property, IntPtr.Zero, new IntPtr (0x7fffffff), true,
1275 Atoms.AnyPropertyType, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop);
1277 if ((long)nitems > 0) {
1278 if (property == Atoms.XA_STRING) {
1279 Clipboard.Item = Marshal.PtrToStringAnsi(prop);
1280 } else if (property == Atoms.XA_BITMAP) {
1281 // FIXME - convert bitmap to image
1282 } else if (property == Atoms.XA_PIXMAP) {
1283 // FIXME - convert pixmap to image
1284 } else if (property == Atoms.OEMTEXT) {
1285 Clipboard.Item = Marshal.PtrToStringAnsi(prop);
1286 } else if (property == Atoms.UNICODETEXT) {
1287 Clipboard.Item = Marshal.PtrToStringAnsi(prop);
1294 // XXX should we be using @handle instead of Atoms.CLIPBOARD here?
1295 public int[] ClipboardAvailableFormats (IntPtr handle)
1297 // XXX deal with the updatemessagequeue stuff
1301 DataFormats.Format f;
1304 f = DataFormats.Format.List;
1306 if (Xlib.XGetSelectionOwner (display, Atoms.CLIPBOARD) == IntPtr.Zero) {
1310 Clipboard.Formats = new ArrayList();
1313 Xlib.XConvertSelection (display, Atoms.CLIPBOARD, (IntPtr)f.Id, (IntPtr)f.Id, FosterParent.Handle, IntPtr.Zero);
1315 Clipboard.Enumerating = true;
1316 while (Clipboard.Enumerating) {
1317 UpdateMessageQueue(null);
1322 result = new int[Clipboard.Formats.Count];
1324 for (int i = 0; i < Clipboard.Formats.Count; i++) {
1325 result[i] = ((IntPtr)Clipboard.Formats[i]).ToInt32 ();
1328 Clipboard.Formats = null;
1333 public void ClipboardClose (IntPtr handle)
1335 if (handle != ClipMagic) {
1336 throw new ArgumentException("handle is not a valid clipboard handle");
1341 public int ClipboardGetID (IntPtr handle, string format)
1343 if (handle != ClipMagic) {
1344 throw new ArgumentException("handle is not a valid clipboard handle");
1347 if (format == "Text" ) return Atoms.XA_STRING.ToInt32();
1348 else if (format == "Bitmap" ) return Atoms.XA_BITMAP.ToInt32();
1349 //else if (format == "MetaFilePict" ) return 3;
1350 //else if (format == "SymbolicLink" ) return 4;
1351 //else if (format == "DataInterchangeFormat" ) return 5;
1352 //else if (format == "Tiff" ) return 6;
1353 else if (format == "OEMText" ) return Atoms.OEMTEXT.ToInt32();
1354 else if (format == "DeviceIndependentBitmap" ) return Atoms.XA_PIXMAP.ToInt32();
1355 else if (format == "Palette" ) return Atoms.XA_COLORMAP.ToInt32(); // Useless
1356 //else if (format == "PenData" ) return 10;
1357 //else if (format == "RiffAudio" ) return 11;
1358 //else if (format == "WaveAudio" ) return 12;
1359 else if (format == "UnicodeText" ) return Atoms.UNICODETEXT.ToInt32();
1360 //else if (format == "EnhancedMetafile" ) return 14;
1361 //else if (format == "FileDrop" ) return 15;
1362 //else if (format == "Locale" ) return 16;
1364 return Xlib.XInternAtom (display, format, false).ToInt32();
1367 public IntPtr ClipboardOpen (bool primary_selection)
1369 if (!primary_selection)
1370 ClipMagic = Atoms.CLIPBOARD;
1372 ClipMagic = Atoms.PRIMARY;
1378 public object ClipboardRetrieve (IntPtr handle, int type, XplatUI.ClipboardToObject converter)
1380 // XXX deal with the UpdateMessageQueue stuff
1384 Xlib.XConvertSelection (display, handle, (IntPtr)type, (IntPtr)type, FosterParent, IntPtr.Zero);
1386 Clipboard.Retrieving = true;
1387 while (Clipboard.Retrieving) {
1388 UpdateMessageQueue(null);
1391 return Clipboard.Item;
1395 public void ClipboardStore (IntPtr handle, object obj, int type, XplatUI.ObjectToClipboard converter)
1397 Clipboard.Item = obj;
1398 Clipboard.Type = type;
1399 Clipboard.Converter = converter;
1402 Xlib.XSetSelectionOwner (display, Atoms.CLIPBOARD, FosterParent.Handle, IntPtr.Zero);
1404 // Clearing the selection
1405 Xlib.XSetSelectionOwner (display, Atoms.CLIPBOARD, IntPtr.Zero, IntPtr.Zero);
1410 public PaintEventArgs PaintEventStart (IntPtr handle, bool client)
1412 X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
1414 if (Caret.Visible == true) {
1415 Caret.Paused = true;
1419 return hwnd.PaintEventStart (client);
1422 public void PaintEventEnd (IntPtr handle, bool client)
1424 X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
1426 hwnd.PaintEventEnd (client);
1428 if (Caret.Visible == true) {
1430 Caret.Paused = false;
1434 public void SetCursor (IntPtr handle, IntPtr cursor)
1438 if (OverrideCursorHandle == IntPtr.Zero) {
1439 if ((LastCursorWindow == handle) && (LastCursorHandle == cursor))
1442 LastCursorHandle = cursor;
1443 LastCursorWindow = handle;
1445 hwnd = Hwnd.ObjectFromHandle(handle);
1446 if (cursor != IntPtr.Zero)
1447 Xlib.XDefineCursor (display, hwnd.whole_window, cursor);
1449 Xlib.XUndefineCursor (display, hwnd.whole_window);
1450 Xlib.XFlush (display);
1453 hwnd = Hwnd.ObjectFromHandle(handle);
1454 Xlib.XDefineCursor (display, hwnd.whole_window, OverrideCursorHandle);
1458 public DragDropEffects StartDrag (IntPtr handle, object data,
1459 DragDropEffects allowed_effects)
1461 X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle (handle);
1464 throw new ArgumentException ("Attempt to begin drag from invalid window handle (" + handle.ToInt32 () + ").");
1466 return Dnd.StartDrag (hwnd.ClientWindow, data, allowed_effects);
1469 public X11Atoms Atoms {
1470 get { return atoms; }
1473 public int CurrentTimestamp {
1475 TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1));
1477 return (int) t.TotalSeconds;
1481 public Size CursorSize {
1486 if (Xlib.XQueryBestCursor (display, RootWindow.Handle, 32, 32, out x, out y) != 0) {
1487 return new Size (x, y);
1489 return new Size (16, 16);
1494 public IntPtr Handle {
1495 get { return display; }
1498 public Size IconSize {
1504 if (Xlib.XGetIconSizes (display, RootWindow.Handle, out list, out count) != 0) {
1508 current = (long)list;
1511 size = new XIconSize();
1513 for (int i = 0; i < count; i++) {
1514 size = (XIconSize)Marshal.PtrToStructure((IntPtr)current, size.GetType());
1515 current += Marshal.SizeOf(size);
1517 // Look for our preferred size
1518 if (size.min_width == 32) {
1520 return new Size(32, 32);
1523 if (size.max_width == 32) {
1525 return new Size(32, 32);
1528 if (size.min_width < 32 && size.max_width > 32) {
1531 // check if we can fit one
1533 while (x < size.max_width) {
1534 x += size.width_inc;
1537 return new Size(32, 32);
1542 if (largest < size.max_width) {
1543 largest = size.max_width;
1547 // We didn't find a match or we wouldn't be here
1548 return new Size(largest, largest);
1551 return new Size(32, 32);
1556 public int KeyboardSpeed {
1559 // A lot harder: need to do:
1560 // XkbQueryExtension(0x08051008, 0xbfffdf4c, 0xbfffdf50, 0xbfffdf54, 0xbfffdf58) = 1
1561 // XkbAllocKeyboard(0x08051008, 0xbfffdf4c, 0xbfffdf50, 0xbfffdf54, 0xbfffdf58) = 0x080517a8
1562 // XkbGetControls(0x08051008, 1, 0x080517a8, 0xbfffdf54, 0xbfffdf58) = 0
1564 // And from that we can tell the repetition rate
1566 // Notice, the values must map to:
1567 // [0, 31] which maps to 2.5 to 30 repetitions per second.
1573 public int KeyboardDelay {
1576 // Return values must range from 0 to 4, 0 meaning 250ms,
1577 // and 4 meaning 1000 ms.
1579 return 1; // ie, 500 ms
1583 public int DefaultScreen {
1584 get { return Xlib.XDefaultScreen (display); }
1587 public IntPtr DefaultColormap {
1589 get { return Xlib.XDefaultColormap (display, DefaultScreen); }
1592 public Keys ModifierKeys {
1593 get { return Keyboard.ModifierKeys; }
1596 public IntPtr OverrideCursor {
1597 get { return OverrideCursorHandle; }
1598 set { OverrideCursorHandle = value; }
1601 public X11RootHwnd RootWindow {
1602 get { return root_hwnd; }
1605 public Size SmallIconSize {
1611 if (Xlib.XGetIconSizes (display, RootWindow.Handle, out list, out count) != 0) {
1615 current = (long)list;
1618 size = new XIconSize();
1620 for (int i = 0; i < count; i++) {
1621 size = (XIconSize)Marshal.PtrToStructure((IntPtr)current, size.GetType());
1622 current += Marshal.SizeOf(size);
1624 // Look for our preferred size
1625 if (size.min_width == 16) {
1627 return new Size(16, 16);
1630 if (size.max_width == 16) {
1632 return new Size(16, 16);
1635 if (size.min_width < 16 && size.max_width > 16) {
1638 // check if we can fit one
1640 while (x < size.max_width) {
1641 x += size.width_inc;
1644 return new Size(16, 16);
1649 if (smallest == 0 || smallest > size.min_width) {
1650 smallest = size.min_width;
1654 // We didn't find a match or we wouldn't be here
1655 return new Size(smallest, smallest);
1658 return new Size(16, 16);
1663 public X11Hwnd FosterParent {
1664 get { return foster_hwnd; }
1667 public int MouseHoverTime {
1668 get { return HoverState.Interval; }
1671 public Rectangle WorkingArea {
1677 IntPtr prop = IntPtr.Zero;
1680 int current_desktop;
1684 Xlib.XGetWindowProperty (display, RootWindow.Handle,
1685 Atoms._NET_CURRENT_DESKTOP, IntPtr.Zero, new IntPtr(1), false, Atoms.XA_CARDINAL,
1686 out actual_atom, out actual_format, out nitems, out bytes_after, ref prop);
1688 if ((long)nitems < 1) {
1692 current_desktop = Marshal.ReadIntPtr(prop, 0).ToInt32();
1695 Xlib.XGetWindowProperty (display, RootWindow.Handle,
1696 Atoms._NET_WORKAREA, IntPtr.Zero, new IntPtr (256), false, Atoms.XA_CARDINAL,
1697 out actual_atom, out actual_format, out nitems, out bytes_after, ref prop);
1699 if ((long)nitems < 4 * current_desktop) {
1703 x = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop).ToInt32();
1704 y = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop + IntPtr.Size).ToInt32();
1705 width = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop + IntPtr.Size * 2).ToInt32();
1706 height = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop + IntPtr.Size * 3).ToInt32();
1709 return new Rectangle(x, y, width, height);
1712 XWindowAttributes attributes = new XWindowAttributes();
1714 Xlib.XGetWindowAttributes (display, RootWindow.Handle, ref attributes);
1716 return new Rectangle(0, 0, attributes.width, attributes.height);
1720 private void XEventThread ()
1724 Syscall.poll (pollfds, 1U, -1);
1726 while (Xlib.XPending (display) > 0) {
1728 XEvent xevent = new XEvent ();
1729 Xlib.XNextEvent (display, ref xevent);
1731 X11Hwnd hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow(xevent.AnyEvent.window);
1733 hwnd.EnqueueEvent (xevent);
1740 private void RedirectMsgToEnabledAncestor (X11Hwnd hwnd, MSG msg, IntPtr window,
1741 ref int event_x, ref int event_y)
1746 msg.hwnd = hwnd.EnabledHwnd;
1747 Xlib.XTranslateCoordinates (display, window,
1748 Hwnd.ObjectFromHandle(msg.hwnd).ClientWindow,
1750 out x, out y, out dummy);
1753 msg.lParam = (IntPtr)(MousePosition.Y << 16 | MousePosition.X);
1757 // This is called from the thread owning the corresponding X11ThreadQueue
1758 [MonoTODO("Implement filtering")]
1759 public bool GetMessage (object queue_id, ref MSG msg, IntPtr handle, int wFilterMin, int wFilterMax)
1761 X11ThreadQueue queue = (X11ThreadQueue)queue_id;
1769 got_xevent = queue.Dequeue (out xevent);
1772 hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (xevent.AnyEvent.window);
1774 // Handle messages for windows that are already or are about to be destroyed.
1776 // we need a special block for this because unless we remove the hwnd from the paint
1777 // queue it will always stay there (since we don't handle the expose), and we'll
1778 // effectively loop infinitely trying to repaint a non-existant window.
1779 if (hwnd != null && hwnd.zombie && xevent.type == XEventName.Expose) {
1780 hwnd.PendingExpose = hwnd.PendingNCExpose = false;
1781 goto ProcessNextMessage;
1784 // We need to make sure we only allow DestroyNotify events through for zombie
1785 // hwnds, since much of the event handling code makes requests using the hwnd's
1786 // ClientWindow, and that'll result in BadWindow errors if there's some lag
1787 // between the XDestroyWindow call and the DestroyNotify event.
1788 if (hwnd == null || hwnd.zombie) {
1789 #if DriverDebug || DriverDebugDestroy
1790 Console.WriteLine("GetMessage(): Got message {0} for non-existent or already destroyed window {1:X}",
1791 xevent.type, xevent.AnyEvent.window.ToInt32());
1793 goto ProcessNextMessage;
1796 client = hwnd.ClientWindow == xevent.AnyEvent.window;
1798 msg.hwnd = hwnd.Handle;
1800 switch (xevent.type) {
1801 case XEventName.KeyPress:
1802 Keyboard.KeyEvent (FocusWindow.Handle, xevent, ref msg);
1805 case XEventName.KeyRelease:
1806 Keyboard.KeyEvent (FocusWindow.Handle, xevent, ref msg);
1809 case XEventName.ButtonPress: {
1810 switch(xevent.ButtonEvent.button) {
1812 MouseState |= MouseButtons.Left;
1814 msg.message = Msg.WM_LBUTTONDOWN;
1816 msg.message = Msg.WM_NCLBUTTONDOWN;
1817 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1819 // TODO: For WM_NCLBUTTONDOWN wParam specifies a hit-test value not the virtual keys down
1820 msg.wParam=GetMousewParam(0);
1824 MouseState |= MouseButtons.Middle;
1826 msg.message = Msg.WM_MBUTTONDOWN;
1828 msg.message = Msg.WM_NCMBUTTONDOWN;
1829 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1831 msg.wParam=GetMousewParam(0);
1835 MouseState |= MouseButtons.Right;
1837 msg.message = Msg.WM_RBUTTONDOWN;
1839 msg.message = Msg.WM_NCRBUTTONDOWN;
1840 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1842 msg.wParam=GetMousewParam(0);
1846 msg.hwnd = FocusWindow.Handle;
1847 msg.message=Msg.WM_MOUSEWHEEL;
1848 msg.wParam=GetMousewParam(120);
1852 msg.hwnd = FocusWindow.Handle;
1853 msg.message=Msg.WM_MOUSEWHEEL;
1854 msg.wParam=GetMousewParam(-120);
1858 msg.lParam=(IntPtr) (xevent.ButtonEvent.y << 16 | xevent.ButtonEvent.x);
1859 MousePosition.X = xevent.ButtonEvent.x;
1860 MousePosition.Y = xevent.ButtonEvent.y;
1862 if (!hwnd.Enabled) {
1863 RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window,
1864 ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1867 if (Grab.Hwnd != IntPtr.Zero)
1868 msg.hwnd = Grab.Hwnd;
1870 if (ClickPending.Pending &&
1871 ((((long)xevent.ButtonEvent.time - ClickPending.Time) < DoubleClickInterval) &&
1872 (msg.wParam == ClickPending.wParam) &&
1873 (msg.lParam == ClickPending.lParam) &&
1874 (msg.message == ClickPending.Message))) {
1875 // Looks like a genuine double click, clicked twice on the same spot with the same keys
1876 switch(xevent.ButtonEvent.button) {
1878 msg.message = client ? Msg.WM_LBUTTONDBLCLK : Msg.WM_NCLBUTTONDBLCLK;
1882 msg.message = client ? Msg.WM_MBUTTONDBLCLK : Msg.WM_NCMBUTTONDBLCLK;
1886 msg.message = client ? Msg.WM_RBUTTONDBLCLK : Msg.WM_NCRBUTTONDBLCLK;
1890 ClickPending.Pending = false;
1894 ClickPending.Pending = true;
1895 ClickPending.Hwnd = msg.hwnd;
1896 ClickPending.Message = msg.message;
1897 ClickPending.wParam = msg.wParam;
1898 ClickPending.lParam = msg.lParam;
1899 ClickPending.Time = (long)xevent.ButtonEvent.time;
1905 case XEventName.ButtonRelease:
1907 Dnd.HandleButtonRelease (ref xevent);
1911 switch(xevent.ButtonEvent.button) {
1914 msg.message = Msg.WM_LBUTTONUP;
1916 msg.message = Msg.WM_NCLBUTTONUP;
1917 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1919 MouseState &= ~MouseButtons.Left;
1920 msg.wParam=GetMousewParam(0);
1925 msg.message = Msg.WM_MBUTTONUP;
1927 msg.message = Msg.WM_NCMBUTTONUP;
1928 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1930 MouseState &= ~MouseButtons.Middle;
1931 msg.wParam=GetMousewParam(0);
1936 msg.message = Msg.WM_RBUTTONUP;
1938 msg.message = Msg.WM_NCRBUTTONUP;
1939 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1941 MouseState &= ~MouseButtons.Right;
1942 msg.wParam=GetMousewParam(0);
1946 goto ProcessNextMessage;
1949 goto ProcessNextMessage;
1952 if (!hwnd.Enabled) {
1953 RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window,
1954 ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1957 if (Grab.Hwnd != IntPtr.Zero)
1958 msg.hwnd = Grab.Hwnd;
1960 msg.lParam=(IntPtr) (xevent.ButtonEvent.y << 16 | xevent.ButtonEvent.x);
1961 MousePosition.X = xevent.ButtonEvent.x;
1962 MousePosition.Y = xevent.ButtonEvent.y;
1965 case XEventName.MotionNotify:
1966 /* XXX move the compression stuff here */
1969 #if DriverDebugExtra
1970 Console.WriteLine("GetMessage(): Window {0:X} MotionNotify x={1} y={2}",
1971 client ? hwnd.ClientWindow.ToInt32() : hwnd.WholeWindow.ToInt32(),
1972 xevent.MotionEvent.x, xevent.MotionEvent.y);
1975 if (Dnd.HandleMotionNotify (ref xevent))
1976 goto ProcessNextMessage;
1978 if (Grab.Hwnd != IntPtr.Zero)
1979 msg.hwnd = Grab.Hwnd;
1981 NativeWindow.WndProc(msg.hwnd, Msg.WM_SETCURSOR, msg.hwnd, (IntPtr)HitTest.HTCLIENT);
1983 msg.message = Msg.WM_MOUSEMOVE;
1984 msg.wParam = GetMousewParam(0);
1985 msg.lParam = (IntPtr) (xevent.MotionEvent.y << 16 | xevent.MotionEvent.x & 0xFFFF);
1987 if (!hwnd.Enabled) {
1988 RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window,
1989 ref xevent.MotionEvent.x, ref xevent.MotionEvent.y);
1992 MousePosition.X = xevent.MotionEvent.x;
1993 MousePosition.Y = xevent.MotionEvent.y;
1995 if ((HoverState.Timer.Enabled) &&
1996 (((MousePosition.X + HoverState.Size.Width) < HoverState.X) ||
1997 ((MousePosition.X - HoverState.Size.Width) > HoverState.X) ||
1998 ((MousePosition.Y + HoverState.Size.Height) < HoverState.Y) ||
1999 ((MousePosition.Y - HoverState.Size.Height) > HoverState.Y))) {
2001 HoverState.Timer.Stop();
2002 HoverState.Timer.Start();
2003 HoverState.X = MousePosition.X;
2004 HoverState.Y = MousePosition.Y;
2013 #if DriverDebugExtra
2014 Console.WriteLine("GetMessage(): non-client area {0:X} MotionNotify x={1} y={2}",
2015 client ? hwnd.ClientWindow.ToInt32() : hwnd.WholeWindow.ToInt32(),
2016 xevent.MotionEvent.x, xevent.MotionEvent.y);
2018 msg.message = Msg.WM_NCMOUSEMOVE;
2020 if (!hwnd.Enabled) {
2021 RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window,
2022 ref xevent.MotionEvent.x, ref xevent.MotionEvent.y);
2025 // The hit test is sent in screen coordinates
2026 Xlib.XTranslateCoordinates (display, xevent.AnyEvent.window, RootWindow.Handle,
2027 xevent.MotionEvent.x, xevent.MotionEvent.y,
2028 out screen_x, out screen_y, out dummy);
2030 msg.lParam = (IntPtr) (screen_y << 16 | screen_x & 0xFFFF);
2031 ht = (HitTest)NativeWindow.WndProc (hwnd.ClientWindow, Msg.WM_NCHITTEST,
2032 IntPtr.Zero, msg.lParam).ToInt32 ();
2033 NativeWindow.WndProc(hwnd.ClientWindow, Msg.WM_SETCURSOR, msg.hwnd, (IntPtr)ht);
2035 MousePosition.X = xevent.MotionEvent.x;
2036 MousePosition.Y = xevent.MotionEvent.y;
2041 case XEventName.EnterNotify:
2043 goto ProcessNextMessage;
2045 if (xevent.CrossingEvent.mode != NotifyMode.NotifyNormal)
2046 goto ProcessNextMessage;
2048 msg.message = Msg.WM_MOUSE_ENTER;
2049 HoverState.X = xevent.CrossingEvent.x;
2050 HoverState.Y = xevent.CrossingEvent.y;
2051 HoverState.Timer.Enabled = true;
2052 HoverState.Window = xevent.CrossingEvent.window;
2056 case XEventName.LeaveNotify:
2058 goto ProcessNextMessage;
2060 if ((xevent.CrossingEvent.mode != NotifyMode.NotifyNormal) ||
2061 (xevent.CrossingEvent.window != hwnd.ClientWindow))
2062 goto ProcessNextMessage;
2064 msg.message=Msg.WM_MOUSE_LEAVE;
2065 HoverState.Timer.Enabled = false;
2066 HoverState.Window = IntPtr.Zero;
2070 case XEventName.ReparentNotify:
2071 if (hwnd.parent == null) { // Toplevel
2072 if ((xevent.ReparentEvent.parent != IntPtr.Zero) && (xevent.ReparentEvent.window == hwnd.WholeWindow)) {
2073 // We need to adjust x/y
2074 // This sucks ass, part 2
2075 // Every WM does the reparenting of toplevel windows different, so there's
2076 // no standard way of getting our adjustment considering frames/decorations
2077 // The code below is needed for metacity. KDE doesn't works just fine without this
2085 hwnd.Reparented = true;
2087 Xlib.XGetGeometry(display, XGetParent(hwnd.WholeWindow),
2088 out dummy_ptr, out new_x, out new_y,
2089 out dummy_int, out dummy_int, out dummy_int, out dummy_int);
2090 hwnd.FrameExtents(out frame_left, out frame_top);
2091 if ((frame_left != 0) && (frame_top != 0) && (new_x != frame_left) && (new_y != frame_top)) {
2094 hwnd.whacky_wm = true;
2097 if (hwnd.opacity != 0xffffffff) {
2100 opacity = (IntPtr)(Int32)hwnd.opacity;
2101 Xlib.XChangeProperty (display, XGetParent(hwnd.WholeWindow),
2102 Atoms._NET_WM_WINDOW_OPACITY, Atoms.XA_CARDINAL, 32,
2103 PropertyMode.Replace, ref opacity, 1);
2105 SendMessage(msg.hwnd, Msg.WM_WINDOWPOSCHANGED, msg.wParam, msg.lParam);
2106 goto ProcessNextMessage;
2108 hwnd.Reparented = false;
2109 goto ProcessNextMessage;
2112 goto ProcessNextMessage;
2114 case XEventName.ConfigureNotify:
2115 hwnd.AddConfigureNotify (xevent);
2116 goto ProcessNextMessage;
2118 case XEventName.FocusIn:
2119 // We received focus. We use X11 focus only to know if the app window does or does not have focus
2120 // We do not track the actual focussed window via it. Instead, this is done via FocusWindow internally
2121 // Receiving focus means we've gotten activated and therefore we need to let the actual FocusWindow know
2122 // about it having focus again
2123 if (xevent.FocusChangeEvent.detail != NotifyDetail.NotifyNonlinear)
2124 goto ProcessNextMessage;
2126 if (FocusWindow == null) {
2127 Control c = Control.FromHandle (hwnd.ClientWindow);
2129 goto ProcessNextMessage;
2130 Form form = c.FindForm ();
2132 goto ProcessNextMessage;
2133 ActiveWindow = (X11Hwnd)Hwnd.ObjectFromHandle (form.Handle);
2134 SendMessage (ActiveWindow.Handle, Msg.WM_ACTIVATE, (IntPtr) WindowActiveFlags.WA_ACTIVE, IntPtr.Zero);
2135 goto ProcessNextMessage;
2137 Keyboard.FocusIn(FocusWindow.Handle);
2138 SendMessage(FocusWindow.Handle, Msg.WM_SETFOCUS, IntPtr.Zero, IntPtr.Zero);
2139 goto ProcessNextMessage;
2141 case XEventName.FocusOut:
2142 // Se the comment for our FocusIn handler
2143 if (xevent.FocusChangeEvent.detail != NotifyDetail.NotifyNonlinear)
2144 goto ProcessNextMessage;
2146 if (FocusWindow == null)
2147 goto ProcessNextMessage;
2149 Keyboard.FocusOut(FocusWindow.Handle);
2151 while (Keyboard.ResetKeyState(FocusWindow.Handle, ref msg))
2152 SendMessage(FocusWindow.Handle, msg.message, msg.wParam, msg.lParam);
2154 SendMessage(FocusWindow.Handle, Msg.WM_KILLFOCUS, IntPtr.Zero, IntPtr.Zero);
2155 goto ProcessNextMessage;
2157 case XEventName.Expose:
2158 hwnd.AddExpose (client,
2159 xevent.ExposeEvent.x, xevent.ExposeEvent.y,
2160 xevent.ExposeEvent.width, xevent.ExposeEvent.height);
2161 goto ProcessNextMessage;
2163 case XEventName.DestroyNotify:
2165 // This is a bit tricky, we don't receive our own DestroyNotify, we only get those for our children
2166 hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(xevent.DestroyWindowEvent.window);
2168 // We may get multiple for the same window, act only one the first (when Hwnd still knows about it)
2169 if ((hwnd != null) && (hwnd.ClientWindow == xevent.DestroyWindowEvent.window)) {
2170 CleanupCachedWindows (hwnd);
2172 #if DriverDebugDestroy
2173 Console.WriteLine("Received X11 Destroy Notification for {0}", XplatUI.Window(hwnd.ClientWindow));
2176 msg.hwnd = hwnd.ClientWindow;
2177 msg.message=Msg.WM_DESTROY;
2181 goto ProcessNextMessage;
2185 case XEventName.ClientMessage:
2186 if (Dnd.HandleClientMessage (ref xevent))
2187 goto ProcessNextMessage;
2189 if (xevent.ClientMessageEvent.message_type == Atoms.AsyncAtom) {
2190 XplatUIDriverSupport.ExecuteClientMessage((GCHandle)xevent.ClientMessageEvent.ptr1);
2191 goto ProcessNextMessage;
2194 if (xevent.ClientMessageEvent.message_type == HoverState.Atom) {
2195 msg.message = Msg.WM_MOUSEHOVER;
2196 msg.wParam = GetMousewParam(0);
2197 msg.lParam = (IntPtr) (xevent.ClientMessageEvent.ptr1);
2201 if (xevent.ClientMessageEvent.message_type == Atoms.PostAtom) {
2202 msg.hwnd = xevent.ClientMessageEvent.ptr1;
2203 msg.message = (Msg) xevent.ClientMessageEvent.ptr2.ToInt32 ();
2204 msg.wParam = xevent.ClientMessageEvent.ptr3;
2205 msg.lParam = xevent.ClientMessageEvent.ptr4;
2209 if (xevent.ClientMessageEvent.message_type == Atoms._XEMBED) {
2210 #if DriverDebugXEmbed
2211 Console.WriteLine("GOT EMBED MESSAGE {0:X}, detail {1:X}",
2212 xevent.ClientMessageEvent.ptr2.ToInt32(), xevent.ClientMessageEvent.ptr3.ToInt32());
2215 if (xevent.ClientMessageEvent.ptr2.ToInt32() == (int)XEmbedMessage.EmbeddedNotify) {
2216 XSizeHints hints = new XSizeHints();
2219 Xlib.XGetWMNormalHints (display, hwnd.WholeWindow, ref hints, out dummy);
2221 hwnd.width = hints.max_width;
2222 hwnd.height = hints.max_height;
2223 hwnd.ClientRect = Rectangle.Empty;
2224 SendMessage(msg.hwnd, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero);
2228 if (xevent.ClientMessageEvent.message_type == Atoms.WM_PROTOCOLS) {
2229 if (xevent.ClientMessageEvent.ptr1 == Atoms.WM_DELETE_WINDOW) {
2230 msg.message = Msg.WM_CLOSE;
2234 // We should not get this, but I'll leave the code in case we need it in the future
2235 if (xevent.ClientMessageEvent.ptr1 == Atoms.WM_TAKE_FOCUS) {
2236 goto ProcessNextMessage;
2239 goto ProcessNextMessage;
2241 case XEventName.PropertyNotify:
2242 // The Hwnd's themselves handle this
2243 hwnd.PropertyChanged (xevent);
2244 goto ProcessNextMessage;
2248 } while (got_xevent);
2250 queue.CheckTimers ();
2252 while (queue.Configure.Count > 0) {
2253 xevent = queue.Configure.Dequeue ();
2255 hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (xevent.AnyEvent.window);
2256 if (hwnd == null || hwnd.zombie)
2259 hwnd.HandleConfigureNotify (xevent);
2262 /* now that we've handled X events, handle any pending paint and configure events */
2264 while (queue.Paint.Count > 0) {
2265 xevent = queue.Paint.Dequeue ();
2267 hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (xevent.AnyEvent.window);
2268 if (hwnd == null || hwnd.zombie)
2271 client = hwnd.ClientWindow == xevent.AnyEvent.window;
2273 if (queue.PostQuitState || !hwnd.Mapped) {
2276 hwnd.PendingExpose = hwnd.PendingNCExpose = false;
2280 hwnd.Queue.Unlock ();
2284 // XXX these should really be error conditions. if we
2285 // have an expose event in the queue, there should be
2289 if (client && !hwnd.PendingExpose) {
2290 Console.WriteLine ("client expose but no expose pending");
2293 else if (!client && !hwnd.PendingNCExpose) {
2294 Console.WriteLine ("non-client expose but no expose pending");
2299 hwnd.Queue.Unlock ();
2302 msg.hwnd = hwnd.Handle;
2305 #if DriverDebugExtra
2306 Console.WriteLine("GetMessage(): Window {0:X} Exposed area {1},{2} {3}x{4}",
2307 hwnd.client_window.ToInt32(),
2308 xevent.ExposeEvent.x, xevent.ExposeEvent.y,
2309 xevent.ExposeEvent.width, xevent.ExposeEvent.height);
2311 msg.message = Msg.WM_PAINT;
2316 switch (hwnd.border_style) {
2317 case FormBorderStyle.Fixed3D:
2318 g = Graphics.FromHwnd(hwnd.WholeWindow);
2319 ControlPaint.DrawBorder3D(g, new Rectangle(0, 0, hwnd.Width, hwnd.Height),
2320 Border3DStyle.Sunken);
2324 case FormBorderStyle.FixedSingle:
2325 g = Graphics.FromHwnd(hwnd.WholeWindow);
2326 ControlPaint.DrawBorder(g, new Rectangle(0, 0, hwnd.Width, hwnd.Height),
2327 Color.Black, ButtonBorderStyle.Solid);
2331 #if DriverDebugExtra
2332 Console.WriteLine("GetMessage(): Window {0:X} Exposed non-client area {1},{2} {3}x{4}",
2333 hwnd.ClientWindow.ToInt32(),
2334 xevent.ExposeEvent.x, xevent.ExposeEvent.y,
2335 xevent.ExposeEvent.width, xevent.ExposeEvent.height);
2338 Rectangle rect = new Rectangle (xevent.ExposeEvent.x, xevent.ExposeEvent.y,
2339 xevent.ExposeEvent.width, xevent.ExposeEvent.height);
2340 Region region = new Region (rect);
2341 IntPtr hrgn = region.GetHrgn (null); // Graphics object isn't needed
2342 msg.message = Msg.WM_NCPAINT;
2343 msg.wParam = hrgn == IntPtr.Zero ? (IntPtr)1 : hrgn;
2344 msg.refobject = region;
2350 if (!queue.PostQuitState) {
2351 msg.hwnd= IntPtr.Zero;
2352 msg.message = Msg.WM_ENTERIDLE;
2356 // We reset ourselves so GetMessage can be called again
2357 queue.PostQuitState = false;
2362 [MonoTODO("Implement filtering and PM_NOREMOVE")]
2363 public bool PeekMessage (object queue_id, ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax, uint flags)
2365 X11ThreadQueue queue = (X11ThreadQueue) queue_id;
2368 if ((flags & (uint)PeekMessageFlags.PM_REMOVE) == 0) {
2369 throw new NotImplementedException("PeekMessage PM_NOREMOVE is not implemented yet"); // FIXME - Implement PM_NOREMOVE flag
2375 if (queue.CountUnlocked > 0)
2385 queue.CheckTimers ();
2390 return GetMessage(queue_id, ref msg, hWnd, wFilterMin, wFilterMax);
2393 public void DoEvents (X11ThreadQueue queue)
2395 MSG msg = new MSG ();
2397 if (OverrideCursorHandle != IntPtr.Zero)
2398 OverrideCursorHandle = IntPtr.Zero;
2400 queue.DispatchIdle = false;
2402 while (PeekMessage(queue, ref msg, IntPtr.Zero, 0, 0, (uint)PeekMessageFlags.PM_REMOVE)) {
2403 TranslateMessage (ref msg);
2404 DispatchMessage (ref msg);
2407 queue.DispatchIdle = true;