VirtualBox

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

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

Frontends: Use VBOX_E_DONT_CALL_AGAIN with IConsoleCallback.

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