ShiftOS-C-/source/ShiftUI/Internal/X11Hwnd.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

1750 lines
51 KiB
C#

// 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;
}
}
}