VirtualBox

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

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

FE/Qt-OSX: Added cube only icon for the dock overlay on Mac OS X.

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