VirtualBox

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

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

FE/SDL: Attach or allocate a console, now that we're running on the WINDOWS subsystem. Also re-route the standard output handles to the parent handles, if available. ​bugref:9449

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