VirtualBox

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

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

Modified IKeyboard::PutScancodes to use SafeArray (including webservice)

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