VirtualBox

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

Last change on this file since 94750 was 93901, checked in by vboxsync, 3 years ago

VMM,Main,++: Removed VM_IS_RAW_MODE_ENABLED/VM_EXEC_ENGINE_RAW_MODE and added VM_IS_EXEC_ENGINE_IEM/VM_EXEC_ENGINE_IEM instead. In IMachineDebugger::getExecutionEngine VMExecutionEngine_RawMode was removed and VMExecutionEngine_Emulated added. Removed dead code and updated frontends accordingly. On darwin.arm64 HM now falls back on IEM execution since neither HM or NEM is availble there. bugref:9898

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