VirtualBox

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

Last change on this file since 17580 was 17255, checked in by vboxsync, 16 years ago

#3551: “Main: Replace remaining collections with safe arrays”
Replaced HostFloppyDriveCollection; tested by lelik with (GASP!) real floppy hardware.

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