VirtualBox

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

Last change on this file since 98305 was 98305, checked in by vboxsync, 23 months ago

FE/SDL. bugref:9449. Removing VBOX_WITH_SDL2 define.

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