From 58b3c20b86d2169ea923f36b4288e66efeb3eaeb Mon Sep 17 00:00:00 2001
From: Sam Hocevar <sam@hocevar.net>
Date: Sun, 6 May 2012 00:49:16 +0000
Subject: [PATCH] input: add core joystick support and bind the SDL input to
 that.

---
 src/Makefile.am               |  5 +-
 src/camera.cpp                | 23 +++++---
 src/core.h                    |  3 +-
 src/{ => input}/input.cpp     | 36 ++++++++++++-
 src/{ => input}/input.h       | 18 +++++--
 src/input/stick.cpp           | 99 +++++++++++++++++++++++++++++++++++
 src/input/stick.h             | 50 ++++++++++++++++++
 src/platform/sdl/sdlinput.cpp | 51 ++++++++++++++----
 src/platform/sdl/sdlinput.h   |  2 +-
 win32/lolcore.vcxproj         |  6 ++-
 win32/lolcore.vcxproj.filters | 21 +++++---
 11 files changed, 283 insertions(+), 31 deletions(-)
 rename src/{ => input}/input.cpp (85%)
 rename src/{ => input}/input.h (76%)
 create mode 100644 src/input/stick.cpp
 create mode 100644 src/input/stick.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 905158d2..87b7a4c2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -6,7 +6,7 @@ liblol_a_SOURCES = \
     audio.cpp audio.h scene.cpp scene.h font.cpp font.h layer.cpp layer.h \
     map.cpp map.h entity.cpp entity.h ticker.cpp ticker.h lolgl.h \
     tileset.cpp tileset.h forge.cpp forge.h video.cpp video.h log.cpp log.h \
-    timer.cpp timer.h bitfield.h profiler.cpp profiler.h input.h input.cpp \
+    timer.cpp timer.h bitfield.h profiler.cpp profiler.h \
     world.cpp world.h sample.cpp sample.h sampler.cpp sampler.h \
     text.cpp text.h emitter.cpp emitter.h numeric.h hash.cpp hash.h \
     worldentity.cpp worldentity.h gradient.cpp gradient.h \
@@ -28,6 +28,9 @@ liblol_a_SOURCES = \
     \
     math/vector.cpp math/real.cpp math/half.cpp math/trig.cpp math/trig.h \
     \
+    input/input.cpp input/input.h \
+    input/stick.cpp input/stick.h \
+    \
     gpu/shader.cpp gpu/shader.h \
     gpu/indexbuffer.cpp gpu/indexbuffer.h \
     gpu/vertexbuffer.cpp gpu/vertexbuffer.h \
