VirtualBox

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

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

qt/darwin: Don't use HID for right hand modifier keys when Qt is Cocoa based. The HID stuff is *very* slow.

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

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