VirtualBox

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

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

FE/Qt: OS/2: Let key events reach Qt when the keyboard is not captured (e.g. a Close dialog is shown); provide special hot keys (Host+`, Host+1, Host+2) for system combinations (Ctrl+Esc, Alt+Tab, Alt+Shift+Tab).

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

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