VirtualBox

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

Last change on this file since 6441 was 6441, checked in by vboxsync, 17 years ago

Fixed wrong display in seamless mode if the toolbars (gnome-panel, windows taskbar, mac dock) of the host are not on the bottom of the screen but on the top/left/right or all together.

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

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