VirtualBox

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

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

FE/SDL. bugref:9449. scm fix.

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