VirtualBox

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

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

darwin: intercept the events before HI gets them. a bit of a hack because of Qt but otherwise it's the right thing to do.

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