VirtualBox

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

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

X11 only

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

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