VirtualBox

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

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

Added the display index parameter to the SetVideoModeHint (in the guest all hints are still processed only for the primary monitor).

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