VirtualBox

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

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

FE/Qt: Rolled back changeset:20385 because the resize deadlocks have been eliminated now.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 87.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.215389.xyz. 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 ignore_mainwnd_resize = false;
827
828 return true;
829 }
830
831#ifdef Q_WS_MAC /* see VBoxQImageFrameBuffer::NotifyUpdate. */
832 case VBoxDefs::RepaintEventType:
833 {
834 VBoxRepaintEvent *re = (VBoxRepaintEvent *) e;
835 viewport()->repaint (re->x(), re->y(), re->width(), re->height(), false);
836 /*cconsole.GetDisplay().UpdateCompleted(); - the event was acked already */
837 return true;
838 }
839#endif /* Q_WS_MAC */
840
841 case VBoxDefs::MousePointerChangeEventType:
842 {
843 MousePointerChangeEvent *me = (MousePointerChangeEvent *) e;
844 /* change cursor shape only when mouse integration is
845 * supported (change mouse shape type event may arrive after
846 * mouse capability change that disables integration */
847 if (mouse_absolute)
848 setPointerShape (me);
849 return true;
850 }
851 case VBoxDefs::MouseCapabilityEventType:
852 {
853 MouseCapabilityEvent *me = (MouseCapabilityEvent *) e;
854 if (mouse_absolute != me->supportsAbsolute())
855 {
856 mouse_absolute = me->supportsAbsolute();
857 /* correct the mouse capture state and reset the cursor
858 * to the default shape if necessary */
859 if (mouse_absolute)
860 captureMouse (false, false);
861 else
862 viewport()->unsetCursor();
863 emitMouseStateChanged();
864 vboxProblem().remindAboutMouseIntegration (mouse_absolute);
865 }
866 return true;
867 }
868
869 case VBoxDefs::ModifierKeyChangeEventType:
870 {
871 ModifierKeyChangeEvent *me = (ModifierKeyChangeEvent* )e;
872 if (me->numLock() != mfNumLock)
873 muNumLockAdaptionCnt = 2;
874 if (me->capsLock() != mfCapsLock)
875 muCapsLockAdaptionCnt = 2;
876 mfNumLock = me->numLock();
877 mfCapsLock = me->capsLock();
878 mfScrollLock = me->scrollLock();
879 return true;
880 }
881
882 case VBoxDefs::MachineStateChangeEventType:
883 {
884 StateChangeEvent *me = (StateChangeEvent *) e;
885 LogFlowFunc (("MachineStateChangeEventType: state=%d\n",
886 me->machineState()));
887 onStateChange (me->machineState());
888 emit machineStateChanged (me->machineState());
889 return true;
890 }
891
892 case VBoxDefs::ActivateMenuEventType:
893 {
894 ActivateMenuEvent *ame = (ActivateMenuEvent *) e;
895 ame->menuData()->activateItemAt (ame->index());
896
897 /*
898 * The main window and its children can be destroyed at this
899 * point (if, for example, the activated menu item closes the
900 * main window). Detect this situation to prevent calls to
901 * destroyed widgets.
902 */
903 QWidgetList *list = QApplication::topLevelWidgets ();
904 bool destroyed = list->find (mainwnd) < 0;
905 delete list;
906 if (!destroyed && mainwnd->statusBar())
907 mainwnd->statusBar()->clear();
908
909 return true;
910 }
911
912 case VBoxDefs::RuntimeErrorEventType:
913 {
914 RuntimeErrorEvent *ee = (RuntimeErrorEvent *) e;
915 vboxProblem().showRuntimeError (cconsole, ee->fatal(),
916 ee->errorID(), ee->message());
917 return true;
918 }
919
920 case QEvent::KeyPress:
921 {
922 QKeyEvent *ke = (QKeyEvent *) e;
923 if (hostkey_pressed)
924 {
925 if (ke->key() >= Key_F1 && ke->key() <= Key_F12)
926 {
927 LONG combo [6];
928 combo [0] = 0x1d; /* Ctrl down */
929 combo [1] = 0x38; /* Alt down */
930 combo [4] = 0xb8; /* Alt up */
931 combo [5] = 0x9d; /* Ctrl up */
932 if (ke->key() >= Key_F1 && ke->key() <= Key_F10)
933 {
934 combo [2] = 0x3b + (ke->key() - Key_F1); /* F1-F10 down */
935 combo [3] = 0xbb + (ke->key() - Key_F1); /* F1-F10 up */
936 }
937 /* some scan slice */
938 else if (ke->key() >= Key_F11 && ke->key() <= Key_F12)
939 {
940 combo [2] = 0x57 + (ke->key() - Key_F11); /* F11-F12 down */
941 combo [3] = 0xd7 + (ke->key() - Key_F11); /* F11-F12 up */
942 }
943 else
944 Assert (0);
945
946 CKeyboard keyboard = cconsole.GetKeyboard();
947 Assert (!keyboard.isNull());
948 keyboard.PutScancodes (combo, 6);
949 }
950 else if (ke->key() == Key_Home)
951 {
952 mainwnd->menuBar()->setFocus();
953 }
954 else
955 {
956 /*
957 * process hot keys not processed in keyEvent()
958 * (as in case of non-alphanumeric keys)
959 */
960 processHotKey (QKeySequence (ke->key()),
961 mainwnd->menuBar());
962 }
963 }
964 else
965 {
966 if (isPaused())
967 {
968 /*
969 * if the reminder is disabled we pass the event to
970 * Qt to enable normal keyboard functionality
971 * (for example, menu access with Alt+Letter)
972 */
973 if (!vboxProblem().remindAboutPausedVMInput())
974 break;
975 }
976 }
977 ke->accept();
978 return true;
979 }
980
981 default:
982 break;
983 }
984 }
985
986 return QScrollView::event (e);
987}
988
989bool VBoxConsoleView::eventFilter (QObject *watched, QEvent *e)
990{
991 if (attached && watched == viewport())
992 {
993 switch (e->type())
994 {
995 case QEvent::MouseMove:
996 case QEvent::MouseButtonPress:
997 case QEvent::MouseButtonDblClick:
998 case QEvent::MouseButtonRelease:
999 {
1000 QMouseEvent *me = (QMouseEvent *) e;
1001 if (mouseEvent (me->type(), me->pos(), me->globalPos(),
1002 me->button(), me->state(), me->stateAfter(),
1003 0, Horizontal))
1004 return true; /* stop further event handling */
1005 break;
1006 }
1007 case QEvent::Wheel:
1008 {
1009 QWheelEvent *we = (QWheelEvent *) e;
1010 if (mouseEvent (we->type(), we->pos(), we->globalPos(),
1011 NoButton, we->state(), we->state(),
1012 we->delta(), we->orientation()))
1013 return true; /* stop further event handling */
1014 break;
1015 }
1016 case QEvent::Resize:
1017 {
1018 if (mouse_captured)
1019 updateMouseClipping();
1020 }
1021 default:
1022 break;
1023 }
1024 }
1025 else if (watched == mainwnd)
1026 {
1027 switch (e->type())
1028 {
1029#if defined (Q_WS_WIN32)
1030#if defined (VBOX_GUI_USE_DDRAW)
1031 case QEvent::Move:
1032 {
1033 /*
1034 * notification from our parent that it has moved. We need this
1035 * in order to possibly adjust the direct screen blitting.
1036 */
1037 if (fb)
1038 fb->moveEvent( (QMoveEvent *) e );
1039 break;
1040 }
1041#endif
1042 /*
1043 * install/uninstall low-level kbd hook on every
1044 * activation/deactivation to:
1045 * a) avoid excess hook calls when we're not active and
1046 * b) be always in front of any other possible hooks
1047 */
1048 case QEvent::WindowActivate:
1049 {
1050 g_kbdhook = SetWindowsHookEx (WH_KEYBOARD_LL, lowLevelKeyboardProc,
1051 GetModuleHandle (NULL), 0);
1052 AssertMsg (g_kbdhook, ("SetWindowsHookEx(): err=%d", GetLastError()));
1053 break;
1054 }
1055 case QEvent::WindowDeactivate:
1056 {
1057 if (g_kbdhook)
1058 {
1059 UnhookWindowsHookEx (g_kbdhook);
1060 g_kbdhook = NULL;
1061 }
1062 break;
1063 }
1064#endif /* defined (Q_WS_WIN32) */
1065#if defined (Q_WS_MAC)
1066 /*
1067 * Install/remove the keyboard event handler.
1068 */
1069 case QEvent::WindowActivate:
1070 darwinGrabKeyboardEvents (true);
1071 break;
1072 case QEvent::WindowDeactivate:
1073 darwinGrabKeyboardEvents (false);
1074 break;
1075#endif /* defined (Q_WS_MAC) */
1076 case QEvent::Resize:
1077 {
1078 if (!ignore_mainwnd_resize)
1079 {
1080 if (autoresize_guest)
1081 resize_hint_timer->start (300, TRUE);
1082 }
1083 break;
1084 }
1085 case QEvent::WindowStateChange:
1086 {
1087 if (!mainwnd->isMinimized())
1088 {
1089 if (!mainwnd->isMaximized())
1090 {
1091 /* The guest screen size (and therefore the contents
1092 * size) could have been changed while we were maximized
1093 * or minimized, so normalize the main window size
1094 * unless we're in true fullscreen mode. Calling
1095 * normalizeGeometry() directly from here doesn't work
1096 * for some reason, so use a single shot timer. */
1097 if (!mainwnd->isTrueFullscreen())
1098 QTimer::singleShot (0, this, SLOT (normalizeGeo()));
1099 }
1100 }
1101 }
1102
1103 default:
1104 break;
1105 }
1106 }
1107 else if (watched == mainwnd->menuBar())
1108 {
1109 /*
1110 * sometimes when we press ESC in the menu it brings the
1111 * focus away (Qt bug?) causing no widget to have a focus,
1112 * or holds the focus itself, instead of returning the focus
1113 * to the console window. here we fix this.
1114 */
1115 switch (e->type())
1116 {
1117 case QEvent::FocusOut:
1118 {
1119 if (qApp->focusWidget() == 0)
1120 setFocus();
1121 break;
1122 }
1123 case QEvent::KeyPress:
1124 {
1125 QKeyEvent *ke = (QKeyEvent *) e;
1126 if (ke->key() == Key_Escape && !(ke->state() & KeyButtonMask))
1127 if (mainwnd->menuBar()->hasFocus())
1128 setFocus();
1129 break;
1130 }
1131 default:
1132 break;
1133 }
1134 }
1135
1136 return QScrollView::eventFilter (watched, e);
1137}
1138
1139#if defined(Q_WS_WIN32)
1140
1141/**
1142 * Low-level keyboard event handler,
1143 * @return
1144 * true to indicate that the message is processed and false otherwise
1145 */
1146bool VBoxConsoleView::winLowKeyboardEvent (UINT msg, const KBDLLHOOKSTRUCT &event)
1147{
1148#if 0
1149 LogFlow (("### vkCode=%08X, scanCode=%08X, flags=%08X, dwExtraInfo=%08X (kbd_captured=%d)\n",
1150 event.vkCode, event.scanCode, event.flags, event.dwExtraInfo, kbd_captured));
1151 char buf [256];
1152 sprintf (buf, "### vkCode=%08X, scanCode=%08X, flags=%08X, dwExtraInfo=%08X",
1153 event.vkCode, event.scanCode, event.flags, event.dwExtraInfo);
1154 mainwnd->statusBar()->message (buf);
1155#endif
1156
1157 /* Sometimes it happens that Win inserts additional events on some key
1158 * press/release. For example, it prepends ALT_GR in German layout with
1159 * the VK_LCONTROL vkey with curious 0x21D scan code (seems to be necessary
1160 * to specially treat ALT_GR to enter additional chars to regular apps).
1161 * These events are definitely unwanted in VM, so filter them out. */
1162 if (hasFocus() && (event.scanCode & ~0xFF))
1163 return true;
1164
1165 if (!kbd_captured)
1166 return false;
1167
1168 /* it's possible that a key has been pressed while the keyboard was not
1169 * captured, but is being released under the capture. Detect this situation
1170 * and return false to let Windows process the message normally and update
1171 * its key state table (to avoid the stuck key effect). */
1172 uint8_t what_pressed = (event.flags & 0x01) && (event.vkCode != VK_RSHIFT)
1173 ? IsExtKeyPressed
1174 : IsKeyPressed;
1175 if ((event.flags & 0x80) /* released */ &&
1176 ((event.vkCode == gs.hostKey() && !hostkey_in_capture) ||
1177 (keys_pressed [event.scanCode] & (IsKbdCaptured | what_pressed)) == what_pressed))
1178 return false;
1179
1180 MSG message;
1181 message.hwnd = winId();
1182 message.message = msg;
1183 message.wParam = event.vkCode;
1184 message.lParam =
1185 1 |
1186 (event.scanCode & 0xFF) << 16 |
1187 (event.flags & 0xFF) << 24;
1188
1189 /* Windows sets here the extended bit when the Right Shift key is pressed,
1190 * which is totally wrong. Undo it. */
1191 if (event.vkCode == VK_RSHIFT)
1192 message.lParam &= ~0x1000000;
1193
1194 /* we suppose here that this hook is always called on the main GUI thread */
1195 return winEvent (&message);
1196}
1197
1198/**
1199 * Get Win32 messages before they are passed to Qt. This allows us to get
1200 * the keyboard events directly and bypass the harmful Qt translation. A
1201 * return value of TRUE indicates to Qt that the event has been handled.
1202 */
1203bool VBoxConsoleView::winEvent (MSG *msg)
1204{
1205 if (!attached || ! (
1206 msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN ||
1207 msg->message == WM_KEYUP || msg->message == WM_SYSKEYUP
1208 ))
1209 return false;
1210
1211 /* check for the special flag possibly set at the end of this function */
1212 if ((msg->lParam >> 25) & 0x1)
1213 return false;
1214
1215#if 0
1216 LogFlow (("*** WM_%04X: vk=%04X rep=%05d scan=%02X ext=%01d rzv=%01X ctx=%01d prev=%01d tran=%01d",
1217 msg->message, msg->wParam,
1218 (msg->lParam & 0xFFFF),
1219 ((msg->lParam >> 16) & 0xFF),
1220 ((msg->lParam >> 24) & 0x1),
1221 ((msg->lParam >> 25) & 0xF),
1222 ((msg->lParam >> 29) & 0x1),
1223 ((msg->lParam >> 30) & 0x1),
1224 ((msg->lParam >> 31) & 0x1)));
1225 char buf [256];
1226 sprintf (buf, "WM_%04X: vk=%04X rep=%05d scan=%02X ext=%01d rzv=%01X ctx=%01d prev=%01d tran=%01d",
1227 msg->message, msg->wParam,
1228 (msg->lParam & 0xFFFF),
1229 ((msg->lParam >> 16) & 0xFF),
1230 ((msg->lParam >> 24) & 0x1),
1231 ((msg->lParam >> 25) & 0xF),
1232 ((msg->lParam >> 29) & 0x1),
1233 ((msg->lParam >> 30) & 0x1),
1234 ((msg->lParam >> 31) & 0x1));
1235 mainwnd->statusBar()->message (buf);
1236#endif
1237
1238 int scan = (msg->lParam >> 16) & 0x7F;
1239 /* scancodes 0x80 and 0x00 are ignored */
1240 if (!scan)
1241 return true;
1242
1243 int vkey = msg->wParam;
1244
1245 /* When one of the SHIFT keys is held and one of the cursor movement
1246 * keys is pressed, Windows duplicates SHIFT press/release messages,
1247 * but with the virtual key code set to 0xFF. These virtual keys are also
1248 * sent in some other situations (Pause, PrtScn, etc.). Ignore such
1249 * messages. */
1250 if (vkey == 0xFF)
1251 return true;
1252
1253 int flags = 0;
1254 if (msg->lParam & 0x1000000)
1255 flags |= KeyExtended;
1256 if (!(msg->lParam & 0x80000000))
1257 flags |= KeyPressed;
1258
1259 switch (vkey)
1260 {
1261 case VK_SHIFT:
1262 case VK_CONTROL:
1263 case VK_MENU:
1264 {
1265 /* overcome stupid Win32 modifier key generalization */
1266 int keyscan = scan;
1267 if (flags & KeyExtended)
1268 keyscan |= 0xE000;
1269 switch (keyscan)
1270 {
1271 case 0x002A: vkey = VK_LSHIFT; break;
1272 case 0x0036: vkey = VK_RSHIFT; break;
1273 case 0x001D: vkey = VK_LCONTROL; break;
1274 case 0xE01D: vkey = VK_RCONTROL; break;
1275 case 0x0038: vkey = VK_LMENU; break;
1276 case 0xE038: vkey = VK_RMENU; break;
1277 }
1278 break;
1279 }
1280 case VK_NUMLOCK:
1281 /* Win32 sets the extended bit for the NumLock key. Reset it. */
1282 flags &= ~KeyExtended;
1283 break;
1284 case VK_SNAPSHOT:
1285 flags |= KeyPrint;
1286 break;
1287 case VK_PAUSE:
1288 flags |= KeyPause;
1289 break;
1290 }
1291
1292 bool result = keyEvent (vkey, scan, flags);
1293 if (!result && kbd_captured)
1294 {
1295 /* keyEvent() returned that it didn't process the message, but since the
1296 * keyboard is captured, we don't want to pass it to Windows. We just want
1297 * to let Qt process the message (to handle non-alphanumeric <HOST>+key
1298 * shortcuts for example). So send it direcltly to the window with the
1299 * special flag in the reserved area of lParam (to avoid recursion). */
1300 ::SendMessage (msg->hwnd, msg->message,
1301 msg->wParam, msg->lParam | (0x1 << 25));
1302 return true;
1303 }
1304 return result;
1305}
1306
1307#elif defined(Q_WS_X11)
1308
1309/**
1310 * This routine gets X11 events before they are processed by Qt. This is
1311 * used for our platform specific keyboard implementation. A return value
1312 * of TRUE indicates that the event has been processed by us.
1313 */
1314bool VBoxConsoleView::x11Event (XEvent *event)
1315{
1316 static WINEKEYBOARDINFO wineKeyboardInfo;
1317
1318 switch (event->type)
1319 {
1320 case XKeyPress:
1321 case XKeyRelease:
1322 if (attached)
1323 break;
1324 /* else fall through */
1325 /// @todo (AH) later, we might want to handle these as well
1326 case KeymapNotify:
1327 case MappingNotify:
1328 default:
1329 return false; /* pass the event to Qt */
1330 }
1331
1332 /* perform the mega-complex translation using the wine algorithms */
1333 handleXKeyEvent (this->x11Display(), event, &wineKeyboardInfo);
1334
1335#if 0
1336 char buf [256];
1337 sprintf (buf, "pr=%d kc=%08X st=%08X fl=%08lX scan=%04X",
1338 event->type == XKeyPress ? 1 : 0, event->xkey.keycode,
1339 event->xkey.state, wineKeyboardInfo.dwFlags, wineKeyboardInfo.wScan);
1340 mainwnd->statusBar()->message (buf);
1341 LogFlow (("### %s\n", buf));
1342#endif
1343
1344 int scan = wineKeyboardInfo.wScan & 0x7F;
1345 // scancodes 0x00 (no valid translation) and 0x80 are ignored
1346 if (!scan)
1347 return true;
1348
1349 KeySym ks = ::XKeycodeToKeysym (event->xkey.display, event->xkey.keycode, 0);
1350
1351 int flags = 0;
1352 if (wineKeyboardInfo.dwFlags & 0x0001)
1353 flags |= KeyExtended;
1354 if (event->type == XKeyPress)
1355 flags |= KeyPressed;
1356
1357 switch (ks)
1358 {
1359 case XK_Num_Lock:
1360 // Wine sets the extended bit for the NumLock key. Reset it.
1361 flags &= ~KeyExtended;
1362 break;
1363 case XK_Print:
1364 flags |= KeyPrint;
1365 break;
1366 case XK_Pause:
1367 flags |= KeyPause;
1368 break;
1369 }
1370
1371 return keyEvent (ks, scan, flags);
1372}
1373
1374#elif defined (Q_WS_MAC)
1375
1376/**
1377 * Invoked by VBoxConsoleView::darwinEventHandlerProc / VBoxConsoleView::macEventFilter when
1378 * it receives a raw keyboard event.
1379 *
1380 * @param inEvent The keyboard event.
1381 *
1382 * @return true if the key was processed, false if it wasn't processed and should be passed on.
1383 */
1384bool VBoxConsoleView::darwinKeyboardEvent (EventRef inEvent)
1385{
1386 bool ret = false;
1387 UInt32 EventKind = ::GetEventKind (inEvent);
1388 if (EventKind != kEventRawKeyModifiersChanged)
1389 {
1390 /* convert keycode to set 1 scan code. */
1391 UInt32 keyCode = ~0U;
1392 ::GetEventParameter (inEvent, kEventParamKeyCode, typeUInt32, NULL, sizeof(keyCode), NULL, &keyCode);
1393 unsigned scanCode = ::DarwinKeycodeToSet1Scancode (keyCode);
1394 if (scanCode)
1395 {
1396 /* calc flags. */
1397 int flags = 0;
1398 UInt32 EventKind = ::GetEventKind (inEvent);
1399 if (EventKind != kEventRawKeyUp)
1400 flags |= KeyPressed;
1401 if (scanCode & 0x8000) /* modifiers (just in case) */
1402 {
1403 flags |= KeyPressed;
1404 scanCode &= ~0x8000;
1405 }
1406 if (scanCode & 0x80)
1407 {
1408 flags |= KeyExtended;
1409 scanCode &= ~0x80;
1410 }
1411 /** @todo KeyPause, KeyPrint. */
1412
1413 /* get the keycode and unicode string (if present). */
1414 UInt32 keyCode = ~0;
1415 ::GetEventParameter (inEvent, kEventParamKeyCode, typeUInt32, NULL,
1416 sizeof (keyCode), NULL, &keyCode);
1417 AssertCompileSize(wchar_t, 2);
1418 AssertCompileSize(UniChar, 2);
1419 wchar_t ucs[32];
1420 if (::GetEventParameter (inEvent, kEventParamKeyUnicodes, typeUnicodeText, NULL,
1421 sizeof (ucs), NULL, &ucs[0]) != 0)
1422 ucs[0] = 0;
1423
1424 ret = keyEvent (keyCode, scanCode, flags, ucs[0] ? ucs : NULL);
1425 }
1426 }
1427 else
1428 {
1429 /* May contain multiple modifier changes, kind of annoying. */
1430 UInt32 newMask = 0;
1431 ::GetEventParameter (inEvent, kEventParamKeyModifiers, typeUInt32, NULL,
1432 sizeof (newMask), NULL, &newMask);
1433 newMask = ::DarwinAdjustModifierMask (newMask);
1434 UInt32 changed = newMask ^ m_darwinKeyModifiers;
1435 ret = kbd_captured;
1436 if (changed)
1437 {
1438 for (UInt32 bit = 0; bit < 32; bit++)
1439 {
1440 if (!(changed & (1 << bit)))
1441 continue;
1442 unsigned scanCode = ::DarwinModifierMaskToSet1Scancode (1 << bit);
1443 if (!scanCode)
1444 continue;
1445 unsigned keyCode = ::DarwinModifierMaskToDarwinKeycode (1 << bit);
1446 Assert (keyCode);
1447
1448 unsigned flags = (newMask & (1 << bit)) ? KeyPressed : 0;
1449 if (scanCode & 0x80)
1450 {
1451 flags |= KeyExtended;
1452 scanCode &= ~0x80;
1453 }
1454 ret |= keyEvent (keyCode, scanCode, flags);
1455 }
1456 }
1457
1458 m_darwinKeyModifiers = newMask;
1459 }
1460
1461 return ret;
1462}
1463
1464
1465/**
1466 * Installs or removes the keyboard event handler.
1467 *
1468 * @param fGrab True if we're to grab the events, false if we're not to.
1469 */
1470void VBoxConsoleView::darwinGrabKeyboardEvents (bool fGrab)
1471{
1472 if (fGrab)
1473 {
1474 ::SetMouseCoalescingEnabled (false, NULL); //??
1475 ::CGSetLocalEventsSuppressionInterval (0.0); //??
1476
1477#ifndef VBOX_WITH_HACKED_QT
1478
1479 EventTypeSpec eventTypes[6];
1480 eventTypes[0].eventClass = kEventClassKeyboard;
1481 eventTypes[0].eventKind = kEventRawKeyDown;
1482 eventTypes[1].eventClass = kEventClassKeyboard;
1483 eventTypes[1].eventKind = kEventRawKeyUp;
1484 eventTypes[2].eventClass = kEventClassKeyboard;
1485 eventTypes[2].eventKind = kEventRawKeyRepeat;
1486 eventTypes[3].eventClass = kEventClassKeyboard;
1487 eventTypes[3].eventKind = kEventRawKeyModifiersChanged;
1488 /* For ignorning Command-H and Command-Q which aren't affected by the
1489 * global hotkey stuff (doesn't work well): */
1490 eventTypes[4].eventClass = kEventClassCommand;
1491 eventTypes[4].eventKind = kEventCommandProcess;
1492 eventTypes[5].eventClass = kEventClassCommand;
1493 eventTypes[5].eventKind = kEventCommandUpdateStatus;
1494
1495 EventHandlerUPP eventHandler = ::NewEventHandlerUPP (VBoxConsoleView::darwinEventHandlerProc);
1496
1497 m_darwinEventHandlerRef = NULL;
1498 ::InstallApplicationEventHandler (eventHandler, RT_ELEMENTS (eventTypes), &eventTypes[0],
1499 this, &m_darwinEventHandlerRef);
1500 ::DisposeEventHandlerUPP (eventHandler);
1501
1502#else /* VBOX_WITH_HACKED_QT */
1503 ((QIApplication *)qApp)->setEventFilter (VBoxConsoleView::macEventFilter, this);
1504#endif /* VBOX_WITH_HACKED_QT */
1505
1506 ::DarwinGrabKeyboard (false);
1507 }
1508 else
1509 {
1510 ::DarwinReleaseKeyboard ();
1511#ifndef VBOX_WITH_HACKED_QT
1512 if (m_darwinEventHandlerRef)
1513 {
1514 ::RemoveEventHandler (m_darwinEventHandlerRef);
1515 m_darwinEventHandlerRef = NULL;
1516 }
1517#else
1518 ((QIApplication *)qApp)->setEventFilter (NULL, NULL);
1519#endif
1520 }
1521}
1522
1523#endif
1524
1525//
1526// Private members
1527/////////////////////////////////////////////////////////////////////////////
1528
1529/**
1530 * Called on every focus change
1531 * and also to forcibly capture/uncapture the input in situations similar
1532 * to gaining or losing focus.
1533 *
1534 * @focus true if the window got focus and false otherwise
1535 */
1536void VBoxConsoleView::focusEvent (bool focus)
1537{
1538 if (focus)
1539 {
1540#ifdef __WIN__
1541 if ( gs.autoCapture()
1542 && GetAncestor (winId(), GA_ROOT) == GetForegroundWindow())
1543#else
1544 if (gs.autoCapture())
1545#endif /* __WIN__ */
1546 {
1547 captureKbd (true);
1548/// @todo (dmik)
1549// the below is for the mouse auto-capture. disabled for now. in order to
1550// properly support it, we need to know when *all* mouse buttons are
1551// released after we got focus, and grab the mouse only after then.
1552// btw, the similar would be good the for keyboard auto-capture, too.
1553// if (!(mouse_absolute && mouse_integration))
1554// captureMouse (true);
1555 }
1556 }
1557 else
1558 {
1559 captureMouse (false);
1560 captureKbd (false, false);
1561 releaseAllKeysPressed (!hasFocus());
1562 }
1563}
1564
1565/**
1566 * Synchronize the views of the host and the guest to the modifier keys.
1567 * This function will add up to 6 additional keycodes to codes.
1568 *
1569 * @param codes pointer to keycodes which are sent to the keyboard
1570 * @param count pointer to the keycodes counter
1571 */
1572void VBoxConsoleView::fixModifierState(LONG *codes, uint *count)
1573{
1574#if defined(Q_WS_X11)
1575
1576 Window wDummy1, wDummy2;
1577 int iDummy3, iDummy4, iDummy5, iDummy6;
1578 unsigned uMask;
1579 unsigned uKeyMaskNum = 0, uKeyMaskCaps = 0, uKeyMaskScroll = 0;
1580
1581 uKeyMaskCaps = LockMask;
1582 XModifierKeymap* map = XGetModifierMapping(qt_xdisplay());
1583 KeyCode keyCodeNum = XKeysymToKeycode(qt_xdisplay(), XK_Num_Lock);
1584 KeyCode keyCodeScroll = XKeysymToKeycode(qt_xdisplay(), XK_Scroll_Lock);
1585
1586 for (int i = 0; i < 8; i++)
1587 {
1588 if ( keyCodeNum != NoSymbol
1589 && map->modifiermap[map->max_keypermod * i] == keyCodeNum)
1590 uKeyMaskNum = 1 << i;
1591 else if ( keyCodeScroll != NoSymbol
1592 && map->modifiermap[map->max_keypermod * i] == keyCodeScroll)
1593 uKeyMaskScroll = 1 << i;
1594 }
1595 XQueryPointer(qt_xdisplay(), DefaultRootWindow(qt_xdisplay()), &wDummy1, &wDummy2,
1596 &iDummy3, &iDummy4, &iDummy5, &iDummy6, &uMask);
1597 XFreeModifiermap(map);
1598
1599 if (muNumLockAdaptionCnt && (mfNumLock ^ !!(uMask & uKeyMaskNum)))
1600 {
1601 muNumLockAdaptionCnt--;
1602 codes[(*count)++] = 0x45;
1603 codes[(*count)++] = 0x45 | 0x80;
1604 }
1605 if (muCapsLockAdaptionCnt && (mfCapsLock ^ !!(uMask & uKeyMaskCaps)))
1606 {
1607 muCapsLockAdaptionCnt--;
1608 codes[(*count)++] = 0x3a;
1609 codes[(*count)++] = 0x3a | 0x80;
1610 }
1611
1612#elif defined(Q_WS_WIN32)
1613
1614 if (muNumLockAdaptionCnt && (mfNumLock ^ !!(GetKeyState(VK_NUMLOCK))))
1615 {
1616 muNumLockAdaptionCnt--;
1617 codes[(*count)++] = 0x45;
1618 codes[(*count)++] = 0x45 | 0x80;
1619 }
1620 if (muCapsLockAdaptionCnt && (mfCapsLock ^ !!(GetKeyState(VK_CAPITAL))))
1621 {
1622 muCapsLockAdaptionCnt--;
1623 codes[(*count)++] = 0x3a;
1624 codes[(*count)++] = 0x3a | 0x80;
1625 }
1626
1627#elif defined(Q_WS_MAC)
1628
1629 if (muNumLockAdaptionCnt)
1630 {
1631 /* Numlock isn't in the modifier mask. */
1632 KeyMap keys = {{0},{0},{0},{0}};
1633 GetKeys(keys);
1634 if (mfNumLock ^ ASMBitTest(keys, 0x47 /* QZ_NUMLOCK */))
1635 {
1636 muNumLockAdaptionCnt--;
1637 codes[(*count)++] = 0x45;
1638 codes[(*count)++] = 0x45 | 0x80;
1639 }
1640 }
1641 if (muCapsLockAdaptionCnt && (mfCapsLock ^ !!(::GetCurrentEventKeyModifiers() & alphaLock)))
1642 {
1643 muCapsLockAdaptionCnt--;
1644 codes[(*count)++] = 0x3a;
1645 codes[(*count)++] = 0x3a | 0x80;
1646 }
1647
1648#else
1649
1650#warning Adapt VBoxConsoleView::fixModifierState
1651
1652#endif
1653
1654
1655}
1656
1657/**
1658 * Called on every key press and release (while in focus).
1659 *
1660 * @key virtual scan code (virtual key on Win32 and KeySym on X11)
1661 * @scan hardware scan code
1662 * @flags flags, a combination of Key* constants
1663 * @aUniKey Unicode translation of the key. Optional.
1664 *
1665 * @return true to consume the event and false to pass it to Qt
1666 */
1667bool VBoxConsoleView::keyEvent (int key, uint8_t scan, int flags, wchar_t *aUniKey/* = NULL*/)
1668{
1669// char bbbuf[256];
1670// sprintf (bbbuf,
1671// "key=%08X scan=%02X flags=%08X",
1672// key, scan, flags);
1673// ((QMainWindow*)mainwnd)->statusBar()->message (bbbuf);
1674
1675 const bool is_hostkey = key == gs.hostKey();
1676
1677 LONG buf [16];
1678 LONG *codes = buf;
1679 uint count = 0;
1680
1681 if (!is_hostkey)
1682 {
1683 if (flags & KeyPrint)
1684 {
1685 static LONG PrintMake[] = { 0xE0, 0x2A, 0xE0, 0x37 };
1686 static LONG PrintBreak[] = { 0xE0, 0xB7, 0xE0, 0xAA };
1687 if (flags & KeyPressed)
1688 {
1689 codes = PrintMake;
1690 count = SIZEOF_ARRAY (PrintMake);
1691 }
1692 else
1693 {
1694 codes = PrintBreak;
1695 count = SIZEOF_ARRAY (PrintBreak);
1696 }
1697 }
1698 else if ((flags & (KeyPause | KeyPressed)) == (KeyPause | KeyPressed))
1699 {
1700 static LONG Pause[] = { 0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5 };
1701 codes = Pause;
1702 count = SIZEOF_ARRAY (Pause);
1703 }
1704 else
1705 {
1706 if (flags & KeyPressed)
1707 {
1708 // Check if the guest has the same view on the modifier keys (NumLock,
1709 // CapsLock, ScrollLock) as the X server. If not, send KeyPress events
1710 // to synchronize the state.
1711 fixModifierState (codes, &count);
1712 }
1713
1714 // process the scancode and update the table of pressed keys
1715 uint8_t what_pressed = IsKeyPressed;
1716
1717 if (flags & KeyExtended)
1718 {
1719 codes [count++] = 0xE0;
1720 what_pressed = IsExtKeyPressed;
1721 }
1722
1723 if (flags & KeyPressed)
1724 {
1725 codes [count++] = scan;
1726 keys_pressed [scan] |= what_pressed;
1727 }
1728 else
1729 {
1730 // if we haven't got this key's press message, we ignore its release
1731 if (!(keys_pressed [scan] & what_pressed))
1732 return true;
1733 codes [count++] = scan | 0x80;
1734 keys_pressed [scan] &= ~what_pressed;
1735 }
1736
1737 if (kbd_captured)
1738 keys_pressed [scan] |= IsKbdCaptured;
1739 else
1740 keys_pressed [scan] &= ~IsKbdCaptured;
1741 }
1742 }
1743 else
1744 {
1745 // currently this is used in winLowKeyboardEvent() only
1746 hostkey_in_capture = kbd_captured;
1747 }
1748
1749 bool emit_signal = false;
1750 int hotkey = 0;
1751
1752 // process the host key
1753 if (flags & KeyPressed)
1754 {
1755 if (is_hostkey)
1756 {
1757 if (!hostkey_pressed)
1758 {
1759 hostkey_pressed = hostkey_alone = true;
1760 if (isRunning())
1761 saveKeyStates();
1762 emit_signal = true;
1763 }
1764 }
1765 else
1766 {
1767 if (hostkey_pressed)
1768 {
1769 if (hostkey_alone)
1770 {
1771 hotkey = key;
1772 hostkey_alone = false;
1773 }
1774 }
1775 }
1776 }
1777 else
1778 {
1779 if (is_hostkey)
1780 {
1781 if (hostkey_pressed)
1782 {
1783 hostkey_pressed = false;
1784 if (hostkey_alone)
1785 {
1786 if (isPaused())
1787 {
1788 vboxProblem().remindAboutPausedVMInput();
1789 }
1790 else
1791 if (isRunning())
1792 {
1793 bool captured = kbd_captured;
1794 if (!captured)
1795 vboxProblem().remindAboutInputCapture();
1796 captureKbd (!captured, false);
1797 if (!(mouse_absolute && mouse_integration))
1798 captureMouse (kbd_captured);
1799 }
1800 }
1801 if (isRunning())
1802 sendChangedKeyStates();
1803 emit_signal = true;
1804 }
1805 }
1806 else
1807 {
1808 if (hostkey_pressed)
1809 hostkey_alone = false;
1810 }
1811 }
1812 // emit the keyboard state change signal
1813 if (emit_signal)
1814 emitKeyboardStateChanged();
1815
1816 // process HOST+<key> shortcuts. currently, <key> is limited to
1817 // alphanumeric chars.
1818 if (hotkey)
1819 {
1820 bool processed = false;
1821#if defined (Q_WS_WIN32)
1822 NOREF(aUniKey);
1823 int n = GetKeyboardLayoutList (0, NULL);
1824 Assert (n);
1825 HKL *list = new HKL [n];
1826 GetKeyboardLayoutList (n, list);
1827 for (int i = 0; i < n && !processed; i++)
1828 {
1829 wchar_t ch;
1830 static BYTE keys [256] = {0};
1831 if (!ToUnicodeEx (hotkey, 0, keys, &ch, 1, 0, list [i]) == 1)
1832 ch = 0;
1833 if (ch)
1834 processed = processHotKey (QKeySequence (UNICODE_ACCEL +
1835 QChar (ch).upper().unicode()),
1836 mainwnd->menuBar());
1837 }
1838 delete[] list;
1839#elif defined (Q_WS_X11)
1840 NOREF(aUniKey);
1841 Display *display = x11Display();
1842 int keysyms_per_keycode = getKeysymsPerKeycode();
1843 KeyCode kc = XKeysymToKeycode (display, key);
1844 // iterate over the first level (not shifted) keysyms in every group
1845 for (int i = 0; i < keysyms_per_keycode && !processed; i += 2)
1846 {
1847 KeySym ks = XKeycodeToKeysym (display, kc, i);
1848 char ch = 0;
1849 if (!XkbTranslateKeySym (display, &ks, 0, &ch, 1, NULL) == 1)
1850 ch = 0;
1851 if (ch)
1852 {
1853 QChar c = QString::fromLocal8Bit (&ch, 1) [0];
1854 processed = processHotKey (QKeySequence (UNICODE_ACCEL +
1855 c.upper().unicode()),
1856 mainwnd->menuBar());
1857 }
1858 }
1859#elif defined (Q_WS_MAC)
1860 if (aUniKey && aUniKey[0] && !aUniKey[1])
1861 processed = processHotKey (QKeySequence (UNICODE_ACCEL +
1862 QChar (aUniKey [0]).upper().unicode()),
1863 mainwnd->menuBar());
1864#endif
1865 // grab the key from Qt if processed, or pass it to Qt otherwise
1866 // in order to process non-alphanumeric keys in event(), after they are
1867 // converted to Qt virtual keys.
1868 return processed;
1869 }
1870
1871 // no more to do, if the host key is in action or the VM is paused
1872 if (hostkey_pressed || is_hostkey || isPaused())
1873 {
1874 // grab the key from Qt and from VM if it's a host key,
1875 // otherwise just pass it to Qt
1876 return is_hostkey;
1877 }
1878
1879 CKeyboard keyboard = cconsole.GetKeyboard();
1880 Assert (!keyboard.isNull());
1881
1882#if defined (Q_WS_WIN32)
1883 // send pending WM_PAINT events
1884 ::UpdateWindow (viewport()->winId());
1885#endif
1886
1887// LogFlow (("*** Putting scan codes: "));
1888// for (int i = 0; i < count; i++)
1889// LogFlow (("%02x ", codes [i]));
1890// LogFlow (("\n"));
1891
1892 keyboard.PutScancodes (codes, count);
1893
1894 // grab the key from Qt
1895 return true;
1896}
1897
1898/**
1899 * Called on every mouse/wheel move and button press/release.
1900 *
1901 * @return true to consume the event and false to pass it to Qt
1902 */
1903bool VBoxConsoleView::mouseEvent (int aType, const QPoint &aPos,
1904 const QPoint &aGlobalPos, ButtonState aButton,
1905 ButtonState aState, ButtonState aStateAfter,
1906 int aWheelDelta, Orientation aWheelDir)
1907{
1908#if 0
1909 char buf [256];
1910 sprintf (buf,
1911 "MOUSE: type=%03d x=%03d y=%03d btn=%03d st=%08X stAfter=%08X "
1912 "wdelta=%03d wdir=%03d",
1913 aType, aPos.x(), aPos.y(), aButton, aState, aStateAfter,
1914 aWheelDelta, aWheelDir);
1915 ((QMainWindow*)mainwnd)->statusBar()->message (buf);
1916#else
1917 Q_UNUSED (aButton);
1918 Q_UNUSED (aState);
1919#endif
1920
1921 int state = 0;
1922 if (aStateAfter & LeftButton)
1923 state |= CEnums::LeftButton;
1924 if (aStateAfter & RightButton)
1925 state |= CEnums::RightButton;
1926 if (aStateAfter & MidButton)
1927 state |= CEnums::MiddleButton;
1928
1929 int wheel = 0;
1930 if (aWheelDir == Vertical)
1931 {
1932 /* the absolute value of wheel delta is 120 units per every wheel
1933 * move; positive deltas correspond to counterclockwize rotations
1934 * (usually up), negative -- to clockwize (usually down). */
1935 wheel = - (aWheelDelta / 120);
1936 }
1937
1938 if (mouse_captured)
1939 {
1940#ifdef Q_WS_WIN32
1941 /* send pending WM_PAINT events */
1942 ::UpdateWindow (viewport()->winId());
1943#endif
1944
1945 CMouse mouse = cconsole.GetMouse();
1946 mouse.PutMouseEvent (aGlobalPos.x() - last_pos.x(),
1947 aGlobalPos.y() - last_pos.y(),
1948 wheel, state);
1949
1950#if defined (Q_WS_MAC)
1951 /*
1952 * Keep the mouse from leaving the widget.
1953 *
1954 * This is a bit tricky to get right because if it escapes we won't necessarily
1955 * get mouse events any longer and can warp it back. So, we keep safety zone
1956 * of up to 300 pixels around the borders of the widget to prevent this from
1957 * happening. Also, the mouse is warped back to the center of the widget.
1958 *
1959 * (Note, aPos seems to be unreliable, it caused endless recursion here at one points...)
1960 * (Note, synergy and other remote clients might not like this cursor warping.)
1961 */
1962 QRect rect = viewport()->visibleRect();
1963 QPoint pw = viewport()->mapToGlobal (viewport()->pos());
1964 rect.moveBy (pw.x(), pw.y());
1965
1966 QRect dpRect = QApplication::desktop()->screenGeometry (viewport());
1967 if (rect.intersects (dpRect))
1968 rect = rect.intersect (dpRect);
1969
1970 int wsafe = rect.width() / 6;
1971 rect.setWidth (rect.width() - wsafe * 2);
1972 rect.setLeft (rect.left() + wsafe);
1973
1974 int hsafe = rect.height() / 6;
1975 rect.setWidth (rect.height() - hsafe * 2);
1976 rect.setTop (rect.top() + hsafe);
1977
1978 if (rect.contains (aGlobalPos, true))
1979 last_pos = aGlobalPos;
1980 else
1981 {
1982 last_pos = rect.center();
1983 QCursor::setPos (last_pos);
1984 }
1985
1986#else /* !Q_WS_MAC */
1987
1988 /* "jerk" the mouse by bringing it to the opposite side
1989 * to simulate the endless moving */
1990
1991#ifdef Q_WS_WIN32
1992 int we = viewport()->width() - 1;
1993 int he = viewport()->height() - 1;
1994 QPoint p = aPos;
1995 if (aPos.x() == 0)
1996 p.setX (we - 1);
1997 else if (aPos.x() == we)
1998 p.setX (1);
1999 if (aPos.y() == 0 )
2000 p.setY (he - 1);
2001 else if (aPos.y() == he)
2002 p.setY (1);
2003
2004 if (p != aPos)
2005 {
2006 last_pos = viewport()->mapToGlobal (p);
2007 QCursor::setPos (last_pos);
2008 }
2009 else
2010 {
2011 last_pos = aGlobalPos;
2012 }
2013#else
2014 int we = QApplication::desktop()->width() - 1;
2015 int he = QApplication::desktop()->height() - 1;
2016 QPoint p = aGlobalPos;
2017 if (aGlobalPos.x() == 0)
2018 p.setX (we - 1);
2019 else if (aGlobalPos.x() == we)
2020 p.setX( 1 );
2021 if (aGlobalPos.y() == 0)
2022 p.setY (he - 1);
2023 else if (aGlobalPos.y() == he)
2024 p.setY (1);
2025
2026 if (p != aGlobalPos)
2027 {
2028 last_pos = p;
2029 QCursor::setPos (last_pos);
2030 }
2031 else
2032 {
2033 last_pos = aGlobalPos;
2034 }
2035#endif
2036#endif /* !Q_WS_MAC */
2037 return true; /* stop further event handling */
2038 }
2039 else /* !mouse_captured */
2040 {
2041#ifdef Q_WS_MAC
2042 /* Update the mouse cursor; this is a bit excessive really... */
2043 if (!DarwinCursorIsNull (&m_darwinCursor))
2044 DarwinCursorSet (&m_darwinCursor);
2045#endif
2046 if (mainwnd->isTrueFullscreen())
2047 {
2048 if (mode != VBoxDefs::SDLMode)
2049 {
2050 /* try to automatically scroll the guest canvas if the
2051 * mouse is on the screen border */
2052 /// @todo (r=dmik) better use a timer for autoscroll
2053 QRect scrGeo = QApplication::desktop()->screenGeometry (this);
2054 int dx = 0, dy = 0;
2055 if (scrGeo.width() < contentsWidth())
2056 {
2057 if (scrGeo.rLeft() == aGlobalPos.x()) dx = -1;
2058 if (scrGeo.rRight() == aGlobalPos.x()) dx = +1;
2059 }
2060 if (scrGeo.height() < contentsHeight())
2061 {
2062 if (scrGeo.rTop() == aGlobalPos.y()) dy = -1;
2063 if (scrGeo.rBottom() == aGlobalPos.y()) dy = +1;
2064 }
2065 if (dx || dy)
2066 scrollBy (dx, dy);
2067 }
2068 }
2069
2070 if (mouse_absolute && mouse_integration)
2071 {
2072 int cw = contentsWidth(), ch = contentsHeight();
2073 int vw = visibleWidth(), vh = visibleHeight();
2074
2075 if (mode != VBoxDefs::SDLMode)
2076 {
2077 /* try to automatically scroll the guest canvas if the
2078 * mouse goes outside its visible part */
2079
2080 int dx = 0;
2081 if (aPos.x() > vw) dx = aPos.x() - vw;
2082 else if (aPos.x() < 0) dx = aPos.x();
2083 int dy = 0;
2084 if (aPos.y() > vh) dy = aPos.y() - vh;
2085 else if (aPos.y() < 0) dy = aPos.y();
2086 if (dx != 0 || dy != 0) scrollBy (dx, dy);
2087 }
2088
2089 QPoint cpnt = viewportToContents (aPos);
2090 if (cpnt.x() < 0) cpnt.setX (0);
2091 else if (cpnt.x() >= cw) cpnt.setX (cw - 1);
2092 if (cpnt.y() < 0) cpnt.setY (0);
2093 else if (cpnt.y() >= ch) cpnt.setY (ch - 1);
2094
2095 CMouse mouse = cconsole.GetMouse();
2096 mouse.PutMouseEventAbsolute (cpnt.x() + 1, cpnt.y() + 1,
2097 wheel, state);
2098 return true; /* stop further event handling */
2099 }
2100 else
2101 {
2102 if (hasFocus() &&
2103 (aType == QEvent::MouseButtonRelease &&
2104 !aStateAfter))
2105 {
2106 if (isPaused())
2107 {
2108 vboxProblem().remindAboutPausedVMInput();
2109 }
2110 else if (isRunning())
2111 {
2112 vboxProblem().remindAboutInputCapture();
2113 captureKbd (true);
2114 captureMouse (true);
2115 }
2116 }
2117 }
2118 }
2119
2120 return false;
2121}
2122
2123void VBoxConsoleView::onStateChange (CEnums::MachineState state)
2124{
2125 switch (state)
2126 {
2127 case CEnums::Paused:
2128 {
2129 if (mode != VBoxDefs::TimerMode && fb)
2130 {
2131 /*
2132 * Take a screen snapshot. Note that TakeScreenShot() always
2133 * needs a 32bpp image
2134 */
2135 QImage shot = QImage (fb->width(), fb->height(), 32, 0);
2136 CDisplay dsp = cconsole.GetDisplay();
2137 dsp.TakeScreenShot (shot.bits(), shot.width(), shot.height());
2138 /*
2139 * TakeScreenShot() may fail if, e.g. the Paused notification
2140 * was delivered after the machine execution was resumed. It's
2141 * not fatal.
2142 */
2143 if (dsp.isOk())
2144 {
2145 dimImage (shot);
2146 mPausedShot = shot;
2147 /* fully repaint to pick up mPausedShot */
2148 viewport()->repaint();
2149 }
2150 }
2151 /* reuse the focus event handler to uncapture everything */
2152 if (hasFocus())
2153 focusEvent (false);
2154 break;
2155 }
2156 case CEnums::Running:
2157 {
2158 if (last_state == CEnums::Paused)
2159 {
2160 if (mode != VBoxDefs::TimerMode && fb)
2161 {
2162 /* reset the pixmap to free memory */
2163 mPausedShot.resize (0, 0);
2164 /*
2165 * ask for full guest display update (it will also update
2166 * the viewport through IFramebuffer::NotifyUpdate)
2167 */
2168 CDisplay dsp = cconsole.GetDisplay();
2169 dsp.InvalidateAndUpdate();
2170 }
2171 }
2172 /* reuse the focus event handler to capture input */
2173 if (hasFocus())
2174 focusEvent (true);
2175 break;
2176 }
2177 default:
2178 break;
2179 }
2180
2181 last_state = state;
2182}
2183
2184void VBoxConsoleView::doRefresh()
2185{
2186#if defined (VBOX_GUI_USE_REFRESH_TIMER)
2187 if ( mode == VBoxDefs::TimerMode ) {
2188 FRAMEBUF_DEBUG_START( xxx );
2189 QSize last_sz = pm.size();
2190 bool rc = display_to_pixmap( cconsole, pm );
2191 if ( rc ) {
2192 if ( pm.size() != last_sz ) {
2193 int pw = pm.width(), ph = pm.height();
2194 resizeContents( pw, ph );
2195 updateGeometry();
2196 setMaximumSize( sizeHint() );
2197 // let our toplevel widget calculate its sizeHint properly
2198 QApplication::sendPostedEvents( 0, QEvent::LayoutHint );
2199 normalizeGeometry();
2200 } else {
2201 // the alternative is to update, so we will be repainted
2202 // on the next event loop iteration. currently disabled.
2203 //updateContents();
2204 repaintContents( false );
2205 }
2206 }
2207 if ( rc )
2208 FRAMEBUF_DEBUG_STOP( xxx, pm.width(), pm.height() );
2209 }
2210 else
2211#endif
2212 repaintContents( false );
2213}
2214
2215void VBoxConsoleView::viewportPaintEvent (QPaintEvent *pe)
2216{
2217#if defined (VBOX_GUI_USE_REFRESH_TIMER)
2218 if (mode == VBoxDefs::TimerMode)
2219 {
2220 if (!pm.isNull())
2221 {
2222 /* draw a part of vbuf */
2223 const QRect &r = pe->rect();
2224 ::bitBlt (viewport(), r.x(), r.y(),
2225 &pm, r.x() + contentsX(), r.y() + contentsY(),
2226 r.width(), r.height(),
2227 CopyROP, TRUE);
2228 }
2229 else
2230 {
2231 viewport()->erase (pe->rect());
2232 }
2233 }
2234 else
2235#endif
2236 {
2237 if (mPausedShot.isNull())
2238 {
2239 /* delegate the paint function to the VBoxFrameBuffer interface */
2240 fb->paintEvent (pe);
2241 return;
2242 }
2243
2244 /* we have a snapshot for the paused state */
2245 QRect r = pe->rect().intersect (viewport()->rect());
2246 QPainter pnt (viewport());
2247 pnt.drawPixmap (r.x(), r.y(), mPausedShot,
2248 r.x() + contentsX(), r.y() + contentsY(),
2249 r.width(), r.height());
2250 }
2251}
2252
2253#ifdef VBOX_GUI_USE_REFRESH_TIMER
2254void VBoxConsoleView::timerEvent( QTimerEvent * )
2255{
2256 doRefresh();
2257}
2258#endif
2259
2260/**
2261 * Captures the keyboard. When captured, no keyboard input reaches the host
2262 * system (including most system combinations like Alt-Tab).
2263 *
2264 * @capture true to capture, false to uncapture
2265 * @emit_signal whether to emit keyboardStateChanged() or not
2266 */
2267void VBoxConsoleView::captureKbd (bool capture, bool emit_signal)
2268{
2269 AssertMsg (attached, ("Console must be attached"));
2270
2271 if (kbd_captured == capture)
2272 return;
2273
2274 /* On Win32, keyboard grabbing is ineffective, a low-level keyboard hook is
2275 * used instead. On X11, we use XGrabKey instead of XGrabKeyboard (called
2276 * by QWidget::grabKeyboard()) because the latter causes problems under
2277 * metacity 2.16 (in particular, due to a bug, a window cannot be moved
2278 * using the mouse if it is currently grabing the keyboard). On Mac OS X,
2279 * we use the Qt methods + disabling global hot keys + watching modifiers
2280 * (for right/left separation). */
2281#if defined (Q_WS_WIN32)
2282 /**/
2283#elif defined (Q_WS_X11)
2284 if (capture)
2285 XGrabKey (x11Display(), AnyKey, AnyModifier,
2286 topLevelWidget()->winId(), False,
2287 GrabModeAsync, GrabModeAsync);
2288 else
2289 XUngrabKey (x11Display(), AnyKey, AnyModifier,
2290 topLevelWidget()->winId());
2291#elif defined (Q_WS_MAC)
2292 if (capture)
2293 {
2294 ::DarwinReleaseKeyboard ();
2295 ::DarwinGrabKeyboard (true);
2296 grabKeyboard();
2297 }
2298 else
2299 {
2300 ::DarwinReleaseKeyboard ();
2301 ::DarwinGrabKeyboard (false);
2302 releaseKeyboard();
2303 }
2304#else
2305 if (capture)
2306 grabKeyboard();
2307 else
2308 releaseKeyboard();
2309#endif
2310
2311 kbd_captured = capture;
2312
2313 if (emit_signal)
2314 emitKeyboardStateChanged ();
2315}
2316
2317/**
2318 * Captures the host mouse pointer. When captured, the mouse pointer is
2319 * unavailable to the host applications.
2320 *
2321 * @capture true to capture, false to uncapture
2322 */
2323void VBoxConsoleView::captureMouse (bool capture, bool emit_signal)
2324{
2325 AssertMsg (attached, ("Console must be attached"));
2326
2327 if (mouse_captured == capture)
2328 return;
2329
2330 if (capture)
2331 {
2332 /* memorize the host position where the cursor was captured */
2333 captured_pos = QCursor::pos();
2334#ifdef Q_WS_WIN32
2335 viewport()->setCursor (QCursor (BlankCursor));
2336 /* move the mouse to the center of the visible area */
2337 QCursor::setPos (mapToGlobal (visibleRect().center()));
2338 last_pos = QCursor::pos();
2339#elif defined (Q_WS_MAC)
2340 /* move the mouse to the center of the visible area */
2341 last_pos = mapToGlobal (visibleRect().center());
2342 QCursor::setPos (last_pos);
2343 /* grab all mouse events. */
2344 viewport()->grabMouse();
2345#else
2346 viewport()->grabMouse();
2347 last_pos = QCursor::pos();
2348#endif
2349 }
2350 else
2351 {
2352#ifndef Q_WS_WIN32
2353 viewport()->releaseMouse();
2354#endif
2355 /* release mouse buttons */
2356 CMouse mouse = cconsole.GetMouse();
2357 mouse.PutMouseEvent (0, 0, 0, 0);
2358 }
2359
2360 mouse_captured = capture;
2361
2362 updateMouseClipping();
2363
2364 if (emit_signal)
2365 emitMouseStateChanged ();
2366}
2367
2368/**
2369 * Searches for a menu item with a given hot key (shortcut). If the item
2370 * is found, activates it and returns true. Otherwise returns false.
2371 */
2372bool VBoxConsoleView::processHotKey (const QKeySequence &key, QMenuData *data)
2373{
2374 if (!data) return false;
2375
2376 /*
2377 * Note: below, we use the internal class QMenuItem, that is subject
2378 * to change w/o notice... (the alternative would be to explicitly assign
2379 * specific IDs to all popup submenus in VBoxConsoleWnd and then
2380 * look through its children in order to find a popup with a given ID,
2381 * which is unconvenient).
2382 */
2383
2384 for (uint i = 0; i < data->count(); i++)
2385 {
2386 int id = data->idAt (i);
2387 QMenuItem *item = data->findItem (id);
2388 if (item->popup())
2389 {
2390 if (processHotKey (key, item->popup()))
2391 return true;
2392 }
2393 else
2394 {
2395 QStringList list = QStringList::split ("\tHost+", data->text (id));
2396 if (list.count() == 2)
2397 {
2398 if (key.matches (QKeySequence (list[1])) == Identical)
2399 {
2400 /*
2401 * we asynchronously post a special event instead of calling
2402 * data->activateItemAt (i) directly, to let key presses
2403 * and releases be processed correctly by Qt first.
2404 * Note: we assume that nobody will delete the menu item
2405 * corresponding to the key sequence, so that the pointer to
2406 * menu data posted along with the event will remain valid in
2407 * the event handler, at least until the main window is closed.
2408 */
2409
2410 QApplication::postEvent (this,
2411 new ActivateMenuEvent (data, i));
2412 return true;
2413 }
2414 }
2415 }
2416 }
2417
2418 return false;
2419}
2420
2421void VBoxConsoleView::releaseAllKeysPressed (bool release_hostkey)
2422{
2423 AssertMsg (attached, ("Console must be attached"));
2424
2425 LONG codes[2];
2426 CKeyboard keyboard = cconsole.GetKeyboard();
2427
2428 // send a dummy scan code (RESEND) to prevent the guest OS from recognizing
2429 // a single key click (for ex., Alt) and performing an unwanted action
2430 // (for ex., activating the menu) when we release all pressed keys below.
2431 // Note, that it's just a guess that sending RESEND will give the desired
2432 // effect :), but at least it works with NT and W2k guests.
2433 codes [0] = 0xFE;
2434 keyboard.PutScancodes (codes, 1);
2435
2436 for (uint i = 0; i < SIZEOF_ARRAY (keys_pressed); i++)
2437 {
2438 if ( keys_pressed[i] & IsKeyPressed )
2439 keyboard.PutScancode (i | 0x80);
2440 if ( keys_pressed[i] & IsKeyPressed )
2441 {
2442 codes[0] = 0xE0;
2443 codes[1] = i | 0x80;
2444 keyboard.PutScancodes (codes, 2);
2445 }
2446 keys_pressed[i] = 0;
2447 }
2448
2449 if (release_hostkey)
2450 hostkey_pressed = false;
2451
2452 emitKeyboardStateChanged ();
2453}
2454
2455void VBoxConsoleView::saveKeyStates()
2456{
2457 ::memcpy(
2458 keys_pressed_copy, keys_pressed,
2459 SIZEOF_ARRAY( keys_pressed )
2460 );
2461}
2462
2463void VBoxConsoleView::sendChangedKeyStates()
2464{
2465 AssertMsg (attached, ("Console must be attached"));
2466
2467 LONG codes[2];
2468 CKeyboard keyboard = cconsole.GetKeyboard();
2469 for ( uint i = 0; i < SIZEOF_ARRAY( keys_pressed ); i++ ) {
2470 uint8_t os = keys_pressed_copy[i];
2471 uint8_t ns = keys_pressed[i];
2472 if ( (os & IsKeyPressed) != (ns & IsKeyPressed) ) {
2473 codes[0] = i;
2474 if ( !(ns & IsKeyPressed) )
2475 codes[0] |= 0x80;
2476 keyboard.PutScancode (codes[0]);
2477 }
2478 if ( (os & IsExtKeyPressed) != (ns & IsExtKeyPressed) ) {
2479 codes[0] = 0xE0;
2480 codes[1] = i;
2481 if ( !(ns & IsExtKeyPressed) )
2482 codes[1] |= 0x80;
2483 keyboard.PutScancodes (codes, 2);
2484 }
2485 }
2486}
2487
2488void VBoxConsoleView::updateMouseClipping()
2489{
2490 AssertMsg (attached, ("Console must be attached"));
2491
2492 if ( mouse_captured ) {
2493 viewport()->setCursor( QCursor( BlankCursor ) );
2494#ifdef Q_WS_WIN32
2495 QRect r = viewport()->rect();
2496 r.moveTopLeft( viewport()->mapToGlobal( QPoint( 0, 0 ) ) );
2497 RECT rect = { r.left(), r.top(), r.right() + 1, r.bottom() + 1 };
2498 ::ClipCursor( &rect );
2499#endif
2500 } else {
2501#ifdef Q_WS_WIN32
2502 ::ClipCursor( NULL );
2503#endif
2504 /* return the cursor to where it was when we captured it and show it */
2505 QCursor::setPos( captured_pos );
2506 viewport()->unsetCursor();
2507 }
2508}
2509
2510void VBoxConsoleView::setPointerShape (MousePointerChangeEvent *me)
2511{
2512 if (me->shapeData () != NULL)
2513 {
2514 bool ok = false;
2515
2516 const uchar *srcAndMaskPtr = me->shapeData();
2517 uint andMaskSize = (me->width() + 7) / 8 * me->height();
2518 const uchar *srcShapePtr = me->shapeData() + ((andMaskSize + 3) & ~3);
2519 uint srcShapePtrScan = me->width() * 4;
2520
2521#if defined (Q_WS_WIN)
2522
2523 BITMAPV5HEADER bi;
2524 HBITMAP hBitmap;
2525 void *lpBits;
2526
2527 ::ZeroMemory (&bi, sizeof (BITMAPV5HEADER));
2528 bi.bV5Size = sizeof (BITMAPV5HEADER);
2529 bi.bV5Width = me->width();
2530 bi.bV5Height = - (LONG) me->height();
2531 bi.bV5Planes = 1;
2532 bi.bV5BitCount = 32;
2533 bi.bV5Compression = BI_BITFIELDS;
2534 // specifiy a supported 32 BPP alpha format for Windows XP
2535 bi.bV5RedMask = 0x00FF0000;
2536 bi.bV5GreenMask = 0x0000FF00;
2537 bi.bV5BlueMask = 0x000000FF;
2538 if (me->hasAlpha())
2539 bi.bV5AlphaMask = 0xFF000000;
2540 else
2541 bi.bV5AlphaMask = 0;
2542
2543 HDC hdc = GetDC (NULL);
2544
2545 // create the DIB section with an alpha channel
2546 hBitmap = CreateDIBSection (hdc, (BITMAPINFO *) &bi, DIB_RGB_COLORS,
2547 (void **) &lpBits, NULL, (DWORD) 0);
2548
2549 ReleaseDC (NULL, hdc);
2550
2551 HBITMAP hMonoBitmap = NULL;
2552 if (me->hasAlpha())
2553 {
2554 // create an empty mask bitmap
2555 hMonoBitmap = CreateBitmap (me->width(), me->height(), 1, 1, NULL);
2556 }
2557 else
2558 {
2559 /* Word aligned AND mask. Will be allocated and created if necessary. */
2560 uint8_t *pu8AndMaskWordAligned = NULL;
2561
2562 /* Width in bytes of the original AND mask scan line. */
2563 uint32_t cbAndMaskScan = (me->width() + 7) / 8;
2564
2565 if (cbAndMaskScan & 1)
2566 {
2567 /* Original AND mask is not word aligned. */
2568
2569 /* Allocate memory for aligned AND mask. */
2570 pu8AndMaskWordAligned = (uint8_t *)RTMemTmpAllocZ ((cbAndMaskScan + 1) * me->height());
2571
2572 Assert(pu8AndMaskWordAligned);
2573
2574 if (pu8AndMaskWordAligned)
2575 {
2576 /* According to MSDN the padding bits must be 0.
2577 * Compute the bit mask to set padding bits to 0 in the last byte of original AND mask.
2578 */
2579 uint32_t u32PaddingBits = cbAndMaskScan * 8 - me->width();
2580 Assert(u32PaddingBits < 8);
2581 uint8_t u8LastBytesPaddingMask = (uint8_t)(0xFF << u32PaddingBits);
2582
2583 Log(("u8LastBytesPaddingMask = %02X, aligned w = %d, width = %d, cbAndMaskScan = %d\n",
2584 u8LastBytesPaddingMask, (cbAndMaskScan + 1) * 8, me->width(), cbAndMaskScan));
2585
2586 uint8_t *src = (uint8_t *)srcAndMaskPtr;
2587 uint8_t *dst = pu8AndMaskWordAligned;
2588
2589 unsigned i;
2590 for (i = 0; i < me->height(); i++)
2591 {
2592 memcpy (dst, src, cbAndMaskScan);
2593
2594 dst[cbAndMaskScan - 1] &= u8LastBytesPaddingMask;
2595
2596 src += cbAndMaskScan;
2597 dst += cbAndMaskScan + 1;
2598 }
2599 }
2600 }
2601
2602 /* create the AND mask bitmap */
2603 hMonoBitmap = ::CreateBitmap (me->width(), me->height(), 1, 1,
2604 pu8AndMaskWordAligned? pu8AndMaskWordAligned: srcAndMaskPtr);
2605
2606 if (pu8AndMaskWordAligned)
2607 {
2608 RTMemTmpFree (pu8AndMaskWordAligned);
2609 }
2610 }
2611
2612 Assert (hBitmap);
2613 Assert (hMonoBitmap);
2614 if (hBitmap && hMonoBitmap)
2615 {
2616 DWORD *dstShapePtr = (DWORD *) lpBits;
2617
2618 for (uint y = 0; y < me->height(); y ++)
2619 {
2620 memcpy (dstShapePtr, srcShapePtr, srcShapePtrScan);
2621 srcShapePtr += srcShapePtrScan;
2622 dstShapePtr += me->width();
2623 }
2624
2625 ICONINFO ii;
2626 ii.fIcon = FALSE;
2627 ii.xHotspot = me->xHot();
2628 ii.yHotspot = me->yHot();
2629 ii.hbmMask = hMonoBitmap;
2630 ii.hbmColor = hBitmap;
2631
2632 HCURSOR hAlphaCursor = CreateIconIndirect (&ii);
2633 Assert (hAlphaCursor);
2634 if (hAlphaCursor)
2635 {
2636 viewport()->setCursor (QCursor (hAlphaCursor));
2637 ok = true;
2638 if (mAlphaCursor)
2639 DestroyIcon (mAlphaCursor);
2640 mAlphaCursor = hAlphaCursor;
2641 }
2642 }
2643
2644 if (hMonoBitmap)
2645 DeleteObject (hMonoBitmap);
2646 if (hBitmap)
2647 DeleteObject (hBitmap);
2648
2649#elif defined (Q_WS_X11)
2650
2651 XcursorImage *img = XcursorImageCreate (me->width(), me->height());
2652 Assert (img);
2653 if (img)
2654 {
2655 img->xhot = me->xHot();
2656 img->yhot = me->yHot();
2657
2658 XcursorPixel *dstShapePtr = img->pixels;
2659
2660 for (uint y = 0; y < me->height(); y ++)
2661 {
2662 memcpy (dstShapePtr, srcShapePtr, srcShapePtrScan);
2663
2664 if (!me->hasAlpha())
2665 {
2666 /* convert AND mask to the alpha channel */
2667 uchar byte = 0;
2668 for (uint x = 0; x < me->width(); x ++)
2669 {
2670 if (!(x % 8))
2671 byte = *(srcAndMaskPtr ++);
2672 else
2673 byte <<= 1;
2674
2675 if (byte & 0x80)
2676 {
2677 /* Linux doesn't support inverted pixels (XOR ops,
2678 * to be exact) in cursor shapes, so we detect such
2679 * pixels and always replace them with black ones to
2680 * make them visible at least over light colors */
2681 if (dstShapePtr [x] & 0x00FFFFFF)
2682 dstShapePtr [x] = 0xFF000000;
2683 else
2684 dstShapePtr [x] = 0x00000000;
2685 }
2686 else
2687 dstShapePtr [x] |= 0xFF000000;
2688 }
2689 }
2690
2691 srcShapePtr += srcShapePtrScan;
2692 dstShapePtr += me->width();
2693 }
2694
2695 Cursor cur = XcursorImageLoadCursor (x11Display(), img);
2696 Assert (cur);
2697 if (cur)
2698 {
2699 viewport()->setCursor (QCursor (cur));
2700 ok = true;
2701 }
2702
2703 XcursorImageDestroy (img);
2704 }
2705
2706#elif defined(Q_WS_MAC)
2707
2708 /*
2709 * Qt3/Mac only supports black/white cursors and it offers no way
2710 * to create your own cursors here unlike on X11 and Windows.
2711 * Which means we're pretty much forced to do it our own way.
2712 */
2713 int rc;
2714
2715 /* dispose of the old cursor. */
2716 if (!DarwinCursorIsNull (&m_darwinCursor))
2717 {
2718 rc = DarwinCursorDestroy (&m_darwinCursor);
2719 AssertRC (rc);
2720 }
2721
2722 /* create the new cursor */
2723 rc = DarwinCursorCreate (me->width(), me->height(), me->xHot(), me->yHot(), me->hasAlpha(),
2724 srcAndMaskPtr, srcShapePtr, &m_darwinCursor);
2725 AssertRC (rc);
2726 if (VBOX_SUCCESS (rc))
2727 {
2728 /** @todo check current mouse coordinates. */
2729 rc = DarwinCursorSet (&m_darwinCursor);
2730 AssertRC (rc);
2731 }
2732 ok = VBOX_SUCCESS (rc);
2733 NOREF (srcShapePtrScan);
2734
2735#else
2736
2737# warning "port me"
2738
2739#endif
2740 if (!ok)
2741 viewport()->unsetCursor();
2742 }
2743 else
2744 {
2745 /*
2746 * We did not get any shape data
2747 */
2748 if (me->isVisible ())
2749 {
2750 /*
2751 * We're supposed to make the last shape we got visible.
2752 * We don't support that for now...
2753 */
2754 /// @todo viewport()->setCursor (QCursor());
2755 }
2756 else
2757 {
2758 viewport()->setCursor (QCursor::BlankCursor);
2759 }
2760 }
2761}
2762
2763inline QRgb qRgbIntensity (QRgb rgb, int mul, int div)
2764{
2765 int r = qRed (rgb);
2766 int g = qGreen (rgb);
2767 int b = qBlue (rgb);
2768 return qRgb (mul * r / div, mul * g / div, mul * b / div);
2769}
2770
2771/* static */
2772void VBoxConsoleView::dimImage (QImage &img)
2773{
2774 for (int y = 0; y < img.height(); y ++) {
2775 if (y % 2) {
2776 if (img.depth() == 32) {
2777 for (int x = 0; x < img.width(); x ++) {
2778 int gray = qGray (img.pixel (x, y)) / 2;
2779 img.setPixel (x, y, qRgb (gray, gray, gray));
2780// img.setPixel (x, y, qRgbIntensity (img.pixel (x, y), 1, 2));
2781 }
2782 } else {
2783 ::memset (img.scanLine (y), 0, img.bytesPerLine());
2784 }
2785 } else {
2786 if (img.depth() == 32) {
2787 for (int x = 0; x < img.width(); x ++) {
2788 int gray = (2 * qGray (img.pixel (x, y))) / 3;
2789 img.setPixel (x, y, qRgb (gray, gray, gray));
2790// img.setPixel (x, y, qRgbIntensity (img.pixel(x, y), 2, 3));
2791 }
2792 }
2793 }
2794 }
2795}
2796
2797void VBoxConsoleView::doResizeHint()
2798{
2799 if (autoresize_guest)
2800 {
2801 /* Get the available size for the guest display.
2802 * We assume here that the centralWidget() contains this view only
2803 * and gives it all available space. */
2804 QSize sz (mainwnd->centralWidget()->size());
2805 sz -= QSize (frameWidth() * 2, frameWidth() * 2);
2806 LogFlowFunc (("Will suggest %d,%d\n", sz.width(), sz.height()));
2807
2808 cconsole.GetDisplay().SetVideoModeHint (sz.width(), sz.height(), 0);
2809 }
2810}
2811
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