e4673534c5396380c50e1504e45b7fd0142338d9
[freeglut] / src / Common / freeglut_cursor.c
1 /*\r
2  * freeglut_cursor.c\r
3  *\r
4  * The mouse cursor related stuff.\r
5  *\r
6  * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.\r
7  * Written by Pawel W. Olszta, <olszta@sourceforge.net>\r
8  * Creation date: Thu Dec 16 1999\r
9  *\r
10  * Permission is hereby granted, free of charge, to any person obtaining a\r
11  * copy of this software and associated documentation files (the "Software"),\r
12  * to deal in the Software without restriction, including without limitation\r
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
14  * and/or sell copies of the Software, and to permit persons to whom the\r
15  * Software is furnished to do so, subject to the following conditions:\r
16  *\r
17  * The above copyright notice and this permission notice shall be included\r
18  * in all copies or substantial portions of the Software.\r
19  *\r
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS\r
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL\r
23  * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
24  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
25  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
26  */\r
27 \r
28 #include <GL/freeglut.h>\r
29 #include "freeglut_internal.h"\r
30 \r
31 /*\r
32  * TODO BEFORE THE STABLE RELEASE:\r
33  *  glutSetCursor()     -- Win32 mappings are incomplete.\r
34  *\r
35  * It would be good to use custom mouse cursor shapes, and introduce\r
36  * an option to display them using glBitmap() and/or texture mapping,\r
37  * apart from the windowing system version.\r
38  */\r
39 \r
40 /* -- PRIVATE FUNCTIONS --------------------------------------------------- */\r
41 \r
42 #if TARGET_HOST_POSIX_X11 || TARGET_HOST_MAC_OSX || TARGET_HOST_SOLARIS\r
43   #include <X11/cursorfont.h>\r
44 \r
45 /*\r
46  * A factory method for an empty cursor\r
47  */\r
48 static Cursor getEmptyCursor( void )\r
49 {\r
50     static Cursor cursorNone = None;\r
51     if( cursorNone == None ) {\r
52         char cursorNoneBits[ 32 ];\r
53         XColor dontCare;\r
54         Pixmap cursorNonePixmap;\r
55         memset( cursorNoneBits, 0, sizeof( cursorNoneBits ) );\r
56         memset( &dontCare, 0, sizeof( dontCare ) );\r
57         cursorNonePixmap = XCreateBitmapFromData ( fgDisplay.Display,\r
58                                                    fgDisplay.RootWindow,\r
59                                                    cursorNoneBits, 16, 16 );\r
60         if( cursorNonePixmap != None ) {\r
61             cursorNone = XCreatePixmapCursor( fgDisplay.Display,\r
62                                               cursorNonePixmap, cursorNonePixmap,\r
63                                               &dontCare, &dontCare, 0, 0 );\r
64             XFreePixmap( fgDisplay.Display, cursorNonePixmap );\r
65         }\r
66     }\r
67     return cursorNone;\r
68 }\r
69 \r
70 typedef struct tag_cursorCacheEntry cursorCacheEntry;\r
71 struct tag_cursorCacheEntry {\r
72     unsigned int cursorShape;    /* an XC_foo value */\r
73     Cursor cachedCursor;         /* None if the corresponding cursor has\r
74                                     not been created yet */\r
75 };\r
76 \r
77 /*\r
78  * Note: The arrangement of the table below depends on the fact that\r
79  * the "normal" GLUT_CURSOR_* values start a 0 and are consecutive.\r
80  */ \r
81 static cursorCacheEntry cursorCache[] = {\r
82     { XC_arrow,               None }, /* GLUT_CURSOR_RIGHT_ARROW */\r
83     { XC_top_left_arrow,      None }, /* GLUT_CURSOR_LEFT_ARROW */\r
84     { XC_hand1,               None }, /* GLUT_CURSOR_INFO */\r
85     { XC_pirate,              None }, /* GLUT_CURSOR_DESTROY */\r
86     { XC_question_arrow,      None }, /* GLUT_CURSOR_HELP */\r
87     { XC_exchange,            None }, /* GLUT_CURSOR_CYCLE */\r
88     { XC_spraycan,            None }, /* GLUT_CURSOR_SPRAY */\r
89     { XC_watch,               None }, /* GLUT_CURSOR_WAIT */\r
90     { XC_xterm,               None }, /* GLUT_CURSOR_TEXT */\r
91     { XC_crosshair,           None }, /* GLUT_CURSOR_CROSSHAIR */\r
92     { XC_sb_v_double_arrow,   None }, /* GLUT_CURSOR_UP_DOWN */\r
93     { XC_sb_h_double_arrow,   None }, /* GLUT_CURSOR_LEFT_RIGHT */\r
94     { XC_top_side,            None }, /* GLUT_CURSOR_TOP_SIDE */\r
95     { XC_bottom_side,         None }, /* GLUT_CURSOR_BOTTOM_SIDE */\r
96     { XC_left_side,           None }, /* GLUT_CURSOR_LEFT_SIDE */\r
97     { XC_right_side,          None }, /* GLUT_CURSOR_RIGHT_SIDE */\r
98     { XC_top_left_corner,     None }, /* GLUT_CURSOR_TOP_LEFT_CORNER */\r
99     { XC_top_right_corner,    None }, /* GLUT_CURSOR_TOP_RIGHT_CORNER */\r
100     { XC_bottom_right_corner, None }, /* GLUT_CURSOR_BOTTOM_RIGHT_CORNER */\r
101     { XC_bottom_left_corner,  None }  /* GLUT_CURSOR_BOTTOM_LEFT_CORNER */\r
102 };\r
103 \r
104 static void fghSetCursor ( SFG_Window *window, int cursorID )\r
105 {\r
106     Cursor cursor;\r
107     /*\r
108      * XXX FULL_CROSSHAIR demotes to plain CROSSHAIR. Old GLUT allows\r
109      * for this, but if there is a system that easily supports a full-\r
110      * window (or full-screen) crosshair, we might consider it.\r
111      */\r
112     int cursorIDToUse =\r
113         ( cursorID == GLUT_CURSOR_FULL_CROSSHAIR ) ? GLUT_CURSOR_CROSSHAIR : cursorID;\r
114 \r
115     if( ( cursorIDToUse >= 0 ) &&\r
116         ( cursorIDToUse < sizeof( cursorCache ) / sizeof( cursorCache[0] ) ) ) {\r
117         cursorCacheEntry *entry = &cursorCache[ cursorIDToUse ];\r
118         if( entry->cachedCursor == None ) {\r
119             entry->cachedCursor =\r
120                 XCreateFontCursor( fgDisplay.Display, entry->cursorShape );\r
121         }\r
122         cursor = entry->cachedCursor;\r
123     } else {\r
124         switch( cursorIDToUse )\r
125         {\r
126         case GLUT_CURSOR_NONE:\r
127             cursor = getEmptyCursor( );\r
128             break;\r
129 \r
130         case GLUT_CURSOR_INHERIT:\r
131             cursor = None;\r
132             break;\r
133 \r
134         default:\r
135             fgError( "Unknown cursor type: %d", cursorIDToUse );\r
136             return;\r
137         }\r
138     }\r
139 \r
140     if ( cursorIDToUse == GLUT_CURSOR_INHERIT ) {\r
141         XUndefineCursor( fgDisplay.Display, window->Window.Handle );\r
142     } else if ( cursor != None ) {\r
143         XDefineCursor( fgDisplay.Display, window->Window.Handle, cursor );\r
144     } else if ( cursorIDToUse != GLUT_CURSOR_NONE ) {\r
145         fgError( "Failed to create cursor" );\r
146     }\r
147 }\r
148 \r
149 \r
150 static void fghWarpPointer ( int x, int y )\r
151 {\r
152     XWarpPointer(\r
153         fgDisplay.Display,\r
154         None,\r
155         fgStructure.CurrentWindow->Window.Handle,\r
156         0, 0, 0, 0,\r
157         x, y\r
158     );\r
159     /* Make the warp visible immediately. */\r
160     XFlush( fgDisplay.Display );\r
161 }\r
162 #endif\r
163 \r
164 \r
165 #if TARGET_HOST_MS_WINDOWS\r
166 static void fghSetCursor ( SFG_Window *window, int cursorID )\r
167 {\r
168     /*\r
169      * Joe Krahn is re-writing the following code.\r
170      */\r
171     /* Set the cursor AND change it for this window class. */\r
172 #if !defined(__MINGW64__) && _MSC_VER <= 1200\r
173 #       define MAP_CURSOR(a,b)                                   \\r
174         case a:                                                  \\r
175             SetCursor( LoadCursor( NULL, b ) );                  \\r
176             SetClassLong( window->Window.Handle,                 \\r
177                           GCL_HCURSOR,                           \\r
178                           ( LONG )LoadCursor( NULL, b ) );       \\r
179         break;\r
180     /* Nuke the cursor AND change it for this window class. */\r
181 #       define ZAP_CURSOR(a,b)                                   \\r
182         case a:                                                  \\r
183             SetCursor( NULL );                                   \\r
184             SetClassLong( window->Window.Handle,                 \\r
185                           GCL_HCURSOR, ( LONG )NULL );           \\r
186         break;\r
187 #else\r
188 #       define MAP_CURSOR(a,b)                                   \\r
189         case a:                                                  \\r
190             SetCursor( LoadCursor( NULL, b ) );                  \\r
191             SetClassLongPtr( window->Window.Handle,              \\r
192                           GCLP_HCURSOR,                          \\r
193                           ( LONG )( LONG_PTR )LoadCursor( NULL, b ) );       \\r
194         break;\r
195     /* Nuke the cursor AND change it for this window class. */\r
196 #       define ZAP_CURSOR(a,b)                                   \\r
197         case a:                                                  \\r
198             SetCursor( NULL );                                   \\r
199             SetClassLongPtr( window->Window.Handle,              \\r
200                           GCLP_HCURSOR, ( LONG )( LONG_PTR )NULL );          \\r
201         break;\r
202 #endif\r
203 \r
204     switch( cursorID )\r
205     {\r
206         MAP_CURSOR( GLUT_CURSOR_RIGHT_ARROW,         IDC_ARROW     );\r
207         MAP_CURSOR( GLUT_CURSOR_LEFT_ARROW,          IDC_ARROW     );\r
208         MAP_CURSOR( GLUT_CURSOR_INFO,                IDC_HELP      );\r
209         MAP_CURSOR( GLUT_CURSOR_DESTROY,             IDC_CROSS     );\r
210         MAP_CURSOR( GLUT_CURSOR_HELP,                IDC_HELP      );\r
211         MAP_CURSOR( GLUT_CURSOR_CYCLE,               IDC_SIZEALL   );\r
212         MAP_CURSOR( GLUT_CURSOR_SPRAY,               IDC_CROSS     );\r
213         MAP_CURSOR( GLUT_CURSOR_WAIT,                IDC_WAIT      );\r
214         MAP_CURSOR( GLUT_CURSOR_TEXT,                IDC_IBEAM     );\r
215         MAP_CURSOR( GLUT_CURSOR_CROSSHAIR,           IDC_CROSS     );\r
216         MAP_CURSOR( GLUT_CURSOR_UP_DOWN,             IDC_SIZENS    );\r
217         MAP_CURSOR( GLUT_CURSOR_LEFT_RIGHT,          IDC_SIZEWE    );\r
218         MAP_CURSOR( GLUT_CURSOR_TOP_SIDE,            IDC_ARROW     ); /* XXX ToDo */\r
219         MAP_CURSOR( GLUT_CURSOR_BOTTOM_SIDE,         IDC_ARROW     ); /* XXX ToDo */\r
220         MAP_CURSOR( GLUT_CURSOR_LEFT_SIDE,           IDC_ARROW     ); /* XXX ToDo */\r
221         MAP_CURSOR( GLUT_CURSOR_RIGHT_SIDE,          IDC_ARROW     ); /* XXX ToDo */\r
222         MAP_CURSOR( GLUT_CURSOR_TOP_LEFT_CORNER,     IDC_SIZENWSE  );\r
223         MAP_CURSOR( GLUT_CURSOR_TOP_RIGHT_CORNER,    IDC_SIZENESW  );\r
224         MAP_CURSOR( GLUT_CURSOR_BOTTOM_RIGHT_CORNER, IDC_SIZENWSE  );\r
225         MAP_CURSOR( GLUT_CURSOR_BOTTOM_LEFT_CORNER,  IDC_SIZENESW  );\r
226         MAP_CURSOR( GLUT_CURSOR_INHERIT,             IDC_ARROW     ); /* XXX ToDo */\r
227         ZAP_CURSOR( GLUT_CURSOR_NONE,                NULL          );\r
228         MAP_CURSOR( GLUT_CURSOR_FULL_CROSSHAIR,      IDC_CROSS     ); /* XXX ToDo */\r
229 \r
230     default:\r
231         fgError( "Unknown cursor type: %d", cursorID );\r
232         break;\r
233     }\r
234 }\r
235 \r
236 \r
237 static void fghWarpPointer ( int x, int y )\r
238 {\r
239     POINT coords;\r
240     coords.x = x;\r
241     coords.y = y;\r
242 \r
243     /* ClientToScreen() translates {coords} for us. */\r
244     ClientToScreen( fgStructure.CurrentWindow->Window.Handle, &coords );\r
245     SetCursorPos( coords.x, coords.y );\r
246 }\r
247 #endif\r
248 \r
249 \r
250 /* -- INTERNAL FUNCTIONS ---------------------------------------------------- */\r
251 void fgSetCursor ( SFG_Window *window, int cursorID )\r
252 {\r
253     fghSetCursor ( window, cursorID );\r
254 }\r
255 \r
256 \r
257 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */\r
258 \r
259 /*\r
260  * Set the cursor image to be used for the current window\r
261  */\r
262 void FGAPIENTRY glutSetCursor( int cursorID )\r
263 {\r
264     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSetCursor" );\r
265     FREEGLUT_EXIT_IF_NO_WINDOW ( "glutSetCursor" );\r
266 \r
267     fghSetCursor ( fgStructure.CurrentWindow, cursorID );\r
268     fgStructure.CurrentWindow->State.Cursor = cursorID;\r
269 }\r
270 \r
271 /*\r
272  * Moves the mouse pointer to given window coordinates\r
273  */\r
274 void FGAPIENTRY glutWarpPointer( int x, int y )\r
275 {\r
276     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWarpPointer" );\r
277     FREEGLUT_EXIT_IF_NO_WINDOW ( "glutWarpPointer" );\r
278 \r
279     fghWarpPointer ( x, y );\r
280 }\r
281 \r
282 /*** END OF FILE ***/\r