diff --git a/src/camera.cpp b/src/camera.cpp
index e7639de9..60d1f884 100644
--- a/src/camera.cpp
+++ b/src/camera.cpp
@@ -57,12 +57,23 @@ void Camera::TickGame(float seconds)
 {
     WorldEntity::TickGame(seconds);
 
-    int updown = Input::GetButtonState(273 /*SDLK_UP*/)
-               - Input::GetButtonState(274 /*SDLK_DOWN*/);
-    int rightleft = Input::GetButtonState(275 /*SDLK_RIGHT*/)
-                  - Input::GetButtonState(276 /*SDLK_LEFT*/);
-    int pgupdown = Input::GetButtonState(280 /*SDLK_PAGEUP*/)
-                 - Input::GetButtonState(281 /*SDLK_PAGEDOWN*/);
+    /* Hackish keyboard support */
+    float updown = Input::GetButtonState(273 /*SDLK_UP*/)
+                 - Input::GetButtonState(274 /*SDLK_DOWN*/);
+    float rightleft = Input::GetButtonState(275 /*SDLK_RIGHT*/)
+                    - Input::GetButtonState(276 /*SDLK_LEFT*/);
+    float pgupdown = Input::GetButtonState(280 /*SDLK_PAGEUP*/)
+                   - Input::GetButtonState(281 /*SDLK_PAGEDOWN*/);
+
+    /* Hackish stick support */
+    static Stick *stick = NULL;
+    if (!stick)
+        stick = Input::TrackStick();
+    if (stick && stick->GetAxisCount() >= 2)
+    {
+        rightleft = 2.f * stick->GetAxis(0) * std::abs(stick->GetAxis(0));
+        updown = -2.f * stick->GetAxis(1) * std::abs(stick->GetAxis(1));
+    }
 
     m_position += vec3(rightleft, pgupdown, -updown) * 200.f * seconds;
     m_target += vec3(rightleft, 0, -updown) * 200.f * seconds;
diff --git a/src/core.h b/src/core.h
index 2789ba17..d7cd60bf 100644
--- a/src/core.h
+++ b/src/core.h
@@ -78,7 +78,8 @@ static inline int isnan(float f)
 #include "video.h"
 #include "audio.h"
 #include "scene.h"
-#include "input.h"
+#include "input/input.h"
+#include "input/stick.h"
 #include "profiler.h"
 
 // Entities
diff --git a/src/input.cpp b/src/input/input.cpp
similarity index 85%
rename from src/input.cpp
rename to src/input/input.cpp
index 4cc7d054..2f6a51bc 100644
--- a/src/input.cpp
+++ b/src/input/input.cpp
@@ -1,7 +1,7 @@
 //
 // Lol Engine
 //
-// Copyright: (c) 2010-2011 Sam Hocevar <sam@hocevar.net>
+// Copyright: (c) 2010-2012 Sam Hocevar <sam@hocevar.net>
 //   This program is free software; you can redistribute it and/or
 //   modify it under the terms of the Do What The Fuck You Want To
 //   Public License, Version 2, as published by Sam Hocevar. See
@@ -48,6 +48,8 @@ private:
     WorldEntity *entities[MAX_ENTITIES];
     int nentities;
     WorldEntity *lastfocus;
+
+    Array<Stick *> m_sticks;
 }
 inputdata;
 
@@ -57,6 +59,7 @@ static InputData * const data = &inputdata;
  * Public Input class
  */
 
+#if 0
 vec2 Input::GetAxis(int axis)
 {
     vec2 ret;
@@ -80,6 +83,7 @@ vec2 Input::GetAxis(int axis)
 
     return ret;
 }
+#endif
 
 ivec2 Input::GetMousePos()
 {
@@ -194,5 +198,35 @@ void Input::UnsetMouseButton(int index)
     }
 }
 
+Stick *Input::CreateStick()
+{
+    Stick *stick = new Stick();
+    Ticker::Ref(stick);
+    data->m_sticks.Push(stick);
+    return stick;
+}
+
+void Input::DestroyStick(Stick *stick)
+{
+    for (int i = 0; i < data->m_sticks.Count(); i++)
+        if (data->m_sticks[i] == stick)
+            data->m_sticks.Remove(i);
+    Ticker::Unref(stick);
+}
+
+Stick *Input::TrackStick()
+{
+    /* FIXME: add the possibility to choose amongst sticks */
+    if (!data->m_sticks.Count())
+        return NULL;
+    Ticker::Ref(data->m_sticks[0]);
+    return data->m_sticks[0];
+}
+
+void Input::UntrackStick(Stick *stick)
+{
+    Ticker::Unref(stick);
+}
+
 } /* namespace lol */
 
diff --git a/src/input.h b/src/input/input.h
similarity index 76%
rename from src/input.h
rename to src/input/input.h
index e728c921..86652fc1 100644
--- a/src/input.h
+++ b/src/input/input.h
@@ -13,10 +13,11 @@
 // ----------------------
 //
 
-#if !defined __LOL_INPUT_H__
-#define __LOL_INPUT_H__
+#if !defined __LOL_INPUT_INPUT_H__
+#define __LOL_INPUT_INPUT_H__
 
 #include "lol/math/vector.h"
