VirtualBox

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

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

warnings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 87.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.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 if (gs.autoCapture())
1541 {
1542 captureKbd (true);
1543/// @todo (dmik)
1544// the below is for the mouse auto-capture. disabled for now. in order to
1545// properly support it, we need to know when *all* mouse buttons are
1546// released after we got focus, and grab the mouse only after then.
1547// btw, the similar would be good the for keyboard auto-capture, too.
1548// if (!(mouse_absolute && mouse_integration))
1549// captureMouse (true);
1550 }
1551 }
1552 else
1553 {
1554 captureMouse (false);
1555 captureKbd (false, false);
1556 releaseAllKeysPressed (!hasFocus());
1557 }
1558}
1559
1560/**
1561 * Synchronize the views of the host and the guest to the modifier keys.
1562 * This function will add up to 6 additional keycodes to codes.
1563 *
1564 * @param codes pointer to keycodes which are sent to the keyboard
1565 * @param count pointer to the keycodes counter
1566 */
1567void VBoxConsoleView::fixModifierState(LONG *codes, uint *count)
1568{
1569#if defined(Q_WS_X11)
1570
1571 Window wDummy1, wDummy2;
1572 int iDummy3, iDummy4, iDummy5, iDummy6;
1573 unsigned uMask;
1574 unsigned uKeyMaskNum = 0, uKeyMaskCaps = 0, uKeyMaskScroll = 0;
1575
1576 uKeyMaskCaps = LockMask;
1577 XModifierKeymap* map = XGetModifierMapping(qt_xdisplay());
1578 KeyCode keyCodeNum = XKeysymToKeycode(qt_xdisplay(), XK_Num_Lock);
1579 KeyCode keyCodeScroll = XKeysymToKeycode(qt_xdisplay(), XK_Scroll_Lock);
1580
1581 for (int i = 0; i < 8; i++)
1582 {
1583 if ( keyCodeNum != NoSymbol
1584 && map->modifiermap[map->max_keypermod * i] == keyCodeNum)
1585 uKeyMaskNum = 1 << i;
1586 else if ( keyCodeScroll != NoSymbol
1587 && map->modifiermap[map->max_keypermod * i] == keyCodeScroll)
1588 uKeyMaskScroll = 1 << i;
1589 }
1590 XQueryPointer(qt_xdisplay(), DefaultRootWindow(qt_xdisplay()), &wDummy1, &wDummy2,
1591 &iDummy3, &iDummy4, &iDummy5, &iDummy6, &uMask);
1592 XFreeModifiermap(map);
1593
1594 if (muNumLockAdaptionCnt && (mfNumLock ^ !!(uMask & uKeyMaskNum)))
1595 {
1596 muNumLockAdaptionCnt--;
1597 codes[(*count)++] = 0x45;
1598 codes[(*count)++] = 0x45 | 0x80;
1599 }
1600 if (muCapsLockAdaptionCnt && (mfCapsLock ^ !!(uMask & uKeyMaskCaps)))
1601 {
1602 muCapsLockAdaptionCnt--;
1603 codes[(*count)++] = 0x3a;
1604 codes[(*count)++] = 0x3a | 0x80;
1605 }
1606
1607#elif defined(Q_WS_WIN32)
1608
1609 if (muNumLockAdaptionCnt && (mfNumLock ^ !!(GetKeyState(VK_NUMLOCK))))
1610 {
1611 muNumLockAdaptionCnt--;
1612 codes[(*count)++] = 0x45;
1613 codes[(*count)++] = 0x45 | 0x80;
1614 }
1615 if (muCapsLockAdaptionCnt && (mfCapsLock ^ !!(GetKeyState(VK_CAPITAL))))
1616 {
1617 muCapsLockAdaptionCnt--;
1618 codes[(*count)++] = 0x3a;
1619 codes[(*count)++] = 0x3a | 0x80;
1620 }
1621
1622#elif defined(Q_WS_MAC)
1623
1624 if (muNumLockAdaptionCnt)
1625 {
1626 /* Numlock isn't in the modifier mask. */
1627 KeyMap keys = {{0},{0},{0},{0}};
1628 GetKeys(keys);
1629 if (mfNumLock ^ ASMBitTest(keys, 0x47 /* QZ_NUMLOCK */))
1630 {
1631 muNumLockAdaptionCnt--;
1632 codes[(*count)++] = 0x45;
1633 codes[(*count)++] = 0x45 | 0x80;
1634 }
1635 }
1636 if (muCapsLockAdaptionCnt && (mfCapsLock ^ !!(::GetCurrentEventKeyModifiers() & alphaLock)))
1637 {
1638 muCapsLockAdaptionCnt--;
1639 codes[(*count)++] = 0x3a;
1640 codes[(*count)++] = 0x3a | 0x80;
1641 }
1642
1643#else
1644
1645#warning Adapt VBoxConsoleView::fixModifierState
1646
1647#endif
1648
1649
1650}
1651
1652/**
1653 * Called on every key press and release (while in focus).
1654 *
1655 * @key virtual scan code (virtual key on Win32 and KeySym on X11)
1656 * @scan hardware scan code
1657 * @flags flags, a combination of Key* constants
1658 * @aUniKey Unicode translation of the key. Optional.
1659 *
1660 * @return true to consume the event and false to pass it to Qt
1661 */
1662bool VBoxConsoleView::keyEvent (int key, uint8_t scan, int flags, wchar_t *aUniKey/* = NULL*/)
1663{
1664// char bbbuf[256];
1665// sprintf (bbbuf,
1666// "key=%08X scan=%02X flags=%08X",
1667// key, scan, flags);
1668// ((QMainWindow*)mainwnd)->statusBar()->message (bbbuf);
1669
1670 const bool is_hostkey = key == gs.hostKey();
1671
1672 LONG buf [16];
1673 LONG *codes = buf;
1674 uint count = 0;
1675
1676 if (!is_hostkey)
1677 {
1678 if (flags & KeyPrint)
1679 {
1680 static LONG PrintMake[] = { 0xE0, 0x2A, 0xE0, 0x37 };
1681 static LONG PrintBreak[] = { 0xE0, 0xB7, 0xE0, 0xAA };
1682 if (flags & KeyPressed)
1683 {
1684 codes = PrintMake;
1685 count = SIZEOF_ARRAY (PrintMake);
1686 }
1687 else
1688 {
1689 codes = PrintBreak;
1690 count = SIZEOF_ARRAY (PrintBreak);
1691 }
1692 }
1693 else if ((flags & (KeyPause | KeyPressed)) == (KeyPause | KeyPressed))
1694 {
1695 static LONG Pause[] = { 0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5 };
1696 codes = Pause;
1697 count = SIZEOF_ARRAY (Pause);
1698 }
1699 else
1700 {
1701 if (flags & KeyPressed)
1702 {
1703 // Check if the guest has the same view on the modifier keys (NumLock,
1704 // CapsLock, ScrollLock) as the X server. If not, send KeyPress events
1705 // to synchronize the state.
1706 fixModifierState (codes, &count);
1707 }
1708
1709 // process the scancode and update the table of pressed keys
1710 uint8_t what_pressed = IsKeyPressed;
1711
1712 if (flags & KeyExtended)
1713 {
1714 codes [count++] = 0xE0;
1715 what_pressed = IsExtKeyPressed;
1716 }
1717
1718 if (flags & KeyPressed)
1719 {
1720 codes [count++] = scan;
1721 keys_pressed [scan] |= what_pressed;
1722 }
1723 else
1724 {
1725 // if we haven't got this key's press message, we ignore its release
1726 if (!(keys_pressed [scan] & what_pressed))
1727 return true;
1728 codes [count++] = scan | 0x80;
1729 keys_pressed [scan] &= ~what_pressed;
1730 }
1731
1732 if (kbd_captured)
1733 keys_pressed [scan] |= IsKbdCaptured;
1734 else
1735 keys_pressed [scan] &= ~IsKbdCaptured;
1736 }
1737 }
1738 else
1739 {
1740 // currently this is used in winLowKeyboardEvent() only
1741 hostkey_in_capture = kbd_captured;
1742 }
1743
1744 bool emit_signal = false;
1745 int hotkey = 0;
1746
1747 // process the host key
1748 if (flags & KeyPressed)
1749 {
1750 if (is_hostkey)
1751 {
1752 if (!hostkey_pressed)
1753 {
1754 hostkey_pressed = hostkey_alone = true;
1755 if (isRunning())
1756 saveKeyStates();
1757 emit_signal = true;
1758 }
1759 }
1760 else
1761 {
1762 if (hostkey_pressed)
1763 {
1764 if (hostkey_alone)
1765 {
1766 hotkey = key;
1767 hostkey_alone = false;
1768 }
1769 }
1770 }
1771 }
1772 else
1773 {
1774 if (is_hostkey)
1775 {
1776 if (hostkey_pressed)
1777 {
1778 hostkey_pressed = false;
1779 if (hostkey_alone)
1780 {
1781 if (isPaused())
1782 {
1783 vboxProblem().remindAboutPausedVMInput();
1784 }
1785 else
1786 if (isRunning())
1787 {
1788 bool captured = kbd_captured;
1789 if (!captured)
1790 vboxProblem().remindAboutInputCapture();
1791 captureKbd (!captured, false);
1792 if (!(mouse_absolute && mouse_integration))
1793 captureMouse (kbd_captured);
1794 }
1795 }
1796 if (isRunning())
1797 sendChangedKeyStates();
1798 emit_signal = true;
1799 }
1800 }
1801 else
1802 {
1803 if (hostkey_pressed)
1804 hostkey_alone = false;
1805 }
1806 }
1807 // emit the keyboard state change signal
1808 if (emit_signal)
1809 emitKeyboardStateChanged();
1810
1811 // process HOST+<key> shortcuts. currently, <key> is limited to
1812 // alphanumeric chars.
1813 if (hotkey)
1814 {
1815 bool processed = false;
1816#if defined (Q_WS_WIN32)
1817 NOREF(aUniKey);
1818 int n = GetKeyboardLayoutList (0, NULL);
1819 Assert (n);
1820 HKL *list = new HKL [n];
1821 GetKeyboardLayoutList (n, list);
1822 for (int i = 0; i < n && !processed; i++)
1823 {
1824 wchar_t ch;
1825 static BYTE keys [256] = {0};
1826 if (!ToUnicodeEx (hotkey, 0, keys, &ch, 1, 0, list [i]) == 1)
1827 ch = 0;
1828 if (ch)
1829 processed = processHotKey (QKeySequence (UNICODE_ACCEL +
1830 QChar (ch).upper().unicode()),
1831 mainwnd->menuBar());
1832 }
1833 delete[] list;
1834#elif defined (Q_WS_X11)
1835 NOREF(aUniKey);
1836 Display *display = x11Display();
1837 int keysyms_per_keycode = getKeysymsPerKeycode();
1838 KeyCode kc = XKeysymToKeycode (display, key);
1839 // iterate over the first level (not shifted) keysyms in every group
1840 for (int i = 0; i < keysyms_per_keycode && !processed; i += 2)
1841 {
1842 KeySym ks = XKeycodeToKeysym (display, kc, i);
1843 char ch = 0;
1844 if (!XkbTranslateKeySym (display, &ks, 0, &ch, 1, NULL) == 1)
1845 ch = 0;
1846 if (ch)
1847 {
1848 QChar c = QString::fromLocal8Bit (&ch, 1) [0];
1849 processed = processHotKey (QKeySequence (UNICODE_ACCEL +
1850 c.upper().unicode()),
1851 mainwnd->menuBar());
1852 }
1853 }
1854#elif defined (Q_WS_MAC)
1855 if (aUniKey && aUniKey[0] && !aUniKey[1])
1856 processed = processHotKey (QKeySequence (UNICODE_ACCEL +
1857 QChar (aUniKey [0]).upper().unicode()),
1858 mainwnd->menuBar());
1859#endif
1860 // grab the key from Qt if processed, or pass it to Qt otherwise
1861 // in order to process non-alphanumeric keys in event(), after they are
1862 // converted to Qt virtual keys.
1863 return processed;
1864 }
1865
1866 // no more to do, if the host key is in action or the VM is paused
1867 if (hostkey_pressed || is_hostkey || isPaused())
1868 {
1869 // grab the key from Qt and from VM if it's a host key,
1870 // otherwise just pass it to Qt
1871 return is_hostkey;
1872 }
1873
1874 CKeyboard keyboard = cconsole.GetKeyboard();
1875 Assert (!keyboard.isNull());
1876
1877#if defined (Q_WS_WIN32)
1878 // send pending WM_PAINT events
1879 ::UpdateWindow (viewport()->winId());
1880#endif
1881
1882// LogFlow (("*** Putting scan codes: "));
1883// for (int i = 0; i < count; i++)
1884// LogFlow (("%02x ", codes [i]));
1885// LogFlow (("\n"));
1886
1887 keyboard.PutScancodes (codes, count);
1888
1889 // grab the key from Qt
1890 return true;
1891}
1892
1893/**
1894 * Called on every mouse/wheel move and button press/release.
1895 *
1896 * @return true to consume the event and false to pass it to Qt
1897 */
1898bool VBoxConsoleView::mouseEvent (int aType, const QPoint &aPos,
1899 const QPoint &aGlobalPos, ButtonState aButton,
1900 ButtonState aState, ButtonState aStateAfter,
1901 int aWheelDelta, Orientation aWheelDir)
1902{
1903#if 0
1904 char buf [256];
1905 sprintf (buf,
1906 "MOUSE: type=%03d x=%03d y=%03d btn=%03d st=%08X stAfter=%08X "
1907 "wdelta=%03d wdir=%03d",
1908 aType, aPos.x(), aPos.y(), aButton, aState, aStateAfter,
1909 aWheelDelta, aWheelDir);
1910 ((QMainWindow*)mainwnd)->statusBar()->message (buf);
1911#else
1912 Q_UNUSED (aButton);
1913 Q_UNUSED (aState);
1914#endif
1915
1916 int state = 0;
1917 if (aStateAfter & LeftButton)
1918 state |= CEnums::LeftButton;
1919 if (aStateAfter & RightButton)
1920 state |= CEnums::RightButton;
1921 if (aStateAfter & MidButton)
1922 state |= CEnums::MiddleButton;
1923
1924 int wheel = 0;
1925 if (aWheelDir == Vertical)
1926 {
1927 /* the absolute value of wheel delta is 120 units per every wheel
1928 * move; positive deltas correspond to counterclockwize rotations
1929 * (usually up), negative -- to clockwize (usually down). */
1930 wheel = - (aWheelDelta / 120);
1931 }
1932
1933 if (mouse_captured)
1934 {
1935#ifdef Q_WS_WIN32
1936 /* send pending WM_PAINT events */
1937 ::UpdateWindow (viewport()->winId());
1938#endif
1939
1940 CMouse mouse = cconsole.GetMouse();
1941 mouse.PutMouseEvent (aGlobalPos.x() - last_pos.x(),
1942 aGlobalPos.y() - last_pos.y(),
1943 wheel, state);
1944
1945#if defined (Q_WS_MAC)
1946 /*
1947 * Keep the mouse from leaving the widget.
1948 *
1949 * This is a bit tricky to get right because if it escapes we won't necessarily
1950 * get mouse events any longer and can warp it back. So, we keep safety zone
1951 * of up to 300 pixels around the borders of the widget to prevent this from
1952 * happening. Also, the mouse is warped back to the center of the widget.
1953 *
1954 * (Note, aPos seems to be unreliable, it caused endless recursion here at one points...)
1955 * (Note, synergy and other remote clients might not like this cursor warping.)
1956 */
1957 QRect rect = viewport()->visibleRect();
1958 QPoint pw = viewport()->mapToGlobal (viewport()->pos());
1959 rect.moveBy (pw.x(), pw.y());
1960
1961 QRect dpRect = QApplication::desktop()->screenGeometry (viewport());
1962 if (rect.intersects (dpRect))
1963 rect = rect.intersect (dpRect);
1964
1965 int wsafe = rect.width() / 6;
1966 rect.setWidth (rect.width() - wsafe * 2);
1967 rect.setLeft (rect.left() + wsafe);
1968
1969 int hsafe = rect.height() / 6;
1970 rect.setWidth (rect.height() - hsafe * 2);
1971 rect.setTop (rect.top() + hsafe);
1972
1973 if (rect.contains (aGlobalPos, true))
1974 last_pos = aGlobalPos;
1975 else
1976 {
1977 last_pos = rect.center();
1978 QCursor::setPos (last_pos);
1979 }
1980
1981#else /* !Q_WS_MAC */
1982
1983 /* "jerk" the mouse by bringing it to the opposite side
1984 * to simulate the endless moving */
1985
1986#ifdef Q_WS_WIN32
1987 int we = viewport()->width() - 1;
1988 int he = viewport()->height() - 1;
1989 QPoint p = aPos;
1990 if (aPos.x() == 0)
1991 p.setX (we - 1);
1992 else if (aPos.x() == we)
1993 p.setX (1);
1994 if (aPos.y() == 0 )
1995 p.setY (he - 1);
1996 else if (aPos.y() == he)
1997 p.setY (1);
1998
1999 if (p != aPos)
2000 {
2001 last_pos = viewport()->mapToGlobal (p);
2002 QCursor::setPos (last_pos);
2003 }
2004 else
2005 {
2006 last_pos = aGlobalPos;
2007 }
2008#else
2009 int we = QApplication::desktop()->width() - 1;
2010 int he = QApplication::desktop()->height() - 1;
2011 QPoint p = aGlobalPos;
2012 if (aGlobalPos.x() == 0)
2013 p.setX (we - 1);
2014 else if (aGlobalPos.x() == we)
2015 p.setX( 1 );
2016 if (aGlobalPos.y() == 0)
2017 p.setY (he - 1);
2018 else if (aGlobalPos.y() == he)
2019 p.setY (1);
2020
2021 if (p != aGlobalPos)
2022 {
2023 last_pos = p;
2024 QCursor::setPos (last_pos);
2025 }
2026 else
2027 {
2028 last_pos = aGlobalPos;
2029 }
2030#endif
2031#endif /* !Q_WS_MAC */
2032 return true; /* stop further event handling */
2033 }
2034 else /* !mouse_captured */
2035 {
2036#ifdef Q_WS_MAC
2037 /* Update the mouse cursor; this is a bit excessive really... */
2038 if (!DarwinCursorIsNull (&m_darwinCursor))
2039 DarwinCursorSet (&m_darwinCursor);
2040#endif
2041 if (mainwnd->isTrueFullscreen())
2042 {
2043 if (mode != VBoxDefs::SDLMode)
2044 {
2045 /* try to automatically scroll the guest canvas if the
2046 * mouse is on the screen border */
2047 /// @todo (r=dmik) better use a timer for autoscroll
2048 QRect scrGeo = QApplication::desktop()->screenGeometry (this);
2049 int dx = 0, dy = 0;
2050 if (scrGeo.width() < contentsWidth())
2051 {
2052 if (scrGeo.rLeft() == aGlobalPos.x()) dx = -1;
2053 if (scrGeo.rRight() == aGlobalPos.x()) dx = +1;
2054 }
2055 if (scrGeo.height() < contentsHeight())
2056 {
2057 if (scrGeo.rTop() == aGlobalPos.y()) dy = -1;
2058 if (scrGeo.rBottom() == aGlobalPos.y()) dy = +1;
2059 }
2060 if (dx || dy)
2061 scrollBy (dx, dy);
2062 }
2063 }
2064
2065 if (mouse_absolute && mouse_integration)
2066 {
2067 int cw = contentsWidth(), ch = contentsHeight();
2068 int vw = visibleWidth(), vh = visibleHeight();
2069
2070 if (mode != VBoxDefs::SDLMode)
2071 {
2072 /* try to automatically scroll the guest canvas if the
2073 * mouse goes outside its visible part */
2074
2075 int dx = 0;
2076 if (aPos.x() > vw) dx = aPos.x() - vw;
2077 else if (aPos.x() < 0) dx = aPos.x();
2078 int dy = 0;
2079 if (aPos.y() > vh) dy = aPos.y() - vh;
2080 else if (aPos.y() < 0) dy = aPos.y();
2081 if (dx != 0 || dy != 0) scrollBy (dx, dy);
2082 }
2083
2084 QPoint cpnt = viewportToContents (aPos);
2085 if (cpnt.x() < 0) cpnt.setX (0);
2086 else if (cpnt.x() >= cw) cpnt.setX (cw - 1);
2087 if (cpnt.y() < 0) cpnt.setY (0);
2088 else if (cpnt.y() >= ch) cpnt.setY (ch - 1);
2089
2090 CMouse mouse = cconsole.GetMouse();
2091 mouse.PutMouseEventAbsolute (cpnt.x() + 1, cpnt.y() + 1,
2092 wheel, state);
2093 return true; /* stop further event handling */
2094 }
2095 else
2096 {
2097 if (hasFocus() &&
2098 (aType == QEvent::MouseButtonRelease &&
2099 !aStateAfter))
2100 {
2101 if (isPaused())
2102 {
2103 vboxProblem().remindAboutPausedVMInput();
2104 }
2105 else if (isRunning())
2106 {
2107 vboxProblem().remindAboutInputCapture();
2108 captureKbd (true);
2109 captureMouse (true);
2110 }
2111 }
2112 }
2113 }
2114
2115 return false;
2116}
2117
2118void VBoxConsoleView::onStateChange (CEnums::MachineState state)
2119{
2120 switch (state)
2121 {
2122 case CEnums::Paused:
2123 {
2124 if (mode != VBoxDefs::TimerMode && fb)
2125 {
2126 /*
2127 * Take a screen snapshot. Note that TakeScreenShot() always
2128 * needs a 32bpp image
2129 */
2130 QImage shot = QImage (fb->width(), fb->height(), 32, 0);
2131 CDisplay dsp = cconsole.GetDisplay();
2132 dsp.TakeScreenShot (shot.bits(), shot.width(), shot.height());
2133 /*
2134 * TakeScreenShot() may fail if, e.g. the Paused notification
2135 * was delivered after the machine execution was resumed. It's
2136 * not fatal.
2137 */
2138 if (dsp.isOk())
2139 {
2140 dimImage (shot);
2141 mPausedShot = shot;
2142 /* fully repaint to pick up mPausedShot */
2143 viewport()->repaint();
2144 }
2145 }
2146 /* reuse the focus event handler to uncapture everything */
2147 if (hasFocus())
2148 focusEvent (false);
2149 break;
2150 }
2151 case CEnums::Running:
2152 {
2153 if (last_state == CEnums::Paused)
2154 {
2155 if (mode != VBoxDefs::TimerMode && fb)
2156 {
2157 /* reset the pixmap to free memory */
2158 mPausedShot.resize (0, 0);
2159 /*
2160 * ask for full guest display update (it will also update
2161 * the viewport through IFramebuffer::NotifyUpdate)
2162 */
2163 CDisplay dsp = cconsole.GetDisplay();
2164 dsp.InvalidateAndUpdate();
2165 }
2166 }
2167 /* reuse the focus event handler to capture input */
2168 if (hasFocus())
2169 focusEvent (true);
2170 break;
2171 }
2172 default:
2173 break;
2174 }
2175
2176 last_state = state;
2177}
2178
2179void VBoxConsoleView::doRefresh()
2180{
2181#if defined (VBOX_GUI_USE_REFRESH_TIMER)
2182 if ( mode == VBoxDefs::TimerMode ) {
2183 FRAMEBUF_DEBUG_START( xxx );
2184 QSize last_sz = pm.size();
2185 bool rc = display_to_pixmap( cconsole, pm );
2186 if ( rc ) {
2187 if ( pm.size() != last_sz ) {
2188 int pw = pm.width(), ph = pm.height();
2189 resizeContents( pw, ph );
2190 updateGeometry();
2191 setMaximumSize( sizeHint() );
2192 // let our toplevel widget calculate its sizeHint properly
2193 QApplication::sendPostedEvents( 0, QEvent::LayoutHint );
2194 normalizeGeometry();
2195 } else {
2196 // the alternative is to update, so we will be repainted
2197 // on the next event loop iteration. currently disabled.
2198 //updateContents();
2199 repaintContents( false );
2200 }
2201 }
2202 if ( rc )
2203 FRAMEBUF_DEBUG_STOP( xxx, pm.width(), pm.height() );
2204 }
2205 else
2206#endif
2207 repaintContents( false );
2208}
2209
2210void VBoxConsoleView::viewportPaintEvent (QPaintEvent *pe)
2211{
2212#if defined (VBOX_GUI_USE_REFRESH_TIMER)
2213 if (mode == VBoxDefs::TimerMode)
2214 {
2215 if (!pm.isNull())
2216 {
2217 /* draw a part of vbuf */
2218 const QRect &r = pe->rect();
2219 ::bitBlt (viewport(), r.x(), r.y(),
2220 &pm, r.x() + contentsX(), r.y() + contentsY(),
2221 r.width(), r.height(),
2222 CopyROP, TRUE);
2223 }
2224 else
2225 {
2226 viewport()->erase (pe->rect());
2227 }
2228 }
2229 else
2230#endif
2231 {
2232 if (mPausedShot.isNull())
2233 {
2234 /* delegate the paint function to the VBoxFrameBuffer interface */
2235 fb->paintEvent (pe);
2236 return;
2237 }
2238
2239 /* we have a snapshot for the paused state */
2240 QRect r = pe->rect().intersect (viewport()->rect());
2241 QPainter pnt (viewport());
2242 pnt.drawPixmap (r.x(), r.y(), mPausedShot,
2243 r.x() + contentsX(), r.y() + contentsY(),
2244 r.width(), r.height());
2245 }
2246}
2247
2248#ifdef VBOX_GUI_USE_REFRESH_TIMER
2249void VBoxConsoleView::timerEvent( QTimerEvent * )
2250{
2251 doRefresh();
2252}
2253#endif
2254
2255/**
2256 * Captures the keyboard. When captured, no keyboard input reaches the host
2257 * system (including most system combinations like Alt-Tab).
2258 *
2259 * @capture true to capture, false to uncapture
2260 * @emit_signal whether to emit keyboardStateChanged() or not
2261 */
2262void VBoxConsoleView::captureKbd (bool capture, bool emit_signal)
2263{
2264 AssertMsg (attached, ("Console must be attached"));
2265
2266 if (kbd_captured == capture)
2267 return;
2268
2269 /* On Win32, keyboard grabbing is ineffective, a low-level keyboard hook is
2270 * used instead. On X11, we use XGrabKey instead of XGrabKeyboard (called
2271 * by QWidget::grabKeyboard()) because the latter causes problems under
2272 * metacity 2.16 (in particular, due to a bug, a window cannot be moved
2273 * using the mouse if it is currently grabing the keyboard). On Mac OS X,
2274 * we use the Qt methods + disabling global hot keys + watching modifiers
2275 * (for right/left separation). */
2276#if defined (Q_WS_WIN32)
2277 /**/
2278#elif defined (Q_WS_X11)
2279 if (capture)
2280 XGrabKey (x11Display(), AnyKey, AnyModifier,
2281 topLevelWidget()->winId(), False,
2282 GrabModeAsync, GrabModeAsync);
2283 else
2284 XUngrabKey (x11Display(), AnyKey, AnyModifier,
2285 topLevelWidget()->winId());
2286#elif defined (Q_WS_MAC)
2287 if (capture)
2288 {
2289 ::DarwinReleaseKeyboard ();
2290 ::DarwinGrabKeyboard (true);
2291 grabKeyboard();
2292 }
2293 else
2294 {
2295 ::DarwinReleaseKeyboard ();
2296 ::DarwinGrabKeyboard (false);
2297 releaseKeyboard();
2298 }
2299#else
2300 if (capture)
2301 grabKeyboard();
2302 else
2303 releaseKeyboard();
2304#endif
2305
2306 kbd_captured = capture;
2307
2308 if (emit_signal)
2309 emitKeyboardStateChanged ();
2310}
2311
2312/**
2313 * Captures the host mouse pointer. When captured, the mouse pointer is
2314 * unavailable to the host applications.
2315 *
2316 * @capture true to capture, false to uncapture
2317 */
2318void VBoxConsoleView::captureMouse (bool capture, bool emit_signal)
2319{
2320 AssertMsg (attached, ("Console must be attached"));
2321
2322 if (mouse_captured == capture)
2323 return;
2324
2325 if (capture)
2326 {
2327 /* memorize the host position where the cursor was captured */
2328 captured_pos = QCursor::pos();
2329#ifdef Q_WS_WIN32
2330 viewport()->setCursor (QCursor (BlankCursor));
2331 /* move the mouse to the center of the visible area */
2332 QCursor::setPos (mapToGlobal (visibleRect().center()));
2333 last_pos = QCursor::pos();
2334#elif defined (Q_WS_MAC)
2335 /* move the mouse to the center of the visible area */
2336 last_pos = mapToGlobal (visibleRect().center());
2337 QCursor::setPos (last_pos);
2338 /* grab all mouse events. */
2339 viewport()->grabMouse();
2340#else
2341 viewport()->grabMouse();
2342 last_pos = QCursor::pos();
2343#endif
2344 }
2345 else
2346 {
2347#ifndef Q_WS_WIN32
2348 viewport()->releaseMouse();
2349#endif
2350 /* release mouse buttons */
2351 CMouse mouse = cconsole.GetMouse();
2352 mouse.PutMouseEvent (0, 0, 0, 0);
2353 }
2354
2355 mouse_captured = capture;
2356
2357 updateMouseClipping();
2358
2359 if (emit_signal)
2360 emitMouseStateChanged ();
2361}
2362
2363/**
2364 * Searches for a menu item with a given hot key (shortcut). If the item
2365 * is found, activates it and returns true. Otherwise returns false.
2366 */
2367bool VBoxConsoleView::processHotKey (const QKeySequence &key, QMenuData *data)
2368{
2369 if (!data) return false;
2370
2371 /*
2372 * Note: below, we use the internal class QMenuItem, that is subject
2373 * to change w/o notice... (the alternative would be to explicitly assign
2374 * specific IDs to all popup submenus in VBoxConsoleWnd and then
2375 * look through its children in order to find a popup with a given ID,
2376 * which is unconvenient).
2377 */
2378
2379 for (uint i = 0; i < data->count(); i++)
2380 {
2381 int id = data->idAt (i);
2382 QMenuItem *item = data->findItem (id);
2383 if (item->popup())
2384 {
2385 if (processHotKey (key, item->popup()))
2386 return true;
2387 }
2388 else
2389 {
2390 QStringList list = QStringList::split ("\tHost+", data->text (id));
2391 if (list.count() == 2)
2392 {
2393 if (key.matches (QKeySequence (list[1])) == Identical)
2394 {
2395 /*
2396 * we asynchronously post a special event instead of calling
2397 * data->activateItemAt (i) directly, to let key presses
2398 * and releases be processed correctly by Qt first.
2399 * Note: we assume that nobody will delete the menu item
2400 * corresponding to the key sequence, so that the pointer to
2401 * menu data posted along with the event will remain valid in
2402 * the event handler, at least until the main window is closed.
2403 */
2404
2405 QApplication::postEvent (this,
2406 new ActivateMenuEvent (data, i));
2407 return true;
2408 }
2409 }
2410 }
2411 }
2412
2413 return false;
2414}
2415
2416void VBoxConsoleView::releaseAllKeysPressed (bool release_hostkey)
2417{
2418 AssertMsg (attached, ("Console must be attached"));
2419
2420 LONG codes[2];
2421 CKeyboard keyboard = cconsole.GetKeyboard();
2422
2423 // send a dummy scan code (RESEND) to prevent the guest OS from recognizing
2424 // a single key click (for ex., Alt) and performing an unwanted action
2425 // (for ex., activating the menu) when we release all pressed keys below.
2426 // Note, that it's just a guess that sending RESEND will give the desired
2427 // effect :), but at least it works with NT and W2k guests.
2428 codes [0] = 0xFE;
2429 keyboard.PutScancodes (codes, 1);
2430
2431 for (uint i = 0; i < SIZEOF_ARRAY (keys_pressed); i++)
2432 {
2433 if ( keys_pressed[i] & IsKeyPressed )
2434 keyboard.PutScancode (i | 0x80);
2435 if ( keys_pressed[i] & IsKeyPressed )
2436 {
2437 codes[0] = 0xE0;
2438 codes[1] = i | 0x80;
2439 keyboard.PutScancodes (codes, 2);
2440 }
2441 keys_pressed[i] = 0;
2442 }
2443
2444 if (release_hostkey)
2445 hostkey_pressed = false;
2446
2447 emitKeyboardStateChanged ();
2448}
2449
2450void VBoxConsoleView::saveKeyStates()
2451{
2452 ::memcpy(
2453 keys_pressed_copy, keys_pressed,
2454 SIZEOF_ARRAY( keys_pressed )
2455 );
2456}
2457
2458void VBoxConsoleView::sendChangedKeyStates()
2459{
2460 AssertMsg (attached, ("Console must be attached"));
2461
2462 LONG codes[2];
2463 CKeyboard keyboard = cconsole.GetKeyboard();
2464 for ( uint i = 0; i < SIZEOF_ARRAY( keys_pressed ); i++ ) {
2465 uint8_t os = keys_pressed_copy[i];
2466 uint8_t ns = keys_pressed[i];
2467 if ( (os & IsKeyPressed) != (ns & IsKeyPressed) ) {
2468 codes[0] = i;
2469 if ( !(ns & IsKeyPressed) )
2470 codes[0] |= 0x80;
2471 keyboard.PutScancode (codes[0]);
2472 }
2473 if ( (os & IsExtKeyPressed) != (ns & IsExtKeyPressed) ) {
2474 codes[0] = 0xE0;
2475 codes[1] = i;
2476 if ( !(ns & IsExtKeyPressed) )
2477 codes[1] |= 0x80;
2478 keyboard.PutScancodes (codes, 2);
2479 }
2480 }
2481}
2482
2483void VBoxConsoleView::updateMouseClipping()
2484{
2485 AssertMsg (attached, ("Console must be attached"));
2486
2487 if ( mouse_captured ) {
2488 viewport()->setCursor( QCursor( BlankCursor ) );
2489#ifdef Q_WS_WIN32
2490 QRect r = viewport()->rect();
2491 r.moveTopLeft( viewport()->mapToGlobal( QPoint( 0, 0 ) ) );
2492 RECT rect = { r.left(), r.top(), r.right() + 1, r.bottom() + 1 };
2493 ::ClipCursor( &rect );
2494#endif
2495 } else {
2496#ifdef Q_WS_WIN32
2497 ::ClipCursor( NULL );
2498#endif
2499 /* return the cursor to where it was when we captured it and show it */
2500 QCursor::setPos( captured_pos );
2501 viewport()->unsetCursor();
2502 }
2503}
2504
2505void VBoxConsoleView::setPointerShape (MousePointerChangeEvent *me)
2506{
2507 if (me->shapeData () != NULL)
2508 {
2509 bool ok = false;
2510
2511 const uchar *srcAndMaskPtr = me->shapeData();
2512 uint andMaskSize = (me->width() + 7) / 8 * me->height();
2513 const uchar *srcShapePtr = me->shapeData() + ((andMaskSize + 3) & ~3);
2514 uint srcShapePtrScan = me->width() * 4;
2515
2516#if defined (Q_WS_WIN)
2517
2518 BITMAPV5HEADER bi;
2519 HBITMAP hBitmap;
2520 void *lpBits;
2521
2522 ::ZeroMemory (&bi, sizeof (BITMAPV5HEADER));
2523 bi.bV5Size = sizeof (BITMAPV5HEADER);
2524 bi.bV5Width = me->width();
2525 bi.bV5Height = - (LONG) me->height();
2526 bi.bV5Planes = 1;
2527 bi.bV5BitCount = 32;
2528 bi.bV5Compression = BI_BITFIELDS;
2529 // specifiy a supported 32 BPP alpha format for Windows XP
2530 bi.bV5RedMask = 0x00FF0000;
2531 bi.bV5GreenMask = 0x0000FF00;
2532 bi.bV5BlueMask = 0x000000FF;
2533 if (me->hasAlpha())
2534 bi.bV5AlphaMask = 0xFF000000;
2535 else
2536 bi.bV5AlphaMask = 0;
2537
2538 HDC hdc = GetDC (NULL);
2539
2540 // create the DIB section with an alpha channel
2541 hBitmap = CreateDIBSection (hdc, (BITMAPINFO *) &bi, DIB_RGB_COLORS,
2542 (void **) &lpBits, NULL, (DWORD) 0);
2543
2544 ReleaseDC (NULL, hdc);
2545
2546 HBITMAP hMonoBitmap = NULL;
2547 if (me->hasAlpha())
2548 {
2549 // create an empty mask bitmap
2550 hMonoBitmap = CreateBitmap (me->width(), me->height(), 1, 1, NULL);
2551 }
2552 else
2553 {
2554 /* Word aligned AND mask. Will be allocated and created if necessary. */
2555 uint8_t *pu8AndMaskWordAligned = NULL;
2556
2557 /* Width in bytes of the original AND mask scan line. */
2558 uint32_t cbAndMaskScan = (me->width() + 7) / 8;
2559
2560 if (cbAndMaskScan & 1)
2561 {
2562 /* Original AND mask is not word aligned. */
2563
2564 /* Allocate memory for aligned AND mask. */
2565 pu8AndMaskWordAligned = (uint8_t *)RTMemTmpAllocZ ((cbAndMaskScan + 1) * me->height());
2566
2567 Assert(pu8AndMaskWordAligned);
2568
2569 if (pu8AndMaskWordAligned)
2570 {
2571 /* According to MSDN the padding bits must be 0.
2572 * Compute the bit mask to set padding bits to 0 in the last byte of original AND mask.
2573 */
2574 uint32_t u32PaddingBits = cbAndMaskScan * 8 - me->width();
2575 Assert(u32PaddingBits < 8);
2576 uint8_t u8LastBytesPaddingMask = (uint8_t)(0xFF << u32PaddingBits);
2577
2578 Log(("u8LastBytesPaddingMask = %02X, aligned w = %d, width = %d, cbAndMaskScan = %d\n",
2579 u8LastBytesPaddingMask, (cbAndMaskScan + 1) * 8, me->width(), cbAndMaskScan));
2580
2581 uint8_t *src = (uint8_t *)srcAndMaskPtr;
2582 uint8_t *dst = pu8AndMaskWordAligned;
2583
2584 unsigned i;
2585 for (i = 0; i < me->height(); i++)
2586 {
2587 memcpy (dst, src, cbAndMaskScan);
2588
2589 dst[cbAndMaskScan - 1] &= u8LastBytesPaddingMask;
2590
2591 src += cbAndMaskScan;
2592 dst += cbAndMaskScan + 1;
2593 }
2594 }
2595 }
2596
2597 /* create the AND mask bitmap */
2598 hMonoBitmap = ::CreateBitmap (me->width(), me->height(), 1, 1,
2599 pu8AndMaskWordAligned? pu8AndMaskWordAligned: srcAndMaskPtr);
2600
2601 if (pu8AndMaskWordAligned)
2602 {
2603 RTMemTmpFree (pu8AndMaskWordAligned);
2604 }
2605 }
2606
2607 Assert (hBitmap);
2608 Assert (hMonoBitmap);
2609 if (hBitmap && hMonoBitmap)
2610 {
2611 DWORD *dstShapePtr = (DWORD *) lpBits;
2612
2613 for (uint y = 0; y < me->height(); y ++)
2614 {
2615 memcpy (dstShapePtr, srcShapePtr, srcShapePtrScan);
2616 srcShapePtr += srcShapePtrScan;
2617 dstShapePtr += me->width();
2618 }
2619
2620 ICONINFO ii;
2621 ii.fIcon = FALSE;
2622 ii.xHotspot = me->xHot();
2623 ii.yHotspot = me->yHot();
2624 ii.hbmMask = hMonoBitmap;
2625 ii.hbmColor = hBitmap;
2626
2627 HCURSOR hAlphaCursor = CreateIconIndirect (&ii);
2628 Assert (hAlphaCursor);
2629 if (hAlphaCursor)
2630 {
2631 viewport()->setCursor (QCursor (hAlphaCursor));
2632 ok = true;
2633 if (mAlphaCursor)
2634 DestroyIcon (mAlphaCursor);
2635 mAlphaCursor = hAlphaCursor;
2636 }
2637 }
2638
2639 if (hMonoBitmap)
2640 DeleteObject (hMonoBitmap);
2641 if (hBitmap)
2642 DeleteObject (hBitmap);
2643
2644#elif defined (Q_WS_X11)
2645
2646 XcursorImage *img = XcursorImageCreate (me->width(), me->height());
2647 Assert (img);
2648 if (img)
2649 {
2650 img->xhot = me->xHot();
2651 img->yhot = me->yHot();
2652
2653 XcursorPixel *dstShapePtr = img->pixels;
2654
2655 for (uint y = 0; y < me->height(); y ++)
2656 {
2657 memcpy (dstShapePtr, srcShapePtr, srcShapePtrScan);
2658
2659 if (!me->hasAlpha())
2660 {
2661 /* convert AND mask to the alpha channel */
2662 uchar byte = 0;
2663 for (uint x = 0; x < me->width(); x ++)
2664 {
2665 if (!(x % 8))
2666 byte = *(srcAndMaskPtr ++);
2667 else
2668 byte <<= 1;
2669
2670 if (byte & 0x80)
2671 {
2672 /* Linux doesn't support inverted pixels (XOR ops,
2673 * to be exact) in cursor shapes, so we detect such
2674 * pixels and always replace them with black ones to
2675 * make them visible at least over light colors */
2676 if (dstShapePtr [x] & 0x00FFFFFF)
2677 dstShapePtr [x] = 0xFF000000;
2678 else
2679 dstShapePtr [x] = 0x00000000;
2680 }
2681 else
2682 dstShapePtr [x] |= 0xFF000000;
2683 }
2684 }
2685
2686 srcShapePtr += srcShapePtrScan;
2687 dstShapePtr += me->width();
2688 }
2689
2690 Cursor cur = XcursorImageLoadCursor (x11Display(), img);
2691 Assert (cur);
2692 if (cur)
2693 {
2694 viewport()->setCursor (QCursor (cur));
2695 ok = true;
2696 }
2697
2698 XcursorImageDestroy (img);
2699 }
2700
2701#elif defined(Q_WS_MAC)
2702
2703 /*
2704 * Qt3/Mac only supports black/white cursors and it offers no way
2705 * to create your own cursors here unlike on X11 and Windows.
2706 * Which means we're pretty much forced to do it our own way.
2707 */
2708 int rc;
2709
2710 /* dispose of the old cursor. */
2711 if (!DarwinCursorIsNull (&m_darwinCursor))
2712 {
2713 rc = DarwinCursorDestroy (&m_darwinCursor);
2714 AssertRC (rc);
2715 }
2716
2717 /* create the new cursor */
2718 rc = DarwinCursorCreate (me->width(), me->height(), me->xHot(), me->yHot(), me->hasAlpha(),
2719 srcAndMaskPtr, srcShapePtr, &m_darwinCursor);
2720 AssertRC (rc);
2721 if (VBOX_SUCCESS (rc))
2722 {
2723 /** @todo check current mouse coordinates. */
2724 rc = DarwinCursorSet (&m_darwinCursor);
2725 AssertRC (rc);
2726 }
2727 ok = VBOX_SUCCESS (rc);
2728 NOREF (srcShapePtrScan);
2729
2730#else
2731
2732# warning "port me"
2733
2734#endif
2735 if (!ok)
2736 viewport()->unsetCursor();
2737 }
2738 else
2739 {
2740 /*
2741 * We did not get any shape data
2742 */
2743 if (me->isVisible ())
2744 {
2745 /*
2746 * We're supposed to make the last shape we got visible.
2747 * We don't support that for now...
2748 */
2749 /// @todo viewport()->setCursor (QCursor());
2750 }
2751 else
2752 {
2753 viewport()->setCursor (QCursor::BlankCursor);
2754 }
2755 }
2756}
2757
2758inline QRgb qRgbIntensity (QRgb rgb, int mul, int div)
2759{
2760 int r = qRed (rgb);
2761 int g = qGreen (rgb);
2762 int b = qBlue (rgb);
2763 return qRgb (mul * r / div, mul * g / div, mul * b / div);
2764}
2765
2766/* static */
2767void VBoxConsoleView::dimImage (QImage &img)
2768{
2769 for (int y = 0; y < img.height(); y ++) {
2770 if (y % 2) {
2771 if (img.depth() == 32) {
2772 for (int x = 0; x < img.width(); x ++) {
2773 int gray = qGray (img.pixel (x, y)) / 2;
2774 img.setPixel (x, y, qRgb (gray, gray, gray));
2775// img.setPixel (x, y, qRgbIntensity (img.pixel (x, y), 1, 2));
2776 }
2777 } else {
2778 ::memset (img.scanLine (y), 0, img.bytesPerLine());
2779 }
2780 } else {
2781 if (img.depth() == 32) {
2782 for (int x = 0; x < img.width(); x ++) {
2783 int gray = (2 * qGray (img.pixel (x, y))) / 3;
2784 img.setPixel (x, y, qRgb (gray, gray, gray));
2785// img.setPixel (x, y, qRgbIntensity (img.pixel(x, y), 2, 3));
2786 }
2787 }
2788 }
2789 }
2790}
2791
2792void VBoxConsoleView::doResizeHint()
2793{
2794 if (autoresize_guest)
2795 {
2796 /* Get the available size for the guest display.
2797 * We assume here that the centralWidget() contains this view only
2798 * and gives it all available space. */
2799 QSize sz (mainwnd->centralWidget()->size());
2800 sz -= QSize (frameWidth() * 2, frameWidth() * 2);
2801 LogFlowFunc (("Will suggest %d,%d\n", sz.width(), sz.height()));
2802
2803 cconsole.GetDisplay().SetVideoModeHint (sz.width(), sz.height(), 0);
2804 }
2805}
2806
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