VirtualBox

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

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

Darwin cursors (didn't get inverted cursors working right, so doing the same as X11).

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