X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?a=blobdiff_plain;f=src%2Ffreeglut_cursor.c;h=9732a2d039d29e4952e1ccc8d0594a5d0e3c5435;hb=d3069af045ceb4de1acfe6dac1308315565bd801;hp=0c4debb3b64dbb781c4d1b7453826d4ea5a35c97;hpb=89c225066899f8956939f8175a0d548cd6f63a62;p=freeglut diff --git a/src/freeglut_cursor.c b/src/freeglut_cursor.c index 0c4debb..9732a2d 100644 --- a/src/freeglut_cursor.c +++ b/src/freeglut_cursor.c @@ -25,17 +25,9 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - #include #include "freeglut_internal.h" -#if TARGET_HOST_UNIX_X11 - #include -#endif - /* * TODO BEFORE THE STABLE RELEASE: * glutSetCursor() -- Win32 mappings are incomplete. @@ -45,190 +37,235 @@ * apart from the windowing system version. */ -/* -- INTERNAL FUNCTIONS --------------------------------------------------- */ +/* -- PRIVATE FUNCTIONS --------------------------------------------------- */ -#if TARGET_HOST_UNIX_X11 +#if TARGET_HOST_POSIX_X11 + #include -static int fghGetCursorError( Cursor cursor ) +/* + * A factory method for an empty cursor + */ +static Cursor getEmptyCursor( void ) { - int ret = 0; - char buf[ 256 ]; - - switch( cursor ) - { - case BadAlloc: - case BadFont: - case BadMatch: - case BadPixmap: - case BadValue: - XGetErrorText( fgDisplay.Display, cursor, buf, sizeof buf ); - fgWarning( "Error in setting cursor:\n %s.", buf ); - ret = cursor; - break; - default: - /* no error */ - break; + static Cursor cursorNone = None; + if( cursorNone == None ) { + char cursorNoneBits[ 32 ]; + XColor dontCare; + Pixmap cursorNonePixmap; + memset( cursorNoneBits, 0, sizeof( cursorNoneBits ) ); + memset( &dontCare, 0, sizeof( dontCare ) ); + cursorNonePixmap = XCreateBitmapFromData ( fgDisplay.Display, + fgDisplay.RootWindow, + cursorNoneBits, 16, 16 ); + if( cursorNonePixmap != None ) { + cursorNone = XCreatePixmapCursor( fgDisplay.Display, + cursorNonePixmap, cursorNonePixmap, + &dontCare, &dontCare, 0, 0 ); + XFreePixmap( fgDisplay.Display, cursorNonePixmap ); + } } - - return ret; + return cursorNone; } -#endif - - -/* -- INTERFACE FUNCTIONS -------------------------------------------------- */ +typedef struct tag_cursorCacheEntry cursorCacheEntry; +struct tag_cursorCacheEntry { + unsigned int cursorShape; /* an XC_foo value */ + Cursor cachedCursor; /* None if the corresponding cursor has + not been created yet */ +}; /* - * Set the cursor image to be used for the current window - */ -void FGAPIENTRY glutSetCursor( int cursorID ) + * Note: The arrangement of the table below depends on the fact that + * the "normal" GLUT_CURSOR_* values start a 0 and are consecutive. + */ +static cursorCacheEntry cursorCache[] = { + { XC_arrow, None }, /* GLUT_CURSOR_RIGHT_ARROW */ + { XC_top_left_arrow, None }, /* GLUT_CURSOR_LEFT_ARROW */ + { XC_hand1, None }, /* GLUT_CURSOR_INFO */ + { XC_pirate, None }, /* GLUT_CURSOR_DESTROY */ + { XC_question_arrow, None }, /* GLUT_CURSOR_HELP */ + { XC_exchange, None }, /* GLUT_CURSOR_CYCLE */ + { XC_spraycan, None }, /* GLUT_CURSOR_SPRAY */ + { XC_watch, None }, /* GLUT_CURSOR_WAIT */ + { XC_xterm, None }, /* GLUT_CURSOR_TEXT */ + { XC_crosshair, None }, /* GLUT_CURSOR_CROSSHAIR */ + { XC_sb_v_double_arrow, None }, /* GLUT_CURSOR_UP_DOWN */ + { XC_sb_h_double_arrow, None }, /* GLUT_CURSOR_LEFT_RIGHT */ + { XC_top_side, None }, /* GLUT_CURSOR_TOP_SIDE */ + { XC_bottom_side, None }, /* GLUT_CURSOR_BOTTOM_SIDE */ + { XC_left_side, None }, /* GLUT_CURSOR_LEFT_SIDE */ + { XC_right_side, None }, /* GLUT_CURSOR_RIGHT_SIDE */ + { XC_top_left_corner, None }, /* GLUT_CURSOR_TOP_LEFT_CORNER */ + { XC_top_right_corner, None }, /* GLUT_CURSOR_TOP_RIGHT_CORNER */ + { XC_bottom_right_corner, None }, /* GLUT_CURSOR_BOTTOM_RIGHT_CORNER */ + { XC_bottom_left_corner, None } /* GLUT_CURSOR_BOTTOM_LEFT_CORNER */ +}; + +static void fghSetCursor ( SFG_Window *window, int cursorID ) { - freeglut_assert_ready; /* XXX WHY do we need the timer active for this? */ - FREEGLUT_EXIT_IF_NO_WINDOW ( "glutSetCursor" ); - -#if TARGET_HOST_UNIX_X11 + Cursor cursor; /* - * Open issues: - * (a) Partial error checking. Is that a problem? - * Is fghGetCursorError() correct? Should we abort on errors? - * Should there be a freeglut-wide X error handler? Should - * we use the X error-handler mechanism? - * (b) FULL_CROSSHAIR demotes to plain CROSSHAIR. Old GLUT allows - * for this, but if there is a system that easily supports a full- - * window (or full-screen) crosshair, we might consider it. - * (c) Out-of-range cursor-types generate warnings. Should we abort? + * XXX FULL_CROSSHAIR demotes to plain CROSSHAIR. Old GLUT allows + * for this, but if there is a system that easily supports a full- + * window (or full-screen) crosshair, we might consider it. */ - { - Cursor cursor = None; - Pixmap no_cursor = None ; /* Used for GLUT_CURSOR_NONE */ - int error = 0; - -#define MAP_CURSOR(a,b) \ - case a: \ - cursor = XCreateFontCursor( fgDisplay.Display, b ); \ - break; - - if( GLUT_CURSOR_FULL_CROSSHAIR == cursorID ) - cursorID = GLUT_CURSOR_CROSSHAIR; - - switch( cursorID ) + int cursorIDToUse = + ( cursorID == GLUT_CURSOR_FULL_CROSSHAIR ) ? GLUT_CURSOR_CROSSHAIR : cursorID; + + if( ( cursorIDToUse >= 0 ) && + ( cursorIDToUse < sizeof( cursorCache ) / sizeof( cursorCache[0] ) ) ) { + cursorCacheEntry *entry = &cursorCache[ cursorIDToUse ]; + if( entry->cachedCursor == None ) { + entry->cachedCursor = + XCreateFontCursor( fgDisplay.Display, entry->cursorShape ); + } + cursor = entry->cachedCursor; + } else { + switch( cursorIDToUse ) { - MAP_CURSOR( GLUT_CURSOR_RIGHT_ARROW, XC_right_ptr); - MAP_CURSOR( GLUT_CURSOR_LEFT_ARROW, XC_left_ptr); - MAP_CURSOR( GLUT_CURSOR_INFO, XC_hand1); - MAP_CURSOR( GLUT_CURSOR_DESTROY, XC_pirate); - MAP_CURSOR( GLUT_CURSOR_HELP, XC_question_arrow); - MAP_CURSOR( GLUT_CURSOR_CYCLE, XC_exchange); - MAP_CURSOR( GLUT_CURSOR_SPRAY, XC_spraycan); - MAP_CURSOR( GLUT_CURSOR_WAIT, XC_watch); - MAP_CURSOR( GLUT_CURSOR_TEXT, XC_xterm); - MAP_CURSOR( GLUT_CURSOR_CROSSHAIR, XC_crosshair); - MAP_CURSOR( GLUT_CURSOR_UP_DOWN, XC_sb_v_double_arrow); - MAP_CURSOR( GLUT_CURSOR_LEFT_RIGHT, XC_sb_h_double_arrow); - MAP_CURSOR( GLUT_CURSOR_TOP_SIDE, XC_top_side); - MAP_CURSOR( GLUT_CURSOR_BOTTOM_SIDE, XC_bottom_side); - MAP_CURSOR( GLUT_CURSOR_LEFT_SIDE, XC_left_side); - MAP_CURSOR( GLUT_CURSOR_RIGHT_SIDE, XC_right_side); - MAP_CURSOR( GLUT_CURSOR_TOP_LEFT_CORNER, XC_top_left_corner); - MAP_CURSOR( GLUT_CURSOR_TOP_RIGHT_CORNER, XC_top_right_corner); - MAP_CURSOR( GLUT_CURSOR_BOTTOM_RIGHT_CORNER, - XC_bottom_right_corner); - MAP_CURSOR( GLUT_CURSOR_BOTTOM_LEFT_CORNER, XC_bottom_left_corner); - /* MAP_CURSOR( GLUT_CURSOR_NONE, XC_bogosity); */ - case GLUT_CURSOR_NONE: - { - /* - * Note that we *never* change {no_cursor_bits} from anything - * but all-zeros. It is our image and mask. We also apparently - * need to pick a color for foreground/background---but what - * one we pick doesn't matter for GLUT_CURSOR_NONE. - */ - static char no_cursor_bits[ 32 ]; - XColor black; - no_cursor = XCreatePixmapFromBitmapData( fgDisplay.Display, - fgDisplay.RootWindow, - no_cursor_bits, - 16, 16, - 1, 0, 1 ); - XParseColor( fgDisplay.Display, - DefaultColormap( fgDisplay.Display, - DefaultScreen( fgDisplay.Display ) ), - "black", - &black ); - cursor = XCreatePixmapCursor( fgDisplay.Display, - no_cursor, no_cursor, - &black, &black, - 0, 0 ); + cursor = getEmptyCursor( ); break; - } case GLUT_CURSOR_INHERIT: + cursor = None; break; default: - fgWarning( "Unknown cursor type: %d", cursorID ); + fgError( "Unknown cursor type: %d", cursorIDToUse ); return; } + } - error = fghGetCursorError( cursor ); - - if( GLUT_CURSOR_INHERIT == cursorID ) - XUndefineCursor( fgDisplay.Display, - fgStructure.Window->Window.Handle ); - else - { - XDefineCursor( fgDisplay.Display, - fgStructure.Window->Window.Handle, cursor ); - XFreeCursor( fgDisplay.Display, cursor ); - if( GLUT_CURSOR_NONE == cursorID ) - XFreePixmap( fgDisplay.Display, no_cursor ); - } + if ( cursorIDToUse == GLUT_CURSOR_INHERIT ) { + XUndefineCursor( fgDisplay.Display, window->Window.Handle ); + } else if ( cursor != None ) { + XDefineCursor( fgDisplay.Display, window->Window.Handle, cursor ); + } else if ( cursorIDToUse != GLUT_CURSOR_NONE ) { + fgError( "Failed to create cursor" ); } +} + + +static void fghWarpPointer ( int x, int y ) +{ + XWarpPointer( + fgDisplay.Display, + None, + fgStructure.CurrentWindow->Window.Handle, + 0, 0, 0, 0, + x, y + ); + /* Make the warp visible immediately. */ + XFlush( fgDisplay.Display ); +} +#endif -#elif TARGET_HOST_WIN32 || TARGET_HOST_WINCE +#if TARGET_HOST_MS_WINDOWS +static void fghSetCursor ( SFG_Window *window, int cursorID ) +{ /* - * This is a temporary solution only... + * Joe Krahn is re-writing the following code. */ /* Set the cursor AND change it for this window class. */ +#if !defined(__MINGW64__) && _MSC_VER <= 1200 # define MAP_CURSOR(a,b) \ case a: \ SetCursor( LoadCursor( NULL, b ) ); \ - SetClassLong( fgStructure.Window->Window.Handle, \ + SetClassLong( window->Window.Handle, \ GCL_HCURSOR, \ ( LONG )LoadCursor( NULL, b ) ); \ break; - /* Nuke the cursor AND change it for this window class. */ # define ZAP_CURSOR(a,b) \ case a: \ SetCursor( NULL ); \ - SetClassLong( fgStructure.Window->Window.Handle, \ + SetClassLong( window->Window.Handle, \ GCL_HCURSOR, ( LONG )NULL ); \ break; +#else +# define MAP_CURSOR(a,b) \ + case a: \ + SetCursor( LoadCursor( NULL, b ) ); \ + SetClassLongPtr( window->Window.Handle, \ + GCLP_HCURSOR, \ + ( LONG )( LONG_PTR )LoadCursor( NULL, b ) ); \ + break; + /* Nuke the cursor AND change it for this window class. */ +# define ZAP_CURSOR(a,b) \ + case a: \ + SetCursor( NULL ); \ + SetClassLongPtr( window->Window.Handle, \ + GCLP_HCURSOR, ( LONG )( LONG_PTR )NULL ); \ + break; +#endif switch( cursorID ) { - MAP_CURSOR( GLUT_CURSOR_RIGHT_ARROW, IDC_ARROW ); - MAP_CURSOR( GLUT_CURSOR_LEFT_ARROW, IDC_ARROW ); - MAP_CURSOR( GLUT_CURSOR_INFO, IDC_HELP ); - MAP_CURSOR( GLUT_CURSOR_DESTROY, IDC_CROSS ); - MAP_CURSOR( GLUT_CURSOR_HELP, IDC_HELP ); - MAP_CURSOR( GLUT_CURSOR_CYCLE, IDC_SIZEALL ); - MAP_CURSOR( GLUT_CURSOR_SPRAY, IDC_CROSS ); - MAP_CURSOR( GLUT_CURSOR_WAIT, IDC_WAIT ); - MAP_CURSOR( GLUT_CURSOR_TEXT, IDC_UPARROW ); - MAP_CURSOR( GLUT_CURSOR_CROSSHAIR, IDC_CROSS ); - /* MAP_CURSOR( GLUT_CURSOR_NONE, IDC_NO ); */ - ZAP_CURSOR( GLUT_CURSOR_NONE, NULL ); + MAP_CURSOR( GLUT_CURSOR_RIGHT_ARROW, IDC_ARROW ); + MAP_CURSOR( GLUT_CURSOR_LEFT_ARROW, IDC_ARROW ); + MAP_CURSOR( GLUT_CURSOR_INFO, IDC_HELP ); + MAP_CURSOR( GLUT_CURSOR_DESTROY, IDC_CROSS ); + MAP_CURSOR( GLUT_CURSOR_HELP, IDC_HELP ); + MAP_CURSOR( GLUT_CURSOR_CYCLE, IDC_SIZEALL ); + MAP_CURSOR( GLUT_CURSOR_SPRAY, IDC_CROSS ); + MAP_CURSOR( GLUT_CURSOR_WAIT, IDC_WAIT ); + MAP_CURSOR( GLUT_CURSOR_TEXT, IDC_IBEAM ); + MAP_CURSOR( GLUT_CURSOR_CROSSHAIR, IDC_CROSS ); + MAP_CURSOR( GLUT_CURSOR_UP_DOWN, IDC_SIZENS ); + MAP_CURSOR( GLUT_CURSOR_LEFT_RIGHT, IDC_SIZEWE ); + MAP_CURSOR( GLUT_CURSOR_TOP_SIDE, IDC_ARROW ); /* XXX ToDo */ + MAP_CURSOR( GLUT_CURSOR_BOTTOM_SIDE, IDC_ARROW ); /* XXX ToDo */ + MAP_CURSOR( GLUT_CURSOR_LEFT_SIDE, IDC_ARROW ); /* XXX ToDo */ + MAP_CURSOR( GLUT_CURSOR_RIGHT_SIDE, IDC_ARROW ); /* XXX ToDo */ + MAP_CURSOR( GLUT_CURSOR_TOP_LEFT_CORNER, IDC_SIZENWSE ); + MAP_CURSOR( GLUT_CURSOR_TOP_RIGHT_CORNER, IDC_SIZENESW ); + MAP_CURSOR( GLUT_CURSOR_BOTTOM_RIGHT_CORNER, IDC_SIZENWSE ); + MAP_CURSOR( GLUT_CURSOR_BOTTOM_LEFT_CORNER, IDC_SIZENESW ); + MAP_CURSOR( GLUT_CURSOR_INHERIT, IDC_ARROW ); /* XXX ToDo */ + ZAP_CURSOR( GLUT_CURSOR_NONE, NULL ); + MAP_CURSOR( GLUT_CURSOR_FULL_CROSSHAIR, IDC_CROSS ); /* XXX ToDo */ default: - MAP_CURSOR( GLUT_CURSOR_UP_DOWN, IDC_ARROW ); + fgError( "Unknown cursor type: %d", cursorID ); + break; } +} + + +static void fghWarpPointer ( int x, int y ) +{ + POINT coords; + coords.x = x; + coords.y = y; + + /* ClientToScreen() translates {coords} for us. */ + ClientToScreen( fgStructure.CurrentWindow->Window.Handle, &coords ); + SetCursorPos( coords.x, coords.y ); +} #endif - fgStructure.Window->State.Cursor = cursorID; + +/* -- INTERNAL FUNCTIONS ---------------------------------------------------- */ +void fgSetCursor ( SFG_Window *window, int cursorID ) +{ + fghSetCursor ( window, cursorID ); +} + + +/* -- INTERFACE FUNCTIONS -------------------------------------------------- */ + +/* + * Set the cursor image to be used for the current window + */ +void FGAPIENTRY glutSetCursor( int cursorID ) +{ + FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSetCursor" ); + FREEGLUT_EXIT_IF_NO_WINDOW ( "glutSetCursor" ); + + fghSetCursor ( fgStructure.CurrentWindow, cursorID ); + fgStructure.CurrentWindow->State.Cursor = cursorID; } /* @@ -236,33 +273,10 @@ void FGAPIENTRY glutSetCursor( int cursorID ) */ void FGAPIENTRY glutWarpPointer( int x, int y ) { - freeglut_assert_ready; /* XXX WHY do we need the timer active for this? */ + FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWarpPointer" ); FREEGLUT_EXIT_IF_NO_WINDOW ( "glutWarpPointer" ); -#if TARGET_HOST_UNIX_X11 - - XWarpPointer( - fgDisplay.Display, - None, - fgStructure.Window->Window.Handle, - 0, 0, 0, 0, - x, y - ); - XFlush( fgDisplay.Display ); /* XXX Is this really necessary? */ - -#elif TARGET_HOST_WIN32 || TARGET_HOST_WINCE - - { - POINT coords; - coords.x = x; - coords.y = y; - - /* ClientToScreen() translates {coords} for us. */ - ClientToScreen( fgStructure.Window->Window.Handle, &coords ); - SetCursorPos( coords.x, coords.y ); - } - -#endif + fghWarpPointer ( x, y ); } /*** END OF FILE ***/