Skip to content

Commit 1d872db

Browse files
authored
feat: expand game controller bindings (#319)
1 parent 321da3d commit 1d872db

File tree

4 files changed

+125
-16
lines changed

4 files changed

+125
-16
lines changed

ChangeLog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
2.5.X.Y
22
=======
33

4+
* Added game controller helper functions: `isGameController`, `mkControllerDevice`, `mkControllerDevice'`, `controllerFromInstanceID`
5+
* Added raw bindings for player index, LED control, rumble triggers, and touchpad query functions
6+
* Added `JoystickIndex` type alias for clarity
7+
* `getControllerID` now returns `Raw.JoystickID` for improved type documentation
8+
* Fixed `controllerDeviceEventWhich` documentation to clarify index vs instance ID semantics
9+
410
2.5.5.1
511
=======
612

src/SDL/Event.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ data ControllerDeviceEventData =
428428
ControllerDeviceEventData {controllerDeviceEventConnection :: !ControllerDeviceConnection
429429
-- ^ Was the device added, removed, or remapped?
430430
,controllerDeviceEventWhich :: !Raw.JoystickID
431-
-- ^ The joystick instance ID that reported the event.
431+
-- ^ The joystick device index for the ADDED event, instance id for the REMOVED or REMAPPED event
432432
}
433433
deriving (Eq,Ord,Generic,Show,Typeable)
434434

src/SDL/Input/GameController.hs

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,15 @@
77

88
module SDL.Input.GameController
99
( ControllerDevice (..)
10+
, GameController
11+
, JoystickIndex
12+
, Raw.JoystickID
13+
14+
, isGameController
15+
, mkControllerDevice
16+
, mkControllerDevice'
17+
, controllerFromInstanceID
1018
, availableControllers
11-
1219
, openController
1320
, closeController
1421
, controllerAttached
@@ -29,12 +36,12 @@ module SDL.Input.GameController
2936
, ControllerDeviceConnection (..)
3037
) where
3138

32-
import Control.Monad (filterM)
39+
import Control.Monad (guard)
3340
import Control.Monad.IO.Class (MonadIO, liftIO)
41+
import Control.Monad.Trans.Maybe (runMaybeT)
3442
import Data.Data (Data)
3543
import Data.Int
3644
import Data.Text (Text)
37-
import Data.Traversable (for)
3845
import Data.Typeable
3946
import Data.Word
4047
import Foreign.C (withCString)
@@ -60,26 +67,61 @@ import qualified Data.Vector as V
6067
import Control.Applicative
6168
#endif
6269

70+
type JoystickIndex = CInt
71+
6372
{- | A description of game controller that can be opened using 'openController'.
6473
To retrieve a list of connected game controllers, use 'availableControllers'.
6574
-}
6675
data ControllerDevice = ControllerDevice
6776
{ gameControllerDeviceName :: Text
68-
, gameControllerDeviceId :: CInt
77+
, gameControllerDeviceId :: JoystickIndex
6978
}
7079
deriving (Eq, Generic, Read, Ord, Show, Typeable)
7180

81+
82+
{- | Check if the given joystick is supported by the game controller interface.
83+
84+
See @<https://wiki.libsdl.org/SDL2/SDL_IsGameController SDL_IsGameController>@ for C documentation.
85+
-}
86+
isGameController :: MonadIO m => JoystickIndex -> m Bool
87+
isGameController = Raw.isGameController
88+
89+
{- | Create a 'ControllerDevice' from a 'JoystickIndex'. Returns 'Nothing' if
90+
the 'JoystickIndex' does not support the game controller interface.
91+
-}
92+
mkControllerDevice :: MonadIO m => JoystickIndex -> m (Maybe ControllerDevice)
93+
mkControllerDevice i = runMaybeT $ do
94+
isGC <- isGameController i
95+
guard isGC
96+
mkControllerDevice' i
97+
98+
{- | Create a 'ControllerDevice' from a 'JoystickIndex'. Does not check whether
99+
the 'JoystickIndex' supports the game controller interface.
100+
-}
101+
mkControllerDevice' :: MonadIO m => JoystickIndex -> m ControllerDevice
102+
mkControllerDevice' i = do
103+
cstr <- liftIO $
104+
throwIfNull "SDL.Input.GameController.mkControllerDevice'" "SDL_GameControllerNameForIndex" $
105+
Raw.gameControllerNameForIndex (fromIntegral i)
106+
name <- liftIO $ Text.decodeUtf8 <$> BS.packCString cstr
107+
return (ControllerDevice name i)
108+
109+
{- | Get the 'GameController' associated with a 'Raw.JoystickID'.
110+
111+
See @<https://wiki.libsdl.org/SDL2/SDL_GameControllerFromInstanceID SDL_GameControllerFromInstanceID>@ for C documentation.
112+
-}
113+
controllerFromInstanceID :: MonadIO m => Raw.JoystickID -> m GameController
114+
controllerFromInstanceID i =
115+
fmap GameController $
116+
throwIfNull "SDL.Input.GameController.controllerFromInstanceID" "SDL_GameControllerFromInstanceID" $
117+
Raw.gameControllerFromInstanceID (fromIntegral i)
118+
119+
72120
-- | Enumerate all connected Controllers, retrieving a description of each.
73121
availableControllers :: MonadIO m => m (V.Vector ControllerDevice)
74122
availableControllers = liftIO $ do
75-
n <- numJoysticks
76-
indices <- filterM Raw.isGameController [0 .. (n - 1)]
77-
fmap V.fromList $ for indices $ \i -> do
78-
cstr <-
79-
throwIfNull "SDL.Input.Controller.availableGameControllers" "SDL_GameControllerNameForIndex" $
80-
Raw.gameControllerNameForIndex i
81-
name <- Text.decodeUtf8 <$> BS.packCString cstr
82-
return (ControllerDevice name i)
123+
n <- fromIntegral <$> numJoysticks
124+
V.catMaybes <$> V.generateM n (mkControllerDevice . fromIntegral)
83125

84126
{- | Open a controller so that you can start receiving events from interaction with this controller.
85127
@@ -90,10 +132,10 @@ openController
90132
=> ControllerDevice
91133
-- ^ The device to open. Use 'availableControllers' to find 'JoystickDevices's
92134
-> m GameController
93-
openController (ControllerDevice _ x) =
135+
openController (ControllerDevice _ i) =
94136
fmap GameController $
95137
throwIfNull "SDL.Input.GameController.openController" "SDL_GameControllerOpen" $
96-
Raw.gameControllerOpen x
138+
Raw.gameControllerOpen (fromIntegral i)
97139

98140
{- | Close a controller previously opened with 'openController'.
99141
@@ -114,7 +156,7 @@ controllerAttached (GameController c) = Raw.gameControllerGetAttached c
114156
115157
See @<https://wiki.libsdl.org/SDL2/SDL_GameControllerInstanceID SDL_GameControllerInstanceID>@ for C documentation.
116158
-}
117-
getControllerID :: MonadIO m => GameController -> m Int32
159+
getControllerID :: MonadIO m => GameController -> m Raw.JoystickID
118160
getControllerID (GameController c) =
119161
throwIfNeg "SDL.Input.GameController.getControllerID" "SDL_JoystickInstanceID" $
120162
Raw.joystickInstanceID c

src/SDL/Raw/Event.hs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ module SDL.Raw.Event (
9696
gameControllerClose,
9797
gameControllerEventState,
9898
gameControllerFromInstanceID,
99+
gameControllerFromPlayerIndex,
99100
gameControllerGetAttached,
100101
gameControllerGetAxis,
101102
gameControllerGetAxisFromString,
@@ -104,6 +105,12 @@ module SDL.Raw.Event (
104105
gameControllerGetButton,
105106
gameControllerGetButtonFromString,
106107
gameControllerGetJoystick,
108+
gameControllerGetNumTouchpadFingers,
109+
gameControllerGetNumTouchpads,
110+
gameControllerGetPlayerIndex,
111+
gameControllerHasLED,
112+
gameControllerHasRumble,
113+
gameControllerHasRumbleTriggers,
107114
gameControllerGetStringForAxis,
108115
gameControllerGetStringForButton,
109116
gameControllerMapping,
@@ -113,6 +120,9 @@ module SDL.Raw.Event (
113120
gameControllerOpen,
114121
gameControllerUpdate,
115122
gameControllerRumble,
123+
gameControllerRumbleTriggers,
124+
gameControllerSetLED,
125+
gameControllerSetPlayerIndex,
116126
isGameController,
117127
eventBuffer,
118128
eventBufferSize
@@ -220,6 +230,7 @@ foreign import ccall "SDL.h SDL_GameControllerAddMappingsFromRW" gameControllerA
220230
foreign import ccall "SDL.h SDL_GameControllerClose" gameControllerCloseFFI :: GameController -> IO ()
221231
foreign import ccall "SDL.h SDL_GameControllerEventState" gameControllerEventStateFFI :: CInt -> IO CInt
222232
foreign import ccall "SDL.h SDL_GameControllerFromInstanceID" gameControllerFromInstanceIDFFI :: JoystickID -> IO GameController
233+
foreign import ccall "SDL.h SDL_GameControllerFromPlayerIndex" gameControllerFromPlayerIndexFFI :: CInt -> IO GameController
223234
foreign import ccall "SDL.h SDL_GameControllerGetAttached" gameControllerGetAttachedFFI :: GameController -> IO Bool
224235
foreign import ccall "SDL.h SDL_GameControllerGetAxis" gameControllerGetAxisFFI :: GameController -> GameControllerAxis -> IO Int16
225236
foreign import ccall "SDL.h SDL_GameControllerGetAxisFromString" gameControllerGetAxisFromStringFFI :: CString -> IO GameControllerAxis
@@ -228,6 +239,12 @@ foreign import ccall "sdlhelper.h SDLHelper_GameControllerGetBindForButton" game
228239
foreign import ccall "SDL.h SDL_GameControllerGetButton" gameControllerGetButtonFFI :: GameController -> GameControllerButton -> IO Word8
229240
foreign import ccall "SDL.h SDL_GameControllerGetButtonFromString" gameControllerGetButtonFromStringFFI :: CString -> IO GameControllerButton
230241
foreign import ccall "SDL.h SDL_GameControllerGetJoystick" gameControllerGetJoystickFFI :: GameController -> IO Joystick
242+
foreign import ccall "SDL.h SDL_GameControllerGetNumTouchpadFingers" gameControllerGetNumTouchpadFingersFFI :: GameController -> CInt -> IO CInt
243+
foreign import ccall "SDL.h SDL_GameControllerGetNumTouchpads" gameControllerGetNumTouchpadsFFI :: GameController -> IO CInt
244+
foreign import ccall "SDL.h SDL_GameControllerGetPlayerIndex" gameControllerGetPlayerIndexFFI :: GameController -> IO CInt
245+
foreign import ccall "SDL.h SDL_GameControllerHasLED" gameControllerHasLEDFFI :: GameController -> IO Bool
246+
foreign import ccall "SDL.h SDL_GameControllerHasRumble" gameControllerHasRumbleFFI :: GameController -> IO Bool
247+
foreign import ccall "SDL.h SDL_GameControllerHasRumbleTriggers" gameControllerHasRumbleTriggersFFI :: GameController -> IO Bool
231248
foreign import ccall "SDL.h SDL_GameControllerGetStringForAxis" gameControllerGetStringForAxisFFI :: GameControllerAxis -> IO CString
232249
foreign import ccall "SDL.h SDL_GameControllerGetStringForButton" gameControllerGetStringForButtonFFI :: GameControllerButton -> IO CString
233250
foreign import ccall "SDL.h SDL_GameControllerMapping" gameControllerMappingFFI :: GameController -> IO CString
@@ -237,6 +254,9 @@ foreign import ccall "SDL.h SDL_GameControllerNameForIndex" gameControllerNameFo
237254
foreign import ccall "SDL.h SDL_GameControllerOpen" gameControllerOpenFFI :: CInt -> IO GameController
238255
foreign import ccall "SDL.h SDL_GameControllerUpdate" gameControllerUpdateFFI :: IO ()
239256
foreign import ccall "SDL.h SDL_GameControllerRumble" gameControllerRumbleFFI :: GameController -> CUShort -> CUShort -> CUInt -> IO CInt
257+
foreign import ccall "SDL.h SDL_GameControllerRumbleTriggers" gameControllerRumbleTriggersFFI :: GameController -> CUShort -> CUShort -> CUInt -> IO CInt
258+
foreign import ccall "SDL.h SDL_GameControllerSetLED" gameControllerSetLEDFFI :: GameController -> Word8 -> Word8 -> Word8 -> IO CInt
259+
foreign import ccall "SDL.h SDL_GameControllerSetPlayerIndex" gameControllerSetPlayerIndexFFI :: GameController -> CInt -> IO ()
240260
foreign import ccall "SDL.h SDL_IsGameController" isGameControllerFFI :: CInt -> IO Bool
241261

242262
foreign import ccall "sdlhelper.c SDLHelper_GetEventBufferSize" eventBufferSize :: CInt
@@ -607,6 +627,10 @@ gameControllerFromInstanceID :: MonadIO m => JoystickID -> m GameController
607627
gameControllerFromInstanceID v1 = liftIO $ gameControllerFromInstanceIDFFI v1
608628
{-# INLINE gameControllerFromInstanceID #-}
609629

630+
gameControllerFromPlayerIndex :: MonadIO m => CInt -> m GameController
631+
gameControllerFromPlayerIndex v1 = liftIO $ gameControllerFromPlayerIndexFFI v1
632+
{-# INLINE gameControllerFromPlayerIndex #-}
633+
610634
gameControllerGetAttached :: MonadIO m => GameController -> m Bool
611635
gameControllerGetAttached v1 = liftIO $ gameControllerGetAttachedFFI v1
612636
{-# INLINE gameControllerGetAttached #-}
@@ -643,6 +667,31 @@ gameControllerGetJoystick :: MonadIO m => GameController -> m Joystick
643667
gameControllerGetJoystick v1 = liftIO $ gameControllerGetJoystickFFI v1
644668
{-# INLINE gameControllerGetJoystick #-}
645669

670+
gameControllerGetNumTouchpadFingers :: MonadIO m => GameController -> CInt -> m CInt
671+
gameControllerGetNumTouchpadFingers gamecontroller touchpad =
672+
liftIO $ gameControllerGetNumTouchpadFingersFFI gamecontroller touchpad
673+
{-# INLINE gameControllerGetNumTouchpadFingers #-}
674+
675+
gameControllerGetNumTouchpads :: MonadIO m => GameController -> m CInt
676+
gameControllerGetNumTouchpads gamecontroller = liftIO $ gameControllerGetNumTouchpadsFFI gamecontroller
677+
{-# INLINE gameControllerGetNumTouchpads #-}
678+
679+
gameControllerGetPlayerIndex :: MonadIO m => GameController -> m CInt
680+
gameControllerGetPlayerIndex gamecontroller = liftIO $ gameControllerGetPlayerIndexFFI gamecontroller
681+
{-# INLINE gameControllerGetPlayerIndex #-}
682+
683+
gameControllerHasLED :: MonadIO m => GameController -> m Bool
684+
gameControllerHasLED gamecontroller = liftIO $ gameControllerHasLEDFFI gamecontroller
685+
{-# INLINE gameControllerHasLED #-}
686+
687+
gameControllerHasRumble :: MonadIO m => GameController -> m Bool
688+
gameControllerHasRumble gamecontroller = liftIO $ gameControllerHasRumbleFFI gamecontroller
689+
{-# INLINE gameControllerHasRumble #-}
690+
691+
gameControllerHasRumbleTriggers :: MonadIO m => GameController -> m Bool
692+
gameControllerHasRumbleTriggers gamecontroller = liftIO $ gameControllerHasRumbleTriggersFFI gamecontroller
693+
{-# INLINE gameControllerHasRumbleTriggers #-}
694+
646695
gameControllerGetStringForAxis :: MonadIO m => GameControllerAxis -> m CString
647696
gameControllerGetStringForAxis v1 = liftIO $ gameControllerGetStringForAxisFFI v1
648697
{-# INLINE gameControllerGetStringForAxis #-}
@@ -681,6 +730,18 @@ gameControllerRumble :: MonadIO m => GameController -> CUShort -> CUShort -> CUI
681730
gameControllerRumble v1 v2 v3 v4 = liftIO $ gameControllerRumbleFFI v1 v2 v3 v4
682731
{-# INLINE gameControllerRumble #-}
683732

733+
gameControllerRumbleTriggers :: MonadIO m => GameController -> CUShort -> CUShort -> CUInt -> m CInt
734+
gameControllerRumbleTriggers gamecontroller v1 v2 v3 = liftIO $ gameControllerRumbleTriggersFFI gamecontroller v1 v2 v3
735+
{-# INLINE gameControllerRumbleTriggers #-}
736+
737+
gameControllerSetLED :: MonadIO m => GameController -> Word8 -> Word8 -> Word8 -> m CInt
738+
gameControllerSetLED gamecontroller v1 v2 v3 = liftIO $ gameControllerSetLEDFFI gamecontroller v1 v2 v3
739+
{-# INLINE gameControllerSetLED #-}
740+
741+
gameControllerSetPlayerIndex :: MonadIO m => GameController -> CInt -> m ()
742+
gameControllerSetPlayerIndex gamecontroller v1 = liftIO $ gameControllerSetPlayerIndexFFI gamecontroller v1
743+
{-# INLINE gameControllerSetPlayerIndex #-}
744+
684745
isGameController :: MonadIO m => CInt -> m Bool
685746
isGameController v1 = liftIO $ isGameControllerFFI v1
686747
{-# INLINE isGameController #-}

0 commit comments

Comments
 (0)