windows-nt/Source/XPSP1/NT/shell/osshell/accesory/quickres/quickres.c
2020-09-26 16:20:57 +08:00

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()