VirtualBox

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

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

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