VirtualBox

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

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

crOpenGL-OSX: add support for realtime dock tile preview of OpenGL content in Cocoa

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