VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxControl/VBoxControl.cpp@ 10834

Last change on this file since 10834 was 10834, checked in by vboxsync, 17 years ago

Additions/common: guest property fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 36.6 KB
Line 
1/** $Id: VBoxControl.cpp 10834 2008-07-23 15:02:33Z vboxsync $ */
2/** @file
3 * VBoxControl - Guest Additions Command Line Management Interface
4 */
5
6/*
7 * Copyright (C) 2007 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#include <iprt/mem.h>
28#include <iprt/string.h>
29#include <iprt/stream.h>
30#include <iprt/path.h>
31#include <iprt/initterm.h>
32#include <VBox/log.h>
33#include <VBox/VBoxGuest.h>
34#include <VBox/version.h>
35#ifdef RT_OS_WINDOWS
36# include <windows.h>
37# include <malloc.h> /* for alloca */
38#endif
39#ifdef VBOX_WITH_GUEST_PROPS
40# include <VBox/HostServices/GuestPropertySvc.h>
41#endif
42#include "VBoxControl.h"
43
44/*******************************************************************************
45* Global Variables *
46*******************************************************************************/
47/** The program name (derived from argv[0]). */
48char const *g_pszProgName = "";
49/** The current verbosity level. */
50int g_cVerbosity = 0;
51
52
53/**
54 * Displays the program usage message.
55 *
56 * @param u64Which
57 *
58 * @{
59 */
60
61/** Helper function */
62static void doUsage(char const *line, char const *name = "", char const *command = "")
63{
64 RTPrintf("%s %-*s%s", name, 32 - strlen(name), command, line);
65}
66
67/** Enumerate the different parts of the usage we might want to print out */
68enum g_eUsage
69{
70#ifdef RT_OS_WINDOWS
71 GET_VIDEO_ACCEL,
72 SET_VIDEO_ACCEL,
73 LIST_CUST_MODES,
74 ADD_CUST_MODE,
75 REMOVE_CUST_MODE,
76 SET_VIDEO_MODE,
77#endif
78#ifdef VBOX_WITH_GUEST_PROPS
79 GUEST_PROP,
80#endif
81 USAGE_ALL = UINT32_MAX
82};
83
84static void usage(g_eUsage eWhich = USAGE_ALL)
85{
86 RTPrintf("Usage:\n\n");
87 RTPrintf("%s [-v|-version] print version number and exit\n", g_pszProgName);
88 RTPrintf("%s -nologo ... suppress the logo\n\n", g_pszProgName);
89
90#ifdef RT_OS_WINDOWS
91 if ((GET_VIDEO_ACCEL == eWhich) || (USAGE_ALL == eWhich))
92 doUsage("\n", g_pszProgName, "getvideoacceleration");
93 if ((SET_VIDEO_ACCEL == eWhich) || (USAGE_ALL == eWhich))
94 doUsage("<on|off>\n", g_pszProgName, "setvideoacceleration");
95 if ((LIST_CUST_MODES == eWhich) || (USAGE_ALL == eWhich))
96 doUsage("\n", g_pszProgName, "listcustommodes");
97 if ((ADD_CUST_MODE == eWhich) || (USAGE_ALL == eWhich))
98 doUsage("<width> <height> <bpp>\n", g_pszProgName, "addcustommode");
99 if ((REMOVE_CUST_MODE == eWhich) || (USAGE_ALL == eWhich))
100 doUsage("<width> <height> <bpp>\n", g_pszProgName, "removecustommode");
101 if ((SET_VIDEO_MODE == eWhich) || (USAGE_ALL == eWhich))
102 doUsage("<width> <height> <bpp> <screen>\n", g_pszProgName, "setvideomode");
103#endif
104#ifdef VBOX_WITH_GUEST_PROPS
105 if ((GUEST_PROP == eWhich) || (USAGE_ALL == eWhich))
106 {
107 doUsage("get <property> [-verbose]\n", g_pszProgName, "guestproperty");
108 doUsage("set <property> [<value>] [--flags <flags>]\n", g_pszProgName, "guestproperty");
109 }
110#endif
111}
112/** @} */
113
114/**
115 * Displays an error message.
116 *
117 * @param pszFormat The message text.
118 * @param ... Format arguments.
119 */
120static void VBoxControlError(const char *pszFormat, ...)
121{
122 // RTStrmPrintf(g_pStdErr, "%s: error: ", g_pszProgName);
123
124 va_list va;
125 va_start(va, pszFormat);
126 RTStrmPrintfV(g_pStdErr, pszFormat, va);
127 va_end(va);
128}
129
130#ifdef RT_OS_WINDOWS
131
132LONG (WINAPI * gpfnChangeDisplaySettingsEx)(LPCTSTR lpszDeviceName, LPDEVMODE lpDevMode, HWND hwnd, DWORD dwflags, LPVOID lParam);
133
134static unsigned nextAdjacentRectXP (RECTL *paRects, unsigned nRects, unsigned iRect)
135{
136 unsigned i;
137 for (i = 0; i < nRects; i++)
138 {
139 if (paRects[iRect].right == paRects[i].left)
140 {
141 return i;
142 }
143 }
144 return ~0;
145}
146
147static unsigned nextAdjacentRectXN (RECTL *paRects, unsigned nRects, unsigned iRect)
148{
149 unsigned i;
150 for (i = 0; i < nRects; i++)
151 {
152 if (paRects[iRect].left == paRects[i].right)
153 {
154 return i;
155 }
156 }
157 return ~0;
158}
159
160static unsigned nextAdjacentRectYP (RECTL *paRects, unsigned nRects, unsigned iRect)
161{
162 unsigned i;
163 for (i = 0; i < nRects; i++)
164 {
165 if (paRects[iRect].bottom == paRects[i].top)
166 {
167 return i;
168 }
169 }
170 return ~0;
171}
172
173unsigned nextAdjacentRectYN (RECTL *paRects, unsigned nRects, unsigned iRect)
174{
175 unsigned i;
176 for (i = 0; i < nRects; i++)
177 {
178 if (paRects[iRect].top == paRects[i].bottom)
179 {
180 return i;
181 }
182 }
183 return ~0;
184}
185
186void resizeRect(RECTL *paRects, unsigned nRects, unsigned iPrimary, unsigned iResized, int NewWidth, int NewHeight)
187{
188 RECTL *paNewRects = (RECTL *)alloca (sizeof (RECTL) * nRects);
189 memcpy (paNewRects, paRects, sizeof (RECTL) * nRects);
190 paNewRects[iResized].right += NewWidth - (paNewRects[iResized].right - paNewRects[iResized].left);
191 paNewRects[iResized].bottom += NewHeight - (paNewRects[iResized].bottom - paNewRects[iResized].top);
192
193 /* Verify all pairs of originally adjacent rectangles for all 4 directions.
194 * If the pair has a "good" delta (that is the first rectangle intersects the second)
195 * at a direction and the second rectangle is not primary one (which can not be moved),
196 * move the second rectangle to make it adjacent to the first one.
197 */
198
199 /* X positive. */
200 unsigned iRect;
201 for (iRect = 0; iRect < nRects; iRect++)
202 {
203 /* Find the next adjacent original rect in x positive direction. */
204 unsigned iNextRect = nextAdjacentRectXP (paRects, nRects, iRect);
205 Log(("next %d -> %d\n", iRect, iNextRect));
206
207 if (iNextRect == ~0 || iNextRect == iPrimary)
208 {
209 continue;
210 }
211
212 /* Check whether there is an X intesection between these adjacent rects in the new rectangles
213 * and fix the intersection if delta is "good".
214 */
215 int delta = paNewRects[iRect].right - paNewRects[iNextRect].left;
216
217 if (delta > 0)
218 {
219 Log(("XP intersection right %d left %d, diff %d\n",
220 paNewRects[iRect].right, paNewRects[iNextRect].left,
221 delta));
222
223 paNewRects[iNextRect].left += delta;
224 paNewRects[iNextRect].right += delta;
225 }
226 }
227
228 /* X negative. */
229 for (iRect = 0; iRect < nRects; iRect++)
230 {
231 /* Find the next adjacent original rect in x negative direction. */
232 unsigned iNextRect = nextAdjacentRectXN (paRects, nRects, iRect);
233 Log(("next %d -> %d\n", iRect, iNextRect));
234
235 if (iNextRect == ~0 || iNextRect == iPrimary)
236 {
237 continue;
238 }
239
240 /* Check whether there is an X intesection between these adjacent rects in the new rectangles
241 * and fix the intersection if delta is "good".
242 */
243 int delta = paNewRects[iRect].left - paNewRects[iNextRect].right;
244
245 if (delta < 0)
246 {
247 Log(("XN intersection left %d right %d, diff %d\n",
248 paNewRects[iRect].left, paNewRects[iNextRect].right,
249 delta));
250
251 paNewRects[iNextRect].left += delta;
252 paNewRects[iNextRect].right += delta;
253 }
254 }
255
256 /* Y positive (in the computer sence, top->down). */
257 for (iRect = 0; iRect < nRects; iRect++)
258 {
259 /* Find the next adjacent original rect in y positive direction. */
260 unsigned iNextRect = nextAdjacentRectYP (paRects, nRects, iRect);
261 Log(("next %d -> %d\n", iRect, iNextRect));
262
263 if (iNextRect == ~0 || iNextRect == iPrimary)
264 {
265 continue;
266 }
267
268 /* Check whether there is an Y intesection between these adjacent rects in the new rectangles
269 * and fix the intersection if delta is "good".
270 */
271 int delta = paNewRects[iRect].bottom - paNewRects[iNextRect].top;
272
273 if (delta > 0)
274 {
275 Log(("YP intersection bottom %d top %d, diff %d\n",
276 paNewRects[iRect].bottom, paNewRects[iNextRect].top,
277 delta));
278
279 paNewRects[iNextRect].top += delta;
280 paNewRects[iNextRect].bottom += delta;
281 }
282 }
283
284 /* Y negative (in the computer sence, down->top). */
285 for (iRect = 0; iRect < nRects; iRect++)
286 {
287 /* Find the next adjacent original rect in x negative direction. */
288 unsigned iNextRect = nextAdjacentRectYN (paRects, nRects, iRect);
289 Log(("next %d -> %d\n", iRect, iNextRect));
290
291 if (iNextRect == ~0 || iNextRect == iPrimary)
292 {
293 continue;
294 }
295
296 /* Check whether there is an Y intesection between these adjacent rects in the new rectangles
297 * and fix the intersection if delta is "good".
298 */
299 int delta = paNewRects[iRect].top - paNewRects[iNextRect].bottom;
300
301 if (delta < 0)
302 {
303 Log(("YN intersection top %d bottom %d, diff %d\n",
304 paNewRects[iRect].top, paNewRects[iNextRect].bottom,
305 delta));
306
307 paNewRects[iNextRect].top += delta;
308 paNewRects[iNextRect].bottom += delta;
309 }
310 }
311
312 memcpy (paRects, paNewRects, sizeof (RECTL) * nRects);
313 return;
314}
315
316/* Returns TRUE to try again. */
317static BOOL ResizeDisplayDevice(ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel)
318{
319 BOOL fModeReset = (Width == 0 && Height == 0 && BitsPerPixel == 0);
320
321 DISPLAY_DEVICE DisplayDevice;
322
323 ZeroMemory(&DisplayDevice, sizeof(DisplayDevice));
324 DisplayDevice.cb = sizeof(DisplayDevice);
325
326 /* Find out how many display devices the system has */
327 DWORD NumDevices = 0;
328 DWORD i = 0;
329 while (EnumDisplayDevices (NULL, i, &DisplayDevice, 0))
330 {
331 Log(("[%d] %s\n", i, DisplayDevice.DeviceName));
332
333 if (DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
334 {
335 Log(("Found primary device. err %d\n", GetLastError ()));
336 NumDevices++;
337 }
338 else if (!(DisplayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
339 {
340
341 Log(("Found secondary device. err %d\n", GetLastError ()));
342 NumDevices++;
343 }
344
345 ZeroMemory(&DisplayDevice, sizeof(DisplayDevice));
346 DisplayDevice.cb = sizeof(DisplayDevice);
347 i++;
348 }
349
350 Log(("Found total %d devices. err %d\n", NumDevices, GetLastError ()));
351
352 if (NumDevices == 0 || Id >= NumDevices)
353 {
354 Log(("Requested identifier %d is invalid. err %d\n", Id, GetLastError ()));
355 return FALSE;
356 }
357
358 DISPLAY_DEVICE *paDisplayDevices = (DISPLAY_DEVICE *)alloca (sizeof (DISPLAY_DEVICE) * NumDevices);
359 DEVMODE *paDeviceModes = (DEVMODE *)alloca (sizeof (DEVMODE) * NumDevices);
360 RECTL *paRects = (RECTL *)alloca (sizeof (RECTL) * NumDevices);
361
362 /* Fetch information about current devices and modes. */
363 DWORD DevNum = 0;
364 DWORD DevPrimaryNum = 0;
365
366 ZeroMemory(&DisplayDevice, sizeof(DISPLAY_DEVICE));
367 DisplayDevice.cb = sizeof(DISPLAY_DEVICE);
368
369 i = 0;
370 while (EnumDisplayDevices (NULL, i, &DisplayDevice, 0))
371 {
372 Log(("[%d(%d)] %s\n", i, DevNum, DisplayDevice.DeviceName));
373
374 BOOL bFetchDevice = FALSE;
375
376 if (DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
377 {
378 Log(("Found primary device. err %d\n", GetLastError ()));
379 DevPrimaryNum = DevNum;
380 bFetchDevice = TRUE;
381 }
382 else if (!(DisplayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
383 {
384
385 Log(("Found secondary device. err %d\n", GetLastError ()));
386 bFetchDevice = TRUE;
387 }
388
389 if (bFetchDevice)
390 {
391 if (DevNum >= NumDevices)
392 {
393 Log(("%d >= %d\n", NumDevices, DevNum));
394 return FALSE;
395 }
396
397 paDisplayDevices[DevNum] = DisplayDevice;
398
399 ZeroMemory(&paDeviceModes[DevNum], sizeof(DEVMODE));
400 paDeviceModes[DevNum].dmSize = sizeof(DEVMODE);
401 if (!EnumDisplaySettings((LPSTR)DisplayDevice.DeviceName,
402 ENUM_REGISTRY_SETTINGS, &paDeviceModes[DevNum]))
403 {
404 Log(("EnumDisplaySettings err %d\n", GetLastError ()));
405 return FALSE;
406 }
407
408 Log(("%dx%d at %d,%d\n",
409 paDeviceModes[DevNum].dmPelsWidth,
410 paDeviceModes[DevNum].dmPelsHeight,
411 paDeviceModes[DevNum].dmPosition.x,
412 paDeviceModes[DevNum].dmPosition.y));
413
414 paRects[DevNum].left = paDeviceModes[DevNum].dmPosition.x;
415 paRects[DevNum].top = paDeviceModes[DevNum].dmPosition.y;
416 paRects[DevNum].right = paDeviceModes[DevNum].dmPosition.x + paDeviceModes[DevNum].dmPelsWidth;
417 paRects[DevNum].bottom = paDeviceModes[DevNum].dmPosition.y + paDeviceModes[DevNum].dmPelsHeight;
418 DevNum++;
419 }
420
421 ZeroMemory(&DisplayDevice, sizeof(DISPLAY_DEVICE));
422 DisplayDevice.cb = sizeof(DISPLAY_DEVICE);
423 i++;
424 }
425
426 if (Width == 0)
427 {
428 Width = paRects[Id].right - paRects[Id].left;
429 }
430
431 if (Height == 0)
432 {
433 Height = paRects[Id].bottom - paRects[Id].top;
434 }
435
436 /* Check whether a mode reset or a change is requested. */
437 if ( !fModeReset
438 && paRects[Id].right - paRects[Id].left == Width
439 && paRects[Id].bottom - paRects[Id].top == Height
440 && paDeviceModes[Id].dmBitsPerPel == BitsPerPixel)
441 {
442 Log(("VBoxDisplayThread : already at desired resolution.\n"));
443 return FALSE;
444 }
445
446 resizeRect(paRects, NumDevices, DevPrimaryNum, Id, Width, Height);
447#ifdef Log
448 for (i = 0; i < NumDevices; i++)
449 {
450 Log(("[%d]: %d,%d %dx%d\n",
451 i, paRects[i].left, paRects[i].top,
452 paRects[i].right - paRects[i].left,
453 paRects[i].bottom - paRects[i].top));
454 }
455#endif /* Log */
456
457 /* Without this, Windows will not ask the miniport for its
458 * mode table but uses an internal cache instead.
459 */
460 DEVMODE tempDevMode;
461 ZeroMemory (&tempDevMode, sizeof (tempDevMode));
462 tempDevMode.dmSize = sizeof(DEVMODE);
463 EnumDisplaySettings(NULL, 0xffffff, &tempDevMode);
464
465 /* Assign the new rectangles to displays. */
466 for (i = 0; i < NumDevices; i++)
467 {
468 paDeviceModes[i].dmPosition.x = paRects[i].left;
469 paDeviceModes[i].dmPosition.y = paRects[i].top;
470 paDeviceModes[i].dmPelsWidth = paRects[i].right - paRects[i].left;
471 paDeviceModes[i].dmPelsHeight = paRects[i].bottom - paRects[i].top;
472
473 paDeviceModes[i].dmFields = DM_POSITION | DM_PELSHEIGHT | DM_PELSWIDTH;
474
475 if ( i == Id
476 && BitsPerPixel != 0)
477 {
478 paDeviceModes[i].dmFields |= DM_BITSPERPEL;
479 paDeviceModes[i].dmBitsPerPel = BitsPerPixel;
480 }
481 Log(("calling pfnChangeDisplaySettingsEx %x\n", gpfnChangeDisplaySettingsEx));
482 gpfnChangeDisplaySettingsEx((LPSTR)paDisplayDevices[i].DeviceName,
483 &paDeviceModes[i], NULL, CDS_NORESET | CDS_UPDATEREGISTRY, NULL);
484 Log(("ChangeDisplaySettings position err %d\n", GetLastError ()));
485 }
486
487 /* A second call to ChangeDisplaySettings updates the monitor. */
488 LONG status = ChangeDisplaySettings(NULL, 0);
489 Log(("ChangeDisplaySettings update status %d\n", status));
490 if (status == DISP_CHANGE_SUCCESSFUL || status == DISP_CHANGE_BADMODE)
491 {
492 /* Successfully set new video mode or our driver can not set the requested mode. Stop trying. */
493 return FALSE;
494 }
495
496 /* Retry the request. */
497 return TRUE;
498}
499
500int handleSetVideoMode(int argc, char *argv[])
501{
502 if (argc != 3 && argc != 4)
503 {
504 usage(SET_VIDEO_MODE);
505 return 1;
506 }
507
508 DWORD xres = atoi(argv[0]);
509 DWORD yres = atoi(argv[1]);
510 DWORD bpp = atoi(argv[2]);
511 DWORD scr = 0;
512
513 if (argc == 4)
514 {
515 scr = atoi(argv[3]);
516 }
517
518 HMODULE hUser = GetModuleHandle("USER32");
519
520 if (hUser)
521 {
522 *(uintptr_t *)&gpfnChangeDisplaySettingsEx = (uintptr_t)GetProcAddress(hUser, "ChangeDisplaySettingsExA");
523 Log(("VBoxService: pChangeDisplaySettingsEx = %p\n", gpfnChangeDisplaySettingsEx));
524
525 if (gpfnChangeDisplaySettingsEx)
526 {
527 /* The screen index is 0 based in the ResizeDisplayDevice call. */
528 scr = scr > 0? scr - 1: 0;
529
530 /* Horizontal resolution must be a multiple of 8, round down. */
531 xres &= ~0x7;
532
533 ResizeDisplayDevice(scr, xres, yres, bpp);
534 }
535 }
536 return 0;
537}
538
539HKEY getVideoKey(bool writable)
540{
541 HKEY hkeyDeviceMap = 0;
542 HKEY hkeyVideo = 0;
543 LONG status;
544
545 status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\VIDEO", 0, KEY_READ, &hkeyDeviceMap);
546 if ((status != ERROR_SUCCESS) || !hkeyDeviceMap)
547 {
548 VBoxControlError("Error opening video device map registry key!\n");
549 return 0;
550 }
551 char szVideoLocation[256];
552 DWORD dwKeyType;
553 szVideoLocation[0] = 0;
554 DWORD len = sizeof(szVideoLocation);
555 status = RegQueryValueExA(hkeyDeviceMap, "\\Device\\Video0", NULL, &dwKeyType, (LPBYTE)szVideoLocation, &len);
556 /*
557 * This value will start with a weird value: \REGISTRY\Machine
558 * Make sure this is true.
559 */
560 if ( (status == ERROR_SUCCESS)
561 && (dwKeyType == REG_SZ)
562 && (_strnicmp(szVideoLocation, "\\REGISTRY\\Machine", 17) == 0))
563 {
564 /* open that branch */
565 status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, &szVideoLocation[18], 0, KEY_READ | (writable ? KEY_WRITE : 0), &hkeyVideo);
566 }
567 else
568 {
569 VBoxControlError("Error opening registry key '%s'\n", &szVideoLocation[18]);
570 }
571 RegCloseKey(hkeyDeviceMap);
572 return hkeyVideo;
573}
574
575int handleGetVideoAcceleration(int argc, char *argv[])
576{
577 ULONG status;
578 HKEY hkeyVideo = getVideoKey(false);
579
580 if (hkeyVideo)
581 {
582 /* query the actual value */
583 DWORD fAcceleration = 1;
584 DWORD len = sizeof(fAcceleration);
585 DWORD dwKeyType;
586 status = RegQueryValueExA(hkeyVideo, "EnableVideoAccel", NULL, &dwKeyType, (LPBYTE)&fAcceleration, &len);
587 if (status != ERROR_SUCCESS)
588 RTPrintf("Video acceleration: default\n");
589 else
590 RTPrintf("Video acceleration: %s\n", fAcceleration ? "on" : "off");
591 RegCloseKey(hkeyVideo);
592 }
593 return 0;
594}
595
596int handleSetVideoAcceleration(int argc, char *argv[])
597{
598 ULONG status;
599 HKEY hkeyVideo;
600
601 /* must have exactly one argument: the new offset */
602 if ( (argc != 1)
603 || ( strcmp(argv[0], "on")
604 && strcmp(argv[0], "off")))
605 {
606 usage(SET_VIDEO_ACCEL);
607 return 1;
608 }
609
610 hkeyVideo = getVideoKey(true);
611
612 if (hkeyVideo)
613 {
614 int fAccel = 0;
615 if (!strcmp(argv[0], "on"))
616 fAccel = 1;
617 /* set a new value */
618 status = RegSetValueExA(hkeyVideo, "EnableVideoAccel", 0, REG_DWORD, (LPBYTE)&fAccel, sizeof(fAccel));
619 if (status != ERROR_SUCCESS)
620 {
621 VBoxControlError("Error %d writing video acceleration status!\n", status);
622 }
623 RegCloseKey(hkeyVideo);
624 }
625 return 0;
626}
627
628#define MAX_CUSTOM_MODES 128
629
630/* the table of custom modes */
631struct
632{
633 DWORD xres;
634 DWORD yres;
635 DWORD bpp;
636} customModes[MAX_CUSTOM_MODES] = {0};
637
638void getCustomModes(HKEY hkeyVideo)
639{
640 ULONG status;
641 int curMode = 0;
642
643 /* null out the table */
644 memset(customModes, 0, sizeof(customModes));
645
646 do
647 {
648 char valueName[20];
649 DWORD xres, yres, bpp = 0;
650 DWORD dwType;
651 DWORD dwLen = sizeof(DWORD);
652
653 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dWidth", curMode);
654 status = RegQueryValueExA(hkeyVideo, valueName, NULL, &dwType, (LPBYTE)&xres, &dwLen);
655 if (status != ERROR_SUCCESS)
656 break;
657 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dHeight", curMode);
658 status = RegQueryValueExA(hkeyVideo, valueName, NULL, &dwType, (LPBYTE)&yres, &dwLen);
659 if (status != ERROR_SUCCESS)
660 break;
661 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dBPP", curMode);
662 status = RegQueryValueExA(hkeyVideo, valueName, NULL, &dwType, (LPBYTE)&bpp, &dwLen);
663 if (status != ERROR_SUCCESS)
664 break;
665
666 /* check if the mode is OK */
667 if ( (xres > (1 << 16))
668 && (yres > (1 << 16))
669 && ( (bpp != 16)
670 || (bpp != 24)
671 || (bpp != 32)))
672 break;
673
674 /* add mode to table */
675 customModes[curMode].xres = xres;
676 customModes[curMode].yres = yres;
677 customModes[curMode].bpp = bpp;
678
679 ++curMode;
680
681 if (curMode >= MAX_CUSTOM_MODES)
682 break;
683 } while(1);
684}
685
686void writeCustomModes(HKEY hkeyVideo)
687{
688 ULONG status;
689 int tableIndex = 0;
690 int modeIndex = 0;
691
692 /* first remove all values */
693 for (int i = 0; i < MAX_CUSTOM_MODES; i++)
694 {
695 char valueName[20];
696 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dWidth", i);
697 RegDeleteValueA(hkeyVideo, valueName);
698 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dHeight", i);
699 RegDeleteValueA(hkeyVideo, valueName);
700 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dBPP", i);
701 RegDeleteValueA(hkeyVideo, valueName);
702 }
703
704 do
705 {
706 if (tableIndex >= MAX_CUSTOM_MODES)
707 break;
708
709 /* is the table entry present? */
710 if ( (!customModes[tableIndex].xres)
711 || (!customModes[tableIndex].yres)
712 || (!customModes[tableIndex].bpp))
713 {
714 tableIndex++;
715 continue;
716 }
717
718 RTPrintf("writing mode %d (%dx%dx%d)\n", modeIndex, customModes[tableIndex].xres, customModes[tableIndex].yres, customModes[tableIndex].bpp);
719 char valueName[20];
720 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dWidth", modeIndex);
721 status = RegSetValueExA(hkeyVideo, valueName, 0, REG_DWORD, (LPBYTE)&customModes[tableIndex].xres,
722 sizeof(customModes[tableIndex].xres));
723 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dHeight", modeIndex);
724 RegSetValueExA(hkeyVideo, valueName, 0, REG_DWORD, (LPBYTE)&customModes[tableIndex].yres,
725 sizeof(customModes[tableIndex].yres));
726 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dBPP", modeIndex);
727 RegSetValueExA(hkeyVideo, valueName, 0, REG_DWORD, (LPBYTE)&customModes[tableIndex].bpp,
728 sizeof(customModes[tableIndex].bpp));
729
730 modeIndex++;
731 tableIndex++;
732
733 } while(1);
734
735}
736
737int handleListCustomModes(int argc, char *argv[])
738{
739 if (argc != 0)
740 {
741 usage(LIST_CUST_MODES);
742 return 1;
743 }
744
745 HKEY hkeyVideo = getVideoKey(false);
746
747 if (hkeyVideo)
748 {
749 getCustomModes(hkeyVideo);
750 for (int i = 0; i < (sizeof(customModes) / sizeof(customModes[0])); i++)
751 {
752 if ( !customModes[i].xres
753 || !customModes[i].yres
754 || !customModes[i].bpp)
755 continue;
756
757 RTPrintf("Mode: %d x %d x %d\n",
758 customModes[i].xres, customModes[i].yres, customModes[i].bpp);
759 }
760 RegCloseKey(hkeyVideo);
761 }
762 return 0;
763}
764
765int handleAddCustomMode(int argc, char *argv[])
766{
767 if (argc != 3)
768 {
769 usage(ADD_CUST_MODE);
770 return 1;
771 }
772
773 DWORD xres = atoi(argv[0]);
774 DWORD yres = atoi(argv[1]);
775 DWORD bpp = atoi(argv[2]);
776
777 /** @todo better check including xres mod 8 = 0! */
778 if ( (xres > (1 << 16))
779 && (yres > (1 << 16))
780 && ( (bpp != 16)
781 || (bpp != 24)
782 || (bpp != 32)))
783 {
784 VBoxControlError("Error: invalid mode specified!\n");
785 return 1;
786 }
787
788 HKEY hkeyVideo = getVideoKey(true);
789
790 if (hkeyVideo)
791 {
792 int i;
793 int fModeExists = 0;
794 getCustomModes(hkeyVideo);
795 for (i = 0; i < MAX_CUSTOM_MODES; i++)
796 {
797 /* mode exists? */
798 if ( customModes[i].xres == xres
799 && customModes[i].yres == yres
800 && customModes[i].bpp == bpp
801 )
802 {
803 fModeExists = 1;
804 }
805 }
806 if (!fModeExists)
807 {
808 for (i = 0; i < MAX_CUSTOM_MODES; i++)
809 {
810 /* item free? */
811 if (!customModes[i].xres)
812 {
813 customModes[i].xres = xres;
814 customModes[i].yres = yres;
815 customModes[i].bpp = bpp;
816 break;
817 }
818 }
819 writeCustomModes(hkeyVideo);
820 }
821 RegCloseKey(hkeyVideo);
822 }
823 return 0;
824}
825
826int handleRemoveCustomMode(int argc, char *argv[])
827{
828 if (argc != 3)
829 {
830 usage(REMOVE_CUST_MODE);
831 return 1;
832 }
833
834 DWORD xres = atoi(argv[0]);
835 DWORD yres = atoi(argv[1]);
836 DWORD bpp = atoi(argv[2]);
837
838 HKEY hkeyVideo = getVideoKey(true);
839
840 if (hkeyVideo)
841 {
842 getCustomModes(hkeyVideo);
843 for (int i = 0; i < MAX_CUSTOM_MODES; i++)
844 {
845 /* correct item? */
846 if ( (customModes[i].xres == xres)
847 && (customModes[i].yres == yres)
848 && (customModes[i].bpp == bpp))
849 {
850 RTPrintf("found mode at index %d\n", i);
851 memset(&customModes[i], 0, sizeof(customModes[i]));
852 break;
853 }
854 }
855 writeCustomModes(hkeyVideo);
856 RegCloseKey(hkeyVideo);
857 }
858}
859
860#endif /* RT_OS_WINDOWS */
861
862#ifdef VBOX_WITH_GUEST_PROPS
863/**
864 * Retrieves a value from the guest property store.
865 * This is accessed through the "VBoxGuestPropSvc" HGCM service.
866 *
867 * @returns 0 on success, 1 on failure
868 * @note see the command line API description for parameters
869 */
870int getGuestProperty(int argc, char **argv)
871{
872 using namespace guestProp;
873
874 bool verbose = false;
875 if ((2 == argc) && (0 == strcmp(argv[1], "-verbose")))
876 verbose = true;
877 else if (argc != 1)
878 {
879 usage(GUEST_PROP);
880 return 1;
881 }
882
883 uint32_t u32ClientId = 0;
884 int rc = VINF_SUCCESS;
885
886 rc = VbglR3GuestPropConnect(&u32ClientId);
887 if (!RT_SUCCESS(rc))
888 VBoxControlError("Failed to connect to the guest property service, error %Rrc\n", rc);
889
890/*
891 * Here we actually retrieve the value from the host.
892 */
893 const char *pszName = argv[0];
894 char *pszValue = NULL;
895 uint64_t u64Timestamp = 0;
896 char *pszFlags = NULL;
897 if (RT_SUCCESS(rc))
898 {
899 /* Because there is a race condition between our reading the size of a
900 * property and the guest updating it, we loop a few times here and
901 * hope. Actually this should never go wrong, as we are generous
902 * enough with buffer space. */
903 bool finish = false;
904 /* We leave a bit of space here in case the maximum values are raised. */
905 void *pvBuf = NULL;
906 uint32_t cbBuf = MAX_VALUE_LEN + MAX_FLAGS_LEN + 1024;
907 for (unsigned i = 0; (i < 10) && !finish; ++i)
908 {
909 void *pvTmpBuf = RTMemRealloc(pvBuf, cbBuf);
910 if (NULL == pvTmpBuf)
911 {
912 RTMemFree(pvBuf);
913 rc = VERR_NO_MEMORY;
914 VBoxControlError("Out of memory\n");
915 }
916 else
917 {
918 pvBuf = pvTmpBuf;
919 rc = VbglR3GuestPropRead(u32ClientId, pszName, pvBuf, cbBuf,
920 &pszValue, &u64Timestamp, &pszFlags,
921 &cbBuf);
922 }
923 if (VERR_BUFFER_OVERFLOW == rc)
924 /* Leave a bit of extra space to be safe */
925 cbBuf += 1024;
926 else
927 finish = true;
928 }
929 if (VERR_TOO_MUCH_DATA == rc)
930 VBoxControlError("Temporarily unable to retrieve the property\n");
931 else if (!RT_SUCCESS(rc) && (rc != VERR_NOT_FOUND))
932 VBoxControlError("Failed to retrieve the property value, error %Rrc\n", rc);
933 }
934/*
935 * And display it on the guest console.
936 */
937 if (VERR_NOT_FOUND == rc)
938 RTPrintf("No value set!\n");
939 if (RT_SUCCESS(rc))
940 RTPrintf("Value: %S\n", pszValue);
941 if (RT_SUCCESS(rc) && verbose)
942 {
943 RTPrintf("Timestamp: %lld ns\n", u64Timestamp);
944 RTPrintf("Flags: %S\n", pszFlags);
945 }
946
947 if (u32ClientId != 0)
948 VbglR3GuestPropDisconnect(u32ClientId);
949 return RT_SUCCESS(rc) ? 0 : 1;
950}
951
952
953/**
954 * Writes a value to the guest property store.
955 * This is accessed through the "VBoxGuestPropSvc" HGCM service.
956 *
957 * @returns 0 on success, 1 on failure
958 * @note see the command line API description for parameters
959 */
960static int setGuestProperty(int argc, char *argv[])
961{
962/*
963 * Check the syntax. We can deduce the correct syntax from the number of
964 * arguments.
965 */
966 bool usageOK = true;
967 const char *pszName = NULL;
968 const char *pszValue = NULL;
969 const char *pszFlags = NULL;
970 if (2 == argc)
971 {
972 pszName = argv[0];
973 pszValue = argv[1];
974 }
975 else if (3 == argc)
976 {
977 pszName = argv[0];
978 if (strcmp(argv[1], "-flags") != 0)
979 usageOK = false;
980 pszFlags = argv[2];
981 }
982 else if (4 == argc)
983 {
984 pszName = argv[0];
985 pszValue = argv[1];
986 if (strcmp(argv[2], "-flags") != 0)
987 usageOK = false;
988 pszFlags = argv[3];
989 }
990 else if (argc != 1)
991 usageOK = false;
992 if (!usageOK)
993 {
994 usage(GUEST_PROP);
995 return 1;
996 }
997
998/*
999 * Do the actual setting.
1000 */
1001 uint32_t u32ClientId = 0;
1002 int rc = VINF_SUCCESS;
1003 rc = VbglR3GuestPropConnect(&u32ClientId);
1004 if (!RT_SUCCESS(rc))
1005 VBoxControlError("Failed to connect to the guest property service, error %Rrc\n", rc);
1006 if (RT_SUCCESS(rc))
1007 {
1008 if (pszFlags != NULL)
1009 rc = VbglR3GuestPropWrite(u32ClientId, pszName, pszValue, pszFlags);
1010 else
1011 rc = VbglR3GuestPropWriteValue(u32ClientId, pszName, pszValue);
1012 if (!RT_SUCCESS(rc))
1013 VBoxControlError("Failed to store the property value, error %Rrc\n", rc);
1014 }
1015
1016 if (u32ClientId != 0)
1017 VbglR3GuestPropDisconnect(u32ClientId);
1018 return RT_SUCCESS(rc) ? 0 : 1;
1019}
1020
1021
1022/**
1023 * Access the guest property store through the "VBoxGuestPropSvc" HGCM
1024 * service.
1025 *
1026 * @returns 0 on success, 1 on failure
1027 * @note see the command line API description for parameters
1028 */
1029static int handleGuestProperty(int argc, char *argv[])
1030{
1031 if (0 == argc)
1032 {
1033 usage(GUEST_PROP);
1034 return 1;
1035 }
1036 if (0 == strcmp(argv[0], "get"))
1037 return getGuestProperty(argc - 1, argv + 1);
1038 else if (0 == strcmp(argv[0], "set"))
1039 return setGuestProperty(argc - 1, argv + 1);
1040 /* else */
1041 usage(GUEST_PROP);
1042 return 1;
1043}
1044
1045#endif
1046
1047/** command handler type */
1048typedef DECLCALLBACK(int) FNHANDLER(int argc, char *argv[]);
1049typedef FNHANDLER *PFNHANDLER;
1050
1051/** The table of all registered command handlers. */
1052struct COMMANDHANDLER
1053{
1054 const char *command;
1055 PFNHANDLER handler;
1056} g_commandHandlers[] =
1057{
1058#ifdef RT_OS_WINDOWS
1059 { "getvideoacceleration", handleGetVideoAcceleration },
1060 { "setvideoacceleration", handleSetVideoAcceleration },
1061 { "listcustommodes", handleListCustomModes },
1062 { "addcustommode", handleAddCustomMode },
1063 { "removecustommode", handleRemoveCustomMode },
1064 { "setvideomode", handleSetVideoMode },
1065#endif
1066#ifdef VBOX_WITH_GUEST_PROPS
1067 { "guestproperty", handleGuestProperty },
1068#endif
1069 { NULL, NULL } /* terminator */
1070};
1071
1072/** Main function */
1073int main(int argc, char **argv)
1074{
1075 /** The application's global return code */
1076 int rc = 0;
1077 /** An IPRT return code for local use */
1078 int rrc = VINF_SUCCESS;
1079 /** The index of the command line argument we are currently processing */
1080 int iArg = 1;
1081 /** Should we show the logo text? */
1082 bool showlogo = true;
1083 /** Should we print the usage after the logo? For the -help switch. */
1084 bool dohelp = false;
1085 /** Will we be executing a command or just printing information? */
1086 bool onlyinfo = false;
1087
1088/*
1089 * Start by handling command line switches
1090 */
1091
1092 /** Are we finished with handling switches? */
1093 bool done = false;
1094 while (!done && (iArg < argc))
1095 {
1096 if ( (0 == strcmp(argv[iArg], "-v"))
1097 || (0 == strcmp(argv[iArg], "--version"))
1098 || (0 == strcmp(argv[iArg], "-version"))
1099 || (0 == strcmp(argv[iArg], "getversion"))
1100 )
1101 {
1102 /* Print version number, and do nothing else. */
1103 RTPrintf("%sr%d\n", VBOX_VERSION_STRING, VBoxSVNRev ());
1104 onlyinfo = true;
1105 showlogo = false;
1106 done = true;
1107 }
1108 else if (0 == strcmp(argv[iArg], "-nologo"))
1109 showlogo = false;
1110 else if (0 == strcmp(argv[iArg], "-help"))
1111 {
1112 onlyinfo = true;
1113 dohelp = true;
1114 done = true;
1115 }
1116 else
1117 /* We have found an argument which isn't a switch. Exit to the
1118 * command processing bit. */
1119 done = true;
1120 if (!done)
1121 ++iArg;
1122 }
1123
1124/*
1125 * Find the application name, show our logo if the user hasn't suppressed it,
1126 * and show the usage if the user asked us to
1127 */
1128
1129 g_pszProgName = RTPathFilename(argv[0]);
1130 if (showlogo)
1131 RTPrintf("VirtualBox Guest Additions Command Line Management Interface Version "
1132 VBOX_VERSION_STRING "\n"
1133 "(C) 2008 Sun Microsystems, Inc.\n"
1134 "All rights reserved\n\n");
1135 if (dohelp)
1136 usage();
1137
1138/*
1139 * Do global initialisation for the programme if we will be handling a command
1140 */
1141
1142 if (!onlyinfo)
1143 {
1144 rrc = RTR3Init(false, 0);
1145 if (!RT_SUCCESS(rrc))
1146 {
1147 VBoxControlError("Failed to initialise the VirtualBox runtime - error %Rrc\n", rrc);
1148 rc = 1;
1149 }
1150 if (0 == rc)
1151 {
1152 if (!RT_SUCCESS(VbglR3Init()))
1153 {
1154 VBoxControlError("Could not contact the host system. Make sure that you are running this\n"
1155 "application inside a VirtualBox guest system, and that you have sufficient\n"
1156 "user permissions.\n");
1157 rc = 1;
1158 }
1159 }
1160 }
1161
1162/*
1163 * Now look for an actual command in the argument list and handle it.
1164 */
1165
1166 if (!onlyinfo && (0 == rc))
1167 {
1168 /*
1169 * The input is in the guest OS'es codepage (NT guarantees ACP).
1170 * For VBox we use UTF-8. For simplicity, just convert the argv[] array
1171 * here.
1172 */
1173 for (int i = iArg; i < argc; i++)
1174 {
1175 char *converted;
1176 RTStrCurrentCPToUtf8(&converted, argv[i]);
1177 argv[i] = converted;
1178 }
1179
1180 if (argc > iArg)
1181 {
1182 /** Is next parameter a known command? */
1183 bool found = false;
1184 /** And if so, what is its position in the table? */
1185 unsigned index = 0;
1186 while ( index < RT_ELEMENTS(g_commandHandlers)
1187 && !found
1188 && (g_commandHandlers[index].command != NULL))
1189 {
1190 if (0 == strcmp(argv[iArg], g_commandHandlers[index].command))
1191 found = true;
1192 else
1193 ++index;
1194 }
1195 if (found)
1196 rc = g_commandHandlers[index].handler(argc - iArg - 1, argv + iArg + 1);
1197 else
1198 {
1199 rc = 1;
1200 usage();
1201 }
1202 }
1203 else
1204 {
1205 /* The user didn't specify a command. */
1206 rc = 1;
1207 usage();
1208 }
1209
1210 /*
1211 * Free converted argument vector
1212 */
1213 for (int i = iArg; i < argc; i++)
1214 RTStrFree(argv[i]);
1215
1216 }
1217
1218/*
1219 * And exit, returning the status
1220 */
1221
1222 return rc;
1223}
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