VirtualBox

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

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

FE/Qt: handle Ctrl-Break properly on Linux hosts

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