How Windows 3.11 Managed Mouse Cursor Shapes

In Microsoft Windows 3.11, mouse cursor shapes were dynamically managed through the 16-bit window messaging system and cooperative multitasking architecture. Rather than relying on a centralized hardware cursor engine, the operating system utilized window class registrations, spatial hit-testing, and the WM_SETCURSOR message to let the active window determine which cursor to display. This approach enabled smooth visual feedback—such as resizing arrows, text selection I-beams, and busy hourglasses—as the user moved the pointer across various applications and interface elements.

The Win16 Messaging Loop and Hit-Testing

Every time the user moved the physical mouse, the mouse driver sent hardware interrupts to the Windows operating system, which translated them into raw mouse coordinates. The system then performed a hit-test to determine which window and UI component lay beneath the pointer.

Once the target window was identified, Windows dispatched a WM_SETCURSOR message to that window's event queue. This message carried crucial context in its parameters:

  • wParam: The window handle (HWND) of the window currently containing the cursor.
  • lParam (low-order word): The hit-test result code (such as HTCLIENT for the interior workspace, HTCAPTION for the title bar, or HTGROWBOX / HTBOTTOMRIGHT for window sizing borders).
  • lParam (high-order word): The specific mouse message that triggered the event, typically WM_MOUSEMOVE.

Window Class Defaults via WNDCLASS

Applications defined their default cursor behavior upon launch during the registration of a window class using the RegisterClass() API function. When filling out the WNDCLASS structure, developers populated the hCursor member with a handle to a preloaded cursor graphic, typically retrieved using LoadCursor().

Common system stock cursors included:

  • IDC_ARROW: Standard pointer.
  • IDC_IBEAM: Text insertion cursor.
  • IDC_WAIT: The system hourglass.
  • IDC_CROSS: Precision crosshair.

If an application did not require complex, context-dependent cursor changes within its client area, its window procedure simply forwarded the WM_SETCURSOR message to the default window procedure, DefWindowProc(). Upon receiving the message, DefWindowProc() checked the hit-test code. If the pointer was in the client area (HTCLIENT), it automatically set the cursor to the handle specified in the window class's hCursor field.

Dynamic, In-Client Context Switching

When an application required different cursor shapes over specific interactive controls—such as a split bar in File Manager or text margins in Windows Write—it intercepted the WM_SETCURSOR message within its window procedure (WindowProc):

  1. Evaluation: The application inspected internal coordinates or control states to determine what element the pointer hovered over.
  2. Assignment: It invoked the SetCursor(hCursor) Win16 API function, passing the handle of the appropriate cursor resource.
  3. Suppression: The application returned TRUE from the window procedure, halting further processing and preventing DefWindowProc() from overwriting the selection with the generic class cursor.

If the pointer hovered over a non-client element (such as a border, minimize/maximize box, or menu), the application passed WM_SETCURSOR back to DefWindowProc(). The system then drew standardized interface cursors, such as the two-headed resizing arrows (IDC_SIZENWSE, IDC_SIZEWE, etc.).

Global System Overrides and Cooperative Limits

Because Windows 3.11 operated on a cooperative multitasking model with a single system-wide message queue, cursor management was closely tied to thread execution:

  • Global Busy States: When an application initiated a blocking operation (such as file I/O or rendering), it manually invoked SetCursor(LoadCursor(NULL, IDC_WAIT)) to display the hourglass. Because the system was not preemptively multitasked, the hourglass often remained visible across the entire screen until the busy application yielded control back to the operating system.
  • Driver Rendering: The 16-bit display driver (.DRV) handled the final drawing of the cursor. On supported hardware, graphics accelerators used hardware-based cursor planes to eliminate flicker. On standard VGA cards, the driver manually saved and restored the bitmap pixels behind the cursor in software every time the shape or position changed.