4 * The windows message processing methods.
6 * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
7 * Written by Pawel W. Olszta, <olszta@sourceforge.net>
8 * Creation date: Fri Dec 3 1999
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:
17 * The above copyright notice and this permission notice shall be included
18 * in all copies or substantial portions of the Software.
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.
32 #define G_LOG_DOMAIN "freeglut-main"
34 #include "../include/GL/freeglut.h"
35 #include "../include/GL/freeglut_internal.h"
38 * TODO BEFORE THE STABLE RELEASE:
40 * There are some issues concerning window redrawing under X11, and maybe
41 * some events are not handled. The Win32 version lacks some more features,
42 * but seems acceptable for not demanding purposes.
44 * Need to investigate why the X11 version breaks out with an error when
45 * closing a window (using the window manager, not glutDestroyWindow)...
48 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
51 * Calls a window's redraw method. This is used when
52 * a redraw is forced by the incoming window messages.
54 static void fghRedrawWindowByHandle
55 #if TARGET_HOST_UNIX_X11
57 #elif TARGET_HOST_WIN32
62 * Find the window we have to redraw...
64 SFG_Window* window = fgWindowByHandle( handle );
65 freeglut_return_if_fail( window != NULL );
68 * Check if there is a display callback hooked to it
70 freeglut_return_if_fail( window->Callbacks.Display != NULL );
73 * Return if the window is not visible
75 freeglut_return_if_fail( window->State.Visible != TRUE );
78 * Set the window as the current one. Calling glutSetWindow()
79 * might seem slow and generally redundant, but it is portable.
81 glutSetWindow( window->ID );
84 * Have the callback executed now. The buffers should
85 * be swapped by the glutSwapBuffers() execution inside
86 * the callback itself.
88 window->Callbacks.Display();
92 * Handle a window configuration change. When no reshape
93 * callback is hooked, the viewport size is updated to
94 * match the new window size.
96 static void fghReshapeWindowByHandle
97 #if TARGET_HOST_UNIX_X11
98 ( Window handle, gint width, gint height )
99 #elif TARGET_HOST_WIN32
100 ( HWND handle, gint width, gint height )
104 * Find the window that received the reshape event
106 SFG_Window* window = fgWindowByHandle( handle );
107 freeglut_return_if_fail( window != NULL );
110 * Remember about setting the current window...
112 glutSetWindow( window->ID );
115 * Check if there is a reshape callback hooked
117 if( window->Callbacks.Reshape != NULL )
120 * OKi, have it called immediately
122 window->Callbacks.Reshape( width, height );
127 * Otherwise just resize the viewport
129 glViewport( 0, 0, width, height );
134 * A static helper function to execute display callback for a window
136 static void fghcbDisplayWindow( gpointer window, gpointer enumerator )
138 #if TARGET_HOST_UNIX_X11
140 * Check if there is an idle callback hooked
142 # warning there is a redisplay hack here (see the code commented out)
143 if( (((SFG_Window *) window)->Callbacks.Display != NULL) /*&&
144 /*(((SFG_Window *) window)->State.Redisplay == TRUE)*/ &&
145 (((SFG_Window *) window)->State.Visible == TRUE) )
148 * OKi, this is the case: have the window set as the current one
150 glutSetWindow( ((SFG_Window *) window)->ID );
153 * Do not exagerate with the redisplaying
155 ((SFG_Window *) window)->State.Redisplay = FALSE;
158 * And execute the display callback immediately after
160 ((SFG_Window *) window)->Callbacks.Display();
163 #elif TARGET_HOST_WIN32
166 * Do we need to explicitly resize the window?
168 if( ((SFG_Window *) window)->State.NeedToResize )
170 glutSetWindow( ((SFG_Window *) window)->ID );
172 fghReshapeWindowByHandle(
173 ((SFG_Window *) window)->Window.Handle,
174 glutGet( GLUT_WINDOW_WIDTH ),
175 glutGet( GLUT_WINDOW_HEIGHT )
179 * Never ever do that again:
181 ((SFG_Window *) window)->State.NeedToResize = FALSE;
185 * This is done in a bit different way under Windows
188 ((SFG_Window *) window)->Window.Handle, NULL, NULL,
189 RDW_NOERASE | RDW_INTERNALPAINT | RDW_INVALIDATE
195 * Process this window's children (if any)
197 fgEnumSubWindows( (SFG_Window *) window, fghcbDisplayWindow, enumerator );
201 * Make all windows perform a display call
203 static void fghDisplayAll( void )
205 SFG_Enumerator enumerator;
208 * Uses a method very similiar for fgWindowByHandle...
210 enumerator.found = FALSE;
211 enumerator.data = NULL;
214 * Start the enumeration now:
216 fgEnumWindows( fghcbDisplayWindow, &enumerator );
220 * Window enumerator callback to check for the joystick polling code
222 static void fghcbCheckJoystickPolls( gpointer window, gpointer enumerator )
224 double checkTime = g_timer_elapsed( fgState.Timer, NULL );
225 SFG_Window* win = (SFG_Window *) window;
228 * Check if actually need to do the poll for the currently enumerated window:
230 if( win->State.JoystickLastPoll + win->State.JoystickPollRate >= checkTime )
233 * Yeah, that's it. Poll the joystick...
235 fgJoystickPollWindow( (SFG_Window *) window );
238 * ...and reset the polling counters:
240 win->State.JoystickLastPoll = checkTime;
244 * Process this window's children (if any)
246 fgEnumSubWindows( (SFG_Window *) window, fghcbCheckJoystickPolls, enumerator );
250 * Check all windows for joystick polling
252 static void fghCheckJoystickPolls( void )
254 SFG_Enumerator enumerator;
257 * Uses a method very similiar for fgWindowByHandle...
259 enumerator.found = FALSE;
260 enumerator.data = NULL;
263 * Start the enumeration now:
265 fgEnumWindows( fghcbCheckJoystickPolls, &enumerator );
269 * Check the global timers
271 static void fghCheckTimers( void )
273 double checkTime = g_timer_elapsed( fgState.Timer, NULL );
274 SFG_Timer* timer = NULL;
275 GList* timedOut = NULL;
279 * For every timer that is waiting for triggering
281 for( i=0; i<(gint) g_list_length( fgState.Timers ); i++ )
284 * ...grab the appropriate timer hook structure pointer
286 timer = (SFG_Timer *) g_list_nth( fgState.Timers, i )->data;
287 g_assert( timer != NULL );
290 * Check for the timeout:
292 if( timer->TriggerTime <= checkTime )
295 * Add the timer to the timed out timers list
297 timedOut = g_list_append( timedOut, timer );
302 * Now, have all the timed out timers removed from the window hooks
304 length = g_list_length( timedOut );
306 for( i=0; i<length; i++ )
308 fgState.Timers = g_list_remove(
310 g_list_nth( timedOut, i )->data
315 * Now feel free to execute all the hooked and timed out timer callbacks
317 for( i=0; i<length; i++ )
319 if( timer->Callback != NULL )
320 timer->Callback( timer->ID );
324 * Finally, delete the timed out timers...
326 for( i=0; i<length; i++ )
327 g_free( g_list_nth( timedOut, i )->data );
330 * Finally, have the timed out timers list released
332 if( timedOut != NULL )
333 g_list_free( timedOut );
337 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
340 * Enters the FreeGLUT processing loop. Never returns.
342 void FGAPIENTRY glutMainLoop( void )
344 #if TARGET_HOST_UNIX_X11
349 * This code was repeated constantly, so here it goes into a definition:
351 # define GETWINDOW(a) window = fgWindowByHandle( event.a.window );if( window == NULL ) break;
352 # define GETMOUSE(a) window->State.MouseX = event.a.x; window->State.MouseY = event.a.y;
355 * Make sure the display has been created etc.
357 freeglut_assert_ready;
360 * Enter the loop. Iterate as long as there are
361 * any windows in the freeglut structure.
363 while( fgStructure.Windows != NULL )
366 * Do we have any event messages pending?
368 if( XPending( fgDisplay.Display ) )
371 * Grab the next event to be processed...
373 XNextEvent( fgDisplay.Display, &event );
376 * Check the event's type
382 * The window creation confirmation
388 * This is sent to confirm the XDestroyWindow call. Ignore it.
394 * Destroy the window when the WM_DELETE_WINDOW message arrives
396 if( (Atom) event.xclient.data.l[ 0 ] == fgDisplay.DeleteWindow )
399 * I wonder if the window still exists ;-)
401 fgDestroyWindow( fgWindowByHandle( event.xclient.window ), TRUE );
407 * A window of ours has been unmapped...
413 * We are too dumb to process partial exposes...
415 if( event.xexpose.count == 0 )
416 fghRedrawWindowByHandle( event.xexpose.window );
419 case ConfigureNotify:
421 * The window gets resized
423 fghReshapeWindowByHandle(
424 event.xconfigure.window,
425 event.xconfigure.width,
426 event.xconfigure.height
432 * Have the client's keyboard knowledge updated (xlib.ps,
433 * page 206, says that's a good thing to do)
435 XRefreshKeyboardMapping( (XMappingEvent *) &event );
438 case VisibilityNotify:
441 * The window's visiblity might have changed
443 GETWINDOW( xvisibility );
446 * Break now if no window status callback has been hooked to that window
448 if( window->Callbacks.WindowStatus == NULL )
452 * We're going to send a callback to a window. Make it current.
454 glutSetWindow( window->ID );
457 * Sending this event, the X server can notify us that the window has just
458 * acquired one of the three possible visibility states: VisibilityUnobscured,
459 * VisibilityPartiallyObscured or VisibilityFullyObscured
461 switch( event.xvisibility.state )
463 case VisibilityUnobscured:
465 * We are fully visible...
467 window->Callbacks.WindowStatus( GLUT_FULLY_RETAINED );
468 window->State.Visible = TRUE;
471 case VisibilityPartiallyObscured:
473 * The window is partially visible
475 window->Callbacks.WindowStatus( GLUT_PARTIALLY_RETAINED );
476 window->State.Visible = TRUE;
479 case VisibilityFullyObscured:
481 * The window is totally obscured
483 window->Callbacks.WindowStatus( GLUT_FULLY_COVERED );
484 window->State.Visible = FALSE;
493 * Mouse is over one of our windows
495 GETWINDOW( xcrossing ); GETMOUSE( xcrossing );
498 * Is there an entry callback hooked to the window?
500 if( window->Callbacks.Entry != NULL )
503 * Yeah. Notify the window about having the mouse cursor over
505 window->Callbacks.Entry( GLUT_ENTERED );
513 * Mouse is no longer over one of our windows
515 GETWINDOW( xcrossing ); GETMOUSE( xcrossing );
518 * Is there an entry callback hooked to the window?
520 if( window->Callbacks.Entry != NULL )
523 * Yeah. Notify the window about having the mouse cursor over
525 window->Callbacks.Entry( GLUT_LEFT );
533 * The mouse cursor was moved...
535 GETWINDOW( xmotion ); GETMOUSE( xmotion );
538 * What kind of a movement was it?
540 if( (event.xmotion.state & Button1Mask) || (event.xmotion.state & Button2Mask) ||
541 (event.xmotion.state & Button3Mask) || (event.xmotion.state & Button4Mask) ||
542 (event.xmotion.state & Button5Mask) )
545 * A mouse button was pressed during the movement...
546 * Is there a motion callback hooked to the window?
548 if( window->Callbacks.Motion != NULL )
551 * Yup. Have it executed immediately
553 window->Callbacks.Motion( event.xmotion.x, event.xmotion.y );
559 * Otherwise it was a passive movement...
561 if( window->Callbacks.Passive != NULL )
564 * That's right, and there is a passive callback, too.
566 window->Callbacks.Passive( event.xmotion.x, event.xmotion.y );
578 * A mouse button has been pressed or released. Traditionally,
579 * break if the window was found within the freeglut structures.
581 GETWINDOW( xbutton ); GETMOUSE( xbutton );
584 * GLUT API assumes that you can't have more than three mouse buttons, so:
586 switch( event.xbutton.button )
589 * WARNING: this might be wrong, if we only have two mouse buttons,
590 * Button2 might mean the right button, isn't that right?
592 case Button1: button = GLUT_LEFT_BUTTON; break;
593 case Button2: button = GLUT_MIDDLE_BUTTON; break;
594 case Button3: button = GLUT_RIGHT_BUTTON; break;
595 default: button = -1; break;
599 * Skip the unwanted mouse buttons...
605 * Do not execute the callback if a menu is hooked to this key.
606 * In that case an appropriate private call should be generated
608 if( window->Menu[ button ] != NULL )
611 * Set the current window
613 glutSetWindow( window->ID );
615 if( event.type == ButtonPress )
618 * Activate the appropriate menu structure...
620 fgActivateMenu( button );
625 * There are two general cases generated when a menu button
626 * is released -- it can provoke a menu call (when released
627 * over a menu area) or just deactivate the menu (when released
628 * somewhere else). Unfortunately, both cases must be checked
629 * recursively due to the submenu possibilities.
631 fgDeactivateMenu( button );
637 * Check if there is a mouse callback hooked to the window
639 if( window->Callbacks.Mouse == NULL )
643 * Set the current window
645 glutSetWindow( window->ID );
648 * Remember the current modifiers state
650 window->State.Modifiers = event.xbutton.state;
653 * Finally execute the mouse callback
655 window->Callbacks.Mouse(
657 event.type == ButtonPress ? GLUT_DOWN : GLUT_UP,
663 * Trash the modifiers state
665 window->State.Modifiers = 0xffffffff;
672 * A key has been pressed, find the window that had the focus:
674 GETWINDOW( xkey ); GETMOUSE( xkey );
677 * Is there a keyboard/special callback hooked for this window?
679 if( (window->Callbacks.Keyboard != NULL) || (window->Callbacks.Special != NULL) )
681 XComposeStatus composeStatus;
682 gchar asciiCode[ 32 ];
687 * Check for the ASCII/KeySym codes associated with the event:
689 len = XLookupString( &event.xkey, asciiCode, sizeof(asciiCode), &keySym, &composeStatus );
692 * Get ready to calling the keyboard/special callbacks
694 glutSetWindow( window->ID );
697 * GLUT API tells us to have two separate callbacks...
702 * ...one for the ASCII translateable keypresses...
704 if( window->Callbacks.Keyboard != NULL )
707 * Remember the current modifiers state
709 window->State.Modifiers = event.xkey.state;
712 * Execute the callback
714 window->Callbacks.Keyboard( asciiCode[ 0 ], event.xkey.x, event.xkey.y );
717 * Trash the modifiers state
719 window->State.Modifiers = 0xffffffff;
727 * ...and one for all the others, which need to be translated to GLUT_KEY_Xs...
732 * First the function keys come:
734 case XK_F1: special = GLUT_KEY_F1; break;
735 case XK_F2: special = GLUT_KEY_F2; break;
736 case XK_F3: special = GLUT_KEY_F3; break;
737 case XK_F4: special = GLUT_KEY_F4; break;
738 case XK_F5: special = GLUT_KEY_F5; break;
739 case XK_F6: special = GLUT_KEY_F6; break;
740 case XK_F7: special = GLUT_KEY_F7; break;
741 case XK_F8: special = GLUT_KEY_F8; break;
742 case XK_F9: special = GLUT_KEY_F9; break;
743 case XK_F10: special = GLUT_KEY_F10; break;
744 case XK_F11: special = GLUT_KEY_F11; break;
745 case XK_F12: special = GLUT_KEY_F12; break;
748 * Then the arrows and stuff:
750 case XK_Left: special = GLUT_KEY_LEFT; break;
751 case XK_Right: special = GLUT_KEY_RIGHT; break;
752 case XK_Up: special = GLUT_KEY_UP; break;
753 case XK_Down: special = GLUT_KEY_DOWN; break;
757 * Execute the callback (if one has been specified),
758 * given that the special code seems to be valid...
760 if( (window->Callbacks.Special != NULL) && (special != -1) )
763 * Remember the current modifiers state
765 window->State.Modifiers = event.xkey.state;
767 window->Callbacks.Special( special, event.xkey.x, event.xkey.y );
770 * Trash the modifiers state
772 window->State.Modifiers = 0xffffffff;
783 * Have all the timers checked.
788 * Poll the joystick and notify all windows that want to be notified...
790 fghCheckJoystickPolls();
793 * No messages in the queue, which means we are idling...
795 if( fgState.IdleCallback != NULL )
796 fgState.IdleCallback();
799 * Remember about displaying all the windows that have
800 * been marked for a redisplay (possibly in the idle call):
806 #elif TARGET_HOST_WIN32
808 gboolean bLoop = TRUE;
812 * The windows main loop is considerably smaller
816 if( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
819 * Grab the message now, checking for WM_QUIT
821 if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 )
825 * Translate virtual-key messages and send them to the window...
827 TranslateMessage( &stMsg );
828 DispatchMessage( &stMsg );
833 * Have all the timers checked.
838 * Poll the joystick and notify all windows that want to be notified...
840 fghCheckJoystickPolls();
843 * No messages in the queue, which means we are idling...
845 if( fgState.IdleCallback != NULL )
846 fgState.IdleCallback();
849 * Remember about displaying all the windows that have
850 * been marked for a redisplay (possibly in the idle call):
855 * We need to terminate the main loop if no windows are left
857 bLoop = (g_list_length( fgStructure.Windows ) != 0);
864 * When this loop terminates, destroy the display, state and structure
865 * of a freeglut session, so that another glutInit() call can happen
871 * The window procedure for handling Win32 events
873 #if TARGET_HOST_WIN32
874 LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
876 SFG_Window* window = fgWindowByHandle( hWnd );
880 # define assert_window_registered if( window == NULL ) return( DefWindowProc( hWnd, uMsg, wParam, lParam ) );
883 * Check what type of message are we receiving
889 * The window structure is passed as the creation structure paramter...
891 window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
892 g_assert( window != NULL );
895 * We can safely store the window's handle now:
897 window->Window.Handle = hWnd;
900 * Get the window's device context
902 window->Window.Device = GetDC( hWnd );
905 * Setup the pixel format of our window
907 fgSetupPixelFormat( window, FALSE );
910 * Create the OpenGL rendering context now
912 window->Window.Context = wglCreateContext( window->Window.Device );
915 * Still, we'll be needing to explicitly resize the window
917 window->State.NeedToResize = TRUE;
920 * Finally, have the window's device context released
922 ReleaseDC( window->Window.Handle, window->Window.Device );
927 * We got resized... But check if the window has been already added...
929 fghReshapeWindowByHandle( hWnd, LOWORD(lParam), HIWORD(lParam) );
934 * Start the painting job
936 BeginPaint( hWnd, &ps );
939 * Call the engine's main frame drawing method
941 fghRedrawWindowByHandle( hWnd );
944 * End the painting job, release the device context
946 EndPaint( hWnd, &ps );
951 * Make sure we don't close a window with current context active
953 if( fgStructure.Window == window )
955 wglMakeCurrent( NULL, NULL );
956 wglDeleteContext( window->Window.Context );
960 * Proceed with the window destruction
962 DestroyWindow( window->Window.Handle );
967 * The window already got destroyed, so forget about it's existence:
969 fgDestroyWindow( window, FALSE );
974 assert_window_registered;
977 * The mouse cursor has moved. Remember the new mouse cursor's position
979 window->State.MouseX = LOWORD( lParam );
980 window->State.MouseY = HIWORD( lParam );
983 * Fallback if there's an active menu hooked to this window
985 if( window->MenuActive[ 0 ] || window->MenuActive[ 1 ] || window->MenuActive[ 2 ] )
989 * Remember the current modifiers state.
991 window->State.Modifiers =
992 (GetKeyState( VK_LSHIFT ) || GetKeyState( VK_RSHIFT )) ? GLUT_ACTIVE_SHIFT : 0 |
993 (GetKeyState( VK_LCONTROL ) || GetKeyState( VK_RCONTROL )) ? GLUT_ACTIVE_CTRL : 0 |
994 (GetKeyState( VK_LMENU ) || GetKeyState( VK_RMENU )) ? GLUT_ACTIVE_ALT : 0;
997 * Check if any of the mouse buttons is pressed...
999 if( (wParam & MK_LBUTTON) || (wParam & MK_MBUTTON) || (wParam & MK_RBUTTON) )
1002 * Yeah, indeed. We need to use the motion callback then:
1004 if( window->Callbacks.Motion != NULL )
1007 * Make sure the current window is set...
1009 glutSetWindow( window->ID );
1012 * Execute the active mouse motion callback now
1014 window->Callbacks.Motion( window->State.MouseX, window->State.MouseY );
1020 * All mouse buttons are up, execute the passive mouse motion callback
1022 if( window->Callbacks.Passive != NULL )
1025 * Make sure the current window is set
1027 glutSetWindow( window->ID );
1030 * Execute the passive mouse motion callback
1032 window->Callbacks.Passive( window->State.MouseX, window->State.MouseY );
1037 * Thrash the current modifiers state now
1039 window->State.Modifiers = 0xffffffff;
1043 case WM_LBUTTONDOWN:
1044 case WM_MBUTTONDOWN:
1045 case WM_RBUTTONDOWN:
1050 gboolean pressed = TRUE;
1054 * A mouse button has been pressed *or* released. Again, break off
1055 * if the message was not directed towards a freeglut window...
1057 assert_window_registered;
1060 * The mouse cursor has moved. Remember the new mouse cursor's position
1062 window->State.MouseX = LOWORD( lParam );
1063 window->State.MouseY = HIWORD( lParam );
1066 * We're curious about the GLUT API button name...
1070 case WM_LBUTTONDOWN: pressed = TRUE; button = GLUT_LEFT_BUTTON; break;
1071 case WM_MBUTTONDOWN: pressed = TRUE; button = GLUT_MIDDLE_BUTTON; break;
1072 case WM_RBUTTONDOWN: pressed = TRUE; button = GLUT_RIGHT_BUTTON; break;
1073 case WM_LBUTTONUP: pressed = FALSE; button = GLUT_LEFT_BUTTON; break;
1074 case WM_MBUTTONUP: pressed = FALSE; button = GLUT_MIDDLE_BUTTON; break;
1075 case WM_RBUTTONUP: pressed = FALSE; button = GLUT_RIGHT_BUTTON; break;
1076 default: pressed = FALSE; button = -1; break;
1080 * The left and right mouse buttons might have been swapped...
1082 if( GetSystemMetrics( SM_SWAPBUTTON ) )
1083 if( button == GLUT_LEFT_BUTTON ) button = GLUT_RIGHT_BUTTON;
1084 else if( button == GLUT_RIGHT_BUTTON ) button = GLUT_LEFT_BUTTON;
1087 * Hey, what's up with you?
1090 return( DefWindowProc( hWnd, uMsg, lParam, wParam ) );
1093 * Do not execute the callback if a menu is hooked to this key.
1094 * In that case an appropriate private call should be generated
1096 if( window->Menu[ button ] != NULL )
1099 * Set the current window
1101 glutSetWindow( window->ID );
1103 if( pressed == TRUE )
1106 * Activate the appropriate menu structure...
1108 fgActivateMenu( button );
1113 * There are two general cases generated when a menu button
1114 * is released -- it can provoke a menu call (when released
1115 * over a menu area) or just deactivate the menu (when released
1116 * somewhere else). Unfortunately, both cases must be checked
1117 * recursively due to the submenu possibilities.
1119 fgDeactivateMenu( button );
1125 * Check if there is a mouse callback hooked to the window
1127 if( window->Callbacks.Mouse == NULL )
1131 * Set the current window
1133 glutSetWindow( window->ID );
1136 * Remember the current modifiers state.
1138 window->State.Modifiers =
1139 (GetKeyState( VK_LSHIFT ) || GetKeyState( VK_RSHIFT )) ? GLUT_ACTIVE_SHIFT : 0 |
1140 (GetKeyState( VK_LCONTROL ) || GetKeyState( VK_RCONTROL )) ? GLUT_ACTIVE_CTRL : 0 |
1141 (GetKeyState( VK_LMENU ) || GetKeyState( VK_RMENU )) ? GLUT_ACTIVE_ALT : 0;
1144 * Finally execute the mouse callback
1146 window->Callbacks.Mouse(
1148 pressed == TRUE ? GLUT_DOWN : GLUT_UP,
1149 window->State.MouseX,
1150 window->State.MouseY
1154 * Trash the modifiers state
1156 window->State.Modifiers = 0xffffffff;
1166 * First of all, make sure that there is a window to be notified of this
1168 assert_window_registered;
1171 * Ignore the automatic key repetition if needed:
1173 if( fgState.IgnoreKeyRepeat && (lParam & KF_REPEAT) )
1177 * Remember the current modifiers state. This is done here in order
1178 * to make sure the VK_DELETE keyboard callback is executed properly.
1180 window->State.Modifiers =
1181 (GetKeyState( VK_LSHIFT ) || GetKeyState( VK_RSHIFT )) ? GLUT_ACTIVE_SHIFT : 0 |
1182 (GetKeyState( VK_LCONTROL ) || GetKeyState( VK_RCONTROL )) ? GLUT_ACTIVE_CTRL : 0 |
1183 (GetKeyState( VK_LMENU ) || GetKeyState( VK_RMENU )) ? GLUT_ACTIVE_ALT : 0;
1186 * Convert the Win32 keystroke codes to GLUTtish way
1188 # define KEY(a,b) case a: keypress = b; break;
1193 * Most of the special characters can be handled automagically...
1195 KEY( VK_F1, GLUT_KEY_F1 ); KEY( VK_F2, GLUT_KEY_F2 );
1196 KEY( VK_F3, GLUT_KEY_F3 ); KEY( VK_F4, GLUT_KEY_F4 );
1197 KEY( VK_F5, GLUT_KEY_F5 ); KEY( VK_F6, GLUT_KEY_F6 );
1198 KEY( VK_F7, GLUT_KEY_F7 ); KEY( VK_F8, GLUT_KEY_F8 );
1199 KEY( VK_F9, GLUT_KEY_F9 ); KEY( VK_F10, GLUT_KEY_F10 );
1200 KEY( VK_F11, GLUT_KEY_F11 ); KEY( VK_F12, GLUT_KEY_F12 );
1201 KEY( VK_PRIOR, GLUT_KEY_PAGE_UP ); KEY( VK_NEXT, GLUT_KEY_PAGE_DOWN );
1202 KEY( VK_HOME, GLUT_KEY_HOME ); KEY( VK_END, GLUT_KEY_END );
1203 KEY( VK_LEFT, GLUT_KEY_LEFT ); KEY( VK_UP, GLUT_KEY_UP );
1204 KEY( VK_RIGHT, GLUT_KEY_RIGHT ); KEY( VK_DOWN, GLUT_KEY_DOWN );
1205 KEY( VK_INSERT, GLUT_KEY_INSERT );
1208 * ...yet there is a small exception we need to have handled...
1212 * The delete key should be treated as an ASCII keypress:
1214 if( window->Callbacks.Keyboard != NULL )
1215 window->Callbacks.Keyboard( 127, window->State.MouseX, window->State.MouseY );
1219 * Execute the special callback, if present, given the conversion was a success:
1221 if( (keypress != -1) && (window->Callbacks.Special != NULL) )
1224 * Have the special callback executed:
1226 window->Callbacks.Special( keypress, window->State.MouseX, window->State.MouseY );
1230 * Thrash the modifiers register now
1232 window->State.Modifiers = 0xffffffff;
1240 * First of all, make sure that there is a window to be notified of this
1242 assert_window_registered;
1245 * Ignore the automatic key repetition if needed:
1247 if( fgState.IgnoreKeyRepeat && (lParam & KF_REPEAT) )
1251 * Clear to go with the keyboard callback, if registered:
1253 if( window->Callbacks.Keyboard != NULL )
1256 * Remember the current modifiers state
1258 window->State.Modifiers =
1259 (GetKeyState( VK_LSHIFT ) || GetKeyState( VK_RSHIFT )) ? GLUT_ACTIVE_SHIFT : 0 |
1260 (GetKeyState( VK_LCONTROL ) || GetKeyState( VK_RCONTROL )) ? GLUT_ACTIVE_CTRL : 0 |
1261 (GetKeyState( VK_LMENU ) || GetKeyState( VK_RMENU )) ? GLUT_ACTIVE_ALT : 0;
1264 * Have the special callback executed:
1266 window->Callbacks.Keyboard( wParam, window->State.MouseX, window->State.MouseY );
1269 * Thrash the modifiers register now
1271 window->State.Modifiers = 0xffffffff;
1278 * Handle unhandled messages
1280 lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
1288 /*** END OF FILE ***/