packages feed

mischief-sdl (empty) → 0.1.0.0

raw patch · 7 files changed

+631/−0 lines, 7 filesdep +basedep +containersdep +data-default

Dependencies added: base, containers, data-default, mischief-ecs, mischief-sdl, primitive, sdl3-bindgen-sys

Files

+ CHANGELOG.md view
@@ -0,0 +1,5 @@+# Revision history for mischief-sdl++## 0.1.0.0 ++* First version. Released on an unsuspecting world.
+ LICENSE view
@@ -0,0 +1,29 @@+Copyright (c) 2026, Damon+++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of the copyright holder nor the names of its+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ mischief-sdl.cabal view
@@ -0,0 +1,135 @@+cabal-version: 3.0+-- The cabal-version field refers to the version of the .cabal specification,+-- and can be different from the cabal-install (the tool) version and the+-- Cabal (the library) version you are using. As such, the Cabal (the library)+-- version used must be equal or greater than the version stated in this field.+-- Starting from the specification version 2.2, the cabal-version field must be+-- the first thing in the cabal file.+-- Initial package description 'mischief' generated by+-- 'cabal init'. For further documentation, see:+--   http://haskell.org/cabal/users-guide/+--+-- The name of the package.+name: mischief-sdl+-- The package version.+-- See the Haskell package versioning policy (PVP) for standards+-- guiding when and how versions should be incremented.+-- https://pvp.haskell.org+-- PVP summary:     +-+------- breaking API changes+--                  | | +----- non-breaking API additions+--                  | | | +--- code changes with no API change+version: 0.1.0.0+-- A short (one-line) description of the package.+-- synopsis:+-- A longer description of the package.+-- description:+-- The license under which the package is released.+homepage: https://codeberg.org/mischief/mischief+synopsis: Library providing SDL integration for the Mischief game engine.+description: Integrates SDL events into Mischief messages and contains windowing utilities.+license: BSD-3-Clause+-- The file containing the license text.+license-file: LICENSE+-- The package author(s).+author: Damon+-- An email address to which users can send suggestions, bug reports, and patches.+maintainer: pvdoriginal2019@gmail.com+-- A copyright notice.+-- copyright:+build-type: Simple+-- Extra doc files to be distributed with the package, such as a CHANGELOG or a README.+extra-doc-files:+  CHANGELOG.md++-- Extra source files to be distributed with the package, such as examples, or a tutorial module.+-- extra-source-files:+common warnings+  ghc-options:+    "-Wall,"+    -Wno-name-shadowing++library+  -- Import common warning flags.+  import: warnings+  -- Modules exported by the library.+  exposed-modules:+    Mischief.SDL+    Mischief.SDL.Events+    Mischief.SDL.Window++  -- Modules included in this library but not exported.+  -- other-modules:+  other-modules:+  -- LANGUAGE extensions used by modules in this package.z+  default-extensions:+    DefaultSignatures+    DeriveAnyClass+    DuplicateRecordFields+    FunctionalDependencies+    ImportQualifiedPost+    MagicHash+    NoFieldSelectors+    OverloadedRecordDot+    OverloadedStrings+    QuasiQuotes+    RequiredTypeArguments+    TemplateHaskell+    TypeFamilyDependencies+    UndecidableInstances++  -- other-extensions:+  -- Other library packages from which modules are imported.+  build-depends:+    base >=4.21.0 && <4.22,+    containers >=0.7 && <0.8,+    data-default >=0.8.0 && <0.9,+    mischief-ecs >=0.2.0 && <0.3,+    primitive >=0.9.1 && <0.10,+    sdl3-bindgen-sys >=0.0.1 && <0.1,++  -- Directories containing source files.+  hs-source-dirs:+    src++  -- Base language which the package is written in.+  default-language:+    GHC2024++  ghc-options: -fno-omit-yields++test-suite mischief-test+  -- Import common warning flags.+  import: warnings+  ghc-options: -fno-omit-yields+  -- Base language which the package is written in.+  default-language: GHC2024+  -- Modules included in this executable, other than Main.+  -- other-modules:+  -- LANGUAGE extensions used by modules in this package.+  default-extensions:+    DeriveAnyClass+    DuplicateRecordFields+    NoFieldSelectors+    NoGeneralizedNewtypeDeriving+    OverloadedRecordDot+    RequiredTypeArguments+    TypeFamilyDependencies++  -- other-extensions:+  -- The interface type and version of the test suite.+  type: exitcode-stdio-1.0+  -- Directories containing source files.+  hs-source-dirs: test+  -- The entrypoint to the test suite.+  main-is: Main.hs+  -- Test dependencies.+  build-depends:+    base,+    containers,+    data-default,+    mischief-ecs,+    mischief-sdl,+    primitive,++  pkgconfig-depends:+    sdl3
+ src/Mischief/SDL.hs view
@@ -0,0 +1,66 @@+-- | Provides SDL utlities for Mischief.+module Mischief.SDL+  ( -- * What is SDL?+    -- $whatis++    -- * Installing SDL+    -- $installing+    SDLPlugin,+    module Mischief.SDL.Events,+    module Mischief.SDL.Window,+  )+where++import Mischief.ECS (Plugin (..), dep)+import Mischief.SDL.Events+import Mischief.SDL.Window++data SDLPlugin++instance Plugin SDLPlugin where+  deps = [dep @SDLEventsPlugin, dep @WindowPlugin]++-- $whatis+-- [SDL](https://wiki.libsdl.org) is a C library offering low-level tools for windows, input, rendering, etc.+-- A lot of the official Mischief libraries (such as @mischief-input@ and @mischief-render@) use this library,+-- and therefore require SDL to be installed.++-- $installing+-- The current official version of SDL used is @sdl-3.4.18@. You should+-- find this pre-built for most Operating Systems, and installing it is relatively easy.+--+-- These install instructions are mostly copied from [sdl3-bindgen-sys](https://hackage.haskell.org/package/sdl3-bindgen-sys)'s readme.+--+-- * Debian / Ubuntu+--+-- @+-- apt install libsdl3-dev-3.4.18+-- @+--+-- * MacOS+--+-- @+-- -- uninstall sdl3 if you have it+-- brew tap-new $USER/local+-- brew extract --version=3.4.18 sdl3 $USER/local+-- brew install $USER/local/sdl3@3.4.18+-- @+--+-- * Arch+--+-- @+-- pacman -S sdl3@3.4.18+-- @+--+-- * Fedora+--+-- @+-- dnf install SDL3-devel-3.4.18+-- @+--+-- * Windows+--+-- @+-- pacman -Syyu # repeat/restart terminal if pacman asks you to+-- pacman -S mingw-w64-ucrt-x86_64-sdl3-3.4.18 mingw-w64-ucrt-x86_64-pkgconf+-- @
+ src/Mischief/SDL/Events.hs view
@@ -0,0 +1,243 @@+{-# 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"+-- @
+ src/Mischief/SDL/Window.hs view
@@ -0,0 +1,122 @@+module Mischief.SDL.Window where++import Control.Monad.IO.Class+import Data.Bits ((.|.))+import Data.Default+import Data.Foldable (for_)+import Foreign.C+import Foreign.C.ConstPtr (ConstPtr (..))+import Mischief.ECS+import Mischief.ECS.App.Schedules (First)+import Mischief.ECS.Messages qualified as Messages+import Mischief.SDL.Events+import SDL3.Sys qualified as SDL3+import SDL3.Sys.Video++-- | A component that will create a new window when added to an entity, and destroy it when removed.+--+-- Windows can be configured by inserting a 'WindowSize', 'WindowTitle', 'WindowFlags' alongside this component.+data Window = Window++instance Component Window where+  onAdd = [hook handleNewWindow]+  onRemove = [hook handleWindowRemove]+  required = require @(WindowSize, WindowTitle, WindowFlags)++-- | The size of a window in pixels.+data WindowSize = WindowSize {width :: Int, height :: Int} deriving (Component)++instance Default WindowSize where+  def = WindowSize 600 400++-- | The displayed title (or name) of a window.+newtype WindowTitle = WindowTitle String deriving anyclass (Component)++instance Default WindowTitle where+  def = WindowTitle "Mischief Window"++handleNewWindow :: HookContext -> System ()+handleNewWindow (HookContext entity) = do+  Just (WindowSize w h, WindowTitle title, flags) <- single [q|entity. WindowSize, WindowTitle, WindowFlags|]+  window <- liftIO $ withCString title $ \title -> SDL3.createWindow (ConstPtr title) (fromIntegral w) (fromIntegral h) (flagsToBit flags)+  insert (SDLWindow window) entity++handleWindowRemove :: HookContext -> System ()+handleWindowRemove (HookContext entity) = do+  Just (SDLWindow p) <- single [q|entity. SDLWindow|]+  liftIO $ SDL3.destroyWindow p++-- | These correspond to the [SDL3 Window Flags](https://wiki.libsdl.org/SDL3/SDL_WindowFlags).+--+-- The most important ones are 'WindowFullscreen' and 'WindowResizable'.+data WindowFlag+  = WindowFullscreen+  | WindowHidden+  | WindowBorderless+  | WindowResizable+  | WindowMinimized+  | WindowMaximized+  | WindowModal+  | WindowHighPixelDensity+  | WindowMouseRelativeMode+  | WindowAlwaysOnTop+  | WindowUtility+  | WindowTooltip+  | WindowPopupMenu+  | WindowTransparent+  | WindowNotFocuable+  | WindowMetal++flagToBit :: WindowFlag -> SDL_WindowFlags+flagToBit WindowFullscreen = SDL_WINDOW_FULLSCREEN+flagToBit WindowHidden = SDL_WINDOW_HIDDEN+flagToBit WindowBorderless = SDL_WINDOW_BORDERLESS+flagToBit WindowResizable = SDL_WINDOW_RESIZABLE+flagToBit WindowMinimized = SDL_WINDOW_MINIMIZED+flagToBit WindowMaximized = SDL_WINDOW_MAXIMIZED+flagToBit WindowModal = SDL_WINDOW_MODAL+flagToBit WindowHighPixelDensity = SDL_WINDOW_HIGH_PIXEL_DENSITY+flagToBit WindowMouseRelativeMode = SDL_WINDOW_MOUSE_RELATIVE_MODE+flagToBit WindowAlwaysOnTop = SDL_WINDOW_ALWAYS_ON_TOP+flagToBit WindowUtility = SDL_WINDOW_UTILITY+flagToBit WindowTooltip = SDL_WINDOW_TOOLTIP+flagToBit WindowPopupMenu = SDL_WINDOW_POPUP_MENU+flagToBit WindowTransparent = SDL_WINDOW_TRANSPARENT+flagToBit WindowNotFocuable = SDL_WINDOW_NOT_FOCUSABLE+flagToBit WindowMetal = SDL_WINDOW_METAL++-- | A list of 'WindowFlag's specifying various configurations for the window. The only one set by default is 'WindowResizable'.+newtype WindowFlags = WindowFlags [WindowFlag] deriving anyclass (Component)++instance Default WindowFlags where+  def = WindowFlags [WindowResizable]++flagsToBit :: WindowFlags -> SDL_WindowFlags+flagsToBit (WindowFlags a) = foldr (\a b -> b .|. flagToBit a) 0 a++-- | Plugin adding windowing functionality.+data WindowPlugin++instance Plugin WindowPlugin where+  init = do+    systems handleWindowEvents+      & after handleEvents+      & schedule @First++handleWindowEvents :: System ()+handleWindowEvents = do+  messages <- Messages.read @(SDLMessage SDL3.SDL_WindowEvent)+  for_ messages handleWindowEvent++handleWindowEvent :: SDLMessage SDL3.SDL_WindowEvent -> System ()+handleWindowEvent (SDLMessage SDL3.SDL_EVENT_WINDOW_RESIZED w) = do+  let width :: Int = fromIntegral w.data1+  let height :: Int = fromIntegral w.data2++  window <- liftIO $ SDL3.getWindowFromID w.windowID++  [q|(Entity, SDLWindow) / With SDLWindow|]+    & qfilter (\(_, SDLWindow p) -> p == window)+    & qinsert (const $ WindowSize width height)+    & query_+handleWindowEvent _ = pure ()
+ test/Main.hs view
@@ -0,0 +1,31 @@+module Main where++import Data.Maybe+import Mischief.ECS+import Mischief.ECS.Components.Spawn+import Mischief.ECS.Systems qualified as Systems+import Mischief.SDL+import Mischief.SDL.Window++main :: IO ()+main = do+  app <- newApp+  addPlugin @MainPlugin app+  runApp app++data MainPlugin = MainPlugin deriving (Eq)++instance Plugin MainPlugin where+  init = do+    schedule @Update $ systems (pure () :: System ())+    schedule @Startup $ systems setup++  deps = [dep @SDLPlugin]++setup :: System ()+setup = do+  w1 <- spawn (Window, WindowSize 100 300)+  w2 <- spawn (Window, WindowTitle "Second Window", WindowSize 1000 40)+  w3 <- spawn Window++  pure ()