From adca39dbdf5eeba4131e7d7637ca325ef571e2e5 Mon Sep 17 00:00:00 2001 From: skyjake Date: Sat, 6 Apr 2013 13:45:40 +0300 Subject: [PATCH] Refactor|libgui|Client: Mouse and keyboard input routing ClientWindow now listens to the input notifications from its Canvas and submits appropriate input events. Canvas was further isolated from concrete implementation of mouse trapping (which will be done by an input driver). --- doomsday/client/include/ui/clientwindow.h | 4 +- doomsday/client/include/ui/sys_input.h | 2 + doomsday/client/src/ui/clientwindow.cpp | 124 +++++++++++++++++----- doomsday/client/src/ui/mouse_qt.cpp | 53 +++++++++ doomsday/libgui/include/de/gui/canvas.h | 5 - doomsday/libgui/src/canvas.cpp | 66 +++--------- 6 files changed, 168 insertions(+), 86 deletions(-) diff --git a/doomsday/client/include/ui/clientwindow.h b/doomsday/client/include/ui/clientwindow.h index b134a24418..a596302d1f 100644 --- a/doomsday/client/include/ui/clientwindow.h +++ b/doomsday/client/include/ui/clientwindow.h @@ -42,8 +42,7 @@ */ class ClientWindow : public de::PersistentCanvasWindow, DENG2_OBSERVES(de::Canvas, GLInit), - DENG2_OBSERVES(de::Canvas, GLResize), - DENG2_OBSERVES(de::Canvas, FocusChange) + DENG2_OBSERVES(de::Canvas, GLResize) { Q_OBJECT @@ -105,7 +104,6 @@ class ClientWindow : public de::PersistentCanvasWindow, void canvasGLInit(de::Canvas &); void canvasGLDraw(de::Canvas &); void canvasGLResized(de::Canvas &); - void canvasFocusChanged(de::Canvas &, bool hasFocus); static ClientWindow &main(); diff --git a/doomsday/client/include/ui/sys_input.h b/doomsday/client/include/ui/sys_input.h index 8f4968b10f..1cea25c5ce 100644 --- a/doomsday/client/include/ui/sys_input.h +++ b/doomsday/client/include/ui/sys_input.h @@ -51,6 +51,8 @@ enum { IMB_RIGHT, IMB_MWHEELUP, // virtual button IMB_MWHEELDOWN, // virtual button + IMB_EXTRA1, + IMB_EXTRA2, // ...other buttons... IMB_MWHEELLEFT = 14, // virtual button IMB_MWHEELRIGHT = 15, // virtual button diff --git a/doomsday/client/src/ui/clientwindow.cpp b/doomsday/client/src/ui/clientwindow.cpp index b667aa067d..a0a08f9032 100644 --- a/doomsday/client/src/ui/clientwindow.cpp +++ b/doomsday/client/src/ui/clientwindow.cpp @@ -36,6 +36,7 @@ #include "ui/sys_input.h" #include "ui/legacywidget.h" #include "ui/busywidget.h" +#include "ui/mouse_qt.h" #include "dd_main.h" #include "con_main.h" @@ -106,7 +107,14 @@ using namespace de; static String const LEGACY_WIDGET_NAME = "legacy"; -DENG2_PIMPL(ClientWindow) +DENG2_PIMPL(ClientWindow), + DENG2_OBSERVES(KeyEventSource, KeyEvent), + DENG2_OBSERVES(MouseEventSource, MouseStateChange), +#ifndef WIN32 + DENG2_OBSERVES(MouseEventSource, MouseAxisEvent), + DENG2_OBSERVES(MouseEventSource, MouseButtonEvent), +#endif + DENG2_OBSERVES(Canvas, FocusChange) { bool needMainInit; bool needRecreateCanvas; @@ -142,6 +150,23 @@ DENG2_PIMPL(ClientWindow) .setLeftTop (busyRoot.viewLeft(), busyRoot.viewTop()) .setRightBottom(busyRoot.viewRight(), busyRoot.viewBottom()); busyRoot.add(busy); + + /// @todo The decision whether to receive input notifications from the + /// canvas is really a concern for the input drivers. + + // Listen to input. + self.canvas().audienceForKeyEvent += this; + self.canvas().audienceForMouseStateChange += this; + +#ifndef WIN32 // On Windows, DirectInput bypasses the mouse input from Canvas. + self.canvas().audienceForMouseAxisEvent += this; + self.canvas().audienceForMouseButtonEvent += this; +#endif + } + + ~Instance() + { + self.canvas().audienceForFocusChange -= this; } void setMode(Mode const &newMode) @@ -171,7 +196,7 @@ DENG2_PIMPL(ClientWindow) self.canvas().trapMouse(); } - self.canvas().audienceForFocusChange += self; + self.canvas().audienceForFocusChange += this; #ifdef WIN32 if(self.isFullScreen()) @@ -183,6 +208,76 @@ DENG2_PIMPL(ClientWindow) DD_FinishInitializationAfterWindowReady(); } + + void keyEvent(KeyEventSource::KeyState state, int ddKey, int nativeCode, String const &text) + { + /** + * @todo Input drivers need to support Unicode text; for now we have to + * submit as Latin1. + */ + Keyboard_Submit(state == KeyEventSource::Pressed? IKE_DOWN : IKE_UP, + ddKey, nativeCode, text.toLatin1()); + } + + void mouseStateChanged(MouseEventSource::State state) + { + Mouse_Trap(state == MouseEventSource::Trapped); + } + +#ifndef WIN32 + void mouseAxisEvent(MouseEventSource::Axis axis, Vector2i const &value) + { + switch(axis) + { + case MouseEventSource::Motion: + Mouse_Qt_SubmitMotion(IMA_POINTER, value.x, value.y); + break; + + case MouseEventSource::Wheel: + Mouse_Qt_SubmitMotion(IMA_WHEEL, value.x, value.y); + break; + + default: + break; + } + } + + void mouseButtonEvent(MouseEventSource::Button button, MouseEventSource::ButtonState state) + { + Mouse_Qt_SubmitButton( + button == MouseEventSource::Left? IMB_LEFT : + button == MouseEventSource::Middle? IMB_MIDDLE : + button == MouseEventSource::Right? IMB_RIGHT : + button == MouseEventSource::XButton1? IMB_EXTRA1 : + button == MouseEventSource::XButton2? IMB_EXTRA2 : IMB_MAXBUTTONS, + state == MouseEventSource::Pressed); + } +#endif // !WIN32 + + void canvasFocusChanged(Canvas &canvas, bool hasFocus) + { + LOG_DEBUG("canvasFocusChanged focus:%b fullscreen:%b hidden:%b minimized:%b") + << hasFocus << self.isFullScreen() << self.isHidden() << self.isMinimized(); + + if(!hasFocus) + { + DD_ClearEvents(); + I_ResetAllDevices(); + canvas.trapMouse(false); + } + else if(self.isFullScreen()) + { + // Trap the mouse again in fullscreen mode. + canvas.trapMouse(); + } + + // Generate an event about this. + ddevent_t ev; + ev.type = E_FOCUS; + ev.focus.gained = hasFocus; + ev.focus.inWindow = 1; /// @todo Ask WindowSystem for an identifier number. + DD_PostEvent(&ev); + } }; ClientWindow::ClientWindow(String const &id) @@ -274,31 +369,6 @@ void ClientWindow::canvasGLResized(Canvas &canvas) d->busyRoot.setViewSize(size); } -void ClientWindow::canvasFocusChanged(Canvas &canvas, bool hasFocus) -{ - LOG_DEBUG("canvasFocusChanged focus:%b fullscreen:%b hidden:%b minimized:%b") - << hasFocus << isFullScreen() << isHidden() << isMinimized(); - - if(!hasFocus) - { - DD_ClearEvents(); - I_ResetAllDevices(); - canvas.trapMouse(false); - } - else if(isFullScreen()) - { - // Trap the mouse again in fullscreen mode. - canvas.trapMouse(); - } - - // Generate an event about this. - ddevent_t ev; - ev.type = E_FOCUS; - ev.focus.gained = hasFocus; - ev.focus.inWindow = 1; /// @todo Ask WindowSystem for an identifier number. - DD_PostEvent(&ev); -} - bool ClientWindow::setDefaultGLFormat() // static { LOG_AS("DefaultGLFormat"); diff --git a/doomsday/client/src/ui/mouse_qt.cpp b/doomsday/client/src/ui/mouse_qt.cpp index 7cafd65af3..fceb8c5def 100644 --- a/doomsday/client/src/ui/mouse_qt.cpp +++ b/doomsday/client/src/ui/mouse_qt.cpp @@ -29,9 +29,11 @@ #include "ui/mouse_qt.h" #include +#include #include #include #include +#include typedef struct clicker_s { int down; // Count for down events. @@ -42,6 +44,7 @@ typedef struct clicker_s { static struct { int dx, dy; } mouseDelta[IMA_MAXAXES]; static clicker_t mouseClickers[IMB_MAXBUTTONS]; static bool mouseTrapped = false; +static bool cursorHidden = false; static QPoint prevMousePos; static int Mouse_Qt_Init(void) @@ -49,6 +52,7 @@ static int Mouse_Qt_Init(void) memset(&mouseDelta, 0, sizeof(mouseDelta)); memset(&mouseClickers, 0, sizeof(mouseClickers)); mouseTrapped = false; + cursorHidden = false; prevMousePos = QPoint(); return true; } @@ -112,10 +116,59 @@ static void Mouse_Qt_GetState(mousestate_t *state) } } +static void Mouse_Qt_ShowCursor(bool yes) +{ + de::Canvas &canvas = WindowSystem::main().canvas(); + + LOG_DEBUG("%s cursor (presently visible? %b)") + << (yes? "showing" : "hiding") << !cursorHidden; + + if(!yes && !cursorHidden) + { + cursorHidden = true; + canvas.setCursor(QCursor(Qt::BlankCursor)); + qApp->setOverrideCursor(QCursor(Qt::BlankCursor)); + } + else if(yes && cursorHidden) + { + cursorHidden = false; + qApp->restoreOverrideCursor(); + canvas.setCursor(QCursor(Qt::ArrowCursor)); // Default cursor. + } +} + +static void Mouse_Qt_InitTrap() +{ + de::Canvas &canvas = WindowSystem::main().canvas(); + + QCursor::setPos(canvas.mapToGlobal(canvas.rect().center())); + canvas.grabMouse(); + + Mouse_Qt_ShowCursor(false); +} + +static void Mouse_Qt_DeinitTrap() +{ + WindowSystem::main().canvas().releaseMouse(); + + Mouse_Qt_ShowCursor(true); +} + static void Mouse_Qt_Trap(boolean enabled) { + if(mouseTrapped == enabled) return; + mouseTrapped = enabled; prevMousePos = QPoint(); + + if(enabled) + { + Mouse_Qt_InitTrap(); + } + else + { + Mouse_Qt_DeinitTrap(); + } } void Mouse_Qt_SubmitButton(int button, boolean isDown) diff --git a/doomsday/libgui/include/de/gui/canvas.h b/doomsday/libgui/include/de/gui/canvas.h index a146874146..6394ac24fc 100644 --- a/doomsday/libgui/include/de/gui/canvas.h +++ b/doomsday/libgui/include/de/gui/canvas.h @@ -126,11 +126,6 @@ class Canvas : public QGLWidget, public KeyEventSource, public MouseEventSource */ bool isMouseTrapped() const; - /** - * Determines if the mouse cursor is currently visible or not. - */ - bool isCursorVisible() const; - /** * Replaces the current audiences of this canvas with another canvas's * audiences. diff --git a/doomsday/libgui/src/canvas.cpp b/doomsday/libgui/src/canvas.cpp index b2f6d1e3fd..db5e0fbd43 100644 --- a/doomsday/libgui/src/canvas.cpp +++ b/doomsday/libgui/src/canvas.cpp @@ -44,7 +44,6 @@ DENG2_PIMPL(Canvas) CanvasWindow *parent; bool readyNotified; Vector2i currentSize; - bool cursorHidden; bool mouseGrabbed; #ifdef WIN32 bool altIsDown; @@ -57,7 +56,6 @@ DENG2_PIMPL(Canvas) : Base(i), parent(parentWindow), readyNotified(false), - cursorHidden(false), mouseGrabbed(false) { wheelDir[0] = wheelDir[1] = 0; @@ -66,46 +64,21 @@ DENG2_PIMPL(Canvas) #endif } - void showCursor(bool yes) - { - LOG_DEBUG("%s cursor (presently visible? %b)") - << (yes? "showing" : "hiding") << !cursorHidden; - - if(!yes && !cursorHidden) - { - cursorHidden = true; - self.setCursor(QCursor(Qt::BlankCursor)); - qApp->setOverrideCursor(QCursor(Qt::BlankCursor)); - } - else if(yes && cursorHidden) - { - cursorHidden = false; - qApp->restoreOverrideCursor(); - self.setCursor(QCursor(Qt::ArrowCursor)); // Default cursor. - } - } - void grabMouse() { if(!self.isVisible()) return; LOG_DEBUG("grabbing mouse (already grabbed? %b)") << mouseGrabbed; - if(mouseGrabbed) return; - - mouseGrabbed = true; - - DENG2_FOR_PUBLIC_AUDIENCE(MouseStateChange, i) + if(!mouseGrabbed) { - i->mouseStateChanged(Trapped); - } + mouseGrabbed = true; -#ifndef WIN32 - // Start tracking the mouse now. - QCursor::setPos(self.mapToGlobal(self.rect().center())); - self.grabMouse(); - showCursor(false); -#endif + DENG2_FOR_PUBLIC_AUDIENCE(MouseStateChange, i) + { + i->mouseStateChanged(Trapped); + } + } } void ungrabMouse() @@ -114,19 +87,15 @@ DENG2_PIMPL(Canvas) LOG_DEBUG("ungrabbing mouse (presently grabbed? %b)") << mouseGrabbed; - if(!mouseGrabbed) return; - -#ifndef WIN32 - self.releaseMouse(); - showCursor(true); -#endif - // Tell the mouse driver that the mouse is untrapped. - mouseGrabbed = false; - //Mouse_Trap(false); - - DENG2_FOR_PUBLIC_AUDIENCE(MouseStateChange, i) + if(mouseGrabbed) { - i->mouseStateChanged(Untrapped); + // Tell the mouse driver that the mouse is untrapped. + mouseGrabbed = false; + + DENG2_FOR_PUBLIC_AUDIENCE(MouseStateChange, i) + { + i->mouseStateChanged(Untrapped); + } } } @@ -245,11 +214,6 @@ bool Canvas::isMouseTrapped() const return d->mouseGrabbed; } -bool Canvas::isCursorVisible() const -{ - return !d->cursorHidden; -} - void Canvas::copyAudiencesFrom(Canvas const &other) { audienceForGLReady = other.audienceForGLReady;