VirtualBox

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

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

FE/SDL: don't pass mouse clicks to the guest in relative mode if mouse is not grabbed (e.g. -nograbonclick activated)

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