VirtualBox

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

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

Main: make VBox interfaces scriptable (that is, callable from Python and VisualBasic)

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