packages feed

workflow-windows-0.0.0: sources/Workflow/Windows/Execute.hs

{-# LANGUAGE LambdaCase, ScopedTypeVariables, FlexibleContexts, ViewPatterns #-}
{-|

high-level bindings.

Glue between "Workflow.Types" and "Workflow.Windows.Types".

-}
module Workflow.Windows.Execute where
import Workflow.Windows.Constants
import Workflow.Windows.Bindings as Win32
import Workflow.Windows.Types
import Workflow.Windows.Extra
import Workflow.Types hiding (Application,URL)

import Control.Monad.Free
import Control.Monad.Trans.Free hiding (Pure, Free, iterM) -- TODO

import Numeric.Natural
import Control.Monad.IO.Class


runWorkflow :: Workflow a -> IO a
runWorkflow = runWorkflowT . toFreeT

{-| eliminate a 'WorkflowT' layer.

e.g. some custom monad:

@
newtype W a = W
 { getW :: WorkflowT IO a
 } deriving
 ( MonadWorkflow
 , MonadIO
 , Monad
 , Applicative
 , Functor
 )
@

specializing:

@
runW :: W a -> IO a
runW = 'runWorkflowT' . getW
@

-}
runWorkflowT :: forall m a. (MonadIO m) => WorkflowT m a -> m a
runWorkflowT = iterT go
 where
 go :: WorkflowF (m a) -> m a
 go = \case

  SendKeyChord    modifiers key k  -> sendKeyChord_Win32 modifiers key >> k
  SendText        s k              -> Win32.sendText s >> k
  -- TODO support Unicode by inserting "directly"
  -- terminates because sendTextAsKeypresses is exclusively a sequence of SendKeyChord'es
  -- TODO SendMouseClick  flags n button k ->
  SendMouseClick    flags n button k -> clickMouse_Win32 flags n button >> k
  SendMouseScroll   flags scroll n k -> scrollMouse_Win32 flags scroll n >> k

  GetClipboard    f                -> Win32.getClipboard >>= f
  SetClipboard    s k              -> Win32.setClipboard s >> k

  CurrentApplication f             -> Win32.currentApplication >>= (getApplication >>> f)
  OpenApplication app k            -> Win32.openApplication (Application app) >> k
  OpenURL         url k            -> Win32.openUrl (URL url) >> k

  Delay           t k              -> delayMilliseconds t >> k
 -- 1,000 µs is 1ms

-----------------------------------------------------------------------------------------

clickMouse_Win32
 :: (MonadIO m) => [Modifier] -> Natural -> MouseButton -> m ()
clickMouse_Win32 modifiers n button = liftIO $ do
  POINT x y <- getCursorPosition --TODO Point has Cursor case
  holdingKeys (fromModifier <$> modifiers) $ do
      clickMouseAt_Win32 button n (x,y)

clickMouseAt_Win32
 :: (MonadIO m) => MouseButton -> Natural -> (LONG,LONG) -> m ()
clickMouseAt_Win32 (encodeMouseButton -> (down, up)) n (x,y)
 = Win32.clickMouseAt (POINT x y) n down up
--TODO type for screen coordinates. more than point. abs/rel. (bounded instance for rel).

scrollMouse_Win32
 :: (MonadIO m) => [Modifier] -> MouseScroll -> Natural -> m ()
scrollMouse_Win32 modifiers (encodeMouseScroll -> (wheel, direction)) n
 = liftIO $ holdingKeys (fromModifier <$> modifiers) $ do
     Win32.scrollMouse wheel direction n

{-|

@
(wheel, direction) = encodeMouseScroll
@

-}
encodeMouseScroll :: MouseScroll -> (MOUSEEVENTF, DWORD)
encodeMouseScroll = \case
    ScrollTowards -> (MOUSEEVENTF_WHEEL,   1)
    ScrollAway    -> (MOUSEEVENTF_WHEEL,  -1)
    ScrollRight   -> (MOUSEEVENTF_HWHEEL,  1)
    ScrollLeft    -> (MOUSEEVENTF_HWHEEL, -1)

{-|

@
(downEvent, upEvent) = encodeMouseButton
@

-}
encodeMouseButton :: MouseButton -> (MOUSEEVENTF,MOUSEEVENTF)
encodeMouseButton = \case
 LeftButton   -> (MOUSEEVENTF_LEFTDOWN   , MOUSEEVENTF_LEFTUP)
 MiddleButton -> (MOUSEEVENTF_MIDDLEDOWN , MOUSEEVENTF_MIDDLEUP)
 RightButton  -> (MOUSEEVENTF_RIGHTDOWN  , MOUSEEVENTF_RIGHTUP)
 -- XButton      -> (MOUSEEVENTF_XDOWN      , MOUSEEVENTF_XUP)

-----------------------------------------------------------------------------------------

sendKeyChord_Win32 :: (MonadIO m) => [Modifier] -> Key -> m ()
sendKeyChord_Win32 modifiers key
 = Win32.pressKeyChord (fromModifier <$> modifiers) (fromKey key)

{-|

-}
fromModifier :: Modifier -> VK
fromModifier = \case
-- "virtual, virtual" modifiers
 MetaModifier     -> VK_MENU
 HyperModifier    -> VK_CONTROL
 -- "actual, virtual" modifiers
 ControlModifier  -> VK_CONTROL
 OptionModifier   -> VK_MENU
 ShiftModifier    -> VK_SHIFT
 FunctionModifier -> VK_FN

{-|

-}
fromKey :: Key -> VK
fromKey = \case

 -- "virtual, virtual" keys

 MetaKey -> VK_MENU
 HyperKey -> VK_CONTROL

 -- "actual, virtual" keys

 ControlKey -> VK_CONTROL
 CapsLockKey -> VK_CAPITAL
 ShiftKey -> VK_SHIFT
 OptionKey -> VK_MENU
 FunctionKey -> VK_FN

 GraveKey -> VK_OEM_3
 MinusKey -> VK_OEM_MINUS
 EqualKey -> VK_OEM_PLUS
 DeleteKey -> VK_BACK
 ForwardDeleteKey -> VK_DELETE
 LeftBracketKey -> VK_OEM_4
 RightBracketKey -> VK_OEM_6
 BackslashKey -> VK_OEM_5
 SemicolonKey -> VK_OEM_1
 QuoteKey -> VK_OEM_7
 CommaKey -> VK_OEM_COMMA
 PeriodKey -> VK_OEM_PERIOD
 SlashKey -> VK_OEM_2

 TabKey -> VK_TAB
 SpaceKey -> VK_SPACE
 ReturnKey -> VK_RETURN

 LeftArrowKey -> VK_LEFT
 RightArrowKey -> VK_RIGHT
 DownArrowKey -> VK_DOWN
 UpArrowKey -> VK_UP

 AKey -> VK_A
 BKey -> VK_B
 CKey -> VK_C
 DKey -> VK_D
 EKey -> VK_E
 FKey -> VK_F
 GKey -> VK_G
 HKey -> VK_H
 IKey -> VK_I
 JKey -> VK_J
 KKey -> VK_K
 LKey -> VK_L
 MKey -> VK_M
 NKey -> VK_N
 OKey -> VK_O
 PKey -> VK_P
 QKey -> VK_Q
 RKey -> VK_R
 SKey -> VK_S
 TKey -> VK_T
 UKey -> VK_U
 VKey -> VK_V
 WKey -> VK_W
 XKey -> VK_X
 YKey -> VK_Y
 ZKey -> VK_Z

 ZeroKey -> VK_0
 OneKey -> VK_1
 TwoKey -> VK_2
 ThreeKey -> VK_3
 FourKey -> VK_4
 FiveKey -> VK_5
 SixKey -> VK_6
 SevenKey -> VK_7
 EightKey -> VK_8
 NineKey -> VK_9

 EscapeKey -> VK_ESCAPE
 F1Key -> VK_F1
 F2Key -> VK_F2
 F3Key -> VK_F3
 F4Key -> VK_F4
 F5Key -> VK_F5
 F6Key -> VK_F6
 F7Key -> VK_F7
 F8Key -> VK_F8
 F9Key -> VK_F9
 F10Key -> VK_F10
 F11Key -> VK_F11
 F12Key -> VK_F12
 F13Key -> VK_F13
 F14Key -> VK_F14
 F15Key -> VK_F15
 F16Key -> VK_F16
 F17Key -> VK_F17
 F18Key -> VK_F18
 F19Key -> VK_F19
 F20Key -> VK_F20

--------------------------------------------------------------------------------