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.

452 lines
12 KiB

  1. #include "precomp.h"
  2. #ifndef DM_POSITION
  3. #define DM_POSITION 0x00000020L
  4. #endif
  5. //
  6. // OSI.CPP
  7. // OS Isolation layer, NT version
  8. //
  9. // Copyright(c) Microsoft 1997-
  10. //
  11. #include <version.h>
  12. #include <ndcgver.h>
  13. #include <osi.h>
  14. #define MLZ_FILE_ZONE ZONE_CORE
  15. //
  16. // NT 5.0 app sharing stuff.
  17. // NOTE:
  18. // The name for NetMeeting's display driver, s_szNmDD, comes from
  19. // mnmdd.sys. We have no define for it nor access to the source. DO NOT
  20. // CHANGE THIS without talking to Andre Vachon. Hopefully, he will perform
  21. // the same courtesy for us if he changes mnmdd.sys. If not, we can't share
  22. // anymore. We won't even find our driver to load.
  23. //
  24. typedef BOOL (WINAPI * FN_ENUMDD)(LPVOID, DWORD, LPDISPLAY_DEVICE, DWORD);
  25. static TCHAR s_szNmDD[] = "NetMeeting driver";
  26. //
  27. // OSI_Load()
  28. // This handles our process attach. We figure out if this is NT5 or not
  29. //
  30. void OSI_Load(void)
  31. {
  32. OSVERSIONINFO osVersion;
  33. ZeroMemory(&osVersion, sizeof(osVersion));
  34. osVersion.dwOSVersionInfoSize = sizeof(osVersion);
  35. GetVersionEx(&osVersion);
  36. ASSERT(osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT);
  37. if (osVersion.dwMajorVersion >= 5)
  38. g_asNT5 = TRUE;
  39. }
  40. //
  41. // OSI_Unload()
  42. // This handles our process detach. We currently do nothing.
  43. //
  44. void OSI_Unload(void)
  45. {
  46. return;
  47. }
  48. //
  49. // OSI_InitDriver50()
  50. //
  51. // Attemps to dynamically load/unload our display driver for NT 5.0. This is
  52. // called on the main thread, and if under a service, on the winlogon
  53. // thread also. It will only succeed on the input focus desktop.
  54. //
  55. void OSI_InitDriver50(BOOL fInit)
  56. {
  57. DWORD iEnum;
  58. DISPLAY_DEVICE dd;
  59. DEVMODE devmode;
  60. FN_ENUMDD pfnEnumDD;
  61. DebugEntry(OSI_InitDriver50);
  62. ASSERT(g_asNT5);
  63. pfnEnumDD = (FN_ENUMDD)GetProcAddress(GetModuleHandle("USER32.DLL"),
  64. "EnumDisplayDevicesA");
  65. if (pfnEnumDD != NULL)
  66. {
  67. ZeroMemory(&dd, sizeof(dd));
  68. dd.cb = sizeof(dd);
  69. iEnum = 0;
  70. while (pfnEnumDD(NULL, iEnum++, &dd, 0))
  71. {
  72. if ((dd.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) &&
  73. !lstrcmpi((LPCSTR)dd.DeviceString, s_szNmDD))
  74. {
  75. LONG lResult;
  76. //
  77. // There may be multiple monitors, drivers, etc.
  78. // We have to actually tell the system what bit depth,
  79. // format, etc. our driver wants just like if we were
  80. // a real driver. We therefore always ask to get 24bpp
  81. // format info, no myriad 16bpp and 32bpp formats to deal
  82. // with anymore.
  83. //
  84. // Also, no more 4bpp not VGA nonsense either--just 8 or 24.
  85. //
  86. ZeroMemory(&devmode, sizeof(devmode));
  87. devmode.dmSize = sizeof(devmode);
  88. devmode.dmFields = DM_POSITION | DM_BITSPERPEL | DM_PELSWIDTH |
  89. DM_PELSHEIGHT;
  90. if (fInit)
  91. {
  92. //
  93. // Fill in fields to get driver attached.
  94. //
  95. if (g_usrCaptureBPP <= 8)
  96. g_usrCaptureBPP = 8;
  97. else
  98. g_usrCaptureBPP = 24;
  99. devmode.dmBitsPerPel = g_usrCaptureBPP;
  100. // devmode.dmPosition is (0, 0), this means "primary"
  101. devmode.dmPelsWidth = GetSystemMetrics(SM_CXSCREEN);
  102. devmode.dmPelsHeight = GetSystemMetrics(SM_CYSCREEN);
  103. }
  104. //
  105. // Before we change the display settings in a multimonitor machine, we better move the cursor out of the way
  106. // If we do not do do we get an extra cursor in the middle of the primary monitor
  107. //
  108. ::SetCursorPos(-1, -1);
  109. //
  110. // This simply changes the state in the registry from
  111. // attached to unattached, without the system actually
  112. // reflecting the change. If/when we have multiple
  113. // listings for our shadow driver, move the CDS(NULL, 0)
  114. // call outside the loop, and get rid of the break.
  115. //
  116. lResult = ChangeDisplaySettingsEx((LPCSTR)dd.DeviceName, &devmode,
  117. NULL, CDS_UPDATEREGISTRY | CDS_NORESET, NULL);
  118. if (lResult != DISP_CHANGE_SUCCESSFUL)
  119. {
  120. WARNING_OUT(("ChangeDisplaySettingsEx failed, error %d", lResult));
  121. }
  122. else
  123. {
  124. //
  125. // This causes Windows to actually go reread the registry and
  126. // update the current display to reflect the attached items,
  127. // positions, sizes, and color depths.
  128. //
  129. ChangeDisplaySettings(NULL, 0);
  130. #ifdef _DEBUG
  131. if (fInit)
  132. {
  133. HDC hdc;
  134. //
  135. // Create a temp DC based on this driver and make sure
  136. // the settings matched what we asked for.
  137. //
  138. hdc = CreateDC(NULL, (LPCSTR)dd.DeviceName, NULL, NULL);
  139. if (!hdc)
  140. {
  141. WARNING_OUT(("OSI_Init: dynamic display driver load failed"));
  142. }
  143. else
  144. {
  145. ASSERT(GetDeviceCaps(hdc, HORZRES) == (int)devmode.dmPelsWidth);
  146. ASSERT(GetDeviceCaps(hdc, VERTRES) == (int)devmode.dmPelsHeight);
  147. ASSERT(GetDeviceCaps(hdc, BITSPIXEL) * GetDeviceCaps(hdc, PLANES) ==
  148. (int)g_usrCaptureBPP);
  149. DeleteDC(hdc);
  150. }
  151. }
  152. #endif // _DEBUG
  153. //
  154. // Tell MNMHOOK_ the name of our driver so it can talk
  155. // to it via ExtEscape.
  156. //
  157. OSI_SetDriverName(fInit ? (LPCSTR)dd.DeviceName : NULL);
  158. }
  159. break;
  160. }
  161. }
  162. }
  163. DebugExitVOID(OSI_InitDriver50);
  164. }
  165. //
  166. // OSI_Init - see osi.h
  167. //
  168. void OSI_Init(void)
  169. {
  170. UINT i;
  171. OSI_INIT_REQUEST requestBuffer;
  172. DebugEntry(OSI_Init);
  173. //
  174. // First, setup up pointer to shared data. This data lives here in NT.
  175. //
  176. #ifdef DEBUG
  177. g_imSharedData.cbSize = sizeof(g_imSharedData);
  178. #endif
  179. g_lpimSharedData = &g_imSharedData;
  180. requestBuffer.result = FALSE;
  181. requestBuffer.pSharedMemory = NULL;
  182. requestBuffer.poaData[0] = NULL;
  183. requestBuffer.poaData[1] = NULL;
  184. requestBuffer.sbcEnabled = FALSE;
  185. for (i = 0; i < SBC_NUM_TILE_SIZES; i++)
  186. {
  187. ASSERT(!g_asbcShuntBuffers[i]);
  188. requestBuffer.psbcTileData[i] = NULL;
  189. }
  190. //
  191. // Do this FIRST. On NT5, only threads on the desktop with input
  192. // can succeed at calling ChangeDisplaySettings. So like other things,
  193. // we must try to dynamically load/unload our driver on both desks.
  194. //
  195. //
  196. // Create the winlogon desktop event injection helper thread
  197. // only if we're started as a service. Note that it will try to
  198. // load display at start too.
  199. //
  200. ASSERT(!g_imNTData.imOtherDesktopThread);
  201. if (g_asOptions & AS_SERVICE)
  202. {
  203. WARNING_OUT(("Starting other desktop thread for SERVICE"));
  204. if (!DCS_StartThread(IMOtherDesktopProc))
  205. {
  206. WARNING_OUT(( "Failed to create other desktop IM thread"));
  207. DC_QUIT;
  208. }
  209. }
  210. //
  211. // DO THIS ONLY FOR NT5
  212. // We are going to enumerate all the entries for our shadow driver
  213. // (currently only one) and attach each to the actual display.
  214. //
  215. if (g_asNT5)
  216. {
  217. OSI_InitDriver50(TRUE);
  218. }
  219. DC_EXIT_POINT:
  220. g_osiInitialized = OSI_FunctionRequest(OSI_ESC_INIT, (LPOSI_ESCAPE_HEADER)&requestBuffer,
  221. sizeof(requestBuffer));
  222. if (!g_osiInitialized)
  223. {
  224. WARNING_OUT(("OSI_ESC_INIT: display driver not present"));
  225. }
  226. else
  227. {
  228. if (requestBuffer.result)
  229. {
  230. g_asCanHost = TRUE;
  231. //
  232. // Got shared memory pointers; keep them around
  233. //
  234. g_asSharedMemory = (LPSHM_SHARED_MEMORY)requestBuffer.pSharedMemory;
  235. ASSERT(g_asSharedMemory);
  236. g_poaData[0] = (LPOA_SHARED_DATA)requestBuffer.poaData[0];
  237. ASSERT(g_poaData[0]);
  238. g_poaData[1] = (LPOA_SHARED_DATA)requestBuffer.poaData[1];
  239. ASSERT(g_poaData[1]);
  240. g_sbcEnabled = requestBuffer.sbcEnabled;
  241. if (g_sbcEnabled)
  242. {
  243. //
  244. // Get shunt buffers
  245. //
  246. for (i = 0; i < SBC_NUM_TILE_SIZES; i++)
  247. {
  248. g_asbcShuntBuffers[i] = (LPSBC_SHUNT_BUFFER)requestBuffer.psbcTileData[i];
  249. ASSERT(g_asbcShuntBuffers[i]);
  250. TRACE_OUT(("OSI_Init: sbc shunt buffer %d: entries %08d, bytes 0x%08x",
  251. i, g_asbcShuntBuffers[i]->numEntries, g_asbcShuntBuffers[i]->numBytes));
  252. }
  253. for (i = 0; i < 3; i++)
  254. {
  255. g_asbcBitMasks[i] = requestBuffer.aBitmasks[i];
  256. }
  257. }
  258. }
  259. }
  260. if (g_asCanHost)
  261. {
  262. //
  263. // Tell HOOK dll (used for control as well as sharing) about our hwnd
  264. // and stuff. If we are able to host.
  265. //
  266. ASSERT(g_asMainWindow);
  267. ASSERT(g_asHostProp);
  268. HOOK_Init(g_asMainWindow, g_asHostProp);
  269. }
  270. DebugExitVOID(OSI_Init);
  271. }
  272. //
  273. // OSI_Term - see osi.h
  274. //
  275. void OSI_Term(void)
  276. {
  277. UINT i;
  278. DebugEntry(OSI_Term);
  279. //
  280. // This can be called on multiple threads:
  281. // * The main DCS thread
  282. // * The last thread of the process causing us to get a process
  283. // detach.
  284. // We call it in the latter case also to make sure we cleanup properly.
  285. //
  286. ASSERT(GetCurrentThreadId() == g_asMainThreadId);
  287. //
  288. // Kill the other desktop thread if it's around.
  289. //
  290. if (g_imNTData.imOtherDesktopThread != 0)
  291. {
  292. ASSERT(g_asOptions & AS_SERVICE);
  293. PostThreadMessage(g_imNTData.imOtherDesktopThread, WM_QUIT, 0, 0);
  294. while (g_imNTData.imOtherDesktopThread)
  295. {
  296. WARNING_OUT(("OSI_Term: waiting for other desktop thread to exit"));
  297. Sleep(1);
  298. }
  299. }
  300. if (g_osiInitialized)
  301. {
  302. OSI_TERM_REQUEST requestBuffer;
  303. g_osiInitialized = FALSE;
  304. //
  305. // We call the term routine only if the driver is actually loaded
  306. // (as opposed to whether something went wrong when trying to setup
  307. // for hosting) so that we will cleanup if something went wrong in
  308. // the middle.
  309. //
  310. OSI_FunctionRequest(OSI_ESC_TERM, (LPOSI_ESCAPE_HEADER)&requestBuffer,
  311. sizeof(requestBuffer));
  312. }
  313. //
  314. // ONLY DO THIS FOR NT5
  315. // We need to undo all the work we did at init time to attach our
  316. // driver(s) to the display, and detach them. Again, enumerate the
  317. // registry entries and look for ours.
  318. //
  319. //
  320. if (g_asNT5)
  321. {
  322. OSI_InitDriver50(FALSE);
  323. }
  324. // Clear our shared memory variables
  325. for (i = 0; i < 3; i++)
  326. {
  327. g_asbcBitMasks[i] = 0;
  328. }
  329. for (i = 0; i < SBC_NUM_TILE_SIZES; i++)
  330. {
  331. g_asbcShuntBuffers[i] = NULL;
  332. }
  333. g_sbcEnabled = FALSE;
  334. g_asSharedMemory = NULL;
  335. g_poaData[0] = NULL;
  336. g_poaData[1] = NULL;
  337. g_asCanHost = FALSE;
  338. g_lpimSharedData = NULL;
  339. DebugExitVOID(OSI_Term);
  340. }
  341. VOID OSI_RepaintDesktop(void)
  342. {
  343. DebugEntry(OSI_RepaintDesktop);
  344. // If this does not appear to be a window it may be a window on the
  345. // winlogon desktop, so we need to get the proxy thread to repaint it
  346. if ( g_imNTData.imOtherDesktopThread )
  347. {
  348. PostThreadMessage(g_imNTData.imOtherDesktopThread,
  349. OSI_WM_DESKTOPREPAINT, 0, 0);
  350. }
  351. DebugExitVOID(OSI_RepaintDesktop);
  352. }
  353. VOID OSI_SetGUIEffects(BOOL fOff)
  354. {
  355. DebugEntry(OSI_SetGUIEffects);
  356. if (g_imNTData.imOtherDesktopThread)
  357. {
  358. PostThreadMessage(g_imNTData.imOtherDesktopThread,
  359. OSI_WM_SETGUIEFFECTS, fOff, 0);
  360. }
  361. DebugExitVOID(OSI_SetGUIEffects);
  362. }