VirtualBox

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

Last change on this file since 78416 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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