VirtualBox

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

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

Frontends/VirtualBox: the saved guest resolution and initial size hint should take guest-initiated resolution changes into account as long as the additions are managing graphics in the guest

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