VirtualBox

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

Last change on this file since 14295 was 14295, checked in by vboxsync, 16 years ago

Corrected a couple of grammos.

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