VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServiceDisplayConfig.cpp@ 105840

Last change on this file since 105840 was 105749, checked in by vboxsync, 4 months ago

GA/Windows: Use two separate bitmasks for displays to enable and disable. bugref:10714

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.2 KB
Line 
1/** @file
2 * VBoxService - Guest displays handling.
3 */
4
5/*
6 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
7 *
8 * This file is part of VirtualBox base platform packages, as
9 * available from https://www.virtualbox.org.
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation, in version 3 of the
14 * License.
15 *
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, see <https://www.gnu.org/licenses>.
23 *
24 * SPDX-License-Identifier: GPL-3.0-only
25 */
26
27#include <iprt/win/windows.h>
28#include <wtsapi32.h>
29
30#include <iprt/errcore.h>
31#include <iprt/system.h>
32
33#include <VBox/VBoxGuestLib.h>
34#include "VBoxServiceInternal.h"
35#include "VBoxServiceUtils.h"
36
37#undef NTDDI_VERSION
38#define NTDDI_VERSION NTDDI_LONGHORN
39#include <iprt/win/d3dkmthk.h>
40
41#define VBOX_WITH_WDDM
42#include <VBoxDisplay.h>
43
44
45/*********************************************************************************************************************************
46* Global Variables *
47*********************************************************************************************************************************/
48
49static PFND3DKMT_ENUMADAPTERS g_pfnD3DKMTEnumAdapters;
50static PFND3DKMT_OPENADAPTERFROMLUID g_pfnD3DKMTOpenAdapterFromLuid;
51static PFND3DKMT_CLOSEADAPTER g_pfnD3DKMTCloseAdapter;
52static PFND3DKMT_ESCAPE g_pfnD3DKMTEscape;
53
54static decltype(WTSFreeMemory) *g_pfnWTSFreeMemory = NULL;
55static decltype(WTSQuerySessionInformationA) *g_pfnWTSQuerySessionInformationA = NULL;
56static decltype(WTSEnumerateSessionsA) *g_pfnWTSEnumerateSessionsA = NULL;
57
58/**
59 * @interface_method_impl{VBOXSERVICE,pfnInit}
60 */
61static DECLCALLBACK(int) vgsvcDisplayConfigInit(void)
62{
63 if (RTSystemGetNtVersion() < RTSYSTEM_MAKE_NT_VERSION(6, 0, 0))
64 {
65 VGSvcVerbose(1, "displayconfig requires Windows Vista or later\n");
66 return VERR_SERVICE_DISABLED;
67 }
68
69 RTLDRMOD hLdrMod;
70 int rc = RTLdrLoadSystem("gdi32.dll", true /*fNoUnload*/, &hLdrMod);
71 if (RT_SUCCESS(rc))
72 {
73 rc = RTLdrGetSymbol(hLdrMod, "D3DKMTEnumAdapters", (void **)&g_pfnD3DKMTEnumAdapters);
74 if (RT_SUCCESS(rc))
75 rc = RTLdrGetSymbol(hLdrMod, "D3DKMTOpenAdapterFromLuid", (void **)&g_pfnD3DKMTOpenAdapterFromLuid);
76 if (RT_SUCCESS(rc))
77 rc = RTLdrGetSymbol(hLdrMod, "D3DKMTCloseAdapter", (void **)&g_pfnD3DKMTCloseAdapter);
78 if (RT_SUCCESS(rc))
79 rc = RTLdrGetSymbol(hLdrMod, "D3DKMTEscape", (void **)&g_pfnD3DKMTEscape);
80 AssertRC(rc);
81 RTLdrClose(hLdrMod);
82 }
83
84 if (RT_FAILURE(rc))
85 {
86 VGSvcVerbose(1, "d3dkmthk API is not available (%Rrc)\n", rc);
87 g_pfnD3DKMTEnumAdapters = NULL;
88 g_pfnD3DKMTOpenAdapterFromLuid = NULL;
89 g_pfnD3DKMTCloseAdapter = NULL;
90 g_pfnD3DKMTEscape = NULL;
91 return VERR_SERVICE_DISABLED;
92 }
93
94 rc = RTLdrLoadSystem("wtsapi32.dll", true /*fNoUnload*/, &hLdrMod);
95 if (RT_SUCCESS(rc))
96 {
97 rc = RTLdrGetSymbol(hLdrMod, "WTSFreeMemory", (void **)&g_pfnWTSFreeMemory);
98 if (RT_SUCCESS(rc))
99 rc = RTLdrGetSymbol(hLdrMod, "WTSQuerySessionInformationA", (void **)&g_pfnWTSQuerySessionInformationA);
100 if (RT_SUCCESS(rc))
101 rc = RTLdrGetSymbol(hLdrMod, "WTSEnumerateSessionsA", (void **)&g_pfnWTSEnumerateSessionsA);
102 AssertRC(rc);
103 RTLdrClose(hLdrMod);
104 }
105
106 if (RT_FAILURE(rc))
107 {
108 VGSvcVerbose(1, "WtsApi32.dll API is not available (%Rrc)\n", rc);
109 g_pfnWTSFreeMemory = NULL;
110 g_pfnWTSQuerySessionInformationA = NULL;
111 g_pfnWTSEnumerateSessionsA = NULL;
112 return VERR_SERVICE_DISABLED;
113 }
114
115 return VINF_SUCCESS;
116}
117
118void ReconnectDisplays(uint32_t cDisplays, VMMDevDisplayDef *paDisplays)
119{
120 D3DKMT_HANDLE hAdapter;
121 D3DKMT_ENUMADAPTERS EnumAdapters = {0};
122 NTSTATUS rcNt;
123 uint32_t u32ConnectMask = 0, u32DisconnectMask = 0;
124
125 for(uint32_t i = 0; i < cDisplays; i++)
126 {
127 uint32_t u32Mask = RT_BIT(paDisplays[i].idDisplay);
128
129 if (paDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED)
130 u32DisconnectMask |= u32Mask;
131 else
132 u32ConnectMask |= u32Mask;
133 }
134
135 VGSvcVerbose(3, "ReconnectDisplays u32ConnectMask 0x%RX32, u32DisconnectMask 0x%RX32\n", u32ConnectMask, u32DisconnectMask);
136
137 EnumAdapters.NumAdapters = RT_ELEMENTS(EnumAdapters.Adapters);
138 rcNt = g_pfnD3DKMTEnumAdapters(&EnumAdapters);
139
140 VGSvcVerbose(3, "D3DKMTEnumAdapters rcNt=%#x NumAdapters=%d\n", rcNt, EnumAdapters.NumAdapters);
141
142 for(ULONG id = 0; id < EnumAdapters.NumAdapters; id++)
143 {
144 D3DKMT_ADAPTERINFO *pAdapterInfo = &EnumAdapters.Adapters[id];
145 VGSvcVerbose(3, "#%d: NumOfSources=%d hAdapter=0x%p Luid(%u, %u)\n", id,
146 pAdapterInfo->NumOfSources, pAdapterInfo->hAdapter, pAdapterInfo->AdapterLuid.HighPart, pAdapterInfo->AdapterLuid.LowPart);
147 }
148
149 D3DKMT_OPENADAPTERFROMLUID OpenAdapterData;
150 RT_ZERO(OpenAdapterData);
151 OpenAdapterData.AdapterLuid = EnumAdapters.Adapters[0].AdapterLuid;
152 rcNt = g_pfnD3DKMTOpenAdapterFromLuid(&OpenAdapterData);
153 VGSvcVerbose(3, "D3DKMTOpenAdapterFromLuid rcNt=%#x hAdapter=0x%p\n", rcNt, OpenAdapterData.hAdapter);
154
155 hAdapter = OpenAdapterData.hAdapter;
156
157 if (hAdapter)
158 {
159 VBOXDISPIFESCAPE_RECONNECT_TARGETS VBoxEscapeReconnectTargets = {{0}};
160
161 VBoxEscapeReconnectTargets.EscapeHdr.escapeCode = VBOXESC_RECONNECT_TARGETS;
162 VBoxEscapeReconnectTargets.EscapeHdr.u32CmdSpecific = 0;
163 VBoxEscapeReconnectTargets.u32ConnectMask = u32ConnectMask;
164 VBoxEscapeReconnectTargets.u32DisconnectMask = u32DisconnectMask;
165
166 D3DKMT_ESCAPE EscapeData = {0};
167 EscapeData.hAdapter = hAdapter;
168 EscapeData.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
169 EscapeData.Flags.HardwareAccess = 1;
170 EscapeData.pPrivateDriverData = &VBoxEscapeReconnectTargets;
171 EscapeData.PrivateDriverDataSize = sizeof (VBoxEscapeReconnectTargets);
172
173 rcNt = g_pfnD3DKMTEscape(&EscapeData);
174 VGSvcVerbose(3, "D3DKMTEscape rcNt=%#x\n", rcNt);
175
176 D3DKMT_CLOSEADAPTER CloseAdapter;
177 CloseAdapter.hAdapter = hAdapter;
178
179 rcNt = g_pfnD3DKMTCloseAdapter(&CloseAdapter);
180 VGSvcVerbose(3, "D3DKMTCloseAdapter rcNt=%#x\n", rcNt);
181 }
182}
183
184static char* WTSSessionState2Str(WTS_CONNECTSTATE_CLASS State)
185{
186 switch(State)
187 {
188 RT_CASE_RET_STR(WTSActive);
189 RT_CASE_RET_STR(WTSConnected);
190 RT_CASE_RET_STR(WTSConnectQuery);
191 RT_CASE_RET_STR(WTSShadow);
192 RT_CASE_RET_STR(WTSDisconnected);
193 RT_CASE_RET_STR(WTSIdle);
194 RT_CASE_RET_STR(WTSListen);
195 RT_CASE_RET_STR(WTSReset);
196 RT_CASE_RET_STR(WTSDown);
197 RT_CASE_RET_STR(WTSInit);
198 default:
199 return "Unknown";
200 }
201}
202
203bool HasActiveLocalUser(void)
204{
205 WTS_SESSION_INFO *paSessionInfos = NULL;
206 DWORD cSessionInfos = 0;
207 bool fRet = false;
208
209 if (g_pfnWTSEnumerateSessionsA(WTS_CURRENT_SERVER_HANDLE, 0, 1, &paSessionInfos, &cSessionInfos))
210 {
211 VGSvcVerbose(3, "WTSEnumerateSessionsA got %u sessions\n", cSessionInfos);
212
213 for(DWORD i = 0; i < cSessionInfos; i++)
214 {
215 VGSvcVerbose(3, "WTS session[%u] SessionId (%u) pWinStationName (%s) State (%s %u)\n", i,
216 paSessionInfos[i].SessionId, paSessionInfos[i].pWinStationName,
217 WTSSessionState2Str(paSessionInfos[i].State), paSessionInfos[i].State);
218
219 if (paSessionInfos[i].State == WTSActive && RTStrNICmpAscii(paSessionInfos[i].pWinStationName, RT_STR_TUPLE("Console")) == 0)
220 {
221 VGSvcVerbose(2, "Found active WTS session %u connected to Console\n", paSessionInfos[i].SessionId);
222 fRet = true;
223 }
224 }
225 }
226 else
227 {
228 VGSvcError("WTSEnumerateSessionsA failed %#x\n", GetLastError());
229 }
230
231 if (paSessionInfos)
232 g_pfnWTSFreeMemory(paSessionInfos);
233
234 return fRet;
235}
236
237/**
238 * @interface_method_impl{VBOXSERVICE,pfnWorker}
239 */
240DECLCALLBACK(int) vgsvcDisplayConfigWorker(bool volatile *pfShutdown)
241{
242 int rc = VINF_SUCCESS;
243
244 /*
245 * Tell the control thread that it can continue spawning services.
246 */
247 RTThreadUserSignal(RTThreadSelf());
248 /*
249 * The Work Loop.
250 */
251
252 rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0);
253 VGSvcVerbose(3, "VbglR3CtlFilterMask set rc=%Rrc\n", rc);
254
255 for (;;)
256 {
257 uint32_t fEvents = 0;
258
259 if (HasActiveLocalUser())
260 {
261 RTThreadSleep(1000);
262 }
263 else
264 {
265 rc = VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, false);
266 VGSvcVerbose(3, "VbglR3AcquireGuestCaps acquire VMMDEV_GUEST_SUPPORTS_GRAPHICS rc=%Rrc\n", rc);
267
268 rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 2000 /*ms*/, &fEvents);
269 VGSvcVerbose(3, "VbglR3WaitEvent rc=%Rrc\n", rc);
270
271 if (RT_SUCCESS(rc))
272 {
273 VMMDevDisplayDef aDisplays[64];
274 uint32_t cDisplays = RT_ELEMENTS(aDisplays);
275
276 rc = VbglR3GetDisplayChangeRequestMulti(cDisplays, &cDisplays, &aDisplays[0], true);
277 VGSvcVerbose(3, "VbglR3GetDisplayChangeRequestMulti rc=%Rrc cDisplays=%d\n", rc, cDisplays);
278 if (cDisplays > 0)
279 {
280 for(uint32_t i = 0; i < cDisplays; i++)
281 {
282 VGSvcVerbose(2, "%u) Display[%u] flags=%#x (%dx%d)\n", i, aDisplays[i].idDisplay,
283 aDisplays[i].fDisplayFlags,
284 aDisplays[i].cx, aDisplays[i].cy);
285 }
286
287 ReconnectDisplays(cDisplays, &aDisplays[0]);
288 }
289 }
290 else
291 {
292 /* To prevent CPU throttle in case of multiple failures */
293 RTThreadSleep(200);
294 }
295
296 rc = VbglR3AcquireGuestCaps(0, VMMDEV_GUEST_SUPPORTS_GRAPHICS, false);
297 VGSvcVerbose(3, "VbglR3AcquireGuestCaps release VMMDEV_GUEST_SUPPORTS_GRAPHICS rc=%Rrc\n", rc);
298 }
299
300 if (*pfShutdown)
301 break;
302 }
303
304 rc = VbglR3CtlFilterMask(0, VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST);
305 VGSvcVerbose(3, "VbglR3CtlFilterMask cleared rc=%Rrc\n", rc);
306
307 return rc;
308}
309
310/**
311 * @interface_method_impl{VBOXSERVICE,pfnStop}
312 */
313static DECLCALLBACK(void) vgsvcDisplayConfigStop(void)
314{
315}
316
317/**
318 * @interface_method_impl{VBOXSERVICE,pfnTerm}
319 */
320static DECLCALLBACK(void) vgsvcDisplayConfigTerm(void)
321{
322}
323
324/**
325 * The 'displayconfig' service description.
326 */
327VBOXSERVICE g_DisplayConfig =
328{
329 /* pszName. */
330 "displayconfig",
331 /* pszDescription. */
332 "Display configuration",
333 /* pszUsage. */
334 NULL,
335 /* pszOptions. */
336 NULL,
337 /* methods */
338 VGSvcDefaultPreInit,
339 VGSvcDefaultOption,
340 vgsvcDisplayConfigInit,
341 vgsvcDisplayConfigWorker,
342 vgsvcDisplayConfigStop,
343 vgsvcDisplayConfigTerm
344};
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette