{-| Module : $Header$ Copyright : (c) 2016-19 Brian W Bush License : MIT Maintainer : Brian W Bush <code@functionally.io> Stability : Stable Portability : Portable Interaction with Leap Motion \<<https://www.leapmotion.com/product/desktop>\>, based on the Web Socket API \<<https://developer.leapmotion.com/documentation/javascript/supplements/Leap_JSON.html>\>. Here is a simple example applicaton that prints all events: @ main :: IO () main = runWithHandler def [setFocused True, setGestures True] $ \event -> print (event :: Event Float) @ -} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} module System.Hardware.Leap ( -- * Configuration Configuration(..) , Version(..) , setBackground , setFocused , setGestures , setOptimizedHMD -- * Applications , ClientApp , Handler , run , runWithHandler ) where import Control.Applicative (empty) import Control.Monad (forever) import Data.Aeson (FromJSON(..), Value(..), (.:), eitherDecode) import Data.Default (Default(..)) import Data.Text (pack) import Network.WebSockets (Connection, receiveData, runClient, sendTextData) import System.Hardware.Leap.Event (Event(..)) -- | Configuration for a connection to a Leap Motion controller via Web Sockets. data Configuration = Configuration { host :: String -- ^ Host name. , port :: Int -- ^ Port number. } deriving (Eq, Ord, Read, Show) instance Default Configuration where def = Configuration { host = "localhost" , port = 6437 } -- | A client application for Leap Motion using Web Sockets. type ClientApp a = Connection -- ^ The Web Socet connection. -> IO a -- ^ Action for the client application. -- | A handler for Leap Motion events. type Handler a = Event a -- ^ The event. -> IO () -- ^ The action for handling the event. -- | Run a Leap Motion application using Web Sockets. run :: Configuration -- ^ The Web Socket configuration. -> ClientApp a -- ^ The client application. -> IO a -- ^ Action for running the client. run Configuration{..} app = runClient host port "/v6.json" $ \connection -> do version' <- eitherDecode <$> receiveData connection case version' of Right v@(Version _ 6) -> putStrLn $ "Leap connection" ++ show v Right v -> error $ "Incorrect version: " ++ show v Left s -> error s app connection -- | Modify a Leap Motion connection. type ConnectionModifier = Connection -- ^ The Web Socket connection. -> IO () -- ^ Action for modifying the connection. -- | Run and process events from Leap Motion using Web Sockets. runWithHandler :: FromJSON a => Configuration -- ^ The Web Socket configuration. -> [ConnectionModifier] -- ^ Modifications to be made to the connection. -> Handler a -- ^ The event handler. -> IO () -- ^ Action for running with event handling. runWithHandler configuration modifiers handler = run configuration $ \connection -> do mapM_ ($ connection) modifiers forever $ do event' <- eitherDecode <$> receiveData connection case event' of Right e -> handler e Left s -> error s -- | Version information for Leap Motion. data Version = Version { serviceVersion :: String , version :: Int } deriving (Eq, Ord, Read, Show) instance FromJSON Version where parseJSON (Object o) = Version <$> o .: "serviceVersion" <*> o .: "version" parseJSON _ = empty -- | Enable or disable background events. See \<<https://developer.leapmotion.com/documentation/javascript/supplements/Leap_JSON.html>\> for details. setBackground :: Bool -- ^ Whether to enable background events. -> ConnectionModifier -- ^ Function for making the modification. setBackground = setSomething "background" -- | Enable or disable focus. See \<<https://developer.leapmotion.com/documentation/javascript/supplements/Leap_JSON.html>\> for details. setFocused :: Bool -- ^ Whether to enable focus. -> ConnectionModifier -- ^ Function for making the modification. setFocused = setSomething "focused" -- | Enable or disable gestures. See \<<https://developer.leapmotion.com/documentation/javascript/supplements/Leap_JSON.html>\> for details. setGestures :: Bool -- ^ Whether to enable gestiure events. -> ConnectionModifier -- ^ Function for making the modification. setGestures = setSomething "enableGestures" -- | Enable or disable head-mounted-display optimization. See \<<https://developer.leapmotion.com/documentation/javascript/supplements/Leap_JSON.html>\> for details. setOptimizedHMD :: Bool -- ^ Whether to enable HMD optimization. -> ConnectionModifier -- ^ Function for making the modification. setOptimizedHMD = setSomething "optimizeHMD" -- | Enable or disable something in Leap Motion. setSomething :: String -- ^ What to enable or disable. -> Bool -- ^ Whether to enable it. -> ConnectionModifier -- ^ Function for making the modification. setSomething item enabled connection = sendTextData connection $ pack $ "{\"" ++ item ++ "\" : " ++ (if enabled then "true" else "false") ++ "}"