VirtualBox

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

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

Frontends/VirtualBox: better handle VideoModeSupported requests - accept sizes less that 100 pixels below the screen size or sizes less than the last hint we sent, whichever is greater

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

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