VirtualBox

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

Last change on this file since 456 was 445, checked in by vboxsync, 18 years ago

New argument: -warpdrive <2..20000>

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 122.0 KB
Line 
1/** @file
2 * VBox frontends: VBoxSDL (simple frontend based on SDL):
3 * Main code
4 */
5
6/*
7 * Copyright (C) 2006 InnoTek Systemberatung GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
20 */
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25#define LOG_GROUP LOG_GROUP_GUI
26
27#include <VBox/com/com.h>
28#include <VBox/com/string.h>
29#include <VBox/com/Guid.h>
30#include <VBox/com/ErrorInfo.h>
31#include <VBox/com/EventQueue.h>
32#include <VBox/com/VirtualBox.h>
33
34using namespace com;
35
36#if defined (__LINUX__)
37#include <X11/Xlib.h>
38#include <X11/cursorfont.h> /* for XC_left_ptr */
39#include <X11/Xcursor/Xcursor.h>
40#include <SDL_syswm.h> /* for SDL_GetWMInfo() */
41#endif
42
43#include "VBoxSDL.h"
44#include "Framebuffer.h"
45#include "Helper.h"
46
47#include <VBox/types.h>
48#include <VBox/err.h>
49#include <VBox/param.h>
50#include <VBox/log.h>
51#include <VBox/version.h>
52#include <iprt/path.h>
53#include <iprt/string.h>
54#include <iprt/runtime.h>
55#include <iprt/assert.h>
56#include <iprt/semaphore.h>
57#include <iprt/stream.h>
58#include <iprt/uuid.h>
59#include <iprt/ldr.h>
60
61#include <stdlib.h> /* for alloca */
62#include <malloc.h> /* for alloca */
63#include <signal.h>
64
65#include <vector>
66
67/* Xlib would re-define our enums */
68#undef True
69#undef False
70
71/*******************************************************************************
72* Defined Constants And Macros *
73*******************************************************************************/
74#ifdef VBOX_SECURELABEL
75/** extra data key for the secure label */
76#define VBOXSDL_SECURELABEL_EXTRADATA "VBoxSDL/SecureLabel"
77/** label area height in pixels */
78#define SECURE_LABEL_HEIGHT 20
79#endif
80
81/** Enables the rawr[0|3], patm, and casm options. */
82#define VBOXSDL_ADVANCED_OPTIONS
83
84/*******************************************************************************
85* Structures and Typedefs *
86*******************************************************************************/
87/** Pointer shape change event data strucure */
88struct PointerShapeChangeData
89{
90 PointerShapeChangeData (BOOL aVisible, BOOL aAlpha, ULONG aXHot, ULONG aYHot,
91 ULONG aWidth, ULONG aHeight, const uint8_t *aShape)
92 : visible (aVisible), alpha (aAlpha), xHot (aXHot), yHot (aYHot),
93 width (aWidth), height (aHeight), shape (NULL)
94 {
95 // make a copy of the shape
96 if (aShape)
97 {
98 uint32_t shapeSize = ((((aWidth + 7) / 8) * aHeight + 3) & ~3) + aWidth * 4 * aHeight;
99 shape = new uint8_t [shapeSize];
100 if (shape)
101 memcpy ((void *) shape, (void *) aShape, shapeSize);
102 }
103 }
104
105 ~PointerShapeChangeData()
106 {
107 if (shape) delete[] shape;
108 }
109
110 const BOOL visible;
111 const BOOL alpha;
112 const ULONG xHot;
113 const ULONG yHot;
114 const ULONG width;
115 const ULONG height;
116 const uint8_t *shape;
117};
118
119enum TitlebarMode
120{
121 TITLEBAR_NORMAL = 1,
122 TITLEBAR_STARTUP = 2,
123 TITLEBAR_SAVE = 3,
124 TITLEBAR_SNAPSHOT = 4
125};
126
127/*******************************************************************************
128* Internal Functions *
129*******************************************************************************/
130static bool UseAbsoluteMouse(void);
131static void ResetKeys(void);
132static uint8_t Keyevent2Keycode(const SDL_KeyboardEvent *ev);
133static void ProcessKey(SDL_KeyboardEvent *ev);
134static void InputGrabStart(void);
135static void InputGrabEnd(void);
136static void SendMouseEvent(int dz, int button, int down);
137static void UpdateTitlebar(TitlebarMode mode, uint32_t u32User = 0);
138static void SetPointerShape(const PointerShapeChangeData *data);
139static void HandleGuestCapsChanged(void);
140static int HandleHostKey(const SDL_KeyboardEvent *pEv);
141static Uint32 StartupTimer(Uint32 interval, void *param);
142
143
144/*******************************************************************************
145* Global Variables *
146*******************************************************************************/
147#if defined (DEBUG_dmik)
148// my mini kbd doesn't have RCTRL...
149static int gHostKeyMod = KMOD_RSHIFT;
150static int gHostKeySym1 = SDLK_RSHIFT;
151static int gHostKeySym2 = SDLK_UNKNOWN;
152#else
153static int gHostKeyMod = KMOD_RCTRL;
154static int gHostKeySym1 = SDLK_RCTRL;
155static int gHostKeySym2 = SDLK_UNKNOWN;
156#endif
157static BOOL gfGrabbed = FALSE;
158static BOOL gfGrabOnMouseClick = TRUE;
159static BOOL gfAllowFullscreenToggle = TRUE;
160static BOOL gfAbsoluteMouseHost = FALSE;
161static BOOL gfAbsoluteMouseGuest = FALSE;
162static BOOL gfGuestNeedsHostCursor = FALSE;
163static BOOL gfOffCursorActive = FALSE;
164static BOOL gfGuestNumLockPressed = FALSE;
165static BOOL gfGuestCapsLockPressed = FALSE;
166static BOOL gfGuestScrollLockPressed = FALSE;
167static int gcGuestNumLockAdaptions = 2;
168static int gcGuestCapsLockAdaptions = 2;
169
170/** modifier keypress status (scancode as index) */
171static uint8_t gaModifiersState[256];
172
173static ComPtr<IMachine> gMachine;
174static ComPtr<IConsole> gConsole;
175static ComPtr<IMachineDebugger> gMachineDebugger;
176static ComPtr<IKeyboard> gKeyboard;
177static ComPtr<IMouse> gMouse;
178static ComPtr<IDisplay> gDisplay;
179static ComPtr<IVRDPServer> gVrdpServer;
180static ComPtr<IProgress> gProgress;
181
182static VBoxSDLFB *gpFrameBuffer = NULL;
183static SDL_Cursor *gpDefaultCursor = NULL;
184#ifdef __LINUX__
185static Cursor gpDefaultOrigX11Cursor;
186#endif
187static SDL_Cursor *gpCustomCursor = NULL;
188static WMcursor *gpCustomOrigWMcursor = NULL;
189static SDL_Cursor *gpOffCursor = NULL;
190
191#ifdef __LINUX__
192static SDL_SysWMinfo gSdlInfo;
193#endif
194
195#ifdef VBOX_SECURELABEL
196#ifdef __WIN__
197#define LIBSDL_TTF_NAME "SDL_ttf"
198#else
199#define LIBSDL_TTF_NAME "libSDL_ttf"
200#endif
201RTLDRMOD gLibrarySDL_ttf = NIL_RTLDRMOD;
202#endif
203
204/**
205 * Callback handler for VirtualBox events
206 */
207class VBoxSDLCallback :
208 public IVirtualBoxCallback
209{
210public:
211 VBoxSDLCallback()
212 {
213#if defined (__WIN__)
214 refcnt = 0;
215#endif
216 }
217
218 virtual ~VBoxSDLCallback()
219 {
220 }
221
222#ifdef __WIN__
223 STDMETHOD_(ULONG, AddRef)()
224 {
225 return ::InterlockedIncrement(&refcnt);
226 }
227 STDMETHOD_(ULONG, Release)()
228 {
229 long cnt = ::InterlockedDecrement(&refcnt);
230 if (cnt == 0)
231 delete this;
232 return cnt;
233 }
234 STDMETHOD(QueryInterface)(REFIID riid , void **ppObj)
235 {
236 if (riid == IID_IUnknown)
237 {
238 *ppObj = this;
239 AddRef();
240 return S_OK;
241 }
242 if (riid == IID_IVirtualBoxCallback)
243 {
244 *ppObj = this;
245 AddRef();
246 return S_OK;
247 }
248 *ppObj = NULL;
249 return E_NOINTERFACE;
250 }
251#endif
252
253 NS_DECL_ISUPPORTS
254
255 STDMETHOD(OnMachineStateChange)(INPTR GUIDPARAM machineId, MachineState_T state)
256 {
257 return S_OK;
258 }
259
260 STDMETHOD(OnMachineDataChange)(INPTR GUIDPARAM machineId)
261 {
262 return S_OK;
263 }
264
265 STDMETHOD(OnExtraDataCanChange)(INPTR GUIDPARAM machineId, INPTR BSTR key, INPTR BSTR value,
266 BOOL *changeAllowed)
267 {
268 /* we never disagree */
269 if (!changeAllowed)
270 return E_INVALIDARG;
271 *changeAllowed = true;
272 return S_OK;
273 }
274
275 STDMETHOD(OnExtraDataChange)(INPTR GUIDPARAM machineId, INPTR BSTR key, INPTR BSTR value)
276 {
277#ifdef VBOX_SECURELABEL
278 Assert(key);
279 /*
280 * check if we're interested in the message
281 */
282 Guid ourGuid;
283 Guid messageGuid = machineId;
284 gMachine->COMGETTER(Id)(ourGuid.asOutParam());
285 if (ourGuid == messageGuid)
286 {
287 Bstr keyString = key;
288 if (keyString && keyString == VBOXSDL_SECURELABEL_EXTRADATA)
289 {
290 /*
291 * Notify SDL thread of the string update
292 */
293 SDL_Event event = {0};
294 event.type = SDL_USEREVENT;
295 event.user.type = SDL_USER_EVENT_SECURELABEL_UPDATE;
296 int rc = SDL_PushEvent(&event);
297 NOREF(rc);
298 AssertMsg(!rc, ("SDL_PushEvent returned with SDL error '%s'\n", SDL_GetError()));
299 }
300 }
301#endif /* VBOX_SECURELABEL */
302 return S_OK;
303 }
304
305 STDMETHOD(OnMachineRegistered)(INPTR GUIDPARAM machineId, BOOL registered)
306 {
307 return S_OK;
308 }
309
310 STDMETHOD(OnSessionStateChange)(INPTR GUIDPARAM machineId, SessionState_T state)
311 {
312 return S_OK;
313 }
314
315 STDMETHOD(OnSnapshotTaken) (INPTR GUIDPARAM aMachineId, INPTR GUIDPARAM aSnapshotId)
316 {
317 return S_OK;
318 }
319
320 STDMETHOD(OnSnapshotDiscarded) (INPTR GUIDPARAM aMachineId, INPTR GUIDPARAM aSnapshotId)
321 {
322 return S_OK;
323 }
324
325 STDMETHOD(OnSnapshotChange) (INPTR GUIDPARAM aMachineId, INPTR GUIDPARAM aSnapshotId)
326 {
327 return S_OK;
328 }
329
330private:
331#ifdef __WIN__
332 long refcnt;
333#endif
334
335};
336
337/**
338 * Callback handler for machine events
339 */
340class VBoxSDLConsoleCallback :
341 public IConsoleCallback
342{
343public:
344 VBoxSDLConsoleCallback() : m_fIgnorePowerOffEvents(false)
345 {
346#if defined (__WIN__)
347 refcnt = 0;
348#endif
349 }
350
351 virtual ~VBoxSDLConsoleCallback()
352 {
353 }
354
355#ifdef __WIN__
356 STDMETHOD_(ULONG, AddRef)()
357 {
358 return ::InterlockedIncrement(&refcnt);
359 }
360 STDMETHOD_(ULONG, Release)()
361 {
362 long cnt = ::InterlockedDecrement(&refcnt);
363 if (cnt == 0)
364 delete this;
365 return cnt;
366 }
367 STDMETHOD(QueryInterface)(REFIID riid , void **ppObj)
368 {
369 if (riid == IID_IUnknown)
370 {
371 *ppObj = this;
372 AddRef();
373 return S_OK;
374 }
375 if (riid == IID_IConsoleCallback)
376 {
377 *ppObj = this;
378 AddRef();
379 return S_OK;
380 }
381 *ppObj = NULL;
382 return E_NOINTERFACE;
383 }
384#endif
385
386 NS_DECL_ISUPPORTS
387
388 STDMETHOD(OnMousePointerShapeChange) (BOOL visible, BOOL alpha, ULONG xHot, ULONG yHot,
389 ULONG width, ULONG height, ULONG shape)
390 {
391 PointerShapeChangeData *data;
392 data = new PointerShapeChangeData (visible, alpha, xHot, yHot, width, height,
393 (const uint8_t *) shape);
394 Assert (data);
395 if (!data)
396 return E_FAIL;
397
398 SDL_Event event = {0};
399 event.type = SDL_USEREVENT;
400 event.user.type = SDL_USER_EVENT_POINTER_CHANGE;
401 event.user.data1 = data;
402
403 int rc = SDL_PushEvent (&event);
404 AssertMsg(!rc, ("SDL_PushEvent returned with SDL error '%s'\n", SDL_GetError()));
405 if (rc)
406 delete data;
407
408 return S_OK;
409 }
410
411 STDMETHOD(OnMouseCapabilityChange)(BOOL supportsAbsolute, BOOL needsHostCursor)
412 {
413 LogFlow(("OnMouseCapabilityChange: supportsAbsolute = %d\n", supportsAbsolute));
414 gfAbsoluteMouseGuest = supportsAbsolute;
415 gfGuestNeedsHostCursor = needsHostCursor;
416
417 SDL_Event event = {0};
418 event.type = SDL_USEREVENT;
419 event.user.type = SDL_USER_EVENT_GUEST_CAP_CHANGED;
420
421 int rc = SDL_PushEvent (&event);
422 NOREF(rc);
423 AssertMsg(!rc, ("SDL_PushEvent returned with SDL error '%s'\n", SDL_GetError()));
424 return S_OK;
425 }
426
427 STDMETHOD(OnStateChange)(MachineState_T machineState)
428 {
429 LogFlow(("OnStateChange: machineState = %d (%s)\n", machineState, GetStateName(machineState)));
430 SDL_Event event = {0};
431
432 if ( machineState == MachineState_Aborted
433 || (machineState == MachineState_Saved && !m_fIgnorePowerOffEvents)
434 || (machineState == MachineState_PoweredOff && !m_fIgnorePowerOffEvents))
435 {
436 /*
437 * We have to inform the SDL thread that the application has be terminated
438 */
439 event.type = SDL_USEREVENT;
440 event.user.type = SDL_USER_EVENT_TERMINATE;
441 event.user.code = machineState == MachineState_Aborted
442 ? VBOXSDL_TERM_ABEND
443 : VBOXSDL_TERM_NORMAL;
444 }
445 else
446 {
447 /*
448 * Inform the SDL thread to refresh the titlebar
449 */
450 event.type = SDL_USEREVENT;
451 event.user.type = SDL_USER_EVENT_UPDATE_TITLEBAR;
452 }
453
454 int rc = SDL_PushEvent(&event);
455 NOREF(rc);
456 AssertMsg(!rc, ("SDL_PushEvent returned with SDL error '%s'\n", SDL_GetError()));
457 return S_OK;
458 }
459
460 STDMETHOD(OnAdditionsStateChange)()
461 {
462 return S_OK;
463 }
464
465 STDMETHOD(OnKeyboardLedsChange)(BOOL fNumLock, BOOL fCapsLock, BOOL fScrollLock)
466 {
467 /* Don't bother the guest with NumLock scancodes if he doesn't set the NumLock LED */
468 if (gfGuestNumLockPressed != fNumLock)
469 gcGuestNumLockAdaptions = 2;
470 if (gfGuestCapsLockPressed != fCapsLock)
471 gcGuestCapsLockAdaptions = 2;
472 gfGuestNumLockPressed = fNumLock;
473 gfGuestCapsLockPressed = fCapsLock;
474 gfGuestScrollLockPressed = fScrollLock;
475 return S_OK;
476 }
477
478 STDMETHOD(OnRuntimeError)(BOOL fatal, INPTR BSTR id, INPTR BSTR message)
479 {
480 return S_OK;
481 }
482
483 static const char *GetStateName(MachineState_T machineState)
484 {
485 switch (machineState)
486 {
487 case MachineState_InvalidMachineState: return "InvalidMachineState";
488 case MachineState_Running: return "Running";
489 case MachineState_Restoring: return "Restoring";
490 case MachineState_Starting: return "Starting";
491 case MachineState_PoweredOff: return "PoweredOff";
492 case MachineState_Saved: return "Saved";
493 case MachineState_Aborted: return "Aborted";
494 case MachineState_Stopping: return "Stopping";
495 default: return "no idea";
496 }
497 }
498
499 void ignorePowerOffEvents(bool fIgnore)
500 {
501 m_fIgnorePowerOffEvents = fIgnore;
502 }
503
504private:
505#ifdef __WIN__
506 long refcnt;
507#endif
508 bool m_fIgnorePowerOffEvents;
509};
510
511#ifdef __LINUX__
512NS_DECL_CLASSINFO(VBoxSDLCallback)
513NS_IMPL_ISUPPORTS1_CI(VBoxSDLCallback, IVirtualBoxCallback)
514NS_DECL_CLASSINFO(VBoxSDLConsoleCallback)
515NS_IMPL_ISUPPORTS1_CI(VBoxSDLConsoleCallback, IConsoleCallback)
516#endif /* __LINUX__ */
517
518static void show_usage()
519{
520 RTPrintf("Usage:\n"
521 " -list List all registered virtual machines and exit\n"
522 " -vm <id|name> Virtual machine to start, either UUID or name\n"
523 " -hda <file> Set temporary first hard disk to file\n"
524 " -fda <file> Set temporary first floppy disk to file\n"
525 " -cdrom <file> Set temporary CDROM/DVD to file/device ('none' to unmount)\n"
526 " -boot <a|c|d> Set temporary boot device (a = floppy, c = first hard disk, d = DVD)\n"
527 " -m <size> Set temporary memory size in megabytes\n"
528 " -vram <size> Set temporary size of video memory in megabytes\n"
529 " -fullscreen Start VM in fullscreen mode\n"
530 " -fixedmode <w> <h> <bpp> Use a fixed SDL video mode with given width, height and bits per pixel\n"
531 " -nofstoggle Forbid switching to/from fullscreen mode\n"
532 " -noresize Make the SDL frame non resizable\n"
533 " -nohostkey Disable hostkey\n"
534 " -nograbonclick Disable mouse/keyboard grabbing on mouse click w/o additions\n"
535 " -detecthostkey Get the hostkey identifier and modifier state\n"
536 " -hostkey <key> {<key2>} <mod> Set the host key to the values obtained using -detecthostkey\n"
537#ifdef __LINUX__
538 " -tapdev<1-N> <dev> Use existing persistent TAP device with the given name\n"
539 " -tapfd<1-N> <fd> Use existing TAP device, don't allocate\n"
540#endif
541#ifdef VBOX_VRDP
542 " -vrdp <port> Listen for VRDP connections on port (default if not specified)\n"
543#endif
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 " -seclabelfgcol <rgb> Secure label text color RGB value in 6 digit hexadecimal (eg: FFFF00)\n"
550 " -seclabelbgcol <rgb> Secure label background color RGB value in 6 digit hexadecimal (eg: FF0000)\n"
551#endif
552#ifdef VBOXSDL_ADVANCED_OPTIONS
553 " -[no]rawr0 Enable or disable raw ring 3\n"
554 " -[no]rawr3 Enable or disable raw ring 0\n"
555 " -[no]patm Enable or disable PATM\n"
556 " -[no]csam Enable or disable CSAM\n"
557 " -[no]hwvirtex Permit or deny the usage of VMX/SVN\n"
558 " -warpdrive <1..20000> The virtual time rate as percent, with 100 as the normal rate.\n"
559#endif
560 "\n");
561}
562
563static void PrintError(const char *pszName, const BSTR pwszDescr, const BSTR pwszComponent=NULL)
564{
565 const char *pszFile, *pszFunc, *pszStat;
566 char pszBuffer[1024];
567 com::ErrorInfo info;
568
569 RTStrPrintf(pszBuffer, sizeof(pszBuffer), "%lS", pwszDescr);
570
571 RTPrintf("\n%s! Error info:\n", pszName);
572 if ( (pszFile = strstr(pszBuffer, "At '"))
573 && (pszFunc = strstr(pszBuffer, ") in "))
574 && (pszStat = strstr(pszBuffer, "VBox status code: ")))
575 RTPrintf(" %.*s %.*s\n In%.*s %s",
576 pszFile-pszBuffer, pszBuffer,
577 pszFunc-pszFile+1, pszFile,
578 pszStat-pszFunc-4, pszFunc+4,
579 pszStat);
580 else
581 RTPrintf("%s\n", pszBuffer);
582
583 if (pwszComponent)
584 RTPrintf("(component %lS).\n", pwszComponent);
585
586 RTPrintf("\n");
587}
588
589#ifdef __LINUX__
590/**
591 * Custom signal handler. Currently it is only used to release modifier
592 * keys when receiving the USR1 signal. When switching VTs, we might not
593 * get release events for Ctrl-Alt and in case a savestate is performed
594 * on the new VT, the VM will be saved with modifier keys stuck. This is
595 * annoying enough for introducing this hack.
596 */
597void signal_handler(int sig, siginfo_t *info, void *secret)
598{
599 /* only SIGUSR1 is interesting */
600 if (sig == SIGUSR1)
601 {
602 /* just release the modifiers */
603 ResetKeys();
604 }
605}
606#endif /* __LINUX__ */
607
608/** entry point */
609int main(int argc, char *argv[])
610{
611 /*
612 * Before we do *anything*, we initialize the runtime.
613 */
614 int rcRT = RTR3Init(true, ~(size_t)0);
615 if (VBOX_FAILURE(rcRT))
616 {
617 RTPrintf("Error: RTR3Init failed rcRC=%d\n", rcRT);
618 return 1;
619 }
620
621 /*
622 * the hostkey detection mode is unrelated to VM processing, so handle it before
623 * we initialize anything COM related
624 */
625 if (argc == 2 && !strcmp(argv[1], "-detecthostkey"))
626 {
627 int rc = SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE);
628 if (rc != 0)
629 {
630 RTPrintf("Error: SDL_InitSubSystem failed with message '%s'\n", SDL_GetError());
631 return 1;
632 }
633 /* we need a video window for the keyboard stuff to work */
634 if (!SDL_SetVideoMode(640, 480, 16, SDL_SWSURFACE))
635 {
636 RTPrintf("Error: could not set SDL video mode\n");
637 return 1;
638 }
639
640 RTPrintf("Please hit one or two function key(s) to get the -hostkey value...\n");
641
642 SDL_Event event1;
643 while (SDL_WaitEvent(&event1))
644 {
645 if (event1.type == SDL_KEYDOWN)
646 {
647 SDL_Event event2;
648 unsigned mod = SDL_GetModState() & ~(KMOD_MODE | KMOD_NUM | KMOD_RESERVED);
649 while (SDL_WaitEvent(&event2))
650 {
651 if (event2.type == SDL_KEYDOWN || event2.type == SDL_KEYUP)
652 {
653 /* pressed additional host key */
654 RTPrintf("-hostkey %d", event1.key.keysym.sym);
655 if (event2.type == SDL_KEYDOWN)
656 {
657 RTPrintf(" %d", event2.key.keysym.sym);
658 RTPrintf(" %d\n", SDL_GetModState() & ~(KMOD_MODE | KMOD_NUM | KMOD_RESERVED));
659 }
660 else
661 {
662 RTPrintf(" %d\n", mod);
663 }
664 /* we're done */
665 break;
666 }
667 }
668 /* we're down */
669 break;
670 }
671 }
672 SDL_Quit();
673 return 1;
674 }
675
676 HRESULT rc;
677 Guid uuid;
678 char *vmName = NULL;
679 DeviceType_T bootDevice = DeviceType_NoDevice;
680 uint32_t memorySize = 0;
681 uint32_t vramSize = 0;
682 VBoxSDLCallback *callback = NULL;
683 VBoxSDLConsoleCallback *consoleCallback = NULL;
684 bool fFullscreen = false;
685 bool fResizable = true;
686 bool fXPCOMEventThreadSignaled = false;
687 bool fListVMs = false;
688 char *hdaFile = NULL;
689 char *cdromFile = NULL;
690 char *fdaFile = NULL;
691#ifdef VBOX_VRDP
692 int portVRDP = ~0;
693#endif
694 bool fDiscardState = false;
695#ifdef VBOX_SECURELABEL
696 BOOL fSecureLabel = false;
697 uint32_t secureLabelPointSize = 12;
698 char *secureLabelFontFile = NULL;
699 uint32_t secureLabelColorFG = 0x0000FF00;
700 uint32_t secureLabelColorBG = 0x00FFFF00;
701#endif
702#ifdef VBOXSDL_ADVANCED_OPTIONS
703 unsigned fRawR0 = ~0U;
704 unsigned fRawR3 = ~0U;
705 unsigned fPATM = ~0U;
706 unsigned fCSAM = ~0U;
707 TriStateBool_T fHWVirt = TriStateBool_Default;
708 uint32_t u32WarpDrive = 0;
709#endif
710#ifdef VBOX_WIN32_UI
711 bool fWin32UI = false;
712#endif
713 bool fShowSDLConfig = false;
714 uint32_t fixedWidth = ~(uint32_t)0;
715 uint32_t fixedHeight = ~(uint32_t)0;
716 uint32_t fixedBPP = ~(uint32_t)0;
717
718 /* The damned GOTOs forces this to be up here - totally out of place. */
719 /*
720 * Host key handling.
721 *
722 * The golden rule is that host-key combinations should not be seen
723 * by the guest. For instance a CAD should not have any extra RCtrl down
724 * and RCtrl up around itself. Nor should a resume be followed by a Ctrl-P
725 * that could encourage applications to start printing.
726 *
727 * We must not confuse the hostkey processing into any release sequences
728 * either, the host key is supposed to be explicitly pressing one key.
729 *
730 * Quick state diagram:
731 *
732 * host key down alone
733 * (Normal) ---------------
734 * ^ ^ |
735 * | | v host combination key down
736 * | | (Host key down) ----------------
737 * | | host key up v | |
738 * | |-------------- | other key down v host combination key down
739 * | | (host key used) -------------
740 * | | | ^ |
741 * | (not host key)-- | |---------------
742 * | | | | |
743 * | | ---- other |
744 * | modifiers = 0 v v
745 * -----------------------------------------------
746 */
747 enum HKEYSTATE
748 {
749 /** The initial and most common state, pass keystrokes to the guest.
750 * Next state: HKEYSTATE_DOWN
751 * Prev state: Any */
752 HKEYSTATE_NORMAL = 1,
753 /** The first host key was pressed down
754 */
755 HKEYSTATE_DOWN_1ST,
756 /** The second host key was pressed down (if gHostKeySym2 != SDLK_UNKNOWN)
757 */
758 HKEYSTATE_DOWN_2ND,
759 /** The host key has been pressed down.
760 * Prev state: HKEYSTATE_NORMAL
761 * Next state: HKEYSTATE_NORMAL - host key up, capture toggle.
762 * Next state: HKEYSTATE_USED - host key combination down.
763 * Next state: HKEYSTATE_NOT_IT - non-host key combination down.
764 */
765 HKEYSTATE_DOWN,
766 /** A host key combination was pressed.
767 * Prev state: HKEYSTATE_DOWN
768 * Next state: HKEYSTATE_NORMAL - when modifiers are all 0
769 */
770 HKEYSTATE_USED,
771 /** A non-host key combination was attempted. Send hostkey down to the
772 * guest and continue until all modifiers have been released.
773 * Prev state: HKEYSTATE_DOWN
774 * Next state: HKEYSTATE_NORMAL - when modifiers are all 0
775 */
776 HKEYSTATE_NOT_IT
777 } enmHKeyState = HKEYSTATE_NORMAL;
778 /** The host key down event which we have been hiding from the guest.
779 * Used when going from HKEYSTATE_DOWN to HKEYSTATE_NOT_IT. */
780 SDL_Event EvHKeyDown1;
781 SDL_Event EvHKeyDown2;
782
783 LogFlow(("SDL GUI started\n"));
784 RTPrintf("VirtualBox SDL GUI %d.%d.%d built %s %s\n",
785 VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, __DATE__, __TIME__);
786
787 // less than one parameter is not possible
788 if (argc < 2)
789 {
790 show_usage();
791 return 1;
792 }
793
794 rc = com::Initialize();
795 if (FAILED(rc))
796 {
797 RTPrintf("Error: COM initialization failed, rc = 0x%x!\n", rc);
798 return 1;
799 }
800
801 do
802 {
803 // scopes all the stuff till shutdown
804 ////////////////////////////////////////////////////////////////////////////
805
806 ComPtr <IVirtualBox> virtualBox;
807 ComPtr <ISession> session;
808 bool sessionOpened = false;
809
810 rc = virtualBox.createLocalObject (CLSID_VirtualBox,
811 "VirtualBoxServer");
812 if (FAILED(rc))
813 {
814 com::ErrorInfo info;
815 if (info.isFullAvailable())
816 PrintError("Failed to create VirtualBox object",
817 info.getText().raw(), info.getComponent().raw());
818 else
819 RTPrintf("Failed to create VirtualBox object! No error information available (rc = 0x%x).\n", rc);
820 break;
821 }
822 rc = session.createInprocObject (CLSID_Session);
823 if (FAILED(rc))
824 {
825 RTPrintf("Failed to create session object, rc = 0x%x!\n", rc);
826 break;
827 }
828
829 // create the event queue
830 // (here it is necessary only to process remaining XPCOM/IPC events
831 // after the session is closed)
832 /// @todo
833// EventQueue eventQ;
834
835#ifdef __LINUX__
836 nsCOMPtr<nsIEventQueue> eventQ;
837 NS_GetMainEventQ(getter_AddRefs(eventQ));
838#endif /* __LINUX__ */
839
840 /* Get the number of network adapters */
841 ULONG NetworkAdapterCount = 0;
842 ComPtr <ISystemProperties> sysInfo;
843 virtualBox->COMGETTER(SystemProperties) (sysInfo.asOutParam());
844 sysInfo->COMGETTER (NetworkAdapterCount) (&NetworkAdapterCount);
845
846#ifdef __LINUX__
847 std::vector <Bstr> tapdev (NetworkAdapterCount);
848 std::vector <int> tapfd (NetworkAdapterCount, 0);
849#endif
850
851 // command line argument parsing stuff
852 for (int curArg = 1; curArg < argc; curArg++)
853 {
854 if (strcmp(argv[curArg], "-list") == 0)
855 {
856 fListVMs = true;
857 }
858 else if (strcmp(argv[curArg], "-vm") == 0
859 || strcmp(argv[curArg], "-startvm") == 0)
860 {
861 if (++curArg >= argc)
862 {
863 RTPrintf("Error: VM not specified (UUID or name)!\n");
864 rc = E_FAIL;
865 break;
866 }
867 // first check if a UUID was supplied
868 if (VBOX_FAILURE(RTUuidFromStr(uuid.ptr(), argv[curArg])))
869 {
870 LogFlow(("invalid UUID format, assuming it's a VM name\n"));
871 vmName = argv[curArg];
872 }
873 }
874 else if (strcmp(argv[curArg], "-boot") == 0)
875 {
876 if (++curArg >= argc)
877 {
878 RTPrintf("Error: missing argument for boot drive!\n");
879 rc = E_FAIL;
880 break;
881 }
882 switch (argv[curArg][0])
883 {
884 case 'a':
885 {
886 bootDevice = DeviceType_FloppyDevice;
887 break;
888 }
889
890 case 'c':
891 {
892 bootDevice = DeviceType_HardDiskDevice;
893 break;
894 }
895
896 case 'd':
897 {
898 bootDevice = DeviceType_DVDDevice;
899 break;
900 }
901
902 default:
903 {
904 RTPrintf("Error: wrong argument for boot drive!\n");
905 rc = E_FAIL;
906 break;
907 }
908 }
909 if (FAILED (rc))
910 break;
911 }
912 else if (strcmp(argv[curArg], "-m") == 0)
913 {
914 if (++curArg >= argc)
915 {
916 RTPrintf("Error: missing argument for memory size!\n");
917 rc = E_FAIL;
918 break;
919 }
920 memorySize = atoi(argv[curArg]);
921 }
922 else if (strcmp(argv[curArg], "-vram") == 0)
923 {
924 if (++curArg >= argc)
925 {
926 RTPrintf("Error: missing argument for vram size!\n");
927 rc = E_FAIL;
928 break;
929 }
930 vramSize = atoi(argv[curArg]);
931 }
932 else if (strcmp(argv[curArg], "-fullscreen") == 0)
933 {
934 fFullscreen = true;
935 }
936 else if (strcmp(argv[curArg], "-fixedmode") == 0)
937 {
938 /* three parameters follow */
939 if (curArg + 3 >= argc)
940 {
941 RTPrintf("Error: missing arguments for fixed video mode!\n");
942 rc = E_FAIL;
943 break;
944 }
945 fixedWidth = atoi(argv[++curArg]);
946 fixedHeight = atoi(argv[++curArg]);
947 fixedBPP = atoi(argv[++curArg]);
948 }
949 else if (strcmp(argv[curArg], "-nofstoggle") == 0)
950 {
951 gfAllowFullscreenToggle = FALSE;
952 }
953 else if (strcmp(argv[curArg], "-noresize") == 0)
954 {
955 fResizable = false;
956 }
957 else if (strcmp(argv[curArg], "-nohostkey") == 0)
958 {
959 gHostKeyMod = 0;
960 gHostKeySym1 = 0;
961 }
962 else if (strcmp(argv[curArg], "-nograbonclick") == 0)
963 {
964 gfGrabOnMouseClick = FALSE;
965 }
966 else if (strcmp(argv[curArg], "-hda") == 0)
967 {
968 if (++curArg >= argc)
969 {
970 RTPrintf("Error: missing file name for first hard disk!\n");
971 rc = E_FAIL;
972 break;
973 }
974 /* resolve it. */
975 hdaFile = RTPathRealDup(argv[curArg]);
976 if (!hdaFile)
977 {
978 RTPrintf("Error: The path to the specified harddisk, '%s', could not be resolved.\n", argv[curArg]);
979 rc = E_FAIL;
980 break;
981 }
982 }
983 else if (strcmp(argv[curArg], "-fda") == 0)
984 {
985 if (++curArg >= argc)
986 {
987 RTPrintf("Error: missing file/device name for first floppy disk!\n");
988 rc = E_FAIL;
989 break;
990 }
991 /* resolve it. */
992 fdaFile = RTPathRealDup(argv[curArg]);
993 if (!fdaFile)
994 {
995 RTPrintf("Error: The path to the specified floppy disk, '%s', could not be resolved.\n", argv[curArg]);
996 rc = E_FAIL;
997 break;
998 }
999 }
1000 else if (strcmp(argv[curArg], "-cdrom") == 0)
1001 {
1002 if (++curArg >= argc)
1003 {
1004 RTPrintf("Error: missing file/device name for first hard disk!\n");
1005 rc = E_FAIL;
1006 break;
1007 }
1008 /* resolve it. */
1009 cdromFile = RTPathRealDup(argv[curArg]);
1010 if (!cdromFile)
1011 {
1012 RTPrintf("Error: The path to the specified cdrom, '%s', could not be resolved.\n", argv[curArg]);
1013 rc = E_FAIL;
1014 break;
1015 }
1016 }
1017#ifdef __LINUX__
1018 else if (strncmp(argv[curArg], "-tapdev", 7) == 0)
1019 {
1020 ULONG n = 0;
1021 if (!argv[curArg][7] || ((n = strtoul(&argv[curArg][7], NULL, 10)) < 1) ||
1022 (n > NetworkAdapterCount) || (argc <= (curArg + 1)))
1023 {
1024 RTPrintf("Error: invalid TAP device option!\n");
1025 rc = E_FAIL;
1026 break;
1027 }
1028 tapdev[n - 1] = argv[curArg + 1];
1029 curArg++;
1030 }
1031 else if (strncmp(argv[curArg], "-tapfd", 6) == 0)
1032 {
1033 ULONG n = 0;
1034 if (!argv[curArg][6] || ((n = strtoul(&argv[curArg][6], NULL, 10)) < 1) ||
1035 (n > NetworkAdapterCount) || (argc <= (curArg + 1)))
1036 {
1037 RTPrintf("Error: invalid TAP file descriptor option!\n");
1038 rc = E_FAIL;
1039 break;
1040 }
1041 tapfd[n - 1] = atoi(argv[curArg + 1]);
1042 curArg++;
1043 }
1044#endif /* __LINUX__ */
1045#ifdef VBOX_VRDP
1046 else if (strcmp(argv[curArg], "-vrdp") == 0)
1047 {
1048 // start with the standard VRDP port
1049 portVRDP = 0;
1050
1051 // is there another argument
1052 if (argc > (curArg + 1))
1053 {
1054 // check if the next argument is a number
1055 int port = atoi(argv[curArg + 1]);
1056 if (port > 0)
1057 {
1058 curArg++;
1059 portVRDP = port;
1060 LogFlow(("Using non standard VRDP port %d\n", portVRDP));
1061 }
1062 }
1063 }
1064#endif /* VBOX_VRDP */
1065 else if (strcmp(argv[curArg], "-discardstate") == 0)
1066 {
1067 fDiscardState = true;
1068 }
1069#ifdef VBOX_SECURELABEL
1070 else if (strcmp(argv[curArg], "-securelabel") == 0)
1071 {
1072 fSecureLabel = true;
1073 LogFlow(("Secure labelling turned on\n"));
1074 }
1075 else if (strcmp(argv[curArg], "-seclabelfnt") == 0)
1076 {
1077 if (++curArg >= argc)
1078 {
1079 RTPrintf("Error: missing font file name for secure label!\n");
1080 rc = E_FAIL;
1081 break;
1082 }
1083 secureLabelFontFile = argv[curArg];
1084 }
1085 else if (strcmp(argv[curArg], "-seclabelsiz") == 0)
1086 {
1087 if (++curArg >= argc)
1088 {
1089 RTPrintf("Error: missing font point size for secure label!\n");
1090 rc = E_FAIL;
1091 break;
1092 }
1093 secureLabelPointSize = atoi(argv[curArg]);
1094 }
1095 else if (strcmp(argv[curArg], "-seclabelfgcol") == 0)
1096 {
1097 if (++curArg >= argc)
1098 {
1099 RTPrintf("Error: missing text color value for secure label!\n");
1100 rc = E_FAIL;
1101 break;
1102 }
1103 sscanf(argv[curArg], "%X", &secureLabelColorFG);
1104 }
1105 else if (strcmp(argv[curArg], "-seclabelbgcol") == 0)
1106 {
1107 if (++curArg >= argc)
1108 {
1109 RTPrintf("Error: missing background color value for secure label!\n");
1110 rc = E_FAIL;
1111 break;
1112 }
1113 sscanf(argv[curArg], "%X", &secureLabelColorBG);
1114 }
1115#endif
1116#ifdef VBOXSDL_ADVANCED_OPTIONS
1117 else if (strcmp(argv[curArg], "-rawr0") == 0)
1118 fRawR0 = true;
1119 else if (strcmp(argv[curArg], "-norawr0") == 0)
1120 fRawR0 = false;
1121 else if (strcmp(argv[curArg], "-rawr3") == 0)
1122 fRawR3 = true;
1123 else if (strcmp(argv[curArg], "-norawr3") == 0)
1124 fRawR3 = false;
1125 else if (strcmp(argv[curArg], "-patm") == 0)
1126 fPATM = true;
1127 else if (strcmp(argv[curArg], "-nopatm") == 0)
1128 fPATM = false;
1129 else if (strcmp(argv[curArg], "-csam") == 0)
1130 fCSAM = true;
1131 else if (strcmp(argv[curArg], "-nocsam") == 0)
1132 fCSAM = false;
1133 else if (strcmp(argv[curArg], "-hwvirtex") == 0)
1134 fHWVirt = TriStateBool_True;
1135 else if (strcmp(argv[curArg], "-nohwvirtex") == 0)
1136 fHWVirt = TriStateBool_False;
1137 else if (strcmp(argv[curArg], "-warpdrive") == 0)
1138 {
1139 if (++curArg >= argc)
1140 {
1141 RTPrintf("Error: missing the rate value for the -warpdrive option!\n");
1142 rc = E_FAIL;
1143 break;
1144 }
1145 u32WarpDrive = RTStrToUInt32(argv[curArg]);
1146 if (u32WarpDrive < 2 || u32WarpDrive > 20000)
1147 {
1148 RTPrintf("Error: the warp drive rate is restricted to [2..20000]. (%d)\n", u32WarpDrive);
1149 rc = E_FAIL;
1150 break;
1151 }
1152 }
1153#endif /* VBOXSDL_ADVANCED_OPTIONS */
1154#ifdef VBOX_WIN32_UI
1155 else if (strcmp(argv[curArg], "-win32ui") == 0)
1156 fWin32UI = true;
1157#endif
1158 else if (strcmp(argv[curArg], "-showsdlconfig") == 0)
1159 fShowSDLConfig = true;
1160 else if (strcmp(argv[curArg], "-hostkey") == 0)
1161 {
1162 if (++curArg + 1 >= argc)
1163 {
1164 RTPrintf("Error: not enough arguments for host keys!\n");
1165 rc = E_FAIL;
1166 break;
1167 }
1168 gHostKeySym1 = atoi(argv[curArg++]);
1169 if (curArg + 1 < argc && (argv[curArg+1][0] == '0' || atoi(argv[curArg+1]) > 0))
1170 {
1171 /* two-key sequence as host key specified */
1172 gHostKeySym2 = atoi(argv[curArg++]);
1173 }
1174 gHostKeyMod = atoi(argv[curArg]);
1175 }
1176 /* just show the help screen */
1177 else
1178 {
1179 RTPrintf("Error: unrecognized switch '%s'\n", argv[curArg]);
1180 show_usage();
1181 return 1;
1182 }
1183 }
1184 if (FAILED (rc))
1185 break;
1186
1187 /*
1188 * Are we supposed to display the list of registered VMs?
1189 */
1190 if (fListVMs)
1191 {
1192 RTPrintf("\nList of registered VMs:\n");
1193 /*
1194 * Get the list of all registered VMs
1195 */
1196 ComPtr<IMachineCollection> collection;
1197 rc = virtualBox->COMGETTER(Machines)(collection.asOutParam());
1198 ComPtr<IMachineEnumerator> enumerator;
1199 if (SUCCEEDED(rc))
1200 rc = collection->Enumerate(enumerator.asOutParam());
1201 if (SUCCEEDED(rc))
1202 {
1203 /*
1204 * Iterate through the collection
1205 */
1206 BOOL hasMore = FALSE;
1207 while (enumerator->HasMore(&hasMore), hasMore)
1208 {
1209 ComPtr<IMachine> machine;
1210 rc =enumerator->GetNext(machine.asOutParam());
1211 if ((SUCCEEDED(rc)) && machine)
1212 {
1213 Bstr machineName;
1214 Guid machineGUID;
1215 Bstr settingsFilePath;
1216 ULONG memorySize;
1217 ULONG vramSize;
1218 machine->COMGETTER(Name)(machineName.asOutParam());
1219 machine->COMGETTER(Id)(machineGUID.asOutParam());
1220 machine->COMGETTER(SettingsFilePath)(settingsFilePath.asOutParam());
1221 machine->COMGETTER(MemorySize)(&memorySize);
1222 machine->COMGETTER(VRAMSize)(&vramSize);
1223 Utf8Str machineNameUtf8(machineName);
1224 Utf8Str settingsFilePathUtf8(settingsFilePath);
1225 RTPrintf("\tName: %s\n", machineNameUtf8.raw());
1226 RTPrintf("\tUUID: %s\n", machineGUID.toString().raw());
1227 RTPrintf("\tConfig file: %s\n", settingsFilePathUtf8.raw());
1228 RTPrintf("\tMemory size: %uMB\n", memorySize);
1229 RTPrintf("\tVRAM size: %uMB\n\n", vramSize);
1230 }
1231 }
1232 }
1233 /* terminate application */
1234 goto leave;
1235 }
1236
1237 /*
1238 * Do we have a name but no UUID?
1239 */
1240 if (vmName && uuid.isEmpty())
1241 {
1242 ComPtr<IMachine> aMachine;
1243 Bstr bstrVMName = vmName;
1244 rc = virtualBox->FindMachine(bstrVMName, aMachine.asOutParam());
1245 if ((rc == S_OK) && aMachine)
1246 {
1247 aMachine->COMGETTER(Id)(uuid.asOutParam());
1248 }
1249 else
1250 {
1251 RTPrintf("Error: machine with the given ID not found!\n");
1252 goto leave;
1253 }
1254 }
1255 else if (uuid.isEmpty())
1256 {
1257 RTPrintf("Error: no machine specified!\n");
1258 goto leave;
1259 }
1260
1261 rc = virtualBox->OpenSession(session, uuid);
1262 if (FAILED(rc))
1263 {
1264 com::ErrorInfo info;
1265 if (info.isFullAvailable())
1266 PrintError("Could not open VirtualBox session",
1267 info.getText().raw(), info.getComponent().raw());
1268 goto leave;
1269 }
1270 if (!session)
1271 {
1272 RTPrintf("Could not open VirtualBox session!\n");
1273 goto leave;
1274 }
1275 sessionOpened = true;
1276 // get the VM we're dealing with
1277 session->COMGETTER(Machine)(gMachine.asOutParam());
1278 if (!gMachine)
1279 {
1280 com::ErrorInfo info;
1281 if (info.isFullAvailable())
1282 PrintError("Cannot start VM!",
1283 info.getText().raw(), info.getComponent().raw());
1284 else
1285 RTPrintf("Error: given machine not found!\n");
1286 goto leave;
1287 }
1288 // get the VM console
1289 session->COMGETTER(Console)(gConsole.asOutParam());
1290 if (!gConsole)
1291 {
1292 RTPrintf("Given console not found!\n");
1293 goto leave;
1294 }
1295
1296 /*
1297 * Are we supposed to use a different hard disk file?
1298 */
1299 if (hdaFile)
1300 {
1301 /*
1302 * Strategy: iterate through all registered hard disk
1303 * and see if one of them points to the same file. If
1304 * so, assign it. If not, register a new image and assing
1305 * it to the VM.
1306 */
1307 Bstr hdaFileBstr = hdaFile;
1308 ComPtr<IHardDisk> hardDisk;
1309 ComPtr<IVirtualDiskImage> vdi;
1310 virtualBox->FindVirtualDiskImage(hdaFileBstr, vdi.asOutParam());
1311 if (vdi)
1312 {
1313 vdi.queryInterfaceTo (hardDisk.asOutParam());
1314 }
1315 else
1316 {
1317 /* we've not found the image */
1318 RTPrintf("Registering hard disk image %s\n", hdaFile);
1319 virtualBox->OpenVirtualDiskImage (hdaFileBstr, vdi.asOutParam());
1320 if (vdi)
1321 {
1322 vdi.queryInterfaceTo (hardDisk.asOutParam());
1323 virtualBox->RegisterHardDisk (hardDisk);
1324 }
1325 }
1326 /* do we have the right image now? */
1327 if (hardDisk)
1328 {
1329 /*
1330 * Go and attach it!
1331 */
1332 Guid uuid;
1333 hardDisk->COMGETTER(Id)(uuid.asOutParam());
1334 gMachine->DetachHardDisk(DiskControllerType_IDE0Controller, 0);
1335 gMachine->AttachHardDisk(uuid, DiskControllerType_IDE0Controller, 0);
1336 /// @todo why is this attachment saved?
1337 }
1338 else
1339 {
1340 RTPrintf("Error: failed to mount the specified hard disk image!\n");
1341 goto leave;
1342 }
1343 }
1344
1345 if (fdaFile)
1346 {
1347 ComPtr<IFloppyDrive> floppyDrive;
1348 gMachine->COMGETTER(FloppyDrive)(floppyDrive.asOutParam());
1349 Assert(floppyDrive);
1350
1351 ComPtr<IFloppyImageCollection> collection;
1352 virtualBox->COMGETTER(FloppyImages)(collection.asOutParam());
1353 Assert(collection);
1354 ComPtr<IFloppyImageEnumerator> enumerator;
1355 collection->Enumerate(enumerator.asOutParam());
1356 Assert(enumerator);
1357 ComPtr<IFloppyImage> floppyImage;
1358 BOOL hasMore = false;
1359 while (enumerator->HasMore(&hasMore), hasMore)
1360 {
1361 enumerator->GetNext(floppyImage.asOutParam());
1362 Assert(floppyImage);
1363 Bstr file;
1364 floppyImage->COMGETTER(FilePath)(file.asOutParam());
1365 Assert(file);
1366 /// @todo this will not work on case insensitive systems if the casing does not match the registration!!!
1367 if (file == fdaFile)
1368 break;
1369 else
1370 floppyImage = NULL;
1371 }
1372 /* we've not found the image? */
1373 if (!floppyImage)
1374 {
1375 RTPrintf("Registering floppy disk image %s\n", fdaFile);
1376 Guid uuid;
1377 Bstr fileBstr = fdaFile;
1378 virtualBox->OpenFloppyImage (fileBstr, uuid, floppyImage.asOutParam());
1379 virtualBox->RegisterFloppyImage (floppyImage);
1380 }
1381 /* do we have the right image now? */
1382 if (floppyImage)
1383 {
1384 /*
1385 * Go and attach it!
1386 */
1387 Guid uuid;
1388 floppyImage->COMGETTER(Id)(uuid.asOutParam());
1389 floppyDrive->MountImage(uuid);
1390 }
1391 else
1392 {
1393 RTPrintf("Error: failed to mount the specified floppy disk image!\n");
1394 goto leave;
1395 }
1396 }
1397
1398 /*
1399 * Are we supposed to use a different CDROM image?
1400 */
1401 if (cdromFile)
1402 {
1403 ComPtr<IDVDDrive> dvdDrive;
1404 gMachine->COMGETTER(DVDDrive)(dvdDrive.asOutParam());
1405 Assert(dvdDrive);
1406
1407 /*
1408 * First special case 'none' to unmount
1409 */
1410 if (strcmp(cdromFile, "none") == 0)
1411 {
1412 dvdDrive->Unmount();
1413 }
1414 else
1415 {
1416 /*
1417 * Determine if it's a host device or ISO image
1418 */
1419 bool fHostDrive = false;
1420#ifdef __WIN__
1421 /* two characters with the 2nd being a colon */
1422 if ((strlen(cdromFile) == 2) && (cdromFile[1] == ':'))
1423 {
1424 cdromFile[0] = toupper(cdromFile[0]);
1425 fHostDrive = true;
1426 }
1427#else /* !__WIN__ */
1428 /* it has to start with /dev/ */
1429 if (strncmp(cdromFile, "/dev/", 5) == 0)
1430 fHostDrive = true;
1431#endif /* !__WIN__ */
1432 if (fHostDrive)
1433 {
1434 ComPtr<IHost> host;
1435 virtualBox->COMGETTER(Host)(host.asOutParam());
1436 ComPtr<IHostDVDDriveCollection> collection;
1437 host->COMGETTER(DVDDrives)(collection.asOutParam());
1438 ComPtr<IHostDVDDriveEnumerator> enumerator;
1439 collection->Enumerate(enumerator.asOutParam());
1440 ComPtr<IHostDVDDrive> hostDVDDrive;
1441 BOOL hasMore = FALSE;
1442 while (enumerator->HasMore(&hasMore), hasMore)
1443 {
1444 enumerator->GetNext(hostDVDDrive.asOutParam());
1445 Bstr driveName;
1446 hostDVDDrive->COMGETTER(Name)(driveName.asOutParam());
1447 Utf8Str driveNameUtf8 = driveName;
1448 char *driveNameStr = (char*)driveNameUtf8.raw();
1449 if (strcmp(driveNameStr, cdromFile) == 0)
1450 {
1451 rc = dvdDrive->CaptureHostDrive(hostDVDDrive);
1452 if (rc != S_OK)
1453 {
1454 RTPrintf("Error: could not mount host DVD drive %s! rc = 0x%x\n", driveNameStr, rc);
1455 }
1456 break;
1457 }
1458 }
1459 if (!hasMore)
1460 RTPrintf("Error: did not recognize DVD drive '%s'!\n", cdromFile);
1461 }
1462 else
1463 {
1464 /*
1465 * Same strategy as with the HDD images: check if already registered,
1466 * if not, register on the fly.
1467 */
1468 ComPtr<IDVDImageCollection> collection;
1469 virtualBox->COMGETTER(DVDImages)(collection.asOutParam());
1470 Assert(collection);
1471 ComPtr<IDVDImageEnumerator> enumerator;
1472 collection->Enumerate(enumerator.asOutParam());
1473 Assert(enumerator);
1474 ComPtr<IDVDImage> dvdImage;
1475 BOOL hasMore = false;
1476 while (enumerator->HasMore(&hasMore), hasMore)
1477 {
1478 enumerator->GetNext(dvdImage.asOutParam());
1479 Assert(dvdImage);
1480 Bstr dvdImageFile;
1481 dvdImage->COMGETTER(FilePath)(dvdImageFile.asOutParam());
1482 Assert(dvdImageFile);
1483 /// @todo not correct for case insensitive platforms (win32)
1484 /// See comment on hdaFile.
1485 if (dvdImageFile == cdromFile)
1486 break;
1487 else
1488 dvdImage = NULL;
1489 }
1490 /* we've not found the image? */
1491 if (!dvdImage)
1492 {
1493 RTPrintf("Registering ISO image %s\n", cdromFile);
1494 Guid uuid; // the system will generate UUID
1495 Bstr cdImageFileBstr = cdromFile;
1496 virtualBox->OpenDVDImage(cdImageFileBstr, uuid, dvdImage.asOutParam());
1497 rc = virtualBox->RegisterDVDImage(dvdImage);
1498 if (!SUCCEEDED(rc))
1499 {
1500 RTPrintf("Image registration failed with %08X\n", rc);
1501 }
1502 }
1503 /* do we have the right image now? */
1504 if (dvdImage)
1505 {
1506 /* attach */
1507 Guid uuid;
1508 dvdImage->COMGETTER(Id)(uuid.asOutParam());
1509 dvdDrive->MountImage(uuid);
1510 }
1511 else
1512 {
1513 RTPrintf("Error: failed to mount the specified ISO image!\n");
1514 goto leave;
1515 }
1516 }
1517 }
1518 }
1519
1520 if (fDiscardState)
1521 {
1522 /*
1523 * If the machine is currently saved,
1524 * discard the saved state first.
1525 */
1526 MachineState_T machineState;
1527 gMachine->COMGETTER(State)(&machineState);
1528 if (machineState == MachineState_Saved)
1529 {
1530 CHECK_ERROR(gConsole, DiscardSavedState());
1531 }
1532 /*
1533 * If there are snapshots, discard the current state,
1534 * i.e. revert to the last snapshot.
1535 */
1536 ULONG cSnapshots;
1537 gMachine->COMGETTER(SnapshotCount)(&cSnapshots);
1538 if (cSnapshots)
1539 {
1540 gProgress = NULL;
1541 CHECK_ERROR(gConsole, DiscardCurrentState(gProgress.asOutParam()));
1542 rc = gProgress->WaitForCompletion(-1);
1543 }
1544 }
1545
1546 // get the machine debugger (does not have to be there)
1547 gConsole->COMGETTER(Debugger)(gMachineDebugger.asOutParam());
1548 if (gMachineDebugger)
1549 {
1550 Log(("Machine debugger available!\n"));
1551 }
1552 gConsole->COMGETTER(Display)(gDisplay.asOutParam());
1553 if (!gDisplay)
1554 {
1555 RTPrintf("Error: could not get display object!\n");
1556 goto leave;
1557 }
1558
1559 // set the boot drive
1560 if (bootDevice != DeviceType_NoDevice)
1561 {
1562 rc = gMachine->SetBootOrder(1, bootDevice);
1563 if (rc != S_OK)
1564 {
1565 RTPrintf("Error: could not set boot device, using default.\n");
1566 }
1567 }
1568
1569 // set the memory size if not default
1570 if (memorySize)
1571 {
1572 rc = gMachine->COMSETTER(MemorySize)(memorySize);
1573 if (rc != S_OK)
1574 {
1575 ULONG ramSize = 0;
1576 gMachine->COMGETTER(MemorySize)(&ramSize);
1577 RTPrintf("Error: could not set memory size, using current setting of %d MBytes\n", ramSize);
1578 }
1579 }
1580
1581 if (vramSize)
1582 {
1583 rc = gMachine->COMSETTER(VRAMSize)(vramSize);
1584 if (rc != S_OK)
1585 {
1586 gMachine->COMGETTER(VRAMSize)((ULONG*)&vramSize);
1587 RTPrintf("Error: could not set VRAM size, using current setting of %d MBytes\n", vramSize);
1588 }
1589 }
1590
1591 // we're always able to process absolute mouse events and we prefer that
1592 gfAbsoluteMouseHost = TRUE;
1593
1594#ifdef VBOX_WIN32_UI
1595 if (fWin32UI)
1596 {
1597 /* initialize the Win32 user interface inside which SDL will be embedded */
1598 if (initUI(fResizable))
1599 return 1;
1600 }
1601#endif
1602
1603 // create our SDL framebuffer instance
1604 gpFrameBuffer = new VBoxSDLFB(fFullscreen, fResizable, fShowSDLConfig,
1605 fixedWidth, fixedHeight, fixedBPP);
1606
1607 if (!gpFrameBuffer)
1608 {
1609 RTPrintf("Error: could not create framebuffer object!\n");
1610 goto leave;
1611 }
1612 if (!gpFrameBuffer->initialized())
1613 goto leave;
1614 gpFrameBuffer->AddRef();
1615 if (fFullscreen)
1616 {
1617 gpFrameBuffer->setFullscreen(true);
1618 }
1619#ifdef VBOX_SECURELABEL
1620 if (fSecureLabel)
1621 {
1622 if (!secureLabelFontFile)
1623 {
1624 RTPrintf("Error: no font file specified for secure label!\n");
1625 goto leave;
1626 }
1627 /* load the SDL_ttf library and get the required imports */
1628 int rcVBox;
1629 rcVBox = RTLdrLoad(LIBSDL_TTF_NAME, &gLibrarySDL_ttf);
1630 if (VBOX_SUCCESS(rcVBox))
1631 rcVBox = RTLdrGetSymbol(gLibrarySDL_ttf, "TTF_Init", (void**)&pTTF_Init);
1632 if (VBOX_SUCCESS(rcVBox))
1633 rcVBox = RTLdrGetSymbol(gLibrarySDL_ttf, "TTF_OpenFont", (void**)&pTTF_OpenFont);
1634 if (VBOX_SUCCESS(rcVBox))
1635 rcVBox = RTLdrGetSymbol(gLibrarySDL_ttf, "TTF_RenderUTF8_Solid", (void**)&pTTF_RenderUTF8_Solid);
1636 if (VBOX_SUCCESS(rcVBox))
1637 rcVBox = RTLdrGetSymbol(gLibrarySDL_ttf, "TTF_CloseFont", (void**)&pTTF_CloseFont);
1638 if (VBOX_SUCCESS(rcVBox))
1639 rcVBox = RTLdrGetSymbol(gLibrarySDL_ttf, "TTF_Quit", (void**)&pTTF_Quit);
1640 if (VBOX_SUCCESS(rcVBox))
1641 rcVBox = gpFrameBuffer->initSecureLabel(SECURE_LABEL_HEIGHT, secureLabelFontFile, secureLabelPointSize);
1642 if (VBOX_FAILURE(rcVBox))
1643 {
1644 RTPrintf("Error: could not initialize secure labeling: rc = %Vrc\n", rcVBox);
1645 goto leave;
1646 }
1647 Bstr key = VBOXSDL_SECURELABEL_EXTRADATA;
1648 Bstr label;
1649 gMachine->GetExtraData(key, label.asOutParam());
1650 Utf8Str labelUtf8 = label;
1651 /*
1652 * Now update the label
1653 */
1654 gpFrameBuffer->setSecureLabelColor(secureLabelColorFG, secureLabelColorBG);
1655 gpFrameBuffer->setSecureLabelText(labelUtf8.raw());
1656 }
1657#endif
1658
1659 // register our framebuffer
1660 rc = gDisplay->RegisterExternalFramebuffer(gpFrameBuffer);
1661 if (rc != S_OK)
1662 {
1663 RTPrintf("Error: could not register framebuffer object!\n");
1664 goto leave;
1665 }
1666
1667 // register a callback for global events
1668 callback = new VBoxSDLCallback();
1669 callback->AddRef();
1670 virtualBox->RegisterCallback(callback);
1671
1672 // register a callback for machine events
1673 consoleCallback = new VBoxSDLConsoleCallback();
1674 consoleCallback->AddRef();
1675 gConsole->RegisterCallback(consoleCallback);
1676 // until we've tried to to start the VM, ignore power off events
1677 consoleCallback->ignorePowerOffEvents(true);
1678
1679#ifdef __LINUX__
1680 /*
1681 * Do we have a TAP device name or file descriptor? If so, communicate
1682 * it to the network adapter so that it doesn't allocate a new one
1683 * in case TAP is already configured.
1684 */
1685 {
1686 ComPtr<INetworkAdapter> networkAdapter;
1687 for (ULONG i = 0; i < NetworkAdapterCount; i++)
1688 {
1689 if (tapdev[i] || tapfd[i])
1690 {
1691 gMachine->GetNetworkAdapter(i, networkAdapter.asOutParam());
1692 if (networkAdapter)
1693 {
1694 NetworkAttachmentType_T attachmentType;
1695 networkAdapter->COMGETTER(AttachmentType)(&attachmentType);
1696 if (attachmentType == NetworkAttachmentType_HostInterfaceNetworkAttachment)
1697 {
1698 if (tapdev[i])
1699 networkAdapter->COMSETTER(HostInterface)(tapdev[i]);
1700 else
1701 networkAdapter->COMSETTER(TAPFileDescriptor)(tapfd[i]);
1702 }
1703 else
1704 {
1705 RTPrintf("Warning: network adapter %d is not configured for TAP. Command ignored!\n", i + 1);
1706 }
1707 }
1708 else
1709 {
1710 /* warning */
1711 RTPrintf("Warning: network adapter %d not defined. Command ignored!\n", i + 1);
1712 }
1713 }
1714 }
1715 }
1716#endif /* __LINUX__ */
1717
1718#ifdef VBOX_VRDP
1719 if (portVRDP != ~0)
1720 {
1721 rc = gMachine->COMGETTER(VRDPServer)(gVrdpServer.asOutParam());
1722 AssertMsg((rc == S_OK) && gVrdpServer, ("Could not get VRDP Server! rc = 0x%x\n", rc));
1723 if (gVrdpServer)
1724 {
1725 // has a non standard VRDP port been requested?
1726 if (portVRDP > 0)
1727 {
1728 rc = gVrdpServer->COMSETTER(Port)(portVRDP);
1729 if (rc != S_OK)
1730 {
1731 RTPrintf("Error: could not set VRDP port! rc = 0x%x\n", rc);
1732 goto leave;
1733 }
1734 }
1735 // now enable VRDP
1736 rc = gVrdpServer->COMSETTER(Enabled)(TRUE);
1737 if (rc != S_OK)
1738 {
1739 RTPrintf("Error: could not enable VRDP server! rc = 0x%x\n", rc);
1740 goto leave;
1741 }
1742 }
1743 }
1744#endif
1745
1746 rc = E_FAIL;
1747#ifdef VBOXSDL_ADVANCED_OPTIONS
1748 if (fRawR0 != ~0U)
1749 {
1750 if (!gMachineDebugger)
1751 {
1752 RTPrintf("Error: No debugger object; -%srawr0 cannot be executed!\n", fRawR0 ? "" : "no");
1753 goto leave;
1754 }
1755 gMachineDebugger->COMSETTER(RecompileSupervisor)(!fRawR0);
1756 }
1757 if (fRawR3 != ~0U)
1758 {
1759 if (!gMachineDebugger)
1760 {
1761 RTPrintf("Error: No debugger object; -%srawr3 cannot be executed!\n", fRawR0 ? "" : "no");
1762 goto leave;
1763 }
1764 gMachineDebugger->COMSETTER(RecompileUser)(!fRawR3);
1765 }
1766 if (fPATM != ~0U)
1767 {
1768 if (!gMachineDebugger)
1769 {
1770 RTPrintf("Error: No debugger object; -%spatm cannot be executed!\n", fRawR0 ? "" : "no");
1771 goto leave;
1772 }
1773 gMachineDebugger->COMSETTER(PATMEnabled)(fPATM);
1774 }
1775 if (fCSAM != ~0U)
1776 {
1777 if (!gMachineDebugger)
1778 {
1779 RTPrintf("Error: No debugger object; -%scsam cannot be executed!\n", fRawR0 ? "" : "no");
1780 goto leave;
1781 }
1782 gMachineDebugger->COMSETTER(CSAMEnabled)(fCSAM);
1783 }
1784 if (fHWVirt != TriStateBool_Default)
1785 {
1786 gMachine->COMSETTER(HWVirtExEnabled)(fHWVirt);
1787 }
1788 if (u32WarpDrive != 0)
1789 {
1790 if (!gMachineDebugger)
1791 {
1792 RTPrintf("Error: No debugger object; -warpdrive %d cannot be executed!\n", u32WarpDrive);
1793 goto leave;
1794 }
1795 gMachineDebugger->COMSETTER(VirtualTimeRate)(u32WarpDrive);
1796 }
1797#endif /* VBOXSDL_ADVANCED_OPTIONS */
1798
1799 /* start with something in the titlebar */
1800 UpdateTitlebar(TITLEBAR_NORMAL);
1801
1802 /* memorize the default cursor */
1803 gpDefaultCursor = SDL_GetCursor();
1804
1805#ifdef __LINUX__
1806 /* Get Window Manager info. We only need the X11 display. */
1807 SDL_VERSION(&gSdlInfo.version);
1808 if (!SDL_GetWMInfo(&gSdlInfo))
1809 {
1810 RTPrintf("Error: could not get SDL Window Manager info!\n");
1811 goto leave;
1812 }
1813
1814 /* SDL uses its own (plain) default cursor. Use the left arrow cursor instead which might look
1815 * much better if a mouse cursor theme is installed. */
1816 gpDefaultOrigX11Cursor = *(Cursor*)gpDefaultCursor->wm_cursor;
1817 *(Cursor*)gpDefaultCursor->wm_cursor = XCreateFontCursor(gSdlInfo.info.x11.display, XC_left_ptr);
1818 SDL_SetCursor(gpDefaultCursor);
1819#endif /* __LINUX__ */
1820
1821 /* create a fake empty cursor */
1822 {
1823 uint8_t cursorData[1] = {0};
1824 gpCustomCursor = SDL_CreateCursor(cursorData, cursorData, 8, 1, 0, 0);
1825 gpCustomOrigWMcursor = gpCustomCursor->wm_cursor;
1826 gpCustomCursor->wm_cursor = NULL;
1827 }
1828
1829 /*
1830 * Register our user signal handler.
1831 */
1832#ifdef __LINUX__
1833 struct sigaction sa;
1834 sa.sa_sigaction = signal_handler;
1835 sigemptyset (&sa.sa_mask);
1836 sa.sa_flags = SA_RESTART | SA_SIGINFO;
1837 sigaction (SIGUSR1, &sa, NULL);
1838#endif /* __LINUX__ */
1839
1840 /*
1841 * Start the VM execution thread. This has to be done
1842 * asynchronously as powering up can take some time
1843 * (accessing devices such as the host DVD drive). In
1844 * the meantime, we have to service the SDL event loop.
1845 */
1846 SDL_Event event;
1847
1848 LogFlow(("Powering up the VM...\n"));
1849 rc = gConsole->PowerUp(gProgress.asOutParam());
1850 if (rc != S_OK)
1851 {
1852 com::ErrorInfo info(gConsole);
1853 if (info.isBasicAvailable())
1854 PrintError("Failed to power up VM", info.getText().raw());
1855 else
1856 RTPrintf("Error: failed to power up VM! No error text available.\n");
1857 goto leave;
1858 }
1859
1860#ifdef __LINUX__
1861 /*
1862 * Before we starting to do stuff, we have to launch the XPCOM
1863 * event queue thread. It will wait for events and send messages
1864 * to the SDL thread. After having done this, we should fairly
1865 * quickly start to process the SDL event queue as an XPCOM
1866 * event storm might arrive. Stupid SDL has a ridiculously small
1867 * event queue buffer!
1868 */
1869 startXPCOMEventQueueThread(eventQ->GetEventQueueSelectFD());
1870#endif /** __LINUX__ */
1871
1872 /* termination flag */
1873 bool fTerminateDuringStartup;
1874 fTerminateDuringStartup = false;
1875
1876 /* start regular timer so we don't starve in the event loop */
1877 SDL_TimerID sdlTimer;
1878 sdlTimer = SDL_AddTimer(100, StartupTimer, NULL);
1879
1880 /* loop until the powerup processing is done */
1881 MachineState_T machineState;
1882 do
1883 {
1884 rc = gMachine->COMGETTER(State)(&machineState);
1885 if ( rc == S_OK
1886 && ( machineState == MachineState_Starting
1887 || machineState == MachineState_Restoring))
1888 {
1889 /*
1890 * wait for the next event. This is uncritical as
1891 * power up guarantees to change the machine state
1892 * to either running or aborted and a machine state
1893 * change will send us an event. However, we have to
1894 * service the XPCOM event queue!
1895 */
1896#ifdef __LINUX__
1897 if (!fXPCOMEventThreadSignaled)
1898 {
1899 signalXPCOMEventQueueThread();
1900 fXPCOMEventThreadSignaled = true;
1901 }
1902#endif
1903 /*
1904 * Wait for SDL events.
1905 */
1906 if (SDL_WaitEvent(&event))
1907 {
1908 switch (event.type)
1909 {
1910 /*
1911 * Timer event. Used to have the titlebar updated.
1912 */
1913 case SDL_USER_EVENT_TIMER:
1914 {
1915 /*
1916 * Update the title bar.
1917 */
1918 UpdateTitlebar(TITLEBAR_STARTUP);
1919 break;
1920 }
1921
1922 /*
1923 * User specific resize event.
1924 */
1925 case SDL_USER_EVENT_RESIZE:
1926 {
1927 LogFlow(("SDL_USER_EVENT_RESIZE\n"));
1928 gpFrameBuffer->resizeGuest();
1929 /* notify the display that the resize has been completed */
1930 gDisplay->ResizeCompleted();
1931 break;
1932 }
1933
1934#ifdef __LINUX__
1935 /*
1936 * User specific XPCOM event queue event
1937 */
1938 case SDL_USER_EVENT_XPCOM_EVENTQUEUE:
1939 {
1940 LogFlow(("SDL_USER_EVENT_XPCOM_EVENTQUEUE: processing XPCOM event queue...\n"));
1941 eventQ->ProcessPendingEvents();
1942 signalXPCOMEventQueueThread();
1943 break;
1944 }
1945#endif /* __LINUX__ */
1946
1947 /*
1948 * Termination event from the on state change callback.
1949 */
1950 case SDL_USER_EVENT_TERMINATE:
1951 {
1952 if (event.user.code != VBOXSDL_TERM_NORMAL)
1953 {
1954 com::ProgressErrorInfo info(gProgress);
1955 if (info.isBasicAvailable())
1956 PrintError("Failed to power up VM", info.getText().raw());
1957 else
1958 RTPrintf("Error: failed to power up VM! No error text available.\n");
1959 }
1960 fTerminateDuringStartup = true;
1961 break;
1962 }
1963
1964 default:
1965 {
1966 LogBird(("VBoxSDL: Unknown SDL event %d (pre)\n", event.type));
1967 break;
1968 }
1969 }
1970
1971 }
1972 }
1973 } while ( rc == S_OK
1974 && ( machineState == MachineState_Starting
1975 || machineState == MachineState_Restoring));
1976
1977 /* kill the timer again */
1978 SDL_RemoveTimer(sdlTimer);
1979 sdlTimer = 0;
1980
1981 /* are we supposed to terminate the process? */
1982 if (fTerminateDuringStartup)
1983 goto leave;
1984
1985 /* did the power up succeed? */
1986 if (machineState != MachineState_Running)
1987 {
1988 com::ProgressErrorInfo info(gProgress);
1989 if (info.isBasicAvailable())
1990 PrintError("Failed to power up VM", info.getText().raw());
1991 else
1992 RTPrintf("Error: failed to power up VM! No error text available (rc = 0x%x state = %d)\n", rc, machineState);
1993 goto leave;
1994 }
1995
1996 // accept power off events from now on because we're running
1997 // note that there's a possible race condition here...
1998 consoleCallback->ignorePowerOffEvents(false);
1999
2000 rc = gConsole->COMGETTER(Keyboard)(gKeyboard.asOutParam());
2001 if (!gKeyboard)
2002 {
2003 RTPrintf("Error: could not get keyboard object!\n");
2004 goto leave;
2005 }
2006 gConsole->COMGETTER(Mouse)(gMouse.asOutParam());
2007 if (!gMouse)
2008 {
2009 RTPrintf("Error: could not get mouse object!\n");
2010 goto leave;
2011 }
2012
2013 UpdateTitlebar(TITLEBAR_NORMAL);
2014
2015 /*
2016 * Enable keyboard repeats
2017 */
2018 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
2019
2020 /*
2021 * Main event loop
2022 */
2023#ifdef __LINUX__
2024 if (!fXPCOMEventThreadSignaled)
2025 {
2026 signalXPCOMEventQueueThread();
2027 }
2028#endif
2029 LogFlow(("VBoxSDL: Entering big event loop\n"));
2030 while (SDL_WaitEvent(&event))
2031 {
2032 switch (event.type)
2033 {
2034 /*
2035 * The screen needs to be repainted.
2036 */
2037 case SDL_VIDEOEXPOSE:
2038 {
2039 /// @todo that somehow doesn't seem to work!
2040 gpFrameBuffer->repaint();
2041 break;
2042 }
2043
2044 /*
2045 * Keyboard events.
2046 */
2047 case SDL_KEYDOWN:
2048 case SDL_KEYUP:
2049 {
2050 SDLKey ksym = event.key.keysym.sym;
2051
2052 switch (enmHKeyState)
2053 {
2054 case HKEYSTATE_NORMAL:
2055 {
2056 if ( event.type == SDL_KEYDOWN
2057 && ksym != SDLK_UNKNOWN
2058 && (ksym == gHostKeySym1 || ksym == gHostKeySym2))
2059 {
2060 EvHKeyDown1 = event;
2061 enmHKeyState = ksym == gHostKeySym1 ? HKEYSTATE_DOWN_1ST
2062 : HKEYSTATE_DOWN_2ND;
2063 break;
2064 }
2065 ProcessKey(&event.key);
2066 break;
2067 }
2068
2069 case HKEYSTATE_DOWN_1ST:
2070 case HKEYSTATE_DOWN_2ND:
2071 {
2072 if (gHostKeySym2 != SDLK_UNKNOWN)
2073 {
2074 if ( event.type == SDL_KEYDOWN
2075 && ksym != SDLK_UNKNOWN
2076 && ( enmHKeyState == HKEYSTATE_DOWN_1ST && ksym == gHostKeySym2
2077 || enmHKeyState == HKEYSTATE_DOWN_2ND && ksym == gHostKeySym1))
2078 {
2079 EvHKeyDown2 = event;
2080 enmHKeyState = HKEYSTATE_DOWN;
2081 break;
2082 }
2083 enmHKeyState = event.type == SDL_KEYUP ? HKEYSTATE_NORMAL
2084 : HKEYSTATE_NOT_IT;
2085 ProcessKey(&EvHKeyDown1.key);
2086 ProcessKey(&event.key);
2087 break;
2088 }
2089 /* fall through if no two-key sequence is used */
2090 }
2091
2092 case HKEYSTATE_DOWN:
2093 {
2094 if (event.type == SDL_KEYDOWN)
2095 {
2096 /* potential host key combination, try execute it */
2097 int rc = HandleHostKey(&event.key);
2098 if (rc == VINF_SUCCESS)
2099 {
2100 enmHKeyState = HKEYSTATE_USED;
2101 break;
2102 }
2103 if (VBOX_SUCCESS(rc))
2104 goto leave;
2105 }
2106 else /* SDL_KEYUP */
2107 {
2108 if ( ksym != SDLK_UNKNOWN
2109 && (ksym == gHostKeySym1 || ksym == gHostKeySym2))
2110 {
2111 /* toggle grabbing state */
2112 if (!gfGrabbed)
2113 InputGrabStart();
2114 else
2115 InputGrabEnd();
2116
2117 /* SDL doesn't always reset the keystates, correct it */
2118 ResetKeys();
2119 enmHKeyState = HKEYSTATE_NORMAL;
2120 break;
2121 }
2122 }
2123
2124 /* not host key */
2125 enmHKeyState = HKEYSTATE_NOT_IT;
2126 ProcessKey(&EvHKeyDown1.key);
2127 if (gHostKeySym2 != SDLK_UNKNOWN)
2128 ProcessKey(&EvHKeyDown2.key);
2129 ProcessKey(&event.key);
2130 break;
2131 }
2132
2133 case HKEYSTATE_USED:
2134 {
2135 if ((SDL_GetModState() & ~(KMOD_MODE | KMOD_NUM | KMOD_RESERVED)) == 0)
2136 enmHKeyState = HKEYSTATE_NORMAL;
2137 if (event.type == SDL_KEYDOWN)
2138 {
2139 int rc = HandleHostKey(&event.key);
2140 if (VBOX_SUCCESS(rc) && rc != VINF_SUCCESS)
2141 goto leave;
2142 }
2143 break;
2144 }
2145
2146 default:
2147 AssertMsgFailed(("enmHKeyState=%d\n", enmHKeyState));
2148 /* fall thru */
2149 case HKEYSTATE_NOT_IT:
2150 {
2151 if ((SDL_GetModState() & ~(KMOD_MODE | KMOD_NUM | KMOD_RESERVED)) == 0)
2152 enmHKeyState = HKEYSTATE_NORMAL;
2153 ProcessKey(&event.key);
2154 break;
2155 }
2156 } /* state switch */
2157 break;
2158 }
2159
2160 /*
2161 * The window was closed.
2162 */
2163 case SDL_QUIT:
2164 {
2165 goto leave;
2166 break;
2167 }
2168
2169 /*
2170 * The mouse has moved
2171 */
2172 case SDL_MOUSEMOTION:
2173 {
2174 if (gfGrabbed || UseAbsoluteMouse())
2175 {
2176 SendMouseEvent(0, 0, 0);
2177 }
2178 break;
2179 }
2180
2181 /*
2182 * A mouse button has been clicked or released.
2183 */
2184 case SDL_MOUSEBUTTONDOWN:
2185 case SDL_MOUSEBUTTONUP:
2186 {
2187 SDL_MouseButtonEvent *bev = &event.button;
2188 /* don't grab on mouse click if we have guest additions */
2189 if (!gfGrabbed && !UseAbsoluteMouse() && gfGrabOnMouseClick)
2190 {
2191 if (event.type == SDL_MOUSEBUTTONDOWN && (bev->state & SDL_BUTTON_LMASK))
2192 {
2193 /* start grabbing all events */
2194 InputGrabStart();
2195 }
2196 }
2197 else if (gfGrabbed || UseAbsoluteMouse())
2198 {
2199 int dz = bev->button == SDL_BUTTON_WHEELUP
2200 ? -1
2201 : bev->button == SDL_BUTTON_WHEELDOWN
2202 ? +1
2203 : 0;
2204
2205 /* end host key combination (CTRL+MouseButton) */
2206 switch (enmHKeyState)
2207 {
2208 case HKEYSTATE_DOWN_1ST:
2209 case HKEYSTATE_DOWN_2ND:
2210 enmHKeyState = HKEYSTATE_NOT_IT;
2211 ProcessKey(&EvHKeyDown1.key);
2212 break;
2213 case HKEYSTATE_DOWN:
2214 enmHKeyState = HKEYSTATE_NOT_IT;
2215 ProcessKey(&EvHKeyDown1.key);
2216 if (gHostKeySym2 != SDLK_UNKNOWN)
2217 ProcessKey(&EvHKeyDown2.key);
2218 break;
2219 default:
2220 break;
2221 }
2222
2223 SendMouseEvent(dz, event.type == SDL_MOUSEBUTTONDOWN, bev->button);
2224 }
2225 break;
2226 }
2227
2228 /*
2229 * The window has gained or lost focus.
2230 */
2231 case SDL_ACTIVEEVENT:
2232 {
2233 /*
2234 * There is a strange behaviour in SDL when running without a window
2235 * manager: When SDL_WM_GrabInput(SDL_GRAB_ON) is called we receive two
2236 * consecutive events SDL_ACTIVEEVENTs (input lost, input gained).
2237 * Asking SDL_GetAppState() seems the better choice.
2238 */
2239 if (gfGrabbed && (SDL_GetAppState() & SDL_APPINPUTFOCUS) == 0)
2240 {
2241 /*
2242 * another window has stolen the (keyboard) input focus
2243 */
2244 InputGrabEnd();
2245 }
2246 break;
2247 }
2248
2249 /*
2250 * The SDL window was resized
2251 */
2252 case SDL_VIDEORESIZE:
2253 {
2254 if (gDisplay)
2255 {
2256#ifdef VBOX_SECURELABEL
2257 /* communicate the resize event to the guest */
2258 gDisplay->SetVideoModeHint(event.resize.w, RT_MAX(0, event.resize.h - SECURE_LABEL_HEIGHT), 0);
2259#else
2260 /* communicate the resize event to the guest */
2261 gDisplay->SetVideoModeHint(event.resize.w, event.resize.h, 0);
2262#endif
2263 }
2264 break;
2265 }
2266
2267 /*
2268 * User specific update event.
2269 */
2270 /** @todo use a common user event handler so that SDL_PeepEvents() won't
2271 * possibly remove other events in the queue!
2272 */
2273 case SDL_USER_EVENT_UPDATERECT:
2274 {
2275 /*
2276 * Decode event parameters.
2277 */
2278 #define DECODEX(event) ((intptr_t)(event).user.data1 >> 16)
2279 #define DECODEY(event) ((intptr_t)(event).user.data1 & 0xFFFF)
2280 #define DECODEW(event) ((intptr_t)(event).user.data2 >> 16)
2281 #define DECODEH(event) ((intptr_t)(event).user.data2 & 0xFFFF)
2282 int x = DECODEX(event);
2283 int y = DECODEY(event);
2284 int w = DECODEW(event);
2285 int h = DECODEH(event);
2286 LogFlow(("SDL_USER_EVENT_UPDATERECT: x = %d, y = %d, w = %d, h = %d\n",
2287 x, y, w, h));
2288
2289 Assert(gpFrameBuffer);
2290 gpFrameBuffer->update(x, y, w, h, true /* fGuestRelative */);
2291
2292 #undef DECODEX
2293 #undef DECODEY
2294 #undef DECODEW
2295 #undef DECODEH
2296 break;
2297 }
2298
2299 /*
2300 * User specific resize event.
2301 */
2302 case SDL_USER_EVENT_RESIZE:
2303 {
2304 LogFlow(("SDL_USER_EVENT_RESIZE\n"));
2305 gpFrameBuffer->resizeGuest();
2306 /* notify the display that the resize has been completed */
2307 gDisplay->ResizeCompleted();
2308 break;
2309 }
2310
2311#ifdef __LINUX__
2312 /*
2313 * User specific XPCOM event queue event
2314 */
2315 case SDL_USER_EVENT_XPCOM_EVENTQUEUE:
2316 {
2317 LogFlow(("SDL_USER_EVENT_XPCOM_EVENTQUEUE: processing XPCOM event queue...\n"));
2318 eventQ->ProcessPendingEvents();
2319 signalXPCOMEventQueueThread();
2320 break;
2321 }
2322#endif /* __LINUX__ */
2323
2324 /*
2325 * User specific update title bar notification event
2326 */
2327 case SDL_USER_EVENT_UPDATE_TITLEBAR:
2328 {
2329 UpdateTitlebar(TITLEBAR_NORMAL);
2330 break;
2331 }
2332
2333 /*
2334 * User specific termination event
2335 */
2336 case SDL_USER_EVENT_TERMINATE:
2337 {
2338 if (event.user.code != VBOXSDL_TERM_NORMAL)
2339 RTPrintf("Error: VM terminated abnormally!\n");
2340 goto leave;
2341 }
2342
2343#ifdef VBOX_SECURELABEL
2344 /*
2345 * User specific secure label update event
2346 */
2347 case SDL_USER_EVENT_SECURELABEL_UPDATE:
2348 {
2349 /*
2350 * Query the new label text
2351 */
2352 Bstr key = VBOXSDL_SECURELABEL_EXTRADATA;
2353 Bstr label;
2354 gMachine->GetExtraData(key, label.asOutParam());
2355 Utf8Str labelUtf8 = label;
2356 /*
2357 * Now update the label
2358 */
2359 gpFrameBuffer->setSecureLabelText(labelUtf8.raw());
2360 break;
2361 }
2362#endif /* VBOX_SECURELABEL */
2363
2364 /*
2365 * User specific pointer shape change event
2366 */
2367 case SDL_USER_EVENT_POINTER_CHANGE:
2368 {
2369 PointerShapeChangeData *data = (PointerShapeChangeData *) event.user.data1;
2370 SetPointerShape (data);
2371 delete data;
2372 break;
2373 }
2374
2375 /*
2376 * User specific guest capabilities changed
2377 */
2378 case SDL_USER_EVENT_GUEST_CAP_CHANGED:
2379 {
2380 HandleGuestCapsChanged();
2381 break;
2382 }
2383
2384 default:
2385 {
2386 LogBird(("unknown SDL event %d\n", event.type));
2387 break;
2388 }
2389 }
2390 }
2391
2392leave:
2393 LogFlow(("leaving...\n"));
2394#ifdef __LINUX__
2395 /* make sure the XPCOM event queue thread doesn't do anything harmful */
2396 terminateXPCOMQueueThread();
2397#endif /* __LINUX__ */
2398
2399#ifdef VBOX_VRDP
2400 if (gVrdpServer)
2401 rc = gVrdpServer->COMSETTER(Enabled)(FALSE);
2402#endif
2403
2404 /*
2405 * Get the machine state.
2406 */
2407 if (gMachine)
2408 gMachine->COMGETTER(State)(&machineState);
2409 else
2410 machineState = MachineState_Aborted;
2411
2412 /*
2413 * Turn off the VM if it's running
2414 */
2415 if ( gConsole
2416 && machineState == MachineState_Running)
2417 {
2418 consoleCallback->ignorePowerOffEvents(true);
2419 rc = gConsole->PowerDown();
2420 if (FAILED(rc))
2421 {
2422 com::ErrorInfo info;
2423 if (info.isFullAvailable())
2424 PrintError("Failed to power down VM",
2425 info.getText().raw(), info.getComponent().raw());
2426 else
2427 RTPrintf("Failed to power down virtual machine! No error information available (rc = 0x%x).\n", rc);
2428 break;
2429 }
2430 }
2431
2432 /*
2433 * Now we discard all settings so that our changes will
2434 * not be flushed to the permanent configuration
2435 */
2436 if ( gMachine
2437 && machineState != MachineState_Saved)
2438 {
2439 rc = gMachine->DiscardSettings();
2440 AssertComRC(rc);
2441 }
2442
2443 /* close the session */
2444 if (sessionOpened)
2445 {
2446 rc = session->Close();
2447 AssertComRC(rc);
2448 }
2449
2450 /* restore the default cursor and free the custom one if any */
2451 if (gpDefaultCursor)
2452 {
2453#ifdef __LINUX__
2454 Cursor pDefaultTempX11Cursor = *(Cursor*)gpDefaultCursor->wm_cursor;
2455 *(Cursor*)gpDefaultCursor->wm_cursor = gpDefaultOrigX11Cursor;
2456#endif /* __LNUX__ */
2457 SDL_SetCursor(gpDefaultCursor);
2458#ifdef __LINUX__
2459 XFreeCursor(gSdlInfo.info.x11.display, pDefaultTempX11Cursor);
2460#endif /* __LINUX__ */
2461 }
2462
2463 if (gpCustomCursor)
2464 {
2465 WMcursor *pCustomTempWMCursor = gpCustomCursor->wm_cursor;
2466 gpCustomCursor->wm_cursor = gpCustomOrigWMcursor;
2467 SDL_FreeCursor(gpCustomCursor);
2468 if (pCustomTempWMCursor)
2469 {
2470#if defined (__WIN__)
2471 ::DestroyCursor(*(HCURSOR *) pCustomTempWMCursor);
2472#elif defined (__LINUX__)
2473 XFreeCursor(gSdlInfo.info.x11.display, *(Cursor *) pCustomTempWMCursor);
2474#endif
2475 free(pCustomTempWMCursor);
2476 }
2477 }
2478
2479 LogFlow(("Releasing mouse, keyboard, vrdpserver, display, console...\n"));
2480 gMouse = NULL;
2481 gKeyboard = NULL;
2482 gVrdpServer = NULL;
2483 gDisplay = NULL;
2484 gConsole = NULL;
2485 gMachineDebugger = NULL;
2486 gProgress = NULL;
2487 // we can only uninitialize SDL here because it is not threadsafe
2488 if (gpFrameBuffer)
2489 {
2490 LogFlow(("Releasing framebuffer...\n"));
2491 gpFrameBuffer->uninit();
2492 gpFrameBuffer->Release();
2493 }
2494#ifdef VBOX_SECURELABEL
2495 /* must do this after destructing the framebuffer */
2496 if (gLibrarySDL_ttf)
2497 RTLdrClose(gLibrarySDL_ttf);
2498#endif
2499 LogFlow(("Releasing machine, session...\n"));
2500 gMachine = NULL;
2501 session = NULL;
2502 LogFlow(("Releasing callback handlers...\n"));
2503 if (callback)
2504 callback->Release();
2505 if (consoleCallback)
2506 consoleCallback->Release();
2507
2508 LogFlow(("Releasing VirtualBox object...\n"));
2509 virtualBox = NULL;
2510
2511 // end "all-stuff" scope
2512 ////////////////////////////////////////////////////////////////////////////
2513 }
2514 while (0);
2515
2516 LogFlow(("Uninitializing COM...\n"));
2517 com::Shutdown();
2518
2519 LogFlow(("Returning from main()!\n"));
2520 RTLogFlush(NULL);
2521 return FAILED (rc) ? 1 : 0;
2522}
2523
2524/**
2525 * Returns whether the absolute mouse is in use, i.e. both host
2526 * and guest have opted to enable it.
2527 *
2528 * @returns bool Flag whether the absolute mouse is in use
2529 */
2530static bool UseAbsoluteMouse(void)
2531{
2532 return (gfAbsoluteMouseHost && gfAbsoluteMouseGuest);
2533}
2534
2535/**
2536 * Converts an SDL keyboard eventcode to a XT scancode.
2537 *
2538 * @returns XT scancode
2539 * @param ev SDL scancode
2540 */
2541static uint8_t Keyevent2Keycode(const SDL_KeyboardEvent *ev)
2542{
2543 int keycode;
2544
2545 // start with the scancode determined by SDL
2546 keycode = ev->keysym.scancode;
2547
2548#ifdef __LINUX__
2549 // workaround for SDL keyboard translation issues on Linux
2550 // keycodes > 0x80 are sent as 0xe0 keycode
2551 static const uint8_t x_keycode_to_pc_keycode[61] =
2552 {
2553 0xc7, /* 97 Home */
2554 0xc8, /* 98 Up */
2555 0xc9, /* 99 PgUp */
2556 0xcb, /* 100 Left */
2557 0x4c, /* 101 KP-5 */
2558 0xcd, /* 102 Right */
2559 0xcf, /* 103 End */
2560 0xd0, /* 104 Down */
2561 0xd1, /* 105 PgDn */
2562 0xd2, /* 106 Ins */
2563 0xd3, /* 107 Del */
2564 0x9c, /* 108 Enter */
2565 0x9d, /* 109 Ctrl-R */
2566 0x0, /* 110 Pause */
2567 0xb7, /* 111 Print */
2568 0xb5, /* 112 Divide */
2569 0xb8, /* 113 Alt-R */
2570 0xc6, /* 114 Break */
2571 0xdb, /* 115 Win Left */
2572 0xdc, /* 116 Win Right */
2573 0xdd, /* 117 Win Menu */
2574 0x0, /* 118 */
2575 0x0, /* 119 */
2576 0x70, /* 120 Hiragana_Katakana */
2577 0x0, /* 121 */
2578 0x0, /* 122 */
2579 0x73, /* 123 backslash */
2580 0x0, /* 124 */
2581 0x0, /* 125 */
2582 0x0, /* 126 */
2583 0x0, /* 127 */
2584 0x0, /* 128 */
2585 0x79, /* 129 Henkan */
2586 0x0, /* 130 */
2587 0x7b, /* 131 Muhenkan */
2588 0x0, /* 132 */
2589 0x7d, /* 133 Yen */
2590 0x0, /* 134 */
2591 0x0, /* 135 */
2592 0x47, /* 136 KP_7 */
2593 0x48, /* 137 KP_8 */
2594 0x49, /* 138 KP_9 */
2595 0x4b, /* 139 KP_4 */
2596 0x4c, /* 140 KP_5 */
2597 0x4d, /* 141 KP_6 */
2598 0x4f, /* 142 KP_1 */
2599 0x50, /* 143 KP_2 */
2600 0x51, /* 144 KP_3 */
2601 0x52, /* 145 KP_0 */
2602 0x53, /* 146 KP_. */
2603 0x47, /* 147 KP_HOME */
2604 0x48, /* 148 KP_UP */
2605 0x49, /* 149 KP_PgUp */
2606 0x4b, /* 150 KP_Left */
2607 0x4c, /* 151 KP_ */
2608 0x4d, /* 152 KP_Right */
2609 0x4f, /* 153 KP_End */
2610 0x50, /* 154 KP_Down */
2611 0x51, /* 155 KP_PgDn */
2612 0x52, /* 156 KP_Ins */
2613 0x53, /* 157 KP_Del */
2614 };
2615
2616 if (keycode < 9)
2617 {
2618 keycode = 0;
2619 }
2620 else if (keycode < 97)
2621 {
2622 // just an offset (Xorg MIN_KEYCODE)
2623 keycode -= 8;
2624 }
2625 else if (keycode < 158)
2626 {
2627 // apply conversion table
2628 keycode = x_keycode_to_pc_keycode[keycode - 97];
2629 }
2630 else
2631 {
2632 keycode = 0;
2633 }
2634#endif
2635 return keycode;
2636}
2637
2638/**
2639 * Releases any modifier keys that are currently in pressed state.
2640 */
2641static void ResetKeys(void)
2642{
2643 int i;
2644
2645 if (!gKeyboard)
2646 return;
2647
2648 for(i = 0; i < 256; i++)
2649 {
2650 if (gaModifiersState[i])
2651 {
2652 if (i & 0x80)
2653 gKeyboard->PutScancode(0xe0);
2654 gKeyboard->PutScancode(i | 0x80);
2655 gaModifiersState[i] = 0;
2656 }
2657 }
2658}
2659
2660/**
2661 * Keyboard event handler.
2662 *
2663 * @param ev SDL keyboard event.
2664 */
2665static void ProcessKey(SDL_KeyboardEvent *ev)
2666{
2667#if defined(DEBUG) || defined(VBOX_WITH_STATISTICS)
2668 if (gMachineDebugger && ev->type == SDL_KEYDOWN)
2669 {
2670 // first handle the debugger hotkeys
2671 uint8_t *keystate = SDL_GetKeyState(NULL);
2672#if 0
2673 // CTRL+ALT+Fn is not free on Linux hosts with Xorg ..
2674 if (keystate[SDLK_LALT] && !keystate[SDLK_LCTRL])
2675#else
2676 if (keystate[SDLK_LALT] && keystate[SDLK_LCTRL])
2677#endif
2678 {
2679 switch (ev->keysym.sym)
2680 {
2681 // pressing CTRL+ALT+F11 dumps the statistics counter
2682 case SDLK_F12:
2683 RTPrintf("ResetStats\n"); /* Visual feedback in console window */
2684 gMachineDebugger->ResetStats();
2685 break;
2686 // pressing CTRL+ALT+F12 resets all statistics counter
2687 case SDLK_F11:
2688 gMachineDebugger->DumpStats();
2689 RTPrintf("DumpStats\n"); /* Vistual feedback in console window */
2690 break;
2691 default:
2692 break;
2693 }
2694 }
2695#if 1
2696 else if (keystate[SDLK_LALT] && !keystate[SDLK_LCTRL])
2697 {
2698 switch (ev->keysym.sym)
2699 {
2700 // pressing Alt-F12 toggles the supervisor recompiler
2701 case SDLK_F12:
2702 {
2703 BOOL recompileSupervisor;
2704 gMachineDebugger->COMGETTER(RecompileSupervisor)(&recompileSupervisor);
2705 gMachineDebugger->COMSETTER(RecompileSupervisor)(!recompileSupervisor);
2706 break;
2707 }
2708 // pressing Alt-F11 toggles the user recompiler
2709 case SDLK_F11:
2710 {
2711 BOOL recompileUser;
2712 gMachineDebugger->COMGETTER(RecompileUser)(&recompileUser);
2713 gMachineDebugger->COMSETTER(RecompileUser)(!recompileUser);
2714 break;
2715 }
2716 // pressing Alt-F10 toggles the patch manager
2717 case SDLK_F10:
2718 {
2719 BOOL patmEnabled;
2720 gMachineDebugger->COMGETTER(PATMEnabled)(&patmEnabled);
2721 gMachineDebugger->COMSETTER(PATMEnabled)(!patmEnabled);
2722 break;
2723 }
2724 // pressing Alt-F9 toggles CSAM
2725 case SDLK_F9:
2726 {
2727 BOOL csamEnabled;
2728 gMachineDebugger->COMGETTER(CSAMEnabled)(&csamEnabled);
2729 gMachineDebugger->COMSETTER(CSAMEnabled)(!csamEnabled);
2730 break;
2731 }
2732 // pressing Alt-F8 toggles singlestepping mode
2733 case SDLK_F8:
2734 {
2735 BOOL singlestepEnabled;
2736 gMachineDebugger->COMGETTER(Singlestep)(&singlestepEnabled);
2737 gMachineDebugger->COMSETTER(Singlestep)(!singlestepEnabled);
2738 break;
2739 }
2740 default:
2741 break;
2742 }
2743 }
2744#endif
2745 // pressing Ctrl-F12 toggles the logger
2746 else if ((keystate[SDLK_RCTRL] || keystate[SDLK_LCTRL]) && ev->keysym.sym == SDLK_F12)
2747 {
2748 BOOL logEnabled = TRUE;
2749 gMachineDebugger->COMGETTER(LogEnabled)(&logEnabled);
2750 gMachineDebugger->COMSETTER(LogEnabled)(!logEnabled);
2751#ifdef DEBUG_bird
2752 return;
2753#endif
2754 }
2755 // pressing F12 sets a logmark
2756 else if (ev->keysym.sym == SDLK_F12)
2757 {
2758 RTLogPrintf("****** LOGGING MARK ******\n");
2759 RTLogFlush(NULL);
2760 }
2761 // now update the titlebar flags
2762 UpdateTitlebar(TITLEBAR_NORMAL);
2763 }
2764#endif // DEBUG || VBOX_WITH_STATISTICS
2765
2766 // the pause key is the weirdest, needs special handling
2767 if (ev->keysym.sym == SDLK_PAUSE)
2768 {
2769 int v = 0;
2770 if (ev->type == SDL_KEYUP)
2771 v |= 0x80;
2772 gKeyboard->PutScancode(0xe1);
2773 gKeyboard->PutScancode(0x1d | v);
2774 gKeyboard->PutScancode(0x45 | v);
2775 return;
2776 }
2777
2778 /*
2779 * Perform SDL key event to scancode conversion
2780 */
2781 int keycode = Keyevent2Keycode(ev);
2782
2783 switch(keycode)
2784 {
2785 case 0x00:
2786 {
2787 /* sent when leaving window: reset the modifiers state */
2788 ResetKeys();
2789 return;
2790 }
2791
2792 case 0x2a: /* Left Shift */
2793 case 0x36: /* Right Shift */
2794 case 0x1d: /* Left CTRL */
2795 case 0x9d: /* Right CTRL */
2796 case 0x38: /* Left ALT */
2797 case 0xb8: /* Right ALT */
2798 {
2799 if (ev->type == SDL_KEYUP)
2800 gaModifiersState[keycode] = 0;
2801 else
2802 gaModifiersState[keycode] = 1;
2803 break;
2804 }
2805
2806 case 0x45: /* Num Lock */
2807 case 0x3a: /* Caps Lock */
2808 {
2809 /* SDL does not send the key up event, so we generate it.
2810 * r=frank: This is not true for never SDL versions. */
2811 if (ev->type == SDL_KEYDOWN)
2812 {
2813 gKeyboard->PutScancode(keycode);
2814 gKeyboard->PutScancode(keycode | 0x80);
2815 }
2816 return;
2817 }
2818 }
2819
2820 if (ev->type != SDL_KEYDOWN)
2821 {
2822 /*
2823 * Some keyboards (e.g. the one of mine T60) don't send a NumLock scan code on every
2824 * press of the key. Both the guest and the host should agree on the NumLock state.
2825 * If they differ, we try to alter the guest NumLock state by sending the NumLock key
2826 * scancode. We will get a feedback through the KBD_CMD_SET_LEDS command if the guest
2827 * tries to set/clear the NumLock LED. If a (silly) guest doesn't change the LED, don't
2828 * bother him with NumLock scancodes. At least our BIOS, Linux and Windows handle the
2829 * NumLock LED well.
2830 */
2831 if ( gcGuestNumLockAdaptions
2832 && (gfGuestNumLockPressed ^ !!(SDL_GetModState() & KMOD_NUM)))
2833 {
2834 gcGuestNumLockAdaptions--;
2835 gKeyboard->PutScancode(0x45);
2836 gKeyboard->PutScancode(0x45 | 0x80);
2837 }
2838#if 0 /* For some reason SDL_GetModState() does not return KMOD_CAPS correctly */
2839 if ( gcGuestCapsLockAdaptions
2840 && (gfGuestCapsLockPressed ^ !!(SDL_GetModState() & KMOD_CAPS)))
2841 {
2842 gcGuestCapsLockAdaptions--;
2843 gKeyboard->PutScancode(0x3a);
2844 gKeyboard->PutScancode(0x3a | 0x80);
2845 }
2846#endif
2847 }
2848
2849 /*
2850 * Now we send the event. Apply extended and release prefixes.
2851 */
2852 if (keycode & 0x80)
2853 gKeyboard->PutScancode(0xe0);
2854
2855 gKeyboard->PutScancode(ev->type == SDL_KEYUP ? keycode | 0x80
2856 : keycode & 0x7f);
2857}
2858
2859/**
2860 * Start grabbing the mouse.
2861 */
2862static void InputGrabStart(void)
2863{
2864 if (!gfGuestNeedsHostCursor)
2865 SDL_ShowCursor(SDL_DISABLE);
2866 SDL_WM_GrabInput(SDL_GRAB_ON);
2867 // dummy read to avoid moving the mouse
2868 SDL_GetRelativeMouseState(NULL, NULL);
2869 gfGrabbed = TRUE;
2870 UpdateTitlebar(TITLEBAR_NORMAL);
2871}
2872
2873/**
2874 * End mouse grabbing.
2875 */
2876static void InputGrabEnd(void)
2877{
2878 SDL_WM_GrabInput(SDL_GRAB_OFF);
2879 if (!gfGuestNeedsHostCursor)
2880 SDL_ShowCursor(SDL_ENABLE);
2881 gfGrabbed = FALSE;
2882 UpdateTitlebar(TITLEBAR_NORMAL);
2883}
2884
2885/**
2886 * Query mouse position and button state from SDL and send to the VM
2887 *
2888 * @param dz Relative mouse wheel movement
2889 */
2890static void SendMouseEvent(int dz, int down, int button)
2891{
2892 int x, y, state, buttons;
2893 bool abs;
2894
2895 /*
2896 * If supported and we're not in grabbed mode, we'll use the absolute mouse.
2897 * If we are in grabbed mode and the guest is not able to draw the mouse cursor
2898 * itself, we have to use absolute coordinates, otherwise the host cursor and
2899 * the coordinates the guest thinks the mouse is at could get out-of-sync. From
2900 * the SDL mailing list:
2901 *
2902 * "The event processing is usually asynchronous and so somewhat delayed, and
2903 * SDL_GetMouseState is returning the immediate mouse state. So at the time you
2904 * call SDL_GetMouseState, the "button" is already up."
2905 */
2906 abs = (UseAbsoluteMouse() && !gfGrabbed) || gfGuestNeedsHostCursor;
2907
2908 /* only used if abs == TRUE */
2909 int xMin = gpFrameBuffer->getXOffset();
2910 int yMin = gpFrameBuffer->getYOffset();
2911 int xMax = xMin + (int)gpFrameBuffer->getGuestXRes();
2912 int yMax = yMin + (int)gpFrameBuffer->getGuestYRes();
2913
2914 state = abs ? SDL_GetMouseState(&x, &y) : SDL_GetRelativeMouseState(&x, &y);
2915
2916 /*
2917 * process buttons
2918 */
2919 buttons = 0;
2920 if (state & SDL_BUTTON(SDL_BUTTON_LEFT))
2921 buttons |= MouseButtonState_LeftButton;
2922 if (state & SDL_BUTTON(SDL_BUTTON_RIGHT))
2923 buttons |= MouseButtonState_RightButton;
2924 if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE))
2925 buttons |= MouseButtonState_MiddleButton;
2926
2927 if (abs)
2928 {
2929 /*
2930 * Check if the mouse event is inside the guest area. This solves the
2931 * following problem: Some guests switch off the VBox hardware mouse
2932 * cursor and draw the mouse cursor itself instead. Moving the mouse
2933 * outside the guest area then leads to annoying mouse hangs if we
2934 * don't pass mouse motion events into the guest.
2935 */
2936 if (x < xMin || y < yMin || x > xMax || y > yMax)
2937 {
2938 /*
2939 * Cursor outside of valid guest area (outside window or in secure
2940 * label area. Don't allow any mouse button press.
2941 */
2942 button = 0;
2943
2944 /*
2945 * Release any pressed button.
2946 */
2947#if 0
2948 /* disabled on customers request */
2949 buttons &= ~(MouseButtonState_LeftButton |
2950 MouseButtonState_MiddleButton |
2951 MouseButtonState_RightButton);
2952#endif
2953
2954 /*
2955 * Prevent negative coordinates.
2956 */
2957 if (x < xMin) x = xMin;
2958 if (x > xMax) x = xMax;
2959 if (y < yMin) y = yMin;
2960 if (y > yMax) y = yMax;
2961
2962 if (!gpOffCursor)
2963 {
2964 gpOffCursor = SDL_GetCursor(); /* Cursor image */
2965 gfOffCursorActive = SDL_ShowCursor(-1); /* enabled / disabled */
2966 SDL_SetCursor(gpDefaultCursor);
2967 SDL_ShowCursor (SDL_ENABLE);
2968 }
2969 }
2970 else
2971 {
2972 if (gpOffCursor)
2973 {
2974 /*
2975 * We just entered the valid guest area. Restore the guest mouse
2976 * cursor.
2977 */
2978 SDL_SetCursor(gpOffCursor);
2979 SDL_ShowCursor(gfOffCursorActive ? SDL_ENABLE : SDL_DISABLE);
2980 gpOffCursor = NULL;
2981 }
2982 }
2983 }
2984
2985 /*
2986 * Button was pressed but that press is not reflected in the button state?
2987 */
2988 if (down && !(state & SDL_BUTTON(button)))
2989 {
2990 /*
2991 * It can happen that a mouse up event follows a mouse down event immediately
2992 * and we see the events when the bit in the button state is already cleared
2993 * again. In that case we simulate the mouse down event.
2994 */
2995 int tmp_button = 0;
2996 switch (button)
2997 {
2998 case SDL_BUTTON_LEFT: tmp_button = MouseButtonState_LeftButton; break;
2999 case SDL_BUTTON_MIDDLE: tmp_button = MouseButtonState_MiddleButton; break;
3000 case SDL_BUTTON_RIGHT: tmp_button = MouseButtonState_RightButton; break;
3001 }
3002
3003 if (abs)
3004 {
3005 /**
3006 * @todo
3007 * PutMouseEventAbsolute() expects x and y starting from 1,1.
3008 * should we do the increment internally in PutMouseEventAbsolute()
3009 * or state it in PutMouseEventAbsolute() docs?
3010 */
3011 gMouse->PutMouseEventAbsolute(x + 1 - xMin,
3012 y + 1 - yMin,
3013 dz, buttons | tmp_button);
3014 }
3015 else
3016 {
3017 gMouse->PutMouseEvent(0, 0, dz, buttons | tmp_button);
3018 }
3019 }
3020
3021 // now send the mouse event
3022 if (abs)
3023 {
3024 /**
3025 * @todo
3026 * PutMouseEventAbsolute() expects x and y starting from 1,1.
3027 * should we do the increment internally in PutMouseEventAbsolute()
3028 * or state it in PutMouseEventAbsolute() docs?
3029 */
3030 gMouse->PutMouseEventAbsolute(x + 1 - xMin,
3031 y + 1 - yMin,
3032 dz, buttons);
3033 }
3034 else
3035 {
3036 gMouse->PutMouseEvent(x, y, dz, buttons);
3037 }
3038}
3039
3040/**
3041 * Resets the VM
3042 */
3043void ResetVM(void)
3044{
3045 if (gConsole)
3046 gConsole->Reset();
3047}
3048
3049/**
3050 * Initiates a saved state and updates the titlebar with progress information
3051 */
3052void SaveState(void)
3053{
3054 ResetKeys();
3055 RTThreadYield();
3056 if (gfGrabbed)
3057 InputGrabEnd();
3058 RTThreadYield();
3059 UpdateTitlebar(TITLEBAR_SAVE);
3060 gProgress = NULL;
3061 HRESULT rc = gConsole->SaveState(gProgress.asOutParam());
3062 if (FAILED(S_OK))
3063 {
3064 RTPrintf("Error saving state! rc = 0x%x\n", rc);
3065 return;
3066 }
3067 Assert(gProgress);
3068
3069 /*
3070 * Wait for the operation to be completed and work
3071 * the title bar in the mean while.
3072 */
3073 LONG cPercent = 0;
3074 for (;;)
3075 {
3076 BOOL fCompleted = false;
3077 rc = gProgress->COMGETTER(Completed)(&fCompleted);
3078 if (FAILED(rc) || fCompleted)
3079 break;
3080 LONG cPercentNow;
3081 rc = gProgress->COMGETTER(Percent)(&cPercentNow);
3082 if (FAILED(rc))
3083 break;
3084 if (cPercentNow != cPercent)
3085 {
3086 UpdateTitlebar(TITLEBAR_SAVE, cPercent);
3087 cPercent = cPercentNow;
3088 }
3089
3090 /* wait */
3091 rc = gProgress->WaitForCompletion(100);
3092 if (FAILED(rc))
3093 break;
3094 /// @todo process gui events.
3095 }
3096
3097 /*
3098 * What's the result of the operation?
3099 */
3100 HRESULT lrc;
3101 rc = gProgress->COMGETTER(ResultCode)(&lrc);
3102 if (FAILED(rc))
3103 lrc = ~0;
3104 if (!lrc)
3105 {
3106 UpdateTitlebar(TITLEBAR_SAVE, 100);
3107 RTThreadYield();
3108 RTPrintf("Saved the state successfully.\n");
3109 }
3110 else
3111 RTPrintf("Error saving state, lrc=%d (%#x)\n", lrc, lrc);
3112}
3113
3114/**
3115 * Build the titlebar string
3116 */
3117static void UpdateTitlebar(TitlebarMode mode, uint32_t u32User)
3118{
3119 static char szTitle[1024] = {0};
3120
3121 /* back up current title */
3122 char szPrevTitle[1024];
3123 strcpy(szPrevTitle, szTitle);
3124
3125
3126 strcpy(szTitle, "InnoTek VirtualBox - ");
3127
3128 Bstr name;
3129 gMachine->COMGETTER(Name)(name.asOutParam());
3130 if (name)
3131 strcat(szTitle, Utf8Str(name).raw());
3132 else
3133 strcat(szTitle, "<noname>");
3134
3135
3136 /* which mode are we in? */
3137 switch (mode)
3138 {
3139 case TITLEBAR_NORMAL:
3140 {
3141 MachineState_T machineState;
3142 gMachine->COMGETTER(State)(&machineState);
3143 if (machineState == MachineState_Paused)
3144 strcat(szTitle, " - [Paused]");
3145
3146 if (gfGrabbed)
3147 strcat(szTitle, " - [Input captured]");
3148
3149 // do we have a debugger interface
3150 if (gMachineDebugger)
3151 {
3152#if defined(DEBUG) || defined(VBOX_WITH_STATISTICS)
3153 // query the machine state
3154 BOOL recompileSupervisor = FALSE;
3155 BOOL recompileUser = FALSE;
3156 BOOL patmEnabled = FALSE;
3157 BOOL csamEnabled = FALSE;
3158 BOOL singlestepEnabled = FALSE;
3159 BOOL logEnabled = FALSE;
3160 BOOL hwVirtEnabled = FALSE;
3161 ULONG virtualTimeRate = 100;
3162 gMachineDebugger->COMGETTER(RecompileSupervisor)(&recompileSupervisor);
3163 gMachineDebugger->COMGETTER(RecompileUser)(&recompileUser);
3164 gMachineDebugger->COMGETTER(PATMEnabled)(&patmEnabled);
3165 gMachineDebugger->COMGETTER(CSAMEnabled)(&csamEnabled);
3166 gMachineDebugger->COMGETTER(LogEnabled)(&logEnabled);
3167 gMachineDebugger->COMGETTER(Singlestep)(&singlestepEnabled);
3168 gMachineDebugger->COMGETTER(HWVirtExEnabled)(&hwVirtEnabled);
3169 gMachineDebugger->COMGETTER(VirtualTimeRate)(&virtualTimeRate);
3170 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
3171 " [STEP=%d CS=%d PAT=%d RR0=%d RR3=%d LOG=%d HWVirt=%d",
3172 singlestepEnabled == TRUE, csamEnabled == TRUE, patmEnabled == TRUE,
3173 recompileSupervisor == FALSE, recompileUser == FALSE,
3174 logEnabled == TRUE, hwVirtEnabled == TRUE);
3175 char *psz = strchr(szTitle, '\0');
3176 if (virtualTimeRate != 100)
3177 RTStrPrintf(psz, &szTitle[sizeof(szTitle)] - psz, " WD=%d%%]", virtualTimeRate);
3178 else
3179 RTStrPrintf(psz, &szTitle[sizeof(szTitle)] - psz, "]");
3180#else
3181 BOOL hwVirtEnabled = FALSE;
3182 gMachineDebugger->COMGETTER(HWVirtExEnabled)(&hwVirtEnabled);
3183 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
3184 "%s", hwVirtEnabled ? " (HWVirtEx)" : "");
3185#endif /* DEBUG */
3186 }
3187 break;
3188 }
3189
3190 case TITLEBAR_STARTUP:
3191 {
3192 /*
3193 * Format it.
3194 */
3195 MachineState_T machineState;
3196 gMachine->COMGETTER(State)(&machineState);
3197 if (machineState == MachineState_Starting)
3198 strcat(szTitle, " - Starting...");
3199 else if (machineState == MachineState_Restoring)
3200 {
3201 LONG cPercentNow;
3202 HRESULT rc = gProgress->COMGETTER(Percent)(&cPercentNow);
3203 if (SUCCEEDED(rc))
3204 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
3205 " - Restoring %d%%...", (int)cPercentNow);
3206 else
3207 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
3208 " - Restoring...");
3209 }
3210 /* ignore other states, we could already be in running or aborted state */
3211 break;
3212 }
3213
3214 case TITLEBAR_SAVE:
3215 {
3216 AssertMsg(u32User >= 0 && u32User <= 100, ("%d\n", u32User));
3217 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
3218 " - Saving %d%%...", u32User);
3219 break;
3220 }
3221
3222 case TITLEBAR_SNAPSHOT:
3223 {
3224 AssertMsg(u32User >= 0 && u32User <= 100, ("%d\n", u32User));
3225 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
3226 " - Taking snapshot %d%%...", u32User);
3227 break;
3228 }
3229
3230 default:
3231 RTPrintf("Error: Invalid title bar mode %d!\n", mode);
3232 return;
3233 }
3234
3235 /*
3236 * Don't update if it didn't change.
3237 */
3238 if (strcmp(szTitle, szPrevTitle) == 0)
3239 return;
3240
3241 /*
3242 * Set the new title
3243 */
3244#ifdef VBOX_WIN32_UI
3245 setUITitle(szTitle);
3246#else
3247 SDL_WM_SetCaption(szTitle, "InnoTek VirtualBox");
3248#endif
3249}
3250
3251#if 0
3252static void vbox_show_shape (unsigned short w, unsigned short h,
3253 uint32_t bg, const uint8_t *image)
3254{
3255 size_t x, y;
3256 unsigned short pitch;
3257 const uint32_t *color;
3258 const uint8_t *mask;
3259 size_t size_mask;
3260
3261 mask = image;
3262 pitch = (w + 7) / 8;
3263 size_mask = (pitch * h + 3) & ~3;
3264
3265 color = (const uint32_t *) (image + size_mask);
3266
3267 printf ("show_shape %dx%d pitch %d size mask %d\n",
3268 w, h, pitch, size_mask);
3269 for (y = 0; y < h; ++y, mask += pitch, color += w)
3270 {
3271 for (x = 0; x < w; ++x) {
3272 if (mask[x / 8] & (1 << (7 - (x % 8))))
3273 printf (" ");
3274 else
3275 {
3276 uint32_t c = color[x];
3277 if (c == bg)
3278 printf ("Y");
3279 else
3280 printf ("X");
3281 }
3282 }
3283 printf ("\n");
3284 }
3285}
3286#endif
3287
3288/**
3289 * Sets the pointer shape according to parameters.
3290 * Must be called only from the main SDL thread.
3291 */
3292static void SetPointerShape (const PointerShapeChangeData *data)
3293{
3294 /*
3295 * don't allow to change the pointer shape if we are outside the valid
3296 * guest area. In that case set standard mouse pointer is set and should
3297 * not get overridden.
3298 */
3299 if (gpOffCursor)
3300 return;
3301
3302 if (data->shape)
3303 {
3304 bool ok = false;
3305
3306 uint32_t andMaskSize = (data->width + 7) / 8 * data->height;
3307 uint32_t srcShapePtrScan = data->width * 4;
3308
3309 const uint8_t *srcAndMaskPtr = data->shape;
3310 const uint8_t *srcShapePtr = data->shape + ((andMaskSize + 3) & ~3);
3311
3312#if 0
3313 /* pointer debugging code */
3314 // vbox_show_shape(data->width, data->height, 0, data->shape);
3315 uint32_t shapeSize = ((((data->width + 7) / 8) * data->height + 3) & ~3) + data->width * 4 * data->height;
3316 printf("visible: %d\n", data->visible);
3317 printf("width = %d\n", data->width);
3318 printf("height = %d\n", data->height);
3319 printf("alpha = %d\n", data->alpha);
3320 printf("xhot = %d\n", data->xHot);
3321 printf("yhot = %d\n", data->yHot);
3322 printf("uint8_t pointerdata[] = { ");
3323 for (uint32_t i = 0; i < shapeSize; i++)
3324 {
3325 printf("0x%x, ", data->shape[i]);
3326 }
3327 printf("};\n");
3328#endif
3329
3330#if defined (__WIN__)
3331
3332 BITMAPV5HEADER bi;
3333 HBITMAP hBitmap;
3334 void *lpBits;
3335 HCURSOR hAlphaCursor = NULL;
3336
3337 ::ZeroMemory (&bi, sizeof (BITMAPV5HEADER));
3338 bi.bV5Size = sizeof (BITMAPV5HEADER);
3339 bi.bV5Width = data->width;
3340 bi.bV5Height = - (LONG) data->height;
3341 bi.bV5Planes = 1;
3342 bi.bV5BitCount = 32;
3343 bi.bV5Compression = BI_BITFIELDS;
3344 // specifiy a supported 32 BPP alpha format for Windows XP
3345 bi.bV5RedMask = 0x00FF0000;
3346 bi.bV5GreenMask = 0x0000FF00;
3347 bi.bV5BlueMask = 0x000000FF;
3348 if (data->alpha)
3349 bi.bV5AlphaMask = 0xFF000000;
3350 else
3351 bi.bV5AlphaMask = 0;
3352
3353 HDC hdc = ::GetDC (NULL);
3354
3355 // create the DIB section with an alpha channel
3356 hBitmap = ::CreateDIBSection (hdc, (BITMAPINFO *) &bi, DIB_RGB_COLORS,
3357 (void **) &lpBits, NULL, (DWORD) 0);
3358
3359 ::ReleaseDC (NULL, hdc);
3360
3361 HBITMAP hMonoBitmap = NULL;
3362 if (data->alpha)
3363 {
3364 // create an empty mask bitmap
3365 hMonoBitmap = ::CreateBitmap (data->width, data->height, 1, 1, NULL);
3366 }
3367 else
3368 {
3369 /* Word aligned AND mask. Will be allocated and created if necessary. */
3370 uint8_t *pu8AndMaskWordAligned = NULL;
3371
3372 /* Width in bytes of the original AND mask scan line. */
3373 uint32_t cbAndMaskScan = (data->width + 7) / 8;
3374
3375 if (cbAndMaskScan & 1)
3376 {
3377 /* Original AND mask is not word aligned. */
3378
3379 /* Allocate memory for aligned AND mask. */
3380 pu8AndMaskWordAligned = (uint8_t *)RTMemTmpAllocZ ((cbAndMaskScan + 1) * data->height);
3381
3382 Assert(pu8AndMaskWordAligned);
3383
3384 if (pu8AndMaskWordAligned)
3385 {
3386 /* According to MSDN the padding bits must be 0.
3387 * Compute the bit mask to set padding bits to 0 in the last byte of original AND mask.
3388 */
3389 uint32_t u32PaddingBits = cbAndMaskScan * 8 - data->width;
3390 Assert(u32PaddingBits < 8);
3391 uint8_t u8LastBytesPaddingMask = (uint8_t)(0xFF << u32PaddingBits);
3392
3393 Log(("u8LastBytesPaddingMask = %02X, aligned w = %d, width = %d, cbAndMaskScan = %d\n",
3394 u8LastBytesPaddingMask, (cbAndMaskScan + 1) * 8, data->width, cbAndMaskScan));
3395
3396 uint8_t *src = (uint8_t *)srcAndMaskPtr;
3397 uint8_t *dst = pu8AndMaskWordAligned;
3398
3399 unsigned i;
3400 for (i = 0; i < data->height; i++)
3401 {
3402 memcpy (dst, src, cbAndMaskScan);
3403
3404 dst[cbAndMaskScan - 1] &= u8LastBytesPaddingMask;
3405
3406 src += cbAndMaskScan;
3407 dst += cbAndMaskScan + 1;
3408 }
3409 }
3410 }
3411
3412 // create the AND mask bitmap
3413 hMonoBitmap = ::CreateBitmap (data->width, data->height, 1, 1,
3414 pu8AndMaskWordAligned? pu8AndMaskWordAligned: srcAndMaskPtr);
3415
3416 if (pu8AndMaskWordAligned)
3417 {
3418 RTMemTmpFree (pu8AndMaskWordAligned);
3419 }
3420 }
3421
3422 Assert (hBitmap);
3423 Assert (hMonoBitmap);
3424 if (hBitmap && hMonoBitmap)
3425 {
3426 DWORD *dstShapePtr = (DWORD *) lpBits;
3427
3428 for (uint32_t y = 0; y < data->height; y ++)
3429 {
3430 memcpy (dstShapePtr, srcShapePtr, srcShapePtrScan);
3431 srcShapePtr += srcShapePtrScan;
3432 dstShapePtr += data->width;
3433 }
3434
3435 ICONINFO ii;
3436 ii.fIcon = FALSE;
3437 ii.xHotspot = data->xHot;
3438 ii.yHotspot = data->yHot;
3439 ii.hbmMask = hMonoBitmap;
3440 ii.hbmColor = hBitmap;
3441
3442 hAlphaCursor = ::CreateIconIndirect (&ii);
3443 Assert (hAlphaCursor);
3444 if (hAlphaCursor)
3445 {
3446 // here we do a dirty trick by substituting a Window Manager's
3447 // cursor handle with the handle we created
3448
3449 WMcursor *pCustomTempWMCursor = gpCustomCursor->wm_cursor;
3450
3451 // see SDL12/src/video/wincommon/SDL_sysmouse.c
3452 void *wm_cursor = malloc (sizeof (HCURSOR) + sizeof (uint8_t *) * 2);
3453 *(HCURSOR *) wm_cursor = hAlphaCursor;
3454
3455 gpCustomCursor->wm_cursor = (WMcursor *) wm_cursor;
3456 SDL_SetCursor (gpCustomCursor);
3457 SDL_ShowCursor (SDL_ENABLE);
3458
3459 if (pCustomTempWMCursor)
3460 {
3461 ::DestroyCursor (* (HCURSOR *) pCustomTempWMCursor);
3462 free (pCustomTempWMCursor);
3463 }
3464
3465 ok = true;
3466 }
3467 }
3468
3469 if (hMonoBitmap)
3470 ::DeleteObject (hMonoBitmap);
3471 if (hBitmap)
3472 ::DeleteObject (hBitmap);
3473
3474#elif defined (__LINUX__)
3475
3476 XcursorImage *img = XcursorImageCreate (data->width, data->height);
3477 Assert (img);
3478 if (img)
3479 {
3480 img->xhot = data->xHot;
3481 img->yhot = data->yHot;
3482
3483 XcursorPixel *dstShapePtr = img->pixels;
3484
3485 for (uint32_t y = 0; y < data->height; y ++)
3486 {
3487 memcpy (dstShapePtr, srcShapePtr, srcShapePtrScan);
3488
3489 if (!data->alpha)
3490 {
3491 // convert AND mask to the alpha channel
3492 uint8_t byte = 0;
3493 for (uint32_t x = 0; x < data->width; x ++)
3494 {
3495 if (!(x % 8))
3496 byte = *(srcAndMaskPtr ++);
3497 else
3498 byte <<= 1;
3499
3500 if (byte & 0x80)
3501 {
3502 // Linux doesn't support inverted pixels (XOR ops,
3503 // to be exact) in cursor shapes, so we detect such
3504 // pixels and always replace them with black ones to
3505 // make them visible at least over light colors
3506 if (dstShapePtr [x] & 0x00FFFFFF)
3507 dstShapePtr [x] = 0xFF000000;
3508 else
3509 dstShapePtr [x] = 0x00000000;
3510 }
3511 else
3512 dstShapePtr [x] |= 0xFF000000;
3513 }
3514 }
3515
3516 srcShapePtr += srcShapePtrScan;
3517 dstShapePtr += data->width;
3518 }
3519
3520 Cursor cur = XcursorImageLoadCursor (gSdlInfo.info.x11.display, img);
3521 Assert (cur);
3522 if (cur)
3523 {
3524 // here we do a dirty trick by substituting a Window Manager's
3525 // cursor handle with the handle we created
3526
3527 WMcursor *pCustomTempWMCursor = gpCustomCursor->wm_cursor;
3528
3529 // see SDL12/src/video/x11/SDL_x11mouse.c
3530 void *wm_cursor = malloc (sizeof (Cursor));
3531 *(Cursor *) wm_cursor = cur;
3532
3533 gpCustomCursor->wm_cursor = (WMcursor *) wm_cursor;
3534 SDL_SetCursor (gpCustomCursor);
3535 SDL_ShowCursor (SDL_ENABLE);
3536
3537 if (pCustomTempWMCursor)
3538 {
3539 XFreeCursor (gSdlInfo.info.x11.display, *(Cursor *) pCustomTempWMCursor);
3540 free (pCustomTempWMCursor);
3541 }
3542
3543 ok = true;
3544 }
3545
3546 XcursorImageDestroy (img);
3547 }
3548
3549#endif
3550
3551 if (!ok)
3552 {
3553 SDL_SetCursor (gpDefaultCursor);
3554 SDL_ShowCursor (SDL_ENABLE);
3555 }
3556 }
3557 else
3558 {
3559 if (data->visible)
3560 SDL_ShowCursor (SDL_ENABLE);
3561 else if (gfAbsoluteMouseGuest)
3562 /* Don't disable the cursor if the guest additions are not active (anymore) */
3563 SDL_ShowCursor (SDL_DISABLE);
3564 }
3565}
3566
3567/**
3568 * Handle changed mouse capabilities
3569 */
3570static void HandleGuestCapsChanged(void)
3571{
3572 if (!gfAbsoluteMouseGuest)
3573 {
3574 // Cursor could be overwritten by the guest tools
3575 SDL_SetCursor(gpDefaultCursor);
3576 SDL_ShowCursor (SDL_ENABLE);
3577 gpOffCursor = NULL;
3578 }
3579 if (gMouse && UseAbsoluteMouse())
3580 {
3581 // Actually switch to absolute coordinates
3582 if (gfGrabbed)
3583 InputGrabEnd();
3584 gMouse->PutMouseEventAbsolute(-1, -1, 0, 0);
3585 }
3586}
3587
3588/**
3589 * Handles a host key down event
3590 */
3591static int HandleHostKey(const SDL_KeyboardEvent *pEv)
3592{
3593 /*
3594 * Revalidate the host key modifier
3595 */
3596 if ((SDL_GetModState() & ~(KMOD_MODE | KMOD_NUM | KMOD_RESERVED)) != gHostKeyMod)
3597 return VERR_NOT_SUPPORTED;
3598
3599 /*
3600 * What was pressed?
3601 */
3602 switch (pEv->keysym.sym)
3603 {
3604 /* Control-Alt-Delete */
3605 case SDLK_DELETE:
3606 {
3607 gKeyboard->PutCAD();
3608 break;
3609 }
3610
3611 /*
3612 * Fullscreen / Windowed toggle.
3613 */
3614 case SDLK_f:
3615 {
3616 if (gfAllowFullscreenToggle)
3617 {
3618 /*
3619 * We have to pause/resume the machine during this
3620 * process because there might be a short moment
3621 * without a valid framebuffer
3622 */
3623 MachineState_T machineState;
3624 gMachine->COMGETTER(State)(&machineState);
3625 if (machineState == MachineState_Running)
3626 gConsole->Pause();
3627 gpFrameBuffer->setFullscreen(!gpFrameBuffer->getFullscreen());
3628 if (machineState == MachineState_Running)
3629 gConsole->Resume();
3630
3631 /*
3632 * We have switched from/to fullscreen, so request a full
3633 * screen repaint, just to be sure.
3634 */
3635 gDisplay->InvalidateAndUpdate();
3636 }
3637 break;
3638 }
3639
3640 /*
3641 * Pause / Resume toggle.
3642 */
3643 case SDLK_p:
3644 {
3645 MachineState_T machineState;
3646 gMachine->COMGETTER(State)(&machineState);
3647 if (machineState == MachineState_Running)
3648 {
3649 if (gfGrabbed)
3650 InputGrabEnd();
3651 gConsole->Pause();
3652 }
3653 else if (machineState == MachineState_Paused)
3654 {
3655 gConsole->Resume();
3656 }
3657 UpdateTitlebar(TITLEBAR_NORMAL);
3658 break;
3659 }
3660
3661 /*
3662 * Reset the VM
3663 */
3664 case SDLK_r:
3665 {
3666 ResetVM();
3667 break;
3668 }
3669
3670 /*
3671 * Terminate the VM
3672 */
3673 case SDLK_q:
3674 {
3675 return VINF_EM_TERMINATE;
3676 break;
3677 }
3678
3679 /*
3680 * Save the machine's state and exit
3681 */
3682 case SDLK_s:
3683 {
3684 SaveState();
3685 return VINF_EM_TERMINATE;
3686 }
3687
3688 case SDLK_h:
3689 {
3690 if (gConsole)
3691 gConsole->PowerButton();
3692 break;
3693 }
3694
3695 /*
3696 * Perform an online snapshot. Continue operation.
3697 */
3698 case SDLK_n:
3699 {
3700 RTThreadYield();
3701 ULONG cSnapshots = 0;
3702 gMachine->COMGETTER(SnapshotCount)(&cSnapshots);
3703 char pszSnapshotName[20];
3704 RTStrPrintf(pszSnapshotName, sizeof(pszSnapshotName), "Snapshot %d", cSnapshots + 1);
3705 gProgress = NULL;
3706 HRESULT rc;
3707 CHECK_ERROR(gConsole, TakeSnapshot(Bstr(pszSnapshotName), Bstr("Taken by VBoxSDL"),
3708 gProgress.asOutParam()));
3709 if (FAILED(rc))
3710 {
3711 RTPrintf("Error taking snapshot! rc = 0x%x\n", rc);
3712 /* continue operation */
3713 return VINF_SUCCESS;
3714 }
3715 /*
3716 * Wait for the operation to be completed and work
3717 * the title bar in the mean while.
3718 */
3719 LONG cPercent = 0;
3720 for (;;)
3721 {
3722 BOOL fCompleted = false;
3723 rc = gProgress->COMGETTER(Completed)(&fCompleted);
3724 if (FAILED(rc) || fCompleted)
3725 break;
3726 LONG cPercentNow;
3727 rc = gProgress->COMGETTER(Percent)(&cPercentNow);
3728 if (FAILED(rc))
3729 break;
3730 if (cPercentNow != cPercent)
3731 {
3732 UpdateTitlebar(TITLEBAR_SNAPSHOT, cPercent);
3733 cPercent = cPercentNow;
3734 }
3735
3736 /* wait */
3737 rc = gProgress->WaitForCompletion(100);
3738 if (FAILED(rc))
3739 break;
3740 /// @todo process gui events.
3741 }
3742
3743 /* continue operation */
3744 return VINF_SUCCESS;
3745 }
3746
3747 case SDLK_F1: case SDLK_F2: case SDLK_F3:
3748 case SDLK_F4: case SDLK_F5: case SDLK_F6:
3749 case SDLK_F7: case SDLK_F8: case SDLK_F9:
3750 case SDLK_F10: case SDLK_F11: case SDLK_F12:
3751 {
3752 /* send Ctrl-Alt-Fx to guest */
3753 static LONG keySequence[] = {
3754 0x1d, // Ctrl down
3755 0x38, // Alt down
3756 0x00, // Fx down (placeholder)
3757 0x00, // Fx up (placeholder)
3758 0xb8, // Alt up
3759 0x9d // Ctrl up
3760 };
3761
3762 /* put in the right Fx key */
3763 keySequence[2] = Keyevent2Keycode(pEv);
3764 keySequence[3] = keySequence[2] + 0x80;
3765
3766 gKeyboard->PutScancodes(keySequence, ELEMENTS(keySequence), NULL);
3767 return VINF_SUCCESS;
3768 }
3769
3770 /*
3771 * Not a host key combination.
3772 * Indicate this by returning false.
3773 */
3774 default:
3775 return VERR_NOT_SUPPORTED;
3776 }
3777
3778 return VINF_SUCCESS;
3779}
3780
3781/**
3782 * Timer callback function for startup processing
3783 */
3784static Uint32 StartupTimer(Uint32 interval, void *param)
3785{
3786 /* post message so we can do something in the startup loop */
3787 SDL_Event event = {0};
3788 event.type = SDL_USEREVENT;
3789 event.user.type = SDL_USER_EVENT_TIMER;
3790 SDL_PushEvent(&event);
3791 return interval;
3792}
Note: See TracBrowser for help on using the repository browser.

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