diff options
| author | MichaelTheShifter <[email protected]> | 2016-07-20 09:40:36 -0400 |
|---|---|---|
| committer | MichaelTheShifter <[email protected]> | 2016-07-20 09:40:36 -0400 |
| commit | d40fed5ce2bc806a91245adb18039634eac13ed0 (patch) | |
| tree | f1d7168aee6db109ac2c738ad18c9db667a6ba69 /source/ShiftUI/Internal/Hwnd.cs | |
| parent | f1856e8ed30ed882229fd3fa2a4038122a5fb441 (diff) | |
| download | shiftos-c--d40fed5ce2bc806a91245adb18039634eac13ed0.tar.gz shiftos-c--d40fed5ce2bc806a91245adb18039634eac13ed0.tar.bz2 shiftos-c--d40fed5ce2bc806a91245adb18039634eac13ed0.zip | |
Move ShiftUI source code to ShiftOS
This'll be a lot easier to work on.
Diffstat (limited to 'source/ShiftUI/Internal/Hwnd.cs')
| -rw-r--r-- | source/ShiftUI/Internal/Hwnd.cs | 910 |
1 files changed, 910 insertions, 0 deletions
diff --git a/source/ShiftUI/Internal/Hwnd.cs b/source/ShiftUI/Internal/Hwnd.cs new file mode 100644 index 0000000..fcdc17d --- /dev/null +++ b/source/ShiftUI/Internal/Hwnd.cs @@ -0,0 +1,910 @@ +// 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) 2005-2006 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok ([email protected]) +// +// + +// NOT COMPLETE + +using System; +using System.Collections; +using System.Drawing; +using System.Runtime.InteropServices; + +// NOTE: Possible optimization: +// Several properties calculate dimensions on the fly; instead; they can +// be stored in a field and only be recalculated when a style is changed (DefaultClientRect, for example) + +namespace ShiftUI { + internal class Hwnd : IDisposable { + #region Local Variables + private static Hashtable windows = new Hashtable(100, 0.5f); + //private const int menu_height = 14; // FIXME - Read this value from somewhere + + private IntPtr handle; + internal IntPtr client_window; + internal IntPtr whole_window; + internal IntPtr cursor; + internal Menu menu; + internal TitleStyle title_style; + internal FormBorderStyle border_style; + internal bool border_static; + internal int x; + internal int y; + internal int width; + internal int height; + internal bool allow_drop; + internal Hwnd parent; + internal bool visible; + internal bool mapped; + internal uint opacity; + internal bool enabled; + internal bool zero_sized; + internal ArrayList invalid_list; + internal Rectangle nc_invalid; + internal bool expose_pending; + internal bool nc_expose_pending; + internal bool configure_pending; + internal bool resizing_or_moving; // Used by the X11 backend to track form resize/move + internal bool reparented; + internal Stack drawing_stack; + internal object user_data; + internal Rectangle client_rectangle; + internal ArrayList marshal_free_list; + internal int caption_height; + internal int tool_caption_height; + internal bool whacky_wm; + internal bool fixed_size; + internal bool zombie; /* X11 only flag. true if the X windows have been destroyed but we haven't been Disposed */ + internal bool topmost; /* X11 only. */ + internal Region user_clip; + internal XEventQueue queue; + internal WindowExStyles initial_ex_style; + internal WindowStyles initial_style; + internal FormWindowState cached_window_state = (FormWindowState)(-1); /* X11 only field */ + internal Point previous_child_startup_location = new Point (int.MinValue, int.MinValue); + static internal Point previous_main_startup_location = new Point (int.MinValue, int.MinValue); + internal ArrayList children; + + [ThreadStatic] + private static Bitmap bmp; + [ThreadStatic] + private static Graphics bmp_g; + #endregion // Local Variables + + // locks for some operations (used in XplatUIX11.cs) + internal object configure_lock = new object (); + internal object expose_lock = new object (); + + #region Constructors and destructors + public Hwnd() { + x = 0; + y = 0; + width = 0; + height = 0; + visible = false; + menu = null; + border_style = FormBorderStyle.None; + client_window = IntPtr.Zero; + whole_window = IntPtr.Zero; + cursor = IntPtr.Zero; + handle = IntPtr.Zero; + parent = null; + invalid_list = new ArrayList(); + expose_pending = false; + nc_expose_pending = false; + enabled = true; + reparented = false; + client_rectangle = Rectangle.Empty; + marshal_free_list = new ArrayList(2); + opacity = 0xffffffff; + fixed_size = false; + drawing_stack = new Stack (); + children = new ArrayList (); + resizing_or_moving = false; + whacky_wm = false; + topmost = false; + } + + public void Dispose() { + expose_pending = false; + nc_expose_pending = false; + Parent = null; + lock (windows) { + windows.Remove(client_window); + windows.Remove(whole_window); + } + client_window = IntPtr.Zero; + whole_window = IntPtr.Zero; + zombie = false; + for (int i = 0; i < marshal_free_list.Count; i++) { + Marshal.FreeHGlobal((IntPtr)marshal_free_list[i]); + } + marshal_free_list.Clear(); + } + #endregion + + #region Static Methods + public static Hwnd ObjectFromWindow(IntPtr window) { + Hwnd rv; + lock (windows) { + rv = (Hwnd)windows[window]; + } + return rv; + } + + public static Hwnd ObjectFromHandle(IntPtr handle) { + //return (Hwnd)(((GCHandle)handle).Target); + Hwnd rv; + lock (windows) { + rv = (Hwnd)windows[handle]; + } + return rv; + } + + public static IntPtr HandleFromObject(Hwnd obj) { + return obj.handle; + } + + public static Hwnd GetObjectFromWindow(IntPtr window) { + Hwnd rv; + lock (windows) { + rv = (Hwnd)windows[window]; + } + return rv; + } + + public static IntPtr GetHandleFromWindow(IntPtr window) { + Hwnd hwnd; + + lock (windows) { + hwnd = (Hwnd)windows[window]; + } + if (hwnd != null) { + return hwnd.handle; + } else { + return IntPtr.Zero; + } + } + + public static Borders GetBorderWidth (CreateParams cp) + { + Borders border_size = new Borders (); + + Size windowborder = ThemeEngine.Current.BorderSize; /*new Size (1, 1);*/ // This is the only one that can be changed from the display properties in windows. + Size border = ThemeEngine.Current.BorderStaticSize; /*new Size (1, 1);*/ + Size clientedge = ThemeEngine.Current.Border3DSize; /*new Size (2, 2);*/ + Size thickframe = new Size (2 + windowborder.Width, 2 + windowborder.Height); + Size dialogframe = ThemeEngine.Current.BorderSizableSize; /* new Size (3, 3);*/ + + if (cp.IsSet (WindowStyles.WS_CAPTION)) { + border_size.Inflate (dialogframe); + } else if (cp.IsSet (WindowStyles.WS_BORDER)) { + if (cp.IsSet (WindowExStyles.WS_EX_DLGMODALFRAME)) { + if (cp.IsSet (WindowStyles.WS_THICKFRAME) && (cp.IsSet (WindowExStyles.WS_EX_STATICEDGE) || cp.IsSet (WindowExStyles.WS_EX_CLIENTEDGE))) { + border_size.Inflate (border); + } + } else { + border_size.Inflate (border); + } + } else if (cp.IsSet (WindowStyles.WS_DLGFRAME)) { + border_size.Inflate (dialogframe); + } + + if (cp.IsSet (WindowStyles.WS_THICKFRAME)) { + if (cp.IsSet (WindowStyles.WS_DLGFRAME)) { + border_size.Inflate (border); + } else { + border_size.Inflate (thickframe); + } + } + + bool only_small_border; + Size small_border = Size.Empty; + + only_small_border = cp.IsSet (WindowStyles.WS_THICKFRAME) || cp.IsSet (WindowStyles.WS_DLGFRAME); + if (only_small_border && cp.IsSet (WindowStyles.WS_THICKFRAME) && !cp.IsSet (WindowStyles.WS_BORDER) && !cp.IsSet (WindowStyles.WS_DLGFRAME)) { + small_border = border; + } + + if (cp.IsSet (WindowExStyles.WS_EX_CLIENTEDGE | WindowExStyles.WS_EX_DLGMODALFRAME)) { + border_size.Inflate (clientedge + (only_small_border ? small_border : dialogframe)); + } else if (cp.IsSet (WindowExStyles.WS_EX_STATICEDGE | WindowExStyles.WS_EX_DLGMODALFRAME)) { + border_size.Inflate (only_small_border ? small_border : dialogframe); + } else if (cp.IsSet (WindowExStyles.WS_EX_STATICEDGE | WindowExStyles.WS_EX_CLIENTEDGE)) { + border_size.Inflate (border + (only_small_border ? Size.Empty : clientedge)); + } else { + if (cp.IsSet (WindowExStyles.WS_EX_CLIENTEDGE)) { + border_size.Inflate (clientedge); + } + if (cp.IsSet (WindowExStyles.WS_EX_DLGMODALFRAME) && !cp.IsSet (WindowStyles.WS_DLGFRAME)) { + border_size.Inflate (cp.IsSet (WindowStyles.WS_THICKFRAME) ? border : dialogframe); + } + if (cp.IsSet (WindowExStyles.WS_EX_STATICEDGE)) { + if (cp.IsSet (WindowStyles.WS_THICKFRAME) || cp.IsSet (WindowStyles.WS_DLGFRAME)) { + border_size.Inflate (new Size (-border.Width, -border.Height)); + } else { + border_size.Inflate (border); + } + } + } + + return border_size; + } + + public static Rectangle GetWindowRectangle (CreateParams cp, Menu menu) + { + return GetWindowRectangle (cp, menu, Rectangle.Empty); + } + + public static Rectangle GetWindowRectangle (CreateParams cp, Menu menu, Rectangle client_rect) + { + Rectangle rect; + Borders borders; + + borders = GetBorders (cp, menu); + + rect = new Rectangle (Point.Empty, client_rect.Size); + rect.Y -= borders.top; + rect.Height += borders.top + borders.bottom; + rect.X -= borders.left; + rect.Width += borders.left + borders.right; + + #if debug + Console.WriteLine ("GetWindowRectangle ({0}, {1}, {2}): {3}", cp, menu != null, client_rect, rect); + #endif + return rect; + } + + public Rectangle GetClientRectangle (int width, int height) + { + CreateParams cp = new CreateParams (); + cp.WindowStyle = initial_style; + cp.WindowExStyle = initial_ex_style; + return GetClientRectangle (cp, menu, width, height); + } + + // This could be greatly optimized by caching the outputs and only updating when something is moved + // in the parent planar space. To do that we need to track z-order in the parent space as well + public ArrayList GetClippingRectangles () + { + ArrayList masks = new ArrayList (); + + if (x < 0) { + masks.Add (new Rectangle (0, 0, x*-1, Height)); + if (y < 0) { + masks.Add (new Rectangle (x*-1, 0, Width, y*-1)); + } + } else if (y < 0) { + masks.Add (new Rectangle (0, 0, Width, y*-1)); + } + + foreach (Hwnd child in children) { + if (child.visible) + masks.Add (new Rectangle (child.X, child.Y, child.Width, child.Height)); + } + + if (parent == null) { + return masks; + } + + ArrayList siblings = parent.children; + + foreach (Hwnd sibling in siblings) { + IntPtr sibling_handle = whole_window; + + if (sibling == this) + continue; + + // This entire method should be cached to find all higher views at the time of query + do { + sibling_handle = XplatUI.GetPreviousWindow (sibling_handle); + + if (sibling_handle == sibling.WholeWindow && sibling.visible) { + + Rectangle intersect = Rectangle.Intersect (new Rectangle (X, Y, Width, Height), new Rectangle (sibling.X, sibling.Y, sibling.Width, sibling.Height)); + + if (intersect == Rectangle.Empty) + continue; + + intersect.X -= X; + intersect.Y -= Y; + + masks.Add (intersect); + } + } while (sibling_handle != IntPtr.Zero); + } + + return masks; + } + + public static Borders GetBorders (CreateParams cp, Menu menu) + { + + Borders borders = new Borders (); + + if (menu != null) { + int menu_height = menu.Rect.Height; + if (menu_height == 0) + menu_height = ThemeEngine.Current.CalcMenuBarSize (GraphicsContext, menu, cp.Width); + borders.top += menu_height; + } + + if (cp.IsSet (WindowStyles.WS_CAPTION)) { + int caption_height; + if (cp.IsSet (WindowExStyles.WS_EX_TOOLWINDOW)) { + caption_height = ThemeEngine.Current.ToolWindowCaptionHeight; + } else { + caption_height = ThemeEngine.Current.CaptionHeight; + } + borders.top += caption_height; + } + + Borders border_width = GetBorderWidth (cp); + + borders.left += border_width.left; + borders.right += border_width.right; + borders.top += border_width.top; + borders.bottom += border_width.bottom; + + return borders; + } + + public static Rectangle GetClientRectangle(CreateParams cp, Menu menu, int width, int height) { + Rectangle rect; + Borders borders; + + borders = GetBorders (cp, menu); + + rect = new Rectangle(0, 0, width, height); + rect.Y += borders.top; + rect.Height -= borders.top + borders.bottom; + rect.X += borders.left; + rect.Width -= borders.left + borders.right; + + #if debug + Console.WriteLine ("GetClientRectangle ({0}, {1}, {2}, {3}): {4}", cp, menu != null, width, height, rect); + #endif + + return rect; + } + + public static Graphics GraphicsContext { + get { + if (bmp_g == null) { + bmp = new Bitmap (1, 1, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + bmp_g = Graphics.FromImage (bmp); + } + + return bmp_g; + } + } + #endregion // Static Methods + + #region Instance Properties + public FormBorderStyle BorderStyle { + get { + return border_style; + } + + set { + border_style = value; + } + } + + public Rectangle ClientRect { + get { + if (client_rectangle == Rectangle.Empty) { + return DefaultClientRect; + } + return client_rectangle; + } + + set { + client_rectangle = value; + } + } + + public IntPtr Cursor { + get { + return cursor; + } + + set { + cursor = value; + } + } + + public IntPtr ClientWindow { + get { + return client_window; + } + + set { + client_window = value; + handle = value; + + zombie = false; + + if (client_window != IntPtr.Zero) { + lock (windows) { + if (windows[client_window] == null) { + windows[client_window] = this; + } + } + } + } + } + + public Region UserClip { + get { + return user_clip; + } + + set { + user_clip = value; + } + } + + public Rectangle DefaultClientRect { + get { + // We pass a Zero for the menu handle so the menu size is + // not computed this is done via an WM_NCCALC + CreateParams cp = new CreateParams (); + Rectangle rect; + + cp.WindowStyle = initial_style; + cp.WindowExStyle = initial_ex_style; + + rect = GetClientRectangle (cp, null, width, height); + + return rect; + } + } + + public bool ExposePending { + get { + return expose_pending; + } + } + + public IntPtr Handle { + get { + if (handle == IntPtr.Zero) { + throw new ArgumentNullException("Handle", "Handle is not yet assigned, need a ClientWindow"); + } + return handle; + } + } + + public int Height { + get { + return height; + } + + set { + height = value; + } + } + + public Menu Menu { + get { + return menu; + } + + set { + menu = value; + } + } + + public bool Reparented { + get { + return reparented; + } + + set { + reparented = value; + } + } + + public uint Opacity { + get { + return opacity; + } + + set { + opacity = value; + } + } + + public XEventQueue Queue { + get { + return queue; + } + + set { + queue = value; + } + } + + public bool Enabled { + get { + if (!enabled) { + return false; + } + + if (parent != null) { + return parent.Enabled; + } + + return true; + } + + set { + enabled = value; + } + } + + public IntPtr EnabledHwnd { + get { + if (Enabled || parent == null) { + return Handle; + } + + return parent.EnabledHwnd; + } + } + + public Point MenuOrigin { + get { + Form frm = Widget.FromHandle (handle) as Form; + if (frm != null && frm.window_manager != null) + return frm.window_manager.GetMenuOrigin (); + + Point pt; + Size border_3D_size = ThemeEngine.Current.Border3DSize; + + pt = new Point(0, 0); + + if (border_style == FormBorderStyle.Fixed3D) { + pt.X += border_3D_size.Width; + pt.Y += border_3D_size.Height; + } else if (border_style == FormBorderStyle.FixedSingle) { + pt.X += 1; + pt.Y += 1; + } + + if (this.title_style == TitleStyle.Normal) { + pt.Y += caption_height; + } else if (this.title_style == TitleStyle.Normal) { + pt.Y += tool_caption_height; + } + + return pt; + } + } + + public Rectangle Invalid { + get { + if (invalid_list.Count == 0) + return Rectangle.Empty; + + Rectangle result = (Rectangle)invalid_list[0]; + for (int i = 1; i < invalid_list.Count; i ++) { + result = Rectangle.Union (result, (Rectangle)invalid_list[i]); + } + return result; + } + } + + public Rectangle[] ClipRectangles { + get { + return (Rectangle[]) invalid_list.ToArray (typeof (Rectangle)); + } + } + + public Rectangle NCInvalid { + get { return nc_invalid; } + set { nc_invalid = value; } + + } + + public bool NCExposePending { + get { + return nc_expose_pending; + } + } + + public Hwnd Parent { + get { + return parent; + } + + set { + if (parent != null) + parent.children.Remove (this); + parent = value; + if (parent != null) + parent.children.Add (this); + } + } + + public bool Mapped { + get { + if (!mapped) { + return false; + } + + if (parent != null) { + return parent.Mapped; + } + + return true; + } + + set { + mapped = value; + } + } + + public int CaptionHeight { + get { return caption_height; } + set { caption_height = value; } + } + + public int ToolCaptionHeight { + get { return tool_caption_height; } + set { tool_caption_height = value; } + } + + public TitleStyle TitleStyle { + get { + return title_style; + } + + set { + title_style = value; + } + } + + public object UserData { + get { + return user_data; + } + + set { + user_data = value; + } + } + + public IntPtr WholeWindow { + get { + return whole_window; + } + + set { + whole_window = value; + + zombie = false; + + if (whole_window != IntPtr.Zero) { + lock (windows) { + if (windows[whole_window] == null) { + windows[whole_window] = this; + } + } + } + } + } + + public int Width { + get { + return width; + } + + set { + width = value; + } + } + + public bool Visible { + get { + return visible; + } + + set { + visible = value; + } + } + + public int X { + get { + return x; + } + + set { + x = value; + } + } + + public int Y { + get { + return y; + } + + set { + y = value; + } + } + + #endregion // Instance properties + + #region Methods + public void AddInvalidArea(int x, int y, int width, int height) { + AddInvalidArea(new Rectangle(x, y, width, height)); + } + + public void AddInvalidArea(Rectangle rect) { + ArrayList tmp = new ArrayList (); + foreach (Rectangle r in invalid_list) { + if (!rect.Contains (r)) { + tmp.Add (r); + } + } + tmp.Add (rect); + invalid_list = tmp; + } + + public void ClearInvalidArea() { + invalid_list.Clear(); + expose_pending = false; + } + + public void AddNcInvalidArea(int x, int y, int width, int height) { + if (nc_invalid == Rectangle.Empty) { + nc_invalid = new Rectangle (x, y, width, height); + return; + } + + int right, bottom; + right = Math.Max (nc_invalid.Right, x + width); + bottom = Math.Max (nc_invalid.Bottom, y + height); + nc_invalid.X = Math.Min (nc_invalid.X, x); + nc_invalid.Y = Math.Min (nc_invalid.Y, y); + + nc_invalid.Width = right - nc_invalid.X; + nc_invalid.Height = bottom - nc_invalid.Y; + } + + public void AddNcInvalidArea(Rectangle rect) { + if (nc_invalid == Rectangle.Empty) { + nc_invalid = rect; + return; + } + nc_invalid = Rectangle.Union (nc_invalid, rect); + } + + public void ClearNcInvalidArea() { + nc_invalid = Rectangle.Empty; + nc_expose_pending = false; + } + + public override string ToString() { + return String.Format("Hwnd, Mapped:{3} ClientWindow:0x{0:X}, WholeWindow:0x{1:X}, Zombie={4}, Parent:[{2:X}]", client_window.ToInt32(), whole_window.ToInt32(), parent != null ? parent.ToString() : "<null>", Mapped, zombie); + } + + public static Point GetNextStackedFormLocation (CreateParams cp, Hwnd parent_hwnd) + { + if (cp.control == null) + return Point.Empty; + + int X = cp.X; + int Y = cp.Y; + Point previous, next; + Rectangle within; + + if (parent_hwnd != null) { + Widget parent = cp.control.Parent; + previous = parent_hwnd.previous_child_startup_location; + if (parent_hwnd.client_rectangle == Rectangle.Empty && parent != null) { + within = parent.ClientRectangle; + } else { + within = parent_hwnd.client_rectangle; + } + } else { + previous = Hwnd.previous_main_startup_location; + within = ShiftUI.Screen.PrimaryScreen.WorkingArea; + } + + if (previous.X == int.MinValue || previous.Y == int.MinValue) { + next = Point.Empty; + } else { + next = new Point (previous.X + 22, previous.Y + 22); + } + + if (!within.Contains (next.X * 3, next.Y * 3)) { + next = Point.Empty; + } + + if (next == Point.Empty && cp.Parent == IntPtr.Zero) { + next = new Point (22, 22); + } + + if (parent_hwnd != null) { + parent_hwnd.previous_child_startup_location = next; + } else { + Hwnd.previous_main_startup_location = next; + } + + if (X == int.MinValue && Y == int.MinValue) { + X = next.X; + Y = next.Y; + } + + return new Point (X, Y); + } + + #endregion // Methods + + internal struct Borders + { + public int top; + public int bottom; + public int left; + public int right; + + public void Inflate (Size size) + { + left += size.Width; + right += size.Width; + top += size.Height; + bottom += size.Height; + } + + public override string ToString () + { + return string.Format("{{top={0}, bottom={1}, left={2}, right={3}}}", top, bottom, left, right); + } + + public static bool operator == (Borders a, Borders b) + { + return (a.left == b.left && a.right == b.right && a.top == b.top && a.bottom == b.bottom); + } + + public static bool operator != (Borders a, Borders b) + { + return !(a == b); + } + + public override bool Equals (object obj) + { + return base.Equals (obj); + } + + public override int GetHashCode () + { + return base.GetHashCode (); + } + } + } +} |
