VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxSDL/VBoxSDL.cpp@ 98372

Last change on this file since 98372 was 98372, checked in by vboxsync, 22 months ago

FE/SDL: Fixed compilation on Linux (got broken in r155613). ​bugref:9449

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 141.1 KB
Line 
1/* $Id: VBoxSDL.cpp 98372 2023-02-01 09:10:40Z vboxsync $ */
2/** @file
3 * VBox frontends: VBoxSDL (simple frontend based on SDL):
4 * Main code
5 */
6
7/*
8 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
9 *
10 * This file is part of VirtualBox base platform packages, as
11 * available from https://www.virtualbox.org.
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation, in version 3 of the
16 * License.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, see <https://www.gnu.org/licenses>.
25 *
26 * SPDX-License-Identifier: GPL-3.0-only
27 */
28
29
30/*********************************************************************************************************************************
31* Header Files *
32*********************************************************************************************************************************/
33#define LOG_GROUP LOG_GROUP_GUI
34
35#include <iprt/stream.h>
36
37#include <VBox/com/com.h>
38#include <VBox/com/string.h>
39#include <VBox/com/Guid.h>
40#include <VBox/com/array.h>
41#include <VBox/com/ErrorInfo.h>
42#include <VBox/com/errorprint.h>
43
44#include <VBox/com/NativeEventQueue.h>
45#include <VBox/com/VirtualBox.h>
46
47using namespace com;
48
49#if defined(VBOXSDL_WITH_X11)
50# include <VBox/VBoxKeyboard.h>
51
52# include <X11/Xlib.h>
53# include <X11/cursorfont.h> /* for XC_left_ptr */
54# if !defined(VBOX_WITHOUT_XCURSOR)
55# include <X11/Xcursor/Xcursor.h>
56# endif
57# include <unistd.h>
58#endif
59
60#include "VBoxSDL.h"
61
62#ifdef _MSC_VER
63# pragma warning(push)
64# pragma warning(disable: 4121) /* warning C4121: 'SDL_SysWMmsg' : alignment of a member was sensitive to packing*/
65#endif
66#ifndef RT_OS_DARWIN
67# include <SDL_syswm.h> /* for SDL_GetWMInfo() */
68#endif
69#ifdef _MSC_VER
70# pragma warning(pop)
71#endif
72
73#include "Framebuffer.h"
74#include "Helper.h"
75
76#include <VBox/types.h>
77#include <VBox/err.h>
78#include <VBox/param.h>
79#include <VBox/log.h>
80#include <VBox/version.h>
81#include <VBoxVideo.h>
82#include <VBox/com/listeners.h>
83
84#include <iprt/alloca.h>
85#include <iprt/asm.h>
86#include <iprt/assert.h>
87#include <iprt/ctype.h>
88#include <iprt/env.h>
89#include <iprt/file.h>
90#include <iprt/ldr.h>
91#include <iprt/initterm.h>
92#include <iprt/message.h>
93#include <iprt/path.h>
94#include <iprt/process.h>
95#include <iprt/semaphore.h>
96#include <iprt/string.h>
97#include <iprt/stream.h>
98#include <iprt/uuid.h>
99
100#include <signal.h>
101
102#include <vector>
103#include <list>
104
105#include "PasswordInput.h"
106
107/* Xlib would re-define our enums */
108#undef True
109#undef False
110
111
112/*********************************************************************************************************************************
113* Defined Constants And Macros *
114*********************************************************************************************************************************/
115/** Enables the rawr[0|3], patm, and casm options. */
116#define VBOXSDL_ADVANCED_OPTIONS
117
118
119/*********************************************************************************************************************************
120* Structures and Typedefs *
121*********************************************************************************************************************************/
122/** Pointer shape change event data structure */
123struct PointerShapeChangeData
124{
125 PointerShapeChangeData(BOOL aVisible, BOOL aAlpha, ULONG aXHot, ULONG aYHot,
126 ULONG aWidth, ULONG aHeight, ComSafeArrayIn(BYTE,pShape))
127 : visible(aVisible), alpha(aAlpha), xHot(aXHot), yHot(aYHot),
128 width(aWidth), height(aHeight)
129 {
130 // make a copy of the shape
131 com::SafeArray<BYTE> aShape(ComSafeArrayInArg(pShape));
132 size_t cbShapeSize = aShape.size();
133 if (cbShapeSize > 0)
134 {
135 shape.resize(cbShapeSize);
136 ::memcpy(shape.raw(), aShape.raw(), cbShapeSize);
137 }
138 }
139
140 ~PointerShapeChangeData()
141 {
142 }
143
144 const BOOL visible;
145 const BOOL alpha;
146 const ULONG xHot;
147 const ULONG yHot;
148 const ULONG width;
149 const ULONG height;
150 com::SafeArray<BYTE> shape;
151};
152
153enum TitlebarMode
154{
155 TITLEBAR_NORMAL = 1,
156 TITLEBAR_STARTUP = 2,
157 TITLEBAR_SAVE = 3,
158 TITLEBAR_SNAPSHOT = 4
159};
160
161
162/*********************************************************************************************************************************
163* Internal Functions *
164*********************************************************************************************************************************/
165static bool UseAbsoluteMouse(void);
166static void ResetKeys(void);
167static void ProcessKey(SDL_KeyboardEvent *ev);
168static void InputGrabStart(void);
169static void InputGrabEnd(void);
170static void SendMouseEvent(VBoxSDLFB *fb, int dz, int button, int down);
171static void UpdateTitlebar(TitlebarMode mode, uint32_t u32User = 0);
172static void SetPointerShape(const PointerShapeChangeData *data);
173static void HandleGuestCapsChanged(void);
174static int HandleHostKey(const SDL_KeyboardEvent *pEv);
175static Uint32 StartupTimer(Uint32 interval, void *param) RT_NOTHROW_PROTO;
176static Uint32 ResizeTimer(Uint32 interval, void *param) RT_NOTHROW_PROTO;
177static Uint32 QuitTimer(Uint32 interval, void *param) RT_NOTHROW_PROTO;
178static int WaitSDLEvent(SDL_Event *event);
179static void SetFullscreen(bool enable);
180static VBoxSDLFB *getFbFromWinId(Uint32 id);
181
182
183/*********************************************************************************************************************************
184* Global Variables *
185*********************************************************************************************************************************/
186static int gHostKeyMod = KMOD_RCTRL;
187static int gHostKeySym1 = SDLK_RCTRL;
188static int gHostKeySym2 = SDLK_UNKNOWN;
189static const char *gHostKeyDisabledCombinations = "";
190static const char *gpszPidFile;
191static BOOL gfGrabbed = FALSE;
192static BOOL gfGrabOnMouseClick = TRUE;
193static BOOL gfFullscreenResize = FALSE;
194static BOOL gfIgnoreNextResize = FALSE;
195static BOOL gfAllowFullscreenToggle = TRUE;
196static BOOL gfAbsoluteMouseHost = FALSE;
197static BOOL gfAbsoluteMouseGuest = FALSE;
198static BOOL gfRelativeMouseGuest = TRUE;
199static BOOL gfGuestNeedsHostCursor = FALSE;
200static BOOL gfOffCursorActive = FALSE;
201static BOOL gfGuestNumLockPressed = FALSE;
202static BOOL gfGuestCapsLockPressed = FALSE;
203static BOOL gfGuestScrollLockPressed = FALSE;
204static BOOL gfACPITerm = FALSE;
205#if defined(VBOXSDL_WITH_X11) && !defined(VBOX_WITHOUT_XCURSOR)
206 static BOOL gfXCursorEnabled = FALSE;
207#endif
208static int gcGuestNumLockAdaptions = 2;
209static int gcGuestCapsLockAdaptions = 2;
210static uint32_t gmGuestNormalXRes;
211static uint32_t gmGuestNormalYRes;
212
213/** modifier keypress status (scancode as index) */
214static uint8_t gaModifiersState[256];
215
216static ComPtr<IMachine> gpMachine;
217static ComPtr<IConsole> gpConsole;
218static ComPtr<IMachineDebugger> gpMachineDebugger;
219static ComPtr<IKeyboard> gpKeyboard;
220static ComPtr<IMouse> gpMouse;
221ComPtr<IDisplay> gpDisplay;
222static ComPtr<IVRDEServer> gpVRDEServer;
223static ComPtr<IProgress> gpProgress;
224
225static ULONG gcMonitors = 1;
226static ComObjPtr<VBoxSDLFB> gpFramebuffer[64];
227static Bstr gaFramebufferId[64];
228static SDL_Cursor *gpDefaultCursor = NULL;
229static SDL_Cursor *gpOffCursor = NULL;
230static SDL_TimerID gSdlResizeTimer = 0;
231static SDL_TimerID gSdlQuitTimer = 0;
232
233static RTSEMEVENT g_EventSemSDLEvents;
234static volatile int32_t g_cNotifyUpdateEventsPending;
235
236/**
237 * Event handler for VirtualBoxClient events
238 */
239class VBoxSDLClientEventListener
240{
241public:
242 VBoxSDLClientEventListener()
243 {
244 }
245
246 virtual ~VBoxSDLClientEventListener()
247 {
248 }
249
250 HRESULT init()
251 {
252 return S_OK;
253 }
254
255 void uninit()
256 {
257 }
258
259 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent * aEvent)
260 {
261 switch (aType)
262 {
263 case VBoxEventType_OnVBoxSVCAvailabilityChanged:
264 {
265 ComPtr<IVBoxSVCAvailabilityChangedEvent> pVSACEv = aEvent;
266 Assert(pVSACEv);
267 BOOL fAvailable = FALSE;
268 pVSACEv->COMGETTER(Available)(&fAvailable);
269 if (!fAvailable)
270 {
271 LogRel(("VBoxSDL: VBoxSVC became unavailable, exiting.\n"));
272 RTPrintf("VBoxSVC became unavailable, exiting.\n");
273 /* Send QUIT event to terminate the VM as cleanly as possible
274 * given that VBoxSVC is no longer present. */
275 SDL_Event event = {0};
276 event.type = SDL_QUIT;
277 PushSDLEventForSure(&event);
278 }
279 break;
280 }
281
282 default:
283 AssertFailed();
284 }
285
286 return S_OK;
287 }
288};
289
290/**
291 * Event handler for VirtualBox (server) events
292 */
293class VBoxSDLEventListener
294{
295public:
296 VBoxSDLEventListener()
297 {
298 }
299
300 virtual ~VBoxSDLEventListener()
301 {
302 }
303
304 HRESULT init()
305 {
306 return S_OK;
307 }
308
309 void uninit()
310 {
311 }
312
313 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent * aEvent)
314 {
315 RT_NOREF(aEvent);
316 switch (aType)
317 {
318 case VBoxEventType_OnExtraDataChanged:
319 break;
320 default:
321 AssertFailed();
322 }
323
324 return S_OK;
325 }
326};
327
328/**
329 * Event handler for Console events
330 */
331class VBoxSDLConsoleEventListener
332{
333public:
334 VBoxSDLConsoleEventListener() : m_fIgnorePowerOffEvents(false)
335 {
336 }
337
338 virtual ~VBoxSDLConsoleEventListener()
339 {
340 }
341
342 HRESULT init()
343 {
344 return S_OK;
345 }
346
347 void uninit()
348 {
349 }
350
351 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent * aEvent)
352 {
353 // likely all this double copy is now excessive, and we can just use existing event object
354 /// @todo eliminate it
355 switch (aType)
356 {
357 case VBoxEventType_OnMousePointerShapeChanged:
358 {
359 ComPtr<IMousePointerShapeChangedEvent> pMPSCEv = aEvent;
360 Assert(pMPSCEv);
361 PointerShapeChangeData *data;
362 BOOL visible, alpha;
363 ULONG xHot, yHot, width, height;
364 com::SafeArray<BYTE> shape;
365
366 pMPSCEv->COMGETTER(Visible)(&visible);
367 pMPSCEv->COMGETTER(Alpha)(&alpha);
368 pMPSCEv->COMGETTER(Xhot)(&xHot);
369 pMPSCEv->COMGETTER(Yhot)(&yHot);
370 pMPSCEv->COMGETTER(Width)(&width);
371 pMPSCEv->COMGETTER(Height)(&height);
372 pMPSCEv->COMGETTER(Shape)(ComSafeArrayAsOutParam(shape));
373 data = new PointerShapeChangeData(visible, alpha, xHot, yHot, width, height,
374 ComSafeArrayAsInParam(shape));
375 Assert(data);
376 if (!data)
377 break;
378
379 SDL_Event event = {0};
380 event.type = SDL_USEREVENT;
381 event.user.type = SDL_USER_EVENT_POINTER_CHANGE;
382 event.user.data1 = data;
383
384 int rc = PushSDLEventForSure(&event);
385 if (rc)
386 delete data;
387
388 break;
389 }
390 case VBoxEventType_OnMouseCapabilityChanged:
391 {
392 ComPtr<IMouseCapabilityChangedEvent> pMCCEv = aEvent;
393 Assert(pMCCEv);
394 pMCCEv->COMGETTER(SupportsAbsolute)(&gfAbsoluteMouseGuest);
395 pMCCEv->COMGETTER(SupportsRelative)(&gfRelativeMouseGuest);
396 pMCCEv->COMGETTER(NeedsHostCursor)(&gfGuestNeedsHostCursor);
397 SDL_Event event = {0};
398 event.type = SDL_USEREVENT;
399 event.user.type = SDL_USER_EVENT_GUEST_CAP_CHANGED;
400
401 PushSDLEventForSure(&event);
402 break;
403 }
404 case VBoxEventType_OnKeyboardLedsChanged:
405 {
406 ComPtr<IKeyboardLedsChangedEvent> pCLCEv = aEvent;
407 Assert(pCLCEv);
408 BOOL fNumLock, fCapsLock, fScrollLock;
409 pCLCEv->COMGETTER(NumLock)(&fNumLock);
410 pCLCEv->COMGETTER(CapsLock)(&fCapsLock);
411 pCLCEv->COMGETTER(ScrollLock)(&fScrollLock);
412 /* Don't bother the guest with NumLock scancodes if he doesn't set the NumLock LED */
413 if (gfGuestNumLockPressed != fNumLock)
414 gcGuestNumLockAdaptions = 2;
415 if (gfGuestCapsLockPressed != fCapsLock)
416 gcGuestCapsLockAdaptions = 2;
417 gfGuestNumLockPressed = fNumLock;
418 gfGuestCapsLockPressed = fCapsLock;
419 gfGuestScrollLockPressed = fScrollLock;
420 break;
421 }
422
423 case VBoxEventType_OnStateChanged:
424 {
425 ComPtr<IStateChangedEvent> pSCEv = aEvent;
426 Assert(pSCEv);
427 MachineState_T machineState;
428 pSCEv->COMGETTER(State)(&machineState);
429 LogFlow(("OnStateChange: machineState = %d (%s)\n", machineState, GetStateName(machineState)));
430 SDL_Event event = {0};
431
432 if ( machineState == MachineState_Aborted
433 || machineState == MachineState_Teleported
434 || (machineState == MachineState_Saved && !m_fIgnorePowerOffEvents)
435 || (machineState == MachineState_AbortedSaved && !m_fIgnorePowerOffEvents)
436 || (machineState == MachineState_PoweredOff && !m_fIgnorePowerOffEvents)
437 )
438 {
439 /*
440 * We have to inform the SDL thread that the application has be terminated
441 */
442 event.type = SDL_USEREVENT;
443 event.user.type = SDL_USER_EVENT_TERMINATE;
444 event.user.code = machineState == MachineState_Aborted
445 ? VBOXSDL_TERM_ABEND
446 : VBOXSDL_TERM_NORMAL;
447 }
448 else
449 {
450 /*
451 * Inform the SDL thread to refresh the titlebar
452 */
453 event.type = SDL_USEREVENT;
454 event.user.type = SDL_USER_EVENT_UPDATE_TITLEBAR;
455 }
456
457 PushSDLEventForSure(&event);
458 break;
459 }
460
461 case VBoxEventType_OnRuntimeError:
462 {
463 ComPtr<IRuntimeErrorEvent> pRTEEv = aEvent;
464 Assert(pRTEEv);
465 BOOL fFatal;
466
467 pRTEEv->COMGETTER(Fatal)(&fFatal);
468 MachineState_T machineState;
469 gpMachine->COMGETTER(State)(&machineState);
470 const char *pszType;
471 bool fPaused = machineState == MachineState_Paused;
472 if (fFatal)
473 pszType = "FATAL ERROR";
474 else if (machineState == MachineState_Paused)
475 pszType = "Non-fatal ERROR";
476 else
477 pszType = "WARNING";
478 Bstr bstrId, bstrMessage;
479 pRTEEv->COMGETTER(Id)(bstrId.asOutParam());
480 pRTEEv->COMGETTER(Message)(bstrMessage.asOutParam());
481 RTPrintf("\n%s: ** %ls **\n%ls\n%s\n", pszType, bstrId.raw(), bstrMessage.raw(),
482 fPaused ? "The VM was paused. Continue with HostKey + P after you solved the problem.\n" : "");
483 break;
484 }
485
486 case VBoxEventType_OnCanShowWindow:
487 {
488 ComPtr<ICanShowWindowEvent> pCSWEv = aEvent;
489 Assert(pCSWEv);
490#ifdef RT_OS_DARWIN
491 /* SDL feature not available on Quartz */
492#else
493 bool fCanShow = false;
494 Uint32 winId = 0;
495 VBoxSDLFB *fb = getFbFromWinId(winId);
496 SDL_SysWMinfo info;
497 SDL_VERSION(&info.version);
498 if (SDL_GetWindowWMInfo(fb->getWindow(), &info))
499 fCanShow = true;
500 if (fCanShow)
501 pCSWEv->AddApproval(NULL);
502 else
503 pCSWEv->AddVeto(NULL);
504#endif
505 break;
506 }
507
508 case VBoxEventType_OnShowWindow:
509 {
510 ComPtr<IShowWindowEvent> pSWEv = aEvent;
511 Assert(pSWEv);
512 LONG64 winId = 0;
513 pSWEv->COMGETTER(WinId)(&winId);
514 if (winId != 0)
515 break; /* WinId already set by some other listener. */
516#ifndef RT_OS_DARWIN
517 SDL_SysWMinfo info;
518 SDL_VERSION(&info.version);
519 VBoxSDLFB *fb = getFbFromWinId(winId);
520 if (SDL_GetWindowWMInfo(fb->getWindow(), &info))
521 {
522# if defined(VBOXSDL_WITH_X11)
523 pSWEv->COMSETTER(WinId)((LONG64)info.info.x11.window);
524# elif defined(RT_OS_WINDOWS)
525 pSWEv->COMSETTER(WinId)((intptr_t)info.info.win.window);
526# else /* !RT_OS_WINDOWS */
527 AssertFailed();
528# endif
529 }
530#endif /* !RT_OS_DARWIN */
531 break;
532 }
533
534 default:
535 AssertFailed();
536 }
537 return S_OK;
538 }
539
540 static const char *GetStateName(MachineState_T machineState)
541 {
542 switch (machineState)
543 {
544 case MachineState_Null: return "<null>";
545 case MachineState_PoweredOff: return "PoweredOff";
546 case MachineState_Saved: return "Saved";
547 case MachineState_Teleported: return "Teleported";
548 case MachineState_Aborted: return "Aborted";
549 case MachineState_AbortedSaved: return "Aborted-Saved";
550 case MachineState_Running: return "Running";
551 case MachineState_Teleporting: return "Teleporting";
552 case MachineState_LiveSnapshotting: return "LiveSnapshotting";
553 case MachineState_Paused: return "Paused";
554 case MachineState_Stuck: return "GuruMeditation";
555 case MachineState_Starting: return "Starting";
556 case MachineState_Stopping: return "Stopping";
557 case MachineState_Saving: return "Saving";
558 case MachineState_Restoring: return "Restoring";
559 case MachineState_TeleportingPausedVM: return "TeleportingPausedVM";
560 case MachineState_TeleportingIn: return "TeleportingIn";
561 case MachineState_RestoringSnapshot: return "RestoringSnapshot";
562 case MachineState_DeletingSnapshot: return "DeletingSnapshot";
563 case MachineState_SettingUp: return "SettingUp";
564 default: return "no idea";
565 }
566 }
567
568 void ignorePowerOffEvents(bool fIgnore)
569 {
570 m_fIgnorePowerOffEvents = fIgnore;
571 }
572
573private:
574 bool m_fIgnorePowerOffEvents;
575};
576
577typedef ListenerImpl<VBoxSDLClientEventListener> VBoxSDLClientEventListenerImpl;
578typedef ListenerImpl<VBoxSDLEventListener> VBoxSDLEventListenerImpl;
579typedef ListenerImpl<VBoxSDLConsoleEventListener> VBoxSDLConsoleEventListenerImpl;
580
581static void show_usage()
582{
583 RTPrintf("Usage:\n"
584 " --startvm <uuid|name> Virtual machine to start, either UUID or name\n"
585 " --separate Run a separate VM process or attach to a running VM\n"
586 " --hda <file> Set temporary first hard disk to file\n"
587 " --fda <file> Set temporary first floppy disk to file\n"
588 " --cdrom <file> Set temporary CDROM/DVD to file/device ('none' to unmount)\n"
589 " --boot <a|c|d|n> Set temporary boot device (a = floppy, c = 1st HD, d = DVD, n = network)\n"
590 " --memory <size> Set temporary memory size in megabytes\n"
591 " --vram <size> Set temporary size of video memory in megabytes\n"
592 " --fullscreen Start VM in fullscreen mode\n"
593 " --fullscreenresize Resize the guest on fullscreen\n"
594 " --fixedmode <w> <h> <bpp> Use a fixed SDL video mode with given width, height and bits per pixel\n"
595 " --nofstoggle Forbid switching to/from fullscreen mode\n"
596 " --noresize Make the SDL frame non resizable\n"
597 " --nohostkey Disable all hostkey combinations\n"
598 " --nohostkeys ... Disable specific hostkey combinations, see below for valid keys\n"
599 " --nograbonclick Disable mouse/keyboard grabbing on mouse click w/o additions\n"
600 " --detecthostkey Get the hostkey identifier and modifier state\n"
601 " --hostkey <key> {<key2>} <mod> Set the host key to the values obtained using --detecthostkey\n"
602 " --termacpi Send an ACPI power button event when closing the window\n"
603 " --vrdp <ports> Listen for VRDP connections on one of specified ports (default if not specified)\n"
604 " --discardstate Discard saved state (if present) and revert to last snapshot (if present)\n"
605 " --settingspw <pw> Specify the settings password\n"
606 " --settingspwfile <file> Specify a file containing the settings password\n"
607#ifdef VBOXSDL_ADVANCED_OPTIONS
608 " --warpdrive <pct> Sets the warp driver rate in percent (100 = normal)\n"
609#endif
610 "\n"
611 "Key bindings:\n"
612 " <hostkey> + f Switch to full screen / restore to previous view\n"
613 " h Press ACPI power button\n"
614 " n Take a snapshot and continue execution\n"
615 " p Pause / resume execution\n"
616 " q Power off\n"
617 " r VM reset\n"
618 " s Save state and power off\n"
619 " <del> Send <ctrl><alt><del>\n"
620 " <F1>...<F12> Send <ctrl><alt><Fx>\n"
621#if defined(DEBUG) || defined(VBOX_WITH_STATISTICS)
622 "\n"
623 "Further key bindings useful for debugging:\n"
624 " LCtrl + Alt + F12 Reset statistics counter\n"
625 " LCtrl + Alt + F11 Dump statistics to logfile\n"
626 " Alt + F8 Toggle single step mode\n"
627 " LCtrl/RCtrl + F12 Toggle logger\n"
628 " F12 Write log marker to logfile\n"
629#endif
630 "\n");
631}
632
633static void PrintError(const char *pszName, CBSTR pwszDescr, CBSTR pwszComponent=NULL)
634{
635 const char *pszFile, *pszFunc, *pszStat;
636 char pszBuffer[1024];
637 com::ErrorInfo info;
638
639 RTStrPrintf(pszBuffer, sizeof(pszBuffer), "%ls", pwszDescr);
640
641 RTPrintf("\n%s! Error info:\n", pszName);
642 if ( (pszFile = strstr(pszBuffer, "At '"))
643 && (pszFunc = strstr(pszBuffer, ") in "))
644 && (pszStat = strstr(pszBuffer, "VBox status code: ")))
645 RTPrintf(" %.*s %.*s\n In%.*s %s",
646 pszFile-pszBuffer, pszBuffer,
647 pszFunc-pszFile+1, pszFile,
648 pszStat-pszFunc-4, pszFunc+4,
649 pszStat);
650 else
651 RTPrintf("%s\n", pszBuffer);
652
653 if (pwszComponent)
654 RTPrintf("(component %ls).\n", pwszComponent);
655
656 RTPrintf("\n");
657}
658
659#ifdef VBOXSDL_WITH_X11
660/**
661 * Custom signal handler. Currently it is only used to release modifier
662 * keys when receiving the USR1 signal. When switching VTs, we might not
663 * get release events for Ctrl-Alt and in case a savestate is performed
664 * on the new VT, the VM will be saved with modifier keys stuck. This is
665 * annoying enough for introducing this hack.
666 */
667void signal_handler_SIGUSR1(int sig, siginfo_t *info, void *secret)
668{
669 RT_NOREF(info, secret);
670
671 /* only SIGUSR1 is interesting */
672 if (sig == SIGUSR1)
673 {
674 /* just release the modifiers */
675 ResetKeys();
676 }
677}
678
679/**
680 * Custom signal handler for catching exit events.
681 */
682void signal_handler_SIGINT(int sig)
683{
684 if (gpszPidFile)
685 RTFileDelete(gpszPidFile);
686 signal(SIGINT, SIG_DFL);
687 signal(SIGQUIT, SIG_DFL);
688 signal(SIGSEGV, SIG_DFL);
689 kill(getpid(), sig);
690}
691#endif /* VBOXSDL_WITH_X11 */
692
693/**
694 * Returns a stringified version of a keyboard modifier.
695 *
696 * @returns Stringified version of the keyboard modifier.
697 * @param mod Modifier code to return a stringified version for.
698 */
699static const char *keyModToStr(unsigned mod)
700{
701 switch (mod)
702 {
703 RT_CASE_RET_STR(KMOD_NONE);
704 RT_CASE_RET_STR(KMOD_LSHIFT);
705 RT_CASE_RET_STR(KMOD_RSHIFT);
706 RT_CASE_RET_STR(KMOD_LCTRL);
707 RT_CASE_RET_STR(KMOD_RCTRL);
708 RT_CASE_RET_STR(KMOD_LALT);
709 RT_CASE_RET_STR(KMOD_RALT);
710 RT_CASE_RET_STR(KMOD_LGUI);
711 RT_CASE_RET_STR(KMOD_RGUI);
712 RT_CASE_RET_STR(KMOD_NUM);
713 RT_CASE_RET_STR(KMOD_CAPS);
714 RT_CASE_RET_STR(KMOD_MODE);
715 RT_CASE_RET_STR(KMOD_SCROLL);
716 default:
717 break;
718 }
719
720 return "<Unknown>";
721}
722
723/**
724 * Handles detecting a host key by printing its values to stdout.
725 *
726 * @returns RTEXITCODE
727 */
728static RTEXITCODE handleDetectHostKey(void)
729{
730 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
731
732 int rc = SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_TIMER);
733 if (rc == 0)
734 {
735 /* We need a window, otherwise we won't get any keypress events. */
736 SDL_Window *pWnd = SDL_CreateWindow("VBoxSDL",
737 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_SHOWN);
738 RTPrintf("Please hit one or two function key(s) to get the --hostkey value. ..\n");
739 RTPrintf("Press CTRL+C to quit.\n");
740 SDL_Event e1;
741 while (SDL_WaitEvent(&e1))
742 {
743 if ( e1.key.keysym.sym == SDLK_c
744 && (e1.key.keysym.mod & KMOD_CTRL) != 0)
745 break;
746 if (e1.type == SDL_QUIT)
747 break;
748 if (e1.type == SDL_KEYDOWN)
749 {
750 unsigned const mod = SDL_GetModState() & ~(KMOD_MODE | KMOD_NUM | KMOD_RESERVED);
751 RTPrintf("--hostkey %d", e1.key.keysym.sym);
752 if (mod)
753 RTPrintf(" %d\n", mod);
754 else
755 RTPrintf("\n");
756
757 if (mod)
758 RTPrintf("Host key is '%s' + '%s'\n", keyModToStr(mod), SDL_GetKeyName(e1.key.keysym.sym));
759 else
760 RTPrintf("Host key is '%s'\n", SDL_GetKeyName(e1.key.keysym.sym));
761 }
762 }
763 SDL_DestroyWindow(pWnd);
764 SDL_Quit();
765 }
766 else
767 {
768 RTPrintf("Error: SDL_InitSubSystem failed with message '%s'\n", SDL_GetError());
769 rcExit = RTEXITCODE_FAILURE;
770 }
771
772 return rcExit;
773}
774
775/** entry point */
776extern "C"
777DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
778{
779 RT_NOREF(envp);
780#ifdef RT_OS_WINDOWS
781 /* As we run with the WINDOWS subsystem, we need to either attach to or create an own console
782 * to get any stdout / stderr output. */
783 bool fAllocConsole = IsDebuggerPresent();
784 if (!fAllocConsole)
785 {
786 if (!AttachConsole(ATTACH_PARENT_PROCESS))
787 fAllocConsole = true;
788 }
789
790 if (fAllocConsole)
791 {
792 if (!AllocConsole())
793 MessageBox(GetDesktopWindow(), L"Unable to attach to or allocate a console!", L"VBoxSDL", MB_OK | MB_ICONERROR);
794 /* Continue running. */
795 }
796
797 RTFILE hStdIn;
798 RTFileFromNative(&hStdIn, (RTHCINTPTR)GetStdHandle(STD_INPUT_HANDLE));
799 /** @todo Closing of standard handles not support via IPRT (yet). */
800 RTStrmOpenFileHandle(hStdIn, "r", 0, &g_pStdIn);
801
802 RTFILE hStdOut;
803 RTFileFromNative(&hStdOut, (RTHCINTPTR)GetStdHandle(STD_OUTPUT_HANDLE));
804 /** @todo Closing of standard handles not support via IPRT (yet). */
805 RTStrmOpenFileHandle(hStdOut, "wt", 0, &g_pStdOut);
806
807 RTFILE hStdErr;
808 RTFileFromNative(&hStdErr, (RTHCINTPTR)GetStdHandle(STD_ERROR_HANDLE));
809 RTStrmOpenFileHandle(hStdErr, "wt", 0, &g_pStdErr);
810
811 if (!fAllocConsole) /* When attaching to the parent console, make sure we start on a fresh line. */
812 RTPrintf("\n");
813
814 ATL::CComModule _Module; /* Required internally by ATL (constructor records instance in global variable). */
815#endif /* RT_OS_WINDOWS */
816
817#ifdef Q_WS_X11
818 if (!XInitThreads())
819 return 1;
820#endif
821#ifdef VBOXSDL_WITH_X11
822 /*
823 * Lock keys on SDL behave different from normal keys: A KeyPress event is generated
824 * if the lock mode gets active and a keyRelease event is generated if the lock mode
825 * gets inactive, that is KeyPress and KeyRelease are sent when pressing the lock key
826 * to change the mode. The current lock mode is reflected in SDL_GetModState().
827 *
828 * Debian patched libSDL to make the lock keys behave like normal keys
829 * generating a KeyPress/KeyRelease event if the lock key was
830 * pressed/released. With the new behaviour, the lock status is not
831 * reflected in the mod status anymore, but the user can request the old
832 * behaviour by setting an environment variable. To confuse matters further
833 * version 1.2.14 (fortunately including the Debian packaged versions)
834 * adopted the Debian behaviour officially, but inverted the meaning of the
835 * environment variable to select the new behaviour, keeping the old as the
836 * default. We disable the new behaviour to ensure a defined environment
837 * and work around the missing KeyPress/KeyRelease events in ProcessKeys().
838 */
839 {
840#if 0
841 const SDL_version *pVersion = SDL_Linked_Version();
842 if ( SDL_VERSIONNUM(pVersion->major, pVersion->minor, pVersion->patch)
843 < SDL_VERSIONNUM(1, 2, 14))
844 RTEnvSet("SDL_DISABLE_LOCK_KEYS", "1");
845#endif
846 }
847#endif
848
849 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
850
851 /*
852 * the hostkey detection mode is unrelated to VM processing, so handle it before
853 * we initialize anything COM related
854 */
855 if (argc == 2 && ( !strcmp(argv[1], "-detecthostkey")
856 || !strcmp(argv[1], "--detecthostkey")))
857 {
858 rcExit = handleDetectHostKey();
859#ifdef RT_OS_WINDOWS
860 FreeConsole(); /* Detach or destroy (from) console. */
861#endif
862 return rcExit;
863 }
864
865 /** @todo r=andy This function is waaaaaay to long, uses goto's and leaks stuff. Use RTGetOpt handling. */
866
867 HRESULT hrc;
868 int vrc;
869 Guid uuidVM;
870 char *vmName = NULL;
871 bool fSeparate = false;
872 DeviceType_T bootDevice = DeviceType_Null;
873 uint32_t memorySize = 0;
874 uint32_t vramSize = 0;
875 ComPtr<IEventListener> pVBoxClientListener;
876 ComPtr<IEventListener> pVBoxListener;
877 ComObjPtr<VBoxSDLConsoleEventListenerImpl> pConsoleListener;
878
879 bool fFullscreen = false;
880 bool fResizable = true;
881#ifdef USE_XPCOM_QUEUE_THREAD
882 bool fXPCOMEventThreadSignaled = false;
883#endif
884 const char *pcszHdaFile = NULL;
885 const char *pcszCdromFile = NULL;
886 const char *pcszFdaFile = NULL;
887 const char *pszPortVRDP = NULL;
888 bool fDiscardState = false;
889 const char *pcszSettingsPw = NULL;
890 const char *pcszSettingsPwFile = NULL;
891#ifdef VBOXSDL_ADVANCED_OPTIONS
892 uint32_t u32WarpDrive = 0;
893#endif
894#ifdef VBOX_WIN32_UI
895 bool fWin32UI = true;
896 int64_t winId = 0;
897#endif
898 bool fShowSDLConfig = false;
899 uint32_t fixedWidth = ~(uint32_t)0;
900 uint32_t fixedHeight = ~(uint32_t)0;
901 uint32_t fixedBPP = ~(uint32_t)0;
902 uint32_t uResizeWidth = ~(uint32_t)0;
903 uint32_t uResizeHeight = ~(uint32_t)0;
904
905 /* The damned GOTOs forces this to be up here - totally out of place. */
906 /*
907 * Host key handling.
908 *
909 * The golden rule is that host-key combinations should not be seen
910 * by the guest. For instance a CAD should not have any extra RCtrl down
911 * and RCtrl up around itself. Nor should a resume be followed by a Ctrl-P
912 * that could encourage applications to start printing.
913 *
914 * We must not confuse the hostkey processing into any release sequences
915 * either, the host key is supposed to be explicitly pressing one key.
916 *
917 * Quick state diagram:
918 *
919 * host key down alone
920 * (Normal) ---------------
921 * ^ ^ |
922 * | | v host combination key down
923 * | | (Host key down) ----------------
924 * | | host key up v | |
925 * | |-------------- | other key down v host combination key down
926 * | | (host key used) -------------
927 * | | | ^ |
928 * | (not host key)-- | |---------------
929 * | | | | |
930 * | | ---- other |
931 * | modifiers = 0 v v
932 * -----------------------------------------------
933 */
934 enum HKEYSTATE
935 {
936 /** The initial and most common state, pass keystrokes to the guest.
937 * Next state: HKEYSTATE_DOWN
938 * Prev state: Any */
939 HKEYSTATE_NORMAL = 1,
940 /** The first host key was pressed down
941 */
942 HKEYSTATE_DOWN_1ST,
943 /** The second host key was pressed down (if gHostKeySym2 != SDLK_UNKNOWN)
944 */
945 HKEYSTATE_DOWN_2ND,
946 /** The host key has been pressed down.
947 * Prev state: HKEYSTATE_NORMAL
948 * Next state: HKEYSTATE_NORMAL - host key up, capture toggle.
949 * Next state: HKEYSTATE_USED - host key combination down.
950 * Next state: HKEYSTATE_NOT_IT - non-host key combination down.
951 */
952 HKEYSTATE_DOWN,
953 /** A host key combination was pressed.
954 * Prev state: HKEYSTATE_DOWN
955 * Next state: HKEYSTATE_NORMAL - when modifiers are all 0
956 */
957 HKEYSTATE_USED,
958 /** A non-host key combination was attempted. Send hostkey down to the
959 * guest and continue until all modifiers have been released.
960 * Prev state: HKEYSTATE_DOWN
961 * Next state: HKEYSTATE_NORMAL - when modifiers are all 0
962 */
963 HKEYSTATE_NOT_IT
964 } enmHKeyState = HKEYSTATE_NORMAL;
965 /** The host key down event which we have been hiding from the guest.
966 * Used when going from HKEYSTATE_DOWN to HKEYSTATE_NOT_IT. */
967 SDL_Event EvHKeyDown1;
968 SDL_Event EvHKeyDown2;
969
970 LogFlow(("SDL GUI started\n"));
971 RTPrintf(VBOX_PRODUCT " SDL GUI version %s\n"
972 "Copyright (C) 2005-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
973 VBOX_VERSION_STRING);
974
975 // less than one parameter is not possible
976 if (argc < 2)
977 {
978 show_usage();
979 return 1;
980 }
981
982 // command line argument parsing stuff
983 for (int curArg = 1; curArg < argc; curArg++)
984 {
985 if ( !strcmp(argv[curArg], "--vm")
986 || !strcmp(argv[curArg], "-vm")
987 || !strcmp(argv[curArg], "--startvm")
988 || !strcmp(argv[curArg], "-startvm")
989 || !strcmp(argv[curArg], "-s")
990 )
991 {
992 if (++curArg >= argc)
993 {
994 RTPrintf("Error: VM not specified (UUID or name)!\n");
995 return 1;
996 }
997 // first check if a UUID was supplied
998 uuidVM = argv[curArg];
999
1000 if (!uuidVM.isValid())
1001 {
1002 LogFlow(("invalid UUID format, assuming it's a VM name\n"));
1003 vmName = argv[curArg];
1004 }
1005 else if (uuidVM.isZero())
1006 {
1007 RTPrintf("Error: UUID argument is zero!\n");
1008 return 1;
1009 }
1010 }
1011 else if ( !strcmp(argv[curArg], "--separate")
1012 || !strcmp(argv[curArg], "-separate"))
1013 {
1014 fSeparate = true;
1015 }
1016 else if ( !strcmp(argv[curArg], "--comment")
1017 || !strcmp(argv[curArg], "-comment"))
1018 {
1019 if (++curArg >= argc)
1020 {
1021 RTPrintf("Error: missing argument for comment!\n");
1022 return 1;
1023 }
1024 }
1025 else if ( !strcmp(argv[curArg], "--boot")
1026 || !strcmp(argv[curArg], "-boot"))
1027 {
1028 if (++curArg >= argc)
1029 {
1030 RTPrintf("Error: missing argument for boot drive!\n");
1031 return 1;
1032 }
1033 switch (argv[curArg][0])
1034 {
1035 case 'a':
1036 {
1037 bootDevice = DeviceType_Floppy;
1038 break;
1039 }
1040
1041 case 'c':
1042 {
1043 bootDevice = DeviceType_HardDisk;
1044 break;
1045 }
1046
1047 case 'd':
1048 {
1049 bootDevice = DeviceType_DVD;
1050 break;
1051 }
1052
1053 case 'n':
1054 {
1055 bootDevice = DeviceType_Network;
1056 break;
1057 }
1058
1059 default:
1060 {
1061 RTPrintf("Error: wrong argument for boot drive!\n");
1062 return 1;
1063 }
1064 }
1065 }
1066 else if ( !strcmp(argv[curArg], "--detecthostkey")
1067 || !strcmp(argv[curArg], "-detecthostkey"))
1068 {
1069 RTPrintf("Error: please specify \"%s\" without any additional parameters!\n",
1070 argv[curArg]);
1071 return 1;
1072 }
1073 else if ( !strcmp(argv[curArg], "--memory")
1074 || !strcmp(argv[curArg], "-memory")
1075 || !strcmp(argv[curArg], "-m"))
1076 {
1077 if (++curArg >= argc)
1078 {
1079 RTPrintf("Error: missing argument for memory size!\n");
1080 return 1;
1081 }
1082 memorySize = atoi(argv[curArg]);
1083 }
1084 else if ( !strcmp(argv[curArg], "--vram")
1085 || !strcmp(argv[curArg], "-vram"))
1086 {
1087 if (++curArg >= argc)
1088 {
1089 RTPrintf("Error: missing argument for vram size!\n");
1090 return 1;
1091 }
1092 vramSize = atoi(argv[curArg]);
1093 }
1094 else if ( !strcmp(argv[curArg], "--fullscreen")
1095 || !strcmp(argv[curArg], "-fullscreen"))
1096 {
1097 fFullscreen = true;
1098 }
1099 else if ( !strcmp(argv[curArg], "--fullscreenresize")
1100 || !strcmp(argv[curArg], "-fullscreenresize"))
1101 {
1102 gfFullscreenResize = true;
1103#ifdef VBOXSDL_WITH_X11
1104 RTEnvSet("SDL_VIDEO_X11_VIDMODE", "0");
1105#endif
1106 }
1107 else if ( !strcmp(argv[curArg], "--fixedmode")
1108 || !strcmp(argv[curArg], "-fixedmode"))
1109 {
1110 /* three parameters follow */
1111 if (curArg + 3 >= argc)
1112 {
1113 RTPrintf("Error: missing arguments for fixed video mode!\n");
1114 return 1;
1115 }
1116 fixedWidth = atoi(argv[++curArg]);
1117 fixedHeight = atoi(argv[++curArg]);
1118 fixedBPP = atoi(argv[++curArg]);
1119 }
1120 else if ( !strcmp(argv[curArg], "--nofstoggle")
1121 || !strcmp(argv[curArg], "-nofstoggle"))
1122 {
1123 gfAllowFullscreenToggle = FALSE;
1124 }
1125 else if ( !strcmp(argv[curArg], "--noresize")
1126 || !strcmp(argv[curArg], "-noresize"))
1127 {
1128 fResizable = false;
1129 }
1130 else if ( !strcmp(argv[curArg], "--nohostkey")
1131 || !strcmp(argv[curArg], "-nohostkey"))
1132 {
1133 gHostKeyMod = 0;
1134 gHostKeySym1 = 0;
1135 }
1136 else if ( !strcmp(argv[curArg], "--nohostkeys")
1137 || !strcmp(argv[curArg], "-nohostkeys"))
1138 {
1139 if (++curArg >= argc)
1140 {
1141 RTPrintf("Error: missing a string of disabled hostkey combinations\n");
1142 return 1;
1143 }
1144 gHostKeyDisabledCombinations = argv[curArg];
1145 size_t cch = strlen(gHostKeyDisabledCombinations);
1146 for (size_t i = 0; i < cch; i++)
1147 {
1148 if (!strchr("fhnpqrs", gHostKeyDisabledCombinations[i]))
1149 {
1150 RTPrintf("Error: <hostkey> + '%c' is not a valid combination\n",
1151 gHostKeyDisabledCombinations[i]);
1152 return 1;
1153 }
1154 }
1155 }
1156 else if ( !strcmp(argv[curArg], "--nograbonclick")
1157 || !strcmp(argv[curArg], "-nograbonclick"))
1158 {
1159 gfGrabOnMouseClick = FALSE;
1160 }
1161 else if ( !strcmp(argv[curArg], "--termacpi")
1162 || !strcmp(argv[curArg], "-termacpi"))
1163 {
1164 gfACPITerm = TRUE;
1165 }
1166 else if ( !strcmp(argv[curArg], "--pidfile")
1167 || !strcmp(argv[curArg], "-pidfile"))
1168 {
1169 if (++curArg >= argc)
1170 {
1171 RTPrintf("Error: missing file name for --pidfile!\n");
1172 return 1;
1173 }
1174 gpszPidFile = argv[curArg];
1175 }
1176 else if ( !strcmp(argv[curArg], "--hda")
1177 || !strcmp(argv[curArg], "-hda"))
1178 {
1179 if (++curArg >= argc)
1180 {
1181 RTPrintf("Error: missing file name for first hard disk!\n");
1182 return 1;
1183 }
1184 /* resolve it. */
1185 if (RTPathExists(argv[curArg]))
1186 pcszHdaFile = RTPathRealDup(argv[curArg]);
1187 if (!pcszHdaFile)
1188 {
1189 RTPrintf("Error: The path to the specified harddisk, '%s', could not be resolved.\n", argv[curArg]);
1190 return 1;
1191 }
1192 }
1193 else if ( !strcmp(argv[curArg], "--fda")
1194 || !strcmp(argv[curArg], "-fda"))
1195 {
1196 if (++curArg >= argc)
1197 {
1198 RTPrintf("Error: missing file/device name for first floppy disk!\n");
1199 return 1;
1200 }
1201 /* resolve it. */
1202 if (RTPathExists(argv[curArg]))
1203 pcszFdaFile = RTPathRealDup(argv[curArg]);
1204 if (!pcszFdaFile)
1205 {
1206 RTPrintf("Error: The path to the specified floppy disk, '%s', could not be resolved.\n", argv[curArg]);
1207 return 1;
1208 }
1209 }
1210 else if ( !strcmp(argv[curArg], "--cdrom")
1211 || !strcmp(argv[curArg], "-cdrom"))
1212 {
1213 if (++curArg >= argc)
1214 {
1215 RTPrintf("Error: missing file/device name for cdrom!\n");
1216 return 1;
1217 }
1218 /* resolve it. */
1219 if (RTPathExists(argv[curArg]))
1220 pcszCdromFile = RTPathRealDup(argv[curArg]);
1221 if (!pcszCdromFile)
1222 {
1223 RTPrintf("Error: The path to the specified cdrom, '%s', could not be resolved.\n", argv[curArg]);
1224 return 1;
1225 }
1226 }
1227 else if ( !strcmp(argv[curArg], "--vrdp")
1228 || !strcmp(argv[curArg], "-vrdp"))
1229 {
1230 // start with the standard VRDP port
1231 pszPortVRDP = "0";
1232
1233 // is there another argument
1234 if (argc > (curArg + 1))
1235 {
1236 curArg++;
1237 pszPortVRDP = argv[curArg];
1238 LogFlow(("Using non standard VRDP port %s\n", pszPortVRDP));
1239 }
1240 }
1241 else if ( !strcmp(argv[curArg], "--discardstate")
1242 || !strcmp(argv[curArg], "-discardstate"))
1243 {
1244 fDiscardState = true;
1245 }
1246 else if (!strcmp(argv[curArg], "--settingspw"))
1247 {
1248 if (++curArg >= argc)
1249 {
1250 RTPrintf("Error: missing password");
1251 return 1;
1252 }
1253 pcszSettingsPw = argv[curArg];
1254 }
1255 else if (!strcmp(argv[curArg], "--settingspwfile"))
1256 {
1257 if (++curArg >= argc)
1258 {
1259 RTPrintf("Error: missing password file\n");
1260 return 1;
1261 }
1262 pcszSettingsPwFile = argv[curArg];
1263 }
1264#ifdef VBOXSDL_ADVANCED_OPTIONS
1265 else if ( !strcmp(argv[curArg], "--warpdrive")
1266 || !strcmp(argv[curArg], "-warpdrive"))
1267 {
1268 if (++curArg >= argc)
1269 {
1270 RTPrintf("Error: missing the rate value for the --warpdrive option!\n");
1271 return 1;
1272 }
1273 u32WarpDrive = RTStrToUInt32(argv[curArg]);
1274 if (u32WarpDrive < 2 || u32WarpDrive > 20000)
1275 {
1276 RTPrintf("Error: the warp drive rate is restricted to [2..20000]. (%d)\n", u32WarpDrive);
1277 return 1;
1278 }
1279 }
1280#endif /* VBOXSDL_ADVANCED_OPTIONS */
1281#ifdef VBOX_WIN32_UI
1282 else if ( !strcmp(argv[curArg], "--win32ui")
1283 || !strcmp(argv[curArg], "-win32ui"))
1284 fWin32UI = true;
1285#endif
1286 else if ( !strcmp(argv[curArg], "--showsdlconfig")
1287 || !strcmp(argv[curArg], "-showsdlconfig"))
1288 fShowSDLConfig = true;
1289 else if ( !strcmp(argv[curArg], "--hostkey")
1290 || !strcmp(argv[curArg], "-hostkey"))
1291 {
1292 if (++curArg + 1 >= argc)
1293 {
1294 RTPrintf("Error: not enough arguments for host keys!\n");
1295 return 1;
1296 }
1297 gHostKeySym1 = atoi(argv[curArg++]);
1298 if (curArg + 1 < argc && (argv[curArg+1][0] == '0' || atoi(argv[curArg+1]) > 0))
1299 {
1300 /* two-key sequence as host key specified */
1301 gHostKeySym2 = atoi(argv[curArg++]);
1302 }
1303 gHostKeyMod = atoi(argv[curArg]);
1304 }
1305 /* just show the help screen */
1306 else
1307 {
1308 if ( strcmp(argv[curArg], "-h")
1309 && strcmp(argv[curArg], "-help")
1310 && strcmp(argv[curArg], "--help"))
1311 RTPrintf("Error: unrecognized switch '%s'\n", argv[curArg]);
1312 show_usage();
1313 return 1;
1314 }
1315 }
1316
1317 hrc = com::Initialize();
1318#ifdef VBOX_WITH_XPCOM
1319 if (hrc == NS_ERROR_FILE_ACCESS_DENIED)
1320 {
1321 char szHome[RTPATH_MAX] = "";
1322 com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
1323 RTPrintf("Failed to initialize COM because the global settings directory '%s' is not accessible!\n", szHome);
1324 return 1;
1325 }
1326#endif
1327 if (FAILED(hrc))
1328 {
1329 RTPrintf("Error: COM initialization failed (rc=%Rhrc)!\n", hrc);
1330 return 1;
1331 }
1332
1333 /* NOTE: do not convert the following scope to a "do {} while (0);", as
1334 * this would make it all too tempting to use "break;" incorrectly - it
1335 * would skip over the cleanup. */
1336 {
1337 // scopes all the stuff till shutdown
1338 ////////////////////////////////////////////////////////////////////////////
1339
1340 ComPtr<IVirtualBoxClient> pVirtualBoxClient;
1341 ComPtr<IVirtualBox> pVirtualBox;
1342 ComPtr<ISession> pSession;
1343 bool sessionOpened = false;
1344 NativeEventQueue* eventQ = com::NativeEventQueue::getMainEventQueue();
1345
1346 ComPtr<IMachine> pMachine;
1347 ComPtr<IGraphicsAdapter> pGraphicsAdapter;
1348
1349 hrc = pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
1350 if (FAILED(hrc))
1351 {
1352 com::ErrorInfo info;
1353 if (info.isFullAvailable())
1354 PrintError("Failed to create VirtualBoxClient object",
1355 info.getText().raw(), info.getComponent().raw());
1356 else
1357 RTPrintf("Failed to create VirtualBoxClient object! No error information available (rc=%Rhrc).\n", hrc);
1358 goto leave;
1359 }
1360
1361 hrc = pVirtualBoxClient->COMGETTER(VirtualBox)(pVirtualBox.asOutParam());
1362 if (FAILED(hrc))
1363 {
1364 RTPrintf("Failed to get VirtualBox object (rc=%Rhrc)!\n", hrc);
1365 goto leave;
1366 }
1367 hrc = pVirtualBoxClient->COMGETTER(Session)(pSession.asOutParam());
1368 if (FAILED(hrc))
1369 {
1370 RTPrintf("Failed to get session object (rc=%Rhrc)!\n", hrc);
1371 goto leave;
1372 }
1373
1374 if (pcszSettingsPw)
1375 {
1376 CHECK_ERROR(pVirtualBox, SetSettingsSecret(Bstr(pcszSettingsPw).raw()));
1377 if (FAILED(hrc))
1378 goto leave;
1379 }
1380 else if (pcszSettingsPwFile)
1381 {
1382 rcExit = settingsPasswordFile(pVirtualBox, pcszSettingsPwFile);
1383 if (rcExit != RTEXITCODE_SUCCESS)
1384 goto leave;
1385 }
1386
1387 /*
1388 * Do we have a UUID?
1389 */
1390 if (uuidVM.isValid())
1391 {
1392 hrc = pVirtualBox->FindMachine(uuidVM.toUtf16().raw(), pMachine.asOutParam());
1393 if (FAILED(hrc) || !pMachine)
1394 {
1395 RTPrintf("Error: machine with the given ID not found!\n");
1396 goto leave;
1397 }
1398 }
1399 else if (vmName)
1400 {
1401 /*
1402 * Do we have a name but no UUID?
1403 */
1404 hrc = pVirtualBox->FindMachine(Bstr(vmName).raw(), pMachine.asOutParam());
1405 if ((hrc == S_OK) && pMachine)
1406 {
1407 Bstr bstrId;
1408 pMachine->COMGETTER(Id)(bstrId.asOutParam());
1409 uuidVM = Guid(bstrId);
1410 }
1411 else
1412 {
1413 RTPrintf("Error: machine with the given name not found!\n");
1414 RTPrintf("Check if this VM has been corrupted and is now inaccessible.");
1415 goto leave;
1416 }
1417 }
1418
1419 /* create SDL event semaphore */
1420 vrc = RTSemEventCreate(&g_EventSemSDLEvents);
1421 AssertReleaseRC(vrc);
1422
1423 hrc = pVirtualBoxClient->CheckMachineError(pMachine);
1424 if (FAILED(hrc))
1425 {
1426 com::ErrorInfo info;
1427 if (info.isFullAvailable())
1428 PrintError("The VM has errors",
1429 info.getText().raw(), info.getComponent().raw());
1430 else
1431 RTPrintf("Failed to check for VM errors! No error information available (rc=%Rhrc).\n", hrc);
1432 goto leave;
1433 }
1434
1435 if (fSeparate)
1436 {
1437 MachineState_T machineState = MachineState_Null;
1438 pMachine->COMGETTER(State)(&machineState);
1439 if ( machineState == MachineState_Running
1440 || machineState == MachineState_Teleporting
1441 || machineState == MachineState_LiveSnapshotting
1442 || machineState == MachineState_Paused
1443 || machineState == MachineState_TeleportingPausedVM
1444 )
1445 {
1446 RTPrintf("VM is already running.\n");
1447 }
1448 else
1449 {
1450 ComPtr<IProgress> progress;
1451 hrc = pMachine->LaunchVMProcess(pSession, Bstr("headless").raw(), ComSafeArrayNullInParam(), progress.asOutParam());
1452 if (SUCCEEDED(hrc) && !progress.isNull())
1453 {
1454 RTPrintf("Waiting for VM to power on...\n");
1455 hrc = progress->WaitForCompletion(-1);
1456 if (SUCCEEDED(hrc))
1457 {
1458 BOOL completed = true;
1459 hrc = progress->COMGETTER(Completed)(&completed);
1460 if (SUCCEEDED(hrc))
1461 {
1462 LONG iRc;
1463 hrc = progress->COMGETTER(ResultCode)(&iRc);
1464 if (SUCCEEDED(hrc))
1465 {
1466 if (FAILED(iRc))
1467 {
1468 ProgressErrorInfo info(progress);
1469 com::GluePrintErrorInfo(info);
1470 }
1471 else
1472 {
1473 RTPrintf("VM has been successfully started.\n");
1474 /* LaunchVMProcess obtains a shared lock on the machine.
1475 * Unlock it here, because the lock will be obtained below
1476 * in the common code path as for already running VM.
1477 */
1478 pSession->UnlockMachine();
1479 }
1480 }
1481 }
1482 }
1483 }
1484 }
1485 if (FAILED(hrc))
1486 {
1487 RTPrintf("Error: failed to power up VM! No error text available.\n");
1488 goto leave;
1489 }
1490
1491 hrc = pMachine->LockMachine(pSession, LockType_Shared);
1492 }
1493 else
1494 {
1495 pSession->COMSETTER(Name)(Bstr("GUI/SDL").raw());
1496 hrc = pMachine->LockMachine(pSession, LockType_VM);
1497 }
1498
1499 if (FAILED(hrc))
1500 {
1501 com::ErrorInfo info;
1502 if (info.isFullAvailable())
1503 PrintError("Could not open VirtualBox session",
1504 info.getText().raw(), info.getComponent().raw());
1505 goto leave;
1506 }
1507 if (!pSession)
1508 {
1509 RTPrintf("Could not open VirtualBox session!\n");
1510 goto leave;
1511 }
1512 sessionOpened = true;
1513 // get the mutable VM we're dealing with
1514 pSession->COMGETTER(Machine)(gpMachine.asOutParam());
1515 if (!gpMachine)
1516 {
1517 com::ErrorInfo info;
1518 if (info.isFullAvailable())
1519 PrintError("Cannot start VM!",
1520 info.getText().raw(), info.getComponent().raw());
1521 else
1522 RTPrintf("Error: given machine not found!\n");
1523 goto leave;
1524 }
1525
1526 // get the VM console
1527 pSession->COMGETTER(Console)(gpConsole.asOutParam());
1528 if (!gpConsole)
1529 {
1530 RTPrintf("Given console not found!\n");
1531 goto leave;
1532 }
1533
1534 /*
1535 * Are we supposed to use a different hard disk file?
1536 */
1537 if (pcszHdaFile)
1538 {
1539 ComPtr<IMedium> pMedium;
1540
1541 /*
1542 * Strategy: if any registered hard disk points to the same file,
1543 * assign it. If not, register a new image and assign it to the VM.
1544 */
1545 Bstr bstrHdaFile(pcszHdaFile);
1546 pVirtualBox->OpenMedium(bstrHdaFile.raw(), DeviceType_HardDisk,
1547 AccessMode_ReadWrite, FALSE /* fForceNewUuid */,
1548 pMedium.asOutParam());
1549 if (!pMedium)
1550 {
1551 /* we've not found the image */
1552 RTPrintf("Adding hard disk '%s'...\n", pcszHdaFile);
1553 pVirtualBox->OpenMedium(bstrHdaFile.raw(), DeviceType_HardDisk,
1554 AccessMode_ReadWrite, FALSE /* fForceNewUuid */,
1555 pMedium.asOutParam());
1556 }
1557 /* do we have the right image now? */
1558 if (pMedium)
1559 {
1560 Bstr bstrSCName;
1561
1562 /* get the first IDE controller to attach the harddisk to
1563 * and if there is none, add one temporarily */
1564 {
1565 ComPtr<IStorageController> pStorageCtl;
1566 com::SafeIfaceArray<IStorageController> aStorageControllers;
1567 CHECK_ERROR(gpMachine, COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(aStorageControllers)));
1568 for (size_t i = 0; i < aStorageControllers.size(); ++ i)
1569 {
1570 StorageBus_T storageBus = StorageBus_Null;
1571
1572 CHECK_ERROR(aStorageControllers[i], COMGETTER(Bus)(&storageBus));
1573 if (storageBus == StorageBus_IDE)
1574 {
1575 pStorageCtl = aStorageControllers[i];
1576 break;
1577 }
1578 }
1579
1580 if (pStorageCtl)
1581 {
1582 CHECK_ERROR(pStorageCtl, COMGETTER(Name)(bstrSCName.asOutParam()));
1583 gpMachine->DetachDevice(bstrSCName.raw(), 0, 0);
1584 }
1585 else
1586 {
1587 bstrSCName = "IDE Controller";
1588 CHECK_ERROR(gpMachine, AddStorageController(bstrSCName.raw(),
1589 StorageBus_IDE,
1590 pStorageCtl.asOutParam()));
1591 }
1592 }
1593
1594 CHECK_ERROR(gpMachine, AttachDevice(bstrSCName.raw(), 0, 0,
1595 DeviceType_HardDisk, pMedium));
1596 /// @todo why is this attachment saved?
1597 }
1598 else
1599 {
1600 RTPrintf("Error: failed to mount the specified hard disk image!\n");
1601 goto leave;
1602 }
1603 }
1604
1605 /*
1606 * Mount a floppy if requested.
1607 */
1608 if (pcszFdaFile)
1609 do
1610 {
1611 ComPtr<IMedium> pMedium;
1612
1613 /* unmount? */
1614 if (!strcmp(pcszFdaFile, "none"))
1615 {
1616 /* nothing to do, NULL object will cause unmount */
1617 }
1618 else
1619 {
1620 Bstr bstrFdaFile(pcszFdaFile);
1621
1622 /* Assume it's a host drive name */
1623 ComPtr<IHost> pHost;
1624 CHECK_ERROR_BREAK(pVirtualBox, COMGETTER(Host)(pHost.asOutParam()));
1625 hrc = pHost->FindHostFloppyDrive(bstrFdaFile.raw(),
1626 pMedium.asOutParam());
1627 if (FAILED(hrc))
1628 {
1629 /* try to find an existing one */
1630 hrc = pVirtualBox->OpenMedium(bstrFdaFile.raw(),
1631 DeviceType_Floppy,
1632 AccessMode_ReadWrite,
1633 FALSE /* fForceNewUuid */,
1634 pMedium.asOutParam());
1635 if (FAILED(hrc))
1636 {
1637 /* try to add to the list */
1638 RTPrintf("Adding floppy image '%s'...\n", pcszFdaFile);
1639 CHECK_ERROR_BREAK(pVirtualBox,
1640 OpenMedium(bstrFdaFile.raw(),
1641 DeviceType_Floppy,
1642 AccessMode_ReadWrite,
1643 FALSE /* fForceNewUuid */,
1644 pMedium.asOutParam()));
1645 }
1646 }
1647 }
1648
1649 Bstr bstrSCName;
1650
1651 /* get the first floppy controller to attach the floppy to
1652 * and if there is none, add one temporarily */
1653 {
1654 ComPtr<IStorageController> pStorageCtl;
1655 com::SafeIfaceArray<IStorageController> aStorageControllers;
1656 CHECK_ERROR(gpMachine, COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(aStorageControllers)));
1657 for (size_t i = 0; i < aStorageControllers.size(); ++ i)
1658 {
1659 StorageBus_T storageBus = StorageBus_Null;
1660
1661 CHECK_ERROR(aStorageControllers[i], COMGETTER(Bus)(&storageBus));
1662 if (storageBus == StorageBus_Floppy)
1663 {
1664 pStorageCtl = aStorageControllers[i];
1665 break;
1666 }
1667 }
1668
1669 if (pStorageCtl)
1670 {
1671 CHECK_ERROR(pStorageCtl, COMGETTER(Name)(bstrSCName.asOutParam()));
1672 gpMachine->DetachDevice(bstrSCName.raw(), 0, 0);
1673 }
1674 else
1675 {
1676 bstrSCName = "Floppy Controller";
1677 CHECK_ERROR(gpMachine, AddStorageController(bstrSCName.raw(),
1678 StorageBus_Floppy,
1679 pStorageCtl.asOutParam()));
1680 }
1681 }
1682
1683 CHECK_ERROR(gpMachine, AttachDevice(bstrSCName.raw(), 0, 0,
1684 DeviceType_Floppy, pMedium));
1685 }
1686 while (0);
1687 if (FAILED(hrc))
1688 goto leave;
1689
1690 /*
1691 * Mount a CD-ROM if requested.
1692 */
1693 if (pcszCdromFile)
1694 do
1695 {
1696 ComPtr<IMedium> pMedium;
1697
1698 /* unmount? */
1699 if (!strcmp(pcszCdromFile, "none"))
1700 {
1701 /* nothing to do, NULL object will cause unmount */
1702 }
1703 else
1704 {
1705 Bstr bstrCdromFile(pcszCdromFile);
1706
1707 /* Assume it's a host drive name */
1708 ComPtr<IHost> pHost;
1709 CHECK_ERROR_BREAK(pVirtualBox, COMGETTER(Host)(pHost.asOutParam()));
1710 hrc = pHost->FindHostDVDDrive(bstrCdromFile.raw(), pMedium.asOutParam());
1711 if (FAILED(hrc))
1712 {
1713 /* try to find an existing one */
1714 hrc = pVirtualBox->OpenMedium(bstrCdromFile.raw(),
1715 DeviceType_DVD,
1716 AccessMode_ReadWrite,
1717 FALSE /* fForceNewUuid */,
1718 pMedium.asOutParam());
1719 if (FAILED(hrc))
1720 {
1721 /* try to add to the list */
1722 RTPrintf("Adding ISO image '%s'...\n", pcszCdromFile);
1723 CHECK_ERROR_BREAK(pVirtualBox,
1724 OpenMedium(bstrCdromFile.raw(),
1725 DeviceType_DVD,
1726 AccessMode_ReadWrite,
1727 FALSE /* fForceNewUuid */,
1728 pMedium.asOutParam()));
1729 }
1730 }
1731 }
1732
1733 Bstr bstrSCName;
1734
1735 /* get the first IDE controller to attach the DVD drive to
1736 * and if there is none, add one temporarily */
1737 {
1738 ComPtr<IStorageController> pStorageCtl;
1739 com::SafeIfaceArray<IStorageController> aStorageControllers;
1740 CHECK_ERROR(gpMachine, COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(aStorageControllers)));
1741 for (size_t i = 0; i < aStorageControllers.size(); ++ i)
1742 {
1743 StorageBus_T storageBus = StorageBus_Null;
1744
1745 CHECK_ERROR(aStorageControllers[i], COMGETTER(Bus)(&storageBus));
1746 if (storageBus == StorageBus_IDE)
1747 {
1748 pStorageCtl = aStorageControllers[i];
1749 break;
1750 }
1751 }
1752
1753 if (pStorageCtl)
1754 {
1755 CHECK_ERROR(pStorageCtl, COMGETTER(Name)(bstrSCName.asOutParam()));
1756 gpMachine->DetachDevice(bstrSCName.raw(), 1, 0);
1757 }
1758 else
1759 {
1760 bstrSCName = "IDE Controller";
1761 CHECK_ERROR(gpMachine, AddStorageController(bstrSCName.raw(),
1762 StorageBus_IDE,
1763 pStorageCtl.asOutParam()));
1764 }
1765 }
1766
1767 CHECK_ERROR(gpMachine, AttachDevice(bstrSCName.raw(), 1, 0,
1768 DeviceType_DVD, pMedium));
1769 }
1770 while (0);
1771 if (FAILED(hrc))
1772 goto leave;
1773
1774 if (fDiscardState)
1775 {
1776 /*
1777 * If the machine is currently saved,
1778 * discard the saved state first.
1779 */
1780 MachineState_T machineState;
1781 gpMachine->COMGETTER(State)(&machineState);
1782 if (machineState == MachineState_Saved || machineState == MachineState_AbortedSaved)
1783 {
1784 CHECK_ERROR(gpMachine, DiscardSavedState(true /* fDeleteFile */));
1785 }
1786 /*
1787 * If there are snapshots, discard the current state,
1788 * i.e. revert to the last snapshot.
1789 */
1790 ULONG cSnapshots;
1791 gpMachine->COMGETTER(SnapshotCount)(&cSnapshots);
1792 if (cSnapshots)
1793 {
1794 gpProgress = NULL;
1795
1796 ComPtr<ISnapshot> pCurrentSnapshot;
1797 CHECK_ERROR(gpMachine, COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam()));
1798 if (FAILED(hrc))
1799 goto leave;
1800
1801 CHECK_ERROR(gpMachine, RestoreSnapshot(pCurrentSnapshot, gpProgress.asOutParam()));
1802 hrc = gpProgress->WaitForCompletion(-1);
1803 }
1804 }
1805
1806 // get the machine debugger (does not have to be there)
1807 gpConsole->COMGETTER(Debugger)(gpMachineDebugger.asOutParam());
1808 if (gpMachineDebugger)
1809 {
1810 Log(("Machine debugger available!\n"));
1811 }
1812 gpConsole->COMGETTER(Display)(gpDisplay.asOutParam());
1813 if (!gpDisplay)
1814 {
1815 RTPrintf("Error: could not get display object!\n");
1816 goto leave;
1817 }
1818
1819 // set the boot drive
1820 if (bootDevice != DeviceType_Null)
1821 {
1822 hrc = gpMachine->SetBootOrder(1, bootDevice);
1823 if (hrc != S_OK)
1824 {
1825 RTPrintf("Error: could not set boot device, using default.\n");
1826 }
1827 }
1828
1829 // set the memory size if not default
1830 if (memorySize)
1831 {
1832 hrc = gpMachine->COMSETTER(MemorySize)(memorySize);
1833 if (hrc != S_OK)
1834 {
1835 ULONG ramSize = 0;
1836 gpMachine->COMGETTER(MemorySize)(&ramSize);
1837 RTPrintf("Error: could not set memory size, using current setting of %d MBytes\n", ramSize);
1838 }
1839 }
1840
1841 hrc = gpMachine->COMGETTER(GraphicsAdapter)(pGraphicsAdapter.asOutParam());
1842 if (hrc != S_OK)
1843 {
1844 RTPrintf("Error: could not get graphics adapter object\n");
1845 goto leave;
1846 }
1847
1848 if (vramSize)
1849 {
1850 hrc = pGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
1851 if (hrc != S_OK)
1852 {
1853 pGraphicsAdapter->COMGETTER(VRAMSize)((ULONG*)&vramSize);
1854 RTPrintf("Error: could not set VRAM size, using current setting of %d MBytes\n", vramSize);
1855 }
1856 }
1857
1858 // we're always able to process absolute mouse events and we prefer that
1859 gfAbsoluteMouseHost = TRUE;
1860
1861#ifdef VBOX_WIN32_UI
1862 if (fWin32UI)
1863 {
1864 /* initialize the Win32 user interface inside which SDL will be embedded */
1865 if (initUI(fResizable, winId))
1866 return 1;
1867 }
1868#endif
1869
1870 /* static initialization of the SDL stuff */
1871 if (!VBoxSDLFB::init(fShowSDLConfig))
1872 goto leave;
1873
1874 pGraphicsAdapter->COMGETTER(MonitorCount)(&gcMonitors);
1875 if (gcMonitors > 64)
1876 gcMonitors = 64;
1877
1878 for (unsigned i = 0; i < gcMonitors; i++)
1879 {
1880 // create our SDL framebuffer instance
1881 gpFramebuffer[i].createObject();
1882 hrc = gpFramebuffer[i]->init(i, fFullscreen, fResizable, fShowSDLConfig, false,
1883 fixedWidth, fixedHeight, fixedBPP, fSeparate);
1884 if (FAILED(hrc))
1885 {
1886 RTPrintf("Error: could not create framebuffer object!\n");
1887 goto leave;
1888 }
1889 }
1890
1891#ifdef VBOX_WIN32_UI
1892 gpFramebuffer[0]->setWinId(winId);
1893#endif
1894
1895 for (unsigned i = 0; i < gcMonitors; i++)
1896 {
1897 if (!gpFramebuffer[i]->initialized())
1898 goto leave;
1899 gpFramebuffer[i]->AddRef();
1900 if (fFullscreen)
1901 SetFullscreen(true);
1902 }
1903
1904#ifdef VBOXSDL_WITH_X11
1905 /* NOTE1: We still want Ctrl-C to work, so we undo the SDL redirections.
1906 * NOTE2: We have to remove the PidFile if this file exists. */
1907 signal(SIGINT, signal_handler_SIGINT);
1908 signal(SIGQUIT, signal_handler_SIGINT);
1909 signal(SIGSEGV, signal_handler_SIGINT);
1910#endif
1911
1912
1913 for (ULONG i = 0; i < gcMonitors; i++)
1914 {
1915 // register our framebuffer
1916 hrc = gpDisplay->AttachFramebuffer(i, gpFramebuffer[i], gaFramebufferId[i].asOutParam());
1917 if (FAILED(hrc))
1918 {
1919 RTPrintf("Error: could not register framebuffer object!\n");
1920 goto leave;
1921 }
1922 ULONG dummy;
1923 LONG xOrigin, yOrigin;
1924 GuestMonitorStatus_T monitorStatus;
1925 hrc = gpDisplay->GetScreenResolution(i, &dummy, &dummy, &dummy, &xOrigin, &yOrigin, &monitorStatus);
1926 gpFramebuffer[i]->setOrigin(xOrigin, yOrigin);
1927 }
1928
1929 {
1930 // register listener for VirtualBoxClient events
1931 ComPtr<IEventSource> pES;
1932 CHECK_ERROR(pVirtualBoxClient, COMGETTER(EventSource)(pES.asOutParam()));
1933 ComObjPtr<VBoxSDLClientEventListenerImpl> listener;
1934 listener.createObject();
1935 listener->init(new VBoxSDLClientEventListener());
1936 pVBoxClientListener = listener;
1937 com::SafeArray<VBoxEventType_T> eventTypes;
1938 eventTypes.push_back(VBoxEventType_OnVBoxSVCAvailabilityChanged);
1939 CHECK_ERROR(pES, RegisterListener(pVBoxClientListener, ComSafeArrayAsInParam(eventTypes), true));
1940 }
1941
1942 {
1943 // register listener for VirtualBox (server) events
1944 ComPtr<IEventSource> pES;
1945 CHECK_ERROR(pVirtualBox, COMGETTER(EventSource)(pES.asOutParam()));
1946 ComObjPtr<VBoxSDLEventListenerImpl> listener;
1947 listener.createObject();
1948 listener->init(new VBoxSDLEventListener());
1949 pVBoxListener = listener;
1950 com::SafeArray<VBoxEventType_T> eventTypes;
1951 eventTypes.push_back(VBoxEventType_OnExtraDataChanged);
1952 CHECK_ERROR(pES, RegisterListener(pVBoxListener, ComSafeArrayAsInParam(eventTypes), true));
1953 }
1954
1955 {
1956 // register listener for Console events
1957 ComPtr<IEventSource> pES;
1958 CHECK_ERROR(gpConsole, COMGETTER(EventSource)(pES.asOutParam()));
1959 pConsoleListener.createObject();
1960 pConsoleListener->init(new VBoxSDLConsoleEventListener());
1961 com::SafeArray<VBoxEventType_T> eventTypes;
1962 eventTypes.push_back(VBoxEventType_OnMousePointerShapeChanged);
1963 eventTypes.push_back(VBoxEventType_OnMouseCapabilityChanged);
1964 eventTypes.push_back(VBoxEventType_OnKeyboardLedsChanged);
1965 eventTypes.push_back(VBoxEventType_OnStateChanged);
1966 eventTypes.push_back(VBoxEventType_OnRuntimeError);
1967 eventTypes.push_back(VBoxEventType_OnCanShowWindow);
1968 eventTypes.push_back(VBoxEventType_OnShowWindow);
1969 CHECK_ERROR(pES, RegisterListener(pConsoleListener, ComSafeArrayAsInParam(eventTypes), true));
1970 // until we've tried to to start the VM, ignore power off events
1971 pConsoleListener->getWrapped()->ignorePowerOffEvents(true);
1972 }
1973
1974 if (pszPortVRDP)
1975 {
1976 hrc = gpMachine->COMGETTER(VRDEServer)(gpVRDEServer.asOutParam());
1977 AssertMsg((hrc == S_OK) && gpVRDEServer, ("Could not get VRDP Server! rc = 0x%x\n", hrc));
1978 if (gpVRDEServer)
1979 {
1980 // has a non standard VRDP port been requested?
1981 if (strcmp(pszPortVRDP, "0"))
1982 {
1983 hrc = gpVRDEServer->SetVRDEProperty(Bstr("TCP/Ports").raw(), Bstr(pszPortVRDP).raw());
1984 if (hrc != S_OK)
1985 {
1986 RTPrintf("Error: could not set VRDP port! rc = 0x%x\n", hrc);
1987 goto leave;
1988 }
1989 }
1990 // now enable VRDP
1991 hrc = gpVRDEServer->COMSETTER(Enabled)(TRUE);
1992 if (hrc != S_OK)
1993 {
1994 RTPrintf("Error: could not enable VRDP server! rc = 0x%x\n", hrc);
1995 goto leave;
1996 }
1997 }
1998 }
1999
2000 hrc = E_FAIL;
2001#ifdef VBOXSDL_ADVANCED_OPTIONS
2002 if (u32WarpDrive != 0)
2003 {
2004 if (!gpMachineDebugger)
2005 {
2006 RTPrintf("Error: No debugger object; --warpdrive %d cannot be executed!\n", u32WarpDrive);
2007 goto leave;
2008 }
2009 gpMachineDebugger->COMSETTER(VirtualTimeRate)(u32WarpDrive);
2010 }
2011#endif /* VBOXSDL_ADVANCED_OPTIONS */
2012
2013 /* start with something in the titlebar */
2014 UpdateTitlebar(TITLEBAR_NORMAL);
2015
2016 /* memorize the default cursor */
2017 gpDefaultCursor = SDL_GetCursor();
2018 /*
2019 * Register our user signal handler.
2020 */
2021#ifdef VBOXSDL_WITH_X11
2022 struct sigaction sa;
2023 sa.sa_sigaction = signal_handler_SIGUSR1;
2024 sigemptyset(&sa.sa_mask);
2025 sa.sa_flags = SA_RESTART | SA_SIGINFO;
2026 sigaction(SIGUSR1, &sa, NULL);
2027#endif /* VBOXSDL_WITH_X11 */
2028
2029 /*
2030 * Start the VM execution thread. This has to be done
2031 * asynchronously as powering up can take some time
2032 * (accessing devices such as the host DVD drive). In
2033 * the meantime, we have to service the SDL event loop.
2034 */
2035 SDL_Event event;
2036
2037 if (!fSeparate)
2038 {
2039 LogFlow(("Powering up the VM...\n"));
2040 hrc = gpConsole->PowerUp(gpProgress.asOutParam());
2041 if (hrc != S_OK)
2042 {
2043 com::ErrorInfo info(gpConsole, COM_IIDOF(IConsole));
2044 if (info.isBasicAvailable())
2045 PrintError("Failed to power up VM", info.getText().raw());
2046 else
2047 RTPrintf("Error: failed to power up VM! No error text available.\n");
2048 goto leave;
2049 }
2050 }
2051
2052#ifdef USE_XPCOM_QUEUE_THREAD
2053 /*
2054 * Before we starting to do stuff, we have to launch the XPCOM
2055 * event queue thread. It will wait for events and send messages
2056 * to the SDL thread. After having done this, we should fairly
2057 * quickly start to process the SDL event queue as an XPCOM
2058 * event storm might arrive. Stupid SDL has a ridiculously small
2059 * event queue buffer!
2060 */
2061 startXPCOMEventQueueThread(eventQ->getSelectFD());
2062#endif /* USE_XPCOM_QUEUE_THREAD */
2063
2064 /* termination flag */
2065 bool fTerminateDuringStartup;
2066 fTerminateDuringStartup = false;
2067
2068 LogRel(("VBoxSDL: NUM lock initially %s, CAPS lock initially %s\n",
2069 !!(SDL_GetModState() & KMOD_NUM) ? "ON" : "OFF",
2070 !!(SDL_GetModState() & KMOD_CAPS) ? "ON" : "OFF"));
2071
2072 /* start regular timer so we don't starve in the event loop */
2073 SDL_TimerID sdlTimer;
2074 sdlTimer = SDL_AddTimer(100, StartupTimer, NULL);
2075
2076 /* loop until the powerup processing is done */
2077 MachineState_T machineState;
2078 do
2079 {
2080 hrc = gpMachine->COMGETTER(State)(&machineState);
2081 if ( hrc == S_OK
2082 && ( machineState == MachineState_Starting
2083 || machineState == MachineState_Restoring
2084 || machineState == MachineState_TeleportingIn
2085 )
2086 )
2087 {
2088 /*
2089 * wait for the next event. This is uncritical as
2090 * power up guarantees to change the machine state
2091 * to either running or aborted and a machine state
2092 * change will send us an event. However, we have to
2093 * service the XPCOM event queue!
2094 */
2095#ifdef USE_XPCOM_QUEUE_THREAD
2096 if (!fXPCOMEventThreadSignaled)
2097 {
2098 signalXPCOMEventQueueThread();
2099 fXPCOMEventThreadSignaled = true;
2100 }
2101#endif
2102 /*
2103 * Wait for SDL events.
2104 */
2105 if (WaitSDLEvent(&event))
2106 {
2107 switch (event.type)
2108 {
2109 /*
2110 * Timer event. Used to have the titlebar updated.
2111 */
2112 case SDL_USER_EVENT_TIMER:
2113 {
2114 /*
2115 * Update the title bar.
2116 */
2117 UpdateTitlebar(TITLEBAR_STARTUP);
2118 break;
2119 }
2120
2121 /*
2122 * User specific framebuffer change event.
2123 */
2124 case SDL_USER_EVENT_NOTIFYCHANGE:
2125 {
2126 LogFlow(("SDL_USER_EVENT_NOTIFYCHANGE\n"));
2127 LONG xOrigin, yOrigin;
2128 gpFramebuffer[event.user.code]->notifyChange(event.user.code);
2129 /* update xOrigin, yOrigin -> mouse */
2130 ULONG dummy;
2131 GuestMonitorStatus_T monitorStatus;
2132 hrc = gpDisplay->GetScreenResolution(event.user.code, &dummy, &dummy, &dummy, &xOrigin, &yOrigin, &monitorStatus);
2133 gpFramebuffer[event.user.code]->setOrigin(xOrigin, yOrigin);
2134 break;
2135 }
2136
2137#ifdef USE_XPCOM_QUEUE_THREAD
2138 /*
2139 * User specific XPCOM event queue event
2140 */
2141 case SDL_USER_EVENT_XPCOM_EVENTQUEUE:
2142 {
2143 LogFlow(("SDL_USER_EVENT_XPCOM_EVENTQUEUE: processing XPCOM event queue...\n"));
2144 eventQ->processEventQueue(0);
2145 signalXPCOMEventQueueThread();
2146 break;
2147 }
2148#endif /* USE_XPCOM_QUEUE_THREAD */
2149
2150 /*
2151 * Termination event from the on state change callback.
2152 */
2153 case SDL_USER_EVENT_TERMINATE:
2154 {
2155 if (event.user.code != VBOXSDL_TERM_NORMAL)
2156 {
2157 com::ProgressErrorInfo info(gpProgress);
2158 if (info.isBasicAvailable())
2159 PrintError("Failed to power up VM", info.getText().raw());
2160 else
2161 RTPrintf("Error: failed to power up VM! No error text available.\n");
2162 }
2163 fTerminateDuringStartup = true;
2164 break;
2165 }
2166
2167 default:
2168 {
2169 Log8(("VBoxSDL: Unknown SDL event %d (pre)\n", event.type));
2170 break;
2171 }
2172 }
2173
2174 }
2175 }
2176 eventQ->processEventQueue(0);
2177 } while ( hrc == S_OK
2178 && ( machineState == MachineState_Starting
2179 || machineState == MachineState_Restoring
2180 || machineState == MachineState_TeleportingIn
2181 )
2182 );
2183
2184 /* kill the timer again */
2185 SDL_RemoveTimer(sdlTimer);
2186 sdlTimer = 0;
2187
2188 /* are we supposed to terminate the process? */
2189 if (fTerminateDuringStartup)
2190 goto leave;
2191
2192 /* did the power up succeed? */
2193 if (machineState != MachineState_Running)
2194 {
2195 com::ProgressErrorInfo info(gpProgress);
2196 if (info.isBasicAvailable())
2197 PrintError("Failed to power up VM", info.getText().raw());
2198 else
2199 RTPrintf("Error: failed to power up VM! No error text available (rc = 0x%x state = %d)\n", hrc, machineState);
2200 goto leave;
2201 }
2202
2203 // accept power off events from now on because we're running
2204 // note that there's a possible race condition here...
2205 pConsoleListener->getWrapped()->ignorePowerOffEvents(false);
2206
2207 hrc = gpConsole->COMGETTER(Keyboard)(gpKeyboard.asOutParam());
2208 if (!gpKeyboard)
2209 {
2210 RTPrintf("Error: could not get keyboard object!\n");
2211 goto leave;
2212 }
2213 gpConsole->COMGETTER(Mouse)(gpMouse.asOutParam());
2214 if (!gpMouse)
2215 {
2216 RTPrintf("Error: could not get mouse object!\n");
2217 goto leave;
2218 }
2219
2220 if (fSeparate && gpMouse)
2221 {
2222 LogFlow(("Fetching mouse caps\n"));
2223
2224 /* Fetch current mouse status, etc */
2225 gpMouse->COMGETTER(AbsoluteSupported)(&gfAbsoluteMouseGuest);
2226 gpMouse->COMGETTER(RelativeSupported)(&gfRelativeMouseGuest);
2227 gpMouse->COMGETTER(NeedsHostCursor)(&gfGuestNeedsHostCursor);
2228
2229 HandleGuestCapsChanged();
2230
2231 ComPtr<IMousePointerShape> mps;
2232 gpMouse->COMGETTER(PointerShape)(mps.asOutParam());
2233 if (!mps.isNull())
2234 {
2235 BOOL visible, alpha;
2236 ULONG hotX, hotY, width, height;
2237 com::SafeArray <BYTE> shape;
2238
2239 mps->COMGETTER(Visible)(&visible);
2240 mps->COMGETTER(Alpha)(&alpha);
2241 mps->COMGETTER(HotX)(&hotX);
2242 mps->COMGETTER(HotY)(&hotY);
2243 mps->COMGETTER(Width)(&width);
2244 mps->COMGETTER(Height)(&height);
2245 mps->COMGETTER(Shape)(ComSafeArrayAsOutParam(shape));
2246
2247 if (shape.size() > 0)
2248 {
2249 PointerShapeChangeData data(visible, alpha, hotX, hotY, width, height,
2250 ComSafeArrayAsInParam(shape));
2251 SetPointerShape(&data);
2252 }
2253 }
2254 }
2255
2256 UpdateTitlebar(TITLEBAR_NORMAL);
2257
2258 /*
2259 * Create PID file.
2260 */
2261 if (gpszPidFile)
2262 {
2263 char szBuf[32];
2264 const char *pcszLf = "\n";
2265 RTFILE PidFile;
2266 RTFileOpen(&PidFile, gpszPidFile, RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE);
2267 RTStrFormatNumber(szBuf, RTProcSelf(), 10, 0, 0, 0);
2268 RTFileWrite(PidFile, szBuf, strlen(szBuf), NULL);
2269 RTFileWrite(PidFile, pcszLf, strlen(pcszLf), NULL);
2270 RTFileClose(PidFile);
2271 }
2272
2273 /*
2274 * Main event loop
2275 */
2276#ifdef USE_XPCOM_QUEUE_THREAD
2277 if (!fXPCOMEventThreadSignaled)
2278 {
2279 signalXPCOMEventQueueThread();
2280 }
2281#endif
2282 LogFlow(("VBoxSDL: Entering big event loop\n"));
2283 while (WaitSDLEvent(&event))
2284 {
2285 switch (event.type)
2286 {
2287 /*
2288 * The screen needs to be repainted.
2289 */
2290 case SDL_WINDOWEVENT:
2291 {
2292 switch (event.window.event)
2293 {
2294 case SDL_WINDOWEVENT_EXPOSED:
2295 {
2296 VBoxSDLFB *fb = getFbFromWinId(event.window.windowID);
2297 if (fb)
2298 fb->repaint();
2299 break;
2300 }
2301 case SDL_WINDOWEVENT_FOCUS_GAINED:
2302 {
2303 break;
2304 }
2305 case SDL_WINDOWEVENT_FOCUS_LOST:
2306 {
2307 break;
2308 }
2309 case SDL_WINDOWEVENT_RESIZED:
2310 {
2311 if (gpDisplay)
2312 {
2313 if (gfIgnoreNextResize)
2314 {
2315 gfIgnoreNextResize = FALSE;
2316 break;
2317 }
2318 uResizeWidth = event.window.data1;
2319 uResizeHeight = event.window.data2;
2320 if (gSdlResizeTimer)
2321 SDL_RemoveTimer(gSdlResizeTimer);
2322 gSdlResizeTimer = SDL_AddTimer(300, ResizeTimer, NULL);
2323 }
2324 break;
2325 }
2326 default:
2327 break;
2328 }
2329 break;
2330 }
2331
2332 /*
2333 * Keyboard events.
2334 */
2335 case SDL_KEYDOWN:
2336 case SDL_KEYUP:
2337 {
2338 SDL_Keycode ksym = event.key.keysym.sym;
2339 switch (enmHKeyState)
2340 {
2341 case HKEYSTATE_NORMAL:
2342 {
2343 if ( event.type == SDL_KEYDOWN
2344 && ksym != SDLK_UNKNOWN
2345 && (ksym == gHostKeySym1 || ksym == gHostKeySym2))
2346 {
2347 EvHKeyDown1 = event;
2348 enmHKeyState = ksym == gHostKeySym1 ? HKEYSTATE_DOWN_1ST
2349 : HKEYSTATE_DOWN_2ND;
2350 break;
2351 }
2352 ProcessKey(&event.key);
2353 break;
2354 }
2355
2356 case HKEYSTATE_DOWN_1ST:
2357 case HKEYSTATE_DOWN_2ND:
2358 {
2359 if (gHostKeySym2 != SDLK_UNKNOWN)
2360 {
2361 if ( event.type == SDL_KEYDOWN
2362 && ksym != SDLK_UNKNOWN
2363 && ( (enmHKeyState == HKEYSTATE_DOWN_1ST && ksym == gHostKeySym2)
2364 || (enmHKeyState == HKEYSTATE_DOWN_2ND && ksym == gHostKeySym1)))
2365 {
2366 EvHKeyDown2 = event;
2367 enmHKeyState = HKEYSTATE_DOWN;
2368 break;
2369 }
2370 enmHKeyState = event.type == SDL_KEYUP ? HKEYSTATE_NORMAL
2371 : HKEYSTATE_NOT_IT;
2372 ProcessKey(&EvHKeyDown1.key);
2373 /* ugly hack: Some guests (e.g. mstsc.exe on Windows XP)
2374 * expect a small delay between two key events. 5ms work
2375 * reliable here so use 10ms to be on the safe side. A
2376 * better but more complicated fix would be to introduce
2377 * a new state and don't wait here. */
2378 RTThreadSleep(10);
2379 ProcessKey(&event.key);
2380 break;
2381 }
2382 }
2383 RT_FALL_THRU();
2384
2385 case HKEYSTATE_DOWN:
2386 {
2387 if (event.type == SDL_KEYDOWN)
2388 {
2389 /* potential host key combination, try execute it */
2390 int irc = HandleHostKey(&event.key);
2391 if (irc == VINF_SUCCESS)
2392 {
2393 enmHKeyState = HKEYSTATE_USED;
2394 break;
2395 }
2396 if (RT_SUCCESS(irc))
2397 goto leave;
2398 }
2399 else /* SDL_KEYUP */
2400 {
2401 if ( ksym != SDLK_UNKNOWN
2402 && (ksym == gHostKeySym1 || ksym == gHostKeySym2))
2403 {
2404 /* toggle grabbing state */
2405 if (!gfGrabbed)
2406 InputGrabStart();
2407 else
2408 InputGrabEnd();
2409
2410 /* SDL doesn't always reset the keystates, correct it */
2411 ResetKeys();
2412 enmHKeyState = HKEYSTATE_NORMAL;
2413 break;
2414 }
2415 }
2416
2417 /* not host key */
2418 enmHKeyState = HKEYSTATE_NOT_IT;
2419 ProcessKey(&EvHKeyDown1.key);
2420 /* see the comment for the 2-key case above */
2421 RTThreadSleep(10);
2422 if (gHostKeySym2 != SDLK_UNKNOWN)
2423 {
2424 ProcessKey(&EvHKeyDown2.key);
2425 /* see the comment for the 2-key case above */
2426 RTThreadSleep(10);
2427 }
2428 ProcessKey(&event.key);
2429 break;
2430 }
2431
2432 case HKEYSTATE_USED:
2433 {
2434 if ((SDL_GetModState() & ~(KMOD_MODE | KMOD_NUM | KMOD_RESERVED)) == 0)
2435 enmHKeyState = HKEYSTATE_NORMAL;
2436 if (event.type == SDL_KEYDOWN)
2437 {
2438 int irc = HandleHostKey(&event.key);
2439 if (RT_SUCCESS(irc) && irc != VINF_SUCCESS)
2440 goto leave;
2441 }
2442 break;
2443 }
2444
2445 default:
2446 AssertMsgFailed(("enmHKeyState=%d\n", enmHKeyState));
2447 RT_FALL_THRU();
2448 case HKEYSTATE_NOT_IT:
2449 {
2450 if ((SDL_GetModState() & ~(KMOD_MODE | KMOD_NUM | KMOD_RESERVED)) == 0)
2451 enmHKeyState = HKEYSTATE_NORMAL;
2452 ProcessKey(&event.key);
2453 break;
2454 }
2455 } /* state switch */
2456 break;
2457 }
2458
2459 /*
2460 * The window was closed.
2461 */
2462 case SDL_QUIT:
2463 {
2464 if (!gfACPITerm || gSdlQuitTimer)
2465 goto leave;
2466 if (gpConsole)
2467 gpConsole->PowerButton();
2468 gSdlQuitTimer = SDL_AddTimer(1000, QuitTimer, NULL);
2469 break;
2470 }
2471
2472 /*
2473 * The mouse has moved
2474 */
2475 case SDL_MOUSEMOTION:
2476 {
2477 if (gfGrabbed || UseAbsoluteMouse())
2478 {
2479 VBoxSDLFB *fb;
2480 fb = getFbFromWinId(event.motion.windowID);
2481 AssertPtrBreak(fb);
2482 SendMouseEvent(fb, 0, 0, 0);
2483 }
2484 break;
2485 }
2486
2487 case SDL_MOUSEWHEEL:
2488 {
2489 VBoxSDLFB *fb;
2490 fb = getFbFromWinId(event.button.windowID);
2491 AssertPtrBreak(fb);
2492 SendMouseEvent(fb, -1 * event.wheel.y, 0, 0);
2493 break;
2494 }
2495 /*
2496 * A mouse button has been clicked or released.
2497 */
2498 case SDL_MOUSEBUTTONDOWN:
2499 case SDL_MOUSEBUTTONUP:
2500 {
2501 SDL_MouseButtonEvent *bev = &event.button;
2502 /* don't grab on mouse click if we have guest additions */
2503 if (!gfGrabbed && !UseAbsoluteMouse() && gfGrabOnMouseClick)
2504 {
2505 if (event.type == SDL_MOUSEBUTTONDOWN && (bev->state & SDL_BUTTON_LMASK))
2506 {
2507 /* start grabbing all events */
2508 InputGrabStart();
2509 }
2510 }
2511 else if (gfGrabbed || UseAbsoluteMouse())
2512 {
2513 /* end host key combination (CTRL+MouseButton) */
2514 switch (enmHKeyState)
2515 {
2516 case HKEYSTATE_DOWN_1ST:
2517 case HKEYSTATE_DOWN_2ND:
2518 enmHKeyState = HKEYSTATE_NOT_IT;
2519 ProcessKey(&EvHKeyDown1.key);
2520 /* ugly hack: small delay to ensure that the key event is
2521 * actually handled _prior_ to the mouse click event */
2522 RTThreadSleep(20);
2523 break;
2524 case HKEYSTATE_DOWN:
2525 enmHKeyState = HKEYSTATE_NOT_IT;
2526 ProcessKey(&EvHKeyDown1.key);
2527 if (gHostKeySym2 != SDLK_UNKNOWN)
2528 ProcessKey(&EvHKeyDown2.key);
2529 /* ugly hack: small delay to ensure that the key event is
2530 * actually handled _prior_ to the mouse click event */
2531 RTThreadSleep(20);
2532 break;
2533 default:
2534 break;
2535 }
2536
2537 VBoxSDLFB *fb;
2538 fb = getFbFromWinId(event.button.windowID);
2539 AssertPtrBreak(fb);
2540 SendMouseEvent(fb, 0 /*wheel vertical movement*/, event.type == SDL_MOUSEBUTTONDOWN, bev->button);
2541 }
2542 break;
2543 }
2544
2545#if 0
2546 /*
2547 * The window has gained or lost focus.
2548 */
2549 case SDL_ACTIVEEVENT: /** @todo Needs to be also fixed with SDL2? Check! */
2550 {
2551 /*
2552 * There is a strange behaviour in SDL when running without a window
2553 * manager: When SDL_WM_GrabInput(SDL_GRAB_ON) is called we receive two
2554 * consecutive events SDL_ACTIVEEVENTs (input lost, input gained).
2555 * Asking SDL_GetAppState() seems the better choice.
2556 */
2557 if (gfGrabbed && (SDL_GetAppState() & SDL_APPINPUTFOCUS) == 0)
2558 {
2559 /*
2560 * another window has stolen the (keyboard) input focus
2561 */
2562 InputGrabEnd();
2563 }
2564 break;
2565 }
2566
2567 /*
2568 * The SDL window was resized.
2569 * For SDL2 this is done in SDL_WINDOWEVENT.
2570 */
2571 case SDL_VIDEORESIZE:
2572 {
2573 if (gpDisplay)
2574 {
2575 if (gfIgnoreNextResize)
2576 {
2577 gfIgnoreNextResize = FALSE;
2578 break;
2579 }
2580 uResizeWidth = event.resize.w;
2581 uResizeHeight = event.resize.h;
2582 if (gSdlResizeTimer)
2583 SDL_RemoveTimer(gSdlResizeTimer);
2584 gSdlResizeTimer = SDL_AddTimer(300, ResizeTimer, NULL);
2585 }
2586 break;
2587 }
2588#endif
2589 /*
2590 * User specific update event.
2591 */
2592 /** @todo use a common user event handler so that SDL_PeepEvents() won't
2593 * possibly remove other events in the queue!
2594 */
2595 case SDL_USER_EVENT_UPDATERECT:
2596 {
2597 /*
2598 * Decode event parameters.
2599 */
2600 ASMAtomicDecS32(&g_cNotifyUpdateEventsPending);
2601 #define DECODEX(event) (int)((intptr_t)(event).user.data1 >> 16)
2602 #define DECODEY(event) (int)((intptr_t)(event).user.data1 & 0xFFFF)
2603 #define DECODEW(event) (int)((intptr_t)(event).user.data2 >> 16)
2604 #define DECODEH(event) (int)((intptr_t)(event).user.data2 & 0xFFFF)
2605 int x = DECODEX(event);
2606 int y = DECODEY(event);
2607 int w = DECODEW(event);
2608 int h = DECODEH(event);
2609 LogFlow(("SDL_USER_EVENT_UPDATERECT: x = %d, y = %d, w = %d, h = %d\n",
2610 x, y, w, h));
2611
2612 Assert(gpFramebuffer[event.user.code]);
2613 gpFramebuffer[event.user.code]->update(x, y, w, h, true /* fGuestRelative */);
2614
2615 #undef DECODEX
2616 #undef DECODEY
2617 #undef DECODEW
2618 #undef DECODEH
2619 break;
2620 }
2621
2622 /*
2623 * User event: Window resize done
2624 */
2625 case SDL_USER_EVENT_WINDOW_RESIZE_DONE:
2626 {
2627 /**
2628 * @todo This is a workaround for synchronization problems between EMT and the
2629 * SDL main thread. It can happen that the SDL thread already starts a
2630 * new resize operation while the EMT is still busy with the old one
2631 * leading to a deadlock. Therefore we call SetVideoModeHint only once
2632 * when the mouse button was released.
2633 */
2634 /* communicate the resize event to the guest */
2635 gpDisplay->SetVideoModeHint(0 /*=display*/, true /*=enabled*/, false /*=changeOrigin*/,
2636 0 /*=originX*/, 0 /*=originY*/,
2637 uResizeWidth, uResizeHeight, 0 /*=don't change bpp*/, true /*=notify*/);
2638 break;
2639
2640 }
2641
2642 /*
2643 * User specific framebuffer change event.
2644 */
2645 case SDL_USER_EVENT_NOTIFYCHANGE:
2646 {
2647 LogFlow(("SDL_USER_EVENT_NOTIFYCHANGE\n"));
2648 LONG xOrigin, yOrigin;
2649 gpFramebuffer[event.user.code]->notifyChange(event.user.code);
2650 /* update xOrigin, yOrigin -> mouse */
2651 ULONG dummy;
2652 GuestMonitorStatus_T monitorStatus;
2653 hrc = gpDisplay->GetScreenResolution(event.user.code, &dummy, &dummy, &dummy, &xOrigin, &yOrigin, &monitorStatus);
2654 gpFramebuffer[event.user.code]->setOrigin(xOrigin, yOrigin);
2655 break;
2656 }
2657
2658#ifdef USE_XPCOM_QUEUE_THREAD
2659 /*
2660 * User specific XPCOM event queue event
2661 */
2662 case SDL_USER_EVENT_XPCOM_EVENTQUEUE:
2663 {
2664 LogFlow(("SDL_USER_EVENT_XPCOM_EVENTQUEUE: processing XPCOM event queue...\n"));
2665 eventQ->processEventQueue(0);
2666 signalXPCOMEventQueueThread();
2667 break;
2668 }
2669#endif /* USE_XPCOM_QUEUE_THREAD */
2670
2671 /*
2672 * User specific update title bar notification event
2673 */
2674 case SDL_USER_EVENT_UPDATE_TITLEBAR:
2675 {
2676 UpdateTitlebar(TITLEBAR_NORMAL);
2677 break;
2678 }
2679
2680 /*
2681 * User specific termination event
2682 */
2683 case SDL_USER_EVENT_TERMINATE:
2684 {
2685 if (event.user.code != VBOXSDL_TERM_NORMAL)
2686 RTPrintf("Error: VM terminated abnormally!\n");
2687 goto leave;
2688 }
2689 /*
2690 * User specific pointer shape change event
2691 */
2692 case SDL_USER_EVENT_POINTER_CHANGE:
2693 {
2694 PointerShapeChangeData *data = (PointerShapeChangeData *)event.user.data1;
2695 SetPointerShape (data);
2696 delete data;
2697 break;
2698 }
2699
2700 /*
2701 * User specific guest capabilities changed
2702 */
2703 case SDL_USER_EVENT_GUEST_CAP_CHANGED:
2704 {
2705 HandleGuestCapsChanged();
2706 break;
2707 }
2708
2709 default:
2710 {
2711 Log8(("unknown SDL event %d\n", event.type));
2712 break;
2713 }
2714 }
2715 }
2716
2717leave:
2718 if (gpszPidFile)
2719 RTFileDelete(gpszPidFile);
2720
2721 LogFlow(("leaving...\n"));
2722#if defined(VBOX_WITH_XPCOM) && !defined(RT_OS_DARWIN) && !defined(RT_OS_OS2)
2723 /* make sure the XPCOM event queue thread doesn't do anything harmful */
2724 terminateXPCOMQueueThread();
2725#endif /* VBOX_WITH_XPCOM */
2726
2727 if (gpVRDEServer)
2728 hrc = gpVRDEServer->COMSETTER(Enabled)(FALSE);
2729
2730 /*
2731 * Get the machine state.
2732 */
2733 if (gpMachine)
2734 gpMachine->COMGETTER(State)(&machineState);
2735 else
2736 machineState = MachineState_Aborted;
2737
2738 if (!fSeparate)
2739 {
2740 /*
2741 * Turn off the VM if it's running
2742 */
2743 if ( gpConsole
2744 && ( machineState == MachineState_Running
2745 || machineState == MachineState_Teleporting
2746 || machineState == MachineState_LiveSnapshotting
2747 /** @todo power off paused VMs too? */
2748 )
2749 )
2750 do
2751 {
2752 pConsoleListener->getWrapped()->ignorePowerOffEvents(true);
2753 ComPtr<IProgress> pProgress;
2754 CHECK_ERROR_BREAK(gpConsole, PowerDown(pProgress.asOutParam()));
2755 CHECK_ERROR_BREAK(pProgress, WaitForCompletion(-1));
2756 BOOL completed;
2757 CHECK_ERROR_BREAK(pProgress, COMGETTER(Completed)(&completed));
2758 ASSERT(completed);
2759 LONG hrc2;
2760 CHECK_ERROR_BREAK(pProgress, COMGETTER(ResultCode)(&hrc2));
2761 if (FAILED(hrc2))
2762 {
2763 com::ErrorInfo info;
2764 if (info.isFullAvailable())
2765 PrintError("Failed to power down VM",
2766 info.getText().raw(), info.getComponent().raw());
2767 else
2768 RTPrintf("Failed to power down virtual machine! No error information available (rc=%Rhrc).\n", hrc2);
2769 break;
2770 }
2771 } while (0);
2772 }
2773
2774 /* unregister Console listener */
2775 if (pConsoleListener)
2776 {
2777 ComPtr<IEventSource> pES;
2778 CHECK_ERROR(gpConsole, COMGETTER(EventSource)(pES.asOutParam()));
2779 if (!pES.isNull())
2780 CHECK_ERROR(pES, UnregisterListener(pConsoleListener));
2781 pConsoleListener.setNull();
2782 }
2783
2784 /*
2785 * Now we discard all settings so that our changes will
2786 * not be flushed to the permanent configuration
2787 */
2788 if ( gpMachine
2789 && machineState != MachineState_Saved
2790 && machineState != MachineState_AbortedSaved)
2791 {
2792 hrc = gpMachine->DiscardSettings();
2793 AssertMsg(SUCCEEDED(hrc), ("DiscardSettings %Rhrc, machineState %d\n", hrc, machineState));
2794 }
2795
2796 /* close the session */
2797 if (sessionOpened)
2798 {
2799 hrc = pSession->UnlockMachine();
2800 AssertComRC(hrc);
2801 }
2802
2803 LogFlow(("Releasing mouse, keyboard, remote desktop server, display, console...\n"));
2804 if (gpDisplay)
2805 {
2806 for (unsigned i = 0; i < gcMonitors; i++)
2807 gpDisplay->DetachFramebuffer(i, gaFramebufferId[i].raw());
2808 }
2809
2810 gpMouse = NULL;
2811 gpKeyboard = NULL;
2812 gpVRDEServer = NULL;
2813 gpDisplay = NULL;
2814 gpConsole = NULL;
2815 gpMachineDebugger = NULL;
2816 gpProgress = NULL;
2817 // we can only uninitialize SDL here because it is not threadsafe
2818
2819 for (unsigned i = 0; i < gcMonitors; i++)
2820 {
2821 if (gpFramebuffer[i])
2822 {
2823 LogFlow(("Releasing framebuffer...\n"));
2824 gpFramebuffer[i]->Release();
2825 gpFramebuffer[i] = NULL;
2826 }
2827 }
2828
2829 VBoxSDLFB::uninit();
2830
2831 /* VirtualBox (server) listener unregistration. */
2832 if (pVBoxListener)
2833 {
2834 ComPtr<IEventSource> pES;
2835 CHECK_ERROR(pVirtualBox, COMGETTER(EventSource)(pES.asOutParam()));
2836 if (!pES.isNull())
2837 CHECK_ERROR(pES, UnregisterListener(pVBoxListener));
2838 pVBoxListener.setNull();
2839 }
2840
2841 /* VirtualBoxClient listener unregistration. */
2842 if (pVBoxClientListener)
2843 {
2844 ComPtr<IEventSource> pES;
2845 CHECK_ERROR(pVirtualBoxClient, COMGETTER(EventSource)(pES.asOutParam()));
2846 if (!pES.isNull())
2847 CHECK_ERROR(pES, UnregisterListener(pVBoxClientListener));
2848 pVBoxClientListener.setNull();
2849 }
2850
2851 LogFlow(("Releasing machine, session...\n"));
2852 gpMachine = NULL;
2853 pSession = NULL;
2854 LogFlow(("Releasing VirtualBox object...\n"));
2855 pVirtualBox = NULL;
2856 LogFlow(("Releasing VirtualBoxClient object...\n"));
2857 pVirtualBoxClient = NULL;
2858
2859 // end "all-stuff" scope
2860 ////////////////////////////////////////////////////////////////////////////
2861 }
2862
2863 /* Must be before com::Shutdown() */
2864 LogFlow(("Uninitializing COM...\n"));
2865 com::Shutdown();
2866
2867 LogFlow(("Returning from main()!\n"));
2868 RTLogFlush(NULL);
2869
2870#ifdef RT_OS_WINDOWS
2871 FreeConsole(); /* Detach or destroy (from) console. */
2872#endif
2873
2874 return FAILED(hrc) ? 1 : 0;
2875}
2876
2877#ifndef VBOX_WITH_HARDENING
2878/**
2879 * Main entry point
2880 */
2881int main(int argc, char **argv)
2882{
2883#ifdef Q_WS_X11
2884 if (!XInitThreads())
2885 return 1;
2886#endif
2887 /*
2888 * Before we do *anything*, we initialize the runtime.
2889 */
2890 int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_TRY_SUPLIB);
2891 if (RT_FAILURE(rc))
2892 return RTMsgInitFailure(rc);
2893
2894 return TrustedMain(argc, argv, NULL);
2895}
2896#endif /* !VBOX_WITH_HARDENING */
2897
2898
2899/**
2900 * Returns whether the absolute mouse is in use, i.e. both host
2901 * and guest have opted to enable it.
2902 *
2903 * @returns bool Flag whether the absolute mouse is in use
2904 */
2905static bool UseAbsoluteMouse(void)
2906{
2907 return (gfAbsoluteMouseHost && gfAbsoluteMouseGuest);
2908}
2909
2910
2911/**
2912 * Releases any modifier keys that are currently in pressed state.
2913 */
2914static void ResetKeys(void)
2915{
2916 int i;
2917
2918 if (!gpKeyboard)
2919 return;
2920
2921 for(i = 0; i < 256; i++)
2922 {
2923 if (gaModifiersState[i])
2924 {
2925 if (i & 0x80)
2926 gpKeyboard->PutScancode(0xe0);
2927 gpKeyboard->PutScancode(i | 0x80);
2928 gaModifiersState[i] = 0;
2929 }
2930 }
2931}
2932
2933/**
2934 * Keyboard event handler.
2935 *
2936 * @param ev SDL keyboard event.
2937 */
2938static void ProcessKey(SDL_KeyboardEvent *ev)
2939{
2940 /* According to SDL2/SDL_scancodes.h ev->keysym.sym stores scancodes which are
2941 * based on USB usage page standard. This is what we can directly pass to
2942 * IKeyboard::putUsageCode. */
2943 gpKeyboard->PutUsageCode(SDL_GetScancodeFromKey(ev->keysym.sym), 0x07, ev->type == SDL_KEYUP ? TRUE : FALSE);
2944}
2945
2946#ifdef RT_OS_DARWIN
2947#include <Carbon/Carbon.h>
2948RT_C_DECLS_BEGIN
2949/* Private interface in 10.3 and later. */
2950typedef int CGSConnection;
2951typedef enum
2952{
2953 kCGSGlobalHotKeyEnable = 0,
2954 kCGSGlobalHotKeyDisable,
2955 kCGSGlobalHotKeyInvalid = -1 /* bird */
2956} CGSGlobalHotKeyOperatingMode;
2957extern CGSConnection _CGSDefaultConnection(void);
2958extern CGError CGSGetGlobalHotKeyOperatingMode(CGSConnection Connection, CGSGlobalHotKeyOperatingMode *enmMode);
2959extern CGError CGSSetGlobalHotKeyOperatingMode(CGSConnection Connection, CGSGlobalHotKeyOperatingMode enmMode);
2960RT_C_DECLS_END
2961
2962/** Keeping track of whether we disabled the hotkeys or not. */
2963static bool g_fHotKeysDisabled = false;
2964/** Whether we've connected or not. */
2965static bool g_fConnectedToCGS = false;
2966/** Cached connection. */
2967static CGSConnection g_CGSConnection;
2968
2969/**
2970 * Disables or enabled global hot keys.
2971 */
2972static void DisableGlobalHotKeys(bool fDisable)
2973{
2974 if (!g_fConnectedToCGS)
2975 {
2976 g_CGSConnection = _CGSDefaultConnection();
2977 g_fConnectedToCGS = true;
2978 }
2979
2980 /* get current mode. */
2981 CGSGlobalHotKeyOperatingMode enmMode = kCGSGlobalHotKeyInvalid;
2982 CGSGetGlobalHotKeyOperatingMode(g_CGSConnection, &enmMode);
2983
2984 /* calc new mode. */
2985 if (fDisable)
2986 {
2987 if (enmMode != kCGSGlobalHotKeyEnable)
2988 return;
2989 enmMode = kCGSGlobalHotKeyDisable;
2990 }
2991 else
2992 {
2993 if ( enmMode != kCGSGlobalHotKeyDisable
2994 /*|| !g_fHotKeysDisabled*/)
2995 return;
2996 enmMode = kCGSGlobalHotKeyEnable;
2997 }
2998
2999 /* try set it and check the actual result. */
3000 CGSSetGlobalHotKeyOperatingMode(g_CGSConnection, enmMode);
3001 CGSGlobalHotKeyOperatingMode enmNewMode = kCGSGlobalHotKeyInvalid;
3002 CGSGetGlobalHotKeyOperatingMode(g_CGSConnection, &enmNewMode);
3003 if (enmNewMode == enmMode)
3004 g_fHotKeysDisabled = enmMode == kCGSGlobalHotKeyDisable;
3005}
3006#endif /* RT_OS_DARWIN */
3007
3008/**
3009 * Start grabbing the mouse.
3010 */
3011static void InputGrabStart(void)
3012{
3013#ifdef RT_OS_DARWIN
3014 DisableGlobalHotKeys(true);
3015#endif
3016 if (!gfGuestNeedsHostCursor && gfRelativeMouseGuest)
3017 SDL_ShowCursor(SDL_DISABLE);
3018 SDL_SetRelativeMouseMode(SDL_TRUE);
3019 gfGrabbed = TRUE;
3020 UpdateTitlebar(TITLEBAR_NORMAL);
3021}
3022
3023/**
3024 * End mouse grabbing.
3025 */
3026static void InputGrabEnd(void)
3027{
3028 SDL_SetRelativeMouseMode(SDL_FALSE);
3029 if (!gfGuestNeedsHostCursor && gfRelativeMouseGuest)
3030 SDL_ShowCursor(SDL_ENABLE);
3031#ifdef RT_OS_DARWIN
3032 DisableGlobalHotKeys(false);
3033#endif
3034 gfGrabbed = FALSE;
3035 UpdateTitlebar(TITLEBAR_NORMAL);
3036}
3037
3038/**
3039 * Query mouse position and button state from SDL and send to the VM
3040 *
3041 * @param dz Relative mouse wheel movement
3042 */
3043static void SendMouseEvent(VBoxSDLFB *fb, int dz, int down, int button)
3044{
3045 int x, y, state, buttons;
3046 bool abs;
3047
3048 if (!fb)
3049 {
3050 SDL_GetMouseState(&x, &y);
3051 RTPrintf("MouseEvent: Cannot find fb mouse = %d,%d\n", x, y);
3052 return;
3053 }
3054
3055 /*
3056 * If supported and we're not in grabbed mode, we'll use the absolute mouse.
3057 * If we are in grabbed mode and the guest is not able to draw the mouse cursor
3058 * itself, or can't handle relative reporting, we have to use absolute
3059 * coordinates, otherwise the host cursor and
3060 * the coordinates the guest thinks the mouse is at could get out-of-sync. From
3061 * the SDL mailing list:
3062 *
3063 * "The event processing is usually asynchronous and so somewhat delayed, and
3064 * SDL_GetMouseState is returning the immediate mouse state. So at the time you
3065 * call SDL_GetMouseState, the "button" is already up."
3066 */
3067 abs = (UseAbsoluteMouse() && !gfGrabbed)
3068 || gfGuestNeedsHostCursor
3069 || !gfRelativeMouseGuest;
3070
3071 /* only used if abs == TRUE */
3072 int xOrigin = fb->getOriginX();
3073 int yOrigin = fb->getOriginY();
3074 int xMin = fb->getXOffset() + xOrigin;
3075 int yMin = fb->getYOffset() + yOrigin;
3076 int xMax = xMin + (int)fb->getGuestXRes();
3077 int yMax = yMin + (int)fb->getGuestYRes();
3078
3079 state = abs ? SDL_GetMouseState(&x, &y)
3080 : SDL_GetRelativeMouseState(&x, &y);
3081
3082 /*
3083 * process buttons
3084 */
3085 buttons = 0;
3086 if (state & SDL_BUTTON(SDL_BUTTON_LEFT))
3087 buttons |= MouseButtonState_LeftButton;
3088 if (state & SDL_BUTTON(SDL_BUTTON_RIGHT))
3089 buttons |= MouseButtonState_RightButton;
3090 if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE))
3091 buttons |= MouseButtonState_MiddleButton;
3092
3093 if (abs)
3094 {
3095 x += xOrigin;
3096 y += yOrigin;
3097
3098 /*
3099 * Check if the mouse event is inside the guest area. This solves the
3100 * following problem: Some guests switch off the VBox hardware mouse
3101 * cursor and draw the mouse cursor itself instead. Moving the mouse
3102 * outside the guest area then leads to annoying mouse hangs if we
3103 * don't pass mouse motion events into the guest.
3104 */
3105 if (x < xMin || y < yMin || x > xMax || y > yMax)
3106 {
3107 /*
3108 * Cursor outside of valid guest area (outside window or in secure
3109 * label area. Don't allow any mouse button press.
3110 */
3111 button = 0;
3112
3113 /*
3114 * Release any pressed button.
3115 */
3116#if 0
3117 /* disabled on customers request */
3118 buttons &= ~(MouseButtonState_LeftButton |
3119 MouseButtonState_MiddleButton |
3120 MouseButtonState_RightButton);
3121#endif
3122
3123 /*
3124 * Prevent negative coordinates.
3125 */
3126 if (x < xMin) x = xMin;
3127 if (x > xMax) x = xMax;
3128 if (y < yMin) y = yMin;
3129 if (y > yMax) y = yMax;
3130
3131 if (!gpOffCursor)
3132 {
3133 gpOffCursor = SDL_GetCursor(); /* Cursor image */
3134 gfOffCursorActive = SDL_ShowCursor(-1); /* enabled / disabled */
3135 SDL_SetCursor(gpDefaultCursor);
3136 SDL_ShowCursor(SDL_ENABLE);
3137 }
3138 }
3139 else
3140 {
3141 if (gpOffCursor)
3142 {
3143 /*
3144 * We just entered the valid guest area. Restore the guest mouse
3145 * cursor.
3146 */
3147 SDL_SetCursor(gpOffCursor);
3148 SDL_ShowCursor(gfOffCursorActive ? SDL_ENABLE : SDL_DISABLE);
3149 gpOffCursor = NULL;
3150 }
3151 }
3152 }
3153
3154 /*
3155 * Button was pressed but that press is not reflected in the button state?
3156 */
3157 if (down && !(state & SDL_BUTTON(button)))
3158 {
3159 /*
3160 * It can happen that a mouse up event follows a mouse down event immediately
3161 * and we see the events when the bit in the button state is already cleared
3162 * again. In that case we simulate the mouse down event.
3163 */
3164 int tmp_button = 0;
3165 switch (button)
3166 {
3167 case SDL_BUTTON_LEFT: tmp_button = MouseButtonState_LeftButton; break;
3168 case SDL_BUTTON_MIDDLE: tmp_button = MouseButtonState_MiddleButton; break;
3169 case SDL_BUTTON_RIGHT: tmp_button = MouseButtonState_RightButton; break;
3170 }
3171
3172 if (abs)
3173 {
3174 /**
3175 * @todo
3176 * PutMouseEventAbsolute() expects x and y starting from 1,1.
3177 * should we do the increment internally in PutMouseEventAbsolute()
3178 * or state it in PutMouseEventAbsolute() docs?
3179 */
3180 gpMouse->PutMouseEventAbsolute(x + 1 - xMin + xOrigin,
3181 y + 1 - yMin + yOrigin,
3182 dz, 0 /* horizontal scroll wheel */,
3183 buttons | tmp_button);
3184 }
3185 else
3186 {
3187 gpMouse->PutMouseEvent(0, 0, dz,
3188 0 /* horizontal scroll wheel */,
3189 buttons | tmp_button);
3190 }
3191 }
3192
3193 // now send the mouse event
3194 if (abs)
3195 {
3196 /**
3197 * @todo
3198 * PutMouseEventAbsolute() expects x and y starting from 1,1.
3199 * should we do the increment internally in PutMouseEventAbsolute()
3200 * or state it in PutMouseEventAbsolute() docs?
3201 */
3202 gpMouse->PutMouseEventAbsolute(x + 1 - xMin + xOrigin,
3203 y + 1 - yMin + yOrigin,
3204 dz, 0 /* Horizontal wheel */, buttons);
3205 }
3206 else
3207 {
3208 gpMouse->PutMouseEvent(x, y, dz, 0 /* Horizontal wheel */, buttons);
3209 }
3210}
3211
3212/**
3213 * Resets the VM
3214 */
3215void ResetVM(void)
3216{
3217 if (gpConsole)
3218 gpConsole->Reset();
3219}
3220
3221/**
3222 * Initiates a saved state and updates the titlebar with progress information
3223 */
3224void SaveState(void)
3225{
3226 ResetKeys();
3227 RTThreadYield();
3228 if (gfGrabbed)
3229 InputGrabEnd();
3230 RTThreadYield();
3231 UpdateTitlebar(TITLEBAR_SAVE);
3232 gpProgress = NULL;
3233 HRESULT hrc = gpMachine->SaveState(gpProgress.asOutParam());
3234 if (FAILED(hrc))
3235 {
3236 RTPrintf("Error saving state! rc=%Rhrc\n", hrc);
3237 return;
3238 }
3239 Assert(gpProgress);
3240
3241 /*
3242 * Wait for the operation to be completed and work
3243 * the title bar in the mean while.
3244 */
3245 ULONG cPercent = 0;
3246#ifndef RT_OS_DARWIN /* don't break the other guys yet. */
3247 for (;;)
3248 {
3249 BOOL fCompleted = false;
3250 hrc = gpProgress->COMGETTER(Completed)(&fCompleted);
3251 if (FAILED(hrc) || fCompleted)
3252 break;
3253 ULONG cPercentNow;
3254 hrc = gpProgress->COMGETTER(Percent)(&cPercentNow);
3255 if (FAILED(hrc))
3256 break;
3257 if (cPercentNow != cPercent)
3258 {
3259 UpdateTitlebar(TITLEBAR_SAVE, cPercent);
3260 cPercent = cPercentNow;
3261 }
3262
3263 /* wait */
3264 hrc = gpProgress->WaitForCompletion(100);
3265 if (FAILED(hrc))
3266 break;
3267 /// @todo process gui events.
3268 }
3269
3270#else /* new loop which processes GUI events while saving. */
3271
3272 /* start regular timer so we don't starve in the event loop */
3273 SDL_TimerID sdlTimer;
3274 sdlTimer = SDL_AddTimer(100, StartupTimer, NULL);
3275
3276 for (;;)
3277 {
3278 /*
3279 * Check for completion.
3280 */
3281 BOOL fCompleted = false;
3282 hrc = gpProgress->COMGETTER(Completed)(&fCompleted);
3283 if (FAILED(hrc) || fCompleted)
3284 break;
3285 ULONG cPercentNow;
3286 hrc = gpProgress->COMGETTER(Percent)(&cPercentNow);
3287 if (FAILED(hrc))
3288 break;
3289 if (cPercentNow != cPercent)
3290 {
3291 UpdateTitlebar(TITLEBAR_SAVE, cPercent);
3292 cPercent = cPercentNow;
3293 }
3294
3295 /*
3296 * Wait for and process GUI a event.
3297 * This is necessary for XPCOM IPC and for updating the
3298 * title bar on the Mac.
3299 */
3300 SDL_Event event;
3301 if (WaitSDLEvent(&event))
3302 {
3303 switch (event.type)
3304 {
3305 /*
3306 * Timer event preventing us from getting stuck.
3307 */
3308 case SDL_USER_EVENT_TIMER:
3309 break;
3310
3311#ifdef USE_XPCOM_QUEUE_THREAD
3312 /*
3313 * User specific XPCOM event queue event
3314 */
3315 case SDL_USER_EVENT_XPCOM_EVENTQUEUE:
3316 {
3317 LogFlow(("SDL_USER_EVENT_XPCOM_EVENTQUEUE: processing XPCOM event queue...\n"));
3318 eventQ->ProcessPendingEvents();
3319 signalXPCOMEventQueueThread();
3320 break;
3321 }
3322#endif /* USE_XPCOM_QUEUE_THREAD */
3323
3324
3325 /*
3326 * Ignore all other events.
3327 */
3328 case SDL_USER_EVENT_NOTIFYCHANGE:
3329 case SDL_USER_EVENT_TERMINATE:
3330 default:
3331 break;
3332 }
3333 }
3334 }
3335
3336 /* kill the timer */
3337 SDL_RemoveTimer(sdlTimer);
3338 sdlTimer = 0;
3339
3340#endif /* RT_OS_DARWIN */
3341
3342 /*
3343 * What's the result of the operation?
3344 */
3345 LONG lrc;
3346 hrc = gpProgress->COMGETTER(ResultCode)(&lrc);
3347 if (FAILED(hrc))
3348 lrc = ~0;
3349 if (!lrc)
3350 {
3351 UpdateTitlebar(TITLEBAR_SAVE, 100);
3352 RTThreadYield();
3353 RTPrintf("Saved the state successfully.\n");
3354 }
3355 else
3356 RTPrintf("Error saving state, lrc=%d (%#x)\n", lrc, lrc);
3357}
3358
3359/**
3360 * Build the titlebar string
3361 */
3362static void UpdateTitlebar(TitlebarMode mode, uint32_t u32User)
3363{
3364 static char szTitle[1024] = {0};
3365
3366 /* back up current title */
3367 char szPrevTitle[1024];
3368 strcpy(szPrevTitle, szTitle);
3369
3370 Bstr bstrName;
3371 gpMachine->COMGETTER(Name)(bstrName.asOutParam());
3372
3373 RTStrPrintf(szTitle, sizeof(szTitle), "%s - " VBOX_PRODUCT,
3374 !bstrName.isEmpty() ? Utf8Str(bstrName).c_str() : "<noname>");
3375
3376 /* which mode are we in? */
3377 switch (mode)
3378 {
3379 case TITLEBAR_NORMAL:
3380 {
3381 MachineState_T machineState;
3382 gpMachine->COMGETTER(State)(&machineState);
3383 if (machineState == MachineState_Paused)
3384 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle), " - [Paused]");
3385
3386 if (gfGrabbed)
3387 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle), " - [Input captured]");
3388
3389#if defined(DEBUG) || defined(VBOX_WITH_STATISTICS)
3390 // do we have a debugger interface
3391 if (gpMachineDebugger)
3392 {
3393 // query the machine state
3394 BOOL singlestepEnabled = FALSE;
3395 BOOL logEnabled = FALSE;
3396 VMExecutionEngine_T enmExecEngine = VMExecutionEngine_NotSet;
3397 ULONG virtualTimeRate = 100;
3398 gpMachineDebugger->COMGETTER(LogEnabled)(&logEnabled);
3399 gpMachineDebugger->COMGETTER(SingleStep)(&singlestepEnabled);
3400 gpMachineDebugger->COMGETTER(ExecutionEngine)(&enmExecEngine);
3401 gpMachineDebugger->COMGETTER(VirtualTimeRate)(&virtualTimeRate);
3402 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
3403 " [STEP=%d LOG=%d EXEC=%s",
3404 singlestepEnabled == TRUE, logEnabled == TRUE,
3405 enmExecEngine == VMExecutionEngine_NotSet ? "NotSet"
3406 : enmExecEngine == VMExecutionEngine_Emulated ? "IEM"
3407 : enmExecEngine == VMExecutionEngine_HwVirt ? "HM"
3408 : enmExecEngine == VMExecutionEngine_NativeApi ? "NEM" : "UNK");
3409 char *psz = strchr(szTitle, '\0');
3410 if (virtualTimeRate != 100)
3411 RTStrPrintf(psz, &szTitle[sizeof(szTitle)] - psz, " WD=%d%%]", virtualTimeRate);
3412 else
3413 RTStrPrintf(psz, &szTitle[sizeof(szTitle)] - psz, "]");
3414 }
3415#endif /* DEBUG || VBOX_WITH_STATISTICS */
3416 break;
3417 }
3418
3419 case TITLEBAR_STARTUP:
3420 {
3421 /*
3422 * Format it.
3423 */
3424 MachineState_T machineState;
3425 gpMachine->COMGETTER(State)(&machineState);
3426 if (machineState == MachineState_Starting)
3427 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
3428 " - Starting...");
3429 else if (machineState == MachineState_Restoring)
3430 {
3431 ULONG cPercentNow;
3432 HRESULT hrc = gpProgress->COMGETTER(Percent)(&cPercentNow);
3433 if (SUCCEEDED(hrc))
3434 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
3435 " - Restoring %d%%...", (int)cPercentNow);
3436 else
3437 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
3438 " - Restoring...");
3439 }
3440 else if (machineState == MachineState_TeleportingIn)
3441 {
3442 ULONG cPercentNow;
3443 HRESULT hrc = gpProgress->COMGETTER(Percent)(&cPercentNow);
3444 if (SUCCEEDED(hrc))
3445 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
3446 " - Teleporting %d%%...", (int)cPercentNow);
3447 else
3448 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
3449 " - Teleporting...");
3450 }
3451 /* ignore other states, we could already be in running or aborted state */
3452 break;
3453 }
3454
3455 case TITLEBAR_SAVE:
3456 {
3457 AssertMsg(u32User <= 100, ("%d\n", u32User));
3458 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
3459 " - Saving %d%%...", u32User);
3460 break;
3461 }
3462
3463 case TITLEBAR_SNAPSHOT:
3464 {
3465 AssertMsg(u32User <= 100, ("%d\n", u32User));
3466 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
3467 " - Taking snapshot %d%%...", u32User);
3468 break;
3469 }
3470
3471 default:
3472 RTPrintf("Error: Invalid title bar mode %d!\n", mode);
3473 return;
3474 }
3475
3476 /*
3477 * Don't update if it didn't change.
3478 */
3479 if (!strcmp(szTitle, szPrevTitle))
3480 return;
3481
3482 /*
3483 * Set the new title
3484 */
3485#ifdef VBOX_WIN32_UI
3486 setUITitle(szTitle);
3487#else
3488 for (unsigned i = 0; i < gcMonitors; i++)
3489 gpFramebuffer[i]->setWindowTitle(szTitle);
3490#endif
3491}
3492
3493#if 0
3494static void vbox_show_shape(unsigned short w, unsigned short h,
3495 uint32_t bg, const uint8_t *image)
3496{
3497 size_t x, y;
3498 unsigned short pitch;
3499 const uint32_t *color;
3500 const uint8_t *mask;
3501 size_t size_mask;
3502
3503 mask = image;
3504 pitch = (w + 7) / 8;
3505 size_mask = (pitch * h + 3) & ~3;
3506
3507 color = (const uint32_t *)(image + size_mask);
3508
3509 printf("show_shape %dx%d pitch %d size mask %d\n",
3510 w, h, pitch, size_mask);
3511 for (y = 0; y < h; ++y, mask += pitch, color += w)
3512 {
3513 for (x = 0; x < w; ++x) {
3514 if (mask[x / 8] & (1 << (7 - (x % 8))))
3515 printf(" ");
3516 else
3517 {
3518 uint32_t c = color[x];
3519 if (c == bg)
3520 printf("Y");
3521 else
3522 printf("X");
3523 }
3524 }
3525 printf("\n");
3526 }
3527}
3528#endif
3529
3530/**
3531 * Sets the pointer shape according to parameters.
3532 * Must be called only from the main SDL thread.
3533 */
3534static void SetPointerShape(const PointerShapeChangeData *data)
3535{
3536 /*
3537 * don't allow to change the pointer shape if we are outside the valid
3538 * guest area. In that case set standard mouse pointer is set and should
3539 * not get overridden.
3540 */
3541 if (gpOffCursor)
3542 return;
3543
3544 if (data->shape.size() > 0)
3545 {
3546 bool ok = false;
3547
3548#if defined(RT_OS_WINDOWS) || (defined(VBOXSDL_WITH_X11) && !defined(VBOX_WITHOUT_XCURSOR))
3549 AssertReturnVoid(data->height); /* Prevent division by zero. */
3550 uint32_t const andMaskSize = (data->width + 7) / 8 * data->height;
3551 uint32_t const srcShapePtrScan = data->width * 4;
3552
3553 uint8_t const *shape = data->shape.raw();
3554 uint8_t const *srcAndMaskPtr = shape;
3555 uint8_t const *srcShapePtr = shape + ((andMaskSize + 3) & ~3);
3556#endif
3557
3558#if 0
3559 /* pointer debugging code */
3560 // vbox_show_shape(data->width, data->height, 0, data->shape);
3561 uint32_t shapeSize = ((((data->width + 7) / 8) * data->height + 3) & ~3) + data->width * 4 * data->height;
3562 printf("visible: %d\n", data->visible);
3563 printf("width = %d\n", data->width);
3564 printf("height = %d\n", data->height);
3565 printf("alpha = %d\n", data->alpha);
3566 printf("xhot = %d\n", data->xHot);
3567 printf("yhot = %d\n", data->yHot);
3568 printf("uint8_t pointerdata[] = { ");
3569 for (uint32_t i = 0; i < shapeSize; i++)
3570 {
3571 printf("0x%x, ", data->shape[i]);
3572 }
3573 printf("};\n");
3574#endif
3575
3576#if defined(RT_OS_WINDOWS)
3577 uint32_t srcShapePtrScan = data->width * 4;
3578 const uint8_t *srcAndMaskPtr = shape;
3579 const uint8_t *srcShapePtr = shape + ((andMaskSize + 3) & ~3);
3580 uint32_t andMaskSize = (data->width + 7) / 8 * data->height;
3581 const uint8_t* shape = data->shape.raw();
3582 BITMAPV5HEADER bi;
3583 HBITMAP hBitmap;
3584 void *lpBits;
3585
3586 ::ZeroMemory(&bi, sizeof(BITMAPV5HEADER));
3587 bi.bV5Size = sizeof(BITMAPV5HEADER);
3588 bi.bV5Width = data->width;
3589 bi.bV5Height = -(LONG)data->height;
3590 bi.bV5Planes = 1;
3591 bi.bV5BitCount = 32;
3592 bi.bV5Compression = BI_BITFIELDS;
3593 // specify a supported 32 BPP alpha format for Windows XP
3594 bi.bV5RedMask = 0x00FF0000;
3595 bi.bV5GreenMask = 0x0000FF00;
3596 bi.bV5BlueMask = 0x000000FF;
3597 if (data->alpha)
3598 bi.bV5AlphaMask = 0xFF000000;
3599 else
3600 bi.bV5AlphaMask = 0;
3601
3602 HDC hdc = ::GetDC(NULL);
3603
3604 // create the DIB section with an alpha channel
3605 hBitmap = ::CreateDIBSection(hdc, (BITMAPINFO *)&bi, DIB_RGB_COLORS,
3606 (void **)&lpBits, NULL, (DWORD)0);
3607
3608 ::ReleaseDC(NULL, hdc);
3609
3610 HBITMAP hMonoBitmap = NULL;
3611 if (data->alpha)
3612 {
3613 // create an empty mask bitmap
3614 hMonoBitmap = ::CreateBitmap(data->width, data->height, 1, 1, NULL);
3615 }
3616 else
3617 {
3618 /* Word aligned AND mask. Will be allocated and created if necessary. */
3619 uint8_t *pu8AndMaskWordAligned = NULL;
3620
3621 /* Width in bytes of the original AND mask scan line. */
3622 uint32_t cbAndMaskScan = (data->width + 7) / 8;
3623
3624 if (cbAndMaskScan & 1)
3625 {
3626 /* Original AND mask is not word aligned. */
3627
3628 /* Allocate memory for aligned AND mask. */
3629 pu8AndMaskWordAligned = (uint8_t *)RTMemTmpAllocZ((cbAndMaskScan + 1) * data->height);
3630
3631 Assert(pu8AndMaskWordAligned);
3632
3633 if (pu8AndMaskWordAligned)
3634 {
3635 /* According to MSDN the padding bits must be 0.
3636 * Compute the bit mask to set padding bits to 0 in the last byte of original AND mask.
3637 */
3638 uint32_t u32PaddingBits = cbAndMaskScan * 8 - data->width;
3639 Assert(u32PaddingBits < 8);
3640 uint8_t u8LastBytesPaddingMask = (uint8_t)(0xFF << u32PaddingBits);
3641
3642 Log(("u8LastBytesPaddingMask = %02X, aligned w = %d, width = %d, cbAndMaskScan = %d\n",
3643 u8LastBytesPaddingMask, (cbAndMaskScan + 1) * 8, data->width, cbAndMaskScan));
3644
3645 uint8_t *src = (uint8_t *)srcAndMaskPtr;
3646 uint8_t *dst = pu8AndMaskWordAligned;
3647
3648 unsigned i;
3649 for (i = 0; i < data->height; i++)
3650 {
3651 memcpy(dst, src, cbAndMaskScan);
3652
3653 dst[cbAndMaskScan - 1] &= u8LastBytesPaddingMask;
3654
3655 src += cbAndMaskScan;
3656 dst += cbAndMaskScan + 1;
3657 }
3658 }
3659 }
3660
3661 // create the AND mask bitmap
3662 hMonoBitmap = ::CreateBitmap(data->width, data->height, 1, 1,
3663 pu8AndMaskWordAligned? pu8AndMaskWordAligned: srcAndMaskPtr);
3664
3665 if (pu8AndMaskWordAligned)
3666 {
3667 RTMemTmpFree(pu8AndMaskWordAligned);
3668 }
3669 }
3670
3671 Assert(hBitmap);
3672 Assert(hMonoBitmap);
3673 if (hBitmap && hMonoBitmap)
3674 {
3675 DWORD *dstShapePtr = (DWORD *)lpBits;
3676
3677 for (uint32_t y = 0; y < data->height; y ++)
3678 {
3679 memcpy(dstShapePtr, srcShapePtr, srcShapePtrScan);
3680 srcShapePtr += srcShapePtrScan;
3681 dstShapePtr += data->width;
3682 }
3683 }
3684
3685 if (hMonoBitmap)
3686 ::DeleteObject(hMonoBitmap);
3687 if (hBitmap)
3688 ::DeleteObject(hBitmap);
3689
3690#elif defined(VBOXSDL_WITH_X11) && !defined(VBOX_WITHOUT_XCURSOR)
3691
3692 if (gfXCursorEnabled)
3693 {
3694 XcursorImage *img = XcursorImageCreate(data->width, data->height);
3695 Assert(img);
3696 if (img)
3697 {
3698 img->xhot = data->xHot;
3699 img->yhot = data->yHot;
3700
3701 XcursorPixel *dstShapePtr = img->pixels;
3702
3703 for (uint32_t y = 0; y < data->height; y ++)
3704 {
3705 memcpy(dstShapePtr, srcShapePtr, srcShapePtrScan);
3706
3707 if (!data->alpha)
3708 {
3709 // convert AND mask to the alpha channel
3710 uint8_t byte = 0;
3711 for (uint32_t x = 0; x < data->width; x ++)
3712 {
3713 if (!(x % 8))
3714 byte = *(srcAndMaskPtr ++);
3715 else
3716 byte <<= 1;
3717
3718 if (byte & 0x80)
3719 {
3720 // Linux doesn't support inverted pixels (XOR ops,
3721 // to be exact) in cursor shapes, so we detect such
3722 // pixels and always replace them with black ones to
3723 // make them visible at least over light colors
3724 if (dstShapePtr [x] & 0x00FFFFFF)
3725 dstShapePtr [x] = 0xFF000000;
3726 else
3727 dstShapePtr [x] = 0x00000000;
3728 }
3729 else
3730 dstShapePtr [x] |= 0xFF000000;
3731 }
3732 }
3733
3734 srcShapePtr += srcShapePtrScan;
3735 dstShapePtr += data->width;
3736 }
3737 }
3738 XcursorImageDestroy(img);
3739 }
3740
3741#endif /* VBOXSDL_WITH_X11 && !VBOX_WITHOUT_XCURSOR */
3742
3743 if (!ok)
3744 {
3745 SDL_SetCursor(gpDefaultCursor);
3746 SDL_ShowCursor(SDL_ENABLE);
3747 }
3748 }
3749 else
3750 {
3751 if (data->visible)
3752 SDL_ShowCursor(SDL_ENABLE);
3753 else if (gfAbsoluteMouseGuest)
3754 /* Don't disable the cursor if the guest additions are not active (anymore) */
3755 SDL_ShowCursor(SDL_DISABLE);
3756 }
3757}
3758
3759/**
3760 * Handle changed mouse capabilities
3761 */
3762static void HandleGuestCapsChanged(void)
3763{
3764 if (!gfAbsoluteMouseGuest)
3765 {
3766 // Cursor could be overwritten by the guest tools
3767 SDL_SetCursor(gpDefaultCursor);
3768 SDL_ShowCursor(SDL_ENABLE);
3769 gpOffCursor = NULL;
3770 }
3771 if (gpMouse && UseAbsoluteMouse())
3772 {
3773 // Actually switch to absolute coordinates
3774 if (gfGrabbed)
3775 InputGrabEnd();
3776 gpMouse->PutMouseEventAbsolute(-1, -1, 0, 0, 0);
3777 }
3778}
3779
3780/**
3781 * Handles a host key down event
3782 */
3783static int HandleHostKey(const SDL_KeyboardEvent *pEv)
3784{
3785 /*
3786 * Revalidate the host key modifier
3787 */
3788 if ((SDL_GetModState() & ~(KMOD_MODE | KMOD_NUM | KMOD_RESERVED)) != gHostKeyMod)
3789 return VERR_NOT_SUPPORTED;
3790
3791 /*
3792 * What was pressed?
3793 */
3794 switch (pEv->keysym.sym)
3795 {
3796 /* Control-Alt-Delete */
3797 case SDLK_DELETE:
3798 {
3799 gpKeyboard->PutCAD();
3800 break;
3801 }
3802
3803 /*
3804 * Fullscreen / Windowed toggle.
3805 */
3806 case SDLK_f:
3807 {
3808 if ( strchr(gHostKeyDisabledCombinations, 'f')
3809 || !gfAllowFullscreenToggle)
3810 return VERR_NOT_SUPPORTED;
3811
3812 /*
3813 * We have to pause/resume the machine during this
3814 * process because there might be a short moment
3815 * without a valid framebuffer
3816 */
3817 MachineState_T machineState;
3818 gpMachine->COMGETTER(State)(&machineState);
3819 bool fPauseIt = machineState == MachineState_Running
3820 || machineState == MachineState_Teleporting
3821 || machineState == MachineState_LiveSnapshotting;
3822 if (fPauseIt)
3823 gpConsole->Pause();
3824 SetFullscreen(!gpFramebuffer[0]->getFullscreen());
3825 if (fPauseIt)
3826 gpConsole->Resume();
3827
3828 /*
3829 * We have switched from/to fullscreen, so request a full
3830 * screen repaint, just to be sure.
3831 */
3832 gpDisplay->InvalidateAndUpdate();
3833 break;
3834 }
3835
3836 /*
3837 * Pause / Resume toggle.
3838 */
3839 case SDLK_p:
3840 {
3841 if (strchr(gHostKeyDisabledCombinations, 'p'))
3842 return VERR_NOT_SUPPORTED;
3843
3844 MachineState_T machineState;
3845 gpMachine->COMGETTER(State)(&machineState);
3846 if ( machineState == MachineState_Running
3847 || machineState == MachineState_Teleporting
3848 || machineState == MachineState_LiveSnapshotting
3849 )
3850 {
3851 if (gfGrabbed)
3852 InputGrabEnd();
3853 gpConsole->Pause();
3854 }
3855 else if (machineState == MachineState_Paused)
3856 {
3857 gpConsole->Resume();
3858 }
3859 UpdateTitlebar(TITLEBAR_NORMAL);
3860 break;
3861 }
3862
3863 /*
3864 * Reset the VM
3865 */
3866 case SDLK_r:
3867 {
3868 if (strchr(gHostKeyDisabledCombinations, 'r'))
3869 return VERR_NOT_SUPPORTED;
3870
3871 ResetVM();
3872 break;
3873 }
3874
3875 /*
3876 * Terminate the VM
3877 */
3878 case SDLK_q:
3879 {
3880 if (strchr(gHostKeyDisabledCombinations, 'q'))
3881 return VERR_NOT_SUPPORTED;
3882
3883 return VINF_EM_TERMINATE;
3884 }
3885
3886 /*
3887 * Save the machine's state and exit
3888 */
3889 case SDLK_s:
3890 {
3891 if (strchr(gHostKeyDisabledCombinations, 's'))
3892 return VERR_NOT_SUPPORTED;
3893
3894 SaveState();
3895 return VINF_EM_TERMINATE;
3896 }
3897
3898 case SDLK_h:
3899 {
3900 if (strchr(gHostKeyDisabledCombinations, 'h'))
3901 return VERR_NOT_SUPPORTED;
3902
3903 if (gpConsole)
3904 gpConsole->PowerButton();
3905 break;
3906 }
3907
3908 /*
3909 * Perform an online snapshot. Continue operation.
3910 */
3911 case SDLK_n:
3912 {
3913 if (strchr(gHostKeyDisabledCombinations, 'n'))
3914 return VERR_NOT_SUPPORTED;
3915
3916 RTThreadYield();
3917 ULONG cSnapshots = 0;
3918 gpMachine->COMGETTER(SnapshotCount)(&cSnapshots);
3919 char pszSnapshotName[20];
3920 RTStrPrintf(pszSnapshotName, sizeof(pszSnapshotName), "Snapshot %d", cSnapshots + 1);
3921 gpProgress = NULL;
3922 HRESULT hrc;
3923 Bstr snapId;
3924 CHECK_ERROR(gpMachine, TakeSnapshot(Bstr(pszSnapshotName).raw(),
3925 Bstr("Taken by VBoxSDL").raw(),
3926 TRUE, snapId.asOutParam(),
3927 gpProgress.asOutParam()));
3928 if (FAILED(hrc))
3929 {
3930 RTPrintf("Error taking snapshot! rc=%Rhrc\n", hrc);
3931 /* continue operation */
3932 return VINF_SUCCESS;
3933 }
3934 /*
3935 * Wait for the operation to be completed and work
3936 * the title bar in the mean while.
3937 */
3938 ULONG cPercent = 0;
3939 for (;;)
3940 {
3941 BOOL fCompleted = false;
3942 hrc = gpProgress->COMGETTER(Completed)(&fCompleted);
3943 if (FAILED(hrc) || fCompleted)
3944 break;
3945 ULONG cPercentNow;
3946 hrc = gpProgress->COMGETTER(Percent)(&cPercentNow);
3947 if (FAILED(hrc))
3948 break;
3949 if (cPercentNow != cPercent)
3950 {
3951 UpdateTitlebar(TITLEBAR_SNAPSHOT, cPercent);
3952 cPercent = cPercentNow;
3953 }
3954
3955 /* wait */
3956 hrc = gpProgress->WaitForCompletion(100);
3957 if (FAILED(hrc))
3958 break;
3959 /// @todo process gui events.
3960 }
3961
3962 /* continue operation */
3963 return VINF_SUCCESS;
3964 }
3965
3966 case SDLK_F1: case SDLK_F2: case SDLK_F3:
3967 case SDLK_F4: case SDLK_F5: case SDLK_F6:
3968 case SDLK_F7: case SDLK_F8: case SDLK_F9:
3969 case SDLK_F10: case SDLK_F11: case SDLK_F12:
3970 {
3971 // /* send Ctrl-Alt-Fx to guest */
3972#if 0 // Fix me. I was not working with PutScancodes API. Need to find the corect way to do this with PutUsagecode API
3973 com::SafeArray<LONG> keys(6);
3974
3975 keys[0] = 0x1d; // Ctrl down
3976 keys[1] = 0x38; // Alt down
3977 keys[2] = Keyevent2Keycode(pEv); // Fx down
3978 keys[3] = keys[2] + 0x80; // Fx up
3979 keys[4] = 0xb8; // Alt up
3980 keys[5] = 0x9d; // Ctrl up
3981
3982 gpKeyboard->PutScancodes(ComSafeArrayAsInParam(keys), NULL);
3983#endif
3984 return VINF_SUCCESS;
3985 }
3986
3987 /*
3988 * Not a host key combination.
3989 * Indicate this by returning false.
3990 */
3991 default:
3992 return VERR_NOT_SUPPORTED;
3993 }
3994
3995 return VINF_SUCCESS;
3996}
3997
3998/**
3999 * Timer callback function for startup processing
4000 */
4001static Uint32 StartupTimer(Uint32 interval, void *param) RT_NOTHROW_DEF
4002{
4003 RT_NOREF(param);
4004
4005 /* post message so we can do something in the startup loop */
4006 SDL_Event event = {0};
4007 event.type = SDL_USEREVENT;
4008 event.user.type = SDL_USER_EVENT_TIMER;
4009 SDL_PushEvent(&event);
4010 RTSemEventSignal(g_EventSemSDLEvents);
4011 return interval;
4012}
4013
4014/**
4015 * Timer callback function to check if resizing is finished
4016 */
4017static Uint32 ResizeTimer(Uint32 interval, void *param) RT_NOTHROW_DEF
4018{
4019 RT_NOREF(interval, param);
4020
4021 /* post message so the window is actually resized */
4022 SDL_Event event = {0};
4023 event.type = SDL_USEREVENT;
4024 event.user.type = SDL_USER_EVENT_WINDOW_RESIZE_DONE;
4025 PushSDLEventForSure(&event);
4026 /* one-shot */
4027 return 0;
4028}
4029
4030/**
4031 * Timer callback function to check if an ACPI power button event was handled by the guest.
4032 */
4033static Uint32 QuitTimer(Uint32 interval, void *param) RT_NOTHROW_DEF
4034{
4035 RT_NOREF(interval, param);
4036
4037 BOOL fHandled = FALSE;
4038
4039 gSdlQuitTimer = 0;
4040 if (gpConsole)
4041 {
4042 int rc = gpConsole->GetPowerButtonHandled(&fHandled);
4043 LogRel(("QuitTimer: rc=%d handled=%d\n", rc, fHandled));
4044 if (RT_FAILURE(rc) || !fHandled)
4045 {
4046 /* event was not handled, power down the guest */
4047 gfACPITerm = FALSE;
4048 SDL_Event event = {0};
4049 event.type = SDL_QUIT;
4050 PushSDLEventForSure(&event);
4051 }
4052 }
4053 /* one-shot */
4054 return 0;
4055}
4056
4057/**
4058 * Wait for the next SDL event. Don't use SDL_WaitEvent since this function
4059 * calls SDL_Delay(10) if the event queue is empty.
4060 */
4061static int WaitSDLEvent(SDL_Event *event)
4062{
4063 for (;;)
4064 {
4065 int rc = SDL_PollEvent(event);
4066 if (rc == 1)
4067 {
4068#ifdef USE_XPCOM_QUEUE_THREAD
4069 if (event->type == SDL_USER_EVENT_XPCOM_EVENTQUEUE)
4070 consumedXPCOMUserEvent();
4071#endif
4072 return 1;
4073 }
4074 /* Immediately wake up if new SDL events are available. This does not
4075 * work for internal SDL events. Don't wait more than 10ms. */
4076 RTSemEventWait(g_EventSemSDLEvents, 10);
4077 }
4078}
4079
4080/**
4081 * Ensure that an SDL event is really enqueued. Try multiple times if necessary.
4082 */
4083int PushSDLEventForSure(SDL_Event *event)
4084{
4085 int ntries = 10;
4086 for (; ntries > 0; ntries--)
4087 {
4088 int rc = SDL_PushEvent(event);
4089 RTSemEventSignal(g_EventSemSDLEvents);
4090 if (rc == 1)
4091 return 0;
4092 Log(("PushSDLEventForSure: waiting for 2ms (rc = %d)\n", rc));
4093 RTThreadSleep(2);
4094 }
4095 LogRel(("WARNING: Failed to enqueue SDL event %d.%d!\n",
4096 event->type, event->type == SDL_USEREVENT ? event->user.type : 0));
4097 return -1;
4098}
4099
4100#ifdef VBOXSDL_WITH_X11
4101/**
4102 * Special SDL_PushEvent function for NotifyUpdate events. These events may occur in bursts
4103 * so make sure they don't flood the SDL event queue.
4104 */
4105void PushNotifyUpdateEvent(SDL_Event *event)
4106{
4107 int rc = SDL_PushEvent(event);
4108 bool fSuccess = (rc == 1);
4109
4110 RTSemEventSignal(g_EventSemSDLEvents);
4111 AssertMsg(fSuccess, ("SDL_PushEvent returned SDL error\n"));
4112 /* A global counter is faster than SDL_PeepEvents() */
4113 if (fSuccess)
4114 ASMAtomicIncS32(&g_cNotifyUpdateEventsPending);
4115 /* In order to not flood the SDL event queue, yield the CPU or (if there are already many
4116 * events queued) even sleep */
4117 if (g_cNotifyUpdateEventsPending > 96)
4118 {
4119 /* Too many NotifyUpdate events, sleep for a small amount to give the main thread time
4120 * to handle these events. The SDL queue can hold up to 128 events. */
4121 Log(("PushNotifyUpdateEvent: Sleep 1ms\n"));
4122 RTThreadSleep(1);
4123 }
4124 else
4125 RTThreadYield();
4126}
4127#endif /* VBOXSDL_WITH_X11 */
4128
4129/**
4130 *
4131 */
4132static void SetFullscreen(bool enable)
4133{
4134 if (enable == gpFramebuffer[0]->getFullscreen())
4135 return;
4136
4137 if (!gfFullscreenResize)
4138 {
4139 /*
4140 * The old/default way: SDL will resize the host to fit the guest screen resolution.
4141 */
4142 gpFramebuffer[0]->setFullscreen(enable);
4143 }
4144 else
4145 {
4146 /*
4147 * The alternate way: Switch to fullscreen with the host screen resolution and adapt
4148 * the guest screen resolution to the host window geometry.
4149 */
4150 uint32_t NewWidth = 0, NewHeight = 0;
4151 if (enable)
4152 {
4153 /* switch to fullscreen */
4154 gmGuestNormalXRes = gpFramebuffer[0]->getGuestXRes();
4155 gmGuestNormalYRes = gpFramebuffer[0]->getGuestYRes();
4156 gpFramebuffer[0]->getFullscreenGeometry(&NewWidth, &NewHeight);
4157 }
4158 else
4159 {
4160 /* switch back to saved geometry */
4161 NewWidth = gmGuestNormalXRes;
4162 NewHeight = gmGuestNormalYRes;
4163 }
4164 if (NewWidth != 0 && NewHeight != 0)
4165 {
4166 gpFramebuffer[0]->setFullscreen(enable);
4167 gfIgnoreNextResize = TRUE;
4168 gpDisplay->SetVideoModeHint(0 /*=display*/, true /*=enabled*/,
4169 false /*=changeOrigin*/, 0 /*=originX*/, 0 /*=originY*/,
4170 NewWidth, NewHeight, 0 /*don't change bpp*/, true /*=notify*/);
4171 }
4172 }
4173}
4174
4175static VBoxSDLFB *getFbFromWinId(Uint32 id)
4176{
4177 for (unsigned i = 0; i < gcMonitors; i++)
4178 if (gpFramebuffer[i]->hasWindow(id))
4179 return gpFramebuffer[i];
4180
4181 return NULL;
4182}
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