VirtualBox

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

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

Main/Frontends: Changed all interface attributes and method parameters that used to use ULONG to pass pointers around; they now use BYTE * for this purpose (should fix problems for 64-bit targets).

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

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