VirtualBox

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

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

FE/Qt: deactivated r42720 and r42792 for now, as they caused regressions with Linux guests

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