VirtualBox

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

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

2800 "FE/Qt: Switch to fullscreen does not work reliably" : Avoid waiting for 2 seconds then switching f/s mode in case of GA disabled.

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