VirtualBox

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

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

FE/Qt4-OSX: Better fix for the mouse wheel problem on Cocoa.

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

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