VirtualBox

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

Last change on this file since 26309 was 26089, checked in by vboxsync, 15 years ago

Branding: Moved the Product & Vendor strings to kBuild, so it could be used
there as well. Added a Copyright year define which points to the current year.
All this should be used on more places. For now the help strings of the
Frontends and most strings of the Mac OS X installer are updated.

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