VirtualBox

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

Last change on this file was 103402, checked in by vboxsync, 3 months ago

FE/VBoxSDL: Fix typo introduced when cleaning up copyright text.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 140.9 KB
RevLine 
[55401]1/* $Id: VBoxSDL.cpp 103402 2024-02-16 15:46:53Z vboxsync $ */
[1]2/** @file
3 * VBox frontends: VBoxSDL (simple frontend based on SDL):
4 * Main code
5 */
6
7/*
[98103]8 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
[1]9 *
[96407]10 * This file is part of VirtualBox base platform packages, as
11 * available from https://www.virtualbox.org.
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation, in version 3 of the
16 * License.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, see <https://www.gnu.org/licenses>.
25 *
26 * SPDX-License-Identifier: GPL-3.0-only
[1]27 */
28
[57358]29
30/*********************************************************************************************************************************
31* Header Files *
32*********************************************************************************************************************************/
[1]33#define LOG_GROUP LOG_GROUP_GUI
34
[98343]35#include <iprt/stream.h>
36
[1]37#include <VBox/com/com.h>
38#include <VBox/com/string.h>
39#include <VBox/com/Guid.h>
[7379]40#include <VBox/com/array.h>
[1]41#include <VBox/com/ErrorInfo.h>
[20928]42#include <VBox/com/errorprint.h>
[16530]43
[46649]44#include <VBox/com/NativeEventQueue.h>
[1]45#include <VBox/com/VirtualBox.h>
46
47using namespace com;
48
[28205]49#if defined(VBOXSDL_WITH_X11)
[29655]50# include <VBox/VBoxKeyboard.h>
51
[4866]52# include <X11/Xlib.h>
53# include <X11/cursorfont.h> /* for XC_left_ptr */
[28205]54# if !defined(VBOX_WITHOUT_XCURSOR)
[4866]55# include <X11/Xcursor/Xcursor.h>
56# endif
[10514]57# include <unistd.h>
[1]58#endif
59
[81537]60#include "VBoxSDL.h"
61
[63298]62#ifdef _MSC_VER
63# pragma warning(push)
64# pragma warning(disable: 4121) /* warning C4121: 'SDL_SysWMmsg' : alignment of a member was sensitive to packing*/
65#endif
[3667]66#ifndef RT_OS_DARWIN
[63298]67# include <SDL_syswm.h> /* for SDL_GetWMInfo() */
[3667]68#endif
[63298]69#ifdef _MSC_VER
70# pragma warning(pop)
71#endif
[2540]72
[1]73#include "Framebuffer.h"
74#include "Helper.h"
75
76#include <VBox/types.h>
77#include <VBox/err.h>
78#include <VBox/param.h>
79#include <VBox/log.h>
80#include <VBox/version.h>
[65381]81#include <VBoxVideo.h>
[33972]82#include <VBox/com/listeners.h>
[11419]83
84#include <iprt/alloca.h>
[14831]85#include <iprt/asm.h>
[11419]86#include <iprt/assert.h>
[42446]87#include <iprt/ctype.h>
[11419]88#include <iprt/env.h>
[14831]89#include <iprt/file.h>
[11419]90#include <iprt/ldr.h>
[14831]91#include <iprt/initterm.h>
[38636]92#include <iprt/message.h>
[1]93#include <iprt/path.h>
[14831]94#include <iprt/process.h>
[1]95#include <iprt/semaphore.h>
[11419]96#include <iprt/string.h>
[1]97#include <iprt/stream.h>
98#include <iprt/uuid.h>
99
100#include <signal.h>
101
102#include <vector>
[7379]103#include <list>
[1]104
[80569]105#include "PasswordInput.h"
106
[1]107/* Xlib would re-define our enums */
108#undef True
109#undef False
110
[57358]111
112/*********************************************************************************************************************************
113* Defined Constants And Macros *
114*********************************************************************************************************************************/
[1]115/** Enables the rawr[0|3], patm, and casm options. */
116#define VBOXSDL_ADVANCED_OPTIONS
117
[57358]118
119/*********************************************************************************************************************************
120* Structures and Typedefs *
121*********************************************************************************************************************************/
[33540]122/** Pointer shape change event data structure */
[1]123struct PointerShapeChangeData
124{
[28205]125 PointerShapeChangeData(BOOL aVisible, BOOL aAlpha, ULONG aXHot, ULONG aYHot,
[29518]126 ULONG aWidth, ULONG aHeight, ComSafeArrayIn(BYTE,pShape))
[28205]127 : visible(aVisible), alpha(aAlpha), xHot(aXHot), yHot(aYHot),
[29518]128 width(aWidth), height(aHeight)
[1]129 {
130 // make a copy of the shape
[34645]131 com::SafeArray<BYTE> aShape(ComSafeArrayInArg(pShape));
[29518]132 size_t cbShapeSize = aShape.size();
[29542]133 if (cbShapeSize > 0)
134 {
135 shape.resize(cbShapeSize);
136 ::memcpy(shape.raw(), aShape.raw(), cbShapeSize);
137 }
[1]138 }
139
140 ~PointerShapeChangeData()
141 {
142 }
143
144 const BOOL visible;
145 const BOOL alpha;
146 const ULONG xHot;
147 const ULONG yHot;
148 const ULONG width;
149 const ULONG height;
[29518]150 com::SafeArray<BYTE> shape;
[1]151};
152
153enum TitlebarMode
154{
155 TITLEBAR_NORMAL = 1,
156 TITLEBAR_STARTUP = 2,
157 TITLEBAR_SAVE = 3,
158 TITLEBAR_SNAPSHOT = 4
159};
160
[57358]161
162/*********************************************************************************************************************************
163* Internal Functions *
164*********************************************************************************************************************************/
[1]165static bool UseAbsoluteMouse(void);
166static void ResetKeys(void);
167static void ProcessKey(SDL_KeyboardEvent *ev);
168static void InputGrabStart(void);
169static void InputGrabEnd(void);
[20433]170static void SendMouseEvent(VBoxSDLFB *fb, int dz, int button, int down);
[1]171static void UpdateTitlebar(TitlebarMode mode, uint32_t u32User = 0);
172static void SetPointerShape(const PointerShapeChangeData *data);
173static void HandleGuestCapsChanged(void);
174static int HandleHostKey(const SDL_KeyboardEvent *pEv);
[85121]175static Uint32 StartupTimer(Uint32 interval, void *param) RT_NOTHROW_PROTO;
176static Uint32 ResizeTimer(Uint32 interval, void *param) RT_NOTHROW_PROTO;
177static Uint32 QuitTimer(Uint32 interval, void *param) RT_NOTHROW_PROTO;
[3289]178static int WaitSDLEvent(SDL_Event *event);
[10675]179static void SetFullscreen(bool enable);
[81537]180static VBoxSDLFB *getFbFromWinId(Uint32 id);
[1]181
[98306]182
[57358]183/*********************************************************************************************************************************
184* Global Variables *
185*********************************************************************************************************************************/
[1]186static int gHostKeyMod = KMOD_RCTRL;
187static int gHostKeySym1 = SDLK_RCTRL;
188static int gHostKeySym2 = SDLK_UNKNOWN;
[10434]189static const char *gHostKeyDisabledCombinations = "";
[10501]190static const char *gpszPidFile;
[1]191static BOOL gfGrabbed = FALSE;
192static BOOL gfGrabOnMouseClick = TRUE;
[10675]193static BOOL gfFullscreenResize = FALSE;
194static BOOL gfIgnoreNextResize = FALSE;
[1]195static BOOL gfAllowFullscreenToggle = TRUE;
196static BOOL gfAbsoluteMouseHost = FALSE;
197static BOOL gfAbsoluteMouseGuest = FALSE;
[26782]198static BOOL gfRelativeMouseGuest = TRUE;
[1]199static BOOL gfGuestNeedsHostCursor = FALSE;
200static BOOL gfOffCursorActive = FALSE;
201static BOOL gfGuestNumLockPressed = FALSE;
202static BOOL gfGuestCapsLockPressed = FALSE;
203static BOOL gfGuestScrollLockPressed = FALSE;
[6437]204static BOOL gfACPITerm = FALSE;
[98371]205#if defined(VBOXSDL_WITH_X11) && !defined(VBOX_WITHOUT_XCURSOR)
206 static BOOL gfXCursorEnabled = FALSE;
207#endif
[315]208static int gcGuestNumLockAdaptions = 2;
209static int gcGuestCapsLockAdaptions = 2;
[10675]210static uint32_t gmGuestNormalXRes;
211static uint32_t gmGuestNormalYRes;
[1]212
213/** modifier keypress status (scancode as index) */
214static uint8_t gaModifiersState[256];
215
[34645]216static ComPtr<IMachine> gpMachine;
217static ComPtr<IConsole> gpConsole;
218static ComPtr<IMachineDebugger> gpMachineDebugger;
219static ComPtr<IKeyboard> gpKeyboard;
220static ComPtr<IMouse> gpMouse;
[51436]221ComPtr<IDisplay> gpDisplay;
[34645]222static ComPtr<IVRDEServer> gpVRDEServer;
223static ComPtr<IProgress> gpProgress;
[1]224
[20435]225static ULONG gcMonitors = 1;
[51677]226static ComObjPtr<VBoxSDLFB> gpFramebuffer[64];
[55133]227static Bstr gaFramebufferId[64];
[1]228static SDL_Cursor *gpDefaultCursor = NULL;
229static SDL_Cursor *gpOffCursor = NULL;
[98301]230static SDL_TimerID gSdlResizeTimer = 0;
231static SDL_TimerID gSdlQuitTimer = 0;
[1]232
[3289]233static RTSEMEVENT g_EventSemSDLEvents;
234static volatile int32_t g_cNotifyUpdateEventsPending;
235
[1]236/**
[34646]237 * Event handler for VirtualBoxClient events
238 */
239class VBoxSDLClientEventListener
240{
241public:
242 VBoxSDLClientEventListener()
243 {
244 }
245
246 virtual ~VBoxSDLClientEventListener()
247 {
248 }
249
[35722]250 HRESULT init()
251 {
252 return S_OK;
253 }
254
255 void uninit()
256 {
257 }
258
[34646]259 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent * aEvent)
260 {
261 switch (aType)
262 {
[35172]263 case VBoxEventType_OnVBoxSVCAvailabilityChanged:
[34646]264 {
[35172]265 ComPtr<IVBoxSVCAvailabilityChangedEvent> pVSACEv = aEvent;
266 Assert(pVSACEv);
267 BOOL fAvailable = FALSE;
268 pVSACEv->COMGETTER(Available)(&fAvailable);
269 if (!fAvailable)
270 {
271 LogRel(("VBoxSDL: VBoxSVC became unavailable, exiting.\n"));
272 RTPrintf("VBoxSVC became unavailable, exiting.\n");
273 /* Send QUIT event to terminate the VM as cleanly as possible
274 * given that VBoxSVC is no longer present. */
275 SDL_Event event = {0};
276 event.type = SDL_QUIT;
277 PushSDLEventForSure(&event);
278 }
[34646]279 break;
280 }
281
282 default:
283 AssertFailed();
284 }
285
286 return S_OK;
287 }
288};
289
290/**
[34645]291 * Event handler for VirtualBox (server) events
[1]292 */
[33963]293class VBoxSDLEventListener
[1]294{
295public:
[30618]296 VBoxSDLEventListener()
[1]297 {
298 }
299
[30618]300 virtual ~VBoxSDLEventListener()
[1]301 {
302 }
303
[35722]304 HRESULT init()
305 {
306 return S_OK;
307 }
308
309 void uninit()
310 {
311 }
312
[33963]313 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent * aEvent)
[1]314 {
[63298]315 RT_NOREF(aEvent);
[30618]316 switch (aType)
[1]317 {
[30871]318 case VBoxEventType_OnExtraDataChanged:
[30618]319 break;
320 default:
321 AssertFailed();
[1]322 }
[30618]323
[28931]324 return S_OK;
[1]325 }
326};
327
328/**
[34645]329 * Event handler for Console events
[1]330 */
[33963]331class VBoxSDLConsoleEventListener
[1]332{
333public:
[30618]334 VBoxSDLConsoleEventListener() : m_fIgnorePowerOffEvents(false)
[1]335 {
336 }
337
[30618]338 virtual ~VBoxSDLConsoleEventListener()
[1]339 {
340 }
341
[35722]342 HRESULT init()
343 {
344 return S_OK;
345 }
346
347 void uninit()
348 {
349 }
350
[33963]351 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent * aEvent)
[1]352 {
[30618]353 // likely all this double copy is now excessive, and we can just use existing event object
[63567]354 /// @todo eliminate it
[30618]355 switch (aType)
356 {
[30871]357 case VBoxEventType_OnMousePointerShapeChanged:
[30618]358 {
[34645]359 ComPtr<IMousePointerShapeChangedEvent> pMPSCEv = aEvent;
360 Assert(pMPSCEv);
[30618]361 PointerShapeChangeData *data;
362 BOOL visible, alpha;
363 ULONG xHot, yHot, width, height;
[34645]364 com::SafeArray<BYTE> shape;
[1]365
[34645]366 pMPSCEv->COMGETTER(Visible)(&visible);
367 pMPSCEv->COMGETTER(Alpha)(&alpha);
368 pMPSCEv->COMGETTER(Xhot)(&xHot);
369 pMPSCEv->COMGETTER(Yhot)(&yHot);
370 pMPSCEv->COMGETTER(Width)(&width);
371 pMPSCEv->COMGETTER(Height)(&height);
372 pMPSCEv->COMGETTER(Shape)(ComSafeArrayAsOutParam(shape));
[30618]373 data = new PointerShapeChangeData(visible, alpha, xHot, yHot, width, height,
374 ComSafeArrayAsInParam(shape));
375 Assert(data);
376 if (!data)
377 break;
[1]378
[30618]379 SDL_Event event = {0};
380 event.type = SDL_USEREVENT;
381 event.user.type = SDL_USER_EVENT_POINTER_CHANGE;
382 event.user.data1 = data;
[1]383
[30618]384 int rc = PushSDLEventForSure(&event);
385 if (rc)
386 delete data;
[1]387
[30618]388 break;
389 }
[30871]390 case VBoxEventType_OnMouseCapabilityChanged:
[30618]391 {
[34645]392 ComPtr<IMouseCapabilityChangedEvent> pMCCEv = aEvent;
393 Assert(pMCCEv);
394 pMCCEv->COMGETTER(SupportsAbsolute)(&gfAbsoluteMouseGuest);
395 pMCCEv->COMGETTER(SupportsRelative)(&gfRelativeMouseGuest);
396 pMCCEv->COMGETTER(NeedsHostCursor)(&gfGuestNeedsHostCursor);
[30618]397 SDL_Event event = {0};
398 event.type = SDL_USEREVENT;
399 event.user.type = SDL_USER_EVENT_GUEST_CAP_CHANGED;
[1]400
[30618]401 PushSDLEventForSure(&event);
402 break;
403 }
[30871]404 case VBoxEventType_OnKeyboardLedsChanged:
[30618]405 {
[34645]406 ComPtr<IKeyboardLedsChangedEvent> pCLCEv = aEvent;
407 Assert(pCLCEv);
[30618]408 BOOL fNumLock, fCapsLock, fScrollLock;
[34645]409 pCLCEv->COMGETTER(NumLock)(&fNumLock);
410 pCLCEv->COMGETTER(CapsLock)(&fCapsLock);
411 pCLCEv->COMGETTER(ScrollLock)(&fScrollLock);
[30618]412 /* Don't bother the guest with NumLock scancodes if he doesn't set the NumLock LED */
413 if (gfGuestNumLockPressed != fNumLock)
414 gcGuestNumLockAdaptions = 2;
415 if (gfGuestCapsLockPressed != fCapsLock)
416 gcGuestCapsLockAdaptions = 2;
417 gfGuestNumLockPressed = fNumLock;
418 gfGuestCapsLockPressed = fCapsLock;
419 gfGuestScrollLockPressed = fScrollLock;
420 break;
421 }
[1]422
[30871]423 case VBoxEventType_OnStateChanged:
[30618]424 {
[34645]425 ComPtr<IStateChangedEvent> pSCEv = aEvent;
426 Assert(pSCEv);
[30618]427 MachineState_T machineState;
[34645]428 pSCEv->COMGETTER(State)(&machineState);
[30618]429 LogFlow(("OnStateChange: machineState = %d (%s)\n", machineState, GetStateName(machineState)));
430 SDL_Event event = {0};
[4131]431
[30618]432 if ( machineState == MachineState_Aborted
433 || machineState == MachineState_Teleported
[91363]434 || (machineState == MachineState_Saved && !m_fIgnorePowerOffEvents)
435 || (machineState == MachineState_AbortedSaved && !m_fIgnorePowerOffEvents)
436 || (machineState == MachineState_PoweredOff && !m_fIgnorePowerOffEvents)
[30618]437 )
438 {
439 /*
440 * We have to inform the SDL thread that the application has be terminated
441 */
442 event.type = SDL_USEREVENT;
443 event.user.type = SDL_USER_EVENT_TERMINATE;
444 event.user.code = machineState == MachineState_Aborted
[24301]445 ? VBOXSDL_TERM_ABEND
446 : VBOXSDL_TERM_NORMAL;
[30618]447 }
448 else
449 {
450 /*
451 * Inform the SDL thread to refresh the titlebar
452 */
453 event.type = SDL_USEREVENT;
454 event.user.type = SDL_USER_EVENT_UPDATE_TITLEBAR;
455 }
[1]456
[30618]457 PushSDLEventForSure(&event);
458 break;
459 }
[1]460
[30618]461 case VBoxEventType_OnRuntimeError:
462 {
[34645]463 ComPtr<IRuntimeErrorEvent> pRTEEv = aEvent;
464 Assert(pRTEEv);
[30618]465 BOOL fFatal;
[1]466
[34645]467 pRTEEv->COMGETTER(Fatal)(&fFatal);
[30618]468 MachineState_T machineState;
[34645]469 gpMachine->COMGETTER(State)(&machineState);
[30618]470 const char *pszType;
471 bool fPaused = machineState == MachineState_Paused;
472 if (fFatal)
473 pszType = "FATAL ERROR";
474 else if (machineState == MachineState_Paused)
475 pszType = "Non-fatal ERROR";
476 else
477 pszType = "WARNING";
[34645]478 Bstr bstrId, bstrMessage;
479 pRTEEv->COMGETTER(Id)(bstrId.asOutParam());
480 pRTEEv->COMGETTER(Message)(bstrMessage.asOutParam());
[38735]481 RTPrintf("\n%s: ** %ls **\n%ls\n%s\n", pszType, bstrId.raw(), bstrMessage.raw(),
[30618]482 fPaused ? "The VM was paused. Continue with HostKey + P after you solved the problem.\n" : "");
483 break;
484 }
[4131]485
[30618]486 case VBoxEventType_OnCanShowWindow:
487 {
[34645]488 ComPtr<ICanShowWindowEvent> pCSWEv = aEvent;
489 Assert(pCSWEv);
[3667]490#ifdef RT_OS_DARWIN
[30618]491 /* SDL feature not available on Quartz */
[3667]492#else
[81537]493 bool fCanShow = false;
494 Uint32 winId = 0;
495 VBoxSDLFB *fb = getFbFromWinId(winId);
[99090]496 if (fb) /* Framebuffer might not be around (yet). */
497 {
498 SDL_SysWMinfo info;
499 SDL_VERSION(&info.version);
500 if (SDL_GetWindowWMInfo(fb->getWindow(), &info))
501 fCanShow = true;
502 if (fCanShow)
503 pCSWEv->AddApproval(NULL);
504 else
505 pCSWEv->AddVeto(NULL);
506 }
[3667]507#endif
[30618]508 break;
509 }
[2463]510
[30618]511 case VBoxEventType_OnShowWindow:
512 {
[34645]513 ComPtr<IShowWindowEvent> pSWEv = aEvent;
514 Assert(pSWEv);
[55543]515 LONG64 winId = 0;
516 pSWEv->COMGETTER(WinId)(&winId);
517 if (winId != 0)
518 break; /* WinId already set by some other listener. */
[3667]519#ifndef RT_OS_DARWIN
[30618]520 SDL_SysWMinfo info;
521 SDL_VERSION(&info.version);
[81537]522 VBoxSDLFB *fb = getFbFromWinId(winId);
523 if (SDL_GetWindowWMInfo(fb->getWindow(), &info))
[30618]524 {
[63298]525# if defined(VBOXSDL_WITH_X11)
[98246]526 pSWEv->COMSETTER(WinId)((LONG64)info.info.x11.window);
[63298]527# elif defined(RT_OS_WINDOWS)
[81537]528 pSWEv->COMSETTER(WinId)((intptr_t)info.info.win.window);
529# else /* !RT_OS_WINDOWS */
[30618]530 AssertFailed();
[63298]531# endif
[30618]532 }
533#endif /* !RT_OS_DARWIN */
534 break;
535 }
536
537 default:
538 AssertFailed();
[2540]539 }
[30618]540 return S_OK;
[2463]541 }
542
[1]543 static const char *GetStateName(MachineState_T machineState)
544 {
545 switch (machineState)
546 {
[23879]547 case MachineState_Null: return "<null>";
548 case MachineState_PoweredOff: return "PoweredOff";
549 case MachineState_Saved: return "Saved";
[24301]550 case MachineState_Teleported: return "Teleported";
[23879]551 case MachineState_Aborted: return "Aborted";
[91363]552 case MachineState_AbortedSaved: return "Aborted-Saved";
[23879]553 case MachineState_Running: return "Running";
[24301]554 case MachineState_Teleporting: return "Teleporting";
555 case MachineState_LiveSnapshotting: return "LiveSnapshotting";
[23879]556 case MachineState_Paused: return "Paused";
557 case MachineState_Stuck: return "GuruMeditation";
558 case MachineState_Starting: return "Starting";
559 case MachineState_Stopping: return "Stopping";
560 case MachineState_Saving: return "Saving";
561 case MachineState_Restoring: return "Restoring";
[24301]562 case MachineState_TeleportingPausedVM: return "TeleportingPausedVM";
563 case MachineState_TeleportingIn: return "TeleportingIn";
[23879]564 case MachineState_RestoringSnapshot: return "RestoringSnapshot";
565 case MachineState_DeletingSnapshot: return "DeletingSnapshot";
566 case MachineState_SettingUp: return "SettingUp";
567 default: return "no idea";
[1]568 }
569 }
570
571 void ignorePowerOffEvents(bool fIgnore)
572 {
573 m_fIgnorePowerOffEvents = fIgnore;
574 }
575
576private:
577 bool m_fIgnorePowerOffEvents;
578};
579
[34646]580typedef ListenerImpl<VBoxSDLClientEventListener> VBoxSDLClientEventListenerImpl;
[33968]581typedef ListenerImpl<VBoxSDLEventListener> VBoxSDLEventListenerImpl;
582typedef ListenerImpl<VBoxSDLConsoleEventListener> VBoxSDLConsoleEventListenerImpl;
[1]583
584static void show_usage()
585{
586 RTPrintf("Usage:\n"
[18751]587 " --startvm <uuid|name> Virtual machine to start, either UUID or name\n"
[51677]588 " --separate Run a separate VM process or attach to a running VM\n"
[18751]589 " --hda <file> Set temporary first hard disk to file\n"
590 " --fda <file> Set temporary first floppy disk to file\n"
591 " --cdrom <file> Set temporary CDROM/DVD to file/device ('none' to unmount)\n"
592 " --boot <a|c|d|n> Set temporary boot device (a = floppy, c = 1st HD, d = DVD, n = network)\n"
593 " --memory <size> Set temporary memory size in megabytes\n"
594 " --vram <size> Set temporary size of video memory in megabytes\n"
595 " --fullscreen Start VM in fullscreen mode\n"
596 " --fullscreenresize Resize the guest on fullscreen\n"
597 " --fixedmode <w> <h> <bpp> Use a fixed SDL video mode with given width, height and bits per pixel\n"
598 " --nofstoggle Forbid switching to/from fullscreen mode\n"
599 " --noresize Make the SDL frame non resizable\n"
600 " --nohostkey Disable all hostkey combinations\n"
601 " --nohostkeys ... Disable specific hostkey combinations, see below for valid keys\n"
602 " --nograbonclick Disable mouse/keyboard grabbing on mouse click w/o additions\n"
603 " --detecthostkey Get the hostkey identifier and modifier state\n"
604 " --hostkey <key> {<key2>} <mod> Set the host key to the values obtained using --detecthostkey\n"
605 " --termacpi Send an ACPI power button event when closing the window\n"
[23998]606 " --vrdp <ports> Listen for VRDP connections on one of specified ports (default if not specified)\n"
[18751]607 " --discardstate Discard saved state (if present) and revert to last snapshot (if present)\n"
[42472]608 " --settingspw <pw> Specify the settings password\n"
609 " --settingspwfile <file> Specify a file containing the settings password\n"
[1]610#ifdef VBOXSDL_ADVANCED_OPTIONS
[93460]611 " --warpdrive <pct> Sets the warp driver rate in percent (100 = normal)\n"
[1]612#endif
[5084]613 "\n"
614 "Key bindings:\n"
615 " <hostkey> + f Switch to full screen / restore to previous view\n"
616 " h Press ACPI power button\n"
617 " n Take a snapshot and continue execution\n"
618 " p Pause / resume execution\n"
619 " q Power off\n"
620 " r VM reset\n"
621 " s Save state and power off\n"
622 " <del> Send <ctrl><alt><del>\n"
623 " <F1>...<F12> Send <ctrl><alt><Fx>\n"
[8834]624#if defined(DEBUG) || defined(VBOX_WITH_STATISTICS)
625 "\n"
626 "Further key bindings useful for debugging:\n"
627 " LCtrl + Alt + F12 Reset statistics counter\n"
628 " LCtrl + Alt + F11 Dump statistics to logfile\n"
629 " Alt + F8 Toggle single step mode\n"
630 " LCtrl/RCtrl + F12 Toggle logger\n"
631 " F12 Write log marker to logfile\n"
632#endif
[1]633 "\n");
634}
635
[15051]636static void PrintError(const char *pszName, CBSTR pwszDescr, CBSTR pwszComponent=NULL)
[1]637{
638 const char *pszFile, *pszFunc, *pszStat;
639 char pszBuffer[1024];
640 com::ErrorInfo info;
641
[38735]642 RTStrPrintf(pszBuffer, sizeof(pszBuffer), "%ls", pwszDescr);
[1]643
644 RTPrintf("\n%s! Error info:\n", pszName);
645 if ( (pszFile = strstr(pszBuffer, "At '"))
646 && (pszFunc = strstr(pszBuffer, ") in "))
647 && (pszStat = strstr(pszBuffer, "VBox status code: ")))
648 RTPrintf(" %.*s %.*s\n In%.*s %s",
649 pszFile-pszBuffer, pszBuffer,
650 pszFunc-pszFile+1, pszFile,
651 pszStat-pszFunc-4, pszFunc+4,
652 pszStat);
653 else
654 RTPrintf("%s\n", pszBuffer);
655
656 if (pwszComponent)
[38735]657 RTPrintf("(component %ls).\n", pwszComponent);
[1]658
659 RTPrintf("\n");
660}
661
[4637]662#ifdef VBOXSDL_WITH_X11
[1]663/**
664 * Custom signal handler. Currently it is only used to release modifier
665 * keys when receiving the USR1 signal. When switching VTs, we might not
666 * get release events for Ctrl-Alt and in case a savestate is performed
667 * on the new VT, the VM will be saved with modifier keys stuck. This is
668 * annoying enough for introducing this hack.
669 */
[10501]670void signal_handler_SIGUSR1(int sig, siginfo_t *info, void *secret)
[1]671{
[63384]672 RT_NOREF(info, secret);
673
[1]674 /* only SIGUSR1 is interesting */
675 if (sig == SIGUSR1)
676 {
677 /* just release the modifiers */
678 ResetKeys();
679 }
680}
[10501]681
682/**
683 * Custom signal handler for catching exit events.
684 */
685void signal_handler_SIGINT(int sig)
686{
687 if (gpszPidFile)
688 RTFileDelete(gpszPidFile);
689 signal(SIGINT, SIG_DFL);
690 signal(SIGQUIT, SIG_DFL);
691 signal(SIGSEGV, SIG_DFL);
692 kill(getpid(), sig);
693}
[4637]694#endif /* VBOXSDL_WITH_X11 */
[1]695
[98357]696/**
697 * Returns a stringified version of a keyboard modifier.
698 *
699 * @returns Stringified version of the keyboard modifier.
700 * @param mod Modifier code to return a stringified version for.
701 */
702static const char *keyModToStr(unsigned mod)
703{
704 switch (mod)
705 {
706 RT_CASE_RET_STR(KMOD_NONE);
707 RT_CASE_RET_STR(KMOD_LSHIFT);
708 RT_CASE_RET_STR(KMOD_RSHIFT);
709 RT_CASE_RET_STR(KMOD_LCTRL);
710 RT_CASE_RET_STR(KMOD_RCTRL);
711 RT_CASE_RET_STR(KMOD_LALT);
712 RT_CASE_RET_STR(KMOD_RALT);
713 RT_CASE_RET_STR(KMOD_LGUI);
714 RT_CASE_RET_STR(KMOD_RGUI);
715 RT_CASE_RET_STR(KMOD_NUM);
716 RT_CASE_RET_STR(KMOD_CAPS);
717 RT_CASE_RET_STR(KMOD_MODE);
718 RT_CASE_RET_STR(KMOD_SCROLL);
719 default:
720 break;
721 }
[35741]722
[98357]723 return "<Unknown>";
724}
725
726/**
727 * Handles detecting a host key by printing its values to stdout.
728 *
729 * @returns RTEXITCODE
730 */
731static RTEXITCODE handleDetectHostKey(void)
732{
733 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
734
735 int rc = SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_TIMER);
736 if (rc == 0)
737 {
738 /* We need a window, otherwise we won't get any keypress events. */
739 SDL_Window *pWnd = SDL_CreateWindow("VBoxSDL",
740 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_SHOWN);
741 RTPrintf("Please hit one or two function key(s) to get the --hostkey value. ..\n");
742 RTPrintf("Press CTRL+C to quit.\n");
743 SDL_Event e1;
744 while (SDL_WaitEvent(&e1))
745 {
746 if ( e1.key.keysym.sym == SDLK_c
747 && (e1.key.keysym.mod & KMOD_CTRL) != 0)
748 break;
749 if (e1.type == SDL_QUIT)
750 break;
751 if (e1.type == SDL_KEYDOWN)
752 {
753 unsigned const mod = SDL_GetModState() & ~(KMOD_MODE | KMOD_NUM | KMOD_RESERVED);
754 RTPrintf("--hostkey %d", e1.key.keysym.sym);
755 if (mod)
756 RTPrintf(" %d\n", mod);
757 else
758 RTPrintf("\n");
759
760 if (mod)
761 RTPrintf("Host key is '%s' + '%s'\n", keyModToStr(mod), SDL_GetKeyName(e1.key.keysym.sym));
762 else
763 RTPrintf("Host key is '%s'\n", SDL_GetKeyName(e1.key.keysym.sym));
764 }
765 }
766 SDL_DestroyWindow(pWnd);
767 SDL_Quit();
768 }
769 else
770 {
771 RTPrintf("Error: SDL_InitSubSystem failed with message '%s'\n", SDL_GetError());
772 rcExit = RTEXITCODE_FAILURE;
773 }
774
775 return rcExit;
776}
777
[1]778/** entry point */
[11725]779extern "C"
780DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
[1]781{
[63298]782 RT_NOREF(envp);
[60865]783#ifdef RT_OS_WINDOWS
[98357]784 /* As we run with the WINDOWS subsystem, we need to either attach to or create an own console
785 * to get any stdout / stderr output. */
786 bool fAllocConsole = IsDebuggerPresent();
787 if (!fAllocConsole)
788 {
789 if (!AttachConsole(ATTACH_PARENT_PROCESS))
790 fAllocConsole = true;
791 }
792
793 if (fAllocConsole)
794 {
795 if (!AllocConsole())
796 MessageBox(GetDesktopWindow(), L"Unable to attach to or allocate a console!", L"VBoxSDL", MB_OK | MB_ICONERROR);
797 /* Continue running. */
798 }
799
800 RTFILE hStdIn;
801 RTFileFromNative(&hStdIn, (RTHCINTPTR)GetStdHandle(STD_INPUT_HANDLE));
802 /** @todo Closing of standard handles not support via IPRT (yet). */
803 RTStrmOpenFileHandle(hStdIn, "r", 0, &g_pStdIn);
804
805 RTFILE hStdOut;
806 RTFileFromNative(&hStdOut, (RTHCINTPTR)GetStdHandle(STD_OUTPUT_HANDLE));
807 /** @todo Closing of standard handles not support via IPRT (yet). */
808 RTStrmOpenFileHandle(hStdOut, "wt", 0, &g_pStdOut);
809
810 RTFILE hStdErr;
811 RTFileFromNative(&hStdErr, (RTHCINTPTR)GetStdHandle(STD_ERROR_HANDLE));
812 RTStrmOpenFileHandle(hStdErr, "wt", 0, &g_pStdErr);
813
814 if (!fAllocConsole) /* When attaching to the parent console, make sure we start on a fresh line. */
815 RTPrintf("\n");
816
[60865]817 ATL::CComModule _Module; /* Required internally by ATL (constructor records instance in global variable). */
[98357]818#endif /* RT_OS_WINDOWS */
[60865]819
[48137]820#ifdef Q_WS_X11
821 if (!XInitThreads())
822 return 1;
823#endif
[4637]824#ifdef VBOXSDL_WITH_X11
[1]825 /*
[3098]826 * Lock keys on SDL behave different from normal keys: A KeyPress event is generated
[33540]827 * if the lock mode gets active and a keyRelease event is generated if the lock mode
[3098]828 * gets inactive, that is KeyPress and KeyRelease are sent when pressing the lock key
829 * to change the mode. The current lock mode is reflected in SDL_GetModState().
830 *
[51122]831 * Debian patched libSDL to make the lock keys behave like normal keys
832 * generating a KeyPress/KeyRelease event if the lock key was
833 * pressed/released. With the new behaviour, the lock status is not
834 * reflected in the mod status anymore, but the user can request the old
835 * behaviour by setting an environment variable. To confuse matters further
836 * version 1.2.14 (fortunately including the Debian packaged versions)
837 * adopted the Debian behaviour officially, but inverted the meaning of the
838 * environment variable to select the new behaviour, keeping the old as the
839 * default. We disable the new behaviour to ensure a defined environment
840 * and work around the missing KeyPress/KeyRelease events in ProcessKeys().
[3098]841 */
[51122]842 {
[98304]843#if 0
[51122]844 const SDL_version *pVersion = SDL_Linked_Version();
845 if ( SDL_VERSIONNUM(pVersion->major, pVersion->minor, pVersion->patch)
846 < SDL_VERSIONNUM(1, 2, 14))
847 RTEnvSet("SDL_DISABLE_LOCK_KEYS", "1");
[98246]848#endif
[51122]849 }
[3098]850#endif
851
[98357]852 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
853
[3098]854 /*
[1]855 * the hostkey detection mode is unrelated to VM processing, so handle it before
856 * we initialize anything COM related
857 */
[18751]858 if (argc == 2 && ( !strcmp(argv[1], "-detecthostkey")
859 || !strcmp(argv[1], "--detecthostkey")))
[1]860 {
[98357]861 rcExit = handleDetectHostKey();
862#ifdef RT_OS_WINDOWS
863 FreeConsole(); /* Detach or destroy (from) console. */
864#endif
865 return rcExit;
[1]866 }
867
[98357]868 /** @todo r=andy This function is waaaaaay to long, uses goto's and leaks stuff. Use RTGetOpt handling. */
869
[95141]870 HRESULT hrc;
[22490]871 int vrc;
[25013]872 Guid uuidVM;
[1]873 char *vmName = NULL;
[51677]874 bool fSeparate = false;
[7207]875 DeviceType_T bootDevice = DeviceType_Null;
[1]876 uint32_t memorySize = 0;
877 uint32_t vramSize = 0;
[35722]878 ComPtr<IEventListener> pVBoxClientListener;
879 ComPtr<IEventListener> pVBoxListener;
880 ComObjPtr<VBoxSDLConsoleEventListenerImpl> pConsoleListener;
[22305]881
[1]882 bool fFullscreen = false;
883 bool fResizable = true;
[967]884#ifdef USE_XPCOM_QUEUE_THREAD
[1]885 bool fXPCOMEventThreadSignaled = false;
[967]886#endif
[42446]887 const char *pcszHdaFile = NULL;
888 const char *pcszCdromFile = NULL;
889 const char *pcszFdaFile = NULL;
[40023]890 const char *pszPortVRDP = NULL;
[1]891 bool fDiscardState = false;
[42446]892 const char *pcszSettingsPw = NULL;
893 const char *pcszSettingsPwFile = NULL;
[1]894#ifdef VBOXSDL_ADVANCED_OPTIONS
[445]895 uint32_t u32WarpDrive = 0;
[1]896#endif
897#ifdef VBOX_WIN32_UI
[12449]898 bool fWin32UI = true;
[31698]899 int64_t winId = 0;
[1]900#endif
[516]901 bool fShowSDLConfig = false;
902 uint32_t fixedWidth = ~(uint32_t)0;
903 uint32_t fixedHeight = ~(uint32_t)0;
904 uint32_t fixedBPP = ~(uint32_t)0;
905 uint32_t uResizeWidth = ~(uint32_t)0;
906 uint32_t uResizeHeight = ~(uint32_t)0;
[1]907
908 /* The damned GOTOs forces this to be up here - totally out of place. */
909 /*
910 * Host key handling.
911 *
912 * The golden rule is that host-key combinations should not be seen
913 * by the guest. For instance a CAD should not have any extra RCtrl down
914 * and RCtrl up around itself. Nor should a resume be followed by a Ctrl-P
915 * that could encourage applications to start printing.
916 *
917 * We must not confuse the hostkey processing into any release sequences
918 * either, the host key is supposed to be explicitly pressing one key.
919 *
920 * Quick state diagram:
921 *
922 * host key down alone
923 * (Normal) ---------------
924 * ^ ^ |
925 * | | v host combination key down
926 * | | (Host key down) ----------------
927 * | | host key up v | |
928 * | |-------------- | other key down v host combination key down
929 * | | (host key used) -------------
930 * | | | ^ |
931 * | (not host key)-- | |---------------
932 * | | | | |
933 * | | ---- other |
934 * | modifiers = 0 v v
935 * -----------------------------------------------
936 */
937 enum HKEYSTATE
938 {
939 /** The initial and most common state, pass keystrokes to the guest.
940 * Next state: HKEYSTATE_DOWN
941 * Prev state: Any */
942 HKEYSTATE_NORMAL = 1,
943 /** The first host key was pressed down
944 */
945 HKEYSTATE_DOWN_1ST,
946 /** The second host key was pressed down (if gHostKeySym2 != SDLK_UNKNOWN)
947 */
948 HKEYSTATE_DOWN_2ND,
949 /** The host key has been pressed down.
950 * Prev state: HKEYSTATE_NORMAL
951 * Next state: HKEYSTATE_NORMAL - host key up, capture toggle.
952 * Next state: HKEYSTATE_USED - host key combination down.
953 * Next state: HKEYSTATE_NOT_IT - non-host key combination down.
954 */
955 HKEYSTATE_DOWN,
956 /** A host key combination was pressed.
957 * Prev state: HKEYSTATE_DOWN
958 * Next state: HKEYSTATE_NORMAL - when modifiers are all 0
959 */
960 HKEYSTATE_USED,
961 /** A non-host key combination was attempted. Send hostkey down to the
962 * guest and continue until all modifiers have been released.
963 * Prev state: HKEYSTATE_DOWN
964 * Next state: HKEYSTATE_NORMAL - when modifiers are all 0
965 */
966 HKEYSTATE_NOT_IT
967 } enmHKeyState = HKEYSTATE_NORMAL;
968 /** The host key down event which we have been hiding from the guest.
969 * Used when going from HKEYSTATE_DOWN to HKEYSTATE_NOT_IT. */
970 SDL_Event EvHKeyDown1;
971 SDL_Event EvHKeyDown2;
972
973 LogFlow(("SDL GUI started\n"));
[26034]974 RTPrintf(VBOX_PRODUCT " SDL GUI version %s\n"
[103402]975 "Copyright (C) 2005-" VBOX_C_YEAR " " VBOX_VENDOR "\n",
[16151]976 VBOX_VERSION_STRING);
[1]977
978 // less than one parameter is not possible
979 if (argc < 2)
980 {
981 show_usage();
982 return 1;
983 }
984
985 // command line argument parsing stuff
986 for (int curArg = 1; curArg < argc; curArg++)
987 {
[18751]988 if ( !strcmp(argv[curArg], "--vm")
989 || !strcmp(argv[curArg], "-vm")
990 || !strcmp(argv[curArg], "--startvm")
991 || !strcmp(argv[curArg], "-startvm")
992 || !strcmp(argv[curArg], "-s")
993 )
[1]994 {
995 if (++curArg >= argc)
996 {
997 RTPrintf("Error: VM not specified (UUID or name)!\n");
[26322]998 return 1;
[1]999 }
1000 // first check if a UUID was supplied
[32780]1001 uuidVM = argv[curArg];
[44039]1002
1003 if (!uuidVM.isValid())
[1]1004 {
1005 LogFlow(("invalid UUID format, assuming it's a VM name\n"));
1006 vmName = argv[curArg];
1007 }
[44039]1008 else if (uuidVM.isZero())
1009 {
1010 RTPrintf("Error: UUID argument is zero!\n");
1011 return 1;
1012 }
[1]1013 }
[51677]1014 else if ( !strcmp(argv[curArg], "--separate")
1015 || !strcmp(argv[curArg], "-separate"))
1016 {
1017 fSeparate = true;
1018 }
[18751]1019 else if ( !strcmp(argv[curArg], "--comment")
1020 || !strcmp(argv[curArg], "-comment"))
[1]1021 {
1022 if (++curArg >= argc)
1023 {
[18751]1024 RTPrintf("Error: missing argument for comment!\n");
[26322]1025 return 1;
[18751]1026 }
1027 }
1028 else if ( !strcmp(argv[curArg], "--boot")
1029 || !strcmp(argv[curArg], "-boot"))
1030 {
1031 if (++curArg >= argc)
1032 {
[1]1033 RTPrintf("Error: missing argument for boot drive!\n");
[26322]1034 return 1;
[1]1035 }
1036 switch (argv[curArg][0])
1037 {
1038 case 'a':
1039 {
[7207]1040 bootDevice = DeviceType_Floppy;
[1]1041 break;
1042 }
1043
1044 case 'c':
1045 {
[7207]1046 bootDevice = DeviceType_HardDisk;
[1]1047 break;
1048 }
1049
1050 case 'd':
1051 {
[7207]1052 bootDevice = DeviceType_DVD;
[1]1053 break;
1054 }
1055
[4486]1056 case 'n':
1057 {
[7207]1058 bootDevice = DeviceType_Network;
[4486]1059 break;
1060 }
1061
[1]1062 default:
1063 {
1064 RTPrintf("Error: wrong argument for boot drive!\n");
[26322]1065 return 1;
[1]1066 }
1067 }
1068 }
[33916]1069 else if ( !strcmp(argv[curArg], "--detecthostkey")
1070 || !strcmp(argv[curArg], "-detecthostkey"))
1071 {
1072 RTPrintf("Error: please specify \"%s\" without any additional parameters!\n",
1073 argv[curArg]);
1074 return 1;
1075 }
[18751]1076 else if ( !strcmp(argv[curArg], "--memory")
1077 || !strcmp(argv[curArg], "-memory")
1078 || !strcmp(argv[curArg], "-m"))
[1]1079 {
1080 if (++curArg >= argc)
1081 {
1082 RTPrintf("Error: missing argument for memory size!\n");
[26322]1083 return 1;
[1]1084 }
1085 memorySize = atoi(argv[curArg]);
1086 }
[18751]1087 else if ( !strcmp(argv[curArg], "--vram")
1088 || !strcmp(argv[curArg], "-vram"))
[1]1089 {
1090 if (++curArg >= argc)
1091 {
1092 RTPrintf("Error: missing argument for vram size!\n");
[26322]1093 return 1;
[1]1094 }
1095 vramSize = atoi(argv[curArg]);
1096 }
[18751]1097 else if ( !strcmp(argv[curArg], "--fullscreen")
1098 || !strcmp(argv[curArg], "-fullscreen"))
[1]1099 {
1100 fFullscreen = true;
1101 }
[18751]1102 else if ( !strcmp(argv[curArg], "--fullscreenresize")
1103 || !strcmp(argv[curArg], "-fullscreenresize"))
[10675]1104 {
1105 gfFullscreenResize = true;
[10678]1106#ifdef VBOXSDL_WITH_X11
[11419]1107 RTEnvSet("SDL_VIDEO_X11_VIDMODE", "0");
[10678]1108#endif
[10675]1109 }
[18751]1110 else if ( !strcmp(argv[curArg], "--fixedmode")
1111 || !strcmp(argv[curArg], "-fixedmode"))
[1]1112 {
1113 /* three parameters follow */
1114 if (curArg + 3 >= argc)
1115 {
1116 RTPrintf("Error: missing arguments for fixed video mode!\n");
[26322]1117 return 1;
[1]1118 }
1119 fixedWidth = atoi(argv[++curArg]);
1120 fixedHeight = atoi(argv[++curArg]);
1121 fixedBPP = atoi(argv[++curArg]);
1122 }
[18751]1123 else if ( !strcmp(argv[curArg], "--nofstoggle")
1124 || !strcmp(argv[curArg], "-nofstoggle"))
[1]1125 {
1126 gfAllowFullscreenToggle = FALSE;
1127 }
[18751]1128 else if ( !strcmp(argv[curArg], "--noresize")
1129 || !strcmp(argv[curArg], "-noresize"))
[1]1130 {
1131 fResizable = false;
1132 }
[18751]1133 else if ( !strcmp(argv[curArg], "--nohostkey")
1134 || !strcmp(argv[curArg], "-nohostkey"))
[1]1135 {
1136 gHostKeyMod = 0;
1137 gHostKeySym1 = 0;
1138 }
[18751]1139 else if ( !strcmp(argv[curArg], "--nohostkeys")
1140 || !strcmp(argv[curArg], "-nohostkeys"))
[10430]1141 {
1142 if (++curArg >= argc)
1143 {
1144 RTPrintf("Error: missing a string of disabled hostkey combinations\n");
[26322]1145 return 1;
[10430]1146 }
1147 gHostKeyDisabledCombinations = argv[curArg];
[18494]1148 size_t cch = strlen(gHostKeyDisabledCombinations);
1149 for (size_t i = 0; i < cch; i++)
[10430]1150 {
1151 if (!strchr("fhnpqrs", gHostKeyDisabledCombinations[i]))
1152 {
1153 RTPrintf("Error: <hostkey> + '%c' is not a valid combination\n",
[18494]1154 gHostKeyDisabledCombinations[i]);
[26322]1155 return 1;
[10430]1156 }
1157 }
1158 }
[18751]1159 else if ( !strcmp(argv[curArg], "--nograbonclick")
1160 || !strcmp(argv[curArg], "-nograbonclick"))
[1]1161 {
1162 gfGrabOnMouseClick = FALSE;
1163 }
[18751]1164 else if ( !strcmp(argv[curArg], "--termacpi")
1165 || !strcmp(argv[curArg], "-termacpi"))
[6437]1166 {
1167 gfACPITerm = TRUE;
1168 }
[18751]1169 else if ( !strcmp(argv[curArg], "--pidfile")
1170 || !strcmp(argv[curArg], "-pidfile"))
[10501]1171 {
1172 if (++curArg >= argc)
1173 {
[18751]1174 RTPrintf("Error: missing file name for --pidfile!\n");
[26322]1175 return 1;
[10501]1176 }
1177 gpszPidFile = argv[curArg];
1178 }
[18751]1179 else if ( !strcmp(argv[curArg], "--hda")
1180 || !strcmp(argv[curArg], "-hda"))
[1]1181 {
1182 if (++curArg >= argc)
1183 {
1184 RTPrintf("Error: missing file name for first hard disk!\n");
[26322]1185 return 1;
[1]1186 }
1187 /* resolve it. */
[2271]1188 if (RTPathExists(argv[curArg]))
[42446]1189 pcszHdaFile = RTPathRealDup(argv[curArg]);
1190 if (!pcszHdaFile)
[1]1191 {
1192 RTPrintf("Error: The path to the specified harddisk, '%s', could not be resolved.\n", argv[curArg]);
[26322]1193 return 1;
[1]1194 }
1195 }
[18751]1196 else if ( !strcmp(argv[curArg], "--fda")
1197 || !strcmp(argv[curArg], "-fda"))
[1]1198 {
1199 if (++curArg >= argc)
1200 {
1201 RTPrintf("Error: missing file/device name for first floppy disk!\n");
[26322]1202 return 1;
[1]1203 }
1204 /* resolve it. */
[2271]1205 if (RTPathExists(argv[curArg]))
[42446]1206 pcszFdaFile = RTPathRealDup(argv[curArg]);
1207 if (!pcszFdaFile)
[1]1208 {
1209 RTPrintf("Error: The path to the specified floppy disk, '%s', could not be resolved.\n", argv[curArg]);
[26322]1210 return 1;
[1]1211 }
1212 }
[18751]1213 else if ( !strcmp(argv[curArg], "--cdrom")
1214 || !strcmp(argv[curArg], "-cdrom"))
[1]1215 {
1216 if (++curArg >= argc)
1217 {
[2238]1218 RTPrintf("Error: missing file/device name for cdrom!\n");
[26322]1219 return 1;
[1]1220 }
1221 /* resolve it. */
[2271]1222 if (RTPathExists(argv[curArg]))
[42446]1223 pcszCdromFile = RTPathRealDup(argv[curArg]);
1224 if (!pcszCdromFile)
[1]1225 {
1226 RTPrintf("Error: The path to the specified cdrom, '%s', could not be resolved.\n", argv[curArg]);
[26322]1227 return 1;
[1]1228 }
1229 }
[18751]1230 else if ( !strcmp(argv[curArg], "--vrdp")
1231 || !strcmp(argv[curArg], "-vrdp"))
[1]1232 {
1233 // start with the standard VRDP port
[40023]1234 pszPortVRDP = "0";
[1]1235
1236 // is there another argument
1237 if (argc > (curArg + 1))
1238 {
[23643]1239 curArg++;
[40023]1240 pszPortVRDP = argv[curArg];
1241 LogFlow(("Using non standard VRDP port %s\n", pszPortVRDP));
[1]1242 }
1243 }
[18751]1244 else if ( !strcmp(argv[curArg], "--discardstate")
1245 || !strcmp(argv[curArg], "-discardstate"))
[1]1246 {
1247 fDiscardState = true;
1248 }
[42446]1249 else if (!strcmp(argv[curArg], "--settingspw"))
1250 {
1251 if (++curArg >= argc)
1252 {
1253 RTPrintf("Error: missing password");
1254 return 1;
1255 }
1256 pcszSettingsPw = argv[curArg];
1257 }
1258 else if (!strcmp(argv[curArg], "--settingspwfile"))
1259 {
1260 if (++curArg >= argc)
1261 {
1262 RTPrintf("Error: missing password file\n");
1263 return 1;
1264 }
1265 pcszSettingsPwFile = argv[curArg];
1266 }
[1]1267#ifdef VBOXSDL_ADVANCED_OPTIONS
[18751]1268 else if ( !strcmp(argv[curArg], "--warpdrive")
1269 || !strcmp(argv[curArg], "-warpdrive"))
[445]1270 {
1271 if (++curArg >= argc)
1272 {
[18751]1273 RTPrintf("Error: missing the rate value for the --warpdrive option!\n");
[26322]1274 return 1;
[445]1275 }
1276 u32WarpDrive = RTStrToUInt32(argv[curArg]);
1277 if (u32WarpDrive < 2 || u32WarpDrive > 20000)
1278 {
1279 RTPrintf("Error: the warp drive rate is restricted to [2..20000]. (%d)\n", u32WarpDrive);
[26322]1280 return 1;
[445]1281 }
1282 }
[1]1283#endif /* VBOXSDL_ADVANCED_OPTIONS */
1284#ifdef VBOX_WIN32_UI
[18751]1285 else if ( !strcmp(argv[curArg], "--win32ui")
1286 || !strcmp(argv[curArg], "-win32ui"))
[1]1287 fWin32UI = true;
1288#endif
[18751]1289 else if ( !strcmp(argv[curArg], "--showsdlconfig")
1290 || !strcmp(argv[curArg], "-showsdlconfig"))
[1]1291 fShowSDLConfig = true;
[18751]1292 else if ( !strcmp(argv[curArg], "--hostkey")
1293 || !strcmp(argv[curArg], "-hostkey"))
[1]1294 {
1295 if (++curArg + 1 >= argc)
1296 {
1297 RTPrintf("Error: not enough arguments for host keys!\n");
[26322]1298 return 1;
[1]1299 }
1300 gHostKeySym1 = atoi(argv[curArg++]);
1301 if (curArg + 1 < argc && (argv[curArg+1][0] == '0' || atoi(argv[curArg+1]) > 0))
1302 {
1303 /* two-key sequence as host key specified */
1304 gHostKeySym2 = atoi(argv[curArg++]);
1305 }
1306 gHostKeyMod = atoi(argv[curArg]);
1307 }
1308 /* just show the help screen */
1309 else
1310 {
[18751]1311 if ( strcmp(argv[curArg], "-h")
1312 && strcmp(argv[curArg], "-help")
[5084]1313 && strcmp(argv[curArg], "--help"))
1314 RTPrintf("Error: unrecognized switch '%s'\n", argv[curArg]);
[1]1315 show_usage();
1316 return 1;
1317 }
1318 }
[26322]1319
[95141]1320 hrc = com::Initialize();
[41100]1321#ifdef VBOX_WITH_XPCOM
[95141]1322 if (hrc == NS_ERROR_FILE_ACCESS_DENIED)
[41100]1323 {
1324 char szHome[RTPATH_MAX] = "";
1325 com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
1326 RTPrintf("Failed to initialize COM because the global settings directory '%s' is not accessible!\n", szHome);
1327 return 1;
1328 }
1329#endif
[95141]1330 if (FAILED(hrc))
[26322]1331 {
[95141]1332 RTPrintf("Error: COM initialization failed (rc=%Rhrc)!\n", hrc);
[26322]1333 return 1;
1334 }
1335
[26439]1336 /* NOTE: do not convert the following scope to a "do {} while (0);", as
1337 * this would make it all too tempting to use "break;" incorrectly - it
1338 * would skip over the cleanup. */
[26322]1339 {
1340 // scopes all the stuff till shutdown
1341 ////////////////////////////////////////////////////////////////////////////
1342
[34646]1343 ComPtr<IVirtualBoxClient> pVirtualBoxClient;
[34645]1344 ComPtr<IVirtualBox> pVirtualBox;
1345 ComPtr<ISession> pSession;
[26322]1346 bool sessionOpened = false;
[46649]1347 NativeEventQueue* eventQ = com::NativeEventQueue::getMainEventQueue();
[26322]1348
[31008]1349 ComPtr<IMachine> pMachine;
[81964]1350 ComPtr<IGraphicsAdapter> pGraphicsAdapter;
[31008]1351
[95141]1352 hrc = pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
1353 if (FAILED(hrc))
[26322]1354 {
1355 com::ErrorInfo info;
1356 if (info.isFullAvailable())
[34646]1357 PrintError("Failed to create VirtualBoxClient object",
[26322]1358 info.getText().raw(), info.getComponent().raw());
1359 else
[95141]1360 RTPrintf("Failed to create VirtualBoxClient object! No error information available (rc=%Rhrc).\n", hrc);
[26439]1361 goto leave;
[26322]1362 }
[34646]1363
[95141]1364 hrc = pVirtualBoxClient->COMGETTER(VirtualBox)(pVirtualBox.asOutParam());
1365 if (FAILED(hrc))
[26322]1366 {
[95141]1367 RTPrintf("Failed to get VirtualBox object (rc=%Rhrc)!\n", hrc);
[26439]1368 goto leave;
[26322]1369 }
[95141]1370 hrc = pVirtualBoxClient->COMGETTER(Session)(pSession.asOutParam());
1371 if (FAILED(hrc))
[34646]1372 {
[95141]1373 RTPrintf("Failed to get session object (rc=%Rhrc)!\n", hrc);
[34646]1374 goto leave;
1375 }
[1]1376
[42446]1377 if (pcszSettingsPw)
1378 {
1379 CHECK_ERROR(pVirtualBox, SetSettingsSecret(Bstr(pcszSettingsPw).raw()));
[95141]1380 if (FAILED(hrc))
[42446]1381 goto leave;
1382 }
1383 else if (pcszSettingsPwFile)
1384 {
[98357]1385 rcExit = settingsPasswordFile(pVirtualBox, pcszSettingsPwFile);
[42446]1386 if (rcExit != RTEXITCODE_SUCCESS)
1387 goto leave;
1388 }
1389
[1]1390 /*
[35666]1391 * Do we have a UUID?
[1]1392 */
[44039]1393 if (uuidVM.isValid())
[1]1394 {
[95141]1395 hrc = pVirtualBox->FindMachine(uuidVM.toUtf16().raw(), pMachine.asOutParam());
1396 if (FAILED(hrc) || !pMachine)
[35666]1397 {
1398 RTPrintf("Error: machine with the given ID not found!\n");
1399 goto leave;
1400 }
1401 }
1402 else if (vmName)
1403 {
1404 /*
1405 * Do we have a name but no UUID?
1406 */
[95141]1407 hrc = pVirtualBox->FindMachine(Bstr(vmName).raw(), pMachine.asOutParam());
1408 if ((hrc == S_OK) && pMachine)
[1]1409 {
[34645]1410 Bstr bstrId;
1411 pMachine->COMGETTER(Id)(bstrId.asOutParam());
1412 uuidVM = Guid(bstrId);
[1]1413 }
1414 else
1415 {
[44970]1416 RTPrintf("Error: machine with the given name not found!\n");
1417 RTPrintf("Check if this VM has been corrupted and is now inaccessible.");
[1]1418 goto leave;
1419 }
1420 }
1421
[3289]1422 /* create SDL event semaphore */
[22490]1423 vrc = RTSemEventCreate(&g_EventSemSDLEvents);
1424 AssertReleaseRC(vrc);
[3289]1425
[95141]1426 hrc = pVirtualBoxClient->CheckMachineError(pMachine);
1427 if (FAILED(hrc))
[44970]1428 {
1429 com::ErrorInfo info;
1430 if (info.isFullAvailable())
1431 PrintError("The VM has errors",
1432 info.getText().raw(), info.getComponent().raw());
1433 else
[95141]1434 RTPrintf("Failed to check for VM errors! No error information available (rc=%Rhrc).\n", hrc);
[44970]1435 goto leave;
1436 }
1437
[51677]1438 if (fSeparate)
1439 {
[53624]1440 MachineState_T machineState = MachineState_Null;
[51677]1441 pMachine->COMGETTER(State)(&machineState);
1442 if ( machineState == MachineState_Running
1443 || machineState == MachineState_Teleporting
1444 || machineState == MachineState_LiveSnapshotting
1445 || machineState == MachineState_Paused
1446 || machineState == MachineState_TeleportingPausedVM
1447 )
1448 {
1449 RTPrintf("VM is already running.\n");
1450 }
1451 else
1452 {
1453 ComPtr<IProgress> progress;
[95141]1454 hrc = pMachine->LaunchVMProcess(pSession, Bstr("headless").raw(), ComSafeArrayNullInParam(), progress.asOutParam());
1455 if (SUCCEEDED(hrc) && !progress.isNull())
[51677]1456 {
1457 RTPrintf("Waiting for VM to power on...\n");
[95141]1458 hrc = progress->WaitForCompletion(-1);
1459 if (SUCCEEDED(hrc))
[51677]1460 {
1461 BOOL completed = true;
[95141]1462 hrc = progress->COMGETTER(Completed)(&completed);
1463 if (SUCCEEDED(hrc))
[51677]1464 {
1465 LONG iRc;
[95141]1466 hrc = progress->COMGETTER(ResultCode)(&iRc);
1467 if (SUCCEEDED(hrc))
[51677]1468 {
1469 if (FAILED(iRc))
1470 {
1471 ProgressErrorInfo info(progress);
1472 com::GluePrintErrorInfo(info);
1473 }
1474 else
1475 {
1476 RTPrintf("VM has been successfully started.\n");
1477 /* LaunchVMProcess obtains a shared lock on the machine.
1478 * Unlock it here, because the lock will be obtained below
1479 * in the common code path as for already running VM.
1480 */
1481 pSession->UnlockMachine();
1482 }
1483 }
1484 }
1485 }
1486 }
1487 }
[95141]1488 if (FAILED(hrc))
[51677]1489 {
1490 RTPrintf("Error: failed to power up VM! No error text available.\n");
1491 goto leave;
1492 }
1493
[95141]1494 hrc = pMachine->LockMachine(pSession, LockType_Shared);
[51677]1495 }
1496 else
1497 {
[55800]1498 pSession->COMSETTER(Name)(Bstr("GUI/SDL").raw());
[95141]1499 hrc = pMachine->LockMachine(pSession, LockType_VM);
[51677]1500 }
1501
[95141]1502 if (FAILED(hrc))
[1]1503 {
1504 com::ErrorInfo info;
1505 if (info.isFullAvailable())
[26068]1506 PrintError("Could not open VirtualBox session",
[34645]1507 info.getText().raw(), info.getComponent().raw());
[1]1508 goto leave;
1509 }
[34645]1510 if (!pSession)
[1]1511 {
[26068]1512 RTPrintf("Could not open VirtualBox session!\n");
[1]1513 goto leave;
1514 }
1515 sessionOpened = true;
[31008]1516 // get the mutable VM we're dealing with
[34645]1517 pSession->COMGETTER(Machine)(gpMachine.asOutParam());
1518 if (!gpMachine)
[1]1519 {
1520 com::ErrorInfo info;
1521 if (info.isFullAvailable())
1522 PrintError("Cannot start VM!",
1523 info.getText().raw(), info.getComponent().raw());
1524 else
1525 RTPrintf("Error: given machine not found!\n");
1526 goto leave;
1527 }
[51677]1528
[1]1529 // get the VM console
[34645]1530 pSession->COMGETTER(Console)(gpConsole.asOutParam());
1531 if (!gpConsole)
[1]1532 {
1533 RTPrintf("Given console not found!\n");
1534 goto leave;
1535 }
1536
1537 /*
1538 * Are we supposed to use a different hard disk file?
1539 */
[42446]1540 if (pcszHdaFile)
[1]1541 {
[34645]1542 ComPtr<IMedium> pMedium;
1543
[1]1544 /*
[34645]1545 * Strategy: if any registered hard disk points to the same file,
1546 * assign it. If not, register a new image and assign it to the VM.
[1]1547 */
[42446]1548 Bstr bstrHdaFile(pcszHdaFile);
[41120]1549 pVirtualBox->OpenMedium(bstrHdaFile.raw(), DeviceType_HardDisk,
1550 AccessMode_ReadWrite, FALSE /* fForceNewUuid */,
[34645]1551 pMedium.asOutParam());
1552 if (!pMedium)
[1]1553 {
1554 /* we've not found the image */
[42446]1555 RTPrintf("Adding hard disk '%s'...\n", pcszHdaFile);
[34645]1556 pVirtualBox->OpenMedium(bstrHdaFile.raw(), DeviceType_HardDisk,
[37525]1557 AccessMode_ReadWrite, FALSE /* fForceNewUuid */,
1558 pMedium.asOutParam());
[1]1559 }
1560 /* do we have the right image now? */
[34645]1561 if (pMedium)
[1]1562 {
[34645]1563 Bstr bstrSCName;
[23825]1564
1565 /* get the first IDE controller to attach the harddisk to
[34645]1566 * and if there is none, add one temporarily */
[23825]1567 {
[34645]1568 ComPtr<IStorageController> pStorageCtl;
[23825]1569 com::SafeIfaceArray<IStorageController> aStorageControllers;
[34645]1570 CHECK_ERROR(gpMachine, COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(aStorageControllers)));
[23825]1571 for (size_t i = 0; i < aStorageControllers.size(); ++ i)
1572 {
1573 StorageBus_T storageBus = StorageBus_Null;
1574
[28205]1575 CHECK_ERROR(aStorageControllers[i], COMGETTER(Bus)(&storageBus));
[23825]1576 if (storageBus == StorageBus_IDE)
1577 {
[34645]1578 pStorageCtl = aStorageControllers[i];
[23825]1579 break;
1580 }
1581 }
1582
[34645]1583 if (pStorageCtl)
[23825]1584 {
[34645]1585 CHECK_ERROR(pStorageCtl, COMGETTER(Name)(bstrSCName.asOutParam()));
1586 gpMachine->DetachDevice(bstrSCName.raw(), 0, 0);
[23825]1587 }
1588 else
1589 {
[34645]1590 bstrSCName = "IDE Controller";
1591 CHECK_ERROR(gpMachine, AddStorageController(bstrSCName.raw(),
1592 StorageBus_IDE,
1593 pStorageCtl.asOutParam()));
[23825]1594 }
1595 }
1596
[34645]1597 CHECK_ERROR(gpMachine, AttachDevice(bstrSCName.raw(), 0, 0,
1598 DeviceType_HardDisk, pMedium));
[1]1599 /// @todo why is this attachment saved?
1600 }
1601 else
1602 {
1603 RTPrintf("Error: failed to mount the specified hard disk image!\n");
1604 goto leave;
1605 }
1606 }
1607
[2980]1608 /*
[2238]1609 * Mount a floppy if requested.
1610 */
[42446]1611 if (pcszFdaFile)
[2238]1612 do
[1]1613 {
[34645]1614 ComPtr<IMedium> pMedium;
[1]1615
[23223]1616 /* unmount? */
[42446]1617 if (!strcmp(pcszFdaFile, "none"))
[1]1618 {
[23223]1619 /* nothing to do, NULL object will cause unmount */
[1]1620 }
[23223]1621 else
1622 {
[42446]1623 Bstr bstrFdaFile(pcszFdaFile);
[2238]1624
[23223]1625 /* Assume it's a host drive name */
[34645]1626 ComPtr<IHost> pHost;
1627 CHECK_ERROR_BREAK(pVirtualBox, COMGETTER(Host)(pHost.asOutParam()));
[95141]1628 hrc = pHost->FindHostFloppyDrive(bstrFdaFile.raw(),
[34645]1629 pMedium.asOutParam());
[95141]1630 if (FAILED(hrc))
[2238]1631 {
[23223]1632 /* try to find an existing one */
[95141]1633 hrc = pVirtualBox->OpenMedium(bstrFdaFile.raw(),
[41120]1634 DeviceType_Floppy,
1635 AccessMode_ReadWrite,
1636 FALSE /* fForceNewUuid */,
[34645]1637 pMedium.asOutParam());
[95141]1638 if (FAILED(hrc))
[23223]1639 {
1640 /* try to add to the list */
[42446]1641 RTPrintf("Adding floppy image '%s'...\n", pcszFdaFile);
[34645]1642 CHECK_ERROR_BREAK(pVirtualBox,
1643 OpenMedium(bstrFdaFile.raw(),
[32718]1644 DeviceType_Floppy,
1645 AccessMode_ReadWrite,
[37525]1646 FALSE /* fForceNewUuid */,
[34645]1647 pMedium.asOutParam()));
[23223]1648 }
[2238]1649 }
[1]1650 }
[23825]1651
[34645]1652 Bstr bstrSCName;
[23825]1653
1654 /* get the first floppy controller to attach the floppy to
[34645]1655 * and if there is none, add one temporarily */
[23825]1656 {
[34645]1657 ComPtr<IStorageController> pStorageCtl;
[23825]1658 com::SafeIfaceArray<IStorageController> aStorageControllers;
[34645]1659 CHECK_ERROR(gpMachine, COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(aStorageControllers)));
[23825]1660 for (size_t i = 0; i < aStorageControllers.size(); ++ i)
1661 {
1662 StorageBus_T storageBus = StorageBus_Null;
1663
[28205]1664 CHECK_ERROR(aStorageControllers[i], COMGETTER(Bus)(&storageBus));
[23825]1665 if (storageBus == StorageBus_Floppy)
1666 {
[34645]1667 pStorageCtl = aStorageControllers[i];
[23825]1668 break;
1669 }
1670 }
1671
[34645]1672 if (pStorageCtl)
[23825]1673 {
[34645]1674 CHECK_ERROR(pStorageCtl, COMGETTER(Name)(bstrSCName.asOutParam()));
1675 gpMachine->DetachDevice(bstrSCName.raw(), 0, 0);
[23825]1676 }
1677 else
1678 {
[34645]1679 bstrSCName = "Floppy Controller";
1680 CHECK_ERROR(gpMachine, AddStorageController(bstrSCName.raw(),
1681 StorageBus_Floppy,
1682 pStorageCtl.asOutParam()));
[23825]1683 }
1684 }
1685
[34645]1686 CHECK_ERROR(gpMachine, AttachDevice(bstrSCName.raw(), 0, 0,
1687 DeviceType_Floppy, pMedium));
[1]1688 }
[2238]1689 while (0);
[95141]1690 if (FAILED(hrc))
[2238]1691 goto leave;
[1]1692
[2980]1693 /*
[2238]1694 * Mount a CD-ROM if requested.
[1]1695 */
[42446]1696 if (pcszCdromFile)
[2238]1697 do
[1]1698 {
[34645]1699 ComPtr<IMedium> pMedium;
[1]1700
[23223]1701 /* unmount? */
[42446]1702 if (!strcmp(pcszCdromFile, "none"))
[1]1703 {
[23223]1704 /* nothing to do, NULL object will cause unmount */
[1]1705 }
[23223]1706 else
1707 {
[42446]1708 Bstr bstrCdromFile(pcszCdromFile);
[2238]1709
[23223]1710 /* Assume it's a host drive name */
[34645]1711 ComPtr<IHost> pHost;
1712 CHECK_ERROR_BREAK(pVirtualBox, COMGETTER(Host)(pHost.asOutParam()));
[95141]1713 hrc = pHost->FindHostDVDDrive(bstrCdromFile.raw(), pMedium.asOutParam());
1714 if (FAILED(hrc))
[1]1715 {
[23223]1716 /* try to find an existing one */
[95141]1717 hrc = pVirtualBox->OpenMedium(bstrCdromFile.raw(),
[41120]1718 DeviceType_DVD,
1719 AccessMode_ReadWrite,
1720 FALSE /* fForceNewUuid */,
1721 pMedium.asOutParam());
[95141]1722 if (FAILED(hrc))
[23223]1723 {
1724 /* try to add to the list */
[42446]1725 RTPrintf("Adding ISO image '%s'...\n", pcszCdromFile);
[34645]1726 CHECK_ERROR_BREAK(pVirtualBox,
1727 OpenMedium(bstrCdromFile.raw(),
1728 DeviceType_DVD,
1729 AccessMode_ReadWrite,
[37525]1730 FALSE /* fForceNewUuid */,
[34645]1731 pMedium.asOutParam()));
[23223]1732 }
[1]1733 }
[2238]1734 }
[23825]1735
[34645]1736 Bstr bstrSCName;
[23825]1737
[34645]1738 /* get the first IDE controller to attach the DVD drive to
1739 * and if there is none, add one temporarily */
[23825]1740 {
[34645]1741 ComPtr<IStorageController> pStorageCtl;
[23825]1742 com::SafeIfaceArray<IStorageController> aStorageControllers;
[34645]1743 CHECK_ERROR(gpMachine, COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(aStorageControllers)));
[23825]1744 for (size_t i = 0; i < aStorageControllers.size(); ++ i)
1745 {
1746 StorageBus_T storageBus = StorageBus_Null;
1747
[28205]1748 CHECK_ERROR(aStorageControllers[i], COMGETTER(Bus)(&storageBus));
[23825]1749 if (storageBus == StorageBus_IDE)
1750 {
[34645]1751 pStorageCtl = aStorageControllers[i];
[23825]1752 break;
1753 }
1754 }
1755
[34645]1756 if (pStorageCtl)
[23825]1757 {
[34645]1758 CHECK_ERROR(pStorageCtl, COMGETTER(Name)(bstrSCName.asOutParam()));
1759 gpMachine->DetachDevice(bstrSCName.raw(), 1, 0);
[23825]1760 }
1761 else
1762 {
[34645]1763 bstrSCName = "IDE Controller";
1764 CHECK_ERROR(gpMachine, AddStorageController(bstrSCName.raw(),
1765 StorageBus_IDE,
1766 pStorageCtl.asOutParam()));
[23825]1767 }
1768 }
1769
[34645]1770 CHECK_ERROR(gpMachine, AttachDevice(bstrSCName.raw(), 1, 0,
1771 DeviceType_DVD, pMedium));
[1]1772 }
[2238]1773 while (0);
[95141]1774 if (FAILED(hrc))
[2238]1775 goto leave;
[1]1776
1777 if (fDiscardState)
1778 {
1779 /*
1780 * If the machine is currently saved,
1781 * discard the saved state first.
1782 */
1783 MachineState_T machineState;
[34645]1784 gpMachine->COMGETTER(State)(&machineState);
[91363]1785 if (machineState == MachineState_Saved || machineState == MachineState_AbortedSaved)
[1]1786 {
[55214]1787 CHECK_ERROR(gpMachine, DiscardSavedState(true /* fDeleteFile */));
[1]1788 }
1789 /*
1790 * If there are snapshots, discard the current state,
1791 * i.e. revert to the last snapshot.
1792 */
1793 ULONG cSnapshots;
[34645]1794 gpMachine->COMGETTER(SnapshotCount)(&cSnapshots);
[1]1795 if (cSnapshots)
1796 {
[34645]1797 gpProgress = NULL;
[23879]1798
1799 ComPtr<ISnapshot> pCurrentSnapshot;
[34645]1800 CHECK_ERROR(gpMachine, COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam()));
[95141]1801 if (FAILED(hrc))
[26439]1802 goto leave;
[23879]1803
[55214]1804 CHECK_ERROR(gpMachine, RestoreSnapshot(pCurrentSnapshot, gpProgress.asOutParam()));
[95141]1805 hrc = gpProgress->WaitForCompletion(-1);
[1]1806 }
1807 }
1808
1809 // get the machine debugger (does not have to be there)
[34645]1810 gpConsole->COMGETTER(Debugger)(gpMachineDebugger.asOutParam());
1811 if (gpMachineDebugger)
[1]1812 {
1813 Log(("Machine debugger available!\n"));
1814 }
[34645]1815 gpConsole->COMGETTER(Display)(gpDisplay.asOutParam());
1816 if (!gpDisplay)
[1]1817 {
1818 RTPrintf("Error: could not get display object!\n");
1819 goto leave;
1820 }
1821
1822 // set the boot drive
[7207]1823 if (bootDevice != DeviceType_Null)
[1]1824 {
[95141]1825 hrc = gpMachine->SetBootOrder(1, bootDevice);
1826 if (hrc != S_OK)
[1]1827 {
1828 RTPrintf("Error: could not set boot device, using default.\n");
1829 }
1830 }
1831
1832 // set the memory size if not default
1833 if (memorySize)
1834 {
[95141]1835 hrc = gpMachine->COMSETTER(MemorySize)(memorySize);
1836 if (hrc != S_OK)
[1]1837 {
1838 ULONG ramSize = 0;
[34645]1839 gpMachine->COMGETTER(MemorySize)(&ramSize);
[1]1840 RTPrintf("Error: could not set memory size, using current setting of %d MBytes\n", ramSize);
1841 }
1842 }
1843
[95141]1844 hrc = gpMachine->COMGETTER(GraphicsAdapter)(pGraphicsAdapter.asOutParam());
1845 if (hrc != S_OK)
[81964]1846 {
1847 RTPrintf("Error: could not get graphics adapter object\n");
1848 goto leave;
1849 }
1850
[1]1851 if (vramSize)
1852 {
[95141]1853 hrc = pGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
1854 if (hrc != S_OK)
[1]1855 {
[81964]1856 pGraphicsAdapter->COMGETTER(VRAMSize)((ULONG*)&vramSize);
[1]1857 RTPrintf("Error: could not set VRAM size, using current setting of %d MBytes\n", vramSize);
1858 }
1859 }
1860
1861 // we're always able to process absolute mouse events and we prefer that
1862 gfAbsoluteMouseHost = TRUE;
1863
1864#ifdef VBOX_WIN32_UI
1865 if (fWin32UI)
1866 {
1867 /* initialize the Win32 user interface inside which SDL will be embedded */
[12449]1868 if (initUI(fResizable, winId))
[1]1869 return 1;
1870 }
1871#endif
1872
[20433]1873 /* static initialization of the SDL stuff */
[41216]1874 if (!VBoxSDLFB::init(fShowSDLConfig))
[23170]1875 goto leave;
[1]1876
[81964]1877 pGraphicsAdapter->COMGETTER(MonitorCount)(&gcMonitors);
[20433]1878 if (gcMonitors > 64)
1879 gcMonitors = 64;
1880
1881 for (unsigned i = 0; i < gcMonitors; i++)
[1]1882 {
[20433]1883 // create our SDL framebuffer instance
[51677]1884 gpFramebuffer[i].createObject();
[95141]1885 hrc = gpFramebuffer[i]->init(i, fFullscreen, fResizable, fShowSDLConfig, false,
[51677]1886 fixedWidth, fixedHeight, fixedBPP, fSeparate);
[95141]1887 if (FAILED(hrc))
[20433]1888 {
1889 RTPrintf("Error: could not create framebuffer object!\n");
1890 goto leave;
1891 }
[1]1892 }
[12449]1893
1894#ifdef VBOX_WIN32_UI
[20433]1895 gpFramebuffer[0]->setWinId(winId);
[12449]1896#endif
1897
[20433]1898 for (unsigned i = 0; i < gcMonitors; i++)
1899 {
1900 if (!gpFramebuffer[i]->initialized())
1901 goto leave;
1902 gpFramebuffer[i]->AddRef();
1903 if (fFullscreen)
1904 SetFullscreen(true);
1905 }
[10501]1906
1907#ifdef VBOXSDL_WITH_X11
1908 /* NOTE1: We still want Ctrl-C to work, so we undo the SDL redirections.
1909 * NOTE2: We have to remove the PidFile if this file exists. */
1910 signal(SIGINT, signal_handler_SIGINT);
1911 signal(SIGQUIT, signal_handler_SIGINT);
1912 signal(SIGSEGV, signal_handler_SIGINT);
1913#endif
1914
[22305]1915
[20435]1916 for (ULONG i = 0; i < gcMonitors; i++)
[1]1917 {
[20433]1918 // register our framebuffer
[95141]1919 hrc = gpDisplay->AttachFramebuffer(i, gpFramebuffer[i], gaFramebufferId[i].asOutParam());
1920 if (FAILED(hrc))
[20433]1921 {
1922 RTPrintf("Error: could not register framebuffer object!\n");
1923 goto leave;
1924 }
[51677]1925 ULONG dummy;
[20435]1926 LONG xOrigin, yOrigin;
[52978]1927 GuestMonitorStatus_T monitorStatus;
[95141]1928 hrc = gpDisplay->GetScreenResolution(i, &dummy, &dummy, &dummy, &xOrigin, &yOrigin, &monitorStatus);
[20433]1929 gpFramebuffer[i]->setOrigin(xOrigin, yOrigin);
[1]1930 }
1931
[30618]1932 {
[34646]1933 // register listener for VirtualBoxClient events
1934 ComPtr<IEventSource> pES;
1935 CHECK_ERROR(pVirtualBoxClient, COMGETTER(EventSource)(pES.asOutParam()));
[35722]1936 ComObjPtr<VBoxSDLClientEventListenerImpl> listener;
1937 listener.createObject();
1938 listener->init(new VBoxSDLClientEventListener());
1939 pVBoxClientListener = listener;
[34646]1940 com::SafeArray<VBoxEventType_T> eventTypes;
[35172]1941 eventTypes.push_back(VBoxEventType_OnVBoxSVCAvailabilityChanged);
[34646]1942 CHECK_ERROR(pES, RegisterListener(pVBoxClientListener, ComSafeArrayAsInParam(eventTypes), true));
1943 }
1944
1945 {
[34645]1946 // register listener for VirtualBox (server) events
1947 ComPtr<IEventSource> pES;
1948 CHECK_ERROR(pVirtualBox, COMGETTER(EventSource)(pES.asOutParam()));
[35722]1949 ComObjPtr<VBoxSDLEventListenerImpl> listener;
1950 listener.createObject();
1951 listener->init(new VBoxSDLEventListener());
1952 pVBoxListener = listener;
[34645]1953 com::SafeArray<VBoxEventType_T> eventTypes;
[30871]1954 eventTypes.push_back(VBoxEventType_OnExtraDataChanged);
[34645]1955 CHECK_ERROR(pES, RegisterListener(pVBoxListener, ComSafeArrayAsInParam(eventTypes), true));
[30618]1956 }
[1]1957
[30618]1958 {
[34645]1959 // register listener for Console events
1960 ComPtr<IEventSource> pES;
1961 CHECK_ERROR(gpConsole, COMGETTER(EventSource)(pES.asOutParam()));
[35722]1962 pConsoleListener.createObject();
1963 pConsoleListener->init(new VBoxSDLConsoleEventListener());
[34645]1964 com::SafeArray<VBoxEventType_T> eventTypes;
[30871]1965 eventTypes.push_back(VBoxEventType_OnMousePointerShapeChanged);
1966 eventTypes.push_back(VBoxEventType_OnMouseCapabilityChanged);
1967 eventTypes.push_back(VBoxEventType_OnKeyboardLedsChanged);
1968 eventTypes.push_back(VBoxEventType_OnStateChanged);
[30618]1969 eventTypes.push_back(VBoxEventType_OnRuntimeError);
1970 eventTypes.push_back(VBoxEventType_OnCanShowWindow);
1971 eventTypes.push_back(VBoxEventType_OnShowWindow);
[34645]1972 CHECK_ERROR(pES, RegisterListener(pConsoleListener, ComSafeArrayAsInParam(eventTypes), true));
[30618]1973 // until we've tried to to start the VM, ignore power off events
[34645]1974 pConsoleListener->getWrapped()->ignorePowerOffEvents(true);
[30618]1975 }
[1]1976
[40023]1977 if (pszPortVRDP)
[1]1978 {
[95141]1979 hrc = gpMachine->COMGETTER(VRDEServer)(gpVRDEServer.asOutParam());
1980 AssertMsg((hrc == S_OK) && gpVRDEServer, ("Could not get VRDP Server! rc = 0x%x\n", hrc));
[34645]1981 if (gpVRDEServer)
[1]1982 {
1983 // has a non standard VRDP port been requested?
[40023]1984 if (strcmp(pszPortVRDP, "0"))
[1]1985 {
[95141]1986 hrc = gpVRDEServer->SetVRDEProperty(Bstr("TCP/Ports").raw(), Bstr(pszPortVRDP).raw());
1987 if (hrc != S_OK)
[1]1988 {
[95141]1989 RTPrintf("Error: could not set VRDP port! rc = 0x%x\n", hrc);
[1]1990 goto leave;
1991 }
1992 }
1993 // now enable VRDP
[95141]1994 hrc = gpVRDEServer->COMSETTER(Enabled)(TRUE);
1995 if (hrc != S_OK)
[1]1996 {
[95141]1997 RTPrintf("Error: could not enable VRDP server! rc = 0x%x\n", hrc);
[1]1998 goto leave;
1999 }
2000 }
2001 }
2002
[95141]2003 hrc = E_FAIL;
[1]2004#ifdef VBOXSDL_ADVANCED_OPTIONS
[445]2005 if (u32WarpDrive != 0)
2006 {
[34645]2007 if (!gpMachineDebugger)
[445]2008 {
[18751]2009 RTPrintf("Error: No debugger object; --warpdrive %d cannot be executed!\n", u32WarpDrive);
[445]2010 goto leave;
2011 }
[34645]2012 gpMachineDebugger->COMSETTER(VirtualTimeRate)(u32WarpDrive);
[445]2013 }
[1]2014#endif /* VBOXSDL_ADVANCED_OPTIONS */
2015
2016 /* start with something in the titlebar */
2017 UpdateTitlebar(TITLEBAR_NORMAL);
2018
2019 /* memorize the default cursor */
2020 gpDefaultCursor = SDL_GetCursor();
2021 /*
2022 * Register our user signal handler.
2023 */
[4637]2024#ifdef VBOXSDL_WITH_X11
[1]2025 struct sigaction sa;
[10501]2026 sa.sa_sigaction = signal_handler_SIGUSR1;
[28205]2027 sigemptyset(&sa.sa_mask);
[1]2028 sa.sa_flags = SA_RESTART | SA_SIGINFO;
[28205]2029 sigaction(SIGUSR1, &sa, NULL);
[4637]2030#endif /* VBOXSDL_WITH_X11 */
[1]2031
2032 /*
2033 * Start the VM execution thread. This has to be done
2034 * asynchronously as powering up can take some time
2035 * (accessing devices such as the host DVD drive). In
2036 * the meantime, we have to service the SDL event loop.
2037 */
2038 SDL_Event event;
2039
[51677]2040 if (!fSeparate)
[1]2041 {
[51677]2042 LogFlow(("Powering up the VM...\n"));
[95141]2043 hrc = gpConsole->PowerUp(gpProgress.asOutParam());
2044 if (hrc != S_OK)
[51677]2045 {
2046 com::ErrorInfo info(gpConsole, COM_IIDOF(IConsole));
2047 if (info.isBasicAvailable())
2048 PrintError("Failed to power up VM", info.getText().raw());
2049 else
2050 RTPrintf("Error: failed to power up VM! No error text available.\n");
2051 goto leave;
2052 }
[1]2053 }
2054
[967]2055#ifdef USE_XPCOM_QUEUE_THREAD
[1]2056 /*
2057 * Before we starting to do stuff, we have to launch the XPCOM
2058 * event queue thread. It will wait for events and send messages
2059 * to the SDL thread. After having done this, we should fairly
2060 * quickly start to process the SDL event queue as an XPCOM
2061 * event storm might arrive. Stupid SDL has a ridiculously small
2062 * event queue buffer!
2063 */
[22911]2064 startXPCOMEventQueueThread(eventQ->getSelectFD());
[967]2065#endif /* USE_XPCOM_QUEUE_THREAD */
[1]2066
2067 /* termination flag */
2068 bool fTerminateDuringStartup;
2069 fTerminateDuringStartup = false;
2070
[3176]2071 LogRel(("VBoxSDL: NUM lock initially %s, CAPS lock initially %s\n",
[34645]2072 !!(SDL_GetModState() & KMOD_NUM) ? "ON" : "OFF",
2073 !!(SDL_GetModState() & KMOD_CAPS) ? "ON" : "OFF"));
[3176]2074
[1]2075 /* start regular timer so we don't starve in the event loop */
2076 SDL_TimerID sdlTimer;
2077 sdlTimer = SDL_AddTimer(100, StartupTimer, NULL);
2078
2079 /* loop until the powerup processing is done */
2080 MachineState_T machineState;
2081 do
2082 {
[95141]2083 hrc = gpMachine->COMGETTER(State)(&machineState);
2084 if ( hrc == S_OK
[1]2085 && ( machineState == MachineState_Starting
[23703]2086 || machineState == MachineState_Restoring
[24301]2087 || machineState == MachineState_TeleportingIn
2088 )
[23703]2089 )
[1]2090 {
2091 /*
2092 * wait for the next event. This is uncritical as
2093 * power up guarantees to change the machine state
2094 * to either running or aborted and a machine state
2095 * change will send us an event. However, we have to
2096 * service the XPCOM event queue!
2097 */
[967]2098#ifdef USE_XPCOM_QUEUE_THREAD
[1]2099 if (!fXPCOMEventThreadSignaled)
2100 {
2101 signalXPCOMEventQueueThread();
2102 fXPCOMEventThreadSignaled = true;
2103 }
2104#endif
2105 /*
2106 * Wait for SDL events.
2107 */
[3289]2108 if (WaitSDLEvent(&event))
[1]2109 {
2110 switch (event.type)
2111 {
2112 /*
2113 * Timer event. Used to have the titlebar updated.
2114 */
2115 case SDL_USER_EVENT_TIMER:
2116 {
2117 /*
2118 * Update the title bar.
2119 */
2120 UpdateTitlebar(TITLEBAR_STARTUP);
2121 break;
2122 }
2123
2124 /*
[51547]2125 * User specific framebuffer change event.
[1]2126 */
[51547]2127 case SDL_USER_EVENT_NOTIFYCHANGE:
[1]2128 {
[51547]2129 LogFlow(("SDL_USER_EVENT_NOTIFYCHANGE\n"));
[20436]2130 LONG xOrigin, yOrigin;
[51547]2131 gpFramebuffer[event.user.code]->notifyChange(event.user.code);
[20433]2132 /* update xOrigin, yOrigin -> mouse */
[51677]2133 ULONG dummy;
[52978]2134 GuestMonitorStatus_T monitorStatus;
[95141]2135 hrc = gpDisplay->GetScreenResolution(event.user.code, &dummy, &dummy, &dummy, &xOrigin, &yOrigin, &monitorStatus);
[20433]2136 gpFramebuffer[event.user.code]->setOrigin(xOrigin, yOrigin);
[1]2137 break;
2138 }
2139
[967]2140#ifdef USE_XPCOM_QUEUE_THREAD
[1]2141 /*
2142 * User specific XPCOM event queue event
2143 */
2144 case SDL_USER_EVENT_XPCOM_EVENTQUEUE:
2145 {
[86650]2146 LogFlow(("SDL_USER_EVENT_XPCOM_EVENTQUEUE: processing XPCOM event queue...\n"));
[22911]2147 eventQ->processEventQueue(0);
[1]2148 signalXPCOMEventQueueThread();
2149 break;
2150 }
[967]2151#endif /* USE_XPCOM_QUEUE_THREAD */
[1]2152
2153 /*
2154 * Termination event from the on state change callback.
2155 */
2156 case SDL_USER_EVENT_TERMINATE:
2157 {
2158 if (event.user.code != VBOXSDL_TERM_NORMAL)
2159 {
[34645]2160 com::ProgressErrorInfo info(gpProgress);
[1]2161 if (info.isBasicAvailable())
2162 PrintError("Failed to power up VM", info.getText().raw());
2163 else
2164 RTPrintf("Error: failed to power up VM! No error text available.\n");
2165 }
2166 fTerminateDuringStartup = true;
2167 break;
2168 }
2169
2170 default:
2171 {
[55988]2172 Log8(("VBoxSDL: Unknown SDL event %d (pre)\n", event.type));
[1]2173 break;
2174 }
2175 }
2176
2177 }
2178 }
[22911]2179 eventQ->processEventQueue(0);
[95141]2180 } while ( hrc == S_OK
[1]2181 && ( machineState == MachineState_Starting
[23703]2182 || machineState == MachineState_Restoring
[24301]2183 || machineState == MachineState_TeleportingIn
2184 )
[23703]2185 );
[1]2186
2187 /* kill the timer again */
2188 SDL_RemoveTimer(sdlTimer);
2189 sdlTimer = 0;
2190
2191 /* are we supposed to terminate the process? */
2192 if (fTerminateDuringStartup)
2193 goto leave;
2194
2195 /* did the power up succeed? */
2196 if (machineState != MachineState_Running)
2197 {
[34645]2198 com::ProgressErrorInfo info(gpProgress);
[1]2199 if (info.isBasicAvailable())
2200 PrintError("Failed to power up VM", info.getText().raw());
2201 else
[95141]2202 RTPrintf("Error: failed to power up VM! No error text available (rc = 0x%x state = %d)\n", hrc, machineState);
[1]2203 goto leave;
2204 }
2205
2206 // accept power off events from now on because we're running
2207 // note that there's a possible race condition here...
[34645]2208 pConsoleListener->getWrapped()->ignorePowerOffEvents(false);
[1]2209
[95141]2210 hrc = gpConsole->COMGETTER(Keyboard)(gpKeyboard.asOutParam());
[34645]2211 if (!gpKeyboard)
[1]2212 {
2213 RTPrintf("Error: could not get keyboard object!\n");
2214 goto leave;
2215 }
[34645]2216 gpConsole->COMGETTER(Mouse)(gpMouse.asOutParam());
2217 if (!gpMouse)
[1]2218 {
2219 RTPrintf("Error: could not get mouse object!\n");
2220 goto leave;
2221 }
2222
[51677]2223 if (fSeparate && gpMouse)
2224 {
2225 LogFlow(("Fetching mouse caps\n"));
2226
2227 /* Fetch current mouse status, etc */
2228 gpMouse->COMGETTER(AbsoluteSupported)(&gfAbsoluteMouseGuest);
2229 gpMouse->COMGETTER(RelativeSupported)(&gfRelativeMouseGuest);
2230 gpMouse->COMGETTER(NeedsHostCursor)(&gfGuestNeedsHostCursor);
2231
2232 HandleGuestCapsChanged();
[52922]2233
2234 ComPtr<IMousePointerShape> mps;
2235 gpMouse->COMGETTER(PointerShape)(mps.asOutParam());
2236 if (!mps.isNull())
2237 {
2238 BOOL visible, alpha;
2239 ULONG hotX, hotY, width, height;
2240 com::SafeArray <BYTE> shape;
2241
2242 mps->COMGETTER(Visible)(&visible);
2243 mps->COMGETTER(Alpha)(&alpha);
2244 mps->COMGETTER(HotX)(&hotX);
2245 mps->COMGETTER(HotY)(&hotY);
2246 mps->COMGETTER(Width)(&width);
2247 mps->COMGETTER(Height)(&height);
2248 mps->COMGETTER(Shape)(ComSafeArrayAsOutParam(shape));
2249
2250 if (shape.size() > 0)
2251 {
2252 PointerShapeChangeData data(visible, alpha, hotX, hotY, width, height,
2253 ComSafeArrayAsInParam(shape));
2254 SetPointerShape(&data);
2255 }
2256 }
[51677]2257 }
2258
[1]2259 UpdateTitlebar(TITLEBAR_NORMAL);
2260
2261 /*
[10501]2262 * Create PID file.
2263 */
2264 if (gpszPidFile)
2265 {
2266 char szBuf[32];
2267 const char *pcszLf = "\n";
2268 RTFILE PidFile;
[23973]2269 RTFileOpen(&PidFile, gpszPidFile, RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE);
[10501]2270 RTStrFormatNumber(szBuf, RTProcSelf(), 10, 0, 0, 0);
2271 RTFileWrite(PidFile, szBuf, strlen(szBuf), NULL);
2272 RTFileWrite(PidFile, pcszLf, strlen(pcszLf), NULL);
2273 RTFileClose(PidFile);
2274 }
2275
2276 /*
[1]2277 * Main event loop
2278 */
[967]2279#ifdef USE_XPCOM_QUEUE_THREAD
[1]2280 if (!fXPCOMEventThreadSignaled)
2281 {
2282 signalXPCOMEventQueueThread();
2283 }
2284#endif
2285 LogFlow(("VBoxSDL: Entering big event loop\n"));
[3289]2286 while (WaitSDLEvent(&event))
[1]2287 {
2288 switch (event.type)
2289 {
2290 /*
2291 * The screen needs to be repainted.
2292 */
[20433]2293 case SDL_WINDOWEVENT:
2294 {
2295 switch (event.window.event)
2296 {
2297 case SDL_WINDOWEVENT_EXPOSED:
2298 {
2299 VBoxSDLFB *fb = getFbFromWinId(event.window.windowID);
2300 if (fb)
2301 fb->repaint();
2302 break;
2303 }
2304 case SDL_WINDOWEVENT_FOCUS_GAINED:
2305 {
2306 break;
2307 }
[81537]2308 case SDL_WINDOWEVENT_FOCUS_LOST:
2309 {
2310 break;
2311 }
2312 case SDL_WINDOWEVENT_RESIZED:
2313 {
2314 if (gpDisplay)
2315 {
2316 if (gfIgnoreNextResize)
2317 {
2318 gfIgnoreNextResize = FALSE;
2319 break;
2320 }
2321 uResizeWidth = event.window.data1;
[98302]2322 uResizeHeight = event.window.data2;
[81537]2323 if (gSdlResizeTimer)
2324 SDL_RemoveTimer(gSdlResizeTimer);
2325 gSdlResizeTimer = SDL_AddTimer(300, ResizeTimer, NULL);
2326 }
2327 break;
2328 }
[20433]2329 default:
2330 break;
2331 }
[1]2332 break;
2333 }
2334
2335 /*
2336 * Keyboard events.
2337 */
2338 case SDL_KEYDOWN:
2339 case SDL_KEYUP:
2340 {
[81537]2341 SDL_Keycode ksym = event.key.keysym.sym;
[1]2342 switch (enmHKeyState)
2343 {
2344 case HKEYSTATE_NORMAL:
2345 {
2346 if ( event.type == SDL_KEYDOWN
2347 && ksym != SDLK_UNKNOWN
2348 && (ksym == gHostKeySym1 || ksym == gHostKeySym2))
2349 {
2350 EvHKeyDown1 = event;
2351 enmHKeyState = ksym == gHostKeySym1 ? HKEYSTATE_DOWN_1ST
2352 : HKEYSTATE_DOWN_2ND;
2353 break;
2354 }
2355 ProcessKey(&event.key);
2356 break;
2357 }
2358
2359 case HKEYSTATE_DOWN_1ST:
2360 case HKEYSTATE_DOWN_2ND:
2361 {
2362 if (gHostKeySym2 != SDLK_UNKNOWN)
2363 {
2364 if ( event.type == SDL_KEYDOWN
2365 && ksym != SDLK_UNKNOWN
[5917]2366 && ( (enmHKeyState == HKEYSTATE_DOWN_1ST && ksym == gHostKeySym2)
2367 || (enmHKeyState == HKEYSTATE_DOWN_2ND && ksym == gHostKeySym1)))
[1]2368 {
2369 EvHKeyDown2 = event;
2370 enmHKeyState = HKEYSTATE_DOWN;
2371 break;
2372 }
[614]2373 enmHKeyState = event.type == SDL_KEYUP ? HKEYSTATE_NORMAL
[1]2374 : HKEYSTATE_NOT_IT;
2375 ProcessKey(&EvHKeyDown1.key);
[28405]2376 /* ugly hack: Some guests (e.g. mstsc.exe on Windows XP)
2377 * expect a small delay between two key events. 5ms work
2378 * reliable here so use 10ms to be on the safe side. A
2379 * better but more complicated fix would be to introduce
2380 * a new state and don't wait here. */
2381 RTThreadSleep(10);
[1]2382 ProcessKey(&event.key);
2383 break;
2384 }
2385 }
[69046]2386 RT_FALL_THRU();
[1]2387
2388 case HKEYSTATE_DOWN:
2389 {
2390 if (event.type == SDL_KEYDOWN)
2391 {
2392 /* potential host key combination, try execute it */
[25013]2393 int irc = HandleHostKey(&event.key);
2394 if (irc == VINF_SUCCESS)
[1]2395 {
2396 enmHKeyState = HKEYSTATE_USED;
2397 break;
2398 }
[25013]2399 if (RT_SUCCESS(irc))
[1]2400 goto leave;
2401 }
2402 else /* SDL_KEYUP */
2403 {
2404 if ( ksym != SDLK_UNKNOWN
2405 && (ksym == gHostKeySym1 || ksym == gHostKeySym2))
2406 {
2407 /* toggle grabbing state */
2408 if (!gfGrabbed)
2409 InputGrabStart();
2410 else
2411 InputGrabEnd();
2412
2413 /* SDL doesn't always reset the keystates, correct it */
2414 ResetKeys();
2415 enmHKeyState = HKEYSTATE_NORMAL;
2416 break;
2417 }
2418 }
2419
2420 /* not host key */
2421 enmHKeyState = HKEYSTATE_NOT_IT;
2422 ProcessKey(&EvHKeyDown1.key);
[28405]2423 /* see the comment for the 2-key case above */
2424 RTThreadSleep(10);
[1]2425 if (gHostKeySym2 != SDLK_UNKNOWN)
[28405]2426 {
[1]2427 ProcessKey(&EvHKeyDown2.key);
[28405]2428 /* see the comment for the 2-key case above */
2429 RTThreadSleep(10);
2430 }
[1]2431 ProcessKey(&event.key);
2432 break;
2433 }
2434
2435 case HKEYSTATE_USED:
2436 {
2437 if ((SDL_GetModState() & ~(KMOD_MODE | KMOD_NUM | KMOD_RESERVED)) == 0)
2438 enmHKeyState = HKEYSTATE_NORMAL;
2439 if (event.type == SDL_KEYDOWN)
2440 {
[25013]2441 int irc = HandleHostKey(&event.key);
2442 if (RT_SUCCESS(irc) && irc != VINF_SUCCESS)
[1]2443 goto leave;
2444 }
2445 break;
2446 }
2447
2448 default:
2449 AssertMsgFailed(("enmHKeyState=%d\n", enmHKeyState));
[69046]2450 RT_FALL_THRU();
[1]2451 case HKEYSTATE_NOT_IT:
2452 {
2453 if ((SDL_GetModState() & ~(KMOD_MODE | KMOD_NUM | KMOD_RESERVED)) == 0)
2454 enmHKeyState = HKEYSTATE_NORMAL;
2455 ProcessKey(&event.key);
2456 break;
2457 }
2458 } /* state switch */
2459 break;
2460 }
2461
2462 /*
2463 * The window was closed.
2464 */
2465 case SDL_QUIT:
2466 {
[6852]2467 if (!gfACPITerm || gSdlQuitTimer)
[6437]2468 goto leave;
[34645]2469 if (gpConsole)
2470 gpConsole->PowerButton();
[6852]2471 gSdlQuitTimer = SDL_AddTimer(1000, QuitTimer, NULL);
[1]2472 break;
2473 }
2474
2475 /*
2476 * The mouse has moved
2477 */
2478 case SDL_MOUSEMOTION:
2479 {
2480 if (gfGrabbed || UseAbsoluteMouse())
2481 {
[99090]2482 VBoxSDLFB *fb = getFbFromWinId(event.motion.windowID);
2483 if (fb)
2484 SendMouseEvent(fb, 0, 0, 0);
[1]2485 }
2486 break;
2487 }
2488
[98345]2489 case SDL_MOUSEWHEEL:
2490 {
[99090]2491 VBoxSDLFB *fb = getFbFromWinId(event.button.windowID);
2492 if (fb)
2493 SendMouseEvent(fb, -1 * event.wheel.y, 0, 0);
[98345]2494 break;
2495 }
[1]2496 /*
2497 * A mouse button has been clicked or released.
2498 */
2499 case SDL_MOUSEBUTTONDOWN:
2500 case SDL_MOUSEBUTTONUP:
2501 {
2502 SDL_MouseButtonEvent *bev = &event.button;
2503 /* don't grab on mouse click if we have guest additions */
2504 if (!gfGrabbed && !UseAbsoluteMouse() && gfGrabOnMouseClick)
2505 {
2506 if (event.type == SDL_MOUSEBUTTONDOWN && (bev->state & SDL_BUTTON_LMASK))
2507 {
2508 /* start grabbing all events */
2509 InputGrabStart();
2510 }
2511 }
[435]2512 else if (gfGrabbed || UseAbsoluteMouse())
[1]2513 {
2514 /* end host key combination (CTRL+MouseButton) */
2515 switch (enmHKeyState)
2516 {
2517 case HKEYSTATE_DOWN_1ST:
2518 case HKEYSTATE_DOWN_2ND:
2519 enmHKeyState = HKEYSTATE_NOT_IT;
2520 ProcessKey(&EvHKeyDown1.key);
[19579]2521 /* ugly hack: small delay to ensure that the key event is
2522 * actually handled _prior_ to the mouse click event */
2523 RTThreadSleep(20);
[1]2524 break;
2525 case HKEYSTATE_DOWN:
2526 enmHKeyState = HKEYSTATE_NOT_IT;
2527 ProcessKey(&EvHKeyDown1.key);
2528 if (gHostKeySym2 != SDLK_UNKNOWN)
2529 ProcessKey(&EvHKeyDown2.key);
[19579]2530 /* ugly hack: small delay to ensure that the key event is
2531 * actually handled _prior_ to the mouse click event */
2532 RTThreadSleep(20);
[1]2533 break;
2534 default:
2535 break;
2536 }
2537
[20433]2538 VBoxSDLFB *fb;
2539 fb = getFbFromWinId(event.button.windowID);
[99090]2540 if (fb)
2541 SendMouseEvent(fb, 0 /*wheel vertical movement*/, event.type == SDL_MOUSEBUTTONDOWN, bev->button);
[1]2542 }
2543 break;
2544 }
2545
[98305]2546#if 0
[1]2547 /*
2548 * The window has gained or lost focus.
2549 */
[81537]2550 case SDL_ACTIVEEVENT: /** @todo Needs to be also fixed with SDL2? Check! */
[1]2551 {
2552 /*
2553 * There is a strange behaviour in SDL when running without a window
2554 * manager: When SDL_WM_GrabInput(SDL_GRAB_ON) is called we receive two
2555 * consecutive events SDL_ACTIVEEVENTs (input lost, input gained).
2556 * Asking SDL_GetAppState() seems the better choice.
2557 */
2558 if (gfGrabbed && (SDL_GetAppState() & SDL_APPINPUTFOCUS) == 0)
2559 {
2560 /*
2561 * another window has stolen the (keyboard) input focus
2562 */
2563 InputGrabEnd();
2564 }
2565 break;
2566 }
2567
2568 /*
[81537]2569 * The SDL window was resized.
2570 * For SDL2 this is done in SDL_WINDOWEVENT.
[1]2571 */
2572 case SDL_VIDEORESIZE:
2573 {
[34645]2574 if (gpDisplay)
[1]2575 {
[10675]2576 if (gfIgnoreNextResize)
2577 {
2578 gfIgnoreNextResize = FALSE;
2579 break;
2580 }
[6934]2581 uResizeWidth = event.resize.w;
[98302]2582 uResizeHeight = event.resize.h;
[522]2583 if (gSdlResizeTimer)
2584 SDL_RemoveTimer(gSdlResizeTimer);
2585 gSdlResizeTimer = SDL_AddTimer(300, ResizeTimer, NULL);
[1]2586 }
2587 break;
2588 }
[81537]2589#endif
[1]2590 /*
2591 * User specific update event.
2592 */
2593 /** @todo use a common user event handler so that SDL_PeepEvents() won't
2594 * possibly remove other events in the queue!
2595 */
2596 case SDL_USER_EVENT_UPDATERECT:
2597 {
2598 /*
2599 * Decode event parameters.
2600 */
[3289]2601 ASMAtomicDecS32(&g_cNotifyUpdateEventsPending);
[1]2602
[99090]2603 SDL_Rect *pUpdateRect = (SDL_Rect *)event.user.data1;
2604 AssertPtrBreak(pUpdateRect);
2605
2606 int const x = pUpdateRect->x;
2607 int const y = pUpdateRect->y;
2608 int const w = pUpdateRect->w;
2609 int const h = pUpdateRect->h;
2610
2611 RTMemFree(event.user.data1);
2612
2613 Log3Func(("SDL_USER_EVENT_UPDATERECT: x=%d y=%d, w=%d, h=%d\n", x, y, w, h));
2614
[20433]2615 Assert(gpFramebuffer[event.user.code]);
2616 gpFramebuffer[event.user.code]->update(x, y, w, h, true /* fGuestRelative */);
[1]2617 break;
2618 }
2619
2620 /*
[522]2621 * User event: Window resize done
2622 */
2623 case SDL_USER_EVENT_WINDOW_RESIZE_DONE:
2624 {
2625 /**
2626 * @todo This is a workaround for synchronization problems between EMT and the
2627 * SDL main thread. It can happen that the SDL thread already starts a
2628 * new resize operation while the EMT is still busy with the old one
2629 * leading to a deadlock. Therefore we call SetVideoModeHint only once
2630 * when the mouse button was released.
2631 */
2632 /* communicate the resize event to the guest */
[42248]2633 gpDisplay->SetVideoModeHint(0 /*=display*/, true /*=enabled*/, false /*=changeOrigin*/,
2634 0 /*=originX*/, 0 /*=originY*/,
[78995]2635 uResizeWidth, uResizeHeight, 0 /*=don't change bpp*/, true /*=notify*/);
[522]2636 break;
2637
2638 }
2639
2640 /*
[51547]2641 * User specific framebuffer change event.
[1]2642 */
[51436]2643 case SDL_USER_EVENT_NOTIFYCHANGE:
2644 {
2645 LogFlow(("SDL_USER_EVENT_NOTIFYCHANGE\n"));
2646 LONG xOrigin, yOrigin;
2647 gpFramebuffer[event.user.code]->notifyChange(event.user.code);
2648 /* update xOrigin, yOrigin -> mouse */
[51677]2649 ULONG dummy;
[52978]2650 GuestMonitorStatus_T monitorStatus;
[95141]2651 hrc = gpDisplay->GetScreenResolution(event.user.code, &dummy, &dummy, &dummy, &xOrigin, &yOrigin, &monitorStatus);
[51436]2652 gpFramebuffer[event.user.code]->setOrigin(xOrigin, yOrigin);
2653 break;
2654 }
2655
[967]2656#ifdef USE_XPCOM_QUEUE_THREAD
[1]2657 /*
2658 * User specific XPCOM event queue event
2659 */
2660 case SDL_USER_EVENT_XPCOM_EVENTQUEUE:
2661 {
2662 LogFlow(("SDL_USER_EVENT_XPCOM_EVENTQUEUE: processing XPCOM event queue...\n"));
[22911]2663 eventQ->processEventQueue(0);
[1]2664 signalXPCOMEventQueueThread();
2665 break;
2666 }
[967]2667#endif /* USE_XPCOM_QUEUE_THREAD */
[1]2668
2669 /*
2670 * User specific update title bar notification event
2671 */
2672 case SDL_USER_EVENT_UPDATE_TITLEBAR:
2673 {
2674 UpdateTitlebar(TITLEBAR_NORMAL);
2675 break;
2676 }
2677
2678 /*
2679 * User specific termination event
2680 */
2681 case SDL_USER_EVENT_TERMINATE:
2682 {
2683 if (event.user.code != VBOXSDL_TERM_NORMAL)
2684 RTPrintf("Error: VM terminated abnormally!\n");
2685 goto leave;
2686 }
2687 /*
2688 * User specific pointer shape change event
2689 */
2690 case SDL_USER_EVENT_POINTER_CHANGE:
2691 {
[28205]2692 PointerShapeChangeData *data = (PointerShapeChangeData *)event.user.data1;
[1]2693 SetPointerShape (data);
2694 delete data;
2695 break;
2696 }
2697
2698 /*
2699 * User specific guest capabilities changed
2700 */
2701 case SDL_USER_EVENT_GUEST_CAP_CHANGED:
2702 {
2703 HandleGuestCapsChanged();
2704 break;
2705 }
2706
2707 default:
2708 {
[55988]2709 Log8(("unknown SDL event %d\n", event.type));
[1]2710 break;
2711 }
2712 }
2713 }
2714
2715leave:
[10501]2716 if (gpszPidFile)
2717 RTFileDelete(gpszPidFile);
2718
[1]2719 LogFlow(("leaving...\n"));
[3669]2720#if defined(VBOX_WITH_XPCOM) && !defined(RT_OS_DARWIN) && !defined(RT_OS_OS2)
[1]2721 /* make sure the XPCOM event queue thread doesn't do anything harmful */
2722 terminateXPCOMQueueThread();
[614]2723#endif /* VBOX_WITH_XPCOM */
[1]2724
[34645]2725 if (gpVRDEServer)
[95141]2726 hrc = gpVRDEServer->COMSETTER(Enabled)(FALSE);
[1]2727
2728 /*
2729 * Get the machine state.
2730 */
[34645]2731 if (gpMachine)
2732 gpMachine->COMGETTER(State)(&machineState);
[1]2733 else
2734 machineState = MachineState_Aborted;
2735
[51677]2736 if (!fSeparate)
2737 {
2738 /*
2739 * Turn off the VM if it's running
2740 */
2741 if ( gpConsole
2742 && ( machineState == MachineState_Running
2743 || machineState == MachineState_Teleporting
2744 || machineState == MachineState_LiveSnapshotting
2745 /** @todo power off paused VMs too? */
2746 )
[24301]2747 )
[51677]2748 do
[1]2749 {
[51677]2750 pConsoleListener->getWrapped()->ignorePowerOffEvents(true);
2751 ComPtr<IProgress> pProgress;
2752 CHECK_ERROR_BREAK(gpConsole, PowerDown(pProgress.asOutParam()));
2753 CHECK_ERROR_BREAK(pProgress, WaitForCompletion(-1));
2754 BOOL completed;
2755 CHECK_ERROR_BREAK(pProgress, COMGETTER(Completed)(&completed));
2756 ASSERT(completed);
[95141]2757 LONG hrc2;
2758 CHECK_ERROR_BREAK(pProgress, COMGETTER(ResultCode)(&hrc2));
2759 if (FAILED(hrc2))
[51677]2760 {
2761 com::ErrorInfo info;
2762 if (info.isFullAvailable())
2763 PrintError("Failed to power down VM",
2764 info.getText().raw(), info.getComponent().raw());
2765 else
[95141]2766 RTPrintf("Failed to power down virtual machine! No error information available (rc=%Rhrc).\n", hrc2);
[51677]2767 break;
2768 }
2769 } while (0);
2770 }
[1]2771
[34645]2772 /* unregister Console listener */
2773 if (pConsoleListener)
[30618]2774 {
[34645]2775 ComPtr<IEventSource> pES;
2776 CHECK_ERROR(gpConsole, COMGETTER(EventSource)(pES.asOutParam()));
2777 if (!pES.isNull())
2778 CHECK_ERROR(pES, UnregisterListener(pConsoleListener));
[35726]2779 pConsoleListener.setNull();
[30618]2780 }
2781
[1]2782 /*
2783 * Now we discard all settings so that our changes will
2784 * not be flushed to the permanent configuration
2785 */
[34645]2786 if ( gpMachine
[91363]2787 && machineState != MachineState_Saved
2788 && machineState != MachineState_AbortedSaved)
[1]2789 {
[95141]2790 hrc = gpMachine->DiscardSettings();
2791 AssertMsg(SUCCEEDED(hrc), ("DiscardSettings %Rhrc, machineState %d\n", hrc, machineState));
[1]2792 }
2793
2794 /* close the session */
2795 if (sessionOpened)
2796 {
[95141]2797 hrc = pSession->UnlockMachine();
2798 AssertComRC(hrc);
[1]2799 }
2800
[33386]2801 LogFlow(("Releasing mouse, keyboard, remote desktop server, display, console...\n"));
[34645]2802 if (gpDisplay)
[20433]2803 {
2804 for (unsigned i = 0; i < gcMonitors; i++)
[55133]2805 gpDisplay->DetachFramebuffer(i, gaFramebufferId[i].raw());
[30681]2806 }
[30618]2807
[34645]2808 gpMouse = NULL;
2809 gpKeyboard = NULL;
2810 gpVRDEServer = NULL;
2811 gpDisplay = NULL;
2812 gpConsole = NULL;
2813 gpMachineDebugger = NULL;
2814 gpProgress = NULL;
[1]2815 // we can only uninitialize SDL here because it is not threadsafe
[20433]2816
2817 for (unsigned i = 0; i < gcMonitors; i++)
[20842]2818 {
[20433]2819 if (gpFramebuffer[i])
2820 {
2821 LogFlow(("Releasing framebuffer...\n"));
2822 gpFramebuffer[i]->Release();
2823 gpFramebuffer[i] = NULL;
2824 }
[1]2825 }
[20433]2826
[41216]2827 VBoxSDLFB::uninit();
[20433]2828
[34645]2829 /* VirtualBox (server) listener unregistration. */
2830 if (pVBoxListener)
[30618]2831 {
[34645]2832 ComPtr<IEventSource> pES;
2833 CHECK_ERROR(pVirtualBox, COMGETTER(EventSource)(pES.asOutParam()));
2834 if (!pES.isNull())
2835 CHECK_ERROR(pES, UnregisterListener(pVBoxListener));
[35726]2836 pVBoxListener.setNull();
[30618]2837 }
[34645]2838
[34646]2839 /* VirtualBoxClient listener unregistration. */
2840 if (pVBoxClientListener)
2841 {
2842 ComPtr<IEventSource> pES;
2843 CHECK_ERROR(pVirtualBoxClient, COMGETTER(EventSource)(pES.asOutParam()));
2844 if (!pES.isNull())
2845 CHECK_ERROR(pES, UnregisterListener(pVBoxClientListener));
[35726]2846 pVBoxClientListener.setNull();
[34646]2847 }
2848
[1]2849 LogFlow(("Releasing machine, session...\n"));
[34645]2850 gpMachine = NULL;
2851 pSession = NULL;
[1]2852 LogFlow(("Releasing VirtualBox object...\n"));
[34645]2853 pVirtualBox = NULL;
[34646]2854 LogFlow(("Releasing VirtualBoxClient object...\n"));
2855 pVirtualBoxClient = NULL;
[1]2856
2857 // end "all-stuff" scope
2858 ////////////////////////////////////////////////////////////////////////////
2859 }
2860
[25026]2861 /* Must be before com::Shutdown() */
[1]2862 LogFlow(("Uninitializing COM...\n"));
2863 com::Shutdown();
2864
2865 LogFlow(("Returning from main()!\n"));
2866 RTLogFlush(NULL);
[98357]2867
2868#ifdef RT_OS_WINDOWS
2869 FreeConsole(); /* Detach or destroy (from) console. */
2870#endif
2871
[95141]2872 return FAILED(hrc) ? 1 : 0;
[1]2873}
2874
[11725]2875#ifndef VBOX_WITH_HARDENING
[1]2876/**
[11725]2877 * Main entry point
2878 */
2879int main(int argc, char **argv)
2880{
[48137]2881#ifdef Q_WS_X11
2882 if (!XInitThreads())
2883 return 1;
2884#endif
[11725]2885 /*
2886 * Before we do *anything*, we initialize the runtime.
2887 */
[92613]2888 int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_TRY_SUPLIB);
[38636]2889 if (RT_FAILURE(rc))
2890 return RTMsgInitFailure(rc);
[98343]2891
[98357]2892 return TrustedMain(argc, argv, NULL);
[11725]2893}
2894#endif /* !VBOX_WITH_HARDENING */
2895
2896
2897/**
[1]2898 * Returns whether the absolute mouse is in use, i.e. both host
2899 * and guest have opted to enable it.
2900 *
2901 * @returns bool Flag whether the absolute mouse is in use
2902 */
2903static bool UseAbsoluteMouse(void)
2904{
2905 return (gfAbsoluteMouseHost && gfAbsoluteMouseGuest);
2906}
2907
[962]2908
2909/**
[1]2910 * Releases any modifier keys that are currently in pressed state.
2911 */
2912static void ResetKeys(void)
2913{
2914 int i;
2915
[34645]2916 if (!gpKeyboard)
[1]2917 return;
2918
2919 for(i = 0; i < 256; i++)
2920 {
2921 if (gaModifiersState[i])
2922 {
2923 if (i & 0x80)
[34645]2924 gpKeyboard->PutScancode(0xe0);
2925 gpKeyboard->PutScancode(i | 0x80);
[1]2926 gaModifiersState[i] = 0;
2927 }
2928 }
2929}
2930
2931/**
2932 * Keyboard event handler.
2933 *
2934 * @param ev SDL keyboard event.
2935 */
2936static void ProcessKey(SDL_KeyboardEvent *ev)
2937{
[98370]2938 /* According to SDL2/SDL_scancodes.h ev->keysym.sym stores scancodes which are
2939 * based on USB usage page standard. This is what we can directly pass to
2940 * IKeyboard::putUsageCode. */
[98392]2941 gpKeyboard->PutUsageCode(SDL_GetScancodeFromKey(ev->keysym.sym), 0x07 /*usage code page id*/, ev->type == SDL_KEYUP ? TRUE : FALSE);
[1]2942}
2943
[3669]2944#ifdef RT_OS_DARWIN
[965]2945#include <Carbon/Carbon.h>
[20374]2946RT_C_DECLS_BEGIN
[965]2947/* Private interface in 10.3 and later. */
2948typedef int CGSConnection;
2949typedef enum
2950{
2951 kCGSGlobalHotKeyEnable = 0,
2952 kCGSGlobalHotKeyDisable,
2953 kCGSGlobalHotKeyInvalid = -1 /* bird */
2954} CGSGlobalHotKeyOperatingMode;
2955extern CGSConnection _CGSDefaultConnection(void);
2956extern CGError CGSGetGlobalHotKeyOperatingMode(CGSConnection Connection, CGSGlobalHotKeyOperatingMode *enmMode);
2957extern CGError CGSSetGlobalHotKeyOperatingMode(CGSConnection Connection, CGSGlobalHotKeyOperatingMode enmMode);
[20374]2958RT_C_DECLS_END
[965]2959
2960/** Keeping track of whether we disabled the hotkeys or not. */
2961static bool g_fHotKeysDisabled = false;
2962/** Whether we've connected or not. */
2963static bool g_fConnectedToCGS = false;
2964/** Cached connection. */
2965static CGSConnection g_CGSConnection;
2966
[1]2967/**
[965]2968 * Disables or enabled global hot keys.
2969 */
2970static void DisableGlobalHotKeys(bool fDisable)
2971{
2972 if (!g_fConnectedToCGS)
2973 {
2974 g_CGSConnection = _CGSDefaultConnection();
2975 g_fConnectedToCGS = true;
2976 }
2977
2978 /* get current mode. */
2979 CGSGlobalHotKeyOperatingMode enmMode = kCGSGlobalHotKeyInvalid;
2980 CGSGetGlobalHotKeyOperatingMode(g_CGSConnection, &enmMode);
2981
2982 /* calc new mode. */
2983 if (fDisable)
2984 {
2985 if (enmMode != kCGSGlobalHotKeyEnable)
2986 return;
2987 enmMode = kCGSGlobalHotKeyDisable;
2988 }
2989 else
2990 {
2991 if ( enmMode != kCGSGlobalHotKeyDisable
2992 /*|| !g_fHotKeysDisabled*/)
2993 return;
2994 enmMode = kCGSGlobalHotKeyEnable;
2995 }
2996
2997 /* try set it and check the actual result. */
2998 CGSSetGlobalHotKeyOperatingMode(g_CGSConnection, enmMode);
2999 CGSGlobalHotKeyOperatingMode enmNewMode = kCGSGlobalHotKeyInvalid;
3000 CGSGetGlobalHotKeyOperatingMode(g_CGSConnection, &enmNewMode);
3001 if (enmNewMode == enmMode)
3002 g_fHotKeysDisabled = enmMode == kCGSGlobalHotKeyDisable;
3003}
[3669]3004#endif /* RT_OS_DARWIN */
[965]3005
3006/**
[1]3007 * Start grabbing the mouse.
3008 */
3009static void InputGrabStart(void)
3010{
[3669]3011#ifdef RT_OS_DARWIN
[965]3012 DisableGlobalHotKeys(true);
3013#endif
[26782]3014 if (!gfGuestNeedsHostCursor && gfRelativeMouseGuest)
[1]3015 SDL_ShowCursor(SDL_DISABLE);
[81537]3016 SDL_SetRelativeMouseMode(SDL_TRUE);
[1]3017 gfGrabbed = TRUE;
3018 UpdateTitlebar(TITLEBAR_NORMAL);
3019}
3020
3021/**
3022 * End mouse grabbing.
3023 */
3024static void InputGrabEnd(void)
3025{
[81537]3026 SDL_SetRelativeMouseMode(SDL_FALSE);
[26782]3027 if (!gfGuestNeedsHostCursor && gfRelativeMouseGuest)
[1]3028 SDL_ShowCursor(SDL_ENABLE);
[3669]3029#ifdef RT_OS_DARWIN
[965]3030 DisableGlobalHotKeys(false);
3031#endif
[1]3032 gfGrabbed = FALSE;
3033 UpdateTitlebar(TITLEBAR_NORMAL);
3034}
3035
3036/**
3037 * Query mouse position and button state from SDL and send to the VM
3038 *
3039 * @param dz Relative mouse wheel movement
3040 */
[20433]3041static void SendMouseEvent(VBoxSDLFB *fb, int dz, int down, int button)
[1]3042{
3043 int x, y, state, buttons;
3044 bool abs;
3045
[20626]3046 if (!fb)
[20433]3047 {
[81537]3048 SDL_GetMouseState(&x, &y);
[20433]3049 RTPrintf("MouseEvent: Cannot find fb mouse = %d,%d\n", x, y);
3050 return;
3051 }
3052
[1]3053 /*
3054 * If supported and we're not in grabbed mode, we'll use the absolute mouse.
3055 * If we are in grabbed mode and the guest is not able to draw the mouse cursor
[26782]3056 * itself, or can't handle relative reporting, we have to use absolute
3057 * coordinates, otherwise the host cursor and
[1]3058 * the coordinates the guest thinks the mouse is at could get out-of-sync. From
3059 * the SDL mailing list:
3060 *
3061 * "The event processing is usually asynchronous and so somewhat delayed, and
3062 * SDL_GetMouseState is returning the immediate mouse state. So at the time you
3063 * call SDL_GetMouseState, the "button" is already up."
3064 */
[26782]3065 abs = (UseAbsoluteMouse() && !gfGrabbed)
3066 || gfGuestNeedsHostCursor
3067 || !gfRelativeMouseGuest;
[1]3068
3069 /* only used if abs == TRUE */
[20433]3070 int xOrigin = fb->getOriginX();
3071 int yOrigin = fb->getOriginY();
3072 int xMin = fb->getXOffset() + xOrigin;
3073 int yMin = fb->getYOffset() + yOrigin;
3074 int xMax = xMin + (int)fb->getGuestXRes();
3075 int yMax = yMin + (int)fb->getGuestYRes();
[1]3076
[81537]3077 state = abs ? SDL_GetMouseState(&x, &y)
3078 : SDL_GetRelativeMouseState(&x, &y);
[1]3079
3080 /*
3081 * process buttons
3082 */
3083 buttons = 0;
3084 if (state & SDL_BUTTON(SDL_BUTTON_LEFT))
3085 buttons |= MouseButtonState_LeftButton;
3086 if (state & SDL_BUTTON(SDL_BUTTON_RIGHT))
3087 buttons |= MouseButtonState_RightButton;
3088 if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE))
3089 buttons |= MouseButtonState_MiddleButton;
3090
3091 if (abs)
3092 {
[20433]3093 x += xOrigin;
3094 y += yOrigin;
3095
[1]3096 /*
3097 * Check if the mouse event is inside the guest area. This solves the
3098 * following problem: Some guests switch off the VBox hardware mouse
3099 * cursor and draw the mouse cursor itself instead. Moving the mouse
3100 * outside the guest area then leads to annoying mouse hangs if we
3101 * don't pass mouse motion events into the guest.
3102 */
3103 if (x < xMin || y < yMin || x > xMax || y > yMax)
3104 {
3105 /*
3106 * Cursor outside of valid guest area (outside window or in secure
3107 * label area. Don't allow any mouse button press.
3108 */
3109 button = 0;
3110
3111 /*
3112 * Release any pressed button.
3113 */
3114#if 0
3115 /* disabled on customers request */
3116 buttons &= ~(MouseButtonState_LeftButton |
3117 MouseButtonState_MiddleButton |
3118 MouseButtonState_RightButton);
3119#endif
3120
3121 /*
3122 * Prevent negative coordinates.
3123 */
3124 if (x < xMin) x = xMin;
3125 if (x > xMax) x = xMax;
3126 if (y < yMin) y = yMin;
3127 if (y > yMax) y = yMax;
3128
3129 if (!gpOffCursor)
3130 {
3131 gpOffCursor = SDL_GetCursor(); /* Cursor image */
3132 gfOffCursorActive = SDL_ShowCursor(-1); /* enabled / disabled */
3133 SDL_SetCursor(gpDefaultCursor);
[28205]3134 SDL_ShowCursor(SDL_ENABLE);
[1]3135 }
3136 }
3137 else
3138 {
3139 if (gpOffCursor)
3140 {
3141 /*
3142 * We just entered the valid guest area. Restore the guest mouse
3143 * cursor.
3144 */
3145 SDL_SetCursor(gpOffCursor);
3146 SDL_ShowCursor(gfOffCursorActive ? SDL_ENABLE : SDL_DISABLE);
3147 gpOffCursor = NULL;
3148 }
3149 }
3150 }
3151
3152 /*
3153 * Button was pressed but that press is not reflected in the button state?
3154 */
3155 if (down && !(state & SDL_BUTTON(button)))
3156 {
3157 /*
3158 * It can happen that a mouse up event follows a mouse down event immediately
3159 * and we see the events when the bit in the button state is already cleared
3160 * again. In that case we simulate the mouse down event.
3161 */
3162 int tmp_button = 0;
3163 switch (button)
3164 {
3165 case SDL_BUTTON_LEFT: tmp_button = MouseButtonState_LeftButton; break;
3166 case SDL_BUTTON_MIDDLE: tmp_button = MouseButtonState_MiddleButton; break;
3167 case SDL_BUTTON_RIGHT: tmp_button = MouseButtonState_RightButton; break;
3168 }
3169
3170 if (abs)
3171 {
3172 /**
3173 * @todo
3174 * PutMouseEventAbsolute() expects x and y starting from 1,1.
3175 * should we do the increment internally in PutMouseEventAbsolute()
3176 * or state it in PutMouseEventAbsolute() docs?
3177 */
[34645]3178 gpMouse->PutMouseEventAbsolute(x + 1 - xMin + xOrigin,
3179 y + 1 - yMin + yOrigin,
3180 dz, 0 /* horizontal scroll wheel */,
3181 buttons | tmp_button);
[1]3182 }
3183 else
3184 {
[34645]3185 gpMouse->PutMouseEvent(0, 0, dz,
3186 0 /* horizontal scroll wheel */,
3187 buttons | tmp_button);
[1]3188 }
3189 }
3190
3191 // now send the mouse event
3192 if (abs)
3193 {
3194 /**
3195 * @todo
3196 * PutMouseEventAbsolute() expects x and y starting from 1,1.
3197 * should we do the increment internally in PutMouseEventAbsolute()
3198 * or state it in PutMouseEventAbsolute() docs?
3199 */
[34645]3200 gpMouse->PutMouseEventAbsolute(x + 1 - xMin + xOrigin,
3201 y + 1 - yMin + yOrigin,
3202 dz, 0 /* Horizontal wheel */, buttons);
[1]3203 }
3204 else
3205 {
[34645]3206 gpMouse->PutMouseEvent(x, y, dz, 0 /* Horizontal wheel */, buttons);
[1]3207 }
3208}
3209
3210/**
3211 * Resets the VM
3212 */
3213void ResetVM(void)
3214{
[34645]3215 if (gpConsole)
3216 gpConsole->Reset();
[1]3217}
3218
3219/**
3220 * Initiates a saved state and updates the titlebar with progress information
3221 */
3222void SaveState(void)
3223{
3224 ResetKeys();
3225 RTThreadYield();
3226 if (gfGrabbed)
3227 InputGrabEnd();
3228 RTThreadYield();
3229 UpdateTitlebar(TITLEBAR_SAVE);
[34645]3230 gpProgress = NULL;
[95141]3231 HRESULT hrc = gpMachine->SaveState(gpProgress.asOutParam());
3232 if (FAILED(hrc))
[1]3233 {
[95141]3234 RTPrintf("Error saving state! rc=%Rhrc\n", hrc);
[1]3235 return;
3236 }
[34645]3237 Assert(gpProgress);
[1]3238
3239 /*
3240 * Wait for the operation to be completed and work
3241 * the title bar in the mean while.
3242 */
[18274]3243 ULONG cPercent = 0;
[3669]3244#ifndef RT_OS_DARWIN /* don't break the other guys yet. */
[1]3245 for (;;)
3246 {
3247 BOOL fCompleted = false;
[95141]3248 hrc = gpProgress->COMGETTER(Completed)(&fCompleted);
3249 if (FAILED(hrc) || fCompleted)
[1]3250 break;
[18274]3251 ULONG cPercentNow;
[95141]3252 hrc = gpProgress->COMGETTER(Percent)(&cPercentNow);
3253 if (FAILED(hrc))
[1]3254 break;
3255 if (cPercentNow != cPercent)
3256 {
3257 UpdateTitlebar(TITLEBAR_SAVE, cPercent);
3258 cPercent = cPercentNow;
3259 }
3260
3261 /* wait */
[95141]3262 hrc = gpProgress->WaitForCompletion(100);
3263 if (FAILED(hrc))
[1]3264 break;
3265 /// @todo process gui events.
3266 }
3267
[967]3268#else /* new loop which processes GUI events while saving. */
3269
3270 /* start regular timer so we don't starve in the event loop */
3271 SDL_TimerID sdlTimer;
3272 sdlTimer = SDL_AddTimer(100, StartupTimer, NULL);
3273
3274 for (;;)
3275 {
3276 /*
3277 * Check for completion.
3278 */
3279 BOOL fCompleted = false;
[95141]3280 hrc = gpProgress->COMGETTER(Completed)(&fCompleted);
3281 if (FAILED(hrc) || fCompleted)
[967]3282 break;
[18274]3283 ULONG cPercentNow;
[95141]3284 hrc = gpProgress->COMGETTER(Percent)(&cPercentNow);
3285 if (FAILED(hrc))
[967]3286 break;
3287 if (cPercentNow != cPercent)
3288 {
3289 UpdateTitlebar(TITLEBAR_SAVE, cPercent);
3290 cPercent = cPercentNow;
3291 }
3292
3293 /*
3294 * Wait for and process GUI a event.
3295 * This is necessary for XPCOM IPC and for updating the
3296 * title bar on the Mac.
3297 */
3298 SDL_Event event;
[3289]3299 if (WaitSDLEvent(&event))
[967]3300 {
3301 switch (event.type)
3302 {
3303 /*
3304 * Timer event preventing us from getting stuck.
3305 */
3306 case SDL_USER_EVENT_TIMER:
3307 break;
3308
3309#ifdef USE_XPCOM_QUEUE_THREAD
3310 /*
3311 * User specific XPCOM event queue event
3312 */
3313 case SDL_USER_EVENT_XPCOM_EVENTQUEUE:
3314 {
3315 LogFlow(("SDL_USER_EVENT_XPCOM_EVENTQUEUE: processing XPCOM event queue...\n"));
3316 eventQ->ProcessPendingEvents();
3317 signalXPCOMEventQueueThread();
3318 break;
3319 }
3320#endif /* USE_XPCOM_QUEUE_THREAD */
3321
3322
3323 /*
3324 * Ignore all other events.
3325 */
[51547]3326 case SDL_USER_EVENT_NOTIFYCHANGE:
[967]3327 case SDL_USER_EVENT_TERMINATE:
3328 default:
3329 break;
3330 }
3331 }
3332 }
3333
3334 /* kill the timer */
3335 SDL_RemoveTimer(sdlTimer);
3336 sdlTimer = 0;
3337
[3669]3338#endif /* RT_OS_DARWIN */
[967]3339
[1]3340 /*
3341 * What's the result of the operation?
3342 */
[20267]3343 LONG lrc;
[95141]3344 hrc = gpProgress->COMGETTER(ResultCode)(&lrc);
3345 if (FAILED(hrc))
[1]3346 lrc = ~0;
3347 if (!lrc)
3348 {
3349 UpdateTitlebar(TITLEBAR_SAVE, 100);
3350 RTThreadYield();
3351 RTPrintf("Saved the state successfully.\n");
3352 }
3353 else
3354 RTPrintf("Error saving state, lrc=%d (%#x)\n", lrc, lrc);
3355}
3356
3357/**
3358 * Build the titlebar string
3359 */
3360static void UpdateTitlebar(TitlebarMode mode, uint32_t u32User)
3361{
[445]3362 static char szTitle[1024] = {0};
[1]3363
3364 /* back up current title */
[445]3365 char szPrevTitle[1024];
3366 strcpy(szPrevTitle, szTitle);
[1]3367
[34645]3368 Bstr bstrName;
3369 gpMachine->COMGETTER(Name)(bstrName.asOutParam());
[1]3370
[26429]3371 RTStrPrintf(szTitle, sizeof(szTitle), "%s - " VBOX_PRODUCT,
[34645]3372 !bstrName.isEmpty() ? Utf8Str(bstrName).c_str() : "<noname>");
[1]3373
3374 /* which mode are we in? */
3375 switch (mode)
3376 {
3377 case TITLEBAR_NORMAL:
3378 {
3379 MachineState_T machineState;
[34645]3380 gpMachine->COMGETTER(State)(&machineState);
[1]3381 if (machineState == MachineState_Paused)
[26429]3382 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle), " - [Paused]");
[1]3383
3384 if (gfGrabbed)
[26429]3385 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle), " - [Input captured]");
[1]3386
[26429]3387#if defined(DEBUG) || defined(VBOX_WITH_STATISTICS)
[1]3388 // do we have a debugger interface
[34645]3389 if (gpMachineDebugger)
[1]3390 {
3391 // query the machine state
3392 BOOL singlestepEnabled = FALSE;
3393 BOOL logEnabled = FALSE;
[93460]3394 VMExecutionEngine_T enmExecEngine = VMExecutionEngine_NotSet;
[445]3395 ULONG virtualTimeRate = 100;
[34645]3396 gpMachineDebugger->COMGETTER(LogEnabled)(&logEnabled);
[39725]3397 gpMachineDebugger->COMGETTER(SingleStep)(&singlestepEnabled);
[93460]3398 gpMachineDebugger->COMGETTER(ExecutionEngine)(&enmExecEngine);
[34645]3399 gpMachineDebugger->COMGETTER(VirtualTimeRate)(&virtualTimeRate);
[445]3400 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
[93460]3401 " [STEP=%d LOG=%d EXEC=%s",
3402 singlestepEnabled == TRUE, logEnabled == TRUE,
[103123]3403 enmExecEngine == VMExecutionEngine_NotSet ? "NotSet"
3404 : enmExecEngine == VMExecutionEngine_Default ? "Default"
3405 : enmExecEngine == VMExecutionEngine_HwVirt ? "HM"
[103124]3406 : enmExecEngine == VMExecutionEngine_NativeApi ? "NEM"
[103123]3407 : enmExecEngine == VMExecutionEngine_Interpreter ? "Interpreter"
[103124]3408 : enmExecEngine == VMExecutionEngine_Recompiler ? "Recompiler" : "UNK");
[445]3409 char *psz = strchr(szTitle, '\0');
3410 if (virtualTimeRate != 100)
3411 RTStrPrintf(psz, &szTitle[sizeof(szTitle)] - psz, " WD=%d%%]", virtualTimeRate);
3412 else
3413 RTStrPrintf(psz, &szTitle[sizeof(szTitle)] - psz, "]");
[1]3414 }
[26429]3415#endif /* DEBUG || VBOX_WITH_STATISTICS */
[1]3416 break;
3417 }
3418
3419 case TITLEBAR_STARTUP:
3420 {
3421 /*
3422 * Format it.
3423 */
3424 MachineState_T machineState;
[34645]3425 gpMachine->COMGETTER(State)(&machineState);
[1]3426 if (machineState == MachineState_Starting)
[26429]3427 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
3428 " - Starting...");
[1]3429 else if (machineState == MachineState_Restoring)
3430 {
[18274]3431 ULONG cPercentNow;
[95141]3432 HRESULT hrc = gpProgress->COMGETTER(Percent)(&cPercentNow);
3433 if (SUCCEEDED(hrc))
[445]3434 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
[1]3435 " - Restoring %d%%...", (int)cPercentNow);
3436 else
[445]3437 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
[1]3438 " - Restoring...");
3439 }
[24301]3440 else if (machineState == MachineState_TeleportingIn)
[23703]3441 {
3442 ULONG cPercentNow;
[95141]3443 HRESULT hrc = gpProgress->COMGETTER(Percent)(&cPercentNow);
3444 if (SUCCEEDED(hrc))
[23703]3445 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
[23801]3446 " - Teleporting %d%%...", (int)cPercentNow);
[23703]3447 else
3448 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
[23801]3449 " - Teleporting...");
[23703]3450 }
[1]3451 /* ignore other states, we could already be in running or aborted state */
3452 break;
3453 }
3454
3455 case TITLEBAR_SAVE:
3456 {
[13130]3457 AssertMsg(u32User <= 100, ("%d\n", u32User));
[445]3458 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
[1]3459 " - Saving %d%%...", u32User);
3460 break;
3461 }
3462
3463 case TITLEBAR_SNAPSHOT:
3464 {
[13130]3465 AssertMsg(u32User <= 100, ("%d\n", u32User));
[445]3466 RTStrPrintf(szTitle + strlen(szTitle), sizeof(szTitle) - strlen(szTitle),
[1]3467 " - Taking snapshot %d%%...", u32User);
3468 break;
3469 }
3470
3471 default:
3472 RTPrintf("Error: Invalid title bar mode %d!\n", mode);
3473 return;
3474 }
3475
3476 /*
3477 * Don't update if it didn't change.
3478 */
[18751]3479 if (!strcmp(szTitle, szPrevTitle))
[1]3480 return;
3481
3482 /*
3483 * Set the new title
3484 */
3485#ifdef VBOX_WIN32_UI
[445]3486 setUITitle(szTitle);
[1]3487#else
[81537]3488 for (unsigned i = 0; i < gcMonitors; i++)
3489 gpFramebuffer[i]->setWindowTitle(szTitle);
[1]3490#endif
3491}
3492
3493#if 0
[34645]3494static void vbox_show_shape(unsigned short w, unsigned short h,
3495 uint32_t bg, const uint8_t *image)
[1]3496{
3497 size_t x, y;
3498 unsigned short pitch;
3499 const uint32_t *color;
3500 const uint8_t *mask;
3501 size_t size_mask;
3502
3503 mask = image;
3504 pitch = (w + 7) / 8;
3505 size_mask = (pitch * h + 3) & ~3;
3506
[28205]3507 color = (const uint32_t *)(image + size_mask);
[1]3508
[28205]3509 printf("show_shape %dx%d pitch %d size mask %d\n",
3510 w, h, pitch, size_mask);
[1]3511 for (y = 0; y < h; ++y, mask += pitch, color += w)
3512 {
3513 for (x = 0; x < w; ++x) {
3514 if (mask[x / 8] & (1 << (7 - (x % 8))))
[28205]3515 printf(" ");
[1]3516 else
3517 {
3518 uint32_t c = color[x];
3519 if (c == bg)
[28205]3520 printf("Y");
[1]3521 else
[28205]3522 printf("X");
[1]3523 }
3524 }
[28205]3525 printf("\n");
[1]3526 }
3527}
3528#endif
3529
3530/**
3531 * Sets the pointer shape according to parameters.
3532 * Must be called only from the main SDL thread.
3533 */
[28205]3534static void SetPointerShape(const PointerShapeChangeData *data)
[1]3535{
3536 /*
3537 * don't allow to change the pointer shape if we are outside the valid
3538 * guest area. In that case set standard mouse pointer is set and should
3539 * not get overridden.
3540 */
3541 if (gpOffCursor)
3542 return;
3543
[29518]3544 if (data->shape.size() > 0)
[1]3545 {
3546 bool ok = false;
3547
[98372]3548#if defined(RT_OS_WINDOWS) || (defined(VBOXSDL_WITH_X11) && !defined(VBOX_WITHOUT_XCURSOR))
3549 AssertReturnVoid(data->height); /* Prevent division by zero. */
3550 uint32_t const andMaskSize = (data->width + 7) / 8 * data->height;
3551 uint32_t const srcShapePtrScan = data->width * 4;
3552
3553 uint8_t const *shape = data->shape.raw();
3554 uint8_t const *srcAndMaskPtr = shape;
3555 uint8_t const *srcShapePtr = shape + ((andMaskSize + 3) & ~3);
3556#endif
3557
[1]3558#if 0
3559 /* pointer debugging code */
3560 // vbox_show_shape(data->width, data->height, 0, data->shape);
3561 uint32_t shapeSize = ((((data->width + 7) / 8) * data->height + 3) & ~3) + data->width * 4 * data->height;
3562 printf("visible: %d\n", data->visible);
3563 printf("width = %d\n", data->width);
3564 printf("height = %d\n", data->height);
3565 printf("alpha = %d\n", data->alpha);
3566 printf("xhot = %d\n", data->xHot);
3567 printf("yhot = %d\n", data->yHot);
3568 printf("uint8_t pointerdata[] = { ");
3569 for (uint32_t i = 0; i < shapeSize; i++)
3570 {
3571 printf("0x%x, ", data->shape[i]);
3572 }
3573 printf("};\n");
3574#endif
3575
[28205]3576#if defined(RT_OS_WINDOWS)
[1]3577 BITMAPV5HEADER bi;
3578 HBITMAP hBitmap;
3579 void *lpBits;
3580
[28205]3581 ::ZeroMemory(&bi, sizeof(BITMAPV5HEADER));
3582 bi.bV5Size = sizeof(BITMAPV5HEADER);
[1]3583 bi.bV5Width = data->width;
[28205]3584 bi.bV5Height = -(LONG)data->height;
[1]3585 bi.bV5Planes = 1;
3586 bi.bV5BitCount = 32;
3587 bi.bV5Compression = BI_BITFIELDS;
[33540]3588 // specify a supported 32 BPP alpha format for Windows XP
[1]3589 bi.bV5RedMask = 0x00FF0000;
3590 bi.bV5GreenMask = 0x0000FF00;
3591 bi.bV5BlueMask = 0x000000FF;
3592 if (data->alpha)
3593 bi.bV5AlphaMask = 0xFF000000;
3594 else
3595 bi.bV5AlphaMask = 0;
3596
[28205]3597 HDC hdc = ::GetDC(NULL);
[1]3598
3599 // create the DIB section with an alpha channel
[28205]3600 hBitmap = ::CreateDIBSection(hdc, (BITMAPINFO *)&bi, DIB_RGB_COLORS,
3601 (void **)&lpBits, NULL, (DWORD)0);
[1]3602
[28205]3603 ::ReleaseDC(NULL, hdc);
[1]3604
3605 HBITMAP hMonoBitmap = NULL;
3606 if (data->alpha)
3607 {
3608 // create an empty mask bitmap
[28205]3609 hMonoBitmap = ::CreateBitmap(data->width, data->height, 1, 1, NULL);
[1]3610 }
3611 else
3612 {
3613 /* Word aligned AND mask. Will be allocated and created if necessary. */
3614 uint8_t *pu8AndMaskWordAligned = NULL;
3615
3616 /* Width in bytes of the original AND mask scan line. */
3617 uint32_t cbAndMaskScan = (data->width + 7) / 8;
3618
3619 if (cbAndMaskScan & 1)
3620 {
3621 /* Original AND mask is not word aligned. */
3622
3623 /* Allocate memory for aligned AND mask. */
[28205]3624 pu8AndMaskWordAligned = (uint8_t *)RTMemTmpAllocZ((cbAndMaskScan + 1) * data->height);
[1]3625
3626 Assert(pu8AndMaskWordAligned);
3627
3628 if (pu8AndMaskWordAligned)
3629 {
3630 /* According to MSDN the padding bits must be 0.
3631 * Compute the bit mask to set padding bits to 0 in the last byte of original AND mask.
3632 */
3633 uint32_t u32PaddingBits = cbAndMaskScan * 8 - data->width;
3634 Assert(u32PaddingBits < 8);
3635 uint8_t u8LastBytesPaddingMask = (uint8_t)(0xFF << u32PaddingBits);
3636
3637 Log(("u8LastBytesPaddingMask = %02X, aligned w = %d, width = %d, cbAndMaskScan = %d\n",
3638 u8LastBytesPaddingMask, (cbAndMaskScan + 1) * 8, data->width, cbAndMaskScan));
3639
3640 uint8_t *src = (uint8_t *)srcAndMaskPtr;
3641 uint8_t *dst = pu8AndMaskWordAligned;
3642
3643 unsigned i;
3644 for (i = 0; i < data->height; i++)
3645 {
[28205]3646 memcpy(dst, src, cbAndMaskScan);
[1]3647
3648 dst[cbAndMaskScan - 1] &= u8LastBytesPaddingMask;
3649
3650 src += cbAndMaskScan;
3651 dst += cbAndMaskScan + 1;
3652 }
3653 }
3654 }
3655
3656 // create the AND mask bitmap
[28205]3657 hMonoBitmap = ::CreateBitmap(data->width, data->height, 1, 1,
3658 pu8AndMaskWordAligned? pu8AndMaskWordAligned: srcAndMaskPtr);
[1]3659
3660 if (pu8AndMaskWordAligned)
3661 {
[28205]3662 RTMemTmpFree(pu8AndMaskWordAligned);
[1]3663 }
3664 }
3665
[28205]3666 Assert(hBitmap);
3667 Assert(hMonoBitmap);
[1]3668 if (hBitmap && hMonoBitmap)
3669 {
[28205]3670 DWORD *dstShapePtr = (DWORD *)lpBits;
[1]3671
3672 for (uint32_t y = 0; y < data->height; y ++)
3673 {
[28205]3674 memcpy(dstShapePtr, srcShapePtr, srcShapePtrScan);
[1]3675 srcShapePtr += srcShapePtrScan;
3676 dstShapePtr += data->width;
3677 }
3678 }
3679
3680 if (hMonoBitmap)
[28205]3681 ::DeleteObject(hMonoBitmap);
[1]3682 if (hBitmap)
[28205]3683 ::DeleteObject(hBitmap);
[1]3684
[28205]3685#elif defined(VBOXSDL_WITH_X11) && !defined(VBOX_WITHOUT_XCURSOR)
[1]3686
[18793]3687 if (gfXCursorEnabled)
[1]3688 {
[28205]3689 XcursorImage *img = XcursorImageCreate(data->width, data->height);
3690 Assert(img);
[18793]3691 if (img)
3692 {
3693 img->xhot = data->xHot;
3694 img->yhot = data->yHot;
[1]3695
[18793]3696 XcursorPixel *dstShapePtr = img->pixels;
[1]3697
[18793]3698 for (uint32_t y = 0; y < data->height; y ++)
3699 {
[28205]3700 memcpy(dstShapePtr, srcShapePtr, srcShapePtrScan);
[1]3701
[18793]3702 if (!data->alpha)
[1]3703 {
[18793]3704 // convert AND mask to the alpha channel
3705 uint8_t byte = 0;
3706 for (uint32_t x = 0; x < data->width; x ++)
[1]3707 {
[18793]3708 if (!(x % 8))
3709 byte = *(srcAndMaskPtr ++);
[1]3710 else
[18793]3711 byte <<= 1;
3712
3713 if (byte & 0x80)
3714 {
3715 // Linux doesn't support inverted pixels (XOR ops,
3716 // to be exact) in cursor shapes, so we detect such
3717 // pixels and always replace them with black ones to
3718 // make them visible at least over light colors
3719 if (dstShapePtr [x] & 0x00FFFFFF)
3720 dstShapePtr [x] = 0xFF000000;
3721 else
3722 dstShapePtr [x] = 0x00000000;
3723 }
3724 else
3725 dstShapePtr [x] |= 0xFF000000;
[1]3726 }
3727 }
[18793]3728
3729 srcShapePtr += srcShapePtrScan;
3730 dstShapePtr += data->width;
[1]3731 }
3732 }
[28205]3733 XcursorImageDestroy(img);
[1]3734 }
3735
[18793]3736#endif /* VBOXSDL_WITH_X11 && !VBOX_WITHOUT_XCURSOR */
[1]3737
3738 if (!ok)
3739 {
[28205]3740 SDL_SetCursor(gpDefaultCursor);
3741 SDL_ShowCursor(SDL_ENABLE);
[1]3742 }
3743 }
3744 else
3745 {
3746 if (data->visible)
[28205]3747 SDL_ShowCursor(SDL_ENABLE);
[1]3748 else if (gfAbsoluteMouseGuest)
3749 /* Don't disable the cursor if the guest additions are not active (anymore) */
[28205]3750 SDL_ShowCursor(SDL_DISABLE);
[1]3751 }
3752}
3753
3754/**
3755 * Handle changed mouse capabilities
3756 */
3757static void HandleGuestCapsChanged(void)
3758{
3759 if (!gfAbsoluteMouseGuest)
3760 {
3761 // Cursor could be overwritten by the guest tools
3762 SDL_SetCursor(gpDefaultCursor);
[28205]3763 SDL_ShowCursor(SDL_ENABLE);
[1]3764 gpOffCursor = NULL;
3765 }
[34645]3766 if (gpMouse && UseAbsoluteMouse())
[1]3767 {
3768 // Actually switch to absolute coordinates
3769 if (gfGrabbed)
3770 InputGrabEnd();
[34645]3771 gpMouse->PutMouseEventAbsolute(-1, -1, 0, 0, 0);
[1]3772 }
3773}
3774
3775/**
3776 * Handles a host key down event
3777 */
3778static int HandleHostKey(const SDL_KeyboardEvent *pEv)
3779{
3780 /*
3781 * Revalidate the host key modifier
3782 */
3783 if ((SDL_GetModState() & ~(KMOD_MODE | KMOD_NUM | KMOD_RESERVED)) != gHostKeyMod)
3784 return VERR_NOT_SUPPORTED;
3785
3786 /*
3787 * What was pressed?
3788 */
3789 switch (pEv->keysym.sym)
3790 {
3791 /* Control-Alt-Delete */
3792 case SDLK_DELETE:
3793 {
[34645]3794 gpKeyboard->PutCAD();
[1]3795 break;
3796 }
3797
3798 /*
3799 * Fullscreen / Windowed toggle.
3800 */
3801 case SDLK_f:
3802 {
[10430]3803 if ( strchr(gHostKeyDisabledCombinations, 'f')
3804 || !gfAllowFullscreenToggle)
3805 return VERR_NOT_SUPPORTED;
[1]3806
[10430]3807 /*
3808 * We have to pause/resume the machine during this
3809 * process because there might be a short moment
3810 * without a valid framebuffer
3811 */
3812 MachineState_T machineState;
[34645]3813 gpMachine->COMGETTER(State)(&machineState);
[24301]3814 bool fPauseIt = machineState == MachineState_Running
3815 || machineState == MachineState_Teleporting
3816 || machineState == MachineState_LiveSnapshotting;
3817 if (fPauseIt)
[34645]3818 gpConsole->Pause();
[20433]3819 SetFullscreen(!gpFramebuffer[0]->getFullscreen());
[24301]3820 if (fPauseIt)
[34645]3821 gpConsole->Resume();
[10430]3822
3823 /*
3824 * We have switched from/to fullscreen, so request a full
3825 * screen repaint, just to be sure.
3826 */
[34645]3827 gpDisplay->InvalidateAndUpdate();
[1]3828 break;
3829 }
3830
3831 /*
3832 * Pause / Resume toggle.
3833 */
3834 case SDLK_p:
3835 {
[10430]3836 if (strchr(gHostKeyDisabledCombinations, 'p'))
3837 return VERR_NOT_SUPPORTED;
3838
[1]3839 MachineState_T machineState;
[34645]3840 gpMachine->COMGETTER(State)(&machineState);
[24301]3841 if ( machineState == MachineState_Running
3842 || machineState == MachineState_Teleporting
3843 || machineState == MachineState_LiveSnapshotting
3844 )
[1]3845 {
3846 if (gfGrabbed)
3847 InputGrabEnd();
[34645]3848 gpConsole->Pause();
[1]3849 }
3850 else if (machineState == MachineState_Paused)
3851 {
[34645]3852 gpConsole->Resume();
[1]3853 }
3854 UpdateTitlebar(TITLEBAR_NORMAL);
3855 break;
3856 }
3857
3858 /*
3859 * Reset the VM
3860 */
3861 case SDLK_r:
3862 {
[10430]3863 if (strchr(gHostKeyDisabledCombinations, 'r'))
3864 return VERR_NOT_SUPPORTED;
3865
[1]3866 ResetVM();
3867 break;
3868 }
3869
3870 /*
3871 * Terminate the VM
3872 */
3873 case SDLK_q:
3874 {
[10430]3875 if (strchr(gHostKeyDisabledCombinations, 'q'))
3876 return VERR_NOT_SUPPORTED;
3877
[1]3878 return VINF_EM_TERMINATE;
3879 }
3880
3881 /*
3882 * Save the machine's state and exit
3883 */
3884 case SDLK_s:
3885 {
[10430]3886 if (strchr(gHostKeyDisabledCombinations, 's'))
3887 return VERR_NOT_SUPPORTED;
3888
[1]3889 SaveState();
3890 return VINF_EM_TERMINATE;
3891 }
3892
3893 case SDLK_h:
3894 {
[10430]3895 if (strchr(gHostKeyDisabledCombinations, 'h'))
3896 return VERR_NOT_SUPPORTED;
3897
[34645]3898 if (gpConsole)
3899 gpConsole->PowerButton();
[1]3900 break;
3901 }
3902
3903 /*
3904 * Perform an online snapshot. Continue operation.
3905 */
3906 case SDLK_n:
3907 {
[10430]3908 if (strchr(gHostKeyDisabledCombinations, 'n'))
3909 return VERR_NOT_SUPPORTED;
3910
[1]3911 RTThreadYield();
3912 ULONG cSnapshots = 0;
[34645]3913 gpMachine->COMGETTER(SnapshotCount)(&cSnapshots);
[1]3914 char pszSnapshotName[20];
3915 RTStrPrintf(pszSnapshotName, sizeof(pszSnapshotName), "Snapshot %d", cSnapshots + 1);
[34645]3916 gpProgress = NULL;
[95141]3917 HRESULT hrc;
[55977]3918 Bstr snapId;
[55214]3919 CHECK_ERROR(gpMachine, TakeSnapshot(Bstr(pszSnapshotName).raw(),
[34645]3920 Bstr("Taken by VBoxSDL").raw(),
[55977]3921 TRUE, snapId.asOutParam(),
[34645]3922 gpProgress.asOutParam()));
[95141]3923 if (FAILED(hrc))
[1]3924 {
[95141]3925 RTPrintf("Error taking snapshot! rc=%Rhrc\n", hrc);
[1]3926 /* continue operation */
3927 return VINF_SUCCESS;
3928 }
3929 /*
3930 * Wait for the operation to be completed and work
3931 * the title bar in the mean while.
3932 */
[18274]3933 ULONG cPercent = 0;
[1]3934 for (;;)
3935 {
3936 BOOL fCompleted = false;
[95141]3937 hrc = gpProgress->COMGETTER(Completed)(&fCompleted);
3938 if (FAILED(hrc) || fCompleted)
[1]3939 break;
[18274]3940 ULONG cPercentNow;
[95141]3941 hrc = gpProgress->COMGETTER(Percent)(&cPercentNow);
3942 if (FAILED(hrc))
[1]3943 break;
3944 if (cPercentNow != cPercent)
3945 {
3946 UpdateTitlebar(TITLEBAR_SNAPSHOT, cPercent);
3947 cPercent = cPercentNow;
3948 }
3949
3950 /* wait */
[95141]3951 hrc = gpProgress->WaitForCompletion(100);
3952 if (FAILED(hrc))
[1]3953 break;
3954 /// @todo process gui events.
3955 }
3956
3957 /* continue operation */
3958 return VINF_SUCCESS;
3959 }
3960
3961 case SDLK_F1: case SDLK_F2: case SDLK_F3:
3962 case SDLK_F4: case SDLK_F5: case SDLK_F6:
3963 case SDLK_F7: case SDLK_F8: case SDLK_F9:
3964 case SDLK_F10: case SDLK_F11: case SDLK_F12:
3965 {
[98392]3966 /* send Ctrl-Alt-Fx to guest */
3967 gpKeyboard->PutUsageCode(0xE0 /*left ctrl*/, 0x07 /*usage code page id*/, FALSE);
3968 gpKeyboard->PutUsageCode(0xE2 /*left alt*/, 0x07 /*usage code page id*/, FALSE);
3969 gpKeyboard->PutUsageCode(pEv->keysym.sym, 0x07 /*usage code page id*/, FALSE);
3970 gpKeyboard->PutUsageCode(pEv->keysym.sym, 0x07 /*usage code page id*/, TRUE);
3971 gpKeyboard->PutUsageCode(0xE0 /*left ctrl*/, 0x07 /*usage code page id*/, TRUE);
3972 gpKeyboard->PutUsageCode(0xE2 /*left alt*/, 0x07 /*usage code page id*/, TRUE);
[1]3973 return VINF_SUCCESS;
3974 }
3975
3976 /*
3977 * Not a host key combination.
3978 * Indicate this by returning false.
3979 */
3980 default:
3981 return VERR_NOT_SUPPORTED;
3982 }
3983
3984 return VINF_SUCCESS;
3985}
3986
3987/**
3988 * Timer callback function for startup processing
3989 */
[85121]3990static Uint32 StartupTimer(Uint32 interval, void *param) RT_NOTHROW_DEF
[1]3991{
[63298]3992 RT_NOREF(param);
3993
[1]3994 /* post message so we can do something in the startup loop */
3995 SDL_Event event = {0};
3996 event.type = SDL_USEREVENT;
3997 event.user.type = SDL_USER_EVENT_TIMER;
3998 SDL_PushEvent(&event);
[3289]3999 RTSemEventSignal(g_EventSemSDLEvents);
[1]4000 return interval;
4001}
[522]4002
4003/**
4004 * Timer callback function to check if resizing is finished
4005 */
[85121]4006static Uint32 ResizeTimer(Uint32 interval, void *param) RT_NOTHROW_DEF
[522]4007{
[63298]4008 RT_NOREF(interval, param);
4009
[522]4010 /* post message so the window is actually resized */
4011 SDL_Event event = {0};
4012 event.type = SDL_USEREVENT;
4013 event.user.type = SDL_USER_EVENT_WINDOW_RESIZE_DONE;
[3289]4014 PushSDLEventForSure(&event);
[522]4015 /* one-shot */
4016 return 0;
4017}
[3289]4018
4019/**
[6852]4020 * Timer callback function to check if an ACPI power button event was handled by the guest.
4021 */
[85121]4022static Uint32 QuitTimer(Uint32 interval, void *param) RT_NOTHROW_DEF
[6852]4023{
[63298]4024 RT_NOREF(interval, param);
4025
[6853]4026 BOOL fHandled = FALSE;
[6852]4027
[98301]4028 gSdlQuitTimer = 0;
[34645]4029 if (gpConsole)
[6852]4030 {
[34645]4031 int rc = gpConsole->GetPowerButtonHandled(&fHandled);
[6852]4032 LogRel(("QuitTimer: rc=%d handled=%d\n", rc, fHandled));
[13835]4033 if (RT_FAILURE(rc) || !fHandled)
[6852]4034 {
4035 /* event was not handled, power down the guest */
4036 gfACPITerm = FALSE;
4037 SDL_Event event = {0};
4038 event.type = SDL_QUIT;
4039 PushSDLEventForSure(&event);
4040 }
4041 }
4042 /* one-shot */
4043 return 0;
4044}
4045
4046/**
[3289]4047 * Wait for the next SDL event. Don't use SDL_WaitEvent since this function
4048 * calls SDL_Delay(10) if the event queue is empty.
4049 */
4050static int WaitSDLEvent(SDL_Event *event)
4051{
4052 for (;;)
4053 {
[28205]4054 int rc = SDL_PollEvent(event);
[3289]4055 if (rc == 1)
[3300]4056 {
4057#ifdef USE_XPCOM_QUEUE_THREAD
4058 if (event->type == SDL_USER_EVENT_XPCOM_EVENTQUEUE)
4059 consumedXPCOMUserEvent();
4060#endif
[3289]4061 return 1;
[3300]4062 }
[3289]4063 /* Immediately wake up if new SDL events are available. This does not
4064 * work for internal SDL events. Don't wait more than 10ms. */
4065 RTSemEventWait(g_EventSemSDLEvents, 10);
4066 }
4067}
4068
4069/**
4070 * Ensure that an SDL event is really enqueued. Try multiple times if necessary.
4071 */
4072int PushSDLEventForSure(SDL_Event *event)
4073{
4074 int ntries = 10;
4075 for (; ntries > 0; ntries--)
4076 {
4077 int rc = SDL_PushEvent(event);
4078 RTSemEventSignal(g_EventSemSDLEvents);
[20433]4079 if (rc == 1)
[3289]4080 return 0;
[20433]4081 Log(("PushSDLEventForSure: waiting for 2ms (rc = %d)\n", rc));
[3289]4082 RTThreadSleep(2);
4083 }
4084 LogRel(("WARNING: Failed to enqueue SDL event %d.%d!\n",
[34645]4085 event->type, event->type == SDL_USEREVENT ? event->user.type : 0));
[3289]4086 return -1;
4087}
4088
[99494]4089#if defined(VBOXSDL_WITH_X11) || defined(RT_OS_DARWIN)
[3289]4090/**
4091 * Special SDL_PushEvent function for NotifyUpdate events. These events may occur in bursts
4092 * so make sure they don't flood the SDL event queue.
4093 */
4094void PushNotifyUpdateEvent(SDL_Event *event)
4095{
4096 int rc = SDL_PushEvent(event);
[20433]4097 bool fSuccess = (rc == 1);
4098
[3289]4099 RTSemEventSignal(g_EventSemSDLEvents);
[20433]4100 AssertMsg(fSuccess, ("SDL_PushEvent returned SDL error\n"));
[3289]4101 /* A global counter is faster than SDL_PeepEvents() */
[20433]4102 if (fSuccess)
[3289]4103 ASMAtomicIncS32(&g_cNotifyUpdateEventsPending);
4104 /* In order to not flood the SDL event queue, yield the CPU or (if there are already many
4105 * events queued) even sleep */
4106 if (g_cNotifyUpdateEventsPending > 96)
4107 {
4108 /* Too many NotifyUpdate events, sleep for a small amount to give the main thread time
4109 * to handle these events. The SDL queue can hold up to 128 events. */
4110 Log(("PushNotifyUpdateEvent: Sleep 1ms\n"));
4111 RTThreadSleep(1);
4112 }
4113 else
4114 RTThreadYield();
4115}
[4637]4116#endif /* VBOXSDL_WITH_X11 */
[10675]4117
4118/**
4119 *
4120 */
4121static void SetFullscreen(bool enable)
4122{
[20433]4123 if (enable == gpFramebuffer[0]->getFullscreen())
[10675]4124 return;
4125
4126 if (!gfFullscreenResize)
4127 {
4128 /*
[11400]4129 * The old/default way: SDL will resize the host to fit the guest screen resolution.
[10675]4130 */
[20433]4131 gpFramebuffer[0]->setFullscreen(enable);
[10675]4132 }
4133 else
4134 {
4135 /*
[11400]4136 * The alternate way: Switch to fullscreen with the host screen resolution and adapt
4137 * the guest screen resolution to the host window geometry.
[10675]4138 */
4139 uint32_t NewWidth = 0, NewHeight = 0;
[11400]4140 if (enable)
[10675]4141 {
[11400]4142 /* switch to fullscreen */
[20433]4143 gmGuestNormalXRes = gpFramebuffer[0]->getGuestXRes();
4144 gmGuestNormalYRes = gpFramebuffer[0]->getGuestYRes();
4145 gpFramebuffer[0]->getFullscreenGeometry(&NewWidth, &NewHeight);
[10675]4146 }
4147 else
4148 {
[11400]4149 /* switch back to saved geometry */
[10675]4150 NewWidth = gmGuestNormalXRes;
4151 NewHeight = gmGuestNormalYRes;
4152 }
4153 if (NewWidth != 0 && NewHeight != 0)
4154 {
[20433]4155 gpFramebuffer[0]->setFullscreen(enable);
[10675]4156 gfIgnoreNextResize = TRUE;
[42248]4157 gpDisplay->SetVideoModeHint(0 /*=display*/, true /*=enabled*/,
4158 false /*=changeOrigin*/, 0 /*=originX*/, 0 /*=originY*/,
[78995]4159 NewWidth, NewHeight, 0 /*don't change bpp*/, true /*=notify*/);
[10675]4160 }
4161 }
4162}
[20433]4163
[81537]4164static VBoxSDLFB *getFbFromWinId(Uint32 id)
[20433]4165{
4166 for (unsigned i = 0; i < gcMonitors; i++)
4167 if (gpFramebuffer[i]->hasWindow(id))
4168 return gpFramebuffer[i];
4169
4170 return NULL;
4171}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use