PickMonitor.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #if 0 // TODO: implement proper monitor picking with latest non-carbon API.
  21. #include "../../idlib/precompiled.h"
  22. #include <Carbon/Carbon.h>
  23. #include "PickMonitor.h"
  24. //====================================================================================
  25. // CONSTANTS
  26. //====================================================================================
  27. #define kMaxMonitors 16
  28. //====================================================================================
  29. // TYPES
  30. //====================================================================================
  31. typedef struct
  32. {
  33. GDHandle device;
  34. Rect origRect;
  35. Rect scaledRect;
  36. int isMain;
  37. }
  38. Monitor;
  39. //====================================================================================
  40. // GLOBALS
  41. //====================================================================================
  42. static GDHandle sSelectedDevice;
  43. static int sNumMonitors;
  44. static Monitor sMonitors[kMaxMonitors];
  45. static RGBColor rgbBlack = { 0x0000, 0x0000, 0x0000 };
  46. static RGBColor rgbWhite = { 0xffff, 0xffff, 0xffff };
  47. static RGBColor rgbGray = { 0x5252, 0x8A8A, 0xCCCC }; // this is the blue used in the Displays control panel
  48. //====================================================================================
  49. // MACROS
  50. //====================================================================================
  51. #undef PtInRect
  52. #undef OffsetRect
  53. #undef InsetRect
  54. #undef EraseRect
  55. #undef MoveTo
  56. #undef LineTo
  57. //====================================================================================
  58. // IMPLEMENTATION
  59. //====================================================================================
  60. //-----------------------------------------------------------------------------
  61. // SetupUserPaneProcs
  62. //-----------------------------------------------------------------------------
  63. // Call this to initialize the specified user pane control before displaying
  64. // the dialog window. Pass NULL for any user pane procs you don't need to install.
  65. OSErr SetupUserPaneProcs( ControlRef inUserPane,
  66. ControlUserPaneDrawProcPtr inDrawProc,
  67. ControlUserPaneHitTestProcPtr inHitTestProc,
  68. ControlUserPaneTrackingProcPtr inTrackingProc)
  69. {
  70. OSErr err = noErr;
  71. ControlUserPaneDrawUPP drawUPP;
  72. ControlUserPaneHitTestUPP hitTestUPP;
  73. ControlUserPaneTrackingUPP trackingUPP;
  74. if (0 == inUserPane) return paramErr;
  75. if (inDrawProc && noErr == err)
  76. {
  77. drawUPP = NewControlUserPaneDrawUPP(inDrawProc);
  78. if (0 == drawUPP)
  79. err = memFullErr;
  80. else
  81. err = SetControlData( inUserPane,
  82. kControlEntireControl,
  83. kControlUserPaneDrawProcTag,
  84. sizeof(ControlUserPaneDrawUPP),
  85. (Ptr)&drawUPP);
  86. }
  87. if (inHitTestProc && noErr == err)
  88. {
  89. hitTestUPP = NewControlUserPaneHitTestUPP(inHitTestProc);
  90. if (0 == hitTestUPP)
  91. err = memFullErr;
  92. else
  93. err = SetControlData( inUserPane,
  94. kControlEntireControl,
  95. kControlUserPaneHitTestProcTag,
  96. sizeof(ControlUserPaneHitTestUPP),
  97. (Ptr)&hitTestUPP);
  98. }
  99. if (inTrackingProc && noErr == err)
  100. {
  101. trackingUPP = NewControlUserPaneTrackingUPP(inTrackingProc);
  102. if (0 == trackingUPP)
  103. err = memFullErr;
  104. else
  105. err = SetControlData( inUserPane,
  106. kControlEntireControl,
  107. kControlUserPaneTrackingProcTag,
  108. sizeof(ControlUserPaneTrackingUPP),
  109. (Ptr)&trackingUPP);
  110. }
  111. return err;
  112. }
  113. //-----------------------------------------------------------------------------
  114. // DisposeUserPaneProcs
  115. //-----------------------------------------------------------------------------
  116. // Call this to clean up when you're done with the specified user pane control.
  117. OSErr DisposeUserPaneProcs(ControlRef inUserPane)
  118. {
  119. ControlUserPaneDrawUPP drawUPP;
  120. ControlUserPaneHitTestUPP hitTestUPP;
  121. ControlUserPaneTrackingUPP trackingUPP;
  122. Size actualSize;
  123. OSErr err;
  124. err = GetControlData(inUserPane, kControlEntireControl, kControlUserPaneDrawProcTag, sizeof(ControlUserPaneDrawUPP), (Ptr)&drawUPP, &actualSize);
  125. if (err == noErr) DisposeControlUserPaneDrawUPP(drawUPP);
  126. err = GetControlData(inUserPane, kControlEntireControl, kControlUserPaneHitTestProcTag, sizeof(ControlUserPaneHitTestUPP), (Ptr)&hitTestUPP, &actualSize);
  127. if (err == noErr) DisposeControlUserPaneHitTestUPP(hitTestUPP);
  128. err = GetControlData(inUserPane, kControlEntireControl, kControlUserPaneTrackingProcTag, sizeof(ControlUserPaneTrackingUPP), (Ptr)&trackingUPP, &actualSize);
  129. if (err == noErr) DisposeControlUserPaneTrackingUPP(trackingUPP);
  130. return noErr;
  131. }
  132. #pragma mark -
  133. //-----------------------------------------------------------------------------
  134. // drawProc
  135. //-----------------------------------------------------------------------------
  136. // Custom drawProc for our UserPane control.
  137. static pascal void drawProc(ControlRef inControl, SInt16 inPart)
  138. {
  139. #pragma unused(inControl, inPart)
  140. int i;
  141. RGBColor saveForeColor;
  142. RGBColor saveBackColor;
  143. PenState savePenState;
  144. GetForeColor(&saveForeColor);
  145. GetBackColor(&saveBackColor);
  146. GetPenState(&savePenState);
  147. RGBForeColor(&rgbBlack);
  148. RGBBackColor(&rgbWhite);
  149. PenNormal();
  150. for (i = 0; i < sNumMonitors; i++)
  151. {
  152. RGBForeColor(&rgbGray);
  153. PaintRect(&sMonitors[i].scaledRect);
  154. if (sMonitors[i].isMain)
  155. {
  156. Rect r = sMonitors[i].scaledRect;
  157. InsetRect(&r, 1, 1);
  158. r.bottom = r.top + 6;
  159. RGBForeColor(&rgbWhite);
  160. PaintRect(&r);
  161. RGBForeColor(&rgbBlack);
  162. PenSize(1,1);
  163. MoveTo(r.left, r.bottom);
  164. LineTo(r.right, r.bottom);
  165. }
  166. if (sMonitors[i].device == sSelectedDevice)
  167. {
  168. PenSize(3,3);
  169. RGBForeColor(&rgbBlack);
  170. FrameRect(&sMonitors[i].scaledRect);
  171. }
  172. else
  173. {
  174. PenSize(1,1);
  175. RGBForeColor(&rgbBlack);
  176. FrameRect(&sMonitors[i].scaledRect);
  177. }
  178. }
  179. // restore the original pen state and colors
  180. RGBForeColor(&saveForeColor);
  181. RGBBackColor(&saveBackColor);
  182. SetPenState(&savePenState);
  183. }
  184. //-----------------------------------------------------------------------------
  185. // hitTestProc
  186. //-----------------------------------------------------------------------------
  187. // Custom hitTestProc for our UserPane control.
  188. // This allows FindControlUnderMouse() to locate our control, which allows
  189. // ModalDialog() to call TrackControl() or HandleControlClick() for our control.
  190. static pascal ControlPartCode hitTestProc(ControlRef inControl, Point inWhere)
  191. {
  192. // return a valid part code so HandleControlClick() will be called
  193. return kControlButtonPart;
  194. }
  195. //-----------------------------------------------------------------------------
  196. // trackingProc
  197. //-----------------------------------------------------------------------------
  198. // Custom trackingProc for our UserPane control.
  199. // This won't be called for our control unless the kControlHandlesTracking feature
  200. // bit is specified when the userPane is created.
  201. static pascal ControlPartCode trackingProc (
  202. ControlRef inControl,
  203. Point inStartPt,
  204. ControlActionUPP inActionProc)
  205. {
  206. #pragma unused (inControl, inStartPt, inActionProc)
  207. int i;
  208. for (i = 0; i < sNumMonitors; i++)
  209. {
  210. if (PtInRect(inStartPt, &sMonitors[i].scaledRect))
  211. {
  212. if (sMonitors[i].device != sSelectedDevice)
  213. {
  214. sSelectedDevice = sMonitors[i].device;
  215. DrawOneControl(inControl);
  216. }
  217. break;
  218. }
  219. }
  220. return kControlNoPart;
  221. }
  222. #pragma mark -
  223. //-----------------------------------------------------------------------------
  224. // SetupPickMonitorPane
  225. //-----------------------------------------------------------------------------
  226. // Call this to initialize the user pane control that is the Pick Monitor
  227. // control. Pass the ControlRef of the user pane control and a display ID
  228. // for the monitor you want selected by default (pass 0 for the main monitor).
  229. // Call this function before displaying the dialog window.
  230. OSErr SetupPickMonitorPane(ControlRef inPane, DisplayIDType inDefaultMonitor)
  231. {
  232. GDHandle dev = GetDeviceList();
  233. OSErr err = noErr;
  234. // make the default monitor the selected device
  235. if (inDefaultMonitor)
  236. DMGetGDeviceByDisplayID(inDefaultMonitor, &sSelectedDevice, true);
  237. else
  238. sSelectedDevice = GetMainDevice();
  239. // build the list of monitors
  240. sNumMonitors = 0;
  241. while (dev && sNumMonitors < kMaxMonitors)
  242. {
  243. if (TestDeviceAttribute(dev, screenDevice) && TestDeviceAttribute(dev, screenActive))
  244. {
  245. sMonitors[sNumMonitors].device = dev;
  246. sMonitors[sNumMonitors].origRect = (**dev).gdRect;
  247. sMonitors[sNumMonitors].isMain = (dev == GetMainDevice());
  248. sNumMonitors++;
  249. }
  250. dev = GetNextDevice(dev);
  251. }
  252. // calculate scaled rects
  253. if (sNumMonitors)
  254. {
  255. Rect origPaneRect, paneRect;
  256. Rect origGrayRect, grayRect, scaledGrayRect;
  257. float srcAspect, dstAspect, scale;
  258. int i;
  259. GetControlBounds(inPane, &origPaneRect);
  260. paneRect = origPaneRect;
  261. OffsetRect(&paneRect, -paneRect.left, -paneRect.top);
  262. GetRegionBounds(GetGrayRgn(), &origGrayRect);
  263. grayRect = origGrayRect;
  264. OffsetRect(&grayRect, -grayRect.left, -grayRect.top);
  265. srcAspect = (float)grayRect.right / (float)grayRect.bottom;
  266. dstAspect = (float)paneRect.right / (float)paneRect.bottom;
  267. scaledGrayRect = paneRect;
  268. if (srcAspect < dstAspect)
  269. {
  270. scaledGrayRect.right = (float)paneRect.bottom * srcAspect;
  271. scale = (float)scaledGrayRect.right / grayRect.right;
  272. }
  273. else
  274. {
  275. scaledGrayRect.bottom = (float)paneRect.right / srcAspect;
  276. scale = (float)scaledGrayRect.bottom / grayRect.bottom;
  277. }
  278. for (i = 0; i < sNumMonitors; i++)
  279. {
  280. Rect r = sMonitors[i].origRect;
  281. Rect r2 = r;
  282. // normalize rect and scale
  283. OffsetRect(&r, -r.left, -r.top);
  284. r.bottom = (float)r.bottom * scale;
  285. r.right = (float)r.right * scale;
  286. // offset rect wrt gray region
  287. OffsetRect(&r, (float)(r2.left - origGrayRect.left) * scale,
  288. (float)(r2.top - origGrayRect.top) * scale);
  289. sMonitors[i].scaledRect = r;
  290. }
  291. // center scaledGrayRect in the pane
  292. OffsetRect(&scaledGrayRect, (paneRect.right - scaledGrayRect.right) / 2,
  293. (paneRect.bottom - scaledGrayRect.bottom) / 2);
  294. // offset monitors to match
  295. for (i = 0; i < sNumMonitors; i++)
  296. OffsetRect(&sMonitors[i].scaledRect, scaledGrayRect.left, scaledGrayRect.top);
  297. }
  298. else
  299. return paramErr;
  300. // setup the procs for the pick monitor user pane
  301. err = SetupUserPaneProcs(inPane, drawProc, hitTestProc, trackingProc);
  302. return err;
  303. }
  304. //-----------------------------------------------------------------------------
  305. // TearDownPickMonitorPane
  306. //-----------------------------------------------------------------------------
  307. // Disposes of everything associated with the Pick Monitor pane. You should
  308. // call this when disposing the dialog.
  309. OSErr TearDownPickMonitorPane(ControlRef inPane)
  310. {
  311. OSErr err;
  312. err = DisposeUserPaneProcs(inPane);
  313. sNumMonitors = 0;
  314. return err;
  315. }
  316. #pragma mark -
  317. //------------------------------------------------------------------------------------
  318. // ¥ PickMonitorHandler
  319. //------------------------------------------------------------------------------------
  320. // Our command handler for the PickMonitor dialog.
  321. static pascal OSStatus PickMonitorHandler( EventHandlerCallRef inHandler, EventRef inEvent, void* inUserData )
  322. {
  323. #pragma unused( inHandler )
  324. HICommand cmd;
  325. OSStatus result = eventNotHandledErr;
  326. WindowRef theWindow = (WindowRef)inUserData;
  327. // The direct object for a 'process commmand' event is the HICommand.
  328. // Extract it here and switch off the command ID.
  329. GetEventParameter( inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof( cmd ), NULL, &cmd );
  330. switch ( cmd.commandID )
  331. {
  332. case kHICommandOK:
  333. QuitAppModalLoopForWindow( theWindow );
  334. result = noErr;
  335. break;
  336. case kHICommandCancel:
  337. // Setting sSelectedDevice to zero will signal that the user cancelled.
  338. sSelectedDevice = 0;
  339. QuitAppModalLoopForWindow( theWindow );
  340. result = noErr;
  341. break;
  342. }
  343. return result;
  344. }
  345. #pragma mark -
  346. //-----------------------------------------------------------------------------
  347. // CanUserPickMonitor
  348. //-----------------------------------------------------------------------------
  349. // Returns true if more than one monitor is available to choose from.
  350. Boolean CanUserPickMonitor (void)
  351. {
  352. GDHandle dev = GetDeviceList();
  353. OSErr err = noErr;
  354. int numMonitors;
  355. // build the list of monitors
  356. numMonitors = 0;
  357. while (dev && numMonitors < kMaxMonitors)
  358. {
  359. if (TestDeviceAttribute(dev, screenDevice) && TestDeviceAttribute(dev, screenActive))
  360. {
  361. numMonitors++;
  362. }
  363. dev = GetNextDevice(dev);
  364. }
  365. if (numMonitors > 1) return true;
  366. else return false;
  367. }
  368. //-----------------------------------------------------------------------------
  369. // PickMonitor
  370. //-----------------------------------------------------------------------------
  371. // Prompts for a monitor. Returns userCanceledErr if the user cancelled.
  372. OSStatus PickMonitor (DisplayIDType *inOutDisplayID, WindowRef parentWindow)
  373. {
  374. WindowRef theWindow;
  375. OSStatus status = noErr;
  376. static const ControlID kUserPane = { 'MONI', 1 };
  377. // Fetch the dialog
  378. IBNibRef aslNib;
  379. CFBundleRef theBundle = CFBundleGetMainBundle();
  380. status = CreateNibReferenceWithCFBundle(theBundle, CFSTR("ASLCore"), &aslNib);
  381. status = ::CreateWindowFromNib(aslNib, CFSTR( "Pick Monitor" ), &theWindow );
  382. if (status != noErr)
  383. {
  384. assert(false);
  385. return userCanceledErr;
  386. }
  387. #if 0
  388. // Put game name in window title. By default the title includes the token <<<kGameName>>>.
  389. Str255 windowTitle;
  390. GetWTitle(theWindow, windowTitle);
  391. FormatPStringWithGameName(windowTitle);
  392. SetWTitle(theWindow, windowTitle);
  393. #endif
  394. // Set up the controls
  395. ControlRef monitorPane;
  396. GetControlByID( theWindow, &kUserPane, &monitorPane );
  397. assert(monitorPane);
  398. SetupPickMonitorPane(monitorPane, *inOutDisplayID);
  399. // Create our UPP and install the handler.
  400. EventTypeSpec cmdEvent = { kEventClassCommand, kEventCommandProcess };
  401. EventHandlerUPP handler = NewEventHandlerUPP( PickMonitorHandler );
  402. InstallWindowEventHandler( theWindow, handler, 1, &cmdEvent, theWindow, NULL );
  403. // Show the window
  404. if (parentWindow)
  405. ShowSheetWindow( theWindow, parentWindow );
  406. else
  407. ShowWindow( theWindow );
  408. // Now we run modally. We will remain here until the PrefHandler
  409. // calls QuitAppModalLoopForWindow if the user clicks OK or
  410. // Cancel.
  411. RunAppModalLoopForWindow( theWindow );
  412. // OK, we're done. Dispose of our window and our UPP.
  413. // We do the UPP last because DisposeWindow can send out
  414. // CarbonEvents, and we haven't explicitly removed our
  415. // handler. If we disposed the UPP, the Toolbox might try
  416. // to call it. That would be bad.
  417. TearDownPickMonitorPane(monitorPane);
  418. if (parentWindow)
  419. HideSheetWindow( theWindow );
  420. DisposeWindow( theWindow );
  421. DisposeEventHandlerUPP( handler );
  422. // Return settings to caller
  423. if (sSelectedDevice != 0)
  424. {
  425. // Read back the controls
  426. DMGetDisplayIDByGDevice (sSelectedDevice, &*inOutDisplayID, true);
  427. return noErr;
  428. }
  429. else
  430. return userCanceledErr;
  431. }
  432. #endif