How can I get notified when the cursor changes?


Today's Little Program tracks changs to the cursor. You might want to do this as part of instrumentation, in order to see how often the user is staring at an hourglass, for example. (It's easier to make the Move cursor appear on demand, so I'll use that instead.)

The magic words here are OBJID_CURSOR and Get­Cursor­Info.

#include <windows.h>
#include <stdio.h>

void log()
{
  CURSORINFO ci = { sizeof(ci) };
  GetCursorInfo(&ci);
  printf("showing = %d, suppressed = %d, pos = (%d, %d), handle = %p\n",
    !!(ci.flags & CURSOR_SHOWING),
    !!(ci.flags & CURSOR_SUPPRESSED),
    ci.ptScreenPos.x,
    ci.ptScreenPos.y,
    ci.hCursor);
}

The log function prints information about the current cursor. For now, we just dump it to the screen, but obviously you could do something fancier with it. The CURSOR_SHOWING flag tells you whether the cursor show count is nonnegative, which is what classically controls whether the cursor is visible on the screen. The CURSOR_SUPPRESSED flag tells tells you that nominally visible cursor is not visible to the user because the user touched the screen with a finger or pen.

void CALLBACK WinEventProc(
  HWINEVENTHOOK hook,
  DWORD event,
  HWND hwnd,
  LONG idObject,
  LONG idChild,
  DWORD idEventThread,
  DWORD time)
{
  if (hwnd == nullptr &&
      idObject == OBJID_CURSOR &&
      idChild == CHILDID_SELF) {
    switch (event) {
    case EVENT_OBJECT_HIDE:
      printf("cursor hidden\n");
      log();
      break;
    case EVENT_OBJECT_SHOW:
      printf("cursor shown\n");
      log();
      break;
    case EVENT_OBJECT_NAMECHANGE:
      printf("cursor changed\n");
      log();
      break;
    }
  }
}

Our event hook procedure checks if we're being notified about the cursor. If so, then we print some information about the event we received, and then log the cursor details.

int __cdecl main(int, char**)
{
  printf("Move cursor = %p\n", LoadCursor(nullptr, IDC_SIZEALL));

  HWINEVENTHOOK hook = SetWinEventHook(
    EVENT_OBJECT_SHOW,
    EVENT_OBJECT_NAMECHANGE,
    nullptr,
    WinEventProc,
    GetCurrentProcessId(),
    GetCurrentThreadId(),
    WINEVENT_OUTOFCONTEXT);

  MessageBox(nullptr, TEXT("Press Ok when bored"),
             TEXT("Title"), MB_OK);

  UnhookWinEvent(hook);
  return 0;
}

Our main program prints the handle of the Move cursor, just to demonstrate that the handle will match the output. Next, it installs the event hook on its own process and thread. (If you want to monitor the entire process, then pass 0 for the thread ID. If you wanted to monitor all processes on the desktop, then pass 0 for both the process ID and thread ID.) Next, we display a message box to give you a way to exit the program, and to fire up a message pump. After you are bored, we remove the hook and exit.

Now, I chose the Move cursor because it is pretty much the only cursor you can get to from a message box: Press Alt+Space, then hit M for Move. Bingo, a Move cursor. And you can see the program spit out the new cursor handle, and it should match the value printed at the start of the program.

Comments (6)
  1. Brian_EE says:

    >The CURSOR_SUPPRESSED flag tells tells you that the cursor is not visible because the user touched the screen with a finger or pen.

    Unreliable. I tried touching my 19" Dell monitors with both my fingers and pens, but the cursor remained visible. (Walking myself over to nit-picker's corner.....)

  2. Chris Long says:

    @Brian_EE, doubling down on the nit-picking... Raymond didn't say that the cursor will always become hidden when you touch the screen, or that CURSOR_SUPPRESSED will always be set if the cursor is hidden (though both those things might still be true)... he just said that if CURSOR_SUPPRESSED is set, then the cursor is hidden because the user touched the screen. Your counter-example doesn't disprove that statement. See also the Wason selection task (en.wikipedia.org/.../Wason_selection_task).

  3. Brian_EE says:

    @Chris Long - I guess these days everyone is so used to devices having touchscreens that one doesn't think to differentiate that and instead uses the generic "screen" term. Related to that, my college professor fiancé has been lamenting that you can't find a decent new laptop without a touchscreen. Although I haven't done any analysis of my own, she claims the touchscreens aren't as bright as the regular LCD screens.

  4. Chris Long says:

    Ah, I see I missed the sarcasm in your comment... oops. You got me :-)

    I can confirm that touching my 21" Sony screen from circa 2004 doesn't hide the cursor either.

  5. Sven2 says:

    When I touch my HP w1907 screen at the cursor position, the cursor becomes occluded by the thumb and thus invisible. How did you manage to touch the screen but not occlude the cursor?

    I don't know if there was a CURSOR_SUPPRESSED message when I touched the screen but it may very well be.

  6. Deltics says:

    @Sven2 - Were you running the hook program at the time ?

    Reading Raymond's explanation I would expect the CURSOR_SUPPRESSED flag to be set if you occluded the cursor with your finger or a pen.  Perhaps you didn't completely occlude the cursor.

    However, I notice that you mention you were testing with your thumb, which being neither a finger nor a pen you would not reasonably expect to result in the flag being set, whether or not you have occluded the cursor.

Comments are closed.

Skip to main content