VirtualBox

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

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

r=bird: int != u. Cnt == c.

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

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