Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5636 lines
170 KiB

  1. /*****************************************************************************\
  2. FILE: SettingsPg.cpp
  3. DESCRIPTION:
  4. This code will display a "Settings" tab in the
  5. "Display Properties" dialog
  6. BryanSt 1/05/2001 Updated and Converted to C++
  7. Copyright (C) Microsoft Corp 1993-2001. All rights reserved.
  8. \*****************************************************************************/
  9. #include "priv.h"
  10. #include "SettingsPg.h"
  11. #include "DisplaySettings.h"
  12. #include "shlobjp.h"
  13. #include "shlwapi.h"
  14. #include "ntreg.hxx"
  15. #include "AdvAppearPg.h"
  16. #include <tchar.h>
  17. #include <dbt.h>
  18. #include <oleacc.h>
  19. #include <devguid.h>
  20. #define EIS_NOT_INVALID 0x00000001
  21. #define EIS_EXEC_INVALID_NEW_DRIVER 0x00000002
  22. #define EIS_EXEC_INVALID_DEFAULT_DISPLAY_MODE 0x00000003
  23. #define EIS_EXEC_INVALID_DISPLAY_DRIVER 0x00000004
  24. #define EIS_EXEC_INVALID_OLD_DISPLAY_DRIVER 0x00000005
  25. #define EIS_EXEC_INVALID_16COLOR_DISPLAY_MODE 0x00000006
  26. #define EIS_EXEC_INVALID_DISPLAY_MODE 0x00000007
  27. #define EIS_EXEC_INVALID_CONFIGURATION 0x00000008
  28. #define EIS_EXEC_INVALID_DISPLAY_DEVICE 0x00000009
  29. LRESULT CALLBACK MonitorWindowProc(HWND hwnd, UINT msg,WPARAM wParam,LPARAM lParam);
  30. LRESULT CALLBACK SliderSubWndProc (HWND hwndSlider, UINT uMsg, WPARAM wParam, LPARAM lParam, WPARAM uID, ULONG_PTR dwRefData);
  31. int ComputeNumberOfDisplayDevices();
  32. BOOL MakeMonitorBitmap(int w, int h, LPCTSTR sz, HBITMAP *pBitmap, HBITMAP *pMaskBitmap, int cx, int cy, BOOL fSelected);
  33. typedef struct _APPEXT {
  34. TCHAR szKeyName[MAX_PATH];
  35. TCHAR szDefaultValue[MAX_PATH];
  36. struct _APPEXT* pNext;
  37. } APPEXT, *PAPPEXT;
  38. VOID CheckForDuplicateAppletExtensions(HKEY hkDriver);
  39. VOID DeskAESnapshot(HKEY hkExtensions, PAPPEXT* ppAppExt);
  40. VOID DeskAECleanup(PAPPEXT pAppExt);
  41. VOID DeskAEDelete(PTCHAR szDeleteFrom, PTCHAR mszExtensionsToRemove);
  42. #define SELECTION_THICKNESS 4
  43. #define MONITOR_BORDER 1
  44. #define REGSTR_VAL_SAFEBOOT TEXT("System\\CurrentControlSet\\Control\\SafeBoot\\Option")
  45. // Maximum number of monitors supported.
  46. #define MONITORS_MAX 10
  47. #define PREVIEWAREARATIO 2
  48. #define MM_REDRAWPREVIEW (WM_USER + 1)
  49. #define MM_MONITORMOVED (WM_USER + 2)
  50. #define ToolTip_Activate(hTT, activate) \
  51. SendMessage(hTT, TTM_ACTIVATE, (WPARAM) activate, (LPARAM) 0)
  52. #define ToolTip_AddTool(hTT, lpti) \
  53. SendMessage(hTT, TTM_ADDTOOL, (WPARAM) 0, (LPARAM) (lpti))
  54. #define ToolTip_DelTool(hTT, lpti) \
  55. SendMessage(hTT, TTM_DELTOOL, (WPARAM) 0, (LPARAM) (lpti))
  56. #define ToolTip_GetCurrentTool(hTT, lpti) \
  57. SendMessage(hTT, TTM_GETCURRENTTOOL, (WPARAM) 0, (LPARAM) (lpti))
  58. #define ToolTip_RelayEvent(hTT, _msg, h, m, wp, lp) \
  59. _msg.hwnd = h; _msg.message = m; _msg.wParam = wp; _msg.lParam = lp;\
  60. SendMessage(hTT, TTM_RELAYEVENT, (WPARAM) 0, (LPARAM) &_msg);
  61. #define ToolTip_SetDelayTime(hTT, d, t) \
  62. SendMessage(hTT, TTM_SETDELAYTIME, (WPARAM) d, (LPARAM)MAKELONG((t), 0))
  63. #define ToolTip_SetToolInfo(hTT, lpti) \
  64. SendMessage(hTT, TTM_SETTOOLINFO, (WPARAM) 0, (LPARAM) (lpti))
  65. #define ToolTip_TrackActivate(hTT, bActivate, lpti) \
  66. SendMessage(hTT, TTM_TRACKACTIVATE, (WPARAM) (bActivate), (LPARAM) (lpti))
  67. #define ToolTip_TrackPosition(hTT, x, y) \
  68. SendMessage(hTT, TTM_TRACKPOSITION, (WPARAM) 0, (LPARAM) MAKELONG((x), (y)))
  69. #define ToolTip_Update(hTT) \
  70. SendMessage(hTT, TTM_UPDATE, (WPARAM) 0, (LPARAM) 0)
  71. VOID
  72. CDECL
  73. TRACE(
  74. PCTSTR pszMsg,
  75. ...
  76. )
  77. /*++
  78. Outputs a message to the setup log. Prepends "desk.cpl " to the strings and
  79. appends the correct newline chars (\r\n)==
  80. --*/
  81. {
  82. TCHAR ach[1024+40]; // Largest path plus extra
  83. va_list vArgs;
  84. va_start(vArgs, pszMsg);
  85. wvsprintf(ach, pszMsg, vArgs);
  86. va_end(vArgs);
  87. OutputDebugString(ach);
  88. }
  89. #ifdef _WIN64
  90. //
  91. // GetDlgItem and GetDlgCtrlID don't support INT_PTR's,
  92. // so we have to do it manually.
  93. // Fortunately, GetWindowLongPtr(GWLP_ID) actually returns a full 64-bit
  94. // value instead of truncating at 32-bits.
  95. //
  96. #define GetDlgCtrlIDP(hwnd) GetWindowLongPtr(hwnd, GWLP_ID)
  97. HWND GetDlgItemP(HWND hDlg, INT_PTR id)
  98. {
  99. HWND hwndChild = GetWindow(hDlg, GW_CHILD);
  100. while (hwndChild && GetDlgCtrlIDP(hwndChild) != id)
  101. hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
  102. return hwndChild;
  103. }
  104. #else
  105. #define GetDlgItemP GetDlgItem
  106. #define GetDlgCtrlIDP GetDlgCtrlID
  107. #endif
  108. //
  109. // display devices
  110. //
  111. typedef struct _multimon_device {
  112. //
  113. // Main class for settings
  114. //
  115. CDisplaySettings * pds;
  116. //
  117. // Color and resolution information cache
  118. // Rebuild when modes are enumerated.
  119. //
  120. int cColors;
  121. PLONGLONG ColorList;
  122. int cResolutions;
  123. PPOINT ResolutionList;
  124. ULONG ComboBoxItem;
  125. DISPLAY_DEVICE DisplayDevice;
  126. ULONG DisplayIndex;
  127. POINT Snap;
  128. HDC hdc;
  129. //
  130. // Image information.
  131. //
  132. int w,h;
  133. HIMAGELIST himl;
  134. int iImage;
  135. BOOLEAN bTracking;
  136. HWND hwndFlash; //Flash window.
  137. } MULTIMON_DEVICE, *PMULTIMON_DEVICE;
  138. #define GetDlgCtrlDevice(hwnd) ((PMULTIMON_DEVICE)GetDlgCtrlIDP(hwnd))
  139. BOOL gfFlashWindowRegistered = FALSE;
  140. HWND ghwndToolTipTracking;
  141. HWND ghwndToolTipPopup;
  142. HWND ghwndPropSheet;
  143. void AddTrackingToolTip(PMULTIMON_DEVICE pDevice, HWND hwnd);
  144. void RemoveTrackingToolTip(HWND hwnd);
  145. void AddPopupToolTip(HWND hwndC);
  146. void RemovePopupToolTip(HWND hwndC);
  147. #if QUICK_REFRESH
  148. #define IDC_FREQUENCY_START 2000
  149. HMENU CreateFrequencyMenu(PMULTIMON_DEVICE pDevice);
  150. #endif
  151. extern int AskDynaCDS(HWND hDlg);
  152. extern int GetDisplayCPLPreference(LPCTSTR szRegVal);
  153. extern void SetDisplayCPLPreference(LPCTSTR szRegVal, int val);
  154. // Prototype for CreateStdAccessibleProxy.
  155. // A and W versions are available - pClassName can be ANSI or UNICODE
  156. // string. This is a TCHAR-style prototype, but you can do a A or W
  157. // specific one if desired.
  158. typedef HRESULT (WINAPI *PFNCREATESTDACCESSIBLEPROXY) (
  159. HWND hWnd,
  160. LPTSTR pClassName,
  161. LONG idObject,
  162. REFIID riid,
  163. void ** ppvObject
  164. );
  165. // Same for LresultFromObject...
  166. typedef LRESULT (WINAPI *PFNLRESULTFROMOBJECT)(
  167. REFIID riid,
  168. WPARAM wParam,
  169. LPUNKNOWN punk
  170. );
  171. PRIVATE PFNCREATESTDACCESSIBLEPROXY s_pfnCreateStdAccessibleProxy = NULL;
  172. PRIVATE PFNLRESULTFROMOBJECT s_pfnLresultFromObject = NULL;
  173. BOOL g_fAttemptedOleAccLoad ;
  174. HMODULE g_hOleAcc;
  175. //-----------------------------------------------------------------------------
  176. static const DWORD sc_MultiMonitorHelpIds[] =
  177. {
  178. IDC_SCREENSAMPLE, IDH_DISPLAY_SETTINGS_MONITOR_GRAPHIC,
  179. IDC_MULTIMONHELP, IDH_DISPLAY_SETTINGS_MONITOR_GRAPHIC,
  180. IDC_DISPLAYDESK, IDH_DISPLAY_SETTINGS_MONITOR_GRAPHIC,
  181. IDC_DISPLAYLABEL, IDH_DISPLAY_SETTINGS_DISPLAY_LIST,
  182. IDC_DISPLAYLIST, IDH_DISPLAY_SETTINGS_DISPLAY_LIST,
  183. IDC_DISPLAYTEXT, IDH_DISPLAY_SETTINGS_DISPLAY_LIST,
  184. IDC_COLORGROUPBOX, IDH_DISPLAY_SETTINGS_COLORBOX,
  185. IDC_COLORBOX, IDH_DISPLAY_SETTINGS_COLORBOX,
  186. IDC_COLORSAMPLE, IDH_DISPLAY_SETTINGS_COLORBOX,
  187. IDC_RESGROUPBOX, IDH_DISPLAY_SETTINGS_SCREENAREA,
  188. IDC_SCREENSIZE, IDH_DISPLAY_SETTINGS_SCREENAREA,
  189. IDC_RES_LESS, IDH_DISPLAY_SETTINGS_SCREENAREA,
  190. IDC_RES_MORE, IDH_DISPLAY_SETTINGS_SCREENAREA,
  191. IDC_RESXY, IDH_DISPLAY_SETTINGS_SCREENAREA,
  192. IDC_DISPLAYUSEME, IDH_DISPLAY_SETTINGS_EXTEND_DESKTOP_CHECKBOX,
  193. IDC_DISPLAYPRIME, IDH_DISPLAY_SETTINGS_USE_PRIMARY_CHECKBOX,
  194. IDC_IDENTIFY, IDH_DISPLAY_SETTINGS_IDENTIFY_BUTTON,
  195. IDC_TROUBLESHOOT, IDH_DISPLAY_SETTINGS_TROUBLE_BUTTON,
  196. IDC_DISPLAYPROPERTIES, IDH_DISPLAY_SETTINGS_ADVANCED_BUTTON,
  197. 0, 0
  198. };
  199. class CAccessibleWrapper: public IAccessible
  200. {
  201. // We need to do our own refcounting for this wrapper object
  202. ULONG m_ref;
  203. // Need ptr to the IAccessible
  204. IAccessible * m_pAcc;
  205. HWND m_hwnd;
  206. public:
  207. CAccessibleWrapper( HWND hwnd, IAccessible * pAcc );
  208. virtual ~CAccessibleWrapper(void);
  209. // IUnknown
  210. // (We do our own ref counting)
  211. virtual STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
  212. virtual STDMETHODIMP_(ULONG) AddRef();
  213. virtual STDMETHODIMP_(ULONG) Release();
  214. // IDispatch
  215. virtual STDMETHODIMP GetTypeInfoCount(UINT* pctinfo);
  216. virtual STDMETHODIMP GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo);
  217. virtual STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR** rgszNames, UINT cNames,
  218. LCID lcid, DISPID* rgdispid);
  219. virtual STDMETHODIMP Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
  220. DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo,
  221. UINT* puArgErr);
  222. // IAccessible
  223. virtual STDMETHODIMP get_accParent(IDispatch ** ppdispParent);
  224. virtual STDMETHODIMP get_accChildCount(long* pChildCount);
  225. virtual STDMETHODIMP get_accChild(VARIANT varChild, IDispatch ** ppdispChild);
  226. virtual STDMETHODIMP get_accName(VARIANT varChild, BSTR* pszName);
  227. virtual STDMETHODIMP get_accValue(VARIANT varChild, BSTR* pszValue);
  228. virtual STDMETHODIMP get_accDescription(VARIANT varChild, BSTR* pszDescription);
  229. virtual STDMETHODIMP get_accRole(VARIANT varChild, VARIANT *pvarRole);
  230. virtual STDMETHODIMP get_accState(VARIANT varChild, VARIANT *pvarState);
  231. virtual STDMETHODIMP get_accHelp(VARIANT varChild, BSTR* pszHelp);
  232. virtual STDMETHODIMP get_accHelpTopic(BSTR* pszHelpFile, VARIANT varChild, long* pidTopic);
  233. virtual STDMETHODIMP get_accKeyboardShortcut(VARIANT varChild, BSTR* pszKeyboardShortcut);
  234. virtual STDMETHODIMP get_accFocus(VARIANT * pvarFocusChild);
  235. virtual STDMETHODIMP get_accSelection(VARIANT * pvarSelectedChildren);
  236. virtual STDMETHODIMP get_accDefaultAction(VARIANT varChild, BSTR* pszDefaultAction);
  237. virtual STDMETHODIMP accSelect(long flagsSel, VARIANT varChild);
  238. virtual STDMETHODIMP accLocation(long* pxLeft, long* pyTop, long* pcxWidth, long* pcyHeight, VARIANT varChild);
  239. virtual STDMETHODIMP accNavigate(long navDir, VARIANT varStart, VARIANT * pvarEndUpAt);
  240. virtual STDMETHODIMP accHitTest(long xLeft, long yTop, VARIANT * pvarChildAtPoint);
  241. virtual STDMETHODIMP accDoDefaultAction(VARIANT varChild);
  242. virtual STDMETHODIMP put_accName(VARIANT varChild, BSTR szName);
  243. virtual STDMETHODIMP put_accValue(VARIANT varChild, BSTR pszValue);
  244. };
  245. CAccessibleWrapper::CAccessibleWrapper( HWND hwnd, IAccessible * pAcc )
  246. : m_ref( 1 ),
  247. m_pAcc( pAcc ),
  248. m_hwnd( hwnd )
  249. {
  250. ASSERT( m_pAcc );
  251. m_pAcc->AddRef();
  252. }
  253. CAccessibleWrapper::~CAccessibleWrapper()
  254. {
  255. m_pAcc->Release();
  256. }
  257. // IUnknown
  258. // Implement refcounting ourselves
  259. // Also implement QI ourselves, so that we return a ptr back to the wrapper.
  260. STDMETHODIMP CAccessibleWrapper::QueryInterface(REFIID riid, void** ppv)
  261. {
  262. *ppv = NULL;
  263. if ((riid == IID_IUnknown) ||
  264. (riid == IID_IDispatch) ||
  265. (riid == IID_IAccessible))
  266. {
  267. *ppv = (IAccessible *) this;
  268. }
  269. else
  270. return(E_NOINTERFACE);
  271. AddRef();
  272. return(NOERROR);
  273. }
  274. STDMETHODIMP_(ULONG) CAccessibleWrapper::AddRef()
  275. {
  276. return ++m_ref;
  277. }
  278. STDMETHODIMP_(ULONG) CAccessibleWrapper::Release()
  279. {
  280. ULONG ulRet = --m_ref;
  281. if( ulRet == 0 )
  282. delete this;
  283. return ulRet;
  284. }
  285. // IDispatch
  286. // - pass all through m_pAcc
  287. STDMETHODIMP CAccessibleWrapper::GetTypeInfoCount(UINT* pctinfo)
  288. {
  289. return m_pAcc->GetTypeInfoCount(pctinfo);
  290. }
  291. STDMETHODIMP CAccessibleWrapper::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo)
  292. {
  293. return m_pAcc->GetTypeInfo(itinfo, lcid, pptinfo);
  294. }
  295. STDMETHODIMP CAccessibleWrapper::GetIDsOfNames(REFIID riid, OLECHAR** rgszNames, UINT cNames,
  296. LCID lcid, DISPID* rgdispid)
  297. {
  298. return m_pAcc->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
  299. }
  300. STDMETHODIMP CAccessibleWrapper::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
  301. DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo,
  302. UINT* puArgErr)
  303. {
  304. return m_pAcc->Invoke(dispidMember, riid, lcid, wFlags,
  305. pdispparams, pvarResult, pexcepinfo,
  306. puArgErr);
  307. }
  308. // IAccessible
  309. // - pass all through m_pAcc
  310. STDMETHODIMP CAccessibleWrapper::get_accParent(IDispatch ** ppdispParent)
  311. {
  312. return m_pAcc->get_accParent(ppdispParent);
  313. }
  314. STDMETHODIMP CAccessibleWrapper::get_accChildCount(long* pChildCount)
  315. {
  316. return m_pAcc->get_accChildCount(pChildCount);
  317. }
  318. STDMETHODIMP CAccessibleWrapper::get_accChild(VARIANT varChild, IDispatch ** ppdispChild)
  319. {
  320. return m_pAcc->get_accChild(varChild, ppdispChild);
  321. }
  322. STDMETHODIMP CAccessibleWrapper::get_accName(VARIANT varChild, BSTR* pszName)
  323. {
  324. return m_pAcc->get_accName(varChild, pszName);
  325. }
  326. STDMETHODIMP CAccessibleWrapper::get_accValue(VARIANT varChild, BSTR* pszValue)
  327. {
  328. // varChild.lVal specifies which sub-part of the component
  329. // is being queried.
  330. // CHILDID_SELF (0) specifies the overall component - other
  331. // non-0 values specify a child.
  332. // In a trackbar, CHILDID_SELF refers to the overall trackbar
  333. // (which is what we want), whereas other values refer to the
  334. // sub-components - the actual slider 'thumb', and the 'page
  335. // up/page down' areas to the left/right of it.
  336. if( varChild.vt == VT_I4 && varChild.lVal == CHILDID_SELF )
  337. {
  338. HWND hDlg;
  339. TCHAR achRes[120];
  340. #ifndef UNICODE
  341. WCHAR wszRes[120];
  342. #endif
  343. hDlg = GetParent( m_hwnd );
  344. SendDlgItemMessage(hDlg, IDC_RESXY, WM_GETTEXT, 120, (LPARAM)achRes);
  345. #ifdef UNICODE
  346. *pszValue = SysAllocString( achRes );
  347. #else
  348. MultiByteToWideChar( CP_ACP, 0, achRes, -1, wszRes, 120 );
  349. *pszValue = SysAllocString( wszRes );
  350. #endif
  351. return S_OK;
  352. }
  353. else
  354. {
  355. // Pass requests about the sub-components to the
  356. // 'original' IAccessible for us).
  357. return m_pAcc->get_accValue(varChild, pszValue);
  358. }
  359. }
  360. STDMETHODIMP CAccessibleWrapper::get_accDescription(VARIANT varChild, BSTR* pszDescription)
  361. {
  362. return m_pAcc->get_accDescription(varChild, pszDescription);
  363. }
  364. STDMETHODIMP CAccessibleWrapper::get_accRole(VARIANT varChild, VARIANT *pvarRole)
  365. {
  366. return m_pAcc->get_accRole(varChild, pvarRole);
  367. }
  368. STDMETHODIMP CAccessibleWrapper::get_accState(VARIANT varChild, VARIANT *pvarState)
  369. {
  370. return m_pAcc->get_accState(varChild, pvarState);
  371. }
  372. STDMETHODIMP CAccessibleWrapper::get_accHelp(VARIANT varChild, BSTR* pszHelp)
  373. {
  374. return m_pAcc->get_accHelp(varChild, pszHelp);
  375. }
  376. STDMETHODIMP CAccessibleWrapper::get_accHelpTopic(BSTR* pszHelpFile, VARIANT varChild, long* pidTopic)
  377. {
  378. return m_pAcc->get_accHelpTopic(pszHelpFile, varChild, pidTopic);
  379. }
  380. STDMETHODIMP CAccessibleWrapper::get_accKeyboardShortcut(VARIANT varChild, BSTR* pszKeyboardShortcut)
  381. {
  382. return m_pAcc->get_accKeyboardShortcut(varChild, pszKeyboardShortcut);
  383. }
  384. STDMETHODIMP CAccessibleWrapper::get_accFocus(VARIANT * pvarFocusChild)
  385. {
  386. return m_pAcc->get_accFocus(pvarFocusChild);
  387. }
  388. STDMETHODIMP CAccessibleWrapper::get_accSelection(VARIANT * pvarSelectedChildren)
  389. {
  390. return m_pAcc->get_accSelection(pvarSelectedChildren);
  391. }
  392. STDMETHODIMP CAccessibleWrapper::get_accDefaultAction(VARIANT varChild, BSTR* pszDefaultAction)
  393. {
  394. return m_pAcc->get_accDefaultAction(varChild, pszDefaultAction);
  395. }
  396. STDMETHODIMP CAccessibleWrapper::accSelect(long flagsSel, VARIANT varChild)
  397. {
  398. return m_pAcc->accSelect(flagsSel, varChild);
  399. }
  400. STDMETHODIMP CAccessibleWrapper::accLocation(long* pxLeft, long* pyTop, long* pcxWidth, long* pcyHeight, VARIANT varChild)
  401. {
  402. return m_pAcc->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, varChild);
  403. }
  404. STDMETHODIMP CAccessibleWrapper::accNavigate(long navDir, VARIANT varStart, VARIANT * pvarEndUpAt)
  405. {
  406. return m_pAcc->accNavigate(navDir, varStart, pvarEndUpAt);
  407. }
  408. STDMETHODIMP CAccessibleWrapper::accHitTest(long xLeft, long yTop, VARIANT * pvarChildAtPoint)
  409. {
  410. return m_pAcc->accHitTest(xLeft, yTop, pvarChildAtPoint);
  411. }
  412. STDMETHODIMP CAccessibleWrapper::accDoDefaultAction(VARIANT varChild)
  413. {
  414. return m_pAcc->accDoDefaultAction(varChild);
  415. }
  416. STDMETHODIMP CAccessibleWrapper::put_accName(VARIANT varChild, BSTR szName)
  417. {
  418. return m_pAcc->put_accName(varChild, szName);
  419. }
  420. STDMETHODIMP CAccessibleWrapper::put_accValue(VARIANT varChild, BSTR pszValue)
  421. {
  422. return m_pAcc->put_accValue(varChild, pszValue);
  423. }
  424. class CSettingsPage : public CObjectWithSite,
  425. public CObjectCLSID,
  426. public IMultiMonConfig,
  427. public IPropertyBag,
  428. public IBasePropPage
  429. {
  430. friend int ComputeNumberOfDisplayDevices();
  431. friend int DisplaySaveSettings(PVOID pContext, HWND hwnd);
  432. private:
  433. // Data Section
  434. PMULTIMON_DEVICE _pCurDevice;
  435. PMULTIMON_DEVICE _pPrimaryDevice;
  436. // HWND for the main window
  437. HWND _hDlg;
  438. HWND _hwndDesk;
  439. HWND _hwndList;
  440. // union of all monitor RECTs
  441. RECT _rcDesk;
  442. // ref count
  443. LONG _cRef;
  444. LONG _nInApply;
  445. // how to translate to preview size
  446. int _DeskScale;
  447. POINT _DeskOff;
  448. UINT _InSetInfo;
  449. ULONG _NumDevices;
  450. HBITMAP _hbmScrSample;
  451. HBITMAP _hbmMonitor;
  452. HIMAGELIST _himl;
  453. DWORD _dwInvalidMode;
  454. // UI variables
  455. int _iColor;
  456. int _iResolution;
  457. BOOL _bBadDriver : 1;
  458. BOOL _bNoAttach : 1;
  459. BOOL _bDirty : 1;
  460. MULTIMON_DEVICE _Devices[MONITORS_MAX];
  461. // Private functions
  462. void _DeskToPreview(LPRECT in, LPRECT out);
  463. void _OffsetPreviewToDesk(HWND hwndC, LPRECT prcNewPreview, LPRECT prcOldPreview, LPRECT out);
  464. BOOL _QueryForceSmallFont();
  465. void _SetPreviewScreenSize(int HRes, int VRes, int iOrgXRes, int iOrgYRes);
  466. void _CleanupRects(HWND hwndP);
  467. void _ConfirmPositions();
  468. void _DoAdvancedSettingsSheet();
  469. BOOL _HandleHScroll(HWND hwndSB, int iCode, int iPos);
  470. void _RedrawDeskPreviews();
  471. void _OnAdvancedClicked();
  472. BOOL _InitDisplaySettings(BOOL bExport);
  473. int _EnumerateAllDisplayDevices(); //Enumerates and returns the number of devices.
  474. void _DestroyMultimonDevice(PMULTIMON_DEVICE pDevice);
  475. void _DestroyDisplaySettings();
  476. void _InitUI();
  477. void _UpdateUI(BOOL fAutoSetColorDepth = TRUE, int FocusToCtrlID = 0);
  478. LPTSTR _FormatMessageInvoke(LPCTSTR pcszFormat, va_list *argList);
  479. LPTSTR _FormatMessageWrap(LPCTSTR pcszFormat, ...);
  480. void _GetDisplayName(PMULTIMON_DEVICE pDevice, LPTSTR pszDisplay, DWORD cchSize);
  481. int _SaveDisplaySettings(DWORD dwSet);
  482. BOOL _RebuildDisplaySettings(BOOL bComplete);
  483. void _ForwardToChildren(UINT message, WPARAM wParam, LPARAM lParam);
  484. static BOOL _CanSkipWarningBecauseKnownSafe(CDisplaySettings *rgpds[], ULONG numDevices);
  485. static BOOL _AnyChange(CDisplaySettings *rgpds[], ULONG numDevices);
  486. static BOOL _AnyColorChange(CDisplaySettings *rgpds[], ULONG numDevices);
  487. static BOOL _IsSingleToMultimonChange(CDisplaySettings *rgpds[],
  488. ULONG numDevices);
  489. static int _DisplaySaveSettings(CDisplaySettings *rgpds[],
  490. ULONG numDevices,
  491. HWND hDlg);
  492. static int _SaveSettings(CDisplaySettings *rgpds[],
  493. ULONG numDevices,
  494. HWND hDlg,
  495. DWORD dwSet);
  496. BOOL _AreExtraMonitorsDisabledOnPersonal();
  497. BOOL _InitMessage();
  498. void _vPreExecMode();
  499. void _vPostExecMode();
  500. public:
  501. CSettingsPage();
  502. virtual ~CSettingsPage(void);
  503. static BOOL RegisterPreviewWindowClass(WNDPROC pfnWndProc);
  504. // *** IUnknown methods ***
  505. STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
  506. STDMETHODIMP_(ULONG) AddRef(void);
  507. STDMETHODIMP_(ULONG) Release(void);
  508. // *** IMultiMonConfig methods ***
  509. STDMETHOD ( Initialize ) ( HWND hwndHost, WNDPROC pfnWndProc, DWORD dwReserved);
  510. STDMETHOD ( GetNumberOfMonitors ) (int * pCMon, DWORD dwReserved);
  511. STDMETHOD ( GetMonitorData) (int iMonitor, MonitorData * pmd, DWORD dwReserved);
  512. STDMETHOD ( Paint) (THIS_ int iMonitor, DWORD dwReserved);
  513. // *** IShellPropSheetExt ***
  514. virtual STDMETHODIMP AddPages(IN LPFNSVADDPROPSHEETPAGE pfnAddPage, IN LPARAM lParam);
  515. virtual STDMETHODIMP ReplacePage(IN EXPPS uPageID, IN LPFNSVADDPROPSHEETPAGE pfnReplaceWith, IN LPARAM lParam);
  516. // *** IObjectWithSite ***
  517. virtual STDMETHODIMP SetSite(IUnknown *punkSite);
  518. // *** IPropertyBag ***
  519. virtual STDMETHODIMP Read(IN LPCOLESTR pszPropName, IN VARIANT * pVar, IN IErrorLog *pErrorLog);
  520. virtual STDMETHODIMP Write(IN LPCOLESTR pszPropName, IN VARIANT *pVar);
  521. // *** IBasePropPage ***
  522. virtual STDMETHODIMP GetAdvancedDialog(OUT IAdvancedDialog ** ppAdvDialog);
  523. virtual STDMETHODIMP OnApply(IN PROPPAGEONAPPLY oaAction);
  524. BOOL InitMultiMonitorDlg(HWND hDlg);
  525. PMULTIMON_DEVICE GetCurDevice(){return _pCurDevice;};
  526. int GetNumberOfAttachedDisplays();
  527. void UpdateActiveDisplay(PMULTIMON_DEVICE pDevice, BOOL bRepaint = TRUE);
  528. BOOL HandleMonitorChange(HWND hwndP, BOOL bMainDlg, BOOL bRepaint = TRUE);
  529. void SetDirty(BOOL bDirty=TRUE);
  530. BOOL SetPrimary(PMULTIMON_DEVICE pDevice);
  531. BOOL SetMonAttached(PMULTIMON_DEVICE pDevice, BOOL bSetAttached,
  532. BOOL bForce, HWND hwnd);
  533. HWND GetCurDeviceHwnd() { return GetDlgItemP(_hwndDesk, (INT_PTR) _pCurDevice);};
  534. ULONG GetNumDevices() { return _NumDevices;};
  535. BOOL QueryNoAttach() { return _bNoAttach;};
  536. BOOL IsDirty() { return _bDirty;};
  537. void GetMonitorPosition(PMULTIMON_DEVICE pDevice, HWND hwndP, PPOINT ptPos);
  538. static INT_PTR CALLBACK SettingsDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
  539. LRESULT CALLBACK WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
  540. IThemeUIPages* _pThemeUI;
  541. };
  542. #if 0
  543. TraceMsg(TF_DUMP_CSETTINGS,"\t cb = %d", _DisplayDevice.cb );
  544. TraceMsg(TF_DUMP_CSETTINGS,"\t DeviceName = %s", _pstrDisplayDevice );
  545. TraceMsg(TF_DUMP_CSETTINGS,"\t DeviceString= %s", _DisplayDevice.DeviceString );
  546. TraceMsg(TF_DUMP_CSETTINGS,"\t StateFlags = %08lx", _DisplayDevice.StateFlags );
  547. #endif
  548. CSettingsPage::CSettingsPage() : _cRef(1), CObjectCLSID(&PPID_Settings)
  549. {
  550. ASSERT(_pCurDevice == NULL);
  551. ASSERT(_pPrimaryDevice == NULL);
  552. ASSERT(_DeskScale == 0);
  553. ASSERT(_InSetInfo == 0);
  554. ASSERT(_NumDevices == 0);
  555. ASSERT(IsRectEmpty(&_rcDesk));
  556. ASSERT(_bNoAttach == FALSE);
  557. ASSERT(_bDirty == FALSE);
  558. _nInApply = 0;
  559. };
  560. CSettingsPage::~CSettingsPage()
  561. {
  562. };
  563. void CSettingsPage::_DestroyMultimonDevice(PMULTIMON_DEVICE pDevice)
  564. {
  565. ASSERT(pDevice->pds);
  566. pDevice->pds->Release();
  567. pDevice->pds = NULL;
  568. if(pDevice->hwndFlash)
  569. {
  570. DestroyWindow(pDevice->hwndFlash);
  571. pDevice->hwndFlash = NULL;
  572. }
  573. if (pDevice->hdc) {
  574. DeleteDC(pDevice->hdc);
  575. pDevice->hdc = NULL;
  576. }
  577. if (pDevice->ResolutionList) {
  578. LocalFree(pDevice->ResolutionList);
  579. pDevice->ResolutionList = NULL;
  580. }
  581. if (pDevice->ColorList) {
  582. LocalFree(pDevice->ColorList);
  583. pDevice->ColorList = NULL;
  584. }
  585. }
  586. void CSettingsPage::_DestroyDisplaySettings()
  587. {
  588. ULONG iDevice;
  589. HWND hwndC;
  590. ASSERT(_NumDevices);
  591. TraceMsg(TF_GENERAL, "DestroyDisplaySettings: %d devices", _NumDevices);
  592. // We are about to destroy the _Devices below. Pointerts to these devices are used as the
  593. // CtrlIDs for the monitor windows. So, we need destroy the monitor windows first;
  594. // otherwise, if the monitor windows are destroyed later, they try to use these invalid
  595. // pDevice in FlashText. (pDevice->hwndFlash will fault).
  596. hwndC = GetWindow(_hwndDesk, GW_CHILD);
  597. while (hwndC)
  598. {
  599. RemoveTrackingToolTip(hwndC);
  600. RemovePopupToolTip(hwndC);
  601. DestroyWindow(hwndC);
  602. hwndC = GetWindow(_hwndDesk, GW_CHILD);
  603. }
  604. // Now, we can destroy the _Devices safely.
  605. for (iDevice = 0; iDevice < _NumDevices; iDevice++) {
  606. _DestroyMultimonDevice(_Devices + iDevice);
  607. // Note: pds is destroyed and set to zero already in the above call.
  608. //delete _Devices[iDevice].pds;
  609. //_Devices[iDevice].pds = 0;
  610. }
  611. if (_himl) {
  612. ImageList_Destroy(_himl);
  613. _himl = NULL;
  614. }
  615. DestroyWindow(ghwndToolTipTracking);
  616. DestroyWindow(ghwndToolTipPopup);
  617. ghwndToolTipTracking = NULL;
  618. ghwndToolTipPopup = NULL;
  619. TraceMsg(TF_GENERAL, "DestroyDisplaySettings: Finished destroying all devices");
  620. }
  621. //
  622. // deterines if the applet is in detect mode.
  623. //
  624. //
  625. // Called to put up initial messages that need to appear above the dialog
  626. // box
  627. //
  628. BOOL CSettingsPage::_InitMessage()
  629. {
  630. {
  631. //
  632. // _bBadDriver will be set when we fail to build the list of modes,
  633. // or something else failed during initialization.
  634. //
  635. // In almost every case, we should already know about this situation
  636. // based on our boot code.
  637. // However, if this is a new situation, just report a "bad driver"
  638. //
  639. DWORD dwExecMode;
  640. if (_pThemeUI && (SUCCEEDED(_pThemeUI->GetExecMode(&dwExecMode))))
  641. {
  642. if (_bBadDriver)
  643. {
  644. ASSERT(dwExecMode == EM_INVALID_MODE);
  645. _pThemeUI->SetExecMode(EM_INVALID_MODE);
  646. dwExecMode = EM_INVALID_MODE;
  647. _dwInvalidMode = EIS_EXEC_INVALID_DISPLAY_DRIVER;
  648. }
  649. if (dwExecMode == EM_INVALID_MODE)
  650. {
  651. DWORD Mesg;
  652. switch(_dwInvalidMode) {
  653. case EIS_EXEC_INVALID_NEW_DRIVER:
  654. Mesg = MSG_INVALID_NEW_DRIVER;
  655. break;
  656. case EIS_EXEC_INVALID_DEFAULT_DISPLAY_MODE:
  657. Mesg = MSG_INVALID_DEFAULT_DISPLAY_MODE;
  658. break;
  659. case EIS_EXEC_INVALID_DISPLAY_DRIVER:
  660. Mesg = MSG_INVALID_DISPLAY_DRIVER;
  661. break;
  662. case EIS_EXEC_INVALID_OLD_DISPLAY_DRIVER:
  663. Mesg = MSG_INVALID_OLD_DISPLAY_DRIVER;
  664. break;
  665. case EIS_EXEC_INVALID_16COLOR_DISPLAY_MODE:
  666. Mesg = MSG_INVALID_16COLOR_DISPLAY_MODE;
  667. break;
  668. case EIS_EXEC_INVALID_DISPLAY_MODE:
  669. Mesg = MSG_INVALID_DISPLAY_MODE;
  670. {
  671. //
  672. // If we are in safe mode, then we will get to here when
  673. // we initially log in. We are in forced VGA mode, so there
  674. // is no real error here. Emulate a click on the OK button
  675. // and everybody is happy.
  676. //
  677. HKEY hSafe;
  678. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  679. REGSTR_VAL_SAFEBOOT,
  680. 0,
  681. KEY_READ,
  682. &hSafe) == ERROR_SUCCESS) {
  683. //
  684. // If we ever care about the actual safe mode, the value
  685. // is nameed "OptionValue"
  686. //
  687. RegCloseKey(hSafe);
  688. PropSheet_PressButton(GetParent(_hDlg), PSBTN_OK);
  689. return TRUE;
  690. }
  691. }
  692. break;
  693. case EIS_EXEC_INVALID_CONFIGURATION:
  694. default:
  695. Mesg = MSG_INVALID_CONFIGURATION;
  696. break;
  697. }
  698. FmtMessageBox(_hDlg,
  699. MB_ICONEXCLAMATION,
  700. MSG_CONFIGURATION_PROBLEM,
  701. Mesg);
  702. //
  703. // For a bad display driver or old display driver, let's send the
  704. // user straight to the installation dialog.
  705. //
  706. if ((_dwInvalidMode == EIS_EXEC_INVALID_OLD_DISPLAY_DRIVER) ||
  707. (_dwInvalidMode == EIS_EXEC_INVALID_DISPLAY_DRIVER))
  708. {
  709. ASSERT(FALSE);
  710. }
  711. }
  712. }
  713. }
  714. return TRUE;
  715. }
  716. VOID CSettingsPage::_vPreExecMode()
  717. {
  718. HKEY hkey;
  719. // DWORD data;
  720. //
  721. // This function sets up the execution mode of the applet.
  722. // There are four vlid modes.
  723. //
  724. // EXEC_NORMAL - When the apple is launched from the control panel
  725. //
  726. // EXEC_INVALID_MODE is exactly the same as for NORMAL except we will
  727. // not mark the current mode as tested so the user has
  728. // to at least test a mode
  729. //
  730. // EXEC_DETECT - When the applet is launched normally, but a detect was
  731. // done on the previous boot (the key in the registry is
  732. // set)
  733. //
  734. // EXEC_SETUP - When we launch the applet in setup mode from setup (Both
  735. // the registry key is set and the setup flag is passed in).
  736. //
  737. //
  738. // These two keys should only be checked \ deleted if the machine has been
  739. // rebooted and the detect \ new display has actually happened.
  740. // So we will look for the RebootNecessary key (a volatile key) and if
  741. // it is not present, then we can delete the key. Otherwise, the reboot
  742. // has not happened, and we keep the key
  743. //
  744. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  745. SZ_REBOOT_NECESSARY,
  746. 0,
  747. KEY_READ | KEY_WRITE,
  748. &hkey) != ERROR_SUCCESS) {
  749. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  750. SZ_DETECT_DISPLAY,
  751. 0,
  752. KEY_READ | KEY_WRITE,
  753. &hkey) == ERROR_SUCCESS) {
  754. //
  755. // NOTE: This key is also set when EXEC_SETUP is being run.
  756. //
  757. DWORD dwExecMode;
  758. if (_pThemeUI && (SUCCEEDED(_pThemeUI->GetExecMode(&dwExecMode))))
  759. {
  760. if (dwExecMode == EM_NORMAL) {
  761. _pThemeUI->SetExecMode(EM_DETECT);
  762. } else {
  763. //
  764. // If we are in setup mode, we also check the extra values
  765. // under DetectDisplay that control the unattended installation.
  766. //
  767. ASSERT(dwExecMode == EM_SETUP);
  768. }
  769. }
  770. RegCloseKey(hkey);
  771. }
  772. //
  773. // Check for a new driver being installed
  774. //
  775. DWORD dwExecMode;
  776. if (_pThemeUI && (SUCCEEDED(_pThemeUI->GetExecMode(&dwExecMode))))
  777. {
  778. if ( (dwExecMode == EM_NORMAL) &&
  779. (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  780. SZ_NEW_DISPLAY,
  781. 0,
  782. KEY_READ | KEY_WRITE,
  783. &hkey) == ERROR_SUCCESS) ) {
  784. _pThemeUI->SetExecMode(EM_INVALID_MODE);
  785. _dwInvalidMode = EIS_EXEC_INVALID_NEW_DRIVER;
  786. RegCloseKey(hkey);
  787. }
  788. }
  789. RegDeleteKey(HKEY_LOCAL_MACHINE,
  790. SZ_DETECT_DISPLAY);
  791. RegDeleteKey(HKEY_LOCAL_MACHINE,
  792. SZ_NEW_DISPLAY);
  793. }
  794. {
  795. LPTSTR psz = NULL;
  796. LPTSTR pszInv = NULL;
  797. DWORD dwExecMode;
  798. if (_pThemeUI && (SUCCEEDED(_pThemeUI->GetExecMode(&dwExecMode))))
  799. {
  800. switch(dwExecMode) {
  801. case EM_NORMAL:
  802. psz = TEXT("Normal Execution mode");
  803. break;
  804. case EM_DETECT:
  805. psz = TEXT("Detection Execution mode");
  806. break;
  807. case EM_SETUP:
  808. psz = TEXT("Setup Execution mode");
  809. break;
  810. case EM_INVALID_MODE:
  811. psz = TEXT("Invalid Mode Execution mode");
  812. switch(_dwInvalidMode) {
  813. case EIS_EXEC_INVALID_NEW_DRIVER:
  814. pszInv = TEXT("Invalid new driver");
  815. break;
  816. default:
  817. pszInv = TEXT("*** Invalid *** Invalid mode");
  818. break;
  819. }
  820. break;
  821. default:
  822. psz = TEXT("*** Invalid *** Execution mode");
  823. break;
  824. }
  825. if (dwExecMode == EM_INVALID_MODE)
  826. {
  827. TraceMsg(TF_FUNC, "\t\t sub invalid mode : %ws", pszInv);
  828. }
  829. }
  830. TraceMsg(TF_FUNC, "\n\n", psz);
  831. }
  832. }
  833. VOID CSettingsPage::_vPostExecMode() {
  834. HKEY hkey;
  835. DWORD cb;
  836. DWORD data;
  837. //
  838. // Check for various invalid configurations
  839. //
  840. DWORD dwExecMode;
  841. if (_pThemeUI && (SUCCEEDED(_pThemeUI->GetExecMode(&dwExecMode))))
  842. {
  843. if ( (dwExecMode == EM_NORMAL) &&
  844. (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  845. SZ_INVALID_DISPLAY,
  846. 0,
  847. KEY_READ | KEY_WRITE,
  848. &hkey) == ERROR_SUCCESS) ) {
  849. _pThemeUI->SetExecMode(EM_INVALID_MODE);
  850. //
  851. // Check for these fields in increasing order of "badness" or
  852. // "detail" so that the *worst* error is the one remaining in the
  853. // _dwInvalidMode variable once all the checks are done.
  854. //
  855. cb = 4;
  856. if (RegQueryValueEx(hkey,
  857. TEXT("DefaultMode"),
  858. NULL,
  859. NULL,
  860. (LPBYTE)(&data),
  861. &cb) == ERROR_SUCCESS)
  862. {
  863. _dwInvalidMode = EIS_EXEC_INVALID_DEFAULT_DISPLAY_MODE;
  864. }
  865. cb = 4;
  866. if (RegQueryValueEx(hkey,
  867. TEXT("BadMode"),
  868. NULL,
  869. NULL,
  870. (LPBYTE)(&data),
  871. &cb) == ERROR_SUCCESS)
  872. {
  873. _dwInvalidMode = EIS_EXEC_INVALID_DISPLAY_MODE;
  874. }
  875. cb = 4;
  876. if (RegQueryValueEx(hkey,
  877. TEXT("16ColorMode"),
  878. NULL,
  879. NULL,
  880. (LPBYTE)(&data),
  881. &cb) == ERROR_SUCCESS)
  882. {
  883. _dwInvalidMode = EIS_EXEC_INVALID_16COLOR_DISPLAY_MODE;
  884. }
  885. cb = 4;
  886. if (RegQueryValueEx(hkey,
  887. TEXT("InvalidConfiguration"),
  888. NULL,
  889. NULL,
  890. (LPBYTE)(&data),
  891. &cb) == ERROR_SUCCESS)
  892. {
  893. _dwInvalidMode = EIS_EXEC_INVALID_CONFIGURATION;
  894. }
  895. cb = 4;
  896. if (RegQueryValueEx(hkey,
  897. TEXT("MissingDisplayDriver"),
  898. NULL,
  899. NULL,
  900. (LPBYTE)(&data),
  901. &cb) == ERROR_SUCCESS)
  902. {
  903. _dwInvalidMode = EIS_EXEC_INVALID_DISPLAY_DRIVER;
  904. }
  905. //
  906. // This last case will be set in addition to the previous one in the
  907. // case where the driver was an old driver linking to winsvr.dll
  908. // and we can not load it.
  909. //
  910. cb = 4;
  911. if (RegQueryValueEx(hkey,
  912. TEXT("OldDisplayDriver"),
  913. NULL,
  914. NULL,
  915. (LPBYTE)(&data),
  916. &cb) == ERROR_SUCCESS)
  917. {
  918. _dwInvalidMode = EIS_EXEC_INVALID_OLD_DISPLAY_DRIVER;
  919. }
  920. RegCloseKey(hkey);
  921. }
  922. }
  923. //
  924. // Delete all of these bad configuration keys since we only want the
  925. // user to see the message once.
  926. //
  927. RegDeleteKey(HKEY_LOCAL_MACHINE,
  928. SZ_INVALID_DISPLAY);
  929. {
  930. LPTSTR psz = NULL;
  931. LPTSTR pszInv = NULL;
  932. DWORD dwExecMode;
  933. if (_pThemeUI && (SUCCEEDED(_pThemeUI->GetExecMode(&dwExecMode))))
  934. {
  935. if (dwExecMode == EM_INVALID_MODE)
  936. {
  937. switch (_dwInvalidMode)
  938. {
  939. case EIS_EXEC_INVALID_DEFAULT_DISPLAY_MODE:
  940. pszInv = TEXT("Default mode being used");
  941. break;
  942. case EIS_EXEC_INVALID_DISPLAY_DRIVER:
  943. pszInv = TEXT("Invalid Display Driver");
  944. break;
  945. case EIS_EXEC_INVALID_OLD_DISPLAY_DRIVER:
  946. pszInv = TEXT("Old Display Driver");
  947. break;
  948. case EIS_EXEC_INVALID_16COLOR_DISPLAY_MODE:
  949. pszInv = TEXT("16 color mode not supported");
  950. break;
  951. case EIS_EXEC_INVALID_DISPLAY_MODE:
  952. pszInv = TEXT("Invalid display mode");
  953. break;
  954. case EIS_EXEC_INVALID_CONFIGURATION:
  955. pszInv = TEXT("Invalid configuration");
  956. break;
  957. default:
  958. psz = TEXT("*** Invalid *** Invalid mode");
  959. break;
  960. }
  961. TraceMsg(TF_FUNC, "\t\t sub invlid mode : %ws", pszInv);
  962. TraceMsg(TF_FUNC, "\n\n", psz);
  963. }
  964. }
  965. }
  966. }
  967. //-----------------------------------------------------------------------------
  968. //-----------------------------------------------------------------------------
  969. void CSettingsPage::_DeskToPreview(LPRECT in, LPRECT out)
  970. {
  971. out->left = _DeskOff.x + MulDiv(in->left - _rcDesk.left,_DeskScale,1000);
  972. out->top = _DeskOff.y + MulDiv(in->top - _rcDesk.top, _DeskScale,1000);
  973. out->right = _DeskOff.x + MulDiv(in->right - _rcDesk.left,_DeskScale,1000);
  974. out->bottom = _DeskOff.y + MulDiv(in->bottom - _rcDesk.top, _DeskScale,1000);
  975. }
  976. //-----------------------------------------------------------------------------
  977. //-----------------------------------------------------------------------------
  978. void CSettingsPage::_OffsetPreviewToDesk(HWND hwndC, LPRECT prcNewPreview, LPRECT prcOldPreview, LPRECT out)
  979. {
  980. int x = 0, y = 0;
  981. int dtLeft, dtRight, dtTop, dtBottom;
  982. int nTotal;
  983. HWND hwndT;
  984. RECT rcC, rcT, rcPosT;
  985. BOOL bx = FALSE, by = FALSE;
  986. PMULTIMON_DEVICE pDeviceT;
  987. dtLeft = prcNewPreview->left - prcOldPreview->left;
  988. dtRight = prcNewPreview->right - prcOldPreview->right;
  989. dtTop = prcNewPreview->top - prcOldPreview->top;
  990. dtBottom = prcNewPreview->bottom - prcOldPreview->bottom;
  991. nTotal = abs(dtLeft) + abs(dtRight) + abs(dtTop) + abs(dtBottom);
  992. if (nTotal == 0) {
  993. return;
  994. } else if (nTotal > 2) {
  995. //
  996. // walk all other windows and snap our window to them
  997. //
  998. GetWindowRect(hwndC, &rcC);
  999. for (hwndT = GetWindow(hwndC, GW_HWNDFIRST);
  1000. (hwndT && (!bx || !by));
  1001. hwndT = GetWindow(hwndT, GW_HWNDNEXT))
  1002. {
  1003. if (hwndT == hwndC)
  1004. continue;
  1005. GetWindowRect(hwndT, &rcT);
  1006. pDeviceT = GetDlgCtrlDevice(hwndT);
  1007. if (pDeviceT) {
  1008. pDeviceT->pds->GetCurPosition(&rcPosT);
  1009. if (!bx) {
  1010. bx = TRUE;
  1011. if (rcC.left == rcT.left) {
  1012. x = rcPosT.left - out->left;
  1013. } else if (rcC.left == rcT.right) {
  1014. x = rcPosT.right - out->left;
  1015. } else if (rcC.right == rcT.left) {
  1016. x = rcPosT.left - out->right;
  1017. } else if (rcC.right == rcT.right) {
  1018. x = rcPosT.right - out->right;
  1019. } else {
  1020. bx = FALSE;
  1021. }
  1022. }
  1023. if (!by) {
  1024. by = TRUE;
  1025. if (rcC.top == rcT.top) {
  1026. y = rcPosT.top - out->top;
  1027. } else if (rcC.top == rcT.bottom) {
  1028. y = rcPosT.bottom - out->top;
  1029. } else if (rcC.bottom == rcT.top) {
  1030. y = rcPosT.top - out->bottom;
  1031. } else if (rcC.bottom == rcT.bottom) {
  1032. y = rcPosT.bottom - out->bottom;
  1033. } else {
  1034. by = FALSE;
  1035. }
  1036. }
  1037. }
  1038. }
  1039. if (!bx) {
  1040. x = _rcDesk.left + MulDiv(prcNewPreview->left - _DeskOff.x,1000,_DeskScale);
  1041. x = x - out->left;
  1042. }
  1043. if (!by) {
  1044. y = _rcDesk.top + MulDiv(prcNewPreview->top - _DeskOff.y,1000,_DeskScale);
  1045. y = y - out->top;
  1046. }
  1047. } else {
  1048. x = dtLeft * 8;
  1049. y = dtTop * 8;
  1050. }
  1051. OffsetRect(out, x, y);
  1052. }
  1053. //-----------------------------------------------------------------------------
  1054. int CSettingsPage::_SaveSettings(CDisplaySettings *rgpds[], ULONG numDevices, HWND hDlg, DWORD dwSet)
  1055. {
  1056. int iRet = 0;
  1057. ULONG iDevice;
  1058. for (iDevice = 0; iDevice < numDevices; iDevice++)
  1059. {
  1060. // PERF - we should only save the settings for devices that have
  1061. // changed.
  1062. if (rgpds[iDevice])
  1063. {
  1064. int iResult = rgpds[iDevice]->SaveSettings(dwSet);
  1065. if (iResult != DISP_CHANGE_SUCCESSFUL)
  1066. {
  1067. if (iResult == DISP_CHANGE_RESTART)
  1068. {
  1069. iRet = iResult;
  1070. continue;
  1071. }
  1072. else
  1073. {
  1074. FmtMessageBox(hDlg,
  1075. MB_ICONEXCLAMATION,
  1076. IDS_CHANGE_SETTINGS,
  1077. IDS_CHANGESETTINGS_FAILED);
  1078. ASSERT(iResult < 0);
  1079. return iResult;
  1080. }
  1081. }
  1082. }
  1083. }
  1084. return iRet;
  1085. }
  1086. INT_PTR CALLBACK KeepNewDlgProc(HWND hDlg, UINT message , WPARAM wParam, LPARAM lParam)
  1087. {
  1088. UINT_PTR idTimer = 0;
  1089. HICON hicon;
  1090. TCHAR szRevert[100];
  1091. TCHAR szString[120];
  1092. switch(message)
  1093. {
  1094. case WM_INITDIALOG:
  1095. hicon = LoadIcon(NULL, IDI_QUESTION);
  1096. if (hicon)
  1097. SendDlgItemMessage(hDlg, IDC_BIGICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hicon);
  1098. LoadString(HINST_THISDLL, IDS_REVERTBACK, szRevert, ARRAYSIZE(szRevert));
  1099. wnsprintf(szString, ARRAYSIZE(szString), szRevert, lParam);
  1100. SetDlgItemText(hDlg, IDC_COUNTDOWN, szString);
  1101. idTimer = SetTimer(hDlg, lParam, 1000, NULL);
  1102. SetFocus(GetDlgItem(hDlg, IDNO));
  1103. // FALSE so that the focus set above is kept
  1104. return FALSE;
  1105. case WM_DESTROY:
  1106. // raymondc - this code is dead; idTimer is initialized to zero
  1107. // fortunately, timers are automatically killed at window destruction
  1108. // if (idTimer)
  1109. // KillTimer(hDlg, idTimer);
  1110. hicon = (HICON)SendDlgItemMessage(hDlg, IDC_BIGICON, STM_GETIMAGE, IMAGE_ICON, 0);
  1111. if (hicon)
  1112. DestroyIcon(hicon);
  1113. break;
  1114. case WM_TIMER:
  1115. KillTimer(hDlg, wParam);
  1116. LoadString(HINST_THISDLL, IDS_REVERTBACK, szRevert, ARRAYSIZE(szRevert));
  1117. wnsprintf(szString, ARRAYSIZE(szString), szRevert, wParam - 1);
  1118. SetDlgItemText(hDlg, IDC_COUNTDOWN, szString);
  1119. idTimer = SetTimer(hDlg, wParam - 1, 1000, NULL);
  1120. if (wParam == 1)
  1121. EndDialog(hDlg, IDNO);
  1122. break;
  1123. case WM_COMMAND:
  1124. EndDialog(hDlg, wParam);
  1125. break;
  1126. default:
  1127. return FALSE;
  1128. }
  1129. return TRUE;
  1130. }
  1131. BOOL CSettingsPage::_RebuildDisplaySettings(BOOL bComplete)
  1132. {
  1133. BOOL result = TRUE;
  1134. HCURSOR hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  1135. for (ULONG iDevice = 0; iDevice < _NumDevices; iDevice++)
  1136. {
  1137. _Devices[iDevice].pds->Release();
  1138. _Devices[iDevice].pds = new CDisplaySettings();
  1139. }
  1140. RedrawWindow(_hDlg, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN);
  1141. SetCursor(hcur);
  1142. return result;
  1143. }
  1144. int CSettingsPage::GetNumberOfAttachedDisplays()
  1145. {
  1146. int nDisplays = 0;
  1147. for (ULONG iDevice = 0; iDevice < _NumDevices; iDevice++)
  1148. {
  1149. if (_Devices[iDevice].pds->IsAttached())
  1150. nDisplays++;
  1151. }
  1152. return nDisplays;
  1153. }
  1154. BOOL CSettingsPage::_AnyColorChange(CDisplaySettings *rgpds[], ULONG numDevices)
  1155. {
  1156. for (ULONG iDevice = 0; iDevice < numDevices; iDevice++)
  1157. {
  1158. if (rgpds[iDevice]->IsAttached() && rgpds[iDevice]->IsColorChanged())
  1159. return TRUE;
  1160. }
  1161. return FALSE;
  1162. }
  1163. /* static */ BOOL CSettingsPage::_IsSingleToMultimonChange(CDisplaySettings *rgpds[],
  1164. ULONG numDevices)
  1165. {
  1166. int nAttached = 0;
  1167. int nOrgAttached = 0;
  1168. for (ULONG iDevice = 0;
  1169. (iDevice < numDevices) && (nOrgAttached <= 1);
  1170. iDevice++)
  1171. {
  1172. if (rgpds[iDevice]->IsOrgAttached())
  1173. nOrgAttached++;
  1174. if (rgpds[iDevice]->IsAttached())
  1175. nAttached++;
  1176. }
  1177. return ((nOrgAttached <= 1) && (nAttached > 1));
  1178. }
  1179. BOOL CSettingsPage::_AnyChange(CDisplaySettings *rgpds[], ULONG numDevices)
  1180. {
  1181. for (ULONG iDevice = 0; iDevice < numDevices; iDevice++)
  1182. {
  1183. if (rgpds[iDevice]->IsAttached() && rgpds[iDevice]->bIsModeChanged())
  1184. {
  1185. return TRUE;
  1186. }
  1187. }
  1188. return FALSE;
  1189. }
  1190. BOOL CSettingsPage::_CanSkipWarningBecauseKnownSafe(CDisplaySettings *rgpds[], ULONG numDevices)
  1191. {
  1192. BOOL fSafe = TRUE;
  1193. for (ULONG iDevice = 0; iDevice < numDevices; iDevice++)
  1194. {
  1195. if (rgpds[iDevice] && !rgpds[iDevice]->IsKnownSafe())
  1196. {
  1197. fSafe = FALSE;
  1198. break;
  1199. }
  1200. }
  1201. return fSafe;
  1202. }
  1203. BOOL CSettingsPage::_QueryForceSmallFont()
  1204. {
  1205. for (ULONG iDevice = 0; iDevice < _NumDevices; iDevice++)
  1206. {
  1207. if ((_Devices[iDevice].pds->IsAttached()) &&
  1208. (!_Devices[iDevice].pds->IsSmallFontNecessary()))
  1209. {
  1210. return FALSE;
  1211. }
  1212. }
  1213. return TRUE;
  1214. }
  1215. LPTSTR CSettingsPage::_FormatMessageInvoke(LPCTSTR pcszFormat, va_list *argList)
  1216. {
  1217. LPTSTR pszOutput;
  1218. if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
  1219. pcszFormat,
  1220. 0, 0,
  1221. reinterpret_cast<LPTSTR>(&pszOutput), 0,
  1222. argList) == 0)
  1223. {
  1224. pszOutput = NULL;
  1225. }
  1226. return(pszOutput);
  1227. }
  1228. LPTSTR CSettingsPage::_FormatMessageWrap(LPCTSTR pcszFormat, ...)
  1229. {
  1230. LPTSTR pszOutput;
  1231. va_list argList;
  1232. va_start(argList, pcszFormat);
  1233. pszOutput = _FormatMessageInvoke(pcszFormat, &argList);
  1234. va_end(argList);
  1235. return(pszOutput);
  1236. }
  1237. void CSettingsPage::_GetDisplayName(PMULTIMON_DEVICE pDevice, LPTSTR pszDisplay, DWORD cchSize)
  1238. {
  1239. LPTSTR pszFormattedOutput;
  1240. TCHAR szMonitor[140];
  1241. TCHAR szDisplayFormat[40];
  1242. LoadString(HINST_THISDLL, IDS_DISPLAYFORMAT, szDisplayFormat, ARRAYSIZE(szDisplayFormat));
  1243. pDevice->pds->GetMonitorName(szMonitor, ARRAYSIZE(szMonitor));
  1244. pszFormattedOutput = _FormatMessageWrap(szDisplayFormat,
  1245. pDevice->DisplayIndex,
  1246. szMonitor,
  1247. pDevice->DisplayDevice.DeviceString);
  1248. lstrcpyn(pszDisplay, pszFormattedOutput, cchSize);
  1249. LocalFree(pszFormattedOutput);
  1250. }
  1251. void CSettingsPage::_OnAdvancedClicked()
  1252. {
  1253. BOOL bCanBePruned, bIsPruningReadOnly;
  1254. BOOL bBeforeIsPruningOn, bAfterIsPruningOn;
  1255. if (_pCurDevice && _pCurDevice->pds)
  1256. {
  1257. _pCurDevice->pds->GetPruningMode(&bCanBePruned,
  1258. &bIsPruningReadOnly,
  1259. &bBeforeIsPruningOn);
  1260. _DoAdvancedSettingsSheet();
  1261. if (bCanBePruned && !bIsPruningReadOnly)
  1262. {
  1263. _pCurDevice->pds->GetPruningMode(&bCanBePruned,
  1264. &bIsPruningReadOnly,
  1265. &bAfterIsPruningOn);
  1266. if (bBeforeIsPruningOn != bAfterIsPruningOn)
  1267. {
  1268. // pruning mode has changed - update the UI
  1269. _InitUI();
  1270. _UpdateUI();
  1271. }
  1272. }
  1273. }
  1274. }
  1275. //-----------------------------------------------------------------------------
  1276. void CSettingsPage::_DoAdvancedSettingsSheet()
  1277. {
  1278. if (_pCurDevice && _pCurDevice->pds)
  1279. {
  1280. PROPSHEETHEADER psh;
  1281. HPROPSHEETPAGE rPages[MAX_PAGES];
  1282. PROPSHEETPAGE psp;
  1283. HPSXA hpsxa = NULL;
  1284. HPSXA hpsxaOEM = NULL;
  1285. HPSXA hpsxaAdapter = NULL;
  1286. HPSXA* phpsxaChildren = NULL;
  1287. INT_PTR iResult = 0;
  1288. TCHAR szDisplay[140 + 256 + 20]; //Monitor-name and Adapter Properties.
  1289. TCHAR szMonitor[140];
  1290. TCHAR szDisplayFormat[35];
  1291. GENERAL_ADVDLG_INITPARAMS generalInitParams = {0};
  1292. // Create the "Monitor-name and Adapter-name properties" string to be used as the title for these
  1293. // property sheets.
  1294. LoadString(HINST_THISDLL, IDS_ADVDIALOGTITLE, szDisplayFormat, ARRAYSIZE(szDisplayFormat));
  1295. _pCurDevice->pds->GetMonitorName(szMonitor, ARRAYSIZE(szMonitor));
  1296. wnsprintf(szDisplay, ARRAYSIZE(szDisplay), szDisplayFormat, szMonitor, _pCurDevice->DisplayDevice.DeviceString);
  1297. generalInitParams.fFoceSmallFont = _QueryForceSmallFont();
  1298. generalInitParams.punkSite = _punkSite; // They don't get a ref because their property dialog appears and goes away before this function returns.
  1299. psh.dwSize = sizeof(psh);
  1300. psh.dwFlags = PSH_PROPTITLE;
  1301. psh.hwndParent = GetParent(_hDlg);
  1302. psh.hInstance = HINST_THISDLL;
  1303. psh.pszCaption = szDisplay;
  1304. psh.nPages = 0;
  1305. psh.nStartPage = 0;
  1306. psh.phpage = rPages;
  1307. psp.dwSize = sizeof(psp);
  1308. psp.dwFlags = PSP_DEFAULT;
  1309. psp.hInstance = HINST_THISDLL;
  1310. psp.pfnDlgProc = GeneralPageProc;
  1311. psp.pszTemplate = MAKEINTRESOURCE(DLG_GENERAL);
  1312. psp.lParam = (LPARAM)&generalInitParams;
  1313. rPages[psh.nPages] = CreatePropertySheetPage(&psp);
  1314. if (rPages[psh.nPages])
  1315. psh.nPages++;
  1316. IDataObject * pdo = NULL;
  1317. _pCurDevice->pds->QueryInterface(IID_IDataObject, (LPVOID *) &pdo);
  1318. CRegistrySettings RegSettings(_pCurDevice->DisplayDevice.DeviceKey);
  1319. HKEY hkDriver = RegSettings.OpenDrvRegKey();
  1320. if (hkDriver != INVALID_HANDLE_VALUE)
  1321. {
  1322. CheckForDuplicateAppletExtensions(hkDriver);
  1323. }
  1324. //
  1325. // load the generic (non hardware specific) extensions
  1326. //
  1327. if( ( hpsxa = SHCreatePropSheetExtArrayEx( HKEY_LOCAL_MACHINE, REGSTR_PATH_CONTROLSFOLDER TEXT("\\Device"), 8, pdo) ) != NULL )
  1328. {
  1329. SHAddFromPropSheetExtArray( hpsxa, _AddDisplayPropSheetPage, (LPARAM)&psh );
  1330. }
  1331. //
  1332. // Load the hardware-specific extensions
  1333. //
  1334. // NOTE it is very important to load the OEM extensions *after* the
  1335. // generic extensions some HW extensions expect to be the last tabs
  1336. // in the propsheet (right before the settings tab)
  1337. //
  1338. // FEATURE - we may need a way to NOT load the vendor extensions in case
  1339. // they break our applet.
  1340. //
  1341. if( ( hpsxaOEM = SHCreatePropSheetExtArrayEx( HKEY_LOCAL_MACHINE, REGSTR_PATH_CONTROLSFOLDER TEXT("\\Display"), 8, pdo) ) != NULL )
  1342. {
  1343. SHAddFromPropSheetExtArray( hpsxaOEM, _AddDisplayPropSheetPage, (LPARAM)&psh );
  1344. }
  1345. //
  1346. // Load the applet extensions for the adapter
  1347. //
  1348. if (hkDriver != INVALID_HANDLE_VALUE)
  1349. {
  1350. if( ( hpsxaAdapter = SHCreatePropSheetExtArrayEx( hkDriver, TEXT("Display"), 8, pdo) ) != NULL )
  1351. {
  1352. SHAddFromPropSheetExtArray( hpsxaAdapter, _AddDisplayPropSheetPage, (LPARAM)&psh );
  1353. }
  1354. RegCloseKey(hkDriver);
  1355. }
  1356. //
  1357. // Load the applet extensions for the adapter child devices (e.g. monitors)
  1358. //
  1359. DEVINST devInstAdapter, devInstMonitor;
  1360. DWORD cChildDevices = 0, nChild, index;
  1361. HDEVINFO hDevMonitors = INVALID_HANDLE_VALUE;
  1362. SP_DEVINFO_DATA DevInfoData;
  1363. HKEY hkMonitor;
  1364. BOOL bMonitors = FALSE;
  1365. LPTSTR szAdapterInstanceID = RegSettings.GetDeviceInstanceId();
  1366. if (szAdapterInstanceID != NULL)
  1367. {
  1368. if (CM_Locate_DevNodeW(&devInstAdapter, szAdapterInstanceID, 0) == CR_SUCCESS)
  1369. {
  1370. //
  1371. // Get the number of child devices
  1372. //
  1373. cChildDevices = 0;
  1374. if (CM_Get_Child(&devInstMonitor, devInstAdapter, 0) == CR_SUCCESS)
  1375. {
  1376. do
  1377. {
  1378. cChildDevices++;
  1379. }
  1380. while (CM_Get_Sibling(&devInstMonitor, devInstMonitor, 0) == CR_SUCCESS);
  1381. }
  1382. //
  1383. // Allocate the memory
  1384. //
  1385. if (cChildDevices > 0)
  1386. {
  1387. phpsxaChildren = (HPSXA*)LocalAlloc(LPTR, cChildDevices * sizeof(HPSXA));
  1388. hDevMonitors = SetupDiGetClassDevs((LPGUID)&GUID_DEVCLASS_MONITOR,
  1389. NULL,
  1390. NULL,
  1391. 0);
  1392. }
  1393. //
  1394. // Load the applet extensions
  1395. //
  1396. if ((phpsxaChildren != NULL) &&
  1397. (hDevMonitors != INVALID_HANDLE_VALUE))
  1398. {
  1399. nChild = 0;
  1400. if (CM_Get_Child(&devInstMonitor, devInstAdapter, 0) == CR_SUCCESS)
  1401. {
  1402. do
  1403. {
  1404. DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
  1405. index = 0;
  1406. while (SetupDiEnumDeviceInfo(hDevMonitors,
  1407. index,
  1408. &DevInfoData)) {
  1409. if (DevInfoData.DevInst == devInstMonitor) {
  1410. hkMonitor = SetupDiOpenDevRegKey(hDevMonitors,
  1411. &DevInfoData,
  1412. DICS_FLAG_GLOBAL,
  1413. 0,
  1414. DIREG_DRV ,
  1415. KEY_ALL_ACCESS);
  1416. if (hkMonitor != INVALID_HANDLE_VALUE)
  1417. {
  1418. if ((phpsxaChildren[nChild] = SHCreatePropSheetExtArrayEx(hkMonitor,
  1419. TEXT("Display"),
  1420. 8,
  1421. pdo)) != NULL)
  1422. {
  1423. bMonitors = TRUE;
  1424. SHAddFromPropSheetExtArray(phpsxaChildren[nChild],
  1425. _AddDisplayPropSheetPage,
  1426. (LPARAM)&psh);
  1427. }
  1428. RegCloseKey(hkMonitor);
  1429. }
  1430. break;
  1431. }
  1432. DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
  1433. index++;
  1434. }
  1435. nChild++;
  1436. }
  1437. while ((nChild < cChildDevices) &&
  1438. (CM_Get_Sibling(&devInstMonitor, devInstMonitor, 0) == CR_SUCCESS));
  1439. }
  1440. }
  1441. }
  1442. }
  1443. //
  1444. // add a fake settings page to fool OEM extensions (must be last)
  1445. //
  1446. if (hpsxa || hpsxaOEM || hpsxaAdapter || bMonitors)
  1447. {
  1448. AddFakeSettingsPage(_pThemeUI, &psh);
  1449. }
  1450. if (psh.nPages)
  1451. {
  1452. iResult = PropertySheet(&psh);
  1453. }
  1454. _GetDisplayName(_pCurDevice, szDisplay, ARRAYSIZE(szDisplay));
  1455. if (_NumDevices == 1)
  1456. {
  1457. //Set the name of the primary in the static text
  1458. //strip the first token off (this is the number we dont want it)
  1459. TCHAR *pch;
  1460. for (pch=szDisplay; *pch && *pch != TEXT(' '); pch++);
  1461. for (;*pch && *pch == TEXT(' '); pch++);
  1462. SetDlgItemText(_hDlg, IDC_DISPLAYTEXT, pch);
  1463. }
  1464. else
  1465. {
  1466. ComboBox_DeleteString(_hwndList, _pCurDevice->ComboBoxItem);
  1467. ComboBox_InsertString(_hwndList, _pCurDevice->ComboBoxItem, szDisplay);
  1468. ComboBox_SetItemData(_hwndList, _pCurDevice->ComboBoxItem, (DWORD_PTR)_pCurDevice);
  1469. ComboBox_SetCurSel(_hwndList, _pCurDevice->ComboBoxItem);
  1470. }
  1471. if( hpsxa )
  1472. SHDestroyPropSheetExtArray( hpsxa );
  1473. if( hpsxaOEM )
  1474. SHDestroyPropSheetExtArray( hpsxaOEM );
  1475. if( hpsxaAdapter )
  1476. SHDestroyPropSheetExtArray( hpsxaAdapter );
  1477. if (phpsxaChildren != NULL)
  1478. {
  1479. for (nChild = 0; nChild < cChildDevices; nChild++) {
  1480. if (phpsxaChildren[nChild] != NULL)
  1481. {
  1482. SHDestroyPropSheetExtArray(phpsxaChildren[nChild]);
  1483. }
  1484. }
  1485. LocalFree(phpsxaChildren);
  1486. }
  1487. if (hDevMonitors != INVALID_HANDLE_VALUE)
  1488. {
  1489. SetupDiDestroyDeviceInfoList(hDevMonitors);
  1490. }
  1491. if (pdo)
  1492. pdo->Release();
  1493. if ((iResult == ID_PSRESTARTWINDOWS) || (iResult == ID_PSREBOOTSYSTEM))
  1494. {
  1495. PropSheet_CancelToClose(GetParent(_hDlg));
  1496. if (iResult == ID_PSREBOOTSYSTEM)
  1497. PropSheet_RebootSystem(ghwndPropSheet);
  1498. else
  1499. PropSheet_RestartWindows(ghwndPropSheet);
  1500. }
  1501. //
  1502. // APPCOMPAT
  1503. // Reset the dirty flag based on what the extensions did.
  1504. //
  1505. //
  1506. // Reset the controls in case someone changed the selected mode.
  1507. //
  1508. UpdateActiveDisplay(NULL);
  1509. }
  1510. }
  1511. //-----------------------------------------------------------------------------
  1512. void CSettingsPage::UpdateActiveDisplay(PMULTIMON_DEVICE pDevice, BOOL bRepaint /*=TRUE*/)
  1513. {
  1514. if (_pCurDevice && _pCurDevice->pds)
  1515. {
  1516. HWND hwndC;
  1517. _InSetInfo++;
  1518. if (pDevice == NULL)
  1519. pDevice = (PMULTIMON_DEVICE)ComboBox_GetItemData(_hwndList, ComboBox_GetCurSel(_hwndList));
  1520. else
  1521. ComboBox_SetCurSel(_hwndList, pDevice->ComboBoxItem);
  1522. if (pDevice && pDevice != (PMULTIMON_DEVICE)CB_ERR)
  1523. {
  1524. hwndC = GetCurDeviceHwnd();
  1525. // The Current Device has changed, so, force recreating the bitmap the next time
  1526. // we paint the monitor on the preview window.
  1527. _pCurDevice->w = pDevice->w = 0;
  1528. _pCurDevice = pDevice;
  1529. if (hwndC)
  1530. RedrawWindow(hwndC, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
  1531. hwndC = GetCurDeviceHwnd();
  1532. if (hwndC)
  1533. RedrawWindow(hwndC, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
  1534. if(_NumDevices > 1)
  1535. {
  1536. // Update the two check box windows
  1537. CheckDlgButton(_hDlg, IDC_DISPLAYPRIME, _pCurDevice->pds->IsPrimary());
  1538. EnableWindow(GetDlgItem(_hDlg, IDC_DISPLAYPRIME),
  1539. _pCurDevice->pds->IsAttached() &&
  1540. !_pCurDevice->pds->IsRemovable() &&
  1541. !_pCurDevice->pds->IsPrimary());
  1542. CheckDlgButton(_hDlg, IDC_DISPLAYUSEME, _pCurDevice->pds->IsAttached());
  1543. EnableWindow(GetDlgItem(_hDlg, IDC_DISPLAYUSEME),
  1544. !_bNoAttach && !_pCurDevice->pds->IsPrimary());
  1545. }
  1546. // Reset the values for the list boxes, and then repaint it
  1547. if(bRepaint)
  1548. {
  1549. _InitUI();
  1550. _UpdateUI(FALSE /*fAutoSetColorDepth*/);
  1551. }
  1552. }
  1553. else
  1554. {
  1555. // No display device !
  1556. TraceMsg(TF_WARNING, "**** UpdateActiveDisplay: No display device!!!!");
  1557. ASSERT(FALSE);
  1558. }
  1559. _InSetInfo--;
  1560. }
  1561. }
  1562. // ---------------------------------------------------------------------------
  1563. // Initialize the resolution and color UI widgets
  1564. //
  1565. void CSettingsPage::_InitUI()
  1566. {
  1567. if (_pCurDevice && _pCurDevice->pds)
  1568. {
  1569. int i;
  1570. int Color;
  1571. // Update the Color list
  1572. TraceMsg(TF_FUNC, "_InitUI() -- Color list");
  1573. SendDlgItemMessage(_hDlg, IDC_COLORBOX, CB_RESETCONTENT, 0, 0);
  1574. if (_pCurDevice->ColorList)
  1575. {
  1576. LocalFree(_pCurDevice->ColorList);
  1577. _pCurDevice->ColorList = NULL;
  1578. }
  1579. _pCurDevice->cColors = _pCurDevice->pds->GetColorList(NULL, &_pCurDevice->ColorList);
  1580. for (i = 0; i < _pCurDevice->cColors; i++)
  1581. {
  1582. TCHAR achColor[50];
  1583. DWORD idColor = ID_DSP_TXT_TRUECOLOR32;
  1584. Color = (int) *(_pCurDevice->ColorList + i);
  1585. //
  1586. // convert bit count to number of colors and make it a string
  1587. //
  1588. switch (Color)
  1589. {
  1590. case 32: idColor = ID_DSP_TXT_TRUECOLOR32; break;
  1591. case 24: idColor = ID_DSP_TXT_TRUECOLOR24; break;
  1592. case 16: idColor = ID_DSP_TXT_16BIT_COLOR; break;
  1593. case 15: idColor = ID_DSP_TXT_15BIT_COLOR; break;
  1594. case 8: idColor = ID_DSP_TXT_8BIT_COLOR; break;
  1595. case 4: idColor = ID_DSP_TXT_4BIT_COLOR; break;
  1596. default:
  1597. ASSERT(FALSE);
  1598. }
  1599. LoadString(HINST_THISDLL, idColor, achColor, ARRAYSIZE(achColor));
  1600. SendDlgItemMessage(_hDlg, IDC_COLORBOX, CB_INSERTSTRING, i, (LPARAM)achColor);
  1601. }
  1602. //
  1603. // Update the screen Size List
  1604. //
  1605. TraceMsg(TF_FUNC, "_InitUI() -- Screen Size list");
  1606. if (_pCurDevice->ResolutionList)
  1607. {
  1608. LocalFree(_pCurDevice->ResolutionList);
  1609. _pCurDevice->ResolutionList = NULL;
  1610. }
  1611. _pCurDevice->cResolutions =
  1612. _pCurDevice->pds->GetResolutionList(-1, &_pCurDevice->ResolutionList);
  1613. SendDlgItemMessage(_hDlg, IDC_SCREENSIZE, TBM_SETRANGE, TRUE,
  1614. MAKELONG(0, _pCurDevice->cResolutions - 1));
  1615. TraceMsg(TF_FUNC, "_InitUI() -- Res MaxRange = %d", _pCurDevice->cResolutions - 1);
  1616. // Reset the indices since they are no longer valid
  1617. _iResolution = -1;
  1618. _iColor = -1;
  1619. //EnableWindow(GetDlgItem(_hDlg, IDC_COLORBOX), _fOrgAttached);
  1620. //EnableWindow(GetDlgItem(_hDlg, IDC_SCREENSIZE), _fOrgAttached);
  1621. }
  1622. }
  1623. // ---------------------------------------------------------------------------
  1624. // Update the resolution and color UI widgets
  1625. //
  1626. void CSettingsPage::_UpdateUI(BOOL fAutoSetColorDepth, int FocusToCtrlID)
  1627. {
  1628. if (_pCurDevice && _pCurDevice->pds)
  1629. {
  1630. int i;
  1631. POINT Res;
  1632. int Color;
  1633. BOOL bRepaint;
  1634. // Get the current values
  1635. _pCurDevice->pds->GetCurResolution(&Res);
  1636. Color = _pCurDevice->pds->GetCurColor();
  1637. // Update the color listbox
  1638. TraceMsg(TF_FUNC, "_UpdateUI() -- Set Color %d", Color);
  1639. for (i=0; i<_pCurDevice->cColors; i++)
  1640. {
  1641. if (Color == (int) *(_pCurDevice->ColorList + i))
  1642. {
  1643. TraceMsg(TF_FUNC, "_UpdateUI() -- Set Color index %d", i);
  1644. if (_iColor == i)
  1645. {
  1646. TraceMsg(TF_FUNC, "_UpdateUI() -- Set Color index %d - is current", i);
  1647. break;
  1648. }
  1649. HBITMAP hbm, hbmOld;
  1650. int iBitmap = IDB_COLOR4DITHER;
  1651. HDC hdc = GetDC(NULL);
  1652. int bpp = GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL);
  1653. SendDlgItemMessage(_hDlg, IDC_COLORBOX, CB_SETCURSEL, i, 0);
  1654. if (Color <= 4)
  1655. iBitmap = IDB_COLOR4;
  1656. else if (bpp >= 16)
  1657. {
  1658. if (Color <= 8)
  1659. iBitmap = IDB_COLOR8;
  1660. else if (Color <= 16)
  1661. iBitmap = IDB_COLOR16;
  1662. else
  1663. iBitmap = IDB_COLOR24;
  1664. }
  1665. ReleaseDC(NULL, hdc);
  1666. hbm = (HBITMAP)LoadImage(HINST_THISDLL, MAKEINTRESOURCE(iBitmap), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
  1667. if (hbm)
  1668. {
  1669. hbmOld = (HBITMAP) SendDlgItemMessage(_hDlg, IDC_COLORSAMPLE, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbm);
  1670. if (hbmOld)
  1671. {
  1672. DeleteObject(hbmOld);
  1673. }
  1674. }
  1675. _iColor = i;
  1676. break;
  1677. }
  1678. }
  1679. if (i == _pCurDevice->cColors)
  1680. {
  1681. TraceMsg(TF_ERROR, "_UpdateUI -- !!! inconsistent color list !!!");
  1682. }
  1683. TraceMsg(TF_FUNC, "_UpdateUI() -- Set Resolution %d %d", Res.x, Res.y);
  1684. // Update the resolution string
  1685. {
  1686. TCHAR achStr[80];
  1687. TCHAR achRes[120];
  1688. LoadString(HINST_THISDLL, ID_DSP_TXT_XBYY, achStr, ARRAYSIZE(achStr));
  1689. wnsprintf(achRes, ARRAYSIZE(achRes), achStr, Res.x, Res.y);
  1690. SendDlgItemMessage(_hDlg, IDC_RESXY, WM_SETTEXT, 0, (LPARAM)achRes);
  1691. }
  1692. // Update the resolution slider
  1693. for (i=0; i<_pCurDevice->cResolutions; i++)
  1694. {
  1695. if ( (Res.x == (*(_pCurDevice->ResolutionList + i)).x) &&
  1696. (Res.y == (*(_pCurDevice->ResolutionList + i)).y) )
  1697. {
  1698. TraceMsg(TF_FUNC, "_UpdateUI() -- Set Resolution index %d", i);
  1699. if (_iResolution == i)
  1700. {
  1701. TraceMsg(TF_FUNC, "_UpdateUI() -- Set Resolution index %d - is current", i);
  1702. break;
  1703. }
  1704. SendDlgItemMessage(_hDlg, IDC_SCREENSIZE, TBM_SETPOS, TRUE, i);
  1705. break;
  1706. }
  1707. }
  1708. if (i == _pCurDevice->cResolutions)
  1709. {
  1710. TraceMsg(TF_ERROR, "_UpdateUI -- !!! inconsistent color list !!!");
  1711. }
  1712. bRepaint = (i != _iResolution);
  1713. _iResolution = i;
  1714. // If the resolution has changed, we have to repaint the preview window
  1715. // Set the focus back to the trackbar after the repaint so any further
  1716. // kb events will be send to it rather than the preview window
  1717. if (bRepaint) {
  1718. SendMessage(_hDlg, MM_REDRAWPREVIEW, 0, 0);
  1719. }
  1720. if (FocusToCtrlID != 0) {
  1721. SetFocus(GetDlgItem(_hDlg, FocusToCtrlID));
  1722. }
  1723. }
  1724. }
  1725. //----------------------------------------------------------------------------
  1726. //
  1727. // SetPrimary()
  1728. //
  1729. //----------------------------------------------------------------------------
  1730. BOOL
  1731. CSettingsPage::SetPrimary(
  1732. PMULTIMON_DEVICE pDevice)
  1733. {
  1734. //
  1735. // Check if state is already set.
  1736. //
  1737. if (pDevice == _pPrimaryDevice)
  1738. {
  1739. pDevice->pds->SetPrimary(TRUE);
  1740. return TRUE;
  1741. }
  1742. ASSERT(pDevice->pds->IsAttached());
  1743. _pPrimaryDevice->pds->SetPrimary(FALSE);
  1744. pDevice->pds->SetPrimary(TRUE);
  1745. _pPrimaryDevice = pDevice;
  1746. SetDirty();
  1747. return TRUE;
  1748. }
  1749. //----------------------------------------------------------------------------
  1750. //
  1751. // SetMonAttached()
  1752. //
  1753. //----------------------------------------------------------------------------
  1754. BOOL
  1755. CSettingsPage::SetMonAttached(
  1756. PMULTIMON_DEVICE pDevice,
  1757. BOOL bSetAttached,
  1758. BOOL bForce,
  1759. HWND hwnd)
  1760. {
  1761. if (pDevice->pds->IsAttached() == bSetAttached)
  1762. {
  1763. return TRUE;
  1764. }
  1765. if (bSetAttached)
  1766. {
  1767. //
  1768. // Make sure this device actually has a rectangle.
  1769. // If it does not (not configured in the registry, then we need
  1770. // to put up a popup and ask the user to configure the device.
  1771. //
  1772. if (hwnd)
  1773. {
  1774. //
  1775. // Check to see if we should ask the user about enabling this device
  1776. //
  1777. if (bForce == FALSE)
  1778. {
  1779. TCHAR szTurnItOn[400];
  1780. TCHAR szTurnOnTitleFormat[30];
  1781. TCHAR szTurnOnTitle[110];
  1782. LPTSTR pstr = szTurnItOn;
  1783. DWORD chSize = ARRAYSIZE(szTurnItOn);
  1784. LoadString(HINST_THISDLL, IDS_TURNONTITLE, szTurnOnTitleFormat, ARRAYSIZE(szTurnOnTitleFormat));
  1785. wnsprintf(szTurnOnTitle, ARRAYSIZE(szTurnOnTitle), szTurnOnTitleFormat, pDevice->DisplayIndex);
  1786. if (GetNumberOfAttachedDisplays() == 1)
  1787. {
  1788. LoadString(HINST_THISDLL, IDS_TURNONMSG, szTurnItOn, ARRAYSIZE(szTurnItOn));
  1789. pstr += lstrlen(szTurnItOn);
  1790. chSize -= lstrlen(szTurnItOn);
  1791. }
  1792. LoadString(HINST_THISDLL, IDS_TURNITON, pstr, chSize);
  1793. if (ShellMessageBox(HINST_THISDLL, hwnd, szTurnItOn, szTurnOnTitle,
  1794. MB_YESNO | MB_ICONINFORMATION) != IDYES)
  1795. {
  1796. return FALSE;
  1797. }
  1798. }
  1799. }
  1800. pDevice->pds->SetAttached(TRUE);
  1801. }
  1802. else // (bSetAttached == FALSE)
  1803. {
  1804. //
  1805. // Can't detach if we have only one device or it's the primary.
  1806. // The UI should disable this situation
  1807. //
  1808. if ((GetNumberOfAttachedDisplays() == 1) ||
  1809. pDevice->pds->IsPrimary())
  1810. {
  1811. ASSERT(FALSE);
  1812. }
  1813. pDevice->pds->SetAttached(FALSE);
  1814. }
  1815. SetDirty();
  1816. return TRUE;
  1817. }
  1818. //----------------------------------------------------------------------------
  1819. //
  1820. // SetDirty
  1821. //
  1822. //----------------------------------------------------------------------------
  1823. void CSettingsPage::SetDirty(BOOL bDirty)
  1824. {
  1825. _bDirty = bDirty;
  1826. if (_bDirty)
  1827. {
  1828. EnableApplyButton(_hDlg);
  1829. // PostMessage(GetParent(_hDlg), PSM_CHANGED, (WPARAM)_hDlg, 0L);
  1830. }
  1831. }
  1832. //-----------------------------------------------------------------------------
  1833. void CSettingsPage::_CleanupRects(HWND hwndP)
  1834. {
  1835. int n;
  1836. HWND hwndC;
  1837. DWORD arcDev[MONITORS_MAX];
  1838. RECT arc[MONITORS_MAX];
  1839. DWORD iArcPrimary = 0;
  1840. RECT rc;
  1841. RECT rcU;
  1842. int i;
  1843. RECT rcPrev;
  1844. int sx,sy;
  1845. int x,y;
  1846. //
  1847. // get the positions of all the windows
  1848. //
  1849. n = 0;
  1850. for (ULONG iDevice = 0; iDevice < _NumDevices; iDevice++)
  1851. {
  1852. PMULTIMON_DEVICE pDevice = &_Devices[iDevice];
  1853. hwndC = GetDlgItemP(hwndP, (INT_PTR) pDevice);
  1854. if (hwndC != NULL)
  1855. {
  1856. RECT rcPos;
  1857. RECT rcPreview;
  1858. TraceMsg(TF_GENERAL, "_CleanupRects start Device %08lx, Dev = %d, hwnd = %08lx",
  1859. pDevice, iDevice, hwndC);
  1860. ShowWindow(hwndC, SW_SHOW);
  1861. GetWindowRect(hwndC, &arc[n]);
  1862. MapWindowPoints(NULL, hwndP, (POINT FAR*)&arc[n], 2);
  1863. pDevice->pds->GetCurPosition(&rcPos);
  1864. pDevice->pds->GetPreviewPosition(&rcPreview);
  1865. _OffsetPreviewToDesk(hwndC, &arc[n], &rcPreview, &rcPos);
  1866. arc[n] = rcPos;
  1867. arcDev[n] = iDevice;
  1868. // TEMP
  1869. // For non-atached devices, make sure they end up to the right
  1870. // Eventually, non-attached devices should be showed aligned on the
  1871. // right hand side of the window.
  1872. if (!pDevice->pds->IsAttached())
  1873. {
  1874. OffsetRect(&arc[n], 10000, 0);
  1875. }
  1876. if (pDevice->pds->IsPrimary())
  1877. {
  1878. TraceMsg(TF_GENERAL, "_CleanupRects primary Device %08lx", pDevice);
  1879. iArcPrimary = n;
  1880. }
  1881. n++;
  1882. }
  1883. }
  1884. //
  1885. // cleanup the rects
  1886. //
  1887. AlignRects(arc, n, iArcPrimary, CUDR_NORMAL);
  1888. //
  1889. // Get the union.
  1890. //
  1891. SetRectEmpty(&rcU);
  1892. for (i=0; i<n; i++)
  1893. UnionRect(&rcU, &rcU, &arc[i]);
  1894. GetClientRect(hwndP, &rcPrev);
  1895. //
  1896. // only rescale if the new desk hangs outside the preview area.
  1897. // or is too small
  1898. //
  1899. _DeskToPreview(&rcU, &rc);
  1900. x = ((rcPrev.right - rcPrev.left)-(rc.right - rc.left))/2;
  1901. y = ((rcPrev.bottom - rcPrev.top) -(rc.bottom - rc.top))/2;
  1902. if (rcU.left < 0 || rcU.top < 0 || x < 0 || y < 0 ||
  1903. rcU.right > rcPrev.right || rcU.bottom > rcPrev.bottom ||
  1904. (x > (rcPrev.right-rcPrev.left)/8 &&
  1905. y > (rcPrev.bottom-rcPrev.top)/8))
  1906. {
  1907. _rcDesk = rcU;
  1908. sx = MulDiv(rcPrev.right - rcPrev.left - 16,1000,_rcDesk.right - _rcDesk.left);
  1909. sy = MulDiv(rcPrev.bottom - rcPrev.top - 16,1000,_rcDesk.bottom - _rcDesk.top);
  1910. _DeskScale = min(sx,sy) * 2 / 3;
  1911. _DeskToPreview(&_rcDesk, &rc);
  1912. _DeskOff.x = ((rcPrev.right - rcPrev.left)-(rc.right - rc.left))/2;
  1913. _DeskOff.y = ((rcPrev.bottom - rcPrev.top) -(rc.bottom - rc.top))/2;
  1914. }
  1915. //
  1916. // Show all the windows and save them all to the devmode.
  1917. //
  1918. for (i=0; i < n; i++)
  1919. {
  1920. RECT rcPos;
  1921. POINT ptPos;
  1922. _Devices[arcDev[i]].pds->GetCurPosition(&rcPos);
  1923. hwndC = GetDlgItemP(hwndP, (INT_PTR) &_Devices[arcDev[i]]);
  1924. _DeskToPreview(&arc[i], &rc);
  1925. rc.right = MulDiv(RECTWIDTH(rcPos), _DeskScale, 1000);
  1926. rc.bottom = MulDiv(RECTHEIGHT(rcPos), _DeskScale, 1000);
  1927. TraceMsg(TF_GENERAL, "_CleanupRects set Dev = %d, hwnd = %08lx", arcDev[i], hwndC);
  1928. TraceMsg(TF_GENERAL, "_CleanupRects window pos %d,%d,%d,%d", rc.left, rc.top, rc.right, rc.bottom);
  1929. SetWindowPos(hwndC,
  1930. NULL,
  1931. rc.left,
  1932. rc.top,
  1933. rc.right,
  1934. rc.bottom,
  1935. SWP_NOZORDER);
  1936. rc.right += rc.left;
  1937. rc.bottom += rc.top;
  1938. _Devices[arcDev[i]].pds->SetPreviewPosition(&rc);
  1939. ptPos.x = arc[i].left;
  1940. ptPos.y = arc[i].top;
  1941. _Devices[arcDev[i]].pds->SetCurPosition(&ptPos);
  1942. }
  1943. TraceMsg(TF_GENERAL, "");
  1944. }
  1945. void CSettingsPage::_ConfirmPositions()
  1946. {
  1947. ASSERT (_NumDevices > 1);
  1948. PMULTIMON_DEVICE pDevice;
  1949. ULONG iDevice;
  1950. for (iDevice = 0; iDevice < _NumDevices; iDevice++)
  1951. {
  1952. pDevice = &_Devices[iDevice];
  1953. if (pDevice->pds->IsOrgAttached())
  1954. {
  1955. RECT rcOrg, rcCur;
  1956. pDevice->pds->GetCurPosition(&rcCur);
  1957. pDevice->pds->GetOrgPosition(&rcOrg);
  1958. if ((rcCur.left != rcOrg.left) ||
  1959. (rcCur.top != rcOrg.top))
  1960. {
  1961. POINT ptOrg;
  1962. ptOrg.x = rcCur.left;
  1963. ptOrg.y = rcCur.top;
  1964. pDevice->pds->SetOrgPosition(&ptOrg);
  1965. SetDirty(TRUE);
  1966. }
  1967. }
  1968. }
  1969. }
  1970. void CSettingsPage::GetMonitorPosition(PMULTIMON_DEVICE pDevice, HWND hwndP, PPOINT ptPos)
  1971. {
  1972. int iPrimary = 0;
  1973. HWND hwndC;
  1974. RECT rcPos;
  1975. RECT rcPreview;
  1976. RECT arc[MONITORS_MAX];
  1977. int i;
  1978. for (ULONG iDevice = 0; iDevice < _NumDevices; iDevice++)
  1979. {
  1980. PMULTIMON_DEVICE pDevice = &_Devices[iDevice];
  1981. hwndC = GetDlgItemP(hwndP, (INT_PTR) pDevice);
  1982. ASSERT(hwndC);
  1983. GetWindowRect(hwndC, &arc[iDevice]);
  1984. MapWindowPoints(NULL, hwndP, (POINT FAR*)&arc[iDevice], 2);
  1985. pDevice->pds->GetCurPosition(&rcPos);
  1986. pDevice->pds->GetPreviewPosition(&rcPreview);
  1987. _OffsetPreviewToDesk(hwndC, &arc[iDevice], &rcPreview, &rcPos);
  1988. arc[iDevice] = rcPos;
  1989. if (pDevice->pds->IsPrimary()) {
  1990. iPrimary = iDevice;
  1991. }
  1992. }
  1993. AlignRects(arc, iDevice, iPrimary, CUDR_NORMAL);
  1994. i = (int)(pDevice - _Devices);
  1995. ptPos->x = arc[i].left;
  1996. ptPos->y = arc[i].top;
  1997. }
  1998. BOOL CSettingsPage::HandleMonitorChange(HWND hwndP, BOOL bMainDlg, BOOL bRepaint /*=TRUE*/)
  1999. {
  2000. if (!bMainDlg && _InSetInfo)
  2001. return FALSE;
  2002. SetDirty();
  2003. if (bMainDlg)
  2004. BringWindowToTop(hwndP);
  2005. _CleanupRects(GetParent(hwndP));
  2006. UpdateActiveDisplay(_pCurDevice, bRepaint);
  2007. return TRUE;
  2008. }
  2009. BOOL CSettingsPage::RegisterPreviewWindowClass(WNDPROC pfnWndProc)
  2010. {
  2011. TraceMsg(TF_GENERAL, "InitMultiMonitorDlg\n");
  2012. WNDCLASS cls;
  2013. cls.hCursor = LoadCursor(NULL,IDC_ARROW);
  2014. cls.hIcon = NULL;
  2015. cls.lpszMenuName = NULL;
  2016. cls.lpszClassName = TEXT("Monitor32");
  2017. cls.hbrBackground = (HBRUSH)(COLOR_DESKTOP + 1);
  2018. cls.hInstance = HINST_THISDLL;
  2019. cls.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
  2020. cls.lpfnWndProc = pfnWndProc;
  2021. cls.cbWndExtra = SIZEOF(LPVOID);
  2022. cls.cbClsExtra = 0;
  2023. return RegisterClass(&cls);
  2024. }
  2025. LRESULT CALLBACK DeskWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uID, DWORD_PTR dwRefData);
  2026. // This function is called from desk.c; Hence extern "C".
  2027. // This function is needed to determine if we need to use the single monitor's dialog
  2028. // or multi-monitor's dialog template at the time of starting the control panel applet.
  2029. int ComputeNumberOfDisplayDevices(void)
  2030. {
  2031. int iNumberOfDevices = 0;
  2032. CSettingsPage * pMultiMon = new CSettingsPage;
  2033. if (pMultiMon)
  2034. {
  2035. int iDevice;
  2036. // Enumerate all display devices to count the number of valid devices.
  2037. iNumberOfDevices = pMultiMon->_EnumerateAllDisplayDevices();
  2038. // Now that we have the number of devices, let's cleanup the device settings we
  2039. // created in the process of enumerating above.
  2040. for (iDevice = 0; iDevice < iNumberOfDevices; iDevice++)
  2041. pMultiMon->_DestroyMultimonDevice(&pMultiMon->_Devices[iDevice]);
  2042. // Let's clean up the MultiMon we allocated earlier.
  2043. pMultiMon->Release();
  2044. }
  2045. return iNumberOfDevices;
  2046. }
  2047. int ComputeNumberOfMonitorsFast(BOOL fFastDetect)
  2048. {
  2049. int nVideoCards = 0;
  2050. int nIndex;
  2051. DISPLAY_DEVICE dispDevice = {0};
  2052. dispDevice.cb = sizeof(dispDevice);
  2053. for (nIndex = 0; EnumDisplayDevices(NULL, nIndex, &dispDevice, 0); nIndex++)
  2054. {
  2055. // Fast Detect means the caller only cares if there are more than 1.
  2056. if (fFastDetect && (nVideoCards > 1))
  2057. {
  2058. break;
  2059. }
  2060. dispDevice.cb = sizeof(dispDevice);
  2061. if (!(dispDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
  2062. {
  2063. nVideoCards++;
  2064. }
  2065. }
  2066. return nVideoCards;
  2067. }
  2068. BOOL CSettingsPage::_InitDisplaySettings(BOOL bExport)
  2069. {
  2070. HWND hwndC;
  2071. int iItem;
  2072. LONG iPrimeDevice = 0;
  2073. TCHAR ach[128];
  2074. PMULTIMON_DEVICE pDevice;
  2075. RECT rcPrimary;
  2076. HCURSOR hcur;
  2077. _InSetInfo = 1;
  2078. hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  2079. //
  2080. // Reset all the data so we can reinitialize the applet.
  2081. //
  2082. {
  2083. ComboBox_ResetContent(_hwndList);
  2084. SetRectEmpty(&_rcDesk);
  2085. hwndC = GetWindow(_hwndDesk, GW_CHILD);
  2086. while (hwndC)
  2087. {
  2088. RemoveTrackingToolTip(hwndC);
  2089. RemovePopupToolTip(hwndC);
  2090. DestroyWindow(hwndC);
  2091. hwndC = GetWindow(_hwndDesk, GW_CHILD);
  2092. }
  2093. ShowWindow(_hwndDesk, SW_HIDE);
  2094. if (_himl != NULL)
  2095. {
  2096. ImageList_Destroy(_himl);
  2097. _himl = NULL;
  2098. }
  2099. //
  2100. // Clear out all the devices.
  2101. //
  2102. for (ULONG iDevice = 0; iDevice < _NumDevices; iDevice++) {
  2103. pDevice = _Devices + iDevice;
  2104. _DestroyMultimonDevice(pDevice);
  2105. ZeroMemory(pDevice, sizeof(MULTIMON_DEVICE));
  2106. }
  2107. ZeroMemory(_Devices + _NumDevices,
  2108. sizeof(_Devices) - sizeof(MULTIMON_DEVICE) * _NumDevices);
  2109. _NumDevices = 0;
  2110. }
  2111. //
  2112. // Enumerate all the devices in the system.
  2113. //
  2114. // Note: This function computes the _NumDevices.
  2115. _EnumerateAllDisplayDevices();
  2116. if (_NumDevices == 0)
  2117. {
  2118. ASSERT(0);
  2119. return FALSE;
  2120. }
  2121. // Because we are getting the registry values, the current state of
  2122. // the registry may be inconsistent with that of the system:
  2123. //
  2124. // EmumDisplayDevices will return the active primary in the
  2125. // system, which may be different than the actual primary marked in the
  2126. // registry
  2127. BOOL bTmpDevicePrimary = FALSE;
  2128. ULONG iDevice;
  2129. _pPrimaryDevice = NULL;
  2130. for (iDevice = 0; iDevice < _NumDevices; iDevice++)
  2131. {
  2132. // First, we can pick any monitor that is attached as the primary.
  2133. if (_Devices[iDevice].pds->IsAttached())
  2134. {
  2135. if ((_pPrimaryDevice == NULL) &&
  2136. !_Devices[iDevice].pds->IsRemovable())
  2137. {
  2138. _pPrimaryDevice = &_Devices[iDevice];
  2139. TraceMsg(TF_GENERAL, "InitDisplaySettings: primary found %d\n", iDevice);
  2140. }
  2141. // If the DISPLAY_DEVICE structure tells us this is the primary,
  2142. // Pick this one.
  2143. if (_Devices[iDevice].DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
  2144. {
  2145. if (bTmpDevicePrimary)
  2146. {
  2147. ASSERT(FALSE);
  2148. }
  2149. else
  2150. {
  2151. _pPrimaryDevice = &_Devices[iDevice];
  2152. bTmpDevicePrimary = TRUE;
  2153. TraceMsg(TF_GENERAL, "InitDisplaySettings: Tmp DEVICE_PRIMARY found %d", iDevice);
  2154. }
  2155. // Check that the position should really be 0,0
  2156. RECT pos;
  2157. _Devices[iDevice].pds->GetCurPosition(&pos);
  2158. if ((pos.left == 0) &&
  2159. (pos.top == 0))
  2160. {
  2161. _pPrimaryDevice = &_Devices[iDevice];
  2162. TraceMsg(TF_GENERAL, "InitDisplaySettings: Best DEVICE_PRIMARY found %d", iDevice);
  2163. }
  2164. else
  2165. {
  2166. ASSERT(FALSE);
  2167. TraceMsg(TF_GENERAL, "InitDisplaySettings: PRIMARY is not at 0,0");
  2168. }
  2169. }
  2170. }
  2171. }
  2172. if (_pPrimaryDevice == NULL)
  2173. {
  2174. TraceMsg(TF_GENERAL, "InitDisplaySettings: NO Attached devices !!!");
  2175. // We must be running setup.
  2176. // Pick the first non-removable device as the primary.
  2177. for (iDevice = 0; iDevice < _NumDevices; iDevice++)
  2178. {
  2179. if (!_Devices[iDevice].pds->IsRemovable())
  2180. {
  2181. _pPrimaryDevice = &_Devices[iDevice];
  2182. break;
  2183. }
  2184. }
  2185. if (_pPrimaryDevice == NULL)
  2186. {
  2187. ASSERT(FALSE);
  2188. TraceMsg(TF_GENERAL, "InitDisplaySettings: All devices are removable !!!");
  2189. _pPrimaryDevice = &_Devices[0];
  2190. }
  2191. }
  2192. _pCurDevice = _pPrimaryDevice;
  2193. //
  2194. // Reset the primary's variables to make sure it is a properly formated
  2195. // primary entry.
  2196. //
  2197. SetMonAttached(_pPrimaryDevice, TRUE, TRUE, NULL);
  2198. SetPrimary(_pPrimaryDevice);
  2199. _pPrimaryDevice->pds->GetCurPosition(&rcPrimary);
  2200. //
  2201. // compute the max image size needed for a monitor bitmap
  2202. //
  2203. // NOTE this must be the max size the images will *ever*
  2204. // be we cant just take the current max size.
  2205. // we use the client window size, a child monitor cant be larger than this.
  2206. //
  2207. RECT rcDesk;
  2208. GetClientRect(_hwndDesk, &rcDesk);
  2209. int cxImage = rcDesk.right;
  2210. int cyImage = rcDesk.bottom;
  2211. //
  2212. // Create a temporary monitor bitmap
  2213. //
  2214. HBITMAP hbm = NULL;
  2215. MakeMonitorBitmap(cxImage, cyImage, NULL, &hbm, NULL, cxImage, cyImage, FALSE);
  2216. //
  2217. // Go through all the devices one last time to create the windows
  2218. //
  2219. for (iDevice = 0; iDevice < _NumDevices; iDevice++)
  2220. {
  2221. TCHAR szDisplay[256];
  2222. pDevice = &_Devices[iDevice];
  2223. MonitorData md = {0};
  2224. RECT rcPos;
  2225. LPVOID pWindowData = (LPVOID)this;
  2226. pDevice->DisplayIndex = iDevice + 1;
  2227. _GetDisplayName(pDevice, szDisplay, ARRAYSIZE(szDisplay));
  2228. iItem = ComboBox_AddString(_hwndList, szDisplay);
  2229. pDevice->ComboBoxItem = iItem;
  2230. ComboBox_SetItemData(_hwndList,
  2231. iItem,
  2232. (DWORD_PTR)pDevice);
  2233. //
  2234. // If the monitor is part of the desktop, show it on the screen
  2235. // otherwise keep it invisible.
  2236. //
  2237. wnsprintf(ach, ARRAYSIZE(ach), TEXT("%d"), iDevice + 1);
  2238. // Set the selection
  2239. //
  2240. if (pDevice == _pPrimaryDevice)
  2241. {
  2242. iPrimeDevice = iDevice;
  2243. }
  2244. if (!pDevice->pds->IsAttached())
  2245. {
  2246. // By default set the unattached monitors to the right of the primary monitor
  2247. POINT ptPos = {rcPrimary.right, rcPrimary.top};
  2248. pDevice->pds->SetCurPosition(&ptPos);
  2249. }
  2250. pDevice->pds->GetCurPosition(&rcPos);
  2251. if (bExport)
  2252. {
  2253. md.dwSize = SIZEOF(MonitorData);
  2254. if ( pDevice->pds->IsPrimary() )
  2255. md.dwStatus |= MD_PRIMARY;
  2256. if ( pDevice->pds->IsAttached() )
  2257. md.dwStatus |= MD_ATTACHED;
  2258. md.rcPos = rcPos;
  2259. pWindowData = &md;
  2260. }
  2261. if (_himl == NULL)
  2262. {
  2263. UINT flags = ILC_COLORDDB | ILC_MASK;
  2264. _himl = ImageList_Create(cxImage, cyImage, flags, _NumDevices, 1);
  2265. ASSERT(_himl);
  2266. ImageList_SetBkColor(_himl, GetSysColor(COLOR_APPWORKSPACE));
  2267. }
  2268. pDevice->w = -1;
  2269. pDevice->h = -1;
  2270. pDevice->himl = _himl;
  2271. pDevice->iImage = ImageList_AddMasked(_himl, hbm, CLR_DEFAULT);
  2272. TraceMsg(TF_GENERAL, "InitDisplaySettings: Creating preview windows %s at %d %d %d %d",
  2273. ach, rcPos.left, rcPos.top, rcPos.right, rcPos.bottom);
  2274. // HACK! Use pDevice as its own id. Doesn't work on Win64.
  2275. hwndC = CreateWindowEx(
  2276. 0, // WS_EX_CLIENTEDGE,
  2277. TEXT("Monitor32"), ach,
  2278. WS_CLIPSIBLINGS | /* WS_DLGFRAME | */ WS_VISIBLE | WS_CHILD,
  2279. rcPos.left, rcPos.top, RECTWIDTH(rcPos), RECTHEIGHT(rcPos),
  2280. _hwndDesk,
  2281. (HMENU)pDevice,
  2282. HINST_THISDLL,
  2283. pWindowData);
  2284. ASSERT(hwndC);
  2285. AddTrackingToolTip(pDevice, hwndC);
  2286. AddPopupToolTip(hwndC);
  2287. }
  2288. ToolTip_Activate(ghwndToolTipPopup, TRUE);
  2289. ToolTip_SetDelayTime(ghwndToolTipPopup, TTDT_INITIAL, 1000);
  2290. ToolTip_SetDelayTime(ghwndToolTipPopup, TTDT_RESHOW, 1000);
  2291. // nuke the temp monitor bitmap.
  2292. if (hbm)
  2293. DeleteObject(hbm);
  2294. //
  2295. // Set the primary device as the current device
  2296. //
  2297. ComboBox_SetCurSel(_hwndList, iPrimeDevice);
  2298. // Initialize all the constants and the settings fields
  2299. _DeskScale = 1000;
  2300. _DeskOff.x = 0;
  2301. _DeskOff.y = 0;
  2302. _CleanupRects(_hwndDesk);
  2303. // Now: depends on whether we have a multimon system, change the UI
  2304. if (_NumDevices == 1)
  2305. {
  2306. HWND hwndDisable;
  2307. hwndDisable = GetDlgItem(_hDlg, IDC_MULTIMONHELP);
  2308. ShowWindow(hwndDisable, SW_HIDE);
  2309. ShowWindow(_hwndDesk, SW_HIDE);
  2310. // set up bitmaps for sample screen
  2311. _hbmScrSample = LoadMonitorBitmap( TRUE ); // let them do the desktop
  2312. SendDlgItemMessage(_hDlg, IDC_SCREENSAMPLE, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)_hbmScrSample);
  2313. // get a base copy of the bitmap for when the "internals" change
  2314. _hbmMonitor = LoadMonitorBitmap( FALSE ); // we'll do the desktop
  2315. //Hide the combo box, keep the static text
  2316. ShowWindow(_hwndList, SW_HIDE);
  2317. //Set the name of the primary in the static text
  2318. //strip the first token off (this is the number we dont want it)
  2319. TCHAR *pch, szDisplay[MAX_PATH];
  2320. _GetDisplayName(_pPrimaryDevice, szDisplay, ARRAYSIZE(szDisplay));
  2321. for (pch=szDisplay; *pch && *pch != TEXT(' '); pch++);
  2322. for (;*pch && *pch == TEXT(' '); pch++);
  2323. SetDlgItemText(_hDlg, IDC_DISPLAYTEXT, pch);
  2324. // Hide the check boxes
  2325. // Single monitors use a different dialog template now!
  2326. hwndDisable = GetDlgItem(_hDlg, IDC_DISPLAYPRIME);
  2327. if(hwndDisable)
  2328. ShowWindow(hwndDisable, SW_HIDE);
  2329. hwndDisable = GetDlgItem(_hDlg, IDC_DISPLAYUSEME);
  2330. if(hwndDisable)
  2331. ShowWindow(hwndDisable, SW_HIDE);
  2332. }
  2333. else if (_NumDevices > 0)
  2334. {
  2335. //Hide the static text, keep the combo box
  2336. ShowWindow(GetDlgItem(_hDlg, IDC_DISPLAYTEXT), SW_HIDE);
  2337. // Hide the Multimon version of the preview objects
  2338. ShowWindow(GetDlgItem(_hDlg, IDC_SCREENSAMPLE), SW_HIDE);
  2339. // In case of multiple devices, subclass the _hwndDesk window for key board support
  2340. SetWindowSubclass(_hwndDesk, DeskWndProc, 0, (DWORD_PTR)this);
  2341. ShowWindow(_hwndDesk, SW_SHOW);
  2342. }
  2343. //
  2344. // Paint the UI.
  2345. //
  2346. UpdateActiveDisplay(_pCurDevice);
  2347. //
  2348. // Reset the cursor and leave
  2349. //
  2350. SetCursor(hcur);
  2351. _InSetInfo--;
  2352. return TRUE;
  2353. }
  2354. //
  2355. // This function enumerates all the devices and returns the number of
  2356. // devices found in the system.
  2357. //
  2358. int CSettingsPage::_EnumerateAllDisplayDevices()
  2359. {
  2360. PMULTIMON_DEVICE pDevice;
  2361. int iEnum;
  2362. BOOL fSuccess;
  2363. ULONG dwVgaPrimary = 0xFFFFFFFF;
  2364. //
  2365. // Enumerate all the devices in the system.
  2366. //
  2367. for (iEnum = 0; _NumDevices < MONITORS_MAX; iEnum++)
  2368. {
  2369. pDevice = &_Devices[_NumDevices];
  2370. ZeroMemory(&(pDevice->DisplayDevice), sizeof(DISPLAY_DEVICE));
  2371. pDevice->DisplayDevice.cb = sizeof(DISPLAY_DEVICE);
  2372. fSuccess = EnumDisplayDevices(NULL, iEnum, &pDevice->DisplayDevice, 0);
  2373. TraceMsg(TF_GENERAL, "Device %d ", iEnum);
  2374. TraceMsg(TF_GENERAL, "cb %d ", pDevice->DisplayDevice.cb);
  2375. TraceMsg(TF_GENERAL, "DeviceName %ws ", pDevice->DisplayDevice.DeviceName);
  2376. TraceMsg(TF_GENERAL, "DeviceString %ws", pDevice->DisplayDevice.DeviceString);
  2377. TraceMsg(TF_GENERAL, "StateFlags %08lx", pDevice->DisplayDevice.StateFlags);
  2378. // ignore device's we cant create a DC for.
  2379. if (!fSuccess)
  2380. {
  2381. TraceMsg(TF_GENERAL, "End of list\n");
  2382. break;
  2383. }
  2384. // We won't even include the MIRRORING drivers in the list for
  2385. // now.
  2386. if (pDevice->DisplayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER)
  2387. {
  2388. TraceMsg(TF_GENERAL, "Mirroring driver - skip it\n");
  2389. continue;
  2390. }
  2391. // dump the device software key
  2392. TraceMsg(TF_GENERAL, "DeviceKey %s", pDevice->DisplayDevice.DeviceKey);
  2393. // Create the settings for this device
  2394. pDevice->pds = new CDisplaySettings();
  2395. if (pDevice->pds)
  2396. {
  2397. if (pDevice->pds->InitSettings(&pDevice->DisplayDevice))
  2398. {
  2399. // Determine if the VGA is the primary.
  2400. // This will only happen for SETUP or BASEVIDEO
  2401. //
  2402. // We want to delete this device later on if we have others.
  2403. if (pDevice->DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
  2404. {
  2405. CRegistrySettings crv(&pDevice->DisplayDevice.DeviceKey[0]);
  2406. LPTSTR pszMini = crv.GetMiniPort();
  2407. // If VGA is active, then go to pass 2.
  2408. // Otherwise, let's try to use this device
  2409. //
  2410. if (pszMini && (!lstrcmpi(TEXT("vga"), pszMini)))
  2411. {
  2412. TraceMsg(TF_GENERAL, "EnumDevices - VGA primary\n");
  2413. dwVgaPrimary = _NumDevices;
  2414. }
  2415. }
  2416. // Add it to the list.
  2417. _NumDevices++;
  2418. }
  2419. else
  2420. {
  2421. pDevice->pds->Release();
  2422. pDevice->pds = NULL;
  2423. }
  2424. }
  2425. }
  2426. //
  2427. // If the primary VGA is not needed, remove it.
  2428. //
  2429. if ((dwVgaPrimary != 0xFFFFFFFF) &&
  2430. (_NumDevices >= 2))
  2431. {
  2432. TraceMsg(TF_GENERAL, "REMOVE primary VGA device\n");
  2433. _Devices[dwVgaPrimary].pds->Release();
  2434. _Devices[dwVgaPrimary].pds = NULL;
  2435. _NumDevices--;
  2436. _Devices[dwVgaPrimary] = _Devices[_NumDevices];
  2437. }
  2438. return(_NumDevices); //Return the number of devices.
  2439. }
  2440. //-----------------------------------------------------------------------------
  2441. BOOL CSettingsPage::InitMultiMonitorDlg(HWND hDlg)
  2442. {
  2443. HWND hwndSlider;
  2444. BOOL fSucceeded;
  2445. _hDlg = hDlg;
  2446. _hwndDesk = GetDlgItem(_hDlg, IDC_DISPLAYDESK);
  2447. _hwndList = GetDlgItem(_hDlg, IDC_DISPLAYLIST);
  2448. hwndSlider = GetDlgItem(hDlg, IDC_SCREENSIZE);
  2449. ASSERT(hwndSlider != NULL);
  2450. fSucceeded = SetWindowSubclass(hwndSlider, SliderSubWndProc, 0, NULL);
  2451. ASSERT(fSucceeded);
  2452. // Determine in what mode we are running the applet before getting information
  2453. _vPreExecMode();
  2454. // Create a tooltip window
  2455. ghwndToolTipTracking = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, TEXT(""),
  2456. WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT,
  2457. CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL,
  2458. HINST_THISDLL, NULL);
  2459. ghwndToolTipPopup = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, TEXT(""),
  2460. WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT,
  2461. CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL,
  2462. HINST_THISDLL, NULL);
  2463. RegisterPreviewWindowClass(&MonitorWindowProc);
  2464. _InitDisplaySettings(FALSE);
  2465. if (_NumDevices > 1)
  2466. _ConfirmPositions();
  2467. if (ClassicGetSystemMetrics(SM_REMOTESESSION)) {
  2468. EnableWindow(GetDlgItem(_hDlg, IDC_DISPLAYPROPERTIES), FALSE);
  2469. }
  2470. // Determine if any errors showed up during enumerations and initialization
  2471. _vPostExecMode();
  2472. // Now tell the user what we found out during initialization
  2473. // Errors, or what we found during detection
  2474. PostMessage(hDlg, MSG_DSP_SETUP_MESSAGE, 0, 0);
  2475. // Since this could have taken a very long time, just make us visible
  2476. // if another app (like progman) came up.
  2477. ShowWindow(hDlg, SW_SHOW);
  2478. return TRUE;
  2479. }
  2480. LRESULT CALLBACK DeskWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uID, DWORD_PTR dwRefData)
  2481. {
  2482. CSettingsPage * pcmm = (CSettingsPage *)dwRefData;
  2483. HWND hwndC;
  2484. RECT rcPos;
  2485. BOOL bMoved = TRUE;
  2486. int iMonitor, nMoveUnit;
  2487. switch(message)
  2488. {
  2489. case WM_GETDLGCODE:
  2490. return DLGC_WANTCHARS | DLGC_WANTARROWS;
  2491. case WM_KILLFOCUS:
  2492. RedrawWindow(hDlg, NULL, NULL, RDW_INVALIDATE);
  2493. break;
  2494. case WM_MOUSEMOVE: {
  2495. MSG mmsg;
  2496. ToolTip_RelayEvent(ghwndToolTipPopup, mmsg, hDlg, message, wParam, lParam);
  2497. }
  2498. break;
  2499. case WM_PAINT:
  2500. if (GetFocus() != hDlg)
  2501. break;
  2502. return(DefSubclassProc(hDlg, message, wParam, lParam));
  2503. break;
  2504. case WM_LBUTTONDOWN:
  2505. SetFocus(hDlg);
  2506. break;
  2507. case WM_KEYDOWN:
  2508. nMoveUnit = ((GetKeyState(VK_CONTROL) & 0x8000) ? 1 : 3);
  2509. hwndC = pcmm->GetCurDeviceHwnd();
  2510. GetWindowRect(hwndC, &rcPos);
  2511. MapWindowRect(NULL, hDlg, &rcPos);
  2512. switch(wParam)
  2513. {
  2514. case VK_LEFT:
  2515. MoveWindow(hwndC, rcPos.left - nMoveUnit, rcPos.top, RECTWIDTH(rcPos), RECTHEIGHT(rcPos), TRUE);
  2516. break;
  2517. case VK_RIGHT:
  2518. MoveWindow(hwndC, rcPos.left + nMoveUnit, rcPos.top, RECTWIDTH(rcPos), RECTHEIGHT(rcPos), TRUE);
  2519. break;
  2520. case VK_UP:
  2521. MoveWindow(hwndC, rcPos.left, rcPos.top - nMoveUnit, RECTWIDTH(rcPos), RECTHEIGHT(rcPos), TRUE);
  2522. break;
  2523. case VK_DOWN:
  2524. MoveWindow(hwndC, rcPos.left, rcPos.top + nMoveUnit, RECTWIDTH(rcPos), RECTHEIGHT(rcPos), TRUE);
  2525. break;
  2526. default:
  2527. bMoved = FALSE;
  2528. break;
  2529. }
  2530. if (bMoved)
  2531. {
  2532. pcmm->HandleMonitorChange(hwndC, FALSE, FALSE);
  2533. if (IsWindowVisible(ghwndToolTipPopup)) {
  2534. ToolTip_Update(ghwndToolTipPopup);
  2535. }
  2536. }
  2537. break;
  2538. case WM_CHAR:
  2539. if (wParam >= TEXT('0') && wParam <= TEXT('9') && pcmm) {
  2540. iMonitor = (TCHAR)wParam - TEXT('0');
  2541. if ((iMonitor == 0) && (pcmm->GetNumDevices() >= 10))
  2542. {
  2543. iMonitor = 10;
  2544. }
  2545. if ((iMonitor > 0) && ((ULONG)iMonitor <= pcmm->GetNumDevices()))
  2546. {
  2547. HWND hwndList = GetDlgItem(GetParent(hDlg), IDC_DISPLAYLIST);
  2548. ComboBox_SetCurSel(hwndList, iMonitor - 1);
  2549. pcmm->UpdateActiveDisplay(NULL);
  2550. return 0;
  2551. }
  2552. }
  2553. break;
  2554. case WM_DESTROY:
  2555. RemoveWindowSubclass(hDlg, DeskWndProc, 0);
  2556. break;
  2557. default:
  2558. break;
  2559. }
  2560. return DefSubclassProc(hDlg, message, wParam, lParam);
  2561. }
  2562. //-----------------------------------------------------------------------------
  2563. //
  2564. // Callback functions PropertySheet can use
  2565. //
  2566. INT_PTR CALLBACK CSettingsPage::SettingsDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
  2567. {
  2568. CSettingsPage * pcmm = (CSettingsPage *) GetWindowLongPtr(hDlg, DWLP_USER);
  2569. switch (message)
  2570. {
  2571. case WM_INITDIALOG:
  2572. {
  2573. PROPSHEETPAGE * pPropSheetPage = (PROPSHEETPAGE *) lParam;
  2574. if (pPropSheetPage)
  2575. {
  2576. SetWindowLongPtr(hDlg, DWLP_USER, pPropSheetPage->lParam);
  2577. pcmm = (CSettingsPage *)pPropSheetPage->lParam;
  2578. }
  2579. if (pcmm)
  2580. {
  2581. SetWindowLongPtr(hDlg, DWLP_USER, (LPARAM)pcmm);
  2582. ghwndPropSheet = GetParent(hDlg);
  2583. SetWindowLong(ghwndPropSheet,
  2584. GWL_STYLE,
  2585. GetWindowLong(ghwndPropSheet, GWL_STYLE) | WS_CLIPCHILDREN);
  2586. if (pcmm->InitMultiMonitorDlg(hDlg))
  2587. {
  2588. //
  2589. // if we have a invalid mode force the user to Apply
  2590. //
  2591. DWORD dwExecMode;
  2592. if (pcmm->_pThemeUI && (SUCCEEDED(pcmm->_pThemeUI->GetExecMode(&dwExecMode))))
  2593. {
  2594. if (dwExecMode == EM_INVALID_MODE)
  2595. pcmm->SetDirty();
  2596. }
  2597. return TRUE;
  2598. }
  2599. else
  2600. return FALSE;
  2601. }
  2602. }
  2603. break;
  2604. case WM_DESTROY:
  2605. if (pcmm)
  2606. {
  2607. pcmm->WndProc(message, wParam, lParam);
  2608. SetWindowLongPtr(hDlg, DWLP_USER, NULL);
  2609. }
  2610. if(gfFlashWindowRegistered)
  2611. {
  2612. gfFlashWindowRegistered = FALSE;
  2613. UnregisterClass(TEXT("MonitorNumber32"), HINST_THISDLL);
  2614. }
  2615. break;
  2616. default:
  2617. if (pcmm)
  2618. return pcmm->WndProc(message, wParam, lParam);
  2619. break;
  2620. }
  2621. return FALSE;
  2622. }
  2623. void CSettingsPage::_SetPreviewScreenSize(int HRes, int VRes, int iOrgXRes, int iOrgYRes)
  2624. {
  2625. HBITMAP hbmOld, hbmOld2;
  2626. HBRUSH hbrOld;
  2627. HDC hdcMem, hdcMem2;
  2628. // stretching the taskbar could get messy, we'll only do the desktop
  2629. int mon_dy = MON_DY - MON_TRAY;
  2630. // init to identical extents
  2631. SIZE dSrc = { MON_DX, mon_dy };
  2632. SIZE dDst = { MON_DX, mon_dy };
  2633. // set up a work area to play in
  2634. if (!_hbmMonitor || !_hbmScrSample)
  2635. return;
  2636. HDC hdc = GetDC(NULL);
  2637. hdcMem = CreateCompatibleDC(hdc);
  2638. hdcMem2 = CreateCompatibleDC(hdc);
  2639. ReleaseDC(NULL, hdc);
  2640. if (!hdcMem2 || !hdcMem)
  2641. return;
  2642. hbmOld2 = (HBITMAP)SelectObject(hdcMem2, _hbmScrSample);
  2643. hbmOld = (HBITMAP)SelectObject(hdcMem, _hbmMonitor);
  2644. // see if we need to shrink either aspect of the image
  2645. if (HRes > iOrgXRes || VRes > iOrgYRes)
  2646. {
  2647. // make sure the uncovered area will be seamless with the desktop
  2648. RECT rc = { MON_X, MON_Y, MON_X + MON_DX, MON_Y + mon_dy };
  2649. HBRUSH hbr = CreateSolidBrush(GetPixel( hdcMem, MON_X + 1, MON_Y + 1 ));
  2650. if (hbr)
  2651. {
  2652. FillRect(hdcMem2, &rc, hbr);
  2653. DeleteObject(hbr);
  2654. }
  2655. }
  2656. // stretch the image to reflect the new resolution
  2657. if( HRes > iOrgXRes )
  2658. dDst.cx = MulDiv( MON_DX, iOrgXRes, HRes );
  2659. else if( HRes < iOrgXRes )
  2660. dSrc.cx = MulDiv( MON_DX, HRes, iOrgXRes );
  2661. if( VRes > iOrgYRes )
  2662. dDst.cy = MulDiv( mon_dy, iOrgYRes, VRes );
  2663. else if( VRes < iOrgYRes )
  2664. dSrc.cy = MulDiv( mon_dy, VRes, iOrgYRes );
  2665. SetStretchBltMode( hdcMem2, COLORONCOLOR );
  2666. StretchBlt( hdcMem2, MON_X, MON_Y, dDst.cx, dDst.cy,
  2667. hdcMem, MON_X, MON_Y, dSrc.cx, dSrc.cy, SRCCOPY);
  2668. // now fill the new image's desktop with the possibly-dithered brush
  2669. // the top right corner seems least likely to be hit by the stretch...
  2670. hbrOld = (HBRUSH)SelectObject( hdcMem2, GetSysColorBrush( COLOR_DESKTOP ) );
  2671. ExtFloodFill(hdcMem2, MON_X + MON_DX - 2, MON_Y+1,
  2672. GetPixel(hdcMem2, MON_X + MON_DX - 2, MON_Y+1), FLOODFILLSURFACE);
  2673. // clean up after ourselves
  2674. SelectObject( hdcMem2, hbrOld );
  2675. SelectObject( hdcMem2, hbmOld2 );
  2676. DeleteObject( hdcMem2 );
  2677. SelectObject( hdcMem, hbmOld );
  2678. DeleteObject( hdcMem );
  2679. }
  2680. void CSettingsPage::_RedrawDeskPreviews()
  2681. {
  2682. if (_NumDevices > 1)
  2683. {
  2684. _CleanupRects(_hwndDesk);
  2685. RedrawWindow(_hwndDesk, NULL, NULL, RDW_ALLCHILDREN | RDW_ERASE | RDW_INVALIDATE);
  2686. }
  2687. else if (_pCurDevice && _pCurDevice->pds)
  2688. {
  2689. RECT rcPos, rcOrgPos;
  2690. _pCurDevice->pds->GetCurPosition(&rcPos);
  2691. _pCurDevice->pds->GetOrgPosition(&rcOrgPos);
  2692. _SetPreviewScreenSize(RECTWIDTH(rcPos), RECTHEIGHT(rcPos), RECTWIDTH(rcOrgPos), RECTHEIGHT(rcOrgPos));
  2693. // only invalidate the "screen" part of the monitor bitmap
  2694. rcPos.left = MON_X;
  2695. rcPos.top = MON_Y;
  2696. rcPos.right = MON_X + MON_DX + 2; // fudge (trust me)
  2697. rcPos.bottom = MON_Y + MON_DY + 1; // fudge (trust me)
  2698. InvalidateRect(GetDlgItem(_hDlg, IDC_SCREENSAMPLE), &rcPos, FALSE);
  2699. }
  2700. }
  2701. int DisplaySaveSettings(PVOID pContext, HWND hwnd)
  2702. {
  2703. CDisplaySettings *rgpds[1];
  2704. rgpds[0] = (CDisplaySettings*) pContext;
  2705. if(rgpds[0]->bIsModeChanged())
  2706. return CSettingsPage::_DisplaySaveSettings(rgpds, 1, hwnd);
  2707. else
  2708. return DISP_CHANGE_SUCCESSFUL;
  2709. }
  2710. int CSettingsPage::_DisplaySaveSettings(CDisplaySettings *rgpds[], ULONG numDevices, HWND hDlg)
  2711. {
  2712. BOOL bReboot = FALSE;
  2713. BOOL bTest = FALSE;
  2714. int iSave;
  2715. ULONG iDevice;
  2716. POINT ptCursorSave;
  2717. // Test the new settings first
  2718. iSave = _SaveSettings(rgpds, numDevices, hDlg, CDS_TEST);
  2719. if (iSave < DISP_CHANGE_SUCCESSFUL)
  2720. {
  2721. FmtMessageBox(hDlg,
  2722. MB_ICONEXCLAMATION,
  2723. IDS_CHANGE_SETTINGS,
  2724. IDS_SETTINGS_INVALID);
  2725. return iSave;
  2726. }
  2727. int iDynaResult;
  2728. // Ask first and then change the settings.
  2729. if (!bReboot &&
  2730. (_AnyChange(rgpds, numDevices) ||
  2731. _IsSingleToMultimonChange(rgpds, numDevices)))
  2732. {
  2733. iDynaResult = AskDynaCDS(hDlg);
  2734. if (iDynaResult == -1)
  2735. {
  2736. return DISP_CHANGE_NOTUPDATED;
  2737. }
  2738. else if (iDynaResult == 0)
  2739. {
  2740. bReboot = TRUE;
  2741. }
  2742. }
  2743. if (!bReboot && _AnyChange(rgpds, numDevices) &&
  2744. !_CanSkipWarningBecauseKnownSafe(rgpds, numDevices))
  2745. {
  2746. bTest = TRUE;
  2747. }
  2748. // Save the settings to the registry.
  2749. iSave = _SaveSettings(rgpds, numDevices, hDlg, CDS_UPDATEREGISTRY | CDS_NORESET);
  2750. if (iSave < DISP_CHANGE_SUCCESSFUL)
  2751. {
  2752. // NOTE
  2753. // If we get NOT_UPDATED, this mean security may be turned on.
  2754. // We could still try to do the dynamic change.
  2755. // This only works in single mon ...
  2756. if (iSave == DISP_CHANGE_NOTUPDATED)
  2757. {
  2758. FmtMessageBox(hDlg,
  2759. MB_ICONEXCLAMATION,
  2760. IDS_CHANGE_SETTINGS,
  2761. IDS_SETTINGS_CANNOT_SAVE);
  2762. }
  2763. else
  2764. {
  2765. FmtMessageBox(hDlg,
  2766. MB_ICONEXCLAMATION,
  2767. IDS_CHANGE_SETTINGS,
  2768. IDS_SETTINGS_FAILED_SAVE);
  2769. }
  2770. // Restore the settings to their original state
  2771. for (iDevice = 0; iDevice < numDevices; iDevice++)
  2772. {
  2773. rgpds[iDevice]->RestoreSettings();
  2774. }
  2775. _SaveSettings(rgpds, numDevices, hDlg, CDS_UPDATEREGISTRY | CDS_NORESET);
  2776. return iSave;
  2777. }
  2778. if (bReboot)
  2779. {
  2780. iSave = DISP_CHANGE_RESTART;
  2781. }
  2782. // Try to change the mode dynamically if it was requested
  2783. GetCursorPos(&ptCursorSave);
  2784. if (iSave == DISP_CHANGE_SUCCESSFUL)
  2785. {
  2786. // If EnumDisplaySettings was called with EDS_RAWMODE, we need CDS_RAWMODE below.
  2787. // Otherwise, it's harmless.
  2788. iSave = ChangeDisplaySettings(NULL, CDS_RAWMODE);
  2789. //We post a message to ourselves to destroy it later.
  2790. // Check the return from the dynamic mode switch.
  2791. if (iSave < 0)
  2792. {
  2793. DWORD dwMessage =
  2794. ((iSave == DISP_CHANGE_BADDUALVIEW) ? IDS_CHANGESETTINGS_BADDUALVIEW
  2795. : IDS_DYNAMIC_CHANGESETTINGS_FAILED);
  2796. FmtMessageBox(hDlg,
  2797. MB_ICONEXCLAMATION,
  2798. IDS_CHANGE_SETTINGS,
  2799. dwMessage);
  2800. }
  2801. else if (iSave == DISP_CHANGE_SUCCESSFUL)
  2802. {
  2803. // Set the cursor to where it was before we changed the display
  2804. // (ie, if we changed a 2ndary monitor, the cursor would have been
  2805. // placed on the primary after the application of the change, move
  2806. // it back to the 2ndary monitor. If the change failed, we are
  2807. // just placing the cursor back to it orig pos
  2808. SetCursorPos(ptCursorSave.x, ptCursorSave.y);
  2809. // Determine what to do based on the return code.
  2810. if (bTest && (IDYES != DialogBoxParam(HINST_THISDLL,
  2811. MAKEINTRESOURCE(DLG_KEEPNEW),
  2812. GetParent(hDlg),
  2813. KeepNewDlgProc, 15)))
  2814. {
  2815. iSave = DISP_CHANGE_NOTUPDATED;
  2816. }
  2817. }
  2818. }
  2819. // Determine what to do based on the return code.
  2820. if (iSave >= DISP_CHANGE_SUCCESSFUL)
  2821. {
  2822. // Confirm the settings
  2823. for (iDevice = 0; iDevice < numDevices; iDevice++)
  2824. {
  2825. rgpds[iDevice]->ConfirmChangeSettings();
  2826. }
  2827. }
  2828. else
  2829. {
  2830. // Restore the settings to their original state
  2831. for (iDevice = 0; iDevice < numDevices; iDevice++)
  2832. {
  2833. rgpds[iDevice]->RestoreSettings();
  2834. }
  2835. //DLI: This last function call will actually go refresh the whole desktop
  2836. // If EnumDisplaySettings was called with EDS_RAWMODE, we need CDS_RAWMODE below.
  2837. // Otherwise, it's harmless.
  2838. ChangeDisplaySettings(NULL, CDS_RAWMODE);
  2839. }
  2840. return iSave;
  2841. }
  2842. //-----------------------------------------------------------------------------
  2843. //
  2844. // Resolution slider
  2845. //
  2846. CSettingsPage::_HandleHScroll(HWND hwndTB, int iCode, int iPos)
  2847. {
  2848. if (_pCurDevice && _pCurDevice->pds)
  2849. {
  2850. int iRes = _iResolution;
  2851. int cRes = (int)SendMessage(hwndTB, TBM_GETRANGEMAX, TRUE, 0);
  2852. TraceMsg(TF_FUNC, "_HandleHScroll: MaxRange = %d, iRes = %d, iPos = %d", cRes, iRes, iPos);
  2853. // Message box if something bad is going to happen ?
  2854. // _VerifyPrimaryMode(TRUE);
  2855. switch (iCode)
  2856. {
  2857. case TB_LINEUP:
  2858. case TB_PAGEUP:
  2859. if (iRes != 0)
  2860. iRes--;
  2861. break;
  2862. case TB_LINEDOWN:
  2863. case TB_PAGEDOWN:
  2864. if (++iRes >= cRes)
  2865. iRes = cRes;
  2866. break;
  2867. case TB_BOTTOM:
  2868. iRes = cRes;
  2869. break;
  2870. case TB_TOP:
  2871. iRes = 0;
  2872. break;
  2873. case TB_THUMBTRACK:
  2874. case TB_THUMBPOSITION:
  2875. iRes = iPos;
  2876. break;
  2877. default:
  2878. return FALSE;
  2879. }
  2880. TraceMsg(TF_FUNC, "_HandleHScroll: iRes = %d, iCode = %d", iRes, iCode);
  2881. // We only want to automatically set the color depth for the user if they
  2882. // changed the resolution. (not just set focus to the control)
  2883. BOOL fAutoSetColorDepth = (_iResolution != iRes); // iPos
  2884. _pCurDevice->pds->SetCurResolution(_pCurDevice->ResolutionList + iRes, fAutoSetColorDepth);
  2885. // Repaint the control in case they changed
  2886. _UpdateUI(TRUE /*fAutoSetColorDepth*/, IDC_SCREENSIZE);
  2887. DWORD dwExecMode;
  2888. if (_pThemeUI && (SUCCEEDED(_pThemeUI->GetExecMode(&dwExecMode))))
  2889. {
  2890. if ( (dwExecMode == EM_NORMAL) ||
  2891. (dwExecMode == EM_INVALID_MODE) ||
  2892. (dwExecMode == EM_DETECT) ) {
  2893. //
  2894. // Set the apply button if resolution has changed
  2895. //
  2896. if (_pCurDevice->pds->bIsModeChanged())
  2897. SetDirty();
  2898. return 0;
  2899. }
  2900. }
  2901. }
  2902. return TRUE;
  2903. }
  2904. void CSettingsPage::_ForwardToChildren(UINT message, WPARAM wParam, LPARAM lParam)
  2905. {
  2906. HWND hwndC = GetDlgItem(_hDlg, IDC_SCREENSIZE);
  2907. if (hwndC)
  2908. SendMessage(hwndC, message, wParam, lParam);
  2909. }
  2910. LRESULT CALLBACK CSettingsPage::WndProc(UINT message, WPARAM wParam, LPARAM lParam)
  2911. {
  2912. NMHDR FAR * lpnm;
  2913. HWND hwndC;
  2914. HWND hwndSample;
  2915. HBITMAP hbm;
  2916. switch (message)
  2917. {
  2918. case WM_NOTIFY:
  2919. lpnm = (NMHDR FAR *)lParam;
  2920. switch (lpnm->code)
  2921. {
  2922. case PSN_APPLY:
  2923. return TRUE;
  2924. default:
  2925. return FALSE;
  2926. }
  2927. break;
  2928. case WM_CTLCOLORSTATIC:
  2929. if (GetDlgCtrlID((HWND)lParam) == IDC_DISPLAYDESK)
  2930. {
  2931. return (UINT_PTR)GetSysColorBrush(COLOR_APPWORKSPACE);
  2932. }
  2933. return FALSE;
  2934. case WM_COMMAND:
  2935. switch (GET_WM_COMMAND_ID(wParam, lParam))
  2936. {
  2937. case IDC_DISPLAYPRIME:
  2938. if (!SetPrimary(_pCurDevice))
  2939. {
  2940. return FALSE;
  2941. }
  2942. hwndC = GetCurDeviceHwnd();
  2943. HandleMonitorChange(hwndC, TRUE);
  2944. break;
  2945. case IDC_DISPLAYUSEME:
  2946. // Don't pop up warning dialog box if this display is already attached
  2947. // or if there are already more than 1 display
  2948. if (!_pCurDevice ||
  2949. !SetMonAttached(_pCurDevice,
  2950. !_pCurDevice->pds->IsAttached(),
  2951. TRUE,
  2952. _hDlg))
  2953. {
  2954. return FALSE;
  2955. }
  2956. hwndC = GetCurDeviceHwnd();
  2957. HandleMonitorChange(hwndC, TRUE);
  2958. break;
  2959. case IDC_DISPLAYLIST:
  2960. switch (GET_WM_COMMAND_CMD(wParam, lParam))
  2961. {
  2962. case CBN_DBLCLK:
  2963. goto DoDeviceSettings;
  2964. case CBN_SELCHANGE:
  2965. UpdateActiveDisplay(NULL);
  2966. break;
  2967. default:
  2968. return FALSE;
  2969. }
  2970. break;
  2971. case IDC_DISPLAYPROPERTIES:
  2972. switch (GET_WM_COMMAND_CMD(wParam, lParam))
  2973. {
  2974. DoDeviceSettings:
  2975. case BN_CLICKED:
  2976. if (IsWindowEnabled(GetDlgItem(_hDlg, IDC_DISPLAYPROPERTIES)))
  2977. _OnAdvancedClicked();
  2978. break;
  2979. default:
  2980. return FALSE;
  2981. }
  2982. break;
  2983. case IDC_COLORBOX:
  2984. switch(GET_WM_COMMAND_CMD(wParam, lParam))
  2985. {
  2986. case CBN_SELCHANGE:
  2987. {
  2988. HWND hwndColorBox = GetDlgItem(_hDlg, IDC_COLORBOX);
  2989. int iClr = ComboBox_GetCurSel(hwndColorBox);
  2990. if ((iClr != CB_ERR) && _pCurDevice && _pCurDevice->pds && _pCurDevice->ColorList)
  2991. {
  2992. // Message box if something bad is going to happen ?
  2993. // _VerifyPrimaryMode(TRUE);
  2994. _pCurDevice->pds->SetCurColor((int) *(_pCurDevice->ColorList + iClr));
  2995. // Repaint the control in case they changed
  2996. _UpdateUI(TRUE /*fAutoSetColorDepth*/, IDC_COLORBOX);
  2997. }
  2998. break;
  2999. }
  3000. default:
  3001. break;
  3002. }
  3003. break;
  3004. case IDC_TROUBLESHOOT:
  3005. // Invokes the trouble shooter for the Settings tab.
  3006. {
  3007. TCHAR szCommand[MAX_PATH];
  3008. LoadString(HINST_THISDLL,IDS_TROUBLESHOOT_EXEC, szCommand, ARRAYSIZE(szCommand));
  3009. HrShellExecute(_hwndDesk, NULL, TEXT("HelpCtr.exe"), szCommand, NULL, SW_NORMAL);
  3010. }
  3011. break;
  3012. case IDC_IDENTIFY:
  3013. // Flashes the Text on all the monitors simultaneously
  3014. {
  3015. HWND hwndC;
  3016. //Enumerate all the monitors and flash this for each!
  3017. hwndC = GetWindow(_hwndDesk, GW_CHILD);
  3018. while (hwndC)
  3019. {
  3020. PostMessage(hwndC, WM_COMMAND, MAKEWPARAM(IDC_FLASH, 0), MAKELPARAM(0, 0));
  3021. hwndC = GetWindow(hwndC, GW_HWNDNEXT);
  3022. }
  3023. }
  3024. break;
  3025. default:
  3026. return FALSE;
  3027. }
  3028. // Enable the apply button only if we are not in setup.
  3029. DWORD dwExecMode;
  3030. if (_pThemeUI && (SUCCEEDED(_pThemeUI->GetExecMode(&dwExecMode))))
  3031. {
  3032. if ( (dwExecMode == EM_NORMAL) ||
  3033. (dwExecMode == EM_INVALID_MODE) ||
  3034. (dwExecMode == EM_DETECT) )
  3035. {
  3036. // Set the apply button if something changed
  3037. if (_pCurDevice && _pCurDevice->pds &&
  3038. _pCurDevice->pds->bIsModeChanged())
  3039. {
  3040. SetDirty();
  3041. }
  3042. }
  3043. }
  3044. break;
  3045. case WM_HSCROLL:
  3046. _HandleHScroll((HWND)lParam, (int) LOWORD(wParam), (int) HIWORD(wParam));
  3047. break;
  3048. #if QUICK_REFRESH
  3049. case WM_RBUTTONDOWN:
  3050. if (_NumDevices == 1 && GetKeyState(VK_CONTROL) & 0x8000)
  3051. {
  3052. HMENU hfreq;
  3053. HWND hwnd;
  3054. POINT pt;
  3055. RECT rc;
  3056. DWORD cmd;
  3057. hwnd = GetDlgItem(_hDlg, IDC_SCREENSAMPLE);
  3058. GetWindowRect(hwnd, &rc);
  3059. GetCursorPos(&pt);
  3060. if (!PtInRect(&rc,pt))
  3061. {
  3062. break;
  3063. }
  3064. hfreq = CreateFrequencyMenu(_pCurDevice);
  3065. cmd = TrackPopupMenu(hfreq, TPM_RIGHTBUTTON | TPM_RETURNCMD,
  3066. pt.x, pt.y, 0, _hDlg, NULL);
  3067. if (cmd && _pCurDevice && _pCurDevice->pds)
  3068. {
  3069. _pCurDevice->pds->SetCurFrequency(cmd - IDC_FREQUENCY_START);
  3070. SetDirty();
  3071. }
  3072. DestroyMenu(hfreq);
  3073. }
  3074. break;
  3075. #endif // QUICK_REFRESH
  3076. case WM_HELP:
  3077. WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, TEXT("display.hlp"), HELP_WM_HELP,
  3078. (DWORD_PTR)(LPTSTR)sc_MultiMonitorHelpIds);
  3079. break;
  3080. case WM_CONTEXTMENU:
  3081. WinHelp((HWND)wParam, TEXT("display.hlp"), HELP_CONTEXTMENU,
  3082. (DWORD_PTR)(LPTSTR)sc_MultiMonitorHelpIds);
  3083. break;
  3084. case WM_DISPLAYCHANGE:
  3085. case WM_WININICHANGE:
  3086. _ForwardToChildren(message, wParam, lParam);
  3087. break;
  3088. case WM_SYSCOLORCHANGE:
  3089. if (_himl)
  3090. ImageList_SetBkColor(_himl, GetSysColor(COLOR_APPWORKSPACE));
  3091. //
  3092. // Needs to be passed to all the new common controls so they repaint
  3093. // correctly using the new system colors
  3094. //
  3095. _ForwardToChildren(message, wParam, lParam);
  3096. //
  3097. // Rerender the monitor(s) bitmap(s) to reflect the new colors
  3098. //
  3099. if (_NumDevices == 1) {
  3100. // set up bitmaps for sample screen
  3101. if (_hbmScrSample && (GetObjectType(_hbmScrSample) != 0)) {
  3102. DeleteObject(_hbmScrSample);
  3103. _hbmScrSample = 0;
  3104. }
  3105. _hbmScrSample = LoadMonitorBitmap( TRUE ); // let them do the desktop
  3106. SendDlgItemMessage(_hDlg, IDC_SCREENSAMPLE, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)_hbmScrSample);
  3107. // get a base copy of the bitmap for when the "internals" change
  3108. if (_hbmMonitor && (GetObjectType(_hbmMonitor) != 0)) {
  3109. DeleteObject(_hbmMonitor);
  3110. _hbmMonitor = 0;
  3111. }
  3112. _hbmMonitor = LoadMonitorBitmap( FALSE ); // we'll do the desktop
  3113. }
  3114. else if (_NumDevices > 0)
  3115. {
  3116. HBITMAP hbm, hbmMask;
  3117. int cx, cy;
  3118. UINT iDevice;
  3119. PMULTIMON_DEVICE pDevice;
  3120. TCHAR ach[4];
  3121. // replace each monitor bitmap with one with correct colors
  3122. for (iDevice = 0; (iDevice < _NumDevices); iDevice++)
  3123. {
  3124. pDevice = &_Devices[iDevice];
  3125. if (pDevice)
  3126. {
  3127. _itot(iDevice+1,ach,10);
  3128. ImageList_GetIconSize(pDevice->himl, &cx, &cy);
  3129. MakeMonitorBitmap(pDevice->w,pDevice->h,ach,&hbm,&hbmMask,cx,cy, (pDevice == _pCurDevice));
  3130. ImageList_Replace(pDevice->himl,pDevice->iImage,hbm,hbmMask);
  3131. DeleteObject(hbm);
  3132. DeleteObject(hbmMask);
  3133. }
  3134. }
  3135. }
  3136. break;
  3137. #if 0
  3138. //
  3139. // NOTE: until video supports device interfaces, we cannot use
  3140. // WM_DEVICECHANGE to detect video changes. The default WM_DEVCHANGE
  3141. // only reports about legacy devices
  3142. //
  3143. case WM_DEVICECHANGE:
  3144. //
  3145. // Rebuild the device list if we are not currently enumerating,
  3146. // because enumerating may cause another device to come on-line
  3147. //
  3148. // We only reenumerate if a new *video* device arrives
  3149. //
  3150. if (!_InSetInfo &&
  3151. (wParam == DBT_DEVICEARRIVAL || wParam == DBT_DEVICEREMOVECOMPLETE))
  3152. {
  3153. DEV_BROADCAST_HDR *bhdr = (DEV_BROADCAST_HDR *) lParam;
  3154. // check for something else here, most likely the dev interface guid
  3155. if (bhdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
  3156. {
  3157. _InitDisplaySettings(FALSE);
  3158. }
  3159. }
  3160. break;
  3161. #endif
  3162. case WM_DESTROY:
  3163. TraceMsg(TF_GENERAL, "WndProc:: WM_DESTROY");
  3164. hwndSample = GetDlgItem(_hDlg, IDC_COLORSAMPLE);
  3165. hbm = (HBITMAP)SendMessage(hwndSample, STM_SETIMAGE, IMAGE_BITMAP, NULL);
  3166. if (hbm)
  3167. DeleteObject(hbm);
  3168. if (_NumDevices == 1)
  3169. {
  3170. hwndSample = GetDlgItem(_hDlg, IDC_SCREENSAMPLE);
  3171. hbm = (HBITMAP)SendMessage(hwndSample, STM_SETIMAGE, IMAGE_BITMAP, NULL);
  3172. if (hbm)
  3173. DeleteObject(hbm);
  3174. if (_hbmScrSample && (GetObjectType(_hbmScrSample) != 0))
  3175. DeleteObject(_hbmScrSample);
  3176. if (_hbmMonitor && (GetObjectType(_hbmMonitor) != 0))
  3177. DeleteObject(_hbmMonitor);
  3178. }
  3179. _DestroyDisplaySettings();
  3180. break;
  3181. case MSG_DSP_SETUP_MESSAGE:
  3182. return _InitMessage();
  3183. // MultiMonitor CPL specific messages
  3184. case MM_REDRAWPREVIEW:
  3185. _RedrawDeskPreviews();
  3186. break;
  3187. case WM_LBUTTONDBLCLK:
  3188. if (_NumDevices == 1)
  3189. {
  3190. HWND hwndSample = GetDlgItem(_hDlg, IDC_SCREENSAMPLE);
  3191. if(NULL != hwndSample)
  3192. {
  3193. POINT pt;
  3194. RECT rc;
  3195. pt.x = GET_X_LPARAM(lParam); // horizontal position of cursor
  3196. pt.y = GET_Y_LPARAM(lParam); // vertical position of cursor
  3197. GetWindowRect(hwndSample, &rc);
  3198. if(ClientToScreen(_hDlg, &pt) && PtInRect(&rc, pt))
  3199. PostMessage(_hDlg, WM_COMMAND, MAKEWPARAM(IDC_DISPLAYPROPERTIES, BN_CLICKED), (LPARAM)hwndSample);
  3200. }
  3201. break;
  3202. }
  3203. else
  3204. return FALSE;
  3205. default:
  3206. return FALSE;
  3207. }
  3208. return TRUE;
  3209. }
  3210. // IUnknown methods
  3211. HRESULT CSettingsPage::QueryInterface(REFIID riid, LPVOID * ppvObj)
  3212. {
  3213. static const QITAB qit[] = {
  3214. QITABENT(CSettingsPage, IObjectWithSite),
  3215. QITABENT(CSettingsPage, IPropertyBag),
  3216. QITABENT(CSettingsPage, IBasePropPage),
  3217. QITABENTMULTI(CSettingsPage, IShellPropSheetExt, IBasePropPage),
  3218. { 0 },
  3219. };
  3220. return QISearch(this, qit, riid, ppvObj);
  3221. }
  3222. ULONG CSettingsPage::AddRef()
  3223. {
  3224. return InterlockedIncrement(&_cRef);
  3225. }
  3226. ULONG CSettingsPage::Release()
  3227. {
  3228. if (InterlockedDecrement(&_cRef))
  3229. return _cRef;
  3230. delete this;
  3231. return 0;
  3232. }
  3233. // IMultiMonConfig methods
  3234. HRESULT CSettingsPage::Initialize(HWND hwndHost, WNDPROC pfnWndProc, DWORD dwReserved)
  3235. {
  3236. HRESULT hr = E_FAIL;
  3237. if (hwndHost && RegisterPreviewWindowClass(pfnWndProc))
  3238. {
  3239. _hwndDesk = hwndHost;
  3240. if (_InitDisplaySettings(TRUE))
  3241. hr = S_OK;
  3242. }
  3243. return hr;
  3244. }
  3245. HRESULT CSettingsPage::GetNumberOfMonitors(int * pCMon, DWORD dwReserved)
  3246. {
  3247. if (pCMon)
  3248. {
  3249. *pCMon = _NumDevices;
  3250. return S_OK;
  3251. }
  3252. return E_FAIL;
  3253. }
  3254. HRESULT CSettingsPage::GetMonitorData(int iMonitor, MonitorData * pmd, DWORD dwReserved)
  3255. {
  3256. ASSERT(pmd);
  3257. if ((pmd == NULL) || ((ULONG)iMonitor >= _NumDevices))
  3258. return ResultFromWin32(ERROR_INVALID_PARAMETER);
  3259. PMULTIMON_DEVICE pDevice = &_Devices[iMonitor];
  3260. pmd->dwSize = SIZEOF(MonitorData);
  3261. if ( pDevice->pds->IsPrimary() )
  3262. pmd->dwStatus |= MD_PRIMARY;
  3263. if ( pDevice->pds->IsAttached() )
  3264. pmd->dwStatus |= MD_ATTACHED;
  3265. pDevice->pds->GetCurPosition(&pmd->rcPos);
  3266. return S_OK;
  3267. }
  3268. HRESULT CSettingsPage::Paint(int iMonitor, DWORD dwReserved)
  3269. {
  3270. _RedrawDeskPreviews();
  3271. return S_OK;
  3272. }
  3273. /*----------------------------------------------------------------------------
  3274. ----------------------------------------------------------------------------*/
  3275. HFONT GetFont(LPRECT prc)
  3276. {
  3277. LOGFONT lf;
  3278. FillMemory(&lf, SIZEOF(lf), 0);
  3279. lf.lfWeight = FW_EXTRABOLD;
  3280. lf.lfHeight = prc->bottom - prc->top;
  3281. lf.lfWidth = 0;
  3282. lf.lfPitchAndFamily = FF_SWISS;
  3283. lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
  3284. return CreateFontIndirect(&lf);
  3285. }
  3286. /*----------------------------------------------------------------------------
  3287. ----------------------------------------------------------------------------*/
  3288. #define HANG_TIME 2500
  3289. LRESULT CALLBACK BigNumberWindowProc(HWND hwnd, UINT msg,WPARAM wParam,LPARAM lParam)
  3290. {
  3291. TCHAR ach[80];
  3292. HFONT hfont;
  3293. RECT rc;
  3294. HDC hdc;
  3295. HRGN hrgnTxtA;
  3296. PAINTSTRUCT ps;
  3297. HGDIOBJ hOldPen;
  3298. HGDIOBJ hNewPen;
  3299. switch (msg)
  3300. {
  3301. case WM_CREATE:
  3302. break;
  3303. case WM_SIZE:
  3304. GetWindowText(hwnd, ach, ARRAYSIZE(ach));
  3305. GetClientRect(hwnd, &rc);
  3306. hfont = GetFont(&rc);
  3307. hdc = GetDC(hwnd);
  3308. SelectObject(hdc, hfont);
  3309. BeginPath(hdc);
  3310. SetBkMode(hdc, TRANSPARENT);
  3311. TextOut(hdc,0,0,ach,lstrlen(ach));
  3312. EndPath(hdc);
  3313. hrgnTxtA = PathToRegion(hdc);
  3314. SetWindowRgn(hwnd,hrgnTxtA,TRUE);
  3315. ReleaseDC(hwnd, hdc);
  3316. DeleteObject(hfont);
  3317. break;
  3318. case WM_TIMER:
  3319. DestroyWindow(hwnd);
  3320. return 0;
  3321. case WM_PAINT:
  3322. GetWindowText(hwnd, ach, ARRAYSIZE(ach));
  3323. GetClientRect(hwnd, &rc);
  3324. hfont = GetFont(&rc);
  3325. if (hfont)
  3326. {
  3327. hdc = BeginPaint(hwnd, &ps);
  3328. //The following paints the whole region (which is in the shape of the number) black!
  3329. PatBlt(hdc, 0, 0, rc.right, rc.bottom, BLACKNESS | NOMIRRORBITMAP);
  3330. SelectObject(hdc, hfont);
  3331. SetTextColor(hdc, 0xFFFFFF);
  3332. //Let's create a path that is the shape of the region by drawing that number.
  3333. BeginPath(hdc);
  3334. SetBkMode(hdc, TRANSPARENT);
  3335. TextOut(hdc,0,0,ach,lstrlen(ach));
  3336. EndPath(hdc);
  3337. // The above TextOut calljust created the path. Let's now actually draw the number!
  3338. // Note: We are drawing the number in white there by changing whatever painted as black
  3339. // a few moments ago!
  3340. TextOut(hdc,0,0,ach,lstrlen(ach));
  3341. //Let's create a thick black brush to paint the borders of the number we just drew!
  3342. hNewPen = CreatePen(PS_INSIDEFRAME, 4, 0x0); //Black Color
  3343. if (hNewPen)
  3344. {
  3345. hOldPen = SelectObject(hdc, hNewPen);
  3346. //Draw the border of the white number with the thick black brush!
  3347. StrokePath(hdc);
  3348. SelectObject(hdc, hOldPen);
  3349. DeleteObject(hNewPen);
  3350. }
  3351. EndPaint(hwnd, &ps);
  3352. DeleteObject(hfont);
  3353. }
  3354. break;
  3355. }
  3356. return DefWindowProc(hwnd,msg,wParam,lParam);
  3357. }
  3358. int Bail()
  3359. {
  3360. POINT pt;
  3361. POINT pt0;
  3362. DWORD time0;
  3363. DWORD d;
  3364. d = GetDoubleClickTime();
  3365. time0 = GetMessageTime();
  3366. pt0.x = (int)(short)LOWORD(GetMessagePos());
  3367. pt0.y = (int)(short)HIWORD(GetMessagePos());
  3368. if (GetTickCount()-time0 > d)
  3369. return 2;
  3370. if (!((GetAsyncKeyState(VK_LBUTTON) | GetAsyncKeyState(VK_RBUTTON)) & 0x8000))
  3371. return 1;
  3372. GetCursorPos(&pt);
  3373. if ((pt.y - pt0.y) > 2 || (pt.y - pt0.y) < -2)
  3374. return 1;
  3375. if ((pt.x - pt0.x) > 2 || (pt.x - pt0.x) < -2)
  3376. return 1;
  3377. return 0;
  3378. }
  3379. void FlashText(HWND hDlg, PMULTIMON_DEVICE pDevice, LPCTSTR sz, LPRECT prc, BOOL fWait)
  3380. {
  3381. HFONT hfont;
  3382. SIZE size;
  3383. HDC hdc;
  3384. int i;
  3385. if (!pDevice->pds->IsOrgAttached())
  3386. return;
  3387. if (pDevice->hwndFlash && IsWindow(pDevice->hwndFlash))
  3388. {
  3389. DestroyWindow(pDevice->hwndFlash);
  3390. pDevice->hwndFlash = NULL;
  3391. }
  3392. if (sz == NULL)
  3393. return;
  3394. if (fWait)
  3395. {
  3396. while ((i=Bail()) == 0)
  3397. ;
  3398. if (i == 1)
  3399. return;
  3400. }
  3401. hdc = GetDC(NULL);
  3402. hfont = GetFont(prc);
  3403. SelectObject(hdc, hfont);
  3404. if (!GetTextExtentPoint(hdc, sz, lstrlen(sz), &size))
  3405. {
  3406. size.cx = 0;
  3407. size.cy = 0;
  3408. }
  3409. ReleaseDC(NULL, hdc);
  3410. DeleteObject(hfont);
  3411. if (!gfFlashWindowRegistered)
  3412. {
  3413. WNDCLASS cls;
  3414. cls.hCursor = LoadCursor(NULL,IDC_ARROW);
  3415. cls.hIcon = NULL;
  3416. cls.lpszMenuName = NULL;
  3417. cls.lpszClassName = TEXT("MonitorNumber32");
  3418. cls.hbrBackground = (HBRUSH)(COLOR_DESKTOP + 1);
  3419. cls.hInstance = HINST_THISDLL;
  3420. cls.style = CS_VREDRAW | CS_HREDRAW;
  3421. cls.lpfnWndProc = BigNumberWindowProc;
  3422. cls.cbWndExtra = 0;
  3423. cls.cbClsExtra = 0;
  3424. RegisterClass(&cls);
  3425. gfFlashWindowRegistered = TRUE;
  3426. }
  3427. pDevice->hwndFlash = CreateWindowEx(
  3428. WS_EX_TOPMOST | WS_EX_TOOLWINDOW, //WS_BORDER,
  3429. TEXT("MonitorNumber32"), sz,
  3430. WS_POPUP,
  3431. (prc->right + prc->left - size.cx)/2,
  3432. (prc->bottom + prc->top - size.cy)/2,
  3433. size.cx,
  3434. size.cy,
  3435. hDlg, //Set the dialog as the parent sothat we get back the activation after flash window goes away!
  3436. NULL,
  3437. HINST_THISDLL,
  3438. NULL);
  3439. if (pDevice->hwndFlash)
  3440. {
  3441. ShowWindow(pDevice->hwndFlash, SW_SHOW);
  3442. UpdateWindow(pDevice->hwndFlash);
  3443. SetTimer(pDevice->hwndFlash, 1, HANG_TIME, NULL);
  3444. }
  3445. }
  3446. void DrawMonitorNum(HDC hdc, int w, int h, LPCTSTR sz, BOOL fDrawBackground=TRUE)
  3447. {
  3448. HFONT hfont;
  3449. HFONT hfontT;
  3450. RECT rc;
  3451. COLORREF rgb;
  3452. COLORREF rgbDesk;
  3453. SetRect(&rc, 0, 0, w, h);
  3454. rgb = GetSysColor(COLOR_CAPTIONTEXT);
  3455. rgbDesk = GetSysColor(COLOR_DESKTOP);
  3456. if (fDrawBackground)
  3457. FillRect(hdc, &rc, GetSysColorBrush (COLOR_DESKTOP));
  3458. InflateRect(&rc, -(MON_X*w / MON_W)>> 1, -(MON_Y*h/ MON_H));
  3459. if (rgbDesk == rgb)
  3460. rgb = GetSysColor(COLOR_WINDOWTEXT);
  3461. if (rgbDesk == rgb)
  3462. rgb = rgbDesk ^ 0x00FFFFFF;
  3463. SetTextColor(hdc, rgb);
  3464. hfont = GetFont(&rc);
  3465. if (hfont)
  3466. {
  3467. hfontT = (HFONT)SelectObject(hdc, hfont);
  3468. SetTextAlign(hdc, TA_CENTER | TA_TOP);
  3469. SetBkMode(hdc, TRANSPARENT);
  3470. ExtTextOut(hdc, (rc.left+rc.right)/2, rc.top, 0, NULL, sz, lstrlen(sz), NULL);
  3471. SelectObject(hdc, hfontT);
  3472. DeleteObject(hfont);
  3473. }
  3474. }
  3475. void AddTrackingToolTip(PMULTIMON_DEVICE pDevice, HWND hwnd)
  3476. {
  3477. TOOLINFO ti;
  3478. TCHAR location[16];
  3479. RECT rcPos;
  3480. //
  3481. // New tool Tip
  3482. //
  3483. pDevice->pds->GetCurPosition(&rcPos);
  3484. wnsprintf(location, ARRAYSIZE(location), TEXT("%d, %d"), rcPos.left, rcPos.top);
  3485. GetWindowRect(hwnd, &rcPos);
  3486. ti.cbSize = sizeof(TOOLINFO);
  3487. ti.uFlags = TTF_TRACK;
  3488. ti.hwnd = hwnd;
  3489. ti.uId = (UINT_PTR) pDevice;
  3490. ti.hinst = HINST_THISDLL;
  3491. ti.lpszText = location;
  3492. ti.rect.left = rcPos.left + 2;
  3493. ti.rect.top = rcPos.top + 2;
  3494. ti.rect.right = rcPos.right - 2;// ti.rect.left + 10;
  3495. ti.rect.bottom = rcPos.bottom - 2; // ti.rect.top + 10;
  3496. ToolTip_AddTool(ghwndToolTipTracking, &ti);
  3497. pDevice->bTracking = FALSE;
  3498. TraceMsg(TF_GENERAL, "Added TOOLTIP hwnd %08lx, uId %08lx\n", ti.hwnd, ti.uId);
  3499. return;
  3500. }
  3501. void RemoveTrackingToolTip(HWND hwnd)
  3502. {
  3503. TOOLINFO ti;
  3504. ZeroMemory(&ti, sizeof(TOOLINFO));
  3505. ti.cbSize = sizeof(TOOLINFO);
  3506. ti.hwnd = hwnd;
  3507. ti.uId = (UINT_PTR) GetDlgCtrlDevice(hwnd);
  3508. ToolTip_DelTool(ghwndToolTipTracking, &ti);
  3509. }
  3510. BOOLEAN TrackToolTip(PMULTIMON_DEVICE pDevice, HWND hwnd, BOOL bTrack)
  3511. {
  3512. TOOLINFO ti;
  3513. BOOLEAN oldTracking;
  3514. ZeroMemory(&ti, sizeof(TOOLINFO));
  3515. ti.cbSize = sizeof(TOOLINFO);
  3516. ti.hwnd = hwnd;
  3517. ti.uId = (UINT_PTR) pDevice;
  3518. oldTracking = pDevice->bTracking;
  3519. pDevice->bTracking = (BOOLEAN)bTrack;
  3520. ToolTip_TrackActivate(ghwndToolTipTracking, bTrack, &ti);
  3521. TraceMsg(TF_GENERAL, "Track TOOLTIP hwnd %08lx, uId %08lx\n", ti.hwnd, ti.uId);
  3522. return oldTracking;
  3523. }
  3524. void AddPopupToolTip(HWND hwndC)
  3525. {
  3526. TOOLINFO ti;
  3527. //
  3528. // New tool Tip
  3529. //
  3530. ti.cbSize = sizeof(TOOLINFO);
  3531. ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS | TTF_CENTERTIP;
  3532. ti.hwnd = hwndC;
  3533. ti.uId = (UINT_PTR) hwndC;
  3534. ti.hinst = HINST_THISDLL;
  3535. GetWindowRect(hwndC, &ti.rect);
  3536. ti.lpszText = LPSTR_TEXTCALLBACK;
  3537. ToolTip_AddTool(ghwndToolTipPopup, &ti);
  3538. }
  3539. void RemovePopupToolTip(HWND hwndC)
  3540. {
  3541. TOOLINFO ti;
  3542. ZeroMemory(&ti, sizeof(TOOLINFO));
  3543. ti.cbSize = sizeof(TOOLINFO);
  3544. ti.hwnd = hwndC;
  3545. ti.uId = (UINT_PTR) hwndC;
  3546. ToolTip_DelTool(ghwndToolTipPopup, &ti);
  3547. }
  3548. #if QUICK_REFRESH
  3549. HMENU CreateFrequencyMenu(PMULTIMON_DEVICE pDevice)
  3550. {
  3551. HMENU hfreq;
  3552. MENUITEMINFO mii;
  3553. ZeroMemory(&mii, sizeof(mii));
  3554. mii.cbSize = sizeof(mii);
  3555. if (!(hfreq = CreatePopupMenu())) {
  3556. return NULL;
  3557. }
  3558. PLONGLONG freqs;
  3559. UINT i, iFreq = pDevice->pds->GetFrequencyList(-1, NULL, &freqs);
  3560. int curFreq = pDevice->pds->GetCurFrequency(), freq;
  3561. TCHAR ach[LINE_LEN], achFre[50];
  3562. mii.fMask = MIIM_TYPE | MFT_STRING | MIIM_ID | MIIM_STATE;
  3563. mii.dwTypeData = ach;
  3564. mii.fType = MFT_STRING;
  3565. mii.fState = MFS_ENABLED;
  3566. for (i = 0; i < iFreq; i++ ) {
  3567. freq = (int) freqs[i];
  3568. if (freq == 1) {
  3569. LoadString(HINST_THISDLL, IDS_DEFFREQ, ach, ARRAYSIZE(ach));
  3570. }
  3571. else {
  3572. DWORD idFreq = IDS_FREQ;
  3573. if (freq < 50) {
  3574. idFreq = IDS_INTERLACED;
  3575. }
  3576. LoadString(HINST_THISDLL, idFreq, achFre, ARRAYSIZE(achFre));
  3577. wnsprintf(ach, ARRAYSIZE(ach), TEXT("%d %s"), freq, achFre);
  3578. }
  3579. mii.cch = lstrlen(ach);
  3580. mii.wID = IDC_FREQUENCY_START + freq;
  3581. mii.fState = MFS_ENABLED;
  3582. if (curFreq == freq)
  3583. mii.fState = MFS_CHECKED;
  3584. InsertMenuItem(hfreq, i, TRUE, &mii);
  3585. }
  3586. LocalFree(freqs);
  3587. return hfreq;
  3588. }
  3589. #endif
  3590. BOOL MakeMonitorBitmap(int w, int h, LPCTSTR sz, HBITMAP *pBitmap, HBITMAP *pMaskBitmap, int cx, int cy, BOOL fSelected)
  3591. {
  3592. HDC hdc; // work dc
  3593. HDC hdcS; // screen dc
  3594. ASSERT(w <= cx);
  3595. ASSERT(h <= cy);
  3596. *pBitmap = NULL;
  3597. hdcS = GetDC(NULL);
  3598. hdc = CreateCompatibleDC(hdcS);
  3599. if (hdc)
  3600. {
  3601. HDC hdcT; // another work dc
  3602. hdcT = CreateCompatibleDC(hdcS);
  3603. if (hdcT)
  3604. {
  3605. HBITMAP hbm; // 128x128 bitmap we will return
  3606. HBITMAP hbmT = NULL;// bitmap loaded from resource
  3607. HBITMAP hbmM = NULL;// mask bitmap
  3608. HDC hdcM = NULL;// another work dc
  3609. RECT rc;
  3610. if (pMaskBitmap)
  3611. hdcM = CreateCompatibleDC(hdcS);
  3612. hbm = CreateCompatibleBitmap(hdcS, cx, cy);
  3613. if (hbm)
  3614. {
  3615. hbmT = CreateCompatibleBitmap(hdcS, w, h);
  3616. if(pMaskBitmap)
  3617. hbmM = CreateBitmap(cx,cy,1,1,NULL);
  3618. ReleaseDC(NULL,hdcS);
  3619. if (hbmT)
  3620. {
  3621. SelectObject(hdc, hbm);
  3622. SelectObject(hdcT,hbmT);
  3623. if (pMaskBitmap && hdcM)
  3624. SelectObject(hdcM, hbmM);
  3625. }
  3626. *pBitmap = hbm;
  3627. }
  3628. // Make sure the color of the borders (selection & normal) is different than the background color.
  3629. HBRUSH hbrDiff = NULL;
  3630. BOOL bNeedDiff = ((fSelected &&
  3631. (GetSysColor(COLOR_APPWORKSPACE) == GetSysColor(COLOR_HIGHLIGHT))) ||
  3632. (GetSysColor(COLOR_APPWORKSPACE) == GetSysColor(COLOR_BTNHIGHLIGHT)));
  3633. if(bNeedDiff)
  3634. {
  3635. DWORD rgbDiff = ((GetSysColor(COLOR_ACTIVEBORDER) != GetSysColor(COLOR_APPWORKSPACE))
  3636. ? GetSysColor(COLOR_ACTIVEBORDER)
  3637. : GetSysColor(COLOR_APPWORKSPACE) ^ 0x00FFFFFF);
  3638. hbrDiff = CreateSolidBrush(rgbDiff);
  3639. }
  3640. // Fill it with the selection color or the background color.
  3641. SetRect(&rc, 0, 0, w, h);
  3642. FillRect(hdcT, &rc,
  3643. (fSelected ? ((GetSysColor(COLOR_APPWORKSPACE) != GetSysColor(COLOR_HIGHLIGHT))
  3644. ? GetSysColorBrush(COLOR_HIGHLIGHT)
  3645. : hbrDiff)
  3646. : GetSysColorBrush(COLOR_APPWORKSPACE)));
  3647. InflateRect(&rc, -SELECTION_THICKNESS, -SELECTION_THICKNESS);
  3648. FillRect(hdcT, &rc,
  3649. ((GetSysColor(COLOR_APPWORKSPACE) != GetSysColor(COLOR_BTNHIGHLIGHT))
  3650. ? GetSysColorBrush(COLOR_BTNHIGHLIGHT)
  3651. : hbrDiff));
  3652. if (hbrDiff)
  3653. {
  3654. DeleteObject(hbrDiff);
  3655. hbrDiff = NULL;
  3656. }
  3657. InflateRect(&rc, -MONITOR_BORDER, -MONITOR_BORDER);
  3658. FillRect(hdcT, &rc, GetSysColorBrush(COLOR_DESKTOP));
  3659. // fill bitmap with transparent color
  3660. SetBkColor(hdc,GetSysColor(COLOR_APPWORKSPACE));
  3661. SetRect(&rc, 0, 0, cx, cy);
  3662. ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
  3663. // copy bitmap to upper-left of bitmap
  3664. BitBlt(hdc,0,0,w,h,hdcT,0,0,SRCCOPY);
  3665. // draw the monitor number, if provided, in the bitmap (in the right place)
  3666. if (sz)
  3667. DrawMonitorNum(hdc, w, h, sz, FALSE);
  3668. // make mask, if needed.
  3669. if (pMaskBitmap && hdcM)
  3670. {
  3671. BitBlt(hdcM,0,0,cx,cy,hdc,0,0,SRCCOPY);
  3672. *pMaskBitmap = hbmM;
  3673. }
  3674. if (hbmT)
  3675. DeleteObject(hbmT);
  3676. if (pMaskBitmap && hdcM)
  3677. DeleteDC(hdcM);
  3678. DeleteDC(hdcT);
  3679. }
  3680. DeleteDC(hdc);
  3681. }
  3682. return TRUE;
  3683. }
  3684. //
  3685. // SnapMonitorRect
  3686. //
  3687. // called while the user is moving a monitor window (WM_MOVING)
  3688. // if the CTRL key is not down we will snap the window rect
  3689. // to the edge of one of the other monitors.
  3690. //
  3691. // this is done so the user can easily align monitors
  3692. //
  3693. // NOTE pDevice->Snap must be initialized to 0,0 in WM_ENTERSIZEMOVE
  3694. //
  3695. void SnapMonitorRect(PMULTIMON_DEVICE pDevice, HWND hwnd, RECT *prc)
  3696. {
  3697. HWND hwndT;
  3698. int d;
  3699. RECT rcT;
  3700. RECT rc;
  3701. //
  3702. // allow the user to move the window anywhere when the CTRL key is down
  3703. //
  3704. if (GetKeyState(VK_CONTROL) & 0x8000)
  3705. return;
  3706. //
  3707. // macros to help in alignment
  3708. //
  3709. #define SNAP_DX 6
  3710. #define SNAP_DY 6
  3711. #define SNAPX(f,x) \
  3712. d = rcT.x - rc.f; if (abs(d) <= SNAP_DX) rc.left+=d, rc.right+=d;
  3713. #define SNAPY(f,y) \
  3714. d = rcT.y - rc.f; if (abs(d) <= SNAP_DY) rc.top+=d, rc.bottom+=d;
  3715. //
  3716. // get current rect and offset it by the amount we have corrected
  3717. // it so far (this alignes the rect with the position of the mouse)
  3718. //
  3719. rc = *prc;
  3720. OffsetRect(&rc, pDevice->Snap.x, pDevice->Snap.y);
  3721. //
  3722. // walk all other windows and snap our window to them
  3723. //
  3724. for (hwndT = GetWindow(hwnd, GW_HWNDFIRST); hwndT;
  3725. hwndT = GetWindow(hwndT, GW_HWNDNEXT))
  3726. {
  3727. if (hwndT == hwnd)
  3728. continue;
  3729. GetWindowRect(hwndT, &rcT);
  3730. InflateRect(&rcT,SNAP_DX,SNAP_DY);
  3731. if (IntersectRect(&rcT, &rcT, &rc))
  3732. {
  3733. GetWindowRect(hwndT, &rcT);
  3734. SNAPX(right,left); SNAPY(bottom,top);
  3735. SNAPX(right,right); SNAPY(bottom,bottom);
  3736. SNAPX(left,left); SNAPY(top,top);
  3737. SNAPX(left,right); SNAPY(top,bottom);
  3738. }
  3739. }
  3740. //
  3741. // adjust the amount we have snap'ed so far, and return the new rect
  3742. //
  3743. pDevice->Snap.x += prc->left - rc.left;
  3744. pDevice->Snap.y += prc->top - rc.top;
  3745. *prc = rc;
  3746. }
  3747. WPARAM GetKeyStates()
  3748. {
  3749. WPARAM wParam = 0x0;
  3750. if (GetKeyState(VK_CONTROL) & 0x8000)
  3751. wParam |= MK_CONTROL;
  3752. if (GetKeyState(VK_LBUTTON) & 0x8000)
  3753. wParam |= MK_LBUTTON;
  3754. if (GetKeyState(VK_MBUTTON) & 0x8000)
  3755. wParam |= MK_MBUTTON;
  3756. if (GetKeyState(VK_RBUTTON) & 0x8000)
  3757. wParam |= MK_RBUTTON;
  3758. if (GetKeyState(VK_SHIFT) & 0x8000)
  3759. wParam |= MK_SHIFT;
  3760. return wParam;
  3761. }
  3762. LRESULT CALLBACK MonitorWindowProc(HWND hwnd, UINT msg,WPARAM wParam,LPARAM lParam)
  3763. {
  3764. TOOLINFO ti;
  3765. PAINTSTRUCT ps;
  3766. HDC hdc;
  3767. RECT rc;
  3768. int w,h;
  3769. TCHAR ach[80];
  3770. PMULTIMON_DEVICE pDevice;
  3771. HWND hDlg = GetParent(GetParent(hwnd));
  3772. RECT rcPos;
  3773. MSG mmsg;
  3774. CSettingsPage * pcmm = (CSettingsPage *) GetWindowLongPtr(hwnd, 0);
  3775. switch (msg)
  3776. {
  3777. case WM_CREATE:
  3778. ASSERT(((LPCREATESTRUCT)lParam)->lpCreateParams);
  3779. SetWindowLongPtr(hwnd, 0, (LONG_PTR)((LPCREATESTRUCT)lParam)->lpCreateParams);
  3780. break;
  3781. case WM_NCCREATE:
  3782. // turn off RTL_MIRRORED_WINDOW in GWL_EXSTYLE
  3783. SHSetWindowBits(hwnd, GWL_EXSTYLE, RTL_MIRRORED_WINDOW, 0);
  3784. break;
  3785. case WM_NCHITTEST:
  3786. //
  3787. // return HTCAPTION so that we can get the ENTERSIZEMOVE message.
  3788. //
  3789. pDevice = GetDlgCtrlDevice(hwnd);
  3790. // Let disabled monitors move
  3791. if (pDevice) // if (pDevice && pDevice->pds->IsAttached())
  3792. return HTCAPTION;
  3793. break;
  3794. case WM_NCLBUTTONDBLCLK:
  3795. FlashText(hDlg, GetDlgCtrlDevice(hwnd), NULL,NULL,FALSE);
  3796. PostMessage(hDlg, WM_COMMAND, MAKEWPARAM(IDC_DISPLAYPROPERTIES, BN_CLICKED), (LPARAM)hwnd );
  3797. break;
  3798. case WM_CHILDACTIVATE:
  3799. if (GetFocus() != GetParent(hwnd)) {
  3800. SetFocus(GetParent(hwnd));
  3801. }
  3802. break;
  3803. case WM_HELP:
  3804. WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, TEXT("display.hlp"), HELP_WM_HELP,
  3805. (DWORD_PTR)(LPTSTR)sc_MultiMonitorHelpIds);
  3806. break;
  3807. case WM_CONTEXTMENU:
  3808. WinHelp((HWND)wParam, TEXT("display.hlp"), HELP_CONTEXTMENU,
  3809. (DWORD_PTR)(LPTSTR)sc_MultiMonitorHelpIds);
  3810. break;
  3811. case WM_COMMAND:
  3812. switch (GET_WM_COMMAND_ID(wParam, lParam))
  3813. {
  3814. case IDC_DISPLAYPRIME:
  3815. case IDC_DISPLAYUSEME:
  3816. case IDC_DISPLAYPROPERTIES:
  3817. PostMessage(hDlg, WM_COMMAND, wParam, lParam);
  3818. break;
  3819. case IDC_FLASH:
  3820. pDevice = GetDlgCtrlDevice(hwnd);
  3821. pDevice->pds->GetOrgPosition(&rcPos);
  3822. if (!IsRectEmpty(&rcPos))
  3823. {
  3824. GetWindowText(hwnd, ach, ARRAYSIZE(ach));
  3825. FlashText(hDlg, pDevice, ach, &rcPos, FALSE);
  3826. }
  3827. break;
  3828. #if QUICK_REFRESH
  3829. default:
  3830. ASSERT(LOWORD(wParam) - IDC_FREQUENCY_START > 0);
  3831. pDevice = GetDlgCtrlDevice(hwnd);
  3832. pDevice->pds->SetCurFrequency(LOWORD(wParam) - IDC_FREQUENCY_START);
  3833. pcmm->SetDirty();
  3834. #endif
  3835. break;
  3836. }
  3837. break;
  3838. case WM_INITMENUPOPUP:
  3839. pDevice = GetDlgCtrlDevice(hwnd);
  3840. CheckMenuItem((HMENU)wParam, IDC_DISPLAYUSEME,
  3841. pDevice->pds->IsAttached() ? MF_CHECKED : MF_UNCHECKED);
  3842. CheckMenuItem((HMENU)wParam, IDC_DISPLAYPRIME,
  3843. pDevice->pds->IsPrimary() ? MF_CHECKED : MF_UNCHECKED);
  3844. // until I figure out how to render on a non attached monitor, just
  3845. // disable the menu item
  3846. EnableMenuItem((HMENU)wParam, IDC_FLASH,
  3847. pDevice->pds->IsAttached() ? MF_ENABLED : MF_GRAYED);
  3848. EnableMenuItem((HMENU)wParam, IDC_DISPLAYPROPERTIES,
  3849. IsWindowEnabled(GetDlgItem(GetParent(GetParent(hwnd)), IDC_DISPLAYPROPERTIES)) ?
  3850. MF_ENABLED : MF_GRAYED);
  3851. EnableMenuItem((HMENU)wParam, IDC_DISPLAYUSEME,
  3852. pDevice->pds->IsPrimary() ? MF_GRAYED : MF_ENABLED);
  3853. EnableMenuItem((HMENU)wParam, IDC_DISPLAYPRIME,
  3854. ((pDevice->pds->IsAttached() &&
  3855. !pDevice->pds->IsRemovable() &&
  3856. !pDevice->pds->IsPrimary()) ?
  3857. MF_ENABLED : MF_GRAYED));
  3858. SetMenuDefaultItem((HMENU)wParam, IDC_DISPLAYPROPERTIES, MF_BYCOMMAND);
  3859. break;
  3860. case WM_NCMOUSEMOVE:
  3861. ToolTip_RelayEvent(ghwndToolTipPopup, mmsg, NULL, WM_MOUSEMOVE, GetKeyStates(), lParam);
  3862. break;
  3863. case WM_NCMBUTTONDOWN:
  3864. ToolTip_RelayEvent(ghwndToolTipPopup, mmsg, NULL, WM_MBUTTONDOWN, GetKeyStates(), lParam);
  3865. break;
  3866. case WM_NCMBUTTONUP:
  3867. ToolTip_RelayEvent(ghwndToolTipPopup, mmsg, NULL, WM_MBUTTONUP, GetKeyStates(), lParam);
  3868. break;
  3869. case WM_NCRBUTTONDOWN:
  3870. ToolTip_RelayEvent(ghwndToolTipPopup, mmsg, NULL, WM_RBUTTONDOWN, GetKeyStates(), lParam);
  3871. pDevice = GetDlgCtrlDevice(hwnd);
  3872. if (pDevice && pcmm)
  3873. {
  3874. HMENU hmenu;
  3875. POINT pt;
  3876. hmenu = LoadMenu(HINST_THISDLL, MAKEINTRESOURCE(MENU_MONITOR));
  3877. if (hmenu)
  3878. {
  3879. #if QUICK_REFRESH
  3880. if (GetKeyState(VK_CONTROL) & 0x8000) {
  3881. HMENU hsub;
  3882. MENUITEMINFO mii;
  3883. TCHAR ach[LINE_LEN];
  3884. LoadString(HINST_THISDLL, IDS_FREQUENCY, ach, ARRAYSIZE(ach));
  3885. hsub = GetSubMenu(hmenu, 0);
  3886. ZeroMemory(&mii, sizeof(mii));
  3887. mii.cbSize = sizeof(mii);
  3888. mii.fMask = MIIM_TYPE;
  3889. mii.fType = MFT_SEPARATOR;
  3890. InsertMenuItem(hsub, GetMenuItemCount(hsub), TRUE, &mii);
  3891. mii.fMask = MIIM_SUBMENU | MIIM_TYPE | MIIM_STATE;
  3892. mii.hSubMenu = CreateFrequencyMenu(pDevice);
  3893. mii.fType = MFT_STRING;
  3894. mii.fState = MFS_ENABLED;
  3895. mii.dwTypeData = ach;
  3896. InsertMenuItem(hsub, GetMenuItemCount(hsub), TRUE, &mii);
  3897. DestroyMenu(mii.hSubMenu);
  3898. }
  3899. #endif // QUICK_REFRESH
  3900. pcmm->UpdateActiveDisplay(pDevice);
  3901. GetCursorPos(&pt);
  3902. TrackPopupMenu(GetSubMenu(hmenu,0), TPM_RIGHTBUTTON,
  3903. pt.x, pt.y, 0, hwnd, NULL);
  3904. DestroyMenu(hmenu);
  3905. }
  3906. }
  3907. break;
  3908. case WM_NCRBUTTONUP:
  3909. ToolTip_RelayEvent(ghwndToolTipPopup, mmsg, NULL, WM_RBUTTONUP, GetKeyStates(), lParam);
  3910. break;
  3911. case WM_NCLBUTTONDOWN:
  3912. //TraceMsg(TF_FUNC, "WM_NCLBUTTONDOWN");
  3913. // don't relay the message here because we want to keep the tool tip
  3914. // active until they start moving the monitor. This click might just
  3915. // be for selection
  3916. // ToolTip_RelayEvent(ghwndToolTipPopup, mmsg, hDlg, WM_LBUTTONDOWN, GetKeyStates(), lParam);
  3917. BringWindowToTop(hwnd);
  3918. pDevice = GetDlgCtrlDevice(hwnd);
  3919. if (pcmm)
  3920. pcmm->UpdateActiveDisplay(pDevice);
  3921. pDevice->pds->GetOrgPosition(&rcPos);
  3922. if (!IsRectEmpty(&rcPos))
  3923. {
  3924. GetWindowText(hwnd, ach, ARRAYSIZE(ach));
  3925. FlashText(hDlg, pDevice, ach, &rcPos, TRUE);
  3926. }
  3927. break;
  3928. #if 0
  3929. case WM_NCLBUTTONUP:
  3930. ToolTip_RelayEvent(ghwndToolTipPopup, mmsg, NULL, WM_LBUTTONUP, GetKeyStates(), lParam);
  3931. break;
  3932. #endif
  3933. case WM_NOTIFY:
  3934. switch (((NMHDR FAR *)lParam)->code)
  3935. {
  3936. case TTN_NEEDTEXT:
  3937. pDevice = GetDlgCtrlDevice(hwnd);
  3938. if (pDevice->pds->IsPrimary())
  3939. {
  3940. LoadString(HINST_THISDLL, IDS_PRIMARY,
  3941. ((LPNMTTDISPINFO)lParam)->szText,
  3942. ARRAYSIZE(((LPNMTTDISPINFO)lParam)->szText) );
  3943. }
  3944. else if (!pDevice->pds->IsAttached())
  3945. {
  3946. LoadString(HINST_THISDLL, IDS_NOTATTACHED,
  3947. ((LPNMTTDISPINFO)lParam)->szText,
  3948. ARRAYSIZE(((LPNMTTDISPINFO)lParam)->szText) );
  3949. }
  3950. else
  3951. {
  3952. TCHAR szSecondary[32];
  3953. LoadString(HINST_THISDLL, IDS_SECONDARY, szSecondary, ARRAYSIZE(szSecondary));
  3954. pDevice->pds->GetCurPosition(&rcPos);
  3955. wsprintf(((LPNMTTDISPINFO)lParam)->szText, TEXT("%s (%d, %d)"), szSecondary, rcPos.left, rcPos.top);
  3956. }
  3957. break;
  3958. default:
  3959. break;
  3960. }
  3961. break;
  3962. case WM_ENTERSIZEMOVE:
  3963. //TraceMsg(TF_FUNC, "WM_ENTERSIZEMOVE");
  3964. // relay a mouse up to clean the information tooltip
  3965. ToolTip_RelayEvent(ghwndToolTipPopup, mmsg, NULL, WM_LBUTTONDOWN, GetKeyStates(), lParam);
  3966. pDevice = GetDlgCtrlDevice(hwnd);
  3967. pDevice->Snap.x = 0;
  3968. pDevice->Snap.y = 0;
  3969. FlashText(hDlg, pDevice, NULL,NULL,FALSE);
  3970. break;
  3971. case WM_MOVING:
  3972. //TraceMsg(TF_FUNC, "WM_MOVING");
  3973. pDevice = GetDlgCtrlDevice(hwnd);
  3974. SnapMonitorRect(pDevice, hwnd, (RECT*)lParam);
  3975. ZeroMemory(&ti, sizeof(ti));
  3976. ti.cbSize = sizeof(TOOLINFO);
  3977. if (!pDevice->bTracking) {
  3978. ToolTip_TrackPosition(ghwndToolTipTracking,
  3979. ((LPRECT)lParam)->left+2,
  3980. ((LPRECT)lParam)->top+2);
  3981. TrackToolTip(pDevice, hwnd, TRUE);
  3982. }
  3983. if (ToolTip_GetCurrentTool(ghwndToolTipTracking, &ti) && pcmm)
  3984. {
  3985. TCHAR location[16];
  3986. POINT pt;
  3987. pcmm->GetMonitorPosition(pDevice, GetParent(hwnd), &pt);
  3988. wnsprintf(location, ARRAYSIZE(location), TEXT("%d, %d"), pt.x, pt.y);
  3989. ti.lpszText = location;
  3990. ti.rect.left = ((RECT*)lParam)->left + 2;
  3991. ti.rect.top = ((RECT*)lParam)->top + 2;
  3992. ti.rect.right = ti.rect.left + 10;
  3993. ti.rect.bottom = ti.rect.top + 10;
  3994. ToolTip_SetToolInfo(ghwndToolTipTracking, &ti);
  3995. ToolTip_TrackPosition(ghwndToolTipTracking, ti.rect.left, ti.rect.top);
  3996. // SendMessage(ghwndToolTip, TTM_UPDATE, 0, 0);
  3997. }
  3998. break;
  3999. case WM_EXITSIZEMOVE:
  4000. //TraceMsg(TF_FUNC, "WM_EXITSIZEMOVE");
  4001. pDevice = GetDlgCtrlDevice(hwnd);
  4002. TrackToolTip(pDevice, hwnd, FALSE);
  4003. //
  4004. // We don't want to pop up any dialogs here because the modal size
  4005. // loop is still active (it eats any mouse movements and the dialogs
  4006. // can't be moved by the user).
  4007. //
  4008. PostMessage(hwnd, MM_MONITORMOVED, 0, 0);
  4009. break;
  4010. case MM_MONITORMOVED:
  4011. pDevice = GetDlgCtrlDevice(hwnd);
  4012. if (pcmm)
  4013. {
  4014. //
  4015. // If the user moved the monitor, see if they want to attach it
  4016. //
  4017. if (!pcmm->QueryNoAttach() && pDevice && !pDevice->pds->IsAttached())
  4018. {
  4019. if (pcmm->SetMonAttached(pDevice, TRUE, FALSE, hwnd))
  4020. {
  4021. pcmm->UpdateActiveDisplay(pDevice);
  4022. }
  4023. }
  4024. pcmm->HandleMonitorChange(hwnd, FALSE);
  4025. }
  4026. RedrawWindow(GetParent(hwnd), NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN);
  4027. return TRUE;
  4028. case WM_DESTROY:
  4029. FlashText(hDlg, GetDlgCtrlDevice(hwnd), NULL,NULL,FALSE);
  4030. SetWindowLong(hwnd, 0, NULL);
  4031. break;
  4032. case WM_ERASEBKGND:
  4033. //GetClientRect(hwnd, &rc);
  4034. //FillRect((HDC)wParam, &rc, GetSysColorBrush(COLOR_APPWORKSPACE));
  4035. return 0L;
  4036. case WM_PAINT:
  4037. hdc = BeginPaint(hwnd,&ps);
  4038. GetWindowText(hwnd, ach, ARRAYSIZE(ach));
  4039. GetClientRect(hwnd, &rc);
  4040. w = rc.right;
  4041. h = rc.bottom;
  4042. pDevice = GetDlgCtrlDevice(hwnd);
  4043. BOOL fSelected = (pcmm ? (BOOL)(pDevice == pcmm->GetCurDevice()) : FALSE);
  4044. if (pDevice->w != w || pDevice->h != h)
  4045. {
  4046. HBITMAP hbm, hbmMask;
  4047. int cx,cy;
  4048. pDevice->w = w;
  4049. pDevice->h = h;
  4050. ImageList_GetIconSize(pDevice->himl, &cx, &cy);
  4051. MakeMonitorBitmap(w,h,ach,&hbm,&hbmMask,cx,cy, fSelected);
  4052. ImageList_Replace(pDevice->himl,pDevice->iImage,hbm,hbmMask);
  4053. DeleteObject(hbm);
  4054. DeleteObject(hbmMask);
  4055. }
  4056. if (!pDevice->pds->IsAttached())
  4057. {
  4058. FillRect(hdc, &rc, GetSysColorBrush(COLOR_APPWORKSPACE));
  4059. if (pcmm && fSelected)
  4060. {
  4061. ImageList_DrawEx(pDevice->himl,pDevice->iImage,hdc,0,0,w,h,
  4062. CLR_DEFAULT,CLR_DEFAULT,ILD_BLEND25);
  4063. }
  4064. else
  4065. {
  4066. ImageList_DrawEx(pDevice->himl,pDevice->iImage,hdc,0,0,w,h,
  4067. CLR_DEFAULT,CLR_NONE,ILD_BLEND50);
  4068. }
  4069. }
  4070. else
  4071. {
  4072. ImageList_DrawEx(pDevice->himl,pDevice->iImage,hdc,0,0,w,h,
  4073. CLR_DEFAULT,CLR_DEFAULT,ILD_IMAGE);
  4074. }
  4075. EndPaint(hwnd,&ps);
  4076. return 0L;
  4077. }
  4078. return DefWindowProc(hwnd,msg,wParam,lParam);
  4079. }
  4080. LRESULT CALLBACK SliderSubWndProc (HWND hwndSlider, UINT uMsg, WPARAM wParam, LPARAM lParam, WPARAM uID, ULONG_PTR dwRefData)
  4081. {
  4082. ASSERT(uID == 0);
  4083. ASSERT(dwRefData == 0);
  4084. switch (uMsg)
  4085. {
  4086. case WM_GETOBJECT:
  4087. if ( lParam == OBJID_CLIENT )
  4088. {
  4089. // At this point we will try to load oleacc and get the functions
  4090. // we need.
  4091. if (!g_fAttemptedOleAccLoad)
  4092. {
  4093. g_fAttemptedOleAccLoad = TRUE;
  4094. ASSERT(s_pfnCreateStdAccessibleProxy == NULL);
  4095. ASSERT(s_pfnLresultFromObject == NULL);
  4096. g_hOleAcc = LoadLibrary(TEXT("OLEACC"));
  4097. if (g_hOleAcc != NULL)
  4098. {
  4099. #ifdef UNICODE
  4100. s_pfnCreateStdAccessibleProxy = (PFNCREATESTDACCESSIBLEPROXY)
  4101. GetProcAddress(g_hOleAcc, "CreateStdAccessibleProxyW");
  4102. #else
  4103. s_pfnCreateStdAccessibleProxy = (PFNCREATESTDACCESSIBLEPROXY)
  4104. GetProcAddress(g_hOleAcc, "CreateStdAccessibleProxyA");
  4105. #endif
  4106. s_pfnLresultFromObject = (PFNLRESULTFROMOBJECT)
  4107. GetProcAddress(g_hOleAcc, "LresultFromObject");
  4108. }
  4109. if (s_pfnLresultFromObject == NULL || s_pfnCreateStdAccessibleProxy == NULL)
  4110. {
  4111. if (g_hOleAcc)
  4112. {
  4113. // No point holding on to Oleacc since we can't use it.
  4114. FreeLibrary(g_hOleAcc);
  4115. g_hOleAcc = NULL;
  4116. }
  4117. s_pfnLresultFromObject = NULL;
  4118. s_pfnCreateStdAccessibleProxy = NULL;
  4119. }
  4120. }
  4121. if (g_hOleAcc && s_pfnCreateStdAccessibleProxy && s_pfnLresultFromObject)
  4122. {
  4123. IAccessible *pAcc = NULL;
  4124. HRESULT hr;
  4125. // Create default slider proxy.
  4126. hr = s_pfnCreateStdAccessibleProxy(
  4127. hwndSlider,
  4128. TEXT("msctls_trackbar32"),
  4129. OBJID_CLIENT,
  4130. IID_IAccessible,
  4131. (void **)&pAcc
  4132. );
  4133. if (SUCCEEDED(hr) && pAcc)
  4134. {
  4135. // now wrap it up in our customized wrapper...
  4136. IAccessible * pWrapAcc = new CAccessibleWrapper( hwndSlider, pAcc );
  4137. // Release our ref to proxy (wrapper has its own addref'd ptr)...
  4138. pAcc->Release();
  4139. if (pWrapAcc != NULL)
  4140. {
  4141. // ...and return the wrapper via LresultFromObject...
  4142. LRESULT lr = s_pfnLresultFromObject( IID_IAccessible, wParam, pWrapAcc );
  4143. // Release our interface pointer - OLEACC has its own addref to the object
  4144. pWrapAcc->Release();
  4145. // Return the lresult, which 'contains' a reference to our wrapper object.
  4146. return lr;
  4147. // All done!
  4148. }
  4149. // If it didn't work, fall through to default behavior instead.
  4150. }
  4151. }
  4152. }
  4153. break;
  4154. case WM_DESTROY:
  4155. RemoveWindowSubclass(hwndSlider, SliderSubWndProc, uID);
  4156. break;
  4157. } /* end switch */
  4158. return DefSubclassProc(hwndSlider, uMsg, wParam, lParam);
  4159. }
  4160. BOOL CSettingsPage::_AreExtraMonitorsDisabledOnPersonal(void)
  4161. {
  4162. BOOL fIsDisabled = IsOS(OS_PERSONAL);
  4163. if (fIsDisabled)
  4164. {
  4165. // POSSIBLE FUTURE REFINEMENT: Insert call to ClassicSystemParametersInfo() to see if there are video cards that we had to disable.
  4166. fIsDisabled = FALSE;
  4167. }
  4168. return fIsDisabled;
  4169. }
  4170. // *** IShellPropSheetExt ***
  4171. HRESULT CSettingsPage::AddPages(IN LPFNSVADDPROPSHEETPAGE pfnAddPage, IN LPARAM lParam)
  4172. {
  4173. HRESULT hr = S_OK;
  4174. PROPSHEETPAGE psp = {0};
  4175. psp.dwSize = sizeof(psp);
  4176. psp.hInstance = HINST_THISDLL;
  4177. psp.dwFlags = PSP_DEFAULT;
  4178. psp.lParam = (LPARAM) this;
  4179. // GetSystemMetics(SM_CMONITORS) returns only the enabled monitors. So, we need to
  4180. // enumerate ourselves to determine if this is a multimonitor scenario. We have our own
  4181. // function to do this.
  4182. // Use the appropriate dlg template for multimonitor and single monitor configs.
  4183. // if(ClassicGetSystemMetrics(SM_CMONITORS) > 1)
  4184. //
  4185. // PERF-WARNING: calling EnumDisplaySettingsEx() is a huge perf hit, so see if we can
  4186. // findout if there is only one adapter with a cheaper call.
  4187. DEBUG_CODE(DebugStartWatch());
  4188. if (!_AreExtraMonitorsDisabledOnPersonal() && (ComputeNumberOfMonitorsFast(TRUE) > 1))
  4189. {
  4190. psp.pszTemplate = MAKEINTRESOURCE(DLG_MULTIMONITOR);
  4191. }
  4192. else
  4193. {
  4194. psp.pszTemplate = MAKEINTRESOURCE(DLG_SINGLEMONITOR);
  4195. }
  4196. DEBUG_CODE(TraceMsg(TF_THEMEUI_PERF, "CSettingsPage::AddPages() took Time=%lums", DebugStopWatch()));
  4197. psp.pfnDlgProc = CSettingsPage::SettingsDlgProc;
  4198. HPROPSHEETPAGE hpsp = CreatePropertySheetPage(&psp);
  4199. if (hpsp)
  4200. {
  4201. if (pfnAddPage(hpsp, lParam))
  4202. {
  4203. hr = S_OK;
  4204. }
  4205. else
  4206. {
  4207. DestroyPropertySheetPage(hpsp);
  4208. hr = E_FAIL;
  4209. }
  4210. }
  4211. else
  4212. {
  4213. hr = E_OUTOFMEMORY;
  4214. }
  4215. return hr;
  4216. }
  4217. HRESULT CSettingsPage::ReplacePage(IN EXPPS uPageID, IN LPFNSVADDPROPSHEETPAGE pfnReplaceWith, IN LPARAM lParam)
  4218. {
  4219. return E_NOTIMPL;
  4220. }
  4221. // *** IObjectWithSite ***
  4222. HRESULT CSettingsPage::SetSite(IN IUnknown * punkSite)
  4223. {
  4224. if (_pThemeUI)
  4225. {
  4226. _pThemeUI->Release();
  4227. _pThemeUI = NULL;
  4228. }
  4229. if (punkSite)
  4230. {
  4231. punkSite->QueryInterface(IID_PPV_ARG(IThemeUIPages, &_pThemeUI));
  4232. }
  4233. return CObjectWithSite::SetSite(punkSite);
  4234. }
  4235. // *** IPropertyBag ***
  4236. HRESULT CSettingsPage::Read(IN LPCOLESTR pszPropName, IN VARIANT * pVar, IN IErrorLog *pErrorLog)
  4237. {
  4238. HRESULT hr = E_INVALIDARG;
  4239. return hr;
  4240. }
  4241. HRESULT CSettingsPage::Write(IN LPCOLESTR pszPropName, IN VARIANT *pVar)
  4242. {
  4243. HRESULT hr = E_INVALIDARG;
  4244. return hr;
  4245. }
  4246. // *** IBasePropPage ***
  4247. HRESULT CSettingsPage::GetAdvancedDialog(OUT IAdvancedDialog ** ppAdvDialog)
  4248. {
  4249. if (ppAdvDialog)
  4250. {
  4251. *ppAdvDialog = NULL;
  4252. }
  4253. return E_NOTIMPL;
  4254. }
  4255. HRESULT CSettingsPage::OnApply(IN PROPPAGEONAPPLY oaAction)
  4256. {
  4257. if (IsDirty() && !_nInApply)
  4258. {
  4259. int status;
  4260. _nInApply++;
  4261. // Apply the settings, and enable\disable the Apply button
  4262. // appropriatly.
  4263. CDisplaySettings *rgpds[MONITORS_MAX];
  4264. ULONG iDevice;
  4265. for (iDevice = 0; iDevice < _NumDevices; iDevice++) {
  4266. rgpds[iDevice] = _Devices[iDevice].pds;
  4267. }
  4268. status = _DisplaySaveSettings(rgpds, _NumDevices, _hDlg);
  4269. SetDirty(status < 0);
  4270. if (status == DISP_CHANGE_RESTART)
  4271. {
  4272. PropSheet_RestartWindows(ghwndPropSheet);
  4273. }
  4274. else if (_pCurDevice && (status == DISP_CHANGE_SUCCESSFUL))
  4275. {
  4276. UINT iDevice;
  4277. TCHAR szDeviceName[32];
  4278. ASSERT(sizeof(szDeviceName) >=
  4279. sizeof(_pCurDevice->DisplayDevice.DeviceName));
  4280. lstrcpy(szDeviceName, _pCurDevice->DisplayDevice.DeviceName);
  4281. _InitDisplaySettings(FALSE);
  4282. for (iDevice = 0; iDevice < _NumDevices; iDevice++)
  4283. {
  4284. if (lstrcmp(_Devices[iDevice].DisplayDevice.DeviceName, szDeviceName) == 0)
  4285. {
  4286. UpdateActiveDisplay(_Devices + iDevice);
  4287. break;
  4288. }
  4289. }
  4290. }
  4291. else
  4292. {
  4293. // Make sure the dialog stays and redraw
  4294. _InitDisplaySettings(FALSE);
  4295. UpdateActiveDisplay(NULL);
  4296. SetWindowLongPtr(_hDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
  4297. }
  4298. _nInApply--;
  4299. }
  4300. return S_OK;
  4301. }
  4302. HRESULT CSettingsPage_CreateInstance(IN IUnknown * punkOuter, IN REFIID riid, OUT LPVOID * ppvObj)
  4303. {
  4304. HRESULT hr = E_INVALIDARG;
  4305. if (!punkOuter && ppvObj)
  4306. {
  4307. CSettingsPage * pThis = new CSettingsPage();
  4308. *ppvObj = NULL;
  4309. if (pThis)
  4310. {
  4311. hr = pThis->QueryInterface(riid, ppvObj);
  4312. pThis->Release();
  4313. }
  4314. else
  4315. {
  4316. hr = E_OUTOFMEMORY;
  4317. }
  4318. }
  4319. return hr;
  4320. }
  4321. VOID CheckForDuplicateAppletExtensions(HKEY hkDriver)
  4322. {
  4323. DWORD dwCheckForDuplicates = 0, cb = sizeof(DWORD), Len = 0;
  4324. HKEY hkExtensions = (HKEY)INVALID_HANDLE_VALUE;
  4325. PAPPEXT pAppExtTemp = NULL, pAppExt = NULL;
  4326. PTCHAR pmszAppExt = NULL;
  4327. if (RegQueryValueEx(hkDriver,
  4328. TEXT("DeskCheckForDuplicates"),
  4329. NULL,
  4330. NULL,
  4331. (LPBYTE)(&dwCheckForDuplicates),
  4332. &cb) == ERROR_SUCCESS)
  4333. {
  4334. RegDeleteValue(hkDriver, TEXT("DeskCheckForDuplicates"));
  4335. }
  4336. if (dwCheckForDuplicates != 1)
  4337. return;
  4338. if (RegOpenKeyEx(hkDriver,
  4339. TEXT("Display\\shellex\\PropertySheetHandlers"),
  4340. 0,
  4341. KEY_READ,
  4342. &hkExtensions) != ERROR_SUCCESS)
  4343. {
  4344. hkExtensions = (HKEY)INVALID_HANDLE_VALUE;
  4345. goto Fallout;
  4346. }
  4347. DeskAESnapshot(hkExtensions, &pAppExt);
  4348. if (pAppExt != NULL)
  4349. {
  4350. pAppExtTemp = pAppExt;
  4351. Len = 0;
  4352. while (pAppExtTemp)
  4353. {
  4354. Len += lstrlen(pAppExtTemp->szDefaultValue) + 1;
  4355. pAppExtTemp = pAppExtTemp->pNext;
  4356. }
  4357. pmszAppExt = (PTCHAR)LocalAlloc(LPTR, (Len + 1) * sizeof(TCHAR));
  4358. if (pmszAppExt != NULL)
  4359. {
  4360. pAppExtTemp = pAppExt;
  4361. Len = 0;
  4362. while (pAppExtTemp)
  4363. {
  4364. lstrcpy(pmszAppExt + Len, pAppExtTemp->szDefaultValue);
  4365. Len += lstrlen(pAppExtTemp->szDefaultValue) + 1;
  4366. pAppExtTemp = pAppExtTemp->pNext;
  4367. }
  4368. DeskAEDelete(REGSTR_PATH_CONTROLSFOLDER TEXT("\\Display\\shellex\\PropertySheetHandlers"),
  4369. pmszAppExt);
  4370. DeskAEDelete(REGSTR_PATH_CONTROLSFOLDER TEXT("\\Device\\shellex\\PropertySheetHandlers"),
  4371. pmszAppExt);
  4372. LocalFree(pmszAppExt);
  4373. }
  4374. DeskAECleanup(pAppExt);
  4375. }
  4376. Fallout:
  4377. if (hkExtensions != INVALID_HANDLE_VALUE)
  4378. {
  4379. RegCloseKey(hkExtensions);
  4380. }
  4381. }
  4382. VOID
  4383. DeskAESnapshot(
  4384. HKEY hkExtensions,
  4385. PAPPEXT* ppAppExt
  4386. )
  4387. {
  4388. HKEY hkSubkey = 0;
  4389. DWORD index = 0;
  4390. DWORD ulSize = MAX_PATH;
  4391. APPEXT AppExtTemp;
  4392. PAPPEXT pAppExtBefore = NULL;
  4393. PAPPEXT pAppExtTemp = NULL;
  4394. ulSize = sizeof(AppExtTemp.szKeyName) / sizeof(TCHAR);
  4395. while (RegEnumKeyEx(hkExtensions,
  4396. index,
  4397. AppExtTemp.szKeyName,
  4398. &ulSize,
  4399. NULL,
  4400. NULL,
  4401. NULL,
  4402. NULL) == ERROR_SUCCESS) {
  4403. if (RegOpenKeyEx(hkExtensions,
  4404. AppExtTemp.szKeyName,
  4405. 0,
  4406. KEY_READ,
  4407. &hkSubkey) == ERROR_SUCCESS) {
  4408. ulSize = sizeof(AppExtTemp.szDefaultValue);
  4409. if ((RegQueryValueEx(hkSubkey,
  4410. NULL,
  4411. 0,
  4412. NULL,
  4413. (PBYTE)AppExtTemp.szDefaultValue,
  4414. &ulSize) == ERROR_SUCCESS) &&
  4415. (AppExtTemp.szDefaultValue[0] != TEXT('\0'))) {
  4416. PAPPEXT pAppExt = (PAPPEXT)LocalAlloc(LPTR, sizeof(APPEXT));
  4417. if (pAppExt != NULL) {
  4418. *pAppExt = AppExtTemp;
  4419. pAppExtBefore = pAppExtTemp = *ppAppExt;
  4420. while((pAppExtTemp != NULL) &&
  4421. (lstrcmpi(pAppExtTemp->szDefaultValue,
  4422. pAppExt->szDefaultValue) < 0)) {
  4423. pAppExtBefore = pAppExtTemp;
  4424. pAppExtTemp = pAppExtTemp->pNext;
  4425. }
  4426. if (pAppExtBefore != pAppExtTemp) {
  4427. pAppExt->pNext = pAppExtBefore->pNext;
  4428. pAppExtBefore->pNext = pAppExt;
  4429. } else {
  4430. pAppExt->pNext = *ppAppExt;
  4431. *ppAppExt = pAppExt;
  4432. }
  4433. }
  4434. }
  4435. RegCloseKey(hkSubkey);
  4436. }
  4437. ulSize = sizeof(AppExtTemp.szKeyName) / sizeof(TCHAR);
  4438. index++;
  4439. }
  4440. }
  4441. VOID
  4442. DeskAEDelete(
  4443. PTCHAR szDeleteFrom,
  4444. PTCHAR mszExtensionsToRemove
  4445. )
  4446. {
  4447. TCHAR szKeyName[MAX_PATH];
  4448. HKEY hkDeleteFrom, hkExt;
  4449. DWORD cSubKeys = 0, cbSize = 0;
  4450. TCHAR szDefaultValue[MAX_PATH];
  4451. PTCHAR szValue;
  4452. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  4453. szDeleteFrom,
  4454. 0,
  4455. KEY_ALL_ACCESS,
  4456. &hkDeleteFrom) == ERROR_SUCCESS) {
  4457. if (RegQueryInfoKey(hkDeleteFrom,
  4458. NULL,
  4459. NULL,
  4460. NULL,
  4461. &cSubKeys,
  4462. NULL,
  4463. NULL,
  4464. NULL,
  4465. NULL,
  4466. NULL,
  4467. NULL,
  4468. NULL) == ERROR_SUCCESS) {
  4469. while (cSubKeys--) {
  4470. if (RegEnumKey(hkDeleteFrom,
  4471. cSubKeys,
  4472. szKeyName,
  4473. ARRAYSIZE(szKeyName)) == ERROR_SUCCESS) {
  4474. int iComp = -1;
  4475. if (RegOpenKeyEx(hkDeleteFrom,
  4476. szKeyName,
  4477. 0,
  4478. KEY_READ,
  4479. &hkExt) == ERROR_SUCCESS) {
  4480. cbSize = sizeof(szDefaultValue);
  4481. if ((RegQueryValueEx(hkExt,
  4482. NULL,
  4483. 0,
  4484. NULL,
  4485. (PBYTE)szDefaultValue,
  4486. &cbSize) == ERROR_SUCCESS) &&
  4487. (szDefaultValue[0] != TEXT('\0'))) {
  4488. szValue = mszExtensionsToRemove;
  4489. while (*szValue != TEXT('\0')) {
  4490. iComp = lstrcmpi(szDefaultValue, szValue);
  4491. if (iComp <= 0) {
  4492. break;
  4493. }
  4494. while (*szValue != TEXT('\0'))
  4495. szValue++;
  4496. szValue++;
  4497. }
  4498. }
  4499. RegCloseKey(hkExt);
  4500. }
  4501. if (iComp == 0) {
  4502. SHDeleteKey(hkDeleteFrom, szKeyName);
  4503. }
  4504. }
  4505. }
  4506. }
  4507. RegCloseKey(hkDeleteFrom);
  4508. }
  4509. }
  4510. VOID
  4511. DeskAECleanup(
  4512. PAPPEXT pAppExt
  4513. )
  4514. {
  4515. PAPPEXT pAppExtTemp;
  4516. while (pAppExt) {
  4517. pAppExtTemp = pAppExt->pNext;
  4518. LocalFree(pAppExt);
  4519. pAppExt = pAppExtTemp;
  4520. }
  4521. }