multimon.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 152k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. /**************************************************************************
  2.  * Module Name: multimon.cpp
  3.  *
  4.  * Contains all the code to manage multiple devices
  5.  *
  6.  * Copyright (c) Microsoft Corp.  1995-1998 All Rights Reserved
  7.  *
  8.  * NOTES:
  9.  *
  10.  **************************************************************************/
  11. #include "precomp.h"
  12. #include "shlobjp.h"
  13. #include "shlwapi.h"
  14. #include "settings.hxx"
  15. #include "ntreg.hxx"
  16. #include <tchar.h>
  17. #include <dbt.h>
  18. #include <oleacc.h>
  19. #if DEBUG
  20. #define new DBG_NEW
  21. #endif
  22. extern "C" {
  23.     LRESULT CALLBACK MonitorWindowProc(HWND hwnd, UINT msg,WPARAM wParam,LPARAM lParam);
  24.     LRESULT CALLBACK SliderSubWndProc (HWND hwndSlider, UINT uMsg, WPARAM wParam, LPARAM lParam, WPARAM uID, ULONG_PTR dwRefData);
  25.     int ComputeNumberOfDisplayDevices();
  26.     //The following are defined in lookdlg.c
  27.     extern  int g_iNewDPI;
  28.     extern  int g_iAppliedNewDPI;
  29.     extern  HWND    g_hDlg;
  30.     extern  void    UpdateUIfontsDueToDPIchange(int iOldDPI, int iNewDPI);    
  31.     extern  void    Look_InitDPIglobals();
  32. }
  33. BOOL MakeMonitorBitmap(int w, int h, LPCTSTR sz, HBITMAP *pBitmap, HBITMAP *pMaskBitmap, int cx, int cy, BOOL fSelected);
  34. #define SELECTION_THICKNESS 4
  35. #define MONITOR_BORDER      1
  36. #define REGSTR_VAL_SAFEBOOT        TEXT("System\CurrentControlSet\Control\SafeBoot\Option")
  37. // Maximum number of monitors supported.
  38. #define MONITORS_MAX    10
  39. #define PREVIEWAREARATIO 2
  40. #define MM_REDRAWPREVIEW (WM_USER + 1)
  41. #define MM_MONITORMOVED  (WM_USER + 2)
  42. #define RECTWIDTH(rc)   ((rc).right-(rc).left)
  43. #define RECTHEIGHT(rc)  ((rc).bottom-(rc).top)
  44. #define ToolTip_Activate(hTT, activate) 
  45.     SendMessage(hTT, TTM_ACTIVATE, (WPARAM) activate, (LPARAM) 0)
  46. #define ToolTip_AddTool(hTT, lpti) 
  47.     SendMessage(hTT, TTM_ADDTOOL, (WPARAM) 0, (LPARAM) (lpti))
  48. #define ToolTip_DelTool(hTT, lpti) 
  49.     SendMessage(hTT, TTM_DELTOOL, (WPARAM) 0, (LPARAM) (lpti))
  50. #define ToolTip_GetCurrentTool(hTT, lpti) 
  51.     SendMessage(hTT, TTM_GETCURRENTTOOL, (WPARAM) 0, (LPARAM) (lpti))
  52. #define ToolTip_RelayEvent(hTT, _msg, h, m, wp, lp)                         
  53.     _msg.hwnd = h; _msg.message = m; _msg.wParam = wp; _msg.lParam = lp;
  54.     SendMessage(hTT, TTM_RELAYEVENT, (WPARAM) 0, (LPARAM) &_msg);       
  55. #define ToolTip_SetDelayTime(hTT, d, t) 
  56.     SendMessage(hTT, TTM_SETDELAYTIME, (WPARAM) d, (LPARAM)MAKELONG((t), 0))
  57. #define ToolTip_SetToolInfo(hTT, lpti) 
  58.     SendMessage(hTT, TTM_SETTOOLINFO, (WPARAM) 0, (LPARAM) (lpti))
  59. #define ToolTip_TrackActivate(hTT, bActivate, lpti) 
  60.     SendMessage(hTT, TTM_TRACKACTIVATE, (WPARAM) (bActivate), (LPARAM) (lpti))
  61. #define ToolTip_TrackPosition(hTT, x, y) 
  62.     SendMessage(hTT, TTM_TRACKPOSITION, (WPARAM) 0, (LPARAM) MAKELONG((x), (y)))
  63. #define ToolTip_Update(hTT) 
  64.     SendMessage(hTT, TTM_UPDATE, (WPARAM) 0, (LPARAM) 0)
  65. VOID
  66. CDECL
  67. TRACE(
  68.     PCTSTR pszMsg,
  69.     ...
  70.     ) 
  71. /*++
  72. Outputs a message to the setup log.  Prepends "desk.cpl  " to the strings and 
  73. appends the correct newline chars (rn)==
  74.   --*/
  75. {
  76.     TCHAR ach[1024+40];    // Largest path plus extra
  77.     va_list vArgs;
  78.     va_start(vArgs, pszMsg);
  79.     wvsprintf(ach, pszMsg, vArgs);
  80.     va_end(vArgs);
  81.     OutputDebugString(ach);
  82. }
  83. #ifdef _WIN64
  84. //
  85. //  GetDlgItem and GetDlgCtrlID don't support INT_PTR's,
  86. //  so we have to do it manually.
  87. //  Fortunately, GetWindowLongPtr(GWLP_ID) actually returns a full 64-bit
  88. //  value instead of truncating at 32-bits.
  89. //
  90. #define GetDlgCtrlIDP(hwnd)  GetWindowLongPtr(hwnd, GWLP_ID)
  91. HWND GetDlgItemP(HWND hDlg, INT_PTR id)
  92. {
  93.     HWND hwndChild = GetWindow(hDlg, GW_CHILD);
  94.     while (hwndChild && GetDlgCtrlIDP(hwndChild) != id)
  95.         hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
  96.     return hwndChild;
  97. }
  98. #else
  99. #define GetDlgItemP     GetDlgItem
  100. #define GetDlgCtrlIDP   GetDlgCtrlID
  101. #endif
  102. //
  103. // display devices
  104. //
  105. typedef struct _multimon_device {
  106.     //
  107.     // Main class for settings
  108.     //
  109.     CDeviceSettings * pds;
  110.     //
  111.     // Color and resolution information cache
  112.     // Rebuild when modes are enumerated.
  113.     //
  114.     int            cColors;
  115.     PLONGLONG      ColorList;
  116.     int            cResolutions;
  117.     PPOINT         ResolutionList;
  118.     ULONG          ComboBoxItem;
  119.     DISPLAY_DEVICE DisplayDevice;
  120.     ULONG          DisplayIndex;
  121.     POINT          Snap;
  122.     HDC            hdc;
  123.     //
  124.     // Image information.
  125.     //
  126.     int            w,h;
  127.     HIMAGELIST     himl;
  128.     int            iImage;
  129.     BOOLEAN        bTracking;
  130.     HWND           hwndFlash;  //Flash window.
  131. } MULTIMON_DEVICE, *PMULTIMON_DEVICE;
  132. #define GetDlgCtrlDevice(hwnd) ((PMULTIMON_DEVICE)GetDlgCtrlIDP(hwnd))
  133. BOOL gfFlashWindowRegistered = FALSE;
  134. HWND ghwndToolTipTracking;
  135. HWND ghwndToolTipPopup;
  136. HWND ghwndPropSheet;
  137. void AddTrackingToolTip(PMULTIMON_DEVICE pDevice, HWND hwnd);
  138. void RemoveTrackingToolTip(HWND hwnd);
  139. void AddPopupToolTip(HWND hwndC);
  140. void RemovePopupToolTip(HWND hwndC);
  141. #if QUICK_REFRESH
  142. #define IDC_FREQUENCY_START     2000
  143. HMENU CreateFrequencyMenu(PMULTIMON_DEVICE pDevice);
  144. #endif
  145. extern int AskDynaCDS(HWND hDlg);
  146. extern int GetDisplayCPLPreference(LPCTSTR szRegVal);
  147. extern void SetDisplayCPLPreference(LPCTSTR szRegVal, int val);
  148. // Prototype for CreateStdAccessibleProxy.
  149. // A and W versions are available - pClassName can be ANSI or UNICODE
  150. // string. This is a TCHAR-style prototype, but you can do a A or W
  151. // specific one if desired.
  152. typedef HRESULT (WINAPI *PFNCREATESTDACCESSIBLEPROXY) (
  153.     HWND     hWnd,
  154.     LPTSTR   pClassName,
  155.     LONG     idObject,
  156.     REFIID   riid,
  157.     void **  ppvObject 
  158.     );
  159. // Same for LresultFromObject...
  160. typedef LRESULT (WINAPI *PFNLRESULTFROMOBJECT)(
  161.     REFIID riid,
  162.     WPARAM wParam,
  163.     LPUNKNOWN punk 
  164.     );
  165. PRIVATE PFNCREATESTDACCESSIBLEPROXY s_pfnCreateStdAccessibleProxy = NULL;
  166. PRIVATE PFNLRESULTFROMOBJECT s_pfnLresultFromObject = NULL;
  167. BOOL g_fAttemptedOleAccLoad ;
  168. HMODULE g_hOleAcc;
  169. //-----------------------------------------------------------------------------
  170. static const DWORD sc_MultiMonitorHelpIds[] =
  171. {
  172.    IDC_SCREENSAMPLE,  IDH_DISPLAY_SETTINGS_MONITOR_GRAPHIC,  
  173.    IDC_MULTIMONHELP,  IDH_DISPLAY_SETTINGS_MONITOR_GRAPHIC, 
  174.    IDC_DISPLAYDESK,   IDH_DISPLAY_SETTINGS_MONITOR_GRAPHIC, 
  175.    IDC_DISPLAYLABEL,  IDH_DISPLAY_SETTINGS_DISPLAY_LIST,
  176.    IDC_DISPLAYLIST,   IDH_DISPLAY_SETTINGS_DISPLAY_LIST,
  177.    IDC_DISPLAYTEXT,   IDH_DISPLAY_SETTINGS_DISPLAY_LIST,
  178.    IDC_COLORGROUPBOX, IDH_DISPLAY_SETTINGS_COLORBOX, 
  179.    IDC_COLORBOX,      IDH_DISPLAY_SETTINGS_COLORBOX,
  180.    IDC_COLORSAMPLE,   IDH_DISPLAY_SETTINGS_COLORBOX, 
  181.    IDC_RESGROUPBOX,   IDH_DISPLAY_SETTINGS_SCREENAREA,
  182.    IDC_SCREENSIZE,    IDH_DISPLAY_SETTINGS_SCREENAREA,
  183.    IDC_RES_LESS,      IDH_DISPLAY_SETTINGS_SCREENAREA,
  184.    IDC_RES_MORE,      IDH_DISPLAY_SETTINGS_SCREENAREA,
  185.    IDC_RESXY,         IDH_DISPLAY_SETTINGS_SCREENAREA,
  186.    IDC_DISPLAYUSEME,  IDH_DISPLAY_SETTINGS_EXTEND_DESKTOP_CHECKBOX, 
  187.    IDC_DISPLAYPRIME,  IDH_DISPLAY_SETTINGS_USE_PRIMARY_CHECKBOX,
  188.    IDC_IDENTIFY,          IDH_DISPLAY_SETTINGS_IDENTIFY_BUTTON,
  189.    IDC_TROUBLESHOOT,      IDH_DISPLAY_SETTINGS_TROUBLE_BUTTON,
  190.    IDC_DISPLAYPROPERTIES, IDH_DISPLAY_SETTINGS_ADVANCED_BUTTON,
  191.    0, 0
  192. };
  193. class CAccessibleWrapper: public IAccessible
  194. {
  195.         // We need to do our own refcounting for this wrapper object
  196.         ULONG          m_ref;
  197.         // Need ptr to the IAccessible
  198.         IAccessible *  m_pAcc;
  199.         HWND           m_hwnd;
  200. public:
  201.         CAccessibleWrapper( HWND hwnd, IAccessible * pAcc );
  202.         virtual ~CAccessibleWrapper();
  203.         // IUnknown
  204.         // (We do our own ref counting)
  205.         virtual STDMETHODIMP            QueryInterface(REFIID riid, void** ppv);
  206.         virtual STDMETHODIMP_(ULONG)    AddRef();
  207.         virtual STDMETHODIMP_(ULONG)    Release();
  208.         
  209.         // IDispatch
  210.         virtual STDMETHODIMP            GetTypeInfoCount(UINT* pctinfo);
  211.         virtual STDMETHODIMP            GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo);
  212.         virtual STDMETHODIMP            GetIDsOfNames(REFIID riid, OLECHAR** rgszNames, UINT cNames,
  213.             LCID lcid, DISPID* rgdispid);
  214.         virtual STDMETHODIMP            Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
  215.             DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo,
  216.             UINT* puArgErr);
  217.         // IAccessible
  218.         virtual STDMETHODIMP            get_accParent(IDispatch ** ppdispParent);
  219.         virtual STDMETHODIMP            get_accChildCount(long* pChildCount);
  220.         virtual STDMETHODIMP            get_accChild(VARIANT varChild, IDispatch ** ppdispChild);
  221.         virtual STDMETHODIMP            get_accName(VARIANT varChild, BSTR* pszName);
  222.         virtual STDMETHODIMP            get_accValue(VARIANT varChild, BSTR* pszValue);
  223.         virtual STDMETHODIMP            get_accDescription(VARIANT varChild, BSTR* pszDescription);
  224.         virtual STDMETHODIMP            get_accRole(VARIANT varChild, VARIANT *pvarRole);
  225.         virtual STDMETHODIMP            get_accState(VARIANT varChild, VARIANT *pvarState);
  226.         virtual STDMETHODIMP            get_accHelp(VARIANT varChild, BSTR* pszHelp);
  227.         virtual STDMETHODIMP            get_accHelpTopic(BSTR* pszHelpFile, VARIANT varChild, long* pidTopic);
  228.         virtual STDMETHODIMP            get_accKeyboardShortcut(VARIANT varChild, BSTR* pszKeyboardShortcut);
  229.         virtual STDMETHODIMP            get_accFocus(VARIANT * pvarFocusChild);
  230.         virtual STDMETHODIMP            get_accSelection(VARIANT * pvarSelectedChildren);
  231.         virtual STDMETHODIMP            get_accDefaultAction(VARIANT varChild, BSTR* pszDefaultAction);
  232.         virtual STDMETHODIMP            accSelect(long flagsSel, VARIANT varChild);
  233.         virtual STDMETHODIMP            accLocation(long* pxLeft, long* pyTop, long* pcxWidth, long* pcyHeight, VARIANT varChild);
  234.         virtual STDMETHODIMP            accNavigate(long navDir, VARIANT varStart, VARIANT * pvarEndUpAt);
  235.         virtual STDMETHODIMP            accHitTest(long xLeft, long yTop, VARIANT * pvarChildAtPoint);
  236.         virtual STDMETHODIMP            accDoDefaultAction(VARIANT varChild);
  237.         virtual STDMETHODIMP            put_accName(VARIANT varChild, BSTR szName);
  238.         virtual STDMETHODIMP            put_accValue(VARIANT varChild, BSTR pszValue);
  239. };
  240. CAccessibleWrapper::CAccessibleWrapper( HWND hwnd, IAccessible * pAcc )
  241.     : m_ref( 1 ),
  242.       m_pAcc( pAcc ),
  243.       m_hwnd( hwnd )
  244. {
  245.     ASSERT( m_pAcc );
  246.     m_pAcc->AddRef();
  247. }
  248. CAccessibleWrapper::~CAccessibleWrapper()
  249. {
  250.     m_pAcc->Release();
  251. }
  252. // IUnknown
  253. // Implement refcounting ourselves
  254. // Also implement QI ourselves, so that we return a ptr back to the wrapper.
  255. STDMETHODIMP  CAccessibleWrapper::QueryInterface(REFIID riid, void** ppv)
  256. {
  257.     *ppv = NULL;
  258.     if ((riid == IID_IUnknown)  ||
  259.         (riid == IID_IDispatch) ||
  260.         (riid == IID_IAccessible))
  261.     {
  262.         *ppv = (IAccessible *) this;
  263.     }
  264.     else
  265.         return(E_NOINTERFACE);
  266.     AddRef();
  267.     return(NOERROR);
  268. }
  269. STDMETHODIMP_(ULONG) CAccessibleWrapper::AddRef()
  270. {
  271.     return ++m_ref;
  272. }
  273. STDMETHODIMP_(ULONG) CAccessibleWrapper::Release()
  274. {
  275.     ULONG ulRet = --m_ref;
  276.     if( ulRet == 0 )
  277.         delete this;
  278.     return ulRet;
  279. }
  280. // IDispatch
  281. // - pass all through m_pAcc
  282. STDMETHODIMP  CAccessibleWrapper::GetTypeInfoCount(UINT* pctinfo)
  283. {
  284.     return m_pAcc->GetTypeInfoCount(pctinfo);
  285. }
  286. STDMETHODIMP  CAccessibleWrapper::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo)
  287. {
  288.     return m_pAcc->GetTypeInfo(itinfo, lcid, pptinfo);
  289. }
  290. STDMETHODIMP  CAccessibleWrapper::GetIDsOfNames(REFIID riid, OLECHAR** rgszNames, UINT cNames,
  291.             LCID lcid, DISPID* rgdispid)
  292. {
  293.     return m_pAcc->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
  294. }
  295. STDMETHODIMP  CAccessibleWrapper::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
  296.             DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo,
  297.             UINT* puArgErr)
  298. {
  299.     return m_pAcc->Invoke(dispidMember, riid, lcid, wFlags,
  300.             pdispparams, pvarResult, pexcepinfo,
  301.             puArgErr);
  302. }
  303. // IAccessible
  304. // - pass all through m_pAcc
  305. STDMETHODIMP  CAccessibleWrapper::get_accParent(IDispatch ** ppdispParent)
  306. {
  307.     return m_pAcc->get_accParent(ppdispParent);
  308. }
  309. STDMETHODIMP  CAccessibleWrapper::get_accChildCount(long* pChildCount)
  310. {
  311.     return m_pAcc->get_accChildCount(pChildCount);
  312. }
  313. STDMETHODIMP  CAccessibleWrapper::get_accChild(VARIANT varChild, IDispatch ** ppdispChild)
  314. {
  315.     return m_pAcc->get_accChild(varChild, ppdispChild);
  316. }
  317. STDMETHODIMP  CAccessibleWrapper::get_accName(VARIANT varChild, BSTR* pszName)
  318. {
  319.     return m_pAcc->get_accName(varChild, pszName);
  320. }
  321. STDMETHODIMP  CAccessibleWrapper::get_accValue(VARIANT varChild, BSTR* pszValue)
  322. {
  323.     // varChild.lVal specifies which sub-part of the component
  324.     // is being queried.
  325.     // CHILDID_SELF (0) specifies the overall component - other
  326.     // non-0 values specify a child.
  327.     // In a trackbar, CHILDID_SELF refers to the overall trackbar
  328.     // (which is what we want), whereas other values refer to the
  329.     // sub-components - the actual slider 'thumb', and the 'page
  330.     // up/page down' areas to the left/right of it.
  331.     if( varChild.vt == VT_I4 && varChild.lVal == CHILDID_SELF )
  332.     {
  333.         HWND hDlg;
  334.         TCHAR achRes[120];
  335. #ifndef UNICODE
  336.         WCHAR wszRes[120];
  337. #endif
  338.         hDlg = GetParent( m_hwnd );
  339.         SendDlgItemMessage(hDlg, IDC_RESXY, WM_GETTEXT, 120, (LPARAM)achRes);
  340. #ifdef UNICODE
  341.         *pszValue = SysAllocString( achRes );
  342. #else
  343.         MultiByteToWideChar( CP_ACP, 0, achRes, -1, wszRes, 120 );
  344.         *pszValue = SysAllocString( wszRes );
  345. #endif
  346.         return S_OK;
  347.     }
  348.     else
  349.     {
  350.         // Pass requests about the sub-components to the
  351.         // 'original' IAccessible for us).
  352.         return m_pAcc->get_accValue(varChild, pszValue);
  353.     }
  354. }
  355. STDMETHODIMP  CAccessibleWrapper::get_accDescription(VARIANT varChild, BSTR* pszDescription)
  356. {
  357.     return m_pAcc->get_accDescription(varChild, pszDescription);
  358. }
  359. STDMETHODIMP  CAccessibleWrapper::get_accRole(VARIANT varChild, VARIANT *pvarRole)
  360. {
  361.     return m_pAcc->get_accRole(varChild, pvarRole);
  362. }
  363. STDMETHODIMP  CAccessibleWrapper::get_accState(VARIANT varChild, VARIANT *pvarState)
  364. {
  365.     return m_pAcc->get_accState(varChild, pvarState);
  366. }
  367. STDMETHODIMP  CAccessibleWrapper::get_accHelp(VARIANT varChild, BSTR* pszHelp)
  368. {
  369.     return m_pAcc->get_accHelp(varChild, pszHelp);
  370. }
  371. STDMETHODIMP  CAccessibleWrapper::get_accHelpTopic(BSTR* pszHelpFile, VARIANT varChild, long* pidTopic)
  372. {
  373.     return m_pAcc->get_accHelpTopic(pszHelpFile, varChild, pidTopic);
  374. }
  375. STDMETHODIMP  CAccessibleWrapper::get_accKeyboardShortcut(VARIANT varChild, BSTR* pszKeyboardShortcut)
  376. {
  377.     return m_pAcc->get_accKeyboardShortcut(varChild, pszKeyboardShortcut);
  378. }
  379. STDMETHODIMP  CAccessibleWrapper::get_accFocus(VARIANT * pvarFocusChild)
  380. {
  381.     return m_pAcc->get_accFocus(pvarFocusChild);
  382. }
  383. STDMETHODIMP  CAccessibleWrapper::get_accSelection(VARIANT * pvarSelectedChildren)
  384. {
  385.     return m_pAcc->get_accSelection(pvarSelectedChildren);
  386. }
  387. STDMETHODIMP  CAccessibleWrapper::get_accDefaultAction(VARIANT varChild, BSTR* pszDefaultAction)
  388. {
  389.     return m_pAcc->get_accDefaultAction(varChild, pszDefaultAction);
  390. }
  391. STDMETHODIMP  CAccessibleWrapper::accSelect(long flagsSel, VARIANT varChild)
  392. {
  393.     return m_pAcc->accSelect(flagsSel, varChild);
  394. }
  395. STDMETHODIMP  CAccessibleWrapper::accLocation(long* pxLeft, long* pyTop, long* pcxWidth, long* pcyHeight, VARIANT varChild)
  396. {
  397.     return m_pAcc->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, varChild);
  398. }
  399. STDMETHODIMP  CAccessibleWrapper::accNavigate(long navDir, VARIANT varStart, VARIANT * pvarEndUpAt)
  400. {
  401.     return m_pAcc->accNavigate(navDir, varStart, pvarEndUpAt);
  402. }
  403. STDMETHODIMP  CAccessibleWrapper::accHitTest(long xLeft, long yTop, VARIANT * pvarChildAtPoint)
  404. {
  405.     return m_pAcc->accHitTest(xLeft, yTop, pvarChildAtPoint);
  406. }
  407. STDMETHODIMP  CAccessibleWrapper::accDoDefaultAction(VARIANT varChild)
  408. {
  409.     return m_pAcc->accDoDefaultAction(varChild);
  410. }
  411. STDMETHODIMP  CAccessibleWrapper::put_accName(VARIANT varChild, BSTR szName)
  412. {
  413.     return m_pAcc->put_accName(varChild, szName);
  414. }
  415. STDMETHODIMP  CAccessibleWrapper::put_accValue(VARIANT varChild, BSTR pszValue)
  416. {
  417.     return m_pAcc->put_accValue(varChild, pszValue);
  418. }
  419. class CMultiMon  : public IMultiMonConfig
  420. {
  421.     friend int ComputeNumberOfDisplayDevices();
  422.     friend int DisplaySaveSettings(PVOID pContext, HWND hwnd);
  423.     private:
  424.         // Data Section
  425.         PMULTIMON_DEVICE _pCurDevice;
  426.         PMULTIMON_DEVICE _pPrimaryDevice;
  427.         // HWND for the main window
  428.         HWND _hDlg;
  429.         HWND _hwndDesk;
  430.         HWND _hwndList;
  431.         // union of all monitor RECTs
  432.         RECT _rcDesk;
  433.         // ref count
  434.         UINT _cRef;
  435.         // how to translate to preview size
  436.         int   _DeskScale;
  437.         POINT _DeskOff;
  438.         UINT  _InSetInfo;
  439.         ULONG _NumDevices;
  440.         HBITMAP _hbmScrSample;
  441.         HBITMAP _hbmMonitor;
  442.         HIMAGELIST _himl;
  443.         // UI variables
  444.         int  _iColor;
  445.         int  _iResolution;
  446.         BOOL _bBadDriver         : 1;
  447.         BOOL _bNoAttach          : 1;
  448.         BOOL _bDirty             : 1;
  449.         MULTIMON_DEVICE _Devices[MONITORS_MAX];
  450.         // Private functions
  451.         void _DeskToPreview(LPRECT in, LPRECT out);
  452.         void _OffsetPreviewToDesk(LPRECT in, LPRECT out);
  453.         BOOL _QueryForceSmallFont();
  454.         void _SetPreviewScreenSize(int HRes, int VRes, int iOrgXRes, int iOrgYRes);
  455.         void _CleanupRects(HWND hwndP);
  456.         void _ConfirmPositions();
  457.         void _DoAdvancedSettingsSheet();
  458.         int  _HandleApply();
  459.         BOOL _HandleHScroll(HWND hwndSB, int iCode, int iPos);
  460.         void _RedrawDeskPreviews();
  461.         void _OnAdvancedClicked();
  462.         BOOL _InitDisplaySettings(BOOL bExport);
  463.         int  _EnumerateAllDisplayDevices(); //Enumerates and returns the number of devices.
  464.         void _DestroyMultimonDevice(PMULTIMON_DEVICE pDevice);
  465.         void _DestroyDisplaySettings();
  466.         void _InitUI();
  467.         void _UpdateUI(int FocusToCtrlID = 0);
  468.         LPTSTR _FormatMessageInvoke(LPCTSTR pcszFormat, va_list *argList);
  469.         LPTSTR _FormatMessageWrap(LPCTSTR pcszFormat, ...);
  470.         void _GetDisplayName(PMULTIMON_DEVICE pDevice, LPTSTR pszDisplay, DWORD cchSize);
  471.         int  _SaveDisplaySettings(DWORD dwSet);
  472.         BOOL _RebuildDisplaySettings(BOOL bComplete);
  473.         void _ForwardToChildren(UINT message, WPARAM wParam, LPARAM lParam);
  474.         static BOOL _AnyChange(CDeviceSettings *rgpds[], ULONG numDevices);
  475.         static BOOL _AnyColorChange(CDeviceSettings *rgpds[], ULONG numDevices);
  476.         static BOOL _IsSingleToMultimonChange(CDeviceSettings *rgpds[], 
  477.                                               ULONG numDevices);
  478.         static int _DisplaySaveSettings(CDeviceSettings* rgpds[],
  479.                                         ULONG            numDevices,
  480.                                         HWND             hDlg);
  481.         static int _SaveSettings(CDeviceSettings *rgpds[],
  482.                                  ULONG numDevices,
  483.                                  HWND hDlg,
  484.                                  DWORD dwSet);
  485.         
  486.         // NT specific stuff
  487. #ifdef WINNT
  488.         BOOL _InitMessage();
  489.         void _vPreExecMode();
  490.         void _vPostExecMode();
  491. #endif
  492.     public:
  493.         CMultiMon();
  494.         static BOOL RegisterPreviewWindowClass(WNDPROC pfnWndProc);
  495.         // *** IUnknown methods ***
  496.         STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
  497.         STDMETHODIMP_(ULONG) AddRef(void);
  498.         STDMETHODIMP_(ULONG) Release(void);
  499.         // *** IMultiMonConfig methods ***
  500.         STDMETHOD ( Initialize ) ( HWND hwndHost, WNDPROC pfnWndProc, DWORD dwReserved);
  501.         STDMETHOD ( GetNumberOfMonitors ) (int * pCMon, DWORD dwReserved);
  502.         STDMETHOD ( GetMonitorData) (int iMonitor, MonitorData * pmd, DWORD dwReserved);
  503.         STDMETHOD ( Paint) (THIS_ int iMonitor, DWORD dwReserved);
  504.         void InitMultiMonitorDlg(HWND hDlg);
  505.         PMULTIMON_DEVICE GetCurDevice(){return _pCurDevice;};
  506.         int  GetNumberOfAttachedDisplays();
  507.         void UpdateActiveDisplay(PMULTIMON_DEVICE pDevice, BOOL bRepaint = TRUE);
  508.         BOOL HandleMonitorChange(HWND hwndP, BOOL bMainDlg, BOOL bRepaint = TRUE);
  509.         void SetDirty(BOOL bDirty=TRUE);
  510.         BOOL SetPrimary(PMULTIMON_DEVICE pDevice);
  511.         BOOL SetMonAttached(PMULTIMON_DEVICE pDevice, BOOL bSetAttached,
  512.                             BOOL bForce, HWND hwnd);
  513.         HWND  GetCurDeviceHwnd() { return GetDlgItemP(_hwndDesk, (INT_PTR) _pCurDevice);};
  514.         ULONG GetNumDevices()    { return _NumDevices;};
  515.         BOOL  QueryNoAttach()    { return _bNoAttach;};
  516.         BOOL  IsDirty()          { return _bDirty;};
  517.         void GetMonitorPosition(PMULTIMON_DEVICE pDevice, HWND hwndP, PPOINT ptPos);
  518.         LRESULT CALLBACK WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
  519. };
  520. #if 0
  521.     TraceMsg(TF_DUMP_CSETTINGS,"t     cb          = %d",     _DisplayDevice.cb           );
  522.     TraceMsg(TF_DUMP_CSETTINGS,"t     DeviceName  = %s",     _pstrDisplayDevice   );
  523.     TraceMsg(TF_DUMP_CSETTINGS,"t     DeviceString= %s",     _DisplayDevice.DeviceString );
  524.     TraceMsg(TF_DUMP_CSETTINGS,"t     StateFlags  = %08lx",  _DisplayDevice.StateFlags   );
  525. #endif
  526. CMultiMon::CMultiMon() : _cRef(1) {
  527.     ASSERT(_pCurDevice == NULL);
  528.     ASSERT(_pPrimaryDevice == NULL);
  529.     ASSERT(_DeskScale == 0);
  530.     ASSERT(_InSetInfo == 0);
  531.     ASSERT(_NumDevices == 0);
  532.     ASSERT(IsRectEmpty(&_rcDesk));
  533.     ASSERT(_bNoAttach == FALSE);
  534.     ASSERT(_bDirty == FALSE);
  535. };
  536. void CMultiMon::_DestroyMultimonDevice(PMULTIMON_DEVICE pDevice)
  537. {
  538.     ASSERT(pDevice->pds);
  539.     delete pDevice->pds;
  540.     pDevice->pds = NULL;
  541.     if(pDevice->hwndFlash)
  542.     {
  543.         DestroyWindow(pDevice->hwndFlash);
  544.         pDevice->hwndFlash = NULL;
  545.     }
  546.     if (pDevice->hdc) {
  547.         DeleteDC(pDevice->hdc);
  548.         pDevice->hdc = NULL;
  549.     }
  550.     if (pDevice->ResolutionList) {
  551.         LocalFree(pDevice->ResolutionList);
  552.         pDevice->ResolutionList = NULL;
  553.     }
  554.     if (pDevice->ColorList) {
  555.         LocalFree(pDevice->ColorList);
  556.         pDevice->ColorList = NULL;
  557.     }
  558. }
  559. void CMultiMon::_DestroyDisplaySettings()
  560. {
  561.     ULONG iDevice;
  562.     HWND    hwndC;
  563.     ASSERT(_NumDevices);
  564.     TraceMsg(TF_GENERAL, "DestroyDisplaySettings: %d devices", _NumDevices);
  565.     // We are about to destroy the _Devices below. Pointerts to these devices are used as the 
  566.     // CtrlIDs for the monitor windows. So, we need destroy the monitor windows first; 
  567.     // otherwise, if the monitor windows are destroyed later, they try to use these invalid 
  568.     // pDevice in FlashText. (pDevice->hwndFlash will fault).
  569.     while (hwndC = GetWindow(_hwndDesk, GW_CHILD))
  570.     {
  571.         RemoveTrackingToolTip(hwndC);
  572.         RemovePopupToolTip(hwndC);
  573.         DestroyWindow(hwndC);
  574.     }
  575.     // Now, we can destroy the _Devices safely.
  576.     for (iDevice = 0; iDevice < _NumDevices; iDevice++) {
  577.         _DestroyMultimonDevice(_Devices + iDevice);
  578.         // Note: pds is destroyed and set to zero already in the above call.
  579.         //delete _Devices[iDevice].pds;
  580.         //_Devices[iDevice].pds = 0;
  581.     }
  582.     if (_himl) {
  583.         ImageList_Destroy(_himl);
  584.         _himl = NULL;
  585.     }
  586.     DestroyWindow(ghwndToolTipTracking);
  587.     DestroyWindow(ghwndToolTipPopup);
  588.     ghwndToolTipTracking = NULL;
  589.     ghwndToolTipPopup = NULL;
  590.     TraceMsg(TF_GENERAL, "DestroyDisplaySettings: Finished destroying all devices");
  591. }
  592. #ifdef WINNT
  593. //
  594. // deterines if the applet is in detect mode.
  595. //
  596. //
  597. // Called to put up initial messages that need to appear above the dialog
  598. // box
  599. //
  600. BOOL CMultiMon::_InitMessage()
  601. {
  602.     {
  603.         //
  604.         // _bBadDriver will be set when we fail to build the list of modes,
  605.         // or something else failed during initialization.
  606.         //
  607.         // In almost every case, we should already know about this situation
  608.         // based on our boot code.
  609.         // However, if this is a new situation, just report a "bad driver"
  610.         //
  611.         if (_bBadDriver)
  612.         {
  613.             ASSERT(gbExecMode == EXEC_INVALID_MODE);
  614.             gbExecMode = EXEC_INVALID_MODE;
  615.             gbInvalidMode = EXEC_INVALID_DISPLAY_DRIVER;
  616.         }
  617.         if (gbExecMode == EXEC_INVALID_MODE)
  618.         {
  619.             DWORD Mesg;
  620.             switch(gbInvalidMode) {
  621.             case EXEC_INVALID_NEW_DRIVER:
  622.                 Mesg = MSG_INVALID_NEW_DRIVER;
  623.                 break;
  624.             case EXEC_INVALID_DEFAULT_DISPLAY_MODE:
  625.                 Mesg = MSG_INVALID_DEFAULT_DISPLAY_MODE;
  626.                 break;
  627.             case EXEC_INVALID_DISPLAY_DRIVER:
  628.                 Mesg = MSG_INVALID_DISPLAY_DRIVER;
  629.                 break;
  630.             case EXEC_INVALID_OLD_DISPLAY_DRIVER:
  631.                 Mesg = MSG_INVALID_OLD_DISPLAY_DRIVER;
  632.                 break;
  633.             case EXEC_INVALID_16COLOR_DISPLAY_MODE:
  634.                 Mesg = MSG_INVALID_16COLOR_DISPLAY_MODE;
  635.                 break;
  636.             case EXEC_INVALID_DISPLAY_MODE:
  637.                 Mesg = MSG_INVALID_DISPLAY_MODE;
  638.                 {
  639.                     //
  640.                     // If we are in safe mode, then we will get to here when
  641.                     // we initially log in.  We are in forced VGA mode, so there
  642.                     // is no real error here.  Emulate a click on the OK button
  643.                     // and everybody is happy.
  644.                     //
  645.                     HKEY hSafe;
  646.                     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  647.                                      REGSTR_VAL_SAFEBOOT,
  648.                                      0,
  649.                                      KEY_READ,
  650.                                      &hSafe) == ERROR_SUCCESS) {
  651.                 
  652.                         //
  653.                         // If we ever care about the actual safe mode, the value
  654.                         // is nameed "OptionValue"
  655.                         //
  656.                         RegCloseKey(hSafe);
  657.                         PropSheet_PressButton(GetParent(_hDlg), PSBTN_OK);
  658.                         return TRUE;
  659.                     }
  660.                 }
  661.                 break;
  662.             case EXEC_INVALID_CONFIGURATION:
  663.             default:
  664.                 Mesg = MSG_INVALID_CONFIGURATION;
  665.                 break;
  666.             }
  667.             FmtMessageBox(_hDlg,
  668.                           MB_ICONEXCLAMATION,
  669.                           MSG_CONFIGURATION_PROBLEM,
  670.                           Mesg);
  671.             //
  672.             // For a bad display driver or old display driver, let's send the
  673.             // user straight to the installation dialog.
  674.             //
  675.             if ((gbInvalidMode == EXEC_INVALID_OLD_DISPLAY_DRIVER) ||
  676.                 (gbInvalidMode == EXEC_INVALID_DISPLAY_DRIVER))
  677.             {
  678.                 ASSERT(FALSE);
  679.             }
  680.         }
  681.     }
  682.     return TRUE;
  683. }
  684. VOID CMultiMon::_vPreExecMode()
  685. {
  686.     HKEY hkey;
  687. //    DWORD data;
  688.     //
  689.     // This function sets up the execution mode of the applet.
  690.     // There are four vlid modes.
  691.     //
  692.     // EXEC_NORMAL - When the apple is launched from the control panel
  693.     //
  694.     // EXEC_INVALID_MODE is exactly the same as for NORMAL except we will
  695.     //                   not mark the current mode as tested so the user has
  696.     //                   to at least test a mode
  697.     //
  698.     // EXEC_DETECT - When the applet is launched normally, but a detect was
  699.     //               done on the previous boot (the key in the registry is
  700.     //               set)
  701.     //
  702.     // EXEC_SETUP  - When we launch the applet in setup mode from setup (Both
  703.     //               the registry key is set and the setup flag is passed in).
  704.     //
  705.     //
  706.     // These two keys should only be checked  deleted if the machine has been
  707.     // rebooted and the detect  new display has actually happened.
  708.     // So we will look for the RebootNecessary key (a volatile key) and if
  709.     // it is not present, then we can delete the key.  Otherwise, the reboot
  710.     // has not happened, and we keep the key
  711.     //
  712.     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  713.                      SZ_REBOOT_NECESSARY,
  714.                      0,
  715.                      KEY_READ | KEY_WRITE,
  716.                      &hkey) != ERROR_SUCCESS) {
  717.         if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  718.                          SZ_DETECT_DISPLAY,
  719.                          0,
  720.                          KEY_READ | KEY_WRITE,
  721.                          &hkey) == ERROR_SUCCESS) {
  722.             //
  723.             // NOTE: This key is also set when EXEC_SETUP is being run.
  724.             //
  725.             if (gbExecMode == EXEC_NORMAL) {
  726.                 gbExecMode = EXEC_DETECT;
  727.             } else {
  728.                 //
  729.                 // If we are in setup mode, we also check the extra values
  730.                 // under DetectDisplay that control the unattended installation.
  731.                 //
  732.                 ASSERT(gbExecMode == EXEC_SETUP);
  733.             }
  734.             RegCloseKey(hkey);
  735.         }
  736.         //
  737.         // Check for a new driver being installed
  738.         //
  739.         if ( (gbExecMode == EXEC_NORMAL) &&
  740.              (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  741.                            SZ_NEW_DISPLAY,
  742.                            0,
  743.                            KEY_READ | KEY_WRITE,
  744.                            &hkey) == ERROR_SUCCESS) ) {
  745.             gbExecMode = EXEC_INVALID_MODE;
  746.             gbInvalidMode = EXEC_INVALID_NEW_DRIVER;
  747.             RegCloseKey(hkey);
  748.         }
  749.         RegDeleteKey(HKEY_LOCAL_MACHINE,
  750.                      SZ_DETECT_DISPLAY);
  751.         RegDeleteKey(HKEY_LOCAL_MACHINE,
  752.                      SZ_NEW_DISPLAY);
  753.     }
  754.     {
  755.         LPTSTR psz;
  756.         LPTSTR pszInv;
  757.         switch(gbExecMode) {
  758.             case EXEC_NORMAL:
  759.                 psz = TEXT("Normal Execution mode");
  760.                 break;
  761.             case EXEC_DETECT:
  762.                 psz = TEXT("Detection Execution mode");
  763.                 break;
  764.             case EXEC_SETUP:
  765.                 psz = TEXT("Setup Execution mode");
  766.                 break;
  767.             case EXEC_INVALID_MODE:
  768.                 psz = TEXT("Invalid Mode Execution mode");
  769.                 switch(gbInvalidMode) {
  770.                     case EXEC_INVALID_NEW_DRIVER:
  771.                         pszInv = TEXT("Invalid new driver");
  772.                         break;
  773.                     default:
  774.                         pszInv = TEXT("*** Invalid *** Invalid mode");
  775.                         break;
  776.                 }
  777.                 break;
  778.             default:
  779.                 psz = TEXT("*** Invalid *** Execution mode");
  780.                 break;
  781.         }
  782.         KdPrint(("n nDisplay.cpl: The display applet is in : %wsn", psz));
  783.         if (gbExecMode == EXEC_INVALID_MODE)
  784.         {
  785.             KdPrint(("tt sub invalid mode : %ws", pszInv));
  786.         }
  787.         KdPrint(("nn", psz));
  788.     }
  789. }
  790. VOID CMultiMon::_vPostExecMode() {
  791.     HKEY hkey;
  792.     DWORD cb;
  793.     DWORD data;
  794.     //
  795.     // Check for various invalid configurations
  796.     //
  797.     if ( (gbExecMode == EXEC_NORMAL) &&
  798.          (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  799.                        SZ_INVALID_DISPLAY,
  800.                        0,
  801.                        KEY_READ | KEY_WRITE,
  802.                        &hkey) == ERROR_SUCCESS) ) {
  803.         gbExecMode = EXEC_INVALID_MODE;
  804.         //
  805.         // Check for these fields in increasing order of "badness" or
  806.         // "detail" so that the *worst* error is the one remaining in the
  807.         // gbInvalidMode  variable once all the checks are done.
  808.         //
  809.         cb = 4;
  810.         if (RegQueryValueEx(hkey,
  811.                             TEXT("DefaultMode"),
  812.                             NULL,
  813.                             NULL,
  814.                             (LPBYTE)(&data),
  815.                             &cb) == ERROR_SUCCESS)
  816.         {
  817.             gbInvalidMode = EXEC_INVALID_DEFAULT_DISPLAY_MODE;
  818.         }
  819.         cb = 4;
  820.         if (RegQueryValueEx(hkey,
  821.                             TEXT("BadMode"),
  822.                             NULL,
  823.                             NULL,
  824.                             (LPBYTE)(&data),
  825.                             &cb) == ERROR_SUCCESS)
  826.         {
  827.             gbInvalidMode = EXEC_INVALID_DISPLAY_MODE;
  828.         }
  829.         cb = 4;
  830.         if (RegQueryValueEx(hkey,
  831.                             TEXT("16ColorMode"),
  832.                             NULL,
  833.                             NULL,
  834.                             (LPBYTE)(&data),
  835.                             &cb) == ERROR_SUCCESS)
  836.         {
  837.             gbInvalidMode = EXEC_INVALID_16COLOR_DISPLAY_MODE;
  838.         }
  839.         cb = 4;
  840.         if (RegQueryValueEx(hkey,
  841.                             TEXT("InvalidConfiguration"),
  842.                             NULL,
  843.                             NULL,
  844.                             (LPBYTE)(&data),
  845.                             &cb) == ERROR_SUCCESS)
  846.         {
  847.             gbInvalidMode = EXEC_INVALID_CONFIGURATION;
  848.         }
  849.         cb = 4;
  850.         if (RegQueryValueEx(hkey,
  851.                             TEXT("MissingDisplayDriver"),
  852.                             NULL,
  853.                             NULL,
  854.                             (LPBYTE)(&data),
  855.                             &cb) == ERROR_SUCCESS)
  856.         {
  857.             gbInvalidMode = EXEC_INVALID_DISPLAY_DRIVER;
  858.         }
  859.         //
  860.         // This last case will be set in addition to the previous one in the
  861.         // case where the driver was an old driver linking to winsvr.dll
  862.         // and we can not load it.
  863.         //
  864.         cb = 4;
  865.         if (RegQueryValueEx(hkey,
  866.                             TEXT("OldDisplayDriver"),
  867.                             NULL,
  868.                             NULL,
  869.                             (LPBYTE)(&data),
  870.                             &cb) == ERROR_SUCCESS)
  871.         {
  872.             gbInvalidMode = EXEC_INVALID_OLD_DISPLAY_DRIVER;
  873.         }
  874.         RegCloseKey(hkey);
  875.     }
  876.     //
  877.     // Delete all of these bad configuration keys since we only want the
  878.     // user to see the message once.
  879.     //
  880.     RegDeleteKey(HKEY_LOCAL_MACHINE,
  881.                  SZ_INVALID_DISPLAY);
  882. {
  883.     LPTSTR psz;
  884.     LPTSTR pszInv;
  885.     if (gbExecMode == EXEC_INVALID_MODE)
  886.     {
  887.         switch (gbInvalidMode)
  888.         {
  889.         case EXEC_INVALID_DEFAULT_DISPLAY_MODE:
  890.             pszInv = TEXT("Default mode being used");
  891.             break;
  892.         case EXEC_INVALID_DISPLAY_DRIVER:
  893.             pszInv = TEXT("Invalid Display Driver");
  894.             break;
  895.         case EXEC_INVALID_OLD_DISPLAY_DRIVER:
  896.             pszInv = TEXT("Old Display Driver");
  897.             break;
  898.         case EXEC_INVALID_16COLOR_DISPLAY_MODE:
  899.             pszInv = TEXT("16 color mode not supported");
  900.             break;
  901.         case EXEC_INVALID_DISPLAY_MODE:
  902.             pszInv = TEXT("Invalid display mode");
  903.             break;
  904.         case EXEC_INVALID_CONFIGURATION:
  905.             pszInv = TEXT("Invalid configuration");
  906.             break;
  907.         default:
  908.             psz = TEXT("*** Invalid *** Invalid mode");
  909.             break;
  910.         }
  911.         KdPrint(("tt sub invlid mode : %ws", pszInv));
  912.         KdPrint(("nn", psz));
  913.     }
  914. }
  915. }
  916. #endif
  917. //-----------------------------------------------------------------------------
  918. //-----------------------------------------------------------------------------
  919. void CMultiMon::_DeskToPreview(LPRECT in, LPRECT out)
  920. {
  921.     out->left   = _DeskOff.x + MulDiv(in->left   - _rcDesk.left,_DeskScale,1000);
  922.     out->top    = _DeskOff.y + MulDiv(in->top    - _rcDesk.top, _DeskScale,1000);
  923.     out->right  = _DeskOff.x + MulDiv(in->right  - _rcDesk.left,_DeskScale,1000);
  924.     out->bottom = _DeskOff.y + MulDiv(in->bottom - _rcDesk.top, _DeskScale,1000);
  925. }
  926. //-----------------------------------------------------------------------------
  927. //-----------------------------------------------------------------------------
  928. void CMultiMon::_OffsetPreviewToDesk(LPRECT in, LPRECT out)
  929. {
  930.     int x, y;
  931.     // Scale preview rects back to desk size
  932.     x = _rcDesk.left + MulDiv(in->left - _DeskOff.x,1000,_DeskScale);
  933.     y = _rcDesk.top  + MulDiv(in->top  - _DeskOff.y,1000,_DeskScale);
  934.     // Figure out how much to offset
  935.     x = x - out->left;
  936.     y = y - out->top;
  937.     OffsetRect(out, x, y);
  938. }
  939. //-----------------------------------------------------------------------------
  940. int CMultiMon::_SaveSettings(CDeviceSettings *rgpds[], ULONG numDevices, HWND hDlg, DWORD dwSet)
  941. {
  942.     int     iRet = 0;
  943.     ULONG   iDevice;
  944.     for (iDevice = 0; iDevice < numDevices; iDevice++)
  945.     {
  946.         // BUGBUG - we should only save the settings for devices that have
  947.         // changed.
  948.         int iResult = rgpds[iDevice]->SaveSettings(dwSet);
  949.         if (iResult != DISP_CHANGE_SUCCESSFUL)
  950.         {
  951.             if (iResult == DISP_CHANGE_RESTART)
  952.             {
  953.                 iRet = iResult;
  954.                 continue;
  955.             }
  956.             else
  957.             {
  958.                 FmtMessageBox(hDlg,
  959.                               MB_ICONEXCLAMATION,
  960.                               IDS_CHANGE_SETTINGS,
  961.                               IDS_CHANGESETTINGS_FAILED);
  962.                 ASSERT(iResult < 0);
  963.                 return iResult;
  964.             }
  965.         }
  966.     }
  967.     return iRet;
  968. }
  969. INT_PTR CALLBACK KeepNewDlgProc(HWND hDlg, UINT message , WPARAM wParam, LPARAM lParam)
  970. {
  971.     UINT_PTR idTimer = 0;
  972.     HICON hicon;
  973.     TCHAR szRevert[100];
  974.     TCHAR szString[120];
  975.     switch(message)
  976.     {
  977.         case WM_INITDIALOG:
  978.             hicon = LoadIcon(NULL, IDI_QUESTION);
  979.             if (hicon)
  980.                 SendDlgItemMessage(hDlg, IDC_BIGICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hicon);
  981.             LoadString(hInstance, IDS_REVERTBACK, szRevert, SIZEOF(szRevert));
  982.             wsprintf(szString, szRevert, 15);
  983.             SetDlgItemText(hDlg, IDC_COUNTDOWN, szString);
  984.             idTimer = SetTimer(hDlg, 15, 1000, NULL);
  985.             SetFocus(GetDlgItem(hDlg, IDNO));
  986.             // FALSE so that the focus set above is kept
  987.             return FALSE;
  988.         case WM_DESTROY:
  989.             // raymondc - this code is dead; idTimer is initialized to zero
  990.             // fortunately, timers are automatically killed at window destruction
  991.             // if (idTimer)
  992.             //    KillTimer(hDlg, idTimer);
  993.             hicon = (HICON)SendDlgItemMessage(hDlg, IDC_BIGICON, STM_GETIMAGE, IMAGE_ICON, 0);
  994.             if (hicon)
  995.                 DestroyIcon(hicon);
  996.             break;
  997.         case WM_TIMER:
  998.             KillTimer(hDlg, wParam);
  999.             LoadString(hInstance, IDS_REVERTBACK, szRevert, SIZEOF(szRevert));
  1000.             wsprintf(szString, szRevert, wParam - 1);
  1001.             SetDlgItemText(hDlg, IDC_COUNTDOWN, szString);
  1002.             idTimer = SetTimer(hDlg, wParam - 1, 1000, NULL);
  1003.             if (wParam == 1)
  1004.                 EndDialog(hDlg, IDNO);
  1005.             break;
  1006.         case WM_COMMAND:
  1007.             EndDialog(hDlg, wParam);
  1008.             break;
  1009.         default:
  1010.             return FALSE;
  1011.     }
  1012.     return TRUE;
  1013. }
  1014. BOOL CMultiMon::_RebuildDisplaySettings(BOOL bComplete)
  1015. {
  1016.     BOOL result = TRUE;
  1017.     HCURSOR hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  1018.     for (ULONG iDevice = 0; iDevice < _NumDevices; iDevice++)
  1019.     {    
  1020.         delete _Devices[iDevice].pds;
  1021.         _Devices[iDevice].pds = new CDeviceSettings();
  1022.     }
  1023.     RedrawWindow(_hDlg, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN);
  1024.     SetCursor(hcur);
  1025.     return result;
  1026. }
  1027. int CMultiMon::GetNumberOfAttachedDisplays()
  1028. {
  1029.     int nDisplays = 0;
  1030.     for (ULONG iDevice = 0; iDevice < _NumDevices; iDevice++)
  1031.     {
  1032.         if (_Devices[iDevice].pds->IsAttached())
  1033.             nDisplays++;
  1034.     }
  1035.     return nDisplays;
  1036. }
  1037. BOOL CMultiMon::_AnyColorChange(CDeviceSettings *rgpds[], ULONG numDevices)
  1038. {
  1039.     for (ULONG iDevice = 0; iDevice < numDevices; iDevice++)
  1040.     {
  1041.         if (rgpds[iDevice]->IsAttached() && rgpds[iDevice]->IsColorChanged())
  1042.             return TRUE;
  1043.     }
  1044.     return FALSE;
  1045. }
  1046. /* static */ BOOL CMultiMon::_IsSingleToMultimonChange(CDeviceSettings *rgpds[], 
  1047.                                                        ULONG numDevices)
  1048. {
  1049.     int nAttached = 0;
  1050.     int nOrgAttached = 0;
  1051.             
  1052.     for (ULONG iDevice = 0; 
  1053.          (iDevice < numDevices) && (nOrgAttached <= 1); 
  1054.          iDevice++)
  1055.     {
  1056.         if (rgpds[iDevice]->IsOrgAttached())
  1057.             nOrgAttached++;
  1058.         if (rgpds[iDevice]->IsAttached())
  1059.             nAttached++;
  1060.     }
  1061.     return ((nOrgAttached <= 1) && (nAttached > 1));
  1062. }
  1063. BOOL CMultiMon::_AnyChange(CDeviceSettings *rgpds[], ULONG numDevices)
  1064. {
  1065.    for (ULONG iDevice = 0; iDevice < numDevices; iDevice++)
  1066.    {
  1067.        if (rgpds[iDevice]->IsAttached() && rgpds[iDevice]->bIsModeChanged())
  1068.        {
  1069.            return TRUE;
  1070.        }
  1071.    }
  1072.    return FALSE;
  1073. }
  1074. BOOL CMultiMon::_QueryForceSmallFont()
  1075. {
  1076.     for (ULONG iDevice = 0; iDevice < _NumDevices; iDevice++)
  1077.     {
  1078.         if ((_Devices[iDevice].pds->IsAttached()) &&
  1079.             (!_Devices[iDevice].pds->IsSmallFontNecessary()))
  1080.         {
  1081.             return FALSE;
  1082.         }
  1083.     }
  1084.     return TRUE;
  1085. }
  1086. LPTSTR  CMultiMon::_FormatMessageInvoke(LPCTSTR pcszFormat, va_list *argList)
  1087. {
  1088.     LPTSTR  pszOutput;
  1089.     if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
  1090.                       pcszFormat,
  1091.                       0, 0,
  1092.                       reinterpret_cast<LPTSTR>(&pszOutput), 0,
  1093.                       argList) == 0)
  1094.     {
  1095.         pszOutput = NULL;
  1096.     }
  1097.     return(pszOutput);
  1098. }
  1099. LPTSTR  CMultiMon::_FormatMessageWrap(LPCTSTR pcszFormat, ...)
  1100. {
  1101.     LPTSTR      pszOutput;
  1102.     va_list     argList;
  1103.     va_start(argList, pcszFormat);
  1104.     pszOutput = _FormatMessageInvoke(pcszFormat, &argList);
  1105.     va_end(argList);
  1106.     return(pszOutput);
  1107. }
  1108. void CMultiMon::_GetDisplayName(PMULTIMON_DEVICE pDevice, LPTSTR pszDisplay, DWORD cchSize)
  1109. {
  1110.     LPTSTR  pszFormattedOutput;
  1111.     TCHAR   szMonitor[140];
  1112.     TCHAR   szDisplayFormat[40];
  1113.     LoadString(hInstance, IDS_DISPLAYFORMAT, szDisplayFormat, SIZEOF(szDisplayFormat));
  1114.     pDevice->pds->GetMonitorName(szMonitor);
  1115.     pszFormattedOutput = _FormatMessageWrap(szDisplayFormat,
  1116.                                             pDevice->DisplayIndex,
  1117.                                             szMonitor,
  1118.                                             pDevice->DisplayDevice.DeviceString);
  1119.     lstrcpyn(pszDisplay, pszFormattedOutput, cchSize);
  1120.     DirectLocalFree(pszFormattedOutput);
  1121. }
  1122. void CMultiMon::_OnAdvancedClicked()
  1123. {
  1124.     BOOL bCanBePruned, bIsPruningReadOnly;
  1125.     BOOL bBeforeIsPruningOn, bAfterIsPruningOn;
  1126.     _pCurDevice->pds->GetPruningMode(&bCanBePruned, 
  1127.                                      &bIsPruningReadOnly, 
  1128.                                      &bBeforeIsPruningOn);
  1129.     _DoAdvancedSettingsSheet();
  1130.     if (bCanBePruned && !bIsPruningReadOnly)
  1131.     {
  1132.         _pCurDevice->pds->GetPruningMode(&bCanBePruned, 
  1133.                                          &bIsPruningReadOnly, 
  1134.                                          &bAfterIsPruningOn);
  1135.         if (bBeforeIsPruningOn != bAfterIsPruningOn)
  1136.         {
  1137.             //
  1138.             // pruning mode has changed - update the UI
  1139.             //
  1140.             _InitUI();
  1141.             _UpdateUI();
  1142.         }
  1143.     }
  1144. }
  1145. //-----------------------------------------------------------------------------
  1146. void CMultiMon::_DoAdvancedSettingsSheet()
  1147. {
  1148. #ifndef WINNT
  1149.     HINSTANCE hDesk16 = LoadLibrary16( "DeskCp16.Dll" );
  1150.     FARPROC16 pDesk16 = (FARPROC16)( hDesk16?
  1151.                         GetProcAddress16( hDesk16, "CplApplet" ) : NULL );
  1152. #endif
  1153.     PROPSHEETHEADER psh;
  1154.     HPROPSHEETPAGE rPages[MAX_PAGES];
  1155.     PROPSHEETPAGE psp;
  1156.     HPSXA hpsxa = NULL;
  1157.     HPSXA hpsxaOEM = NULL;
  1158.     INT_PTR iResult = 0;
  1159.     TCHAR szDisplay[140 + 256 + 20];  //Monitor-name and Adapter Properties.
  1160.     TCHAR szMonitor[140];
  1161.     TCHAR szDisplayFormat[35];
  1162.     // Create the "Monitor-name and Adapter-name properties" string to be used as the title for these
  1163.     // property sheets.
  1164.     LoadString(hInstance, IDS_ADVDIALOGTITLE, szDisplayFormat, SIZEOF(szDisplayFormat));
  1165.     _pCurDevice->pds->GetMonitorName(szMonitor);
  1166.     wsprintf(szDisplay,
  1167.              szDisplayFormat,
  1168.              szMonitor,
  1169.              _pCurDevice->DisplayDevice.DeviceString);
  1170.     psh.dwSize = sizeof(psh);
  1171.     psh.dwFlags = PSH_PROPTITLE;
  1172.     psh.hwndParent = GetParent(_hDlg);
  1173.     psh.hInstance = hInstance;
  1174.     psh.pszCaption = szDisplay;
  1175.     psh.nPages = 0;
  1176.     psh.nStartPage = 0;
  1177.     psh.phpage = rPages;
  1178.     psp.dwSize = sizeof(psp);
  1179.     psp.dwFlags = PSP_DEFAULT;
  1180.     psp.hInstance = hInstance;
  1181.     psp.pfnDlgProc = GeneralPageProc;
  1182.     psp.pszTemplate = MAKEINTRESOURCE(DLG_GENERAL);
  1183.     psp.lParam = (LPARAM)_QueryForceSmallFont();
  1184.     if (rPages[psh.nPages] = CreatePropertySheetPage(&psp))
  1185.         psh.nPages++;
  1186. #ifndef WINNT
  1187.     ATOM AtomDevice = GlobalAddAtom((char *)&_pCurDevice->DisplayDevice.DeviceName);
  1188.     if( pDesk16 && CallCPLEntry16( hDesk16, pDesk16, NULL, CPL_INIT, (LPARAM)AtomDevice, 0 ) )
  1189.     {
  1190.         // or just add the default page
  1191.         SHAddPages16( NULL, "DESKCP16.DLL,GetAdapterPage",
  1192.                       _AddDisplayPropSheetPage, (LPARAM)&psh );
  1193.         //
  1194.         // only add the monitor tab iff a monitor exists
  1195.         //
  1196.         if (_pCurDevice->DisplayDevice.DeviceName[0])
  1197.         {
  1198.             TCHAR szMonitor[140];
  1199.             if (_pCurDevice->pds->GetMonitorName(szMonitor))
  1200.             {
  1201.                 SHAddPages16( NULL, "DESKCP16.DLL,GetMonitorPage",
  1202.                           _AddDisplayPropSheetPage, (LPARAM)&psh );
  1203.             }
  1204.         }
  1205.         SHAddPages16( NULL, "DESKCP16.DLL,GetPerformancePage",
  1206.                       _AddDisplayPropSheetPage, (LPARAM)&psh );
  1207.     }
  1208. #endif
  1209.     IDataObject * pdo = NULL;
  1210.     _pCurDevice->pds->QueryInterface(IID_IDataObject, (LPVOID *) &pdo);
  1211.     //
  1212.     // load any extensions from the registry
  1213.     //
  1214.     //
  1215.     // load the generic (non hardware specific) extensions
  1216.     //
  1217.     if( ( hpsxa = SHCreatePropSheetExtArrayEx( HKEY_LOCAL_MACHINE, REGSTR_PATH_CONTROLSFOLDER TEXT("\Device"), 8, pdo) ) != NULL )
  1218.     {
  1219.         SHAddFromPropSheetExtArray( hpsxa, _AddDisplayPropSheetPage, (LPARAM)&psh );
  1220.     }
  1221.     //
  1222.     // load the hardware-specific extensions
  1223.     //
  1224.     // NOTE it is very important to load the OEM extensions *after* the
  1225.     // generic extensions some HW extensions expect to be the last tabs
  1226.     // in the propsheet (right before the settings tab)
  1227.     //
  1228.     // BUGBUG - we may need a way to NOT load the vendor extensions in case
  1229.     // they break our applet.
  1230.     //
  1231. #ifdef WINNT
  1232.     if( ( hpsxaOEM = SHCreatePropSheetExtArrayEx( HKEY_LOCAL_MACHINE, REGSTR_PATH_CONTROLSFOLDER TEXT("\Display"), 8, pdo) ) != NULL )
  1233.     {
  1234.         SHAddFromPropSheetExtArray( hpsxaOEM, _AddDisplayPropSheetPage, (LPARAM)&psh );
  1235.     }
  1236. #else
  1237.     if( ( hpsxaOEM = SHCreatePropSheetExtArrayEx( HKEY_LOCAL_MACHINE, _pCurDevice->DisplayDevice.DeviceKey, 8, pdo) ) != NULL )
  1238.     {
  1239.         SHAddFromPropSheetExtArray( hpsxaOEM, _AddDisplayPropSheetPage, (LPARAM)&psh );
  1240.     }
  1241. #endif
  1242.     //
  1243.     // add a fake settings page to fool OEM extensions (must be last)
  1244.     //
  1245.     if (hpsxa || hpsxaOEM)
  1246.     {
  1247.         AddFakeSettingsPage(&psh);
  1248.     }
  1249.     if (psh.nPages)
  1250.     {
  1251.         iResult = PropertySheet(&psh);
  1252.     }
  1253.     _GetDisplayName(_pCurDevice, szDisplay, ARRAYSIZE(szDisplay));
  1254.     if (_NumDevices == 1)
  1255.     {
  1256.         //Set the name of the primary in the static text
  1257.         //strip the first token off (this is the number we dont want it)
  1258.         TCHAR *pch;
  1259.         for (pch=szDisplay; *pch && *pch != TEXT(' '); pch++);
  1260.         for (;*pch && *pch == TEXT(' '); pch++);
  1261.         SetDlgItemText(_hDlg, IDC_DISPLAYTEXT, pch);
  1262.     }
  1263.     else
  1264.     {
  1265.         ComboBox_DeleteString(_hwndList, _pCurDevice->ComboBoxItem);
  1266.         ComboBox_InsertString(_hwndList, _pCurDevice->ComboBoxItem, szDisplay);
  1267.         ComboBox_SetItemData(_hwndList, _pCurDevice->ComboBoxItem, (DWORD_PTR)_pCurDevice);
  1268.         ComboBox_SetCurSel(_hwndList, _pCurDevice->ComboBoxItem);
  1269.     }
  1270.     if( hpsxa )
  1271.         SHDestroyPropSheetExtArray( hpsxa );
  1272.     if( hpsxaOEM )
  1273.         SHDestroyPropSheetExtArray( hpsxaOEM );
  1274.     if (pdo)
  1275.         pdo->Release();
  1276. #ifndef WINNT
  1277.     if (pDesk16)
  1278.         CallCPLEntry16( hDesk16, pDesk16, NULL, CPL_EXIT, 0, 0 );
  1279.     if (AtomDevice)
  1280.         GlobalDeleteAtom(AtomDevice);
  1281.     if( hDesk16 )
  1282.         FreeLibrary16( hDesk16 );
  1283. #endif
  1284.     if ((iResult == ID_PSRESTARTWINDOWS) || (iResult == ID_PSREBOOTSYSTEM))
  1285.     {
  1286.         PropSheet_CancelToClose(GetParent(_hDlg));
  1287.         if (iResult == ID_PSREBOOTSYSTEM)
  1288.             PropSheet_RebootSystem(ghwndPropSheet);
  1289.         else
  1290.             PropSheet_RestartWindows(ghwndPropSheet);
  1291.     }
  1292.     //
  1293.     // BUGBUG
  1294.     // Reset the dirty flag based on what the extensions did.
  1295.     //
  1296.     //
  1297.     // Reset the controls in case someone changed the selected mode.
  1298.     //
  1299.     UpdateActiveDisplay(NULL);
  1300. }
  1301. //-----------------------------------------------------------------------------
  1302. void CMultiMon::UpdateActiveDisplay(PMULTIMON_DEVICE pDevice, BOOL bRepaint /*=TRUE*/)
  1303. {
  1304.     HWND hwndC;
  1305.     _InSetInfo++;
  1306.     if (pDevice == NULL)
  1307.         pDevice = (PMULTIMON_DEVICE)ComboBox_GetItemData(_hwndList, ComboBox_GetCurSel(_hwndList));
  1308.     else
  1309.         ComboBox_SetCurSel(_hwndList, pDevice->ComboBoxItem);
  1310.     if (pDevice && pDevice != (PMULTIMON_DEVICE)CB_ERR)
  1311.     {
  1312.         hwndC = GetCurDeviceHwnd();
  1313.         // The Current Device has changed, so, force recreating the bitmap the next time 
  1314.         // we paint the monitor on the preview window.
  1315.         _pCurDevice->w = pDevice->w = 0;
  1316.         _pCurDevice = pDevice;
  1317.         if (hwndC)
  1318.             RedrawWindow(hwndC, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
  1319.         hwndC = GetCurDeviceHwnd();
  1320.         if (hwndC)
  1321.             RedrawWindow(hwndC, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
  1322.         if(_NumDevices > 1)
  1323.         {
  1324.             //
  1325.             // Update the two check box windows
  1326.             //
  1327. #ifdef WINNT
  1328.             CheckDlgButton(_hDlg, IDC_DISPLAYPRIME, _pCurDevice->pds->IsPrimary());
  1329.             EnableWindow(GetDlgItem(_hDlg, IDC_DISPLAYPRIME),
  1330.                      _pCurDevice->pds->IsAttached() &&
  1331.                      !_pCurDevice->pds->IsPrimary());
  1332. #endif
  1333.             CheckDlgButton(_hDlg, IDC_DISPLAYUSEME, _pCurDevice->pds->IsAttached());
  1334.             EnableWindow(GetDlgItem(_hDlg, IDC_DISPLAYUSEME),
  1335.                      !_bNoAttach && !_pCurDevice->pds->IsPrimary());
  1336.         }
  1337.         //
  1338.         // Reset the values for the list boxes, and then repaint it
  1339.         //
  1340.         if(bRepaint)
  1341.         {
  1342.             _InitUI();
  1343.             _UpdateUI();
  1344.         }
  1345.     }
  1346.     else
  1347.     {
  1348.         //
  1349.         // No display device !
  1350.         //
  1351.         TraceMsg(TF_WARNING, "**** UpdateActiveDisplay: No display device!!!!");
  1352.         ASSERT(FALSE);
  1353.     }
  1354.     _InSetInfo--;
  1355. }
  1356. // ---------------------------------------------------------------------------
  1357. // Initialize the resolution and color UI widgets
  1358. //
  1359. void CMultiMon::_InitUI()
  1360. {
  1361.     int       i;
  1362.     int       Color;
  1363.     //
  1364.     // Update the Color list
  1365.     //
  1366.     TraceMsg(TF_FUNC, "_InitUI() -- Color list");
  1367.     SendDlgItemMessage(_hDlg, IDC_COLORBOX, CB_RESETCONTENT, 0, 0);
  1368.     if (_pCurDevice->ColorList)
  1369.     {
  1370.         LocalFree(_pCurDevice->ColorList);
  1371.         _pCurDevice->ColorList = NULL;
  1372.     }
  1373.     _pCurDevice->cColors = _pCurDevice->pds->GetColorList(NULL, &_pCurDevice->ColorList);
  1374.     for (i = 0; i < _pCurDevice->cColors; i++)
  1375.     {
  1376.         TCHAR  achColor[50];
  1377.         DWORD  idColor;
  1378.         Color = (int) *(_pCurDevice->ColorList + i);
  1379.         //
  1380.         // convert bit count to number of colors and make it a string
  1381.         //
  1382.         switch (Color)
  1383.         {
  1384.         case 32: idColor = ID_DSP_TXT_TRUECOLOR32; break;
  1385.         case 24: idColor = ID_DSP_TXT_TRUECOLOR24; break;
  1386.         case 16: idColor = ID_DSP_TXT_16BIT_COLOR; break;
  1387.         case 15: idColor = ID_DSP_TXT_15BIT_COLOR; break;
  1388.         case  8: idColor = ID_DSP_TXT_8BIT_COLOR; break;
  1389.         case  4: idColor = ID_DSP_TXT_4BIT_COLOR; break;
  1390.         default:
  1391.             ASSERT(FALSE);
  1392.         }
  1393.         LoadString(hInstance, idColor, achColor, SIZEOF(achColor));
  1394.         SendDlgItemMessage(_hDlg, IDC_COLORBOX, CB_INSERTSTRING, i, (LPARAM)achColor);
  1395.     }
  1396.     //
  1397.     // Update the screen Size List
  1398.     //
  1399.     TraceMsg(TF_FUNC, "_InitUI() -- Screen Size list");
  1400.     if (_pCurDevice->ResolutionList)
  1401.     {
  1402.         LocalFree(_pCurDevice->ResolutionList);
  1403.         _pCurDevice->ResolutionList = NULL;
  1404.     }
  1405.     _pCurDevice->cResolutions =
  1406.         _pCurDevice->pds->GetResolutionList(-1, &_pCurDevice->ResolutionList);
  1407.     SendDlgItemMessage(_hDlg, IDC_SCREENSIZE, TBM_SETRANGE, TRUE,
  1408.                        MAKELONG(0, _pCurDevice->cResolutions - 1));
  1409.     TraceMsg(TF_FUNC, "_InitUI() -- Res MaxRange = %d", _pCurDevice->cResolutions - 1);
  1410.     //
  1411.     // Reset the indices since they are no longer valid
  1412.     //
  1413.     _iResolution = -1;
  1414.     _iColor = -1;
  1415.     //EnableWindow(GetDlgItem(_hDlg, IDC_COLORBOX), _fOrgAttached);
  1416.     //EnableWindow(GetDlgItem(_hDlg, IDC_SCREENSIZE), _fOrgAttached);
  1417. }
  1418. // ---------------------------------------------------------------------------
  1419. // Update the resolution and color UI widgets
  1420. //
  1421. void CMultiMon::_UpdateUI(int FocusToCtrlID)
  1422. {
  1423.     int  i;
  1424.     POINT Res;
  1425.     int   Color;
  1426.     BOOL bRepaint;
  1427.     //
  1428.     // Get the current values
  1429.     //
  1430.     _pCurDevice->pds->GetCurResolution(&Res);
  1431.     Color = _pCurDevice->pds->GetCurColor();
  1432.     //
  1433.     // Update the color listbox
  1434.     //
  1435.     TraceMsg(TF_FUNC, "_UpdateUI() -- Set Color %d", Color);
  1436.     for (i=0; i<_pCurDevice->cColors; i++)
  1437.     {
  1438.         if (Color == (int) *(_pCurDevice->ColorList + i))
  1439.         {
  1440.             TraceMsg(TF_FUNC, "_UpdateUI() -- Set Color index %d", i);
  1441.             if (_iColor == i)
  1442.             {
  1443.                 TraceMsg(TF_FUNC, "_UpdateUI() -- Set Color index %d - is current", i);
  1444.                 break;
  1445.             }
  1446.             HBITMAP hbm, hbmOld;
  1447.             int iBitmap = IDB_COLOR4DITHER;
  1448.             HDC hdc = GetDC(NULL);
  1449.             int bpp = GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL);
  1450.             SendDlgItemMessage(_hDlg, IDC_COLORBOX, CB_SETCURSEL, i, 0);
  1451.             if (Color <= 4)
  1452.                 iBitmap = IDB_COLOR4;
  1453.             else if (bpp >= 16)
  1454.             {
  1455.                 if (Color <= 8)
  1456.                     iBitmap = IDB_COLOR8;
  1457.                 else if (Color <= 16)
  1458.                     iBitmap = IDB_COLOR16;
  1459.                 else
  1460.                     iBitmap = IDB_COLOR24;
  1461.             }
  1462.             ReleaseDC(NULL, hdc);
  1463.             hbm = (HBITMAP)LoadImage(hInstance, MAKEINTRESOURCE(iBitmap), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
  1464.             if (hbm)
  1465.             {
  1466.                 hbmOld = (HBITMAP) SendDlgItemMessage(_hDlg, IDC_COLORSAMPLE, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbm);
  1467.                 if (hbmOld)
  1468.                 {
  1469.                     DeleteObject(hbmOld);
  1470.                 }
  1471.             }
  1472.             _iColor = i;
  1473.             break;
  1474.         }
  1475.     }
  1476.     if (i == _pCurDevice->cColors)
  1477.     {
  1478.         TraceMsg(TF_ERROR, "_UpdateUI -- !!! inconsistent color list !!!");
  1479.     }
  1480.     TraceMsg(TF_FUNC, "_UpdateUI() -- Set Resolution %d %d", Res.x, Res.y);
  1481.     //
  1482.     // Update the resolution string
  1483.     //
  1484.     {
  1485.         TCHAR achStr[80];
  1486.         TCHAR achRes[120];
  1487.         LoadString(hInstance, ID_DSP_TXT_XBYY, achStr, SIZEOF(achStr));
  1488.         wsprintf(achRes, achStr, Res.x, Res.y);
  1489.         SendDlgItemMessage(_hDlg, IDC_RESXY, WM_SETTEXT, 0, (LPARAM)achRes);
  1490.     }
  1491.     //
  1492.     // Update the resolution slider
  1493.     //
  1494.     for (i=0; i<_pCurDevice->cResolutions; i++)
  1495.     {
  1496.         if ( (Res.x == (*(_pCurDevice->ResolutionList + i)).x) &&
  1497.              (Res.y == (*(_pCurDevice->ResolutionList + i)).y) )
  1498.         {
  1499.             TraceMsg(TF_FUNC, "_UpdateUI() -- Set Resolution index %d", i);
  1500.             if (_iResolution == i)
  1501.             {
  1502.                 TraceMsg(TF_FUNC, "_UpdateUI() -- Set Resolution index %d - is current", i);
  1503.                 break;
  1504.             }
  1505.             SendDlgItemMessage(_hDlg, IDC_SCREENSIZE, TBM_SETPOS, TRUE, i);
  1506.             break;
  1507.         }
  1508.     }
  1509.     if (i == _pCurDevice->cResolutions)
  1510.     {
  1511.         TraceMsg(TF_ERROR, "_UpdateUI -- !!! inconsistent color list !!!");
  1512.     }
  1513.     bRepaint = (i != _iResolution);
  1514.     _iResolution = i;
  1515.     //
  1516.     // If the resolution has changed, we have to repaint the preview window
  1517.     // Set the focus back to the trackbar after the repaint so any further
  1518.     // kb events will be send to it rather than the preview window
  1519.     //
  1520.     if (bRepaint) {
  1521.         SendMessage(_hDlg, MM_REDRAWPREVIEW, 0, 0);
  1522.     }
  1523.     if (FocusToCtrlID != 0) {
  1524.         SetFocus(GetDlgItem(_hDlg, FocusToCtrlID));
  1525.     }
  1526. }
  1527. //----------------------------------------------------------------------------
  1528. //
  1529. //  SetPrimary()
  1530. //
  1531. //----------------------------------------------------------------------------
  1532. BOOL
  1533. CMultiMon::SetPrimary(
  1534.     PMULTIMON_DEVICE pDevice)
  1535. {
  1536.     //
  1537.     // Check if state is already set.
  1538.     //
  1539.     if (pDevice == _pPrimaryDevice)
  1540.     {
  1541.         pDevice->pds->SetPrimary(TRUE);
  1542.         return TRUE;
  1543.     }
  1544.     ASSERT(pDevice->pds->IsAttached());
  1545.     //
  1546.     // make sure the primary is in a valid mode
  1547.     //
  1548.     // BUGBUG
  1549.     //_VerifyPrimaryMode(FALSE);
  1550.     //
  1551.     // make sure the primary is in a mode >= 256 color
  1552.     //
  1553.     if (pDevice->pds->GetCurColor() < 8)
  1554.     {
  1555.         for (ULONG iDevice = 0; iDevice < _NumDevices; iDevice++)
  1556.         {
  1557.             _Devices[iDevice].pds->SetAttached(FALSE);
  1558.         }
  1559.         pDevice->pds->SetAttached(TRUE);
  1560.         RedrawWindow(GetDlgItem(_hDlg, IDC_DISPLAYDESK), NULL, NULL,
  1561.             RDW_ALLCHILDREN | RDW_ERASE | RDW_INVALIDATE);
  1562.     }
  1563.     _pPrimaryDevice->pds->SetPrimary(FALSE);
  1564.     pDevice->pds->SetPrimary(TRUE);
  1565.     _pPrimaryDevice = pDevice;
  1566.     SetDirty();
  1567.     return TRUE;
  1568. }
  1569. //----------------------------------------------------------------------------
  1570. //
  1571. //  SetMonAttached()
  1572. //
  1573. //----------------------------------------------------------------------------
  1574. BOOL
  1575. CMultiMon::SetMonAttached(
  1576.     PMULTIMON_DEVICE pDevice,
  1577.     BOOL bSetAttached,
  1578.     BOOL bForce,
  1579.     HWND hwnd)
  1580. {
  1581.     if (pDevice->pds->IsAttached() == bSetAttached)
  1582.     {
  1583.         return TRUE;
  1584.     }
  1585.     if (bSetAttached)
  1586.     {
  1587.         //
  1588.         // Make sure this device actually has a rectangle.
  1589.         // If it does not (not configured in the registry, then we need
  1590.         // to put up a popup and ask the user to configure the device.
  1591.         //
  1592.         // BUGBUG
  1593.         // RECT rcPos;
  1594.         // pDevice->pds->GetCurPosition(&rcPos);
  1595.         // if (IsRectEmpty(&rcPos))
  1596.         //     return FALSE;
  1597.         if (hwnd)
  1598.         {
  1599.             //
  1600.             // Check to see if we should ask the user about enabling this device
  1601.             //
  1602.             if (bForce == FALSE)
  1603.             {
  1604.                 TCHAR szTurnItOn[400];
  1605.                 TCHAR szTurnOnTitleFormat[30];
  1606.                 TCHAR szTurnOnTitle[110];
  1607.                 LPTSTR pstr = szTurnItOn;
  1608.                 LoadString(hInstance, IDS_TURNONTITLE, szTurnOnTitleFormat, SIZEOF(szTurnOnTitleFormat));
  1609.                 wsprintf(szTurnOnTitle, szTurnOnTitleFormat, pDevice->DisplayIndex);
  1610.                 if (GetNumberOfAttachedDisplays() == 1)
  1611.                 {
  1612.                     LoadString(hInstance, IDS_TURNONMSG, szTurnItOn, SIZEOF(szTurnItOn));
  1613.                     pstr += lstrlen(szTurnItOn);
  1614.                 }
  1615.                 LoadString(hInstance, IDS_TURNITON, pstr, SIZEOF(szTurnItOn));
  1616.                 if (ShellMessageBox(hInstance, hwnd, szTurnItOn, szTurnOnTitle,
  1617.                                     MB_YESNO | MB_ICONINFORMATION) != IDYES)
  1618.                 {
  1619.                    return FALSE;
  1620.                 }
  1621.             }
  1622.         }
  1623.         pDevice->pds->SetAttached(TRUE);
  1624.     }
  1625.     else  // (bSetAttached == FALSE)
  1626.     {
  1627.         //
  1628.         // Can't detach if we have only one device or it's the primary.
  1629.         // The UI should disable this situation
  1630.         //
  1631.         if ((GetNumberOfAttachedDisplays() == 1) ||
  1632.             pDevice->pds->IsPrimary())
  1633.         {
  1634.             ASSERT(FALSE);
  1635.         }
  1636.         pDevice->pds->SetAttached(FALSE);
  1637.     }
  1638.     SetDirty();
  1639.     return TRUE;
  1640. #if 0
  1641. #ifndef WINNT
  1642.         // BUGBUG Memphis stuff
  1643.         //else
  1644.         //{
  1645.         //    _pPrimaryDevice->pds->SetMode(640,480,8);
  1646.         //}
  1647. #endif
  1648. #endif
  1649. }
  1650. //----------------------------------------------------------------------------
  1651. //
  1652. //  SetDirty
  1653. //
  1654. //----------------------------------------------------------------------------
  1655. void CMultiMon::SetDirty(BOOL bDirty)
  1656. {
  1657.     _bDirty = bDirty;
  1658.     if (_bDirty)
  1659.     {
  1660.         PostMessage(GetParent(_hDlg), PSM_CHANGED, (WPARAM)_hDlg, 0L);
  1661.     }
  1662. }
  1663. //-----------------------------------------------------------------------------
  1664. void CMultiMon::_CleanupRects(HWND hwndP)
  1665. {
  1666.     int   n;
  1667.     HWND  hwndC;
  1668.     DWORD arcDev[MONITORS_MAX];
  1669.     RECT arc[MONITORS_MAX];
  1670.     DWORD iArcPrimary = 0;
  1671.     RECT rc;
  1672.     RECT rcU;
  1673.     int   i;
  1674.     RECT rcPrev;
  1675.     int sx,sy;
  1676.     int x,y;
  1677.     //
  1678.     // get the positions of all the windows
  1679.     //
  1680.     n = 0;
  1681.     for (ULONG iDevice = 0; iDevice < _NumDevices; iDevice++)
  1682.     {
  1683.         PMULTIMON_DEVICE pDevice = &_Devices[iDevice];
  1684.         hwndC = GetDlgItemP(hwndP, (INT_PTR) pDevice);
  1685.         if (hwndC != NULL)
  1686.         {
  1687.             RECT rcPos;
  1688.             TraceMsg(TF_GENERAL, "_CleanupRects start Device %08lx, Dev = %d, hwnd = %08lx",
  1689.                      pDevice, iDevice, hwndC);
  1690.             ShowWindow(hwndC, SW_SHOW);
  1691.             GetWindowRect(hwndC, &arc[n]);
  1692.             MapWindowPoints(NULL, hwndP, (POINT FAR*)&arc[n], 2);
  1693.             pDevice->pds->GetCurPosition(&rcPos);
  1694.             _OffsetPreviewToDesk(&arc[n], &rcPos);
  1695.             arc[n] = rcPos;
  1696.             arcDev[n] = iDevice;
  1697.             // TEMP
  1698.             // For non-atached devices, make sure they end up to the right
  1699.             // Eventually, non-attached devices should be showed aligned on the
  1700.             // right hand side of the window.
  1701.             if (!pDevice->pds->IsAttached())
  1702.             {
  1703.                 OffsetRect(&arc[n], 10000, 0);
  1704.             }
  1705.             if (pDevice->pds->IsPrimary())
  1706.             {
  1707.                 TraceMsg(TF_GENERAL, "_CleanupRects primary Device %08lx", pDevice);
  1708.                 iArcPrimary = n;
  1709.             }
  1710.             n++;
  1711.         }
  1712.     }
  1713.     //
  1714.     // cleanup the rects
  1715.     //
  1716.     AlignRects(arc, n, iArcPrimary, CUDR_NORMAL);
  1717.     //
  1718.     // Get the union.
  1719.     //
  1720.     SetRectEmpty(&rcU);
  1721.     for (i=0; i<n; i++)
  1722.         UnionRect(&rcU, &rcU, &arc[i]);
  1723.     GetClientRect(hwndP, &rcPrev);
  1724.     //
  1725.     // only rescale if the new desk hangs outside the preview area.
  1726.     // or is too small
  1727.     //
  1728.     _DeskToPreview(&rcU, &rc);
  1729.     x = ((rcPrev.right  - rcPrev.left)-(rc.right  - rc.left))/2;
  1730.     y = ((rcPrev.bottom - rcPrev.top) -(rc.bottom - rc.top))/2;
  1731.     if (rcU.left < 0 || rcU.top < 0 || x < 0 || y < 0 ||
  1732.         rcU.right > rcPrev.right || rcU.bottom > rcPrev.bottom ||
  1733.         (x > (rcPrev.right-rcPrev.left)/8 &&
  1734.          y > (rcPrev.bottom-rcPrev.top)/8))
  1735.     {
  1736.         _rcDesk = rcU;
  1737.         sx = MulDiv(rcPrev.right  - rcPrev.left - 16,1000,_rcDesk.right  - _rcDesk.left);
  1738.         sy = MulDiv(rcPrev.bottom - rcPrev.top  - 16,1000,_rcDesk.bottom - _rcDesk.top);
  1739.         _DeskScale = min(sx,sy) * 2 / 3;
  1740.         _DeskToPreview(&_rcDesk, &rc);
  1741.         _DeskOff.x = ((rcPrev.right  - rcPrev.left)-(rc.right  - rc.left))/2;
  1742.         _DeskOff.y = ((rcPrev.bottom - rcPrev.top) -(rc.bottom - rc.top))/2;
  1743.     }
  1744.     //
  1745.     // Show all the windows and save them all to the devmode.
  1746.     //
  1747.     for (i=0; i < n; i++)
  1748.     {
  1749.         RECT rcPos;
  1750.         POINT ptPos;
  1751.         _Devices[arcDev[i]].pds->GetCurPosition(&rcPos);
  1752.         hwndC = GetDlgItemP(hwndP, (INT_PTR) &_Devices[arcDev[i]]);
  1753.         _DeskToPreview(&arc[i], &rc);
  1754.         rc.right =  MulDiv(RECTWIDTH(rcPos),  _DeskScale, 1000);
  1755.         rc.bottom = MulDiv(RECTHEIGHT(rcPos), _DeskScale, 1000);
  1756.         TraceMsg(TF_GENERAL, "_CleanupRects set Dev = %d, hwnd = %08lx", arcDev[i], hwndC);
  1757.         TraceMsg(TF_GENERAL, "_CleanupRects window pos %d,%d,%d,%d", rc.left, rc.top, rc.right, rc.bottom);
  1758.         SetWindowPos(hwndC,
  1759.                      NULL,
  1760.                      rc.left,
  1761.                      rc.top,
  1762.                      rc.right,
  1763.                      rc.bottom,
  1764.                      SWP_NOZORDER);
  1765.         ptPos.x = arc[i].left;
  1766.         ptPos.y = arc[i].top;
  1767.         _Devices[arcDev[i]].pds->SetCurPosition(&ptPos);
  1768.     }
  1769.     TraceMsg(TF_GENERAL, "");
  1770. }
  1771. void CMultiMon::_ConfirmPositions()
  1772. {
  1773.     ASSERT (_NumDevices > 1);
  1774.     PMULTIMON_DEVICE pDevice;
  1775.     ULONG iDevice;
  1776.     
  1777.     for (iDevice = 0; iDevice < _NumDevices; iDevice++)
  1778.     {
  1779.         pDevice = &_Devices[iDevice];
  1780.         if (pDevice->pds->IsOrgAttached())
  1781.         {
  1782.             RECT rcOrg, rcCur;
  1783.             
  1784.             pDevice->pds->GetCurPosition(&rcCur);
  1785.             pDevice->pds->GetOrgPosition(&rcOrg);
  1786.             if ((rcCur.left != rcOrg.left) ||
  1787.                 (rcCur.top != rcOrg.top))
  1788.             {
  1789.                 POINT ptOrg;
  1790.     
  1791.                 ptOrg.x = rcCur.left;
  1792.                 ptOrg.y = rcCur.top;
  1793.                 pDevice->pds->SetOrgPosition(&ptOrg);
  1794.                 SetDirty(TRUE);
  1795.             }
  1796.         }
  1797.     }
  1798. }
  1799. void CMultiMon::GetMonitorPosition(PMULTIMON_DEVICE pDevice, HWND hwndP, PPOINT ptPos)
  1800. {
  1801.     int iPrimary;
  1802.     HWND hwndC;
  1803.     RECT rcPos;
  1804.     RECT arc[MONITORS_MAX];
  1805.     int i;
  1806.     for (ULONG iDevice = 0; iDevice < _NumDevices; iDevice++)
  1807.     {
  1808.         PMULTIMON_DEVICE pDevice = &_Devices[iDevice];
  1809.         hwndC = GetDlgItemP(hwndP, (INT_PTR) pDevice);
  1810.         ASSERT(hwndC);
  1811.         GetWindowRect(hwndC, &arc[iDevice]);
  1812.         MapWindowPoints(NULL, hwndP, (POINT FAR*)&arc[iDevice], 2);
  1813.         pDevice->pds->GetCurPosition(&rcPos);
  1814.         _OffsetPreviewToDesk(&arc[iDevice], &rcPos);
  1815.         arc[iDevice] = rcPos;
  1816.         if (pDevice->pds->IsPrimary()) {
  1817.             iPrimary = iDevice;
  1818.         }
  1819.     }
  1820.     AlignRects(arc, iDevice, iPrimary, CUDR_NORMAL);
  1821.     i = (int)(pDevice - _Devices);
  1822.     ptPos->x = arc[i].left;
  1823.     ptPos->y = arc[i].top;
  1824. }
  1825. BOOL CMultiMon::HandleMonitorChange(HWND hwndP, BOOL bMainDlg, BOOL bRepaint /*=TRUE*/)
  1826. {
  1827.     if (!bMainDlg && _InSetInfo)
  1828.         return FALSE;
  1829.     SetDirty();
  1830.     if (bMainDlg)
  1831.         BringWindowToTop(hwndP);
  1832.     _CleanupRects(GetParent(hwndP));
  1833.     UpdateActiveDisplay(_pCurDevice, bRepaint);
  1834.     return TRUE;
  1835. }
  1836. BOOL CMultiMon::RegisterPreviewWindowClass(WNDPROC pfnWndProc)
  1837. {
  1838.     TraceMsg(TF_GENERAL, "InitMultiMonitorDlgn");
  1839.     WNDCLASS         cls;
  1840.     cls.hCursor        = LoadCursor(NULL,IDC_ARROW);
  1841.     cls.hIcon          = NULL;
  1842.     cls.lpszMenuName   = NULL;
  1843.     cls.lpszClassName  = TEXT("Monitor32");
  1844.     cls.hbrBackground  = (HBRUSH)(COLOR_DESKTOP + 1);
  1845.     cls.hInstance      = hInstance;
  1846.     cls.style          = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
  1847.     cls.lpfnWndProc    = pfnWndProc;
  1848.     cls.cbWndExtra     = SIZEOF(LPVOID);
  1849.     cls.cbClsExtra     = 0;
  1850.     return RegisterClass(&cls);
  1851. }
  1852. LRESULT CALLBACK DeskWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uID, DWORD_PTR dwRefData);
  1853. // This function is called from desk.c; Hence extern "C".
  1854. // This function is needed to determine if we need to use the single monitor's dialog 
  1855. // or multi-monitor's dialog template at the time of starting the control panel applet.
  1856. int ComputeNumberOfDisplayDevices()
  1857. {
  1858.     int iNumberOfDevices = 0;
  1859.     CMultiMon   *pMultiMon = new CMultiMon;
  1860.     int iDevice;
  1861.     // Enumerate all display devices to count the number of valid devices.
  1862.     iNumberOfDevices = pMultiMon->_EnumerateAllDisplayDevices();
  1863.     // Now that we have the number of devices, let's cleanup the device settings we
  1864.     // created in the process of enumerating above.
  1865.     for (iDevice = 0; iDevice < iNumberOfDevices; iDevice++)
  1866.         pMultiMon->_DestroyMultimonDevice(&pMultiMon->_Devices[iDevice]);
  1867.     // Let's clean up the MultiMon we allocated earlier.
  1868.     delete pMultiMon;
  1869.     
  1870.     return iNumberOfDevices;
  1871. }
  1872. BOOL CMultiMon::_InitDisplaySettings(BOOL bExport)
  1873. {
  1874.     HWND             hwndC;
  1875.     int              iItem;
  1876.     LONG             iPrimeDevice;
  1877.     TCHAR            ach[128];
  1878.     PMULTIMON_DEVICE pDevice;
  1879.     RECT             rcPrimary;
  1880.     HCURSOR hcur;
  1881.     _InSetInfo = 1;
  1882.     hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  1883.     //
  1884.     // Reset all the data so we can reinitialize the applet.
  1885.     //
  1886.     {
  1887.         ComboBox_ResetContent(_hwndList);
  1888.         SetRectEmpty(&_rcDesk);
  1889.         while (hwndC = GetWindow(_hwndDesk, GW_CHILD))
  1890.         {
  1891.             RemoveTrackingToolTip(hwndC);
  1892.             RemovePopupToolTip(hwndC);
  1893.             DestroyWindow(hwndC);
  1894.         }
  1895.         ShowWindow(_hwndDesk, SW_HIDE);
  1896.         if (_himl != NULL)
  1897.         {
  1898.             ImageList_Destroy(_himl);
  1899.             _himl = NULL;
  1900.         }
  1901.         //
  1902.         // Clear out all the devices.
  1903.         //
  1904.         for (ULONG iDevice = 0; iDevice < _NumDevices; iDevice++) {
  1905.             pDevice = _Devices + iDevice;
  1906.             _DestroyMultimonDevice(pDevice); 
  1907.             ZeroMemory(pDevice, sizeof(MULTIMON_DEVICE));
  1908.         }
  1909.         ZeroMemory(_Devices + _NumDevices,
  1910.                    sizeof(_Devices) - sizeof(MULTIMON_DEVICE) * _NumDevices);
  1911.         _NumDevices = 0;
  1912.     }
  1913.     //
  1914.     // Enumerate all the devices in the system.
  1915.     //
  1916.     // Note: This function computes the _NumDevices.
  1917.     _EnumerateAllDisplayDevices();
  1918.     
  1919.     if (_NumDevices == 0)
  1920.     {
  1921.         ASSERT(0);
  1922.         return FALSE;
  1923.     }
  1924.     //
  1925.     // Because we are getting the registry values, the current state of
  1926.     // the registry may be inconsistent with that of the system:
  1927.     //
  1928.     // EmumDisplayDevices will return the active primary in the
  1929.     // system, which may be different than the actual primary marked in the
  1930.     // registry
  1931.     //
  1932.     BOOL bTmpDevicePrimary  = FALSE;
  1933.     BOOL bBestDevicePrimary = FALSE;
  1934.     ULONG iDevice;
  1935.     _pPrimaryDevice = NULL;
  1936.     for (iDevice = 0; iDevice < _NumDevices; iDevice++)
  1937.     {
  1938.         //
  1939.         // First, we can pick any monitor that is attached as the primary.
  1940.         //
  1941.         if (_Devices[iDevice].pds->IsAttached())
  1942.         {
  1943.             if (_pPrimaryDevice == NULL)
  1944.             {
  1945.                 _pPrimaryDevice = &_Devices[iDevice];
  1946.                 TraceMsg(TF_GENERAL, "InitDisplaySettings: primary found %dn", iDevice);
  1947.             }
  1948.             //
  1949.             // If the DISPLAY_DEVICE structure tells us this is the primary,
  1950.             // Pick this one.
  1951.             //
  1952.             if (_Devices[iDevice].DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
  1953.             {
  1954.                 if (bTmpDevicePrimary)
  1955.                 {
  1956.                     ASSERT(FALSE);
  1957.                 }
  1958.                 else
  1959.                 {
  1960.                     _pPrimaryDevice = &_Devices[iDevice];
  1961.                     bTmpDevicePrimary = TRUE;
  1962.                     TraceMsg(TF_GENERAL, "InitDisplaySettings: Tmp DEVICE_PRIMARY found %d", iDevice);
  1963.                 }
  1964.                 //
  1965.                 // Check that the position should really be 0,0
  1966.                 //
  1967.                 RECT pos;
  1968.                 _Devices[iDevice].pds->GetCurPosition(&pos);
  1969.                 if ((pos.left == 0) &&
  1970.                     (pos.top == 0))
  1971.                 {
  1972.                     _pPrimaryDevice = &_Devices[iDevice];
  1973.                     TraceMsg(TF_GENERAL, "InitDisplaySettings: Best DEVICE_PRIMARY found %d", iDevice);
  1974.                 }
  1975.                 else
  1976.                 {
  1977.                     ASSERT(FALSE);
  1978.                     TraceMsg(TF_GENERAL, "InitDisplaySettings: PRIMARY is not at 0,0");
  1979.                 }
  1980.             }
  1981.         }
  1982.     }
  1983.     if (_pPrimaryDevice == NULL)
  1984.     {
  1985.         ASSERT(FALSE);
  1986.         TraceMsg(TF_GENERAL, "InitDisplaySettings: NO Attached devices !!!");
  1987.         //
  1988.         // We must be running setup - pick the first device as the primary.
  1989.         //
  1990.         _pPrimaryDevice = &_Devices[0];
  1991.     }
  1992.     _pCurDevice = _pPrimaryDevice;
  1993.     //
  1994.     // Reset the primary's variables to make sure it is a properly formated
  1995.     // primary entry.
  1996.     //
  1997.     SetMonAttached(_pPrimaryDevice, TRUE, TRUE, NULL);
  1998.     SetPrimary(_pPrimaryDevice);
  1999.     _pPrimaryDevice->pds->GetCurPosition(&rcPrimary);
  2000. #ifndef WINNT
  2001.     if (!lstrcmpi((LPTSTR)_pPrimaryDevice->DisplayDevice.DeviceString,TEXT("Standard VGA")))
  2002.         _bNoAttach = TRUE;
  2003. #endif
  2004.     //
  2005.     // compute the max image size needed for a monitor bitmap
  2006.     //
  2007.     // NOTE this must be the max size the images will *ever*
  2008.     // be we cant just take the current max size.
  2009.     // we use the client window size, a child monitor cant be larger than this.
  2010.     //
  2011.     RECT rcDesk;
  2012.     GetClientRect(_hwndDesk, &rcDesk);
  2013.     int cxImage = rcDesk.right;
  2014.     int cyImage = rcDesk.bottom;
  2015.     //
  2016.     // Create a temporary monitor bitmap 
  2017.     //
  2018.     HBITMAP hbm = NULL;
  2019.     MakeMonitorBitmap(cxImage, cyImage, NULL, &hbm, NULL, cxImage, cyImage, FALSE);
  2020.     //
  2021.     // Go through all the devices one last time to create the windows
  2022.     //
  2023.     for (iDevice = 0; iDevice < _NumDevices; iDevice++)
  2024.     {
  2025.         TCHAR szDisplay[256];
  2026.         pDevice = &_Devices[iDevice];
  2027.         MonitorData md = {0};
  2028.         RECT rcPos;
  2029.         LPVOID pWindowData = (LPVOID)this;
  2030.         pDevice->DisplayIndex = iDevice + 1;
  2031.         _GetDisplayName(pDevice, szDisplay, ARRAYSIZE(szDisplay));
  2032.         iItem = ComboBox_AddString(_hwndList, szDisplay);
  2033.         pDevice->ComboBoxItem = iItem;
  2034.         ComboBox_SetItemData(_hwndList,
  2035.                              iItem,
  2036.                              (DWORD_PTR)pDevice);
  2037.         //
  2038.         // If the monitor is part of the desktop, show it on the screen
  2039.         // otherwise keep it invisible.
  2040.         //
  2041.         wsprintf(ach, TEXT("%d"), iDevice + 1);