VirtualBox

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

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

Main:

  • Prototyped IConsoleCallback::onRuntimeError();
  • All size parameters in IHardDisk are now ULONG64.

Frontends:

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