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.

337 lines
9.3 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. KeepWindowOnMonitor.cpp
  5. Abstract:
  6. Do not allow a window to be placed off the Monitor.
  7. History:
  8. 04/24/2001 robkenny Created
  9. 09/10/2001 robkenny Made shim more generic.
  10. --*/
  11. #include "precomp.h"
  12. IMPLEMENT_SHIM_BEGIN(KeepWindowOnMonitor)
  13. #include "ShimHookMacro.h"
  14. APIHOOK_ENUM_BEGIN
  15. APIHOOK_ENUM_ENTRY(SetWindowPos)
  16. APIHOOK_ENUM_ENTRY(MoveWindow)
  17. APIHOOK_ENUM_ENTRY(CreateWindowA)
  18. APIHOOK_ENUM_ENTRY(CreateWindowExA)
  19. APIHOOK_ENUM_END
  20. /*++
  21. Are these two RECTs equal
  22. --*/
  23. BOOL operator == (const RECT & rc1, const RECT & rc2)
  24. {
  25. return rc1.left == rc2.left &&
  26. rc1.right == rc2.right &&
  27. rc1.top == rc2.top &&
  28. rc1.bottom == rc2.bottom;
  29. }
  30. /*++
  31. Are these two RECTs different
  32. --*/
  33. BOOL operator != (const RECT & rc1, const RECT & rc2)
  34. {
  35. return ! (rc1 == rc2);
  36. }
  37. /*++
  38. Is rcWindow entirely visible on rcMonitor
  39. --*/
  40. BOOL EntirelyVisible(const RECT & rcWindow, const RECT & rcMonitor)
  41. {
  42. return rcWindow.left >= rcMonitor.left &&
  43. rcWindow.right <= rcMonitor.right &&
  44. rcWindow.top >= rcMonitor.top &&
  45. rcWindow.bottom <= rcMonitor.bottom;
  46. }
  47. #define MONITOR_CENTER 0x0001 // center rect to monitor
  48. #define MONITOR_CLIP 0x0000 // clip rect to monitor
  49. #define MONITOR_WORKAREA 0x0002 // use monitor work area
  50. #define MONITOR_AREA 0x0000 // use monitor entire area
  51. //
  52. // ClipOrCenterRectToMonitor
  53. //
  54. // The most common problem apps have when running on a
  55. // multimonitor system is that they "clip" or "pin" windows
  56. // based on the SM_CXSCREEN and SM_CYSCREEN system metrics.
  57. // Because of app compatibility reasons these system metrics
  58. // return the size of the primary monitor.
  59. //
  60. // This shows how you use the new Win32 multimonitor APIs
  61. // to do the same thing.
  62. //
  63. BOOL ClipOrCenterRectToMonitor(
  64. LPRECT prcWindowPos,
  65. UINT flags)
  66. {
  67. HMONITOR hMonitor;
  68. MONITORINFO mi;
  69. RECT rcMonitorRect;
  70. int w = prcWindowPos->right - prcWindowPos->left;
  71. int h = prcWindowPos->bottom - prcWindowPos->top;
  72. //
  73. // get the nearest monitor to the passed rect.
  74. //
  75. hMonitor = MonitorFromRect(prcWindowPos, MONITOR_DEFAULTTONEAREST);
  76. //
  77. // get the work area or entire monitor rect.
  78. //
  79. mi.cbSize = sizeof(mi);
  80. if ( !GetMonitorInfo(hMonitor, &mi) )
  81. {
  82. return FALSE;
  83. }
  84. if (flags & MONITOR_WORKAREA)
  85. rcMonitorRect = mi.rcWork;
  86. else
  87. rcMonitorRect = mi.rcMonitor;
  88. // We only want to move the window if it is not entirely visible.
  89. if (EntirelyVisible(*prcWindowPos, rcMonitorRect))
  90. {
  91. return FALSE;
  92. }
  93. //
  94. // center or clip the passed rect to the monitor rect
  95. //
  96. if (flags & MONITOR_CENTER)
  97. {
  98. prcWindowPos->left = rcMonitorRect.left + (rcMonitorRect.right - rcMonitorRect.left - w) / 2;
  99. prcWindowPos->top = rcMonitorRect.top + (rcMonitorRect.bottom - rcMonitorRect.top - h) / 2;
  100. prcWindowPos->right = prcWindowPos->left + w;
  101. prcWindowPos->bottom = prcWindowPos->top + h;
  102. }
  103. else
  104. {
  105. prcWindowPos->left = max(rcMonitorRect.left, min(rcMonitorRect.right-w, prcWindowPos->left));
  106. prcWindowPos->top = max(rcMonitorRect.top, min(rcMonitorRect.bottom-h, prcWindowPos->top));
  107. prcWindowPos->right = prcWindowPos->left + w;
  108. prcWindowPos->bottom = prcWindowPos->top + h;
  109. }
  110. return TRUE;
  111. }
  112. /*++
  113. If hwnd is not entirely visible on a single monitor,
  114. move/resize the window as necessary.
  115. --*/
  116. void ClipOrCenterWindowToMonitor(
  117. HWND hwnd,
  118. HWND hWndParent,
  119. UINT flags,
  120. const char * API)
  121. {
  122. // We only want to forcibly move top-level windows
  123. if (hWndParent == NULL || hWndParent == GetDesktopWindow())
  124. {
  125. // Grab the current position of the window
  126. RECT rcWindowPos;
  127. if ( GetWindowRect(hwnd, &rcWindowPos) )
  128. {
  129. RECT rcOrigWindowPos = rcWindowPos;
  130. // Calculate the new position of the window, based on flags
  131. if ( ClipOrCenterRectToMonitor(&rcWindowPos, flags) )
  132. {
  133. if (rcWindowPos != rcOrigWindowPos)
  134. {
  135. DPFN( eDbgLevelInfo, "[%s] HWnd(0x08x) OrigWindowRect (%d, %d) x (%d, %d) moved to (%d, %d) x (%d, %d)\n",
  136. API, hwnd,
  137. rcOrigWindowPos.left, rcOrigWindowPos.top, rcOrigWindowPos.right, rcOrigWindowPos.bottom,
  138. rcWindowPos.left, rcWindowPos.top, rcWindowPos.right, rcWindowPos.bottom);
  139. SetWindowPos(hwnd, NULL, rcWindowPos.left, rcWindowPos.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  140. }
  141. }
  142. }
  143. }
  144. }
  145. /*++
  146. Call SetWindowPos,
  147. but if the window is not entirely visible,
  148. the window will be centered on the nearest monitor.
  149. --*/
  150. BOOL
  151. APIHOOK(SetWindowPos)(
  152. HWND hWnd, // handle to window
  153. HWND hWndInsertAfter, // placement-order handle
  154. int X, // horizontal position
  155. int Y, // vertical position
  156. int cx, // width
  157. int cy, // height
  158. UINT uFlags // window-positioning options
  159. )
  160. {
  161. BOOL bReturn = ORIGINAL_API(SetWindowPos)(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags);
  162. ClipOrCenterWindowToMonitor(hWnd, GetParent(hWnd), MONITOR_CENTER | MONITOR_WORKAREA, "SetWindowPos");
  163. return bReturn;
  164. }
  165. /*++
  166. Call MoveWindow,
  167. but if the window is not entirely visible,
  168. the window will be centered on the nearest monitor.
  169. --*/
  170. BOOL
  171. APIHOOK(MoveWindow)(
  172. HWND hWnd, // handle to window
  173. int X, // horizontal position
  174. int Y, // vertical position
  175. int nWidth, // width
  176. int nHeight, // height
  177. BOOL bRepaint // repaint option
  178. )
  179. {
  180. BOOL bReturn = ORIGINAL_API(MoveWindow)(hWnd, X, Y, nWidth, nHeight, bRepaint);
  181. ClipOrCenterWindowToMonitor(hWnd, GetParent(hWnd), MONITOR_CENTER | MONITOR_WORKAREA, "MoveWindow");
  182. return bReturn;
  183. }
  184. /*++
  185. Call CreateWindowA,
  186. but if the window is not entirely visible,
  187. the window will be centered on the nearest monitor.
  188. --*/
  189. HWND
  190. APIHOOK(CreateWindowA)(
  191. LPCSTR lpClassName, // registered class name
  192. LPCSTR lpWindowName, // window name
  193. DWORD dwStyle, // window style
  194. int x, // horizontal position of window
  195. int y, // vertical position of window
  196. int nWidth, // window width
  197. int nHeight, // window height
  198. HWND hWndParent, // handle to parent or owner window
  199. HMENU hMenu, // menu handle or child identifier
  200. HINSTANCE hInstance, // handle to application instance
  201. LPVOID lpParam // window-creation data
  202. )
  203. {
  204. HWND hWnd = ORIGINAL_API(CreateWindowA)(lpClassName,
  205. lpWindowName,
  206. dwStyle,
  207. x,
  208. y,
  209. nWidth,
  210. nHeight,
  211. hWndParent,
  212. hMenu,
  213. hInstance,
  214. lpParam);
  215. if (hWnd)
  216. {
  217. ClipOrCenterWindowToMonitor(hWnd, hWndParent, MONITOR_CENTER | MONITOR_WORKAREA, "CreateWindowA");
  218. }
  219. return hWnd;
  220. }
  221. /*++
  222. Call CreateWindowExA,
  223. but if the window is not entirely visible,
  224. the window will be centered on the nearest monitor.
  225. --*/
  226. HWND
  227. APIHOOK(CreateWindowExA)(
  228. DWORD dwExStyle, // extended window style
  229. LPCSTR lpClassName, // registered class name
  230. LPCSTR lpWindowName, // window name
  231. DWORD dwStyle, // window style
  232. int x, // horizontal position of window
  233. int y, // vertical position of window
  234. int nWidth, // window width
  235. int nHeight, // window height
  236. HWND hWndParent, // handle to parent or owner window
  237. HMENU hMenu, // menu handle or child identifier
  238. HINSTANCE hInstance, // handle to application instance
  239. LPVOID lpParam // window-creation data
  240. )
  241. {
  242. HWND hWnd = ORIGINAL_API(CreateWindowExA)(dwExStyle,
  243. lpClassName,
  244. lpWindowName,
  245. dwStyle,
  246. x,
  247. y,
  248. nWidth,
  249. nHeight,
  250. hWndParent,
  251. hMenu,
  252. hInstance,
  253. lpParam);
  254. if (hWnd)
  255. {
  256. ClipOrCenterWindowToMonitor(hWnd, hWndParent, MONITOR_CENTER | MONITOR_WORKAREA, "CreateWindowExA");
  257. }
  258. return hWnd;
  259. }
  260. /*++
  261. Register hooked functions
  262. --*/
  263. HOOK_BEGIN
  264. APIHOOK_ENTRY(USER32.DLL, SetWindowPos)
  265. APIHOOK_ENTRY(USER32.DLL, MoveWindow)
  266. APIHOOK_ENTRY(USER32.DLL, CreateWindowA)
  267. APIHOOK_ENTRY(USER32.DLL, CreateWindowExA)
  268. HOOK_END
  269. IMPLEMENT_SHIM_END