| 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 | |