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 }