+#include "input/stick.h"
 
 namespace lol
 {
@@ -27,7 +28,6 @@ class Input
 {
 public:
     /* These methods are general queries */
-    static vec2 GetAxis(int axis);
     static ivec2 GetMousePos();
     static ivec3 GetMouseButtons();
     //BH : Added this, is a v0.1 Alpha version.
@@ -41,9 +41,19 @@ public:
     static void SetMousePos(ivec2 coord);
     static void SetMouseButton(int index);
     static void UnsetMouseButton(int index);
+
+    /*
+     * Joystick handling
+     */
+
+    static Stick *CreateStick();
+    static void DestroyStick(Stick *stick);
+
+    static Stick *TrackStick();
+    static void UntrackStick(Stick *stick);
 };
 
 } /* namespace lol */
 
-#endif // __LOL_INPUT_H__
+#endif // __LOL_INPUT_INPUT_H__
 
diff --git a/src/input/stick.cpp b/src/input/stick.cpp
new file mode 100644
index 00000000..1b7cd1b6
--- /dev/null
+++ b/src/input/stick.cpp
@@ -0,0 +1,99 @@
+//
+// Lol Engine
+//
+// Copyright: (c) 2010-2011 Sam Hocevar <sam@hocevar.net>
+//   This program is free software; you can redistribute it and/or
+//   modify it under the terms of the Do What The Fuck You Want To
+//   Public License, Version 2, as published by Sam Hocevar. See
+//   http://sam.zoy.org/projects/COPYING.WTFPL for more details.
+//
+
+#if defined HAVE_CONFIG_H
+#   include "config.h"
+#endif
+
+#include <cstdlib>
+#include <cmath>
+
+#include "core.h"
+
+namespace lol
+{
+
+/*
+ * Stick implementation class
+ */
+
+static class StickData
+{
+    friend class Stick;
+
+public:
+    StickData() { }
+
+private:
+    Array<float> m_axes;
+    Array<int> m_buttons;
+}
+stickdata;
+
+/*
+ * Public Stick class
+ */
+
+Stick::Stick()
+  : m_data(new StickData())
+{
+}
+
+Stick::~Stick()
+{
+    delete m_data;
+}
+
+void Stick::SetAxisCount(int n)
+{
+    m_data->m_axes.Empty();
+    for (int i = 0; i < n; i++)
+        m_data->m_axes.Push(0.f);
+}
+
+void Stick::SetButtonCount(int n)
+{
+    m_data->m_buttons.Empty();
+    for (int i = 0; i < n; i++)
+        m_data->m_buttons.Push(0);
+}
+
+void Stick::SetAxis(int n, float val)
+{
+    m_data->m_axes[n] = val;
+}
+
+void Stick::SetButton(int n, int val)
+{
+    m_data->m_buttons[n] = val;
+}
+
+int Stick::GetAxisCount()
+{
+    return m_data->m_axes.Count();
+}
+
+int Stick::GetButtonCount()
+{
+    return m_data->m_buttons.Count();
+}
+
+float Stick::GetAxis(int n)
+{
+    return m_data->m_axes[n];
+}
+
+int Stick::GetButton(int n)
+{
+    return m_data->m_buttons[n];
+}
+
+} /* namespace lol */
+
diff --git a/src/input/stick.h b/src/input/stick.h
new file mode 100644
index 00000000..a1ebed3f
--- /dev/null
+++ b/src/input/stick.h
@@ -0,0 +1,50 @@
+//
+// Lol Engine
+//
+// Copyright: (c) 2010-2012 Sam Hocevar <sam@hocevar.net>
+//   This program is free software; you can redistribute it and/or
+//   modify it under the terms of the Do What The Fuck You Want To
+//   Public License, Version 2, as published by Sam Hocevar. See
+//   http://sam.zoy.org/projects/COPYING.WTFPL for more details.
+//
+
+//
+// The Stick class
+// ---------------
+//
+
+#if !defined __LOL_INPUT_STICK_H__
+#define __LOL_INPUT_STICK_H__
+
+#include "entity.h"
+
+namespace lol
+{
+
+class StickData;
+
+class Stick : public Entity
+{
+    friend class Input;
+
+public:
+    void SetAxisCount(int n);
+    void SetButtonCount(int n);
+    void SetAxis(int n, float val);
+    void SetButton(int n, int val);
+
+    int GetAxisCount();
+    int GetButtonCount();
+    float GetAxis(int n);
+    int GetButton(int n);
+
+private:
+    Stick();
+    ~Stick();
+    StickData *m_data;
+};
+
+} /* namespace lol */
+
+#endif // __LOL_INPUT_STICK_H__
+
diff --git a/src/platform/sdl/sdlinput.cpp b/src/platform/sdl/sdlinput.cpp
index 9bd8ba2b..5f3599b2 100644
--- a/src/platform/sdl/sdlinput.cpp
+++ b/src/platform/sdl/sdlinput.cpp
@@ -34,6 +34,9 @@ private:
     void Tick(float seconds);
 
     static ivec2 GetMousePos();
