VirtualBox

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

Last change on this file since 96402 was 96402, checked in by vboxsync, 2 years ago

/Config.kmk and many other places: Change VBOX_VENDOR to the official copyright holder text, needs follow-up changes and equivalent adjustments elsewhere.

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

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