VirtualBox

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

Last change on this file since 27839 was 27839, checked in by vboxsync, 15 years ago

2d: working multi-monitor + some fixing for working with not-using-VRAM mode

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