VirtualBox

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

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

FE/Qt4: Make sure guest in fullscreen mode will get the maximum possible resolution according whole screen-geometry.

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