4

Yes, I do realize there have been many question like this, but none of them worked for me. I need to check whether the system cursor is hidden.

I have tried Cursor.Current == null. It did not do anything, ever. ( I hid the cursor with fullscreen Youtube, Discord and Krita ).

I also tried using GetCursorInfo from user32.dll and checking if the flag is 0 ( hidden ). Nothing.

[StructLayout( LayoutKind.Sequential )]
struct POINT {
    public Int32 x;
    public Int32 y;
}

[StructLayout( LayoutKind.Sequential )]
struct CURSORINFO {
    public Int32 cbSize;        

    public Int32 flags;         

    public IntPtr hCursor;
    public POINT ptScreenPos;
}

[DllImport( "user32.dll" )]
static extern bool GetCursorInfo ( out CURSORINFO pci );

public static bool CursorHidden () {
    CURSORINFO cinfo;
    cinfo.cbSize = Marshal.SizeOf( typeof( CURSORINFO ) );
    GetCursorInfo( out cinfo );
    return cinfo.flags == 0;
}

My goal is to hide a screen overlay UI when the cursor hides.

So, how do I check if the system cursor is hidden in any other way?

Edit:

Okay, I've made a discovery with the aid of Ahmed Abdelhameed. Basically, browsers and the like don't render the cursor, but its still considered visible by the system. However, the handle is changed. There is a limited amout of handles the system offers by default ( Cursors.<Name>.Handle ), and the invisible browser cursor uses none of them.

enum CursorTypes { ... }
...
CURSORINFO cinfo;
... // see code above
Cursor[] cursors = new Cursor[] { ... }; // listed all of them in the same order as CursorTypes
int type = 1;
foreach ( Cursor cursor in cursors ) {
    if ( cinfo.hCursor == cursor.Handle ) {
        return (CursorTypes)type;
    }
    type++;
}
return CursorTypes.Other;

This is not a complete sulution however because browsers and other software can create a new type of cursor other than invisible ( like tilted backwards in VS ) which will also return CursorTypes.Other.

As Herohtar pointed out, hidden cursors usually have large pointer values ( In my case, Math.Abs( pointer ) > 10000000 worked most of the time ). That covers most browsers.

I honestly don't think there is much more we can do in terms of detecting a hidden cursor. In the rare case that a cusor is not hidden but my app thinks it is or the opposite, I decided to just have an option to let the user click a button in the settings that after a set amout of time adds the current cursor handle to exceptions.

