Click here to Skip to main content
15,879,095 members
Articles / Programming Languages / C++
Article

Auto Wait Cursor

Rate me:
Please Sign up or sign in to vote.
4.25/5 (15 votes)
31 Dec 20014 min read 125.2K   1.5K   47   20
Automate changing the mouse cursor to the hourglass wait cursor.

Introduction

It's recommended practice to change the mouse cursor to an hourglass wait cursor to inform the user that the program is working on something. This is easy enough to do when the programmer knows in advance that something is going to take a while. However, there are often circumstances where you can't predict how long something is going to take, so it's hard to know whether to display the hourglass or not. Ideally, you'd like the cursor to change to an hourglass automatically, as soon as your task had run for more than some time limit, e.g. 1/10 of a second. I needed to do this for a project of mine so I searched through my books and the internet, but couldn't find any applicable code. The closest thing I could find was an article explaining how to do it in Java. It didn't seem like it should be too tough to write a Windows/C++ version, but it ended up taking me longer than I expected to get it working properly so I thought I'd share the results.

Implementation

The basic idea I had was to create a secondary thread that would act as a "timer". The message loop would keep resetting the timer so it normally wouldn't run out. However, if a task took longer, then the timer would run out and the secondary thread would change the cursor to an hourglass. It didn't' take long to set this up, but mysteriously, it didn't work. A little debugging reassured me that my code was working properly, but that the SetCursor call in the secondary thread wasn't working. I guessed that SetCursor didn't work from secondary threads, but a search of the MSDN documentation and the internet didn't find anything about this. Finally, I posted a question to comp.os.ms-windows.programmer.win32 and went home. The first few responses I got didn't really help, but the third response turned out to be the key. The message referred me to an article by Petter Hesselberg in the Nov. 2001 Windows Developers Journal - "The Right Time and Place for the Wait Cursor". I couldn't find the article, but the source-code was online and the key turned out to be using AttachThreadInput. Once I added this to my code, things started to work.

I actually thought I was finished until I discovered that when I pulled down a menu (or brought up a context menu) the cursor changed to an hourglass. The problem was that while the menu was displayed, my message loop wasn't running and so the timer wasn't getting reset. After a certain amount of head scratching I came up with the idea of using a GetMessage hook to reset the timer, since I figured the menu must still be calling GetMessage (or PeekMessage). Sure enough this solved the menu problem. (And probably some related issues like modal dialogs.)

Again I thought I was finished, but I found one last glitch. Just before a tool-tip appeared, the cursor flashed to an hourglass and back. I guess tool-tips don't call GetMessage or PeekMessage while they wait. I fixed this by simply making my timer longer than the tool-tip timer.

My last task was to extract the code, which I'd mixed in with my message loop, into something more reusable. I ended up packaging it into a C++ class. To use the class you simply have to create an instance of it inside your message loop. Something like:

#include "awcursor.h"

...

while (GetMessage(&msg, NULL, 0, 0))
{
    AutoWaitCursor awc;
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

The AutoWaitCursor constructor "starts" the timer, and the destructor (called automatically at the end of the loop) restores the cursor if necessary. The constructor also looks after creating the thread and the hook the first time around.

If you look at the code, you may notice that the only concession I've made to multi-threading is declaring several of the variables volatile. I don't make any attempt to synchronize the threads or prevent them from accessing variables at the same time. This was a deliberate choice, because I wanted to make the code as fast as possible in order to not add overhead to the message loop. How do I justify this? First, the variables are plain integers, and therefore reading and writing them is atomic anyway. Second, if in some rare case, a synchronization problem occurs, the worst that can happen is the cursor might be wrong momentarily. This is a small price to pay to keep the code simple and fast. In practice I haven't seen any problems.

For simplicity, I've included the definition/initialization of the static class members in the header file. This isn't the best setup since it means you can't include this header in more than one source file. Ideally, you'd put the definitions in a separate .cpp file. However, you normally only have one message loop in your program anyway, so this setup seems acceptable.

I hope you find the code and the explanation useful. Good luck!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Canada Canada
I've been programming for 25 years, first in C and then in C++. I'm a fan of design patterns and agile methodologies like extreme programming. My interests include language and database design, and memory management and garbage collection. The last few years I have been working more or less full time on the open source Suneido project.

Comments and Discussions

 
GeneralAttachThreadInput Pin
emshelly20-Jan-05 6:02
emshelly20-Jan-05 6:02 
GeneralRe: AttachThreadInput Pin
Andrew McKinlay20-Jan-05 12:18
Andrew McKinlay20-Jan-05 12:18 
GeneralRe: AttachThreadInput [modified] Pin
HQH15-Oct-07 15:06
HQH15-Oct-07 15:06 
QuestionHow to use in MFC program? Pin
LarryLeonard10-Apr-03 4:20
LarryLeonard10-Apr-03 4:20 
AnswerRe: How to use in MFC program? Pin
Andrew McKinlay10-Apr-03 5:42
Andrew McKinlay10-Apr-03 5:42 
GeneralRe: How to use in MFC program? Pin
Stefan Noll25-Aug-04 10:53
Stefan Noll25-Aug-04 10:53 
GeneralRe: How to use in MFC program? Pin
Andrew McKinlay3-Sep-04 8:21
Andrew McKinlay3-Sep-04 8:21 
Questionwhy bother? Pin
lucy16-Jan-03 7:51
lucy16-Jan-03 7:51 
GeneralGood Job~ Pin
Robbie Wang24-Aug-02 0:53
Robbie Wang24-Aug-02 0:53 
GeneralRe: Another possible way... Pin
Andrew McKinlay14-Aug-02 17:15
Andrew McKinlay14-Aug-02 17:15 
Interesting idea, might be worth trying. There are still a few rare situations where the current code shows the wait cursor when it shouldn't. It would be interesting to see whether this approach would work better. It seems awkward to require a window though - is there a message that could be sent without a specific window?

Andrew McKinlay - mckinlay@suneido.com
Suneido Software - http://www.suneido.com
GeneralDialog Box Problem Pin
Sonu_P4-Aug-02 18:15
Sonu_P4-Aug-02 18:15 
GeneralRe: Dialog Box Problem Pin
Andrew McKinlay5-Aug-02 5:11
Andrew McKinlay5-Aug-02 5:11 
GeneralAwesome! Pin
Swinefeaster17-Jun-02 14:25
Swinefeaster17-Jun-02 14:25 
GeneralTHANKS! Pin
Todd.Harvey31-Mar-09 7:57
Todd.Harvey31-Mar-09 7:57 
Generalbetter hook Pin
Andrew McKinlay25-Feb-02 5:17
Andrew McKinlay25-Feb-02 5:17 
GeneralRe: better hook Pin
Swinefeaster16-Jun-02 16:25
Swinefeaster16-Jun-02 16:25 
GeneralRe: better hook Pin
Andrew McKinlay17-Jun-02 4:49
Andrew McKinlay17-Jun-02 4:49 
GeneralRe: better hook Pin
Swinefeaster17-Jun-02 14:24
Swinefeaster17-Jun-02 14:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.