packages feed

robot (empty) → 1.0

raw patch · 13 files changed

+1504/−0 lines, 13 filesdep +basedep +containersdep +transformerssetup-changed

Dependencies added: base, containers, transformers, xhb

Files

+ LICENSE view
@@ -0,0 +1,202 @@++                                 Apache License+                           Version 2.0, January 2004+                        http://www.apache.org/licenses/++   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION++   1. Definitions.++      "License" shall mean the terms and conditions for use, reproduction,+      and distribution as defined by Sections 1 through 9 of this document.++      "Licensor" shall mean the copyright owner or entity authorized by+      the copyright owner that is granting the License.++      "Legal Entity" shall mean the union of the acting entity and all+      other entities that control, are controlled by, or are under common+      control with that entity. For the purposes of this definition,+      "control" means (i) the power, direct or indirect, to cause the+      direction or management of such entity, whether by contract or+      otherwise, or (ii) ownership of fifty percent (50%) or more of the+      outstanding shares, or (iii) beneficial ownership of such entity.++      "You" (or "Your") shall mean an individual or Legal Entity+      exercising permissions granted by this License.++      "Source" form shall mean the preferred form for making modifications,+      including but not limited to software source code, documentation+      source, and configuration files.++      "Object" form shall mean any form resulting from mechanical+      transformation or translation of a Source form, including but+      not limited to compiled object code, generated documentation,+      and conversions to other media types.++      "Work" shall mean the work of authorship, whether in Source or+      Object form, made available under the License, as indicated by a+      copyright notice that is included in or attached to the work+      (an example is provided in the Appendix below).++      "Derivative Works" shall mean any work, whether in Source or Object+      form, that is based on (or derived from) the Work and for which the+      editorial revisions, annotations, elaborations, or other modifications+      represent, as a whole, an original work of authorship. For the purposes+      of this License, Derivative Works shall not include works that remain+      separable from, or merely link (or bind by name) to the interfaces of,+      the Work and Derivative Works thereof.++      "Contribution" shall mean any work of authorship, including+      the original version of the Work and any modifications or additions+      to that Work or Derivative Works thereof, that is intentionally+      submitted to Licensor for inclusion in the Work by the copyright owner+      or by an individual or Legal Entity authorized to submit on behalf of+      the copyright owner. For the purposes of this definition, "submitted"+      means any form of electronic, verbal, or written communication sent+      to the Licensor or its representatives, including but not limited to+      communication on electronic mailing lists, source code control systems,+      and issue tracking systems that are managed by, or on behalf of, the+      Licensor for the purpose of discussing and improving the Work, but+      excluding communication that is conspicuously marked or otherwise+      designated in writing by the copyright owner as "Not a Contribution."++      "Contributor" shall mean Licensor and any individual or Legal Entity+      on behalf of whom a Contribution has been received by Licensor and+      subsequently incorporated within the Work.++   2. Grant of Copyright License. Subject to the terms and conditions of+      this License, each Contributor hereby grants to You a perpetual,+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable+      copyright license to reproduce, prepare Derivative Works of,+      publicly display, publicly perform, sublicense, and distribute the+      Work and such Derivative Works in Source or Object form.++   3. Grant of Patent License. Subject to the terms and conditions of+      this License, each Contributor hereby grants to You a perpetual,+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable+      (except as stated in this section) patent license to make, have made,+      use, offer to sell, sell, import, and otherwise transfer the Work,+      where such license applies only to those patent claims licensable+      by such Contributor that are necessarily infringed by their+      Contribution(s) alone or by combination of their Contribution(s)+      with the Work to which such Contribution(s) was submitted. If You+      institute patent litigation against any entity (including a+      cross-claim or counterclaim in a lawsuit) alleging that the Work+      or a Contribution incorporated within the Work constitutes direct+      or contributory patent infringement, then any patent licenses+      granted to You under this License for that Work shall terminate+      as of the date such litigation is filed.++   4. Redistribution. You may reproduce and distribute copies of the+      Work or Derivative Works thereof in any medium, with or without+      modifications, and in Source or Object form, provided that You+      meet the following conditions:++      (a) You must give any other recipients of the Work or+          Derivative Works a copy of this License; and++      (b) You must cause any modified files to carry prominent notices+          stating that You changed the files; and++      (c) You must retain, in the Source form of any Derivative Works+          that You distribute, all copyright, patent, trademark, and+          attribution notices from the Source form of the Work,+          excluding those notices that do not pertain to any part of+          the Derivative Works; and++      (d) If the Work includes a "NOTICE" text file as part of its+          distribution, then any Derivative Works that You distribute must+          include a readable copy of the attribution notices contained+          within such NOTICE file, excluding those notices that do not+          pertain to any part of the Derivative Works, in at least one+          of the following places: within a NOTICE text file distributed+          as part of the Derivative Works; within the Source form or+          documentation, if provided along with the Derivative Works; or,+          within a display generated by the Derivative Works, if and+          wherever such third-party notices normally appear. The contents+          of the NOTICE file are for informational purposes only and+          do not modify the License. You may add Your own attribution+          notices within Derivative Works that You distribute, alongside+          or as an addendum to the NOTICE text from the Work, provided+          that such additional attribution notices cannot be construed+          as modifying the License.++      You may add Your own copyright statement to Your modifications and+      may provide additional or different license terms and conditions+      for use, reproduction, or distribution of Your modifications, or+      for any such Derivative Works as a whole, provided Your use,+      reproduction, and distribution of the Work otherwise complies with+      the conditions stated in this License.++   5. Submission of Contributions. Unless You explicitly state otherwise,+      any Contribution intentionally submitted for inclusion in the Work+      by You to the Licensor shall be under the terms and conditions of+      this License, without any additional terms or conditions.+      Notwithstanding the above, nothing herein shall supersede or modify+      the terms of any separate license agreement you may have executed+      with Licensor regarding such Contributions.++   6. Trademarks. This License does not grant permission to use the trade+      names, trademarks, service marks, or product names of the Licensor,+      except as required for reasonable and customary use in describing the+      origin of the Work and reproducing the content of the NOTICE file.++   7. Disclaimer of Warranty. Unless required by applicable law or+      agreed to in writing, Licensor provides the Work (and each+      Contributor provides its Contributions) on an "AS IS" BASIS,+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or+      implied, including, without limitation, any warranties or conditions+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A+      PARTICULAR PURPOSE. You are solely responsible for determining the+      appropriateness of using or redistributing the Work and assume any+      risks associated with Your exercise of permissions under this License.++   8. Limitation of Liability. In no event and under no legal theory,+      whether in tort (including negligence), contract, or otherwise,+      unless required by applicable law (such as deliberate and grossly+      negligent acts) or agreed to in writing, shall any Contributor be+      liable to You for damages, including any direct, indirect, special,+      incidental, or consequential damages of any character arising as a+      result of this License or out of the use or inability to use the+      Work (including but not limited to damages for loss of goodwill,+      work stoppage, computer failure or malfunction, or any and all+      other commercial damages or losses), even if such Contributor+      has been advised of the possibility of such damages.++   9. Accepting Warranty or Additional Liability. While redistributing+      the Work or Derivative Works thereof, You may choose to offer,+      and charge a fee for, acceptance of support, warranty, indemnity,+      or other liability obligations and/or rights consistent with this+      License. However, in accepting such obligations, You may act only+      on Your own behalf and on Your sole responsibility, not on behalf+      of any other Contributor, and only if You agree to indemnify,+      defend, and hold each Contributor harmless for any liability+      incurred by, or claims asserted against, such Contributor by reason+      of your accepting any such warranty or additional liability.++   END OF TERMS AND CONDITIONS++   APPENDIX: How to apply the Apache License to your work.++      To apply the Apache License to your work, attach the following+      boilerplate notice, with the fields enclosed by brackets "[]"+      replaced with your own identifying information. (Don't include+      the brackets!)  The text should be enclosed in the appropriate+      comment syntax for the file format. We also recommend that a+      file or class name and description of purpose be included on the+      same "printed page" as the copyright notice for easier+      identification within third-party archives.++   Copyright [yyyy] [name of copyright owner]++   Licensed under the Apache License, Version 2.0 (the "License");+   you may not use this file except in compliance with the License.+   You may obtain a copy of the License at++       http://www.apache.org/licenses/LICENSE-2.0++   Unless required by applicable law or agreed to in writing, software+   distributed under the License is distributed on an "AS IS" BASIS,+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+   See the License for the specific language governing permissions and+   limitations under the License.
+ README.markdown view
@@ -0,0 +1,34 @@+The Haskell Robot+=================++**Robot** is a library for sending fake keyboard and mouse events using+[Haskell][]. Applications will respond as though the user performed the+actions themselves.++[Haskell]: http://haskell.org/++Only X11 systems are supported right now, but Windows and Mac can be+added later if anyone cares.++Here are some things you can do:++* Test graphical interfaces and websites!++* Play silly pranks on your friends!++* Finally beat those [silly Flash games where you press the same button+  over and over again really fast][blah blah blah]. I don't like those.++[blah blah blah]: http://mixermike622.deviantart.com/art/GO-FAST-287788805+++Quick start+-----------++First, download and install the library:++    git clone https://github.com/lfairy/robot.git+    cd robot+    cabal install++Now try running some of the programs in the `examples` directory.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ Test/Robot.hs view
@@ -0,0 +1,89 @@+-- | The main Robot interface.++module Test.Robot+    (+      -- * Running your robot+      Robot()  -- hide implementation+    , runRobot+    , runRobotWithConnection++      -- * Key and button constants+    , module Test.Robot.Types++      -- * Doing things+      -- ** Basic operations+    , Pressable(press, release)+    , moveBy+    , moveTo+      -- ** Derived operations+    , hold+    , tap++      -- * Fancy utilities+    , sleep++    ) where+++import Control.Applicative+import Control.Concurrent (threadDelay)+import Control.Monad.IO.Class++import Test.Robot.Internal+import Test.Robot.Types+++-- | Represents things that can be pressed: either a key on a keyboard+-- or a button on a mouse.+class Pressable x where++    -- | Press a key or button.+    press :: x -> Robot ()++    -- | Release a key or button.+    release :: x -> Robot ()++instance Pressable Key where+    press = keyboard True+    release = keyboard False++instance Pressable Button where+    press = button True+    release = button False+++-- | Move the pointer by an offset.+moveBy :: Int -> Int -> Robot ()+moveBy = motion True++-- | Move the pointer to a point on the screen.+moveTo :: Int -> Int -> Robot ()+moveTo = motion False+++-- | @hold x act@ holds down @x@ while executing @act@. It is equivalent+-- to the code:+--+-- @+-- press x+-- act+-- release x+-- @+--+-- For example, you type some text in ALL CAPS using:+--+-- @+-- hold _Shift $ mapM_ tap [_D, _U, _C, _K, _S]+-- @+--+hold :: Pressable x => x -> Robot a -> Robot a+hold = bracketRobot_ <$> press <*> release++-- | Press the argument, then release it.+tap :: Pressable x => x -> Robot ()+tap = (`hold` return ())+++-- | Do nothing for the specified number of seconds.+sleep :: Rational -> Robot ()+sleep = liftIO . threadDelay . round . (* 1000000)
+ Test/Robot/Internal.hs view
@@ -0,0 +1,101 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}++-- | Miscellaneous low-level functions. You should rarely need to use+-- these directly.++module Test.Robot.Internal+    (+      -- * The Robot monad+      Robot(..)+    , runRobot+    , runRobotWithConnection+    , mkRobot+    , mkRobot'++      -- * Exception safety+    , bracketRobot_++      -- * Synthesizing events+    , keyboard+    , button+    , motion+    , releaseAll++    ) where+++import Control.Applicative+import Control.Exception (bracket_, finally)+import Control.Monad.IO.Class+import Control.Monad.Trans.Reader+import Data.Map (Map)+import qualified Data.Map as M++import Graphics.XHB++import qualified Test.Robot.Internal.XTest as X+import Test.Robot.Types+++-- | The Robot monad: a reader monad over IO.+newtype Robot a = Robot { unRobot :: ReaderT (Connection, Map KEYSYM KEYCODE) IO a }+    deriving (Functor, Applicative, Monad, MonadIO)++-- | Run the robot, connecting to the display automatically.+runRobot :: Robot a -> IO a+runRobot m = do+    Just c <- connect+    runRobotWithConnection m c++-- | Run the robot using an existing connection.+runRobotWithConnection :: Robot a -> Connection -> IO a+runRobotWithConnection (Robot m) c = do+    keymap <- X.getKeysymMap c+    runReaderT m (c, keymap) `finally` X.releaseAll c++mkRobot :: ((Connection, Map KEYSYM KEYCODE) -> IO a) -> Robot a+mkRobot = Robot . ReaderT++mkRobot' :: (Connection -> IO a) -> Robot a+mkRobot' = mkRobot . (. fst)+++bracketRobot_ :: Robot a -> Robot z -> Robot r -> Robot r+bracketRobot_ (Robot before) (Robot after) (Robot middle)+    = mkRobot $ \env -> let run = flip runReaderT env+                        in bracket_ (run before) (run after) (run middle)+++keyboard :: Bool -> Key -> Robot ()+keyboard press key = mkRobot $ \(c, keymap) -> do+    case M.lookup (rawKey key) keymap of+        Nothing -> error $ "Unknown keysym: " ++ show (rawKey key)+        Just keycode -> X.keyboard c press keycode++button :: Bool -> Button -> Robot ()+button press butt {- LOL -} = mkRobot' $ \c ->+    X.button c press (rawButton butt)++motion :: Bool -> Int -> Int -> Robot ()+motion press x y = mkRobot' $ \c ->+    X.motion c press (clipIntegral x, clipIntegral y)++-- | @clipIntegral x :: T@ converts the value @x@ to type @T@.+-- If the argument does not fit in @T@, it is clipped, rather than+-- wrapping around as with 'fromIntegral'.+--+-- The range of the result type must be a subset of the argument type.+-- If not, the behavior of this function is undefined.+--+clipIntegral :: (Integral a, Integral b, Ord a, Bounded b) => a -> b+clipIntegral x = result+  where+    result+      | x < fromIntegral (minBound `asTypeOf` result) = minBound+      | x > fromIntegral (maxBound `asTypeOf` result) = maxBound+      | otherwise = fromIntegral x++-- | Release all the keys and buttons, in case some were left held down.+-- This is called automatically before 'runRobot' returns.+releaseAll :: Robot ()+releaseAll = mkRobot' $ \c -> X.releaseAll c
+ Test/Robot/Internal/XTest.hs view
@@ -0,0 +1,115 @@+-- | Low-level wrappers around the XTest protocol.++module Test.Robot.Internal.XTest+    (+    -- * XTest primitives+      keyboard+    , button+    , motion+    , releaseAll++    -- * Utility functions+    , getKeysymMap++    ) where++import Control.Arrow (second)+import Control.Applicative (liftA2)+import Control.Monad (forM_, (>=>))+import Data.List (unfoldr)+import Data.Map (Map)+import qualified Data.Map as M+import Data.Tuple (swap)+import Data.Word (Word16)++import Graphics.XHB+import Graphics.XHB.Gen.Test+++keyboard :: Connection -> Bool -> KEYCODE -> IO ()+keyboard c press keycode =+    let eventType = if press then 2 else 3+        req = MkFakeInput eventType keycode 0 noWindow 0 0 0+    in fakeInput c req+++button :: Connection -> Bool -> BUTTON -> IO ()+button c press butt =+    let eventType = if press then 4 else 5+        req = MkFakeInput eventType butt 0 noWindow 0 0 0+    in fakeInput c req+++motion :: Connection -> Bool -> (Word16, Word16) -> IO ()+motion c relative (x, y) =+    let eventType = 6+        isRelative = if relative then 1 else 0+        req = MkFakeInput eventType isRelative 0 noWindow x y 0+    in fakeInput c req+++noWindow :: WINDOW+noWindow = fromXid xidNone+++-- | Release all the keys and buttons, in case some were left held down.+releaseAll :: Connection -> IO ()+releaseAll c = do+    let (low, high) = keycodeRange c+    forM_ [low..high] $ keyboard c False+    -- There's no limit to the number of buttons, afaik. So hazard a guess.+    forM_ [1..42] $ button c False+++-- | Return a structure mapping each keycode to its keysyms.+getKeyboardMap :: Connection -> IO (Map KEYCODE [KEYSYM])+getKeyboardMap c = do+    let (low, high) = keycodeRange c+    MkGetKeyboardMappingReply+        { keysyms_per_keycode_GetKeyboardMappingReply = blockSize+        , keysyms_GetKeyboardMappingReply = rawKeysyms }+            <- getKeyboardMapping c low (high-low+1) >>= getReply'+    return . M.fromList $ zip [low..high]+                                (chunksOf (fromIntegral blockSize) rawKeysyms)+++-- | Like 'getReply', but calls @error@ instead of returning @Left@.+getReply' :: Receipt a -> IO a+getReply' = getReply >=> either (error . show) return+++-- | Split the input list into chunks of size @k@. The last chunk may be+-- shorter than the other chunks, depending on the length of the input.+-- Exammples:+--+-- > chunksOf 2 [1..6] == [[1, 2], [3, 4], [5, 6]]+-- > chunksOf 3 "Hello, world!" == ["Hel", "lo,", " wo", "rld", "!"]+--+chunksOf :: Int -> [a] -> [[a]]+chunksOf k = unfoldr $ \xs -> case xs of+    [] -> Nothing+    _  -> Just (splitAt k xs)+++-- | Return a structure mapping keysyms to their keycode.+getKeysymMap :: Connection -> IO (Map KEYSYM KEYCODE)+getKeysymMap = fmap flipTable . getKeyboardMap+++flipTable :: Map KEYCODE [KEYSYM] -> Map KEYSYM KEYCODE+flipTable+    = M.fromList+    . map swap+    . concatMap expandEntry+    . removeZeroes+    . M.toList+  where+    removeZeroes = map (second (filter (/= 0)))++    expandEntry :: (a, [b]) -> [(a, b)]+    expandEntry (x, ys) = zip (repeat x) ys+++keycodeRange :: Connection -> (KEYCODE, KEYCODE)+keycodeRange+    = liftA2 (,) min_keycode_Setup max_keycode_Setup . connectionSetup
+ Test/Robot/Types.hs view
@@ -0,0 +1,61 @@+-- | Defines the 'Key' and 'Button' types, along with a list of+-- constants of those types.++module Test.Robot.Types+    (+      -- * Types+      Key()  -- Hide implementation+    , Button()++      -- ** Creating+      -- $custom+    , customKey+    , customButton++      -- ** Extracting+    , rawKey+    , rawButton++      -- * Constants+      -- ** Buttons+    , leftButton, middleButton, rightButton+    , scrollUp, scrollDown, scrollLeft, scrollRight+    , backButton, forwardButton++    , module Test.Robot.Types.Keys++    ) where++import Graphics.XHB++import Test.Robot.Types.Core+import Test.Robot.Types.Keys++-- $custom+-- These two functions let you define your own keys and buttons.+--+-- 'customKey' requires a magic number (/keysym/) as its argument. On+-- most Unix systems, you can look up common keysyms in the file+-- @\/usr\/include\/X11\/keysymdef.h@.+--+-- For example, the pound sign @£@ is listed as:+--+-- > #define XK_sterling                      0x00a3  /* U+00A3 POUND SIGN */+--+-- so to input this key, we use @customKey 0x00a3@.+--+-- Note: this feature only works if the key actually exists on your+-- keyboard. If not, then using the result will raise an exception.++customKey :: KEYSYM -> Key+customKey = Key++customButton :: BUTTON -> Button+customButton = Button++leftButton, middleButton, rightButton :: Button+scrollUp, scrollDown, scrollLeft, scrollRight :: Button+backButton, forwardButton :: Button+[leftButton, middleButton, rightButton,+    scrollUp, scrollDown, scrollLeft, scrollRight,+    backButton, forwardButton] = map Button [1..9]
+ Test/Robot/Types/Core.hs view
@@ -0,0 +1,18 @@+module Test.Robot.Types.Core where++import Graphics.XHB (BUTTON, KEYSYM)++-- | A key on the keyboard.+newtype Key = Key+    { -- | Retrieve the internal identifier for this key.+      rawKey :: KEYSYM }+    deriving (Eq, Ord, Read, Show)++-- | A mouse button or scroll wheel.+--+-- Note that the scroll wheel is treated as two separate buttons: one+-- scrolling up, and one scrolling down.+newtype Button = Button+    { -- | Retrieve the internal identifier for this button.+      rawButton :: BUTTON }+    deriving (Eq, Ord, Read, Show)
+ Test/Robot/Types/Keys.hs view
@@ -0,0 +1,569 @@+{-# OPTIONS_GHC -fno-warn-missing-signatures #-}++-- | This module is automatically generated from the file @keys.txt@,+-- and should not be edited directly. If you want to add a keysym, refer+-- to that file instead.++module Test.Robot.Types.Keys+    (+      -- ** Control sequences+      _Backspace+    , _Tab+    , _LineFeed+    , _Clear+    , _Return+    , _Pause+    , _ScrollLock+    , _SysReq+    , _Escape+    , _Delete++      -- ** Cursor control and motion+    , _Home+    , _Left+    , _Up+    , _Right+    , _Down+    , _Prior+    , _PageUp+    , _Next+    , _PageDown+    , _End+    , _Begin++      -- ** Miscellaneous functions+    , _Select+    , _Print+    , _Execute+    , _Insert+    , _Undo+    , _Redo+    , _Menu+    , _Find+    , _Cancel+    , _Help+    , _Break+    , _ModeSwitch+    , _ScriptSwitch+    , _NumLock++      -- ** Keypad functions+    , _KP_Space+    , _KP_Tab+    , _KP_Enter+    , _KP_F1+    , _KP_F2+    , _KP_F3+    , _KP_F4+    , _KP_Home+    , _KP_Left+    , _KP_Up+    , _KP_Right+    , _KP_Down+    , _KP_Prior+    , _KP_PageUp+    , _KP_Next+    , _KP_PageDown+    , _KP_End+    , _KP_Begin+    , _KP_Insert+    , _KP_Delete+    , _KP_Multiply+    , _KP_Add+    , _KP_Separator+    , _KP_Subtract+    , _KP_Decimal+    , _KP_Divide+    , _KP_Equal++      -- ** Keypad numbers+    , _KP_0+    , _KP_1+    , _KP_2+    , _KP_3+    , _KP_4+    , _KP_5+    , _KP_6+    , _KP_7+    , _KP_8+    , _KP_9++      -- ** Function keys+    , _F1+    , _F2+    , _F3+    , _F4+    , _F5+    , _F6+    , _F7+    , _F8+    , _F9+    , _F10+    , _F11+    , _F12+    , _F13+    , _F14+    , _F15+    , _F16+    , _F17+    , _F18+    , _F19++      -- ** Modifiers+    , _Shift+    , _Shift_L+    , _Shift_R+    , _Ctrl+    , _Control+    , _Control_L+    , _Control_R+    , _CapsLock+    , _ShiftLock++      -- ** More modifiers+    , _Meta+    , _Meta_L+    , _Meta_R+    , _Alt+    , _Alt_L+    , _Alt_R+    , _Super+    , _Super_L+    , _Super_R+    , _Hyper+    , _Hyper_L+    , _Hyper_R++      -- ** Letters, numbers and symbols+    , _Space+    , _Exclamation+    , _Quotation+    , _NumberSign+    , _Dollar+    , _Percent+    , _Ampersand+    , _Apostrophe+    , _ParenLeft+    , _ParenRight+    , _Asterisk+    , _Plus+    , _Comma+    , _Minus+    , _Period+    , _Slash+    , _0+    , _1+    , _2+    , _3+    , _4+    , _5+    , _6+    , _7+    , _8+    , _9+    , _Colon+    , _Semicolon+    , _Less+    , _Equal+    , _Greater+    , _Question+    , _At+    , _A+    , _B+    , _C+    , _D+    , _E+    , _F+    , _G+    , _H+    , _I+    , _J+    , _K+    , _L+    , _M+    , _N+    , _O+    , _P+    , _Q+    , _R+    , _S+    , _T+    , _U+    , _V+    , _W+    , _X+    , _Y+    , _Z+    , _BracketLeft+    , _Backslash+    , _BracketRight+    , _Circumflex+    , _Underscore+    , _Grave+    , _BraceLeft+    , _Bar+    , _BraceRight+    , _Tilde++    ) where++import Test.Robot.Types.Core++_Backspace :: Key+_Backspace                     = Key 0xff08+_Tab :: Key+_Tab                           = Key 0xff09+_LineFeed :: Key+_LineFeed                      = Key 0xff0a+_Clear :: Key+_Clear                         = Key 0xff0b+_Return :: Key+_Return                        = Key 0xff0d+_Pause :: Key+_Pause                         = Key 0xff13+_ScrollLock :: Key+_ScrollLock                    = Key 0xff14+_SysReq :: Key+_SysReq                        = Key 0xff15+_Escape :: Key+_Escape                        = Key 0xff1b+_Delete :: Key+_Delete                        = Key 0xffff++_Home :: Key+_Home                          = Key 0xff50+_Left :: Key+_Left                          = Key 0xff51+_Up :: Key+_Up                            = Key 0xff52+_Right :: Key+_Right                         = Key 0xff53+_Down :: Key+_Down                          = Key 0xff54+_Prior, _PageUp :: Key+_Prior                         = Key 0xff55+_PageUp                        = Key 0xff55+_Next, _PageDown :: Key+_Next                          = Key 0xff56+_PageDown                      = Key 0xff56+_End :: Key+_End                           = Key 0xff57+_Begin :: Key+_Begin                         = Key 0xff58++_Select :: Key+_Select                        = Key 0xff60+_Print :: Key+_Print                         = Key 0xff61+_Execute :: Key+_Execute                       = Key 0xff62+_Insert :: Key+_Insert                        = Key 0xff63+_Undo :: Key+_Undo                          = Key 0xff65+_Redo :: Key+_Redo                          = Key 0xff66+_Menu :: Key+_Menu                          = Key 0xff67+_Find :: Key+_Find                          = Key 0xff68+_Cancel :: Key+_Cancel                        = Key 0xff69+_Help :: Key+_Help                          = Key 0xff6a+_Break :: Key+_Break                         = Key 0xff6b+_ModeSwitch, _ScriptSwitch :: Key+_ModeSwitch                    = Key 0xff7e+_ScriptSwitch                  = Key 0xff7e+_NumLock :: Key+_NumLock                       = Key 0xff7f++_KP_Space :: Key+_KP_Space                      = Key 0xff80+_KP_Tab :: Key+_KP_Tab                        = Key 0xff89+_KP_Enter :: Key+_KP_Enter                      = Key 0xff8d+_KP_F1 :: Key+_KP_F1                         = Key 0xff91+_KP_F2 :: Key+_KP_F2                         = Key 0xff92+_KP_F3 :: Key+_KP_F3                         = Key 0xff93+_KP_F4 :: Key+_KP_F4                         = Key 0xff94+_KP_Home :: Key+_KP_Home                       = Key 0xff95+_KP_Left :: Key+_KP_Left                       = Key 0xff96+_KP_Up :: Key+_KP_Up                         = Key 0xff97+_KP_Right :: Key+_KP_Right                      = Key 0xff98+_KP_Down :: Key+_KP_Down                       = Key 0xff99+_KP_Prior, _KP_PageUp :: Key+_KP_Prior                      = Key 0xff9a+_KP_PageUp                     = Key 0xff9a+_KP_Next, _KP_PageDown :: Key+_KP_Next                       = Key 0xff9b+_KP_PageDown                   = Key 0xff9b+_KP_End :: Key+_KP_End                        = Key 0xff9c+_KP_Begin :: Key+_KP_Begin                      = Key 0xff9d+_KP_Insert :: Key+_KP_Insert                     = Key 0xff9e+_KP_Delete :: Key+_KP_Delete                     = Key 0xff9f+_KP_Multiply :: Key+_KP_Multiply                   = Key 0xffaa+_KP_Add :: Key+_KP_Add                        = Key 0xffab+_KP_Separator :: Key+_KP_Separator                  = Key 0xffac+_KP_Subtract :: Key+_KP_Subtract                   = Key 0xffad+_KP_Decimal :: Key+_KP_Decimal                    = Key 0xffae+_KP_Divide :: Key+_KP_Divide                     = Key 0xffaf+_KP_Equal :: Key+_KP_Equal                      = Key 0xffbd++_KP_0 :: Key+_KP_0                          = Key 0xffb0+_KP_1 :: Key+_KP_1                          = Key 0xffb1+_KP_2 :: Key+_KP_2                          = Key 0xffb2+_KP_3 :: Key+_KP_3                          = Key 0xffb3+_KP_4 :: Key+_KP_4                          = Key 0xffb4+_KP_5 :: Key+_KP_5                          = Key 0xffb5+_KP_6 :: Key+_KP_6                          = Key 0xffb6+_KP_7 :: Key+_KP_7                          = Key 0xffb7+_KP_8 :: Key+_KP_8                          = Key 0xffb8+_KP_9 :: Key+_KP_9                          = Key 0xffb9++_F1 :: Key+_F1                            = Key 0xffbe+_F2 :: Key+_F2                            = Key 0xffbf+_F3 :: Key+_F3                            = Key 0xffc0+_F4 :: Key+_F4                            = Key 0xffc1+_F5 :: Key+_F5                            = Key 0xffc2+_F6 :: Key+_F6                            = Key 0xffc3+_F7 :: Key+_F7                            = Key 0xffc4+_F8 :: Key+_F8                            = Key 0xffc5+_F9 :: Key+_F9                            = Key 0xffc6+_F10 :: Key+_F10                           = Key 0xffc7+_F11 :: Key+_F11                           = Key 0xffc8+_F12 :: Key+_F12                           = Key 0xffc9+_F13 :: Key+_F13                           = Key 0xffca+_F14 :: Key+_F14                           = Key 0xffcb+_F15 :: Key+_F15                           = Key 0xffcc+_F16 :: Key+_F16                           = Key 0xffcd+_F17 :: Key+_F17                           = Key 0xffce+_F18 :: Key+_F18                           = Key 0xffcf+_F19 :: Key+_F19                           = Key 0xffd0++_Shift, _Shift_L :: Key+_Shift                         = Key 0xffe1+_Shift_L                       = Key 0xffe1+_Shift_R :: Key+_Shift_R                       = Key 0xffe2+_Ctrl, _Control, _Control_L :: Key+_Ctrl                          = Key 0xffe3+_Control                       = Key 0xffe3+_Control_L                     = Key 0xffe3+_Control_R :: Key+_Control_R                     = Key 0xffe4+_CapsLock :: Key+_CapsLock                      = Key 0xffe5+_ShiftLock :: Key+_ShiftLock                     = Key 0xffe6++_Meta, _Meta_L :: Key+_Meta                          = Key 0xffe7+_Meta_L                        = Key 0xffe7+_Meta_R :: Key+_Meta_R                        = Key 0xffe8+_Alt, _Alt_L :: Key+_Alt                           = Key 0xffe9+_Alt_L                         = Key 0xffe9+_Alt_R :: Key+_Alt_R                         = Key 0xffea+_Super, _Super_L :: Key+_Super                         = Key 0xffeb+_Super_L                       = Key 0xffeb+_Super_R :: Key+_Super_R                       = Key 0xffec+_Hyper, _Hyper_L :: Key+_Hyper                         = Key 0xffed+_Hyper_L                       = Key 0xffed+_Hyper_R :: Key+_Hyper_R                       = Key 0xffee++_Space :: Key+_Space                         = Key 0x0020+_Exclamation :: Key+_Exclamation                   = Key 0x0021+_Quotation :: Key+_Quotation                     = Key 0x0022+_NumberSign :: Key+_NumberSign                    = Key 0x0023+_Dollar :: Key+_Dollar                        = Key 0x0024+_Percent :: Key+_Percent                       = Key 0x0025+_Ampersand :: Key+_Ampersand                     = Key 0x0026+_Apostrophe :: Key+_Apostrophe                    = Key 0x0027+_ParenLeft :: Key+_ParenLeft                     = Key 0x0028+_ParenRight :: Key+_ParenRight                    = Key 0x0029+_Asterisk :: Key+_Asterisk                      = Key 0x002a+_Plus :: Key+_Plus                          = Key 0x002b+_Comma :: Key+_Comma                         = Key 0x002c+_Minus :: Key+_Minus                         = Key 0x002d+_Period :: Key+_Period                        = Key 0x002e+_Slash :: Key+_Slash                         = Key 0x002f+_0 :: Key+_0                             = Key 0x0030+_1 :: Key+_1                             = Key 0x0031+_2 :: Key+_2                             = Key 0x0032+_3 :: Key+_3                             = Key 0x0033+_4 :: Key+_4                             = Key 0x0034+_5 :: Key+_5                             = Key 0x0035+_6 :: Key+_6                             = Key 0x0036+_7 :: Key+_7                             = Key 0x0037+_8 :: Key+_8                             = Key 0x0038+_9 :: Key+_9                             = Key 0x0039+_Colon :: Key+_Colon                         = Key 0x003a+_Semicolon :: Key+_Semicolon                     = Key 0x003b+_Less :: Key+_Less                          = Key 0x003c+_Equal :: Key+_Equal                         = Key 0x003d+_Greater :: Key+_Greater                       = Key 0x003e+_Question :: Key+_Question                      = Key 0x003f+_At :: Key+_At                            = Key 0x0040+_A :: Key+_A                             = Key 0x0041+_B :: Key+_B                             = Key 0x0042+_C :: Key+_C                             = Key 0x0043+_D :: Key+_D                             = Key 0x0044+_E :: Key+_E                             = Key 0x0045+_F :: Key+_F                             = Key 0x0046+_G :: Key+_G                             = Key 0x0047+_H :: Key+_H                             = Key 0x0048+_I :: Key+_I                             = Key 0x0049+_J :: Key+_J                             = Key 0x004a+_K :: Key+_K                             = Key 0x004b+_L :: Key+_L                             = Key 0x004c+_M :: Key+_M                             = Key 0x004d+_N :: Key+_N                             = Key 0x004e+_O :: Key+_O                             = Key 0x004f+_P :: Key+_P                             = Key 0x0050+_Q :: Key+_Q                             = Key 0x0051+_R :: Key+_R                             = Key 0x0052+_S :: Key+_S                             = Key 0x0053+_T :: Key+_T                             = Key 0x0054+_U :: Key+_U                             = Key 0x0055+_V :: Key+_V                             = Key 0x0056+_W :: Key+_W                             = Key 0x0057+_X :: Key+_X                             = Key 0x0058+_Y :: Key+_Y                             = Key 0x0059+_Z :: Key+_Z                             = Key 0x005a+_BracketLeft :: Key+_BracketLeft                   = Key 0x005b+_Backslash :: Key+_Backslash                     = Key 0x005c+_BracketRight :: Key+_BracketRight                  = Key 0x005d+_Circumflex :: Key+_Circumflex                    = Key 0x005e+_Underscore :: Key+_Underscore                    = Key 0x005f+_Grave :: Key+_Grave                         = Key 0x0060+_BraceLeft :: Key+_BraceLeft                     = Key 0x007b+_Bar :: Key+_Bar                           = Key 0x007c+_BraceRight :: Key+_BraceRight                    = Key 0x007d+_Tilde :: Key+_Tilde                         = Key 0x007e
+ examples/RightClick.hs view
@@ -0,0 +1,10 @@+-- | Open the right-click menu, then use the keyboard to select the+-- first entry.++import Test.Robot++main = runRobot $ mapM_ (>> sleep 0.25)+                        [tap rightButton, tap _Down, tap _Return]+  where+    _Down = customKey 0xff54+    _Return = customKey 0xff0d
+ generate_keys.py view
@@ -0,0 +1,66 @@+#!/usr/bin/env python3++from collections import defaultdict+from functools import partial++HEADER = '''+{-# OPTIONS_GHC -fno-warn-missing-signatures #-}++-- | This module is automatically generated from the file @keys.txt@,+-- and should not be edited directly. If you want to add a keysym, refer+-- to that file instead.++module Test.Robot.Types.Keys+'''.strip()++def read_blocks(raw):+    for block in raw.split('\n\n'):+        lines = block.strip().split('\n')+        title = lines[0]++        data = defaultdict(list)+        for name, code in map(str.split, lines[1:]):+            data[code].append(name)++        yield title, data+++def dump_header(blocks, print=print):+    print(HEADER)++    print_i = partial(print, '   ')+    print_i('(', end='')++    first = True+    for title, data in blocks:+        print()+        print_i('  -- **', title)+        for code, names in sorted(data.items()):+            for name in names:+                print_i(' ' if first else ',', '_'+name)+            first = False++    print()+    print_i(') where')+    print()+    print('import Test.Robot.Types.Core')+++def dump_definitions(blocks, print=print):+    for title, data in blocks:+        print()+        for code, names in sorted(data.items()):+            names = ['_' + name for name in names]+            # Gather all the aliases into the same signature, so Haddock+            # displays them on one line+            print(', '.join(names), ':: Key')+            for name in names:+                print(name.ljust(30), '= Key', code)+++if __name__ == '__main__':+    raw = open('keys.txt').read()+    with open('Test/Robot/Types/Keys.hs', 'w', newline='\n') as out_f:+        print = partial(print, file=out_f)+        dump_header(read_blocks(raw), print)+        dump_definitions(read_blocks(raw), print)
+ keys.txt view
@@ -0,0 +1,198 @@+Control sequences+Backspace                     0xff08+Tab                           0xff09+LineFeed                      0xff0a+Clear                         0xff0b+Return                        0xff0d+Pause                         0xff13+ScrollLock                    0xff14+SysReq                        0xff15+Escape                        0xff1b+Delete                        0xffff++Cursor control and motion+Home                          0xff50+Left                          0xff51+Up                            0xff52+Right                         0xff53+Down                          0xff54+Prior                         0xff55+PageUp                        0xff55+Next                          0xff56+PageDown                      0xff56+End                           0xff57+Begin                         0xff58++Miscellaneous functions+Select                        0xff60+Print                         0xff61+Execute                       0xff62+Insert                        0xff63+Undo                          0xff65+Redo                          0xff66+Menu                          0xff67+Find                          0xff68+Cancel                        0xff69+Help                          0xff6a+Break                         0xff6b+ModeSwitch                    0xff7e+ScriptSwitch                  0xff7e+NumLock                       0xff7f++Keypad functions+KP_Space                      0xff80+KP_Tab                        0xff89+KP_Enter                      0xff8d+KP_F1                         0xff91+KP_F2                         0xff92+KP_F3                         0xff93+KP_F4                         0xff94+KP_Home                       0xff95+KP_Left                       0xff96+KP_Up                         0xff97+KP_Right                      0xff98+KP_Down                       0xff99+KP_Prior                      0xff9a+KP_PageUp                     0xff9a+KP_Next                       0xff9b+KP_PageDown                   0xff9b+KP_End                        0xff9c+KP_Begin                      0xff9d+KP_Insert                     0xff9e+KP_Delete                     0xff9f+KP_Equal                      0xffbd+KP_Multiply                   0xffaa+KP_Add                        0xffab+KP_Separator                  0xffac+KP_Subtract                   0xffad+KP_Decimal                    0xffae+KP_Divide                     0xffaf++Keypad numbers+KP_0                          0xffb0+KP_1                          0xffb1+KP_2                          0xffb2+KP_3                          0xffb3+KP_4                          0xffb4+KP_5                          0xffb5+KP_6                          0xffb6+KP_7                          0xffb7+KP_8                          0xffb8+KP_9                          0xffb9++Function keys+F1                            0xffbe+F2                            0xffbf+F3                            0xffc0+F4                            0xffc1+F5                            0xffc2+F6                            0xffc3+F7                            0xffc4+F8                            0xffc5+F9                            0xffc6+F10                           0xffc7+F11                           0xffc8+F12                           0xffc9+F13                           0xffca+F14                           0xffcb+F15                           0xffcc+F16                           0xffcd+F17                           0xffce+F18                           0xffcf+F19                           0xffd0++Modifiers+Shift                         0xffe1+Shift_L                       0xffe1+Shift_R                       0xffe2+Ctrl                          0xffe3+Control                       0xffe3+Control_L                     0xffe3+Control_R                     0xffe4+CapsLock                      0xffe5+ShiftLock                     0xffe6++More modifiers+Meta                          0xffe7+Meta_L                        0xffe7+Meta_R                        0xffe8+Alt                           0xffe9+Alt_L                         0xffe9+Alt_R                         0xffea+Super                         0xffeb+Super_L                       0xffeb+Super_R                       0xffec+Hyper                         0xffed+Hyper_L                       0xffed+Hyper_R                       0xffee++Letters, numbers and symbols+Space                         0x0020+Exclamation                   0x0021+Quotation                     0x0022+NumberSign                    0x0023+Dollar                        0x0024+Percent                       0x0025+Ampersand                     0x0026+Apostrophe                    0x0027+ParenLeft                     0x0028+ParenRight                    0x0029+Asterisk                      0x002a+Plus                          0x002b+Comma                         0x002c+Minus                         0x002d+Period                        0x002e+Slash                         0x002f+0                             0x0030+1                             0x0031+2                             0x0032+3                             0x0033+4                             0x0034+5                             0x0035+6                             0x0036+7                             0x0037+8                             0x0038+9                             0x0039+Colon                         0x003a+Semicolon                     0x003b+Less                          0x003c+Equal                         0x003d+Greater                       0x003e+Question                      0x003f+At                            0x0040+A                             0x0041+B                             0x0042+C                             0x0043+D                             0x0044+E                             0x0045+F                             0x0046+G                             0x0047+H                             0x0048+I                             0x0049+J                             0x004a+K                             0x004b+L                             0x004c+M                             0x004d+N                             0x004e+O                             0x004f+P                             0x0050+Q                             0x0051+R                             0x0052+S                             0x0053+T                             0x0054+U                             0x0055+V                             0x0056+W                             0x0057+X                             0x0058+Y                             0x0059+Z                             0x005a+BracketLeft                   0x005b+Backslash                     0x005c+BracketRight                  0x005d+Circumflex                    0x005e+Underscore                    0x005f+Grave                         0x0060+BraceLeft                     0x007b+Bar                           0x007c+BraceRight                    0x007d+Tilde                         0x007e
+ robot.cabal view
@@ -0,0 +1,39 @@+Name:                robot+Version:             1.0+Synopsis:            Simulate keyboard and mouse events+Homepage:            https://github.com/lfairy/robot+License:             OtherLicense+License-File:        LICENSE+Author:              Chris Wong+Maintainer:          lambda.fairy@gmail.com+Category:            Graphics+Build-Type:          Simple+Extra-Source-Files:  README.markdown, examples/*.hs, generate_keys.py, keys.txt+Cabal-Version:       >= 1.6++Description:+    Generate native keyboard and mouse events. Using this library,+    applications will respond as if the user performed the action+    themselves.++Library+    Exposed-Modules:+        Test.Robot+        Test.Robot.Internal+        Test.Robot.Internal.XTest+        Test.Robot.Types++    Other-Modules:+        Test.Robot.Types.Core+        Test.Robot.Types.Keys++    Build-Depends:    base == 4.*+                    , containers+                    , transformers+                    , xhb++    GHC-Options: -Wall++Source-Repository head+    Type: git+    Location: https://github.com/lfairy/robot.git