VirtualBox

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

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

FE/Qt-OSX: Make the handling of Darwin tasks more generic for Carbon & Cocoa. Disable settings window animation on Cocoa.

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