VirtualBox

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

Last change on this file since 2764 was 2764, checked in by vboxsync, 18 years ago

FE/Qt: Fixed spelling; added proper console window restriction for SDL mode when no guest additions are active or when the auto-resize feature is turned off.

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

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