VirtualBox

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

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

COM: missed pieces of scriptability support

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

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