/*****************************************************************************\ FILE: SettingsPg.cpp DESCRIPTION: This code will display a "Settings" tab in the "Display Properties" dialog BryanSt 1/05/2001 Updated and Converted to C++ Copyright (C) Microsoft Corp 1993-2001. All rights reserved. \*****************************************************************************/ #include "priv.h" #include "SettingsPg.h" #include "DisplaySettings.h" #include "shlobjp.h" #include "shlwapi.h" #include "ntreg.hxx" #include "AdvAppearPg.h" #include #include #include #include #define EIS_NOT_INVALID 0x00000001 #define EIS_EXEC_INVALID_NEW_DRIVER 0x00000002 #define EIS_EXEC_INVALID_DEFAULT_DISPLAY_MODE 0x00000003 #define EIS_EXEC_INVALID_DISPLAY_DRIVER 0x00000004 #define EIS_EXEC_INVALID_OLD_DISPLAY_DRIVER 0x00000005 #define EIS_EXEC_INVALID_16COLOR_DISPLAY_MODE 0x00000006 #define EIS_EXEC_INVALID_DISPLAY_MODE 0x00000007 #define EIS_EXEC_INVALID_CONFIGURATION 0x00000008 #define EIS_EXEC_INVALID_DISPLAY_DEVICE 0x00000009 LRESULT CALLBACK MonitorWindowProc(HWND hwnd, UINT msg,WPARAM wParam,LPARAM lParam); LRESULT CALLBACK SliderSubWndProc (HWND hwndSlider, UINT uMsg, WPARAM wParam, LPARAM lParam, WPARAM uID, ULONG_PTR dwRefData); int ComputeNumberOfDisplayDevices(); BOOL MakeMonitorBitmap(int w, int h, LPCTSTR sz, HBITMAP *pBitmap, HBITMAP *pMaskBitmap, int cx, int cy, BOOL fSelected); typedef struct _APPEXT { TCHAR szKeyName[MAX_PATH]; TCHAR szDefaultValue[MAX_PATH]; struct _APPEXT* pNext; } APPEXT, *PAPPEXT; VOID CheckForDuplicateAppletExtensions(HKEY hkDriver); VOID DeskAESnapshot(HKEY hkExtensions, PAPPEXT* ppAppExt); VOID DeskAECleanup(PAPPEXT pAppExt); VOID DeskAEDelete(PTCHAR szDeleteFrom, PTCHAR mszExtensionsToRemove); #define SELECTION_THICKNESS 4 #define MONITOR_BORDER 1 #define REGSTR_VAL_SAFEBOOT TEXT("System\\CurrentControlSet\\Control\\SafeBoot\\Option") // Maximum number of monitors supported. #define MONITORS_MAX 10 #define PREVIEWAREARATIO 2 #define MM_REDRAWPREVIEW (WM_USER + 1) #define MM_MONITORMOVED (WM_USER + 2) #define ToolTip_Activate(hTT, activate) \ SendMessage(hTT, TTM_ACTIVATE, (WPARAM) activate, (LPARAM) 0) #define ToolTip_AddTool(hTT, lpti) \ SendMessage(hTT, TTM_ADDTOOL, (WPARAM) 0, (LPARAM) (lpti)) #define ToolTip_DelTool(hTT, lpti) \ SendMessage(hTT, TTM_DELTOOL, (WPARAM) 0, (LPARAM) (lpti)) #define ToolTip_GetCurrentTool(hTT, lpti) \ SendMessage(hTT, TTM_GETCURRENTTOOL, (WPARAM) 0, (LPARAM) (lpti)) #define ToolTip_RelayEvent(hTT, _msg, h, m, wp, lp) \ _msg.hwnd = h; _msg.message = m; _msg.wParam = wp; _msg.lParam = lp;\ SendMessage(hTT, TTM_RELAYEVENT, (WPARAM) 0, (LPARAM) &_msg); #define ToolTip_SetDelayTime(hTT, d, t) \ SendMessage(hTT, TTM_SETDELAYTIME, (WPARAM) d, (LPARAM)MAKELONG((t), 0)) #define ToolTip_SetToolInfo(hTT, lpti) \ SendMessage(hTT, TTM_SETTOOLINFO, (WPARAM) 0, (LPARAM) (lpti)) #define ToolTip_TrackActivate(hTT, bActivate, lpti) \ SendMessage(hTT, TTM_TRACKACTIVATE, (WPARAM) (bActivate), (LPARAM) (lpti)) #define ToolTip_TrackPosition(hTT, x, y) \ SendMessage(hTT, TTM_TRACKPOSITION, (WPARAM) 0, (LPARAM) MAKELONG((x), (y))) #define ToolTip_Update(hTT) \ SendMessage(hTT, TTM_UPDATE, (WPARAM) 0, (LPARAM) 0) VOID CDECL TRACE( PCTSTR pszMsg, ... ) /*++ Outputs a message to the setup log. Prepends "desk.cpl " to the strings and appends the correct newline chars (\r\n)== --*/ { TCHAR ach[1024+40]; // Largest path plus extra va_list vArgs; va_start(vArgs, pszMsg); StringCchVPrintf(ach, ARRAYSIZE(ach), pszMsg, vArgs); va_end(vArgs); OutputDebugString(ach); } #ifdef _WIN64 // // GetDlgItem and GetDlgCtrlID don't support INT_PTR's, // so we have to do it manually. // Fortunately, GetWindowLongPtr(GWLP_ID) actually returns a full 64-bit // value instead of truncating at 32-bits. // #define GetDlgCtrlIDP(hwnd) GetWindowLongPtr(hwnd, GWLP_ID) HWND GetDlgItemP(HWND hDlg, INT_PTR id) { HWND hwndChild = GetWindow(hDlg, GW_CHILD); while (hwndChild && GetDlgCtrlIDP(hwndChild) != id) hwndChild = GetWindow(hwndChild, GW_HWNDNEXT); return hwndChild; } #else #define GetDlgItemP GetDlgItem #define GetDlgCtrlIDP GetDlgCtrlID #endif // // display devices // typedef struct _multimon_device { // // Main class for settings // CDisplaySettings * pds; // // Color and resolution information cache // Rebuild when modes are enumerated. // int cColors; PLONGLONG ColorList; int cResolutions; PPOINT ResolutionList; ULONG ComboBoxItem; DISPLAY_DEVICE DisplayDevice; ULONG DisplayIndex; POINT Snap; HDC hdc; // // Image information. // int w,h; HIMAGELIST himl; int iImage; BOOLEAN bTracking; HWND hwndFlash; //Flash window. } MULTIMON_DEVICE, *PMULTIMON_DEVICE; #define GetDlgCtrlDevice(hwnd) ((PMULTIMON_DEVICE)GetDlgCtrlIDP(hwnd)) BOOL gfFlashWindowRegistered = FALSE; HWND ghwndToolTipTracking; HWND ghwndToolTipPopup; HWND ghwndPropSheet; void AddTrackingToolTip(PMULTIMON_DEVICE pDevice, HWND hwnd); void RemoveTrackingToolTip(HWND hwnd); void AddPopupToolTip(HWND hwndC); void RemovePopupToolTip(HWND hwndC); extern int AskDynaCDS(HWND hDlg); extern int GetDisplayCPLPreference(LPCTSTR szRegVal); extern void SetDisplayCPLPreference(LPCTSTR szRegVal, int val); // Prototype for CreateStdAccessibleProxy. // A and W versions are available - pClassName can be ANSI or UNICODE // string. This is a TCHAR-style prototype, but you can do a A or W // specific one if desired. typedef HRESULT (WINAPI *PFNCREATESTDACCESSIBLEPROXY) ( HWND hWnd, LPTSTR pClassName, LONG idObject, REFIID riid, void ** ppvObject ); // Same for LresultFromObject... typedef LRESULT (WINAPI *PFNLRESULTFROMOBJECT)( REFIID riid, WPARAM wParam, LPUNKNOWN punk ); PRIVATE PFNCREATESTDACCESSIBLEPROXY s_pfnCreateStdAccessibleProxy = NULL; PRIVATE PFNLRESULTFROMOBJECT s_pfnLresultFromObject = NULL; BOOL g_fAttemptedOleAccLoad ; HMODULE g_hOleAcc; //----------------------------------------------------------------------------- static const DWORD sc_MultiMonitorHelpIds[] = { IDC_SCREENSAMPLE, IDH_DISPLAY_SETTINGS_MONITOR_GRAPHIC, IDC_MULTIMONHELP, IDH_DISPLAY_SETTINGS_MONITOR_GRAPHIC, IDC_DISPLAYDESK, IDH_DISPLAY_SETTINGS_MONITOR_GRAPHIC, IDC_DISPLAYLABEL, IDH_DISPLAY_SETTINGS_DISPLAY_LIST, IDC_DISPLAYLIST, IDH_DISPLAY_SETTINGS_DISPLAY_LIST, IDC_DISPLAYTEXT, IDH_DISPLAY_SETTINGS_DISPLAY_LIST, IDC_COLORGROUPBOX, IDH_DISPLAY_SETTINGS_COLORBOX, IDC_COLORBOX, IDH_DISPLAY_SETTINGS_COLORBOX, IDC_COLORSAMPLE, IDH_DISPLAY_SETTINGS_COLORBOX, IDC_RESGROUPBOX, IDH_DISPLAY_SETTINGS_SCREENAREA, IDC_SCREENSIZE, IDH_DISPLAY_SETTINGS_SCREENAREA, IDC_RES_LESS, IDH_DISPLAY_SETTINGS_SCREENAREA, IDC_RES_MORE, IDH_DISPLAY_SETTINGS_SCREENAREA, IDC_RESXY, IDH_DISPLAY_SETTINGS_SCREENAREA, IDC_DISPLAYUSEME, IDH_DISPLAY_SETTINGS_EXTEND_DESKTOP_CHECKBOX, IDC_DISPLAYPRIME, IDH_DISPLAY_SETTINGS_USE_PRIMARY_CHECKBOX, IDC_IDENTIFY, IDH_DISPLAY_SETTINGS_IDENTIFY_BUTTON, IDC_TROUBLESHOOT, IDH_DISPLAY_SETTINGS_TROUBLE_BUTTON, IDC_DISPLAYPROPERTIES, IDH_DISPLAY_SETTINGS_ADVANCED_BUTTON, 0, 0 }; class CAccessibleWrapper: public IAccessible { // We need to do our own refcounting for this wrapper object LONG m_cRef; // Need ptr to the IAccessible IAccessible * m_pAcc; HWND m_hwnd; public: CAccessibleWrapper( HWND hwnd, IAccessible * pAcc ); virtual ~CAccessibleWrapper(void); // IUnknown // (We do our own ref counting) virtual STDMETHODIMP QueryInterface(REFIID riid, void** ppv); virtual STDMETHODIMP_(ULONG) AddRef(); virtual STDMETHODIMP_(ULONG) Release(); // IDispatch virtual STDMETHODIMP GetTypeInfoCount(UINT* pctinfo); virtual STDMETHODIMP GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo); virtual STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR** rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid); virtual STDMETHODIMP Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr); // IAccessible virtual STDMETHODIMP get_accParent(IDispatch ** ppdispParent); virtual STDMETHODIMP get_accChildCount(long* pChildCount); virtual STDMETHODIMP get_accChild(VARIANT varChild, IDispatch ** ppdispChild); virtual STDMETHODIMP get_accName(VARIANT varChild, BSTR* pszName); virtual STDMETHODIMP get_accValue(VARIANT varChild, BSTR* pszValue); virtual STDMETHODIMP get_accDescription(VARIANT varChild, BSTR* pszDescription); virtual STDMETHODIMP get_accRole(VARIANT varChild, VARIANT *pvarRole); virtual STDMETHODIMP get_accState(VARIANT varChild, VARIANT *pvarState); virtual STDMETHODIMP get_accHelp(VARIANT varChild, BSTR* pszHelp); virtual STDMETHODIMP get_accHelpTopic(BSTR* pszHelpFile, VARIANT varChild, long* pidTopic); virtual STDMETHODIMP get_accKeyboardShortcut(VARIANT varChild, BSTR* pszKeyboardShortcut); virtual STDMETHODIMP get_accFocus(VARIANT * pvarFocusChild); virtual STDMETHODIMP get_accSelection(VARIANT * pvarSelectedChildren); virtual STDMETHODIMP get_accDefaultAction(VARIANT varChild, BSTR* pszDefaultAction); virtual STDMETHODIMP accSelect(long flagsSel, VARIANT varChild); virtual STDMETHODIMP accLocation(long* pxLeft, long* pyTop, long* pcxWidth, long* pcyHeight, VARIANT varChild); virtual STDMETHODIMP accNavigate(long navDir, VARIANT varStart, VARIANT * pvarEndUpAt); virtual STDMETHODIMP accHitTest(long xLeft, long yTop, VARIANT * pvarChildAtPoint); virtual STDMETHODIMP accDoDefaultAction(VARIANT varChild); virtual STDMETHODIMP put_accName(VARIANT varChild, BSTR szName); virtual STDMETHODIMP put_accValue(VARIANT varChild, BSTR pszValue); }; CAccessibleWrapper::CAccessibleWrapper( HWND hwnd, IAccessible * pAcc ) : m_cRef( 1 ), m_pAcc( pAcc ), m_hwnd( hwnd ) { ASSERT( m_pAcc ); m_pAcc->AddRef(); } CAccessibleWrapper::~CAccessibleWrapper() { m_pAcc->Release(); } // IUnknown // Implement refcounting ourselves // Also implement QI ourselves, so that we return a ptr back to the wrapper. STDMETHODIMP CAccessibleWrapper::QueryInterface(REFIID riid, void** ppv) { *ppv = NULL; if ((riid == IID_IUnknown) || (riid == IID_IDispatch) || (riid == IID_IAccessible)) { *ppv = (IAccessible *) this; } else return(E_NOINTERFACE); AddRef(); return(NOERROR); } STDMETHODIMP_(ULONG) CAccessibleWrapper::AddRef() { return InterlockedIncrement(&m_cRef); } STDMETHODIMP_(ULONG) CAccessibleWrapper::Release() { ASSERT( 0 != m_cRef ); ULONG cRef = InterlockedDecrement(&m_cRef); if ( 0 == cRef ) { delete this; } return cRef; } // IDispatch // - pass all through m_pAcc STDMETHODIMP CAccessibleWrapper::GetTypeInfoCount(UINT* pctinfo) { return m_pAcc->GetTypeInfoCount(pctinfo); } STDMETHODIMP CAccessibleWrapper::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo) { return m_pAcc->GetTypeInfo(itinfo, lcid, pptinfo); } STDMETHODIMP CAccessibleWrapper::GetIDsOfNames(REFIID riid, OLECHAR** rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid) { return m_pAcc->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); } STDMETHODIMP CAccessibleWrapper::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr) { return m_pAcc->Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); } // IAccessible // - pass all through m_pAcc STDMETHODIMP CAccessibleWrapper::get_accParent(IDispatch ** ppdispParent) { return m_pAcc->get_accParent(ppdispParent); } STDMETHODIMP CAccessibleWrapper::get_accChildCount(long* pChildCount) { return m_pAcc->get_accChildCount(pChildCount); } STDMETHODIMP CAccessibleWrapper::get_accChild(VARIANT varChild, IDispatch ** ppdispChild) { return m_pAcc->get_accChild(varChild, ppdispChild); } STDMETHODIMP CAccessibleWrapper::get_accName(VARIANT varChild, BSTR* pszName) { return m_pAcc->get_accName(varChild, pszName); } STDMETHODIMP CAccessibleWrapper::get_accValue(VARIANT varChild, BSTR* pszValue) { // varChild.lVal specifies which sub-part of the component // is being queried. // CHILDID_SELF (0) specifies the overall component - other // non-0 values specify a child. // In a trackbar, CHILDID_SELF refers to the overall trackbar // (which is what we want), whereas other values refer to the // sub-components - the actual slider 'thumb', and the 'page // up/page down' areas to the left/right of it. if( varChild.vt == VT_I4 && varChild.lVal == CHILDID_SELF ) { HWND hDlg; TCHAR achRes[120]; hDlg = GetParent( m_hwnd ); SendDlgItemMessage(hDlg, IDC_RESXY, WM_GETTEXT, 120, (LPARAM)achRes); *pszValue = SysAllocString( achRes ); return S_OK; } else { // Pass requests about the sub-components to the // 'original' IAccessible for us). return m_pAcc->get_accValue(varChild, pszValue); } } STDMETHODIMP CAccessibleWrapper::get_accDescription(VARIANT varChild, BSTR* pszDescription) { return m_pAcc->get_accDescription(varChild, pszDescription); } STDMETHODIMP CAccessibleWrapper::get_accRole(VARIANT varChild, VARIANT *pvarRole) { return m_pAcc->get_accRole(varChild, pvarRole); } STDMETHODIMP CAccessibleWrapper::get_accState(VARIANT varChild, VARIANT *pvarState) { return m_pAcc->get_accState(varChild, pvarState); } STDMETHODIMP CAccessibleWrapper::get_accHelp(VARIANT varChild, BSTR* pszHelp) { return m_pAcc->get_accHelp(varChild, pszHelp); } STDMETHODIMP CAccessibleWrapper::get_accHelpTopic(BSTR* pszHelpFile, VARIANT varChild, long* pidTopic) { return m_pAcc->get_accHelpTopic(pszHelpFile, varChild, pidTopic); } STDMETHODIMP CAccessibleWrapper::get_accKeyboardShortcut(VARIANT varChild, BSTR* pszKeyboardShortcut) { return m_pAcc->get_accKeyboardShortcut(varChild, pszKeyboardShortcut); } STDMETHODIMP CAccessibleWrapper::get_accFocus(VARIANT * pvarFocusChild) { return m_pAcc->get_accFocus(pvarFocusChild); } STDMETHODIMP CAccessibleWrapper::get_accSelection(VARIANT * pvarSelectedChildren) { return m_pAcc->get_accSelection(pvarSelectedChildren); } STDMETHODIMP CAccessibleWrapper::get_accDefaultAction(VARIANT varChild, BSTR* pszDefaultAction) { return m_pAcc->get_accDefaultAction(varChild, pszDefaultAction); } STDMETHODIMP CAccessibleWrapper::accSelect(long flagsSel, VARIANT varChild) { return m_pAcc->accSelect(flagsSel, varChild); } STDMETHODIMP CAccessibleWrapper::accLocation(long* pxLeft, long* pyTop, long* pcxWidth, long* pcyHeight, VARIANT varChild) { return m_pAcc->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, varChild); } STDMETHODIMP CAccessibleWrapper::accNavigate(long navDir, VARIANT varStart, VARIANT * pvarEndUpAt) { return m_pAcc->accNavigate(navDir, varStart, pvarEndUpAt); } STDMETHODIMP CAccessibleWrapper::accHitTest(long xLeft, long yTop, VARIANT * pvarChildAtPoint) { return m_pAcc->accHitTest(xLeft, yTop, pvarChildAtPoint); } STDMETHODIMP CAccessibleWrapper::accDoDefaultAction(VARIANT varChild) { return m_pAcc->accDoDefaultAction(varChild); } STDMETHODIMP CAccessibleWrapper::put_accName(VARIANT varChild, BSTR szName) { return m_pAcc->put_accName(varChild, szName); } STDMETHODIMP CAccessibleWrapper::put_accValue(VARIANT varChild, BSTR pszValue) { return m_pAcc->put_accValue(varChild, pszValue); } class CSettingsPage : public CObjectWithSite, public CObjectCLSID, public IMultiMonConfig, public IPropertyBag, public IBasePropPage { friend int ComputeNumberOfDisplayDevices(); friend int DisplaySaveSettings(PVOID pContext, HWND hwnd); private: // Data Section PMULTIMON_DEVICE _pCurDevice; PMULTIMON_DEVICE _pPrimaryDevice; // HWND for the main window HWND _hDlg; HWND _hwndDesk; HWND _hwndList; // union of all monitor RECTs RECT _rcDesk; // ref count LONG _cRef; LONG _nInApply; // how to translate to preview size int _DeskScale; POINT _DeskOff; UINT _InSetInfo; ULONG _NumDevices; HBITMAP _hbmScrSample; HBITMAP _hbmMonitor; HIMAGELIST _himl; DWORD _dwInvalidMode; // UI variables int _iColor; int _iResolution; BOOL _bBadDriver : 1; BOOL _bNoAttach : 1; BOOL _bDirty : 1; MULTIMON_DEVICE _Devices[MONITORS_MAX]; // Private functions void _DeskToPreview(LPRECT in, LPRECT out); void _OffsetPreviewToDesk(HWND hwndC, LPRECT prcNewPreview, LPRECT prcOldPreview, LPRECT out); BOOL _QueryForceSmallFont(); void _SetPreviewScreenSize(int HRes, int VRes, int iOrgXRes, int iOrgYRes); void _CleanupRects(HWND hwndP); void _ConfirmPositions(); void _DoAdvancedSettingsSheet(); BOOL _HandleHScroll(HWND hwndSB, int iCode, int iPos); void _RedrawDeskPreviews(); void _OnAdvancedClicked(); BOOL _InitDisplaySettings(BOOL bExport); int _EnumerateAllDisplayDevices(); //Enumerates and returns the number of devices. void _DestroyMultimonDevice(PMULTIMON_DEVICE pDevice); void _DestroyDisplaySettings(); void _InitUI(); void _UpdateUI(BOOL fAutoSetColorDepth = TRUE, int FocusToCtrlID = 0); LPTSTR _FormatMessageInvoke(LPCTSTR pcszFormat, va_list *argList); LPTSTR _FormatMessageWrap(LPCTSTR pcszFormat, ...); void _GetDisplayName(PMULTIMON_DEVICE pDevice, LPTSTR pszDisplay, DWORD cchSize); int _SaveDisplaySettings(DWORD dwSet); void _ForwardToChildren(UINT message, WPARAM wParam, LPARAM lParam); static BOOL _CanSkipWarningBecauseKnownSafe(CDisplaySettings *rgpds[], ULONG numDevices); static BOOL _AnyChange(CDisplaySettings *rgpds[], ULONG numDevices); static BOOL _IsSingleToMultimonChange(CDisplaySettings *rgpds[], ULONG numDevices); static int _DisplaySaveSettings(CDisplaySettings *rgpds[], ULONG numDevices, HWND hDlg); static int _SaveSettings(CDisplaySettings *rgpds[], ULONG numDevices, HWND hDlg, DWORD dwSet); BOOL _AreExtraMonitorsDisabledOnPersonal(); BOOL _InitMessage(); void _vPreExecMode(); void _vPostExecMode(); public: CSettingsPage(); static BOOL RegisterPreviewWindowClass(WNDPROC pfnWndProc); // *** IUnknown methods *** STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // *** IMultiMonConfig methods *** STDMETHOD ( Initialize ) ( HWND hwndHost, WNDPROC pfnWndProc, DWORD dwReserved); STDMETHOD ( GetNumberOfMonitors ) (int * pCMon, DWORD dwReserved); STDMETHOD ( GetMonitorData) (int iMonitor, MonitorData * pmd, DWORD dwReserved); STDMETHOD ( Paint) (THIS_ int iMonitor, DWORD dwReserved); // *** IShellPropSheetExt *** virtual STDMETHODIMP AddPages(IN LPFNSVADDPROPSHEETPAGE pfnAddPage, IN LPARAM lParam); virtual STDMETHODIMP ReplacePage(IN EXPPS uPageID, IN LPFNSVADDPROPSHEETPAGE pfnReplaceWith, IN LPARAM lParam); // *** IObjectWithSite *** virtual STDMETHODIMP SetSite(IUnknown *punkSite); // *** IPropertyBag *** virtual STDMETHODIMP Read(IN LPCOLESTR pszPropName, IN VARIANT * pVar, IN IErrorLog *pErrorLog); virtual STDMETHODIMP Write(IN LPCOLESTR pszPropName, IN VARIANT *pVar); // *** IBasePropPage *** virtual STDMETHODIMP GetAdvancedDialog(OUT IAdvancedDialog ** ppAdvDialog); virtual STDMETHODIMP OnApply(IN PROPPAGEONAPPLY oaAction); BOOL InitMultiMonitorDlg(HWND hDlg); PMULTIMON_DEVICE GetCurDevice(){return _pCurDevice;}; int GetNumberOfAttachedDisplays(); void UpdateActiveDisplay(PMULTIMON_DEVICE pDevice, BOOL bRepaint = TRUE); BOOL HandleMonitorChange(HWND hwndP, BOOL bMainDlg, BOOL bRepaint = TRUE); void SetDirty(BOOL bDirty=TRUE); BOOL SetPrimary(PMULTIMON_DEVICE pDevice); BOOL SetMonAttached(PMULTIMON_DEVICE pDevice, BOOL bSetAttached, BOOL bForce, HWND hwnd); HWND GetCurDeviceHwnd() { return GetDlgItemP(_hwndDesk, (INT_PTR) _pCurDevice);}; ULONG GetNumDevices() { return _NumDevices;}; BOOL QueryNoAttach() { return _bNoAttach;}; BOOL IsDirty() { return _bDirty;}; void GetMonitorPosition(PMULTIMON_DEVICE pDevice, HWND hwndP, PPOINT ptPos); static INT_PTR CALLBACK SettingsDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); LRESULT CALLBACK WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam); IThemeUIPages* _pThemeUI; }; CSettingsPage::CSettingsPage() : _cRef(1), CObjectCLSID(&PPID_Settings) { ASSERT(_pCurDevice == NULL); ASSERT(_pPrimaryDevice == NULL); ASSERT(_DeskScale == 0); ASSERT(_InSetInfo == 0); ASSERT(_NumDevices == 0); ASSERT(IsRectEmpty(&_rcDesk)); ASSERT(_bNoAttach == FALSE); ASSERT(_bDirty == FALSE); _nInApply = 0; }; void CSettingsPage::_DestroyMultimonDevice(PMULTIMON_DEVICE pDevice) { ASSERT(pDevice->pds); pDevice->pds->Release(); pDevice->pds = NULL; if(pDevice->hwndFlash) { DestroyWindow(pDevice->hwndFlash); pDevice->hwndFlash = NULL; } if (pDevice->hdc) { DeleteDC(pDevice->hdc); pDevice->hdc = NULL; } if (pDevice->ResolutionList) { LocalFree(pDevice->ResolutionList); pDevice->ResolutionList = NULL; } if (pDevice->ColorList) { LocalFree(pDevice->ColorList); pDevice->ColorList = NULL; } } void CSettingsPage::_DestroyDisplaySettings() { ULONG iDevice; HWND hwndC; ASSERT(_NumDevices); TraceMsg(TF_GENERAL, "DestroyDisplaySettings: %d devices", _NumDevices); // We are about to destroy the _Devices below. Pointerts to these devices are used as the // CtrlIDs for the monitor windows. So, we need destroy the monitor windows first; // otherwise, if the monitor windows are destroyed later, they try to use these invalid // pDevice in FlashText. (pDevice->hwndFlash will fault). hwndC = GetWindow(_hwndDesk, GW_CHILD); while (hwndC) { RemoveTrackingToolTip(hwndC); RemovePopupToolTip(hwndC); DestroyWindow(hwndC); hwndC = GetWindow(_hwndDesk, GW_CHILD); } // Now, we can destroy the _Devices safely. for (iDevice = 0; iDevice < _NumDevices; iDevice++) { _DestroyMultimonDevice(_Devices + iDevice); // Note: pds is destroyed and set to zero already in the above call. //delete _Devices[iDevice].pds; //_Devices[iDevice].pds = 0; } if (_himl) { ImageList_Destroy(_himl); _himl = NULL; } DestroyWindow(ghwndToolTipTracking); DestroyWindow(ghwndToolTipPopup); ghwndToolTipTracking = NULL; ghwndToolTipPopup = NULL; TraceMsg(TF_GENERAL, "DestroyDisplaySettings: Finished destroying all devices"); } // // deterines if the applet is in detect mode. // // // Called to put up initial messages that need to appear above the dialog // box // BOOL CSettingsPage::_InitMessage() { { // // _bBadDriver will be set when we fail to build the list of modes, // or something else failed during initialization. // // In almost every case, we should already know about this situation // based on our boot code. // However, if this is a new situation, just report a "bad driver" // DWORD dwExecMode; if (_pThemeUI && (SUCCEEDED(_pThemeUI->GetExecMode(&dwExecMode)))) { if (_bBadDriver) { ASSERT(dwExecMode == EM_INVALID_MODE); _pThemeUI->SetExecMode(EM_INVALID_MODE); dwExecMode = EM_INVALID_MODE; _dwInvalidMode = EIS_EXEC_INVALID_DISPLAY_DRIVER; } if (dwExecMode == EM_INVALID_MODE) { DWORD Mesg; switch(_dwInvalidMode) { case EIS_EXEC_INVALID_NEW_DRIVER: Mesg = MSG_INVALID_NEW_DRIVER; break; case EIS_EXEC_INVALID_DEFAULT_DISPLAY_MODE: Mesg = MSG_INVALID_DEFAULT_DISPLAY_MODE; break; case EIS_EXEC_INVALID_DISPLAY_DRIVER: Mesg = MSG_INVALID_DISPLAY_DRIVER; break; case EIS_EXEC_INVALID_OLD_DISPLAY_DRIVER: Mesg = MSG_INVALID_OLD_DISPLAY_DRIVER; break; case EIS_EXEC_INVALID_16COLOR_DISPLAY_MODE: Mesg = MSG_INVALID_16COLOR_DISPLAY_MODE; break; case EIS_EXEC_INVALID_DISPLAY_MODE: Mesg = MSG_INVALID_DISPLAY_MODE; { // // If we are in safe mode, then we will get to here when // we initially log in. We are in forced VGA mode, so there // is no real error here. Emulate a click on the OK button // and everybody is happy. // HKEY hSafe; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_VAL_SAFEBOOT, 0, KEY_READ, &hSafe) == ERROR_SUCCESS) { // // If we ever care about the actual safe mode, the value // is nameed "OptionValue" // RegCloseKey(hSafe); PropSheet_PressButton(GetParent(_hDlg), PSBTN_OK); return TRUE; } } break; case EIS_EXEC_INVALID_CONFIGURATION: default: Mesg = MSG_INVALID_CONFIGURATION; break; } FmtMessageBox(_hDlg, MB_ICONEXCLAMATION, MSG_CONFIGURATION_PROBLEM, Mesg); // // For a bad display driver or old display driver, let's send the // user straight to the installation dialog. // if ((_dwInvalidMode == EIS_EXEC_INVALID_OLD_DISPLAY_DRIVER) || (_dwInvalidMode == EIS_EXEC_INVALID_DISPLAY_DRIVER)) { ASSERT(FALSE); } } } } return TRUE; } VOID CSettingsPage::_vPreExecMode() { HKEY hkey; // // This function sets up the execution mode of the applet. // There are four vlid modes. // // EXEC_NORMAL - When the apple is launched from the control panel // // EXEC_INVALID_MODE is exactly the same as for NORMAL except we will // not mark the current mode as tested so the user has // to at least test a mode // // EXEC_DETECT - When the applet is launched normally, but a detect was // done on the previous boot (the key in the registry is // set) // // EXEC_SETUP - When we launch the applet in setup mode from setup (Both // the registry key is set and the setup flag is passed in). // // // These two keys should only be checked \ deleted if the machine has been // rebooted and the detect \ new display has actually happened. // So we will look for the RebootNecessary key (a volatile key) and if // it is not present, then we can delete the key. Otherwise, the reboot // has not happened, and we keep the key // if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, SZ_REBOOT_NECESSARY, 0, KEY_READ | KEY_WRITE, &hkey) != ERROR_SUCCESS) { if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, SZ_DETECT_DISPLAY, 0, KEY_READ | KEY_WRITE, &hkey) == ERROR_SUCCESS) { // // NOTE: This key is also set when EXEC_SETUP is being run. // DWORD dwExecMode; if (_pThemeUI && (SUCCEEDED(_pThemeUI->GetExecMode(&dwExecMode)))) { if (dwExecMode == EM_NORMAL) { _pThemeUI->SetExecMode(EM_DETECT); } else { // // If we are in setup mode, we also check the extra values // under DetectDisplay that control the unattended installation. // ASSERT(dwExecMode == EM_SETUP); } } RegCloseKey(hkey); } // // Check for a new driver being installed // DWORD dwExecMode; if (_pThemeUI && (SUCCEEDED(_pThemeUI->GetExecMode(&dwExecMode)))) { if ( (dwExecMode == EM_NORMAL) && (RegOpenKeyEx(HKEY_LOCAL_MACHINE, SZ_NEW_DISPLAY, 0, KEY_READ | KEY_WRITE, &hkey) == ERROR_SUCCESS) ) { _pThemeUI->SetExecMode(EM_INVALID_MODE); _dwInvalidMode = EIS_EXEC_INVALID_NEW_DRIVER; RegCloseKey(hkey); } } RegDeleteKey(HKEY_LOCAL_MACHINE, SZ_DETECT_DISPLAY); RegDeleteKey(HKEY_LOCAL_MACHINE, SZ_NEW_DISPLAY); } { LPTSTR psz = NULL; LPTSTR pszInv = NULL; DWORD dwExecMode; if (_pThemeUI && (SUCCEEDED(_pThemeUI->GetExecMode(&dwExecMode)))) { switch(dwExecMode) { case EM_NORMAL: psz = TEXT("Normal Execution mode"); break; case EM_DETECT: psz = TEXT("Detection Execution mode"); break; case EM_SETUP: psz = TEXT("Setup Execution mode"); break; case EM_INVALID_MODE: psz = TEXT("Invalid Mode Execution mode"); switch(_dwInvalidMode) { case EIS_EXEC_INVALID_NEW_DRIVER: pszInv = TEXT("Invalid new driver"); break; default: pszInv = TEXT("*** Invalid *** Invalid mode"); break; } break; default: psz = TEXT("*** Invalid *** Execution mode"); break; } if (dwExecMode == EM_INVALID_MODE) { TraceMsg(TF_FUNC, "\t\t sub invalid mode : %ws", pszInv); } } TraceMsg(TF_FUNC, "\n\n", psz); } } VOID CSettingsPage::_vPostExecMode() { HKEY hkey; DWORD cb; DWORD data; // // Check for various invalid configurations // DWORD dwExecMode; if (_pThemeUI && (SUCCEEDED(_pThemeUI->GetExecMode(&dwExecMode)))) { if ( (dwExecMode == EM_NORMAL) && (RegOpenKeyEx(HKEY_LOCAL_MACHINE, SZ_INVALID_DISPLAY, 0, KEY_READ | KEY_WRITE, &hkey) == ERROR_SUCCESS) ) { _pThemeUI->SetExecMode(EM_INVALID_MODE); // // Check for these fields in increasing order of "badness" or // "detail" so that the *worst* error is the one remaining in the // _dwInvalidMode variable once all the checks are done. // cb = sizeof(data); if (RegQueryValueEx(hkey, TEXT("DefaultMode"), NULL, NULL, (LPBYTE)(&data), &cb) == ERROR_SUCCESS) { _dwInvalidMode = EIS_EXEC_INVALID_DEFAULT_DISPLAY_MODE; } cb = sizeof(data); if (RegQueryValueEx(hkey, TEXT("BadMode"), NULL, NULL, (LPBYTE)(&data), &cb) == ERROR_SUCCESS) { _dwInvalidMode = EIS_EXEC_INVALID_DISPLAY_MODE; } cb = sizeof(data); if (RegQueryValueEx(hkey, TEXT("16ColorMode"), NULL, NULL, (LPBYTE)(&data), &cb) == ERROR_SUCCESS) { _dwInvalidMode = EIS_EXEC_INVALID_16COLOR_DISPLAY_MODE; } cb = sizeof(data); if (RegQueryValueEx(hkey, TEXT("InvalidConfiguration"), NULL, NULL, (LPBYTE)(&data), &cb) == ERROR_SUCCESS) { _dwInvalidMode = EIS_EXEC_INVALID_CONFIGURATION; } cb = sizeof(data); if (RegQueryValueEx(hkey, TEXT("MissingDisplayDriver"), NULL, NULL, (LPBYTE)(&data), &cb) == ERROR_SUCCESS) { _dwInvalidMode = EIS_EXEC_INVALID_DISPLAY_DRIVER; } // // This last case will be set in addition to the previous one in the // case where the driver was an old driver linking to winsvr.dll // and we can not load it. // cb = sizeof(data); if (RegQueryValueEx(hkey, TEXT("OldDisplayDriver"), NULL, NULL, (LPBYTE)(&data), &cb) == ERROR_SUCCESS) { _dwInvalidMode = EIS_EXEC_INVALID_OLD_DISPLAY_DRIVER; } RegCloseKey(hkey); } } // // Delete all of these bad configuration keys since we only want the // user to see the message once. // RegDeleteKey(HKEY_LOCAL_MACHINE, SZ_INVALID_DISPLAY); { LPTSTR psz = NULL; LPTSTR pszInv = NULL; DWORD dwExecMode; if (_pThemeUI && (SUCCEEDED(_pThemeUI->GetExecMode(&dwExecMode)))) { if (dwExecMode == EM_INVALID_MODE) { switch (_dwInvalidMode) { case EIS_EXEC_INVALID_DEFAULT_DISPLAY_MODE: pszInv = TEXT("Default mode being used"); break; case EIS_EXEC_INVALID_DISPLAY_DRIVER: pszInv = TEXT("Invalid Display Driver"); break; case EIS_EXEC_INVALID_OLD_DISPLAY_DRIVER: pszInv = TEXT("Old Display Driver"); break; case EIS_EXEC_INVALID_16COLOR_DISPLAY_MODE: pszInv = TEXT("16 color mode not supported"); break; case EIS_EXEC_INVALID_DISPLAY_MODE: pszInv = TEXT("Invalid display mode"); break; case EIS_EXEC_INVALID_CONFIGURATION: pszInv = TEXT("Invalid configuration"); break; default: psz = TEXT("*** Invalid *** Invalid mode"); break; } TraceMsg(TF_FUNC, "\t\t sub invlid mode : %ws", pszInv); TraceMsg(TF_FUNC, "\n\n", psz); } } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CSettingsPage::_DeskToPreview(LPRECT in, LPRECT out) { out->left = _DeskOff.x + MulDiv(in->left - _rcDesk.left,_DeskScale,1000); out->top = _DeskOff.y + MulDiv(in->top - _rcDesk.top, _DeskScale,1000); out->right = _DeskOff.x + MulDiv(in->right - _rcDesk.left,_DeskScale,1000); out->bottom = _DeskOff.y + MulDiv(in->bottom - _rcDesk.top, _DeskScale,1000); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CSettingsPage::_OffsetPreviewToDesk(HWND hwndC, LPRECT prcNewPreview, LPRECT prcOldPreview, LPRECT out) { int x = 0, y = 0; int dtLeft, dtRight, dtTop, dtBottom; int nTotal; HWND hwndT; RECT rcC, rcT, rcPosT; BOOL bx = FALSE, by = FALSE; PMULTIMON_DEVICE pDeviceT; dtLeft = prcNewPreview->left - prcOldPreview->left; dtRight = prcNewPreview->right - prcOldPreview->right; dtTop = prcNewPreview->top - prcOldPreview->top; dtBottom = prcNewPreview->bottom - prcOldPreview->bottom; nTotal = abs(dtLeft) + abs(dtRight) + abs(dtTop) + abs(dtBottom); if (nTotal == 0) { return; } else if (nTotal > 2) { // // walk all other windows and snap our window to them // GetWindowRect(hwndC, &rcC); for (hwndT = GetWindow(hwndC, GW_HWNDFIRST); (hwndT && (!bx || !by)); hwndT = GetWindow(hwndT, GW_HWNDNEXT)) { if (hwndT == hwndC) continue; GetWindowRect(hwndT, &rcT); pDeviceT = GetDlgCtrlDevice(hwndT); if (pDeviceT) { pDeviceT->pds->GetCurPosition(&rcPosT); if (!bx) { bx = TRUE; if (rcC.left == rcT.left) { x = rcPosT.left - out->left; } else if (rcC.left == rcT.right) { x = rcPosT.right - out->left; } else if (rcC.right == rcT.left) { x = rcPosT.left - out->right; } else if (rcC.right == rcT.right) { x = rcPosT.right - out->right; } else { bx = FALSE; } } if (!by) { by = TRUE; if (rcC.top == rcT.top) { y = rcPosT.top - out->top; } else if (rcC.top == rcT.bottom) { y = rcPosT.bottom - out->top; } else if (rcC.bottom == rcT.top) { y = rcPosT.top - out->bottom; } else if (rcC.bottom == rcT.bottom) { y = rcPosT.bottom - out->bottom; } else { by = FALSE; } } } } if (!bx) { x = _rcDesk.left + MulDiv(prcNewPreview->left - _DeskOff.x,1000,_DeskScale); x = x - out->left; } if (!by) { y = _rcDesk.top + MulDiv(prcNewPreview->top - _DeskOff.y,1000,_DeskScale); y = y - out->top; } } else { x = dtLeft * 8; y = dtTop * 8; } OffsetRect(out, x, y); } //----------------------------------------------------------------------------- int CSettingsPage::_SaveSettings(CDisplaySettings *rgpds[], ULONG numDevices, HWND hDlg, DWORD dwSet) { int iRet = 0; ULONG iDevice; for (iDevice = 0; iDevice < numDevices; iDevice++) { // PERF - we should only save the settings for devices that have // changed. if (rgpds[iDevice]) { int iResult = rgpds[iDevice]->SaveSettings(dwSet); if (iResult != DISP_CHANGE_SUCCESSFUL) { if (iResult == DISP_CHANGE_RESTART) { iRet = iResult; continue; } else { FmtMessageBox(hDlg, MB_ICONEXCLAMATION, IDS_CHANGE_SETTINGS, IDS_CHANGESETTINGS_FAILED); ASSERT(iResult < 0); return iResult; } } } } return iRet; } INT_PTR CALLBACK KeepNewDlgProc(HWND hDlg, UINT message , WPARAM wParam, LPARAM lParam) { UINT_PTR idTimer = 0; HICON hicon; TCHAR szRevert[100]; TCHAR szString[120]; switch(message) { case WM_INITDIALOG: hicon = LoadIcon(NULL, IDI_QUESTION); if (hicon) SendDlgItemMessage(hDlg, IDC_BIGICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hicon); LoadString(HINST_THISDLL, IDS_REVERTBACK, szRevert, ARRAYSIZE(szRevert)); StringCchPrintf(szString, ARRAYSIZE(szString), szRevert, lParam); SetDlgItemText(hDlg, IDC_COUNTDOWN, szString); idTimer = SetTimer(hDlg, lParam, 1000, NULL); SetFocus(GetDlgItem(hDlg, IDNO)); // FALSE so that the focus set above is kept return FALSE; case WM_DESTROY: // raymondc - this code is dead; idTimer is initialized to zero // fortunately, timers are automatically killed at window destruction // if (idTimer) // KillTimer(hDlg, idTimer); hicon = (HICON)SendDlgItemMessage(hDlg, IDC_BIGICON, STM_GETIMAGE, IMAGE_ICON, 0); if (hicon) DestroyIcon(hicon); break; case WM_TIMER: KillTimer(hDlg, wParam); LoadString(HINST_THISDLL, IDS_REVERTBACK, szRevert, ARRAYSIZE(szRevert)); StringCchPrintf(szString, ARRAYSIZE(szString), szRevert, wParam - 1); SetDlgItemText(hDlg, IDC_COUNTDOWN, szString); idTimer = SetTimer(hDlg, wParam - 1, 1000, NULL); if (wParam == 1) EndDialog(hDlg, IDNO); break; case WM_COMMAND: EndDialog(hDlg, wParam); break; default: return FALSE; } return TRUE; } int CSettingsPage::GetNumberOfAttachedDisplays() { int nDisplays = 0; for (ULONG iDevice = 0; iDevice < _NumDevices; iDevice++) { if (_Devices[iDevice].pds->IsAttached()) nDisplays++; } return nDisplays; } BOOL CSettingsPage::_IsSingleToMultimonChange(CDisplaySettings *rgpds[], ULONG numDevices) { int nAttached = 0; int nOrgAttached = 0; for (ULONG iDevice = 0; (iDevice < numDevices) && (nOrgAttached <= 1); iDevice++) { if (rgpds[iDevice]->IsOrgAttached()) nOrgAttached++; if (rgpds[iDevice]->IsAttached()) nAttached++; } return ((nOrgAttached <= 1) && (nAttached > 1)); } BOOL CSettingsPage::_AnyChange(CDisplaySettings *rgpds[], ULONG numDevices) { for (ULONG iDevice = 0; iDevice < numDevices; iDevice++) { if (rgpds[iDevice]->IsAttached() && rgpds[iDevice]->bIsModeChanged()) { return TRUE; } } return FALSE; } BOOL CSettingsPage::_CanSkipWarningBecauseKnownSafe(CDisplaySettings *rgpds[], ULONG numDevices) { BOOL fSafe = TRUE; for (ULONG iDevice = 0; iDevice < numDevices; iDevice++) { if (rgpds[iDevice] && !rgpds[iDevice]->IsKnownSafe()) { fSafe = FALSE; break; } } return fSafe; } BOOL CSettingsPage::_QueryForceSmallFont() { for (ULONG iDevice = 0; iDevice < _NumDevices; iDevice++) { if ((_Devices[iDevice].pds->IsAttached()) && (!_Devices[iDevice].pds->IsSmallFontNecessary())) { return FALSE; } } return TRUE; } LPTSTR CSettingsPage::_FormatMessageInvoke(LPCTSTR pcszFormat, va_list *argList) { LPTSTR pszOutput; if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING, pcszFormat, 0, 0, reinterpret_cast(&pszOutput), 0, argList) == 0) { pszOutput = NULL; } return(pszOutput); } LPTSTR CSettingsPage::_FormatMessageWrap(LPCTSTR pcszFormat, ...) { LPTSTR pszOutput; va_list argList; va_start(argList, pcszFormat); pszOutput = _FormatMessageInvoke(pcszFormat, &argList); va_end(argList); return(pszOutput); } void CSettingsPage::_GetDisplayName(PMULTIMON_DEVICE pDevice, LPTSTR pszDisplay, DWORD cchSize) { LPTSTR pszFormattedOutput; TCHAR szMonitor[140]; TCHAR szDisplayFormat[40]; LoadString(HINST_THISDLL, IDS_DISPLAYFORMAT, szDisplayFormat, ARRAYSIZE(szDisplayFormat)); pDevice->pds->GetMonitorName(szMonitor, ARRAYSIZE(szMonitor)); pszFormattedOutput = _FormatMessageWrap(szDisplayFormat, pDevice->DisplayIndex, szMonitor, pDevice->DisplayDevice.DeviceString); StringCchCopy(pszDisplay, cchSize, pszFormattedOutput); LocalFree(pszFormattedOutput); } void CSettingsPage::_OnAdvancedClicked() { BOOL bCanBePruned, bIsPruningReadOnly; BOOL bBeforeIsPruningOn, bAfterIsPruningOn; if (_pCurDevice && _pCurDevice->pds) { _pCurDevice->pds->GetPruningMode(&bCanBePruned, &bIsPruningReadOnly, &bBeforeIsPruningOn); _DoAdvancedSettingsSheet(); if (bCanBePruned && !bIsPruningReadOnly) { _pCurDevice->pds->GetPruningMode(&bCanBePruned, &bIsPruningReadOnly, &bAfterIsPruningOn); if (bBeforeIsPruningOn != bAfterIsPruningOn) { // pruning mode has changed - update the UI _InitUI(); _UpdateUI(); } } } } //----------------------------------------------------------------------------- void CSettingsPage::_DoAdvancedSettingsSheet() { if (_pCurDevice && _pCurDevice->pds) { PROPSHEETHEADER psh; HPROPSHEETPAGE rPages[MAX_PAGES]; PROPSHEETPAGE psp; HPSXA hpsxa = NULL; HPSXA hpsxaOEM = NULL; HPSXA hpsxaAdapter = NULL; HPSXA* phpsxaChildren = NULL; INT_PTR iResult = 0; TCHAR szDisplay[140 + 256 + 20]; //Monitor-name and Adapter Properties. TCHAR szMonitor[140]; TCHAR szDisplayFormat[35]; GENERAL_ADVDLG_INITPARAMS generalInitParams = {0}; // Create the "Monitor-name and Adapter-name properties" string to be used as the title for these // property sheets. LoadString(HINST_THISDLL, IDS_ADVDIALOGTITLE, szDisplayFormat, ARRAYSIZE(szDisplayFormat)); _pCurDevice->pds->GetMonitorName(szMonitor, ARRAYSIZE(szMonitor)); StringCchPrintf(szDisplay, ARRAYSIZE(szDisplay), szDisplayFormat, szMonitor, _pCurDevice->DisplayDevice.DeviceString); generalInitParams.fFoceSmallFont = _QueryForceSmallFont(); generalInitParams.punkSite = _punkSite; // They don't get a ref because their property dialog appears and goes away before this function returns. psh.dwSize = sizeof(psh); psh.dwFlags = PSH_PROPTITLE; psh.hwndParent = GetParent(_hDlg); psh.hInstance = HINST_THISDLL; psh.pszCaption = szDisplay; psh.nPages = 0; psh.nStartPage = 0; psh.phpage = rPages; psp.dwSize = sizeof(psp); psp.dwFlags = PSP_DEFAULT; psp.hInstance = HINST_THISDLL; psp.pfnDlgProc = GeneralPageProc; psp.pszTemplate = MAKEINTRESOURCE(DLG_GENERAL); psp.lParam = (LPARAM)&generalInitParams; rPages[psh.nPages] = CreatePropertySheetPage(&psp); if (rPages[psh.nPages]) psh.nPages++; IDataObject * pdo = NULL; _pCurDevice->pds->QueryInterface(IID_IDataObject, (LPVOID *) &pdo); CRegistrySettings RegSettings(_pCurDevice->DisplayDevice.DeviceKey); HKEY hkDriver = RegSettings.OpenDrvRegKey(); if (hkDriver != INVALID_HANDLE_VALUE) { CheckForDuplicateAppletExtensions(hkDriver); } // // load the generic (non hardware specific) extensions // if( ( hpsxa = SHCreatePropSheetExtArrayEx( HKEY_LOCAL_MACHINE, REGSTR_PATH_CONTROLSFOLDER TEXT("\\Device"), 8, pdo) ) != NULL ) { SHAddFromPropSheetExtArray( hpsxa, _AddDisplayPropSheetPage, (LPARAM)&psh ); } // // Load the hardware-specific extensions // // NOTE it is very important to load the OEM extensions *after* the // generic extensions some HW extensions expect to be the last tabs // in the propsheet (right before the settings tab) // // FEATURE - we may need a way to NOT load the vendor extensions in case // they break our applet. // if( ( hpsxaOEM = SHCreatePropSheetExtArrayEx( HKEY_LOCAL_MACHINE, REGSTR_PATH_CONTROLSFOLDER TEXT("\\Display"), 8, pdo) ) != NULL ) { SHAddFromPropSheetExtArray( hpsxaOEM, _AddDisplayPropSheetPage, (LPARAM)&psh ); } // // Load the applet extensions for the adapter // if (hkDriver != INVALID_HANDLE_VALUE) { if( ( hpsxaAdapter = SHCreatePropSheetExtArrayEx( hkDriver, TEXT("Display"), 8, pdo) ) != NULL ) { SHAddFromPropSheetExtArray( hpsxaAdapter, _AddDisplayPropSheetPage, (LPARAM)&psh ); } RegCloseKey(hkDriver); } // // Load the applet extensions for the adapter child devices (e.g. monitors) // DEVINST devInstAdapter, devInstMonitor; DWORD cChildDevices = 0, nChild, index; HDEVINFO hDevMonitors = INVALID_HANDLE_VALUE; SP_DEVINFO_DATA DevInfoData; HKEY hkMonitor; BOOL bMonitors = FALSE; LPTSTR szAdapterInstanceID = RegSettings.GetDeviceInstanceId(); if (szAdapterInstanceID != NULL) { if (CM_Locate_DevNodeW(&devInstAdapter, szAdapterInstanceID, 0) == CR_SUCCESS) { // // Get the number of child devices // cChildDevices = 0; if (CM_Get_Child(&devInstMonitor, devInstAdapter, 0) == CR_SUCCESS) { do { cChildDevices++; } while (CM_Get_Sibling(&devInstMonitor, devInstMonitor, 0) == CR_SUCCESS); } // // Allocate the memory // if (cChildDevices > 0) { phpsxaChildren = (HPSXA*)LocalAlloc(LPTR, cChildDevices * sizeof(HPSXA)); hDevMonitors = SetupDiGetClassDevs((LPGUID)&GUID_DEVCLASS_MONITOR, NULL, NULL, 0); } // // Load the applet extensions // if ((phpsxaChildren != NULL) && (hDevMonitors != INVALID_HANDLE_VALUE)) { nChild = 0; if (CM_Get_Child(&devInstMonitor, devInstAdapter, 0) == CR_SUCCESS) { do { DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA); index = 0; while (SetupDiEnumDeviceInfo(hDevMonitors, index, &DevInfoData)) { if (DevInfoData.DevInst == devInstMonitor) { hkMonitor = SetupDiOpenDevRegKey(hDevMonitors, &DevInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV , KEY_WRITE | KEY_READ); if (hkMonitor != INVALID_HANDLE_VALUE) { if ((phpsxaChildren[nChild] = SHCreatePropSheetExtArrayEx(hkMonitor, TEXT("Display"), 8, pdo)) != NULL) { bMonitors = TRUE; SHAddFromPropSheetExtArray(phpsxaChildren[nChild], _AddDisplayPropSheetPage, (LPARAM)&psh); } RegCloseKey(hkMonitor); } break; } DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA); index++; } nChild++; } while ((nChild < cChildDevices) && (CM_Get_Sibling(&devInstMonitor, devInstMonitor, 0) == CR_SUCCESS)); } } } } // // add a fake settings page to fool OEM extensions (must be last) // if (hpsxa || hpsxaOEM || hpsxaAdapter || bMonitors) { AddFakeSettingsPage(_pThemeUI, &psh); } if (psh.nPages) { iResult = PropertySheet(&psh); } _GetDisplayName(_pCurDevice, szDisplay, ARRAYSIZE(szDisplay)); if (_NumDevices == 1) { //Set the name of the primary in the static text //strip the first token off (this is the number we dont want it) TCHAR *pch; for (pch=szDisplay; *pch && *pch != TEXT(' '); pch++); for (;*pch && *pch == TEXT(' '); pch++); SetDlgItemText(_hDlg, IDC_DISPLAYTEXT, pch); } else { ComboBox_DeleteString(_hwndList, _pCurDevice->ComboBoxItem); ComboBox_InsertString(_hwndList, _pCurDevice->ComboBoxItem, szDisplay); ComboBox_SetItemData(_hwndList, _pCurDevice->ComboBoxItem, (DWORD_PTR)_pCurDevice); ComboBox_SetCurSel(_hwndList, _pCurDevice->ComboBoxItem); } if( hpsxa ) SHDestroyPropSheetExtArray( hpsxa ); if( hpsxaOEM ) SHDestroyPropSheetExtArray( hpsxaOEM ); if( hpsxaAdapter ) SHDestroyPropSheetExtArray( hpsxaAdapter ); if (phpsxaChildren != NULL) { for (nChild = 0; nChild < cChildDevices; nChild++) { if (phpsxaChildren[nChild] != NULL) { SHDestroyPropSheetExtArray(phpsxaChildren[nChild]); } } LocalFree(phpsxaChildren); } if (hDevMonitors != INVALID_HANDLE_VALUE) { SetupDiDestroyDeviceInfoList(hDevMonitors); } if (pdo) pdo->Release(); if ((iResult == ID_PSRESTARTWINDOWS) || (iResult == ID_PSREBOOTSYSTEM)) { PropSheet_CancelToClose(GetParent(_hDlg)); if (iResult == ID_PSREBOOTSYSTEM) PropSheet_RebootSystem(ghwndPropSheet); else PropSheet_RestartWindows(ghwndPropSheet); } // // APPCOMPAT // Reset the dirty flag based on what the extensions did. // // // Reset the controls in case someone changed the selected mode. // UpdateActiveDisplay(NULL); } } //----------------------------------------------------------------------------- void CSettingsPage::UpdateActiveDisplay(PMULTIMON_DEVICE pDevice, BOOL bRepaint /*=TRUE*/) { if (_pCurDevice && _pCurDevice->pds) { HWND hwndC; _InSetInfo++; if (pDevice == NULL) pDevice = (PMULTIMON_DEVICE)ComboBox_GetItemData(_hwndList, ComboBox_GetCurSel(_hwndList)); else ComboBox_SetCurSel(_hwndList, pDevice->ComboBoxItem); if (pDevice && pDevice != (PMULTIMON_DEVICE)CB_ERR) { hwndC = GetCurDeviceHwnd(); // The Current Device has changed, so, force recreating the bitmap the next time // we paint the monitor on the preview window. _pCurDevice->w = pDevice->w = 0; _pCurDevice = pDevice; if (hwndC) RedrawWindow(hwndC, NULL, NULL, RDW_ERASE | RDW_INVALIDATE); hwndC = GetCurDeviceHwnd(); if (hwndC) RedrawWindow(hwndC, NULL, NULL, RDW_ERASE | RDW_INVALIDATE); if(_NumDevices > 1) { // Update the two check box windows CheckDlgButton(_hDlg, IDC_DISPLAYPRIME, _pCurDevice->pds->IsPrimary()); EnableWindow(GetDlgItem(_hDlg, IDC_DISPLAYPRIME), _pCurDevice->pds->IsAttached() && !_pCurDevice->pds->IsRemovable() && !_pCurDevice->pds->IsPrimary()); CheckDlgButton(_hDlg, IDC_DISPLAYUSEME, _pCurDevice->pds->IsAttached()); EnableWindow(GetDlgItem(_hDlg, IDC_DISPLAYUSEME), !_bNoAttach && !_pCurDevice->pds->IsPrimary()); } // Reset the values for the list boxes, and then repaint it if(bRepaint) { _InitUI(); _UpdateUI(FALSE /*fAutoSetColorDepth*/); } } else { // No display device ! TraceMsg(TF_WARNING, "**** UpdateActiveDisplay: No display device!!!!"); ASSERT(FALSE); } _InSetInfo--; } } // --------------------------------------------------------------------------- // Initialize the resolution and color UI widgets // void CSettingsPage::_InitUI() { if (_pCurDevice && _pCurDevice->pds) { int i; int Color; // Update the Color list TraceMsg(TF_FUNC, "_InitUI() -- Color list"); SendDlgItemMessage(_hDlg, IDC_COLORBOX, CB_RESETCONTENT, 0, 0); if (_pCurDevice->ColorList) { LocalFree(_pCurDevice->ColorList); _pCurDevice->ColorList = NULL; } _pCurDevice->cColors = _pCurDevice->pds->GetColorList(NULL, &_pCurDevice->ColorList); for (i = 0; i < _pCurDevice->cColors; i++) { TCHAR achColor[50]; DWORD idColor = ID_DSP_TXT_TRUECOLOR32; Color = (int) *(_pCurDevice->ColorList + i); // // convert bit count to number of colors and make it a string // switch (Color) { case 32: idColor = ID_DSP_TXT_TRUECOLOR32; break; case 24: idColor = ID_DSP_TXT_TRUECOLOR24; break; case 16: idColor = ID_DSP_TXT_16BIT_COLOR; break; case 15: idColor = ID_DSP_TXT_15BIT_COLOR; break; case 8: idColor = ID_DSP_TXT_8BIT_COLOR; break; case 4: idColor = ID_DSP_TXT_4BIT_COLOR; break; default: ASSERT(FALSE); } LoadString(HINST_THISDLL, idColor, achColor, ARRAYSIZE(achColor)); SendDlgItemMessage(_hDlg, IDC_COLORBOX, CB_INSERTSTRING, i, (LPARAM)achColor); } // // Update the screen Size List // TraceMsg(TF_FUNC, "_InitUI() -- Screen Size list"); if (_pCurDevice->ResolutionList) { LocalFree(_pCurDevice->ResolutionList); _pCurDevice->ResolutionList = NULL; } _pCurDevice->cResolutions = _pCurDevice->pds->GetResolutionList(-1, &_pCurDevice->ResolutionList); SendDlgItemMessage(_hDlg, IDC_SCREENSIZE, TBM_SETRANGE, TRUE, MAKELONG(0, _pCurDevice->cResolutions - 1)); TraceMsg(TF_FUNC, "_InitUI() -- Res MaxRange = %d", _pCurDevice->cResolutions - 1); // Reset the indices since they are no longer valid _iResolution = -1; _iColor = -1; } } // --------------------------------------------------------------------------- // Update the resolution and color UI widgets // void CSettingsPage::_UpdateUI(BOOL fAutoSetColorDepth, int FocusToCtrlID) { if (_pCurDevice && _pCurDevice->pds) { int i; POINT Res; int Color; BOOL bRepaint; // Get the current values _pCurDevice->pds->GetCurResolution(&Res); Color = _pCurDevice->pds->GetCurColor(); // Update the color listbox TraceMsg(TF_FUNC, "_UpdateUI() -- Set Color %d", Color); for (i=0; i<_pCurDevice->cColors; i++) { if (Color == (int) *(_pCurDevice->ColorList + i)) { TraceMsg(TF_FUNC, "_UpdateUI() -- Set Color index %d", i); if (_iColor == i) { TraceMsg(TF_FUNC, "_UpdateUI() -- Set Color index %d - is current", i); break; } HBITMAP hbm, hbmOld; int iBitmap = IDB_COLOR4DITHER; HDC hdc = GetDC(NULL); int bpp = GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL); SendDlgItemMessage(_hDlg, IDC_COLORBOX, CB_SETCURSEL, i, 0); if (Color <= 4) iBitmap = IDB_COLOR4; else if (bpp >= 16) { if (Color <= 8) iBitmap = IDB_COLOR8; else if (Color <= 16) iBitmap = IDB_COLOR16; else iBitmap = IDB_COLOR24; } ReleaseDC(NULL, hdc); hbm = (HBITMAP)LoadImage(HINST_THISDLL, MAKEINTRESOURCE(iBitmap), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION); if (hbm) { hbmOld = (HBITMAP) SendDlgItemMessage(_hDlg, IDC_COLORSAMPLE, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbm); if (hbmOld) { DeleteObject(hbmOld); } } _iColor = i; break; } } if (i == _pCurDevice->cColors) { TraceMsg(TF_ERROR, "_UpdateUI -- !!! inconsistent color list !!!"); } TraceMsg(TF_FUNC, "_UpdateUI() -- Set Resolution %d %d", Res.x, Res.y); // Update the resolution string { TCHAR achStr[80]; TCHAR achRes[120]; LoadString(HINST_THISDLL, ID_DSP_TXT_XBYY, achStr, ARRAYSIZE(achStr)); StringCchPrintf(achRes, ARRAYSIZE(achRes), achStr, Res.x, Res.y); SendDlgItemMessage(_hDlg, IDC_RESXY, WM_SETTEXT, 0, (LPARAM)achRes); } // Update the resolution slider for (i=0; i<_pCurDevice->cResolutions; i++) { if ( (Res.x == (*(_pCurDevice->ResolutionList + i)).x) && (Res.y == (*(_pCurDevice->ResolutionList + i)).y) ) { TraceMsg(TF_FUNC, "_UpdateUI() -- Set Resolution index %d", i); if (_iResolution == i) { TraceMsg(TF_FUNC, "_UpdateUI() -- Set Resolution index %d - is current", i); break; } SendDlgItemMessage(_hDlg, IDC_SCREENSIZE, TBM_SETPOS, TRUE, i); break; } } if (i == _pCurDevice->cResolutions) { TraceMsg(TF_ERROR, "_UpdateUI -- !!! inconsistent color list !!!"); } bRepaint = (i != _iResolution); _iResolution = i; // If the resolution has changed, we have to repaint the preview window // Set the focus back to the trackbar after the repaint so any further // kb events will be send to it rather than the preview window if (bRepaint) { SendMessage(_hDlg, MM_REDRAWPREVIEW, 0, 0); } if (FocusToCtrlID != 0) { SetFocus(GetDlgItem(_hDlg, FocusToCtrlID)); } } } //---------------------------------------------------------------------------- // // SetPrimary() // //---------------------------------------------------------------------------- BOOL CSettingsPage::SetPrimary( PMULTIMON_DEVICE pDevice) { // // Check if state is already set. // if (pDevice == _pPrimaryDevice) { pDevice->pds->SetPrimary(TRUE); return TRUE; } ASSERT(pDevice->pds->IsAttached()); _pPrimaryDevice->pds->SetPrimary(FALSE); pDevice->pds->SetPrimary(TRUE); _pPrimaryDevice = pDevice; SetDirty(); return TRUE; } //---------------------------------------------------------------------------- // // SetMonAttached() // //---------------------------------------------------------------------------- BOOL CSettingsPage::SetMonAttached( PMULTIMON_DEVICE pDevice, BOOL bSetAttached, BOOL bForce, HWND hwnd) { if (pDevice->pds->IsAttached() == bSetAttached) { return TRUE; } if (bSetAttached) { // // Make sure this device actually has a rectangle. // If it does not (not configured in the registry, then we need // to put up a popup and ask the user to configure the device. // if (hwnd) { // // Check to see if we should ask the user about enabling this device // if (bForce == FALSE) { TCHAR szTurnItOn[400]; TCHAR szTurnOnTitleFormat[30]; TCHAR szTurnOnTitle[110]; LPTSTR pstr = szTurnItOn; DWORD chSize = ARRAYSIZE(szTurnItOn); LoadString(HINST_THISDLL, IDS_TURNONTITLE, szTurnOnTitleFormat, ARRAYSIZE(szTurnOnTitleFormat)); StringCchPrintf(szTurnOnTitle, ARRAYSIZE(szTurnOnTitle), szTurnOnTitleFormat, pDevice->DisplayIndex); if (GetNumberOfAttachedDisplays() == 1) { LoadString(HINST_THISDLL, IDS_TURNONMSG, szTurnItOn, ARRAYSIZE(szTurnItOn)); pstr += lstrlen(szTurnItOn); chSize -= lstrlen(szTurnItOn); } LoadString(HINST_THISDLL, IDS_TURNITON, pstr, chSize); if (ShellMessageBox(HINST_THISDLL, hwnd, szTurnItOn, szTurnOnTitle, MB_YESNO | MB_ICONINFORMATION) != IDYES) { return FALSE; } } } pDevice->pds->SetAttached(TRUE); } else // (bSetAttached == FALSE) { // // Can't detach if we have only one device or it's the primary. // The UI should disable this situation // if ((GetNumberOfAttachedDisplays() == 1) || pDevice->pds->IsPrimary()) { ASSERT(FALSE); } pDevice->pds->SetAttached(FALSE); } SetDirty(); return TRUE; } //---------------------------------------------------------------------------- // // SetDirty // //---------------------------------------------------------------------------- void CSettingsPage::SetDirty(BOOL bDirty) { _bDirty = bDirty; if (_bDirty) { EnableApplyButton(_hDlg); } } //----------------------------------------------------------------------------- void CSettingsPage::_CleanupRects(HWND hwndP) { int n; HWND hwndC; DWORD arcDev[MONITORS_MAX]; RECT arc[MONITORS_MAX]; DWORD iArcPrimary = 0; RECT rc; RECT rcU; int i; RECT rcPrev; int sx,sy; int x,y; // // get the positions of all the windows // n = 0; for (ULONG iDevice = 0; iDevice < _NumDevices; iDevice++) { PMULTIMON_DEVICE pDevice = &_Devices[iDevice]; hwndC = GetDlgItemP(hwndP, (INT_PTR) pDevice); if (hwndC != NULL) { RECT rcPos; RECT rcPreview; TraceMsg(TF_GENERAL, "_CleanupRects start Device %08lx, Dev = %d, hwnd = %08lx", pDevice, iDevice, hwndC); ShowWindow(hwndC, SW_SHOW); GetWindowRect(hwndC, &arc[n]); MapWindowPoints(NULL, hwndP, (POINT FAR*)&arc[n], 2); pDevice->pds->GetCurPosition(&rcPos); pDevice->pds->GetPreviewPosition(&rcPreview); _OffsetPreviewToDesk(hwndC, &arc[n], &rcPreview, &rcPos); arc[n] = rcPos; arcDev[n] = iDevice; // TEMP // For non-atached devices, make sure they end up to the right // Eventually, non-attached devices should be showed aligned on the // right hand side of the window. if (!pDevice->pds->IsAttached()) { OffsetRect(&arc[n], 10000, 0); } if (pDevice->pds->IsPrimary()) { TraceMsg(TF_GENERAL, "_CleanupRects primary Device %08lx", pDevice); iArcPrimary = n; } n++; } } // // cleanup the rects // AlignRects(arc, n, iArcPrimary, CUDR_NORMAL); // // Get the union. // SetRectEmpty(&rcU); for (i=0; i rcPrev.right || rcU.bottom > rcPrev.bottom || (x > (rcPrev.right-rcPrev.left)/8 && y > (rcPrev.bottom-rcPrev.top)/8)) { _rcDesk = rcU; sx = MulDiv(rcPrev.right - rcPrev.left - 16,1000,_rcDesk.right - _rcDesk.left); sy = MulDiv(rcPrev.bottom - rcPrev.top - 16,1000,_rcDesk.bottom - _rcDesk.top); _DeskScale = min(sx,sy) * 2 / 3; _DeskToPreview(&_rcDesk, &rc); _DeskOff.x = ((rcPrev.right - rcPrev.left)-(rc.right - rc.left))/2; _DeskOff.y = ((rcPrev.bottom - rcPrev.top) -(rc.bottom - rc.top))/2; } // // Show all the windows and save them all to the devmode. // for (i=0; i < n; i++) { RECT rcPos; POINT ptPos; _Devices[arcDev[i]].pds->GetCurPosition(&rcPos); hwndC = GetDlgItemP(hwndP, (INT_PTR) &_Devices[arcDev[i]]); _DeskToPreview(&arc[i], &rc); rc.right = MulDiv(RECTWIDTH(rcPos), _DeskScale, 1000); rc.bottom = MulDiv(RECTHEIGHT(rcPos), _DeskScale, 1000); TraceMsg(TF_GENERAL, "_CleanupRects set Dev = %d, hwnd = %08lx", arcDev[i], hwndC); TraceMsg(TF_GENERAL, "_CleanupRects window pos %d,%d,%d,%d", rc.left, rc.top, rc.right, rc.bottom); SetWindowPos(hwndC, NULL, rc.left, rc.top, rc.right, rc.bottom, SWP_NOZORDER); rc.right += rc.left; rc.bottom += rc.top; _Devices[arcDev[i]].pds->SetPreviewPosition(&rc); ptPos.x = arc[i].left; ptPos.y = arc[i].top; _Devices[arcDev[i]].pds->SetCurPosition(&ptPos); } TraceMsg(TF_GENERAL, ""); } void CSettingsPage::_ConfirmPositions() { ASSERT (_NumDevices > 1); PMULTIMON_DEVICE pDevice; ULONG iDevice; for (iDevice = 0; iDevice < _NumDevices; iDevice++) { pDevice = &_Devices[iDevice]; if (pDevice->pds->IsOrgAttached()) { RECT rcOrg, rcCur; pDevice->pds->GetCurPosition(&rcCur); pDevice->pds->GetOrgPosition(&rcOrg); if ((rcCur.left != rcOrg.left) || (rcCur.top != rcOrg.top)) { POINT ptOrg; ptOrg.x = rcCur.left; ptOrg.y = rcCur.top; pDevice->pds->SetOrgPosition(&ptOrg); SetDirty(TRUE); } } } } void CSettingsPage::GetMonitorPosition(PMULTIMON_DEVICE pDevice, HWND hwndP, PPOINT ptPos) { int iPrimary = 0; HWND hwndC; RECT rcPos; RECT rcPreview; RECT arc[MONITORS_MAX]; int i; for (ULONG iDevice = 0; iDevice < _NumDevices; iDevice++) { PMULTIMON_DEVICE pDevice = &_Devices[iDevice]; hwndC = GetDlgItemP(hwndP, (INT_PTR) pDevice); ASSERT(hwndC); GetWindowRect(hwndC, &arc[iDevice]); MapWindowPoints(NULL, hwndP, (POINT FAR*)&arc[iDevice], 2); pDevice->pds->GetCurPosition(&rcPos); pDevice->pds->GetPreviewPosition(&rcPreview); _OffsetPreviewToDesk(hwndC, &arc[iDevice], &rcPreview, &rcPos); arc[iDevice] = rcPos; if (pDevice->pds->IsPrimary()) { iPrimary = iDevice; } } AlignRects(arc, iDevice, iPrimary, CUDR_NORMAL); i = (int)(pDevice - _Devices); ptPos->x = arc[i].left; ptPos->y = arc[i].top; } BOOL CSettingsPage::HandleMonitorChange(HWND hwndP, BOOL bMainDlg, BOOL bRepaint /*=TRUE*/) { if (!bMainDlg && _InSetInfo) return FALSE; SetDirty(); if (bMainDlg) BringWindowToTop(hwndP); _CleanupRects(GetParent(hwndP)); UpdateActiveDisplay(_pCurDevice, bRepaint); return TRUE; } BOOL CSettingsPage::RegisterPreviewWindowClass(WNDPROC pfnWndProc) { TraceMsg(TF_GENERAL, "InitMultiMonitorDlg\n"); WNDCLASS cls; cls.hCursor = LoadCursor(NULL,IDC_ARROW); cls.hIcon = NULL; cls.lpszMenuName = NULL; cls.lpszClassName = TEXT("Monitor32"); cls.hbrBackground = (HBRUSH)(COLOR_DESKTOP + 1); cls.hInstance = HINST_THISDLL; cls.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS; cls.lpfnWndProc = pfnWndProc; cls.cbWndExtra = sizeof(LPVOID); cls.cbClsExtra = 0; return RegisterClass(&cls); } LRESULT CALLBACK DeskWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uID, DWORD_PTR dwRefData); // This function is called from desk.c; Hence extern "C". // This function is needed to determine if we need to use the single monitor's dialog // or multi-monitor's dialog template at the time of starting the control panel applet. int ComputeNumberOfDisplayDevices(void) { int iNumberOfDevices = 0; CSettingsPage * pMultiMon = new CSettingsPage; if (pMultiMon) { int iDevice; // Enumerate all display devices to count the number of valid devices. iNumberOfDevices = pMultiMon->_EnumerateAllDisplayDevices(); // Now that we have the number of devices, let's cleanup the device settings we // created in the process of enumerating above. for (iDevice = 0; iDevice < iNumberOfDevices; iDevice++) pMultiMon->_DestroyMultimonDevice(&pMultiMon->_Devices[iDevice]); // Let's clean up the MultiMon we allocated earlier. pMultiMon->Release(); } return iNumberOfDevices; } int ComputeNumberOfMonitorsFast(BOOL fFastDetect) { int nVideoCards = 0; int nIndex; DISPLAY_DEVICE dispDevice = {0}; dispDevice.cb = sizeof(dispDevice); for (nIndex = 0; EnumDisplayDevices(NULL, nIndex, &dispDevice, 0); nIndex++) { // Fast Detect means the caller only cares if there are more than 1. if (fFastDetect && (nVideoCards > 1)) { break; } dispDevice.cb = sizeof(dispDevice); if (!(dispDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER)) { nVideoCards++; } } return nVideoCards; } BOOL CSettingsPage::_InitDisplaySettings(BOOL bExport) { HWND hwndC; int iItem; LONG iPrimeDevice = 0; TCHAR ach[128]; PMULTIMON_DEVICE pDevice; RECT rcPrimary; HCURSOR hcur; _InSetInfo = 1; hcur = SetCursor(LoadCursor(NULL, IDC_WAIT)); // // Reset all the data so we can reinitialize the applet. // { ComboBox_ResetContent(_hwndList); SetRectEmpty(&_rcDesk); hwndC = GetWindow(_hwndDesk, GW_CHILD); while (hwndC) { RemoveTrackingToolTip(hwndC); RemovePopupToolTip(hwndC); DestroyWindow(hwndC); hwndC = GetWindow(_hwndDesk, GW_CHILD); } ShowWindow(_hwndDesk, SW_HIDE); if (_himl != NULL) { ImageList_Destroy(_himl); _himl = NULL; } // // Clear out all the devices. // for (ULONG iDevice = 0; iDevice < _NumDevices; iDevice++) { pDevice = _Devices + iDevice; _DestroyMultimonDevice(pDevice); ZeroMemory(pDevice, sizeof(*pDevice)); } ZeroMemory(_Devices + _NumDevices, sizeof(_Devices) - sizeof(MULTIMON_DEVICE) * _NumDevices); _NumDevices = 0; } // // Enumerate all the devices in the system. // // Note: This function computes the _NumDevices. _EnumerateAllDisplayDevices(); if (_NumDevices == 0) { ASSERT(0); return FALSE; } // Because we are getting the registry values, the current state of // the registry may be inconsistent with that of the system: // // EmumDisplayDevices will return the active primary in the // system, which may be different than the actual primary marked in the // registry BOOL bTmpDevicePrimary = FALSE; ULONG iDevice; _pPrimaryDevice = NULL; for (iDevice = 0; iDevice < _NumDevices; iDevice++) { // First, we can pick any monitor that is attached as the primary. if (_Devices[iDevice].pds->IsAttached()) { if ((_pPrimaryDevice == NULL) && !_Devices[iDevice].pds->IsRemovable()) { _pPrimaryDevice = &_Devices[iDevice]; TraceMsg(TF_GENERAL, "InitDisplaySettings: primary found %d\n", iDevice); } // If the DISPLAY_DEVICE structure tells us this is the primary, // Pick this one. if (_Devices[iDevice].DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) { if (bTmpDevicePrimary) { ASSERT(FALSE); } else { _pPrimaryDevice = &_Devices[iDevice]; bTmpDevicePrimary = TRUE; TraceMsg(TF_GENERAL, "InitDisplaySettings: Tmp DEVICE_PRIMARY found %d", iDevice); } // Check that the position should really be 0,0 RECT pos; _Devices[iDevice].pds->GetCurPosition(&pos); if ((pos.left == 0) && (pos.top == 0)) { _pPrimaryDevice = &_Devices[iDevice]; TraceMsg(TF_GENERAL, "InitDisplaySettings: Best DEVICE_PRIMARY found %d", iDevice); } else { ASSERT(FALSE); TraceMsg(TF_GENERAL, "InitDisplaySettings: PRIMARY is not at 0,0"); } } } } if (_pPrimaryDevice == NULL) { TraceMsg(TF_GENERAL, "InitDisplaySettings: NO Attached devices !!!"); // We must be running setup. // Pick the first non-removable device as the primary. for (iDevice = 0; iDevice < _NumDevices; iDevice++) { if (!_Devices[iDevice].pds->IsRemovable()) { _pPrimaryDevice = &_Devices[iDevice]; break; } } if (_pPrimaryDevice == NULL) { ASSERT(FALSE); TraceMsg(TF_GENERAL, "InitDisplaySettings: All devices are removable !!!"); _pPrimaryDevice = &_Devices[0]; } } _pCurDevice = _pPrimaryDevice; // // Reset the primary's variables to make sure it is a properly formated // primary entry. // SetMonAttached(_pPrimaryDevice, TRUE, TRUE, NULL); SetPrimary(_pPrimaryDevice); _pPrimaryDevice->pds->GetCurPosition(&rcPrimary); // // compute the max image size needed for a monitor bitmap // // NOTE this must be the max size the images will *ever* // be we cant just take the current max size. // we use the client window size, a child monitor cant be larger than this. // RECT rcDesk; GetClientRect(_hwndDesk, &rcDesk); int cxImage = rcDesk.right; int cyImage = rcDesk.bottom; // // Create a temporary monitor bitmap // HBITMAP hbm = NULL; MakeMonitorBitmap(cxImage, cyImage, NULL, &hbm, NULL, cxImage, cyImage, FALSE); // // Go through all the devices one last time to create the windows // for (iDevice = 0; iDevice < _NumDevices; iDevice++) { TCHAR szDisplay[256]; pDevice = &_Devices[iDevice]; MonitorData md = {0}; RECT rcPos; LPVOID pWindowData = (LPVOID)this; pDevice->DisplayIndex = iDevice + 1; _GetDisplayName(pDevice, szDisplay, ARRAYSIZE(szDisplay)); iItem = ComboBox_AddString(_hwndList, szDisplay); pDevice->ComboBoxItem = iItem; ComboBox_SetItemData(_hwndList, iItem, (DWORD_PTR)pDevice); // // If the monitor is part of the desktop, show it on the screen // otherwise keep it invisible. // StringCchPrintf(ach, ARRAYSIZE(ach), TEXT("%d"), iDevice + 1); // Set the selection // if (pDevice == _pPrimaryDevice) { iPrimeDevice = iDevice; } if (!pDevice->pds->IsAttached()) { // By default set the unattached monitors to the right of the primary monitor POINT ptPos = {rcPrimary.right, rcPrimary.top}; pDevice->pds->SetCurPosition(&ptPos); } pDevice->pds->GetCurPosition(&rcPos); if (bExport) { md.dwSize = sizeof(MonitorData); if ( pDevice->pds->IsPrimary() ) md.dwStatus |= MD_PRIMARY; if ( pDevice->pds->IsAttached() ) md.dwStatus |= MD_ATTACHED; md.rcPos = rcPos; pWindowData = &md; } if (_himl == NULL) { UINT flags = ILC_COLORDDB | ILC_MASK; _himl = ImageList_Create(cxImage, cyImage, flags, _NumDevices, 1); ASSERT(_himl); ImageList_SetBkColor(_himl, GetSysColor(COLOR_APPWORKSPACE)); } pDevice->w = -1; pDevice->h = -1; pDevice->himl = _himl; pDevice->iImage = ImageList_AddMasked(_himl, hbm, CLR_DEFAULT); TraceMsg(TF_GENERAL, "InitDisplaySettings: Creating preview windows %s at %d %d %d %d", ach, rcPos.left, rcPos.top, rcPos.right, rcPos.bottom); // HACK! Use pDevice as its own id. Doesn't work on Win64. hwndC = CreateWindowEx( 0, TEXT("Monitor32"), ach, WS_CLIPSIBLINGS | WS_VISIBLE | WS_CHILD, rcPos.left, rcPos.top, RECTWIDTH(rcPos), RECTHEIGHT(rcPos), _hwndDesk, (HMENU)pDevice, HINST_THISDLL, pWindowData); ASSERT(hwndC); AddTrackingToolTip(pDevice, hwndC); AddPopupToolTip(hwndC); } ToolTip_Activate(ghwndToolTipPopup, TRUE); ToolTip_SetDelayTime(ghwndToolTipPopup, TTDT_INITIAL, 1000); ToolTip_SetDelayTime(ghwndToolTipPopup, TTDT_RESHOW, 1000); // nuke the temp monitor bitmap. if (hbm) DeleteObject(hbm); // // Set the primary device as the current device // ComboBox_SetCurSel(_hwndList, iPrimeDevice); // Initialize all the constants and the settings fields _DeskScale = 1000; _DeskOff.x = 0; _DeskOff.y = 0; _CleanupRects(_hwndDesk); // Now: depends on whether we have a multimon system, change the UI if (_NumDevices == 1) { HWND hwndDisable; hwndDisable = GetDlgItem(_hDlg, IDC_MULTIMONHELP); ShowWindow(hwndDisable, SW_HIDE); ShowWindow(_hwndDesk, SW_HIDE); // set up bitmaps for sample screen _hbmScrSample = LoadMonitorBitmap( TRUE ); // let them do the desktop SendDlgItemMessage(_hDlg, IDC_SCREENSAMPLE, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)_hbmScrSample); // get a base copy of the bitmap for when the "internals" change _hbmMonitor = LoadMonitorBitmap( FALSE ); // we'll do the desktop //Hide the combo box, keep the static text ShowWindow(_hwndList, SW_HIDE); //Set the name of the primary in the static text //strip the first token off (this is the number we dont want it) TCHAR *pch, szDisplay[MAX_PATH]; _GetDisplayName(_pPrimaryDevice, szDisplay, ARRAYSIZE(szDisplay)); for (pch=szDisplay; *pch && *pch != TEXT(' '); pch++); for (;*pch && *pch == TEXT(' '); pch++); SetDlgItemText(_hDlg, IDC_DISPLAYTEXT, pch); // Hide the check boxes // Single monitors use a different dialog template now! hwndDisable = GetDlgItem(_hDlg, IDC_DISPLAYPRIME); if(hwndDisable) ShowWindow(hwndDisable, SW_HIDE); hwndDisable = GetDlgItem(_hDlg, IDC_DISPLAYUSEME); if(hwndDisable) ShowWindow(hwndDisable, SW_HIDE); } else if (_NumDevices > 0) { //Hide the static text, keep the combo box ShowWindow(GetDlgItem(_hDlg, IDC_DISPLAYTEXT), SW_HIDE); // Hide the Multimon version of the preview objects ShowWindow(GetDlgItem(_hDlg, IDC_SCREENSAMPLE), SW_HIDE); // In case of multiple devices, subclass the _hwndDesk window for key board support SetWindowSubclass(_hwndDesk, DeskWndProc, 0, (DWORD_PTR)this); ShowWindow(_hwndDesk, SW_SHOW); } // // Paint the UI. // UpdateActiveDisplay(_pCurDevice); // // Reset the cursor and leave // SetCursor(hcur); _InSetInfo--; return TRUE; } // // This function enumerates all the devices and returns the number of // devices found in the system. // int CSettingsPage::_EnumerateAllDisplayDevices() { PMULTIMON_DEVICE pDevice; int iEnum; BOOL fSuccess; ULONG dwVgaPrimary = 0xFFFFFFFF; // // Enumerate all the devices in the system. // for (iEnum = 0; _NumDevices < MONITORS_MAX; iEnum++) { pDevice = &_Devices[_NumDevices]; ZeroMemory(&(pDevice->DisplayDevice), sizeof(pDevice->DisplayDevice)); pDevice->DisplayDevice.cb = sizeof(pDevice->DisplayDevice); fSuccess = EnumDisplayDevices(NULL, iEnum, &pDevice->DisplayDevice, 0); TraceMsg(TF_GENERAL, "Device %d ", iEnum); TraceMsg(TF_GENERAL, "cb %d ", pDevice->DisplayDevice.cb); TraceMsg(TF_GENERAL, "DeviceName %ws ", pDevice->DisplayDevice.DeviceName); TraceMsg(TF_GENERAL, "DeviceString %ws", pDevice->DisplayDevice.DeviceString); TraceMsg(TF_GENERAL, "StateFlags %08lx", pDevice->DisplayDevice.StateFlags); // ignore device's we cant create a DC for. if (!fSuccess) { TraceMsg(TF_GENERAL, "End of list\n"); break; } // We won't even include the MIRRORING drivers in the list for // now. if (pDevice->DisplayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) { TraceMsg(TF_GENERAL, "Mirroring driver - skip it\n"); continue; } // dump the device software key TraceMsg(TF_GENERAL, "DeviceKey %s", pDevice->DisplayDevice.DeviceKey); // Create the settings for this device pDevice->pds = new CDisplaySettings(); if (pDevice->pds) { if (pDevice->pds->InitSettings(&pDevice->DisplayDevice)) { // Determine if the VGA is the primary. // This will only happen for SETUP or BASEVIDEO // // We want to delete this device later on if we have others. if (pDevice->DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) { CRegistrySettings crv(&pDevice->DisplayDevice.DeviceKey[0]); LPTSTR pszMini = crv.GetMiniPort(); // If VGA is active, then go to pass 2. // Otherwise, let's try to use this device // if (pszMini && (!lstrcmpi(TEXT("vga"), pszMini))) { TraceMsg(TF_GENERAL, "EnumDevices - VGA primary\n"); dwVgaPrimary = _NumDevices; } } // Add it to the list. _NumDevices++; } else { pDevice->pds->Release(); pDevice->pds = NULL; } } } // // If the primary VGA is not needed, remove it. // if ((dwVgaPrimary != 0xFFFFFFFF) && (_NumDevices >= 2)) { TraceMsg(TF_GENERAL, "REMOVE primary VGA device\n"); _Devices[dwVgaPrimary].pds->Release(); _Devices[dwVgaPrimary].pds = NULL; _NumDevices--; _Devices[dwVgaPrimary] = _Devices[_NumDevices]; } return(_NumDevices); //Return the number of devices. } //----------------------------------------------------------------------------- BOOL CSettingsPage::InitMultiMonitorDlg(HWND hDlg) { HWND hwndSlider; BOOL fSucceeded; _hDlg = hDlg; _hwndDesk = GetDlgItem(_hDlg, IDC_DISPLAYDESK); _hwndList = GetDlgItem(_hDlg, IDC_DISPLAYLIST); hwndSlider = GetDlgItem(hDlg, IDC_SCREENSIZE); ASSERT(hwndSlider != NULL); fSucceeded = SetWindowSubclass(hwndSlider, SliderSubWndProc, 0, NULL); ASSERT(fSucceeded); // Determine in what mode we are running the applet before getting information _vPreExecMode(); // Create a tooltip window ghwndToolTipTracking = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, TEXT(""), WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, HINST_THISDLL, NULL); ghwndToolTipPopup = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, TEXT(""), WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, HINST_THISDLL, NULL); RegisterPreviewWindowClass(&MonitorWindowProc); _InitDisplaySettings(FALSE); if (_NumDevices > 1) _ConfirmPositions(); if (ClassicGetSystemMetrics(SM_REMOTESESSION)) { EnableWindow(GetDlgItem(_hDlg, IDC_DISPLAYPROPERTIES), FALSE); } // Determine if any errors showed up during enumerations and initialization _vPostExecMode(); // Now tell the user what we found out during initialization // Errors, or what we found during detection PostMessage(hDlg, MSG_DSP_SETUP_MESSAGE, 0, 0); // Since this could have taken a very long time, just make us visible // if another app (like progman) came up. ShowWindow(hDlg, SW_SHOW); return TRUE; } LRESULT CALLBACK DeskWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uID, DWORD_PTR dwRefData) { CSettingsPage * pcmm = (CSettingsPage *)dwRefData; HWND hwndC; RECT rcPos; BOOL bMoved = TRUE; int iMonitor, nMoveUnit; switch(message) { case WM_GETDLGCODE: return DLGC_WANTCHARS | DLGC_WANTARROWS; case WM_KILLFOCUS: RedrawWindow(hDlg, NULL, NULL, RDW_INVALIDATE); break; case WM_MOUSEMOVE: { MSG mmsg; ToolTip_RelayEvent(ghwndToolTipPopup, mmsg, hDlg, message, wParam, lParam); } break; case WM_PAINT: if (GetFocus() != hDlg) break; return(DefSubclassProc(hDlg, message, wParam, lParam)); break; case WM_LBUTTONDOWN: SetFocus(hDlg); break; case WM_KEYDOWN: nMoveUnit = ((GetKeyState(VK_CONTROL) & 0x8000) ? 1 : 3); hwndC = pcmm->GetCurDeviceHwnd(); GetWindowRect(hwndC, &rcPos); MapWindowRect(NULL, hDlg, &rcPos); switch(wParam) { case VK_LEFT: MoveWindow(hwndC, rcPos.left - nMoveUnit, rcPos.top, RECTWIDTH(rcPos), RECTHEIGHT(rcPos), TRUE); break; case VK_RIGHT: MoveWindow(hwndC, rcPos.left + nMoveUnit, rcPos.top, RECTWIDTH(rcPos), RECTHEIGHT(rcPos), TRUE); break; case VK_UP: MoveWindow(hwndC, rcPos.left, rcPos.top - nMoveUnit, RECTWIDTH(rcPos), RECTHEIGHT(rcPos), TRUE); break; case VK_DOWN: MoveWindow(hwndC, rcPos.left, rcPos.top + nMoveUnit, RECTWIDTH(rcPos), RECTHEIGHT(rcPos), TRUE); break; default: bMoved = FALSE; break; } if (bMoved) { pcmm->HandleMonitorChange(hwndC, FALSE, FALSE); if (IsWindowVisible(ghwndToolTipPopup)) { ToolTip_Update(ghwndToolTipPopup); } } break; case WM_CHAR: if (wParam >= TEXT('0') && wParam <= TEXT('9') && pcmm) { iMonitor = (TCHAR)wParam - TEXT('0'); if ((iMonitor == 0) && (pcmm->GetNumDevices() >= 10)) { iMonitor = 10; } if ((iMonitor > 0) && ((ULONG)iMonitor <= pcmm->GetNumDevices())) { HWND hwndList = GetDlgItem(GetParent(hDlg), IDC_DISPLAYLIST); ComboBox_SetCurSel(hwndList, iMonitor - 1); pcmm->UpdateActiveDisplay(NULL); return 0; } } break; case WM_DESTROY: RemoveWindowSubclass(hDlg, DeskWndProc, 0); break; default: break; } return DefSubclassProc(hDlg, message, wParam, lParam); } //----------------------------------------------------------------------------- // // Callback functions PropertySheet can use // INT_PTR CALLBACK CSettingsPage::SettingsDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { CSettingsPage * pcmm = (CSettingsPage *) GetWindowLongPtr(hDlg, DWLP_USER); switch (message) { case WM_INITDIALOG: { PROPSHEETPAGE * pPropSheetPage = (PROPSHEETPAGE *) lParam; if (pPropSheetPage) { SetWindowLongPtr(hDlg, DWLP_USER, pPropSheetPage->lParam); pcmm = (CSettingsPage *)pPropSheetPage->lParam; } if (pcmm) { SetWindowLongPtr(hDlg, DWLP_USER, (LPARAM)pcmm); ghwndPropSheet = GetParent(hDlg); SetWindowLong(ghwndPropSheet, GWL_STYLE, GetWindowLong(ghwndPropSheet, GWL_STYLE) | WS_CLIPCHILDREN); if (pcmm->InitMultiMonitorDlg(hDlg)) { // // if we have a invalid mode force the user to Apply // DWORD dwExecMode; if (pcmm->_pThemeUI && (SUCCEEDED(pcmm->_pThemeUI->GetExecMode(&dwExecMode)))) { if (dwExecMode == EM_INVALID_MODE) pcmm->SetDirty(); } return TRUE; } else return FALSE; } } break; case WM_DESTROY: if (pcmm) { pcmm->WndProc(message, wParam, lParam); SetWindowLongPtr(hDlg, DWLP_USER, NULL); } if(gfFlashWindowRegistered) { gfFlashWindowRegistered = FALSE; UnregisterClass(TEXT("MonitorNumber32"), HINST_THISDLL); } break; default: if (pcmm) return pcmm->WndProc(message, wParam, lParam); break; } return FALSE; } void CSettingsPage::_SetPreviewScreenSize(int HRes, int VRes, int iOrgXRes, int iOrgYRes) { HBITMAP hbmOld, hbmOld2; HBRUSH hbrOld; HDC hdcMem, hdcMem2; // stretching the taskbar could get messy, we'll only do the desktop int mon_dy = MON_DY - MON_TRAY; // init to identical extents SIZE dSrc = { MON_DX, mon_dy }; SIZE dDst = { MON_DX, mon_dy }; // set up a work area to play in if (!_hbmMonitor || !_hbmScrSample) return; HDC hdc = GetDC(NULL); hdcMem = CreateCompatibleDC(hdc); hdcMem2 = CreateCompatibleDC(hdc); ReleaseDC(NULL, hdc); if (!hdcMem2 || !hdcMem) return; hbmOld2 = (HBITMAP)SelectObject(hdcMem2, _hbmScrSample); hbmOld = (HBITMAP)SelectObject(hdcMem, _hbmMonitor); // see if we need to shrink either aspect of the image if (HRes > iOrgXRes || VRes > iOrgYRes) { // make sure the uncovered area will be seamless with the desktop RECT rc = { MON_X, MON_Y, MON_X + MON_DX, MON_Y + mon_dy }; HBRUSH hbr = CreateSolidBrush(GetPixel( hdcMem, MON_X + 1, MON_Y + 1 )); if (hbr) { FillRect(hdcMem2, &rc, hbr); DeleteObject(hbr); } } // stretch the image to reflect the new resolution if( HRes > iOrgXRes ) dDst.cx = MulDiv( MON_DX, iOrgXRes, HRes ); else if( HRes < iOrgXRes ) dSrc.cx = MulDiv( MON_DX, HRes, iOrgXRes ); if( VRes > iOrgYRes ) dDst.cy = MulDiv( mon_dy, iOrgYRes, VRes ); else if( VRes < iOrgYRes ) dSrc.cy = MulDiv( mon_dy, VRes, iOrgYRes ); SetStretchBltMode( hdcMem2, COLORONCOLOR ); StretchBlt( hdcMem2, MON_X, MON_Y, dDst.cx, dDst.cy, hdcMem, MON_X, MON_Y, dSrc.cx, dSrc.cy, SRCCOPY); // now fill the new image's desktop with the possibly-dithered brush // the top right corner seems least likely to be hit by the stretch... hbrOld = (HBRUSH)SelectObject( hdcMem2, GetSysColorBrush( COLOR_DESKTOP ) ); ExtFloodFill(hdcMem2, MON_X + MON_DX - 2, MON_Y+1, GetPixel(hdcMem2, MON_X + MON_DX - 2, MON_Y+1), FLOODFILLSURFACE); // clean up after ourselves SelectObject( hdcMem2, hbrOld ); SelectObject( hdcMem2, hbmOld2 ); DeleteObject( hdcMem2 ); SelectObject( hdcMem, hbmOld ); DeleteObject( hdcMem ); } void CSettingsPage::_RedrawDeskPreviews() { if (_NumDevices > 1) { _CleanupRects(_hwndDesk); RedrawWindow(_hwndDesk, NULL, NULL, RDW_ALLCHILDREN | RDW_ERASE | RDW_INVALIDATE); } else if (_pCurDevice && _pCurDevice->pds) { RECT rcPos, rcOrgPos; _pCurDevice->pds->GetCurPosition(&rcPos); _pCurDevice->pds->GetOrgPosition(&rcOrgPos); _SetPreviewScreenSize(RECTWIDTH(rcPos), RECTHEIGHT(rcPos), RECTWIDTH(rcOrgPos), RECTHEIGHT(rcOrgPos)); // only invalidate the "screen" part of the monitor bitmap rcPos.left = MON_X; rcPos.top = MON_Y; rcPos.right = MON_X + MON_DX + 2; // fudge (trust me) rcPos.bottom = MON_Y + MON_DY + 1; // fudge (trust me) InvalidateRect(GetDlgItem(_hDlg, IDC_SCREENSAMPLE), &rcPos, FALSE); } } int DisplaySaveSettings(PVOID pContext, HWND hwnd) { CDisplaySettings *rgpds[1]; rgpds[0] = (CDisplaySettings*) pContext; if(rgpds[0]->bIsModeChanged()) return CSettingsPage::_DisplaySaveSettings(rgpds, 1, hwnd); else return DISP_CHANGE_SUCCESSFUL; } int CSettingsPage::_DisplaySaveSettings(CDisplaySettings *rgpds[], ULONG numDevices, HWND hDlg) { BOOL bReboot = FALSE; BOOL bTest = FALSE; int iSave; ULONG iDevice; POINT ptCursorSave; // Test the new settings first iSave = _SaveSettings(rgpds, numDevices, hDlg, CDS_TEST); if (iSave < DISP_CHANGE_SUCCESSFUL) { FmtMessageBox(hDlg, MB_ICONEXCLAMATION, IDS_CHANGE_SETTINGS, IDS_SETTINGS_INVALID); return iSave; } int iDynaResult; // Ask first and then change the settings. if (!bReboot && (_AnyChange(rgpds, numDevices) || _IsSingleToMultimonChange(rgpds, numDevices))) { iDynaResult = AskDynaCDS(hDlg); if (iDynaResult == -1) { return DISP_CHANGE_NOTUPDATED; } else if (iDynaResult == 0) { bReboot = TRUE; } } if (!bReboot && _AnyChange(rgpds, numDevices) && !_CanSkipWarningBecauseKnownSafe(rgpds, numDevices)) { bTest = TRUE; } // Save the settings to the registry. iSave = _SaveSettings(rgpds, numDevices, hDlg, CDS_UPDATEREGISTRY | CDS_NORESET); if (iSave < DISP_CHANGE_SUCCESSFUL) { // NOTE // If we get NOT_UPDATED, this mean security may be turned on. // We could still try to do the dynamic change. // This only works in single mon ... if (iSave == DISP_CHANGE_NOTUPDATED) { FmtMessageBox(hDlg, MB_ICONEXCLAMATION, IDS_CHANGE_SETTINGS, IDS_SETTINGS_CANNOT_SAVE); } else { FmtMessageBox(hDlg, MB_ICONEXCLAMATION, IDS_CHANGE_SETTINGS, IDS_SETTINGS_FAILED_SAVE); } // Restore the settings to their original state for (iDevice = 0; iDevice < numDevices; iDevice++) { rgpds[iDevice]->RestoreSettings(); } _SaveSettings(rgpds, numDevices, hDlg, CDS_UPDATEREGISTRY | CDS_NORESET); return iSave; } if (bReboot) { iSave = DISP_CHANGE_RESTART; } // Try to change the mode dynamically if it was requested GetCursorPos(&ptCursorSave); if (iSave == DISP_CHANGE_SUCCESSFUL) { // If EnumDisplaySettings was called with EDS_RAWMODE, we need CDS_RAWMODE below. // Otherwise, it's harmless. iSave = ChangeDisplaySettings(NULL, CDS_RAWMODE); //We post a message to ourselves to destroy it later. // Check the return from the dynamic mode switch. if (iSave < 0) { DWORD dwMessage = ((iSave == DISP_CHANGE_BADDUALVIEW) ? IDS_CHANGESETTINGS_BADDUALVIEW : IDS_DYNAMIC_CHANGESETTINGS_FAILED); FmtMessageBox(hDlg, MB_ICONEXCLAMATION, IDS_CHANGE_SETTINGS, dwMessage); } else if (iSave == DISP_CHANGE_SUCCESSFUL) { // Set the cursor to where it was before we changed the display // (ie, if we changed a 2ndary monitor, the cursor would have been // placed on the primary after the application of the change, move // it back to the 2ndary monitor. If the change failed, we are // just placing the cursor back to it orig pos SetCursorPos(ptCursorSave.x, ptCursorSave.y); // Determine what to do based on the return code. if (bTest && (IDYES != DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_KEEPNEW), GetParent(hDlg), KeepNewDlgProc, 15))) { iSave = DISP_CHANGE_NOTUPDATED; } } } // Determine what to do based on the return code. if (iSave >= DISP_CHANGE_SUCCESSFUL) { // Confirm the settings for (iDevice = 0; iDevice < numDevices; iDevice++) { rgpds[iDevice]->ConfirmChangeSettings(); } } else { // Restore the settings to their original state for (iDevice = 0; iDevice < numDevices; iDevice++) { rgpds[iDevice]->RestoreSettings(); } //DLI: This last function call will actually go refresh the whole desktop // If EnumDisplaySettings was called with EDS_RAWMODE, we need CDS_RAWMODE below. // Otherwise, it's harmless. ChangeDisplaySettings(NULL, CDS_RAWMODE); } return iSave; } //----------------------------------------------------------------------------- // // Resolution slider // CSettingsPage::_HandleHScroll(HWND hwndTB, int iCode, int iPos) { if (_pCurDevice && _pCurDevice->pds) { int iRes = _iResolution; int cRes = (int)SendMessage(hwndTB, TBM_GETRANGEMAX, TRUE, 0); TraceMsg(TF_FUNC, "_HandleHScroll: MaxRange = %d, iRes = %d, iPos = %d", cRes, iRes, iPos); // Message box if something bad is going to happen ? // _VerifyPrimaryMode(TRUE); switch (iCode) { case TB_LINEUP: case TB_PAGEUP: if (iRes != 0) iRes--; break; case TB_LINEDOWN: case TB_PAGEDOWN: if (++iRes >= cRes) iRes = cRes; break; case TB_BOTTOM: iRes = cRes; break; case TB_TOP: iRes = 0; break; case TB_THUMBTRACK: case TB_THUMBPOSITION: iRes = iPos; break; default: return FALSE; } TraceMsg(TF_FUNC, "_HandleHScroll: iRes = %d, iCode = %d", iRes, iCode); // We only want to automatically set the color depth for the user if they // changed the resolution. (not just set focus to the control) BOOL fAutoSetColorDepth = (_iResolution != iRes); // iPos _pCurDevice->pds->SetCurResolution(_pCurDevice->ResolutionList + iRes, fAutoSetColorDepth); // Repaint the control in case they changed _UpdateUI(TRUE /*fAutoSetColorDepth*/, IDC_SCREENSIZE); DWORD dwExecMode; if (_pThemeUI && (SUCCEEDED(_pThemeUI->GetExecMode(&dwExecMode)))) { if ( (dwExecMode == EM_NORMAL) || (dwExecMode == EM_INVALID_MODE) || (dwExecMode == EM_DETECT) ) { // // Set the apply button if resolution has changed // if (_pCurDevice->pds->bIsModeChanged()) SetDirty(); return 0; } } } return TRUE; } void CSettingsPage::_ForwardToChildren(UINT message, WPARAM wParam, LPARAM lParam) { HWND hwndC = GetDlgItem(_hDlg, IDC_SCREENSIZE); if (hwndC) SendMessage(hwndC, message, wParam, lParam); } LRESULT CALLBACK CSettingsPage::WndProc(UINT message, WPARAM wParam, LPARAM lParam) { NMHDR FAR * lpnm; HWND hwndC; HWND hwndSample; HBITMAP hbm; switch (message) { case WM_NOTIFY: lpnm = (NMHDR FAR *)lParam; switch (lpnm->code) { case PSN_APPLY: return TRUE; default: return FALSE; } break; case WM_CTLCOLORSTATIC: if (GetDlgCtrlID((HWND)lParam) == IDC_DISPLAYDESK) { return (UINT_PTR)GetSysColorBrush(COLOR_APPWORKSPACE); } return FALSE; case WM_COMMAND: switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_DISPLAYPRIME: if (!SetPrimary(_pCurDevice)) { return FALSE; } hwndC = GetCurDeviceHwnd(); HandleMonitorChange(hwndC, TRUE); break; case IDC_DISPLAYUSEME: // Don't pop up warning dialog box if this display is already attached // or if there are already more than 1 display if (!_pCurDevice || !SetMonAttached(_pCurDevice, !_pCurDevice->pds->IsAttached(), TRUE, _hDlg)) { return FALSE; } hwndC = GetCurDeviceHwnd(); HandleMonitorChange(hwndC, TRUE); break; case IDC_DISPLAYLIST: switch (GET_WM_COMMAND_CMD(wParam, lParam)) { case CBN_DBLCLK: goto DoDeviceSettings; case CBN_SELCHANGE: UpdateActiveDisplay(NULL); break; default: return FALSE; } break; case IDC_DISPLAYPROPERTIES: switch (GET_WM_COMMAND_CMD(wParam, lParam)) { DoDeviceSettings: case BN_CLICKED: if (IsWindowEnabled(GetDlgItem(_hDlg, IDC_DISPLAYPROPERTIES))) _OnAdvancedClicked(); break; default: return FALSE; } break; case IDC_COLORBOX: switch(GET_WM_COMMAND_CMD(wParam, lParam)) { case CBN_SELCHANGE: { HWND hwndColorBox = GetDlgItem(_hDlg, IDC_COLORBOX); int iClr = ComboBox_GetCurSel(hwndColorBox); if ((iClr != CB_ERR) && _pCurDevice && _pCurDevice->pds && _pCurDevice->ColorList) { // Message box if something bad is going to happen ? // _VerifyPrimaryMode(TRUE); _pCurDevice->pds->SetCurColor((int) *(_pCurDevice->ColorList + iClr)); // Repaint the control in case they changed _UpdateUI(TRUE /*fAutoSetColorDepth*/, IDC_COLORBOX); } break; } default: break; } break; case IDC_TROUBLESHOOT: // Invokes the trouble shooter for the Settings tab. { TCHAR szCommand[MAX_PATH]; LoadString(HINST_THISDLL,IDS_TROUBLESHOOT_EXEC, szCommand, ARRAYSIZE(szCommand)); // Get location of HelpCtr.exe, which is the value in the // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\HELPCTR.EXE key TCHAR szHelpCtr[MAX_PATH]; HKEY hKey = NULL; DWORD cbHelpCtr = sizeof(szHelpCtr); if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Microsoft\\Windows\\CurrentVersion\\App Paths\\HELPCTR.EXE"), 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS || RegQueryValueEx(hKey, TEXT(""), NULL, NULL, (LPBYTE)szHelpCtr, &cbHelpCtr) != ERROR_SUCCESS) { StringCchCopy(szHelpCtr, ARRAYSIZE(szHelpCtr), TEXT("HelpCtr.exe")); } if (hKey) { RegCloseKey(hKey); } HrShellExecute(_hwndDesk, NULL, szHelpCtr, szCommand, NULL, SW_NORMAL); } break; case IDC_IDENTIFY: // Flashes the Text on all the monitors simultaneously { HWND hwndC; //Enumerate all the monitors and flash this for each! hwndC = GetWindow(_hwndDesk, GW_CHILD); while (hwndC) { PostMessage(hwndC, WM_COMMAND, MAKEWPARAM(IDC_FLASH, 0), MAKELPARAM(0, 0)); hwndC = GetWindow(hwndC, GW_HWNDNEXT); } } break; default: return FALSE; } // Enable the apply button only if we are not in setup. DWORD dwExecMode; if (_pThemeUI && (SUCCEEDED(_pThemeUI->GetExecMode(&dwExecMode)))) { if ( (dwExecMode == EM_NORMAL) || (dwExecMode == EM_INVALID_MODE) || (dwExecMode == EM_DETECT) ) { // Set the apply button if something changed if (_pCurDevice && _pCurDevice->pds && _pCurDevice->pds->bIsModeChanged()) { SetDirty(); } } } break; case WM_HSCROLL: _HandleHScroll((HWND)lParam, (int) LOWORD(wParam), (int) HIWORD(wParam)); break; case WM_HELP: WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, TEXT("display.hlp"), HELP_WM_HELP, (DWORD_PTR)(LPTSTR)sc_MultiMonitorHelpIds); break; case WM_CONTEXTMENU: WinHelp((HWND)wParam, TEXT("display.hlp"), HELP_CONTEXTMENU, (DWORD_PTR)(LPTSTR)sc_MultiMonitorHelpIds); break; case WM_DISPLAYCHANGE: case WM_WININICHANGE: _ForwardToChildren(message, wParam, lParam); break; case WM_SYSCOLORCHANGE: if (_himl) ImageList_SetBkColor(_himl, GetSysColor(COLOR_APPWORKSPACE)); // // Needs to be passed to all the new common controls so they repaint // correctly using the new system colors // _ForwardToChildren(message, wParam, lParam); // // Rerender the monitor(s) bitmap(s) to reflect the new colors // if (_NumDevices == 1) { // set up bitmaps for sample screen if (_hbmScrSample && (GetObjectType(_hbmScrSample) != 0)) { DeleteObject(_hbmScrSample); _hbmScrSample = 0; } _hbmScrSample = LoadMonitorBitmap( TRUE ); // let them do the desktop SendDlgItemMessage(_hDlg, IDC_SCREENSAMPLE, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)_hbmScrSample); // get a base copy of the bitmap for when the "internals" change if (_hbmMonitor && (GetObjectType(_hbmMonitor) != 0)) { DeleteObject(_hbmMonitor); _hbmMonitor = 0; } _hbmMonitor = LoadMonitorBitmap( FALSE ); // we'll do the desktop } else if (_NumDevices > 0) { HBITMAP hbm, hbmMask; int cx, cy; UINT iDevice; PMULTIMON_DEVICE pDevice; TCHAR ach[12]; // replace each monitor bitmap with one with correct colors for (iDevice = 0; (iDevice < _NumDevices); iDevice++) { pDevice = &_Devices[iDevice]; if (pDevice) { _itot(iDevice+1,ach,10); ImageList_GetIconSize(pDevice->himl, &cx, &cy); MakeMonitorBitmap(pDevice->w,pDevice->h,ach,&hbm,&hbmMask,cx,cy, (pDevice == _pCurDevice)); ImageList_Replace(pDevice->himl,pDevice->iImage,hbm,hbmMask); DeleteObject(hbm); DeleteObject(hbmMask); } } } break; #if 0 // // NOTE: until video supports device interfaces, we cannot use // WM_DEVICECHANGE to detect video changes. The default WM_DEVCHANGE // only reports about legacy devices // case WM_DEVICECHANGE: // // Rebuild the device list if we are not currently enumerating, // because enumerating may cause another device to come on-line // // We only reenumerate if a new *video* device arrives // if (!_InSetInfo && (wParam == DBT_DEVICEARRIVAL || wParam == DBT_DEVICEREMOVECOMPLETE)) { DEV_BROADCAST_HDR *bhdr = (DEV_BROADCAST_HDR *) lParam; // check for something else here, most likely the dev interface guid if (bhdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { _InitDisplaySettings(FALSE); } } break; #endif case WM_DESTROY: TraceMsg(TF_GENERAL, "WndProc:: WM_DESTROY"); hwndSample = GetDlgItem(_hDlg, IDC_COLORSAMPLE); hbm = (HBITMAP)SendMessage(hwndSample, STM_SETIMAGE, IMAGE_BITMAP, NULL); if (hbm) DeleteObject(hbm); if (_NumDevices == 1) { hwndSample = GetDlgItem(_hDlg, IDC_SCREENSAMPLE); hbm = (HBITMAP)SendMessage(hwndSample, STM_SETIMAGE, IMAGE_BITMAP, NULL); if (hbm) DeleteObject(hbm); if (_hbmScrSample && (GetObjectType(_hbmScrSample) != 0)) DeleteObject(_hbmScrSample); if (_hbmMonitor && (GetObjectType(_hbmMonitor) != 0)) DeleteObject(_hbmMonitor); } _DestroyDisplaySettings(); break; case MSG_DSP_SETUP_MESSAGE: return _InitMessage(); // MultiMonitor CPL specific messages case MM_REDRAWPREVIEW: _RedrawDeskPreviews(); break; case WM_LBUTTONDBLCLK: if (_NumDevices == 1) { HWND hwndSample = GetDlgItem(_hDlg, IDC_SCREENSAMPLE); if(NULL != hwndSample) { POINT pt; RECT rc; pt.x = GET_X_LPARAM(lParam); // horizontal position of cursor pt.y = GET_Y_LPARAM(lParam); // vertical position of cursor GetWindowRect(hwndSample, &rc); if(ClientToScreen(_hDlg, &pt) && PtInRect(&rc, pt)) PostMessage(_hDlg, WM_COMMAND, MAKEWPARAM(IDC_DISPLAYPROPERTIES, BN_CLICKED), (LPARAM)hwndSample); } break; } else return FALSE; default: return FALSE; } return TRUE; } // IUnknown methods HRESULT CSettingsPage::QueryInterface(REFIID riid, LPVOID * ppvObj) { static const QITAB qit[] = { QITABENT(CSettingsPage, IObjectWithSite), QITABENT(CSettingsPage, IPropertyBag), QITABENT(CSettingsPage, IBasePropPage), QITABENTMULTI(CSettingsPage, IShellPropSheetExt, IBasePropPage), { 0 }, }; return QISearch(this, qit, riid, ppvObj); } ULONG CSettingsPage::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CSettingsPage::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } // IMultiMonConfig methods HRESULT CSettingsPage::Initialize(HWND hwndHost, WNDPROC pfnWndProc, DWORD dwReserved) { HRESULT hr = E_FAIL; if (hwndHost && RegisterPreviewWindowClass(pfnWndProc)) { _hwndDesk = hwndHost; if (_InitDisplaySettings(TRUE)) hr = S_OK; } return hr; } HRESULT CSettingsPage::GetNumberOfMonitors(int * pCMon, DWORD dwReserved) { if (pCMon) { *pCMon = _NumDevices; return S_OK; } return E_FAIL; } HRESULT CSettingsPage::GetMonitorData(int iMonitor, MonitorData * pmd, DWORD dwReserved) { ASSERT(pmd); if ((pmd == NULL) || ((ULONG)iMonitor >= _NumDevices)) return ResultFromWin32(ERROR_INVALID_PARAMETER); PMULTIMON_DEVICE pDevice = &_Devices[iMonitor]; pmd->dwSize = sizeof(MonitorData); if ( pDevice->pds->IsPrimary() ) pmd->dwStatus |= MD_PRIMARY; if ( pDevice->pds->IsAttached() ) pmd->dwStatus |= MD_ATTACHED; pDevice->pds->GetCurPosition(&pmd->rcPos); return S_OK; } HRESULT CSettingsPage::Paint(int iMonitor, DWORD dwReserved) { _RedrawDeskPreviews(); return S_OK; } /*---------------------------------------------------------------------------- ----------------------------------------------------------------------------*/ HFONT GetFont(LPRECT prc) { LOGFONT lf; ZeroMemory(&lf, sizeof(lf)); lf.lfWeight = FW_EXTRABOLD; lf.lfHeight = prc->bottom - prc->top; lf.lfWidth = 0; lf.lfPitchAndFamily = FF_SWISS; lf.lfOutPrecision = OUT_TT_ONLY_PRECIS; return CreateFontIndirect(&lf); } /*---------------------------------------------------------------------------- ----------------------------------------------------------------------------*/ #define HANG_TIME 2500 LRESULT CALLBACK BigNumberWindowProc(HWND hwnd, UINT msg,WPARAM wParam,LPARAM lParam) { TCHAR ach[80]; HFONT hfont; RECT rc; HDC hdc; HRGN hrgnTxtA; PAINTSTRUCT ps; HGDIOBJ hOldPen; HGDIOBJ hNewPen; switch (msg) { case WM_CREATE: break; case WM_SIZE: GetWindowText(hwnd, ach, ARRAYSIZE(ach)); GetClientRect(hwnd, &rc); hfont = GetFont(&rc); hdc = GetDC(hwnd); SelectObject(hdc, hfont); BeginPath(hdc); SetBkMode(hdc, TRANSPARENT); TextOut(hdc,0,0,ach,lstrlen(ach)); EndPath(hdc); hrgnTxtA = PathToRegion(hdc); SetWindowRgn(hwnd,hrgnTxtA,TRUE); ReleaseDC(hwnd, hdc); DeleteObject(hfont); break; case WM_TIMER: DestroyWindow(hwnd); return 0; case WM_PAINT: GetWindowText(hwnd, ach, ARRAYSIZE(ach)); GetClientRect(hwnd, &rc); hfont = GetFont(&rc); if (hfont) { hdc = BeginPaint(hwnd, &ps); //The following paints the whole region (which is in the shape of the number) black! PatBlt(hdc, 0, 0, rc.right, rc.bottom, BLACKNESS | NOMIRRORBITMAP); SelectObject(hdc, hfont); SetTextColor(hdc, 0xFFFFFF); //Let's create a path that is the shape of the region by drawing that number. BeginPath(hdc); SetBkMode(hdc, TRANSPARENT); TextOut(hdc,0,0,ach,lstrlen(ach)); EndPath(hdc); // The above TextOut calljust created the path. Let's now actually draw the number! // Note: We are drawing the number in white there by changing whatever painted as black // a few moments ago! TextOut(hdc,0,0,ach,lstrlen(ach)); //Let's create a thick black brush to paint the borders of the number we just drew! hNewPen = CreatePen(PS_INSIDEFRAME, 4, 0x0); //Black Color if (hNewPen) { hOldPen = SelectObject(hdc, hNewPen); //Draw the border of the white number with the thick black brush! StrokePath(hdc); SelectObject(hdc, hOldPen); DeleteObject(hNewPen); } EndPaint(hwnd, &ps); DeleteObject(hfont); } break; } return DefWindowProc(hwnd,msg,wParam,lParam); } int Bail() { POINT pt; POINT pt0; DWORD time0; DWORD d; d = GetDoubleClickTime(); time0 = GetMessageTime(); pt0.x = (int)(short)LOWORD(GetMessagePos()); pt0.y = (int)(short)HIWORD(GetMessagePos()); if (GetTickCount()-time0 > d) return 2; if (!((GetAsyncKeyState(VK_LBUTTON) | GetAsyncKeyState(VK_RBUTTON)) & 0x8000)) return 1; GetCursorPos(&pt); if ((pt.y - pt0.y) > 2 || (pt.y - pt0.y) < -2) return 1; if ((pt.x - pt0.x) > 2 || (pt.x - pt0.x) < -2) return 1; return 0; } void FlashText(HWND hDlg, PMULTIMON_DEVICE pDevice, LPCTSTR sz, LPRECT prc, BOOL fWait) { HFONT hfont; SIZE size; HDC hdc; int i; if (!pDevice->pds->IsOrgAttached()) return; if (pDevice->hwndFlash && IsWindow(pDevice->hwndFlash)) { DestroyWindow(pDevice->hwndFlash); pDevice->hwndFlash = NULL; } if (sz == NULL) return; if (fWait) { while ((i=Bail()) == 0) ; if (i == 1) return; } hdc = GetDC(NULL); hfont = GetFont(prc); SelectObject(hdc, hfont); if (!GetTextExtentPoint(hdc, sz, lstrlen(sz), &size)) { size.cx = 0; size.cy = 0; } ReleaseDC(NULL, hdc); DeleteObject(hfont); if (!gfFlashWindowRegistered) { WNDCLASS cls; cls.hCursor = LoadCursor(NULL,IDC_ARROW); cls.hIcon = NULL; cls.lpszMenuName = NULL; cls.lpszClassName = TEXT("MonitorNumber32"); cls.hbrBackground = (HBRUSH)(COLOR_DESKTOP + 1); cls.hInstance = HINST_THISDLL; cls.style = CS_VREDRAW | CS_HREDRAW; cls.lpfnWndProc = BigNumberWindowProc; cls.cbWndExtra = 0; cls.cbClsExtra = 0; RegisterClass(&cls); gfFlashWindowRegistered = TRUE; } pDevice->hwndFlash = CreateWindowEx( WS_EX_TOPMOST | WS_EX_TOOLWINDOW, //WS_BORDER, TEXT("MonitorNumber32"), sz, WS_POPUP, (prc->right + prc->left - size.cx)/2, (prc->bottom + prc->top - size.cy)/2, size.cx, size.cy, hDlg, //Set the dialog as the parent sothat we get back the activation after flash window goes away! NULL, HINST_THISDLL, NULL); if (pDevice->hwndFlash) { ShowWindow(pDevice->hwndFlash, SW_SHOW); UpdateWindow(pDevice->hwndFlash); SetTimer(pDevice->hwndFlash, 1, HANG_TIME, NULL); } } void DrawMonitorNum(HDC hdc, int w, int h, LPCTSTR sz, BOOL fDrawBackground=TRUE) { HFONT hfont; HFONT hfontT; RECT rc; COLORREF rgb; COLORREF rgbDesk; SetRect(&rc, 0, 0, w, h); rgb = GetSysColor(COLOR_CAPTIONTEXT); rgbDesk = GetSysColor(COLOR_DESKTOP); if (fDrawBackground) FillRect(hdc, &rc, GetSysColorBrush (COLOR_DESKTOP)); InflateRect(&rc, -(MON_X*w / MON_W)>> 1, -(MON_Y*h/ MON_H)); if (rgbDesk == rgb) rgb = GetSysColor(COLOR_WINDOWTEXT); if (rgbDesk == rgb) rgb = rgbDesk ^ 0x00FFFFFF; SetTextColor(hdc, rgb); hfont = GetFont(&rc); if (hfont) { hfontT = (HFONT)SelectObject(hdc, hfont); SetTextAlign(hdc, TA_CENTER | TA_TOP); SetBkMode(hdc, TRANSPARENT); ExtTextOut(hdc, (rc.left+rc.right)/2, rc.top, 0, NULL, sz, lstrlen(sz), NULL); SelectObject(hdc, hfontT); DeleteObject(hfont); } } void AddTrackingToolTip(PMULTIMON_DEVICE pDevice, HWND hwnd) { TOOLINFO ti; TCHAR location[16]; RECT rcPos; // // New tool Tip // pDevice->pds->GetCurPosition(&rcPos); StringCchPrintf(location, ARRAYSIZE(location), TEXT("%d, %d"), rcPos.left, rcPos.top); GetWindowRect(hwnd, &rcPos); ti.cbSize = sizeof(TOOLINFO); ti.uFlags = TTF_TRACK; ti.hwnd = hwnd; ti.uId = (UINT_PTR) pDevice; ti.hinst = HINST_THISDLL; ti.lpszText = location; ti.rect.left = rcPos.left + 2; ti.rect.top = rcPos.top + 2; ti.rect.right = rcPos.right - 2;// ti.rect.left + 10; ti.rect.bottom = rcPos.bottom - 2; // ti.rect.top + 10; ToolTip_AddTool(ghwndToolTipTracking, &ti); pDevice->bTracking = FALSE; TraceMsg(TF_GENERAL, "Added TOOLTIP hwnd %08lx, uId %08lx\n", ti.hwnd, ti.uId); return; } void RemoveTrackingToolTip(HWND hwnd) { TOOLINFO ti; ZeroMemory(&ti, sizeof(ti)); ti.cbSize = sizeof(ti); ti.hwnd = hwnd; ti.uId = (UINT_PTR) GetDlgCtrlDevice(hwnd); ToolTip_DelTool(ghwndToolTipTracking, &ti); } BOOLEAN TrackToolTip(PMULTIMON_DEVICE pDevice, HWND hwnd, BOOL bTrack) { TOOLINFO ti; BOOLEAN oldTracking; ZeroMemory(&ti, sizeof(ti)); ti.cbSize = sizeof(ti); ti.hwnd = hwnd; ti.uId = (UINT_PTR) pDevice; oldTracking = pDevice->bTracking; pDevice->bTracking = (BOOLEAN)bTrack; ToolTip_TrackActivate(ghwndToolTipTracking, bTrack, &ti); TraceMsg(TF_GENERAL, "Track TOOLTIP hwnd %08lx, uId %08lx\n", ti.hwnd, ti.uId); return oldTracking; } void AddPopupToolTip(HWND hwndC) { TOOLINFO ti; // // New tool Tip // ti.cbSize = sizeof(TOOLINFO); ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS | TTF_CENTERTIP; ti.hwnd = hwndC; ti.uId = (UINT_PTR) hwndC; ti.hinst = HINST_THISDLL; GetWindowRect(hwndC, &ti.rect); ti.lpszText = LPSTR_TEXTCALLBACK; ToolTip_AddTool(ghwndToolTipPopup, &ti); } void RemovePopupToolTip(HWND hwndC) { TOOLINFO ti; ZeroMemory(&ti, sizeof(ti)); ti.cbSize = sizeof(ti); ti.hwnd = hwndC; ti.uId = (UINT_PTR) hwndC; ToolTip_DelTool(ghwndToolTipPopup, &ti); } BOOL MakeMonitorBitmap(int w, int h, LPCTSTR sz, HBITMAP *pBitmap, HBITMAP *pMaskBitmap, int cx, int cy, BOOL fSelected) { HDC hdc; // work dc HDC hdcS; // screen dc ASSERT(w <= cx); ASSERT(h <= cy); *pBitmap = NULL; hdcS = GetDC(NULL); hdc = CreateCompatibleDC(hdcS); if (hdc) { HDC hdcT; // another work dc hdcT = CreateCompatibleDC(hdcS); if (hdcT) { HBITMAP hbm; // 128x128 bitmap we will return HBITMAP hbmT = NULL;// bitmap loaded from resource HBITMAP hbmM = NULL;// mask bitmap HDC hdcM = NULL;// another work dc RECT rc; if (pMaskBitmap) hdcM = CreateCompatibleDC(hdcS); hbm = CreateCompatibleBitmap(hdcS, cx, cy); if (hbm) { hbmT = CreateCompatibleBitmap(hdcS, w, h); if(pMaskBitmap) hbmM = CreateBitmap(cx,cy,1,1,NULL); ReleaseDC(NULL,hdcS); if (hbmT) { SelectObject(hdc, hbm); SelectObject(hdcT,hbmT); if (pMaskBitmap && hdcM) SelectObject(hdcM, hbmM); } *pBitmap = hbm; } // Make sure the color of the borders (selection & normal) is different than the background color. HBRUSH hbrDiff = NULL; BOOL bNeedDiff = ((fSelected && (GetSysColor(COLOR_APPWORKSPACE) == GetSysColor(COLOR_HIGHLIGHT))) || (GetSysColor(COLOR_APPWORKSPACE) == GetSysColor(COLOR_BTNHIGHLIGHT))); if(bNeedDiff) { DWORD rgbDiff = ((GetSysColor(COLOR_ACTIVEBORDER) != GetSysColor(COLOR_APPWORKSPACE)) ? GetSysColor(COLOR_ACTIVEBORDER) : GetSysColor(COLOR_APPWORKSPACE) ^ 0x00FFFFFF); hbrDiff = CreateSolidBrush(rgbDiff); } // Fill it with the selection color or the background color. SetRect(&rc, 0, 0, w, h); FillRect(hdcT, &rc, (fSelected ? ((GetSysColor(COLOR_APPWORKSPACE) != GetSysColor(COLOR_HIGHLIGHT)) ? GetSysColorBrush(COLOR_HIGHLIGHT) : hbrDiff) : GetSysColorBrush(COLOR_APPWORKSPACE))); InflateRect(&rc, -SELECTION_THICKNESS, -SELECTION_THICKNESS); FillRect(hdcT, &rc, ((GetSysColor(COLOR_APPWORKSPACE) != GetSysColor(COLOR_BTNHIGHLIGHT)) ? GetSysColorBrush(COLOR_BTNHIGHLIGHT) : hbrDiff)); if (hbrDiff) { DeleteObject(hbrDiff); hbrDiff = NULL; } InflateRect(&rc, -MONITOR_BORDER, -MONITOR_BORDER); FillRect(hdcT, &rc, GetSysColorBrush(COLOR_DESKTOP)); // fill bitmap with transparent color SetBkColor(hdc,GetSysColor(COLOR_APPWORKSPACE)); SetRect(&rc, 0, 0, cx, cy); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL); // copy bitmap to upper-left of bitmap BitBlt(hdc,0,0,w,h,hdcT,0,0,SRCCOPY); // draw the monitor number, if provided, in the bitmap (in the right place) if (sz) DrawMonitorNum(hdc, w, h, sz, FALSE); // make mask, if needed. if (pMaskBitmap && hdcM) { BitBlt(hdcM,0,0,cx,cy,hdc,0,0,SRCCOPY); *pMaskBitmap = hbmM; } if (hbmT) DeleteObject(hbmT); if (pMaskBitmap && hdcM) DeleteDC(hdcM); DeleteDC(hdcT); } DeleteDC(hdc); } return TRUE; } // // SnapMonitorRect // // called while the user is moving a monitor window (WM_MOVING) // if the CTRL key is not down we will snap the window rect // to the edge of one of the other monitors. // // this is done so the user can easily align monitors // // NOTE pDevice->Snap must be initialized to 0,0 in WM_ENTERSIZEMOVE // void SnapMonitorRect(PMULTIMON_DEVICE pDevice, HWND hwnd, RECT *prc) { HWND hwndT; int d; RECT rcT; RECT rc; // // allow the user to move the window anywhere when the CTRL key is down // if (GetKeyState(VK_CONTROL) & 0x8000) return; // // macros to help in alignment // #define SNAP_DX 6 #define SNAP_DY 6 #define SNAPX(f,x) \ d = rcT.x - rc.f; if (abs(d) <= SNAP_DX) rc.left+=d, rc.right+=d; #define SNAPY(f,y) \ d = rcT.y - rc.f; if (abs(d) <= SNAP_DY) rc.top+=d, rc.bottom+=d; // // get current rect and offset it by the amount we have corrected // it so far (this alignes the rect with the position of the mouse) // rc = *prc; OffsetRect(&rc, pDevice->Snap.x, pDevice->Snap.y); // // walk all other windows and snap our window to them // for (hwndT = GetWindow(hwnd, GW_HWNDFIRST); hwndT; hwndT = GetWindow(hwndT, GW_HWNDNEXT)) { if (hwndT == hwnd) continue; GetWindowRect(hwndT, &rcT); InflateRect(&rcT,SNAP_DX,SNAP_DY); if (IntersectRect(&rcT, &rcT, &rc)) { GetWindowRect(hwndT, &rcT); SNAPX(right,left); SNAPY(bottom,top); SNAPX(right,right); SNAPY(bottom,bottom); SNAPX(left,left); SNAPY(top,top); SNAPX(left,right); SNAPY(top,bottom); } } // // adjust the amount we have snap'ed so far, and return the new rect // pDevice->Snap.x += prc->left - rc.left; pDevice->Snap.y += prc->top - rc.top; *prc = rc; } WPARAM GetKeyStates() { WPARAM wParam = 0x0; if (GetKeyState(VK_CONTROL) & 0x8000) wParam |= MK_CONTROL; if (GetKeyState(VK_LBUTTON) & 0x8000) wParam |= MK_LBUTTON; if (GetKeyState(VK_MBUTTON) & 0x8000) wParam |= MK_MBUTTON; if (GetKeyState(VK_RBUTTON) & 0x8000) wParam |= MK_RBUTTON; if (GetKeyState(VK_SHIFT) & 0x8000) wParam |= MK_SHIFT; return wParam; } LRESULT CALLBACK MonitorWindowProc(HWND hwnd, UINT msg,WPARAM wParam,LPARAM lParam) { TOOLINFO ti; PAINTSTRUCT ps; HDC hdc; RECT rc; int w,h; TCHAR ach[80]; PMULTIMON_DEVICE pDevice; HWND hDlg = GetParent(GetParent(hwnd)); RECT rcPos; MSG mmsg; CSettingsPage * pcmm = (CSettingsPage *) GetWindowLongPtr(hwnd, 0); switch (msg) { case WM_CREATE: ASSERT(((LPCREATESTRUCT)lParam)->lpCreateParams); SetWindowLongPtr(hwnd, 0, (LONG_PTR)((LPCREATESTRUCT)lParam)->lpCreateParams); break; case WM_NCCREATE: // turn off RTL_MIRRORED_WINDOW in GWL_EXSTYLE SHSetWindowBits(hwnd, GWL_EXSTYLE, RTL_MIRRORED_WINDOW, 0); break; case WM_NCHITTEST: // // return HTCAPTION so that we can get the ENTERSIZEMOVE message. // pDevice = GetDlgCtrlDevice(hwnd); // Let disabled monitors move if (pDevice) // if (pDevice && pDevice->pds->IsAttached()) return HTCAPTION; break; case WM_NCLBUTTONDBLCLK: FlashText(hDlg, GetDlgCtrlDevice(hwnd), NULL,NULL,FALSE); PostMessage(hDlg, WM_COMMAND, MAKEWPARAM(IDC_DISPLAYPROPERTIES, BN_CLICKED), (LPARAM)hwnd ); break; case WM_CHILDACTIVATE: if (GetFocus() != GetParent(hwnd)) { SetFocus(GetParent(hwnd)); } break; case WM_HELP: WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, TEXT("display.hlp"), HELP_WM_HELP, (DWORD_PTR)(LPTSTR)sc_MultiMonitorHelpIds); break; case WM_CONTEXTMENU: WinHelp((HWND)wParam, TEXT("display.hlp"), HELP_CONTEXTMENU, (DWORD_PTR)(LPTSTR)sc_MultiMonitorHelpIds); break; case WM_COMMAND: switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_DISPLAYPRIME: case IDC_DISPLAYUSEME: case IDC_DISPLAYPROPERTIES: PostMessage(hDlg, WM_COMMAND, wParam, lParam); break; case IDC_FLASH: pDevice = GetDlgCtrlDevice(hwnd); pDevice->pds->GetOrgPosition(&rcPos); if (!IsRectEmpty(&rcPos)) { GetWindowText(hwnd, ach, ARRAYSIZE(ach)); FlashText(hDlg, pDevice, ach, &rcPos, FALSE); } break; } break; case WM_INITMENUPOPUP: pDevice = GetDlgCtrlDevice(hwnd); CheckMenuItem((HMENU)wParam, IDC_DISPLAYUSEME, pDevice->pds->IsAttached() ? MF_CHECKED : MF_UNCHECKED); CheckMenuItem((HMENU)wParam, IDC_DISPLAYPRIME, pDevice->pds->IsPrimary() ? MF_CHECKED : MF_UNCHECKED); // until I figure out how to render on a non attached monitor, just // disable the menu item EnableMenuItem((HMENU)wParam, IDC_FLASH, pDevice->pds->IsAttached() ? MF_ENABLED : MF_GRAYED); EnableMenuItem((HMENU)wParam, IDC_DISPLAYPROPERTIES, IsWindowEnabled(GetDlgItem(GetParent(GetParent(hwnd)), IDC_DISPLAYPROPERTIES)) ? MF_ENABLED : MF_GRAYED); EnableMenuItem((HMENU)wParam, IDC_DISPLAYUSEME, pDevice->pds->IsPrimary() ? MF_GRAYED : MF_ENABLED); EnableMenuItem((HMENU)wParam, IDC_DISPLAYPRIME, ((pDevice->pds->IsAttached() && !pDevice->pds->IsRemovable() && !pDevice->pds->IsPrimary()) ? MF_ENABLED : MF_GRAYED)); SetMenuDefaultItem((HMENU)wParam, IDC_DISPLAYPROPERTIES, MF_BYCOMMAND); break; case WM_NCMOUSEMOVE: ToolTip_RelayEvent(ghwndToolTipPopup, mmsg, NULL, WM_MOUSEMOVE, GetKeyStates(), lParam); break; case WM_NCMBUTTONDOWN: ToolTip_RelayEvent(ghwndToolTipPopup, mmsg, NULL, WM_MBUTTONDOWN, GetKeyStates(), lParam); break; case WM_NCMBUTTONUP: ToolTip_RelayEvent(ghwndToolTipPopup, mmsg, NULL, WM_MBUTTONUP, GetKeyStates(), lParam); break; case WM_NCRBUTTONDOWN: ToolTip_RelayEvent(ghwndToolTipPopup, mmsg, NULL, WM_RBUTTONDOWN, GetKeyStates(), lParam); pDevice = GetDlgCtrlDevice(hwnd); if (pDevice && pcmm) { HMENU hmenu; POINT pt; hmenu = LoadMenu(HINST_THISDLL, MAKEINTRESOURCE(MENU_MONITOR)); if (hmenu) { pcmm->UpdateActiveDisplay(pDevice); GetCursorPos(&pt); TrackPopupMenu(GetSubMenu(hmenu,0), TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, NULL); DestroyMenu(hmenu); } } break; case WM_NCRBUTTONUP: ToolTip_RelayEvent(ghwndToolTipPopup, mmsg, NULL, WM_RBUTTONUP, GetKeyStates(), lParam); break; case WM_NCLBUTTONDOWN: //TraceMsg(TF_FUNC, "WM_NCLBUTTONDOWN"); // don't relay the message here because we want to keep the tool tip // active until they start moving the monitor. This click might just // be for selection // ToolTip_RelayEvent(ghwndToolTipPopup, mmsg, hDlg, WM_LBUTTONDOWN, GetKeyStates(), lParam); BringWindowToTop(hwnd); pDevice = GetDlgCtrlDevice(hwnd); if (pcmm) pcmm->UpdateActiveDisplay(pDevice); pDevice->pds->GetOrgPosition(&rcPos); if (!IsRectEmpty(&rcPos)) { GetWindowText(hwnd, ach, ARRAYSIZE(ach)); FlashText(hDlg, pDevice, ach, &rcPos, TRUE); } break; case WM_NOTIFY: switch (((NMHDR FAR *)lParam)->code) { case TTN_NEEDTEXT: pDevice = GetDlgCtrlDevice(hwnd); if (pDevice->pds->IsPrimary()) { LoadString(HINST_THISDLL, IDS_PRIMARY, ((LPNMTTDISPINFO)lParam)->szText, ARRAYSIZE(((LPNMTTDISPINFO)lParam)->szText) ); } else if (!pDevice->pds->IsAttached()) { LoadString(HINST_THISDLL, IDS_NOTATTACHED, ((LPNMTTDISPINFO)lParam)->szText, ARRAYSIZE(((LPNMTTDISPINFO)lParam)->szText) ); } else { TCHAR szSecondary[32]; LoadString(HINST_THISDLL, IDS_SECONDARY, szSecondary, ARRAYSIZE(szSecondary)); pDevice->pds->GetCurPosition(&rcPos); StringCchPrintf(((LPNMTTDISPINFO)lParam)->szText, ARRAYSIZE(((LPNMTTDISPINFO)lParam)->szText), TEXT("%s (%d, %d)"), szSecondary, rcPos.left, rcPos.top); } break; default: break; } break; case WM_ENTERSIZEMOVE: //TraceMsg(TF_FUNC, "WM_ENTERSIZEMOVE"); // relay a mouse up to clean the information tooltip ToolTip_RelayEvent(ghwndToolTipPopup, mmsg, NULL, WM_LBUTTONDOWN, GetKeyStates(), lParam); pDevice = GetDlgCtrlDevice(hwnd); pDevice->Snap.x = 0; pDevice->Snap.y = 0; FlashText(hDlg, pDevice, NULL,NULL,FALSE); break; case WM_MOVING: //TraceMsg(TF_FUNC, "WM_MOVING"); pDevice = GetDlgCtrlDevice(hwnd); SnapMonitorRect(pDevice, hwnd, (RECT*)lParam); ZeroMemory(&ti, sizeof(ti)); ti.cbSize = sizeof(ti); if (!pDevice->bTracking) { ToolTip_TrackPosition(ghwndToolTipTracking, ((LPRECT)lParam)->left+2, ((LPRECT)lParam)->top+2); TrackToolTip(pDevice, hwnd, TRUE); } if (ToolTip_GetCurrentTool(ghwndToolTipTracking, &ti) && pcmm) { TCHAR location[16]; POINT pt; pcmm->GetMonitorPosition(pDevice, GetParent(hwnd), &pt); StringCchPrintf(location, ARRAYSIZE(location), TEXT("%d, %d"), pt.x, pt.y); ti.lpszText = location; ti.rect.left = ((RECT*)lParam)->left + 2; ti.rect.top = ((RECT*)lParam)->top + 2; ti.rect.right = ti.rect.left + 10; ti.rect.bottom = ti.rect.top + 10; ToolTip_SetToolInfo(ghwndToolTipTracking, &ti); ToolTip_TrackPosition(ghwndToolTipTracking, ti.rect.left, ti.rect.top); // SendMessage(ghwndToolTip, TTM_UPDATE, 0, 0); } break; case WM_EXITSIZEMOVE: //TraceMsg(TF_FUNC, "WM_EXITSIZEMOVE"); pDevice = GetDlgCtrlDevice(hwnd); TrackToolTip(pDevice, hwnd, FALSE); // // We don't want to pop up any dialogs here because the modal size // loop is still active (it eats any mouse movements and the dialogs // can't be moved by the user). // PostMessage(hwnd, MM_MONITORMOVED, 0, 0); break; case MM_MONITORMOVED: pDevice = GetDlgCtrlDevice(hwnd); if (pcmm) { // // If the user moved the monitor, see if they want to attach it // if (!pcmm->QueryNoAttach() && pDevice && !pDevice->pds->IsAttached()) { if (pcmm->SetMonAttached(pDevice, TRUE, FALSE, hwnd)) { pcmm->UpdateActiveDisplay(pDevice); } } pcmm->HandleMonitorChange(hwnd, FALSE); } RedrawWindow(GetParent(hwnd), NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN); return TRUE; case WM_DESTROY: FlashText(hDlg, GetDlgCtrlDevice(hwnd), NULL,NULL,FALSE); SetWindowLong(hwnd, 0, NULL); break; case WM_ERASEBKGND: //GetClientRect(hwnd, &rc); //FillRect((HDC)wParam, &rc, GetSysColorBrush(COLOR_APPWORKSPACE)); return 0L; case WM_PAINT: hdc = BeginPaint(hwnd,&ps); GetWindowText(hwnd, ach, ARRAYSIZE(ach)); GetClientRect(hwnd, &rc); w = rc.right; h = rc.bottom; pDevice = GetDlgCtrlDevice(hwnd); BOOL fSelected = (pcmm ? (BOOL)(pDevice == pcmm->GetCurDevice()) : FALSE); if (pDevice->w != w || pDevice->h != h) { HBITMAP hbm, hbmMask; int cx,cy; pDevice->w = w; pDevice->h = h; ImageList_GetIconSize(pDevice->himl, &cx, &cy); MakeMonitorBitmap(w,h,ach,&hbm,&hbmMask,cx,cy, fSelected); ImageList_Replace(pDevice->himl,pDevice->iImage,hbm,hbmMask); DeleteObject(hbm); DeleteObject(hbmMask); } if (!pDevice->pds->IsAttached()) { FillRect(hdc, &rc, GetSysColorBrush(COLOR_APPWORKSPACE)); if (pcmm && fSelected) { ImageList_DrawEx(pDevice->himl,pDevice->iImage,hdc,0,0,w,h, CLR_DEFAULT,CLR_DEFAULT,ILD_BLEND25); } else { ImageList_DrawEx(pDevice->himl,pDevice->iImage,hdc,0,0,w,h, CLR_DEFAULT,CLR_NONE,ILD_BLEND50); } } else { ImageList_DrawEx(pDevice->himl,pDevice->iImage,hdc,0,0,w,h, CLR_DEFAULT,CLR_DEFAULT,ILD_IMAGE); } EndPaint(hwnd,&ps); return 0L; } return DefWindowProc(hwnd,msg,wParam,lParam); } LRESULT CALLBACK SliderSubWndProc (HWND hwndSlider, UINT uMsg, WPARAM wParam, LPARAM lParam, WPARAM uID, ULONG_PTR dwRefData) { ASSERT(uID == 0); ASSERT(dwRefData == 0); switch (uMsg) { case WM_GETOBJECT: if ( lParam == OBJID_CLIENT ) { // At this point we will try to load oleacc and get the functions // we need. if (!g_fAttemptedOleAccLoad) { g_fAttemptedOleAccLoad = TRUE; ASSERT(s_pfnCreateStdAccessibleProxy == NULL); ASSERT(s_pfnLresultFromObject == NULL); g_hOleAcc = LoadLibrary(TEXT("OLEACC")); if (g_hOleAcc != NULL) { s_pfnCreateStdAccessibleProxy = (PFNCREATESTDACCESSIBLEPROXY) GetProcAddress(g_hOleAcc, "CreateStdAccessibleProxyW"); s_pfnLresultFromObject = (PFNLRESULTFROMOBJECT) GetProcAddress(g_hOleAcc, "LresultFromObject"); } if (s_pfnLresultFromObject == NULL || s_pfnCreateStdAccessibleProxy == NULL) { if (g_hOleAcc) { // No point holding on to Oleacc since we can't use it. FreeLibrary(g_hOleAcc); g_hOleAcc = NULL; } s_pfnLresultFromObject = NULL; s_pfnCreateStdAccessibleProxy = NULL; } } if (g_hOleAcc && s_pfnCreateStdAccessibleProxy && s_pfnLresultFromObject) { IAccessible *pAcc = NULL; HRESULT hr; // Create default slider proxy. hr = s_pfnCreateStdAccessibleProxy( hwndSlider, TEXT("msctls_trackbar32"), OBJID_CLIENT, IID_IAccessible, (void **)&pAcc ); if (SUCCEEDED(hr) && pAcc) { // now wrap it up in our customized wrapper... IAccessible * pWrapAcc = new CAccessibleWrapper( hwndSlider, pAcc ); // Release our ref to proxy (wrapper has its own addref'd ptr)... pAcc->Release(); if (pWrapAcc != NULL) { // ...and return the wrapper via LresultFromObject... LRESULT lr = s_pfnLresultFromObject( IID_IAccessible, wParam, pWrapAcc ); // Release our interface pointer - OLEACC has its own addref to the object pWrapAcc->Release(); // Return the lresult, which 'contains' a reference to our wrapper object. return lr; // All done! } // If it didn't work, fall through to default behavior instead. } } } break; case WM_DESTROY: RemoveWindowSubclass(hwndSlider, SliderSubWndProc, uID); break; } /* end switch */ return DefSubclassProc(hwndSlider, uMsg, wParam, lParam); } BOOL CSettingsPage::_AreExtraMonitorsDisabledOnPersonal(void) { BOOL fIsDisabled = IsOS(OS_PERSONAL); if (fIsDisabled) { // POSSIBLE FUTURE REFINEMENT: Insert call to ClassicSystemParametersInfo() to see if there are video cards that we had to disable. fIsDisabled = FALSE; } return fIsDisabled; } // *** IShellPropSheetExt *** HRESULT CSettingsPage::AddPages(IN LPFNSVADDPROPSHEETPAGE pfnAddPage, IN LPARAM lParam) { HRESULT hr = S_OK; PROPSHEETPAGE psp = {0}; psp.dwSize = sizeof(psp); psp.hInstance = HINST_THISDLL; psp.dwFlags = PSP_DEFAULT; psp.lParam = (LPARAM) this; // GetSystemMetics(SM_CMONITORS) returns only the enabled monitors. So, we need to // enumerate ourselves to determine if this is a multimonitor scenario. We have our own // function to do this. // Use the appropriate dlg template for multimonitor and single monitor configs. // if(ClassicGetSystemMetrics(SM_CMONITORS) > 1) // // PERF-WARNING: calling EnumDisplaySettingsEx() is a huge perf hit, so see if we can // findout if there is only one adapter with a cheaper call. DEBUG_CODE(DebugStartWatch()); if (!_AreExtraMonitorsDisabledOnPersonal() && (ComputeNumberOfMonitorsFast(TRUE) > 1)) { psp.pszTemplate = MAKEINTRESOURCE(DLG_MULTIMONITOR); } else { psp.pszTemplate = MAKEINTRESOURCE(DLG_SINGLEMONITOR); } DEBUG_CODE(TraceMsg(TF_THEMEUI_PERF, "CSettingsPage::AddPages() took Time=%lums", DebugStopWatch())); psp.pfnDlgProc = CSettingsPage::SettingsDlgProc; HPROPSHEETPAGE hpsp = CreatePropertySheetPage(&psp); if (hpsp) { if (pfnAddPage(hpsp, lParam)) { hr = S_OK; } else { DestroyPropertySheetPage(hpsp); hr = E_FAIL; } } else { hr = E_OUTOFMEMORY; } return hr; } HRESULT CSettingsPage::ReplacePage(IN EXPPS uPageID, IN LPFNSVADDPROPSHEETPAGE pfnReplaceWith, IN LPARAM lParam) { return E_NOTIMPL; } // *** IObjectWithSite *** HRESULT CSettingsPage::SetSite(IN IUnknown * punkSite) { if (_pThemeUI) { _pThemeUI->Release(); _pThemeUI = NULL; } if (punkSite) { punkSite->QueryInterface(IID_PPV_ARG(IThemeUIPages, &_pThemeUI)); } return CObjectWithSite::SetSite(punkSite); } // *** IPropertyBag *** HRESULT CSettingsPage::Read(IN LPCOLESTR pszPropName, IN VARIANT * pVar, IN IErrorLog *pErrorLog) { HRESULT hr = E_INVALIDARG; return hr; } HRESULT CSettingsPage::Write(IN LPCOLESTR pszPropName, IN VARIANT *pVar) { HRESULT hr = E_INVALIDARG; return hr; } // *** IBasePropPage *** HRESULT CSettingsPage::GetAdvancedDialog(OUT IAdvancedDialog ** ppAdvDialog) { if (ppAdvDialog) { *ppAdvDialog = NULL; } return E_NOTIMPL; } HRESULT CSettingsPage::OnApply(IN PROPPAGEONAPPLY oaAction) { if (IsDirty() && !_nInApply) { int status; _nInApply++; // Apply the settings, and enable\disable the Apply button // appropriatly. CDisplaySettings *rgpds[MONITORS_MAX]; ULONG iDevice; for (iDevice = 0; iDevice < _NumDevices; iDevice++) { rgpds[iDevice] = _Devices[iDevice].pds; } status = _DisplaySaveSettings(rgpds, _NumDevices, _hDlg); SetDirty(status < 0); if (status == DISP_CHANGE_RESTART) { PropSheet_RestartWindows(ghwndPropSheet); } else if (_pCurDevice && (status == DISP_CHANGE_SUCCESSFUL)) { UINT iDevice; TCHAR szDeviceName[32]; ASSERT(sizeof(szDeviceName) >= sizeof(_pCurDevice->DisplayDevice.DeviceName)); StringCchCopy(szDeviceName, ARRAYSIZE(szDeviceName), _pCurDevice->DisplayDevice.DeviceName); _InitDisplaySettings(FALSE); for (iDevice = 0; iDevice < _NumDevices; iDevice++) { if (lstrcmp(_Devices[iDevice].DisplayDevice.DeviceName, szDeviceName) == 0) { UpdateActiveDisplay(_Devices + iDevice); break; } } } else { // Make sure the dialog stays and redraw _InitDisplaySettings(FALSE); UpdateActiveDisplay(NULL); SetWindowLongPtr(_hDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE); } _nInApply--; } return S_OK; } HRESULT CSettingsPage_CreateInstance(IN IUnknown * punkOuter, IN REFIID riid, OUT LPVOID * ppvObj) { HRESULT hr = E_INVALIDARG; if (!punkOuter && ppvObj) { CSettingsPage * pThis = new CSettingsPage(); *ppvObj = NULL; if (pThis) { hr = pThis->QueryInterface(riid, ppvObj); pThis->Release(); } else { hr = E_OUTOFMEMORY; } } return hr; } VOID CheckForDuplicateAppletExtensions(HKEY hkDriver) { DWORD dwCheckForDuplicates = 0, cb = sizeof(DWORD), Len = 0; HKEY hkExtensions = (HKEY)INVALID_HANDLE_VALUE; PAPPEXT pAppExtTemp = NULL, pAppExt = NULL; PTCHAR pmszAppExt = NULL; if (RegQueryValueEx(hkDriver, TEXT("DeskCheckForDuplicates"), NULL, NULL, (LPBYTE)(&dwCheckForDuplicates), &cb) == ERROR_SUCCESS) { RegDeleteValue(hkDriver, TEXT("DeskCheckForDuplicates")); } if (dwCheckForDuplicates != 1) return; if (RegOpenKeyEx(hkDriver, TEXT("Display\\shellex\\PropertySheetHandlers"), 0, KEY_READ, &hkExtensions) != ERROR_SUCCESS) { hkExtensions = (HKEY)INVALID_HANDLE_VALUE; goto Fallout; } DeskAESnapshot(hkExtensions, &pAppExt); if (pAppExt != NULL) { pAppExtTemp = pAppExt; Len = 0; while (pAppExtTemp) { Len += lstrlen(pAppExtTemp->szDefaultValue) + 1; pAppExtTemp = pAppExtTemp->pNext; } DWORD cchAppExt = (Len + 1); pmszAppExt = (PTCHAR)LocalAlloc(LPTR, cchAppExt * sizeof(TCHAR)); if (pmszAppExt != NULL) { pAppExtTemp = pAppExt; Len = 0; while (pAppExtTemp) { StringCchCopy(pmszAppExt + Len, cchAppExt - Len, pAppExtTemp->szDefaultValue); Len += lstrlen(pAppExtTemp->szDefaultValue) + 1; pAppExtTemp = pAppExtTemp->pNext; } DeskAEDelete(REGSTR_PATH_CONTROLSFOLDER TEXT("\\Display\\shellex\\PropertySheetHandlers"), pmszAppExt); DeskAEDelete(REGSTR_PATH_CONTROLSFOLDER TEXT("\\Device\\shellex\\PropertySheetHandlers"), pmszAppExt); LocalFree(pmszAppExt); } DeskAECleanup(pAppExt); } Fallout: if (hkExtensions != INVALID_HANDLE_VALUE) { RegCloseKey(hkExtensions); } } VOID DeskAESnapshot( HKEY hkExtensions, PAPPEXT* ppAppExt ) { HKEY hkSubkey = 0; DWORD index = 0; DWORD ulSize = MAX_PATH; APPEXT AppExtTemp; PAPPEXT pAppExtBefore = NULL; PAPPEXT pAppExtTemp = NULL; ulSize = sizeof(AppExtTemp.szKeyName) / sizeof(TCHAR); while (RegEnumKeyEx(hkExtensions, index, AppExtTemp.szKeyName, &ulSize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { if (RegOpenKeyEx(hkExtensions, AppExtTemp.szKeyName, 0, KEY_READ, &hkSubkey) == ERROR_SUCCESS) { ulSize = sizeof(AppExtTemp.szDefaultValue); if ((RegQueryValueEx(hkSubkey, NULL, 0, NULL, (PBYTE)AppExtTemp.szDefaultValue, &ulSize) == ERROR_SUCCESS) && (AppExtTemp.szDefaultValue[0] != TEXT('\0'))) { PAPPEXT pAppExt = (PAPPEXT)LocalAlloc(LPTR, sizeof(APPEXT)); if (pAppExt != NULL) { *pAppExt = AppExtTemp; pAppExtBefore = pAppExtTemp = *ppAppExt; while((pAppExtTemp != NULL) && (lstrcmpi(pAppExtTemp->szDefaultValue, pAppExt->szDefaultValue) < 0)) { pAppExtBefore = pAppExtTemp; pAppExtTemp = pAppExtTemp->pNext; } if (pAppExtBefore != pAppExtTemp) { pAppExt->pNext = pAppExtBefore->pNext; pAppExtBefore->pNext = pAppExt; } else { pAppExt->pNext = *ppAppExt; *ppAppExt = pAppExt; } } } RegCloseKey(hkSubkey); } ulSize = sizeof(AppExtTemp.szKeyName) / sizeof(TCHAR); index++; } } VOID DeskAEDelete( PTCHAR szDeleteFrom, PTCHAR mszExtensionsToRemove ) { TCHAR szKeyName[MAX_PATH]; HKEY hkDeleteFrom, hkExt; DWORD cSubKeys = 0, cbSize = 0; TCHAR szDefaultValue[MAX_PATH]; PTCHAR szValue; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szDeleteFrom, 0, KEY_READ, &hkDeleteFrom) == ERROR_SUCCESS) { if (RegQueryInfoKey(hkDeleteFrom, NULL, NULL, NULL, &cSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { while (cSubKeys--) { if (RegEnumKey(hkDeleteFrom, cSubKeys, szKeyName, ARRAYSIZE(szKeyName)) == ERROR_SUCCESS) { int iComp = -1; if (RegOpenKeyEx(hkDeleteFrom, szKeyName, 0, KEY_READ, &hkExt) == ERROR_SUCCESS) { cbSize = sizeof(szDefaultValue); if ((RegQueryValueEx(hkExt, NULL, 0, NULL, (PBYTE)szDefaultValue, &cbSize) == ERROR_SUCCESS) && (szDefaultValue[0] != TEXT('\0'))) { szValue = mszExtensionsToRemove; while (*szValue != TEXT('\0')) { iComp = lstrcmpi(szDefaultValue, szValue); if (iComp <= 0) { break; } while (*szValue != TEXT('\0')) szValue++; szValue++; } } RegCloseKey(hkExt); } if (iComp == 0) { SHDeleteKey(hkDeleteFrom, szKeyName); } } } } RegCloseKey(hkDeleteFrom); } } VOID DeskAECleanup( PAPPEXT pAppExt ) { PAPPEXT pAppExtTemp; while (pAppExt) { pAppExtTemp = pAppExt->pNext; LocalFree(pAppExt); pAppExt = pAppExtTemp; } }