Reputation: 47956
When you launch a Windows desktop application, the system changes the arrow to one with an animated busy spinner (presumably from IDI_ARROW to IDI_APPSTARTING). Once the application presents a window, the cursor is restored to indicate that it's ready for input.
With my own Win32 applications, the spinner continues for several (5?) seconds, even though the main window is fully rendered and ready for interaction. It was my understanding that the spinner vanishes once the just-launched program started pumping messages. More specifically, I thought it used the same criteria as WaitForInputIdle.
Since Windows 10, however, some applications, including ones I've written, appear to be stuck with the busy cursor for the duration of the timeout—even beyond the moment that the application becomes responsive to mouse and keyboard input.
What should I do in my programs to let the system know that initialization is complete and the spinner is no longer needed?
UPDATE: The problem occurs only when launching the application from the keyboard (like a CMD prompt, the Run window, or using the keyboard to launch the process in the debugger in Visual Studio). If you double-click the program with the mouse, the spinner vanishes quickly, as expected.
UPDATE 2: Given that others cannot repro, my best guess is that there's a buggy driver involved. Thanks to everyone who helped brainstorm in the comments.
Self-contained repro below.
#include <Windows.h>
LRESULT OnPaint(HWND hwnd, UINT, WPARAM, LPARAM) {
PAINTSTRUCT ps;
::BeginPaint(hwnd, &ps);
RECT rc;
::GetClientRect(hwnd, &rc);
::DrawTextW(ps.hdc, L"Hello, World", -1, &rc,
DT_SINGLELINE | DT_CENTER | DT_VCENTER | DT_NOPREFIX);
::EndPaint(hwnd, &ps);
return 0;
}
LRESULT MyWindowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
switch (msg) {
case WM_DESTROY: ::PostQuitMessage(0); break;
case WM_PAINT: return OnPaint(hwnd, msg, wp, lp);
}
return ::DefWindowProcW(hwnd, msg, wp, lp);
}
int WinMain(HINSTANCE hinst, HINSTANCE, LPSTR, int nShowCmd) {
WNDCLASSW wc = {0};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = MyWindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hinst;
wc.hIcon = NULL;
wc.hCursor = ::LoadCursorW(NULL, IDC_ARROW);
wc.hbrBackground = ::GetSysColorBrush(COLOR_WINDOW);
wc.lpszMenuName = nullptr;
wc.lpszClassName = L"My Very Own Window Class";
const ATOM atomClass = ::RegisterClassW(&wc);
if (atomClass == 0) return ::GetLastError();
// Ugly cast for WinAPI's legacy type punning.
const LPCWSTR wndclass =
reinterpret_cast<LPCWSTR>(static_cast<UINT_PTR>(atomClass));
const HWND hwnd =
::CreateWindowW(wndclass, L"Test", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL,
NULL, hinst, nullptr);
if (hwnd == NULL) return ::GetLastError();
::ShowWindow(hwnd, nShowCmd);
MSG msg = {0};
while (::GetMessageW(&msg, NULL, 0, 0) > 0) {
::TranslateMessage(&msg);
::DispatchMessageW(&msg);
}
return (msg.message == WM_QUIT) ? static_cast<int>(msg.wParam) : 1;
}
Upvotes: 3
Views: 611
Reputation: 47956
What should I do in my programs to let the system know that initialization is complete and the spinner is no longer needed?
There seems to be agreement that the spinner should vanish once the application has started pumping messages and has no pending input events queued up.
The fact that's not working in all cases for me is likely a local issue, like a buggy driver spamming the input queue (which somehow never make it to window messages). There's not much point in hashing that out here on SO, so I'm putting up this community wiki answer and moving on.
Upvotes: 0