1 /* Copyright (C) 2018 Wildfire Games.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include "precompiled.h"
24 #include "lib/sysdep/cursor.h"
25 
26 #include "lib/sysdep/gfx.h"
27 #include "lib/sysdep/os/win/win.h"
28 #include "lib/sysdep/os/win/wutil.h"
29 
cursor_from_HICON(HICON hIcon)30 static sys_cursor cursor_from_HICON(HICON hIcon)
31 {
32 	return (sys_cursor)(uintptr_t)hIcon;
33 }
34 
cursor_from_HCURSOR(HCURSOR hCursor)35 static sys_cursor cursor_from_HCURSOR(HCURSOR hCursor)
36 {
37 	return (sys_cursor)(uintptr_t)hCursor;
38 }
39 
HICON_from_cursor(sys_cursor cursor)40 static HICON HICON_from_cursor(sys_cursor cursor)
41 {
42 	return (HICON)(uintptr_t)cursor;
43 }
44 
HCURSOR_from_cursor(sys_cursor cursor)45 static HCURSOR HCURSOR_from_cursor(sys_cursor cursor)
46 {
47 	return (HCURSOR)(uintptr_t)cursor;
48 }
49 
50 
sys_cursor_create_common(int w,int h,void * bgra_img,void * mask_img,int hx,int hy,sys_cursor * cursor)51 static Status sys_cursor_create_common(int w, int h, void* bgra_img, void* mask_img, int hx, int hy, sys_cursor* cursor)
52 {
53 	*cursor = 0;
54 
55 	// MSDN says selecting this HBITMAP into a DC is slower since we use
56 	// CreateBitmap; bpp/format must be checked against those of the DC.
57 	// this is the simplest way and we don't care about slight performance
58 	// differences because this is typically only called once.
59 	HBITMAP hbmColor = CreateBitmap(w, h, 1, 32, bgra_img);
60 
61 	// CreateIconIndirect doesn't access this; we just need to pass
62 	// an empty bitmap.
63 	HBITMAP hbmMask = CreateBitmap(w, h, 1, 1, mask_img);
64 
65 	// create the cursor (really an icon; they differ only in
66 	// fIcon and the hotspot definitions).
67 	ICONINFO ii;
68 	ii.fIcon = FALSE;  // cursor
69 	ii.xHotspot = (DWORD)hx;
70 	ii.yHotspot = (DWORD)hy;
71 	ii.hbmMask  = hbmMask;
72 	ii.hbmColor = hbmColor;
73 	HICON hIcon = CreateIconIndirect(&ii);
74 
75 	// CreateIconIndirect makes copies, so we no longer need these.
76 	DeleteObject(hbmMask);
77 	DeleteObject(hbmColor);
78 
79 	if(!wutil_IsValidHandle(hIcon))
80 		WARN_RETURN(ERR::FAIL);
81 
82 	*cursor = cursor_from_HICON(hIcon);
83 	return INFO::OK;
84 }
85 
sys_cursor_create(int w,int h,void * bgra_img,int hx,int hy,sys_cursor * cursor)86 Status sys_cursor_create(int w, int h, void* bgra_img, int hx, int hy, sys_cursor* cursor)
87 {
88 	// alpha-blended cursors do not work on a 16-bit display
89 	// (they get drawn as a black square), so refuse to load the
90 	// cursor in that case
91 	int bpp = 0;
92 	RETURN_STATUS_IF_ERR(gfx::GetVideoMode(NULL, NULL, &bpp, NULL));
93 	if (bpp <= 16)
94 		return ERR::FAIL;
95 
96 	return sys_cursor_create_common(w, h, bgra_img, NULL, hx, hy, cursor);
97 }
98 
sys_cursor_create_empty(sys_cursor * cursor)99 Status sys_cursor_create_empty(sys_cursor* cursor)
100 {
101 	// the mask gets ignored on 32-bit displays, but is used on 16-bit displays;
102 	// setting it to 0xFF makes the cursor invisible (though I'm not quite
103 	// sure why it's that way round)
104 	u8 bgra_img[] = {0, 0, 0, 0};
105 	u8 mask_img[] = {0xFF};
106 	return sys_cursor_create_common(1, 1, bgra_img, mask_img, 0, 0, cursor);
107 }
108 
109 
sys_cursor_set(sys_cursor cursor)110 Status sys_cursor_set(sys_cursor cursor)
111 {
112 	// restore default cursor.
113 	if(!cursor)
114 		cursor = cursor_from_HCURSOR(LoadCursor(0, IDC_ARROW));
115 
116 	(void)SetCursor(HCURSOR_from_cursor(cursor));
117 	// return value (previous cursor) is useless.
118 
119 	return INFO::OK;
120 }
121 
122 
sys_cursor_free(sys_cursor cursor)123 Status sys_cursor_free(sys_cursor cursor)
124 {
125 	// bail now to prevent potential confusion below; there's nothing to do.
126 	if(!cursor)
127 		return INFO::OK;
128 
129 	// if the cursor being freed is active, restore the default arrow
130 	// (just for safety).
131 	if(cursor_from_HCURSOR(GetCursor()) == cursor)
132 		WARN_IF_ERR(sys_cursor_set(0));
133 
134 	if(!DestroyIcon(HICON_from_cursor(cursor)))
135 		WARN_RETURN(StatusFromWin());
136 	return INFO::OK;
137 }
138 
sys_cursor_reset()139 Status sys_cursor_reset()
140 {
141 	return INFO::OK;
142 }
143