VirtualBox

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

Last change on this file since 21772 was 21772, checked in by vboxsync, 16 years ago

FE/Qt: fix a double mouse pointer issue in Moblin

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

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