VirtualBox

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

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

treat a repeate key as another keydown (dunno if this is correct, but linux is happy now.)

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

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