diff options
Diffstat (limited to 'source/ShiftUI/Internal/X11Hwnd.cs')
| -rw-r--r-- | source/ShiftUI/Internal/X11Hwnd.cs | 1750 |
1 files changed, 1750 insertions, 0 deletions
diff --git a/source/ShiftUI/Internal/X11Hwnd.cs b/source/ShiftUI/Internal/X11Hwnd.cs new file mode 100644 index 0000000..0ef248d --- /dev/null +++ b/source/ShiftUI/Internal/X11Hwnd.cs @@ -0,0 +1,1750 @@ +// Permission is hereby granted, free of charge, to any person obtaining +// 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.Drawing; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using ShiftUI; + +namespace ShiftUI.X11Internal { + + internal class X11Hwnd : Hwnd + { + X11Display display; + + bool refetch_window_type = true; + bool refetch_window_opacity = true; + + IntPtr[] wm_state = new IntPtr[0]; + IntPtr[] window_type = new IntPtr[0]; + double trans = 1.0; + + string text; + new X11ThreadQueue queue; + + const EventMask SelectInputMask = (EventMask.ButtonPressMask | + EventMask.ButtonReleaseMask | + EventMask.KeyPressMask | + EventMask.KeyReleaseMask | + EventMask.EnterWindowMask | + EventMask.LeaveWindowMask | + EventMask.ExposureMask | + EventMask.FocusChangeMask | + EventMask.PointerMotionMask | + EventMask.SubstructureNotifyMask); + + public X11Hwnd (X11Display display) + { + this.display = display; + Queue = XplatUIX11_new.GetInstance().ThreadQueue(Thread.CurrentThread); + } + + public X11Hwnd (X11Display display, IntPtr handle) : this (display) + { + if (handle == IntPtr.Zero) + throw new ArgumentNullException ("handle"); + WholeWindow = ClientWindow = handle; + } + + // XXX this needs to be here so we don't have to + // change Hwnd. once we land, remove this and make + // Hwnd.Queue virtual or abstract + public new X11ThreadQueue Queue { + get { return queue; } + set { queue = value; } + } + + public virtual void CreateWindow (CreateParams cp) + { + if (WholeWindow != IntPtr.Zero || ClientWindow != IntPtr.Zero) + throw new Exception ("createwindow called a second time on live X11Hwnd"); + + XSetWindowAttributes Attributes; + int x; + int y; + int width; + int height; + IntPtr ParentHandle; + SetWindowValuemask ValueMask; + + Attributes = new XSetWindowAttributes(); + x = cp.X; + y = cp.Y; + width = cp.Width; + height = cp.Height; + initial_ex_style = (WindowExStyles) cp.ExStyle; + + /* Figure out our parent handle */ + if (cp.Parent != IntPtr.Zero) + // the parent handle is specified in the CreateParams + ParentHandle = Hwnd.ObjectFromHandle(cp.Parent).ClientWindow; + else if (StyleSet (cp.Style, WindowStyles.WS_CHILD)) + // a child Widget with an unassigned parent gets created under the FosterParent + ParentHandle = display.FosterParent.Handle; + else + // for all other cases, the parent is the root window + ParentHandle = display.RootWindow.Handle; + + ValueMask = SetWindowValuemask.BitGravity | SetWindowValuemask.WinGravity; + + Attributes.bit_gravity = Gravity.NorthWestGravity; + Attributes.win_gravity = Gravity.NorthWestGravity; + + // Save what's under the toolwindow + if (ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) { + Attributes.save_under = true; + ValueMask |= SetWindowValuemask.SaveUnder; + } + + // If we're a popup without caption we override the WM + if (StyleSet (cp.Style, WindowStyles.WS_POPUP) && !StyleSet (cp.Style, WindowStyles.WS_CAPTION)) { + Attributes.override_redirect = true; + ValueMask |= SetWindowValuemask.OverrideRedirect; + } + + // Default position on screen, if window manager doesn't place us somewhere else + if (!StyleSet (cp.Style, WindowStyles.WS_CHILD) + && !StyleSet (cp.Style, WindowStyles.WS_POPUP)) { + if (x<0) x = 50; + if (y<0) y = 50; + } + // minimum width/height + if (width<1) width=1; + if (height<1) height=1; + + X = x; + Y = y; + Width = width; + Height = height; + Parent = Hwnd.ObjectFromHandle (cp.Parent); + + Enabled = !StyleSet (cp.Style, WindowStyles.WS_DISABLED); + + ClientWindow = IntPtr.Zero; + + WholeWindow = Xlib.XCreateWindow (display.Handle, ParentHandle, + X, Y, Width, Height, 0, + (int)CreateWindowArgs.CopyFromParent, (int)CreateWindowArgs.InputOutput, + IntPtr.Zero, new UIntPtr ((uint)ValueMask), ref Attributes); + if (WholeWindow == IntPtr.Zero) + throw new Exception ("Coult not create X11 nc window"); + + ValueMask &= ~(SetWindowValuemask.OverrideRedirect | SetWindowValuemask.SaveUnder); + + if (display.CustomVisual != IntPtr.Zero && display.CustomColormap != IntPtr.Zero) { + ValueMask |= SetWindowValuemask.ColorMap; + Attributes.colormap = display.CustomColormap; + } + + ClientWindow = Xlib.XCreateWindow (display.Handle, WholeWindow, + ClientRect.X, ClientRect.Y, ClientRect.Width, ClientRect.Height, 0, + (int)CreateWindowArgs.CopyFromParent, (int)CreateWindowArgs.InputOutput, + display.CustomVisual, new UIntPtr ((uint)ValueMask), ref Attributes); + + if (ClientWindow == IntPtr.Zero) + throw new Exception("Could not create X11 client window"); + +#if DriverDebug || DriverDebugCreate + Console.WriteLine("Created window {0:X} / {1:X} parent {2:X}, Style {3}, ExStyle {4}", ClientWindow.ToInt32(), WholeWindow.ToInt32(), Parent != null ? Parent.Handle.ToInt32() : 0, (WindowStyles)cp.Style, (WindowExStyles)cp.ExStyle); +#endif + + if (!StyleSet (cp.Style, WindowStyles.WS_CHILD)) { + if ((X != unchecked((int)0x80000000)) && (Y != unchecked((int)0x80000000))) { + XSizeHints hints; + + hints = new XSizeHints(); + hints.x = X; + hints.y = Y; + hints.flags = (IntPtr)(XSizeHintsFlags.USPosition | XSizeHintsFlags.PPosition); + Xlib.XSetWMNormalHints (display.Handle, WholeWindow, ref hints); + } + } + + Xlib.XSelectInput (display.Handle, WholeWindow, new IntPtr ((int)(SelectInputMask | EventMask.StructureNotifyMask))); + if (WholeWindow != ClientWindow) + Xlib.XSelectInput (display.Handle, ClientWindow, new IntPtr ((int)SelectInputMask)); + + if (ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_TOPMOST)) { + WINDOW_TYPE = display.Atoms._NET_WM_WINDOW_TYPE_NORMAL; + Xlib.XSetTransientForHint (display.Handle, WholeWindow, display.RootWindow.Handle); + } + + SetWMStyles (cp); + + // set the group leader + XWMHints wm_hints = new XWMHints (); + + wm_hints.flags = (IntPtr)(XWMHintsFlags.InputHint | XWMHintsFlags.StateHint | XWMHintsFlags.WindowGroupHint); + wm_hints.input = !StyleSet (cp.Style, WindowStyles.WS_DISABLED); + wm_hints.initial_state = StyleSet (cp.Style, WindowStyles.WS_MINIMIZE) ? XInitialState.IconicState : XInitialState.NormalState; + wm_hints.window_group = ParentHandle == display.RootWindow.Handle ? ParentHandle : WholeWindow; + + Xlib.XSetWMHints (display.Handle, WholeWindow, ref wm_hints ); + + if (StyleSet (cp.Style, WindowStyles.WS_MINIMIZE)) + SetWindowState (FormWindowState.Minimized); + else if (StyleSet (cp.Style, WindowStyles.WS_MAXIMIZE)) + SetWindowState (FormWindowState.Maximized); + + // for now make all windows dnd enabled + display.Dnd.SetAllowDrop (this, true); + + // Set caption/window title + Text = cp.Caption; + + display.SendMessage (Handle, Msg.WM_CREATE, (IntPtr)1, IntPtr.Zero /* XXX unused */); + SendParentNotify (Msg.WM_CREATE, int.MaxValue, int.MaxValue); + + if (StyleSet (cp.Style, WindowStyles.WS_VISIBLE)) { + visible = true; + Map (); + if (!(Widget.FromHandle(Handle) is Form)) + display.SendMessage (Handle, Msg.WM_SHOWWINDOW, (IntPtr)1, IntPtr.Zero); + } + } + + public virtual void DestroyWindow () + { + if (WholeWindow != IntPtr.Zero) { +#if DriverDebug || DriverDebugDestroy + Console.WriteLine ("XDestroyWindow (whole_window = {0:X})", WholeWindow.ToInt32()); +#endif + Xlib.XDestroyWindow (display.Handle, WholeWindow); + } + else if (ClientWindow != IntPtr.Zero) { +#if DriverDebug || DriverDebugDestroy + Console.WriteLine ("XDestroyWindow (client_window = {0:X})", ClientWindow.ToInt32()); +#endif + Xlib.XDestroyWindow (display.Handle, ClientWindow); + } + } + + public void Activate () + { + if (((IList)display.RootWindow._NET_SUPPORTED).Contains (display.Atoms._NET_ACTIVE_WINDOW)) { + display.SendNetWMMessage (WholeWindow, display.Atoms._NET_ACTIVE_WINDOW, (IntPtr)1, IntPtr.Zero, IntPtr.Zero); + } + else { + Xlib.XRaiseWindow (display.Handle, WholeWindow); + } + } + + public void Update () + { + try { + Queue.Lock (); + if (!Visible || !PendingExpose || !Mapped) + return; + + // XXX this SendMessage call should probably not be inside the lock + display.SendMessage (ClientWindow, Msg.WM_PAINT, IntPtr.Zero, IntPtr.Zero); + + PendingExpose = false; + } + finally { + Queue.Unlock (); + } + } + + public void MenuToScreen (ref int x, ref int y) + { + int dest_x_return; + int dest_y_return; + IntPtr child; + + Xlib.XTranslateCoordinates (display.Handle, + WholeWindow, display.RootWindow.Handle, + x, y, out dest_x_return, out dest_y_return, out child); + + x = dest_x_return; + y = dest_y_return; + } + + public virtual void PropertyChanged (XEvent xevent) + { + if (xevent.PropertyEvent.atom == display.Atoms._NET_WM_WINDOW_TYPE) { + // we need to recache our WINDOW_TYPE on the next query + refetch_window_type = true; + window_type = null; + } + else if (xevent.PropertyEvent.atom == display.Atoms._NET_WM_STATE) { + // we need to recache our WM_STATE on the next query + } + else if (xevent.PropertyEvent.atom == display.Atoms._NET_WM_NAME) { + // update our Text property + } + else if (xevent.PropertyEvent.atom == display.Atoms._NET_WM_WINDOW_OPACITY) { + // we need to recache our _NET_WM_WINDOW_OPACITY on the next query. + refetch_window_opacity = true; + } + // else we don't care about it + + } + + public void SetIcon (Icon icon) + { + if (icon == null) { + Xlib.XDeleteProperty (display.Handle, WholeWindow, display.Atoms._NET_WM_ICON); + } + else { + Bitmap bitmap; + int size; + IntPtr[] data; + int index; + + bitmap = icon.ToBitmap(); + index = 0; + size = bitmap.Width * bitmap.Height + 2; + data = new IntPtr[size]; + + data[index++] = (IntPtr)bitmap.Width; + data[index++] = (IntPtr)bitmap.Height; + + for (int y = 0; y < bitmap.Height; y++) { + for (int x = 0; x < bitmap.Width; x++) { + data[index++] = (IntPtr)bitmap.GetPixel(x, y).ToArgb(); + } + } + + Xlib.XChangeProperty (display.Handle, WholeWindow, + display.Atoms._NET_WM_ICON, display.Atoms.XA_CARDINAL, 32, + PropertyMode.Replace, data, size); + } + } + + public double GetWindowTransparency () + { + if (refetch_window_opacity) { + trans = 1.0; + + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + + IntPtr w = WholeWindow; + if (reparented) + w = display.XGetParent (WholeWindow); + + Xlib.XGetWindowProperty (display.Handle, w, + display.Atoms._NET_WM_WINDOW_OPACITY, IntPtr.Zero, new IntPtr (16), false, + display.Atoms.XA_CARDINAL, + out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + + if (((long)nitems == 1) && (prop != IntPtr.Zero)) { + uint x11_opacity = (uint)Marshal.ReadInt32(prop, 0); + trans = ((double)x11_opacity) / (uint)0xffffffff; + } + + if (prop != IntPtr.Zero) { + Xlib.XFree(prop); + } + } + + return trans; + } + + public void SetWindowTransparency (double transparency, Color key) + { + IntPtr x11_opacity; + + opacity = (uint)(0xffffffff * transparency); + x11_opacity = (IntPtr)((int)opacity); + + IntPtr w = WholeWindow; + if (reparented) + w = display.XGetParent (WholeWindow); + Xlib.XChangeProperty (display.Handle, w, + display.Atoms._NET_WM_WINDOW_OPACITY, display.Atoms.XA_CARDINAL, 32, + PropertyMode.Replace, ref x11_opacity, 1); + } + + public IntPtr DefWndProc (ref Message msg) + { + switch ((Msg)msg.Msg) { + case Msg.WM_PAINT: + Queue.Lock (); + PendingExpose = false; + Queue.Unlock (); + return IntPtr.Zero; + + case Msg.WM_NCPAINT: + Queue.Lock (); + PendingNCExpose = false; + Queue.Unlock (); + return IntPtr.Zero; + + case Msg.WM_CONTEXTMENU: + if (Parent != null) + display.SendMessage (Parent.ClientWindow, Msg.WM_CONTEXTMENU, msg.WParam, msg.LParam); + return IntPtr.Zero; + + case Msg.WM_MOUSEWHEEL: + if (Parent != null) { + display.SendMessage (Parent.ClientWindow, Msg.WM_MOUSEWHEEL, msg.WParam, msg.LParam); + if (msg.Result == IntPtr.Zero) + return IntPtr.Zero; + } + return IntPtr.Zero; + + case Msg.WM_SETCURSOR: + X11Hwnd parent = (X11Hwnd)Parent; + // Pass to parent window first + while ((parent != null) && (msg.Result == IntPtr.Zero)) { + msg.Result = NativeWindow.WndProc (parent.Handle, Msg.WM_SETCURSOR, msg.HWnd, msg.LParam); + parent = (X11Hwnd)Parent; + } + + if (msg.Result == IntPtr.Zero) { + IntPtr handle; + + switch((HitTest)(msg.LParam.ToInt32() & 0xffff)) { + case HitTest.HTBOTTOM: handle = Cursors.SizeNS.handle; break; + case HitTest.HTBORDER: handle = Cursors.SizeNS.handle; break; + case HitTest.HTBOTTOMLEFT: handle = Cursors.SizeNESW.handle; break; + case HitTest.HTBOTTOMRIGHT: handle = Cursors.SizeNWSE.handle; break; + case HitTest.HTERROR: + if ((msg.LParam.ToInt32() >> 16) == (int)Msg.WM_LBUTTONDOWN) + display.AudibleAlert(); + handle = Cursors.Default.handle; + break; + + case HitTest.HTHELP: handle = Cursors.Help.handle; break; + case HitTest.HTLEFT: handle = Cursors.SizeWE.handle; break; + case HitTest.HTRIGHT: handle = Cursors.SizeWE.handle; break; + case HitTest.HTTOP: handle = Cursors.SizeNS.handle; break; + case HitTest.HTTOPLEFT: handle = Cursors.SizeNWSE.handle; break; + case HitTest.HTTOPRIGHT: handle = Cursors.SizeNESW.handle; break; + +#if SameAsDefault + case HitTest.HTGROWBOX: + case HitTest.HTSIZE: + case HitTest.HTZOOM: + case HitTest.HTVSCROLL: + case HitTest.HTSYSMENU: + case HitTest.HTREDUCE: + case HitTest.HTNOWHERE: + case HitTest.HTMAXBUTTON: + case HitTest.HTMINBUTTON: + case HitTest.HTMENU: + case HitTest.HSCROLL: + case HitTest.HTBOTTOM: + case HitTest.HTCAPTION: + case HitTest.HTCLIENT: + case HitTest.HTCLOSE: +#endif + default: handle = Cursors.Default.handle; break; + } + + display.SetCursor (msg.HWnd, handle); + } + return (IntPtr)1; + + default: + return IntPtr.Zero; + } + } + + + public void AddExpose (bool client, int x, int y, int width, int height) + { + // Don't waste time + if ((x > Width) || (y > Height) || ((x + width) <= 0) || ((y + height) <= 0)) + return; + + // Keep the invalid area as small as needed + if ((x + width) > Width) + width = Width - x; + + if ((y + height) > Height) + height = Height - y; + + if (client) { + AddInvalidArea(x, y, width, height); + PendingExpose = true; + } + else { + AddNcInvalidArea (x, y, width, height); + PendingNCExpose = true; + } + } + + public void AddConfigureNotify (XEvent xevent) + { + // We drop configure events for Client windows + if ((xevent.ConfigureEvent.window != WholeWindow) || (xevent.ConfigureEvent.window != xevent.ConfigureEvent.xevent)) + return; + + if (!reparented) { + X = xevent.ConfigureEvent.x; + Y = xevent.ConfigureEvent.y; + } else { + // This sucks ass, part 1 + // Every WM does the ConfigureEvents of toplevel windows different, so there's + // no standard way of getting our adjustment. + // The code below is needed for KDE and FVWM, the 'whacky_wm' part is for metacity + // Several other WMs do their decorations different yet again and we fail to deal + // with that, since I couldn't find any frigging commonality between them. + // The only sane WM seems to be KDE + + if (!xevent.ConfigureEvent.send_event) { + IntPtr dummy_ptr; + + int trans_x; + int trans_y; + + Xlib.XTranslateCoordinates (display.Handle, WholeWindow, display.RootWindow.Handle, + -xevent.ConfigureEvent.x, -xevent.ConfigureEvent.y, + out trans_x, out trans_y, out dummy_ptr); + + X = trans_x; + Y = trans_y; + } else { + // This is a synthetic event, coordinates are in root space + X = xevent.ConfigureEvent.x; + Y = xevent.ConfigureEvent.y; + if (whacky_wm) { + int frame_left; + int frame_top; + + FrameExtents (out frame_left, out frame_top); + X -= frame_left; + Y -= frame_top; + } + } + } + + Width = xevent.ConfigureEvent.width; + Height = xevent.ConfigureEvent.height; + ClientRect = Rectangle.Empty; + + if (!configure_pending) { + Queue.AddConfigure (this); + configure_pending = true; + } + } + + public void HandleMapEvent (XEvent xevent) + { + if (xevent.type == XEventName.MapNotify) { + } + else { + } + } + + public void HandleConfigureNotify (XEvent xevent) + { + configure_pending = false; + + display.SendMessage (Handle, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero); + + // We need to adjust our client window to track the resize of whole_window + if (WholeWindow != ClientWindow) + PerformNCCalc (); + } + + public void Invalidate (Rectangle rc, bool clear) + { + if (clear) { + AddExpose (true, X, Y, Width, Height); + } else { + AddExpose (true, rc.X, rc.Y, rc.Width, rc.Height); + } + } + + public void InvalidateNC () + { + AddExpose (false, 0, 0, Width, Height); + } + + // XXX this assumes the queue lock is held + public bool PendingNCExpose { + get { return nc_expose_pending; } + set { + if (nc_expose_pending == value) + return; + nc_expose_pending = value; + + if (nc_expose_pending && !expose_pending) + Queue.AddPaint (this); + else if (!nc_expose_pending && !expose_pending) + Queue.RemovePaint (this); + } + } + + // XXX this assumes the queue lock is held + public bool PendingExpose { + get { return expose_pending; } + set { + if (expose_pending == value) + return; + expose_pending = value; + + if (expose_pending && !nc_expose_pending) + Queue.AddPaint (this); + else if (!expose_pending && !nc_expose_pending) + Queue.RemovePaint (this); + } + } + + public PaintEventArgs PaintEventStart (ref Message m, bool client) + { + PaintEventArgs paint_event; + Graphics dc; + + if (client) { + dc = Graphics.FromHwnd (ClientWindow); + + Region clip_region = new Region (); + clip_region.MakeEmpty(); + + foreach (Rectangle r in ClipRectangles) + clip_region.Union (r); + + if (UserClip != null) + clip_region.Intersect(UserClip); + + dc.Clip = clip_region; + paint_event = new PaintEventArgs(dc, Invalid); + PendingExpose = false; + + ClearInvalidArea(); + + drawing_stack.Push (paint_event); + drawing_stack.Push (dc); + + return paint_event; + } + else { + dc = Graphics.FromHwnd (WholeWindow); + + if (!nc_invalid.IsEmpty) { + dc.SetClip (nc_invalid); + paint_event = new PaintEventArgs(dc, nc_invalid); + } + else { + paint_event = new PaintEventArgs(dc, new Rectangle(0, 0, width, height)); + } + PendingNCExpose = false; + + ClearNcInvalidArea (); + + drawing_stack.Push (paint_event); + drawing_stack.Push (dc); + + return paint_event; + } + } + + public void PaintEventEnd (ref Message m, bool client) + { + Graphics dc = (Graphics)drawing_stack.Pop (); + dc.Flush(); + dc.Dispose(); + + PaintEventArgs pe = (PaintEventArgs)drawing_stack.Pop(); + pe.SetGraphics (null); + pe.Dispose (); + } + + public void DrawReversibleRectangle (Rectangle rect, int line_width) + { + XGCValues gc_values; + IntPtr gc; + + gc_values = new XGCValues (); + + gc_values.subwindow_mode = GCSubwindowMode.IncludeInferiors; + gc_values.line_width = line_width; + + // XXX multiscreen support + gc_values.foreground = Xlib.XBlackPixel (display.Handle, display.DefaultScreen); + + // This logic will give us true rubber bands: (libsx, SANE_XOR) + //mask = foreground ^ background; + //XSetForeground(DisplayHandle, gc, 0xffffffff); + //XSetBackground(DisplayHandle, gc, background); + //XSetFunction(DisplayHandle, gc, GXxor); + //XSetPlaneMask(DisplayHandle, gc, mask); + + + gc = Xlib.XCreateGC (display.Handle, ClientWindow, + new IntPtr ((int) (GCFunction.GCSubwindowMode | GCFunction.GCLineWidth | GCFunction.GCForeground)), ref gc_values); + uint foreground; + uint background; + + Widget Widget; + Widget = Widget.FromHandle(Handle); + + XColor xcolor = new XColor(); + + xcolor.red = (ushort)(Widget.ForeColor.R * 257); + xcolor.green = (ushort)(Widget.ForeColor.G * 257); + xcolor.blue = (ushort)(Widget.ForeColor.B * 257); + Xlib.XAllocColor (display.Handle, display.DefaultColormap, ref xcolor); + foreground = (uint)xcolor.pixel.ToInt32(); + + xcolor.red = (ushort)(Widget.BackColor.R * 257); + xcolor.green = (ushort)(Widget.BackColor.G * 257); + xcolor.blue = (ushort)(Widget.BackColor.B * 257); + Xlib.XAllocColor (display.Handle, display.DefaultColormap, ref xcolor); + background = (uint)xcolor.pixel.ToInt32(); + + uint mask = foreground ^ background; + + Xlib.XSetForeground (display.Handle, gc, (UIntPtr)0xffffffff); + Xlib.XSetBackground (display.Handle, gc, (UIntPtr)background); + Xlib.XSetFunction (display.Handle, gc, GXFunction.GXxor); + Xlib.XSetPlaneMask (display.Handle, gc, (IntPtr)mask); + + if ((rect.Width > 0) && (rect.Height > 0)) + Xlib.XDrawRectangle (display.Handle, ClientWindow, gc, rect.Left, rect.Top, rect.Width, rect.Height); + else if (rect.Width > 0) + Xlib.XDrawLine (display.Handle, ClientWindow, gc, rect.X, rect.Y, rect.Right, rect.Y); + else + Xlib.XDrawLine (display.Handle, ClientWindow, gc, rect.X, rect.Y, rect.X, rect.Bottom); + + Xlib.XFreeGC (display.Handle, gc); + } + + private void WaitForMessage (Msg message) + { + MSG msg = new MSG (); + + queue.DispatchIdle = false; + + bool done = false; + do { + if (display.PeekMessage(queue, ref msg, IntPtr.Zero, 0, 0, (uint)PeekMessageFlags.PM_REMOVE)) { + if ((Msg)msg.message == Msg.WM_QUIT) { + // XXX this should live someplace else + XplatUI.PostQuitMessage (0); + done = true; + } + else { + if ((msg.hwnd == Handle) && + ((Msg)msg.message == message || (Msg)msg.message == Msg.WM_DESTROY)) + done = true; + display.TranslateMessage (ref msg); + display.DispatchMessage (ref msg); + } + } + } while (!done); + + queue.DispatchIdle = true; + } + + public void Map () + { + // FIXME why do we set this here and also in the MapNotify event handling? + if (!mapped) { + + Xlib.XMapWindow (display.Handle, WholeWindow); + Xlib.XMapWindow (display.Handle, ClientWindow); + + mapped = true; + + if (Widget.FromHandle(Handle) is Form) + WaitForMessage (Msg.WM_SHOWWINDOW); + } + } + + public void Unmap () + { + // FIXME why do we set this here and also in the UnmapNotify event handling? + if (mapped) { + Xlib.XUnmapWindow (display.Handle, ClientWindow); + Xlib.XUnmapWindow (display.Handle, WholeWindow); + + mapped = false; + + if (Widget.FromHandle(Handle) is Form) + WaitForMessage (Msg.WM_SHOWWINDOW); + } + } + + public void PerformNCCalc () + { + XplatUIWin32.NCCALCSIZE_PARAMS ncp; + IntPtr ptr; + Rectangle rect; + + rect = DefaultClientRect; + + ncp = new XplatUIWin32.NCCALCSIZE_PARAMS (); + ptr = Marshal.AllocHGlobal (Marshal.SizeOf(ncp)); + + ncp.rgrc1.left = rect.Left; + ncp.rgrc1.top = rect.Top; + ncp.rgrc1.right = rect.Right; + ncp.rgrc1.bottom = rect.Bottom; + + Marshal.StructureToPtr (ncp, ptr, true); + NativeWindow.WndProc (ClientWindow, Msg.WM_NCCALCSIZE, (IntPtr)1, ptr); + ncp = (XplatUIWin32.NCCALCSIZE_PARAMS)Marshal.PtrToStructure (ptr, typeof(XplatUIWin32.NCCALCSIZE_PARAMS)); + Marshal.FreeHGlobal(ptr); + + // FIXME - debug this with Menus + + rect = new Rectangle(ncp.rgrc1.left, ncp.rgrc1.top, ncp.rgrc1.right - ncp.rgrc1.left, ncp.rgrc1.bottom - ncp.rgrc1.top); + ClientRect = rect; + + if (Visible) { + if ((rect.Width < 1) || (rect.Height < 1)) + Xlib.XMoveResizeWindow (display.Handle, ClientWindow, -5, -5, 1, 1); + else + Xlib.XMoveResizeWindow (display.Handle, ClientWindow, rect.X, rect.Y, rect.Width, rect.Height); + } + + InvalidateNC (); + } + + public void RequestNCRecalc () + { + PerformNCCalc (); + display.SendMessage (Handle, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero); + InvalidateNC (); + } + + [MonoTODO] + public void RequestAdditionalWM_NCMessages (bool hover, bool leave) + { + // Missing messages won't crash anything so just don't generate them for the moment. + // throw new NotImplementedException( ); + } + + public void FrameExtents (out int left, out int top) + { + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + + Xlib.XGetWindowProperty (display.Handle, WholeWindow, + display.Atoms._NET_FRAME_EXTENTS, IntPtr.Zero, new IntPtr (16), false, + display.Atoms.XA_CARDINAL, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + if (((long)nitems == 4) && (prop != IntPtr.Zero)) { + left = Marshal.ReadIntPtr(prop, 0).ToInt32(); + //right = Marshal.ReadIntPtr(prop, IntPtr.Size).ToInt32(); + top = Marshal.ReadIntPtr(prop, IntPtr.Size * 2).ToInt32(); + //bottom = Marshal.ReadIntPtr(prop, IntPtr.Size * 3).ToInt32(); + } else { + left = 0; + top = 0; + } + + if (prop != IntPtr.Zero) { + Xlib.XFree(prop); + } + } + + static bool StyleSet (int s, WindowStyles ws) + { + return (s & (int)ws) == (int)ws; + } + + static bool ExStyleSet (int ex, WindowExStyles exws) + { + return (ex & (int)exws) == (int)exws; + } + + // XXX this should be a static method on Hwnd so other backends can use it + public static void DeriveStyles(int Style, int ExStyle, out FormBorderStyle border_style, out bool border_static, + out TitleStyle title_style, out int caption_height, out int tool_caption_height) + { + + // Only MDI windows get caption_heights + caption_height = 0; + tool_caption_height = 19; + border_static = false; + + if (StyleSet (Style, WindowStyles.WS_CHILD)) { + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_CLIENTEDGE)) { + border_style = FormBorderStyle.Fixed3D; + } else if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_STATICEDGE)) { + border_style = FormBorderStyle.Fixed3D; + border_static = true; + } else if (!StyleSet (Style, WindowStyles.WS_BORDER)) { + border_style = FormBorderStyle.None; + } else { + border_style = FormBorderStyle.FixedSingle; + } + title_style = TitleStyle.None; + + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_MDICHILD)) { + caption_height = 26; + + if (StyleSet (Style, WindowStyles.WS_CAPTION)) { + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) + title_style = TitleStyle.Tool; + else + title_style = TitleStyle.Normal; + } + + if (StyleSet (Style, WindowStyles.WS_OVERLAPPEDWINDOW) || + ExStyleSet (ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) + border_style = (FormBorderStyle) 0xFFFF; + else + border_style = FormBorderStyle.None; + } + } + else { + title_style = TitleStyle.None; + if (StyleSet (Style, WindowStyles.WS_CAPTION)) { + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) + title_style = TitleStyle.Tool; + else + title_style = TitleStyle.Normal; + } + + border_style = FormBorderStyle.None; + + if (StyleSet (Style, WindowStyles.WS_THICKFRAME)) { + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) + border_style = FormBorderStyle.SizableToolWindow; + else + border_style = FormBorderStyle.Sizable; + } else { + if (StyleSet (Style, WindowStyles.WS_CAPTION)) { + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_CLIENTEDGE)) + border_style = FormBorderStyle.Fixed3D; + else if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_STATICEDGE)) { + border_style = FormBorderStyle.Fixed3D; + border_static = true; + } else if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_DLGMODALFRAME)) + border_style = FormBorderStyle.FixedDialog; + else if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) + border_style = FormBorderStyle.FixedToolWindow; + else if (StyleSet (Style, WindowStyles.WS_BORDER)) + border_style = FormBorderStyle.FixedSingle; + } else if (StyleSet (Style, WindowStyles.WS_BORDER)) + border_style = FormBorderStyle.FixedSingle; + } + } + } + + public void SetHwndStyles (CreateParams cp) + { + DeriveStyles(cp.Style, cp.ExStyle, out this.border_style, out this.border_static, out this.title_style, out this.caption_height, out this.tool_caption_height); + } + + public void SetWMStyles (CreateParams cp) + { + MotifWmHints mwmHints; + MotifFunctions functions; + MotifDecorations decorations; + IntPtr[] atoms; + int atom_count; + Rectangle client_rect; + + // Child windows don't need WM window styles + if (StyleSet (cp.Style, WindowStyles.WS_CHILDWINDOW)) + return; + + atoms = new IntPtr[8]; + mwmHints = new MotifWmHints(); + functions = 0; + decorations = 0; + + mwmHints.flags = (IntPtr)(MotifFlags.Functions | MotifFlags.Decorations); + mwmHints.functions = (IntPtr)0; + mwmHints.decorations = (IntPtr)0; + + if (ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_TOOLWINDOW) + || !StyleSet (cp.Style, WindowStyles.WS_CAPTION | WindowStyles.WS_BORDER | WindowStyles.WS_DLGFRAME)) { + /* tool windows get no window manager + decorations, and neither do windows + which lack CAPTION/BORDER/DLGFRAME + styles. + */ + + /* just because the window doesn't get any decorations doesn't + mean we should disable the functions. for instance, without + MotifFunctions.Maximize, changing the windowstate to Maximized + is ignored by metacity. */ + functions |= MotifFunctions.Move | MotifFunctions.Resize | MotifFunctions.Minimize | MotifFunctions.Maximize; + } + else { + if (StyleSet (cp.Style, WindowStyles.WS_CAPTION)) { + functions |= MotifFunctions.Move; + decorations |= MotifDecorations.Title | MotifDecorations.Menu; + } + + if (StyleSet (cp.Style, WindowStyles.WS_THICKFRAME)) { + functions |= MotifFunctions.Move | MotifFunctions.Resize; + decorations |= MotifDecorations.Border | MotifDecorations.ResizeH; + } + + if (StyleSet (cp.Style, WindowStyles.WS_MINIMIZEBOX)) { + functions |= MotifFunctions.Minimize; + decorations |= MotifDecorations.Minimize; + } + + if (StyleSet (cp.Style, WindowStyles.WS_MAXIMIZEBOX)) { + functions |= MotifFunctions.Maximize; + decorations |= MotifDecorations.Maximize; + } + + if (StyleSet (cp.Style, WindowStyles.WS_SIZEBOX)) { + functions |= MotifFunctions.Resize; + decorations |= MotifDecorations.ResizeH; + } + + if (ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_DLGMODALFRAME)) { + decorations |= MotifDecorations.Border; + } + + if (StyleSet (cp.Style, WindowStyles.WS_BORDER)) { + decorations |= MotifDecorations.Border; + } + + if (StyleSet (cp.Style, WindowStyles.WS_DLGFRAME)) { + decorations |= MotifDecorations.Border; + } + + if (StyleSet (cp.Style, WindowStyles.WS_SYSMENU)) { + functions |= MotifFunctions.Close; + } + else { + functions &= ~(MotifFunctions.Maximize | MotifFunctions.Minimize | MotifFunctions.Close); + decorations &= ~(MotifDecorations.Menu | MotifDecorations.Maximize | MotifDecorations.Minimize); + if (cp.Caption == "") { + functions &= ~MotifFunctions.Move; + decorations &= ~(MotifDecorations.Title | MotifDecorations.ResizeH); + } + } + } + + if ((functions & MotifFunctions.Resize) == 0) { + fixed_size = true; + SetMinMax (new Rectangle(cp.X, cp.Y, cp.Width, cp.Height), new Size(cp.Width, cp.Height), new Size(cp.Width, cp.Height)); + } else { + fixed_size = false; + } + + mwmHints.functions = (IntPtr)functions; + mwmHints.decorations = (IntPtr)decorations; + + FormWindowState current_state = GetWindowState (); + if (current_state == (FormWindowState)(-1)) + current_state = FormWindowState.Normal; + + client_rect = ClientRect; + + atom_count = 0; + + // needed! map toolwindows to _NET_WM_WINDOW_TYPE_UTILITY to make newer metacity versions happy + // and get those windows in front of their parents + if (ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) { + WINDOW_TYPE = display.Atoms._NET_WM_WINDOW_TYPE_UTILITY; + + Form f = Widget.FromHandle(Handle) as Form; + if (f != null && !reparented) { + if (f.Owner != null && f.Owner.Handle != IntPtr.Zero) { + Hwnd owner_hwnd = Hwnd.ObjectFromHandle(f.Owner.Handle); + if (owner_hwnd != null) + Xlib.XSetTransientForHint (display.Handle, WholeWindow, + owner_hwnd.WholeWindow); + } + } + } + + Xlib.XChangeProperty (display.Handle, WholeWindow, + display.Atoms._MOTIF_WM_HINTS, display.Atoms._MOTIF_WM_HINTS, 32, + PropertyMode.Replace, ref mwmHints, 5); + + if (StyleSet (cp.Style, WindowStyles.WS_POPUP) && (parent != null) && (parent.WholeWindow != IntPtr.Zero)) { + WINDOW_TYPE = display.Atoms._NET_WM_WINDOW_TYPE_NORMAL; + Xlib.XSetTransientForHint(display.Handle, WholeWindow, parent.WholeWindow); + } else if (!ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_APPWINDOW)) { + /* this line keeps the window from showing up in gnome's taskbar */ + atoms[atom_count++] = display.Atoms._NET_WM_STATE_SKIP_TASKBAR; + } + if ((client_rect.Width < 1) || (client_rect.Height < 1)) { + Xlib.XMoveResizeWindow (display.Handle, ClientWindow, -5, -5, 1, 1); + } else { + Xlib.XMoveResizeWindow (display.Handle, ClientWindow, client_rect.X, client_rect.Y, client_rect.Width, client_rect.Height); + } + + if (ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) + atoms[atom_count++] = display.Atoms._NET_WM_STATE_SKIP_TASKBAR; + + /* we need to add these atoms in the + * event we're maximized, since we're + * replacing the existing + * _NET_WM_STATE here. If we don't + * add them, future calls to + * GetWindowState will return Normal + * for a window which is maximized. */ + if (current_state == FormWindowState.Maximized) { + atoms[atom_count++] = display.Atoms._NET_WM_STATE_MAXIMIZED_HORZ; + atoms[atom_count++] = display.Atoms._NET_WM_STATE_MAXIMIZED_VERT; + } + + Set_WM_STATE (atoms, atom_count); + + atom_count = 0; + atoms[atom_count++] = display.Atoms.WM_DELETE_WINDOW; + if (ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_CONTEXTHELP)) + atoms[atom_count++] = display.Atoms._NET_WM_CONTEXT_HELP; + + Xlib.XSetWMProtocols (display.Handle, WholeWindow, atoms, atom_count); + } + + public void ClientToScreen (ref int x, ref int y) + { + int dest_x_return; + int dest_y_return; + IntPtr child; + + Xlib.XTranslateCoordinates (display.Handle, + ClientWindow, display.RootWindow.Handle, + x, y, out dest_x_return, out dest_y_return, out child); + + x = dest_x_return; + y = dest_y_return; + } + + public void ScreenToClient (ref int x, ref int y) + { + int dest_x_return; + int dest_y_return; + IntPtr child; + + Xlib.XTranslateCoordinates (display.Handle, + display.RootWindow.Handle, ClientWindow, + x, y, out dest_x_return, out dest_y_return, out child); + + x = dest_x_return; + y = dest_y_return; + } + + + public void ScreenToMenu (ref int x, ref int y) + { + int dest_x_return; + int dest_y_return; + IntPtr child; + + Xlib.XTranslateCoordinates (display.Handle, + display.RootWindow.Handle, WholeWindow, + x, y, out dest_x_return, out dest_y_return, out child); + + x = dest_x_return; + y = dest_y_return; + } + + public void ScrollWindow (Rectangle area, int XAmount, int YAmount, bool with_children) + { + IntPtr gc; + XGCValues gc_values; + + Rectangle r = Rectangle.Intersect (Invalid, area); + if (!r.IsEmpty) { + /* We have an invalid area in the window we're scrolling. + Adjust our stored invalid rectangle to to match the scrolled amount */ + + r.X += XAmount; + r.Y += YAmount; + + if (r.X < 0) { + r.Width += r.X; + r.X =0; + } + + if (r.Y < 0) { + r.Height += r.Y; + r.Y =0; + } + + if (area.Contains (Invalid)) + ClearInvalidArea(); + AddInvalidArea(r); + } + + gc_values = new XGCValues(); + + gc_values.graphics_exposures = false; + if (with_children) + gc_values.subwindow_mode = GCSubwindowMode.IncludeInferiors; + + gc = Xlib.XCreateGC (display.Handle, ClientWindow, IntPtr.Zero, ref gc_values); + + int src_x, src_y; + int dest_x, dest_y; + int width, height; + + if (YAmount > 0) { + src_y = area.Y; + height = area.Height - YAmount; + dest_y = area.Y + YAmount; + } + else { + src_y = area.Y - YAmount; + height = area.Height + YAmount; + dest_y = area.Y; + } + + if (XAmount > 0) { + src_x = area.X; + width = area.Width - XAmount; + dest_x = area.X + XAmount; + } + else { + src_x = area.X - XAmount; + width = area.Width + XAmount; + dest_x = area.X; + } + + Xlib.XCopyArea (display.Handle, ClientWindow, ClientWindow, gc, src_x, src_y, width, height, dest_x, dest_y); + + // Generate an expose for the area exposed by the horizontal scroll + // We don't use AddExpose since we're + if (XAmount > 0) { + AddExpose (true, area.X, area.Y, XAmount, area.Height); + } else if (XAmount < 0) { + AddExpose (true, XAmount + area.X + area.Width, area.Y, -XAmount, area.Height); + } + + // Generate an expose for the area exposed by the vertical scroll + if (YAmount > 0) { + AddExpose (true, area.X, area.Y, area.Width, YAmount); + } else if (YAmount < 0) { + AddExpose (true, area.X, YAmount + area.Y + area.Height, area.Width, -YAmount); + } + + Xlib.XFreeGC (display.Handle, gc); + } + + + public void SetBorderStyle (FormBorderStyle border_style) + { + Form form = Widget.FromHandle (Handle) as Form; + if (form != null && form.window_manager == null && (border_style == FormBorderStyle.FixedToolWindow || + border_style == FormBorderStyle.SizableToolWindow)) { + form.window_manager = new ToolWindowManager (form); + } + + BorderStyle = border_style; + RequestNCRecalc (); + } + + // XXX this should probably be in Hwnd + public void SetClipRegion (Region region) + { + UserClip = region; + Invalidate (new Rectangle(0, 0, Width, Height), false); + } + + // XXX this should probably be in Hwnd + public Region GetClipRegion () + { + return UserClip; + } + + public void SetMenu (Menu menu) + { + Menu = menu; + + RequestNCRecalc (); + } + + public void SetMinMax (Rectangle maximized, Size min, Size max) + { + XSizeHints hints; + IntPtr dummy; + + hints = new XSizeHints(); + + Xlib.XGetWMNormalHints (display.Handle, WholeWindow, ref hints, out dummy); + if ((min != Size.Empty) && (min.Width > 0) && (min.Height > 0)) { + hints.flags = (IntPtr)((int)hints.flags | (int)XSizeHintsFlags.PMinSize); + hints.min_width = min.Width; + hints.min_height = min.Height; + } + + if ((max != Size.Empty) && (max.Width > 0) && (max.Height > 0)) { + hints.flags = (IntPtr)((int)hints.flags | (int)XSizeHintsFlags.PMaxSize); + hints.max_width = max.Width; + hints.max_height = max.Height; + } + + if (hints.flags != IntPtr.Zero) + Xlib.XSetWMNormalHints (display.Handle, WholeWindow, ref hints); + + if ((maximized != Rectangle.Empty) && (maximized.Width > 0) && (maximized.Height > 0)) { + hints.flags = (IntPtr)XSizeHintsFlags.PPosition; + hints.x = maximized.X; + hints.y = maximized.Y; + hints.width = maximized.Width; + hints.height = maximized.Height; + + // Metacity does not seem to follow this constraint for maximized (zoomed) windows + Xlib.XSetZoomHints (display.Handle, WholeWindow, ref hints); + } + } + + // For WM_LBUTTONDOWN, WM_MBUTTONDOWN, WM_RBUTTONDOWN, WM_XBUTTONDOWN + // WM_CREATE and WM_DESTROY causes + public void SendParentNotify (Msg cause, int x, int y) + { + if (Handle == IntPtr.Zero) + return; + + if (ExStyleSet ((int) initial_ex_style, WindowExStyles.WS_EX_NOPARENTNOTIFY)) + return; + + if (Parent == null || Parent.Handle == IntPtr.Zero) + return; + + if (cause == Msg.WM_CREATE || cause == Msg.WM_DESTROY) { + display.SendMessage(Parent.Handle, Msg.WM_PARENTNOTIFY, Widget.MakeParam((int)cause, 0), Handle); + } else { + display.SendMessage(Parent.Handle, Msg.WM_PARENTNOTIFY, Widget.MakeParam((int)cause, 0), Widget.MakeParam(x, y)); + } + + ((X11Hwnd)Parent).SendParentNotify (cause, x, y); + } + + + public void GetPosition (bool is_toplevel, out int x, out int y, out int width, out int height, out int client_width, out int client_height) + { + x = X; + y = Y; + width = Width; + height = Height; + + PerformNCCalc (); + + client_width = ClientRect.Width; + client_height = ClientRect.Height; + } + + public void SetPosition (int x, int y, int width, int height) + { + // Win32 automatically changes negative width/height to 0. + if (width < 0) + width = 0; + if (height < 0) + height = 0; + + // X requires a sanity check for width & height; otherwise it dies + if (zero_sized && width > 0 && height > 0) { + if (Visible) { + Map (); + } + zero_sized = false; + } + + if ((width < 1) || (height < 1)) { + zero_sized = true; + Unmap (); + } + + // Save a server roundtrip (and prevent a feedback loop) + if ((X == x) && (Y == y) && + (Width == width) && (Height == height)) { + return; + } + + if (!zero_sized) { + //Hack? + X = x; + Y = y; + Width = width; + Height = height; + display.SendMessage (Handle, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero); + + if (fixed_size) { + SetMinMax (Rectangle.Empty, new Size(width, height), new Size(width, height)); + } + + Xlib.XMoveResizeWindow (display.Handle, WholeWindow, x, y, width, height); + PerformNCCalc (); + } + + // Update our position/size immediately, so + // that future calls to SetWindowPos aren't + // kept from calling XMoveResizeWindow (by the + // "Save a server roundtrip" block above). + X = x; + Y = y; + Width = width; + Height = height; + ClientRect = Rectangle.Empty; + } + + public void SetParent (X11Hwnd parent_hwnd) + { + Parent = parent_hwnd; + +#if DriverDebug || DriverDebugParent + Console.WriteLine("Parent for window {0} = {1}", XplatUI.Window(Handle), XplatUI.Window(hwnd.parent != null ? parent_hwnd.Handle : IntPtr.Zero)); +#endif + Xlib.XReparentWindow (display.Handle, WholeWindow, + parent_hwnd == null ? display.FosterParent.ClientWindow : parent_hwnd.ClientWindow, + X, Y); + } + + public void SetCursorPos (int x, int y) + { + Xlib.XWarpPointer (display.Handle, IntPtr.Zero, ClientWindow, 0, 0, 0, 0, x, y); + } + + public bool SetTopmost (bool enabled) + { + if (enabled) { + int[] atoms = new int[8]; + atoms[0] = display.Atoms._NET_WM_STATE_ABOVE.ToInt32(); + Xlib.XChangeProperty (display.Handle, WholeWindow, display.Atoms._NET_WM_STATE, (IntPtr)Atom.XA_ATOM, 32, PropertyMode.Replace, atoms, 1); + } + else { + Xlib.XDeleteProperty (display.Handle, WholeWindow, display.Atoms._NET_WM_STATE); + } + + return true; + } + + public bool SetOwner (X11Hwnd owner) + { + if (owner != null) { + WINDOW_TYPE = display.Atoms._NET_WM_WINDOW_TYPE_NORMAL; + if (owner != null) + Xlib.XSetTransientForHint (display.Handle, WholeWindow, owner.WholeWindow); + else + Xlib.XSetTransientForHint (display.Handle, WholeWindow, display.RootWindow.WholeWindow); + } + else { + Xlib.XDeleteProperty (display.Handle, WholeWindow, display.Atoms.XA_WM_TRANSIENT_FOR); + } + + return true; + } + + public bool SetVisible (bool visible, bool activate) + { + Visible = visible; + + if (visible) { + Map (); + + if (Widget.FromHandle (Handle) is Form) { + FormWindowState s; + + s = ((Form)Widget.FromHandle(Handle)).WindowState; + + switch(s) { + case FormWindowState.Minimized: SetWindowState (FormWindowState.Minimized); break; + case FormWindowState.Maximized: SetWindowState (FormWindowState.Maximized); break; + } + + } + + display.SendMessage (Handle, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero); + } + else { + Unmap (); + } + + return true; + } + + public FormWindowState GetWindowState () + { + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + IntPtr atom; + int maximized; + bool minimized; + XWindowAttributes attributes; + + maximized = 0; + minimized = false; + Xlib.XGetWindowProperty (display.Handle, WholeWindow, + display.Atoms._NET_WM_STATE, IntPtr.Zero, new IntPtr (256), false, + display.Atoms.XA_ATOM, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + + if (((long)nitems > 0) && (prop != IntPtr.Zero)) { + for (int i = 0; i < (long)nitems; i++) { + // XXX 64 bit clean? + atom = (IntPtr)Marshal.ReadInt32(prop, i * 4); + if ((atom == display.Atoms._NET_WM_STATE_MAXIMIZED_HORZ) || (atom == display.Atoms._NET_WM_STATE_MAXIMIZED_VERT)) + maximized++; + else if (atom == display.Atoms._NET_WM_STATE_HIDDEN) + minimized = true; + } + Xlib.XFree(prop); + } + + if (minimized) + return FormWindowState.Minimized; + else if (maximized == 2) + return FormWindowState.Maximized; + + attributes = new XWindowAttributes(); + Xlib.XGetWindowAttributes (display.Handle, ClientWindow, ref attributes); + if (attributes.map_state == MapState.IsUnmapped) + return (FormWindowState)(-1); + + return FormWindowState.Normal; + } + + + public void SetWindowState (FormWindowState state) + { + FormWindowState current_state; + + current_state = GetWindowState (); + + if (current_state == state) + return; + + switch (state) { + case FormWindowState.Normal: + if (current_state == FormWindowState.Minimized) + Map (); + else if (current_state == FormWindowState.Maximized) + display.SendNetWMMessage (WholeWindow, + display.Atoms._NET_WM_STATE, (IntPtr)2 /* toggle */, + display.Atoms._NET_WM_STATE_MAXIMIZED_HORZ, + display.Atoms._NET_WM_STATE_MAXIMIZED_VERT); + Activate (); + break; + + case FormWindowState.Minimized: + if (current_state == FormWindowState.Maximized) + display.SendNetWMMessage (WholeWindow, + display.Atoms._NET_WM_STATE, (IntPtr)2 /* toggle */, + display.Atoms._NET_WM_STATE_MAXIMIZED_HORZ, + display.Atoms._NET_WM_STATE_MAXIMIZED_VERT); + + // FIXME multiscreen support + Xlib.XIconifyWindow (display.Handle, WholeWindow, display.DefaultScreen); + break; + + case FormWindowState.Maximized: + if (current_state == FormWindowState.Minimized) + Map (); + + display.SendNetWMMessage (WholeWindow, + display.Atoms._NET_WM_STATE, (IntPtr)1 /* Add */, + display.Atoms._NET_WM_STATE_MAXIMIZED_HORZ, + display.Atoms._NET_WM_STATE_MAXIMIZED_VERT); + Activate (); + break; + } + } + + public bool SetZOrder (X11Hwnd after_hwnd, bool top, bool bottom) + { + if (top) { + Xlib.XRaiseWindow (display.Handle, WholeWindow); + return true; + } + else if (bottom) { + Xlib.XLowerWindow (display.Handle, WholeWindow); + return true; + } + else { + if (after_hwnd == null) { + Update_USER_TIME (); + Xlib.XRaiseWindow (display.Handle, WholeWindow); + display.SendNetWMMessage (WholeWindow, display.Atoms._NET_ACTIVE_WINDOW, (IntPtr)1, IntPtr.Zero, IntPtr.Zero); + return true; + } + + XWindowChanges values = new XWindowChanges(); + values.sibling = after_hwnd.WholeWindow; + values.stack_mode = StackMode.Below; + + Xlib.XConfigureWindow (display.Handle, WholeWindow, ChangeWindowFlags.CWStackMode | ChangeWindowFlags.CWSibling, ref values); + } + return false; + } + + public X11Display Display { + get { return display; } + } + + public string Text { + get { return text; } + set { + if (value == null) + value = ""; + + if (value == text) + return; + + text = value; + + Xlib.XChangeProperty(display.Handle, WholeWindow, + display.Atoms._NET_WM_NAME, display.Atoms.UNICODETEXT, 8, + PropertyMode.Replace, text, Encoding.UTF8.GetByteCount (text)); + + // XXX this has problems with UTF8. + // we need to either use the actual + // text if it's latin-1, or convert it + // to compound text if it's in a + // different charset. + Xlib.XStoreName(display.Handle, WholeWindow, text); + } + } + + public bool GetText (out string text) + { + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + + Xlib.XGetWindowProperty (display.Handle, WholeWindow, + display.Atoms._NET_WM_NAME, IntPtr.Zero, new IntPtr (1), false, + display.Atoms.UNICODETEXT, out actual_atom, out actual_format, + out nitems, out bytes_after, ref prop); + + if ((long)nitems > 0 && prop != IntPtr.Zero) { + text = Marshal.PtrToStringUni (prop, (int)nitems); + Xlib.XFree (prop); + return true; + } + else { + // fallback on the non-_NET property + IntPtr textptr; + + textptr = IntPtr.Zero; + + Xlib.XFetchName (display.Handle, WholeWindow, ref textptr); + if (textptr != IntPtr.Zero) { + text = Marshal.PtrToStringAnsi(textptr); + Xlib.XFree(textptr); + return true; + } else { + text = ""; + return false; + } + } + } + + public IntPtr WINDOW_TYPE { + get { + if (refetch_window_type) { + window_type = GetAtomListProperty (display.Atoms._NET_WM_WINDOW_TYPE); + refetch_window_type = false; + } + + return window_type.Length > 0 ? window_type[0] : IntPtr.Zero; + } + set { + Set_WINDOW_TYPE (new IntPtr[] {value}, 1); + } + } + + public void Set_WINDOW_TYPE (IntPtr[] value, int count) + { + if (refetch_window_type) { + window_type = GetAtomListProperty (display.Atoms._NET_WM_WINDOW_TYPE); + refetch_window_type = false; + } + + if (ArrayDifferent (window_type, value)) { + window_type = value; + Xlib.XChangeProperty (display.Handle, WholeWindow, + display.Atoms._NET_WM_WINDOW_TYPE, display.Atoms.XA_ATOM, 32, + PropertyMode.Replace, window_type, window_type.Length); + } + } + + public void Set_WM_STATE (IntPtr[] value, int count) + { + if (ArrayDifferent (wm_state, value)) { + wm_state = value; + Xlib.XChangeProperty (display.Handle, WholeWindow, + display.Atoms._NET_WM_STATE, display.Atoms.XA_ATOM, 32, + PropertyMode.Replace, wm_state, wm_state.Length); + } + } + + public void Update_USER_TIME () + { + int[] args; + + args = new int[2]; + args[0] = display.CurrentTimestamp; + Xlib.XChangeProperty (display.Handle, WholeWindow, + display.Atoms._NET_WM_USER_TIME, display.Atoms.XA_CARDINAL, 32, + PropertyMode.Replace, args, 1); + } + + public IntPtr[] GetAtomListProperty (IntPtr atom) + { + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + + Xlib.XGetWindowProperty (display.Handle, WholeWindow, + atom, IntPtr.Zero, new IntPtr (Int32.MaxValue), false, + display.Atoms.XA_ATOM, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + + if (actual_atom != display.Atoms.XA_ATOM || + (long)nitems == 0 || + prop == IntPtr.Zero) { + return new IntPtr[0]; + } + + IntPtr[] values = new IntPtr[(long)nitems]; + int ofs = 0; + + for (int i = 0; i < values.Length; i ++) { + values[i] = Marshal.ReadIntPtr (prop, ofs); ofs += IntPtr.Size; + } + + Xlib.XFree (prop); + + return values; + } + + bool ArrayDifferent (IntPtr[] a, IntPtr[] b) + { + if (a.Length != b.Length) + return true; + + for (int i = 0; i < a.Length; i ++) { + if (a[i] != b[i]) + return true; + } + + return false; + } + } +} |
