X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?a=blobdiff_plain;f=src%2Fmswin%2Ffg_main_mswin.c;h=4ee9008759d8800f3a64ebe069ce7b37336d7f04;hb=29ce77740209818d54bdea174d193307314f4084;hp=7cd993e6fceec6d135fb1e3b377ab174110eff87;hpb=fb6675ecf8a5b341385a83563854b54ce58ecbb1;p=freeglut diff --git a/src/mswin/fg_main_mswin.c b/src/mswin/fg_main_mswin.c index 7cd993e..4ee9008 100644 --- a/src/mswin/fg_main_mswin.c +++ b/src/mswin/fg_main_mswin.c @@ -128,33 +128,51 @@ void fgPlatformProcessSingleEvent ( void ) -void fgPlatformMainLoopPreliminaryWork ( void ) +static void fghUpdateWindowStatus(SFG_Window *window, GLboolean visState) { - SFG_Window *window = (SFG_Window *)fgStructure.Windows.First ; - - /* - * 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 ) + SFG_Window* child; + + if (window->State.Visible != visState) { - if ( FETCH_WCB( *window, Visibility ) ) - { - SFG_Window *current_window = fgStructure.CurrentWindow ; + window->State.Visible = visState; + /* On win32 we only have two states, window displayed and window not displayed (iconified) + * We map these to GLUT_FULLY_RETAINED and GLUT_HIDDEN respectively. + */ + INVOKE_WCB( *window, WindowStatus, ( visState ? GLUT_FULLY_RETAINED:GLUT_HIDDEN ) ); - INVOKE_WCB( *window, Visibility, ( window->State.Visible ) ); - fgSetWindow( current_window ); + /* If top level window (not a subwindow/child), and icon title text available, switch titles based on visibility state */ + if (!window->Parent && window->State.pWState.IconTitle) + { + if (visState) + /* visible, set window title */ + SetWindowText( window->Window.Handle, window->State.pWState.WindowTitle ); + else + /* not visible, set icon title */ + SetWindowText( window->Window.Handle, window->State.pWState.IconTitle ); } + } - window = (SFG_Window *)window->Node.Next ; + /* Also set visibility state for children */ + for( child = ( SFG_Window * )window->Children.First; + child; + child = ( SFG_Window * )child->Node.Next ) + { + fghUpdateWindowStatus(child, visState); } } +void fghNotifyWindowStatus(SFG_Window *window) +{ + INVOKE_WCB( *window, WindowStatus, ( window->State.Visible?GLUT_FULLY_RETAINED:GLUT_HIDDEN ) ); + + /* Don't notify children, they get their own just before first time they're drawn */ +} + +void fgPlatformMainLoopPreliminaryWork ( void ) +{ + /* no-op */ +} + /* * Determine a GLUT modifier mask based on MS-WINDOWS system info. @@ -321,37 +339,37 @@ static LRESULT fghWindowProcKeyPress(SFG_Window *window, UINT uMsg, GLboolean ke return 1; } -void fghWindowUnderCursor(SFG_Window *window, SFG_Window **child_window) +static SFG_Window* fghWindowUnderCursor(SFG_Window *window) { /* Check if the current window that the mouse is over is a child window * of the window the message was sent to. Some events only sent to main window, * and when handling some messages, we need to make sure that we process * callbacks on the child window instead. This mirrors how GLUT does things. + * returns either the original window or the found child. */ - if (window && window->Children.First) + if (window && window->Children.First) /* This window has childs */ { - POINT mouse_pos; - SFG_WindowHandleType hwnd; - SFG_Window* temp_window; + HWND hwnd; + SFG_Window* child_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); + DWORD mouse_pos_dw = GetMessagePos(); + POINT mouse_pos = {GET_X_LPARAM(mouse_pos_dw), GET_Y_LPARAM(mouse_pos_dw)}; 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 = fgWindowByHandle(hwnd); + if (child_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); + window = fghWindowUnderCursor(child_window); } } } + + return window; } /* @@ -361,6 +379,7 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR { SFG_Window *window; LRESULT lRet = 1; + static int setCaptureActive = 0; FREEGLUT_INTERNAL_ERROR_EXIT_IF_NOT_INITIALISED ( "Event Handler" ) ; @@ -465,11 +484,15 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR break; case WM_SIZE: - /* - * If the window is visible, then it is the user manually resizing it. - * If it is not, then it is the system sending us a dummy resize with - * zero dimensions on a "glutIconifyWindow" call. - */ + /* printf("WM_SIZE (ID: %i): wParam: %i, new size: %ix%i \n",window->ID,wParam,LOWORD(lParam),HIWORD(lParam)); */ + + /* Update visibility state of the window */ + if (wParam==SIZE_MINIMIZED) + fghUpdateWindowStatus(window,GL_FALSE); + else if (wParam==SIZE_RESTORED && !window->State.Visible) + fghUpdateWindowStatus(window,GL_TRUE); + + /* Check window visible, we don't want to resize when the user or glutIconifyWindow minimized the window */ if( window->State.Visible ) { /* get old values first to compare to below */ @@ -487,29 +510,44 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR window->State.NeedToResize = GL_TRUE; } + /* according to docs, should return 0 */ + lRet = 0; break; case WM_MOVE: { SFG_Window* saved_window = fgStructure.CurrentWindow; RECT windowRect; - GetWindowRect( window->Window.Handle, &windowRect ); - - if (window->Parent) + + /* Check window is minimized, we don't want to call the position callback when the user or glutIconifyWindow minimized the window */ + if (!IsIconic(window->Window.Handle)) { - /* For child window, we should return relative to upper-left - * of parent's client area. - */ - POINT topleft = {windowRect.left,windowRect.top}; - - ScreenToClient(window->Parent->Window.Handle,&topleft); - windowRect.left = topleft.x; - windowRect.top = topleft.y; - } + /* Get top-left of non-client area of window, matching coordinates of + * glutInitPosition and glutPositionWindow, but not those of + * glutGet(GLUT_WINDOW_X) and glutGet(GLUT_WINDOW_Y), which return + * top-left of client area. + */ + GetWindowRect( window->Window.Handle, &windowRect ); + + if (window->Parent) + { + /* For child window, we should return relative to upper-left + * of parent's client area. + */ + POINT topleft = {windowRect.left,windowRect.top}; - INVOKE_WCB( *window, Position, ( windowRect.left, windowRect.top ) ); - fgSetWindow(saved_window); + ScreenToClient(window->Parent->Window.Handle,&topleft); + windowRect.left = topleft.x; + windowRect.top = topleft.y; + } + + INVOKE_WCB( *window, Position, ( windowRect.left, windowRect.top ) ); + fgSetWindow(saved_window); + } } + + /* according to docs, should return 0 */ + lRet = 0; break; case WM_SETFOCUS: @@ -518,18 +556,14 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); SetActiveWindow( window->Window.Handle ); - INVOKE_WCB( *window, Entry, ( GLUT_ENTERED ) ); UpdateWindow ( hWnd ); break; case WM_KILLFOCUS: { - SFG_Window* saved_window = fgStructure.CurrentWindow; /* printf("WM_KILLFOCUS: %p\n", window ); */ lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); - INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) ); - fgSetWindow(saved_window); /* Check if there are any open menus that need to be closed */ fgPlatformCheckMenuDeactivate(); @@ -538,7 +572,7 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR #if 0 case WM_ACTIVATE: - //printf("WM_ACTIVATE: %x %d %d\n",lParam, HIWORD(wParam), LOWORD(wParam)); + //printf("WM_ACTIVATE: %x (ID: %i) %d %d\n",lParam, window->ID, HIWORD(wParam), LOWORD(wParam)); if (LOWORD(wParam) != WA_INACTIVE) { /* printf("WM_ACTIVATE: fgSetCursor( %p, %d)\n", window, @@ -553,25 +587,85 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR case WM_SETCURSOR: /* printf ( "Cursor event %x %x %x %x\n", window, window->State.Cursor, lParam, wParam ) ; */ if( LOWORD( lParam ) == HTCLIENT ) - fgSetCursor ( window, window->State.Cursor ) ; + { + if (!window->State.pWState.MouseTracking) + { + TRACKMOUSEEVENT tme; + + /* Cursor just entered window, set cursor look */ + fgSetCursor ( window, window->State.Cursor ) ; + + /* If an EntryFunc callback is specified by the user, also + * invoke that callback and start mouse tracking so that + * we get a WM_MOUSELEAVE message + */ + if (FETCH_WCB( *window, Entry )) + { + SFG_Window* saved_window = fgStructure.CurrentWindow; + INVOKE_WCB( *window, Entry, ( GLUT_ENTERED ) ); + fgSetWindow(saved_window); + + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = window->Window.Handle; + TrackMouseEvent(&tme); + + window->State.pWState.MouseTracking = GL_TRUE; + } + } + } else + /* Only pass non-client WM_SETCURSOR to DefWindowProc, or we get WM_SETCURSOR on parents of children as well */ lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); break; + case WM_MOUSELEAVE: + { + /* NB: This message is only received when a EntryFunc callback + * is specified by the user, as that is the only condition under + * which mouse tracking is setup in WM_SETCURSOR handler above + */ + SFG_Window* saved_window = fgStructure.CurrentWindow; + INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) ); + fgSetWindow(saved_window); + + window->State.pWState.MouseTracking = GL_FALSE; + lRet = 0; /* As per docs, must return zero */ + } + break; + case WM_SHOWWINDOW: - window->State.Visible = GL_TRUE; - window->State.Redisplay = GL_TRUE; + /* printf("WM_SHOWWINDOW, shown? %i, source: %i\n",wParam,lParam); */ + if (wParam) + { + fghUpdateWindowStatus(window, GL_TRUE); + window->State.Redisplay = GL_TRUE; + } + else + { + fghUpdateWindowStatus(window, GL_FALSE); + window->State.Redisplay = GL_FALSE; + } break; case WM_PAINT: { - PAINTSTRUCT ps; - /* Turn on the visibility in case it was turned off somehow */ - window->State.Visible = GL_TRUE; - InvalidateRect( hWnd, NULL, GL_FALSE ); /* Make sure whole window is repainted. Bit of a hack, but a safe one from what google turns up... */ - BeginPaint( hWnd, &ps ); - fghRedrawWindow( window ); - EndPaint( hWnd, &ps ); + RECT rect; + + if (GetUpdateRect(hWnd,&rect,FALSE)) + { + /* As per docs, upon receiving WM_PAINT, first check if the update region is not empty before you call BeginPaint */ + PAINTSTRUCT ps; + + /* Turn on the visibility in case it was turned off somehow */ + window->State.Visible = GL_TRUE; + + InvalidateRect( hWnd, NULL, GL_FALSE ); + BeginPaint( hWnd, &ps ); + fghRedrawWindow( window ); + EndPaint( hWnd, &ps ); + } + lRet = 0; /* As per docs, should return 0 */ } break; @@ -589,12 +683,13 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR case WM_MOUSEMOVE: { + /* Per docs, use LOWORD/HIWORD for WinCE and GET_X_LPARAM/GET_Y_LPARAM for desktop windows */ #if defined(_WIN32_WCE) - window->State.MouseX = 320-HIWORD( lParam ); + window->State.MouseX = 320-HIWORD( lParam ); /* XXX: Docs say x should be loword and y hiword? */ window->State.MouseY = LOWORD( lParam ); #else - window->State.MouseX = LOWORD( lParam ); - window->State.MouseY = HIWORD( lParam ); + window->State.MouseX = GET_X_LPARAM( lParam ); + window->State.MouseY = GET_Y_LPARAM( lParam ); #endif /* defined(_WIN32_WCE) */ /* Restrict to [-32768, 32767] to match X11 behaviour */ /* See comment in "freeglut_developer" mailing list 10/4/04 */ @@ -632,12 +727,13 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR GLboolean pressed = GL_TRUE; int button; + /* Per docs, use LOWORD/HIWORD for WinCE and GET_X_LPARAM/GET_Y_LPARAM for desktop windows */ #if defined(_WIN32_WCE) - window->State.MouseX = 320-HIWORD( lParam ); + window->State.MouseX = 320-HIWORD( lParam ); /* XXX: Docs say x should be loword and y hiword? */ window->State.MouseY = LOWORD( lParam ); #else - window->State.MouseX = LOWORD( lParam ); - window->State.MouseY = HIWORD( lParam ); + window->State.MouseX = GET_X_LPARAM( lParam ); + window->State.MouseY = GET_Y_LPARAM( lParam ); #endif /* defined(_WIN32_WCE) */ /* Restrict to [-32768, 32767] to match X11 behaviour */ @@ -700,17 +796,20 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR window->State.MouseX, window->State.MouseY ) ) break; - /* Set capture so that the window captures all the mouse messages */ - /* - * XXX - Multiple button support: Under X11, the mouse is not released - * XXX - from the window until all buttons have been released, even if the - * XXX - user presses a button in another window. This will take more - * XXX - code changes than I am up to at the moment (10/5/04). The present - * XXX - is a 90 percent solution. + /* Set capture so that the window captures all the mouse messages + * + * The mouse is not released from the window until all buttons have + * been released, even if the user presses a button in another window. + * This is consistent with the behavior on X11. */ if ( pressed == GL_TRUE ) - SetCapture ( window->Window.Handle ) ; - else + { + if (!setCaptureActive) + SetCapture ( window->Window.Handle ) ; + setCaptureActive = 1; /* Set to false in WM_CAPTURECHANGED handler */ + } + else if (!GetAsyncKeyState(VK_LBUTTON) && !GetAsyncKeyState(VK_MBUTTON) && !GetAsyncKeyState(VK_RBUTTON)) + /* Make sure all mouse buttons are released before releasing capture */ ReleaseCapture () ; if( ! FETCH_WCB( *window, Mouse ) ) @@ -729,18 +828,32 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR ); fgState.Modifiers = INVALID_MODIFIERS; + + /* As per docs, should return zero */ + lRet = 0; } break; case WM_MOUSEWHEEL: { - SFG_Window *child_window = NULL; - int wheel_number = LOWORD( wParam ); - short ticks = ( short )HIWORD( wParam ); + int wheel_number = 0; /* Only one scroll wheel on windows */ +#if defined(_WIN32_WCE) + int modkeys = LOWORD(wParam); + short ticks = (short)HIWORD(wParam); + /* commented out as should not be needed here, mouse motion is processed in WM_MOUSEMOVE first: + xPos = LOWORD(lParam); -- straight from docs, not consistent with mouse nutton and mouse motion above (which i think is wrong) + yPos = HIWORD(lParam); + */ +#else + /* int modkeys = GET_KEYSTATE_WPARAM( wParam ); */ + short ticks = GET_WHEEL_DELTA_WPARAM( wParam ); + /* commented out as should not be needed here, mouse motion is processed in WM_MOUSEMOVE first: + window->State.MouseX = GET_X_LPARAM( lParam ); + window->State.MouseY = GET_Y_LPARAM( lParam ); + */ +#endif /* defined(_WIN32_WCE) */ - fghWindowUnderCursor(window, &child_window); - if (child_window) - window = child_window; + window = fghWindowUnderCursor(window); fgState.MouseWheelTicks += ticks; if ( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA ) @@ -791,34 +904,31 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR fgState.Modifiers = INVALID_MODIFIERS; } + /* Per docs, should return zero */ + lRet = 0; } break ; case WM_SYSKEYDOWN: case WM_KEYDOWN: { - SFG_Window *child_window = NULL; - fghWindowUnderCursor(window, &child_window); - lRet = fghWindowProcKeyPress(child_window?child_window:window,uMsg,GL_TRUE,wParam,lParam); + window = fghWindowUnderCursor(window); + lRet = fghWindowProcKeyPress(window,uMsg,GL_TRUE,wParam,lParam); } break; case WM_SYSKEYUP: case WM_KEYUP: { - SFG_Window *child_window = NULL; - fghWindowUnderCursor(window, &child_window); - lRet = fghWindowProcKeyPress(child_window?child_window:window,uMsg,GL_FALSE,wParam,lParam); + window = fghWindowUnderCursor(window); + lRet = fghWindowProcKeyPress(window,uMsg,GL_FALSE,wParam,lParam); } break; case WM_SYSCHAR: case WM_CHAR: { - SFG_Window *child_window = NULL; - fghWindowUnderCursor(window, &child_window); - if (child_window) - window = child_window; + window = fghWindowUnderCursor(window); if( (fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE) && (HIWORD(lParam) & KF_REPEAT) ) break; @@ -833,34 +943,13 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR break; case WM_CAPTURECHANGED: - /* User has finished resizing the window, force a redraw */ - INVOKE_WCB( *window, 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 ); + if (!lParam || !fgWindowByHandle((HWND)lParam)) + /* Capture released or capture taken by non-FreeGLUT window */ + setCaptureActive = 0; + /* Docs advise a redraw */ + InvalidateRect( hWnd, NULL, GL_FALSE ); + UpdateWindow(hWnd); + lRet = 0; /* Per docs, should return zero */ break; #if !defined(_WIN32_WCE) @@ -871,12 +960,6 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR /* 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; - case WM_SYSCOMMAND : /* 0x0112 */ { /* @@ -899,8 +982,7 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR case SC_MINIMIZE : /* User has clicked on the "-" to minimize the window */ - /* Turn off the visibility */ - window->State.Visible = GL_FALSE ; + /* Turning off the visibility is handled in WM_SIZE handler */ break ;