+#if defined USE_SDL
+    Array<SDL_Joystick *, Stick *> m_joysticks;
+#endif
 };
 
 /*
@@ -41,21 +44,47 @@ private:
  */
 
 SdlInput::SdlInput()
-  : data(new SdlInputData())
+  : m_data(new SdlInputData())
 {
 #if defined USE_SDL
-    SDL_Init(SDL_INIT_TIMER);
+    SDL_Init(SDL_INIT_TIMER | SDL_INIT_JOYSTICK);
+
+    /* Register all the joysticks we can find, and let the input
+     * system decide what it wants to track. */
+    SDL_JoystickEventState(SDL_ENABLE);
+    for (int i = 0; i < SDL_NumJoysticks(); i++)
+    {
+        SDL_Joystick *sdlstick = SDL_JoystickOpen(i);
+        Stick *stick = Input::CreateStick();
+        stick->SetAxisCount(SDL_JoystickNumAxes(sdlstick));
+        stick->SetButtonCount(SDL_JoystickNumButtons(sdlstick));
+        m_data->m_joysticks.Push(sdlstick, stick);
+    }
 #endif
 
     m_gamegroup = GAMEGROUP_BEFORE;
 }
 
+SdlInput::~SdlInput()
+{
+#if defined USE_SDL
+    /* Unregister all the joysticks we added */
+    while (m_data->m_joysticks.Count())
+    {
+        SDL_JoystickClose(m_data->m_joysticks[0].m1);
+        Input::DestroyStick(m_data->m_joysticks[0].m2);
+        m_data->m_joysticks.Remove(0);
+    }
+#endif
+    delete m_data;
+}
+
 void SdlInput::TickGame(float seconds)
 {
     Entity::TickGame(seconds);
 
 #if !defined _WIN32
-    data->Tick(seconds);
+    m_data->Tick(seconds);
 #endif
 }
 
@@ -64,7 +93,7 @@ void SdlInput::TickDraw(float seconds)
     Entity::TickDraw(seconds);
 
 #if defined _WIN32
-    data->Tick(seconds);
+    m_data->Tick(seconds);
 #endif
 }
 
@@ -101,6 +130,15 @@ void SdlInputData::Tick(float seconds)
                 Input::UnsetMouseButton(event.button.button - 1);
             break;
         }
+
+        case SDL_JOYAXISMOTION:
+            m_joysticks[event.jaxis.which].m2->SetAxis(event.jaxis.axis, (float)event.jaxis.value / 32768.f);
+            break;
+
+        case SDL_JOYBUTTONUP:
+        case SDL_JOYBUTTONDOWN:
+            m_joysticks[event.jbutton.which].m2->SetButton(event.jbutton.button, event.jbutton.state);
+            break;
         }
     }
 
@@ -114,11 +152,6 @@ void SdlInputData::Tick(float seconds)
 #endif
 }
 
