VirtualBox

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

Last change on this file since 6456 was 6456, checked in by vboxsync, 17 years ago

refine r27431: handle XFocusIn as well

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

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