6d889f25aa041c7a4e6ccd3fa626ce29c179f50f
[freeglut] / src / x11 / fg_cursor_x11.c
1 /*
2  * fg_cursor_x11.c
3  *
4  * The Windows-specific mouse cursor related stuff.
5  *
6  * Copyright (c) 2012 Stephen J. Baker. All Rights Reserved.
7  * Written by John F. Fay, <fayjf@sourceforge.net>
8  * Creation date: Sun Feb 5, 2012
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 "../fg_internal.h"
30
31 /* This code is for Posix/X11, Solaris, and OSX */
32 #include <X11/cursorfont.h>
33
34 /*
35  * A factory method for an empty cursor
36  */
37 static Cursor getEmptyCursor( void )
38 {
39     static Cursor cursorNone = None;
40     if( cursorNone == None ) {
41         char cursorNoneBits[ 32 ];
42         XColor dontCare;
43         Pixmap cursorNonePixmap;
44         memset( cursorNoneBits, 0, sizeof( cursorNoneBits ) );
45         memset( &dontCare, 0, sizeof( dontCare ) );
46         cursorNonePixmap = XCreateBitmapFromData ( fgDisplay.pDisplay.Display,
47                                                    fgDisplay.pDisplay.RootWindow,
48                                                    cursorNoneBits, 16, 16 );
49         if( cursorNonePixmap != None ) {
50             cursorNone = XCreatePixmapCursor( fgDisplay.pDisplay.Display,
51                                               cursorNonePixmap, cursorNonePixmap,
52                                               &dontCare, &dontCare, 0, 0 );
53             XFreePixmap( fgDisplay.pDisplay.Display, cursorNonePixmap );
54         }
55     }
56     return cursorNone;
57 }
58
59 typedef struct tag_cursorCacheEntry cursorCacheEntry;
60 struct tag_cursorCacheEntry {
61     unsigned int cursorShape;    /* an XC_foo value */
62     Cursor cachedCursor;         /* None if the corresponding cursor has
63                                     not been created yet */
64 };
65
66 /*
67  * Note: The arrangement of the table below depends on the fact that
68  * the "normal" GLUT_CURSOR_* values start a 0 and are consecutive.
69  */ 
70 static cursorCacheEntry cursorCache[] = {
71     { XC_arrow,               None }, /* GLUT_CURSOR_RIGHT_ARROW */
72     { XC_top_left_arrow,      None }, /* GLUT_CURSOR_LEFT_ARROW */
73     { XC_hand1,               None }, /* GLUT_CURSOR_INFO */
74     { XC_pirate,              None }, /* GLUT_CURSOR_DESTROY */
75     { XC_question_arrow,      None }, /* GLUT_CURSOR_HELP */
76     { XC_exchange,            None }, /* GLUT_CURSOR_CYCLE */
77     { XC_spraycan,            None }, /* GLUT_CURSOR_SPRAY */
78     { XC_watch,               None }, /* GLUT_CURSOR_WAIT */
79     { XC_xterm,               None }, /* GLUT_CURSOR_TEXT */
80     { XC_crosshair,           None }, /* GLUT_CURSOR_CROSSHAIR */
81     { XC_sb_v_double_arrow,   None }, /* GLUT_CURSOR_UP_DOWN */
82     { XC_sb_h_double_arrow,   None }, /* GLUT_CURSOR_LEFT_RIGHT */
83     { XC_top_side,            None }, /* GLUT_CURSOR_TOP_SIDE */
84     { XC_bottom_side,         None }, /* GLUT_CURSOR_BOTTOM_SIDE */
85     { XC_left_side,           None }, /* GLUT_CURSOR_LEFT_SIDE */
86     { XC_right_side,          None }, /* GLUT_CURSOR_RIGHT_SIDE */
87     { XC_top_left_corner,     None }, /* GLUT_CURSOR_TOP_LEFT_CORNER */
88     { XC_top_right_corner,    None }, /* GLUT_CURSOR_TOP_RIGHT_CORNER */
89     { XC_bottom_right_corner, None }, /* GLUT_CURSOR_BOTTOM_RIGHT_CORNER */
90     { XC_bottom_left_corner,  None }  /* GLUT_CURSOR_BOTTOM_LEFT_CORNER */
91 };
92
93 void fgPlatformSetCursor ( SFG_Window *window, int cursorID )
94 {
95     Cursor cursor;
96     /*
97      * XXX FULL_CROSSHAIR demotes to plain CROSSHAIR. Old GLUT allows
98      * for this, but if there is a system that easily supports a full-
99      * window (or full-screen) crosshair, we might consider it.
100      */
101     int cursorIDToUse =
102         ( cursorID == GLUT_CURSOR_FULL_CROSSHAIR ) ? GLUT_CURSOR_CROSSHAIR : cursorID;
103
104     if( ( cursorIDToUse >= 0 ) &&
105         ( cursorIDToUse < sizeof( cursorCache ) / sizeof( cursorCache[0] ) ) ) {
106         cursorCacheEntry *entry = &cursorCache[ cursorIDToUse ];
107         if( entry->cachedCursor == None ) {
108             entry->cachedCursor =
109                 XCreateFontCursor( fgDisplay.pDisplay.Display, entry->cursorShape );
110         }
111         cursor = entry->cachedCursor;
112     } else {
113         switch( cursorIDToUse )
114         {
115         case GLUT_CURSOR_NONE:
116             cursor = getEmptyCursor( );
117             break;
118
119         case GLUT_CURSOR_INHERIT:
120             cursor = None;
121             break;
122
123         default:
124             fgError( "Unknown cursor type: %d", cursorIDToUse );
125             return;
126         }
127     }
128
129     if ( cursorIDToUse == GLUT_CURSOR_INHERIT ) {
130         XUndefineCursor( fgDisplay.pDisplay.Display, window->Window.Handle );
131     } else if ( cursor != None ) {
132         XDefineCursor( fgDisplay.pDisplay.Display, window->Window.Handle, cursor );
133     } else if ( cursorIDToUse != GLUT_CURSOR_NONE ) {
134         fgError( "Failed to create cursor" );
135     }
136 }
137
138
139 void fgPlatformWarpPointer ( int x, int y )
140 {
141     XWarpPointer(
142         fgDisplay.pDisplay.Display,
143         None,
144         fgStructure.CurrentWindow->Window.Handle,
145         0, 0, 0, 0,
146         x, y
147     );
148     /* Make the warp visible immediately. */
149     XFlush( fgDisplay.pDisplay.Display );
150 }
151
152 void fghPlatformGetCursorPos(const SFG_Window *window, GLboolean client, SFG_XYUse *mouse_pos)
153 {
154     /* Get current pointer location in screen coordinates (if client is false or window is NULL), else
155      * Get current pointer location relative to top-left of client area of window (if client is true and window is not NULL)
156      */
157     Window w = (client && window && window->Window.Handle)? window->Window.Handle: fgDisplay.pDisplay.RootWindow;
158     Window junk_window;
159     unsigned int junk_mask;
160     int clientX, clientY;
161
162     XQueryPointer(fgDisplay.pDisplay.Display, w,
163             &junk_window, &junk_window,
164             &mouse_pos->X, &mouse_pos->Y, /* Screen coords relative to root window's top-left */
165             &clientX, &clientY,           /* Client coords relative to window's top-left */
166             &junk_mask);
167
168     if (client && window && window->Window.Handle)
169     {
170         mouse_pos->X = clientX;
171         mouse_pos->Y = clientY;
172     }
173
174     mouse_pos->Use = GL_TRUE;
175 }