packages feed

TimePiece (empty) → 0.0.1

raw patch · 44 files changed

+651/−0 lines, 44 filesdep +SDLdep +SDL-gfxdep +SDL-imagesetup-changedbinary-added

Dependencies added: SDL, SDL-gfx, SDL-image, SDL-ttf, base, containers, old-locale, old-time, random

Files

+ 1/10.png view

binary file changed (absent → 969 bytes)

+ 1/20.png view

binary file changed (absent → 2900 bytes)

+ 1/30.png view

binary file changed (absent → 5743 bytes)

+ 1/40.png view

binary file changed (absent → 9367 bytes)

+ 1/50.png view

binary file changed (absent → 14258 bytes)

+ 1/60.png view

binary file changed (absent → 19532 bytes)

+ 1/70.png view

binary file changed (absent → 25580 bytes)

+ 1/80.png view

binary file changed (absent → 32542 bytes)

+ 1/90.png view

binary file changed (absent → 40056 bytes)

+ 2/10.png view

binary file changed (absent → 1131 bytes)

+ 2/20.png view

binary file changed (absent → 3359 bytes)

+ 2/30.png view

binary file changed (absent → 6880 bytes)

+ 2/40.png view

binary file changed (absent → 11736 bytes)

+ 2/50.png view

binary file changed (absent → 18184 bytes)

+ 2/60.png view

binary file changed (absent → 25755 bytes)

+ 2/70.png view

binary file changed (absent → 34459 bytes)

+ 2/80.png view

binary file changed (absent → 44846 bytes)

+ 2/90.png view

binary file changed (absent → 56163 bytes)

+ 3/10.png view

binary file changed (absent → 1076 bytes)

+ 3/20.png view

binary file changed (absent → 3141 bytes)

+ 3/30.png view

binary file changed (absent → 6247 bytes)

+ 3/40.png view

binary file changed (absent → 10167 bytes)

+ 3/50.png view

binary file changed (absent → 15340 bytes)

+ 3/60.png view

binary file changed (absent → 21425 bytes)

+ 3/70.png view

binary file changed (absent → 28547 bytes)

+ 3/80.png view

binary file changed (absent → 37007 bytes)

+ 3/90.png view

binary file changed (absent → 46459 bytes)

