VirtualBox

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

Last change on this file since 106945 was 106466, checked in by vboxsync, 7 weeks ago

Additions/VBoxTray: More cleanup (renaming). ​bugref:10763

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