diff --git a/neo/framework/Common.cpp b/neo/framework/Common.cpp index 62bfabb4..21ab1960 100644 --- a/neo/framework/Common.cpp +++ b/neo/framework/Common.cpp @@ -86,6 +86,10 @@ idCVar preload_CommonAssets( "preload_CommonAssets", "1", CVAR_SYSTEM | CVAR_BOO idCVar net_inviteOnly( "net_inviteOnly", "1", CVAR_BOOL | CVAR_ARCHIVE, "whether or not the private server you create allows friends to join or invite only" ); +// DG: add cvar for pause +idCVar com_pause( "com_pause", "0", CVAR_BOOL | CVAR_SYSTEM , "set to 1 to pause game, to 0 to unpause again" ); +// DG end + extern idCVar g_demoMode; idCVar com_engineHz( "com_engineHz", "60", CVAR_FLOAT | CVAR_ARCHIVE, "Frames per second the engine runs at", 10.0f, 1024.0f ); diff --git a/neo/framework/common_frame.cpp b/neo/framework/common_frame.cpp index a1968f50..a63f0053 100644 --- a/neo/framework/common_frame.cpp +++ b/neo/framework/common_frame.cpp @@ -438,6 +438,8 @@ void idCommonLocal::ProcessGameReturn( const gameReturn_t& ret ) extern idCVar com_forceGenericSIMD; +extern idCVar com_pause; + /* ================= idCommonLocal::Frame @@ -485,13 +487,16 @@ void idCommonLocal::Frame() // if the console or another gui is down, we don't need to hold the mouse cursor bool chatting = false; + // DG: Add pause from com_pause cvar // RB begin #if defined(USE_DOOMCLASSIC) - if( console->Active() || Dialog().IsDialogActive() || session->IsSystemUIShowing() || ( game && game->InhibitControls() && !IsPlayingDoomClassic() ) ) + if( console->Active() || Dialog().IsDialogActive() || session->IsSystemUIShowing() + || ( game && game->InhibitControls() && !IsPlayingDoomClassic() ) ) #else - if( console->Active() || Dialog().IsDialogActive() || session->IsSystemUIShowing() || ( game && game->InhibitControls() ) ) + if( com_pause.GetInteger() || console->Active() || Dialog().IsDialogActive() || session->IsSystemUIShowing() + || ( game && game->InhibitControls() ) ) #endif - // RB end + // RB end, DG end { Sys_GrabMouseCursor( false ); usercmdGen->InhibitUsercmd( INHIBIT_SESSION, true ); @@ -505,9 +510,16 @@ void idCommonLocal::Frame() // RB begin #if defined(USE_DOOMCLASSIC) - const bool pauseGame = ( !mapSpawned || ( !IsMultiplayer() && ( Dialog().IsDialogPausing() || session->IsSystemUIShowing() || ( game && game->Shell_IsActive() ) ) ) ) && !IsPlayingDoomClassic(); + const bool pauseGame = ( !mapSpawned + || ( !IsMultiplayer() + && ( Dialog().IsDialogPausing() || session->IsSystemUIShowing() + || ( game && game->Shell_IsActive() ) || com_pause.GetInteger() ) ) ) + && !IsPlayingDoomClassic(); #else - const bool pauseGame = ( !mapSpawned || ( !IsMultiplayer() && ( Dialog().IsDialogPausing() || session->IsSystemUIShowing() || ( game && game->Shell_IsActive() ) ) ) ); + const bool pauseGame = ( !mapSpawned + || ( !IsMultiplayer() + && ( Dialog().IsDialogPausing() || session->IsSystemUIShowing() + || ( game && game->Shell_IsActive() ) || com_pause.GetInteger() ) ) ); #endif // RB end diff --git a/neo/libs/zlib/minizip/zip.h b/neo/libs/zlib/minizip/zip.h index 477fe89b..925d0d96 100644 --- a/neo/libs/zlib/minizip/zip.h +++ b/neo/libs/zlib/minizip/zip.h @@ -47,7 +47,7 @@ extern "C" { //#define HAVE_BZIP2 #ifndef _ZLIB_H -#include "../../libs/zlib/zlib.h" +#include "../zlib.h" #endif #ifndef _ZLIBIOAPI_H diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp index c10b83e1..66dfcefc 100644 --- a/neo/renderer/RenderSystem_init.cpp +++ b/neo/renderer/RenderSystem_init.cpp @@ -53,7 +53,13 @@ idCVar r_skipIntelWorkarounds( "r_skipIntelWorkarounds", "0", CVAR_RENDERER | CV idCVar r_multiSamples( "r_multiSamples", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "number of antialiasing samples" ); idCVar r_vidMode( "r_vidMode", "0", CVAR_ARCHIVE | CVAR_RENDERER | CVAR_INTEGER, "fullscreen video mode number" ); idCVar r_displayRefresh( "r_displayRefresh", "0", CVAR_RENDERER | CVAR_INTEGER | CVAR_NOCHEAT, "optional display refresh rate option for vid mode", 0.0f, 240.0f ); +#ifdef WIN32 idCVar r_fullscreen( "r_fullscreen", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "0 = windowed, 1 = full screen on monitor 1, 2 = full screen on monitor 2, etc" ); +#else +// DG: add mode -2 for SDL, also defaulting to windowed mode, as that causes less trouble on linux +idCVar r_fullscreen( "r_fullscreen", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "-2 = use current monitor, -1 = (reserved), 0 = windowed, 1 = full screen on monitor 1, 2 = full screen on monitor 2, etc" ); +// DG end +#endif idCVar r_customWidth( "r_customWidth", "1280", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "custom screen width. set r_vidMode to -1 to activate" ); idCVar r_customHeight( "r_customHeight", "720", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "custom screen height. set r_vidMode to -1 to activate" ); idCVar r_windowX( "r_windowX", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "Non-fullscreen parameter" ); diff --git a/neo/sys/posix/posix_main.cpp b/neo/sys/posix/posix_main.cpp index ea460114..80ee0831 100644 --- a/neo/sys/posix/posix_main.cpp +++ b/neo/sys/posix/posix_main.cpp @@ -42,6 +42,7 @@ If you have questions concerning this license or the applicable additional terms #include #include #include +#include // RB begin #if defined(__ANDROID__) @@ -358,21 +359,23 @@ int Sys_ListFiles( const char* directory, const char* extension, idStrList& list list.Clear(); debug = cvarSystem->GetCVarBool( "fs_debug" ); - - // DG: handle "*" as special case that matches everything - // FIXME: handle * properly as a wildcase somewhere in the string? - if( !extension || ( extension[0] == '*' && extension[1] == '\0' ) ) - extension = ""; - // DG end + // DG: we use fnmatch for shell-style pattern matching + // so the pattern should at least contain "*" to match everything, + // the extension will be added behind that (if !dironly) + idStr pattern( "*" ); // passing a slash as extension will find directories if( extension[0] == '/' && extension[1] == 0 ) { - extension = ""; dironly = true; } + else + { + // so we have *, the same as in the windows code basically + pattern += extension; + } + // DG end - // search // NOTE: case sensitivity of directory path can screw us up here if( ( fdir = opendir( directory ) ) == NULL ) { @@ -399,8 +402,6 @@ int Sys_ListFiles( const char* directory, const char* extension, idStrList& list return 0; } - int extLen = idStr::Length( extension ); - while( readdir_r( fdir, entry, &d ) == 0 && d != NULL ) { // DG end @@ -410,14 +411,11 @@ int Sys_ListFiles( const char* directory, const char* extension, idStrList& list if( !dironly ) { // DG: the original code didn't work because d3 bfg abuses the extension - // to match whole filenames in the savegame-code, not just file extensions... - // the extension must be the last chars of the filename - // so start matching at startPos = strlen(d->d_name) - strlen(extension) - int startPos = idStr::Length( d->d_name ) - extLen; - // of course the extension can't match if it's longer than the filename, i.e. startPos < 0 - if( startPos < 0 || idStr::FindText( d->d_name, extension, true, startPos ) < 0 ) + // to match whole filenames and patterns in the savegame-code, not just file extensions... + // so just use fnmatch() which supports matching shell wildcard patterns ("*.foo" etc) + // if we should ever need case insensitivity, use FNM_CASEFOLD as third flag + if( fnmatch( pattern.c_str(), d->d_name, 0 ) != 0 ) continue; - // DG end } if( ( dironly && !( st.st_mode & S_IFDIR ) ) || diff --git a/neo/sys/posix/posix_session_local.cpp b/neo/sys/posix/posix_session_local.cpp index 404f4670..cca9802e 100644 --- a/neo/sys/posix/posix_session_local.cpp +++ b/neo/sys/posix/posix_session_local.cpp @@ -456,10 +456,9 @@ idSessionLocalWin::IsSystemUIShowing */ bool idSessionLocalWin::IsSystemUIShowing() const { - // RB: TODO track SDL_ACTIVEENT + // DG: pausing here when window is out of focus like originally done on windows is hacky + // it's done with com_pause now. return isSysUIShowing; - - //return !win32.activeApp || isSysUIShowing; // If the user alt+tabs away, treat it the same as bringing up the steam overlay } /* diff --git a/neo/sys/sdl/sdl_events.cpp b/neo/sys/sdl/sdl_events.cpp index 7fd4c890..e0e4ca21 100644 --- a/neo/sys/sdl/sdl_events.cpp +++ b/neo/sys/sdl/sdl_events.cpp @@ -5,6 +5,7 @@ Doom 3 GPL Source Code Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. Copyright (C) 2012 dhewg (dhewm3) Copyright (C) 2012 Robert Beckebans +Copyright (C) 2013 Daniel Gibson This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). @@ -28,9 +29,10 @@ If you have questions concerning this license or the applicable additional terms =========================================================================== */ +#include "../../idlib/precompiled.h" + #include -#include "../../idlib/precompiled.h" #include "renderer/tr_local.h" #include "sdl_local.h" #include "../posix/posix_public.h" @@ -58,6 +60,13 @@ If you have questions concerning this license or the applicable additional terms // DG end #endif +// DG: those are needed for moving/resizing windows +extern idCVar r_windowX; +extern idCVar r_windowY; +extern idCVar r_windowWidth; +extern idCVar r_windowHeight; +// DG end + const char* kbdNames[] = { "english", "french", "german", "italian", "spanish", "turkish", "norwegian", NULL @@ -732,28 +741,55 @@ sysEvent_t Sys_GetEvent() newmod |= KMOD_CAPS; SDL_SetModState( ( SDL_Keymod )newmod ); - // DG: disabling the cursor is now done once in GLimp_Init() because it should always be disabled - GLimp_GrabInput( GRAB_ENABLE | GRAB_REENABLE ); + + // DG: un-pause the game when focus is gained, that also re-grabs the input + // disabling the cursor is now done once in GLimp_Init() because it should always be disabled + cvarSystem->SetCVarBool( "com_pause", false ); // DG end break; } case SDL_WINDOWEVENT_FOCUS_LOST: - GLimp_GrabInput( 0 ); + // DG: pause the game when focus is lost, that also un-grabs the input + cvarSystem->SetCVarBool( "com_pause", true ); + // DG end break; - // TODO: SDL_WINDOWEVENT_RESIZED + // DG: handle resizing and moving of window + case SDL_WINDOWEVENT_RESIZED: + { + int w = ev.window.data1; + int h = ev.window.data2; + r_windowWidth.SetInteger( w ); + r_windowHeight.SetInteger( h ); + + glConfig.nativeScreenWidth = w; + glConfig.nativeScreenHeight = h; + break; + } + + case SDL_WINDOWEVENT_MOVED: + { + int x = ev.window.data1; + int y = ev.window.data2; + r_windowX.SetInteger( x ); + r_windowY.SetInteger( y ); + break; + } + // DG end } return res_none; #else case SDL_ACTIVEEVENT: { - int flags = 0; + // DG: (un-)pause the game when focus is gained, that also (un-)grabs the input + bool pause = true; if( ev.active.gain ) { - flags = GRAB_ENABLE | GRAB_REENABLE | GRAB_HIDECURSOR; + + pause = false; // unset modifier, in case alt-tab was used to leave window and ALT is still set // as that can cause fullscreen-toggling when pressing enter... @@ -765,23 +801,58 @@ sysEvent_t Sys_GetEvent() SDL_SetModState( ( SDLMod )newmod ); } - GLimp_GrabInput( flags ); + cvarSystem->SetCVarBool( "com_pause", pause ); } return res_none; case SDL_VIDEOEXPOSE: return res_none; -#endif + // DG: handle resizing and moving of window + case SDL_VIDEORESIZE: + { + int w = ev.resize.w; + int h = ev.resize.h; + r_windowWidth.SetInteger( w ); + r_windowHeight.SetInteger( h ); + + glConfig.nativeScreenWidth = w; + glConfig.nativeScreenHeight = h; + // for some reason this needs a vid_restart in SDL1 but not SDL2 so GLimp_SetScreenParms() is called + PushConsoleEvent( "vid_restart" ); + return res_none; + } + // DG end +#endif + case SDL_KEYDOWN: if( ev.key.keysym.sym == SDLK_RETURN && ( ev.key.keysym.mod & KMOD_ALT ) > 0 ) { - cvarSystem->SetCVarBool( "r_fullscreen", !renderSystem->IsFullScreen() ); + // DG: go to fullscreen on current display, instead of always first display + int fullscreen = 0; + if( ! renderSystem->IsFullScreen() ) + { + // this will be handled as "fullscreen on current window" + // r_fullscreen 1 means "fullscreen on first window" in d3 bfg + fullscreen = -2; + } + cvarSystem->SetCVarInteger( "r_fullscreen", fullscreen ); + // DG end PushConsoleEvent( "vid_restart" ); return res_none; } + // DG: ctrl-g to un-grab mouse - yeah, left ctrl shoots, then just use right ctrl :) + if( ev.key.keysym.sym == SDLK_g && ( ev.key.keysym.mod & KMOD_CTRL ) > 0 ) + { + bool grab = cvarSystem->GetCVarBool( "in_nograb" ); + grab = !grab; + cvarSystem->SetCVarBool( "in_nograb", grab ); + return res_none; + } + // DG end + // fall through case SDL_KEYUP: { diff --git a/neo/sys/sdl/sdl_glimp.cpp b/neo/sys/sdl/sdl_glimp.cpp index 556112c0..fee6c78f 100644 --- a/neo/sys/sdl/sdl_glimp.cpp +++ b/neo/sys/sdl/sdl_glimp.cpp @@ -5,6 +5,7 @@ Doom 3 GPL Source Code Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. Copyright (C) 2012 dhewg (dhewm3) Copyright (C) 2012 Robert Beckebans +Copyright (C) 2013 Daniel Gibson This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). @@ -54,6 +55,7 @@ static SDL_GLContext context = NULL; static SDL_Surface* window = NULL; #define SDL_WINDOW_OPENGL SDL_OPENGL #define SDL_WINDOW_FULLSCREEN SDL_FULLSCREEN +#define SDL_WINDOW_RESIZABLE SDL_RESIZABLE #endif bool QGL_Init( const char* dllname ); @@ -89,7 +91,9 @@ bool GLimp_Init( glimpParms_t parms ) GLimp_PreInit(); // DG: make sure SDL is initialized - Uint32 flags = SDL_WINDOW_OPENGL; + // DG: make window resizable + Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; + // DG end if( parms.fullScreen ) flags |= SDL_WINDOW_FULLSCREEN; @@ -198,10 +202,34 @@ bool GLimp_Init( glimpParms_t parms ) } // RB end + // DG: set display num for fullscreen + int windowPos = SDL_WINDOWPOS_UNDEFINED; + if( parms.fullScreen > 0 ) + { + if( parms.fullScreen > SDL_GetNumVideoDisplays() ) + { + common->Warning( "Couldn't set display to num %i because we only have %i displays", + parms.fullScreen, SDL_GetNumVideoDisplays() ); + } + else + { + // -1 because SDL starts counting displays at 0, while parms.fullScreen starts at 1 + windowPos = SDL_WINDOWPOS_UNDEFINED_DISPLAY( ( parms.fullScreen - 1 ) ); + } + } + // TODO: if parms.fullScreen == -1 there should be a borderless window spanning multiple displays + /* + * NOTE that this implicitly handles parms.fullScreen == -2 (from r_fullscreen -2) meaning + * "do fullscreen, but I don't care on what monitor", at least on my box it's the monitor with + * the mouse cursor. + */ + + window = SDL_CreateWindow( GAME_NAME, - SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, + windowPos, + windowPos, parms.width, parms.height, flags ); + // DG end context = SDL_GL_CreateContext( window ); if( !window ) @@ -273,6 +301,105 @@ bool GLimp_Init( glimpParms_t parms ) return true; } +/* +=================== + Helper functions for GLimp_SetScreenParms() +=================== +*/ + +#if SDL_VERSION_ATLEAST(2, 0, 0) +// SDL1 doesn't support multiple displays, so the source is much shorter and doesn't need seperate functions +// makes sure the window will be full-screened on the right display and returns the SDL display index +static int ScreenParmsHandleDisplayIndex( glimpParms_t parms ) +{ + int displayIdx; + if( parms.fullScreen > 0 ) + { + displayIdx = parms.fullScreen - 1; // first display for SDL is 0, in parms it's 1 + } + else // -2 == use current display + { + displayIdx = SDL_GetWindowDisplay( window ); + if( displayIdx < 0 ) // for some reason the display for the window couldn't be detected + displayIdx = 0; + } + + if( parms.fullScreen > SDL_GetNumVideoDisplays() ) + { + common->Warning( "Can't set fullscreen mode to display number %i, because SDL2 only knows about %i displays!", + parms.fullScreen, SDL_GetNumVideoDisplays() ); + return -1; + } + + if( parms.fullScreen != glConfig.isFullscreen ) + { + // we have to switch to another display + if( glConfig.isFullscreen ) + { + // if we're already in fullscreen mode but want to switch to another monitor + // we have to go to windowed mode first to move the window.. SDL-oddity. + SDL_SetWindowFullscreen( window, SDL_FALSE ); + } + // select display ; SDL_WINDOWPOS_UNDEFINED_DISPLAY() doesn't work. + int x = SDL_WINDOWPOS_CENTERED_DISPLAY( displayIdx ); + // move window to the center of selected display + SDL_SetWindowPosition( window, x, x ); + } + return displayIdx; +} + +static bool SetScreenParmsFullscreen( glimpParms_t parms ) +{ + SDL_DisplayMode m = {0}; + int displayIdx = ScreenParmsHandleDisplayIndex( parms ); + if( displayIdx < 0 ) + return false; + + // get current mode of display the window should be full-screened on + SDL_GetCurrentDisplayMode( displayIdx, &m ); + + // change settings in that display mode according to parms + // FIXME: check if refreshrate, width and height are supported? + // m.refresh_rate = parms.displayHz; + m.w = parms.width; + m.h = parms.height; + + // set that displaymode + if( SDL_SetWindowDisplayMode( window, &m ) < 0 ) + { + common->Warning( "Couldn't set window mode for fullscreen, reason: %s", SDL_GetError() ); + return false; + } + + // if we're currently not in fullscreen mode, we need to switch to fullscreen + if( !( SDL_GetWindowFlags( window ) & SDL_WINDOW_FULLSCREEN ) ) + { + if( SDL_SetWindowFullscreen( window, SDL_TRUE ) < 0 ) + { + common->Warning( "Couldn't switch to fullscreen mode, reason: %s!", SDL_GetError() ); + return false; + } + } + return true; +} + +static bool SetScreenParmsWindowed( glimpParms_t parms ) +{ + SDL_SetWindowSize( window, parms.width, parms.height ); + SDL_SetWindowPosition( window, parms.x, parms.y ); + + // if we're currently in fullscreen mode, we need to disable that + if( SDL_GetWindowFlags( window ) & SDL_WINDOW_FULLSCREEN ) + { + if( SDL_SetWindowFullscreen( window, SDL_FALSE ) < 0 ) + { + common->Warning( "Couldn't switch to windowed mode, reason: %s!", SDL_GetError() ); + return false; + } + } + return true; +} +#endif // SDL_VERSION_ATLEAST(2, 0, 0) /* =================== @@ -281,7 +408,63 @@ GLimp_SetScreenParms */ bool GLimp_SetScreenParms( glimpParms_t parms ) { - common->DPrintf( "TODO: GLimp_SetScreenParms\n" ); +#if SDL_VERSION_ATLEAST(2, 0, 0) + if( parms.fullScreen > 0 || parms.fullScreen == -2 ) + { + if( !SetScreenParmsFullscreen( parms ) ) + return false; + } + else if( parms.fullScreen == 0 ) // windowed mode + { + if( !SetScreenParmsWindowed( parms ) ) + return false; + } + else + { + common->Warning( "GLimp_SetScreenParms: fullScreen -1 (borderless window for multiple displays) currently unsupported!" ); + return false; + } +#else // SDL 1.2 - so much shorter, but doesn't handle multiple displays + SDL_Surface* s = SDL_GetVideoSurface(); + if( s == NULL ) + { + common->Warning( "GLimp_SetScreenParms: Couldn't get video information, reason: %s", SDL_GetError() ); + return false; + } + + + int bitsperpixel = 24; + if( s->format ) + bitsperpixel = s->format->BitsPerPixel; + + Uint32 flags = s->flags; + + if( parms.fullScreen ) + flags |= SDL_FULLSCREEN; + else + flags &= ~SDL_FULLSCREEN; + + s = SDL_SetVideoMode( parms.width, parms.height, bitsperpixel, flags ); + if( s == NULL ) + { + common->Warning( "GLimp_SetScreenParms: Couldn't set video information, reason: %s", SDL_GetError() ); + return false; + } +#endif // SDL_VERSION_ATLEAST(2, 0, 0) + + // Note: the following stuff would also work with SDL1.2 + SDL_GL_SetAttribute( SDL_GL_STEREO, parms.stereo ? 1 : 0 ); + + SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, parms.multiSamples ? 1 : 0 ); + SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, parms.multiSamples ); + + glConfig.isFullscreen = parms.fullScreen; + glConfig.isStereoPixelFormat = parms.stereo; + glConfig.nativeScreenWidth = parms.width; + glConfig.nativeScreenHeight = parms.height; + glConfig.displayFrequency = parms.displayHz; + glConfig.multisamples = parms.multiSamples; + return true; } diff --git a/neo/sys/win32/win_session_local.cpp b/neo/sys/win32/win_session_local.cpp index 3b175eb7..b76b065b 100644 --- a/neo/sys/win32/win_session_local.cpp +++ b/neo/sys/win32/win_session_local.cpp @@ -456,7 +456,9 @@ idSessionLocalWin::IsSystemUIShowing */ bool idSessionLocalWin::IsSystemUIShowing() const { - return !win32.activeApp || isSysUIShowing; // If the user alt+tabs away, treat it the same as bringing up the steam overlay + // DG: wtf, !win32.activeApp doesn't belong here, this is totally confusing and hacky. + // pause (when losing focus or invoking explicitly) is now handled properly by com_pause + return isSysUIShowing; } /* diff --git a/neo/sys/win32/win_wndproc.cpp b/neo/sys/win32/win_wndproc.cpp index 680b467e..5eec81eb 100644 --- a/neo/sys/win32/win_wndproc.cpp +++ b/neo/sys/win32/win_wndproc.cpp @@ -288,6 +288,10 @@ LONG WINAPI MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) // start playing the game sound world soundSystem->SetMute( !win32.activeApp ); + // DG: set com_pause so game pauses when focus is lost + // and continues when focus is regained + cvarSystem->SetCVarBool( "com_pause", !win32.activeApp ); + // DG end // we do not actually grab or release the mouse here, // that will be done next time through the main loop