VirtualBox

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

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

fixed GDI handle leak

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

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