aboutsummaryrefslogtreecommitdiff
path: root/source/ShiftUI/Cursor.cs
diff options
context:
space:
mode:
authorMichaelTheShifter <[email protected]>2016-07-20 09:40:36 -0400
committerMichaelTheShifter <[email protected]>2016-07-20 09:40:36 -0400
commitd40fed5ce2bc806a91245adb18039634eac13ed0 (patch)
treef1d7168aee6db109ac2c738ad18c9db667a6ba69 /source/ShiftUI/Cursor.cs
parentf1856e8ed30ed882229fd3fa2a4038122a5fb441 (diff)
downloadshiftos-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/Cursor.cs')
-rw-r--r--source/ShiftUI/Cursor.cs663
1 files changed, 663 insertions, 0 deletions
diff --git a/source/ShiftUI/Cursor.cs b/source/ShiftUI/Cursor.cs
new file mode 100644
index 0000000..a598e04
--- /dev/null
+++ b/source/ShiftUI/Cursor.cs
@@ -0,0 +1,663 @@
+// 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) 2004-2010 Novell, Inc.
+//
+// Authors:
+// Peter Bartok [email protected]
+//
+
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+using System.Reflection;
+
+namespace ShiftUI {
+ //[Editor(//"System.Drawing.Design.CursorEditor, " + Consts.AssemblySystem_Drawing_Design, typeof(System.Drawing.Design.UITypeEditor))]
+ [Serializable]
+ [TypeConverter(typeof(CursorConverter))]
+ public sealed class Cursor : IDisposable, ISerializable {
+ #region Internal Structs
+ [StructLayout (LayoutKind.Sequential)]
+ private struct CursorDir {
+ internal ushort idReserved; // Reserved
+ internal ushort idType; // resource type (2 for cursors)
+ internal ushort idCount; // how many cursors
+ internal CursorEntry[] idEntries; // the entries for each cursor
+ };
+
+ [StructLayout (LayoutKind.Sequential)]
+ private struct CursorEntry {
+ internal byte width; // Width of cursor
+ internal byte height; // Height of cursor
+ internal byte colorCount; // colors in cursor
+ internal byte reserved; // Reserved
+ internal ushort xHotspot; // Hotspot X
+ internal ushort yHotspot; // Hotspot Y
+ internal ushort bitCount; // Bits per pixel
+ internal uint sizeInBytes; // size of (CursorInfoHeader + ANDBitmap + ORBitmap)
+ internal uint fileOffset; // position in file
+ };
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct CursorInfoHeader {
+ internal uint biSize;
+ internal int biWidth;
+ internal int biHeight;
+ internal ushort biPlanes;
+ internal ushort biBitCount;
+ internal uint biCompression;
+ internal uint biSizeImage;
+ internal int biXPelsPerMeter;
+ internal int biYPelsPerMeter;
+ internal uint biClrUsed;
+ internal uint biClrImportant;
+ };
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct CursorImage {
+ internal CursorInfoHeader cursorHeader; // image header
+ internal uint[] cursorColors; // colors table
+ internal byte[] cursorXOR; // bits for XOR mask
+ internal byte[] cursorAND; // bits for AND mask
+ };
+ #endregion // Internal structs
+
+ #region Local Variables
+ private static Cursor current;
+ private CursorDir cursor_dir;
+ private CursorImage[] cursor_data;
+ private int id;
+
+ internal IntPtr handle;
+ private Size size;
+ private Bitmap shape;
+ private Bitmap mask;
+ private Bitmap cursor;
+ internal string name;
+ private StdCursor std_cursor = (StdCursor) (-1);
+
+ private object tag;
+
+ #endregion // Local Variables
+
+ #region Public Constructors
+ private void CreateCursor (Stream stream)
+ {
+ InitFromStream(stream);
+ this.shape = ToBitmap(true, false);
+ this.mask = ToBitmap(false, false);
+ handle = XplatUI.DefineCursor(shape, mask, Color.FromArgb(255, 255, 255), Color.FromArgb(255, 255, 255), cursor_dir.idEntries[id].xHotspot, cursor_dir.idEntries[id].yHotspot);
+ this.shape.Dispose();
+ this.shape = null;
+ this.mask.Dispose();
+ this.mask = null;
+
+ if (handle != IntPtr.Zero) {
+ this.cursor = ToBitmap(true, true);
+ }
+ }
+
+ internal Cursor (StdCursor cursor) : this (XplatUI.DefineStdCursor (cursor))
+ {
+ std_cursor = cursor;
+ }
+
+ private Cursor(SerializationInfo info, StreamingContext context)
+ {
+ }
+
+ private Cursor()
+ {
+ }
+
+ ~Cursor()
+ {
+ Dispose();
+ }
+
+ // This is supposed to take a Win32 handle
+ public Cursor (IntPtr handle)
+ {
+ this.handle = handle;
+ }
+
+ public Cursor (Stream stream)
+ {
+ CreateCursor(stream);
+ }
+
+ public Cursor (string fileName)
+ {
+ using (FileStream fs = File.OpenRead (fileName)) {
+ CreateCursor (fs);
+ }
+ }
+
+ public Cursor(byte[] cursor) {
+ using (Stream s = new MemoryStream(cursor)) {
+ if (s != null) {
+ CreateCursor (s);
+ return;
+ }
+ }
+
+
+ throw new ArgumentException ("byte[] array isn't a proper cursor.");
+ }
+ #endregion // Public Constructors
+
+ #region Public Static Properties
+ public static Rectangle Clip {
+ get {
+ IntPtr handle;
+ bool confined;
+ Rectangle rect;
+ Size size;
+
+ XplatUI.GrabInfo (out handle, out confined, out rect);
+ if (handle != IntPtr.Zero) {
+ return rect;
+ }
+
+ XplatUI.GetDisplaySize (out size);
+ rect.X = 0;
+ rect.Y = 0;
+ rect.Width = size.Width;
+ rect.Height = size.Height;
+ return rect;
+ }
+
+ [MonoTODO ("Stub, does nothing")]
+ [MonoInternalNote ("First need to add ability to set cursor clip rectangle to XplatUI drivers to implement this property")]
+ set {
+ ;
+ }
+ }
+
+ public static Cursor Current {
+ get {
+ if (current != null)
+ return current;
+ return Cursors.Default;
+ }
+
+ set {
+ if (current == value)
+ return;
+
+ current = value;
+ if (current == null){
+ // FIXME - define and set empty cursor
+ XplatUI.OverrideCursor(IntPtr.Zero);
+ } else
+ XplatUI.OverrideCursor(current.handle);
+ }
+ }
+
+ public static Point Position {
+ get {
+ int x;
+ int y;
+
+ XplatUI.GetCursorPos (IntPtr.Zero, out x, out y);
+ return new Point (x, y);
+ }
+
+ set {
+ XplatUI.SetCursorPos(IntPtr.Zero, value.X, value.Y);
+ }
+ }
+ #endregion // Public Static Properties
+
+ #region Public Instance Properties
+ public IntPtr Handle {
+ get {
+ return handle;
+ }
+ }
+
+ [MonoTODO ("Implemented for Win32, X11 always returns 0,0")]
+ public Point HotSpot {
+ get {
+ int cursor_w, cursor_h, hot_x, hot_y;
+ XplatUI.GetCursorInfo (Handle, out cursor_w, out cursor_h, out hot_x, out hot_y);
+
+ return new Point (hot_x, hot_y);
+ }
+ }
+
+ public Size Size {
+ get {
+ return size;
+ }
+ }
+
+ [Localizable (false)]
+ [Bindable (true)]
+ [TypeConverter (typeof (StringConverter))]
+ [DefaultValue (null)]
+ [MWFCategory ("Data")]
+ public object Tag {
+ get { return this.tag; }
+ set { this.tag = value; }
+ }
+
+ #endregion // Public Instance Properties
+
+ #region Public Static Methods
+ public static void Hide ()
+ {
+ XplatUI.ShowCursor(false);
+ }
+
+ public static void Show ()
+ {
+ XplatUI.ShowCursor(true);
+ }
+
+ public static bool operator != (Cursor left, Cursor right) {
+ if ((object)left == (object)right)
+ return false;
+
+ if ((object)left == null || (object)right == null)
+ return true;
+
+ if (left.handle == right.handle)
+ return false;
+ return true;
+ }
+
+
+ public static bool operator ==(Cursor left, Cursor right)
+ {
+ if ((object)left == (object)right)
+ return true;
+
+ if ((object)left == null || (object)right == null)
+ return false;
+
+ if (left.handle == right.handle)
+ return true;
+
+ return false;
+ }
+ #endregion // Public Static Methods
+
+ #region Public Instance Methods
+ public IntPtr CopyHandle() {
+ return handle;
+ }
+
+ public void Dispose ()
+ {
+ if (cursor != null) {
+ cursor.Dispose ();
+ cursor = null;
+ }
+
+ if (shape != null) {
+ shape.Dispose ();
+ shape = null;
+ }
+
+ if (mask != null) {
+ mask.Dispose ();
+ mask = null;
+ }
+
+ GC.SuppressFinalize (this);
+ }
+
+ public void Draw (Graphics g, Rectangle targetRect)
+ {
+ if (cursor == null && std_cursor != (StdCursor) (-1))
+ cursor = XplatUI.DefineStdCursorBitmap (std_cursor);
+
+ if (cursor != null) {
+ // Size of the targetRect is not considered at all
+ g.DrawImage (cursor, targetRect.X, targetRect.Y);
+ }
+ }
+
+ public void DrawStretched (Graphics g, Rectangle targetRect)
+ {
+ if (cursor == null && std_cursor != (StdCursor)(-1))
+ cursor = XplatUI.DefineStdCursorBitmap (std_cursor);
+
+ if (cursor != null) {
+ g.DrawImage (cursor, targetRect, new Rectangle(0, 0, cursor.Width, cursor.Height), GraphicsUnit.Pixel);
+ }
+ }
+
+ public override bool Equals (object obj)
+ {
+ if (!(obj is Cursor))
+ return false;
+
+ if (((Cursor)obj).handle == handle)
+ return true;
+
+ return false;
+ }
+
+ public override int GetHashCode()
+ {
+ return base.GetHashCode ();
+ }
+
+ public override string ToString()
+ {
+ if (name != null) {
+ return "[Cursor:" + name + "]";
+ }
+
+ throw new FormatException("Cannot convert custom cursors to string.");
+ }
+
+ void ISerializable.GetObjectData (SerializationInfo si, StreamingContext context)
+ {
+ MemoryStream ms;
+ BinaryWriter wr;
+ CursorImage ci;
+
+ ms = new MemoryStream ();
+ wr = new BinaryWriter (ms);
+ ci = cursor_data [this.id];
+
+ // Build the headers, first the CursorDir
+ wr.Write ((ushort) 0); // Reserved
+ wr.Write ((ushort) 2); // Resource type
+ wr.Write ((ushort) 1); // Count
+
+ // Next the CursorEntry
+ wr.Write ((byte)cursor_dir.idEntries [this.id].width);
+ wr.Write ((byte)cursor_dir.idEntries [this.id].height);
+ wr.Write ((byte)cursor_dir.idEntries [this.id].colorCount);
+ wr.Write ((byte)cursor_dir.idEntries [this.id].reserved);
+ wr.Write ((ushort)cursor_dir.idEntries [this.id].xHotspot);
+ wr.Write ((ushort)cursor_dir.idEntries [this.id].yHotspot);
+ wr.Write ((uint)(40 + (ci.cursorColors.Length * 4) + ci.cursorXOR.Length + ci.cursorAND.Length));
+ wr.Write ((uint)(6 + 16)); // CursorDir + CursorEntry size
+
+ // Then the CursorInfoHeader
+ wr.Write (ci.cursorHeader.biSize);
+ wr.Write (ci.cursorHeader.biWidth);
+ wr.Write (ci.cursorHeader.biHeight);
+ wr.Write (ci.cursorHeader.biPlanes);
+ wr.Write (ci.cursorHeader.biBitCount);
+ wr.Write (ci.cursorHeader.biCompression);
+ wr.Write (ci.cursorHeader.biSizeImage);
+ wr.Write (ci.cursorHeader.biXPelsPerMeter);
+ wr.Write (ci.cursorHeader.biYPelsPerMeter);
+ wr.Write (ci.cursorHeader.biClrUsed);
+ wr.Write (ci.cursorHeader.biClrImportant);
+
+ for (int i = 0; i < ci.cursorColors.Length; i++)
+ wr.Write(ci.cursorColors[i]);
+
+ wr.Write (ci.cursorXOR);
+ wr.Write (ci.cursorAND);
+ wr.Flush ();
+
+ si.AddValue ("CursorData", ms.ToArray ());
+ }
+ #endregion // Public Instance Methods
+
+ #region Private Methods
+ private void InitFromStream (Stream stream)
+ {
+ ushort entry_count;
+ CursorEntry ce;
+ uint largest;
+
+ //read the cursor header
+ if (stream == null || stream.Length == 0)
+ throw new ArgumentException ("The argument 'stream' must be a picture that can be used as a cursor", "stream");
+
+ BinaryReader reader = new BinaryReader (stream);
+
+ cursor_dir = new CursorDir ();
+ cursor_dir.idReserved = reader.ReadUInt16();
+ cursor_dir.idType = reader.ReadUInt16();
+ if (cursor_dir.idReserved != 0 || !(cursor_dir.idType == 2 || cursor_dir.idType == 1))
+ throw new ArgumentException ("Invalid Argument, format error", "stream");
+
+ entry_count = reader.ReadUInt16();
+ cursor_dir.idCount = entry_count;
+ cursor_dir.idEntries = new CursorEntry[entry_count];
+ cursor_data = new CursorImage[entry_count];
+
+ //now read in the CursorEntry structures
+ for (int i=0; i < entry_count; i++){
+ ce = new CursorEntry();
+
+ ce.width = reader.ReadByte();
+ ce.height = reader.ReadByte();
+ ce.colorCount = reader.ReadByte();
+ ce.reserved = reader.ReadByte();
+ ce.xHotspot = reader.ReadUInt16();
+ ce.yHotspot = reader.ReadUInt16();
+ if (cursor_dir.idType == 1) {
+ ce.xHotspot = (ushort)(ce.width / 2);
+ ce.yHotspot = (ushort)(ce.height / 2);
+ }
+ ce.sizeInBytes = reader.ReadUInt32();
+ ce.fileOffset = reader.ReadUInt32();
+
+ cursor_dir.idEntries[i] = ce;
+ }
+
+ // If we have more than one pick the largest cursor
+ largest = 0;
+ for (int j = 0; j < entry_count; j++){
+ if (cursor_dir.idEntries[j].sizeInBytes >= largest) {
+ largest = cursor_dir.idEntries[j].sizeInBytes;
+ this.id = (ushort)j;
+ this.size.Height = cursor_dir.idEntries[j].height;
+ this.size.Width = cursor_dir.idEntries[j].width;
+ }
+ }
+
+ //now read in the cursor data
+ for (int j = 0; j < entry_count; j++) {
+ CursorImage curdata;
+ CursorInfoHeader cih;
+ byte[] buffer;
+ BinaryReader cih_reader;
+ int num_colors;
+ int cursor_height;
+ int bytes_per_line;
+ int xor_size;
+ int and_size;
+
+ curdata = new CursorImage();
+ cih = new CursorInfoHeader();
+
+ stream.Seek (cursor_dir.idEntries[j].fileOffset, SeekOrigin.Begin);
+ buffer = new byte [cursor_dir.idEntries[j].sizeInBytes];
+ stream.Read (buffer, 0, buffer.Length);
+
+ cih_reader = new BinaryReader(new MemoryStream(buffer));
+
+ cih.biSize = cih_reader.ReadUInt32 ();
+ if (cih.biSize != 40) {
+ throw new ArgumentException ("Invalid cursor file", "stream");
+ }
+ cih.biWidth = cih_reader.ReadInt32 ();
+ cih.biHeight = cih_reader.ReadInt32 ();
+ cih.biPlanes = cih_reader.ReadUInt16 ();
+ cih.biBitCount = cih_reader.ReadUInt16 ();
+ cih.biCompression = cih_reader.ReadUInt32 ();
+ cih.biSizeImage = cih_reader.ReadUInt32 ();
+ cih.biXPelsPerMeter = cih_reader.ReadInt32 ();
+ cih.biYPelsPerMeter = cih_reader.ReadInt32 ();
+ cih.biClrUsed = cih_reader.ReadUInt32 ();
+ cih.biClrImportant = cih_reader.ReadUInt32 ();
+
+ curdata.cursorHeader = cih;
+
+ //Read the number of colors used and corresponding memory occupied by
+ //color table. Fill this memory chunk into rgbquad[]
+ switch (cih.biBitCount){
+ case 1: num_colors = 2; break;
+ case 4: num_colors = 16; break;
+ case 8: num_colors = 256; break;
+ default: num_colors = 0; break;
+ }
+
+ curdata.cursorColors = new uint[num_colors];
+ for (int i = 0; i < num_colors; i++) {
+ curdata.cursorColors[i] = cih_reader.ReadUInt32 ();
+ }
+
+ //XOR mask is immediately after ColorTable and its size is
+ //icon height* no. of bytes per line
+
+ //cursor height is half of BITMAPINFOHEADER.biHeight, since it contains
+ //both XOR as well as AND mask bytes
+ cursor_height = cih.biHeight/2;
+
+ //bytes per line should should be uint aligned
+ bytes_per_line = ((((cih.biWidth * cih.biPlanes * cih.biBitCount)+ 31)>>5)<<2);
+
+ //Determine the XOR array Size
+ xor_size = bytes_per_line * cursor_height;
+ curdata.cursorXOR = new byte[xor_size];
+ for (int i = 0; i < xor_size; i++) {
+ curdata.cursorXOR[i] = cih_reader.ReadByte();
+ }
+
+ //Determine the AND array size
+ and_size = (int)(cih_reader.BaseStream.Length - cih_reader.BaseStream.Position);
+ curdata.cursorAND = new byte[and_size];
+ for (int i = 0; i < and_size; i++) {
+ curdata.cursorAND[i] = cih_reader.ReadByte();
+ }
+
+ cursor_data[j] = curdata;
+ cih_reader.Close();
+ }
+
+ reader.Close();
+ }
+
+ private Bitmap ToBitmap(bool xor, bool transparent)
+ {
+ CursorImage ci;
+ CursorInfoHeader cih;
+ int ncolors;
+ Bitmap bmp;
+ BitmapData bits;
+ ColorPalette pal;
+ int biHeight;
+ int bytesPerLine;
+
+ if (cursor_data == null)
+ return new Bitmap(32, 32);
+
+ ci = cursor_data[this.id];
+ cih = ci.cursorHeader;
+ biHeight = cih.biHeight / 2;
+
+ if (!xor) {
+ // The AND mask is 1bit - very straightforward
+ bmp = new Bitmap(cih.biWidth, biHeight, PixelFormat.Format1bppIndexed);
+ pal = bmp.Palette;
+ pal.Entries[0] = Color.FromArgb(0, 0, 0);
+ pal.Entries[1] = Color.FromArgb(unchecked((int)0xffffffffff));
+ bits = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
+
+ for (int y = 0; y < biHeight; y++) {
+ Marshal.Copy(ci.cursorAND, bits.Stride * y, (IntPtr)(bits.Scan0.ToInt64() + bits.Stride * (biHeight - 1 - y)), bits.Stride);
+ }
+
+ bmp.UnlockBits(bits);
+ } else {
+ ncolors = (int)cih.biClrUsed;
+ if (ncolors == 0) {
+ if (cih.biBitCount < 24) {
+ ncolors = (int)(1 << cih.biBitCount);
+ }
+ }
+
+ switch(cih.biBitCount) {
+ case 1: { // Monochrome
+ bmp = new Bitmap (cih.biWidth, biHeight, PixelFormat.Format1bppIndexed);
+ break;
+ }
+
+ case 4: { // 4bpp
+ bmp = new Bitmap (cih.biWidth, biHeight, PixelFormat.Format4bppIndexed);
+ break;
+ }
+
+ case 8: { // 8bpp
+ bmp = new Bitmap (cih.biWidth, biHeight, PixelFormat.Format8bppIndexed);
+ break;
+ }
+
+ case 24:
+ case 32: { // 32bpp
+ bmp = new Bitmap (cih.biWidth, biHeight, PixelFormat.Format32bppArgb);
+ break;
+ }
+
+ default:
+ throw new Exception("Unexpected number of bits:" + cih.biBitCount.ToString());
+ }
+
+ if (cih.biBitCount < 24) {
+ pal = bmp.Palette; // Managed palette
+ for (int i = 0; i < ci.cursorColors.Length; i++)
+ pal.Entries[i] = Color.FromArgb((int)ci.cursorColors[i] | unchecked((int)0xff000000));
+ bmp.Palette = pal;
+ }
+
+ bytesPerLine = (int)((((cih.biWidth * cih.biBitCount) + 31) & ~31) >> 3);
+ bits = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
+
+ for (int y = 0; y < biHeight; y++)
+ Marshal.Copy(ci.cursorXOR, bytesPerLine * y, (IntPtr)(bits.Scan0.ToInt64() + bits.Stride * (biHeight - 1 - y)), bytesPerLine);
+
+ bmp.UnlockBits(bits);
+ }
+
+ if (transparent) {
+ bmp = new Bitmap(bmp); // This makes a 32bpp image out of an indexed one
+ // Apply the mask to make properly transparent
+ for (int y = 0; y < biHeight; y++) {
+ for (int x = 0; x < cih.biWidth / 8; x++) {
+ for (int bit = 7; bit >= 0; bit--) {
+ if (((ci.cursorAND[y * cih.biWidth / 8 +x] >> bit) & 1) != 0)
+ bmp.SetPixel(x*8 + 7-bit, biHeight - y - 1, Color.Transparent);
+ }
+ }
+ }
+ }
+
+ return bmp;
+ }
+ #endregion // Private Methods
+ }
+}