VirtualBox

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

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

A quick hack to prevent getting the typing-while-paused on Host-Q.

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