VirtualBox

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

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

Hack to ignore Command-H and Command-Q when the keyboard is captured. (todo: proper fix / translate the events into key down/up.)

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