VirtualBox

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

Last change on this file since 8154 was 8154, checked in by vboxsync, 17 years ago

FE/SDL: introduced -seclabelofs

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

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