mischief-sdl-0.1.0.0: src/Mischief/SDL/Events.hs
{-# LANGUAGE MultiWayIf #-}
module Mischief.SDL.Events
( -- $intro
SDLEventsPlugin,
SDLMessage (..),
SDLWindow (..),
handleEvents,
)
where
import Control.Monad
import Control.Monad.IO.Class
import Data.Foldable
import Data.Primitive.Ptr
import Foreign (Storable (peek), castPtr)
import Mischief.ECS
import Mischief.ECS.App.Schedules (First)
import Mischief.ECS.Foreign as F
import Mischief.ECS.Messages (Message)
import Mischief.ECS.Messages qualified as Messages
import SDL3.Sys (destroyWindow)
import SDL3.Sys qualified as SDL3
import System.Exit
newtype SDLWindow = SDLWindow {ptr :: Ptr SDL3.SDL_Window}
deriving anyclass (Component)
-- deriving newtype (Show)
initSdl :: IO ()
initSdl = do
version <- SDL3.getVersion
let (major, rest) = fromIntegral version `divMod` (1000000 :: Int)
(minor, micro) = rest `divMod` 1000
putStrLn
( "sdl3-raw: linked against SDL "
<> show major
<> "."
<> show minor
<> "."
<> show micro
)
-- The curated layer types the macro constant at the SDL_InitFlags
-- newtype (the raw sDL_INIT_VIDEO CUInt stays available under
-- SDL3.Sys.Bindgen.Init).
ok <- SDL3.init SDL3.SDL_INIT_VIDEO
unless ok (die "SDL_Init")
-- window <- withCString "sdl3-raw" $ \title -> SDL3.createWindow (ConstPtr title) 640 360 0
-- when (window == nullPtr) (die "Couldn't create window")
-- pure window
data SDLMessage e = SDLMessage {eventType :: SDL3.SDL_EventType, event :: e}
deriving anyclass (Message)
deriving stock (Show)
data SDLEventsPlugin
instance Plugin SDLEventsPlugin where
init = do
liftIO initSdl
systems handleEvents
& schedule @First
systems (handleQuit, handleWindowClose)
& after handleEvents
& schedule @First
-- addMessage @(SDLMessage SDL3.SDL_DisplayEvent)
-- addMessage @(SDLMessage SDl3.SDL_WindowEvent)
-- addMessage @(SDLMessage SDL3.SDL_KeyboardEvent)
-- addMessage @(SDLMessage SDLMouseButtonEvent)
-- addMessage @(SDLMessage SDLMouseMotionEvent)
-- addMessage @(SDLMessage SDLQuitEvent)
-- addSystems First handleEvents
-- addSystems First $ handleQuit `after` handleEvents
handleEvents :: System ()
handleEvents = F.alloca @SDL3.SDL_Event $ \event -> do
pending <- liftIO $ SDL3.pollEvent event
when pending $ do
e <- liftIO $ peek event
eventType <- liftIO $ peek (castPtr event :: Ptr SDL3.SDL_EventType)
handleEvent eventType e
handleEvents
-- alloca @SDL3.SDL_Event $ \event -> do
-- liftIO SDL3.pollEventSafe
-- case event of
-- Nothing -> do
-- return ()
-- Just event -> do
-- handleEvent event
-- handleEvents
-- these correspond to https://wiki.libsdl.org/SDL3/SDL_Event
handleEvent :: SDL3.SDL_EventType -> SDL3.SDL_Event -> System ()
handleEvent t e = case t of
-- audio
SDL3.SDL_EVENT_AUDIO_DEVICE_ADDED -> s t e.adevice
SDL3.SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED -> s t e.adevice
SDL3.SDL_EVENT_AUDIO_DEVICE_REMOVED -> s t e.adevice
-- TODO: camera
-- TODO: clipboard
-- display
SDL3.SDL_EVENT_DISPLAY_ADDED -> s t e.display
SDL3.SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED -> s t e.display
SDL3.SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED -> s t e.display
SDL3.SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED -> s t e.display
SDL3.SDL_EVENT_DISPLAY_FIRST -> s t e.display
SDL3.SDL_EVENT_DISPLAY_LAST -> s t e.display
SDL3.SDL_EVENT_DISPLAY_MOVED -> s t e.display
SDL3.SDL_EVENT_DISPLAY_ORIENTATION -> s t e.display
SDL3.SDL_EVENT_DISPLAY_REMOVED -> s t e.display
SDL3.SDL_EVENT_DISPLAY_USABLE_BOUNDS_CHANGED -> s t e.display
-- TODO: gamepad
-- TODO: gamepad axis
-- TODO: gamepad button
-- TODO: gamepad touchpad
-- TODO: gamepad sensor
-- TODO: drop
-- TODO: finger
-- TODO: pinch
-- keyboard
SDL3.SDL_EVENT_KEYBOARD_ADDED -> s t e.kdevice
SDL3.SDL_EVENT_KEYBOARD_REMOVED -> s t e.kdevice
-- key
SDL3.SDL_EVENT_KEY_DOWN -> s t e.key
SDL3.SDL_EVENT_KEY_UP -> s t e.key
-- TODO: joystick
-- TODO: joystick axis
-- TODO: joystick ball
-- TODO: joystick hat
-- TODO: joystick battery
-- TODO: joystick button
-- mouse
SDL3.SDL_EVENT_MOUSE_ADDED -> s t e.mdevice
SDL3.SDL_EVENT_MOUSE_REMOVED -> s t e.mdevice
-- mouse motion
SDL3.SDL_EVENT_MOUSE_MOTION -> s t e.motion
-- mouse button
SDL3.SDL_EVENT_MOUSE_BUTTON_DOWN -> s t e.button
SDL3.SDL_EVENT_MOUSE_BUTTON_UP -> s t e.button
-- mouse wheel
SDL3.SDL_EVENT_MOUSE_WHEEL -> s t e.wheel
-- TODO: pen proximity
-- TODO: pen touch
-- TODO: pen motion
-- TODO: pen button
-- TODO: pen axis
-- quit
SDL3.SDL_EVENT_QUIT -> s t e.quit
-- TODO: sensor
-- TODO: edit
-- TODO: edit candidates
-- text
SDL3.SDL_EVENT_TEXT_INPUT -> s t e.text
-- user
SDL3.SDL_EVENT_USER -> s t e.user
-- window
SDL3.SDL_EVENT_WINDOW_CLOSE_REQUESTED -> s t e.window
SDL3.SDL_EVENT_WINDOW_DESTROYED -> s t e.window
SDL3.SDL_EVENT_WINDOW_DISPLAY_CHANGED -> s t e.window
SDL3.SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED -> s t e.window
SDL3.SDL_EVENT_WINDOW_ENTER_FULLSCREEN -> s t e.window
SDL3.SDL_EVENT_WINDOW_EXPOSED -> s t e.window
SDL3.SDL_EVENT_WINDOW_FIRST -> s t e.window
SDL3.SDL_EVENT_WINDOW_FOCUS_GAINED -> s t e.window
SDL3.SDL_EVENT_WINDOW_FOCUS_LOST -> s t e.window
SDL3.SDL_EVENT_WINDOW_HDR_STATE_CHANGED -> s t e.window
SDL3.SDL_EVENT_WINDOW_HIDDEN -> s t e.window
SDL3.SDL_EVENT_WINDOW_HIT_TEST -> s t e.window
SDL3.SDL_EVENT_WINDOW_ICCPROF_CHANGED -> s t e.window
SDL3.SDL_EVENT_WINDOW_LAST -> s t e.window
SDL3.SDL_EVENT_WINDOW_LEAVE_FULLSCREEN -> s t e.window
SDL3.SDL_EVENT_WINDOW_MAXIMIZED -> s t e.window
SDL3.SDL_EVENT_WINDOW_METAL_VIEW_RESIZED -> s t e.window
SDL3.SDL_EVENT_WINDOW_MINIMIZED -> s t e.window
SDL3.SDL_EVENT_WINDOW_MOUSE_ENTER -> s t e.window
SDL3.SDL_EVENT_WINDOW_MOUSE_LEAVE -> s t e.window
SDL3.SDL_EVENT_WINDOW_MOVED -> s t e.window
SDL3.SDL_EVENT_WINDOW_OCCLUDED -> s t e.window
SDL3.SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED -> s t e.window
SDL3.SDL_EVENT_WINDOW_RESIZED -> s t e.window
SDL3.SDL_EVENT_WINDOW_RESTORED -> s t e.window
SDL3.SDL_EVENT_WINDOW_SAFE_AREA_CHANGED -> s t e.window
SDL3.SDL_EVENT_WINDOW_SHOWN -> s t e.window
-- common
t -> s t e.common
where
s t a = Messages.write $ SDLMessage t a
handleQuit :: System ()
handleQuit = do
messages <- Messages.read @(SDLMessage SDL3.SDL_QuitEvent)
unless (null messages) $ liftIO exitSuccess
handleWindowClose :: System ()
handleWindowClose = do
messages <- filter (\(SDLMessage t _) -> t == SDL3.SDL_EVENT_WINDOW_CLOSE_REQUESTED) <$> Messages.read @(SDLMessage SDL3.SDL_WindowEvent)
for_ messages $ \(SDLMessage _ SDL3.SDL_WindowEvent {windowID}) -> do
window <- liftIO $ SDL3.getWindowFromID windowID
unless (window == nullPtr) $ do
liftIO $ destroyWindow window
a <-
[q|(Entity, SDLWindow) / With SDLWindow|]
& qfilter (\(_, SDLWindow p) -> p == window)
& qmap fst
& single
traverse_ despawn a
-- $intro
-- This package provides the 'sdlPlugin' for @Mischief@, along with a few components.
--
-- These are necessary to run any SDL-based packages, such as @MischiefInput@, and @MischiefRender@.
--
-- In order to properly run this, you'll need to build / install SDL locally. This can be done via:
--
-- @
-- git clone --recurse-submodules https://github.com/PVDoriginal/mischief.git
-- cd mischief\/external\/SDL3
-- mkdir -p build && cd build
-- cmake -DCMAKE_BUILD_TYPE=Release -GNinja ..
-- cmake --build . --config Release --parallel
-- sudo cmake --install . --config Release # omit sudo on Windows
-- @
--
-- On @Windows@, ensure @SDL3.dll@ is available in @PATH@ (or next to your .exe). If @pkg-config@ cannot find SDL3, set:
--
-- @
-- set PKG_CONFIG_PATH=path\to\SDL3\lib\pkgconfig
-- # or
-- \$env:PKG_CONFIG_PATH="path\to\SDL3\lib\pkgconfig"
-- @