ShiftOS-C-/source/ShiftUI/Internal/X11Display.cs
MichaelTheShifter d40fed5ce2 Move ShiftUI source code to ShiftOS
This'll be a lot easier to work on.
2016-07-20 09:40:36 -04:00

2693 lines
76 KiB
C#

// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Copyright (c) 2006 Novell, Inc. (http://www.novell.com)
//
//
using System;
using System.Collections;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using ShiftUI;
// Only do the poll when building with mono for now
#if __MonoCS__
//using Mono.Unix.Native;
#endif
namespace ShiftUI.X11Internal {
internal class X11Display {
IntPtr display; /* our X handle */
// XXX internal because X11Hwnd needs them
internal IntPtr CustomVisual; // Visual for window creation
internal IntPtr CustomColormap; // Colormap for window creation
X11Keyboard Keyboard;
internal X11Dnd Dnd; // XXX X11Hwnd needs it to enable Dnd
bool detectable_key_auto_repeat;
X11Atoms atoms;
X11RootHwnd root_hwnd;
X11Hwnd foster_hwnd;
// Clipboard
IntPtr ClipMagic;
// Focus tracking
internal X11Hwnd ActiveWindow;
X11Hwnd FocusWindow;
// Modality support
Stack ModalWindows; // Stack of our modal windows
// Caret
CaretStruct Caret;
// mouse hover message generation
// XXX internal because X11Atoms needs to access it..
internal HoverStruct HoverState;
// double click message generation
ClickStruct ClickPending;
int DoubleClickInterval; // msec; max interval between clicks to count as double click
// Support for mouse grab
GrabStruct Grab;
// Cursors
IntPtr LastCursorWindow; // The last window we set the cursor on
IntPtr LastCursorHandle; // The handle that was last set on LastCursorWindow
IntPtr OverrideCursorHandle; // The cursor that is set to override any other cursors
// State
Point MousePosition; // Last position of mouse, in screen coords
MouseButtons MouseState; // Last state of mouse buttons
XErrorHandler ErrorHandler; // Error handler delegate
bool ErrorExceptions; // Throw exceptions on X errors
Thread event_thread; // the background thread that just watches our X socket
#if __MonoCS__
Pollfd[] pollfds;
#endif
public X11Display (IntPtr display)
{
if (display == IntPtr.Zero) {
throw new ArgumentNullException("Display",
"Could not open display (X-Server required. Check your DISPLAY environment variable)");
}
this.display = display;
// Debugging support
if (Environment.GetEnvironmentVariable ("MONO_XSYNC") != null) {
Xlib.XSynchronize (display, true);
}
if (Environment.GetEnvironmentVariable ("MONO_XEXCEPTIONS") != null) {
ErrorExceptions = true;
}
atoms = new X11Atoms (this);
DoubleClickInterval = 500;
HoverState.Interval = 500;
HoverState.Timer = new Timer();
HoverState.Timer.Enabled = false;
HoverState.Timer.Interval = HoverState.Interval;
HoverState.Timer.Tick += new EventHandler(MouseHover);
HoverState.Size = new Size(4, 4);
HoverState.X = -1;
HoverState.Y = -1;
ActiveWindow = null;
FocusWindow = null;
ModalWindows = new Stack(3);
MouseState = MouseButtons.None;
MousePosition = new Point(0, 0);
Caret.Timer = new Timer();
Caret.Timer.Interval = 500; // FIXME - where should this number come from?
Caret.Timer.Tick += new EventHandler(CaretCallback);
// XXX multiscreen work here
root_hwnd = new X11RootHwnd (this, Xlib.XRootWindow (display, DefaultScreen));
// XXX do we need a per-screen foster parent?
// Create the foster parent
foster_hwnd = new X11Hwnd (this,
Xlib.XCreateSimpleWindow (display, root_hwnd.WholeWindow,
0, 0, 1, 1, 4, UIntPtr.Zero, UIntPtr.Zero));
#if __MonoCS__
pollfds = new Pollfd [1];
pollfds [0] = new Pollfd ();
pollfds [0].fd = Xlib.XConnectionNumber (display);
pollfds [0].events = PollEvents.POLLIN;
#endif
Keyboard = new X11Keyboard(display, foster_hwnd.Handle);
Dnd = new X11Dnd (display, Keyboard);
ErrorExceptions = false;
// Handle any upcoming errors
ErrorHandler = new XErrorHandler (HandleError);
Xlib.XSetErrorHandler (ErrorHandler);
X11DesktopColors.Initialize(); // XXX we need to figure out how to make this display specific?
// Disable keyboard autorepeat
try {
Xlib.XkbSetDetectableAutoRepeat (display, true, IntPtr.Zero);
detectable_key_auto_repeat = true;
} catch {
Console.Error.WriteLine ("Could not disable keyboard auto repeat, will attempt to disable manually.");
detectable_key_auto_repeat = false;
}
// we re-set our error handler here, X11DesktopColor stuff might have stolen it (gtk does)
Xlib.XSetErrorHandler (ErrorHandler);
// create our event thread (just sits on the X socket waiting for events)
event_thread = new Thread (new ThreadStart (XEventThread));
event_thread.IsBackground = true;
event_thread.Start ();
}
#region Callbacks
private void MouseHover(object sender, EventArgs e)
{
HoverState.Timer.Enabled = false;
if (HoverState.Window != IntPtr.Zero) {
X11Hwnd hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (HoverState.Window);
if (hwnd != null) {
XEvent xevent = new XEvent ();
xevent.type = XEventName.ClientMessage;
xevent.ClientMessageEvent.display = display;
xevent.ClientMessageEvent.window = HoverState.Window;
xevent.ClientMessageEvent.message_type = HoverState.Atom;
xevent.ClientMessageEvent.format = 32;
xevent.ClientMessageEvent.ptr1 = (IntPtr) (HoverState.Y << 16 | HoverState.X);
hwnd.Queue.Enqueue (xevent);
}
}
}
private void CaretCallback (object sender, EventArgs e)
{
if (Caret.Paused) {
return;
}
Caret.On = !Caret.On;
Xlib.XDrawLine (display, Caret.Hwnd, Caret.gc, Caret.X, Caret.Y, Caret.X, Caret.Y + Caret.Height);
}
internal string WhereString ()
{
StackTrace stack;
StackFrame frame;
string newline;
string unknown;
StringBuilder sb;
MethodBase method;
newline = String.Format("{0}\t {1} ", Environment.NewLine, "at");
unknown = "<unknown method>";
sb = new StringBuilder();
stack = new StackTrace(true);
for (int i = 0; i < stack.FrameCount; i++) {
frame = stack.GetFrame (i);
sb.Append(newline);
method = frame.GetMethod();
if (method != null) {
if (frame.GetFileLineNumber() != 0)
sb.AppendFormat ("{0}.{1} () [{2}:{3}]",
method.DeclaringType.FullName, method.Name,
Path.GetFileName(frame.GetFileName()), frame.GetFileLineNumber());
else
sb.AppendFormat ("{0}.{1} ()", method.DeclaringType.FullName, method.Name);
} else {
sb.Append(unknown);
}
}
return sb.ToString();
}
private int HandleError (IntPtr display, ref XErrorEvent error_event)
{
if (ErrorExceptions)
throw new X11Exception (error_event.display, error_event.resourceid,
error_event.serial, error_event.error_code,
error_event.request_code, error_event.minor_code);
else
Console.WriteLine ("X11 Error encountered: {0}{1}\n",
X11Exception.GetMessage(error_event.display, error_event.resourceid,
error_event.serial, error_event.error_code,
error_event.request_code, error_event.minor_code),
WhereString());
return 0;
}
#endregion // Callbacks
private void ShowCaret()
{
if ((Caret.gc == IntPtr.Zero) || Caret.On) {
return;
}
Caret.On = true;
Xlib.XDrawLine (display, Caret.Window, Caret.gc, Caret.X, Caret.Y, Caret.X, Caret.Y + Caret.Height);
}
private void HideCaret()
{
if ((Caret.gc == IntPtr.Zero) || !Caret.On) {
return;
}
Caret.On = false;
Xlib.XDrawLine (display, Caret.Window, Caret.gc, Caret.X, Caret.Y, Caret.X, Caret.Y + Caret.Height);
}
public void CaretVisible (IntPtr handle, bool visible)
{
if (Caret.Hwnd == handle) {
if (visible) {
if (!Caret.Visible) {
Caret.Visible = true;
ShowCaret();
Caret.Timer.Start();
}
} else {
Caret.Visible = false;
Caret.Timer.Stop();
HideCaret();
}
}
}
public void AudibleAlert ()
{
Xlib.XBell (display, 0);
}
public void Flush ()
{
Xlib.XFlush (display);
}
public void Close ()
{
// XXX shut down the event_thread
Xlib.XCloseDisplay (display);
}
public IntPtr XGetParent(IntPtr handle)
{
IntPtr Root;
IntPtr Parent;
IntPtr Children;
int ChildCount;
Xlib.XQueryTree (display, handle, out Root, out Parent, out Children, out ChildCount);
if (Children!=IntPtr.Zero) {
Xlib.XFree(Children);
}
return Parent;
}
public bool SystrayAdd(IntPtr handle, string tip, Icon icon, out ToolTip tt)
{
IntPtr SystrayMgrWindow;
Xlib.XGrabServer (display);
SystrayMgrWindow = Xlib.XGetSelectionOwner (display, Atoms._NET_SYSTEM_TRAY_S);
Xlib.XUngrabServer (display);
if (SystrayMgrWindow != IntPtr.Zero) {
XSizeHints size_hints;
X11Hwnd hwnd;
hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
#if DriverDebug
Console.WriteLine("Adding Systray Whole:{0:X}, Client:{1:X}",
hwnd.WholeWindow.ToInt32(), hwnd.ClientWindow.ToInt32());
#endif
// Oh boy.
if (hwnd.ClientWindow != hwnd.WholeWindow) {
Xlib.XDestroyWindow (display, hwnd.ClientWindow);
hwnd.ClientWindow = hwnd.WholeWindow;
try {
hwnd.Queue.Lock ();
/* by virtue of the way the tests are ordered when determining if it's PAINT
or NCPAINT, ClientWindow == WholeWindow will always be PAINT. So, if we're
waiting on an nc_expose, drop it and remove the hwnd from the list (unless
there's a pending expose). */
hwnd.PendingNCExpose = false;
}
finally {
hwnd.Queue.Unlock ();
}
}
size_hints = new XSizeHints();
size_hints.flags = (IntPtr)(XSizeHintsFlags.PMinSize | XSizeHintsFlags.PMaxSize | XSizeHintsFlags.PBaseSize);
size_hints.min_width = 24;
size_hints.min_height = 24;
size_hints.max_width = 24;
size_hints.max_height = 24;
size_hints.base_width = 24;
size_hints.base_height = 24;
Xlib.XSetWMNormalHints (display, hwnd.WholeWindow, ref size_hints);
int[] atoms = new int[2];
atoms [0] = 1; // Version 1
atoms [1] = 1; // we want to be mapped
// This line cost me 3 days...
Xlib.XChangeProperty (display,
hwnd.WholeWindow, Atoms._XEMBED_INFO, Atoms._XEMBED_INFO, 32,
PropertyMode.Replace, atoms, 2);
// Need to pick some reasonable defaults
tt = new ToolTip();
tt.AutomaticDelay = 100;
tt.InitialDelay = 250;
tt.ReshowDelay = 250;
tt.ShowAlways = true;
if ((tip != null) && (tip != string.Empty)) {
tt.SetToolTip(Widget.FromHandle(handle), tip);
tt.Active = true;
} else {
tt.Active = false;
}
SendNetClientMessage (SystrayMgrWindow,
Atoms._NET_SYSTEM_TRAY_OPCODE,
IntPtr.Zero,
(IntPtr)SystrayRequest.SYSTEM_TRAY_REQUEST_DOCK,
hwnd.WholeWindow);
return true;
}
tt = null;
return false;
}
public bool SystrayChange (IntPtr handle, string tip, Icon icon, ref ToolTip tt)
{
Widget Widget;
Widget = Widget.FromHandle(handle);
if (Widget != null && tt != null) {
tt.SetToolTip(Widget, tip);
tt.Active = true;
return true;
} else {
return false;
}
}
public void SystrayRemove(IntPtr handle, ref ToolTip tt)
{
#if GTKSOCKET_SUPPORTS_REPARENTING
X11Hwnd hwnd;
hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
/* in the XEMBED spec, it mentions 3 ways for a client window to break the protocol with the embedder.
* 1. The embedder can unmap the window and reparent to the root window (we should probably handle this...)
* 2. The client can reparent its window out of the embedder window.
* 3. The client can destroy its window.
*
* this call to SetParent is case 2, but in
* the spec it also mentions that gtk doesn't
* support this at present. Looking at HEAD
* gtksocket-x11.c jives with this statement.
*
* so we can't reparent. we have to destroy.
*/
SetParent(hwnd.WholeWindow, FosterParent);
#else
Widget Widget = Widget.FromHandle(handle);
if (Widget is NotifyIcon.NotifyIconWindow)
((NotifyIcon.NotifyIconWindow)Widget).InternalRecreateHandle ();
#endif
// The caller can now re-dock it later...
if (tt != null) {
tt.Dispose();
tt = null;
}
}
public void ResetMouseHover (X11Hwnd hovering)
{
HoverState.Timer.Enabled = hovering != null;
HoverState.X = MousePosition.X;
HoverState.Y = MousePosition.Y;
HoverState.Window = hovering == null ? IntPtr.Zero : hovering.Handle;
}
public void ShowCursor (bool show)
{
; // FIXME - X11 doesn't 'hide' the cursor. we could create an empty cursor
}
public void SetModal (X11Hwnd hwnd, bool Modal)
{
if (Modal) {
ModalWindows.Push(hwnd);
} else {
// XXX do we need to pop until the
// hwnd is off the stack? or just the
// most recently pushed hwnd?
if (ModalWindows.Contains(hwnd)) {
ModalWindows.Pop();
}
if (ModalWindows.Count > 0) {
X11Hwnd top_hwnd = (X11Hwnd)ModalWindows.Peek();
top_hwnd.Activate();
}
}
}
public TransparencySupport SupportsTransparency ()
{
// compiz adds _NET_WM_WINDOW_OPACITY to _NET_SUPPORTED on the root window, check for that
return ((IList)root_hwnd._NET_SUPPORTED).Contains (Atoms._NET_WM_WINDOW_OPACITY) ? TransparencySupport.GetSet : TransparencySupport.None;
}
public void SendAsyncMethod (AsyncMethodData method)
{
X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(method.Handle);
XEvent xevent = new XEvent ();
xevent.type = XEventName.ClientMessage;
xevent.ClientMessageEvent.display = display;
xevent.ClientMessageEvent.window = method.Handle;
xevent.ClientMessageEvent.message_type = Atoms.AsyncAtom;
xevent.ClientMessageEvent.format = 32;
xevent.ClientMessageEvent.ptr1 = (IntPtr) GCHandle.Alloc (method);
hwnd.Queue.Enqueue (xevent);
}
delegate IntPtr WndProcDelegate (IntPtr hwnd, Msg message, IntPtr wParam, IntPtr lParam);
public IntPtr SendMessage (IntPtr handle, Msg message, IntPtr wParam, IntPtr lParam)
{
X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
if (hwnd == null)
return IntPtr.Zero;
if (hwnd.Queue.Thread != Thread.CurrentThread) {
AsyncMethodResult result;
AsyncMethodData data;
result = new AsyncMethodResult ();
data = new AsyncMethodData ();
data.Handle = hwnd.Handle;
data.Method = new WndProcDelegate (NativeWindow.WndProc);
data.Args = new object[] { hwnd.Handle, message, wParam, lParam };
data.Result = result;
SendAsyncMethod (data);
#if DriverDebug || DriverDebugThreads
Console.WriteLine ("Sending {0} message across.", message);
#endif
return IntPtr.Zero;
}
else {
return NativeWindow.WndProc (hwnd.Handle, message, wParam, lParam);
}
}
public int SendInput (IntPtr handle, Queue keys) {
if (handle == IntPtr.Zero)
return 0;
int count = keys.Count;
Hwnd hwnd = Hwnd.ObjectFromHandle(handle);
while (keys.Count > 0) {
MSG msg = (MSG)keys.Dequeue();
XEvent xevent = new XEvent ();
xevent.type = (msg.message == Msg.WM_KEYUP ? XEventName.KeyRelease : XEventName.KeyPress);
xevent.KeyEvent.display = display;
if (hwnd != null) {
xevent.KeyEvent.window = hwnd.whole_window;
} else {
xevent.KeyEvent.window = IntPtr.Zero;
}
xevent.KeyEvent.keycode = Keyboard.ToKeycode((int)msg.wParam);
hwnd.Queue.EnqueueLocked (xevent);
}
return count;
}
// FIXME - I think this should just enqueue directly
public bool PostMessage (IntPtr handle, Msg message, IntPtr wparam, IntPtr lparam)
{
XEvent xevent = new XEvent ();
X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
xevent.type = XEventName.ClientMessage;
xevent.ClientMessageEvent.display = display;
if (hwnd != null) {
xevent.ClientMessageEvent.window = hwnd.WholeWindow;
} else {
xevent.ClientMessageEvent.window = IntPtr.Zero;
}
xevent.ClientMessageEvent.message_type = Atoms.PostAtom;
xevent.ClientMessageEvent.format = 32;
xevent.ClientMessageEvent.ptr1 = handle;
xevent.ClientMessageEvent.ptr2 = (IntPtr) message;
xevent.ClientMessageEvent.ptr3 = wparam;
xevent.ClientMessageEvent.ptr4 = lparam;
hwnd.Queue.Enqueue (xevent);
return true;
}
public void SendNetWMMessage (IntPtr window, IntPtr message_type, IntPtr l0, IntPtr l1, IntPtr l2)
{
XEvent xev;
xev = new XEvent();
xev.ClientMessageEvent.type = XEventName.ClientMessage;
xev.ClientMessageEvent.send_event = true;
xev.ClientMessageEvent.window = window;
xev.ClientMessageEvent.message_type = message_type;
xev.ClientMessageEvent.format = 32;
xev.ClientMessageEvent.ptr1 = l0;
xev.ClientMessageEvent.ptr2 = l1;
xev.ClientMessageEvent.ptr3 = l2;
Xlib.XSendEvent (display, root_hwnd.Handle, false,
new IntPtr ((int) (EventMask.SubstructureRedirectMask | EventMask.SubstructureNotifyMask)), ref xev);
}
public void SendNetClientMessage (IntPtr window, IntPtr message_type, IntPtr l0, IntPtr l1, IntPtr l2)
{
XEvent xev;
xev = new XEvent();
xev.ClientMessageEvent.type = XEventName.ClientMessage;
xev.ClientMessageEvent.send_event = true;
xev.ClientMessageEvent.window = window;
xev.ClientMessageEvent.message_type = message_type;
xev.ClientMessageEvent.format = 32;
xev.ClientMessageEvent.ptr1 = l0;
xev.ClientMessageEvent.ptr2 = l1;
xev.ClientMessageEvent.ptr3 = l2;
Xlib.XSendEvent (display, window, false, new IntPtr ((int)EventMask.NoEventMask), ref xev);
}
public bool TranslateMessage (ref MSG msg)
{
return Keyboard.TranslateMessage (ref msg);
}
public IntPtr DispatchMessage (ref MSG msg)
{
return NativeWindow.WndProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
}
private void QueryPointer (IntPtr w, out IntPtr root, out IntPtr child,
out int root_x, out int root_y, out int child_x, out int child_y,
out int mask)
{
/* this code was written with the help of
glance at gdk. I never would have realized we
needed a loop in order to traverse down in the
hierarchy. I would have assumed you'd get the
most deeply nested child and have to do
XQueryTree to move back up the hierarchy..
stupid me, of course. */
IntPtr c;
// Xlib.XGrabServer (display);
Xlib.XQueryPointer (display, w, out root, out c,
out root_x, out root_y, out child_x, out child_y,
out mask);
if (root != w)
c = root;
IntPtr child_last = IntPtr.Zero;
while (c != IntPtr.Zero) {
child_last = c;
Xlib.XQueryPointer (display, c, out root, out c,
out root_x, out root_y, out child_x, out child_y,
out mask);
}
// Xlib.XUngrabServer (display);
child = child_last;
}
public void SetCursorPos (int x, int y)
{
IntPtr root, child;
int root_x, root_y, child_x, child_y, mask;
/* we need to do a
* QueryPointer before warping
* because if the warp is on
* the RootWindow, the x/y are
* relative to the current
* mouse position
*/
QueryPointer (RootWindow.Handle,
out root,
out child,
out root_x, out root_y,
out child_x, out child_y,
out mask);
Xlib.XWarpPointer (display, IntPtr.Zero, IntPtr.Zero, 0, 0, 0, 0, x - root_x, y - root_y);
Xlib.XFlush (display);
/* then we need to a
* QueryPointer after warping
* to manually generate a
* motion event for the window
* we move into.
*/
QueryPointer (RootWindow.Handle,
out root,
out child,
out root_x, out root_y,
out child_x, out child_y,
out mask);
X11Hwnd child_hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(child);
if (child_hwnd == null)
return;
XEvent xevent = new XEvent ();
xevent.type = XEventName.MotionNotify;
xevent.MotionEvent.display = display;
xevent.MotionEvent.window = child_hwnd.Handle;
xevent.MotionEvent.root = RootWindow.Handle;
xevent.MotionEvent.x = child_x;
xevent.MotionEvent.y = child_y;
xevent.MotionEvent.x_root = root_x;
xevent.MotionEvent.y_root = root_y;
xevent.MotionEvent.state = mask;
child_hwnd.Queue.Enqueue (xevent);
}
public void SetFocus (X11Hwnd new_focus)
{
if (new_focus == FocusWindow)
return;
X11Hwnd prev_focus = FocusWindow;
FocusWindow = new_focus;
if (prev_focus != null)
SendMessage (prev_focus.Handle, Msg.WM_KILLFOCUS,
FocusWindow == null ? IntPtr.Zero : FocusWindow.Handle, IntPtr.Zero);
if (FocusWindow != null)
SendMessage (FocusWindow.Handle, Msg.WM_SETFOCUS,
prev_focus == null ? IntPtr.Zero : prev_focus.Handle, IntPtr.Zero);
//XSetInputFocus(DisplayHandle, Hwnd.ObjectFromHandle(handle).ClientWindow, RevertTo.None, IntPtr.Zero);
}
public IntPtr DefineCursor (Bitmap bitmap, Bitmap mask, Color cursor_pixel, Color mask_pixel, int xHotSpot, int yHotSpot)
{
IntPtr cursor;
Bitmap cursor_bitmap;
Bitmap cursor_mask;
Byte[] cursor_bits;
Byte[] mask_bits;
Color c_pixel;
Color m_pixel;
int width;
int height;
IntPtr cursor_pixmap;
IntPtr mask_pixmap;
XColor fg;
XColor bg;
bool and;
bool xor;
if (Xlib.XQueryBestCursor (display, RootWindow.Handle, bitmap.Width, bitmap.Height, out width, out height) == 0) {
return IntPtr.Zero;
}
// Win32 only allows creation cursors of a certain size
if ((bitmap.Width != width) || (bitmap.Width != height)) {
cursor_bitmap = new Bitmap(bitmap, new Size(width, height));
cursor_mask = new Bitmap(mask, new Size(width, height));
} else {
cursor_bitmap = bitmap;
cursor_mask = mask;
}
width = cursor_bitmap.Width;
height = cursor_bitmap.Height;
cursor_bits = new Byte[(width / 8) * height];
mask_bits = new Byte[(width / 8) * height];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
c_pixel = cursor_bitmap.GetPixel(x, y);
m_pixel = cursor_mask.GetPixel(x, y);
and = c_pixel == cursor_pixel;
xor = m_pixel == mask_pixel;
if (!and && !xor) {
// Black
// cursor_bits[y * width / 8 + x / 8] &= (byte)~((1 << (x % 8))); // The bit already is 0
mask_bits[y * width / 8 + x / 8] |= (byte)(1 << (x % 8));
} else if (and && !xor) {
// White
cursor_bits[y * width / 8 + x / 8] |= (byte)(1 << (x % 8));
mask_bits[y * width / 8 + x / 8] |= (byte)(1 << (x % 8));
#if notneeded
} else if (and && !xor) {
// Screen
} else if (and && xor) {
// Inverse Screen
// X11 doesn't know the 'reverse screen' concept, so we'll treat them the same
// we want both to be 0 so nothing to be done
//cursor_bits[y * width / 8 + x / 8] &= (byte)~((1 << (x % 8)));
//mask_bits[y * width / 8 + x / 8] |= (byte)(01 << (x % 8));
#endif
}
}
}
cursor_pixmap = Xlib.XCreatePixmapFromBitmapData (display, RootWindow.Handle,
cursor_bits, width, height, (IntPtr)1, (IntPtr)0, 1);
mask_pixmap = Xlib.XCreatePixmapFromBitmapData (display, RootWindow.Handle,
mask_bits, width, height, (IntPtr)1, (IntPtr)0, 1);
fg = new XColor();
bg = new XColor();
fg.pixel = Xlib.XWhitePixel (display, DefaultScreen);
fg.red = (ushort)65535;
fg.green = (ushort)65535;
fg.blue = (ushort)65535;
bg.pixel = Xlib.XBlackPixel (display, DefaultScreen);
cursor = Xlib.XCreatePixmapCursor (display, cursor_pixmap, mask_pixmap, ref fg, ref bg, xHotSpot, yHotSpot);
Xlib.XFreePixmap (display, cursor_pixmap);
Xlib.XFreePixmap (display, mask_pixmap);
return cursor;
}
public Bitmap DefineStdCursorBitmap (StdCursor id)
{
CursorFontShape shape;
string name;
IntPtr theme;
int size;
Bitmap bmp = null;
try {
shape = XplatUIX11.StdCursorToFontShape (id);
name = shape.ToString ().Replace ("XC_", string.Empty);
size = XplatUIX11.XcursorGetDefaultSize (Handle);
theme = XplatUIX11.XcursorGetTheme (Handle);
IntPtr images_ptr = XplatUIX11.XcursorLibraryLoadImages (name, theme, size);
#if debug
Console.WriteLine ("DefineStdCursorBitmap, id={0}, #id={1}, name{2}, size={3}, theme: {4}, images_ptr={5}", id, (int) id, name, size, Marshal.PtrToStringAnsi (theme), images_ptr);
#endif
if (images_ptr == IntPtr.Zero) {
return null;
}
XcursorImages images = (XcursorImages)Marshal.PtrToStructure (images_ptr, typeof (XcursorImages));
#if debug
Console.WriteLine ("DefineStdCursorBitmap, cursor has {0} images", images.nimage);
#endif
if (images.nimage > 0) {
// We only care about the first image.
XcursorImage image = (XcursorImage)Marshal.PtrToStructure (Marshal.ReadIntPtr (images.images), typeof (XcursorImage));
#if debug
Console.WriteLine ("DefineStdCursorBitmap, loaded image <size={0}, height={1}, width={2}, xhot={3}, yhot={4}, pixels={5}", image.size, image.height, image.width, image.xhot, image.yhot, image.pixels);
#endif
// A sanity check
if (image.width <= short.MaxValue && image.height <= short.MaxValue) {
int [] pixels = new int [image.width * image.height];
Marshal.Copy (image.pixels, pixels, 0, pixels.Length);
bmp = new Bitmap (image.width, image.height);
for (int w = 0; w < image.width; w++) {
for (int h = 0; h < image.height; h++) {
bmp.SetPixel (w, h, Color.FromArgb (pixels [h * image.width + w]));
}
}
}
}
XplatUIX11.XcursorImagesDestroy (images_ptr);
} catch (DllNotFoundException ex) {
Console.WriteLine ("Could not load libXcursor: " + ex.Message + " (" + ex.GetType ().Name + ")");
return null;
}
return bmp;
}
public IntPtr DefineStdCursor (StdCursor id)
{
CursorFontShape shape;
// FIXME - define missing shapes
switch (id) {
case StdCursor.AppStarting:
shape = CursorFontShape.XC_watch;
break;
case StdCursor.Arrow:
shape = CursorFontShape.XC_top_left_arrow;
break;
case StdCursor.Cross:
shape = CursorFontShape.XC_crosshair;
break;
case StdCursor.Default:
shape = CursorFontShape.XC_top_left_arrow;
break;
case StdCursor.Hand:
shape = CursorFontShape.XC_hand1;
break;
case StdCursor.Help:
shape = CursorFontShape.XC_question_arrow;
break;
case StdCursor.HSplit:
shape = CursorFontShape.XC_sb_v_double_arrow;
break;
case StdCursor.IBeam:
shape = CursorFontShape.XC_xterm;
break;
case StdCursor.No:
shape = CursorFontShape.XC_circle;
break;
case StdCursor.NoMove2D:
shape = CursorFontShape.XC_fleur;
break;
case StdCursor.NoMoveHoriz:
shape = CursorFontShape.XC_fleur;
break;
case StdCursor.NoMoveVert:
shape = CursorFontShape.XC_fleur;
break;
case StdCursor.PanEast:
shape = CursorFontShape.XC_fleur;
break;
case StdCursor.PanNE:
shape = CursorFontShape.XC_fleur;
break;
case StdCursor.PanNorth:
shape = CursorFontShape.XC_fleur;
break;
case StdCursor.PanNW:
shape = CursorFontShape.XC_fleur;
break;
case StdCursor.PanSE:
shape = CursorFontShape.XC_fleur;
break;
case StdCursor.PanSouth:
shape = CursorFontShape.XC_fleur;
break;
case StdCursor.PanSW:
shape = CursorFontShape.XC_fleur;
break;
case StdCursor.PanWest:
shape = CursorFontShape.XC_sizing;
break;
case StdCursor.SizeAll:
shape = CursorFontShape.XC_fleur;
break;
case StdCursor.SizeNESW:
shape = CursorFontShape.XC_top_right_corner;
break;
case StdCursor.SizeNS:
shape = CursorFontShape.XC_sb_v_double_arrow;
break;
case StdCursor.SizeNWSE:
shape = CursorFontShape.XC_top_left_corner;
break;
case StdCursor.SizeWE:
shape = CursorFontShape.XC_sb_h_double_arrow;
break;
case StdCursor.UpArrow:
shape = CursorFontShape.XC_center_ptr;
break;
case StdCursor.VSplit:
shape = CursorFontShape.XC_sb_h_double_arrow;
break;
case StdCursor.WaitCursor:
shape = CursorFontShape.XC_watch;
break;
default:
return IntPtr.Zero;
}
return Xlib.XCreateFontCursor (display, shape);
}
// XXX this should take an X11Hwnd.
public void CreateCaret (IntPtr handle, int width, int height)
{
XGCValues gc_values;
X11Hwnd hwnd;
hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
if (Caret.Hwnd != IntPtr.Zero)
DestroyCaret(Caret.Hwnd);
Caret.Hwnd = handle;
Caret.Window = hwnd.ClientWindow;
Caret.Width = width;
Caret.Height = height;
Caret.Visible = false;
Caret.On = false;
gc_values = new XGCValues();
gc_values.line_width = width;
Caret.gc = Xlib.XCreateGC (display, Caret.Window, new IntPtr ((int)GCFunction.GCLineWidth), ref gc_values);
if (Caret.gc == IntPtr.Zero) {
Caret.Hwnd = IntPtr.Zero;
return;
}
Xlib.XSetFunction (display, Caret.gc, GXFunction.GXinvert);
}
// XXX this should take an X11Hwnd.
public void DestroyCaret (IntPtr handle)
{
if (Caret.Hwnd == handle) {
if (Caret.Visible == true) {
Caret.Timer.Stop ();
}
if (Caret.gc != IntPtr.Zero) {
Xlib.XFreeGC (display, Caret.gc);
Caret.gc = IntPtr.Zero;
}
Caret.Hwnd = IntPtr.Zero;
Caret.Visible = false;
Caret.On = false;
}
}
public void SetCaretPos (IntPtr handle, int x, int y)
{
if (Caret.Hwnd == handle) {
Caret.Timer.Stop();
HideCaret();
Caret.X = x;
Caret.Y = y;
if (Caret.Visible == true) {
ShowCaret();
Caret.Timer.Start();
}
}
}
public void DestroyCursor (IntPtr cursor)
{
Xlib.XFreeCursor (display, cursor);
}
private void AccumulateDestroyedHandles (Widget c, ArrayList list)
{
if (c != null) {
Widget[] Widgets = c.Widgets.GetAllWidgets ();
if (c.IsHandleCreated && !c.IsDisposed) {
X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(c.Handle);
#if DriverDebug || DriverDebugDestroy
Console.WriteLine (" + adding {0} to the list of zombie windows", XplatUI.Window (hwnd.Handle));
Console.WriteLine (" + parent X window is {0:X}", XGetParent (hwnd.WholeWindow).ToInt32());
#endif
list.Add (hwnd);
CleanupCachedWindows (hwnd);
hwnd.zombie = true;
}
for (int i = 0; i < Widgets.Length; i ++) {
AccumulateDestroyedHandles (Widgets[i], list);
}
}
}
void CleanupCachedWindows (X11Hwnd hwnd)
{
if (ActiveWindow == hwnd) {
SendMessage (hwnd.ClientWindow, Msg.WM_ACTIVATE, (IntPtr)WindowActiveFlags.WA_INACTIVE, IntPtr.Zero);
ActiveWindow = null;
}
if (FocusWindow == hwnd) {
SendMessage (hwnd.ClientWindow, Msg.WM_KILLFOCUS, IntPtr.Zero, IntPtr.Zero);
FocusWindow = null;
}
if (Grab.Hwnd == hwnd.Handle) {
Grab.Hwnd = IntPtr.Zero;
Grab.Confined = false;
}
DestroyCaret (hwnd.Handle);
}
public void DestroyWindow (X11Hwnd hwnd)
{
CleanupCachedWindows (hwnd);
hwnd.SendParentNotify (Msg.WM_DESTROY, int.MaxValue, int.MaxValue);
ArrayList windows = new ArrayList ();
AccumulateDestroyedHandles (Widget.WidgetNativeWindow.WidgetFromHandle(hwnd.Handle), windows);
hwnd.DestroyWindow ();
foreach (X11Hwnd h in windows) {
SendMessage (h.Handle, Msg.WM_DESTROY, IntPtr.Zero, IntPtr.Zero);
}
}
public X11Hwnd GetActiveWindow ()
{
IntPtr actual_atom;
int actual_format;
IntPtr nitems;
IntPtr bytes_after;
IntPtr prop = IntPtr.Zero;
IntPtr active = IntPtr.Zero;
Xlib.XGetWindowProperty (display, RootWindow.Handle,
Atoms._NET_ACTIVE_WINDOW, IntPtr.Zero, new IntPtr (1), false,
Atoms.XA_WINDOW, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop);
if (((long)nitems > 0) && (prop != IntPtr.Zero)) {
active = (IntPtr)Marshal.ReadInt32(prop);
Xlib.XFree(prop);
}
return (X11Hwnd)Hwnd.GetObjectFromWindow(active);
}
public void SetActiveWindow (X11Hwnd new_active_window)
{
if (new_active_window != ActiveWindow) {
if (ActiveWindow != null)
PostMessage (ActiveWindow.Handle, Msg.WM_ACTIVATE,
(IntPtr)WindowActiveFlags.WA_INACTIVE, IntPtr.Zero);
ActiveWindow = new_active_window;
if (ActiveWindow != null)
PostMessage (ActiveWindow.Handle, Msg.WM_ACTIVATE,
(IntPtr)WindowActiveFlags.WA_ACTIVE, IntPtr.Zero);
}
if (ModalWindows.Count > 0) {
// Modality handling, if we are modal and the new active window is one
// of ours but not the modal one, switch back to the modal window
if (ActiveWindow != null &&
NativeWindow.FromHandle (ActiveWindow.Handle) != null) {
if (ActiveWindow != (X11Hwnd)ModalWindows.Peek())
((X11Hwnd)ModalWindows.Peek()).Activate ();
}
}
}
public void GetDisplaySize (out Size size)
{
XWindowAttributes attributes = new XWindowAttributes();
// FIXME - use _NET_WM messages instead?
Xlib.XGetWindowAttributes (display, RootWindow.Handle, ref attributes);
size = new Size(attributes.width, attributes.height);
}
// XXX this method doesn't really fit well anywhere in the backend
public SizeF GetAutoScaleSize (Font font)
{
Graphics g;
float width;
string magic_string = "The quick brown fox jumped over the lazy dog.";
double magic_number = 44.549996948242189; // XXX my god, where did this number come from?
g = Graphics.FromHwnd (FosterParent.Handle);
width = (float) (g.MeasureString (magic_string, font).Width / magic_number);
return new SizeF(width, font.Height);
}
public void GetCursorPos (X11Hwnd hwnd, out int x, out int y)
{
IntPtr use_handle;
IntPtr root;
IntPtr child;
int root_x;
int root_y;
int win_x;
int win_y;
int keys_buttons;
if (hwnd != null)
use_handle = hwnd.Handle;
else
use_handle = RootWindow.Handle;
QueryPointer (use_handle, out root, out child, out root_x, out root_y, out win_x, out win_y, out keys_buttons);
if (hwnd != null) {
x = win_x;
y = win_y;
} else {
x = root_x;
y = root_y;
}
}
public IntPtr GetFocus ()
{
return FocusWindow.Handle;
}
public IntPtr GetMousewParam (int Delta)
{
int result = 0;
if ((MouseState & MouseButtons.Left) != 0) {
result |= (int)MsgButtons.MK_LBUTTON;
}
if ((MouseState & MouseButtons.Middle) != 0) {
result |= (int)MsgButtons.MK_MBUTTON;
}
if ((MouseState & MouseButtons.Right) != 0) {
result |= (int)MsgButtons.MK_RBUTTON;
}
Keys mods = ModifierKeys;
if ((mods & Keys.Widget) != 0) {
result |= (int)MsgButtons.MK_CONTROL;
}
if ((mods & Keys.Shift) != 0) {
result |= (int)MsgButtons.MK_SHIFT;
}
result |= Delta << 16;
return (IntPtr)result;
}
public void GrabInfo (out IntPtr handle, out bool GrabConfined, out Rectangle GrabArea)
{
handle = Grab.Hwnd;
GrabConfined = Grab.Confined;
GrabArea = Grab.Area;
}
public void GrabWindow (X11Hwnd hwnd, X11Hwnd confine_to)
{
IntPtr confine_to_window;
confine_to_window = IntPtr.Zero;
if (confine_to != null) {
Console.WriteLine (Environment.StackTrace);
XWindowAttributes attributes = new XWindowAttributes();
Xlib.XGetWindowAttributes (display, confine_to.ClientWindow, ref attributes);
Grab.Area.X = attributes.x;
Grab.Area.Y = attributes.y;
Grab.Area.Width = attributes.width;
Grab.Area.Height = attributes.height;
Grab.Confined = true;
confine_to_window = confine_to.ClientWindow;
}
Grab.Hwnd = hwnd.ClientWindow;
Xlib.XGrabPointer (display, hwnd.ClientWindow, false,
EventMask.ButtonPressMask | EventMask.ButtonMotionMask |
EventMask.ButtonReleaseMask | EventMask.PointerMotionMask,
GrabMode.GrabModeAsync, GrabMode.GrabModeAsync, confine_to_window, IntPtr.Zero, IntPtr.Zero);
}
public void UngrabWindow (X11Hwnd hwnd)
{
Xlib.XUngrabPointer (display, IntPtr.Zero);
Xlib.XFlush (display);
// XXX make sure hwnd is what should have the grab and throw if not
Grab.Hwnd = IntPtr.Zero;
Grab.Confined = false;
}
#if notyet
private void TranslatePropertyToClipboard (IntPtr property)
{
IntPtr actual_atom;
int actual_format;
IntPtr nitems;
IntPtr bytes_after;
IntPtr prop = IntPtr.Zero;
Clipboard.Item = null;
Xlib.XGetWindowProperty (display, FosterParent.Handle,
property, IntPtr.Zero, new IntPtr (0x7fffffff), true,
Atoms.AnyPropertyType, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop);
if ((long)nitems > 0) {
if (property == Atoms.XA_STRING) {
Clipboard.Item = Marshal.PtrToStringAnsi(prop);
} else if (property == Atoms.XA_BITMAP) {
// FIXME - convert bitmap to image
} else if (property == Atoms.XA_PIXMAP) {
// FIXME - convert pixmap to image
} else if (property == Atoms.OEMTEXT) {
Clipboard.Item = Marshal.PtrToStringAnsi(prop);
} else if (property == Atoms.UNICODETEXT) {
Clipboard.Item = Marshal.PtrToStringAnsi(prop);
}
Xlib.XFree(prop);
}
}
#endif
// XXX should we be using @handle instead of Atoms.CLIPBOARD here?
public int[] ClipboardAvailableFormats (IntPtr handle)
{
// XXX deal with the updatemessagequeue stuff
#if true
return new int[0];
#else
DataFormats.Format f;
int[] result;
f = DataFormats.Format.List;
if (Xlib.XGetSelectionOwner (display, Atoms.CLIPBOARD) == IntPtr.Zero) {
return null;
}
Clipboard.Formats = new ArrayList();
while (f != null) {
Xlib.XConvertSelection (display, Atoms.CLIPBOARD, (IntPtr)f.Id, (IntPtr)f.Id, FosterParent.Handle, IntPtr.Zero);
Clipboard.Enumerating = true;
while (Clipboard.Enumerating) {
UpdateMessageQueue(null);
}
f = f.Next;
}
result = new int[Clipboard.Formats.Count];
for (int i = 0; i < Clipboard.Formats.Count; i++) {
result[i] = ((IntPtr)Clipboard.Formats[i]).ToInt32 ();
}
Clipboard.Formats = null;
return result;
#endif
}
public void ClipboardClose (IntPtr handle)
{
if (handle != ClipMagic) {
throw new ArgumentException("handle is not a valid clipboard handle");
}
return;
}
public int ClipboardGetID (IntPtr handle, string format)
{
if (handle != ClipMagic) {
throw new ArgumentException("handle is not a valid clipboard handle");
}
if (format == "Text" ) return Atoms.XA_STRING.ToInt32();
else if (format == "Bitmap" ) return Atoms.XA_BITMAP.ToInt32();
//else if (format == "MetaFilePict" ) return 3;
//else if (format == "SymbolicLink" ) return 4;
//else if (format == "DataInterchangeFormat" ) return 5;
//else if (format == "Tiff" ) return 6;
else if (format == "OEMText" ) return Atoms.OEMTEXT.ToInt32();
else if (format == "DeviceIndependentBitmap" ) return Atoms.XA_PIXMAP.ToInt32();
else if (format == "Palette" ) return Atoms.XA_COLORMAP.ToInt32(); // Useless
//else if (format == "PenData" ) return 10;
//else if (format == "RiffAudio" ) return 11;
//else if (format == "WaveAudio" ) return 12;
else if (format == "UnicodeText" ) return Atoms.UNICODETEXT.ToInt32();
//else if (format == "EnhancedMetafile" ) return 14;
//else if (format == "FileDrop" ) return 15;
//else if (format == "Locale" ) return 16;
return Xlib.XInternAtom (display, format, false).ToInt32();
}
public IntPtr ClipboardOpen (bool primary_selection)
{
if (!primary_selection)
ClipMagic = Atoms.CLIPBOARD;
else
ClipMagic = Atoms.PRIMARY;
return ClipMagic;
}
// XXX @converter?
public object ClipboardRetrieve (IntPtr handle, int type, XplatUI.ClipboardToObject converter)
{
// XXX deal with the UpdateMessageQueue stuff
#if true
return null;
#else
Xlib.XConvertSelection (display, handle, (IntPtr)type, (IntPtr)type, FosterParent, IntPtr.Zero);
Clipboard.Retrieving = true;
while (Clipboard.Retrieving) {
UpdateMessageQueue(null);
}
return Clipboard.Item;
#endif
}
public PaintEventArgs PaintEventStart (ref Message m, IntPtr handle, bool client)
{
X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
if (Caret.Visible == true) {
Caret.Paused = true;
HideCaret();
}
return hwnd.PaintEventStart (ref m, client);
}
public void PaintEventEnd (ref Message m, IntPtr handle, bool client)
{
X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);
hwnd.PaintEventEnd (ref m, client);
if (Caret.Visible == true) {
ShowCaret();
Caret.Paused = false;
}
}
public void SetCursor (IntPtr handle, IntPtr cursor)
{
Hwnd hwnd;
if (OverrideCursorHandle == IntPtr.Zero) {
if ((LastCursorWindow == handle) && (LastCursorHandle == cursor))
return;
LastCursorHandle = cursor;
LastCursorWindow = handle;
hwnd = Hwnd.ObjectFromHandle(handle);
if (cursor != IntPtr.Zero)
Xlib.XDefineCursor (display, hwnd.whole_window, cursor);
else
Xlib.XUndefineCursor (display, hwnd.whole_window);
Xlib.XFlush (display);
}
else {
hwnd = Hwnd.ObjectFromHandle(handle);
Xlib.XDefineCursor (display, hwnd.whole_window, OverrideCursorHandle);
}
}
public DragDropEffects StartDrag (IntPtr handle, object data,
DragDropEffects allowed_effects)
{
X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle (handle);
if (hwnd == null)
throw new ArgumentException ("Attempt to begin drag from invalid window handle (" + handle.ToInt32 () + ").");
return Dnd.StartDrag (hwnd.ClientWindow, data, allowed_effects);
}
public X11Atoms Atoms {
get { return atoms; }
}
public int CurrentTimestamp {
get {
TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1));
return (int) t.TotalSeconds;
}
}
public Size CursorSize {
get {
int x;
int y;
if (Xlib.XQueryBestCursor (display, RootWindow.Handle, 32, 32, out x, out y) != 0) {
return new Size (x, y);
} else {
return new Size (16, 16);
}
}
}
public IntPtr Handle {
get { return display; }
}
public Size IconSize {
get {
IntPtr list;
XIconSize size;
int count;
if (Xlib.XGetIconSizes (display, RootWindow.Handle, out list, out count) != 0) {
long current;
int largest;
current = (long)list;
largest = 0;
size = new XIconSize();
for (int i = 0; i < count; i++) {
size = (XIconSize)Marshal.PtrToStructure((IntPtr)current, size.GetType());
current += Marshal.SizeOf(size);
// Look for our preferred size
if (size.min_width == 32) {
Xlib.XFree(list);
return new Size(32, 32);
}
if (size.max_width == 32) {
Xlib.XFree(list);
return new Size(32, 32);
}
if (size.min_width < 32 && size.max_width > 32) {
int x;
// check if we can fit one
x = size.min_width;
while (x < size.max_width) {
x += size.width_inc;
if (x == 32) {
Xlib.XFree(list);
return new Size(32, 32);
}
}
}
if (largest < size.max_width) {
largest = size.max_width;
}
}
// We didn't find a match or we wouldn't be here
return new Size(largest, largest);
} else {
return new Size(32, 32);
}
}
}
public int KeyboardSpeed {
get {
//
// A lot harder: need to do:
// XkbQueryExtension(0x08051008, 0xbfffdf4c, 0xbfffdf50, 0xbfffdf54, 0xbfffdf58) = 1
// XkbAllocKeyboard(0x08051008, 0xbfffdf4c, 0xbfffdf50, 0xbfffdf54, 0xbfffdf58) = 0x080517a8
// XkbGetWidgets(0x08051008, 1, 0x080517a8, 0xbfffdf54, 0xbfffdf58) = 0
//
// And from that we can tell the repetition rate
//
// Notice, the values must map to:
// [0, 31] which maps to 2.5 to 30 repetitions per second.
//
return 0;
}
}
public int KeyboardDelay {
get {
//
// Return values must range from 0 to 4, 0 meaning 250ms,
// and 4 meaning 1000 ms.
//
return 1; // ie, 500 ms
}
}
public int DefaultScreen {
get { return Xlib.XDefaultScreen (display); }
}
public IntPtr DefaultColormap {
// XXX multiscreen
get { return Xlib.XDefaultColormap (display, DefaultScreen); }
}
public Keys ModifierKeys {
get { return Keyboard.ModifierKeys; }
}
public IntPtr OverrideCursor {
get { return OverrideCursorHandle; }
set {
if (Grab.Hwnd != IntPtr.Zero) {
Xlib.XChangeActivePointerGrab (display,
EventMask.ButtonMotionMask |
EventMask.PointerMotionMask |
EventMask.ButtonPressMask |
EventMask.ButtonReleaseMask,
value, IntPtr.Zero);
return;
}
OverrideCursorHandle = value;
}
}
public X11RootHwnd RootWindow {
get { return root_hwnd; }
}
public Size SmallIconSize {
get {
IntPtr list;
XIconSize size;
int count;
if (Xlib.XGetIconSizes (display, RootWindow.Handle, out list, out count) != 0) {
long current;
int smallest;
current = (long)list;
smallest = 0;
size = new XIconSize();
for (int i = 0; i < count; i++) {
size = (XIconSize)Marshal.PtrToStructure((IntPtr)current, size.GetType());
current += Marshal.SizeOf(size);
// Look for our preferred size
if (size.min_width == 16) {
Xlib.XFree(list);
return new Size(16, 16);
}
if (size.max_width == 16) {
Xlib.XFree(list);
return new Size(16, 16);
}
if (size.min_width < 16 && size.max_width > 16) {
int x;
// check if we can fit one
x = size.min_width;
while (x < size.max_width) {
x += size.width_inc;
if (x == 16) {
Xlib.XFree(list);
return new Size(16, 16);
}
}
}
if (smallest == 0 || smallest > size.min_width) {
smallest = size.min_width;
}
}
// We didn't find a match or we wouldn't be here
return new Size(smallest, smallest);
} else {
return new Size(16, 16);
}
}
}
public X11Hwnd FosterParent {
get { return foster_hwnd; }
}
public int MouseHoverTime {
get { return HoverState.Interval; }
}
public Rectangle VirtualScreen {
get {
IntPtr actual_atom;
int actual_format;
IntPtr nitems;
IntPtr bytes_after;
IntPtr prop = IntPtr.Zero;
int width;
int height;
Xlib.XGetWindowProperty (display, RootWindow.Handle,
Atoms._NET_DESKTOP_GEOMETRY, IntPtr.Zero, new IntPtr (256), false, Atoms.XA_CARDINAL,
out actual_atom, out actual_format, out nitems, out bytes_after, ref prop);
if ((long)nitems < 2)
goto failsafe;
width = Marshal.ReadIntPtr(prop, 0).ToInt32();
height = Marshal.ReadIntPtr(prop, IntPtr.Size).ToInt32();
Xlib.XFree(prop);
return new Rectangle(0, 0, width, height);
failsafe:
XWindowAttributes attributes = new XWindowAttributes();
Xlib.XGetWindowAttributes (display, RootWindow.Handle, ref attributes);
return new Rectangle(0, 0, attributes.width, attributes.height);
}
}
public Rectangle WorkingArea {
get {
IntPtr actual_atom;
int actual_format;
IntPtr nitems;
IntPtr bytes_after;
IntPtr prop = IntPtr.Zero;
int width;
int height;
int current_desktop;
int x;
int y;
Xlib.XGetWindowProperty (display, RootWindow.Handle,
Atoms._NET_CURRENT_DESKTOP, IntPtr.Zero, new IntPtr(1), false, Atoms.XA_CARDINAL,
out actual_atom, out actual_format, out nitems, out bytes_after, ref prop);
if ((long)nitems < 1) {
goto failsafe;
}
current_desktop = Marshal.ReadIntPtr(prop, 0).ToInt32();
Xlib.XFree(prop);
Xlib.XGetWindowProperty (display, RootWindow.Handle,
Atoms._NET_WORKAREA, IntPtr.Zero, new IntPtr (256), false, Atoms.XA_CARDINAL,
out actual_atom, out actual_format, out nitems, out bytes_after, ref prop);
if ((long)nitems < 4 * current_desktop) {
goto failsafe;
}
x = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop).ToInt32();
y = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop + IntPtr.Size).ToInt32();
width = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop + IntPtr.Size * 2).ToInt32();
height = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop + IntPtr.Size * 3).ToInt32();
Xlib.XFree(prop);
return new Rectangle(x, y, width, height);
failsafe:
XWindowAttributes attributes = new XWindowAttributes();
Xlib.XGetWindowAttributes (display, RootWindow.Handle, ref attributes);
return new Rectangle(0, 0, attributes.width, attributes.height);
}
}
private void XEventThread ()
{
while (true) {
#if __MonoCS__
Syscall.poll (pollfds, 1U, -1);
while (Xlib.XPending (display) > 0) {
#endif
XEvent xevent = new XEvent ();
Xlib.XNextEvent (display, ref xevent);
// this is kind of a gross place to put this, but we don't know about the
// key repeat state in X11ThreadQueue, nor to we want the queue code calling
// XPeekEvent.
if (!detectable_key_auto_repeat &&
xevent.type == XEventName.KeyRelease &&
Xlib.XPending (display) > 0) {
XEvent nextevent = new XEvent ();
Xlib.XPeekEvent (display, ref nextevent);
if (nextevent.type == XEventName.KeyPress &&
nextevent.KeyEvent.keycode == xevent.KeyEvent.keycode &&
nextevent.KeyEvent.time == xevent.KeyEvent.time) {
continue;
}
}
X11Hwnd hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow(xevent.AnyEvent.window);
if (hwnd != null)
hwnd.Queue.Enqueue (xevent);
#if __MonoCS__
}
#endif
}
}
private void RedirectMsgToEnabledAncestor (X11Hwnd hwnd, MSG msg, IntPtr window,
ref int event_x, ref int event_y)
{
int x, y;
IntPtr dummy;
msg.hwnd = hwnd.EnabledHwnd;
Xlib.XTranslateCoordinates (display, window,
Hwnd.ObjectFromHandle(msg.hwnd).ClientWindow,
event_x, event_y,
out x, out y, out dummy);
event_x = x;
event_y = y;
msg.lParam = (IntPtr)(MousePosition.Y << 16 | MousePosition.X);
}
// This is called from the thread owning the corresponding X11ThreadQueue
[MonoTODO("Implement filtering")]
public bool GetMessage (object queue_id, ref MSG msg, IntPtr handle, int wFilterMin, int wFilterMax)
{
X11ThreadQueue queue = (X11ThreadQueue)queue_id;
XEvent xevent;
bool client;
bool got_xevent = false;
X11Hwnd hwnd;
ProcessNextMessage:
do {
got_xevent = queue.Dequeue (out xevent);
if (!got_xevent) {
#if spew
Console.WriteLine (">");
Console.Out.Flush ();
#endif
break;
}
#if spew
Console.Write ("-");
Console.Out.Flush ();
#endif
hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (xevent.AnyEvent.window);
// Handle messages for windows that are already or are about to be destroyed.
// we need a special block for this because unless we remove the hwnd from the paint
// queue it will always stay there (since we don't handle the expose), and we'll
// effectively loop infinitely trying to repaint a non-existant window.
if (hwnd != null && hwnd.zombie && xevent.type == XEventName.Expose) {
hwnd.PendingExpose = hwnd.PendingNCExpose = false;
goto ProcessNextMessage;
}
// We need to make sure we only allow DestroyNotify events through for zombie
// hwnds, since much of the event handling code makes requests using the hwnd's
// ClientWindow, and that'll result in BadWindow errors if there's some lag
// between the XDestroyWindow call and the DestroyNotify event.
if (hwnd == null || hwnd.zombie) {
#if DriverDebug || DriverDebugDestroy
Console.WriteLine("GetMessage(): Got message {0} for non-existent or already destroyed window {1:X}",
xevent.type, xevent.AnyEvent.window.ToInt32());
#endif
goto ProcessNextMessage;
}
client = hwnd.ClientWindow == xevent.AnyEvent.window;
msg.hwnd = hwnd.Handle;
switch (xevent.type) {
case XEventName.KeyPress:
Keyboard.KeyEvent (FocusWindow.Handle, xevent, ref msg);
return true;
case XEventName.KeyRelease:
Keyboard.KeyEvent (FocusWindow.Handle, xevent, ref msg);
return true;
case XEventName.ButtonPress: {
switch(xevent.ButtonEvent.button) {
case 1:
MouseState |= MouseButtons.Left;
if (client) {
msg.message = Msg.WM_LBUTTONDOWN;
} else {
msg.message = Msg.WM_NCLBUTTONDOWN;
hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
}
// TODO: For WM_NCLBUTTONDOWN wParam specifies a hit-test value not the virtual keys down
msg.wParam=GetMousewParam(0);
break;
case 2:
MouseState |= MouseButtons.Middle;
if (client) {
msg.message = Msg.WM_MBUTTONDOWN;
} else {
msg.message = Msg.WM_NCMBUTTONDOWN;
hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
}
msg.wParam=GetMousewParam(0);
break;
case 3:
MouseState |= MouseButtons.Right;
if (client) {
msg.message = Msg.WM_RBUTTONDOWN;
} else {
msg.message = Msg.WM_NCRBUTTONDOWN;
hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
}
msg.wParam=GetMousewParam(0);
break;
case 4:
msg.hwnd = FocusWindow.Handle;
msg.message=Msg.WM_MOUSEWHEEL;
msg.wParam=GetMousewParam(120);
break;
case 5:
msg.hwnd = FocusWindow.Handle;
msg.message=Msg.WM_MOUSEWHEEL;
msg.wParam=GetMousewParam(-120);
break;
}
msg.lParam=(IntPtr) (xevent.ButtonEvent.y << 16 | xevent.ButtonEvent.x);
MousePosition.X = xevent.ButtonEvent.x;
MousePosition.Y = xevent.ButtonEvent.y;
if (!hwnd.Enabled) {
RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window,
ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
}
if (Grab.Hwnd != IntPtr.Zero)
msg.hwnd = Grab.Hwnd;
if (ClickPending.Pending &&
((((long)xevent.ButtonEvent.time - ClickPending.Time) < DoubleClickInterval) &&
(msg.wParam == ClickPending.wParam) &&
(msg.lParam == ClickPending.lParam) &&
(msg.message == ClickPending.Message))) {
// Looks like a genuine double click, clicked twice on the same spot with the same keys
switch(xevent.ButtonEvent.button) {
case 1:
msg.message = client ? Msg.WM_LBUTTONDBLCLK : Msg.WM_NCLBUTTONDBLCLK;
break;
case 2:
msg.message = client ? Msg.WM_MBUTTONDBLCLK : Msg.WM_NCMBUTTONDBLCLK;
break;
case 3:
msg.message = client ? Msg.WM_RBUTTONDBLCLK : Msg.WM_NCRBUTTONDBLCLK;
break;
}
ClickPending.Pending = false;
}
else {
ClickPending.Pending = true;
ClickPending.Hwnd = msg.hwnd;
ClickPending.Message = msg.message;
ClickPending.wParam = msg.wParam;
ClickPending.lParam = msg.lParam;
ClickPending.Time = (long)xevent.ButtonEvent.time;
}
if (msg.message == Msg.WM_LBUTTONDOWN || msg.message == Msg.WM_MBUTTONDOWN || msg.message == Msg.WM_RBUTTONDOWN) {
hwnd.SendParentNotify (msg.message, MousePosition.X, MousePosition.Y);
// Win32 splurts MouseMove events all over the place, regardless of whether the mouse is actually moving or
// not, especially after mousedown and mouseup. To support apps relying on mousemove events between and after
// mouse clicks to repaint or whatever, we generate a mousemove event here. *sigh*
XEvent motionEvent = new XEvent ();
motionEvent.type = XEventName.MotionNotify;
motionEvent.MotionEvent.display = display;
motionEvent.MotionEvent.window = xevent.ButtonEvent.window;
motionEvent.MotionEvent.x = xevent.ButtonEvent.x;
motionEvent.MotionEvent.y = xevent.ButtonEvent.y;
hwnd.Queue.Enqueue (motionEvent);
}
return true;
}
case XEventName.ButtonRelease:
switch(xevent.ButtonEvent.button) {
case 1:
if (client) {
msg.message = Msg.WM_LBUTTONUP;
} else {
msg.message = Msg.WM_NCLBUTTONUP;
hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
}
MouseState &= ~MouseButtons.Left;
msg.wParam=GetMousewParam(0);
break;
case 2:
if (client) {
msg.message = Msg.WM_MBUTTONUP;
} else {
msg.message = Msg.WM_NCMBUTTONUP;
hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
}
MouseState &= ~MouseButtons.Middle;
msg.wParam=GetMousewParam(0);
break;
case 3:
if (client) {
msg.message = Msg.WM_RBUTTONUP;
} else {
msg.message = Msg.WM_NCRBUTTONUP;
hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
}
MouseState &= ~MouseButtons.Right;
msg.wParam=GetMousewParam(0);
break;
case 4:
goto ProcessNextMessage;
case 5:
goto ProcessNextMessage;
}
if (!hwnd.Enabled) {
RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window,
ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
}
if (Grab.Hwnd != IntPtr.Zero)
msg.hwnd = Grab.Hwnd;
msg.lParam=(IntPtr) (xevent.ButtonEvent.y << 16 | xevent.ButtonEvent.x);
MousePosition.X = xevent.ButtonEvent.x;
MousePosition.Y = xevent.ButtonEvent.y;
// Win32 splurts MouseMove events all over the place, regardless of whether the mouse is actually moving or
// not, especially after mousedown and mouseup. To support apps relying on mousemove events between and after
// mouse clicks to repaint or whatever, we generate a mousemove event here. *sigh*
if (msg.message == Msg.WM_LBUTTONUP || msg.message == Msg.WM_MBUTTONUP || msg.message == Msg.WM_RBUTTONUP) {
XEvent motionEvent = new XEvent ();
motionEvent.type = XEventName.MotionNotify;
motionEvent.MotionEvent.display = display;
motionEvent.MotionEvent.window = xevent.ButtonEvent.window;
motionEvent.MotionEvent.x = xevent.ButtonEvent.x;
motionEvent.MotionEvent.y = xevent.ButtonEvent.y;
hwnd.Queue.Enqueue (motionEvent);
}
return true;
case XEventName.MotionNotify:
/* XXX move the compression stuff here */
if (client) {
#if DriverDebugExtra
Console.WriteLine("GetMessage(): Window {0:X} MotionNotify x={1} y={2}",
client ? hwnd.ClientWindow.ToInt32() : hwnd.WholeWindow.ToInt32(),
xevent.MotionEvent.x, xevent.MotionEvent.y);
#endif
if (Grab.Hwnd != IntPtr.Zero)
msg.hwnd = Grab.Hwnd;
else
NativeWindow.WndProc(msg.hwnd, Msg.WM_SETCURSOR, msg.hwnd, (IntPtr)HitTest.HTCLIENT);
msg.message = Msg.WM_MOUSEMOVE;
msg.wParam = GetMousewParam(0);
msg.lParam = (IntPtr) (xevent.MotionEvent.y << 16 | xevent.MotionEvent.x & 0xFFFF);
if (!hwnd.Enabled) {
RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window,
ref xevent.MotionEvent.x, ref xevent.MotionEvent.y);
}
MousePosition.X = xevent.MotionEvent.x;
MousePosition.Y = xevent.MotionEvent.y;
if ((HoverState.Timer.Enabled) &&
(((MousePosition.X + HoverState.Size.Width) < HoverState.X) ||
((MousePosition.X - HoverState.Size.Width) > HoverState.X) ||
((MousePosition.Y + HoverState.Size.Height) < HoverState.Y) ||
((MousePosition.Y - HoverState.Size.Height) > HoverState.Y))) {
HoverState.Timer.Stop();
HoverState.Timer.Start();
HoverState.X = MousePosition.X;
HoverState.Y = MousePosition.Y;
}
}
else {
HitTest ht;
IntPtr dummy;
int screen_x;
int screen_y;
#if DriverDebugExtra
Console.WriteLine("GetMessage(): non-client area {0:X} MotionNotify x={1} y={2}",
client ? hwnd.ClientWindow.ToInt32() : hwnd.WholeWindow.ToInt32(),
xevent.MotionEvent.x, xevent.MotionEvent.y);
#endif
msg.message = Msg.WM_NCMOUSEMOVE;
if (!hwnd.Enabled) {
RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window,
ref xevent.MotionEvent.x, ref xevent.MotionEvent.y);
}
// The hit test is sent in screen coordinates
Xlib.XTranslateCoordinates (display, xevent.AnyEvent.window, RootWindow.Handle,
xevent.MotionEvent.x, xevent.MotionEvent.y,
out screen_x, out screen_y, out dummy);
msg.lParam = (IntPtr) (screen_y << 16 | screen_x & 0xFFFF);
ht = (HitTest)NativeWindow.WndProc (hwnd.ClientWindow, Msg.WM_NCHITTEST,
IntPtr.Zero, msg.lParam).ToInt32 ();
NativeWindow.WndProc(hwnd.ClientWindow, Msg.WM_SETCURSOR, msg.hwnd, (IntPtr)ht);
MousePosition.X = xevent.MotionEvent.x;
MousePosition.Y = xevent.MotionEvent.y;
}
return true;
case XEventName.EnterNotify:
if (!hwnd.Enabled)
goto ProcessNextMessage;
if (xevent.CrossingEvent.mode != NotifyMode.NotifyNormal)
goto ProcessNextMessage;
msg.message = Msg.WM_MOUSE_ENTER;
HoverState.X = xevent.CrossingEvent.x;
HoverState.Y = xevent.CrossingEvent.y;
HoverState.Timer.Enabled = true;
HoverState.Window = xevent.CrossingEvent.window;
return true;
case XEventName.LeaveNotify:
if (!hwnd.Enabled)
goto ProcessNextMessage;
if ((xevent.CrossingEvent.mode != NotifyMode.NotifyNormal) ||
(xevent.CrossingEvent.window != hwnd.ClientWindow))
goto ProcessNextMessage;
msg.message=Msg.WM_MOUSELEAVE;
HoverState.Timer.Enabled = false;
HoverState.Window = IntPtr.Zero;
return true;
case XEventName.ReparentNotify:
if (hwnd.parent == null) { // Toplevel
if ((xevent.ReparentEvent.parent != IntPtr.Zero) && (xevent.ReparentEvent.window == hwnd.WholeWindow)) {
// We need to adjust x/y
// This sucks ass, part 2
// Every WM does the reparenting of toplevel windows different, so there's
// no standard way of getting our adjustment considering frames/decorations
// The code below is needed for metacity. KDE doesn't works just fine without this
int dummy_int;
IntPtr dummy_ptr;
int new_x;
int new_y;
int frame_left;
int frame_top;
hwnd.Reparented = true;
Xlib.XGetGeometry(display, XGetParent(hwnd.WholeWindow),
out dummy_ptr, out new_x, out new_y,
out dummy_int, out dummy_int, out dummy_int, out dummy_int);
hwnd.FrameExtents(out frame_left, out frame_top);
if ((frame_left != 0) && (frame_top != 0) && (new_x != frame_left) && (new_y != frame_top)) {
hwnd.x = new_x;
hwnd.y = new_y;
hwnd.whacky_wm = true;
}
if (hwnd.opacity != 0xffffffff) {
IntPtr opacity;
opacity = (IntPtr)(Int32)hwnd.opacity;
Xlib.XChangeProperty (display, XGetParent(hwnd.WholeWindow),
Atoms._NET_WM_WINDOW_OPACITY, Atoms.XA_CARDINAL, 32,
PropertyMode.Replace, ref opacity, 1);
}
SendMessage(msg.hwnd, Msg.WM_WINDOWPOSCHANGED, msg.wParam, msg.lParam);
goto ProcessNextMessage;
} else {
hwnd.Reparented = false;
goto ProcessNextMessage;
}
}
goto ProcessNextMessage;
case XEventName.ConfigureNotify:
hwnd.HandleConfigureNotify (xevent);
goto ProcessNextMessage;
case XEventName.MapNotify: {
if (client && (xevent.ConfigureEvent.xevent == xevent.ConfigureEvent.window)) { // Ignore events for children (SubstructureNotify) and client areas
hwnd.Mapped = true;
msg.message = Msg.WM_SHOWWINDOW;
msg.wParam = (IntPtr) 1;
// XXX we're missing the lParam..
break;
}
goto ProcessNextMessage;
}
case XEventName.UnmapNotify: {
if (client && (xevent.ConfigureEvent.xevent == xevent.ConfigureEvent.window)) { // Ignore events for children (SubstructureNotify) and client areas
hwnd.Mapped = false;
msg.message = Msg.WM_SHOWWINDOW;
msg.wParam = (IntPtr) 0;
// XXX we're missing the lParam..
break;
}
goto ProcessNextMessage;
}
case XEventName.FocusIn:
// We received focus. We use X11 focus only to know if the app window does or does not have focus
// We do not track the actual focussed window via it. Instead, this is done via FocusWindow internally
// Receiving focus means we've gotten activated and therefore we need to let the actual FocusWindow know
// about it having focus again
if (xevent.FocusChangeEvent.detail != NotifyDetail.NotifyNonlinear)
goto ProcessNextMessage;
if (FocusWindow == null) {
Widget c = Widget.FromHandle (hwnd.ClientWindow);
if (c == null)
goto ProcessNextMessage;
Form form = c.FindForm ();
if (form == null)
goto ProcessNextMessage;
X11Hwnd new_active = (X11Hwnd)Hwnd.ObjectFromHandle (form.Handle);
if (ActiveWindow != new_active) {
ActiveWindow = new_active;
SendMessage (ActiveWindow.Handle, Msg.WM_ACTIVATE, (IntPtr) WindowActiveFlags.WA_ACTIVE, IntPtr.Zero);
}
goto ProcessNextMessage;
}
Keyboard.FocusIn(FocusWindow.Handle);
SendMessage(FocusWindow.Handle, Msg.WM_SETFOCUS, IntPtr.Zero, IntPtr.Zero);
goto ProcessNextMessage;
case XEventName.FocusOut:
// Se the comment for our FocusIn handler
if (xevent.FocusChangeEvent.detail != NotifyDetail.NotifyNonlinear)
goto ProcessNextMessage;
if (FocusWindow == null)
goto ProcessNextMessage;
Keyboard.FocusOut(FocusWindow.Handle);
while (Keyboard.ResetKeyState(FocusWindow.Handle, ref msg))
SendMessage(FocusWindow.Handle, msg.message, msg.wParam, msg.lParam);
SendMessage(FocusWindow.Handle, Msg.WM_KILLFOCUS, IntPtr.Zero, IntPtr.Zero);
goto ProcessNextMessage;
case XEventName.Expose:
if (!hwnd.Mapped) {
hwnd.PendingExpose = hwnd.PendingNCExpose = false;
continue;
}
msg.hwnd = hwnd.Handle;
if (client) {
#if DriverDebugExtra
Console.WriteLine("GetMessage(): Window {0:X} Exposed area {1},{2} {3}x{4}",
hwnd.client_window.ToInt32(),
xevent.ExposeEvent.x, xevent.ExposeEvent.y,
xevent.ExposeEvent.width, xevent.ExposeEvent.height);
#endif
msg.message = Msg.WM_PAINT;
}
else {
Graphics g;
switch (hwnd.border_style) {
case FormBorderStyle.Fixed3D:
g = Graphics.FromHwnd(hwnd.WholeWindow);
WidgetPaint.DrawBorder3D(g, new Rectangle(0, 0, hwnd.Width, hwnd.Height),
Border3DStyle.Sunken);
g.Dispose();
break;
case FormBorderStyle.FixedSingle:
g = Graphics.FromHwnd(hwnd.WholeWindow);
WidgetPaint.DrawBorder(g, new Rectangle(0, 0, hwnd.Width, hwnd.Height),
Color.Black, ButtonBorderStyle.Solid);
g.Dispose();
break;
}
#if DriverDebugExtra
Console.WriteLine("GetMessage(): Window {0:X} Exposed non-client area {1},{2} {3}x{4}",
hwnd.ClientWindow.ToInt32(),
xevent.ExposeEvent.x, xevent.ExposeEvent.y,
xevent.ExposeEvent.width, xevent.ExposeEvent.height);
#endif
Rectangle rect = new Rectangle (xevent.ExposeEvent.x, xevent.ExposeEvent.y,
xevent.ExposeEvent.width, xevent.ExposeEvent.height);
Region region = new Region (rect);
IntPtr hrgn = region.GetHrgn (null); // Graphics object isn't needed
msg.message = Msg.WM_NCPAINT;
msg.wParam = hrgn == IntPtr.Zero ? (IntPtr)1 : hrgn;
msg.refobject = region;
}
return true;
case XEventName.DestroyNotify:
// This is a bit tricky, we don't receive our own DestroyNotify, we only get those for our children
hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(xevent.DestroyWindowEvent.window);
// We may get multiple for the same window, act only one the first (when Hwnd still knows about it)
if ((hwnd != null) && (hwnd.ClientWindow == xevent.DestroyWindowEvent.window)) {
CleanupCachedWindows (hwnd);
#if DriverDebugDestroy
Console.WriteLine("Received X11 Destroy Notification for {0}", XplatUI.Window(hwnd.ClientWindow));
#endif
msg.hwnd = hwnd.ClientWindow;
msg.message=Msg.WM_DESTROY;
hwnd.Dispose();
}
else
goto ProcessNextMessage;
return true;
case XEventName.ClientMessage:
if (Dnd.HandleClientMessage (ref xevent))
goto ProcessNextMessage;
if (xevent.ClientMessageEvent.message_type == Atoms.AsyncAtom) {
XplatUIDriverSupport.ExecuteClientMessage((GCHandle)xevent.ClientMessageEvent.ptr1);
goto ProcessNextMessage;
}
if (xevent.ClientMessageEvent.message_type == HoverState.Atom) {
msg.message = Msg.WM_MOUSEHOVER;
msg.wParam = GetMousewParam(0);
msg.lParam = (IntPtr) (xevent.ClientMessageEvent.ptr1);
return true;
}
if (xevent.ClientMessageEvent.message_type == Atoms.PostAtom) {
msg.hwnd = xevent.ClientMessageEvent.ptr1;
msg.message = (Msg) xevent.ClientMessageEvent.ptr2.ToInt32 ();
msg.wParam = xevent.ClientMessageEvent.ptr3;
msg.lParam = xevent.ClientMessageEvent.ptr4;
// if we posted a WM_QUIT message, make sure we return
// false here as well.
if (msg.message == (Msg)Msg.WM_QUIT)
return false;
else
return true;
}
if (xevent.ClientMessageEvent.message_type == Atoms._XEMBED) {
#if DriverDebugXEmbed
Console.WriteLine("GOT EMBED MESSAGE {0:X}, detail {1:X}",
xevent.ClientMessageEvent.ptr2.ToInt32(), xevent.ClientMessageEvent.ptr3.ToInt32());
#endif
if (xevent.ClientMessageEvent.ptr2.ToInt32() == (int)XEmbedMessage.EmbeddedNotify) {
XSizeHints hints = new XSizeHints();
IntPtr dummy;
Xlib.XGetWMNormalHints (display, hwnd.WholeWindow, ref hints, out dummy);
hwnd.width = hints.max_width;
hwnd.height = hints.max_height;
hwnd.ClientRect = Rectangle.Empty;
SendMessage(msg.hwnd, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero);
}
}
if (xevent.ClientMessageEvent.message_type == Atoms.WM_PROTOCOLS) {
if (xevent.ClientMessageEvent.ptr1 == Atoms.WM_DELETE_WINDOW) {
msg.message = Msg.WM_CLOSE;
return true;
}
// We should not get this, but I'll leave the code in case we need it in the future
if (xevent.ClientMessageEvent.ptr1 == Atoms.WM_TAKE_FOCUS) {
goto ProcessNextMessage;
}
}
goto ProcessNextMessage;
case XEventName.PropertyNotify:
// The Hwnd's themselves handle this
hwnd.PropertyChanged (xevent);
goto ProcessNextMessage;
}
} while (true);
msg.hwnd= IntPtr.Zero;
msg.message = Msg.WM_ENTERIDLE;
return true;
}
[MonoTODO("Implement filtering and PM_NOREMOVE")]
public bool PeekMessage (object queue_id, ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax, uint flags)
{
X11ThreadQueue queue = (X11ThreadQueue) queue_id;
bool pending;
if ((flags & (uint)PeekMessageFlags.PM_REMOVE) == 0) {
throw new NotImplementedException("PeekMessage PM_NOREMOVE is not implemented yet"); // FIXME - Implement PM_NOREMOVE flag
}
try {
queue.Lock ();
pending = false;
if (queue.CountUnlocked > 0)
pending = true;
}
catch {
return false;
}
finally {
queue.Unlock ();
}
queue.CheckTimers ();
if (!pending)
return false;
return GetMessage(queue_id, ref msg, hWnd, wFilterMin, wFilterMax);
}
public void DoEvents (X11ThreadQueue queue)
{
MSG msg = new MSG ();
if (OverrideCursorHandle != IntPtr.Zero)
OverrideCursorHandle = IntPtr.Zero;
queue.DispatchIdle = false;
while (PeekMessage(queue, ref msg, IntPtr.Zero, 0, 0, (uint)PeekMessageFlags.PM_REMOVE)) {
TranslateMessage (ref msg);
DispatchMessage (ref msg);
}
queue.DispatchIdle = true;
}
// double buffering support
public void CreateOffscreenDrawable (IntPtr handle,
int width, int height,
out object offscreen_drawable)
{
IntPtr root_out;
int x_out, y_out, width_out, height_out, border_width_out, depth_out;
Xlib.XGetGeometry (display, handle,
out root_out,
out x_out, out y_out,
out width_out, out height_out,
out border_width_out, out depth_out);
IntPtr pixmap = Xlib.XCreatePixmap (display, handle, width, height, depth_out);
offscreen_drawable = pixmap;
}
public void DestroyOffscreenDrawable (object offscreen_drawable)
{
Xlib.XFreePixmap (display, (IntPtr)offscreen_drawable);
}
public Graphics GetOffscreenGraphics (object offscreen_drawable)
{
return Graphics.FromHwnd ((IntPtr) offscreen_drawable);
}
public void BlitFromOffscreen (IntPtr dest_handle,
Graphics dest_dc,
object offscreen_drawable,
Graphics offscreen_dc,
Rectangle r)
{
XGCValues gc_values;
IntPtr gc;
gc_values = new XGCValues();
gc = Xlib.XCreateGC (display, dest_handle, IntPtr.Zero, ref gc_values);
Xlib.XCopyArea (display, (IntPtr)offscreen_drawable, dest_handle,
gc, r.X, r.Y, r.Width, r.Height, r.X, r.Y);
Xlib.XFreeGC (display, gc);
}
// reversible screen-level drawing
IntPtr GetReversibleScreenGC (Color backColor)
{
XGCValues gc_values;
IntPtr gc;
uint pixel;
XColor xcolor = new XColor();
xcolor.red = (ushort)(backColor.R * 257);
xcolor.green = (ushort)(backColor.G * 257);
xcolor.blue = (ushort)(backColor.B * 257);
Xlib.XAllocColor (display, DefaultColormap, ref xcolor);
pixel = (uint)xcolor.pixel.ToInt32();
gc_values = new XGCValues();
gc_values.subwindow_mode = GCSubwindowMode.IncludeInferiors;
gc_values.foreground = (IntPtr)pixel;
gc = Xlib.XCreateGC (display, RootWindow.Handle, new IntPtr ((int) (GCFunction.GCSubwindowMode | GCFunction.GCForeground)), ref gc_values);
Xlib.XSetForeground (display, gc, (UIntPtr)pixel);
Xlib.XSetFunction (display, gc, GXFunction.GXxor);
return gc;
}
public void DrawReversibleLine (Point start, Point end, Color backColor)
{
if (backColor.GetBrightness() < 0.5)
backColor = Color.FromArgb(255 - backColor.R, 255 - backColor.G, 255 - backColor.B);
IntPtr gc = GetReversibleScreenGC (backColor);
Xlib.XDrawLine (display, RootWindow.Handle, gc, start.X, start.Y, end.X, end.Y);
Xlib.XFreeGC (display, gc);
}
public void FillReversibleRectangle (Rectangle rectangle, Color backColor)
{
if (backColor.GetBrightness() < 0.5)
backColor = Color.FromArgb(255 - backColor.R, 255 - backColor.G, 255 - backColor.B);
IntPtr gc = GetReversibleScreenGC (backColor);
if (rectangle.Width < 0) {
rectangle.X += rectangle.Width;
rectangle.Width = -rectangle.Width;
}
if (rectangle.Height < 0) {
rectangle.Y += rectangle.Height;
rectangle.Height = -rectangle.Height;
}
Xlib.XFillRectangle (display, RootWindow.Handle, gc, rectangle.Left, rectangle.Top, rectangle.Width, rectangle.Height);
Xlib.XFreeGC (display, gc);
}
public void DrawReversibleFrame (Rectangle rectangle, Color backColor, FrameStyle style)
{
if (backColor.GetBrightness() < 0.5)
backColor = Color.FromArgb(255 - backColor.R, 255 - backColor.G, 255 - backColor.B);
IntPtr gc = GetReversibleScreenGC (backColor);
if (rectangle.Width < 0) {
rectangle.X += rectangle.Width;
rectangle.Width = -rectangle.Width;
}
if (rectangle.Height < 0) {
rectangle.Y += rectangle.Height;
rectangle.Height = -rectangle.Height;
}
int line_width = 1;
GCLineStyle line_style = GCLineStyle.LineSolid;
GCCapStyle cap_style = GCCapStyle.CapButt;
GCJoinStyle join_style = GCJoinStyle.JoinMiter;
switch (style) {
case FrameStyle.Dashed:
line_style = GCLineStyle.LineOnOffDash;
break;
case FrameStyle.Thick:
line_width = 2;
break;
}
Xlib.XSetLineAttributes (display, gc, line_width, line_style, cap_style, join_style);
Xlib.XDrawRectangle (display, RootWindow.Handle, gc, rectangle.Left, rectangle.Top, rectangle.Width, rectangle.Height);
Xlib.XFreeGC (display, gc);
}
}
}