VirtualBox

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

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

The Big Sun Rebranding Header Change

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

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