Since I started working on our 3D engine, I've found that there are certain subjects which a lot of people post questions about, which don't get good answers. There are tantalyzing half-answers, followed by people posting "Thanks, I got it working now", but I rarely find a definitive post that says "This is how you do it," step by step, with possible pitfalls. Yet it often turns out to be a simple answer, like in my article about how to compensate, using in-engine code, for the difference between 3D Studio Max's camera and a left-handed system camera.
Direct3D cursors are an example of this.
I started out using DirectInput for the mouse in my engine, because I was ripping a lot of stuff out of the example code in Peter Walsh's The Zen of Direct3D Game Progamming. I had lots of problems with it, and we eventually ended up taking it out, and that also took out the direct3d cursor support because I had put everything in there.
So yesterday, at the end of the day, I decided to look at direct3d cursors again. It was simple enough.
First, I modified the code in the winproc so that the system cursor would get turned off if direct3d cursors were enabled.
case WM_SETCURSOR:
if (bDirect3DCursor)
{
MouseSet(NULL);
if (g_pDevice)
g_pDevice->ShowCursor( TRUE );
}
else
{
MouseSet (hcurArrow);
MouseSet (ulCurrentCursor);
}
return (TRUE);
case WM_MOUSEMOVE:
MWinSetDragSpritePos (LOWORD (lParam), HIWORD (lParam));
usMouseFlag = 0;
if (wParam & MK_CONTROL) usMouseFlag |= MFLG_CONTROL;
if (wParam & MK_SHIFT) usMouseFlag |= MFLG_SHIFT;
sLastMouseX = (SHORT) LOWORD (lParam);
sLastMouseY = (SHORT) HIWORD (lParam);
DefaultCallback ((PVOID)&mwinAnchor,
MAKEULONG (0, MSG_HS_MOUSEMOVE),
(ULONG) &mwinAnchor.hsHot,
MAKEULONG (LOWORD (lParam), HIWORD (lParam)));
if (bDirect3DCursor)
{
MouseSet(NULL);
if (g_pDevice)
{
g_pDevice->ShowCursor( TRUE );
UpdateCursorPos();
}
}
else
{
MouseSet (ulCurrentCursor);
MouseShow ();
}
return (TRUE);
Then I wrote a function to create the device cursor, and one to update its position
VOID LoadDirect3DCursor()
{
HRESULT hr;
hr = g_pDevice->CreateOffscreenPlainSurface(32,
32,
D3DFMT_A8R8G8B8,
D3DPOOL_DEFAULT,
&g_pCursorSurface,
NULL);
if (FAILED(hr))
{
OutputDebugMessage(DXGetErrorDescription9(hr));
bDirect3DCursor = FALSE;
return;
}
hr = D3DXLoadSurfaceFromFile(g_pCursorSurface,
NULL,
NULL,
"Gfx\\Arrow.png",
NULL,
D3DX_FILTER_NONE,
0,
NULL);
if (FAILED(hr))
{
OutputDebugMessage(DXGetErrorDescription9(hr));
bDirect3DCursor = FALSE;
return;
}
g_pDevice->SetCursorProperties(0,0, g_pCursorSurface);
}
VOID UpdateCursorPos()
{
SHORT sMouseX;
SHORT sMouseY;
QueryMousePos(&sMouseX, &sMouseY);
g_pDevice->SetCursorPosition(sMouseX, sMouseY, 0);
}
Now, that worked great for fullscreen. But in windowed mode, the cursor stayed stuck at the top of the screen. The Remarks in the help file about SetCursorPosition mentioned that in windowed mode, the coordinates that it accepted were desktop coordinates. Posts in the google groups devoted to direct3d suggested that you couldn't use direct3d cursors in windowed mode, and to use GDI cursors. But I was convinced that you could use direct3d cursors. After all, why would Microsoft put that in their helpfiles about it taking desktop coordinates in windowed mode? Microsoft uses direct3d cursors in some of its samples, not that you can tell because they're using the plain old black and white arrow. They had a function ScreenToClient that they used to translate the mouse coordinates, but that was if it wasn't windowed mode. So I checked the help files, and sure enough, there was a function ClientToScreen.
VOID UpdateCursorPos()
{
SHORT sMouseX;
SHORT sMouseY;
POINT MousePoint;
QueryMousePos(&sMouseX, &sMouseY);
if (!bFullScreen)
{
MousePoint.x = sMouseX;
MousePoint.y = sMouseY;
ClientToScreen(QueryWindowHandle(), &MousePoint);
sMouseX = MousePoint.x;
sMouseY = MousePoint.y;
sprintf(szDbgBuffer, "Mouse coordinates: %d,%d", sMouseX, sMouseY);
OutputDebugMessage(szDbgBuffer);
}
g_pDevice->SetCursorPosition(sMouseX, sMouseY, 0);
}
So now it works beautifully. I tried taking a screenshot, but the mouse cursor didn't show up. If someone tells me how, I'll take one and edit it into this blog.

The mouse cursor image is taken from Z71's Cursor XP theme, Realized.