-SdlInput::~SdlInput()
-{
-    delete data;
-}
-
 ivec2 SdlInputData::GetMousePos()
 {
     ivec2 ret(-1, -1);
diff --git a/src/platform/sdl/sdlinput.h b/src/platform/sdl/sdlinput.h
index 34b96922..6518e7fb 100644
--- a/src/platform/sdl/sdlinput.h
+++ b/src/platform/sdl/sdlinput.h
@@ -34,7 +34,7 @@ protected:
     virtual void TickDraw(float seconds);
 
 private:
-    SdlInputData *data;
+    SdlInputData *m_data;
 };
 
 } /* namespace lol */
diff --git a/win32/lolcore.vcxproj b/win32/lolcore.vcxproj
index a0029279..bb7feee1 100644
--- a/win32/lolcore.vcxproj
+++ b/win32/lolcore.vcxproj
@@ -98,7 +98,8 @@
     <ClCompile Include="..\src\image\codec\ps3-image.cpp" />
     <ClCompile Include="..\src\image\codec\sdl-image.cpp" />
     <ClCompile Include="..\src\image\image.cpp" />
-    <ClCompile Include="..\src\input.cpp" />
+    <ClCompile Include="..\src\input\input.cpp" />
+    <ClCompile Include="..\src\input\stick.cpp" />
     <ClCompile Include="..\src\layer.cpp" />
     <ClCompile Include="..\src\log.cpp" />
     <ClCompile Include="..\src\map.cpp" />
@@ -149,7 +150,8 @@
     <ClInclude Include="..\src\hash.h" />
     <ClInclude Include="..\src\image\image-private.h" />
     <ClInclude Include="..\src\image\image.h" />
-    <ClInclude Include="..\src\input.h" />
+    <ClInclude Include="..\src\input\input.h" />
+    <ClInclude Include="..\src\input\stick.h" />
     <ClInclude Include="..\src\layer.h" />
     <ClInclude Include="..\src\log.h" />
     <ClInclude Include="..\src\loldebug.h" />
diff --git a/win32/lolcore.vcxproj.filters b/win32/lolcore.vcxproj.filters
index 5acb9d74..5d18e43a 100644
--- a/win32/lolcore.vcxproj.filters
+++ b/win32/lolcore.vcxproj.filters
@@ -41,6 +41,9 @@
     <Filter Include="src\platform\xbox">
       <UniqueIdentifier>{317cb5cc-5dcc-4e14-be90-40a125a2e2ec}</UniqueIdentifier>
     </Filter>
+    <Filter Include="src\input">
+      <UniqueIdentifier>{94992c0e-ebc5-4185-b766-323b06547dcf}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\src\image\image.cpp">
@@ -100,9 +103,6 @@
     <ClCompile Include="..\src\hash.cpp">
       <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="..\src\input.cpp">
-      <Filter>src</Filter>
-    </ClCompile>
     <ClCompile Include="..\src\layer.cpp">
       <Filter>src</Filter>
     </ClCompile>
@@ -196,6 +196,12 @@
     <ClCompile Include="..\src\gpu\indexbuffer.cpp">
       <Filter>src\gpu</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\input\input.cpp">
+      <Filter>src\input</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\input\stick.cpp">
+      <Filter>src\input</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\src\image\image.h">
@@ -258,9 +264,6 @@
     <ClInclude Include="..\src\hash.h">
       <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="..\src\input.h">
-      <Filter>src</Filter>
-    </ClInclude>
     <ClInclude Include="..\src\layer.h">
       <Filter>src</Filter>
     </ClInclude>
@@ -372,5 +375,11 @@
     <ClInclude Include="..\src\lol\debug.h">
       <Filter>src\lol</Filter>
     </ClInclude>
+    <ClInclude Include="..\src\input\input.h">
+      <Filter>src\input</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\input\stick.h">
+      <Filter>src\input</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>
\ No newline at end of file