VirtualBox

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

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

FE/Qt, Devices/Input, Main, FE/*: upgrade mouse device to an MS IntelliMouse Explorer (five buttons and tilt wheel)

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