33{-# LANGUAGE DeriveGeneric #-}
44{-# LANGUAGE MultiParamTypeClasses #-}
55{-# LANGUAGE OverloadedStrings #-}
6- {-# LANGUAGE PatternSynonyms #-}
76
87module SDL.Input.Mouse
98 ( -- * Relative Mouse Mode
@@ -35,6 +34,7 @@ module SDL.Input.Mouse
3534 , SystemCursor (.. )
3635 , activeCursor
3736 , createCursor
37+ , createCursorFrom
3838 , freeCursor
3939 , createColorCursor
4040 , createSystemCursor
@@ -45,6 +45,7 @@ import Control.Monad.IO.Class (MonadIO, liftIO)
4545import Data.Bits
4646import Data.Bool
4747import Data.Data (Data )
48+ import Data.List (nub )
4849import Data.StateVar
4950import Data.Typeable
5051import Data.Word
@@ -257,21 +258,86 @@ activeCursor = makeStateVar getCursor setCursor
257258 setCursor = Raw. setCursor . unwrapCursor
258259
259260-- | Create a cursor using the specified bitmap data and mask (in MSB format).
260- --
261- --
262261createCursor :: MonadIO m
263- => V. Vector Bool -- ^ Whether this part of the cursor is black. Use 'False' for white and 'True' for black.
264- -> V. Vector Bool -- ^ Whether or not pixels are visible. Use 'True' for visible and 'False' for transparent.
262+ => V. Vector Word8 -- ^ Whether this part of the cursor is black. Use 'False' for white and 'True' for black.
263+ -> V. Vector Word8 -- ^ Whether or not pixels are visible. Use 'True' for visible and 'False' for transparent.
265264 -> V2 CInt -- ^ The width and height of the cursor.
266265 -> Point V2 CInt -- ^ The X- and Y-axis location of the upper left corner of the cursor relative to the actual mouse position
267266 -> m Cursor
268267createCursor dta msk (V2 w h) (P (V2 hx hy)) =
269268 liftIO . fmap Cursor $
270269 throwIfNull " SDL.Input.Mouse.createCursor" " SDL_createCursor" $
271- V. unsafeWith ( V. map (bool 0 1 ) dta) $ \ unsafeDta ->
272- V. unsafeWith ( V. map (bool 0 1 ) msk) $ \ unsafeMsk ->
270+ V. unsafeWith dta $ \ unsafeDta ->
271+ V. unsafeWith msk $ \ unsafeMsk ->
273272 Raw. createCursor unsafeDta unsafeMsk w h hx hy
274273
274+ {- | Create a cursor from a bit art painting of it.
275+
276+ The number of columns must be a multiple of 8.
277+
278+ Symbols used: @ @ (space) - transparent, @.@ - visible black, @#@ (or anything else) - visible white.
279+
280+ A minimal cursor template:
281+ @
282+ source8x8 :: [[Char]]
283+ source8x8 =
284+ [ " "
285+ , " "
286+ , " "
287+ , " "
288+ , " "
289+ , " "
290+ , " "
291+ , " "
292+ ]
293+ @
294+ -}
295+ createCursorFrom :: MonadIO m
296+ => Point V2 CInt -- ^ The X- and Y-axis location of the upper left corner of the cursor relative to the actual mouse position
297+ -> [[Char ]]
298+ -> m Cursor
299+ createCursorFrom point source = do
300+ createCursor color mask (V2 w h) point
301+ where
302+ h = fromIntegral (length source)
303+ w = case nub $ map length source of
304+ [okay] ->
305+ fromIntegral okay
306+ mismatch ->
307+ error $ " Inconsistent row widths: " <> show mismatch
308+
309+ color = packBools colorBits
310+ mask = packBools maskBits
311+ (colorBits, maskBits) = unzip $ map charToBool $ concat source
312+
313+ packBools = V. fromList . boolListToWord8List
314+
315+ charToBool ' ' = (False , False ) -- transparent
316+ charToBool ' .' = (True , True ) -- visible black
317+ charToBool _ = (True , False ) -- visible white
318+
319+ boolListToWord8List xs =
320+ case xs of
321+ b1 : b2 : b3 : b4 : b5 : b6 : b7 : b8 : rest ->
322+ let
323+ packed =
324+ i b1 128 +
325+ i b2 64 +
326+ i b3 32 +
327+ i b4 16 +
328+ i b5 8 +
329+ i b6 4 +
330+ i b7 2 +
331+ i b8 1
332+ in
333+ packed : boolListToWord8List rest
334+ [] ->
335+ []
336+ _leftovers ->
337+ error " The number of columns must be a multiple of 8."
338+ where
339+ i True multiple = multiple
340+ i False _ = 0
275341
276342-- | Free a cursor created with 'createCursor', 'createColorCusor' and 'createSystemCursor'.
277343--
0 commit comments