VirtualBox

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

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

FE/Qt: synchronize the num lock state between host and guest

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