VirtualBox

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

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

FE/Qt: Applied temorary fix to avoud VM hangs when Auto Resize Guest is turned on and resizing is done using window borders instead of the size grip.

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

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