VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxVRDP.cpp@ 95961

Last change on this file since 95961 was 95961, checked in by vboxsync, 3 years ago

Additions/VBoxTray: Got rid of VBoxDisplay.h (renamed to VBoxTrayInternal.h, more stuff added later), as I also tripped over this several times in the past already, log include fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.7 KB
Line 
1/* $Id: VBoxVRDP.cpp 95961 2022-08-01 14:08:20Z vboxsync $ */
2/** @file
3 * VBoxVRDP - VBox VRDP connection notification
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include <iprt/assert.h>
19#include <iprt/ldr.h>
20#include <VBox/log.h>
21
22/* 0x0501 for SPI_SETDROPSHADOW */
23#define _WIN32_WINNT 0x0501
24#include <iprt/win/windows.h>
25
26#include <VBox/VMMDev.h> /* for VMMDEV_EVENT_VRDP and VRDP_EXPERIENCE_LEVEL_XXX */
27
28#include "VBoxTray.h"
29#include "VBoxHelpers.h"
30#include "VBoxVRDP.h"
31
32
33
34/* The guest receives VRDP_ACTIVE/VRDP_INACTIVE notifications.
35 *
36 * When VRDP_ACTIVE is received, the guest asks host about the experience level.
37 * The experience level is an integer value, different values disable some GUI effects.
38 *
39 * On VRDP_INACTIVE the original values are restored.
40 *
41 * Note: that this is not controlled from the client, that is a per VM settings.
42 *
43 * Note: theming is disabled separately by EnableTheming.
44 */
45
46#define VBOX_SPI_STRING 0
47#define VBOX_SPI_BOOL_PTR 1
48#define VBOX_SPI_BOOL 2
49#define VBOX_SPI_PTR 3
50
51static ANIMATIONINFO animationInfoDisable =
52{
53 sizeof (ANIMATIONINFO),
54 FALSE
55};
56
57typedef struct _VBOXVRDPEXPPARAM
58{
59 const char *name;
60 UINT uActionSet;
61 UINT uActionGet;
62 uint32_t level; /* The parameter remain enabled at this or higher level. */
63 int type;
64 void *pvDisable;
65 UINT cbSavedValue;
66 char achSavedValue[2 * MAX_PATH]; /* Large enough to save the bitmap path. */
67} VBOXVRDPEXPPARAM, *PVBOXVRDPEXPPARAM;
68
69typedef struct _VBOXVRDPCONTEXT
70{
71 const VBOXSERVICEENV *pEnv;
72
73 uint32_t level;
74 BOOL fSavedThemeEnabled;
75
76 RTLDRMOD hModUxTheme;
77
78 HRESULT (* pfnEnableTheming)(BOOL fEnable);
79 BOOL (* pfnIsThemeActive)(VOID);
80} VBOXVRDPCONTEXT, *PVBOXVRDPCONTEXT;
81
82static VBOXVRDPCONTEXT g_Ctx = { 0 };
83
84#define SPI_(l, a) #a, SPI_SET##a, SPI_GET##a, VRDP_EXPERIENCE_LEVEL_##l
85
86static VBOXVRDPEXPPARAM s_aSPIParams[] =
87{
88 { SPI_(MEDIUM, DESKWALLPAPER), VBOX_SPI_STRING, "" },
89 { SPI_(FULL, DROPSHADOW), VBOX_SPI_BOOL_PTR, },
90 { SPI_(HIGH, FONTSMOOTHING), VBOX_SPI_BOOL, },
91 { SPI_(FULL, MENUFADE), VBOX_SPI_BOOL_PTR, },
92 { SPI_(FULL, COMBOBOXANIMATION), VBOX_SPI_BOOL_PTR, },
93 { SPI_(FULL, CURSORSHADOW), VBOX_SPI_BOOL_PTR, },
94 { SPI_(HIGH, GRADIENTCAPTIONS), VBOX_SPI_BOOL_PTR, },
95 { SPI_(FULL, LISTBOXSMOOTHSCROLLING), VBOX_SPI_BOOL_PTR, },
96 { SPI_(FULL, MENUANIMATION), VBOX_SPI_BOOL_PTR, },
97 { SPI_(FULL, SELECTIONFADE), VBOX_SPI_BOOL_PTR, },
98 { SPI_(FULL, TOOLTIPANIMATION), VBOX_SPI_BOOL_PTR, },
99 { SPI_(FULL, ANIMATION), VBOX_SPI_PTR, &animationInfoDisable, sizeof (ANIMATIONINFO) },
100 { SPI_(MEDIUM, DRAGFULLWINDOWS), VBOX_SPI_BOOL, }
101};
102
103#undef SPI_
104
105static void vboxExperienceSet(uint32_t uLevel)
106{
107 for (size_t i = 0; i < RT_ELEMENTS(s_aSPIParams); i++)
108 {
109 PVBOXVRDPEXPPARAM pParam = &s_aSPIParams[i];
110 if (pParam->level > uLevel)
111 {
112 /*
113 * The parameter has to be disabled.
114 */
115 LogFlowFunc(("Saving %s\n", pParam->name));
116
117 /* Save the current value. */
118 switch (pParam->type)
119 {
120 case VBOX_SPI_STRING:
121 {
122 /* The 2nd parameter is size in characters of the buffer.
123 * The 3rd parameter points to the buffer.
124 */
125 SystemParametersInfo (pParam->uActionGet,
126 MAX_PATH,
127 pParam->achSavedValue,
128 0);
129 } break;
130
131 case VBOX_SPI_BOOL:
132 case VBOX_SPI_BOOL_PTR:
133 {
134 /* The 3rd parameter points to BOOL. */
135 SystemParametersInfo (pParam->uActionGet,
136 0,
137 pParam->achSavedValue,
138 0);
139 } break;
140
141 case VBOX_SPI_PTR:
142 {
143 /* The 3rd parameter points to the structure.
144 * The cbSize member of this structure must be set.
145 * The uiParam parameter must alos be set.
146 */
147 if (pParam->cbSavedValue > sizeof (pParam->achSavedValue))
148 {
149 LogFlowFunc(("Not enough space %d > %d\n", pParam->cbSavedValue, sizeof (pParam->achSavedValue)));
150 break;
151 }
152
153 *(UINT *)&pParam->achSavedValue[0] = pParam->cbSavedValue;
154
155 SystemParametersInfo (s_aSPIParams[i].uActionGet,
156 s_aSPIParams[i].cbSavedValue,
157 s_aSPIParams[i].achSavedValue,
158 0);
159 } break;
160
161 default:
162 break;
163 }
164
165 LogFlowFunc(("Disabling %s\n", pParam->name));
166
167 /* Disable the feature. */
168 switch (pParam->type)
169 {
170 case VBOX_SPI_STRING:
171 {
172 /* The 3rd parameter points to the string. */
173 SystemParametersInfo (pParam->uActionSet,
174 0,
175 pParam->pvDisable,
176 SPIF_SENDCHANGE);
177 } break;
178
179 case VBOX_SPI_BOOL:
180 {
181 /* The 2nd parameter is BOOL. */
182 SystemParametersInfo (pParam->uActionSet,
183 FALSE,
184 NULL,
185 SPIF_SENDCHANGE);
186 } break;
187
188 case VBOX_SPI_BOOL_PTR:
189 {
190 /* The 3rd parameter is NULL to disable. */
191 SystemParametersInfo (pParam->uActionSet,
192 0,
193 NULL,
194 SPIF_SENDCHANGE);
195 } break;
196
197 case VBOX_SPI_PTR:
198 {
199 /* The 3rd parameter points to the structure. */
200 SystemParametersInfo (pParam->uActionSet,
201 0,
202 pParam->pvDisable,
203 SPIF_SENDCHANGE);
204 } break;
205
206 default:
207 break;
208 }
209 }
210 }
211}
212
213static void vboxExperienceRestore(uint32_t uLevel)
214{
215 int i;
216 for (i = 0; i < RT_ELEMENTS(s_aSPIParams); i++)
217 {
218 PVBOXVRDPEXPPARAM pParam = &s_aSPIParams[i];
219 if (pParam->level > uLevel)
220 {
221 LogFlowFunc(("Restoring %s\n", pParam->name));
222
223 /* Restore the feature. */
224 switch (pParam->type)
225 {
226 case VBOX_SPI_STRING:
227 {
228 /* The 3rd parameter points to the string. */
229 SystemParametersInfo (pParam->uActionSet,
230 0,
231 pParam->achSavedValue,
232 SPIF_SENDCHANGE);
233 } break;
234
235 case VBOX_SPI_BOOL:
236 {
237 /* The 2nd parameter is BOOL. */
238 SystemParametersInfo (pParam->uActionSet,
239 *(BOOL *)&pParam->achSavedValue[0],
240 NULL,
241 SPIF_SENDCHANGE);
242 } break;
243
244 case VBOX_SPI_BOOL_PTR:
245 {
246 /* The 3rd parameter is NULL to disable. */
247 BOOL fSaved = *(BOOL *)&pParam->achSavedValue[0];
248
249 SystemParametersInfo (pParam->uActionSet,
250 0,
251 fSaved? &fSaved: NULL,
252 SPIF_SENDCHANGE);
253 } break;
254
255 case VBOX_SPI_PTR:
256 {
257 /* The 3rd parameter points to the structure. */
258 SystemParametersInfo (pParam->uActionSet,
259 0,
260 pParam->achSavedValue,
261 SPIF_SENDCHANGE);
262 } break;
263
264 default:
265 break;
266 }
267 }
268 }
269}
270
271static DECLCALLBACK(int) VBoxVRDPInit(const PVBOXSERVICEENV pEnv, void **ppInstance)
272{
273 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
274 AssertPtrReturn(ppInstance, VERR_INVALID_POINTER);
275
276 LogFlowFuncEnter();
277
278 PVBOXVRDPCONTEXT pCtx = &g_Ctx; /* Only one instance at the moment. */
279 AssertPtr(pCtx);
280
281 pCtx->pEnv = pEnv;
282 pCtx->level = VRDP_EXPERIENCE_LEVEL_FULL;
283 pCtx->fSavedThemeEnabled = FALSE;
284
285 int rc = RTLdrLoadSystem("UxTheme.dll", false /*fNoUnload*/, &g_Ctx.hModUxTheme);
286 if (RT_SUCCESS(rc))
287 {
288 *(PFNRT *)&pCtx->pfnEnableTheming = RTLdrGetFunction(g_Ctx.hModUxTheme, "EnableTheming");
289 *(PFNRT *)&pCtx->pfnIsThemeActive = RTLdrGetFunction(g_Ctx.hModUxTheme, "IsThemeActive");
290
291 *ppInstance = &g_Ctx;
292 }
293 else
294 {
295 g_Ctx.hModUxTheme = NIL_RTLDRMOD;
296 g_Ctx.pfnEnableTheming = NULL;
297 g_Ctx.pfnIsThemeActive = NULL;
298 }
299
300 /* Tell the caller that the service does not work but it is OK to continue. */
301 if (RT_FAILURE(rc))
302 rc = VERR_NOT_SUPPORTED;
303
304 LogFlowFuncLeaveRC(rc);
305 return rc;
306}
307
308static DECLCALLBACK(void) VBoxVRDPDestroy(void *pInstance)
309{
310 AssertPtrReturnVoid(pInstance);
311
312 LogFlowFuncEnter();
313
314 PVBOXVRDPCONTEXT pCtx = (PVBOXVRDPCONTEXT)pInstance;
315
316 vboxExperienceRestore (pCtx->level);
317 if (pCtx->hModUxTheme != NIL_RTLDRMOD)
318 {
319 RTLdrClose(g_Ctx.hModUxTheme);
320 pCtx->hModUxTheme = NIL_RTLDRMOD;
321 }
322
323 return;
324}
325
326/**
327 * Thread function to wait for and process mode change requests
328 */
329static DECLCALLBACK(int) VBoxVRDPWorker(void *pvInstance, bool volatile *pfShutdown)
330{
331 AssertPtrReturn(pvInstance, VERR_INVALID_POINTER);
332 PVBOXVRDPCONTEXT pCtx = (PVBOXVRDPCONTEXT)pvInstance;
333
334 LogFlowFuncEnter();
335
336 /*
337 * Tell the control thread that it can continue spawning services.
338 */
339 RTThreadUserSignal(RTThreadSelf());
340
341 int rc = VbglR3CtlFilterMask(VMMDEV_EVENT_VRDP, 0 /*fNot*/);
342 if (RT_FAILURE(rc))
343 {
344 LogRel(("VbglR3CtlFilterMask(VMMDEV_EVENT_VRDP, 0) failed with %Rrc, exiting...\n"));
345 return rc;
346 }
347
348 for (;;)
349 {
350 /*
351 * Wait for the event, checking the shutdown flag both before and after the call.
352 */
353 if (*pfShutdown)
354 {
355 rc = VINF_SUCCESS;
356 break;
357 }
358
359 uint32_t fEvent = 0;
360 rc = VbglR3WaitEvent(VMMDEV_EVENT_VRDP, 5000 /*ms*/, &fEvent);
361
362 if (*pfShutdown)
363 {
364 rc = VINF_SUCCESS;
365 break;
366 }
367
368 if (RT_SUCCESS(rc))
369 {
370 /* did we get the right event? */
371 if (fEvent & VMMDEV_EVENT_VRDP)
372 {
373 bool fActive = false;
374 uint32_t uExperienceLevel = 0;
375 rc = VbglR3VrdpGetChangeRequest(&fActive, &uExperienceLevel);
376 if (RT_SUCCESS(rc))
377 {
378 LogFlowFunc(("u8VRDPActive = %d, level %d\n", fActive, uExperienceLevel));
379
380 if (fActive)
381 {
382 pCtx->level = uExperienceLevel;
383 vboxExperienceSet (pCtx->level);
384
385 if (pCtx->level == VRDP_EXPERIENCE_LEVEL_ZERO
386 && pCtx->pfnEnableTheming
387 && pCtx->pfnIsThemeActive)
388 {
389 pCtx->fSavedThemeEnabled = pCtx->pfnIsThemeActive ();
390
391 LogFlowFunc(("pCtx->fSavedThemeEnabled = %d\n", pCtx->fSavedThemeEnabled));
392
393 if (pCtx->fSavedThemeEnabled)
394 {
395 pCtx->pfnEnableTheming (FALSE);
396 }
397 }
398 }
399 else
400 {
401 if (pCtx->level == VRDP_EXPERIENCE_LEVEL_ZERO
402 && pCtx->pfnEnableTheming
403 && pCtx->pfnIsThemeActive)
404 {
405 if (pCtx->fSavedThemeEnabled)
406 {
407 /** @todo the call returns S_OK but theming remains disabled. */
408 HRESULT hrc = pCtx->pfnEnableTheming (TRUE);
409 LogFlowFunc(("enabling theme rc = 0x%08X\n", hrc)); NOREF(hrc);
410 pCtx->fSavedThemeEnabled = FALSE;
411 }
412 }
413
414 vboxExperienceRestore (pCtx->level);
415
416 pCtx->level = VRDP_EXPERIENCE_LEVEL_FULL;
417 }
418 }
419 else
420 {
421 /* sleep a bit to not eat too much CPU in case the above call always fails */
422 RTThreadSleep(10);
423 }
424 }
425 }
426 /* sleep a bit to not eat too much CPU in case the above call always fails */
427 else
428 RTThreadSleep(50);
429 }
430
431 int rc2 = VbglR3CtlFilterMask(0 /*fOr*/, VMMDEV_EVENT_VRDP);
432 if (RT_FAILURE(rc2))
433 LogRel(("VbglR3CtlFilterMask(0 /*fOr*/, VMMDEV_EVENT_VRDP) failed with %Rrc\n", rc));
434
435 LogFlowFuncLeaveRC(rc);
436 return rc;
437}
438
439/**
440 * The service description.
441 */
442VBOXSERVICEDESC g_SvcDescVRDP =
443{
444 /* pszName. */
445 "VRDP",
446 /* pszDescription. */
447 "VRDP Connection Notification",
448 /* methods */
449 VBoxVRDPInit,
450 VBoxVRDPWorker,
451 NULL /* pfnStop */,
452 VBoxVRDPDestroy
453};
454
Note: See TracBrowser for help on using the repository browser.

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