packages feed

mischief-input-0.1.0.0: src/Mischief/Input/Mouse.hs

{-# OPTIONS_GHC -Wno-missing-pattern-synonym-signatures #-}

module Mischief.Input.Mouse
  ( Mouse,
    MousePlugin,
    pressed,
    released,
    justPressed,
    justReleased,
    pos,
    delta,
    getPos,
    getDelta,
    pattern Mischief.Input.Mouse.Left,
    pattern Mischief.Input.Mouse.Right,
    pattern Mischief.Input.Mouse.Middle,
    pattern Mischief.Input.Mouse.X1,
    pattern Mischief.Input.Mouse.X2,
  )
where

import Control.Monad.IO.Class (MonadIO (..))
import Foreign (Bits ((.&.)), Storable (peek), alloca)
import Foreign.C (CFloat)
import Mischief.ECS
import Mischief.ECS.App.Schedules
import Mischief.SDL
import SDL3.Sys qualified as SDL3

data MouseData = MouseData
  { flags :: SDL3.SDL_MouseButtonFlags,
    x :: CFloat,
    y :: CFloat
  }

data Mouse = Mouse
  { current :: MouseData,
    old :: MouseData
  }
  deriving (Component)

-- | Plugin which keeps track of key inputs and updates the 'Keys' resource.
data MousePlugin

instance Plugin MousePlugin where
  init = do
    systems updateMouse
      & schedule @First

    insertRes $ Mouse (MouseData 0 0 0) (MouseData 0 0 0)

  deps = [dep @SDLPlugin]

updateMouse :: System ()
updateMouse = do
  Just mouse <- res @Mouse

  (flags, x, y) <- liftIO $ alloca @CFloat $ \x -> alloca @CFloat $ \y -> do
    a <- liftIO $ SDL3.getMouseState x y
    x <- peek x
    y <- peek y
    pure (a, x, y)

  insertRes $ Mouse {old = mouse.current, current = MouseData flags x y}

pressed :: Mouse -> SDL3.SDL_MouseButtonFlags -> Bool
pressed Mouse {current = MouseData {flags}} = getVal flags

released :: Mouse -> SDL3.SDL_MouseButtonFlags -> Bool
released Mouse {current = MouseData {flags}} = getVal flags

justPressed :: Mouse -> SDL3.SDL_MouseButtonFlags -> Bool
justPressed Mouse {current = MouseData {flags}, old = MouseData {flags = flags'}} flag = getVal flags flag && not (getVal flags' flag)

justReleased :: Mouse -> SDL3.SDL_MouseButtonFlags -> Bool
justReleased Mouse {current = MouseData {flags}, old = MouseData {flags = flags'}} flag = not (getVal flags flag) && getVal flags' flag

getVal :: SDL3.SDL_MouseButtonFlags -> SDL3.SDL_MouseButtonFlags -> Bool
getVal x i = (x .&. i) /= 0

pos :: (MonadSystem w m) => m (Float, Float)
pos = getPos . unwrap <$> res @Mouse

getPos :: Mouse -> (Float, Float)
getPos Mouse {current = MouseData {x, y}} = (realToFrac x, realToFrac y)

delta :: (MonadSystem w m) => m (Float, Float)
delta = getDelta . unwrap <$> res @Mouse

getDelta :: Mouse -> (Float, Float)
getDelta Mouse {current = MouseData {x, y}, old = MouseData {x = x', y = y'}} = (realToFrac $ x - x', realToFrac $ y - y')

pattern Left = SDL3.SDL_BUTTON_LMASK

pattern Middle = SDL3.SDL_BUTTON_MMASK

pattern Right = SDL3.SDL_BUTTON_RMASK

pattern X1 = SDL3.SDL_BUTTON_X1MASK

pattern X2 = SDL3.SDL_BUTTON_X2MASK