VirtualBox

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

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

GA/Windows: Prevent CPU throttle in case of multiple failures. bugref:10714

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.4 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
32#include <VBox/VBoxGuestLib.h>
33#include "VBoxServiceInternal.h"
34#include "VBoxServiceUtils.h"
35
36#undef NTDDI_VERSION
37#define NTDDI_VERSION NTDDI_LONGHORN
38#include <iprt/win/d3dkmthk.h>
39
40#define VBOX_WITH_WDDM
41#include <VBoxDisplay.h>
42
43
44/*********************************************************************************************************************************
45* Global Variables *
46*********************************************************************************************************************************/
47
48static PFND3DKMT_ENUMADAPTERS g_pfnD3DKMTEnumAdapters;
49static PFND3DKMT_OPENADAPTERFROMLUID g_pfnD3DKMTOpenAdapterFromLuid;
50static PFND3DKMT_CLOSEADAPTER g_pfnD3DKMTCloseAdapter;
51static PFND3DKMT_ESCAPE g_pfnD3DKMTEscape;
52
53static decltype(WTSFreeMemory) *g_pfnWTSFreeMemory = NULL;
54static decltype(WTSQuerySessionInformationA) *g_pfnWTSQuerySessionInformationA = NULL;
55static decltype(WTSEnumerateSessionsA) *g_pfnWTSEnumerateSessionsA = NULL;
56
57/**
58 * @interface_method_impl{VBOXSERVICE,pfnInit}
59 */
60static DECLCALLBACK(int) vgsvcDisplayConfigInit(void)
61{
62 RTLDRMOD hLdrMod;
63 int rc = RTLdrLoadSystem("gdi32.dll", true /*fNoUnload*/, &hLdrMod);
64 if (RT_SUCCESS(rc))
65 {
66 rc = RTLdrGetSymbol(hLdrMod, "D3DKMTEnumAdapters", (void **)&g_pfnD3DKMTEnumAdapters);
67 if (RT_SUCCESS(rc))
68 rc = RTLdrGetSymbol(hLdrMod, "D3DKMTOpenAdapterFromLuid", (void **)&g_pfnD3DKMTOpenAdapterFromLuid);
69 if (RT_SUCCESS(rc))
70 rc = RTLdrGetSymbol(hLdrMod, "D3DKMTCloseAdapter", (void **)&g_pfnD3DKMTCloseAdapter);
71 if (RT_SUCCESS(rc))
72 rc = RTLdrGetSymbol(hLdrMod, "D3DKMTEscape", (void **)&g_pfnD3DKMTEscape);
73 AssertRC(rc);
74 RTLdrClose(hLdrMod);
75 }
76
77 VGSvcVerbose(3, "DXGK d3dkmthk callbacks are %s\n", RT_SUCCESS(rc) ? "Ok" : "Fail");
78
79 rc = RTLdrLoadSystem("wtsapi32.dll", true /*fNoUnload*/, &hLdrMod);
80 if (RT_SUCCESS(rc))
81 {
82 rc = RTLdrGetSymbol(hLdrMod, "WTSFreeMemory", (void **)&g_pfnWTSFreeMemory);
83 if (RT_SUCCESS(rc))
84 rc = RTLdrGetSymbol(hLdrMod, "WTSQuerySessionInformationA", (void **)&g_pfnWTSQuerySessionInformationA);
85 if (RT_SUCCESS(rc))
86 rc = RTLdrGetSymbol(hLdrMod, "WTSEnumerateSessionsA", (void **)&g_pfnWTSEnumerateSessionsA);
87 AssertRC(rc);
88 RTLdrClose(hLdrMod);
89 }
90
91 if (RT_FAILURE(rc))
92 {
93 VGSvcVerbose(1, "WtsApi32.dll APIs are not available (%Rrc)\n", rc);
94 g_pfnWTSFreeMemory = NULL;
95 g_pfnWTSQuerySessionInformationA = NULL;
96 g_pfnWTSEnumerateSessionsA = NULL;
97 // Assert(RTSystemGetNtVersion() < RTSYSTEM_MAKE_NT_VERSION(5, 0, 0));
98 }
99
100 return VINF_SUCCESS;
101}
102
103void ReconnectDisplays(uint32_t cDisplays, VMMDevDisplayDef *paDisplays)
104{
105 D3DKMT_HANDLE hAdapter;
106 D3DKMT_ENUMADAPTERS EnumAdapters = {0};
107 NTSTATUS rcNt;
108 uint32_t u32Mask = 0;
109
110 for(uint32_t i = 0; i < cDisplays; i++)
111 {
112 u32Mask |= (paDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED) ? 0 : RT_BIT(i);
113 }
114
115 VGSvcVerbose(3, "ReconnectDisplays u32Mask 0x%x\n", u32Mask);
116
117 EnumAdapters.NumAdapters = RT_ELEMENTS(EnumAdapters.Adapters);
118 rcNt = g_pfnD3DKMTEnumAdapters(&EnumAdapters);
119
120 VGSvcVerbose(3, "D3DKMTEnumAdapters rcNt=%#x NumAdapters=%d\n", rcNt, EnumAdapters.NumAdapters);
121
122 for(ULONG id = 0; id < EnumAdapters.NumAdapters; id++)
123 {
124 D3DKMT_ADAPTERINFO *pAdapterInfo = &EnumAdapters.Adapters[id];
125 VGSvcVerbose(3, "#%d: NumOfSources=%d hAdapter=0x%p Luid(%u, %u)\n", id,
126 pAdapterInfo->NumOfSources, pAdapterInfo->hAdapter, pAdapterInfo->AdapterLuid.HighPart, pAdapterInfo->AdapterLuid.LowPart);
127 }
128
129 D3DKMT_OPENADAPTERFROMLUID OpenAdapterData;
130 RT_ZERO(OpenAdapterData);
131 OpenAdapterData.AdapterLuid = EnumAdapters.Adapters[0].AdapterLuid;
132 rcNt = g_pfnD3DKMTOpenAdapterFromLuid(&OpenAdapterData);
133 VGSvcVerbose(3, "D3DKMTOpenAdapterFromLuid rcNt=%#x hAdapter=0x%p\n", rcNt, OpenAdapterData.hAdapter);
134
135 hAdapter = OpenAdapterData.hAdapter;
136
137 if (hAdapter)
138 {
139 VBOXDISPIFESCAPE EscapeHdr = {0};
140 EscapeHdr.escapeCode = VBOXESC_RECONNECT_TARGETS;
141 EscapeHdr.u32CmdSpecific = u32Mask;
142
143 D3DKMT_ESCAPE EscapeData = {0};
144 EscapeData.hAdapter = hAdapter;
145 EscapeData.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
146 EscapeData.Flags.HardwareAccess = 1;
147 EscapeData.pPrivateDriverData = &EscapeHdr;
148 EscapeData.PrivateDriverDataSize = sizeof (EscapeHdr);
149
150 rcNt = g_pfnD3DKMTEscape(&EscapeData);
151 VGSvcVerbose(3, "D3DKMTEscape rcNt=%#x\n", rcNt);
152
153 D3DKMT_CLOSEADAPTER CloseAdapter;
154 CloseAdapter.hAdapter = hAdapter;
155
156 rcNt = g_pfnD3DKMTCloseAdapter(&CloseAdapter);
157 VGSvcVerbose(3, "D3DKMTCloseAdapter rcNt=%#x\n", rcNt);
158 }
159}
160
161static char* WTSSessionState2Str(WTS_CONNECTSTATE_CLASS State)
162{
163 switch(State)
164 {
165 RT_CASE_RET_STR(WTSActive);
166 RT_CASE_RET_STR(WTSConnected);
167 RT_CASE_RET_STR(WTSConnectQuery);
168 RT_CASE_RET_STR(WTSShadow);
169 RT_CASE_RET_STR(WTSDisconnected);
170 RT_CASE_RET_STR(WTSIdle);
171 RT_CASE_RET_STR(WTSListen);
172 RT_CASE_RET_STR(WTSReset);
173 RT_CASE_RET_STR(WTSDown);
174 RT_CASE_RET_STR(WTSInit);
175 default:
176 return "Unknown";
177 }
178}
179
180bool HasActiveLocalUser(void)
181{
182 WTS_SESSION_INFO *paSessionInfos = NULL;
183 DWORD cSessionInfos = 0;
184 bool fRet = false;
185
186 if (g_pfnWTSEnumerateSessionsA(WTS_CURRENT_SERVER_HANDLE, 0, 1, &paSessionInfos, &cSessionInfos))
187 {
188 VGSvcVerbose(3, "WTSEnumerateSessionsA got %u sessions\n", cSessionInfos);
189
190 for(DWORD i = 0; i < cSessionInfos; i++)
191 {
192 VGSvcVerbose(3, "WTS session[%u] SessionId (%u) pWinStationName (%s) State (%s %u)\n", i,
193 paSessionInfos[i].SessionId, paSessionInfos[i].pWinStationName,
194 WTSSessionState2Str(paSessionInfos[i].State), paSessionInfos[i].State);
195
196 if (paSessionInfos[i].State == WTSActive && RTStrNICmpAscii(paSessionInfos[i].pWinStationName, RT_STR_TUPLE("Console")) == 0)
197 {
198 VGSvcVerbose(2, "Found active WTS session %u connected to Console\n", paSessionInfos[i].SessionId);
199 fRet = true;
200 }
201 }
202 }
203 else
204 {
205 VGSvcError("WTSEnumerateSessionsA failed %#x\n", GetLastError());
206 }
207
208 if (paSessionInfos)
209 g_pfnWTSFreeMemory(paSessionInfos);
210
211 return fRet;
212}
213
214/**
215 * @interface_method_impl{VBOXSERVICE,pfnWorker}
216 */
217DECLCALLBACK(int) vgsvcDisplayConfigWorker(bool volatile *pfShutdown)
218{
219 int rc = VINF_SUCCESS;
220
221 /*
222 * Tell the control thread that it can continue spawning services.
223 */
224 RTThreadUserSignal(RTThreadSelf());
225 /*
226 * The Work Loop.
227 */
228
229 rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0);
230 VGSvcVerbose(3, "VbglR3CtlFilterMask set rc=%Rrc\n", rc);
231
232 for (;;)
233 {
234 uint32_t fEvents = 0;
235
236 if (HasActiveLocalUser())
237 {
238 RTThreadSleep(1000);
239 }
240 else
241 {
242 rc = VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, false);
243 VGSvcVerbose(3, "VbglR3AcquireGuestCaps acquire VMMDEV_GUEST_SUPPORTS_GRAPHICS rc=%Rrc\n", rc);
244
245 rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 2000 /*ms*/, &fEvents);
246 VGSvcVerbose(3, "VbglR3WaitEvent rc=%Rrc\n", rc);
247
248 if (RT_SUCCESS(rc))
249 {
250 VMMDevDisplayDef aDisplays[64];
251 uint32_t cDisplays = RT_ELEMENTS(aDisplays);
252
253 rc = VbglR3GetDisplayChangeRequestMulti(cDisplays, &cDisplays, &aDisplays[0], true);
254 VGSvcVerbose(3, "VbglR3GetDisplayChangeRequestMulti rc=%Rrc cDisplays=%d\n", rc, cDisplays);
255 if (cDisplays > 0)
256 {
257 for(uint32_t i = 0; i < cDisplays; i++)
258 {
259 VGSvcVerbose(2, "Display[%i] flags=%#x (%dx%d)\n", i,
260 aDisplays[i].fDisplayFlags,
261 aDisplays[i].cx, aDisplays[i].cy);
262 }
263
264 ReconnectDisplays(cDisplays, &aDisplays[0]);
265 }
266 }
267 else
268 {
269 /* To prevent CPU throttle in case of multiple failures */
270 RTThreadSleep(200);
271 }
272
273 rc = VbglR3AcquireGuestCaps(0, VMMDEV_GUEST_SUPPORTS_GRAPHICS, false);
274 VGSvcVerbose(3, "VbglR3AcquireGuestCaps release VMMDEV_GUEST_SUPPORTS_GRAPHICS rc=%Rrc\n", rc);
275 }
276
277 if (*pfShutdown)
278 break;
279 }
280
281 rc = VbglR3CtlFilterMask(0, VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST);
282 VGSvcVerbose(3, "VbglR3CtlFilterMask cleared rc=%Rrc\n", rc);
283
284 return rc;
285}
286
287/**
288 * @interface_method_impl{VBOXSERVICE,pfnStop}
289 */
290static DECLCALLBACK(void) vgsvcDisplayConfigStop(void)
291{
292}
293
294/**
295 * @interface_method_impl{VBOXSERVICE,pfnTerm}
296 */
297static DECLCALLBACK(void) vgsvcDisplayConfigTerm(void)
298{
299}
300
301/**
302 * The 'displayconfig' service description.
303 */
304VBOXSERVICE g_DisplayConfig =
305{
306 /* pszName. */
307 "displayconfig",
308 /* pszDescription. */
309 "Display configuration",
310 /* pszUsage. */
311 NULL,
312 /* pszOptions. */
313 NULL,
314 /* methods */
315 VGSvcDefaultPreInit,
316 VGSvcDefaultOption,
317 vgsvcDisplayConfigInit,
318 vgsvcDisplayConfigWorker,
319 vgsvcDisplayConfigStop,
320 vgsvcDisplayConfigTerm
321};
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