3153 lines
78 KiB
C
3153 lines
78 KiB
C
//***************************************************************************
|
|
//
|
|
// QuickRes for Windows NT and Windows 9x
|
|
//
|
|
// Tray app to change your display resolution quickly.
|
|
//
|
|
// written by ToddLa
|
|
//
|
|
// 03/03/96 - ChrisW : Get to build on NT
|
|
// 03/28/96 - MDesai : Finish porting; add submenus with frequencies;
|
|
// Test for valid devmode.
|
|
// 04/23/96 - MDesai : option for 'showing tested modes only'
|
|
// 10/01/96 - MDesai : fix all win95-specific bugs
|
|
// 11/03/98 - MDesai : 'multimonitor aware'
|
|
// 12/02/99 - MDesai : better multimon support
|
|
//
|
|
//***************************************************************************
|
|
|
|
|
|
#include "QuickRes.h"
|
|
|
|
|
|
PTCHAR szAppName;
|
|
|
|
HINSTANCE hInstApp;
|
|
HICON AppIcon;
|
|
|
|
|
|
//
|
|
// options, properties, about and exit...
|
|
// monitor menu
|
|
//
|
|
|
|
HMENU MainMenu;
|
|
HMENU MonitorMenu=NULL;
|
|
|
|
//
|
|
// number of monitors/display devices installed
|
|
// pointer to monitorinfo struct for each monitor
|
|
//
|
|
|
|
INT iMonitors;
|
|
LPQRMONITORINFO pMonitors;
|
|
|
|
|
|
//
|
|
// Waiting for a Popup - don't process any tray messages
|
|
//
|
|
|
|
BOOL Waiting=FALSE;
|
|
|
|
|
|
//
|
|
// Flags: update registry, show restart modes, sort order
|
|
// also where the freq menu(s) go.
|
|
//
|
|
WORD QuickResFlags;
|
|
WORD FreqMenuLocation;
|
|
|
|
|
|
//
|
|
// Function pointers for NT5
|
|
//
|
|
FARPROC lpfnEDSEx=NULL;
|
|
FARPROC lpfnEDD=NULL;
|
|
|
|
|
|
//
|
|
//***************************************************************************
|
|
//
|
|
// GetResourceString( UINT )
|
|
//
|
|
// Load a resource string into a LPTSTR - the memory for the string
|
|
// is dynamically allocated. The callee must free the memory!
|
|
//
|
|
//***************************************************************************
|
|
//
|
|
|
|
LPTSTR GetResourceString ( UINT ResourceID )
|
|
{
|
|
|
|
|
|
INT BuffSize=RESOURCE_STRINGLEN; // current max size of string
|
|
PTCHAR BigBuf; // buffer to find size of resource
|
|
PTCHAR ResBuf; // buffer for resource
|
|
INT len; // length of the resource
|
|
|
|
|
|
while (1)
|
|
{
|
|
|
|
//
|
|
// Allocate hopefully oversized buffer
|
|
//
|
|
|
|
if( !(BigBuf= LocalAlloc( LPTR, BuffSize ) ) )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Try to read string into BigBuf to get its length
|
|
//
|
|
|
|
if ( !(len = LoadString(hInstApp, ResourceID, BigBuf, BuffSize)) )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Buffer is too small - try again.
|
|
//
|
|
|
|
if( len >= BuffSize-1 )
|
|
{
|
|
BuffSize <<= 1;
|
|
LocalFree ( BigBuf );
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
//
|
|
// Reallocate properly sized string buffer,
|
|
// and copy string into it
|
|
//
|
|
|
|
len = ( len + 1 ) * sizeof( TCHAR );
|
|
|
|
if (ResBuf = LocalAlloc( LPTR, len ))
|
|
{
|
|
lstrcpyn ( ResBuf, BigBuf, len );
|
|
}
|
|
|
|
LocalFree ( BigBuf );
|
|
|
|
return( ResBuf );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
//***************************************************************************
|
|
//
|
|
// GetModeName( PDEVMODE, PTCHAR*, PTCHAR* )
|
|
//
|
|
// Translate devmode into user friendly strings-
|
|
// one for resolution and color depth; one for refresh rate
|
|
//
|
|
//***************************************************************************
|
|
//
|
|
|
|
void GetModeName(PDEVMODE pDevMode, PTCHAR *szMode, PTCHAR *szFreq )
|
|
{
|
|
|
|
PTCHAR FmtRes=NULL; // Format strings for
|
|
PTCHAR FmtHz=NULL; // resolution and Hz
|
|
|
|
|
|
//
|
|
// Load format string corresponding to devmode
|
|
//
|
|
|
|
FmtRes = GetResourceString ( IDS_CRES + BPP(pDevMode) );
|
|
|
|
|
|
//
|
|
// Use Default Freq string if necessary
|
|
//
|
|
if (fShowFreqs)
|
|
{
|
|
if( HZ(pDevMode) == 0 || HZ(pDevMode) == 1)
|
|
{
|
|
FmtHz = GetResourceString ( IDS_DEFHERTZ );
|
|
}
|
|
else
|
|
{
|
|
FmtHz = GetResourceString ( IDS_HERTZ );
|
|
}
|
|
}
|
|
|
|
//
|
|
// return separate resolution and frequency strings
|
|
// need to convert "%d"-> "12345", add byte for '\0'
|
|
//
|
|
|
|
if (FmtRes)
|
|
{
|
|
if (*szMode = LocalAlloc( LPTR, sizeof(TCHAR)*
|
|
(lstrlen(FmtRes)+2*INT_FORMAT_TO_5_DIGITS+1 ) ))
|
|
{
|
|
wsprintf(*szMode, FmtRes, XRES(pDevMode), YRES(pDevMode) );
|
|
}
|
|
|
|
LocalFree ( FmtRes );
|
|
}
|
|
|
|
|
|
if (fShowFreqs && FmtHz)
|
|
{
|
|
|
|
if (*szFreq = LocalAlloc ( LPTR, sizeof(TCHAR)*
|
|
(lstrlen(FmtHz)+INT_FORMAT_TO_5_DIGITS+1) ))
|
|
{
|
|
wsprintf(*szFreq, FmtHz, HZ(pDevMode));
|
|
}
|
|
|
|
LocalFree ( FmtHz );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//***************************************************************************
|
|
//
|
|
// GetCurrentDevMode( INT, PDEVMODE )
|
|
//
|
|
// Get a pointer to the current devmode into *pDM
|
|
//
|
|
//***************************************************************************
|
|
//
|
|
|
|
PDEVMODE GetCurrentDevMode(INT iDisplay, PDEVMODE pDM)
|
|
{
|
|
|
|
UINT uRet=0;
|
|
|
|
pDM->dmSize= sizeof(DEVMODE);
|
|
|
|
//
|
|
// NT specific; returns current devmode
|
|
//
|
|
|
|
if (fShowFreqs)
|
|
{
|
|
uRet = EnumDisplaySettings( pMonitors[iDisplay].DeviceName, (DWORD)ENUM_CURRENT_SETTINGS, pDM);
|
|
}
|
|
|
|
if (!uRet)
|
|
{
|
|
//
|
|
// ENUM_CURRENT_SETTINGS doesnt work on win95
|
|
// Get current settings via GetDeviceCaps
|
|
//
|
|
|
|
HDC hDC;
|
|
UINT HorzRes;
|
|
UINT VertRes;
|
|
UINT BPP;
|
|
UINT VRefresh;
|
|
UINT Index;
|
|
|
|
hDC = GetDC( NULL );
|
|
HorzRes = GetDeviceCaps( hDC, HORZRES );
|
|
VertRes = GetDeviceCaps( hDC, VERTRES );
|
|
BPP = GetDeviceCaps( hDC, BITSPIXEL ) * GetDeviceCaps( hDC, PLANES );
|
|
VRefresh = GetDeviceCaps( hDC, VREFRESH );
|
|
|
|
//
|
|
// Enumerate all settings until one matches our current settings
|
|
//
|
|
|
|
for ( Index=0;
|
|
EnumDisplaySettings( pMonitors[iDisplay].DeviceName, Index, pDM);
|
|
Index++ )
|
|
{
|
|
if ( HorzRes == XRES(pDM) &&
|
|
VertRes == YRES(pDM) &&
|
|
BPP == BPP(pDM)
|
|
)
|
|
{
|
|
//
|
|
// if frequency matters, then check for it
|
|
//
|
|
|
|
if (!fShowFreqs || (VRefresh == HZ(pDM)) )
|
|
break;
|
|
}
|
|
}
|
|
|
|
ReleaseDC (NULL, hDC);
|
|
}
|
|
|
|
return pDM;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//***************************************************************************
|
|
//
|
|
// SetMode( HWND, UINT )
|
|
//
|
|
// Set the new devmode and update registry on request using
|
|
// the CDS_UPDATEREGISTRY flag. If user wants to change and
|
|
// restart, then we need to update the registry and restart.
|
|
//
|
|
//***************************************************************************
|
|
//
|
|
|
|
BOOL SetMode( HWND hwnd, INT iDisplay, UINT index )
|
|
{
|
|
DWORD CDSret=0; // ret value, ChangeDisplaySettings
|
|
DWORD CDSFlags=0; // 2nd param of call to CDS
|
|
INT_PTR DialogBoxRet=0; // IDYES/NO/ABORT/CANCEL
|
|
LPDEVMODEINFO pSave; // save ptr in iDisplay to remember orig mode
|
|
LPDEVMODEINFO pdm; // new mode to be set
|
|
BOOL bChange=FALSE; // changing modes or not
|
|
LPQRMONITORINFO pCurrMon; // ptr to current monitorinfo strcut
|
|
|
|
|
|
pCurrMon = &pMonitors[iDisplay];
|
|
|
|
//
|
|
// Save current mode; find ptr to new mode
|
|
//
|
|
|
|
pSave = pCurrMon->pCurrentdm;
|
|
pdm = &(pCurrMon->pModes[index]);
|
|
|
|
//
|
|
// If user wants to update registry
|
|
//
|
|
|
|
if( fUpdateReg )
|
|
{
|
|
CDSFlags |= CDS_UPDATEREGISTRY;
|
|
}
|
|
|
|
|
|
//
|
|
// Tell CDS what fields may be changing
|
|
// Also, keep appwndproc from doing anything while we are testing
|
|
//
|
|
|
|
pdm->dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
|
|
Waiting=TRUE;
|
|
|
|
|
|
//
|
|
// Call CDS and update registry on request. (If it is
|
|
// a known bad mode give user chance to change his mind.)
|
|
//
|
|
|
|
if( (VALIDMODE(pdm) != MODE_INVALID ) ||
|
|
( MsgBox( IDS_INVALIDMODE, 0, MB_YESNO | MB_ICONQUESTION )==IDYES ) )
|
|
{
|
|
|
|
CDSret = ChangeDisplaySettingsEx( pCurrMon->DeviceName, &(pdm->dm), NULL, CDSFlags, 0);
|
|
|
|
if (CDSret == DISP_CHANGE_SUCCESSFUL)
|
|
{
|
|
//
|
|
// Even though it may be temporary, current dm has changed.
|
|
// Need to reset pCurrentdm to point to new current DM.
|
|
// Change tooltip to reflect old settings
|
|
//
|
|
|
|
pCurrMon->pCurrentdm = pdm;
|
|
|
|
TrayMessage(hwnd, NIM_MODIFY, TRAY_ID, AppIcon);
|
|
|
|
//
|
|
// Return value claims that it 'worked.' But, it may not visible
|
|
// to the user (e.g. the mode is unsupported by the monitor).
|
|
// If the User has not already approved this new resolution,
|
|
// then make the user approve the change, or we default back to
|
|
// the last devmode.
|
|
//
|
|
|
|
if ( fGoodMode(pdm) )
|
|
{
|
|
//
|
|
// VALID or BESTHZ modes - go ahead and change
|
|
//
|
|
|
|
bChange = TRUE;
|
|
}
|
|
|
|
else
|
|
{
|
|
//
|
|
// Ask user if it looks okay
|
|
// Flag the mode based on return value.
|
|
//
|
|
|
|
switch( DialogBoxRet = DialogBoxParam( hInstApp,
|
|
MAKEINTRESOURCE(KeepNewRes),
|
|
NULL,
|
|
KeepNewResDlgProc,
|
|
iDisplay) )
|
|
{
|
|
|
|
//
|
|
// There should NOT be a break after
|
|
// IDYES. Fall thru by design.
|
|
//
|
|
case IDYES: bChange = TRUE;
|
|
|
|
case IDABORT: VALIDMODE(pdm) = MODE_VALID;
|
|
break;
|
|
|
|
case IDNO:
|
|
case IDCANCEL: VALIDMODE(pdm) = MODE_INVALID;
|
|
break;
|
|
|
|
} // switch
|
|
|
|
} // else - MODE_INVALID
|
|
|
|
}
|
|
|
|
if (CDSret != DISP_CHANGE_SUCCESSFUL)
|
|
{
|
|
//
|
|
// Requires restart. Ask user if thats okay.
|
|
//
|
|
|
|
if (CDSret == DISP_CHANGE_RESTART)
|
|
{
|
|
|
|
if ( MsgBox(IDS_RESTART, 0, MB_YESNO | MB_ICONQUESTION) == IDYES )
|
|
{
|
|
|
|
//
|
|
// After restart all modes will need to be tested again?
|
|
//
|
|
|
|
SetDevmodeFlags ( iDisplay, TRUE );
|
|
|
|
|
|
//
|
|
// Call CDS again to update registry
|
|
//
|
|
|
|
ChangeDisplaySettingsEx( pCurrMon->DeviceName, &(pdm->dm), NULL,
|
|
(CDSFlags | CDS_UPDATEREGISTRY), 0);
|
|
|
|
ExitWindowsEx(EWX_REBOOT, 0);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// Tell user we cannot change to this devmode
|
|
//
|
|
|
|
MsgBox(IDS_CANTSETMODE, 0, MB_OK | MB_ICONEXCLAMATION);
|
|
}
|
|
|
|
} // end else != DISP_CHANGE_SUCCESSFUL
|
|
|
|
|
|
if (bChange)
|
|
{
|
|
//
|
|
// Changing to a valid mode; destroy and rebuild menu
|
|
// Mark mode we were just in as valid (if it wasnt already)
|
|
//
|
|
|
|
VALIDMODE(pSave) |= MODE_VALID;
|
|
|
|
//
|
|
// This is the new "Best Hz" mode. The old mode is 'only valid'.
|
|
//
|
|
|
|
if ((FreqMenuLocation == IDD_ONEMENUMOBILE) ||
|
|
(FreqMenuLocation == IDD_ONEMENUBOTTOM) )
|
|
{
|
|
VALIDMODE(pCurrMon->pCurrentdm) = MODE_BESTHZ;
|
|
}
|
|
|
|
DestroyModeMenu( iDisplay, TRUE, FALSE );
|
|
}
|
|
|
|
else // !bChange
|
|
{
|
|
|
|
//
|
|
// Change back to last good devmode; do not have to recheck menuitems
|
|
//
|
|
|
|
pCurrMon->pCurrentdm = pSave;
|
|
|
|
|
|
//
|
|
// Change back, and reset registry IF we had set it above
|
|
// Change tooltip to reflect old settings
|
|
//
|
|
|
|
if (CDSret != DISP_CHANGE_RESTART)
|
|
{
|
|
ChangeDisplaySettingsEx( pCurrMon->DeviceName, &(pCurrMon->pCurrentdm->dm),
|
|
NULL, CDSFlags, 0);
|
|
}
|
|
|
|
TrayMessage(hwnd, NIM_MODIFY, TRAY_ID, AppIcon);
|
|
|
|
} // bChange
|
|
|
|
|
|
} // endif
|
|
|
|
|
|
//
|
|
// Save new settings for this devmode to the registry.
|
|
// Even if quickres does not exit gracefully, preferences
|
|
// will be saved.
|
|
//
|
|
|
|
if (fRememberModes)
|
|
{
|
|
SaveAllSettings();
|
|
}
|
|
|
|
//
|
|
// Show modemenu again; allow appwndproc to process messages
|
|
//
|
|
|
|
if (!bChange)
|
|
{
|
|
SetTimer(hwnd, TRAY_ID, 10, NULL);
|
|
}
|
|
|
|
Waiting=FALSE;
|
|
|
|
|
|
//
|
|
// if Current hasnt changed then we return false
|
|
//
|
|
|
|
return (bChange);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
//********************************************************************
|
|
//
|
|
// CompareDevmodes ( LPDEVMODEINFO, LPDEVMODEINFO )
|
|
//
|
|
// Compares 2 devmodes -
|
|
// Returns 0 if equal, -1 if first > second, +1 if first < second
|
|
//
|
|
// msb to lsb: xres, yres, bpp, hertz
|
|
//********************************************************************
|
|
//
|
|
|
|
int _cdecl CompareDevmodes( LPDEVMODEINFO pDmi1, LPDEVMODEINFO pDmi2 )
|
|
{
|
|
INT compare;
|
|
LPDEVMODE pDm1 = &(pDmi1->dm);
|
|
LPDEVMODE pDm2 = &(pDmi2->dm);
|
|
|
|
|
|
//
|
|
// Compare Xs, then Ys, BPP, and Hz. If !fShowFreqs
|
|
// then compare only Xs, Ys, and BPP.
|
|
//
|
|
|
|
if ( !fSortByBPP || ((compare= BPP(pDm1) - BPP(pDm2)) == 0))
|
|
{
|
|
if( (compare= ( XRES(pDm1) - XRES(pDm2) ) ) == 0 )
|
|
{
|
|
if( (compare= ( YRES(pDm1) - YRES(pDm2) ) ) == 0 )
|
|
{
|
|
if ( fSortByBPP || ((compare= BPP(pDm1) - BPP(pDm2)) == 0))
|
|
{
|
|
compare = fShowFreqs ? (HZ(pDm1) - HZ(pDm2)) : 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set return value as -1, 0, or 1 only
|
|
//
|
|
|
|
if( compare < 0)
|
|
{
|
|
compare= -1;
|
|
}
|
|
|
|
else
|
|
{
|
|
if( compare > 0 )
|
|
{
|
|
compare= 1;
|
|
}
|
|
}
|
|
|
|
return( compare );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
//********************************************************************
|
|
//
|
|
// CheckMenuItemCurrentMode ( INT )
|
|
//
|
|
// Traverse all menu items and check the Hz value corresponding
|
|
// to the current mode. Also, highlight the current resolution/
|
|
// BPP as defaultmenuitem
|
|
//
|
|
//********************************************************************
|
|
//
|
|
|
|
void CheckMenuItemCurrentMode( INT iDisplay )
|
|
{
|
|
|
|
int i; // counter
|
|
DEVMODEINFO dmi; // temporary storage for current DM
|
|
LPQRMONITORINFO lpqrmi; // temporary ptr
|
|
HMENU hMenu; // Frequency submenu for a given Res/BPP
|
|
UINT MenuItem; // Menu item for exact devmode
|
|
DWORD dwSta; // returns status variable
|
|
|
|
|
|
|
|
lpqrmi = &pMonitors[iDisplay];
|
|
|
|
//
|
|
// Need a pointer to the current devmode. This function will search
|
|
// pModes trying to match the devmode pointed to by pCurrentdm.
|
|
// After the 1st time through, pCurrentdm will be a ptr IN pModes
|
|
//
|
|
|
|
if (!lpqrmi->pCurrentdm)
|
|
{
|
|
//
|
|
// Get current devmode
|
|
//
|
|
|
|
GetCurrentDevMode(iDisplay, &(dmi.dm));
|
|
lpqrmi->pCurrentdm = &dmi;
|
|
}
|
|
|
|
|
|
//
|
|
// Uncheck all menu items
|
|
//
|
|
|
|
for( i=0; i<lpqrmi->iModes; i++ )
|
|
{
|
|
|
|
hMenu = lpqrmi->FreqMenu[FREQMENU( &lpqrmi->pModes[i] )];
|
|
|
|
MenuItem= MENUITEM( &lpqrmi->pModes[i] );
|
|
|
|
//
|
|
// Uncheck the Hz in the FreqMenu (if applicable); uncheck item on mode menu
|
|
//
|
|
|
|
if (hMenu)
|
|
{
|
|
dwSta= CheckMenuItem(hMenu, MenuItem, MF_BYCOMMAND|MF_UNCHECKED);
|
|
CheckMenuItem(lpqrmi->ModeMenu, FREQMENU( &lpqrmi->pModes[i] ), MF_BYPOSITION | MF_UNCHECKED );
|
|
}
|
|
|
|
CheckMenuItem(lpqrmi->ModeMenu, MenuItem, MF_BYCOMMAND | MF_UNCHECKED );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Check the current one
|
|
//
|
|
|
|
for( i=0; i<lpqrmi->iModes; i++ )
|
|
{
|
|
|
|
//
|
|
// Go through the array looking for a match of the current devmode
|
|
//
|
|
|
|
if( ( CompareDevmodes( lpqrmi->pCurrentdm, &lpqrmi->pModes[i] ) ) == 0 )
|
|
{
|
|
|
|
//
|
|
// Found it!
|
|
// Get the menu item ID for this devmode and which
|
|
// frequency submenu it is a part of.
|
|
//
|
|
|
|
hMenu = lpqrmi->FreqMenu[FREQMENU( &lpqrmi->pModes[i] )];
|
|
MenuItem= MENUITEM( &lpqrmi->pModes[i] );
|
|
|
|
|
|
//
|
|
// Save this ptr in the pCurrentdm variable
|
|
// check menu item on mode menu and check mode
|
|
// on frequency submenu (if applicable)
|
|
//
|
|
|
|
lpqrmi->pCurrentdm = &lpqrmi->pModes[i];
|
|
|
|
if (hMenu)
|
|
{
|
|
dwSta= CheckMenuItem(hMenu, MenuItem, MF_BYCOMMAND|MF_CHECKED);
|
|
CheckMenuItem(lpqrmi->ModeMenu, FREQMENU(&lpqrmi->pModes[i]), MF_BYPOSITION | MF_CHECKED );
|
|
}
|
|
else
|
|
{
|
|
CheckMenuItem(lpqrmi->ModeMenu, MenuItem, MF_BYCOMMAND | MF_CHECKED );
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//********************************************************************
|
|
//
|
|
// DestroyModeMenu( INT iDisplay, BOOL bRebuild, BOOL bNeedtoSort )
|
|
//
|
|
// Free all frequency submenus and the mode menu
|
|
//
|
|
//********************************************************************
|
|
//
|
|
|
|
void DestroyModeMenu( INT iDisplay, BOOL bRebuild, BOOL bNeedtoSort)
|
|
{
|
|
|
|
int i;
|
|
LPQRMONITORINFO lpqrmi; // temporary ptr
|
|
|
|
|
|
|
|
lpqrmi = &pMonitors[iDisplay];
|
|
|
|
//
|
|
// Free all frequency submenus
|
|
//
|
|
|
|
for ( i = 0; i < lpqrmi->iModes; i++ )
|
|
{
|
|
|
|
if (IsMenu(lpqrmi->FreqMenu[i]))
|
|
{
|
|
DestroyMenu( lpqrmi->FreqMenu[i] );
|
|
lpqrmi->FreqMenu[i] = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Free the mode menu (resolutions/BPP)
|
|
//
|
|
|
|
if (lpqrmi->ModeMenu)
|
|
{
|
|
DestroyMenu(lpqrmi->ModeMenu);
|
|
lpqrmi->ModeMenu = NULL;
|
|
|
|
if (iMonitors==1)
|
|
{
|
|
DestroyMenu(MonitorMenu);
|
|
MonitorMenu = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
if (bRebuild)
|
|
{
|
|
lpqrmi->ModeMenu = GetModeMenu( iDisplay, bNeedtoSort );
|
|
|
|
if (iMonitors==1)
|
|
{
|
|
MonitorMenu = lpqrmi->ModeMenu;
|
|
AppendMainMenu();
|
|
}
|
|
else
|
|
{
|
|
// If ModifyMenu replaces a menu item that opens a drop-down menu or submenu, the
|
|
// function destroys the old drop-down menu/submenu & frees the memory used by it.
|
|
|
|
ModifyMenu( MonitorMenu, iDisplay, MF_BYPOSITION | MF_POPUP, (UINT_PTR)lpqrmi->ModeMenu,
|
|
(pMonitors[iDisplay].bPrimary ? pMonitors[iDisplay].PrimaryMonitorName : pMonitors[iDisplay].MonitorName) );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//********************************************************************
|
|
//
|
|
// HandleFreqMenu( )
|
|
//
|
|
// Either append submenu to res/bpp, save it for later, or
|
|
// ditch it and put all Hz entries on mode menu.
|
|
// If there is only one Hz for a given Res, we dont need it.
|
|
//
|
|
//********************************************************************
|
|
//
|
|
VOID HandleFreqMenu( INT iDisplay, int FreqCount, int ResCounter, int pFirst)
|
|
{
|
|
|
|
PTCHAR Res=NULL;
|
|
PTCHAR Hz=NULL;
|
|
LPQRMONITORINFO lpqrmi; // temporary ptr
|
|
|
|
|
|
lpqrmi = &pMonitors[iDisplay];
|
|
GetModeName(&lpqrmi->pModes[pFirst].dm, &Res, &Hz);
|
|
|
|
//
|
|
// Dont use submenus if there is only 1 Hz
|
|
// This is always true when freqmenulocation==IDD_ALLMODEMENU
|
|
// OR not showing frequency menus
|
|
// Concatenate Res & Hz into one string (IF fShowFreqs)
|
|
//
|
|
|
|
if ( FreqCount == 1 )
|
|
{
|
|
if (fShowFreqs)
|
|
{
|
|
PTCHAR ResHz;
|
|
|
|
if (ResHz=LocalAlloc( LPTR, sizeof(TCHAR)*
|
|
(lstrlen(Res)+lstrlen(Hz)+1) ))
|
|
{
|
|
wsprintf(ResHz,TEXT("%s%s"),Res,Hz);
|
|
AppendMenu(lpqrmi->ModeMenu, MF_STRING,
|
|
(iDisplay+1)*MENU_RES+pFirst, ResHz);
|
|
}
|
|
|
|
LocalFree(ResHz);
|
|
}
|
|
else
|
|
{
|
|
AppendMenu(lpqrmi->ModeMenu, MF_STRING,
|
|
(iDisplay+1)*MENU_RES+pFirst, Res);
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
int i=0;
|
|
int nAppended=0;
|
|
|
|
//
|
|
// Create Popup and append all Hz strings
|
|
// Append FreqCount items, possibly skipping over some modes
|
|
//
|
|
|
|
lpqrmi->FreqMenu[ResCounter] = CreatePopupMenu();
|
|
|
|
for (i=0; nAppended < FreqCount; i++)
|
|
{
|
|
|
|
PTCHAR LoopRes=NULL;
|
|
PTCHAR LoopHz=NULL;
|
|
|
|
//
|
|
// Skip untested modes if requested. FreqCount does NOT
|
|
// include skipped modes, so we count up with nAppended, not i.
|
|
//
|
|
|
|
if ( !fShowTestedModes || fGoodMode(&lpqrmi->pModes[pFirst+i]) )
|
|
{
|
|
|
|
GetModeName(&lpqrmi->pModes[pFirst+i].dm,&LoopRes,&LoopHz);
|
|
AppendMenu(lpqrmi->FreqMenu[ResCounter],MF_STRING,
|
|
(iDisplay+1)*MENU_RES+pFirst+i,LoopHz);
|
|
nAppended++;
|
|
|
|
LocalFree(LoopRes);
|
|
LocalFree(LoopHz);
|
|
LoopRes=NULL;
|
|
LoopHz=NULL;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Hang menu off side of each bpp/res
|
|
//
|
|
|
|
if (FreqMenuLocation == IDD_SUBMENUS)
|
|
{
|
|
AppendMenu(lpqrmi->ModeMenu,MF_POPUP,
|
|
(UINT_PTR)lpqrmi->FreqMenu[ResCounter],Res);
|
|
}
|
|
|
|
else
|
|
{
|
|
//
|
|
// Only show submenu for the current mode
|
|
// Use BESTHZ mode or the VALID mode with the
|
|
// lowest frequency.
|
|
//
|
|
|
|
if ( (FreqMenuLocation == IDD_ONEMENUMOBILE) ||
|
|
(FreqMenuLocation == IDD_ONEMENUBOTTOM) )
|
|
{
|
|
|
|
int BestHz=0;
|
|
int index;
|
|
|
|
//
|
|
// Start with highest freq (pFirst+i-1)
|
|
// and work down to pFirst looking for BestHz.
|
|
// if we find BESTHZ use that one, else
|
|
// use last VALIDMODE we get before loop ends
|
|
//
|
|
|
|
for (index=pFirst+i-1 ; index >= pFirst; index--)
|
|
{
|
|
if ( VALIDMODE(&lpqrmi->pModes[index]) == MODE_BESTHZ )
|
|
{
|
|
BestHz = index;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (VALIDMODE(&lpqrmi->pModes[index])!=MODE_INVALID)
|
|
{
|
|
BestHz = index;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// No valid/besthz modes. Use smallest Hz for that Res
|
|
//
|
|
|
|
if (!BestHz)
|
|
{
|
|
BestHz = pFirst;
|
|
}
|
|
|
|
AppendMenu(lpqrmi->ModeMenu,MF_STRING,
|
|
(iDisplay+1)*MENU_RES+BestHz,Res);
|
|
}
|
|
}
|
|
}
|
|
|
|
LocalFree(Res);
|
|
LocalFree(Hz);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
//********************************************************************
|
|
//
|
|
// GetModeMenu( INT, BOOL )
|
|
//
|
|
// Build the mode menu with each resolution/BPP having a
|
|
// pointer to its own frequency submenu
|
|
//
|
|
//********************************************************************
|
|
//
|
|
|
|
HMENU GetModeMenu ( INT iDisplay, BOOL bNeedtoSort )
|
|
{
|
|
|
|
int n; // counter
|
|
BOOL bMajorChange=FALSE; // change in the major sort order field
|
|
BOOL bMinorChange=FALSE; // change in the minor sort order field
|
|
int FreqCount=0; // number of freqs on the current submenu
|
|
int ResCounter=0; // Res/Color defines the freqmenu #
|
|
INT FirstMode=-1; // index in pmodes; 1st mode for given res/bpp
|
|
LPQRMONITORINFO lpqrmi; // temporary ptr
|
|
|
|
|
|
|
|
lpqrmi = &pMonitors[iDisplay];
|
|
|
|
|
|
if (!lpqrmi->ModeMenu)
|
|
{
|
|
lpqrmi->ModeMenu = CreatePopupMenu();
|
|
|
|
|
|
if (bNeedtoSort)
|
|
{
|
|
qsort( (void*) lpqrmi->pModes,
|
|
(size_t) lpqrmi->iModes,
|
|
(size_t) sizeof(DEVMODEINFO),
|
|
( int (_cdecl*)(const void*,const void*) ) CompareDevmodes );
|
|
|
|
lpqrmi->pCurrentdm = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// For each devmode, add res/color to menu.
|
|
// Make a submenu of frequencies for each res/color
|
|
//
|
|
|
|
for (n=0; n < lpqrmi->iModes; n++)
|
|
{
|
|
LPDEVMODEINFO pDM = &lpqrmi->pModes[n];
|
|
|
|
//
|
|
// Tested successfully or might require restart
|
|
//
|
|
|
|
if ( ( (CDSTEST(pDM) == DISP_CHANGE_SUCCESSFUL) ||
|
|
(fShowModesThatNeedRestart && (CDSTEST(pDM) == DISP_CHANGE_RESTART)) ) &&
|
|
|
|
( !fShowTestedModes || fGoodMode(pDM) ) )
|
|
{
|
|
|
|
|
|
//
|
|
// Check for change in the major/minor sort item
|
|
// *only after we 'initialize' firstmode below
|
|
//
|
|
|
|
if (FirstMode == -1)
|
|
{
|
|
|
|
//
|
|
// First time thru, initialize FirstMode,counter
|
|
//
|
|
|
|
FirstMode = n;
|
|
FreqCount=0;
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
if( BPP(&lpqrmi->pModes[FirstMode].dm) != BPP(&pDM->dm) )
|
|
{
|
|
bMajorChange = fSortByBPP;
|
|
bMinorChange = !fSortByBPP;
|
|
}
|
|
|
|
if( ( XRES(&lpqrmi->pModes[FirstMode].dm) != XRES(&pDM->dm) ) ||
|
|
( YRES(&lpqrmi->pModes[FirstMode].dm) != YRES(&pDM->dm) ) )
|
|
{
|
|
bMajorChange |= !fSortByBPP;
|
|
bMinorChange |= fSortByBPP;
|
|
}
|
|
|
|
|
|
//
|
|
// The BPP and/or the Resolution changed.
|
|
//
|
|
|
|
if ( bMajorChange || bMinorChange )
|
|
{
|
|
|
|
//
|
|
// Appends a Res/BPP and a submenu if applicable
|
|
//
|
|
|
|
HandleFreqMenu(iDisplay,FreqCount,ResCounter,FirstMode);
|
|
ResCounter++;
|
|
|
|
//
|
|
// Need a separator when major sort item changes
|
|
//
|
|
|
|
if ( bMajorChange )
|
|
{
|
|
AppendMenu(lpqrmi->ModeMenu,MF_SEPARATOR,0,NULL);
|
|
ResCounter++;
|
|
}
|
|
|
|
|
|
//
|
|
// n is first mode for the new res/bpp
|
|
// reset counter, flags
|
|
//
|
|
|
|
FirstMode = n;
|
|
FreqCount= 0;
|
|
bMajorChange = FALSE;
|
|
bMinorChange = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Fill in fields for this mode; inc freqcount
|
|
//
|
|
|
|
|
|
MENUITEM( pDM ) = (iDisplay+1)*MENU_RES+n;
|
|
FREQMENU( pDM ) = ResCounter;
|
|
FreqCount++;
|
|
|
|
|
|
//
|
|
// ALLMODEMENU - Force menu append every time
|
|
//
|
|
|
|
if (FreqMenuLocation == IDD_ALLMODEMENU)
|
|
{
|
|
bMinorChange = TRUE;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
} // end for
|
|
|
|
|
|
//
|
|
// NO VALID MODES!!! Certainly the current mode should be valid. Make
|
|
// this mode VALID. Setup FreqCount, FirstMode for the last HandleFreqMenu
|
|
//
|
|
|
|
if (FirstMode == -1)
|
|
{
|
|
DEVMODEINFO DisplayModeInfo;
|
|
|
|
DisplayModeInfo.dm.dmSize= sizeof(DEVMODE);
|
|
GetCurrentDevMode(iDisplay, &DisplayModeInfo.dm);
|
|
|
|
for (n=0;
|
|
CompareDevmodes(&DisplayModeInfo,&lpqrmi->pModes[n]) != 0;
|
|
n++ )
|
|
{
|
|
}
|
|
|
|
VALIDMODE(&lpqrmi->pModes[n]) = MODE_BESTHZ;
|
|
FirstMode = n;
|
|
FreqCount = 1;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Handle the FreqMenu for the last Res/BPP.
|
|
//
|
|
|
|
HandleFreqMenu(iDisplay,FreqCount,ResCounter,FirstMode);
|
|
|
|
|
|
//
|
|
// Update menu checks; mode status
|
|
//
|
|
|
|
CheckMenuItemCurrentMode( iDisplay );
|
|
|
|
|
|
//
|
|
// Put Hz menu next to current mode, or at the bottom
|
|
//
|
|
|
|
if (FreqMenuLocation == IDD_ONEMENUMOBILE)
|
|
{
|
|
MENUITEMINFO mii;
|
|
|
|
ZeroMemory(&mii, sizeof(mii));
|
|
mii.cbSize = sizeof(mii);
|
|
mii.fMask = MIIM_SUBMENU;
|
|
mii.hSubMenu = lpqrmi->FreqMenu[FREQMENU(lpqrmi->pCurrentdm)];
|
|
SetMenuItemInfo(lpqrmi->ModeMenu, FREQMENU(lpqrmi->pCurrentdm), MF_BYPOSITION, &mii);
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
if (FreqMenuLocation == IDD_ONEMENUBOTTOM)
|
|
{
|
|
PTCHAR szRefRate;
|
|
UINT flags=MF_POPUP;
|
|
|
|
szRefRate = GetResourceString(IDS_REFRESHRATE);
|
|
|
|
if ( !lpqrmi->FreqMenu[FREQMENU(lpqrmi->pCurrentdm)] )
|
|
{
|
|
flags = MF_GRAYED;
|
|
}
|
|
|
|
AppendMenu(lpqrmi->ModeMenu,MF_SEPARATOR,0,NULL);
|
|
AppendMenu(lpqrmi->ModeMenu, flags,
|
|
(UINT_PTR)lpqrmi->FreqMenu[FREQMENU(lpqrmi->pCurrentdm)],
|
|
szRefRate);
|
|
|
|
LocalFree(szRefRate);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (lpqrmi->ModeMenu);
|
|
}
|
|
|
|
|
|
//
|
|
//********************************************************************
|
|
//
|
|
// AppendMainMenu( VOID )
|
|
//
|
|
// Append main menu (from .rc file) to monitor menu
|
|
//
|
|
//********************************************************************
|
|
//
|
|
|
|
VOID AppendMainMenu()
|
|
{
|
|
|
|
#ifdef MAINWITHMODE
|
|
|
|
int n; // counter
|
|
|
|
//
|
|
// Add main menu to bottom of mode menu. These menu
|
|
// items come from MainMenu as defined in .rc file
|
|
//
|
|
|
|
AppendMenu(MonitorMenu,MF_SEPARATOR,0,NULL);
|
|
|
|
for (n=0; n < GetMenuItemCount(MainMenu); n++)
|
|
{
|
|
|
|
MENUITEMINFO mii;
|
|
|
|
//
|
|
// Set up mii struct to retrieve the length of
|
|
// each menu item string via GetMenuItemInfo().
|
|
//
|
|
|
|
ZeroMemory(&mii, sizeof(mii));
|
|
mii.cbSize = sizeof(mii);
|
|
mii.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID;
|
|
|
|
mii.cch = GetMenuString(MainMenu, n, NULL, 0, MF_BYPOSITION) +1;
|
|
|
|
//
|
|
// Allocate enough memory and read in the string.
|
|
//
|
|
|
|
if (mii.dwTypeData = LocalAlloc( LPTR, mii.cch*sizeof(TCHAR) ))
|
|
{
|
|
|
|
//
|
|
// Read in the string, get it's ID and append to the menu
|
|
//
|
|
|
|
if (GetMenuString(MainMenu, n, mii.dwTypeData, mii.cch,MF_BYPOSITION))
|
|
{
|
|
mii.wID=GetMenuItemID(MainMenu, n);
|
|
|
|
AppendMenu(MonitorMenu, MF_STRING, mii.wID, mii.dwTypeData);
|
|
}
|
|
|
|
LocalFree(mii.dwTypeData);
|
|
}
|
|
}
|
|
|
|
SetMenuDefaultItem(MonitorMenu,MENU_PROPERTIES,MF_BYCOMMAND);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
//
|
|
//********************************************************************
|
|
//
|
|
// GetMonitorMenu( BOOL )
|
|
//
|
|
// Build all mode menus with each resolution/BPP having a
|
|
// pointer to its own frequency submenu
|
|
//
|
|
//********************************************************************
|
|
//
|
|
|
|
HMENU GetMonitorMenu ( BOOL bNeedtoSort )
|
|
{
|
|
|
|
|
|
if (!MonitorMenu)
|
|
{
|
|
//
|
|
// Use Modemenu of iDisplay==0 as the monitor menu
|
|
//
|
|
|
|
if (iMonitors == 1)
|
|
{
|
|
MonitorMenu = GetModeMenu(0, bNeedtoSort);
|
|
}
|
|
else
|
|
{
|
|
INT iDisplay;
|
|
|
|
MonitorMenu = CreatePopupMenu();
|
|
|
|
for (iDisplay=0; iDisplay < iMonitors; iDisplay++)
|
|
{
|
|
//
|
|
// append each monitor name to the main monitor menu
|
|
//
|
|
|
|
AppendMenu( MonitorMenu, MF_POPUP,
|
|
(UINT_PTR)GetModeMenu(iDisplay, bNeedtoSort),
|
|
(pMonitors[iDisplay].bPrimary ? pMonitors[iDisplay].PrimaryMonitorName : pMonitors[iDisplay].MonitorName) );
|
|
}
|
|
|
|
}
|
|
|
|
AppendMainMenu();
|
|
|
|
}
|
|
|
|
return MonitorMenu;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//********************************************************************
|
|
//
|
|
// SetMonitorDeviceInfo( BOOL bFirstTime )
|
|
//
|
|
// Set monitor info fields : primary? attached?
|
|
// and the correct monitor name, based on bPrimary
|
|
//
|
|
//********************************************************************
|
|
//
|
|
BOOL SetMonitorDeviceInfo( BOOL bFirstTime )
|
|
{
|
|
BOOL bFoundPrimary=FALSE;
|
|
BOOL bChange=FALSE;
|
|
PTCHAR szMonitorRes;
|
|
int iDisplay;
|
|
int n;
|
|
DISPLAY_DEVICE DispDev;
|
|
|
|
|
|
|
|
if ( (iMonitors > 1) && (lpfnEDD) )
|
|
{
|
|
|
|
szMonitorRes = GetResourceString(IDS_MONITOR);
|
|
|
|
DispDev.cb = sizeof(DispDev);
|
|
|
|
iDisplay=0;
|
|
|
|
for ( n=0; (lpfnEDD)(NULL, n, &DispDev, 0); n++ )
|
|
{
|
|
|
|
if ( !(DispDev.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) )
|
|
{
|
|
|
|
DWORD dwSize;
|
|
TCHAR index[8];
|
|
|
|
//
|
|
// For each display, get the monitor name, primary monitor name, & Device name
|
|
// Alloc enough memory for MonitorName string to have up to 3 digits for the monitor index.
|
|
//
|
|
|
|
if (DispDev.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
|
|
{
|
|
bChange |= (pMonitors[iDisplay].bPrimary != TRUE);
|
|
pMonitors[iDisplay].bPrimary = TRUE;
|
|
bFoundPrimary=TRUE;
|
|
}
|
|
else
|
|
{
|
|
bChange |= (pMonitors[iDisplay].bPrimary != FALSE);
|
|
pMonitors[iDisplay].bPrimary = FALSE;
|
|
}
|
|
|
|
//
|
|
// Allocation sizes, device name, and primarymonitorname all never change
|
|
//
|
|
|
|
if (bFirstTime)
|
|
{
|
|
pMonitors[iDisplay].DeviceName = GlobalAlloc(GPTR, sizeof(TCHAR)*(lstrlen(DispDev.DeviceName)+1));
|
|
pMonitors[iDisplay].MonitorName = GlobalAlloc( GPTR, sizeof(TCHAR)*dwSize );
|
|
pMonitors[iDisplay].PrimaryMonitorName = GlobalAlloc( GPTR, sizeof(TCHAR)*dwSize );
|
|
}
|
|
|
|
// Memory allocation failed we can't continue
|
|
if (!pMonitors[iDisplay].DeviceName ||
|
|
!pMonitors[iDisplay].MonitorName ||
|
|
!pMonitors[iDisplay].PrimaryMonitorName)
|
|
return FALSE;
|
|
|
|
if (bFirstTime)
|
|
{
|
|
lstrcpy(pMonitors[iDisplay].DeviceName, DispDev.DeviceName);
|
|
|
|
dwSize = lstrlen(DispDev.DeviceString) + lstrlen(szMonitorRes);
|
|
dwSize += lstrlen(TEXT("Primary"));
|
|
|
|
wsprintf( pMonitors[iDisplay].PrimaryMonitorName, szMonitorRes,
|
|
TEXT("Primary "), TEXT(""), DispDev.DeviceString );
|
|
|
|
bChange = TRUE;
|
|
}
|
|
|
|
|
|
// this ensures we always have primary, 2,3,4, etc. (never a monitor 0 or 1)
|
|
//
|
|
|
|
_itot( (iDisplay + (bFoundPrimary ? 1 : 2)),index,8);
|
|
lstrcat(index,TEXT(" "));
|
|
|
|
wsprintf( pMonitors[iDisplay].MonitorName, szMonitorRes,
|
|
TEXT(""), index, DispDev.DeviceString);
|
|
|
|
|
|
bChange |= (pMonitors[iDisplay].bAttached != (BOOL)(DispDev.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP));
|
|
pMonitors[iDisplay].bAttached = (DispDev.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP);
|
|
|
|
iDisplay++;
|
|
}
|
|
|
|
DispDev.cb = sizeof(DispDev); // always reset before next call to be safe
|
|
}
|
|
|
|
LocalFree(szMonitorRes);
|
|
|
|
if (!bFirstTime)
|
|
{
|
|
for (n=0; n< iMonitors; n++)
|
|
{
|
|
ModifyMenu( MonitorMenu, n, MF_BYPOSITION | MF_POPUP, (UINT_PTR)pMonitors[n].ModeMenu,
|
|
(pMonitors[n].bPrimary ? pMonitors[n].PrimaryMonitorName : pMonitors[n].MonitorName));
|
|
}
|
|
}
|
|
}
|
|
|
|
return bChange;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//********************************************************************
|
|
//
|
|
// BuildMonitorArray( )
|
|
//
|
|
// Allocate & fill in a monitorinfo struct for each display device
|
|
//
|
|
//********************************************************************
|
|
//
|
|
|
|
BOOL BuildMonitorArray( )
|
|
{
|
|
|
|
int iDisplay;
|
|
DISPLAY_DEVICE DispDev;
|
|
|
|
|
|
|
|
//
|
|
// Find the number of monitors/displaydevices
|
|
// alloc a monitorinfo struct per monitor
|
|
//
|
|
|
|
iMonitors = 1;
|
|
// getSysMet != EnumDispDevices!!! (netmtg, etc.)
|
|
// iMonitors = max(GetSystemMetrics(SM_CMONITORS),1);
|
|
|
|
|
|
//
|
|
// EDSEx is win98 & NT5 only -- the ones with multimonitor, the check for EDD is just to
|
|
// check that we have the api we're going to call. win95 osr2.5/NT4 are single-mon only
|
|
//
|
|
|
|
if (lpfnEDSEx && lpfnEDD)
|
|
{
|
|
DispDev.cb = sizeof(DispDev);
|
|
|
|
iMonitors=0;
|
|
for (iDisplay=0; (lpfnEDD)(NULL, iDisplay, &DispDev, 0); iDisplay++)
|
|
{
|
|
if ( !(DispDev.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) )
|
|
{
|
|
iMonitors++;
|
|
}
|
|
}
|
|
}
|
|
|
|
pMonitors = GlobalAlloc(GPTR, iMonitors*sizeof(QRMONITORINFO));
|
|
|
|
|
|
if (pMonitors)
|
|
{
|
|
//
|
|
// On a multimon system, get the display device info --
|
|
// monitor and device names + primary? and attached?
|
|
//
|
|
|
|
if ( (iMonitors > 1) && (lpfnEDD) )
|
|
{
|
|
SetMonitorDeviceInfo( TRUE );
|
|
}
|
|
else
|
|
{
|
|
pMonitors[0].DeviceName = NULL;
|
|
pMonitors[0].MonitorName = NULL;
|
|
pMonitors[0].PrimaryMonitorName = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iMonitors = 0;
|
|
}
|
|
|
|
return pMonitors != NULL;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//********************************************************************
|
|
//
|
|
// BuildDevmodeLists( )
|
|
//
|
|
// Enumerate all devmodes for each display device into an array.
|
|
// Sort them, remove duplicates, and filter out 4bpp modes
|
|
// Create a popup menu for each display device (put all modes
|
|
// on the main menu if it is a single monitor machine)
|
|
//
|
|
//********************************************************************
|
|
//
|
|
|
|
BOOL BuildDevmodeLists( )
|
|
{
|
|
|
|
DEVMODE DisplayMode; // temporary devmode storage
|
|
BOOL bShrink=FALSE; // set if iModes ever decreases
|
|
int nModes,
|
|
n,
|
|
iDisplay; // counters
|
|
|
|
LPDEVMODEINFO lpdm;
|
|
LPQRMONITORINFO lpqrmi;
|
|
|
|
|
|
|
|
DisplayMode.dmSize= sizeof(DEVMODE);
|
|
|
|
//
|
|
// Fill in each display's/monitor's mode list
|
|
//
|
|
|
|
for (iDisplay=0; iDisplay < iMonitors; iDisplay++)
|
|
{
|
|
DWORD dwFlags = 0;
|
|
|
|
lpqrmi = &pMonitors[iDisplay];
|
|
|
|
lpqrmi->ModeMenu = NULL;
|
|
lpqrmi->FreqMenu = NULL;
|
|
lpqrmi->iModes = 0;
|
|
lpqrmi->pModes = NULL;
|
|
lpqrmi->pCurrentdm = NULL;
|
|
|
|
|
|
//
|
|
// Find the number of modes known by driver for each monitor
|
|
//
|
|
|
|
if (lpfnEDSEx)
|
|
{
|
|
for( nModes=0; (lpfnEDSEx)(pMonitors[iDisplay].DeviceName, nModes, &DisplayMode, dwFlags); nModes++)
|
|
;
|
|
}
|
|
else
|
|
{
|
|
for( nModes=0; EnumDisplaySettings(pMonitors[iDisplay].DeviceName, nModes, &DisplayMode); nModes++)
|
|
;
|
|
}
|
|
|
|
//
|
|
// Get space for all modes
|
|
//
|
|
|
|
lpqrmi->pModes = (LPDEVMODEINFO) GlobalAlloc( GPTR, nModes*sizeof(DEVMODEINFO) );
|
|
lpdm = lpqrmi->pModes;
|
|
|
|
if( !lpdm )
|
|
{
|
|
DestroyModeMenu( iDisplay, FALSE, FALSE );
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// Get all display modes into the pModes array
|
|
//
|
|
|
|
for( n=0; n<nModes; n++ )
|
|
{
|
|
lpdm[n].dm.dmSize= sizeof(DEVMODE);
|
|
|
|
//
|
|
// Get next mode into next spot in pModes
|
|
//
|
|
|
|
if (lpfnEDSEx)
|
|
{
|
|
(lpfnEDSEx)(pMonitors[iDisplay].DeviceName, n, &lpdm[n].dm, dwFlags );
|
|
}
|
|
else
|
|
{
|
|
EnumDisplaySettings(pMonitors[iDisplay].DeviceName, n, &lpdm[n].dm );
|
|
}
|
|
|
|
|
|
//
|
|
// If any Hz is NOT 0 or 1 (default), then turn on Freq flag.
|
|
// This will be true on NT. Win95 will always return 0 or 1.
|
|
//
|
|
|
|
if ( HZ(&lpdm[n].dm) && (HZ(&lpdm[n].dm) != 1) )
|
|
QuickResFlags |= QF_SHOWFREQS;
|
|
}
|
|
|
|
|
|
//
|
|
// sort them according to QF_SORTBYBPP :
|
|
// (1) BPP X Y HZ or (2) X Y BPP HZ
|
|
//
|
|
|
|
qsort( (void*) lpdm,
|
|
(size_t) nModes,
|
|
(size_t) sizeof(DEVMODEINFO),
|
|
( int (_cdecl*)(const void*,const void*) ) CompareDevmodes );
|
|
|
|
|
|
//
|
|
// Filter out any duplicate devmodes return by the driver
|
|
// and any modes with x resolution < 640 pixels. We dont
|
|
// want to show ModeX modes (320x200, 320x240, etc.)
|
|
//
|
|
|
|
if (nModes > 1 )
|
|
{
|
|
|
|
for (n=0; n+1 < nModes; )
|
|
{
|
|
|
|
if (XRES(&lpdm[n].dm) < 640)
|
|
{
|
|
nModes--;
|
|
bShrink = TRUE;
|
|
MoveMemory( &lpdm[n],
|
|
&lpdm[n+1],
|
|
(nModes-n)*sizeof(DEVMODEINFO) );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If consecutive devmodes are identical, then copy the next
|
|
// one over the dup and decrement iModes (# of devmodes).
|
|
//
|
|
|
|
while ( CompareDevmodes(&lpdm[n],&lpdm[n+1]) == 0 )
|
|
{
|
|
//
|
|
// Don't go past the last devmode
|
|
//
|
|
|
|
if (n+2 < nModes--)
|
|
{
|
|
bShrink = TRUE;
|
|
MoveMemory( &lpdm[n],
|
|
&lpdm[n+1],
|
|
(nModes-n)*sizeof(DEVMODEINFO) );
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
n++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Check CDS return value for all modes and eliminate all 4bpp
|
|
// modes that have a corresponding 8bpp mode at the same res
|
|
//
|
|
|
|
for (n=0; n < nModes; n++)
|
|
{
|
|
|
|
CDSTEST(&lpdm[n]) = (WORD)ChangeDisplaySettingsEx( lpqrmi->DeviceName, &lpdm[n].dm,
|
|
NULL, CDS_TEST, 0);
|
|
|
|
//
|
|
// Filter out all 4BPP modes that have an 8BPP mode at the same resolution
|
|
//
|
|
|
|
if (BPP(&lpdm[n].dm)==8)
|
|
{
|
|
INT i;
|
|
|
|
for (i=0; i < n; )
|
|
{
|
|
|
|
if ( (BPP (&lpdm[i].dm) == 4) &&
|
|
(XRES(&lpdm[n].dm) == XRES(&lpdm[i].dm)) &&
|
|
(YRES(&lpdm[n].dm) == YRES(&lpdm[i].dm)) )
|
|
{
|
|
nModes--;
|
|
bShrink = TRUE;
|
|
MoveMemory( &lpdm[i],
|
|
&lpdm[i+1],
|
|
(nModes-i)*sizeof(DEVMODEINFO) );
|
|
n--;
|
|
}
|
|
else
|
|
{
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// nModes might have decreased; might as well free up some memory.
|
|
// Note that iModes could NOT have increased, so the ReAlloc will be okay.
|
|
//
|
|
|
|
if (bShrink)
|
|
{
|
|
lpqrmi->pModes = (LPDEVMODEINFO) GlobalReAlloc(lpqrmi->pModes,
|
|
nModes*sizeof(DEVMODEINFO),
|
|
GMEM_MOVEABLE );
|
|
}
|
|
|
|
lpqrmi->iModes = nModes;
|
|
|
|
//
|
|
// At most, we need 1 freqmenu per mode (actually it's always < #modes.)
|
|
// Note : separators take up 1 (unused) hmenu in the array, so it would
|
|
// that we need hmenus > #modes, when "all modes on main menu". But,
|
|
// in that case, we dont use the freq submenus at all. :)
|
|
//
|
|
|
|
lpqrmi->FreqMenu = (HMENU*) GlobalAlloc(GPTR,nModes*sizeof(HMENU));
|
|
for (n=0; n < nModes; n++)
|
|
{
|
|
lpqrmi->FreqMenu[n] = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Get modeflags from registry or zero out modeflags[]
|
|
//
|
|
|
|
GetDevmodeFlags(iDisplay);
|
|
|
|
|
|
//
|
|
// Call GetModeMenu to put all strings/popups in place
|
|
// Current mode will be the best until user changes it.
|
|
//
|
|
|
|
GetModeMenu( iDisplay, FALSE );
|
|
|
|
VALIDMODE(lpqrmi->pCurrentdm) = MODE_BESTHZ;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
//********************************************************************
|
|
//
|
|
// DestroyDevmodeLists( )
|
|
//
|
|
// free all the memory allocated for modes and menus for each device
|
|
//
|
|
//********************************************************************
|
|
//
|
|
|
|
BOOL DestroyDevmodeLists()
|
|
{
|
|
|
|
int iDisplay;
|
|
LPQRMONITORINFO lpqrmi;
|
|
|
|
|
|
|
|
for (iDisplay=0; iDisplay < iMonitors; iDisplay++)
|
|
{
|
|
|
|
lpqrmi = &pMonitors[iDisplay];
|
|
|
|
//
|
|
// DestroyModeMenu has freed all the individual menus in this array for us
|
|
//
|
|
|
|
if (lpqrmi->FreqMenu)
|
|
{
|
|
GlobalFree(lpqrmi->FreqMenu);
|
|
lpqrmi->FreqMenu = NULL;
|
|
}
|
|
|
|
if (lpqrmi->pModes)
|
|
{
|
|
GlobalFree(lpqrmi->pModes);
|
|
lpqrmi->pModes = NULL;
|
|
}
|
|
|
|
lpqrmi->iModes = 0;
|
|
lpqrmi->pCurrentdm = NULL;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//********************************************************************
|
|
//
|
|
// DoProperties( )
|
|
//
|
|
// Calls the control panel applet to show 'Display Properties'
|
|
// specifically the display settings
|
|
//
|
|
//********************************************************************
|
|
//
|
|
|
|
void DoProperties( )
|
|
{
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION pi;
|
|
TCHAR lpszProperties[64] = DISPLAYPROPERTIES;
|
|
|
|
GetStartupInfo( &si );
|
|
|
|
|
|
//
|
|
// Start it up.
|
|
//
|
|
|
|
if (CreateProcess(NULL, lpszProperties, NULL, NULL, FALSE,
|
|
0, NULL, NULL, &si, &pi))
|
|
{
|
|
|
|
|
|
//
|
|
// Dont care what wait return value is, but we want
|
|
// to 'disable' tray icon for a minute or until the
|
|
// user kills desk.cpl
|
|
//
|
|
|
|
WaitForSingleObject( pi.hProcess, 60*1000 );
|
|
|
|
|
|
CloseHandle ( pi.hThread );
|
|
CloseHandle ( pi.hProcess );
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//********************************************************************
|
|
//
|
|
// AppWndProc(HWND, UINT, WPARAM, LPARAM)
|
|
//
|
|
// Main window proc to process messages
|
|
//
|
|
//********************************************************************
|
|
//
|
|
|
|
LRESULT CALLBACK AppWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
|
|
POINT pt; // Get cursor pos for the menu placement
|
|
INT i;
|
|
|
|
switch (msg)
|
|
{
|
|
|
|
case WM_CREATE:
|
|
|
|
//
|
|
// Add icon to tray next to time
|
|
//
|
|
|
|
TrayMessage(hwnd, NIM_ADD, TRAY_ID, AppIcon);
|
|
|
|
break;
|
|
|
|
|
|
case WM_DESTROY:
|
|
|
|
//
|
|
// Remove icon from tray.
|
|
//
|
|
|
|
TrayMessage(hwnd, NIM_DELETE, TRAY_ID, NULL );
|
|
|
|
PostQuitMessage(0);
|
|
|
|
break;
|
|
|
|
|
|
case WM_DISPLAYCHANGE:
|
|
|
|
//
|
|
// New settings. Reset pCurrentdm as index in pModes
|
|
// No need to destroy/rebuild the mode menu, but bPrimary,
|
|
// bAttached may have changed.
|
|
//
|
|
// The Waiting flag makes this a no-op, when its a qres-initiated change
|
|
//
|
|
|
|
if (!Waiting)
|
|
{
|
|
for (i=0; i<iMonitors; i++)
|
|
{
|
|
pMonitors[i].pCurrentdm = NULL;
|
|
CheckMenuItemCurrentMode(i);
|
|
}
|
|
|
|
SetMonitorDeviceInfo( FALSE );
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
#if 0
|
|
hard to understand when and why we get this message. better leave it alone
|
|
case WM_DEVICECHANGE:
|
|
|
|
if (wParam == DBT_CONFIGCHANGED ||
|
|
wParam == DBT_MONITORCHANGE)
|
|
{
|
|
for (i=0; i<iMonitors; i++)
|
|
{
|
|
DestroyModeMenu( i, FALSE, FALSE );
|
|
SetDevmodeFlags( i, TRUE );
|
|
}
|
|
|
|
if (MonitorMenu)
|
|
{
|
|
DestroyMenu(MonitorMenu);
|
|
MonitorMenu = NULL;
|
|
}
|
|
DestroyDevmodeLists();
|
|
BuildDevmodeLists();
|
|
MonitorMenu = GetMonitorMenu( TRUE );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
|
|
switch (LOWORD(wParam))
|
|
{
|
|
|
|
case MENU_CLOSE:
|
|
|
|
PostMessage(hwnd, WM_CLOSE, 0, 0);
|
|
|
|
break;
|
|
|
|
case MENU_PROPERTIES:
|
|
|
|
//
|
|
// Start control panel applet
|
|
//
|
|
|
|
DoProperties();
|
|
|
|
break;
|
|
|
|
|
|
case MENU_ABOUT:
|
|
|
|
//
|
|
// Show a generic about box
|
|
//
|
|
|
|
MsgBox(IDS_ABOUT, 0, MB_OK );
|
|
|
|
break;
|
|
|
|
|
|
case MENU_OPTIONS:
|
|
|
|
//
|
|
// After showing options dlg box, show mode menu again
|
|
//
|
|
|
|
if (fShowFreqs)
|
|
DialogBox(hInstApp, MAKEINTRESOURCE(NTOptions), NULL, NTOptionsDlgProc);
|
|
else
|
|
DialogBox(hInstApp, MAKEINTRESOURCE(W95Options),NULL, W95OptionsDlgProc);
|
|
|
|
SetTimer(hwnd, TRAY_ID, 10, NULL);
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
{
|
|
|
|
//
|
|
// Change devmode to pModes[OffsetPdev]
|
|
//
|
|
|
|
INT OffsetPdev;
|
|
INT iDisplay;
|
|
|
|
//
|
|
// The menu item is an offset from MENU_RES
|
|
// of the selected item.
|
|
//
|
|
|
|
iDisplay = LOWORD(wParam) / MENU_RES - 1;
|
|
OffsetPdev = LOWORD(wParam) % MENU_RES;
|
|
|
|
//
|
|
// Check that the offset is within range
|
|
//
|
|
|
|
if( OffsetPdev >= 0 && OffsetPdev < pMonitors[iDisplay].iModes )
|
|
{
|
|
|
|
//
|
|
// if different from current devmode then change it
|
|
//
|
|
|
|
if ( CompareDevmodes( &pMonitors[iDisplay].pModes[OffsetPdev],
|
|
pMonitors[iDisplay].pCurrentdm) )
|
|
{
|
|
SetMode(hwnd, iDisplay, OffsetPdev);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
case WM_TIMER:
|
|
|
|
//
|
|
// Left click was not a double-click
|
|
//
|
|
|
|
KillTimer(hwnd, TRAY_ID);
|
|
GetCursorPos(&pt);
|
|
SetForegroundWindow(hwnd);
|
|
|
|
//
|
|
// Create and/or Get resolutions menu
|
|
//
|
|
|
|
TrackPopupMenu(GetMonitorMenu( FALSE ), TPM_LEFTBUTTON,
|
|
pt.x, pt.y, 0, hwnd, NULL);
|
|
|
|
break;
|
|
|
|
|
|
case TRAY_MSG:
|
|
{
|
|
|
|
//
|
|
// No messages processed while waiting on
|
|
// a dlg/msg box to return
|
|
//
|
|
|
|
if (!Waiting)
|
|
{
|
|
|
|
switch (lParam)
|
|
{
|
|
case WM_RBUTTONUP:
|
|
|
|
//
|
|
// Properties, about, Exit
|
|
//
|
|
|
|
SetForegroundWindow(hwnd);
|
|
GetCursorPos(&pt);
|
|
|
|
TrackPopupMenu(MainMenu, TPM_RIGHTBUTTON,
|
|
pt.x, pt.y, 0, hwnd, NULL);
|
|
|
|
break;
|
|
|
|
|
|
case WM_LBUTTONDOWN:
|
|
|
|
//
|
|
// Resolutions menu
|
|
//
|
|
|
|
SetTimer(hwnd, TRAY_ID, GetDoubleClickTime()+10, NULL);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_LBUTTONDBLCLK:
|
|
|
|
//
|
|
// start control panel applet
|
|
//
|
|
|
|
KillTimer(hwnd, TRAY_ID);
|
|
DoProperties();
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return DefWindowProc(hwnd,msg,wParam,lParam);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
//********************************************************************
|
|
//
|
|
// MsgBox(int, UINT, UINT)
|
|
//
|
|
// Generic messagebox function that can print a value into
|
|
// a format string
|
|
//
|
|
//********************************************************************
|
|
//
|
|
|
|
int MsgBox(int id, UINT value, UINT flags)
|
|
{
|
|
|
|
PTCHAR msgboxtext=NULL; // message box body text
|
|
INT ret = 0;
|
|
MSGBOXPARAMS mb;
|
|
|
|
|
|
//
|
|
// Ignore tray clicks while msgbox is up, and
|
|
// Show at least an OK button.
|
|
//
|
|
|
|
Waiting = TRUE;
|
|
if (flags == 0)
|
|
{
|
|
flags = MB_OK | MB_USERICON;
|
|
}
|
|
|
|
|
|
//
|
|
// Can print a value into a format string, if value!=0.
|
|
//
|
|
|
|
if (value)
|
|
{
|
|
PTCHAR msgboxfmt; // body test format
|
|
|
|
if (msgboxfmt = GetResourceString ( id ))
|
|
{
|
|
if (msgboxtext = LocalAlloc ( LPTR, sizeof(TCHAR)*
|
|
(lstrlen(msgboxfmt)+INT_FORMAT_TO_5_DIGITS+1)))
|
|
{
|
|
wsprintf(msgboxtext,msgboxfmt,value);
|
|
}
|
|
|
|
LocalFree( msgboxfmt );
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
msgboxtext = GetResourceString ( id );
|
|
}
|
|
|
|
|
|
if (msgboxtext)
|
|
{
|
|
|
|
mb.cbSize = sizeof(mb);
|
|
mb.hwndOwner = NULL;
|
|
mb.hInstance = hInstApp;
|
|
mb.lpszText = msgboxtext;
|
|
mb.lpszCaption = szAppName;
|
|
mb.dwStyle = flags;
|
|
mb.lpszIcon = szAppName;
|
|
mb.dwContextHelpId = 0;
|
|
mb.lpfnMsgBoxCallback = NULL;
|
|
mb.dwLanguageId = MAKELANGID (LANG_NEUTRAL, SUBLANG_NEUTRAL);;
|
|
|
|
|
|
//
|
|
// Special API for the about box. otherwise, use Messageboxindirect
|
|
//
|
|
|
|
if (id == IDS_ABOUT)
|
|
{
|
|
ret = ShellAbout(mb.hwndOwner, mb.lpszCaption, mb.lpszText, AppIcon);
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
if (flags & MB_USERICON)
|
|
{
|
|
//
|
|
// only use MessageBoxIndirect if we have to.
|
|
// has problems on win9x.
|
|
//
|
|
|
|
ret = MessageBoxIndirect(&mb);
|
|
}
|
|
|
|
else
|
|
{
|
|
//
|
|
// MessageBoxEx works great on both NT and Win95
|
|
//
|
|
|
|
ret = MessageBoxEx ( mb.hwndOwner, mb.lpszText, mb.lpszCaption,
|
|
mb.dwStyle, (WORD)mb.dwLanguageId );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free string memory; start processing tray msgs again
|
|
//
|
|
|
|
LocalFree( msgboxtext );
|
|
}
|
|
|
|
Waiting = FALSE;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
//********************************************************************
|
|
//
|
|
// WinMain
|
|
//
|
|
//********************************************************************
|
|
//
|
|
|
|
int NEAR PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
|
|
{
|
|
WNDCLASS cls;
|
|
MSG msg;
|
|
HWND hwnd;
|
|
INT iDisplay;
|
|
HINSTANCE hInstUser;
|
|
|
|
hInstApp = hInst;
|
|
szAppName = GetResourceString( IDS_TITLE );
|
|
|
|
|
|
//
|
|
// App is already running. Do not start a 2nd instance
|
|
//
|
|
|
|
if ( FindWindow( szAppName, szAppName ) )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (hInstUser=GetModuleHandle(TEXT("user32.dll")))
|
|
{
|
|
lpfnEDD = GetProcAddress( hInstUser, ENUMDISPLAYDEVICES );
|
|
lpfnEDSEx = GetProcAddress( hInstUser, ENUMDISPLAYSETTINGSEX );
|
|
}
|
|
|
|
|
|
AppIcon = LoadIcon(hInst,szAppName);
|
|
|
|
|
|
//
|
|
// Register a class for the main application window
|
|
//
|
|
|
|
cls.lpszClassName = szAppName;
|
|
cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
|
cls.hInstance = hInstApp;
|
|
cls.hIcon = AppIcon;
|
|
cls.hCursor = LoadCursor(NULL,IDC_ARROW);
|
|
cls.lpszMenuName = szAppName;
|
|
cls.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
|
|
cls.lpfnWndProc = AppWndProc;
|
|
cls.cbWndExtra = 0;
|
|
cls.cbClsExtra = 0;
|
|
|
|
if (!RegisterClass(&cls))
|
|
return FALSE;
|
|
|
|
hwnd = CreateWindow(szAppName,
|
|
szAppName,
|
|
WS_OVERLAPPEDWINDOW,
|
|
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
|
|
NULL, NULL,
|
|
hInstApp, NULL);
|
|
|
|
|
|
//
|
|
// Properties, about, exit - properties is the default
|
|
//
|
|
|
|
MainMenu = GetSubMenu(GetMenu(hwnd), 0);
|
|
SetMenuDefaultItem(MainMenu,MENU_PROPERTIES,MF_BYCOMMAND);
|
|
|
|
|
|
//
|
|
// Get flags from registry and build the modemenu
|
|
// from scratch.
|
|
//
|
|
|
|
GetQuickResFlags( );
|
|
|
|
if (!BuildMonitorArray())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!BuildDevmodeLists())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// Update tray tooltip to be current resolution
|
|
//
|
|
|
|
TrayMessage( hwnd, NIM_MODIFY, TRAY_ID, AppIcon );
|
|
|
|
|
|
//
|
|
// Polling messages from event queue
|
|
//
|
|
|
|
while (GetMessage(&msg, NULL, 0, 0))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
|
|
//
|
|
// write flags to registry
|
|
//
|
|
|
|
SaveAllSettings();
|
|
|
|
|
|
//
|
|
// Free up dynamically allocated globals.
|
|
//
|
|
|
|
LocalFree ( szAppName );
|
|
|
|
for (iDisplay=0; iDisplay<iMonitors; iDisplay++)
|
|
{
|
|
if (pMonitors[iDisplay].DeviceName)
|
|
GlobalFree(pMonitors[iDisplay].DeviceName);
|
|
|
|
if (pMonitors[iDisplay].MonitorName)
|
|
GlobalFree(pMonitors[iDisplay].MonitorName);
|
|
|
|
if (pMonitors[iDisplay].PrimaryMonitorName)
|
|
GlobalFree(pMonitors[iDisplay].PrimaryMonitorName);
|
|
|
|
GlobalFree(pMonitors[iDisplay].pModes);
|
|
|
|
GlobalFree(pMonitors[iDisplay].FreqMenu);
|
|
}
|
|
|
|
GlobalFree(pMonitors);
|
|
|
|
|
|
return (int)msg.wParam;
|
|
}
|
|
|
|
|
|
//
|
|
//********************************************************************
|
|
//
|
|
// TrayMessage (HWND, DWORD, UINT, HICON )
|
|
//
|
|
// Add/remove icon to/from tray next to the time
|
|
//
|
|
//********************************************************************
|
|
//
|
|
|
|
BOOL TrayMessage(HWND hwnd, DWORD msg, UINT id, HICON hIcon )
|
|
{
|
|
|
|
NOTIFYICONDATA tnd;
|
|
PTCHAR Res=NULL;
|
|
PTCHAR Hz=NULL;
|
|
UINT uDisplay=0;
|
|
UINT uNewlen=0;
|
|
|
|
tnd.cbSize = sizeof(NOTIFYICONDATA);
|
|
tnd.hWnd = hwnd;
|
|
tnd.uID = id;
|
|
tnd.szTip[0] = '\0';
|
|
tnd.uFlags = NIF_MESSAGE|NIF_ICON|NIF_TIP;
|
|
tnd.uCallbackMessage = TRAY_MSG;
|
|
tnd.hIcon = hIcon;
|
|
|
|
|
|
//
|
|
// Changing tooltip text to match current resolution
|
|
// (Make sure pCurrentdm is valid / not NULL.)
|
|
//
|
|
|
|
if (msg == NIM_MODIFY)
|
|
{
|
|
|
|
do
|
|
{
|
|
|
|
if (pMonitors[uDisplay].pCurrentdm)
|
|
{
|
|
GetModeName(&(pMonitors[uDisplay].pCurrentdm->dm), &Res, &Hz);
|
|
|
|
//
|
|
// calculate how long the string will be (need to be sure it's < 64)
|
|
// old tip + new Res + Hz (if applicable) + ", " (if its not the 1st mon)
|
|
//
|
|
|
|
uNewlen = lstrlen(tnd.szTip);
|
|
|
|
if (Res)
|
|
{
|
|
uNewlen += lstrlen(Res);
|
|
}
|
|
|
|
if (uDisplay > 0)
|
|
{
|
|
uNewlen += 2;
|
|
}
|
|
|
|
if (fShowFreqs && Hz)
|
|
{
|
|
uNewlen += lstrlen(Hz);
|
|
}
|
|
|
|
|
|
if (uNewlen < 64)
|
|
{
|
|
//
|
|
// this displays information will fit in the tooltip
|
|
// add ", " if not 1st mon, then the Res, then Hz (if applicable)
|
|
//
|
|
|
|
if ( uDisplay > 0 )
|
|
{
|
|
lstrcat(tnd.szTip,TEXT(", "));
|
|
}
|
|
|
|
if (Res)
|
|
{
|
|
lstrcat(tnd.szTip,Res);
|
|
}
|
|
|
|
if (fShowFreqs && Hz)
|
|
{
|
|
lstrcat(tnd.szTip,Hz);
|
|
}
|
|
}
|
|
|
|
if (Res)
|
|
{
|
|
LocalFree(Res);
|
|
}
|
|
|
|
if (Hz)
|
|
{
|
|
LocalFree(Hz);
|
|
}
|
|
|
|
++uDisplay;
|
|
}
|
|
|
|
} while (uDisplay < (UINT)iMonitors);
|
|
|
|
}
|
|
|
|
//
|
|
// Adding the tray icon - Current devmode
|
|
// is not known so use AppName as tip
|
|
//
|
|
|
|
else
|
|
{
|
|
wsprintf(tnd.szTip, szAppName);
|
|
}
|
|
|
|
return Shell_NotifyIcon( msg, &tnd );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//*****************************************************************************
|
|
//
|
|
// KeepNewResDlgProc(HWND, UINT, WPARAM, LPARAM )
|
|
//
|
|
// User must enter Yes to keep new res, or we default back to the old res.
|
|
//
|
|
//*****************************************************************************
|
|
//
|
|
|
|
INT_PTR FAR PASCAL KeepNewResDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
|
|
//
|
|
// Save strings as static pointers, and free them only in YES/NO/ABORT/CANCEL,
|
|
// because they will otherwise disappear from a Win95 dialog box immediately
|
|
// after they are free'd.
|
|
//
|
|
|
|
static int NOTimeOut; // countdown to 0
|
|
static PTCHAR NewResString=NULL; // user friendly name for devmode
|
|
static PTCHAR NewHzString=NULL; // and frequency
|
|
static PTCHAR szAt=NULL; // ", at"
|
|
static PTCHAR TotalString=NULL; // "<wid x ht>, at <freq>"
|
|
|
|
|
|
switch (message)
|
|
{
|
|
|
|
case WM_INITDIALOG: // initialize values and focus
|
|
|
|
{
|
|
|
|
//
|
|
// Initialize values and focus
|
|
//
|
|
|
|
DEVMODE dm;
|
|
|
|
|
|
//
|
|
// Ignore tray messages while waiting for yes/no.
|
|
// Wait KEEP_RES_TIMEOUT seconds.
|
|
//
|
|
|
|
Waiting=TRUE;
|
|
|
|
|
|
//
|
|
// Get current devmode; lparam is the iDisplay
|
|
//
|
|
GetCurrentDevMode( (INT)lParam, &dm );
|
|
|
|
|
|
//
|
|
// Get user friendly strings (concatenate Res & Hz, if applicable)
|
|
//
|
|
|
|
GetModeName( &dm, &NewResString, &NewHzString);
|
|
|
|
if (NewResString)
|
|
{
|
|
if (fShowFreqs && NewHzString)
|
|
{
|
|
szAt = GetResourceString ( IDS_AT );
|
|
|
|
//
|
|
// Replace 2nd text item of msgbox
|
|
//
|
|
|
|
if (TotalString = LocalAlloc ( LPTR, sizeof(TCHAR)*
|
|
( lstrlen(NewResString)+
|
|
lstrlen(NewHzString)+
|
|
lstrlen(szAt)+
|
|
1 ) ))
|
|
{
|
|
lstrcpy(TotalString, NewResString);
|
|
lstrcat(TotalString, szAt);
|
|
lstrcat(TotalString, NewHzString);
|
|
|
|
SetDlgItemText(hDlg, IDTEXT2, TotalString);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetDlgItemText(hDlg, IDTEXT2, NewResString);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set timeout length and start waiting
|
|
//
|
|
|
|
NOTimeOut=KEEP_RES_TIMEOUT;
|
|
|
|
SetTimer(hDlg,IDD_COUNTDOWN,1000,NULL);
|
|
|
|
return (TRUE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
case WM_TIMER:
|
|
|
|
{
|
|
PTCHAR NoTextFmt=NULL; // "NO: %d"
|
|
PTCHAR NoText=NULL; // e.g. "NO: 15"
|
|
|
|
//
|
|
// Still counting down
|
|
//
|
|
|
|
if ( NOTimeOut >= 0 )
|
|
{
|
|
//
|
|
// Get format string for NO Button.
|
|
// Write it to NoText String and to dlg box
|
|
//
|
|
|
|
NoTextFmt = GetResourceString ( IDS_NOTEXT );
|
|
|
|
if (NoTextFmt)
|
|
{
|
|
NoText = LocalAlloc ( LPTR, sizeof(TCHAR)*
|
|
( lstrlen(NoTextFmt)+1 ) );
|
|
wsprintf(NoText, NoTextFmt, NOTimeOut--);
|
|
|
|
SetDlgItemText(hDlg, IDNO, NoText);
|
|
|
|
LocalFree ( NoTextFmt );
|
|
LocalFree ( NoText );
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
//
|
|
// Give up on the user - return NO
|
|
//
|
|
|
|
KillTimer(hDlg, IDD_COUNTDOWN);
|
|
SendMessage(hDlg, WM_COMMAND, IDNO, 0);
|
|
}
|
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case WM_COMMAND:
|
|
|
|
//
|
|
// Start processing tray messages again
|
|
//
|
|
|
|
Waiting=FALSE;
|
|
|
|
switch (LOWORD(wParam))
|
|
|
|
{
|
|
|
|
//
|
|
// return value based on the button pressed
|
|
//
|
|
|
|
case IDYES :
|
|
case IDNO :
|
|
case IDABORT :
|
|
case IDCANCEL :
|
|
|
|
//
|
|
// LocalFree handles NULL pointers gracefully (does nothing)
|
|
//
|
|
|
|
LocalFree ( szAt );
|
|
LocalFree ( NewResString );
|
|
LocalFree ( NewHzString );
|
|
LocalFree ( TotalString );
|
|
|
|
EndDialog(hDlg, LOWORD(wParam));
|
|
return (TRUE);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
} // switch (wParam)
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
} // switch (message)
|
|
|
|
|
|
return (FALSE); // Didn't process a message
|
|
|
|
|
|
} // KeepNewResDlgProc()
|
|
|
|
|
|
|
|
//
|
|
//*****************************************************************************
|
|
//
|
|
// NTOptionsDlgProc(HWND, UINT, WPARAM, LPARAM )
|
|
//
|
|
//
|
|
//
|
|
//*****************************************************************************
|
|
//
|
|
|
|
INT_PTR FAR PASCAL NTOptionsDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
INT i;
|
|
static WORD SaveQRFlags;
|
|
|
|
switch (message)
|
|
{
|
|
|
|
case WM_INITDIALOG:
|
|
|
|
//
|
|
// Stop processing tray messages; check buttons properly
|
|
//
|
|
|
|
Waiting = TRUE;
|
|
SaveQRFlags = QuickResFlags;
|
|
|
|
CheckRadioButton(hDlg,IDD_SORT_RES,IDD_SORT_BPP,
|
|
(fSortByBPP ? IDD_SORT_BPP : IDD_SORT_RES) );
|
|
CheckRadioButton(hDlg,IDD_SUBMENUS,IDD_ALLMODEMENU, FreqMenuLocation );
|
|
|
|
CheckDlgButton(hDlg, IDD_UPDATEREG, fUpdateReg );
|
|
CheckDlgButton(hDlg, IDD_REMMODES, fRememberModes );
|
|
CheckDlgButton(hDlg, IDD_RESTARTREQ, fShowModesThatNeedRestart );
|
|
CheckDlgButton(hDlg, IDD_SHOWTESTED, fShowTestedModes );
|
|
|
|
return TRUE;
|
|
break;
|
|
|
|
|
|
case WM_COMMAND:
|
|
|
|
|
|
switch (LOWORD(wParam))
|
|
|
|
{
|
|
|
|
//
|
|
// Update buttons : sorting by BPP or Res?
|
|
//
|
|
|
|
case IDD_SORT_RES:
|
|
case IDD_SORT_BPP:
|
|
CheckRadioButton(hDlg,IDD_SORT_RES,IDD_SORT_BPP,LOWORD(wParam));
|
|
return TRUE;
|
|
break;
|
|
|
|
//
|
|
// Update buttons : where to display freq menus?
|
|
//
|
|
|
|
case IDD_SUBMENUS:
|
|
case IDD_ONEMENUMOBILE:
|
|
case IDD_ONEMENUBOTTOM:
|
|
case IDD_ALLMODEMENU:
|
|
CheckRadioButton(hDlg,IDD_SUBMENUS,IDD_ALLMODEMENU,LOWORD(wParam));
|
|
return TRUE;
|
|
break;
|
|
|
|
|
|
//
|
|
// Clear all registry remembered settings
|
|
// Make user verify he did this on purpose
|
|
//
|
|
|
|
case IDD_CLEARREG:
|
|
if (MsgBox(IDS_CLEARREG,
|
|
0,
|
|
MB_YESNO | MB_ICONQUESTION | MB_TASKMODAL)
|
|
== IDYES)
|
|
{
|
|
|
|
//
|
|
// Reset flags for all monitors; destroy and rebuild
|
|
// each mode menu
|
|
//
|
|
|
|
for (i=0; i<iMonitors; i++)
|
|
{
|
|
SetDevmodeFlags(i, TRUE);
|
|
VALIDMODE(pMonitors[i].pCurrentdm) = MODE_BESTHZ;
|
|
DestroyModeMenu( i, TRUE, FALSE);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
break;
|
|
|
|
|
|
//
|
|
// XOR QuickResFlags on and off
|
|
//
|
|
|
|
case IDD_UPDATEREG:
|
|
QuickResFlags ^= QF_UPDATEREG;
|
|
return TRUE;
|
|
break;
|
|
|
|
case IDD_REMMODES:
|
|
QuickResFlags ^= QF_REMMODES;
|
|
return TRUE;
|
|
break;
|
|
|
|
case IDD_RESTARTREQ:
|
|
QuickResFlags ^= QF_SHOWRESTART;
|
|
return TRUE;
|
|
break;
|
|
|
|
case IDD_SHOWTESTED:
|
|
QuickResFlags ^= QF_SHOWTESTED;
|
|
return TRUE;
|
|
break;
|
|
|
|
|
|
case IDOK:
|
|
{
|
|
|
|
BOOL bRebuildMenu = FALSE;
|
|
BOOL bNeedToSort = FALSE;
|
|
|
|
|
|
//
|
|
// See if sort order has changed.
|
|
//
|
|
|
|
if ( (IsDlgButtonChecked (hDlg, IDD_SORT_RES) && fSortByBPP) ||
|
|
(IsDlgButtonChecked (hDlg, IDD_SORT_BPP) && !fSortByBPP) )
|
|
{
|
|
QuickResFlags ^= QF_SORT_BYBPP;
|
|
bNeedToSort = TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// If "show modes that require restart", or "show tested "modes only",
|
|
// then rebuild menu is required
|
|
//
|
|
|
|
if ( (fShowModesThatNeedRestart != (SaveQRFlags & QF_SHOWRESTART)) ||
|
|
(fShowTestedModes != (SaveQRFlags & QF_SHOWTESTED))
|
|
)
|
|
{
|
|
bRebuildMenu = TRUE;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// see if FreqMenuLocation has changed
|
|
//
|
|
|
|
if (!IsDlgButtonChecked (hDlg, FreqMenuLocation))
|
|
{
|
|
WORD i;
|
|
|
|
//
|
|
// Freq menu location has changed; update & ask for rebuild
|
|
//
|
|
|
|
bRebuildMenu = TRUE;
|
|
|
|
for ( i=IDD_SUBMENUS; i <= IDD_ALLMODEMENU; i++ )
|
|
{
|
|
if (IsDlgButtonChecked (hDlg, i))
|
|
{
|
|
FreqMenuLocation = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If rebuilding and or resorting, just destroy & rebuild the menus
|
|
//
|
|
|
|
if ( bNeedToSort || bRebuildMenu )
|
|
{
|
|
for (i=0; i<iMonitors; i++)
|
|
DestroyModeMenu( i, TRUE, bNeedToSort);
|
|
}
|
|
|
|
SaveAllSettings();
|
|
|
|
Waiting = FALSE;
|
|
EndDialog(hDlg, LOWORD(wParam));
|
|
return TRUE;
|
|
break;
|
|
}
|
|
|
|
case IDCANCEL :
|
|
|
|
Waiting = FALSE;
|
|
QuickResFlags = SaveQRFlags;
|
|
EndDialog(hDlg, LOWORD(wParam));
|
|
return TRUE;
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
} // switch (wParam)
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
} // switch (message)
|
|
|
|
|
|
return FALSE; // Didn't process a message
|
|
|
|
|
|
} // NTOptionsDlgProc()
|
|
|
|
|
|
|
|
//
|
|
//*****************************************************************************
|
|
//
|
|
// W95OptionsDlgProc(HWND, UINT, WPARAM, LPARAM )
|
|
//
|
|
//
|
|
//
|
|
//*****************************************************************************
|
|
//
|
|
|
|
INT_PTR FAR PASCAL W95OptionsDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
|
|
INT i;
|
|
static WORD SaveQRFlags;
|
|
|
|
switch (message)
|
|
{
|
|
|
|
case WM_INITDIALOG:
|
|
|
|
//
|
|
// Stop processing tray messages; check buttons properly
|
|
//
|
|
|
|
Waiting = TRUE;
|
|
SaveQRFlags = QuickResFlags;
|
|
|
|
CheckRadioButton(hDlg,IDD_SORT_RES,IDD_SORT_BPP,
|
|
(fSortByBPP ? IDD_SORT_BPP : IDD_SORT_RES) );
|
|
|
|
CheckDlgButton(hDlg, IDD_UPDATEREG, fUpdateReg );
|
|
CheckDlgButton(hDlg, IDD_REMMODES, fRememberModes );
|
|
CheckDlgButton(hDlg, IDD_RESTARTREQ, fShowModesThatNeedRestart );
|
|
CheckDlgButton(hDlg, IDD_SHOWTESTED, fShowTestedModes );
|
|
|
|
return TRUE;
|
|
break;
|
|
|
|
|
|
case WM_COMMAND:
|
|
|
|
|
|
switch (LOWORD(wParam))
|
|
|
|
{
|
|
|
|
//
|
|
// Update buttons : sorting by BPP or Res?
|
|
//
|
|
|
|
case IDD_SORT_RES:
|
|
case IDD_SORT_BPP:
|
|
CheckRadioButton(hDlg,IDD_SORT_RES,IDD_SORT_BPP,LOWORD(wParam));
|
|
return TRUE;
|
|
break;
|
|
|
|
//
|
|
// Clear all registry remembered settings
|
|
// Make user verify he did this on purpose
|
|
//
|
|
|
|
case IDD_CLEARREG:
|
|
if (MsgBox(IDS_CLEARREG,
|
|
0,
|
|
MB_YESNO | MB_ICONQUESTION | MB_TASKMODAL)
|
|
== IDYES)
|
|
{
|
|
|
|
//
|
|
// Reset flags for all monitors; destroy and rebuild
|
|
// each mode menu
|
|
//
|
|
|
|
for (i=0; i<iMonitors; i++)
|
|
{
|
|
SetDevmodeFlags(i, TRUE);
|
|
VALIDMODE(pMonitors[i].pCurrentdm) = MODE_BESTHZ;
|
|
DestroyModeMenu( i, TRUE, FALSE);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
break;
|
|
|
|
|
|
//
|
|
// XOR QuickResFlags on and off
|
|
//
|
|
|
|
case IDD_UPDATEREG:
|
|
QuickResFlags ^= QF_UPDATEREG;
|
|
return TRUE;
|
|
break;
|
|
|
|
case IDD_REMMODES:
|
|
QuickResFlags ^= QF_REMMODES;
|
|
return TRUE;
|
|
break;
|
|
|
|
case IDD_RESTARTREQ:
|
|
QuickResFlags ^= QF_SHOWRESTART;
|
|
return TRUE;
|
|
break;
|
|
|
|
case IDD_SHOWTESTED:
|
|
QuickResFlags ^= QF_SHOWTESTED;
|
|
return TRUE;
|
|
break;
|
|
|
|
case IDOK:
|
|
{
|
|
BOOL bNeedToSort = FALSE;
|
|
|
|
//
|
|
// Note if the sort order has changed
|
|
//
|
|
|
|
if ( (IsDlgButtonChecked (hDlg, IDD_SORT_RES) && fSortByBPP) ||
|
|
(IsDlgButtonChecked (hDlg, IDD_SORT_BPP) && !fSortByBPP) )
|
|
{
|
|
QuickResFlags ^= QF_SORT_BYBPP;
|
|
bNeedToSort = TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// If "sort order", "show modes that require restart", or "show tested
|
|
// "modes only" changed, then destroy and rebuild old menu (resort if nec.)
|
|
//
|
|
|
|
if ( bNeedToSort ||
|
|
(fShowModesThatNeedRestart != (SaveQRFlags & QF_SHOWRESTART)) ||
|
|
(fShowTestedModes != (SaveQRFlags & QF_SHOWTESTED))
|
|
)
|
|
{
|
|
for (i=0; i<iMonitors; i++)
|
|
DestroyModeMenu( i, TRUE, bNeedToSort);
|
|
}
|
|
|
|
SaveAllSettings();
|
|
|
|
//
|
|
// No break after IDOK, by design.
|
|
// IDOK AND IDCANCEL : start processing tray clicks,
|
|
// and return ok/cancel as return value.
|
|
//
|
|
}
|
|
|
|
case IDCANCEL :
|
|
|
|
Waiting = FALSE;
|
|
EndDialog(hDlg, LOWORD(wParam));
|
|
return TRUE;
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
} // switch (wParam)
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
} // switch (message)
|
|
|
|
|
|
return FALSE; // Didn't process a message
|
|
|
|
|
|
} // W95OptionsDlgProc()
|