VirtualBox

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

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

FE/Qt: adjust the guest resolution when the guest is started in fullscreen, in case the host resolution has changed

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