VirtualBox

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

Last change on this file since 55214 was 55214, checked in by vboxsync, 10 years ago

Main/Console+Machine+Session+Snapshot: move the save state and snapshot related methods from IConsole to IMachine, with lots of unavoidable code restructuring and cleanup. Also define two new machine states (so that the "Saving" one is specifically for saving state now) which requires more changes everywhere
Frontends: necessary adjustments
doc/SDK: document the changes

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette