VirtualBox

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

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

2223: Improve Visual Mode change:

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