+static LRESULT fghWindowProcKeyPress(SFG_Window *window, UINT uMsg, GLboolean keydown, WPARAM wParam, LPARAM lParam)
+{
+ static unsigned char lControl = 0, lShift = 0, lAlt = 0,
+ rControl = 0, rShift = 0, rAlt = 0;
+
+ int keypress = -1;
+
+ /* if keydown, check for repeat */
+ /* If repeat is globally switched off, it cannot be switched back on per window.
+ * But if it is globally switched on, it can be switched off per window. This matches
+ * GLUT's behavior on X11, but not Nate Robbins' win32 GLUT, as he didn't implement the
+ * global state switch.
+ */
+ if( keydown && ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE ) && (HIWORD(lParam) & KF_REPEAT) )
+ return 1;
+
+ /* Remember the current modifiers state so user can query it from their callback */
+ fgState.Modifiers = fgPlatformGetModifiers( );
+
+ /* Convert the Win32 keystroke codes to GLUTtish way */
+# define KEY(a,b) case a: keypress = b; break;
+
+ switch( wParam )
+ {
+ 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 );
+
+ /* handle control, alt and shift. For GLUT, we want to distinguish between left and right presses.
+ * The VK_L* & VK_R* left and right Alt, Ctrl and Shift virtual keys are however only used as parameters to GetAsyncKeyState() and GetKeyState()
+ * so when we get an alt, shift or control keypress here, we manually check whether it was the left or the right
+ */
+#define ASYNC_KEY_EVENT(winKey,glutKey,keyStateVar)\
+ if (!keyStateVar && GetAsyncKeyState ( winKey ))\
+ {\
+ keypress = glutKey;\
+ keyStateVar = 1;\
+ }\
+ else if (keyStateVar && !GetAsyncKeyState ( winKey ))\
+ {\
+ keypress = glutKey;\
+ keyStateVar = 0;\
+ }
+ case VK_CONTROL:
+ ASYNC_KEY_EVENT(VK_LCONTROL,GLUT_KEY_CTRL_L,lControl);
+ ASYNC_KEY_EVENT(VK_RCONTROL,GLUT_KEY_CTRL_R,rControl);
+ break;
+ case VK_SHIFT:
+ ASYNC_KEY_EVENT(VK_LSHIFT,GLUT_KEY_SHIFT_L,lShift);
+ ASYNC_KEY_EVENT(VK_RSHIFT,GLUT_KEY_SHIFT_R,rShift);
+ break;
+ case VK_MENU:
+ ASYNC_KEY_EVENT(VK_LMENU,GLUT_KEY_ALT_L,lAlt);
+ ASYNC_KEY_EVENT(VK_RMENU,GLUT_KEY_ALT_R,rAlt);
+ break;
+#undef ASYNC_KEY_EVENT
+
+ case VK_DELETE:
+ /* The delete key should be treated as an ASCII keypress: */
+ if (keydown)
+ INVOKE_WCB( *window, Keyboard,
+ ( 127, window->State.MouseX, window->State.MouseY )
+ );
+ else
+ INVOKE_WCB( *window, KeyboardUp,
+ ( 127, window->State.MouseX, window->State.MouseY )
+ );
+ break;
+
+#if !defined(_WIN32_WCE)
+ default:
+ /* keydown displayable characters are handled with WM_CHAR message, but no corresponding up is generated. So get that here. */
+ if (!keydown)
+ {
+ BYTE state[ 256 ];
+ WORD code[ 2 ];
+
+ GetKeyboardState( state );
+
+ if( ToAscii( (UINT)wParam, 0, state, code, 0 ) == 1 )
+ wParam=code[ 0 ];
+
+ INVOKE_WCB( *window, KeyboardUp,
+ ( (char)wParam,
+ window->State.MouseX, window->State.MouseY )
+ );
+ }
+#endif
+ }
+
+#if defined(_WIN32_WCE)
+ if(keydown && !(lParam & 0x40000000)) /* Prevent auto-repeat */
+ {
+ if(wParam==(unsigned)gxKeyList.vkRight)
+ keypress = GLUT_KEY_RIGHT;
+ else if(wParam==(unsigned)gxKeyList.vkLeft)
+ keypress = GLUT_KEY_LEFT;
+ else if(wParam==(unsigned)gxKeyList.vkUp)
+ keypress = GLUT_KEY_UP;
+ else if(wParam==(unsigned)gxKeyList.vkDown)
+ keypress = GLUT_KEY_DOWN;
+ else if(wParam==(unsigned)gxKeyList.vkA)
+ keypress = GLUT_KEY_F1;
+ else if(wParam==(unsigned)gxKeyList.vkB)
+ keypress = GLUT_KEY_F2;
+ else if(wParam==(unsigned)gxKeyList.vkC)
+ keypress = GLUT_KEY_F3;
+ else if(wParam==(unsigned)gxKeyList.vkStart)
+ keypress = GLUT_KEY_F4;
+ }
+#endif
+
+ if( keypress != -1 )
+ if (keydown)
+ INVOKE_WCB( *window, Special,
+ ( keypress,
+ window->State.MouseX, window->State.MouseY )
+ );
+ else
+ INVOKE_WCB( *window, SpecialUp,
+ ( keypress,
+ window->State.MouseX, window->State.MouseY )
+ );
+
+ fgState.Modifiers = INVALID_MODIFIERS;
+
+ /* SYSKEY events should be sent to default window proc for system to handle them */
+ if (uMsg==WM_SYSKEYDOWN || uMsg==WM_SYSKEYUP)
+ return DefWindowProc( window->Window.Handle, uMsg, wParam, lParam );
+ else
+ return 1;
+}
+
+void fghWindowUnderCursor(SFG_Window *window, SFG_Window **child_window)
+{
+ /* Check if the current window that the mouse is over is a child window
+ * of the window the message was sent to.
+ */
+ if (window && window->Children.First)
+ {
+ POINT mouse_pos;
+ SFG_WindowHandleType hwnd;
+ SFG_Window* temp_window;
+
+ /* Get mouse position at time of message */
+ DWORD mouse_pos_Dword = GetMessagePos();
+ mouse_pos.x = GET_X_LPARAM(mouse_pos_Dword);
+ mouse_pos.y = GET_Y_LPARAM(mouse_pos_Dword);
+ ScreenToClient( window->Window.Handle, &mouse_pos );
+
+ hwnd = ChildWindowFromPoint(window->Window.Handle, mouse_pos);
+ if (hwnd && hwnd!=window->Window.Handle) /* can be NULL if mouse outside parent by the time we get here, or can be same as parent if we didn't find a child */
+ {
+ temp_window = fgWindowByHandle(hwnd);
+ if (temp_window) /* Verify we got a FreeGLUT window */
+ {
+ *child_window = temp_window;
+ /* ChildWindowFromPoint only searches immediate children, so search again to see if actually in grandchild or further descendant */
+ fghWindowUnderCursor(temp_window,child_window);
+ }
+ }
+ }
+}
+