VirtualBox

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

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

FE/Qt: Commeted out debug code.

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