VirtualBox

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

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

typo (wonder where it comes from / why it used to compile...)

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