VirtualBox

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

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

2079: Create seamless mode:

The new seamless region will be updated only if it differs from the previous.

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