VirtualBox

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

Last change on this file since 1 was 1, checked in by vboxsync, 55 years ago

import

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