multimon.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 99k
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-1996 All Rights Reserved
  7.  *
  8.  * NOTES:
  9.  *
  10.  * History: Create by toddla -- multimon.c
  11.  *          Changed to C++ by dli -- multimon.cpp on 7/10/1997
  12.  *
  13.  **************************************************************************/
  14. #include "precomp.h"
  15. #include "shlobjp.h"
  16. #include "shlwapi.h"
  17. #define COMPILE_MULTIMON_STUBS
  18. #include "multimon.h"
  19. #include "device.hxx"
  20. #include "settings.hxx"
  21. extern "C" {
  22.     LONG CALLBACK MonitorWindowProc(HWND hwnd, UINT msg,WPARAM wParam,LPARAM lParam);
  23.     extern BOOL g_bGradient;  // from lookdlg.c
  24. }
  25. #define MONITORS_MAX    10
  26. #define PREVIEWAREARATIO 2
  27. #ifdef WINNT
  28. // Global configuration variables 
  29. ULONG gUnattenedConfigureAtLogon = 0;
  30. ULONG gUnattenedInstall = 0;
  31. ULONG gUnattenedBitsPerPel = 0;
  32. ULONG gUnattenedXResolution = 0;
  33. ULONG gUnattenedYResolution = 0;
  34. ULONG gUnattenedVRefresh = 0;
  35. ULONG gUnattenedFlags = 0;
  36. ULONG gUnattenedAutoConfirm = 0;
  37. #endif
  38. static const TCHAR sc_szVideo[] = TEXT("Video");
  39. static const TCHAR sc_szOptimize[] = TEXT("Optimize");
  40. static const TCHAR sc_szQtwIni[] = TEXT("qtw.ini");
  41. static const TCHAR sc_szVGA[] = TEXT("Standard VGA");
  42. //
  43. // display devices
  44. //
  45. typedef struct _multimon_device {
  46.     ULONG          ComboBoxItem;
  47.     BOOL           bRepositionable;
  48.     DISPLAY_DEVICE DisplayDevice;
  49.     DEVMODE        DisplaySetting;
  50.     ULONG          DisplayIndex;
  51.     TCHAR          DisplayRegKey[128];
  52.     CDeviceSettings * pds;
  53.     int            w,h;
  54.     HIMAGELIST     himl;
  55.     int            iImage;
  56.     POINT          Snap;
  57.     HDC            hdc;
  58. } MULTIMON_DEVICE, *PMULTIMON_DEVICE;
  59. extern HWND ghwndPropSheet;
  60. extern int AskDynaCDS(HWND hDlg);
  61. extern BOOL WarnUserAboutCompatibility(HWND hDlg);
  62. //-----------------------------------------------------------------------------
  63. static const DWORD sc_MultiMonitorHelpIds[] =
  64. {
  65.    IDC_DISPLAYDESK,   IDH_SETTINGS_DISPLAYDESK, 
  66.    IDC_DISPLAYLIST,   IDH_SETTINGS_DISPLAYLIST, 
  67.    IDC_COLORBOX,      IDH_SETTINGS_COLORBOX,
  68.    IDC_SCREENSIZE,    IDH_SETTINGS_SCREENSIZE,
  69.    IDC_DISPLAYUSEME,  IDH_SETTINGS_DISPLAYUSEME, 
  70.    IDC_DISPLAYPROPERTIES, IDH_SETTINGS_DISPLAYPROPERTIES, 
  71.    0, 0
  72. };
  73. class CMultiMon  : public IMultiMonConfig
  74. {
  75.     private:
  76.         // Data Section
  77.         MULTIMON_DEVICE _Devices[MONITORS_MAX];
  78.         PMULTIMON_DEVICE _pCurDevice;
  79.         PMULTIMON_DEVICE _pPrimaryDevice;
  80.         // HWND for the main window
  81.         HWND _hDlg;
  82.         HWND _hwndDesk;
  83.         HWND _hwndList;
  84.         
  85.         // union of all monitor RECTs
  86.         RECT _rcDesk;
  87.         // ref count 
  88.         UINT _cRef;
  89.         
  90.         // how to translate to preview size
  91.         int   _DeskScale;
  92.         POINT _DeskOff;
  93.         UINT  _InSetInfo;
  94.         int   _NumDevices;
  95.         HBITMAP _hbmScrSample;
  96.         HBITMAP _hbmMonitor;
  97.         HIMAGELIST _himl;
  98.         
  99.         BOOL _bBadDriver         : 1;
  100.         BOOL _bNewDriver         : 1;
  101.         BOOL _bNoAttach          : 1;
  102.         BOOL _bDirty             : 1;
  103.         
  104.         // Private functions 
  105.         void _DeskToPreview(LPRECT in, LPRECT out);
  106.         void _OffsetPreviewToDesk(LPRECT in, LPRECT out);
  107.         BOOL _AnyColorChange();
  108.         BOOL _AnyResolutionChange();
  109.         BOOL _QueryForceSmallFont();
  110.         void _SetPreviewScreenSize(int HRes, int VRes, int iOrgXRes, int iOrgYRes);
  111.         void _CleanupRects(HWND hwndP);
  112.         BOOL _WriteMultiMonProfile(); 
  113.         void _SetReposition(HWND hwnd, PMULTIMON_DEVICE pDevice);
  114.         void _DoAdvancedSettingsSheet();
  115.         void _VerifyPrimaryMode(BOOL fKeepMode);
  116.         BOOL _HandleApply();
  117.         BOOL _HandleHScroll(HWND hwndSB, int iCode, int iPos); 
  118.         void _RedrawDeskPreviews();
  119.         void _ForwardToChildren(UINT msg, WPARAM wParam, LPARAM lParam);
  120.         BOOL _InitDisplaySettings(BOOL bExport);
  121.         void _DestroyDisplaySettings();
  122.         int  _GetMinColorBits();
  123.         void _GetDisplayName(PMULTIMON_DEVICE pDevice, LPTSTR pszDisplay);
  124.         int  _SaveDisplaySettings(DWORD dwSet);
  125.         void _RestoreDisplaySettings();
  126.         void _ConfirmDisplaySettingsChange();
  127.         BOOL _TestNewSettingsChange();
  128.         BOOL _RebuildDisplaySettings(BOOL bComplete = FALSE);
  129.         // NT specific stuff
  130. #ifdef WINNT
  131.         BOOL _InitMessage();
  132.         void _vPreExecMode();
  133.         void _vPostExecMode();
  134. #endif
  135.     public:
  136.         CMultiMon();
  137.         static BOOL RegisterPreviewWindowClass(WNDPROC pfnWndProc);
  138.         // *** IUnknown methods *** 
  139.         STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
  140.         STDMETHODIMP_(ULONG) AddRef(void);
  141.         STDMETHODIMP_(ULONG) Release(void);    
  142.         // *** IMultiMonConfig methods ***
  143.         STDMETHOD ( Initialize ) ( HWND hwndHost, WNDPROC pfnWndProc, DWORD dwReserved);
  144.         STDMETHOD ( GetNumberOfMonitors ) (int * pCMon, DWORD dwReserved);
  145.         STDMETHOD ( GetMonitorData) (int iMonitor, MonitorData * pmd, DWORD dwReserved);
  146.         STDMETHOD ( Paint) (THIS_ int iMonitor, DWORD dwReserved);     
  147.         void InitMultiMonitorDlg(HWND hDlg);
  148.         PMULTIMON_DEVICE GetCurDevice(){return _pCurDevice;};
  149.         
  150.         int  GetNumberOfAttachedDisplays();
  151.         BOOL ToggleAttached(PMULTIMON_DEVICE pDevice, BOOL bPrime);
  152.         void UpdateActiveDisplay(PMULTIMON_DEVICE pDevice);
  153.         BOOL HandleMonitorChange(HWND hwndP, BOOL bMainDlg);
  154.         void SetDirty(BOOL bDirty=TRUE);
  155.         HWND GetCurDeviceHwnd() { return GetDlgItem(_hwndDesk, (int) _pCurDevice);};
  156.         int  GetNumDevices() { return _NumDevices;};
  157.         BOOL QueryNoAttach() { return _bNoAttach;};
  158.         BOOL IsDirty()       { return _bDirty;};
  159.         LRESULT CALLBACK WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
  160. };
  161. CMultiMon::CMultiMon() : _cRef(1) {
  162.     ASSERT(_pCurDevice == NULL);
  163.     ASSERT(_pPrimaryDevice == NULL);
  164.     ASSERT(_DeskScale == 0);    
  165.     ASSERT(_InSetInfo == 0);
  166.     ASSERT(IsRectEmpty(&_rcDesk));
  167.     ASSERT(_bBadDriver == FALSE);
  168.     ASSERT(_bNewDriver == FALSE);
  169.     ASSERT(_bNoAttach == FALSE);
  170.     ASSERT(_bDirty == FALSE);
  171. };
  172. /*BUGBUG: (dli) per my talk with AndreVa, the unattended configuration stuff are NT only
  173.        and I don't have to do it. I tried my best to preserve the code and make them work
  174.        but they still may not work. 
  175.        
  176. */
  177. void CMultiMon::_DestroyDisplaySettings()
  178. {
  179.     int iDevice;
  180.     ASSERT(_NumDevices);
  181.     TraceMsg(TF_GENERAL, "DestroyDisplaySettings: %s devices", _NumDevices);
  182.     for (iDevice = 0; _Devices[iDevice].DisplayDevice.cb != 0; iDevice++)
  183.     {
  184.         ASSERT(_Devices[iDevice].pds);
  185.         delete _Devices[iDevice].pds;
  186.         if (_Devices[iDevice].hdc)
  187.         {
  188.             DeleteDC(_Devices[iDevice].hdc);
  189.             _Devices[iDevice].hdc = NULL;
  190.         }
  191.     }
  192.     if (_himl)
  193.     {
  194.         ImageList_Destroy(_himl);
  195.         _himl = NULL;
  196.     }
  197.     TraceMsg(TF_GENERAL, "DestroyDisplaySettings: Finished destroying all devices");
  198. }
  199. #ifdef WINNT  
  200. //
  201. // deterines if the applet is in detect mode.
  202. //
  203. //
  204. // Called to put up initial messages that need to appear above the dialog
  205. // box
  206. //
  207. BOOL CMultiMon::_InitMessage()
  208. {
  209.     //
  210.     // If configure at logon is set, then we don't want to do anything
  211.     //
  212.     if (gUnattenedConfigureAtLogon)
  213.     {
  214.         PropSheet_PressButton(ghwndPropSheet, PSBTN_CANCEL);
  215.     }
  216.     else if (gUnattenedAutoConfirm)
  217.     {
  218.         PropSheet_PressButton(ghwndPropSheet, PSBTN_OK);
  219.     }
  220.     else
  221.     {
  222.         //
  223.         // _bBadDriver will be set when we fail to build the list of modes,
  224.         // or something else failed during initialization.
  225.         //
  226.         // In almost every case, we should already know about this situation
  227.         // based on our boot code.
  228.         // However, if this is a new situation, just report a "bad driver"
  229.         //
  230.         if (_bBadDriver)
  231.         {
  232.             ASSERT(gbExecMode == EXEC_INVALID_MODE);
  233.             gbExecMode = EXEC_INVALID_MODE;
  234.             gbInvalidMode = EXEC_INVALID_DISPLAY_DRIVER;
  235.         }
  236.         if (gbExecMode == EXEC_INVALID_MODE)
  237.         {
  238.             DWORD Mesg;
  239.             switch(gbInvalidMode) {
  240.             case EXEC_INVALID_NEW_DRIVER:
  241.                 Mesg = MSG_INVALID_NEW_DRIVER;
  242.                 break;
  243.             case EXEC_INVALID_DEFAULT_DISPLAY_MODE:
  244.                 Mesg = MSG_INVALID_DEFAULT_DISPLAY_MODE;
  245.                 break;
  246.             case EXEC_INVALID_DISPLAY_DRIVER:
  247.                 Mesg = MSG_INVALID_DISPLAY_DRIVER;
  248.                 break;
  249.             case EXEC_INVALID_OLD_DISPLAY_DRIVER:
  250.                 Mesg = MSG_INVALID_OLD_DISPLAY_DRIVER;
  251.                 break;
  252.             case EXEC_INVALID_16COLOR_DISPLAY_MODE:
  253.                 Mesg = MSG_INVALID_16COLOR_DISPLAY_MODE;
  254.                 break;
  255.             case EXEC_INVALID_DISPLAY_MODE:
  256.                 Mesg = MSG_INVALID_DISPLAY_MODE;
  257.                 break;
  258.             case EXEC_INVALID_CONFIGURATION:
  259.             default:
  260.                 Mesg = MSG_INVALID_CONFIGURATION;
  261.                 break;
  262.             }
  263.             FmtMessageBox(_hDlg,
  264.                           MB_ICONEXCLAMATION,
  265.                           FALSE,
  266.                           MSG_CONFIGURATION_PROBLEM,
  267.                           Mesg);
  268.             //
  269.             // For a bad display driver or old display driver, let's send the
  270.             // user straight to the installation dialog.
  271.             //
  272.             if ((gbInvalidMode == EXEC_INVALID_OLD_DISPLAY_DRIVER) ||
  273.                 (gbInvalidMode == EXEC_INVALID_DISPLAY_DRIVER))
  274.             {
  275.                 BOOL unused;
  276.                 //BUGBUG: (dli) _pCurDevice is the best guess.
  277.                 if (InstallNewDriver(_hDlg,
  278.                                      (LPCTSTR)_pCurDevice->DisplayDevice.DeviceString[0],
  279.                                      &unused) == NO_ERROR)
  280.                 {
  281.                     //
  282.                     // Set this flag so that we don't get a message about
  283.                     // having an untested mode.
  284.                     //
  285.                     _bNewDriver = TRUE;
  286.                     //
  287.                     // Let's leave the applet so the user sees the reboot
  288.                     // popup.
  289.                     //
  290.                     PropSheet_PressButton(ghwndPropSheet, PSBTN_OK);
  291.                 }
  292.             }
  293.         }
  294.     }
  295.     return TRUE;
  296. }
  297. VOID CMultiMon::_vPreExecMode()
  298. {
  299.     HKEY hkey;
  300.     DWORD cb;
  301. //    DWORD data;
  302.     //
  303.     // This function sets up the execution mode of the applet.
  304.     // There are four vlid modes.
  305.     //
  306.     // EXEC_NORMAL - When the apple is launched from the control panel
  307.     //
  308.     // EXEC_INVALID_MODE is exactly the same as for NORMAL except we will
  309.     //                   not mark the current mode as tested so the user has
  310.     //                   to at least test a mode
  311.     //
  312.     // EXEC_DETECT - When the applet is launched normally, but a detect was
  313.     //               done on the previous boot (the key in the registry is
  314.     //               set)
  315.     //
  316.     // EXEC_SETUP  - When we launch the applet in setup mode from setup (Both
  317.     //               the registry key is set and the setup flag is passed in).
  318.     //
  319.     //
  320.     // These two keys should only be checked  deleted if the machine has been
  321.     // rebooted and the detect  new display has actually happened.
  322.     // So we will look for the RebootNecessary key (a volatile key) and if
  323.     // it is not present, then we can delete the key.  Otherwise, the reboot
  324.     // has not happened, and we keep the key
  325.     //
  326.     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  327.                      SZ_REBOOT_NECESSARY,
  328.                      0,
  329.                      KEY_READ | KEY_WRITE,
  330.                      &hkey) != ERROR_SUCCESS) {
  331.         if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  332.                          SZ_DETECT_DISPLAY,
  333.                          0,
  334.                          KEY_READ | KEY_WRITE,
  335.                          &hkey) == ERROR_SUCCESS) {
  336.             //
  337.             // NOTE: This key is also set when EXEC_SETUP is being run.
  338.             //
  339.             if (gbExecMode == EXEC_NORMAL) {
  340.                 gbExecMode = EXEC_DETECT;
  341.             } else {
  342.                 //
  343.                 // If we are in setup mode, we also check the extra values
  344.                 // under DetectDisplay that control the unattended installation.
  345.                 //
  346.                 ASSERT(gbExecMode == EXEC_SETUP);
  347.                 cb = 4;
  348.                 if (RegQueryValueEx(hkey,
  349.                                     SZ_CONFIGURE_AT_LOGON,
  350.                                     NULL,
  351.                                     NULL,
  352.                                     (LPBYTE) &gUnattenedConfigureAtLogon,
  353.                                     &cb) == ERROR_SUCCESS) {
  354.                     //
  355.                     // We delete only this value since the other values must remain
  356.                     // until the next boot
  357.                     //
  358.                     RegDeleteValue(hkey,
  359.                                    SZ_CONFIGURE_AT_LOGON);
  360.                 }
  361.                 if (gUnattenedConfigureAtLogon == 0)
  362.                 {
  363.                     cb = 4;
  364.                     RegQueryValueEx(hkey,
  365.                                     SZ_UNATTEND_INSTALL,
  366.                                     NULL,
  367.                                     NULL,
  368.                                     (LPBYTE) &gUnattenedInstall,
  369.                                     &cb);
  370.                     cb = 4;
  371.                     RegQueryValueEx(hkey,
  372.                                     SZ_UNATTEND_BPP,
  373.                                     NULL,
  374.                                     NULL,
  375.                                     (LPBYTE) &gUnattenedBitsPerPel,
  376.                                     &cb);
  377.                     cb = 4;
  378.                     RegQueryValueEx(hkey,
  379.                                     SZ_UNATTEND_X,
  380.                                     NULL,
  381.                                     NULL,
  382.                                     (LPBYTE) &gUnattenedXResolution,
  383.                                     &cb);
  384.                     cb = 4;
  385.                     RegQueryValueEx(hkey,
  386.                                     SZ_UNATTEND_Y,
  387.                                     NULL,
  388.                                     NULL,
  389.                                     (LPBYTE) &gUnattenedYResolution,
  390.                                     &cb);
  391.                     cb = 4;
  392.                     RegQueryValueEx(hkey,
  393.                                     SZ_UNATTEND_REF,
  394.                                     NULL,
  395.                                     NULL,
  396.                                     (LPBYTE) &gUnattenedVRefresh,
  397.                                     &cb);
  398.                     cb = 4;
  399.                     RegQueryValueEx(hkey,
  400.                                     SZ_UNATTEND_FLAGS,
  401.                                     NULL,
  402.                                     NULL,
  403.                                     (LPBYTE) &gUnattenedFlags,
  404.                                     &cb);
  405.                     cb = 4;
  406.                     RegQueryValueEx(hkey,
  407.                                     SZ_UNATTEND_CONFIRM,
  408.                                     NULL,
  409.                                     NULL,
  410.                                     (LPBYTE) &gUnattenedAutoConfirm,
  411.                                     &cb);
  412.                 }
  413.             }
  414.             RegCloseKey(hkey);
  415.         }
  416.         //
  417.         // Check for a new driver being installed
  418.         //
  419.         if ( (gbExecMode == EXEC_NORMAL) &&
  420.              (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  421.                            SZ_NEW_DISPLAY,
  422.                            0,
  423.                            KEY_READ | KEY_WRITE,
  424.                            &hkey) == ERROR_SUCCESS) ) {
  425.             gbExecMode = EXEC_INVALID_MODE;
  426.             gbInvalidMode = EXEC_INVALID_NEW_DRIVER;
  427.             RegCloseKey(hkey);
  428.         }
  429.         RegDeleteKey(HKEY_LOCAL_MACHINE,
  430.                      SZ_DETECT_DISPLAY);
  431.         RegDeleteKey(HKEY_LOCAL_MACHINE,
  432.                      SZ_NEW_DISPLAY);
  433.     }
  434.     {
  435.         LPTSTR psz;
  436.         LPTSTR pszInv;
  437.         switch(gbExecMode) {
  438.             case EXEC_NORMAL:
  439.                 psz = TEXT("Normal Execution mode");
  440.                 break;
  441.             case EXEC_DETECT:
  442.                 psz = TEXT("Detection Execution mode");
  443.                 break;
  444.             case EXEC_SETUP:
  445.                 psz = TEXT("Setup Execution mode");
  446.                 break;
  447.             case EXEC_INVALID_MODE:
  448.                 psz = TEXT("Invalid Mode Execution mode");
  449.                 switch(gbInvalidMode) {
  450.                     case EXEC_INVALID_NEW_DRIVER:
  451.                         pszInv = TEXT("Invalid new driver");
  452.                         break;
  453.                     default:
  454.                         pszInv = TEXT("*** Invalid *** Invalid mode");
  455.                         break;
  456.                 }
  457.                 break;
  458.             default:
  459.                 psz = TEXT("*** Invalid *** Execution mode");
  460.                 break;
  461.         }
  462.         KdPrint(("n nDisplay.cpl: The display applet is in : %wsn", psz));
  463.         if (gbExecMode == EXEC_INVALID_MODE)
  464.         {
  465.             KdPrint(("tt sub invalid mode : %ws", pszInv));
  466.         }
  467.         KdPrint(("nn", psz));
  468.     }
  469. }
  470. VOID CMultiMon::_vPostExecMode() {
  471.     HKEY hkey;
  472.     DWORD cb;
  473.     DWORD data;
  474.     //
  475.     // Check for various invalid configurations
  476.     //
  477.     if ( (gbExecMode == EXEC_NORMAL) &&
  478.          (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  479.                        SZ_INVALID_DISPLAY,
  480.                        0,
  481.                        KEY_READ | KEY_WRITE,
  482.                        &hkey) == ERROR_SUCCESS) ) {
  483.         gbExecMode = EXEC_INVALID_MODE;
  484.         //
  485.         // Check for these fields in increasing order of "badness" or
  486.         // "detail" so that the *worst* error is the one remaining in the
  487.         // gbInvalidMode  variable once all the checks are done.
  488.         //
  489.         cb = 4;
  490.         if (RegQueryValueEx(hkey,
  491.                             TEXT("DefaultMode"),
  492.                             NULL,
  493.                             NULL,
  494.                             (LPBYTE)(&data),
  495.                             &cb) == ERROR_SUCCESS)
  496.         {
  497.             gbInvalidMode = EXEC_INVALID_DEFAULT_DISPLAY_MODE;
  498.         }
  499.         cb = 4;
  500.         if (RegQueryValueEx(hkey,
  501.                             TEXT("BadMode"),
  502.                             NULL,
  503.                             NULL,
  504.                             (LPBYTE)(&data),
  505.                             &cb) == ERROR_SUCCESS)
  506.         {
  507.             gbInvalidMode = EXEC_INVALID_DISPLAY_MODE;
  508.         }
  509.         cb = 4;
  510.         if (RegQueryValueEx(hkey,
  511.                             TEXT("16ColorMode"),
  512.                             NULL,
  513.                             NULL,
  514.                             (LPBYTE)(&data),
  515.                             &cb) == ERROR_SUCCESS)
  516.         {
  517.             gbInvalidMode = EXEC_INVALID_16COLOR_DISPLAY_MODE;
  518.         }
  519.         cb = 4;
  520.         if (RegQueryValueEx(hkey,
  521.                             TEXT("InvalidConfiguration"),
  522.                             NULL,
  523.                             NULL,
  524.                             (LPBYTE)(&data),
  525.                             &cb) == ERROR_SUCCESS)
  526.         {
  527.             gbInvalidMode = EXEC_INVALID_CONFIGURATION;
  528.         }
  529.         cb = 4;
  530.         if (RegQueryValueEx(hkey,
  531.                             TEXT("MissingDisplayDriver"),
  532.                             NULL,
  533.                             NULL,
  534.                             (LPBYTE)(&data),
  535.                             &cb) == ERROR_SUCCESS)
  536.         {
  537.             gbInvalidMode = EXEC_INVALID_DISPLAY_DRIVER;
  538.         }
  539.         //
  540.         // This last case will be set in addition to the previous one in the
  541.         // case where the driver was an old driver linking to winsvr.dll
  542.         // and we can not load it.
  543.         //
  544.         cb = 4;
  545.         if (RegQueryValueEx(hkey,
  546.                             TEXT("OldDisplayDriver"),
  547.                             NULL,
  548.                             NULL,
  549.                             (LPBYTE)(&data),
  550.                             &cb) == ERROR_SUCCESS)
  551.         {
  552.             gbInvalidMode = EXEC_INVALID_OLD_DISPLAY_DRIVER;
  553.         }
  554.         RegCloseKey(hkey);
  555.     }
  556.     //
  557.     // Delete all of these bad configuration keys since we only want the
  558.     // user to see the message once.
  559.     //
  560.     RegDeleteKey(HKEY_LOCAL_MACHINE,
  561.                  SZ_INVALID_DISPLAY);
  562. {
  563.     LPTSTR psz;
  564.     LPTSTR pszInv;
  565.     if (gbExecMode == EXEC_INVALID_MODE)
  566.     {
  567.         switch (gbInvalidMode)
  568.         {
  569.         case EXEC_INVALID_DEFAULT_DISPLAY_MODE:
  570.             pszInv = TEXT("Default mode being used");
  571.             break;
  572.         case EXEC_INVALID_DISPLAY_DRIVER:
  573.             pszInv = TEXT("Invalid Display Driver");
  574.             break;
  575.         case EXEC_INVALID_OLD_DISPLAY_DRIVER:
  576.             pszInv = TEXT("Old Display Driver");
  577.             break;
  578.         case EXEC_INVALID_16COLOR_DISPLAY_MODE:
  579.             pszInv = TEXT("16 color mode not supported");
  580.             break;
  581.         case EXEC_INVALID_DISPLAY_MODE:
  582.             pszInv = TEXT("Invalid display mode");
  583.             break;
  584.         case EXEC_INVALID_CONFIGURATION:
  585.             pszInv = TEXT("Invalid configuration");
  586.             break;
  587.         default:
  588.             psz = TEXT("*** Invalid *** Invalid mode");
  589.             break;
  590.         }
  591.         KdPrint(("tt sub invlid mode : %ws", pszInv));
  592.         KdPrint(("nn", psz));
  593.     }
  594. }
  595. }
  596. #endif
  597. //-----------------------------------------------------------------------------
  598. //-----------------------------------------------------------------------------
  599. void CMultiMon::_DeskToPreview(LPRECT in, LPRECT out)
  600. {
  601.     out->left   = _DeskOff.x + MulDiv(in->left   - _rcDesk.left,_DeskScale,1000);
  602.     out->top    = _DeskOff.y + MulDiv(in->top    - _rcDesk.top, _DeskScale,1000);
  603.     out->right  = _DeskOff.x + MulDiv(in->right  - _rcDesk.left,_DeskScale,1000);
  604.     out->bottom = _DeskOff.y + MulDiv(in->bottom - _rcDesk.top, _DeskScale,1000);
  605. }
  606. //-----------------------------------------------------------------------------
  607. //-----------------------------------------------------------------------------
  608. void CMultiMon::_OffsetPreviewToDesk(LPRECT in, LPRECT out)
  609. {
  610.     int x, y;
  611.     // Scale preview rects back to desk size
  612.     x = _rcDesk.left + MulDiv(in->left - _DeskOff.x,1000,_DeskScale);
  613.     y = _rcDesk.top  + MulDiv(in->top  - _DeskOff.y,1000,_DeskScale);
  614.     // Figure out how much to offset
  615.     x = x - out->left;
  616.     y = y - out->top;
  617.     OffsetRect(out, x, y);
  618. }
  619. void CMultiMon::_SetReposition(HWND hwnd, PMULTIMON_DEVICE pDevice)
  620. {
  621.     //
  622.     // Try to open the device with the new settings.
  623.     // If it works, we get the reposition flag, otherwise we assume
  624.     // the paramters are wrong and we fail (since the rectangle will
  625.     // not be updated).
  626.     //
  627. #ifdef WINNT
  628.     HDC hdc;
  629.     if (pDevice->bRepositionable == FALSE)
  630.     {
  631.         hdc = CreateDC(NULL,
  632.                        pDevice->DisplayDevice.DeviceName,
  633.                        NULL,
  634.                        &pDevice->DisplaySetting);
  635.         if (hdc == NULL)
  636.         {
  637.             //
  638.             // Device may be attached - try with a NULL DEVMODE.
  639.             //
  640.             hdc = CreateDC(NULL,
  641.                            pDevice->DisplayDevice.DeviceName,
  642.                            NULL,
  643.                            NULL);
  644.         }
  645.         if (hdc)
  646.         {
  647.             pDevice->bRepositionable = TRUE;
  648.             // DLI: make it Compile GetDeviceCaps(hdc, RASTERCAPS) & RC_REPOSITIONABLE;
  649.             DeleteDC(hdc);
  650.         }
  651.     }
  652. #else
  653.     //
  654.     // For WIN95, any device can be relocated.
  655.     //
  656.     pDevice->bRepositionable = TRUE;
  657. #endif
  658. }
  659. int CMultiMon::_GetMinColorBits()
  660. {
  661.     
  662.     int     iMinColorBits = 64; // This is the max possible
  663.     int     iDevice;
  664.     for (iDevice = 0; _Devices[iDevice].DisplayDevice.cb != 0; iDevice++)
  665.     {
  666.         int iColorBits = _Devices[iDevice].pds->GetColorBits();
  667.         if (iColorBits < iMinColorBits)
  668.             iMinColorBits = iColorBits;
  669.     }
  670.     return iMinColorBits;
  671. }
  672. //-----------------------------------------------------------------------------
  673. int CMultiMon::_SaveDisplaySettings(DWORD dwSet)
  674. {
  675.     int     iRet = 0;
  676.     int     iDevice;
  677.     HWND    hwndC;
  678.     _VerifyPrimaryMode(FALSE);
  679.     for (iDevice = 0; _Devices[iDevice].DisplayDevice.cb != 0; iDevice++)
  680.     {
  681.         int iResult = _Devices[iDevice].pds->SaveSettings(dwSet);
  682.         if (iResult != DISP_CHANGE_SUCCESSFUL)
  683.         {
  684.             if (iResult == DISP_CHANGE_RESTART)
  685.             {
  686.                 iRet = iResult;
  687.                 continue;
  688.             }
  689.             else
  690.             {
  691.                 ASSERT(iResult < 0);
  692.                 return iResult;
  693.             }
  694.         }
  695.     }
  696.     // If we get here, the above functions are successful or requires restart
  697.     // This ChangeDisplaySettings call will actually go refresh the whole desktop
  698.     // Of course we don't want to do it at a test. 
  699.     if (!(dwSet & CDS_TEST))
  700.     {
  701.         if (_GetMinColorBits() > 8)
  702.             g_bGradient = TRUE;
  703.         else
  704.             g_bGradient = FALSE;
  705.         SystemParametersInfo(SPI_SETGRADIENTCAPTIONS, 0, (LPVOID)g_bGradient, SPIF_UPDATEINIFILE);
  706.         
  707.         hwndC = CreateCoverWindow(COVER_NOPAINT);
  708.         ChangeDisplaySettings(NULL, NULL);
  709.         DestroyCoverWindow(hwndC);
  710.     }
  711.     return iRet;
  712. }
  713. void CMultiMon::_RestoreDisplaySettings()
  714. {
  715.     int     iDevice;
  716.     HWND    hwndC;
  717.     for (iDevice = 0; _Devices[iDevice].DisplayDevice.cb != 0; iDevice++)
  718.         _Devices[iDevice].pds->RestoreSettings();
  719.     
  720.     //DLI: This last function call will actually go refresh the whole desktop
  721.     hwndC = CreateCoverWindow(COVER_NOPAINT);
  722.     ChangeDisplaySettings(NULL, NULL);
  723.     DestroyCoverWindow(hwndC);
  724. }
  725. void CMultiMon::_ConfirmDisplaySettingsChange()
  726. {
  727.     int iDevice;
  728.     for (iDevice = 0; _Devices[iDevice].DisplayDevice.cb != 0; iDevice++)
  729.         _Devices[iDevice].pds->ConfirmChangeSettings();
  730.     if (GetNumberOfAttachedDisplays() > 1)
  731.         _WriteMultiMonProfile();
  732. }
  733. BOOL CALLBACK KeepNewDlgProc(HWND hDlg, UINT message , WPARAM wParam, LPARAM lParam)
  734. {
  735.     UINT idTimer = 0;
  736.     HICON hicon;
  737.     switch(message)
  738.     {
  739.         case WM_INITDIALOG:
  740.             hicon = LoadIcon(NULL, IDI_QUESTION);
  741.             if (hicon)
  742.                 SendDlgItemMessage(hDlg, IDC_BIGICON, STM_SETIMAGE, IMAGE_ICON, (DWORD)hicon);
  743.             idTimer = SetTimer(hDlg, 1, 20000, NULL);
  744.             break;
  745.         case WM_DESTROY:
  746.             if (idTimer)
  747.                 KillTimer(hDlg, idTimer);
  748.             hicon = (HICON)SendDlgItemMessage(hDlg, IDC_BIGICON, STM_GETIMAGE, IMAGE_ICON, 0);
  749.             if (hicon)
  750.                 DestroyIcon(hicon);
  751.             break;
  752.         case WM_TIMER:
  753.             EndDialog(hDlg, IDNO);
  754.             break;
  755.         case WM_COMMAND:
  756.             EndDialog(hDlg, wParam);
  757.             break;
  758.         default:
  759.             return FALSE;
  760.     }
  761.     return TRUE;
  762. }
  763. BOOL CMultiMon::_TestNewSettingsChange()
  764. {
  765.     int iAnswer = DialogBox(hInstance, MAKEINTRESOURCE(DLG_KEEPNEW), GetParent(_hDlg),
  766.                             KeepNewDlgProc);
  767.     return (iAnswer == IDYES);
  768. }
  769. BOOL CMultiMon::_RebuildDisplaySettings(BOOL bComplete)
  770. {
  771.     BOOL result = TRUE;
  772.     HCURSOR hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  773.     for (int iDevice = 0; _Devices[iDevice].DisplayDevice.cb != 0; iDevice++)
  774.         if (!_Devices[iDevice].pds->RefreshSettings(bComplete))
  775.             result = FALSE;
  776.     RedrawWindow(_hDlg, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN);
  777.     SetCursor(hcur);
  778.     return result;
  779. }
  780. int CMultiMon::GetNumberOfAttachedDisplays()
  781. {
  782.     int nDisplays = 0;
  783.     int iDevice;
  784.     for (iDevice = 0; _Devices[iDevice].DisplayDevice.cb != 0; iDevice++)
  785.     {
  786.         if (_Devices[iDevice].pds->IsAttached())
  787.             nDisplays++;
  788.     }
  789.     return nDisplays;
  790. }
  791. BOOL CMultiMon::_AnyColorChange()
  792. {
  793.     int iDevice;
  794.     for (iDevice = 0; _Devices[iDevice].DisplayDevice.cb != 0; iDevice++)
  795.     {
  796.         if (_Devices[iDevice].pds->IsAttached() && _Devices[iDevice].pds->IsColorChanged())
  797.             return TRUE;
  798.     }
  799.     return FALSE;
  800. }
  801. BOOL CMultiMon::_AnyResolutionChange()
  802. {
  803.    int iDevice;
  804.    for (iDevice = 0; _Devices[iDevice].DisplayDevice.cb != 0; iDevice++)
  805.    {
  806.        if (_Devices[iDevice].pds->IsAttached() && _Devices[iDevice].pds->IsResolutionChanged())
  807.            return TRUE;
  808.    }
  809.    return FALSE;
  810. }
  811. BOOL CMultiMon::_QueryForceSmallFont()
  812. {
  813.     int iDevice;
  814.     for (iDevice = 0; _Devices[iDevice].DisplayDevice.cb != 0; iDevice++)
  815.     {
  816.         if (_Devices[iDevice].pds->IsSmallFontNecessary())
  817.             return TRUE;
  818.     }
  819.     return FALSE;
  820. }
  821. void CMultiMon::_GetDisplayName(PMULTIMON_DEVICE pDevice, LPTSTR pszDisplay)
  822. {
  823.     TCHAR szMonitor[140];
  824.     TCHAR szDisplayFormat[20];
  825.     LoadString(hInstance, IDS_DISPLAYFORMAT, szDisplayFormat, SIZEOF(szDisplayFormat));
  826.     if (!pDevice->pds->GetMonitorName(szMonitor, NULL) || szMonitor[0] == 0)
  827.         LoadString(hInstance, IDS_UNKNOWNMONITOR, szMonitor, SIZEOF(szMonitor));
  828.         
  829.     wsprintf(pszDisplay, szDisplayFormat, pDevice->DisplayIndex, szMonitor, pDevice->DisplayDevice.DeviceString);
  830. }
  831. //-----------------------------------------------------------------------------
  832. void CMultiMon::_DoAdvancedSettingsSheet()
  833. {
  834. #ifndef WINNT
  835.     HINSTANCE hDesk16 = LoadLibrary16( "DeskCp16.Dll" );
  836.     FARPROC16 pDesk16 = (FARPROC16)( hDesk16?
  837.                         GetProcAddress16( hDesk16, "CplApplet" ) : NULL );
  838. #endif
  839.     PROPSHEETHEADER psh;
  840.     HPROPSHEETPAGE rPages[MAX_PAGES];
  841.     PROPSHEETPAGE psp;
  842.     HPSXA hpsxa = NULL;
  843.     HPSXA hpsxaOEM = NULL;
  844.     int iResult = 0;
  845.     TCHAR szDisplay[256];
  846.     PMONPARAM pmMon = NULL;
  847.     psh.dwSize = sizeof(psh);
  848.     psh.dwFlags = PSH_PROPTITLE;
  849.     psh.hwndParent = GetParent(_hDlg);
  850.     psh.hInstance = hInstance;
  851.     psh.pszCaption = (LPTSTR)_pCurDevice->DisplayDevice.DeviceString;
  852.     psh.nPages = 0;
  853.     psh.nStartPage = 0;
  854.     psh.phpage = rPages;
  855.     psp.dwSize = sizeof(psp);
  856.     psp.dwFlags = PSP_DEFAULT;
  857.     psp.hInstance = hInstance;
  858.     psp.pfnDlgProc = GeneralPageProc;
  859.     psp.pszTemplate = MAKEINTRESOURCE(DLG_GENERAL);
  860.     psp.lParam = (LPARAM)_QueryForceSmallFont();
  861.     if (rPages[psh.nPages] = CreatePropertySheetPage(&psp))
  862.         psh.nPages++;
  863. #ifdef WINNT
  864.     //BUGBUG: (dli) Should use the one from pds
  865.     GetAdvAdapterPropPageParam( NULL, _AddDisplayPropSheetPage, (LPARAM)&psh, (LPARAM)&_pCurDevice->DisplaySetting);
  866.     pmMon = _pCurDevice->pds->GetMonitorParams();
  867.     if (pmMon)
  868.         GetAdvMonitorPropPageParam( NULL, _AddDisplayPropSheetPage, (LPARAM)&psh, (LPARAM)pmMon );
  869. #else
  870.     ATOM AtomDevice = GlobalAddAtom((char *)&_pCurDevice->DisplayDevice.DeviceName);
  871.     if( pDesk16 && CallCPLEntry16( hDesk16, pDesk16, NULL, CPL_INIT, (LPARAM)AtomDevice, 0 ) )
  872.     {
  873.         // or just add the default page
  874.         SHAddPages16( NULL, "DESKCP16.DLL,GetAdapterPage",
  875.                       _AddDisplayPropSheetPage, (LPARAM)&psh );
  876.         //
  877.         // only add the monitor tab iff a monitor exists
  878.         //
  879.         if (_pCurDevice->DisplayDevice.DeviceName[0])
  880.         {
  881.             TCHAR szMonitor[140];
  882.             szMonitor[0] = 0;
  883.             _pCurDevice->pds->GetMonitorName(szMonitor, NULL);
  884.             if (szMonitor[0])
  885.             {
  886.                 SHAddPages16( NULL, "DESKCP16.DLL,GetMonitorPage",
  887.                           _AddDisplayPropSheetPage, (LPARAM)&psh );
  888.             }
  889.         }
  890.         SHAddPages16( NULL, "DESKCP16.DLL,GetPerformancePage",
  891.                       _AddDisplayPropSheetPage, (LPARAM)&psh );
  892.     }
  893. #endif
  894.     InitClipboardFormats();
  895.     IDataObject * pdo = NULL;
  896.     _pCurDevice->pds->QueryInterface(IID_IDataObject, (LPVOID *) &pdo);
  897.     //
  898.     // load any extensions from the registry
  899.     //
  900.     if (gbExecMode == EXEC_NORMAL)
  901.     {
  902.         //
  903.         // load the generic (non hardware specific) extensions
  904.         //
  905.         if( ( hpsxa = SHCreatePropSheetExtArrayEx( HKEY_LOCAL_MACHINE, REGSTR_PATH_CONTROLSFOLDER TEXT("\Device"), 8, pdo) ) != NULL )
  906.         {
  907.             SHAddFromPropSheetExtArray( hpsxa, _AddDisplayPropSheetPage, (LPARAM)&psh );
  908.         }
  909.         //
  910.         // load the hardware-specific extensions
  911.         //
  912.         // NOTE it is very important to load the OEM extensions *after* the
  913.         // generic extensions some HW extensions expect to be the last tabs
  914.         // in the propsheet (right before the settings tab)
  915.         //
  916.         if( ( hpsxaOEM = SHCreatePropSheetExtArrayEx( HKEY_LOCAL_MACHINE, _pCurDevice->DisplayRegKey, 8, pdo) ) != NULL )
  917.         {
  918.             SHAddFromPropSheetExtArray( hpsxaOEM, _AddDisplayPropSheetPage, (LPARAM)&psh );
  919.         }
  920.         //
  921.         // add a fake settings page to fool OEM extensions (must be last)
  922.         //
  923.         if (hpsxa || hpsxaOEM)
  924.         {
  925.             AddFakeSettingsPage(&psh);
  926.         }
  927.     }
  928.     if (psh.nPages)
  929.     {
  930.         iResult = PropertySheet(&psh);
  931.     }
  932.     _GetDisplayName(_pCurDevice, szDisplay);
  933.     if (_NumDevices == 1)
  934.     {
  935.         //Set the name of the primary in the static text
  936.         //strip the first token off (this is the number we dont want it)
  937.         TCHAR *pch;
  938.         for (pch=szDisplay; *pch && *pch != TEXT(' '); pch++);
  939.         for (;*pch && *pch == TEXT(' '); pch++);
  940.         SetDlgItemText(_hDlg, IDC_DISPLAYTEXT, pch);
  941.     }
  942.     else
  943.     {
  944.         ComboBox_DeleteString(_hwndList, _pCurDevice->ComboBoxItem);
  945.         ComboBox_InsertString(_hwndList, _pCurDevice->ComboBoxItem, szDisplay);
  946.         ComboBox_SetItemData(_hwndList, _pCurDevice->ComboBoxItem, (DWORD)_pCurDevice);
  947.         ComboBox_SetCurSel(_hwndList, _pCurDevice->ComboBoxItem);
  948.     }
  949.     
  950. #ifdef WINNT
  951.     _pCurDevice->pds->UpdateRefreshRate(pmMon);
  952. #endif
  953.     if( hpsxa )
  954.         SHDestroyPropSheetExtArray( hpsxa );
  955.     if( hpsxaOEM )
  956.         SHDestroyPropSheetExtArray( hpsxaOEM );
  957.     if (pdo)
  958.         pdo->Release();
  959.     
  960. #ifndef WINNT
  961.     if (pDesk16)
  962.         CallCPLEntry16( hDesk16, pDesk16, NULL, CPL_EXIT, 0, 0 );
  963.     if (AtomDevice)
  964.         GlobalDeleteAtom(AtomDevice);
  965.     if( hDesk16 )
  966.         FreeLibrary16( hDesk16 );
  967. #endif
  968.     
  969.     if ((iResult == ID_PSRESTARTWINDOWS) || (iResult == ID_PSREBOOTSYSTEM))
  970.     {
  971.         PropSheet_CancelToClose(GetParent(_hDlg));
  972.         if (iResult == ID_PSREBOOTSYSTEM)
  973.             PropSheet_RebootSystem(ghwndPropSheet);
  974.         else
  975.             PropSheet_RestartWindows(ghwndPropSheet);
  976.     }
  977. }
  978. //-----------------------------------------------------------------------------
  979. void CMultiMon::UpdateActiveDisplay(PMULTIMON_DEVICE pDevice)
  980. {
  981.     HWND hwndC;
  982.     _InSetInfo++;
  983.     if (pDevice == NULL)
  984.         pDevice = (PMULTIMON_DEVICE)ComboBox_GetItemData(_hwndList, ComboBox_GetCurSel(_hwndList));
  985.     else
  986.         ComboBox_SetCurSel(_hwndList, pDevice->ComboBoxItem);
  987.     if (pDevice && ((ULONG)pDevice) != 0xFFFFFFFF)
  988.     {
  989.         hwndC = GetDlgItem(_hwndDesk, (int) _pCurDevice);
  990.         _pCurDevice->pds->SetActive(FALSE);
  991.         _pCurDevice = pDevice;
  992.         if (hwndC)
  993.             RedrawWindow(hwndC, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
  994.         hwndC = GetDlgItem(_hwndDesk, (int) _pCurDevice);
  995.         if (hwndC)
  996.             RedrawWindow(hwndC, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
  997.         //
  998.         // Update the two check box windows
  999.         //
  1000. #ifdef WINNT 
  1001.         CheckDlgButton(_hDlg, IDC_DISPLAYPRIME, _pCurDevice->pds->IsPrimary());
  1002.         EnableWindow(GetDlgItem(_hDlg, IDC_DISPLAYPRIME), 1);  // fUseDevice);
  1003. #endif
  1004.         CheckDlgButton(_hDlg, IDC_DISPLAYUSEME, _pCurDevice->pds->IsAttached());
  1005.         EnableWindow(GetDlgItem(_hDlg, IDC_DISPLAYUSEME), !_bNoAttach && !_pCurDevice->pds->IsPrimary());
  1006.         // Update the color and screen size combo box
  1007.         _pCurDevice->pds->SetActive(TRUE);
  1008.     }
  1009.     else
  1010.     {
  1011.         //
  1012.         // No display device !
  1013.         //
  1014.         WarnMsg(TEXT("UpdateActiveDisplay: No display device!!!!"), TEXT(""));
  1015.         ASSERT(FALSE);
  1016.     }
  1017.     _InSetInfo--;
  1018. }
  1019. //----------------------------------------------------------------------------
  1020. //
  1021. //  VerifyPrimaryMode()
  1022. //
  1023. //----------------------------------------------------------------------------
  1024. void CMultiMon::_VerifyPrimaryMode(BOOL fKeepMode)
  1025. {
  1026. #ifndef WINNT
  1027.     //
  1028.     // on Win9x make sure the primary is in a mode >= 256 color
  1029.     //
  1030.     if (GetNumberOfAttachedDisplays() > 1 && _pPrimaryDevice &&
  1031.         _pPrimaryDevice->pds->GetColorBits() < 8)
  1032.     {
  1033.         if (fKeepMode)
  1034.         {
  1035.             for (int i = 0; _Devices[i].DisplayDevice.cb != 0; i++)
  1036.             {
  1037.                 if (_pPrimaryDevice != &_Devices[i])
  1038.                     _Devices[i].pds->SetAttached(FALSE);
  1039.             }
  1040.             RedrawWindow(GetDlgItem(_hDlg, IDC_DISPLAYDESK), NULL, NULL,
  1041.                 RDW_ALLCHILDREN | RDW_ERASE | RDW_INVALIDATE);
  1042.         }
  1043.         else
  1044.         {
  1045.             _pPrimaryDevice->pds->SetMode(640,480,8);
  1046.         }
  1047.         SetDirty();
  1048.     }
  1049. #endif
  1050. }
  1051. //----------------------------------------------------------------------------
  1052. //
  1053. //  SetDirty
  1054. //
  1055. //----------------------------------------------------------------------------
  1056. void CMultiMon::SetDirty(BOOL bDirty)
  1057. {
  1058.     _bDirty = bDirty;
  1059.     if (_bDirty)
  1060.     {
  1061.         PostMessage(GetParent(_hDlg), PSM_CHANGED, (WPARAM)_hDlg, 0L);
  1062.     }
  1063. }
  1064. //----------------------------------------------------------------------------
  1065. //
  1066. //  ToggleAttached()
  1067. //
  1068. //  Toggles the attached state of the device, unless the bPrime is set which
  1069. //  forces an attach also.
  1070. //
  1071. //----------------------------------------------------------------------------
  1072. BOOL CMultiMon::ToggleAttached(PMULTIMON_DEVICE pDevice, BOOL bPrime)
  1073. {
  1074.     DWORD iDevice;
  1075.     RECT rcPos;
  1076.     if (bPrime || (!pDevice->pds->IsAttached()))
  1077.     {
  1078.         //
  1079.         // Make sure this device actually has a rectangle.
  1080.         // If it does not (not configured in the registry, then we need
  1081.         // to put up a popup and ask the user to configure the device.
  1082.         //
  1083.         pDevice->pds->GetCurPosition(&rcPos);
  1084.         if (IsRectEmpty(&rcPos))
  1085.             return FALSE;
  1086. #ifdef WINNT
  1087.         //
  1088.         // NT requires the same number of colors.
  1089.         // Don't let the driver get attached if it is has a different color
  1090.         // depth than the primary (unless we are settings it to the primary.
  1091.         //
  1092.         if ((bPrime == FALSE) &&
  1093.             (pDevice->DisplaySetting.dmBitsPerPel !=
  1094.              _pPrimaryDevice->DisplaySetting.dmBitsPerPel))
  1095.         {
  1096.             FmtMessageBox(_hDlg,
  1097.                           MB_ICONSTOP | MB_OK,
  1098.                           FALSE,
  1099.                           ID_DSP_TXT_SETTINGS,
  1100.                           MSG_MULTI_BAD_COLORS);
  1101.             return FALSE;
  1102.         }
  1103. #endif
  1104.         //
  1105.         // If the device is not the primary, let's see if it is repositionable.
  1106.         // If not, we will have to ask the user if it should be the primary also.
  1107.         //
  1108.         if ((bPrime == FALSE) && (!pDevice->bRepositionable))
  1109.         {
  1110.             if (FmtMessageBox(_hDlg,
  1111.                               MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2,
  1112.                               FALSE,
  1113.                               ID_DSP_TXT_SETTINGS,
  1114.                               MSG_MULTI_NO_REPOSITION) == IDYES)
  1115.             {
  1116.                 bPrime = TRUE;
  1117.             }
  1118.             else
  1119.             {
  1120.                 return FALSE;
  1121.             }
  1122.         }
  1123.         //
  1124.         // The DM_POSITION determines whether the device should be
  1125.         // part of the desktop or not.
  1126.         // Make sure this device actually has a rectangle now.
  1127.         //
  1128.         pDevice->pds->SetAttached(TRUE);
  1129.         //
  1130.         // make sure the primary is in a valid mode
  1131.         //
  1132.         _VerifyPrimaryMode(FALSE);
  1133.     }
  1134.     else
  1135.         //
  1136.         // Toggling the device out of the desktop, unless it's the primary
  1137.         //
  1138.         pDevice->pds->SetAttached(FALSE);
  1139.     if (bPrime)
  1140.     {
  1141.         //
  1142.         // Unmark the primary device.
  1143.         //
  1144.         // Remove all the devices which are non-repositionable or of a
  1145.         // different color depths.
  1146.         // Also, if this device is not repositionable, then remove it from the
  1147.         // desktop.
  1148.         //
  1149.         for (iDevice = 0; _Devices[iDevice].DisplayDevice.cb != 0; iDevice++)
  1150.         {
  1151.             _Devices[iDevice].pds->SetPrimary(FALSE);
  1152. #ifdef WINNT
  1153.             if (pDevice->DisplaySetting.dmBitsPerPel != _Devices[iDevice].DisplaySetting.dmBitsPerPel)
  1154.                 _Devices[iDevice].pds->SetAttached(FALSE);
  1155. #endif
  1156.             if (!_Devices[iDevice].bRepositionable)
  1157.                 _Devices[iDevice].pds->SetAttached(FALSE);
  1158.         }
  1159.         _pPrimaryDevice->pds->SetPrimary(FALSE);
  1160.         //
  1161.         // Mark the new device as the primary and attached since we
  1162.         // unattached it in the above loop.
  1163.         //
  1164.         _pPrimaryDevice = pDevice;
  1165.         _pPrimaryDevice->pds->SetPrimary(TRUE);
  1166.         _pPrimaryDevice->pds->SetAttached(TRUE);
  1167.     }
  1168.     return TRUE;
  1169. }
  1170. //-----------------------------------------------------------------------------
  1171. void CMultiMon::_CleanupRects(HWND hwndP)
  1172. {
  1173.     int   n;
  1174.     ULONG iDevice;
  1175.     HWND  hwndC;
  1176.     RECT  arc[20];
  1177.     DWORD arcDev[20];
  1178.     DWORD iArcPrimary = 0;
  1179.     RECT rc;
  1180.     RECT rcU;
  1181.     int   i;
  1182.     RECT rcPrev;
  1183.     int sx,sy;
  1184.     int x,y;
  1185.     //
  1186.     // get the positions of all the windows
  1187.     //
  1188.     n = 0;
  1189.     for (iDevice = 0; _Devices[iDevice].DisplayDevice.cb != 0; iDevice++)
  1190.     {
  1191.         PMULTIMON_DEVICE pDevice = &_Devices[iDevice];
  1192.         hwndC = GetDlgItem(hwndP, (int) pDevice);
  1193.         if (hwndC != NULL)
  1194.         {
  1195.             RECT rcPos;
  1196. #ifdef WINNT
  1197.             ASSERT(pDevice->DisplaySetting.dmBitsPerPel ==
  1198.                    _pPrimaryDevice->DisplaySetting.dmBitsPerPel);
  1199. #endif
  1200.             TraceMsg(TF_GENERAL, "_CleanupRects start Device %08lx, Dev = %d, hwnd = %08lxn",
  1201.                      pDevice, iDevice, hwndC);
  1202.             ShowWindow(hwndC, SW_SHOW);
  1203.             GetWindowRect(hwndC, &arc[n]);
  1204.             MapWindowPoints(NULL, hwndP, (POINT FAR*)&arc[n], 2);
  1205.             pDevice->pds->GetCurPosition(&rcPos);
  1206.             _OffsetPreviewToDesk(&arc[n], &rcPos);
  1207.             arc[n] = rcPos;
  1208.             arcDev[n] = iDevice;
  1209.             if (pDevice->pds->IsPrimary())
  1210.             {
  1211.                 TraceMsg(TF_GENERAL, "_CleanupRects primary Device %08lxn", pDevice);
  1212.                 iArcPrimary = n;
  1213.             }
  1214.             else
  1215.             {
  1216.                 ASSERT(pDevice->bRepositionable);
  1217.             }
  1218.             n++;
  1219.         }
  1220.     }
  1221.     //
  1222.     // cleanup the rects
  1223.     //
  1224.     AlignRects(arc, n, iArcPrimary, CUDR_NORMAL);
  1225.     //
  1226.     // Get the union.
  1227.     //
  1228.     SetRectEmpty(&rcU);
  1229.     for (i=0; i<n; i++)
  1230.         UnionRect(&rcU, &rcU, &arc[i]);
  1231.     GetClientRect(hwndP, &rcPrev);
  1232.     //
  1233.     // only rescale if the new desk hangs outside the preview area.
  1234.     // or is too small
  1235.     //
  1236.     _DeskToPreview(&rcU, &rc);
  1237.     x = ((rcPrev.right  - rcPrev.left)-(rc.right  - rc.left))/2;
  1238.     y = ((rcPrev.bottom - rcPrev.top) -(rc.bottom - rc.top))/2;
  1239.     if (rcU.left < 0 || rcU.top < 0 || x < 0 || y < 0 ||
  1240.         rcU.right > rcPrev.right || rcU.bottom > rcPrev.bottom ||
  1241.         (x > (rcPrev.right-rcPrev.left)/8 &&
  1242.          y > (rcPrev.bottom-rcPrev.top)/8))
  1243.     {
  1244.         _rcDesk = rcU;
  1245.         sx = MulDiv(rcPrev.right  - rcPrev.left - 16,1000,_rcDesk.right  - _rcDesk.left);
  1246.         sy = MulDiv(rcPrev.bottom - rcPrev.top  - 16,1000,_rcDesk.bottom - _rcDesk.top);
  1247.         _DeskScale = min(sx,sy) * 2 / 3;
  1248.         _DeskToPreview(&_rcDesk, &rc);
  1249.         _DeskOff.x = ((rcPrev.right  - rcPrev.left)-(rc.right  - rc.left))/2;
  1250.         _DeskOff.y = ((rcPrev.bottom - rcPrev.top) -(rc.bottom - rc.top))/2;
  1251.     }
  1252.     //
  1253.     // Show all the windows and save them all to the devmode.
  1254.     //
  1255.     for (i=0; i < n; i++)
  1256.     {
  1257.         RECT rcPos;
  1258.         POINT ptPos;
  1259.         
  1260.         _Devices[arcDev[i]].pds->GetCurPosition(&rcPos);
  1261.         hwndC = GetDlgItem(hwndP, (int) &_Devices[arcDev[i]]);
  1262.         _DeskToPreview(&arc[i], &rc);
  1263.         rc.right =  MulDiv(RECTWIDTH(rcPos),  _DeskScale, 1000);
  1264.         rc.bottom = MulDiv(RECTHEIGHT(rcPos), _DeskScale, 1000);
  1265.         TraceMsg(TF_GENERAL, "_CleanupRects set Dev = %d, hwnd = %08lxn", arcDev[i], hwndC);
  1266.         TraceMsg(TF_GENERAL, "_CleanupRects window pos %d,%d,%d,%dn", rc.left, rc.top, rc.right, rc.bottom);
  1267.         SetWindowPos(hwndC,
  1268.                      NULL,
  1269.                      rc.left,
  1270.                      rc.top,
  1271.                      rc.right,
  1272.                      rc.bottom,
  1273.                      SWP_NOZORDER);
  1274.         ptPos.x = arc[i].left;
  1275.         ptPos.y = arc[i].top;
  1276.         _Devices[arcDev[i]].pds->SetCurPosition(&ptPos);
  1277.     }
  1278. }
  1279. BOOL CMultiMon::_WriteMultiMonProfile()
  1280. {
  1281.     return WritePrivateProfileString(sc_szVideo, sc_szOptimize, TEXT("bmp"), sc_szQtwIni);
  1282. }
  1283. BOOL CMultiMon::HandleMonitorChange(HWND hwndP, BOOL bMainDlg)
  1284. {
  1285.     if (!bMainDlg && _InSetInfo)
  1286.         return FALSE;
  1287.     SetDirty();
  1288.     if (bMainDlg)
  1289.         BringWindowToTop(hwndP);
  1290.     _CleanupRects(GetParent(hwndP));
  1291.     UpdateActiveDisplay(_pCurDevice);
  1292.     return TRUE;
  1293. }
  1294. BOOL CMultiMon::RegisterPreviewWindowClass(WNDPROC pfnWndProc)
  1295. {
  1296.     TraceMsg(TF_GENERAL, "InitMultiMonitorDlgn");
  1297.     WNDCLASS         cls;
  1298.     cls.hCursor        = LoadCursor(NULL,IDC_ARROW);
  1299.     cls.hIcon          = NULL;
  1300.     cls.lpszMenuName   = NULL;
  1301.     cls.lpszClassName  = TEXT("Monitor32");
  1302.     cls.hbrBackground  = (HBRUSH)(COLOR_DESKTOP + 1);
  1303.     cls.hInstance      = hInstance;
  1304.     cls.style          = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
  1305.     cls.lpfnWndProc    = (WNDPROC)pfnWndProc;
  1306.     cls.cbWndExtra     = SIZEOF(LPVOID);
  1307.     cls.cbClsExtra     = 0;
  1308.     return RegisterClass(&cls);
  1309. }
  1310. LRESULT CALLBACK DeskWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, UINT uID, DWORD dwRefData);
  1311. BOOL CMultiMon::_InitDisplaySettings(BOOL bExport)
  1312. {
  1313.     HWND             hwndC;
  1314.     int              iItem;
  1315.     DWORD            iDevice = 0;
  1316.     DWORD            iEnum;
  1317.     LONG             iPrimeDevice = -1;
  1318.     TCHAR            ach[128];
  1319.     PMULTIMON_DEVICE pDevice;
  1320.     RECT             rcPrimary;
  1321.     BOOL             f;
  1322.     //
  1323.     // Reset some globals.
  1324.     // Clear out the list box.
  1325.     //
  1326.     _InSetInfo = 1;
  1327.     ComboBox_ResetContent(_hwndList);
  1328.     SetRectEmpty(&_rcDesk);
  1329.     while (hwndC = GetWindow(_hwndDesk, GW_CHILD))
  1330.         DestroyWindow(hwndC);
  1331.     ShowWindow(_hwndDesk, SW_HIDE);
  1332.     if (_himl != NULL)
  1333.     {
  1334.         ImageList_Destroy(_himl);
  1335.         _himl = NULL;
  1336.     }
  1337.     //
  1338.     // Reenumerate all the devices in the system.
  1339.     //
  1340.     for (iDevice = 0, iEnum = 0; iEnum < MONITORS_MAX; iEnum++)
  1341.     {
  1342.         TraceMsg(TF_GENERAL, "Device %d", iEnum);
  1343.         pDevice = &_Devices[iDevice];
  1344.         ZeroMemory(pDevice, sizeof(DISPLAY_DEVICE));
  1345.         pDevice->DisplayDevice.cb = sizeof(DISPLAY_DEVICE);
  1346.         f = EnumDisplayDevices(NULL, iEnum, &pDevice->DisplayDevice, 0);
  1347.         //
  1348.         // ignore device's we cant create a DC for.
  1349.         //
  1350.         if (f)
  1351.         {
  1352.             pDevice->hdc = CreateDC(NULL,(LPTSTR)pDevice->DisplayDevice.DeviceName,NULL,NULL);
  1353.             f = pDevice->hdc != NULL;
  1354.         }
  1355.         //
  1356.         // EnumDisplayDevices is returning NO devices, this is bad
  1357.         // invent a fake device.
  1358.         //
  1359.         if (!f && iEnum == 0)
  1360.         {
  1361.             pDevice->DisplayDevice.DeviceName[0] = 0;
  1362.             LoadString(hInstance, IDS_UNKNOWNDEVICE,
  1363.                 (LPTSTR)&pDevice->DisplayDevice.DeviceString[0],
  1364.                 SIZEOF(pDevice->DisplayDevice.DeviceString));
  1365.             pDevice->DisplayDevice.StateFlags = DISPLAY_DEVICE_PRIMARY_DEVICE;
  1366.             gbExecMode    = EXEC_INVALID_MODE;
  1367.             gbInvalidMode = EXEC_INVALID_DISPLAY_DEVICE;
  1368.             f = TRUE;
  1369.         }
  1370.         if (f)
  1371.         {
  1372.             TraceMsg(TF_GENERAL, "%s   %sn", pDevice->DisplayDevice.DeviceName, pDevice->DisplayDevice.DeviceString);
  1373.             //
  1374.             // We won't even include the MIRRORING drivers in the list for
  1375.             // now.
  1376.             //
  1377.             if (pDevice->DisplayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER)
  1378.             {
  1379.                 TraceMsg(TF_GENERAL, "Mirroring driver - skip itn");
  1380.                 continue;
  1381.             }
  1382.             //
  1383.             // get the device software key
  1384.             //
  1385.             pDevice->DisplayRegKey[0] = 0;
  1386.             GetDisplayKey(iEnum, pDevice->DisplayRegKey, sizeof(pDevice->DisplayRegKey));
  1387.             TraceMsg(TF_GENERAL, "DeviceKey %s", pDevice->DisplayRegKey);
  1388.             FillMemory(&(pDevice->DisplaySetting), sizeof(DEVMODE), 0);
  1389.             pDevice->DisplaySetting.dmSize = sizeof(DEVMODE);
  1390.             _SetReposition(_hwndDesk, pDevice);
  1391.             pDevice->pds = new CDeviceSettings(_hDlg);
  1392.             if (pDevice->pds->InitSettings(&pDevice->DisplayDevice))
  1393.                 iDevice++;
  1394.             else
  1395.                 delete pDevice->pds;
  1396.         }
  1397.         else
  1398.         {
  1399.             //
  1400.             // Mark the end of the list of devices.
  1401.             //
  1402.             TraceMsg(TF_GENERAL, "End of listn");
  1403.             pDevice->DisplayDevice.cb = 0;
  1404.             break;
  1405.         }
  1406.     }
  1407.     if (iDevice == 0)
  1408.     {
  1409.         ASSERT(0);
  1410.         return FALSE;
  1411.     }
  1412.     //
  1413.     // Because we are getting the registry values, the current state of
  1414.     // the registry may be inconsistent with that of the system:
  1415.     //
  1416.     // EmumDisplayDevices will return the active primary in the
  1417.     // system, which may be different than the actual primary marked in the
  1418.     // registry
  1419.     //
  1420.     // First, we determine if the registry is in a consistent state with
  1421.     // the system by making sure the system primary is also marked as
  1422.     // primary in the regsitry.  This means the primary as returned by
  1423.     // EnumDisplayDevices is also at position 0,0, with the DM_POSITION flag
  1424.     // set.
  1425.     //
  1426.     // If this is not the case, then we will present the current state of the
  1427.     // registry (if there is any) or the primary device only (for a non-
  1428.     // configured system) and ask the user to properly configure the devices.
  1429.     //
  1430.     _pPrimaryDevice = NULL;
  1431.     for (iDevice = 0; _Devices[iDevice].DisplayDevice.cb != 0; iDevice++)
  1432.     {
  1433.         if (_Devices[iDevice].pds->IsAttached() &&
  1434.             (_Devices[iDevice].DisplaySetting.dmPosition.x  == 0)   &&
  1435.             (_Devices[iDevice].DisplaySetting.dmPosition.y == 0)    &&
  1436.             _Devices[iDevice].pds->IsPrimary())
  1437.         {
  1438.             if (iPrimeDevice == -1)
  1439.             {
  1440.                 iPrimeDevice = iDevice;
  1441.                 _pPrimaryDevice = &_Devices[iDevice];
  1442.             }
  1443.             else
  1444.             {
  1445.                 // Multiple primaries are set !
  1446.                 ASSERT(FALSE);
  1447.                 //_pPrimaryDevice = NULL;
  1448.             }
  1449.         }
  1450.     }
  1451.     //
  1452.     // Primary is not set correctly !
  1453.     // Let's show the state of the registry if it appears the user has ever
  1454.     // setup multiple displays in the registry.
  1455.     //
  1456.     if (_pPrimaryDevice == NULL)
  1457.     {
  1458.         for (iDevice = 0; _Devices[iDevice].DisplayDevice.cb != 0; iDevice++)
  1459.         {
  1460.             //
  1461.             // We can have multiple devices at this location since we
  1462.             // support overlapping devices.  Just pick anyone since this is an
  1463.             // error path.  Just make sure we are not picking a remote control
  1464.             // driver.
  1465.             //
  1466.             if (_Devices[iDevice].pds->IsAttached() &&
  1467.                 ((_pPrimaryDevice == NULL) || _Devices[iDevice].pds->IsPrimary()))
  1468.             {
  1469.                 TraceMsg(TF_GENERAL, "InitDisplaySettings: attached device foundn");
  1470.                 
  1471.                 iPrimeDevice = iDevice;
  1472.                 _pPrimaryDevice = &_Devices[iDevice];
  1473.             }
  1474.         }
  1475.         //
  1476.         // If we did find a device, then let's make sure we reset the primary
  1477.         // flag in our structure to be the one we will reset to 0,0
  1478.         //
  1479.         if (_pPrimaryDevice != NULL)
  1480.         {
  1481.             WarnMsg(TEXT("InitDisplaySettings:"), TEXT("Resetting primary"));
  1482.             for (iDevice = 0; _Devices[iDevice].DisplayDevice.cb != 0; iDevice++)
  1483.                 _Devices[iDevice].pds->SetPrimary(&_Devices[iDevice] == _pPrimaryDevice);
  1484.         }
  1485.     }
  1486.     //
  1487.     // If we still do not have a primary device, (no registry information)
  1488.     // assume this is an initial multimonitor setup and we should just deal
  1489.     // with the primary device.
  1490.     //
  1491.     // We will have to setup at least one rectangle.
  1492.     //
  1493.     if (_pPrimaryDevice == NULL)
  1494.     {
  1495.         for (iDevice = 0; _Devices[iDevice].DisplayDevice.cb != 0; iDevice++)
  1496.         {
  1497.             if (_Devices[iDevice].pds->IsAttached())
  1498.             {
  1499.                 POINT ptPos = {0,0};
  1500.                 iPrimeDevice = iDevice;
  1501.                 _pPrimaryDevice = &_Devices[iDevice];
  1502.                 _pPrimaryDevice->pds->SetCurPosition(&ptPos);
  1503.                 break;
  1504.             }
  1505.         }
  1506.     }
  1507.     //
  1508.     // Reset the primary's variables to make sure it is a properly formated
  1509.     // primary entry.
  1510.     //
  1511.     ASSERT(_pPrimaryDevice != NULL);
  1512.     if (_pPrimaryDevice == NULL)
  1513.     {
  1514.         WarnMsg(TEXT("InitDisplaySettings: No Primary Device!!!!"), TEXT(""));
  1515.         return FALSE;
  1516.     }
  1517.     _pPrimaryDevice->pds->SetPrimary(TRUE);
  1518.     _pPrimaryDevice->pds->GetCurPosition(&rcPrimary);
  1519.     if (!lstrcmpi((LPTSTR)_pPrimaryDevice->DisplayDevice.DeviceString,sc_szVGA))
  1520.         _bNoAttach = TRUE;
  1521.     //
  1522.     // compute the max image size needed for a monitor bitmap
  1523.     //
  1524.     // NOTE this must be the max size the images will *ever*
  1525.     // be we cant just take the current max size.
  1526.     // we use the client window size, a child monitor cant be larger than this.
  1527.     //
  1528.     RECT rcDesk;
  1529.     GetClientRect(_hwndDesk, &rcDesk);
  1530.     int cxImage = rcDesk.right;
  1531.     int cyImage = rcDesk.bottom;
  1532.     //
  1533.     // load the monitor bitmap
  1534.     //
  1535.     HBITMAP hbm = (HBITMAP)LoadImage(hInstance, MAKEINTRESOURCE(BMP_MONITOR2),
  1536.             IMAGE_BITMAP, cxImage, cyImage, 0);
  1537.     //
  1538.     // Go through all the devices to count them
  1539.     //
  1540.     for (iDevice = 0; _Devices[iDevice].DisplayDevice.cb != 0; iDevice++)
  1541.         ;
  1542.     // For the ASSERT: We should have at least the primary device
  1543.     ASSERT(iDevice > 0);
  1544.     _NumDevices = iDevice;
  1545.     //
  1546.     // Go through all the devices one last time to create the windows
  1547.     //
  1548.     for (iDevice = 0; _Devices[iDevice].DisplayDevice.cb != 0; iDevice++)
  1549.     {
  1550.         TCHAR szDisplay[256];
  1551.         pDevice = &_Devices[iDevice];
  1552.         MonitorData md = {0};
  1553.         RECT rcPos;
  1554.         LPVOID pWindowData = (LPVOID)this;
  1555.         pDevice->DisplayIndex = iDevice + 1;
  1556.         _GetDisplayName(pDevice, szDisplay);
  1557.         iItem = ComboBox_AddString(_hwndList, szDisplay);
  1558.         pDevice->ComboBoxItem = iItem;
  1559.         ComboBox_SetItemData(_hwndList,
  1560.                              iItem,
  1561.                              (DWORD)pDevice);
  1562.         //
  1563.         // If the monitor is part of the desktop, show it on the screen
  1564.         // otherwise keep it invisible.
  1565.         //
  1566.         // On NT, we require all the devices to have the same color depth.
  1567.         // Don't let them on the desktop if they don't
  1568.         //
  1569. #ifdef WINNT
  1570.         if (pDevice->DisplaySetting.dmBitsPerPel != _pPrimaryDevice->DisplaySetting.dmBitsPerPel)
  1571.             pDevice->pds->SetAttached(FALSE);
  1572. #endif
  1573.         wsprintf(ach, TEXT("%d"), iDevice + 1);
  1574.         if (!pDevice->pds->IsAttached())
  1575.         {
  1576.             // By default set the unattached monitors to the right of the primary monitor
  1577.             POINT ptPos = {rcPrimary.right, rcPrimary.top};
  1578.             pDevice->pds->SetCurPosition(&ptPos);
  1579.         }
  1580.         pDevice->pds->GetCurPosition(&rcPos);
  1581.         if (bExport)
  1582.         {
  1583.             md.dwSize = SIZEOF(MonitorData);
  1584.             if ( pDevice->pds->IsPrimary() )
  1585.                 md.dwStatus |= MD_PRIMARY;
  1586.             if ( pDevice->pds->IsAttached() )
  1587.                 md.dwStatus |= MD_ATTACHED;
  1588.             md.rcPos = rcPos;
  1589.             pWindowData = &md;
  1590.         }
  1591.         if (_himl == NULL)
  1592.         {
  1593.             // use ILC_COLORDDB to always get stipple selection
  1594.             // UINT flags = ILC_COLORDDB | ILC_MASK;
  1595.             // use ILC_COLOR4   to get HiColor selection
  1596.             UINT flags = ILC_COLOR4 | ILC_MASK;
  1597.             _himl = ImageList_Create(cxImage, cyImage, flags, _NumDevices, 1);
  1598.             ASSERT(_himl);
  1599.             ImageList_SetBkColor(_himl, GetSysColor(COLOR_APPWORKSPACE));
  1600.         }
  1601.         pDevice->w      = -1;
  1602.         pDevice->h      = -1;
  1603.         pDevice->himl   = _himl;
  1604.         pDevice->iImage = ImageList_AddMasked(_himl, hbm, CLR_DEFAULT);
  1605.         TraceMsg(TF_GENERAL, "InitDisplaySettings: Creating preview windows %s at %d %d %d %d",
  1606.                  ach, rcPos.left, rcPos.top, rcPos.right, rcPos.bottom);
  1607.         hwndC = CreateWindowEx(
  1608.                                0, // WS_EX_CLIENTEDGE,
  1609.                                TEXT("Monitor32"), ach,
  1610.                                WS_CLIPSIBLINGS | /* WS_DLGFRAME | */ WS_VISIBLE | WS_CHILD,
  1611.                                rcPos.left, rcPos.top, RECTWIDTH(rcPos), RECTHEIGHT(rcPos),
  1612.                                _hwndDesk,
  1613.                                (HMENU)pDevice,
  1614.                                hInstance,
  1615.                                pWindowData);
  1616.         ASSERT(hwndC);
  1617.     }
  1618.     //  nuke the temp monitor bitmap
  1619.     if (hbm)
  1620.         DeleteObject(hbm);
  1621.     // Set the primary device as the current device
  1622.     ASSERT(iPrimeDevice >= 0);
  1623.     ComboBox_SetCurSel(_hwndList, iPrimeDevice);
  1624.     _pCurDevice = _pPrimaryDevice;
  1625.     // Initialize all the constants and the settings fields
  1626.     _DeskScale = 1000;
  1627.     _DeskOff.x = 0;
  1628.     _DeskOff.y = 0;
  1629.     _CleanupRects(_hwndDesk);
  1630.     _VerifyPrimaryMode(FALSE);
  1631.     UpdateActiveDisplay(_pCurDevice);
  1632.     ShowWindow(_hwndDesk, SW_SHOW);
  1633.     _InSetInfo--;
  1634.     return TRUE;
  1635. }
  1636.                     
  1637. //-----------------------------------------------------------------------------
  1638. void CMultiMon::InitMultiMonitorDlg(HWND hDlg)
  1639. {
  1640.     HCURSOR hcur;
  1641.     hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  1642.     _hDlg = hDlg;
  1643.     _hwndDesk = GetDlgItem(_hDlg, IDC_DISPLAYDESK);
  1644.     _hwndList = GetDlgItem(_hDlg, IDC_DISPLAYLIST);
  1645.     
  1646. #ifdef WINNT
  1647.     //
  1648.     // Determine in what mode we are running the applet before getting information
  1649.     //
  1650.     _vPreExecMode();
  1651. #endif
  1652.     RegisterPreviewWindowClass(&MonitorWindowProc);
  1653.     _InitDisplaySettings(FALSE);
  1654.     // Now: depends on whether we have a multimon system, change the UI
  1655.     if (_NumDevices == 1)
  1656.     {
  1657.         HWND hwndMultimonHelp = GetDlgItem(_hDlg, IDC_MULTIMONHELP);
  1658.         ShowWindow(hwndMultimonHelp, SW_HIDE);
  1659.         ShowWindow(_hwndDesk, SW_HIDE);
  1660.         
  1661.         // set up bitmaps for sample screen
  1662.         _hbmScrSample = LoadMonitorBitmap( TRUE ); // let them do the desktop
  1663.         SendDlgItemMessage(_hDlg, IDC_SCREENSAMPLE, STM_SETIMAGE, IMAGE_BITMAP, (DWORD)_hbmScrSample);
  1664.         
  1665.         // get a base copy of the bitmap for when the "internals" change
  1666.         _hbmMonitor = LoadMonitorBitmap( FALSE ); // we'll do the desktop
  1667.         
  1668.         //Hide the combo box, keep the static text
  1669.         ShowWindow(_hwndList, SW_HIDE);
  1670.         //Set the name of the primary in the static text
  1671.         //strip the first token off (this is the number we dont want it)
  1672.         TCHAR *pch, szDisplay[MAX_PATH];
  1673.         _GetDisplayName(_pPrimaryDevice, szDisplay);
  1674.         for (pch=szDisplay; *pch && *pch != TEXT(' '); pch++);
  1675.         for (;*pch && *pch == TEXT(' '); pch++);
  1676.         SetDlgItemText(_hDlg, IDC_DISPLAYTEXT, pch);
  1677.     }
  1678.     else if (_NumDevices > 0)
  1679.     {
  1680.         //Hide the static text, keep the combo box
  1681.         ShowWindow(GetDlgItem(_hDlg, IDC_DISPLAYTEXT), SW_HIDE);
  1682.         // Hide the Multimon version of the preview objects
  1683.         ShowWindow(GetDlgItem(_hDlg, IDC_SCREENSAMPLE), SW_HIDE);
  1684.         // In case of multiple devices, subclass the _hwndDesk window for key board support
  1685.         SetWindowSubclass(_hwndDesk, DeskWndProc, 0, (DWORD)this);
  1686.         ShowWindow(_hwndDesk, SW_SHOW);
  1687.     }
  1688. #ifdef WINNT
  1689.     //
  1690.     // Determine if any errors showed up during enumerations and initialization
  1691.     //
  1692.     _vPostExecMode();
  1693.     
  1694.     //
  1695.     // Now tell the user what we found out during initialization
  1696.     // Errors, or what we found during detection
  1697.     //
  1698.     PostMessage(hDlg, MSG_DSP_SETUP_MESSAGE, 0, 0);
  1699.     //
  1700.     // Since this could have taken a very long time, just make us visible
  1701.     // if another app (like progman) came up.
  1702.     //
  1703.     ShowWindow(hDlg, SW_SHOW);
  1704. #endif
  1705.     SetCursor(hcur);
  1706. }
  1707. LRESULT CALLBACK DeskWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, UINT uID, DWORD dwRefData)
  1708. {
  1709.     CMultiMon * pcmm = (CMultiMon *)dwRefData;
  1710.     HWND hwndC;
  1711.     HDC  hdc;
  1712.     RECT rcPos;
  1713.     BOOL bMoved = TRUE;
  1714.     LRESULT lret;
  1715.     int iMonitor;
  1716.     switch(message)
  1717.     {
  1718.         case WM_GETDLGCODE:
  1719.             return DLGC_WANTCHARS | DLGC_WANTARROWS;
  1720.         case WM_KILLFOCUS:
  1721.             RedrawWindow(hDlg, NULL, NULL, RDW_INVALIDATE);
  1722.             break;
  1723.         case WM_PAINT:
  1724.             if (GetFocus() != hDlg)
  1725.                 break;
  1726.             lret = DefSubclassProc(hDlg, message, wParam, lParam);
  1727.             // Fall through
  1728.         case WM_SETFOCUS:
  1729.             hdc = GetDC(hDlg);
  1730.             GetClientRect(hDlg, &rcPos);
  1731.             InflateRect(&rcPos, -1, -1);
  1732.             //DrawFocusRect(hdc, &rcPos);
  1733.             DrawEdge(hdc, &rcPos, EDGE_SUNKEN, BF_TOP | BF_BOTTOM | BF_LEFT | BF_RIGHT);
  1734.             ReleaseDC(hDlg, hdc);
  1735.             if (message == WM_PAINT)
  1736.                 return lret;
  1737.             break;            
  1738.         case WM_LBUTTONDOWN:
  1739.             SetFocus(hDlg);
  1740.             break;
  1741.             
  1742.         case WM_KEYDOWN:
  1743. #define MONITORMOVEUNIT 3
  1744.             hwndC = pcmm->GetCurDeviceHwnd();
  1745.             GetWindowRect(hwndC, &rcPos);
  1746.             MapWindowRect(NULL, hDlg, &rcPos);
  1747.             switch(wParam)
  1748.             {
  1749.                 case VK_LEFT:
  1750.                     MoveWindow(hwndC, rcPos.left - MONITORMOVEUNIT, rcPos.top, RECTWIDTH(rcPos), RECTHEIGHT(rcPos), TRUE);
  1751.                     break;
  1752.                 case VK_RIGHT:
  1753.                     MoveWindow(hwndC, rcPos.left + MONITORMOVEUNIT, rcPos.top, RECTWIDTH(rcPos), RECTHEIGHT(rcPos), TRUE);
  1754.                     break;
  1755.                 case VK_UP:
  1756.                     MoveWindow(hwndC, rcPos.left, rcPos.top - MONITORMOVEUNIT, RECTWIDTH(rcPos), RECTHEIGHT(rcPos), TRUE);
  1757.                     break;
  1758.                 case VK_DOWN:
  1759.                     MoveWindow(hwndC, rcPos.left, rcPos.top + MONITORMOVEUNIT, RECTWIDTH(rcPos), RECTHEIGHT(rcPos), TRUE);
  1760.                     break;
  1761.                 default:
  1762.                     bMoved = FALSE;
  1763.                     break;
  1764.             }
  1765. #undef MONITORMOVEUNIT
  1766.             
  1767.             pcmm->HandleMonitorChange(hwndC, FALSE);
  1768.             break;
  1769.         case WM_CHAR:
  1770.             if (pcmm)
  1771.             {
  1772.                 iMonitor = (TCHAR)wParam - TEXT('0');
  1773.                 if ((iMonitor == 0) && (pcmm->GetNumDevices() >= 10))
  1774.                     iMonitor = 10;
  1775.                 if ((iMonitor > 0) && (iMonitor <= pcmm->GetNumDevices()))
  1776.                 {
  1777.                     HWND hwndList = GetDlgItem(GetParent(hDlg), IDC_DISPLAYLIST);
  1778.                     ComboBox_SetCurSel(hwndList, iMonitor - 1);
  1779.                     pcmm->UpdateActiveDisplay(NULL);
  1780.                     return 0;
  1781.                 }
  1782.             }
  1783.             break;
  1784.         case WM_DESTROY:
  1785.             RemoveWindowSubclass(hDlg, DeskWndProc, 0);
  1786.             break;
  1787.         default:
  1788.             break;
  1789.     }
  1790.     
  1791.     return DefSubclassProc(hDlg, message, wParam, lParam);
  1792. }
  1793. //-----------------------------------------------------------------------------
  1794. //
  1795. // Callback functions PropertySheet can use
  1796. //
  1797. BOOL CALLBACK
  1798. MultiMonitorDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
  1799. {
  1800.     CMultiMon * pcmm = (CMultiMon *) GetWindowLong(hDlg, DWL_USER);
  1801.     switch (message)
  1802.     {
  1803.         case WM_INITDIALOG:
  1804.             ASSERT(!pcmm);
  1805.             pcmm = new CMultiMon;
  1806.             if (pcmm)
  1807.             {
  1808.                 SetWindowLong(hDlg, DWL_USER, (LPARAM)pcmm);
  1809.                 ghwndPropSheet = GetParent(hDlg);
  1810.                 pcmm->InitMultiMonitorDlg(hDlg);
  1811.                 //
  1812.                 // if we have a invalid mode force the user to Apply
  1813.                 //
  1814.                 if (gbExecMode == EXEC_INVALID_MODE)
  1815.                     pcmm->SetDirty();
  1816.                 return TRUE;
  1817.             }
  1818.             break;
  1819.         case WM_DESTROY:
  1820.             if (pcmm)
  1821.             {
  1822.                 pcmm->WndProc(message, wParam, lParam);
  1823.                 SetWindowLong(hDlg, DWL_USER, NULL);
  1824.                 delete pcmm;
  1825.             }
  1826.             break;
  1827.         default:
  1828.             if (pcmm)
  1829.                 return pcmm->WndProc(message, wParam, lParam);
  1830.             break;
  1831.     }
  1832.     return FALSE;
  1833. }
  1834. void CMultiMon::_SetPreviewScreenSize(int HRes, int VRes, int iOrgXRes, int iOrgYRes)
  1835. {
  1836.     HBITMAP hbmOld;
  1837.     HBRUSH hbrOld;
  1838.     HDC hdcMem2;
  1839.     // stretching the taskbar could get messy, we'll only do the desktop
  1840.     int mon_dy = MON_DY - MON_TRAY;
  1841.     // init to identical extents
  1842.     SIZE dSrc = { MON_DX, mon_dy };
  1843.     SIZE dDst = { MON_DX, mon_dy };
  1844.     // set up a work area to play in
  1845.     if (!_hbmMonitor || !_hbmScrSample)
  1846.         return;
  1847.     hdcMem2 = CreateCompatibleDC(g_hdcMem);
  1848.     if (!hdcMem2)
  1849.         return;
  1850.     SelectObject(hdcMem2, _hbmScrSample);
  1851.     hbmOld = (HBITMAP)SelectObject(g_hdcMem, _hbmMonitor);
  1852.     // see if we need to shrink either aspect of the image
  1853.     if (HRes > iOrgXRes || VRes > iOrgYRes)
  1854.     {
  1855.         // make sure the uncovered area will be seamless with the desktop
  1856.         RECT rc = { MON_X, MON_Y, MON_X + MON_DX, MON_Y + mon_dy };
  1857.         HBRUSH hbr =
  1858.                     CreateSolidBrush( GetPixel( g_hdcMem, MON_X + 1, MON_Y + 1 ) );
  1859.         FillRect(hdcMem2, &rc, hbr);
  1860.         DeleteObject( hbr );
  1861.     }
  1862.     // stretch the image to reflect the new resolution
  1863.     if( HRes > iOrgXRes )
  1864.         dDst.cx = MulDiv( MON_DX, iOrgXRes, HRes );
  1865.     else if( HRes < iOrgXRes )
  1866.         dSrc.cx = MulDiv( MON_DX, HRes, iOrgXRes );
  1867.     if( VRes > iOrgYRes )
  1868.         dDst.cy = MulDiv( mon_dy, iOrgYRes, VRes );
  1869.     else if( VRes < iOrgYRes )
  1870.         dSrc.cy = MulDiv( mon_dy, VRes, iOrgYRes );
  1871.     SetStretchBltMode( hdcMem2, COLORONCOLOR );
  1872.     StretchBlt( hdcMem2, MON_X, MON_Y, dDst.cx, dDst.cy,
  1873.                 g_hdcMem, MON_X, MON_Y, dSrc.cx, dSrc.cy, SRCCOPY );
  1874.     // now fill the new image's desktop with the possibly-dithered brush
  1875.     // the top right corner seems least likely to be hit by the stretch...
  1876.     hbrOld = (HBRUSH)SelectObject( hdcMem2, GetSysColorBrush( COLOR_DESKTOP ) );
  1877.     ExtFloodFill(hdcMem2, MON_X + MON_DX - 2, MON_Y+1,
  1878.                  GetPixel(hdcMem2, MON_X + MON_DX - 2, MON_Y+1), FLOODFILLSURFACE);
  1879.     // clean up after ourselves
  1880.     SelectObject( hdcMem2, hbrOld );
  1881.     SelectObject( hdcMem2, g_hbmDefault );
  1882.     DeleteObject( hdcMem2 );
  1883.     SelectObject( g_hdcMem, hbmOld );    
  1884. }
  1885. void CMultiMon::_RedrawDeskPreviews()
  1886. {
  1887.     if (_NumDevices > 1)
  1888.     {
  1889.         _CleanupRects(_hwndDesk);
  1890.         RedrawWindow(_hwndDesk, NULL, NULL, RDW_ALLCHILDREN | RDW_ERASE | RDW_INVALIDATE);
  1891.     }
  1892.     else if (_pCurDevice && _pCurDevice->pds)
  1893.     {
  1894.         RECT rcPos, rcOrigPos;
  1895.         _pCurDevice->pds->GetCurPosition(&rcPos);
  1896.         _pCurDevice->pds->GetOrigPosition(&rcOrigPos);
  1897.         _SetPreviewScreenSize(RECTWIDTH(rcPos), RECTHEIGHT(rcPos), RECTWIDTH(rcOrigPos), RECTHEIGHT(rcOrigPos));
  1898.         // only invalidate the "screen" part of the monitor bitmap
  1899.         rcPos.left = MON_X;
  1900.         rcPos.top = MON_Y;
  1901.         rcPos.right = MON_X + MON_DX + 2;  // fudge (trust me)
  1902.         rcPos.bottom = MON_Y + MON_DY + 1; // fudge (trust me)
  1903.         InvalidateRect(GetDlgItem(_hDlg, IDC_SCREENSAMPLE), &rcPos, FALSE);
  1904.     }
  1905. }
  1906. BOOL CMultiMon::_HandleApply()
  1907. {
  1908.     BOOL bReboot = FALSE;
  1909.     BOOL bTest = FALSE;
  1910.     int  iTestResult;
  1911.     int  iSave;
  1912. #ifdef WINNT
  1913.     //
  1914.     // If a new driver is installed, we just want to tell the
  1915.     // system to reboot.
  1916.     //
  1917.     // NOTE - this is only until we can get drivers to load on the fly.
  1918.     //
  1919.     if (_bNewDriver)
  1920.     {
  1921.         PropSheet_RestartWindows(ghwndPropSheet);
  1922.         SetWindowLong(_hDlg, DWL_MSGRESULT, PSNRET_NOERROR);
  1923.         return TRUE;
  1924.     }
  1925. #endif
  1926. #ifndef WINNT   //BUGBUG should we do this on NT???
  1927.     //
  1928.     // if the user hits apply when we dont have a valid display device
  1929.     // force VGA mode in the registry and restart.
  1930.     //
  1931.     if (gbExecMode    == EXEC_INVALID_MODE &&
  1932.         gbInvalidMode == EXEC_INVALID_DISPLAY_DEVICE)
  1933.     {
  1934.         NukeDisplaySettings();
  1935.         PropSheet_RestartWindows(ghwndPropSheet);
  1936.         return TRUE;
  1937.     }
  1938.     //
  1939.     // if we are in VGA fallback mode, and the user stays in VGA mode
  1940.     // also nuke all settings from the registry.
  1941.     //
  1942.     if (gbExecMode    == EXEC_INVALID_MODE &&
  1943.         gbInvalidMode == EXEC_INVALID_DISPLAY_MODE &&
  1944.         _pPrimaryDevice->pds->GetColorBits() == 4)
  1945.     {
  1946.         NukeDisplaySettings();
  1947.         PropSheet_RestartWindows(ghwndPropSheet);
  1948.         return TRUE;
  1949.     }
  1950. #endif
  1951.     // Test the new settings first
  1952.     iTestResult = _SaveDisplaySettings(CDS_TEST);
  1953.     if (iTestResult == DISP_CHANGE_RESTART)
  1954.         bReboot = TRUE;
  1955.     else if (iTestResult < 0)
  1956.         return FALSE;
  1957.     
  1958.     // Ask first and then change the settings.
  1959.     if (!bReboot && _AnyColorChange())
  1960.     {
  1961.         int iDynaResult = AskDynaCDS(_hDlg);
  1962.         if (iDynaResult == -1)
  1963.             return FALSE;
  1964.         else if (iDynaResult == 0)
  1965.             bReboot = TRUE;
  1966.     }
  1967.     if (!bReboot && _AnyResolutionChange())
  1968.     {
  1969.         TCHAR szWarnFlicker[510];
  1970.         LPTSTR pstr;
  1971.         LoadString(hInstance, IDS_WARNFLICK1, szWarnFlicker, SIZEOF(szWarnFlicker));
  1972.         pstr = szWarnFlicker + lstrlen(szWarnFlicker);
  1973.         LoadString(hInstance, IDS_WARNFLICK2, pstr, SIZEOF(szWarnFlicker) - (pstr - (LPTSTR)szWarnFlicker));
  1974.         if (ShellMessageBox(hInstance, GetParent(_hDlg), szWarnFlicker, NULL, MB_OKCANCEL | MB_ICONINFORMATION) != IDOK)
  1975.             return FALSE;
  1976.         bTest = TRUE;
  1977.     }
  1978.     iSave = _SaveDisplaySettings(CDS_UPDATEREGISTRY | CDS_NORESET);
  1979.     // This shouldn't fail because we already tested it above.
  1980.     ASSERT(iSave >= 0);
  1981.     if (iSave == DISP_CHANGE_RESTART)
  1982.         bReboot = TRUE;
  1983.     else if ((iSave < 0) || (bTest && !_TestNewSettingsChange()))
  1984.     {
  1985.         _RestoreDisplaySettings();
  1986.         return FALSE;
  1987.     }
  1988.     else
  1989.         _ConfirmDisplaySettingsChange();
  1990.     
  1991.     if (bReboot)
  1992.         PropSheet_RestartWindows(ghwndPropSheet);
  1993.     return TRUE;
  1994. }
  1995. CMultiMon::_HandleHScroll(HWND hwndTB, int iCode, int iPos)
  1996. {
  1997.     int cRes = SendMessage(hwndTB, TBM_GETRANGEMAX, TRUE, 0);
  1998.     int iRes = _pCurDevice->pds->GetCurResolution();
  1999.     switch(iCode ) {
  2000.         case TB_LINEUP:
  2001.         case TB_PAGEUP:
  2002.             if (iRes != 0)
  2003.                 iRes--;
  2004.             break;
  2005.         case TB_LINEDOWN:
  2006.         case TB_PAGEDOWN:
  2007.             if (++(iRes) >= cRes)
  2008.                 iRes = cRes;
  2009.             break;
  2010.         case TB_BOTTOM:
  2011.             iRes = cRes;
  2012.             break;
  2013.         case TB_TOP:
  2014.             iRes = 0;
  2015.             break;
  2016.         case TB_THUMBTRACK:
  2017.         case TB_THUMBPOSITION:
  2018.             iRes = iPos;
  2019.             break;
  2020.         default:
  2021.             return FALSE;
  2022.     }
  2023.     _pCurDevice->pds->ChangeResolution(iRes);
  2024.     _VerifyPrimaryMode(TRUE);
  2025.     if ( (gbExecMode == EXEC_NORMAL) ||
  2026.          (gbExecMode == EXEC_INVALID_MODE) ||
  2027.          (gbExecMode == EXEC_DETECT) ) {
  2028.         //
  2029.         // Set the apply button if resolution has changed
  2030.         //
  2031.         if (_pCurDevice->pds->IsResolutionChanged())
  2032.             SetDirty();
  2033.         return 0;
  2034.     }
  2035.     return TRUE;
  2036. }
  2037. void CMultiMon::_ForwardToChildren(UINT msg, WPARAM wParam, LPARAM lParam)
  2038. {
  2039.     HWND hwndC = GetDlgItem(_hDlg, IDC_SCREENSIZE);
  2040.     if (hwndC)
  2041.         SendMessage(hwndC, msg, wParam, lParam);
  2042. }
  2043. LRESULT CALLBACK CMultiMon::WndProc(UINT message, WPARAM wParam, LPARAM lParam)
  2044. {
  2045.     NMHDR FAR *lpnm;
  2046.     HWND hwndC;
  2047.     HWND hwndSample;
  2048.     HBITMAP hbm;
  2049.     
  2050.     switch (message)
  2051.     {
  2052.     case WM_NOTIFY:
  2053.         lpnm = (NMHDR FAR *)lParam;
  2054.         switch (lpnm->code)
  2055.         {
  2056.             case PSN_APPLY:
  2057.                 if (IsDirty())
  2058.                 {
  2059.                     _HandleApply();
  2060.                     SetDirty(FALSE);
  2061.                     // if the close flag is false, rebuild the settings
  2062.                     if (((PSHNOTIFY*)lParam)->lParam == FALSE)
  2063.                         _RebuildDisplaySettings(TRUE);
  2064.                 }
  2065.                 break;
  2066.             default:
  2067.                 return FALSE;
  2068.         }
  2069.         break;
  2070.     case WM_CTLCOLORSTATIC:
  2071.         if (GetDlgCtrlID((HWND)lParam) == IDC_DISPLAYDESK)
  2072.         {
  2073.             return (BOOL)(UINT)GetSysColorBrush(COLOR_APPWORKSPACE);
  2074.         }
  2075.         return FALSE;
  2076.     case WM_COMMAND:
  2077.         switch (GET_WM_COMMAND_ID(wParam, lParam))
  2078.         {
  2079. #ifdef WINNT
  2080.         case IDC_DISPLAYPRIME:
  2081. #endif
  2082.         case IDC_DISPLAYUSEME:
  2083.         {
  2084.             //
  2085.             // Don't pop up warning dialog box if this display is already attached
  2086.             // or if there are already more than 1 display
  2087.             //
  2088.             BOOL bWarn = (!_pCurDevice->pds->IsAttached() && GetNumberOfAttachedDisplays() == 1);
  2089.             
  2090.             if (!ToggleAttached(_pCurDevice, GET_WM_COMMAND_ID(wParam, lParam) ==
  2091.                                 IDC_DISPLAYPRIME))
  2092.                 return FALSE;
  2093.             
  2094.             if ((GET_WM_COMMAND_ID(wParam, lParam) == IDC_DISPLAYUSEME) && bWarn
  2095.                 && !WarnUserAboutCompatibility(_hDlg))
  2096.             {
  2097.                 _pCurDevice->pds->SetAttached(FALSE);
  2098.                 UpdateActiveDisplay(_pCurDevice);
  2099.             }
  2100.             else
  2101.             {
  2102.                 hwndC = GetDlgItem(_hwndDesk, (int) _pCurDevice);
  2103.                 HandleMonitorChange(hwndC, TRUE);
  2104.             }
  2105.             return TRUE;
  2106.         }  
  2107.         case IDC_DISPLAYLIST:
  2108.             switch (GET_WM_COMMAND_CMD(wParam, lParam))
  2109.             {
  2110.             case CBN_DBLCLK:
  2111.                 goto DoDeviceSettings;
  2112.             case CBN_SELCHANGE:
  2113.                 UpdateActiveDisplay(NULL);
  2114.                 break;
  2115.             default:
  2116.                 return FALSE;
  2117.             }
  2118.             break;
  2119.         case IDC_DISPLAYPROPERTIES:
  2120.             switch (GET_WM_COMMAND_CMD(wParam, lParam))
  2121.             {
  2122.             DoDeviceSettings:
  2123.             case BN_CLICKED:
  2124.                 if (IsWindowEnabled(GetDlgItem(_hDlg, IDC_DISPLAYPROPERTIES)))
  2125.                 {
  2126.                     _DoAdvancedSettingsSheet();
  2127.                     _RebuildDisplaySettings(TRUE);
  2128.                 }
  2129.                 break;
  2130.             default:
  2131.                 return FALSE;
  2132.             }
  2133.             break;
  2134.         case IDC_COLORBOX:
  2135.             switch(GET_WM_COMMAND_CMD(wParam, lParam))
  2136.             {
  2137.                 
  2138.                 case CBN_SELCHANGE:
  2139.                 {
  2140.                     HWND hwndColorBox = GetDlgItem(_hDlg, IDC_COLORBOX);
  2141.                     int iClr = ComboBox_GetCurSel(hwndColorBox);
  2142.                     
  2143.                     if (iClr != CB_ERR ) {
  2144.                         _pCurDevice->pds->ChangeColor(iClr);
  2145.                         _VerifyPrimaryMode(TRUE);
  2146.                     }
  2147.                     break;
  2148.                 }
  2149.                 default:
  2150.                     break;
  2151.             }
  2152.             break;
  2153.             
  2154.         default:
  2155.             return FALSE;
  2156.         }
  2157.         //
  2158.         // Enable the apply button only if we are not in setup.
  2159.         //
  2160.         
  2161.         if ( (gbExecMode == EXEC_NORMAL) ||
  2162.              (gbExecMode == EXEC_INVALID_MODE) ||
  2163.              (gbExecMode == EXEC_DETECT) ) {
  2164.             
  2165.             //
  2166.             // Set the apply button if something changed
  2167.             //
  2168.             if (_pCurDevice->pds->IsResolutionChanged() ||
  2169.                 _pCurDevice->pds->IsColorChanged() ||
  2170.                 _pCurDevice->pds->IsFrequencyChanged())
  2171.             {
  2172.                 SetDirty();
  2173.             }
  2174.             return TRUE;
  2175.         }
  2176.         break;
  2177.     case WM_HSCROLL:
  2178.         _HandleHScroll((HWND)lParam, (int) LOWORD(wParam), (int) HIWORD(wParam));
  2179.         break;
  2180.         
  2181.     case WM_HELP:
  2182.         WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, TEXT("mds.hlp"), HELP_WM_HELP,
  2183.             (DWORD)(LPTSTR)sc_MultiMonitorHelpIds);
  2184.         break;
  2185.     case WM_CONTEXTMENU:
  2186.         WinHelp((HWND)wParam, TEXT("mds.hlp"), HELP_CONTEXTMENU,
  2187.             (DWORD)(LPTSTR)sc_MultiMonitorHelpIds);
  2188.         break;
  2189.     case WM_WININICHANGE:
  2190.         _ForwardToChildren(message, wParam, lParam);
  2191.         break;
  2192.     case WM_SYSCOLORCHANGE:
  2193.         if (_himl)
  2194.             ImageList_SetBkColor(_himl, GetSysColor(COLOR_APPWORKSPACE));
  2195.         _ForwardToChildren(message, wParam, lParam);
  2196.         break;
  2197.     case WM_DISPLAYCHANGE:
  2198.         _RebuildDisplaySettings();
  2199.         _RedrawDeskPreviews();
  2200.         _ForwardToChildren(message, wParam, lParam);
  2201.         break;
  2202.         
  2203.     case WM_DESTROY:
  2204.         TraceMsg(TF_GENERAL, "WndProc:: WM_DESTROY");
  2205.         hwndSample = GetDlgItem(_hDlg, IDC_COLORSAMPLE);
  2206.         if (hbm = (HBITMAP)SendMessage(hwndSample, STM_SETIMAGE, IMAGE_BITMAP, NULL))
  2207.             DeleteObject(hbm);
  2208.         
  2209.         if (_NumDevices == 1)
  2210.         {
  2211.             hwndSample = GetDlgItem(_hDlg, IDC_SCREENSAMPLE);
  2212.             if (hbm = (HBITMAP)SendMessage(hwndSample, STM_SETIMAGE, IMAGE_BITMAP, NULL))
  2213.                 DeleteObject(hbm);
  2214.             
  2215.             if (_hbmScrSample && (GetObjectType(_hbmScrSample) != 0))
  2216.                 DeleteObject(_hbmScrSample);
  2217.             if (_hbmMonitor && (GetObjectType(_hbmMonitor) != 0))
  2218.                 DeleteObject(_hbmMonitor);
  2219.         }
  2220.         _DestroyDisplaySettings();
  2221.         break;
  2222.         
  2223. #ifdef WINNT
  2224.     case MSG_DSP_SETUP_MESSAGE:
  2225.         return _InitMessage();
  2226. #endif
  2227.         // MultiMonitor CPL specific messages
  2228.     case MM_REDRAWPREVIEW:
  2229.         _RedrawDeskPreviews();
  2230.         break;
  2231.     default:
  2232.         return FALSE;
  2233.     }
  2234.     return TRUE;
  2235. }
  2236. // IUnknown methods
  2237. HRESULT CMultiMon::QueryInterface(REFIID riid, LPVOID * ppvObj)
  2238.     // ppvObj must not be NULL
  2239.     ASSERT(ppvObj != NULL);
  2240.     
  2241.     if (ppvObj == NULL)
  2242.         return E_INVALIDARG;
  2243.     *ppvObj = NULL;
  2244.     if (IsEqualIID(riid, IID_IUnknown))
  2245.         *ppvObj = SAFECAST(this, IUnknown *);
  2246.     else if (IsEqualIID(riid, IID_IMultiMonConfig))
  2247.         *ppvObj = SAFECAST(this, IMultiMonConfig *);
  2248.     else
  2249.         return E_NOINTERFACE;
  2250.      
  2251.     
  2252.     AddRef();
  2253.     return S_OK;
  2254. }
  2255. ULONG CMultiMon::AddRef()
  2256. {
  2257.     _cRef++;
  2258.     return _cRef;
  2259. }
  2260. ULONG CMultiMon::Release()
  2261. {
  2262.     _cRef--;
  2263.     if (_cRef > 0)
  2264.         return _cRef;
  2265.     delete this;
  2266.     return 0;
  2267. }
  2268. // IMultiMonConfig methods
  2269. HRESULT CMultiMon::Initialize(HWND hwndHost, WNDPROC pfnWndProc, DWORD dwReserved)
  2270. {
  2271.     WNDCLASS wc = {0};
  2272.     HRESULT hr = E_FAIL;
  2273.     if (hwndHost && RegisterPreviewWindowClass(pfnWndProc))
  2274.     {
  2275.         _hwndDesk = hwndHost;
  2276.         if (_InitDisplaySettings(TRUE))
  2277.             hr = S_OK;
  2278.     }
  2279.     return hr;
  2280. }
  2281. HRESULT CMultiMon::GetNumberOfMonitors(int * pCMon, DWORD dwReserved)
  2282. {
  2283.     if (pCMon)
  2284.     {
  2285.         *pCMon = _NumDevices;
  2286.         return S_OK;
  2287.     }
  2288.     return E_FAIL;
  2289. }
  2290. HRESULT CMultiMon::GetMonitorData(int iMonitor, MonitorData * pmd, DWORD dwReserved)
  2291. {
  2292.     ASSERT(pmd);
  2293.     if ((pmd == NULL) || (iMonitor >= _NumDevices))
  2294.         return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
  2295.     PMULTIMON_DEVICE pDevice = &_Devices[iMonitor];
  2296.     pmd->dwSize = SIZEOF(MonitorData);
  2297.     if ( pDevice->pds->IsPrimary() )
  2298.         pmd->dwStatus |= MD_PRIMARY;
  2299.     if ( pDevice->pds->IsAttached() )
  2300.         pmd->dwStatus |= MD_ATTACHED;
  2301.     pDevice->pds->GetCurPosition(&pmd->rcPos);
  2302.     return S_OK;
  2303. }
  2304. HRESULT CMultiMon::Paint(int iMonitor, DWORD dwReserved)
  2305. {
  2306.     _RedrawDeskPreviews();
  2307.     return S_OK;
  2308. }
  2309. STDAPI CMultiMonConfig_CreateInstance(IUnknown* pUnkOuter, REFIID riid, OUT LPVOID * ppvOut)
  2310. {
  2311.     HRESULT hr = E_OUTOFMEMORY;
  2312.     *ppvOut = NULL;                     // null the out param
  2313.     if (pUnkOuter)
  2314.         return CLASS_E_NOAGGREGATION;
  2315.     CMultiMon *pmm = new CMultiMon;
  2316.     if (pmm)
  2317.     {
  2318.         hr = pmm->QueryInterface(riid, ppvOut);
  2319.         pmm->Release();
  2320.     }
  2321.     return hr;
  2322. }
  2323. /*----------------------------------------------------------------------------
  2324. ----------------------------------------------------------------------------*/
  2325. HFONT GetFont(LPRECT prc)
  2326. {
  2327.     LOGFONT lf;
  2328.     FillMemory(&lf,  SIZEOF(lf), 0);
  2329.     lf.lfWeight = FW_EXTRABOLD;
  2330.     lf.lfHeight = prc->bottom - prc->top;
  2331.     lf.lfWidth  = 0;
  2332.     lf.lfPitchAndFamily = FF_SWISS;
  2333.     lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
  2334.     return CreateFontIndirect(&lf);
  2335. }
  2336. /*----------------------------------------------------------------------------
  2337. ----------------------------------------------------------------------------*/
  2338. #define HANG_TIME 2500
  2339. LONG CALLBACK BigNumberWindowProc(HWND hwnd, UINT msg,WPARAM wParam,LPARAM lParam)
  2340. {
  2341.     TCHAR ach[80];
  2342.     HFONT hfont;
  2343.     RECT  rc;
  2344.     HDC   hdc;
  2345.     HRGN  hrgnTxtA;
  2346.     HRGN  hrgnTxtB;
  2347.     HRGN  hrgnTxtU;
  2348.     PAINTSTRUCT ps;
  2349.     switch (msg)
  2350.     {
  2351.     case WM_CREATE:
  2352.         break;
  2353.     case WM_SIZE:
  2354.         GetWindowText(hwnd, ach, sizeof(ach));
  2355.         GetClientRect(hwnd, &rc);
  2356.         hfont = GetFont(&rc);
  2357.         hdc = GetDC(hwnd);
  2358.         SelectObject(hdc, hfont);
  2359.         BeginPath(hdc);
  2360.             SetBkMode(hdc, TRANSPARENT);
  2361.             TextOut(hdc,0,0,ach,lstrlen(ach));
  2362.         EndPath(hdc);
  2363.         hrgnTxtA = PathToRegion(hdc);
  2364.         hrgnTxtB = CreateRectRgn(0, 0, 1, 1);
  2365.         hrgnTxtU = CreateRectRgn(0, 0, 1, 1);
  2366.         // Copy region A (our number) to a new region (B)
  2367.         CombineRgn(hrgnTxtB, hrgnTxtA, NULL, RGN_COPY);
  2368.         // Offset region B by two pixels in x and y
  2369.         OffsetRgn(hrgnTxtB, 2, 2);
  2370.         // Now create a region that is the union of A and B.
  2371.         // This gives us our number with 2 pixels worth of
  2372.         // room for shadowing.
  2373.         CombineRgn(hrgnTxtU, hrgnTxtA, hrgnTxtB, RGN_OR);
  2374.         DeleteObject(hrgnTxtA);
  2375.         DeleteObject(hrgnTxtB);
  2376.         SetWindowRgn(hwnd,hrgnTxtU,TRUE);
  2377.         ReleaseDC(hwnd, hdc);
  2378.         DeleteObject(hfont);
  2379.         break;
  2380.     case WM_TIMER:
  2381.         DestroyWindow(hwnd);
  2382.         return 0;
  2383.     case WM_PAINT:
  2384.         GetWindowText(hwnd, ach, sizeof(ach));
  2385.         GetClientRect(hwnd, &rc);
  2386.         hfont = GetFont(&rc);
  2387.         hdc = BeginPaint(hwnd, &ps);
  2388.         PatBlt(hdc, 0, 0, rc.right, rc.bottom, BLACKNESS | NOMIRRORBITMAP);
  2389.         SelectObject(hdc, hfont);
  2390.         SetTextColor(hdc, 0xFFFFFF);
  2391.         SetBkMode(hdc, TRANSPARENT);
  2392.         TextOut(hdc,0,0,ach,lstrlen(ach));
  2393.         EndPaint(hwnd, &ps);
  2394.         DeleteObject(hfont);
  2395.         break;
  2396.     }
  2397.     return DefWindowProc(hwnd,msg,wParam,lParam);
  2398. }
  2399. int Bail()
  2400. {
  2401.     POINT pt;
  2402.     POINT pt0;
  2403.     DWORD time0;
  2404.     DWORD d;
  2405.     d     = GetDoubleClickTime();
  2406.     time0 = GetMessageTime();
  2407.     pt0.x = (int)(short)LOWORD(GetMessagePos());
  2408.     pt0.y = (int)(short)HIWORD(GetMessagePos());
  2409.     if (GetTickCount()-time0 > d)
  2410.         return 2;
  2411.     if (!((GetAsyncKeyState(VK_LBUTTON) | GetAsyncKeyState(VK_RBUTTON)) & 0x8000))
  2412.         return 1;
  2413.     GetCursorPos(&pt);
  2414.     if ((pt.y - pt0.y) > 2 || (pt.y - pt0.y) < -2)
  2415.         return 1;
  2416.     if ((pt.x - pt0.x) > 2 || (pt.x - pt0.x) < -2)
  2417.         return 1;
  2418.     return 0;
  2419. }
  2420. void FlashText(LPCTSTR sz, LPRECT prc, BOOL fWait)
  2421. {
  2422.     HFONT hfont;
  2423.     SIZE  size;
  2424.     HDC   hdc;
  2425.     int   i;
  2426.     static HWND hWnd;
  2427.     if (hWnd && IsWindow(hWnd))
  2428.     {
  2429.         DestroyWindow(hWnd);
  2430.         hWnd = NULL;
  2431.     }
  2432.     if (sz == NULL)
  2433.         return;
  2434.     if (fWait)
  2435.     {
  2436.         while ((i=Bail()) == 0)
  2437.             ;
  2438.         if (i == 1)
  2439.             return;
  2440.     }
  2441.     hdc = GetDC(NULL);
  2442.     hfont = GetFont(prc);
  2443.     SelectObject(hdc, hfont);
  2444.     GetTextExtentPoint(hdc, sz, lstrlen(sz), &size);
  2445.     ReleaseDC(NULL, hdc);
  2446.     DeleteObject(hfont);
  2447.     WNDCLASS    cls;
  2448.     cls.hCursor        = LoadCursor(NULL,IDC_ARROW);
  2449.     cls.hIcon          = NULL;
  2450.     cls.lpszMenuName   = NULL;
  2451.     cls.lpszClassName  = TEXT("MonitorNumber32");
  2452.     cls.hbrBackground  = (HBRUSH)(COLOR_DESKTOP + 1);
  2453.     cls.hInstance      = hInstance;
  2454.     cls.style          = CS_VREDRAW | CS_HREDRAW;
  2455.     cls.lpfnWndProc    = (WNDPROC)BigNumberWindowProc;
  2456.     cls.cbWndExtra     = 0;
  2457.     cls.cbClsExtra     = 0;
  2458.     RegisterClass(&cls);
  2459.     hWnd = CreateWindowEx(
  2460.         WS_EX_TOPMOST, //WS_BORDER,
  2461.         TEXT("MonitorNumber32"), sz,
  2462.         WS_POPUP,
  2463.         (prc->right  + prc->left - size.cx)/2,
  2464.         (prc->bottom + prc->top  - size.cy)/2,
  2465.         size.cx,
  2466.         size.cy,
  2467.         NULL,
  2468.         NULL,
  2469.         hInstance,
  2470.         NULL);
  2471.     ShowWindow(hWnd, SW_SHOW);
  2472.     UpdateWindow(hWnd);
  2473.     SetTimer(hWnd, 1, HANG_TIME, NULL);
  2474. }
  2475. void PaintCurrentDevice(HDC hdc, LPRECT prc)
  2476. {
  2477.     FillRect(hdc, prc, GetSysColorBrush(COLOR_ACTIVECAPTION));
  2478. }
  2479. void DrawMonitorNum(HDC hdc, int w, int h, LPCTSTR sz, BOOL fDrawBackground=TRUE)
  2480. {
  2481.     HFONT    hfont;
  2482.     HFONT    hfontT;
  2483.     RECT     rc;
  2484.     COLORREF rgb;
  2485.     COLORREF rgbDesk;
  2486.     rc.left   = MON_X * w / MON_W;
  2487.     rc.top    = MON_Y * h / MON_H;
  2488.     rc.right  = rc.left + ((MON_DX * w + MON_W-1) / MON_W);
  2489.     rc.bottom = rc.top  + ((MON_DY * h + MON_H-1) / MON_H);
  2490.     rgb     = GetSysColor(COLOR_CAPTIONTEXT);
  2491.     rgbDesk = GetSysColor(COLOR_DESKTOP);
  2492.     if (fDrawBackground)
  2493.         FillRect(hdc, &rc, GetSysColorBrush(COLOR_DESKTOP));
  2494.     if (rgbDesk == rgb)
  2495.         rgb = GetSysColor(COLOR_WINDOWTEXT);
  2496.     if (rgbDesk == rgb)
  2497.         rgb = rgbDesk ^ 0x00FFFFFF;
  2498.     SetTextColor(hdc, rgb);
  2499.     hfont = GetFont(&rc);
  2500.     hfontT = (HFONT)SelectObject(hdc, hfont);
  2501.     SetTextAlign(hdc, TA_CENTER | TA_TOP);
  2502.     SetBkMode(hdc, TRANSPARENT);
  2503.     ExtTextOut(hdc, (rc.left+rc.right)/2, rc.top, 0, NULL, sz, lstrlen(sz), NULL);
  2504.     SelectObject(hdc, hfontT);
  2505.     DeleteObject(hfont);
  2506. }
  2507. BOOL MakeMonitorBitmap(int w, int h, LPCTSTR sz, HBITMAP *pBitmap, HBITMAP *pMaskBitmap, int cx, int cy)
  2508. {
  2509.     HBITMAP hbm;        // 128x128 bitmap we will return
  2510.     HBITMAP hbmT;       // bitmap loaded from resource
  2511.     HBITMAP hbmM;       // mask bitmap
  2512.     HDC     hdc;        // work dc
  2513.     HDC     hdcS;       // screen dc
  2514.     HDC     hdcT;       // another work dc
  2515.     HDC     hdcM;       // another work dc
  2516.     RECT    rc;
  2517.     ASSERT(w <= cx);
  2518.     ASSERT(h <= cy);
  2519.     hdcS = GetDC(NULL);
  2520.     hdc  = CreateCompatibleDC(hdcS);
  2521.     hdcT = CreateCompatibleDC(hdcS);
  2522.     hdcM = CreateCompatibleDC(hdcS);
  2523.     hbm  = CreateCompatibleBitmap(hdcS, cx, cy);
  2524.     hbmM = CreateBitmap(cx,cy,1,1,NULL);
  2525.     hbmT = (HBITMAP)LoadImage(hInstance, MAKEINTRESOURCE(BMP_MONITOR2), IMAGE_BITMAP, w, h, 0);
  2526.     ReleaseDC(NULL,hdcS);
  2527.     SelectObject(hdc, hbm);
  2528.     SelectObject(hdcT,hbmT);
  2529.     SelectObject(hdcM,hbmM);
  2530.     //
  2531.     // fill bitmap with transparent color
  2532.     //
  2533.     SetBkColor(hdc,GetPixel(hdcT, 0, h-1));
  2534.     SetRect(&rc, 0, 0, cx, cy);
  2535.     ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
  2536.     //
  2537.     // copy bitmap to upper-left of bitmap
  2538.     //
  2539.     BitBlt(hdc,0,0,w,h,hdcT,0,0,SRCCOPY);
  2540.     //
  2541.     // make mask
  2542.     //
  2543.     BitBlt(hdcM,0,0,cx,cy,hdc,0,0,SRCCOPY);
  2544.     //
  2545.     // draw the monitor number in the bitmap (in the right place)
  2546.     //
  2547.     DrawMonitorNum(hdc, w, h, sz);
  2548.     DeleteDC(hdc);
  2549.     DeleteDC(hdcT);
  2550.     DeleteDC(hdcM);
  2551.     DeleteObject(hbmT);
  2552.     *pBitmap     = hbm;
  2553.     *pMaskBitmap = hbmM;
  2554.     return TRUE;
  2555. }
  2556. //
  2557. // SnapMonitorRect
  2558. //
  2559. // called while the user is moving a monitor window (WM_MOVING)
  2560. // if the CTRL key is not down we will snap the window rect
  2561. // to the edge of one of the other monitors.
  2562. //
  2563. // this is done so the user can easily align monitors
  2564. //
  2565. // NOTE pDevice->Snap must be initialized to 0,0 in WM_ENTERSIZEMOVE
  2566. //
  2567. void SnapMonitorRect(PMULTIMON_DEVICE pDevice, HWND hwnd, RECT *prc)
  2568. {
  2569.     HWND hwndT;
  2570.     int  d;
  2571.     RECT rcT;
  2572.     RECT rc;
  2573.     //
  2574.     // allow the user to move the window anywhere when the CTRL key is down
  2575.     //
  2576.     if (GetKeyState(VK_CONTROL) & 0x8000)
  2577.         return;
  2578.     //
  2579.     // macros to help in alignment
  2580.     //
  2581.     #define SNAP_DX 6
  2582.     #define SNAP_DY 6
  2583.     #define SNAPX(f,x) 
  2584.         d = rcT.x - rc.f; if (abs(d) <= SNAP_DX) rc.left+=d, rc.right+=d;
  2585.     #define SNAPY(f,y) 
  2586.         d = rcT.y - rc.f; if (abs(d) <= SNAP_DY) rc.top+=d, rc.bottom+=d;
  2587.     //
  2588.     // get current rect and offset it by the amount we have corrected
  2589.     // it so far (this alignes the rect with the position of the mouse)
  2590.     //
  2591.     rc = *prc;
  2592.     OffsetRect(&rc, pDevice->Snap.x, pDevice->Snap.y);
  2593.     //
  2594.     // walk all other windows and snap our window to them
  2595.     //
  2596.     for (hwndT = GetWindow(hwnd,  GW_HWNDFIRST); hwndT;
  2597.          hwndT = GetWindow(hwndT, GW_HWNDNEXT))
  2598.     {
  2599.         if (hwndT == hwnd)
  2600.             continue;
  2601.         GetWindowRect(hwndT, &rcT);
  2602.         InflateRect(&rcT,SNAP_DX,SNAP_DY);
  2603.         if (IntersectRect(&rcT, &rcT, &rc))
  2604.         {
  2605.             GetWindowRect(hwndT, &rcT);
  2606.             SNAPX(right,left);  SNAPY(bottom,top);
  2607.             SNAPX(right,right); SNAPY(bottom,bottom);
  2608.             SNAPX(left,left);   SNAPY(top,top);
  2609.             SNAPX(left,right);  SNAPY(top,bottom);
  2610.         }
  2611.     }
  2612.     //
  2613.     // adjust the amount we have snap'ed so far, and return the new rect
  2614.     //
  2615.     pDevice->Snap.x += prc->left - rc.left;
  2616.     pDevice->Snap.y += prc->top  - rc.top;
  2617.     *prc = rc;
  2618. }
  2619. LONG CALLBACK MonitorWindowProc(HWND hwnd, UINT msg,WPARAM wParam,LPARAM lParam)
  2620. {
  2621.     PAINTSTRUCT ps;
  2622.     HDC hdc;
  2623.     RECT rc;
  2624.     int w,h;
  2625.     TCHAR ach[80];
  2626.     PMULTIMON_DEVICE pDevice;
  2627.     HWND hDlg = NULL;
  2628.     RECT rcPos;
  2629.     CMultiMon * pcmm = (CMultiMon *) GetWindowLong(hwnd, 0);
  2630.     switch (msg)
  2631.     {
  2632.         case WM_CREATE:
  2633.             ASSERT(((LPCREATESTRUCT)lParam)->lpCreateParams);
  2634.             SetWindowLong(hwnd, 0, (LONG)((LPCREATESTRUCT)lParam)->lpCreateParams);
  2635.             break;
  2636.         case WM_NCCREATE:
  2637.             // turn off RTL_MIRRORED_WINDOW in GWL_EXSTYLE
  2638.             SHSetWindowBits(hwnd, GWL_EXSTYLE, RTL_MIRRORED_WINDOW, 0);
  2639.             break;
  2640.         case WM_NCHITTEST:
  2641.             pDevice = (PMULTIMON_DEVICE) GetDlgCtrlID(hwnd);
  2642.             if (pDevice && pDevice->pds->IsAttached())
  2643.                 return HTCAPTION;
  2644.             break;
  2645.             
  2646.         case WM_NCLBUTTONDBLCLK:
  2647.             FlashText(NULL,NULL,FALSE);
  2648.             hDlg = GetParent(GetParent(hwnd));
  2649.             PostMessage(hDlg, WM_COMMAND, MAKEWPARAM(IDC_DISPLAYPROPERTIES, BN_CLICKED), (LPARAM)hwnd );
  2650.             break;
  2651.         case WM_COMMAND:
  2652.             switch (GET_WM_COMMAND_ID(wParam, lParam))
  2653.             {
  2654.             case IDC_DISPLAYPRIME:
  2655.             case IDC_DISPLAYUSEME:
  2656.             case IDC_DISPLAYPROPERTIES:
  2657.                 hDlg = GetParent(GetParent(hwnd));
  2658.                 PostMessage(hDlg, WM_COMMAND, wParam, lParam);
  2659.                 break;
  2660.             case IDC_FLASH:
  2661.                 pDevice = (PMULTIMON_DEVICE) GetDlgCtrlID(hwnd);
  2662.                 pDevice->pds->GetOrigPosition(&rcPos);
  2663.                 if (!IsRectEmpty(&rcPos))
  2664.                 {
  2665.                     GetWindowText(hwnd, ach, ARRAYSIZE(ach));
  2666.                     FlashText(ach, &rcPos, FALSE);
  2667.                 }
  2668.             }
  2669.             break;
  2670.         case WM_INITMENUPOPUP:
  2671.             pDevice = (PMULTIMON_DEVICE) GetDlgCtrlID(hwnd);
  2672.             CheckMenuItem((HMENU)wParam, IDC_DISPLAYUSEME, pDevice->pds->IsAttached() ? MF_CHECKED : MF_UNCHECKED);
  2673.             CheckMenuItem((HMENU)wParam, IDC_DISPLAYPRIME, pDevice->pds->IsPrimary()  ? MF_CHECKED : MF_UNCHECKED);
  2674.             EnableMenuItem((HMENU)wParam, IDC_FLASH,             pDevice->pds->IsAttached() ? MF_ENABLED : MF_GRAYED);
  2675.             EnableMenuItem((HMENU)wParam, IDC_DISPLAYPROPERTIES, pDevice->pds->IsAttached() ? MF_ENABLED : MF_GRAYED);
  2676. #ifndef WINNT
  2677.             EnableMenuItem((HMENU)wParam, IDC_DISPLAYPRIME, MF_GRAYED);
  2678.             EnableMenuItem((HMENU)wParam, IDC_DISPLAYUSEME, pDevice->pds->IsPrimary() ? MF_GRAYED : MF_ENABLED);
  2679. #endif
  2680.             SetMenuDefaultItem((HMENU)wParam, IDC_DISPLAYPROPERTIES, MF_BYCOMMAND);
  2681.             break;
  2682.         case WM_RBUTTONDOWN:
  2683.         case WM_NCRBUTTONDOWN:
  2684.             hDlg = GetParent(GetParent(hwnd));
  2685.             pDevice = (PMULTIMON_DEVICE) GetDlgCtrlID(hwnd);
  2686.             if (pDevice && pcmm)
  2687.             {
  2688.                 HMENU hmenu;
  2689.                 hmenu = LoadMenu(hInstance, MAKEINTRESOURCE(MENU_MONITOR));
  2690.                 if (hmenu)
  2691.                 {
  2692.                     POINT pt;
  2693.                     pcmm->UpdateActiveDisplay(pDevice);
  2694.                     GetCursorPos(&pt);
  2695.                     TrackPopupMenu(GetSubMenu(hmenu,0), TPM_RIGHTBUTTON,
  2696.                         pt.x, pt.y, 0, hwnd, NULL);
  2697.                     DestroyMenu(hmenu);
  2698.                 }
  2699.             }
  2700.             break;
  2701.         case WM_NCLBUTTONDOWN:
  2702.             BringWindowToTop(hwnd);
  2703.             hDlg = GetParent(GetParent(hwnd));
  2704.             pDevice = (PMULTIMON_DEVICE) GetDlgCtrlID(hwnd);
  2705.             if (pDevice->pds->IsAttached())
  2706.             {
  2707.                 if (pcmm)
  2708.                     pcmm->UpdateActiveDisplay(pDevice);
  2709.                 pDevice->pds->GetOrigPosition(&rcPos);
  2710.                 if (!IsRectEmpty(&rcPos))
  2711.                 {
  2712.                     GetWindowText(hwnd, ach, ARRAYSIZE(ach));
  2713.                     FlashText(ach, &rcPos, TRUE);
  2714.                 }
  2715.             }
  2716.             break;
  2717.         case WM_LBUTTONUP:
  2718.             pDevice = (PMULTIMON_DEVICE) GetDlgCtrlID(hwnd);
  2719.             if (pcmm && !pcmm->QueryNoAttach() && pDevice && !pDevice->pds->IsAttached())
  2720.             {
  2721.                 TCHAR szTurnItOn[400];
  2722.                 TCHAR szTurnOnTitleFormat[30];
  2723.                 TCHAR szTurnOnTitle[110];
  2724.                 
  2725.                 LoadString(hInstance, IDS_TURNONTITLE, szTurnOnTitleFormat, SIZEOF(szTurnOnTitleFormat));
  2726.                 GetWindowText(hwnd, ach, SIZEOF(ach));
  2727.                 wsprintf(szTurnOnTitle, szTurnOnTitleFormat, ach);
  2728.                 LPTSTR pstr = szTurnItOn;
  2729.                 if (pcmm->GetNumberOfAttachedDisplays() <= 1)
  2730.                 {
  2731.                     LoadString(hInstance, IDS_TURNONWARN, szTurnItOn, SIZEOF(szTurnItOn));
  2732.                     pstr += lstrlen(szTurnItOn);
  2733.                 }
  2734.                 
  2735.                 LoadString(hInstance, IDS_TURNITON, pstr, SIZEOF(szTurnItOn));
  2736.                 hDlg = GetParent(GetParent(hwnd));
  2737.                 if (ShellMessageBox(hInstance, hwnd, szTurnItOn, szTurnOnTitle, MB_YESNO | MB_ICONINFORMATION) == IDYES)
  2738.                 {
  2739.                     if (pcmm->ToggleAttached(pDevice, FALSE))
  2740.                     {
  2741.                         pcmm->SetDirty();
  2742.                         pcmm->UpdateActiveDisplay(pDevice);
  2743.                     }
  2744.                 }
  2745.             }
  2746.             break;
  2747.         case WM_ENTERSIZEMOVE:
  2748.             pDevice = (PMULTIMON_DEVICE) GetDlgCtrlID(hwnd);
  2749.             pDevice->Snap.x = 0;
  2750.             pDevice->Snap.y = 0;
  2751.             FlashText(NULL,NULL,FALSE);
  2752.             break;
  2753.         case WM_MOVING:
  2754.             pDevice = (PMULTIMON_DEVICE) GetDlgCtrlID(hwnd);
  2755.             SnapMonitorRect(pDevice, hwnd, (RECT*)lParam);
  2756.             break;
  2757.         case WM_EXITSIZEMOVE:
  2758.             if (pcmm)
  2759.                 pcmm->HandleMonitorChange(hwnd, FALSE);
  2760.             RedrawWindow(GetParent(hwnd), NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN);
  2761.             break;
  2762.         case WM_DESTROY:
  2763.             SetWindowLong(hwnd, 0, NULL);
  2764.             break;
  2765.         case WM_ERASEBKGND:
  2766.             //GetClientRect(hwnd, &rc);
  2767.             //FillRect((HDC)wParam, &rc, GetSysColorBrush(COLOR_APPWORKSPACE));
  2768.             return 0L;
  2769.         case WM_PAINT:
  2770.             hdc = BeginPaint(hwnd,&ps);
  2771.             GetWindowText(hwnd, ach, ARRAYSIZE(ach));
  2772.             GetClientRect(hwnd, &rc);
  2773.             w = rc.right;
  2774.             h = rc.bottom;
  2775.             pDevice = (PMULTIMON_DEVICE) GetDlgCtrlID(hwnd);
  2776.             if (pDevice->w != w || pDevice->h != h)
  2777.             {
  2778.                 HBITMAP hbm, hbmMask;
  2779.                 int cx,cy;
  2780.                 pDevice->w = w;
  2781.                 pDevice->h = h;
  2782.                 ImageList_GetIconSize(pDevice->himl, &cx, &cy);
  2783.                 MakeMonitorBitmap(w,h,ach,&hbm,&hbmMask,cx,cy);
  2784.                 ImageList_Replace(pDevice->himl,pDevice->iImage,hbm,hbmMask);
  2785.                 DeleteObject(hbm);
  2786.                 DeleteObject(hbmMask);
  2787.             }
  2788.             if (!pDevice->pds->IsAttached())
  2789.             {
  2790.                 FillRect(hdc, &rc, GetSysColorBrush(COLOR_APPWORKSPACE));
  2791.                 ImageList_DrawEx(pDevice->himl,pDevice->iImage,hdc,0,0,w,h,
  2792.                     CLR_DEFAULT,CLR_NONE,ILD_BLEND50);
  2793.             }
  2794.             else if (pcmm && (pDevice == pcmm->GetCurDevice()))
  2795.             {
  2796.                 ImageList_DrawEx(pDevice->himl,pDevice->iImage,hdc,0,0,w,h,
  2797.                     CLR_DEFAULT,CLR_DEFAULT,ILD_BLEND50);
  2798.                 DrawMonitorNum(hdc, w, h, ach, FALSE);
  2799.             }
  2800.             else
  2801.             {
  2802.                 ImageList_DrawEx(pDevice->himl,pDevice->iImage,hdc,0,0,w,h,
  2803.                     CLR_DEFAULT,CLR_DEFAULT,ILD_NORMAL);
  2804.             }
  2805.             EndPaint(hwnd,&ps);
  2806.             return 0L;
  2807.     }
  2808.     return DefWindowProc(hwnd,msg,wParam,lParam);
  2809. }