From: Sven Panne Date: Wed, 5 Jan 2005 13:11:30 +0000 (+0000) Subject: Reworked the X11 part of glutSetCursor, fixing bug #764187 (Variable mouse X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?a=commitdiff_plain;h=ba21dbc1eca4d994466497da8a309ce1300e775f;p=freeglut Reworked the X11 part of glutSetCursor, fixing bug #764187 (Variable mouse pointers don't work) on the way: * Use slightly more compatible cursor shapes for GLUT_CURSOR_RIGHT_ARROW and GLUT_CURSOR_LEFT_ARROW. * Refactored and fixed the erroneous code for GLUT_CURSOR_NONE. * Removed the incorrect use of XFreeCursor and use a cache of cursors instead. Cursors are never freed now, we could do this e.g. via reference countig if this is really needed. * Fixed error handling. * Unknown cursor types are an error now. * Now the window state always corresponds to the wanted cursorID, even in the case of GLUT_CURSOR_FULL_CROSSHAIR. NOTE: I am not sure where the cursor cache should really reside, currently it is simply a file-local variable. git-svn-id: svn+ssh://svn.code.sf.net/p/freeglut/code/trunk/freeglut/freeglut@560 7f0cb862-5218-0410-a997-914c9d46530a --- diff --git a/ChangeLog b/ChangeLog index ad408ea..ffc15be 100644 --- a/ChangeLog +++ b/ChangeLog @@ -905,3 +905,16 @@ a bit to do that. (235) Fixed part of bug #926883 (Video mode matching code, memory leaks, fullscreen), i.e. issue warnings when XF86VidModeFOO fails. + +(236) Reworked the X11 part of glutSetCursor, fixing bug #764187 (Variable +mouse pointers don't work) on the way: + * Use slightly more compatible cursor shapes for GLUT_CURSOR_RIGHT_ARROW + and GLUT_CURSOR_LEFT_ARROW. + * Refactored and fixed the erroneous code for GLUT_CURSOR_NONE. + * Removed the incorrect use of XFreeCursor and use a cache of cursors + instead. Cursors are never freed now, we could do this e.g. via + reference countig if this is really needed. + * Fixed error handling. + * Unknown cursor types are an error now. + * Now the window state always corresponds to the wanted cursorID, even in + the case of GLUT_CURSOR_FULL_CROSSHAIR. diff --git a/src/freeglut_cursor.c b/src/freeglut_cursor.c index bbe27cd..db033ed 100644 --- a/src/freeglut_cursor.c +++ b/src/freeglut_cursor.c @@ -43,34 +43,64 @@ /* -- INTERNAL FUNCTIONS --------------------------------------------------- */ -#if TARGET_HOST_UNIX_X11 - -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 +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 */ +}; /* -- INTERFACE FUNCTIONS -------------------------------------------------- */ @@ -83,103 +113,46 @@ void FGAPIENTRY glutSetCursor( int cursorID ) FREEGLUT_EXIT_IF_NO_WINDOW ( "glutSetCursor" ); #if TARGET_HOST_UNIX_X11 - /* - * 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? - */ { - 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 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; - } - - case GLUT_CURSOR_INHERIT: - break; - - default: - fgWarning( "Unknown cursor type: %d", cursorID ); - return; + 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; + } } - 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_NONE ) && ( cursor == None ) ) { + fgError( "Failed to create cursor" ); } + XDefineCursor( fgDisplay.Display, + fgStructure.Window->Window.Handle, cursor ); } #elif TARGET_HOST_WIN32 || TARGET_HOST_WINCE @@ -244,7 +217,8 @@ void FGAPIENTRY glutWarpPointer( int x, int y ) 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 || TARGET_HOST_WINCE