VirtualBox

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

Last change on this file since 47972 was 46649, checked in by vboxsync, 11 years ago

Forward ported r85941 and required build fixes (Main: Implemented new event queue to separate system's native event queue and our own. Also, XPCOM is not needed for handling our own events. On Windows this also fixes the system's queue quota limitation).

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