VirtualBox

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

Last change on this file since 21711 was 21711, checked in by vboxsync, 16 years ago

FE/Qt: allow users to specify their own scan code mappings on X11 hosts, thanks to user therp on virtualbox.org (see #2302)

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