Last updated:
22. July 2001

User Interface Programming

(Start main menu) Home ­ Articles ­ Book ­ Resources (End main menu)

The Hourglass Cursor (Tech Tip)


(Start sub-menu)

Java

User Interface Programming Column

Features

Tech Tips

Reviews

Code Archive


Range Slider

Splitter Controls and Dialog Resizing


 

Feedback…

My blog »

(End sub-menu)

Windows app­li­ca­t­ions typically display an hourglass cursor during lengthy op­er­ations, as follows:

// Set hourglass cursor and save the current cursor:
const HCURSOR hcurSave =
    SetCursor( LoadCursor( 0, IDC_WAIT ) );
// ...perform lengthy op­er­ation here...
// Restore old cursor:
SetCursor( hcurSave );

SetCursor() returns a handle to the previous cursor; this seems to imply such usage. Most Windows books I’ve seen do the above, and the MFC class CWaitCursor encapsulates the same functionality.

So, what’s wrong with this picture? Consider the following sequence of events:

  1. When the op­er­ation starts, the cursor is an IDC_ARROW, a fact re­mem­ber­ed by hcurSave.
  2. During the op­er­ation, the user moves the mouse and the hourglass cursor comes to rest on a split bar.
  3. The op­er­ation ends and the cursor is changed back to an arrow.

The problem is that the cursor should not be an arrow when it’s on the split bar. As soon as the mouse pointer is moved, the cursor will display correctly, but in the meantime, there’s a tiny crack in the smooth surface of illusion we strive to create for our users.

This par­ti­cu­lar crack can be mended quite easily, and the result is actually simpler to use than the standard example above. What I need is a function analogous to InvalidateRect(), except that it operates on the cursor. I no longer need to save the existing cursor, and the above example becomes:

// Set hourglass cursor:
SetCursor( LoadCursor( 0, IDC_WAIT ) );
// ...perform lengthy op­er­ation here...
// Restore normal cursor:
InvalidateCursor();

The implementation of InvalidateCursor() is simple:

void InvalidateCursor( void ) {
   POINT pt; // Screen coordinates!
   GetCursorPos( &pt );
   SetCursorPos( pt.x, pt.y );
}

This moves the cursor to its current location (i.e., the cursor does not actually move anywhere), forcing a WM_SETCURSOR mes­sage in the process. Doing it this way is a lot simpler than synthesizing and dispatching a WM_SETCURSOR mes­sage.

Please note that GetCursorPos() and SetCursorPos() have changed some­what during the migration from Win16 to Win32. In Win16, these were void functions; in Win32, they each return TRUE for success and FALSE for failure. Presumably, this change is related to preemptive multithreading. As is its wont, however, Microsoft’s documentation is not very specific about possible causes of failure, except to say that the calling process must have WINSTA_READATTRIBUTES access to the win­dow station. This is relevant only for the Windows NT family of operating systems.

waitcurs.h and waitcurs.cpp show a C++ replacement class for CWaitCursor, including a some­what more careful Win32 implementation of the InvalidateCursor() functionality.


(Start bottom menu)

TopHomeArticlesBookResources
Win­dows De­vel­oper Maga­zineR&D BooksCMP Books
Amazon.comAmazon.co.ukContact Petter Hesselberg

(End bottom menu)

Figure 1: WaitCurs.h

Figure 2: WaitCurs.cpp