Peri
  • 574
  • 1
  • 3
  • 19
  • Have you tried using `System.Windows.Forms.Cursor` and check its position if it is within your window's visible area? – Angelo Oct 31 '19 at 23:06
  • @Angelo How would that relate to whether the cursor is hidden or not? My window is a transparent click-through overlay covering the whole screen. – Peri Oct 31 '19 at 23:10
  • 3
    When the cursor is hidden by most programs, the value of both `flags` and `hCursor` (the handle) of the `CURSORINFO` struct should be zero. However, it appears that when the cursor is hidden _by the browser_, the value of `flags` doesn't change (stays 1) and the value of `hCursor` is set to a custom handle which seems to be different for each browser. I'm not sure whether or not there's a way to detect that. Will let you know if I find something. – 41686d6564 stands w. Palestine Oct 31 '19 at 23:34
  • 1
    Webpages mostly use CSS (i.e., `cursor: none`) to "hide" the cursor temporarily. What `cursor: none` does is that it tells the browser to render the cursor invisible. The way the browsers seem to do this is by using a custom (invisible) cursor. In other words, the actual OS cursor is still there and has a handle. There are [other ways](https://stackoverflow.com/a/39795813/8967612) to hide the cursor by a web application that actually remove the OS cursor but that's not what `cursor: none` does... – 41686d6564 stands w. Palestine Nov 01 '19 at 00:01
  • 2
    ...What you could do, as a workaround, is maybe obtain the handle of the invisible cursor for the common browsers and compare it to the handle of `CURSORINFO` _whenever the active window is a browser_. This is obviously very hacky and not foolproof though. Hopefully, someone can come up with a better solution. – 41686d6564 stands w. Palestine Nov 01 '19 at 00:02
  • https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-attachthreadinput – Hans Passant Nov 01 '19 at 00:22
  • Indeed, `this.Cursor.Hide()` on a WinForms works with the WinAPI but `Cursor.Current` remains Default, and it does not work at all if the browser go to fullscreen and hide the mouse cursor... flags remains 1. –  Nov 01 '19 at 03:35
  • What I could also do, as a different workaround, is perhaps somehow check whether the active window is in fullscreen mode, and if its a browser, i know that the cursor will hide after 3 seconds of inactivity. – Peri Nov 01 '19 at 11:31
  • @Flutterish Nope, that's not always true. Try pressing `F11` on this page, for example. The browser will enter fullscreen mode but the cursor will never disappear. – 41686d6564 stands w. Palestine Nov 01 '19 at 12:17
  • 1
    Looking at the values on my system, the "regular" cursor handles are things like `65539`, `65541`, `65555`. However, when the cursor is hidden by the browser, I get values like `-977859735 (0xFFFFFFFFC5B70B69)` (64-bit pointers). I'm not sure if that's a valid pointer or not, but even if it is, it's so completely different from the usual values, it seems like you should just be able to check for something extremely different like that? – Herohtar Nov 02 '19 at 03:41

1 Answers1

0

There isn't just one way to detect if a cursor is hidden. Some of the time, the system will be told not to render it, some of the time the software itself will not render it, and sometimes it renders it as something that is not a cursor ( digital art brush outline for example ).

In the most common case, when the system itself doesn't render the cursor, all you have to do is check if the cursors flag is 0 ( hidden ). ( or take a shrotcut and check if Cursor.Current == null )

[StructLayout( LayoutKind.Sequential )]
struct POINT {
    public Int32 x;
    public Int32 y;
}

[StructLayout( LayoutKind.Sequential )]
struct CURSORINFO {
    public Int32 cbSize;        

    public Int32 flags;         

    public IntPtr hCursor;
    public POINT ptScreenPos;
}

[DllImport( "user32.dll" )]
static extern bool GetCursorInfo ( out CURSORINFO pci );

CURSORINFO GetCinfo () {
    CURSORINFO cinfo;
    cinfo.cbSize = Marshal.SizeOf( typeof( CURSORINFO ) );
    GetCursorInfo( out cinfo );
    return cinfo;
}
public bool FlagHidden () {
    CURSORINFO cinfo = GetCinfo();
    return cingo.flags == 0 || Cursor.Current == null; // using both for extra safety
}

Now we encounter web browsers. Browsers dont render the hidden cursor, but they do change the cursors handle. Most of the time, but not necessarily always the value of the handle will be large.

public bool BrowserHidden () {
    CURSORINFO cinfo = GetCinfo();
    return Math.Abs( cinfo.hCursor.ToInt64() ) > 10000000;
}

Now, we are almost done, but don't forget that there will be exceptions. Krita, for example, renders an outline of a brush while hiding the cursor, but as far as the system is concerned, the cursor is still visible. There is not an easy, generic way to deal with this, so we pretty much have to explicitly tell our software to look out for these specyfic cases.

List<IntPtr> exceptions = new List<IntPtr>{ ... }; // put all the common exceptions here, implement some way to add / remove them
public bool IsException () {
    CURSORINFO cinfo = GetCinfo();
    return exceptions.Contains( cinfo.hCursor );
}

public bool CursorHidden () {
    bool hidden = FlagHidden() || BrowserHidden();
    return hidden != IsException(); // hidden xor exception ( inverts hidden if its an exception )
}

There could be a case when pointer X can mean A to software B but C to software D. This is very unlikely, but if you want to be extra safe, the exceptions list should be a list of touples of apps and handles.

Peri
  • 574
  • 1
  • 3
  • 19