multimon.pp 14 KB

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