+ Cache.hs view
@@ -0,0 +1,36 @@+module Cache where+import System.IO.Unsafe+import Data.IORef+import qualified Data.Map as M++type Cache k a = IORef (M.Map k a)+type CacheOnce k a = IORef (Maybe (k, a))++initCache :: IO a -> a+initCache = unsafePerformIO++newCache :: (Eq k, Ord k) => IO (Cache k a)+newCache = newIORef (M.empty)++newCacheOnce :: Eq k => IO (CacheOnce k a)+newCacheOnce = newIORef Nothing++cacheOnce :: Eq k => CacheOnce k a -> (k -> IO a) -> k -> IO a+cacheOnce ref f key = do+    m <- readIORef ref+    case m of+        Just (k, v) | k == key -> return v+        _ -> do+            v <- f key+            writeIORef ref (Just (key, v))+            return v++cache :: (Eq k, Ord k) => Cache k a -> (k -> IO a) -> k -> IO a+cache ref f key = do+    m <- readIORef ref+    case M.lookup key m of+        Just v  -> return v+        _       -> do+            v <- f key+            modifyIORef ref (M.insert key v)+            return v
+ Check.hs view
@@ -0,0 +1,40 @@+module Check where+import Data.List (sort)+import Tiles+import Render+import Test.LazySmallCheck++instance Serial Point where+    series = cons2 MkPoint++prop_tiling points = +    toPointSet points ==+    toPointSet (tilePoints $ makeTiles points)++main = do+    pts <- renderChar "test.ttf" 24 '龍'+    print pts+    putStrLn $ showPoints pts+    tiles <- makeRandomTiles pts+    putStrLn . showTiles $ tiles+    putStrLn . showPoints $ tilePoints tiles++sampleTiles :: Tiles+sampleTiles = concat+    [ [(MkPoint x 0, 1) | x <- [0..4]]+    , [(MkPoint x 1, 2) | x <- [0..5]]+    , [(MkPoint x 2, 3) | x <- [2..7]]+    , [(MkPoint x 3, 1) | x <- [3..7]]+    ]++samplePoints' :: [Point]+samplePoints' = concat+    [ [MkPoint x 0 | x <- [0..4]]+    , [MkPoint x 1 | x <- [0..5]]+    , [MkPoint x 2 | x <- [2..7]]+    , [MkPoint x 3 | x <- [3..7]]+    ]++samplePoints = +    [MkPoint {pointX = 0, pointY = 1},MkPoint {pointX = 0, pointY = 0},MkPoint {pointX = 0, pointY = 0},MkPoint {pointX = 1, pointY = 0},MkPoint {pointX = 1, pointY = 1}+    ]
+ HSMain.hs view
@@ -0,0 +1,10 @@+{-# LANGUAGE ForeignFunctionInterface #-}+ +module HSMain where+ +import Main+ +foreign export ccall hs_main :: IO ()+ +hs_main :: IO ()+hs_main = main
+ HSMain_stub.c view
@@ -0,0 +1,24 @@+#define IN_STG_CODE 0+#include "Rts.h"+#include "Stg.h"+#ifdef __cplusplus+extern "C" {+#endif+ +extern StgClosure HSMain_zdfhszumainzuatN_closure;+void hs_main(void)+{+Capability *cap;+HaskellObj ret;+cap = rts_lock();+cap=rts_evalIO(cap,rts_apply(cap,(HaskellObj)runIO_closure,&HSMain_zdfhszumainzuatN_closure) ,&ret);+rts_checkSchedStatus("hs_main",cap);+rts_unlock(cap);+}+static void stginit_export_HSMain_zdfhszumainzuatN() __attribute__((constructor));+static void stginit_export_HSMain_zdfhszumainzuatN()+{getStablePtr((StgPtr) &HSMain_zdfhszumainzuatN_closure);}+#ifdef __cplusplus+}+#endif+
+ HSMain_stub.h view
@@ -0,0 +1,9 @@+#include "HsFFI.h"+#ifdef __cplusplus+extern "C" {+#endif+extern void hs_main(void);+#ifdef __cplusplus+}+#endif+
+ Image.hs view
@@ -0,0 +1,51 @@+module Image where+import Internals (setScreenWidth, setScreenHeight, getDataFileName)+import Cache+import Point+import Zoom+import Graphics.UI.SDL as SDL+import qualified Graphics.UI.SDL.TTF as TTF+import qualified Graphics.UI.SDL.Image as SDLImage+import qualified Graphics.UI.SDL.Rotozoomer as SDLRotozoomer++initSDL :: (Surface -> IO a) -> IO ()+initSDL f = SDL.withInit [InitTimer, InitAudio, InitVideo] $ do +    info    <- getVideoInfo+    let w = videoInfoWidth info+        h = videoInfoHeight info+    setScreenWidth w +    setScreenHeight h+    screen  <- setVideoMode w h 32+        [AnyFormat, Fullscreen, HWAccel, HWSurface, DoubleBuf]+    TTF.init+    f screen+    TTF.quit++blitTile :: (Show a) => X -> Y -> Surface -> a -> Zoom -> (Point, Int) -> IO Bool+blitTile xOff yOff canvas seed z (MkPoint x y, sz) = do+    img <- loadImageSized (show seed, (sz * 9) *** z)+    let x'  = x * 9 + xOff+        y'  = y * 12 + yOff+        pos = Just (Rect (x' ~~~ z) (y' ||| z) 0 0)+    blitSurface img Nothing canvas pos++{-# NOINLINE _loadImage #-}+_loadImage :: Cache FilePath Surface+_loadImage = initCache newCache+loadImage :: FilePath -> IO Surface+loadImage = cache _loadImage SDLImage.load++{-# NOINLINE _loadImageSized #-}+_loadImageSized :: Cache (String, Int) Surface+_loadImageSized = initCache newCache+loadImageSized :: (String, Int) -> IO Surface+loadImageSized = cache _loadImageSized $ \(fn, sz) -> do+    -- Round to nearest multiples of 10+    img <- getDataFileName $ fn ++ "/" ++ show ((sz + 9) `div` 10) ++ "0.png"+    src <- loadImage img+    let r = (fromIntegral sz + 0.5) / fromIntegral srcWidth+        srcWidth = SDL.surfaceGetWidth src+    if srcWidth == sz+        then return src+        else SDLRotozoomer.zoom src r r True+
+ Internals.hs view
@@ -0,0 +1,35 @@+module Internals ( getDataFileName, module Internals ) where+import Paths_TimePiece+import System.IO.Unsafe+import Data.IORef++{-# NOINLINE _WIDTH_ #-}+_WIDTH_ :: IORef Integer+_WIDTH_ = unsafePerformIO $ newIORef 1024++{-# NOINLINE _HEIGHT_ #-}+_HEIGHT_ :: IORef Integer+_HEIGHT_ = unsafePerformIO $ newIORef 768++getScreenWidth :: Num a => IO a+getScreenWidth = fmap fromIntegral (readIORef _WIDTH_)++getScreenHeight :: Num a => IO a+getScreenHeight = fmap fromIntegral (readIORef _HEIGHT_)++setScreenWidth :: Integral a => a -> IO ()+setScreenWidth = writeIORef _WIDTH_ . toInteger++setScreenHeight :: Integral a => a -> IO ()+setScreenHeight = writeIORef _HEIGHT_ . toInteger++type X = Int+type Y = Int+type Size = Int++data Point = MkPoint+    { pointX :: !X+    , pointY :: !Y+    }+    deriving (Show, Eq, Ord)+
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright 2008 by Audrey Tang++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be+included in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Main.hs view
@@ -0,0 +1,126 @@+module Main where+import System.Time+import System.Random+import System.Locale+import Control.Monad+import Graphics.UI.SDL as SDL++import Internals (getScreenWidth, getScreenHeight, getDataFileName)+import Zoom+import Image+import Tiles+import Render+import Cache++data Status+    = Static  { sTime :: String }+    | ZoomIn  { sTime :: String, sZoom :: Zoom, sSpeed :: Speed }+    | ZoomOut { sTime :: String, sZoom :: Zoom, sSpeed :: Speed }++main :: IO ()+main = initSDL $ \screen -> loop (Static "") $ \status -> do+    time' <- getCurrentTime+    case status of+        Static{ sTime = t } | t == time' -> do+            SDL.delay 50+            return status+        Static{} -> do+            paintScreen screen time' noZoom+            if last time' == '0'+                then initZoomIn time'+                else return status{ sTime = time' }+        ZoomIn{ sZoom = z, sSpeed = s } -> do+            paintScreen screen time' z+            -- print $ z+            let ratio' = fixRatio $ ratio z + s+            return $ if ratio z >= maxBound+                then ZoomOut+                    { sTime  = time'+                    , sZoom  = z+                    , sSpeed = 0.05+                    }+                else status+                    { sTime  = time'+                    , sZoom  = z{ ratio = ratio' }+                    , sSpeed = (maxBound - ratio z) / 50 + 0.01+                    }+        ZoomOut{ sZoom = z, sSpeed = s } -> do+            paintScreen screen time' z+            let ratio' = fixRatio $ ratio z - s+            return $ if ratio z <= minBound+                then Static{ sTime = time' }+                else status+                    { sTime = time'+                    , sZoom = z{ ratio = ratio' }+                    }++data Tiling = MkTiling+    { tTiles :: Tiles+    , tSeeds :: [Int]+    , tEdge  :: Point+    }++{-# NOINLINE _calculateTiling #-}+_calculateTiling :: CacheOnce String [Tiling]+_calculateTiling = initCache newCacheOnce+calculateTiling :: String -> IO [Tiling]+calculateTiling = cacheOnce _calculateTiling $ \time -> forM time $ \ch -> do+    ttf     <- getDataFileName "TimePiece.ttf"+    pts     <- renderChar ttf 22 ch+    tiling  <- makeRandomTiles pts+    seeds   <- forM tiling . const $ randomRIO (1, 3)+    return $ MkTiling tiling seeds (edgePoint (toPointSet pts))++paintScreen :: Surface -> String -> Zoom -> IO ()+paintScreen screen time zoom = do+    w <- getScreenWidth+    h <- getScreenHeight+    bgColor <- mapRGB (surfaceGetPixelFormat screen) 0x00 0x00 0x33+    fillRect screen Nothing bgColor+    rvs <- calculateTiling time+    let maxX  = 10 -- maximum (map pointX edges)+        maxY  = maximum (map pointY edges)+        edges = map tEdge rvs+    forM_ ([0..] `zip` rvs) $ \(n, MkTiling tiles seeds edge) -> do+        let tiles' = map adjust tiles+            adjust (MkPoint x y, sz) = (MkPoint (x + deltaX) (y + deltaY), sz)+            deltaX = (maxX - pointX edge + 1) `div` 2 + 1+            deltaY = (maxY - pointY edge + 1) `div` 2 + 1+        forM_ (tiles' `zip` seeds) $ \(tile, seed) -> do+            let x = (((16 * 15 * n) `div` 2) + ((w - 964) `div` 2))+                y = (h - 288) `div` 2+            blitTile x y screen seed zoom tile+    SDL.flip screen++getCurrentTime :: IO String+getCurrentTime = do+    fmap (formatCalendarTime defaultTimeLocale "%H:%M:%S")+        . toCalendarTime =<< getClockTime++loop :: Status -> (Status -> IO Status) -> IO ()+loop x f = f x >>= \x' -> do+    event <- pollEvent+    case event of+        Quit -> return ()+        KeyDown (Keysym SDLK_SPACE _ _) -> do+            status <- initZoomIn (sTime x')+            loop status f+        KeyDown (Keysym SDLK_k _ _) -> loop x' f+        KeyDown{}           -> return ()+        MouseButtonDown{}   -> return ()+        _                   -> loop x' f++initZoomIn :: String -> IO Status+initZoomIn time' = do+    w <- getScreenWidth+    h <- getScreenHeight+    xf <- randomRIO (0, w)+    yf <- randomRIO ((h `div` 2)-96, (h `div` 2)+96)+    return $ ZoomIn+        { sZoom = MkZoom+            { ratio = 1+            , focus = MkPoint xf yf+            }+        , sTime  = time'+        , sSpeed = 0+        }
+ Point.hs view
@@ -0,0 +1,12 @@+module Point where++type X = Int+type Y = Int+type Size = Int++data Point = MkPoint+    { pointX :: !X+    , pointY :: !Y+    }+    deriving (Show, Eq, Ord)+
+ Render.hs view
@@ -0,0 +1,36 @@+module Render where+import Foreign+import Point+import qualified Graphics.UI.SDL as SDL+import qualified Graphics.UI.SDL.TTF as TTF++type FontName = FilePath++renderChar :: FontName -> Size -> Char -> IO [Point]+renderChar fn size ch = do+    font            <- TTF.openFont fn size+    Just textImage  <- TTF.tryRenderGlyphSolid font ch white+    let width  = SDL.surfaceGetWidth textImage+        height = SDL.surfaceGetHeight textImage+    pixels  <- SDL.surfaceGetPixels textImage+    vals    <- peekArray (width * height) (castPtr pixels)++    let points = [ MkPoint x y | y <- [0..(height-1)], x <- [0..(width-1)] ]++    return [ pt | (pt, v) <- points `zip` vals, v > (0 :: Word8) ]++white :: SDL.Color+white = SDL.Color 255 255 255++showPixels :: SDL.Surface -> IO String+showPixels surface = do+    pixels <- SDL.surfaceGetPixels surface+    let width  = SDL.surfaceGetWidth surface+        height = SDL.surfaceGetHeight surface+        format = SDL.surfaceGetPixelFormat surface+    bpp     <- SDL.pixelFormatGetBytesPerPixel format+    vals    <- peekArray (fromEnum bpp * width * height) (castPtr pixels)+    let part [] = []+        part xs = let (r, rs) = splitAt width xs in (r:part rs)+    return . unlines $ map (map (\x -> if x > (0 :: Word8) then '*' else ' ')) (part vals)+
+ Setup.lhs view
@@ -0,0 +1,3 @@+#!/usr/bin/env runhaskell+> import Distribution.Simple+> main = defaultMain
+ Tiles.hs view
@@ -0,0 +1,153 @@+module Tiles+    ( Point(..), PointSet, Tiles+    , makeRandomTiles, makeTiles+    , tilePoints, showTiles, showPoints+    , toPointSet, fromPointSet, showPointSet, edgePoint+    ) where+import Point+import System.Random+import Data.List (find)+import qualified Data.IntMap as IM+import qualified Data.IntSet as IS++type Tiles = [(Point, Size)]++-- Map from -X to existence of -Y+type PointSet = IM.IntMap IS.IntSet++tilePoints :: Tiles -> [Point]+tilePoints [] = []+tilePoints ((p@(MkPoint x y), 4):rest) = block ++ (p:tilePoints rest)+    where+    block = [ MkPoint (x+dx) (y+dy) | (dx, dy) <- _size2 ++ _size3 ++ _size4 ]+tilePoints ((p@(MkPoint x y), 3):rest) = block ++ (p:tilePoints rest)+    where+    block = [ MkPoint (x+dx) (y+dy) | (dx, dy) <- _size2 ++ _size3 ]+tilePoints ((p@(MkPoint x y), 2):rest) = block ++ (p:tilePoints rest)+    where+    block = [ MkPoint (x+dx) (y+dy) | (dx, dy) <- _size2 ]+tilePoints ((p, _):rest) = (p:tilePoints rest)++makeTiles :: [Point] -> Tiles+makeTiles [] = []+makeTiles points = (m:makeTiles rest')+    where+    ps = toPointSet points+    (m@(_, msz), rest) = case find ((== 4) . snd) tilings of+        Just m4 -> (m4, points)+        _       -> case find ((== 3) . snd) tilings of+            Just m3 -> (m3, points)+            _       -> case find ((== 2) . snd) tilings of+                Just m2 -> (m2, points)+                _       -> ((head points, 1), tail points)+    rest' = if msz == 1 then rest else filter (not . inArea m) rest+    tilings = [ (p, getTileSize p ps) | p <- points ]++makeRandomTiles :: [Point] -> IO Tiles+makeRandomTiles = fmap makeTiles . shuffle++insertPoint :: Point -> PointSet -> PointSet+insertPoint (MkPoint px py) ps = IM.insert x ys' ps+    where+    x   = -px+    y   = -py+    ys' = case IM.lookup x ps of+        Just ys -> IS.insert y ys+        _       -> IS.singleton y+    ++lookupPoint :: Point -> PointSet -> Bool+lookupPoint (MkPoint px py) ps = case IM.lookup x ps of+    Just ys -> IS.member y ys+    _       -> False+    where+    x = -px+    y = -py++{-+deletePoint :: Point -> PointSet -> PointSet+deletePoint (MkPoint px py) = IM.update doUpdate x+    where+    x = -px+    y = -py+    doUpdate ys = if IS.null ys' then Nothing else Just ys'+        where+        ys' = IS.delete y ys+-}++-- At least we have size 1; the goal is to check for bigger sizes. +getTileSize :: Point -> PointSet -> Size+getTileSize (MkPoint x y) ps+    | all ok _size2 = if all ok _size3 then if all ok _size4 then 4 else 3 else 2+    | otherwise = 1+    where+    ok (dx, dy) = lookupPoint (MkPoint (x+dx) (y+dy)) ps++_size2, _size3, _size4 :: [(X, Y)]+_size2 = [(1, 0), (0, 1), (1, 1)]+_size3 = [(2, 0), (2, 1), (0, 2), (1, 2), (2, 2)]+_size4 = [(3, 0), (3, 1), (3, 2), (0, 3), (1, 3), (2, 3), (3, 3)]++-- The bottom right edge of a point set+edgePoint :: PointSet -> Point+edgePoint ps = MkPoint (-x) (-y)+    where+    x = head (IM.keys ps)+    y = minimum $ map (head . IS.elems) (IM.elems ps)++shuffle :: [a] -> IO [a]+shuffle [] = return []+shuffle [c] = return [c]+shuffle deck0 = part deck0 [] []+    where+    part [] p0 p1 = do+        s1 <- shuffle p0+        s2 <- shuffle p1+        return (s1 ++ s2)+    part (d : deck) p0 p1 = do+        n <- randomRIO (False, True)+        if n then part deck (d : p0) p1+             else part deck p0 (d : p1)++inArea :: (Point, Size) -> Point -> Bool+inArea (MkPoint x y, sz) (MkPoint tx ty)+    =  dx >= 0 && dx < sz+    && dy >= 0 && dy < sz+    where+    dx = tx - x+    dy = ty - y++-- doMakeTiles :: PointSet -> Tiles++showTiles :: Tiles -> String+showTiles mosaic = unlines+    [   [ case lookup (MkPoint x y) mosaic of+            Just sz -> toEnum (48 + sz)+            _       -> ' '+        | x <- [0..maxX]+        ]+    | y <- [0..maxY]+    ]+    where+    pts  = map fst mosaic+    maxX = maximum (map pointX pts)+    maxY = maximum (map pointY pts)++showPoints :: [Point] -> String+showPoints = showPointSet . toPointSet++showPointSet :: PointSet -> String+showPointSet ps = unlines+    [   [ if lookupPoint (MkPoint x y) ps then '*' else ' '+        | x <- [0..maxX]+        ]+    | y <- [0..maxY]+    ]+    where+    MkPoint maxX maxY = edgePoint ps++toPointSet :: [Point] -> PointSet+toPointSet = foldl (flip insertPoint) IM.empty++fromPointSet :: PointSet -> [Point]+fromPointSet ps = concat [ [ MkPoint x y | y <- IS.elems ys ] | (x, ys) <- IM.assocs ps ]
+ TimePiece.cabal view
@@ -0,0 +1,23 @@+Name:               TimePiece+Version:            0.0.1+Synopsis:           A simple tile-based digital clock screen saver+Description:        A simple tile-based digital clock screen saver+License:            BSD3+License-File:       LICENSE+Category:           Screensaver+Author:             Audrey Tang+Maintainer:         Audrey Tang <audreyt@audreyt.org>+Build-Depends:      base, SDL >= 0.5.4, SDL-image, SDL-ttf, SDL-gfx, containers, random, old-locale, old-time+Build-Type:         Simple+Data-Files:         1/10.png 1/20.png 1/30.png 1/40.png 1/50.png 1/60.png 1/70.png 1/80.png 1/90.png+                    2/10.png 2/20.png 2/30.png 2/40.png 2/50.png 2/60.png 2/70.png 2/80.png 2/90.png+                    3/10.png 3/20.png 3/30.png 3/40.png 3/50.png 3/60.png 3/70.png 3/80.png 3/90.png+                    HSMain_stub.c HSMain_stub.h TimePiece.ttf+ +Executable:         TimePiece+HS-Source-Dirs:     .+Main-Is:            HSMain.hs+GHC-Options:        -no-hs-main -Wall+C-Sources:          c_main.c+Extra-Libraries:    SDLmain+Other-Modules:      Cache Check Image Internals Main Point Render Tiles Zoom
+ TimePiece.ttf view

binary file changed (absent → 21296 bytes)

+ Zoom.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+module Zoom where+import Point++newtype Ratio = MkRatio { fromRatio :: Double }+    deriving (Show, RealFrac, Fractional, Real, Ord, Num, Eq, Enum)+type Speed = Ratio++data Zoom = MkZoom+    { ratio :: !Ratio+    , focus :: !Point+    }+    deriving Show++noZoom :: Zoom+noZoom = MkZoom 1 (MkPoint 0 0)++fixRatio :: Ratio -> Ratio+fixRatio = min maxBound . max minBound++instance Bounded Ratio where+    minBound = 1+    maxBound = 2++(***) :: (Integral b, Integral a) => a -> Zoom -> b+x *** MkZoom r _ = round (fromIntegral x * r)++(~~~) :: (Integral b, Integral a) => a -> Zoom -> b+x ~~~ MkZoom r (MkPoint dx _) = +    round (fromIntegral x * r + ((fromIntegral dx - 256) * 2 / pred maxBound) * (1-r))++(|||) :: (Integral b, Integral a) => a -> Zoom -> b+y ||| MkZoom r (MkPoint _ dy) = +    round (fromIntegral y * r + ((fromIntegral dy - 192) * 2 / pred maxBound) * (1-r))
+ c_main.c view
@@ -0,0 +1,39 @@+#include <stdio.h>+#include "HsFFI.h"+ +#ifdef __APPLE__+#include <objc/objc.h>+#include <objc/objc-runtime.h>+#endif+ +#include <SDL.h>+ +#ifdef __GLASGOW_HASKELL__+#include "HSMain_stub.h"+extern void __stginit_HSMain ( void );+#endif+ +int SDL_main(int argc, char *argv[])+{+    int i;+ +#ifdef __APPLE__+    void * pool =+      objc_msgSend(objc_lookUpClass("NSAutoreleasePool"), sel_getUid("alloc"));+    objc_msgSend(pool, sel_getUid("init"));+#endif+ +    hs_init(&argc, &argv);+#ifdef __GLASGOW_HASKELL__+    hs_add_root(__stginit_HSMain);+#endif+ +    hs_main();+ +    hs_exit();+ +#ifdef __APPLE__+    objc_msgSend(pool, sel_getUid("release"));+#endif+    return 0;+}