VirtualBox

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

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

FE/Qt4: 3865: Host key shortcut for installing Guest Additions - Implemented.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 139.3 KB
Line 
1/** @file
2 *
3 * VBox frontends: Qt GUI ("VirtualBox"):
4 * VBoxConsoleView class implementation
5 */
6
7/*
8 * Copyright (C) 22006-2007 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23#include "VBoxConsoleView.h"
24#include "VBoxConsoleWnd.h"
25#include "VBoxUtils.h"
26
27#include "VBoxFrameBuffer.h"
28#include "VBoxGlobal.h"
29#include "VBoxProblemReporter.h"
30
31#ifdef Q_WS_PM
32#include "QIHotKeyEdit.h"
33#endif
34
35/* Qt includes */
36#include <QMenuBar>
37#include <QDesktopWidget>
38#include <QTimer>
39#include <QStatusBar>
40#include <QPainter>
41#include <QBitmap>
42
43#ifdef Q_WS_WIN
44// VBox/cdefs.h defines these:
45#undef LOWORD
46#undef HIWORD
47#undef LOBYTE
48#undef HIBYTE
49#include <windows.h>
50#endif
51
52#ifdef Q_WS_X11
53#include <QX11Info>
54// We need to capture some X11 events directly which
55// requires the XEvent structure to be defined. However,
56// including the Xlib header file will cause some nasty
57// conflicts with Qt. Therefore we use the following hack
58// to redefine those conflicting identifiers.
59#define XK_XKB_KEYS
60#define XK_MISCELLANY
61#include <X11/Xlib.h>
62#include <X11/Xutil.h>
63#include <X11/XKBlib.h>
64#include <X11/keysym.h>
65#ifdef KeyPress
66const int XFocusOut = FocusOut;
67const int XFocusIn = FocusIn;
68const int XKeyPress = KeyPress;
69const int XKeyRelease = KeyRelease;
70#undef KeyRelease
71#undef KeyPress
72#undef FocusOut
73#undef FocusIn
74#endif
75#include "XKeyboard.h"
76#ifndef VBOX_WITHOUT_XCURSOR
77# include <X11/Xcursor/Xcursor.h>
78#endif
79#endif // Q_WS_X11
80
81#if defined (Q_WS_MAC)
82# include "VBoxDockIconPreview.h"
83# include "DarwinKeyboard.h"
84# ifdef QT_MAC_USE_COCOA
85# include "darwin/VBoxCocoaApplication.h"
86# elif defined(VBOX_WITH_HACKED_QT)
87# include "QIApplication.h"
88# endif
89# include <Carbon/Carbon.h>
90# include <VBox/err.h>
91#endif /* defined (Q_WS_MAC) */
92
93#if defined (Q_WS_WIN32)
94
95static HHOOK gKbdHook = NULL;
96static VBoxConsoleView *gView = 0;
97
98LRESULT CALLBACK VBoxConsoleView::lowLevelKeyboardProc (int nCode,
99 WPARAM wParam, LPARAM lParam)
100{
101 Assert (gView);
102 if (gView && nCode == HC_ACTION &&
103 gView->winLowKeyboardEvent (wParam, *(KBDLLHOOKSTRUCT *) lParam))
104 return 1;
105
106 return CallNextHookEx (NULL, nCode, wParam, lParam);
107}
108
109#endif
110
111#if defined (Q_WS_MAC)
112# if defined (QT_MAC_USE_COCOA)
113/**
114 * Event handler callback for Mac OS X, Cocoa variant.
115 *
116 * (Registered with and called from VBoxCocoaApplication.)
117 *
118 * @returns true if the event should be dropped, false if it should be passed
119 * along.
120 * @param pvCocoaEvent The Cocoa event object.
121 * @param pvCarbonEvent The Carbon event object reference.
122 * @param pvUser The user argument.
123 */
124/* static */
125bool VBoxConsoleView::darwinEventHandlerProc (const void *pvCocoaEvent,
126 const void *pvCarbonEvent,
127 void *pvUser)
128{
129 VBoxConsoleView *view = (VBoxConsoleView *)pvUser;
130 EventRef inEvent = (EventRef)pvCarbonEvent;
131 UInt32 eventClass = ::GetEventClass (inEvent);
132
133#if 0
134 /* For debugging events. */
135 if (eventClass != 'cute')
136 ::VBoxCocoaApplication_printEvent ("view: ", pvCocoaEvent);
137#endif
138
139 /*
140 * Not sure but this seems an triggered event if the spotlight searchbar is
141 * displayed. So flag that the host key isn't pressed alone.
142 */
143 if ( eventClass == 'cgs '
144 && view->mIsHostkeyPressed
145 && ::GetEventKind (inEvent) == 0x15)
146 view->mIsHostkeyAlone = false;
147
148 /*
149 * All keyboard class events needs to be handled.
150 */
151 if (eventClass == kEventClassKeyboard)
152 {
153 if (view->darwinKeyboardEvent (pvCocoaEvent, inEvent))
154 return true;
155 }
156 /*
157 * Command-H and Command-Q aren't properly disabled yet, and it's still
158 * possible to use the left command key to invoke them when the keyboard
159 * is captured. We discard the events these if the keyboard is captured
160 * as a half measure to prevent unexpected behaviour. However, we don't
161 * get any key down/up events, so these combinations are dead to the guest...
162 */
163 else if (eventClass == kEventClassCommand)
164 {
165 if (view->mKbdCaptured)
166 return true;
167 }
168 /* Pass the event along. */
169 return false;
170}
171
172# elif !defined (VBOX_WITH_HACKED_QT)
173/**
174 * Event handler callback for Mac OS X.
175 */
176/* static */
177pascal OSStatus VBoxConsoleView::darwinEventHandlerProc (EventHandlerCallRef inHandlerCallRef,
178 EventRef inEvent, void *inUserData)
179{
180 VBoxConsoleView *view = static_cast<VBoxConsoleView *> (inUserData);
181 UInt32 eventClass = ::GetEventClass (inEvent);
182
183 /* For debugging events */
184 /*
185 if (eventClass != 'cute')
186 ::darwinDebugPrintEvent ("view: ", inEvent);
187 */
188
189 /* Not sure but this seems an triggered event if the spotlight searchbar is
190 * displayed. So flag that the host key isn't pressed alone. */
191 if ( eventClass == 'cgs '
192 && view->mIsHostkeyPressed
193 && ::GetEventKind (inEvent) == 0x15)
194 view->mIsHostkeyAlone = false;
195
196 if (eventClass == kEventClassKeyboard)
197 {
198 if (view->darwinKeyboardEvent (NULL, inEvent))
199 return 0;
200 }
201 /*
202 * Command-H and Command-Q aren't properly disabled yet, and it's still
203 * possible to use the left command key to invoke them when the keyboard
204 * is captured. We discard the events these if the keyboard is captured
205 * as a half measure to prevent unexpected behaviour. However, we don't
206 * get any key down/up events, so these combinations are dead to the guest...
207 */
208 else if (eventClass == kEventClassCommand)
209 {
210 if (view->mKbdCaptured)
211 return 0;
212 }
213 return ::CallNextEventHandler (inHandlerCallRef, inEvent);
214}
215
216# else /* VBOX_WITH_HACKED_QT */
217/**
218 * Event handler callback for Mac OS X.
219 */
220/* static */
221bool VBoxConsoleView::macEventFilter (EventRef inEvent, void *inUserData)
222{
223 VBoxConsoleView *view = static_cast<VBoxConsoleView *> (inUserData);
224 UInt32 eventClass = ::GetEventClass (inEvent);
225 UInt32 eventKind = ::GetEventKind (inEvent);
226
227 /* For debugging events */
228 /*
229 if (!(eventClass == 'cute'))
230 ::darwinDebugPrintEvent ("view: ", inEvent);
231 */
232
233 /* Not sure but this seems an triggered event if the spotlight searchbar is
234 * displayed. So flag that the host key isn't pressed alone. */
235 if (eventClass == 'cgs ' && eventKind == 0x15 &&
236 view->mIsHostkeyPressed)
237 view->mIsHostkeyAlone = false;
238
239 if (eventClass == kEventClassKeyboard)
240 {
241 if (view->darwinKeyboardEvent (NULL, inEvent))
242 return true;
243 }
244 return false;
245}
246# endif /* VBOX_WITH_HACKED_QT */
247
248#endif /* Q_WS_MAC */
249
250/** Guest mouse pointer shape change event. */
251class MousePointerChangeEvent : public QEvent
252{
253public:
254 MousePointerChangeEvent (bool visible, bool alpha, uint xhot, uint yhot,
255 uint width, uint height,
256 const uchar *shape) :
257 QEvent ((QEvent::Type) VBoxDefs::MousePointerChangeEventType),
258 vis (visible), alph (alpha), xh (xhot), yh (yhot), w (width), h (height),
259 data (NULL)
260 {
261 // make a copy of shape
262 uint dataSize = ((((width + 7) / 8 * height) + 3) & ~3) + width * 4 * height;
263
264 if (shape) {
265 data = new uchar [dataSize];
266 memcpy ((void *) data, (void *) shape, dataSize);
267 }
268 }
269 ~MousePointerChangeEvent()
270 {
271 if (data) delete[] data;
272 }
273 bool isVisible() const { return vis; }
274 bool hasAlpha() const { return alph; }
275 uint xHot() const { return xh; }
276 uint yHot() const { return yh; }
277 uint width() const { return w; }
278 uint height() const { return h; }
279 const uchar *shapeData() const { return data; }
280private:
281 bool vis, alph;
282 uint xh, yh, w, h;
283 const uchar *data;
284};
285
286/** Guest mouse absolute positioning capability change event. */
287class MouseCapabilityEvent : public QEvent
288{
289public:
290 MouseCapabilityEvent (bool supportsAbsolute, bool needsHostCursor) :
291 QEvent ((QEvent::Type) VBoxDefs::MouseCapabilityEventType),
292 can_abs (supportsAbsolute),
293 needs_host_cursor (needsHostCursor) {}
294 bool supportsAbsolute() const { return can_abs; }
295 bool needsHostCursor() const { return needs_host_cursor; }
296private:
297 bool can_abs;
298 bool needs_host_cursor;
299};
300
301/** Machine state change. */
302class StateChangeEvent : public QEvent
303{
304public:
305 StateChangeEvent (KMachineState state) :
306 QEvent ((QEvent::Type) VBoxDefs::MachineStateChangeEventType),
307 s (state) {}
308 KMachineState machineState() const { return s; }
309private:
310 KMachineState s;
311};
312
313/** Guest Additions property changes. */
314class GuestAdditionsEvent : public QEvent
315{
316public:
317 GuestAdditionsEvent (const QString &aOsTypeId,
318 const QString &aAddVersion,
319 bool aAddActive,
320 bool aSupportsSeamless,
321 bool aSupportsGraphics) :
322 QEvent ((QEvent::Type) VBoxDefs::AdditionsStateChangeEventType),
323 mOsTypeId (aOsTypeId), mAddVersion (aAddVersion),
324 mAddActive (aAddActive), mSupportsSeamless (aSupportsSeamless),
325 mSupportsGraphics (aSupportsGraphics) {}
326 const QString &osTypeId() const { return mOsTypeId; }
327 const QString &additionVersion() const { return mAddVersion; }
328 bool additionActive() const { return mAddActive; }
329 bool supportsSeamless() const { return mSupportsSeamless; }
330 bool supportsGraphics() const { return mSupportsGraphics; }
331private:
332 QString mOsTypeId;
333 QString mAddVersion;
334 bool mAddActive;
335 bool mSupportsSeamless;
336 bool mSupportsGraphics;
337};
338
339/** DVD/Floppy drive change event */
340class MediaDriveChangeEvent : public QEvent
341{
342public:
343 MediaDriveChangeEvent (VBoxDefs::MediaType aType)
344 : QEvent ((QEvent::Type) VBoxDefs::MediaDriveChangeEventType)
345 , mType (aType) {}
346 VBoxDefs::MediaType type() const { return mType; }
347private:
348 VBoxDefs::MediaType mType;
349};
350
351/** Menu activation event */
352class ActivateMenuEvent : public QEvent
353{
354public:
355 ActivateMenuEvent (QAction *aData) :
356 QEvent ((QEvent::Type) VBoxDefs::ActivateMenuEventType),
357 mAction (aData) {}
358 QAction *action() const { return mAction; }
359private:
360 QAction *mAction;
361};
362
363/** VM Runtime error event */
364class RuntimeErrorEvent : public QEvent
365{
366public:
367 RuntimeErrorEvent (bool aFatal, const QString &aErrorID,
368 const QString &aMessage) :
369 QEvent ((QEvent::Type) VBoxDefs::RuntimeErrorEventType),
370 mFatal (aFatal), mErrorID (aErrorID), mMessage (aMessage) {}
371 bool fatal() const { return mFatal; }
372 QString errorID() const { return mErrorID; }
373 QString message() const { return mMessage; }
374private:
375 bool mFatal;
376 QString mErrorID;
377 QString mMessage;
378};
379
380/** Modifier key change event */
381class ModifierKeyChangeEvent : public QEvent
382{
383public:
384 ModifierKeyChangeEvent (bool fNumLock, bool fCapsLock, bool fScrollLock) :
385 QEvent ((QEvent::Type) VBoxDefs::ModifierKeyChangeEventType),
386 mNumLock (fNumLock), mCapsLock (fCapsLock), mScrollLock (fScrollLock) {}
387 bool numLock() const { return mNumLock; }
388 bool capsLock() const { return mCapsLock; }
389 bool scrollLock() const { return mScrollLock; }
390private:
391 bool mNumLock, mCapsLock, mScrollLock;
392};
393
394/** Network adapter change event */
395class NetworkAdapterChangeEvent : public QEvent
396{
397public:
398 NetworkAdapterChangeEvent (INetworkAdapter *aAdapter) :
399 QEvent ((QEvent::Type) VBoxDefs::NetworkAdapterChangeEventType),
400 mAdapter (aAdapter) {}
401 INetworkAdapter* networkAdapter() { return mAdapter; }
402private:
403 INetworkAdapter *mAdapter;
404};
405
406/** USB controller state change event */
407class USBControllerStateChangeEvent : public QEvent
408{
409public:
410 USBControllerStateChangeEvent()
411 : QEvent ((QEvent::Type) VBoxDefs::USBCtlStateChangeEventType) {}
412};
413
414/** USB device state change event */
415class USBDeviceStateChangeEvent : public QEvent
416{
417public:
418 USBDeviceStateChangeEvent (const CUSBDevice &aDevice, bool aAttached,
419 const CVirtualBoxErrorInfo &aError) :
420 QEvent ((QEvent::Type) VBoxDefs::USBDeviceStateChangeEventType),
421 mDevice (aDevice), mAttached (aAttached), mError (aError) {}
422 CUSBDevice device() const { return mDevice; }
423 bool attached() const { return mAttached; }
424 CVirtualBoxErrorInfo error() const { return mError; }
425private:
426 CUSBDevice mDevice;
427 bool mAttached;
428 CVirtualBoxErrorInfo mError;
429};
430
431//
432// VBoxConsoleCallback class
433/////////////////////////////////////////////////////////////////////////////
434
435class VBoxConsoleCallback : VBOX_SCRIPTABLE_IMPL(IConsoleCallback)
436{
437public:
438
439 VBoxConsoleCallback (VBoxConsoleView *v) {
440#if defined (Q_WS_WIN)
441 mRefCnt = 0;
442#endif
443 mView = v;
444 }
445
446 virtual ~VBoxConsoleCallback() {}
447
448 NS_DECL_ISUPPORTS
449
450#if defined (Q_WS_WIN)
451 STDMETHOD_(ULONG, AddRef)() {
452 return ::InterlockedIncrement (&mRefCnt);
453 }
454 STDMETHOD_(ULONG, Release)()
455 {
456 long cnt = ::InterlockedDecrement (&mRefCnt);
457 if (cnt == 0)
458 delete this;
459 return cnt;
460 }
461 STDMETHOD(QueryInterface) (REFIID riid , void **ppObj)
462 {
463 if (riid == IID_IUnknown) {
464 *ppObj = this;
465 AddRef();
466 return S_OK;
467 }
468 if (riid == IID_IConsoleCallback) {
469 *ppObj = this;
470 AddRef();
471 return S_OK;
472 }
473 *ppObj = NULL;
474 return E_NOINTERFACE;
475 }
476#endif
477
478 STDMETHOD(OnMousePointerShapeChange) (BOOL visible, BOOL alpha,
479 ULONG xhot, ULONG yhot,
480 ULONG width, ULONG height,
481 BYTE *shape)
482 {
483 QApplication::postEvent (mView,
484 new MousePointerChangeEvent (visible, alpha,
485 xhot, yhot,
486 width, height, shape));
487 return S_OK;
488 }
489
490 STDMETHOD(OnMouseCapabilityChange)(BOOL supportsAbsolute, BOOL needsHostCursor)
491 {
492 QApplication::postEvent (mView,
493 new MouseCapabilityEvent (supportsAbsolute,
494 needsHostCursor));
495 return S_OK;
496 }
497
498 STDMETHOD(OnKeyboardLedsChange)(BOOL fNumLock, BOOL fCapsLock, BOOL fScrollLock)
499 {
500 QApplication::postEvent (mView,
501 new ModifierKeyChangeEvent (fNumLock, fCapsLock,
502 fScrollLock));
503 return S_OK;
504 }
505
506 STDMETHOD(OnStateChange)(MachineState_T machineState)
507 {
508 LogFlowFunc (("machineState=%d\n", machineState));
509 QApplication::postEvent (mView,
510 new StateChangeEvent ((KMachineState) machineState));
511 return S_OK;
512 }
513
514 STDMETHOD(OnAdditionsStateChange)()
515 {
516 CGuest guest = mView->console().GetGuest();
517 LogFlowFunc (("ver=%s, active=%d\n",
518 guest.GetAdditionsVersion().toLatin1().constData(),
519 guest.GetAdditionsActive()));
520 QApplication::postEvent (mView,
521 new GuestAdditionsEvent (
522 guest.GetOSTypeId(),
523 guest.GetAdditionsVersion(),
524 guest.GetAdditionsActive(),
525 guest.GetSupportsSeamless(),
526 guest.GetSupportsGraphics()));
527 return S_OK;
528 }
529
530 STDMETHOD(OnDVDDriveChange)()
531 {
532 LogFlowFunc (("DVD Drive changed\n"));
533 QApplication::postEvent (mView,
534 new MediaDriveChangeEvent (VBoxDefs::MediaType_DVD));
535 return S_OK;
536 }
537
538 STDMETHOD(OnFloppyDriveChange)()
539 {
540 LogFlowFunc (("Floppy Drive changed\n"));
541 QApplication::postEvent (mView,
542 new MediaDriveChangeEvent (VBoxDefs::MediaType_Floppy));
543 return S_OK;
544 }
545
546 STDMETHOD(OnNetworkAdapterChange) (INetworkAdapter *aNetworkAdapter)
547 {
548 QApplication::postEvent (mView,
549 new NetworkAdapterChangeEvent (aNetworkAdapter));
550 return S_OK;
551 }
552
553 STDMETHOD(OnStorageControllerChange) ()
554 {
555 /* @todo */
556 //QApplication::postEvent (mView,
557 // new StorageControllerChangeEvent ());
558 return S_OK;
559 }
560
561 STDMETHOD(OnSerialPortChange) (ISerialPort *aSerialPort)
562 {
563 NOREF(aSerialPort);
564 return S_OK;
565 }
566
567 STDMETHOD(OnParallelPortChange) (IParallelPort *aParallelPort)
568 {
569 NOREF(aParallelPort);
570 return S_OK;
571 }
572
573 STDMETHOD(OnVRDPServerChange)()
574 {
575 return S_OK;
576 }
577
578 STDMETHOD(OnUSBControllerChange)()
579 {
580 QApplication::postEvent (mView,
581 new USBControllerStateChangeEvent());
582 return S_OK;
583 }
584
585 STDMETHOD(OnUSBDeviceStateChange)(IUSBDevice *aDevice, BOOL aAttached,
586 IVirtualBoxErrorInfo *aError)
587 {
588 QApplication::postEvent (mView,
589 new USBDeviceStateChangeEvent (
590 CUSBDevice (aDevice),
591 bool (aAttached),
592 CVirtualBoxErrorInfo (aError)));
593 return S_OK;
594 }
595
596 STDMETHOD(OnSharedFolderChange) (Scope_T aScope)
597 {
598 NOREF(aScope);
599 QApplication::postEvent (mView,
600 new QEvent ((QEvent::Type)
601 VBoxDefs::SharedFolderChangeEventType));
602 return S_OK;
603 }
604
605 STDMETHOD(OnRuntimeError)(BOOL fatal, IN_BSTR id, IN_BSTR message)
606 {
607 QApplication::postEvent (mView,
608 new RuntimeErrorEvent (!!fatal,
609 QString::fromUtf16 (id),
610 QString::fromUtf16 (message)));
611 return S_OK;
612 }
613
614 STDMETHOD(OnCanShowWindow) (BOOL *canShow)
615 {
616 if (!canShow)
617 return E_POINTER;
618
619 /* as long as there is VBoxConsoleView (which creates/destroys us), it
620 * can be shown */
621 *canShow = TRUE;
622 return S_OK;
623 }
624
625 STDMETHOD(OnShowWindow) (ULONG64 *winId)
626 {
627 if (!winId)
628 return E_POINTER;
629
630#if defined (Q_WS_MAC)
631 /*
632 * Let's try the simple approach first - grab the focus.
633 * Getting a window out of the dock (minimized or whatever it's called)
634 * needs to be done on the GUI thread, so post it a note.
635 */
636 *winId = 0;
637 if (!mView)
638 return S_OK;
639
640 ProcessSerialNumber psn = { 0, kCurrentProcess };
641 OSErr rc = ::SetFrontProcess (&psn);
642 if (!rc)
643 QApplication::postEvent (mView, new QEvent ((QEvent::Type)VBoxDefs::ShowWindowEventType));
644 else
645 {
646 /*
647 * It failed for some reason, send the other process our PSN so it can try.
648 * (This is just a precaution should Mac OS X start imposing the same sensible
649 * focus stealing restrictions that other window managers implement.)
650 */
651 AssertMsgFailed(("SetFrontProcess -> %#x\n", rc));
652 if (::GetCurrentProcess (&psn))
653 *winId = RT_MAKE_U64 (psn.lowLongOfPSN, psn.highLongOfPSN);
654 }
655
656#else
657 /* Return the ID of the top-level console window. */
658 *winId = (ULONG64) mView->window()->winId();
659#endif
660
661 return S_OK;
662 }
663
664protected:
665
666 VBoxConsoleView *mView;
667
668#if defined (Q_WS_WIN)
669private:
670 long mRefCnt;
671#endif
672};
673
674#if !defined (Q_WS_WIN)
675NS_DECL_CLASSINFO (VBoxConsoleCallback)
676NS_IMPL_THREADSAFE_ISUPPORTS1_CI (VBoxConsoleCallback, IConsoleCallback)
677#endif
678
679class VBoxViewport: public QWidget
680{
681public:
682 VBoxViewport (QWidget *aParent)
683 : QWidget (aParent)
684 {
685 /* No need for background drawing */
686 setAttribute (Qt::WA_OpaquePaintEvent);
687 }
688 virtual QPaintEngine * paintEngine() const
689 {
690 if (testAttribute (Qt::WA_PaintOnScreen))
691 return NULL;
692 else
693 return QWidget::paintEngine();
694 }
695};
696
697//
698// VBoxConsoleView class
699/////////////////////////////////////////////////////////////////////////////
700
701/** @class VBoxConsoleView
702 *
703 * The VBoxConsoleView class is a widget that implements a console
704 * for the running virtual machine.
705 */
706
707VBoxConsoleView::VBoxConsoleView (VBoxConsoleWnd *mainWnd,
708 const CConsole &console,
709 VBoxDefs::RenderMode rm,
710 QWidget *parent)
711 : QAbstractScrollArea (parent)
712 , mMainWnd (mainWnd)
713 , mConsole (console)
714 , gs (vboxGlobal().settings())
715 , mAttached (false)
716 , mKbdCaptured (false)
717 , mMouseCaptured (false)
718 , mMouseAbsolute (false)
719 , mMouseIntegration (true)
720 , mDisableAutoCapture (false)
721 , mIsHostkeyPressed (false)
722 , mIsHostkeyAlone (false)
723 , mIgnoreMainwndResize (true)
724 , mAutoresizeGuest (false)
725 , mIgnoreFrameBufferResize (false)
726 , mDoResize (false)
727 , mGuestSupportsGraphics (false)
728 , mNumLock (false)
729 , mScrollLock (false)
730 , mCapsLock (false)
731 , muNumLockAdaptionCnt (2)
732 , muCapsLockAdaptionCnt (2)
733 , mode (rm)
734#if defined(Q_WS_WIN)
735 , mAlphaCursor (NULL)
736#endif
737#if defined(Q_WS_MAC)
738# if !defined (VBOX_WITH_HACKED_QT) && !defined (QT_MAC_USE_COCOA)
739 , mDarwinEventHandlerRef (NULL)
740# endif
741 , mDarwinKeyModifiers (0)
742 , mKeyboardGrabbed (false)
743 , mDockIconEnabled (true)
744#endif
745 , mDesktopGeo (DesktopGeo_Invalid)
746 , mPassCAD (false)
747 , mHideHostPointer (false)
748{
749 Assert (!mConsole.isNull() &&
750 !mConsole.GetDisplay().isNull() &&
751 !mConsole.GetKeyboard().isNull() &&
752 !mConsole.GetMouse().isNull());
753
754#ifdef Q_WS_MAC
755 /* Overlay logo for the dock icon */
756 //mVirtualBoxLogo = ::darwinToCGImageRef ("VirtualBox_cube_42px.png");
757 QString osTypeId = mConsole.GetGuest().GetOSTypeId();
758 mDockIconPreview = new VBoxDockIconPreview (mMainWnd, vboxGlobal().vmGuestOSTypeIcon (osTypeId));
759
760# ifdef QT_MAC_USE_COCOA
761 /** @todo Carbon -> Cocoa */
762# else /* !QT_MAC_USE_COCOA */
763 /* Install the event handler which will proceed external window handling */
764 EventHandlerUPP eventHandler = ::NewEventHandlerUPP (::darwinOverlayWindowHandler);
765 EventTypeSpec eventTypes[] =
766 {
767 { kEventClassVBox, kEventVBoxShowWindow },
768 { kEventClassVBox, kEventVBoxHideWindow },
769 { kEventClassVBox, kEventVBoxMoveWindow },
770 { kEventClassVBox, kEventVBoxResizeWindow },
771 { kEventClassVBox, kEventVBoxDisposeWindow },
772 { kEventClassVBox, kEventVBoxUpdateDock }
773 };
774
775 mDarwinWindowOverlayHandlerRef = NULL;
776 ::InstallApplicationEventHandler (eventHandler, RT_ELEMENTS (eventTypes), &eventTypes[0],
777 this, &mDarwinWindowOverlayHandlerRef);
778 ::DisposeEventHandlerUPP (eventHandler);
779# endif /* !QT_MAC_USE_COCOA */
780#endif /* QT_WS_MAC */
781
782 /* No frame around the view */
783 setFrameStyle (QFrame::NoFrame);
784
785 VBoxViewport *pViewport = new VBoxViewport (this);
786 setViewport (pViewport);
787
788 /* enable MouseMove events */
789 viewport()->setMouseTracking (true);
790
791 /*
792 * QScrollView does the below on its own, but let's do it anyway
793 * for the case it will not do it in the future.
794 */
795 viewport()->installEventFilter (this);
796
797 /* to fix some focus issues */
798 mMainWnd->menuBar()->installEventFilter (this);
799
800 /* we want to be notified on some parent's events */
801 mMainWnd->installEventFilter (this);
802
803#ifdef Q_WS_X11
804 /* initialize the X keyboard subsystem */
805 initXKeyboard (QX11Info::display());
806#endif
807
808 ::memset (mPressedKeys, 0, sizeof (mPressedKeys));
809
810 /* setup rendering */
811
812 CDisplay display = mConsole.GetDisplay();
813 Assert (!display.isNull());
814
815 mFrameBuf = NULL;
816
817 LogFlowFunc (("Rendering mode: %d\n", mode));
818
819 switch (mode)
820 {
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.RegisterExternalFramebuffer (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.SetupInternalFramebuffer (0);
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 default:
1690 break;
1691 }
1692 }
1693
1694 return QAbstractScrollArea::event (e);
1695}
1696
1697bool VBoxConsoleView::eventFilter (QObject *watched, QEvent *e)
1698{
1699 if (mAttached && watched == viewport())
1700 {
1701 switch (e->type())
1702 {
1703 case QEvent::MouseMove:
1704 case QEvent::MouseButtonPress:
1705 case QEvent::MouseButtonDblClick:
1706 case QEvent::MouseButtonRelease:
1707 {
1708 QMouseEvent *me = (QMouseEvent *) e;
1709 if (mouseEvent (me->type(), me->pos(), me->globalPos(),
1710 me->buttons(), me->modifiers(),
1711 0, Qt::Horizontal))
1712 return true; /* stop further event handling */
1713 break;
1714 }
1715 case QEvent::Wheel:
1716 {
1717 QWheelEvent *we = (QWheelEvent *) e;
1718 if (mouseEvent (we->type(), we->pos(), we->globalPos(),
1719 we->buttons(), we->modifiers(),
1720 we->delta(), we->orientation()))
1721 return true; /* stop further event handling */
1722 break;
1723 }
1724#ifdef Q_WS_MAC
1725 case QEvent::Leave:
1726 {
1727 /* Enable mouse event compression if we leave the VM view. This
1728 is necessary for having smooth resizing of the VM/other
1729 windows. */
1730 setMouseCoalescingEnabled (true);
1731 break;
1732 }
1733 case QEvent::Enter:
1734 {
1735 /* Disable mouse event compression if we enter the VM view. So
1736 all mouse events are registered in the VM. Only do this if
1737 the keyboard/mouse is grabbed (this is when we have a valid
1738 event handler). */
1739 setMouseCoalescingEnabled (false);
1740 break;
1741 }
1742#endif /* Q_WS_MAC */
1743 case QEvent::Resize:
1744 {
1745 if (mMouseCaptured)
1746 updateMouseClipping();
1747 break;
1748 }
1749 default:
1750 break;
1751 }
1752 }
1753 else if (watched == mMainWnd)
1754 {
1755 switch (e->type())
1756 {
1757#if defined (Q_WS_WIN32)
1758#if defined (VBOX_GUI_USE_DDRAW)
1759 case QEvent::Move:
1760 {
1761 /*
1762 * notification from our parent that it has moved. We need this
1763 * in order to possibly adjust the direct screen blitting.
1764 */
1765 if (mFrameBuf)
1766 mFrameBuf->moveEvent ((QMoveEvent *) e);
1767 break;
1768 }
1769#endif
1770 /*
1771 * install/uninstall low-level kbd hook on every
1772 * activation/deactivation to:
1773 * a) avoid excess hook calls when we're not active and
1774 * b) be always in front of any other possible hooks
1775 */
1776 case QEvent::WindowActivate:
1777 {
1778 gKbdHook = SetWindowsHookEx (WH_KEYBOARD_LL, lowLevelKeyboardProc,
1779 GetModuleHandle (NULL), 0);
1780 AssertMsg (gKbdHook, ("SetWindowsHookEx(): err=%d", GetLastError()));
1781 break;
1782 }
1783 case QEvent::WindowDeactivate:
1784 {
1785 if (gKbdHook)
1786 {
1787 UnhookWindowsHookEx (gKbdHook);
1788 gKbdHook = NULL;
1789 }
1790 break;
1791 }
1792#endif /* defined (Q_WS_WIN32) */
1793#if defined (Q_WS_MAC)
1794 /*
1795 * Install/remove the keyboard event handler.
1796 */
1797 case QEvent::WindowActivate:
1798 darwinGrabKeyboardEvents (true);
1799 break;
1800 case QEvent::WindowDeactivate:
1801 darwinGrabKeyboardEvents (false);
1802 break;
1803#endif /* defined (Q_WS_MAC) */
1804 case QEvent::Resize:
1805 {
1806 /* Set the "guest needs to resize" hint. This hint is acted upon
1807 * when (and only when) the autoresize property is "true". */
1808 mDoResize = mGuestSupportsGraphics || mMainWnd->isTrueFullscreen();
1809 if (!mIgnoreMainwndResize &&
1810 mGuestSupportsGraphics && mAutoresizeGuest)
1811 QTimer::singleShot (300, this, SLOT (doResizeHint()));
1812 break;
1813 }
1814 case QEvent::WindowStateChange:
1815 {
1816 /* During minimizing and state restoring mMainWnd gets the focus
1817 * which belongs to console view window, so returning it properly. */
1818 QWindowStateChangeEvent *ev = static_cast <QWindowStateChangeEvent*> (e);
1819 if (ev->oldState() & Qt::WindowMinimized)
1820 {
1821 if (QApplication::focusWidget())
1822 {
1823 QApplication::focusWidget()->clearFocus();
1824 qApp->processEvents();
1825 }
1826 QTimer::singleShot (0, this, SLOT (setFocus()));
1827 }
1828 break;
1829 }
1830
1831 default:
1832 break;
1833 }
1834 }
1835 else if (watched == mMainWnd->menuBar())
1836 {
1837 /*
1838 * sometimes when we press ESC in the menu it brings the
1839 * focus away (Qt bug?) causing no widget to have a focus,
1840 * or holds the focus itself, instead of returning the focus
1841 * to the console window. here we fix this.
1842 */
1843 switch (e->type())
1844 {
1845 case QEvent::FocusOut:
1846 {
1847 if (qApp->focusWidget() == 0)
1848 setFocus();
1849 break;
1850 }
1851 case QEvent::KeyPress:
1852 {
1853 QKeyEvent *ke = (QKeyEvent *) e;
1854 if (ke->key() == Qt::Key_Escape && (ke->modifiers() == Qt::NoModifier))
1855 if (mMainWnd->menuBar()->hasFocus())
1856 setFocus();
1857 break;
1858 }
1859 default:
1860 break;
1861 }
1862 }
1863
1864 return QAbstractScrollArea::eventFilter (watched, e);
1865}
1866
1867#if defined(Q_WS_WIN32)
1868
1869/**
1870 * Low-level keyboard event handler,
1871 * @return
1872 * true to indicate that the message is processed and false otherwise
1873 */
1874bool VBoxConsoleView::winLowKeyboardEvent (UINT msg, const KBDLLHOOKSTRUCT &event)
1875{
1876#if 0
1877 LogFlow (("### vkCode=%08X, scanCode=%08X, flags=%08X, dwExtraInfo=%08X (mKbdCaptured=%d)\n",
1878 event.vkCode, event.scanCode, event.flags, event.dwExtraInfo, mKbdCaptured));
1879 char buf [256];
1880 sprintf (buf, "### vkCode=%08X, scanCode=%08X, flags=%08X, dwExtraInfo=%08X",
1881 event.vkCode, event.scanCode, event.flags, event.dwExtraInfo);
1882 mMainWnd->statusBar()->message (buf);
1883#endif
1884
1885 /* Sometimes it happens that Win inserts additional events on some key
1886 * press/release. For example, it prepends ALT_GR in German layout with
1887 * the VK_LCONTROL vkey with curious 0x21D scan code (seems to be necessary
1888 * to specially treat ALT_GR to enter additional chars to regular apps).
1889 * These events are definitely unwanted in VM, so filter them out. */
1890 if (hasFocus() && (event.scanCode & ~0xFF))
1891 return true;
1892
1893 if (!mKbdCaptured)
1894 return false;
1895
1896 /* it's possible that a key has been pressed while the keyboard was not
1897 * captured, but is being released under the capture. Detect this situation
1898 * and return false to let Windows process the message normally and update
1899 * its key state table (to avoid the stuck key effect). */
1900 uint8_t what_pressed = (event.flags & 0x01) && (event.vkCode != VK_RSHIFT)
1901 ? IsExtKeyPressed
1902 : IsKeyPressed;
1903 if ((event.flags & 0x80) /* released */ &&
1904 ((event.vkCode == gs.hostKey() && !hostkey_in_capture) ||
1905 (mPressedKeys [event.scanCode] & (IsKbdCaptured | what_pressed)) == what_pressed))
1906 return false;
1907
1908 MSG message;
1909 message.hwnd = winId();
1910 message.message = msg;
1911 message.wParam = event.vkCode;
1912 message.lParam =
1913 1 |
1914 (event.scanCode & 0xFF) << 16 |
1915 (event.flags & 0xFF) << 24;
1916
1917 /* Windows sets here the extended bit when the Right Shift key is pressed,
1918 * which is totally wrong. Undo it. */
1919 if (event.vkCode == VK_RSHIFT)
1920 message.lParam &= ~0x1000000;
1921
1922 /* we suppose here that this hook is always called on the main GUI thread */
1923 long dummyResult;
1924 return winEvent (&message, &dummyResult);
1925}
1926
1927/**
1928 * Get Win32 messages before they are passed to Qt. This allows us to get
1929 * the keyboard events directly and bypass the harmful Qt translation. A
1930 * return value of @c true indicates to Qt that the event has been handled.
1931 */
1932bool VBoxConsoleView::winEvent (MSG *aMsg, long* /* aResult */)
1933{
1934 if (!mAttached || ! (
1935 aMsg->message == WM_KEYDOWN || aMsg->message == WM_SYSKEYDOWN ||
1936 aMsg->message == WM_KEYUP || aMsg->message == WM_SYSKEYUP
1937 ))
1938 return false;
1939
1940 /* check for the special flag possibly set at the end of this function */
1941 if (aMsg->lParam & (0x1 << 25))
1942 {
1943 aMsg->lParam &= ~(0x1 << 25);
1944 return false;
1945 }
1946
1947#if 0
1948 char buf [256];
1949 sprintf (buf, "WM_%04X: vk=%04X rep=%05d scan=%02X ext=%01d rzv=%01X ctx=%01d prev=%01d tran=%01d",
1950 aMsg->message, aMsg->wParam,
1951 (aMsg->lParam & 0xFFFF),
1952 ((aMsg->lParam >> 16) & 0xFF),
1953 ((aMsg->lParam >> 24) & 0x1),
1954 ((aMsg->lParam >> 25) & 0xF),
1955 ((aMsg->lParam >> 29) & 0x1),
1956 ((aMsg->lParam >> 30) & 0x1),
1957 ((aMsg->lParam >> 31) & 0x1));
1958 mMainWnd->statusBar()->message (buf);
1959 LogFlow (("%s\n", buf));
1960#endif
1961
1962 int scan = (aMsg->lParam >> 16) & 0x7F;
1963 /* scancodes 0x80 and 0x00 are ignored */
1964 if (!scan)
1965 return true;
1966
1967 int vkey = aMsg->wParam;
1968
1969 /* When one of the SHIFT keys is held and one of the cursor movement
1970 * keys is pressed, Windows duplicates SHIFT press/release messages,
1971 * but with the virtual key code set to 0xFF. These virtual keys are also
1972 * sent in some other situations (Pause, PrtScn, etc.). Ignore such
1973 * messages. */
1974 if (vkey == 0xFF)
1975 return true;
1976
1977 int flags = 0;
1978 if (aMsg->lParam & 0x1000000)
1979 flags |= KeyExtended;
1980 if (!(aMsg->lParam & 0x80000000))
1981 flags |= KeyPressed;
1982
1983 switch (vkey)
1984 {
1985 case VK_SHIFT:
1986 case VK_CONTROL:
1987 case VK_MENU:
1988 {
1989 /* overcome stupid Win32 modifier key generalization */
1990 int keyscan = scan;
1991 if (flags & KeyExtended)
1992 keyscan |= 0xE000;
1993 switch (keyscan)
1994 {
1995 case 0x002A: vkey = VK_LSHIFT; break;
1996 case 0x0036: vkey = VK_RSHIFT; break;
1997 case 0x001D: vkey = VK_LCONTROL; break;
1998 case 0xE01D: vkey = VK_RCONTROL; break;
1999 case 0x0038: vkey = VK_LMENU; break;
2000 case 0xE038: vkey = VK_RMENU; break;
2001 }
2002 break;
2003 }
2004 case VK_NUMLOCK:
2005 /* Win32 sets the extended bit for the NumLock key. Reset it. */
2006 flags &= ~KeyExtended;
2007 break;
2008 case VK_SNAPSHOT:
2009 flags |= KeyPrint;
2010 break;
2011 case VK_PAUSE:
2012 flags |= KeyPause;
2013 break;
2014 }
2015
2016 bool result = keyEvent (vkey, scan, flags);
2017 if (!result && mKbdCaptured)
2018 {
2019 /* keyEvent() returned that it didn't process the message, but since the
2020 * keyboard is captured, we don't want to pass it to Windows. We just want
2021 * to let Qt process the message (to handle non-alphanumeric <HOST>+key
2022 * shortcuts for example). So send it direcltly to the window with the
2023 * special flag in the reserved area of lParam (to avoid recursion). */
2024 ::SendMessage (aMsg->hwnd, aMsg->message,
2025 aMsg->wParam, aMsg->lParam | (0x1 << 25));
2026 return true;
2027 }
2028
2029 /* These special keys have to be handled by Windows as well to update the
2030 * internal modifier state and to enable/disable the keyboard LED */
2031 if (vkey == VK_NUMLOCK || vkey == VK_CAPITAL)
2032 return false;
2033
2034 return result;
2035}
2036
2037#elif defined (Q_WS_PM)
2038
2039/**
2040 * Get PM messages before they are passed to Qt. This allows us to get
2041 * the keyboard events directly and bypass the harmful Qt translation. A
2042 * return value of @c true indicates to Qt that the event has been handled.
2043 */
2044bool VBoxConsoleView::pmEvent (QMSG *aMsg)
2045{
2046 if (!mAttached)
2047 return false;
2048
2049 if (aMsg->msg == UM_PREACCEL_CHAR)
2050 {
2051 /* we are inside the input hook */
2052
2053 /* let the message go through the normal system pipeline */
2054 if (!mKbdCaptured)
2055 return false;
2056 }
2057
2058 if (aMsg->msg != WM_CHAR &&
2059 aMsg->msg != UM_PREACCEL_CHAR)
2060 return false;
2061
2062 /* check for the special flag possibly set at the end of this function */
2063 if (SHORT2FROMMP (aMsg->mp2) & 0x8000)
2064 {
2065 aMsg->mp2 = MPFROM2SHORT (SHORT1FROMMP (aMsg->mp2),
2066 SHORT2FROMMP (aMsg->mp2) & ~0x8000);
2067 return false;
2068 }
2069
2070#if 0
2071 {
2072 char buf [256];
2073 sprintf (buf, "*** %s: f=%04X rep=%03d scan=%02X ch=%04X vk=%04X",
2074 (aMsg->msg == WM_CHAR ? "WM_CHAR" : "UM_PREACCEL_CHAR"),
2075 SHORT1FROMMP (aMsg->mp1), CHAR3FROMMP (aMsg->mp1),
2076 CHAR4FROMMP (aMsg->mp1), SHORT1FROMMP (aMsg->mp2),
2077 SHORT2FROMMP (aMsg->mp2));
2078 mMainWnd->statusBar()->message (buf);
2079 LogFlow (("%s\n", buf));
2080 }
2081#endif
2082
2083 USHORT ch = SHORT1FROMMP (aMsg->mp2);
2084 USHORT f = SHORT1FROMMP (aMsg->mp1);
2085
2086 int scan = (unsigned int) CHAR4FROMMP (aMsg->mp1);
2087 if (!scan || scan > 0x7F)
2088 return true;
2089
2090 int vkey = QIHotKeyEdit::virtualKey (aMsg);
2091
2092 int flags = 0;
2093
2094 if ((ch & 0xFF) == 0xE0)
2095 {
2096 flags |= KeyExtended;
2097 scan = ch >> 8;
2098 }
2099 else if (scan == 0x5C && (ch & 0xFF) == '/')
2100 {
2101 /* this is the '/' key on the keypad */
2102 scan = 0x35;
2103 flags |= KeyExtended;
2104 }
2105 else
2106 {
2107 /* For some keys, the scan code passed in QMSG is a pseudo scan
2108 * code. We replace it with a real hardware scan code, according to
2109 * http://www.computer-engineering.org/ps2keyboard/scancodes1.html.
2110 * Also detect Pause and PrtScn and set flags. */
2111 switch (vkey)
2112 {
2113 case VK_ENTER: scan = 0x1C; flags |= KeyExtended; break;
2114 case VK_CTRL: scan = 0x1D; flags |= KeyExtended; break;
2115 case VK_ALTGRAF: scan = 0x38; flags |= KeyExtended; break;
2116 case VK_LWIN: scan = 0x5B; flags |= KeyExtended; break;
2117 case VK_RWIN: scan = 0x5C; flags |= KeyExtended; break;
2118 case VK_WINMENU: scan = 0x5D; flags |= KeyExtended; break;
2119 case VK_FORWARD: scan = 0x69; flags |= KeyExtended; break;
2120 case VK_BACKWARD: scan = 0x6A; flags |= KeyExtended; break;
2121#if 0
2122 /// @todo this would send 0xE0 0x46 0xE0 0xC6. It's not fully
2123 // clear what is more correct
2124 case VK_BREAK: scan = 0x46; flags |= KeyExtended; break;
2125#else
2126 case VK_BREAK: scan = 0; flags |= KeyPause; break;
2127#endif
2128 case VK_PAUSE: scan = 0; flags |= KeyPause; break;
2129 case VK_PRINTSCRN: scan = 0; flags |= KeyPrint; break;
2130 default:;
2131 }
2132 }
2133
2134 if (!(f & KC_KEYUP))
2135 flags |= KeyPressed;
2136
2137 bool result = keyEvent (vkey, scan, flags);
2138 if (!result && mKbdCaptured)
2139 {
2140 /* keyEvent() returned that it didn't process the message, but since the
2141 * keyboard is captured, we don't want to pass it to PM. We just want
2142 * to let Qt process the message (to handle non-alphanumeric <HOST>+key
2143 * shortcuts for example). So send it direcltly to the window with the
2144 * special flag in the reserved area of lParam (to avoid recursion). */
2145 ::WinSendMsg (aMsg->hwnd, WM_CHAR,
2146 aMsg->mp1,
2147 MPFROM2SHORT (SHORT1FROMMP (aMsg->mp2),
2148 SHORT2FROMMP (aMsg->mp2) | 0x8000));
2149 return true;
2150 }
2151 return result;
2152}
2153
2154#elif defined(Q_WS_X11)
2155
2156/**
2157 * This function is a "predicate" for XCheckIfEvent(). It will check
2158 * the XEvent passed to it to see if it is a keypress event matching
2159 * the keyrelease event in @a pvArg.
2160 * @returns True if the event matches, False otherwise
2161 * @param pEvent the event to compare, taken from the event queue
2162 * @param pvArg the keyrelease event we would like to compare against
2163 */
2164static Bool VBoxConsoleViewCompEvent(Display *, XEvent *pEvent,
2165 XPointer pvArg)
2166{
2167 XEvent *pKeyEvent = (XEvent *) pvArg;
2168 if ((pEvent->type == XKeyPress) &&
2169 (pEvent->xkey.keycode == pKeyEvent->xkey.keycode))
2170 return True;
2171 else
2172 return False;
2173}
2174
2175/**
2176 * This routine gets X11 events before they are processed by Qt. This is
2177 * used for our platform specific keyboard implementation. A return value
2178 * of TRUE indicates that the event has been processed by us.
2179 */
2180bool VBoxConsoleView::x11Event (XEvent *event)
2181{
2182 switch (event->type)
2183 {
2184 /* We have to handle XFocusOut right here as this event is not passed
2185 * to VBoxConsoleView::event(). Handling this event is important for
2186 * releasing the keyboard before the screen saver gets active. */
2187 case XFocusOut:
2188 case XFocusIn:
2189 if (isRunning())
2190 focusEvent (event->type == XFocusIn);
2191 return false;
2192 case XKeyPress:
2193 case XKeyRelease:
2194 if (mAttached)
2195 break;
2196 /* else fall through */
2197 default:
2198 return false; /* pass the event to Qt */
2199 }
2200
2201 /* Translate the keycode to a PC scan code. */
2202 unsigned scan = handleXKeyEvent (event);
2203
2204#if 0
2205 char buf [256];
2206 sprintf (buf, "pr=%d kc=%08X st=%08X extended=%s scan=%04X",
2207 event->type == XKeyPress ? 1 : 0, event->xkey.keycode,
2208 event->xkey.state, scan >> 8 ? "true" : "false", scan & 0x7F);
2209 mMainWnd->statusBar()->message (buf);
2210 LogFlow (("### %s\n", buf));
2211#endif
2212
2213 // scancodes 0x00 (no valid translation) and 0x80 are ignored
2214 if (!scan & 0x7F)
2215 return true;
2216
2217 /* Fix for http://www.virtualbox.org/ticket/1296:
2218 * when X11 sends events for repeated keys, it always inserts an
2219 * XKeyRelease before the XKeyPress. */
2220 XEvent returnEvent;
2221 if ((event->type == XKeyRelease) &&
2222 (XCheckIfEvent(event->xkey.display, &returnEvent,
2223 VBoxConsoleViewCompEvent, (XPointer) event) == True)) {
2224 XPutBackEvent(event->xkey.display, &returnEvent);
2225 /* Discard it, don't pass it to Qt. */
2226 return true;
2227 }
2228
2229 KeySym ks = ::XKeycodeToKeysym (event->xkey.display, event->xkey.keycode, 0);
2230
2231 int flags = 0;
2232 if (scan >> 8)
2233 flags |= KeyExtended;
2234 if (event->type == XKeyPress)
2235 flags |= KeyPressed;
2236
2237 /* Remove the extended flag */
2238 scan &= 0x7F;
2239
2240 switch (ks)
2241 {
2242 case XK_Print:
2243 flags |= KeyPrint;
2244 break;
2245 case XK_Pause:
2246 flags |= KeyPause;
2247 break;
2248 }
2249
2250 return keyEvent (ks, scan, flags);
2251}
2252
2253#elif defined (Q_WS_MAC)
2254
2255/**
2256 * Invoked by VBoxConsoleView::darwinEventHandlerProc / VBoxConsoleView::macEventFilter when
2257 * it receives a raw keyboard event.
2258 *
2259 * @param pvCocoaEvent The Cocoa keyboard event. Can be NULL in some configs.
2260 * @param inEvent The keyboard event.
2261 *
2262 * @return true if the key was processed, false if it wasn't processed and should be passed on.
2263 */
2264bool VBoxConsoleView::darwinKeyboardEvent (const void *pvCocoaEvent, EventRef inEvent)
2265{
2266 bool ret = false;
2267 UInt32 EventKind = ::GetEventKind (inEvent);
2268 if (EventKind != kEventRawKeyModifiersChanged)
2269 {
2270 /* convert keycode to set 1 scan code. */
2271 UInt32 keyCode = ~0U;
2272 ::GetEventParameter (inEvent, kEventParamKeyCode, typeUInt32, NULL, sizeof (keyCode), NULL, &keyCode);
2273 unsigned scanCode = ::DarwinKeycodeToSet1Scancode (keyCode);
2274 if (scanCode)
2275 {
2276 /* calc flags. */
2277 int flags = 0;
2278 if (EventKind != kEventRawKeyUp)
2279 flags |= KeyPressed;
2280 if (scanCode & VBOXKEY_EXTENDED)
2281 flags |= KeyExtended;
2282 /** @todo KeyPause, KeyPrint. */
2283 scanCode &= VBOXKEY_SCANCODE_MASK;
2284
2285 /* get the unicode string (if present). */
2286 AssertCompileSize (wchar_t, 2);
2287 AssertCompileSize (UniChar, 2);
2288 ByteCount cbWritten = 0;
2289 wchar_t ucs[8];
2290 if (::GetEventParameter (inEvent, kEventParamKeyUnicodes, typeUnicodeText, NULL,
2291 sizeof (ucs), &cbWritten, &ucs[0]) != 0)
2292 cbWritten = 0;
2293 ucs[cbWritten / sizeof(wchar_t)] = 0; /* The api doesn't terminate it. */
2294
2295 ret = keyEvent (keyCode, scanCode, flags, ucs[0] ? ucs : NULL);
2296 }
2297 }
2298 else
2299 {
2300 /* May contain multiple modifier changes, kind of annoying. */
2301 UInt32 newMask = 0;
2302 ::GetEventParameter (inEvent, kEventParamKeyModifiers, typeUInt32, NULL,
2303 sizeof (newMask), NULL, &newMask);
2304 newMask = ::DarwinAdjustModifierMask (newMask, pvCocoaEvent);
2305 UInt32 changed = newMask ^ mDarwinKeyModifiers;
2306 if (changed)
2307 {
2308 for (UInt32 bit = 0; bit < 32; bit++)
2309 {
2310 if (!(changed & (1 << bit)))
2311 continue;
2312 unsigned scanCode = ::DarwinModifierMaskToSet1Scancode (1 << bit);
2313 if (!scanCode)
2314 continue;
2315 unsigned keyCode = ::DarwinModifierMaskToDarwinKeycode (1 << bit);
2316 Assert (keyCode);
2317
2318 if (!(scanCode & VBOXKEY_LOCK))
2319 {
2320 unsigned flags = (newMask & (1 << bit)) ? KeyPressed : 0;
2321 if (scanCode & VBOXKEY_EXTENDED)
2322 flags |= KeyExtended;
2323 scanCode &= VBOXKEY_SCANCODE_MASK;
2324 ret |= keyEvent (keyCode, scanCode & 0xff, flags);
2325 }
2326 else
2327 {
2328 unsigned flags = 0;
2329 if (scanCode & VBOXKEY_EXTENDED)
2330 flags |= KeyExtended;
2331 scanCode &= VBOXKEY_SCANCODE_MASK;
2332 keyEvent (keyCode, scanCode, flags | KeyPressed);
2333 keyEvent (keyCode, scanCode, flags);
2334 }
2335 }
2336 }
2337
2338 mDarwinKeyModifiers = newMask;
2339
2340 /* Always return true here because we'll otherwise getting a Qt event
2341 we don't want and that will only cause the Pause warning to pop up. */
2342 ret = true;
2343 }
2344
2345 return ret;
2346}
2347
2348
2349/**
2350 * Installs or removes the keyboard event handler.
2351 *
2352 * @param fGrab True if we're to grab the events, false if we're not to.
2353 */
2354void VBoxConsoleView::darwinGrabKeyboardEvents (bool fGrab)
2355{
2356 mKeyboardGrabbed = fGrab;
2357 if (fGrab)
2358 {
2359 /* Disable mouse and keyboard event compression/delaying to make sure
2360 we *really* get all of the events. */
2361 ::CGSetLocalEventsSuppressionInterval (0.0);
2362 setMouseCoalescingEnabled (false);
2363
2364 /* Register the event callback/hook and grab the keyboard. */
2365# ifdef QT_MAC_USE_COCOA
2366 ::VBoxCocoaApplication_setCallback (UINT32_MAX, /** @todo fix mask */
2367 VBoxConsoleView::darwinEventHandlerProc, this);
2368
2369# elif !defined (VBOX_WITH_HACKED_QT)
2370 EventTypeSpec eventTypes[6];
2371 eventTypes[0].eventClass = kEventClassKeyboard;
2372 eventTypes[0].eventKind = kEventRawKeyDown;
2373 eventTypes[1].eventClass = kEventClassKeyboard;
2374 eventTypes[1].eventKind = kEventRawKeyUp;
2375 eventTypes[2].eventClass = kEventClassKeyboard;
2376 eventTypes[2].eventKind = kEventRawKeyRepeat;
2377 eventTypes[3].eventClass = kEventClassKeyboard;
2378 eventTypes[3].eventKind = kEventRawKeyModifiersChanged;
2379 /* For ignorning Command-H and Command-Q which aren't affected by the
2380 * global hotkey stuff (doesn't work well): */
2381 eventTypes[4].eventClass = kEventClassCommand;
2382 eventTypes[4].eventKind = kEventCommandProcess;
2383 eventTypes[5].eventClass = kEventClassCommand;
2384 eventTypes[5].eventKind = kEventCommandUpdateStatus;
2385
2386 EventHandlerUPP eventHandler = ::NewEventHandlerUPP (VBoxConsoleView::darwinEventHandlerProc);
2387
2388 mDarwinEventHandlerRef = NULL;
2389 ::InstallApplicationEventHandler (eventHandler, RT_ELEMENTS (eventTypes), &eventTypes[0],
2390 this, &mDarwinEventHandlerRef);
2391 ::DisposeEventHandlerUPP (eventHandler);
2392
2393# else /* VBOX_WITH_HACKED_QT */
2394 ((QIApplication *)qApp)->setEventFilter (VBoxConsoleView::macEventFilter, this);
2395# endif /* VBOX_WITH_HACKED_QT */
2396
2397 ::DarwinGrabKeyboard (false);
2398 }
2399 else
2400 {
2401 ::DarwinReleaseKeyboard();
2402# ifdef QT_MAC_USE_COCOA
2403 ::VBoxCocoaApplication_unsetCallback (UINT32_MAX, /** @todo fix mask */
2404 VBoxConsoleView::darwinEventHandlerProc, this);
2405# elif !defined(VBOX_WITH_HACKED_QT)
2406 if (mDarwinEventHandlerRef)
2407 {
2408 ::RemoveEventHandler (mDarwinEventHandlerRef);
2409 mDarwinEventHandlerRef = NULL;
2410 }
2411# else /* VBOX_WITH_HACKED_QT */
2412 ((QIApplication *)qApp)->setEventFilter (NULL, NULL);
2413# endif /* VBOX_WITH_HACKED_QT */
2414 }
2415}
2416
2417#endif // defined (Q_WS_MAC)
2418
2419//
2420// Private members
2421/////////////////////////////////////////////////////////////////////////////
2422
2423/**
2424 * Called on every focus change and also to forcibly capture/uncapture the
2425 * input in situations similar to gaining or losing focus.
2426 *
2427 * @param aHasFocus true if the window got focus and false otherwise.
2428 * @param aReleaseHostKey true to release the host key (used only when
2429 * @a aHasFocus is false.
2430 */
2431void VBoxConsoleView::focusEvent (bool aHasFocus,
2432 bool aReleaseHostKey /* = true */)
2433{
2434 if (aHasFocus)
2435 {
2436#ifdef RT_OS_WINDOWS
2437 if ( !mDisableAutoCapture && gs.autoCapture()
2438 && GetAncestor (winId(), GA_ROOT) == GetForegroundWindow())
2439#else
2440 if (!mDisableAutoCapture && gs.autoCapture())
2441#endif /* RT_OS_WINDOWS */
2442 {
2443 captureKbd (true);
2444/// @todo (dmik)
2445// the below is for the mouse auto-capture. disabled for now. in order to
2446// properly support it, we need to know when *all* mouse buttons are
2447// released after we got focus, and grab the mouse only after then.
2448// btw, the similar would be good the for keyboard auto-capture, too.
2449// if (!(mMouseAbsolute && mMouseIntegration))
2450// captureMouse (true);
2451 }
2452
2453 /* reset the single-time disable capture flag */
2454 if (mDisableAutoCapture)
2455 mDisableAutoCapture = false;
2456 }
2457 else
2458 {
2459 captureMouse (false);
2460 captureKbd (false, false);
2461 releaseAllPressedKeys (aReleaseHostKey);
2462 }
2463}
2464
2465/**
2466 * Synchronize the views of the host and the guest to the modifier keys.
2467 * This function will add up to 6 additional keycodes to codes.
2468 *
2469 * @param codes pointer to keycodes which are sent to the keyboard
2470 * @param count pointer to the keycodes counter
2471 */
2472void VBoxConsoleView::fixModifierState (LONG *codes, uint *count)
2473{
2474#if defined(Q_WS_X11)
2475
2476 Window wDummy1, wDummy2;
2477 int iDummy3, iDummy4, iDummy5, iDummy6;
2478 unsigned uMask;
2479 unsigned uKeyMaskNum = 0, uKeyMaskCaps = 0, uKeyMaskScroll = 0;
2480
2481 uKeyMaskCaps = LockMask;
2482 XModifierKeymap* map = XGetModifierMapping(QX11Info::display());
2483 KeyCode keyCodeNum = XKeysymToKeycode(QX11Info::display(), XK_Num_Lock);
2484 KeyCode keyCodeScroll = XKeysymToKeycode(QX11Info::display(), XK_Scroll_Lock);
2485
2486 for (int i = 0; i < 8; i++)
2487 {
2488 if ( keyCodeNum != NoSymbol
2489 && map->modifiermap[map->max_keypermod * i] == keyCodeNum)
2490 uKeyMaskNum = 1 << i;
2491 else if ( keyCodeScroll != NoSymbol
2492 && map->modifiermap[map->max_keypermod * i] == keyCodeScroll)
2493 uKeyMaskScroll = 1 << i;
2494 }
2495 XQueryPointer(QX11Info::display(), DefaultRootWindow(QX11Info::display()), &wDummy1, &wDummy2,
2496 &iDummy3, &iDummy4, &iDummy5, &iDummy6, &uMask);
2497 XFreeModifiermap(map);
2498
2499 if (muNumLockAdaptionCnt && (mNumLock ^ !!(uMask & uKeyMaskNum)))
2500 {
2501 muNumLockAdaptionCnt--;
2502 codes[(*count)++] = 0x45;
2503 codes[(*count)++] = 0x45 | 0x80;
2504 }
2505 if (muCapsLockAdaptionCnt && (mCapsLock ^ !!(uMask & uKeyMaskCaps)))
2506 {
2507 muCapsLockAdaptionCnt--;
2508 codes[(*count)++] = 0x3a;
2509 codes[(*count)++] = 0x3a | 0x80;
2510 }
2511
2512#elif defined(Q_WS_WIN32)
2513
2514 if (muNumLockAdaptionCnt && (mNumLock ^ !!(GetKeyState(VK_NUMLOCK))))
2515 {
2516 muNumLockAdaptionCnt--;
2517 codes[(*count)++] = 0x45;
2518 codes[(*count)++] = 0x45 | 0x80;
2519 }
2520 if (muCapsLockAdaptionCnt && (mCapsLock ^ !!(GetKeyState(VK_CAPITAL))))
2521 {
2522 muCapsLockAdaptionCnt--;
2523 codes[(*count)++] = 0x3a;
2524 codes[(*count)++] = 0x3a | 0x80;
2525 }
2526
2527#elif defined (Q_WS_MAC)
2528
2529 /* if (muNumLockAdaptionCnt) ... - NumLock isn't implemented by Mac OS X so ignore it. */
2530 if (muCapsLockAdaptionCnt && (mCapsLock ^ !!(::GetCurrentEventKeyModifiers() & alphaLock)))
2531 {
2532 muCapsLockAdaptionCnt--;
2533 codes[(*count)++] = 0x3a;
2534 codes[(*count)++] = 0x3a | 0x80;
2535 }
2536
2537#else
2538
2539//#warning Adapt VBoxConsoleView::fixModifierState
2540
2541#endif
2542
2543
2544}
2545
2546/**
2547 * Called on enter/exit seamless/fullscreen mode.
2548 */
2549void VBoxConsoleView::toggleFSMode (const QSize &aSize)
2550{
2551 if ((mGuestSupportsGraphics && mAutoresizeGuest) ||
2552 mMainWnd->isTrueSeamless() ||
2553 mMainWnd->isTrueFullscreen())
2554 {
2555 QSize newSize;
2556 if (aSize.isValid())
2557 {
2558 mNormalSize = aSize;
2559 newSize = maximumSize();
2560 }
2561 else
2562 newSize = mNormalSize;
2563 doResizeHint (newSize);
2564 }
2565}
2566
2567/**
2568 * Get the current available desktop geometry for the console/framebuffer
2569 *
2570 * @returns the geometry. An empty rectangle means unrestricted.
2571 */
2572QRect VBoxConsoleView::desktopGeometry()
2573{
2574 QRect rc;
2575 switch (mDesktopGeo)
2576 {
2577 case DesktopGeo_Fixed:
2578 case DesktopGeo_Automatic:
2579 rc = QRect (0, 0,
2580 RT_MAX (mDesktopGeometry.width(), mLastSizeHint.width()),
2581 RT_MAX (mDesktopGeometry.height(), mLastSizeHint.height()));
2582 break;
2583 case DesktopGeo_Any:
2584 rc = QRect (0, 0, 0, 0);
2585 break;
2586 default:
2587 AssertMsgFailed (("Bad geometry type %d\n", mDesktopGeo));
2588 }
2589 return rc;
2590}
2591
2592bool VBoxConsoleView::isAutoresizeGuestActive()
2593{
2594 return mGuestSupportsGraphics && mAutoresizeGuest;
2595}
2596
2597/**
2598 * Called on every key press and release (while in focus).
2599 *
2600 * @param aKey virtual scan code (virtual key on Win32 and KeySym on X11)
2601 * @param aScan hardware scan code
2602 * @param aFlags flags, a combination of Key* constants
2603 * @param aUniKey Unicode translation of the key. Optional.
2604 *
2605 * @return true to consume the event and false to pass it to Qt
2606 */
2607bool VBoxConsoleView::keyEvent (int aKey, uint8_t aScan, int aFlags,
2608 wchar_t *aUniKey/* = NULL*/)
2609{
2610#if 0
2611 {
2612 char buf [256];
2613 sprintf (buf, "aKey=%08X aScan=%02X aFlags=%08X",
2614 aKey, aScan, aFlags);
2615 mMainWnd->statusBar()->message (buf);
2616 }
2617#endif
2618
2619 const bool isHostKey = aKey == gs.hostKey();
2620
2621 LONG buf [16];
2622 LONG *codes = buf;
2623 uint count = 0;
2624 uint8_t whatPressed = 0;
2625
2626 if (!isHostKey && !mIsHostkeyPressed)
2627 {
2628 if (aFlags & KeyPrint)
2629 {
2630 static LONG PrintMake[] = { 0xE0, 0x2A, 0xE0, 0x37 };
2631 static LONG PrintBreak[] = { 0xE0, 0xB7, 0xE0, 0xAA };
2632 if (aFlags & KeyPressed)
2633 {
2634 codes = PrintMake;
2635 count = SIZEOF_ARRAY (PrintMake);
2636 }
2637 else
2638 {
2639 codes = PrintBreak;
2640 count = SIZEOF_ARRAY (PrintBreak);
2641 }
2642 }
2643 else if (aFlags & KeyPause)
2644 {
2645 if (aFlags & KeyPressed)
2646 {
2647 static LONG Pause[] = { 0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5 };
2648 codes = Pause;
2649 count = SIZEOF_ARRAY (Pause);
2650 }
2651 else
2652 {
2653 /* Pause shall not produce a break code */
2654 return true;
2655 }
2656 }
2657 else
2658 {
2659 if (aFlags & KeyPressed)
2660 {
2661 /* Check if the guest has the same view on the modifier keys (NumLock,
2662 * CapsLock, ScrollLock) as the X server. If not, send KeyPress events
2663 * to synchronize the state. */
2664 fixModifierState (codes, &count);
2665 }
2666
2667 /* Check if it's C-A-D and GUI/PassCAD is not true */
2668 if (!mPassCAD &&
2669 aScan == 0x53 /* Del */ &&
2670 ((mPressedKeys [0x38] & IsKeyPressed) /* Alt */ ||
2671 (mPressedKeys [0x38] & IsExtKeyPressed)) &&
2672 ((mPressedKeys [0x1d] & IsKeyPressed) /* Ctrl */ ||
2673 (mPressedKeys [0x1d] & IsExtKeyPressed)))
2674 {
2675 /* Use the C-A-D combination as a last resort to get the
2676 * keyboard and mouse back to the host when the user forgets
2677 * the Host Key. Note that it's always possible to send C-A-D
2678 * to the guest using the Host+Del combination. BTW, it would
2679 * be preferrable to completely ignore C-A-D in guests, but
2680 * that's not possible because we cannot predict what other
2681 * keys will be pressed next when one of C, A, D is held. */
2682
2683 if (isRunning() && mKbdCaptured)
2684 {
2685 captureKbd (false);
2686 if (!(mMouseAbsolute && mMouseIntegration))
2687 captureMouse (false);
2688 }
2689
2690 return true;
2691 }
2692
2693 /* process the scancode and update the table of pressed keys */
2694 whatPressed = IsKeyPressed;
2695
2696 if (aFlags & KeyExtended)
2697 {
2698 codes [count++] = 0xE0;
2699 whatPressed = IsExtKeyPressed;
2700 }
2701
2702 if (aFlags & KeyPressed)
2703 {
2704 codes [count++] = aScan;
2705 mPressedKeys [aScan] |= whatPressed;
2706 }
2707 else
2708 {
2709 /* if we haven't got this key's press message, we ignore its
2710 * release */
2711 if (!(mPressedKeys [aScan] & whatPressed))
2712 return true;
2713 codes [count++] = aScan | 0x80;
2714 mPressedKeys [aScan] &= ~whatPressed;
2715 }
2716
2717 if (mKbdCaptured)
2718 mPressedKeys [aScan] |= IsKbdCaptured;
2719 else
2720 mPressedKeys [aScan] &= ~IsKbdCaptured;
2721 }
2722 }
2723 else
2724 {
2725 /* currently this is used in winLowKeyboardEvent() only */
2726 hostkey_in_capture = mKbdCaptured;
2727 }
2728
2729 bool emitSignal = false;
2730 int hotkey = 0;
2731
2732 /* process the host key */
2733 if (aFlags & KeyPressed)
2734 {
2735 if (isHostKey)
2736 {
2737 if (!mIsHostkeyPressed)
2738 {
2739 mIsHostkeyPressed = mIsHostkeyAlone = true;
2740 if (isRunning())
2741 saveKeyStates();
2742 emitSignal = true;
2743 }
2744 }
2745 else
2746 {
2747 if (mIsHostkeyPressed)
2748 {
2749 if (mIsHostkeyAlone)
2750 {
2751 hotkey = aKey;
2752 mIsHostkeyAlone = false;
2753 }
2754 }
2755 }
2756 }
2757 else
2758 {
2759 if (isHostKey)
2760 {
2761 if (mIsHostkeyPressed)
2762 {
2763 mIsHostkeyPressed = false;
2764
2765 if (mIsHostkeyAlone)
2766 {
2767 if (isPaused())
2768 {
2769 vboxProblem().remindAboutPausedVMInput();
2770 }
2771 else
2772 if (isRunning())
2773 {
2774 bool captured = mKbdCaptured;
2775 bool ok = true;
2776 if (!captured)
2777 {
2778 /* temporarily disable auto capture that will take
2779 * place after this dialog is dismissed because
2780 * the capture state is to be defined by the
2781 * dialog result itself */
2782 mDisableAutoCapture = true;
2783 bool autoConfirmed = false;
2784 ok = vboxProblem().confirmInputCapture (&autoConfirmed);
2785 if (autoConfirmed)
2786 mDisableAutoCapture = false;
2787 /* otherwise, the disable flag will be reset in
2788 * the next console view's foucs in event (since
2789 * may happen asynchronously on some platforms,
2790 * after we return from this code) */
2791 }
2792
2793 if (ok)
2794 {
2795 captureKbd (!captured, false);
2796 if (!(mMouseAbsolute && mMouseIntegration))
2797 {
2798#ifdef Q_WS_X11
2799 /* make sure that pending FocusOut events from the
2800 * previous message box are handled, otherwise the
2801 * mouse is immediately ungrabbed. */
2802 qApp->processEvents();
2803#endif
2804 captureMouse (mKbdCaptured);
2805 }
2806 }
2807 }
2808 }
2809
2810 if (isRunning())
2811 sendChangedKeyStates();
2812
2813 emitSignal = true;
2814 }
2815 }
2816 else
2817 {
2818 if (mIsHostkeyPressed)
2819 mIsHostkeyAlone = false;
2820 }
2821 }
2822
2823 /* emit the keyboard state change signal */
2824 if (emitSignal)
2825 emitKeyboardStateChanged();
2826
2827 /* Process Host+<key> shortcuts. currently, <key> is limited to
2828 * alphanumeric chars. Other Host+<key> combinations are handled in
2829 * event(). */
2830 if (hotkey)
2831 {
2832 bool processed = false;
2833#if defined (Q_WS_WIN32)
2834 NOREF(aUniKey);
2835 int n = GetKeyboardLayoutList (0, NULL);
2836 Assert (n);
2837 HKL *list = new HKL [n];
2838 GetKeyboardLayoutList (n, list);
2839 for (int i = 0; i < n && !processed; i++)
2840 {
2841 wchar_t ch;
2842 static BYTE keys [256] = {0};
2843 if (!ToUnicodeEx (hotkey, 0, keys, &ch, 1, 0, list [i]) == 1)
2844 ch = 0;
2845 if (ch)
2846 processed = processHotKey (QKeySequence (Qt::UNICODE_ACCEL +
2847 QChar (ch).toUpper().unicode()),
2848 mMainWnd->menuBar()->actions());
2849 }
2850 delete[] list;
2851#elif defined (Q_WS_X11)
2852 NOREF(aUniKey);
2853 Display *display = QX11Info::display();
2854 int keysyms_per_keycode = getKeysymsPerKeycode();
2855 KeyCode kc = XKeysymToKeycode (display, aKey);
2856 // iterate over the first level (not shifted) keysyms in every group
2857 for (int i = 0; i < keysyms_per_keycode && !processed; i += 2)
2858 {
2859 KeySym ks = XKeycodeToKeysym (display, kc, i);
2860 char ch = 0;
2861 if (!XkbTranslateKeySym (display, &ks, 0, &ch, 1, NULL) == 1)
2862 ch = 0;
2863 if (ch)
2864 {
2865 QChar c = QString::fromLocal8Bit (&ch, 1) [0];
2866 processed = processHotKey (QKeySequence (Qt::UNICODE_ACCEL +
2867 c.toUpper().unicode()),
2868 mMainWnd->menuBar()->actions());
2869 }
2870 }
2871#elif defined (Q_WS_MAC)
2872 if (aUniKey && aUniKey [0] && !aUniKey [1])
2873 processed = processHotKey (QKeySequence (Qt::UNICODE_ACCEL +
2874 QChar (aUniKey [0]).toUpper().unicode()),
2875 mMainWnd->menuBar()->actions());
2876
2877 /* Don't consider the hot key as pressed since the guest never saw
2878 * it. (probably a generic thing) */
2879 mPressedKeys [aScan] &= ~whatPressed;
2880#endif
2881
2882 /* grab the key from Qt if processed, or pass it to Qt otherwise
2883 * in order to process non-alphanumeric keys in event(), after they are
2884 * converted to Qt virtual keys. */
2885 return processed;
2886 }
2887
2888 /* no more to do, if the host key is in action or the VM is paused */
2889 if (mIsHostkeyPressed || isHostKey || isPaused())
2890 {
2891 /* grab the key from Qt and from VM if it's a host key,
2892 * otherwise just pass it to Qt */
2893 return isHostKey;
2894 }
2895
2896 CKeyboard keyboard = mConsole.GetKeyboard();
2897 Assert (!keyboard.isNull());
2898
2899#if defined (Q_WS_WIN32)
2900 /* send pending WM_PAINT events */
2901 ::UpdateWindow (viewport()->winId());
2902#endif
2903
2904#if 0
2905 {
2906 char buf [256];
2907 sprintf (buf, "*** SCANS: ");
2908 for (uint i = 0; i < count; ++ i)
2909 sprintf (buf + strlen (buf), "%02X ", codes [i]);
2910 mMainWnd->statusBar()->message (buf);
2911 LogFlow (("%s\n", buf));
2912 }
2913#endif
2914
2915 std::vector <LONG> scancodes(codes, &codes[count]);
2916 keyboard.PutScancodes (QVector<LONG>::fromStdVector(scancodes));
2917
2918 /* grab the key from Qt */
2919 return true;
2920}
2921
2922/**
2923 * Called on every mouse/wheel move and button press/release.
2924 *
2925 * @return true to consume the event and false to pass it to Qt
2926 */
2927bool VBoxConsoleView::mouseEvent (int aType, const QPoint &aPos, const QPoint &aGlobalPos,
2928 Qt::MouseButtons aButtons, Qt::KeyboardModifiers aModifiers,
2929 int aWheelDelta, Qt::Orientation aWheelDir)
2930{
2931#if 0
2932 char buf [256];
2933 sprintf (buf,
2934 "MOUSE: type=%03d x=%03d y=%03d btn=%03d btns=%08X mod=%08X "
2935 "wdelta=%03d wdir=%03d",
2936 aType, aPos.x(), aPos.y(), aButtons, aModifiers,
2937 aWheelDelta, aWheelDir);
2938 mMainWnd->statusBar()->message (buf);
2939#else
2940 Q_UNUSED (aModifiers);
2941#endif
2942
2943 int state = 0;
2944 if (aButtons & Qt::LeftButton)
2945 state |= KMouseButtonState_LeftButton;
2946 if (aButtons & Qt::RightButton)
2947 state |= KMouseButtonState_RightButton;
2948 if (aButtons & Qt::MidButton)
2949 state |= KMouseButtonState_MiddleButton;
2950
2951#ifdef Q_WS_MAC
2952 /* Simulate the right click on
2953 * Host+Left Mouse */
2954 if (mIsHostkeyPressed &&
2955 mIsHostkeyAlone &&
2956 state == KMouseButtonState_LeftButton)
2957 state = KMouseButtonState_RightButton;
2958#endif /* Q_WS_MAC */
2959
2960 int wheel = 0;
2961 if (aWheelDir == Qt::Vertical)
2962 {
2963 /* the absolute value of wheel delta is 120 units per every wheel
2964 * move; positive deltas correspond to counterclockwize rotations
2965 * (usually up), negative -- to clockwize (usually down). */
2966 wheel = - (aWheelDelta / 120);
2967 }
2968
2969 if (mMouseCaptured)
2970 {
2971#ifdef Q_WS_WIN32
2972 /* send pending WM_PAINT events */
2973 ::UpdateWindow (viewport()->winId());
2974#endif
2975
2976 CMouse mouse = mConsole.GetMouse();
2977 mouse.PutMouseEvent (aGlobalPos.x() - mLastPos.x(),
2978 aGlobalPos.y() - mLastPos.y(),
2979 wheel, state);
2980
2981#if defined (Q_WS_MAC)
2982 /*
2983 * Keep the mouse from leaving the widget.
2984 *
2985 * This is a bit tricky to get right because if it escapes we won't necessarily
2986 * get mouse events any longer and can warp it back. So, we keep safety zone
2987 * of up to 300 pixels around the borders of the widget to prevent this from
2988 * happening. Also, the mouse is warped back to the center of the widget.
2989 *
2990 * (Note, aPos seems to be unreliable, it caused endless recursion here at one points...)
2991 * (Note, synergy and other remote clients might not like this cursor warping.)
2992 */
2993 QRect rect = viewport()->visibleRegion().boundingRect();
2994 QPoint pw = viewport()->mapToGlobal (viewport()->pos());
2995 rect.translate (pw.x(), pw.y());
2996
2997 QRect dpRect = QApplication::desktop()->screenGeometry (viewport());
2998 if (rect.intersects (dpRect))
2999 rect = rect.intersect (dpRect);
3000
3001 int wsafe = rect.width() / 6;
3002 rect.setWidth (rect.width() - wsafe * 2);
3003 rect.setLeft (rect.left() + wsafe);
3004
3005 int hsafe = rect.height() / 6;
3006 rect.setWidth (rect.height() - hsafe * 2);
3007 rect.setTop (rect.top() + hsafe);
3008
3009 if (rect.contains (aGlobalPos, true))
3010 mLastPos = aGlobalPos;
3011 else
3012 {
3013 mLastPos = rect.center();
3014 QCursor::setPos (mLastPos);
3015 }
3016
3017#else /* !Q_WS_MAC */
3018
3019 /* "jerk" the mouse by bringing it to the opposite side
3020 * to simulate the endless moving */
3021
3022#ifdef Q_WS_WIN32
3023 int we = viewport()->width() - 1;
3024 int he = viewport()->height() - 1;
3025 QPoint p = aPos;
3026 if (aPos.x() == 0)
3027 p.setX (we - 1);
3028 else if (aPos.x() == we)
3029 p.setX (1);
3030 if (aPos.y() == 0 )
3031 p.setY (he - 1);
3032 else if (aPos.y() == he)
3033 p.setY (1);
3034
3035 if (p != aPos)
3036 {
3037 mLastPos = viewport()->mapToGlobal (p);
3038 QCursor::setPos (mLastPos);
3039 }
3040 else
3041 {
3042 mLastPos = aGlobalPos;
3043 }
3044#else
3045 int we = QApplication::desktop()->width() - 1;
3046 int he = QApplication::desktop()->height() - 1;
3047 QPoint p = aGlobalPos;
3048 if (aGlobalPos.x() == 0)
3049 p.setX (we - 1);
3050 else if (aGlobalPos.x() == we)
3051 p.setX( 1 );
3052 if (aGlobalPos.y() == 0)
3053 p.setY (he - 1);
3054 else if (aGlobalPos.y() == he)
3055 p.setY (1);
3056
3057 if (p != aGlobalPos)
3058 {
3059 mLastPos = p;
3060 QCursor::setPos (mLastPos);
3061 }
3062 else
3063 {
3064 mLastPos = aGlobalPos;
3065 }
3066#endif
3067#endif /* !Q_WS_MAC */
3068 return true; /* stop further event handling */
3069 }
3070 else /* !mMouseCaptured */
3071 {
3072 if (mMainWnd->isTrueFullscreen())
3073 {
3074 if (mode != VBoxDefs::SDLMode)
3075 {
3076 /* try to automatically scroll the guest canvas if the
3077 * mouse is on the screen border */
3078 /// @todo (r=dmik) better use a timer for autoscroll
3079 QRect scrGeo = QApplication::desktop()->screenGeometry (this);
3080 int dx = 0, dy = 0;
3081 if (scrGeo.width() < contentsWidth())
3082 {
3083 if (scrGeo.left() == aGlobalPos.x()) dx = -1;
3084 if (scrGeo.right() == aGlobalPos.x()) dx = +1;
3085 }
3086 if (scrGeo.height() < contentsHeight())
3087 {
3088 if (scrGeo.top() == aGlobalPos.y()) dy = -1;
3089 if (scrGeo.bottom() == aGlobalPos.y()) dy = +1;
3090 }
3091 if (dx || dy)
3092 scrollBy (dx, dy);
3093 }
3094 }
3095
3096 if (mMouseAbsolute && mMouseIntegration)
3097 {
3098 int cw = contentsWidth(), ch = contentsHeight();
3099 int vw = visibleWidth(), vh = visibleHeight();
3100
3101 if (mode != VBoxDefs::SDLMode)
3102 {
3103 /* try to automatically scroll the guest canvas if the
3104 * mouse goes outside its visible part */
3105
3106 int dx = 0;
3107 if (aPos.x() > vw) dx = aPos.x() - vw;
3108 else if (aPos.x() < 0) dx = aPos.x();
3109 int dy = 0;
3110 if (aPos.y() > vh) dy = aPos.y() - vh;
3111 else if (aPos.y() < 0) dy = aPos.y();
3112 if (dx != 0 || dy != 0) scrollBy (dx, dy);
3113 }
3114
3115 QPoint cpnt = viewportToContents (aPos);
3116 if (cpnt.x() < 0) cpnt.setX (0);
3117 else if (cpnt.x() >= cw) cpnt.setX (cw - 1);
3118 if (cpnt.y() < 0) cpnt.setY (0);
3119 else if (cpnt.y() >= ch) cpnt.setY (ch - 1);
3120
3121 CMouse mouse = mConsole.GetMouse();
3122 mouse.PutMouseEventAbsolute (cpnt.x() + 1, cpnt.y() + 1,
3123 wheel, state);
3124 return true; /* stop further event handling */
3125 }
3126 else
3127 {
3128 if (hasFocus() &&
3129 (aType == QEvent::MouseButtonRelease &&
3130 aButtons == Qt::NoButton))
3131 {
3132 if (isPaused())
3133 {
3134 vboxProblem().remindAboutPausedVMInput();
3135 }
3136 else if (isRunning())
3137 {
3138 /* temporarily disable auto capture that will take
3139 * place after this dialog is dismissed because
3140 * the capture state is to be defined by the
3141 * dialog result itself */
3142 mDisableAutoCapture = true;
3143 bool autoConfirmed = false;
3144 bool ok = vboxProblem().confirmInputCapture (&autoConfirmed);
3145 if (autoConfirmed)
3146 mDisableAutoCapture = false;
3147 /* otherwise, the disable flag will be reset in
3148 * the next console view's foucs in event (since
3149 * may happen asynchronously on some platforms,
3150 * after we return from this code) */
3151
3152 if (ok)
3153 {
3154#ifdef Q_WS_X11
3155 /* make sure that pending FocusOut events from the
3156 * previous message box are handled, otherwise the
3157 * mouse is immediately ungrabbed again */
3158 qApp->processEvents();
3159#endif
3160 captureKbd (true);
3161 captureMouse (true);
3162 }
3163 }
3164 }
3165 }
3166 }
3167
3168 return false;
3169}
3170
3171void VBoxConsoleView::onStateChange (KMachineState state)
3172{
3173 switch (state)
3174 {
3175 case KMachineState_Paused:
3176 {
3177 if (mode != VBoxDefs::TimerMode && mFrameBuf)
3178 {
3179 /*
3180 * Take a screen snapshot. Note that TakeScreenShot() always
3181 * needs a 32bpp image
3182 */
3183 QImage shot = QImage (mFrameBuf->width(), mFrameBuf->height(), QImage::Format_RGB32);
3184 CDisplay dsp = mConsole.GetDisplay();
3185 dsp.TakeScreenShot (shot.bits(), shot.width(), shot.height());
3186 /*
3187 * TakeScreenShot() may fail if, e.g. the Paused notification
3188 * was delivered after the machine execution was resumed. It's
3189 * not fatal.
3190 */
3191 if (dsp.isOk())
3192 {
3193 dimImage (shot);
3194 mPausedShot = QPixmap::fromImage (shot);
3195 /* fully repaint to pick up mPausedShot */
3196 repaint();
3197 }
3198 }
3199 /* fall through */
3200 }
3201 case KMachineState_Stuck:
3202 {
3203 /* reuse the focus event handler to uncapture everything */
3204 if (hasFocus())
3205 focusEvent (false /* aHasFocus*/, false /* aReleaseHostKey */);
3206 break;
3207 }
3208 case KMachineState_Running:
3209 {
3210 if (mLastState == KMachineState_Paused)
3211 {
3212 if (mode != VBoxDefs::TimerMode && mFrameBuf)
3213 {
3214 /* reset the pixmap to free memory */
3215 mPausedShot = QPixmap ();
3216 /*
3217 * ask for full guest display update (it will also update
3218 * the viewport through IFramebuffer::NotifyUpdate)
3219 */
3220 CDisplay dsp = mConsole.GetDisplay();
3221 dsp.InvalidateAndUpdate();
3222 }
3223 }
3224 /* reuse the focus event handler to capture input */
3225 if (hasFocus())
3226 focusEvent (true /* aHasFocus */);
3227 break;
3228 }
3229 default:
3230 break;
3231 }
3232
3233 mLastState = state;
3234}
3235
3236void VBoxConsoleView::doRefresh()
3237{
3238 viewport()->repaint();
3239}
3240
3241void VBoxConsoleView::resizeEvent (QResizeEvent *)
3242{
3243 updateSliders();
3244#if defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA)
3245 QRect r = viewport()->geometry();
3246// printf ("qt resize: %d %d %d %d\n", r.x(), r.y(), r.width(), r.height());
3247 PostBoundsChanged (r);
3248#endif /* Q_WS_MAC */
3249}
3250
3251void VBoxConsoleView::moveEvent (QMoveEvent *)
3252{
3253#if defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA)
3254 QRect r = viewport()->geometry();
3255// printf ("qt resize: %d %d %d %d\n", r.x(), r.y(), r.width(), r.height());
3256 PostBoundsChanged (r);
3257#endif /* Q_WS_MAC */
3258}
3259
3260void VBoxConsoleView::paintEvent (QPaintEvent *pe)
3261{
3262 if (mPausedShot.isNull())
3263 {
3264 /* delegate the paint function to the VBoxFrameBuffer interface */
3265 if (mFrameBuf)
3266 mFrameBuf->paintEvent (pe);
3267#ifdef Q_WS_MAC
3268 /* Update the dock icon if we are in the running state */
3269 if (isRunning())
3270 updateDockIcon();
3271#endif
3272 return;
3273 }
3274
3275#ifdef VBOX_GUI_USE_QUARTZ2D
3276 if (mode == VBoxDefs::Quartz2DMode && mFrameBuf)
3277 {
3278 mFrameBuf->paintEvent (pe);
3279 updateDockIcon();
3280 }
3281 else
3282#endif
3283 {
3284 /* we have a snapshot for the paused state */
3285 QRect r = pe->rect().intersect (viewport()->rect());
3286 /* We have to disable paint on screen if we are using the regular painter */
3287 bool paintOnScreen = viewport()->testAttribute (Qt::WA_PaintOnScreen);
3288 viewport()->setAttribute (Qt::WA_PaintOnScreen, false);
3289 QPainter pnt (viewport());
3290 pnt.drawPixmap (r.x(), r.y(), mPausedShot,
3291 r.x() + contentsX(), r.y() + contentsY(),
3292 r.width(), r.height());
3293 /* Restore the attribute to its previous state */
3294 viewport()->setAttribute (Qt::WA_PaintOnScreen, paintOnScreen);
3295#ifdef Q_WS_MAC
3296 updateDockIcon();
3297#endif
3298 }
3299
3300}
3301
3302/**
3303 * Captures the keyboard. When captured, no keyboard input reaches the host
3304 * system (including most system combinations like Alt-Tab).
3305 *
3306 * @param aCapture true to capture, false to uncapture.
3307 * @param aEmitSignal Whether to emit keyboardStateChanged() or not.
3308 */
3309void VBoxConsoleView::captureKbd (bool aCapture, bool aEmitSignal /* = true */)
3310{
3311 AssertMsg (mAttached, ("Console must be attached"));
3312
3313 if (mKbdCaptured == aCapture)
3314 return;
3315
3316 /* On Win32, keyboard grabbing is ineffective, a low-level keyboard hook is
3317 * used instead. On X11, we use XGrabKey instead of XGrabKeyboard (called
3318 * by QWidget::grabKeyboard()) because the latter causes problems under
3319 * metacity 2.16 (in particular, due to a bug, a window cannot be moved
3320 * using the mouse if it is currently grabing the keyboard). On Mac OS X,
3321 * we use the Qt methods + disabling global hot keys + watching modifiers
3322 * (for right/left separation). */
3323#if defined (Q_WS_WIN32)
3324 /**/
3325#elif defined (Q_WS_X11)
3326 if (aCapture)
3327 XGrabKey (QX11Info::display(), AnyKey, AnyModifier,
3328 window()->winId(), False,
3329 GrabModeAsync, GrabModeAsync);
3330 else
3331 XUngrabKey (QX11Info::display(), AnyKey, AnyModifier,
3332 window()->winId());
3333#elif defined (Q_WS_MAC)
3334 if (aCapture)
3335 {
3336 ::DarwinDisableGlobalHotKeys (true);
3337 grabKeyboard();
3338 }
3339 else
3340 {
3341 ::DarwinDisableGlobalHotKeys (false);
3342 releaseKeyboard();
3343 }
3344#else
3345 if (aCapture)
3346 grabKeyboard();
3347 else
3348 releaseKeyboard();
3349#endif
3350
3351 mKbdCaptured = aCapture;
3352
3353 if (aEmitSignal)
3354 emitKeyboardStateChanged();
3355}
3356
3357/**
3358 * Captures the host mouse pointer. When captured, the mouse pointer is
3359 * unavailable to the host applications.
3360 *
3361 * @param aCapture true to capture, false to uncapture.
3362 * @param aEmitSignal Whether to emit mouseStateChanged() or not.
3363 */
3364void VBoxConsoleView::captureMouse (bool aCapture, bool aEmitSignal /* = true */)
3365{
3366 AssertMsg (mAttached, ("Console must be attached"));
3367
3368 if (mMouseCaptured == aCapture)
3369 return;
3370
3371 if (aCapture)
3372 {
3373 /* memorize the host position where the cursor was captured */
3374 mCapturedPos = QCursor::pos();
3375#ifdef Q_WS_WIN32
3376 viewport()->setCursor (QCursor (Qt::BlankCursor));
3377 /* move the mouse to the center of the visible area */
3378 QCursor::setPos (mapToGlobal (visibleRegion().boundingRect().center()));
3379 mLastPos = QCursor::pos();
3380#elif defined (Q_WS_MAC)
3381 /* move the mouse to the center of the visible area */
3382 mLastPos = mapToGlobal (visibleRegion().boundingRect().center());
3383 QCursor::setPos (mLastPos);
3384 /* grab all mouse events. */
3385 viewport()->grabMouse();
3386#else
3387 viewport()->grabMouse();
3388 mLastPos = QCursor::pos();
3389#endif
3390 }
3391 else
3392 {
3393#ifndef Q_WS_WIN32
3394 viewport()->releaseMouse();
3395#endif
3396 /* release mouse buttons */
3397 CMouse mouse = mConsole.GetMouse();
3398 mouse.PutMouseEvent (0, 0, 0, 0);
3399 }
3400
3401 mMouseCaptured = aCapture;
3402
3403 updateMouseClipping();
3404
3405 if (aEmitSignal)
3406 emitMouseStateChanged();
3407}
3408
3409/**
3410 * Searches for a menu item with a given hot key (shortcut). If the item
3411 * is found, activates it and returns true. Otherwise returns false.
3412 */
3413bool VBoxConsoleView::processHotKey (const QKeySequence &aKey,
3414 const QList <QAction*> &aData)
3415{
3416 foreach (QAction *pAction, aData)
3417 {
3418 if (QMenu *menu = pAction->menu())
3419 {
3420 /* Process recursively for each sub-menu */
3421 if (processHotKey (aKey, menu->actions()))
3422 return true;
3423 }
3424 else
3425 {
3426 QString hotkey = VBoxGlobal::extractKeyFromActionText (pAction->text());
3427 if (pAction->isEnabled() && !hotkey.isEmpty())
3428 {
3429 if (aKey.matches (QKeySequence (hotkey)) == QKeySequence::ExactMatch)
3430 {
3431 /* We asynchronously post a special event instead of calling
3432 * pAction->trigger() directly, to let key presses and
3433 * releases be processed correctly by Qt first.
3434 * Note: we assume that nobody will delete the menu item
3435 * corresponding to the key sequence, so that the pointer to
3436 * menu data posted along with the event will remain valid in
3437 * the event handler, at least until the main window is closed. */
3438 QApplication::postEvent (this, new ActivateMenuEvent (pAction));
3439 return true;
3440 }
3441 }
3442 }
3443 }
3444
3445 return false;
3446}
3447
3448/**
3449 * Send the KEY BREAK code to the VM for all currently pressed keys.
3450 *
3451 * @param aReleaseHostKey @c true to set the host key state to unpressed.
3452 */
3453void VBoxConsoleView::releaseAllPressedKeys (bool aReleaseHostKey /* = true*/)
3454{
3455 AssertMsg (mAttached, ("Console must be attached"));
3456
3457 CKeyboard keyboard = mConsole.GetKeyboard();
3458 bool fSentRESEND = false;
3459
3460 /* send a dummy scan code (RESEND) to prevent the guest OS from recognizing
3461 * a single key click (for ex., Alt) and performing an unwanted action
3462 * (for ex., activating the menu) when we release all pressed keys below.
3463 * Note, that it's just a guess that sending RESEND will give the desired
3464 * effect :), but at least it works with NT and W2k guests. */
3465
3466 /// @todo Sending 0xFE is responsible for the warning
3467 //
3468 // ``atkbd.c: Spurious NAK on isa0060/serio0. Some program might
3469 // be trying access hardware directly''
3470 //
3471 // on Linux guests (#1944). It might also be responsible for #1949. Don't
3472 // send this command unless we really have to release any key modifier.
3473 // --frank
3474
3475 for (uint i = 0; i < SIZEOF_ARRAY (mPressedKeys); i++)
3476 {
3477 if (mPressedKeys [i] & IsKeyPressed)
3478 {
3479 if (!fSentRESEND)
3480 {
3481 keyboard.PutScancode (0xFE);
3482 fSentRESEND = true;
3483 }
3484 keyboard.PutScancode (i | 0x80);
3485 }
3486 else if (mPressedKeys [i] & IsExtKeyPressed)
3487 {
3488 if (!fSentRESEND)
3489 {
3490 keyboard.PutScancode (0xFE);
3491 fSentRESEND = true;
3492 }
3493 QVector <LONG> codes (2);
3494 codes[0] = 0xE0;
3495 codes[1] = i | 0x80;
3496 keyboard.PutScancodes (codes);
3497 }
3498 mPressedKeys [i] = 0;
3499 }
3500
3501 if (aReleaseHostKey)
3502 mIsHostkeyPressed = false;
3503
3504#ifdef Q_WS_MAC
3505 /* clear most of the modifiers. */
3506 mDarwinKeyModifiers &=
3507 alphaLock | kEventKeyModifierNumLockMask |
3508 (aReleaseHostKey ? 0 : ::DarwinKeyCodeToDarwinModifierMask (gs.hostKey()));
3509#endif
3510
3511 emitKeyboardStateChanged();
3512}
3513
3514void VBoxConsoleView::saveKeyStates()
3515{
3516 ::memcpy (mPressedKeysCopy, mPressedKeys, sizeof (mPressedKeys));
3517}
3518
3519void VBoxConsoleView::sendChangedKeyStates()
3520{
3521 AssertMsg (mAttached, ("Console must be attached"));
3522
3523 QVector <LONG> codes (2);
3524 CKeyboard keyboard = mConsole.GetKeyboard();
3525 for (uint i = 0; i < SIZEOF_ARRAY (mPressedKeys); ++ i)
3526 {
3527 uint8_t os = mPressedKeysCopy [i];
3528 uint8_t ns = mPressedKeys [i];
3529 if ((os & IsKeyPressed) != (ns & IsKeyPressed))
3530 {
3531 codes [0] = i;
3532 if (!(ns & IsKeyPressed))
3533 codes[0] |= 0x80;
3534 keyboard.PutScancode (codes[0]);
3535 }
3536 else if ((os & IsExtKeyPressed) != (ns & IsExtKeyPressed))
3537 {
3538 codes [0] = 0xE0;
3539 codes [1] = i;
3540 if (!(ns & IsExtKeyPressed))
3541 codes [1] |= 0x80;
3542 keyboard.PutScancodes (codes);
3543 }
3544 }
3545}
3546
3547void VBoxConsoleView::updateMouseClipping()
3548{
3549 AssertMsg (mAttached, ("Console must be attached"));
3550
3551 if (mMouseCaptured)
3552 {
3553 viewport()->setCursor (QCursor (Qt::BlankCursor));
3554#ifdef Q_WS_WIN32
3555 QRect r = viewport()->rect();
3556 r.moveTopLeft (viewport()->mapToGlobal (QPoint (0, 0)));
3557 RECT rect = { r.left(), r.top(), r.right() + 1, r.bottom() + 1 };
3558 ::ClipCursor (&rect);
3559#endif
3560 }
3561 else
3562 {
3563#ifdef Q_WS_WIN32
3564 ::ClipCursor (NULL);
3565#endif
3566 /* return the cursor to where it was when we captured it and show it */
3567 QCursor::setPos (mCapturedPos);
3568 viewport()->unsetCursor();
3569 }
3570}
3571
3572void VBoxConsoleView::setPointerShape (MousePointerChangeEvent *me)
3573{
3574 if (me->shapeData() != NULL)
3575 {
3576 bool ok = false;
3577
3578 const uchar *srcAndMaskPtr = me->shapeData();
3579 uint andMaskSize = (me->width() + 7) / 8 * me->height();
3580 const uchar *srcShapePtr = me->shapeData() + ((andMaskSize + 3) & ~3);
3581 uint srcShapePtrScan = me->width() * 4;
3582
3583#if defined (Q_WS_WIN)
3584
3585 BITMAPV5HEADER bi;
3586 HBITMAP hBitmap;
3587 void *lpBits;
3588
3589 ::ZeroMemory (&bi, sizeof (BITMAPV5HEADER));
3590 bi.bV5Size = sizeof (BITMAPV5HEADER);
3591 bi.bV5Width = me->width();
3592 bi.bV5Height = - (LONG) me->height();
3593 bi.bV5Planes = 1;
3594 bi.bV5BitCount = 32;
3595 bi.bV5Compression = BI_BITFIELDS;
3596 // specifiy a supported 32 BPP alpha format for Windows XP
3597 bi.bV5RedMask = 0x00FF0000;
3598 bi.bV5GreenMask = 0x0000FF00;
3599 bi.bV5BlueMask = 0x000000FF;
3600 if (me->hasAlpha())
3601 bi.bV5AlphaMask = 0xFF000000;
3602 else
3603 bi.bV5AlphaMask = 0;
3604
3605 HDC hdc = GetDC (NULL);
3606
3607 // create the DIB section with an alpha channel
3608 hBitmap = CreateDIBSection (hdc, (BITMAPINFO *) &bi, DIB_RGB_COLORS,
3609 (void **) &lpBits, NULL, (DWORD) 0);
3610
3611 ReleaseDC (NULL, hdc);
3612
3613 HBITMAP hMonoBitmap = NULL;
3614 if (me->hasAlpha())
3615 {
3616 // create an empty mask bitmap
3617 hMonoBitmap = CreateBitmap (me->width(), me->height(), 1, 1, NULL);
3618 }
3619 else
3620 {
3621 /* Word aligned AND mask. Will be allocated and created if necessary. */
3622 uint8_t *pu8AndMaskWordAligned = NULL;
3623
3624 /* Width in bytes of the original AND mask scan line. */
3625 uint32_t cbAndMaskScan = (me->width() + 7) / 8;
3626
3627 if (cbAndMaskScan & 1)
3628 {
3629 /* Original AND mask is not word aligned. */
3630
3631 /* Allocate memory for aligned AND mask. */
3632 pu8AndMaskWordAligned = (uint8_t *)RTMemTmpAllocZ ((cbAndMaskScan + 1) * me->height());
3633
3634 Assert(pu8AndMaskWordAligned);
3635
3636 if (pu8AndMaskWordAligned)
3637 {
3638 /* According to MSDN the padding bits must be 0.
3639 * Compute the bit mask to set padding bits to 0 in the last byte of original AND mask.
3640 */
3641 uint32_t u32PaddingBits = cbAndMaskScan * 8 - me->width();
3642 Assert(u32PaddingBits < 8);
3643 uint8_t u8LastBytesPaddingMask = (uint8_t)(0xFF << u32PaddingBits);
3644
3645 Log(("u8LastBytesPaddingMask = %02X, aligned w = %d, width = %d, cbAndMaskScan = %d\n",
3646 u8LastBytesPaddingMask, (cbAndMaskScan + 1) * 8, me->width(), cbAndMaskScan));
3647
3648 uint8_t *src = (uint8_t *)srcAndMaskPtr;
3649 uint8_t *dst = pu8AndMaskWordAligned;
3650
3651 unsigned i;
3652 for (i = 0; i < me->height(); i++)
3653 {
3654 memcpy (dst, src, cbAndMaskScan);
3655
3656 dst[cbAndMaskScan - 1] &= u8LastBytesPaddingMask;
3657
3658 src += cbAndMaskScan;
3659 dst += cbAndMaskScan + 1;
3660 }
3661 }
3662 }
3663
3664 /* create the AND mask bitmap */
3665 hMonoBitmap = ::CreateBitmap (me->width(), me->height(), 1, 1,
3666 pu8AndMaskWordAligned? pu8AndMaskWordAligned: srcAndMaskPtr);
3667
3668 if (pu8AndMaskWordAligned)
3669 {
3670 RTMemTmpFree (pu8AndMaskWordAligned);
3671 }
3672 }
3673
3674 Assert (hBitmap);
3675 Assert (hMonoBitmap);
3676 if (hBitmap && hMonoBitmap)
3677 {
3678 DWORD *dstShapePtr = (DWORD *) lpBits;
3679
3680 for (uint y = 0; y < me->height(); y ++)
3681 {
3682 memcpy (dstShapePtr, srcShapePtr, srcShapePtrScan);
3683 srcShapePtr += srcShapePtrScan;
3684 dstShapePtr += me->width();
3685 }
3686
3687 ICONINFO ii;
3688 ii.fIcon = FALSE;
3689 ii.xHotspot = me->xHot();
3690 ii.yHotspot = me->yHot();
3691 ii.hbmMask = hMonoBitmap;
3692 ii.hbmColor = hBitmap;
3693
3694 HCURSOR hAlphaCursor = CreateIconIndirect (&ii);
3695 Assert (hAlphaCursor);
3696 if (hAlphaCursor)
3697 {
3698 viewport()->setCursor (QCursor (hAlphaCursor));
3699 ok = true;
3700 if (mAlphaCursor)
3701 DestroyIcon (mAlphaCursor);
3702 mAlphaCursor = hAlphaCursor;
3703 }
3704 }
3705
3706 if (hMonoBitmap)
3707 DeleteObject (hMonoBitmap);
3708 if (hBitmap)
3709 DeleteObject (hBitmap);
3710
3711#elif defined (Q_WS_X11) && !defined (VBOX_WITHOUT_XCURSOR)
3712
3713 XcursorImage *img = XcursorImageCreate (me->width(), me->height());
3714 Assert (img);
3715 if (img)
3716 {
3717 img->xhot = me->xHot();
3718 img->yhot = me->yHot();
3719
3720 XcursorPixel *dstShapePtr = img->pixels;
3721
3722 for (uint y = 0; y < me->height(); y ++)
3723 {
3724 memcpy (dstShapePtr, srcShapePtr, srcShapePtrScan);
3725
3726 if (!me->hasAlpha())
3727 {
3728 /* convert AND mask to the alpha channel */
3729 uchar byte = 0;
3730 for (uint x = 0; x < me->width(); x ++)
3731 {
3732 if (!(x % 8))
3733 byte = *(srcAndMaskPtr ++);
3734 else
3735 byte <<= 1;
3736
3737 if (byte & 0x80)
3738 {
3739 /* Linux doesn't support inverted pixels (XOR ops,
3740 * to be exact) in cursor shapes, so we detect such
3741 * pixels and always replace them with black ones to
3742 * make them visible at least over light colors */
3743 if (dstShapePtr [x] & 0x00FFFFFF)
3744 dstShapePtr [x] = 0xFF000000;
3745 else
3746 dstShapePtr [x] = 0x00000000;
3747 }
3748 else
3749 dstShapePtr [x] |= 0xFF000000;
3750 }
3751 }
3752
3753 srcShapePtr += srcShapePtrScan;
3754 dstShapePtr += me->width();
3755 }
3756
3757 Cursor cur = XcursorImageLoadCursor (QX11Info::display(), img);
3758 Assert (cur);
3759 if (cur)
3760 {
3761 viewport()->setCursor (QCursor (cur));
3762 ok = true;
3763 }
3764
3765 XcursorImageDestroy (img);
3766 }
3767
3768#elif defined(Q_WS_MAC)
3769
3770 /* Create a ARGB image out of the shape data. */
3771 QImage image (me->width(), me->height(), QImage::Format_ARGB32);
3772 const uint8_t* pbSrcMask = static_cast<const uint8_t*> (srcAndMaskPtr);
3773 unsigned cbSrcMaskLine = RT_ALIGN (me->width(), 8) / 8;
3774 for (unsigned int y = 0; y < me->height(); ++y)
3775 {
3776 for (unsigned int x = 0; x < me->width(); ++x)
3777 {
3778 unsigned int color = ((unsigned int*)srcShapePtr)[y*me->width()+x];
3779 /* If the alpha channel isn't in the shape data, we have to
3780 * create them from the and-mask. This is a bit field where 1
3781 * represent transparency & 0 opaque respectively. */
3782 if (!me->hasAlpha())
3783 {
3784 if (!(pbSrcMask[x / 8] & (1 << (7 - (x % 8)))))
3785 color |= 0xff000000;
3786 else
3787 {
3788 /* This isn't quite right, but it's the best we can do I
3789 * think... */
3790 if (color & 0x00ffffff)
3791 color = 0xff000000;
3792 else
3793 color = 0x00000000;
3794 }
3795 }
3796 image.setPixel (x, y, color);
3797 }
3798 /* Move one scanline forward. */
3799 pbSrcMask += cbSrcMaskLine;
3800 }
3801 /* Set the new cursor */
3802 QCursor cursor (QPixmap::fromImage (image),
3803 me->xHot(), me->yHot());
3804 viewport()->setCursor (cursor);
3805 ok = true;
3806 NOREF (srcShapePtrScan);
3807
3808#else
3809
3810# warning "port me"
3811
3812#endif
3813 if (!ok)
3814 viewport()->unsetCursor();
3815 }
3816 else
3817 {
3818 /*
3819 * We did not get any shape data
3820 */
3821 if (me->isVisible())
3822 {
3823 /*
3824 * We're supposed to make the last shape we got visible.
3825 * We don't support that for now...
3826 */
3827 /// @todo viewport()->setCursor (QCursor());
3828 }
3829 else
3830 {
3831 viewport()->setCursor (Qt::BlankCursor);
3832 }
3833 }
3834 mHideHostPointer = !me->isVisible();
3835}
3836
3837inline QRgb qRgbIntensity (QRgb rgb, int mul, int div)
3838{
3839 int r = qRed (rgb);
3840 int g = qGreen (rgb);
3841 int b = qBlue (rgb);
3842 return qRgb (mul * r / div, mul * g / div, mul * b / div);
3843}
3844
3845/* static */
3846void VBoxConsoleView::dimImage (QImage &img)
3847{
3848 for (int y = 0; y < img.height(); y ++) {
3849 if (y % 2) {
3850 if (img.depth() == 32) {
3851 for (int x = 0; x < img.width(); x ++) {
3852 int gray = qGray (img.pixel (x, y)) / 2;
3853 img.setPixel (x, y, qRgb (gray, gray, gray));
3854// img.setPixel (x, y, qRgbIntensity (img.pixel (x, y), 1, 2));
3855 }
3856 } else {
3857 ::memset (img.scanLine (y), 0, img.bytesPerLine());
3858 }
3859 } else {
3860 if (img.depth() == 32) {
3861 for (int x = 0; x < img.width(); x ++) {
3862 int gray = (2 * qGray (img.pixel (x, y))) / 3;
3863 img.setPixel (x, y, qRgb (gray, gray, gray));
3864// img.setPixel (x, y, qRgbIntensity (img.pixel(x, y), 2, 3));
3865 }
3866 }
3867 }
3868 }
3869}
3870
3871void VBoxConsoleView::doResizeHint (const QSize &aToSize)
3872{
3873 if (mGuestSupportsGraphics && mAutoresizeGuest)
3874 {
3875 /* If this slot is invoked directly then use the passed size
3876 * otherwise get the available size for the guest display.
3877 * We assume here that the centralWidget() contains this view only
3878 * and gives it all available space. */
3879 QSize sz (aToSize.isValid() ? aToSize : mMainWnd->centralWidget()->size());
3880 if (!aToSize.isValid())
3881 sz -= QSize (frameWidth() * 2, frameWidth() * 2);
3882 /* We only actually send the hint if
3883 * 1) the autoresize property is set to true and
3884 * 2) either an explicit new size was given (e.g. if the request
3885 * was triggered directly by a console resize event) or if no
3886 * explicit size was specified but a resize is flagged as being
3887 * needed (e.g. the autoresize was just enabled and the console
3888 * was resized while it was disabled). */
3889 if (mAutoresizeGuest &&
3890 (aToSize.isValid() || mDoResize))
3891 {
3892 LogFlowFunc (("Will suggest %d x %d\n", sz.width(), sz.height()));
3893
3894 /* Increase the maximum allowed size to the new size if needed. */
3895 setDesktopGeoHint (sz.width(), sz.height());
3896
3897 mConsole.GetDisplay().SetVideoModeHint (sz.width(), sz.height(), 0, 0);
3898 }
3899 /* we have resized now... */
3900 mDoResize = false;
3901 }
3902}
3903
3904
3905/* If the desktop geometry is set automatically, this will update it. */
3906void VBoxConsoleView::doResizeDesktop (int)
3907{
3908 calculateDesktopGeometry();
3909}
3910
3911/**
3912 * Remember a geometry hint sent by the console window. This is used to
3913 * determine the maximum supported guest resolution in the @a desktopGeometry
3914 * method. A hint will always override other restrictions.
3915 *
3916 * @param aWidth width of the resolution hint
3917 * @param aHeight height of the resolution hint
3918 */
3919void VBoxConsoleView::setDesktopGeoHint (int aWidth, int aHeight)
3920{
3921 LogFlowThisFunc (("aWidth=%d, aHeight=%d\n", aWidth, aHeight));
3922 mLastSizeHint = QRect (0, 0, aWidth, aHeight);
3923}
3924
3925/**
3926 * Do initial setup of desktop geometry restrictions on the guest framebuffer.
3927 * These determine the maximum size the guest framebuffer can take on.
3928 *
3929 * @note a hint from the host will always override these restrictions.
3930 *
3931 * @param aGeo Fixed - the guest has a fixed maximum framebuffer size
3932 * Automatic - we calculate the maximum size ourselves. The
3933 * calculations will not actually be done until
3934 * @a calculateDesktopGeometry is called, since
3935 * we don't initially have the information needed.
3936 * Any - any size is allowed
3937 * @param aWidth The maximum width for the guest screen or zero for no change
3938 * (only used for fixed geometry)
3939 * @param aHeight The maximum height for the guest screen or zero for no change
3940 * (only used for fixed geometry)
3941 */
3942void VBoxConsoleView::setDesktopGeometry (DesktopGeo aGeo, int aWidth, int aHeight)
3943{
3944 LogFlowThisFunc (("aGeo=%s, aWidth=%d, aHeight=%d\n",
3945 (aGeo == DesktopGeo_Fixed ? "Fixed" :
3946 aGeo == DesktopGeo_Automatic ? "Automatic" :
3947 aGeo == DesktopGeo_Any ? "Any" : "Invalid"),
3948 aWidth, aHeight));
3949 switch (aGeo)
3950 {
3951 case DesktopGeo_Fixed:
3952 mDesktopGeo = DesktopGeo_Fixed;
3953 if (aWidth != 0 && aHeight != 0)
3954 mDesktopGeometry = QRect (0, 0, aWidth, aHeight);
3955 else
3956 mDesktopGeometry = QRect (0, 0, 0, 0);
3957 setDesktopGeoHint (0, 0);
3958 break;
3959 case DesktopGeo_Automatic:
3960 mDesktopGeo = DesktopGeo_Automatic;
3961 mDesktopGeometry = QRect (0, 0, 0, 0);
3962 setDesktopGeoHint (0, 0);
3963 break;
3964 case DesktopGeo_Any:
3965 mDesktopGeo = DesktopGeo_Any;
3966 mDesktopGeometry = QRect (0, 0, 0, 0);
3967 break;
3968 default:
3969 AssertMsgFailed(("Invalid desktop geometry type %d\n", aGeo));
3970 mDesktopGeo = DesktopGeo_Invalid;
3971 }
3972}
3973
3974
3975/**
3976 * If we are in automatic mode, the geometry restrictions will be recalculated.
3977 * This is needed in particular on the first widget resize, as we can't
3978 * calculate them correctly before that.
3979 *
3980 * @note a hint from the host will always override these restrictions.
3981 * @note we can't do calculations on the fly when they are needed, because
3982 * they require querying the X server on X11 hosts and this must be done
3983 * from within the GUI thread, due to the single threadedness of Xlib.
3984 */
3985void VBoxConsoleView::calculateDesktopGeometry()
3986{
3987 LogFlowThisFunc (("Entering\n"));
3988 /* This method should not get called until we have initially set up the */
3989 Assert ((mDesktopGeo != DesktopGeo_Invalid));
3990 /* If we are not doing automatic geometry calculation then there is
3991 * nothing to do. */
3992 if (DesktopGeo_Automatic == mDesktopGeo)
3993 {
3994 /* Available geometry of the desktop. If the desktop is a single
3995 * screen, this will exclude space taken up by desktop taskbars
3996 * and things, but this is unfortunately not true for the more
3997 * complex case of a desktop spanning multiple screens. */
3998 QRect desktop = QApplication::desktop()->availableGeometry (this);
3999 /* The area taken up by the console window on the desktop,
4000 * including window frame, title and menu bar and whatnot. */
4001 QRect frame = mMainWnd->frameGeometry();
4002 /* The area taken up by the console window, minus all
4003 * decorations. */
4004 QRect window = mMainWnd->centralWidget()->geometry();
4005 /* To work out how big we can make the console window while still
4006 * fitting on the desktop, we calculate desktop - frame + window.
4007 * This works because the difference between frame and window
4008 * (or at least its width and height) is a constant. */
4009 mDesktopGeometry =
4010 QRect (0, 0, desktop.width() - frame.width() + window.width(),
4011 desktop.height() - frame.height() + window.height());
4012 LogFlowThisFunc (("Setting %d, %d\n", mDesktopGeometry.width(),
4013 mDesktopGeometry.height()));
4014 }
4015}
4016
4017/**
4018 * Sets the minimum size restriction depending on the auto-resize feature
4019 * state and the current rendering mode.
4020 *
4021 * Currently, the restriction is set only in SDL mode and only when the
4022 * auto-resize feature is inactive. We need to do that because we cannot
4023 * correctly draw in a scrolled window in SDL mode.
4024 *
4025 * In all other modes, or when auto-resize is in force, this function does
4026 * nothing.
4027 */
4028void VBoxConsoleView::maybeRestrictMinimumSize()
4029{
4030 if (mode == VBoxDefs::SDLMode)
4031 {
4032 if (!mGuestSupportsGraphics || !mAutoresizeGuest)
4033 setMinimumSize (sizeHint());
4034 else
4035 setMinimumSize (0, 0);
4036 }
4037}
4038
4039int VBoxConsoleView::contentsWidth() const
4040{
4041 return mFrameBuf->width();
4042}
4043
4044int VBoxConsoleView::contentsHeight() const
4045{
4046 return mFrameBuf->height();
4047}
4048
4049void VBoxConsoleView::updateSliders()
4050{
4051 QSize p = viewport()->size();
4052 QSize m = maximumViewportSize();
4053
4054 QSize v = QSize (mFrameBuf->width(), mFrameBuf->height());
4055 /* no scroll bars needed */
4056 if (m.expandedTo(v) == m)
4057 p = m;
4058
4059 horizontalScrollBar()->setRange(0, v.width() - p.width());
4060 verticalScrollBar()->setRange(0, v.height() - p.height());
4061 horizontalScrollBar()->setPageStep(p.width());
4062 verticalScrollBar()->setPageStep(p.height());
4063}
4064
4065void VBoxConsoleView::requestToResize (const QSize &aSize)
4066{
4067 mIgnoreFrameBufferResize = true;
4068 mNormalSize = aSize;
4069}
4070
4071#if defined(Q_WS_MAC)
4072
4073void VBoxConsoleView::updateDockIcon()
4074{
4075 if (mDockIconEnabled)
4076 {
4077 if (!mPausedShot.isNull())
4078 {
4079 CGImageRef pauseImg = ::darwinToCGImageRef (&mPausedShot);
4080 /* Use the pause image as background */
4081 mDockIconPreview->updateDockPreview (pauseImg);
4082 CGImageRelease (pauseImg);
4083 }
4084 else
4085 {
4086# if defined (VBOX_GUI_USE_QUARTZ2D)
4087 if (mode == VBoxDefs::Quartz2DMode)
4088 {
4089 /* If the render mode is Quartz2D we could use the CGImageRef
4090 * of the framebuffer for the dock icon creation. This saves
4091 * some conversion time. */
4092 mDockIconPreview->updateDockPreview (static_cast <VBoxQuartz2DFrameBuffer *> (mFrameBuf)->imageRef());
4093 }
4094 else
4095# endif
4096 /* In image mode we have to create the image ref out of the
4097 * framebuffer */
4098 mDockIconPreview->updateDockPreview (mFrameBuf);
4099 }
4100 }
4101}
4102
4103void VBoxConsoleView::updateDockOverlay()
4104{
4105 /* Only to an update to the realtime preview if this is enabled by the user
4106 * & we are in an state where the framebuffer is likely valid. Otherwise to
4107 * the overlay stuff only. */
4108 if (mDockIconEnabled &&
4109 (mLastState == KMachineState_Running ||
4110 mLastState == KMachineState_Paused ||
4111 mLastState == KMachineState_Restoring ||
4112 mLastState == KMachineState_Saving))
4113 updateDockIcon();
4114 else
4115 mDockIconPreview->updateDockOverlay();
4116}
4117
4118/**
4119 * Wrapper for SetMouseCoalescingEnabled().
4120 *
4121 * Called by eventFilter() and darwinGrabKeyboardEvents().
4122 *
4123 * @param aOn Switch it on (true) or off (false).
4124 */
4125void VBoxConsoleView::setMouseCoalescingEnabled (bool aOn)
4126{
4127 /* Enable mouse event compression if we leave the VM view. This
4128 is necessary for having smooth resizing of the VM/other
4129 windows.
4130 Disable mouse event compression if we enter the VM view. So
4131 all mouse events are registered in the VM. Only do this if
4132 the keyboard/mouse is grabbed (this is when we have a valid
4133 event handler). */
4134 if (aOn || mKeyboardGrabbed)
4135 ::darwinSetMouseCoalescingEnabled (aOn);
4136}
4137
4138#endif /* Q_WS_MAC */
4139
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