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
, 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
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 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
);
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 public int SendInput (IntPtr handle
, Queue keys
) {
559 if (handle
== IntPtr
.Zero
)
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
;
575 xevent
.KeyEvent
.window
= hwnd
.whole_window
;
577 xevent
.KeyEvent
.window
= IntPtr
.Zero
;
580 xevent
.KeyEvent
.keycode
= Keyboard
.ToKeycode((int)msg
.wParam
);
582 hwnd
.Queue
.EnqueueLocked (xevent
);
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
;
597 xevent
.ClientMessageEvent
.window
= hwnd
.WholeWindow
;
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
);
614 public void SendNetWMMessage (IntPtr window
, IntPtr message_type
, IntPtr l0
, IntPtr l1
, IntPtr l2
)
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
)
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
,
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. */
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
,
681 IntPtr child_last
= IntPtr
.Zero
;
682 while (c
!= IntPtr
.Zero
) {
684 Xlib
.XQueryPointer (display
, c
, out root
, out c
,
685 out root_x
, out root_y
, out child_x
, out child_y
,
689 // Xlib.XUngrabServer (display);
694 public void SetCursorPos (int x
, int y
)
697 int root_x
, root_y
, child_x
, child_y
, mask
;
700 * QueryPointer before warping
701 * because if the warp is on
702 * the RootWindow, the x/y are
703 * relative to the current
706 QueryPointer (RootWindow
.Handle
,
709 out root_x
, out root_y
,
710 out child_x
, out child_y
,
713 Xlib
.XWarpPointer (display
, IntPtr
.Zero
, IntPtr
.Zero
, 0, 0, 0, 0, x
- root_x
, y
- root_y
);
715 Xlib
.XFlush (display
);
718 * QueryPointer after warping
719 * to manually generate a
720 * motion event for the window
723 QueryPointer (RootWindow
.Handle
,
726 out root_x
, out root_y
,
727 out child_x
, out child_y
,
730 X11Hwnd child_hwnd
= (X11Hwnd
)Hwnd
.ObjectFromHandle(child
);
731 if (child_hwnd
== null)
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
)
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
)
770 Bitmap cursor_bitmap
;
778 IntPtr cursor_pixmap
;
785 if (Xlib
.XQueryBestCursor (display
, RootWindow
.Handle
, bitmap
.Width
, bitmap
.Height
, out width
, out height
) == 0) {
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
));
794 cursor_bitmap
= bitmap
;
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
;
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
) {
818 cursor_bits
[y
* width
/ 8 + x
/ 8] |= (byte)(1 << (x
% 8));
819 mask_bits
[y
* width
/ 8 + x
/ 8] |= (byte)(1 << (x
% 8));
821 } else if (and
&& !xor
) {
823 } else if (and
&& xor
) {
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));
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);
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
);
857 public IntPtr
DefineStdCursor (StdCursor id
)
859 CursorFontShape shape
;
861 // FIXME - define missing shapes
864 case StdCursor
.AppStarting
:
865 shape
= CursorFontShape
.XC_watch
;
868 case StdCursor
.Arrow
:
869 shape
= CursorFontShape
.XC_top_left_arrow
;
872 case StdCursor
.Cross
:
873 shape
= CursorFontShape
.XC_crosshair
;
876 case StdCursor
.Default
:
877 shape
= CursorFontShape
.XC_top_left_arrow
;
881 shape
= CursorFontShape
.XC_hand1
;
885 shape
= CursorFontShape
.XC_question_arrow
;
888 case StdCursor
.HSplit
:
889 shape
= CursorFontShape
.XC_sb_v_double_arrow
;
892 case StdCursor
.IBeam
:
893 shape
= CursorFontShape
.XC_xterm
;
897 shape
= CursorFontShape
.XC_circle
;
900 case StdCursor
.NoMove2D
:
901 shape
= CursorFontShape
.XC_fleur
;
904 case StdCursor
.NoMoveHoriz
:
905 shape
= CursorFontShape
.XC_fleur
;
908 case StdCursor
.NoMoveVert
:
909 shape
= CursorFontShape
.XC_fleur
;
912 case StdCursor
.PanEast
:
913 shape
= CursorFontShape
.XC_fleur
;
916 case StdCursor
.PanNE
:
917 shape
= CursorFontShape
.XC_fleur
;
920 case StdCursor
.PanNorth
:
921 shape
= CursorFontShape
.XC_fleur
;
924 case StdCursor
.PanNW
:
925 shape
= CursorFontShape
.XC_fleur
;
928 case StdCursor
.PanSE
:
929 shape
= CursorFontShape
.XC_fleur
;
932 case StdCursor
.PanSouth
:
933 shape
= CursorFontShape
.XC_fleur
;
936 case StdCursor
.PanSW
:
937 shape
= CursorFontShape
.XC_fleur
;
940 case StdCursor
.PanWest
:
941 shape
= CursorFontShape
.XC_sizing
;
944 case StdCursor
.SizeAll
:
945 shape
= CursorFontShape
.XC_fleur
;
948 case StdCursor
.SizeNESW
:
949 shape
= CursorFontShape
.XC_top_right_corner
;
952 case StdCursor
.SizeNS
:
953 shape
= CursorFontShape
.XC_sb_v_double_arrow
;
956 case StdCursor
.SizeNWSE
:
957 shape
= CursorFontShape
.XC_top_left_corner
;
960 case StdCursor
.SizeWE
:
961 shape
= CursorFontShape
.XC_sb_h_double_arrow
;
964 case StdCursor
.UpArrow
:
965 shape
= CursorFontShape
.XC_center_ptr
;
968 case StdCursor
.VSplit
:
969 shape
= CursorFontShape
.XC_sb_h_double_arrow
;
972 case StdCursor
.WaitCursor
:
973 shape
= CursorFontShape
.XC_watch
;
980 return Xlib
.XCreateFontCursor (display
, shape
);
983 // XXX this should take an X11Hwnd.
984 public void CreateCaret (IntPtr handle
, int width
, int height
)
989 hwnd
= (X11Hwnd
)Hwnd
.ObjectFromHandle(handle
);
991 if (Caret
.Hwnd
!= IntPtr
.Zero
)
992 DestroyCaret(Caret
.Hwnd
);
995 Caret
.Window
= hwnd
.ClientWindow
;
997 Caret
.Height
= height
;
998 Caret
.Visible
= 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
;
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;
1031 public void SetCaretPos (IntPtr handle
, int x
, int y
)
1033 if (Caret
.Hwnd
== handle
) {
1040 if (Caret
.Visible
== true) {
1042 Caret
.Timer
.Start();
1047 public void DestroyCursor (IntPtr cursor
)
1049 Xlib
.XFreeCursor (display
, cursor
);
1052 private void AccumulateDestroyedHandles (Control c
, ArrayList list
)
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());
1066 CleanupCachedWindows (hwnd
);
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
);
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 ()
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
);
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
)
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
)
1198 use_handle
= hwnd
.Handle
;
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
);
1213 public IntPtr
GetFocus ()
1215 return FocusWindow
.Handle
;
1218 public IntPtr
GetMousewParam (int Delta
)
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
)
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;
1295 private void TranslatePropertyToClipboard (IntPtr property
)
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
);
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
1334 DataFormats
.Format f
;
1337 f
= DataFormats
.Format
.List
;
1339 if (Xlib
.XGetSelectionOwner (display
, Atoms
.CLIPBOARD
) == IntPtr
.Zero
) {
1343 Clipboard
.Formats
= new ArrayList();
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);
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;
1366 public void ClipboardClose (IntPtr handle
)
1368 if (handle
!= ClipMagic
) {
1369 throw new ArgumentException("handle is not a valid clipboard handle");
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
;
1405 ClipMagic
= Atoms
.PRIMARY
;
1411 public object ClipboardRetrieve (IntPtr handle
, int type
, XplatUI
.ClipboardToObject converter
)
1413 // XXX deal with the UpdateMessageQueue stuff
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
;
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
;
1435 Xlib
.XSetSelectionOwner (display
, Atoms
.CLIPBOARD
, FosterParent
.Handle
, IntPtr
.Zero
);
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;
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) {
1463 Caret
.Paused
= false;
1467 public void SetCursor (IntPtr handle
, IntPtr cursor
)
1471 if (OverrideCursorHandle
== IntPtr
.Zero
) {
1472 if ((LastCursorWindow
== handle
) && (LastCursorHandle
== cursor
))
1475 LastCursorHandle
= cursor
;
1476 LastCursorWindow
= handle
;
1478 hwnd
= Hwnd
.ObjectFromHandle(handle
);
1479 if (cursor
!= IntPtr
.Zero
)
1480 Xlib
.XDefineCursor (display
, hwnd
.whole_window
, cursor
);
1482 Xlib
.XUndefineCursor (display
, hwnd
.whole_window
);
1483 Xlib
.XFlush (display
);
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
);
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
{
1508 TimeSpan t
= (DateTime
.UtcNow
- new DateTime(1970, 1, 1));
1510 return (int) t
.TotalSeconds
;
1514 public Size CursorSize
{
1519 if (Xlib
.XQueryBestCursor (display
, RootWindow
.Handle
, 32, 32, out x
, out y
) != 0) {
1520 return new Size (x
, y
);
1522 return new Size (16, 16);
1527 public IntPtr Handle
{
1528 get { return display; }
1531 public Size IconSize
{
1537 if (Xlib
.XGetIconSizes (display
, RootWindow
.Handle
, out list
, out count
) != 0) {
1541 current
= (long)list
;
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) {
1553 return new Size(32, 32);
1556 if (size
.max_width
== 32) {
1558 return new Size(32, 32);
1561 if (size
.min_width
< 32 && size
.max_width
> 32) {
1564 // check if we can fit one
1566 while (x
< size
.max_width
) {
1567 x
+= size
.width_inc
;
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
);
1584 return new Size(32, 32);
1589 public int KeyboardSpeed
{
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.
1606 public int KeyboardDelay
{
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
{
1622 get { return Xlib.XDefaultColormap (display, DefaultScreen); }
1625 public Keys ModifierKeys
{
1626 get { return Keyboard.ModifierKeys; }
1629 public IntPtr OverrideCursor
{
1630 get { return OverrideCursorHandle; }
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
);
1642 OverrideCursorHandle
= value;
1646 public X11RootHwnd RootWindow
{
1647 get { return root_hwnd; }
1650 public Size SmallIconSize
{
1656 if (Xlib
.XGetIconSizes (display
, RootWindow
.Handle
, out list
, out count
) != 0) {
1660 current
= (long)list
;
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) {
1672 return new Size(16, 16);
1675 if (size
.max_width
== 16) {
1677 return new Size(16, 16);
1680 if (size
.min_width
< 16 && size
.max_width
> 16) {
1683 // check if we can fit one
1685 while (x
< size
.max_width
) {
1686 x
+= size
.width_inc
;
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
);
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
{
1722 IntPtr prop
= IntPtr
.Zero
;
1725 int current_desktop
;
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) {
1737 current_desktop
= Marshal
.ReadIntPtr(prop
, 0).ToInt32();
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
) {
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();
1754 return new Rectangle(x
, y
, width
, height
);
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 ()
1769 Syscall
.poll (pollfds
, 1U, -1);
1771 while (Xlib
.XPending (display
) > 0) {
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
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
) {
1793 X11Hwnd hwnd
= (X11Hwnd
)Hwnd
.GetObjectFromWindow(xevent
.AnyEvent
.window
);
1795 hwnd
.Queue
.Enqueue (xevent
);
1802 private void RedirectMsgToEnabledAncestor (X11Hwnd hwnd
, MSG msg
, IntPtr window
,
1803 ref int event_x
, ref int event_y
)
1808 msg
.hwnd
= hwnd
.EnabledHwnd
;
1809 Xlib
.XTranslateCoordinates (display
, window
,
1810 Hwnd
.ObjectFromHandle(msg
.hwnd
).ClientWindow
,
1812 out x
, out y
, out dummy
);
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
;
1826 bool got_xevent
= false;
1832 got_xevent
= queue
.Dequeue (out xevent
);
1836 Console
.WriteLine (">");
1837 Console
.Out
.Flush ();
1843 Console
.Write ("-");
1844 Console
.Out
.Flush ();
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());
1868 goto ProcessNextMessage
;
1871 client
= hwnd
.ClientWindow
== xevent
.AnyEvent
.window
;
1873 msg
.hwnd
= hwnd
.Handle
;
1875 switch (xevent
.type
) {
1876 case XEventName
.KeyPress
:
1878 Dnd
.HandleKeyPress (ref xevent
);
1879 Keyboard
.KeyEvent (FocusWindow
.Handle
, xevent
, ref msg
);
1882 case XEventName
.KeyRelease
:
1883 Keyboard
.KeyEvent (FocusWindow
.Handle
, xevent
, ref msg
);
1886 case XEventName
.ButtonPress
: {
1887 switch(xevent
.ButtonEvent
.button
) {
1889 MouseState
|= MouseButtons
.Left
;
1891 msg
.message
= Msg
.WM_LBUTTONDOWN
;
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);
1901 MouseState
|= MouseButtons
.Middle
;
1903 msg
.message
= Msg
.WM_MBUTTONDOWN
;
1905 msg
.message
= Msg
.WM_NCMBUTTONDOWN
;
1906 hwnd
.MenuToScreen (ref xevent
.ButtonEvent
.x
, ref xevent
.ButtonEvent
.y
);
1908 msg
.wParam
=GetMousewParam(0);
1912 MouseState
|= MouseButtons
.Right
;
1914 msg
.message
= Msg
.WM_RBUTTONDOWN
;
1916 msg
.message
= Msg
.WM_NCRBUTTONDOWN
;
1917 hwnd
.MenuToScreen (ref xevent
.ButtonEvent
.x
, ref xevent
.ButtonEvent
.y
);
1919 msg
.wParam
=GetMousewParam(0);
1923 msg
.hwnd
= FocusWindow
.Handle
;
1924 msg
.message
=Msg
.WM_MOUSEWHEEL
;
1925 msg
.wParam
=GetMousewParam(120);
1929 msg
.hwnd
= FocusWindow
.Handle
;
1930 msg
.message
=Msg
.WM_MOUSEWHEEL
;
1931 msg
.wParam
=GetMousewParam(-120);
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
) {
1955 msg
.message
= client
? Msg
.WM_LBUTTONDBLCLK
: Msg
.WM_NCLBUTTONDBLCLK
;
1959 msg
.message
= client
? Msg
.WM_MBUTTONDBLCLK
: Msg
.WM_NCMBUTTONDBLCLK
;
1963 msg
.message
= client
? Msg
.WM_RBUTTONDBLCLK
: Msg
.WM_NCRBUTTONDBLCLK
;
1967 ClickPending
.Pending
= false;
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
);
1985 case XEventName
.ButtonRelease
:
1987 if (Dnd
.HandleButtonRelease (ref xevent
))
1989 // Don't return here, so that the BUTTONUP message can get through
1992 switch(xevent
.ButtonEvent
.button
) {
1995 msg
.message
= Msg
.WM_LBUTTONUP
;
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);
2006 msg
.message
= Msg
.WM_MBUTTONUP
;
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);
2017 msg
.message
= Msg
.WM_RBUTTONUP
;
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);
2027 goto ProcessNextMessage
;
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
;
2046 case XEventName
.MotionNotify
:
2047 /* XXX move the compression stuff here */
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
);
2056 if (Dnd
.HandleMotionNotify (ref xevent
))
2057 goto ProcessNextMessage
;
2059 if (Grab
.Hwnd
!= IntPtr
.Zero
)
2060 msg
.hwnd
= Grab
.Hwnd
;
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
;
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
);
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
;
2122 case XEventName
.EnterNotify
:
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
;
2137 case XEventName
.LeaveNotify
:
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
;
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
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
)) {
2175 hwnd
.whacky_wm
= true;
2178 if (hwnd
.opacity
!= 0xffffffff) {
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
;
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
2202 msg
.message
= Msg
.WM_SHOWWINDOW
;
2203 msg
.wParam
= (IntPtr
) 1;
2204 // XXX we're missing the lParam..
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..
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
);
2232 goto ProcessNextMessage
;
2233 Form form
= c
.FindForm ();
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
:
2265 hwnd
.PendingExpose
= hwnd
.PendingNCExpose
= false;
2269 msg
.hwnd
= hwnd
.Handle
;
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
);
2278 msg
.message
= Msg
.WM_PAINT
;
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
);
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
);
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
);
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
;
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
));
2329 msg
.hwnd
= hwnd
.ClientWindow
;
2330 msg
.message
=Msg
.WM_DESTROY
;
2334 goto ProcessNextMessage
;
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
);
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
)
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());
2374 if (xevent
.ClientMessageEvent
.ptr2
.ToInt32() == (int)XEmbedMessage
.EmbeddedNotify
) {
2375 XSizeHints hints
= new XSizeHints();
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
;
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
;
2408 msg
.hwnd
= IntPtr
.Zero
;
2409 msg
.message
= Msg
.WM_ENTERIDLE
;
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
;
2419 if ((flags
& (uint)PeekMessageFlags
.PM_REMOVE
) == 0) {
2420 throw new NotImplementedException("PeekMessage PM_NOREMOVE is not implemented yet"); // FIXME - Implement PM_NOREMOVE flag
2426 if (queue
.CountUnlocked
> 0)
2436 queue
.CheckTimers ();
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
)
2467 int x_out
, y_out
, width_out
, height_out
, border_width_out
, depth_out
;
2469 Xlib
.XGetGeometry (display
, handle
,
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
,
2492 object offscreen_drawable
,
2493 Graphics offscreen_dc
,
2496 XGCValues gc_values
;
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
;
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
);
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
;
2578 GCLineStyle line_style
= GCLineStyle
.LineSolid
;
2579 GCCapStyle cap_style
= GCCapStyle
.CapButt
;
2580 GCJoinStyle join_style
= GCJoinStyle
.JoinMiter
;
2583 case FrameStyle
.Dashed
:
2584 line_style
= GCLineStyle
.LineOnOffDash
;
2586 case FrameStyle
.Thick
:
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
);