VirtualBox

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

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

Video Hw Accel: debugging & better color support (still debugging & perf enhancements needed)

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette