[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n[*.swift]\nindent_style = space\nindent_size = 4\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yaml",
    "content": "name: Bug Report\ndescription: Report a bug you've found\ntitle: \"[bug]: \"\nlabels: [bug]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thanks for taking the time to file a bug.\n  - type: textarea\n    attributes:\n      label: What went wrong?\n      description: Describe the issue including reproduction steps.\n      placeholder: |\n        Executing `sendkeys ...`\n\n        Fails with error: ...\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: \"Expected result:\"\n      description: Describe what you expected to happen.\n  - type: textarea\n    attributes:\n      label: \"Actual result:\"\n      description: Describe what actually happened.\n  - type: textarea\n    attributes:\n      label: \"Other information:\"\n      description: |\n        Include any other useful information that would help to reproduce/fix the issue.\n      value: |\n        - Sendkeys version: ...\n        - Operating system: ...\n        - Processor (e.g. Intel, M1): ...\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yaml",
    "content": "name: Feature Request\ndescription: Suggest an idea for this project\ntitle: \"[feature]: \"\nlabels: [enhancement]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thank you for using sendkeys.\n  - type: textarea\n    attributes:\n      label: What feature would you like to see in sendkeys?\n      description: Describe the feature and the problem that you would like to see solved.\n      placeholder: |\n        I would like to be able to ...\n        \n        So that I can ...\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: \"Other information:\"\n      description: |\n        Include any other information that may be useful for prioritizing the feature request. Examples may include your own proposed solution, other alternatives, references to similar features in other tools, or screenshots.\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: build\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    branches:\n      - main\n\njobs:\n  build:\n    runs-on: macos-14\n\n    env:\n      DEVELOPER_DIR: /Applications/Xcode_16.0.app/Contents/Developer\n\n    name: Build\n\n    steps:\n      - name: checkout\n        uses: actions/checkout@v4\n\n      - uses: actions/cache@v4\n        with:\n          path: .build\n          key: ${{ runner.os }}-xcode-${{ hashFiles('**/Package.resolved') }}\n          restore-keys: |\n            ${{ runner.os }}-xcode-\n\n      - name: build\n        run: |\n          ls -n /Applications/ | grep Xcode*\n          make build\n\n      - name: test\n        run: |\n          ./scripts/code-coverage.sh\n\n  create_release:\n    needs:\n      - build\n\n    runs-on: macos-14\n    if: github.ref == 'refs/heads/main'\n\n    name: Create release\n\n    outputs:\n      release_created: ${{ steps.release.outputs.release_created }}\n      tag_name: ${{ steps.release.outputs.tag_name }}\n      sha: ${{ steps.release.outputs.sha }}\n      upload_url: ${{ steps.release.outputs.upload_url }}\n\n    steps:\n      - id: release\n        uses: google-github-actions/release-please-action@v3\n        with:\n          token: ${{ secrets.GITHUB_TOKEN }}\n          release-type: simple\n          package-name: sendkeys\n          changelog-types: |\n            [\n              {\"type\":\"feat\",\"section\":\"Features\",\"hidden\":false},\n              {\"type\":\"fix\",\"section\":\"Bug Fixes\",\"hidden\":false},\n              {\"type\":\"docs\",\"section\":\"Documentation\",\"hidden\":false},\n              {\"type\":\"misc\",\"section\":\"Miscellaneous\",\"hidden\":false}\n            ]\n\n  create_bottle:\n    needs:\n      - create_release\n\n    runs-on: macos-14\n    if: ${{ needs.create_release.outputs.release_created }}\n\n    name: Create bottle\n\n    outputs:\n      sha: ${{ steps.bottle.outputs.sha }}\n      root_url: ${{ steps.bottle.outputs.root_url }}\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - id: bottle\n        name: Create bottle\n        run: |\n          ./scripts/update-version.sh ${{ needs.create_release.outputs.tag_name }}\n          ./scripts/bottle.sh ${{ needs.create_release.outputs.tag_name }}\n\n      - name: Upload bottle big_sur\n        if: ${{ needs.create_release.outputs.release_created }}\n        uses: actions/upload-release-asset@v1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          upload_url: ${{ needs.create_release.outputs.upload_url }}\n          asset_path: ./${{ steps.bottle.outputs.big_sur }}\n          asset_name: ${{ steps.bottle.outputs.big_sur }}\n          asset_content_type: application/gzip\n\n      - name: Upload bottle arm64_big_sur\n        if: ${{ needs.create_release.outputs.release_created }}\n        uses: actions/upload-release-asset@v1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          upload_url: ${{ needs.create_release.outputs.upload_url }}\n          asset_path: ./${{ steps.bottle.outputs.arm64_big_sur }}\n          asset_name: ${{ steps.bottle.outputs.arm64_big_sur }}\n          asset_content_type: application/gzip\n\n  homebrew:\n    needs:\n      - create_release\n      - create_bottle\n\n    runs-on: macos-14\n    if: ${{ needs.create_release.outputs.release_created }}\n\n    name: Update homebrew formula\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Update homebrew formula\n        run: |\n          git config user.name github-actions[bot]\n          git config user.email socsieng-github-actions[bot]@users.noreply.github.com\n          git clone \"https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/socsieng/homebrew-tap.git\"\n          cd homebrew-tap\n\n          git checkout -B main\n          formula='Formula/sendkeys.rb'\n          version=`echo '${{ needs.create_release.outputs.tag_name }}' | sed -E 's/^v//g'`\n          revision='${{ needs.create_release.outputs.sha }}'\n          sed_root_url=`echo '${{ needs.create_bottle.outputs.root_url }}' | sed 's/\\\\//\\\\\\\\\\//g'`\n          sha='${{ needs.create_bottle.outputs.sha }}'\n\n          sed -E -i \"\" \"s/tag: \\\"[^\\\"]+\\\"/tag: \\\"v$version\\\"/g\" $formula\n          sed -E -i \"\" \"s/revision: \\\"[^\\\"]+\\\"/revision: \\\"$revision\\\"/g\" $formula\n          sed -E -i \"\" \"s/version \\\"[^\\\"]+\\\"/version \\\"$version\\\"/g\" $formula\n          sed -E -i \"\" \"s/root_url \\\"[^\\\"]+\\\"/root_url \\\"$sed_root_url\\\"/g\" $formula\n          sed -E -i \"\" \"s/sha256 cellar: :any_skip_relocation, arm64_big_sur: \\\"[^\\\"]+\\\"/sha256 cellar: :any_skip_relocation, arm64_big_sur: \\\"$sha\\\"/g\" $formula\n          sed -E -i \"\" \"s/sha256 cellar: :any_skip_relocation, big_sur:       \\\"[^\\\"]+\\\"/sha256 cellar: :any_skip_relocation, big_sur:       \\\"$sha\\\"/g\" $formula\n\n          git commit -am \"chore: update sendkeys to ${{ needs.create_release.outputs.tag_name }}\"\n          git push origin main\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\n/.build\n/Packages\n/*.xcodeproj\nxcuserdata/\n\n.swiftpm/\n\n*.bak\n\nFormula/sendkeys.rb\n*.tar.gz\n\n/.output\n"
  },
  {
    "path": ".prettierignore",
    "content": "node_modules/\n.build/\n\nCHANGELOG.md\n"
  },
  {
    "path": ".prettierrc.yml",
    "content": "arrowParens: avoid\nprintWidth: 120\nquoteProps: consistent\nsemi: true\nsingleQuote: true\ntabWidth: 2\ntrailingComma: all\noverrides:\n  - files: '*.md'\n    options:\n      parser: markdown\n      proseWrap: always\n"
  },
  {
    "path": ".swift-format",
    "content": "{\n  \"fileScopedDeclarationPrivacy\": {\n    \"accessLevel\": \"private\"\n  },\n  \"indentation\": {\n    \"spaces\": 4\n  },\n  \"indentConditionalCompilationBlocks\": true,\n  \"indentSwitchCaseLabels\": false,\n  \"lineBreakAroundMultilineExpressionChainComponents\": false,\n  \"lineBreakBeforeControlFlowKeywords\": false,\n  \"lineBreakBeforeEachArgument\": false,\n  \"lineBreakBeforeEachGenericRequirement\": false,\n  \"lineLength\": 120,\n  \"maximumBlankLines\": 1,\n  \"prioritizeKeepingFunctionOutputTogether\": false,\n  \"respectsExistingLineBreaks\": true,\n  \"rules\": {\n    \"AllPublicDeclarationsHaveDocumentation\": false,\n    \"AlwaysUseLowerCamelCase\": true,\n    \"AmbiguousTrailingClosureOverload\": true,\n    \"BeginDocumentationCommentWithOneLineSummary\": true,\n    \"DoNotUseSemicolons\": true,\n    \"DontRepeatTypeInStaticProperties\": true,\n    \"FileScopedDeclarationPrivacy\": true,\n    \"FullyIndirectEnum\": true,\n    \"GroupNumericLiterals\": true,\n    \"IdentifiersMustBeASCII\": true,\n    \"NeverForceUnwrap\": false,\n    \"NeverUseForceTry\": false,\n    \"NeverUseImplicitlyUnwrappedOptionals\": false,\n    \"NoAccessLevelOnExtensionDeclaration\": true,\n    \"NoBlockComments\": true,\n    \"NoCasesWithOnlyFallthrough\": true,\n    \"NoEmptyTrailingClosureParentheses\": true,\n    \"NoLabelsInCasePatterns\": true,\n    \"NoLeadingUnderscores\": false,\n    \"NoParensAroundConditions\": true,\n    \"NoVoidReturnOnFunctionSignature\": true,\n    \"OneCasePerLine\": true,\n    \"OneVariableDeclarationPerLine\": true,\n    \"OnlyOneTrailingClosureArgument\": true,\n    \"OrderedImports\": true,\n    \"ReturnVoidInsteadOfEmptyTuple\": true,\n    \"UseLetInEveryBoundCaseVariable\": true,\n    \"UseShorthandTypeNames\": true,\n    \"UseSingleLinePropertyGetter\": true,\n    \"UseSynthesizedInitializer\": true,\n    \"UseTripleSlashForDocumentationComments\": true,\n    \"ValidateDocumentationComments\": false\n  },\n  \"tabWidth\": 8,\n  \"version\": 1\n}\n"
  },
  {
    "path": ".vscode/extensions.json",
    "content": "{\n  \"recommendations\": [\"vknabel.vscode-apple-swift-format\", \"sswg.swift-lang\", \"editorconfig.editorconfig\"]\n}\n"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n  \"configurations\": [\n    {\n      \"type\": \"lldb\",\n      \"request\": \"launch\",\n      \"sourceLanguages\": [\"swift\"],\n      \"name\": \"Debug sendkeys\",\n      \"program\": \"${workspaceFolder:sendkeys}/.build/debug/sendkeys\",\n      \"args\": [],\n      \"cwd\": \"${workspaceFolder:sendkeys}\",\n      \"preLaunchTask\": \"swift: Build Debug sendkeys\"\n    },\n    {\n      \"type\": \"lldb\",\n      \"request\": \"launch\",\n      \"sourceLanguages\": [\"swift\"],\n      \"name\": \"Release sendkeys\",\n      \"program\": \"${workspaceFolder:sendkeys}/.build/release/sendkeys\",\n      \"args\": [],\n      \"cwd\": \"${workspaceFolder:sendkeys}\",\n      \"preLaunchTask\": \"swift: Build Release sendkeys\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"editor.formatOnSave\": true\n}\n"
  },
  {
    "path": ".vscode/tasks.json",
    "content": "{\n  // See https://go.microsoft.com/fwlink/?LinkId=733558\n  // for the documentation about the tasks.json format\n  \"version\": \"2.0.0\",\n  \"tasks\": [\n    {\n      \"label\": \"build\",\n      \"type\": \"shell\",\n      \"command\": \"swift build\"\n    }\n  ]\n}\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n## [4.3.1](https://github.com/socsieng/sendkeys/compare/v4.3.0...v4.3.1) (2024-12-14)\n\n\n### Bug Fixes\n\n* add esc alias for escape key ([ff88df1](https://github.com/socsieng/sendkeys/commit/ff88df1022633e3ab4f7e85a05801ac607024872)), closes [#93](https://github.com/socsieng/sendkeys/issues/93)\n\n## [4.3.0](https://github.com/socsieng/sendkeys/compare/v4.2.0...v4.3.0) (2024-10-28)\n\n\n### Features\n\n* add option to specify arbitrary configuration file ([61133bf](https://github.com/socsieng/sendkeys/commit/61133bf7ec8089d6f3e631a69f35a198cc8c644b))\n* add support for custom key mappings in configuration file ([cc21cce](https://github.com/socsieng/sendkeys/commit/cc21ccee8b10109c74735a8458f8f7161515992d))\n* add support for reading configuration from home directory ([01908f1](https://github.com/socsieng/sendkeys/commit/01908f11f3b85a551f643b2f679f1e595afbf8e3))\n\n## [4.2.0](https://github.com/socsieng/sendkeys/compare/v4.1.2...v4.2.0) (2024-10-26)\n\n\n### Features\n\n* add support for alternate keyboard layouts ([7db41a8](https://github.com/socsieng/sendkeys/commit/7db41a829d1f403dc77206f6c737db11c2bb064e))\n\n## [4.1.2](https://github.com/socsieng/sendkeys/compare/v4.1.1...v4.1.2) (2024-10-24)\n\n\n### Bug Fixes\n\n* add support for function modifier key ([db36c92](https://github.com/socsieng/sendkeys/commit/db36c9200f2bb982c2e0077df42436b3f7a1086e))\n* **build:** restore arm64 binary in homebrew formula ([e38451a](https://github.com/socsieng/sendkeys/commit/e38451aad1f9a2b8c8d4ab5afb25c9de26147d8f))\n\n## [4.1.1](https://github.com/socsieng/sendkeys/compare/v4.1.0...v4.1.1) (2024-10-22)\n\n\n### Bug Fixes\n\n* **build:** fix broken build ([234b441](https://github.com/socsieng/sendkeys/commit/234b441a210bf038b6cfcd60e9ac691d9dcc0d6c))\n\n## [4.1.0](https://github.com/socsieng/sendkeys/compare/v4.0.4...v4.1.0) (2024-10-22)\n\n\n### Features\n\n* add argument to terminate execution ([1b8c1f1](https://github.com/socsieng/sendkeys/commit/1b8c1f1aa2ceb9ae666aaa74142df150d7a85b67)), closes [#79](https://github.com/socsieng/sendkeys/issues/79)\n\n\n### Bug Fixes\n\n* **build:** update macos runners ([2af43cd](https://github.com/socsieng/sendkeys/commit/2af43cda3362757dc1fb2404bd456ae34292e955))\n* update package versions ([b1e1319](https://github.com/socsieng/sendkeys/commit/b1e1319259cef524e5c83632eabecdbcd353b0bb))\n\n## [4.0.4](https://github.com/socsieng/sendkeys/compare/v4.0.3...v4.0.4) (2023-10-07)\n\n\n### Bug Fixes\n\n* **build:** add cache busting parameter ([c2a4f59](https://github.com/socsieng/sendkeys/commit/c2a4f598f0677a0dd490339eddbfaf22083e5b30))\n\n## [4.0.3](https://github.com/socsieng/sendkeys/compare/v4.0.2...v4.0.3) (2023-10-07)\n\n\n### Bug Fixes\n\n* **build:** ignore errors if archive does not exist ([7f2fc4b](https://github.com/socsieng/sendkeys/commit/7f2fc4b5cd5c865228f5455892d7f1cabdad2612))\n\n## [4.0.2](https://github.com/socsieng/sendkeys/compare/v4.0.1...v4.0.2) (2023-10-07)\n\n\n### Bug Fixes\n\n* **build:** clean up archives before bottling ([408db73](https://github.com/socsieng/sendkeys/commit/408db7323ab29733552fe7140405804bb4207e51))\n\n## [4.0.1](https://github.com/socsieng/sendkeys/compare/v4.0.0...v4.0.1) (2023-10-07)\n\n\n### Bug Fixes\n\n* **build:** clean up archives after bottling ([1ef78ed](https://github.com/socsieng/sendkeys/commit/1ef78ed4297d0c69ab1327ed6796fdddda1d2543))\n\n## [4.0.0](https://github.com/socsieng/sendkeys/compare/v3.0.2...v4.0.0) (2023-10-07)\n\n\n### ⚠ BREAKING CHANGES\n\n* fix handling of key strokes to use shift key when appropriate\n\n### Bug Fixes\n\n* fix handling of key strokes to use shift key when appropriate ([beb535b](https://github.com/socsieng/sendkeys/commit/beb535b2693085cf50bc479276a4d806e55d288c)), closes [#62](https://github.com/socsieng/sendkeys/issues/62)\n* fix issue with unspecified application name ([440153d](https://github.com/socsieng/sendkeys/commit/440153d073c10888fd33990197434d9565b430d1))\n\n## [3.0.2](https://github.com/socsieng/sendkeys/compare/v3.0.1...v3.0.2) (2023-10-06)\n\n\n### Bug Fixes\n\n* introduce a small delay to allow commands to be processed before terminating ([9ffc2b2](https://github.com/socsieng/sendkeys/commit/9ffc2b2a012314d983c7e2a94385747abce8ef24)), closes [#60](https://github.com/socsieng/sendkeys/issues/60)\n\n## [3.0.1](https://github.com/socsieng/sendkeys/compare/v3.0.0...v3.0.1) (2023-10-06)\n\n\n### Bug Fixes\n\n* update key handling to use keyboardEventSource ([c26bbfa](https://github.com/socsieng/sendkeys/commit/c26bbfaaab700a6063b8fbb967622241595eb5a0))\n\n## [3.0.0](https://www.github.com/socsieng/sendkeys/compare/v2.9.1...v3.0.0) (2023-10-06)\n\n\n### ⚠ BREAKING CHANGES\n\n* drop support for building on macOS catalina\n\n### Features\n\n* add support for sending keys to an application without activation ([b42d4bc](https://www.github.com/socsieng/sendkeys/commit/b42d4bc347d1800068e8753bc6daa13b255319b2)), closes [#67](https://www.github.com/socsieng/sendkeys/issues/67)\n\n\n### Bug Fixes\n\n* remove dependency on macos-10.15 ([14aa5a8](https://www.github.com/socsieng/sendkeys/commit/14aa5a82ba2fd34e2fa49d5b749b0fb941d1f902))\n\n\n### Miscellaneous\n\n* drop support for building on macOS catalina ([8c027cb](https://www.github.com/socsieng/sendkeys/commit/8c027cb14e4ca9bfe1515a41191c1ecfa4499112))\n\n### [2.9.1](https://www.github.com/socsieng/sendkeys/compare/v2.9.0...v2.9.1) (2023-04-15)\n\n\n### Bug Fixes\n\n* fix homebrew update script ([dac393f](https://www.github.com/socsieng/sendkeys/commit/dac393fe982b7b5ce39a9450277a36b0ea46d58e))\n\n## [2.9.0](https://www.github.com/socsieng/sendkeys/compare/v2.8.0...v2.9.0) (2023-04-15)\n\n\n### Features\n\n* add support for activating application by process id ([0a44470](https://www.github.com/socsieng/sendkeys/commit/0a4447044d2acddba176db74d9698cbef4dfc872)), closes [#63](https://www.github.com/socsieng/sendkeys/issues/63)\n\n## [2.8.0](https://www.github.com/socsieng/sendkeys/compare/v2.7.1...v2.8.0) (2021-09-01)\n\n\n### Features\n\n* convert mouse coordinates from integers to doubles ([bbd4534](https://www.github.com/socsieng/sendkeys/commit/bbd45342cf0f1974ed2a2365d386b44b3225841d))\n* make scaleY option in path command ([f1fe840](https://www.github.com/socsieng/sendkeys/commit/f1fe8406a13c7abee8844123d0d5aeda903b6620))\n* output mouse positions as decimals ([d723082](https://www.github.com/socsieng/sendkeys/commit/d723082a068f4806bad9363098727af287c067d7))\n\n\n### Documentation\n\n* add sample command for mouse path command ([9c9e6cb](https://www.github.com/socsieng/sendkeys/commit/9c9e6cbe0b4c7ca313116eb4b7966824c681ed17))\n\n### [2.7.1](https://www.github.com/socsieng/sendkeys/compare/v2.7.0...v2.7.1) (2021-08-31)\n\n\n### Bug Fixes\n\n* remove debug print statements ([5109a12](https://www.github.com/socsieng/sendkeys/commit/5109a12045bb65d18fe570db53c9ce88e6fa4d3d))\n\n## [2.7.0](https://www.github.com/socsieng/sendkeys/compare/v2.6.2...v2.7.0) (2021-08-31)\n\n\n### Features\n\n* add mouse path command ([6c5c0b9](https://www.github.com/socsieng/sendkeys/commit/6c5c0b9ab5e25d795ee470884a25de2b28d5988d))\n\n\n### Documentation\n\n* add example animation for mouse path command ([2c49b8d](https://www.github.com/socsieng/sendkeys/commit/2c49b8d16735d60186c1b1c842ff31607ec265bb))\n\n### [2.6.2](https://www.github.com/socsieng/sendkeys/compare/v2.6.1...v2.6.2) (2021-08-22)\n\n\n### Features\n\n* improve application name matching algorithm ([456586a](https://www.github.com/socsieng/sendkeys/commit/456586a746bea2294fd64db238cf595d9f7bf417)), closes [#50](https://www.github.com/socsieng/sendkeys/issues/50)\n\n\n### Documentation\n\n* update README to include details on  apps sub command ([40f49be](https://www.github.com/socsieng/sendkeys/commit/40f49bec4ce9574ee7e7ecafafd7facba067f64c))\n\n### [2.6.1](https://www.github.com/socsieng/sendkeys/compare/v2.6.0...v2.6.1) (2021-08-21)\n\n\n### chore\n\n* bump version ([ee86888](https://www.github.com/socsieng/sendkeys/commit/ee86888368363598d51fbd5b1b49ca59a68ef541))\n\n## [2.6.0](https://www.github.com/socsieng/sendkeys/compare/v2.5.2...v2.6.0) (2021-08-21)\n\n\n### Features\n\n* display a list applications that can be used with sendkeys ([94626fa](https://www.github.com/socsieng/sendkeys/commit/94626fa39a30b9b51f03548aa0b8dbb62bd5cfb6)), closes [#46](https://www.github.com/socsieng/sendkeys/issues/46)\n\n### [2.5.2](https://www.github.com/socsieng/sendkeys/compare/v2.5.1...v2.5.2) (2021-06-19)\n\n\n### Bug Fixes\n\n* **build:** remove usage of realpath ([c9019d7](https://www.github.com/socsieng/sendkeys/commit/c9019d7d3ac9a3776b15a50708365de86d6f3b72))\n\n### [2.5.1](https://www.github.com/socsieng/sendkeys/compare/v2.5.0...v2.5.1) (2021-06-19)\n\n\n### Bug Fixes\n\n* **build:** apply changes to address broken homebrew build ([536603a](https://www.github.com/socsieng/sendkeys/commit/536603a0e4f224ffe87487954318f4b617264e87))\n\n## [2.5.0](https://www.github.com/socsieng/sendkeys/compare/v2.4.0...v2.5.0) (2021-06-18)\n\n\n### Features\n\n* add option to output mouse position commands with predefined duration ([f662869](https://www.github.com/socsieng/sendkeys/commit/f662869cf207bca9978407758eadef244c9d026f))\n\n## [2.4.0](https://www.github.com/socsieng/sendkeys/compare/v2.3.10...v2.4.0) (2021-03-27)\n\n\n### Features\n\n* add support for apple m1 processors ([6706f85](https://www.github.com/socsieng/sendkeys/commit/6706f85b340168783ecb5e4fe56bde545c2bf86a))\n\n### [2.3.10](https://www.github.com/socsieng/sendkeys/compare/v2.3.0...v2.3.10) (2021-03-27)\n\n\n### Documentation\n\n* add example file for using transform ([1a2c8e8](https://www.github.com/socsieng/sendkeys/commit/1a2c8e82cd6b04321aadb78979c73a120d340bf4))\n* update readme to include installation instructions for alternate versions ([72030ab](https://www.github.com/socsieng/sendkeys/commit/72030abc018a7bd81e31509ac7085e77620d6528))\n\n## [2.3.0](https://www.github.com/socsieng/sendkeys/compare/v2.2.0...v2.3.0) (2021-01-20)\n\n\n### Features\n\n* add mouse focus command ([1bbab2d](https://www.github.com/socsieng/sendkeys/commit/1bbab2d0a9377260d892a78cfbc41713064ac736))\n\n## [2.2.0](https://www.github.com/socsieng/sendkeys/compare/v2.1.1...v2.2.0) (2021-01-16)\n\n\n### Features\n\n* add support for triggering mouse up and down events independently ([bee0fbe](https://www.github.com/socsieng/sendkeys/commit/bee0fbe5032a97fe63090fedf9cc7c9d537f60f6))\n\n### [2.1.1](https://www.github.com/socsieng/sendkeys/compare/v2.1.0...v2.1.1) (2021-01-07)\n\n\n### Bug Fixes\n\n* fix typo in key mappings ([c4a4997](https://www.github.com/socsieng/sendkeys/commit/c4a4997375c55cc4217c1a381df39ff94d9ff751))\n* handle `a` key correctly as keycode 0 ([29209a3](https://www.github.com/socsieng/sendkeys/commit/29209a34a495afdc0028e353ccf1b8375b32f005))\n\n## [2.1.0](https://www.github.com/socsieng/sendkeys/compare/v2.0.0...v2.1.0) (2021-01-06)\n\n\n### Features\n\n* add option to change animation refresh rate ([73a2c29](https://www.github.com/socsieng/sendkeys/commit/73a2c294a878f9e8e64c14a4b6664ddb1586e13b)), closes [#21](https://www.github.com/socsieng/sendkeys/issues/21)\n* add transform subcommand ([3893313](https://www.github.com/socsieng/sendkeys/commit/38933135a3746343d501cd720d4b66f7ee1ac552))\n\n\n### Bug Fixes\n\n* start mouse timing when mouse-position command is executed ([1437aac](https://www.github.com/socsieng/sendkeys/commit/1437aac909e289a782d16677601a81c49d443d85))\n* support negative values for mouse click and drag events ([f50209a](https://www.github.com/socsieng/sendkeys/commit/f50209ae1e8924dbd189a11a6ecad388082c1d17)), closes [#23](https://www.github.com/socsieng/sendkeys/issues/23)\n\n\n### Documentation\n\n* update documentation to state that the application should be running ([3e0d973](https://www.github.com/socsieng/sendkeys/commit/3e0d9736559d48e45c3287c48830bd743926e3ef))\n\n## [2.0.0](https://www.github.com/socsieng/sendkeys/compare/v1.3.0...v2.0.0) (2021-01-04)\n\n\n### Features\n\n* use click timings when producing mouse commands ([970e1df](https://www.github.com/socsieng/sendkeys/commit/970e1df52a903c93da62a572edc99caea1ffc1b5)), closes [#18](https://www.github.com/socsieng/sendkeys/issues/18)\n\n\n### Bug Fixes\n\n* check file exists before activating application ([94cda37](https://www.github.com/socsieng/sendkeys/commit/94cda379c54175e1b59a87633512456b327e63fa)), closes [#17](https://www.github.com/socsieng/sendkeys/issues/17)\n\n\n### Documentation\n\n* include example recording and replaying mouse commands ([5019461](https://www.github.com/socsieng/sendkeys/commit/501946176dab36189e05b49c6e09800ec3bd77ad)), closes [#19](https://www.github.com/socsieng/sendkeys/issues/19)\n\n## [1.3.0](https://www.github.com/socsieng/sendkeys/compare/v1.2.0...v1.3.0) (2021-01-04)\n\n\n### Features\n\n* add option to listen to mouse clicks ([d1d129f](https://www.github.com/socsieng/sendkeys/commit/d1d129fed23f969b10bf0475aeb77f9752a4a5bd))\n\n\n### Documentation\n\n* use expanded argument names in examples ([f4839a3](https://www.github.com/socsieng/sendkeys/commit/f4839a3c027bc78fb13e6f717147f206c3c043d8))\n\n## [1.2.0](https://www.github.com/socsieng/sendkeys/compare/v1.1.1...v1.2.0) (2021-01-03)\n\n\n### Features\n\n* defer accesibility check to execution of the command ([670d091](https://www.github.com/socsieng/sendkeys/commit/670d091d17100fd9b9cdb74ba0eebf69cae6af51))\n\n### [1.1.1](https://www.github.com/socsieng/sendkeys/compare/v1.1.0...v1.1.1) (2021-01-02)\n\n\n### Bug Fixes\n\n* address modifier key issue on key up ([0bfa58a](https://www.github.com/socsieng/sendkeys/commit/0bfa58ad87ebcff46b905900584877544a533463))\n* double key entry issue ([26ad67e](https://www.github.com/socsieng/sendkeys/commit/26ad67e80aaa30fcd9c2a1bb20f5987288760bbc))\n\n## [1.1.0](https://www.github.com/socsieng/sendkeys/compare/v1.0.0...v1.1.0) (2021-01-02)\n\n\n### Features\n\n* add support for key down and up commands ([2c5cefb](https://www.github.com/socsieng/sendkeys/commit/2c5cefb28c802dea94d55920c4092b232f50e62e))\n* add support for mouse clicks with modifier keys ([1654fd7](https://www.github.com/socsieng/sendkeys/commit/1654fd7217c7588c16c22ff779d26564aee634f9))\n* add support for mouse drag with modifier keys ([32964a7](https://www.github.com/socsieng/sendkeys/commit/32964a725312bfa04d203c4b56d3cf20cf237b52))\n* add support for mouse move with modifier keys ([7f4c891](https://www.github.com/socsieng/sendkeys/commit/7f4c891be9aed26f37c38d6275247830d9f04b5c))\n* add support for mouse scroll with modifier keys ([27afc5b](https://www.github.com/socsieng/sendkeys/commit/27afc5bb7c50b762d4d1333f3c6eeef6b92a8d8d))\n\n\n### Bug Fixes\n\n* add event source attribution to related events ([d443fdf](https://www.github.com/socsieng/sendkeys/commit/d443fdf6c2e64b8258e76c7a2ea23a94de62a695))\n* make right click work consistently across applications ([464a401](https://www.github.com/socsieng/sendkeys/commit/464a401d532ef2afc1cfa6a206d84479aa92888a))\n* use click count when triggering mouse click ([f824a98](https://www.github.com/socsieng/sendkeys/commit/f824a98823551c451fcb4e8f6c9196d9fe26b8e6))\n\n\n### Documentation\n\n* add example of mouse move command ([2470c7b](https://www.github.com/socsieng/sendkeys/commit/2470c7bc56feb207fda2b1f2e39b1377ccf4ffd2))\n* **modifier keys:** add documentation for mouse modifier keys ([7c5e9d9](https://www.github.com/socsieng/sendkeys/commit/7c5e9d917d5eb18b003095a8a361e9ac99078826))\n\n## [1.0.0](https://www.github.com/socsieng/sendkeys/compare/v0.5.0...v1.0.0) (2020-12-31)\n\n\n### Features\n\n* stable release 1.0.0 ([27bcfc6](https://www.github.com/socsieng/sendkeys/commit/27bcfc68bc183b7a0d6e466b32ea619d7eee7aed))\n\n\n### Bug Fixes\n\n* read file relative to current directory ([49e1253](https://www.github.com/socsieng/sendkeys/commit/49e12537b288a34e7eb3bc060bc513ae36a86a82))\n\n\n### Miscellaneous\n\n* append newline when raising a fatal error ([b33f495](https://www.github.com/socsieng/sendkeys/commit/b33f495d7c60810cc82a540b920467dd61f8b869))\n\n## [0.5.0](https://www.github.com/socsieng/sendkeys/compare/v0.4.0...v0.5.0) (2020-12-31)\n\n\n### Features\n\n* add support for scrolling ([b438e00](https://www.github.com/socsieng/sendkeys/commit/b438e0089b947339c935a21ba39c66375dea4b5d))\n\n## [0.4.0](https://www.github.com/socsieng/sendkeys/compare/v0.3.0...v0.4.0) (2020-12-31)\n\n\n### Features\n\n* add check to see if accessibility permissions have been enabled ([409e0fb](https://www.github.com/socsieng/sendkeys/commit/409e0fbe935113329b1d61cdbe3f2cd186781117))\n* add sub command to display the current mouse position ([507f8b8](https://www.github.com/socsieng/sendkeys/commit/507f8b8aa1ccedbee2936295c2d192f1a1c33ede))\n* add wait option for mouse-position ([2c0aa8f](https://www.github.com/socsieng/sendkeys/commit/2c0aa8fab484de46b835cc0ec52afc22699f9033))\n\n\n### Bug Fixes\n\n* display help when no commands supplied ([c3175ba](https://www.github.com/socsieng/sendkeys/commit/c3175ba182ab98831dfbb29afcd2241f6e22cdfc))\n* only perform accessibility check if stdin is tty ([6043a78](https://www.github.com/socsieng/sendkeys/commit/6043a784a9f2245bd826b30787d494a687899428))\n\n\n### Documentation\n\n* add documentation on how to use mouse-position command ([42e9955](https://www.github.com/socsieng/sendkeys/commit/42e9955b4f3efbc2d56c062f85134d17deea68b7))\n\n## [0.3.0](https://www.github.com/socsieng/sendkeys/compare/v0.2.4...v0.3.0) (2020-12-31)\n\n\n### ⚠ BREAKING CHANGES\n\n* update build step to update homebrew formula\n\n### build\n\n* update build step to update homebrew formula ([5fd8722](https://www.github.com/socsieng/sendkeys/commit/5fd8722409c6b536ec2388db7e99e0dfa6a134ea))\n\n### [0.2.4](https://www.github.com/socsieng/sendkeys/compare/v0.2.3...v0.2.4) (2020-12-31)\n\n\n### Documentation\n\n* add brew install instructions ([95023b1](https://www.github.com/socsieng/sendkeys/commit/95023b1a8533ab7f2fb6e7ec4f650a312e0ab828))\n\n### [0.2.3](https://www.github.com/socsieng/sendkeys/compare/v0.2.2...v0.2.3) (2020-12-31)\n\n\n### Bug Fixes\n\n* update bottle name to work with brew ([259c140](https://www.github.com/socsieng/sendkeys/commit/259c14051895c5672f290e2f8f2bf299395e31a4))\n\n### [0.2.2](https://www.github.com/socsieng/sendkeys/compare/v0.2.1...v0.2.2) (2020-12-30)\n\n\n### Bug Fixes\n\n* **build:** fix bad substitution in sed ([254161e](https://www.github.com/socsieng/sendkeys/commit/254161e567bfb8a8063d0dd8c8b976ec0d2c09da))\n\n### [0.2.1](https://www.github.com/socsieng/sendkeys/compare/v0.2.0...v0.2.1) (2020-12-30)\n\n\n### Bug Fixes\n\n* **build:** update scripts to handle differences in tag_name ([13fbf6b](https://www.github.com/socsieng/sendkeys/commit/13fbf6bb377ada5becd25399b75e9367cbbc4302))\n\n## [0.2.0](https://www.github.com/socsieng/sendkeys/compare/v0.1.0...v0.2.0) (2020-12-30)\n\n\n### ⚠ BREAKING CHANGES\n\n* update build step names\n\n### Build System\n\n* update build step names ([4d6a0f4](https://www.github.com/socsieng/sendkeys/commit/4d6a0f476d5331a1d99bfd150dcf88281d0f1dd3))\n\n## [0.1.0](https://www.github.com/socsieng/sendkeys/compare/v0.0.3...v0.1.0) (2020-12-30)\n\n\n### Features\n\n* add support for reading from stdin ([463d777](https://www.github.com/socsieng/sendkeys/commit/463d7775e1bc11a293917c43e7c00335d2e77404))\n"
  },
  {
    "path": "Formula/sendkeys_template.rb",
    "content": "# Documentation: https://docs.brew.sh/Formula-Cookbook\n#                https://rubydoc.brew.sh/Formula\nclass Sendkeys < Formula\n  desc \"Command line tool for automating keystrokes and mouse events\"\n  homepage \"https://github.com/socsieng/sendkeys\"\n  url \"\"\n  version \"0.0.0\"\n  license \"Apache-2.0\"\n\n  depends_on :xcode => [\"12.0\", :build]\n\n  def install\n    system \"make\", \"install\", \"prefix=#{prefix}\"\n  end\n\n  test do\n    system \"sendkeys\" \"--help\"\n  end\nend\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "Makefile",
    "content": "prefix ?= /usr/local\nbindir ?= $(prefix)/bin\n\n.PHONY: build\nbuild:\n\t@scripts/update-version.sh\n\t@swift build -c release --disable-sandbox --arch arm64 --arch x86_64\n\n.PHONY: verify\nverify:\n\t@swift test\n\t@scripts/verify-output.sh\n\n.PHONY: install\ninstall: build\n\t@install -d \"$(bindir)\"\n\t@install \".build/apple/Products/Release/sendkeys\" \"$(bindir)/sendkeys\"\n\n.PHONY: uninstall\nuninstall:\n\t@rm -rf \"$(bindir)/sendkeys\"\n\n.PHONY: clean\nclean:\n\t@rm -rf .build\n"
  },
  {
    "path": "Package.resolved",
    "content": "{\n  \"object\": {\n    \"pins\": [\n      {\n        \"package\": \"swift-argument-parser\",\n        \"repositoryURL\": \"https://github.com/apple/swift-argument-parser\",\n        \"state\": {\n          \"branch\": null,\n          \"revision\": \"41982a3656a71c768319979febd796c6fd111d5c\",\n          \"version\": \"1.5.0\"\n        }\n      },\n      {\n        \"package\": \"cmark-gfm\",\n        \"repositoryURL\": \"https://github.com/apple/swift-cmark.git\",\n        \"state\": {\n          \"branch\": null,\n          \"revision\": \"3ccff77b2dc5b96b77db3da0d68d28068593fa53\",\n          \"version\": \"0.5.0\"\n        }\n      },\n      {\n        \"package\": \"swift-format\",\n        \"repositoryURL\": \"https://github.com/apple/swift-format.git\",\n        \"state\": {\n          \"branch\": null,\n          \"revision\": \"65f9da9aad84adb7e2028eb32ca95164aa590e3b\",\n          \"version\": \"600.0.0\"\n        }\n      },\n      {\n        \"package\": \"swift-markdown\",\n        \"repositoryURL\": \"https://github.com/apple/swift-markdown.git\",\n        \"state\": {\n          \"branch\": null,\n          \"revision\": \"8f79cb175981458a0a27e76cb42fee8e17b1a993\",\n          \"version\": \"0.5.0\"\n        }\n      },\n      {\n        \"package\": \"swift-syntax\",\n        \"repositoryURL\": \"https://github.com/swiftlang/swift-syntax.git\",\n        \"state\": {\n          \"branch\": null,\n          \"revision\": \"0687f71944021d616d34d922343dcef086855920\",\n          \"version\": \"600.0.1\"\n        }\n      },\n      {\n        \"package\": \"Yams\",\n        \"repositoryURL\": \"https://github.com/jpsim/Yams.git\",\n        \"state\": {\n          \"branch\": null,\n          \"revision\": \"3036ba9d69cf1fd04d433527bc339dc0dc75433d\",\n          \"version\": \"5.1.3\"\n        }\n      }\n    ]\n  },\n  \"version\": 1\n}\n"
  },
  {
    "path": "Package.swift",
    "content": "// swift-tools-version:5.5\n// The swift-tools-version declares the minimum version of Swift required to build this package.\n\nimport PackageDescription\n\nlet package = Package(\n    name: \"sendkeys\",\n    dependencies: [\n        // Dependencies declare other packages that this package depends on.\n        .package(url: \"https://github.com/apple/swift-argument-parser\", from: \"1.5.0\"),\n        .package(url: \"https://github.com/apple/swift-format\", from: \"600.0.0\"),\n        .package(url: \"https://github.com/jpsim/Yams.git\", from: \"5.1.3\"),\n    ],\n    targets: [\n        // Targets are the basic building blocks of a package. A target can define a module or a test suite.\n        // Targets can depend on other targets in this package, and on products in packages which this package depends on.\n        .executableTarget(\n            name: \"sendkeys\",\n            dependencies: [\n                .product(name: \"ArgumentParser\", package: \"swift-argument-parser\"),\n                \"SendKeysLib\",\n            ]),\n        .target(\n            name: \"SendKeysLib\",\n            dependencies: [\n                .product(name: \"ArgumentParser\", package: \"swift-argument-parser\"),\n                .product(name: \"Yams\", package: \"Yams\"),\n            ]),\n        .testTarget(\n            name: \"SendKeysTests\",\n            dependencies: [\"sendkeys\", \"SendKeysLib\"]),\n    ]\n)\n"
  },
  {
    "path": "README.md",
    "content": "# SendKeys\n\n![Build status](https://github.com/socsieng/sendkeys/workflows/build/badge.svg)\n![Homebrew installs](https://img.shields.io/github/downloads/socsieng/sendkeys/total.svg?label=%F0%9F%8D%BA+installs&labelColor=32393F&color=brightgreen)\n\nSendKeys is a macOS command line application used to automate the keystrokes and mouse events.\n\nIt is a great tool for automating input and mouse events for screen recordings.\n\nThis is a Swift rewrite of [`sendkeys-macos`](https://github.com/socsieng/sendkeys-macos).\n\n## Usage\n\nBasic usage:\n\n```sh\nsendkeys --application-name \"Notes\" --characters \"Hello<p:1> world<c:left:option,shift><c:i:command>\"\n```\n\n![hello world example](https://github.com/socsieng/sendkeys/raw/main/docs/images/example1.gif)\n\n_Activates the Notes application (assuming Notes is already running) and types `Hello` (followed by a 1 second pause)\nand `world`, and then selects the word `world` and changes the font to italics with `command` + `i`._\n\nInput can be read from a file:\n\n```sh\nsendkeys --application-name \"Code\" --input-file example.txt\n```\n\n_Activates Visual Studio Code and sends keystrokes loaded from `example.txt`._\n\nInput can also be piped to `stdin`:\n\n```sh\ncat example.txt | sendkeys --application-name \"Notes\"\n```\n\n_Activates the Notes application and sends keystrokes piped from `stdout` of the preceding command._\n\n### Arguments\n\n- `--application-name <application-name>`: The application name to activate or target when sending commands. Note that a\n  list of applications that can be used in `--application-name` can be found using the\n  [`apps` sub command](#list-of-applications-names).\n- `--pid <process-id>`: The process id of the application to target when sending commands. Note that this if this\n  argument is supplied with `--application-name`, `--pid` takes precedence.\n- `--targeted`: If supplied, the application keystrokes will only be sent to the targeted application.\n- `--no-activate`: If supplied, the specified application will not be activated before sending commands.\n- `--input-file <file-name>`: The path to a file containing the commands to send to the application.\n- `--characters <characters>`: The characters to send to the application. Note that this argument is ignored if\n  `--input-file` is supplied.\n- `--delay <delay>`: The delay between keystrokes and instructions. Defaults to `0.1` seconds.\n- `--initial-delay <initial-delay>`: The initial delay before sending the first keystroke or instruction. Defaults to\n  `1` second.\n- `--animation-interval <interval-in-seconds>`: The time between mouse movements when animating mouse commands. Lower\n  values results in smoother animations. Defaults to `0.01` seconds.\n- `--terminate-command <command>`: The command that should be used to terminate the application. Not set by default.\n  Follows a similar convention to `--characters`. (e.g. `f12:command,shift`).\n- `--keyboard-layout <layout>`: Use alternate keyboard layout. Defaults to `qwerty`. `colemak` and `dvorak` are also\n  supported, pull requests for other common keyboard layouts may be considered. If a specific keyboard layout is not\n  supported, a custom layout can be defined in using the `--config` option or using the\n  [`.sendkeysrc.yml`](./examples/.sendkeysrc.yml) configuration file (`send.remap`).\n- `--config <yaml-file>`: Configuration file to load settings from.\n\n## Installation\n\n### Homebrew (recommended)\n\nInstall using [homebrew](https://brew.sh/):\n\n```sh\nbrew install socsieng/tap/sendkeys\n```\n\n### Manual installation\n\nAlternatively, install from source:\n\n```sh\ngit clone https://github.com/socsieng/sendkeys.git\ncd sendkeys\nmake install\n```\n\n## Markup\n\nMost printable characters will be sent as keystrokes to the active application. Support for additional instructions is\nprovided by some basic markup which is unlikely to be used in other markup languages to avoid conflicts.\n\n### Key codes and modifier keys\n\nSupport for special key codes and modifier keys is provided with the following markup structure: `<c:key[:modifiers]>`\n\n- `key` can include any printable character or, one of the following key names: `f1`, `f2`, `f3`, `f4`, `f5`, `f6`,\n  `f7`, `f8`, `f9`, `f10`, `f11`, `f12`, `esc`, `return`, `enter`, `delete`, `space`, `tab`, `up`, `down`, `left`,\n  `right`, `home`, `end`, `pgup`, and `pgdown`. See list of\n  [mapped keys](https://github.com/socsieng/sendkeys/blob/main/Sources/SendKeysLib/KeyCodes.swift#L127) for a full list.\n- `modifiers` is an optional list of comma separated values that can include `command`, `shift`, `control`, `option`,\n  and `function`.\n\nExample key combinations:\n\n- `tab`: `<c:tab>`\n- `command` + `a`: `<c:a:command>`\n- `option` + `shift` + `left arrow`: `<c:left:option,shift>`\n\n#### Key down and up\n\nSome applications expect modifier keys to be pressed explicitly before invoking actions like mouse click. An example of\nthis is Pixelmator which expect the `option` key to be pressed before executing the alternate click action. This can be\nachieved with key down `<kd:key[:modifiers]>` and key up `<ku:key[:modifiers]>`.\n\nNote that these command shoulds only be used in these special cases when the mouse action and modifier keys are not\nsupported natively.\n\nAn example of how to trigger alternate click behavior in Pixelmator as described above:\n`<kd:option><m:left:option><ku:option>`.\n\n### Mouse commands\n\n#### Move mouse cursor\n\nThe mouse cursor can be moved using the following markup: `<m:[x1,y1,]x2,y2[:duration][:modifiers]>`\n\n- `x1` and `y1` are optional x and y coordinates to move the mouse from. Defaults to the current mouse position.\n- `x2` and `y2` are x and y coordinates to move the mouse to. These values are required.\n- `duration` is optional and determines the number of seconds (supports partial seconds) that should be used to move the\n  mouse cursor (larger number means slower movement). Defaults to `0`.\n- `modifiers` is an optional list of comma separated values that can include `command`, `shift`, `control`, and\n  `option`.\n\nExample usage:\n\n- `<m:400,400:0.5>`: Move mouse cursor from current position to 400, 400 over 0.5 seconds.\n- `<m:400,400,0,0:2>`: Move mouse cursor from 400, 400 position to 0, 0 over 2 seconds.\n- `<m:400,400>`: Move mouse cursor to 400, 400 instantly.\n\n![mouse move example](https://github.com/socsieng/sendkeys/raw/main/docs/images/mouse.gif) <br>_Sample command:\n`sendkeys -c \"<m:100,300,300,300:0.5><p:0.5><m:100,300:0.5>\"`_\n\n#### Mouse click\n\nA mouse click can be activated using the following markup: `<m:button[:modifiers][:clicks]>`\n\n- `button` is required and refers to the mouse button to click. Supported values include `left`, `center`, and `right`.\n- `modifiers` is an optional list of comma separated values that can include `command`, `shift`, `control`, and\n  `option`.\n- `clicks` is optional and specifies the number of times the button should be clicked. Defaults to `1`.\n\nExample usage:\n\n- `<m:right>`: Right mouse click at the current mouse location.\n- `<m:left:2>`: Double click the left button at the current mouse location.\n\n#### Mouse drag\n\nA mouse drag be initiated with: `<d:[x1,y1,]x2,y2[:duration][:button[:modifiers]]>`\n\nThe argument structure is similar to moving the mouse cursor.\n\n- `x1` and `y1` are optional x and y coordinates to start the drage. Defaults to the current mouse position.\n- `x2` and `y2` are x and y coordinates to end the drag. These values are required.\n- `duration` is optional and determines the number of seconds (supports partial seconds) that should be used to drag the\n  mouse (larger number means slower movement). Defaults to `0`.\n- `button` is optional and refers to the mouse button to use when initiating the mouse drag. Supported values include\n  `left`, `center`, and `right`. Defaults to `left`.\n- `modifiers` is an optional list of comma separated values that can include `command`, `shift`, `control`, and\n  `option`. Note that modifiers can only be used if `button` is explicitly set.\n\nExample usage:\n\n- `<d:400,400:0.5>`: Drag the mouse using the left mouse button from current position to 400, 400 over 0.5 seconds.\n- `<d:400,400,0,0:2:right>`: Drag the mouse using the right mouse button from 400, 400 position to 0, 0 over 2 seconds.\n- `<d:400,400:2:left:shift>`: Drag the mouse using the left mouse button to 400, 400 over 2 seconds with the `shift` key\n  down.\n\n![mouse drag example](https://github.com/socsieng/sendkeys/raw/main/docs/images/mouse-drag.gif)\n\n#### Mouse scrolling\n\nA mouse scroll can be initiated with: `<s:x,y[:duration][:modifiers]>`\n\n- `x` is required and controls horizontal scrolling. Positive values scroll to the right, while negative values scroll\n  to the left.\n- `y` is required and controls vertical scrolling. Positive values scroll down, while negative values scroll up.\n- `duration` is optional and determines the number of seconds (supports partial seconds) that should be used to drag the\n  mouse (larger number means slower movement). Defaults to `0`.\n- `modifiers` is an optional list of comma separated values that can include `command`, `shift`, `control`, and\n  `option`.\n\nExample usage:\n\n- `<s:0,400:0.5>`: Scrolls down 400 pixels over 0.5 seconds.\n- `<s:0,-100:0.2>`: Scrolls up 400 pixels over 0.2 seconds.\n- `<s:100,0>`: Scrolls 100 pixel to the right instantly.\n\n#### Mouse focus\n\nThe mouse focus command can be used to draw attention to an area of the screen by moving the cursor in a circular\npattern. The mouse focus command uses the following markup:\n`<mf:centerX,centerY:radiusX[,radiusY]:angleFrom,angleTo:duration>`\n\n- `centerX` is required and represents the center x coordinate of the circular path.\n- `centerY` is required and represents the center y coordinate of the circular path.\n- `radiusX` is required and represents the size of the radius along the x axis of the circular path.\n- `radiusY` is optional and represents the size of the radius along the y axis of the circular path. If omitted,\n  `radiusX` will be used indicating that the circular path will be a regular circle. An elipse can be achieved by having\n  different values for `radiusX` and `radiusY`.\n- `angleFrom` is required and represents the start angle/position of the circular path. Angle is defined using degrees\n  where `0` represents 12 o'clock on an analog clock, and positive are applied in a clockwize direction. (e.g. 90\n  degrees is 3 o'clock).\n- `angleTo` is required and represents the end angle/position of the circular path.\n- `duration` is required and determines the number of seconds (supports partial seconds) used to complete the animation\n  between `angleFrom` to `angleTo`.\n\nExample usage:\n\n- `<mf:1000,200:50,20:180,900:2>`: Draws attention to position 1000, 200 by moving the mouse along an eliptical 50\n  pixels wide by 20 pixels high starting at the bottom (180 degrees) to 900 degrees (delta of 720 degrees) over a period\n  of 2 seconds.\n\n![mouse focus example](https://github.com/socsieng/sendkeys/raw/main/docs/images/mouse-focus.gif)\n\n#### Mouse path\n\nThe mouse path command can be used move the mouse cursor along a path. The mouse path command uses the following markup:\n`<mpath:path[:ofssetX,offsetY[,scaleX[,scaleY]]]:duration>`\n\n- `path` is required and defines path for the mouse cursor to follow. The path is described using\n  [SVG Path data](https://www.w3.org/TR/SVG/paths.html#PathData)\n- `ofssetX` and `offsetY` are optional and can be used to offset path coordinates by their respective `x` and `y`\n  values. Defaults to `0,0`.\n- `scaleX` and `scaleY` are also optional and can be used to scale path coordinates by their respective `x` and `y`\n  values. Defaults to `1,1`. If `scaleY` is omitted while `scaleX` is provided, a uniform scale will be assumed. i.e.\n  `x` = `y`.\n- `duration` is required and determines the number of seconds (supports partial seconds) used to complete the animation\n  along the `path`.\n\nExample usage:\n\n- `<mpath:c0,40 200,40 200,0:2>`: Moves the mouse from its current position along a cubic bezier path with control\n  points `0,40` and `200,40` to the final position of `200,1`.\n\n![mouse path example](https://github.com/socsieng/sendkeys/raw/main/docs/images/mouse-path.gif) <br>_Sample command:\n`sendkeys -c \"<mpath:M100,100 h 100 l5,30 10,-60 5,30 h 100:2><mpath:c0,40 -220,40 -220,0:1.5>\"`_\n\n#### Mouse down and up\n\nMouse down and up events can be used to manually initiate a drag event or multiple mouse move commands while the mouse\nbutton is down. This can be achieved with mouse down `<md:button[:modifiers]>` and mouse up `<mu:button[:modifiers]>`.\n\nNote that the drag command is recommended for basic drag functionality..\n\nAn example of how include multiple mouse movements while the mouse button is down:\n`<md:left><m:0,0,100,0:1><m:100,100:1><mu:left>`.\n\n### Pauses\n\nThe default time between keystrokes and instructions is determined by the `--delay`/`-d` argument (default value is\n`0.1`). Pauses can be customized with: `<p:duration>`\n\n- `duration` is required and controls the amount of time to pause before the next keystroke/instruction is executed.\n\n`<P:seconds>` (note upper case `P`) can be used to modify the default delay between subsequent keystrokes.\n\n### Continuation\n\nA continuation can be used to ignore the next keystroke or instruction. This is useful to help with formatting a long\nsequence of character and inserting a new line for readability.\n\nInsert a continuation using the character sequence `<\\>`. The following instruction the sequence will be skipped over\n(including another continuation).\n\n## Transforming text for text editors\n\nSome text editors like Visual Studio Code will automatically indent or insert closing brackets which can cause\nduplication of whitespace and characters. The `transform` subcommand can help transform text files for better\ncompatibility with similar text editors.\n\nExample:\n\n```sh\nsendkeys transform --input-file examples/node.js\n```\n\nYou can also pipe the output of the `transform` command directly to your editor of choice. Example:\n\n```sh\nsendkeys transform --input-file examples/node.js | sendkeys --application-name \"Code\"\n```\n\n## Retrieving mouse position\n\nThe `mouse-position` sub command can be used to help determine which mouse coordinates to use in your scripts.\n\nFor a one off read, move your mouse to the desired position, switch to your terminal app using `command` + `tab` and\nexecute the following command:\n\n```sh\nsendkeys mouse-position\n```\n\nUse the `--watch` option to capture the location of mouse clicks, and combine it with `--output commands` to output\napproximate mouse commands that can be used to _replay_ mouse actions.\n\n```sh\n# capture mouse commands\nsendkeys mouse-position --watch --output commands > mouse_commands.txt\n\n# replay mouse commands\nsendkeys --input-file mouse_commands.txt\n```\n\n## List of applications names\n\nA list of the current applications that can be activated by SendKeys (`--application-name`) can be displayed using the\n`apps` command.\n\n```sh\n# list apps that can be activated with --application-name\nsendkeys apps\n```\n\nSample output:\n\n```text\nCode             id:com.microsoft.VSCode\nFinder           id:com.apple.finder\nGoogle Chrome    id:com.google.Chrome\nSafari           id:com.apple.Safari\n```\n\nThe first column includes the application name and the second column includes the application's bundle ID.\n\nSendKeys will use `--application-name` to activate the first application instance that matches either the application\nname or bundle id (case insensitive). If there are no exact matches, it will attempt to match on whole words for the\napplication name, followed by the bundle id.\n\n## Configuration\n\nCommon arguments can be stored in a [`.sendkeysrc.yml`](./examples/.senkeysrc.yml) configuration file. Configuration\nvalues are applied and merged in the following priority order:\n\n1. Command line arguments\n2. Configuration file defined with `--config` option\n3. Configuration file defined in `~/.sendkeysrc.yml`\n4. Default values\n\n## Prerequisites\n\nThis application will only run on macOS 10.11 or later.\n\nWhen running from the terminal, ensure that the terminal has permission to use accessibility features. This can be done\nby navigating to System Preferences > Security & Privacy > Privacy > Accessibility and adding your terminal application\nthere.\n\n![accessibility settings](https://github.com/socsieng/sendkeys/raw/main/docs/images/accessibility.gif)\n\n## Installing previous versions\n\nA specific version of the package can be installed by targeting the appropriate release artifact. Here's an example of\nthe command:\n\n```sh\nbrew install --force-bottle https://github.com/socsieng/sendkeys/releases/download/v2.3.0/sendkeys-2.3.0.catalina.bottle.tar.gz\n```\n"
  },
  {
    "path": "Sources/SendKeysLib/Animator.swift",
    "content": "import Foundation\n\nclass Animator {\n    typealias AnimationCallback = (_ progress: Double) -> Void\n\n    let duration: TimeInterval\n    let frequency: TimeInterval\n    let animateFn: AnimationCallback\n\n    init(_ duration: TimeInterval, _ frequency: TimeInterval, _ animateFn: @escaping AnimationCallback) {\n        self.duration = duration\n        self.frequency = frequency\n        self.animateFn = animateFn\n    }\n\n    func animate() {\n        let startDate = Date()\n\n        while -startDate.timeIntervalSinceNow < duration {\n            let progress = min(-startDate.timeIntervalSinceNow as Double / duration as Double, 1)\n            let easedValue = easeInOut(progress)\n\n            Sleeper.sleep(seconds: frequency)\n            animateFn(easedValue)\n        }\n\n        animateFn(1)\n    }\n\n    func easeInOut(_ x: Double) -> Double {\n        return x < 0.5 ? 2 * x * x : 1 - pow(-2 * x + 2, 2) / 2\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/AppActivator.swift",
    "content": "import Cocoa\n\nclass AppActivator: NSObject {\n    private var application: NSRunningApplication!\n    private let appName: String?\n    private let processId: Int?\n\n    init(appName: String?, processId: Int?) {\n        self.appName = appName?.lowercased()\n        self.processId = processId\n    }\n\n    func find() throws -> NSRunningApplication? {\n        let apps = NSWorkspace.shared.runningApplications.filter({ a in\n            return a.activationPolicy == .regular\n        })\n\n        var app: NSRunningApplication?\n\n        if processId != nil {\n            app =\n                apps.filter({ a in\n                    return a.processIdentifier == pid_t(processId!)\n                }).first\n\n            if app == nil {\n                throw RuntimeError(\n                    \"Application with process id \\(processId!) could not be found.\"\n                )\n            }\n        } else if appName != nil {\n            // exact match (case insensitive)\n            app =\n                apps.filter({ a in\n                    return a.localizedName?.lowercased() == appName\n                        || a.bundleIdentifier?.lowercased() == appName\n                }).first\n\n            let expression = try! NSRegularExpression(\n                pattern: \"\\\\b\\(NSRegularExpression.escapedPattern(for: appName!))\\\\b\", options: .caseInsensitive)\n\n            // partial name match\n            if app == nil {\n                app =\n                    apps.filter({ a in\n                        let nameMatch = expression.firstMatch(\n                            in: a.localizedName ?? \"\", options: [],\n                            range: NSMakeRange(0, a.localizedName?.utf16.count ?? 0)\n                        )\n                        return nameMatch != nil\n                    }).first\n            }\n\n            // patial bundle id match\n            if app == nil {\n                app =\n                    apps.filter({ a in\n                        let bundleMatch = expression.firstMatch(\n                            in: a.bundleIdentifier ?? \"\", options: [],\n                            range: NSMakeRange(0, a.bundleIdentifier?.utf16.count ?? 0))\n                        return bundleMatch != nil\n                    }).first\n            }\n        }\n\n        return app\n    }\n\n    func activate() throws {\n        let app = try! self.find()\n\n        if app == nil && appName != nil {\n            throw RuntimeError(\n                \"Application \\(appName!) cannot be activated. Run `sendkeys apps` to see a list of applications that can be activated.\"\n            )\n        }\n\n        if app != nil {\n            self.application = app\n\n            self.unhideAppIfNeeded()\n            self.activateAppIfNeeded()\n        }\n    }\n\n    private func unhideAppIfNeeded() {\n        if application.isHidden {\n            application.addObserver(self, forKeyPath: \"isHidden\", options: .new, context: nil)\n            application.unhide()\n        }\n    }\n\n    private func activateAppIfNeeded() {\n        if !application.isHidden && !application.isActive {\n            application.addObserver(self, forKeyPath: \"isActive\", options: .new, context: nil)\n            application.activate(options: .activateIgnoringOtherApps)\n        }\n    }\n\n    override func observeValue(\n        forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?,\n        context: UnsafeMutableRawPointer?\n    ) {\n        if keyPath == \"isHidden\" {\n            application.removeObserver(self, forKeyPath: \"isHidden\")\n            activateAppIfNeeded()\n        } else if keyPath == \"isActive\" {\n            application.removeObserver(self, forKeyPath: \"isActive\")\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/AppLister.swift",
    "content": "import ArgumentParser\nimport Cocoa\nimport Foundation\n\nclass AppLister: ParsableCommand {\n    public static let configuration = CommandConfiguration(\n        commandName: \"apps\",\n        abstract:\n            \"Lists apps that can be used with the send command.\"\n    )\n\n    struct AppInfo: Hashable {\n        let name: String?\n        let id: String?\n\n        init(name: String?, id: String?) {\n            self.name = name\n            self.id = id\n        }\n\n        static func == (lhs: AppInfo, rhs: AppInfo) -> Bool {\n            return lhs.name == rhs.name && lhs.id == rhs.id\n        }\n\n        func hash(into hasher: inout Hasher) {\n            hasher.combine(self.name)\n            hasher.combine(self.id)\n        }\n    }\n\n    required init() {\n    }\n\n    func run() {\n        let apps = Set(\n            NSWorkspace.shared.runningApplications.filter { app in\n                return app.activationPolicy == .regular\n            }\n            .map { app in\n                return AppInfo(name: app.localizedName, id: app.bundleIdentifier)\n            }\n        )\n        .sorted { a, b in\n            return a.name?.lowercased() ?? \"\" < b.name?.lowercased() ?? \"\"\n        }\n\n        let maxLength = apps.reduce(\n            0,\n            { max, info in\n                return info.name?.count ?? 0 > max ? info.name!.count : max\n            })\n\n        apps.forEach { info in\n            print(\n                \"\\((info.name ?? \"-\").padding(toLength: maxLength + 4, withPad: \" \", startingAt: 0))id:\\(info.id ?? \"-\")\"\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Bridge.swift",
    "content": "func bridge<T: AnyObject>(obj: T) -> UnsafeRawPointer {\n    return UnsafeRawPointer(Unmanaged.passUnretained(obj).toOpaque())\n}\n\nfunc bridge<T: AnyObject>(ptr: UnsafeRawPointer) -> T {\n    return Unmanaged<T>.fromOpaque(ptr).takeUnretainedValue()\n}\n\nfunc bridgeRetained<T: AnyObject>(obj: T) -> UnsafeRawPointer {\n    return UnsafeRawPointer(Unmanaged.passRetained(obj).toOpaque())\n}\n\nfunc bridgeTransfer<T: AnyObject>(ptr: UnsafeRawPointer) -> T {\n    return Unmanaged<T>.fromOpaque(ptr).takeRetainedValue()\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Commands/Command.swift",
    "content": "import Foundation\n\npublic enum CommandType {\n    case undefined\n    case keyPress\n    case keyDown\n    case keyUp\n    case pause\n    case stickyPause\n    case mouseMove\n    case mousePath\n    case mouseClick\n    case mouseDrag\n    case mouseScroll\n    case mouseDown\n    case mouseUp\n    case continuation\n}\n\npublic protocol CommandProtocol {\n    static var commandType: CommandType { get }\n    static var expression: NSRegularExpression { get }\n\n    init(arguments: [String?])\n    func execute() throws\n    func equals(_ comparison: Command) -> Bool\n}\n\nprotocol RequiresKeyPresser {\n    var keyPresser: KeyPresser? { get set }\n}\n\nprotocol RequiresMouseController {\n    var mouseController: MouseController? { get set }\n}\n\npublic class Command: Equatable, CustomStringConvertible {\n    public class var commandType: CommandType { return .undefined }\n\n    private static let _expression = try! NSRegularExpression(pattern: \".\")\n    public class var expression: NSRegularExpression { return _expression }\n\n    init() {}\n\n    required public init(arguments: [String?]) {\n    }\n\n    public func execute() throws {\n    }\n\n    public func equals(_ comparison: Command) -> Bool {\n        return type(of: self) == type(of: comparison)\n    }\n\n    public static func == (lhs: Command, rhs: Command) -> Bool {\n        return lhs.equals(rhs) && rhs.equals(lhs)\n    }\n\n    public var description: String {\n        let output = \"\\(type(of: self)): \\(type(of: self).commandType)\"\n        let members = describeMembers()\n\n        if !members.isEmpty {\n            return \"\\(output) (\\(members))\"\n        }\n\n        return output\n    }\n\n    func describeMembers() -> String {\n        return \"\"\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Commands/CommandExecutor.swift",
    "content": "import Foundation\n\npublic protocol CommandExecutorProtocol {\n    func execute(_ command: Command)\n}\n\npublic class CommandExecutor: CommandExecutorProtocol {\n    public func execute(_ command: Command) {\n        try! command.execute()\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Commands/CommandFactory.swift",
    "content": "public class CommandFactory {\n    public static let commands: [Command.Type] = [\n        KeyPressCommand.self,\n        KeyDownCommand.self,\n        KeyUpCommand.self,\n        StickyPauseCommand.self,\n        PauseCommand.self,\n        ContinuationCommand.self,\n        NewlineCommand.self,\n        MouseMoveCommand.self,\n        MousePathCommand.self,\n        MouseClickCommand.self,\n        MouseDragCommand.self,\n        MouseScrollCommand.self,\n        MouseDownCommand.self,\n        MouseUpCommand.self,\n        MouseFocusCommand.self,\n        DefaultCommand.self,\n    ]\n\n    let keyPresser: KeyPresser\n    let mouseController: MouseController\n\n    init(keyPresser: KeyPresser, mouseController: MouseController) {\n        self.keyPresser = keyPresser\n        self.mouseController = mouseController\n    }\n\n    convenience public init(keyPresser: KeyPresser) {\n        self.init(\n            keyPresser: keyPresser,\n            mouseController: MouseController(animationRefreshInterval: 0.01, keyPresser: keyPresser))\n    }\n\n    public func create(_ commandType: Command.Type, arguments: [String?]) -> Command {\n        let command = commandType.init(arguments: arguments)\n\n        if var keyCommand = command as? RequiresKeyPresser {\n            keyCommand.keyPresser = keyPresser\n        }\n\n        if var mouseCommand = command as? RequiresMouseController {\n            mouseCommand.mouseController = mouseController\n        }\n\n        return command\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Commands/CommandsIterator.swift",
    "content": "import Foundation\n\npublic class CommandsIterator: IteratorProtocol {\n    public typealias Element = Command\n\n    let commandString: String\n    let commandFactory: CommandFactory\n\n    var index = 0\n\n    public init(_ commandString: String, commandFactory: CommandFactory) {\n        self.commandString = commandString\n        self.commandFactory = commandFactory\n    }\n\n    public func next() -> Element? {\n        let length = commandString.utf16.count\n        if index < length {\n            var matchResult: NSTextCheckingResult?\n            if let commandType = CommandFactory.commands.first(where: { (commandType: Command.Type) -> Bool in\n                matchResult = commandType.expression.firstMatch(\n                    in: commandString, options: .anchored, range: NSMakeRange(index, length - index))\n                return matchResult != nil\n            }\n            ) {\n                let args = getArguments(commandString, matchResult!)\n                let command = commandFactory.create(commandType, arguments: args)\n\n                if matchResult != nil {\n                    let range = Range(matchResult!.range, in: commandString)\n                    index = range!.upperBound.utf16Offset(in: commandString)\n                }\n\n                return command\n            } else {\n                fatalError(\"Unmatched sequence.\\n\")\n            }\n        }\n        return nil\n    }\n\n    private func getArguments(_ commandString: String, _ matchResult: NSTextCheckingResult) -> [String?] {\n        var args: [String?] = []\n        let numberOfRanges = matchResult.numberOfRanges\n\n        for i in 0..<numberOfRanges {\n            let range = Range(matchResult.range(at: i), in: commandString)\n            let arg = range == nil ? nil : String(commandString[range!])\n            args.append(arg)\n        }\n\n        return args\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Commands/CommandsProcessor.swift",
    "content": "import Foundation\n\npublic class CommandsProcessor {\n    var defaultPause: TimeInterval\n\n    let numberFormatter = NumberFormatter()\n    let commandExecutor: CommandExecutorProtocol\n    let keyPresser: KeyPresser\n    let mouseController: MouseController\n\n    init(\n        defaultPause: Double, keyPresser: KeyPresser, mouseController: MouseController,\n        commandExecutor: CommandExecutorProtocol? = nil\n    ) {\n        self.defaultPause = defaultPause\n        self.commandExecutor = commandExecutor ?? CommandExecutor()\n        self.keyPresser = keyPresser\n        self.mouseController = mouseController\n\n        numberFormatter.usesSignificantDigits = true\n        numberFormatter.minimumSignificantDigits = 1\n        numberFormatter.maximumSignificantDigits = 3\n    }\n\n    convenience public init(\n        defaultPause: Double, keyPresser: KeyPresser, commandExecutor: CommandExecutorProtocol? = nil\n    ) {\n        self.init(\n            defaultPause: defaultPause, keyPresser: keyPresser,\n            mouseController: MouseController(animationRefreshInterval: 0.01, keyPresser: keyPresser),\n            commandExecutor: commandExecutor)\n    }\n\n    private func getDefaultPauseCommand() -> Command {\n        return PauseCommand(duration: defaultPause)\n    }\n\n    public func process(_ commandString: String) {\n        let commandFactory = CommandFactory(keyPresser: keyPresser, mouseController: mouseController)\n        let commands = IteratorSequence(CommandsIterator(commandString, commandFactory: commandFactory))\n        var shouldDefaultPause = false\n        var shouldIgnoreNextCommand = false\n\n        for command in commands {\n            if shouldIgnoreNextCommand {\n                shouldIgnoreNextCommand = false\n                continue\n            }\n\n            if command is ContinuationCommand {\n                shouldIgnoreNextCommand = true\n                continue\n            }\n\n            if command is StickyPauseCommand {\n                shouldDefaultPause = false\n                defaultPause = (command as! StickyPauseCommand).duration\n            } else if command is PauseCommand {\n                shouldDefaultPause = false\n            } else if shouldDefaultPause {\n                commandExecutor.execute(getDefaultPauseCommand())\n                shouldDefaultPause = true\n            } else {\n                shouldDefaultPause = true\n            }\n\n            commandExecutor.execute(command)\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Commands/ContinuationCommand.swift",
    "content": "import Foundation\n\npublic class ContinuationCommand: Command {\n    public override class var commandType: CommandType { return .continuation }\n\n    private static let _expression = try! NSRegularExpression(pattern: \"\\\\<\\\\\\\\\\\\>\")\n    public override class var expression: NSRegularExpression { return _expression }\n\n    public override init() {\n        super.init()\n    }\n\n    required public init(arguments: [String?]) {\n        super.init(arguments: arguments)\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Commands/DefaultCommand.swift",
    "content": "import Foundation\n\npublic class DefaultCommand: KeyPressCommand {\n    private static let _expression = try! NSRegularExpression(pattern: \".\")\n    public override class var expression: NSRegularExpression { return _expression }\n\n    public init(key: String) {\n        super.init()\n\n        self.key = key\n    }\n\n    required public init(arguments: [String?]) {\n        super.init()\n        self.key = arguments[0]!\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Commands/KeyDownCommand.swift",
    "content": "import Foundation\n\npublic class KeyDownCommand: KeyPressCommand {\n    public override class var commandType: CommandType { return .keyDown }\n\n    private static let _expression = try! NSRegularExpression(pattern: \"\\\\<kd:(.|[\\\\w]+)(:([,\\\\w⌘^⌥⇧]+))?\\\\>\")\n    public override class var expression: NSRegularExpression { return _expression }\n\n    public override init(key: String, modifiers: [String]) {\n        super.init(key: key, modifiers: modifiers)\n    }\n\n    required public init(arguments: [String?]) {\n        super.init(arguments: arguments)\n    }\n\n    public override func execute() throws {\n        let _ = try! keyPresser!.keyDown(key: key!, modifiers: modifiers)\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Commands/KeyPressCommand.swift",
    "content": "import Foundation\n\npublic class KeyPressCommand: Command, RequiresKeyPresser {\n    public override class var commandType: CommandType { return .keyPress }\n\n    private static let _expression = try! NSRegularExpression(pattern: \"\\\\<[ck]:(.|[\\\\w]+)(:([,\\\\w⌘^⌥⇧]+))?\\\\>\")\n    public override class var expression: NSRegularExpression { return _expression }\n\n    var key: String?\n    var modifiers: [String] = []\n\n    var keyPresser: KeyPresser?\n\n    override init() {\n        super.init()\n    }\n\n    public init(key: String, modifiers: [String]) {\n        super.init()\n\n        self.key = key\n        self.modifiers = modifiers\n    }\n\n    required public init(arguments: [String?]) {\n        super.init()\n\n        self.key = arguments[1]!\n        self.modifiers = arguments[3]?.components(separatedBy: \",\").filter({ !$0.isEmpty }) ?? []\n    }\n\n    public override func execute() throws {\n        let _ = try! keyPresser!.keyPress(key: key!, modifiers: modifiers)\n    }\n\n    public override func equals(_ comparison: Command) -> Bool {\n        return super.equals(comparison)\n            && {\n                if let command = comparison as? KeyPressCommand {\n                    return key == command.key\n                        && modifiers == command.modifiers\n                }\n                return false\n            }()\n    }\n\n    public override func describeMembers() -> String {\n        return \"key: \\(key ?? \"''\")), modifiers: \\(modifiers)\"\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Commands/KeyUpCommand.swift",
    "content": "import Foundation\n\npublic class KeyUpCommand: KeyPressCommand {\n    public override class var commandType: CommandType { return .keyUp }\n\n    private static let _expression = try! NSRegularExpression(pattern: \"\\\\<ku:(.|[\\\\w]+)(:([,\\\\w⌘^⌥⇧]+))?\\\\>\")\n    public override class var expression: NSRegularExpression { return _expression }\n\n    public override init(key: String, modifiers: [String]) {\n        super.init(key: key, modifiers: modifiers)\n    }\n\n    required public init(arguments: [String?]) {\n        super.init(arguments: arguments)\n    }\n\n    public override func execute() throws {\n        let _ = try! keyPresser!.keyUp(key: key!, modifiers: modifiers)\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Commands/MouseClickCommand.swift",
    "content": "import Cocoa\nimport Foundation\n\npublic class MouseClickCommand: Command, RequiresMouseController {\n    public override class var commandType: CommandType { return .mouseClick }\n\n    private static let _expression = try! NSRegularExpression(pattern: \"\\\\<m:([a-z]+)(:([a-z,]+))?(:(\\\\d+))?\\\\>\")\n    public override class var expression: NSRegularExpression { return _expression }\n\n    var button: String?\n    var modifiers: [String] = []\n    var clicks: Int = 1\n\n    var mouseController: MouseController?\n\n    override init() {\n        super.init()\n    }\n\n    public init(button: String?, modifiers: [String], clicks: Int) {\n        super.init()\n\n        self.button = button\n        self.modifiers = modifiers\n        self.clicks = clicks\n    }\n\n    required public init(arguments: [String?]) {\n        super.init()\n\n        self.button = arguments[1]!\n        self.modifiers = arguments[3]?.components(separatedBy: \",\").filter({ !$0.isEmpty }) ?? []\n        self.clicks = Int(arguments[5] ?? \"1\")!\n    }\n\n    public override func execute() throws {\n        try! mouseController!.click(\n            nil,\n            button: getMouseButton(button: button!),\n            flags: try! KeyPresser.getModifierFlags(modifiers),\n            clickCount: clicks\n        )\n    }\n\n    public override func equals(_ comparison: Command) -> Bool {\n        return super.equals(comparison)\n            && {\n                if let command = comparison as? MouseClickCommand {\n                    return button == command.button\n                        && modifiers == command.modifiers\n                        && clicks == command.clicks\n                }\n                return false\n            }()\n    }\n\n    public override func describeMembers() -> String {\n        return \"button: \\(button ?? \"''\")), modifiers: \\(modifiers), clicks: \\(clicks)\"\n    }\n\n    func getMouseButton(button: String) throws -> CGMouseButton {\n        switch button {\n        case \"left\":\n            return CGMouseButton.left\n        case \"center\":\n            return CGMouseButton.center\n        case \"right\":\n            return CGMouseButton.right\n        default:\n            throw RuntimeError(\"Unknown mouse button: \\(button)\")\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Commands/MouseDownCommand.swift",
    "content": "import Foundation\n\npublic class MouseDownCommand: MouseClickCommand {\n    public override class var commandType: CommandType { return .mouseDown }\n\n    private static let _expression = try! NSRegularExpression(pattern: \"\\\\<md:([a-z]+)(:([a-z,]+))?\\\\>\")\n    public override class var expression: NSRegularExpression { return _expression }\n\n    override init() {\n        super.init()\n    }\n\n    public init(button: String?, modifiers: [String]) {\n        super.init()\n\n        self.button = button\n        self.modifiers = modifiers\n    }\n\n    required public init(arguments: [String?]) {\n        super.init()\n\n        self.button = arguments[1]!\n        self.modifiers = arguments[3]?.components(separatedBy: \",\").filter({ !$0.isEmpty }) ?? []\n    }\n\n    public override func execute() throws {\n        try! mouseController!.down(\n            nil,\n            button: getMouseButton(button: button!),\n            flags: try! KeyPresser.getModifierFlags(modifiers)\n        )\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Commands/MouseDragCommand.swift",
    "content": "import Foundation\n\npublic class MouseDragCommand: MouseMoveCommand {\n    public override class var commandType: CommandType { return .mouseDrag }\n\n    private static let _expression = try! NSRegularExpression(\n        pattern: \"\\\\<d:((-?[.\\\\d]+),(-?[.\\\\d]+),)?(-?[.\\\\d]+),(-?[.\\\\d]+)(:([\\\\d.]+))?(:([a-z]+))?(:([a-z,]+))?\\\\>\")\n    public override class var expression: NSRegularExpression { return _expression }\n\n    public init(\n        x1: Double?, y1: Double?, x2: Double, y2: Double, duration: TimeInterval, button: String?, modifiers: [String]\n    ) {\n        super.init()\n\n        self.x1 = x1\n        self.y1 = y1\n        self.x2 = x2\n        self.y2 = y2\n        self.duration = duration\n        self.button = button\n        self.modifiers = modifiers\n    }\n\n    required public init(arguments: [String?]) {\n        super.init()\n        self.x1 = Double(arguments[2] ?? \"\")\n        self.y1 = Double(arguments[3] ?? \"\")\n        self.x2 = Double(arguments[4]!)!\n        self.y2 = Double(arguments[5]!)!\n        self.duration = TimeInterval(arguments[7] ?? \"0\")!\n        self.button = arguments[9] ?? \"left\"\n        self.modifiers = arguments[11]?.components(separatedBy: \",\").filter({ !$0.isEmpty }) ?? []\n    }\n\n    public override func describeMembers() -> String {\n        return\n            \"x1: \\(x1?.description ?? \"nil\")), y1: \\(y1?.description ?? \"nil\"), x2: \\(x2), y2: \\(y2), duration: \\(duration), button: \\(button ?? \"''\")), modifiers: \\(modifiers)\"\n    }\n\n    public override func execute() throws {\n        try! mouseController!.drag(\n            start: x1 == nil || y1 == nil ? nil : CGPoint(x: x1!, y: y1!),\n            end: CGPoint(x: x2, y: y2),\n            duration: duration,\n            button: getMouseButton(button: button!),\n            flags: try! KeyPresser.getModifierFlags(modifiers)\n        )\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Commands/MouseFocusCommand.swift",
    "content": "import Foundation\n\npublic class MouseFocusCommand: MouseClickCommand {\n    public override class var commandType: CommandType { return .mouseScroll }\n\n    // <mf:centerX,centerY:radiusX[,radiusY]:angleFrom,angleTo:duration>\n    private static let _expression = try! NSRegularExpression(\n        pattern: \"\\\\<mf:(-?[.\\\\d]+),(-?[.\\\\d]+):([.\\\\d]+)(,([.\\\\d]+))?:(-?[.\\\\d]+),(-?[.\\\\d]+):([.\\\\d]+)\\\\>\")\n    public override class var expression: NSRegularExpression { return _expression }\n\n    var x: Double\n    var y: Double\n    var rx: Double\n    var ry: Double\n    var from: Double\n    var to: Double\n    var duration: TimeInterval\n\n    public init(\n        x: Double, y: Double, rx: Double, ry: Double, angleFrom: Double, angleTo: Double, duration: TimeInterval\n    ) {\n        self.x = x\n        self.y = y\n        self.rx = rx\n        self.ry = ry\n        self.from = angleFrom\n        self.to = angleTo\n        self.duration = duration\n\n        super.init()\n    }\n\n    required public init(arguments: [String?]) {\n        self.x = Double(arguments[1]!)!\n        self.y = Double(arguments[2]!)!\n        self.rx = Double(arguments[3]!)!\n        self.ry = Double(arguments[5] ?? arguments[3]!)!\n        self.from = Double(arguments[6]!)!\n        self.to = Double(arguments[7]!)!\n        self.duration = TimeInterval(arguments[8]!)!\n\n        super.init()\n    }\n\n    public override func execute() throws {\n        mouseController!.circle(CGPoint(x: x, y: y), CGPoint(x: rx, y: ry), from, to, duration)\n    }\n\n    public override func describeMembers() -> String {\n        return \"x: \\(x), y: \\(y), rx: \\(rx), ry: \\(ry), from: \\(from), to: \\(to), duration: \\(duration)\"\n    }\n\n    public override func equals(_ comparison: Command) -> Bool {\n        return super.equals(comparison)\n            && {\n                if let command = comparison as? MouseFocusCommand {\n                    return x == command.x\n                        && y == command.y\n                        && rx == command.rx\n                        && ry == command.ry\n                        && from == command.from\n                        && to == command.to\n                        && duration == command.duration\n                }\n                return false\n            }()\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Commands/MouseMoveCommand.swift",
    "content": "import Foundation\n\npublic class MouseMoveCommand: MouseClickCommand {\n    public override class var commandType: CommandType { return .mouseMove }\n\n    private static let _expression = try! NSRegularExpression(\n        pattern: \"\\\\<m:((-?[.\\\\d]+),(-?[.\\\\d]+),)?(-?[.\\\\d]+),(-?[.\\\\d]+)(:([\\\\d.]+))?(:([a-z,]+))?\\\\>\")\n    public override class var expression: NSRegularExpression { return _expression }\n\n    var x1: Double?\n    var y1: Double?\n    var x2: Double = 0\n    var y2: Double = 0\n    var duration: TimeInterval = 0\n\n    override init() {\n        super.init()\n    }\n\n    public init(x1: Double?, y1: Double?, x2: Double, y2: Double, duration: TimeInterval, modifiers: [String]) {\n        super.init()\n\n        self.x1 = x1\n        self.y1 = y1\n        self.x2 = x2\n        self.y2 = y2\n        self.duration = duration\n        self.modifiers = modifiers\n    }\n\n    required public init(arguments: [String?]) {\n        super.init()\n        self.x1 = Double(arguments[2] ?? \"\")\n        self.y1 = Double(arguments[3] ?? \"\")\n        self.x2 = Double(arguments[4]!)!\n        self.y2 = Double(arguments[5]!)!\n        self.duration = TimeInterval(arguments[7] ?? \"0\")!\n        self.modifiers = arguments[9]?.components(separatedBy: \",\").filter({ !$0.isEmpty }) ?? []\n    }\n\n    public override func execute() throws {\n        mouseController!.move(\n            start: x1 == nil || y1 == nil ? nil : CGPoint(x: x1!, y: y1!),\n            end: CGPoint(x: x2, y: y2),\n            duration: duration,\n            flags: try! KeyPresser.getModifierFlags(modifiers)\n        )\n    }\n\n    public override func equals(_ comparison: Command) -> Bool {\n        return super.equals(comparison)\n            && {\n                if let command = comparison as? MouseMoveCommand {\n                    return x1 == command.x1\n                        && y1 == command.y1\n                        && x2 == command.x2\n                        && y2 == command.y2\n                        && duration == command.duration\n                }\n                return false\n            }()\n    }\n\n    public override func describeMembers() -> String {\n        return\n            \"x1: \\(x1?.description ?? \"nil\")), y1: \\(y1?.description ?? \"nil\"), x2: \\(x2), y2: \\(y2), duration: \\(duration)\"\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Commands/MousePathCommand.swift",
    "content": "import Foundation\n\npublic class MousePathCommand: MouseClickCommand {\n    public override class var commandType: CommandType { return .mousePath }\n\n    private static let _expression = try! NSRegularExpression(\n        pattern:\n            \"\\\\<mpath:([^:\\\\>]+)(:(-?[\\\\d.]+),(-?[\\\\d.]+)(,(-?[\\\\d.]+)(,(-?[\\\\d.]+))?)?)?:([\\\\d.]+)(:([a-z,]+))?\\\\>\")\n    public override class var expression: NSRegularExpression { return _expression }\n\n    var path: String\n    var offsetX: Double = 0\n    var offsetY: Double = 0\n    var scaleX: Double = 1\n    var scaleY: Double = 1\n    var duration: TimeInterval = 0\n\n    public init(\n        path: String, offsetX: Double, offsetY: Double, scaleX: Double, scaleY: Double, duration: TimeInterval,\n        modifiers: [String]\n    ) {\n        self.path = path\n        self.offsetX = offsetX\n        self.offsetY = offsetY\n        self.scaleX = scaleX\n        self.scaleY = scaleY\n        self.duration = duration\n\n        super.init()\n        self.modifiers = modifiers\n    }\n\n    required public init(arguments: [String?]) {\n        self.path = arguments[1]!\n        self.offsetX = Double(arguments[3] ?? \"0\")!\n        self.offsetY = Double(arguments[4] ?? \"0\")!\n        self.scaleX = Double(arguments[6] ?? \"1\")!\n        self.scaleY = Double(arguments[8] ?? arguments[6] ?? \"1\")!\n        self.duration = TimeInterval(arguments[9] ?? \"0\")!\n\n        super.init()\n        self.modifiers = arguments[10]?.components(separatedBy: \",\").filter({ !$0.isEmpty }) ?? []\n    }\n\n    public override func execute() throws {\n        mouseController!.move(\n            start: nil,\n            path: path,\n            offset: CGPoint(x: offsetX, y: offsetY),\n            scale: CGPoint(x: scaleX, y: scaleY),\n            duration: duration,\n            flags: try! KeyPresser.getModifierFlags(modifiers)\n        )\n    }\n\n    public override func equals(_ comparison: Command) -> Bool {\n        return super.equals(comparison)\n            && {\n                if let command = comparison as? MousePathCommand {\n                    return path == command.path\n                        && offsetX == command.offsetX\n                        && offsetY == command.offsetY\n                        && scaleX == command.scaleX\n                        && scaleY == command.scaleY\n                        && duration == command.duration\n                }\n                return false\n            }()\n    }\n\n    public override func describeMembers() -> String {\n        return\n            \"path: \\(path), offsetX: \\(offsetX), offsetY: \\(offsetY), scaleX: \\(scaleX), scaleY: \\(scaleY), duration: \\(duration)\"\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Commands/MouseScrollCommand.swift",
    "content": "import Foundation\n\npublic class MouseScrollCommand: MouseClickCommand {\n    public override class var commandType: CommandType { return .mouseScroll }\n\n    private static let _expression = try! NSRegularExpression(\n        pattern: \"\\\\<s:(-?[.\\\\d]+),(-?[.\\\\d]+)(:([.\\\\d]+))?(:([a-z,]+))?\\\\>\")\n    public override class var expression: NSRegularExpression { return _expression }\n\n    var x: Double\n    var y: Double\n    var duration: TimeInterval\n\n    public init(x: Double, y: Double, duration: TimeInterval, modifiers: [String]) {\n        self.x = x\n        self.y = y\n        self.duration = duration\n\n        super.init()\n        self.modifiers = modifiers\n    }\n\n    required public init(arguments: [String?]) {\n        self.x = Double(arguments[1]!)!\n        self.y = Double(arguments[2]!)!\n        self.duration = TimeInterval(arguments[4] ?? \"0\")!\n\n        super.init()\n        self.modifiers = arguments[6]?.components(separatedBy: \",\").filter({ !$0.isEmpty }) ?? []\n    }\n\n    public override func execute() throws {\n        mouseController!.scroll(\n            CGPoint(x: x, y: y),\n            duration,\n            flags: try! KeyPresser.getModifierFlags(modifiers)\n        )\n    }\n\n    public override func describeMembers() -> String {\n        return \"x: \\(x), y: \\(y), duration: \\(duration), modifiers: \\(modifiers)\"\n    }\n\n    public override func equals(_ comparison: Command) -> Bool {\n        return super.equals(comparison)\n            && {\n                if let command = comparison as? MouseScrollCommand {\n                    return x == command.x\n                        && y == command.y\n                        && duration == command.duration\n                }\n                return false\n            }()\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Commands/MouseUpCommand.swift",
    "content": "import Foundation\n\npublic class MouseUpCommand: MouseClickCommand {\n    public override class var commandType: CommandType { return .mouseUp }\n\n    private static let _expression = try! NSRegularExpression(pattern: \"\\\\<mu:([a-z]+)(:([a-z,]+))?\\\\>\")\n    public override class var expression: NSRegularExpression { return _expression }\n\n    override init() {\n        super.init()\n    }\n\n    public init(button: String?, modifiers: [String]) {\n        super.init()\n\n        self.button = button\n        self.modifiers = modifiers\n    }\n\n    required public init(arguments: [String?]) {\n        super.init()\n\n        self.button = arguments[1]!\n        self.modifiers = arguments[3]?.components(separatedBy: \",\").filter({ !$0.isEmpty }) ?? []\n    }\n\n    public override func execute() throws {\n        try! mouseController!.up(\n            nil,\n            button: getMouseButton(button: button!),\n            flags: try! KeyPresser.getModifierFlags(modifiers)\n        )\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Commands/NewlineCommand.swift",
    "content": "import Foundation\n\npublic class NewlineCommand: KeyPressCommand {\n    private static let _expression = try! NSRegularExpression(pattern: \"\\\\r?\\\\n\")\n    public override class var expression: NSRegularExpression { return _expression }\n\n    public override init() {\n        super.init()\n        self.key = \"return\"\n    }\n\n    required public convenience init(arguments: [String?]) {\n        self.init()\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Commands/PauseCommand.swift",
    "content": "import Foundation\n\npublic class PauseCommand: Command {\n    public override class var commandType: CommandType { return .pause }\n\n    private static let _expression = try! NSRegularExpression(pattern: \"\\\\<p:([\\\\d.]+)\\\\>\")\n    public override class var expression: NSRegularExpression { return _expression }\n\n    var duration: TimeInterval = 0\n\n    init(duration: TimeInterval) {\n        super.init()\n\n        self.duration = duration\n    }\n\n    required public init(arguments: [String?]) {\n        super.init()\n\n        self.duration = TimeInterval(arguments[1]!)!\n    }\n\n    public override func execute() throws {\n        Sleeper.sleep(seconds: duration)\n    }\n\n    public override func equals(_ comparison: Command) -> Bool {\n        return super.equals(comparison)\n            && {\n                if let command = comparison as? PauseCommand {\n                    return duration == command.duration\n                }\n                return false\n            }()\n    }\n\n    public override func describeMembers() -> String {\n        return \"duration: \\(duration)\"\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Commands/StickyPauseCommand.swift",
    "content": "import Foundation\n\npublic class StickyPauseCommand: PauseCommand {\n    public override class var commandType: CommandType { return .stickyPause }\n\n    private static let _expression = try! NSRegularExpression(pattern: \"\\\\<P:([\\\\d.]+)\\\\>\")\n    public override class var expression: NSRegularExpression { return _expression }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Configuration/AllConfiguration.swift",
    "content": "struct AllConfiguration: Codable {\n    var send: SendConfig?\n    var mousePosition: MousePositionConfig?\n    var transformer: TransformerConfig?\n\n    init(send: SendConfig? = nil, mousePosition: MousePositionConfig? = nil, transformer: TransformerConfig? = nil) {\n        self.send = send\n        self.mousePosition = mousePosition\n        self.transformer = transformer\n    }\n\n    func merge(with other: AllConfiguration?) -> AllConfiguration {\n        return AllConfiguration(\n            send: other?.send?.merge(with: self.send) ?? self.send,\n            mousePosition: other?.mousePosition?.merge(with: self.mousePosition) ?? self.mousePosition,\n            transformer: other?.transformer?.merge(with: self.transformer) ?? self.transformer\n        )\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Configuration/ConfigLoader.swift",
    "content": "import Foundation\nimport Yams\n\nlet defaultConfigFiles = [\n    NSString(\"~/.sendkeysrc.yml\").expandingTildeInPath,\n    NSString(\"~/.sendkeysrc.yaml\").expandingTildeInPath,\n]\n\nstruct ConfigLoader {\n    static func loadConfig(_ file: String? = nil) -> AllConfiguration {\n        var config = AllConfiguration()\n\n        let configFiles =\n            if file != nil {\n                [file!]\n            } else {\n                defaultConfigFiles\n            }\n\n        for configFile in configFiles {\n            if !configFile.isEmpty && FileManager.default.fileExists(atPath: configFile) {\n                if let contents = FileManager.default.contents(atPath: configFile) {\n                    do {\n                        let decoder = YAMLDecoder()\n                        config = config.merge(with: try decoder.decode(AllConfiguration.self, from: contents))\n                    } catch {\n                        print(\"Unable to read \\(configFile): \\(error)\")\n                    }\n                }\n            }\n        }\n\n        return config\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Configuration/MousePositionConfig.swift",
    "content": "struct MousePositionConfig: Codable {\n    var watch: Bool?\n    var output: OutputMode?\n    var duration: Double?\n\n    init(watch: Bool? = nil, output: OutputMode? = nil, duration: Double? = nil) {\n        self.watch = watch\n        self.output = output\n        self.duration = duration\n    }\n\n    func merge(with other: MousePositionConfig?) -> MousePositionConfig {\n        return MousePositionConfig(\n            watch: other?.watch ?? self.watch,\n            output: other?.output ?? self.output,\n            duration: other?.duration ?? self.duration\n        )\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Configuration/SendConfig.swift",
    "content": "struct SendConfig: Codable {\n    var activate: Bool?\n    var animationInterval: Double?\n    var delay: Double?\n    var initialDelay: Double?\n    var keyboardLayout: KeyMappings.Layouts?\n    var remap: [String: String]?\n    var targeted: Bool?\n    var terminateCommand: String?\n\n    init(\n        activate: Bool? = nil, animationInterval: Double? = nil, delay: Double? = nil, initialDelay: Double? = nil,\n        keyboardLayout: KeyMappings.Layouts? = nil, remap: [String: String]? = nil, targeted: Bool? = nil,\n        terminateCommand: String? = nil\n    ) {\n        self.activate = activate\n        self.animationInterval = animationInterval\n        self.delay = delay\n        self.initialDelay = initialDelay\n        self.keyboardLayout = keyboardLayout\n        self.remap = remap\n        self.targeted = targeted\n        self.terminateCommand = terminateCommand\n    }\n\n    func merge(with other: SendConfig?) -> SendConfig {\n        return SendConfig(\n            activate: other?.activate ?? self.activate,\n            animationInterval: other?.animationInterval ?? self.animationInterval,\n            delay: other?.delay ?? self.delay,\n            initialDelay: other?.initialDelay ?? self.initialDelay,\n            keyboardLayout: other?.keyboardLayout ?? self.keyboardLayout,\n            remap: other?.remap ?? self.remap,\n            targeted: other?.targeted ?? self.targeted,\n            terminateCommand: other?.terminateCommand ?? self.terminateCommand\n        )\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Configuration/TransformerConfig.swift",
    "content": "struct TransformerConfig: Codable {\n    var indent: Bool?\n    var autoClose: String?\n\n    init(indent: Bool? = nil, autoClose: String? = nil) {\n        self.indent = indent\n        self.autoClose = autoClose\n    }\n\n    func merge(with other: TransformerConfig?) -> TransformerConfig {\n        return TransformerConfig(\n            indent: other?.indent ?? self.indent,\n            autoClose: other?.autoClose ?? self.autoClose\n        )\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/KeyCodes.swift",
    "content": "import Cocoa\n\n// From: https://gist.github.com/swillits/df648e87016772c7f7e5dbed2b345066\nstruct KeyCodes {\n    // Layout-independent Keys\n    // eg.These key codes are always the same key on all layouts.\n    static let returnKey: UInt16 = 0x24\n    static let enter: UInt16 = 0x4C\n    static let tab: UInt16 = 0x30\n    static let space: UInt16 = 0x31\n    static let delete: UInt16 = 0x33\n    static let escape: UInt16 = 0x35\n    static let command: UInt16 = 0x37\n    static let shift: UInt16 = 0x38\n    static let capsLock: UInt16 = 0x39\n    static let option: UInt16 = 0x3A\n    static let control: UInt16 = 0x3B\n    static let rightShift: UInt16 = 0x3C\n    static let rightOption: UInt16 = 0x3D\n    static let rightControl: UInt16 = 0x3E\n    static let leftArrow: UInt16 = 0x7B\n    static let rightArrow: UInt16 = 0x7C\n    static let downArrow: UInt16 = 0x7D\n    static let upArrow: UInt16 = 0x7E\n    static let volumeUp: UInt16 = 0x48\n    static let volumeDown: UInt16 = 0x49\n    static let mute: UInt16 = 0x4A\n    static let help: UInt16 = 0x72\n    static let home: UInt16 = 0x73\n    static let pageUp: UInt16 = 0x74\n    static let forwardDelete: UInt16 = 0x75\n    static let end: UInt16 = 0x77\n    static let pageDown: UInt16 = 0x79\n    static let function: UInt16 = 0x3F\n    static let f1: UInt16 = 0x7A\n    static let f2: UInt16 = 0x78\n    static let f4: UInt16 = 0x76\n    static let f5: UInt16 = 0x60\n    static let f6: UInt16 = 0x61\n    static let f7: UInt16 = 0x62\n    static let f3: UInt16 = 0x63\n    static let f8: UInt16 = 0x64\n    static let f9: UInt16 = 0x65\n    static let f10: UInt16 = 0x6D\n    static let f11: UInt16 = 0x67\n    static let f12: UInt16 = 0x6F\n    static let f13: UInt16 = 0x69\n    static let f14: UInt16 = 0x6B\n    static let f15: UInt16 = 0x71\n    static let f16: UInt16 = 0x6A\n    static let f17: UInt16 = 0x40\n    static let f18: UInt16 = 0x4F\n    static let f19: UInt16 = 0x50\n    static let f20: UInt16 = 0x5A\n\n    // US-ANSI Keyboard Positions\n    // eg. These key codes are for the physical key (in any keyboard layout)\n    // at the location of the named key in the US-ANSI layout.\n    static let a: UInt16 = 0x00\n    static let b: UInt16 = 0x0B\n    static let c: UInt16 = 0x08\n    static let d: UInt16 = 0x02\n    static let e: UInt16 = 0x0E\n    static let f: UInt16 = 0x03\n    static let g: UInt16 = 0x05\n    static let h: UInt16 = 0x04\n    static let i: UInt16 = 0x22\n    static let j: UInt16 = 0x26\n    static let k: UInt16 = 0x28\n    static let l: UInt16 = 0x25\n    static let m: UInt16 = 0x2E\n    static let n: UInt16 = 0x2D\n    static let o: UInt16 = 0x1F\n    static let p: UInt16 = 0x23\n    static let q: UInt16 = 0x0C\n    static let r: UInt16 = 0x0F\n    static let s: UInt16 = 0x01\n    static let t: UInt16 = 0x11\n    static let u: UInt16 = 0x20\n    static let v: UInt16 = 0x09\n    static let w: UInt16 = 0x0D\n    static let x: UInt16 = 0x07\n    static let y: UInt16 = 0x10\n    static let z: UInt16 = 0x06\n\n    static let zero: UInt16 = 0x1D\n    static let one: UInt16 = 0x12\n    static let two: UInt16 = 0x13\n    static let three: UInt16 = 0x14\n    static let four: UInt16 = 0x15\n    static let five: UInt16 = 0x17\n    static let six: UInt16 = 0x16\n    static let seven: UInt16 = 0x1A\n    static let eight: UInt16 = 0x1C\n    static let nine: UInt16 = 0x19\n\n    static let equals: UInt16 = 0x18\n    static let minus: UInt16 = 0x1B\n    static let semicolon: UInt16 = 0x29\n    static let apostrophe: UInt16 = 0x27\n    static let comma: UInt16 = 0x2B\n    static let period: UInt16 = 0x2F\n    static let forwardSlash: UInt16 = 0x2C\n    static let backslash: UInt16 = 0x2A\n    static let grave: UInt16 = 0x32\n    static let leftBracket: UInt16 = 0x21\n    static let rightBracket: UInt16 = 0x1E\n\n    static let keypadDecimal: UInt16 = 0x41\n    static let keypadMultiply: UInt16 = 0x43\n    static let keypadPlus: UInt16 = 0x45\n    static let keypadClear: UInt16 = 0x47\n    static let keypadDivide: UInt16 = 0x4B\n    static let keypadEnter: UInt16 = 0x4C\n    static let keypadMinus: UInt16 = 0x4E\n    static let keypadEquals: UInt16 = 0x51\n    static let keypad0: UInt16 = 0x52\n    static let keypad1: UInt16 = 0x53\n    static let keypad2: UInt16 = 0x54\n    static let keypad3: UInt16 = 0x55\n    static let keypad4: UInt16 = 0x56\n    static let keypad5: UInt16 = 0x57\n    static let keypad6: UInt16 = 0x58\n    static let keypad7: UInt16 = 0x59\n    static let keypad8: UInt16 = 0x5B\n    static let keypad9: UInt16 = 0x5C\n\n    struct KeyCodeWithFlags {\n        let keyCode: UInt16\n        let flags: [CGEventFlags]\n\n        init(_ keyCode: UInt16, _ flags: [CGEventFlags] = []) {\n            self.keyCode = keyCode\n            self.flags = flags\n        }\n    }\n\n    struct KeyWithFlags {\n        let key: String\n        let flags: [CGEventFlags]\n\n        init(_ key: String, _ flags: [CGEventFlags] = []) {\n            self.key = key\n            self.flags = flags\n        }\n    }\n\n    // map\n    private static let keyDictionary = [\n        \"return\": KeyCodeWithFlags(returnKey),\n        \"enter\": KeyCodeWithFlags(enter),\n        \"tab\": KeyCodeWithFlags(tab),\n        \"space\": KeyCodeWithFlags(space),\n        \"delete\": KeyCodeWithFlags(delete),\n        \"escape\": KeyCodeWithFlags(escape),\n        \"esc\": KeyCodeWithFlags(escape),\n        \"⌘\": KeyCodeWithFlags(command),\n        \"cmd\": KeyCodeWithFlags(command),\n        \"command\": KeyCodeWithFlags(command),\n        \"shift\": KeyCodeWithFlags(shift),\n        \"capslock\": KeyCodeWithFlags(capsLock),\n        \"⌥\": KeyCodeWithFlags(option),\n        \"alt\": KeyCodeWithFlags(option),\n        \"option\": KeyCodeWithFlags(option),\n        \"ctrl\": KeyCodeWithFlags(control),\n        \"control\": KeyCodeWithFlags(control),\n        \"rightshift\": KeyCodeWithFlags(rightShift),\n        \"rightoption\": KeyCodeWithFlags(rightOption),\n        \"rightControl\": KeyCodeWithFlags(rightControl),\n        \"left\": KeyCodeWithFlags(leftArrow),\n        \"right\": KeyCodeWithFlags(rightArrow),\n        \"down\": KeyCodeWithFlags(downArrow),\n        \"up\": KeyCodeWithFlags(upArrow),\n        \"volumeup\": KeyCodeWithFlags(volumeUp),\n        \"volumedown\": KeyCodeWithFlags(volumeDown),\n        \"mute\": KeyCodeWithFlags(mute),\n        \"help\": KeyCodeWithFlags(help),\n        \"home\": KeyCodeWithFlags(home),\n        \"pgup\": KeyCodeWithFlags(pageUp),\n        \"forwarddelete\": KeyCodeWithFlags(forwardDelete),\n        \"end\": KeyCodeWithFlags(end),\n        \"pgdown\": KeyCodeWithFlags(pageDown),\n        \"fn\": KeyCodeWithFlags(function),\n        \"function\": KeyCodeWithFlags(function),\n        \"f1\": KeyCodeWithFlags(f1),\n        \"f2\": KeyCodeWithFlags(f2),\n        \"f4\": KeyCodeWithFlags(f4),\n        \"f5\": KeyCodeWithFlags(f5),\n        \"f6\": KeyCodeWithFlags(f6),\n        \"f7\": KeyCodeWithFlags(f7),\n        \"f3\": KeyCodeWithFlags(f3),\n        \"f8\": KeyCodeWithFlags(f8),\n        \"f9\": KeyCodeWithFlags(f9),\n        \"f10\": KeyCodeWithFlags(f10),\n        \"f11\": KeyCodeWithFlags(f11),\n        \"f12\": KeyCodeWithFlags(f12),\n        \"f13\": KeyCodeWithFlags(f13),\n        \"f14\": KeyCodeWithFlags(f14),\n        \"f15\": KeyCodeWithFlags(f15),\n        \"f16\": KeyCodeWithFlags(f16),\n        \"f17\": KeyCodeWithFlags(f17),\n        \"f18\": KeyCodeWithFlags(f18),\n        \"f19\": KeyCodeWithFlags(f19),\n        \"f20\": KeyCodeWithFlags(f20),\n        \"a\": KeyCodeWithFlags(a),\n        \"b\": KeyCodeWithFlags(b),\n        \"c\": KeyCodeWithFlags(c),\n        \"d\": KeyCodeWithFlags(d),\n        \"e\": KeyCodeWithFlags(e),\n        \"f\": KeyCodeWithFlags(f),\n        \"g\": KeyCodeWithFlags(g),\n        \"h\": KeyCodeWithFlags(h),\n        \"i\": KeyCodeWithFlags(i),\n        \"j\": KeyCodeWithFlags(j),\n        \"k\": KeyCodeWithFlags(k),\n        \"l\": KeyCodeWithFlags(l),\n        \"m\": KeyCodeWithFlags(m),\n        \"n\": KeyCodeWithFlags(n),\n        \"o\": KeyCodeWithFlags(o),\n        \"p\": KeyCodeWithFlags(p),\n        \"q\": KeyCodeWithFlags(q),\n        \"r\": KeyCodeWithFlags(r),\n        \"s\": KeyCodeWithFlags(s),\n        \"t\": KeyCodeWithFlags(t),\n        \"u\": KeyCodeWithFlags(u),\n        \"v\": KeyCodeWithFlags(v),\n        \"w\": KeyCodeWithFlags(w),\n        \"x\": KeyCodeWithFlags(x),\n        \"y\": KeyCodeWithFlags(y),\n        \"z\": KeyCodeWithFlags(z),\n        \"0\": KeyCodeWithFlags(zero),\n        \"1\": KeyCodeWithFlags(one),\n        \"2\": KeyCodeWithFlags(two),\n        \"3\": KeyCodeWithFlags(three),\n        \"4\": KeyCodeWithFlags(four),\n        \"5\": KeyCodeWithFlags(five),\n        \"6\": KeyCodeWithFlags(six),\n        \"7\": KeyCodeWithFlags(seven),\n        \"8\": KeyCodeWithFlags(eight),\n        \"9\": KeyCodeWithFlags(nine),\n        \"=\": KeyCodeWithFlags(equals),\n        \"-\": KeyCodeWithFlags(minus),\n        \";\": KeyCodeWithFlags(semicolon),\n        \"'\": KeyCodeWithFlags(apostrophe),\n        \",\": KeyCodeWithFlags(comma),\n        \".\": KeyCodeWithFlags(period),\n        \"/\": KeyCodeWithFlags(forwardSlash),\n        \"\\\\\": KeyCodeWithFlags(backslash),\n        \"`\": KeyCodeWithFlags(grave),\n        \"[\": KeyCodeWithFlags(leftBracket),\n        \"]\": KeyCodeWithFlags(rightBracket),\n        \"keypaddecimal\": KeyCodeWithFlags(keypadDecimal),\n        \"keypadmultiply\": KeyCodeWithFlags(keypadMultiply),\n        \"keypadplus\": KeyCodeWithFlags(keypadPlus),\n        \"keypadclear\": KeyCodeWithFlags(keypadClear),\n        \"keypaddivide\": KeyCodeWithFlags(keypadDivide),\n        \"keypadenter\": KeyCodeWithFlags(keypadEnter),\n        \"keypadminus\": KeyCodeWithFlags(keypadMinus),\n        \"keypadequals\": KeyCodeWithFlags(keypadEquals),\n        \"keypad0\": KeyCodeWithFlags(keypad0),\n        \"keypad1\": KeyCodeWithFlags(keypad1),\n        \"keypad2\": KeyCodeWithFlags(keypad2),\n        \"keypad3\": KeyCodeWithFlags(keypad3),\n        \"keypad4\": KeyCodeWithFlags(keypad4),\n        \"keypad5\": KeyCodeWithFlags(keypad5),\n        \"keypad6\": KeyCodeWithFlags(keypad6),\n        \"keypad7\": KeyCodeWithFlags(keypad7),\n        \"keypad8\": KeyCodeWithFlags(keypad8),\n        \"keypad9\": KeyCodeWithFlags(keypad9),\n    ]\n\n    private static let modifierKeyDictionary = [\n        \"A\": KeyWithFlags(\"a\", [.maskShift]),\n        \"B\": KeyWithFlags(\"b\", [.maskShift]),\n        \"C\": KeyWithFlags(\"c\", [.maskShift]),\n        \"D\": KeyWithFlags(\"d\", [.maskShift]),\n        \"E\": KeyWithFlags(\"e\", [.maskShift]),\n        \"F\": KeyWithFlags(\"f\", [.maskShift]),\n        \"G\": KeyWithFlags(\"g\", [.maskShift]),\n        \"H\": KeyWithFlags(\"h\", [.maskShift]),\n        \"I\": KeyWithFlags(\"i\", [.maskShift]),\n        \"J\": KeyWithFlags(\"j\", [.maskShift]),\n        \"K\": KeyWithFlags(\"k\", [.maskShift]),\n        \"L\": KeyWithFlags(\"l\", [.maskShift]),\n        \"M\": KeyWithFlags(\"m\", [.maskShift]),\n        \"N\": KeyWithFlags(\"n\", [.maskShift]),\n        \"O\": KeyWithFlags(\"o\", [.maskShift]),\n        \"P\": KeyWithFlags(\"p\", [.maskShift]),\n        \"Q\": KeyWithFlags(\"q\", [.maskShift]),\n        \"R\": KeyWithFlags(\"r\", [.maskShift]),\n        \"S\": KeyWithFlags(\"s\", [.maskShift]),\n        \"T\": KeyWithFlags(\"t\", [.maskShift]),\n        \"U\": KeyWithFlags(\"u\", [.maskShift]),\n        \"V\": KeyWithFlags(\"v\", [.maskShift]),\n        \"W\": KeyWithFlags(\"w\", [.maskShift]),\n        \"X\": KeyWithFlags(\"x\", [.maskShift]),\n        \"Y\": KeyWithFlags(\"y\", [.maskShift]),\n        \"Z\": KeyWithFlags(\"z\", [.maskShift]),\n        \")\": KeyWithFlags(\"0\", [.maskShift]),\n        \"!\": KeyWithFlags(\"1\", [.maskShift]),\n        \"@\": KeyWithFlags(\"2\", [.maskShift]),\n        \"#\": KeyWithFlags(\"3\", [.maskShift]),\n        \"$\": KeyWithFlags(\"4\", [.maskShift]),\n        \"%\": KeyWithFlags(\"5\", [.maskShift]),\n        \"^\": KeyWithFlags(\"6\", [.maskShift]),\n        \"&\": KeyWithFlags(\"7\", [.maskShift]),\n        \"*\": KeyWithFlags(\"8\", [.maskShift]),\n        \"(\": KeyWithFlags(\"9\", [.maskShift]),\n        \"+\": KeyWithFlags(\"=\", [.maskShift]),\n        \"_\": KeyWithFlags(\"-\", [.maskShift]),\n        \":\": KeyWithFlags(\";\", [.maskShift]),\n        \"\\\"\": KeyWithFlags(\"'\", [.maskShift]),\n        \"<\": KeyWithFlags(\",\", [.maskShift]),\n        \">\": KeyWithFlags(\".\", [.maskShift]),\n        \"?\": KeyWithFlags(\"/\", [.maskShift]),\n        \"|\": KeyWithFlags(\"\\\\\", [.maskShift]),\n        \"~\": KeyWithFlags(\"`\", [.maskShift]),\n        \"{\": KeyWithFlags(\"[\", [.maskShift]),\n        \"}\": KeyWithFlags(\"]\", [.maskShift]),\n    ]\n\n    private static var remappingDictionary: [String: String] = [:]\n\n    static func updateMapping(_ newOldMapping: [String: String]) {\n        // Merge the new mapping with the existing one\n        remappingDictionary.merge(newOldMapping) { current, new in new }\n    }\n\n    private static func getRemappedKey(_ key: String) -> String {\n        return remappingDictionary[key] ?? key\n    }\n\n    static func getKeyInfo(_ name: String) -> KeyCodeWithFlags? {\n        let keys = keyDictionary[getRemappedKey(name)]\n\n        if keys != nil {\n            return keys\n        }\n\n        let modifierKeys = modifierKeyDictionary[name]\n\n        if modifierKeys != nil {\n            let key = keyDictionary[getRemappedKey(modifierKeys!.key)]\n\n            if key != nil {\n                return KeyCodeWithFlags(key!.keyCode, modifierKeys!.flags)\n            }\n        }\n\n        return nil\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/KeyMappings.swift",
    "content": "import ArgumentParser\n\nstruct KeyMappings {\n    enum Layouts: String, Codable, ExpressibleByArgument {\n        case qwerty\n        case colemak\n        case dvorak\n    }\n\n    static let Mappings: [Layouts: [String: String]] = [\n        .qwerty: Qwerty,\n        .colemak: Colemak,\n        .dvorak: Dvorak,\n    ]\n\n    static let Qwerty: [String: String] = [:]\n\n    static let Colemak: [String: String] = [\n        \"q\": \"q\",\n        \"w\": \"w\",\n        \"f\": \"e\",\n        \"p\": \"r\",\n        \"g\": \"t\",\n        \"j\": \"y\",\n        \"l\": \"u\",\n        \"u\": \"i\",\n        \"y\": \"o\",\n        \";\": \"p\",\n        \"a\": \"a\",\n        \"r\": \"s\",\n        \"s\": \"d\",\n        \"t\": \"f\",\n        \"d\": \"g\",\n        \"h\": \"h\",\n        \"n\": \"j\",\n        \"e\": \"k\",\n        \"i\": \"l\",\n        \"o\": \";\",\n        \"z\": \"z\",\n        \"x\": \"x\",\n        \"c\": \"c\",\n        \"b\": \"b\",\n        \"k\": \"n\",\n        \"m\": \"m\",\n    ]\n\n    static let Dvorak: [String: String] = [\n        \"[\": \"-\",\n        \"]\": \"=\",\n        \"'\": \"q\",\n        \",\": \"w\",\n        \".\": \"e\",\n        \"p\": \"r\",\n        \"y\": \"t\",\n        \"f\": \"y\",\n        \"g\": \"u\",\n        \"c\": \"i\",\n        \"r\": \"o\",\n        \"l\": \"p\",\n        \"/\": \"[\",\n        \"=\": \"]\",\n        \"a\": \"a\",\n        \"o\": \"s\",\n        \"e\": \"d\",\n        \"u\": \"f\",\n        \"i\": \"g\",\n        \"d\": \"h\",\n        \"h\": \"j\",\n        \"t\": \"k\",\n        \"n\": \"l\",\n        \"s\": \":\",\n        \"-\": \"'\",\n        \";\": \"z\",\n        \"q\": \"x\",\n        \"j\": \"c\",\n        \"k\": \"v\",\n        \"x\": \"b\",\n        \"b\": \"n\",\n        \"m\": \"m\",\n        \"w\": \",\",\n        \"v\": \".\",\n        \"z\": \"/\",\n    ]\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/KeyPresser.swift",
    "content": "import Carbon\nimport Cocoa\nimport Foundation\n\npublic class KeyPresser {\n    private var application: NSRunningApplication?\n\n    init(app: NSRunningApplication?) {\n        self.application = app\n    }\n\n    func keyPress(key: String, modifiers: [String]) throws -> CGEvent? {\n        if let keyDownEvent = try! keyDown(key: key, modifiers: modifiers) {\n            return keyUp(key: key, modifiers: modifiers, event: keyDownEvent)\n        }\n\n        return nil\n    }\n\n    func keyDown(key: String, modifiers: [String]) throws -> CGEvent? {\n        let keyDownEvent = try! createKeyEvent(key: key, modifiers: modifiers, keyDown: true)\n\n        if self.application == nil {\n            keyDownEvent?.post(tap: CGEventTapLocation.cghidEventTap)\n        } else {\n            if #available(OSX 10.11, *) {\n                keyDownEvent?.postToPid(self.application!.processIdentifier)\n            } else {\n                keyDownEvent?.post(tap: CGEventTapLocation.cghidEventTap)\n            }\n        }\n\n        return keyDownEvent\n    }\n\n    func keyUp(key: String, modifiers: [String]) throws -> CGEvent? {\n        let keyUpEvent = try! createKeyEvent(key: key, modifiers: modifiers, keyDown: false)\n\n        if self.application == nil {\n            keyUpEvent?.post(tap: CGEventTapLocation.cghidEventTap)\n        } else {\n            if #available(OSX 10.11, *) {\n                keyUpEvent?.postToPid(self.application!.processIdentifier)\n            } else {\n                keyUpEvent?.post(tap: CGEventTapLocation.cghidEventTap)\n            }\n        }\n\n        return keyUpEvent\n    }\n\n    func keyUp(key: String, modifiers: [String], event: CGEvent) -> CGEvent? {\n        let keyUpEvent = try! createKeyEvent(\n            key: key, modifiers: modifiers, keyDown: false, parentEventSource: CGEventSource(event: event))\n\n        if self.application == nil {\n            keyUpEvent?.post(tap: CGEventTapLocation.cghidEventTap)\n        } else {\n            if #available(OSX 10.11, *) {\n                keyUpEvent?.postToPid(self.application!.processIdentifier)\n            } else {\n                keyUpEvent?.post(tap: CGEventTapLocation.cghidEventTap)\n            }\n        }\n\n        return keyUpEvent\n    }\n\n    private func createKeyEvent(\n        key: String, modifiers: [String], keyDown: Bool, parentEventSource: CGEventSource? = nil\n    ) throws -> CGEvent? {\n        let info = KeyCodes.getKeyInfo(key)\n        let flags = try! KeyPresser.getModifierFlags(modifiers)\n        let mergedFlags = flags.union(CGEventFlags(info?.flags ?? []))\n        let eventSource = parentEventSource ?? CGEventSource(stateID: .hidSystemState)\n        let keyEvent = CGEvent(keyboardEventSource: eventSource, virtualKey: info?.keyCode ?? 0, keyDown: keyDown)\n\n        if info == nil {\n            if key.count == 1 {\n                let utf16Chars = Array(key.utf16)\n                keyEvent!.keyboardSetUnicodeString(stringLength: utf16Chars.count, unicodeString: utf16Chars)\n            } else {\n                throw RuntimeError(\"Unrecognized key: \\(key)\")\n            }\n        }\n\n        if !mergedFlags.isEmpty {\n            keyEvent?.flags = mergedFlags\n        } else {\n            keyEvent?.flags = []\n        }\n\n        return keyEvent\n    }\n\n    static func setKeyboardLayout(_ layout: KeyMappings.Layouts) {\n        KeyCodes.updateMapping(KeyMappings.Mappings[layout]!)\n    }\n\n    static func getModifierFlags(_ modifiers: [String]) throws -> CGEventFlags {\n        var flags: CGEventFlags = []\n\n        for modifier in modifiers.filter({ !$0.isEmpty }) {\n            let flag = try getModifierFlag(modifier)\n            flags.insert(flag)\n        }\n\n        return flags\n    }\n\n    static func getModifierFlag(_ modifier: String) throws -> CGEventFlags {\n        switch modifier {\n        case \"⌘\",\n            \"cmd\",\n            \"command\":\n            return CGEventFlags.maskCommand\n        case \"^\",\n            \"ctrl\",\n            \"control\":\n            return CGEventFlags.maskControl\n        case \"⌥\",\n            \"alt\",\n            \"option\":\n            return CGEventFlags.maskAlternate\n        case \"⇧\",\n            \"shift\":\n            return CGEventFlags.maskShift\n        case \"fn\",\n            \"function\":\n            return CGEventFlags.maskSecondaryFn\n        default:\n            throw RuntimeError(\"Unrecognized modifier: \\(modifier)\")\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/MouseController.swift",
    "content": "import Cocoa\nimport Foundation\n\nclass MouseController {\n    enum ScrollAxis {\n        case horizontal\n        case vertical\n    }\n\n    enum mouseEventType {\n        case up\n        case down\n        case move\n        case drag\n    }\n\n    let animationRefreshInterval: TimeInterval\n    let keyPresser: KeyPresser\n    var downButtons = Set<CGMouseButton>()\n\n    init(animationRefreshInterval: TimeInterval, keyPresser: KeyPresser) {\n        self.animationRefreshInterval = animationRefreshInterval\n        self.keyPresser = keyPresser\n    }\n\n    func move(start: CGPoint?, end: CGPoint, duration: TimeInterval, flags: CGEventFlags) {\n        let resolvedStart = start ?? getLocation()!\n        let eventSource = CGEventSource(event: nil)\n        let button = downButtons.first\n        let moveType = getEventType(.move, button)\n\n        let animator = Animator(\n            duration, animationRefreshInterval,\n            { progress in\n                let location = CGFloat(progress) * (end - resolvedStart) + resolvedStart\n                self.setLocation(location, eventSource: eventSource, moveType: moveType, button: button, flags: flags)\n            })\n\n        animator.animate()\n    }\n\n    func move(\n        start: CGPoint?, path: String, offset: CGPoint, scale: CGPoint, duration: TimeInterval, flags: CGEventFlags\n    ) {\n        let resolvedStart = start ?? getLocation()!\n        let eventSource = CGEventSource(event: nil)\n        let button = downButtons.first\n        let moveType = getEventType(.move, button)\n        let pathData = PathData(path, resolvedStart)\n\n        let animator = Animator(\n            duration, animationRefreshInterval,\n            { progress in\n                let location = offset + (pathData.getPointAtInterval(progress) * scale)\n                self.setLocation(location, eventSource: eventSource, moveType: moveType, button: button, flags: flags)\n            })\n\n        animator.animate()\n    }\n\n    func click(_ location: CGPoint?, button: CGMouseButton, flags: CGEventFlags, clickCount: Int) {\n        let resolvedLocation = location ?? getLocation()!\n        let downMouseType = getEventType(.down, button)\n        let upMouseType = getEventType(.up, button)\n\n        let downEvent = CGEvent(\n            mouseEventSource: nil, mouseType: downMouseType, mouseCursorPosition: resolvedLocation, mouseButton: button)\n        downEvent?.setIntegerValueField(.mouseEventClickState, value: Int64(clickCount))\n        downEvent?.flags = flags\n        downEvent?.post(tap: CGEventTapLocation.cghidEventTap)\n        let eventSource = CGEventSource(event: downEvent)\n\n        let upEvent = CGEvent(\n            mouseEventSource: eventSource, mouseType: upMouseType, mouseCursorPosition: resolvedLocation,\n            mouseButton: button)\n        upEvent?.post(tap: CGEventTapLocation.cghidEventTap)\n    }\n\n    func down(_ location: CGPoint?, button: CGMouseButton, flags: CGEventFlags) {\n        let resolvedLocation = location ?? getLocation()!\n        let downMouseType = getEventType(.down, button)\n\n        let downEvent = CGEvent(\n            mouseEventSource: nil, mouseType: downMouseType, mouseCursorPosition: resolvedLocation, mouseButton: button)\n        downEvent?.flags = flags\n        downEvent?.post(tap: CGEventTapLocation.cghidEventTap)\n\n        downButtons.insert(button)\n    }\n\n    func up(_ location: CGPoint?, button: CGMouseButton, flags: CGEventFlags) {\n        let resolvedLocation = location ?? getLocation()!\n        let upMouseType = getEventType(.up, button)\n\n        let upEvent = CGEvent(\n            mouseEventSource: nil, mouseType: upMouseType, mouseCursorPosition: resolvedLocation,\n            mouseButton: button)\n        upEvent?.post(tap: CGEventTapLocation.cghidEventTap)\n        downButtons.remove(button)\n    }\n\n    func drag(start: CGPoint?, end: CGPoint, duration: TimeInterval, button: CGMouseButton, flags: CGEventFlags) {\n        let resolvedStart = start ?? getLocation()!\n        let downMouseType = getEventType(.down, button)\n        let upMouseType = getEventType(.up, button)\n        let moveType = getEventType(.drag, button)\n        var eventSource: CGEventSource?\n\n        let animator = Animator(\n            duration, animationRefreshInterval,\n            { progress in\n                let location = CGFloat(progress) * (end - resolvedStart) + resolvedStart\n                self.setLocation(location, eventSource: eventSource, moveType: moveType, button: button, flags: flags)\n            })\n\n        if !downButtons.contains(button) {\n            let downEvent = CGEvent(\n                mouseEventSource: nil, mouseType: downMouseType, mouseCursorPosition: resolvedStart, mouseButton: button\n            )\n            downEvent?.flags = flags\n            downEvent?.post(tap: CGEventTapLocation.cghidEventTap)\n            eventSource = CGEventSource(event: downEvent)\n        }\n\n        animator.animate()\n\n        if !downButtons.contains(button) {\n            let upEvent = CGEvent(\n                mouseEventSource: eventSource, mouseType: upMouseType, mouseCursorPosition: end, mouseButton: button)\n            upEvent?.post(tap: CGEventTapLocation.cghidEventTap)\n        }\n    }\n\n    func scroll(_ delta: CGPoint, _ duration: TimeInterval, flags: CGEventFlags) {\n        var scrolledX: Int = 0\n        var scrolledY: Int = 0\n        let eventSource = CGEventSource(event: nil)\n\n        let animator = Animator(\n            duration, animationRefreshInterval,\n            { progress in\n                if delta.x != 0 {\n                    let amount = Int((Double(delta.x) * progress) - Double(scrolledX))\n                    scrolledX += amount\n\n                    self.scrollBy(amount, .horizontal, eventSource: eventSource, flags: flags)\n                }\n                if delta.y != 0 {\n                    let amount = Int((Double(delta.y) * progress) - Double(scrolledY))\n                    scrolledY += amount\n\n                    self.scrollBy(amount, .vertical, eventSource: eventSource, flags: flags)\n                }\n            })\n\n        animator.animate()\n    }\n\n    func circle(_ center: CGPoint, _ radius: CGPoint, _ fromAngle: Double, _ toAngle: Double, _ duration: TimeInterval)\n    {\n        let eventSource = CGEventSource(event: nil)\n        let ANGLE_OFFSET: Double = -90\n        let button = downButtons.first\n        let moveType = getEventType(.move, button)\n\n        let animator = Animator(\n            duration, animationRefreshInterval,\n            { progress in\n                let angle = (toAngle - fromAngle) * progress + fromAngle + ANGLE_OFFSET\n                let location = CGPoint(\n                    x: cos(angle * Double.pi / 180) * Double(radius.x) + Double(center.x),\n                    y: sin(angle * Double.pi / 180) * Double(radius.y) + Double(center.y)\n                )\n                self.setLocation(location, eventSource: eventSource, moveType: moveType, button: .left, flags: [])\n            })\n\n        animator.animate()\n    }\n\n    func scrollBy(_ amount: Int, _ axis: ScrollAxis, eventSource: CGEventSource?, flags: CGEventFlags) {\n        if #available(OSX 10.13, *) {\n            let event = CGEvent(\n                scrollWheelEvent2Source: eventSource, units: .pixel, wheelCount: 1, wheel1: 0, wheel2: 0, wheel3: 0)\n            let field =\n                axis == .vertical\n                ? CGEventField.scrollWheelEventPointDeltaAxis1 : CGEventField.scrollWheelEventPointDeltaAxis2\n\n            event?.setIntegerValueField(field, value: Int64(amount * -1))\n            event?.flags = flags\n\n            event?.post(tap: CGEventTapLocation.cghidEventTap)\n        } else {\n            fatalError(\"Scrolling is only available on 10.13 or later\\n\")\n        }\n    }\n\n    func getLocation() -> CGPoint? {\n        let event = CGEvent(source: nil)\n        return event?.location\n    }\n\n    private func setLocation(\n        _ location: CGPoint, eventSource: CGEventSource?, moveType: CGEventType = CGEventType.mouseMoved,\n        button: CGMouseButton? = nil, flags: CGEventFlags = []\n    ) {\n        let moveEvent = CGEvent(\n            mouseEventSource: eventSource, mouseType: moveType, mouseCursorPosition: location,\n            mouseButton: button ?? CGMouseButton.left)\n        moveEvent?.flags = flags\n        moveEvent?.post(tap: CGEventTapLocation.cghidEventTap)\n    }\n\n    private func getEventType(_ mouseType: mouseEventType, _ button: CGMouseButton? = nil) -> CGEventType {\n        switch mouseType {\n        case .up:\n            if button == CGMouseButton.left {\n                return CGEventType.leftMouseUp\n            } else if button == CGMouseButton.right {\n                return CGEventType.rightMouseUp\n            } else {\n                return CGEventType.otherMouseUp\n            }\n        case .down:\n            if button == CGMouseButton.left {\n                return CGEventType.leftMouseDown\n            } else if button == CGMouseButton.right {\n                return CGEventType.rightMouseDown\n            } else {\n                return CGEventType.otherMouseDown\n            }\n        case .move:\n            if button == nil {\n                return CGEventType.mouseMoved\n            } else if button == CGMouseButton.left {\n                return CGEventType.leftMouseDragged\n            } else if button == CGMouseButton.right {\n                return CGEventType.rightMouseDragged\n            } else {\n                return CGEventType.otherMouseDragged\n            }\n        case .drag:\n            if button == CGMouseButton.left {\n                return CGEventType.leftMouseDragged\n            } else if button == CGMouseButton.right {\n                return CGEventType.rightMouseDragged\n            } else {\n                return CGEventType.otherMouseDragged\n            }\n        }\n    }\n\n    private func resolveLocation(_ location: CGPoint) -> CGPoint {\n        let currentLocation = getLocation()\n        return CGPoint(\n            x: location.x < 0 ? (currentLocation?.x ?? 0) : location.x,\n            y: location.y < 0 ? (currentLocation?.y ?? 0) : location.y\n        )\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/MouseEventProcessor.swift",
    "content": "import Cocoa\nimport Foundation\n\nenum MouseEventType {\n    case click\n    case drag\n}\n\nenum MouseButton: String, CustomStringConvertible {\n    case left\n    case right\n    case center\n    case other\n\n    var description: String {\n        return self.rawValue\n    }\n}\n\nstruct RawMouseEvent {\n    let eventType: CGEventType\n    let button: MouseButton\n    let point: CGPoint\n\n    init(eventType: CGEventType, button: MouseButton, point: CGPoint) {\n        self.eventType = eventType\n        self.button = button\n        self.point = point\n    }\n}\n\nclass MouseEvent: CustomStringConvertible {\n    let eventType: MouseEventType\n    let button: MouseButton\n    let startPoint: CGPoint\n    let endPoint: CGPoint\n    var duration: TimeInterval\n\n    static let numberFormatter = createNumberFormatter()\n\n    init(eventType: MouseEventType, button: MouseButton, startPoint: CGPoint, endPoint: CGPoint, duration: TimeInterval)\n    {\n        self.eventType = eventType\n        self.button = button\n        self.startPoint = startPoint\n        self.endPoint = endPoint\n        self.duration = duration\n    }\n\n    var description: String {\n        switch eventType {\n        case .click:\n            var moveParts: [String] = []\n            var clickParts: [String] = []\n\n            moveParts.append(\n                \"\\(Self.numberFormatter.string(for: endPoint.x)!),\\(Self.numberFormatter.string(for: endPoint.y)!)\")\n\n            if duration > 0 {\n                moveParts.append(Self.numberFormatter.string(for: duration)!)\n            }\n\n            clickParts.append(button.description)\n\n            return \"<m:\\(moveParts.joined(separator: \":\"))><m:\\(clickParts.joined(separator: \":\"))><\\\\>\"\n        case .drag:\n            var parts: [String] = []\n\n            parts.append(\n                \"\\(Self.numberFormatter.string(for: startPoint.x)!),\\(Self.numberFormatter.string(for: startPoint.y)!),\\(Self.numberFormatter.string(for: endPoint.x)!),\\(Self.numberFormatter.string(for: endPoint.y)!)\"\n            )\n\n            if duration > 0 {\n                parts.append(Self.numberFormatter.string(for: duration)!)\n            }\n\n            parts.append(button.description)\n\n            return \"<d:\\(parts.joined(separator: \":\"))><\\\\>\"\n        }\n    }\n\n    static func createNumberFormatter() -> NumberFormatter {\n        let numberFormatter = NumberFormatter()\n\n        numberFormatter.maximumFractionDigits = 2\n\n        return numberFormatter\n    }\n}\n\nclass MouseEventProcessor {\n    var events: [RawMouseEvent] = []\n    var lastDate: Date = Date()\n\n    func start() {\n        lastDate = Date()\n    }\n\n    func consumeEvent(type: CGEventType, event: CGEvent) -> MouseEvent? {\n        let button = getMouseButton(type: type, event: event)\n        let rawEvent = RawMouseEvent(eventType: type, button: button, point: event.location)\n        var mouseEvent: MouseEvent? = nil\n\n        switch type {\n        case .leftMouseUp, .rightMouseUp, .otherMouseUp:\n            switch events.last?.eventType {\n            case .leftMouseDown, .rightMouseDown, .otherMouseDown:\n                mouseEvent = MouseEvent(\n                    eventType: .click, button: button, startPoint: events.first!.point, endPoint: event.location,\n                    duration: -lastDate.timeIntervalSinceNow)\n            case .leftMouseDragged, .rightMouseDragged, .otherMouseDragged:\n                mouseEvent = MouseEvent(\n                    eventType: .drag, button: button, startPoint: events.first!.point, endPoint: event.location,\n                    duration: -lastDate.timeIntervalSinceNow)\n            default:\n                events.append(rawEvent)\n            }\n\n            lastDate = Date()\n            events = []\n        default:\n            events.append(rawEvent)\n        }\n\n        return mouseEvent\n    }\n\n    private func getMouseButton(type: CGEventType, event: CGEvent) -> MouseButton {\n        var button: MouseButton = .other\n\n        switch type {\n        case .leftMouseDown, .leftMouseUp, .leftMouseDragged:\n            button = .left\n        case .rightMouseDown, .rightMouseUp, .rightMouseDragged:\n            button = .right\n        case .otherMouseDown, .otherMouseUp, .otherMouseDragged:\n            let buttonNumber = event.getIntegerValueField(.mouseEventButtonNumber)\n            switch UInt32(buttonNumber) {\n            case CGMouseButton.left.rawValue:\n                button = .left\n            case CGMouseButton.right.rawValue:\n                button = .right\n            case CGMouseButton.center.rawValue:\n                button = .center\n            default:\n                button = .other\n            }\n        default:\n            button = .other\n        }\n\n        return button\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/MousePosition.swift",
    "content": "import ArgumentParser\nimport Cocoa\nimport Foundation\n\nenum OutputMode: String, Codable, ExpressibleByArgument {\n    case coordinates\n    case commands\n}\n\nclass MousePosition: ParsableCommand {\n    public static let configuration = CommandConfiguration(\n        abstract: \"Prints the current mouse position.\"\n    )\n\n    @Flag(\n        name: .shortAndLong, inversion: FlagInversion.prefixedNo,\n        help: \"Watch and display the mouse positions as the mouse is clicked.\")\n    var watch: Bool?\n\n    @Option(\n        name: NameSpecification([.customShort(\"o\"), .customLong(\"output\", withSingleDash: false)]),\n        help: \"Displays results as either a series of coordinates or commands.\")\n    var mode: OutputMode?\n\n    @Option(\n        name: .shortAndLong,\n        help:\n            \"Duration (in seconds) to output for mouse events. A negative value uses elapsed time between mouse events.\"\n    )\n    var duration: Double?\n\n    var config: MousePositionConfig\n\n    static let eventProcessor = MouseEventProcessor()\n\n    private static func createNumberFormatter() -> NumberFormatter {\n        let numberFormatter = NumberFormatter()\n\n        numberFormatter.maximumFractionDigits = 2\n\n        return numberFormatter\n    }\n\n    required init() {\n        self.config = MousePositionConfig(watch: false, output: .commands, duration: -1)\n    }\n\n    func run() {\n        self.config = self.config\n            .merge(with: ConfigLoader.loadConfig().mousePosition)\n            .merge(with: MousePositionConfig(watch: watch, output: mode, duration: duration))\n\n        if self.config.watch! {\n            watchMouseInput()\n        } else {\n            printMousePosition(nil)\n        }\n    }\n\n    func printMousePosition(_ position: CGPoint?) {\n        let numberFormatter = Self.createNumberFormatter()\n        let location =\n            position ?? MouseController(animationRefreshInterval: 0.01, keyPresser: KeyPresser(app: nil)).getLocation()!\n\n        printAndFlush(\"\\(numberFormatter.string(for: location.x)!),\\(numberFormatter.string(for: location.y)!)\")\n    }\n\n    func listenForInput() {\n        fputs(\n            \"Waiting for user input... Escape or ctrl + d to stop, or any other key to capture mouse position.\\n\",\n            stderr)\n\n        waitForCharInput { _ in\n            printMousePosition(nil)\n        }\n    }\n\n    func waitForCharInput(callback: (_ char: UInt8) -> Void) {\n        let stdIn = FileHandle.standardInput\n        let originalTerm = enableRawMode(fileHandle: stdIn)\n\n        var char: UInt8 = 0\n        while read(stdIn.fileDescriptor, &char, 1) == 1 {\n            if char == 4 /* EOF (Ctrl+D) */ || char == 27 /* escape */ {\n                break\n            }\n\n            callback(char)\n        }\n\n        restoreRawMode(fileHandle: stdIn, originalTerm: originalTerm)\n    }\n\n    func watchMouseInput() {\n        fputs(\"Waiting for mouse input... ctrl + c to stop.\\n\", stderr)\n\n        MousePosition.eventProcessor.start()\n\n        var eventMask =\n            (1 << CGEventType.leftMouseDown.rawValue)\n            | (1 << CGEventType.leftMouseUp.rawValue)\n            | (1 << CGEventType.leftMouseDragged.rawValue)\n        eventMask =\n            eventMask | (1 << CGEventType.rightMouseDown.rawValue)\n            | (1 << CGEventType.rightMouseUp.rawValue)\n            | (1 << CGEventType.rightMouseDragged.rawValue)\n        eventMask =\n            eventMask | (1 << CGEventType.otherMouseDown.rawValue)\n            | (1 << CGEventType.otherMouseUp.rawValue)\n            | (1 << CGEventType.otherMouseDragged.rawValue)\n\n        let info = UnsafeMutableRawPointer(mutating: bridge(obj: self))\n\n        guard\n            let eventTap = CGEvent.tapCreate(\n                tap: .cghidEventTap, place: .tailAppendEventTap, options: .defaultTap,\n                eventsOfInterest: CGEventMask(eventMask),\n                callback: {\n                    (proxy: CGEventTapProxy, eventType: CGEventType, event: CGEvent, refcon: UnsafeMutableRawPointer?)\n                        -> Unmanaged<CGEvent>? in\n                    let command: MousePosition = bridge(ptr: UnsafeRawPointer(refcon)!)\n\n                    if let mouseEvent = MousePosition.eventProcessor.consumeEvent(type: eventType, event: event) {\n                        // if duration is set, override all mouse event durations\n                        if command.config.duration! >= 0 {\n                            mouseEvent.duration = command.config.duration!\n                        }\n\n                        switch command.config.output! {\n                        case .coordinates:\n                            if mouseEvent.eventType == .click {\n                                command.printMousePosition(mouseEvent.endPoint)\n                            }\n                        case .commands:\n                            command.printAndFlush(mouseEvent.description)\n                        }\n                    }\n\n                    return Unmanaged.passRetained(event)\n                }, userInfo: info)\n        else {\n            MousePosition.exit(withError: RuntimeError(\"Failed to create event tap.\"))\n        }\n\n        let runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0)\n        CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, .commonModes)\n        CGEvent.tapEnable(tap: eventTap, enable: true)\n        CFRunLoopRun()\n    }\n\n    func printAndFlush(_ message: String) {\n        print(message)\n        fflush(stdout)\n    }\n\n    func eventCallback(proxy: CGEventTapProxy, type: CGEventType, event: CGEvent, refcon: UnsafeMutableRawPointer?)\n        -> Unmanaged<CGEvent>?\n    {\n        switch self.config.output! {\n        case .coordinates:\n            printMousePosition(nil)\n        case .commands:\n            printMousePosition(nil)\n        }\n        printAndFlush(\"Event \\(type) \\(type.rawValue)\")\n\n        return Unmanaged.passRetained(event)\n    }\n\n    // see https://stackoverflow.com/a/24335355/669586\n    func initStruct<S>() -> S {\n        let struct_pointer = UnsafeMutablePointer<S>.allocate(capacity: 1)\n        let struct_memory = struct_pointer.pointee\n        struct_pointer.deallocate()\n        return struct_memory\n    }\n\n    func enableRawMode(fileHandle: FileHandle) -> termios {\n        var raw: termios = initStruct()\n        tcgetattr(fileHandle.fileDescriptor, &raw)\n\n        let original = raw\n\n        raw.c_lflag &= ~(UInt(ECHO | ICANON))\n        tcsetattr(fileHandle.fileDescriptor, TCSAFLUSH, &raw)\n\n        return original\n    }\n\n    func restoreRawMode(fileHandle: FileHandle, originalTerm: termios) {\n        var term = originalTerm\n        tcsetattr(fileHandle.fileDescriptor, TCSAFLUSH, &term)\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Path/Extensions.swift",
    "content": "import AppKit\nimport Foundation\n\nextension CGPoint {\n    // Vector math\n    public static func + (left: CGPoint, right: CGPoint) -> CGPoint {\n        return CGPoint(x: left.x + right.x, y: left.y + right.y)\n    }\n\n    public static func - (left: CGPoint, right: CGPoint) -> CGPoint {\n        return CGPoint(x: left.x - right.x, y: left.y - right.y)\n    }\n\n    public static func * (left: CGFloat, right: CGPoint) -> CGPoint {\n        return CGPoint(x: left * right.x, y: left * right.y)\n    }\n\n    public static func * (left: CGPoint, right: CGPoint) -> CGPoint {\n        return CGPoint(x: left.x * right.x, y: left.y * right.y)\n    }\n\n    public func distance(from: CGPoint) -> Double {\n        let delta = self - from\n        let sqr = delta * delta\n        return sqrt(Double(sqr.x + sqr.y))\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Path/PathCommands.swift",
    "content": "import Foundation\n\npublic class PointValue: CustomStringConvertible {\n    var point: CGPoint\n\n    init(_ point: CGPoint) {\n        self.point = point\n    }\n\n    public var description: String {\n        return \"\\(point.x) \\(point.y)\"\n    }\n}\n\npublic class ControlPointValue: PointValue {\n    var controlPoint1: CGPoint?\n\n    init(_ controlPoint: CGPoint?, _ point: CGPoint) {\n        super.init(point)\n        self.controlPoint1 = controlPoint\n    }\n\n    override public var description: String {\n        return \"\\(controlPoint1 != nil ? \"\\(controlPoint1!.x) \\(controlPoint1!.y)\" : \"\") \\(point.x) \\(point.y)\"\n            .trimmingCharacters(in: [\" \"])\n    }\n}\n\npublic class ControlPointsValue: ControlPointValue {\n    var controlPoint2: CGPoint\n\n    init(_ controlPoint1: CGPoint?, _ controlPoint2: CGPoint, _ point: CGPoint) {\n        self.controlPoint2 = controlPoint2\n        super.init(controlPoint1, point)\n    }\n\n    override public var description: String {\n        return\n            \"\\(controlPoint1 != nil ? \"\\(controlPoint1!.x) \\(controlPoint1!.y)\" : \"\") \\(controlPoint2.x) \\(controlPoint2.y) \\(point.x) \\(point.y)\"\n            .trimmingCharacters(in: [\" \"])\n    }\n}\n\npublic class ArchCommandValue: PointValue {\n    var radius: CGPoint\n    var angle: Double\n    var largeArc: Bool\n    var sweep: Bool\n\n    init(_ radius: CGPoint, _ angle: Double, _ largeArc: Bool, _ sweep: Bool, _ point: CGPoint) {\n        self.radius = radius\n        self.angle = angle\n        self.largeArc = largeArc\n        self.sweep = sweep\n        super.init(point)\n    }\n\n    override public var description: String {\n        return \"\\(radius.x) \\(radius.y) \\(angle) \\(largeArc ? \"1\" : \"0\") \\(sweep ? \"1\" : \"0\") \\(point.x) \\(point.y)\"\n    }\n}\n\npublic class PathCommandBase: Equatable, CustomStringConvertible {\n    var type: Character\n\n    init(_ type: Character) {\n        self.type = type\n    }\n\n    func decompose(from: CGPoint) -> [PathCommandBase] {\n        return [self]\n    }\n\n    public func equals(_ comparison: PathCommandBase) -> Bool {\n        return self.description == comparison.description\n    }\n\n    public static func == (lhs: PathCommandBase, rhs: PathCommandBase) -> Bool {\n        return lhs.equals(rhs) && rhs.equals(lhs)\n    }\n\n    public var description: String {\n        return \"\\(type)\"\n    }\n\n    public var currentPoint: CGPoint? {\n        return nil\n    }\n\n    public var isRelative: Bool {\n        return type.isLowercase\n    }\n\n    public func makeAbsolute(_ point: CGPoint) {\n        if isRelative {\n            type = Character(type.uppercased())\n        }\n    }\n\n    public func distanceFrom(point: CGPoint) -> Double {\n        return 0\n    }\n\n    public func pointAlongPath(interval: Double, from: CGPoint) -> CGPoint {\n        if currentPoint == nil {\n            return from\n        }\n        return from + CGFloat(interval) * (currentPoint! - from)\n    }\n}\n\npublic class PathCommand<T>: PathCommandBase {\n    var value: T\n\n    init(_ type: Character, _ value: T) {\n        self.value = value\n        super.init(type)\n    }\n\n    override public var description: String {\n        return \"\\(type) \\(value)\"\n    }\n}\n\npublic class NumericPathCommand: PathCommand<Double> {\n    override func decompose(from: CGPoint) -> [PathCommandBase] {\n        if type == \"H\" {\n            return [PointPathCommand(\"L\", PointValue(CGPoint(x: CGFloat(value), y: from.y)))]\n        }\n        if type == \"V\" {\n            return [PointPathCommand(\"L\", PointValue(CGPoint(x: from.x, y: CGFloat(value))))]\n        }\n        return []\n    }\n\n    override public func makeAbsolute(_ point: CGPoint) {\n        if isRelative {\n            super.makeAbsolute(point)\n\n            if type == \"H\" {\n                value = Double(point.x) + value\n            }\n            if type == \"V\" {\n                value = Double(point.y) + value\n            }\n        }\n    }\n\n    override public func distanceFrom(point: CGPoint) -> Double {\n        fatalError(\"Not implemented\")\n    }\n}\n\npublic class PointPathCommand: PathCommand<PointValue> {\n    override public func makeAbsolute(_ point: CGPoint) {\n        if isRelative {\n            super.makeAbsolute(point)\n\n            value.point = point + value.point\n        }\n    }\n\n    override public func distanceFrom(point: CGPoint) -> Double {\n        if type == \"L\" {\n            return value.point.distance(from: point)\n        } else if type == \"M\" {\n            return 0\n        }\n        fatalError(\"Not implemented\")\n    }\n\n    override public var currentPoint: CGPoint? {\n        return value.point\n    }\n}\n\npublic class ArcPathCommand: PathCommand<ArchCommandValue> {\n    override func decompose(from: CGPoint) -> [PathCommandBase] {\n        if from == value.point {\n            return []\n        }\n\n        if value.radius.x == 0 || value.radius.y == 0 {\n            return [PointPathCommand(\"L\", PointValue(value.point))]\n        }\n\n        let midpointDistance = CGFloat(0.5) * (from - value.point)\n        var matrix = AffineTransform()\n        matrix.rotate(byDegrees: CGFloat(value.angle))\n        let tranformedMidpoint = matrix.transform(midpointDistance)\n        var rx = value.radius.x\n        var ry = value.radius.y\n        let squareRx = rx * rx\n        let squareRy = ry * ry\n        let squareX = tranformedMidpoint.x * tranformedMidpoint.x\n        let squareY = tranformedMidpoint.y * tranformedMidpoint.y\n\n        let radiiScale = squareX / squareRx + squareY / squareRy\n        if radiiScale > 1 {\n            rx *= sqrt(radiiScale)\n            ry *= sqrt(radiiScale)\n        }\n\n        matrix = AffineTransform()\n        matrix.scale(x: 1 / rx, y: 1 / ry)\n        matrix.rotate(byDegrees: CGFloat(-value.angle))\n        var point1 = matrix.transform(from)\n        var point2 = matrix.transform(value.point)\n        var delta = point2 - point1\n        let d = delta.x * delta.x + delta.y * delta.y\n\n        var scaleFactor = sqrt(max(1 / d - 0.25, 0))\n        if value.sweep == value.largeArc {\n            scaleFactor = -scaleFactor\n        }\n\n        delta = scaleFactor * delta\n        let center = 0.5 * (point1 + point2) + CGPoint(x: -delta.y, y: delta.x)\n\n        let theta1 = Double(atan2(point1.y - center.y, point1.x - center.x))\n        let theta2 = Double(atan2(point2.y - center.y, point2.x - center.x))\n        var thetaArc = Double(theta2 - theta1)\n        if thetaArc < 0 && value.sweep {\n            thetaArc += Double.pi * 2.0\n        } else if thetaArc > 0 && !value.sweep {\n            thetaArc -= Double.pi * 2.0\n        }\n\n        matrix = AffineTransform()\n        matrix.rotate(byDegrees: CGFloat(value.angle))\n        matrix.scale(x: rx, y: ry)\n        let segments = Int(ceil(fabs(thetaArc / Double.pi / 2)))\n\n        var commands: [PathCommandBase] = []\n\n        for i in 0...segments - 1 {\n            let startTheta = theta1 + (Double(i) * thetaArc) / Double(segments)\n            let endTheta = theta1 + ((Double(i) + 1.0) * thetaArc) / Double(segments)\n            let t = CGFloat((8.0 / 6.0) * tan(0.25 * (endTheta - startTheta)))\n            // if (!std::isfinite(t))\n            //     return false;\n            let sinStartTheta = CGFloat(sin(startTheta))\n            let cosStartTheta = CGFloat(cos(startTheta))\n            let sinEndTheta = CGFloat(sin(endTheta))\n            let cosEndTheta = CGFloat(cos(endTheta))\n            point1 = CGPoint(\n                x: cosStartTheta - t * sinStartTheta + center.x,\n                y: sinStartTheta + t * cosStartTheta + center.y\n            )\n            var targetPoint = CGPoint(\n                x: cosEndTheta + center.x,\n                y: sinEndTheta + center.y\n            )\n            point2 = CGPoint(x: targetPoint.x + t * sinEndTheta, y: targetPoint.y - t * cosEndTheta)\n\n            point1 = matrix.transform(point1)\n            point2 = matrix.transform(point2)\n            targetPoint = matrix.transform(targetPoint)\n\n            commands.append(CubicBezierPathCommand(\"C\", ControlPointsValue(point1, point2, targetPoint)))\n        }\n\n        return commands\n    }\n\n    override public var currentPoint: CGPoint? {\n        return value.point\n    }\n\n    override public func makeAbsolute(_ point: CGPoint) {\n        if isRelative {\n            super.makeAbsolute(point)\n\n            value.point = point + value.point\n        }\n    }\n\n    override public func distanceFrom(point: CGPoint) -> Double {\n        fatalError(\"Not implemented\")\n    }\n}\n\npublic class QuadraticBezierPathCommand: PathCommand<ControlPointValue> {\n    override func decompose(from: CGPoint) -> [PathCommandBase] {\n        let controlPoint = value.controlPoint1 ?? from\n        return [\n            CubicBezierPathCommand(\n                \"C\",\n                ControlPointsValue(\n                    from + (2.0 / 3.0) * (controlPoint - from),\n                    value.point + (2.0 / 3.0) * (controlPoint - value.point),\n                    value.point\n                ))\n        ]\n    }\n\n    override public var currentPoint: CGPoint? {\n        return value.point\n    }\n\n    override public func makeAbsolute(_ point: CGPoint) {\n        if isRelative {\n            super.makeAbsolute(point)\n\n            if value.controlPoint1 != nil {\n                value.controlPoint1 = point + value.controlPoint1!\n            }\n            value.point = point + value.point\n        }\n    }\n\n    override public func distanceFrom(point: CGPoint) -> Double {\n        fatalError(\"Not implemented\")\n    }\n}\n\npublic class CubicBezierPathCommand: PathCommand<ControlPointsValue> {\n    override public var currentPoint: CGPoint? {\n        return value.point\n    }\n\n    override public func makeAbsolute(_ point: CGPoint) {\n        if isRelative {\n            super.makeAbsolute(point)\n\n            if value.controlPoint1 != nil {\n                value.controlPoint1 = point + value.controlPoint1!\n            }\n            value.controlPoint2 = point + value.controlPoint2\n            value.point = point + value.point\n        }\n    }\n\n    private func lengthValueAt(t: CGFloat, p0: CGFloat, c1: CGFloat, c2: CGFloat, p1: CGFloat) -> CGFloat {\n        var value: CGFloat = 0.0\n\n        // (1-t)^3 * p0 + 3 * (1-t)^2 * t * c1 + 3 * (1-t) * t^2 * c2 + t^3 * p1\n        value += pow(1 - t, 3) * p0\n        value += 3 * pow(1 - t, 2) * t * c1\n        value += 3 * (1 - t) * pow(t, 2) * c2\n        value += pow(t, 3) * p1\n\n        return value\n    }\n\n    private func poinAt(t: CGFloat, start: CGPoint) -> CGPoint {\n        let x = lengthValueAt(\n            t: t, p0: start.x, c1: value.controlPoint1?.x ?? start.x, c2: value.controlPoint2.x, p1: value.point.x)\n        let y = lengthValueAt(\n            t: t, p0: start.y, c1: value.controlPoint1?.y ?? start.y, c2: value.controlPoint2.y, p1: value.point.y)\n\n        return CGPoint(x: x, y: y)\n    }\n\n    override public func distanceFrom(point: CGPoint) -> Double {\n        var total = 0.0\n        var previousPoint = point\n        let segments = 1_000\n\n        for i in 1...segments {\n            let intervalPoint = poinAt(t: CGFloat(i) / CGFloat(segments), start: point)\n            total += intervalPoint.distance(from: previousPoint)  // Command.distanceBetweenPoints(previousPoint, intervalPoint);\n\n            previousPoint = intervalPoint\n        }\n\n        return Double(total)\n    }\n\n    override public func pointAlongPath(interval: Double, from: CGPoint) -> CGPoint {\n        return poinAt(t: CGFloat(interval), start: from)\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Path/PathData.swift",
    "content": "import Foundation\n\nstruct SegmentInfo {\n    var startLength: Double\n    var length: Double\n    var startPoint: CGPoint\n    var command: PathCommandBase\n\n    init(_ startLength: Double, _ length: Double, _ startPoint: CGPoint, _ command: PathCommandBase) {\n        self.startLength = startLength\n        self.length = length\n        self.startPoint = startPoint\n        self.command = command\n    }\n}\n\npublic class PathData {\n    public let commands: [PathCommandBase]\n    private let segments: [SegmentInfo]\n\n    convenience init(_ data: String) {\n        self.init(data, CGPoint.zero)\n    }\n\n    init(_ data: String, _ startPoint: CGPoint) {\n        commands = PathData.normalize(PathParser(data).parse(), startPoint)\n        segments = PathData.getSegments(commands, startPoint)\n    }\n\n    private static func normalize(_ commands: [PathCommandBase], _ startPoint: CGPoint) -> [PathCommandBase] {\n        var currentPoint = startPoint\n        var pathStart = currentPoint\n        var previousCubic: CubicBezierPathCommand?\n        var previousQuadratic: QuadraticBezierPathCommand?\n\n        return commands.flatMap { command -> [PathCommandBase] in\n            command.makeAbsolute(currentPoint)\n\n            if command.type == \"Z\" {\n                return [PointPathCommand(\"L\", PointValue(pathStart))]\n            }\n            if command.type == \"M\", let pointCommand = command as? PointPathCommand {\n                pathStart = pointCommand.value.point\n            }\n            if let cubicCommand = command as? CubicBezierPathCommand {\n                if command.type == \"S\" {\n                    if previousCubic != nil {\n                        // reflect\n                        cubicCommand.value.controlPoint1 = 2 * currentPoint - previousCubic!.value.controlPoint2\n                    } else {\n                        cubicCommand.value.controlPoint1 = currentPoint\n                    }\n                }\n                previousCubic = cubicCommand\n            }\n            if let quadtraticCommand = command as? QuadraticBezierPathCommand {\n                if command.type == \"T\" {\n                    if previousQuadratic != nil {\n                        // reflect\n                        quadtraticCommand.value.controlPoint1 =\n                            2 * currentPoint - (previousQuadratic!.value.controlPoint1 ?? CGPoint.zero)\n                    } else {\n                        quadtraticCommand.value.controlPoint1 = currentPoint\n                    }\n                }\n                previousQuadratic = quadtraticCommand\n            }\n\n            let newCommands: [PathCommandBase] = command.decompose(from: currentPoint)\n\n            if command.currentPoint != nil {\n                currentPoint = command.currentPoint!\n            } else if newCommands.last?.currentPoint != nil {\n                currentPoint = (newCommands.last?.currentPoint)!\n            }\n\n            return newCommands\n        }\n    }\n\n    static func getSegments(_ commands: [PathCommandBase], _ startPoint: CGPoint) -> [SegmentInfo] {\n        var total = 0.0\n        var previousPoint = startPoint\n\n        var segments: [SegmentInfo] = []\n\n        for command in commands {\n            let length = command.distanceFrom(point: previousPoint)\n\n            if length >= 0 {\n                segments.append(SegmentInfo(total, length, previousPoint, command))\n            }\n\n            if command.currentPoint != nil {\n                previousPoint = command.currentPoint!\n            }\n\n            total += length\n        }\n\n        return segments\n    }\n\n    func getTotalDistance() -> Double {\n        let last = segments.last\n\n        if last == nil {\n            return 0.0\n        }\n\n        return last!.startLength + last!.length\n    }\n\n    func getPointAtInterval(_ interval: Double) -> CGPoint {\n        let targetLength = getTotalDistance() * max(min(interval, 1), 0)\n        let index = segments.firstIndex { segment in\n            return segment.startLength > targetLength\n        }\n\n        var segment: SegmentInfo?\n        if index ?? 0 <= 0 {\n            segment = segments.last\n        } else {\n            segment = segments[index! - 1]\n        }\n\n        if segment == nil {\n            return CGPoint.zero\n        }\n\n        return segment!.command.pointAlongPath(\n            interval: (targetLength - segment!.startLength) / segment!.length, from: segment!.startPoint)\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Path/PathParser.swift",
    "content": "import Foundation\n\npublic class PathParser {\n    private var index: Int\n    private let data: [Character]\n    private var previousType: Character?\n\n    init(_ data: String) {\n        self.index = 0\n        self.data = Array(data)\n    }\n\n    public func parse() -> [PathCommandBase] {\n        readIgnored()\n        var commands: [PathCommandBase] = []\n\n        while case let current = readCommand(false), current != nil {\n            previousType = current!.type\n            commands.append(current!)\n        }\n\n        return commands\n    }\n\n    private func readCommand(_ usePrevious: Bool) -> PathCommandBase? {\n        let type = usePrevious ? previousType : index < data.count ? data[index] : nil\n        var command: PathCommandBase\n\n        if type == nil {\n            return nil\n        }\n\n        if !usePrevious {\n            index += 1\n        }\n\n        switch type {\n        case \"a\", \"A\":\n            let radius = readPoint()\n            let rotation = readDouble()\n            let largeArc = readInt() != 0\n            let sweep = readInt() != 0\n            let point = readPoint()\n\n            command = ArcPathCommand(type!, ArchCommandValue(radius, rotation, largeArc, sweep, point))\n        case \"c\", \"C\":\n            let point1 = readPoint()\n            let point2 = readPoint()\n            let point = readPoint()\n\n            command = CubicBezierPathCommand(type!, ControlPointsValue(point1, point2, point))\n        case \"h\", \"H\", \"v\", \"V\":\n            command = NumericPathCommand(type!, readDouble())\n        case \"l\", \"L\", \"m\", \"M\":\n            command = PointPathCommand(type!, PointValue(readPoint()))\n        case \"q\", \"Q\":\n            let point1 = readPoint()\n            let point = readPoint()\n\n            command = QuadraticBezierPathCommand(type!, ControlPointValue(point1, point))\n        case \"s\", \"S\":\n            let point2 = readPoint()\n            let point = readPoint()\n\n            command = CubicBezierPathCommand(type!, ControlPointsValue(nil, point2, point))\n        case \"t\", \"T\":\n            let point = readPoint()\n\n            command = QuadraticBezierPathCommand(type!, ControlPointValue(nil, point))\n        case \"z\", \"Z\":\n            command = PathCommandBase(type!)\n        default:\n            index -= 1\n            if previousType != nil {\n                return readCommand(true)\n            } else {\n                return nil\n            }\n        }\n\n        readIgnored()\n\n        return command\n    }\n\n    private func readIgnored() {\n        var current: Character\n        while index < data.count {\n            current = data[index]\n            if \" \\r\\n\\t,\".contains(current) {\n                index += 1\n            } else {\n                break\n            }\n        }\n    }\n\n    private func readDouble() -> Double {\n        var current: Character\n\n        readIgnored()\n\n        let start = index\n        while index < data.count {\n            current = data[index]\n            if \"+-0123456789eE.\".contains(current) {\n                index += 1\n            } else {\n                break\n            }\n        }\n\n        let end = index\n\n        readIgnored()\n\n        if start == index {\n            fatalError(\"Unable to read Double\")\n        }\n\n        return Double(String(data[start..<end]))!\n    }\n\n    private func readInt() -> Int {\n        var current: Character\n\n        readIgnored()\n\n        let start = index\n        while index < data.count {\n            current = data[index]\n            if \"+-0123456789eE\".contains(current) {\n                index += 1\n            } else {\n                break\n            }\n        }\n\n        let end = index\n\n        readIgnored()\n\n        if start == index {\n            fatalError(\"Unable to read Int\")\n        }\n\n        return Int(String(data[start..<end]))!\n    }\n\n    private func readPoint() -> CGPoint {\n        return CGPoint(x: readDouble(), y: readDouble())\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/RuntimeError.swift",
    "content": "struct RuntimeError: Error {\n    let message: String\n\n    init(_ message: String) {\n        self.message = message\n    }\n\n    public var localizedDescription: String {\n        return message\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/SendKeysCli.swift",
    "content": "import ArgumentParser\nimport Foundation\n\n@available(OSX 10.11, *)\npublic struct SendKeysCli: ParsableCommand {\n    public static let configuration = CommandConfiguration(\n        commandName: \"sendkeys\",\n        abstract:\n            \"Command line tool for automating keystrokes and mouse events. More information: https://github.com/socsieng/sendkeys/blob/main/README.md\",\n        version: \"0.0.0\", /* auto-updated */\n        subcommands: [Sender.self, AppLister.self, MousePosition.self, Transformer.self],\n        defaultSubcommand: Sender.self\n    )\n\n    public init() {}\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Sender.swift",
    "content": "import ArgumentParser\nimport Cocoa\nimport Foundation\n\n@available(OSX 10.11, *)\npublic struct Sender: ParsableCommand {\n    public static let configuration = CommandConfiguration(\n        commandName: \"send\",\n        abstract: \"Sends keystroke and mouse event commands.\"\n    )\n\n    @Option(name: .shortAndLong, help: \"Name of a running application to send keys to.\")\n    var applicationName: String?\n\n    @Option(\n        name: NameSpecification([.short, .customLong(\"pid\")]),\n        help: \"Process id of a running application to send keys to.\")\n    var processId: Int?\n\n    @Flag(\n        name: .long, inversion: FlagInversion.prefixedNo,\n        help: \"Activate the specified app or process before sending commands.\")\n    var activate: Bool?\n\n    @Flag(\n        name: .long, inversion: FlagInversion.prefixedNo, help: \"Only send keystrokes to the targeted app or process.\")\n    var targeted: Bool?\n\n    @Option(name: .shortAndLong, help: \"Default delay between keystrokes in seconds.\")\n    var delay: Double?\n\n    @Option(name: .shortAndLong, help: \"Initial delay before sending commands in seconds.\")\n    var initialDelay: Double?\n\n    @Option(name: NameSpecification([.customShort(\"f\"), .long]), help: \"File containing keystroke instructions.\")\n    var inputFile: String?\n\n    @Option(name: .shortAndLong, help: \"String of characters to send.\")\n    var characters: String?\n\n    @Option(help: \"Number of seconds between animation updates.\")\n    var animationInterval: Double?\n\n    @Option(name: .shortAndLong, help: \"Character sequence to use to terminate execution (e.g. f12:command).\")\n    var terminateCommand: String?\n\n    @Option(\n        name: NameSpecification([.customLong(\"config\")]),\n        help: \"Configuration file to load settings from (yaml format).\")\n    var configurationFile: String?\n\n    @Option(name: .long, help: \"Keyboard layout to use for sending keystrokes.\")\n    var keyboardLayout: KeyMappings.Layouts?\n\n    var config: SendConfig\n\n    public init() {\n        self.config = SendConfig(\n            activate: true, animationInterval: 0.01, delay: 0.1, initialDelay: 1,\n            targeted: false, terminateCommand: nil)\n    }\n\n    public mutating func run() throws {\n        let accessEnabled = AXIsProcessTrustedWithOptions(\n            [kAXTrustedCheckOptionPrompt.takeUnretainedValue() as String: true] as CFDictionary)\n\n        if !accessEnabled {\n            fputs(\n                \"WARNING: Accessibility preferences must be enabled to use this tool. If running from the terminal, make sure that your terminal app has accessibility permissiions enabled.\\n\\n\",\n                stderr)\n        }\n\n        let activator = AppActivator(appName: applicationName, processId: processId)\n        let app: NSRunningApplication? = try activator.find()\n        let keyPresser: KeyPresser\n\n        // load from home directory by default\n        self.config = self.config.merge(with: ConfigLoader.loadConfig().send)\n\n        if !(configurationFile ?? \"\").isEmpty {\n            self.config = self.config.merge(with: ConfigLoader.loadConfig(configurationFile!).send)\n        }\n\n        self.config = self.config.merge(\n            with: SendConfig(\n                activate: activate, animationInterval: animationInterval, delay: delay, initialDelay: initialDelay,\n                keyboardLayout: keyboardLayout, targeted: targeted, terminateCommand: terminateCommand))\n\n        let activate = activate ?? self.config.activate!\n        let targeted = targeted ?? self.config.targeted!\n        let delay = delay ?? self.config.delay!\n        let initialDelay = initialDelay ?? self.config.initialDelay!\n        let animationInterval = animationInterval ?? self.config.animationInterval!\n        let terminateCommand = terminateCommand ?? self.config.terminateCommand\n        let keyboardLayout = keyboardLayout ?? self.config.keyboardLayout\n\n        if keyboardLayout != nil {\n            KeyPresser.setKeyboardLayout(keyboardLayout!)\n        }\n\n        if self.config.remap != nil {\n            KeyCodes.updateMapping(self.config.remap!)\n        }\n\n        if targeted {\n            if app == nil {\n                throw RuntimeError(\"Application could not be found.\")\n            }\n            keyPresser = KeyPresser(app: app)\n        } else {\n            keyPresser = KeyPresser(app: nil)\n        }\n\n        let mouseController = MouseController(animationRefreshInterval: animationInterval, keyPresser: keyPresser)\n        let commandProcessor = CommandsProcessor(\n            defaultPause: delay, keyPresser: keyPresser, mouseController: mouseController)\n        var commandString: String?\n\n        if !(inputFile ?? \"\").isEmpty {\n            if let data = FileManager.default.contents(atPath: inputFile!) {\n                commandString = String(data: data, encoding: .utf8)\n            } else {\n                fatalError(\"Could not read file \\(inputFile!)\\n\")\n            }\n        } else if !(characters ?? \"\").isEmpty {\n            commandString = characters\n        }\n\n        var listener: TerminationListener?\n        if terminateCommand != nil && !terminateCommand!.isEmpty {\n            listener = TerminationListener(sequence: terminateCommand!) {\n                Sender.exit()\n            }\n            listener!.listen()\n        }\n\n        if activate {\n            try activator.activate()\n        }\n\n        if initialDelay > 0 {\n            Sleeper.sleep(seconds: initialDelay)\n        }\n\n        if !(commandString ?? \"\").isEmpty {\n            commandProcessor.process(commandString!)\n            Sleeper.sleep(seconds: 0.01)\n        } else if !isTty() {\n            var data: Data\n\n            repeat {\n                data = FileHandle.standardInput.availableData\n\n                if data.count > 0 {\n                    commandString = String(data: data, encoding: .utf8)\n                    commandProcessor.process(commandString!)\n                }\n            } while data.count > 0\n        } else {\n            print(SendKeysCli.helpMessage(for: Self.self))\n        }\n\n        if listener != nil {\n            listener!.stop()\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Sleeper.swift",
    "content": "import Foundation\n\nstruct Sleeper {\n    static func sleep(seconds: Double) {\n        usleep(useconds_t(seconds * 1_000_000))\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/TerminationListener.swift",
    "content": "import Cocoa\nimport Foundation\n\nclass TerminationListener {\n    private var keycode: UInt16\n    private var modifiers: [CGEventFlags]\n    private var callback: () -> Void\n    private let flags: [CGEventFlags] = [.maskCommand, .maskControl, .maskShift, .maskAlternate]\n    private var runLoopSource: CFRunLoopSource?\n    private var runLoop: CFRunLoop?\n    private let expression = try! NSRegularExpression(pattern: \"^(.|[\\\\w]+)(:([,\\\\w⌘^⌥⇧]+))?$\")\n\n    init(sequence: String, callback: @escaping () -> Void) {\n        guard let groups = getRegexGroups(expression, sequence) else {\n            fatalError(\"Invalid sequence: \\(sequence)\")\n        }\n        let modifiers = groups[3]?.split(separator: \",\") ?? []\n\n        self.keycode = KeyCodes.getKeyInfo(groups[1]!)!.keyCode\n        do {\n            self.modifiers = try modifiers.map {\n                (modifier) -> CGEventFlags in\n                try KeyPresser.getModifierFlag(String(modifier))\n            }\n        } catch {\n            fatalError(\"Failed to get modifier flags: \\(error)\")\n        }\n        self.callback = callback\n    }\n\n    func listen() {\n        DispatchQueue.global(qos: .background).async {\n            self.listenSync()\n        }\n    }\n\n    private func listenSync() {\n        let eventMask = 1 << CGEventType.keyDown.rawValue\n\n        self.runLoop = CFRunLoopGetCurrent()\n        let info = UnsafeMutableRawPointer(mutating: bridge(obj: self))\n\n        guard\n            let eventTap = CGEvent.tapCreate(\n                tap: .cghidEventTap, place: .tailAppendEventTap, options: .defaultTap,\n                eventsOfInterest: CGEventMask(eventMask),\n                callback: {\n                    (proxy: CGEventTapProxy, eventType: CGEventType, event: CGEvent, refcon: UnsafeMutableRawPointer?)\n                        -> Unmanaged<CGEvent>? in\n\n                    let listener: TerminationListener = bridge(ptr: UnsafeRawPointer(refcon)!)\n                    let keycode = event.getIntegerValueField(.keyboardEventKeycode)\n\n                    if keycode == listener.keycode {\n                        var flagsMatch = true\n                        for flag in listener.flags {\n                            if event.flags.contains(flag) != listener.modifiers.contains(flag) {\n                                flagsMatch = false\n                                break\n                            }\n                        }\n\n                        if flagsMatch {\n                            listener.callback()\n                        }\n                    }\n\n                    return Unmanaged.passRetained(event)\n                }, userInfo: info)\n        else {\n            fatalError(\"Failed to create event tap.\")\n        }\n\n        self.runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0)\n        CFRunLoopAddSource(self.runLoop, runLoopSource, .commonModes)\n        CGEvent.tapEnable(tap: eventTap, enable: true)\n        CFRunLoopRun()\n    }\n\n    func stop() {\n        guard let runLoopSource = self.runLoopSource else {\n            return\n        }\n\n        guard let runLoop = self.runLoop else {\n            return\n        }\n\n        CFRunLoopRemoveSource(runLoop, runLoopSource, .commonModes)\n        CFRunLoopStop(runLoop)\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Transformer.swift",
    "content": "import ArgumentParser\nimport Foundation\n\n@available(OSX 10.11, *)\nclass Transformer: ParsableCommand {\n    public static let configuration = CommandConfiguration(\n        commandName: \"transform\",\n        abstract:\n            \"Transforms raw text input into application friendly character sequences. Examples include accounting for applications that automatically indent source code and insert closing brackets.\"\n    )\n\n    @Flag(\n        name: .shortAndLong, inversion: FlagInversion.prefixedNo,\n        help: \"Determines if the application automatically inserts indentation.\")\n    var indent: Bool?\n\n    @Option(\n        name: .shortAndLong,\n        help:\n            \"Specifies which brackets are automatically closed by the application and don't need to be explicitly closed.\"\n    )\n    var autoClose: String?\n\n    @Option(\n        name: NameSpecification([.customShort(\"f\"), .long]),\n        help: \"File containing keystroke instructions to transform.\")\n    var inputFile: String?\n\n    @Option(name: .shortAndLong, help: \"String of characters to transform.\")\n    var characters: String?\n\n    var config: TransformerConfig\n\n    public init(indent: Bool, autoClose: String = \"}])\") {\n        self.config = TransformerConfig(indent: indent, autoClose: autoClose)\n    }\n\n    required init() {\n        self.config = TransformerConfig(indent: true, autoClose: \"}])\")\n    }\n\n    func run() {\n        var commandString: String?\n        self.config = self.config\n            .merge(with: ConfigLoader.loadConfig().transformer)\n            .merge(with: TransformerConfig(indent: indent, autoClose: autoClose))\n\n        if !(inputFile ?? \"\").isEmpty {\n            if let data = FileManager.default.contents(atPath: inputFile!) {\n                commandString = String(data: data, encoding: .utf8)\n            } else {\n                fatalError(\"Could not read file \\(inputFile!)\\n\")\n            }\n        } else if !(characters ?? \"\").isEmpty {\n            commandString = characters\n        }\n\n        if !(commandString ?? \"\").isEmpty {\n            fputs(transform(commandString!), stdout)\n        } else if !isTty() {\n            var data: Data\n\n            repeat {\n                data = FileHandle.standardInput.availableData\n\n                if data.count > 0 {\n                    commandString = String(data: data, encoding: .utf8)\n                    fputs(transform(commandString!), stdout)\n                }\n            } while data.count > 0\n        } else {\n            print(SendKeysCli.helpMessage(for: Self.self))\n        }\n    }\n\n    func transform(_ input: String) -> String {\n        var output = input\n\n        if self.config.indent! {\n            let removeIndentExpression = try! NSRegularExpression(pattern: \"^[\\\\t ]+\", options: .anchorsMatchLines)\n            let range = NSRange(location: 0, length: output.count)\n            output = removeIndentExpression.stringByReplacingMatches(\n                in: output, options: [], range: range, withTemplate: \"\")\n        }\n\n        if !self.config.autoClose!.isEmpty {\n            let removeBracketExpression = try! NSRegularExpression(\n                pattern:\n                    \"\\\\n[\\\\t ]*[\\(NSRegularExpression.escapedPattern(for: self.config.autoClose!).replacingOccurrences(of: \"]\", with: \"\\\\]\"))]+\"\n            )\n            let range = NSRange(location: 0, length: output.count)\n            output = removeBracketExpression.stringByReplacingMatches(\n                in: output, options: .withoutAnchoringBounds, range: range,\n                withTemplate: \"<\\\\\\\\>\\n<c:down><p:0><c:right:command>\")\n        }\n\n        return output\n    }\n}\n"
  },
  {
    "path": "Sources/SendKeysLib/Utilities.swift",
    "content": "import Foundation\n\nfunc isTty() -> Bool {\n    return isatty(FileHandle.standardInput.fileDescriptor) == 1\n}\n\nfunc getRegexGroups(_ expression: NSRegularExpression, _ input: String) -> [String?]? {\n    var groups: [String?] = []\n    let matchResult = expression.firstMatch(\n        in: input, options: .anchored, range: NSRange(location: 0, length: input.utf8.count))\n\n    if matchResult == nil {\n        return nil\n    }\n\n    let numberOfRanges = matchResult!.numberOfRanges\n\n    for i in 0..<numberOfRanges {\n        let range = Range(matchResult!.range(at: i), in: input)\n        let arg = range == nil ? nil : String(input[range!])\n        groups.append(arg)\n    }\n\n    return groups\n}\n"
  },
  {
    "path": "Sources/sendkeys/main.swift",
    "content": "import Foundation\nimport SendKeysLib\n\nif #available(OSX 10.11, *) {\n    SendKeysCli.main()\n} else {\n    print(\"OS version 10.11 or higher is required.\")\n}\n"
  },
  {
    "path": "Tests/LinuxMain.swift",
    "content": "import XCTest\nimport sendkeysTests\n\nvar tests = [XCTestCaseEntry]()\ntests += sendkeysTests.allTests()\nXCTMain(tests)\n"
  },
  {
    "path": "Tests/SendKeysTests/CommandIteratorTests.swift",
    "content": "import XCTest\n\n@testable import SendKeysLib\n\nfinal class CommandIteratorTests: XCTestCase {\n    var commandFactory: CommandFactory!\n\n    override func setUp() {\n        let keyPresser = KeyPresser(app: nil)\n        commandFactory = CommandFactory(\n            keyPresser: keyPresser,\n            mouseController: MouseController(animationRefreshInterval: 0.01, keyPresser: keyPresser))\n    }\n\n    func testParsesCharacters() throws {\n        let commands = getCommands(CommandsIterator(\"abc\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                DefaultCommand(key: \"a\"),\n                DefaultCommand(key: \"b\"),\n                DefaultCommand(key: \"c\"),\n            ])\n    }\n\n    func testParsesKeyPress() throws {\n        let commands = getCommands(CommandsIterator(\"<c:a>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                KeyPressCommand(key: \"a\", modifiers: [])\n            ])\n    }\n\n    func testParsesKeyPressDelete() throws {\n        let commands = getCommands(CommandsIterator(\"<c:delete>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                KeyPressCommand(key: \"delete\", modifiers: [])\n            ])\n    }\n\n    func testParsesKeyPressesWithModifierKey() throws {\n        let commands = getCommands(CommandsIterator(\"<c:a:command>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                KeyPressCommand(key: \"a\", modifiers: [\"command\"])\n            ])\n    }\n\n    func testParsesKeyPressesWithModifierKeys() throws {\n        let commands = getCommands(CommandsIterator(\"<c:a:command,shift>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                KeyPressCommand(key: \"a\", modifiers: [\"command\", \"shift\"])\n            ])\n    }\n\n    func testParsesKeyPressAlias() throws {\n        let commands = getCommands(CommandsIterator(\"<k:a>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                KeyPressCommand(key: \"a\", modifiers: [])\n            ])\n    }\n\n    func testParsesKeyDown() throws {\n        let commands = getCommands(CommandsIterator(\"<kd:a>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                KeyDownCommand(key: \"a\", modifiers: [])\n            ])\n    }\n\n    func testParsesKeyDownWithModifierKey() throws {\n        let commands = getCommands(CommandsIterator(\"<kd:a:shift>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                KeyDownCommand(key: \"a\", modifiers: [\"shift\"])\n            ])\n    }\n\n    func testParsesKeyDownAsModifierKey() throws {\n        let commands = getCommands(CommandsIterator(\"<kd:shift>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                KeyDownCommand(key: \"shift\", modifiers: [])\n            ])\n    }\n\n    func testParsesKeyUp() throws {\n        let commands = getCommands(CommandsIterator(\"<ku:a>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                KeyUpCommand(key: \"a\", modifiers: [])\n            ])\n    }\n\n    func testParsesKeyUpWithModifierKey() throws {\n        let commands = getCommands(CommandsIterator(\"<ku:a:shift>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                KeyUpCommand(key: \"a\", modifiers: [\"shift\"])\n            ])\n    }\n\n    func testParsesKeyUpAsModifierKey() throws {\n        let commands = getCommands(CommandsIterator(\"<ku:shift>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                KeyUpCommand(key: \"shift\", modifiers: [])\n            ])\n    }\n\n    func testParsesNewLines() throws {\n        let commands = getCommands(CommandsIterator(\"\\n\\n\\n\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                NewlineCommand(),\n                NewlineCommand(),\n                NewlineCommand(),\n            ])\n    }\n\n    func testParsesNewLinesWithCarriageReturns() throws {\n        let commands = getCommands(CommandsIterator(\"\\r\\n\\r\\n\\n\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                NewlineCommand(),\n                NewlineCommand(),\n                NewlineCommand(),\n            ])\n    }\n\n    func testParsesMultipleKeyPresses() throws {\n        let commands = getCommands(CommandsIterator(\"<c:a:command><c:c:command>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                KeyPressCommand(key: \"a\", modifiers: [\"command\"]),\n                KeyPressCommand(key: \"c\", modifiers: [\"command\"]),\n            ])\n    }\n\n    func testParsesContinuation() throws {\n        let commands = getCommands(CommandsIterator(\"<\\\\>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                ContinuationCommand()\n            ])\n    }\n\n    func testParsesPause() throws {\n        let commands = getCommands(CommandsIterator(\"<p:0.2>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                PauseCommand(duration: 0.2)\n            ])\n    }\n\n    func testParsesStickyPause() throws {\n        let commands = getCommands(CommandsIterator(\"<P:0.2>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                StickyPauseCommand(duration: 0.2)\n            ])\n    }\n\n    func testParsesMouseMove() throws {\n        let commands = getCommands(CommandsIterator(\"<m:1.5,2.5,3.5,4.5>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseMoveCommand(x1: 1.5, y1: 2.5, x2: 3.5, y2: 4.5, duration: 0, modifiers: [])\n            ])\n    }\n\n    func testParsesMouseMoveWithModifier() throws {\n        let commands = getCommands(CommandsIterator(\"<m:1,2,3,4:command>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseMoveCommand(x1: 1, y1: 2, x2: 3, y2: 4, duration: 0, modifiers: [\"command\"])\n            ])\n    }\n\n    func testParsesMouseMoveWithDuration() throws {\n        let commands = getCommands(CommandsIterator(\"<m:1,2,3,4:0.1>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseMoveCommand(x1: 1, y1: 2, x2: 3, y2: 4, duration: 0.1, modifiers: [])\n            ])\n    }\n\n    func testParsesMouseMoveWithNegativeCoordinates() throws {\n        let commands = getCommands(CommandsIterator(\"<m:-1,-2,-3,-4:0.1>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseMoveCommand(x1: -1, y1: -2, x2: -3, y2: -4, duration: 0.1, modifiers: [])\n            ])\n    }\n\n    func testParsesMouseMoveWithDurationAndModifiers() throws {\n        let commands = getCommands(CommandsIterator(\"<m:1,2,3,4:0.1:shift,command>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseMoveCommand(x1: 1, y1: 2, x2: 3, y2: 4, duration: 0.1, modifiers: [\"shift\", \"command\"])\n            ])\n    }\n\n    func testParsesPartialMouseMove() throws {\n        let commands = getCommands(CommandsIterator(\"<m:3,4>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseMoveCommand(x1: nil, y1: nil, x2: 3, y2: 4, duration: 0, modifiers: [])\n            ])\n    }\n\n    func testParsesPartialMouseMoveWithDuration() throws {\n        let commands = getCommands(CommandsIterator(\"<m:3,4:2>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseMoveCommand(x1: nil, y1: nil, x2: 3, y2: 4, duration: 2, modifiers: [])\n            ])\n    }\n\n    func testParsesMouseClick() throws {\n        let commands = getCommands(CommandsIterator(\"<m:left>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseClickCommand(button: \"left\", modifiers: [], clicks: 1)\n            ])\n    }\n\n    func testParsesMouseClickWithModifiers() throws {\n        let commands = getCommands(CommandsIterator(\"<m:left:shift,command>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseClickCommand(button: \"left\", modifiers: [\"shift\", \"command\"], clicks: 1)\n            ])\n    }\n\n    func testParsesMouseClickWithClickCount() throws {\n        let commands = getCommands(CommandsIterator(\"<m:right:2>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseClickCommand(button: \"right\", modifiers: [], clicks: 2)\n            ])\n    }\n\n    func testParsesMouseClickWithModifiersAndClickCount() throws {\n        let commands = getCommands(CommandsIterator(\"<m:right:command:2>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseClickCommand(button: \"right\", modifiers: [\"command\"], clicks: 2)\n            ])\n    }\n\n    func testParsesMousePath() throws {\n        let commands = getCommands(CommandsIterator(\"<mpath:L 200 400:2>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MousePathCommand(\n                    path: \"L 200 400\", offsetX: 0, offsetY: 0, scaleX: 1, scaleY: 1, duration: 2, modifiers: [])\n            ])\n    }\n\n    func testParsesMousePathWithOffset() throws {\n        let commands = getCommands(CommandsIterator(\"<mpath:L 200 400:100,200:2>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MousePathCommand(\n                    path: \"L 200 400\", offsetX: 100, offsetY: 200, scaleX: 1, scaleY: 1, duration: 2, modifiers: [])\n            ])\n    }\n\n    func testParsesMousePathWithOffsetAndScale() throws {\n        let commands = getCommands(\n            CommandsIterator(\"<mpath:L 200 400:100,200,0.5,2.5:2>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MousePathCommand(\n                    path: \"L 200 400\", offsetX: 100, offsetY: 200, scaleX: 0.5, scaleY: 2.5, duration: 2, modifiers: [])\n            ])\n    }\n\n    func testParsesMousePathWithOffsetAndPartialScale() throws {\n        let commands = getCommands(CommandsIterator(\"<mpath:L 200 400:100,200,0.4:2>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MousePathCommand(\n                    path: \"L 200 400\", offsetX: 100, offsetY: 200, scaleX: 0.4, scaleY: 0.4, duration: 2, modifiers: [])\n            ])\n    }\n\n    func testParsesMouseDrag() throws {\n        let commands = getCommands(CommandsIterator(\"<d:1.5,2.5,3.5,4.5>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseDragCommand(x1: 1.5, y1: 2.5, x2: 3.5, y2: 4.5, duration: 0, button: \"left\", modifiers: [])\n            ])\n    }\n\n    func testParsesMouseDragWithButton() throws {\n        let commands = getCommands(CommandsIterator(\"<d:1,2,3,4:right>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseDragCommand(x1: 1, y1: 2, x2: 3, y2: 4, duration: 0, button: \"right\", modifiers: [])\n            ])\n    }\n\n    func testParsesMouseDragWithButtonAndModifiers() throws {\n        let commands = getCommands(CommandsIterator(\"<d:1,2,3,4:right:command,shift>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseDragCommand(\n                    x1: 1, y1: 2, x2: 3, y2: 4, duration: 0, button: \"right\", modifiers: [\"command\", \"shift\"])\n            ])\n    }\n\n    func testParsesMouseDragWithDuration() throws {\n        let commands = getCommands(CommandsIterator(\"<d:1,2,3,4:0.1>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseDragCommand(x1: 1, y1: 2, x2: 3, y2: 4, duration: 0.1, button: \"left\", modifiers: [])\n            ])\n    }\n\n    func testParsesMouseDragWithDurationWithNegativeCoordinates() throws {\n        let commands = getCommands(CommandsIterator(\"<d:-1.5,-2,-3,-4:0.1>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseDragCommand(x1: -1.5, y1: -2, x2: -3, y2: -4, duration: 0.1, button: \"left\", modifiers: [])\n            ])\n    }\n\n    func testParsesMouseDragWithDurationAndButton() throws {\n        let commands = getCommands(CommandsIterator(\"<d:1,2,3,4:0.1:right>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseDragCommand(x1: 1, y1: 2, x2: 3, y2: 4, duration: 0.1, button: \"right\", modifiers: [])\n            ])\n    }\n\n    func testParsesMouseDragWithDurationAndButtonAndModifier() throws {\n        let commands = getCommands(CommandsIterator(\"<d:1,2,3,4:0.1:right:command>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseDragCommand(x1: 1, y1: 2, x2: 3, y2: 4, duration: 0.1, button: \"right\", modifiers: [\"command\"])\n            ])\n    }\n\n    func testParsesPartialMouseDrag() throws {\n        let commands = getCommands(CommandsIterator(\"<d:3,4>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseDragCommand(x1: nil, y1: nil, x2: 3, y2: 4, duration: 0, button: \"left\", modifiers: [])\n            ])\n    }\n\n    func testParsesPartialMouseDragWithDuration() throws {\n        let commands = getCommands(CommandsIterator(\"<d:3,4:2>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseDragCommand(x1: nil, y1: nil, x2: 3, y2: 4, duration: 2, button: \"left\", modifiers: [])\n            ])\n    }\n\n    func testParsesPartialMouseDragWithDurationAndButton() throws {\n        let commands = getCommands(CommandsIterator(\"<d:3,4:2:center>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseDragCommand(x1: nil, y1: nil, x2: 3, y2: 4, duration: 2, button: \"center\", modifiers: [])\n            ])\n    }\n\n    func testParsesMouseScroll() throws {\n        let commands = getCommands(CommandsIterator(\"<s:0,10.5>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseScrollCommand(x: 0, y: 10.5, duration: 0, modifiers: [])\n            ])\n    }\n\n    func testParsesMouseScrollWithNegativeAmount() throws {\n        let commands = getCommands(CommandsIterator(\"<s:-100,10>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseScrollCommand(x: -100, y: 10, duration: 0, modifiers: [])\n            ])\n    }\n\n    func testParsesMouseScrollWithDuration() throws {\n        let commands = getCommands(CommandsIterator(\"<s:0,10:0.5>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseScrollCommand(x: 0, y: 10, duration: 0.5, modifiers: [])\n            ])\n    }\n\n    func testParsesMouseScrollWithDurationAndModifiers() throws {\n        let commands = getCommands(CommandsIterator(\"<s:0,10:0.5:shift>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseScrollCommand(x: 0, y: 10, duration: 0.5, modifiers: [\"shift\"])\n            ])\n    }\n\n    func testParsesMouseScrollWithNegativeAmountAndDuration() throws {\n        let commands = getCommands(CommandsIterator(\"<s:0,-10:0.5>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseScrollCommand(x: 0, y: -10, duration: 0.5, modifiers: [])\n            ])\n    }\n\n    func testParsesMouseDown() throws {\n        let commands = getCommands(CommandsIterator(\"<md:right>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseDownCommand(button: \"right\", modifiers: [])\n            ])\n    }\n\n    func testParsesMouseDownWithModifiers() throws {\n        let commands = getCommands(CommandsIterator(\"<md:left:shift,command>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseDownCommand(button: \"left\", modifiers: [\"shift\", \"command\"])\n            ])\n    }\n\n    func testParsesMouseUp() throws {\n        let commands = getCommands(CommandsIterator(\"<mu:center>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseUpCommand(button: \"center\", modifiers: [])\n            ])\n    }\n\n    func testParsesMouseUpWithModifiers() throws {\n        let commands = getCommands(CommandsIterator(\"<mu:right:option,command>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseUpCommand(button: \"right\", modifiers: [\"option\", \"command\"])\n            ])\n    }\n\n    func testParsesMouseFocus() throws {\n        let commands = getCommands(\n            CommandsIterator(\"<mf:0.5,0.5:100.5,50.5:0.5,360.5:1>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseFocusCommand(x: 0.5, y: 0.5, rx: 100.5, ry: 50.5, angleFrom: 0.5, angleTo: 360.5, duration: 1)\n            ])\n    }\n\n    func testParsesMouseFocusWithSingleRadius() throws {\n        let commands = getCommands(CommandsIterator(\"<mf:0,0:100:0,360:0.1>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseFocusCommand(x: 0, y: 0, rx: 100, ry: 100, angleFrom: 0, angleTo: 360, duration: 0.1)\n            ])\n    }\n\n    func testParsesMouseFocusWithNegativeCoordinates() throws {\n        let commands = getCommands(CommandsIterator(\"<mf:-10,-20:100,50:0,360:1.5>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseFocusCommand(x: -10, y: -20, rx: 100, ry: 50, angleFrom: 0, angleTo: 360, duration: 1.5)\n            ])\n    }\n\n    func testParsesMouseFocusWithNegativeAngles() throws {\n        let commands = getCommands(CommandsIterator(\"<mf:-10,-20:100,50:100,-360:1.5>\", commandFactory: commandFactory))\n        XCTAssertEqual(\n            commands,\n            [\n                MouseFocusCommand(x: -10, y: -20, rx: 100, ry: 50, angleFrom: 100, angleTo: -360, duration: 1.5)\n            ])\n    }\n\n    private func getCommands(_ iterator: CommandsIterator) -> [Command] {\n        var commands: [Command] = []\n\n        while true {\n            let command = iterator.next()\n\n            if command == nil {\n                break\n            }\n\n            commands.append(command!)\n        }\n\n        return commands\n    }\n}\n"
  },
  {
    "path": "Tests/SendKeysTests/CommandsProcessorTests.swift",
    "content": "import XCTest\n\n@testable import SendKeysLib\n\nclass CommandExecutorSpy: CommandExecutorProtocol {\n    var commands: [Command] = []\n\n    func execute(_ command: Command) {\n        commands.append(command)\n    }\n}\n\nfinal class CommandProcessorTests: XCTestCase {\n    var commandExecutor: CommandExecutorSpy?\n    var commandsProcessor: CommandsProcessor?\n\n    override func setUp() {\n        commandExecutor = CommandExecutorSpy()\n        commandsProcessor = CommandsProcessor(\n            defaultPause: 0.1, keyPresser: KeyPresser(app: nil), commandExecutor: commandExecutor)\n    }\n\n    func testExecutesSingleKeyPress() {\n        commandsProcessor!.process(\"a\")\n        let commands = commandExecutor!.commands\n\n        XCTAssertEqual(\n            commands,\n            [\n                DefaultCommand(key: \"a\")\n            ])\n    }\n\n    func testExecutesColonKeyPress() {\n        commandsProcessor!.process(\"<c::>\")\n        let commands = commandExecutor!.commands\n\n        XCTAssertEqual(\n            commands,\n            [\n                KeyPressCommand(key: \":\", modifiers: [])\n            ])\n    }\n\n    func testExecutesCommandOpenKeyPress() {\n        commandsProcessor!.process(\"<c:<>\")\n        let commands = commandExecutor!.commands\n\n        XCTAssertEqual(\n            commands,\n            [\n                KeyPressCommand(key: \"<\", modifiers: [])\n            ])\n    }\n\n    func testExecutesCommandCloseKeyPress() {\n        commandsProcessor!.process(\"<c:>>\")\n        let commands = commandExecutor!.commands\n\n        XCTAssertEqual(\n            commands,\n            [\n                KeyPressCommand(key: \">\", modifiers: [])\n            ])\n    }\n\n    func testExecutesColonKeyPressWithModifier() {\n        commandsProcessor!.process(\"<c:::control>\")\n        let commands = commandExecutor!.commands\n\n        XCTAssertEqual(\n            commands,\n            [\n                KeyPressCommand(key: \":\", modifiers: [\"control\"])\n            ])\n    }\n\n    func testExecutesCommandOpenKeyPressWithModifier() {\n        commandsProcessor!.process(\"<c:<:shift>\")\n        let commands = commandExecutor!.commands\n\n        XCTAssertEqual(\n            commands,\n            [\n                KeyPressCommand(key: \"<\", modifiers: [\"shift\"])\n            ])\n    }\n\n    func testExecutesCommandCloseKeyPressWithModifier() {\n        commandsProcessor!.process(\"<c:>:shift>\")\n        let commands = commandExecutor!.commands\n\n        XCTAssertEqual(\n            commands,\n            [\n                KeyPressCommand(key: \">\", modifiers: [\"shift\"])\n            ])\n    }\n\n    func testExecutesSpecialKeyPress() {\n        commandsProcessor!.process(\"<c:tab>\")\n        let commands = commandExecutor!.commands\n\n        XCTAssertEqual(\n            commands,\n            [\n                KeyPressCommand(key: \"tab\", modifiers: [])\n            ])\n    }\n\n    func testExecutesSpecialKeyPressWithModifier() {\n        commandsProcessor!.process(\"<c:tab:shift>\")\n        let commands = commandExecutor!.commands\n\n        XCTAssertEqual(\n            commands,\n            [\n                KeyPressCommand(key: \"tab\", modifiers: [\"shift\"])\n            ])\n    }\n\n    func testExecutesMultipleKeyPressWithDelay() {\n        commandsProcessor!.process(\"hello\")\n        let commands = commandExecutor!.commands\n\n        XCTAssertEqual(\n            commands,\n            [\n                DefaultCommand(key: \"h\"),\n                PauseCommand(duration: 0.1),\n                DefaultCommand(key: \"e\"),\n                PauseCommand(duration: 0.1),\n                DefaultCommand(key: \"l\"),\n                PauseCommand(duration: 0.1),\n                DefaultCommand(key: \"l\"),\n                PauseCommand(duration: 0.1),\n                DefaultCommand(key: \"o\"),\n            ])\n    }\n\n    func testExecutesMultipleKeyPressWithExplicitPause() {\n        commandsProcessor!.process(\"a<p:10><c:a:command>\")\n        let commands = commandExecutor!.commands\n\n        XCTAssertEqual(\n            commands,\n            [\n                DefaultCommand(key: \"a\"),\n                PauseCommand(duration: 10),\n                KeyPressCommand(key: \"a\", modifiers: [\"command\"]),\n            ])\n    }\n\n    func testExecutesMultipleKeyPressWithStickyPause() {\n        commandsProcessor!.process(\"ab<P:10>cd\")\n        let commands = commandExecutor!.commands\n\n        XCTAssertEqual(\n            commands,\n            [\n                DefaultCommand(key: \"a\"),\n                PauseCommand(duration: 0.1),\n                DefaultCommand(key: \"b\"),\n                StickyPauseCommand(duration: 10),\n                DefaultCommand(key: \"c\"),\n                PauseCommand(duration: 10),\n                DefaultCommand(key: \"d\"),\n            ])\n    }\n\n    func testIgnoreContinuation() {\n        commandsProcessor!.process(\"<\\\\>\")\n        let commands = commandExecutor!.commands\n\n        XCTAssertEqual(commands, [])\n    }\n\n    func testIgnoreConsecutiveContinuations() {\n        commandsProcessor!.process(\"<\\\\><\\\\>\")\n        let commands = commandExecutor!.commands\n\n        XCTAssertEqual(commands, [])\n    }\n\n    func testNegateConsecutiveContinuations() {\n        commandsProcessor!.process(\"<\\\\><\\\\>a\")\n        let commands = commandExecutor!.commands\n\n        XCTAssertEqual(\n            commands,\n            [\n                DefaultCommand(key: \"a\")\n            ])\n    }\n\n    func testExecutesMultipleKeyPressWithContinuation() {\n        commandsProcessor!.process(\"a<\\\\>b\")\n        let commands = commandExecutor!.commands\n\n        XCTAssertEqual(\n            commands,\n            [\n                DefaultCommand(key: \"a\")\n            ])\n    }\n\n    func testExecutesMultipleMouseCommands() {\n        commandsProcessor!.process(\"<m:20,20:1><m:200,200:1><m:left>\")\n        let commands = commandExecutor!.commands\n\n        XCTAssertEqual(\n            commands,\n            [\n                MouseMoveCommand(x1: nil, y1: nil, x2: 20, y2: 20, duration: 1, modifiers: []),\n                PauseCommand(duration: 0.1),\n                MouseMoveCommand(x1: nil, y1: nil, x2: 200, y2: 200, duration: 1, modifiers: []),\n                PauseCommand(duration: 0.1),\n                MouseClickCommand(button: \"left\", modifiers: [], clicks: 1),\n            ])\n    }\n}\n"
  },
  {
    "path": "Tests/SendKeysTests/KeyPresserTests.swift",
    "content": "import XCTest\n\n@testable import SendKeysLib\n\nfinal class KeyPresserTests: XCTestCase {\n    func testEscapeKey() throws {\n        let presser = KeyPresser(app: nil)\n        let event = try! presser.keyPress(key: \"escape\", modifiers: [])\n\n        XCTAssertEqual(event!.getIntegerValueField(.keyboardEventKeycode), 53)\n    }\n\n    func testEscapeKeyUsingAlias() throws {\n        let presser = KeyPresser(app: nil)\n        let event = try! presser.keyPress(key: \"esc\", modifiers: [])\n\n        XCTAssertEqual(event!.getIntegerValueField(.keyboardEventKeycode), 53)\n    }\n\n    func testEnterKey() throws {\n        let presser = KeyPresser(app: nil)\n        let event = try! presser.keyPress(key: \"return\", modifiers: [])\n\n        XCTAssertEqual(event!.getIntegerValueField(.keyboardEventKeycode), 36)\n    }\n\n    func testShiftModifier() throws {\n        let presser = KeyPresser(app: nil)\n        let event = try! presser.keyPress(key: \"a\", modifiers: [\"shift\"])\n\n        XCTAssertEqual(event!.getIntegerValueField(.keyboardEventKeycode), 0)\n        XCTAssertTrue(event!.flags.contains([.maskShift]))\n    }\n\n    func testCommandControlModifier() throws {\n        let presser = KeyPresser(app: nil)\n        let event = try! presser.keyPress(key: \"c\", modifiers: [\"command\", \"control\"])\n\n        XCTAssertEqual(event!.getIntegerValueField(.keyboardEventKeycode), 8)\n        XCTAssertTrue(event!.flags.contains([.maskCommand, .maskControl]))\n    }\n}\n"
  },
  {
    "path": "Tests/SendKeysTests/PathDataTests.swift",
    "content": "import XCTest\n\n@testable import SendKeysLib\n\nfinal class PathDataTests: XCTestCase {\n    func testNormalizesPathData() throws {\n        let result = PathData(\"M 0 1 L 100 101 H-1V+2 Z\").commands\n        XCTAssertEqual(\n            result,\n            [\n                PointPathCommand(\"M\", PointValue(CGPoint(x: 0, y: 1))),\n                PointPathCommand(\"L\", PointValue(CGPoint(x: 100, y: 101))),\n                PointPathCommand(\"L\", PointValue(CGPoint(x: -1, y: 101))),\n                PointPathCommand(\"L\", PointValue(CGPoint(x: -1, y: 2))),\n                PointPathCommand(\"L\", PointValue(CGPoint(x: 0, y: 1))),\n            ]\n        )\n    }\n\n    func testNormalizesPathDataWithRelativeCommands() throws {\n        let result = PathData(\"m 10 20 h 6 v 10 l 15 -8 c 10 10 20 20 30 30\").commands\n        XCTAssertEqual(\n            result,\n            [\n                PointPathCommand(\"M\", PointValue(CGPoint(x: 10, y: 20))),\n                PointPathCommand(\"L\", PointValue(CGPoint(x: 16, y: 20))),\n                PointPathCommand(\"L\", PointValue(CGPoint(x: 16, y: 30))),\n                PointPathCommand(\"L\", PointValue(CGPoint(x: 31, y: 22))),\n                CubicBezierPathCommand(\n                    \"C\", ControlPointsValue(CGPoint(x: 41, y: 32), CGPoint(x: 51, y: 42), CGPoint(x: 61, y: 52))),\n            ]\n        )\n    }\n\n    func testGetsTotalDistance() throws {\n        let result = PathData(\"M 100 200 l 300 400\").getTotalDistance()\n        XCTAssertEqual(\n            result,\n            500\n        )\n    }\n\n    func testGetsTotalDistanceOfMultipleStraightLines() throws {\n        let result = PathData(\"M 100 200 h 150 v 220\").getTotalDistance()\n        XCTAssertEqual(\n            result,\n            370\n        )\n    }\n\n    func testGetsPointAtInterval_beginning() throws {\n        let result = PathData(\"M 100 200 l 300 400\").getPointAtInterval(0)\n        XCTAssertEqual(\n            result,\n            CGPoint(x: 100, y: 200)\n        )\n    }\n\n    func testGetsPointAtInterval_middle() throws {\n        let result = PathData(\"M 100 200 l 300 400\").getPointAtInterval(0.5)\n        XCTAssertEqual(\n            result,\n            CGPoint(x: 250, y: 400)\n        )\n    }\n\n    func testGetsPointAtInterval_end() throws {\n        let result = PathData(\"M 100 200 l 300 400\").getPointAtInterval(1)\n        XCTAssertEqual(\n            result,\n            CGPoint(x: 400, y: 600)\n        )\n    }\n\n    func testGetsPointAtIntervalOfMultipleStraightLines_middle() throws {\n        let result = PathData(\"M 100 200 h 150 v 220\").getPointAtInterval(0.5)\n        XCTAssertEqual(\n            result,\n            CGPoint(x: 250, y: 235)\n        )\n    }\n\n    func testGetsPointAtIntervalOfMultipleStraightLines_beyond_end() throws {\n        let result = PathData(\"M 100 200 h 150 v 220\").getPointAtInterval(1.2)\n        XCTAssertEqual(\n            result,\n            CGPoint(x: 250, y: 420)\n        )\n    }\n\n    func testGetsPointAtIntervalOfMultipleStraightLinePaths_end() throws {\n        let result = PathData(\"M 100 0 h 20 20 20 l 200 0\").getPointAtInterval(1)\n        XCTAssertEqual(\n            result,\n            CGPoint(x: 360, y: 0)\n        )\n    }\n\n    func testGetsPointAtIntervalOfMultiplePaths_end() throws {\n        let result = PathData(\"M 100 0 a 100,100 0 0,1 200,0 l 100 0\").getPointAtInterval(1)\n        XCTAssertEqual(\n            result,\n            CGPoint(x: 400, y: 0)\n        )\n    }\n}\n"
  },
  {
    "path": "Tests/SendKeysTests/PathParserTests.swift",
    "content": "import XCTest\n\n@testable import SendKeysLib\n\nfinal class PathParserTests: XCTestCase {\n    func testParsesRelativeCommand_m() throws {\n        let result = PathParser(\"m 0 1\").parse()\n        XCTAssertEqual(\n            result,\n            [PointPathCommand(\"m\", PointValue(CGPoint(x: 0, y: 1)))]\n        )\n    }\n\n    func testParsesRelativeCommand_l() throws {\n        let result = PathParser(\"l 0 1\").parse()\n        XCTAssertEqual(\n            result,\n            [PointPathCommand(\"l\", PointValue(CGPoint(x: 0, y: 1)))]\n        )\n    }\n\n    func testParsesRelativeCommand_c() throws {\n        let result = PathParser(\"c 0 1 2 3 4 5\").parse()\n        XCTAssertEqual(\n            result,\n            [\n                CubicBezierPathCommand(\n                    \"c\", ControlPointsValue(CGPoint(x: 0, y: 1), CGPoint(x: 2, y: 3), CGPoint(x: 4, y: 5)))\n            ]\n        )\n    }\n\n    func testParsesRelativeCommand_s() throws {\n        let result = PathParser(\"s 0 1 2 3\").parse()\n        XCTAssertEqual(\n            result,\n            [CubicBezierPathCommand(\"s\", ControlPointsValue(nil, CGPoint(x: 0, y: 1), CGPoint(x: 2, y: 3)))]\n        )\n    }\n\n    func testParsesRelativeCommand_a() throws {\n        let result = PathParser(\"a 10,11 2 0,1 5,6\").parse()\n        XCTAssertEqual(\n            result,\n            [ArcPathCommand(\"a\", ArchCommandValue(CGPoint(x: 10, y: 11), 2, false, true, CGPoint(x: 5, y: 6)))]\n        )\n    }\n\n    func testParsesRelativeCommand_h() throws {\n        let result = PathParser(\"h 1\").parse()\n        XCTAssertEqual(\n            result,\n            [NumericPathCommand(\"h\", 1)]\n        )\n    }\n\n    func testParsesRelativeCommand_v() throws {\n        let result = PathParser(\"v 1\").parse()\n        XCTAssertEqual(\n            result,\n            [NumericPathCommand(\"v\", 1)]\n        )\n    }\n\n    func testParsesRelativeCommand_t() throws {\n        let result = PathParser(\"t 0 1\").parse()\n        XCTAssertEqual(\n            result,\n            [QuadraticBezierPathCommand(\"t\", ControlPointValue(nil, CGPoint(x: 0, y: 1)))]\n        )\n    }\n\n    func testParsesRelativeCommand_q() throws {\n        let result = PathParser(\"q 0 1 2 3\").parse()\n        XCTAssertEqual(\n            result,\n            [QuadraticBezierPathCommand(\"q\", ControlPointValue(CGPoint(x: 0, y: 1), CGPoint(x: 2, y: 3)))]\n        )\n    }\n\n    func testParsesAbsoluteCommands() throws {\n        let result = PathParser(\"M 0 1 L 100 101 H-1V+2 A 10.2,11 2 0,1 5,6 Z Q 0 1 2 3 T 2 3 C 0 1 2 3 4 5 S 0 1 2 3\")\n            .parse()\n        XCTAssertEqual(\n            result,\n            [\n                PointPathCommand(\"M\", PointValue(CGPoint(x: 0, y: 1))),\n                PointPathCommand(\"L\", PointValue(CGPoint(x: 100, y: 101))),\n                NumericPathCommand(\"H\", -1),\n                NumericPathCommand(\"V\", 2),\n                ArcPathCommand(\"A\", ArchCommandValue(CGPoint(x: 10.2, y: 11), 2, false, true, CGPoint(x: 5, y: 6))),\n                PathCommandBase(\"Z\"),\n                QuadraticBezierPathCommand(\"Q\", ControlPointValue(CGPoint(x: 0, y: 1), CGPoint(x: 2, y: 3))),\n                QuadraticBezierPathCommand(\"T\", ControlPointValue(nil, CGPoint(x: 2, y: 3))),\n                CubicBezierPathCommand(\n                    \"C\", ControlPointsValue(CGPoint(x: 0, y: 1), CGPoint(x: 2, y: 3), CGPoint(x: 4, y: 5))),\n                CubicBezierPathCommand(\"S\", ControlPointsValue(nil, CGPoint(x: 0, y: 1), CGPoint(x: 2, y: 3))),\n            ]\n        )\n    }\n\n    func testParsesAbsoluteCommandsWithAdjacent() throws {\n        let result = PathParser(\"M 0 1 L 200 201 -100 -101 V2,3\").parse()\n        XCTAssertEqual(\n            result,\n            [\n                PointPathCommand(\"M\", PointValue(CGPoint(x: 0, y: 1))),\n                PointPathCommand(\"L\", PointValue(CGPoint(x: 200, y: 201))),\n                PointPathCommand(\"L\", PointValue(CGPoint(x: -100, y: -101))),\n                NumericPathCommand(\"V\", 2),\n                NumericPathCommand(\"V\", 3),\n            ]\n        )\n    }\n}\n"
  },
  {
    "path": "Tests/SendKeysTests/TransformerTests.swift",
    "content": "import XCTest\n\n@testable import SendKeysLib\n\nfinal class TransformerTests: XCTestCase {\n    func testShouldNoteTransformSingleLine() {\n        let transformer = Transformer(indent: true)\n        let result = transformer.transform(\"hello world\")\n\n        XCTAssertEqual(result, \"hello world\")\n    }\n\n    func testShouldNotTransformCurlyBraceOnSameLine() {\n        let transformer = Transformer(indent: true)\n        let result = transformer.transform(\"{}\")\n\n        XCTAssertEqual(result, \"{}\")\n    }\n\n    func testTransformCurlyBraceOnDifferentLine() {\n        let transformer = Transformer(indent: true)\n        let result = transformer.transform(\"{\\n}\")\n\n        XCTAssertEqual(result, \"{<\\\\>\\n<c:down><p:0><c:right:command>\")\n    }\n\n    func testTransformCurlyBraceWithBasicContent() {\n        let transformer = Transformer(indent: true)\n        let result = transformer.transform(\"hello {\\n  world\\n}\")\n\n        XCTAssertEqual(result, \"hello {\\nworld<\\\\>\\n<c:down><p:0><c:right:command>\")\n    }\n\n    func testTransformBracketAndCurlyBraceWithBasicContent() {\n        let transformer = Transformer(indent: true)\n        let result = transformer.transform(\"hello ({\\n  world\\n})\")\n\n        XCTAssertEqual(result, \"hello ({\\nworld<\\\\>\\n<c:down><p:0><c:right:command>\")\n    }\n}\n"
  },
  {
    "path": "Tests/SendKeysTests/XCTestManifests.swift",
    "content": "import XCTest\n\n#if !canImport(ObjectiveC)\n    public func allTests() -> [XCTestCaseEntry] {\n        return [\n            testCase(sendkeysTests.allTests)\n        ]\n    }\n#endif\n"
  },
  {
    "path": "Tests/SendKeysTests/sendkeysTests.swift",
    "content": "import XCTest\n\nimport class Foundation.Bundle\n\nfinal class sendkeysTests: XCTestCase {\n    func testHelp() throws {\n        // This is an example of a functional test case.\n        // Use XCTAssert and related functions to verify your tests produce the correct\n        // results.\n\n        // Some of the APIs that we use below are available in macOS 10.13 and above.\n        guard #available(macOS 10.13, *) else {\n            return\n        }\n\n        let fooBinary = productsDirectory.appendingPathComponent(\"sendkeys\")\n\n        let process = Process()\n        process.executableURL = fooBinary\n        process.arguments = [\"--help\"]\n\n        let pipe = Pipe()\n        process.standardOutput = pipe\n\n        try process.run()\n        process.waitUntilExit()\n\n        let data = pipe.fileHandleForReading.readDataToEndOfFile()\n        let output = String(data: data, encoding: .utf8)\n\n        XCTAssertTrue(output!.hasPrefix(\"OVERVIEW: Command line tool for automating keystrokes and mouse events\"))\n    }\n\n    /// Returns path to the built products directory.\n    var productsDirectory: URL {\n        #if os(macOS)\n            for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(\".xctest\") {\n                return bundle.bundleURL.deletingLastPathComponent()\n            }\n            fatalError(\"Couldn't find the products directory\\n\")\n        #else\n            return Bundle.main.bundleURL\n        #endif\n    }\n}\n"
  },
  {
    "path": "Tests/keys.txt",
    "content": "0123456789<\\>\nabcdefghijklmnopqrstuvwxyz<\\>\nABCDEFGHIJKLMNOPQRSTUVWXYZ<\\>\n-,;.'[]/\\`=<\\>\n<c:c:control><\\>\n"
  },
  {
    "path": "examples/.sendkeysrc.yml",
    "content": "# All properties are optional\nsend:\n  activate: true\n  animationInterval: 0.01\n  delay: 0.1\n  initialDelay: 1\n  keyboardLayout: qwerty\n  targeted: false\n  terminateCommand: \"c:control\"\n  remap:\n    # Example of custom key mapping to support dvorak keyboard layout\n    \"[\": \"-\"\n    \"]\": \"=\"\n    \"'\": \"q\"\n    \",\": \"w\"\n    \".\": \"e\"\n    \"p\": \"r\"\n    \"y\": \"t\"\n    \"f\": \"y\"\n    \"g\": \"u\"\n    \"c\": \"i\"\n    \"r\": \"o\"\n    \"l\": \"p\"\n    \"/\": \"[\"\n    \"=\": \"]\"\n    \"a\": \"a\"\n    \"o\": \"s\"\n    \"e\": \"d\"\n    \"u\": \"f\"\n    \"i\": \"g\"\n    \"d\": \"h\"\n    \"h\": \"j\"\n    \"t\": \"k\"\n    \"n\": \"l\"\n    \"s\": \":\"\n    \"-\": \"'\"\n    \";\": \"z\"\n    \"q\": \"x\"\n    \"j\": \"c\"\n    \"k\": \"v\"\n    \"x\": \"b\"\n    \"b\": \"n\"\n    \"m\": \"m\"\n    \"w\": \",\"\n    \"v\": \".\"\n    \"z\": \"/\"\n\nmousePosition:\n  watch: true\n  output: commands\n  duration: 1\n\ntransform:\n  indent: true\n  autoClose: \"}])\"\n"
  },
  {
    "path": "examples/node.js",
    "content": "const http = require('http');\n\nconst hostname = '127.0.0.1';\nconst port = 3000;\n\nconst server = http.createServer((req, res) => {\n  res.statusCode = 200;\n  res.setHeader('Content-Type', 'text/plain');\n  res.end('Hello World');\n});\n\nserver.listen(port, hostname, () => {\n  console.log(`Server running at http://${hostname}:${port}/`);\n});\n"
  },
  {
    "path": "scripts/bottle.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\n\ncwd=`pwd`\nscript_folder=`cd $(dirname $0) && pwd`\nversion=$1\nformula_template=$script_folder/../Formula/sendkeys_template.rb\nformula=$script_folder/../Formula/sendkeys.rb\nurl=\"file://$cwd/sendkeys.tar.gz?date=`date +%s`\"\nsed_url=`echo $url | sed 's/\\\\//\\\\\\\\\\//g'`\n\nversion=`echo $version | sed -E 's/^v//g'`\n\nrm sendkeys*.tar.gz || true\ntar zcvf sendkeys.tar.gz --exclude=\".git\" --exclude=\".build\" ./\n\ncp $formula_template $formula\n\n# update url\nsed -E -i \"\" \"s/url \\\"\\\"/url \\\"$sed_url\\\"/g\" $formula\n\n# update version number\nsed -E -i \"\" \"s/version \\\"[0-9]+\\.[0-9]+\\.[0-9]+\\\"/version \\\"$version\\\"/g\" $formula\n\nbrew install --force --formula --build-bottle $formula\n\necho \"Bottle built\"\n\nbrew bottle sendkeys --force-core-tap --no-rebuild --root-url \"https://github.com/socsieng/sendkeys/releases/download/v${version}\"\n\nbottle=`ls sendkeys--$version.*.tar.gz`\nbottle_big_sur=\"sendkeys-$version.big_sur.bottle.tar.gz\"\nbottle_arm64_big_sur=\"sendkeys-$version.arm64_big_sur.bottle.tar.gz\"\n\ncp $bottle $bottle_big_sur\ncp $bottle $bottle_arm64_big_sur\n\necho \"big_sur=$bottle_big_sur\" >> $GITHUB_OUTPUT\necho \"arm64_big_sur=$bottle_arm64_big_sur\" >> $GITHUB_OUTPUT\necho \"root_url=https://github.com/socsieng/sendkeys/releases/download/v${version}\" >> $GITHUB_OUTPUT\necho \"url=https://github.com/socsieng/sendkeys/releases/download/v${version}/$bottle_rename\" >> $GITHUB_OUTPUT\necho \"sha=$(shasum -a 256 $bottle | awk '{printf $1}')\" >> $GITHUB_OUTPUT\n\nbrew uninstall sendkeys\n"
  },
  {
    "path": "scripts/code-coverage.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\n\ncwd=`pwd`\nscript_folder=`cd $(dirname $0) && pwd`\nbuild_folder=$script_folder/../.build\noutput_folder=$script_folder/../.output\n\nmkdir -p $output_folder\n\nswift test --enable-code-coverage\n\n# if in the CI environment, then use llvm-cov report instead\nif [ -n \"$CI\" ]; then\n  xcrun llvm-cov report \\\n    --ignore-filename-regex='(.build|Tests)[/\\\\].*' \\\n    -instr-profile \"$(swift test --show-codecov-path | xargs dirname)/default.profdata\" \\\n    .build/debug/sendkeysPackageTests.xctest/Contents/*/sendkeysPackageTests\n  exit 0\nfi\n\nxcrun llvm-cov export \\\n  --format=lcov \\\n  --ignore-filename-regex='(.build|Tests)[/\\\\].*' \\\n  -instr-profile \"$(swift test --show-codecov-path | xargs dirname)/default.profdata\" \\\n  .build/debug/sendkeysPackageTests.xctest/Contents/*/sendkeysPackageTests > \"$output_folder/coverage.lcov\"\n\ngenhtml -o \"$output_folder/coverage\" \"$output_folder/coverage.lcov\"\n\nopen \"file://$output_folder/coverage/index.html\"\n"
  },
  {
    "path": "scripts/format.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\n\ncwd=`pwd`\nscript_folder=`cd $(dirname $0) && pwd`\nfile=$1\n\nif [ -z \"$file\" ]\nthen\n  # all files\n  swift-format --configuration $script_folder/../.swift-format -ir $script_folder/../*.swift $script_folder/../Sources $script_folder/../Tests\nelse\n  swift-format --configuration $script_folder/../.swift-format -ir $file\nfi\n"
  },
  {
    "path": "scripts/install-pre-commit.sh",
    "content": "#!/usr/bin/env bash\nset -e\n\nscript_folder=`cd $(dirname $0) && pwd`\nrepo_folder=`git rev-parse --show-toplevel`\n\ncp -f $script_folder/pre-commit.sh $repo_folder/.git/hooks/pre-commit\n"
  },
  {
    "path": "scripts/pre-commit.sh",
    "content": "#!/usr/bin/env bash\nset -e\n\n# get the repository root\nrepo_folder=`git rev-parse --show-toplevel`\n\n# use repository root if there is a value, otherwise use the current folder\nroot_folder=${repo_folder:-`pwd`}\n\nfiles=`git diff --cached --name-only --diff-filter=ACM`\n\nfor f in $files\ndo\n  if [[ $f == *.swift ]]\n  then\n    echo \"Formatting $f\"\n    $root_folder/scripts/format.sh $f\n    git add $f\n  fi\ndone\n"
  },
  {
    "path": "scripts/update-version.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\n\ncwd=`pwd`\nscript_folder=`cd $(dirname $0) && pwd`\nversion=${1:-`cat $script_folder/../version.txt`}\n\nversion=`echo $version | sed -E 's/^v//g'`\n\necho \"updating version to $version\"\n\nsed -E -i \"\" \"s/version: \\\"[0-9]+\\.[0-9]+\\.[0-9]+\\\", \\/\\* auto-updated \\*\\//version: \\\"$version\\\", \\/\\* auto-updated \\*\\//g\" $script_folder/../Sources/SendKeysLib/SendKeysCli.swift\n"
  },
  {
    "path": "scripts/verify-output.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\n\ncwd=`pwd`\nscript_folder=`cd $(dirname $0) && pwd`\noutput_folder=\"$script_folder/../.output\"\nbuild_folder=\"$script_folder/../.build/debug\"\nexamples_folder=\"$script_folder/../examples\"\n\nmkdir -p $output_folder\n\nrm -f $output_folder/example.js\ntouch $output_folder/example.js\ncode $output_folder/example.js\nsleep 1\n\n$build_folder/sendkeys transform -f $examples_folder/node.js | $build_folder/sendkeys -d 0.05\n$build_folder/sendkeys -c '<c:s:command><m:100,100,300,300:0.1><p:1>'\n\nexpected_output=`cat $examples_folder/node.js`\nresult=`cat $output_folder/example.js`\n\nif [[ \"$expected_output\" != \"$result\" ]]; then\n  echo \"transform test failed.\"\n  exit 1\nfi\n"
  },
  {
    "path": "version.txt",
    "content": "4.3.1\n"
  }
]