/ src / modules / MouseWithoutBorders / App / Class / CustomCursor.cs
CustomCursor.cs
  1  // Copyright (c) Microsoft Corporation
  2  // The Microsoft Corporation licenses this file to you under the MIT license.
  3  // See the LICENSE file in the project root for more information.
  4  
  5  // <summary>
  6  //     Create a customed cursor.
  7  // </summary>
  8  // <history>
  9  //     2008 created by Truong Do (ductdo).
 10  //     2009-... modified by Truong Do (TruongDo).
 11  //     2023- Included in PowerToys.
 12  // </history>
 13  using System;
 14  using System.Drawing;
 15  using System.Drawing.Imaging;
 16  using System.Runtime.InteropServices;
 17  using System.Windows.Forms;
 18  
 19  using MouseWithoutBorders.Core;
 20  
 21  // Disable the warning to preserve original code
 22  #pragma warning disable CA1716
 23  namespace MouseWithoutBorders.Class
 24  #pragma warning restore CA1716
 25  {
 26      internal struct IconInfo
 27      {
 28          // Suppress warning to match COM names
 29          #pragma warning disable SA1307
 30          internal bool fIcon;
 31          internal int xHotspot;
 32          internal int yHotspot;
 33          internal IntPtr hbmMask;
 34          internal IntPtr hbmColor;
 35          #pragma warning restore SA1307
 36      }
 37  
 38      internal class CustomCursor
 39      {
 40          private CustomCursor()
 41          {
 42          }
 43  
 44          internal static Cursor CreateCursor(Bitmap bmp, int xHotSpot, int yHotSpot)
 45          {
 46              IconInfo tmp = default;
 47              tmp.xHotspot = xHotSpot;
 48              tmp.yHotspot = yHotSpot;
 49              tmp.fIcon = false;
 50  
 51              // GetIconInfo(bmp.GetHicon(), ref tmp);
 52              tmp.hbmColor = bmp.GetHbitmap();
 53              tmp.hbmMask = bmp.GetHbitmap();
 54              return new Cursor(NativeMethods.CreateIconIndirect(ref tmp));
 55          }
 56  
 57          internal static Cursor CreateDotCursor()
 58          {
 59              Bitmap bitmap = new(32, 32, PixelFormat.Format24bppRgb);
 60              bitmap.MakeTransparent(Color.Black);
 61              Graphics g = Graphics.FromImage(bitmap);
 62              g.DrawLine(Pens.Gray, 0, 0, 1, 1);
 63              Cursor c = CreateCursor(bitmap, 0, 0);
 64              bitmap.Dispose();
 65              return c;
 66          }
 67  
 68          private static int hiding;
 69          private static NativeMethods.CURSORINFO ci;
 70  
 71          internal static void ShowFakeMouseCursor(int x, int y)
 72          {
 73              if (Setting.Values.DrawMouse)
 74              {
 75                  ci.cbSize = Marshal.SizeOf(typeof(NativeMethods.CURSORINFO));
 76                  _ = NativeMethods.GetCursorInfo(out ci);
 77  
 78                  // The cursor is hidden or suppressed.q
 79                  if (ci.flags != 1)
 80                  {
 81                      Common.DoSomethingInTheInputCallbackThread(
 82                          () =>
 83                      {
 84                          Common.MouseCursorForm ??= new FrmMouseCursor();
 85  
 86                          try
 87                          {
 88                              Common.MouseCursorForm.Text = string.Empty;
 89  
 90                              if (x == int.MinValue || y == int.MinValue)
 91                              {
 92                                  hiding = 3;
 93                              }
 94  
 95                              if (hiding > 0)
 96                              {
 97                                  hiding--;
 98  
 99                                  Common.MouseCursorForm.Hide();
100                              }
101                              else
102                              {
103                                  Common.MouseCursorForm.Left = x + 1;
104                                  Common.MouseCursorForm.Top = y + 1;
105                                  Common.MouseCursorForm.Width = Common.MouseCursorForm.Height = 32;
106                                  Common.MouseCursorForm.TopMost = true;
107                                  Common.MouseCursorForm.Show();
108                              }
109                          }
110                          catch (NullReferenceException)
111                          {
112                              Logger.Log($"{nameof(Common.MouseCursorForm)} has been set to null by another thread.");
113                              Common.MouseCursorForm = new FrmMouseCursor();
114                          }
115                          catch (ObjectDisposedException)
116                          {
117                              Logger.Log($"{nameof(Common.MouseCursorForm)} has been disposed.");
118                              Common.MouseCursorForm = new FrmMouseCursor();
119                          }
120                      },
121                          false);
122  
123                      return;
124                  }
125              }
126  
127              Common.DoSomethingInTheInputCallbackThread(
128                  () =>
129              {
130                  if (Common.MouseCursorForm != null)
131                  {
132                      try
133                      {
134                          Common.MouseCursorForm.Close();
135                          Common.MouseCursorForm.Dispose();
136                      }
137                      catch (NullReferenceException)
138                      {
139                          Logger.Log($"{nameof(Common.MouseCursorForm)} has already been set to null by another thread!");
140                      }
141                      catch (ObjectDisposedException)
142                      {
143                          Logger.Log($"{nameof(Common.MouseCursorForm)} has already been disposed!");
144                      }
145  
146                      Common.MouseCursorForm = null;
147                  }
148              },
149                  false);
150          }
151      }
152  }