VirtualBox

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

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

Fixing some issues in fullscreen/seamless modes switch:

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