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 | Display *dpy; /* display used to allocate this cursor */ |
65 | }; |
66 | |
67 | /* |
68 | * Note: The arrangement of the table below depends on the fact that |
69 | * the "normal" GLUT_CURSOR_* values start a 0 and are consecutive. |
70 | */ |
71 | static cursorCacheEntry cursorCache[] = { |
72 | { XC_arrow, None, 0 }, /* GLUT_CURSOR_RIGHT_ARROW */ |
73 | { XC_top_left_arrow, None, 0 }, /* GLUT_CURSOR_LEFT_ARROW */ |
74 | { XC_hand1, None, 0 }, /* GLUT_CURSOR_INFO */ |
75 | { XC_pirate, None, 0 }, /* GLUT_CURSOR_DESTROY */ |
76 | { XC_question_arrow, None, 0 }, /* GLUT_CURSOR_HELP */ |
77 | { XC_exchange, None, 0 }, /* GLUT_CURSOR_CYCLE */ |
78 | { XC_spraycan, None, 0 }, /* GLUT_CURSOR_SPRAY */ |
79 | { XC_watch, None, 0 }, /* GLUT_CURSOR_WAIT */ |
80 | { XC_xterm, None, 0 }, /* GLUT_CURSOR_TEXT */ |
81 | { XC_crosshair, None, 0 }, /* GLUT_CURSOR_CROSSHAIR */ |
82 | { XC_sb_v_double_arrow, None, 0 }, /* GLUT_CURSOR_UP_DOWN */ |
83 | { XC_sb_h_double_arrow, None, 0 }, /* GLUT_CURSOR_LEFT_RIGHT */ |
84 | { XC_top_side, None, 0 }, /* GLUT_CURSOR_TOP_SIDE */ |
85 | { XC_bottom_side, None, 0 }, /* GLUT_CURSOR_BOTTOM_SIDE */ |
86 | { XC_left_side, None, 0 }, /* GLUT_CURSOR_LEFT_SIDE */ |
87 | { XC_right_side, None, 0 }, /* GLUT_CURSOR_RIGHT_SIDE */ |
88 | { XC_top_left_corner, None, 0 }, /* GLUT_CURSOR_TOP_LEFT_CORNER */ |
89 | { XC_top_right_corner, None, 0 }, /* GLUT_CURSOR_TOP_RIGHT_CORNER */ |
90 | { XC_bottom_right_corner, None, 0 }, /* GLUT_CURSOR_BOTTOM_RIGHT_CORNER */ |
91 | { XC_bottom_left_corner, None, 0 } /* GLUT_CURSOR_BOTTOM_LEFT_CORNER */ |
92 | }; |
93 | |
94 | void fgPlatformSetCursor ( SFG_Window *window, int cursorID ) |
95 | { |
96 | Cursor cursor; |
97 | /* |
98 | * XXX FULL_CROSSHAIR demotes to plain CROSSHAIR. Old GLUT allows |
99 | * for this, but if there is a system that easily supports a full- |
100 | * window (or full-screen) crosshair, we might consider it. |
101 | */ |
102 | int cursorIDToUse = |
103 | ( cursorID == GLUT_CURSOR_FULL_CROSSHAIR ) ? GLUT_CURSOR_CROSSHAIR : cursorID; |
104 | |
105 | if( ( cursorIDToUse >= 0 ) && |
106 | ( cursorIDToUse < sizeof( cursorCache ) / sizeof( cursorCache[0] ) ) ) { |
107 | cursorCacheEntry *entry = &cursorCache[ cursorIDToUse ]; |
108 | |
109 | /* the second clause forces an invalidation of the cached cursor, if it was |
110 | * created through a different display connection. |
111 | * This can only happen, in the extremely rare case where the user program calls the |
112 | * freeglut extension glutLeaveMainLoop, and then re-initializes freeglut and |
113 | * starts over. |
114 | */ |
115 | if( entry->cachedCursor == None || entry->dpy != fgDisplay.pDisplay.Display ) { |
116 | entry->cachedCursor = |
117 | XCreateFontCursor( fgDisplay.pDisplay.Display, entry->cursorShape ); |
118 | entry->dpy = fgDisplay.pDisplay.Display; |
119 | } |
120 | cursor = entry->cachedCursor; |
121 | } else { |
122 | switch( cursorIDToUse ) |
123 | { |
124 | case GLUT_CURSOR_NONE: |
125 | cursor = getEmptyCursor( ); |
126 | break; |
127 | |
128 | case GLUT_CURSOR_INHERIT: |
129 | cursor = None; |
130 | break; |
131 | |
132 | default: |
133 | fgError( "Unknown cursor type: %d" , cursorIDToUse ); |
134 | return; |
135 | } |
136 | } |
137 | |
138 | if ( cursorIDToUse == GLUT_CURSOR_INHERIT ) { |
139 | XUndefineCursor( fgDisplay.pDisplay.Display, window->Window.Handle ); |
140 | } else if ( cursor != None ) { |
141 | XDefineCursor( fgDisplay.pDisplay.Display, window->Window.Handle, cursor ); |
142 | } else if ( cursorIDToUse != GLUT_CURSOR_NONE ) { |
143 | fgError( "Failed to create cursor" ); |
144 | } |
145 | } |
146 | |
147 | |
148 | void fgPlatformWarpPointer ( int x, int y ) |
149 | { |
150 | XWarpPointer( |
151 | fgDisplay.pDisplay.Display, |
152 | None, |
153 | fgStructure.CurrentWindow->Window.Handle, |
154 | 0, 0, 0, 0, |
155 | x, y |
156 | ); |
157 | /* Make the warp visible immediately. */ |
158 | XFlush( fgDisplay.pDisplay.Display ); |
159 | } |
160 | |
161 | void fghPlatformGetCursorPos(const SFG_Window *window, GLboolean client, SFG_XYUse *mouse_pos) |
162 | { |
163 | /* Get current pointer location in screen coordinates (if client is false or window is NULL), else |
164 | * Get current pointer location relative to top-left of client area of window (if client is true and window is not NULL) |
165 | */ |
166 | Window w = (client && window && window->Window.Handle)? window->Window.Handle: fgDisplay.pDisplay.RootWindow; |
167 | Window junk_window; |
168 | unsigned int junk_mask; |
169 | int clientX, clientY; |
170 | |
171 | XQueryPointer(fgDisplay.pDisplay.Display, w, |
172 | &junk_window, &junk_window, |
173 | &mouse_pos->X, &mouse_pos->Y, /* Screen coords relative to root window's top-left */ |
174 | &clientX, &clientY, /* Client coords relative to window's top-left */ |
175 | &junk_mask); |
176 | |
177 | if (client && window && window->Window.Handle) |
178 | { |
179 | mouse_pos->X = clientX; |
180 | mouse_pos->Y = clientY; |
181 | } |
182 | |
183 | mouse_pos->Use = GL_TRUE; |
184 | } |
185 | |