VirtualBox

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

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

video 2d accel: move overlay functionality to a separate class/files, make it easy to reuse with different framebuffers

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