VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/VBoxConsoleView.cpp@ 26729

Last change on this file since 26729 was 26729, checked in by vboxsync, 15 years ago

FE/Qt4-OSX: cleanup

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 146.1 KB
Line 
1/* $Id: VBoxConsoleView.cpp 26729 2010-02-24 10:46:58Z vboxsync $ */
2/** @file
3 *
4 * VBox frontends: Qt GUI ("VirtualBox"):
5 * VBoxConsoleView class implementation
6 */
7
8/*
9 * Copyright (C) 22006-2007 Sun Microsystems, Inc.
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
20 * Clara, CA 95054 USA or visit http://www.sun.com if you need
21 * additional information or have any questions.
22 */
23
24#ifdef VBOX_WITH_PRECOMPILED_HEADERS
25# include "precomp.h"
26#else /* !VBOX_WITH_PRECOMPILED_HEADERS */
27#include <VBox/VBoxVideo.h>
28
29#include "VBoxConsoleView.h"
30#include "VBoxConsoleWnd.h"
31#include "VBoxUtils.h"
32
33#include "VBoxFrameBuffer.h"
34#include "VBoxGlobal.h"
35#include "VBoxProblemReporter.h"
36
37#ifdef Q_WS_PM
38#include "QIHotKeyEdit.h"
39#endif
40
41/* Qt includes */
42#include <QMenuBar>
43#include <QDesktopWidget>
44#include <QTimer>
45#include <QStatusBar>
46#include <QPainter>
47#include <QBitmap>
48
49#endif /* !VBOX_WITH_PRECOMPILED_HEADERS */ /** @todo Move this further down! Requires some cleanup below though, so later. */
50
51#ifdef Q_WS_WIN
52// VBox/cdefs.h defines these:
53#undef LOWORD
54#undef HIWORD
55#undef LOBYTE
56#undef HIBYTE
57#include <windows.h>
58#endif
59
60#ifdef Q_WS_X11
61#include <QX11Info>
62// We need to capture some X11 events directly which
63// requires the XEvent structure to be defined. However,
64// including the Xlib header file will cause some nasty
65// conflicts with Qt. Therefore we use the following hack
66// to redefine those conflicting identifiers.
67#define XK_XKB_KEYS
68#define XK_MISCELLANY
69#include <X11/Xlib.h>
70#include <X11/Xutil.h>
71#include <X11/XKBlib.h>
72#include <X11/keysym.h>
73#ifdef KeyPress
74const int XFocusOut = FocusOut;
75const int XFocusIn = FocusIn;
76const int XKeyPress = KeyPress;
77const int XKeyRelease = KeyRelease;
78#undef KeyRelease
79#undef KeyPress
80#undef FocusOut
81#undef FocusIn
82#endif
83#include "XKeyboard.h"
84#ifndef VBOX_WITHOUT_XCURSOR
85# include <X11/Xcursor/Xcursor.h>
86#endif
87#endif // Q_WS_X11
88
89#if defined (Q_WS_MAC)
90# include "DockIconPreview.h"
91# include "DarwinKeyboard.h"
92# ifdef QT_MAC_USE_COCOA
93# include "darwin/VBoxCocoaApplication.h"
94# else /* QT_MAC_USE_COCOA */
95# include <Carbon/Carbon.h>
96# endif /* !QT_MAC_USE_COCOA */
97# include <VBox/err.h>
98#endif /* defined (Q_WS_MAC) */
99
100#if defined (Q_WS_WIN32)
101
102static HHOOK gKbdHook = NULL;
103static VBoxConsoleView *gView = 0;
104
105LRESULT CALLBACK VBoxConsoleView::lowLevelKeyboardProc (int nCode,
106 WPARAM wParam, LPARAM lParam)
107{
108 Assert (gView);
109 if (gView && nCode == HC_ACTION &&
110 gView->winLowKeyboardEvent (wParam, *(KBDLLHOOKSTRUCT *) lParam))
111 return 1;
112
113 return CallNextHookEx (NULL, nCode, wParam, lParam);
114}
115
116#endif
117
118#if defined (Q_WS_MAC)
119# if defined (QT_MAC_USE_COCOA)
120/**
121 * Event handler callback for Mac OS X, Cocoa variant.
122 *
123 * (Registered with and called from VBoxCocoaApplication.)
124 *
125 * @returns true if the event should be dropped, false if it should be passed
126 * along.
127 * @param pvCocoaEvent The Cocoa event object.
128 * @param pvCarbonEvent The Carbon event object reference.
129 * @param pvUser The user argument.
130 */
131/* static */
132bool VBoxConsoleView::darwinEventHandlerProc (const void *pvCocoaEvent,
133 const void *pvCarbonEvent,
134 void *pvUser)
135{
136 VBoxConsoleView *view = (VBoxConsoleView *)pvUser;
137 EventRef inEvent = (EventRef)pvCarbonEvent;
138 UInt32 eventClass = ::GetEventClass (inEvent);
139
140#if 0
141 /* For debugging events. */
142 if (eventClass != 'cute')
143 ::VBoxCocoaApplication_printEvent ("view: ", pvCocoaEvent);
144#endif
145 /* Check if this is an application key combo. In that case we will not pass
146 the event to the guest, but let the host process it. */
147 if (VBoxCocoaApplication_isApplicationCommand(pvCocoaEvent))
148 return false;
149
150 /*
151 * All keyboard class events needs to be handled.
152 */
153 if (eventClass == kEventClassKeyboard)
154 {
155 if (view->darwinKeyboardEvent (pvCocoaEvent, inEvent))
156 return true;
157 }
158 /* Pass the event along. */
159 return false;
160}
161
162# else /* QT_MAC_USE_COCOA */
163/**
164 * Event handler callback for Mac OS X.
165 */
166/* static */
167pascal OSStatus VBoxConsoleView::darwinEventHandlerProc (EventHandlerCallRef inHandlerCallRef,
168 EventRef inEvent, void *inUserData)
169{
170 VBoxConsoleView *view = static_cast<VBoxConsoleView *> (inUserData);
171 UInt32 eventClass = ::GetEventClass (inEvent);
172
173 /* For debugging events */
174 /*
175 if (eventClass != 'cute')
176 ::darwinDebugPrintEvent ("view: ", inEvent);
177 */
178
179 /* Not sure but this seems an triggered event if the spotlight searchbar is
180 * displayed. So flag that the host key isn't pressed alone. */
181 if ( eventClass == 'cgs '
182 && view->mIsHostkeyPressed
183 && ::GetEventKind (inEvent) == 0x15)
184 view->mIsHostkeyAlone = false;
185
186 if (eventClass == kEventClassKeyboard)
187 {
188 if (view->darwinKeyboardEvent (NULL, inEvent))
189 return 0;
190 }
191 /*
192 * Command-H and Command-Q aren't properly disabled yet, and it's still
193 * possible to use the left command key to invoke them when the keyboard
194 * is captured. We discard the events these if the keyboard is captured
195 * as a half measure to prevent unexpected behavior. However, we don't
196 * get any key down/up events, so these combinations are dead to the guest...
197 */
198 else if (eventClass == kEventClassCommand)
199 {
200 if (view->mKbdCaptured)
201 return 0;
202 }
203 return ::CallNextEventHandler (inHandlerCallRef, inEvent);
204}
205# endif /* !QT_MAC_USE_COCOA */
206
207#endif /* Q_WS_MAC */
208
209/** Guest mouse pointer shape change event. */
210class MousePointerChangeEvent : public QEvent
211{
212public:
213 MousePointerChangeEvent (bool visible, bool alpha, uint xhot, uint yhot,
214 uint width, uint height,
215 const uchar *shape) :
216 QEvent ((QEvent::Type) VBoxDefs::MousePointerChangeEventType),
217 vis (visible), alph (alpha), xh (xhot), yh (yhot), w (width), h (height),
218 data (NULL)
219 {
220 // make a copy of shape
221 uint dataSize = ((((width + 7) / 8 * height) + 3) & ~3) + width * 4 * height;
222
223 if (shape) {
224 data = new uchar [dataSize];
225 memcpy ((void *) data, (void *) shape, dataSize);
226 }
227 }
228 ~MousePointerChangeEvent()
229 {
230 if (data) delete[] data;
231 }
232 bool isVisible() const { return vis; }
233 bool hasAlpha() const { return alph; }
234 uint xHot() const { return xh; }
235 uint yHot() const { return yh; }
236 uint width() const { return w; }
237 uint height() const { return h; }
238 const uchar *shapeData() const { return data; }
239private:
240 bool vis, alph;
241 uint xh, yh, w, h;
242 const uchar *data;
243};
244
245/** Guest mouse absolute positioning capability change event. */
246class MouseCapabilityEvent : public QEvent
247{
248public:
249 MouseCapabilityEvent (bool supportsAbsolute, bool needsHostCursor) :
250 QEvent ((QEvent::Type) VBoxDefs::MouseCapabilityEventType),
251 can_abs (supportsAbsolute),
252 needs_host_cursor (needsHostCursor) {}
253 bool supportsAbsolute() const { return can_abs; }
254 bool needsHostCursor() const { return needs_host_cursor; }
255private:
256 bool can_abs;
257 bool needs_host_cursor;
258};
259
260/** Machine state change. */
261class StateChangeEvent : public QEvent
262{
263public:
264 StateChangeEvent (KMachineState state) :
265 QEvent ((QEvent::Type) VBoxDefs::MachineStateChangeEventType),
266 s (state) {}
267 KMachineState machineState() const { return s; }
268private:
269 KMachineState s;
270};
271
272/** Guest Additions property changes. */
273class GuestAdditionsEvent : public QEvent
274{
275public:
276 GuestAdditionsEvent (const QString &aOsTypeId,
277 const QString &aAddVersion,
278 bool aAddActive,
279 bool aSupportsSeamless,
280 bool aSupportsGraphics) :
281 QEvent ((QEvent::Type) VBoxDefs::AdditionsStateChangeEventType),
282 mOsTypeId (aOsTypeId), mAddVersion (aAddVersion),
283 mAddActive (aAddActive), mSupportsSeamless (aSupportsSeamless),
284 mSupportsGraphics (aSupportsGraphics) {}
285 const QString &osTypeId() const { return mOsTypeId; }
286 const QString &additionVersion() const { return mAddVersion; }
287 bool additionActive() const { return mAddActive; }
288 bool supportsSeamless() const { return mSupportsSeamless; }
289 bool supportsGraphics() const { return mSupportsGraphics; }
290private:
291 QString mOsTypeId;
292 QString mAddVersion;
293 bool mAddActive;
294 bool mSupportsSeamless;
295 bool mSupportsGraphics;
296};
297
298/** DVD/Floppy drive change event */
299class MediaDriveChangeEvent : public QEvent
300{
301public:
302 MediaDriveChangeEvent (VBoxDefs::MediumType aType)
303 : QEvent ((QEvent::Type) VBoxDefs::MediaDriveChangeEventType)
304 , mType (aType) {}
305 VBoxDefs::MediumType type() const { return mType; }
306private:
307 VBoxDefs::MediumType mType;
308};
309
310/** Menu activation event */
311class ActivateMenuEvent : public QEvent
312{
313public:
314 ActivateMenuEvent (QAction *aData) :
315 QEvent ((QEvent::Type) VBoxDefs::ActivateMenuEventType),
316 mAction (aData) {}
317 QAction *action() const { return mAction; }
318private:
319 QAction *mAction;
320};
321
322/** VM Runtime error event */
323class RuntimeErrorEvent : public QEvent
324{
325public:
326 RuntimeErrorEvent (bool aFatal, const QString &aErrorID,
327 const QString &aMessage) :
328 QEvent ((QEvent::Type) VBoxDefs::RuntimeErrorEventType),
329 mFatal (aFatal), mErrorID (aErrorID), mMessage (aMessage) {}
330 bool fatal() const { return mFatal; }
331 QString errorID() const { return mErrorID; }
332 QString message() const { return mMessage; }
333private:
334 bool mFatal;
335 QString mErrorID;
336 QString mMessage;
337};
338
339/** Modifier key change event */
340class ModifierKeyChangeEvent : public QEvent
341{
342public:
343 ModifierKeyChangeEvent (bool fNumLock, bool fCapsLock, bool fScrollLock) :
344 QEvent ((QEvent::Type) VBoxDefs::ModifierKeyChangeEventType),
345 mNumLock (fNumLock), mCapsLock (fCapsLock), mScrollLock (fScrollLock) {}
346 bool numLock() const { return mNumLock; }
347 bool capsLock() const { return mCapsLock; }
348 bool scrollLock() const { return mScrollLock; }
349private:
350 bool mNumLock, mCapsLock, mScrollLock;
351};
352
353/** Network adapter change event */
354class NetworkAdapterChangeEvent : public QEvent
355{
356public:
357 NetworkAdapterChangeEvent (INetworkAdapter *aAdapter) :
358 QEvent ((QEvent::Type) VBoxDefs::NetworkAdapterChangeEventType),
359 mAdapter (aAdapter) {}
360 INetworkAdapter* networkAdapter() { return mAdapter; }
361private:
362 INetworkAdapter *mAdapter;
363};
364
365/** USB controller state change event */
366class USBControllerStateChangeEvent : public QEvent
367{
368public:
369 USBControllerStateChangeEvent()
370 : QEvent ((QEvent::Type) VBoxDefs::USBCtlStateChangeEventType) {}
371};
372
373/** USB device state change event */
374class USBDeviceStateChangeEvent : public QEvent
375{
376public:
377 USBDeviceStateChangeEvent (const CUSBDevice &aDevice, bool aAttached,
378 const CVirtualBoxErrorInfo &aError) :
379 QEvent ((QEvent::Type) VBoxDefs::USBDeviceStateChangeEventType),
380 mDevice (aDevice), mAttached (aAttached), mError (aError) {}
381 CUSBDevice device() const { return mDevice; }
382 bool attached() const { return mAttached; }
383 CVirtualBoxErrorInfo error() const { return mError; }
384private:
385 CUSBDevice mDevice;
386 bool mAttached;
387 CVirtualBoxErrorInfo mError;
388};
389
390//
391// VBoxConsoleCallback class
392/////////////////////////////////////////////////////////////////////////////
393
394class VBoxConsoleCallback : VBOX_SCRIPTABLE_IMPL(IConsoleCallback)
395{
396public:
397
398 VBoxConsoleCallback (VBoxConsoleView *v) {
399#if defined (Q_WS_WIN)
400 mRefCnt = 0;
401#endif
402 mView = v;
403 }
404
405 virtual ~VBoxConsoleCallback() {}
406
407 NS_DECL_ISUPPORTS
408
409#if defined (Q_WS_WIN)
410 STDMETHOD_(ULONG, AddRef)() {
411 return ::InterlockedIncrement (&mRefCnt);
412 }
413 STDMETHOD_(ULONG, Release)()
414 {
415 long cnt = ::InterlockedDecrement (&mRefCnt);
416 if (cnt == 0)
417 delete this;
418 return cnt;
419 }
420#endif
421 VBOX_SCRIPTABLE_DISPATCH_IMPL(IConsoleCallback)
422
423 STDMETHOD(OnMousePointerShapeChange) (BOOL visible, BOOL alpha,
424 ULONG xhot, ULONG yhot,
425 ULONG width, ULONG height,
426 BYTE *shape)
427 {
428 QApplication::postEvent (mView,
429 new MousePointerChangeEvent (visible, alpha,
430 xhot, yhot,
431 width, height, shape));
432 return S_OK;
433 }
434
435 STDMETHOD(OnMouseCapabilityChange)(BOOL supportsAbsolute, BOOL needsHostCursor)
436 {
437 QApplication::postEvent (mView,
438 new MouseCapabilityEvent (supportsAbsolute,
439 needsHostCursor));
440 return S_OK;
441 }
442
443 STDMETHOD(OnKeyboardLedsChange)(BOOL fNumLock, BOOL fCapsLock, BOOL fScrollLock)
444 {
445 QApplication::postEvent (mView,
446 new ModifierKeyChangeEvent (fNumLock, fCapsLock,
447 fScrollLock));
448 return S_OK;
449 }
450
451 STDMETHOD(OnStateChange)(MachineState_T machineState)
452 {
453 LogFlowFunc (("machineState=%d\n", machineState));
454 QApplication::postEvent (mView,
455 new StateChangeEvent ((KMachineState) machineState));
456 return S_OK;
457 }
458
459 STDMETHOD(OnAdditionsStateChange)()
460 {
461 CGuest guest = mView->console().GetGuest();
462 LogFlowFunc (("ver=%s, active=%d\n",
463 guest.GetAdditionsVersion().toLatin1().constData(),
464 guest.GetAdditionsActive()));
465 QApplication::postEvent (mView,
466 new GuestAdditionsEvent (
467 guest.GetOSTypeId(),
468 guest.GetAdditionsVersion(),
469 guest.GetAdditionsActive(),
470 guest.GetSupportsSeamless(),
471 guest.GetSupportsGraphics()));
472 return S_OK;
473 }
474
475 STDMETHOD(OnNetworkAdapterChange) (INetworkAdapter *aNetworkAdapter)
476 {
477 QApplication::postEvent (mView,
478 new NetworkAdapterChangeEvent (aNetworkAdapter));
479 return S_OK;
480 }
481
482 STDMETHOD(OnStorageControllerChange) ()
483 {
484 /* @todo */
485 //QApplication::postEvent (mView,
486 // new StorageControllerChangeEvent ());
487 return S_OK;
488 }
489
490 STDMETHOD(OnMediumChange)(IMediumAttachment *aMediumAttachment)
491 {
492 CMediumAttachment att(aMediumAttachment);
493 switch (att.GetType())
494 {
495 case KDeviceType_Floppy:
496 QApplication::postEvent(mView,
497 new MediaDriveChangeEvent(VBoxDefs::MediumType_Floppy));
498 break;
499 case KDeviceType_DVD:
500 QApplication::postEvent(mView,
501 new MediaDriveChangeEvent(VBoxDefs::MediumType_DVD));
502 break;
503 default:
504 /* @todo later add hard disk change as well */
505 break;
506 }
507 return S_OK;
508 }
509
510 STDMETHOD(OnCPUChange)(ULONG aCPU, BOOL aRemove)
511 {
512 NOREF(aCPU);
513 NOREF(aRemove);
514 return S_OK;
515 }
516
517 STDMETHOD(OnSerialPortChange) (ISerialPort *aSerialPort)
518 {
519 NOREF(aSerialPort);
520 return S_OK;
521 }
522
523 STDMETHOD(OnParallelPortChange) (IParallelPort *aParallelPort)
524 {
525 NOREF(aParallelPort);
526 return S_OK;
527 }
528
529 STDMETHOD(OnVRDPServerChange)()
530 {
531 return S_OK;
532 }
533
534 STDMETHOD(OnRemoteDisplayInfoChange)()
535 {
536 return S_OK;
537 }
538
539 STDMETHOD(OnUSBControllerChange)()
540 {
541 QApplication::postEvent (mView,
542 new USBControllerStateChangeEvent());
543 return S_OK;
544 }
545
546 STDMETHOD(OnUSBDeviceStateChange)(IUSBDevice *aDevice, BOOL aAttached,
547 IVirtualBoxErrorInfo *aError)
548 {
549 QApplication::postEvent (mView,
550 new USBDeviceStateChangeEvent (
551 CUSBDevice (aDevice),
552 bool (aAttached),
553 CVirtualBoxErrorInfo (aError)));
554 return S_OK;
555 }
556
557 STDMETHOD(OnSharedFolderChange) (Scope_T aScope)
558 {
559 NOREF(aScope);
560 QApplication::postEvent (mView,
561 new QEvent ((QEvent::Type)
562 VBoxDefs::SharedFolderChangeEventType));
563 return S_OK;
564 }
565
566 STDMETHOD(OnRuntimeError)(BOOL fatal, IN_BSTR id, IN_BSTR message)
567 {
568 QApplication::postEvent (mView,
569 new RuntimeErrorEvent (!!fatal,
570 QString::fromUtf16 (id),
571 QString::fromUtf16 (message)));
572 return S_OK;
573 }
574
575 STDMETHOD(OnCanShowWindow) (BOOL *canShow)
576 {
577 if (!canShow)
578 return E_POINTER;
579
580 /* as long as there is VBoxConsoleView (which creates/destroys us), it
581 * can be shown */
582 *canShow = TRUE;
583 return S_OK;
584 }
585
586 STDMETHOD(OnShowWindow) (ULONG64 *winId)
587 {
588 if (!winId)
589 return E_POINTER;
590
591#if defined (Q_WS_MAC)
592 /*
593 * Let's try the simple approach first - grab the focus.
594 * Getting a window out of the dock (minimized or whatever it's called)
595 * needs to be done on the GUI thread, so post it a note.
596 */
597 *winId = 0;
598 if (!mView)
599 return S_OK;
600
601 ProcessSerialNumber psn = { 0, kCurrentProcess };
602 OSErr rc = ::SetFrontProcess (&psn);
603 if (!rc)
604 QApplication::postEvent (mView, new QEvent ((QEvent::Type)VBoxDefs::ShowWindowEventType));
605 else
606 {
607 /*
608 * It failed for some reason, send the other process our PSN so it can try.
609 * (This is just a precaution should Mac OS X start imposing the same sensible
610 * focus stealing restrictions that other window managers implement.)
611 */
612 AssertMsgFailed(("SetFrontProcess -> %#x\n", rc));
613 if (::GetCurrentProcess (&psn))
614 *winId = RT_MAKE_U64 (psn.lowLongOfPSN, psn.highLongOfPSN);
615 }
616
617#else
618 /* Return the ID of the top-level console window. */
619 *winId = (ULONG64) mView->window()->winId();
620#endif
621
622 return S_OK;
623 }
624
625protected:
626
627 VBoxConsoleView *mView;
628
629#if defined (Q_WS_WIN)
630private:
631 long mRefCnt;
632#endif
633};
634
635#if !defined (Q_WS_WIN)
636NS_DECL_CLASSINFO (VBoxConsoleCallback)
637NS_IMPL_THREADSAFE_ISUPPORTS1_CI (VBoxConsoleCallback, IConsoleCallback)
638#endif
639
640class VBoxViewport: public QWidget
641{
642public:
643 VBoxViewport (QWidget *aParent)
644 : QWidget (aParent)
645 {
646 /* No need for background drawing */
647 setAttribute (Qt::WA_OpaquePaintEvent);
648 }
649 virtual QPaintEngine * paintEngine() const
650 {
651 if (testAttribute (Qt::WA_PaintOnScreen))
652 return NULL;
653 else
654 return QWidget::paintEngine();
655 }
656};
657
658//
659// VBoxConsoleView class
660/////////////////////////////////////////////////////////////////////////////
661
662/** @class VBoxConsoleView
663 *
664 * The VBoxConsoleView class is a widget that implements a console
665 * for the running virtual machine.
666 */
667
668VBoxConsoleView::VBoxConsoleView (VBoxConsoleWnd *mainWnd,
669 const CConsole &console,
670 VBoxDefs::RenderMode rm,
671#ifdef VBOX_WITH_VIDEOHWACCEL
672 bool accelerate2DVideo,
673#endif
674 QWidget *parent)
675 : QAbstractScrollArea (parent)
676 , mMainWnd (mainWnd)
677 , mConsole (console)
678 , gs (vboxGlobal().settings())
679 , mAttached (false)
680 , mKbdCaptured (false)
681 , mMouseCaptured (false)
682 , mMouseAbsolute (false)
683 , mMouseIntegration (true)
684 , m_iLastMouseWheelDelta(0)
685 , mDisableAutoCapture (false)
686 , mIsHostkeyPressed (false)
687 , mIsHostkeyAlone (false)
688 , mIgnoreMainwndResize (true)
689 , mAutoresizeGuest (false)
690 , mIgnoreFrameBufferResize (false)
691 , mIgnoreGuestResize (false)
692 , mDoResize (false)
693 , mGuestSupportsGraphics (false)
694 , mNumLock (false)
695 , mScrollLock (false)
696 , mCapsLock (false)
697 , muNumLockAdaptionCnt (2)
698 , muCapsLockAdaptionCnt (2)
699 , mode (rm)
700#ifdef VBOX_WITH_VIDEOHWACCEL
701 , mAccelerate2DVideo(accelerate2DVideo)
702#endif
703#if defined(Q_WS_WIN)
704 , mAlphaCursor (NULL)
705#endif
706#if defined(Q_WS_MAC)
707# ifndef QT_MAC_USE_COCOA
708 , mDarwinEventHandlerRef (NULL)
709# endif
710 , mDarwinKeyModifiers (0)
711 , mKeyboardGrabbed (false)
712 , mDockIconEnabled (true)
713#endif
714 , mDesktopGeo (DesktopGeo_Invalid)
715 , mPassCAD (false)
716 /* Don't show a hardware pointer until we have one to show */
717 , mHideHostPointer (true)
718{
719 Assert (!mConsole.isNull() &&
720 !mConsole.GetDisplay().isNull() &&
721 !mConsole.GetKeyboard().isNull() &&
722 !mConsole.GetMouse().isNull());
723
724#ifdef Q_WS_MAC
725 /* Overlay logo for the dock icon */
726 QString osTypeId = mConsole.GetGuest().GetOSTypeId();
727 mDockIconPreview = new VBoxDockIconPreview (mMainWnd, vboxGlobal().vmGuestOSTypeIcon (osTypeId));
728
729# ifndef QT_MAC_USE_COCOA
730 /* Install the event handler which will proceed external window handling */
731 EventHandlerUPP eventHandler = ::NewEventHandlerUPP (::darwinOverlayWindowHandler);
732 EventTypeSpec eventTypes[] =
733 {
734 { kEventClassVBox, kEventVBoxShowWindow },
735 { kEventClassVBox, kEventVBoxHideWindow },
736 { kEventClassVBox, kEventVBoxMoveWindow },
737 { kEventClassVBox, kEventVBoxResizeWindow },
738 { kEventClassVBox, kEventVBoxDisposeWindow },
739 { kEventClassVBox, kEventVBoxUpdateDock }
740 };
741
742 mDarwinWindowOverlayHandlerRef = NULL;
743 ::InstallApplicationEventHandler (eventHandler, RT_ELEMENTS (eventTypes), &eventTypes[0],
744 this, &mDarwinWindowOverlayHandlerRef);
745 ::DisposeEventHandlerUPP (eventHandler);
746# endif /* !QT_MAC_USE_COCOA */
747#endif /* QT_WS_MAC */
748
749 /* No frame around the view */
750 setFrameStyle (QFrame::NoFrame);
751
752#ifdef VBOX_GUI_USE_QGL
753 QWidget *pViewport;
754 switch (mode)
755 {
756 case VBoxDefs::QGLMode:
757 pViewport = new VBoxGLWidget (this, this, NULL);
758 break;
759 default:
760 pViewport = new VBoxViewport (this);
761 }
762#else
763 VBoxViewport *pViewport = new VBoxViewport (this);
764#endif
765 setViewport (pViewport);
766// pViewport->vboxDoInit();
767
768 /* enable MouseMove events */
769 viewport()->setMouseTracking (true);
770
771 /*
772 * QScrollView does the below on its own, but let's do it anyway
773 * for the case it will not do it in the future.
774 */
775 viewport()->installEventFilter (this);
776
777 /* to fix some focus issues */
778 mMainWnd->menuBar()->installEventFilter (this);
779
780 /* we want to be notified on some parent's events */
781 mMainWnd->installEventFilter (this);
782
783#ifdef Q_WS_X11
784 /* initialize the X keyboard subsystem */
785 initMappedX11Keyboard(QX11Info::display(),
786 vboxGlobal().settings().publicProperty ("GUI/RemapScancodes"));
787#endif
788
789 ::memset (mPressedKeys, 0, sizeof (mPressedKeys));
790
791 /* setup rendering */
792
793 CDisplay display = mConsole.GetDisplay();
794 Assert (!display.isNull());
795
796 mFrameBuf = NULL;
797
798 LogFlowFunc (("Rendering mode: %d\n", mode));
799
800 switch (mode)
801 {
802#if defined (VBOX_GUI_USE_QGL)
803 case VBoxDefs::QGLMode:
804 mFrameBuf = new VBoxQGLFrameBuffer (this);
805 break;
806// case VBoxDefs::QGLOverlayMode:
807// mFrameBuf = new VBoxQGLOverlayFrameBuffer (this);
808// break;
809#endif
810#if defined (VBOX_GUI_USE_QIMAGE)
811 case VBoxDefs::QImageMode:
812 mFrameBuf =
813#ifdef VBOX_WITH_VIDEOHWACCEL
814 mAccelerate2DVideo ? new VBoxOverlayFrameBuffer<VBoxQImageFrameBuffer> (this, &mainWnd->session()) :
815#endif
816 new VBoxQImageFrameBuffer (this);
817 break;
818#endif
819#if defined (VBOX_GUI_USE_SDL)
820 case VBoxDefs::SDLMode:
821 /* Indicate that we are doing all
822 * drawing stuff ourself */
823 pViewport->setAttribute (Qt::WA_PaintOnScreen);
824# ifdef Q_WS_X11
825 /* This is somehow necessary to prevent strange X11 warnings on
826 * i386 and segfaults on x86_64. */
827 XFlush(QX11Info::display());
828# endif
829 mFrameBuf =
830#if defined(VBOX_WITH_VIDEOHWACCEL) && defined(DEBUG_misha) /* not tested yet */
831 mAccelerate2DVideo ? new VBoxOverlayFrameBuffer<VBoxSDLFrameBuffer> (this, &mainWnd->session()) :
832#endif
833 new VBoxSDLFrameBuffer (this);
834 /*
835 * disable scrollbars because we cannot correctly draw in a
836 * scrolled window using SDL
837 */
838 horizontalScrollBar()->setEnabled (false);
839 verticalScrollBar()->setEnabled (false);
840 break;
841#endif
842#if defined (VBOX_GUI_USE_DDRAW)
843 case VBoxDefs::DDRAWMode:
844 mFrameBuf = new VBoxDDRAWFrameBuffer (this);
845 break;
846#endif
847#if defined (VBOX_GUI_USE_QUARTZ2D)
848 case VBoxDefs::Quartz2DMode:
849 /* Indicate that we are doing all
850 * drawing stuff ourself */
851 pViewport->setAttribute (Qt::WA_PaintOnScreen);
852 mFrameBuf =
853#ifdef VBOX_WITH_VIDEOHWACCEL
854 mAccelerate2DVideo ? new VBoxOverlayFrameBuffer<VBoxQuartz2DFrameBuffer> (this, &mainWnd->session()) :
855#endif
856 new VBoxQuartz2DFrameBuffer (this);
857 break;
858#endif
859 default:
860 AssertReleaseMsgFailed (("Render mode must be valid: %d\n", mode));
861 LogRel (("Invalid render mode: %d\n", mode));
862 qApp->exit (1);
863 break;
864 }
865
866#if defined (VBOX_GUI_USE_DDRAW)
867 if (!mFrameBuf || mFrameBuf->address() == NULL)
868 {
869 if (mFrameBuf)
870 delete mFrameBuf;
871 mode = VBoxDefs::QImageMode;
872 mFrameBuf = new VBoxQImageFrameBuffer (this);
873 }
874#endif
875
876 if (mFrameBuf)
877 {
878 mFrameBuf->AddRef();
879 display.SetFramebuffer (VBOX_VIDEO_PRIMARY_SCREEN, CFramebuffer (mFrameBuf));
880 }
881
882 /* setup the callback */
883 mCallback = CConsoleCallback (new VBoxConsoleCallback (this));
884 mConsole.RegisterCallback (mCallback);
885 AssertWrapperOk (mConsole);
886
887 QPalette palette (viewport()->palette());
888 palette.setColor (viewport()->backgroundRole(), Qt::black);
889 viewport()->setPalette (palette);
890
891 setSizePolicy (QSizePolicy (QSizePolicy::Maximum, QSizePolicy::Maximum));
892 setMaximumSize (sizeHint());
893
894 setFocusPolicy (Qt::WheelFocus);
895
896 /* Remember the desktop geometry and register for geometry change
897 events for telling the guest about video modes we like. */
898
899 QString desktopGeometry = vboxGlobal().settings()
900 .publicProperty ("GUI/MaxGuestResolution");
901 if ((desktopGeometry == QString::null) ||
902 (desktopGeometry == "auto"))
903 setDesktopGeometry (DesktopGeo_Automatic, 0, 0);
904 else if (desktopGeometry == "any")
905 setDesktopGeometry (DesktopGeo_Any, 0, 0);
906 else
907 {
908 int width = desktopGeometry.section (',', 0, 0).toInt();
909 int height = desktopGeometry.section (',', 1, 1).toInt();
910 setDesktopGeometry (DesktopGeo_Fixed, width, height);
911 }
912 connect (QApplication::desktop(), SIGNAL (resized (int)),
913 this, SLOT (doResizeDesktop (int)));
914
915 QString passCAD = mConsole.GetMachine().GetExtraData (VBoxDefs::GUI_PassCAD);
916 if (!passCAD.isEmpty() &&
917 ((passCAD != "false") || (passCAD != "no"))
918 )
919 mPassCAD = true;
920
921#if defined (Q_WS_WIN)
922 gView = this;
923#endif
924
925#if defined (Q_WS_PM)
926 bool ok = VBoxHlpInstallKbdHook (0, winId(), UM_PREACCEL_CHAR);
927 Assert (ok);
928 NOREF (ok);
929#endif
930}
931
932VBoxConsoleView::~VBoxConsoleView()
933{
934#if defined (Q_WS_PM)
935 bool ok = VBoxHlpUninstallKbdHook (0, winId(), UM_PREACCEL_CHAR);
936 Assert (ok);
937 NOREF (ok);
938#endif
939
940#if defined (Q_WS_WIN)
941 if (gKbdHook)
942 UnhookWindowsHookEx (gKbdHook);
943 gView = 0;
944 if (mAlphaCursor)
945 DestroyIcon (mAlphaCursor);
946#endif
947
948 if (mFrameBuf)
949 {
950 /* detach our framebuffer from Display */
951 CDisplay display = mConsole.GetDisplay();
952 Assert (!display.isNull());
953 display.SetFramebuffer (VBOX_VIDEO_PRIMARY_SCREEN, CFramebuffer(NULL));
954 /* release the reference */
955 mFrameBuf->Release();
956 mFrameBuf = NULL;
957 }
958
959 mConsole.UnregisterCallback (mCallback);
960
961#if defined (Q_WS_MAC)
962# if !defined (QT_MAC_USE_COCOA)
963 if (mDarwinWindowOverlayHandlerRef)
964 {
965 ::RemoveEventHandler (mDarwinWindowOverlayHandlerRef);
966 mDarwinWindowOverlayHandlerRef = NULL;
967 }
968# endif
969 delete mDockIconPreview;
970 mDockIconPreview = NULL;
971#endif
972}
973
974//
975// Public members
976/////////////////////////////////////////////////////////////////////////////
977
978QSize VBoxConsoleView::sizeHint() const
979{
980#ifdef VBOX_WITH_DEBUGGER /** @todo figure out a more proper fix. */
981 /* HACK ALERT! Really ugly workaround for the resizing to 9x1 done
982 * by DevVGA if provoked before power on. */
983 QSize fb(mFrameBuf->width(), mFrameBuf->height());
984 if ( ( fb.width() < 16
985 || fb.height() < 16)
986 && ( vboxGlobal().isStartPausedEnabled()
987 || vboxGlobal().isDebuggerAutoShowEnabled()) )
988 fb = QSize(640, 480);
989 return QSize (fb.width() + frameWidth() * 2,
990 fb.height() + frameWidth() * 2);
991#else
992 return QSize (mFrameBuf->width() + frameWidth() * 2,
993 mFrameBuf->height() + frameWidth() * 2);
994#endif
995}
996
997/**
998 * Attaches this console view to the managed virtual machine.
999 *
1000 * @note This method is not really necessary these days -- the only place where
1001 * it gets called is VBoxConsole::openView(), right after powering the
1002 * VM up. We leave it as is just in case attaching/detaching will become
1003 * necessary some day (there are useful attached checks everywhere in the
1004 * code).
1005 */
1006void VBoxConsoleView::attach()
1007{
1008 if (!mAttached)
1009 {
1010 mAttached = true;
1011 }
1012}
1013
1014/**
1015 * Detaches this console view from the VM. Must be called to indicate
1016 * that the virtual machine managed by this instance will be no more valid
1017 * after this call.
1018 *
1019 * @note This method is not really necessary these days -- the only place where
1020 * it gets called is VBoxConsole::closeView(), when the VM is powered
1021 * down, before deleting VBoxConsoleView. We leave it as is just in case
1022 * attaching/detaching will become necessary some day (there are useful
1023 * attached checks everywhere in the code).
1024 */
1025void VBoxConsoleView::detach()
1026{
1027 if (mAttached)
1028 {
1029 /* reuse the focus event handler to uncapture everything */
1030 focusEvent (false);
1031 mAttached = false;
1032 }
1033}
1034
1035/**
1036 * Resizes the toplevel widget to fit the console view w/o scrollbars.
1037 * If adjustPosition is true and such resize is not possible (because the
1038 * console view size is lagrer then the available screen space) the toplevel
1039 * widget is resized and moved to become as large as possible while staying
1040 * fully visible.
1041 */
1042void VBoxConsoleView::normalizeGeometry (bool adjustPosition /* = false */)
1043{
1044 /* Make no normalizeGeometry in case we are in manual resize
1045 * mode or main window is maximized */
1046 if (mMainWnd->isWindowMaximized() || mMainWnd->isWindowFullScreen())
1047 return;
1048
1049 QWidget *tlw = window();
1050
1051 /* calculate client window offsets */
1052 QRect fr = tlw->frameGeometry();
1053 QRect r = tlw->geometry();
1054 int dl = r.left() - fr.left();
1055 int dt = r.top() - fr.top();
1056 int dr = fr.right() - r.right();
1057 int db = fr.bottom() - r.bottom();
1058
1059 /* get the best size w/o scroll bars */
1060 QSize s = tlw->sizeHint();
1061
1062 /* resize the frame to fit the contents */
1063 s -= tlw->size();
1064 fr.setRight (fr.right() + s.width());
1065 fr.setBottom (fr.bottom() + s.height());
1066
1067 if (adjustPosition)
1068 {
1069 QRegion ar;
1070 QDesktopWidget *dwt = QApplication::desktop();
1071 if (dwt->isVirtualDesktop())
1072 /* Compose complex available region */
1073 for (int i = 0; i < dwt->numScreens(); ++ i)
1074 ar += dwt->availableGeometry (i);
1075 else
1076 /* Get just a simple available rectangle */
1077 ar = dwt->availableGeometry (tlw->pos());
1078
1079 fr = VBoxGlobal::normalizeGeometry (
1080 fr, ar, mode != VBoxDefs::SDLMode /* canResize */);
1081 }
1082
1083#if 0
1084 /* center the frame on the desktop */
1085 fr.moveCenter (ar.center());
1086#endif
1087
1088 /* finally, set the frame geometry */
1089 tlw->setGeometry (fr.left() + dl, fr.top() + dt,
1090 fr.width() - dl - dr, fr.height() - dt - db);
1091}
1092
1093/**
1094 * Pauses or resumes the VM execution.
1095 */
1096bool VBoxConsoleView::pause (bool on)
1097{
1098 /* QAction::setOn() emits the toggled() signal, so avoid recursion when
1099 * QAction::setOn() is called from VBoxConsoleWnd::updateMachineState() */
1100 if (isPaused() == on)
1101 return true;
1102
1103 if (on)
1104 mConsole.Pause();
1105 else
1106 mConsole.Resume();
1107
1108 bool ok = mConsole.isOk();
1109 if (!ok)
1110 {
1111 if (on)
1112 vboxProblem().cannotPauseMachine (mConsole);
1113 else
1114 vboxProblem().cannotResumeMachine (mConsole);
1115 }
1116
1117 return ok;
1118}
1119
1120/**
1121 * Temporarily disables the mouse integration (or enables it back).
1122 */
1123void VBoxConsoleView::setMouseIntegrationEnabled (bool enabled)
1124{
1125 if (mMouseIntegration == enabled)
1126 return;
1127
1128 if (mMouseAbsolute)
1129 captureMouse (!enabled, false);
1130
1131 /* Hiding host cursor in case we are entering mouse integration
1132 * mode until it's shape is set to the guest cursor shape in
1133 * OnMousePointerShapeChange event handler.
1134 *
1135 * This is necessary to avoid double-cursor issues where both the
1136 * guest and the host cursors are displayed in one place, one above the
1137 * other.
1138 *
1139 * This is a workaround because the correct decision would be to notify
1140 * the Guest Additions about we are entering the mouse integration
1141 * mode. The GuestOS should hide it's cursor to allow using of
1142 * host cursor for the guest's manipulation.
1143 *
1144 * This notification is not always possible though, as not all guests
1145 * support switching to a hardware pointer on demand. */
1146 if (enabled)
1147 viewport()->setCursor (QCursor (Qt::BlankCursor));
1148
1149 mMouseIntegration = enabled;
1150
1151 emitMouseStateChanged();
1152}
1153
1154void VBoxConsoleView::setAutoresizeGuest (bool on)
1155{
1156 if (mAutoresizeGuest != on)
1157 {
1158 mAutoresizeGuest = on;
1159
1160 maybeRestrictMinimumSize();
1161
1162 if (mGuestSupportsGraphics && mAutoresizeGuest)
1163 doResizeHint();
1164 }
1165}
1166
1167/**
1168 * This method is called by VBoxConsoleWnd after it does everything necessary
1169 * on its side to go to or from fullscreen, but before it is shown.
1170 */
1171void VBoxConsoleView::onFullscreenChange (bool /* on */)
1172{
1173 /* Nothing to do here so far */
1174}
1175
1176/**
1177 * Notify the console scroll-view about the console-window is opened.
1178 */
1179void VBoxConsoleView::onViewOpened()
1180{
1181 /* Variable mIgnoreMainwndResize was initially "true" to ignore QT
1182 * initial resize event in case of auto-resize feature is on.
1183 * Currently, initial resize event is already processed, so we set
1184 * mIgnoreMainwndResize to "false" to process all further resize
1185 * events as user-initiated window resize events. */
1186 mIgnoreMainwndResize = false;
1187}
1188
1189//
1190// Protected Events
1191/////////////////////////////////////////////////////////////////////////////
1192
1193bool VBoxConsoleView::event (QEvent *e)
1194{
1195 if (mAttached)
1196 {
1197 switch (e->type())
1198 {
1199 case QEvent::FocusIn:
1200 {
1201 if (isRunning())
1202 focusEvent (true);
1203 break;
1204 }
1205 case QEvent::FocusOut:
1206 {
1207 if (isRunning())
1208 focusEvent (false);
1209 else
1210 {
1211 /* release the host key and all other pressed keys too even
1212 * when paused (otherwise, we will get stuck keys in the
1213 * guest when doing sendChangedKeyStates() on resume because
1214 * key presses were already recorded in mPressedKeys but key
1215 * releases will most likely not reach us but the new focus
1216 * window instead). */
1217 releaseAllPressedKeys (true /* aReleaseHostKey */);
1218 }
1219 break;
1220 }
1221
1222 case VBoxDefs::ResizeEventType:
1223 {
1224 /* Some situations require initial VGA Resize Request
1225 * to be ignored at all, leaving previous framebuffer,
1226 * console widget and vm window size preserved. */
1227 if (mIgnoreGuestResize)
1228 return true;
1229
1230 bool oldIgnoreMainwndResize = mIgnoreMainwndResize;
1231 mIgnoreMainwndResize = true;
1232
1233 VBoxResizeEvent *re = (VBoxResizeEvent *) e;
1234 LogRelFlowFunc (("VBoxDefs::ResizeEventType: %d x %d x %d bpp\n",
1235 re->width(), re->height(),
1236 re->bitsPerPixel()));
1237
1238 bool notifyMainWnd = mFrameBuf->width() != re->width()
1239 || mFrameBuf->height() != re->height();
1240
1241 /* Store the new size to prevent unwanted resize hints being
1242 * sent back. */
1243 storeConsoleSize(re->width(), re->height());
1244 /* do frame buffer dependent resize */
1245
1246 /* restoreOverrideCursor() is broken in Qt 4.4.0 if WA_PaintOnScreen
1247 * widgets are present. This is the case on linux with SDL. As
1248 * workaround we save/restore the arrow cursor manually. See
1249 * http://trolltech.com/developer/task-tracker/index_html?id=206165&method=entry
1250 * for details.
1251 *
1252 * Moreover the current cursor, which could be set by the guest,
1253 * should be restored after resize.
1254 */
1255 QCursor cursor;
1256 if (shouldHideHostPointer())
1257 cursor = QCursor (Qt::BlankCursor);
1258 else
1259 cursor = viewport()->cursor();
1260 mFrameBuf->resizeEvent (re);
1261 viewport()->setCursor (cursor);
1262
1263#ifdef Q_WS_MAC
1264 mDockIconPreview->setOriginalSize (re->width(), re->height());
1265#endif /* Q_WS_MAC */
1266
1267 /* This event appears in case of guest video was changed
1268 * for somehow even without video resolution change.
1269 * In this last case the host VM window will not be resized
1270 * according this event and the host mouse cursor which was
1271 * unset to default here will not be hidden in capture state.
1272 * So it is necessary to perform updateMouseClipping() for
1273 * the guest resize event if the mouse cursor was captured. */
1274 if (mMouseCaptured)
1275 updateMouseClipping();
1276
1277 /* apply maximum size restriction */
1278 setMaximumSize (sizeHint());
1279
1280 maybeRestrictMinimumSize();
1281
1282 /* resize the guest canvas */
1283 if (!mIgnoreFrameBufferResize)
1284 resize (re->width(), re->height());
1285 updateSliders();
1286 /* Let our toplevel widget calculate its sizeHint properly. */
1287#ifdef Q_WS_X11
1288 /* We use processEvents rather than sendPostedEvents & set the
1289 * time out value to max cause on X11 otherwise the layout
1290 * isn't calculated correctly. Dosn't find the bug in Qt, but
1291 * this could be triggered through the async nature of the X11
1292 * window event system. */
1293 QCoreApplication::processEvents (QEventLoop::AllEvents, INT_MAX);
1294#else /* Q_WS_X11 */
1295 QCoreApplication::sendPostedEvents (0, QEvent::LayoutRequest);
1296#endif /* Q_WS_X11 */
1297
1298 if (!mIgnoreFrameBufferResize)
1299 normalizeGeometry (true /* adjustPosition */);
1300
1301 /* report to the VM thread that we finished resizing */
1302 mConsole.GetDisplay().ResizeCompleted (0);
1303
1304 mIgnoreMainwndResize = oldIgnoreMainwndResize;
1305
1306 /* update geometry after entering fullscreen | seamless */
1307 if (mMainWnd->isTrueFullscreen() || mMainWnd->isTrueSeamless())
1308 updateGeometry();
1309
1310 /* make sure that all posted signals are processed */
1311 qApp->processEvents();
1312
1313 /* emit a signal about guest was resized */
1314 emit resizeHintDone();
1315
1316 /* We also recalculate the desktop geometry if this is determined
1317 * automatically. In fact, we only need this on the first resize,
1318 * but it is done every time to keep the code simpler. */
1319 calculateDesktopGeometry();
1320
1321 /* Enable frame-buffer resize watching. */
1322 if (mIgnoreFrameBufferResize)
1323 {
1324 mIgnoreFrameBufferResize = false;
1325 }
1326
1327 if (notifyMainWnd)
1328 mMainWnd->onDisplayResize (re->width(), re->height());
1329
1330 return true;
1331 }
1332
1333 /* See VBox[QImage|SDL]FrameBuffer::NotifyUpdate(). */
1334 case VBoxDefs::RepaintEventType:
1335 {
1336 VBoxRepaintEvent *re = (VBoxRepaintEvent *) e;
1337 viewport()->repaint (re->x() - contentsX(),
1338 re->y() - contentsY(),
1339 re->width(), re->height());
1340 /* mConsole.GetDisplay().UpdateCompleted(); - the event was acked already */
1341 return true;
1342 }
1343
1344#ifdef VBOX_WITH_VIDEOHWACCEL
1345 case VBoxDefs::VHWACommandProcessType:
1346 {
1347 mFrameBuf->doProcessVHWACommand(e);
1348 return true;
1349 }
1350#endif
1351
1352 case VBoxDefs::SetRegionEventType:
1353 {
1354 VBoxSetRegionEvent *sre = (VBoxSetRegionEvent*) e;
1355 if (mMainWnd->isTrueSeamless() &&
1356 sre->region() != mLastVisibleRegion)
1357 {
1358 mLastVisibleRegion = sre->region();
1359 mMainWnd->setMask (sre->region());
1360 }
1361 else if (!mLastVisibleRegion.isEmpty() &&
1362 !mMainWnd->isTrueSeamless())
1363 mLastVisibleRegion = QRegion();
1364 return true;
1365 }
1366
1367 case VBoxDefs::MousePointerChangeEventType:
1368 {
1369 MousePointerChangeEvent *me = (MousePointerChangeEvent *) e;
1370 /* change cursor shape only when mouse integration is
1371 * supported (change mouse shape type event may arrive after
1372 * mouse capability change that disables integration */
1373 if (mMouseAbsolute)
1374 setPointerShape (me);
1375 else
1376 /* Note: actually we should still remember the requested
1377 * cursor shape. If we can't do that, at least remember
1378 * the requested visiblilty. */
1379 mHideHostPointer = !me->isVisible();
1380 return true;
1381 }
1382 case VBoxDefs::MouseCapabilityEventType:
1383 {
1384 MouseCapabilityEvent *me = (MouseCapabilityEvent *) e;
1385 if (mMouseAbsolute != me->supportsAbsolute())
1386 {
1387 mMouseAbsolute = me->supportsAbsolute();
1388 /* correct the mouse capture state and reset the cursor
1389 * to the default shape if necessary */
1390 if (mMouseAbsolute)
1391 {
1392 CMouse mouse = mConsole.GetMouse();
1393 mouse.PutMouseEventAbsolute (-1, -1, 0,
1394 0 /* Horizontal wheel */,
1395 0);
1396 captureMouse (false, false);
1397 }
1398 else
1399 viewport()->unsetCursor();
1400 emitMouseStateChanged();
1401 vboxProblem().remindAboutMouseIntegration (mMouseAbsolute);
1402 }
1403 if (me->needsHostCursor())
1404 mMainWnd->setMouseIntegrationLocked (false);
1405 else
1406 mMainWnd->setMouseIntegrationLocked (true);
1407 return true;
1408 }
1409
1410 case VBoxDefs::ModifierKeyChangeEventType:
1411 {
1412 ModifierKeyChangeEvent *me = (ModifierKeyChangeEvent* )e;
1413 if (me->numLock() != mNumLock)
1414 muNumLockAdaptionCnt = 2;
1415 if (me->capsLock() != mCapsLock)
1416 muCapsLockAdaptionCnt = 2;
1417 mNumLock = me->numLock();
1418 mCapsLock = me->capsLock();
1419 mScrollLock = me->scrollLock();
1420 return true;
1421 }
1422
1423 case VBoxDefs::MachineStateChangeEventType:
1424 {
1425 StateChangeEvent *me = (StateChangeEvent *) e;
1426 LogFlowFunc (("MachineStateChangeEventType: state=%d\n",
1427 me->machineState()));
1428 onStateChange (me->machineState());
1429 emit machineStateChanged (me->machineState());
1430 return true;
1431 }
1432
1433 case VBoxDefs::AdditionsStateChangeEventType:
1434 {
1435 GuestAdditionsEvent *ge = (GuestAdditionsEvent *) e;
1436 LogFlowFunc (("AdditionsStateChangeEventType\n"));
1437
1438 /* Always send a size hint if we are in fullscreen or seamless
1439 * when the graphics capability is enabled, in case the host
1440 * resolution has changed since the VM was last run. */
1441#if 0
1442 if (!mDoResize && !mGuestSupportsGraphics &&
1443 ge->supportsGraphics() &&
1444 (mMainWnd->isTrueSeamless() || mMainWnd->isTrueFullscreen()))
1445 mDoResize = true;
1446#endif
1447
1448 mGuestSupportsGraphics = ge->supportsGraphics();
1449
1450 maybeRestrictMinimumSize();
1451
1452#if 0
1453 /* This will only be acted upon if mDoResize is true. */
1454 doResizeHint();
1455#endif
1456
1457 emit additionsStateChanged (ge->additionVersion(),
1458 ge->additionActive(),
1459 ge->supportsSeamless(),
1460 ge->supportsGraphics());
1461 return true;
1462 }
1463
1464 case VBoxDefs::MediaDriveChangeEventType:
1465 {
1466 MediaDriveChangeEvent *mce = (MediaDriveChangeEvent *) e;
1467 LogFlowFunc (("MediaChangeEvent\n"));
1468
1469 emit mediaDriveChanged (mce->type());
1470 return true;
1471 }
1472
1473 case VBoxDefs::ActivateMenuEventType:
1474 {
1475 ActivateMenuEvent *ame = (ActivateMenuEvent *) e;
1476 ame->action()->trigger();
1477
1478 /*
1479 * The main window and its children can be destroyed at this
1480 * point (if, for example, the activated menu item closes the
1481 * main window). Detect this situation to prevent calls to
1482 * destroyed widgets.
1483 */
1484 QWidgetList list = QApplication::topLevelWidgets();
1485 bool destroyed = list.indexOf (mMainWnd) < 0;
1486 if (!destroyed && mMainWnd->statusBar())
1487 mMainWnd->statusBar()->clearMessage();
1488
1489 return true;
1490 }
1491
1492 case VBoxDefs::NetworkAdapterChangeEventType:
1493 {
1494 /* no specific adapter information stored in this
1495 * event is currently used */
1496 emit networkStateChange();
1497 return true;
1498 }
1499
1500 case VBoxDefs::USBCtlStateChangeEventType:
1501 {
1502 emit usbStateChange();
1503 return true;
1504 }
1505
1506 case VBoxDefs::USBDeviceStateChangeEventType:
1507 {
1508 USBDeviceStateChangeEvent *ue = (USBDeviceStateChangeEvent *)e;
1509
1510 bool success = ue->error().isNull();
1511
1512 if (!success)
1513 {
1514 if (ue->attached())
1515 vboxProblem().cannotAttachUSBDevice (
1516 mConsole,
1517 vboxGlobal().details (ue->device()), ue->error());
1518 else
1519 vboxProblem().cannotDetachUSBDevice (
1520 mConsole,
1521 vboxGlobal().details (ue->device()), ue->error());
1522 }
1523
1524 emit usbStateChange();
1525
1526 return true;
1527 }
1528
1529 case VBoxDefs::SharedFolderChangeEventType:
1530 {
1531 emit sharedFoldersChanged();
1532 return true;
1533 }
1534
1535 case VBoxDefs::RuntimeErrorEventType:
1536 {
1537 RuntimeErrorEvent *ee = (RuntimeErrorEvent *) e;
1538 vboxProblem().showRuntimeError (mConsole, ee->fatal(),
1539 ee->errorID(), ee->message());
1540 return true;
1541 }
1542
1543 case QEvent::KeyPress:
1544 case QEvent::KeyRelease:
1545 {
1546 QKeyEvent *ke = (QKeyEvent *) e;
1547
1548#ifdef Q_WS_PM
1549 /// @todo temporary solution to send Alt+Tab and friends to
1550 // the guest. The proper solution is to write a keyboard
1551 // driver that will steal these combos from the host (it's
1552 // impossible to do so using hooks on OS/2).
1553
1554 if (mIsHostkeyPressed)
1555 {
1556 bool pressed = e->type() == QEvent::KeyPress;
1557 CKeyboard keyboard = mConsole.GetKeyboard();
1558
1559 /* whether the host key is Shift so that it will modify
1560 * the hot key values? Note that we don't distinguish
1561 * between left and right shift here (too much hassle) */
1562 const bool kShift = (gs.hostKey() == VK_SHIFT ||
1563 gs.hostKey() == VK_LSHIFT) &&
1564 (ke->state() & Qt::ShiftModifier);
1565 /* define hot keys according to the Shift state */
1566 const int kAltTab = kShift ? Qt::Key_Exclam : Qt::Key_1;
1567 const int kAltShiftTab = kShift ? Qt::Key_At : Qt::Key_2;
1568 const int kCtrlEsc = kShift ? Qt::Key_AsciiTilde : Qt::Key_QuoteLeft;
1569
1570 /* Simulate Alt+Tab on Host+1 and Alt+Shift+Tab on Host+2 */
1571 if (ke->key() == kAltTab || ke->key() == kAltShiftTab)
1572 {
1573 if (pressed)
1574 {
1575 /* Send the Alt press to the guest */
1576 if (!(mPressedKeysCopy [0x38] & IsKeyPressed))
1577 {
1578 /* store the press in *Copy to have it automatically
1579 * released when the Host key is released */
1580 mPressedKeysCopy [0x38] |= IsKeyPressed;
1581 keyboard.PutScancode (0x38);
1582 }
1583
1584 /* Make sure Shift is pressed if it's Key_2 and released
1585 * if it's Key_1 */
1586 if (ke->key() == kAltTab &&
1587 (mPressedKeysCopy [0x2A] & IsKeyPressed))
1588 {
1589 mPressedKeysCopy [0x2A] &= ~IsKeyPressed;
1590 keyboard.PutScancode (0xAA);
1591 }
1592 else
1593 if (ke->key() == kAltShiftTab &&
1594 !(mPressedKeysCopy [0x2A] & IsKeyPressed))
1595 {
1596 mPressedKeysCopy [0x2A] |= IsKeyPressed;
1597 keyboard.PutScancode (0x2A);
1598 }
1599 }
1600
1601 keyboard.PutScancode (pressed ? 0x0F : 0x8F);
1602
1603 ke->accept();
1604 return true;
1605 }
1606
1607 /* Simulate Ctrl+Esc on Host+Tilde */
1608 if (ke->key() == kCtrlEsc)
1609 {
1610 /* Send the Ctrl press to the guest */
1611 if (pressed && !(mPressedKeysCopy [0x1d] & IsKeyPressed))
1612 {
1613 /* store the press in *Copy to have it automatically
1614 * released when the Host key is released */
1615 mPressedKeysCopy [0x1d] |= IsKeyPressed;
1616 keyboard.PutScancode (0x1d);
1617 }
1618
1619 keyboard.PutScancode (pressed ? 0x01 : 0x81);
1620
1621 ke->accept();
1622 return true;
1623 }
1624 }
1625
1626 /* fall through to normal processing */
1627
1628#endif /* Q_WS_PM */
1629
1630 if (mIsHostkeyPressed && e->type() == QEvent::KeyPress)
1631 {
1632 if (ke->key() >= Qt::Key_F1 && ke->key() <= Qt::Key_F12)
1633 {
1634 QVector <LONG> combo (6);
1635 combo [0] = 0x1d; /* Ctrl down */
1636 combo [1] = 0x38; /* Alt down */
1637 combo [4] = 0xb8; /* Alt up */
1638 combo [5] = 0x9d; /* Ctrl up */
1639 if (ke->key() >= Qt::Key_F1 && ke->key() <= Qt::Key_F10)
1640 {
1641 combo [2] = 0x3b + (ke->key() - Qt::Key_F1); /* F1-F10 down */
1642 combo [3] = 0xbb + (ke->key() - Qt::Key_F1); /* F1-F10 up */
1643 }
1644 /* some scan slice */
1645 else if (ke->key() >= Qt::Key_F11 && ke->key() <= Qt::Key_F12)
1646 {
1647 combo [2] = 0x57 + (ke->key() - Qt::Key_F11); /* F11-F12 down */
1648 combo [3] = 0xd7 + (ke->key() - Qt::Key_F11); /* F11-F12 up */
1649 }
1650 else
1651 Assert (0);
1652
1653 CKeyboard keyboard = mConsole.GetKeyboard();
1654 keyboard.PutScancodes (combo);
1655 }
1656 else if (ke->key() == Qt::Key_Home)
1657 {
1658 /* Activate the main menu */
1659 if (mMainWnd->isTrueSeamless() || mMainWnd->isTrueFullscreen())
1660 mMainWnd->popupMainMenu (mMouseCaptured);
1661 else
1662 {
1663 /* In Qt4 it is not enough to just set the focus to
1664 * menu-bar. So to get the menu-bar we have to send
1665 * Qt::Key_Alt press/release events directly. */
1666 QKeyEvent e1 (QEvent::KeyPress, Qt::Key_Alt,
1667 Qt::NoModifier);
1668 QKeyEvent e2 (QEvent::KeyRelease, Qt::Key_Alt,
1669 Qt::NoModifier);
1670 QApplication::sendEvent (mMainWnd->menuBar(), &e1);
1671 QApplication::sendEvent (mMainWnd->menuBar(), &e2);
1672 }
1673 }
1674 else
1675 {
1676 /* process hot keys not processed in keyEvent()
1677 * (as in case of non-alphanumeric keys) */
1678 processHotKey (QKeySequence (ke->key()),
1679 mMainWnd->menuBar()->actions());
1680 }
1681 }
1682 else if (!mIsHostkeyPressed && e->type() == QEvent::KeyRelease)
1683 {
1684 /* Show a possible warning on key release which seems to
1685 * be more expected by the end user */
1686
1687 if (isPaused())
1688 {
1689 /* if the reminder is disabled we pass the event to
1690 * Qt to enable normal keyboard functionality
1691 * (for example, menu access with Alt+Letter) */
1692 if (!vboxProblem().remindAboutPausedVMInput())
1693 break;
1694 }
1695 }
1696
1697 ke->accept();
1698 return true;
1699 }
1700
1701#ifdef Q_WS_MAC
1702 /* posted OnShowWindow */
1703 case VBoxDefs::ShowWindowEventType:
1704 {
1705 /*
1706 * Dunno what Qt3 thinks a window that has minimized to the dock
1707 * should be - it is not hidden, neither is it minimized. OTOH it is
1708 * marked shown and visible, but not activated. This latter isn't of
1709 * much help though, since at this point nothing is marked activated.
1710 * I might have overlooked something, but I'm buggered what if I know
1711 * what. So, I'll just always show & activate the stupid window to
1712 * make it get out of the dock when the user wishes to show a VM.
1713 */
1714 window()->show();
1715 window()->activateWindow();
1716 return true;
1717 }
1718#endif
1719 default:
1720 break;
1721 }
1722 }
1723
1724 return QAbstractScrollArea::event (e);
1725}
1726
1727#ifdef VBOX_WITH_VIDEOHWACCEL
1728void VBoxConsoleView::scrollContentsBy (int dx, int dy)
1729{
1730 if (mAttached && mFrameBuf)
1731 {
1732 mFrameBuf->viewportScrolled(dx, dy);
1733 }
1734 QAbstractScrollArea::scrollContentsBy (dx, dy);
1735}
1736#endif
1737
1738
1739bool VBoxConsoleView::eventFilter (QObject *watched, QEvent *e)
1740{
1741 if (mAttached && watched == viewport())
1742 {
1743 switch (e->type())
1744 {
1745 case QEvent::MouseMove:
1746 case QEvent::MouseButtonPress:
1747 case QEvent::MouseButtonDblClick:
1748 case QEvent::MouseButtonRelease:
1749 {
1750 QMouseEvent *me = (QMouseEvent *) e;
1751 m_iLastMouseWheelDelta = 0;
1752 if (mouseEvent (me->type(), me->pos(), me->globalPos(),
1753 me->buttons(), me->modifiers(),
1754 0, Qt::Horizontal))
1755 return true; /* stop further event handling */
1756 break;
1757 }
1758 case QEvent::Wheel:
1759 {
1760 QWheelEvent *we = (QWheelEvent *) e;
1761 /* There are pointing devices which send smaller values for the
1762 * delta than 120. Here we sum them up until we are greater
1763 * than 120. This allows to have finer control over the speed
1764 * acceleration & enables such devices to send a valid wheel
1765 * event to our guest mouse device at all. */
1766 int iDelta = 0;
1767 m_iLastMouseWheelDelta += we->delta();
1768 if (qAbs(m_iLastMouseWheelDelta) >= 120)
1769 {
1770 iDelta = m_iLastMouseWheelDelta;
1771 m_iLastMouseWheelDelta = m_iLastMouseWheelDelta % 120;
1772 }
1773 if (mouseEvent (we->type(), we->pos(), we->globalPos(),
1774#ifdef QT_MAC_USE_COCOA
1775 /* Qt Cocoa is buggy. It always reports a left
1776 * button pressed when the mouse wheel event
1777 * occurs. A workaround is to ask the
1778 * application which buttons are pressed
1779 * currently. */
1780 QApplication::mouseButtons(),
1781#else /* QT_MAC_USE_COCOA */
1782 we->buttons(),
1783#endif /* QT_MAC_USE_COCOA */
1784 we->modifiers(),
1785 iDelta, we->orientation()))
1786 return true; /* stop further event handling */
1787 break;
1788 }
1789#ifdef Q_WS_MAC
1790 case QEvent::Leave:
1791 {
1792 /* Enable mouse event compression if we leave the VM view. This
1793 is necessary for having smooth resizing of the VM/other
1794 windows. */
1795 setMouseCoalescingEnabled (true);
1796 break;
1797 }
1798 case QEvent::Enter:
1799 {
1800 /* Disable mouse event compression if we enter the VM view. So
1801 all mouse events are registered in the VM. Only do this if
1802 the keyboard/mouse is grabbed (this is when we have a valid
1803 event handler). */
1804 setMouseCoalescingEnabled (false);
1805 break;
1806 }
1807#endif /* Q_WS_MAC */
1808 case QEvent::Resize:
1809 {
1810 if (mMouseCaptured)
1811 updateMouseClipping();
1812#ifdef VBOX_WITH_VIDEOHWACCEL
1813 if (mFrameBuf)
1814 {
1815 mFrameBuf->viewportResized((QResizeEvent*)e);
1816 }
1817#endif
1818 break;
1819 }
1820 default:
1821 break;
1822 }
1823 }
1824 else if (watched == mMainWnd)
1825 {
1826 switch (e->type())
1827 {
1828#if defined (Q_WS_WIN32)
1829#if defined (VBOX_GUI_USE_DDRAW)
1830 case QEvent::Move:
1831 {
1832 /*
1833 * notification from our parent that it has moved. We need this
1834 * in order to possibly adjust the direct screen blitting.
1835 */
1836 if (mFrameBuf)
1837 mFrameBuf->moveEvent ((QMoveEvent *) e);
1838 break;
1839 }
1840#endif
1841 /*
1842 * install/uninstall low-level kbd hook on every
1843 * activation/deactivation to:
1844 * a) avoid excess hook calls when we're not active and
1845 * b) be always in front of any other possible hooks
1846 */
1847 case QEvent::WindowActivate:
1848 {
1849 gKbdHook = SetWindowsHookEx (WH_KEYBOARD_LL, lowLevelKeyboardProc,
1850 GetModuleHandle (NULL), 0);
1851 AssertMsg (gKbdHook, ("SetWindowsHookEx(): err=%d", GetLastError()));
1852 break;
1853 }
1854 case QEvent::WindowDeactivate:
1855 {
1856 if (gKbdHook)
1857 {
1858 UnhookWindowsHookEx (gKbdHook);
1859 gKbdHook = NULL;
1860 }
1861 break;
1862 }
1863#endif /* defined (Q_WS_WIN32) */
1864#if defined (Q_WS_MAC)
1865 /*
1866 * Install/remove the keyboard event handler.
1867 */
1868 case QEvent::WindowActivate:
1869 darwinGrabKeyboardEvents (true);
1870 break;
1871 case QEvent::WindowDeactivate:
1872 darwinGrabKeyboardEvents (false);
1873 break;
1874#endif /* defined (Q_WS_MAC) */
1875 case QEvent::Resize:
1876 {
1877 /* Set the "guest needs to resize" hint. This hint is acted upon
1878 * when (and only when) the autoresize property is "true". */
1879 mDoResize = mGuestSupportsGraphics || mMainWnd->isTrueFullscreen();
1880 if (!mIgnoreMainwndResize &&
1881 mGuestSupportsGraphics && mAutoresizeGuest)
1882 QTimer::singleShot (300, this, SLOT (doResizeHint()));
1883 break;
1884 }
1885 case QEvent::WindowStateChange:
1886 {
1887 /* During minimizing and state restoring mMainWnd gets the focus
1888 * which belongs to console view window, so returning it properly. */
1889 QWindowStateChangeEvent *ev = static_cast <QWindowStateChangeEvent*> (e);
1890 if (ev->oldState() & Qt::WindowMinimized)
1891 {
1892 if (QApplication::focusWidget())
1893 {
1894 QApplication::focusWidget()->clearFocus();
1895 qApp->processEvents();
1896 }
1897 QTimer::singleShot (0, this, SLOT (setFocus()));
1898 }
1899 break;
1900 }
1901
1902 default:
1903 break;
1904 }
1905 }
1906 else if (watched == mMainWnd->menuBar())
1907 {
1908 /*
1909 * sometimes when we press ESC in the menu it brings the
1910 * focus away (Qt bug?) causing no widget to have a focus,
1911 * or holds the focus itself, instead of returning the focus
1912 * to the console window. here we fix this.
1913 */
1914 switch (e->type())
1915 {
1916 case QEvent::FocusOut:
1917 {
1918 if (qApp->focusWidget() == 0)
1919 setFocus();
1920 break;
1921 }
1922 case QEvent::KeyPress:
1923 {
1924 QKeyEvent *ke = (QKeyEvent *) e;
1925 if (ke->key() == Qt::Key_Escape && (ke->modifiers() == Qt::NoModifier))
1926 if (mMainWnd->menuBar()->hasFocus())
1927 setFocus();
1928 break;
1929 }
1930 default:
1931 break;
1932 }
1933 }
1934
1935 return QAbstractScrollArea::eventFilter (watched, e);
1936}
1937
1938#if defined(Q_WS_WIN32)
1939
1940/**
1941 * Low-level keyboard event handler,
1942 * @return
1943 * true to indicate that the message is processed and false otherwise
1944 */
1945bool VBoxConsoleView::winLowKeyboardEvent (UINT msg, const KBDLLHOOKSTRUCT &event)
1946{
1947#if 0
1948 LogFlow (("### vkCode=%08X, scanCode=%08X, flags=%08X, dwExtraInfo=%08X (mKbdCaptured=%d)\n",
1949 event.vkCode, event.scanCode, event.flags, event.dwExtraInfo, mKbdCaptured));
1950 char buf [256];
1951 sprintf (buf, "### vkCode=%08X, scanCode=%08X, flags=%08X, dwExtraInfo=%08X",
1952 event.vkCode, event.scanCode, event.flags, event.dwExtraInfo);
1953 mMainWnd->statusBar()->message (buf);
1954#endif
1955
1956 /* Sometimes it happens that Win inserts additional events on some key
1957 * press/release. For example, it prepends ALT_GR in German layout with
1958 * the VK_LCONTROL vkey with curious 0x21D scan code (seems to be necessary
1959 * to specially treat ALT_GR to enter additional chars to regular apps).
1960 * These events are definitely unwanted in VM, so filter them out. */
1961 /* Note (michael): it also sometimes sends the VK_CAPITAL vkey with scan
1962 * code 0x23a. If this is not passed through then it is impossible to
1963 * cancel CapsLock on a French keyboard. I didn't find any other examples
1964 * of these strange events. Let's hope we are not missing anything else
1965 * of importance! */
1966 if (hasFocus() && (event.scanCode & ~0xFF))
1967 {
1968 if (event.vkCode == VK_CAPITAL)
1969 return false;
1970 else
1971 return true;
1972 }
1973
1974 if (!mKbdCaptured)
1975 return false;
1976
1977 /* it's possible that a key has been pressed while the keyboard was not
1978 * captured, but is being released under the capture. Detect this situation
1979 * and return false to let Windows process the message normally and update
1980 * its key state table (to avoid the stuck key effect). */
1981 uint8_t what_pressed = (event.flags & 0x01) && (event.vkCode != VK_RSHIFT)
1982 ? IsExtKeyPressed
1983 : IsKeyPressed;
1984 if ((event.flags & 0x80) /* released */ &&
1985 ((event.vkCode == gs.hostKey() && !hostkey_in_capture) ||
1986 (mPressedKeys [event.scanCode] & (IsKbdCaptured | what_pressed)) == what_pressed))
1987 return false;
1988
1989 MSG message;
1990 message.hwnd = winId();
1991 message.message = msg;
1992 message.wParam = event.vkCode;
1993 message.lParam =
1994 1 |
1995 (event.scanCode & 0xFF) << 16 |
1996 (event.flags & 0xFF) << 24;
1997
1998 /* Windows sets here the extended bit when the Right Shift key is pressed,
1999 * which is totally wrong. Undo it. */
2000 if (event.vkCode == VK_RSHIFT)
2001 message.lParam &= ~0x1000000;
2002
2003 /* we suppose here that this hook is always called on the main GUI thread */
2004 long dummyResult;
2005 return winEvent (&message, &dummyResult);
2006}
2007
2008/**
2009 * Get Win32 messages before they are passed to Qt. This allows us to get
2010 * the keyboard events directly and bypass the harmful Qt translation. A
2011 * return value of @c true indicates to Qt that the event has been handled.
2012 */
2013bool VBoxConsoleView::winEvent (MSG *aMsg, long* /* aResult */)
2014{
2015 if (!mAttached || ! (
2016 aMsg->message == WM_KEYDOWN || aMsg->message == WM_SYSKEYDOWN ||
2017 aMsg->message == WM_KEYUP || aMsg->message == WM_SYSKEYUP
2018 ))
2019 return false;
2020
2021 /* check for the special flag possibly set at the end of this function */
2022 if (aMsg->lParam & (0x1 << 25))
2023 {
2024 aMsg->lParam &= ~(0x1 << 25);
2025 return false;
2026 }
2027
2028#if 0
2029 char buf [256];
2030 sprintf (buf, "WM_%04X: vk=%04X rep=%05d scan=%02X ext=%01d rzv=%01X ctx=%01d prev=%01d tran=%01d",
2031 aMsg->message, aMsg->wParam,
2032 (aMsg->lParam & 0xFFFF),
2033 ((aMsg->lParam >> 16) & 0xFF),
2034 ((aMsg->lParam >> 24) & 0x1),
2035 ((aMsg->lParam >> 25) & 0xF),
2036 ((aMsg->lParam >> 29) & 0x1),
2037 ((aMsg->lParam >> 30) & 0x1),
2038 ((aMsg->lParam >> 31) & 0x1));
2039 mMainWnd->statusBar()->message (buf);
2040 LogFlow (("%s\n", buf));
2041#endif
2042
2043 int scan = (aMsg->lParam >> 16) & 0x7F;
2044 /* scancodes 0x80 and 0x00 are ignored */
2045 if (!scan)
2046 return true;
2047
2048 int vkey = aMsg->wParam;
2049
2050 /* When one of the SHIFT keys is held and one of the cursor movement
2051 * keys is pressed, Windows duplicates SHIFT press/release messages,
2052 * but with the virtual key code set to 0xFF. These virtual keys are also
2053 * sent in some other situations (Pause, PrtScn, etc.). Ignore such
2054 * messages. */
2055 if (vkey == 0xFF)
2056 return true;
2057
2058 int flags = 0;
2059 if (aMsg->lParam & 0x1000000)
2060 flags |= KeyExtended;
2061 if (!(aMsg->lParam & 0x80000000))
2062 flags |= KeyPressed;
2063
2064 switch (vkey)
2065 {
2066 case VK_SHIFT:
2067 case VK_CONTROL:
2068 case VK_MENU:
2069 {
2070 /* overcome stupid Win32 modifier key generalization */
2071 int keyscan = scan;
2072 if (flags & KeyExtended)
2073 keyscan |= 0xE000;
2074 switch (keyscan)
2075 {
2076 case 0x002A: vkey = VK_LSHIFT; break;
2077 case 0x0036: vkey = VK_RSHIFT; break;
2078 case 0x001D: vkey = VK_LCONTROL; break;
2079 case 0xE01D: vkey = VK_RCONTROL; break;
2080 case 0x0038: vkey = VK_LMENU; break;
2081 case 0xE038: vkey = VK_RMENU; break;
2082 }
2083 break;
2084 }
2085 case VK_NUMLOCK:
2086 /* Win32 sets the extended bit for the NumLock key. Reset it. */
2087 flags &= ~KeyExtended;
2088 break;
2089 case VK_SNAPSHOT:
2090 flags |= KeyPrint;
2091 break;
2092 case VK_PAUSE:
2093 flags |= KeyPause;
2094 break;
2095 }
2096
2097 bool result = keyEvent (vkey, scan, flags);
2098 if (!result && mKbdCaptured)
2099 {
2100 /* keyEvent() returned that it didn't process the message, but since the
2101 * keyboard is captured, we don't want to pass it to Windows. We just want
2102 * to let Qt process the message (to handle non-alphanumeric <HOST>+key
2103 * shortcuts for example). So send it direcltly to the window with the
2104 * special flag in the reserved area of lParam (to avoid recursion). */
2105 ::SendMessage (aMsg->hwnd, aMsg->message,
2106 aMsg->wParam, aMsg->lParam | (0x1 << 25));
2107 return true;
2108 }
2109
2110 /* These special keys have to be handled by Windows as well to update the
2111 * internal modifier state and to enable/disable the keyboard LED */
2112 if (vkey == VK_NUMLOCK || vkey == VK_CAPITAL || vkey == VK_LSHIFT || vkey == VK_RSHIFT)
2113 return false;
2114
2115 return result;
2116}
2117
2118#elif defined (Q_WS_PM)
2119
2120/**
2121 * Get PM messages before they are passed to Qt. This allows us to get
2122 * the keyboard events directly and bypass the harmful Qt translation. A
2123 * return value of @c true indicates to Qt that the event has been handled.
2124 */
2125bool VBoxConsoleView::pmEvent (QMSG *aMsg)
2126{
2127 if (!mAttached)
2128 return false;
2129
2130 if (aMsg->msg == UM_PREACCEL_CHAR)
2131 {
2132 /* we are inside the input hook */
2133
2134 /* let the message go through the normal system pipeline */
2135 if (!mKbdCaptured)
2136 return false;
2137 }
2138
2139 if (aMsg->msg != WM_CHAR &&
2140 aMsg->msg != UM_PREACCEL_CHAR)
2141 return false;
2142
2143 /* check for the special flag possibly set at the end of this function */
2144 if (SHORT2FROMMP (aMsg->mp2) & 0x8000)
2145 {
2146 aMsg->mp2 = MPFROM2SHORT (SHORT1FROMMP (aMsg->mp2),
2147 SHORT2FROMMP (aMsg->mp2) & ~0x8000);
2148 return false;
2149 }
2150
2151#if 0
2152 {
2153 char buf [256];
2154 sprintf (buf, "*** %s: f=%04X rep=%03d scan=%02X ch=%04X vk=%04X",
2155 (aMsg->msg == WM_CHAR ? "WM_CHAR" : "UM_PREACCEL_CHAR"),
2156 SHORT1FROMMP (aMsg->mp1), CHAR3FROMMP (aMsg->mp1),
2157 CHAR4FROMMP (aMsg->mp1), SHORT1FROMMP (aMsg->mp2),
2158 SHORT2FROMMP (aMsg->mp2));
2159 mMainWnd->statusBar()->message (buf);
2160 LogFlow (("%s\n", buf));
2161 }
2162#endif
2163
2164 USHORT ch = SHORT1FROMMP (aMsg->mp2);
2165 USHORT f = SHORT1FROMMP (aMsg->mp1);
2166
2167 int scan = (unsigned int) CHAR4FROMMP (aMsg->mp1);
2168 if (!scan || scan > 0x7F)
2169 return true;
2170
2171 int vkey = QIHotKeyEdit::virtualKey (aMsg);
2172
2173 int flags = 0;
2174
2175 if ((ch & 0xFF) == 0xE0)
2176 {
2177 flags |= KeyExtended;
2178 scan = ch >> 8;
2179 }
2180 else if (scan == 0x5C && (ch & 0xFF) == '/')
2181 {
2182 /* this is the '/' key on the keypad */
2183 scan = 0x35;
2184 flags |= KeyExtended;
2185 }
2186 else
2187 {
2188 /* For some keys, the scan code passed in QMSG is a pseudo scan
2189 * code. We replace it with a real hardware scan code, according to
2190 * http://www.computer-engineering.org/ps2keyboard/scancodes1.html.
2191 * Also detect Pause and PrtScn and set flags. */
2192 switch (vkey)
2193 {
2194 case VK_ENTER: scan = 0x1C; flags |= KeyExtended; break;
2195 case VK_CTRL: scan = 0x1D; flags |= KeyExtended; break;
2196 case VK_ALTGRAF: scan = 0x38; flags |= KeyExtended; break;
2197 case VK_LWIN: scan = 0x5B; flags |= KeyExtended; break;
2198 case VK_RWIN: scan = 0x5C; flags |= KeyExtended; break;
2199 case VK_WINMENU: scan = 0x5D; flags |= KeyExtended; break;
2200 case VK_FORWARD: scan = 0x69; flags |= KeyExtended; break;
2201 case VK_BACKWARD: scan = 0x6A; flags |= KeyExtended; break;
2202#if 0
2203 /// @todo this would send 0xE0 0x46 0xE0 0xC6. It's not fully
2204 // clear what is more correct
2205 case VK_BREAK: scan = 0x46; flags |= KeyExtended; break;
2206#else
2207 case VK_BREAK: scan = 0; flags |= KeyPause; break;
2208#endif
2209 case VK_PAUSE: scan = 0; flags |= KeyPause; break;
2210 case VK_PRINTSCRN: scan = 0; flags |= KeyPrint; break;
2211 default:;
2212 }
2213 }
2214
2215 if (!(f & KC_KEYUP))
2216 flags |= KeyPressed;
2217
2218 bool result = keyEvent (vkey, scan, flags);
2219 if (!result && mKbdCaptured)
2220 {
2221 /* keyEvent() returned that it didn't process the message, but since the
2222 * keyboard is captured, we don't want to pass it to PM. We just want
2223 * to let Qt process the message (to handle non-alphanumeric <HOST>+key
2224 * shortcuts for example). So send it direcltly to the window with the
2225 * special flag in the reserved area of lParam (to avoid recursion). */
2226 ::WinSendMsg (aMsg->hwnd, WM_CHAR,
2227 aMsg->mp1,
2228 MPFROM2SHORT (SHORT1FROMMP (aMsg->mp2),
2229 SHORT2FROMMP (aMsg->mp2) | 0x8000));
2230 return true;
2231 }
2232 return result;
2233}
2234
2235#elif defined(Q_WS_X11)
2236
2237/**
2238 * This function is a "predicate" for XCheckIfEvent(). It will check
2239 * the XEvent passed to it to see if it is a keypress event matching
2240 * the keyrelease event in @a pvArg.
2241 * @returns True if the event matches, False otherwise
2242 * @param pEvent the event to compare, taken from the event queue
2243 * @param pvArg the keyrelease event we would like to compare against
2244 */
2245static Bool VBoxConsoleViewCompEvent(Display *, XEvent *pEvent,
2246 XPointer pvArg)
2247{
2248 XEvent *pKeyEvent = (XEvent *) pvArg;
2249 if ((pEvent->type == XKeyPress) &&
2250 (pEvent->xkey.keycode == pKeyEvent->xkey.keycode))
2251 return True;
2252 else
2253 return False;
2254}
2255
2256/**
2257 * This routine gets X11 events before they are processed by Qt. This is
2258 * used for our platform specific keyboard implementation. A return value
2259 * of TRUE indicates that the event has been processed by us.
2260 */
2261bool VBoxConsoleView::x11Event (XEvent *event)
2262{
2263 switch (event->type)
2264 {
2265 /* We have to handle XFocusOut right here as this event is not passed
2266 * to VBoxConsoleView::event(). Handling this event is important for
2267 * releasing the keyboard before the screen saver gets active. */
2268 case XFocusOut:
2269 case XFocusIn:
2270 if (isRunning())
2271 focusEvent (event->type == XFocusIn);
2272 return false;
2273 case XKeyPress:
2274 case XKeyRelease:
2275 if (mAttached)
2276 break;
2277 /* else fall through */
2278 default:
2279 return false; /* pass the event to Qt */
2280 }
2281
2282 /* Translate the keycode to a PC scan code. */
2283 unsigned scan = handleXKeyEvent (event);
2284
2285#if 0
2286 char buf [256];
2287 sprintf (buf, "pr=%d kc=%08X st=%08X extended=%s scan=%04X",
2288 event->type == XKeyPress ? 1 : 0, event->xkey.keycode,
2289 event->xkey.state, scan >> 8 ? "true" : "false", scan & 0x7F);
2290 mMainWnd->statusBar()->message (buf);
2291 LogFlow (("### %s\n", buf));
2292#endif
2293
2294 // scancodes 0x00 (no valid translation) and 0x80 are ignored
2295 if (!scan & 0x7F)
2296 return true;
2297
2298 /* Fix for http://www.virtualbox.org/ticket/1296:
2299 * when X11 sends events for repeated keys, it always inserts an
2300 * XKeyRelease before the XKeyPress. */
2301 XEvent returnEvent;
2302 if ((event->type == XKeyRelease) &&
2303 (XCheckIfEvent(event->xkey.display, &returnEvent,
2304 VBoxConsoleViewCompEvent, (XPointer) event) == True)) {
2305 XPutBackEvent(event->xkey.display, &returnEvent);
2306 /* Discard it, don't pass it to Qt. */
2307 return true;
2308 }
2309
2310 KeySym ks = ::XKeycodeToKeysym (event->xkey.display, event->xkey.keycode, 0);
2311
2312 int flags = 0;
2313 if (scan >> 8)
2314 flags |= KeyExtended;
2315 if (event->type == XKeyPress)
2316 flags |= KeyPressed;
2317
2318 /* Remove the extended flag */
2319 scan &= 0x7F;
2320
2321 switch (ks)
2322 {
2323 case XK_Print:
2324 flags |= KeyPrint;
2325 break;
2326 case XK_Pause:
2327 if (event->xkey.state & ControlMask) /* Break */
2328 {
2329 ks = XK_Break;
2330 flags |= KeyExtended;
2331 scan = 0x46;
2332 }
2333 else
2334 flags |= KeyPause;
2335 break;
2336 }
2337
2338 return keyEvent (ks, scan, flags);
2339}
2340
2341#elif defined (Q_WS_MAC)
2342
2343/**
2344 * Invoked by VBoxConsoleView::darwinEventHandlerProc / VBoxConsoleView::macEventFilter when
2345 * it receives a raw keyboard event.
2346 *
2347 * @param pvCocoaEvent The Cocoa keyboard event. Can be NULL in some configs.
2348 * @param inEvent The keyboard event.
2349 *
2350 * @return true if the key was processed, false if it wasn't processed and should be passed on.
2351 */
2352bool VBoxConsoleView::darwinKeyboardEvent (const void *pvCocoaEvent, EventRef inEvent)
2353{
2354 bool ret = false;
2355 UInt32 EventKind = ::GetEventKind (inEvent);
2356 if (EventKind != kEventRawKeyModifiersChanged)
2357 {
2358 /* convert keycode to set 1 scan code. */
2359 UInt32 keyCode = ~0U;
2360 ::GetEventParameter (inEvent, kEventParamKeyCode, typeUInt32, NULL, sizeof (keyCode), NULL, &keyCode);
2361 /* The usb keyboard driver translates these codes to different virtual
2362 * key codes depending of the keyboard type. There are ANSI, ISO, JIS
2363 * and unknown. For European keyboards (ISO) the key 0xa and 0x32 have
2364 * to be switched. Here we are doing this at runtime, cause the user
2365 * can have more than one keyboard (of different type), where he may
2366 * switch at will all the time. Default is the ANSI standard as defined
2367 * in g_aDarwinToSet1. */
2368 if ( (keyCode == 0xa || keyCode == 0x32)
2369 && KBGetLayoutType(LMGetKbdType()) == kKeyboardISO)
2370 keyCode = 0x3c - keyCode;
2371 unsigned scanCode = ::DarwinKeycodeToSet1Scancode (keyCode);
2372 if (scanCode)
2373 {
2374 /* calc flags. */
2375 int flags = 0;
2376 if (EventKind != kEventRawKeyUp)
2377 flags |= KeyPressed;
2378 if (scanCode & VBOXKEY_EXTENDED)
2379 flags |= KeyExtended;
2380 /** @todo KeyPause, KeyPrint. */
2381 scanCode &= VBOXKEY_SCANCODE_MASK;
2382
2383 /* get the unicode string (if present). */
2384 AssertCompileSize (wchar_t, 2);
2385 AssertCompileSize (UniChar, 2);
2386 ByteCount cbWritten = 0;
2387 wchar_t ucs[8];
2388 if (::GetEventParameter (inEvent, kEventParamKeyUnicodes, typeUnicodeText, NULL,
2389 sizeof (ucs), &cbWritten, &ucs[0]) != 0)
2390 cbWritten = 0;
2391 ucs[cbWritten / sizeof(wchar_t)] = 0; /* The api doesn't terminate it. */
2392
2393 ret = keyEvent (keyCode, scanCode, flags, ucs[0] ? ucs : NULL);
2394 }
2395 }
2396 else
2397 {
2398 /* May contain multiple modifier changes, kind of annoying. */
2399 UInt32 newMask = 0;
2400 ::GetEventParameter (inEvent, kEventParamKeyModifiers, typeUInt32, NULL,
2401 sizeof (newMask), NULL, &newMask);
2402 newMask = ::DarwinAdjustModifierMask (newMask, pvCocoaEvent);
2403 UInt32 changed = newMask ^ mDarwinKeyModifiers;
2404 if (changed)
2405 {
2406 for (UInt32 bit = 0; bit < 32; bit++)
2407 {
2408 if (!(changed & (1 << bit)))
2409 continue;
2410 unsigned scanCode = ::DarwinModifierMaskToSet1Scancode (1 << bit);
2411 if (!scanCode)
2412 continue;
2413 unsigned keyCode = ::DarwinModifierMaskToDarwinKeycode (1 << bit);
2414 Assert (keyCode);
2415
2416 if (!(scanCode & VBOXKEY_LOCK))
2417 {
2418 unsigned flags = (newMask & (1 << bit)) ? KeyPressed : 0;
2419 if (scanCode & VBOXKEY_EXTENDED)
2420 flags |= KeyExtended;
2421 scanCode &= VBOXKEY_SCANCODE_MASK;
2422 ret |= keyEvent (keyCode, scanCode & 0xff, flags);
2423 }
2424 else
2425 {
2426 unsigned flags = 0;
2427 if (scanCode & VBOXKEY_EXTENDED)
2428 flags |= KeyExtended;
2429 scanCode &= VBOXKEY_SCANCODE_MASK;
2430 keyEvent (keyCode, scanCode, flags | KeyPressed);
2431 keyEvent (keyCode, scanCode, flags);
2432 }
2433 }
2434 }
2435
2436 mDarwinKeyModifiers = newMask;
2437
2438 /* Always return true here because we'll otherwise getting a Qt event
2439 we don't want and that will only cause the Pause warning to pop up. */
2440 ret = true;
2441 }
2442
2443 return ret;
2444}
2445
2446
2447/**
2448 * Installs or removes the keyboard event handler.
2449 *
2450 * @param fGrab True if we're to grab the events, false if we're not to.
2451 */
2452void VBoxConsoleView::darwinGrabKeyboardEvents (bool fGrab)
2453{
2454 mKeyboardGrabbed = fGrab;
2455 if (fGrab)
2456 {
2457 /* Disable mouse and keyboard event compression/delaying to make sure
2458 we *really* get all of the events. */
2459 ::CGSetLocalEventsSuppressionInterval (0.0);
2460 setMouseCoalescingEnabled (false);
2461
2462 /* Register the event callback/hook and grab the keyboard. */
2463# ifdef QT_MAC_USE_COCOA
2464 ::VBoxCocoaApplication_setCallback (UINT32_MAX, /** @todo fix mask */
2465 VBoxConsoleView::darwinEventHandlerProc, this);
2466
2467# else /* QT_MAC_USE_COCOA */
2468 EventTypeSpec eventTypes[6];
2469 eventTypes[0].eventClass = kEventClassKeyboard;
2470 eventTypes[0].eventKind = kEventRawKeyDown;
2471 eventTypes[1].eventClass = kEventClassKeyboard;
2472 eventTypes[1].eventKind = kEventRawKeyUp;
2473 eventTypes[2].eventClass = kEventClassKeyboard;
2474 eventTypes[2].eventKind = kEventRawKeyRepeat;
2475 eventTypes[3].eventClass = kEventClassKeyboard;
2476 eventTypes[3].eventKind = kEventRawKeyModifiersChanged;
2477 /* For ignorning Command-H and Command-Q which aren't affected by the
2478 * global hotkey stuff (doesn't work well): */
2479 eventTypes[4].eventClass = kEventClassCommand;
2480 eventTypes[4].eventKind = kEventCommandProcess;
2481 eventTypes[5].eventClass = kEventClassCommand;
2482 eventTypes[5].eventKind = kEventCommandUpdateStatus;
2483
2484 EventHandlerUPP eventHandler = ::NewEventHandlerUPP (VBoxConsoleView::darwinEventHandlerProc);
2485
2486 mDarwinEventHandlerRef = NULL;
2487 ::InstallApplicationEventHandler (eventHandler, RT_ELEMENTS (eventTypes), &eventTypes[0],
2488 this, &mDarwinEventHandlerRef);
2489 ::DisposeEventHandlerUPP (eventHandler);
2490# endif /* !QT_MAC_USE_COCOA */
2491
2492 ::DarwinGrabKeyboard (false);
2493 }
2494 else
2495 {
2496 ::DarwinReleaseKeyboard();
2497# ifdef QT_MAC_USE_COCOA
2498 ::VBoxCocoaApplication_unsetCallback (UINT32_MAX, /** @todo fix mask */
2499 VBoxConsoleView::darwinEventHandlerProc, this);
2500# else /* !QT_MAC_USE_COCOA */
2501 if (mDarwinEventHandlerRef)
2502 {
2503 ::RemoveEventHandler (mDarwinEventHandlerRef);
2504 mDarwinEventHandlerRef = NULL;
2505 }
2506# endif /* !QT_MAC_USE_COCOA */
2507 }
2508}
2509
2510#endif // defined (Q_WS_MAC)
2511
2512//
2513// Private members
2514/////////////////////////////////////////////////////////////////////////////
2515
2516/**
2517 * Called on every focus change and also to forcibly capture/uncapture the
2518 * input in situations similar to gaining or losing focus.
2519 *
2520 * @param aHasFocus true if the window got focus and false otherwise.
2521 * @param aReleaseHostKey true to release the host key (used only when
2522 * @a aHasFocus is false.
2523 */
2524void VBoxConsoleView::focusEvent (bool aHasFocus,
2525 bool aReleaseHostKey /* = true */)
2526{
2527 if (aHasFocus)
2528 {
2529#ifdef RT_OS_WINDOWS
2530 if ( !mDisableAutoCapture && gs.autoCapture()
2531 && GetAncestor (winId(), GA_ROOT) == GetForegroundWindow())
2532#else
2533 if (!mDisableAutoCapture && gs.autoCapture())
2534#endif /* RT_OS_WINDOWS */
2535 {
2536 captureKbd (true);
2537/// @todo (dmik)
2538// the below is for the mouse auto-capture. disabled for now. in order to
2539// properly support it, we need to know when *all* mouse buttons are
2540// released after we got focus, and grab the mouse only after then.
2541// btw, the similar would be good the for keyboard auto-capture, too.
2542// if (!(mMouseAbsolute && mMouseIntegration))
2543// captureMouse (true);
2544 }
2545
2546 /* reset the single-time disable capture flag */
2547 if (mDisableAutoCapture)
2548 mDisableAutoCapture = false;
2549 }
2550 else
2551 {
2552 captureMouse (false);
2553 captureKbd (false, false);
2554 releaseAllPressedKeys (aReleaseHostKey);
2555 }
2556}
2557
2558/**
2559 * Synchronize the views of the host and the guest to the modifier keys.
2560 * This function will add up to 6 additional keycodes to codes.
2561 *
2562 * @param codes pointer to keycodes which are sent to the keyboard
2563 * @param count pointer to the keycodes counter
2564 */
2565void VBoxConsoleView::fixModifierState (LONG *codes, uint *count)
2566{
2567#if defined(Q_WS_X11)
2568
2569 Window wDummy1, wDummy2;
2570 int iDummy3, iDummy4, iDummy5, iDummy6;
2571 unsigned uMask;
2572 unsigned uKeyMaskNum = 0, uKeyMaskCaps = 0, uKeyMaskScroll = 0;
2573
2574 uKeyMaskCaps = LockMask;
2575 XModifierKeymap* map = XGetModifierMapping(QX11Info::display());
2576 KeyCode keyCodeNum = XKeysymToKeycode(QX11Info::display(), XK_Num_Lock);
2577 KeyCode keyCodeScroll = XKeysymToKeycode(QX11Info::display(), XK_Scroll_Lock);
2578
2579 for (int i = 0; i < 8; i++)
2580 {
2581 if ( keyCodeNum != NoSymbol
2582 && map->modifiermap[map->max_keypermod * i] == keyCodeNum)
2583 uKeyMaskNum = 1 << i;
2584 else if ( keyCodeScroll != NoSymbol
2585 && map->modifiermap[map->max_keypermod * i] == keyCodeScroll)
2586 uKeyMaskScroll = 1 << i;
2587 }
2588 XQueryPointer(QX11Info::display(), DefaultRootWindow(QX11Info::display()), &wDummy1, &wDummy2,
2589 &iDummy3, &iDummy4, &iDummy5, &iDummy6, &uMask);
2590 XFreeModifiermap(map);
2591
2592 if (muNumLockAdaptionCnt && (mNumLock ^ !!(uMask & uKeyMaskNum)))
2593 {
2594 muNumLockAdaptionCnt--;
2595 codes[(*count)++] = 0x45;
2596 codes[(*count)++] = 0x45 | 0x80;
2597 }
2598 if (muCapsLockAdaptionCnt && (mCapsLock ^ !!(uMask & uKeyMaskCaps)))
2599 {
2600 muCapsLockAdaptionCnt--;
2601 codes[(*count)++] = 0x3a;
2602 codes[(*count)++] = 0x3a | 0x80;
2603 /* Some keyboard layouts require shift to be pressed to break
2604 * capslock. For simplicity, only do this if shift is not
2605 * already held down. */
2606 if (mCapsLock && !(mPressedKeys [0x2a] & IsKeyPressed))
2607 {
2608 codes[(*count)++] = 0x2a;
2609 codes[(*count)++] = 0x2a | 0x80;
2610 }
2611 }
2612
2613#elif defined(Q_WS_WIN32)
2614
2615 if (muNumLockAdaptionCnt && (mNumLock ^ !!(GetKeyState(VK_NUMLOCK))))
2616 {
2617 muNumLockAdaptionCnt--;
2618 codes[(*count)++] = 0x45;
2619 codes[(*count)++] = 0x45 | 0x80;
2620 }
2621 if (muCapsLockAdaptionCnt && (mCapsLock ^ !!(GetKeyState(VK_CAPITAL))))
2622 {
2623 muCapsLockAdaptionCnt--;
2624 codes[(*count)++] = 0x3a;
2625 codes[(*count)++] = 0x3a | 0x80;
2626 /* Some keyboard layouts require shift to be pressed to break
2627 * capslock. For simplicity, only do this if shift is not
2628 * already held down. */
2629 if (mCapsLock && !(mPressedKeys [0x2a] & IsKeyPressed))
2630 {
2631 codes[(*count)++] = 0x2a;
2632 codes[(*count)++] = 0x2a | 0x80;
2633 }
2634 }
2635
2636#elif defined (Q_WS_MAC)
2637
2638 /* if (muNumLockAdaptionCnt) ... - NumLock isn't implemented by Mac OS X so ignore it. */
2639 if (muCapsLockAdaptionCnt && (mCapsLock ^ !!(::GetCurrentEventKeyModifiers() & alphaLock)))
2640 {
2641 muCapsLockAdaptionCnt--;
2642 codes[(*count)++] = 0x3a;
2643 codes[(*count)++] = 0x3a | 0x80;
2644 /* Some keyboard layouts require shift to be pressed to break
2645 * capslock. For simplicity, only do this if shift is not
2646 * already held down. */
2647 if (mCapsLock && !(mPressedKeys [0x2a] & IsKeyPressed))
2648 {
2649 codes[(*count)++] = 0x2a;
2650 codes[(*count)++] = 0x2a | 0x80;
2651 }
2652 }
2653
2654#else
2655
2656//#warning Adapt VBoxConsoleView::fixModifierState
2657
2658#endif
2659
2660
2661}
2662
2663/**
2664 * Called on enter/exit seamless/fullscreen mode.
2665 */
2666void VBoxConsoleView::toggleFSMode (const QSize &aSize)
2667{
2668 if ((mGuestSupportsGraphics && mAutoresizeGuest) ||
2669 mMainWnd->isTrueSeamless() ||
2670 mMainWnd->isTrueFullscreen())
2671 {
2672 QSize newSize;
2673 if (aSize.isValid())
2674 {
2675 mNormalSize = aSize;
2676 newSize = maximumSize();
2677 }
2678 else
2679 newSize = mNormalSize;
2680 doResizeHint (newSize);
2681 }
2682
2683 /* Reactivate the console window to preserve the focus position.
2684 * Else focus will move to the mini-tool-bar. */
2685 activateWindow();
2686}
2687
2688/**
2689 * Get the current available desktop geometry for the console/framebuffer
2690 *
2691 * @returns the geometry. An empty rectangle means unrestricted.
2692 */
2693QRect VBoxConsoleView::desktopGeometry()
2694{
2695 QRect rc;
2696 switch (mDesktopGeo)
2697 {
2698 case DesktopGeo_Fixed:
2699 case DesktopGeo_Automatic:
2700 rc = QRect (0, 0,
2701 RT_MAX (mDesktopGeometry.width(), mStoredConsoleSize.width()),
2702 RT_MAX (mDesktopGeometry.height(), mStoredConsoleSize.height()));
2703 break;
2704 case DesktopGeo_Any:
2705 rc = QRect (0, 0, 0, 0);
2706 break;
2707 default:
2708 AssertMsgFailed (("Bad geometry type %d\n", mDesktopGeo));
2709 }
2710 return rc;
2711}
2712
2713QRegion VBoxConsoleView::lastVisibleRegion() const
2714{
2715 return mLastVisibleRegion;
2716}
2717
2718bool VBoxConsoleView::isAutoresizeGuestActive()
2719{
2720 return mGuestSupportsGraphics && mAutoresizeGuest;
2721}
2722
2723/**
2724 * Called on every key press and release (while in focus).
2725 *
2726 * @param aKey virtual scan code (virtual key on Win32 and KeySym on X11)
2727 * @param aScan hardware scan code
2728 * @param aFlags flags, a combination of Key* constants
2729 * @param aUniKey Unicode translation of the key. Optional.
2730 *
2731 * @return true to consume the event and false to pass it to Qt
2732 */
2733bool VBoxConsoleView::keyEvent (int aKey, uint8_t aScan, int aFlags,
2734 wchar_t *aUniKey/* = NULL*/)
2735{
2736#if 0
2737 {
2738 char buf [256];
2739 sprintf (buf, "aKey=%08X aScan=%02X aFlags=%08X",
2740 aKey, aScan, aFlags);
2741 mMainWnd->statusBar()->message (buf);
2742 }
2743#endif
2744
2745 const bool isHostKey = aKey == gs.hostKey();
2746
2747 LONG buf [16];
2748 LONG *codes = buf;
2749 uint count = 0;
2750 uint8_t whatPressed = 0;
2751
2752 if (!isHostKey && !mIsHostkeyPressed)
2753 {
2754 if (aFlags & KeyPrint)
2755 {
2756 static LONG PrintMake[] = { 0xE0, 0x2A, 0xE0, 0x37 };
2757 static LONG PrintBreak[] = { 0xE0, 0xB7, 0xE0, 0xAA };
2758 if (aFlags & KeyPressed)
2759 {
2760 codes = PrintMake;
2761 count = SIZEOF_ARRAY (PrintMake);
2762 }
2763 else
2764 {
2765 codes = PrintBreak;
2766 count = SIZEOF_ARRAY (PrintBreak);
2767 }
2768 }
2769 else if (aFlags & KeyPause)
2770 {
2771 if (aFlags & KeyPressed)
2772 {
2773 static LONG Pause[] = { 0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5 };
2774 codes = Pause;
2775 count = SIZEOF_ARRAY (Pause);
2776 }
2777 else
2778 {
2779 /* Pause shall not produce a break code */
2780 return true;
2781 }
2782 }
2783 else
2784 {
2785 if (aFlags & KeyPressed)
2786 {
2787 /* Check if the guest has the same view on the modifier keys (NumLock,
2788 * CapsLock, ScrollLock) as the X server. If not, send KeyPress events
2789 * to synchronize the state. */
2790 fixModifierState (codes, &count);
2791 }
2792
2793 /* Check if it's C-A-D and GUI/PassCAD is not true */
2794 if (!mPassCAD &&
2795 aScan == 0x53 /* Del */ &&
2796 ((mPressedKeys [0x38] & IsKeyPressed) /* Alt */ ||
2797 (mPressedKeys [0x38] & IsExtKeyPressed)) &&
2798 ((mPressedKeys [0x1d] & IsKeyPressed) /* Ctrl */ ||
2799 (mPressedKeys [0x1d] & IsExtKeyPressed)))
2800 {
2801 /* Use the C-A-D combination as a last resort to get the
2802 * keyboard and mouse back to the host when the user forgets
2803 * the Host Key. Note that it's always possible to send C-A-D
2804 * to the guest using the Host+Del combination. BTW, it would
2805 * be preferrable to completely ignore C-A-D in guests, but
2806 * that's not possible because we cannot predict what other
2807 * keys will be pressed next when one of C, A, D is held. */
2808
2809 if (isRunning() && mKbdCaptured)
2810 {
2811 captureKbd (false);
2812 if (!(mMouseAbsolute && mMouseIntegration))
2813 captureMouse (false);
2814 }
2815
2816 return true;
2817 }
2818
2819 /* process the scancode and update the table of pressed keys */
2820 whatPressed = IsKeyPressed;
2821
2822 if (aFlags & KeyExtended)
2823 {
2824 codes [count++] = 0xE0;
2825 whatPressed = IsExtKeyPressed;
2826 }
2827
2828 if (aFlags & KeyPressed)
2829 {
2830 codes [count++] = aScan;
2831 mPressedKeys [aScan] |= whatPressed;
2832 }
2833 else
2834 {
2835 /* if we haven't got this key's press message, we ignore its
2836 * release */
2837 if (!(mPressedKeys [aScan] & whatPressed))
2838 return true;
2839 codes [count++] = aScan | 0x80;
2840 mPressedKeys [aScan] &= ~whatPressed;
2841 }
2842
2843 if (mKbdCaptured)
2844 mPressedKeys [aScan] |= IsKbdCaptured;
2845 else
2846 mPressedKeys [aScan] &= ~IsKbdCaptured;
2847 }
2848 }
2849 else
2850 {
2851 /* currently this is used in winLowKeyboardEvent() only */
2852 hostkey_in_capture = mKbdCaptured;
2853 }
2854
2855 bool emitSignal = false;
2856 int hotkey = 0;
2857
2858 /* process the host key */
2859 if (aFlags & KeyPressed)
2860 {
2861 if (isHostKey)
2862 {
2863 if (!mIsHostkeyPressed)
2864 {
2865 mIsHostkeyPressed = mIsHostkeyAlone = true;
2866 if (isRunning())
2867 saveKeyStates();
2868 emitSignal = true;
2869 }
2870 }
2871 else
2872 {
2873 if (mIsHostkeyPressed)
2874 {
2875 if (mIsHostkeyAlone)
2876 {
2877 hotkey = aKey;
2878 mIsHostkeyAlone = false;
2879 }
2880 }
2881 }
2882 }
2883 else
2884 {
2885 if (isHostKey)
2886 {
2887 if (mIsHostkeyPressed)
2888 {
2889 mIsHostkeyPressed = false;
2890
2891 if (mIsHostkeyAlone)
2892 {
2893 if (isPaused())
2894 {
2895 vboxProblem().remindAboutPausedVMInput();
2896 }
2897 else
2898 if (isRunning())
2899 {
2900 bool captured = mKbdCaptured;
2901 bool ok = true;
2902 if (!captured)
2903 {
2904 /* temporarily disable auto capture that will take
2905 * place after this dialog is dismissed because
2906 * the capture state is to be defined by the
2907 * dialog result itself */
2908 mDisableAutoCapture = true;
2909 bool autoConfirmed = false;
2910 ok = vboxProblem().confirmInputCapture (&autoConfirmed);
2911 if (autoConfirmed)
2912 mDisableAutoCapture = false;
2913 /* otherwise, the disable flag will be reset in
2914 * the next console view's foucs in event (since
2915 * may happen asynchronously on some platforms,
2916 * after we return from this code) */
2917 }
2918
2919 if (ok)
2920 {
2921 captureKbd (!captured, false);
2922 if (!(mMouseAbsolute && mMouseIntegration))
2923 {
2924#ifdef Q_WS_X11
2925 /* make sure that pending FocusOut events from the
2926 * previous message box are handled, otherwise the
2927 * mouse is immediately ungrabbed. */
2928 qApp->processEvents();
2929#endif
2930 captureMouse (mKbdCaptured);
2931 }
2932 }
2933 }
2934 }
2935
2936 if (isRunning())
2937 sendChangedKeyStates();
2938
2939 emitSignal = true;
2940 }
2941 }
2942 else
2943 {
2944 if (mIsHostkeyPressed)
2945 mIsHostkeyAlone = false;
2946 }
2947 }
2948
2949 /* emit the keyboard state change signal */
2950 if (emitSignal)
2951 emitKeyboardStateChanged();
2952
2953 /* Process Host+<key> shortcuts. currently, <key> is limited to
2954 * alphanumeric chars. Other Host+<key> combinations are handled in
2955 * event(). */
2956 if (hotkey)
2957 {
2958 bool processed = false;
2959#if defined (Q_WS_WIN32)
2960 NOREF(aUniKey);
2961 int n = GetKeyboardLayoutList (0, NULL);
2962 Assert (n);
2963 HKL *list = new HKL [n];
2964 GetKeyboardLayoutList (n, list);
2965 for (int i = 0; i < n && !processed; i++)
2966 {
2967 wchar_t ch;
2968 static BYTE keys [256] = {0};
2969 if (!ToUnicodeEx (hotkey, 0, keys, &ch, 1, 0, list [i]) == 1)
2970 ch = 0;
2971 if (ch)
2972 processed = processHotKey (QKeySequence (Qt::UNICODE_ACCEL +
2973 QChar (ch).toUpper().unicode()),
2974 mMainWnd->menuBar()->actions());
2975 }
2976 delete[] list;
2977#elif defined (Q_WS_X11)
2978 NOREF(aUniKey);
2979 Display *display = QX11Info::display();
2980 int keysyms_per_keycode = getKeysymsPerKeycode();
2981 KeyCode kc = XKeysymToKeycode (display, aKey);
2982 // iterate over the first level (not shifted) keysyms in every group
2983 for (int i = 0; i < keysyms_per_keycode && !processed; i += 2)
2984 {
2985 KeySym ks = XKeycodeToKeysym (display, kc, i);
2986 char ch = 0;
2987 if (!XkbTranslateKeySym (display, &ks, 0, &ch, 1, NULL) == 1)
2988 ch = 0;
2989 if (ch)
2990 {
2991 QChar c = QString::fromLocal8Bit (&ch, 1) [0];
2992 processed = processHotKey (QKeySequence (Qt::UNICODE_ACCEL +
2993 c.toUpper().unicode()),
2994 mMainWnd->menuBar()->actions());
2995 }
2996 }
2997#elif defined (Q_WS_MAC)
2998 if (aUniKey && aUniKey [0] && !aUniKey [1])
2999 processed = processHotKey (QKeySequence (Qt::UNICODE_ACCEL +
3000 QChar (aUniKey [0]).toUpper().unicode()),
3001 mMainWnd->menuBar()->actions());
3002
3003 /* Don't consider the hot key as pressed since the guest never saw
3004 * it. (probably a generic thing) */
3005 mPressedKeys [aScan] &= ~whatPressed;
3006#endif
3007
3008 /* grab the key from Qt if processed, or pass it to Qt otherwise
3009 * in order to process non-alphanumeric keys in event(), after they are
3010 * converted to Qt virtual keys. */
3011 return processed;
3012 }
3013
3014 /* no more to do, if the host key is in action or the VM is paused */
3015 if (mIsHostkeyPressed || isHostKey || isPaused())
3016 {
3017 /* grab the key from Qt and from VM if it's a host key,
3018 * otherwise just pass it to Qt */
3019 return isHostKey;
3020 }
3021
3022 CKeyboard keyboard = mConsole.GetKeyboard();
3023 Assert (!keyboard.isNull());
3024
3025#if defined (Q_WS_WIN32)
3026 /* send pending WM_PAINT events */
3027 ::UpdateWindow (viewport()->winId());
3028#endif
3029
3030#if 0
3031 {
3032 char buf [256];
3033 sprintf (buf, "*** SCANS: ");
3034 for (uint i = 0; i < count; ++ i)
3035 sprintf (buf + strlen (buf), "%02X ", codes [i]);
3036 mMainWnd->statusBar()->message (buf);
3037 LogFlow (("%s\n", buf));
3038 }
3039#endif
3040
3041 std::vector <LONG> scancodes(codes, &codes[count]);
3042 keyboard.PutScancodes (QVector<LONG>::fromStdVector(scancodes));
3043
3044 /* grab the key from Qt */
3045 return true;
3046}
3047
3048/**
3049 * Called on every mouse/wheel move and button press/release.
3050 *
3051 * @return true to consume the event and false to pass it to Qt
3052 */
3053bool VBoxConsoleView::mouseEvent (int aType, const QPoint &aPos, const QPoint &aGlobalPos,
3054 Qt::MouseButtons aButtons, Qt::KeyboardModifiers aModifiers,
3055 int aWheelDelta, Qt::Orientation aWheelDir)
3056{
3057#if 1
3058
3059 LogRel3(("%s: type=%03d x=%03d y=%03d btns=%08X wdelta=%03d wdir=%s\n",
3060 __PRETTY_FUNCTION__ , aType, aPos.x(), aPos.y(),
3061 (aButtons & Qt::LeftButton ? 1 : 0)
3062 | (aButtons & Qt::RightButton ? 2 : 0)
3063 | (aButtons & Qt::MidButton ? 4 : 0)
3064 | (aButtons & Qt::XButton1 ? 8 : 0)
3065 | (aButtons & Qt::XButton2 ? 16 : 0),
3066 aWheelDelta,
3067 aWheelDir == Qt::Horizontal ? "Horizontal"
3068 : aWheelDir == Qt::Vertical ? "Vertical" : "Unknown"));
3069 Q_UNUSED (aModifiers);
3070#else
3071 Q_UNUSED (aModifiers);
3072#endif
3073
3074 int state = 0;
3075 if (aButtons & Qt::LeftButton)
3076 state |= KMouseButtonState_LeftButton;
3077 if (aButtons & Qt::RightButton)
3078 state |= KMouseButtonState_RightButton;
3079 if (aButtons & Qt::MidButton)
3080 state |= KMouseButtonState_MiddleButton;
3081 if (aButtons & Qt::XButton1)
3082 state |= KMouseButtonState_XButton1;
3083 if (aButtons & Qt::XButton2)
3084 state |= KMouseButtonState_XButton2;
3085
3086#ifdef Q_WS_MAC
3087 /* Simulate the right click on
3088 * Host+Left Mouse */
3089 if (mIsHostkeyPressed &&
3090 mIsHostkeyAlone &&
3091 state == KMouseButtonState_LeftButton)
3092 state = KMouseButtonState_RightButton;
3093#endif /* Q_WS_MAC */
3094
3095 int wheelVertical = 0;
3096 int wheelHorizontal = 0;
3097 if (aWheelDir == Qt::Vertical)
3098 {
3099 /* the absolute value of wheel delta is 120 units per every wheel
3100 * move; positive deltas correspond to counterclockwize rotations
3101 * (usually up), negative -- to clockwize (usually down). */
3102 wheelVertical = - (aWheelDelta / 120);
3103 }
3104 else if (aWheelDir == Qt::Horizontal)
3105 wheelHorizontal = aWheelDelta / 120;
3106
3107 if (mMouseCaptured)
3108 {
3109#ifdef Q_WS_WIN32
3110 /* send pending WM_PAINT events */
3111 ::UpdateWindow (viewport()->winId());
3112#endif
3113
3114 CMouse mouse = mConsole.GetMouse();
3115 mouse.PutMouseEvent (aGlobalPos.x() - mLastPos.x(),
3116 aGlobalPos.y() - mLastPos.y(),
3117 wheelVertical, wheelHorizontal, state);
3118
3119#if defined (Q_WS_MAC)
3120 /*
3121 * Keep the mouse from leaving the widget.
3122 *
3123 * This is a bit tricky to get right because if it escapes we won't necessarily
3124 * get mouse events any longer and can warp it back. So, we keep safety zone
3125 * of up to 300 pixels around the borders of the widget to prevent this from
3126 * happening. Also, the mouse is warped back to the center of the widget.
3127 *
3128 * (Note, aPos seems to be unreliable, it caused endless recursion here at one points...)
3129 * (Note, synergy and other remote clients might not like this cursor warping.)
3130 */
3131 QRect rect = viewport()->visibleRegion().boundingRect();
3132 QPoint pw = viewport()->mapToGlobal (viewport()->pos());
3133 rect.translate (pw.x(), pw.y());
3134
3135 QRect dpRect = QApplication::desktop()->screenGeometry (viewport());
3136 if (rect.intersects (dpRect))
3137 rect = rect.intersect (dpRect);
3138
3139 int wsafe = rect.width() / 6;
3140 rect.setWidth (rect.width() - wsafe * 2);
3141 rect.setLeft (rect.left() + wsafe);
3142
3143 int hsafe = rect.height() / 6;
3144 rect.setWidth (rect.height() - hsafe * 2);
3145 rect.setTop (rect.top() + hsafe);
3146
3147 if (rect.contains (aGlobalPos, true))
3148 mLastPos = aGlobalPos;
3149 else
3150 {
3151 mLastPos = rect.center();
3152 QCursor::setPos (mLastPos);
3153 }
3154
3155#else /* !Q_WS_MAC */
3156
3157 /* "jerk" the mouse by bringing it to the opposite side
3158 * to simulate the endless moving */
3159
3160#ifdef Q_WS_WIN32
3161 int we = viewport()->width() - 1;
3162 int he = viewport()->height() - 1;
3163 QPoint p = aPos;
3164 if (aPos.x() == 0)
3165 p.setX (we - 1);
3166 else if (aPos.x() == we)
3167 p.setX (1);
3168 if (aPos.y() == 0 )
3169 p.setY (he - 1);
3170 else if (aPos.y() == he)
3171 p.setY (1);
3172
3173 if (p != aPos)
3174 {
3175 mLastPos = viewport()->mapToGlobal (p);
3176 QCursor::setPos (mLastPos);
3177 }
3178 else
3179 {
3180 mLastPos = aGlobalPos;
3181 }
3182#else
3183 int we = QApplication::desktop()->width() - 1;
3184 int he = QApplication::desktop()->height() - 1;
3185 QPoint p = aGlobalPos;
3186 if (aGlobalPos.x() == 0)
3187 p.setX (we - 1);
3188 else if (aGlobalPos.x() == we)
3189 p.setX( 1 );
3190 if (aGlobalPos.y() == 0)
3191 p.setY (he - 1);
3192 else if (aGlobalPos.y() == he)
3193 p.setY (1);
3194
3195 if (p != aGlobalPos)
3196 {
3197 mLastPos = p;
3198 QCursor::setPos (mLastPos);
3199 }
3200 else
3201 {
3202 mLastPos = aGlobalPos;
3203 }
3204#endif
3205#endif /* !Q_WS_MAC */
3206 return true; /* stop further event handling */
3207 }
3208 else /* !mMouseCaptured */
3209 {
3210 if (mMainWnd->isTrueFullscreen())
3211 {
3212 if (mode != VBoxDefs::SDLMode)
3213 {
3214 /* try to automatically scroll the guest canvas if the
3215 * mouse is on the screen border */
3216 /// @todo (r=dmik) better use a timer for autoscroll
3217 QRect scrGeo = QApplication::desktop()->screenGeometry (this);
3218 int dx = 0, dy = 0;
3219 if (scrGeo.width() < contentsWidth())
3220 {
3221 if (scrGeo.left() == aGlobalPos.x()) dx = -1;
3222 if (scrGeo.right() == aGlobalPos.x()) dx = +1;
3223 }
3224 if (scrGeo.height() < contentsHeight())
3225 {
3226 if (scrGeo.top() == aGlobalPos.y()) dy = -1;
3227 if (scrGeo.bottom() == aGlobalPos.y()) dy = +1;
3228 }
3229 if (dx || dy)
3230 scrollBy (dx, dy);
3231 }
3232 }
3233
3234 if (mMouseAbsolute && mMouseIntegration)
3235 {
3236 int cw = contentsWidth(), ch = contentsHeight();
3237 int vw = visibleWidth(), vh = visibleHeight();
3238
3239 if (mode != VBoxDefs::SDLMode)
3240 {
3241 /* try to automatically scroll the guest canvas if the
3242 * mouse goes outside its visible part */
3243
3244 int dx = 0;
3245 if (aPos.x() > vw) dx = aPos.x() - vw;
3246 else if (aPos.x() < 0) dx = aPos.x();
3247 int dy = 0;
3248 if (aPos.y() > vh) dy = aPos.y() - vh;
3249 else if (aPos.y() < 0) dy = aPos.y();
3250 if (dx != 0 || dy != 0) scrollBy (dx, dy);
3251 }
3252
3253 QPoint cpnt = viewportToContents (aPos);
3254 if (cpnt.x() < 0) cpnt.setX (0);
3255 else if (cpnt.x() > cw) cpnt.setX (cw);
3256 if (cpnt.y() < 0) cpnt.setY (0);
3257 else if (cpnt.y() > ch) cpnt.setY (ch);
3258
3259 CMouse mouse = mConsole.GetMouse();
3260 mouse.PutMouseEventAbsolute (cpnt.x(), cpnt.y(), wheelVertical,
3261 wheelHorizontal, state);
3262 return true; /* stop further event handling */
3263 }
3264 else
3265 {
3266 if (hasFocus() &&
3267 (aType == QEvent::MouseButtonRelease &&
3268 aButtons == Qt::NoButton))
3269 {
3270 if (isPaused())
3271 {
3272 vboxProblem().remindAboutPausedVMInput();
3273 }
3274 else if (isRunning())
3275 {
3276 /* temporarily disable auto capture that will take
3277 * place after this dialog is dismissed because
3278 * the capture state is to be defined by the
3279 * dialog result itself */
3280 mDisableAutoCapture = true;
3281 bool autoConfirmed = false;
3282 bool ok = vboxProblem().confirmInputCapture (&autoConfirmed);
3283 if (autoConfirmed)
3284 mDisableAutoCapture = false;
3285 /* otherwise, the disable flag will be reset in
3286 * the next console view's foucs in event (since
3287 * may happen asynchronously on some platforms,
3288 * after we return from this code) */
3289
3290 if (ok)
3291 {
3292#ifdef Q_WS_X11
3293 /* make sure that pending FocusOut events from the
3294 * previous message box are handled, otherwise the
3295 * mouse is immediately ungrabbed again */
3296 qApp->processEvents();
3297#endif
3298 captureKbd (true);
3299 captureMouse (true);
3300 }
3301 }
3302 }
3303 }
3304 }
3305
3306 return false;
3307}
3308
3309void VBoxConsoleView::onStateChange (KMachineState state)
3310{
3311 switch (state)
3312 {
3313 case KMachineState_Paused:
3314 case KMachineState_TeleportingPausedVM:
3315 {
3316 if ( mode != VBoxDefs::TimerMode
3317 && mFrameBuf
3318 && ( state != KMachineState_TeleportingPausedVM
3319 || mLastState != KMachineState_Teleporting)
3320 )
3321 {
3322 /*
3323 * Take a screen snapshot. Note that TakeScreenShot() always
3324 * needs a 32bpp image
3325 */
3326 QImage shot = QImage (mFrameBuf->width(), mFrameBuf->height(), QImage::Format_RGB32);
3327 CDisplay dsp = mConsole.GetDisplay();
3328 dsp.TakeScreenShot (shot.bits(), shot.width(), shot.height());
3329 /*
3330 * TakeScreenShot() may fail if, e.g. the Paused notification
3331 * was delivered after the machine execution was resumed. It's
3332 * not fatal.
3333 */
3334 if (dsp.isOk())
3335 {
3336 dimImage (shot);
3337 mPausedShot = QPixmap::fromImage (shot);
3338 /* fully repaint to pick up mPausedShot */
3339 repaint();
3340 }
3341 }
3342 /* fall through */
3343 }
3344 case KMachineState_Stuck:
3345 {
3346 /* reuse the focus event handler to uncapture everything */
3347 if (hasFocus())
3348 focusEvent (false /* aHasFocus*/, false /* aReleaseHostKey */);
3349 break;
3350 }
3351 case KMachineState_Running:
3352 {
3353 if ( mLastState == KMachineState_Paused
3354 || mLastState == KMachineState_TeleportingPausedVM
3355 )
3356 {
3357 if (mode != VBoxDefs::TimerMode && mFrameBuf)
3358 {
3359 /* reset the pixmap to free memory */
3360 mPausedShot = QPixmap ();
3361 /*
3362 * ask for full guest display update (it will also update
3363 * the viewport through IFramebuffer::NotifyUpdate)
3364 */
3365 CDisplay dsp = mConsole.GetDisplay();
3366 dsp.InvalidateAndUpdate();
3367 }
3368 }
3369 /* reuse the focus event handler to capture input */
3370 if (hasFocus())
3371 focusEvent (true /* aHasFocus */);
3372 break;
3373 }
3374 default:
3375 break;
3376 }
3377
3378 mLastState = state;
3379}
3380
3381void VBoxConsoleView::doRefresh()
3382{
3383 viewport()->repaint();
3384}
3385
3386void VBoxConsoleView::resizeEvent (QResizeEvent *)
3387{
3388 updateSliders();
3389#if defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA)
3390 QRect r = viewport()->geometry();
3391// printf ("qt resize: %d %d %d %d\n", r.x(), r.y(), r.width(), r.height());
3392 PostBoundsChanged (r);
3393#endif /* Q_WS_MAC */
3394}
3395
3396void VBoxConsoleView::moveEvent (QMoveEvent *)
3397{
3398#if defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA)
3399 QRect r = viewport()->geometry();
3400// printf ("qt resize: %d %d %d %d\n", r.x(), r.y(), r.width(), r.height());
3401 PostBoundsChanged (r);
3402#endif /* Q_WS_MAC */
3403}
3404
3405void VBoxConsoleView::paintEvent (QPaintEvent *pe)
3406{
3407 if (mPausedShot.isNull())
3408 {
3409 /* delegate the paint function to the VBoxFrameBuffer interface */
3410 if (mFrameBuf)
3411 mFrameBuf->paintEvent (pe);
3412#ifdef Q_WS_MAC
3413 /* Update the dock icon if we are in the running state */
3414 if (isRunning())
3415 updateDockIcon();
3416#endif
3417 return;
3418 }
3419
3420#ifdef VBOX_GUI_USE_QUARTZ2D
3421 if (mode == VBoxDefs::Quartz2DMode && mFrameBuf)
3422 {
3423 mFrameBuf->paintEvent (pe);
3424 updateDockIcon();
3425 }
3426 else
3427#endif
3428 {
3429 /* we have a snapshot for the paused state */
3430 QRect r = pe->rect().intersect (viewport()->rect());
3431 /* We have to disable paint on screen if we are using the regular painter */
3432 bool paintOnScreen = viewport()->testAttribute (Qt::WA_PaintOnScreen);
3433 viewport()->setAttribute (Qt::WA_PaintOnScreen, false);
3434 QPainter pnt (viewport());
3435 pnt.drawPixmap (r.x(), r.y(), mPausedShot,
3436 r.x() + contentsX(), r.y() + contentsY(),
3437 r.width(), r.height());
3438 /* Restore the attribute to its previous state */
3439 viewport()->setAttribute (Qt::WA_PaintOnScreen, paintOnScreen);
3440#ifdef Q_WS_MAC
3441 updateDockIcon();
3442#endif
3443 }
3444
3445}
3446
3447/**
3448 * Captures the keyboard. When captured, no keyboard input reaches the host
3449 * system (including most system combinations like Alt-Tab).
3450 *
3451 * @param aCapture true to capture, false to uncapture.
3452 * @param aEmitSignal Whether to emit keyboardStateChanged() or not.
3453 */
3454void VBoxConsoleView::captureKbd (bool aCapture, bool aEmitSignal /* = true */)
3455{
3456 AssertMsg (mAttached, ("Console must be attached"));
3457
3458 if (mKbdCaptured == aCapture)
3459 return;
3460
3461 /* On Win32, keyboard grabbing is ineffective, a low-level keyboard hook is
3462 * used instead. On X11, we use XGrabKey instead of XGrabKeyboard (called
3463 * by QWidget::grabKeyboard()) because the latter causes problems under
3464 * metacity 2.16 (in particular, due to a bug, a window cannot be moved
3465 * using the mouse if it is currently grabing the keyboard). On Mac OS X,
3466 * we use the Qt methods + disabling global hot keys + watching modifiers
3467 * (for right/left separation). */
3468#if defined (Q_WS_WIN32)
3469 /**/
3470#elif defined (Q_WS_X11)
3471 if (aCapture)
3472 XGrabKey (QX11Info::display(), AnyKey, AnyModifier,
3473 window()->winId(), False,
3474 GrabModeAsync, GrabModeAsync);
3475 else
3476 XUngrabKey (QX11Info::display(), AnyKey, AnyModifier,
3477 window()->winId());
3478#elif defined (Q_WS_MAC)
3479 if (aCapture)
3480 {
3481 ::DarwinDisableGlobalHotKeys (true);
3482 grabKeyboard();
3483 }
3484 else
3485 {
3486 ::DarwinDisableGlobalHotKeys (false);
3487 releaseKeyboard();
3488 }
3489#else
3490 if (aCapture)
3491 grabKeyboard();
3492 else
3493 releaseKeyboard();
3494#endif
3495
3496 mKbdCaptured = aCapture;
3497
3498 if (aEmitSignal)
3499 emitKeyboardStateChanged();
3500}
3501
3502/**
3503 * Captures the host mouse pointer. When captured, the mouse pointer is
3504 * unavailable to the host applications.
3505 *
3506 * @param aCapture true to capture, false to uncapture.
3507 * @param aEmitSignal Whether to emit mouseStateChanged() or not.
3508 */
3509void VBoxConsoleView::captureMouse (bool aCapture, bool aEmitSignal /* = true */)
3510{
3511 AssertMsg (mAttached, ("Console must be attached"));
3512
3513 if (mMouseCaptured == aCapture)
3514 return;
3515
3516 if (aCapture)
3517 {
3518 /* memorize the host position where the cursor was captured */
3519 mCapturedPos = QCursor::pos();
3520#ifdef Q_WS_WIN32
3521 viewport()->setCursor (QCursor (Qt::BlankCursor));
3522 /* move the mouse to the center of the visible area */
3523 QCursor::setPos (mapToGlobal (visibleRegion().boundingRect().center()));
3524 mLastPos = QCursor::pos();
3525#elif defined (Q_WS_MAC)
3526 /* move the mouse to the center of the visible area */
3527 mLastPos = mapToGlobal (visibleRegion().boundingRect().center());
3528 QCursor::setPos (mLastPos);
3529 /* grab all mouse events. */
3530 viewport()->grabMouse();
3531#else
3532 viewport()->grabMouse();
3533 mLastPos = QCursor::pos();
3534#endif
3535 }
3536 else
3537 {
3538#ifndef Q_WS_WIN32
3539 viewport()->releaseMouse();
3540#endif
3541 /* release mouse buttons */
3542 CMouse mouse = mConsole.GetMouse();
3543 mouse.PutMouseEvent (0, 0, 0, 0 /* Horizontal wheel */, 0);
3544 }
3545
3546 mMouseCaptured = aCapture;
3547
3548 updateMouseClipping();
3549
3550 if (aEmitSignal)
3551 emitMouseStateChanged();
3552}
3553
3554/**
3555 * Searches for a menu item with a given hot key (shortcut). If the item
3556 * is found, activates it and returns true. Otherwise returns false.
3557 */
3558bool VBoxConsoleView::processHotKey (const QKeySequence &aKey,
3559 const QList <QAction*> &aData)
3560{
3561 foreach (QAction *pAction, aData)
3562 {
3563 if (QMenu *menu = pAction->menu())
3564 {
3565 /* Process recursively for each sub-menu */
3566 if (processHotKey (aKey, menu->actions()))
3567 return true;
3568 }
3569 else
3570 {
3571 QString hotkey = VBoxGlobal::extractKeyFromActionText (pAction->text());
3572 if (pAction->isEnabled() && !hotkey.isEmpty())
3573 {
3574 if (aKey.matches (QKeySequence (hotkey)) == QKeySequence::ExactMatch)
3575 {
3576 /* We asynchronously post a special event instead of calling
3577 * pAction->trigger() directly, to let key presses and
3578 * releases be processed correctly by Qt first.
3579 * Note: we assume that nobody will delete the menu item
3580 * corresponding to the key sequence, so that the pointer to
3581 * menu data posted along with the event will remain valid in
3582 * the event handler, at least until the main window is closed. */
3583 QApplication::postEvent (this, new ActivateMenuEvent (pAction));
3584 return true;
3585 }
3586 }
3587 }
3588 }
3589
3590 return false;
3591}
3592
3593/**
3594 * Send the KEY BREAK code to the VM for all currently pressed keys.
3595 *
3596 * @param aReleaseHostKey @c true to set the host key state to unpressed.
3597 */
3598void VBoxConsoleView::releaseAllPressedKeys (bool aReleaseHostKey /* = true*/)
3599{
3600 AssertMsg (mAttached, ("Console must be attached"));
3601
3602 CKeyboard keyboard = mConsole.GetKeyboard();
3603 bool fSentRESEND = false;
3604
3605 /* send a dummy scan code (RESEND) to prevent the guest OS from recognizing
3606 * a single key click (for ex., Alt) and performing an unwanted action
3607 * (for ex., activating the menu) when we release all pressed keys below.
3608 * Note, that it's just a guess that sending RESEND will give the desired
3609 * effect :), but at least it works with NT and W2k guests. */
3610
3611 /// @todo Sending 0xFE is responsible for the warning
3612 //
3613 // ``atkbd.c: Spurious NAK on isa0060/serio0. Some program might
3614 // be trying access hardware directly''
3615 //
3616 // on Linux guests (#1944). It might also be responsible for #1949. Don't
3617 // send this command unless we really have to release any key modifier.
3618 // --frank
3619
3620 for (uint i = 0; i < SIZEOF_ARRAY (mPressedKeys); i++)
3621 {
3622 if (mPressedKeys [i] & IsKeyPressed)
3623 {
3624 if (!fSentRESEND)
3625 {
3626 keyboard.PutScancode (0xFE);
3627 fSentRESEND = true;
3628 }
3629 keyboard.PutScancode (i | 0x80);
3630 }
3631 else if (mPressedKeys [i] & IsExtKeyPressed)
3632 {
3633 if (!fSentRESEND)
3634 {
3635 keyboard.PutScancode (0xFE);
3636 fSentRESEND = true;
3637 }
3638 QVector <LONG> codes (2);
3639 codes[0] = 0xE0;
3640 codes[1] = i | 0x80;
3641 keyboard.PutScancodes (codes);
3642 }
3643 mPressedKeys [i] = 0;
3644 }
3645
3646 if (aReleaseHostKey)
3647 mIsHostkeyPressed = false;
3648
3649#ifdef Q_WS_MAC
3650 /* clear most of the modifiers. */
3651 mDarwinKeyModifiers &=
3652 alphaLock | kEventKeyModifierNumLockMask |
3653 (aReleaseHostKey ? 0 : ::DarwinKeyCodeToDarwinModifierMask (gs.hostKey()));
3654#endif
3655
3656 emitKeyboardStateChanged();
3657}
3658
3659void VBoxConsoleView::saveKeyStates()
3660{
3661 ::memcpy (mPressedKeysCopy, mPressedKeys, sizeof (mPressedKeys));
3662}
3663
3664void VBoxConsoleView::sendChangedKeyStates()
3665{
3666 AssertMsg (mAttached, ("Console must be attached"));
3667
3668 QVector <LONG> codes (2);
3669 CKeyboard keyboard = mConsole.GetKeyboard();
3670 for (uint i = 0; i < SIZEOF_ARRAY (mPressedKeys); ++ i)
3671 {
3672 uint8_t os = mPressedKeysCopy [i];
3673 uint8_t ns = mPressedKeys [i];
3674 if ((os & IsKeyPressed) != (ns & IsKeyPressed))
3675 {
3676 codes [0] = i;
3677 if (!(ns & IsKeyPressed))
3678 codes[0] |= 0x80;
3679 keyboard.PutScancode (codes[0]);
3680 }
3681 else if ((os & IsExtKeyPressed) != (ns & IsExtKeyPressed))
3682 {
3683 codes [0] = 0xE0;
3684 codes [1] = i;
3685 if (!(ns & IsExtKeyPressed))
3686 codes [1] |= 0x80;
3687 keyboard.PutScancodes (codes);
3688 }
3689 }
3690}
3691
3692void VBoxConsoleView::updateMouseClipping()
3693{
3694 AssertMsg (mAttached, ("Console must be attached"));
3695
3696 if (mMouseCaptured)
3697 {
3698 viewport()->setCursor (QCursor (Qt::BlankCursor));
3699#ifdef Q_WS_WIN32
3700 QRect r = viewport()->rect();
3701 r.moveTopLeft (viewport()->mapToGlobal (QPoint (0, 0)));
3702 RECT rect = { r.left(), r.top(), r.right() + 1, r.bottom() + 1 };
3703 ::ClipCursor (&rect);
3704#endif
3705 }
3706 else
3707 {
3708#ifdef Q_WS_WIN32
3709 ::ClipCursor (NULL);
3710#endif
3711 /* return the cursor to where it was when we captured it and show it */
3712 QCursor::setPos (mCapturedPos);
3713 viewport()->unsetCursor();
3714 }
3715}
3716
3717void VBoxConsoleView::setPointerShape (MousePointerChangeEvent *me)
3718{
3719 if (me->shapeData() != NULL)
3720 {
3721 bool ok = false;
3722
3723 const uchar *srcAndMaskPtr = me->shapeData();
3724 uint andMaskSize = (me->width() + 7) / 8 * me->height();
3725 const uchar *srcShapePtr = me->shapeData() + ((andMaskSize + 3) & ~3);
3726 uint srcShapePtrScan = me->width() * 4;
3727
3728#if defined (Q_WS_WIN)
3729
3730 BITMAPV5HEADER bi;
3731 HBITMAP hBitmap;
3732 void *lpBits;
3733
3734 ::ZeroMemory (&bi, sizeof (BITMAPV5HEADER));
3735 bi.bV5Size = sizeof (BITMAPV5HEADER);
3736 bi.bV5Width = me->width();
3737 bi.bV5Height = - (LONG) me->height();
3738 bi.bV5Planes = 1;
3739 bi.bV5BitCount = 32;
3740 bi.bV5Compression = BI_BITFIELDS;
3741 // specifiy a supported 32 BPP alpha format for Windows XP
3742 bi.bV5RedMask = 0x00FF0000;
3743 bi.bV5GreenMask = 0x0000FF00;
3744 bi.bV5BlueMask = 0x000000FF;
3745 if (me->hasAlpha())
3746 bi.bV5AlphaMask = 0xFF000000;
3747 else
3748 bi.bV5AlphaMask = 0;
3749
3750 HDC hdc = GetDC (NULL);
3751
3752 // create the DIB section with an alpha channel
3753 hBitmap = CreateDIBSection (hdc, (BITMAPINFO *) &bi, DIB_RGB_COLORS,
3754 (void **) &lpBits, NULL, (DWORD) 0);
3755
3756 ReleaseDC (NULL, hdc);
3757
3758 HBITMAP hMonoBitmap = NULL;
3759 if (me->hasAlpha())
3760 {
3761 // create an empty mask bitmap
3762 hMonoBitmap = CreateBitmap (me->width(), me->height(), 1, 1, NULL);
3763 }
3764 else
3765 {
3766 /* Word aligned AND mask. Will be allocated and created if necessary. */
3767 uint8_t *pu8AndMaskWordAligned = NULL;
3768
3769 /* Width in bytes of the original AND mask scan line. */
3770 uint32_t cbAndMaskScan = (me->width() + 7) / 8;
3771
3772 if (cbAndMaskScan & 1)
3773 {
3774 /* Original AND mask is not word aligned. */
3775
3776 /* Allocate memory for aligned AND mask. */
3777 pu8AndMaskWordAligned = (uint8_t *)RTMemTmpAllocZ ((cbAndMaskScan + 1) * me->height());
3778
3779 Assert(pu8AndMaskWordAligned);
3780
3781 if (pu8AndMaskWordAligned)
3782 {
3783 /* According to MSDN the padding bits must be 0.
3784 * Compute the bit mask to set padding bits to 0 in the last byte of original AND mask.
3785 */
3786 uint32_t u32PaddingBits = cbAndMaskScan * 8 - me->width();
3787 Assert(u32PaddingBits < 8);
3788 uint8_t u8LastBytesPaddingMask = (uint8_t)(0xFF << u32PaddingBits);
3789
3790 Log(("u8LastBytesPaddingMask = %02X, aligned w = %d, width = %d, cbAndMaskScan = %d\n",
3791 u8LastBytesPaddingMask, (cbAndMaskScan + 1) * 8, me->width(), cbAndMaskScan));
3792
3793 uint8_t *src = (uint8_t *)srcAndMaskPtr;
3794 uint8_t *dst = pu8AndMaskWordAligned;
3795
3796 unsigned i;
3797 for (i = 0; i < me->height(); i++)
3798 {
3799 memcpy (dst, src, cbAndMaskScan);
3800
3801 dst[cbAndMaskScan - 1] &= u8LastBytesPaddingMask;
3802
3803 src += cbAndMaskScan;
3804 dst += cbAndMaskScan + 1;
3805 }
3806 }
3807 }
3808
3809 /* create the AND mask bitmap */
3810 hMonoBitmap = ::CreateBitmap (me->width(), me->height(), 1, 1,
3811 pu8AndMaskWordAligned? pu8AndMaskWordAligned: srcAndMaskPtr);
3812
3813 if (pu8AndMaskWordAligned)
3814 {
3815 RTMemTmpFree (pu8AndMaskWordAligned);
3816 }
3817 }
3818
3819 Assert (hBitmap);
3820 Assert (hMonoBitmap);
3821 if (hBitmap && hMonoBitmap)
3822 {
3823 DWORD *dstShapePtr = (DWORD *) lpBits;
3824
3825 for (uint y = 0; y < me->height(); y ++)
3826 {
3827 memcpy (dstShapePtr, srcShapePtr, srcShapePtrScan);
3828 srcShapePtr += srcShapePtrScan;
3829 dstShapePtr += me->width();
3830 }
3831
3832 ICONINFO ii;
3833 ii.fIcon = FALSE;
3834 ii.xHotspot = me->xHot();
3835 ii.yHotspot = me->yHot();
3836 ii.hbmMask = hMonoBitmap;
3837 ii.hbmColor = hBitmap;
3838
3839 HCURSOR hAlphaCursor = CreateIconIndirect (&ii);
3840 Assert (hAlphaCursor);
3841 if (hAlphaCursor)
3842 {
3843 viewport()->setCursor (QCursor (hAlphaCursor));
3844 ok = true;
3845 if (mAlphaCursor)
3846 DestroyIcon (mAlphaCursor);
3847 mAlphaCursor = hAlphaCursor;
3848 }
3849 }
3850
3851 if (hMonoBitmap)
3852 DeleteObject (hMonoBitmap);
3853 if (hBitmap)
3854 DeleteObject (hBitmap);
3855
3856#elif defined (Q_WS_X11) && !defined (VBOX_WITHOUT_XCURSOR)
3857
3858 XcursorImage *img = XcursorImageCreate (me->width(), me->height());
3859 Assert (img);
3860 if (img)
3861 {
3862 img->xhot = me->xHot();
3863 img->yhot = me->yHot();
3864
3865 XcursorPixel *dstShapePtr = img->pixels;
3866
3867 for (uint y = 0; y < me->height(); y ++)
3868 {
3869 memcpy (dstShapePtr, srcShapePtr, srcShapePtrScan);
3870
3871 if (!me->hasAlpha())
3872 {
3873 /* convert AND mask to the alpha channel */
3874 uchar byte = 0;
3875 for (uint x = 0; x < me->width(); x ++)
3876 {
3877 if (!(x % 8))
3878 byte = *(srcAndMaskPtr ++);
3879 else
3880 byte <<= 1;
3881
3882 if (byte & 0x80)
3883 {
3884 /* Linux doesn't support inverted pixels (XOR ops,
3885 * to be exact) in cursor shapes, so we detect such
3886 * pixels and always replace them with black ones to
3887 * make them visible at least over light colors */
3888 if (dstShapePtr [x] & 0x00FFFFFF)
3889 dstShapePtr [x] = 0xFF000000;
3890 else
3891 dstShapePtr [x] = 0x00000000;
3892 }
3893 else
3894 dstShapePtr [x] |= 0xFF000000;
3895 }
3896 }
3897
3898 srcShapePtr += srcShapePtrScan;
3899 dstShapePtr += me->width();
3900 }
3901
3902 Cursor cur = XcursorImageLoadCursor (QX11Info::display(), img);
3903 Assert (cur);
3904 if (cur)
3905 {
3906 viewport()->setCursor (QCursor (cur));
3907 ok = true;
3908 }
3909
3910 XcursorImageDestroy (img);
3911 }
3912
3913#elif defined(Q_WS_MAC)
3914
3915 /* Create a ARGB image out of the shape data. */
3916 QImage image (me->width(), me->height(), QImage::Format_ARGB32);
3917 const uint8_t* pbSrcMask = static_cast<const uint8_t*> (srcAndMaskPtr);
3918 unsigned cbSrcMaskLine = RT_ALIGN (me->width(), 8) / 8;
3919 for (unsigned int y = 0; y < me->height(); ++y)
3920 {
3921 for (unsigned int x = 0; x < me->width(); ++x)
3922 {
3923 unsigned int color = ((unsigned int*)srcShapePtr)[y*me->width()+x];
3924 /* If the alpha channel isn't in the shape data, we have to
3925 * create them from the and-mask. This is a bit field where 1
3926 * represent transparency & 0 opaque respectively. */
3927 if (!me->hasAlpha())
3928 {
3929 if (!(pbSrcMask[x / 8] & (1 << (7 - (x % 8)))))
3930 color |= 0xff000000;
3931 else
3932 {
3933 /* This isn't quite right, but it's the best we can do I
3934 * think... */
3935 if (color & 0x00ffffff)
3936 color = 0xff000000;
3937 else
3938 color = 0x00000000;
3939 }
3940 }
3941 image.setPixel (x, y, color);
3942 }
3943 /* Move one scanline forward. */
3944 pbSrcMask += cbSrcMaskLine;
3945 }
3946 /* Set the new cursor */
3947 QCursor cursor (QPixmap::fromImage (image),
3948 me->xHot(), me->yHot());
3949 viewport()->setCursor (cursor);
3950 ok = true;
3951 NOREF (srcShapePtrScan);
3952
3953#else
3954
3955# warning "port me"
3956
3957#endif
3958 if (ok)
3959 mLastCursor = viewport()->cursor();
3960 else
3961 viewport()->unsetCursor();
3962 }
3963 else
3964 {
3965 /*
3966 * We did not get any shape data
3967 */
3968 if (me->isVisible())
3969 {
3970 viewport()->setCursor (mLastCursor);
3971 }
3972 else
3973 {
3974 viewport()->setCursor (Qt::BlankCursor);
3975 }
3976 }
3977 mHideHostPointer = !me->isVisible();
3978}
3979
3980inline QRgb qRgbIntensity (QRgb rgb, int mul, int div)
3981{
3982 int r = qRed (rgb);
3983 int g = qGreen (rgb);
3984 int b = qBlue (rgb);
3985 return qRgb (mul * r / div, mul * g / div, mul * b / div);
3986}
3987
3988/* static */
3989void VBoxConsoleView::dimImage (QImage &img)
3990{
3991 for (int y = 0; y < img.height(); y ++) {
3992 if (y % 2) {
3993 if (img.depth() == 32) {
3994 for (int x = 0; x < img.width(); x ++) {
3995 int gray = qGray (img.pixel (x, y)) / 2;
3996 img.setPixel (x, y, qRgb (gray, gray, gray));
3997// img.setPixel (x, y, qRgbIntensity (img.pixel (x, y), 1, 2));
3998 }
3999 } else {
4000 ::memset (img.scanLine (y), 0, img.bytesPerLine());
4001 }
4002 } else {
4003 if (img.depth() == 32) {
4004 for (int x = 0; x < img.width(); x ++) {
4005 int gray = (2 * qGray (img.pixel (x, y))) / 3;
4006 img.setPixel (x, y, qRgb (gray, gray, gray));
4007// img.setPixel (x, y, qRgbIntensity (img.pixel(x, y), 2, 3));
4008 }
4009 }
4010 }
4011 }
4012}
4013
4014void VBoxConsoleView::doResizeHint (const QSize &aToSize)
4015{
4016 if (mGuestSupportsGraphics && mAutoresizeGuest)
4017 {
4018 /* If this slot is invoked directly then use the passed size
4019 * otherwise get the available size for the guest display.
4020 * We assume here that the centralWidget() contains this view only
4021 * and gives it all available space. */
4022 QSize sz (aToSize.isValid() ? aToSize : mMainWnd->centralWidget()->size());
4023 if (!aToSize.isValid())
4024 sz -= QSize (frameWidth() * 2, frameWidth() * 2);
4025 /* Do not send out useless hints. */
4026 if ((sz.width() == mStoredConsoleSize.width()) &&
4027 (sz.height() == mStoredConsoleSize.height()))
4028 return;
4029 /* We only actually send the hint if
4030 * 1) the autoresize property is set to true and
4031 * 2) either an explicit new size was given (e.g. if the request
4032 * was triggered directly by a console resize event) or if no
4033 * explicit size was specified but a resize is flagged as being
4034 * needed (e.g. the autoresize was just enabled and the console
4035 * was resized while it was disabled). */
4036 if (mAutoresizeGuest &&
4037 (aToSize.isValid() || mDoResize))
4038 {
4039 LogFlowFunc (("Will suggest %d x %d\n", sz.width(), sz.height()));
4040
4041 /* Remember the new size. */
4042 storeConsoleSize (sz.width(), sz.height());
4043
4044 mConsole.GetDisplay().SetVideoModeHint (sz.width(), sz.height(), 0, 0);
4045 }
4046 /* we have resized now... */
4047 mDoResize = false;
4048 }
4049}
4050
4051
4052/* If the desktop geometry is set automatically, this will update it. */
4053void VBoxConsoleView::doResizeDesktop (int)
4054{
4055 calculateDesktopGeometry();
4056}
4057
4058/**
4059 * Store the current size of the console (i.e. the viewport to the guest).
4060 * This has two purposes. One is to suppress unwanted resize events for
4061 * which the new size is the same as the stored size. The other is to expand
4062 * the maximum size to which we will let the guest resize itself. It makes
4063 * no sense to forbid guest resizes which are less than the current resolution
4064 * anyway.
4065 *
4066 * @param aWidth width of the resolution hint
4067 * @param aHeight height of the resolution hint
4068 */
4069void VBoxConsoleView::storeConsoleSize (int aWidth, int aHeight)
4070{
4071 LogFlowThisFunc (("aWidth=%d, aHeight=%d\n", aWidth, aHeight));
4072 mStoredConsoleSize = QRect (0, 0, aWidth, aHeight);
4073}
4074
4075/**
4076 * Do initial setup of desktop geometry restrictions on the guest framebuffer.
4077 * These determine the maximum size the guest framebuffer can take on.
4078 *
4079 * @note a hint from the host will always override these restrictions.
4080 *
4081 * @param aGeo Fixed - the guest has a fixed maximum framebuffer size
4082 * Automatic - we calculate the maximum size ourselves. The
4083 * calculations will not actually be done until
4084 * @a calculateDesktopGeometry is called, since
4085 * we don't initially have the information needed.
4086 * Any - any size is allowed
4087 * @param aWidth The maximum width for the guest screen or zero for no change
4088 * (only used for fixed geometry)
4089 * @param aHeight The maximum height for the guest screen or zero for no change
4090 * (only used for fixed geometry)
4091 */
4092void VBoxConsoleView::setDesktopGeometry (DesktopGeo aGeo, int aWidth, int aHeight)
4093{
4094 LogFlowThisFunc (("aGeo=%s, aWidth=%d, aHeight=%d\n",
4095 (aGeo == DesktopGeo_Fixed ? "Fixed" :
4096 aGeo == DesktopGeo_Automatic ? "Automatic" :
4097 aGeo == DesktopGeo_Any ? "Any" : "Invalid"),
4098 aWidth, aHeight));
4099 switch (aGeo)
4100 {
4101 case DesktopGeo_Fixed:
4102 mDesktopGeo = DesktopGeo_Fixed;
4103 if (aWidth != 0 && aHeight != 0)
4104 mDesktopGeometry = QRect (0, 0, aWidth, aHeight);
4105 else
4106 mDesktopGeometry = QRect (0, 0, 0, 0);
4107 storeConsoleSize (0, 0);
4108 break;
4109 case DesktopGeo_Automatic:
4110 mDesktopGeo = DesktopGeo_Automatic;
4111 mDesktopGeometry = QRect (0, 0, 0, 0);
4112 storeConsoleSize (0, 0);
4113 break;
4114 case DesktopGeo_Any:
4115 mDesktopGeo = DesktopGeo_Any;
4116 mDesktopGeometry = QRect (0, 0, 0, 0);
4117 break;
4118 default:
4119 AssertMsgFailed(("Invalid desktop geometry type %d\n", aGeo));
4120 mDesktopGeo = DesktopGeo_Invalid;
4121 }
4122}
4123
4124
4125/**
4126 * If we are in automatic mode, the geometry restrictions will be recalculated.
4127 * This is needed in particular on the first widget resize, as we can't
4128 * calculate them correctly before that.
4129 *
4130 * @note a hint from the host will always override these restrictions.
4131 * @note we can't do calculations on the fly when they are needed, because
4132 * they require querying the X server on X11 hosts and this must be done
4133 * from within the GUI thread, due to the single threadedness of Xlib.
4134 */
4135void VBoxConsoleView::calculateDesktopGeometry()
4136{
4137 LogFlowThisFunc (("Entering\n"));
4138 /* This method should not get called until we have initially set up the */
4139 Assert ((mDesktopGeo != DesktopGeo_Invalid));
4140 /* If we are not doing automatic geometry calculation then there is
4141 * nothing to do. */
4142 if (DesktopGeo_Automatic == mDesktopGeo)
4143 {
4144 /* Available geometry of the desktop. If the desktop is a single
4145 * screen, this will exclude space taken up by desktop taskbars
4146 * and things, but this is unfortunately not true for the more
4147 * complex case of a desktop spanning multiple screens. */
4148 QRect desktop = availableGeometry();
4149 /* The area taken up by the console window on the desktop,
4150 * including window frame, title and menu bar and whatnot. */
4151 QRect frame = mMainWnd->frameGeometry();
4152 /* The area taken up by the console window, minus all
4153 * decorations. */
4154 QRect window = mMainWnd->centralWidget()->geometry();
4155 /* To work out how big we can make the console window while still
4156 * fitting on the desktop, we calculate desktop - frame + window.
4157 * This works because the difference between frame and window
4158 * (or at least its width and height) is a constant. */
4159 mDesktopGeometry =
4160 QRect (0, 0, desktop.width() - frame.width() + window.width(),
4161 desktop.height() - frame.height() + window.height());
4162 LogFlowThisFunc (("Setting %d, %d\n", mDesktopGeometry.width(),
4163 mDesktopGeometry.height()));
4164 }
4165}
4166
4167/**
4168 * Sets the minimum size restriction depending on the auto-resize feature
4169 * state and the current rendering mode.
4170 *
4171 * Currently, the restriction is set only in SDL mode and only when the
4172 * auto-resize feature is inactive. We need to do that because we cannot
4173 * correctly draw in a scrolled window in SDL mode.
4174 *
4175 * In all other modes, or when auto-resize is in force, this function does
4176 * nothing.
4177 */
4178void VBoxConsoleView::maybeRestrictMinimumSize()
4179{
4180 if (mode == VBoxDefs::SDLMode)
4181 {
4182 if (!mGuestSupportsGraphics || !mAutoresizeGuest)
4183 setMinimumSize (sizeHint());
4184 else
4185 setMinimumSize (0, 0);
4186 }
4187}
4188
4189QRect VBoxConsoleView::availableGeometry() const
4190{
4191 return mMainWnd->isWindowFullScreen() ?
4192 QApplication::desktop()->screenGeometry(this) :
4193 QApplication::desktop()->availableGeometry(this);
4194}
4195
4196int VBoxConsoleView::contentsWidth() const
4197{
4198 return mFrameBuf->width();
4199}
4200
4201int VBoxConsoleView::contentsHeight() const
4202{
4203 return mFrameBuf->height();
4204}
4205
4206void VBoxConsoleView::updateSliders()
4207{
4208 QSize p = viewport()->size();
4209 QSize m = maximumViewportSize();
4210
4211 QSize v = QSize (mFrameBuf->width(), mFrameBuf->height());
4212 /* no scroll bars needed */
4213 if (m.expandedTo(v) == m)
4214 p = m;
4215
4216 horizontalScrollBar()->setRange(0, v.width() - p.width());
4217 verticalScrollBar()->setRange(0, v.height() - p.height());
4218 horizontalScrollBar()->setPageStep(p.width());
4219 verticalScrollBar()->setPageStep(p.height());
4220}
4221
4222void VBoxConsoleView::requestToResize (const QSize &aSize)
4223{
4224 mIgnoreFrameBufferResize = true;
4225 mNormalSize = aSize;
4226}
4227
4228#if defined(Q_WS_MAC)
4229
4230void VBoxConsoleView::updateDockIcon()
4231{
4232 if (mDockIconEnabled)
4233 {
4234 if (!mPausedShot.isNull())
4235 {
4236 CGImageRef pauseImg = ::darwinToCGImageRef (&mPausedShot);
4237 /* Use the pause image as background */
4238 mDockIconPreview->updateDockPreview (pauseImg);
4239 CGImageRelease (pauseImg);
4240 }
4241 else
4242 {
4243# if defined (VBOX_GUI_USE_QUARTZ2D)
4244 if (mode == VBoxDefs::Quartz2DMode)
4245 {
4246 /* If the render mode is Quartz2D we could use the CGImageRef
4247 * of the framebuffer for the dock icon creation. This saves
4248 * some conversion time. */
4249 mDockIconPreview->updateDockPreview (static_cast <VBoxQuartz2DFrameBuffer *> (mFrameBuf)->imageRef());
4250 }
4251 else
4252# endif
4253 /* In image mode we have to create the image ref out of the
4254 * framebuffer */
4255 mDockIconPreview->updateDockPreview (mFrameBuf);
4256 }
4257 }
4258}
4259
4260void VBoxConsoleView::updateDockOverlay()
4261{
4262 /* Only to an update to the realtime preview if this is enabled by the user
4263 * & we are in an state where the framebuffer is likely valid. Otherwise to
4264 * the overlay stuff only. */
4265 if (mDockIconEnabled &&
4266 (mLastState == KMachineState_Running ||
4267 mLastState == KMachineState_Paused ||
4268 mLastState == KMachineState_Teleporting ||
4269 mLastState == KMachineState_LiveSnapshotting ||
4270 mLastState == KMachineState_Restoring ||
4271 mLastState == KMachineState_TeleportingPausedVM ||
4272 mLastState == KMachineState_TeleportingIn ||
4273 mLastState == KMachineState_Saving))
4274 updateDockIcon();
4275 else
4276 mDockIconPreview->updateDockOverlay();
4277}
4278
4279/**
4280 * Wrapper for SetMouseCoalescingEnabled().
4281 *
4282 * Called by eventFilter() and darwinGrabKeyboardEvents().
4283 *
4284 * @param aOn Switch it on (true) or off (false).
4285 */
4286void VBoxConsoleView::setMouseCoalescingEnabled (bool aOn)
4287{
4288 /* Enable mouse event compression if we leave the VM view. This
4289 is necessary for having smooth resizing of the VM/other
4290 windows.
4291 Disable mouse event compression if we enter the VM view. So
4292 all mouse events are registered in the VM. Only do this if
4293 the keyboard/mouse is grabbed (this is when we have a valid
4294 event handler). */
4295 if (aOn || mKeyboardGrabbed)
4296 ::darwinSetMouseCoalescingEnabled (aOn);
4297}
4298
4299#endif /* Q_WS_MAC */
4300
Note: See TracBrowser for help on using the repository browser.

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