X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?a=blobdiff_plain;f=freeglut-1.3%2Ffreeglut_main.c;h=1c11f0d30801079b6250992b6fe770684b6414df;hb=9ddfa4b5ee92933e7ff826ea6685cab44d7a04cd;hp=682e316725a8198d9aeb0a33760e46fab2d8a160;hpb=56d746d5a727b7a5fb489e81b1f19816f4ab9b9d;p=freeglut diff --git a/freeglut-1.3/freeglut_main.c b/freeglut-1.3/freeglut_main.c index 682e316..1c11f0d 100644 --- a/freeglut-1.3/freeglut_main.c +++ b/freeglut-1.3/freeglut_main.c @@ -32,7 +32,7 @@ #define G_LOG_DOMAIN "freeglut-main" #include "../include/GL/freeglut.h" -#include "../include/GL/freeglut_internal.h" +#include "freeglut_internal.h" /* * TODO BEFORE THE STABLE RELEASE: @@ -51,6 +51,7 @@ * Calls a window's redraw method. This is used when * a redraw is forced by the incoming window messages. */ + static void fghRedrawWindowByHandle #if TARGET_HOST_UNIX_X11 ( Window handle ) @@ -75,16 +76,21 @@ static void fghRedrawWindowByHandle freeglut_return_if_fail( window->State.Visible == TRUE ); /* - * Set the window as the current one. Calling glutSetWindow() - * might seem slow and generally redundant, but it is portable. + * Set the window as the current one. + */ + fgSetWindow( window ); + + /* + * Do not exagerate with the redisplaying */ - glutSetWindow( window->ID ); + window->State.Redisplay = FALSE; /* * Have the callback executed now. The buffers should * be swapped by the glutSwapBuffers() execution inside * the callback itself. */ + window->Callbacks.Display(); } @@ -109,7 +115,7 @@ static void fghReshapeWindowByHandle /* * Remember about setting the current window... */ - glutSetWindow( window->ID ); + fgSetWindow( window ); /* * Check if there is a reshape callback hooked @@ -128,6 +134,14 @@ static void fghReshapeWindowByHandle */ glViewport( 0, 0, width, height ); } + + /* + * Force a window redraw. In Windows at least this is only a partial solution: if the + * window is increasing in size in either dimension, the already-drawn part does not get + * drawn again and things look funny. But without this we get this bad behaviour whenever + * we resize the window. + */ + window->State.Redisplay = TRUE ; } /* @@ -146,7 +160,7 @@ static void fghcbDisplayWindow( SFG_Window *window, SFG_Enumerator *enumerator ) /* * OKi, this is the case: have the window set as the current one */ - glutSetWindow( window->ID ); + fgSetWindow( window ); /* * Do not exagerate with the redisplaying @@ -166,7 +180,7 @@ static void fghcbDisplayWindow( SFG_Window *window, SFG_Enumerator *enumerator ) */ if( window->State.NeedToResize ) { - glutSetWindow( window->ID ); + fgSetWindow( window ); fghReshapeWindowByHandle( window->Window.Handle, @@ -183,10 +197,20 @@ static void fghcbDisplayWindow( SFG_Window *window, SFG_Enumerator *enumerator ) /* * This is done in a bit different way under Windows */ - RedrawWindow( + if( (window->Callbacks.Display != NULL) && + (window->State.Redisplay == TRUE) && + (window->State.Visible == TRUE) ) + { + /* + * Do not exagerate with the redisplaying + */ + window->State.Redisplay = FALSE; + + RedrawWindow( window->Window.Handle, NULL, NULL, - RDW_NOERASE | RDW_INTERNALPAINT | RDW_INVALIDATE - ); + RDW_NOERASE | RDW_INTERNALPAINT | RDW_INVALIDATE | RDW_UPDATENOW + ); + } #endif @@ -220,7 +244,7 @@ static void fghDisplayAll( void ) */ static void fghcbCheckJoystickPolls( SFG_Window *window, SFG_Enumerator *enumerator ) { - double checkTime = fgElapsedTime(); + long int checkTime = fgElapsedTime(); /* * Check if actually need to do the poll for the currently enumerated window: @@ -307,12 +331,13 @@ static void fghCheckTimers( void ) } } + /* * Elapsed Time */ long fgElapsedTime( void ) { -#ifndef WIN32 +#if TARGET_HOST_UNIX_X11 struct timeval now; long elapsed; @@ -322,8 +347,8 @@ long fgElapsedTime( void ) elapsed += (now.tv_sec - fgState.Time.Value.tv_sec) * 1000; return( elapsed ); -#else - return (timeGetTime() - fgState.Time.Value); +#elif TARGET_HOST_WIN32 + return (timeGetTime() - fgState.Time.Value); #endif } @@ -358,559 +383,754 @@ void fgWarning( const char *fmt, ... ) va_end( ap ); } +/* + * Clean up on exit + */ +static void fgCleanUpGlutsMess( void ) +{ + int i; + + i = 0; + + if ( fgStructure.Windows.First != NULL ) + { + SFG_Window *win = fgStructure.Windows.First ; + glEnd(); + glFinish(); + glFlush(); + while ( win != NULL ) + { + SFG_Window *temp_win = win->Node.Next ; + fgDestroyWindow ( win, FALSE ) ; + win = temp_win ; + } + } + +#if 0 + /* these are pointers to external handles */ + + __glutWindowListSize = 0; + __glutStaleWindowList = NULL; + __glutWindowList = NULL; + __glutCurrentWindow = NULL; + + /* make sure we no longer have a GL context */ + + if ( wglGetCurrentContext() != NULL ) + { + wglDeleteContext( wglGetCurrentContext() ); + } + + hInstance = GetModuleHandle(NULL); + UnregisterClass( classname, hInstance ); + + /* clean up allocated timer memory */ + + tList = __glutTimerList; + i = 0; + + while ( __glutTimerList ) + { + i++; + tList = __glutTimerList; + + if ( __glutTimerList ) + __glutTimerList = __glutTimerList->next; + + if ( tList ) + free( tList ); + } +#endif +} + + /* -- INTERFACE FUNCTIONS -------------------------------------------------- */ /* - * Enters the FreeGLUT processing loop. Never returns. + * Executes a single iteration in the freeglut processing loop. */ -void FGAPIENTRY glutMainLoop( void ) +void FGAPIENTRY glutMainLoopEvent( void ) { #if TARGET_HOST_UNIX_X11 - SFG_Window* window; - XEvent event; - int modifiers; - + SFG_Window* window; + XEvent event; + int modifiers; + + /* + * This code was repeated constantly, so here it goes into a definition: + */ +# define GETWINDOW(a) window = fgWindowByHandle( event.a.window );if( window == NULL ) break; +# define GETMOUSE(a) window->State.MouseX = event.a.x; window->State.MouseY = event.a.y; + + /* + * Make sure the display has been created etc. + */ + freeglut_assert_ready; + + /* + * Do we have any event messages pending? + */ + if( XPending( fgDisplay.Display ) ) + { /* - * This code was repeated constantly, so here it goes into a definition: + * Grab the next event to be processed... */ -# define GETWINDOW(a) window = fgWindowByHandle( event.a.window );if( window == NULL ) break; -# define GETMOUSE(a) window->State.MouseX = event.a.x; window->State.MouseY = event.a.y; + XNextEvent( fgDisplay.Display, &event ); + window = fgWindowByHandle ( event.xany.window ) ; /* - * Make sure the display has been created etc. + * Check the event's type */ - freeglut_assert_ready; - - /* - * Enter the loop. Iterate as long as there are - * any windows in the freeglut structure. - */ - while( fgStructure.Windows.First != NULL ) + switch( event.type ) { + case CreateNotify: + /* + * The window creation confirmation + */ + break; + + case DestroyNotify: + /* + * This is sent to confirm the XDestroyWindow call. + */ + /* + * Call the window closure callback, remove from the structure, etc. + */ + fgStructure.Window = window ; +/* fgAddToWindowDestroyList ( window, FALSE ); */ + + break; + + case ClientMessage: + /* + * Destroy the window when the WM_DELETE_WINDOW message arrives + */ + if( (Atom) event.xclient.data.l[ 0 ] == fgDisplay.DeleteWindow ) + { + fgStructure.Window = window ; + /* - * Do we have any event messages pending? + * Call the XWindows functions to close the window */ - if( XPending( fgDisplay.Display ) ) + fgCloseWindow ( window ) ; + + /* + * Call the window closure callback, remove from the structure, etc. + */ + fgAddToWindowDestroyList ( window, FALSE ); + } + break; + + case UnmapNotify: + /* + * A window of ours has been unmapped... + */ + break; + + case Expose: + /* + * We are too dumb to process partial exposes... + */ + if( event.xexpose.count == 0 ) + fghRedrawWindowByHandle( window->Window.Handle ); + break; + + case ConfigureNotify: + /* + * The window gets resized + */ + fghReshapeWindowByHandle( + event.xconfigure.window, + event.xconfigure.width, + event.xconfigure.height + ); + break; + + case MappingNotify: + /* + * Have the client's keyboard knowledge updated (xlib.ps, + * page 206, says that's a good thing to do) + */ + XRefreshKeyboardMapping( (XMappingEvent *) &event ); + break; + + case VisibilityNotify: + { + /* + * The window's visiblity might have changed + */ + /* + * Break now if no window status callback has been hooked to that window + */ + if( window->Callbacks.WindowStatus == NULL ) + break; + + /* + * We're going to send a callback to a window. Make it current. + */ + fgSetWindow( window ); + + /* + * Sending this event, the X server can notify us that the window has just + * acquired one of the three possible visibility states: VisibilityUnobscured, + * VisibilityPartiallyObscured or VisibilityFullyObscured + */ + switch( event.xvisibility.state ) { - /* - * Grab the next event to be processed... - */ - XNextEvent( fgDisplay.Display, &event ); + case VisibilityUnobscured: + /* + * We are fully visible... + */ + window->Callbacks.WindowStatus( GLUT_FULLY_RETAINED ); + window->State.Visible = TRUE; + break; + + case VisibilityPartiallyObscured: + /* + * The window is partially visible + */ + window->Callbacks.WindowStatus( GLUT_PARTIALLY_RETAINED ); + window->State.Visible = TRUE; + break; + + case VisibilityFullyObscured: + /* + * The window is totally obscured + */ + window->Callbacks.WindowStatus( GLUT_FULLY_COVERED ); + window->State.Visible = FALSE; + break; + } + } + break; - /* - * Check the event's type - */ - switch( event.type ) - { - case CreateNotify: - /* - * The window creation confirmation - */ - break; + case EnterNotify: + { + /* + * Mouse is over one of our windows + */ + GETMOUSE( xcrossing ); - case DestroyNotify: - /* - * This is sent to confirm the XDestroyWindow call. Ignore it. - */ - break; + /* + * Is there an entry callback hooked to the window? + */ + if( window->Callbacks.Entry != NULL ) + { + /* + * Yeah. Notify the window about having the mouse cursor over + */ + window->Callbacks.Entry( GLUT_ENTERED ); + } + } + break; - case ClientMessage: - /* - * Destroy the window when the WM_DELETE_WINDOW message arrives - */ - if( (Atom) event.xclient.data.l[ 0 ] == fgDisplay.DeleteWindow ) - { - /* - * I wonder if the window still exists ;-) - */ - fgDestroyWindow( fgWindowByHandle( event.xclient.window ), TRUE ); - } - break; - - case UnmapNotify: - /* - * A window of ours has been unmapped... - */ - break; + case LeaveNotify: + { + /* + * Mouse is no longer over one of our windows + */ + GETMOUSE( xcrossing ); - case Expose: - /* - * We are too dumb to process partial exposes... - */ - if( event.xexpose.count == 0 ) - fghRedrawWindowByHandle( event.xexpose.window ); - break; + /* + * Is there an entry callback hooked to the window? + */ + if( window->Callbacks.Entry != NULL ) + { + /* + * Yeah. Notify the window about having the mouse cursor over + */ + window->Callbacks.Entry( GLUT_LEFT ); + } + } + break; - case ConfigureNotify: - /* - * The window gets resized - */ - fghReshapeWindowByHandle( - event.xconfigure.window, - event.xconfigure.width, - event.xconfigure.height - ); - break; - - case MappingNotify: - /* - * Have the client's keyboard knowledge updated (xlib.ps, - * page 206, says that's a good thing to do) - */ - XRefreshKeyboardMapping( (XMappingEvent *) &event ); - break; - - case VisibilityNotify: - { - /* - * The window's visiblity might have changed - */ - GETWINDOW( xvisibility ); - - /* - * Break now if no window status callback has been hooked to that window - */ - if( window->Callbacks.WindowStatus == NULL ) - break; - - /* - * We're going to send a callback to a window. Make it current. - */ - glutSetWindow( window->ID ); - - /* - * Sending this event, the X server can notify us that the window has just - * acquired one of the three possible visibility states: VisibilityUnobscured, - * VisibilityPartiallyObscured or VisibilityFullyObscured - */ - switch( event.xvisibility.state ) - { - case VisibilityUnobscured: - /* - * We are fully visible... - */ - window->Callbacks.WindowStatus( GLUT_FULLY_RETAINED ); - window->State.Visible = TRUE; - break; - - case VisibilityPartiallyObscured: - /* - * The window is partially visible - */ - window->Callbacks.WindowStatus( GLUT_PARTIALLY_RETAINED ); - window->State.Visible = TRUE; - break; - - case VisibilityFullyObscured: - /* - * The window is totally obscured - */ - window->Callbacks.WindowStatus( GLUT_FULLY_COVERED ); - window->State.Visible = FALSE; - break; - } - } - break; - - case EnterNotify: - { - /* - * Mouse is over one of our windows - */ - GETWINDOW( xcrossing ); GETMOUSE( xcrossing ); - - /* - * Is there an entry callback hooked to the window? - */ - if( window->Callbacks.Entry != NULL ) - { - /* - * Yeah. Notify the window about having the mouse cursor over - */ - window->Callbacks.Entry( GLUT_ENTERED ); - } - } - break; - - case LeaveNotify: - { - /* - * Mouse is no longer over one of our windows - */ - GETWINDOW( xcrossing ); GETMOUSE( xcrossing ); - - /* - * Is there an entry callback hooked to the window? - */ - if( window->Callbacks.Entry != NULL ) - { - /* - * Yeah. Notify the window about having the mouse cursor over - */ - window->Callbacks.Entry( GLUT_LEFT ); - } - } - break; - - case MotionNotify: - { - /* - * The mouse cursor was moved... - */ - GETWINDOW( xmotion ); GETMOUSE( xmotion ); - - /* - * What kind of a movement was it? - */ - if( (event.xmotion.state & Button1Mask) || (event.xmotion.state & Button2Mask) || - (event.xmotion.state & Button3Mask) || (event.xmotion.state & Button4Mask) || - (event.xmotion.state & Button5Mask) ) - { - /* - * A mouse button was pressed during the movement... - * Is there a motion callback hooked to the window? - */ - if( window->Callbacks.Motion != NULL ) - { - /* - * Yup. Have it executed immediately - */ - window->Callbacks.Motion( event.xmotion.x, event.xmotion.y ); - } - } - else - { - /* - * Otherwise it was a passive movement... - */ - if( window->Callbacks.Passive != NULL ) - { - /* - * That's right, and there is a passive callback, too. - */ - window->Callbacks.Passive( event.xmotion.x, event.xmotion.y ); - } - } - } - break; - - case ButtonRelease: - case ButtonPress: - { - int button; - - /* - * A mouse button has been pressed or released. Traditionally, - * break if the window was found within the freeglut structures. - */ - GETWINDOW( xbutton ); GETMOUSE( xbutton ); - - /* - * GLUT API assumes that you can't have more than three mouse buttons, so: - */ - switch( event.xbutton.button ) - { - /* - * WARNING: this might be wrong, if we only have two mouse buttons, - * Button2 might mean the right button, isn't that right? - */ - case Button1: button = GLUT_LEFT_BUTTON; break; - case Button2: button = GLUT_MIDDLE_BUTTON; break; - case Button3: button = GLUT_RIGHT_BUTTON; break; - default: button = -1; break; - } - - /* - * Skip the unwanted mouse buttons... - */ - if( button == -1 ) - break; - - /* - * Do not execute the callback if a menu is hooked to this key. - * In that case an appropriate private call should be generated - */ - if( window->Menu[ button ] != NULL ) - { - /* - * Set the current window - */ - glutSetWindow( window->ID ); - - if( event.type == ButtonPress ) - { - /* - * Activate the appropriate menu structure... - */ - fgActivateMenu( button ); - } - else - { - /* - * There are two general cases generated when a menu button - * is released -- it can provoke a menu call (when released - * over a menu area) or just deactivate the menu (when released - * somewhere else). Unfortunately, both cases must be checked - * recursively due to the submenu possibilities. - */ - fgDeactivateMenu( button ); - } - break; - } - - /* - * Check if there is a mouse callback hooked to the window - */ - if( window->Callbacks.Mouse == NULL ) - break; - - /* - * Set the current window - */ - glutSetWindow( window->ID ); - - /* - * Remember the current modifiers state - */ - modifiers = 0; - if (event.xbutton.state & (ShiftMask|LockMask)) - modifiers |= GLUT_ACTIVE_SHIFT; - if (event.xbutton.state & ControlMask) - modifiers |= GLUT_ACTIVE_CTRL; - if (event.xbutton.state & Mod1Mask) - modifiers |= GLUT_ACTIVE_ALT; - window->State.Modifiers = modifiers; - - /* - * Finally execute the mouse callback - */ - window->Callbacks.Mouse( - button, - event.type == ButtonPress ? GLUT_DOWN : GLUT_UP, - event.xbutton.x, - event.xbutton.y - ); - - /* - * Trash the modifiers state - */ - window->State.Modifiers = 0xffffffff; - } - break; - - case KeyPress: - { - /* - * A key has been pressed, find the window that had the focus: - */ - GETWINDOW( xkey ); GETMOUSE( xkey ); - - /* - * Is there a keyboard/special callback hooked for this window? - */ - if( (window->Callbacks.Keyboard != NULL) || (window->Callbacks.Special != NULL) ) - { - XComposeStatus composeStatus; - char asciiCode[ 32 ]; - KeySym keySym; - int len; - - /* - * Check for the ASCII/KeySym codes associated with the event: - */ - len = XLookupString( &event.xkey, asciiCode, sizeof(asciiCode), &keySym, &composeStatus ); - - /* - * Get ready to calling the keyboard/special callbacks - */ - glutSetWindow( window->ID ); - - /* - * GLUT API tells us to have two separate callbacks... - */ - if( len > 0 ) - { - /* - * ...one for the ASCII translateable keypresses... - */ - if( window->Callbacks.Keyboard != NULL ) - { - /* - * Remember the current modifiers state - */ - modifiers = 0; - if (event.xkey.state & (ShiftMask|LockMask)) - modifiers |= GLUT_ACTIVE_SHIFT; - if (event.xkey.state & ControlMask) - modifiers |= GLUT_ACTIVE_CTRL; - if (event.xkey.state & Mod1Mask) - modifiers |= GLUT_ACTIVE_ALT; - window->State.Modifiers = modifiers; - - /* - * Execute the callback - */ - window->Callbacks.Keyboard( asciiCode[ 0 ], event.xkey.x, event.xkey.y ); - - /* - * Trash the modifiers state - */ - window->State.Modifiers = 0xffffffff; - } - } - else - { - int special = -1; - - /* - * ...and one for all the others, which need to be translated to GLUT_KEY_Xs... - */ - switch( keySym ) - { - /* - * First the function keys come: - */ - case XK_F1: special = GLUT_KEY_F1; break; - case XK_F2: special = GLUT_KEY_F2; break; - case XK_F3: special = GLUT_KEY_F3; break; - case XK_F4: special = GLUT_KEY_F4; break; - case XK_F5: special = GLUT_KEY_F5; break; - case XK_F6: special = GLUT_KEY_F6; break; - case XK_F7: special = GLUT_KEY_F7; break; - case XK_F8: special = GLUT_KEY_F8; break; - case XK_F9: special = GLUT_KEY_F9; break; - case XK_F10: special = GLUT_KEY_F10; break; - case XK_F11: special = GLUT_KEY_F11; break; - case XK_F12: special = GLUT_KEY_F12; break; - - /* - * Then the arrows and stuff: - */ - case XK_Left: special = GLUT_KEY_LEFT; break; - case XK_Right: special = GLUT_KEY_RIGHT; break; - case XK_Up: special = GLUT_KEY_UP; break; - case XK_Down: special = GLUT_KEY_DOWN; break; - } - - /* - * Execute the callback (if one has been specified), - * given that the special code seems to be valid... - */ - if( (window->Callbacks.Special != NULL) && (special != -1) ) - { - /* - * Remember the current modifiers state - */ - modifiers = 0; - if (event.xkey.state & (ShiftMask|LockMask)) - modifiers |= GLUT_ACTIVE_SHIFT; - if (event.xkey.state & ControlMask) - modifiers |= GLUT_ACTIVE_CTRL; - if (event.xkey.state & Mod1Mask) - modifiers |= GLUT_ACTIVE_ALT; - window->State.Modifiers = modifiers; - - window->Callbacks.Special( special, event.xkey.x, event.xkey.y ); - - /* - * Trash the modifiers state - */ - window->State.Modifiers = 0xffffffff; - } - } - } - } - break; - } + case MotionNotify: + { + /* + * The mouse cursor was moved... + */ + GETMOUSE( xmotion ); + + /* + * Set the current window + */ + fgStructure.Window = window ; + + /* + * What kind of a movement was it? + */ + if( (event.xmotion.state & Button1Mask) || (event.xmotion.state & Button2Mask) || + (event.xmotion.state & Button3Mask) || (event.xmotion.state & Button4Mask) || + (event.xmotion.state & Button5Mask) ) + { + /* + * A mouse button was pressed during the movement... + * Is there a motion callback hooked to the window? + */ + if( window->Callbacks.Motion != NULL ) + { + /* + * Yup. Have it executed immediately + */ + window->Callbacks.Motion( event.xmotion.x, event.xmotion.y ); + } } else { + /* + * Otherwise it was a passive movement... + */ + if( window->Callbacks.Passive != NULL ) + { /* - * Have all the timers checked. + * That's right, and there is a passive callback, too. */ - fghCheckTimers(); + window->Callbacks.Passive( event.xmotion.x, event.xmotion.y ); + } + } + } + break; - /* - * Poll the joystick and notify all windows that want to be notified... - */ - fghCheckJoystickPolls(); + case ButtonRelease: + case ButtonPress: + { + GLboolean pressed = TRUE ; + int button; - /* - * No messages in the queue, which means we are idling... - */ - if( fgState.IdleCallback != NULL ) - fgState.IdleCallback(); + if ( event.type == ButtonRelease ) pressed = FALSE ; - /* - * Remember about displaying all the windows that have - * been marked for a redisplay (possibly in the idle call): - */ - fghDisplayAll(); + /* + * A mouse button has been pressed or released. Traditionally, + * break if the window was found within the freeglut structures. + */ + GETMOUSE( xbutton ); + + /* + * GLUT API assumes that you can't have more than three mouse buttons, so: + */ + switch( event.xbutton.button ) + { + /* + * WARNING: this might be wrong, if we only have two mouse buttons, + * Button2 might mean the right button, isn't that right? + */ + case Button1: button = GLUT_LEFT_BUTTON; break; + case Button2: button = GLUT_MIDDLE_BUTTON; break; + case Button3: button = GLUT_RIGHT_BUTTON; break; + default: button = -1; break; } - } -#elif TARGET_HOST_WIN32 + /* + * Skip the unwanted mouse buttons... + */ + if( button == -1 ) + break; - GLboolean bLoop = TRUE; - MSG stMsg; + /* + * Do not execute the application's mouse callback if a menu is hooked to this button. + * In that case an appropriate private call should be generated. + * Near as I can tell, this is the menu behaviour: + * - Down-click the menu button, menu not active: activate the menu with its upper left-hand corner at the mouse location. + * - Down-click any button outside the menu, menu active: deactivate the menu + * - Down-click any button inside the menu, menu active: select the menu entry and deactivate the menu + * - Up-click the menu button, menu not active: nothing happens + * - Up-click the menu button outside the menu, menu active: nothing happens + * - Up-click the menu button inside the menu, menu active: select the menu entry and deactivate the menu + */ + if ( window->ActiveMenu != NULL ) /* Window has an active menu, it absorbs any mouse click */ + { + if ( fgCheckActiveMenu ( window, window->ActiveMenu ) == TRUE ) /* Inside the menu, invoke the callback and deactivate the menu*/ + { + /* Save the current window and menu and set the current window to the window whose menu this is */ + SFG_Window *save_window = fgStructure.Window ; + SFG_Menu *save_menu = fgStructure.Menu ; + fgSetWindow ( window ) ; + fgStructure.Menu = window->ActiveMenu ; + + /* Execute the menu callback */ + fgExecuteMenuCallback ( window->ActiveMenu ) ; + fgDeactivateMenu ( window ) ; + + /* Restore the current window and menu */ + fgSetWindow ( save_window ) ; + fgStructure.Menu = save_menu ; + } + else /* Outside the menu, deactivate the menu if it's a downclick */ + { + if ( pressed == TRUE ) fgDeactivateMenu ( window ) ; + } + + /* + * Let's make the window redraw as a result of the mouse click and menu activity. + */ + window->State.Redisplay = TRUE ; + + break ; + } - /* - * The windows main loop is considerably smaller - */ - while( bLoop ) - { - if( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) ) + /* + * No active menu, let's check whether we need to activate one. + */ + if ( ( window->Menu[ button ] != NULL ) && ( pressed == TRUE ) ) { - /* - * Grab the message now, checking for WM_QUIT - */ - if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 ) - bLoop = FALSE; + /* + * Let's make the window redraw as a result of the mouse click. + */ + window->State.Redisplay = TRUE ; - /* - * Translate virtual-key messages and send them to the window... - */ - TranslateMessage( &stMsg ); - DispatchMessage( &stMsg ); + /* + * Activate the appropriate menu structure... + */ + fgActivateMenu( window, button ); + + break; + } + + /* + * Check if there is a mouse callback hooked to the window + */ + if( window->Callbacks.Mouse == NULL ) + break; + + /* + * Set the current window + */ + fgSetWindow( window ); + + /* + * Remember the current modifiers state + */ + modifiers = 0; + if (event.xbutton.state & (ShiftMask|LockMask)) + modifiers |= GLUT_ACTIVE_SHIFT; + if (event.xbutton.state & ControlMask) + modifiers |= GLUT_ACTIVE_CTRL; + if (event.xbutton.state & Mod1Mask) + modifiers |= GLUT_ACTIVE_ALT; + window->State.Modifiers = modifiers; + + /* + * Finally execute the mouse callback + */ + window->Callbacks.Mouse( + button, + event.type == ButtonPress ? GLUT_DOWN : GLUT_UP, + event.xbutton.x, + event.xbutton.y + ); + + /* + * Trash the modifiers state + */ + window->State.Modifiers = 0xffffffff; + } + break; + + case KeyRelease: + case KeyPress: + { + FGCBkeyboard keyboard_cb; + FGCBspecial special_cb; + + /* + * A key has been pressed, find the window that had the focus: + */ + GETMOUSE( xkey ); + + if( event.type == KeyPress ) + { + keyboard_cb = window->Callbacks.Keyboard; + special_cb = window->Callbacks.Special; } else { + keyboard_cb = window->Callbacks.KeyboardUp; + special_cb = window->Callbacks.SpecialUp; + } + + /* + * Is there a keyboard/special callback hooked for this window? + */ + if( (keyboard_cb != NULL) || (special_cb != NULL) ) + { + XComposeStatus composeStatus; + char asciiCode[ 32 ]; + KeySym keySym; + int len; + + /* + * Check for the ASCII/KeySym codes associated with the event: + */ + len = XLookupString( &event.xkey, asciiCode, sizeof(asciiCode), &keySym, &composeStatus ); + + /* + * Get ready to calling the keyboard/special callbacks + */ + fgSetWindow( window ); + + /* + * GLUT API tells us to have two separate callbacks... + */ + if( len > 0 ) + { /* - * Have all the timers checked. + * ...one for the ASCII translateable keypresses... */ - fghCheckTimers(); + if( keyboard_cb != NULL ) + { + /* + * Remember the current modifiers state + */ + modifiers = 0; + if (event.xkey.state & (ShiftMask|LockMask)) + modifiers |= GLUT_ACTIVE_SHIFT; + if (event.xkey.state & ControlMask) + modifiers |= GLUT_ACTIVE_CTRL; + if (event.xkey.state & Mod1Mask) + modifiers |= GLUT_ACTIVE_ALT; + window->State.Modifiers = modifiers; + + /* + * Execute the callback + */ + keyboard_cb( asciiCode[ 0 ], event.xkey.x, event.xkey.y ); + + /* + * Trash the modifiers state + */ + window->State.Modifiers = 0xffffffff; + } + } + else + { + int special = -1; /* - * Poll the joystick and notify all windows that want to be notified... + * ...and one for all the others, which need to be translated to GLUT_KEY_Xs... */ - fghCheckJoystickPolls(); - + switch( keySym ) + { /* - * No messages in the queue, which means we are idling... + * First the function keys come: */ - if( fgState.IdleCallback != NULL ) - fgState.IdleCallback(); + case XK_F1: special = GLUT_KEY_F1; break; + case XK_F2: special = GLUT_KEY_F2; break; + case XK_F3: special = GLUT_KEY_F3; break; + case XK_F4: special = GLUT_KEY_F4; break; + case XK_F5: special = GLUT_KEY_F5; break; + case XK_F6: special = GLUT_KEY_F6; break; + case XK_F7: special = GLUT_KEY_F7; break; + case XK_F8: special = GLUT_KEY_F8; break; + case XK_F9: special = GLUT_KEY_F9; break; + case XK_F10: special = GLUT_KEY_F10; break; + case XK_F11: special = GLUT_KEY_F11; break; + case XK_F12: special = GLUT_KEY_F12; break; /* - * Remember about displaying all the windows that have - * been marked for a redisplay (possibly in the idle call): + * Then the arrows and stuff: */ - fghDisplayAll(); + case XK_Left: special = GLUT_KEY_LEFT; break; + case XK_Right: special = GLUT_KEY_RIGHT; break; + case XK_Up: special = GLUT_KEY_UP; break; + case XK_Down: special = GLUT_KEY_DOWN; break; + + case XK_KP_Prior: + case XK_Prior: special = GLUT_KEY_PAGE_UP; break; + case XK_KP_Next: + case XK_Next: special = GLUT_KEY_PAGE_DOWN; break; + case XK_KP_Home: + case XK_Home: special = GLUT_KEY_HOME; break; + case XK_KP_End: + case XK_End: special = GLUT_KEY_END; break; + case XK_KP_Insert: + case XK_Insert: special = GLUT_KEY_INSERT; break; + } /* - * We need to terminate the main loop if no windows are left + * Execute the callback (if one has been specified), + * given that the special code seems to be valid... */ - bLoop = (g_list_length( fgStructure.Windows ) != 0); + if( (special_cb != NULL) && (special != -1) ) + { + /* + * Remember the current modifiers state + */ + modifiers = 0; + if (event.xkey.state & (ShiftMask|LockMask)) + modifiers |= GLUT_ACTIVE_SHIFT; + if (event.xkey.state & ControlMask) + modifiers |= GLUT_ACTIVE_CTRL; + if (event.xkey.state & Mod1Mask) + modifiers |= GLUT_ACTIVE_ALT; + window->State.Modifiers = modifiers; + + special_cb( special, event.xkey.x, event.xkey.y ); + + /* + * Trash the modifiers state + */ + window->State.Modifiers = 0xffffffff; + } + } } + } + break; } + } + else + { + /* + * Have all the timers checked. + */ + fghCheckTimers(); + + /* + * Poll the joystick and notify all windows that want to be notified... + */ + fghCheckJoystickPolls(); + + /* + * No messages in the queue, which means we are idling... + */ + if( fgState.IdleCallback != NULL ) + fgState.IdleCallback(); + + /* + * Remember about displaying all the windows that have + * been marked for a redisplay (possibly in the idle call): + */ + fghDisplayAll(); + } + +#elif TARGET_HOST_WIN32 + + MSG stMsg; + + /* + * The windows processing is considerably smaller + */ + if( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) ) + { + /* + * Grab the message now, checking for WM_QUIT + */ + if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 ) + fgState.ExecState = GLUT_EXEC_STATE_STOP ; + + /* + * Translate virtual-key messages and send them to the window... + */ + TranslateMessage( &stMsg ); + DispatchMessage( &stMsg ); + } + else + { + /* + * Have all the timers checked. + */ + fghCheckTimers(); + + /* + * Poll the joystick and notify all windows that want to be notified... + */ + fghCheckJoystickPolls(); + + /* + * No messages in the queue, which means we are idling... + */ + if( fgState.IdleCallback != NULL ) + fgState.IdleCallback(); + + /* + * Remember about displaying all the windows that have + * been marked for a redisplay (possibly in the idle call): + */ + fghDisplayAll(); + } +#endif +} + +/* + * Enters the freeglut processing loop. Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP". + */ +void FGAPIENTRY glutMainLoop( void ) +{ +#if TARGET_HOST_WIN32 + SFG_Window *window = fgStructure.Windows.First ; +#endif + + /* + * Make sure the display has been created etc. + */ + freeglut_assert_ready; +#if TARGET_HOST_WIN32 + /* + * Processing before the main loop: If there is a window which is open and which + * has a visibility callback, call it. I know this is an ugly hack, but I'm not sure + * what else to do about it. Ideally we should leave something uninitialized in the + * create window code and initialize it in the main loop, and have that initialization + * create a "WM_ACTIVATE" message. Then we would put the visibility callback code in + * the "case WM_ACTIVATE" block below. - John Fay -- 10/24/02 + */ + while ( window != NULL ) + { + if ( window->Callbacks.Visibility != NULL ) + window->Callbacks.Visibility ( window->State.Visible ) ; + + window = window->Node.Next ; + } #endif + /* + * Set freeglut to be running + */ + fgState.ExecState = GLUT_EXEC_STATE_RUNNING ; + + /* + * Enter the main loop itself. Inside the loop, process events and check for loop exit. + */ + while ( fgState.ExecState == GLUT_EXEC_STATE_RUNNING ) + { + glutMainLoopEvent () ; + + /* + * If an event caused a window to be closed, do the actual closing here + */ + fgCloseWindows () ; + /* - * When this loop terminates, destroy the display, state and structure - * of a freeglut session, so that another glutInit() call can happen + * If there are no more windows open, stop execution */ - fgDeinitialize(); + if ( fgStructure.Windows.First == NULL ) + fgState.ExecState = GLUT_EXEC_STATE_STOP ; + } + + + /* + * If we got here by the user closing a window or by the application closing down, there may still be windows open. + */ + fgCleanUpGlutsMess () ; + + /* + * Check whether we return to the calling program or simply exit + */ + if ( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT ) + exit ( 0 ) ; + + /* + * When this loop terminates, destroy the display, state and structure + * of a freeglut session, so that another glutInit() call can happen + */ + fgDeinitialize(); +} + +/* + * Leaves the freeglut processing loop. + */ +void FGAPIENTRY glutLeaveMainLoop( void ) +{ + fgState.ExecState = GLUT_EXEC_STATE_STOP ; } /* @@ -923,8 +1143,10 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara PAINTSTRUCT ps; LONG lRet = 1; -# define assert_window_registered if( window == NULL ) return( DefWindowProc( hWnd, uMsg, wParam, lParam ) ); + if ( ( window == NULL ) && ( uMsg != WM_CREATE ) ) + return( DefWindowProc( hWnd, uMsg, wParam, lParam ) ); +/* printf ( "Window %3d message <%04x> %12d %12d\n", window?window->ID:0, uMsg, wParam, lParam ) ; */ /* * Check what type of message are we receiving */ @@ -950,7 +1172,7 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara /* * Setup the pixel format of our window */ - fgSetupPixelFormat( window, FALSE ); + fgSetupPixelFormat( window, FALSE, PFD_MAIN_PLANE ); /* * Create the OpenGL rendering context now @@ -974,11 +1196,78 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara */ fghReshapeWindowByHandle( hWnd, LOWORD(lParam), HIWORD(lParam) ); break; +#if 0 + case WM_SETFOCUS: + printf("WM_SETFOCUS: %p\n", window ); + lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); + break; + + case WM_ACTIVATE: + if (LOWORD(wParam) != WA_INACTIVE) + { + /* glutSetCursor( fgStructure.Window->State.Cursor ); */ + printf("WM_ACTIVATE: glutSetCursor( %p, %d)\n", window, window->State.Cursor ); + + glutSetCursor( window->State.Cursor ); + } + + lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); + break; +#endif + + case WM_SETCURSOR: + /* + * Windows seems to need reminding to erase the cursor for NONE. + */ +#if 0 + if ((LOWORD(lParam) == HTCLIENT) && + (fgStructure.Window->State.Cursor == GLUT_CURSOR_NONE)) + SetCursor( NULL ); +#else + /* Set the cursor AND change it for this window class. */ +# define MAP_CURSOR(a,b) case a: SetCursor( LoadCursor( NULL, b ) ); \ + break; + /* Nuke the cursor AND change it for this window class. */ +# define ZAP_CURSOR(a,b) case a: SetCursor( NULL ); \ + break; + + if (LOWORD(lParam) == HTCLIENT) + switch( window->State.Cursor ) + { + 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 ); + + default: + MAP_CURSOR( GLUT_CURSOR_UP_DOWN, IDC_ARROW ); + } +#endif + else + lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); + break; + + case WM_SHOWWINDOW: + /* + * We are now Visible! + */ + window->State.Visible = TRUE; + window->State.Redisplay = TRUE; + break; case WM_PAINT: /* * Start the painting job */ + BeginPaint( hWnd, &ps ); /* @@ -1003,22 +1292,24 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara } /* + * Put on a linked list of windows to be removed after all the callbacks have returned + */ + fgAddToWindowDestroyList ( window, FALSE ) ; + + /* * Proceed with the window destruction */ - DestroyWindow( window->Window.Handle ); + DestroyWindow( hWnd ); break; case WM_DESTROY: /* - * The window already got destroyed, so forget about it's existence: + * The window already got destroyed, so don't bother with it. */ - fgDestroyWindow( window, FALSE ); return( 0 ); case WM_MOUSEMOVE: { - assert_window_registered; - /* * The mouse cursor has moved. Remember the new mouse cursor's position */ @@ -1028,16 +1319,23 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara /* * Fallback if there's an active menu hooked to this window */ - if( window->MenuActive[ 0 ] || window->MenuActive[ 1 ] || window->MenuActive[ 2 ] ) + if( window->ActiveMenu != NULL ) + { + /* + * Let's make the window redraw as a result of the mouse motion. + */ + window->State.Redisplay = TRUE ; + break; + } /* * Remember the current modifiers state. */ window->State.Modifiers = - (GetKeyState( VK_LSHIFT ) || GetKeyState( VK_RSHIFT )) ? GLUT_ACTIVE_SHIFT : 0 | - (GetKeyState( VK_LCONTROL ) || GetKeyState( VK_RCONTROL )) ? GLUT_ACTIVE_CTRL : 0 | - (GetKeyState( VK_LMENU ) || GetKeyState( VK_RMENU )) ? GLUT_ACTIVE_ALT : 0; + ( ( (GetKeyState( VK_LSHIFT ) < 0 ) || ( GetKeyState( VK_RSHIFT ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) | + ( ( (GetKeyState( VK_LCONTROL ) < 0 ) || ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL : 0 ) | + ( ( (GetKeyState( VK_LMENU ) < 0 ) || ( GetKeyState( VK_RMENU ) < 0 )) ? GLUT_ACTIVE_ALT : 0 ); /* * Check if any of the mouse buttons is pressed... @@ -1052,7 +1350,7 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara /* * Make sure the current window is set... */ - glutSetWindow( window->ID ); + fgSetWindow( window ); /* * Execute the active mouse motion callback now @@ -1070,7 +1368,7 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara /* * Make sure the current window is set */ - glutSetWindow( window->ID ); + fgSetWindow( window ); /* * Execute the passive mouse motion callback @@ -1097,12 +1395,6 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara int button; /* - * A mouse button has been pressed *or* released. Again, break off - * if the message was not directed towards a freeglut window... - */ - assert_window_registered; - - /* * The mouse cursor has moved. Remember the new mouse cursor's position */ window->State.MouseX = LOWORD( lParam ); @@ -1136,34 +1428,62 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara return( DefWindowProc( hWnd, uMsg, lParam, wParam ) ); /* - * Do not execute the callback if a menu is hooked to this key. - * In that case an appropriate private call should be generated + * Do not execute the application's mouse callback if a menu is hooked to this button. + * In that case an appropriate private call should be generated. + * Near as I can tell, this is the menu behaviour: + * - Down-click the menu button, menu not active: activate the menu with its upper left-hand corner at the mouse location. + * - Down-click any button outside the menu, menu active: deactivate the menu + * - Down-click any button inside the menu, menu active: select the menu entry and deactivate the menu + * - Up-click the menu button, menu not active: nothing happens + * - Up-click the menu button outside the menu, menu active: nothing happens + * - Up-click the menu button inside the menu, menu active: select the menu entry and deactivate the menu + */ + if ( window->ActiveMenu != NULL ) /* Window has an active menu, it absorbs any mouse click */ + { + if ( fgCheckActiveMenu ( window, window->ActiveMenu ) == TRUE ) /* Inside the menu, invoke the callback and deactivate the menu*/ + { + /* Save the current window and menu and set the current window to the window whose menu this is */ + SFG_Window *save_window = fgStructure.Window ; + SFG_Menu *save_menu = fgStructure.Menu ; + fgSetWindow ( window ) ; + fgStructure.Menu = window->ActiveMenu ; + + /* Execute the menu callback */ + fgExecuteMenuCallback ( window->ActiveMenu ) ; + fgDeactivateMenu ( window ) ; + + /* Restore the current window and menu */ + fgSetWindow ( save_window ) ; + fgStructure.Menu = save_menu ; + } + else /* Outside the menu, deactivate the menu if it's a downclick */ + { + if ( pressed == TRUE ) fgDeactivateMenu ( window ) ; + } + + /* + * Let's make the window redraw as a result of the mouse click and menu activity. + */ + window->State.Redisplay = TRUE ; + + break ; + } + + /* + * No active menu, let's check whether we need to activate one. */ - if( window->Menu[ button ] != NULL ) + if ( ( window->Menu[ button ] != NULL ) && ( pressed == TRUE ) ) { /* - * Set the current window + * Let's make the window redraw as a result of the mouse click. */ - glutSetWindow( window->ID ); + window->State.Redisplay = TRUE ; + + /* + * Activate the appropriate menu structure... + */ + fgActivateMenu( window, button ); - if( pressed == TRUE ) - { - /* - * Activate the appropriate menu structure... - */ - fgActivateMenu( button ); - } - else - { - /* - * There are two general cases generated when a menu button - * is released -- it can provoke a menu call (when released - * over a menu area) or just deactivate the menu (when released - * somewhere else). Unfortunately, both cases must be checked - * recursively due to the submenu possibilities. - */ - fgDeactivateMenu( button ); - } break; } @@ -1176,15 +1496,15 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara /* * Set the current window */ - glutSetWindow( window->ID ); + fgSetWindow( window ); /* * Remember the current modifiers state. */ window->State.Modifiers = - (GetKeyState( VK_LSHIFT ) || GetKeyState( VK_RSHIFT )) ? GLUT_ACTIVE_SHIFT : 0 | - (GetKeyState( VK_LCONTROL ) || GetKeyState( VK_RCONTROL )) ? GLUT_ACTIVE_CTRL : 0 | - (GetKeyState( VK_LMENU ) || GetKeyState( VK_RMENU )) ? GLUT_ACTIVE_ALT : 0; + ( ( (GetKeyState( VK_LSHIFT ) < 0 ) || ( GetKeyState( VK_RSHIFT ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) | + ( ( (GetKeyState( VK_LCONTROL ) < 0 ) || ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL : 0 ) | + ( ( (GetKeyState( VK_LMENU ) < 0 ) || ( GetKeyState( VK_RMENU ) < 0 )) ? GLUT_ACTIVE_ALT : 0 ); /* * Finally execute the mouse callback @@ -1207,11 +1527,7 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara case WM_KEYDOWN: { int keypress = -1; - - /* - * First of all, make sure that there is a window to be notified of this - */ - assert_window_registered; + POINT mouse_pos ; /* * Ignore the automatic key repetition if needed: @@ -1220,13 +1536,27 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara break; /* + * Set the current window + */ + fgSetWindow( window ); + + /* * Remember the current modifiers state. This is done here in order * to make sure the VK_DELETE keyboard callback is executed properly. */ window->State.Modifiers = - (GetKeyState( VK_LSHIFT ) || GetKeyState( VK_RSHIFT )) ? GLUT_ACTIVE_SHIFT : 0 | - (GetKeyState( VK_LCONTROL ) || GetKeyState( VK_RCONTROL )) ? GLUT_ACTIVE_CTRL : 0 | - (GetKeyState( VK_LMENU ) || GetKeyState( VK_RMENU )) ? GLUT_ACTIVE_ALT : 0; + ( ( (GetKeyState( VK_LSHIFT ) < 0 ) || ( GetKeyState( VK_RSHIFT ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) | + ( ( (GetKeyState( VK_LCONTROL ) < 0 ) || ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL : 0 ) | + ( ( (GetKeyState( VK_LMENU ) < 0 ) || ( GetKeyState( VK_RMENU ) < 0 )) ? GLUT_ACTIVE_ALT : 0 ); + + /* + * Set the mouse position + */ + GetCursorPos ( &mouse_pos ) ; + ScreenToClient ( window->Window.Handle, &mouse_pos ) ; + + window->State.MouseX = mouse_pos.x ; + window->State.MouseY = mouse_pos.y ; /* * Convert the Win32 keystroke codes to GLUTtish way @@ -1279,15 +1609,107 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara } break; - case WM_SYSCHAR: - case WM_CHAR: + case WM_SYSKEYUP: + case WM_KEYUP: { + int keypress = -1; + POINT mouse_pos ; + + /* + * Set the current window + */ + fgSetWindow( window ); + /* - * First of all, make sure that there is a window to be notified of this + * Remember the current modifiers state. This is done here in order + * to make sure the VK_DELETE keyboard callback is executed properly. */ - assert_window_registered; + window->State.Modifiers = + ( ( (GetKeyState( VK_LSHIFT ) < 0 ) || ( GetKeyState( VK_RSHIFT ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) | + ( ( (GetKeyState( VK_LCONTROL ) < 0 ) || ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL : 0 ) | + ( ( (GetKeyState( VK_LMENU ) < 0 ) || ( GetKeyState( VK_RMENU ) < 0 )) ? GLUT_ACTIVE_ALT : 0 ); /* + * Set the mouse position + */ + GetCursorPos ( &mouse_pos ) ; + ScreenToClient ( window->Window.Handle, &mouse_pos ) ; + + window->State.MouseX = mouse_pos.x ; + window->State.MouseY = mouse_pos.y ; + + /* + * Convert the Win32 keystroke codes to GLUTtish way. "KEY(a,b)" was defined under "WM_KEYDOWN" + */ + + switch( wParam ) + { + /* + * Most of the special characters can be handled automagically... + */ + KEY( VK_F1, GLUT_KEY_F1 ); KEY( VK_F2, GLUT_KEY_F2 ); + KEY( VK_F3, GLUT_KEY_F3 ); KEY( VK_F4, GLUT_KEY_F4 ); + KEY( VK_F5, GLUT_KEY_F5 ); KEY( VK_F6, GLUT_KEY_F6 ); + KEY( VK_F7, GLUT_KEY_F7 ); KEY( VK_F8, GLUT_KEY_F8 ); + KEY( VK_F9, GLUT_KEY_F9 ); KEY( VK_F10, GLUT_KEY_F10 ); + KEY( VK_F11, GLUT_KEY_F11 ); KEY( VK_F12, GLUT_KEY_F12 ); + KEY( VK_PRIOR, GLUT_KEY_PAGE_UP ); KEY( VK_NEXT, GLUT_KEY_PAGE_DOWN ); + KEY( VK_HOME, GLUT_KEY_HOME ); KEY( VK_END, GLUT_KEY_END ); + KEY( VK_LEFT, GLUT_KEY_LEFT ); KEY( VK_UP, GLUT_KEY_UP ); + KEY( VK_RIGHT, GLUT_KEY_RIGHT ); KEY( VK_DOWN, GLUT_KEY_DOWN ); + KEY( VK_INSERT, GLUT_KEY_INSERT ); + + /* + * ...yet there is a small exception we need to have handled... + */ + case VK_DELETE: + /* + * The delete key should be treated as an ASCII keypress: + */ + if( window->Callbacks.KeyboardUp != NULL ) + window->Callbacks.KeyboardUp( 127, window->State.MouseX, window->State.MouseY ); + + break ; + default: + { + /* + * Call the KeyboardUp callback for a regular character if there is one. + */ + BYTE state[ 256 ]; + WORD code[ 2 ]; + + GetKeyboardState(state); + + if ( ToAscii( wParam, 0, state, code, 0 ) == 1 ) + wParam=code[ 0 ]; + + if( window->Callbacks.KeyboardUp != NULL ) + window->Callbacks.KeyboardUp( (char)wParam, window->State.MouseX, window->State.MouseY ); + } + } + + /* + * Execute the special callback, if present, given the conversion was a success: + */ + if( (keypress != -1) && (window->Callbacks.SpecialUp != NULL) ) + { + /* + * Have the special callback executed: + */ + window->Callbacks.SpecialUp( keypress, window->State.MouseX, window->State.MouseY ); + } + + /* + * Thrash the modifiers register now + */ + window->State.Modifiers = 0xffffffff; + } + break; + + case WM_SYSCHAR: + case WM_CHAR: + { + /* * Ignore the automatic key repetition if needed: */ if( fgState.IgnoreKeyRepeat && (lParam & KF_REPEAT) ) @@ -1302,14 +1724,14 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara * Remember the current modifiers state */ window->State.Modifiers = - (GetKeyState( VK_LSHIFT ) || GetKeyState( VK_RSHIFT )) ? GLUT_ACTIVE_SHIFT : 0 | - (GetKeyState( VK_LCONTROL ) || GetKeyState( VK_RCONTROL )) ? GLUT_ACTIVE_CTRL : 0 | - (GetKeyState( VK_LMENU ) || GetKeyState( VK_RMENU )) ? GLUT_ACTIVE_ALT : 0; + ( ( (GetKeyState( VK_LSHIFT ) < 0 ) || ( GetKeyState( VK_RSHIFT ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) | + ( ( (GetKeyState( VK_LCONTROL ) < 0 ) || ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL : 0 ) | + ( ( (GetKeyState( VK_LMENU ) < 0 ) || ( GetKeyState( VK_RMENU ) < 0 )) ? GLUT_ACTIVE_ALT : 0 ); /* * Have the special callback executed: */ - window->Callbacks.Keyboard( wParam, window->State.MouseX, window->State.MouseY ); + window->Callbacks.Keyboard( (char)wParam, window->State.MouseX, window->State.MouseY ); /* * Thrash the modifiers register now @@ -1319,6 +1741,47 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara } break; + case WM_CAPTURECHANGED : /* User has finished resizing the window, force a redraw */ + if ( window->Callbacks.Display ) + window->Callbacks.Display () ; + +/* lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ) ; */ + break ; + + /* + * Other messages that I have seen and which are not handled already + */ + case WM_SETTEXT : /* 0x000c */ + lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); /* Pass it on to "DefWindowProc" to set the window text */ + break ; + + case WM_GETTEXT : /* 0x000d */ + /* Ideally we would copy the title of the window into "lParam" */ +/* strncpy ( (char *)lParam, "Window Title", wParam ) ; + lRet = ( wParam > 12 ) ? 12 : wParam ; */ /* the number of characters copied */ + lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); + break ; + + case WM_GETTEXTLENGTH : /* 0x000e */ + /* Ideally we would get the length of the title of the window */ + lRet = 12 ; /* the number of characters in "Window Title\0" (see above) */ + break ; + + case WM_ERASEBKGND : /* 0x0014 */ + lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); + break ; + + case WM_SYNCPAINT : /* 0x0088 */ + /* Another window has moved, need to update this one */ + window->State.Redisplay = TRUE ; + lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); /* Help screen says this message must be passed to "DefWindowProc" */ + break ; + + case WM_NCPAINT : /* 0x0085 */ + /* Need to update the border of this window */ + lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); /* Pass it on to "DefWindowProc" to repaint a standard border */ + break ; + default: /* * Handle unhandled messages