/******************************Module*Header*******************************\ * Module Name: glscrnsv.c * * Companion file to scrnsave.c. Hooks out any changes in functionality * defined as GL_SCRNSAVE in scrnsave.c, and does general intialization. * * Copyright (c) 1996 Microsoft Corporation * \**************************************************************************/ #include #include #include #include "scrnsave.h" #include "glscrnsv.h" #include "ssintrnl.hxx" #include "sswindow.hxx" #include "glscrnsv.hxx" #include "sswproc.hxx" static UINT (*KeyDownFunc)(int) = NULL; // Global ptr to screen saver instance SCRNSAVE *gpss = NULL; // Global strings. #define GEN_STRING_SIZE 64 TCHAR szScreenSaverTitle[GEN_STRING_SIZE]; extern TCHAR szClassName[]; // from sswindow.cxx LPCTSTR pszWindowClass = TEXT("WindowsScreenSaverClass"); // main class name TCHAR szClassName[] = TEXT("ScreenSaverClass"); // child class name // forward declarations of internal fns static BOOL RegisterMainClass( WNDPROC wndProc, HBRUSH hbrBg, HCURSOR hCursor ); static BOOL RegisterChildClass(); static BOOL AttemptResolutionSwitch( int width, int height, ISIZE *pNewSize ); // externs extern void InitRealScreenSave(); // scrnsave.cxx extern LRESULT WINAPI RealScreenSaverProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); extern VOID UnloadPwdDLL(VOID); extern BOOL GLScreenSaverConfigureDialog( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ); // sswproc.cxx #ifdef SS_INITIAL_CLEAR static void InitialClear( PSSW *pssw ); #endif #ifdef SS_WIN95 #define SS_WIN95_MULTIWINDOW_WORKAROUND 1 #define SS_WIN95_DESKTOP_REDRAW_FIX 1 static BOOL bPowerSaveEnabled(); #endif /**************************************************************************\ * GLDoScreenSave * * Runs the screen saver in the specified mode * * GL version of DoScreenSave in scrnsave.c * * Does basic init, creates initial set of windows, and starts the message * loop, which runs until terminated by some event. * \**************************************************************************/ static int GLDoScreenSave( int winType, LPARAM lParam ) { MSG msg; // Create screen saver instance - this calls ss_Init() SCRNSAVE ss( winType, lParam ); // Setup all the windows and start the message loop if( ss.SetupInitialWindows() ) { // Send message to main window to start the drawing timer #ifdef SS_DELAYED_START_KLUGE // Kluge to work around 'window-not-ready' problem in child // preview mode - trigger off of WM_PAINT instead if( ! SS_DELAY_START(winType) ) SendMessage( ss.psswMain->hwnd, SS_WM_START, 0, 0 ); #else SendMessage( ss.psswMain->hwnd, SS_WM_START, 0, 0 ); #endif // SS_DELAYED_START_KLUGE while( GetMessage( &msg, NULL, 0, 0 ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } } // We're done - screen saver exiting. // free password-handling DLL if loaded UnloadPwdDLL(); return msg.wParam; } /**************************************************************************\ * SCRNSAVE constructors * \**************************************************************************/ SCRNSAVE::SCRNSAVE( int typeArg, LPARAM lParam ) { type = typeArg; initParam = lParam; Init(); } SCRNSAVE::SCRNSAVE( int typeArg ) { type = typeArg; initParam = 0; Init(); } void SCRNSAVE::Init() { psswMain = NULL; psswGL = NULL; bResSwitch = FALSE; pssc = NULL; pssPal = NULL; flags = 0; #ifdef SS_DEBUG bDoTiming = type == SS_TYPE_NORMAL ? TRUE : FALSE; #endif // Global ptr to the screen saver instance gpss = this; // Platform detections ss_QueryOSVersion(); // Initialize randomizer ss_RandInit(); // Disable message boxes in GLAUX tkErrorPopups(FALSE); // Create multi-purpose black bg brush hbrBg = (HBRUSH) GetStockObject( BLACK_BRUSH ); // Call client ss's init function, to get ptr to its configuration // request if( type == SS_TYPE_CONFIG ) { // This case handled differently return; } pssc = ss_Init(); SS_ASSERT( pssc, "SCRNSAVE constructor failure\n" ); // Set GL config structure from pssc GLc.pfFlags = 0; GLc.hrc = 0; GLc.pStretch = NULL; switch( pssc->depthType ) { case SS_DEPTH16 : GLc.pfFlags |= SS_DEPTH16_BIT; break; case SS_DEPTH32 : GLc.pfFlags |= SS_DEPTH32_BIT; break; } if( pssc->bDoubleBuf ) GLc.pfFlags |= SS_DOUBLEBUF_BIT; if( pssc->bStretch ) GLc.pStretch = &pssc->stretchInfo; } /**************************************************************************\ * SetupInitialWindows * * Create / Configure all required windows. * \**************************************************************************/ BOOL SCRNSAVE::SetupInitialWindows() { // Create the windows if( ! CreateInitialWindows() ) { SS_WARNING( "SCRNSAVE:: Couldn't create windows\n" ); return FALSE; } // Initial window clear //mf: doesn't seem to be necessary now... //#define SS_INITIAL_MAIN_WINDOW_CLEAR 1 #ifdef SS_INITIAL_MAIN_WINDOW_CLEAR if( type == SS_TYPE_PREVIEW ) { // Make sure the screen is cleared to black before we start drawing // anything, as sometimes the background WM_PAINT doesn't get to us right // away. This is only a problem in preview mode psswMain->GdiClear(); } #endif // Configure and Init the windows, if applicable #ifdef SS_DELAYED_START_KLUGE // delay start for some configurations if( ! SS_DELAY_START(type) ) { SendMessage( psswMain->hwnd, SS_WM_INITGL, 0, 0 ); } #else SendMessage( psswMain->hwnd, SS_WM_INITGL, 0, 0 ); #endif // SS_DELAYED_START_KLUGE return TRUE; } /**************************************************************************\ * CreateInitialWindows * * Create the intitial set of windows. * \**************************************************************************/ BOOL SCRNSAVE::CreateInitialWindows() { PSSW pssw; UINT uStyle; UINT uExStyle; LPCTSTR pszWindowTitle; if( !pssc ) return FALSE; // Handle any request for resolution change #define SS_RESOLUTION_SWITCH 1 #ifdef SS_RESOLUTION_SWITCH if( pssc->bStretch && (type == SS_TYPE_FULLSCREEN) && ss_fOnWin95() ) { STRETCH_INFO *pStretch = &pssc->stretchInfo; ISIZE newSize; // Try and change screen resolution to match stretch size bResSwitch = AttemptResolutionSwitch( pStretch->baseWidth, pStretch->baseHeight, &newSize ); // Is stretching still necessary if resolution changed ? if( bResSwitch ) { if( (newSize.width == pStretch->baseWidth) && (newSize.height == pStretch->baseHeight) ) // exact match, no stretching now necessary pssc->bStretch = FALSE; } } #endif // Currently the bitmaps used in stretch mode don't support palette // messages, so disable any stretching when in PREVIEW mode (where we // need to support palette interaction). // mf: actually this is only a consideration in 8-bit mode... if( (type == SS_TYPE_PREVIEW) && pssc->bStretch ) pssc->bStretch = FALSE; // Create the main ss window if( ! CreateMainWindow() ) return FALSE; #ifdef SS_INITIAL_CLEAR // If main window is transparent, can do an initial clear here before // any other windows are created or palettes modified // This is bogus on NT, as system switches to secure desktop when screen // saver kicks in automatically. InitialClear( pssw ); #endif // For now, simple window environment is described by pssc, so things // like bFloater and bStretch are mutually exclusive. SS_GL_CONFIG *pGLc = &gpss->GLc; if( pssc->bFloater ) { if( !(pssw = CreateChildWindow( &pssc->floaterInfo )) ) return FALSE; pssw->pGLc = pGLc; psswGL = pssw; // support old-style } else { psswMain->pGLc = pGLc; psswGL = psswMain; // support old-style } return TRUE; } /**************************************************************************\ * NormalWindowScreenSaverProc * * Highest level window proc, used only in normal window (/w) mode. * \**************************************************************************/ LRESULT WINAPI NormalWindowScreenSaverProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { switch( uMsg ) { case WM_SETTEXT: // RealScreenSaverProc won't allow this - bypass it return ScreenSaverProc( hWnd, uMsg, wParam, lParam ); case WM_CHAR: if( KeyDownFunc ) { int key = wParam; (*KeyDownFunc)(key); } break; case WM_KEYDOWN: if( wParam == VK_ESCAPE ) { PostMessage( hWnd, WM_CLOSE, 0, 0l ); break; } else if( KeyDownFunc ) { (*KeyDownFunc)(wParam); return 0; } return 0; // ?? default: break; } return RealScreenSaverProc( hWnd, uMsg, wParam, lParam ); } /**************************************************************************\ * DoScreenSave * * Hooked out version of DoScreenSave in standard scrnsave.c * \**************************************************************************/ int DoScreenSave( HWND hwndParent ) { return GLDoScreenSave( hwndParent ? SS_TYPE_PREVIEW : SS_TYPE_FULLSCREEN, (LPARAM) hwndParent ); } /**************************************************************************\ * DoWindowedScreenSave * * Called when screen saver invoked with /w (window mode) parameter * \**************************************************************************/ int DoWindowedScreenSave( LPCTSTR szArgs ) { return GLDoScreenSave( SS_TYPE_NORMAL, (LPARAM) szArgs ); } /**************************************************************************\ * DoConfigBox * * Hooked out version of DoConfigBox in standard scrnsave.c * \**************************************************************************/ int DoConfigBox( HWND hwndParent ) { // let the consumer register any special controls for the dialog if( !RegisterDialogClasses( hMainInstance ) ) return FALSE; // Create screen saver instance SCRNSAVE ss( SS_TYPE_CONFIG ); int retVal = DialogBox( hMainInstance, MAKEINTRESOURCE( DLG_SCRNSAVECONFIGURE ), hwndParent, (DLGPROC)GLScreenSaverConfigureDialog ); return retVal; } /**************************************************************************\ * CreateMainWindow * * Creates main screen saver window based on the window type * \**************************************************************************/ BOOL SCRNSAVE::CreateMainWindow() { WNDPROC wndProc; int nCx, nCy; ISIZE size; UINT uStyle = 0; UINT uExStyle = 0; IPOINT2D pos; LPCTSTR pszWindowTitle; HCURSOR hCursor = NULL; HBRUSH hbrBgMain; PSSW pssw; BOOL bFailed; HWND hwndParent = NULL; wndProc = RealScreenSaverProc; switch( type ) { case SS_TYPE_FULLSCREEN: { HWND hOther; nCx = GetSystemMetrics( SM_CXSCREEN ); nCy = GetSystemMetrics( SM_CYSCREEN ); #ifdef SS_DEBUG //#define SS_FULLSCREEN_DEBUG 1 #ifdef SS_FULLSCREEN_DEBUG // Reduce window size so we can see debugger nCx /= 2; nCy /= 2; #endif #endif uStyle = WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; uExStyle = WS_EX_TOPMOST; pszWindowTitle = TEXT("Screen Saver"); // MUST differ from preview // if there is another NORMAL screen save instance, switch to it hOther = FindWindow( szClassName, pszWindowTitle ); if( hOther && IsWindow( hOther ) ) { SetForegroundWindow( hOther ); return FALSE; } InitRealScreenSave(); } break; case SS_TYPE_PREVIEW: { RECT rcParent; hwndParent = (HWND) initParam; GetClientRect( hwndParent, &rcParent ); fChildPreview = TRUE; nCx = rcParent.right; nCy = rcParent.bottom; uStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN; pszWindowTitle = TEXT("Preview"); // MUST differ from full screen } break; case SS_TYPE_NORMAL: { // We set fChildPreview even when we're running in a normal window, // as this flag is used in scrnsave.c to differentiate from full // screen. fChildPreview = TRUE; // init size to half screen by default nCx = GetSystemMetrics( SM_CXSCREEN ) >> 1; nCy = GetSystemMetrics( SM_CYSCREEN ) >> 1; if( initParam ) { // get size of window from args LPCTSTR szArgs = (LPCTSTR) initParam; //mf: not yet implemented } LoadString(hMainInstance, IDS_DESCRIPTION, szScreenSaverTitle, sizeof(szScreenSaverTitle) / sizeof(TCHAR)); pszWindowTitle = szScreenSaverTitle; // MUST differ from preview uStyle = WS_VISIBLE | WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; hCursor = LoadCursor( NULL, IDC_ARROW ); // Wrap RealScreenSaverProc wndProc = NormalWindowScreenSaverProc; } break; default: break; } size.width = nCx; size.height = nCy; #ifdef SS_INITIAL_CLEAR hbrBgMain = NULL; #else hbrBgMain = hbrBg; #endif pos.x = pos.y = 0; // Create SSW window wrapper pssw = new SSW( NULL, // parent &size, &pos, FALSE, // bMotion NULL // ChildSizeFunc ); if( !pssw ) return FALSE; // Initialize the window class and create the window if( !RegisterMainClass ( wndProc, hbrBgMain, hCursor ) || !pssw->CreateSSWindow ( hMainInstance, uStyle, uExStyle, szScreenSaverTitle, wndProc, pszWindowClass, hwndParent // mf: ! hwndParentOverride ) ) { delete pssw; return FALSE; } if( type != SS_TYPE_PREVIEW ) #ifndef SS_DEBUG SetForegroundWindow( pssw->hwnd ); #else { if( !SetForegroundWindow( pssw->hwnd ) ) SS_DBGPRINT( "Main_Proc: SetForegroundWindow failed\n" ); } #endif // Always configure the main window for gdi pssw->ConfigureForGdi(); psswMain = pssw; return TRUE; } /**************************************************************************\ * CreateChildWindow * * Creates a child window of the parent window * * This is a kind of wrapper-constructor \**************************************************************************/ PSSW SCRNSAVE::CreateChildWindow( FLOATER_INFO *pFloater ) { pFloater->bSubWindow = FALSE; // default is no logical subwin's #ifdef SS_WIN95_MULTIWINDOW_WORKAROUND if( ss_fOnWin95() && ss_fFullScreenMode() && bPowerSaveEnabled() ) { // Make the floater a logical sub-window of the main window, not a // separate window. This works around a USER bug where the power // save feature was not getting activated for multi-windowed screen // savers SS_DBGINFO( "SCRNSAVE::CreateChildWindow : Using logical sub-windows\n" ); pFloater->bSubWindow = TRUE; } #endif // SS_WIN95_MULTIWINDOW_WORKAROUND // Register child window class // This only has to be done once, since so far, all child window // classes are the same if( !pFloater->bSubWindow && !RegisterChildClass() ) return NULL; return CreateChildWindow( psswMain, pFloater ); } PSSW SCRNSAVE::CreateChildWindow( PSSW psswParent, FLOATER_INFO *pFloater ) { UINT uStyle = 0; UINT uExStyle = 0; PSSW pssw; uStyle = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; // Size and position are NULL here, as the SSW will call the size // function callback in pFloater to get these and other values // Create the SSW window wrapper pssw = new SSW( psswParent, // parent NULL, // size NULL, // position pFloater->bMotion, pFloater->ChildSizeFunc ); if( !pssw ) return NULL; if( pFloater->bSubWindow ) // Don't need to create win32 window return pssw; // Create a window if( !pssw->CreateSSWindow ( hMainInstance, uStyle, 0, // uExStyle szScreenSaverTitle , SS_ScreenSaverProc, szClassName, NULL // hwndParentOverride ) ) { delete pssw; return NULL; } return pssw; } /**************************************************************************\ * RegisterMainClass * * Registers class of the main SS window \**************************************************************************/ static BOOL RegisterMainClass( WNDPROC wndProc, HBRUSH hbrBg, HCURSOR hCursor ) { WNDCLASS cls; cls.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS | CS_OWNDC; cls.lpfnWndProc = wndProc; cls.cbClsExtra = 0; cls.cbWndExtra = 0; cls.hInstance = hMainInstance; cls.hIcon = LoadIcon( hMainInstance, MAKEINTATOM( ID_APP ) ); cls.hCursor = hCursor; cls.hbrBackground = hbrBg; cls.lpszMenuName = (LPTSTR)NULL; cls.lpszClassName = (LPTSTR)pszWindowClass; return RegisterClass(&cls); } /**************************************************************************\ * RegisterChildClass * * Registers class of a standard child window \**************************************************************************/ static BOOL RegisterChildClass() { static BOOL bRegistered = FALSE; if( bRegistered ) return TRUE; WNDCLASS cls; cls.style = CS_VREDRAW | CS_HREDRAW; cls.lpfnWndProc = SS_ScreenSaverProc; cls.cbClsExtra = 0; cls.cbWndExtra = 0; cls.hInstance = hMainInstance; cls.hIcon = NULL; cls.hCursor = NULL; cls.hbrBackground = NULL; cls.lpszMenuName = (LPTSTR)NULL; cls.lpszClassName = (LPTSTR)szClassName; if( !RegisterClass(&cls) ) return FALSE; // success bRegistered = TRUE; return TRUE; } /******************************Public*Routine******************************\ * AttemptResolutionSwitch * * Try doing resolution switching to match or get close to the desired size. * \**************************************************************************/ static BOOL AttemptResolutionSwitch( int width, int height, ISIZE *pNewSize ) { BOOL bChanged = FALSE; // Try doing resolution switching to match or get close to the // desired width and height // Try switching to requested size #if 0 //mf: not ready for prime time if( ss_ChangeDisplaySettings( width, height, 0 ) ) { #else if( 0 ) { // for now force failure of user request and try standard 640x480 #endif bChanged = TRUE; } else { // Can't switch to requested size, try for best match // mf: !!! for now, let's play it safe and just try 640x480. width = 640; height = 480; // If screen already this size or less, leave be if( (GetSystemMetrics( SM_CXSCREEN ) <= width) && (GetSystemMetrics( SM_CYSCREEN ) <= height) ) return FALSE; //mf: use this when trying for best match // ss_QueryDisplaySettings(); if( ss_ChangeDisplaySettings( width, height, 0 ) ) bChanged = TRUE; } if( bChanged ) { pNewSize->width = width; pNewSize->height = height; } return bChanged; } #ifdef SS_INITIAL_CLEAR static void InitialClear( PSSW *pssw ) { ss_GdiRectWipeClear( pssw->hwnd, pssw->size.width, pssw->size.height ); } #endif // SS_INITIAL_CLEAR /**************************************************************************\ * CloseWindows * * Close down any open windows. * * This sends a WM_CLOSE message to the top-level window if it is still open. If * the window has any children, they are also closed. For each window, the * SSW destructor is called. \**************************************************************************/ void SCRNSAVE::CloseWindows() { if( psswMain ) { if( psswMain->bOwnWindow ) DestroyWindow( psswMain->hwnd ); else delete psswMain; } } /**************************************************************************\ * SCRNSAVE destructor * \**************************************************************************/ SCRNSAVE::~SCRNSAVE() { // Close any open windows (there might be some open if errors occurred) CloseWindows(); #ifdef SS_WIN95_DESKTOP_REDRAW_FIX // On win95, sometimes the desktop is drawn incorrectly after we have // taken over the palette if( flags & SS_PALETTE_TAKEOVER ) { SS_DBGINFO( "SCRNSAVE destructor: Redrawing desktop\n" ); ss_RedrawDesktop(); } #endif if( bResSwitch ) { // Restore previous display settings ChangeDisplaySettings(NULL, CDS_FULLSCREEN); } gpss = NULL; } #ifdef SS_WIN95_MULTIWINDOW_WORKAROUND // Macro to convert string to integer #ifdef UNICODE #define sztoi( pszStr ) _wtoi( (wchar_t *) (pszStr) ) #else #define sztoi( pszStr ) atoi( (char *) (pszStr) ) #endif /**************************************************************************\ * GetRegValueBool * * Get registry value and convert to BOOL \**************************************************************************/ #define SS_REG_BUF_SIZE 30 static BOOL bGetRegValueBool( HKEY hKey, LPTSTR szValue, BOOL *pbEnabled ) { TCHAR szBuf[SS_REG_BUF_SIZE]; DWORD dataSize = SS_REG_BUF_SIZE; DWORD dwType; // Get the value if( ! ( RegQueryValueEx( hKey, szValue, (LPDWORD) NULL, (LPDWORD) &dwType, (LPBYTE) szBuf, (LPDWORD) &dataSize ) == ERROR_SUCCESS ) ) return FALSE; // Convert value to BOOL (the expected type is REG_SZ) switch( dwType ) { case REG_SZ : // Convert string to BOOL *pbEnabled = (BOOL) sztoi( szBuf ); break; case REG_DWORD : // Handle numerical value *pbEnabled = (BOOL) *( (LPDWORD) szBuf ); break; default: SS_WARNING( "GetRegValueBool : Unexpected type\n" ); return FALSE; } return TRUE; } /**************************************************************************\ * bPowerSaveEnabled * * Detect if power saving is enabled by checking registry values. * \**************************************************************************/ static BOOL bPowerSaveEnabled() { BOOL bLowPowerActive; BOOL bPowerOffActive; HKEY hKey; // Open desktop key if( ! ( RegOpenKeyEx( HKEY_CURRENT_USER, (LPCTSTR) TEXT( "Control Panel\\desktop"), 0, KEY_QUERY_VALUE, &hKey ) == ERROR_SUCCESS ) ) { return FALSE; // Can't open desktop key } // Check values - power saving is enabled if either ScreenSaveLowPowerActive // or ScreenSavePowerOffActive is enabled. if( ( bGetRegValueBool( hKey, (LPTSTR) TEXT("ScreenSaveLowPowerActive"), &bLowPowerActive ) && bLowPowerActive ) || ( bGetRegValueBool( hKey, (LPTSTR) TEXT("ScreenSavePowerOffActive"), &bPowerOffActive ) && bPowerOffActive ) ) { return TRUE; } return FALSE; } #endif // SS_WIN95_MULTIWINDOW_WORKAROUND