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