VirtualBox

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

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

FE/Qt: take keyboard layouts which use shift to cancel CapsLock into account

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