VirtualBox

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

Last change on this file since 34645 was 34645, checked in by vboxsync, 14 years ago

Frontends/VBoxSDL: variable naming cleanup and other minor cleanups

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

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