VirtualBox

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

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

FE/VBoxSDL+VirtualBox,Main/Console+Machine+VirtualBox.xidl: VMs which
crash while restoring from the 'Saved' state shouldn't lose their saved
state file. bugref:1484

A new machine state named 'AbortedSaved' has been added which a VM will
enter if it crashes when restoring from the 'Saved' state before the
'Running' state has been reached. A VM in the 'AbortedSaved' machine
state will have its saved state file preserved so that the VM can still be
restored once the cause of the failure to powerUp() and reach the
'Running' state has been resolved.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 182.8 KB
Line 
1/* $Id: VBoxSDL.cpp 91363 2021-09-24 13:08:32Z vboxsync $ */
2/** @file
3 * VBox frontends: VBoxSDL (simple frontend based on SDL):
4 * Main code
5 */
6
7/*
8 * Copyright (C) 2006-2020 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19
20/*********************************************************************************************************************************
21* Header Files *
22*********************************************************************************************************************************/
23#define LOG_GROUP LOG_GROUP_GUI
24
25#include <VBox/com/com.h>
26#include <VBox/com/string.h>
27#include <VBox/com/Guid.h>
28#include <VBox/com/array.h>
29#include <VBox/com/ErrorInfo.h>
30#include <VBox/com/errorprint.h>
31
32#include <VBox/com/NativeEventQueue.h>
33#include <VBox/com/VirtualBox.h>
34
35using namespace com;
36
37#if defined(VBOXSDL_WITH_X11)
38# include <VBox/VBoxKeyboard.h>
39
40# include <X11/Xlib.h>
41# include <X11/cursorfont.h> /* for XC_left_ptr */
42# if !defined(VBOX_WITHOUT_XCURSOR)
43# include <X11/Xcursor/Xcursor.h>
44# endif
45# include <unistd.h>
46#endif
47
48#include "VBoxSDL.h"
49
50#ifdef _MSC_VER
51# pragma warning(push)
52# pragma warning(disable: 4121) /* warning C4121: 'SDL_SysWMmsg' : alignment of a member was sensitive to packing*/
53#endif
54#ifndef RT_OS_DARWIN
55# include <SDL_syswm.h> /* for SDL_GetWMInfo() */
56#endif
57#ifdef _MSC_VER
58# pragma warning(pop)
59#endif
60
61#include "Framebuffer.h"
62#include "Helper.h"
63
64#include <VBox/types.h>
65#include <VBox/err.h>
66#include <VBox/param.h>
67#include <VBox/log.h>
68#include <VBox/version.h>
69#include <VBoxVideo.h>
70#include <VBox/com/listeners.h>
71
72#include <iprt/alloca.h>
73#include <iprt/asm.h>
74#include <iprt/assert.h>
75#include <iprt/ctype.h>
76#include <iprt/env.h>
77#include <iprt/file.h>
78#include <iprt/ldr.h>
79#include <iprt/initterm.h>
80#include <iprt/message.h>
81#include <iprt/path.h>
82#include <iprt/process.h>
83#include <iprt/semaphore.h>
84#include <iprt/string.h>
85#include <iprt/stream.h>
86#include <iprt/uuid.h>
87
88#include <signal.h>
89
90#include <vector>
91#include <list>
92
93#include "PasswordInput.h"
94
95/* Xlib would re-define our enums */
96#undef True
97#undef False
98
99
100/*********************************************************************************************************************************
101* Defined Constants And Macros *
102*********************************************************************************************************************************/
103#ifdef VBOX_SECURELABEL
104/** extra data key for the secure label */
105#define VBOXSDL_SECURELABEL_EXTRADATA "VBoxSDL/SecureLabel"
106/** label area height in pixels */
107#define SECURE_LABEL_HEIGHT 20
108#endif
109
110/** Enables the rawr[0|3], patm, and casm options. */
111#define VBOXSDL_ADVANCED_OPTIONS
112
113
114/*********************************************************************************************************************************
115* Structures and Typedefs *
116*********************************************************************************************************************************/
117/** Pointer shape change event data structure */
118struct PointerShapeChangeData
119{
120 PointerShapeChangeData(BOOL aVisible, BOOL aAlpha, ULONG aXHot, ULONG aYHot,
121 ULONG aWidth, ULONG aHeight, ComSafeArrayIn(BYTE,pShape))
122 : visible(aVisible), alpha(aAlpha), xHot(aXHot), yHot(aYHot),
123 width(aWidth), height(aHeight)
124 {
125 // make a copy of the shape
126 com::SafeArray<BYTE> aShape(ComSafeArrayInArg(pShape));
127 size_t cbShapeSize = aShape.size();
128 if (cbShapeSize > 0)
129 {
130 shape.resize(cbShapeSize);
131 ::memcpy(shape.raw(), aShape.raw(), cbShapeSize);
132 }
133 }
134
135 ~PointerShapeChangeData()
136 {
137 }
138
139 const BOOL visible;
140 const BOOL alpha;
141 const ULONG xHot;
142 const ULONG yHot;
143 const ULONG width;
144 const ULONG height;
145 com::SafeArray<BYTE> shape;
146};
147
148enum TitlebarMode
149{
150 TITLEBAR_NORMAL = 1,
151 TITLEBAR_STARTUP = 2,
152 TITLEBAR_SAVE = 3,
153 TITLEBAR_SNAPSHOT = 4
154};
155
156
157/*********************************************************************************************************************************
158* Internal Functions *
159*********************************************************************************************************************************/
160static bool UseAbsoluteMouse(void);
161static void ResetKeys(void);
162static void ProcessKey(SDL_KeyboardEvent *ev);
163static void InputGrabStart(void);
164static void InputGrabEnd(void);
165static void SendMouseEvent(VBoxSDLFB *fb, int dz, int button, int down);
166static void UpdateTitlebar(TitlebarMode mode, uint32_t u32User = 0);
167static void SetPointerShape(const PointerShapeChangeData *data);
168static void HandleGuestCapsChanged(void);
169static int HandleHostKey(const SDL_KeyboardEvent *pEv);
170static Uint32 StartupTimer(Uint32 interval, void *param) RT_NOTHROW_PROTO;
171static Uint32 ResizeTimer(Uint32 interval, void *param) RT_NOTHROW_PROTO;
172static Uint32 QuitTimer(Uint32 interval, void *param) RT_NOTHROW_PROTO;
173static int WaitSDLEvent(SDL_Event *event);
174static void SetFullscreen(bool enable);
175
176#ifdef VBOX_WITH_SDL2
177static VBoxSDLFB *getFbFromWinId(Uint32 id);
178#endif
179
180
181/*********************************************************************************************************************************
182* Global Variables *
183*********************************************************************************************************************************/
184static int gHostKeyMod = KMOD_RCTRL;
185static int gHostKeySym1 = SDLK_RCTRL;
186static int gHostKeySym2 = SDLK_UNKNOWN;
187static const char *gHostKeyDisabledCombinations = "";
188static const char *gpszPidFile;
189static BOOL gfGrabbed = FALSE;
190static BOOL gfGrabOnMouseClick = TRUE;
191static BOOL gfFullscreenResize = FALSE;
192static BOOL gfIgnoreNextResize = FALSE;
193static BOOL gfAllowFullscreenToggle = TRUE;
194static BOOL gfAbsoluteMouseHost = FALSE;
195static BOOL gfAbsoluteMouseGuest = FALSE;
196static BOOL gfRelativeMouseGuest = TRUE;
197static BOOL gfGuestNeedsHostCursor = FALSE;
198static BOOL gfOffCursorActive = FALSE;
199static BOOL gfGuestNumLockPressed = FALSE;
200static BOOL gfGuestCapsLockPressed = FALSE;
201static BOOL gfGuestScrollLockPressed = FALSE;
202static BOOL gfACPITerm = FALSE;
203static BOOL gfXCursorEnabled = FALSE;
204static int gcGuestNumLockAdaptions = 2;
205static int gcGuestCapsLockAdaptions = 2;
206static uint32_t gmGuestNormalXRes;
207static uint32_t gmGuestNormalYRes;
208
209/** modifier keypress status (scancode as index) */
210static uint8_t gaModifiersState[256];
211
212static ComPtr<IMachine> gpMachine;
213static ComPtr<IConsole> gpConsole;
214static ComPtr<IMachineDebugger> gpMachineDebugger;
215static ComPtr<IKeyboard> gpKeyboard;
216static ComPtr<IMouse> gpMouse;
217ComPtr<IDisplay> gpDisplay;
218static ComPtr<IVRDEServer> gpVRDEServer;
219static ComPtr<IProgress> gpProgress;
220
221static ULONG gcMonitors = 1;
222static ComObjPtr<VBoxSDLFB> gpFramebuffer[64];
223static Bstr gaFramebufferId[64];
224static SDL_Cursor *gpDefaultCursor = NULL;
225#ifdef VBOXSDL_WITH_X11
226static Cursor gpDefaultOrigX11Cursor;
227#endif
228static SDL_Cursor *gpCustomCursor = NULL;
229#ifndef VBOX_WITH_SDL2
230static WMcursor *gpCustomOrigWMcursor = NULL;
231#endif
232static SDL_Cursor *gpOffCursor = NULL;
233static SDL_TimerID gSdlResizeTimer = NULL;
234static SDL_TimerID gSdlQuitTimer = NULL;
235
236#if defined(VBOXSDL_WITH_X11) && !defined(VBOX_WITH_SDL2)
237static SDL_SysWMinfo gSdlInfo;
238#endif
239
240#ifdef VBOX_SECURELABEL
241#ifdef RT_OS_WINDOWS
242#define LIBSDL_TTF_NAME "SDL_ttf"
243#else
244#define LIBSDL_TTF_NAME "libSDL_ttf-2.0.so.0"
245#endif
246RTLDRMOD gLibrarySDL_ttf = NIL_RTLDRMOD;
247#endif
248
249static RTSEMEVENT g_EventSemSDLEvents;
250static volatile int32_t g_cNotifyUpdateEventsPending;
251
252/**
253 * Event handler for VirtualBoxClient events
254 */
255class VBoxSDLClientEventListener
256{
257public:
258 VBoxSDLClientEventListener()
259 {
260 }
261
262 virtual ~VBoxSDLClientEventListener()
263 {
264 }
265
266 HRESULT init()
267 {
268 return S_OK;
269 }
270
271 void uninit()
272 {
273 }
274
275 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent * aEvent)
276 {
277 switch (aType)
278 {
279 case VBoxEventType_OnVBoxSVCAvailabilityChanged:
280 {
281 ComPtr<IVBoxSVCAvailabilityChangedEvent> pVSACEv = aEvent;
282 Assert(pVSACEv);
283 BOOL fAvailable = FALSE;
284 pVSACEv->COMGETTER(Available)(&fAvailable);
285 if (!fAvailable)
286 {
287 LogRel(("VBoxSDL: VBoxSVC became unavailable, exiting.\n"));
288 RTPrintf("VBoxSVC became unavailable, exiting.\n");
289 /* Send QUIT event to terminate the VM as cleanly as possible
290 * given that VBoxSVC is no longer present. */
291 SDL_Event event = {0};
292 event.type = SDL_QUIT;
293 PushSDLEventForSure(&event);
294 }
295 break;
296 }
297
298 default:
299 AssertFailed();
300 }
301
302 return S_OK;
303 }
304};
305
306/**
307 * Event handler for VirtualBox (server) events
308 */
309class VBoxSDLEventListener
310{
311public:
312 VBoxSDLEventListener()
313 {
314 }
315
316 virtual ~VBoxSDLEventListener()
317 {
318 }
319
320 HRESULT init()
321 {
322 return S_OK;
323 }
324
325 void uninit()
326 {
327 }
328
329 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent * aEvent)
330 {
331 RT_NOREF(aEvent);
332 switch (aType)
333 {
334 case VBoxEventType_OnExtraDataChanged:
335 {
336#ifdef VBOX_SECURELABEL
337 ComPtr<IExtraDataChangedEvent> pEDCEv = aEvent;
338 Assert(pEDCEv);
339 Bstr bstrMachineId;
340 pEDCEv->COMGETTER(MachineId)(bstrMachineId.asOutParam());
341 if (gpMachine)
342 {
343 /*
344 * check if we're interested in the message
345 */
346 Bstr bstrOurId;
347 gpMachine->COMGETTER(Id)(bstrOurId.asOutParam());
348 if (bstrOurId == bstrMachineId)
349 {
350 Bstr bstrKey;
351 pEDCEv->COMGETTER(Key)(bstrKey.asOutParam());
352 if (bstrKey == VBOXSDL_SECURELABEL_EXTRADATA)
353 {
354 /*
355 * Notify SDL thread of the string update
356 */
357 SDL_Event event = {0};
358 event.type = SDL_USEREVENT;
359 event.user.type = SDL_USER_EVENT_SECURELABEL_UPDATE;
360 PushSDLEventForSure(&event);
361 }
362 }
363 }
364#endif
365 break;
366 }
367
368 default:
369 AssertFailed();
370 }
371
372 return S_OK;
373 }
374};
375
376/**
377 * Event handler for Console events
378 */
379class VBoxSDLConsoleEventListener
380{
381public:
382 VBoxSDLConsoleEventListener() : m_fIgnorePowerOffEvents(false)
383 {
384 }
385
386 virtual ~VBoxSDLConsoleEventListener()
387 {
388 }
389
390 HRESULT init()
391 {
392 return S_OK;
393 }
394
395 void uninit()
396 {
397 }
398
399 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent * aEvent)
400 {
401 // likely all this double copy is now excessive, and we can just use existing event object
402 /// @todo eliminate it
403 switch (aType)
404 {
405 case VBoxEventType_OnMousePointerShapeChanged:
406 {
407 ComPtr<IMousePointerShapeChangedEvent> pMPSCEv = aEvent;
408 Assert(pMPSCEv);
409 PointerShapeChangeData *data;
410 BOOL visible, alpha;
411 ULONG xHot, yHot, width, height;
412 com::SafeArray<BYTE> shape;
413
414 pMPSCEv->COMGETTER(Visible)(&visible);
415 pMPSCEv->COMGETTER(Alpha)(&alpha);
416 pMPSCEv->COMGETTER(Xhot)(&xHot);
417 pMPSCEv->COMGETTER(Yhot)(&yHot);
418 pMPSCEv->COMGETTER(Width)(&width);
419 pMPSCEv->COMGETTER(Height)(&height);
420 pMPSCEv->COMGETTER(Shape)(ComSafeArrayAsOutParam(shape));
421 data = new PointerShapeChangeData(visible, alpha, xHot, yHot, width, height,
422 ComSafeArrayAsInParam(shape));
423 Assert(data);
424 if (!data)
425 break;
426
427 SDL_Event event = {0};
428 event.type = SDL_USEREVENT;
429 event.user.type = SDL_USER_EVENT_POINTER_CHANGE;
430 event.user.data1 = data;
431
432 int rc = PushSDLEventForSure(&event);
433 if (rc)
434 delete data;
435
436 break;
437 }
438 case VBoxEventType_OnMouseCapabilityChanged:
439 {
440 ComPtr<IMouseCapabilityChangedEvent> pMCCEv = aEvent;
441 Assert(pMCCEv);
442 pMCCEv->COMGETTER(SupportsAbsolute)(&gfAbsoluteMouseGuest);
443 pMCCEv->COMGETTER(SupportsRelative)(&gfRelativeMouseGuest);
444 pMCCEv->COMGETTER(NeedsHostCursor)(&gfGuestNeedsHostCursor);
445 SDL_Event event = {0};
446 event.type = SDL_USEREVENT;
447 event.user.type = SDL_USER_EVENT_GUEST_CAP_CHANGED;
448
449 PushSDLEventForSure(&event);
450 break;
451 }
452 case VBoxEventType_OnKeyboardLedsChanged:
453 {
454 ComPtr<IKeyboardLedsChangedEvent> pCLCEv = aEvent;
455 Assert(pCLCEv);
456 BOOL fNumLock, fCapsLock, fScrollLock;
457 pCLCEv->COMGETTER(NumLock)(&fNumLock);
458 pCLCEv->COMGETTER(CapsLock)(&fCapsLock);
459 pCLCEv->COMGETTER(ScrollLock)(&fScrollLock);
460 /* Don't bother the guest with NumLock scancodes if he doesn't set the NumLock LED */
461 if (gfGuestNumLockPressed != fNumLock)
462 gcGuestNumLockAdaptions = 2;
463 if (gfGuestCapsLockPressed != fCapsLock)
464 gcGuestCapsLockAdaptions = 2;
465 gfGuestNumLockPressed = fNumLock;
466 gfGuestCapsLockPressed = fCapsLock;
467 gfGuestScrollLockPressed = fScrollLock;
468 break;
469 }
470
471 case VBoxEventType_OnStateChanged:
472 {
473 ComPtr<IStateChangedEvent> pSCEv = aEvent;
474 Assert(pSCEv);
475 MachineState_T machineState;
476 pSCEv->COMGETTER(State)(&machineState);
477 LogFlow(("OnStateChange: machineState = %d (%s)\n", machineState, GetStateName(machineState)));
478 SDL_Event event = {0};
479
480 if ( machineState == MachineState_Aborted
481 || machineState == MachineState_Teleported
482 || (machineState == MachineState_Saved && !m_fIgnorePowerOffEvents)
483 || (machineState == MachineState_AbortedSaved && !m_fIgnorePowerOffEvents)
484 || (machineState == MachineState_PoweredOff && !m_fIgnorePowerOffEvents)
485 )
486 {
487 /*
488 * We have to inform the SDL thread that the application has be terminated
489 */
490 event.type = SDL_USEREVENT;
491 event.user.type = SDL_USER_EVENT_TERMINATE;
492 event.user.code = machineState == MachineState_Aborted
493 ? VBOXSDL_TERM_ABEND
494 : VBOXSDL_TERM_NORMAL;
495 }
496 else
497 {
498 /*
499 * Inform the SDL thread to refresh the titlebar
500 */
501 event.type = SDL_USEREVENT;
502 event.user.type = SDL_USER_EVENT_UPDATE_TITLEBAR;
503 }
504
505 PushSDLEventForSure(&event);
506 break;
507 }
508
509 case VBoxEventType_OnRuntimeError:
510 {
511 ComPtr<IRuntimeErrorEvent> pRTEEv = aEvent;
512 Assert(pRTEEv);
513 BOOL fFatal;
514
515 pRTEEv->COMGETTER(Fatal)(&fFatal);
516 MachineState_T machineState;
517 gpMachine->COMGETTER(State)(&machineState);
518 const char *pszType;
519 bool fPaused = machineState == MachineState_Paused;
520 if (fFatal)
521 pszType = "FATAL ERROR";
522 else if (machineState == MachineState_Paused)
523 pszType = "Non-fatal ERROR";
524 else
525 pszType = "WARNING";
526 Bstr bstrId, bstrMessage;
527 pRTEEv->COMGETTER(Id)(bstrId.asOutParam());
528 pRTEEv->COMGETTER(Message)(bstrMessage.asOutParam());
529 RTPrintf("\n%s: ** %ls **\n%ls\n%s\n", pszType, bstrId.raw(), bstrMessage.raw(),
530 fPaused ? "The VM was paused. Continue with HostKey + P after you solved the problem.\n" : "");
531 break;
532 }
533
534 case VBoxEventType_OnCanShowWindow:
535 {
536 ComPtr<ICanShowWindowEvent> pCSWEv = aEvent;
537 Assert(pCSWEv);
538#ifdef RT_OS_DARWIN
539 /* SDL feature not available on Quartz */
540#else
541 bool fCanShow = false;
542
543# ifdef VBOX_WITH_SDL2
544 Uint32 winId = 0;
545
546 VBoxSDLFB *fb = getFbFromWinId(winId);
547
548 SDL_SysWMinfo info;
549 SDL_VERSION(&info.version);
550 if (SDL_GetWindowWMInfo(fb->getWindow(), &info))
551 fCanShow = true;
552# else
553 SDL_SysWMinfo info;
554 SDL_VERSION(&info.version);
555 if (!SDL_GetWMInfo(&info))
556 fCanShow = false;
557 else
558 fCanShow = true;
559# endif /* VBOX_WITH_SDL2 */
560
561 if (fCanShow)
562 pCSWEv->AddApproval(NULL);
563 else
564 pCSWEv->AddVeto(NULL);
565#endif
566 break;
567 }
568
569 case VBoxEventType_OnShowWindow:
570 {
571 ComPtr<IShowWindowEvent> pSWEv = aEvent;
572 Assert(pSWEv);
573 LONG64 winId = 0;
574 pSWEv->COMGETTER(WinId)(&winId);
575 if (winId != 0)
576 break; /* WinId already set by some other listener. */
577#ifndef RT_OS_DARWIN
578 SDL_SysWMinfo info;
579 SDL_VERSION(&info.version);
580# ifdef VBOX_WITH_SDL2
581 VBoxSDLFB *fb = getFbFromWinId(winId);
582 if (SDL_GetWindowWMInfo(fb->getWindow(), &info))
583# else
584 if (SDL_GetWMInfo(&info))
585# endif /* VBOX_WITH_SDL2 */
586 {
587# if defined(VBOXSDL_WITH_X11)
588 pSWEv->COMSETTER(WinId)((LONG64)info.info.x11.wmwindow);
589# elif defined(RT_OS_WINDOWS)
590# ifdef VBOX_WITH_SDL2
591 pSWEv->COMSETTER(WinId)((intptr_t)info.info.win.window);
592# else
593 pSWEv->COMSETTER(WinId)((intptr_t)info.window);
594# endif /* VBOX_WITH_SDL2 */
595# else /* !RT_OS_WINDOWS */
596 AssertFailed();
597# endif
598 }
599#endif /* !RT_OS_DARWIN */
600 break;
601 }
602
603 default:
604 AssertFailed();
605 }
606 return S_OK;
607 }
608
609 static const char *GetStateName(MachineState_T machineState)
610 {
611 switch (machineState)
612 {
613 case MachineState_Null: return "<null>";
614 case MachineState_PoweredOff: return "PoweredOff";
615 case MachineState_Saved: return "Saved";
616 case MachineState_Teleported: return "Teleported";
617 case MachineState_Aborted: return "Aborted";
618 case MachineState_AbortedSaved: return "Aborted-Saved";
619 case MachineState_Running: return "Running";
620 case MachineState_Teleporting: return "Teleporting";
621 case MachineState_LiveSnapshotting: return "LiveSnapshotting";
622 case MachineState_Paused: return "Paused";
623 case MachineState_Stuck: return "GuruMeditation";
624 case MachineState_Starting: return "Starting";
625 case MachineState_Stopping: return "Stopping";
626 case MachineState_Saving: return "Saving";
627 case MachineState_Restoring: return "Restoring";
628 case MachineState_TeleportingPausedVM: return "TeleportingPausedVM";
629 case MachineState_TeleportingIn: return "TeleportingIn";
630 case MachineState_RestoringSnapshot: return "RestoringSnapshot";
631 case MachineState_DeletingSnapshot: return "DeletingSnapshot";
632 case MachineState_SettingUp: return "SettingUp";
633 default: return "no idea";
634 }
635 }
636
637 void ignorePowerOffEvents(bool fIgnore)
638 {
639 m_fIgnorePowerOffEvents = fIgnore;
640 }
641
642private:
643 bool m_fIgnorePowerOffEvents;
644};
645
646typedef ListenerImpl<VBoxSDLClientEventListener> VBoxSDLClientEventListenerImpl;
647typedef ListenerImpl<VBoxSDLEventListener> VBoxSDLEventListenerImpl;
648typedef ListenerImpl<VBoxSDLConsoleEventListener> VBoxSDLConsoleEventListenerImpl;
649
650static void show_usage()
651{
652 RTPrintf("Usage:\n"
653 " --startvm <uuid|name> Virtual machine to start, either UUID or name\n"
654 " --separate Run a separate VM process or attach to a running VM\n"
655 " --hda <file> Set temporary first hard disk to file\n"
656 " --fda <file> Set temporary first floppy disk to file\n"
657 " --cdrom <file> Set temporary CDROM/DVD to file/device ('none' to unmount)\n"
658 " --boot <a|c|d|n> Set temporary boot device (a = floppy, c = 1st HD, d = DVD, n = network)\n"
659 " --memory <size> Set temporary memory size in megabytes\n"
660 " --vram <size> Set temporary size of video memory in megabytes\n"
661 " --fullscreen Start VM in fullscreen mode\n"
662 " --fullscreenresize Resize the guest on fullscreen\n"
663 " --fixedmode <w> <h> <bpp> Use a fixed SDL video mode with given width, height and bits per pixel\n"
664 " --nofstoggle Forbid switching to/from fullscreen mode\n"
665 " --noresize Make the SDL frame non resizable\n"
666 " --nohostkey Disable all hostkey combinations\n"
667 " --nohostkeys ... Disable specific hostkey combinations, see below for valid keys\n"
668 " --nograbonclick Disable mouse/keyboard grabbing on mouse click w/o additions\n"
669 " --detecthostkey Get the hostkey identifier and modifier state\n"
670 " --hostkey <key> {<key2>} <mod> Set the host key to the values obtained using --detecthostkey\n"
671 " --termacpi Send an ACPI power button event when closing the window\n"
672 " --vrdp <ports> Listen for VRDP connections on one of specified ports (default if not specified)\n"
673 " --discardstate Discard saved state (if present) and revert to last snapshot (if present)\n"
674 " --settingspw <pw> Specify the settings password\n"
675 " --settingspwfile <file> Specify a file containing the settings password\n"
676#ifdef VBOX_SECURELABEL
677 " --securelabel Display a secure VM label at the top of the screen\n"
678 " --seclabelfnt TrueType (.ttf) font file for secure session label\n"
679 " --seclabelsiz Font point size for secure session label (default 12)\n"
680 " --seclabelofs Font offset within the secure label (default 0)\n"
681 " --seclabelfgcol <rgb> Secure label text color RGB value in 6 digit hexadecimal (eg: FFFF00)\n"
682 " --seclabelbgcol <rgb> Secure label background color RGB value in 6 digit hexadecimal (eg: FF0000)\n"
683#endif
684#ifdef VBOXSDL_ADVANCED_OPTIONS
685 " --[no]rawr0 Enable or disable raw ring 3\n"
686 " --[no]rawr3 Enable or disable raw ring 0\n"
687 " --[no]patm Enable or disable PATM\n"
688 " --[no]csam Enable or disable CSAM\n"
689 " --[no]hwvirtex Permit or deny the usage of VT-x/AMD-V\n"
690#endif
691 "\n"
692 "Key bindings:\n"
693 " <hostkey> + f Switch to full screen / restore to previous view\n"
694 " h Press ACPI power button\n"
695 " n Take a snapshot and continue execution\n"
696 " p Pause / resume execution\n"
697 " q Power off\n"
698 " r VM reset\n"
699 " s Save state and power off\n"
700 " <del> Send <ctrl><alt><del>\n"
701 " <F1>...<F12> Send <ctrl><alt><Fx>\n"
702#if defined(DEBUG) || defined(VBOX_WITH_STATISTICS)
703 "\n"
704 "Further key bindings useful for debugging:\n"
705 " LCtrl + Alt + F12 Reset statistics counter\n"
706 " LCtrl + Alt + F11 Dump statistics to logfile\n"
707 " Alt + F12 Toggle R0 recompiler\n"
708 " Alt + F11 Toggle R3 recompiler\n"
709 " Alt + F10 Toggle PATM\n"
710 " Alt + F9 Toggle CSAM\n"
711 " Alt + F8 Toggle single step mode\n"
712 " LCtrl/RCtrl + F12 Toggle logger\n"
713 " F12 Write log marker to logfile\n"
714#endif
715 "\n");
716}
717
718static void PrintError(const char *pszName, CBSTR pwszDescr, CBSTR pwszComponent=NULL)
719{
720 const char *pszFile, *pszFunc, *pszStat;
721 char pszBuffer[1024];
722 com::ErrorInfo info;
723
724 RTStrPrintf(pszBuffer, sizeof(pszBuffer), "%ls", pwszDescr);
725
726 RTPrintf("\n%s! Error info:\n", pszName);
727 if ( (pszFile = strstr(pszBuffer, "At '"))
728 && (pszFunc = strstr(pszBuffer, ") in "))
729 && (pszStat = strstr(pszBuffer, "VBox status code: ")))
730 RTPrintf(" %.*s %.*s\n In%.*s %s",
731 pszFile-pszBuffer, pszBuffer,
732 pszFunc-pszFile+1, pszFile,
733 pszStat-pszFunc-4, pszFunc+4,
734 pszStat);
735 else
736 RTPrintf("%s\n", pszBuffer);
737
738 if (pwszComponent)
739 RTPrintf("(component %ls).\n", pwszComponent);
740
741 RTPrintf("\n");
742}
743
744#ifdef VBOXSDL_WITH_X11
745/**
746 * Custom signal handler. Currently it is only used to release modifier
747 * keys when receiving the USR1 signal. When switching VTs, we might not
748 * get release events for Ctrl-Alt and in case a savestate is performed
749 * on the new VT, the VM will be saved with modifier keys stuck. This is
750 * annoying enough for introducing this hack.
751 */
752void signal_handler_SIGUSR1(int sig, siginfo_t *info, void *secret)
753{
754 RT_NOREF(info, secret);
755
756 /* only SIGUSR1 is interesting */
757 if (sig == SIGUSR1)
758 {
759 /* just release the modifiers */
760 ResetKeys();
761 }
762}
763
764/**
765 * Custom signal handler for catching exit events.
766 */
767void signal_handler_SIGINT(int sig)
768{
769 if (gpszPidFile)
770 RTFileDelete(gpszPidFile);
771 signal(SIGINT, SIG_DFL);
772 signal(SIGQUIT, SIG_DFL);
773 signal(SIGSEGV, SIG_DFL);
774 kill(getpid(), sig);
775}
776#endif /* VBOXSDL_WITH_X11 */
777
778
779/** entry point */
780extern "C"
781DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
782{
783 RT_NOREF(envp);
784#ifdef RT_OS_WINDOWS
785 ATL::CComModule _Module; /* Required internally by ATL (constructor records instance in global variable). */
786#endif
787
788#ifdef Q_WS_X11
789 if (!XInitThreads())
790 return 1;
791#endif
792#ifdef VBOXSDL_WITH_X11
793 /*
794 * Lock keys on SDL behave different from normal keys: A KeyPress event is generated
795 * if the lock mode gets active and a keyRelease event is generated if the lock mode
796 * gets inactive, that is KeyPress and KeyRelease are sent when pressing the lock key
797 * to change the mode. The current lock mode is reflected in SDL_GetModState().
798 *
799 * Debian patched libSDL to make the lock keys behave like normal keys
800 * generating a KeyPress/KeyRelease event if the lock key was
801 * pressed/released. With the new behaviour, the lock status is not
802 * reflected in the mod status anymore, but the user can request the old
803 * behaviour by setting an environment variable. To confuse matters further
804 * version 1.2.14 (fortunately including the Debian packaged versions)
805 * adopted the Debian behaviour officially, but inverted the meaning of the
806 * environment variable to select the new behaviour, keeping the old as the
807 * default. We disable the new behaviour to ensure a defined environment
808 * and work around the missing KeyPress/KeyRelease events in ProcessKeys().
809 */
810 {
811 const SDL_version *pVersion = SDL_Linked_Version();
812 if ( SDL_VERSIONNUM(pVersion->major, pVersion->minor, pVersion->patch)
813 < SDL_VERSIONNUM(1, 2, 14))
814 RTEnvSet("SDL_DISABLE_LOCK_KEYS", "1");
815 }
816#endif
817
818 /*
819 * the hostkey detection mode is unrelated to VM processing, so handle it before
820 * we initialize anything COM related
821 */
822 if (argc == 2 && ( !strcmp(argv[1], "-detecthostkey")
823 || !strcmp(argv[1], "--detecthostkey")))
824 {
825 Uint32 fInitSubSystem = SDL_INIT_VIDEO | SDL_INIT_TIMER;
826#ifndef VBOX_WITH_SDL2
827 fInitSubSystem |= SDL_INIT_NOPARACHUTE;
828#endif
829 int rc = SDL_InitSubSystem(fInitSubSystem);
830 if (rc != 0)
831 {
832 RTPrintf("Error: SDL_InitSubSystem failed with message '%s'\n", SDL_GetError());
833 return 1;
834 }
835 /* we need a video window for the keyboard stuff to work */
836#ifndef VBOX_WITH_SDL2 /** @todo Is this correct? */
837 if (!SDL_SetVideoMode(640, 480, 16, SDL_SWSURFACE))
838 {
839 RTPrintf("Error: could not set SDL video mode\n");
840 return 1;
841 }
842#endif
843 RTPrintf("Please hit one or two function key(s) to get the --hostkey value...\n");
844
845 SDL_Event event1;
846 while (SDL_WaitEvent(&event1))
847 {
848 if (event1.type == SDL_KEYDOWN)
849 {
850 SDL_Event event2;
851 unsigned mod = SDL_GetModState() & ~(KMOD_MODE | KMOD_NUM | KMOD_RESERVED);
852 while (SDL_WaitEvent(&event2))
853 {
854 if (event2.type == SDL_KEYDOWN || event2.type == SDL_KEYUP)
855 {
856 /* pressed additional host key */
857 RTPrintf("--hostkey %d", event1.key.keysym.sym);
858 if (event2.type == SDL_KEYDOWN)
859 {
860 RTPrintf(" %d", event2.key.keysym.sym);
861 RTPrintf(" %d\n", SDL_GetModState() & ~(KMOD_MODE | KMOD_NUM | KMOD_RESERVED));
862 }
863 else
864 {
865 RTPrintf(" %d\n", mod);
866 }
867 /* we're done */
868 break;
869 }
870 }
871 /* we're down */
872 break;
873 }
874 }
875 SDL_Quit();
876 return 1;
877 }
878
879 HRESULT rc;
880 int vrc;
881 Guid uuidVM;
882 char *vmName = NULL;
883 bool fSeparate = false;
884 DeviceType_T bootDevice = DeviceType_Null;
885 uint32_t memorySize = 0;
886 uint32_t vramSize = 0;
887 ComPtr<IEventListener> pVBoxClientListener;
888 ComPtr<IEventListener> pVBoxListener;
889 ComObjPtr<VBoxSDLConsoleEventListenerImpl> pConsoleListener;
890
891 bool fFullscreen = false;
892 bool fResizable = true;
893#ifdef USE_XPCOM_QUEUE_THREAD
894 bool fXPCOMEventThreadSignaled = false;
895#endif
896 const char *pcszHdaFile = NULL;
897 const char *pcszCdromFile = NULL;
898 const char *pcszFdaFile = NULL;
899 const char *pszPortVRDP = NULL;
900 bool fDiscardState = false;
901 const char *pcszSettingsPw = NULL;
902 const char *pcszSettingsPwFile = NULL;
903#ifdef VBOX_SECURELABEL
904 BOOL fSecureLabel = false;
905 uint32_t secureLabelPointSize = 12;
906 uint32_t secureLabelFontOffs = 0;
907 char *secureLabelFontFile = NULL;
908 uint32_t secureLabelColorFG = 0x0000FF00;
909 uint32_t secureLabelColorBG = 0x00FFFF00;
910#endif
911#ifdef VBOXSDL_ADVANCED_OPTIONS
912 unsigned fRawR0 = ~0U;
913 unsigned fRawR3 = ~0U;
914 unsigned fPATM = ~0U;
915 unsigned fCSAM = ~0U;
916 unsigned fHWVirt = ~0U;
917 uint32_t u32WarpDrive = 0;
918#endif
919#ifdef VBOX_WIN32_UI
920 bool fWin32UI = true;
921 int64_t winId = 0;
922#endif
923 bool fShowSDLConfig = false;
924 uint32_t fixedWidth = ~(uint32_t)0;
925 uint32_t fixedHeight = ~(uint32_t)0;
926 uint32_t fixedBPP = ~(uint32_t)0;
927 uint32_t uResizeWidth = ~(uint32_t)0;
928 uint32_t uResizeHeight = ~(uint32_t)0;
929
930 /* The damned GOTOs forces this to be up here - totally out of place. */
931 /*
932 * Host key handling.
933 *
934 * The golden rule is that host-key combinations should not be seen
935 * by the guest. For instance a CAD should not have any extra RCtrl down
936 * and RCtrl up around itself. Nor should a resume be followed by a Ctrl-P
937 * that could encourage applications to start printing.
938 *
939 * We must not confuse the hostkey processing into any release sequences
940 * either, the host key is supposed to be explicitly pressing one key.
941 *
942 * Quick state diagram:
943 *
944 * host key down alone
945 * (Normal) ---------------
946 * ^ ^ |
947 * | | v host combination key down
948 * | | (Host key down) ----------------
949 * | | host key up v | |
950 * | |-------------- | other key down v host combination key down
951 * | | (host key used) -------------
952 * | | | ^ |
953 * | (not host key)-- | |---------------
954 * | | | | |
955 * | | ---- other |
956 * | modifiers = 0 v v
957 * -----------------------------------------------
958 */
959 enum HKEYSTATE
960 {
961 /** The initial and most common state, pass keystrokes to the guest.
962 * Next state: HKEYSTATE_DOWN
963 * Prev state: Any */
964 HKEYSTATE_NORMAL = 1,
965 /** The first host key was pressed down
966 */
967 HKEYSTATE_DOWN_1ST,
968 /** The second host key was pressed down (if gHostKeySym2 != SDLK_UNKNOWN)
969 */
970 HKEYSTATE_DOWN_2ND,
971 /** The host key has been pressed down.
972 * Prev state: HKEYSTATE_NORMAL
973 * Next state: HKEYSTATE_NORMAL - host key up, capture toggle.
974 * Next state: HKEYSTATE_USED - host key combination down.
975 * Next state: HKEYSTATE_NOT_IT - non-host key combination down.
976 */
977 HKEYSTATE_DOWN,
978 /** A host key combination was pressed.
979 * Prev state: HKEYSTATE_DOWN
980 * Next state: HKEYSTATE_NORMAL - when modifiers are all 0
981 */
982 HKEYSTATE_USED,
983 /** A non-host key combination was attempted. Send hostkey down to the
984 * guest and continue until all modifiers have been released.
985 * Prev state: HKEYSTATE_DOWN
986 * Next state: HKEYSTATE_NORMAL - when modifiers are all 0
987 */
988 HKEYSTATE_NOT_IT
989 } enmHKeyState = HKEYSTATE_NORMAL;
990 /** The host key down event which we have been hiding from the guest.
991 * Used when going from HKEYSTATE_DOWN to HKEYSTATE_NOT_IT. */
992 SDL_Event EvHKeyDown1;
993 SDL_Event EvHKeyDown2;
994
995 LogFlow(("SDL GUI started\n"));
996 RTPrintf(VBOX_PRODUCT " SDL GUI version %s\n"
997 "(C) 2005-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
998 "All rights reserved.\n\n",
999 VBOX_VERSION_STRING);
1000
1001 // less than one parameter is not possible
1002 if (argc < 2)
1003 {
1004 show_usage();
1005 return 1;
1006 }
1007
1008 // command line argument parsing stuff
1009 for (int curArg = 1; curArg < argc; curArg++)
1010 {
1011 if ( !strcmp(argv[curArg], "--vm")
1012 || !strcmp(argv[curArg], "-vm")
1013 || !strcmp(argv[curArg], "--startvm")
1014 || !strcmp(argv[curArg], "-startvm")
1015 || !strcmp(argv[curArg], "-s")
1016 )
1017 {
1018 if (++curArg >= argc)
1019 {
1020 RTPrintf("Error: VM not specified (UUID or name)!\n");
1021 return 1;
1022 }
1023 // first check if a UUID was supplied
1024 uuidVM = argv[curArg];
1025
1026 if (!uuidVM.isValid())
1027 {
1028 LogFlow(("invalid UUID format, assuming it's a VM name\n"));
1029 vmName = argv[curArg];
1030 }
1031 else if (uuidVM.isZero())
1032 {
1033 RTPrintf("Error: UUID argument is zero!\n");
1034 return 1;
1035 }
1036 }
1037 else if ( !strcmp(argv[curArg], "--separate")
1038 || !strcmp(argv[curArg], "-separate"))
1039 {
1040 fSeparate = true;
1041 }
1042 else if ( !strcmp(argv[curArg], "--comment")
1043 || !strcmp(argv[curArg], "-comment"))
1044 {
1045 if (++curArg >= argc)
1046 {
1047 RTPrintf("Error: missing argument for comment!\n");
1048 return 1;
1049 }
1050 }
1051 else if ( !strcmp(argv[curArg], "--boot")
1052 || !strcmp(argv[curArg], "-boot"))
1053 {
1054 if (++curArg >= argc)
1055 {
1056 RTPrintf("Error: missing argument for boot drive!\n");
1057 return 1;
1058 }
1059 switch (argv[curArg][0])
1060 {
1061 case 'a':
1062 {
1063 bootDevice = DeviceType_Floppy;
1064 break;
1065 }
1066
1067 case 'c':
1068 {
1069 bootDevice = DeviceType_HardDisk;
1070 break;
1071 }
1072
1073 case 'd':
1074 {
1075 bootDevice = DeviceType_DVD;
1076 break;
1077 }
1078
1079 case 'n':
1080 {
1081 bootDevice = DeviceType_Network;
1082 break;
1083 }
1084
1085 default:
1086 {
1087 RTPrintf("Error: wrong argument for boot drive!\n");
1088 return 1;
1089 }
1090 }
1091 }
1092 else if ( !strcmp(argv[curArg], "--detecthostkey")
1093 || !strcmp(argv[curArg], "-detecthostkey"))
1094 {
1095 RTPrintf("Error: please specify \"%s\" without any additional parameters!\n",
1096 argv[curArg]);
1097 return 1;
1098 }
1099 else if ( !strcmp(argv[curArg], "--memory")
1100 || !strcmp(argv[curArg], "-memory")
1101 || !strcmp(argv[curArg], "-m"))
1102 {
1103 if (++curArg >= argc)
1104 {
1105 RTPrintf("Error: missing argument for memory size!\n");
1106 return 1;
1107 }
1108 memorySize = atoi(argv[curArg]);
1109 }
1110 else if ( !strcmp(argv[curArg], "--vram")
1111 || !strcmp(argv[curArg], "-vram"))
1112 {
1113 if (++curArg >= argc)
1114 {
1115 RTPrintf("Error: missing argument for vram size!\n");
1116 return 1;
1117 }
1118 vramSize = atoi(argv[curArg]);
1119 }
1120 else if ( !strcmp(argv[curArg], "--fullscreen")
1121 || !strcmp(argv[curArg], "-fullscreen"))
1122 {
1123 fFullscreen = true;
1124 }
1125 else if ( !strcmp(argv[curArg], "--fullscreenresize")
1126 || !strcmp(argv[curArg], "-fullscreenresize"))
1127 {
1128 gfFullscreenResize = true;
1129#ifdef VBOXSDL_WITH_X11
1130 RTEnvSet("SDL_VIDEO_X11_VIDMODE", "0");
1131#endif
1132 }
1133 else if ( !strcmp(argv[curArg], "--fixedmode")
1134 || !strcmp(argv[curArg], "-fixedmode"))
1135 {
1136 /* three parameters follow */
1137 if (curArg + 3 >= argc)
1138 {
1139 RTPrintf("Error: missing arguments for fixed video mode!\n");
1140 return 1;
1141 }
1142 fixedWidth = atoi(argv[++curArg]);
1143 fixedHeight = atoi(argv[++curArg]);
1144 fixedBPP = atoi(argv[++curArg]);
1145 }
1146 else if ( !strcmp(argv[curArg], "--nofstoggle")
1147 || !strcmp(argv[curArg], "-nofstoggle"))
1148 {
1149 gfAllowFullscreenToggle = FALSE;
1150 }
1151 else if ( !strcmp(argv[curArg], "--noresize")
1152 || !strcmp(argv[curArg], "-noresize"))
1153 {
1154 fResizable = false;
1155 }
1156 else if ( !strcmp(argv[curArg], "--nohostkey")
1157 || !strcmp(argv[curArg], "-nohostkey"))
1158 {
1159 gHostKeyMod = 0;
1160 gHostKeySym1 = 0;
1161 }
1162 else if ( !strcmp(argv[curArg], "--nohostkeys")
1163 || !strcmp(argv[curArg], "-nohostkeys"))
1164 {
1165 if (++curArg >= argc)
1166 {
1167 RTPrintf("Error: missing a string of disabled hostkey combinations\n");
1168 return 1;
1169 }
1170 gHostKeyDisabledCombinations = argv[curArg];
1171 size_t cch = strlen(gHostKeyDisabledCombinations);
1172 for (size_t i = 0; i < cch; i++)
1173 {
1174 if (!strchr("fhnpqrs", gHostKeyDisabledCombinations[i]))
1175 {
1176 RTPrintf("Error: <hostkey> + '%c' is not a valid combination\n",
1177 gHostKeyDisabledCombinations[i]);
1178 return 1;
1179 }
1180 }
1181 }
1182 else if ( !strcmp(argv[curArg], "--nograbonclick")
1183 || !strcmp(argv[curArg], "-nograbonclick"))
1184 {
1185 gfGrabOnMouseClick = FALSE;
1186 }
1187 else if ( !strcmp(argv[curArg], "--termacpi")
1188 || !strcmp(argv[curArg], "-termacpi"))
1189 {
1190 gfACPITerm = TRUE;
1191 }
1192 else if ( !strcmp(argv[curArg], "--pidfile")
1193 || !strcmp(argv[curArg], "-pidfile"))
1194 {
1195 if (++curArg >= argc)
1196 {
1197 RTPrintf("Error: missing file name for --pidfile!\n");
1198 return 1;
1199 }
1200 gpszPidFile = argv[curArg];
1201 }
1202 else if ( !strcmp(argv[curArg], "--hda")
1203 || !strcmp(argv[curArg], "-hda"))
1204 {
1205 if (++curArg >= argc)
1206 {
1207 RTPrintf("Error: missing file name for first hard disk!\n");
1208 return 1;
1209 }
1210 /* resolve it. */
1211 if (RTPathExists(argv[curArg]))
1212 pcszHdaFile = RTPathRealDup(argv[curArg]);
1213 if (!pcszHdaFile)
1214 {
1215 RTPrintf("Error: The path to the specified harddisk, '%s', could not be resolved.\n", argv[curArg]);
1216 return 1;
1217 }
1218 }
1219 else if ( !strcmp(argv[curArg], "--fda")
1220 || !strcmp(argv[curArg], "-fda"))
1221 {
1222 if (++curArg >= argc)
1223 {
1224 RTPrintf("Error: missing file/device name for first floppy disk!\n");
1225 return 1;
1226 }
1227 /* resolve it. */
1228 if (RTPathExists(argv[curArg]))
1229 pcszFdaFile = RTPathRealDup(argv[curArg]);
1230 if (!pcszFdaFile)
1231 {
1232 RTPrintf("Error: The path to the specified floppy disk, '%s', could not be resolved.\n", argv[curArg]);
1233 return 1;
1234 }
1235 }
1236 else if ( !strcmp(argv[curArg], "--cdrom")
1237 || !strcmp(argv[curArg], "-cdrom"))
1238 {
1239 if (++curArg >= argc)
1240 {
1241 RTPrintf("Error: missing file/device name for cdrom!\n");
1242 return 1;
1243 }
1244 /* resolve it. */
1245 if (RTPathExists(argv[curArg]))
1246 pcszCdromFile = RTPathRealDup(argv[curArg]);
1247 if (!pcszCdromFile)
1248 {
1249 RTPrintf("Error: The path to the specified cdrom, '%s', could not be resolved.\n", argv[curArg]);
1250 return 1;
1251 }
1252 }
1253 else if ( !strcmp(argv[curArg], "--vrdp")
1254 || !strcmp(argv[curArg], "-vrdp"))
1255 {
1256 // start with the standard VRDP port
1257 pszPortVRDP = "0";
1258
1259 // is there another argument
1260 if (argc > (curArg + 1))
1261 {
1262 curArg++;
1263 pszPortVRDP = argv[curArg];
1264 LogFlow(("Using non standard VRDP port %s\n", pszPortVRDP));
1265 }
1266 }
1267 else if ( !strcmp(argv[curArg], "--discardstate")
1268 || !strcmp(argv[curArg], "-discardstate"))
1269 {
1270 fDiscardState = true;
1271 }
1272 else if (!strcmp(argv[curArg], "--settingspw"))
1273 {
1274 if (++curArg >= argc)
1275 {
1276 RTPrintf("Error: missing password");
1277 return 1;
1278 }
1279 pcszSettingsPw = argv[curArg];
1280 }
1281 else if (!strcmp(argv[curArg], "--settingspwfile"))
1282 {
1283 if (++curArg >= argc)
1284 {
1285 RTPrintf("Error: missing password file\n");
1286 return 1;
1287 }
1288 pcszSettingsPwFile = argv[curArg];
1289 }
1290#ifdef VBOX_SECURELABEL
1291 else if ( !strcmp(argv[curArg], "--securelabel")
1292 || !strcmp(argv[curArg], "-securelabel"))
1293 {
1294 fSecureLabel = true;
1295 LogFlow(("Secure labelling turned on\n"));
1296 }
1297 else if ( !strcmp(argv[curArg], "--seclabelfnt")
1298 || !strcmp(argv[curArg], "-seclabelfnt"))
1299 {
1300 if (++curArg >= argc)
1301 {
1302 RTPrintf("Error: missing font file name for secure label!\n");
1303 return 1;
1304 }
1305 secureLabelFontFile = argv[curArg];
1306 }
1307 else if ( !strcmp(argv[curArg], "--seclabelsiz")
1308 || !strcmp(argv[curArg], "-seclabelsiz"))
1309 {
1310 if (++curArg >= argc)
1311 {
1312 RTPrintf("Error: missing font point size for secure label!\n");
1313 return 1;
1314 }
1315 secureLabelPointSize = atoi(argv[curArg]);
1316 }
1317 else if ( !strcmp(argv[curArg], "--seclabelofs")
1318 || !strcmp(argv[curArg], "-seclabelofs"))
1319 {
1320 if (++curArg >= argc)
1321 {
1322 RTPrintf("Error: missing font pixel offset for secure label!\n");
1323 return 1;
1324 }
1325 secureLabelFontOffs = atoi(argv[curArg]);
1326 }
1327 else if ( !strcmp(argv[curArg], "--seclabelfgcol")
1328 || !strcmp(argv[curArg], "-seclabelfgcol"))
1329 {
1330 if (++curArg >= argc)
1331 {
1332 RTPrintf("Error: missing text color value for secure label!\n");
1333 return 1;
1334 }
1335 sscanf(argv[curArg], "%X", &secureLabelColorFG);
1336 }
1337 else if ( !strcmp(argv[curArg], "--seclabelbgcol")
1338 || !strcmp(argv[curArg], "-seclabelbgcol"))
1339 {
1340 if (++curArg >= argc)
1341 {
1342 RTPrintf("Error: missing background color value for secure label!\n");
1343 return 1;
1344 }
1345 sscanf(argv[curArg], "%X", &secureLabelColorBG);
1346 }
1347#endif
1348#ifdef VBOXSDL_ADVANCED_OPTIONS
1349 else if ( !strcmp(argv[curArg], "--rawr0")
1350 || !strcmp(argv[curArg], "-rawr0"))
1351 fRawR0 = true;
1352 else if ( !strcmp(argv[curArg], "--norawr0")
1353 || !strcmp(argv[curArg], "-norawr0"))
1354 fRawR0 = false;
1355 else if ( !strcmp(argv[curArg], "--rawr3")
1356 || !strcmp(argv[curArg], "-rawr3"))
1357 fRawR3 = true;
1358 else if ( !strcmp(argv[curArg], "--norawr3")
1359 || !strcmp(argv[curArg], "-norawr3"))
1360 fRawR3 = false;
1361 else if ( !strcmp(argv[curArg], "--patm")
1362 || !strcmp(argv[curArg], "-patm"))
1363 fPATM = true;
1364 else if ( !strcmp(argv[curArg], "--nopatm")
1365 || !strcmp(argv[curArg], "-nopatm"))
1366 fPATM = false;
1367 else if ( !strcmp(argv[curArg], "--csam")
1368 || !strcmp(argv[curArg], "-csam"))
1369 fCSAM = true;
1370 else if ( !strcmp(argv[curArg], "--nocsam")
1371 || !strcmp(argv[curArg], "-nocsam"))
1372 fCSAM = false;
1373 else if ( !strcmp(argv[curArg], "--hwvirtex")
1374 || !strcmp(argv[curArg], "-hwvirtex"))
1375 fHWVirt = true;
1376 else if ( !strcmp(argv[curArg], "--nohwvirtex")
1377 || !strcmp(argv[curArg], "-nohwvirtex"))
1378 fHWVirt = false;
1379 else if ( !strcmp(argv[curArg], "--warpdrive")
1380 || !strcmp(argv[curArg], "-warpdrive"))
1381 {
1382 if (++curArg >= argc)
1383 {
1384 RTPrintf("Error: missing the rate value for the --warpdrive option!\n");
1385 return 1;
1386 }
1387 u32WarpDrive = RTStrToUInt32(argv[curArg]);
1388 if (u32WarpDrive < 2 || u32WarpDrive > 20000)
1389 {
1390 RTPrintf("Error: the warp drive rate is restricted to [2..20000]. (%d)\n", u32WarpDrive);
1391 return 1;
1392 }
1393 }
1394#endif /* VBOXSDL_ADVANCED_OPTIONS */
1395#ifdef VBOX_WIN32_UI
1396 else if ( !strcmp(argv[curArg], "--win32ui")
1397 || !strcmp(argv[curArg], "-win32ui"))
1398 fWin32UI = true;
1399#endif
1400 else if ( !strcmp(argv[curArg], "--showsdlconfig")
1401 || !strcmp(argv[curArg], "-showsdlconfig"))
1402 fShowSDLConfig = true;
1403 else if ( !strcmp(argv[curArg], "--hostkey")
1404 || !strcmp(argv[curArg], "-hostkey"))
1405 {
1406 if (++curArg + 1 >= argc)
1407 {
1408 RTPrintf("Error: not enough arguments for host keys!\n");
1409 return 1;
1410 }
1411 gHostKeySym1 = atoi(argv[curArg++]);
1412 if (curArg + 1 < argc && (argv[curArg+1][0] == '0' || atoi(argv[curArg+1]) > 0))
1413 {
1414 /* two-key sequence as host key specified */
1415 gHostKeySym2 = atoi(argv[curArg++]);
1416 }
1417 gHostKeyMod = atoi(argv[curArg]);
1418 }
1419 /* just show the help screen */
1420 else
1421 {
1422 if ( strcmp(argv[curArg], "-h")
1423 && strcmp(argv[curArg], "-help")
1424 && strcmp(argv[curArg], "--help"))
1425 RTPrintf("Error: unrecognized switch '%s'\n", argv[curArg]);
1426 show_usage();
1427 return 1;
1428 }
1429 }
1430
1431 rc = com::Initialize();
1432#ifdef VBOX_WITH_XPCOM
1433 if (rc == NS_ERROR_FILE_ACCESS_DENIED)
1434 {
1435 char szHome[RTPATH_MAX] = "";
1436 com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
1437 RTPrintf("Failed to initialize COM because the global settings directory '%s' is not accessible!\n", szHome);
1438 return 1;
1439 }
1440#endif
1441 if (FAILED(rc))
1442 {
1443 RTPrintf("Error: COM initialization failed (rc=%Rhrc)!\n", rc);
1444 return 1;
1445 }
1446
1447 /* NOTE: do not convert the following scope to a "do {} while (0);", as
1448 * this would make it all too tempting to use "break;" incorrectly - it
1449 * would skip over the cleanup. */
1450 {
1451 // scopes all the stuff till shutdown
1452 ////////////////////////////////////////////////////////////////////////////
1453
1454 ComPtr<IVirtualBoxClient> pVirtualBoxClient;
1455 ComPtr<IVirtualBox> pVirtualBox;
1456 ComPtr<ISession> pSession;
1457 bool sessionOpened = false;
1458 NativeEventQueue* eventQ = com::NativeEventQueue::getMainEventQueue();
1459
1460 ComPtr<IMachine> pMachine;
1461 ComPtr<IGraphicsAdapter> pGraphicsAdapter;
1462
1463 rc = pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
1464 if (FAILED(rc))
1465 {
1466 com::ErrorInfo info;
1467 if (info.isFullAvailable())
1468 PrintError("Failed to create VirtualBoxClient object",
1469 info.getText().raw(), info.getComponent().raw());
1470 else
1471 RTPrintf("Failed to create VirtualBoxClient object! No error information available (rc=%Rhrc).\n", rc);
1472 goto leave;
1473 }
1474
1475 rc = pVirtualBoxClient->COMGETTER(VirtualBox)(pVirtualBox.asOutParam());
1476 if (FAILED(rc))
1477 {
1478 RTPrintf("Failed to get VirtualBox object (rc=%Rhrc)!\n", rc);
1479 goto leave;
1480 }
1481 rc = pVirtualBoxClient->COMGETTER(Session)(pSession.asOutParam());
1482 if (FAILED(rc))
1483 {
1484 RTPrintf("Failed to get session object (rc=%Rhrc)!\n", rc);
1485 goto leave;
1486 }
1487
1488 if (pcszSettingsPw)
1489 {
1490 CHECK_ERROR(pVirtualBox, SetSettingsSecret(Bstr(pcszSettingsPw).raw()));
1491 if (FAILED(rc))
1492 goto leave;
1493 }
1494 else if (pcszSettingsPwFile)
1495 {
1496 int rcExit = settingsPasswordFile(pVirtualBox, pcszSettingsPwFile);
1497 if (rcExit != RTEXITCODE_SUCCESS)
1498 goto leave;
1499 }
1500
1501 /*
1502 * Do we have a UUID?
1503 */
1504 if (uuidVM.isValid())
1505 {
1506 rc = pVirtualBox->FindMachine(uuidVM.toUtf16().raw(), pMachine.asOutParam());
1507 if (FAILED(rc) || !pMachine)
1508 {
1509 RTPrintf("Error: machine with the given ID not found!\n");
1510 goto leave;
1511 }
1512 }
1513 else if (vmName)
1514 {
1515 /*
1516 * Do we have a name but no UUID?
1517 */
1518 rc = pVirtualBox->FindMachine(Bstr(vmName).raw(), pMachine.asOutParam());
1519 if ((rc == S_OK) && pMachine)
1520 {
1521 Bstr bstrId;
1522 pMachine->COMGETTER(Id)(bstrId.asOutParam());
1523 uuidVM = Guid(bstrId);
1524 }
1525 else
1526 {
1527 RTPrintf("Error: machine with the given name not found!\n");
1528 RTPrintf("Check if this VM has been corrupted and is now inaccessible.");
1529 goto leave;
1530 }
1531 }
1532
1533 /* create SDL event semaphore */
1534 vrc = RTSemEventCreate(&g_EventSemSDLEvents);
1535 AssertReleaseRC(vrc);
1536
1537 rc = pVirtualBoxClient->CheckMachineError(pMachine);
1538 if (FAILED(rc))
1539 {
1540 com::ErrorInfo info;
1541 if (info.isFullAvailable())
1542 PrintError("The VM has errors",
1543 info.getText().raw(), info.getComponent().raw());
1544 else
1545 RTPrintf("Failed to check for VM errors! No error information available (rc=%Rhrc).\n", rc);
1546 goto leave;
1547 }
1548
1549 if (fSeparate)
1550 {
1551 MachineState_T machineState = MachineState_Null;
1552 pMachine->COMGETTER(State)(&machineState);
1553 if ( machineState == MachineState_Running
1554 || machineState == MachineState_Teleporting
1555 || machineState == MachineState_LiveSnapshotting
1556 || machineState == MachineState_Paused
1557 || machineState == MachineState_TeleportingPausedVM
1558 )
1559 {
1560 RTPrintf("VM is already running.\n");
1561 }
1562 else
1563 {
1564 ComPtr<IProgress> progress;
1565 rc = pMachine->LaunchVMProcess(pSession, Bstr("headless").raw(), ComSafeArrayNullInParam(), progress.asOutParam());
1566 if (SUCCEEDED(rc) && !progress.isNull())
1567 {
1568 RTPrintf("Waiting for VM to power on...\n");
1569 rc = progress->WaitForCompletion(-1);
1570 if (SUCCEEDED(rc))
1571 {
1572 BOOL completed = true;
1573 rc = progress->COMGETTER(Completed)(&completed);
1574 if (SUCCEEDED(rc))
1575 {
1576 LONG iRc;
1577 rc = progress->COMGETTER(ResultCode)(&iRc);
1578 if (SUCCEEDED(rc))
1579 {
1580 if (FAILED(iRc))
1581 {
1582 ProgressErrorInfo info(progress);
1583 com::GluePrintErrorInfo(info);
1584 }
1585 else
1586 {
1587 RTPrintf("VM has been successfully started.\n");
1588 /* LaunchVMProcess obtains a shared lock on the machine.
1589 * Unlock it here, because the lock will be obtained below
1590 * in the common code path as for already running VM.
1591 */
1592 pSession->UnlockMachine();
1593 }
1594 }
1595 }
1596 }
1597 }
1598 }
1599 if (FAILED(rc))
1600 {
1601 RTPrintf("Error: failed to power up VM! No error text available.\n");
1602 goto leave;
1603 }
1604
1605 rc = pMachine->LockMachine(pSession, LockType_Shared);
1606 }
1607 else
1608 {
1609 pSession->COMSETTER(Name)(Bstr("GUI/SDL").raw());
1610 rc = pMachine->LockMachine(pSession, LockType_VM);
1611 }
1612
1613 if (FAILED(rc))
1614 {
1615 com::ErrorInfo info;
1616 if (info.isFullAvailable())
1617 PrintError("Could not open VirtualBox session",
1618 info.getText().raw(), info.getComponent().raw());
1619 goto leave;
1620 }
1621 if (!pSession)
1622 {
1623 RTPrintf("Could not open VirtualBox session!\n");
1624 goto leave;
1625 }
1626 sessionOpened = true;
1627 // get the mutable VM we're dealing with
1628 pSession->COMGETTER(Machine)(gpMachine.asOutParam());
1629 if (!gpMachine)
1630 {
1631 com::ErrorInfo info;
1632 if (info.isFullAvailable())
1633 PrintError("Cannot start VM!",
1634 info.getText().raw(), info.getComponent().raw());
1635 else
1636 RTPrintf("Error: given machine not found!\n");
1637 goto leave;
1638 }
1639
1640 // get the VM console
1641 pSession->COMGETTER(Console)(gpConsole.asOutParam());
1642 if (!gpConsole)
1643 {
1644 RTPrintf("Given console not found!\n");
1645 goto leave;
1646 }
1647
1648 /*
1649 * Are we supposed to use a different hard disk file?
1650 */
1651 if (pcszHdaFile)
1652 {
1653 ComPtr<IMedium> pMedium;
1654
1655 /*
1656 * Strategy: if any registered hard disk points to the same file,
1657 * assign it. If not, register a new image and assign it to the VM.
1658 */
1659 Bstr bstrHdaFile(pcszHdaFile);
1660 pVirtualBox->OpenMedium(bstrHdaFile.raw(), DeviceType_HardDisk,
1661 AccessMode_ReadWrite, FALSE /* fForceNewUuid */,
1662 pMedium.asOutParam());
1663 if (!pMedium)
1664 {
1665 /* we've not found the image */
1666 RTPrintf("Adding hard disk '%s'...\n", pcszHdaFile);
1667 pVirtualBox->OpenMedium(bstrHdaFile.raw(), DeviceType_HardDisk,
1668 AccessMode_ReadWrite, FALSE /* fForceNewUuid */,
1669 pMedium.asOutParam());
1670 }
1671 /* do we have the right image now? */
1672 if (pMedium)
1673 {
1674 Bstr bstrSCName;
1675
1676 /* get the first IDE controller to attach the harddisk to
1677 * and if there is none, add one temporarily */
1678 {
1679 ComPtr<IStorageController> pStorageCtl;
1680 com::SafeIfaceArray<IStorageController> aStorageControllers;
1681 CHECK_ERROR(gpMachine, COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(aStorageControllers)));
1682 for (size_t i = 0; i < aStorageControllers.size(); ++ i)
1683 {
1684 StorageBus_T storageBus = StorageBus_Null;
1685
1686 CHECK_ERROR(aStorageControllers[i], COMGETTER(Bus)(&storageBus));
1687 if (storageBus == StorageBus_IDE)
1688 {
1689 pStorageCtl = aStorageControllers[i];
1690 break;
1691 }
1692 }
1693
1694 if (pStorageCtl)
1695 {
1696 CHECK_ERROR(pStorageCtl, COMGETTER(Name)(bstrSCName.asOutParam()));
1697 gpMachine->DetachDevice(bstrSCName.raw(), 0, 0);
1698 }
1699 else
1700 {
1701 bstrSCName = "IDE Controller";
1702 CHECK_ERROR(gpMachine, AddStorageController(bstrSCName.raw(),
1703 StorageBus_IDE,
1704 pStorageCtl.asOutParam()));
1705 }
1706 }
1707
1708 CHECK_ERROR(gpMachine, AttachDevice(bstrSCName.raw(), 0, 0,
1709 DeviceType_HardDisk, pMedium));
1710 /// @todo why is this attachment saved?
1711 }
1712 else
1713 {
1714 RTPrintf("Error: failed to mount the specified hard disk image!\n");
1715 goto leave;
1716 }
1717 }
1718
1719 /*
1720 * Mount a floppy if requested.
1721 */
1722 if (pcszFdaFile)
1723 do
1724 {
1725 ComPtr<IMedium> pMedium;
1726
1727 /* unmount? */
1728 if (!strcmp(pcszFdaFile, "none"))
1729 {
1730 /* nothing to do, NULL object will cause unmount */
1731 }
1732 else
1733 {
1734 Bstr bstrFdaFile(pcszFdaFile);
1735
1736 /* Assume it's a host drive name */
1737 ComPtr<IHost> pHost;
1738 CHECK_ERROR_BREAK(pVirtualBox, COMGETTER(Host)(pHost.asOutParam()));
1739 rc = pHost->FindHostFloppyDrive(bstrFdaFile.raw(),
1740 pMedium.asOutParam());
1741 if (FAILED(rc))
1742 {
1743 /* try to find an existing one */
1744 rc = pVirtualBox->OpenMedium(bstrFdaFile.raw(),
1745 DeviceType_Floppy,
1746 AccessMode_ReadWrite,
1747 FALSE /* fForceNewUuid */,
1748 pMedium.asOutParam());
1749 if (FAILED(rc))
1750 {
1751 /* try to add to the list */
1752 RTPrintf("Adding floppy image '%s'...\n", pcszFdaFile);
1753 CHECK_ERROR_BREAK(pVirtualBox,
1754 OpenMedium(bstrFdaFile.raw(),
1755 DeviceType_Floppy,
1756 AccessMode_ReadWrite,
1757 FALSE /* fForceNewUuid */,
1758 pMedium.asOutParam()));
1759 }
1760 }
1761 }
1762
1763 Bstr bstrSCName;
1764
1765 /* get the first floppy controller to attach the floppy to
1766 * and if there is none, add one temporarily */
1767 {
1768 ComPtr<IStorageController> pStorageCtl;
1769 com::SafeIfaceArray<IStorageController> aStorageControllers;
1770 CHECK_ERROR(gpMachine, COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(aStorageControllers)));
1771 for (size_t i = 0; i < aStorageControllers.size(); ++ i)
1772 {
1773 StorageBus_T storageBus = StorageBus_Null;
1774
1775 CHECK_ERROR(aStorageControllers[i], COMGETTER(Bus)(&storageBus));
1776 if (storageBus == StorageBus_Floppy)
1777 {
1778 pStorageCtl = aStorageControllers[i];
1779 break;
1780 }
1781 }
1782
1783 if (pStorageCtl)
1784 {
1785 CHECK_ERROR(pStorageCtl, COMGETTER(Name)(bstrSCName.asOutParam()));
1786 gpMachine->DetachDevice(bstrSCName.raw(), 0, 0);
1787 }
1788 else
1789 {
1790 bstrSCName = "Floppy Controller";
1791 CHECK_ERROR(gpMachine, AddStorageController(bstrSCName.raw(),
1792 StorageBus_Floppy,
1793 pStorageCtl.asOutParam()));
1794 }
1795 }
1796
1797 CHECK_ERROR(gpMachine, AttachDevice(bstrSCName.raw(), 0, 0,
1798 DeviceType_Floppy, pMedium));
1799 }
1800 while (0);
1801 if (FAILED(rc))
1802 goto leave;
1803
1804 /*
1805 * Mount a CD-ROM if requested.
1806 */
1807 if (pcszCdromFile)
1808 do
1809 {
1810 ComPtr<IMedium> pMedium;
1811
1812 /* unmount? */
1813 if (!strcmp(pcszCdromFile, "none"))
1814 {
1815 /* nothing to do, NULL object will cause unmount */
1816 }
1817 else
1818 {
1819 Bstr bstrCdromFile(pcszCdromFile);
1820
1821 /* Assume it's a host drive name */
1822 ComPtr<IHost> pHost;
1823 CHECK_ERROR_BREAK(pVirtualBox, COMGETTER(Host)(pHost.asOutParam()));
1824 rc = pHost->FindHostDVDDrive(bstrCdromFile.raw(), pMedium.asOutParam());
1825 if (FAILED(rc))
1826 {
1827 /* try to find an existing one */
1828 rc = pVirtualBox->OpenMedium(bstrCdromFile.raw(),
1829 DeviceType_DVD,
1830 AccessMode_ReadWrite,
1831 FALSE /* fForceNewUuid */,
1832 pMedium.asOutParam());
1833 if (FAILED(rc))
1834 {
1835 /* try to add to the list */
1836 RTPrintf("Adding ISO image '%s'...\n", pcszCdromFile);
1837 CHECK_ERROR_BREAK(pVirtualBox,
1838 OpenMedium(bstrCdromFile.raw(),
1839 DeviceType_DVD,
1840 AccessMode_ReadWrite,
1841 FALSE /* fForceNewUuid */,
1842 pMedium.asOutParam()));
1843 }
1844 }
1845 }
1846
1847 Bstr bstrSCName;
1848
1849 /* get the first IDE controller to attach the DVD drive to
1850 * and if there is none, add one temporarily */
1851 {
1852 ComPtr<IStorageController> pStorageCtl;
1853 com::SafeIfaceArray<IStorageController> aStorageControllers;
1854 CHECK_ERROR(gpMachine, COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(aStorageControllers)));
1855 for (size_t i = 0; i < aStorageControllers.size(); ++ i)
1856 {
1857 StorageBus_T storageBus = StorageBus_Null;
1858
1859 CHECK_ERROR(aStorageControllers[i], COMGETTER(Bus)(&storageBus));
1860 if (storageBus == StorageBus_IDE)
1861 {
1862 pStorageCtl = aStorageControllers[i];
1863 break;
1864 }
1865 }
1866
1867 if (pStorageCtl)
1868 {
1869 CHECK_ERROR(pStorageCtl, COMGETTER(Name)(bstrSCName.asOutParam()));
1870 gpMachine->DetachDevice(bstrSCName.raw(), 1, 0);
1871 }
1872 else
1873 {
1874 bstrSCName = "IDE Controller";
1875 CHECK_ERROR(gpMachine, AddStorageController(bstrSCName.raw(),
1876 StorageBus_IDE,
1877 pStorageCtl.asOutParam()));
1878 }
1879 }
1880
1881 CHECK_ERROR(gpMachine, AttachDevice(bstrSCName.raw(), 1, 0,
1882 DeviceType_DVD, pMedium));
1883 }
1884 while (0);
1885 if (FAILED(rc))
1886 goto leave;
1887
1888 if (fDiscardState)
1889 {
1890 /*
1891 * If the machine is currently saved,
1892 * discard the saved state first.
1893 */
1894 MachineState_T machineState;
1895 gpMachine->COMGETTER(State)(&machineState);
1896 if (machineState == MachineState_Saved || machineState == MachineState_AbortedSaved)
1897 {
1898 CHECK_ERROR(gpMachine, DiscardSavedState(true /* fDeleteFile */));
1899 }
1900 /*
1901 * If there are snapshots, discard the current state,
1902 * i.e. revert to the last snapshot.
1903 */
1904 ULONG cSnapshots;
1905 gpMachine->COMGETTER(SnapshotCount)(&cSnapshots);
1906 if (cSnapshots)
1907 {
1908 gpProgress = NULL;
1909
1910 ComPtr<ISnapshot> pCurrentSnapshot;
1911 CHECK_ERROR(gpMachine, COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam()));
1912 if (FAILED(rc))
1913 goto leave;
1914
1915 CHECK_ERROR(gpMachine, RestoreSnapshot(pCurrentSnapshot, gpProgress.asOutParam()));
1916 rc = gpProgress->WaitForCompletion(-1);
1917 }
1918 }
1919
1920 // get the machine debugger (does not have to be there)
1921 gpConsole->COMGETTER(Debugger)(gpMachineDebugger.asOutParam());
1922 if (gpMachineDebugger)
1923 {
1924 Log(("Machine debugger available!\n"));
1925 }
1926 gpConsole->COMGETTER(Display)(gpDisplay.asOutParam());
1927 if (!gpDisplay)
1928 {
1929 RTPrintf("Error: could not get display object!\n");
1930 goto leave;
1931 }
1932
1933 // set the boot drive
1934 if (bootDevice != DeviceType_Null)
1935 {
1936 rc = gpMachine->SetBootOrder(1, bootDevice);
1937 if (rc != S_OK)
1938 {
1939 RTPrintf("Error: could not set boot device, using default.\n");
1940 }
1941 }
1942
1943 // set the memory size if not default
1944 if (memorySize)
1945 {
1946 rc = gpMachine->COMSETTER(MemorySize)(memorySize);
1947 if (rc != S_OK)
1948 {
1949 ULONG ramSize = 0;
1950 gpMachine->COMGETTER(MemorySize)(&ramSize);
1951 RTPrintf("Error: could not set memory size, using current setting of %d MBytes\n", ramSize);
1952 }
1953 }
1954
1955 rc = gpMachine->COMGETTER(GraphicsAdapter)(pGraphicsAdapter.asOutParam());
1956 if (rc != S_OK)
1957 {
1958 RTPrintf("Error: could not get graphics adapter object\n");
1959 goto leave;
1960 }
1961
1962 if (vramSize)
1963 {
1964 rc = pGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
1965 if (rc != S_OK)
1966 {
1967 pGraphicsAdapter->COMGETTER(VRAMSize)((ULONG*)&vramSize);
1968 RTPrintf("Error: could not set VRAM size, using current setting of %d MBytes\n", vramSize);
1969 }
1970 }
1971
1972 // we're always able to process absolute mouse events and we prefer that
1973 gfAbsoluteMouseHost = TRUE;
1974
1975#ifdef VBOX_WIN32_UI
1976 if (fWin32UI)
1977 {
1978 /* initialize the Win32 user interface inside which SDL will be embedded */
1979 if (initUI(fResizable, winId))
1980 return 1;
1981 }
1982#endif
1983
1984 /* static initialization of the SDL stuff */
1985 if (!VBoxSDLFB::init(fShowSDLConfig))
1986 goto leave;
1987
1988 pGraphicsAdapter->COMGETTER(MonitorCount)(&gcMonitors);
1989 if (gcMonitors > 64)
1990 gcMonitors = 64;
1991
1992 for (unsigned i = 0; i < gcMonitors; i++)
1993 {
1994 // create our SDL framebuffer instance
1995 gpFramebuffer[i].createObject();
1996 rc = gpFramebuffer[i]->init(i, fFullscreen, fResizable, fShowSDLConfig, false,
1997 fixedWidth, fixedHeight, fixedBPP, fSeparate);
1998 if (FAILED(rc))
1999 {
2000 RTPrintf("Error: could not create framebuffer object!\n");
2001 goto leave;
2002 }
2003 }
2004
2005#ifdef VBOX_WIN32_UI
2006 gpFramebuffer[0]->setWinId(winId);
2007#endif
2008
2009 for (unsigned i = 0; i < gcMonitors; i++)
2010 {
2011 if (!gpFramebuffer[i]->initialized())
2012 goto leave;
2013 gpFramebuffer[i]->AddRef();
2014 if (fFullscreen)
2015 SetFullscreen(true);
2016 }
2017
2018#ifdef VBOX_SECURELABEL
2019 if (fSecureLabel)
2020 {
2021 if (!secureLabelFontFile)
2022 {
2023 RTPrintf("Error: no font file specified for secure label!\n");
2024 goto leave;
2025 }
2026 /* load the SDL_ttf library and get the required imports */
2027 vrc = RTLdrLoadSystem(LIBSDL_TTF_NAME, true /*fNoUnload*/, &gLibrarySDL_ttf);
2028 if (RT_SUCCESS(vrc))
2029 vrc = RTLdrGetSymbol(gLibrarySDL_ttf, "TTF_Init", (void**)&pTTF_Init);
2030 if (RT_SUCCESS(vrc))
2031 vrc = RTLdrGetSymbol(gLibrarySDL_ttf, "TTF_OpenFont", (void**)&pTTF_OpenFont);
2032 if (RT_SUCCESS(vrc))
2033 vrc = RTLdrGetSymbol(gLibrarySDL_ttf, "TTF_RenderUTF8_Solid", (void**)&pTTF_RenderUTF8_Solid);
2034 if (RT_SUCCESS(vrc))
2035 {
2036 /* silently ignore errors here */
2037 vrc = RTLdrGetSymbol(gLibrarySDL_ttf, "TTF_RenderUTF8_Blended", (void**)&pTTF_RenderUTF8_Blended);
2038 if (RT_FAILURE(vrc))
2039 pTTF_RenderUTF8_Blended = NULL;
2040 vrc = VINF_SUCCESS;
2041 }
2042 if (RT_SUCCESS(vrc))
2043 vrc = RTLdrGetSymbol(gLibrarySDL_ttf, "TTF_CloseFont", (void**)&pTTF_CloseFont);
2044 if (RT_SUCCESS(vrc))
2045 vrc = RTLdrGetSymbol(gLibrarySDL_ttf, "TTF_Quit", (void**)&pTTF_Quit);
2046 if (RT_SUCCESS(vrc))
2047 vrc = gpFramebuffer[0]->initSecureLabel(SECURE_LABEL_HEIGHT, secureLabelFontFile, secureLabelPointSize, secureLabelFontOffs);
2048 if (RT_FAILURE(vrc))
2049 {
2050 RTPrintf("Error: could not initialize secure labeling: rc = %Rrc\n", vrc);
2051 goto leave;
2052 }
2053 Bstr bstrLabel;
2054 gpMachine->GetExtraData(Bstr(VBOXSDL_SECURELABEL_EXTRADATA).raw(), bstrLabel.asOutParam());
2055 Utf8Str labelUtf8(bstrLabel);
2056 /*
2057 * Now update the label
2058 */
2059 gpFramebuffer[0]->setSecureLabelColor(secureLabelColorFG, secureLabelColorBG);
2060 gpFramebuffer[0]->setSecureLabelText(labelUtf8.c_str());
2061 }
2062#endif
2063
2064#ifdef VBOXSDL_WITH_X11
2065 /* NOTE1: We still want Ctrl-C to work, so we undo the SDL redirections.
2066 * NOTE2: We have to remove the PidFile if this file exists. */
2067 signal(SIGINT, signal_handler_SIGINT);
2068 signal(SIGQUIT, signal_handler_SIGINT);
2069 signal(SIGSEGV, signal_handler_SIGINT);
2070#endif
2071
2072
2073 for (ULONG i = 0; i < gcMonitors; i++)
2074 {
2075 // register our framebuffer
2076 rc = gpDisplay->AttachFramebuffer(i, gpFramebuffer[i], gaFramebufferId[i].asOutParam());
2077 if (FAILED(rc))
2078 {
2079 RTPrintf("Error: could not register framebuffer object!\n");
2080 goto leave;
2081 }
2082 ULONG dummy;
2083 LONG xOrigin, yOrigin;
2084 GuestMonitorStatus_T monitorStatus;
2085 rc = gpDisplay->GetScreenResolution(i, &dummy, &dummy, &dummy, &xOrigin, &yOrigin, &monitorStatus);
2086 gpFramebuffer[i]->setOrigin(xOrigin, yOrigin);
2087 }
2088
2089 {
2090 // register listener for VirtualBoxClient events
2091 ComPtr<IEventSource> pES;
2092 CHECK_ERROR(pVirtualBoxClient, COMGETTER(EventSource)(pES.asOutParam()));
2093 ComObjPtr<VBoxSDLClientEventListenerImpl> listener;
2094 listener.createObject();
2095 listener->init(new VBoxSDLClientEventListener());
2096 pVBoxClientListener = listener;
2097 com::SafeArray<VBoxEventType_T> eventTypes;
2098 eventTypes.push_back(VBoxEventType_OnVBoxSVCAvailabilityChanged);
2099 CHECK_ERROR(pES, RegisterListener(pVBoxClientListener, ComSafeArrayAsInParam(eventTypes), true));
2100 }
2101
2102 {
2103 // register listener for VirtualBox (server) events
2104 ComPtr<IEventSource> pES;
2105 CHECK_ERROR(pVirtualBox, COMGETTER(EventSource)(pES.asOutParam()));
2106 ComObjPtr<VBoxSDLEventListenerImpl> listener;
2107 listener.createObject();
2108 listener->init(new VBoxSDLEventListener());
2109 pVBoxListener = listener;
2110 com::SafeArray<VBoxEventType_T> eventTypes;
2111 eventTypes.push_back(VBoxEventType_OnExtraDataChanged);
2112 CHECK_ERROR(pES, RegisterListener(pVBoxListener, ComSafeArrayAsInParam(eventTypes), true));
2113 }
2114
2115 {
2116 // register listener for Console events
2117 ComPtr<IEventSource> pES;
2118 CHECK_ERROR(gpConsole, COMGETTER(EventSource)(pES.asOutParam()));
2119 pConsoleListener.createObject();
2120 pConsoleListener->init(new VBoxSDLConsoleEventListener());
2121 com::SafeArray<VBoxEventType_T> eventTypes;
2122 eventTypes.push_back(VBoxEventType_OnMousePointerShapeChanged);
2123 eventTypes.push_back(VBoxEventType_OnMouseCapabilityChanged);
2124 eventTypes.push_back(VBoxEventType_OnKeyboardLedsChanged);
2125 eventTypes.push_back(VBoxEventType_OnStateChanged);
2126 eventTypes.push_back(VBoxEventType_OnRuntimeError);
2127 eventTypes.push_back(VBoxEventType_OnCanShowWindow);
2128 eventTypes.push_back(VBoxEventType_OnShowWindow);
2129 CHECK_ERROR(pES, RegisterListener(pConsoleListener, ComSafeArrayAsInParam(eventTypes), true));
2130 // until we've tried to to start the VM, ignore power off events
2131 pConsoleListener->getWrapped()->ignorePowerOffEvents(true);
2132 }
2133
2134 if (pszPortVRDP)
2135 {
2136 rc = gpMachine->COMGETTER(VRDEServer)(gpVRDEServer.asOutParam());
2137 AssertMsg((rc == S_OK) && gpVRDEServer, ("Could not get VRDP Server! rc = 0x%x\n", rc));
2138 if (gpVRDEServer)
2139 {
2140 // has a non standard VRDP port been requested?
2141 if (strcmp(pszPortVRDP, "0"))
2142 {
2143 rc = gpVRDEServer->SetVRDEProperty(Bstr("TCP/Ports").raw(), Bstr(pszPortVRDP).raw());
2144 if (rc != S_OK)
2145 {
2146 RTPrintf("Error: could not set VRDP port! rc = 0x%x\n", rc);
2147 goto leave;
2148 }
2149 }
2150 // now enable VRDP
2151 rc = gpVRDEServer->COMSETTER(Enabled)(TRUE);
2152 if (rc != S_OK)
2153 {
2154 RTPrintf("Error: could not enable VRDP server! rc = 0x%x\n", rc);
2155 goto leave;
2156 }
2157 }
2158 }
2159
2160 rc = E_FAIL;
2161#ifdef VBOXSDL_ADVANCED_OPTIONS
2162 if (fRawR0 != ~0U)
2163 {
2164 if (!gpMachineDebugger)
2165 {
2166 RTPrintf("Error: No debugger object; -%srawr0 cannot be executed!\n", fRawR0 ? "" : "no");
2167 goto leave;
2168 }
2169 gpMachineDebugger->COMSETTER(RecompileSupervisor)(!fRawR0);
2170 }
2171 if (fRawR3 != ~0U)
2172 {
2173 if (!gpMachineDebugger)
2174 {
2175 RTPrintf("Error: No debugger object; -%srawr3 cannot be executed!\n", fRawR3 ? "" : "no");
2176 goto leave;
2177 }
2178 gpMachineDebugger->COMSETTER(RecompileUser)(!fRawR3);
2179 }
2180 if (fPATM != ~0U)
2181 {
2182 if (!gpMachineDebugger)
2183 {
2184 RTPrintf("Error: No debugger object; -%spatm cannot be executed!\n", fPATM ? "" : "no");
2185 goto leave;
2186 }
2187 gpMachineDebugger->COMSETTER(PATMEnabled)(fPATM);
2188 }
2189 if (fCSAM != ~0U)
2190 {
2191 if (!gpMachineDebugger)
2192 {
2193 RTPrintf("Error: No debugger object; -%scsam cannot be executed!\n", fCSAM ? "" : "no");
2194 goto leave;
2195 }
2196 gpMachineDebugger->COMSETTER(CSAMEnabled)(fCSAM);
2197 }
2198 if (fHWVirt != ~0U)
2199 {
2200 gpMachine->SetHWVirtExProperty(HWVirtExPropertyType_Enabled, fHWVirt);
2201 }
2202 if (u32WarpDrive != 0)
2203 {
2204 if (!gpMachineDebugger)
2205 {
2206 RTPrintf("Error: No debugger object; --warpdrive %d cannot be executed!\n", u32WarpDrive);
2207 goto leave;
2208 }
2209 gpMachineDebugger->COMSETTER(VirtualTimeRate)(u32WarpDrive);
2210 }
2211#endif /* VBOXSDL_ADVANCED_OPTIONS */
2212
2213 /* start with something in the titlebar */
2214 UpdateTitlebar(TITLEBAR_NORMAL);
2215
2216 /* memorize the default cursor */
2217 gpDefaultCursor = SDL_GetCursor();
2218
2219#if !defined(VBOX_WITH_SDL2)
2220# if defined(VBOXSDL_WITH_X11)
2221 /* Get Window Manager info. We only need the X11 display. */
2222 SDL_VERSION(&gSdlInfo.version);
2223 if (!SDL_GetWMInfo(&gSdlInfo))
2224 RTPrintf("Error: could not get SDL Window Manager info -- no Xcursor support!\n");
2225 else
2226 gfXCursorEnabled = TRUE;
2227
2228# if !defined(VBOX_WITHOUT_XCURSOR)
2229 /* SDL uses its own (plain) default cursor. Use the left arrow cursor instead which might look
2230 * much better if a mouse cursor theme is installed. */
2231 if (gfXCursorEnabled)
2232 {
2233 gpDefaultOrigX11Cursor = *(Cursor*)gpDefaultCursor->wm_cursor;
2234 *(Cursor*)gpDefaultCursor->wm_cursor = XCreateFontCursor(gSdlInfo.info.x11.display, XC_left_ptr);
2235 SDL_SetCursor(gpDefaultCursor);
2236 }
2237# endif
2238 /* Initialise the keyboard */
2239 X11DRV_InitKeyboard(gSdlInfo.info.x11.display, NULL, NULL, NULL, NULL);
2240# endif /* VBOXSDL_WITH_X11 */
2241
2242 /* create a fake empty cursor */
2243 {
2244 uint8_t cursorData[1] = {0};
2245 gpCustomCursor = SDL_CreateCursor(cursorData, cursorData, 8, 1, 0, 0);
2246 gpCustomOrigWMcursor = gpCustomCursor->wm_cursor;
2247 gpCustomCursor->wm_cursor = NULL;
2248 }
2249#endif /* !VBOX_WITH_SDL2 */
2250
2251 /*
2252 * Register our user signal handler.
2253 */
2254#ifdef VBOXSDL_WITH_X11
2255 struct sigaction sa;
2256 sa.sa_sigaction = signal_handler_SIGUSR1;
2257 sigemptyset(&sa.sa_mask);
2258 sa.sa_flags = SA_RESTART | SA_SIGINFO;
2259 sigaction(SIGUSR1, &sa, NULL);
2260#endif /* VBOXSDL_WITH_X11 */
2261
2262 /*
2263 * Start the VM execution thread. This has to be done
2264 * asynchronously as powering up can take some time
2265 * (accessing devices such as the host DVD drive). In
2266 * the meantime, we have to service the SDL event loop.
2267 */
2268 SDL_Event event;
2269
2270 if (!fSeparate)
2271 {
2272 LogFlow(("Powering up the VM...\n"));
2273 rc = gpConsole->PowerUp(gpProgress.asOutParam());
2274 if (rc != S_OK)
2275 {
2276 com::ErrorInfo info(gpConsole, COM_IIDOF(IConsole));
2277 if (info.isBasicAvailable())
2278 PrintError("Failed to power up VM", info.getText().raw());
2279 else
2280 RTPrintf("Error: failed to power up VM! No error text available.\n");
2281 goto leave;
2282 }
2283 }
2284
2285#ifdef USE_XPCOM_QUEUE_THREAD
2286 /*
2287 * Before we starting to do stuff, we have to launch the XPCOM
2288 * event queue thread. It will wait for events and send messages
2289 * to the SDL thread. After having done this, we should fairly
2290 * quickly start to process the SDL event queue as an XPCOM
2291 * event storm might arrive. Stupid SDL has a ridiculously small
2292 * event queue buffer!
2293 */
2294 startXPCOMEventQueueThread(eventQ->getSelectFD());
2295#endif /* USE_XPCOM_QUEUE_THREAD */
2296
2297 /* termination flag */
2298 bool fTerminateDuringStartup;
2299 fTerminateDuringStartup = false;
2300
2301 LogRel(("VBoxSDL: NUM lock initially %s, CAPS lock initially %s\n",
2302 !!(SDL_GetModState() & KMOD_NUM) ? "ON" : "OFF",
2303 !!(SDL_GetModState() & KMOD_CAPS) ? "ON" : "OFF"));
2304
2305 /* start regular timer so we don't starve in the event loop */
2306 SDL_TimerID sdlTimer;
2307 sdlTimer = SDL_AddTimer(100, StartupTimer, NULL);
2308
2309 /* loop until the powerup processing is done */
2310 MachineState_T machineState;
2311 do
2312 {
2313 rc = gpMachine->COMGETTER(State)(&machineState);
2314 if ( rc == S_OK
2315 && ( machineState == MachineState_Starting
2316 || machineState == MachineState_Restoring
2317 || machineState == MachineState_TeleportingIn
2318 )
2319 )
2320 {
2321 /*
2322 * wait for the next event. This is uncritical as
2323 * power up guarantees to change the machine state
2324 * to either running or aborted and a machine state
2325 * change will send us an event. However, we have to
2326 * service the XPCOM event queue!
2327 */
2328#ifdef USE_XPCOM_QUEUE_THREAD
2329 if (!fXPCOMEventThreadSignaled)
2330 {
2331 signalXPCOMEventQueueThread();
2332 fXPCOMEventThreadSignaled = true;
2333 }
2334#endif
2335 /*
2336 * Wait for SDL events.
2337 */
2338 if (WaitSDLEvent(&event))
2339 {
2340 switch (event.type)
2341 {
2342 /*
2343 * Timer event. Used to have the titlebar updated.
2344 */
2345 case SDL_USER_EVENT_TIMER:
2346 {
2347 /*
2348 * Update the title bar.
2349 */
2350 UpdateTitlebar(TITLEBAR_STARTUP);
2351 break;
2352 }
2353
2354 /*
2355 * User specific framebuffer change event.
2356 */
2357 case SDL_USER_EVENT_NOTIFYCHANGE:
2358 {
2359 LogFlow(("SDL_USER_EVENT_NOTIFYCHANGE\n"));
2360 LONG xOrigin, yOrigin;
2361 gpFramebuffer[event.user.code]->notifyChange(event.user.code);
2362 /* update xOrigin, yOrigin -> mouse */
2363 ULONG dummy;
2364 GuestMonitorStatus_T monitorStatus;
2365 rc = gpDisplay->GetScreenResolution(event.user.code, &dummy, &dummy, &dummy, &xOrigin, &yOrigin, &monitorStatus);
2366 gpFramebuffer[event.user.code]->setOrigin(xOrigin, yOrigin);
2367 break;
2368 }
2369
2370#ifdef USE_XPCOM_QUEUE_THREAD
2371 /*
2372 * User specific XPCOM event queue event
2373 */
2374 case SDL_USER_EVENT_XPCOM_EVENTQUEUE:
2375 {
2376 LogFlow(("SDL_USER_EVENT_XPCOM_EVENTQUEUE: processing XPCOM event queue...\n"));
2377 eventQ->processEventQueue(0);
2378 signalXPCOMEventQueueThread();
2379 break;
2380 }
2381#endif /* USE_XPCOM_QUEUE_THREAD */
2382
2383 /*
2384 * Termination event from the on state change callback.
2385 */
2386 case SDL_USER_EVENT_TERMINATE:
2387 {
2388 if (event.user.code != VBOXSDL_TERM_NORMAL)
2389 {
2390 com::ProgressErrorInfo info(gpProgress);
2391 if (info.isBasicAvailable())
2392 PrintError("Failed to power up VM", info.getText().raw());
2393 else
2394 RTPrintf("Error: failed to power up VM! No error text available.\n");
2395 }
2396 fTerminateDuringStartup = true;
2397 break;
2398 }
2399
2400 default:
2401 {
2402 Log8(("VBoxSDL: Unknown SDL event %d (pre)\n", event.type));
2403 break;
2404 }
2405 }
2406
2407 }
2408 }
2409 eventQ->processEventQueue(0);
2410 } while ( rc == S_OK
2411 && ( machineState == MachineState_Starting
2412 || machineState == MachineState_Restoring
2413 || machineState == MachineState_TeleportingIn
2414 )
2415 );
2416
2417 /* kill the timer again */
2418 SDL_RemoveTimer(sdlTimer);
2419 sdlTimer = 0;
2420
2421 /* are we supposed to terminate the process? */
2422 if (fTerminateDuringStartup)
2423 goto leave;
2424
2425 /* did the power up succeed? */
2426 if (machineState != MachineState_Running)
2427 {
2428 com::ProgressErrorInfo info(gpProgress);
2429 if (info.isBasicAvailable())
2430 PrintError("Failed to power up VM", info.getText().raw());
2431 else
2432 RTPrintf("Error: failed to power up VM! No error text available (rc = 0x%x state = %d)\n", rc, machineState);
2433 goto leave;
2434 }
2435
2436 // accept power off events from now on because we're running
2437 // note that there's a possible race condition here...
2438 pConsoleListener->getWrapped()->ignorePowerOffEvents(false);
2439
2440 rc = gpConsole->COMGETTER(Keyboard)(gpKeyboard.asOutParam());
2441 if (!gpKeyboard)
2442 {
2443 RTPrintf("Error: could not get keyboard object!\n");
2444 goto leave;
2445 }
2446 gpConsole->COMGETTER(Mouse)(gpMouse.asOutParam());
2447 if (!gpMouse)
2448 {
2449 RTPrintf("Error: could not get mouse object!\n");
2450 goto leave;
2451 }
2452
2453 if (fSeparate && gpMouse)
2454 {
2455 LogFlow(("Fetching mouse caps\n"));
2456
2457 /* Fetch current mouse status, etc */
2458 gpMouse->COMGETTER(AbsoluteSupported)(&gfAbsoluteMouseGuest);
2459 gpMouse->COMGETTER(RelativeSupported)(&gfRelativeMouseGuest);
2460 gpMouse->COMGETTER(NeedsHostCursor)(&gfGuestNeedsHostCursor);
2461
2462 HandleGuestCapsChanged();
2463
2464 ComPtr<IMousePointerShape> mps;
2465 gpMouse->COMGETTER(PointerShape)(mps.asOutParam());
2466 if (!mps.isNull())
2467 {
2468 BOOL visible, alpha;
2469 ULONG hotX, hotY, width, height;
2470 com::SafeArray <BYTE> shape;
2471
2472 mps->COMGETTER(Visible)(&visible);
2473 mps->COMGETTER(Alpha)(&alpha);
2474 mps->COMGETTER(HotX)(&hotX);
2475 mps->COMGETTER(HotY)(&hotY);
2476 mps->COMGETTER(Width)(&width);
2477 mps->COMGETTER(Height)(&height);
2478 mps->COMGETTER(Shape)(ComSafeArrayAsOutParam(shape));
2479
2480 if (shape.size() > 0)
2481 {
2482 PointerShapeChangeData data(visible, alpha, hotX, hotY, width, height,
2483 ComSafeArrayAsInParam(shape));
2484 SetPointerShape(&data);
2485 }
2486 }
2487 }
2488
2489 UpdateTitlebar(TITLEBAR_NORMAL);
2490
2491#ifdef VBOX_WITH_SDL2
2492 /* Key repeats are enabled by default on SDL2. */
2493#else
2494 /*
2495 * Enable keyboard repeats
2496 */
2497 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
2498#endif
2499
2500 /*
2501 * Create PID file.
2502 */
2503 if (gpszPidFile)
2504 {
2505 char szBuf[32];
2506 const char *pcszLf = "\n";
2507 RTFILE PidFile;
2508 RTFileOpen(&PidFile, gpszPidFile, RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE);
2509 RTStrFormatNumber(szBuf, RTProcSelf(), 10, 0, 0, 0);
2510 RTFileWrite(PidFile, szBuf, strlen(szBuf), NULL);
2511 RTFileWrite(PidFile, pcszLf, strlen(pcszLf), NULL);
2512 RTFileClose(PidFile);
2513 }
2514
2515 /*
2516 * Main event loop
2517 */
2518#ifdef USE_XPCOM_QUEUE_THREAD
2519 if (!fXPCOMEventThreadSignaled)
2520 {
2521 signalXPCOMEventQueueThread();
2522 }
2523#endif
2524 LogFlow(("VBoxSDL: Entering big event loop\n"));
2525 while (WaitSDLEvent(&event))
2526 {
2527 switch (event.type)
2528 {
2529 /*
2530 * The screen needs to be repainted.
2531 */
2532#ifdef VBOX_WITH_SDL2
2533 case SDL_WINDOWEVENT:
2534 {
2535 switch (event.window.event)
2536 {
2537 case SDL_WINDOWEVENT_EXPOSED:
2538 {
2539 VBoxSDLFB *fb = getFbFromWinId(event.window.windowID);
2540 if (fb)
2541 fb->repaint();
2542 break;
2543 }
2544 case SDL_WINDOWEVENT_FOCUS_GAINED:
2545 {
2546 break;
2547 }
2548 case SDL_WINDOWEVENT_FOCUS_LOST:
2549 {
2550 break;
2551 }
2552 case SDL_WINDOWEVENT_RESIZED:
2553 {
2554 if (gpDisplay)
2555 {
2556 if (gfIgnoreNextResize)
2557 {
2558 gfIgnoreNextResize = FALSE;
2559 break;
2560 }
2561 uResizeWidth = event.window.data1;
2562#ifdef VBOX_SECURELABEL
2563 if (fSecureLabel)
2564 uResizeHeight = RT_MAX(0, event.window.data2 - SECURE_LABEL_HEIGHT);
2565 else
2566#endif
2567 uResizeHeight = event.window.data2;
2568 if (gSdlResizeTimer)
2569 SDL_RemoveTimer(gSdlResizeTimer);
2570 gSdlResizeTimer = SDL_AddTimer(300, ResizeTimer, NULL);
2571 }
2572 break;
2573 }
2574 default:
2575 break;
2576 }
2577 }
2578#else
2579 case SDL_VIDEOEXPOSE:
2580 {
2581 gpFramebuffer[0]->repaint();
2582 break;
2583 }
2584#endif
2585
2586 /*
2587 * Keyboard events.
2588 */
2589 case SDL_KEYDOWN:
2590 case SDL_KEYUP:
2591 {
2592#ifdef VBOX_WITH_SDL2
2593 SDL_Keycode ksym = event.key.keysym.sym;
2594#else
2595 SDLKey ksym = event.key.keysym.sym;
2596#endif
2597 switch (enmHKeyState)
2598 {
2599 case HKEYSTATE_NORMAL:
2600 {
2601 if ( event.type == SDL_KEYDOWN
2602 && ksym != SDLK_UNKNOWN
2603 && (ksym == gHostKeySym1 || ksym == gHostKeySym2))
2604 {
2605 EvHKeyDown1 = event;
2606 enmHKeyState = ksym == gHostKeySym1 ? HKEYSTATE_DOWN_1ST
2607 : HKEYSTATE_DOWN_2ND;
2608 break;
2609 }
2610 ProcessKey(&event.key);
2611 break;
2612 }
2613
2614 case HKEYSTATE_DOWN_1ST:
2615 case HKEYSTATE_DOWN_2ND:
2616 {
2617 if (gHostKeySym2 != SDLK_UNKNOWN)
2618 {
2619 if ( event.type == SDL_KEYDOWN
2620 && ksym != SDLK_UNKNOWN
2621 && ( (enmHKeyState == HKEYSTATE_DOWN_1ST && ksym == gHostKeySym2)
2622 || (enmHKeyState == HKEYSTATE_DOWN_2ND && ksym == gHostKeySym1)))
2623 {
2624 EvHKeyDown2 = event;
2625 enmHKeyState = HKEYSTATE_DOWN;
2626 break;
2627 }
2628 enmHKeyState = event.type == SDL_KEYUP ? HKEYSTATE_NORMAL
2629 : HKEYSTATE_NOT_IT;
2630 ProcessKey(&EvHKeyDown1.key);
2631 /* ugly hack: Some guests (e.g. mstsc.exe on Windows XP)
2632 * expect a small delay between two key events. 5ms work
2633 * reliable here so use 10ms to be on the safe side. A
2634 * better but more complicated fix would be to introduce
2635 * a new state and don't wait here. */
2636 RTThreadSleep(10);
2637 ProcessKey(&event.key);
2638 break;
2639 }
2640 }
2641 RT_FALL_THRU();
2642
2643 case HKEYSTATE_DOWN:
2644 {
2645 if (event.type == SDL_KEYDOWN)
2646 {
2647 /* potential host key combination, try execute it */
2648 int irc = HandleHostKey(&event.key);
2649 if (irc == VINF_SUCCESS)
2650 {
2651 enmHKeyState = HKEYSTATE_USED;
2652 break;
2653 }
2654 if (RT_SUCCESS(irc))
2655 goto leave;
2656 }
2657 else /* SDL_KEYUP */
2658 {
2659 if ( ksym != SDLK_UNKNOWN
2660 && (ksym == gHostKeySym1 || ksym == gHostKeySym2))
2661 {
2662 /* toggle grabbing state */
2663 if (!gfGrabbed)
2664 InputGrabStart();
2665 else
2666 InputGrabEnd();
2667
2668 /* SDL doesn't always reset the keystates, correct it */
2669 ResetKeys();
2670 enmHKeyState = HKEYSTATE_NORMAL;
2671 break;
2672 }
2673 }
2674
2675 /* not host key */
2676 enmHKeyState = HKEYSTATE_NOT_IT;
2677 ProcessKey(&EvHKeyDown1.key);
2678 /* see the comment for the 2-key case above */
2679 RTThreadSleep(10);
2680 if (gHostKeySym2 != SDLK_UNKNOWN)
2681 {
2682 ProcessKey(&EvHKeyDown2.key);
2683 /* see the comment for the 2-key case above */
2684 RTThreadSleep(10);
2685 }
2686 ProcessKey(&event.key);
2687 break;
2688 }
2689
2690 case HKEYSTATE_USED:
2691 {
2692 if ((SDL_GetModState() & ~(KMOD_MODE | KMOD_NUM | KMOD_RESERVED)) == 0)
2693 enmHKeyState = HKEYSTATE_NORMAL;
2694 if (event.type == SDL_KEYDOWN)
2695 {
2696 int irc = HandleHostKey(&event.key);
2697 if (RT_SUCCESS(irc) && irc != VINF_SUCCESS)
2698 goto leave;
2699 }
2700 break;
2701 }
2702
2703 default:
2704 AssertMsgFailed(("enmHKeyState=%d\n", enmHKeyState));
2705 RT_FALL_THRU();
2706 case HKEYSTATE_NOT_IT:
2707 {
2708 if ((SDL_GetModState() & ~(KMOD_MODE | KMOD_NUM | KMOD_RESERVED)) == 0)
2709 enmHKeyState = HKEYSTATE_NORMAL;
2710 ProcessKey(&event.key);
2711 break;
2712 }
2713 } /* state switch */
2714 break;
2715 }
2716
2717 /*
2718 * The window was closed.
2719 */
2720 case SDL_QUIT:
2721 {
2722 if (!gfACPITerm || gSdlQuitTimer)
2723 goto leave;
2724 if (gpConsole)
2725 gpConsole->PowerButton();
2726 gSdlQuitTimer = SDL_AddTimer(1000, QuitTimer, NULL);
2727 break;
2728 }
2729
2730 /*
2731 * The mouse has moved
2732 */
2733 case SDL_MOUSEMOTION:
2734 {
2735 if (gfGrabbed || UseAbsoluteMouse())
2736 {
2737 VBoxSDLFB *fb;
2738#ifdef VBOX_WITH_SDL2
2739 fb = getFbFromWinId(event.motion.windowID);
2740#else
2741 fb = gpFramebuffer[0];
2742#endif
2743 AssertPtrBreak(fb);
2744 SendMouseEvent(fb, 0, 0, 0);
2745 }
2746 break;
2747 }
2748
2749 /*
2750 * A mouse button has been clicked or released.
2751 */
2752 case SDL_MOUSEBUTTONDOWN:
2753 case SDL_MOUSEBUTTONUP:
2754 {
2755 SDL_MouseButtonEvent *bev = &event.button;
2756 /* don't grab on mouse click if we have guest additions */
2757 if (!gfGrabbed && !UseAbsoluteMouse() && gfGrabOnMouseClick)
2758 {
2759 if (event.type == SDL_MOUSEBUTTONDOWN && (bev->state & SDL_BUTTON_LMASK))
2760 {
2761 /* start grabbing all events */
2762 InputGrabStart();
2763 }
2764 }
2765 else if (gfGrabbed || UseAbsoluteMouse())
2766 {
2767#ifdef VBOX_WITH_SDL2
2768 int dz = 0; /** @todo Implement mouse wheel support with SDL2 (event SDL_MOUSEWHEEL). */
2769#else
2770 int dz = bev->button == SDL_BUTTON_WHEELUP
2771 ? -1
2772 : bev->button == SDL_BUTTON_WHEELDOWN
2773 ? +1
2774 : 0;
2775#endif
2776 /* end host key combination (CTRL+MouseButton) */
2777 switch (enmHKeyState)
2778 {
2779 case HKEYSTATE_DOWN_1ST:
2780 case HKEYSTATE_DOWN_2ND:
2781 enmHKeyState = HKEYSTATE_NOT_IT;
2782 ProcessKey(&EvHKeyDown1.key);
2783 /* ugly hack: small delay to ensure that the key event is
2784 * actually handled _prior_ to the mouse click event */
2785 RTThreadSleep(20);
2786 break;
2787 case HKEYSTATE_DOWN:
2788 enmHKeyState = HKEYSTATE_NOT_IT;
2789 ProcessKey(&EvHKeyDown1.key);
2790 if (gHostKeySym2 != SDLK_UNKNOWN)
2791 ProcessKey(&EvHKeyDown2.key);
2792 /* ugly hack: small delay to ensure that the key event is
2793 * actually handled _prior_ to the mouse click event */
2794 RTThreadSleep(20);
2795 break;
2796 default:
2797 break;
2798 }
2799
2800 VBoxSDLFB *fb;
2801#ifdef VBOX_WITH_SDL2
2802 fb = getFbFromWinId(event.button.windowID);
2803#else
2804 fb = gpFramebuffer[0];
2805#endif
2806 AssertPtrBreak(fb);
2807 SendMouseEvent(fb, dz, event.type == SDL_MOUSEBUTTONDOWN, bev->button);
2808 }
2809 break;
2810 }
2811
2812#ifndef VBOX_WITH_SDL2
2813 /*
2814 * The window has gained or lost focus.
2815 */
2816 case SDL_ACTIVEEVENT: /** @todo Needs to be also fixed with SDL2? Check! */
2817 {
2818 /*
2819 * There is a strange behaviour in SDL when running without a window
2820 * manager: When SDL_WM_GrabInput(SDL_GRAB_ON) is called we receive two
2821 * consecutive events SDL_ACTIVEEVENTs (input lost, input gained).
2822 * Asking SDL_GetAppState() seems the better choice.
2823 */
2824 if (gfGrabbed && (SDL_GetAppState() & SDL_APPINPUTFOCUS) == 0)
2825 {
2826 /*
2827 * another window has stolen the (keyboard) input focus
2828 */
2829 InputGrabEnd();
2830 }
2831 break;
2832 }
2833
2834 /*
2835 * The SDL window was resized.
2836 * For SDL2 this is done in SDL_WINDOWEVENT.
2837 */
2838 case SDL_VIDEORESIZE:
2839 {
2840 if (gpDisplay)
2841 {
2842 if (gfIgnoreNextResize)
2843 {
2844 gfIgnoreNextResize = FALSE;
2845 break;
2846 }
2847 uResizeWidth = event.resize.w;
2848#ifdef VBOX_SECURELABEL
2849 if (fSecureLabel)
2850 uResizeHeight = RT_MAX(0, event.resize.h - SECURE_LABEL_HEIGHT);
2851 else
2852#endif
2853 uResizeHeight = event.resize.h;
2854 if (gSdlResizeTimer)
2855 SDL_RemoveTimer(gSdlResizeTimer);
2856 gSdlResizeTimer = SDL_AddTimer(300, ResizeTimer, NULL);
2857 }
2858 break;
2859 }
2860#endif
2861
2862 /*
2863 * User specific update event.
2864 */
2865 /** @todo use a common user event handler so that SDL_PeepEvents() won't
2866 * possibly remove other events in the queue!
2867 */
2868 case SDL_USER_EVENT_UPDATERECT:
2869 {
2870 /*
2871 * Decode event parameters.
2872 */
2873 ASMAtomicDecS32(&g_cNotifyUpdateEventsPending);
2874 #define DECODEX(event) (int)((intptr_t)(event).user.data1 >> 16)
2875 #define DECODEY(event) (int)((intptr_t)(event).user.data1 & 0xFFFF)
2876 #define DECODEW(event) (int)((intptr_t)(event).user.data2 >> 16)
2877 #define DECODEH(event) (int)((intptr_t)(event).user.data2 & 0xFFFF)
2878 int x = DECODEX(event);
2879 int y = DECODEY(event);
2880 int w = DECODEW(event);
2881 int h = DECODEH(event);
2882 LogFlow(("SDL_USER_EVENT_UPDATERECT: x = %d, y = %d, w = %d, h = %d\n",
2883 x, y, w, h));
2884
2885 Assert(gpFramebuffer[event.user.code]);
2886 gpFramebuffer[event.user.code]->update(x, y, w, h, true /* fGuestRelative */);
2887
2888 #undef DECODEX
2889 #undef DECODEY
2890 #undef DECODEW
2891 #undef DECODEH
2892 break;
2893 }
2894
2895 /*
2896 * User event: Window resize done
2897 */
2898 case SDL_USER_EVENT_WINDOW_RESIZE_DONE:
2899 {
2900 /**
2901 * @todo This is a workaround for synchronization problems between EMT and the
2902 * SDL main thread. It can happen that the SDL thread already starts a
2903 * new resize operation while the EMT is still busy with the old one
2904 * leading to a deadlock. Therefore we call SetVideoModeHint only once
2905 * when the mouse button was released.
2906 */
2907 /* communicate the resize event to the guest */
2908 gpDisplay->SetVideoModeHint(0 /*=display*/, true /*=enabled*/, false /*=changeOrigin*/,
2909 0 /*=originX*/, 0 /*=originY*/,
2910 uResizeWidth, uResizeHeight, 0 /*=don't change bpp*/, true /*=notify*/);
2911 break;
2912
2913 }
2914
2915 /*
2916 * User specific framebuffer change event.
2917 */
2918 case SDL_USER_EVENT_NOTIFYCHANGE:
2919 {
2920 LogFlow(("SDL_USER_EVENT_NOTIFYCHANGE\n"));
2921 LONG xOrigin, yOrigin;
2922 gpFramebuffer[event.user.code]->notifyChange(event.user.code);
2923 /* update xOrigin, yOrigin -> mouse */
2924 ULONG dummy;
2925 GuestMonitorStatus_T monitorStatus;
2926 rc = gpDisplay->GetScreenResolution(event.user.code, &dummy, &dummy, &dummy, &xOrigin, &yOrigin, &monitorStatus);
2927 gpFramebuffer[event.user.code]->setOrigin(xOrigin, yOrigin);
2928 break;
2929 }
2930
2931#ifdef USE_XPCOM_QUEUE_THREAD
2932 /*
2933 * User specific XPCOM event queue event
2934 */
2935 case SDL_USER_EVENT_XPCOM_EVENTQUEUE:
2936 {
2937 LogFlow(("SDL_USER_EVENT_XPCOM_EVENTQUEUE: processing XPCOM event queue...\n"));
2938 eventQ->processEventQueue(0);
2939 signalXPCOMEventQueueThread();
2940 break;
2941 }
2942#endif /* USE_XPCOM_QUEUE_THREAD */
2943
2944 /*
2945 * User specific update title bar notification event
2946 */
2947 case SDL_USER_EVENT_UPDATE_TITLEBAR:
2948 {
2949 UpdateTitlebar(TITLEBAR_NORMAL);
2950 break;
2951 }
2952
2953 /*
2954 * User specific termination event
2955 */
2956 case SDL_USER_EVENT_TERMINATE:
2957 {
2958 if (event.user.code != VBOXSDL_TERM_NORMAL)
2959 RTPrintf("Error: VM terminated abnormally!\n");
2960 goto leave;
2961 }
2962
2963#ifdef VBOX_SECURELABEL
2964 /*
2965 * User specific secure label update event
2966 */
2967 case SDL_USER_EVENT_SECURELABEL_UPDATE:
2968 {
2969 /*
2970 * Query the new label text
2971 */
2972 Bstr bstrLabel;
2973 gpMachine->GetExtraData(Bstr(VBOXSDL_SECURELABEL_EXTRADATA).raw(), bstrLabel.asOutParam());
2974 Utf8Str labelUtf8(bstrLabel);
2975 /*
2976 * Now update the label
2977 */
2978 gpFramebuffer[0]->setSecureLabelText(labelUtf8.c_str());
2979 break;
2980 }
2981#endif /* VBOX_SECURELABEL */
2982
2983 /*
2984 * User specific pointer shape change event
2985 */
2986 case SDL_USER_EVENT_POINTER_CHANGE:
2987 {
2988 PointerShapeChangeData *data = (PointerShapeChangeData *)event.user.data1;
2989 SetPointerShape (data);
2990 delete data;
2991 break;
2992 }
2993
2994 /*
2995 * User specific guest capabilities changed
2996 */
2997 case SDL_USER_EVENT_GUEST_CAP_CHANGED:
2998 {
2999 HandleGuestCapsChanged();
3000 break;
3001 }
3002
3003 default:
3004 {
3005 Log8(("unknown SDL event %d\n", event.type));
3006 break;
3007 }
3008 }
3009 }
3010
3011leave:
3012 if (gpszPidFile)
3013 RTFileDelete(gpszPidFile);
3014
3015 LogFlow(("leaving...\n"));
3016#if defined(VBOX_WITH_XPCOM) && !defined(RT_OS_DARWIN) && !defined(RT_OS_OS2)
3017 /* make sure the XPCOM event queue thread doesn't do anything harmful */
3018 terminateXPCOMQueueThread();
3019#endif /* VBOX_WITH_XPCOM */
3020
3021 if (gpVRDEServer)
3022 rc = gpVRDEServer->COMSETTER(Enabled)(FALSE);
3023
3024 /*
3025 * Get the machine state.
3026 */
3027 if (gpMachine)
3028 gpMachine->COMGETTER(State)(&machineState);
3029 else
3030 machineState = MachineState_Aborted;
3031
3032 if (!fSeparate)
3033 {
3034 /*
3035 * Turn off the VM if it's running
3036 */
3037 if ( gpConsole
3038 && ( machineState == MachineState_Running
3039 || machineState == MachineState_Teleporting
3040 || machineState == MachineState_LiveSnapshotting
3041 /** @todo power off paused VMs too? */
3042 )
3043 )
3044 do
3045 {
3046 pConsoleListener->getWrapped()->ignorePowerOffEvents(true);
3047 ComPtr<IProgress> pProgress;
3048 CHECK_ERROR_BREAK(gpConsole, PowerDown(pProgress.asOutParam()));
3049 CHECK_ERROR_BREAK(pProgress, WaitForCompletion(-1));
3050 BOOL completed;
3051 CHECK_ERROR_BREAK(pProgress, COMGETTER(Completed)(&completed));
3052 ASSERT(completed);
3053 LONG hrc;
3054 CHECK_ERROR_BREAK(pProgress, COMGETTER(ResultCode)(&hrc));
3055 if (FAILED(hrc))
3056 {
3057 com::ErrorInfo info;
3058 if (info.isFullAvailable())
3059 PrintError("Failed to power down VM",
3060 info.getText().raw(), info.getComponent().raw());
3061 else
3062 RTPrintf("Failed to power down virtual machine! No error information available (rc = 0x%x).\n", hrc);
3063 break;
3064 }
3065 } while (0);
3066 }
3067
3068 /* unregister Console listener */
3069 if (pConsoleListener)
3070 {
3071 ComPtr<IEventSource> pES;
3072 CHECK_ERROR(gpConsole, COMGETTER(EventSource)(pES.asOutParam()));
3073 if (!pES.isNull())
3074 CHECK_ERROR(pES, UnregisterListener(pConsoleListener));
3075 pConsoleListener.setNull();
3076 }
3077
3078 /*
3079 * Now we discard all settings so that our changes will
3080 * not be flushed to the permanent configuration
3081 */
3082 if ( gpMachine
3083 && machineState != MachineState_Saved
3084 && machineState != MachineState_AbortedSaved)
3085 {
3086 rc = gpMachine->DiscardSettings();
3087 AssertMsg(SUCCEEDED(rc), ("DiscardSettings %Rhrc, machineState %d\n", rc, machineState));
3088 }
3089
3090 /* close the session */
3091 if (sessionOpened)
3092 {
3093 rc = pSession->UnlockMachine();
3094 AssertComRC(rc);
3095 }
3096
3097#ifndef VBOX_WITH_SDL2
3098 /* restore the default cursor and free the custom one if any */
3099 if (gpDefaultCursor)
3100 {
3101# ifdef VBOXSDL_WITH_X11
3102 Cursor pDefaultTempX11Cursor = 0;
3103 if (gfXCursorEnabled)
3104 {
3105 pDefaultTempX11Cursor = *(Cursor*)gpDefaultCursor->wm_cursor;
3106 *(Cursor*)gpDefaultCursor->wm_cursor = gpDefaultOrigX11Cursor;
3107 }
3108# endif /* VBOXSDL_WITH_X11 */
3109 SDL_SetCursor(gpDefaultCursor);
3110# if defined(VBOXSDL_WITH_X11) && !defined(VBOX_WITHOUT_XCURSOR)
3111 if (gfXCursorEnabled)
3112 XFreeCursor(gSdlInfo.info.x11.display, pDefaultTempX11Cursor);
3113# endif /* VBOXSDL_WITH_X11 && !VBOX_WITHOUT_XCURSOR */
3114 }
3115
3116 if (gpCustomCursor)
3117 {
3118 WMcursor *pCustomTempWMCursor = gpCustomCursor->wm_cursor;
3119 gpCustomCursor->wm_cursor = gpCustomOrigWMcursor;
3120 SDL_FreeCursor(gpCustomCursor);
3121 if (pCustomTempWMCursor)
3122 {
3123# if defined(RT_OS_WINDOWS)
3124 ::DestroyCursor(*(HCURSOR *)pCustomTempWMCursor);
3125# elif defined(VBOXSDL_WITH_X11) && !defined(VBOX_WITHOUT_XCURSOR)
3126 if (gfXCursorEnabled)
3127 XFreeCursor(gSdlInfo.info.x11.display, *(Cursor *)pCustomTempWMCursor);
3128# endif /* VBOXSDL_WITH_X11 && !VBOX_WITHOUT_XCURSOR */
3129 free(pCustomTempWMCursor);
3130 }
3131 }
3132#endif
3133
3134 LogFlow(("Releasing mouse, keyboard, remote desktop server, display, console...\n"));
3135 if (gpDisplay)
3136 {
3137 for (unsigned i = 0; i < gcMonitors; i++)
3138 gpDisplay->DetachFramebuffer(i, gaFramebufferId[i].raw());
3139 }
3140
3141 gpMouse = NULL;
3142 gpKeyboard = NULL;
3143 gpVRDEServer = NULL;
3144 gpDisplay = NULL;
3145 gpConsole = NULL;
3146 gpMachineDebugger = NULL;
3147 gpProgress = NULL;
3148 // we can only uninitialize SDL here because it is not threadsafe
3149
3150 for (unsigned i = 0; i < gcMonitors; i++)
3151 {
3152 if (gpFramebuffer[i])
3153 {
3154 LogFlow(("Releasing framebuffer...\n"));
3155 gpFramebuffer[i]->Release();
3156 gpFramebuffer[i] = NULL;
3157 }
3158 }
3159
3160 VBoxSDLFB::uninit();
3161
3162#ifdef VBOX_SECURELABEL
3163 /* must do this after destructing the framebuffer */
3164 if (gLibrarySDL_ttf)
3165 RTLdrClose(gLibrarySDL_ttf);
3166#endif
3167
3168 /* VirtualBox (server) listener unregistration. */
3169 if (pVBoxListener)
3170 {
3171 ComPtr<IEventSource> pES;
3172 CHECK_ERROR(pVirtualBox, COMGETTER(EventSource)(pES.asOutParam()));
3173 if (!pES.isNull())
3174 CHECK_ERROR(pES, UnregisterListener(pVBoxListener));
3175 pVBoxListener.setNull();
3176 }
3177
3178 /* VirtualBoxClient listener unregistration. */
3179 if (pVBoxClientListener)
3180 {
3181 ComPtr<IEventSource> pES;
3182 CHECK_ERROR(pVirtualBoxClient, COMGETTER(EventSource)(pES.asOutParam()));
3183 if (!pES.isNull())
3184 CHECK_ERROR(pES, UnregisterListener(pVBoxClientListener));
3185 pVBoxClientListener.setNull();
3186 }
3187
3188 LogFlow(("Releasing machine, session...\n"));
3189 gpMachine = NULL;
3190 pSession = NULL;
3191 LogFlow(("Releasing VirtualBox object...\n"));
3192 pVirtualBox = NULL;
3193 LogFlow(("Releasing VirtualBoxClient object...\n"));
3194 pVirtualBoxClient = NULL;
3195
3196 // end "all-stuff" scope
3197 ////////////////////////////////////////////////////////////////////////////
3198 }
3199
3200 /* Must be before com::Shutdown() */
3201 LogFlow(("Uninitializing COM...\n"));
3202 com::Shutdown();
3203
3204 LogFlow(("Returning from main()!\n"));
3205 RTLogFlush(NULL);
3206 return FAILED(rc) ? 1 : 0;
3207}
3208
3209#ifndef VBOX_WITH_HARDENING
3210/**
3211 * Main entry point
3212 */
3213int main(int argc, char **argv)
3214{
3215#ifdef Q_WS_X11
3216 if (!XInitThreads())
3217 return 1;
3218#endif
3219 /*
3220 * Before we do *anything*, we initialize the runtime.
3221 */
3222 int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB);
3223 if (RT_FAILURE(rc))
3224 return RTMsgInitFailure(rc);
3225 return TrustedMain(argc, argv, NULL);
3226}
3227#endif /* !VBOX_WITH_HARDENING */
3228
3229
3230/**
3231 * Returns whether the absolute mouse is in use, i.e. both host
3232 * and guest have opted to enable it.
3233 *
3234 * @returns bool Flag whether the absolute mouse is in use
3235 */
3236static bool UseAbsoluteMouse(void)
3237{
3238 return (gfAbsoluteMouseHost && gfAbsoluteMouseGuest);
3239}
3240
3241#if defined(RT_OS_DARWIN) || defined(RT_OS_OS2)
3242/**
3243 * Fallback keycode conversion using SDL symbols.
3244 *
3245 * This is used to catch keycodes that's missing from the translation table.
3246 *
3247 * @returns XT scancode
3248 * @param ev SDL scancode
3249 */
3250static uint16_t Keyevent2KeycodeFallback(const SDL_KeyboardEvent *ev)
3251{
3252 const SDLKey sym = ev->keysym.sym;
3253 Log(("SDL key event: sym=%d scancode=%#x unicode=%#x\n",
3254 sym, ev->keysym.scancode, ev->keysym.unicode));
3255 switch (sym)
3256 { /* set 1 scan code */
3257 case SDLK_ESCAPE: return 0x01;
3258 case SDLK_EXCLAIM:
3259 case SDLK_1: return 0x02;
3260 case SDLK_AT:
3261 case SDLK_2: return 0x03;
3262 case SDLK_HASH:
3263 case SDLK_3: return 0x04;
3264 case SDLK_DOLLAR:
3265 case SDLK_4: return 0x05;
3266 /* % */
3267 case SDLK_5: return 0x06;
3268 case SDLK_CARET:
3269 case SDLK_6: return 0x07;
3270 case SDLK_AMPERSAND:
3271 case SDLK_7: return 0x08;
3272 case SDLK_ASTERISK:
3273 case SDLK_8: return 0x09;
3274 case SDLK_LEFTPAREN:
3275 case SDLK_9: return 0x0a;
3276 case SDLK_RIGHTPAREN:
3277 case SDLK_0: return 0x0b;
3278 case SDLK_UNDERSCORE:
3279 case SDLK_MINUS: return 0x0c;
3280 case SDLK_EQUALS:
3281 case SDLK_PLUS: return 0x0d;
3282 case SDLK_BACKSPACE: return 0x0e;
3283 case SDLK_TAB: return 0x0f;
3284 case SDLK_q: return 0x10;
3285 case SDLK_w: return 0x11;
3286 case SDLK_e: return 0x12;
3287 case SDLK_r: return 0x13;
3288 case SDLK_t: return 0x14;
3289 case SDLK_y: return 0x15;
3290 case SDLK_u: return 0x16;
3291 case SDLK_i: return 0x17;
3292 case SDLK_o: return 0x18;
3293 case SDLK_p: return 0x19;
3294 case SDLK_LEFTBRACKET: return 0x1a;
3295 case SDLK_RIGHTBRACKET: return 0x1b;
3296 case SDLK_RETURN: return 0x1c;
3297 case SDLK_KP_ENTER: return 0x1c | 0x100;
3298 case SDLK_LCTRL: return 0x1d;
3299 case SDLK_RCTRL: return 0x1d | 0x100;
3300 case SDLK_a: return 0x1e;
3301 case SDLK_s: return 0x1f;
3302 case SDLK_d: return 0x20;
3303 case SDLK_f: return 0x21;
3304 case SDLK_g: return 0x22;
3305 case SDLK_h: return 0x23;
3306 case SDLK_j: return 0x24;
3307 case SDLK_k: return 0x25;
3308 case SDLK_l: return 0x26;
3309 case SDLK_COLON:
3310 case SDLK_SEMICOLON: return 0x27;
3311 case SDLK_QUOTEDBL:
3312 case SDLK_QUOTE: return 0x28;
3313 case SDLK_BACKQUOTE: return 0x29;
3314 case SDLK_LSHIFT: return 0x2a;
3315 case SDLK_BACKSLASH: return 0x2b;
3316 case SDLK_z: return 0x2c;
3317 case SDLK_x: return 0x2d;
3318 case SDLK_c: return 0x2e;
3319 case SDLK_v: return 0x2f;
3320 case SDLK_b: return 0x30;
3321 case SDLK_n: return 0x31;
3322 case SDLK_m: return 0x32;
3323 case SDLK_LESS:
3324 case SDLK_COMMA: return 0x33;
3325 case SDLK_GREATER:
3326 case SDLK_PERIOD: return 0x34;
3327 case SDLK_KP_DIVIDE: /*??*/
3328 case SDLK_QUESTION:
3329 case SDLK_SLASH: return 0x35;
3330 case SDLK_RSHIFT: return 0x36;
3331 case SDLK_KP_MULTIPLY:
3332 case SDLK_PRINT: return 0x37; /* fixme */
3333 case SDLK_LALT: return 0x38;
3334 case SDLK_MODE: /* alt gr*/
3335 case SDLK_RALT: return 0x38 | 0x100;
3336 case SDLK_SPACE: return 0x39;
3337 case SDLK_CAPSLOCK: return 0x3a;
3338 case SDLK_F1: return 0x3b;
3339 case SDLK_F2: return 0x3c;
3340 case SDLK_F3: return 0x3d;
3341 case SDLK_F4: return 0x3e;
3342 case SDLK_F5: return 0x3f;
3343 case SDLK_F6: return 0x40;
3344 case SDLK_F7: return 0x41;
3345 case SDLK_F8: return 0x42;
3346 case SDLK_F9: return 0x43;
3347 case SDLK_F10: return 0x44;
3348 case SDLK_PAUSE: return 0x45; /* not right */
3349 case SDLK_NUMLOCK: return 0x45;
3350 case SDLK_SCROLLOCK: return 0x46;
3351 case SDLK_KP7: return 0x47;
3352 case SDLK_HOME: return 0x47 | 0x100;
3353 case SDLK_KP8: return 0x48;
3354 case SDLK_UP: return 0x48 | 0x100;
3355 case SDLK_KP9: return 0x49;
3356 case SDLK_PAGEUP: return 0x49 | 0x100;
3357 case SDLK_KP_MINUS: return 0x4a;
3358 case SDLK_KP4: return 0x4b;
3359 case SDLK_LEFT: return 0x4b | 0x100;
3360 case SDLK_KP5: return 0x4c;
3361 case SDLK_KP6: return 0x4d;
3362 case SDLK_RIGHT: return 0x4d | 0x100;
3363 case SDLK_KP_PLUS: return 0x4e;
3364 case SDLK_KP1: return 0x4f;
3365 case SDLK_END: return 0x4f | 0x100;
3366 case SDLK_KP2: return 0x50;
3367 case SDLK_DOWN: return 0x50 | 0x100;
3368 case SDLK_KP3: return 0x51;
3369 case SDLK_PAGEDOWN: return 0x51 | 0x100;
3370 case SDLK_KP0: return 0x52;
3371 case SDLK_INSERT: return 0x52 | 0x100;
3372 case SDLK_KP_PERIOD: return 0x53;
3373 case SDLK_DELETE: return 0x53 | 0x100;
3374 case SDLK_SYSREQ: return 0x54;
3375 case SDLK_F11: return 0x57;
3376 case SDLK_F12: return 0x58;
3377 case SDLK_F13: return 0x5b;
3378 case SDLK_LMETA:
3379 case SDLK_LSUPER: return 0x5b | 0x100;
3380 case SDLK_F14: return 0x5c;
3381 case SDLK_RMETA:
3382 case SDLK_RSUPER: return 0x5c | 0x100;
3383 case SDLK_F15: return 0x5d;
3384 case SDLK_MENU: return 0x5d | 0x100;
3385#if 0
3386 case SDLK_CLEAR: return 0x;
3387 case SDLK_KP_EQUALS: return 0x;
3388 case SDLK_COMPOSE: return 0x;
3389 case SDLK_HELP: return 0x;
3390 case SDLK_BREAK: return 0x;
3391 case SDLK_POWER: return 0x;
3392 case SDLK_EURO: return 0x;
3393 case SDLK_UNDO: return 0x;
3394#endif
3395 default:
3396 Log(("Unhandled sdl key event: sym=%d scancode=%#x unicode=%#x\n",
3397 ev->keysym.sym, ev->keysym.scancode, ev->keysym.unicode));
3398 return 0;
3399 }
3400}
3401#endif /* RT_OS_DARWIN */
3402
3403/**
3404 * Converts an SDL keyboard eventcode to a XT scancode.
3405 *
3406 * @returns XT scancode
3407 * @param ev SDL scancode
3408 */
3409static uint16_t Keyevent2Keycode(const SDL_KeyboardEvent *ev)
3410{
3411 // start with the scancode determined by SDL
3412 int keycode = ev->keysym.scancode;
3413
3414#ifdef VBOXSDL_WITH_X11
3415# ifdef VBOX_WITH_SDL2
3416
3417 switch (ev->keysym.sym)
3418 {
3419 case SDLK_ESCAPE: return 0x01;
3420 case SDLK_EXCLAIM:
3421 case SDLK_1: return 0x02;
3422 case SDLK_AT:
3423 case SDLK_2: return 0x03;
3424 case SDLK_HASH:
3425 case SDLK_3: return 0x04;
3426 case SDLK_DOLLAR:
3427 case SDLK_4: return 0x05;
3428 /* % */
3429 case SDLK_5: return 0x06;
3430 case SDLK_CARET:
3431 case SDLK_6: return 0x07;
3432 case SDLK_AMPERSAND:
3433 case SDLK_7: return 0x08;
3434 case SDLK_ASTERISK:
3435 case SDLK_8: return 0x09;
3436 case SDLK_LEFTPAREN:
3437 case SDLK_9: return 0x0a;
3438 case SDLK_RIGHTPAREN:
3439 case SDLK_0: return 0x0b;
3440 case SDLK_UNDERSCORE:
3441 case SDLK_MINUS: return 0x0c;
3442 case SDLK_PLUS: return 0x0d;
3443 case SDLK_BACKSPACE: return 0x0e;
3444 case SDLK_TAB: return 0x0f;
3445 case SDLK_q: return 0x10;
3446 case SDLK_w: return 0x11;
3447 case SDLK_e: return 0x12;
3448 case SDLK_r: return 0x13;
3449 case SDLK_t: return 0x14;
3450 case SDLK_y: return 0x15;
3451 case SDLK_u: return 0x16;
3452 case SDLK_i: return 0x17;
3453 case SDLK_o: return 0x18;
3454 case SDLK_p: return 0x19;
3455 case SDLK_RETURN: return 0x1c;
3456 case SDLK_KP_ENTER: return 0x1c | 0x100;
3457 case SDLK_LCTRL: return 0x1d;
3458 case SDLK_RCTRL: return 0x1d | 0x100;
3459 case SDLK_a: return 0x1e;
3460 case SDLK_s: return 0x1f;
3461 case SDLK_d: return 0x20;
3462 case SDLK_f: return 0x21;
3463 case SDLK_g: return 0x22;
3464 case SDLK_h: return 0x23;
3465 case SDLK_j: return 0x24;
3466 case SDLK_k: return 0x25;
3467 case SDLK_l: return 0x26;
3468 case SDLK_COLON: return 0x27;
3469 case SDLK_QUOTEDBL:
3470 case SDLK_QUOTE: return 0x28;
3471 case SDLK_BACKQUOTE: return 0x29;
3472 case SDLK_LSHIFT: return 0x2a;
3473 case SDLK_z: return 0x2c;
3474 case SDLK_x: return 0x2d;
3475 case SDLK_c: return 0x2e;
3476 case SDLK_v: return 0x2f;
3477 case SDLK_b: return 0x30;
3478 case SDLK_n: return 0x31;
3479 case SDLK_m: return 0x32;
3480 case SDLK_LESS: return 0x33;
3481 case SDLK_GREATER: return 0x34;
3482 case SDLK_KP_DIVIDE: /*??*/
3483 case SDLK_QUESTION: return 0x35;
3484 case SDLK_RSHIFT: return 0x36;
3485 case SDLK_KP_MULTIPLY:
3486 case SDLK_PRINT: return 0x37; /* fixme */
3487 case SDLK_LALT: return 0x38;
3488 case SDLK_MODE: /* alt gr*/
3489 case SDLK_RALT: return 0x38 | 0x100;
3490 case SDLK_SPACE: return 0x39;
3491 case SDLK_CAPSLOCK: return 0x3a;
3492 case SDLK_F1: return 0x3b;
3493 case SDLK_F2: return 0x3c;
3494 case SDLK_F3: return 0x3d;
3495 case SDLK_F4: return 0x3e;
3496 case SDLK_F5: return 0x3f;
3497 case SDLK_F6: return 0x40;
3498 case SDLK_F7: return 0x41;
3499 case SDLK_F8: return 0x42;
3500 case SDLK_F9: return 0x43;
3501 case SDLK_F10: return 0x44;
3502 case SDLK_PAUSE: return 0x45; /* not right */
3503 case SDLK_NUMLOCK: return 0x45;
3504 case SDLK_SCROLLOCK: return 0x46;
3505 case SDLK_KP7: return 0x47;
3506 case SDLK_HOME: return 0x47 | 0x100;
3507 case SDLK_KP8: return 0x48;
3508 case SDLK_UP: return 0x48 | 0x100;
3509 case SDLK_KP9: return 0x49;
3510 case SDLK_PAGEUP: return 0x49 | 0x100;
3511 case SDLK_KP_MINUS: return 0x4a;
3512 case SDLK_KP4: return 0x4b;
3513 case SDLK_LEFT: return 0x4b | 0x100;
3514 case SDLK_KP5: return 0x4c;
3515 case SDLK_KP6: return 0x4d;
3516 case SDLK_RIGHT: return 0x4d | 0x100;
3517 case SDLK_KP_PLUS: return 0x4e;
3518 case SDLK_KP1: return 0x4f;
3519 case SDLK_END: return 0x4f | 0x100;
3520 case SDLK_KP2: return 0x50;
3521 case SDLK_DOWN: return 0x50 | 0x100;
3522 case SDLK_KP3: return 0x51;
3523 case SDLK_PAGEDOWN: return 0x51 | 0x100;
3524 case SDLK_KP0: return 0x52;
3525 case SDLK_INSERT: return 0x52 | 0x100;
3526 case SDLK_KP_PERIOD: return 0x53;
3527 case SDLK_DELETE: return 0x53 | 0x100;
3528 case SDLK_SYSREQ: return 0x54;
3529 case SDLK_F11: return 0x57;
3530 case SDLK_F12: return 0x58;
3531 case SDLK_F13: return 0x5b;
3532 case SDLK_F14: return 0x5c;
3533 case SDLK_F15: return 0x5d;
3534 case SDLK_MENU: return 0x5d | 0x100;
3535 default:
3536 return 0;
3537 }
3538# else
3539 keycode = X11DRV_KeyEvent(gSdlInfo.info.x11.display, keycode);
3540# endif
3541#elif defined(RT_OS_DARWIN)
3542 /* This is derived partially from SDL_QuartzKeys.h and partially from testing. */
3543 static const uint16_t s_aMacToSet1[] =
3544 {
3545 /* set-1 SDL_QuartzKeys.h */
3546 0x1e, /* QZ_a 0x00 */
3547 0x1f, /* QZ_s 0x01 */
3548 0x20, /* QZ_d 0x02 */
3549 0x21, /* QZ_f 0x03 */
3550 0x23, /* QZ_h 0x04 */
3551 0x22, /* QZ_g 0x05 */
3552 0x2c, /* QZ_z 0x06 */
3553 0x2d, /* QZ_x 0x07 */
3554 0x2e, /* QZ_c 0x08 */
3555 0x2f, /* QZ_v 0x09 */
3556 0x56, /* between lshift and z. 'INT 1'? */
3557 0x30, /* QZ_b 0x0B */
3558 0x10, /* QZ_q 0x0C */
3559 0x11, /* QZ_w 0x0D */
3560 0x12, /* QZ_e 0x0E */
3561 0x13, /* QZ_r 0x0F */
3562 0x15, /* QZ_y 0x10 */
3563 0x14, /* QZ_t 0x11 */
3564 0x02, /* QZ_1 0x12 */
3565 0x03, /* QZ_2 0x13 */
3566 0x04, /* QZ_3 0x14 */
3567 0x05, /* QZ_4 0x15 */
3568 0x07, /* QZ_6 0x16 */
3569 0x06, /* QZ_5 0x17 */
3570 0x0d, /* QZ_EQUALS 0x18 */
3571 0x0a, /* QZ_9 0x19 */
3572 0x08, /* QZ_7 0x1A */
3573 0x0c, /* QZ_MINUS 0x1B */
3574 0x09, /* QZ_8 0x1C */
3575 0x0b, /* QZ_0 0x1D */
3576 0x1b, /* QZ_RIGHTBRACKET 0x1E */
3577 0x18, /* QZ_o 0x1F */
3578 0x16, /* QZ_u 0x20 */
3579 0x1a, /* QZ_LEFTBRACKET 0x21 */
3580 0x17, /* QZ_i 0x22 */
3581 0x19, /* QZ_p 0x23 */
3582 0x1c, /* QZ_RETURN 0x24 */
3583 0x26, /* QZ_l 0x25 */
3584 0x24, /* QZ_j 0x26 */
3585 0x28, /* QZ_QUOTE 0x27 */
3586 0x25, /* QZ_k 0x28 */
3587 0x27, /* QZ_SEMICOLON 0x29 */
3588 0x2b, /* QZ_BACKSLASH 0x2A */
3589 0x33, /* QZ_COMMA 0x2B */
3590 0x35, /* QZ_SLASH 0x2C */
3591 0x31, /* QZ_n 0x2D */
3592 0x32, /* QZ_m 0x2E */
3593 0x34, /* QZ_PERIOD 0x2F */
3594 0x0f, /* QZ_TAB 0x30 */
3595 0x39, /* QZ_SPACE 0x31 */
3596 0x29, /* QZ_BACKQUOTE 0x32 */
3597 0x0e, /* QZ_BACKSPACE 0x33 */
3598 0x9c, /* QZ_IBOOK_ENTER 0x34 */
3599 0x01, /* QZ_ESCAPE 0x35 */
3600 0x5c|0x100, /* QZ_RMETA 0x36 */
3601 0x5b|0x100, /* QZ_LMETA 0x37 */
3602 0x2a, /* QZ_LSHIFT 0x38 */
3603 0x3a, /* QZ_CAPSLOCK 0x39 */
3604 0x38, /* QZ_LALT 0x3A */
3605 0x1d, /* QZ_LCTRL 0x3B */
3606 0x36, /* QZ_RSHIFT 0x3C */
3607 0x38|0x100, /* QZ_RALT 0x3D */
3608 0x1d|0x100, /* QZ_RCTRL 0x3E */
3609 0, /* */
3610 0, /* */
3611 0x53, /* QZ_KP_PERIOD 0x41 */
3612 0, /* */
3613 0x37, /* QZ_KP_MULTIPLY 0x43 */
3614 0, /* */
3615 0x4e, /* QZ_KP_PLUS 0x45 */
3616 0, /* */
3617 0x45, /* QZ_NUMLOCK 0x47 */
3618 0, /* */
3619 0, /* */
3620 0, /* */
3621 0x35|0x100, /* QZ_KP_DIVIDE 0x4B */
3622 0x1c|0x100, /* QZ_KP_ENTER 0x4C */
3623 0, /* */
3624 0x4a, /* QZ_KP_MINUS 0x4E */
3625 0, /* */
3626 0, /* */
3627 0x0d/*?*/, /* QZ_KP_EQUALS 0x51 */
3628 0x52, /* QZ_KP0 0x52 */
3629 0x4f, /* QZ_KP1 0x53 */
3630 0x50, /* QZ_KP2 0x54 */
3631 0x51, /* QZ_KP3 0x55 */
3632 0x4b, /* QZ_KP4 0x56 */
3633 0x4c, /* QZ_KP5 0x57 */
3634 0x4d, /* QZ_KP6 0x58 */
3635 0x47, /* QZ_KP7 0x59 */
3636 0, /* */
3637 0x48, /* QZ_KP8 0x5B */
3638 0x49, /* QZ_KP9 0x5C */
3639 0, /* */
3640 0, /* */
3641 0, /* */
3642 0x3f, /* QZ_F5 0x60 */
3643 0x40, /* QZ_F6 0x61 */
3644 0x41, /* QZ_F7 0x62 */
3645 0x3d, /* QZ_F3 0x63 */
3646 0x42, /* QZ_F8 0x64 */
3647 0x43, /* QZ_F9 0x65 */
3648 0, /* */
3649 0x57, /* QZ_F11 0x67 */
3650 0, /* */
3651 0x37|0x100, /* QZ_PRINT / F13 0x69 */
3652 0x63, /* QZ_F16 0x6A */
3653 0x46, /* QZ_SCROLLOCK 0x6B */
3654 0, /* */
3655 0x44, /* QZ_F10 0x6D */
3656 0x5d|0x100, /* */
3657 0x58, /* QZ_F12 0x6F */
3658 0, /* */
3659 0/* 0xe1,0x1d,0x45*/, /* QZ_PAUSE 0x71 */
3660 0x52|0x100, /* QZ_INSERT / HELP 0x72 */
3661 0x47|0x100, /* QZ_HOME 0x73 */
3662 0x49|0x100, /* QZ_PAGEUP 0x74 */
3663 0x53|0x100, /* QZ_DELETE 0x75 */
3664 0x3e, /* QZ_F4 0x76 */
3665 0x4f|0x100, /* QZ_END 0x77 */
3666 0x3c, /* QZ_F2 0x78 */
3667 0x51|0x100, /* QZ_PAGEDOWN 0x79 */
3668 0x3b, /* QZ_F1 0x7A */
3669 0x4b|0x100, /* QZ_LEFT 0x7B */
3670 0x4d|0x100, /* QZ_RIGHT 0x7C */
3671 0x50|0x100, /* QZ_DOWN 0x7D */
3672 0x48|0x100, /* QZ_UP 0x7E */
3673 0x5e|0x100, /* QZ_POWER 0x7F */ /* have different break key! */
3674 };
3675
3676 if (keycode == 0)
3677 {
3678 /* This could be a modifier or it could be 'a'. */
3679 switch (ev->keysym.sym)
3680 {
3681 case SDLK_LSHIFT: keycode = 0x2a; break;
3682 case SDLK_RSHIFT: keycode = 0x36; break;
3683 case SDLK_LCTRL: keycode = 0x1d; break;
3684 case SDLK_RCTRL: keycode = 0x1d | 0x100; break;
3685 case SDLK_LALT: keycode = 0x38; break;
3686 case SDLK_MODE: /* alt gr */
3687 case SDLK_RALT: keycode = 0x38 | 0x100; break;
3688 case SDLK_RMETA:
3689 case SDLK_RSUPER: keycode = 0x5c | 0x100; break;
3690 case SDLK_LMETA:
3691 case SDLK_LSUPER: keycode = 0x5b | 0x100; break;
3692 /* Assumes normal key. */
3693 default: keycode = s_aMacToSet1[keycode]; break;
3694 }
3695 }
3696 else
3697 {
3698 if ((unsigned)keycode < RT_ELEMENTS(s_aMacToSet1))
3699 keycode = s_aMacToSet1[keycode];
3700 else
3701 keycode = 0;
3702 if (!keycode)
3703 {
3704# ifdef DEBUG_bird
3705 RTPrintf("Untranslated: keycode=%#x (%d)\n", keycode, keycode);
3706# endif
3707 keycode = Keyevent2KeycodeFallback(ev);
3708 }
3709 }
3710# ifdef DEBUG_bird
3711 RTPrintf("scancode=%#x -> %#x\n", ev->keysym.scancode, keycode);
3712# endif
3713
3714#elif defined(RT_OS_OS2)
3715 keycode = Keyevent2KeycodeFallback(ev);
3716#endif /* RT_OS_DARWIN */
3717 return keycode;
3718}
3719
3720/**
3721 * Releases any modifier keys that are currently in pressed state.
3722 */
3723static void ResetKeys(void)
3724{
3725 int i;
3726
3727 if (!gpKeyboard)
3728 return;
3729
3730 for(i = 0; i < 256; i++)
3731 {
3732 if (gaModifiersState[i])
3733 {
3734 if (i & 0x80)
3735 gpKeyboard->PutScancode(0xe0);
3736 gpKeyboard->PutScancode(i | 0x80);
3737 gaModifiersState[i] = 0;
3738 }
3739 }
3740}
3741
3742/**
3743 * Keyboard event handler.
3744 *
3745 * @param ev SDL keyboard event.
3746 */
3747static void ProcessKey(SDL_KeyboardEvent *ev)
3748{
3749#if (defined(DEBUG) || defined(VBOX_WITH_STATISTICS)) && !defined(VBOX_WITH_SDL2)
3750 if (gpMachineDebugger && ev->type == SDL_KEYDOWN)
3751 {
3752 // first handle the debugger hotkeys
3753 uint8_t *keystate = SDL_GetKeyState(NULL);
3754#if 0
3755 // CTRL+ALT+Fn is not free on Linux hosts with Xorg ..
3756 if (keystate[SDLK_LALT] && !keystate[SDLK_LCTRL])
3757#else
3758 if (keystate[SDLK_LALT] && keystate[SDLK_LCTRL])
3759#endif
3760 {
3761 switch (ev->keysym.sym)
3762 {
3763 // pressing CTRL+ALT+F11 dumps the statistics counter
3764 case SDLK_F12:
3765 RTPrintf("ResetStats\n"); /* Visual feedback in console window */
3766 gpMachineDebugger->ResetStats(NULL);
3767 break;
3768 // pressing CTRL+ALT+F12 resets all statistics counter
3769 case SDLK_F11:
3770 gpMachineDebugger->DumpStats(NULL);
3771 RTPrintf("DumpStats\n"); /* Vistual feedback in console window */
3772 break;
3773 default:
3774 break;
3775 }
3776 }
3777#if 1
3778 else if (keystate[SDLK_LALT] && !keystate[SDLK_LCTRL])
3779 {
3780 switch (ev->keysym.sym)
3781 {
3782 // pressing Alt-F12 toggles the supervisor recompiler
3783 case SDLK_F12:
3784 {
3785 BOOL recompileSupervisor;
3786 gpMachineDebugger->COMGETTER(RecompileSupervisor)(&recompileSupervisor);
3787 gpMachineDebugger->COMSETTER(RecompileSupervisor)(!recompileSupervisor);
3788 break;
3789 }
3790 // pressing Alt-F11 toggles the user recompiler
3791 case SDLK_F11:
3792 {
3793 BOOL recompileUser;
3794 gpMachineDebugger->COMGETTER(RecompileUser)(&recompileUser);
3795 gpMachineDebugger->COMSETTER(RecompileUser)(!recompileUser);
3796 break;
3797 }
3798 // pressing Alt-F10 toggles the patch manager
3799 case SDLK_F10:
3800 {
3801 BOOL patmEnabled;
3802 gpMachineDebugger->COMGETTER(PATMEnabled)(&patmEnabled);
3803 gpMachineDebugger->COMSETTER(PATMEnabled)(!patmEnabled);
3804 break;
3805 }
3806 // pressing Alt-F9 toggles CSAM
3807 case SDLK_F9:
3808 {
3809 BOOL csamEnabled;
3810 gpMachineDebugger->COMGETTER(CSAMEnabled)(&csamEnabled);
3811 gpMachineDebugger->COMSETTER(CSAMEnabled)(!csamEnabled);
3812 break;
3813 }
3814 // pressing Alt-F8 toggles singlestepping mode
3815 case SDLK_F8:
3816 {
3817 BOOL singlestepEnabled;
3818 gpMachineDebugger->COMGETTER(SingleStep)(&singlestepEnabled);
3819 gpMachineDebugger->COMSETTER(SingleStep)(!singlestepEnabled);
3820 break;
3821 }
3822 default:
3823 break;
3824 }
3825 }
3826#endif
3827 // pressing Ctrl-F12 toggles the logger
3828 else if ((keystate[SDLK_RCTRL] || keystate[SDLK_LCTRL]) && ev->keysym.sym == SDLK_F12)
3829 {
3830 BOOL logEnabled = TRUE;
3831 gpMachineDebugger->COMGETTER(LogEnabled)(&logEnabled);
3832 gpMachineDebugger->COMSETTER(LogEnabled)(!logEnabled);
3833#ifdef DEBUG_bird
3834 return;
3835#endif
3836 }
3837 // pressing F12 sets a logmark
3838 else if (ev->keysym.sym == SDLK_F12)
3839 {
3840 RTLogPrintf("****** LOGGING MARK ******\n");
3841 RTLogFlush(NULL);
3842 }
3843 // now update the titlebar flags
3844 UpdateTitlebar(TITLEBAR_NORMAL);
3845 }
3846#endif // DEBUG || VBOX_WITH_STATISTICS
3847
3848 // the pause key is the weirdest, needs special handling
3849 if (ev->keysym.sym == SDLK_PAUSE)
3850 {
3851 int v = 0;
3852 if (ev->type == SDL_KEYUP)
3853 v |= 0x80;
3854 gpKeyboard->PutScancode(0xe1);
3855 gpKeyboard->PutScancode(0x1d | v);
3856 gpKeyboard->PutScancode(0x45 | v);
3857 return;
3858 }
3859
3860 /*
3861 * Perform SDL key event to scancode conversion
3862 */
3863 int keycode = Keyevent2Keycode(ev);
3864
3865 switch(keycode)
3866 {
3867 case 0x00:
3868 {
3869 /* sent when leaving window: reset the modifiers state */
3870 ResetKeys();
3871 return;
3872 }
3873
3874 case 0x2a: /* Left Shift */
3875 case 0x36: /* Right Shift */
3876 case 0x1d: /* Left CTRL */
3877 case 0x1d|0x100: /* Right CTRL */
3878 case 0x38: /* Left ALT */
3879 case 0x38|0x100: /* Right ALT */
3880 {
3881 if (ev->type == SDL_KEYUP)
3882 gaModifiersState[keycode & ~0x100] = 0;
3883 else
3884 gaModifiersState[keycode & ~0x100] = 1;
3885 break;
3886 }
3887
3888 case 0x45: /* Num Lock */
3889 case 0x3a: /* Caps Lock */
3890 {
3891 /*
3892 * SDL generates a KEYDOWN event if the lock key is active and a KEYUP event
3893 * if the lock key is inactive. See SDL_DISABLE_LOCK_KEYS.
3894 */
3895 if (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP)
3896 {
3897 gpKeyboard->PutScancode(keycode);
3898 gpKeyboard->PutScancode(keycode | 0x80);
3899 }
3900 return;
3901 }
3902 }
3903
3904 if (ev->type != SDL_KEYDOWN)
3905 {
3906 /*
3907 * Some keyboards (e.g. the one of mine T60) don't send a NumLock scan code on every
3908 * press of the key. Both the guest and the host should agree on the NumLock state.
3909 * If they differ, we try to alter the guest NumLock state by sending the NumLock key
3910 * scancode. We will get a feedback through the KBD_CMD_SET_LEDS command if the guest
3911 * tries to set/clear the NumLock LED. If a (silly) guest doesn't change the LED, don't
3912 * bother him with NumLock scancodes. At least our BIOS, Linux and Windows handle the
3913 * NumLock LED well.
3914 */
3915 if ( gcGuestNumLockAdaptions
3916 && (gfGuestNumLockPressed ^ !!(SDL_GetModState() & KMOD_NUM)))
3917 {
3918 gcGuestNumLockAdaptions--;
3919 gpKeyboard->PutScancode(0x45);
3920 gpKeyboard->PutScancode(0x45 | 0x80);
3921 }
3922 if ( gcGuestCapsLockAdaptions
3923 && (gfGuestCapsLockPressed ^ !!(SDL_GetModState() & KMOD_CAPS)))
3924 {
3925 gcGuestCapsLockAdaptions--;
3926 gpKeyboard->PutScancode(0x3a);
3927 gpKeyboard->PutScancode(0x3a | 0x80);
3928 }
3929 }
3930
3931 /*
3932 * Now we send the event. Apply extended and release prefixes.
3933 */
3934 if (keycode & 0x100)
3935 gpKeyboard->PutScancode(0xe0);
3936
3937 gpKeyboard->PutScancode(ev->type == SDL_KEYUP ? (keycode & 0x7f) | 0x80
3938 : (keycode & 0x7f));
3939}
3940
3941#ifdef RT_OS_DARWIN
3942#include <Carbon/Carbon.h>
3943RT_C_DECLS_BEGIN
3944/* Private interface in 10.3 and later. */
3945typedef int CGSConnection;
3946typedef enum
3947{
3948 kCGSGlobalHotKeyEnable = 0,
3949 kCGSGlobalHotKeyDisable,
3950 kCGSGlobalHotKeyInvalid = -1 /* bird */
3951} CGSGlobalHotKeyOperatingMode;
3952extern CGSConnection _CGSDefaultConnection(void);
3953extern CGError CGSGetGlobalHotKeyOperatingMode(CGSConnection Connection, CGSGlobalHotKeyOperatingMode *enmMode);
3954extern CGError CGSSetGlobalHotKeyOperatingMode(CGSConnection Connection, CGSGlobalHotKeyOperatingMode enmMode);
3955RT_C_DECLS_END
3956
3957/** Keeping track of whether we disabled the hotkeys or not. */
3958static bool g_fHotKeysDisabled = false;
3959/** Whether we've connected or not. */
3960static bool g_fConnectedToCGS = false;
3961/** Cached connection. */
3962static CGSConnection g_CGSConnection;
3963
3964/**
3965 * Disables or enabled global hot keys.
3966 */
3967static void DisableGlobalHotKeys(bool fDisable)
3968{
3969 if (!g_fConnectedToCGS)
3970 {
3971 g_CGSConnection = _CGSDefaultConnection();
3972 g_fConnectedToCGS = true;
3973 }
3974
3975 /* get current mode. */
3976 CGSGlobalHotKeyOperatingMode enmMode = kCGSGlobalHotKeyInvalid;
3977 CGSGetGlobalHotKeyOperatingMode(g_CGSConnection, &enmMode);
3978
3979 /* calc new mode. */
3980 if (fDisable)
3981 {
3982 if (enmMode != kCGSGlobalHotKeyEnable)
3983 return;
3984 enmMode = kCGSGlobalHotKeyDisable;
3985 }
3986 else
3987 {
3988 if ( enmMode != kCGSGlobalHotKeyDisable
3989 /*|| !g_fHotKeysDisabled*/)
3990 return;
3991 enmMode = kCGSGlobalHotKeyEnable;
3992 }
3993
3994 /* try set it and check the actual result. */
3995 CGSSetGlobalHotKeyOperatingMode(g_CGSConnection, enmMode);
3996 CGSGlobalHotKeyOperatingMode enmNewMode = kCGSGlobalHotKeyInvalid;
3997 CGSGetGlobalHotKeyOperatingMode(g_CGSConnection, &enmNewMode);
3998 if (enmNewMode == enmMode)
3999 g_fHotKeysDisabled = enmMode == kCGSGlobalHotKeyDisable;
4000}
4001#endif /* RT_OS_DARWIN */
4002
4003/**
4004 * Start grabbing the mouse.
4005 */
4006static void InputGrabStart(void)
4007{
4008#ifdef RT_OS_DARWIN
4009 DisableGlobalHotKeys(true);
4010#endif
4011 if (!gfGuestNeedsHostCursor && gfRelativeMouseGuest)
4012 SDL_ShowCursor(SDL_DISABLE);
4013#ifdef VBOX_WITH_SDL2
4014 SDL_SetRelativeMouseMode(SDL_TRUE);
4015#else
4016 SDL_WM_GrabInput(SDL_GRAB_ON);
4017 // dummy read to avoid moving the mouse
4018 SDL_GetRelativeMouseState(NULL, NULL);
4019#endif
4020 gfGrabbed = TRUE;
4021 UpdateTitlebar(TITLEBAR_NORMAL);
4022}
4023
4024/**
4025 * End mouse grabbing.
4026 */
4027static void InputGrabEnd(void)
4028{
4029#ifdef VBOX_WITH_SDL2
4030 SDL_SetRelativeMouseMode(SDL_FALSE);
4031#else
4032 SDL_WM_GrabInput(SDL_GRAB_OFF);
4033#endif
4034 if (!gfGuestNeedsHostCursor && gfRelativeMouseGuest)
4035 SDL_ShowCursor(SDL_ENABLE);
4036#ifdef RT_OS_DARWIN
4037 DisableGlobalHotKeys(false);
4038#endif
4039 gfGrabbed = FALSE;
4040 UpdateTitlebar(TITLEBAR_NORMAL);
4041}
4042
4043/**
4044 * Query mouse position and button state from SDL and send to the VM
4045 *
4046 * @param dz Relative mouse wheel movement
4047 */
4048static void SendMouseEvent(VBoxSDLFB *fb, int dz, int down, int button)
4049{
4050 int x, y, state, buttons;
4051 bool abs;
4052
4053#ifdef VBOX_WITH_SDL2
4054 if (!fb)
4055 {
4056 SDL_GetMouseState(&x, &y);
4057 RTPrintf("MouseEvent: Cannot find fb mouse = %d,%d\n", x, y);
4058 return;
4059 }
4060#else
4061 AssertRelease(fb != NULL);
4062#endif
4063
4064 /*
4065 * If supported and we're not in grabbed mode, we'll use the absolute mouse.
4066 * If we are in grabbed mode and the guest is not able to draw the mouse cursor
4067 * itself, or can't handle relative reporting, we have to use absolute
4068 * coordinates, otherwise the host cursor and
4069 * the coordinates the guest thinks the mouse is at could get out-of-sync. From
4070 * the SDL mailing list:
4071 *
4072 * "The event processing is usually asynchronous and so somewhat delayed, and
4073 * SDL_GetMouseState is returning the immediate mouse state. So at the time you
4074 * call SDL_GetMouseState, the "button" is already up."
4075 */
4076 abs = (UseAbsoluteMouse() && !gfGrabbed)
4077 || gfGuestNeedsHostCursor
4078 || !gfRelativeMouseGuest;
4079
4080 /* only used if abs == TRUE */
4081 int xOrigin = fb->getOriginX();
4082 int yOrigin = fb->getOriginY();
4083 int xMin = fb->getXOffset() + xOrigin;
4084 int yMin = fb->getYOffset() + yOrigin;
4085 int xMax = xMin + (int)fb->getGuestXRes();
4086 int yMax = yMin + (int)fb->getGuestYRes();
4087
4088 state = abs ? SDL_GetMouseState(&x, &y)
4089 : SDL_GetRelativeMouseState(&x, &y);
4090
4091 /*
4092 * process buttons
4093 */
4094 buttons = 0;
4095 if (state & SDL_BUTTON(SDL_BUTTON_LEFT))
4096 buttons |= MouseButtonState_LeftButton;
4097 if (state & SDL_BUTTON(SDL_BUTTON_RIGHT))
4098 buttons |= MouseButtonState_RightButton;
4099 if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE))
4100 buttons |= MouseButtonState_MiddleButton;
4101
4102 if (abs)
4103 {
4104 x += xOrigin;
4105 y += yOrigin;
4106
4107 /*
4108 * Check if the mouse event is inside the guest area. This solves the
4109 * following problem: Some guests switch off the VBox hardware mouse
4110 * cursor and draw the mouse cursor itself instead. Moving the mouse
4111 * outside the guest area then leads to annoying mouse hangs if we
4112 * don't pass mouse motion events into the guest.
4113 */
4114 if (x < xMin || y < yMin || x > xMax || y > yMax)
4115 {
4116 /*
4117 * Cursor outside of valid guest area (outside window or in secure
4118 * label area. Don't allow any mouse button press.
4119 */
4120 button = 0;
4121
4122 /*
4123 * Release any pressed button.
4124 */
4125#if 0
4126 /* disabled on customers request */
4127 buttons &= ~(MouseButtonState_LeftButton |
4128 MouseButtonState_MiddleButton |
4129 MouseButtonState_RightButton);
4130#endif
4131
4132 /*
4133 * Prevent negative coordinates.
4134 */
4135 if (x < xMin) x = xMin;
4136 if (x > xMax) x = xMax;
4137 if (y < yMin) y = yMin;
4138 if (y > yMax) y = yMax;
4139
4140 if (!gpOffCursor)
4141 {
4142 gpOffCursor = SDL_GetCursor(); /* Cursor image */
4143 gfOffCursorActive = SDL_ShowCursor(-1); /* enabled / disabled */
4144 SDL_SetCursor(gpDefaultCursor);
4145 SDL_ShowCursor(SDL_ENABLE);
4146 }
4147 }
4148 else
4149 {
4150 if (gpOffCursor)
4151 {
4152 /*
4153 * We just entered the valid guest area. Restore the guest mouse
4154 * cursor.
4155 */
4156 SDL_SetCursor(gpOffCursor);
4157 SDL_ShowCursor(gfOffCursorActive ? SDL_ENABLE : SDL_DISABLE);
4158 gpOffCursor = NULL;
4159 }
4160 }
4161 }
4162
4163 /*
4164 * Button was pressed but that press is not reflected in the button state?
4165 */
4166 if (down && !(state & SDL_BUTTON(button)))
4167 {
4168 /*
4169 * It can happen that a mouse up event follows a mouse down event immediately
4170 * and we see the events when the bit in the button state is already cleared
4171 * again. In that case we simulate the mouse down event.
4172 */
4173 int tmp_button = 0;
4174 switch (button)
4175 {
4176 case SDL_BUTTON_LEFT: tmp_button = MouseButtonState_LeftButton; break;
4177 case SDL_BUTTON_MIDDLE: tmp_button = MouseButtonState_MiddleButton; break;
4178 case SDL_BUTTON_RIGHT: tmp_button = MouseButtonState_RightButton; break;
4179 }
4180
4181 if (abs)
4182 {
4183 /**
4184 * @todo
4185 * PutMouseEventAbsolute() expects x and y starting from 1,1.
4186 * should we do the increment internally in PutMouseEventAbsolute()
4187 * or state it in PutMouseEventAbsolute() docs?
4188 */
4189 gpMouse->PutMouseEventAbsolute(x + 1 - xMin + xOrigin,
4190 y + 1 - yMin + yOrigin,
4191 dz, 0 /* horizontal scroll wheel */,
4192 buttons | tmp_button);
4193 }
4194 else
4195 {
4196 gpMouse->PutMouseEvent(0, 0, dz,
4197 0 /* horizontal scroll wheel */,
4198 buttons | tmp_button);
4199 }
4200 }
4201
4202 // now send the mouse event
4203 if (abs)
4204 {
4205 /**
4206 * @todo
4207 * PutMouseEventAbsolute() expects x and y starting from 1,1.
4208 * should we do the increment internally in PutMouseEventAbsolute()
4209 * or state it in PutMouseEventAbsolute() docs?
4210 */
4211 gpMouse->PutMouseEventAbsolute(x + 1 - xMin + xOrigin,
4212 y + 1 - yMin + yOrigin,
4213 dz, 0 /* Horizontal wheel */, buttons);
4214 }
4215 else
4216 {
4217 gpMouse->PutMouseEvent(x, y, dz, 0 /* Horizontal wheel */, buttons);
4218 }
4219}
4220
4221/**
4222 * Resets the VM
4223 */
4224void ResetVM(void)
4225{
4226 if (gpConsole)
4227 gpConsole->Reset();
4228}
4229
4230/**
4231 * Initiates a saved state and updates the titlebar with progress information
4232 */
4233void SaveState(void)
4234{
4235 ResetKeys();
4236 RTThreadYield();
4237 if (gfGrabbed)
4238 InputGrabEnd();
4239 RTThreadYield();
4240 UpdateTitlebar(TITLEBAR_SAVE);
4241 gpProgress = NULL;
4242 HRESULT rc = gpMachine->SaveState(gpProgress.asOutParam());
4243 if (FAILED(rc))
4244 {
4245 RTPrintf("Error saving state! rc = 0x%x\n", rc);
4246 return;
4247 }
4248 Assert(gpProgress);
4249
4250 /*
4251 * Wait for the operation to be completed and work
4252 * the title bar in the mean while.
4253 */
4254 ULONG cPercent = 0;
4255#ifndef RT_OS_DARWIN /* don't break the other guys yet. */
4256 for (;;)
4257 {
4258 BOOL fCompleted = false;
4259 rc = gpProgress->COMGETTER(Completed)(&fCompleted);
4260 if (FAILED(rc) || fCompleted)
4261 break;
4262 ULONG cPercentNow;
4263 rc = gpProgress->COMGETTER(Percent)(&cPercentNow);
4264 if (FAILED(rc))
4265 break;
4266 if (cPercentNow != cPercent)
4267 {
4268 UpdateTitlebar(TITLEBAR_SAVE, cPercent);
4269 cPercent = cPercentNow;
4270 }
4271
4272 /* wait */
4273 rc = gpProgress->WaitForCompletion(100);
4274 if (FAILED(rc))
4275 break;
4276 /// @todo process gui events.
4277 }
4278
4279#else /* new loop which processes GUI events while saving. */
4280
4281 /* start regular timer so we don't starve in the event loop */
4282 SDL_TimerID sdlTimer;
4283 sdlTimer = SDL_AddTimer(100, StartupTimer, NULL);
4284
4285 for (;;)
4286 {
4287 /*
4288 * Check for completion.
4289 */
4290 BOOL fCompleted = false;
4291 rc = gpProgress->COMGETTER(Completed)(&fCompleted);
4292 if (FAILED(rc) || fCompleted)
4293 break;
4294 ULONG cPercentNow;
4295 rc = gpProgress->COMGETTER(Percent)(&cPercentNow);
4296 if (FAILED(rc))
4297 break;
4298 if (cPercentNow != cPercent)
4299 {
4300 UpdateTitlebar(TITLEBAR_SAVE, cPercent);
4301 cPercent = cPercentNow;
4302 }
4303
4304 /*
4305 * Wait for and process GUI a event.
4306 * This is necessary for XPCOM IPC and for updating the
4307 * title bar on the Mac.
4308 */
4309 SDL_Event event;
4310 if (WaitSDLEvent(&event))
4311 {
4312 switch (event.type)
4313 {
4314 /*
4315 * Timer event preventing us from getting stuck.
4316 */
4317 case SDL_USER_EVENT_TIMER:
4318 break;
4319
4320#ifdef USE_XPCOM_QUEUE_THREAD
4321 /*
4322 * User specific XPCOM event queue event
4323 */
4324 case SDL_USER_EVENT_XPCOM_EVENTQUEUE:
4325 {
4326 LogFlow(("SDL_USER_EVENT_XPCOM_EVENTQUEUE: processing XPCOM event queue...\n"));
4327 eventQ->ProcessPendingEvents();
4328 signalXPCOMEventQueueThread();
4329 break;
4330 }
4331#endif /* USE_XPCOM_QUEUE_THREAD */
4332
4333
4334 /*
4335 * Ignore all other events.
4336 */
4337 case SDL_USER_EVENT_NOTIFYCHANGE:
4338 case SDL_USER_EVENT_TERMINATE:
4339 default:
4340 break;
4341 }
4342 }
4343 }
4344
4345 /* kill the timer */
4346 SDL_RemoveTimer(sdlTimer);
4347 sdlTimer = 0;
4348
4349#endif /* RT_OS_DARWIN */
4350
4351 /*
4352 * What's the result of the operation?
4353 */
4354 LONG lrc;
4355 rc = gpProgress->COMGETTER(ResultCode)(&lrc);
4356 if (FAILED(rc))
4357 lrc = ~0;
4358 if (!lrc)
4359 {
4360 UpdateTitlebar(TITLEBAR_SAVE, 100);
4361 RTThreadYield();
4362 RTPrintf("Saved the state successfully.\n");
4363 }
4364 else
4365 RTPrintf("Error saving state, lrc=%d (%#x)\n", lrc, lrc);
4366}
4367
4368/**
4369 * Build the titlebar string
4370 */
4371static void UpdateTitlebar(TitlebarMode mode, uint32_t u32User)
4372{
4373 static char szTitle[1024] = {0};
4374
4375 /* back up current title */
4376 char szPrevTitle[1024];
4377 strcpy(szPrevTitle, szTitle);
4378
4379 Bstr bstrName;
4380 gpMachine->COMGETTER(Name)(bstrName.asOutParam());
4381
4382 RTStrPrintf(szTitle, sizeof(szTitle), "%s - " VBOX_PRODUCT,
4383 !bstrName.isEmpty() ? Utf8Str(bstrName).c_str() : "<noname>");
4384
4385 /* which mode are we in? */
4386 switch (mode)
4387 {
4388 case TITLEBAR_NORMAL:
4389 {
4390 MachineState_T machineState;
4391 gpMachine->COMGETTER(State)(&machineState);
4392 if (machineState == MachineState_Paused)
4393 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle), " - [Paused]");
4394
4395 if (gfGrabbed)
4396 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle), " - [Input captured]");
4397
4398#if defined(DEBUG) || defined(VBOX_WITH_STATISTICS)
4399 // do we have a debugger interface
4400 if (gpMachineDebugger)
4401 {
4402 // query the machine state
4403 BOOL recompileSupervisor = FALSE;
4404 BOOL recompileUser = FALSE;
4405 BOOL patmEnabled = FALSE;
4406 BOOL csamEnabled = FALSE;
4407 BOOL singlestepEnabled = FALSE;
4408 BOOL logEnabled = FALSE;
4409 BOOL hwVirtEnabled = FALSE;
4410 ULONG virtualTimeRate = 100;
4411 gpMachineDebugger->COMGETTER(RecompileSupervisor)(&recompileSupervisor);
4412 gpMachineDebugger->COMGETTER(RecompileUser)(&recompileUser);
4413 gpMachineDebugger->COMGETTER(PATMEnabled)(&patmEnabled);
4414 gpMachineDebugger->COMGETTER(CSAMEnabled)(&csamEnabled);
4415 gpMachineDebugger->COMGETTER(LogEnabled)(&logEnabled);
4416 gpMachineDebugger->COMGETTER(SingleStep)(&singlestepEnabled);
4417 gpMachineDebugger->COMGETTER(HWVirtExEnabled)(&hwVirtEnabled);
4418 gpMachineDebugger->COMGETTER(VirtualTimeRate)(&virtualTimeRate);
4419 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
4420 " [STEP=%d CS=%d PAT=%d RR0=%d RR3=%d LOG=%d HWVirt=%d",
4421 singlestepEnabled == TRUE, csamEnabled == TRUE, patmEnabled == TRUE,
4422 recompileSupervisor == FALSE, recompileUser == FALSE,
4423 logEnabled == TRUE, hwVirtEnabled == TRUE);
4424 char *psz = strchr(szTitle, '\0');
4425 if (virtualTimeRate != 100)
4426 RTStrPrintf(psz, &szTitle[sizeof(szTitle)] - psz, " WD=%d%%]", virtualTimeRate);
4427 else
4428 RTStrPrintf(psz, &szTitle[sizeof(szTitle)] - psz, "]");
4429 }
4430#endif /* DEBUG || VBOX_WITH_STATISTICS */
4431 break;
4432 }
4433
4434 case TITLEBAR_STARTUP:
4435 {
4436 /*
4437 * Format it.
4438 */
4439 MachineState_T machineState;
4440 gpMachine->COMGETTER(State)(&machineState);
4441 if (machineState == MachineState_Starting)
4442 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
4443 " - Starting...");
4444 else if (machineState == MachineState_Restoring)
4445 {
4446 ULONG cPercentNow;
4447 HRESULT rc = gpProgress->COMGETTER(Percent)(&cPercentNow);
4448 if (SUCCEEDED(rc))
4449 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
4450 " - Restoring %d%%...", (int)cPercentNow);
4451 else
4452 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
4453 " - Restoring...");
4454 }
4455 else if (machineState == MachineState_TeleportingIn)
4456 {
4457 ULONG cPercentNow;
4458 HRESULT rc = gpProgress->COMGETTER(Percent)(&cPercentNow);
4459 if (SUCCEEDED(rc))
4460 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
4461 " - Teleporting %d%%...", (int)cPercentNow);
4462 else
4463 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
4464 " - Teleporting...");
4465 }
4466 /* ignore other states, we could already be in running or aborted state */
4467 break;
4468 }
4469
4470 case TITLEBAR_SAVE:
4471 {
4472 AssertMsg(u32User <= 100, ("%d\n", u32User));
4473 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
4474 " - Saving %d%%...", u32User);
4475 break;
4476 }
4477
4478 case TITLEBAR_SNAPSHOT:
4479 {
4480 AssertMsg(u32User <= 100, ("%d\n", u32User));
4481 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
4482 " - Taking snapshot %d%%...", u32User);
4483 break;
4484 }
4485
4486 default:
4487 RTPrintf("Error: Invalid title bar mode %d!\n", mode);
4488 return;
4489 }
4490
4491 /*
4492 * Don't update if it didn't change.
4493 */
4494 if (!strcmp(szTitle, szPrevTitle))
4495 return;
4496
4497 /*
4498 * Set the new title
4499 */
4500#ifdef VBOX_WIN32_UI
4501 setUITitle(szTitle);
4502#else
4503# ifdef VBOX_WITH_SDL2
4504 for (unsigned i = 0; i < gcMonitors; i++)
4505 gpFramebuffer[i]->setWindowTitle(szTitle);
4506# else
4507 SDL_WM_SetCaption(szTitle, VBOX_PRODUCT);
4508# endif
4509#endif
4510}
4511
4512#if 0
4513static void vbox_show_shape(unsigned short w, unsigned short h,
4514 uint32_t bg, const uint8_t *image)
4515{
4516 size_t x, y;
4517 unsigned short pitch;
4518 const uint32_t *color;
4519 const uint8_t *mask;
4520 size_t size_mask;
4521
4522 mask = image;
4523 pitch = (w + 7) / 8;
4524 size_mask = (pitch * h + 3) & ~3;
4525
4526 color = (const uint32_t *)(image + size_mask);
4527
4528 printf("show_shape %dx%d pitch %d size mask %d\n",
4529 w, h, pitch, size_mask);
4530 for (y = 0; y < h; ++y, mask += pitch, color += w)
4531 {
4532 for (x = 0; x < w; ++x) {
4533 if (mask[x / 8] & (1 << (7 - (x % 8))))
4534 printf(" ");
4535 else
4536 {
4537 uint32_t c = color[x];
4538 if (c == bg)
4539 printf("Y");
4540 else
4541 printf("X");
4542 }
4543 }
4544 printf("\n");
4545 }
4546}
4547#endif
4548
4549/**
4550 * Sets the pointer shape according to parameters.
4551 * Must be called only from the main SDL thread.
4552 */
4553static void SetPointerShape(const PointerShapeChangeData *data)
4554{
4555 /*
4556 * don't allow to change the pointer shape if we are outside the valid
4557 * guest area. In that case set standard mouse pointer is set and should
4558 * not get overridden.
4559 */
4560 if (gpOffCursor)
4561 return;
4562
4563 if (data->shape.size() > 0)
4564 {
4565 bool ok = false;
4566
4567 uint32_t andMaskSize = (data->width + 7) / 8 * data->height;
4568 uint32_t srcShapePtrScan = data->width * 4;
4569
4570 const uint8_t* shape = data->shape.raw();
4571 const uint8_t *srcAndMaskPtr = shape;
4572 const uint8_t *srcShapePtr = shape + ((andMaskSize + 3) & ~3);
4573
4574#if 0
4575 /* pointer debugging code */
4576 // vbox_show_shape(data->width, data->height, 0, data->shape);
4577 uint32_t shapeSize = ((((data->width + 7) / 8) * data->height + 3) & ~3) + data->width * 4 * data->height;
4578 printf("visible: %d\n", data->visible);
4579 printf("width = %d\n", data->width);
4580 printf("height = %d\n", data->height);
4581 printf("alpha = %d\n", data->alpha);
4582 printf("xhot = %d\n", data->xHot);
4583 printf("yhot = %d\n", data->yHot);
4584 printf("uint8_t pointerdata[] = { ");
4585 for (uint32_t i = 0; i < shapeSize; i++)
4586 {
4587 printf("0x%x, ", data->shape[i]);
4588 }
4589 printf("};\n");
4590#endif
4591
4592#if defined(RT_OS_WINDOWS)
4593
4594 BITMAPV5HEADER bi;
4595 HBITMAP hBitmap;
4596 void *lpBits;
4597
4598 ::ZeroMemory(&bi, sizeof(BITMAPV5HEADER));
4599 bi.bV5Size = sizeof(BITMAPV5HEADER);
4600 bi.bV5Width = data->width;
4601 bi.bV5Height = -(LONG)data->height;
4602 bi.bV5Planes = 1;
4603 bi.bV5BitCount = 32;
4604 bi.bV5Compression = BI_BITFIELDS;
4605 // specify a supported 32 BPP alpha format for Windows XP
4606 bi.bV5RedMask = 0x00FF0000;
4607 bi.bV5GreenMask = 0x0000FF00;
4608 bi.bV5BlueMask = 0x000000FF;
4609 if (data->alpha)
4610 bi.bV5AlphaMask = 0xFF000000;
4611 else
4612 bi.bV5AlphaMask = 0;
4613
4614 HDC hdc = ::GetDC(NULL);
4615
4616 // create the DIB section with an alpha channel
4617 hBitmap = ::CreateDIBSection(hdc, (BITMAPINFO *)&bi, DIB_RGB_COLORS,
4618 (void **)&lpBits, NULL, (DWORD)0);
4619
4620 ::ReleaseDC(NULL, hdc);
4621
4622 HBITMAP hMonoBitmap = NULL;
4623 if (data->alpha)
4624 {
4625 // create an empty mask bitmap
4626 hMonoBitmap = ::CreateBitmap(data->width, data->height, 1, 1, NULL);
4627 }
4628 else
4629 {
4630 /* Word aligned AND mask. Will be allocated and created if necessary. */
4631 uint8_t *pu8AndMaskWordAligned = NULL;
4632
4633 /* Width in bytes of the original AND mask scan line. */
4634 uint32_t cbAndMaskScan = (data->width + 7) / 8;
4635
4636 if (cbAndMaskScan & 1)
4637 {
4638 /* Original AND mask is not word aligned. */
4639
4640 /* Allocate memory for aligned AND mask. */
4641 pu8AndMaskWordAligned = (uint8_t *)RTMemTmpAllocZ((cbAndMaskScan + 1) * data->height);
4642
4643 Assert(pu8AndMaskWordAligned);
4644
4645 if (pu8AndMaskWordAligned)
4646 {
4647 /* According to MSDN the padding bits must be 0.
4648 * Compute the bit mask to set padding bits to 0 in the last byte of original AND mask.
4649 */
4650 uint32_t u32PaddingBits = cbAndMaskScan * 8 - data->width;
4651 Assert(u32PaddingBits < 8);
4652 uint8_t u8LastBytesPaddingMask = (uint8_t)(0xFF << u32PaddingBits);
4653
4654 Log(("u8LastBytesPaddingMask = %02X, aligned w = %d, width = %d, cbAndMaskScan = %d\n",
4655 u8LastBytesPaddingMask, (cbAndMaskScan + 1) * 8, data->width, cbAndMaskScan));
4656
4657 uint8_t *src = (uint8_t *)srcAndMaskPtr;
4658 uint8_t *dst = pu8AndMaskWordAligned;
4659
4660 unsigned i;
4661 for (i = 0; i < data->height; i++)
4662 {
4663 memcpy(dst, src, cbAndMaskScan);
4664
4665 dst[cbAndMaskScan - 1] &= u8LastBytesPaddingMask;
4666
4667 src += cbAndMaskScan;
4668 dst += cbAndMaskScan + 1;
4669 }
4670 }
4671 }
4672
4673 // create the AND mask bitmap
4674 hMonoBitmap = ::CreateBitmap(data->width, data->height, 1, 1,
4675 pu8AndMaskWordAligned? pu8AndMaskWordAligned: srcAndMaskPtr);
4676
4677 if (pu8AndMaskWordAligned)
4678 {
4679 RTMemTmpFree(pu8AndMaskWordAligned);
4680 }
4681 }
4682
4683 Assert(hBitmap);
4684 Assert(hMonoBitmap);
4685 if (hBitmap && hMonoBitmap)
4686 {
4687 DWORD *dstShapePtr = (DWORD *)lpBits;
4688
4689 for (uint32_t y = 0; y < data->height; y ++)
4690 {
4691 memcpy(dstShapePtr, srcShapePtr, srcShapePtrScan);
4692 srcShapePtr += srcShapePtrScan;
4693 dstShapePtr += data->width;
4694 }
4695
4696#ifndef VBOX_WITH_SDL2 /** @BUGBUG Implement alpha cursor support handling. */
4697 ICONINFO ii;
4698 ii.fIcon = FALSE;
4699 ii.xHotspot = data->xHot;
4700 ii.yHotspot = data->yHot;
4701 ii.hbmMask = hMonoBitmap;
4702 ii.hbmColor = hBitmap;
4703
4704 HCURSOR hAlphaCursor = ::CreateIconIndirect(&ii);
4705 Assert(hAlphaCursor);
4706 if (hAlphaCursor)
4707 {
4708 // here we do a dirty trick by substituting a Window Manager's
4709 // cursor handle with the handle we created
4710
4711 WMcursor *pCustomTempWMCursor = gpCustomCursor->wm_cursor;
4712 // see SDL12/src/video/wincommon/SDL_sysmouse.c
4713 void *wm_cursor = malloc(sizeof(HCURSOR) + sizeof(uint8_t *) * 2);
4714 *(HCURSOR *)wm_cursor = hAlphaCursor;
4715
4716 gpCustomCursor->wm_cursor = (WMcursor *)wm_cursor;
4717 SDL_SetCursor(gpCustomCursor);
4718 SDL_ShowCursor(SDL_ENABLE);
4719
4720 if (pCustomTempWMCursor)
4721 {
4722 ::DestroyCursor(*(HCURSOR *)pCustomTempWMCursor);
4723 free(pCustomTempWMCursor);
4724 }
4725
4726 ok = true;
4727 }
4728#endif
4729 }
4730
4731 if (hMonoBitmap)
4732 ::DeleteObject(hMonoBitmap);
4733 if (hBitmap)
4734 ::DeleteObject(hBitmap);
4735
4736#elif defined(VBOXSDL_WITH_X11) && !defined(VBOX_WITHOUT_XCURSOR)
4737
4738 if (gfXCursorEnabled)
4739 {
4740 XcursorImage *img = XcursorImageCreate(data->width, data->height);
4741 Assert(img);
4742 if (img)
4743 {
4744 img->xhot = data->xHot;
4745 img->yhot = data->yHot;
4746
4747 XcursorPixel *dstShapePtr = img->pixels;
4748
4749 for (uint32_t y = 0; y < data->height; y ++)
4750 {
4751 memcpy(dstShapePtr, srcShapePtr, srcShapePtrScan);
4752
4753 if (!data->alpha)
4754 {
4755 // convert AND mask to the alpha channel
4756 uint8_t byte = 0;
4757 for (uint32_t x = 0; x < data->width; x ++)
4758 {
4759 if (!(x % 8))
4760 byte = *(srcAndMaskPtr ++);
4761 else
4762 byte <<= 1;
4763
4764 if (byte & 0x80)
4765 {
4766 // Linux doesn't support inverted pixels (XOR ops,
4767 // to be exact) in cursor shapes, so we detect such
4768 // pixels and always replace them with black ones to
4769 // make them visible at least over light colors
4770 if (dstShapePtr [x] & 0x00FFFFFF)
4771 dstShapePtr [x] = 0xFF000000;
4772 else
4773 dstShapePtr [x] = 0x00000000;
4774 }
4775 else
4776 dstShapePtr [x] |= 0xFF000000;
4777 }
4778 }
4779
4780 srcShapePtr += srcShapePtrScan;
4781 dstShapePtr += data->width;
4782 }
4783
4784#ifndef VBOX_WITH_SDL2
4785 Cursor cur = XcursorImageLoadCursor(gSdlInfo.info.x11.display, img);
4786 Assert(cur);
4787 if (cur)
4788 {
4789 // here we do a dirty trick by substituting a Window Manager's
4790 // cursor handle with the handle we created
4791
4792 WMcursor *pCustomTempWMCursor = gpCustomCursor->wm_cursor;
4793
4794 // see SDL12/src/video/x11/SDL_x11mouse.c
4795 void *wm_cursor = malloc(sizeof(Cursor));
4796 *(Cursor *)wm_cursor = cur;
4797
4798 gpCustomCursor->wm_cursor = (WMcursor *)wm_cursor;
4799 SDL_SetCursor(gpCustomCursor);
4800 SDL_ShowCursor(SDL_ENABLE);
4801
4802 if (pCustomTempWMCursor)
4803 {
4804 XFreeCursor(gSdlInfo.info.x11.display, *(Cursor *)pCustomTempWMCursor);
4805 free(pCustomTempWMCursor);
4806 }
4807
4808 ok = true;
4809 }
4810#endif
4811 }
4812 XcursorImageDestroy(img);
4813 }
4814
4815#endif /* VBOXSDL_WITH_X11 && !VBOX_WITHOUT_XCURSOR */
4816
4817 if (!ok)
4818 {
4819 SDL_SetCursor(gpDefaultCursor);
4820 SDL_ShowCursor(SDL_ENABLE);
4821 }
4822 }
4823 else
4824 {
4825 if (data->visible)
4826 SDL_ShowCursor(SDL_ENABLE);
4827 else if (gfAbsoluteMouseGuest)
4828 /* Don't disable the cursor if the guest additions are not active (anymore) */
4829 SDL_ShowCursor(SDL_DISABLE);
4830 }
4831}
4832
4833/**
4834 * Handle changed mouse capabilities
4835 */
4836static void HandleGuestCapsChanged(void)
4837{
4838 if (!gfAbsoluteMouseGuest)
4839 {
4840 // Cursor could be overwritten by the guest tools
4841 SDL_SetCursor(gpDefaultCursor);
4842 SDL_ShowCursor(SDL_ENABLE);
4843 gpOffCursor = NULL;
4844 }
4845 if (gpMouse && UseAbsoluteMouse())
4846 {
4847 // Actually switch to absolute coordinates
4848 if (gfGrabbed)
4849 InputGrabEnd();
4850 gpMouse->PutMouseEventAbsolute(-1, -1, 0, 0, 0);
4851 }
4852}
4853
4854/**
4855 * Handles a host key down event
4856 */
4857static int HandleHostKey(const SDL_KeyboardEvent *pEv)
4858{
4859 /*
4860 * Revalidate the host key modifier
4861 */
4862 if ((SDL_GetModState() & ~(KMOD_MODE | KMOD_NUM | KMOD_RESERVED)) != gHostKeyMod)
4863 return VERR_NOT_SUPPORTED;
4864
4865 /*
4866 * What was pressed?
4867 */
4868 switch (pEv->keysym.sym)
4869 {
4870 /* Control-Alt-Delete */
4871 case SDLK_DELETE:
4872 {
4873 gpKeyboard->PutCAD();
4874 break;
4875 }
4876
4877 /*
4878 * Fullscreen / Windowed toggle.
4879 */
4880 case SDLK_f:
4881 {
4882 if ( strchr(gHostKeyDisabledCombinations, 'f')
4883 || !gfAllowFullscreenToggle)
4884 return VERR_NOT_SUPPORTED;
4885
4886 /*
4887 * We have to pause/resume the machine during this
4888 * process because there might be a short moment
4889 * without a valid framebuffer
4890 */
4891 MachineState_T machineState;
4892 gpMachine->COMGETTER(State)(&machineState);
4893 bool fPauseIt = machineState == MachineState_Running
4894 || machineState == MachineState_Teleporting
4895 || machineState == MachineState_LiveSnapshotting;
4896 if (fPauseIt)
4897 gpConsole->Pause();
4898 SetFullscreen(!gpFramebuffer[0]->getFullscreen());
4899 if (fPauseIt)
4900 gpConsole->Resume();
4901
4902 /*
4903 * We have switched from/to fullscreen, so request a full
4904 * screen repaint, just to be sure.
4905 */
4906 gpDisplay->InvalidateAndUpdate();
4907 break;
4908 }
4909
4910 /*
4911 * Pause / Resume toggle.
4912 */
4913 case SDLK_p:
4914 {
4915 if (strchr(gHostKeyDisabledCombinations, 'p'))
4916 return VERR_NOT_SUPPORTED;
4917
4918 MachineState_T machineState;
4919 gpMachine->COMGETTER(State)(&machineState);
4920 if ( machineState == MachineState_Running
4921 || machineState == MachineState_Teleporting
4922 || machineState == MachineState_LiveSnapshotting
4923 )
4924 {
4925 if (gfGrabbed)
4926 InputGrabEnd();
4927 gpConsole->Pause();
4928 }
4929 else if (machineState == MachineState_Paused)
4930 {
4931 gpConsole->Resume();
4932 }
4933 UpdateTitlebar(TITLEBAR_NORMAL);
4934 break;
4935 }
4936
4937 /*
4938 * Reset the VM
4939 */
4940 case SDLK_r:
4941 {
4942 if (strchr(gHostKeyDisabledCombinations, 'r'))
4943 return VERR_NOT_SUPPORTED;
4944
4945 ResetVM();
4946 break;
4947 }
4948
4949 /*
4950 * Terminate the VM
4951 */
4952 case SDLK_q:
4953 {
4954 if (strchr(gHostKeyDisabledCombinations, 'q'))
4955 return VERR_NOT_SUPPORTED;
4956
4957 return VINF_EM_TERMINATE;
4958 }
4959
4960 /*
4961 * Save the machine's state and exit
4962 */
4963 case SDLK_s:
4964 {
4965 if (strchr(gHostKeyDisabledCombinations, 's'))
4966 return VERR_NOT_SUPPORTED;
4967
4968 SaveState();
4969 return VINF_EM_TERMINATE;
4970 }
4971
4972 case SDLK_h:
4973 {
4974 if (strchr(gHostKeyDisabledCombinations, 'h'))
4975 return VERR_NOT_SUPPORTED;
4976
4977 if (gpConsole)
4978 gpConsole->PowerButton();
4979 break;
4980 }
4981
4982 /*
4983 * Perform an online snapshot. Continue operation.
4984 */
4985 case SDLK_n:
4986 {
4987 if (strchr(gHostKeyDisabledCombinations, 'n'))
4988 return VERR_NOT_SUPPORTED;
4989
4990 RTThreadYield();
4991 ULONG cSnapshots = 0;
4992 gpMachine->COMGETTER(SnapshotCount)(&cSnapshots);
4993 char pszSnapshotName[20];
4994 RTStrPrintf(pszSnapshotName, sizeof(pszSnapshotName), "Snapshot %d", cSnapshots + 1);
4995 gpProgress = NULL;
4996 HRESULT rc;
4997 Bstr snapId;
4998 CHECK_ERROR(gpMachine, TakeSnapshot(Bstr(pszSnapshotName).raw(),
4999 Bstr("Taken by VBoxSDL").raw(),
5000 TRUE, snapId.asOutParam(),
5001 gpProgress.asOutParam()));
5002 if (FAILED(rc))
5003 {
5004 RTPrintf("Error taking snapshot! rc = 0x%x\n", rc);
5005 /* continue operation */
5006 return VINF_SUCCESS;
5007 }
5008 /*
5009 * Wait for the operation to be completed and work
5010 * the title bar in the mean while.
5011 */
5012 ULONG cPercent = 0;
5013 for (;;)
5014 {
5015 BOOL fCompleted = false;
5016 rc = gpProgress->COMGETTER(Completed)(&fCompleted);
5017 if (FAILED(rc) || fCompleted)
5018 break;
5019 ULONG cPercentNow;
5020 rc = gpProgress->COMGETTER(Percent)(&cPercentNow);
5021 if (FAILED(rc))
5022 break;
5023 if (cPercentNow != cPercent)
5024 {
5025 UpdateTitlebar(TITLEBAR_SNAPSHOT, cPercent);
5026 cPercent = cPercentNow;
5027 }
5028
5029 /* wait */
5030 rc = gpProgress->WaitForCompletion(100);
5031 if (FAILED(rc))
5032 break;
5033 /// @todo process gui events.
5034 }
5035
5036 /* continue operation */
5037 return VINF_SUCCESS;
5038 }
5039
5040 case SDLK_F1: case SDLK_F2: case SDLK_F3:
5041 case SDLK_F4: case SDLK_F5: case SDLK_F6:
5042 case SDLK_F7: case SDLK_F8: case SDLK_F9:
5043 case SDLK_F10: case SDLK_F11: case SDLK_F12:
5044 {
5045 // /* send Ctrl-Alt-Fx to guest */
5046 com::SafeArray<LONG> keys(6);
5047
5048 keys[0] = 0x1d; // Ctrl down
5049 keys[1] = 0x38; // Alt down
5050 keys[2] = Keyevent2Keycode(pEv); // Fx down
5051 keys[3] = keys[2] + 0x80; // Fx up
5052 keys[4] = 0xb8; // Alt up
5053 keys[5] = 0x9d; // Ctrl up
5054
5055 gpKeyboard->PutScancodes(ComSafeArrayAsInParam(keys), NULL);
5056 return VINF_SUCCESS;
5057 }
5058
5059 /*
5060 * Not a host key combination.
5061 * Indicate this by returning false.
5062 */
5063 default:
5064 return VERR_NOT_SUPPORTED;
5065 }
5066
5067 return VINF_SUCCESS;
5068}
5069
5070/**
5071 * Timer callback function for startup processing
5072 */
5073static Uint32 StartupTimer(Uint32 interval, void *param) RT_NOTHROW_DEF
5074{
5075 RT_NOREF(param);
5076
5077 /* post message so we can do something in the startup loop */
5078 SDL_Event event = {0};
5079 event.type = SDL_USEREVENT;
5080 event.user.type = SDL_USER_EVENT_TIMER;
5081 SDL_PushEvent(&event);
5082 RTSemEventSignal(g_EventSemSDLEvents);
5083 return interval;
5084}
5085
5086/**
5087 * Timer callback function to check if resizing is finished
5088 */
5089static Uint32 ResizeTimer(Uint32 interval, void *param) RT_NOTHROW_DEF
5090{
5091 RT_NOREF(interval, param);
5092
5093 /* post message so the window is actually resized */
5094 SDL_Event event = {0};
5095 event.type = SDL_USEREVENT;
5096 event.user.type = SDL_USER_EVENT_WINDOW_RESIZE_DONE;
5097 PushSDLEventForSure(&event);
5098 /* one-shot */
5099 return 0;
5100}
5101
5102/**
5103 * Timer callback function to check if an ACPI power button event was handled by the guest.
5104 */
5105static Uint32 QuitTimer(Uint32 interval, void *param) RT_NOTHROW_DEF
5106{
5107 RT_NOREF(interval, param);
5108
5109 BOOL fHandled = FALSE;
5110
5111 gSdlQuitTimer = NULL;
5112 if (gpConsole)
5113 {
5114 int rc = gpConsole->GetPowerButtonHandled(&fHandled);
5115 LogRel(("QuitTimer: rc=%d handled=%d\n", rc, fHandled));
5116 if (RT_FAILURE(rc) || !fHandled)
5117 {
5118 /* event was not handled, power down the guest */
5119 gfACPITerm = FALSE;
5120 SDL_Event event = {0};
5121 event.type = SDL_QUIT;
5122 PushSDLEventForSure(&event);
5123 }
5124 }
5125 /* one-shot */
5126 return 0;
5127}
5128
5129/**
5130 * Wait for the next SDL event. Don't use SDL_WaitEvent since this function
5131 * calls SDL_Delay(10) if the event queue is empty.
5132 */
5133static int WaitSDLEvent(SDL_Event *event)
5134{
5135 for (;;)
5136 {
5137 int rc = SDL_PollEvent(event);
5138 if (rc == 1)
5139 {
5140#ifdef USE_XPCOM_QUEUE_THREAD
5141 if (event->type == SDL_USER_EVENT_XPCOM_EVENTQUEUE)
5142 consumedXPCOMUserEvent();
5143#endif
5144 return 1;
5145 }
5146 /* Immediately wake up if new SDL events are available. This does not
5147 * work for internal SDL events. Don't wait more than 10ms. */
5148 RTSemEventWait(g_EventSemSDLEvents, 10);
5149 }
5150}
5151
5152/**
5153 * Ensure that an SDL event is really enqueued. Try multiple times if necessary.
5154 */
5155int PushSDLEventForSure(SDL_Event *event)
5156{
5157 int ntries = 10;
5158 for (; ntries > 0; ntries--)
5159 {
5160 int rc = SDL_PushEvent(event);
5161 RTSemEventSignal(g_EventSemSDLEvents);
5162#ifdef VBOX_WITH_SDL2
5163 if (rc == 1)
5164#else
5165 if (rc == 0)
5166#endif
5167 return 0;
5168 Log(("PushSDLEventForSure: waiting for 2ms (rc = %d)\n", rc));
5169 RTThreadSleep(2);
5170 }
5171 LogRel(("WARNING: Failed to enqueue SDL event %d.%d!\n",
5172 event->type, event->type == SDL_USEREVENT ? event->user.type : 0));
5173 return -1;
5174}
5175
5176#ifdef VBOXSDL_WITH_X11
5177/**
5178 * Special SDL_PushEvent function for NotifyUpdate events. These events may occur in bursts
5179 * so make sure they don't flood the SDL event queue.
5180 */
5181void PushNotifyUpdateEvent(SDL_Event *event)
5182{
5183 int rc = SDL_PushEvent(event);
5184#ifdef VBOX_WITH_SDL2
5185 bool fSuccess = (rc == 1);
5186#else
5187 bool fSuccess = (rc == 0);
5188#endif
5189
5190 RTSemEventSignal(g_EventSemSDLEvents);
5191 AssertMsg(fSuccess, ("SDL_PushEvent returned SDL error\n"));
5192 /* A global counter is faster than SDL_PeepEvents() */
5193 if (fSuccess)
5194 ASMAtomicIncS32(&g_cNotifyUpdateEventsPending);
5195 /* In order to not flood the SDL event queue, yield the CPU or (if there are already many
5196 * events queued) even sleep */
5197 if (g_cNotifyUpdateEventsPending > 96)
5198 {
5199 /* Too many NotifyUpdate events, sleep for a small amount to give the main thread time
5200 * to handle these events. The SDL queue can hold up to 128 events. */
5201 Log(("PushNotifyUpdateEvent: Sleep 1ms\n"));
5202 RTThreadSleep(1);
5203 }
5204 else
5205 RTThreadYield();
5206}
5207#endif /* VBOXSDL_WITH_X11 */
5208
5209/**
5210 *
5211 */
5212static void SetFullscreen(bool enable)
5213{
5214 if (enable == gpFramebuffer[0]->getFullscreen())
5215 return;
5216
5217 if (!gfFullscreenResize)
5218 {
5219 /*
5220 * The old/default way: SDL will resize the host to fit the guest screen resolution.
5221 */
5222 gpFramebuffer[0]->setFullscreen(enable);
5223 }
5224 else
5225 {
5226 /*
5227 * The alternate way: Switch to fullscreen with the host screen resolution and adapt
5228 * the guest screen resolution to the host window geometry.
5229 */
5230 uint32_t NewWidth = 0, NewHeight = 0;
5231 if (enable)
5232 {
5233 /* switch to fullscreen */
5234 gmGuestNormalXRes = gpFramebuffer[0]->getGuestXRes();
5235 gmGuestNormalYRes = gpFramebuffer[0]->getGuestYRes();
5236 gpFramebuffer[0]->getFullscreenGeometry(&NewWidth, &NewHeight);
5237 }
5238 else
5239 {
5240 /* switch back to saved geometry */
5241 NewWidth = gmGuestNormalXRes;
5242 NewHeight = gmGuestNormalYRes;
5243 }
5244 if (NewWidth != 0 && NewHeight != 0)
5245 {
5246 gpFramebuffer[0]->setFullscreen(enable);
5247 gfIgnoreNextResize = TRUE;
5248 gpDisplay->SetVideoModeHint(0 /*=display*/, true /*=enabled*/,
5249 false /*=changeOrigin*/, 0 /*=originX*/, 0 /*=originY*/,
5250 NewWidth, NewHeight, 0 /*don't change bpp*/, true /*=notify*/);
5251 }
5252 }
5253}
5254
5255#ifdef VBOX_WITH_SDL2
5256static VBoxSDLFB *getFbFromWinId(Uint32 id)
5257{
5258 for (unsigned i = 0; i < gcMonitors; i++)
5259 if (gpFramebuffer[i]->hasWindow(id))
5260 return gpFramebuffer[i];
5261
5262 return NULL;
5263}
5264#endif
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