VirtualBox

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

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

Network & USB ToolTip classes were removed from ConsoleWnd.
Call-back notifications are used for tool-tip updates reason now.

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