Handmade Hero»Forums»Code
Shazan Shums
159 posts
Some day I will make quality software. Programming FTW.
How to implement mouse cursor
How to implement mouse cursor in Win32 ?
Because I see in games like skyrim there issues in the cursor when switching fullscreen and should i change cursor using Win32 or to render cursor in the game itself?
I am sorry if this is a simple problem for you to fix.
Mārtiņš Možeiko
2559 posts / 2 projects
How to implement mouse cursor
You should use win32 provided cursor instead of hiding it and drawing sprite on your own.
That's because OS provided cursor has less lag. Your own rendered will always lag a frame or two (maybe more?).
To set current cursor image use SetCursor function. It needs HCURSOR handle. You get that by calling CreateCursor function to create it from memory, or LoadImage function to load from image file.
@Mattias_G
42 posts / 1 project
Amateur (ex-pro) game dev, making retro styled games and small public domain C/C++ libs.
How to implement mouse cursor
Yeah, the windows cursor has significantly less lag.

To provide some more detail, I've found that the best way to use SetCursor is to place this code in you WndProc:
1
2
3
4
5
6
7
case WM_SETCURSOR:
	if( LOWORD( lparam ) == HTCLIENT )
	{
		SetCursor( my_hcursor_handle );
		return 0;
	}
	break;

If you just call SetCursor once, the cursor will be changed when you hover over special areas of your window (like when resizing), so you need to call SetCursor in response to the WM_SETCURSOR message. By checking that lparam is HTCLIENT, you ensure that the cursor is only applied when you are actually within your client area - otherwise you will be overriding built in cursors like the resizing cursor.

To create a cursor, with full color and alpha channel, from an argb bitmap (as you would get from stb_image), I use this code:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
static HCURSOR create_cursor_from_image( HWND hwnd, int width, int height, uint32_t* pixels_abgr, int hotspot_x, int hotspot_y )
{
	BITMAPV5HEADER header;
	memset( &header, 0, sizeof( BITMAPV5HEADER ) );
	header.bV5Size = sizeof( BITMAPV5HEADER );
	header.bV5Width = (LONG) width;
	header.bV5Height = -(LONG) height;
	header.bV5Planes = 1;
	header.bV5BitCount = 32;
	header.bV5Compression = BI_BITFIELDS;
	header.bV5RedMask   =  0x00FF0000;
	header.bV5GreenMask =  0x0000FF00;
	header.bV5BlueMask  =  0x000000FF;
	header.bV5AlphaMask =  0xFF000000; 

	HDC hdc = GetDC( hwnd );
	void* bits = NULL;
	HBITMAP bitmap = CreateDIBSection( hdc, (BITMAPINFO*)&header, DIB_RGB_COLORS,  (void**) &bits, NULL, (DWORD) 0);
	ReleaseDC( NULL, hdc );

	uint32_t* ptr = (uint32_t*) bits;
	for( int y = 0; y < height; ++y )
	{
		for( int x = 0; x < width; ++x )
		{
			uint32_t c = pixels_abgr[ x + y * width ];
			uint32_t a = ( c & 0xff000000 ) >> 24;
			uint32_t b = ( c & 0x00ff0000 ) >> 16;
			uint32_t g = ( c & 0x0000ff00 ) >> 8;
			uint32_t r = ( c & 0x000000ff );
			*ptr++ = ( a << 24 ) | ( r << 16 ) | ( g << 8 ) | b;
		}
	}

	HBITMAP empty_mask = CreateBitmap( width, height, 1, 1, NULL );
	ICONINFO icon_info;
	icon_info.fIcon = FALSE; 
	icon_info.xHotspot = (DWORD) hotspot_x;
	icon_info.yHotspot = (DWORD) hotspot_y;
	icon_info.hbmMask = empty_mask;
	icon_info.hbmColor = bitmap;

	HCURSOR cursor = CreateIconIndirect( &icon_info );
	DeleteObject( bitmap );          
	DeleteObject( empty_mask ); 

	return cursor;    
}