multimon.pp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2009 by the Free Pascal development team
  4. member of the Free Pascal development team.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. //=============================================================================
  12. //
  13. // multimon.h -- Stub module that fakes multiple monitor apis on Win32 OSes
  14. // without them.
  15. //
  16. // By using this header your code will get back default values from
  17. // GetSystemMetrics() for new metrics, and the new multimonitor APIs
  18. // will act like only one display is present on a Win32 OS without
  19. // multimonitor APIs.
  20. //
  21. // Exactly one source must include this with COMPILE_MULTIMON_STUBS defined.
  22. //
  23. // Copyright (c) Microsoft Corporation. All rights reserved.
  24. //
  25. //=============================================================================
  26. unit MultiMon;
  27. {$mode objfpc}{$H+}
  28. {$ifdef FPC_OS_UNICODE}
  29. {$define UNICODE}
  30. {$endif}
  31. interface
  32. uses
  33. Windows;
  34. //
  35. // If we are building with Win95/NT4 headers, we need to declare
  36. // the multimonitor-related metrics and APIs ourselves.
  37. //
  38. const
  39. SM_XVIRTUALSCREEN = 76;
  40. SM_YVIRTUALSCREEN = 77;
  41. SM_CXVIRTUALSCREEN = 78;
  42. SM_CYVIRTUALSCREEN = 79;
  43. SM_CMONITORS = 80;
  44. SM_SAMEDISPLAYFORMAT = 81;
  45. // HMONITOR is already declared if WINVER >= 0x0500 in windef.h
  46. // This is for components built with an older version number.
  47. type
  48. HMONITOR = Windows.HMonitor;
  49. const
  50. MONITOR_DEFAULTTONULL = $00000000;
  51. MONITOR_DEFAULTTOPRIMARY = $00000001;
  52. MONITOR_DEFAULTTONEAREST = $00000002;
  53. MONITORINFOF_PRIMARY = $00000001;
  54. type
  55. tagMONITORINFO = record
  56. cbSize: DWORD;
  57. rcMonitor: TRect;
  58. rcWork: TRect;
  59. dwFlags: DWORD;
  60. end;
  61. MONITORINFO = tagMONITORINFO;
  62. LPMONITORINFO = ^tagMONITORINFO;
  63. TMonitorInfo = MONITORINFO;
  64. PMonitorInfo = LPMONITORINFO;
  65. const
  66. CCHDEVICENAME = 32;
  67. type
  68. tagMONITORINFOEXA = record
  69. cbSize: DWORD;
  70. rcMonitor: TRect;
  71. rcWork: TRect;
  72. dwFlags: DWORD;
  73. szDevice: array[0..CCHDEVICENAME - 1] of Char;
  74. end;
  75. MONITORINFOEXA = tagMONITORINFOEXA;
  76. LPMONITORINFOEXA = ^tagMONITORINFOEXA;
  77. TMonitorInfoExA = MONITORINFOEXA;
  78. PMonitorInfoExA = LPMONITORINFOEXA;
  79. tagMONITORINFOEXW = record
  80. cbSize: DWORD;
  81. rcMonitor: TRect;
  82. rcWork: TRect;
  83. dwFlags: DWORD;
  84. szDevice: array[0..CCHDEVICENAME - 1] of WideChar;
  85. end;
  86. MONITORINFOEXW = tagMONITORINFOEXW;
  87. LPMONITORINFOEXW = ^tagMONITORINFOEXW;
  88. TMonitorInfoExW = MONITORINFOEXW;
  89. PMonitorInfoExW = LPMONITORINFOEXW;
  90. {$ifdef UNICODE}
  91. MONITORINFOEX = MONITORINFOEXW;
  92. LPMONITORINFOEX = LPMONITORINFOEXW;
  93. TMonitorInfoEx = MONITORINFOEXW;
  94. PMonitorInfoEx = LPMONITORINFOEXW;
  95. {$else}
  96. MONITORINFOEX = MONITORINFOEXA;
  97. LPMONITORINFOEX = LPMONITORINFOEXA;
  98. TMonitorInfoEx = MONITORINFOEXA;
  99. PMonitorInfoEx = LPMONITORINFOEXA;
  100. {$endif}
  101. type
  102. TMonitorEnumProc = function(hMonitor: HMONITOR; hdcMonitor: HDC; lprcMonitor: PRect;
  103. dwData: LPARAM): BOOL; stdcall;
  104. // ifndef DISPLAY_DEVICE_ATTACHED_TO_DESKTOP
  105. type
  106. _DISPLAY_DEVICEA = record
  107. cb: DWORD;
  108. DeviceName: array[0..31] of Char;
  109. DeviceString: array[0..127] of Char;
  110. StateFlags: DWORD;
  111. DeviceID: array[0..127] of Char;
  112. DeviceKey: array[0..127] of Char;
  113. end;
  114. DISPLAY_DEVICEA = _DISPLAY_DEVICEA;
  115. PDISPLAY_DEVICEA = ^_DISPLAY_DEVICEA;
  116. LPDISPLAY_DEVICEA = ^_DISPLAY_DEVICEA;
  117. TDisplayDeviceA = DISPLAY_DEVICEA;
  118. PDisplayDeviceA = PDISPLAY_DEVICEA;
  119. _DISPLAY_DEVICEW = record
  120. cb: DWORD;
  121. DeviceName: array[0..31] of WideChar;
  122. DeviceString: array[0..127] of WideChar;
  123. StateFlags: DWORD;
  124. DeviceID: array[0..127] of WideChar;
  125. DeviceKey: array[0..127] of WideChar;
  126. end;
  127. DISPLAY_DEVICEW = _DISPLAY_DEVICEW;
  128. PDISPLAY_DEVICEW = ^_DISPLAY_DEVICEW;
  129. LPDISPLAY_DEVICEW = ^_DISPLAY_DEVICEW;
  130. TDisplayDeviceW = DISPLAY_DEVICEW;
  131. PDisplayDeviceW = PDISPLAY_DEVICEW;
  132. {$ifdef UNICODE}
  133. DISPLAY_DEVICE = DISPLAY_DEVICEW;
  134. PDISPLAY_DEVICE = PDISPLAY_DEVICEW;
  135. LPDISPLAY_DEVICE = LPDISPLAY_DEVICEW;
  136. TDisplayDevice = TDisplayDeviceW;
  137. PDisplayDevice = PDisplayDeviceW;
  138. {$else}
  139. DISPLAY_DEVICE = DISPLAY_DEVICEA;
  140. PDISPLAY_DEVICE = PDISPLAY_DEVICEA;
  141. LPDISPLAY_DEVICE = LPDISPLAY_DEVICEA;
  142. TDisplayDevice = TDisplayDeviceA;
  143. PDisplayDevice = PDisplayDeviceA;
  144. {$endif} // UNICODE
  145. const
  146. DISPLAY_DEVICE_ATTACHED_TO_DESKTOP = $00000001;
  147. DISPLAY_DEVICE_MULTI_DRIVER = $00000002;
  148. DISPLAY_DEVICE_PRIMARY_DEVICE = $00000004;
  149. DISPLAY_DEVICE_MIRRORING_DRIVER = $00000008;
  150. DISPLAY_DEVICE_VGA_COMPATIBLE = $00000010;
  151. type
  152. TGetSystemMetrics = function(nIndex: longint): longint; stdcall;
  153. TMonitorFromWindow = function(hWnd: HWND; dwFlags: DWORD): HMONITOR; stdcall;
  154. TMonitorFromRect = function(lprcScreenCoords: PRect; dwFlags: DWord): HMONITOR; stdcall;
  155. TMonitorFromPoint = function(ptScreenCoords: TPoint; dwFlags: DWord): HMONITOR; stdcall;
  156. TGetMonitorInfo = function(hMonitor: HMONITOR; lpmi: PMonitorInfo): BOOL; stdcall;
  157. TEnumDisplayMonitors = function(hdc: HDC; lprcClip: PRect; lpfnEnum: TMonitorEnumProc; dwData: LPARAM): BOOL; stdcall;
  158. TEnumDisplayDevices = function(lpDevice: Pointer; iDevNum: DWORD; lpDisplayDevice: PDisplayDevice; dwFlags: DWORD): BOOL; stdcall;
  159. var
  160. GetSystemMetrics: TGetSystemMetrics;
  161. MonitorFromWindow: TMonitorFromWindow;
  162. MonitorFromRect: TMonitorFromRect;
  163. MonitorFromPoint: TMonitorFromPoint;
  164. GetMonitorInfo: TGetMonitorInfo;
  165. EnumDisplayMonitors: TEnumDisplayMonitors;
  166. EnumDisplayDevices: TEnumDisplayDevices;
  167. implementation
  168. var
  169. g_fMultiMonInitDone: Boolean = False;
  170. g_pfnGetSystemMetrics: TGetSystemMetrics = nil;
  171. g_pfnMonitorFromWindow: TMonitorFromWindow = nil;
  172. g_pfnMonitorFromRect: TMonitorFromRect = nil;
  173. g_pfnMonitorFromPoint: TMonitorFromPoint = nil;
  174. g_pfnGetMonitorInfo: TGetMonitorInfo = nil;
  175. g_pfnEnumDisplayMonitors: TEnumDisplayMonitors = nil;
  176. g_pfnEnumDisplayDevices: TEnumDisplayDevices = nil;
  177. function IsPlatformNT: Boolean;
  178. var
  179. osvi: TOSVersionInfo;
  180. begin
  181. {$HINTS OFF}
  182. FillChar(osvi, SizeOf(osvi), 0);
  183. {$HINTS ON}
  184. osvi.dwOSVersionInfoSize := sizeof(osvi);
  185. GetVersionExA(@osvi);
  186. Result := VER_PLATFORM_WIN32_NT = osvi.dwPlatformId;
  187. end;
  188. function InitMultipleMonitorStubs: Boolean;
  189. var
  190. hUser32: HMODULE;
  191. begin
  192. if g_fMultiMonInitDone then
  193. Exit(@g_pfnGetMonitorInfo <> nil);
  194. hUser32 := GetModuleHandle('USER32');
  195. if hUser32 <> 0 then
  196. begin
  197. Pointer(g_pfnGetSystemMetrics) := GetProcAddress(hUser32, 'GetSystemMetrics');
  198. Pointer(g_pfnMonitorFromWindow) := GetProcAddress(hUser32, 'MonitorFromWindow');
  199. Pointer(g_pfnMonitorFromRect) := GetProcAddress(hUser32, 'MonitorFromRect');
  200. Pointer(g_pfnMonitorFromPoint) := GetProcAddress(hUser32, 'MonitorFromPoint');
  201. Pointer(g_pfnEnumDisplayMonitors) := GetProcAddress(hUser32, 'EnumDisplayMonitors');
  202. {$ifdef UNICODE}
  203. Pointer(g_pfnEnumDisplayDevices) := GetProcAddress(hUser32, 'EnumDisplayDevicesW');
  204. if IsPlatformNT then
  205. Pointer(g_pfnGetMonitorInfo) := GetProcAddress(hUser32, 'GetMonitorInfoW')
  206. else
  207. Pointer(g_pfnGetMonitorInfo) := GetProcAddress(hUser32, 'GetMonitorInfoA');
  208. {$else}
  209. Pointer(g_pfnGetMonitorInfo) := GetProcAddress(hUser32, 'GetMonitorInfoA');
  210. Pointer(g_pfnEnumDisplayDevices) := GetProcAddress(hUser32, 'EnumDisplayDevicesA');
  211. {$endif}
  212. g_fMultiMonInitDone := True;
  213. Result := True;
  214. end
  215. else
  216. begin
  217. Pointer(g_pfnGetSystemMetrics) := nil;
  218. Pointer(g_pfnMonitorFromWindow) := nil;
  219. Pointer(g_pfnMonitorFromRect) := nil;
  220. Pointer(g_pfnMonitorFromPoint) := nil;
  221. Pointer(g_pfnEnumDisplayMonitors) := nil;
  222. Pointer(g_pfnGetMonitorInfo) := nil;
  223. Pointer(g_pfnEnumDisplayDevices) := nil;
  224. g_fMultiMonInitDone := True;
  225. Result := False;
  226. end;
  227. end;
  228. //-----------------------------------------------------------------------------
  229. //
  230. // fake implementations of Monitor APIs that work with the primary display
  231. // no special parameter validation is made since these run in client code
  232. //
  233. //-----------------------------------------------------------------------------
  234. function xGetSystemMetrics(nIndex: Integer): Integer; stdcall;
  235. begin
  236. if (InitMultipleMonitorStubs()) then
  237. Exit(g_pfnGetSystemMetrics(nIndex));
  238. case nIndex of
  239. SM_CMONITORS,
  240. SM_SAMEDISPLAYFORMAT:
  241. Exit(1);
  242. SM_XVIRTUALSCREEN,
  243. SM_YVIRTUALSCREEN:
  244. Exit(0);
  245. SM_CXVIRTUALSCREEN:
  246. nIndex := SM_CXSCREEN;
  247. SM_CYVIRTUALSCREEN:
  248. nIndex := SM_CYSCREEN;
  249. end;
  250. Result := GetSystemMetrics(nIndex);
  251. end;
  252. const
  253. xPRIMARY_MONITOR = HMONITOR($12340042);
  254. function xMonitorFromPoint(ptScreenCoords: TPoint; dwFlags: DWORD): HMONITOR; stdcall;
  255. begin
  256. if (InitMultipleMonitorStubs()) then
  257. Exit(g_pfnMonitorFromPoint(ptScreenCoords, dwFlags));
  258. if ((dwFlags and (MONITOR_DEFAULTTOPRIMARY or MONITOR_DEFAULTTONEAREST) <> 0 ) or
  259. ((ptScreenCoords.x >= 0) and
  260. (ptScreenCoords.x < GetSystemMetrics(SM_CXSCREEN)) and
  261. (ptScreenCoords.y >= 0) and
  262. (ptScreenCoords.y < GetSystemMetrics(SM_CYSCREEN)))) then
  263. Result := xPRIMARY_MONITOR
  264. else
  265. Result := 0;
  266. end;
  267. function xMonitorFromRect(lprcScreenCoords: PRect; dwFlags: DWORD): HMONITOR; stdcall;
  268. begin
  269. if (InitMultipleMonitorStubs()) then
  270. Exit(g_pfnMonitorFromRect(lprcScreenCoords, dwFlags));
  271. if ((dwFlags and (MONITOR_DEFAULTTOPRIMARY or MONITOR_DEFAULTTONEAREST) <> 0) or
  272. ((lprcScreenCoords^.right > 0) and
  273. (lprcScreenCoords^.bottom > 0) and
  274. (lprcScreenCoords^.left < GetSystemMetrics(SM_CXSCREEN)) and
  275. (lprcScreenCoords^.top < GetSystemMetrics(SM_CYSCREEN)))) then
  276. Result := xPRIMARY_MONITOR
  277. else
  278. Result := 0;
  279. end;
  280. function xMonitorFromWindow(hWnd: HWND; dwFlags: DWORD): HMONITOR; stdcall;
  281. var
  282. wp: TWindowPlacement;
  283. B: Boolean;
  284. begin
  285. if (InitMultipleMonitorStubs()) then
  286. Exit(g_pfnMonitorFromWindow(hWnd, dwFlags));
  287. if (dwFlags and (MONITOR_DEFAULTTOPRIMARY or MONITOR_DEFAULTTONEAREST) <> 0) then
  288. Exit(xPRIMARY_MONITOR);
  289. if IsIconic(hWnd) then
  290. B := GetWindowPlacement(hWnd, @wp)
  291. else
  292. B := GetWindowRect(hWnd, @wp.rcNormalPosition);
  293. if B then
  294. Result := xMonitorFromRect(@wp.rcNormalPosition, dwFlags)
  295. else
  296. Result := 0;
  297. end;
  298. function xGetMonitorInfo(hMonitor: HMONITOR; lpMonitorInfo: PMonitorInfo): BOOL; stdcall;
  299. var
  300. rcWork: TRect;
  301. f: BOOL;
  302. begin
  303. if (InitMultipleMonitorStubs()) then
  304. begin
  305. f := g_pfnGetMonitorInfo(hMonitor, lpMonitorInfo);
  306. {$ifdef UNICODE}
  307. if (f and not IsPlatformNT and (lpMonitorInfo^.cbSize >= sizeof(TMonitorInfoEx))) then
  308. begin
  309. MultiByteToWideChar(CP_ACP, 0,
  310. LPCSTR(@PMonitorInfoEx(lpMonitorInfo)^.szDevice[0]), -1,
  311. @PMonitorInfoEx(lpMonitorInfo)^.szDevice[0], CCHDEVICENAME);
  312. end;
  313. {$endif}
  314. Exit(f);
  315. end;
  316. if ((hMonitor = xPRIMARY_MONITOR) and
  317. (lpMonitorInfo <> nil) and
  318. (lpMonitorInfo^.cbSize >= sizeof(TMonitorInfo)) and
  319. SystemParametersInfo(SPI_GETWORKAREA, 0, @rcWork, 0)) then
  320. begin
  321. lpMonitorInfo^.rcMonitor.left := 0;
  322. lpMonitorInfo^.rcMonitor.top := 0;
  323. lpMonitorInfo^.rcMonitor.right := GetSystemMetrics(SM_CXSCREEN);
  324. lpMonitorInfo^.rcMonitor.bottom := GetSystemMetrics(SM_CYSCREEN);
  325. lpMonitorInfo^.rcWork := rcWork;
  326. lpMonitorInfo^.dwFlags := MONITORINFOF_PRIMARY;
  327. if (lpMonitorInfo^.cbSize >= sizeof(TMonitorInfoEx)) then
  328. PMonitorInfoEx(lpMonitorInfo)^.szDevice := 'DISPLAY';
  329. Result := True;
  330. end
  331. else
  332. Result := False;
  333. end;
  334. function xEnumDisplayMonitors(hdcOptionalForPainting: HDC; lprcEnumMonitorsThatIntersect: PRect;
  335. lpfnEnumProc: TMonitorEnumProc; dwData: LPARAM): BOOL; stdcall;
  336. var
  337. rcLimit, rcClip: TRect;
  338. ptOrg: TPoint;
  339. Cb: Integer;
  340. begin
  341. if (InitMultipleMonitorStubs()) then
  342. Exit(g_pfnEnumDisplayMonitors(
  343. hdcOptionalForPainting,
  344. lprcEnumMonitorsThatIntersect,
  345. lpfnEnumProc,
  346. dwData));
  347. if (lpfnEnumProc = nil) then
  348. Exit(False);
  349. rcLimit.left := 0;
  350. rcLimit.top := 0;
  351. rcLimit.right := GetSystemMetrics(SM_CXSCREEN);
  352. rcLimit.bottom := GetSystemMetrics(SM_CYSCREEN);
  353. if (hdcOptionalForPainting <> 0) then
  354. begin
  355. Cb := GetClipBox(hdcOptionalForPainting, @rcClip);
  356. if not GetDCOrgEx(hdcOptionalForPainting, @ptOrg) then
  357. Exit(False);
  358. OffsetRect(rcLimit, -ptOrg.x, -ptOrg.y);
  359. if (IntersectRect(rcLimit, rcLimit, rcClip) and
  360. ((lprcEnumMonitorsThatIntersect = nil) or
  361. IntersectRect(rcLimit, rcLimit, lprcEnumMonitorsThatIntersect^))) then
  362. begin
  363. if Cb = NULLREGION then
  364. Exit(True)
  365. else
  366. if Cb = ERROR then
  367. Exit(False);
  368. end
  369. end
  370. else
  371. if ((lprcEnumMonitorsThatIntersect <> nil) and
  372. not IntersectRect(rcLimit, rcLimit, lprcEnumMonitorsThatIntersect^)) then
  373. Exit(True);
  374. Result := lpfnEnumProc(
  375. xPRIMARY_MONITOR,
  376. hdcOptionalForPainting,
  377. @rcLimit,
  378. dwData);
  379. end;
  380. function xEnumDisplayDevices(Unused: Pointer; iDevNum: DWORD; lpDisplayDevice: PDisplayDevice;
  381. dwFlags: DWORD): BOOL; stdcall;
  382. begin
  383. if (InitMultipleMonitorStubs()) then
  384. Exit(g_pfnEnumDisplayDevices(Unused, iDevNum, lpDisplayDevice, dwFlags));
  385. if (Unused <> nil) then
  386. Exit(False);
  387. if (iDevNum <> 0) then
  388. Exit(False);
  389. if (lpDisplayDevice = nil) or (lpDisplayDevice^.cb < sizeof(TDisplayDevice)) then
  390. Exit(False);
  391. lpDisplayDevice^.DeviceName := 'DISPLAY';
  392. lpDisplayDevice^.DeviceString := 'DISPLAY';
  393. lpDisplayDevice^.StateFlags := DISPLAY_DEVICE_ATTACHED_TO_DESKTOP or DISPLAY_DEVICE_PRIMARY_DEVICE;
  394. Result := True;
  395. end;
  396. initialization
  397. Pointer(GetSystemMetrics) := @xGetSystemMetrics;
  398. Pointer(MonitorFromWindow) := @xMonitorFromWindow;
  399. Pointer(MonitorFromRect) := @xMonitorFromRect;
  400. Pointer(MonitorFromPoint) := @xMonitorFromPoint;
  401. Pointer(GetMonitorInfo) := @xGetMonitorInfo;
  402. Pointer(EnumDisplayMonitors) := @xEnumDisplayMonitors;
  403. Pointer(EnumDisplayDevices) := @xEnumDisplayDevices;
  404. end.