VirtualBox

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

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

FE/Qt4: forgot one

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