X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?a=blobdiff_plain;f=src%2Ffreeglut_cursor.c;h=93b6b2056c55f1b4595091e74aa30ad34ffd3d17;hb=e9ebb8053106d2504605c70a1fd777a52f1d9da4;hp=63b90700feec9eab8cd47bbc57a6adc68d23e850;hpb=6d46289cbc2703c9dc2f46160d6bd00fc09fe46a;p=freeglut diff --git a/src/freeglut_cursor.c b/src/freeglut_cursor.c index 63b9070..93b6b20 100644 --- a/src/freeglut_cursor.c +++ b/src/freeglut_cursor.c @@ -25,14 +25,10 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "../include/GL/freeglut.h" +#include #include "freeglut_internal.h" -#if TARGET_HOST_UNIX_X11 +#if TARGET_HOST_POSIX_X11 #include #endif @@ -45,190 +41,205 @@ * apart from the windowing system version. */ -/* -- INTERNAL FUNCTIONS --------------------------------------------------- */ +/* -- PRIVATE FUNCTIONS --------------------------------------------------- */ -#if TARGET_HOST_UNIX_X11 - -int fgGetCursorError( Cursor cursor ) +#if TARGET_HOST_POSIX_X11 +/* + * 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; } +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 */ +}; + +/* + * 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 */ +}; #endif - -/* -- INTERFACE FUNCTIONS -------------------------------------------------- */ +/* -- INTERNAL FUNCTIONS ---------------------------------------------------- */ /* * Set the cursor image to be used for the current window */ -void FGAPIENTRY glutSetCursor( int cursorID ) +void fgSetCursor ( SFG_Window *window, int cursorID ) { - freeglut_assert_ready; /* XXX WHY do we need the timer active for this? */ - freeglut_assert_window; - -#if TARGET_HOST_UNIX_X11 - /* - * Open issues: - * (a) Partial error checking. Is that a problem? - * Is fgGetCursorError() 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? - */ +#if TARGET_HOST_POSIX_X11 { - 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 ) - { - 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 unsigned 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 ); - break; + Cursor cursor; + /* + * 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. + */ + 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 ) + { + case GLUT_CURSOR_NONE: + cursor = getEmptyCursor( ); + break; + + case GLUT_CURSOR_INHERIT: + cursor = None; + break; + + default: + fgError( "Unknown cursor type: %d", cursorIDToUse ); + return; + } } - - case GLUT_CURSOR_INHERIT: - break; - default: - fgWarning( "Unknown cursor type: %d\n", cursorID ); - return; - } - - error = fgGetCursorError( 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" ); } } -#elif TARGET_HOST_WIN32 +#elif TARGET_HOST_MS_WINDOWS /* - * 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 _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; } #endif - fgStructure.Window->State.Cursor = cursorID; + window->State.Cursor = 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" ); + + fgSetCursor ( fgStructure.CurrentWindow, cursorID ); } /* @@ -236,31 +247,30 @@ 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_assert_window; + FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutWarpPointer" ); + FREEGLUT_EXIT_IF_NO_WINDOW ( "glutWarpPointer" ); -#if TARGET_HOST_UNIX_X11 +#if TARGET_HOST_POSIX_X11 XWarpPointer( fgDisplay.Display, None, - fgStructure.Window->Window.Handle, + fgStructure.CurrentWindow->Window.Handle, 0, 0, 0, 0, x, y ); - XFlush( fgDisplay.Display ); /* XXX Is this really necessary? */ + /* Make the warp visible immediately. */ + XFlush( fgDisplay.Display ); -#elif TARGET_HOST_WIN32 +#elif TARGET_HOST_MS_WINDOWS { POINT coords; coords.x = x; coords.y = y; - /* - * ClientToScreen() translates {coords} for us. - */ - ClientToScreen( fgStructure.Window->Window.Handle, &coords ); + /* ClientToScreen() translates {coords} for us. */ + ClientToScreen( fgStructure.CurrentWindow->Window.Handle, &coords ); SetCursorPos( coords.x, coords.y ); }