VirtualBox

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

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

Made it compile on solaris.

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