In Test/System.Windows.Forms:
[mono-project.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms.X11Internal / X11Display.cs
blob0251ab370ac9570333f4c0865c167c1f8c792643
1 // a copy of this software and associated documentation files (the
2 // "Software"), to deal in the Software without restriction, including
3 // without limitation the rights to use, copy, modify, merge, publish,
4 // distribute, sublicense, and/or sell copies of the Software, and to
5 // permit persons to whom the Software is furnished to do so, subject to
6 // the following conditions:
7 //
8 // The above copyright notice and this permission notice shall be
9 // included in all copies or substantial portions of the Software.
10 //
11 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
13 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
14 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
15 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
16 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
17 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 // Copyright (c) 2006 Novell, Inc. (http://www.novell.com)
23 using System;
24 using System.Collections;
25 using System.Diagnostics;
26 using System.Drawing;
27 using System.Drawing.Drawing2D;
28 using System.Drawing.Imaging;
29 using System.IO;
30 using System.Net;
31 using System.Net.Sockets;
32 using System.Reflection;
33 using System.Runtime.InteropServices;
34 using System.Text;
35 using System.Threading;
36 using System.Windows.Forms;
37 // Only do the poll when building with mono for now
38 #if __MonoCS__
39 using Mono.Unix.Native;
40 #endif
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
52 X11Keyboard Keyboard;
53 internal X11Dnd Dnd; // XXX X11Hwnd needs it to enable Dnd
54 bool detectable_key_auto_repeat;
56 X11Atoms atoms;
57 X11RootHwnd root_hwnd;
58 X11Hwnd foster_hwnd;
60 // Clipboard
61 IntPtr ClipMagic;
62 ClipboardStruct Clipboard; // Our clipboard
64 // Focus tracking
65 internal X11Hwnd ActiveWindow;
66 X11Hwnd FocusWindow;
68 // Modality support
69 Stack ModalWindows; // Stack of our modal windows
71 // Caret
72 CaretStruct Caret;
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
83 GrabStruct Grab;
85 // Cursors
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
90 // State
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
99 #if __MonoCS__
100 Pollfd[] pollfds;
101 #endif
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;
112 // Debugging support
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);
131 HoverState.X = -1;
132 HoverState.Y = -1;
134 ActiveWindow = null;
135 FocusWindow = null;
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));
154 #if __MonoCS__
155 pollfds = new Pollfd [1];
156 pollfds [0] = new Pollfd ();
157 pollfds [0].fd = Xlib.XConnectionNumber (display);
158 pollfds [0].events = PollEvents.POLLIN;
159 #endif
161 Keyboard = new X11Keyboard(display, foster_hwnd.Handle);
162 Dnd = new X11Dnd (display, Keyboard);
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
173 try {
174 Xlib.XkbSetDetectableAutoRepeat (display, true, IntPtr.Zero);
175 detectable_key_auto_repeat = true;
176 } catch {
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 ();
190 #region Callbacks
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);
197 if (hwnd != null) {
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)
214 if (Caret.Paused) {
215 return;
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 ()
224 StackTrace stack;
225 StackFrame frame;
226 string newline;
227 string unknown;
228 StringBuilder sb;
229 MethodBase method;
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);
238 sb.Append(newline);
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());
246 else
247 sb.AppendFormat ("{0}.{1} ()", method.DeclaringType.FullName, method.Name);
248 } else {
249 sb.Append(unknown);
252 return sb.ToString();
255 private int HandleError (IntPtr display, ref XErrorEvent error_event)
257 if (ErrorExceptions)
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);
261 else
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),
266 WhereString());
267 return 0;
269 #endregion // Callbacks
271 private void ShowCaret()
273 if ((Caret.gc == IntPtr.Zero) || Caret.On) {
274 return;
276 Caret.On = true;
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) {
284 return;
286 Caret.On = false;
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) {
294 if (visible) {
295 if (!Caret.Visible) {
296 Caret.Visible = true;
297 ShowCaret();
298 Caret.Timer.Start();
300 } else {
301 Caret.Visible = false;
302 Caret.Timer.Stop();
303 HideCaret();
308 public void AudibleAlert ()
310 Xlib.XBell (display, 0);
313 public void Flush ()
315 Xlib.XFlush (display);
318 public void Close ()
320 // XXX shut down the event_thread
321 Xlib.XCloseDisplay (display);
324 public IntPtr XGetParent(IntPtr handle)
326 IntPtr Root;
327 IntPtr Parent;
328 IntPtr Children;
329 int ChildCount;
331 Xlib.XQueryTree (display, handle, out Root, out Parent, out Children, out ChildCount);
333 if (Children!=IntPtr.Zero) {
334 Xlib.XFree(Children);
337 return Parent;
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;
350 X11Hwnd hwnd;
352 hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
353 #if DriverDebug
354 Console.WriteLine("Adding Systray Whole:{0:X}, Client:{1:X}",
355 hwnd.WholeWindow.ToInt32(), hwnd.ClientWindow.ToInt32());
356 #endif
358 // Oh boy.
359 if (hwnd.ClientWindow != hwnd.WholeWindow) {
360 Xlib.XDestroyWindow (display, hwnd.ClientWindow);
361 hwnd.ClientWindow = hwnd.WholeWindow;
363 try {
364 hwnd.Queue.Lock ();
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;
372 finally {
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
400 tt = new ToolTip();
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);
408 tt.Active = true;
409 } else {
410 tt.Active = false;
413 SendNetClientMessage (SystrayMgrWindow,
414 Atoms._NET_SYSTEM_TRAY_OPCODE,
415 IntPtr.Zero,
416 (IntPtr)SystrayRequest.SYSTEM_TRAY_REQUEST_DOCK,
417 hwnd.WholeWindow);
419 return true;
422 tt = null;
423 return false;
426 public bool SystrayChange (IntPtr handle, string tip, Icon icon, ref ToolTip tt)
428 Control control;
430 control = Control.FromHandle(handle);
431 if (control != null && tt != null) {
432 tt.SetToolTip(control, tip);
433 tt.Active = true;
434 return true;
435 } else {
436 return false;
440 public void SystrayRemove(IntPtr handle, ref ToolTip tt)
442 #if GTKSOCKET_SUPPORTS_REPARENTING
443 X11Hwnd hwnd;
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);
460 #else
461 Control control = Control.FromHandle(handle);
462 if (control is NotifyIcon.NotifyIconWindow)
463 ((NotifyIcon.NotifyIconWindow)control).InternalRecreateHandle ();
464 #endif
466 // The caller can now re-dock it later...
467 if (tt != null) {
468 tt.Dispose();
469 tt = null;
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)
488 if (Modal) {
489 ModalWindows.Push(hwnd);
490 } else {
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)) {
495 ModalWindows.Pop();
498 if (ModalWindows.Count > 0) {
499 X11Hwnd top_hwnd = (X11Hwnd)ModalWindows.Peek();
500 top_hwnd.Activate();
505 public TransparencySupport 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) ? TransparencySupport.GetSet : TransparencySupport.None;
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);
531 if (hwnd == null)
532 return IntPtr.Zero;
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);
549 #endif
551 return IntPtr.Zero;
553 else {
554 return NativeWindow.WndProc (hwnd.Handle, message, wParam, lParam);
558 public int SendInput (IntPtr handle, Queue keys) {
559 if (handle == IntPtr.Zero)
560 return 0;
562 int count = keys.Count;
563 Hwnd hwnd = Hwnd.ObjectFromHandle(handle);
565 while (keys.Count > 0) {
567 MSG msg = (MSG)keys.Dequeue();
569 XEvent xevent = new XEvent ();
571 xevent.type = (msg.message == Msg.WM_KEYUP ? XEventName.KeyRelease : XEventName.KeyPress);
572 xevent.KeyEvent.display = display;
574 if (hwnd != null) {
575 xevent.KeyEvent.window = hwnd.whole_window;
576 } else {
577 xevent.KeyEvent.window = IntPtr.Zero;
580 xevent.KeyEvent.keycode = Keyboard.ToKeycode((int)msg.wParam);
582 hwnd.Queue.EnqueueLocked (xevent);
584 return count;
587 // FIXME - I think this should just enqueue directly
588 public bool PostMessage (IntPtr handle, Msg message, IntPtr wparam, IntPtr lparam)
590 XEvent xevent = new XEvent ();
591 X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
593 xevent.type = XEventName.ClientMessage;
594 xevent.ClientMessageEvent.display = display;
596 if (hwnd != null) {
597 xevent.ClientMessageEvent.window = hwnd.WholeWindow;
598 } else {
599 xevent.ClientMessageEvent.window = IntPtr.Zero;
602 xevent.ClientMessageEvent.message_type = Atoms.PostAtom;
603 xevent.ClientMessageEvent.format = 32;
604 xevent.ClientMessageEvent.ptr1 = handle;
605 xevent.ClientMessageEvent.ptr2 = (IntPtr) message;
606 xevent.ClientMessageEvent.ptr3 = wparam;
607 xevent.ClientMessageEvent.ptr4 = lparam;
609 hwnd.Queue.Enqueue (xevent);
611 return true;
614 public void SendNetWMMessage (IntPtr window, IntPtr message_type, IntPtr l0, IntPtr l1, IntPtr l2)
616 XEvent xev;
618 xev = new XEvent();
619 xev.ClientMessageEvent.type = XEventName.ClientMessage;
620 xev.ClientMessageEvent.send_event = true;
621 xev.ClientMessageEvent.window = window;
622 xev.ClientMessageEvent.message_type = message_type;
623 xev.ClientMessageEvent.format = 32;
624 xev.ClientMessageEvent.ptr1 = l0;
625 xev.ClientMessageEvent.ptr2 = l1;
626 xev.ClientMessageEvent.ptr3 = l2;
628 Xlib.XSendEvent (display, root_hwnd.Handle, false,
629 new IntPtr ((int) (EventMask.SubstructureRedirectMask | EventMask.SubstructureNotifyMask)), ref xev);
632 public void SendNetClientMessage (IntPtr window, IntPtr message_type, IntPtr l0, IntPtr l1, IntPtr l2)
634 XEvent xev;
636 xev = new XEvent();
637 xev.ClientMessageEvent.type = XEventName.ClientMessage;
638 xev.ClientMessageEvent.send_event = true;
639 xev.ClientMessageEvent.window = window;
640 xev.ClientMessageEvent.message_type = message_type;
641 xev.ClientMessageEvent.format = 32;
642 xev.ClientMessageEvent.ptr1 = l0;
643 xev.ClientMessageEvent.ptr2 = l1;
644 xev.ClientMessageEvent.ptr3 = l2;
646 Xlib.XSendEvent (display, window, false, new IntPtr ((int)EventMask.NoEventMask), ref xev);
649 public bool TranslateMessage (ref MSG msg)
651 return Keyboard.TranslateMessage (ref msg);
654 public IntPtr DispatchMessage (ref MSG msg)
656 return NativeWindow.WndProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
659 private void QueryPointer (IntPtr w, out IntPtr root, out IntPtr child,
660 out int root_x, out int root_y, out int child_x, out int child_y,
661 out int mask)
663 /* this code was written with the help of
664 glance at gdk. I never would have realized we
665 needed a loop in order to traverse down in the
666 hierarchy. I would have assumed you'd get the
667 most deeply nested child and have to do
668 XQueryTree to move back up the hierarchy..
669 stupid me, of course. */
670 IntPtr c;
672 // Xlib.XGrabServer (display);
674 Xlib.XQueryPointer (display, w, out root, out c,
675 out root_x, out root_y, out child_x, out child_y,
676 out mask);
678 if (root != w)
679 c = root;
681 IntPtr child_last = IntPtr.Zero;
682 while (c != IntPtr.Zero) {
683 child_last = c;
684 Xlib.XQueryPointer (display, c, out root, out c,
685 out root_x, out root_y, out child_x, out child_y,
686 out mask);
689 // Xlib.XUngrabServer (display);
691 child = child_last;
694 public void SetCursorPos (int x, int y)
696 IntPtr root, child;
697 int root_x, root_y, child_x, child_y, mask;
699 /* we need to do a
700 * QueryPointer before warping
701 * because if the warp is on
702 * the RootWindow, the x/y are
703 * relative to the current
704 * mouse position
706 QueryPointer (RootWindow.Handle,
707 out root,
708 out child,
709 out root_x, out root_y,
710 out child_x, out child_y,
711 out mask);
713 Xlib.XWarpPointer (display, IntPtr.Zero, IntPtr.Zero, 0, 0, 0, 0, x - root_x, y - root_y);
715 Xlib.XFlush (display);
717 /* then we need to a
718 * QueryPointer after warping
719 * to manually generate a
720 * motion event for the window
721 * we move into.
723 QueryPointer (RootWindow.Handle,
724 out root,
725 out child,
726 out root_x, out root_y,
727 out child_x, out child_y,
728 out mask);
730 X11Hwnd child_hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(child);
731 if (child_hwnd == null)
732 return;
734 XEvent xevent = new XEvent ();
736 xevent.type = XEventName.MotionNotify;
737 xevent.MotionEvent.display = display;
738 xevent.MotionEvent.window = child_hwnd.Handle;
739 xevent.MotionEvent.root = RootWindow.Handle;
740 xevent.MotionEvent.x = child_x;
741 xevent.MotionEvent.y = child_y;
742 xevent.MotionEvent.x_root = root_x;
743 xevent.MotionEvent.y_root = root_y;
744 xevent.MotionEvent.state = mask;
746 child_hwnd.Queue.Enqueue (xevent);
749 public void SetFocus (X11Hwnd new_focus)
751 if (new_focus == FocusWindow)
752 return;
754 X11Hwnd prev_focus = FocusWindow;
755 FocusWindow = new_focus;
757 if (prev_focus != null)
758 SendMessage (prev_focus.Handle, Msg.WM_KILLFOCUS,
759 FocusWindow == null ? IntPtr.Zero : FocusWindow.Handle, IntPtr.Zero);
760 if (FocusWindow != null)
761 SendMessage (FocusWindow.Handle, Msg.WM_SETFOCUS,
762 prev_focus == null ? IntPtr.Zero : prev_focus.Handle, IntPtr.Zero);
764 //XSetInputFocus(DisplayHandle, Hwnd.ObjectFromHandle(handle).ClientWindow, RevertTo.None, IntPtr.Zero);
767 public IntPtr DefineCursor (Bitmap bitmap, Bitmap mask, Color cursor_pixel, Color mask_pixel, int xHotSpot, int yHotSpot)
769 IntPtr cursor;
770 Bitmap cursor_bitmap;
771 Bitmap cursor_mask;
772 Byte[] cursor_bits;
773 Byte[] mask_bits;
774 Color c_pixel;
775 Color m_pixel;
776 int width;
777 int height;
778 IntPtr cursor_pixmap;
779 IntPtr mask_pixmap;
780 XColor fg;
781 XColor bg;
782 bool and;
783 bool xor;
785 if (Xlib.XQueryBestCursor (display, RootWindow.Handle, bitmap.Width, bitmap.Height, out width, out height) == 0) {
786 return IntPtr.Zero;
789 // Win32 only allows creation cursors of a certain size
790 if ((bitmap.Width != width) || (bitmap.Width != height)) {
791 cursor_bitmap = new Bitmap(bitmap, new Size(width, height));
792 cursor_mask = new Bitmap(mask, new Size(width, height));
793 } else {
794 cursor_bitmap = bitmap;
795 cursor_mask = mask;
798 width = cursor_bitmap.Width;
799 height = cursor_bitmap.Height;
801 cursor_bits = new Byte[(width / 8) * height];
802 mask_bits = new Byte[(width / 8) * height];
804 for (int y = 0; y < height; y++) {
805 for (int x = 0; x < width; x++) {
806 c_pixel = cursor_bitmap.GetPixel(x, y);
807 m_pixel = cursor_mask.GetPixel(x, y);
809 and = c_pixel == cursor_pixel;
810 xor = m_pixel == mask_pixel;
812 if (!and && !xor) {
813 // Black
814 // cursor_bits[y * width / 8 + x / 8] &= (byte)~((1 << (x % 8))); // The bit already is 0
815 mask_bits[y * width / 8 + x / 8] |= (byte)(1 << (x % 8));
816 } else if (and && !xor) {
817 // White
818 cursor_bits[y * width / 8 + x / 8] |= (byte)(1 << (x % 8));
819 mask_bits[y * width / 8 + x / 8] |= (byte)(1 << (x % 8));
820 #if notneeded
821 } else if (and && !xor) {
822 // Screen
823 } else if (and && xor) {
824 // Inverse Screen
826 // X11 doesn't know the 'reverse screen' concept, so we'll treat them the same
827 // we want both to be 0 so nothing to be done
828 //cursor_bits[y * width / 8 + x / 8] &= (byte)~((1 << (x % 8)));
829 //mask_bits[y * width / 8 + x / 8] |= (byte)(01 << (x % 8));
830 #endif
835 cursor_pixmap = Xlib.XCreatePixmapFromBitmapData (display, RootWindow.Handle,
836 cursor_bits, width, height, (IntPtr)1, (IntPtr)0, 1);
837 mask_pixmap = Xlib.XCreatePixmapFromBitmapData (display, RootWindow.Handle,
838 mask_bits, width, height, (IntPtr)1, (IntPtr)0, 1);
839 fg = new XColor();
840 bg = new XColor();
842 fg.pixel = Xlib.XWhitePixel (display, DefaultScreen);
843 fg.red = (ushort)65535;
844 fg.green = (ushort)65535;
845 fg.blue = (ushort)65535;
847 bg.pixel = Xlib.XBlackPixel (display, DefaultScreen);
849 cursor = Xlib.XCreatePixmapCursor (display, cursor_pixmap, mask_pixmap, ref fg, ref bg, xHotSpot, yHotSpot);
851 Xlib.XFreePixmap (display, cursor_pixmap);
852 Xlib.XFreePixmap (display, mask_pixmap);
854 return cursor;
857 public IntPtr DefineStdCursor (StdCursor id)
859 CursorFontShape shape;
861 // FIXME - define missing shapes
863 switch (id) {
864 case StdCursor.AppStarting:
865 shape = CursorFontShape.XC_watch;
866 break;
868 case StdCursor.Arrow:
869 shape = CursorFontShape.XC_top_left_arrow;
870 break;
872 case StdCursor.Cross:
873 shape = CursorFontShape.XC_crosshair;
874 break;
876 case StdCursor.Default:
877 shape = CursorFontShape.XC_top_left_arrow;
878 break;
880 case StdCursor.Hand:
881 shape = CursorFontShape.XC_hand1;
882 break;
884 case StdCursor.Help:
885 shape = CursorFontShape.XC_question_arrow;
886 break;
888 case StdCursor.HSplit:
889 shape = CursorFontShape.XC_sb_v_double_arrow;
890 break;
892 case StdCursor.IBeam:
893 shape = CursorFontShape.XC_xterm;
894 break;
896 case StdCursor.No:
897 shape = CursorFontShape.XC_circle;
898 break;
900 case StdCursor.NoMove2D:
901 shape = CursorFontShape.XC_fleur;
902 break;
904 case StdCursor.NoMoveHoriz:
905 shape = CursorFontShape.XC_fleur;
906 break;
908 case StdCursor.NoMoveVert:
909 shape = CursorFontShape.XC_fleur;
910 break;
912 case StdCursor.PanEast:
913 shape = CursorFontShape.XC_fleur;
914 break;
916 case StdCursor.PanNE:
917 shape = CursorFontShape.XC_fleur;
918 break;
920 case StdCursor.PanNorth:
921 shape = CursorFontShape.XC_fleur;
922 break;
924 case StdCursor.PanNW:
925 shape = CursorFontShape.XC_fleur;
926 break;
928 case StdCursor.PanSE:
929 shape = CursorFontShape.XC_fleur;
930 break;
932 case StdCursor.PanSouth:
933 shape = CursorFontShape.XC_fleur;
934 break;
936 case StdCursor.PanSW:
937 shape = CursorFontShape.XC_fleur;
938 break;
940 case StdCursor.PanWest:
941 shape = CursorFontShape.XC_sizing;
942 break;
944 case StdCursor.SizeAll:
945 shape = CursorFontShape.XC_fleur;
946 break;
948 case StdCursor.SizeNESW:
949 shape = CursorFontShape.XC_top_right_corner;
950 break;
952 case StdCursor.SizeNS:
953 shape = CursorFontShape.XC_sb_v_double_arrow;
954 break;
956 case StdCursor.SizeNWSE:
957 shape = CursorFontShape.XC_top_left_corner;
958 break;
960 case StdCursor.SizeWE:
961 shape = CursorFontShape.XC_sb_h_double_arrow;
962 break;
964 case StdCursor.UpArrow:
965 shape = CursorFontShape.XC_center_ptr;
966 break;
968 case StdCursor.VSplit:
969 shape = CursorFontShape.XC_sb_h_double_arrow;
970 break;
972 case StdCursor.WaitCursor:
973 shape = CursorFontShape.XC_watch;
974 break;
976 default:
977 return IntPtr.Zero;
980 return Xlib.XCreateFontCursor (display, shape);
983 // XXX this should take an X11Hwnd.
984 public void CreateCaret (IntPtr handle, int width, int height)
986 XGCValues gc_values;
987 X11Hwnd hwnd;
989 hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
991 if (Caret.Hwnd != IntPtr.Zero)
992 DestroyCaret(Caret.Hwnd);
994 Caret.Hwnd = handle;
995 Caret.Window = hwnd.ClientWindow;
996 Caret.Width = width;
997 Caret.Height = height;
998 Caret.Visible = false;
999 Caret.On = false;
1001 gc_values = new XGCValues();
1002 gc_values.line_width = width;
1004 Caret.gc = Xlib.XCreateGC (display, Caret.Window, new IntPtr ((int)GCFunction.GCLineWidth), ref gc_values);
1005 if (Caret.gc == IntPtr.Zero) {
1006 Caret.Hwnd = IntPtr.Zero;
1007 return;
1010 Xlib.XSetFunction (display, Caret.gc, GXFunction.GXinvert);
1014 // XXX this should take an X11Hwnd.
1015 public void DestroyCaret (IntPtr handle)
1017 if (Caret.Hwnd == handle) {
1018 if (Caret.Visible == true) {
1019 Caret.Timer.Stop ();
1021 if (Caret.gc != IntPtr.Zero) {
1022 Xlib.XFreeGC (display, Caret.gc);
1023 Caret.gc = IntPtr.Zero;
1025 Caret.Hwnd = IntPtr.Zero;
1026 Caret.Visible = false;
1027 Caret.On = false;
1031 public void SetCaretPos (IntPtr handle, int x, int y)
1033 if (Caret.Hwnd == handle) {
1034 Caret.Timer.Stop();
1035 HideCaret();
1037 Caret.X = x;
1038 Caret.Y = y;
1040 if (Caret.Visible == true) {
1041 ShowCaret();
1042 Caret.Timer.Start();
1047 public void DestroyCursor (IntPtr cursor)
1049 Xlib.XFreeCursor (display, cursor);
1052 private void AccumulateDestroyedHandles (Control c, ArrayList list)
1054 if (c != null) {
1055 Control[] controls = c.Controls.GetAllControls ();
1057 if (c.IsHandleCreated && !c.IsDisposed) {
1058 X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(c.Handle);
1060 #if DriverDebug || DriverDebugDestroy
1061 Console.WriteLine (" + adding {0} to the list of zombie windows", XplatUI.Window (hwnd.Handle));
1062 Console.WriteLine (" + parent X window is {0:X}", XGetParent (hwnd.WholeWindow).ToInt32());
1063 #endif
1065 list.Add (hwnd);
1066 CleanupCachedWindows (hwnd);
1067 hwnd.zombie = true;
1070 for (int i = 0; i < controls.Length; i ++) {
1071 AccumulateDestroyedHandles (controls[i], list);
1077 void CleanupCachedWindows (X11Hwnd hwnd)
1079 if (ActiveWindow == hwnd) {
1080 SendMessage (hwnd.ClientWindow, Msg.WM_ACTIVATE, (IntPtr)WindowActiveFlags.WA_INACTIVE, IntPtr.Zero);
1081 ActiveWindow = null;
1084 if (FocusWindow == hwnd) {
1085 SendMessage (hwnd.ClientWindow, Msg.WM_KILLFOCUS, IntPtr.Zero, IntPtr.Zero);
1086 FocusWindow = null;
1089 if (Grab.Hwnd == hwnd.Handle) {
1090 Grab.Hwnd = IntPtr.Zero;
1091 Grab.Confined = false;
1094 DestroyCaret (hwnd.Handle);
1098 public void DestroyWindow (X11Hwnd hwnd)
1100 CleanupCachedWindows (hwnd);
1102 hwnd.SendParentNotify (Msg.WM_DESTROY, int.MaxValue, int.MaxValue);
1104 ArrayList windows = new ArrayList ();
1106 AccumulateDestroyedHandles (Control.ControlNativeWindow.ControlFromHandle(hwnd.Handle), windows);
1108 hwnd.DestroyWindow ();
1110 foreach (X11Hwnd h in windows) {
1111 SendMessage (h.Handle, Msg.WM_DESTROY, IntPtr.Zero, IntPtr.Zero);
1115 public X11Hwnd GetActiveWindow ()
1117 IntPtr actual_atom;
1118 int actual_format;
1119 IntPtr nitems;
1120 IntPtr bytes_after;
1121 IntPtr prop = IntPtr.Zero;
1122 IntPtr active = IntPtr.Zero;
1124 Xlib.XGetWindowProperty (display, RootWindow.Handle,
1125 Atoms._NET_ACTIVE_WINDOW, IntPtr.Zero, new IntPtr (1), false,
1126 Atoms.XA_WINDOW, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop);
1128 if (((long)nitems > 0) && (prop != IntPtr.Zero)) {
1129 active = (IntPtr)Marshal.ReadInt32(prop);
1130 Xlib.XFree(prop);
1133 return (X11Hwnd)Hwnd.GetObjectFromWindow(active);
1136 public void SetActiveWindow (X11Hwnd new_active_window)
1138 if (new_active_window != ActiveWindow) {
1139 if (ActiveWindow != null)
1140 PostMessage (ActiveWindow.Handle, Msg.WM_ACTIVATE,
1141 (IntPtr)WindowActiveFlags.WA_INACTIVE, IntPtr.Zero);
1143 ActiveWindow = new_active_window;
1145 if (ActiveWindow != null)
1146 PostMessage (ActiveWindow.Handle, Msg.WM_ACTIVATE,
1147 (IntPtr)WindowActiveFlags.WA_ACTIVE, IntPtr.Zero);
1150 if (ModalWindows.Count > 0) {
1151 // Modality handling, if we are modal and the new active window is one
1152 // of ours but not the modal one, switch back to the modal window
1154 if (ActiveWindow != null &&
1155 NativeWindow.FindWindow (ActiveWindow.Handle) != null) {
1156 if (ActiveWindow != (X11Hwnd)ModalWindows.Peek())
1157 ((X11Hwnd)ModalWindows.Peek()).Activate ();
1162 public void GetDisplaySize (out Size size)
1164 XWindowAttributes attributes = new XWindowAttributes();
1166 // FIXME - use _NET_WM messages instead?
1167 Xlib.XGetWindowAttributes (display, RootWindow.Handle, ref attributes);
1169 size = new Size(attributes.width, attributes.height);
1172 // XXX this method doesn't really fit well anywhere in the backend
1173 public SizeF GetAutoScaleSize (Font font)
1175 Graphics g;
1176 float width;
1177 string magic_string = "The quick brown fox jumped over the lazy dog.";
1178 double magic_number = 44.549996948242189; // XXX my god, where did this number come from?
1180 g = Graphics.FromHwnd (FosterParent.Handle);
1182 width = (float) (g.MeasureString (magic_string, font).Width / magic_number);
1183 return new SizeF(width, font.Height);
1186 public void GetCursorPos (X11Hwnd hwnd, out int x, out int y)
1188 IntPtr use_handle;
1189 IntPtr root;
1190 IntPtr child;
1191 int root_x;
1192 int root_y;
1193 int win_x;
1194 int win_y;
1195 int keys_buttons;
1197 if (hwnd != null)
1198 use_handle = hwnd.Handle;
1199 else
1200 use_handle = RootWindow.Handle;
1202 QueryPointer (use_handle, out root, out child, out root_x, out root_y, out win_x, out win_y, out keys_buttons);
1204 if (hwnd != null) {
1205 x = win_x;
1206 y = win_y;
1207 } else {
1208 x = root_x;
1209 y = root_y;
1213 public IntPtr GetFocus ()
1215 return FocusWindow.Handle;
1218 public IntPtr GetMousewParam (int Delta)
1220 int result = 0;
1222 if ((MouseState & MouseButtons.Left) != 0) {
1223 result |= (int)MsgButtons.MK_LBUTTON;
1226 if ((MouseState & MouseButtons.Middle) != 0) {
1227 result |= (int)MsgButtons.MK_MBUTTON;
1230 if ((MouseState & MouseButtons.Right) != 0) {
1231 result |= (int)MsgButtons.MK_RBUTTON;
1234 Keys mods = ModifierKeys;
1235 if ((mods & Keys.Control) != 0) {
1236 result |= (int)MsgButtons.MK_CONTROL;
1239 if ((mods & Keys.Shift) != 0) {
1240 result |= (int)MsgButtons.MK_SHIFT;
1243 result |= Delta << 16;
1245 return (IntPtr)result;
1248 public void GrabInfo (out IntPtr handle, out bool GrabConfined, out Rectangle GrabArea)
1250 handle = Grab.Hwnd;
1251 GrabConfined = Grab.Confined;
1252 GrabArea = Grab.Area;
1255 public void GrabWindow (X11Hwnd hwnd, X11Hwnd confine_to)
1257 IntPtr confine_to_window;
1259 confine_to_window = IntPtr.Zero;
1261 if (confine_to != null) {
1262 Console.WriteLine (Environment.StackTrace);
1264 XWindowAttributes attributes = new XWindowAttributes();
1266 Xlib.XGetWindowAttributes (display, confine_to.ClientWindow, ref attributes);
1268 Grab.Area.X = attributes.x;
1269 Grab.Area.Y = attributes.y;
1270 Grab.Area.Width = attributes.width;
1271 Grab.Area.Height = attributes.height;
1272 Grab.Confined = true;
1273 confine_to_window = confine_to.ClientWindow;
1276 Grab.Hwnd = hwnd.ClientWindow;
1278 Xlib.XGrabPointer (display, hwnd.ClientWindow, false,
1279 EventMask.ButtonPressMask | EventMask.ButtonMotionMask |
1280 EventMask.ButtonReleaseMask | EventMask.PointerMotionMask,
1281 GrabMode.GrabModeAsync, GrabMode.GrabModeAsync, confine_to_window, IntPtr.Zero, IntPtr.Zero);
1284 public void UngrabWindow (X11Hwnd hwnd)
1286 Xlib.XUngrabPointer (display, IntPtr.Zero);
1287 Xlib.XFlush (display);
1289 // XXX make sure hwnd is what should have the grab and throw if not
1290 Grab.Hwnd = IntPtr.Zero;
1291 Grab.Confined = false;
1294 #if notyet
1295 private void TranslatePropertyToClipboard (IntPtr property)
1297 IntPtr actual_atom;
1298 int actual_format;
1299 IntPtr nitems;
1300 IntPtr bytes_after;
1301 IntPtr prop = IntPtr.Zero;
1303 Clipboard.Item = null;
1305 Xlib.XGetWindowProperty (display, FosterParent.Handle,
1306 property, IntPtr.Zero, new IntPtr (0x7fffffff), true,
1307 Atoms.AnyPropertyType, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop);
1309 if ((long)nitems > 0) {
1310 if (property == Atoms.XA_STRING) {
1311 Clipboard.Item = Marshal.PtrToStringAnsi(prop);
1312 } else if (property == Atoms.XA_BITMAP) {
1313 // FIXME - convert bitmap to image
1314 } else if (property == Atoms.XA_PIXMAP) {
1315 // FIXME - convert pixmap to image
1316 } else if (property == Atoms.OEMTEXT) {
1317 Clipboard.Item = Marshal.PtrToStringAnsi(prop);
1318 } else if (property == Atoms.UNICODETEXT) {
1319 Clipboard.Item = Marshal.PtrToStringAnsi(prop);
1322 Xlib.XFree(prop);
1325 #endif
1327 // XXX should we be using @handle instead of Atoms.CLIPBOARD here?
1328 public int[] ClipboardAvailableFormats (IntPtr handle)
1330 // XXX deal with the updatemessagequeue stuff
1331 #if true
1332 return new int[0];
1333 #else
1334 DataFormats.Format f;
1335 int[] result;
1337 f = DataFormats.Format.List;
1339 if (Xlib.XGetSelectionOwner (display, Atoms.CLIPBOARD) == IntPtr.Zero) {
1340 return null;
1343 Clipboard.Formats = new ArrayList();
1345 while (f != null) {
1346 Xlib.XConvertSelection (display, Atoms.CLIPBOARD, (IntPtr)f.Id, (IntPtr)f.Id, FosterParent.Handle, IntPtr.Zero);
1348 Clipboard.Enumerating = true;
1349 while (Clipboard.Enumerating) {
1350 UpdateMessageQueue(null);
1352 f = f.Next;
1355 result = new int[Clipboard.Formats.Count];
1357 for (int i = 0; i < Clipboard.Formats.Count; i++) {
1358 result[i] = ((IntPtr)Clipboard.Formats[i]).ToInt32 ();
1361 Clipboard.Formats = null;
1362 return result;
1363 #endif
1366 public void ClipboardClose (IntPtr handle)
1368 if (handle != ClipMagic) {
1369 throw new ArgumentException("handle is not a valid clipboard handle");
1371 return;
1374 public int ClipboardGetID (IntPtr handle, string format)
1376 if (handle != ClipMagic) {
1377 throw new ArgumentException("handle is not a valid clipboard handle");
1380 if (format == "Text" ) return Atoms.XA_STRING.ToInt32();
1381 else if (format == "Bitmap" ) return Atoms.XA_BITMAP.ToInt32();
1382 //else if (format == "MetaFilePict" ) return 3;
1383 //else if (format == "SymbolicLink" ) return 4;
1384 //else if (format == "DataInterchangeFormat" ) return 5;
1385 //else if (format == "Tiff" ) return 6;
1386 else if (format == "OEMText" ) return Atoms.OEMTEXT.ToInt32();
1387 else if (format == "DeviceIndependentBitmap" ) return Atoms.XA_PIXMAP.ToInt32();
1388 else if (format == "Palette" ) return Atoms.XA_COLORMAP.ToInt32(); // Useless
1389 //else if (format == "PenData" ) return 10;
1390 //else if (format == "RiffAudio" ) return 11;
1391 //else if (format == "WaveAudio" ) return 12;
1392 else if (format == "UnicodeText" ) return Atoms.UNICODETEXT.ToInt32();
1393 //else if (format == "EnhancedMetafile" ) return 14;
1394 //else if (format == "FileDrop" ) return 15;
1395 //else if (format == "Locale" ) return 16;
1397 return Xlib.XInternAtom (display, format, false).ToInt32();
1400 public IntPtr ClipboardOpen (bool primary_selection)
1402 if (!primary_selection)
1403 ClipMagic = Atoms.CLIPBOARD;
1404 else
1405 ClipMagic = Atoms.PRIMARY;
1407 return ClipMagic;
1410 // XXX @converter?
1411 public object ClipboardRetrieve (IntPtr handle, int type, XplatUI.ClipboardToObject converter)
1413 // XXX deal with the UpdateMessageQueue stuff
1414 #if true
1415 return null;
1416 #else
1417 Xlib.XConvertSelection (display, handle, (IntPtr)type, (IntPtr)type, FosterParent, IntPtr.Zero);
1419 Clipboard.Retrieving = true;
1420 while (Clipboard.Retrieving) {
1421 UpdateMessageQueue(null);
1424 return Clipboard.Item;
1425 #endif
1428 public void ClipboardStore (IntPtr handle, object obj, int type, XplatUI.ObjectToClipboard converter)
1430 Clipboard.Item = obj;
1431 Clipboard.Type = type;
1432 Clipboard.Converter = converter;
1434 if (obj != null) {
1435 Xlib.XSetSelectionOwner (display, Atoms.CLIPBOARD, FosterParent.Handle, IntPtr.Zero);
1436 } else {
1437 // Clearing the selection
1438 Xlib.XSetSelectionOwner (display, Atoms.CLIPBOARD, IntPtr.Zero, IntPtr.Zero);
1443 public PaintEventArgs PaintEventStart (IntPtr handle, bool client)
1445 X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
1447 if (Caret.Visible == true) {
1448 Caret.Paused = true;
1449 HideCaret();
1452 return hwnd.PaintEventStart (client);
1455 public void PaintEventEnd (IntPtr handle, bool client)
1457 X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
1459 hwnd.PaintEventEnd (client);
1461 if (Caret.Visible == true) {
1462 ShowCaret();
1463 Caret.Paused = false;
1467 public void SetCursor (IntPtr handle, IntPtr cursor)
1469 Hwnd hwnd;
1471 if (OverrideCursorHandle == IntPtr.Zero) {
1472 if ((LastCursorWindow == handle) && (LastCursorHandle == cursor))
1473 return;
1475 LastCursorHandle = cursor;
1476 LastCursorWindow = handle;
1478 hwnd = Hwnd.ObjectFromHandle(handle);
1479 if (cursor != IntPtr.Zero)
1480 Xlib.XDefineCursor (display, hwnd.whole_window, cursor);
1481 else
1482 Xlib.XUndefineCursor (display, hwnd.whole_window);
1483 Xlib.XFlush (display);
1485 else {
1486 hwnd = Hwnd.ObjectFromHandle(handle);
1487 Xlib.XDefineCursor (display, hwnd.whole_window, OverrideCursorHandle);
1491 public DragDropEffects StartDrag (IntPtr handle, object data,
1492 DragDropEffects allowed_effects)
1494 X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle (handle);
1496 if (hwnd == null)
1497 throw new ArgumentException ("Attempt to begin drag from invalid window handle (" + handle.ToInt32 () + ").");
1499 return Dnd.StartDrag (hwnd.ClientWindow, data, allowed_effects);
1502 public X11Atoms Atoms {
1503 get { return atoms; }
1506 public int CurrentTimestamp {
1507 get {
1508 TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1));
1510 return (int) t.TotalSeconds;
1514 public Size CursorSize {
1515 get {
1516 int x;
1517 int y;
1519 if (Xlib.XQueryBestCursor (display, RootWindow.Handle, 32, 32, out x, out y) != 0) {
1520 return new Size (x, y);
1521 } else {
1522 return new Size (16, 16);
1527 public IntPtr Handle {
1528 get { return display; }
1531 public Size IconSize {
1532 get {
1533 IntPtr list;
1534 XIconSize size;
1535 int count;
1537 if (Xlib.XGetIconSizes (display, RootWindow.Handle, out list, out count) != 0) {
1538 long current;
1539 int largest;
1541 current = (long)list;
1542 largest = 0;
1544 size = new XIconSize();
1546 for (int i = 0; i < count; i++) {
1547 size = (XIconSize)Marshal.PtrToStructure((IntPtr)current, size.GetType());
1548 current += Marshal.SizeOf(size);
1550 // Look for our preferred size
1551 if (size.min_width == 32) {
1552 Xlib.XFree(list);
1553 return new Size(32, 32);
1556 if (size.max_width == 32) {
1557 Xlib.XFree(list);
1558 return new Size(32, 32);
1561 if (size.min_width < 32 && size.max_width > 32) {
1562 int x;
1564 // check if we can fit one
1565 x = size.min_width;
1566 while (x < size.max_width) {
1567 x += size.width_inc;
1568 if (x == 32) {
1569 Xlib.XFree(list);
1570 return new Size(32, 32);
1575 if (largest < size.max_width) {
1576 largest = size.max_width;
1580 // We didn't find a match or we wouldn't be here
1581 return new Size(largest, largest);
1583 } else {
1584 return new Size(32, 32);
1589 public int KeyboardSpeed {
1590 get {
1592 // A lot harder: need to do:
1593 // XkbQueryExtension(0x08051008, 0xbfffdf4c, 0xbfffdf50, 0xbfffdf54, 0xbfffdf58) = 1
1594 // XkbAllocKeyboard(0x08051008, 0xbfffdf4c, 0xbfffdf50, 0xbfffdf54, 0xbfffdf58) = 0x080517a8
1595 // XkbGetControls(0x08051008, 1, 0x080517a8, 0xbfffdf54, 0xbfffdf58) = 0
1597 // And from that we can tell the repetition rate
1599 // Notice, the values must map to:
1600 // [0, 31] which maps to 2.5 to 30 repetitions per second.
1602 return 0;
1606 public int KeyboardDelay {
1607 get {
1609 // Return values must range from 0 to 4, 0 meaning 250ms,
1610 // and 4 meaning 1000 ms.
1612 return 1; // ie, 500 ms
1616 public int DefaultScreen {
1617 get { return Xlib.XDefaultScreen (display); }
1620 public IntPtr DefaultColormap {
1621 // XXX multiscreen
1622 get { return Xlib.XDefaultColormap (display, DefaultScreen); }
1625 public Keys ModifierKeys {
1626 get { return Keyboard.ModifierKeys; }
1629 public IntPtr OverrideCursor {
1630 get { return OverrideCursorHandle; }
1631 set {
1632 if (Grab.Hwnd != IntPtr.Zero) {
1633 Xlib.XChangeActivePointerGrab (display,
1634 EventMask.ButtonMotionMask |
1635 EventMask.PointerMotionMask |
1636 EventMask.ButtonPressMask |
1637 EventMask.ButtonReleaseMask,
1638 value, IntPtr.Zero);
1639 return;
1642 OverrideCursorHandle = value;
1646 public X11RootHwnd RootWindow {
1647 get { return root_hwnd; }
1650 public Size SmallIconSize {
1651 get {
1652 IntPtr list;
1653 XIconSize size;
1654 int count;
1656 if (Xlib.XGetIconSizes (display, RootWindow.Handle, out list, out count) != 0) {
1657 long current;
1658 int smallest;
1660 current = (long)list;
1661 smallest = 0;
1663 size = new XIconSize();
1665 for (int i = 0; i < count; i++) {
1666 size = (XIconSize)Marshal.PtrToStructure((IntPtr)current, size.GetType());
1667 current += Marshal.SizeOf(size);
1669 // Look for our preferred size
1670 if (size.min_width == 16) {
1671 Xlib.XFree(list);
1672 return new Size(16, 16);
1675 if (size.max_width == 16) {
1676 Xlib.XFree(list);
1677 return new Size(16, 16);
1680 if (size.min_width < 16 && size.max_width > 16) {
1681 int x;
1683 // check if we can fit one
1684 x = size.min_width;
1685 while (x < size.max_width) {
1686 x += size.width_inc;
1687 if (x == 16) {
1688 Xlib.XFree(list);
1689 return new Size(16, 16);
1694 if (smallest == 0 || smallest > size.min_width) {
1695 smallest = size.min_width;
1699 // We didn't find a match or we wouldn't be here
1700 return new Size(smallest, smallest);
1702 } else {
1703 return new Size(16, 16);
1708 public X11Hwnd FosterParent {
1709 get { return foster_hwnd; }
1712 public int MouseHoverTime {
1713 get { return HoverState.Interval; }
1716 public Rectangle WorkingArea {
1717 get {
1718 IntPtr actual_atom;
1719 int actual_format;
1720 IntPtr nitems;
1721 IntPtr bytes_after;
1722 IntPtr prop = IntPtr.Zero;
1723 int width;
1724 int height;
1725 int current_desktop;
1726 int x;
1727 int y;
1729 Xlib.XGetWindowProperty (display, RootWindow.Handle,
1730 Atoms._NET_CURRENT_DESKTOP, IntPtr.Zero, new IntPtr(1), false, Atoms.XA_CARDINAL,
1731 out actual_atom, out actual_format, out nitems, out bytes_after, ref prop);
1733 if ((long)nitems < 1) {
1734 goto failsafe;
1737 current_desktop = Marshal.ReadIntPtr(prop, 0).ToInt32();
1738 Xlib.XFree(prop);
1740 Xlib.XGetWindowProperty (display, RootWindow.Handle,
1741 Atoms._NET_WORKAREA, IntPtr.Zero, new IntPtr (256), false, Atoms.XA_CARDINAL,
1742 out actual_atom, out actual_format, out nitems, out bytes_after, ref prop);
1744 if ((long)nitems < 4 * current_desktop) {
1745 goto failsafe;
1748 x = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop).ToInt32();
1749 y = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop + IntPtr.Size).ToInt32();
1750 width = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop + IntPtr.Size * 2).ToInt32();
1751 height = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop + IntPtr.Size * 3).ToInt32();
1752 Xlib.XFree(prop);
1754 return new Rectangle(x, y, width, height);
1756 failsafe:
1757 XWindowAttributes attributes = new XWindowAttributes();
1759 Xlib.XGetWindowAttributes (display, RootWindow.Handle, ref attributes);
1761 return new Rectangle(0, 0, attributes.width, attributes.height);
1765 private void XEventThread ()
1767 while (true) {
1768 #if __MonoCS__
1769 Syscall.poll (pollfds, 1U, -1);
1771 while (Xlib.XPending (display) > 0) {
1772 #endif
1773 XEvent xevent = new XEvent ();
1774 Xlib.XNextEvent (display, ref xevent);
1776 // this is kind of a gross place to put this, but we don't know about the
1777 // key repeat state in X11ThreadQueue, nor to we want the queue code calling
1778 // XPeekEvent.
1779 if (!detectable_key_auto_repeat &&
1780 xevent.type == XEventName.KeyRelease &&
1781 Xlib.XPending (display) > 0) {
1783 XEvent nextevent = new XEvent ();
1784 Xlib.XPeekEvent (display, ref nextevent);
1786 if (nextevent.type == XEventName.KeyPress &&
1787 nextevent.KeyEvent.keycode == xevent.KeyEvent.keycode &&
1788 nextevent.KeyEvent.time == xevent.KeyEvent.time) {
1789 continue;
1793 X11Hwnd hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow(xevent.AnyEvent.window);
1794 if (hwnd != null)
1795 hwnd.Queue.Enqueue (xevent);
1796 #if __MonoCS__
1798 #endif
1802 private void RedirectMsgToEnabledAncestor (X11Hwnd hwnd, MSG msg, IntPtr window,
1803 ref int event_x, ref int event_y)
1805 int x, y;
1807 IntPtr dummy;
1808 msg.hwnd = hwnd.EnabledHwnd;
1809 Xlib.XTranslateCoordinates (display, window,
1810 Hwnd.ObjectFromHandle(msg.hwnd).ClientWindow,
1811 event_x, event_y,
1812 out x, out y, out dummy);
1813 event_x = x;
1814 event_y = y;
1815 msg.lParam = (IntPtr)(MousePosition.Y << 16 | MousePosition.X);
1819 // This is called from the thread owning the corresponding X11ThreadQueue
1820 [MonoTODO("Implement filtering")]
1821 public bool GetMessage (object queue_id, ref MSG msg, IntPtr handle, int wFilterMin, int wFilterMax)
1823 X11ThreadQueue queue = (X11ThreadQueue)queue_id;
1824 XEvent xevent;
1825 bool client;
1826 bool got_xevent = false;
1828 X11Hwnd hwnd;
1830 ProcessNextMessage:
1831 do {
1832 got_xevent = queue.Dequeue (out xevent);
1834 if (!got_xevent) {
1835 #if spew
1836 Console.WriteLine (">");
1837 Console.Out.Flush ();
1838 #endif
1839 break;
1842 #if spew
1843 Console.Write ("-");
1844 Console.Out.Flush ();
1845 #endif
1847 hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (xevent.AnyEvent.window);
1849 // Handle messages for windows that are already or are about to be destroyed.
1851 // we need a special block for this because unless we remove the hwnd from the paint
1852 // queue it will always stay there (since we don't handle the expose), and we'll
1853 // effectively loop infinitely trying to repaint a non-existant window.
1854 if (hwnd != null && hwnd.zombie && xevent.type == XEventName.Expose) {
1855 hwnd.PendingExpose = hwnd.PendingNCExpose = false;
1856 goto ProcessNextMessage;
1859 // We need to make sure we only allow DestroyNotify events through for zombie
1860 // hwnds, since much of the event handling code makes requests using the hwnd's
1861 // ClientWindow, and that'll result in BadWindow errors if there's some lag
1862 // between the XDestroyWindow call and the DestroyNotify event.
1863 if (hwnd == null || hwnd.zombie) {
1864 #if DriverDebug || DriverDebugDestroy
1865 Console.WriteLine("GetMessage(): Got message {0} for non-existent or already destroyed window {1:X}",
1866 xevent.type, xevent.AnyEvent.window.ToInt32());
1867 #endif
1868 goto ProcessNextMessage;
1871 client = hwnd.ClientWindow == xevent.AnyEvent.window;
1873 msg.hwnd = hwnd.Handle;
1875 switch (xevent.type) {
1876 case XEventName.KeyPress:
1877 if (Dnd.InDrag ())
1878 Dnd.HandleKeyPress (ref xevent);
1879 Keyboard.KeyEvent (FocusWindow.Handle, xevent, ref msg);
1880 return true;
1882 case XEventName.KeyRelease:
1883 Keyboard.KeyEvent (FocusWindow.Handle, xevent, ref msg);
1884 return true;
1886 case XEventName.ButtonPress: {
1887 switch(xevent.ButtonEvent.button) {
1888 case 1:
1889 MouseState |= MouseButtons.Left;
1890 if (client) {
1891 msg.message = Msg.WM_LBUTTONDOWN;
1892 } else {
1893 msg.message = Msg.WM_NCLBUTTONDOWN;
1894 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1896 // TODO: For WM_NCLBUTTONDOWN wParam specifies a hit-test value not the virtual keys down
1897 msg.wParam=GetMousewParam(0);
1898 break;
1900 case 2:
1901 MouseState |= MouseButtons.Middle;
1902 if (client) {
1903 msg.message = Msg.WM_MBUTTONDOWN;
1904 } else {
1905 msg.message = Msg.WM_NCMBUTTONDOWN;
1906 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1908 msg.wParam=GetMousewParam(0);
1909 break;
1911 case 3:
1912 MouseState |= MouseButtons.Right;
1913 if (client) {
1914 msg.message = Msg.WM_RBUTTONDOWN;
1915 } else {
1916 msg.message = Msg.WM_NCRBUTTONDOWN;
1917 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1919 msg.wParam=GetMousewParam(0);
1920 break;
1922 case 4:
1923 msg.hwnd = FocusWindow.Handle;
1924 msg.message=Msg.WM_MOUSEWHEEL;
1925 msg.wParam=GetMousewParam(120);
1926 break;
1928 case 5:
1929 msg.hwnd = FocusWindow.Handle;
1930 msg.message=Msg.WM_MOUSEWHEEL;
1931 msg.wParam=GetMousewParam(-120);
1932 break;
1935 msg.lParam=(IntPtr) (xevent.ButtonEvent.y << 16 | xevent.ButtonEvent.x);
1936 MousePosition.X = xevent.ButtonEvent.x;
1937 MousePosition.Y = xevent.ButtonEvent.y;
1939 if (!hwnd.Enabled) {
1940 RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window,
1941 ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
1944 if (Grab.Hwnd != IntPtr.Zero)
1945 msg.hwnd = Grab.Hwnd;
1947 if (ClickPending.Pending &&
1948 ((((long)xevent.ButtonEvent.time - ClickPending.Time) < DoubleClickInterval) &&
1949 (msg.wParam == ClickPending.wParam) &&
1950 (msg.lParam == ClickPending.lParam) &&
1951 (msg.message == ClickPending.Message))) {
1952 // Looks like a genuine double click, clicked twice on the same spot with the same keys
1953 switch(xevent.ButtonEvent.button) {
1954 case 1:
1955 msg.message = client ? Msg.WM_LBUTTONDBLCLK : Msg.WM_NCLBUTTONDBLCLK;
1956 break;
1958 case 2:
1959 msg.message = client ? Msg.WM_MBUTTONDBLCLK : Msg.WM_NCMBUTTONDBLCLK;
1960 break;
1962 case 3:
1963 msg.message = client ? Msg.WM_RBUTTONDBLCLK : Msg.WM_NCRBUTTONDBLCLK;
1964 break;
1967 ClickPending.Pending = false;
1970 else {
1971 ClickPending.Pending = true;
1972 ClickPending.Hwnd = msg.hwnd;
1973 ClickPending.Message = msg.message;
1974 ClickPending.wParam = msg.wParam;
1975 ClickPending.lParam = msg.lParam;
1976 ClickPending.Time = (long)xevent.ButtonEvent.time;
1979 if (msg.message == Msg.WM_LBUTTONDOWN || msg.message == Msg.WM_MBUTTONDOWN || msg.message == Msg.WM_RBUTTONDOWN)
1980 hwnd.SendParentNotify (msg.message, MousePosition.X, MousePosition.Y);
1982 return true;
1985 case XEventName.ButtonRelease:
1986 if (Dnd.InDrag()) {
1987 if (Dnd.HandleButtonRelease (ref xevent))
1988 return true;
1989 // Don't return here, so that the BUTTONUP message can get through
1992 switch(xevent.ButtonEvent.button) {
1993 case 1:
1994 if (client) {
1995 msg.message = Msg.WM_LBUTTONUP;
1996 } else {
1997 msg.message = Msg.WM_NCLBUTTONUP;
1998 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
2000 MouseState &= ~MouseButtons.Left;
2001 msg.wParam=GetMousewParam(0);
2002 break;
2004 case 2:
2005 if (client) {
2006 msg.message = Msg.WM_MBUTTONUP;
2007 } else {
2008 msg.message = Msg.WM_NCMBUTTONUP;
2009 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
2011 MouseState &= ~MouseButtons.Middle;
2012 msg.wParam=GetMousewParam(0);
2013 break;
2015 case 3:
2016 if (client) {
2017 msg.message = Msg.WM_RBUTTONUP;
2018 } else {
2019 msg.message = Msg.WM_NCRBUTTONUP;
2020 hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
2022 MouseState &= ~MouseButtons.Right;
2023 msg.wParam=GetMousewParam(0);
2024 break;
2026 case 4:
2027 goto ProcessNextMessage;
2029 case 5:
2030 goto ProcessNextMessage;
2033 if (!hwnd.Enabled) {
2034 RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window,
2035 ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
2038 if (Grab.Hwnd != IntPtr.Zero)
2039 msg.hwnd = Grab.Hwnd;
2041 msg.lParam=(IntPtr) (xevent.ButtonEvent.y << 16 | xevent.ButtonEvent.x);
2042 MousePosition.X = xevent.ButtonEvent.x;
2043 MousePosition.Y = xevent.ButtonEvent.y;
2044 return true;
2046 case XEventName.MotionNotify:
2047 /* XXX move the compression stuff here */
2049 if (client) {
2050 #if DriverDebugExtra
2051 Console.WriteLine("GetMessage(): Window {0:X} MotionNotify x={1} y={2}",
2052 client ? hwnd.ClientWindow.ToInt32() : hwnd.WholeWindow.ToInt32(),
2053 xevent.MotionEvent.x, xevent.MotionEvent.y);
2054 #endif
2056 if (Dnd.HandleMotionNotify (ref xevent))
2057 goto ProcessNextMessage;
2059 if (Grab.Hwnd != IntPtr.Zero)
2060 msg.hwnd = Grab.Hwnd;
2061 else
2062 NativeWindow.WndProc(msg.hwnd, Msg.WM_SETCURSOR, msg.hwnd, (IntPtr)HitTest.HTCLIENT);
2064 msg.message = Msg.WM_MOUSEMOVE;
2065 msg.wParam = GetMousewParam(0);
2066 msg.lParam = (IntPtr) (xevent.MotionEvent.y << 16 | xevent.MotionEvent.x & 0xFFFF);
2068 if (!hwnd.Enabled) {
2069 RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window,
2070 ref xevent.MotionEvent.x, ref xevent.MotionEvent.y);
2073 MousePosition.X = xevent.MotionEvent.x;
2074 MousePosition.Y = xevent.MotionEvent.y;
2076 if ((HoverState.Timer.Enabled) &&
2077 (((MousePosition.X + HoverState.Size.Width) < HoverState.X) ||
2078 ((MousePosition.X - HoverState.Size.Width) > HoverState.X) ||
2079 ((MousePosition.Y + HoverState.Size.Height) < HoverState.Y) ||
2080 ((MousePosition.Y - HoverState.Size.Height) > HoverState.Y))) {
2082 HoverState.Timer.Stop();
2083 HoverState.Timer.Start();
2084 HoverState.X = MousePosition.X;
2085 HoverState.Y = MousePosition.Y;
2088 else {
2089 HitTest ht;
2090 IntPtr dummy;
2091 int screen_x;
2092 int screen_y;
2094 #if DriverDebugExtra
2095 Console.WriteLine("GetMessage(): non-client area {0:X} MotionNotify x={1} y={2}",
2096 client ? hwnd.ClientWindow.ToInt32() : hwnd.WholeWindow.ToInt32(),
2097 xevent.MotionEvent.x, xevent.MotionEvent.y);
2098 #endif
2099 msg.message = Msg.WM_NCMOUSEMOVE;
2101 if (!hwnd.Enabled) {
2102 RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window,
2103 ref xevent.MotionEvent.x, ref xevent.MotionEvent.y);
2106 // The hit test is sent in screen coordinates
2107 Xlib.XTranslateCoordinates (display, xevent.AnyEvent.window, RootWindow.Handle,
2108 xevent.MotionEvent.x, xevent.MotionEvent.y,
2109 out screen_x, out screen_y, out dummy);
2111 msg.lParam = (IntPtr) (screen_y << 16 | screen_x & 0xFFFF);
2112 ht = (HitTest)NativeWindow.WndProc (hwnd.ClientWindow, Msg.WM_NCHITTEST,
2113 IntPtr.Zero, msg.lParam).ToInt32 ();
2114 NativeWindow.WndProc(hwnd.ClientWindow, Msg.WM_SETCURSOR, msg.hwnd, (IntPtr)ht);
2116 MousePosition.X = xevent.MotionEvent.x;
2117 MousePosition.Y = xevent.MotionEvent.y;
2120 return true;
2122 case XEventName.EnterNotify:
2123 if (!hwnd.Enabled)
2124 goto ProcessNextMessage;
2126 if (xevent.CrossingEvent.mode != NotifyMode.NotifyNormal)
2127 goto ProcessNextMessage;
2129 msg.message = Msg.WM_MOUSE_ENTER;
2130 HoverState.X = xevent.CrossingEvent.x;
2131 HoverState.Y = xevent.CrossingEvent.y;
2132 HoverState.Timer.Enabled = true;
2133 HoverState.Window = xevent.CrossingEvent.window;
2135 return true;
2137 case XEventName.LeaveNotify:
2138 if (!hwnd.Enabled)
2139 goto ProcessNextMessage;
2141 if ((xevent.CrossingEvent.mode != NotifyMode.NotifyNormal) ||
2142 (xevent.CrossingEvent.window != hwnd.ClientWindow))
2143 goto ProcessNextMessage;
2145 msg.message=Msg.WM_MOUSELEAVE;
2146 HoverState.Timer.Enabled = false;
2147 HoverState.Window = IntPtr.Zero;
2149 return true;
2151 case XEventName.ReparentNotify:
2152 if (hwnd.parent == null) { // Toplevel
2153 if ((xevent.ReparentEvent.parent != IntPtr.Zero) && (xevent.ReparentEvent.window == hwnd.WholeWindow)) {
2154 // We need to adjust x/y
2155 // This sucks ass, part 2
2156 // Every WM does the reparenting of toplevel windows different, so there's
2157 // no standard way of getting our adjustment considering frames/decorations
2158 // The code below is needed for metacity. KDE doesn't works just fine without this
2159 int dummy_int;
2160 IntPtr dummy_ptr;
2161 int new_x;
2162 int new_y;
2163 int frame_left;
2164 int frame_top;
2166 hwnd.Reparented = true;
2168 Xlib.XGetGeometry(display, XGetParent(hwnd.WholeWindow),
2169 out dummy_ptr, out new_x, out new_y,
2170 out dummy_int, out dummy_int, out dummy_int, out dummy_int);
2171 hwnd.FrameExtents(out frame_left, out frame_top);
2172 if ((frame_left != 0) && (frame_top != 0) && (new_x != frame_left) && (new_y != frame_top)) {
2173 hwnd.x = new_x;
2174 hwnd.y = new_y;
2175 hwnd.whacky_wm = true;
2178 if (hwnd.opacity != 0xffffffff) {
2179 IntPtr opacity;
2181 opacity = (IntPtr)(Int32)hwnd.opacity;
2182 Xlib.XChangeProperty (display, XGetParent(hwnd.WholeWindow),
2183 Atoms._NET_WM_WINDOW_OPACITY, Atoms.XA_CARDINAL, 32,
2184 PropertyMode.Replace, ref opacity, 1);
2186 SendMessage(msg.hwnd, Msg.WM_WINDOWPOSCHANGED, msg.wParam, msg.lParam);
2187 goto ProcessNextMessage;
2188 } else {
2189 hwnd.Reparented = false;
2190 goto ProcessNextMessage;
2193 goto ProcessNextMessage;
2195 case XEventName.ConfigureNotify:
2196 hwnd.HandleConfigureNotify (xevent);
2197 goto ProcessNextMessage;
2199 case XEventName.MapNotify: {
2200 if (client && (xevent.ConfigureEvent.xevent == xevent.ConfigureEvent.window)) { // Ignore events for children (SubstructureNotify) and client areas
2201 hwnd.Mapped = true;
2202 msg.message = Msg.WM_SHOWWINDOW;
2203 msg.wParam = (IntPtr) 1;
2204 // XXX we're missing the lParam..
2205 break;
2207 goto ProcessNextMessage;
2210 case XEventName.UnmapNotify: {
2211 if (client && (xevent.ConfigureEvent.xevent == xevent.ConfigureEvent.window)) { // Ignore events for children (SubstructureNotify) and client areas
2212 hwnd.Mapped = false;
2213 msg.message = Msg.WM_SHOWWINDOW;
2214 msg.wParam = (IntPtr) 0;
2215 // XXX we're missing the lParam..
2216 break;
2218 goto ProcessNextMessage;
2221 case XEventName.FocusIn:
2222 // We received focus. We use X11 focus only to know if the app window does or does not have focus
2223 // We do not track the actual focussed window via it. Instead, this is done via FocusWindow internally
2224 // Receiving focus means we've gotten activated and therefore we need to let the actual FocusWindow know
2225 // about it having focus again
2226 if (xevent.FocusChangeEvent.detail != NotifyDetail.NotifyNonlinear)
2227 goto ProcessNextMessage;
2229 if (FocusWindow == null) {
2230 Control c = Control.FromHandle (hwnd.ClientWindow);
2231 if (c == null)
2232 goto ProcessNextMessage;
2233 Form form = c.FindForm ();
2234 if (form == null)
2235 goto ProcessNextMessage;
2236 X11Hwnd new_active = (X11Hwnd)Hwnd.ObjectFromHandle (form.Handle);
2237 if (ActiveWindow != new_active) {
2238 ActiveWindow = new_active;
2239 SendMessage (ActiveWindow.Handle, Msg.WM_ACTIVATE, (IntPtr) WindowActiveFlags.WA_ACTIVE, IntPtr.Zero);
2241 goto ProcessNextMessage;
2243 Keyboard.FocusIn(FocusWindow.Handle);
2244 SendMessage(FocusWindow.Handle, Msg.WM_SETFOCUS, IntPtr.Zero, IntPtr.Zero);
2245 goto ProcessNextMessage;
2247 case XEventName.FocusOut:
2248 // Se the comment for our FocusIn handler
2249 if (xevent.FocusChangeEvent.detail != NotifyDetail.NotifyNonlinear)
2250 goto ProcessNextMessage;
2252 if (FocusWindow == null)
2253 goto ProcessNextMessage;
2255 Keyboard.FocusOut(FocusWindow.Handle);
2257 while (Keyboard.ResetKeyState(FocusWindow.Handle, ref msg))
2258 SendMessage(FocusWindow.Handle, msg.message, msg.wParam, msg.lParam);
2260 SendMessage(FocusWindow.Handle, Msg.WM_KILLFOCUS, IntPtr.Zero, IntPtr.Zero);
2261 goto ProcessNextMessage;
2263 case XEventName.Expose:
2264 if (!hwnd.Mapped) {
2265 hwnd.PendingExpose = hwnd.PendingNCExpose = false;
2266 continue;
2269 msg.hwnd = hwnd.Handle;
2271 if (client) {
2272 #if DriverDebugExtra
2273 Console.WriteLine("GetMessage(): Window {0:X} Exposed area {1},{2} {3}x{4}",
2274 hwnd.client_window.ToInt32(),
2275 xevent.ExposeEvent.x, xevent.ExposeEvent.y,
2276 xevent.ExposeEvent.width, xevent.ExposeEvent.height);
2277 #endif
2278 msg.message = Msg.WM_PAINT;
2280 else {
2281 Graphics g;
2283 switch (hwnd.border_style) {
2284 case FormBorderStyle.Fixed3D:
2285 g = Graphics.FromHwnd(hwnd.WholeWindow);
2286 ControlPaint.DrawBorder3D(g, new Rectangle(0, 0, hwnd.Width, hwnd.Height),
2287 Border3DStyle.Sunken);
2288 g.Dispose();
2289 break;
2291 case FormBorderStyle.FixedSingle:
2292 g = Graphics.FromHwnd(hwnd.WholeWindow);
2293 ControlPaint.DrawBorder(g, new Rectangle(0, 0, hwnd.Width, hwnd.Height),
2294 Color.Black, ButtonBorderStyle.Solid);
2295 g.Dispose();
2296 break;
2298 #if DriverDebugExtra
2299 Console.WriteLine("GetMessage(): Window {0:X} Exposed non-client area {1},{2} {3}x{4}",
2300 hwnd.ClientWindow.ToInt32(),
2301 xevent.ExposeEvent.x, xevent.ExposeEvent.y,
2302 xevent.ExposeEvent.width, xevent.ExposeEvent.height);
2303 #endif
2305 Rectangle rect = new Rectangle (xevent.ExposeEvent.x, xevent.ExposeEvent.y,
2306 xevent.ExposeEvent.width, xevent.ExposeEvent.height);
2307 Region region = new Region (rect);
2308 IntPtr hrgn = region.GetHrgn (null); // Graphics object isn't needed
2309 msg.message = Msg.WM_NCPAINT;
2310 msg.wParam = hrgn == IntPtr.Zero ? (IntPtr)1 : hrgn;
2311 msg.refobject = region;
2314 return true;
2316 case XEventName.DestroyNotify:
2318 // This is a bit tricky, we don't receive our own DestroyNotify, we only get those for our children
2319 hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(xevent.DestroyWindowEvent.window);
2321 // We may get multiple for the same window, act only one the first (when Hwnd still knows about it)
2322 if ((hwnd != null) && (hwnd.ClientWindow == xevent.DestroyWindowEvent.window)) {
2323 CleanupCachedWindows (hwnd);
2325 #if DriverDebugDestroy
2326 Console.WriteLine("Received X11 Destroy Notification for {0}", XplatUI.Window(hwnd.ClientWindow));
2327 #endif
2329 msg.hwnd = hwnd.ClientWindow;
2330 msg.message=Msg.WM_DESTROY;
2331 hwnd.Dispose();
2333 else
2334 goto ProcessNextMessage;
2336 return true;
2338 case XEventName.ClientMessage:
2339 if (Dnd.HandleClientMessage (ref xevent))
2340 goto ProcessNextMessage;
2342 if (xevent.ClientMessageEvent.message_type == Atoms.AsyncAtom) {
2343 XplatUIDriverSupport.ExecuteClientMessage((GCHandle)xevent.ClientMessageEvent.ptr1);
2344 goto ProcessNextMessage;
2347 if (xevent.ClientMessageEvent.message_type == HoverState.Atom) {
2348 msg.message = Msg.WM_MOUSEHOVER;
2349 msg.wParam = GetMousewParam(0);
2350 msg.lParam = (IntPtr) (xevent.ClientMessageEvent.ptr1);
2351 return true;
2354 if (xevent.ClientMessageEvent.message_type == Atoms.PostAtom) {
2355 msg.hwnd = xevent.ClientMessageEvent.ptr1;
2356 msg.message = (Msg) xevent.ClientMessageEvent.ptr2.ToInt32 ();
2357 msg.wParam = xevent.ClientMessageEvent.ptr3;
2358 msg.lParam = xevent.ClientMessageEvent.ptr4;
2360 // if we posted a WM_QUIT message, make sure we return
2361 // false here as well.
2362 if (msg.message == (Msg)Msg.WM_QUIT)
2363 return false;
2364 else
2365 return true;
2368 if (xevent.ClientMessageEvent.message_type == Atoms._XEMBED) {
2369 #if DriverDebugXEmbed
2370 Console.WriteLine("GOT EMBED MESSAGE {0:X}, detail {1:X}",
2371 xevent.ClientMessageEvent.ptr2.ToInt32(), xevent.ClientMessageEvent.ptr3.ToInt32());
2372 #endif
2374 if (xevent.ClientMessageEvent.ptr2.ToInt32() == (int)XEmbedMessage.EmbeddedNotify) {
2375 XSizeHints hints = new XSizeHints();
2376 IntPtr dummy;
2378 Xlib.XGetWMNormalHints (display, hwnd.WholeWindow, ref hints, out dummy);
2380 hwnd.width = hints.max_width;
2381 hwnd.height = hints.max_height;
2382 hwnd.ClientRect = Rectangle.Empty;
2383 SendMessage(msg.hwnd, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero);
2387 if (xevent.ClientMessageEvent.message_type == Atoms.WM_PROTOCOLS) {
2388 if (xevent.ClientMessageEvent.ptr1 == Atoms.WM_DELETE_WINDOW) {
2389 msg.message = Msg.WM_CLOSE;
2390 return true;
2393 // We should not get this, but I'll leave the code in case we need it in the future
2394 if (xevent.ClientMessageEvent.ptr1 == Atoms.WM_TAKE_FOCUS) {
2395 goto ProcessNextMessage;
2399 goto ProcessNextMessage;
2401 case XEventName.PropertyNotify:
2402 // The Hwnd's themselves handle this
2403 hwnd.PropertyChanged (xevent);
2404 goto ProcessNextMessage;
2406 } while (true);
2408 msg.hwnd= IntPtr.Zero;
2409 msg.message = Msg.WM_ENTERIDLE;
2410 return true;
2413 [MonoTODO("Implement filtering and PM_NOREMOVE")]
2414 public bool PeekMessage (object queue_id, ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax, uint flags)
2416 X11ThreadQueue queue = (X11ThreadQueue) queue_id;
2417 bool pending;
2419 if ((flags & (uint)PeekMessageFlags.PM_REMOVE) == 0) {
2420 throw new NotImplementedException("PeekMessage PM_NOREMOVE is not implemented yet"); // FIXME - Implement PM_NOREMOVE flag
2423 try {
2424 queue.Lock ();
2425 pending = false;
2426 if (queue.CountUnlocked > 0)
2427 pending = true;
2429 catch {
2430 return false;
2432 finally {
2433 queue.Unlock ();
2436 queue.CheckTimers ();
2438 if (!pending)
2439 return false;
2441 return GetMessage(queue_id, ref msg, hWnd, wFilterMin, wFilterMax);
2444 public void DoEvents (X11ThreadQueue queue)
2446 MSG msg = new MSG ();
2448 if (OverrideCursorHandle != IntPtr.Zero)
2449 OverrideCursorHandle = IntPtr.Zero;
2451 queue.DispatchIdle = false;
2453 while (PeekMessage(queue, ref msg, IntPtr.Zero, 0, 0, (uint)PeekMessageFlags.PM_REMOVE)) {
2454 TranslateMessage (ref msg);
2455 DispatchMessage (ref msg);
2458 queue.DispatchIdle = true;
2461 // double buffering support
2462 public void CreateOffscreenDrawable (IntPtr handle,
2463 int width, int height,
2464 out object offscreen_drawable)
2466 IntPtr root_out;
2467 int x_out, y_out, width_out, height_out, border_width_out, depth_out;
2469 Xlib.XGetGeometry (display, handle,
2470 out root_out,
2471 out x_out, out y_out,
2472 out width_out, out height_out,
2473 out border_width_out, out depth_out);
2475 IntPtr pixmap = Xlib.XCreatePixmap (display, handle, width, height, depth_out);
2477 offscreen_drawable = pixmap;
2480 public void DestroyOffscreenDrawable (object offscreen_drawable)
2482 Xlib.XFreePixmap (display, (IntPtr)offscreen_drawable);
2485 public Graphics GetOffscreenGraphics (object offscreen_drawable)
2487 return Graphics.FromHwnd ((IntPtr) offscreen_drawable);
2490 public void BlitFromOffscreen (IntPtr dest_handle,
2491 Graphics dest_dc,
2492 object offscreen_drawable,
2493 Graphics offscreen_dc,
2494 Rectangle r)
2496 XGCValues gc_values;
2497 IntPtr gc;
2499 gc_values = new XGCValues();
2501 gc = Xlib.XCreateGC (display, dest_handle, IntPtr.Zero, ref gc_values);
2503 Xlib.XCopyArea (display, (IntPtr)offscreen_drawable, dest_handle,
2504 gc, r.X, r.Y, r.Width, r.Height, r.X, r.Y);
2506 Xlib.XFreeGC (display, gc);
2510 // reversible screen-level drawing
2511 IntPtr GetReversibleScreenGC (Color backColor)
2513 XGCValues gc_values;
2514 IntPtr gc;
2515 uint pixel;
2517 XColor xcolor = new XColor();
2518 xcolor.red = (ushort)(backColor.R * 257);
2519 xcolor.green = (ushort)(backColor.G * 257);
2520 xcolor.blue = (ushort)(backColor.B * 257);
2521 Xlib.XAllocColor (display, DefaultColormap, ref xcolor);
2522 pixel = (uint)xcolor.pixel.ToInt32();
2525 gc_values = new XGCValues();
2527 gc_values.subwindow_mode = GCSubwindowMode.IncludeInferiors;
2528 gc_values.foreground = (IntPtr)pixel;
2530 gc = Xlib.XCreateGC (display, RootWindow.Handle, new IntPtr ((int) (GCFunction.GCSubwindowMode | GCFunction.GCForeground)), ref gc_values);
2531 Xlib.XSetForeground (display, gc, (UIntPtr)pixel);
2532 Xlib.XSetFunction (display, gc, GXFunction.GXxor);
2534 return gc;
2537 public void DrawReversibleLine (Point start, Point end, Color backColor)
2539 IntPtr gc = GetReversibleScreenGC (backColor);
2541 Xlib.XDrawLine (display, RootWindow.Handle, gc, start.X, start.Y, end.X, end.Y);
2543 Xlib.XFreeGC (display, gc);
2546 public void FillReversibleRectangle (Rectangle rectangle, Color backColor)
2548 IntPtr gc = GetReversibleScreenGC (backColor);
2550 if (rectangle.Width < 0) {
2551 rectangle.X += rectangle.Width;
2552 rectangle.Width = -rectangle.Width;
2554 if (rectangle.Height < 0) {
2555 rectangle.Y += rectangle.Height;
2556 rectangle.Height = -rectangle.Height;
2559 Xlib.XFillRectangle (display, RootWindow.Handle, gc, rectangle.Left, rectangle.Top, rectangle.Width, rectangle.Height);
2561 Xlib.XFreeGC (display, gc);
2564 public void DrawReversibleFrame (Rectangle rectangle, Color backColor, FrameStyle style)
2566 IntPtr gc = GetReversibleScreenGC (backColor);
2568 if (rectangle.Width < 0) {
2569 rectangle.X += rectangle.Width;
2570 rectangle.Width = -rectangle.Width;
2572 if (rectangle.Height < 0) {
2573 rectangle.Y += rectangle.Height;
2574 rectangle.Height = -rectangle.Height;
2577 int line_width = 1;
2578 GCLineStyle line_style = GCLineStyle.LineSolid;
2579 GCCapStyle cap_style = GCCapStyle.CapButt;
2580 GCJoinStyle join_style = GCJoinStyle.JoinMiter;
2582 switch (style) {
2583 case FrameStyle.Dashed:
2584 line_style = GCLineStyle.LineOnOffDash;
2585 break;
2586 case FrameStyle.Thick:
2587 line_width = 2;
2588 break;
2591 Xlib.XSetLineAttributes (display, gc, line_width, line_style, cap_style, join_style);
2593 Xlib.XDrawRectangle (display, RootWindow.Handle, gc, rectangle.Left, rectangle.Top, rectangle.Width, rectangle.Height);
2595 Xlib.XFreeGC (display, gc);