[
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "## How to contribute\n\n* Please try to match the style of the code around the feature/bug you're working on. \n* Use tabs not spaces. \n* Please make sure your pull request only includes changes to the lines you're working on. For example, disable the whitespace extension when using Atom.\n* All pull requests must include code for every platform (Mac, Windows, and Linux) before they can be merged. The exception is platform specific features. Feel free to submit a pull request with code for one platform and others can fill in the gaps to help get it merged."
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "<!--- Provide a general summary of the issue in the Title above. -->\n\n## Expected Behavior\n<!--- If you're describing a bug, tell us what should happen. -->\n<!--- If you're suggesting a change/improvement, tell us how it should work. -->\n\n## Current Behavior\n<!--- If describing a bug, tell us what happens instead of the expected behavior. -->\n<!--- If suggesting a change/improvement, explain the difference from current behavior. -->\n\n## Possible Solution\n<!--- Not obligatory, but suggest a fix/reason for the bug, -->\n<!--- or ideas how to implement the addition or change. -->\n\n## Steps to Reproduce (for bugs)\n<!--- Please provide an unambiguous set of steps to reproduce this bug. -->\n<!--- Include code to reproduce. -->\n1.\n2.\n3.\n4.\n\n## Context\n<!--- How has this issue affected you? What are you trying to accomplish? -->\n<!--- Providing context helps us come up with a solution that is most useful in the real world. -->\n\n## Your Environment\n<!--- Include as many relevant details about the environment you experienced the bug in. -->\n* RobotJS version:\n* Node.js version:\n* npm version:\n* Operating System:\n"
  },
  {
    "path": ".gitignore",
    "content": "build/\nnode_modules/\nprebuilds/\n/.idea"
  },
  {
    "path": ".travis.yml",
    "content": "sudo: false\nlanguage: cpp\n\nenv:\n  matrix:\n    - TRAVIS_NODE_VERSION=\"8\"\n    - TRAVIS_NODE_VERSION=\"10\"\n    - TRAVIS_NODE_VERSION=\"12\"\n    - TRAVIS_NODE_VERSION=\"stable\"\n\nos:\n  - linux\n  - osx\n\ncache:\n  directories:\n    - node_modules\n\ngit:\n  depth: 5\n\naddons:\n  apt:\n    sources:\n      - ubuntu-toolchain-r-test\n    packages:\n      - libx11-dev\n      - zlib1g-dev\n      - libpng12-dev\n      - libxtst-dev\n      - g++-4.8\n      - gcc-4.8\n\nbefore_install:\n  - rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh\n  - nvm install $TRAVIS_NODE_VERSION\n  - PATH=$PATH:`pwd`/node_modules/.bin\n  # print versions\n  - node --version\n  - npm --version\n\n  # use g++-4.8 on Linux\n  - if [[ $TRAVIS_OS_NAME == \"linux\" ]]; then export CXX=g++-4.8; fi\n  - $CXX --version\n\ninstall:\n  - npm install\n\nscript:\n  - if [ \"$TRAVIS_OS_NAME\" = \"osx\" ]; then npm test; fi\n  - if [ \"$TRAVIS_OS_NAME\" = \"linux\" ]; then xvfb-run npm test; fi\n\nafter_success:\n  - if [[ $TRAVIS_TAG != \"\" ]]; then npm run prebuild -- -u $GITHUB_TOKEN; fi\n\nnotifications:\n  webhooks:\n    urls:\n      - https://webhooks.gitter.im/e/e737dd5170be50cdba95\n    on_success: change\n    on_failure: always\n    on_start: never\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "## 0.7.0 (2026-03-11)\n\n* build: Bump prebuild version and MACOSX_DEPLOYMENT_TARGET. ([7bcd7ff](https://github.com/octalmage/robotjs/commit/7bcd7ff))\n* Fix multi-monitor mouse coordinates and remove targetpractice ([7aef129](https://github.com/octalmage/robotjs/commit/7aef129)), closes [#693](https://github.com/octalmage/robotjs/issues/693)\n* Don't wait for a response from X when typing a key ([087ec35](https://github.com/octalmage/robotjs/commit/087ec35))\n* Apply to mouse as well ([bf54f85](https://github.com/octalmage/robotjs/commit/bf54f85))\n* Fixed scrollMouse() bug on windows ([c10ffdd](https://github.com/octalmage/robotjs/commit/c10ffdd))\n* src/keycode: fix buffer overflow ([274d03b](https://github.com/octalmage/robotjs/commit/274d03b))\n* Support typing of Unicode Characters (similar to keyTap) ([94ed67c](https://github.com/octalmage/robotjs/commit/94ed67c))\n* Support Unicode for typeString() in Windows. ([3d7e7cf](https://github.com/octalmage/robotjs/commit/3d7e7cf))\n* fix: scrolling on Windows ([cb0da09](https://github.com/octalmage/robotjs/commit/cb0da09))\n* fix: improve y scrolling on Windows by send y-scrolling event first ([a348c0e](https://github.com/octalmage/robotjs/commit/a348c0e))\n\n\n## 0.6.0 (2019-12-08)\n\n* 0.6.0 ([99021d8](https://github.com/octalmage/robotjs/commit/99021d8))\n* Add changelog ([7e067cf](https://github.com/octalmage/robotjs/commit/7e067cf))\n* Fix deprecations ([4f16371](https://github.com/octalmage/robotjs/commit/4f16371))\n* Fix cpp deprecation ([3b93d07](https://github.com/octalmage/robotjs/commit/3b93d07))\n* Update packages ([77d7cd9](https://github.com/octalmage/robotjs/commit/77d7cd9))\n* Update 3rd party builder configs ([28a428d](https://github.com/octalmage/robotjs/commit/28a428d))\n* Make keypress/release order more \"human\" ([ec88aa8](https://github.com/octalmage/robotjs/commit/ec88aa8))\n* Use scancodes instead of virtual keys ([bd93bdf](https://github.com/octalmage/robotjs/commit/bd93bdf))\n* Added parameter to change the speed of mouse in movemouseSmooth ([1fad4c8](https://github.com/octalmage/robotjs/commit/1fad4c8))\n* Fix error with mouse moving in Windows with multiple displays ([45e323a](https://github.com/octalmage/robotjs/commit/45e323a))\n* Migrate to SendInput for toggleMouse ([1c7b96a](https://github.com/octalmage/robotjs/commit/1c7b96a))\n* Upgrade to SendInput for win32KeyEvent ([218e208](https://github.com/octalmage/robotjs/commit/218e208))\n* Migrate to windows-build-tools as a dev dependency for Windows ([e5469fa](https://github.com/octalmage/robotjs/commit/e5469fa))\n* Reduce compilation warnings ([da17c61](https://github.com/octalmage/robotjs/commit/da17c61))\n* Reduce repeated SendInput calls in scroll Mouse ([ee432a6](https://github.com/octalmage/robotjs/commit/ee432a6))\n* Updated node_version 12 ([42e6d28](https://github.com/octalmage/robotjs/commit/42e6d28))\n* node v8 deprecated errors fixed. Added support for node v12.x.x ([77f741f](https://github.com/octalmage/robotjs/commit/77f741f))\n* v8::{Handle => Local} for node 12 compatibility ([9a3b161](https://github.com/octalmage/robotjs/commit/9a3b161))\n\n\n\n## <small>0.5.1 (2018-03-03)</small>\n\n* 0.5.1 ([b9b0ab6](https://github.com/octalmage/robotjs/commit/b9b0ab6))\n\n\n\n## 0.5.0 (2018-03-03)\n\n* 0.5.0 ([0687a34](https://github.com/octalmage/robotjs/commit/0687a34))\n* Another attempt. ([f5f4a60](https://github.com/octalmage/robotjs/commit/f5f4a60))\n* Only build on tag. ([3b8cf98](https://github.com/octalmage/robotjs/commit/3b8cf98))\n* Only run on master. ([91a5397](https://github.com/octalmage/robotjs/commit/91a5397))\n* Pass token through npm. ([b69246c](https://github.com/octalmage/robotjs/commit/b69246c))\n* Should be tag name. ([eb1c889](https://github.com/octalmage/robotjs/commit/eb1c889))\n* Try using defined. ([dd96c25](https://github.com/octalmage/robotjs/commit/dd96c25))\n* Upload Windows build. ([5d92169](https://github.com/octalmage/robotjs/commit/5d92169))\n* Use EQU instead. ([755bcd1](https://github.com/octalmage/robotjs/commit/755bcd1))\n* Use on_success. ([e7dc303](https://github.com/octalmage/robotjs/commit/e7dc303))\n\n\n\n## <small>0.4.8 (2018-02-26)</small>\n\n* - package lock ([34d3653](https://github.com/octalmage/robotjs/commit/34d3653))\n* Add integration tests. ([c29d20d](https://github.com/octalmage/robotjs/commit/c29d20d))\n* Add test for screen. ([eb11444](https://github.com/octalmage/robotjs/commit/eb11444))\n* Added right_control and left_control to key flags as well as right_shift ([33f838d](https://github.com/octalmage/robotjs/commit/33f838d))\n* Allow negative coordinates for mouseMove and dragMouse ([bb63305](https://github.com/octalmage/robotjs/commit/bb63305))\n* Cat for testing. ([02a7f94](https://github.com/octalmage/robotjs/commit/02a7f94))\n* Change to prebuild command. ([895db63](https://github.com/octalmage/robotjs/commit/895db63))\n* Deploy scripts. ([9c26d95](https://github.com/octalmage/robotjs/commit/9c26d95))\n* Finish Jasmine migration. ([98eb2ca](https://github.com/octalmage/robotjs/commit/98eb2ca))\n* Fix API URL ([a2f6da3](https://github.com/octalmage/robotjs/commit/a2f6da3))\n* Fix comment. ([f2bbeab](https://github.com/octalmage/robotjs/commit/f2bbeab))\n* Fix createStringForKey failure on Mac OS for non-Latin languages ([0045e18](https://github.com/octalmage/robotjs/commit/0045e18))\n* Fix for Windows. ([facc34f](https://github.com/octalmage/robotjs/commit/facc34f))\n* Fix scrolling issue. ([d95866f](https://github.com/octalmage/robotjs/commit/d95866f))\n* Fix test for Linux. ([643b925](https://github.com/octalmage/robotjs/commit/643b925))\n* Fixed Linux Y-axis scrolling bug ([798f761](https://github.com/octalmage/robotjs/commit/798f761))\n* Flip x on Windows to match other platforms. ([db15ff7](https://github.com/octalmage/robotjs/commit/db15ff7))\n* horizontal scroll test. ([0ebd39e](https://github.com/octalmage/robotjs/commit/0ebd39e))\n* Increase timeout. ([4c053ea](https://github.com/octalmage/robotjs/commit/4c053ea))\n* Jasmine migration in progress. ([24ed91d](https://github.com/octalmage/robotjs/commit/24ed91d))\n* Link in readme to docs goes straight to website ([64f5400](https://github.com/octalmage/robotjs/commit/64f5400))\n* Move keyboard tests to it's own file. ([37e3165](https://github.com/octalmage/robotjs/commit/37e3165))\n* Remove old header tags. ([da920c2](https://github.com/octalmage/robotjs/commit/da920c2))\n* Remove unsupported Node versions. ([112a9a4](https://github.com/octalmage/robotjs/commit/112a9a4))\n* Remove unsupported Node.js versions for Appveyor. ([452bd6c](https://github.com/octalmage/robotjs/commit/452bd6c))\n* Support additional keys (capslock, numpad-keys, right alt+control). ([55444c3](https://github.com/octalmage/robotjs/commit/55444c3)), closes [#358](https://github.com/octalmage/robotjs/issues/358)\n* Support older versions of Node. ([07d6f0e](https://github.com/octalmage/robotjs/commit/07d6f0e))\n* Test cleanup. ([cc03080](https://github.com/octalmage/robotjs/commit/cc03080))\n* The quotes break tests on windows. ([1f80fb1](https://github.com/octalmage/robotjs/commit/1f80fb1))\n* Update package.json to remove tape. ([6584c95](https://github.com/octalmage/robotjs/commit/6584c95))\n* Update screen file. ([23f9513](https://github.com/octalmage/robotjs/commit/23f9513))\n* Update scroll mouse for 0.4.6 ([cdb0b76](https://github.com/octalmage/robotjs/commit/cdb0b76)), closes [#194](https://github.com/octalmage/robotjs/issues/194)\n* Update target practice. ([092f3c7](https://github.com/octalmage/robotjs/commit/092f3c7))\n* Updated link to docs in under #API ([f7170e5](https://github.com/octalmage/robotjs/commit/f7170e5))\n* Use XSync instead of XFlush everywhere - fixes https://github.com/octalmage/robotjs/issues/347 ([9f3a4c4](https://github.com/octalmage/robotjs/commit/9f3a4c4))\n* Windows scrolling bug ([b4869dd](https://github.com/octalmage/robotjs/commit/b4869dd)), closes [#360](https://github.com/octalmage/robotjs/issues/360)\n\n\n\n## <small>0.4.7 (2017-03-26)</small>\n\n* 0.4.7 ([06dedb4](https://github.com/octalmage/robotjs/commit/06dedb4))\n* Fix trailing comma. ([733caae](https://github.com/octalmage/robotjs/commit/733caae))\n* Use prebuild-install. ([7bd769c](https://github.com/octalmage/robotjs/commit/7bd769c))\n\n\n\n## <small>0.4.6 (2017-03-26)</small>\n\n* 0.4.6 ([d935e41](https://github.com/octalmage/robotjs/commit/d935e41))\n* Accidentally added old scroll code back. ([6f8d2b6](https://github.com/octalmage/robotjs/commit/6f8d2b6))\n* Add \"menu\" key (windows only) ([96220f1](https://github.com/octalmage/robotjs/commit/96220f1))\n* Added left and right control keys ([c1f04e0](https://github.com/octalmage/robotjs/commit/c1f04e0))\n* Bump prebuild. ([ee210f1](https://github.com/octalmage/robotjs/commit/ee210f1))\n* Do not break linux or mac build ([85c5b9f](https://github.com/octalmage/robotjs/commit/85c5b9f))\n* Encode to utf-16 for CGEventKeyboardSetUnicodeString ([ea45808](https://github.com/octalmage/robotjs/commit/ea45808))\n* Fix errors reported by CI on undeclared variables ([daadbcc](https://github.com/octalmage/robotjs/commit/daadbcc))\n* Fixed spacing ([630b198](https://github.com/octalmage/robotjs/commit/630b198))\n* Fixed spacing. ([3ff4434](https://github.com/octalmage/robotjs/commit/3ff4434))\n* Modified the mouseToggle method signature, since according to the documentation the button parameter ([cfac5ab](https://github.com/octalmage/robotjs/commit/cfac5ab))\n* Support Unicode Characters for .typeString() ([7236ff3](https://github.com/octalmage/robotjs/commit/7236ff3))\n\n\n\n## <small>0.4.5 (2016-10-16)</small>\n\n* 0.4.5 ([dfd7e60](https://github.com/octalmage/robotjs/commit/dfd7e60))\n* Add CONTRIBUTING.md. ([76e56a6](https://github.com/octalmage/robotjs/commit/76e56a6))\n* Add f13-24. ([f4854ce](https://github.com/octalmage/robotjs/commit/f4854ce))\n* Add ISSUE_TEMPLATE.md. ([30dfce7](https://github.com/octalmage/robotjs/commit/30dfce7))\n* Add LICENSE.md. ([60b36ca](https://github.com/octalmage/robotjs/commit/60b36ca))\n* Add link to Electron instructions. ([b4adaaa](https://github.com/octalmage/robotjs/commit/b4adaaa))\n* Add spaces before comments. ([a9e12ed](https://github.com/octalmage/robotjs/commit/a9e12ed))\n* Add typescript declaration file ([56b90e0](https://github.com/octalmage/robotjs/commit/56b90e0))\n* Add waffle.io link. ([b4c8108](https://github.com/octalmage/robotjs/commit/b4c8108))\n* better argument parsing ([4501a8b](https://github.com/octalmage/robotjs/commit/4501a8b))\n* change function style, matching rest of code ([9257211](https://github.com/octalmage/robotjs/commit/9257211))\n* Fix merge conflict. ([d6d9e23](https://github.com/octalmage/robotjs/commit/d6d9e23))\n* fix os compatibility ([91cd9ce](https://github.com/octalmage/robotjs/commit/91cd9ce))\n* Fix tests for higher density screens. ([299a66d](https://github.com/octalmage/robotjs/commit/299a66d))\n* Fixed ascii art. ([efa3582](https://github.com/octalmage/robotjs/commit/efa3582))\n* Fixed formatting ([9623852](https://github.com/octalmage/robotjs/commit/9623852))\n* get and set display name for XOpenDisplay() ([cdbf8df](https://github.com/octalmage/robotjs/commit/cdbf8df))\n* improve variable names, move hasDisplayNameChanged reset ([0e457ba](https://github.com/octalmage/robotjs/commit/0e457ba))\n* No saving code for now. ([42a2562](https://github.com/octalmage/robotjs/commit/42a2562))\n* Remove .save. ([4e0f44c](https://github.com/octalmage/robotjs/commit/4e0f44c))\n* Remove bitmap save method. ([f58e519](https://github.com/octalmage/robotjs/commit/f58e519))\n* updated jub3i's code to work with new nan, fixes #153 ([3f3dc7b](https://github.com/octalmage/robotjs/commit/3f3dc7b)), closes [#153](https://github.com/octalmage/robotjs/issues/153)\n* Use a smarter fix for tests. ([f4707f2](https://github.com/octalmage/robotjs/commit/f4707f2))\n\n\n\n## <small>0.4.4 (2016-06-01)</small>\n\n* 0.4.4 ([483b46d](https://github.com/octalmage/robotjs/commit/483b46d))\n* Add right_shift. ([666cc6f](https://github.com/octalmage/robotjs/commit/666cc6f))\n\n\n\n## <small>0.4.3 (2016-05-30)</small>\n\n* 0.4.3 ([3e5f02b](https://github.com/octalmage/robotjs/commit/3e5f02b))\n* Add missing word. ([a76792a](https://github.com/octalmage/robotjs/commit/a76792a))\n* Add numpad 0-9. ([afc4863](https://github.com/octalmage/robotjs/commit/afc4863))\n* Add numpad tests. ([8e24802](https://github.com/octalmage/robotjs/commit/8e24802))\n* Added safety checks. ([ab802ba](https://github.com/octalmage/robotjs/commit/ab802ba))\n* Align AppVeyor build versions with Travis CI. ([d2fc806](https://github.com/octalmage/robotjs/commit/d2fc806))\n* Build on Mac and Linux. ([efd4ec6](https://github.com/octalmage/robotjs/commit/efd4ec6))\n* Changed mouseScroll to use X and Y as direction. ([9917f49](https://github.com/octalmage/robotjs/commit/9917f49))\n* Compile and test our module. ([c688f04](https://github.com/octalmage/robotjs/commit/c688f04))\n* Don't run numpad tests on Linux. ([d7cc60e](https://github.com/octalmage/robotjs/commit/d7cc60e))\n* Fix whitespace. ([041df96](https://github.com/octalmage/robotjs/commit/041df96))\n* Fixed error: ‘for’ loop initial declarations are only allowed in C99 mode ([3f3c5c5](https://github.com/octalmage/robotjs/commit/3f3c5c5))\n* Fixed old variable left over. ([b6eecbf](https://github.com/octalmage/robotjs/commit/b6eecbf))\n* Fixed scroll now using signed ints. ([51082e9](https://github.com/octalmage/robotjs/commit/51082e9))\n* Fixed unused variable warning. ([60021fc](https://github.com/octalmage/robotjs/commit/60021fc))\n* Ignore prebuilds directory. ([4d003a5](https://github.com/octalmage/robotjs/commit/4d003a5))\n* Migrate to SendInput from mouse_event (#181) ([66611b7](https://github.com/octalmage/robotjs/commit/66611b7)), closes [#181](https://github.com/octalmage/robotjs/issues/181)\n* New .travis.yml for Mac testing. ([25e4dcf](https://github.com/octalmage/robotjs/commit/25e4dcf))\n* New screen capture code. ([fdbbff9](https://github.com/octalmage/robotjs/commit/fdbbff9))\n* New sine wave mouse gif. ([6e09549](https://github.com/octalmage/robotjs/commit/6e09549))\n* Not sure how this file got here. ([e22fec6](https://github.com/octalmage/robotjs/commit/e22fec6))\n* Only start Xvfb on Linux. ([00effc9](https://github.com/octalmage/robotjs/commit/00effc9))\n* Readme updates! ([2756b3e](https://github.com/octalmage/robotjs/commit/2756b3e))\n* Release mode and pixEnc. ([58f6c52](https://github.com/octalmage/robotjs/commit/58f6c52))\n* Remove whitespace. ([cea1cae](https://github.com/octalmage/robotjs/commit/cea1cae))\n* Seperate before_install and before_script. ([f0e067a](https://github.com/octalmage/robotjs/commit/f0e067a))\n* Space before comment. ([e2ff6cd](https://github.com/octalmage/robotjs/commit/e2ff6cd))\n* Test that numpad throws on Linux. ([7543df7](https://github.com/octalmage/robotjs/commit/7543df7))\n\n\n\n## <small>0.4.2 (2016-05-08)</small>\n\n* 0.4.2 ([d3226d2](https://github.com/octalmage/robotjs/commit/d3226d2))\n* add insert-key ([b910e7e](https://github.com/octalmage/robotjs/commit/b910e7e))\n* fix spacing ([7720012](https://github.com/octalmage/robotjs/commit/7720012))\n* fixing insert-key for x11 ([3ce729f](https://github.com/octalmage/robotjs/commit/3ce729f))\n* fixing whitespaces caused by editor-settings ([037abf3](https://github.com/octalmage/robotjs/commit/037abf3))\n* Insert isn't a valid key on Mac. ([ac0d16b](https://github.com/octalmage/robotjs/commit/ac0d16b))\n* Migrate to SendInput for Mouse movement ([39f89f0](https://github.com/octalmage/robotjs/commit/39f89f0))\n* Revert \"Test fix for node 0.8 failing.\" ([d5e3668](https://github.com/octalmage/robotjs/commit/d5e3668))\n* Update to nan 2.2.1. ([54c65c3](https://github.com/octalmage/robotjs/commit/54c65c3))\n* Use prebuild. ([7b48ed5](https://github.com/octalmage/robotjs/commit/7b48ed5))\n\n\n\n## <small>0.4.1 (2016-04-17)</small>\n\n* 0.4.1 ([e2b7108](https://github.com/octalmage/robotjs/commit/e2b7108))\n* Add new test to help identify keycode issues. ([3d8ba52](https://github.com/octalmage/robotjs/commit/3d8ba52))\n* Remove keyCodeRequiresSystemDefinedEvent and use new keycodes. ([bfcafa4](https://github.com/octalmage/robotjs/commit/bfcafa4))\n* Use new keycode for multimedia keys. ([7a4d90c](https://github.com/octalmage/robotjs/commit/7a4d90c))\n\n\n\n## 0.4.0 (2016-04-17)\n\n* 0 is the image, 1 is x, and 2 is y. :| ([c411267](https://github.com/octalmage/robotjs/commit/c411267))\n* 0 is the image, 1 is x, and 2 is y. :| ([c41f382](https://github.com/octalmage/robotjs/commit/c41f382))\n* 0.4.0 ([9f3818e](https://github.com/octalmage/robotjs/commit/9f3818e))\n* Add notifications to TravisCI builds. ([c8cfc1c](https://github.com/octalmage/robotjs/commit/c8cfc1c))\n* Add tests for bitmap. ([ab29b45](https://github.com/octalmage/robotjs/commit/ab29b45))\n* Added BMP class and buildBMP. ([bcac2ac](https://github.com/octalmage/robotjs/commit/bcac2ac))\n* Added KEYEVENTF_EXTENDEDKEY flag when extended keys are triggered on win32 ([8e0cc92](https://github.com/octalmage/robotjs/commit/8e0cc92))\n* Added KEYEVENTF_EXTENDEDKEY flag when extended keys are triggered on win32 ([081978e](https://github.com/octalmage/robotjs/commit/081978e))\n* Added typeStringDelayed. ([45afde6](https://github.com/octalmage/robotjs/commit/45afde6))\n* Allow x, y, width, and height to be passed. ([bb2ab38](https://github.com/octalmage/robotjs/commit/bb2ab38))\n* Argument checking for getPixelColor. ([4a5572f](https://github.com/octalmage/robotjs/commit/4a5572f))\n* Better error handling for dragMouse. ([93417d3](https://github.com/octalmage/robotjs/commit/93417d3))\n* Better error handling for moveMouse. ([dd6e658](https://github.com/octalmage/robotjs/commit/dd6e658))\n* Better error handling for moveMouseSmooth. ([2e221ba](https://github.com/octalmage/robotjs/commit/2e221ba))\n* Better tests for getPixelColor. ([d8cbb74](https://github.com/octalmage/robotjs/commit/d8cbb74))\n* Better tests for moveMouse. ([0f09623](https://github.com/octalmage/robotjs/commit/0f09623))\n* Call captureScreen with arguments if passed. ([da59926](https://github.com/octalmage/robotjs/commit/da59926))\n* Convert the rest of the spaces. ([f653b92](https://github.com/octalmage/robotjs/commit/f653b92))\n* Correct buffer size. ([cb76f5e](https://github.com/octalmage/robotjs/commit/cb76f5e))\n* Create calculateDeltas function and use it in dragMouse. ([7a199d4](https://github.com/octalmage/robotjs/commit/7a199d4))\n* Create calculateDeltas function and use it in dragMouse. ([ad713f0](https://github.com/octalmage/robotjs/commit/ad713f0))\n* Define colorAt at declaration. ([03759cb](https://github.com/octalmage/robotjs/commit/03759cb))\n* fix scroll mouse on windows XP, see #97, C89 (2008) variable declaration on top of function ([99a96e0](https://github.com/octalmage/robotjs/commit/99a96e0)), closes [#97](https://github.com/octalmage/robotjs/issues/97)\n* Make sure coords are in bounds. ([b1f2852](https://github.com/octalmage/robotjs/commit/b1f2852))\n* Map virtual key before triggering keybd_event ([c47f8b0](https://github.com/octalmage/robotjs/commit/c47f8b0))\n* Only define calculateDeltas for Mac. ([3eef8cb](https://github.com/octalmage/robotjs/commit/3eef8cb))\n* Only define calculateDeltas for Mac. ([f7fac28](https://github.com/octalmage/robotjs/commit/f7fac28))\n* Remove extra spaces. ([2ad647b](https://github.com/octalmage/robotjs/commit/2ad647b))\n* Remove unnecessary space. ([a764c66](https://github.com/octalmage/robotjs/commit/a764c66))\n* Remove unused code. ([6539910](https://github.com/octalmage/robotjs/commit/6539910))\n* Run keyboard tests. ([4d2c20d](https://github.com/octalmage/robotjs/commit/4d2c20d))\n* Starting tests for keyTap. ([c9063ca](https://github.com/octalmage/robotjs/commit/c9063ca))\n* Starting to implement bitmap sharing. ([b58962f](https://github.com/octalmage/robotjs/commit/b58962f))\n* Starts at 0. ([0cd6db2](https://github.com/octalmage/robotjs/commit/0cd6db2))\n* Test fix for node 0.8 failing. ([eb18a1f](https://github.com/octalmage/robotjs/commit/eb18a1f))\n* Tests for dragMouse. ([dfa83f6](https://github.com/octalmage/robotjs/commit/dfa83f6))\n* Tests for mouseClick. ([b0cfad0](https://github.com/octalmage/robotjs/commit/b0cfad0))\n* Tests for moveMouseSmooth. ([ad7e5dc](https://github.com/octalmage/robotjs/commit/ad7e5dc))\n* This commit fixes #148: Volume keys don't work on Mac. ([163feb5](https://github.com/octalmage/robotjs/commit/163feb5)), closes [#148](https://github.com/octalmage/robotjs/issues/148)\n* Throw catchable error if requested pixels are outside the bitmap's dimensions. ([d65576b](https://github.com/octalmage/robotjs/commit/d65576b))\n* Un-mix up the deltaX and deltaY. ([e3f084e](https://github.com/octalmage/robotjs/commit/e3f084e))\n* Un-mix up the deltaX and deltaY. ([1cf70f1](https://github.com/octalmage/robotjs/commit/1cf70f1))\n* Update keypress.c ([faddf9c](https://github.com/octalmage/robotjs/commit/faddf9c))\n* Updated TravisCI and Appveyor configs to allow 0.8.x failures. ([38f85c0](https://github.com/octalmage/robotjs/commit/38f85c0))\n* Use tabs instead of spaces. ([8ecd518](https://github.com/octalmage/robotjs/commit/8ecd518))\n* Use tabs instead of spaces. ([47b901f](https://github.com/octalmage/robotjs/commit/47b901f))\n* Workaround for games not detecting mouse moves. ([9e3a067](https://github.com/octalmage/robotjs/commit/9e3a067))\n* Workaround for games not detecting mouse moves. ([6fd31a8](https://github.com/octalmage/robotjs/commit/6fd31a8))\n* TODO: Need tests for mouseToggle, and scrollMouse. ([648fff4](https://github.com/octalmage/robotjs/commit/648fff4))\n\n\n\n## <small>0.3.7 (2015-12-29)</small>\n\n* 0.3.7 ([4843f0b](https://github.com/octalmage/robotjs/commit/4843f0b))\n* Fixed F9 key ([48c432f](https://github.com/octalmage/robotjs/commit/48c432f))\n* Keep code style consistent. ([69ab661](https://github.com/octalmage/robotjs/commit/69ab661))\n* Screen Capture Fix. ([bcec0bc](https://github.com/octalmage/robotjs/commit/bcec0bc))\n* Updated comments to match code style. ([d5096fe](https://github.com/octalmage/robotjs/commit/d5096fe))\n* Use 9999 instead of -1 because CGKeyCode is an unsigned int. ([64e0ecf](https://github.com/octalmage/robotjs/commit/64e0ecf)), closes [#106](https://github.com/octalmage/robotjs/issues/106)\n\n\n\n## <small>0.3.6 (2015-12-14)</small>\n\n* 0.3.6 ([1a3df83](https://github.com/octalmage/robotjs/commit/1a3df83))\n\n\n\n## <small>0.3.5 (2015-12-07)</small>\n\n* 0.3.5 ([3e9d533](https://github.com/octalmage/robotjs/commit/3e9d533))\n* Link to relevant wiki sections. ([727313b](https://github.com/octalmage/robotjs/commit/727313b))\n* Prevent unused variable warning for Mac and Linux. ([c3edbc2](https://github.com/octalmage/robotjs/commit/c3edbc2))\n* Update README URLs based on HTTP redirects ([2072907](https://github.com/octalmage/robotjs/commit/2072907))\n\n\n\n## <small>0.3.4 (2015-11-22)</small>\n\n* 0.3.4 ([4b28b99](https://github.com/octalmage/robotjs/commit/4b28b99))\n* Add FAQ section. ([ee0fe6d](https://github.com/octalmage/robotjs/commit/ee0fe6d))\n* Add link to create a new issue. ([1e740ec](https://github.com/octalmage/robotjs/commit/1e740ec))\n* Add multi-monitor question to FAQ. ([9592dc9](https://github.com/octalmage/robotjs/commit/9592dc9))\n* Added dragMouse function ([406acfb](https://github.com/octalmage/robotjs/commit/406acfb)), closes [#127](https://github.com/octalmage/robotjs/issues/127)\n* Added support for arrays of key flags ([c2e62d6](https://github.com/octalmage/robotjs/commit/c2e62d6))\n* Center badges. ([a264ed8](https://github.com/octalmage/robotjs/commit/a264ed8))\n* Center logo. ([b615176](https://github.com/octalmage/robotjs/commit/b615176))\n* Change wording a bit. ([721bb8d](https://github.com/octalmage/robotjs/commit/721bb8d))\n* Link to Twitter and blog. ([5fd2c51](https://github.com/octalmage/robotjs/commit/5fd2c51))\n* Made compatible with older versions of Windows. ([d99f3bb](https://github.com/octalmage/robotjs/commit/d99f3bb))\n* Passing constant instead of zero for mouseButton on mouse move ([78603ff](https://github.com/octalmage/robotjs/commit/78603ff))\n* Remove blog section. ([9edeefc](https://github.com/octalmage/robotjs/commit/9edeefc))\n* Use new blog URL. ([f0a6f2f](https://github.com/octalmage/robotjs/commit/f0a6f2f))\n* Using v8::Handle instead of v8::Local for older versions of Node ([e24ee51](https://github.com/octalmage/robotjs/commit/e24ee51))\n\n\n\n## <small>0.3.3 (2015-11-02)</small>\n\n* 0.3.3 ([eeb7633](https://github.com/octalmage/robotjs/commit/eeb7633))\n* Add some new keywords. ([dd59242](https://github.com/octalmage/robotjs/commit/dd59242))\n* Fix for #118. ([570f453](https://github.com/octalmage/robotjs/commit/570f453)), closes [#118](https://github.com/octalmage/robotjs/issues/118)\n\n\n\n## <small>0.3.2 (2015-10-28)</small>\n\n* 0.3.2 ([9d5d2e1](https://github.com/octalmage/robotjs/commit/9d5d2e1))\n* Add AppVeyor badge. ([7a89376](https://github.com/octalmage/robotjs/commit/7a89376))\n* Add desktop keyword. ([b1bc0ef](https://github.com/octalmage/robotjs/commit/b1bc0ef))\n* Added special multimedia keycodes + restructured code ([ef4f36d](https://github.com/octalmage/robotjs/commit/ef4f36d))\n* Another attempt to fix the accuracy. ([637feed](https://github.com/octalmage/robotjs/commit/637feed))\n* Basic appveyor.yml. ([cfc1f78](https://github.com/octalmage/robotjs/commit/cfc1f78))\n* Dynamic nan include. ([af0a9f1](https://github.com/octalmage/robotjs/commit/af0a9f1))\n* Fix several differences ([523d8e1](https://github.com/octalmage/robotjs/commit/523d8e1))\n* Fixed an issue with 'typeString' under Windows. When converting a char to a keycode we now pull the  ([ae58913](https://github.com/octalmage/robotjs/commit/ae58913))\n* Fixed the API link ([d5d7c5c](https://github.com/octalmage/robotjs/commit/d5d7c5c))\n* Improve conversion accuracy. ([b0258ad](https://github.com/octalmage/robotjs/commit/b0258ad))\n* Increase delay to help test reliability. ([66b31b6](https://github.com/octalmage/robotjs/commit/66b31b6))\n* Log currentPos to help debug. ([84edc0c](https://github.com/octalmage/robotjs/commit/84edc0c))\n* Log mouse positions to help debug. ([49cc03f](https://github.com/octalmage/robotjs/commit/49cc03f))\n* New mouse example! ([7121302](https://github.com/octalmage/robotjs/commit/7121302))\n* Remove extra newline between badges. ([db69a4f](https://github.com/octalmage/robotjs/commit/db69a4f))\n* Remove logging. ([60c86d1](https://github.com/octalmage/robotjs/commit/60c86d1))\n* Remove more newlines. ([0522faf](https://github.com/octalmage/robotjs/commit/0522faf))\n* Remove newline after example descriptions. ([a1048b9](https://github.com/octalmage/robotjs/commit/a1048b9))\n* Remove Waffle.io badge. ([3d193ce](https://github.com/octalmage/robotjs/commit/3d193ce))\n* Reorganize badges. ([53adde4](https://github.com/octalmage/robotjs/commit/53adde4))\n* Test the latest 4.x.x Node.js version. ([716d3a2](https://github.com/octalmage/robotjs/commit/716d3a2))\n\n\n\n## <small>0.3.1 (2015-10-12)</small>\n\n* 0.3.1 ([6ecb2f1](https://github.com/octalmage/robotjs/commit/6ecb2f1))\n* Add some comments. ([07a6b11](https://github.com/octalmage/robotjs/commit/07a6b11))\n* Allow up or down for second argument. ([718c822](https://github.com/octalmage/robotjs/commit/718c822))\n* Bitmap header! ([13cf060](https://github.com/octalmage/robotjs/commit/13cf060))\n* Change color to colorAt. ([b413365](https://github.com/octalmage/robotjs/commit/b413365))\n* Create and use padHex function. ([547ba93](https://github.com/octalmage/robotjs/commit/547ba93))\n* Create imageBuffer manually. ([2425d4a](https://github.com/octalmage/robotjs/commit/2425d4a))\n* Destroy bitmap after getting hex. ([e0f616a](https://github.com/octalmage/robotjs/commit/e0f616a))\n* Export captureScreen to Node.js. ([5dc3557](https://github.com/octalmage/robotjs/commit/5dc3557))\n* Export getColor. ([c27054e](https://github.com/octalmage/robotjs/commit/c27054e))\n* Fix key state error. ([1526cf3](https://github.com/octalmage/robotjs/commit/1526cf3))\n* Fix mouseToggle ([446d40f](https://github.com/octalmage/robotjs/commit/446d40f))\n* Get the correct argument. ([b39d416](https://github.com/octalmage/robotjs/commit/b39d416))\n* Get the correct arguments. ([752d2b9](https://github.com/octalmage/robotjs/commit/752d2b9))\n* Implemented captureScreen. #13 ([ec4f953](https://github.com/octalmage/robotjs/commit/ec4f953)), closes [#13](https://github.com/octalmage/robotjs/issues/13)\n* More accurate types. ([e28cb2a](https://github.com/octalmage/robotjs/commit/e28cb2a))\n* No spaces before comment. ([5e81b63](https://github.com/octalmage/robotjs/commit/5e81b63))\n* Pass the buffer to createMMBitmap. ([edf9412](https://github.com/octalmage/robotjs/commit/edf9412))\n* Pass x and y in with the bitmap object. ([ea719a2](https://github.com/octalmage/robotjs/commit/ea719a2))\n* Re-arrange order. ([96d8547](https://github.com/octalmage/robotjs/commit/96d8547))\n* Remove unnecessary printf. ([fc9df2a](https://github.com/octalmage/robotjs/commit/fc9df2a))\n* RobotJS type. ([634259e](https://github.com/octalmage/robotjs/commit/634259e))\n* Started getColor function. ([df737d1](https://github.com/octalmage/robotjs/commit/df737d1))\n* Use nan to define strings. ([d2faaf0](https://github.com/octalmage/robotjs/commit/d2faaf0))\n* Use Nan::Utf8String ([f114464](https://github.com/octalmage/robotjs/commit/f114464))\n* Use x and y. ([54a9cae](https://github.com/octalmage/robotjs/commit/54a9cae))\n\n\n\n## 0.3.0 (2015-10-04)\n\n* __MMMouseWheelDirection is the same on all OSes, so we only need to define it once. ([534b4b6](https://github.com/octalmage/robotjs/commit/534b4b6))\n* 0.3.0 ([679989c](https://github.com/octalmage/robotjs/commit/679989c))\n* Accept \"up\" or \"down\" for direction. ([a3346ab](https://github.com/octalmage/robotjs/commit/a3346ab))\n* Added header for doubleClick function. ([47de9e1](https://github.com/octalmage/robotjs/commit/47de9e1))\n* Added header to moveMouse. ([be280d8](https://github.com/octalmage/robotjs/commit/be280d8))\n* Added header to toggleMouse. ([5501e7e](https://github.com/octalmage/robotjs/commit/5501e7e))\n* Added link to the blog. ([e2d20c8](https://github.com/octalmage/robotjs/commit/e2d20c8))\n* Added microsleep to scrollMouse. ([351176b](https://github.com/octalmage/robotjs/commit/351176b))\n* Added missing period. ([be33adc](https://github.com/octalmage/robotjs/commit/be33adc))\n* Added printscreen to CheckKeyCodes. ([ae004c8](https://github.com/octalmage/robotjs/commit/ae004c8))\n* Added VK_SNAPSHOT to accessible keycodes on Windows. ([61b0584](https://github.com/octalmage/robotjs/commit/61b0584))\n* Changed getting started to examples ([cf7c392](https://github.com/octalmage/robotjs/commit/cf7c392))\n* Commented this code out for now. ([062be3f](https://github.com/octalmage/robotjs/commit/062be3f)), closes [#50](https://github.com/octalmage/robotjs/issues/50)\n* Fixed Examples link. ([21ce43c](https://github.com/octalmage/robotjs/commit/21ce43c))\n* Fixed TravisCI config. ([25590a2](https://github.com/octalmage/robotjs/commit/25590a2))\n* printscreen is only supported on Windows. ([09d8b21](https://github.com/octalmage/robotjs/commit/09d8b21))\n* Reorganize readme ([b009e13](https://github.com/octalmage/robotjs/commit/b009e13))\n* uncomment line 43 ([a65488c](https://github.com/octalmage/robotjs/commit/a65488c))\n* Update README.md ([0dc62ab](https://github.com/octalmage/robotjs/commit/0dc62ab))\n* Updated package.json to use nan 2.0.9. ([945663f](https://github.com/octalmage/robotjs/commit/945663f))\n* updated robotjs to compile with nan-2.0.9 ([811a813](https://github.com/octalmage/robotjs/commit/811a813))\n* updating travis config file ([11beae0](https://github.com/octalmage/robotjs/commit/11beae0))\n* Use nan macro and fix conversion for Windows. ([e8da392](https://github.com/octalmage/robotjs/commit/e8da392))\n\n\n\n## <small>0.2.4 (2015-08-28)</small>\n\n* 0.2.4 ([94bdb16](https://github.com/octalmage/robotjs/commit/94bdb16))\n* Define MMMouseWheelDirection for Mac. ([1725c10](https://github.com/octalmage/robotjs/commit/1725c10))\n* Implemented scrollMouse for Mac. ([ded851c](https://github.com/octalmage/robotjs/commit/ded851c))\n* Indent the description. ([630822c](https://github.com/octalmage/robotjs/commit/630822c))\n* Linux scrollMouse implementation. ([0403623](https://github.com/octalmage/robotjs/commit/0403623))\n* Make global delays configurable. #14 ([dc52bc2](https://github.com/octalmage/robotjs/commit/dc52bc2)), closes [#14](https://github.com/octalmage/robotjs/issues/14)\n* MMMouseWheelDirection for Linux. ([58ff736](https://github.com/octalmage/robotjs/commit/58ff736))\n* Needed for abs(); ([13a5e15](https://github.com/octalmage/robotjs/commit/13a5e15))\n* Show build status for master branch. ([ce6274f](https://github.com/octalmage/robotjs/commit/ce6274f))\n* Use global delays for mouse and keyboard functions. ([3cbfc50](https://github.com/octalmage/robotjs/commit/3cbfc50))\n\n\n\n## <small>0.2.3 (2015-08-05)</small>\n\n* 0.2.3 ([b6326bc](https://github.com/octalmage/robotjs/commit/b6326bc))\n* Added detailed install instructions. ([2e9fc20](https://github.com/octalmage/robotjs/commit/2e9fc20))\n* Added doubleClick function. ([f1afaab](https://github.com/octalmage/robotjs/commit/f1afaab))\n* Better example titles. ([5711f7b](https://github.com/octalmage/robotjs/commit/5711f7b))\n* Fixed mouseClick bug on Windows. ([291890c](https://github.com/octalmage/robotjs/commit/291890c))\n* io.js v3 is not supported. ([efb0fd7](https://github.com/octalmage/robotjs/commit/efb0fd7))\n* Removed the Windows specific double click. ([d2e33e1](https://github.com/octalmage/robotjs/commit/d2e33e1))\n* Sleep for 200 milliseconds instead of 500. ([6e56e16](https://github.com/octalmage/robotjs/commit/6e56e16))\n* Updated mouseClick to support double clicks. ([b5db4cb](https://github.com/octalmage/robotjs/commit/b5db4cb))\n\n\n\n## <small>0.2.2 (2015-08-01)</small>\n\n* 0.2.2 ([ec1fd14](https://github.com/octalmage/robotjs/commit/ec1fd14))\n* Add spaces around assignment operator. ([c1e62de](https://github.com/octalmage/robotjs/commit/c1e62de))\n* Added whitespace ([4159921](https://github.com/octalmage/robotjs/commit/4159921))\n* Change \"specially\" to \"especially\" ([bc1c330](https://github.com/octalmage/robotjs/commit/bc1c330))\n* Changed readme wording. ([c2f49a7](https://github.com/octalmage/robotjs/commit/c2f49a7))\n* Fix for #15 ([e7983b7](https://github.com/octalmage/robotjs/commit/e7983b7)), closes [#15](https://github.com/octalmage/robotjs/issues/15)\n* Fixed minor typo ([673ea16](https://github.com/octalmage/robotjs/commit/673ea16))\n* MouseScroll feature addition ([4052946](https://github.com/octalmage/robotjs/commit/4052946))\n* Something like this :cheers: ([c632f78](https://github.com/octalmage/robotjs/commit/c632f78))\n* Update README.md ([38ec22c](https://github.com/octalmage/robotjs/commit/38ec22c))\n* Update README.md ([34421b4](https://github.com/octalmage/robotjs/commit/34421b4))\n* Update README.md ([2f67943](https://github.com/octalmage/robotjs/commit/2f67943))\n* Update README.md ([1f2dd31](https://github.com/octalmage/robotjs/commit/1f2dd31))\n* Update robotjs.cc ([fb4198c](https://github.com/octalmage/robotjs/commit/fb4198c))\n\n\n\n## <small>0.2.1 (2015-07-26)</small>\n\n* 0.2.1 ([b0dc3bc](https://github.com/octalmage/robotjs/commit/b0dc3bc))\n* Confirm that the mouse is in the correct location. ([8081dd1](https://github.com/octalmage/robotjs/commit/8081dd1))\n* Move the mouse in the moveMouse test. ([cf93363](https://github.com/octalmage/robotjs/commit/cf93363))\n* Sleep after mouse events. #14 ([939518a](https://github.com/octalmage/robotjs/commit/939518a)), closes [#14](https://github.com/octalmage/robotjs/issues/14)\n* Test moving the mouse. ([3043db2](https://github.com/octalmage/robotjs/commit/3043db2))\n\n\n\n## 0.2.0 (2015-07-25)\n\n* 0.2.0 ([cac32ba](https://github.com/octalmage/robotjs/commit/cac32ba))\n* Add Windows support. ([bcc8164](https://github.com/octalmage/robotjs/commit/bcc8164))\n* Added link to list of projects. ([491f675](https://github.com/octalmage/robotjs/commit/491f675))\n* Fixed formatting. ([7222506](https://github.com/octalmage/robotjs/commit/7222506))\n* getPixelColor bug fix. ([20c9a19](https://github.com/octalmage/robotjs/commit/20c9a19))\n* Include the portable snprintf. ([fc4016d](https://github.com/octalmage/robotjs/commit/fc4016d))\n* Line up arguments. ([056b6b1](https://github.com/octalmage/robotjs/commit/056b6b1))\n* Make the portable snprintf C++ compatible. ([5e1dadf](https://github.com/octalmage/robotjs/commit/5e1dadf))\n* No ms_stdint.h. ([12c1ff7](https://github.com/octalmage/robotjs/commit/12c1ff7))\n* Remove sponsor line. ([547aa0c](https://github.com/octalmage/robotjs/commit/547aa0c))\n* RobotJS is now cross platform! ([6561826](https://github.com/octalmage/robotjs/commit/6561826))\n* Run the screen tests. ([64e9e4a](https://github.com/octalmage/robotjs/commit/64e9e4a))\n* Tests for getPixelColor and getScreenSize. ([790c3c6](https://github.com/octalmage/robotjs/commit/790c3c6))\n* These throw errors, need to come back to this. ([5bc2eb2](https://github.com/octalmage/robotjs/commit/5bc2eb2))\n* Typecast from int to double. ([8730e94](https://github.com/octalmage/robotjs/commit/8730e94))\n* Unnecessary and causes warnings. ([25c3b84](https://github.com/octalmage/robotjs/commit/25c3b84))\n* Updated to match code style. ([1208cf8](https://github.com/octalmage/robotjs/commit/1208cf8))\n* Use cross platform microsleep instead of mssleep. ([a511724](https://github.com/octalmage/robotjs/commit/a511724))\n\n\n\n## <small>0.1.4 (2015-07-19)</small>\n\n* 0.1.4 ([c982f0a](https://github.com/octalmage/robotjs/commit/c982f0a))\n* Added space to keycode list. ([523cf3b](https://github.com/octalmage/robotjs/commit/523cf3b))\n* Added space to keyTap and keyToggle. ([589ca78](https://github.com/octalmage/robotjs/commit/589ca78))\n* Corrected spacebar keycode. ([e935825](https://github.com/octalmage/robotjs/commit/e935825))\n* Fixed buffer overflow. Closes #19. ([1da780f](https://github.com/octalmage/robotjs/commit/1da780f)), closes [#19](https://github.com/octalmage/robotjs/issues/19)\n* Updating description. ([e2541ef](https://github.com/octalmage/robotjs/commit/e2541ef))\n\n\n\n## <small>0.1.3 (2015-07-18)</small>\n\n* 0.1.3 ([71dceb0](https://github.com/octalmage/robotjs/commit/71dceb0))\n* add framework to accept modifiers on keys. set up keytoggle. ([527f9d9](https://github.com/octalmage/robotjs/commit/527f9d9))\n* Added \"maintained by\" line. ([fcd4c98](https://github.com/octalmage/robotjs/commit/fcd4c98))\n* Added missing periods. ([431abe5](https://github.com/octalmage/robotjs/commit/431abe5))\n* Added note about a sponsor. ([10a171e](https://github.com/octalmage/robotjs/commit/10a171e))\n* Added space before License header. ([eb64d14](https://github.com/octalmage/robotjs/commit/eb64d14))\n* Changed wording to hint that Linux is supported. ([cd38d0e](https://github.com/octalmage/robotjs/commit/cd38d0e))\n* Fixed code style. ([36c5297](https://github.com/octalmage/robotjs/commit/36c5297))\n* Fixed style of keyToggle. ([0ba1797](https://github.com/octalmage/robotjs/commit/0ba1797))\n* fixed typo ([2a42cc3](https://github.com/octalmage/robotjs/commit/2a42cc3))\n* Fixed typo ([c2f6ee0](https://github.com/octalmage/robotjs/commit/c2f6ee0))\n* fixes keyTap not releasing. allows for multiple keypresses or keytaps in a row by adding sleep funct ([47ff4a2](https://github.com/octalmage/robotjs/commit/47ff4a2))\n* get keytap working for cmd-tab, started on keytoggle ([ebd2bb4](https://github.com/octalmage/robotjs/commit/ebd2bb4))\n* More spaces to tabs. ([d3f03d0](https://github.com/octalmage/robotjs/commit/d3f03d0))\n* removed wiki link ([8c221d9](https://github.com/octalmage/robotjs/commit/8c221d9))\n* Spaces to tabs. ([72a4e94](https://github.com/octalmage/robotjs/commit/72a4e94))\n* Switched to tab. ([16cfd6c](https://github.com/octalmage/robotjs/commit/16cfd6c))\n* Update README.md ([b9c57a4](https://github.com/octalmage/robotjs/commit/b9c57a4))\n* Updated readme with new description. ([41b762c](https://github.com/octalmage/robotjs/commit/41b762c))\n* Updating progress chart for keyboard. ([6e89410](https://github.com/octalmage/robotjs/commit/6e89410))\n\n\n\n## <small>0.1.2 (2015-04-26)</small>\n\n* 0.1.2 ([82df310](https://github.com/octalmage/robotjs/commit/82df310))\n* A travis configuration that I reckon will work (including xvfb) ([9246dac](https://github.com/octalmage/robotjs/commit/9246dac))\n* Add required apt packages ([779d238](https://github.com/octalmage/robotjs/commit/779d238))\n* Add xdisplay to the compilation list for linux ([36d1951](https://github.com/octalmage/robotjs/commit/36d1951))\n* Added build status. ([30f753d](https://github.com/octalmage/robotjs/commit/30f753d))\n* Added compiler warnings as per autopy ([b04a3bf](https://github.com/octalmage/robotjs/commit/b04a3bf))\n* Added getScreenSize, closes #25. ([d94ca48](https://github.com/octalmage/robotjs/commit/d94ca48)), closes [#25](https://github.com/octalmage/robotjs/issues/25)\n* Added link to wiki. ([7ad2129](https://github.com/octalmage/robotjs/commit/7ad2129))\n* Added linux link settings ([7e705e2](https://github.com/octalmage/robotjs/commit/7e705e2))\n* Added Tab key to keyTap ([d1e7b01](https://github.com/octalmage/robotjs/commit/d1e7b01))\n* MIT bitches. ([f415f54](https://github.com/octalmage/robotjs/commit/f415f54))\n* pandering to my OCD - sorry :/ ([099a21d](https://github.com/octalmage/robotjs/commit/099a21d))\n* Reformat to use single quotes as that seems to be the standard for gyp ([b70a67c](https://github.com/octalmage/robotjs/commit/b70a67c))\n* shadow too noisy when working with V8 ([4cdeb86](https://github.com/octalmage/robotjs/commit/4cdeb86))\n* Some basic tests to get the ball rolling ([86714bf](https://github.com/octalmage/robotjs/commit/86714bf))\n* This was suppose to be MIT. ([aca285b](https://github.com/octalmage/robotjs/commit/aca285b))\n* Updated progress chart. ([1da2e14](https://github.com/octalmage/robotjs/commit/1da2e14))\n* Use tape for testing ([4f4b043](https://github.com/octalmage/robotjs/commit/4f4b043))\n\n\n\n## <small>0.1.1 (2015-02-06)</small>\n\n* Added additional keys for keyTap. ([1895133](https://github.com/octalmage/robotjs/commit/1895133))\n* Fixed extern location. ([581cbdc](https://github.com/octalmage/robotjs/commit/581cbdc))\n* Updated keyTap example. ([7d056e8](https://github.com/octalmage/robotjs/commit/7d056e8))\n* Updated keyTap for correct use. Closes #3. ([ec8adb9](https://github.com/octalmage/robotjs/commit/ec8adb9)), closes [#3](https://github.com/octalmage/robotjs/issues/3)\n* Updated to version 0.1.1. ([764dd11](https://github.com/octalmage/robotjs/commit/764dd11))\n\n\n\n## 0.1.0 (2015-02-04)\n\n* Add periods to all errors. ([b11648a](https://github.com/octalmage/robotjs/commit/b11648a))\n* Added mouseToggle, closes #8. ([3f4918b](https://github.com/octalmage/robotjs/commit/3f4918b)), closes [#8](https://github.com/octalmage/robotjs/issues/8)\n* Added right/middle click support, closes #6. ([2fc100a](https://github.com/octalmage/robotjs/commit/2fc100a)), closes [#6](https://github.com/octalmage/robotjs/issues/6)\n* Changed button variable name. ([6113ec5](https://github.com/octalmage/robotjs/commit/6113ec5))\n* Uniform syntax. ([ca191c1](https://github.com/octalmage/robotjs/commit/ca191c1))\n* Version 0.1.0. ([245a862](https://github.com/octalmage/robotjs/commit/245a862))\n\n\n\n## <small>0.0.5 (2015-02-04)</small>\n\n* Added additional examples. ([ba5cb80](https://github.com/octalmage/robotjs/commit/ba5cb80))\n* Added getPixelColor. ([f250355](https://github.com/octalmage/robotjs/commit/f250355))\n* Added more badges. ([39ef2f7](https://github.com/octalmage/robotjs/commit/39ef2f7))\n* Added mouseMoveSmooth. ([ecdd16a](https://github.com/octalmage/robotjs/commit/ecdd16a))\n* Added waffle.io badge! ([1488a75](https://github.com/octalmage/robotjs/commit/1488a75))\n* Converted mouseClick function. ([87fe58e](https://github.com/octalmage/robotjs/commit/87fe58e))\n* Converted rest of code to nan. ([d44b1a2](https://github.com/octalmage/robotjs/commit/d44b1a2))\n* Converted typeString to use nan. Closes #1. ([ea62efa](https://github.com/octalmage/robotjs/commit/ea62efa)), closes [#1](https://github.com/octalmage/robotjs/issues/1)\n* Fixed code style. ([de175e7](https://github.com/octalmage/robotjs/commit/de175e7))\n* Fixed heading syntax. ([d2102c8](https://github.com/octalmage/robotjs/commit/d2102c8))\n* Fixed plans. ([17d7f24](https://github.com/octalmage/robotjs/commit/17d7f24))\n* Include screen related code. ([ea69110](https://github.com/octalmage/robotjs/commit/ea69110))\n* Link to syntax issue. ([9b8b765](https://github.com/octalmage/robotjs/commit/9b8b765))\n* Linked to nan issue. ([9f7cdba](https://github.com/octalmage/robotjs/commit/9f7cdba))\n* nan rewrite done! ([f217e9a](https://github.com/octalmage/robotjs/commit/f217e9a))\n* Removed node_modules. ([b4f2085](https://github.com/octalmage/robotjs/commit/b4f2085))\n* Removed unused functions. ([c9509bf](https://github.com/octalmage/robotjs/commit/c9509bf))\n* Removed unused helper functions. ([6f60b53](https://github.com/octalmage/robotjs/commit/6f60b53))\n* Updated to be compatible with C++. ([cacd8f9](https://github.com/octalmage/robotjs/commit/cacd8f9))\n* Updated version to 0.0.4. ([9f97237](https://github.com/octalmage/robotjs/commit/9f97237))\n* Updated version to 0.0.5. ([e981548](https://github.com/octalmage/robotjs/commit/e981548))\n\n\n\n## <small>0.0.3 (2015-01-20)</small>\n\n* Added build instructions. ([4a9e8c6](https://github.com/octalmage/robotjs/commit/4a9e8c6))\n* Added keyTap and typeString, started on bitmap. ([8cb04e5](https://github.com/octalmage/robotjs/commit/8cb04e5))\n* Added more details about plans. ([97ead45](https://github.com/octalmage/robotjs/commit/97ead45))\n* Added nan module. ([bcf1111](https://github.com/octalmage/robotjs/commit/bcf1111))\n* Added note about progress. ([c5da2d2](https://github.com/octalmage/robotjs/commit/c5da2d2))\n* Added story section. ([0ff5407](https://github.com/octalmage/robotjs/commit/0ff5407))\n* Added Window module to progress table. ([b9da81d](https://github.com/octalmage/robotjs/commit/b9da81d))\n* Cleaning up the code, moving window code to new branch. ([16f67af](https://github.com/octalmage/robotjs/commit/16f67af))\n* Excluding build/ ([5ba0e11](https://github.com/octalmage/robotjs/commit/5ba0e11))\n* Experimenting with Window manipulation. ([ea34961](https://github.com/octalmage/robotjs/commit/ea34961))\n* Including nan. ([1f407d8](https://github.com/octalmage/robotjs/commit/1f407d8))\n* Moved screen related functions to it's own branch. ([2b4564d](https://github.com/octalmage/robotjs/commit/2b4564d))\n* New release. ([0361eb4](https://github.com/octalmage/robotjs/commit/0361eb4))\n* Removed screen related files from build. ([919fc80](https://github.com/octalmage/robotjs/commit/919fc80))\n* Started converting to nan. ([19261d7](https://github.com/octalmage/robotjs/commit/19261d7))\n* Update README.md ([cbeb5af](https://github.com/octalmage/robotjs/commit/cbeb5af))\n* Updated dependencies to include nan. ([106788b](https://github.com/octalmage/robotjs/commit/106788b))\n* Updated install instructions. ([1d1d841](https://github.com/octalmage/robotjs/commit/1d1d841))\n* Updated package version. ([d9b1d98](https://github.com/octalmage/robotjs/commit/d9b1d98))\n* Updating progress. ([29a293d](https://github.com/octalmage/robotjs/commit/29a293d))\n\n\n\n## <small>0.0.2 (2014-09-01)</small>\n\n* Changed name to RobotJS. This is final! ([9651d26](https://github.com/octalmage/robotjs/commit/9651d26))\n* Fixed install instructions. ([8a94cc6](https://github.com/octalmage/robotjs/commit/8a94cc6))\n* Published to NPM! ([1401e63](https://github.com/octalmage/robotjs/commit/1401e63))\n* Updated README.md ([5e05785](https://github.com/octalmage/robotjs/commit/5e05785))\n\n\n\n## <small>0.0.1 (2014-09-01)</small>\n\n* Added build instructions. ([98ef54e](https://github.com/octalmage/robotjs/commit/98ef54e))\n* Added mouseClick function. ([e4ba3c0](https://github.com/octalmage/robotjs/commit/e4ba3c0))\n* Added progress. ([7f2090d](https://github.com/octalmage/robotjs/commit/7f2090d))\n* Create README.md ([d78304b](https://github.com/octalmage/robotjs/commit/d78304b))\n* Getting ready for NPM. ([33f7d82](https://github.com/octalmage/robotjs/commit/33f7d82))\n* Got some basic mouse functions working. ([d84e253](https://github.com/octalmage/robotjs/commit/d84e253))\n* Initial commit. ([38c1700](https://github.com/octalmage/robotjs/commit/38c1700))\n* Moved some files around. ([aad9e6d](https://github.com/octalmage/robotjs/commit/aad9e6d))\n* Removed unnecessary files. ([d2f1a3d](https://github.com/octalmage/robotjs/commit/d2f1a3d))\n* Update README.md ([4cee4c5](https://github.com/octalmage/robotjs/commit/4cee4c5))\n\n\n\n"
  },
  {
    "path": "LICENSE.md",
    "content": "Copyright (c) 2014 Jason Stallings\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\"><img src=\"https://cldup.com/1ATDf2JMtv.png\"></p>\n\n> Node.js Desktop Automation. Control the mouse, keyboard, and read the screen.\n\nRobotJS supports Mac, [Windows](https://github.com/octalmage/robotjs/issues/2), and [Linux](https://github.com/octalmage/robotjs/issues/17).\n\nThis is a work in progress so the exported functions could change at any time before the first stable release (1.0.0). [Ideas?](https://github.com/octalmage/robotjs/issues/4)\n\n[Check out some of the cool things people are making with  RobotJS](https://github.com/octalmage/robotjs/wiki/Projects-using-RobotJS)! Have your own rad RobotJS project? Feel free to add it!\n\n## Contents\n\n- [Installation](#installation)\n- [Examples](#examples)\n- [API](https://robotjs.dev/docs/syntax)\n- [Building](#building)\n- [Plans](#plans)\n- [Progress](#progress)\n- [FAQ](#faq)\n- [License](#license)\n\n## Installation\n\nInstall RobotJS using npm:\n\n```\nnpm install robotjs\n```\nIt's that easy! npm will download one of the prebuilt [binaries](https://github.com/octalmage/robotjs/releases/latest) for your OS.\n\nYou can get npm [here](https://nodejs.org/en/download/) if you don't have it installed.\n\nIf you need to build RobotJS, see the [building](#building) section. Instructions for [Electron](https://github.com/octalmage/robotjs/wiki/Electron).\n\n## Examples\n\n##### [Mouse](https://github.com/octalmage/robotjs/wiki/Syntax#mouse)\n\n<p align=\"center\"><img src=\"https://cldup.com/lugVjjAkEi.gif\"></p>\n\n```JavaScript\n// Move the mouse across the screen as a sine wave.\nvar robot = require(\"robotjs\");\n\n// Speed up the mouse.\nrobot.setMouseDelay(2);\n\nvar twoPI = Math.PI * 2.0;\nvar screenSize = robot.getScreenSize();\nvar height = (screenSize.height / 2) - 10;\nvar width = screenSize.width;\n\nfor (var x = 0; x < width; x++)\n{\n\ty = height * Math.sin((twoPI * x) / width) + height;\n\trobot.moveMouse(x, y);\n}\n```\n\n##### [Keyboard](https://github.com/octalmage/robotjs/wiki/Syntax#keyboard)\n\n```JavaScript\n// Type \"Hello World\" then press enter.\nvar robot = require(\"robotjs\");\n\n// Type \"Hello World\".\nrobot.typeString(\"Hello World\");\n\n// Press enter.\nrobot.keyTap(\"enter\");\n```\n\n##### [Screen](https://github.com/octalmage/robotjs/wiki/Syntax#screen)\n\n```JavaScript\n// Get pixel color under the mouse.\nvar robot = require(\"robotjs\");\n\n// Get mouse position.\nvar mouse = robot.getMousePos();\n\n// Get pixel color in hex format.\nvar hex = robot.getPixelColor(mouse.x, mouse.y);\nconsole.log(\"#\" + hex + \" at x:\" + mouse.x + \" y:\" + mouse.y);\n```\nRead the [Wiki](https://github.com/octalmage/robotjs/wiki) for more information!\n\n## [API](http://robotjs.dev/docs/syntax)\n\nThe RobotJS API is hosted at <https://robotjs.dev/docs/syntax>.\n\n## Building\n\nPlease ensure you have the required dependencies before installing:\n\n* Windows\n  * windows-build-tools npm package (`npm install --global --production windows-build-tools` from an elevated PowerShell or CMD.exe)\n* Mac\n  * Xcode Command Line Tools.\n* Linux\n  * Python (v2.7 recommended, v3.x.x is not supported).\n  * make.\n  * A C/C++ compiler like GCC.\n  * libxtst-dev and libpng++-dev (`sudo apt-get install libxtst-dev libpng++-dev`).\n\nInstall node-gyp using npm:\n\n```\nnpm install -g node-gyp\n```\n\nThen build:\n\n```\nnode-gyp rebuild\n```\n\nSee the [node-gyp readme](https://github.com/nodejs/node-gyp#installation) for more details.\n\n## Plans\n\n* √ Control the mouse by changing the mouse position, left/right clicking, and dragging.\n* √ Control the keyboard by pressing keys, holding keys down, and typing words.\n* √ Read pixel color from the screen and capture the screen.\n* Find an image on screen, read pixels from an image.\n* Possibly include window management?\n\n## Progress\n\n| Module        | Status        | Notes   |\n| ------------- |-------------: | ------- |\n| Mouse         | 100%           | All planned features implemented.       |\n| Keyboard      | 100%           | All planned features implemented.       |\n| Screen        | 85%            | Image search, pixel search. |\n| Bitmap        | 0%             |  Saving/opening, png support.  |\n\n## FAQ\n\n#### Does RobotJS support global hotkeys? \n\nNot currently, and I don't know if it ever will. I personally use [Electron](http://electron.atom.io/)/[NW.js](http://nwjs.io/) for global hotkeys, and this works well. Later on I might add hotkey support or create a separate module. See [#55](https://github.com/octalmage/robotjs/issues/55) for details. \n\n#### Can I take a screenshot with RobotJS? \n\nSoon! This is a bit more complicated than the rest of the features, so I saved it for last. Luckily the code is already there, I just need to write the bindings, and I've already started. Subscribe to [#13](https://github.com/octalmage/robotjs/issues/13) for updates. \n\n#### Why is &#60;insert key&#62; missing from the keyboard functions? \n\nWe've been implementing keys as we need them. Feel free to create an issue or submit a pull request!\n\n#### How about multi-monitor support?\n\nThe library doesn't have explicit multi-monitor support, so anything that works is kind of on accident. Subscribe to [#88](https://github.com/octalmage/robotjs/issues/88) for updates.\n\nFor any other questions please [submit an issue](https://github.com/octalmage/robotjs/issues/new).\n\n## Story\n\nI'm a huge fan of [AutoHotkey](https://www.autohotkey.com/), and I've used it for a very long time. AutoHotkey is great for automation and it can do a bunch of things that are very difficult in other languages. For example, it's [imagesearch](https://www.autohotkey.com/docs/commands/ImageSearch.htm) and [pixel](https://www.autohotkey.com/docs/commands/PixelGetColor.htm) related functions are hard to reproduce on Mac, especially in scripting languages. These functions are great for automating apps that can't be automated like [Netflix](http://blueshirtdesign.com/apps/autoflix/). This has never been a big deal since I've always used Windows at work, but for the past few years I've been using Mac exclusively. \n\nI like AutoHotkey, but I like Node.js more. By developing RobotJS I get an AutoHotkey replacement on Mac (finally!), and I get to use my favorite language. \n\n**TLDR:** There's nothing like AutoHotkey on Mac, so I'm making it. \n\n## License\n\nMIT\n\nBased on [autopy](https://github.com/msanders/autopy). \nMaintained by [Jason Stallings](http://jason.stallin.gs).\n"
  },
  {
    "path": "appveyor.yml",
    "content": "# http://www.appveyor.com/docs/appveyor-yml\n\n# Test against these versions of Io.js and Node.js.\nenvironment:\n  matrix:\n  # node.js\n    - nodejs_version: \"10\"\n    - nodejs_version: \"12\"\n    - nodejs_version: \"14\"\n    - nodejs_version: \"Stable\"\n\ncache:\n  - node_modules\n\nclone_depth: 5\n\nplatform:\n  - x86\n  - x64\n\n# Install scripts. (runs after repo cloning)\ninstall:\n  # Get the latest stable version of Node 0.STABLE.latest\n  - ps: Install-Product node $env:nodejs_version\n  - npm -g install npm\n  - set PATH=%APPDATA%\\npm;%PATH%\n  # Typical npm stuff.\n  - npm install\n\ntest_script:\n  # Output useful info for debugging.\n  - node --version\n  - npm --version\n  # run tests\n  - npm test\n\non_success:\n  - IF defined APPVEYOR_REPO_TAG_NAME npm run prebuild -- -u %GITHUB_TOKEN%\n\nbuild: off\nversion: \"{build}\"\n"
  },
  {
    "path": "binding.gyp",
    "content": "{\n  'targets': [{\n    'target_name': 'robotjs',\n      'cflags!': [ '-fno-exceptions' ],\n      'cflags_cc!': [ '-fno-exceptions' ],\n      'xcode_settings': { 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',\n        'CLANG_CXX_LIBRARY': 'libc++',\n        'MACOSX_DEPLOYMENT_TARGET': '10.15',\n      },\n      'msvs_settings': {\n        'VCCLCompilerTool': { 'ExceptionHandling': 1 },\n      },\n    'include_dirs': [\n      '<!(node -p \"require(\\'node-addon-api\\').include_dir\")',\n    ],\n    \n    'conditions': [\n      ['OS == \"mac\"', {\n        'include_dirs': [\n          '<!(node -p \"require(\\'node-addon-api\\').include_dir\")',\n          'System/Library/Frameworks/CoreFoundation.Framework/Headers',\n          'System/Library/Frameworks/Carbon.Framework/Headers',\n          'System/Library/Frameworks/ApplicationServices.framework/Headers',\n          'System/Library/Frameworks/OpenGL.framework/Headers',\n        ],\n        'link_settings': {\n          'libraries': [\n            '-framework Carbon',\n            '-framework CoreFoundation',\n            '-framework ApplicationServices',\n            '-framework OpenGL'\n          ]\n        }\n      }],\n      \n      ['OS == \"linux\"', {\n        'link_settings': {\n          'libraries': [\n            '-lpng',\n            '-lz',\n            '-lX11',\n            '-lXtst'\n          ]\n        },\n        \n        'sources': [\n          'src/xdisplay.c'\n        ]\n      }],\n\n      [\"OS=='win'\", {\n        'defines': ['IS_WINDOWS']\n      }]\n    ],\n    \n    'sources': [\n      'src/robotjs.cc',\n      'src/deadbeef_rand.c',\n      'src/mouse.c',\n      'src/keypress.c',\n      'src/keycode.c',\n      'src/screen.c',\n      'src/screengrab.c',\n      'src/snprintf.c',\n      'src/MMBitmap.c'\n    ]\n  }]\n}"
  },
  {
    "path": "index.d.ts",
    "content": "export interface Bitmap {\n  width: number\n  height: number\n  image: any\n  byteWidth: number\n  bitsPerPixel: number\n  bytesPerPixel: number\n  colorAt(x: number, y: number): string\n}\n\nexport interface Screen {\n  capture(x?: number, y?: number, width?: number, height?: number): Bitmap\n}\n\nexport function setKeyboardDelay(ms: number) : void\nexport function keyTap(key: string, modifier?: string | string[]) : void\nexport function keyToggle(key: string, down: string, modifier?: string | string[]) : void\nexport function unicodeTap(value: number) : void\nexport function typeString(string: string) : void\nexport function typeStringDelayed(string: string, cpm: number) : void\nexport function setMouseDelay(delay: number) : void\nexport function updateScreenMetrics() : void\nexport function moveMouse(x: number, y: number) : void\nexport function moveMouseSmooth(x: number, y: number,speed?:number) : void\nexport function mouseClick(button?: string, double?: boolean) : void\nexport function mouseToggle(down?: string, button?: string) : void\nexport function dragMouse(x: number, y: number) : void\nexport function scrollMouse(x: number, y: number) : void\nexport function getMousePos(): { x: number, y: number }\nexport function getPixelColor(x: number, y: number): string\nexport function getScreenSize(): { width: number, height: number }\n\nexport var screen: Screen\n"
  },
  {
    "path": "index.js",
    "content": "var robotjs = require('./build/Release/robotjs.node');\n\nmodule.exports = robotjs;\n\nmodule.exports.screen = {};\n\nfunction bitmap(width, height, byteWidth, bitsPerPixel, bytesPerPixel, image) \n{\n    this.width = width;\n    this.height = height;\n    this.byteWidth = byteWidth;\n    this.bitsPerPixel = bitsPerPixel;\n    this.bytesPerPixel = bytesPerPixel;\n    this.image = image;\n\n    this.colorAt = function(x, y)\n    {\n        return robotjs.getColor(this, x, y);\n    };\n\n}\n\nmodule.exports.screen.capture = function(x, y, width, height)\n{\n    //If coords have been passed, use them.\n    if (typeof x !== \"undefined\" && typeof y !== \"undefined\" && typeof width !== \"undefined\" && typeof height !== \"undefined\")\n    {\n        b = robotjs.captureScreen(x, y, width, height);\n    }\n    else \n    {\n        b = robotjs.captureScreen();\n    }\n\n    return new bitmap(b.width, b.height, b.byteWidth, b.bitsPerPixel, b.bytesPerPixel, b.image);\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"robotjs\",\n  \"version\": \"0.7.0\",\n  \"description\": \"Node.js Desktop Automation.\",\n  \"main\": \"index.js\",\n  \"typings\": \"index.d.ts\",\n  \"scripts\": {\n    \"test\": \"run-script-os\",\n    \"test:darwin:linux\": \"jasmine test/**/*.js\",\n    \"test-keyboard\": \"node test/keyboard.js\",\n    \"test:win32\": \"jasmine test/**/*.js\",\n    \"install\": \"prebuild-install --runtime napi || node-gyp rebuild\",\n    \"install-debug\": \"prebuild-install --runtime napi || node-gyp rebuild --debug\",\n    \"prebuild\": \"prebuild --all --runtime napi\",\n    \"build\": \"node-gyp rebuild\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/octalmage/robotjs.git\"\n  },\n  \"keywords\": [\n    \"Automation\",\n    \"GUI\",\n    \"mouse\",\n    \"keyboard\",\n    \"screenshot\",\n    \"image\",\n    \"pixel\",\n    \"desktop\",\n    \"robotjs\",\n    \"screen\",\n    \"recognition\",\n    \"autohotkey\",\n    \"machine\",\n    \"learning\",\n    \"color\"\n  ],\n  \"author\": \"Jason Stallings\",\n  \"license\": \"MIT\",\n  \"gypfile\": true,\n  \"binary\": {\n    \"napi_versions\": [\n      3\n    ]\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/octalmage/robotjs/issues\"\n  },\n  \"homepage\": \"https://github.com/octalmage/robotjs\",\n  \"dependencies\": {\n    \"node-addon-api\": \"^4.2.0\",\n    \"prebuild-install\": \"^5.3.3\"\n  },\n  \"devDependencies\": {\n    \"jasmine\": \"^2.99.0\",\n    \"node-gyp\": \"^12.2.0\",\n    \"prebuild\": \"^13\",\n    \"run-script-os\": \"^1.0.3\",\n    \"tape\": \"^4.8.0\"\n  }\n}\n"
  },
  {
    "path": "src/MMBitmap.c",
    "content": "#include \"MMBitmap.h\"\n#include <assert.h>\n#include <string.h>\n\nMMBitmapRef createMMBitmap(uint8_t *buffer,\n                           size_t width,\n                           size_t height,\n                           size_t bytewidth,\n                           uint8_t bitsPerPixel,\n                           uint8_t bytesPerPixel)\n{\n\tMMBitmapRef bitmap = malloc(sizeof(MMBitmap));\n\tif (bitmap == NULL) return NULL;\n\n\tbitmap->imageBuffer = buffer;\n\tbitmap->width = width;\n\tbitmap->height = height;\n\tbitmap->bytewidth = bytewidth;\n\tbitmap->bitsPerPixel = bitsPerPixel;\n\tbitmap->bytesPerPixel = bytesPerPixel;\n\n\treturn bitmap;\n}\n\nvoid destroyMMBitmap(MMBitmapRef bitmap)\n{\n\tassert(bitmap != NULL);\n\n\tif (bitmap->imageBuffer != NULL) {\n\t\tfree(bitmap->imageBuffer);\n\t\tbitmap->imageBuffer = NULL;\n\t}\n\n\tfree(bitmap);\n}\n\nvoid destroyMMBitmapBuffer(char * bitmapBuffer, void * hint)\n{\n\tif (bitmapBuffer != NULL)\t\n\t{\n\t\tfree(bitmapBuffer);\n\t}\n}\n\nMMBitmapRef copyMMBitmap(MMBitmapRef bitmap)\n{\n\tuint8_t *copiedBuf = NULL;\n\n\tassert(bitmap != NULL);\n\tif (bitmap->imageBuffer != NULL) {\n\t\tconst size_t bufsize = bitmap->height * bitmap->bytewidth;\n\t\tcopiedBuf = malloc(bufsize);\n\t\tif (copiedBuf == NULL) return NULL;\n\n\t\tmemcpy(copiedBuf, bitmap->imageBuffer, bufsize);\n\t}\n\n\treturn createMMBitmap(copiedBuf,\n\t                      bitmap->width,\n\t                      bitmap->height,\n\t                      bitmap->bytewidth,\n\t                      bitmap->bitsPerPixel,\n\t                      bitmap->bytesPerPixel);\n}\n\nMMBitmapRef copyMMBitmapFromPortion(MMBitmapRef source, MMRect rect)\n{\n\tassert(source != NULL);\n\n\tif (source->imageBuffer == NULL || !MMBitmapRectInBounds(source, rect)) {\n\t\treturn NULL;\n\t} else {\n\t\tuint8_t *copiedBuf = NULL;\n\t\tconst size_t bufsize = rect.size.height * source->bytewidth;\n\t\tconst size_t offset = (source->bytewidth * rect.origin.y) +\n\t\t                      (rect.origin.x * source->bytesPerPixel);\n\n\t\t/* Don't go over the bounds, programmer! */\n\t\tassert((bufsize + offset) <= (source->bytewidth * source->height));\n\n\t\tcopiedBuf = malloc(bufsize);\n\t\tif (copiedBuf == NULL) return NULL;\n\n\t\tmemcpy(copiedBuf, source->imageBuffer + offset, bufsize);\n\n\t\treturn createMMBitmap(copiedBuf,\n\t\t                      rect.size.width,\n\t\t                      rect.size.height,\n\t\t                      source->bytewidth,\n\t\t                      source->bitsPerPixel,\n\t\t                      source->bytesPerPixel);\n\t}\n}\n"
  },
  {
    "path": "src/MMBitmap.h",
    "content": "#pragma once\n#ifndef MMBITMAP_H\n#define MMBITMAP_H\n\n#include \"types.h\"\n#include \"rgb.h\"\n#include <assert.h>\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" \n{\n#endif\n\nstruct _MMBitmap {\n\tuint8_t *imageBuffer;  /* Pixels stored in Quad I format; i.e., origin is in\n\t                        * top left. Length should be height * bytewidth. */\n\tsize_t width;          /* Never 0, unless image is NULL. */\n\tsize_t height;         /* Never 0, unless image is NULL. */\n\tsize_t bytewidth;      /* The aligned width (width + padding). */\n\tuint8_t bitsPerPixel;  /* Should be either 24 or 32. */\n\tuint8_t bytesPerPixel; /* For convenience; should be bitsPerPixel / 8. */\n};\n\ntypedef struct _MMBitmap MMBitmap;\ntypedef MMBitmap *MMBitmapRef;\n\n/* Creates new MMBitmap with the given values.\n * Follows the Create Rule (caller is responsible for destroy()'ing object). */\nMMBitmapRef createMMBitmap(uint8_t *buffer, size_t width, size_t height,\n                           size_t bytewidth, uint8_t bitsPerPixel,\n\t\t\t\t\t\t   uint8_t bytesPerPixel);\n\n/* Releases memory occupied by MMBitmap. */\nvoid destroyMMBitmap(MMBitmapRef bitmap);\n\n/* Releases memory occupied by MMBitmap. Acts via CallBack method*/\nvoid destroyMMBitmapBuffer(char * bitmapBuffer, void * hint);\n\n/* Returns copy of MMBitmap, to be destroy()'d by caller. */\nMMBitmapRef copyMMBitmap(MMBitmapRef bitmap);\n\n/* Returns copy of one MMBitmap juxtaposed in another (to be destroy()'d\n * by the caller.), or NULL on error. */\nMMBitmapRef copyMMBitmapFromPortion(MMBitmapRef source, MMRect rect);\n\n#define MMBitmapPointInBounds(image, p) ((p).x < (image)->width && \\\n                                         (p).y < (image)->height)\n#define MMBitmapRectInBounds(image, r)                    \\\n\t(((r).origin.x + (r).size.width <= (image)->width) && \\\n\t ((r).origin.y + (r).size.height <= (image)->height))\n\n#define MMBitmapGetBounds(image) MMRectMake(0, 0, image->width, image->height)\n\n/* Get pointer to pixel of MMBitmapRef. No bounds checking is performed (check\n * yourself before calling this with MMBitmapPointInBounds(). */\n#define MMRGBColorRefAtPoint(image, x, y) \\\n\t(MMRGBColor *)(assert(MMBitmapPointInBounds(bitmap, MMPointMake(x, y))), \\\n\t               ((image)->imageBuffer) + (((image)->bytewidth * (y)) \\\n\t                                      + ((x) * (image)->bytesPerPixel)))\n\n/* Dereference pixel of MMBitmapRef. Again, no bounds checking is performed. */\n#define MMRGBColorAtPoint(image, x, y) *MMRGBColorRefAtPoint(image, x, y)\n\n/* Hex/integer value of color at point. */\n#define MMRGBHexAtPoint(image, x, y) \\\n\thexFromMMRGB(MMRGBColorAtPoint(image, x, y))\n\n/* Increment either point.x or point.y depending on the position of point.x.\n * That is, if x + 1 is >= width, increment y and start x at the beginning.\n * Otherwise, increment x.\n *\n * This is used as a convenience macro to scan rows when calling functions such\n * as findColorInRectAt() and findBitmapInBitmapAt(). */\n#define ITER_NEXT_POINT(pixel, width, start_x) \\\ndo {                                           \\\n  if (++(pixel).x >= (width)) {                \\\n    (pixel).x = start_x;                       \\\n    ++(point).y;                               \\\n  }                                            \\\n} while (0);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* MMBITMAP_H */"
  },
  {
    "path": "src/MMPointArray.c",
    "content": "#include \"MMPointArray.h\"\n#include <stdlib.h>\n\nMMPointArrayRef createMMPointArray(size_t initialCount)\n{\n\tMMPointArrayRef pointArray = calloc(1, sizeof(MMPointArray));\n\n\tif (initialCount == 0) initialCount = 1;\n\n\tpointArray->_allocedCount = initialCount;\n\tpointArray->array = malloc(pointArray->_allocedCount * sizeof(MMPoint));\n\tif (pointArray->array == NULL) return NULL;\n\n\treturn pointArray;\n}\n\nvoid destroyMMPointArray(MMPointArrayRef pointArray)\n{\n\tif (pointArray->array != NULL) {\n\t\tfree(pointArray->array);\n\t\tpointArray->array = NULL;\n\t}\n\n\tfree(pointArray);\n}\n\nvoid MMPointArrayAppendPoint(MMPointArrayRef pointArray, MMPoint point)\n{\n\tconst size_t newCount = ++(pointArray->count);\n\tif (pointArray->_allocedCount < newCount) {\n\t\tdo {\n\t\t\t/* Double size each time to avoid calls to realloc(). */\n\t\t\tpointArray->_allocedCount <<= 1;\n\t\t} while (pointArray->_allocedCount < newCount);\n\t\tpointArray->array = realloc(pointArray->array,\n\t\t                            sizeof(point) *\n\t\t                            pointArray->_allocedCount);\n\t}\n\n\tpointArray->array[pointArray->count - 1] = point;\n}\n"
  },
  {
    "path": "src/MMPointArray.h",
    "content": "#pragma once\n#ifndef MMARRAY_H\n#define MMARRAY_H\n\n#include \"types.h\"\n\nstruct _MMPointArray {\n\tMMPoint *array; /* Pointer to actual data. */\n\tsize_t count;   /* Number of elements in array. */\n\tsize_t _allocedCount; /* Private; do not use outside of MMPointArray.c. */\n};\n\ntypedef struct _MMPointArray MMPointArray;\ntypedef MMPointArray *MMPointArrayRef;\n\n/* Creates array of an initial size (the maximum size is still limitless).\n * This follows the \"Create\" Rule; i.e., responsibility for \"destroying\" the\n * array is given to the caller. */\nMMPointArrayRef createMMPointArray(size_t initialCount);\n\n/* Frees memory occupied by |pointArray|. Does not accept NULL. */\nvoid destroyMMPointArray(MMPointArrayRef pointArray);\n\n/* Appends a point to an array, increasing the internal size if necessary. */\nvoid MMPointArrayAppendPoint(MMPointArrayRef pointArray, MMPoint point);\n\n/* Retrieve point from array. */\n#define MMPointArrayGetItem(a, i) ((a)->array)[i]\n\n/* Set point in array. */\n#define MMPointArraySetItem(a, i, item) ((a)->array[i] = item)\n\n#endif /* MMARRAY_H */\n"
  },
  {
    "path": "src/UTHashTable.c",
    "content": "#include \"UTHashTable.h\"\n#include <stdlib.h>\n#include <assert.h>\n\n/* Base struct class (all nodes must contain at least the elements in\n * this struct). */\nstruct _UTHashNode {\n\tUTHashNode_HEAD\n};\n\ntypedef struct _UTHashNode UTHashNode;\n\nvoid initHashTable(UTHashTable *table, size_t initialCount, size_t nodeSize)\n{\n\tassert(table != NULL);\n\tassert(nodeSize >= sizeof(UTHashNode));\n\n\ttable->uttable = NULL; /* Must be set to NULL for uthash. */\n\ttable->allocedNodeCount = (initialCount == 0) ? 1 : initialCount;\n\ttable->nodeCount = 0;\n\ttable->nodeSize = nodeSize;\n\ttable->nodes = calloc(table->nodeSize, nodeSize * table->allocedNodeCount);\n}\n\nvoid destroyHashTable(UTHashTable *table)\n{\n\tUTHashNode *uttable = table->uttable;\n\tUTHashNode *node;\n\n\t/* Let uthash do its magic. */\n\twhile (uttable != NULL) {\n\t\tnode = uttable; /* Grab pointer to first item. */\n\t\tHASH_DEL(uttable, node); /* Delete it (table advances to next). */\n\t}\n\n\t/* Only giant malloc'd block containing each node must be freed. */\n\tif (table->nodes != NULL) free(table->nodes);\n\ttable->uttable = table->nodes = NULL;\n}\n\nvoid *getNewNode(UTHashTable *table)\n{\n\t/* Increment node count, resizing table if necessary. */\n\tconst size_t newNodeCount = ++(table->nodeCount);\n\tif (table->allocedNodeCount < newNodeCount) {\n\t\tdo {\n\t\t\t/* Double size each time to avoid calls to realloc(). */\n\t\t\ttable->allocedNodeCount <<= 1;\n\t\t} while (table->allocedNodeCount < newNodeCount);\n\n\t\ttable->nodes = realloc(table->nodes, table->nodeSize *\n\t\t                                     table->allocedNodeCount);\n\t}\n\n\treturn (char *)table->nodes + (table->nodeSize * (table->nodeCount - 1));\n}\n"
  },
  {
    "path": "src/UTHashTable.h",
    "content": "#pragma once\n#ifndef UTHASHTABLE_H\n#define UTHASHTABLE_H\n\n#include <stddef.h>\n#include \"uthash.h\"\n\n/* All node structs must begin with this (note that there is NO semicolon). */\n#define UTHashNode_HEAD UT_hash_handle hh;\n\n/* This file contains convenience macros and a standard struct for working with\n * uthash hash tables.\n *\n * The main purpose of this is for convenience of creating/freeing nodes. */\nstruct _UTHashTable {\n\tvoid *uttable; /* The uthash table -- must start out as NULL. */\n\tvoid *nodes; /* Contiguous array of nodes. */\n\tsize_t allocedNodeCount; /* Node count currently allocated for. */\n\tsize_t nodeCount; /* Current node count. */\n\tsize_t nodeSize; /* Size of each node. */\n};\n\ntypedef struct _UTHashTable UTHashTable;\n\n/* Initiates a hash table to the default values. |table| should point to an\n * already allocated UTHashTable struct.\n *\n * If the |initialCount| argument in initHashTable is given, |nodes| is\n * allocated immediately to the maximum size and new nodes are simply slices of\n * that array. This can save calls to malloc if many nodes are to be added, and\n * the a reasonable maximum number is known ahead of time.\n *\n * If the node count goes over this maximum, or if |initialCount| is 0, the\n * array is dynamically reallocated to fit the size.\n */\nvoid initHashTable(UTHashTable *table, size_t initialCount, size_t nodeSize);\n\n/* Frees memory occupied by a UTHashTable's members.\n *\n * Note that this does NOT free memory for the UTHashTable pointed to by\n * |table| itself; if that was allocated on the heap, you must free() it\n * yourself after calling this. */\nvoid destroyHashTable(UTHashTable *table);\n\n/* Returns memory allocated for a new node. Responsibility for freeing this is\n * up to the destroyHashTable() macro; this should NOT be freed by the caller.\n *\n * This is intended to be used with a HASH_ADD() macro, e.g.:\n * {%\n *     struct myNode *uttable = utHashTable->uttable;\n *     struct myNode *node = getNewNode(utHashTable);\n *     node->key = 42;\n *     node->value = someValue;\n *     HASH_ADD_INT(uttable, key, node);\n *     utHashTable->uttable = uttable;\n * %}\n *\n * Or, use the UTHASHTABLE_ADD_INT or UTHASHTABLE_ADD_STR macros\n * for convenience (they are exactly equivalent):\n * {%\n *     struct myNode *node = getNewNode(utHashTable);\n *     node->key = 42;\n *     node->value = someValue;\n *     UTHASHTABLE_ADD_INT(utHashTable, key, node, struct myNode);\n * %}\n */\nvoid *getNewNode(UTHashTable *table);\n\n#define UTHASHTABLE_ADD_INT(tablePtr, keyName, node, nodeType) \\\ndo {                                       \\\n  nodeType *uttable = (tablePtr)->uttable; \\\n  HASH_ADD_INT(uttable, keyName, node);    \\\n  (tablePtr)->uttable = uttable;           \\\n} while (0)\n\n#define UTHASHTABLE_ADD_STR(tablePtr, keyName, node, nodeType) \\\ndo {                                       \\\n  nodeType *uttable = (tablePtr)->uttable; \\\n  HASH_ADD_STR(uttable, keyName, node);    \\\n  (tablePtr)->uttable = uttable;           \\\n} while (0)\n\n#endif /* MMHASHTABLE_H */\n"
  },
  {
    "path": "src/alert.c",
    "content": "#include \"alert.h\"\n#include \"os.h\"\n#include <assert.h>\n\n#if defined(IS_MACOSX)\n\t#include <CoreFoundation/CoreFoundation.h>\n#elif defined(USE_X11)\n\t#include <stdio.h> /* For fputs() */\n\t#include <stdlib.h> /* For exit() */\n\t#include <sys/wait.h> /* For wait() */\n\t#include <unistd.h> /* For fork() */\n\t#include <sys/types.h> /* For pid_t */\n\t#include \"snprintf.h\" /* For asprintf() */\n#endif\n\n#if defined(USE_X11)\n\nenum {\n\tTASK_SUCCESS = 0,\n\tFORK_FAILED = -1,\n\tEXEC_FAILED = -2\n};\n\n/*\n * Unfortunately, X has no standard method of displaying alerts, so instead we\n * have to rely on the shell command \"xmessage\" (or nicer-looking equivalents).\n *\n * The return value and arguments are the same as those from to runTask()\n * (see below).\n */\nstatic int xmessage(char *argv[], int *exit_status);\n\n#elif defined(IS_MACOSX)\n\t#define CFStringCreateWithUTF8String(string) \\\n\t\t((string) == NULL ? NULL : CFStringCreateWithCString(NULL, \\\n\t\t                                                     string, \\\n\t\t                                                     kCFStringEncodingUTF8))\n#endif\n\nint showAlert(const char *title, const char *msg, const char *defaultButton,\n              const char *cancelButton)\n{\n#if defined(IS_MACOSX)\n\tCFStringRef alertHeader = CFStringCreateWithUTF8String(title);\n\tCFStringRef alertMessage = CFStringCreateWithUTF8String(msg);\n\tCFStringRef defaultButtonTitle = CFStringCreateWithUTF8String(defaultButton);\n\tCFStringRef cancelButtonTitle = CFStringCreateWithUTF8String(cancelButton);\n\tCFOptionFlags responseFlags;\n\tSInt32 err = CFUserNotificationDisplayAlert(0.0,\n\t                                            kCFUserNotificationNoteAlertLevel,\n\t                                            NULL,\n\t                                            NULL,\n\t                                            NULL,\n\t                                            alertHeader,\n\t                                            alertMessage,\n\t                                            defaultButtonTitle,\n\t                                            cancelButtonTitle,\n\t                                            NULL,\n\t                                            &responseFlags);\n\tif (alertHeader != NULL) CFRelease(alertHeader);\n\tif (alertMessage != NULL) CFRelease(alertMessage);\n\tif (defaultButtonTitle != NULL) CFRelease(defaultButtonTitle);\n\tif (cancelButtonTitle != NULL) CFRelease(cancelButtonTitle);\n\n\tif (err != 0) return -1;\n\treturn (responseFlags == kCFUserNotificationDefaultResponse) ? 0 : 1;\n#elif defined(USE_X11)\n\t/* Note that args[0] is set by the xmessage() function. */\n\tconst char *args[10] = {NULL, msg, \"-title\", title, \"-center\"};\n\tint response, ret;\n\tchar *buttonList = NULL; /* To be free()'d. */\n\n\tif (defaultButton == NULL) defaultButton = \"OK\";\n\n\tif (cancelButton == NULL) {\n\t\tasprintf(&buttonList, \"%s:2\", defaultButton);\n\t} else {\n\t\tasprintf(&buttonList, \"%s:2,%s:3\", defaultButton, cancelButton);\n\t}\n\n\tif (buttonList == NULL) return -1; /* asprintf() failed. */\n\targs[5] = \"-buttons\";\n\targs[6] = buttonList;\n\targs[7] = \"-default\";\n\targs[8] = defaultButton;\n\targs[9] = NULL;\n\n\tret = xmessage((char **)args, &response);\n\tif (buttonList != NULL) {\n\t\tfree(buttonList);\n\t\tbuttonList = NULL;\n\t}\n\n\tif (ret != TASK_SUCCESS) {\n\t\tif (ret == EXEC_FAILED) {\n\t\t\tfputs(\"xmessage or equivalent not found.\\n\", stderr);\n\t\t}\n\t\treturn -1;\n\t}\n\n\treturn (response == 2) ? 0 : 1;\n#else\n\t/* TODO: Display custom buttons instead of the pre-defined \"OK\"\n\t * and \"Cancel\". */\n\tint response = MessageBox(NULL, msg, title,\n\t                          (cancelButton == NULL) ? MB_OK : MB_OKCANCEL);\n\treturn (response == IDOK) ? 0 : 1;\n#endif\n}\n\n#if defined(USE_X11)\n\n/*\n * Attempts to run the given task synchronously with the given arguments.\n *\n * If |exit_status| is non-NULL and the task ran successfully, |exit_status| is\n * set to the exit code of the task on return.\n *\n * Returns -1 if process could not be forked, -2 if the task could not be run,\n * or 0 if the task was ran successfully.\n */\nstatic int runTask(const char *taskname, char * const argv[], int *exit_status);\n\nstatic int xmessage(char *argv[], int *exit_status)\n{\n\tstatic const char * const MSG_PROGS[] = {\"gmessage\", \"gxmessage\",\n\t                                         \"kmessage\", \"xmessage\"};\n\tstatic int PREV_MSG_INDEX = -1;\n\t#define MSG_PROGS_LEN (sizeof(MSG_PROGS) / sizeof(MSG_PROGS[0]))\n\n\tchar *prog = NULL;\n\tint ret;\n\n\t/* Save some fork()'ing and attempt to use last program if possible. */\n\tif (PREV_MSG_INDEX >= 0) {\n\t\tassert(PREV_MSG_INDEX < MSG_PROGS_LEN);\n\n\t\tprog = argv[0] = (char *)MSG_PROGS[PREV_MSG_INDEX];\n\t\tret = runTask(prog, argv, exit_status);\n\t} else {\n\t\t/* Otherwise, try running each xmessage alternative until one works or\n\t\t * we run out of options. */\n\t\tsize_t i;\n\t\tfor (i = 0; i < MSG_PROGS_LEN; ++i) {\n\t\t\tprog = argv[0] = (char *)MSG_PROGS[i];\n\t\t\tret = runTask(prog, argv, exit_status);\n\t\t\tif (ret != EXEC_FAILED) break;\n\t\t}\n\n\t\tif (ret == TASK_SUCCESS) PREV_MSG_INDEX = i;\n\t}\n\n\treturn ret;\n}\n\nstatic int runTask(const char *taskname, char * const argv[], int *exit_status)\n{\n\tpid_t pid;\n\tint status;\n\n\tswitch (pid = fork()) {\n\t\tcase -1: /* Failed to fork */\n\t\t\tperror(\"fork\");\n\t\t\treturn FORK_FAILED; /* Failed to fork. */\n\t\tcase 0: /* Child process */\n\t\t\texecvp(taskname, argv);\n\t\t\texit(42); /* Failed to run task. */\n\t\tdefault: /* Parent process */\n\t\t\twait(&status); /* Block execution until finished. */\n\n\t\t\tif (!WIFEXITED(status) || (status = WEXITSTATUS(status)) == 42) {\n\t\t\t\treturn EXEC_FAILED; /* Task failed to run. */\n\t\t\t}\n\t\t\tif (exit_status != NULL) *exit_status = status;\n\t\t\treturn TASK_SUCCESS; /* Success! */\n\t}\n}\n\n#endif\n"
  },
  {
    "path": "src/alert.h",
    "content": "#pragma once\n#ifndef ALERT_H\n#define ALERT_H\n\n#if defined(_MSC_VER)\n\t#include \"ms_stdbool.h\"\n#else\n\t#include <stdbool.h>\n#endif\n\n/* Displays alert with given attributes, and blocks execution until the user\n * responds. Returns 0 if defaultButton was pressed, 1 if cancelButton was\n * pressed, or -1 if an error occurred. */\nint showAlert(const char *title, const char *msg, const char *defaultButton,\n              const char *cancelButton);\n\n#endif /* ALERT_H */\n"
  },
  {
    "path": "src/base64.c",
    "content": "#include \"base64.h\"\n#include <string.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n\n/* Encoding table as described in RFC1113. */\nconst static uint8_t b64_encode_table[] =\n\t\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n\t\"abcdefghijklmnopqrstuvwxyz0123456789+/\";\n\n/* Decoding table. */\nconst static int8_t b64_decode_table[256] = {\n\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\t/* 00-0F */\n\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\t/* 10-1F */\n\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,\t/* 20-2F */\n\t52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,\t/* 30-3F */\n\t-1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, /* 40-4F */\n\t15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,\t/* 50-5F */\n\t-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,\t/* 60-6F */\n\t41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,\t/* 70-7F */\n\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\t/* 80-8F */\n\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\t/* 90-9F */\n\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\t/* A0-AF */\n\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\t/* B0-BF */\n\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\t/* C0-CF */\n\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\t/* D0-DF */\n\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\t/* E0-EF */\n\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1\t/* F0-FF */\n};\n\nuint8_t *base64decode(const uint8_t *src, const size_t buflen, size_t *retlen)\n{\n\tint8_t digit, lastdigit;\n\tsize_t i, j;\n\tuint8_t *decoded;\n\tconst size_t maxlen = ((buflen + 3) / 4) * 3;\n\n\t/* Sanity check */\n\tassert(src != NULL);\n\n\tdigit = lastdigit = j = 0;\n\tdecoded = malloc(maxlen + 1);\n\tif (decoded == NULL) return NULL;\n\tfor (i = 0; i < buflen; ++i) {\n\t\tif ((digit = b64_decode_table[src[i]]) != -1) {\n\t\t\t/* Decode block */\n\t\t\tswitch (i % 4) {\n\t\t\t\tcase 1:\n\t\t\t\t\tdecoded[j++] = ((lastdigit << 2) | ((digit & 0x30) >> 4));\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tdecoded[j++] = (((lastdigit & 0xF) << 4) | ((digit & 0x3C) >> 2));\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\tdecoded[j++] = (((lastdigit & 0x03) << 6) | digit);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tlastdigit = digit;\n\t\t}\n\t}\n\n\tif (retlen != NULL) *retlen = j;\n\tdecoded[j] = '\\0';\n\treturn decoded; /* Must be free()'d by caller */\n}\n\nuint8_t *base64encode(const uint8_t *src, const size_t buflen, size_t *retlen)\n{\n\tsize_t i, j;\n\tconst size_t maxlen = (((buflen + 3) & ~3)) * 4;\n\tuint8_t *encoded = malloc(maxlen + 1);\n\tif (encoded == NULL) return NULL;\n\n\t/* Sanity check */\n\tassert(src != NULL);\n\tassert(buflen > 0);\n\n\tj = 0;\n\tfor (i = 0; i < buflen + 1; ++i) {\n\t\t/* Encode block */\n\t\tswitch (i % 3) {\n\t\t\tcase 0:\n\t\t\t\tencoded[j++] = b64_encode_table[src[i] >> 2];\n\t\t\t\tencoded[j++] = b64_encode_table[((src[i] & 0x03) << 4) |\n\t\t\t\t                                ((src[i + 1] & 0xF0) >> 4)];\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\tencoded[j++] = b64_encode_table[((src[i] & 0x0F) << 2) |\n\t\t\t\t                                ((src[i + 1] & 0xC0) >> 6)];\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\tencoded[j++] = b64_encode_table[(src[i] & 0x3F)];\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* Add padding if necessary */\n\tif ((j % 4) != 0) {\n\t\tconst size_t with_padding = ((j + 3) & ~3); /* Align to 4 bytes */\n\t\tdo {\n\t\t\tencoded[j++] = '=';\n\t\t} while (j < with_padding);\n\t}\n\n\tassert(j <= maxlen);\n\n\tif (retlen != NULL) *retlen = j;\n\tencoded[j] = '\\0';\n\treturn encoded; /* Must be free()'d by caller */\n}\n"
  },
  {
    "path": "src/base64.h",
    "content": "#pragma once\n#ifndef BASE64_H\n#define BASE64_H\n\n#include <stddef.h>\n\n#if defined(_MSC_VER)\n\t#include \"ms_stdint.h\"\n#else\n\t#include <stdint.h>\n#endif\n\n/* Decode a base64 encoded string discarding line breaks and noise.\n *\n * Returns a new string to be free()'d by caller, or NULL on error.\n * Returned string is guaranteed to be NUL-terminated.\n *\n * If |retlen| is not NULL, it is set to the length of the returned string\n * (minus the NUL-terminator) on successful return. */\nuint8_t *base64decode(const uint8_t *buf, const size_t buflen, size_t *retlen);\n\n/* Encode a base64 encoded string without line breaks or noise.\n *\n * Returns a new string to be free()'d by caller, or NULL on error.\n * Returned string is guaranteed to be NUL-terminated with the correct padding.\n *\n * If |retlen| is not NULL, it is set to the length of the returned string\n * (minus the NUL-terminator) on successful return. */\nuint8_t *base64encode(const uint8_t *buf, const size_t buflen, size_t *retlen);\n\n#endif /* BASE64_H */\n"
  },
  {
    "path": "src/bitmap_find.c",
    "content": "#include \"bitmap_find.h\"\n#include \"UTHashTable.h\"\n#include <assert.h>\n\n/* Node to be used in hash table. */\nstruct shiftNode {\n\tUTHashNode_HEAD /* Make structure hashable */\n\tMMRGBHex color; /* Key */\n\tMMPoint offset; /* Value */\n};\n\n/* --- Hash table helper functions --- */\n\n/* Adds hex-color/offset pair to jump table. */\nstatic void addNodeToTable(UTHashTable *table, MMRGBHex color, MMPoint offset);\n\n/* Returns node associated with color in jump table, or NULL if it\n * doesn't exist. */\nstatic struct shiftNode *nodeForColor(UTHashTable *table, MMRGBHex color);\n\n/* Returns nonzero (true) if table has key, or zero (false) if not. */\n#define tableHasKey(table, color) (nodeForColor(table, color) != NULL)\n\n/* --- Boyer-Moore helper functions --- */\n\n/* Calculates the first table for use in a Boyer-Moore search algorithm.\n * Table is in the form [colors: shift_values], where colors are those in\n * |needle|, and the shift values are each color's distance from the rightmost\n * offset. All other colors are assumed to have a shift value equal to the\n * length of needle.\n */\nstatic void initBadShiftTable(UTHashTable *jumpTable, MMBitmapRef needle);\n\n/* Frees memory occupied by calling initBadShiftTable().\n * Currently this is just an alias for destroyHashTable(). */\n#define destroyBadShiftTable(jumpTable) destroyHashTable(jumpTable)\n\n/* Returns true if |needle| is found in |haystack| at |offset|. */\nstatic int needleAtOffset(MMBitmapRef needle, MMBitmapRef haystack,\n                          MMPoint offset, float tolerance);\n/* --- --- */\n\n/* An modification of the Boyer-Moore-Horspool Algorithm, only applied to\n * bitmaps and colors instead of strings and characters.\n *\n * TODO: The Boyer-Moore algorithm (with the second jump table) would probably\n * be more efficient, but this was simpler (for now).\n *\n * The jump table (|badShiftTable|) is passed as a parameter to avoid being\n * recalculated each time. It should be a pointer to a UTHashTable init'd with\n * initBadShiftTable().\n *\n * Returns 0 and sets |point| to the starting point of |needle| in |haystack|\n * if |needle| was found in |haystack|, or returns -1 if not. */\nstatic int findBitmapInRectAt(MMBitmapRef needle,\n                                MMBitmapRef haystack,\n                                MMPoint *point,\n                                MMRect rect,\n                                float tolerance,\n                                MMPoint startPoint,\n                                UTHashTable *badShiftTable)\n{\n\tconst size_t scanHeight = rect.size.height - needle->height;\n\tconst size_t scanWidth = rect.size.width - needle->width;\n\tMMPoint pointOffset = startPoint;\n\t/* const MMPoint lastPoint = MMPointMake(needle->width - 1, needle->height - 1); */\n\n\t/* Sanity check */\n\tif (needle->height > haystack->height || needle->width > haystack->width ||\n\t    !MMBitmapRectInBounds(haystack, rect)) {\n\t\treturn -1;\n\t}\n\n\tassert(point != NULL);\n\tassert(needle != NULL);\n\tassert(needle->height > 0 && needle->width > 0);\n\tassert(haystack != NULL);\n\tassert(haystack->height > 0 && haystack->width > 0);\n\tassert(badShiftTable != NULL);\n\n\t/* Search |haystack|, while |needle| can still be within it. */\n\twhile (pointOffset.y <= scanHeight) {\n\t\t/* struct shiftNode *node = NULL;\n\t\tMMRGBHex lastColor; */\n\n\t\twhile (pointOffset.x <= scanWidth) {\n\t\t\t/* Check offset in |haystack| for |needle|. */\n\t\t\tif (needleAtOffset(needle, haystack, pointOffset, tolerance)) {\n\t\t\t\t++pointOffset.x;\n\t\t\t\t++pointOffset.y;\n\t\t\t\t*point = pointOffset;\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\t/* Otherwise, calculate next x offset to check. */\n\t\t\t/*\n\t\t\t * Note that here we are getting the skip value based on the last\n\t\t\t * color of |needle|, no matter where we didn't match. The\n\t\t\t * alternative of pretending that the mismatched color was the previous\n\t\t\t * color is slower in the normal case.\n\t\t\t */\n\t\t\t/* lastColor = MMRGBHexAtPoint(haystack, pointOffset.x + lastPoint.x,\n\t\t\t                                      pointOffset.y + lastPoint.y); */\n\n\t\t\t/* TODO: This fails on certain edge cases (issue#7). */\n\t\t\t/* When a color is encountered that does not occur in |needle|, we can\n\t\t\t * safely skip ahead for the whole length of |needle|.\n\t\t\t * Otherwise, use the value stored in the jump table. */\n\t\t\t/* node = nodeForColor(badShiftTable, lastColor);\n\t\t\tpointOffset.x += (node == NULL) ? needle->width : (node->offset).x; */\n\n\t\t\t/* For now, be naive. */\n\t\t\t++pointOffset.x;\n\t\t}\n\n\t\tpointOffset.x = rect.origin.x;\n\n\t\t/* lastColor = MMRGBHexAtPoint(haystack, pointOffset.x + lastPoint.x,\n\t\t                               pointOffset.y + lastPoint.y);\n\t\tnode = nodeForColor(badShiftTable, lastColor);\n\t\tpointOffset.y += node == NULL ? lastPoint.y : (node->offset).y; */\n\n\t\t/* TODO: The above commented out code fails at certain edge cases, e.g.:\n\t\t * Needle: [B, b\n\t\t *          b, b,\n\t\t *          B, b]\n\t\t * Haystack: [w, w, w, w, w\n\t\t *            w, w, w, w, b\n\t\t *            w, w, w, b, b\n\t\t *            w, w, w, w, b]\n\t\t * The previous algorithm noticed that the first 3 x 3 block had nothing\n\t\t * in common with the image, and thus, after scanning the first row,\n\t\t * skipped three blocks downward to scan the next (which didn't exist,\n\t\t * so the loop ended). However, the needle was hidden IN-BETWEEN this\n\t\t * jump -- skipping was appropriate for scanning the column but not\n\t\t * the row.\n\t\t *\n\t\t * I need to figure out a more optimal solution; temporarily I am just\n\t\t * scanning every single y coordinate, only skipping on x's. This\n\t\t * always works, but is probably not optimal.\n\t\t */\n\t\t++pointOffset.y;\n\t}\n\n\treturn -1;\n}\n\nint findBitmapInRect(MMBitmapRef needle,\n\t\t             MMBitmapRef haystack,\n                     MMPoint *point,\n                     MMRect rect,\n                     float tolerance)\n{\n\tUTHashTable badShiftTable;\n\tint ret;\n\n\tinitBadShiftTable(&badShiftTable, needle);\n\tret = findBitmapInRectAt(needle, haystack, point, rect,\n\t                         tolerance, MMPointZero, &badShiftTable);\n\tdestroyBadShiftTable(&badShiftTable);\n\treturn ret;\n}\n\nMMPointArrayRef findAllBitmapInRect(MMBitmapRef needle, MMBitmapRef haystack,\n                                    MMRect rect, float tolerance)\n{\n\tMMPointArrayRef pointArray = createMMPointArray(0);\n\tMMPoint point = MMPointZero;\n\tUTHashTable badShiftTable;\n\n\tinitBadShiftTable(&badShiftTable, needle);\n\twhile (findBitmapInRectAt(needle, haystack, &point, rect,\n\t                          tolerance, point, &badShiftTable) == 0) {\n\t\tconst size_t scanWidth = (haystack->width - needle->width) + 1;\n\t\tMMPointArrayAppendPoint(pointArray, point);\n\t\tITER_NEXT_POINT(point, scanWidth, 0);\n\t}\n\tdestroyBadShiftTable(&badShiftTable);\n\n\treturn pointArray;\n}\n\nsize_t countOfBitmapInRect(MMBitmapRef needle, MMBitmapRef haystack,\n                           MMRect rect, float tolerance)\n{\n\tsize_t count = 0;\n\tMMPoint point = MMPointZero;\n\tUTHashTable badShiftTable;\n\n\tinitBadShiftTable(&badShiftTable, needle);\n\twhile (findBitmapInRectAt(needle, haystack, &point, rect,\n\t                          tolerance, point, &badShiftTable) == 0) {\n\t\tconst size_t scanWidth = (haystack->width - needle->width) + 1;\n\t\t++count;\n\t\tITER_NEXT_POINT(point, scanWidth, 0);\n\t}\n\tdestroyBadShiftTable(&badShiftTable);\n\n\treturn count;\n}\n\n/* --- Boyer-Moore helper functions --- */\n\nstatic void initBadShiftTable(UTHashTable *jumpTable, MMBitmapRef needle)\n{\n\tconst MMPoint lastPoint = MMPointMake(needle->width - 1, needle->height - 1);\n\tconst size_t maxColors = needle->width * needle->height;\n\tMMPoint scan;\n\n\t/* Allocate max size initially to avoid a million calls to malloc(). */\n\tinitHashTable(jumpTable, maxColors, sizeof(struct shiftNode));\n\n\t/* Populate jumpTable with analysis of |needle|. */\n\tfor (scan.y = lastPoint.y; ; --scan.y) {\n\t\tfor (scan.x = lastPoint.x; ; --scan.x) {\n\t\t\tMMRGBHex color = MMRGBHexAtPoint(needle, scan.x, scan.y);\n\t\t\tif (!tableHasKey(jumpTable, color)) {\n\t\t\t\taddNodeToTable(jumpTable, color,\n\t\t\t\t               MMPointMake(needle->width - scan.x,\n\t\t\t\t                           needle->height - scan.y));\n\t\t\t}\n\n\t\t\tif (scan.x == 0) break; /* Avoid infinite loop from unsigned type. */\n\t\t}\n\t\tif (scan.y == 0) break;\n\t}\n}\n\nstatic int needleAtOffset(MMBitmapRef needle, MMBitmapRef haystack,\n                          MMPoint offset, float tolerance)\n{\n\tconst MMPoint lastPoint = MMPointMake(needle->width - 1, needle->height - 1);\n\tMMPoint scan;\n\n\t/* Note that |needle| is searched backwards, in accordance with the\n\t * Boyer-Moore search algorithm. */\n\tfor (scan.y = lastPoint.y; ; --scan.y) {\n\t\tfor (scan.x = lastPoint.x; ; --scan.x) {\n\t\t\tMMRGBHex ncolor = MMRGBHexAtPoint(needle, scan.x, scan.y);\n\t\t\tMMRGBHex hcolor = MMRGBHexAtPoint(haystack, offset.x + scan.x,\n\t\t\t                                            offset.y + scan.y);\n\t\t\tif (!MMRGBHexSimilarToColor(ncolor, hcolor, tolerance)) return 0;\n\t\t\tif (scan.x == 0) break; /* Avoid infinite loop from unsigned type. */\n\t\t}\n\t\tif (scan.y == 0) break;\n\t}\n\n\treturn 1;\n}\n\n/* --- Hash table helper functions --- */\n\nstatic void addNodeToTable(UTHashTable *table,\n                           MMRGBHex hexColor,\n                           MMPoint offset)\n{\n\tstruct shiftNode *node = getNewNode(table);\n\tnode->color = hexColor;\n\tnode->offset = offset;\n\tUTHASHTABLE_ADD_INT(table, color, node, struct shiftNode);\n}\n\nstatic struct shiftNode *nodeForColor(UTHashTable *table,\n                                      MMRGBHex color)\n{\n\tstruct shiftNode *uttable = table->uttable;\n\tstruct shiftNode *node;\n\tHASH_FIND_INT(uttable, &color, node);\n\treturn node;\n}\n"
  },
  {
    "path": "src/bitmap_find.h",
    "content": "#pragma once\n#ifndef BITMAP_H\n#define BITMAP_H\n\n#include \"types.h\"\n#include \"MMBitmap.h\"\n#include \"MMPointArray.h\"\n\n/* Convenience wrapper around findBitmapInRect(), where |rect| is the bounds\n * of |haystack|. */\n#define findBitmapInBitmap(needle, haystack, pointPtr, tol) \\\n\tfindBitmapInRect(needle, haystack, pointPtr, MMBitmapGetBounds(haystack), tol)\n\n/* Returns 0 and sets |point| to the origin of |needle| in |haystack| if\n * |needle| was found in |haystack| inside of |rect|, or returns -1 if not.\n *\n * |tolerance| should be in the range 0.0f - 1.0f, denoting how closely the\n * colors in the bitmaps need to match, with 0 being exact and 1 being any.\n */\nint findBitmapInRect(MMBitmapRef needle, MMBitmapRef haystack,\n                     MMPoint *point, MMRect rect, float tolerance);\n\n/* Convenience wrapper around findAllBitmapInRect(), where |rect| is the bounds\n * of |haystack|. */\n#define findAllBitmapInBitmap(needle, haystack, tolerance) \\\n\tfindAllBitmapInRect(needle, haystack, \\\n\t\t\t            MMBitmapGetBounds(haystack), tolerance)\n\n/* Returns MMPointArray of all occurrences of |needle| in |haystack| inside of\n * |rect|. Note that an is returned regardless of whether |needle| was found;\n * check array->count to see if it actually was.\n *\n * |tolerance| should be in the range 0.0f - 1.0f, denoting how closely the\n * colors in the bitmaps need to match, with 0 being exact and 1 being any.\n *\n * Responsibility for freeing the MMPointArray with destroyMMPointArray() is\n * given to the caller.\n */\nMMPointArrayRef findAllBitmapInRect(MMBitmapRef needle, MMBitmapRef haystack,\n                                    MMRect rect, float tolerance);\n\n/* Convenience wrapper around countOfBitmapInRect(), where |rect| is the bounds\n * of |haystack|. */\n#define countOfBitmapInBitmap(needle, haystack, tolerance) \\\n\tcountOfBitmapInRect(needle, haystack, MMBitmapGetBounds(haystack), tolerance)\n\n/* Returns the number of occurences of |needle| in |haystack| inside\n * of |rect|. */\nsize_t countOfBitmapInRect(MMBitmapRef needle, MMBitmapRef haystack,\n                           MMRect rect, float tolerance);\n\n#endif /* BITMAP_H */\n"
  },
  {
    "path": "src/bmp_io.c",
    "content": "#include \"bmp_io.h\"\n#include \"os.h\"\n#include \"endian.h\"\n#include <stdio.h> /* fopen() */\n#include <string.h> /* memcpy() */\n\n#if defined(_MSC_VER)\n\t#include \"ms_stdbool.h\"\n\t#include \"ms_stdint.h\"\n#else\n\t#include <stdbool.h>\n\t#include <stdint.h>\n#endif\n\n#pragma pack(push, 1) /* The following structs should be continguous, so we can\n                       * copy them in one read. */\n/*\n * Standard, initial BMP Header\n */\nstruct BITMAP_FILE_HEADER {\n\tuint16_t magic;       /* First two byes of the file; should be 0x4D42. */\n\tuint32_t fileSize;    /* Size of the BMP file in bytes (unreliable). */\n\tuint32_t reserved;    /* Application-specific. */\n\tuint32_t imageOffset; /* Offset to bitmap data. */\n};\n\n#define BMP_MAGIC 0x4D42 /* The starting key that marks the file as a BMP. */\n\nenum _BMP_COMPRESSION {\n\tkBMP_RGB = 0, /* No compression. */\n\tkBMP_RLE8 = 1, /* Can only be used with 8-bit bitmaps. */\n\tkBMP_RLE4 = 2, /* Can only be used with 4-bit bitmaps. */\n\tkBMP_BITFIELDS = 3, /* Can only be used with 16/32-bit bitmaps. */\n\tkBMP_JPEG = 4, /* Bitmap contains a JPEG image. */\n\tkBMP_PNG = 5 /* Bitmap contains a PNG image. */\n};\n\ntypedef uint32_t BMP_COMPRESSION;\n\n/*\n * Windows 3 Header\n */\nstruct BITMAP_INFO_HEADER {\n\tuint32_t headerSize;         /* The size of this header (40 bytes). */\n\tint32_t width;               /* The bitmap width in pixels. */\n\tint32_t height;              /* The bitmap height in pixels. */\n\t                             /* (A negative value denotes that the image\n\t\t\t\t\t\t\t\t  * is flipped.) */\n\tuint16_t colorPlanes;        /* The number of color planes; must be 1. */\n\tuint16_t bitsPerPixel;       /* The color depth of the image (1, 4, 8, 16,\n\t                              * 24, or 32). */\n\tBMP_COMPRESSION compression; /* The compression method being used. */\n\tuint32_t imageSize;          /* Size of the bitmap in bytes (unreliable).*/\n\tint32_t xRes;                /* The horizontal resolution (unreliable). */\n\tint32_t yRes;                /* The vertical resolution (unreliable). */\n\tuint32_t colorsUsed;         /* The number of colors in the color table,\n\t                              * or 0 to default to 2^n. */\n\tuint32_t colorsImportant;    /* Colors important for displaying bitmap,\n\t                              * or 0 when every color is equally important;\n\t                              * ignored. */\n};\n\n/*\n * OS/2 v1 Header\n */\nstruct BITMAP_CORE_HEADER {\n\tuint32_t headerSize;   /* The size of this header (12 bytes). */\n\tuint16_t width;        /* The bitmap width in pixels. */\n\tuint16_t height;       /* The bitmap height in pixels. */\n\tuint16_t colorPlanes;  /* The number of color planes; must be 1. */\n\tuint16_t bitsPerPixel; /* Color depth of the image (1, 4, 8, or 24). */\n};\n\n#pragma pack(pop) /* Let the compiler do what it wants now. */\n\n/* BMP files are always saved in little endian format (x86), so we need to\n * convert them if we're not on a little endian machine (e.g., ARM & ppc). */\n\n#if __BYTE_ORDER == __BIG_ENDIAN\n\n/* Converts bitmap file header from to and from little endian, if and only if\n * host is big endian. */\nstatic void convertBitmapFileHeader(struct BITMAP_FILE_HEADER *header)\n{\n\theader->magic = swapLittleAndHost16(header->magic);\n\tswapLittleAndHost32(header->fileSize);\n\tswapLittleAndHost32(header->reserved);\n\tswapLittleAndHost32(header->imageOffset);\n}\n\n/* Converts bitmap info header from to and from little endian, if and only if\n * host is big endian. */\nstatic void convertBitmapInfoHeader(struct BITMAP_INFO_HEADER *header)\n{\n\theader->headerSize = swapLittleAndHost32(header->headerSize);\n\theader->width = swapLittleAndHost32(header->width);\n\theader->height = swapLittleAndHost32(header->height);\n\theader->colorPlanes = swapLittleAndHost16(header->colorPlanes);\n\theader->bitsPerPixel = swapLittleAndHost16(header->bitsPerPixel);\n\theader->compression = swapLittleAndHost32(header->compression);\n\theader->imageSize = swapLittleAndHost32(header->imageSize);\n\theader->xRes = swapLittleAndHost32(header->xRes);\n\theader->yRes = swapLittleAndHost32(header->yRes);\n\theader->colorsUsed = swapLittleAndHost32(header->colorsUsed);\n\theader->colorsImportant = swapLittleAndHost32(header->colorsImportant);\n}\n\n#elif __BYTE_ORDER == __LITTLE_ENDIAN\n\t/* No conversion necessary if we are already little endian. */\n\t#define convertBitmapFileHeader(header)\n\t#define convertBitmapInfoHeader(header)\n#endif\n\n/* Returns newly alloc'd image data from bitmap file. The current position of\n * the file must be at the start of the image before calling this. */\nstatic uint8_t *readImageData(FILE *fp, size_t width, size_t height,\n                              uint8_t bytesPerPixel, size_t bytewidth);\n\n/* Copys image buffer from |bitmap| to |dest| in BGR format. */\nstatic void copyBGRDataFromMMBitmap(MMBitmapRef bitmap, uint8_t *dest);\n\nconst char *MMBMPReadErrorString(MMIOError error)\n{\n\tswitch (error) {\n\t\tcase kBMPAccessError:\n\t\t\treturn \"Could not open file\";\n\t\tcase kBMPInvalidKeyError:\n\t\t\treturn \"Not a BMP file\";\n\t\tcase kBMPUnsupportedHeaderError:\n\t\t\treturn \"Unsupported BMP header\";\n\t\tcase kBMPInvalidColorPanesError:\n\t\t\treturn \"Invalid number of color panes in BMP file\";\n\t\tcase kBMPUnsupportedColorDepthError:\n\t\t\treturn \"Unsupported color depth in BMP file\";\n\t\tcase kBMPUnsupportedCompressionError:\n\t\t\treturn \"Unsupported file compression in BMP file\";\n\t\tcase kBMPInvalidPixelDataError:\n\t\t\treturn \"Could not read BMP pixel data\";\n\t\tdefault:\n\t\t\treturn NULL;\n\t}\n}\n\nMMBitmapRef newMMBitmapFromBMP(const char *path, MMBMPReadError *err)\n{\n\tFILE *fp;\n\tstruct BITMAP_FILE_HEADER fileHeader = {0}; /* Initialize elements to 0. */\n\tstruct BITMAP_INFO_HEADER dibHeader = {0};\n\tuint32_t headerSize = 0;\n\tuint8_t bytesPerPixel;\n\tsize_t bytewidth;\n\tuint8_t *imageBuf;\n\n\tif ((fp = fopen(path, \"rb\")) == NULL) {\n\t\tif (err != NULL) *err = kBMPAccessError;\n\t\treturn NULL;\n\t}\n\n\t/* Initialize error code to generic value. */\n\tif (err != NULL) *err = kBMPGenericError;\n\n\tif (fread(&fileHeader, sizeof(fileHeader), 1, fp) == 0) goto bail;\n\n\t/* Convert from little-endian if it's not already. */\n\tconvertBitmapFileHeader(&fileHeader);\n\n\t/* First two bytes should always be 0x4D42. */\n\tif (fileHeader.magic != BMP_MAGIC) {\n\t\tif (err != NULL) *err = kBMPInvalidKeyError;\n\t\tgoto bail;\n\t}\n\n\t/* Get header size. */\n\tif (fread(&headerSize, sizeof(headerSize), 1, fp) == 0) goto bail;\n\theaderSize = swapLittleAndHost32(headerSize);\n\n\t/* Back up before reading header. */\n\tif (fseek(fp, -(long)sizeof(headerSize), SEEK_CUR) < 0) goto bail;\n\n\tif (headerSize == 12) { /* OS/2 v1 header */\n\t\tstruct BITMAP_CORE_HEADER coreHeader = {0};\n\t\tif (fread(&coreHeader, sizeof(coreHeader), 1, fp) == 0) goto bail;\n\n\t\tdibHeader.width = coreHeader.width;\n\t\tdibHeader.height = coreHeader.height;\n\t\tdibHeader.colorPlanes = coreHeader.colorPlanes;\n\t\tdibHeader.bitsPerPixel = coreHeader.bitsPerPixel;\n\t} else if (headerSize == 40 || headerSize == 108 || headerSize == 124) {\n\t\t/* Windows v3/v4/v5 header */\n\t\t/* Read only the common part (v3) and skip over the rest. */\n\t\tif (fread(&dibHeader, sizeof(dibHeader), 1, fp) == 0) goto bail;\n\t} else {\n\t\tif (err != NULL) *err = kBMPUnsupportedHeaderError;\n\t\tgoto bail;\n\t}\n\n\tconvertBitmapInfoHeader(&dibHeader);\n\n\tif (dibHeader.colorPlanes != 1) {\n\t\tif (err != NULL) *err = kBMPInvalidColorPanesError;\n\t\tgoto bail;\n\t}\n\n\t/* Currently only 24-bit and 32-bit are supported. */\n\tif (dibHeader.bitsPerPixel != 24 && dibHeader.bitsPerPixel != 32) {\n\t\tif (err != NULL) *err = kBMPUnsupportedColorDepthError;\n\t\tgoto bail;\n\t}\n\n\tif (dibHeader.compression != kBMP_RGB) {\n\t\tif (err != NULL) *err = kBMPUnsupportedCompressionError;\n\t\tgoto bail;\n\t}\n\n\t/* This can happen because we don't fully parse Windows v4/v5 headers. */\n\tif (ftell(fp) != (long)fileHeader.imageOffset) {\n\t\tfseek(fp, fileHeader.imageOffset, SEEK_SET);\n\t}\n\n\t/* Get bytes per row, including padding. */\n\tbytesPerPixel = dibHeader.bitsPerPixel / 8;\n\tbytewidth = ADD_PADDING(dibHeader.width * bytesPerPixel);\n\n\timageBuf = readImageData(fp, dibHeader.width, abs(dibHeader.height),\n\t                         bytesPerPixel, bytewidth);\n\tfclose(fp);\n\n\tif (imageBuf == NULL) {\n\t\tif (err != NULL) *err = kBMPInvalidPixelDataError;\n\t\treturn NULL;\n\t}\n\n\t/* A negative height indicates that the image is flipped.\n\t *\n\t * We store our bitmaps as \"flipped\" according to the BMP format; i.e., (0, 0)\n\t * is the top left, not bottom left. So we only need to flip the bitmap if\n\t * the height is NOT negative. */\n\tif (dibHeader.height < 0) {\n\t\tdibHeader.height = -dibHeader.height;\n\t} else {\n\t\tflipBitmapData(imageBuf, dibHeader.width, dibHeader.height, bytewidth);\n\t}\n\n\treturn createMMBitmap(imageBuf, dibHeader.width, dibHeader.height,\n\t                      bytewidth, (uint8_t)dibHeader.bitsPerPixel,\n\t                      bytesPerPixel);\n\nbail:\n\tfclose(fp);\n\treturn NULL;\n}\n\nuint8_t *createBitmapData(MMBitmapRef bitmap, size_t *len)\n{\n\t/* BMP files are always aligned to 4 bytes. */\n\tconst size_t bytewidth = ((bitmap->width * bitmap->bytesPerPixel) + 3) & ~3;\n\n\tconst size_t imageSize = bytewidth * bitmap->height;\n\tstruct BITMAP_FILE_HEADER *fileHeader;\n\tstruct BITMAP_INFO_HEADER *dibHeader;\n\n\t/* Should always be 54. */\n\tconst size_t imageOffset = sizeof(*fileHeader) + sizeof(*dibHeader);\n\tuint8_t *data;\n\tconst size_t dataLen = imageOffset + imageSize;\n\n\tdata = calloc(1, dataLen);\n\tif (data == NULL) return NULL;\n\n\t/* Save top header. */\n\tfileHeader = (struct BITMAP_FILE_HEADER *)data;\n\tfileHeader->magic = BMP_MAGIC;\n\tfileHeader->fileSize = (uint32_t)(sizeof(*dibHeader) + imageSize);\n\tfileHeader->imageOffset = (uint32_t)imageOffset;\n\n\t/* BMP files are always stored as little-endian, so we need to convert back\n\t * if necessary. */\n\tconvertBitmapFileHeader(fileHeader);\n\n\t/* Copy Windows v3 header. */\n\tdibHeader = (struct BITMAP_INFO_HEADER *)(data + sizeof(*fileHeader));\n\tdibHeader->headerSize = sizeof(*dibHeader); /* Should always be 40. */\n\tdibHeader->width = (int32_t)bitmap->width;\n\tdibHeader->height = -(int32_t)bitmap->height; /* Our bitmaps are \"flipped\". */\n\tdibHeader->colorPlanes = 1;\n\tdibHeader->bitsPerPixel = bitmap->bitsPerPixel;\n\tdibHeader->compression = kBMP_RGB; /* Don't save with compression. */\n\tdibHeader->imageSize = (uint32_t)imageSize;\n\n\tconvertBitmapInfoHeader(dibHeader);\n\n\t/* Lastly, copy the pixel data. */\n\tcopyBGRDataFromMMBitmap(bitmap, data + imageOffset);\n\n\tif (len != NULL) *len = dataLen;\n\treturn data;\n}\n\nint saveMMBitmapAsBMP(MMBitmapRef bitmap, const char *path)\n{\n\tFILE *fp;\n\tsize_t dataLen;\n\tuint8_t *data;\n\n\tif ((fp = fopen(path, \"wb\")) == NULL) return -1;\n\n\tif ((data = createBitmapData(bitmap, &dataLen)) == NULL) {\n\t\tfclose(fp);\n\t\treturn -1;\n\t}\n\n\tif (fwrite(data, dataLen, 1, fp) == 0) {\n\t\tfree(data);\n\t\tfclose(fp);\n\t\treturn -1;\n\t}\n\n\tfree(data);\n\tfclose(fp);\n\treturn 0;\n}\n\nstatic uint8_t *readImageData(FILE *fp, size_t width, size_t height,\n                              uint8_t bytesPerPixel, size_t bytewidth)\n{\n\tsize_t imageSize = bytewidth * height;\n\tuint8_t *imageBuf = calloc(1, imageSize);\n\n\tif (MMRGB_IS_BGR && (bytewidth % 4) == 0) { /* No conversion needed. */\n\t\tif (fread(imageBuf, imageSize, 1, fp) == 0) {\n\t\t\tfree(imageBuf);\n\t\t\treturn NULL;\n\t\t}\n\t} else { /* Convert from BGR with 4-byte alignment. */\n\t\tuint8_t *row = malloc(bytewidth);\n\t\tsize_t y;\n\t\tconst size_t bmp_bytewidth = (width * bytesPerPixel + 3) & ~3;\n\n\t\tif (row == NULL) return NULL;\n\t\tassert(bmp_bytewidth <= bytewidth);\n\n\t\t/* Read image data row by row. */\n\t\tfor (y = 0; y < height; ++y) {\n\t\t\tconst size_t rowOffset = y * bytewidth;\n\t\t\tsize_t x;\n\t\t\tuint8_t *rowptr = row;\n\t\t\tif (fread(row, bmp_bytewidth, 1, fp) == 0) {\n\t\t\t\tfree(imageBuf);\n\t\t\t\tfree(row);\n\t\t\t\treturn NULL;\n\t\t\t}\n\n\t\t\tfor (x = 0; x < width; ++x) {\n\t\t\t\tconst size_t colOffset = x * bytesPerPixel;\n\t\t\t\tMMRGBColor *color = (MMRGBColor *)(imageBuf +\n\t\t\t\t                                   rowOffset + colOffset);\n\n\t\t\t\t/* BMP files are stored in BGR format. */\n\t\t\t\tcolor->blue = rowptr[0];\n\t\t\t\tcolor->green = rowptr[1];\n\t\t\t\tcolor->red = rowptr[2];\n\t\t\t\trowptr += bytesPerPixel;\n\t\t\t}\n\t\t}\n\n\t\tfree(row);\n\t}\n\n\treturn imageBuf;\n}\n\nstatic void copyBGRDataFromMMBitmap(MMBitmapRef bitmap, uint8_t *dest)\n{\n\tif (MMRGB_IS_BGR && (bitmap->bytewidth % 4) == 0) { /* No conversion needed. */\n\t\tmemcpy(dest, bitmap->imageBuffer, bitmap->bytewidth * bitmap->height);\n\t} else { /* Convert to RGB with other-than-4-byte alignment. */\n\t\tconst size_t bytewidth = (bitmap->width * bitmap->bytesPerPixel + 3) & ~3;\n\t\tsize_t y;\n\n\t\t/* Copy image data row by row. */\n\t\tfor (y = 0; y < bitmap->height; ++y) {\n\t\t\tuint8_t *rowptr = dest + (y * bytewidth);\n\t\t\tsize_t x;\n\t\t\tfor (x = 0; x < bitmap->width; ++x) {\n\t\t\t\tMMRGBColor *color = MMRGBColorRefAtPoint(bitmap, x, y);\n\n\t\t\t\t/* BMP files are stored in BGR format. */\n\t\t\t\trowptr[0] = color->blue;\n\t\t\t\trowptr[1] = color->green;\n\t\t\t\trowptr[2] = color->red;\n\n\t\t\t\trowptr += bitmap->bytesPerPixel;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/* Perform an in-place swap from Quadrant 1 to Quadrant III format (upside-down\n * PostScript/GL to right side up QD/CG raster format) We do this in-place,\n * which requires more copying, but will touch only half the pages.\n *\n * This is blatantly copied from Apple's glGrab example code. */\nvoid flipBitmapData(void *data, size_t width, size_t height, size_t bytewidth)\n{\n\tsize_t top, bottom;\n\tvoid *topP;\n\tvoid *bottomP;\n\tvoid *tempbuf;\n\n\tif (height <= 1) return; /* No flipping necessary if height is <= 1. */\n\n\ttop = 0;\n\tbottom = height - 1;\n\ttempbuf = malloc(bytewidth);\n\tif (tempbuf == NULL) return;\n\n\twhile (top < bottom) {\n\t\ttopP = (void *)((top * bytewidth) + (intptr_t)data);\n\t\tbottomP = (void *)((bottom * bytewidth) + (intptr_t)data);\n\n\t\t/* Save and swap scanlines.\n\t\t * Does a simple in-place exchange with a temp buffer. */\n\t\tmemcpy(tempbuf, topP, bytewidth);\n\t\tmemcpy(topP, bottomP, bytewidth);\n\t\tmemcpy(bottomP, tempbuf, bytewidth);\n\n\t\t++top;\n\t\t--bottom;\n\t}\n\tfree(tempbuf);\n}\n"
  },
  {
    "path": "src/bmp_io.h",
    "content": "#pragma once\n#ifndef BMP_IO_H\n#define BMP_IO_H\n\n#include \"MMBitmap.h\"\n#include \"io.h\"\n\n#ifdef __cplusplus\nextern \"C\" \n{\n#endif\n\nenum _BMPReadError {\n\tkBMPGenericError = 0,\n\tkBMPAccessError,\n\tkBMPInvalidKeyError,\n\tkBMPUnsupportedHeaderError,\n\tkBMPInvalidColorPanesError,\n\tkBMPUnsupportedColorDepthError,\n\tkBMPUnsupportedCompressionError,\n\tkBMPInvalidPixelDataError\n};\n\ntypedef MMIOError MMBMPReadError;\n\n/* Returns description of given MMBMPReadError.\n * Returned string is constant and hence should not be freed. */\nconst char *MMBMPReadErrorString(MMIOError error);\n\n/* Attempts to read bitmap file at path; returns new MMBitmap on success, or\n * NULL on error. If |error| is non-NULL, it will be set to the error code\n * on return.\n *\n * Currently supports:\n *     - Uncompressed Windows v3/v4/v5 24-bit or 32-bit BMP.\n *     - OS/2 v1 or v2 24-bit BMP.\n *     - Does NOT yet support: 1-bit, 4-bit, 8-bit, 16-bit, compressed bitmaps,\n *       or PNGs/JPEGs disguised as BMPs (and returns NULL if those are given).\n *\n * Responsibility for destroy()'ing returned MMBitmap is left up to caller. */\nMMBitmapRef newMMBitmapFromBMP(const char *path, MMBMPReadError *error);\n\n/* Returns a buffer containing the raw BMP file data in Windows v3 BMP format,\n * ready to be saved to a file. If |len| is not NULL, it will be set to the\n * number of bytes allocated in the returned buffer.\n *\n * Responsibility for free()'ing data is left up to the caller. */\nuint8_t *createBitmapData(MMBitmapRef bitmap, size_t *len);\n\n/* Saves bitmap to file in Windows v3 BMP format.\n * Returns 0 on success, -1 on error. */\nint saveMMBitmapAsBMP(MMBitmapRef bitmap, const char *path);\n\n/* Swaps bitmap from Quadrant 1 to Quadran III format, or vice versa\n * (upside-down Cartesian/PostScript/GL <-> right side up QD/CG raster format).\n */\nvoid flipBitmapData(void *data, size_t width, size_t height, size_t bytewidth);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* BMP_IO_H */\n"
  },
  {
    "path": "src/color_find.c",
    "content": "#include \"color_find.h\"\n#include \"screen.h\"\n#include <stdlib.h>\n\n/* Abstracted, general function to avoid repeated code. */\nstatic int findColorInRectAt(MMBitmapRef image, MMRGBHex color, MMPoint *point,\n                             MMRect rect, float tolerance, MMPoint startPoint)\n{\n\tMMPoint scan = startPoint;\n\tif (!MMBitmapRectInBounds(image, rect)) return -1;\n\n\tfor (; scan.y < rect.size.height; ++scan.y) {\n\t\tfor (; scan.x < rect.size.width; ++scan.x) {\n\t\t\tMMRGBHex found = MMRGBHexAtPoint(image, scan.x, scan.y);\n\t\t\tif (MMRGBHexSimilarToColor(color, found, tolerance)) {\n\t\t\t\tif (point != NULL) *point = scan;\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t\tscan.x = rect.origin.x;\n\t}\n\n\treturn -1;\n}\n\nint findColorInRect(MMBitmapRef image, MMRGBHex color,\n                    MMPoint *point, MMRect rect, float tolerance)\n{\n\treturn findColorInRectAt(image, color, point, rect, tolerance, rect.origin);\n}\n\nMMPointArrayRef findAllColorInRect(MMBitmapRef image, MMRGBHex color,\n                                   MMRect rect, float tolerance)\n{\n\tMMPointArrayRef pointArray = createMMPointArray(0);\n\tMMPoint point = MMPointZero;\n\n\twhile (findColorInRectAt(image, color, &point, rect, tolerance, point) == 0) {\n\t\tMMPointArrayAppendPoint(pointArray, point);\n\t\tITER_NEXT_POINT(point, rect.size.width, rect.origin.x);\n\t}\n\n\treturn pointArray;\n}\n\nsize_t countOfColorsInRect(MMBitmapRef image, MMRGBHex color, MMRect rect,\n                           float tolerance)\n{\n\tsize_t count = 0;\n\tMMPoint point = MMPointZero;\n\n\twhile (findColorInRectAt(image, color, &point, rect, tolerance, point) == 0) {\n\t\tITER_NEXT_POINT(point, rect.size.width, rect.origin.x);\n\t\t++count;\n\t}\n\n\treturn count;\n}\n"
  },
  {
    "path": "src/color_find.h",
    "content": "#pragma once\n#ifndef COLOR_FIND_H\n#define COLOR_FIND_H\n\n#include \"MMBitmap.h\"\n#include \"MMPointArray.h\"\n\n/* Convenience wrapper around findColorInRect(), where |rect| is the bounds of\n * the image. */\n#define findColorInImage(image, color, pointPtr, tolerance) \\\n\tfindColorInRect(image, color, pointPtr, MMBitmapGetBounds(image), tolerance)\n\n/* Attempt to find a pixel with the given color in |image| inside |rect|.\n * Returns 0 on success, non-zero on failure. If the color was found and\n * |point| is not NULL, it will be initialized to the (x, y) coordinates the\n * RGB color.\n *\n * |tolerance| should be in the range 0.0f - 1.0f, denoting how closely the\n * colors need to match, with 0 being exact and 1 being any. */\nint findColorInRect(MMBitmapRef image, MMRGBHex color, MMPoint *point,\n                    MMRect rect, float tolerance);\n\n/* Convenience wrapper around findAllRGBInRect(), where |rect| is the bounds of\n * the image. */\n#define findAllColorInImage(image, color, tolerance) \\\n\tfindAllColorInRect(image, color, MMBitmapGetBounds(image), tolerance)\n\n/* Returns MMPointArray of all pixels of given color in |image| inside of\n * |rect|. Note that an array is returned regardless of whether the color was\n * found; check array->count to see if it actually was.\n *\n * Responsibility for freeing the MMPointArray with destroyMMPointArray() is\n * given to the caller.\n *\n * |tolerance| should be in the range 0.0f - 1.0f, denoting how closely the\n * colors need to match, with 0 being exact and 1 being any. */\nMMPointArrayRef findAllColorInRect(MMBitmapRef image, MMRGBHex color,\n                                   MMRect rect, float tolerance);\n\n/* Convenience wrapper around countOfColorsInRect, where |rect| is the bounds\n * of the image. */\n#define countOfColorsInImage(image, color, tolerance) \\\n\tcountOfColorsInRect(image, color, MMBitmapGetBounds(image), tolerance)\n\n/* Returns the count of the given color in |rect| inside of |image|. */\nsize_t countOfColorsInRect(MMBitmapRef image, MMRGBHex color, MMRect rect,\n                           float tolerance);\n\n#endif /* COLOR_FIND_H */\n"
  },
  {
    "path": "src/deadbeef_rand.c",
    "content": "#include \"deadbeef_rand.h\"\n#include <time.h>\n\nstatic uint32_t deadbeef_seed;\nstatic uint32_t deadbeef_beef = 0xdeadbeef;\n\nuint32_t deadbeef_rand(void)\n{\n\tdeadbeef_seed = (deadbeef_seed << 7) ^ ((deadbeef_seed >> 25) + deadbeef_beef);\n\tdeadbeef_beef = (deadbeef_beef << 7) ^ ((deadbeef_beef >> 25) + 0xdeadbeef);\n\treturn deadbeef_seed;\n}\n\nvoid deadbeef_srand(uint32_t x)\n{\n\tdeadbeef_seed = x;\n\tdeadbeef_beef = 0xdeadbeef;\n}\n\n/* Taken directly from the documentation:\n * http://inglorion.net/software/cstuff/deadbeef_rand/ */\nuint32_t deadbeef_generate_seed(void)\n{\n\t  uint32_t t = (uint32_t)time(NULL);\n\t  uint32_t c = (uint32_t)clock();\n\t  return (t << 24) ^ (c << 11) ^ t ^ (size_t) &c;\n}\n"
  },
  {
    "path": "src/deadbeef_rand.h",
    "content": "#ifndef DEADBEEF_RAND_H\n#define DEADBEEF_RAND_H\n\n#include <stdint.h>\n\n#define DEADBEEF_MAX UINT32_MAX\n\n/* Dead Beef Random Number Generator\n * From: http://inglorion.net/software/deadbeef_rand\n * A fast, portable psuedo-random number generator by BJ Amsterdam Zuidoost.\n * Stated in license terms: \"Feel free to use the code in your own software.\" */\n\n/* Generates a random number between 0 and DEADBEEF_MAX. */\nuint32_t deadbeef_rand(void);\n\n/* Seeds with the given integer. */\nvoid deadbeef_srand(uint32_t x);\n\n/* Generates seed from the current time. */\nuint32_t deadbeef_generate_seed(void);\n\n/* Seeds with the above function. */\n#define deadbeef_srand_time() deadbeef_srand(deadbeef_generate_seed())\n\n/* Returns random double in the range [a, b).\n * Taken directly from the rand() man page. */\n#define DEADBEEF_UNIFORM(a, b) \\\n\t((a) + (deadbeef_rand() / (((double)DEADBEEF_MAX / (b - a) + 1))))\n\n/* Returns random integer in the range [a, b).\n * Also taken from the rand() man page. */\n#define DEADBEEF_RANDRANGE(a, b) \\\n\t(uint32_t)DEADBEEF_UNIFORM(a, b)\n\n#endif /* DEADBEEF_RAND_H */\n"
  },
  {
    "path": "src/endian.h",
    "content": "#pragma once\n#ifndef ENDIAN_H\n#define ENDIAN_H\n\n#include \"os.h\"\n\n/*\n * (Mostly) cross-platform endian definitions and bit swapping macros.\n * Unfortunately, there is no standard C header for this, so we just\n * include the most common ones and fallback to our own custom macros.\n */\n\n#if defined(__linux__) /* Linux */\n\t#include <endian.h>\n\t#include <byteswap.h>\n#elif (defined(__FreeBSD__) && __FreeBSD_version >= 470000) || \\\n       defined(__OpenBSD__) || defined(__NetBSD__) /* (Free|Open|Net)BSD */\n\t#include <sys/endian.h>\n\t#define __BIG_ENDIAN BIG_ENDIAN\n\t#define __LITTLE_ENDIAN LITTLE_ENDIAN\n\t#define __BYTE_ORDER BYTE_ORDER\n#elif defined(IS_MACOSX) || (defined(BSD) && (BSD >= 199103)) /* Other BSD */\n\t#include <machine/endian.h>\n\t#define __BIG_ENDIAN BIG_ENDIAN\n\t#define __LITTLE_ENDIAN LITTLE_ENDIAN\n\t#define __BYTE_ORDER BYTE_ORDER\n#elif defined(IS_WINDOWS) /* Windows is assumed to be little endian only. */\n\t#define __BIG_ENDIAN 4321\n\t#define __LITTLE_ENDIAN 1234\n\t#define __BYTE_ORDER __LITTLE_ENDIAN\n#endif\n\n/* Fallback to custom constants. */\n#if !defined(__BIG_ENDIAN)\n\t#define __BIG_ENDIAN 4321\n#endif\n\n#if !defined(__LITTLE_ENDIAN)\n\t#define __LITTLE_ENDIAN 1234\n#endif\n\n/* Prefer compiler flag settings if given. */\n#if defined(MM_BIG_ENDIAN)\n\t#undef __BYTE_ORDER /* Avoid redefined macro compiler warning. */\n\t#define __BYTE_ORDER  __BIG_ENDIAN\n#elif defined(MM_LITTLE_ENDIAN)\n\t#undef __BYTE_ORDER /* Avoid redefined macro compiler warning. */\n\t#define __BYTE_ORDER __LITTLE_ENDIAN\n#endif\n\n/* Define default endian-ness. */\n#ifndef __LITTLE_ENDIAN\n\t#define __LITTLE_ENDIAN 1234\n#endif /* __LITTLE_ENDIAN */\n\n#ifndef __BIG_ENDIAN\n\t#define __BIG_ENDIAN 4321\n#endif /* __BIG_ENDIAN */\n\n#ifndef __BYTE_ORDER\n\t#warning \"Byte order not defined on your system; assuming little endian\"\n\t#define __BYTE_ORDER __LITTLE_ENDIAN\n#endif /* __BYTE_ORDER */\n\n#if __BYTE_ORDER != __BIG_ENDIAN && __BYTE_ORDER != __LITTLE_ENDIAN\n\t#error \"__BYTE_ORDER set to unknown byte order\"\n#endif\n\n#if defined(IS_MACOSX)\n\t#include <libkern/OSByteOrder.h>\n\n\t/* OS X system functions. */\n\t#define bitswap16(i) OSSwapInt16(i)\n\t#define bitswap32(i) OSSwapInt32(i)\n\t#define swapLittleAndHost32(i) OSSwapLittleToHostInt32(i)\n\t#define swapLittleAndHost16(i) OSSwapLittleToHostInt16(i)\n#else\n\t#ifndef bitswap16\n\t\t#if defined(bswap16)\n\t\t\t#define bitswap16(i) bswap16(i) /* FreeBSD system function */\n\t\t#elif defined(bswap_16)\n\t\t\t#define bitswap16(i) bswap_16(i) /* Linux system function */\n\t\t#else /* Default macro */\n\t\t\t#define bitswap16(i) (((uint16_t)(i) & 0xFF00) >> 8) | \\\n \t\t                          (((uint16_t)(i) & 0x00FF) << 8)\n\t\t#endif\n\t#endif /* bitswap16 */\n\n\t#ifndef bitswap32\n\t\t#if defined(bswap32)\n\t\t\t#define bitswap32(i) bswap32(i) /* FreeBSD system function. */\n\t\t#elif defined(bswap_32)\n\t\t\t#define bitswap32(i) bswap_32(i) /* Linux system function. */\n\t\t#else /* Default macro */\n\t\t\t#define bitswap32(i) (((uint32_t)(i) & 0xFF000000) >> 24) | \\\n\t\t\t                      ((uint32_t)((i) & 0x00FF0000) >> 8) | \\\n\t\t\t                      ((uint32_t)((i) & 0x0000FF00) << 8) | \\\n\t\t\t                      ((uint32_t)((i) & 0x000000FF) << 24)\n\t\t#endif\n\t#endif /* bitswap32 */\n#endif\n\n#if __BYTE_ORDER == __BIG_ENDIAN\n\t/* Little endian to/from host byte order (big endian). */\n\t#ifndef swapLittleAndHost16\n\t\t#define swapLittleAndHost16(i) bitswap16(i)\n\t#endif /* swapLittleAndHost16 */\n\n\t#ifndef swapLittleAndHost32\n\t\t#define swapLittleAndHost32(i) bitswap32(i)\n\t#endif /* swapLittleAndHost32 */\n#elif __BYTE_ORDER == __LITTLE_ENDIAN\n\t/* We are already little endian, so no conversion is needed. */\n\t#ifndef swapLittleAndHost16\n\t\t#define swapLittleAndHost16(i) i\n\t#endif /* swapLittleAndHost16 */\n\n\t#ifndef swapLittleAndHost32\n\t\t#define swapLittleAndHost32(i) i\n\t#endif /* swapLittleAndHost32 */\n#endif\n\n#endif /* ENDIAN_H */\n"
  },
  {
    "path": "src/inline_keywords.h",
    "content": "#pragma once\n\n/* A complicated, portable model for declaring inline functions in\n * header files. */\n#if !defined(H_INLINE)\n    #if defined(__GNUC__)\n        #define H_INLINE static __inline__ __attribute__((always_inline))\n    #elif defined(__MWERKS__) || defined(__cplusplus)\n        #define H_INLINE static inline\n    #elif defined(_MSC_VER)\n        #define H_INLINE static __inline\n    #elif TARGET_OS_WIN32\n        #define H_INLINE static __inline__\n    #endif\n#endif /* H_INLINE */\n"
  },
  {
    "path": "src/io.c",
    "content": "#include \"io.h\"\n#include \"os.h\"\n#include \"bmp_io.h\"\n#include \"png_io.h\"\n#include <stdio.h> /* For fputs() */\n#include <string.h> /* For strcmp() */\n#include <ctype.h> /* For tolower() */\n\nconst char *getExtension(const char *fname, size_t len)\n{\n\tif (fname == NULL || len <= 0) return NULL;\n\n\twhile (--len > 0 && fname[len] != '.' && fname[len] != '\\0')\n\t\t;\n\n\treturn fname + len + 1;\n}\n\nMMImageType imageTypeFromExtension(const char *extension)\n{\n\tchar ext[4];\n\tconst size_t maxlen = sizeof(ext) / sizeof(ext[0]);\n\tsize_t i;\n\n\tfor (i = 0; extension[i] != '\\0'; ++i) {\n\t\tif (i >= maxlen) return kInvalidImageType;\n\t\text[i] = tolower(extension[i]);\n\t}\n\text[i] = '\\0';\n\n\tif (strcmp(ext, \"png\") == 0) {\n\t\treturn kPNGImageType;\n\t} else if (strcmp(ext, \"bmp\") == 0) {\n\t\treturn kBMPImageType;\n\t} else {\n\t\treturn kInvalidImageType;\n\t}\n}\n\nMMBitmapRef newMMBitmapFromFile(const char *path, \n                                MMImageType type,\n                                MMIOError *err)\n{\n\tswitch (type) {\n\t\tcase kBMPImageType:\n\t\t\treturn newMMBitmapFromBMP(path, err);\n\t\tcase kPNGImageType:\n\t\t\treturn newMMBitmapFromPNG(path, err);\n\t\tdefault:\n\t\t\tif (err != NULL) *err = kMMIOUnsupportedTypeError;\n\t\t\treturn NULL;\n\t}\n}\n\nint saveMMBitmapToFile(MMBitmapRef bitmap,\n                       const char *path,\n                       MMImageType type)\n{\n\tswitch (type) {\n\t\tcase kBMPImageType:\n\t\t\treturn saveMMBitmapAsBMP(bitmap, path);\n\t\tcase kPNGImageType:\n\t\t\treturn saveMMBitmapAsPNG(bitmap, path);\n\t\tdefault:\n\t\t\treturn -1;\n\t}\n}\n\nconst char *MMIOErrorString(MMImageType type, MMIOError error)\n{\n\tswitch (type) {\n\t\tcase kBMPImageType:\n\t\t\treturn MMBMPReadErrorString(error);\n\t\tcase kPNGImageType:\n\t\t\treturn MMPNGReadErrorString(error);\n\t\tdefault:\n\t\t\treturn \"Unsupported image type\";\n\t}\n}\n"
  },
  {
    "path": "src/io.h",
    "content": "#pragma once\n#ifndef IO_H\n#define IO_H\n\n#include \"MMBitmap.h\"\n#include <stddef.h>\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" \n{\n#endif\n\nenum _MMImageType {\n\tkInvalidImageType = 0,\n\tkPNGImageType,\n\tkBMPImageType /* Currently only PNG and BMP are supported. */\n};\n\ntypedef uint16_t MMImageType;\n\nenum _MMIOError {\n\tkMMIOUnsupportedTypeError = 0\n};\n\ntypedef uint16_t MMIOError;\n\nconst char *getExtension(const char *fname, size_t len);\n\n/* Returns best guess at the MMImageType based on a file extension, or\n * |kInvalidImageType| if no matching type was found. */\nMMImageType imageTypeFromExtension(const char *ext);\n\n/* Attempts to parse the file of the given type at the given path.\n * |filepath| is an ASCII string describing the absolute POSIX path.\n * Returns new bitmap (to be destroy()'d by caller) on success, NULL on error.\n * If |error| is non-NULL, it will be set to the error code on return.\n */\nMMBitmapRef newMMBitmapFromFile(const char *path, MMImageType type, MMIOError *err);\n\n/* Saves |bitmap| to a file of the given type at the given path.\n * |filepath| is an ASCII string describing the absolute POSIX path.\n * Returns 0 on success, -1 on error. */\nint saveMMBitmapToFile(MMBitmapRef bitmap, const char *path, MMImageType type);\n\n/* Returns description of given error code.\n * Returned string is constant and hence should not be freed. */\nconst char *MMIOErrorString(MMImageType type, MMIOError error);\n\n#ifdef __cplusplus\n}\n#endif\n\n\n#endif /* IO_H */\n"
  },
  {
    "path": "src/keycode.c",
    "content": "#include \"keycode.h\"\n\n#if defined(IS_MACOSX)\n\n#include <CoreFoundation/CoreFoundation.h>\n#include <Carbon/Carbon.h> /* For kVK_ constants, and TIS functions. */\n\n/* Returns string representation of key, if it is printable.\n * Ownership follows the Create Rule; that is, it is the caller's\n * responsibility to release the returned object. */\nCFStringRef createStringForKey(CGKeyCode keyCode);\n\n#elif defined(USE_X11)\n\n/*\n * Structs to store key mappings not handled by XStringToKeysym() on some\n * Linux systems.\n */\n\nstruct XSpecialCharacterMapping {\n\tchar name;\n\tMMKeyCode code;\n};\n\nstruct XSpecialCharacterMapping XSpecialCharacterTable[] = {\n\t{'~', XK_asciitilde},\n  \t{'_', XK_underscore},\n  \t{'[', XK_bracketleft},\n  \t{']', XK_bracketright},\n  \t{'!', XK_exclam},\n  \t{'\\'', XK_quotedbl},\n  \t{'#', XK_numbersign},\n  \t{'$', XK_dollar},\n  \t{'%', XK_percent},\n  \t{'&', XK_ampersand},\n  \t{'\\'', XK_quoteright},\n  \t{'*', XK_asterisk},\n  \t{'+', XK_plus},\n  \t{',', XK_comma},\n  \t{'-', XK_minus},\n  \t{'.', XK_period},\n  \t{'?', XK_question},\n  \t{'<', XK_less},\n  \t{'>', XK_greater},\n  \t{'=', XK_equal},\n  \t{'@', XK_at},\n  \t{':', XK_colon},\n  \t{';', XK_semicolon},\n  \t{'\\\\', XK_backslash},\n  \t{'`', XK_grave},\n  \t{'{', XK_braceleft},\n  \t{'}', XK_braceright},\n  \t{'|', XK_bar},\n  \t{'^', XK_asciicircum},\n  \t{'(', XK_parenleft},\n  \t{')', XK_parenright},\n  \t{' ', XK_space},\n  \t{'/', XK_slash},\n  \t{'\\t', XK_Tab},\n  \t{'\\n', XK_Return}\n};\n\n#endif\n\nMMKeyCode keyCodeForChar(const char c)\n{\n#if defined(IS_MACOSX)\n\t/* OS X does not appear to have a built-in function for this, so instead we\n\t * have to write our own. */\n\tstatic CFMutableDictionaryRef charToCodeDict = NULL;\n\tsize_t code;\n\tUniChar character = c;\n\tCFStringRef charStr = NULL;\n\n\t/* Generate table of keycodes and characters. */\n\tif (charToCodeDict == NULL) {\n\t\tsize_t i;\n\t\tcharToCodeDict = CFDictionaryCreateMutable(kCFAllocatorDefault,\n\t\t                                           128,\n\t\t                                           &kCFCopyStringDictionaryKeyCallBacks,\n\t\t                                           NULL);\n\t\tif (charToCodeDict == NULL) return UINT16_MAX;\n\n\t\t/* Loop through every keycode (0 - 127) to find its current mapping. */\n\t\tfor (i = 0; i < 128; ++i) {\n\t\t\tCFStringRef string = createStringForKey((CGKeyCode)i);\n\t\t\tif (string != NULL) {\n\t\t\t\tCFDictionaryAddValue(charToCodeDict, string, (const void *)i);\n\t\t\t\tCFRelease(string);\n\t\t\t}\n\t\t}\n\t}\n\n\tcharStr = CFStringCreateWithCharacters(kCFAllocatorDefault, &character, 1);\n\n\t/* Our values may be NULL (0), so we need to use this function. */\n\tif (!CFDictionaryGetValueIfPresent(charToCodeDict, charStr,\n\t                                   (const void **)&code)) {\n\t\tcode = UINT16_MAX; /* Error */\n\t}\n\n\tCFRelease(charStr);\n\treturn (MMKeyCode)code;\n#elif defined(IS_WINDOWS)\n\treturn VkKeyScan(c);\n#elif defined(USE_X11)\n\tMMKeyCode code;\n\n\tchar buf[2];\n\tbuf[0] = c;\n\tbuf[1] = '\\0';\n\n\tcode = XStringToKeysym(buf);\n\tif (code == NoSymbol) {\n\t\t/* Some special keys are apparently not handled properly by\n\t\t * XStringToKeysym() on some systems, so search for them instead in our\n\t\t * mapping table. */\n\t\tsize_t i;\n\t\tconst size_t specialCharacterCount =\n\t\t\tsizeof(XSpecialCharacterTable) / sizeof(XSpecialCharacterTable[0]);\n\t\tfor (i = 0; i < specialCharacterCount; ++i) {\n\t\t\tif (c == XSpecialCharacterTable[i].name) {\n\t\t\t\tcode = XSpecialCharacterTable[i].code;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn code;\n#endif\n}\n\n#if defined(IS_MACOSX)\n\nCFStringRef createStringForKey(CGKeyCode keyCode)\n{\n\tTISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardLayoutInputSource();\n\tCFDataRef layoutData =\n\t\tTISGetInputSourceProperty(currentKeyboard,\n\t\t                          kTISPropertyUnicodeKeyLayoutData);\n\tconst UCKeyboardLayout *keyboardLayout =\n\t\t(const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);\n\n\tUInt32 keysDown = 0;\n\tUniChar chars[4];\n\tUniCharCount realLength;\n\n\tUCKeyTranslate(keyboardLayout,\n\t               keyCode,\n\t               kUCKeyActionDisplay,\n\t               0,\n\t               LMGetKbdType(),\n\t               kUCKeyTranslateNoDeadKeysBit,\n\t               &keysDown,\n\t               sizeof(chars) / sizeof(chars[0]),\n\t               &realLength,\n\t               chars);\n\tCFRelease(currentKeyboard);\n\n\treturn CFStringCreateWithCharacters(kCFAllocatorDefault, chars, 1);\n}\n\n#endif\n"
  },
  {
    "path": "src/keycode.h",
    "content": "#pragma once\n#ifndef KEYCODE_H\n#define KEYCODE_H\n\n#include \"os.h\"\n\n#ifdef __cplusplus\nextern \"C\" \n{\n#endif\n\n#if defined(IS_MACOSX)\n\n#include <Carbon/Carbon.h> /* Really only need <HIToolbox/Events.h> */\n#include <ApplicationServices/ApplicationServices.h>\n#import <IOKit/hidsystem/ev_keymap.h>\n\t\nenum _MMKeyCode {\n\tK_NOT_A_KEY = 9999,\n\tK_BACKSPACE = kVK_Delete,\n\tK_DELETE = kVK_ForwardDelete,\n\tK_RETURN = kVK_Return,\n\tK_TAB = kVK_Tab,\n\tK_ESCAPE = kVK_Escape,\n\tK_UP = kVK_UpArrow,\n\tK_DOWN = kVK_DownArrow,\n\tK_RIGHT = kVK_RightArrow,\n\tK_LEFT = kVK_LeftArrow,\n\tK_HOME = kVK_Home,\n\tK_END = kVK_End,\n\tK_PAGEUP = kVK_PageUp,\n\tK_PAGEDOWN = kVK_PageDown,\n\tK_F1 = kVK_F1,\n\tK_F2 = kVK_F2,\n\tK_F3 = kVK_F3,\n\tK_F4 = kVK_F4,\n\tK_F5 = kVK_F5,\n\tK_F6 = kVK_F6,\n\tK_F7 = kVK_F7,\n\tK_F8 = kVK_F8,\n\tK_F9 = kVK_F9,\n\tK_F10 = kVK_F10,\n\tK_F11 = kVK_F11,\n\tK_F12 = kVK_F12,\n\tK_F13 = kVK_F13,\n\tK_F14 = kVK_F14,\n\tK_F15 = kVK_F15,\n\tK_F16 = kVK_F16,\n\tK_F17 = kVK_F17,\n\tK_F18 = kVK_F18,\n\tK_F19 = kVK_F19,\n\tK_F20 = kVK_F20,\n\tK_F21 = K_NOT_A_KEY,\n\tK_F22 = K_NOT_A_KEY,\n\tK_F23 = K_NOT_A_KEY,\n\tK_F24 = K_NOT_A_KEY,\n\tK_META = kVK_Command,\n\tK_ALT = kVK_Option,\n\tK_RIGHT_ALT = kVK_Option,\n\tK_CONTROL = kVK_Control,\n\tK_LEFT_CONTROL = kVK_Control,\n\tK_RIGHT_CONTROL = kVK_RightControl,\n\tK_SHIFT = kVK_Shift,\n\tK_RIGHTSHIFT = kVK_RightShift,\n\tK_CAPSLOCK = kVK_CapsLock,\n\tK_SPACE = kVK_Space,\n\tK_INSERT = K_NOT_A_KEY,\n\tK_PRINTSCREEN = K_NOT_A_KEY,\n\tK_MENU = K_NOT_A_KEY,\n\n\tK_NUMPAD_LOCK = K_NOT_A_KEY,\n\tK_NUMPAD_0 = kVK_ANSI_Keypad0,\n\tK_NUMPAD_1 = kVK_ANSI_Keypad1,\n\tK_NUMPAD_2 = kVK_ANSI_Keypad2,\n\tK_NUMPAD_3 = kVK_ANSI_Keypad3,\n\tK_NUMPAD_4 = kVK_ANSI_Keypad4,\n\tK_NUMPAD_5 = kVK_ANSI_Keypad5,\n\tK_NUMPAD_6 = kVK_ANSI_Keypad6,\n\tK_NUMPAD_7 = kVK_ANSI_Keypad7,\n\tK_NUMPAD_8 = kVK_ANSI_Keypad8,\n\tK_NUMPAD_9 = kVK_ANSI_Keypad9,\n\tK_NUMPAD_PLUS = kVK_ANSI_KeypadPlus,\n\tK_NUMPAD_MINUS = kVK_ANSI_KeypadMinus,\n\tK_NUMPAD_MULTIPLY = kVK_ANSI_KeypadMultiply,\n\tK_NUMPAD_DIVIDE = kVK_ANSI_KeypadDivide,\n\tK_NUMPAD_DECIMAL = kVK_ANSI_KeypadDecimal,\n\n\tK_AUDIO_VOLUME_MUTE = 1007,\n\tK_AUDIO_VOLUME_DOWN = 1001,\n\tK_AUDIO_VOLUME_UP = 1000,\n\tK_AUDIO_PLAY = 1016,\n\tK_AUDIO_STOP = K_NOT_A_KEY,\n\tK_AUDIO_PAUSE = 1016,\n\tK_AUDIO_PREV = 1018,\n\tK_AUDIO_NEXT = 1017,\n\tK_AUDIO_REWIND = K_NOT_A_KEY,\n\tK_AUDIO_FORWARD = K_NOT_A_KEY,\n\tK_AUDIO_REPEAT = K_NOT_A_KEY,\n\tK_AUDIO_RANDOM = K_NOT_A_KEY,\n\n\tK_LIGHTS_MON_UP = 1002,\n\tK_LIGHTS_MON_DOWN = 1003,\n\tK_LIGHTS_KBD_TOGGLE = 1023,\n\tK_LIGHTS_KBD_UP = 1021,\n\tK_LIGHTS_KBD_DOWN = 1022\n};\n\ntypedef CGKeyCode MMKeyCode;\n\n#elif defined(USE_X11)\n\n#include <X11/Xutil.h>\n#include <X11/XF86keysym.h>\n\nenum _MMKeyCode {\n\tK_NOT_A_KEY = 9999,\n\tK_BACKSPACE = XK_BackSpace,\n\tK_DELETE = XK_Delete,\n\tK_RETURN = XK_Return,\n\tK_TAB = XK_Tab,\n\tK_ESCAPE = XK_Escape,\n\tK_UP = XK_Up,\n\tK_DOWN = XK_Down,\n\tK_RIGHT = XK_Right,\n\tK_LEFT = XK_Left,\n\tK_HOME = XK_Home,\n\tK_END = XK_End,\n\tK_PAGEUP = XK_Page_Up,\n\tK_PAGEDOWN = XK_Page_Down,\n\tK_F1 = XK_F1,\n\tK_F2 = XK_F2,\n\tK_F3 = XK_F3,\n\tK_F4 = XK_F4,\n\tK_F5 = XK_F5,\n\tK_F6 = XK_F6,\n\tK_F7 = XK_F7,\n\tK_F8 = XK_F8,\n\tK_F9 = XK_F9,\n\tK_F10 = XK_F10,\n\tK_F11 = XK_F11,\n\tK_F12 = XK_F12,\n\tK_F13 = XK_F13,\n\tK_F14 = XK_F14,\n\tK_F15 = XK_F15,\n\tK_F16 = XK_F16,\n\tK_F17 = XK_F17,\n\tK_F18 = XK_F18,\n\tK_F19 = XK_F19,\n\tK_F20 = XK_F20,\n\tK_F21 = XK_F21,\n\tK_F22 = XK_F22,\n\tK_F23 = XK_F23,\n\tK_F24 = XK_F24,\n\tK_META = XK_Super_L,\n\tK_ALT = XK_Alt_L,\n\tK_RIGHT_ALT = XK_Alt_R,\n\tK_CONTROL = XK_Control_L,\n\tK_LEFT_CONTROL = XK_Control_L,\n\tK_RIGHT_CONTROL = XK_Control_R,\n\tK_SHIFT = XK_Shift_L,\n\tK_RIGHTSHIFT = XK_Shift_R,\n\tK_CAPSLOCK = XK_Shift_Lock,\n\tK_SPACE = XK_space,\n\tK_INSERT = XK_Insert,\n\tK_PRINTSCREEN = XK_Print,\n\tK_MENU = K_NOT_A_KEY,\n\n\tK_NUMPAD_LOCK = K_NOT_A_KEY,\n\tK_NUMPAD_0 = K_NOT_A_KEY,\n\tK_NUMPAD_1 = K_NOT_A_KEY,\n\tK_NUMPAD_2 = K_NOT_A_KEY,\n\tK_NUMPAD_3 = K_NOT_A_KEY,\n\tK_NUMPAD_4 = K_NOT_A_KEY,\n\tK_NUMPAD_5 = K_NOT_A_KEY,\n\tK_NUMPAD_6 = K_NOT_A_KEY,\n\tK_NUMPAD_7 = K_NOT_A_KEY,\n\tK_NUMPAD_8 = K_NOT_A_KEY,\n\tK_NUMPAD_9 = K_NOT_A_KEY,\n\tK_NUMPAD_PLUS = K_NOT_A_KEY,\n\tK_NUMPAD_MINUS = K_NOT_A_KEY,\n\tK_NUMPAD_MULTIPLY = K_NOT_A_KEY,\n\tK_NUMPAD_DIVIDE = K_NOT_A_KEY,\n\tK_NUMPAD_DECIMAL = K_NOT_A_KEY,\n\n\tK_AUDIO_VOLUME_MUTE = XF86XK_AudioMute,\n\tK_AUDIO_VOLUME_DOWN = XF86XK_AudioLowerVolume,\n\tK_AUDIO_VOLUME_UP = XF86XK_AudioRaiseVolume,\n\tK_AUDIO_PLAY = XF86XK_AudioPlay,\n\tK_AUDIO_STOP = XF86XK_AudioStop,\n\tK_AUDIO_PAUSE = XF86XK_AudioPause,\n\tK_AUDIO_PREV = XF86XK_AudioPrev,\n\tK_AUDIO_NEXT = XF86XK_AudioNext,\n\tK_AUDIO_REWIND = XF86XK_AudioRewind,\n\tK_AUDIO_FORWARD = XF86XK_AudioForward,\n\tK_AUDIO_REPEAT = XF86XK_AudioRepeat,\n\tK_AUDIO_RANDOM = XF86XK_AudioRandomPlay,\n\n\tK_LIGHTS_MON_UP = XF86XK_MonBrightnessUp,\n\tK_LIGHTS_MON_DOWN = XF86XK_MonBrightnessDown,\n\tK_LIGHTS_KBD_TOGGLE = XF86XK_KbdLightOnOff,\n\tK_LIGHTS_KBD_UP = XF86XK_KbdBrightnessUp,\n\tK_LIGHTS_KBD_DOWN = XF86XK_KbdBrightnessDown\n};\n\ntypedef KeySym MMKeyCode;\n\n#elif defined(IS_WINDOWS)\n\nenum _MMKeyCode {\n\tK_NOT_A_KEY = 9999,\n\tK_BACKSPACE = VK_BACK,\n\tK_DELETE = VK_DELETE,\n\tK_RETURN = VK_RETURN,\n\tK_TAB = VK_TAB,\n\tK_ESCAPE = VK_ESCAPE,\n\tK_UP = VK_UP,\n\tK_DOWN = VK_DOWN,\n\tK_RIGHT = VK_RIGHT,\n\tK_LEFT = VK_LEFT,\n\tK_HOME = VK_HOME,\n\tK_END = VK_END,\n\tK_PAGEUP = VK_PRIOR,\n\tK_PAGEDOWN = VK_NEXT,\n\tK_F1 = VK_F1,\n\tK_F2 = VK_F2,\n\tK_F3 = VK_F3,\n\tK_F4 = VK_F4,\n\tK_F5 = VK_F5,\n\tK_F6 = VK_F6,\n\tK_F7 = VK_F7,\n\tK_F8 = VK_F8,\n\tK_F9 = VK_F9,\n\tK_F10 = VK_F10,\n\tK_F11 = VK_F11,\n\tK_F12 = VK_F12,\n\tK_F13 = VK_F13,\n\tK_F14 = VK_F14,\n\tK_F15 = VK_F15,\n\tK_F16 = VK_F16,\n\tK_F17 = VK_F17,\n\tK_F18 = VK_F18,\n\tK_F19 = VK_F19,\n\tK_F20 = VK_F20,\n\tK_F21 = VK_F21,\n\tK_F22 = VK_F22,\n\tK_F23 = VK_F23,\n\tK_F24 = VK_F24,\n\tK_META = VK_LWIN,\n\tK_CONTROL = VK_CONTROL,\n\tK_LEFT_CONTROL = VK_LCONTROL,\n\tK_RIGHT_CONTROL = VK_RCONTROL,\n\tK_SHIFT = VK_SHIFT,\n\tK_RIGHTSHIFT = VK_RSHIFT,\n\tK_ALT = VK_MENU,\n\tK_RIGHT_ALT = VK_MENU,\n\tK_CAPSLOCK = VK_CAPITAL,\n\tK_SPACE = VK_SPACE,\n\tK_PRINTSCREEN = VK_SNAPSHOT,\n\tK_INSERT = VK_INSERT,\n\tK_MENU = VK_APPS,\n\n\tK_NUMPAD_LOCK = VK_NUMLOCK,\n\tK_NUMPAD_0 = VK_NUMPAD0,\n\tK_NUMPAD_1 = VK_NUMPAD1,\n\tK_NUMPAD_2 = VK_NUMPAD2,\n\tK_NUMPAD_3 = VK_NUMPAD3,\n\tK_NUMPAD_4 = VK_NUMPAD4,\n\tK_NUMPAD_5 = VK_NUMPAD5,\n\tK_NUMPAD_6 = VK_NUMPAD6,\n\tK_NUMPAD_7 = VK_NUMPAD7,\n\tK_NUMPAD_8 = VK_NUMPAD8,\n\tK_NUMPAD_9 = VK_NUMPAD9,\n\tK_NUMPAD_PLUS = VK_ADD,\n\tK_NUMPAD_MINUS = VK_SUBTRACT,\n\tK_NUMPAD_MULTIPLY = VK_MULTIPLY,\n\tK_NUMPAD_DIVIDE = VK_DIVIDE,\n\tK_NUMPAD_DECIMAL = VK_DECIMAL,\n\n\tK_AUDIO_VOLUME_MUTE = VK_VOLUME_MUTE,\n\tK_AUDIO_VOLUME_DOWN = VK_VOLUME_DOWN,\n\tK_AUDIO_VOLUME_UP = VK_VOLUME_UP,\n\tK_AUDIO_PLAY = VK_MEDIA_PLAY_PAUSE,\n\tK_AUDIO_STOP = VK_MEDIA_STOP,\n\tK_AUDIO_PAUSE = VK_MEDIA_PLAY_PAUSE,\n\tK_AUDIO_PREV = VK_MEDIA_PREV_TRACK,\n\tK_AUDIO_NEXT = VK_MEDIA_NEXT_TRACK,\n\tK_AUDIO_REWIND = K_NOT_A_KEY,\n\tK_AUDIO_FORWARD = K_NOT_A_KEY,\n\tK_AUDIO_REPEAT = K_NOT_A_KEY,\n\tK_AUDIO_RANDOM = K_NOT_A_KEY,\n\n\tK_LIGHTS_MON_UP = K_NOT_A_KEY,\n\tK_LIGHTS_MON_DOWN = K_NOT_A_KEY,\n\tK_LIGHTS_KBD_TOGGLE = K_NOT_A_KEY,\n\tK_LIGHTS_KBD_UP = K_NOT_A_KEY,\n\tK_LIGHTS_KBD_DOWN = K_NOT_A_KEY\n};\n\ntypedef int MMKeyCode;\n\n#endif\n\n/* Returns the keyCode corresponding to the current keyboard layout for the\n * given ASCII character. */\nMMKeyCode keyCodeForChar(const char c);\n\n#endif /* KEYCODE_H */\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "src/keypress.c",
    "content": "#include \"keypress.h\"\n#include \"deadbeef_rand.h\"\n#include \"microsleep.h\"\n\n#include <ctype.h> /* For isupper() */\n\n#if defined(IS_MACOSX)\n\t#include <ApplicationServices/ApplicationServices.h>\n\t#import <IOKit/hidsystem/IOHIDLib.h>\n\t#import <IOKit/hidsystem/ev_keymap.h>\n#elif defined(USE_X11)\n\t#include <X11/extensions/XTest.h>\n\t#include \"xdisplay.h\"\n#endif\n\n/* Convenience wrappers around ugly APIs. */\n#if defined(IS_WINDOWS)\n\t#define WIN32_KEY_EVENT_WAIT(key, flags) \\\n\t\t(win32KeyEvent(key, flags))\n#elif defined(USE_X11)\n\t#define X_KEY_EVENT(display, key, is_press) \\\n\t\t(XTestFakeKeyEvent(display, \\\n\t\t                   XKeysymToKeycode(display, key), \\\n\t\t                   is_press, CurrentTime), \\\n\t\t XFlush(display))\n\t#define X_KEY_EVENT_WAIT(display, key, is_press) \\\n\t\t(X_KEY_EVENT(display, key, is_press))\n#endif\n\n#if defined(IS_MACOSX)\nstatic io_connect_t _getAuxiliaryKeyDriver(void)\n{\n\tstatic mach_port_t sEventDrvrRef = 0;\n\tmach_port_t masterPort, service, iter;\n\tkern_return_t kr;\n\n\tif (!sEventDrvrRef) {\n\t\tkr = IOMasterPort( bootstrap_port, &masterPort );\n\t\tassert(KERN_SUCCESS == kr);\n\t\tkr = IOServiceGetMatchingServices(masterPort, IOServiceMatching( kIOHIDSystemClass), &iter );\n\t\tassert(KERN_SUCCESS == kr);\n\t\tservice = IOIteratorNext( iter );\n\t\tassert(service);\n\t\tkr = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &sEventDrvrRef );\n\t\tassert(KERN_SUCCESS == kr);\n\t\tIOObjectRelease(service);\n\t\tIOObjectRelease(iter);\n\t}\n\treturn sEventDrvrRef;\n}\n#endif\n\n#if defined(IS_WINDOWS)\nvoid win32KeyEvent(int key, MMKeyFlags flags)\n{\n\tint scan = MapVirtualKey(key & 0xff, MAPVK_VK_TO_VSC);\n\n\t/* Set the scan code for extended keys */\n\tswitch (key)\n\t{\n\t\tcase VK_RCONTROL:\n\t\tcase VK_SNAPSHOT: /* Print Screen */\n\t\tcase VK_RMENU: /* Right Alt / Alt Gr */\n\t\tcase VK_PAUSE: /* Pause / Break */\n\t\tcase VK_HOME:\n\t\tcase VK_UP:\n\t\tcase VK_PRIOR: /* Page up */\n\t\tcase VK_LEFT:\n\t\tcase VK_RIGHT:\n\t\tcase VK_END:\n\t\tcase VK_DOWN:\n\t\tcase VK_NEXT: /* 'Page Down' */\n\t\tcase VK_INSERT:\n\t\tcase VK_DELETE:\n\t\tcase VK_LWIN:\n\t\tcase VK_RWIN:\n\t\tcase VK_APPS: /* Application */\n\t\tcase VK_VOLUME_MUTE:\n\t\tcase VK_VOLUME_DOWN:\n\t\tcase VK_VOLUME_UP:\n\t\tcase VK_MEDIA_NEXT_TRACK:\n\t\tcase VK_MEDIA_PREV_TRACK:\n\t\tcase VK_MEDIA_STOP:\n\t\tcase VK_MEDIA_PLAY_PAUSE:\n\t\tcase VK_BROWSER_BACK:\n\t\tcase VK_BROWSER_FORWARD:\n\t\tcase VK_BROWSER_REFRESH:\n\t\tcase VK_BROWSER_STOP:\n\t\tcase VK_BROWSER_SEARCH:\n\t\tcase VK_BROWSER_FAVORITES:\n\t\tcase VK_BROWSER_HOME:\n\t\tcase VK_LAUNCH_MAIL:\n\t\t{\n\t\t\tflags |= KEYEVENTF_EXTENDEDKEY;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tflags |= KEYEVENTF_SCANCODE;\n\n\tINPUT keyboardInput;\n\tkeyboardInput.type = INPUT_KEYBOARD;\n\tkeyboardInput.ki.wVk = 0;\n\tkeyboardInput.ki.wScan = scan;\n\tkeyboardInput.ki.dwFlags = flags;\n\tkeyboardInput.ki.time = 0;\n\tkeyboardInput.ki.dwExtraInfo = 0;\n\tSendInput(1, &keyboardInput, sizeof(keyboardInput));\n}\n#endif\n\nvoid toggleKeyCode(MMKeyCode code, const bool down, MMKeyFlags flags)\n{\n#if defined(IS_MACOSX)\n\t/* The media keys all have 1000 added to them to help us detect them. */\n\tif (code >= 1000) {\n\t\tcode = code - 1000; /* Get the real keycode. */\n\t\tNXEventData   event;\n\t\tkern_return_t kr;\n\t\tIOGPoint loc = { 0, 0 };\n\t\tUInt32 evtInfo = code << 16 | (down?NX_KEYDOWN:NX_KEYUP) << 8;\n\t\tbzero(&event, sizeof(NXEventData));\n\t\tevent.compound.subType = NX_SUBTYPE_AUX_CONTROL_BUTTONS;\n\t\tevent.compound.misc.L[0] = evtInfo;\n\t\tkr = IOHIDPostEvent( _getAuxiliaryKeyDriver(), NX_SYSDEFINED, loc, &event, kNXEventDataVersion, 0, FALSE );\n\t\tassert( KERN_SUCCESS == kr );\n\t} else {\n\t\tCGEventRef keyEvent = CGEventCreateKeyboardEvent(NULL,\n\t\t                                                 (CGKeyCode)code, down);\n\t\tassert(keyEvent != NULL);\n\n\t\tCGEventSetType(keyEvent, down ? kCGEventKeyDown : kCGEventKeyUp);\n\t\tCGEventSetFlags(keyEvent, flags);\n\t\tCGEventPost(kCGSessionEventTap, keyEvent);\n\t\tCFRelease(keyEvent);\n\t}\n#elif defined(IS_WINDOWS)\n\tconst DWORD dwFlags = down ? 0 : KEYEVENTF_KEYUP;\n\n\tif (down) {\n\t\t/* Parse modifier keys. */\n\t\tif (flags & MOD_META) WIN32_KEY_EVENT_WAIT(K_META, dwFlags);\n\t\tif (flags & MOD_ALT) WIN32_KEY_EVENT_WAIT(K_ALT, dwFlags);\n\t\tif (flags & MOD_CONTROL) WIN32_KEY_EVENT_WAIT(K_CONTROL, dwFlags);\n\t\tif (flags & MOD_SHIFT) WIN32_KEY_EVENT_WAIT(K_SHIFT, dwFlags);\n\n\t\tWIN32_KEY_EVENT_WAIT(code, dwFlags);\n\t} else {\n\t\t/* Reverse order for key up */\n\t\tWIN32_KEY_EVENT_WAIT(code, dwFlags);\n\n\t\t/* Parse modifier keys. */\n\t\tif (flags & MOD_META) win32KeyEvent(K_META, dwFlags);\n\t\tif (flags & MOD_ALT) win32KeyEvent(K_ALT, dwFlags);\n\t\tif (flags & MOD_CONTROL) win32KeyEvent(K_CONTROL, dwFlags);\n\t\tif (flags & MOD_SHIFT) win32KeyEvent(K_SHIFT, dwFlags);\n\t}\n#elif defined(USE_X11)\n\tDisplay *display = XGetMainDisplay();\n\tconst Bool is_press = down ? True : False; /* Just to be safe. */\n\n\tif (down) {\n\t\t/* Parse modifier keys. */\n\t\tif (flags & MOD_META) X_KEY_EVENT_WAIT(display, K_META, is_press);\n\t\tif (flags & MOD_ALT) X_KEY_EVENT_WAIT(display, K_ALT, is_press);\n\t\tif (flags & MOD_CONTROL) X_KEY_EVENT_WAIT(display, K_CONTROL, is_press);\n\t\tif (flags & MOD_SHIFT) X_KEY_EVENT_WAIT(display, K_SHIFT, is_press);\n\n\t\tX_KEY_EVENT_WAIT(display, code, is_press);\n\t} else {\n\t\t/* Reverse order for key up */\n\t\tX_KEY_EVENT_WAIT(display, code, is_press);\n\n\t\t/* Parse modifier keys. */\n\t\tif (flags & MOD_META) X_KEY_EVENT(display, K_META, is_press);\n\t\tif (flags & MOD_ALT) X_KEY_EVENT(display, K_ALT, is_press);\n\t\tif (flags & MOD_CONTROL) X_KEY_EVENT(display, K_CONTROL, is_press);\n\t\tif (flags & MOD_SHIFT) X_KEY_EVENT(display, K_SHIFT, is_press);\n\t}\n#endif\n}\n\nvoid tapKeyCode(MMKeyCode code, MMKeyFlags flags)\n{\n\ttoggleKeyCode(code, true, flags);\n\ttoggleKeyCode(code, false, flags);\n}\n\nvoid toggleKey(char c, const bool down, MMKeyFlags flags)\n{\n\tMMKeyCode keyCode = keyCodeForChar(c);\n\n\t//Prevent unused variable warning for Mac and Linux.\n#if defined(IS_WINDOWS)\n\tint modifiers;\n#endif\n\n\tif (isupper(c) && !(flags & MOD_SHIFT)) {\n\t\tflags |= MOD_SHIFT; /* Not sure if this is safe for all layouts. */\n\t}\n\n#if defined(IS_WINDOWS)\n\tmodifiers = keyCode >> 8; // Pull out modifers.\n\tif ((modifiers & 1) != 0) flags |= MOD_SHIFT; // Uptdate flags from keycode modifiers.\n    if ((modifiers & 2) != 0) flags |= MOD_CONTROL;\n    if ((modifiers & 4) != 0) flags |= MOD_ALT;\n    keyCode = keyCode & 0xff; // Mask out modifiers.\n#endif\n\ttoggleKeyCode(keyCode, down, flags);\n}\n\nvoid tapKey(char c, MMKeyFlags flags)\n{\n\ttoggleKey(c, true, flags);\n\ttoggleKey(c, false, flags);\n}\n\n#if defined(IS_MACOSX)\nvoid toggleUnicode(UniChar ch, const bool down)\n{\n\t/* This function relies on the convenient\n\t * CGEventKeyboardSetUnicodeString(), which allows us to not have to\n\t * convert characters to a keycode, but does not support adding modifier\n\t * flags. It is therefore only used in typeStringDelayed()\n\t * -- if you need modifier keys, use the above functions instead. */\n\tCGEventRef keyEvent = CGEventCreateKeyboardEvent(NULL, 0, down);\n\tif (keyEvent == NULL) {\n\t\tfputs(\"Could not create keyboard event.\\n\", stderr);\n\t\treturn;\n\t}\n\n\tif (ch > 0xFFFF) {\n\t\t// encode to utf-16 if necessary\n\t\tUniChar surrogates[2] = {\n\t\t\t0xD800 + ((ch - 0x10000) >> 10),\n\t\t\t0xDC00 + (ch & 0x3FF)\n\t\t};\n\n\t\tCGEventKeyboardSetUnicodeString(keyEvent, 2, (UniChar*) &surrogates);\n\t} else {\n\t\tCGEventKeyboardSetUnicodeString(keyEvent, 1, (UniChar*) &ch);\n\t}\n\n\tCGEventPost(kCGSessionEventTap, keyEvent);\n\tCFRelease(keyEvent);\n}\n#elif defined(USE_X11)\n\t#define toggleUniKey(c, down) toggleKey(c, down, MOD_NONE)\n#endif\n\nvoid unicodeTap(const unsigned value)\n{\n\t#if defined(USE_X11)\n\t\tchar ch = (char)value;\n\n\t\ttoggleUniKey(ch, true);\n\t\ttoggleUniKey(ch, false);\n\t#elif defined(IS_MACOSX)\n\t\tUniChar ch = (UniChar)value; // Convert to unsigned char\n\n\t\ttoggleUnicode(ch, true);\n\t\ttoggleUnicode(ch, false);\n\t#elif defined(IS_WINDOWS)\n\t\tINPUT ip;\n\n\t\t// Set up a generic keyboard event.\n\t\tip.type = INPUT_KEYBOARD;\n\t\tip.ki.wVk = 0; // Virtual-key code\n\t\tip.ki.wScan = value; // Hardware scan code for key\n\t\tip.ki.time = 0; // System will provide its own time stamp.\n\t\tip.ki.dwExtraInfo = 0; // No extra info. Use the GetMessageExtraInfo function to obtain this information if needed.\n\t\tip.ki.dwFlags = KEYEVENTF_UNICODE; // KEYEVENTF_KEYUP for key release.\n\n\t\tSendInput(1, &ip, sizeof(INPUT));\n\t#endif\n}\n\nvoid typeStringDelayed(const char *str, const unsigned cpm)\n{\n\tunsigned long n;\n\tunsigned short c;\n\tunsigned short c1;\n\tunsigned short c2;\n\tunsigned short c3;\n\n\t/* Characters per second */\n\tconst double cps = (double)cpm / 60.0;\n\n\t/* Average milli-seconds per character */\n\tconst double mspc = (cps == 0.0) ? 0.0 : 1000.0 / cps;\n\n\twhile (*str != '\\0') {\n\t\tc = *str++;\n\n\t\t// warning, the following utf8 decoder\n\t\t// doesn't perform validation\n\t\tif (c <= 0x7F) {\n\t\t\t// 0xxxxxxx one byte\n\t\t\tn = c;\n\t\t} else if ((c & 0xE0) == 0xC0)  {\n\t\t\t// 110xxxxx two bytes\n\t\t\tc1 = (*str++) & 0x3F;\n\t\t\tn = ((c & 0x1F) << 6) | c1;\n\t\t} else if ((c & 0xF0) == 0xE0) {\n\t\t\t// 1110xxxx three bytes\n\t\t\tc1 = (*str++) & 0x3F;\n\t\t\tc2 = (*str++) & 0x3F;\n\t\t\tn = ((c & 0x0F) << 12) | (c1 << 6) | c2;\n\t\t} else if ((c & 0xF8) == 0xF0) {\n\t\t\t// 11110xxx four bytes\n\t\t\tc1 = (*str++) & 0x3F;\n\t\t\tc2 = (*str++) & 0x3F;\n\t\t\tc3 = (*str++) & 0x3F;\n\t\t\tn = ((c & 0x07) << 18) | (c1 << 12) | (c2 << 6) | c3;\n\t\t}\n\n\t\tunicodeTap(n);\n\n\t\tif (mspc > 0) {\n\t\t\tmicrosleep(mspc);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/keypress.h",
    "content": "#pragma once\n#ifndef KEYPRESS_H\n#define KEYPRESS_H\n\n#include \"os.h\"\n#include \"keycode.h\"\n\n#if defined(_MSC_VER)\n\t#include \"ms_stdbool.h\"\n#else\n\t#include <stdbool.h>\n#endif\n#ifdef __cplusplus\nextern \"C\" \n{\n#endif\n#if defined(IS_MACOSX)\n\n\ttypedef enum  {\n\t\tMOD_NONE = 0,\n\t\tMOD_META = kCGEventFlagMaskCommand,\n\t\tMOD_ALT = kCGEventFlagMaskAlternate,\n\t\tMOD_CONTROL = kCGEventFlagMaskControl,\n\t\tMOD_SHIFT = kCGEventFlagMaskShift\n\t} MMKeyFlags;\n\n#elif defined(USE_X11)\n\n\tenum _MMKeyFlags {\n\t\tMOD_NONE = 0,\n\t\tMOD_META = Mod4Mask,\n\t\tMOD_ALT = Mod1Mask,\n\t\tMOD_CONTROL = ControlMask,\n\t\tMOD_SHIFT = ShiftMask\n\t};\n\n\ttypedef unsigned int MMKeyFlags;\n\n#elif defined(IS_WINDOWS)\n\n\tenum _MMKeyFlags {\n\t\tMOD_NONE = 0,\n\t\t/* These are already defined by the Win32 API */\n\t\t/* MOD_ALT = 0,\n\t\tMOD_CONTROL = 0,\n\t\tMOD_SHIFT = 0, */\n\t\tMOD_META = MOD_WIN\n\t};\n\n\ttypedef unsigned int MMKeyFlags;\n\n#endif\n\n#if defined(IS_WINDOWS)\n/* Send win32 key event for given key. */\nvoid win32KeyEvent(int key, MMKeyFlags flags);\n#endif\n\n/* Toggles the given key down or up. */\nvoid toggleKeyCode(MMKeyCode code, const bool down, MMKeyFlags flags);\n\n/* Toggles the key down and then up. */\nvoid tapKeyCode(MMKeyCode code, MMKeyFlags flags);\n\n/* Toggles the key corresponding to the given UTF character up or down. */\nvoid toggleKey(char c, const bool down, MMKeyFlags flags);\nvoid tapKey(char c, MMKeyFlags flags);\n\n/* Sends a Unicode character without modifiers. */\nvoid unicodeTap(const unsigned value);\n\n/* Macro to convert WPM to CPM integers.\n * (the average English word length is 5.1 characters.) */\n#define WPM_TO_CPM(WPM) (unsigned)(5.1 * WPM)\n\n/* Sends a UTF-8 string without modifiers and with partially random delays between each letter.\n * Note that deadbeef_srand() must be called before this function if you actually want\n * randomness. */\nvoid typeStringDelayed(const char *str, const unsigned cpm);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* KEYPRESS_H */\n"
  },
  {
    "path": "src/microsleep.h",
    "content": "#pragma once\n#ifndef MICROSLEEP_H\n#define MICROSLEEP_H\n\n#include \"os.h\"\n#include \"inline_keywords.h\"\n\n#if !defined(IS_WINDOWS)\n\t/* Make sure nanosleep gets defined even when using C89. */\n\t#if !defined(__USE_POSIX199309) || !__USE_POSIX199309\n\t\t#define __USE_POSIX199309 1\n\t#endif\n\n\t#include <time.h> /* For nanosleep() */\n#endif\n\n/*\n * A more widely supported alternative to usleep(), based on Sleep() in Windows\n * and nanosleep() everywhere else.\n *\n * Pauses execution for the given amount of milliseconds.\n */\nH_INLINE void microsleep(double milliseconds)\n{\n#if defined(IS_WINDOWS)\n\tSleep((DWORD)milliseconds); /* (Unfortunately truncated to a 32-bit integer.) */\n#else\n\t/* Technically, nanosleep() is not an ANSI function, but it is the most\n\t * supported precise sleeping function I can find.\n\t *\n\t * If it is really necessary, it may be possible to emulate this with some\n\t * hack using select() in the future if we really have to. */\n\tstruct timespec sleepytime;\n\tsleepytime.tv_sec = milliseconds / 1000;\n\tsleepytime.tv_nsec = (milliseconds - (sleepytime.tv_sec * 1000)) * 1000000;\n\tnanosleep(&sleepytime, NULL);\n#endif\n}\n\n#endif /* MICROSLEEP_H */\n"
  },
  {
    "path": "src/mouse.c",
    "content": "#include \"mouse.h\"\n#include \"screen.h\"\n#include \"deadbeef_rand.h\"\n#include \"microsleep.h\"\n\n#include <math.h> /* For floor() */\n\n#if defined(IS_MACOSX)\n\t#include <ApplicationServices/ApplicationServices.h>\n#elif defined(USE_X11)\n\t#include <X11/Xlib.h>\n\t#include <X11/extensions/XTest.h>\n\t#include <stdlib.h>\n\t#include \"xdisplay.h\"\n#endif\n\n#if !defined(M_SQRT2)\n\t#define M_SQRT2 1.4142135623730950488016887 /* Fix for MSVC. */\n#endif\n\n/* Some convenience macros for converting our enums to the system API types. */\n#if defined(IS_MACOSX)\n\n#define MMMouseToCGEventType(down, button) \\\n\t(down ? MMMouseDownToCGEventType(button) : MMMouseUpToCGEventType(button))\n\n#define MMMouseDownToCGEventType(button) \\\n\t((button) == (LEFT_BUTTON) ? kCGEventLeftMouseDown \\\n\t                       : ((button) == RIGHT_BUTTON ? kCGEventRightMouseDown \\\n\t                                                   : kCGEventOtherMouseDown))\n\n#define MMMouseUpToCGEventType(button) \\\n\t((button) == LEFT_BUTTON ? kCGEventLeftMouseUp \\\n\t                         : ((button) == RIGHT_BUTTON ? kCGEventRightMouseUp \\\n\t                                                     : kCGEventOtherMouseUp))\n\n#define MMMouseDragToCGEventType(button) \\\n\t((button) == LEFT_BUTTON ? kCGEventLeftMouseDragged \\\n\t                         : ((button) == RIGHT_BUTTON ? kCGEventRightMouseDragged \\\n\t                                                     : kCGEventOtherMouseDragged))\n\n#elif defined(IS_WINDOWS)\n\n// The width of the virtual screen, in pixels.\nstatic int vscreenWidth = -1; // not initialized\n\n// The height of the virtual screen, in pixels.\nstatic int vscreenHeight = -1; // not initialized\n\n// The coordinates for the left side of the virtual screen.\nstatic int vscreenMinX = 0;\n\n// The coordinates for the top of the virtual screen.\nstatic int vscreenMinY = 0;\n\n#define MMMouseToMEventF(down, button) \\\n\t(down ? MMMouseDownToMEventF(button) : MMMouseUpToMEventF(button))\n\n#define MMMouseUpToMEventF(button) \\\n\t((button) == LEFT_BUTTON ? MOUSEEVENTF_LEFTUP \\\n\t                         : ((button) == RIGHT_BUTTON ? MOUSEEVENTF_RIGHTUP \\\n\t                                                     : MOUSEEVENTF_MIDDLEUP))\n\n#define MMMouseDownToMEventF(button) \\\n\t((button) == LEFT_BUTTON ? MOUSEEVENTF_LEFTDOWN \\\n\t                         : ((button) == RIGHT_BUTTON ? MOUSEEVENTF_RIGHTDOWN \\\n\t                                                     : MOUSEEVENTF_MIDDLEDOWN))\n\n#endif\n\n#if defined(IS_MACOSX)\n/**\n * Calculate the delta for a mouse move and add them to the event.\n * @param event The mouse move event (by ref).\n * @param point The new mouse x and y.\n */\nvoid calculateDeltas(CGEventRef *event, MMSignedPoint point)\n{\n\t/**\n\t * The next few lines are a workaround for games not detecting mouse moves.\n\t * See this issue for more information:\n\t * https://github.com/octalmage/robotjs/issues/159\n\t */\n\tCGEventRef get = CGEventCreate(NULL);\n\tCGPoint mouse = CGEventGetLocation(get);\n\n\t// Calculate the deltas.\n\tint64_t deltaX = point.x - mouse.x;\n\tint64_t deltaY = point.y - mouse.y;\n\n\tCGEventSetIntegerValueField(*event, kCGMouseEventDeltaX, deltaX);\n\tCGEventSetIntegerValueField(*event, kCGMouseEventDeltaY, deltaY);\n\n\tCFRelease(get);\n}\n#endif\n\nvoid updateScreenMetrics()\n{\n\t#if defined(IS_WINDOWS)\n\t\tvscreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\n\t\tvscreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\n\t\tvscreenMinX = GetSystemMetrics(SM_XVIRTUALSCREEN);\n\t\tvscreenMinY = GetSystemMetrics(SM_YVIRTUALSCREEN);\n\t#endif\n}\n/**\n * Move the mouse to a specific point.\n * @param point The coordinates to move the mouse to (x, y).\n */\nvoid moveMouse(MMSignedPoint point)\n{\n#if defined(IS_MACOSX)\n\tCGEventRef move = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved,\n\t                                          CGPointFromMMSignedPoint(point),\n\t                                          kCGMouseButtonLeft);\n\n\tcalculateDeltas(&move, point);\n\n\tCGEventPost(kCGSessionEventTap, move);\n\tCFRelease(move);\n#elif defined(USE_X11)\n\tDisplay *display = XGetMainDisplay();\n\tXWarpPointer(display, None, DefaultRootWindow(display),\n\t             0, 0, 0, 0, point.x, point.y);\n\tXFlush(display);\n#elif defined(IS_WINDOWS)\n\n\tif(vscreenWidth<0 || vscreenHeight<0)\n\t\tupdateScreenMetrics();\n\n\t//Mouse motion is now done using SendInput with MOUSEINPUT. We use Absolute mouse positioning\n\t#define MOUSE_COORD_TO_ABS(coord, width_or_height) ((65536 * (coord) / width_or_height) + ((coord) < 0 ? -1 : 1))\n\n\tsize_t x = MOUSE_COORD_TO_ABS(point.x-vscreenMinX, vscreenWidth);\n\tsize_t y = MOUSE_COORD_TO_ABS(point.y-vscreenMinY, vscreenHeight);\n\n\tINPUT mouseInput = {0};\n\tmouseInput.type = INPUT_MOUSE;\n\tmouseInput.mi.dx = x;\n\tmouseInput.mi.dy = y;\n\tmouseInput.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE | MOUSEEVENTF_VIRTUALDESK;\n\tmouseInput.mi.time = 0; //System will provide the timestamp\n\n\tSendInput(1, &mouseInput, sizeof(mouseInput));\n#endif\n}\n\nvoid dragMouse(MMSignedPoint point, const MMMouseButton button)\n{\n#if defined(IS_MACOSX)\n\tconst CGEventType dragType = MMMouseDragToCGEventType(button);\n\tCGEventRef drag = CGEventCreateMouseEvent(NULL, dragType,\n\t                                                CGPointFromMMSignedPoint(point),\n\t                                                (CGMouseButton)button);\n\tcalculateDeltas(&drag, point);\n\n\tCGEventPost(kCGSessionEventTap, drag);\n\tCFRelease(drag);\n#else\n\tmoveMouse(point);\n#endif\n}\n\nMMSignedPoint getMousePos()\n{\n#if defined(IS_MACOSX)\n\tCGEventRef event = CGEventCreate(NULL);\n\tCGPoint point = CGEventGetLocation(event);\n\tCFRelease(event);\n\n\treturn MMSignedPointFromCGPoint(point);\n#elif defined(USE_X11)\n\tint x, y; /* This is all we care about. Seriously. */\n\tWindow garb1, garb2; /* Why you can't specify NULL as a parameter */\n\tint garb_x, garb_y;  /* is beyond me. */\n\tunsigned int more_garbage;\n\n\tDisplay *display = XGetMainDisplay();\n\tXQueryPointer(display, XDefaultRootWindow(display), &garb1, &garb2,\n\t              &x, &y, &garb_x, &garb_y, &more_garbage);\n\n\treturn MMSignedPointMake(x, y);\n#elif defined(IS_WINDOWS)\n\tPOINT point;\n\tGetCursorPos(&point);\n\n\treturn MMSignedPointFromPOINT(point);\n#endif\n}\n\n/**\n * Press down a button, or release it.\n * @param down   True for down, false for up.\n * @param button The button to press down or release.\n */\nvoid toggleMouse(bool down, MMMouseButton button)\n{\n#if defined(IS_MACOSX)\n\tconst CGPoint currentPos = CGPointFromMMSignedPoint(getMousePos());\n\tconst CGEventType mouseType = MMMouseToCGEventType(down, button);\n\tCGEventRef event = CGEventCreateMouseEvent(NULL,\n\t                                           mouseType,\n\t                                           currentPos,\n\t                                           (CGMouseButton)button);\n\tCGEventPost(kCGSessionEventTap, event);\n\tCFRelease(event);\n#elif defined(USE_X11)\n\tDisplay *display = XGetMainDisplay();\n\tXTestFakeButtonEvent(display, button, down ? True : False, CurrentTime);\n\tXFlush(display);\n#elif defined(IS_WINDOWS)\n\tINPUT mouseInput;\n\tmouseInput.type = INPUT_MOUSE;\n\tmouseInput.mi.dx = 0;\n\tmouseInput.mi.dy = 0;\n\tmouseInput.mi.dwFlags = MMMouseToMEventF(down, button);\n\tmouseInput.mi.time = 0; //System will provide the timestamp\n\tmouseInput.mi.dwExtraInfo = 0;\n\tmouseInput.mi.mouseData = 0;\n\tSendInput(1, &mouseInput, sizeof(mouseInput));\n#endif\n}\n\nvoid clickMouse(MMMouseButton button)\n{\n\ttoggleMouse(true, button);\n\ttoggleMouse(false, button);\n}\n\n/**\n * Special function for sending double clicks, needed for Mac OS X.\n * @param button Button to click.\n */\nvoid doubleClick(MMMouseButton button)\n{\n\n#if defined(IS_MACOSX)\n\n\t/* Double click for Mac. */\n\tconst CGPoint currentPos = CGPointFromMMSignedPoint(getMousePos());\n\tconst CGEventType mouseTypeDown = MMMouseToCGEventType(true, button);\n\tconst CGEventType mouseTypeUP = MMMouseToCGEventType(false, button);\n\n\tCGEventRef event = CGEventCreateMouseEvent(NULL, mouseTypeDown, currentPos, kCGMouseButtonLeft);\n\n\t/* Set event to double click. */\n\tCGEventSetIntegerValueField(event, kCGMouseEventClickState, 2);\n\n\tCGEventPost(kCGHIDEventTap, event);\n\n\tCGEventSetType(event, mouseTypeUP);\n\tCGEventPost(kCGHIDEventTap, event);\n\n\tCFRelease(event);\n\n#else\n\n\t/* Double click for everything else. */\n\tclickMouse(button);\n\tmicrosleep(200);\n\tclickMouse(button);\n\n#endif\n}\n\nvoid scrollMouse(int x, int y)\n{\n#if defined(IS_WINDOWS)\n\t// Fix for #97 https://github.com/octalmage/robotjs/issues/97,\n\t// C89 needs variables declared on top of functions (mouseScrollInput)\n\tINPUT mouseScrollInputs[2];\n#endif\n\n  /* Direction should only be considered based on the scrollDirection. This\n   * Should not interfere. */\n\n  /* Set up the OS specific solution */\n#if defined(__APPLE__)\n\n\tCGEventRef event;\n\n\tevent = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, 2, y, x);\n\tCGEventPost(kCGHIDEventTap, event);\n\n\tCFRelease(event);\n\n#elif defined(USE_X11)\n\n\t/*\n\tX11 Mouse Button Numbering\n\t1 = left button\n\t2 = middle button (pressing the scroll wheel)\n\t3 = right button\n\t4 = turn scroll wheel up\n\t5 = turn scroll wheel down\n\t6 = push scroll wheel left\n\t7 = push scroll wheel right\n\t8 = 4th button (aka browser backward button)\n\t9 = 5th button (aka browser forward button)\n\t*/\n\tint ydir = 4; // Button 4 is up, 5 is down.\n\tint xdir = 6; // Button 6 is left, 7 is right.\n\tDisplay *display = XGetMainDisplay();\n\n\tif (y < 0){\n\t\tydir = 5;\n\t}\n\tif (x < 0){\n\t\txdir = 7;\n\t}\n\n\tint xi;\n\tint yi;\n\tfor (xi = 0; xi < abs(x); xi++) {\n\t\tXTestFakeButtonEvent(display, xdir, 1, CurrentTime);\n\t\tXTestFakeButtonEvent(display, xdir, 0, CurrentTime);\n\t}\n\tfor (yi = 0; yi < abs(y); yi++) {\n\t\tXTestFakeButtonEvent(display, ydir, 1, CurrentTime);\n\t\tXTestFakeButtonEvent(display, ydir, 0, CurrentTime);\n\t}\n\n\tXFlush(display);\n\n#elif defined(IS_WINDOWS)\n\n\t// Must send y first, otherwise we get stuck when scrolling on y axis\n\tmouseScrollInputs[0].type = INPUT_MOUSE;\n\tmouseScrollInputs[0].mi.dx = 0;\n\tmouseScrollInputs[0].mi.dy = 0;\n\tmouseScrollInputs[0].mi.dwFlags = MOUSEEVENTF_WHEEL;\n\tmouseScrollInputs[0].mi.time = 0;\n\tmouseScrollInputs[0].mi.dwExtraInfo = 0;\n\t// Flip x to match other platforms.\n\tmouseScrollInputs[0].mi.mouseData = -x;\n\n\tmouseScrollInputs[1].type = INPUT_MOUSE;\n\tmouseScrollInputs[1].mi.dx = 0;\n\tmouseScrollInputs[1].mi.dy = 0;\n\tmouseScrollInputs[1].mi.dwFlags = MOUSEEVENTF_HWHEEL;\n\tmouseScrollInputs[1].mi.time = 0;\n\tmouseScrollInputs[1].mi.dwExtraInfo = 0;\n\tmouseScrollInputs[1].mi.mouseData = y;\n\n\tSendInput(2, mouseScrollInputs, sizeof(INPUT));\n#endif\n}\n\n/*\n * A crude, fast hypot() approximation to get around the fact that hypot() is\n * not a standard ANSI C function.\n *\n * It is not particularly accurate but that does not matter for our use case.\n *\n * Taken from this StackOverflow answer:\n * http://stackoverflow.com/questions/3506404/fast-hypotenuse-algorithm-for-embedded-processor#3507882\n *\n */\nstatic double crude_hypot(double x, double y)\n{\n\tdouble big = fabs(x); /* max(|x|, |y|) */\n\tdouble small = fabs(y); /* min(|x|, |y|) */\n\n\tif (big > small) {\n\t\tdouble temp = big;\n\t\tbig = small;\n\t\tsmall = temp;\n\t}\n\n\treturn ((M_SQRT2 - 1.0) * small) + big;\n}\n\nbool smoothlyMoveMouse(MMPoint endPoint,double speed)\n{\n\tMMSignedPoint pos = getMousePos();\n\tMMSize screenSize = getMainDisplaySize();\n\tdouble velo_x = 0.0, velo_y = 0.0;\n\tdouble distance;\n\n\twhile ((distance = crude_hypot((double)pos.x - endPoint.x,\n\t                               (double)pos.y - endPoint.y)) > 1.0) {\n\t\tdouble gravity = DEADBEEF_UNIFORM(5.0, 500.0);\n\t\tdouble veloDistance;\n\t\tvelo_x += (gravity * ((double)endPoint.x - pos.x)) / distance;\n\t\tvelo_y += (gravity * ((double)endPoint.y - pos.y)) / distance;\n\n\t\t/* Normalize velocity to get a unit vector of length 1. */\n\t\tveloDistance = crude_hypot(velo_x, velo_y);\n\t\tvelo_x /= veloDistance;\n\t\tvelo_y /= veloDistance;\n\n\t\tpos.x += floor(velo_x + 0.5);\n\t\tpos.y += floor(velo_y + 0.5);\n\n\t\t/* Make sure we are in the screen boundaries!\n\t\t * (Strange things will happen if we are not.) */\n\t\tif (pos.x >= (int32_t)screenSize.width || pos.y >= (int32_t)screenSize.height) {\n\t\t\treturn false;\n\t\t}\n\n\t\tmoveMouse(pos);\n\n\t\t/* Wait 1 - (speed) milliseconds. */\n\t\tmicrosleep(DEADBEEF_UNIFORM(0.7, speed));\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "src/mouse.h",
    "content": "#pragma once\n#ifndef MOUSE_H\n#define MOUSE_H\n\n#include \"os.h\"\n#include \"types.h\"\n\n#if defined(_MSC_VER)\n\t#include \"ms_stdbool.h\"\n#else\n\t#include <stdbool.h>\n#endif\n#ifdef __cplusplus\nextern \"C\"\n{\n#endif\n#if defined(IS_MACOSX)\n\n\t#include <ApplicationServices/ApplicationServices.h>\n\n\ttypedef enum  {\n\t\tLEFT_BUTTON = kCGMouseButtonLeft,\n\t\tRIGHT_BUTTON = kCGMouseButtonRight,\n\t\tCENTER_BUTTON = kCGMouseButtonCenter\n\t} MMMouseButton;\n\n#elif defined(USE_X11)\n\n\tenum _MMMouseButton {\n\t\tLEFT_BUTTON = 1,\n\t\tCENTER_BUTTON = 2,\n\t\tRIGHT_BUTTON = 3\n\t};\n\ttypedef unsigned int MMMouseButton;\n\n#elif defined(IS_WINDOWS)\n\n\tenum _MMMouseButton {\n\t\tLEFT_BUTTON = 1,\n\t\tCENTER_BUTTON = 2,\n\t\tRIGHT_BUTTON = 3\n\t};\n\ttypedef unsigned int MMMouseButton;\n\n#else\n\t#error \"No mouse button constants set for platform\"\n#endif\n\n#define MMMouseButtonIsValid(button) \\\n\t(button == LEFT_BUTTON || button == RIGHT_BUTTON || \\\n\t button == CENTER_BUTTON)\n\nenum __MMMouseWheelDirection\n{\n\tDIRECTION_DOWN \t= -1,\n\tDIRECTION_UP\t= 1\n};\ntypedef int MMMouseWheelDirection;\n\n/* Updates information about current virtual screen size and coordinates\n * in Windows\n * It is up to the caller to ensure that this called before mouse moving\n*/\nvoid updateScreenMetrics();\n\n/* Immediately moves the mouse to the given point on-screen.\n * It is up to the caller to ensure that this point is within the\n * screen boundaries. */\nvoid moveMouse(MMSignedPoint point);\n\n/* Like moveMouse, moves the mouse to the given point on-screen, but marks\n * the event as the mouse being dragged on platforms where it is supported.\n * It is up to the caller to ensure that this point is within the screen\n * boundaries. */\nvoid dragMouse(MMSignedPoint point, const MMMouseButton button);\n\n/* Smoothly moves the mouse from the current position to the given point.\n * deadbeef_srand() should be called before using this function.\n *\n * Returns false if unsuccessful (i.e. a point was hit that is outside of the\n * screen boundaries), or true if successful. */\nbool smoothlyMoveMouse(MMPoint point,double speed);\n\n/* Returns the coordinates of the mouse on the current screen. */\nMMSignedPoint getMousePos(void);\n\n/* Holds down or releases the mouse with the given button in the current\n * position. */\nvoid toggleMouse(bool down, MMMouseButton button);\n\n/* Clicks the mouse with the given button in the current position. */\nvoid clickMouse(MMMouseButton button);\n\n/* Double clicks the mouse with the given button. */\nvoid doubleClick(MMMouseButton button);\n\n/* Scrolls the mouse in the stated direction.\n * TODO: Add a smoothly scroll mouse next. */\nvoid scrollMouse(int x, int y);\n\n#endif /* MOUSE_H */\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "src/ms_stdbool.h",
    "content": "#pragma once\n#if !defined(MS_STDBOOL_H) && \\\n\t(!defined(__bool_true_false_are_defined) || __bool_true_false_are_defined)\n#define MS_STDBOOL_H\n\n#ifndef _MSC_VER\n\t#error \"Use this header only with Microsoft Visual C++ compilers!\"\n#endif /* _MSC_VER */\n\n#define __bool_true_false_are_defined 1\n\n#ifndef __cplusplus\n\n\t#if defined(true) || defined(false) || defined(bool)\n\t\t#error \"Boolean type already defined\"\n\t#endif\n\n\tenum {\n\t\tfalse = 0,\n\t\ttrue = 1\n\t};\n\n\ttypedef unsigned char bool;\n\n#endif /* !__cplusplus */\n\n#endif /* MS_STDBOOL_H */\n"
  },
  {
    "path": "src/ms_stdint.h",
    "content": "/* ISO C9x  compliant stdint.h for Microsoft Visual Studio\n * Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124\n *\n *  Copyright (c) 2006-2008 Alexander Chemeris\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   1. Redistributions of source code must retain the above copyright notice,\n *      this list of conditions and the following disclaimer.\n *\n *   2. Redistributions in binary form must reproduce the above copyright\n *      notice, this list of conditions and the following disclaimer in the\n *      documentation and/or other materials provided with the distribution.\n *\n *   3. The name of the author may be used to endorse or promote products\n *      derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED\n * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO\n * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\n * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\n * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef _MSC_VER\n\t#error \"Use this header only with Microsoft Visual C++ compilers!\"\n#endif /* _MSC_VER */\n\n#ifndef MSC_STDINT_H\n#define MSC_STDINT_H\n\n#if _MSC_VER > 1000\n\t#pragma once\n#endif\n\n#include <limits.h>\n\n/* For Visual Studio 6 in C++ mode and for many Visual Studio versions when\n * compiling for ARM we should wrap <wchar.h> include with 'extern \"C++\" {}'\n * or compiler give many errors like this: */\n#if defined(__cplusplus)\nextern \"C\" {\n#endif /* __cplusplus */\n\t#include <wchar.h>\n#if defined(__cplusplus)\n}\n#endif /* __cplusplus */\n\n/* Define _W64 macros to mark types changing their size, like intptr_t. */\n#ifndef _W64\n\t#if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300\n\t\t#define _W64 __w64\n\t#else\n\t\t#define _W64\n\t#endif\n#endif\n\n\n/* 7.18.1 Integer types */\n\n/* 7.18.1.1 Exact-width integer types */\n\n/* Visual Studio 6 and Embedded Visual C++ 4 doesn't\n * realize that, e.g. char has the same size as __int8\n * so we give up on __intX for them. */\n#if _MSC_VER < 1300\n\ttypedef signed char       int8_t;\n\ttypedef signed short      int16_t;\n\ttypedef signed int        int32_t;\n\ttypedef unsigned char     uint8_t;\n\ttypedef unsigned short    uint16_t;\n\ttypedef unsigned int      uint32_t;\n#else\n\ttypedef signed __int8     int8_t;\n\ttypedef signed __int16    int16_t;\n\ttypedef signed __int32    int32_t;\n\ttypedef unsigned __int8   uint8_t;\n\ttypedef unsigned __int16  uint16_t;\n\ttypedef unsigned __int32  uint32_t;\n#endif\ntypedef signed __int64       int64_t;\ntypedef unsigned __int64     uint64_t;\n\n/* 7.18.1.2 Minimum-width integer types */\ntypedef int8_t    int_least8_t;\ntypedef int16_t   int_least16_t;\ntypedef int32_t   int_least32_t;\ntypedef int64_t   int_least64_t;\ntypedef uint8_t   uint_least8_t;\ntypedef uint16_t  uint_least16_t;\ntypedef uint32_t  uint_least32_t;\ntypedef uint64_t  uint_least64_t;\n\n/* 7.18.1.3 Fastest minimum-width integer types */\ntypedef int8_t    int_fast8_t;\ntypedef int16_t   int_fast16_t;\ntypedef int32_t   int_fast32_t;\ntypedef int64_t   int_fast64_t;\ntypedef uint8_t   uint_fast8_t;\ntypedef uint16_t  uint_fast16_t;\ntypedef uint32_t  uint_fast32_t;\ntypedef uint64_t  uint_fast64_t;\n\n/* 7.18.1.4 Integer types capable of holding object pointers */\n#if defined(_WIN64)\n\ttypedef signed __int64    intptr_t;\n\ttypedef unsigned __int64  uintptr_t;\n#else\n\ttypedef _W64 signed int   intptr_t;\n\ttypedef _W64 unsigned int uintptr_t;\n#endif /* _WIN64 ] */\n\n/* 7.18.1.5 Greatest-width integer types */\ntypedef int64_t   intmax_t;\ntypedef uint64_t  uintmax_t;\n\n/* 7.18.2 Limits of specified-width integer types */\n\n/* See footnote 220 at page 257 and footnote 221 at page 259 */\n#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS)\n\n\t/* 7.18.2.1 Limits of exact-width integer types */\n\t#define INT8_MIN     ((int8_t)_I8_MIN)\n\t#define INT8_MAX     _I8_MAX\n\t#define INT16_MIN    ((int16_t)_I16_MIN)\n\t#define INT16_MAX    _I16_MAX\n\t#define INT32_MIN    ((int32_t)_I32_MIN)\n\t#define INT32_MAX    _I32_MAX\n\t#define INT64_MIN    ((int64_t)_I64_MIN)\n\t#define INT64_MAX    _I64_MAX\n\t#define UINT8_MAX    _UI8_MAX\n\t#define UINT16_MAX   _UI16_MAX\n\t#define UINT32_MAX   _UI32_MAX\n\t#define UINT64_MAX   _UI64_MAX\n\n\t/* 7.18.2.2 Limits of minimum-width integer types */\n\t#define INT_LEAST8_MIN    INT8_MIN\n\t#define INT_LEAST8_MAX    INT8_MAX\n\t#define INT_LEAST16_MIN   INT16_MIN\n\t#define INT_LEAST16_MAX   INT16_MAX\n\t#define INT_LEAST32_MIN   INT32_MIN\n\t#define INT_LEAST32_MAX   INT32_MAX\n\t#define INT_LEAST64_MIN   INT64_MIN\n\t#define INT_LEAST64_MAX   INT64_MAX\n\t#define UINT_LEAST8_MAX   UINT8_MAX\n\t#define UINT_LEAST16_MAX  UINT16_MAX\n\t#define UINT_LEAST32_MAX  UINT32_MAX\n\t#define UINT_LEAST64_MAX  UINT64_MAX\n\n\t/* 7.18.2.4 Limits of integer types capable of holding object pointers */\n\t#if defined(_WIN64)\n\t\t#define INTPTR_MIN   INT64_MIN\n\t\t#define INTPTR_MAX   INT64_MAX\n\t\t#define UINTPTR_MAX  UINT64_MAX\n\t#else\n\t\t#define INTPTR_MIN   INT32_MIN\n\t\t#define INTPTR_MAX   INT32_MAX\n\t\t#define UINTPTR_MAX  UINT32_MAX\n\t#endif\n\n\t/* 7.18.3 Limits of other integer types */\n\n\t#if defined(_WIN64)\n\t\t#define PTRDIFF_MIN  _I64_MIN\n\t\t#define PTRDIFF_MAX  _I64_MAX\n\t#else\n\t\t#define PTRDIFF_MIN  _I32_MIN\n\t\t#define PTRDIFF_MAX  _I32_MAX\n\t#endif  /* _WIN64 */\n\n\t#define SIG_ATOMIC_MIN  INT_MIN\n\t#define SIG_ATOMIC_MAX  INT_MAX\n\n\t#ifndef SIZE_MAX\n\t\t#if defined(_WIN64)\n\t\t\t#define SIZE_MAX  _UI64_MAX\n\t\t#else\n\t\t\t#define SIZE_MAX  _UI32_MAX\n\t\t#endif\n\t#endif\n\n\t/* WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h> */\n\t#ifndef WCHAR_MIN\n\t\t#define WCHAR_MIN  0\n\t#endif  /* WCHAR_MIN */\n\n\t#ifndef WCHAR_MAX\n\t\t#define WCHAR_MAX  _UI16_MAX\n\t#endif  /* WCHAR_MAX */\n\n\t#define WINT_MIN  0\n\t#define WINT_MAX  _UI16_MAX\n#endif /* __STDC_LIMIT_MACROS */\n\n\n/* 7.18.4 Limits of other integer types */\n\n#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) /* See footnote 224 at page 260 */\n\n\t/* 7.18.4.1 Macros for minimum-width integer constants */\n\n\t#define INT8_C(val)  val##i8\n\t#define INT16_C(val) val##i16\n\t#define INT32_C(val) val##i32\n\t#define INT64_C(val) val##i64\n\n\t#define UINT8_C(val)  val##ui8\n\t#define UINT16_C(val) val##ui16\n\t#define UINT32_C(val) val##ui32\n\t#define UINT64_C(val) val##ui64\n\n\t/* 7.18.4.2 Macros for greatest-width integer constants */\n\t#define INTMAX_C   INT64_C\n\t#define UINTMAX_C  UINT64_C\n\n#endif /* __STDC_CONSTANT_MACROS */\n\n#endif /* MSC_STDINT_H */\n"
  },
  {
    "path": "src/os.h",
    "content": "#pragma once\n#ifndef OS_H\n#define OS_H\n\n/* Python versions under 2.5 don't support this macro, but it's not\n * terribly difficult to replicate: */\n#ifndef PyModule_AddIntMacro\n\t#define PyModule_AddIntMacro(module, macro) \\\n\t\tPyModule_AddIntConstant(module, #macro, macro)\n#endif /* PyModule_AddIntMacro */\n\n#if !defined(IS_MACOSX) && defined(__APPLE__) && defined(__MACH__)\n\t#define IS_MACOSX\n#endif /* IS_MACOSX */\n\n#if !defined(IS_WINDOWS) && (defined(WIN32) || defined(_WIN32) || \\\n                             defined(__WIN32__) || defined(__WINDOWS__))\n\t#define IS_WINDOWS\n#endif /* IS_WINDOWS */\n\n#if !defined(USE_X11) && !defined(NUSE_X11) && !defined(IS_MACOSX) && !defined(IS_WINDOWS)\n\t#define USE_X11\n#endif /* USE_X11 */\n\n#if defined(IS_WINDOWS)\n\t#define STRICT /* Require use of exact types. */\n\t#define WIN32_LEAN_AND_MEAN 1 /* Speed up compilation. */\n\t#include <windows.h>\n#elif !defined(IS_MACOSX) && !defined(USE_X11)\n\t#error \"Sorry, this platform isn't supported yet!\"\n#endif\n\n/* Interval to align by for large buffers (e.g. bitmaps). */\n/* Must be a power of 2. */\n#ifndef BYTE_ALIGN\n\t#define BYTE_ALIGN 4 /* Bytes to align pixel buffers to. */\n\t/* #include <stddef.h> */\n\t/* #define BYTE_ALIGN (sizeof(size_t)) */\n#endif /* BYTE_ALIGN */\n\n#if BYTE_ALIGN == 0\n\t/* No alignment needed. */\n\t#define ADD_PADDING(width) (width)\n#else\n\t/* Aligns given width to padding. */\n\t#define ADD_PADDING(width) (BYTE_ALIGN + (((width) - 1) & ~(BYTE_ALIGN - 1)))\n#endif\n\n#endif /* OS_H */\n"
  },
  {
    "path": "src/pasteboard.c",
    "content": "#include \"pasteboard.h\"\n#include \"os.h\"\n\n#if defined(IS_MACOSX)\n\t#include \"png_io.h\"\n\t#include <ApplicationServices/ApplicationServices.h>\n#elif defined(IS_WINDOWS)\n\t#include \"bmp_io.h\"\n#endif\n\nMMPasteError copyMMBitmapToPasteboard(MMBitmapRef bitmap)\n{\n#if defined(IS_MACOSX)\n\tPasteboardRef clipboard;\n\n\tsize_t len;\n\tuint8_t *pngbuf;\n\tCFDataRef data;\n\tOSStatus err;\n\n\tif (PasteboardCreate(kPasteboardClipboard, &clipboard) != noErr) {\n\t\treturn kMMPasteOpenError;\n\t}\n\n\tif (PasteboardClear(clipboard) != noErr) {\n\t\tCFRelease(clipboard);\n\t\treturn kMMPasteClearError;\n\t}\n\n\tpngbuf = createPNGData(bitmap, &len);\n\tif (pngbuf == NULL) {\n\t\tCFRelease(clipboard);\n\t\treturn kMMPasteDataError;\n\t}\n\n\tdata = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, pngbuf, len,\n\t                                   kCFAllocatorNull);\n\tif (data == NULL) {\n\t\tCFRelease(clipboard);\n\t\tfree(pngbuf);\n\t\treturn kMMPasteDataError;\n\t}\n\n\terr = PasteboardPutItemFlavor(clipboard, bitmap, kUTTypePNG, data, 0);\n\tCFRelease(data);\n\tCFRelease(clipboard);\n\tfree(pngbuf);\n\treturn (err == noErr) ? kMMPasteNoError : kMMPastePasteError;\n#elif defined(IS_WINDOWS)\n\tMMPasteError ret = kMMPasteNoError;\n\tuint8_t *bmpData;\n\tsize_t len;\n\tHGLOBAL handle;\n\n\tif (!OpenClipboard(NULL)) return kMMPasteOpenError;\n\tif (!EmptyClipboard()) return kMMPasteClearError;\n\n\tbmpData = createBitmapData(bitmap, &len);\n\tif (bmpData == NULL) return kMMPasteDataError;\n\n\t/* CF_DIB does not include the BITMAPFILEHEADER struct (and displays a\n\t * cryptic error if it is included). */\n\tlen -= sizeof(BITMAPFILEHEADER);\n\n\t/* SetClipboardData() needs a \"handle\", not just a buffer, so we have to\n\t * allocate one with GlobalAlloc(). */\n\tif ((handle = GlobalAlloc(GMEM_MOVEABLE, len)) == NULL) {\n\t\tCloseClipboard();\n\t\tfree(bmpData);\n\t\treturn kMMPasteDataError;\n\t}\n\n\tmemcpy(GlobalLock(handle), bmpData + sizeof(BITMAPFILEHEADER), len);\n\tGlobalUnlock(handle);\n\tfree(bmpData);\n\n\tif (SetClipboardData(CF_DIB, handle) == NULL) {\n\t\tret = kMMPastePasteError;\n\t}\n\n\tCloseClipboard();\n\tGlobalFree(handle);\n\treturn ret;\n#elif defined(USE_X11)\n\t/* TODO (X11's clipboard is _weird_.) */\n\treturn kMMPasteUnsupportedError;\n#endif\n}\n\nconst char *MMPasteErrorString(MMPasteError err)\n{\n\tswitch (err) {\n\t\tcase kMMPasteOpenError:\n\t\t\treturn \"Could not open pasteboard\";\n\t\tcase kMMPasteClearError:\n\t\t\treturn \"Could not clear pasteboard\";\n\t\tcase kMMPasteDataError:\n\t\t\treturn \"Could not create image data from bitmap\";\n\t\tcase kMMPastePasteError:\n\t\t\treturn \"Could not paste data\";\n\t\tcase kMMPasteUnsupportedError:\n\t\t\treturn \"Unsupported platform\";\n\t\tdefault:\n\t\t\treturn NULL;\n\t}\n}\n"
  },
  {
    "path": "src/pasteboard.h",
    "content": "#pragma once\n#ifndef PASTEBOARD_H\n#define PASTEBOARD_H\n\n#include \"MMBitmap.h\"\n#include \"io.h\"\n\nenum _MMBitmapPasteError {\n\tkMMPasteNoError = 0,\n\tkMMPasteGenericError,\n\tkMMPasteOpenError,\n\tkMMPasteClearError,\n\tkMMPasteDataError,\n\tkMMPastePasteError,\n\tkMMPasteUnsupportedError\n};\n\ntypedef MMIOError MMPasteError;\n\n/* Copies |bitmap| to the pasteboard as a PNG.\n * Returns 0 on success, non-zero on error. */\nMMPasteError copyMMBitmapToPasteboard(MMBitmapRef bitmap);\n\n/* Returns description of given MMPasteError.\n * Returned string is constant and hence should not be freed. */\nconst char *MMPasteErrorString(MMPasteError error);\n\n#endif /* PASTEBOARD_H */\n"
  },
  {
    "path": "src/png_io.c",
    "content": "#include \"png_io.h\"\n#include \"os.h\"\n#include <png.h>\n#include <stdio.h> /* fopen() */\n#include <stdlib.h> /* malloc/realloc */\n#include <assert.h>\n\n#if defined(_MSC_VER)\n\t#include \"ms_stdint.h\"\n\t#include \"ms_stdbool.h\"\n#else\n\t#include <stdint.h>\n\t#include <stdbool.h>\n#endif\n\nconst char *MMPNGReadErrorString(MMIOError error)\n{\n\tswitch (error) {\n\t\tcase kPNGAccessError:\n\t\t\treturn \"Could not open file\";\n\t\tcase kPNGReadError:\n\t\t\treturn \"Could not read file\";\n\t\tcase kPNGInvalidHeaderError:\n\t\t\treturn \"Not a PNG file\";\n\t\tdefault:\n\t\t\treturn NULL;\n\t}\n}\n\nMMBitmapRef newMMBitmapFromPNG(const char *path, MMPNGReadError *err)\n{\n\tFILE *fp;\n\tuint8_t header[8];\n\tpng_struct *png_ptr = NULL;\n\tpng_info *info_ptr = NULL;\n\tpng_byte bit_depth, color_type;\n\tuint8_t *row, *bitmapData;\n\tuint8_t bytesPerPixel;\n\tpng_uint_32 width, height, y;\n\tuint32_t bytewidth;\n\n\tif ((fp = fopen(path, \"rb\")) == NULL) {\n\t\tif (err != NULL) *err = kPNGAccessError;\n\t\treturn NULL;\n\t}\n\n\t/* Initialize error code to generic value. */\n\tif (err != NULL) *err = kPNGGenericError;\n\n\t/* Validate the PNG. */\n\tif (fread(header, 1, sizeof header, fp) == 0) {\n\t\tif (err != NULL) *err = kPNGReadError;\n\t\tgoto bail;\n\t} else if (png_sig_cmp(header, 0, sizeof(header)) != 0) {\n\t\tif (err != NULL) *err = kPNGInvalidHeaderError;\n\t\tgoto bail;\n\t}\n\n\tpng_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);\n\tif (png_ptr == NULL) goto bail;\n\n\tinfo_ptr = png_create_info_struct(png_ptr);\n\tif (info_ptr == NULL) goto bail;\n\n\t/* Set up error handling. */\n\tif (setjmp(png_jmpbuf(png_ptr))) {\n\t\tgoto bail;\n\t}\n\n\tpng_init_io(png_ptr, fp);\n\n\t/* Skip past the header. */\n\tpng_set_sig_bytes(png_ptr, sizeof header);\n\n\tpng_read_info(png_ptr, info_ptr);\n\n\t/* Convert different image types to common type to be read. */\n\tbit_depth = png_get_bit_depth(png_ptr, info_ptr);\n\tcolor_type = png_get_color_type(png_ptr, info_ptr);\n\n\t/* Convert color palettes to RGB. */\n\tif (color_type == PNG_COLOR_TYPE_PALETTE) {\n\t\tpng_set_palette_to_rgb(png_ptr);\n\t}\n\n\t/* Convert PNG to bit depth of 8. */\n\tif (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {\n\t\tpng_set_expand_gray_1_2_4_to_8(png_ptr);\n\t} else if (bit_depth == 16) {\n\t\tpng_set_strip_16(png_ptr);\n\t}\n\n\t/* Convert transparency chunk to alpha channel. */\n\tif (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))  {\n\t\tpng_set_tRNS_to_alpha(png_ptr);\n\t}\n\n\t/* Convert gray images to RGB. */\n\tif (color_type == PNG_COLOR_TYPE_GRAY ||\n\t    color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {\n\t\tpng_set_gray_to_rgb(png_ptr);\n\t}\n\n\t/* Ignore alpha for now. */\n\tif (color_type & PNG_COLOR_MASK_ALPHA) {\n\t\tpng_set_strip_alpha(png_ptr);\n\t}\n\n\t/* Get image attributes. */\n\twidth = png_get_image_width(png_ptr, info_ptr);\n\theight = png_get_image_height(png_ptr, info_ptr);\n\tbytesPerPixel = 3; /* All images decompress to this size. */\n\tbytewidth = ADD_PADDING(width * bytesPerPixel); /* Align width. */\n\n\t/* Decompress the PNG row by row. */\n\tbitmapData = calloc(1, bytewidth * height);\n\trow = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr));\n\tif (bitmapData == NULL || row == NULL) goto bail;\n\tfor (y = 0; y < height; ++y) {\n\t\tpng_uint_32 x;\n\t\tconst uint32_t rowOffset = y * bytewidth;\n\t\tuint8_t *rowptr = row;\n\t\tpng_read_row(png_ptr, (png_byte *)row, NULL);\n\n\t\tfor (x = 0; x < width; ++x) {\n\t\t\tconst uint32_t colOffset = x * bytesPerPixel;\n\t\t\tMMRGBColor *color = (MMRGBColor *)(bitmapData + rowOffset + colOffset);\n\t\t\tcolor->red = *rowptr++;\n\t\t\tcolor->green = *rowptr++;\n\t\t\tcolor->blue = *rowptr++;\n\t\t}\n\t}\n\tfree(row);\n\n\t/* Finish reading. */\n\tpng_read_end(png_ptr, NULL);\n\tpng_destroy_read_struct(&png_ptr, &info_ptr, NULL);\n\tfclose(fp);\n\n\treturn createMMBitmap(bitmapData, width, height,\n\t                      bytewidth, bytesPerPixel * 8, bytesPerPixel);\n\nbail:\n\tpng_destroy_read_struct(&png_ptr, &info_ptr, NULL);\n\tfclose(fp);\n\treturn NULL;\n}\n\nstruct _PNGWriteInfo {\n\tpng_struct *png_ptr;\n\tpng_info *info_ptr;\n\tpng_byte **row_pointers;\n\tsize_t row_count;\n\tbool free_row_pointers;\n};\n\ntypedef struct _PNGWriteInfo PNGWriteInfo;\ntypedef PNGWriteInfo *PNGWriteInfoRef;\n\n/* Returns pointer to PNGWriteInfo struct containing data ready to be used with\n * functions such as png_write_png().\n *\n * It is the caller's responsibility to destroy() the returned structure with\n * destroyPNGWriteInfo(). */\nstatic PNGWriteInfoRef createPNGWriteInfo(MMBitmapRef bitmap)\n{\n\tPNGWriteInfoRef info = malloc(sizeof(PNGWriteInfo));\n\tpng_uint_32 y;\n\n\tif (info == NULL) return NULL;\n\tinfo->png_ptr = NULL;\n\tinfo->info_ptr = NULL;\n\tinfo->row_pointers = NULL;\n\n\tassert(bitmap != NULL);\n\n\t/* Initialize the write struct. */\n\tinfo->png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,\n\t                                        NULL, NULL, NULL);\n\tif (info->png_ptr == NULL) goto bail;\n\n\t/* Set up error handling. */\n\tif (setjmp(png_jmpbuf(info->png_ptr))) {\n\t\tpng_destroy_write_struct(&(info->png_ptr), &(info->info_ptr));\n\t\tgoto bail;\n\t}\n\n\t/* Initialize the info struct. */\n\tinfo->info_ptr = png_create_info_struct(info->png_ptr);\n\tif (info->info_ptr == NULL) {\n\t\tpng_destroy_write_struct(&(info->png_ptr), NULL);\n\t\tgoto bail;\n\t}\n\n\t/* Set image attributes. */\n\tpng_set_IHDR(info->png_ptr,\n\t             info->info_ptr,\n\t             (png_uint_32)bitmap->width,\n\t             (png_uint_32)bitmap->height,\n\t             8,\n\t             PNG_COLOR_TYPE_RGB,\n\t             PNG_INTERLACE_NONE,\n\t             PNG_COMPRESSION_TYPE_DEFAULT,\n\t             PNG_FILTER_TYPE_DEFAULT);\n\n\tinfo->row_count = bitmap->height;\n\tinfo->row_pointers = png_malloc(info->png_ptr,\n\t                                sizeof(png_byte *) * info->row_count);\n\n\tif (bitmap->bytesPerPixel == 3) {\n\t\t/* No alpha channel; image data can be copied directly. */\n\t\tfor (y = 0; y < info->row_count; ++y) {\n\t\t\tinfo->row_pointers[y] = bitmap->imageBuffer + (bitmap->bytewidth * y);\n\t\t}\n\t\tinfo->free_row_pointers = false;\n\n\t\t/* Convert BGR to RGB if necessary. */\n\t\tif (MMRGB_IS_BGR) {\n\t\t\tpng_set_bgr(info->png_ptr);\n\t\t}\n\t} else {\n\t\t/* Ignore alpha channel; copy image data row by row. */\n\t\tconst size_t bytesPerPixel = 3;\n\t\tconst size_t bytewidth = ADD_PADDING(bitmap->width * bytesPerPixel);\n\n\t\tfor (y = 0; y < info->row_count; ++y) {\n\t\t\tpng_uint_32 x;\n\t\t\tpng_byte *row_ptr = png_malloc(info->png_ptr, bytewidth);\n\t\t\tinfo->row_pointers[y] = row_ptr;\n\t\t\tfor (x = 0; x < bitmap->width; ++x) {\n\t\t\t\tMMRGBColor *color = MMRGBColorRefAtPoint(bitmap, x, y);\n\t\t\t\trow_ptr[0] = color->red;\n\t\t\t\trow_ptr[1] = color->green;\n\t\t\t\trow_ptr[2] = color->blue;\n\n\t\t\t\trow_ptr += bytesPerPixel;\n\t\t\t}\n\t\t}\n\t\tinfo->free_row_pointers = true;\n\t}\n\n\tpng_set_rows(info->png_ptr, info->info_ptr, info->row_pointers);\n\treturn info;\n\nbail:\n\tif (info != NULL) free(info);\n\treturn NULL;\n}\n\n/* Free memory in use by |info|. */\nstatic void destroyPNGWriteInfo(PNGWriteInfoRef info)\n{\n\tassert(info != NULL);\n\tif (info->row_pointers != NULL) {\n\t\tif (info->free_row_pointers) {\n\t\t\tsize_t y;\n\t\t\tfor (y = 0; y < info->row_count; ++y) {\n\t\t\t\tfree(info->row_pointers[y]);\n\t\t\t}\n\t\t}\n\t\tpng_free(info->png_ptr, info->row_pointers);\n\t}\n\n\tpng_destroy_write_struct(&(info->png_ptr), &(info->info_ptr));\n\tfree(info);\n}\n\nint saveMMBitmapAsPNG(MMBitmapRef bitmap, const char *path)\n{\n\tFILE *fp = fopen(path, \"wb\");\n\tPNGWriteInfoRef info;\n\tif (fp == NULL) return -1;\n\n\tif ((info = createPNGWriteInfo(bitmap)) == NULL) {\n\t\tfclose(fp);\n\t\treturn -1;\n\t}\n\n\tpng_init_io(info->png_ptr, fp);\n\tpng_write_png(info->png_ptr, info->info_ptr, PNG_TRANSFORM_IDENTITY, NULL);\n\tfclose(fp);\n\n\tdestroyPNGWriteInfo(info);\n\treturn 0;\n}\n\n/* Structure to store PNG image bytes. */\nstruct io_data\n{\n\tuint8_t *buffer; /* Pointer to raw file data. */\n\tsize_t size; /* Number of bytes actually written to buffer. */\n\tsize_t allocedSize; /* Number of bytes allocated for buffer. */\n};\n\n/* Called each time libpng attempts to write data in createPNGData(). */\nvoid png_append_data(png_struct *png_ptr,\n                     png_byte *new_data,\n                     png_size_t length)\n{\n\tstruct io_data *data = png_get_io_ptr(png_ptr);\n\tdata->size += length;\n\n\t/* Allocate or grow buffer. */\n\tif (data->buffer == NULL) {\n\t\tdata->allocedSize = data->size;\n\t\tdata->buffer = png_malloc(png_ptr, data->allocedSize);\n\t\tassert(data->buffer != NULL);\n\t} else if (data->allocedSize < data->size) {\n\t\tdo {\n\t\t\t/* Double size each time to avoid calls to realloc. */\n\t\t\tdata->allocedSize <<= 1;\n\t\t} while (data->allocedSize < data->size);\n\n\t\tdata->buffer = realloc(data->buffer, data->allocedSize);\n\t}\n\n\t/* Copy new bytes to end of buffer. */\n\tmemcpy(data->buffer + data->size - length, new_data, length);\n}\n\nuint8_t *createPNGData(MMBitmapRef bitmap, size_t *len)\n{\n\tPNGWriteInfoRef info = NULL;\n\tstruct io_data data = {NULL, 0, 0};\n\n\tassert(bitmap != NULL);\n\tassert(len != NULL);\n\n\tif ((info = createPNGWriteInfo(bitmap)) == NULL) return NULL;\n\n\tpng_set_write_fn(info->png_ptr, &data, &png_append_data, NULL);\n\tpng_write_png(info->png_ptr, info->info_ptr, PNG_TRANSFORM_IDENTITY, NULL);\n\n\tdestroyPNGWriteInfo(info);\n\n\t*len = data.size;\n\treturn data.buffer;\n}\n"
  },
  {
    "path": "src/png_io.h",
    "content": "#pragma once\n#ifndef PNG_IO_H\n#define PNG_IO_H\n\n#include \"MMBitmap.h\"\n#include \"io.h\"\n\nenum _PNGReadError {\n\tkPNGGenericError = 0,\n\tkPNGReadError,\n\tkPNGAccessError,\n\tkPNGInvalidHeaderError\n};\n\ntypedef MMIOError MMPNGReadError;\n\n/* Returns description of given MMPNGReadError.\n * Returned string is constant and hence should not be freed. */\nconst char *MMPNGReadErrorString(MMIOError error);\n\n/* Attempts to read PNG file at path; returns new MMBitmap on success, or\n * NULL on error. If |error| is non-NULL, it will be set to the error code\n * on return.\n * Responsibility for destroy()'ing returned MMBitmap is left up to caller. */\nMMBitmapRef newMMBitmapFromPNG(const char *path, MMPNGReadError *error);\n\n/* Attempts to write PNG at path; returns 0 on success, -1 on error. */\nint saveMMBitmapAsPNG(MMBitmapRef bitmap, const char *path);\n\n/* Returns a buffer containing the raw PNG file data, ready to be saved to a\n * file. |len| will be set to the number of bytes allocated in the returned\n * buffer (it cannot be NULL).\n *\n * Responsibility for free()'ing data is left up to the caller. */\nuint8_t *createPNGData(MMBitmapRef bitmap, size_t *len);\n\n#endif /* PNG_IO_H */\n"
  },
  {
    "path": "src/rgb.h",
    "content": "#pragma once\n#ifndef RGB_H\n#define RGB_H\n\n#include <stdlib.h> /* For abs() */\n#include <math.h>\n#include \"inline_keywords.h\" /* For H_INLINE */\n#include <stdint.h>\n\n\n/* RGB colors in MMBitmaps are stored as BGR for convenience in converting\n * to/from certain formats (mainly OpenGL).\n *\n * It is best not to rely on the order (simply use rgb.{blue,green,red} to\n * access values), but some situations (e.g., glReadPixels) require one to\n * do so. In that case, check to make sure to use MMRGB_IS_BGR for future\n * compatibility. */\n\n/* #define MMRGB_IS_BGR (offsetof(MMRGBColor, red) > offsetof(MMRGBColor, blue)) */\n#define MMRGB_IS_BGR 1\n\nstruct _MMRGBColor {\n\tuint8_t blue;\n\tuint8_t green;\n\tuint8_t red;\n};\n\ntypedef struct _MMRGBColor MMRGBColor;\n\n/* MMRGBHex is a hexadecimal color value, akin to HTML's, in the form 0xRRGGBB\n * where RR is the red value expressed as hexadecimal, GG is the green value,\n * and BB is the blue value. */\ntypedef uint32_t MMRGBHex;\n\n#define MMRGBHEX_MIN 0x000000\n#define MMRGBHEX_MAX 0xFFFFFF\n\n/* Converts rgb color to hexadecimal value.\n * |red|, |green|, and |blue| should each be of the type |uint8_t|, where the\n * range is 0 - 255. */\n#define RGB_TO_HEX(red, green, blue) (((red) << 16) | ((green) << 8) | (blue))\n\n/* Convenience wrapper for MMRGBColors. */\nH_INLINE MMRGBHex hexFromMMRGB(MMRGBColor rgb)\n{\n\treturn RGB_TO_HEX(rgb.red, rgb.green, rgb.blue);\n}\n\n#define RED_FROM_HEX(hex) ((hex >> 16) & 0xFF)\n#define GREEN_FROM_HEX(hex) ((hex >> 8) & 0xFF)\n#define BLUE_FROM_HEX(hex) (hex & 0xFF)\n\n/* Converts hexadecimal color to MMRGBColor. */\nH_INLINE MMRGBColor MMRGBFromHex(MMRGBHex hex)\n{\n\tMMRGBColor color;\n\tcolor.red = RED_FROM_HEX(hex);\n\tcolor.green = GREEN_FROM_HEX(hex);\n\tcolor.blue = BLUE_FROM_HEX(hex);\n\treturn color;\n}\n\n/* Check absolute equality of two RGB colors. */\n#define MMRGBColorEqualToColor(c1, c2) ((c1).red == (c2).red && \\\n                                        (c1).blue == (c2).blue && \\\n                                        (c1).green == (c2).green)\n\n/* Returns whether two colors are similar within the given range, |tolerance|.\n * Tolerance can be in the range 0.0f - 1.0f, where 0 denotes the exact\n * color and 1 denotes any color. */\nH_INLINE int MMRGBColorSimilarToColor(MMRGBColor c1, MMRGBColor c2,\n                                      float tolerance)\n{\n\t/* Speedy case */\n\tif (tolerance <= 0.0f) {\n\t\treturn MMRGBColorEqualToColor(c1, c2);\n\t} else { /* Otherwise, use a Euclidean space to determine similarity */\n\t\tuint8_t d1 = c1.red - c2.red;\n\t\tuint8_t d2 = c1.green - c2.green;\n\t\tuint8_t d3 = c1.blue - c2.blue;\n\t\treturn sqrt((double)(d1 * d1) +\n\t\t            (d2 * d2) +\n\t\t            (d3 * d3)) <= (tolerance * 442.0f);\n\t}\n\n}\n\n/* Identical to MMRGBColorSimilarToColor, only for hex values. */\nH_INLINE int MMRGBHexSimilarToColor(MMRGBHex h1, MMRGBHex h2, float tolerance)\n{\n\tif (tolerance <= 0.0f) {\n\t\treturn h1 == h2;\n\t} else {\n\t\tuint8_t d1 = RED_FROM_HEX(h1) - RED_FROM_HEX(h2);\n\t\tuint8_t d2 = GREEN_FROM_HEX(h1) - GREEN_FROM_HEX(h2);\n\t\tuint8_t d3 = BLUE_FROM_HEX(h1) - BLUE_FROM_HEX(h2);\n\t\treturn sqrt((double)(d1 * d1) +\n\t\t            (d2 * d2) +\n\t\t            (d3 * d3)) <= (tolerance * 442.0f);\n\t}\n}\n\n#endif /* RGB_H */\n"
  },
  {
    "path": "src/robotjs.cc",
    "content": "#include <napi.h>\n#include <vector>\n#include \"mouse.h\"\n#include \"deadbeef_rand.h\"\n#include \"keypress.h\"\n#include \"screen.h\"\n#include \"screengrab.h\"\n#include \"MMBitmap.h\"\n#include \"snprintf.h\"\n#include \"microsleep.h\"\n#if defined(USE_X11)\n\t#include \"xdisplay.h\"\n#endif\n\n//Global delays.\nint mouseDelay = 10;\nint keyboardDelay = 10;\n\n/*\n __  __\n|  \\/  | ___  _   _ ___  ___\n| |\\/| |/ _ \\| | | / __|/ _ \\\n| |  | | (_) | |_| \\__ \\  __/\n|_|  |_|\\___/ \\__,_|___/\\___|\n\n*/\n\nint CheckMouseButton(const char * const b, MMMouseButton * const button)\n{\n\tif (!button) return -1;\n\n\tif (strcmp(b, \"left\") == 0)\n\t{\n\t\t*button = LEFT_BUTTON;\n\t}\n\telse if (strcmp(b, \"right\") == 0)\n\t{\n\t\t*button = RIGHT_BUTTON;\n\t}\n\telse if (strcmp(b, \"middle\") == 0)\n\t{\n\t\t*button = CENTER_BUTTON;\n\t}\n\telse\n\t{\n\t\treturn -2;\n\t}\n\n\treturn 0;\n}\n\nNapi::Value dragMouseWrapper(const Napi::CallbackInfo& info)\n{\n\tNapi::Env env = info.Env();\n\n\tif (info.Length() < 2 || info.Length() > 3)\n\t{\n\t\tNapi::Error::New(env, \"Invalid number of arguments.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t}\n\n\tconst int32_t x = info[0].As<Napi::Number>().Int32Value();\n\tconst int32_t y = info[1].As<Napi::Number>().Int32Value();\n\tMMMouseButton button = LEFT_BUTTON;\n\n\tif (info.Length() == 3)\n\t{\n\t\tstd::string bstr = info[2].As<Napi::String>().Utf8Value();\n\t\tconst char * const b = bstr.c_str();\n\n\t\tswitch (CheckMouseButton(b, &button))\n\t\t{\n\t\t\tcase -1:\n\t\t\t\tNapi::Error::New(env, \"Null pointer in mouse button code.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t\t\t\tbreak;\n\t\t\tcase -2:\n\t\t\t\tNapi::Error::New(env, \"Invalid mouse button specified.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tMMSignedPoint point;\n\tpoint = MMSignedPointMake(x, y);\n\tdragMouse(point, button);\n\tmicrosleep(mouseDelay);\n\n\treturn Napi::Number::New(env, 1);\n}\n\nNapi::Value updateScreenMetricsWrapper(const Napi::CallbackInfo& info)\n{\n\tNapi::Env env = info.Env();\n\n\tupdateScreenMetrics();\n\n\treturn Napi::Number::New(env, 1);\n}\n\nNapi::Value moveMouseWrapper(const Napi::CallbackInfo& info)\n{\n\tNapi::Env env = info.Env();\n\n\tif (info.Length() != 2)\n\t{\n\t\tNapi::Error::New(env, \"Invalid number of arguments.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t}\n\n\tint32_t x = info[0].As<Napi::Number>().Int32Value();\n\tint32_t y = info[1].As<Napi::Number>().Int32Value();\n\n\tMMSignedPoint point;\n\tpoint = MMSignedPointMake(x, y);\n\tmoveMouse(point);\n\tmicrosleep(mouseDelay);\n\n\treturn Napi::Number::New(env, 1);\n}\n\nNapi::Value moveMouseSmoothWrapper(const Napi::CallbackInfo& info)\n{\n\tNapi::Env env = info.Env();\n\n\tif (info.Length() > 3 || info.Length() < 2 )\n\t{\n\t\tNapi::Error::New(env, \"Invalid number of arguments.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t}\n\tsize_t x = info[0].As<Napi::Number>().Int32Value();\n\tsize_t y = info[1].As<Napi::Number>().Int32Value();\n\n\tMMPoint point;\n\tpoint = MMPointMake(x, y);\n\tif (info.Length() == 3)\n\t{\n\t\tsize_t speed = info[2].As<Napi::Number>().Int32Value();\n\t\tsmoothlyMoveMouse(point, speed);\n\t}\n\telse\n\t{\n\t\tsmoothlyMoveMouse(point, 3.0);\n\t}\n\tmicrosleep(mouseDelay);\n\n\treturn Napi::Number::New(env, 1);\n}\n\nNapi::Value getMousePosWrapper(const Napi::CallbackInfo& info)\n{\n\tNapi::Env env = info.Env();\n\n\tMMSignedPoint pos = getMousePos();\n\n\t//Return object with .x and .y.\n\tNapi::Object obj = Napi::Object::New(env);\n\tobj.Set(Napi::String::New(env, \"x\"), Napi::Number::New(env, (int)pos.x));\n\tobj.Set(Napi::String::New(env, \"y\"), Napi::Number::New(env, (int)pos.y));\n\treturn obj;\n}\n\nNapi::Value mouseClickWrapper(const Napi::CallbackInfo& info)\n{\n\tNapi::Env env = info.Env();\n\n\tMMMouseButton button = LEFT_BUTTON;\n\tbool doubleC = false;\n\n\tif (info.Length() > 0)\n\t{\n\t\tstd::string bstr = info[0].As<Napi::String>().Utf8Value();\n\t\tconst char * const b = bstr.c_str();\n\n\t\tswitch (CheckMouseButton(b, &button))\n\t\t{\n\t\t\tcase -1:\n\t\t\t\tNapi::Error::New(env, \"Null pointer in mouse button code.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t\t\t\tbreak;\n\t\t\tcase -2:\n\t\t\t\tNapi::Error::New(env, \"Invalid mouse button specified.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (info.Length() == 2)\n\t{\n\t\tdoubleC = info[1].As<Napi::Boolean>().Value();\n\t}\n\telse if (info.Length() > 2)\n\t{\n\t\tNapi::Error::New(env, \"Invalid number of arguments.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t}\n\n\tif (!doubleC)\n\t{\n\t\tclickMouse(button);\n\t}\n\telse\n\t{\n\t\tdoubleClick(button);\n\t}\n\n\tmicrosleep(mouseDelay);\n\n\treturn Napi::Number::New(env, 1);\n}\n\nNapi::Value mouseToggleWrapper(const Napi::CallbackInfo& info)\n{\n\tNapi::Env env = info.Env();\n\n\tMMMouseButton button = LEFT_BUTTON;\n\tbool down = false;\n\n\tif (info.Length() > 0)\n\t{\n\t\tconst char *d;\n\n\t\tstd::string dstr = info[0].As<Napi::String>();\n\t\td = dstr.c_str();\n\n\t\tif (strcmp(d, \"down\") == 0)\n\t\t{\n\t\t\tdown = true;\n\t\t}\n\t\telse if (strcmp(d, \"up\") == 0)\n\t\t{\n\t\t\tdown = false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tNapi::Error::New(env, \"Invalid mouse button state specified.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t\t}\n\t}\n\n\tif (info.Length() == 2)\n\t{\n\t\tstd::string bstr = info[1].As<Napi::String>();\n\t\tconst char * const b = bstr.c_str();\n\n\t\tswitch (CheckMouseButton(b, &button))\n\t\t{\n\t\t\tcase -1:\n\t\t\t\tNapi::Error::New(env, \"Null pointer in mouse button code.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t\t\t\tbreak;\n\t\t\tcase -2:\n\t\t\t\tNapi::Error::New(env, \"Invalid mouse button specified.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t\t\t\tbreak;\n\t\t}\n\t}\n\telse if (info.Length() > 2)\n\t{\n\t\tNapi::Error::New(env, \"Invalid number of arguments.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t}\n\n\ttoggleMouse(down, button);\n\tmicrosleep(mouseDelay);\n\n\treturn Napi::Number::New(env, 1);\n}\n\nNapi::Value setMouseDelayWrapper(const Napi::CallbackInfo& info)\n{\n\tNapi::Env env = info.Env();\n\n\tif (info.Length() != 1)\n\t{\n\t\tNapi::Error::New(env, \"Invalid number of arguments.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t}\n\n\tmouseDelay = info[0].As<Napi::Number>().Int32Value();\n\n\treturn Napi::Number::New(env, 1);\n}\n\nNapi::Value scrollMouseWrapper(const Napi::CallbackInfo& info)\n{\n\tNapi::Env env = info.Env();\n\n\tif (info.Length() != 2)\n\t{\n    \tNapi::Error::New(env, \"Invalid number of arguments.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t}\n\n\tint x = info[0].As<Napi::Number>().Int32Value();\n\tint y = info[1].As<Napi::Number>().Int32Value();\n\n\tscrollMouse(x, y);\n\tmicrosleep(mouseDelay);\n\n\treturn Napi::Number::New(env, 1);\n}\n/*\n _  __          _                         _\n| |/ /___ _   _| |__   ___   __ _ _ __ __| |\n| ' // _ \\ | | | '_ \\ / _ \\ / _` | '__/ _` |\n| . \\  __/ |_| | |_) | (_) | (_| | | | (_| |\n|_|\\_\\___|\\__, |_.__/ \\___/ \\__,_|_|  \\__,_|\n          |___/\n*/\nstruct KeyNames\n{\n\tconst char* name;\n\tMMKeyCode   key;\n};\n\nstatic KeyNames key_names[] =\n{\n\t{ \"backspace\",      K_BACKSPACE },\n\t{ \"delete\",         K_DELETE },\n\t{ \"enter\",          K_RETURN },\n\t{ \"tab\",            K_TAB },\n\t{ \"escape\",         K_ESCAPE },\n\t{ \"up\",             K_UP },\n\t{ \"down\",           K_DOWN },\n\t{ \"right\",          K_RIGHT },\n\t{ \"left\",           K_LEFT },\n\t{ \"home\",           K_HOME },\n\t{ \"end\",            K_END },\n\t{ \"pageup\",         K_PAGEUP },\n\t{ \"pagedown\",       K_PAGEDOWN },\n\t{ \"f1\",             K_F1 },\n\t{ \"f2\",             K_F2 },\n\t{ \"f3\",             K_F3 },\n\t{ \"f4\",             K_F4 },\n\t{ \"f5\",             K_F5 },\n\t{ \"f6\",             K_F6 },\n\t{ \"f7\",             K_F7 },\n\t{ \"f8\",             K_F8 },\n\t{ \"f9\",             K_F9 },\n\t{ \"f10\",            K_F10 },\n\t{ \"f11\",            K_F11 },\n\t{ \"f12\",            K_F12 },\n\t{ \"f13\",            K_F13 },\n\t{ \"f14\",            K_F14 },\n\t{ \"f15\",            K_F15 },\n\t{ \"f16\",            K_F16 },\n\t{ \"f17\",            K_F17 },\n\t{ \"f18\",            K_F18 },\n\t{ \"f19\",            K_F19 },\n\t{ \"f20\",            K_F20 },\n\t{ \"f21\",            K_F21 },\n\t{ \"f22\",            K_F22 },\n\t{ \"f23\",            K_F23 },\n\t{ \"f24\",            K_F24 },\n\t{ \"capslock\",       K_CAPSLOCK },\n\t{ \"command\",        K_META },\n\t{ \"alt\",            K_ALT },\n\t{ \"right_alt\",      K_RIGHT_ALT },\n\t{ \"control\",        K_CONTROL },\n\t{ \"left_control\",   K_LEFT_CONTROL },\n\t{ \"right_control\",  K_RIGHT_CONTROL },\n\t{ \"shift\",          K_SHIFT },\n\t{ \"right_shift\",    K_RIGHTSHIFT },\n\t{ \"space\",          K_SPACE },\n\t{ \"printscreen\",    K_PRINTSCREEN },\n\t{ \"insert\",         K_INSERT },\n\t{ \"menu\",           K_MENU },\n\n\t{ \"audio_mute\",     K_AUDIO_VOLUME_MUTE },\n\t{ \"audio_vol_down\", K_AUDIO_VOLUME_DOWN },\n\t{ \"audio_vol_up\",   K_AUDIO_VOLUME_UP },\n\t{ \"audio_play\",     K_AUDIO_PLAY },\n\t{ \"audio_stop\",     K_AUDIO_STOP },\n\t{ \"audio_pause\",    K_AUDIO_PAUSE },\n\t{ \"audio_prev\",     K_AUDIO_PREV },\n\t{ \"audio_next\",     K_AUDIO_NEXT },\n\t{ \"audio_rewind\",   K_AUDIO_REWIND },\n\t{ \"audio_forward\",  K_AUDIO_FORWARD },\n\t{ \"audio_repeat\",   K_AUDIO_REPEAT },\n\t{ \"audio_random\",   K_AUDIO_RANDOM },\n\n\t{ \"numpad_lock\",\tK_NUMPAD_LOCK },\n\t{ \"numpad_0\",\t\tK_NUMPAD_0 },\n\t{ \"numpad_0\",\t\tK_NUMPAD_0 },\n\t{ \"numpad_1\",\t\tK_NUMPAD_1 },\n\t{ \"numpad_2\",\t\tK_NUMPAD_2 },\n\t{ \"numpad_3\",\t\tK_NUMPAD_3 },\n\t{ \"numpad_4\",\t\tK_NUMPAD_4 },\n\t{ \"numpad_5\",\t\tK_NUMPAD_5 },\n\t{ \"numpad_6\",\t\tK_NUMPAD_6 },\n\t{ \"numpad_7\",\t\tK_NUMPAD_7 },\n\t{ \"numpad_8\",\t\tK_NUMPAD_8 },\n\t{ \"numpad_9\",\t\tK_NUMPAD_9 },\n\t{ \"numpad_+\",\t\tK_NUMPAD_PLUS },\n\t{ \"numpad_-\",\t\tK_NUMPAD_MINUS },\n\t{ \"numpad_*\",\t\tK_NUMPAD_MULTIPLY },\n\t{ \"numpad_/\",\t\tK_NUMPAD_DIVIDE },\n\t{ \"numpad_.\",\t\tK_NUMPAD_DECIMAL },\n\n\t{ \"lights_mon_up\",    K_LIGHTS_MON_UP },\n\t{ \"lights_mon_down\",  K_LIGHTS_MON_DOWN },\n\t{ \"lights_kbd_toggle\",K_LIGHTS_KBD_TOGGLE },\n\t{ \"lights_kbd_up\",    K_LIGHTS_KBD_UP },\n\t{ \"lights_kbd_down\",  K_LIGHTS_KBD_DOWN },\n\n\t{ NULL,               K_NOT_A_KEY } /* end marker */\n};\n\nint CheckKeyCodes(const char* k, MMKeyCode *key)\n{\n\tif (!key) return -1;\n\n\tif (strlen(k) == 1)\n\t{\n\t\t*key = keyCodeForChar(*k);\n\t\treturn 0;\n\t}\n\n\t*key = K_NOT_A_KEY;\n\n\tKeyNames* kn = key_names;\n\twhile (kn->name)\n\t{\n\t\tif (strcmp(k, kn->name) == 0)\n\t\t{\n\t\t\t*key = kn->key;\n\t\t\tbreak;\n\t\t}\n\t\tkn++;\n\t}\n\n\tif (*key == K_NOT_A_KEY)\n\t{\n\t\treturn -2;\n\t}\n\n\treturn 0;\n}\n\nint CheckKeyFlags(const char* f, MMKeyFlags* flags)\n{\n\tif (!flags) return -1;\n\n\tif (strcmp(f, \"alt\") == 0 || strcmp(f, \"right_alt\") == 0)\n\t{\n\t\t*flags = MOD_ALT;\n\t}\n\telse if(strcmp(f, \"command\") == 0)\n\t{\n\t\t*flags = MOD_META;\n\t}\n\telse if(strcmp(f, \"control\") == 0 || strcmp(f, \"right_control\") == 0 || strcmp(f, \"left_control\") == 0)\n\t{\n\t\t*flags = MOD_CONTROL;\n\t}\n\telse if(strcmp(f, \"shift\") == 0 || strcmp(f, \"right_shift\") == 0)\n\t{\n\t\t*flags = MOD_SHIFT;\n\t}\n\telse if(strcmp(f, \"none\") == 0)\n\t{\n\t\t*flags = MOD_NONE;\n\t}\n\telse\n\t{\n\t\treturn -2;\n\t}\n\n\treturn 0;\n}\n\nint GetFlagsFromString(Napi::Value value, MMKeyFlags* flags) {\n\tNapi::Env env = value.Env();\n\tNapi::String fstr(env, value.ToString());\n\treturn CheckKeyFlags(fstr.Utf8Value().c_str(), flags);\n}\n\nint GetFlagsFromValue(Napi::Value value, MMKeyFlags* flags) {\n\tif (!flags) return -1;\n\n\t//Optionally allow an array of flag strings to be passed.\n\tif (value.IsArray())\n\t{\n\t\tNapi::Array a = value.As<Napi::Array>();\n\t\tfor (uint32_t i = 0; i < a.Length(); i++)\n\t\t{\n\t\t  if ((a).Has(i)) {\n                Napi::Value v((a).Get(i));\n                if (!v.IsString()) return -2;\n\n                MMKeyFlags f = MOD_NONE;\n                const int rv = GetFlagsFromString(v, &f);\n                if (rv) return rv;\n\n                *flags = (MMKeyFlags)(*flags | f);\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\t//If it's not an array, it should be a single string value.\n\treturn GetFlagsFromString(value, flags);\n}\n\nNapi::Value keyTapWrapper(const Napi::CallbackInfo& info)\n{\n\tNapi::Env env = info.Env();\n\n\tMMKeyFlags flags = MOD_NONE;\n\tMMKeyCode key;\n\tconst char *k;\n\n\tNapi::String kstr(env, info[0].ToString());\n\tk = kstr.Utf8Value().c_str();\n\n\tswitch (info.Length())\n\t{\n\t\tcase 2:\n\t\t\tswitch (GetFlagsFromValue(info[1], &flags))\n\t\t\t{\n\t\t\t\tcase -1:\n\t\t\t\t\tNapi::Error::New(env, \"Null pointer in key flag.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t\t\t\t\tbreak;\n\t\t\t\tcase -2:\n\t\t\t\t\tNapi::Error::New(env, \"Invalid key flag specified.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tNapi::Error::New(env, \"Invalid number of arguments.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t}\n\n\tswitch(CheckKeyCodes(k, &key))\n\t{\n\t\tcase -1:\n\t\t\tNapi::Error::New(env, \"Null pointer in key code.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t\t\tbreak;\n\t\tcase -2:\n\t\t\tNapi::Error::New(env, \"Invalid key code specified.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t\t\tbreak;\n\t\tdefault:\n\t\t\ttoggleKeyCode(key, true, flags);\n\t\t\tmicrosleep(keyboardDelay);\n\t\t\ttoggleKeyCode(key, false, flags);\n\t\t\tmicrosleep(keyboardDelay);\n\t\t\tbreak;\n\t}\n\n\treturn Napi::Number::New(env, 1);\n}\n\n\nNapi::Value keyToggleWrapper(const Napi::CallbackInfo& info)\n{\n\tNapi::Env env = info.Env();\n\n\tMMKeyFlags flags = MOD_NONE;\n\tMMKeyCode key;\n\n\tbool down;\n\tconst char *k;\n\n\t//Get arguments from JavaScript.\n\tstd::string kstr = info[0].As<Napi::String>();\n\n\t//Convert arguments to chars.\n\tk = kstr.c_str();\n\n\t//Check and confirm number of arguments.\n\tswitch (info.Length())\n\t{\n\t\tcase 3:\n\t\t\t//Get key modifier.\n\t\t\tswitch (GetFlagsFromValue(info[2], &flags))\n\t\t\t{\n\t\t\t\tcase -1:\n\t\t\t\t\tNapi::Error::New(env, \"Null pointer in key flag.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t\t\t\t\tbreak;\n\t\t\t\tcase -2:\n\t\t\t\t\tNapi::Error::New(env, \"Invalid key flag specified.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tNapi::Error::New(env, \"Invalid number of arguments.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t}\n\n\t//Get down value if provided.\n\tif (info.Length() > 1)\n\t{\n\t\tconst char *d;\n\n\t\tstd::string dstr = info[1].As<Napi::String>();\n\t\td = dstr.c_str();\n\n\t\tif (strcmp(d, \"down\") == 0)\n\t\t{\n\t\t\tdown = true;\n\t\t}\n\t\telse if (strcmp(d, \"up\") == 0)\n\t\t{\n\t\t\tdown = false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tNapi::Error::New(env, \"Invalid key state specified.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t\t}\n\t}\n\n\t//Get the actual key.\n\tswitch(CheckKeyCodes(k, &key))\n\t{\n\t\tcase -1:\n\t\t\tNapi::Error::New(env, \"Null pointer in key code.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t\t\tbreak;\n\t\tcase -2:\n\t\t\tNapi::Error::New(env, \"Invalid key code specified.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t\t\tbreak;\n\t\tdefault:\n\t\t\ttoggleKeyCode(key, down, flags);\n\t\t\tmicrosleep(keyboardDelay);\n\t}\n\n\treturn Napi::Number::New(env, 1);\n}\n\nNapi::Value unicodeTapWrapper(const Napi::CallbackInfo& info)\n{\n\tNapi::Env env = info.Env();\n\n\tsize_t value = info[0].As<Napi::Number>().Int32Value();\n\n\tif (value != 0) {\n\t\tunicodeTap(value);\n\n\t\treturn Napi::Number::New(env, 1);\n\t} else {\n\t\tNapi::Error::New(env, \"Invalid character typed.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t}\n}\n\nNapi::Value typeStringWrapper(const Napi::CallbackInfo& info)\n{\n\tNapi::Env env = info.Env();\n\n\tif (info.Length() > 0) {\n\t\tconst char *s;\n\t\tstd::string str = info[0].As<Napi::String>();\n\n\t\ts = str.c_str();\n\n\t\ttypeStringDelayed(s, 0);\n\n\t\treturn Napi::Number::New(env, 1);\n\t} else {\n\t\tNapi::Error::New(env, \"Invalid number of arguments.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t}\n}\n\nNapi::Value typeStringDelayedWrapper(const Napi::CallbackInfo& info)\n{\n\tNapi::Env env = info.Env();\n\n\tif (info.Length() > 0) {\n\t\tconst char *s;\n\t\tstd::string str = info[0].As<Napi::String>();\n\n\t\ts = str.c_str();\n\n\tsize_t cpm = info[1].As<Napi::Number>().Int32Value();\n\n\t\ttypeStringDelayed(s, cpm);\n\n\t\treturn Napi::Number::New(env, 1);\n\t} else {\n\t\tNapi::Error::New(env, \"Invalid number of arguments.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t}\n}\n\nNapi::Value setKeyboardDelayWrapper(const Napi::CallbackInfo& info)\n{\n\tNapi::Env env = info.Env();\n\n\tif (info.Length() != 1)\n\t{\n\t\tNapi::Error::New(env, \"Invalid number of arguments.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t}\n\n\tkeyboardDelay = info[0].As<Napi::Number>().Int32Value();\n\n\treturn Napi::Number::New(env, 1);\n}\n\n/*\n  ____\n / ___|  ___ _ __ ___  ___ _ __\n \\___ \\ / __| '__/ _ \\/ _ \\ '_ \\\n  ___) | (__| | |  __/  __/ | | |\n |____/ \\___|_|  \\___|\\___|_| |_|\n\n*/\n\n/**\n * Pad hex color code with leading zeros.\n * @param color Hex value to pad.\n * @param hex   Hex value to output.\n */\nvoid padHex(MMRGBHex color, char* hex)\n{\n\t//Length needs to be 7 because snprintf includes a terminating null.\n\t//Use %06x to pad hex value with leading 0s.\n\tsnprintf(hex, 7, \"%06x\", color);\n}\n\nNapi::Value getPixelColorWrapper(const Napi::CallbackInfo& info)\n{\n\tNapi::Env env = info.Env();\n\n\tif (info.Length() != 2)\n\t{\n\t\tNapi::Error::New(env, \"Invalid number of arguments.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t}\n\n\tMMBitmapRef bitmap;\n\tMMRGBHex color;\n\n\tsize_t x = info[0].As<Napi::Number>().Int32Value();\n\tsize_t y = info[1].As<Napi::Number>().Int32Value();\n\n\tif (!pointVisibleOnMainDisplay(MMPointMake(x, y)))\n\t{\n\t\tNapi::Error::New(env, \"Requested coordinates are outside the main screen's dimensions.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t}\n\n\tbitmap = copyMMBitmapFromDisplayInRect(MMRectMake(x, y, 1, 1));\n\n\tcolor = MMRGBHexAtPoint(bitmap, 0, 0);\n\n\tchar hex[7];\n\n\tpadHex(color, hex);\n\n\tdestroyMMBitmap(bitmap);\n\n\treturn Napi::String::New(env, hex);\n}\n\nNapi::Value getScreenSizeWrapper(const Napi::CallbackInfo& info)\n{\n\tNapi::Env env = info.Env();\n\n\t//Get display size.\n\tMMSize displaySize = getMainDisplaySize();\n\n\t//Create our return object.\n\tNapi::Object obj = Napi::Object::New(env);\n\tobj.Set(Napi::String::New(env, \"width\"), Napi::Number::New(env, displaySize.width));\n\tobj.Set(Napi::String::New(env, \"height\"), Napi::Number::New(env, displaySize.height));\n\n\t//Return our object with .width and .height.\n\treturn obj;\n}\n\nNapi::Value getXDisplayNameWrapper(const Napi::CallbackInfo& info)\n{\n\tNapi::Env env = info.Env();\n\n\t#if defined(USE_X11)\n\tconst char* display = getXDisplay();\n\treturn Napi::String::New(env, display);\n\t#else\n\tNapi::Error::New(env, \"getXDisplayName is only supported on Linux\").ThrowAsJavaScriptException();\n\treturn env.Null();\n\t#endif\n}\n\nNapi::Value setXDisplayNameWrapper(const Napi::CallbackInfo& info)\n{\n\tNapi::Env env = info.Env();\n\n\t#if defined(USE_X11)\n\tstd::string string = info[0].As<Napi::String>();\n\tsetXDisplay(string.c_str());\n\treturn Napi::Number::New(env, 1);\n\t#else\n\tNapi::Error::New(env, \"setXDisplayName is only supported on Linux\").ThrowAsJavaScriptException();\n\treturn env.Null();\n\t#endif\n}\n\nNapi::Value captureScreenWrapper(const Napi::CallbackInfo& info)\n{\n\tNapi::Env env = info.Env();\n\tsize_t x;\n\tsize_t y;\n\tsize_t w;\n\tsize_t h;\n\n\t//If user has provided screen coords, use them!\n\tif (info.Length() == 4)\n\t{\n\t\t//TODO: Make sure requested coords are within the screen bounds, or we get a seg fault.\n\t\t// \t\tAn error message is much nicer!\n\n\t\tx = info[0].As<Napi::Number>().Int32Value();\n\t\ty = info[1].As<Napi::Number>().Int32Value();\n\t\tw = info[2].As<Napi::Number>().Int32Value();\n\t\th = info[3].As<Napi::Number>().Int32Value();\n\t}\n\telse\n\t{\n\t\t//We're getting the full screen.\n\t\tx = 0;\n\t\ty = 0;\n\n\t\t//Get screen size.\n\t\tMMSize displaySize = getMainDisplaySize();\n\t\tw = displaySize.width;\n\t\th = displaySize.height;\n\t}\n\n\tMMBitmapRef bitmap = copyMMBitmapFromDisplayInRect(MMRectMake(x, y, w, h));\n\n\tuint32_t bufferSize = bitmap->bytewidth * bitmap->height;\n\tNapi::Object buffer = Napi::Buffer<char>::Copy(env, (char*)bitmap->imageBuffer, bufferSize);\n\n\tNapi::Object obj = Napi::Object::New(env);\n\tobj.Set(\"width\", Napi::Number::New(env, bitmap->width));\n\tobj.Set(\"height\", Napi::Number::New(env, bitmap->height));\n\tobj.Set(\"byteWidth\", Napi::Number::New(env, bitmap->bytewidth));\n\tobj.Set(\"bitsPerPixel\", Napi::Number::New(env, bitmap->bitsPerPixel));\n\tobj.Set(\"bytesPerPixel\", Napi::Number::New(env, bitmap->bytesPerPixel));\n\tobj.Set(\"image\", buffer);\n\n\treturn obj;\n}\n\n/*\n ____  _ _\n| __ )(_) |_ _ __ ___   __ _ _ __\n|  _ \\| | __| '_ ` _ \\ / _` | '_ \\\n| |_) | | |_| | | | | | (_| | |_) |\n|____/|_|\\__|_| |_| |_|\\__,_| .__/\n                            |_|\n */\n\nclass BMP\n{\n\tpublic:\n\t\tsize_t width;\n\t\tsize_t height;\n\t\tsize_t byteWidth;\n\t\tuint8_t bitsPerPixel;\n\t\tuint8_t bytesPerPixel;\n\t\tuint8_t *image;\n};\n\n//Convert object from Javascript to a C++ class (BMP).\nBMP buildBMP(Napi::Object obj)\n{\n\tBMP img;\n\n\timg.width = obj.Get(\"width\").As<Napi::Number>().Uint32Value();\n\timg.height = obj.Get(\"height\").As<Napi::Number>().Uint32Value();\n\timg.byteWidth = obj.Get(\"byteWidth\").As<Napi::Number>().Uint32Value();\n\timg.bitsPerPixel = obj.Get(\"bitsPerPixel\").As<Napi::Number>().Uint32Value();\n\timg.bytesPerPixel = obj.Get(\"bytesPerPixel\").As<Napi::Number>().Uint32Value();\n\n\tchar* buf = obj.Get(\"image\").As<Napi::Buffer<char>>().Data();\n\n\t//Convert the buffer to a uint8_t which createMMBitmap requires.\n\timg.image = (uint8_t *)malloc(img.byteWidth * img.height);\n\tmemcpy(img.image, buf, img.byteWidth * img.height);\n\n\treturn img;\n }\n\nNapi::Value getColorWrapper(const Napi::CallbackInfo& info)\n{\n\tNapi::Env env = info.Env();\n\tMMBitmapRef bitmap;\n\tMMRGBHex color;\n\n\tsize_t x = info[1].As<Napi::Number>().Int32Value();\n\tsize_t y = info[2].As<Napi::Number>().Int32Value();\n\n\t//Get our image object from JavaScript.\n\tBMP img = buildBMP(info[0].ToObject());\n\n\t//Create the bitmap.\n\tbitmap = createMMBitmap(img.image, img.width, img.height, img.byteWidth, img.bitsPerPixel, img.bytesPerPixel);\n\n\t// Make sure the requested pixel is inside the bitmap.\n\tif (!MMBitmapPointInBounds(bitmap, MMPointMake(x, y)))\n\t{\n\t\tNapi::Error::New(env, \"Requested coordinates are outside the bitmap's dimensions.\").ThrowAsJavaScriptException();\nreturn env.Null();\n\t}\n\n\tcolor = MMRGBHexAtPoint(bitmap, x, y);\n\n\tchar hex[7];\n\n\tpadHex(color, hex);\n\n\tdestroyMMBitmap(bitmap);\n\n\treturn Napi::String::New(env, hex);\n\n}\n\nNapi::Object InitAll(Napi::Env env, Napi::Object exports)\n{\n\texports.Set(Napi::String::New(env, \"dragMouse\"),\n\t\t\t\tNapi::Function::New(env, dragMouseWrapper));\n\n\texports.Set(Napi::String::New(env, \"updateScreenMetrics\"),\n\t\t\t\tNapi::Function::New(env, updateScreenMetricsWrapper));\n\n\texports.Set(Napi::String::New(env, \"moveMouse\"),\n\t\t\t\tNapi::Function::New(env, moveMouseWrapper));\n\n\texports.Set(Napi::String::New(env, \"moveMouseSmooth\"),\n\t\t\t\tNapi::Function::New(env, moveMouseSmoothWrapper));\n\n\texports.Set(Napi::String::New(env, \"getMousePos\"),\n\t\t\t\tNapi::Function::New(env, getMousePosWrapper));\n\n\texports.Set(Napi::String::New(env, \"mouseClick\"),\n\t\t\t\tNapi::Function::New(env, mouseClickWrapper));\n\n\texports.Set(Napi::String::New(env, \"mouseToggle\"),\n\t\t\t\tNapi::Function::New(env, mouseToggleWrapper));\n\n\texports.Set(Napi::String::New(env, \"scrollMouse\"),\n\t\t\t\tNapi::Function::New(env, scrollMouseWrapper));\n\n\texports.Set(Napi::String::New(env, \"setMouseDelay\"),\n\t\t\t\tNapi::Function::New(env, setMouseDelayWrapper));\n\n\texports.Set(Napi::String::New(env, \"keyTap\"),\n\t\t\t\tNapi::Function::New(env, keyTapWrapper));\n\n\texports.Set(Napi::String::New(env, \"keyToggle\"),\n\t\t\t\tNapi::Function::New(env, keyToggleWrapper));\n\n\texports.Set(Napi::String::New(env, \"unicodeTap\"),\n\t\t\t\tNapi::Function::New(env, unicodeTapWrapper));\n\n\texports.Set(Napi::String::New(env, \"typeString\"),\n\t\t\t\tNapi::Function::New(env, typeStringWrapper));\n\n\texports.Set(Napi::String::New(env, \"typeStringDelayed\"),\n\t\t\t\tNapi::Function::New(env, typeStringDelayedWrapper));\n\n\texports.Set(Napi::String::New(env, \"setKeyboardDelay\"),\n\t\t\t\tNapi::Function::New(env, setKeyboardDelayWrapper));\n\n\texports.Set(Napi::String::New(env, \"getPixelColor\"),\n\t\t\t\tNapi::Function::New(env, getPixelColorWrapper));\n\n\texports.Set(Napi::String::New(env, \"getScreenSize\"),\n\t\t\t\tNapi::Function::New(env, getScreenSizeWrapper));\n\n\texports.Set(Napi::String::New(env, \"captureScreen\"),\n\t\t\t\tNapi::Function::New(env, captureScreenWrapper));\n\n\texports.Set(Napi::String::New(env, \"getColor\"),\n\t\t\t\tNapi::Function::New(env, getColorWrapper));\n\n\texports.Set(Napi::String::New(env, \"getXDisplayName\"),\n\t\t\t\tNapi::Function::New(env, getXDisplayNameWrapper));\n\n\texports.Set(Napi::String::New(env, \"setXDisplayName\"),\n\t\t\t\tNapi::Function::New(env, setXDisplayNameWrapper));\n\n\treturn exports;\n}\n\nNODE_API_MODULE(robotjs, InitAll)\n"
  },
  {
    "path": "src/screen.c",
    "content": "#include \"screen.h\"\n#include \"os.h\"\n\n#if defined(IS_MACOSX)\n\t#include <ApplicationServices/ApplicationServices.h>\n#elif defined(USE_X11)\n\t#include <X11/Xlib.h>\n\t#include \"xdisplay.h\"\n#endif\n\nMMSize getMainDisplaySize(void)\n{\n#if defined(IS_MACOSX)\n\tCGDirectDisplayID displayID = CGMainDisplayID();\n\treturn MMSizeMake(CGDisplayPixelsWide(displayID),\n\t                  CGDisplayPixelsHigh(displayID));\n#elif defined(USE_X11)\n\tDisplay *display = XGetMainDisplay();\n\tconst int screen = DefaultScreen(display);\n\n\treturn MMSizeMake((size_t)DisplayWidth(display, screen),\n\t                  (size_t)DisplayHeight(display, screen));\n#elif defined(IS_WINDOWS)\n\treturn MMSizeMake((size_t)GetSystemMetrics(SM_CXSCREEN),\n\t                  (size_t)GetSystemMetrics(SM_CYSCREEN));\n#endif\n}\n\nbool pointVisibleOnMainDisplay(MMPoint point)\n{\n\tMMSize displaySize = getMainDisplaySize();\n\treturn point.x < displaySize.width && point.y < displaySize.height;\n}\n"
  },
  {
    "path": "src/screen.h",
    "content": "#pragma once\n#ifndef SCREEN_H\n#define SCREEN_H\n\n#include \"types.h\"\n\n#if defined(_MSC_VER)\n\t#include \"ms_stdbool.h\"\n#else\n\t#include <stdbool.h>\n#endif\n\n#ifdef __cplusplus\nextern \"C\" \n{\n#endif\n\n/* Returns the size of the main display. */\nMMSize getMainDisplaySize(void);\n\n/* Convenience function that returns whether the given point is in the bounds\n * of the main screen. */\nbool pointVisibleOnMainDisplay(MMPoint point);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* SCREEN_H */\n"
  },
  {
    "path": "src/screengrab.c",
    "content": "#include \"screengrab.h\"\n#include \"bmp_io.h\"\n#include \"endian.h\"\n#include <stdlib.h> /* malloc() */\n\n#if defined(IS_MACOSX)\n\t#include <OpenGL/OpenGL.h>\n\t#include <OpenGL/gl.h>\n\t#include <ApplicationServices/ApplicationServices.h>\n#elif defined(USE_X11)\n\t#include <X11/Xlib.h>\n\t#include <X11/Xutil.h>\n\t#include \"xdisplay.h\"\n#elif defined(IS_WINDOWS)\n\t#include <string.h>\n#endif\n\nMMBitmapRef copyMMBitmapFromDisplayInRect(MMRect rect)\n{\n#if defined(IS_MACOSX)\n\n\tMMBitmapRef bitmap = NULL;\n\tuint8_t *buffer = NULL;\n\tsize_t bufferSize = 0;\n\n\tCGDirectDisplayID displayID = CGMainDisplayID();\n\n\tCGImageRef image = CGDisplayCreateImageForRect(displayID,\n\t\tCGRectMake(rect.origin.x,\n\t\t\trect.origin.y,\n\t\t\trect.size.width,\n\t\t\trect.size.height));\n\n\tif (!image) { return NULL; }\n\n\tCFDataRef imageData = CGDataProviderCopyData(CGImageGetDataProvider(image));\n\n\tif (!imageData) { return NULL; }\n\n\tbufferSize = CFDataGetLength(imageData);\n\tbuffer = malloc(bufferSize);\n\n\tCFDataGetBytes(imageData, CFRangeMake(0,bufferSize), buffer);\n\n\tbitmap = createMMBitmap(buffer,\n\t\tCGImageGetWidth(image),\n\t\tCGImageGetHeight(image),\n\t\tCGImageGetBytesPerRow(image),\n\t\tCGImageGetBitsPerPixel(image),\n\t\tCGImageGetBitsPerPixel(image) / 8);\n\n\tCFRelease(imageData);\n\n\tCGImageRelease(image);\n\n\treturn bitmap;\n\n#elif defined(USE_X11)\n\tMMBitmapRef bitmap;\n\n\tDisplay *display = XOpenDisplay(NULL);\n\tXImage *image = XGetImage(display,\n\t                          XDefaultRootWindow(display),\n\t                          (int)rect.origin.x,\n\t                          (int)rect.origin.y,\n\t                          (unsigned int)rect.size.width,\n\t                          (unsigned int)rect.size.height,\n\t                          AllPlanes, ZPixmap);\n\tXCloseDisplay(display);\n\tif (image == NULL) return NULL;\n\n\tbitmap = createMMBitmap((uint8_t *)image->data,\n\t                        rect.size.width,\n\t                        rect.size.height,\n\t                        (size_t)image->bytes_per_line,\n\t                        (uint8_t)image->bits_per_pixel,\n\t                        (uint8_t)image->bits_per_pixel / 8);\n\timage->data = NULL; /* Steal ownership of bitmap data so we don't have to\n\t                     * copy it. */\n\tXDestroyImage(image);\n\n\treturn bitmap;\n#elif defined(IS_WINDOWS)\n\tMMBitmapRef bitmap;\n\tvoid *data;\n\tHDC screen = NULL, screenMem = NULL;\n\tHBITMAP dib;\n\tBITMAPINFO bi;\n\n\t/* Initialize bitmap info. */\n\tbi.bmiHeader.biSize = sizeof(bi.bmiHeader);\n   \tbi.bmiHeader.biWidth = (long)rect.size.width;\n   \tbi.bmiHeader.biHeight = -(long)rect.size.height; /* Non-cartesian, please */\n   \tbi.bmiHeader.biPlanes = 1;\n   \tbi.bmiHeader.biBitCount = 32;\n   \tbi.bmiHeader.biCompression = BI_RGB;\n   \tbi.bmiHeader.biSizeImage = (DWORD)(4 * rect.size.width * rect.size.height);\n\tbi.bmiHeader.biXPelsPerMeter = 0;\n\tbi.bmiHeader.biYPelsPerMeter = 0;\n\tbi.bmiHeader.biClrUsed = 0;\n\tbi.bmiHeader.biClrImportant = 0;\n\n\tscreen = GetDC(NULL); /* Get entire screen */\n\tif (screen == NULL) return NULL;\n\n\t/* Get screen data in display device context. */\n   \tdib = CreateDIBSection(screen, &bi, DIB_RGB_COLORS, &data, NULL, 0);\n\n\t/* Copy the data into a bitmap struct. */\n\tif ((screenMem = CreateCompatibleDC(screen)) == NULL ||\n\t    SelectObject(screenMem, dib) == NULL ||\n\t    !BitBlt(screenMem,\n\t            (int)0,\n\t            (int)0,\n\t            (int)rect.size.width,\n\t            (int)rect.size.height,\n\t\t\t\tscreen,\n\t\t\t\trect.origin.x,\n\t\t\t\trect.origin.y,\n\t\t\t\tSRCCOPY)) {\n\t\t\n\t\t/* Error copying data. */\n\t\tReleaseDC(NULL, screen);\n\t\tDeleteObject(dib);\n\t\tif (screenMem != NULL) DeleteDC(screenMem);\n\n\t\treturn NULL;\n\t}\n\n\tbitmap = createMMBitmap(NULL,\n\t                        rect.size.width,\n\t                        rect.size.height,\n\t                        4 * rect.size.width,\n\t                        (uint8_t)bi.bmiHeader.biBitCount,\n\t                        4);\n\n\t/* Copy the data to our pixel buffer. */\n\tif (bitmap != NULL) {\n\t\tbitmap->imageBuffer = malloc(bitmap->bytewidth * bitmap->height);\n\t\tmemcpy(bitmap->imageBuffer, data, bitmap->bytewidth * bitmap->height);\n\t}\n\n\tReleaseDC(NULL, screen);\n\tDeleteObject(dib);\n\tDeleteDC(screenMem);\n\n\treturn bitmap;\n#endif\n}\n"
  },
  {
    "path": "src/screengrab.h",
    "content": "#pragma once\n#ifndef SCREENGRAB_H\n#define SCREENGRAB_H\n\n#include \"types.h\"\n#include \"MMBitmap.h\"\n\n#ifdef __cplusplus\nextern \"C\" \n{\n#endif\n\n/* Returns a raw bitmap of screengrab of the display (to be destroyed()'d by\n * caller), or NULL on error. */\nMMBitmapRef copyMMBitmapFromDisplayInRect(MMRect rect);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* SCREENGRAB_H */\n"
  },
  {
    "path": "src/snprintf.c",
    "content": "/*\n * snprintf.c - a portable implementation of snprintf\n *\n * AUTHOR\n *   Mark Martinec <mark.martinec@ijs.si>, April 1999.\n *\n *   Copyright 1999, Mark Martinec. All rights reserved.\n *\n * TERMS AND CONDITIONS\n *   This program is free software; you can redistribute it and/or modify\n *   it under the terms of the \"Frontier Artistic License\" which comes\n *   with this Kit.\n *\n *   This program is distributed in the hope that it will be useful,\n *   but WITHOUT ANY WARRANTY; without even the implied warranty\n *   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n *   See the Frontier Artistic License for more details.\n *\n *   You should have received a copy of the Frontier Artistic License\n *   with this Kit in the file named LICENSE.txt .\n *   If not, I'll be glad to provide one.\n *\n * FEATURES\n * - careful adherence to specs regarding flags, field width and precision;\n * - good performance for large string handling (large format, large\n *   argument or large paddings). Performance is similar to system's sprintf\n *   and in several cases significantly better (make sure you compile with\n *   optimizations turned on, tell the compiler the code is strict ANSI\n *   if necessary to give it more freedom for optimizations);\n * - return value semantics per ISO/IEC 9899:1999 (\"ISO C99\");\n * - written in standard ISO/ANSI C - requires an ANSI C compiler.\n *\n * SUPPORTED CONVERSION SPECIFIERS AND DATA TYPES\n *\n * This snprintf only supports the following conversion specifiers:\n * s, c, d, u, o, x, X, p  (and synonyms: i, D, U, O - see below)\n * with flags: '-', '+', ' ', '0' and '#'.\n * An asterisk is supported for field width as well as precision.\n *\n * Length modifiers 'h' (short int), 'l' (long int),\n * and 'll' (long long int) are supported.\n * NOTE:\n *   If macro SNPRINTF_LONGLONG_SUPPORT is not defined (default) the\n *   length modifier 'll' is recognized but treated the same as 'l',\n *   which may cause argument value truncation! Defining\n *   SNPRINTF_LONGLONG_SUPPORT requires that your system's sprintf also\n *   handles length modifier 'll'.  long long int is a language extension\n *   which may not be portable.\n *\n * Conversion of numeric data (conversion specifiers d, u, o, x, X, p)\n * with length modifiers (none or h, l, ll) is left to the system routine\n * sprintf, but all handling of flags, field width and precision as well as\n * c and s conversions is done very carefully by this portable routine.\n * If a string precision (truncation) is specified (e.g. %.8s) it is\n * guaranteed the string beyond the specified precision will not be referenced.\n *\n * Length modifiers h, l and ll are ignored for c and s conversions (data\n * types wint_t and wchar_t are not supported).\n *\n * The following common synonyms for conversion characters are supported:\n *   - i is a synonym for d\n *   - D is a synonym for ld, explicit length modifiers are ignored\n *   - U is a synonym for lu, explicit length modifiers are ignored\n *   - O is a synonym for lo, explicit length modifiers are ignored\n * The D, O and U conversion characters are nonstandard, they are supported\n * for backward compatibility only, and should not be used for new code.\n *\n * The following is specifically NOT supported:\n *   - flag ' (thousands' grouping character) is recognized but ignored\n *   - numeric conversion specifiers: f, e, E, g, G and synonym F,\n *     as well as the new a and A conversion specifiers\n *   - length modifier 'L' (long double) and 'q' (quad - use 'll' instead)\n *   - wide character/string conversions: lc, ls, and nonstandard\n *     synonyms C and S\n *   - writeback of converted string length: conversion character n\n *   - the n$ specification for direct reference to n-th argument\n *   - locales\n *\n * It is permitted for str_m to be zero, and it is permitted to specify NULL\n * pointer for resulting string argument if str_m is zero (as per ISO C99).\n *\n * The return value is the number of characters which would be generated\n * for the given input, excluding the trailing null. If this value\n * is greater or equal to str_m, not all characters from the result\n * have been stored in str, output bytes beyond the (str_m-1) -th character\n * are discarded. If str_m is greater than zero it is guaranteed\n * the resulting string will be null-terminated.\n *\n * NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1,\n * but is different from some older and vendor implementations,\n * and is also different from XPG, XSH5, SUSv2 specifications.\n * For historical discussion on changes in the semantics and standards\n * of snprintf see printf(3) man page in the Linux programmers manual.\n *\n * Routines asprintf and vasprintf return a pointer (in the ptr argument)\n * to a buffer sufficiently large to hold the resulting string. This pointer\n * should be passed to free(3) to release the allocated storage when it is\n * no longer needed. If sufficient space cannot be allocated, these functions\n * will return -1 and set ptr to be a NULL pointer. These two routines are a\n * GNU C library extensions (glibc).\n *\n * Routines asnprintf and vasnprintf are similar to asprintf and vasprintf,\n * yet, like snprintf and vsnprintf counterparts, will write at most str_m-1\n * characters into the allocated output string, the last character in the\n * allocated buffer then gets the terminating null. If the formatted string\n * length (the return value) is greater than or equal to the str_m argument,\n * the resulting string was truncated and some of the formatted characters\n * were discarded. These routines present a handy way to limit the amount\n * of allocated memory to some sane value.\n *\n * AVAILABILITY\n *   http://www.ijs.si/software/snprintf/\n *\n * REVISION HISTORY\n * 1999-04\tV0.9  Mark Martinec\n *\t\t- initial version, some modifications after comparing printf\n *\t\t  man pages for Digital Unix 4.0, Solaris 2.6 and HPUX 10,\n *\t\t  and checking how Perl handles sprintf (differently!);\n * 1999-04-09\tV1.0  Mark Martinec <mark.martinec@ijs.si>\n *\t\t- added main test program, fixed remaining inconsistencies,\n *\t\t  added optional (long long int) support;\n * 1999-04-12\tV1.1  Mark Martinec <mark.martinec@ijs.si>\n *\t\t- support the 'p' conversion (pointer to void);\n *\t\t- if a string precision is specified\n *\t\t  make sure the string beyond the specified precision\n *\t\t  will not be referenced (e.g. by strlen);\n * 1999-04-13\tV1.2  Mark Martinec <mark.martinec@ijs.si>\n *\t\t- support synonyms %D=%ld, %U=%lu, %O=%lo;\n *\t\t- speed up the case of long format string with few conversions;\n * 1999-06-30\tV1.3  Mark Martinec <mark.martinec@ijs.si>\n *\t\t- fixed runaway loop (eventually crashing when str_l wraps\n *\t\t  beyond 2^31) while copying format string without\n *\t\t  conversion specifiers to a buffer that is too short\n *\t\t  (thanks to Edwin Young <edwiny@autonomy.com> for\n *\t\t  spotting the problem);\n *\t\t- added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR)\n *\t\t  to snprintf.h\n * 2000-02-14\tV2.0 (never released) Mark Martinec <mark.martinec@ijs.si>\n *\t\t- relaxed license terms: The Artistic License now applies.\n *\t\t  You may still apply the GNU GENERAL PUBLIC LICENSE\n *\t\t  as was distributed with previous versions, if you prefer;\n *\t\t- changed REVISION HISTORY dates to use ISO 8601 date format;\n *\t\t- added vsnprintf (patch also independently proposed by\n *\t\t  Caolan McNamara 2000-05-04, and Keith M Willenson 2000-06-01)\n * 2000-06-27\tV2.1  Mark Martinec <mark.martinec@ijs.si>\n *\t\t- removed POSIX check for str_m<1; value 0 for str_m is\n *\t\t  allowed by ISO C99 (and GNU C library 2.1) - (pointed out\n *\t\t  on 2000-05-04 by Caolan McNamara, caolan@ csn dot ul dot ie).\n *\t\t  Besides relaxed license this change in standards adherence\n *\t\t  is the main reason to bump up the major version number;\n *\t\t- added nonstandard routines asnprintf, vasnprintf, asprintf,\n *\t\t  vasprintf that dynamically allocate storage for the\n *\t\t  resulting string; these routines are not compiled by default,\n *\t\t  see comments where NEED_V?ASN?PRINTF macros are defined;\n *\t\t- autoconf contributed by Caolan McNamara\n * 2000-10-06\tV2.2  Mark Martinec <mark.martinec@ijs.si>\n *\t\t- BUG FIX: the %c conversion used a temporary variable\n *\t\t  that was no longer in scope when referenced,\n *\t\t  possibly causing incorrect resulting character;\n *\t\t- BUG FIX: make precision and minimal field width unsigned\n *\t\t  to handle huge values (2^31 <= n < 2^32) correctly;\n *\t\t  also be more careful in the use of signed/unsigned/size_t\n *\t\t  internal variables - probably more careful than many\n *\t\t  vendor implementations, but there may still be a case\n *\t\t  where huge values of str_m, precision or minimal field\n *\t\t  could cause incorrect behaviour;\n *\t\t- use separate variables for signed/unsigned arguments,\n *\t\t  and for short/int, long, and long long argument lengths\n *\t\t  to avoid possible incompatibilities on certain\n *\t\t  computer architectures. Also use separate variable\n *\t\t  arg_sign to hold sign of a numeric argument,\n *\t\t  to make code more transparent;\n *\t\t- some fiddling with zero padding and \"0x\" to make it\n *\t\t  Linux compatible;\n *\t\t- systematically use macros fast_memcpy and fast_memset\n *\t\t  instead of case-by-case hand optimization; determine some\n *\t\t  breakeven string lengths for different architectures;\n *\t\t- terminology change: 'format' -> 'conversion specifier',\n *\t\t  'C9x' -> 'ISO/IEC 9899:1999 (\"ISO C99\")',\n *\t\t  'alternative form' -> 'alternate form',\n *\t\t  'data type modifier' -> 'length modifier';\n *\t\t- several comments rephrased and new ones added;\n *\t\t- make compiler not complain about 'credits' defined but\n *\t\t  not used;\n */\n\n\n/* Define HAVE_SNPRINTF if your system already has snprintf and vsnprintf.\n *\n * If HAVE_SNPRINTF is defined this module will not produce code for\n * snprintf and vsnprintf, unless PREFER_PORTABLE_SNPRINTF is defined as well,\n * causing this portable version of snprintf to be called portable_snprintf\n * (and portable_vsnprintf).\n */\n/* #define HAVE_SNPRINTF */\n\n/* Define PREFER_PORTABLE_SNPRINTF if your system does have snprintf and\n * vsnprintf but you would prefer to use the portable routine(s) instead.\n * In this case the portable routine is declared as portable_snprintf\n * (and portable_vsnprintf) and a macro 'snprintf' (and 'vsnprintf')\n * is defined to expand to 'portable_v?snprintf' - see file snprintf.h .\n * Defining this macro is only useful if HAVE_SNPRINTF is also defined,\n * but does does no harm if defined nevertheless.\n */\n/* #define PREFER_PORTABLE_SNPRINTF */\n\n/* Define SNPRINTF_LONGLONG_SUPPORT if you want to support\n * data type (long long int) and length modifier 'll' (e.g. %lld).\n * If undefined, 'll' is recognized but treated as a single 'l'.\n *\n * If the system's sprintf does not handle 'll'\n * the SNPRINTF_LONGLONG_SUPPORT must not be defined!\n *\n * This is off by default as (long long int) is a language extension.\n */\n/* #define SNPRINTF_LONGLONG_SUPPORT */\n\n/* Define NEED_SNPRINTF_ONLY if you only need snprintf, and not vsnprintf.\n * If NEED_SNPRINTF_ONLY is defined, the snprintf will be defined directly,\n * otherwise both snprintf and vsnprintf routines will be defined\n * and snprintf will be a simple wrapper around vsnprintf, at the expense\n * of an extra procedure call.\n */\n/* #define NEED_SNPRINTF_ONLY */\n\n/* Define NEED_V?ASN?PRINTF macros if you need library extension\n * routines asprintf, vasprintf, asnprintf, vasnprintf respectively,\n * and your system library does not provide them. They are all small\n * wrapper routines around portable_vsnprintf. Defining any of the four\n * NEED_V?ASN?PRINTF macros automatically turns off NEED_SNPRINTF_ONLY\n * and turns on PREFER_PORTABLE_SNPRINTF.\n *\n * Watch for name conflicts with the system library if these routines\n * are already present there.\n *\n * NOTE: vasprintf and vasnprintf routines need va_copy() from stdarg.h, as\n * specified by C99, to be able to traverse the same list of arguments twice.\n * I don't know of any other standard and portable way of achieving the same.\n * With some versions of gcc you may use __va_copy(). You might even get away\n * with \"ap2 = ap\", in this case you must not call va_end(ap2) !\n *   #define va_copy(ap2,ap) ap2 = ap\n */\n/* #define NEED_ASPRINTF   */\n/* #define NEED_ASNPRINTF  */\n/* #define NEED_VASPRINTF  */\n/* #define NEED_VASNPRINTF */\n\n\n/* Define the following macros if desired:\n *   SOLARIS_COMPATIBLE, SOLARIS_BUG_COMPATIBLE,\n *   HPUX_COMPATIBLE, HPUX_BUG_COMPATIBLE, LINUX_COMPATIBLE,\n *   DIGITAL_UNIX_COMPATIBLE, DIGITAL_UNIX_BUG_COMPATIBLE,\n *   PERL_COMPATIBLE, PERL_BUG_COMPATIBLE,\n *\n * - For portable applications it is best not to rely on peculiarities\n *   of a given implementation so it may be best not to define any\n *   of the macros that select compatibility and to avoid features\n *   that vary among the systems.\n *\n * - Selecting compatibility with more than one operating system\n *   is not strictly forbidden but is not recommended.\n *\n * - 'x'_BUG_COMPATIBLE implies 'x'_COMPATIBLE .\n *\n * - 'x'_COMPATIBLE refers to (and enables) a behaviour that is\n *   documented in a sprintf man page on a given operating system\n *   and actually adhered to by the system's sprintf (but not on\n *   most other operating systems). It may also refer to and enable\n *   a behaviour that is declared 'undefined' or 'implementation specific'\n *   in the man page but a given implementation behaves predictably\n *   in a certain way.\n *\n * - 'x'_BUG_COMPATIBLE refers to (and enables) a behaviour of system's sprintf\n *   that contradicts the sprintf man page on the same operating system.\n *\n * - I do not claim that the 'x'_COMPATIBLE and 'x'_BUG_COMPATIBLE\n *   conditionals take into account all idiosyncrasies of a particular\n *   implementation, there may be other incompatibilities.\n */\n\n\n\f\n/* ============================================= */\n/* NO USER SERVICABLE PARTS FOLLOWING THIS POINT */\n/* ============================================= */\n\n#define PORTABLE_SNPRINTF_VERSION_MAJOR 2\n#define PORTABLE_SNPRINTF_VERSION_MINOR 2\n\n#if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF)\n# if defined(NEED_SNPRINTF_ONLY)\n# undef NEED_SNPRINTF_ONLY\n# endif\n# if !defined(PREFER_PORTABLE_SNPRINTF)\n# define PREFER_PORTABLE_SNPRINTF\n# endif\n#endif\n\n#if defined(SOLARIS_BUG_COMPATIBLE) && !defined(SOLARIS_COMPATIBLE)\n#define SOLARIS_COMPATIBLE\n#endif\n\n#if defined(HPUX_BUG_COMPATIBLE) && !defined(HPUX_COMPATIBLE)\n#define HPUX_COMPATIBLE\n#endif\n\n#if defined(DIGITAL_UNIX_BUG_COMPATIBLE) && !defined(DIGITAL_UNIX_COMPATIBLE)\n#define DIGITAL_UNIX_COMPATIBLE\n#endif\n\n#if defined(PERL_BUG_COMPATIBLE) && !defined(PERL_COMPATIBLE)\n#define PERL_COMPATIBLE\n#endif\n\n#if defined(LINUX_BUG_COMPATIBLE) && !defined(LINUX_COMPATIBLE)\n#define LINUX_COMPATIBLE\n#endif\n\n#include \"snprintf.h\"\n#include <sys/types.h>\n#include <string.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <stdarg.h>\n#include <assert.h>\n#include <errno.h>\n\n#ifdef isdigit\n#undef isdigit\n#endif\n#define isdigit(c) ((c) >= '0' && (c) <= '9')\n\n/* For copying strings longer or equal to 'breakeven_point'\n * it is more efficient to call memcpy() than to do it inline.\n * The value depends mostly on the processor architecture,\n * but also on the compiler and its optimization capabilities.\n * The value is not critical, some small value greater than zero\n * will be just fine if you don't care to squeeze every drop\n * of performance out of the code.\n *\n * Small values favor memcpy, large values favor inline code.\n */\n#if defined(__alpha__) || defined(__alpha)\n#  define breakeven_point   2\t/* AXP (DEC Alpha)     - gcc or cc or egcs */\n#endif\n#if defined(__i386__)  || defined(__i386)\n#  define breakeven_point  12\t/* Intel Pentium/Linux - gcc 2.96 */\n#endif\n#if defined(__hppa)\n#  define breakeven_point  10\t/* HP-PA               - gcc */\n#endif\n#if defined(__sparc__) || defined(__sparc)\n#  define breakeven_point  33\t/* Sun Sparc 5         - gcc 2.8.1 */\n#endif\n\n/* some other values of possible interest: */\n/* #define breakeven_point  8 */  /* VAX 4000          - vaxc */\n/* #define breakeven_point 19 */  /* VAX 4000          - gcc 2.7.0 */\n\n#ifndef breakeven_point\n#  define breakeven_point   6\t/* some reasonable one-size-fits-all value */\n#endif\n\n#define fast_memcpy(d,s,n) \\\n  { register size_t nn = (size_t)(n); \\\n    if (nn >= breakeven_point) memcpy((d), (s), nn); \\\n    else if (nn > 0) { /* proc call overhead is worth only for large strings*/\\\n      register char *dd; register const char *ss; \\\n      for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } }\n\n#define fast_memset(d,c,n) \\\n  { register size_t nn = (size_t)(n); \\\n    if (nn >= breakeven_point) memset((d), (int)(c), nn); \\\n    else if (nn > 0) { /* proc call overhead is worth only for large strings*/\\\n      register char *dd; register const int cc=(int)(c); \\\n      for (dd=(d); nn>0; nn--) *dd++ = cc; } }\n\n/* prototypes */\n\n#if defined(NEED_ASPRINTF)\nint asprintf   (char **ptr, const char *fmt, /*args*/ ...);\n#endif\n#if defined(NEED_VASPRINTF)\nint vasprintf  (char **ptr, const char *fmt, va_list ap);\n#endif\n#if defined(NEED_ASNPRINTF)\nint asnprintf  (char **ptr, size_t str_m, const char *fmt, /*args*/ ...);\n#endif\n#if defined(NEED_VASNPRINTF)\nint vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap);\n#endif\n\n#if defined(HAVE_SNPRINTF)\n/* declare our portable snprintf  routine under name portable_snprintf  */\n/* declare our portable vsnprintf routine under name portable_vsnprintf */\n#else\n/* declare our portable routines under names snprintf and vsnprintf */\n#define portable_snprintf snprintf\n#if !defined(NEED_SNPRINTF_ONLY)\n#define portable_vsnprintf vsnprintf\n#endif\n#endif\n\n#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)\nint portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...);\n#if !defined(NEED_SNPRINTF_ONLY)\nint portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap);\n#endif\n#endif\n\n/* declarations */\n\n#if defined(NEED_ASPRINTF)\nint asprintf(char **ptr, const char *fmt, /*args*/ ...) {\n  va_list ap;\n  size_t str_m;\n  int str_l;\n\n  *ptr = NULL;\n  va_start(ap, fmt);                            /* measure the required size */\n  str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap);\n  va_end(ap);\n  assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */\n  *ptr = (char *) malloc(str_m = (size_t)str_l + 1);\n  if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }\n  else {\n    int str_l2;\n    va_start(ap, fmt);\n    str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);\n    va_end(ap);\n    assert(str_l2 == str_l);\n  }\n  return str_l;\n}\n#endif\n\n#if defined(NEED_VASPRINTF)\nint vasprintf(char **ptr, const char *fmt, va_list ap) {\n  size_t str_m;\n  int str_l;\n\n  *ptr = NULL;\n  { va_list ap2;\n    va_copy(ap2, ap);  /* don't consume the original ap, we'll need it again */\n    str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/\n    va_end(ap2);\n  }\n  assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */\n  *ptr = (char *) malloc(str_m = (size_t)str_l + 1);\n  if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }\n  else {\n    int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);\n    assert(str_l2 == str_l);\n  }\n  return str_l;\n}\n#endif\n\n#if defined(NEED_ASNPRINTF)\nint asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) {\n  va_list ap;\n  int str_l;\n\n  *ptr = NULL;\n  va_start(ap, fmt);                            /* measure the required size */\n  str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap);\n  va_end(ap);\n  assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */\n  if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1;      /* truncate */\n  /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */\n  if (str_m == 0) {  /* not interested in resulting string, just return size */\n  } else {\n    *ptr = (char *) malloc(str_m);\n    if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }\n    else {\n      int str_l2;\n      va_start(ap, fmt);\n      str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);\n      va_end(ap);\n      assert(str_l2 == str_l);\n    }\n  }\n  return str_l;\n}\n#endif\n\n#if defined(NEED_VASNPRINTF)\nint vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) {\n  int str_l;\n\n  *ptr = NULL;\n  { va_list ap2;\n    va_copy(ap2, ap);  /* don't consume the original ap, we'll need it again */\n    str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/\n    va_end(ap2);\n  }\n  assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */\n  if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1;      /* truncate */\n  /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */\n  if (str_m == 0) {  /* not interested in resulting string, just return size */\n  } else {\n    *ptr = (char *) malloc(str_m);\n    if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }\n    else {\n      int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);\n      assert(str_l2 == str_l);\n    }\n  }\n  return str_l;\n}\n#endif\n\n/*\n * If the system does have snprintf and the portable routine is not\n * specifically required, this module produces no code for snprintf/vsnprintf.\n */\n#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)\n\n#if !defined(NEED_SNPRINTF_ONLY)\nint portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {\n  va_list ap;\n  int str_l;\n\n  va_start(ap, fmt);\n  str_l = portable_vsnprintf(str, str_m, fmt, ap);\n  va_end(ap);\n  return str_l;\n}\n#endif\n\n#if defined(NEED_SNPRINTF_ONLY)\nint portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {\n#else\nint portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) {\n#endif\n\n#if defined(NEED_SNPRINTF_ONLY)\n  va_list ap;\n#endif\n  size_t str_l = 0;\n  const char *p = fmt;\n\n/* In contrast with POSIX, the ISO C99 now says\n * that str can be NULL and str_m can be 0.\n * This is more useful than the old:  if (str_m < 1) return -1; */\n\n#if defined(NEED_SNPRINTF_ONLY)\n  va_start(ap, fmt);\n#endif\n  if (!p) p = \"\";\n  while (*p) {\n    if (*p != '%') {\n   /* if (str_l < str_m) str[str_l++] = *p++;    -- this would be sufficient */\n   /* but the following code achieves better performance for cases\n    * where format string is long and contains few conversions */\n      const char *q = strchr(p+1,'%');\n      size_t n = !q ? strlen(p) : (q-p);\n      if (str_l < str_m) {\n        size_t avail = str_m-str_l;\n        fast_memcpy(str+str_l, p, (n>avail?avail:n));\n      }\n      p += n; str_l += n;\n    } else {\n      const char *starting_p;\n      size_t min_field_width = 0, precision = 0;\n      int zero_padding = 0, precision_specified = 0, justify_left = 0;\n      int alternate_form = 0, force_sign = 0;\n      int space_for_positive = 1; /* If both the ' ' and '+' flags appear,\n                                     the ' ' flag should be ignored. */\n      char length_modifier = '\\0';            /* allowed values: \\0, h, l, L */\n      char tmp[32];/* temporary buffer for simple numeric->string conversion */\n\n      const char *str_arg;      /* string address in case of string argument */\n      size_t str_arg_l;         /* natural field width of arg without padding\n                                   and sign */\n      unsigned char uchar_arg;\n        /* unsigned char argument value - only defined for c conversion.\n           N.B. standard explicitly states the char argument for\n           the c conversion is unsigned */\n\n      size_t number_of_zeros_to_pad = 0;\n        /* number of zeros to be inserted for numeric conversions\n           as required by the precision or minimal field width */\n\n      size_t zero_padding_insertion_ind = 0;\n        /* index into tmp where zero padding is to be inserted */\n\n      char fmt_spec = '\\0';\n        /* current conversion specifier character */\n\n      starting_p = p; p++;  /* skip '%' */\n   /* parse flags */\n      while (*p == '0' || *p == '-' || *p == '+' ||\n             *p == ' ' || *p == '#' || *p == '\\'') {\n        switch (*p) {\n        case '0': zero_padding = 1; break;\n        case '-': justify_left = 1; break;\n        case '+': force_sign = 1; space_for_positive = 0; break;\n        case ' ': force_sign = 1;\n     /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored */\n#ifdef PERL_COMPATIBLE\n     /* ... but in Perl the last of ' ' and '+' applies */\n                  space_for_positive = 1;\n#endif\n                  break;\n        case '#': alternate_form = 1; break;\n        case '\\'': break;\n        }\n        p++;\n      }\n   /* If the '0' and '-' flags both appear, the '0' flag should be ignored. */\n\n   /* parse field width */\n      if (*p == '*') {\n        int j;\n        p++; j = va_arg(ap, int);\n        if (j >= 0) min_field_width = j;\n        else { min_field_width = -j; justify_left = 1; }\n      } else if (isdigit((int)(*p))) {\n        /* size_t could be wider than unsigned int;\n           make sure we treat argument like common implementations do */\n        unsigned int uj = *p++ - '0';\n        while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0');\n        min_field_width = uj;\n      }\n   /* parse precision */\n      if (*p == '.') {\n        p++; precision_specified = 1;\n        if (*p == '*') {\n          int j = va_arg(ap, int);\n          p++;\n          if (j >= 0) precision = j;\n          else {\n            precision_specified = 0; precision = 0;\n         /* NOTE:\n          *   Solaris 2.6 man page claims that in this case the precision\n          *   should be set to 0.  Digital Unix 4.0, HPUX 10 and BSD man page\n          *   claim that this case should be treated as unspecified precision,\n          *   which is what we do here.\n          */\n          }\n        } else if (isdigit((int)(*p))) {\n          /* size_t could be wider than unsigned int;\n             make sure we treat argument like common implementations do */\n          unsigned int uj = *p++ - '0';\n          while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0');\n          precision = uj;\n        }\n      }\n   /* parse 'h', 'l' and 'll' length modifiers */\n      if (*p == 'h' || *p == 'l') {\n        length_modifier = *p; p++;\n        if (length_modifier == 'l' && *p == 'l') {   /* double l = long long */\n#ifdef SNPRINTF_LONGLONG_SUPPORT\n          length_modifier = '2';                  /* double l encoded as '2' */\n#else\n          length_modifier = 'l';                 /* treat it as a single 'l' */\n#endif\n          p++;\n        }\n      }\n      fmt_spec = *p;\n   /* common synonyms: */\n      switch (fmt_spec) {\n      case 'i': fmt_spec = 'd'; break;\n      case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;\n      case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;\n      case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;\n      default: break;\n      }\n   /* get parameter value, do initial processing */\n      switch (fmt_spec) {\n      case '%': /* % behaves similar to 's' regarding flags and field widths */\n      case 'c': /* c behaves similar to 's' regarding flags and field widths */\n      case 's':\n        length_modifier = '\\0';          /* wint_t and wchar_t not supported */\n     /* the result of zero padding flag with non-numeric conversion specifier*/\n     /* is undefined. Solaris and HPUX 10 does zero padding in this case,    */\n     /* Digital Unix and Linux does not. */\n#if !defined(SOLARIS_COMPATIBLE) && !defined(HPUX_COMPATIBLE)\n        zero_padding = 0;    /* turn zero padding off for string conversions */\n#endif\n        str_arg_l = 1;\n        switch (fmt_spec) {\n        case '%':\n          str_arg = p; break;\n        case 'c': {\n          int j = va_arg(ap, int);\n          uchar_arg = (unsigned char) j;   /* standard demands unsigned char */\n          str_arg = (const char *) &uchar_arg;\n          break;\n        }\n        case 's':\n          str_arg = va_arg(ap, const char *);\n          if (!str_arg) str_arg_l = 0;\n       /* make sure not to address string beyond the specified precision !!! */\n          else if (!precision_specified) str_arg_l = strlen(str_arg);\n       /* truncate string if necessary as requested by precision */\n          else if (precision == 0) str_arg_l = 0;\n          else {\n       /* memchr on HP does not like n > 2^31  !!! */\n            const char *q = memchr(str_arg, '\\0',\n                             precision <= 0x7fffffff ? precision : 0x7fffffff);\n            str_arg_l = !q ? precision : (q-str_arg);\n          }\n          break;\n        default: break;\n        }\n        break;\n      case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': {\n        /* NOTE: the u, o, x, X and p conversion specifiers imply\n                 the value is unsigned;  d implies a signed value */\n\n        int arg_sign = 0;\n          /* 0 if numeric argument is zero (or if pointer is NULL for 'p'),\n            +1 if greater than zero (or nonzero for unsigned arguments),\n            -1 if negative (unsigned argument is never negative) */\n\n        int int_arg = 0;  unsigned int uint_arg = 0;\n          /* only defined for length modifier h, or for no length modifiers */\n\n        long int long_arg = 0;  unsigned long int ulong_arg = 0;\n          /* only defined for length modifier l */\n\n        void *ptr_arg = NULL;\n          /* pointer argument value -only defined for p conversion */\n\n#ifdef SNPRINTF_LONGLONG_SUPPORT\n        long long int long_long_arg = 0;\n        unsigned long long int ulong_long_arg = 0;\n          /* only defined for length modifier ll */\n#endif\n        if (fmt_spec == 'p') {\n        /* HPUX 10: An l, h, ll or L before any other conversion character\n         *   (other than d, i, u, o, x, or X) is ignored.\n         * Digital Unix:\n         *   not specified, but seems to behave as HPUX does.\n         * Solaris: If an h, l, or L appears before any other conversion\n         *   specifier (other than d, i, u, o, x, or X), the behavior\n         *   is undefined. (Actually %hp converts only 16-bits of address\n         *   and %llp treats address as 64-bit data which is incompatible\n         *   with (void *) argument on a 32-bit system).\n         */\n#ifdef SOLARIS_COMPATIBLE\n#  ifdef SOLARIS_BUG_COMPATIBLE\n          /* keep length modifiers even if it represents 'll' */\n#  else\n          if (length_modifier == '2') length_modifier = '\\0';\n#  endif\n#else\n          length_modifier = '\\0';\n#endif\n          ptr_arg = va_arg(ap, void *);\n          if (ptr_arg != NULL) arg_sign = 1;\n        } else if (fmt_spec == 'd') {  /* signed */\n          switch (length_modifier) {\n          case '\\0':\n          case 'h':\n         /* It is non-portable to specify a second argument of char or short\n          * to va_arg, because arguments seen by the called function\n          * are not char or short.  C converts char and short arguments\n          * to int before passing them to a function.\n          */\n            int_arg = va_arg(ap, int);\n            if      (int_arg > 0) arg_sign =  1;\n            else if (int_arg < 0) arg_sign = -1;\n            break;\n          case 'l':\n            long_arg = va_arg(ap, long int);\n            if      (long_arg > 0) arg_sign =  1;\n            else if (long_arg < 0) arg_sign = -1;\n            break;\n#ifdef SNPRINTF_LONGLONG_SUPPORT\n          case '2':\n            long_long_arg = va_arg(ap, long long int);\n            if      (long_long_arg > 0) arg_sign =  1;\n            else if (long_long_arg < 0) arg_sign = -1;\n            break;\n#endif\n          }\n        } else {  /* unsigned */\n          switch (length_modifier) {\n          case '\\0':\n          case 'h':\n            uint_arg = va_arg(ap, unsigned int);\n            if (uint_arg) arg_sign = 1;\n            break;\n          case 'l':\n            ulong_arg = va_arg(ap, unsigned long int);\n            if (ulong_arg) arg_sign = 1;\n            break;\n#ifdef SNPRINTF_LONGLONG_SUPPORT\n          case '2':\n            ulong_long_arg = va_arg(ap, unsigned long long int);\n            if (ulong_long_arg) arg_sign = 1;\n            break;\n#endif\n          }\n        }\n        str_arg = tmp; str_arg_l = 0;\n     /* NOTE:\n      *   For d, i, u, o, x, and X conversions, if precision is specified,\n      *   the '0' flag should be ignored. This is so with Solaris 2.6,\n      *   Digital UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl.\n      */\n#ifndef PERL_COMPATIBLE\n        if (precision_specified) zero_padding = 0;\n#endif\n        if (fmt_spec == 'd') {\n          if (force_sign && arg_sign >= 0)\n            tmp[str_arg_l++] = space_for_positive ? ' ' : '+';\n         /* leave negative numbers for sprintf to handle,\n            to avoid handling tricky cases like (short int)(-32768) */\n#ifdef LINUX_COMPATIBLE\n        } else if (fmt_spec == 'p' && force_sign && arg_sign > 0) {\n          tmp[str_arg_l++] = space_for_positive ? ' ' : '+';\n#endif\n        } else if (alternate_form) {\n          if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') )\n            { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; }\n         /* alternate form should have no effect for p conversion, but ... */\n#ifdef HPUX_COMPATIBLE\n          else if (fmt_spec == 'p'\n         /* HPUX 10: for an alternate form of p conversion,\n          *          a nonzero result is prefixed by 0x. */\n#ifndef HPUX_BUG_COMPATIBLE\n         /* Actually it uses 0x prefix even for a zero value. */\n                   && arg_sign != 0\n#endif\n                  ) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = 'x'; }\n#endif\n        }\n        zero_padding_insertion_ind = str_arg_l;\n        if (!precision_specified) precision = 1;   /* default precision is 1 */\n        if (precision == 0 && arg_sign == 0\n#if defined(HPUX_BUG_COMPATIBLE) || defined(LINUX_COMPATIBLE)\n            && fmt_spec != 'p'\n         /* HPUX 10 man page claims: With conversion character p the result of\n          * converting a zero value with a precision of zero is a null string.\n          * Actually HP returns all zeroes, and Linux returns \"(nil)\". */\n#endif\n        ) {\n         /* converted to null string */\n         /* When zero value is formatted with an explicit precision 0,\n            the resulting formatted string is empty (d, i, u, o, x, X, p).   */\n        } else {\n          char f[5]; int f_l = 0;\n          f[f_l++] = '%';    /* construct a simple format string for sprintf */\n          if (!length_modifier) { }\n          else if (length_modifier=='2') { f[f_l++] = 'l'; f[f_l++] = 'l'; }\n          else f[f_l++] = length_modifier;\n          f[f_l++] = fmt_spec; f[f_l++] = '\\0';\n          if (fmt_spec == 'p') str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg);\n          else if (fmt_spec == 'd') {  /* signed */\n            switch (length_modifier) {\n            case '\\0':\n            case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg);  break;\n            case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg); break;\n#ifdef SNPRINTF_LONGLONG_SUPPORT\n            case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,long_long_arg); break;\n#endif\n            }\n          } else {  /* unsigned */\n            switch (length_modifier) {\n            case '\\0':\n            case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, uint_arg);  break;\n            case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, ulong_arg); break;\n#ifdef SNPRINTF_LONGLONG_SUPPORT\n            case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,ulong_long_arg);break;\n#endif\n            }\n          }\n         /* include the optional minus sign and possible \"0x\"\n            in the region before the zero padding insertion point */\n          if (zero_padding_insertion_ind < str_arg_l &&\n              tmp[zero_padding_insertion_ind] == '-') {\n            zero_padding_insertion_ind++;\n          }\n          if (zero_padding_insertion_ind+1 < str_arg_l &&\n              tmp[zero_padding_insertion_ind]   == '0' &&\n             (tmp[zero_padding_insertion_ind+1] == 'x' ||\n              tmp[zero_padding_insertion_ind+1] == 'X') ) {\n            zero_padding_insertion_ind += 2;\n          }\n        }\n        { size_t num_of_digits = str_arg_l - zero_padding_insertion_ind;\n          if (alternate_form && fmt_spec == 'o'\n#ifdef HPUX_COMPATIBLE                                  /* (\"%#.o\",0) -> \"\"  */\n              && (str_arg_l > 0)\n#endif\n#ifdef DIGITAL_UNIX_BUG_COMPATIBLE                      /* (\"%#o\",0) -> \"00\" */\n#else\n              /* unless zero is already the first character */\n              && !(zero_padding_insertion_ind < str_arg_l\n                   && tmp[zero_padding_insertion_ind] == '0')\n#endif\n          ) {        /* assure leading zero for alternate-form octal numbers */\n            if (!precision_specified || precision < num_of_digits+1) {\n             /* precision is increased to force the first character to be zero,\n                except if a zero value is formatted with an explicit precision\n                of zero */\n              precision = num_of_digits+1; precision_specified = 1;\n            }\n          }\n       /* zero padding to specified precision? */\n          if (num_of_digits < precision) \n            number_of_zeros_to_pad = precision - num_of_digits;\n        }\n     /* zero padding to specified minimal field width? */\n        if (!justify_left && zero_padding) {\n          int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);\n          if (n > 0) number_of_zeros_to_pad += n;\n        }\n        break;\n      }\n      default: /* unrecognized conversion specifier, keep format string as-is*/\n        zero_padding = 0;  /* turn zero padding off for non-numeric convers. */\n#ifndef DIGITAL_UNIX_COMPATIBLE\n        justify_left = 1; min_field_width = 0;                /* reset flags */\n#endif\n#if defined(PERL_COMPATIBLE) || defined(LINUX_COMPATIBLE)\n     /* keep the entire format string unchanged */\n        str_arg = starting_p; str_arg_l = p - starting_p;\n     /* well, not exactly so for Linux, which does something inbetween,\n      * and I don't feel an urge to imitate it: \"%+++++hy\" -> \"%+y\"  */\n#else\n     /* discard the unrecognized conversion, just keep *\n      * the unrecognized conversion character          */\n        str_arg = p; str_arg_l = 0;\n#endif\n        if (*p) str_arg_l++;  /* include invalid conversion specifier unchanged\n                                 if not at end-of-string */\n        break;\n      }\n      if (*p) p++;      /* step over the just processed conversion specifier */\n   /* insert padding to the left as requested by min_field_width;\n      this does not include the zero padding in case of numerical conversions*/\n      if (!justify_left) {                /* left padding with blank or zero */\n        int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);\n        if (n > 0) {\n          if (str_l < str_m) {\n            size_t avail = str_m-str_l;\n            fast_memset(str+str_l, (zero_padding?'0':' '), (n>avail?avail:n));\n          }\n          str_l += n;\n        }\n      }\n   /* zero padding as requested by the precision or by the minimal field width\n    * for numeric conversions required? */\n      if (number_of_zeros_to_pad <= 0) {\n     /* will not copy first part of numeric right now, *\n      * force it to be copied later in its entirety    */\n        zero_padding_insertion_ind = 0;\n      } else {\n     /* insert first part of numerics (sign or '0x') before zero padding */\n        int n = zero_padding_insertion_ind;\n        if (n > 0) {\n          if (str_l < str_m) {\n            size_t avail = str_m-str_l;\n            fast_memcpy(str+str_l, str_arg, (n>avail?avail:n));\n          }\n          str_l += n;\n        }\n     /* insert zero padding as requested by the precision or min field width */\n        n = number_of_zeros_to_pad;\n        if (n > 0) {\n          if (str_l < str_m) {\n            size_t avail = str_m-str_l;\n            fast_memset(str+str_l, '0', (n>avail?avail:n));\n          }\n          str_l += n;\n        }\n      }\n   /* insert formatted string\n    * (or as-is conversion specifier for unknown conversions) */\n      { int n = str_arg_l - zero_padding_insertion_ind;\n        if (n > 0) {\n          if (str_l < str_m) {\n            size_t avail = str_m-str_l;\n            fast_memcpy(str+str_l, str_arg+zero_padding_insertion_ind,\n                        (n>avail?avail:n));\n          }\n          str_l += n;\n        }\n      }\n   /* insert right padding */\n      if (justify_left) {          /* right blank padding to the field width */\n        int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);\n        if (n > 0) {\n          if (str_l < str_m) {\n            size_t avail = str_m-str_l;\n            fast_memset(str+str_l, ' ', (n>avail?avail:n));\n          }\n          str_l += n;\n        }\n      }\n    }\n  }\n#if defined(NEED_SNPRINTF_ONLY)\n  va_end(ap);\n#endif\n  if (str_m > 0) { /* make sure the string is null-terminated\n                      even at the expense of overwriting the last character\n                      (shouldn't happen, but just in case) */\n    str[str_l <= str_m-1 ? str_l : str_m-1] = '\\0';\n  }\n  /* Return the number of characters formatted (excluding trailing null\n   * character), that is, the number of characters that would have been\n   * written to the buffer if it were large enough.\n   *\n   * The value of str_l should be returned, but str_l is of unsigned type\n   * size_t, and snprintf is int, possibly leading to an undetected\n   * integer overflow, resulting in a negative return value, which is illegal.\n   * Both XSH5 and ISO C99 (at least the draft) are silent on this issue.\n   * Should errno be set to EOVERFLOW and EOF returned in this case???\n   */\n  return (int) str_l;\n}\n#endif\n"
  },
  {
    "path": "src/snprintf.h",
    "content": "#ifndef _PORTABLE_SNPRINTF_H_\n#define _PORTABLE_SNPRINTF_H_\n\n#define PORTABLE_SNPRINTF_VERSION_MAJOR 2\n#define PORTABLE_SNPRINTF_VERSION_MINOR 2\n\n#include \"os.h\"\n#if defined(IS_MACOSX)\n\t#define HAVE_SNPRINTF\n#else\n\t#define HAVE_SNPRINTF\n\t#define PREFER_PORTABLE_SNPRINTF\n#endif\n\n#include <stddef.h>\n#include <stdarg.h>\n\n#ifdef __cplusplus\nextern \"C\" \n{\n#endif\n\n#ifdef HAVE_SNPRINTF\n#include <stdio.h>\n#else\nextern int snprintf(char *, size_t, const char *, /*args*/ ...);\nextern int vsnprintf(char *, size_t, const char *, va_list);\n#endif\n\n#if defined(HAVE_SNPRINTF) && defined(PREFER_PORTABLE_SNPRINTF)\nextern int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...);\nextern int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap);\n#define snprintf  portable_snprintf\n#define vsnprintf portable_vsnprintf\n#endif\n\nextern int asprintf  (char **ptr, const char *fmt, /*args*/ ...);\nextern int vasprintf (char **ptr, const char *fmt, va_list ap);\nextern int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...);\nextern int vasnprintf(char **ptr, size_t str_m, const char *fmt, va_list ap);\n\n#endif\n\n#ifdef __cplusplus\n}\n#endif"
  },
  {
    "path": "src/str_io.c",
    "content": "#include \"str_io.h\"\n#include \"zlib_util.h\"\n#include \"base64.h\"\n#include \"snprintf.h\" /* snprintf() */\n#include <stdio.h> /* fputs() */\n#include <ctype.h> /* isdigit() */\n#include <stdlib.h> /* atoi() */\n#include <string.h> /* strlen() */\n#include <assert.h>\n\n#if defined(_MSC_VER)\n\t#include \"ms_stdbool.h\"\n#else\n\t#include <stdbool.h>\n#endif\n\n#define STR_BITS_PER_PIXEL 24\n#define STR_BYTES_PER_PIXEL ((STR_BITS_PER_PIXEL) / 8)\n\n#define MAX_DIMENSION_LEN 5 /* Maximum length for [width] or [height]\n                             * in string. */\n\nconst char *MMBitmapStringErrorString(MMBMPStringError err)\n{\n\tswitch (err) {\n\t\tcase kMMBMPStringInvalidHeaderError:\n\t\t\treturn \"Invalid header for string\";\n\t\tcase kMMBMPStringDecodeError:\n\t\t\treturn \"Error decoding string\";\n\t\tcase kMMBMPStringDecompressError:\n\t\t\treturn \"Error decompressing string\";\n\t\tcase kMMBMPStringSizeError:\n\t\t\treturn \"String not of expected size\";\n\t\tcase MMMBMPStringEncodeError:\n\t\t\treturn \"Error encoding string\";\n\t\tcase kMMBMPStringCompressError:\n\t\t\treturn \"Error compressing string\";\n\t\tdefault:\n\t\t\treturn NULL;\n\t}\n}\n\n/* Parses beginning of string in the form of \"[width],[height],*\".\n *\n * If successful, |width| and |height| are set to the appropropriate values,\n * |len| is set to the length of [width] + the length of [height] + 2,\n * and true is returned; otherwise, false is returned.\n */\nstatic bool getSizeFromString(const uint8_t *buf, size_t buflen,\n                              size_t *width, size_t *height,\n                              size_t *len);\n\nMMBitmapRef createMMBitmapFromString(const uint8_t *buffer, size_t buflen,\n                                     MMBMPStringError *err)\n{\n\tuint8_t *decoded, *decompressed;\n\tsize_t width, height;\n\tsize_t len, bytewidth;\n\n\tif (*buffer++ != 'b' || !getSizeFromString(buffer, --buflen,\n\t                                           &width, &height, &len)) {\n\t\tif (err != NULL) *err = kMMBMPStringInvalidHeaderError;\n\t\treturn NULL;\n\t}\n\tbuffer += len;\n\tbuflen -= len;\n\n\tdecoded = base64decode(buffer, buflen, NULL);\n\tif (decoded == NULL) {\n\t\tif (err != NULL) *err = kMMBMPStringDecodeError;\n\t\treturn NULL;\n\t}\n\n\tdecompressed = zlib_decompress(decoded, &len);\n\tfree(decoded);\n\n\tif (decompressed == NULL) {\n\t\tif (err != NULL) *err = kMMBMPStringDecompressError;\n\t\treturn NULL;\n\t}\n\n\tbytewidth = width * STR_BYTES_PER_PIXEL; /* Note that bytewidth is NOT\n\t                                          * aligned to a padding. */\n\tif (height * bytewidth != len) {\n\t\tif (err != NULL) *err = kMMBMPStringSizeError;\n\t\treturn NULL;\n\t}\n\n\treturn createMMBitmap(decompressed, width, height,\n\t                      bytewidth, STR_BITS_PER_PIXEL, STR_BYTES_PER_PIXEL);\n}\n\n/* Returns bitmap data suitable for encoding to a string; that is, 24-bit BGR\n * bitmap with no padding and 3 bytes per pixel.\n *\n * Caller is responsible for free()'ing returned buffer. */\nstatic uint8_t *createRawBitmapData(MMBitmapRef bitmap);\n\nuint8_t *createStringFromMMBitmap(MMBitmapRef bitmap, MMBMPStringError *err)\n{\n\tuint8_t *raw, *compressed;\n\tuint8_t *ret, *encoded;\n\tsize_t len, retlen;\n\n\tassert(bitmap != NULL);\n\n\traw = createRawBitmapData(bitmap);\n\tif (raw == NULL) {\n\t\tif (err != NULL) *err = kMMBMPStringGenericError;\n\t\treturn NULL;\n\t}\n\n\tcompressed = zlib_compress(raw,\n\t                           bitmap->width * bitmap->height *\n\t                           STR_BYTES_PER_PIXEL,\n\t                           9, &len);\n\tfree(raw);\n\tif (compressed == NULL) {\n\t\tif (err != NULL) *err = kMMBMPStringCompressError;\n\t\treturn NULL;\n\t}\n\n\tencoded = base64encode(compressed, len - 1, &retlen);\n\tfree(compressed);\n\tif (encoded == NULL) {\n\t\tif (err != NULL) *err = MMMBMPStringEncodeError;\n\t\treturn NULL;\n\t}\n\n\tretlen += 3 + (MAX_DIMENSION_LEN * 2);\n\tret = calloc(sizeof(char), (retlen + 1));\n\tsnprintf((char *)ret, retlen, \"b%lu,%lu,%s\", (unsigned long)bitmap->width,\n\t                                             (unsigned long)bitmap->height,\n\t\t\t\t\t\t\t\t\t\t\t\t encoded);\n\tret[retlen] = '\\0';\n\tfree(encoded);\n\treturn ret;\n}\n\nstatic uint32_t parseDimension(const uint8_t *buf, size_t buflen,\n                               size_t *numlen);\n\nstatic bool getSizeFromString(const uint8_t *buf, size_t buflen,\n                              size_t *width, size_t *height,\n                              size_t *len)\n{\n\tsize_t numlen;\n\tassert(buf != NULL);\n\tassert(width != NULL);\n\tassert(height != NULL);\n\n\tif ((*width = parseDimension(buf, buflen, &numlen)) == 0) {\n\t\treturn false;\n\t}\n\t*len = numlen + 1;\n\n\tif ((*height = parseDimension(buf + *len, buflen, &numlen)) == 0) {\n\t\treturn false;\n\t}\n\t*len += numlen + 1;\n\n\treturn true;\n}\n\n/* Parses one dimension from string as described in getSizeFromString().\n * Returns dimension on success, or 0 on error. */\nstatic uint32_t parseDimension(const uint8_t *buf, size_t buflen,\n                               size_t *numlen)\n{\n\tchar num[MAX_DIMENSION_LEN + 1];\n\tsize_t i;\n\n\tassert(buf != NULL);\n\tassert(len != NULL);\n\tfor (i = 0; i < buflen && buf[i] != ',' && buf[i] != '\\0'; ++i) {\n\t\tif (!isdigit(buf[i]) || i > MAX_DIMENSION_LEN) return 0;\n\t\tnum[i] = buf[i];\n\t}\n\tnum[i] = '\\0';\n\t*numlen = i;\n\n\treturn (uint32_t)atoi(num);\n}\n\nstatic uint8_t *createRawBitmapData(MMBitmapRef bitmap)\n{\n\tuint8_t *raw = calloc(STR_BYTES_PER_PIXEL, bitmap->width * bitmap->height);\n\tsize_t y;\n\n\tfor (y = 0; y < bitmap->height; ++y) {\n\t\t/* No padding is added to string bitmaps. */\n\t\tconst size_t rowOffset = y * bitmap->width * STR_BYTES_PER_PIXEL;\n\t\tsize_t x;\n\t\tfor (x = 0; x < bitmap->width; ++x) {\n\t\t\t/* Copy in BGR format. */\n\t\t\tconst size_t colOffset = x * STR_BYTES_PER_PIXEL;\n\t\t\tuint8_t *dest = raw + rowOffset + colOffset;\n\t\t\tMMRGBColor *srcColor = MMRGBColorRefAtPoint(bitmap, x, y);\n\t\t\tdest[0] = srcColor->blue;\n\t\t\tdest[1] = srcColor->green;\n\t\t\tdest[2] = srcColor->red;\n\t\t}\n\t}\n\n\treturn raw;\n}\n"
  },
  {
    "path": "src/str_io.h",
    "content": "#pragma once\n#ifndef STR_IO_H\n#define STR_IO_H\n\n#include \"MMBitmap.h\"\n#include \"io.h\"\n#include <stdint.h>\n\n\nenum _MMBMPStringError {\n\tkMMBMPStringGenericError = 0,\n\tkMMBMPStringInvalidHeaderError,\n\tkMMBMPStringDecodeError,\n\tkMMBMPStringDecompressError,\n\tkMMBMPStringSizeError, /* Size does not match header. */\n\tMMMBMPStringEncodeError,\n\tkMMBMPStringCompressError\n};\n\ntypedef MMIOError MMBMPStringError;\n\n/* Creates a 24-bit bitmap from a compressed, printable string.\n *\n * String should be in the format: \"b[width],[height],[data]\",\n * where [width] and [height] are the image width & height, and [data]\n * is the raw image data run through zlib_compress() and base64_encode().\n *\n * Returns NULL on error; follows the Create Rule (that is, the caller is\n * responsible for destroy'()ing object).\n * If |error| is non-NULL, it will be set to the error code on return.\n */\nMMBitmapRef createMMBitmapFromString(const uint8_t *buffer, size_t buflen,\n                                     MMBMPStringError *error);\n\n/* Inverse of createMMBitmapFromString().\n *\n * Creates string in the format: \"b[width],[height],[data]\", where [width] and\n * [height] are the image width & height, and [data] is the raw image data run\n * through zlib_compress() and base64_encode().\n *\n * Returns NULL on error, or new string on success (to be free'()d by caller).\n * If |error| is non-NULL, it will be set to the error code on return.\n */\nuint8_t *createStringFromMMBitmap(MMBitmapRef bitmap, MMBMPStringError *error);\n\n/* Returns description of given error code.\n * Returned string is constant and hence should not be freed. */\nconst char *MMBitmapStringErrorString(MMBMPStringError err);\n\n#endif /* STR_IO_H */\n"
  },
  {
    "path": "src/types.h",
    "content": "#pragma once\n#ifndef TYPES_H\n#define TYPES_H\n\n#include \"os.h\"\n#include \"inline_keywords.h\" /* For H_INLINE */\n#include <stddef.h>\n#include <stdint.h>\n\n/* Some generic, cross-platform types. */\n\nstruct _MMPoint {\n\tsize_t x;\n\tsize_t y;\n};\n\ntypedef struct _MMPoint MMPoint;\n\n\nstruct _MMSignedPoint {\n\tint32_t x;\n\tint32_t y;\n};\n\ntypedef struct _MMSignedPoint MMSignedPoint;\n\nstruct _MMSize {\n\tsize_t width;\n\tsize_t height;\n};\n\ntypedef struct _MMSize MMSize;\n\nstruct _MMRect {\n\tMMPoint origin;\n\tMMSize size;\n};\n\ntypedef struct _MMRect MMRect;\n\nH_INLINE MMPoint MMPointMake(size_t x, size_t y)\n{\n\tMMPoint point;\n\tpoint.x = x;\n\tpoint.y = y;\n\treturn point;\n}\n\nH_INLINE MMSignedPoint MMSignedPointMake(int32_t x, int32_t y)\n{\n\tMMSignedPoint point;\n\tpoint.x = x;\n\tpoint.y = y;\n\treturn point;\n}\n\nH_INLINE MMSize MMSizeMake(size_t width, size_t height)\n{\n\tMMSize size;\n\tsize.width = width;\n\tsize.height = height;\n\treturn size;\n}\n\nH_INLINE MMRect MMRectMake(size_t x, size_t y, size_t width, size_t height)\n{\n\tMMRect rect;\n\trect.origin = MMPointMake(x, y);\n\trect.size = MMSizeMake(width, height);\n\treturn rect;\n}\n\n#define MMPointZero MMPointMake(0, 0)\n\n#if defined(IS_MACOSX)\n\n#define CGPointFromMMSignedPoint(p) CGPointMake((CGFloat)(p).x, (CGFloat)(p).y)\n#define MMSignedPointFromCGPoint(p) MMSignedPointMake((int32_t)(p).x, (int32_t)(p).y)\n\n#elif defined(IS_WINDOWS)\n\n#define MMSignedPointFromPOINT(p) MMSignedPointMake((int32_t)p.x, (int32_t)p.y)\n\n#endif\n\n#endif /* TYPES_H */\n"
  },
  {
    "path": "src/uthash.h",
    "content": "/*\n * Copyright (c) 2003-2009, Troy D. Hanson     http://uthash.sourceforge.net\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\n * IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\n * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\n * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER\n * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#pragma once\n#ifndef UTHASH_H\n#define UTHASH_H\n\n#include <string.h> /* memcmp, strlen */\n#include <stddef.h> /* ptrdiff_t */\n#include <stdint.h>\n\n\n#define UTHASH_VERSION 1.8\n\n/* C++ requires extra stringent casting */\n#if defined __cplusplus\n#define TYPEOF(x) (typeof(x))\n#else\n#define TYPEOF(x)\n#endif\n\n\n#define uthash_fatal(msg) exit(-1)        /* fatal error (out of memory,etc) */\n#define uthash_malloc(sz) malloc(sz)      /* malloc fcn                      */\n#define uthash_free(ptr) free(ptr)        /* free fcn                        */\n\n#define uthash_noexpand_fyi(tbl)          /* can be defined to log noexpand  */\n#define uthash_expand_fyi(tbl)            /* can be defined to log expands   */\n\n/* initial number of buckets */\n#define HASH_INITIAL_NUM_BUCKETS 32      /* initial number of buckets        */\n#define HASH_INITIAL_NUM_BUCKETS_LOG2 5  /* lg2 of initial number of buckets */\n#define HASH_BKT_CAPACITY_THRESH 10      /* expand when bucket count reaches */\n\n/* calculate the element whose hash handle address is hhe */\n#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)hhp) - (tbl)->hho))\n\n#define HASH_FIND(hh,head,keyptr,keylen,out)                                    \\\ndo {                                                                            \\\n  unsigned _hf_bkt,_hf_hashv;                                                   \\\n  out=TYPEOF(out)NULL;                                                          \\\n  if (head) {                                                                   \\\n     HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt);  \\\n     if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) {                          \\\n       HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \\\n                        keyptr,keylen,out);                                     \\\n     }                                                                          \\\n  }                                                                             \\\n} while (0)\n\n#if defined(HASH_BLOOM)\n#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM)\n#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0)\n#define HASH_BLOOM_MAKE(tbl)                                                    \\\ndo {                                                                            \\\n  (tbl)->bloom_nbits = HASH_BLOOM;                                              \\\n  (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN);                \\\n  if (!((tbl)->bloom_bv))  { uthash_fatal( \"out of memory\"); }                  \\\n  memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN);                               \\\n  (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE;                                      \\\n} while (0);\n\n#define HASH_BLOOM_FREE(tbl)                                                    \\\ndo {                                                                            \\\n  uthash_free((tbl)->bloom_bv);                                                 \\\n} while (0);\n\n#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8)))\n#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8)))\n\n#define HASH_BLOOM_ADD(tbl,hashv)                                               \\\n  HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))\n\n#define HASH_BLOOM_TEST(tbl,hashv)                                              \\\n  HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))\n\n#else\n#define HASH_BLOOM_MAKE(tbl)\n#define HASH_BLOOM_FREE(tbl)\n#define HASH_BLOOM_ADD(tbl,hashv)\n#define HASH_BLOOM_TEST(tbl,hashv) (1)\n#endif\n\n#define HASH_MAKE_TABLE(hh,head)                                                \\\ndo {                                                                            \\\n  (head)->hh.tbl = (UT_hash_table*)uthash_malloc(                               \\\n                  sizeof(UT_hash_table));                                       \\\n  if (!((head)->hh.tbl))  { uthash_fatal( \"out of memory\"); }                   \\\n  memset((head)->hh.tbl, 0, sizeof(UT_hash_table));                             \\\n  (head)->hh.tbl->tail = &((head)->hh);                                         \\\n  (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS;                       \\\n  (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2;             \\\n  (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head);                   \\\n  (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc(                     \\\n          HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket));              \\\n  if (! (head)->hh.tbl->buckets) { uthash_fatal( \"out of memory\"); }            \\\n  memset((head)->hh.tbl->buckets, 0,                                            \\\n          HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket));              \\\n  HASH_BLOOM_MAKE((head)->hh.tbl);                                              \\\n  (head)->hh.tbl->signature = HASH_SIGNATURE;                                   \\\n} while(0)\n\n#define HASH_ADD(hh,head,fieldname,keylen_in,add)                               \\\n        HASH_ADD_KEYPTR(hh,head,&add->fieldname,keylen_in,add)\n\n#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add)                           \\\ndo {                                                                            \\\n unsigned _ha_bkt;                                                              \\\n (add)->hh.next = NULL;                                                         \\\n (add)->hh.key = (char*)keyptr;                                                 \\\n (add)->hh.keylen = keylen_in;                                                  \\\n if (!(head)) {                                                                 \\\n    head = (add);                                                               \\\n    (head)->hh.prev = NULL;                                                     \\\n    HASH_MAKE_TABLE(hh,head);                                                   \\\n } else {                                                                       \\\n    (head)->hh.tbl->tail->next = (add);                                         \\\n    (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail);        \\\n    (head)->hh.tbl->tail = &((add)->hh);                                        \\\n }                                                                              \\\n (head)->hh.tbl->num_items++;                                                   \\\n (add)->hh.tbl = (head)->hh.tbl;                                                \\\n HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets,                        \\\n         (add)->hh.hashv, _ha_bkt);                                             \\\n HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh);                  \\\n HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv);                                \\\n HASH_EMIT_KEY(hh,head,keyptr,keylen_in);                                       \\\n HASH_FSCK(hh,head);                                                            \\\n} while(0)\n\n#define HASH_TO_BKT( hashv, num_bkts, bkt )                                     \\\ndo {                                                                            \\\n  bkt = ((hashv) & ((num_bkts) - 1));                                           \\\n} while(0)\n\n/* delete \"delptr\" from the hash table.\n * \"the usual\" patch-up process for the app-order doubly-linked-list.\n * The use of _hd_hh_del below deserves special explanation.\n * These used to be expressed using (delptr) but that led to a bug\n * if someone used the same symbol for the head and deletee, like\n *  HASH_DELETE(hh,users,users);\n * We want that to work, but by changing the head (users) below\n * we were forfeiting our ability to further refer to the deletee (users)\n * in the patch-up process. Solution: use scratch space in the table to\n * copy the deletee pointer, then the latter references are via that\n * scratch pointer rather than through the repointed (users) symbol.\n */\n#define HASH_DELETE(hh,head,delptr)                                             \\\ndo {                                                                            \\\n    unsigned _hd_bkt;                                                           \\\n    struct UT_hash_handle *_hd_hh_del;                                          \\\n    if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) )  {        \\\n        uthash_free((head)->hh.tbl->buckets );                                  \\\n        HASH_BLOOM_FREE((head)->hh.tbl);                                        \\\n        uthash_free((head)->hh.tbl);                                            \\\n        head = NULL;                                                            \\\n    } else {                                                                    \\\n        _hd_hh_del = &((delptr)->hh);                                           \\\n        if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) {    \\\n            (head)->hh.tbl->tail =                                              \\\n                (UT_hash_handle*)((char*)((delptr)->hh.prev) +                  \\\n                (head)->hh.tbl->hho);                                           \\\n        }                                                                       \\\n        if ((delptr)->hh.prev) {                                                \\\n            ((UT_hash_handle*)((char*)((delptr)->hh.prev) +                     \\\n                    (head)->hh.tbl->hho))->next = (delptr)->hh.next;            \\\n        } else {                                                                \\\n            head = TYPEOF(head)((delptr)->hh.next);                             \\\n        }                                                                       \\\n        if (_hd_hh_del->next) {                                                 \\\n            ((UT_hash_handle*)((char*)_hd_hh_del->next +                        \\\n                    (head)->hh.tbl->hho))->prev =                               \\\n                    _hd_hh_del->prev;                                           \\\n        }                                                                       \\\n        HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt);  \\\n        HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del);       \\\n        (head)->hh.tbl->num_items--;                                            \\\n    }                                                                           \\\n    HASH_FSCK(hh,head);                                                         \\\n} while (0)\n\n\n/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */\n#define HASH_FIND_STR(head,findstr,out)                                         \\\n    HASH_FIND(hh,head,findstr,strlen(findstr),out)\n#define HASH_ADD_STR(head,strfield,add)                                         \\\n    HASH_ADD(hh,head,strfield,strlen(add->strfield),add)\n#define HASH_FIND_INT(head,findint,out)                                         \\\n    HASH_FIND(hh,head,findint,sizeof(int),out)\n#define HASH_ADD_INT(head,intfield,add)                                         \\\n    HASH_ADD(hh,head,intfield,sizeof(int),add)\n#define HASH_DEL(head,delptr)                                                   \\\n    HASH_DELETE(hh,head,delptr)\n\n/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.\n * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.\n */\n#if defined(HASH_DEBUG)\n#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)\n#define HASH_FSCK(hh,head)                                                      \\\ndo {                                                                            \\\n    unsigned _bkt_i;                                                            \\\n    unsigned _count, _bkt_count;                                                \\\n    char *_prev;                                                                \\\n    struct UT_hash_handle *_thh;                                                \\\n    if (head) {                                                                 \\\n        _count = 0;                                                             \\\n        for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) {      \\\n            _bkt_count = 0;                                                     \\\n            _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head;                     \\\n            _prev = NULL;                                                       \\\n            while (_thh) {                                                      \\\n               if (_prev != (char*)(_thh->hh_prev)) {                           \\\n                   HASH_OOPS(\"invalid hh_prev %p, actual %p\\n\",                 \\\n                    _thh->hh_prev, _prev );                                     \\\n               }                                                                \\\n               _bkt_count++;                                                    \\\n               _prev = (char*)(_thh);                                           \\\n               _thh = _thh->hh_next;                                            \\\n            }                                                                   \\\n            _count += _bkt_count;                                               \\\n            if ((head)->hh.tbl->buckets[_bkt_i].count !=  _bkt_count) {         \\\n               HASH_OOPS(\"invalid bucket count %d, actual %d\\n\",                \\\n                (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count);             \\\n            }                                                                   \\\n        }                                                                       \\\n        if (_count != (head)->hh.tbl->num_items) {                              \\\n            HASH_OOPS(\"invalid hh item count %d, actual %d\\n\",                  \\\n                (head)->hh.tbl->num_items, _count );                            \\\n        }                                                                       \\\n        /* traverse hh in app order; check next/prev integrity, count */        \\\n        _count = 0;                                                             \\\n        _prev = NULL;                                                           \\\n        _thh =  &(head)->hh;                                                    \\\n        while (_thh) {                                                          \\\n           _count++;                                                            \\\n           if (_prev !=(char*)(_thh->prev)) {                                   \\\n              HASH_OOPS(\"invalid prev %p, actual %p\\n\",                         \\\n                    _thh->prev, _prev );                                        \\\n           }                                                                    \\\n           _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh);                   \\\n           _thh = ( _thh->next ?  (UT_hash_handle*)((char*)(_thh->next) +       \\\n                                  (head)->hh.tbl->hho) : NULL );                \\\n        }                                                                       \\\n        if (_count != (head)->hh.tbl->num_items) {                              \\\n            HASH_OOPS(\"invalid app item count %d, actual %d\\n\",                 \\\n                (head)->hh.tbl->num_items, _count );                            \\\n        }                                                                       \\\n    }                                                                           \\\n} while (0)\n#else\n#define HASH_FSCK(hh,head)\n#endif\n\n/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to\n * the descriptor to which this macro is defined for tuning the hash function.\n * The app can #include <unistd.h> to get the prototype for write(2). */\n#if defined(HASH_EMIT_KEYS)\n#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)                                  \\\ndo {                                                                            \\\n    unsigned _klen = fieldlen;                                                  \\\n    write(HASH_EMIT_KEYS, &_klen, sizeof(_klen));                               \\\n    write(HASH_EMIT_KEYS, keyptr, fieldlen);                                    \\\n} while (0)\n#else\n#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)\n#endif\n\n/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */\n#if defined(HASH_FUNCTION)\n#define HASH_FCN HASH_FUNCTION\n#else\n#define HASH_FCN HASH_JEN\n#endif\n\n/* The Bernstein hash function, used in Perl prior to v5.6 */\n#define HASH_BER(key,keylen,num_bkts,hashv,bkt)                                 \\\ndo {                                                                            \\\n  unsigned _hb_keylen=keylen;                                                   \\\n  char *_hb_key=(char*)key;                                                     \\\n  (hashv) = 0;                                                                  \\\n  while (_hb_keylen--)  { (hashv) = ((hashv) * 33) + *_hb_key++; }              \\\n  bkt = (hashv) & (num_bkts-1);                                                 \\\n} while (0)\n\n\n/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at\n * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */\n#define HASH_SAX(key,keylen,num_bkts,hashv,bkt)                                 \\\ndo {                                                                            \\\n  unsigned _sx_i;                                                               \\\n  char *_hs_key=(char*)key;                                                     \\\n  hashv = 0;                                                                    \\\n  for(_sx_i=0; _sx_i < keylen; _sx_i++)                                         \\\n      hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i];                    \\\n  bkt = hashv & (num_bkts-1);                                                   \\\n} while (0)\n\n#define HASH_FNV(key,keylen,num_bkts,hashv,bkt)                                 \\\ndo {                                                                            \\\n  unsigned _fn_i;                                                               \\\n  char *_hf_key=(char*)key;                                                     \\\n  hashv = 2166136261UL;                                                         \\\n  for(_fn_i=0; _fn_i < keylen; _fn_i++)                                         \\\n      hashv = (hashv * 16777619) ^ _hf_key[_fn_i];                              \\\n  bkt = hashv & (num_bkts-1);                                                   \\\n} while(0);\n\n#define HASH_OAT(key,keylen,num_bkts,hashv,bkt)                                 \\\ndo {                                                                            \\\n  unsigned _ho_i;                                                               \\\n  char *_ho_key=(char*)key;                                                     \\\n  hashv = 0;                                                                    \\\n  for(_ho_i=0; _ho_i < keylen; _ho_i++) {                                       \\\n      hashv += _ho_key[_ho_i];                                                  \\\n      hashv += (hashv << 10);                                                   \\\n      hashv ^= (hashv >> 6);                                                    \\\n  }                                                                             \\\n  hashv += (hashv << 3);                                                        \\\n  hashv ^= (hashv >> 11);                                                       \\\n  hashv += (hashv << 15);                                                       \\\n  bkt = hashv & (num_bkts-1);                                                   \\\n} while(0)\n\n#define HASH_JEN_MIX(a,b,c)                                                     \\\ndo {                                                                            \\\n  a -= b; a -= c; a ^= ( c >> 13 );                                             \\\n  b -= c; b -= a; b ^= ( a << 8 );                                              \\\n  c -= a; c -= b; c ^= ( b >> 13 );                                             \\\n  a -= b; a -= c; a ^= ( c >> 12 );                                             \\\n  b -= c; b -= a; b ^= ( a << 16 );                                             \\\n  c -= a; c -= b; c ^= ( b >> 5 );                                              \\\n  a -= b; a -= c; a ^= ( c >> 3 );                                              \\\n  b -= c; b -= a; b ^= ( a << 10 );                                             \\\n  c -= a; c -= b; c ^= ( b >> 15 );                                             \\\n} while (0)\n\n#define HASH_JEN(key,keylen,num_bkts,hashv,bkt)                                 \\\ndo {                                                                            \\\n  unsigned _hj_i,_hj_j,_hj_k;                                                   \\\n  char *_hj_key=(char*)key;                                                     \\\n  hashv = 0xfeedbeef;                                                           \\\n  _hj_i = _hj_j = 0x9e3779b9;                                                   \\\n  _hj_k = keylen;                                                               \\\n  while (_hj_k >= 12) {                                                         \\\n    _hj_i +=    (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 )                     \\\n        + ( (unsigned)_hj_key[2] << 16 )                                        \\\n        + ( (unsigned)_hj_key[3] << 24 ) );                                     \\\n    _hj_j +=    (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 )                     \\\n        + ( (unsigned)_hj_key[6] << 16 )                                        \\\n        + ( (unsigned)_hj_key[7] << 24 ) );                                     \\\n    hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 )                        \\\n        + ( (unsigned)_hj_key[10] << 16 )                                       \\\n        + ( (unsigned)_hj_key[11] << 24 ) );                                    \\\n                                                                                \\\n     HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                         \\\n                                                                                \\\n     _hj_key += 12;                                                             \\\n     _hj_k -= 12;                                                               \\\n  }                                                                             \\\n  hashv += keylen;                                                              \\\n  switch ( _hj_k ) {                                                            \\\n     case 11: hashv += ( (unsigned)_hj_key[10] << 24 );                         \\\n     case 10: hashv += ( (unsigned)_hj_key[9] << 16 );                          \\\n     case 9:  hashv += ( (unsigned)_hj_key[8] << 8 );                           \\\n     case 8:  _hj_j += ( (unsigned)_hj_key[7] << 24 );                          \\\n     case 7:  _hj_j += ( (unsigned)_hj_key[6] << 16 );                          \\\n     case 6:  _hj_j += ( (unsigned)_hj_key[5] << 8 );                           \\\n     case 5:  _hj_j += _hj_key[4];                                              \\\n     case 4:  _hj_i += ( (unsigned)_hj_key[3] << 24 );                          \\\n     case 3:  _hj_i += ( (unsigned)_hj_key[2] << 16 );                          \\\n     case 2:  _hj_i += ( (unsigned)_hj_key[1] << 8 );                           \\\n     case 1:  _hj_i += _hj_key[0];                                              \\\n  }                                                                             \\\n  HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                            \\\n  bkt = hashv & (num_bkts-1);                                                   \\\n} while(0)\n\n/* The Paul Hsieh hash function */\n#undef get16bits\n#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__)            \\\n  || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)\n#define get16bits(d) (*((const uint16_t *) (d)))\n#endif\n\n#if !defined (get16bits)\n#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)            \\\n                       +(uint32_t)(((const uint8_t *)(d))[0]) )\n#endif\n#define HASH_SFH(key,keylen,num_bkts,hashv,bkt)                                 \\\ndo {                                                                            \\\n  char *_sfh_key=(char*)key;                                                    \\\n  uint32_t _sfh_tmp, _sfh_len = keylen;                                         \\\n                                                                                \\\n  int _sfh_rem = _sfh_len & 3;                                                  \\\n  _sfh_len >>= 2;                                                               \\\n  hashv = 0xcafebabe;                                                           \\\n                                                                                \\\n  /* Main loop */                                                               \\\n  for (;_sfh_len > 0; _sfh_len--) {                                             \\\n    hashv    += get16bits (_sfh_key);                                           \\\n    _sfh_tmp       = (get16bits (_sfh_key+2) << 11) ^ hashv;                    \\\n    hashv     = (hashv << 16) ^ _sfh_tmp;                                       \\\n    _sfh_key += 2*sizeof (uint16_t);                                            \\\n    hashv    += hashv >> 11;                                                    \\\n  }                                                                             \\\n                                                                                \\\n  /* Handle end cases */                                                        \\\n  switch (_sfh_rem) {                                                           \\\n    case 3: hashv += get16bits (_sfh_key);                                      \\\n            hashv ^= hashv << 16;                                               \\\n            hashv ^= _sfh_key[sizeof (uint16_t)] << 18;                         \\\n            hashv += hashv >> 11;                                               \\\n            break;                                                              \\\n    case 2: hashv += get16bits (_sfh_key);                                      \\\n            hashv ^= hashv << 11;                                               \\\n            hashv += hashv >> 17;                                               \\\n            break;                                                              \\\n    case 1: hashv += *_sfh_key;                                                 \\\n            hashv ^= hashv << 10;                                               \\\n            hashv += hashv >> 1;                                                \\\n  }                                                                             \\\n                                                                                \\\n    /* Force \"avalanching\" of final 127 bits */                                 \\\n    hashv ^= hashv << 3;                                                        \\\n    hashv += hashv >> 5;                                                        \\\n    hashv ^= hashv << 4;                                                        \\\n    hashv += hashv >> 17;                                                       \\\n    hashv ^= hashv << 25;                                                       \\\n    hashv += hashv >> 6;                                                        \\\n    bkt = hashv & (num_bkts-1);                                                 \\\n} while(0);\n\n#if defined(HASH_USING_NO_STRICT_ALIASING)\n/* The MurmurHash exploits some CPU's (e.g. x86) tolerance for unaligned reads.\n * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error.\n * So MurmurHash comes in two versions, the faster unaligned one and the slower\n * aligned one. We only use the faster one on CPU's where we know it's safe.\n *\n * Note the preprocessor built-in defines can be emitted using:\n *\n *   gcc -m64 -dM -E - < /dev/null                  (on gcc)\n *   cc -## a.c (where a.c is a simple test file)   (Sun Studio)\n */\n#if (defined(__i386__) || defined(__x86_64__))\n#define HASH_MUR HASH_MUR_UNALIGNED\n#else\n#define HASH_MUR HASH_MUR_ALIGNED\n#endif\n\n/* Appleby's MurmurHash fast version for unaligned-tolerant archs like i386 */\n#define HASH_MUR_UNALIGNED(key,keylen,num_bkts,hashv,bkt)                       \\\ndo {                                                                            \\\n  const unsigned int _mur_m = 0x5bd1e995;                                       \\\n  const int _mur_r = 24;                                                        \\\n  hashv = 0xcafebabe ^ keylen;                                                  \\\n  char *_mur_key = (char *)key;                                                 \\\n  uint32_t _mur_tmp, _mur_len = keylen;                                         \\\n                                                                                \\\n  for (;_mur_len >= 4; _mur_len-=4) {                                           \\\n    _mur_tmp = *(uint32_t *)_mur_key;                                           \\\n    _mur_tmp *= _mur_m;                                                         \\\n    _mur_tmp ^= _mur_tmp >> _mur_r;                                             \\\n    _mur_tmp *= _mur_m;                                                         \\\n    hashv *= _mur_m;                                                            \\\n    hashv ^= _mur_tmp;                                                          \\\n    _mur_key += 4;                                                              \\\n  }                                                                             \\\n                                                                                \\\n  switch(_mur_len)                                                              \\\n  {                                                                             \\\n    case 3: hashv ^= _mur_key[2] << 16;                                         \\\n    case 2: hashv ^= _mur_key[1] << 8;                                          \\\n    case 1: hashv ^= _mur_key[0];                                               \\\n            hashv *= _mur_m;                                                    \\\n  };                                                                            \\\n                                                                                \\\n  hashv ^= hashv >> 13;                                                         \\\n  hashv *= _mur_m;                                                              \\\n  hashv ^= hashv >> 15;                                                         \\\n                                                                                \\\n  bkt = hashv & (num_bkts-1);                                                   \\\n} while(0)\n\n/* Appleby's MurmurHash version for alignment-sensitive archs like Sparc */\n#define HASH_MUR_ALIGNED(key,keylen,num_bkts,hashv,bkt)                         \\\ndo {                                                                            \\\n  const unsigned int _mur_m = 0x5bd1e995;                                       \\\n  const int _mur_r = 24;                                                        \\\n  hashv = 0xcafebabe ^ keylen;                                                  \\\n  char *_mur_key = (char *)key;                                                 \\\n  uint32_t _mur_len = keylen;                                                   \\\n  int _mur_align = (int)_mur_key & 3;                                           \\\n                                                                                \\\n  if (_mur_align && (_mur_len >= 4)) {                                          \\\n    unsigned _mur_t = 0, _mur_d = 0;                                            \\\n    switch(_mur_align) {                                                        \\\n      case 1: _mur_t |= _mur_key[2] << 16;                                      \\\n      case 2: _mur_t |= _mur_key[1] << 8;                                       \\\n      case 3: _mur_t |= _mur_key[0];                                            \\\n    }                                                                           \\\n    _mur_t <<= (8 * _mur_align);                                                \\\n    _mur_key += 4-_mur_align;                                                   \\\n    _mur_len -= 4-_mur_align;                                                   \\\n    int _mur_sl = 8 * (4-_mur_align);                                           \\\n    int _mur_sr = 8 * _mur_align;                                               \\\n                                                                                \\\n    for (;_mur_len >= 4; _mur_len-=4) {                                         \\\n      _mur_d = *(unsigned *)_mur_key;                                           \\\n      _mur_t = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl);                       \\\n      unsigned _mur_k = _mur_t;                                                 \\\n      _mur_k *= _mur_m;                                                         \\\n      _mur_k ^= _mur_k >> _mur_r;                                               \\\n      _mur_k *= _mur_m;                                                         \\\n      hashv *= _mur_m;                                                          \\\n      hashv ^= _mur_k;                                                          \\\n      _mur_t = _mur_d;                                                          \\\n      _mur_key += 4;                                                            \\\n    }                                                                           \\\n    _mur_d = 0;                                                                 \\\n    if(_mur_len >= _mur_align) {                                                \\\n      switch(_mur_align) {                                                      \\\n        case 3: _mur_d |= _mur_key[2] << 16;                                    \\\n        case 2: _mur_d |= _mur_key[1] << 8;                                     \\\n        case 1: _mur_d |= _mur_key[0];                                          \\\n      }                                                                         \\\n      unsigned _mur_k = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl);              \\\n      _mur_k *= _mur_m;                                                         \\\n      _mur_k ^= _mur_k >> _mur_r;                                               \\\n      _mur_k *= _mur_m;                                                         \\\n      hashv *= _mur_m;                                                          \\\n      hashv ^= _mur_k;                                                          \\\n      _mur_k += _mur_align;                                                     \\\n      _mur_len -= _mur_align;                                                   \\\n                                                                                \\\n      switch(_mur_len)                                                          \\\n      {                                                                         \\\n        case 3: hashv ^= _mur_key[2] << 16;                                     \\\n        case 2: hashv ^= _mur_key[1] << 8;                                      \\\n        case 1: hashv ^= _mur_key[0];                                           \\\n                hashv *= _mur_m;                                                \\\n      }                                                                         \\\n    } else {                                                                    \\\n      switch(_mur_len)                                                          \\\n      {                                                                         \\\n        case 3: _mur_d ^= _mur_key[2] << 16;                                    \\\n        case 2: _mur_d ^= _mur_key[1] << 8;                                     \\\n        case 1: _mur_d ^= _mur_key[0];                                          \\\n        case 0: hashv ^= (_mur_t >> _mur_sr) | (_mur_d << _mur_sl);             \\\n        hashv *= _mur_m;                                                        \\\n      }                                                                         \\\n    }                                                                           \\\n                                                                                \\\n    hashv ^= hashv >> 13;                                                       \\\n    hashv *= _mur_m;                                                            \\\n    hashv ^= hashv >> 15;                                                       \\\n  } else {                                                                      \\\n    for (;_mur_len >= 4; _mur_len-=4) {                                         \\\n      unsigned _mur_k = *(unsigned*)_mur_key;                                   \\\n      _mur_k *= _mur_m;                                                         \\\n      _mur_k ^= _mur_k >> _mur_r;                                               \\\n      _mur_k *= _mur_m;                                                         \\\n      hashv *= _mur_m;                                                          \\\n      hashv ^= _mur_k;                                                          \\\n      _mur_key += 4;                                                            \\\n    }                                                                           \\\n    switch(_mur_len)                                                            \\\n    {                                                                           \\\n      case 3: hashv ^= _mur_key[2] << 16;                                       \\\n      case 2: hashv ^= _mur_key[1] << 8;                                        \\\n      case 1: hashv ^= _mur_key[0];                                             \\\n      hashv *= _mur_m;                                                          \\\n    }                                                                           \\\n                                                                                \\\n    hashv ^= hashv >> 13;                                                       \\\n    hashv *= _mur_m;                                                            \\\n    hashv ^= hashv >> 15;                                                       \\\n  }                                                                             \\\n  bkt = hashv & (num_bkts-1);                                                   \\\n} while(0)\n#endif  /* HASH_USING_NO_STRICT_ALIASING */\n\n/* key comparison function; return 0 if keys equal */\n#define HASH_KEYCMP(a,b,len) memcmp(a,b,len)\n\n/* iterate over items in a known bucket to find desired item */\n#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out)                      \\\nout = TYPEOF(out)((head.hh_head) ? ELMT_FROM_HH(tbl,head.hh_head) : NULL);      \\\nwhile (out) {                                                                   \\\n    if (out->hh.keylen == keylen_in) {                                          \\\n        if ((HASH_KEYCMP(out->hh.key,keyptr,keylen_in)) == 0) break;            \\\n    }                                                                           \\\n    out= TYPEOF(out)((out->hh.hh_next) ?                                        \\\n                     ELMT_FROM_HH(tbl,out->hh.hh_next) : NULL);                 \\\n}\n\n/* add an item to a bucket  */\n#define HASH_ADD_TO_BKT(head,addhh)                                             \\\ndo {                                                                            \\\n head.count++;                                                                  \\\n (addhh)->hh_next = head.hh_head;                                               \\\n (addhh)->hh_prev = NULL;                                                       \\\n if (head.hh_head) { (head).hh_head->hh_prev = (addhh); }                       \\\n (head).hh_head=addhh;                                                          \\\n if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH)            \\\n     && (addhh)->tbl->noexpand != 1) {                                          \\\n       HASH_EXPAND_BUCKETS((addhh)->tbl);                                       \\\n }                                                                              \\\n} while(0)\n\n/* remove an item from a given bucket */\n#define HASH_DEL_IN_BKT(hh,head,hh_del)                                         \\\n    (head).count--;                                                             \\\n    if ((head).hh_head == hh_del) {                                             \\\n      (head).hh_head = hh_del->hh_next;                                         \\\n    }                                                                           \\\n    if (hh_del->hh_prev) {                                                      \\\n        hh_del->hh_prev->hh_next = hh_del->hh_next;                             \\\n    }                                                                           \\\n    if (hh_del->hh_next) {                                                      \\\n        hh_del->hh_next->hh_prev = hh_del->hh_prev;                             \\\n    }\n\n/* Bucket expansion has the effect of doubling the number of buckets\n * and redistributing the items into the new buckets. Ideally the\n * items will distribute more or less evenly into the new buckets\n * (the extent to which this is true is a measure of the quality of\n * the hash function as it applies to the key domain).\n *\n * With the items distributed into more buckets, the chain length\n * (item count) in each bucket is reduced. Thus by expanding buckets\n * the hash keeps a bound on the chain length. This bounded chain\n * length is the essence of how a hash provides constant time lookup.\n *\n * The calculation of tbl->ideal_chain_maxlen below deserves some\n * explanation. First, keep in mind that we're calculating the ideal\n * maximum chain length based on the *new* (doubled) bucket count.\n * In fractions this is just n/b (n=number of items,b=new num buckets).\n * Since the ideal chain length is an integer, we want to calculate\n * ceil(n/b). We don't depend on floating point arithmetic in this\n * hash, so to calculate ceil(n/b) with integers we could write\n *\n *      ceil(n/b) = (n/b) + ((n%b)?1:0)\n *\n * and in fact a previous version of this hash did just that.\n * But now we have improved things a bit by recognizing that b is\n * always a power of two. We keep its base 2 log handy (call it lb),\n * so now we can write this with a bit shift and logical AND:\n *\n *      ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)\n *\n */\n#define HASH_EXPAND_BUCKETS(tbl)                                                \\\ndo {                                                                            \\\n    unsigned _he_bkt;                                                           \\\n    unsigned _he_bkt_i;                                                         \\\n    struct UT_hash_handle *_he_thh, *_he_hh_nxt;                                \\\n    UT_hash_bucket *_he_new_buckets, *_he_newbkt;                               \\\n    _he_new_buckets = (UT_hash_bucket*)uthash_malloc(                           \\\n             2 * tbl->num_buckets * sizeof(struct UT_hash_bucket));             \\\n    if (!_he_new_buckets) { uthash_fatal( \"out of memory\"); }                   \\\n    memset(_he_new_buckets, 0,                                                  \\\n            2 * tbl->num_buckets * sizeof(struct UT_hash_bucket));              \\\n    tbl->ideal_chain_maxlen =                                                   \\\n       (tbl->num_items >> (tbl->log2_num_buckets+1)) +                          \\\n       ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0);                   \\\n    tbl->nonideal_items = 0;                                                    \\\n    for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++)               \\\n    {                                                                           \\\n        _he_thh = tbl->buckets[ _he_bkt_i ].hh_head;                            \\\n        while (_he_thh) {                                                       \\\n           _he_hh_nxt = _he_thh->hh_next;                                       \\\n           HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt);           \\\n           _he_newbkt = &(_he_new_buckets[ _he_bkt ]);                          \\\n           if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) {               \\\n             tbl->nonideal_items++;                                             \\\n             _he_newbkt->expand_mult = _he_newbkt->count /                      \\\n                                        tbl->ideal_chain_maxlen;                \\\n           }                                                                    \\\n           _he_thh->hh_prev = NULL;                                             \\\n           _he_thh->hh_next = _he_newbkt->hh_head;                              \\\n           if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev =              \\\n                _he_thh;                                                        \\\n           _he_newbkt->hh_head = _he_thh;                                       \\\n           _he_thh = _he_hh_nxt;                                                \\\n        }                                                                       \\\n    }                                                                           \\\n    tbl->num_buckets *= 2;                                                      \\\n    tbl->log2_num_buckets++;                                                    \\\n    uthash_free( tbl->buckets );                                                \\\n    tbl->buckets = _he_new_buckets;                                             \\\n    tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ?        \\\n        (tbl->ineff_expands+1) : 0;                                             \\\n    if (tbl->ineff_expands > 1) {                                               \\\n        tbl->noexpand=1;                                                        \\\n        uthash_noexpand_fyi(tbl);                                               \\\n    }                                                                           \\\n    uthash_expand_fyi(tbl);                                                     \\\n} while(0)\n\n\n/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */\n/* Note that HASH_SORT assumes the hash handle name to be hh.\n * HASH_SRT was added to allow the hash handle name to be passed in. */\n#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)\n#define HASH_SRT(hh,head,cmpfcn)                                                \\\ndo {                                                                            \\\n  unsigned _hs_i;                                                               \\\n  unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize;              \\\n  struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail;           \\\n  if (head) {                                                                   \\\n      _hs_insize = 1;                                                           \\\n      _hs_looping = 1;                                                          \\\n      _hs_list = &((head)->hh);                                                 \\\n      while (_hs_looping) {                                                     \\\n          _hs_p = _hs_list;                                                     \\\n          _hs_list = NULL;                                                      \\\n          _hs_tail = NULL;                                                      \\\n          _hs_nmerges = 0;                                                      \\\n          while (_hs_p) {                                                       \\\n              _hs_nmerges++;                                                    \\\n              _hs_q = _hs_p;                                                    \\\n              _hs_psize = 0;                                                    \\\n              for ( _hs_i = 0; _hs_i  < _hs_insize; _hs_i++ ) {                 \\\n                  _hs_psize++;                                                  \\\n                  _hs_q = (UT_hash_handle*)((_hs_q->next) ?                     \\\n                          ((void*)((char*)(_hs_q->next) +                       \\\n                          (head)->hh.tbl->hho)) : NULL);                        \\\n                  if (! (_hs_q) ) break;                                        \\\n              }                                                                 \\\n              _hs_qsize = _hs_insize;                                           \\\n              while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) {          \\\n                  if (_hs_psize == 0) {                                         \\\n                      _hs_e = _hs_q;                                            \\\n                      _hs_q = (UT_hash_handle*)((_hs_q->next) ?                 \\\n                              ((void*)((char*)(_hs_q->next) +                   \\\n                              (head)->hh.tbl->hho)) : NULL);                    \\\n                      _hs_qsize--;                                              \\\n                  } else if ( (_hs_qsize == 0) || !(_hs_q) ) {                  \\\n                      _hs_e = _hs_p;                                            \\\n                      _hs_p = (UT_hash_handle*)((_hs_p->next) ?                 \\\n                              ((void*)((char*)(_hs_p->next) +                   \\\n                              (head)->hh.tbl->hho)) : NULL);                    \\\n                      _hs_psize--;                                              \\\n                  } else if ((                                                  \\\n                      cmpfcn(TYPEOF(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)),  \\\n                            TYPEOF(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q)))   \\\n                             ) <= 0) {                                          \\\n                      _hs_e = _hs_p;                                            \\\n                      _hs_p = (UT_hash_handle*)((_hs_p->next) ?                 \\\n                              ((void*)((char*)(_hs_p->next) +                   \\\n                              (head)->hh.tbl->hho)) : NULL);                    \\\n                      _hs_psize--;                                              \\\n                  } else {                                                      \\\n                      _hs_e = _hs_q;                                            \\\n                      _hs_q = (UT_hash_handle*)((_hs_q->next) ?                 \\\n                              ((void*)((char*)(_hs_q->next) +                   \\\n                              (head)->hh.tbl->hho)) : NULL);                    \\\n                      _hs_qsize--;                                              \\\n                  }                                                             \\\n                  if ( _hs_tail ) {                                             \\\n                      _hs_tail->next = ((_hs_e) ?                               \\\n                            ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL);         \\\n                  } else {                                                      \\\n                      _hs_list = _hs_e;                                         \\\n                  }                                                             \\\n                  _hs_e->prev = ((_hs_tail) ?                                   \\\n                     ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL);             \\\n                  _hs_tail = _hs_e;                                             \\\n              }                                                                 \\\n              _hs_p = _hs_q;                                                    \\\n          }                                                                     \\\n          _hs_tail->next = NULL;                                                \\\n          if ( _hs_nmerges <= 1 ) {                                             \\\n              _hs_looping=0;                                                    \\\n              (head)->hh.tbl->tail = _hs_tail;                                  \\\n              (head) = TYPEOF(head)ELMT_FROM_HH((head)->hh.tbl, _hs_list);      \\\n          }                                                                     \\\n          _hs_insize *= 2;                                                      \\\n      }                                                                         \\\n      HASH_FSCK(hh,head);                                                       \\\n }                                                                              \\\n} while (0)\n\n/* This function selects items from one hash into another hash.\n * The end result is that the selected items have dual presence\n * in both hashes. There is no copy of the items made; rather\n * they are added into the new hash through a secondary hash\n * hash handle that must be present in the structure. */\n#define HASH_SELECT(hh_dst, dst, hh_src, src, cond)                             \\\ndo {                                                                            \\\n  unsigned _src_bkt, _dst_bkt;                                                  \\\n  void *_last_elt=NULL, *_elt;                                                  \\\n  UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL;                        \\\n  ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst));                \\\n  if (src) {                                                                    \\\n    for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) {    \\\n      for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head;               \\\n          _src_hh;                                                              \\\n          _src_hh = _src_hh->hh_next) {                                         \\\n          _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh);                      \\\n          if (cond(_elt)) {                                                     \\\n            _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho);              \\\n            _dst_hh->key = _src_hh->key;                                        \\\n            _dst_hh->keylen = _src_hh->keylen;                                  \\\n            _dst_hh->hashv = _src_hh->hashv;                                    \\\n            _dst_hh->prev = _last_elt;                                          \\\n            _dst_hh->next = NULL;                                               \\\n            if (_last_elt_hh) { _last_elt_hh->next = _elt; }                    \\\n            if (!dst) {                                                         \\\n              dst = TYPEOF(dst)_elt;                                            \\\n              HASH_MAKE_TABLE(hh_dst,dst);                                      \\\n            } else {                                                            \\\n              _dst_hh->tbl = (dst)->hh_dst.tbl;                                 \\\n            }                                                                   \\\n            HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt);   \\\n            HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh);           \\\n            (dst)->hh_dst.tbl->num_items++;                                     \\\n            _last_elt = _elt;                                                   \\\n            _last_elt_hh = _dst_hh;                                             \\\n          }                                                                     \\\n      }                                                                         \\\n    }                                                                           \\\n  }                                                                             \\\n  HASH_FSCK(hh_dst,dst);                                                        \\\n} while (0)\n\n#define HASH_CLEAR(hh,head)                                                     \\\ndo {                                                                            \\\n  if (head) {                                                                   \\\n    uthash_free((head)->hh.tbl->buckets );                                      \\\n    uthash_free((head)->hh.tbl);                                                \\\n    (head)=NULL;                                                                \\\n  }                                                                             \\\n} while(0)\n\n/* obtain a count of items in the hash */\n#define HASH_COUNT(head) HASH_CNT(hh,head)\n#define HASH_CNT(hh,head) (head?(head->hh.tbl->num_items):0)\n\ntypedef struct UT_hash_bucket {\n   struct UT_hash_handle *hh_head;\n   unsigned count;\n\n   /* expand_mult is normally set to 0. In this situation, the max chain length\n    * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If\n    * the bucket's chain exceeds this length, bucket expansion is triggered).\n    * However, setting expand_mult to a non-zero value delays bucket expansion\n    * (that would be triggered by additions to this particular bucket)\n    * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.\n    * (The multiplier is simply expand_mult+1). The whole idea of this\n    * multiplier is to reduce bucket expansions, since they are expensive, in\n    * situations where we know that a particular bucket tends to be overused.\n    * It is better to let its chain length grow to a longer yet-still-bounded\n    * value, than to do an O(n) bucket expansion too often.\n    */\n   unsigned expand_mult;\n\n} UT_hash_bucket;\n\n/* random signature used only to find hash tables in external analysis */\n#define HASH_SIGNATURE 0xa0111fe1\n#define HASH_BLOOM_SIGNATURE 0xb12220f2\n\ntypedef struct UT_hash_table {\n   UT_hash_bucket *buckets;\n   unsigned num_buckets, log2_num_buckets;\n   unsigned num_items;\n   struct UT_hash_handle *tail; /* tail hh in app order, for fast append    */\n   ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */\n\n   /* in an ideal situation (all buckets used equally), no bucket would have\n    * more than ceil(#items/#buckets) items. that's the ideal chain length. */\n   unsigned ideal_chain_maxlen;\n\n   /* nonideal_items is the number of items in the hash whose chain position\n    * exceeds the ideal chain maxlen. these items pay the penalty for an uneven\n    * hash distribution; reaching them in a chain traversal takes >ideal steps */\n   unsigned nonideal_items;\n\n   /* ineffective expands occur when a bucket doubling was performed, but\n    * afterward, more than half the items in the hash had nonideal chain\n    * positions. If this happens on two consecutive expansions we inhibit any\n    * further expansion, as it's not helping; this happens when the hash\n    * function isn't a good fit for the key domain. When expansion is inhibited\n    * the hash will still work, albeit no longer in constant time. */\n   unsigned ineff_expands, noexpand;\n\n   uint32_t signature; /* used only to find hash tables in external analysis */\n#if defined(HASH_BLOOM)\n   uint32_t bloom_sig; /* used only to test bloom exists in external analysis */\n   uint8_t *bloom_bv;\n   char bloom_nbits;\n#endif\n\n} UT_hash_table;\n\ntypedef struct UT_hash_handle {\n   struct UT_hash_table *tbl;\n   void *prev;                       /* prev element in app order      */\n   void *next;                       /* next element in app order      */\n   struct UT_hash_handle *hh_prev;   /* previous hh in bucket order    */\n   struct UT_hash_handle *hh_next;   /* next hh in bucket order        */\n   void *key;                        /* ptr to enclosing struct's key  */\n   unsigned keylen;                  /* enclosing struct's key len     */\n   unsigned hashv;                   /* result of hash-fcn(key)        */\n} UT_hash_handle;\n\n#endif /* UTHASH_H */\n"
  },
  {
    "path": "src/xdisplay.c",
    "content": "#include \"xdisplay.h\"\n#include <stdio.h> /* For fputs() */\n#include <stdlib.h> /* For atexit() */\n\nstatic Display *mainDisplay = NULL;\nstatic int registered = 0;\nstatic char *displayName = \":0.0\";\nstatic int hasDisplayNameChanged = 0;\n\nDisplay *XGetMainDisplay(void)\n{\n\t/* Close the display if displayName has changed */\n\tif (hasDisplayNameChanged) {\n\t\tXCloseMainDisplay();\n\t\thasDisplayNameChanged = 0;\n\t}\n\n\tif (mainDisplay == NULL) {\n\t\t/* First try the user set displayName */\n\t\tmainDisplay = XOpenDisplay(displayName);\n\n\t\t/* Then try using environment variable DISPLAY */\n\t\tif (mainDisplay == NULL) {\n\t\t\tmainDisplay = XOpenDisplay(NULL);\n\t\t}\n\n\t\tif (mainDisplay == NULL) {\n\t\t\tfputs(\"Could not open main display\\n\", stderr);\n\t\t} else if (!registered) {\n\t\t\tatexit(&XCloseMainDisplay);\n\t\t\tregistered = 1;\n\t\t}\n\t}\n\n\treturn mainDisplay;\n}\n\nvoid XCloseMainDisplay(void)\n{\n\tif (mainDisplay != NULL) {\n\t\tXCloseDisplay(mainDisplay);\n\t\tmainDisplay = NULL;\n\t}\n}\n\nchar *getXDisplay(void)\n{\n\treturn displayName;\n}\n\nvoid setXDisplay(const char *name)\n{\n\tdisplayName = strdup(name);\n\thasDisplayNameChanged = 1;\n}\n"
  },
  {
    "path": "src/xdisplay.h",
    "content": "#pragma once\n#ifndef XDISPLAY_H\n#define XDISPLAY_H\n\n#include <X11/Xlib.h>\n\n/* Returns the main display, closed either on exit or when closeMainDisplay()\n * is invoked. This removes a bit of the overhead of calling XOpenDisplay() &\n * XCloseDisplay() everytime the main display needs to be used.\n *\n * Note that this is almost certainly not thread safe. */\nDisplay *XGetMainDisplay(void);\n\n/* Closes the main display if it is open, or does nothing if not. */\nvoid XCloseMainDisplay(void);\n\n#ifdef __cplusplus\nextern \"C\"\n{\n#endif\n\nchar *getXDisplay(void);\nvoid setXDisplay(const char *name);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* XDISPLAY_H */\n"
  },
  {
    "path": "src/zlib_util.c",
    "content": "#include \"zlib_util.h\"\n#include <zlib.h>\n#include <stdio.h> /* fprintf() */\n#include <stdlib.h> /* malloc() */\n#include <assert.h>\n\n#define ZLIB_CHUNK (16 * 1024)\n\nuint8_t *zlib_decompress(const uint8_t *buf, size_t *len)\n{\n\tsize_t output_size = ZLIB_CHUNK;\n\tuint8_t *output = malloc(output_size);\n\tint err;\n\tz_stream zst;\n\n\t/* Sanity check */\n\tif (output == NULL) return NULL;\n\tassert(buf != NULL);\n\n\t/* Set inflate state */\n\tzst.zalloc = Z_NULL;\n\tzst.zfree = Z_NULL;\n\tzst.opaque = Z_NULL;\n\tzst.next_out = (Byte *)output;\n\tzst.next_in = (Byte *)buf;\n\tzst.avail_out = ZLIB_CHUNK;\n\n\tif (inflateInit(&zst) != Z_OK) goto error;\n\n\t/* Decompress input buffer */\n\tdo {\n\t\tif ((err = inflate(&zst, Z_NO_FLUSH)) == Z_OK) { /* Need more memory */\n\t\t\tzst.avail_out = (uInt)output_size;\n\n\t\t\t/* Double size each time to avoid calls to realloc() */\n\t\t\toutput_size <<= 1;\n\t\t\toutput = realloc(output, output_size + 1);\n\t\t\tif (output == NULL) return NULL;\n\n\t\t\tzst.next_out = (Byte *)(output + zst.avail_out);\n\t\t} else if (err != Z_STREAM_END) { /* Error decompressing */\n\t\t\tif (zst.msg != NULL) {\n\t\t\t\tfprintf(stderr, \"Could not decompress data: %s\\n\", zst.msg);\n\t\t\t}\n\t\t\tinflateEnd(&zst);\n\t\t\tgoto error;\n\t\t}\n\t} while (err != Z_STREAM_END);\n\n\tif (len != NULL) *len = zst.total_out;\n\tif (inflateEnd(&zst) != Z_OK) goto error;\n\treturn output; /* To be free()'d by caller */\n\nerror:\n\tif (output != NULL) free(output);\n\treturn NULL;\n}\n\nuint8_t *zlib_compress(const uint8_t *buf, const size_t buflen, int level,\n                       size_t *len)\n{\n\tz_stream zst;\n\tuint8_t *output = NULL;\n\n\t/* Sanity check */\n\tassert(buf != NULL);\n\tassert(len != NULL);\n\tassert(level <= 9 && level >= 0);\n\n\tzst.avail_out = (uInt)((buflen + (buflen / 10)) + 12);\n\toutput = malloc(zst.avail_out);\n\tif (output == NULL) return NULL;\n\n\t/* Set deflate state */\n\tzst.zalloc = Z_NULL;\n\tzst.zfree = Z_NULL;\n\tzst.next_out = (Byte *)output;\n\tzst.next_in = (Byte *)buf;\n\tzst.avail_in = (uInt)buflen;\n\n\tif (deflateInit(&zst, level) != Z_OK) goto error;\n\n\t/* Compress input buffer */\n\tif (deflate(&zst, Z_FINISH) != Z_STREAM_END) {\n\t\tif (zst.msg != NULL) {\n\t\t\tfprintf(stderr, \"Could not compress data: %s\\n\", zst.msg);\n\t\t}\n\t\tdeflateEnd(&zst);\n\t\tgoto error;\n\t}\n\n\tif (len != NULL) *len = zst.total_out;\n\tif (deflateEnd(&zst) != Z_OK) goto error;\n\treturn output; /* To be free()'d by caller */\n\nerror:\n\tif (output != NULL) free(output);\n\treturn NULL;\n}\n"
  },
  {
    "path": "src/zlib_util.h",
    "content": "#pragma once\n#ifndef ZLIB_UTIL_H\n#define ZLIB_UTIL_H\n\n#include <stddef.h>\n\n#if defined(_MSC_VER)\n\t#include \"ms_stdint.h\"\n#else\n\t#include <stdint.h>\n#endif\n\n/* Attempts to decompress given deflated NUL-terminated buffer.\n *\n * If successful and |len| is not NULL, |len| will be set to the number of\n * bytes in the returned buffer.\n * Returns new string to be free()'d by caller, or NULL on error. */\nuint8_t *zlib_decompress(const uint8_t *buf, size_t *len);\n\n/* Attempt to compress given buffer.\n *\n * The compression level is passed directly to zlib: it must between 0 and 9,\n * where 1 gives best speed, 9 gives best compression, and 0 gives no\n * compression at all.\n *\n * If successful and |len| is not NULL, |len| will be set to the number of\n * bytes in the returned buffer.\n * Returns new string to be free()'d by caller, or NULL on error. */\nuint8_t *zlib_compress(const uint8_t *buf, const size_t buflen, int level,\n                       size_t *len);\n\n#endif /* ZLIB_UTIL_H */\n"
  },
  {
    "path": "test/bitmap.js",
    "content": "var robot = require('..');\n\ndescribe('Bitmap', () => {\n  var params = {\n\t\t'width': 'number',\n\t\t'height': 'number',\n\t\t'byteWidth': 'number',\n\t\t'bitsPerPixel': 'number',\n\t\t'bytesPerPixel': 'number',\n\t\t'image': 'object'\n\t};\n\n\tit('Get a bitmap and check the parameters.', function() {\n\t\tvar img = robot.screen.capture();\n\n\t\tfor (var x in params)\n\t\t{\n\t\t\texpect(typeof img[x]).toEqual(params[x]);\n\t\t}\n\t});\n\n\tit('Get a bitmap of a specific size.', function()\n\t{\n\t\tvar size = 10;\n\t\tvar img = robot.screen.capture(0, 0, size, size);\n\n\t\t// Support for higher density screens.\n\t\tvar multi = img.width / size;\n\t\tvar size = size * multi;\n\t\texpect(img.height).toEqual(size);\n\t\texpect(img.width).toEqual(size);\n\t});\n\n\tit('Get a bitmap and make sure the colorAt works as expected.', function()\n\t{\n\t\tvar img = robot.screen.capture();\n\t\tvar hex = img.colorAt(0, 0);\n\n\t\t// t.ok(.it(hex), \"colorAt returned valid hex.\");\n\t\texpect(hex).toMatch(/^[0-9A-F]{6}$/i);\n\n\t\tvar screenSize = robot.getScreenSize();\n\t\tvar width = screenSize.width;\n\t\tvar height = screenSize.height;\n\n\t\t// Support for higher density screens.\n\t\tvar multi = img.width / width;\n\t\twidth = width * multi;\n\t\theight = height * multi;\n\t\texpect(() => img.colorAt(0, height)).toThrowError(/are outside the bitmap/);\n\t\texpect(() => img.colorAt(0, height-1)).not.toThrow();\n\t\texpect(() => img.colorAt(width, 0)).toThrowError(/are outside the bitmap/);\n\t\texpect(() => img.colorAt(9999999999999, 0)).toThrowError(/are outside the bitmap/);\n\t\texpect(() => img.colorAt(0, 9999999999999)).toThrowError(/are outside the bitmap/);\n\t});\n});\n"
  },
  {
    "path": "test/integration/keyboard.js",
    "content": "/* jshint esversion: 6 */\nvar robot = require('../..');\nvar targetpractice = require('targetpractice/index.js');\nvar os = require('os');\n\nrobot.setMouseDelay(100);\n\nvar target, elements;\n\ndescribe('Integration/Keyboard', () => {\n\tbeforeEach(done => {\n\t\ttarget = targetpractice.start();\n\t\ttarget.once('elements', message => {\n\t\t\telements = message;\n\t\t\tdone();\n\t\t});\n\t});\n\n\tafterEach(() => {\n\t\ttargetpractice.stop();\n\t\ttarget = null;\n\t});\n\n\tit('types', done => {\n\t\tconst stringToType = 'hello world';\n\t\t// Currently Target Practice waits for the \"user\" to finish typing before sending the event.\n\t\ttarget.once('type', element => {\n\t\t\texpect(element.id).toEqual('input_1');\n\t\t\texpect(element.text).toEqual(stringToType);\n\t\t\tdone();\n\t\t});\n\n\t\tconst input_1 = elements.input_1;\n\t\trobot.moveMouse(input_1.x, input_1.y);\n\t\trobot.mouseClick();\n\t\trobot.typeString(stringToType);\n\t});\n});\n"
  },
  {
    "path": "test/integration/mouse.js",
    "content": "/* jshint esversion: 6 */\nvar robot = require('../..');\nvar targetpractice = require('targetpractice/index.js');\nvar os = require('os');\n\nrobot.setMouseDelay(100);\n\nvar target, elements;\n\ndescribe('Integration/Mouse', () => {\n\tbeforeEach(done => {\n\t\ttarget = targetpractice.start();\n\t\ttarget.once('elements', message => {\n\t\t\telements = message;\n\t\t\tdone();\n\t\t});\n\t});\n\n\tafterEach(() => {\n\t\ttargetpractice.stop();\n\t\ttarget = null;\n\t});\n\n\tit('clicks', done => {\n\t\t// Alright we got a click event, did we click the button we wanted?\n\t\ttarget.once('click', function(e)\n\t\t{\n\t\t\texpect(e.id).toEqual('button_1');\n\t\t\texpect(e.type).toEqual('click');\n\t\t\tdone();\n\t\t});\n\n\t\t// For this test we want a button.\n\t\tvar button_1 = elements.button_1;\n\t\t// Click it!\n\t\trobot.moveMouse(button_1.x, button_1.y);\n\t\trobot.mouseClick();\n\t});\n\n\tit('scrolls vertically', done => {\n\t\ttarget.once('scroll', element => {\n\t\t\t/**\n\t\t\t *  TODO: This is gross! The scroll distance is different for each OS. I want\n\t\t\t *  to look into this further, but at least these numbers are consistent.\n\t\t\t */\n\t\t\tvar expectedScroll;\n\t\t\tswitch(os.platform()) {\n\t\t\t\tcase 'linux':\n\t\t\t\t\texpectedScroll = 180;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'win32':\n\t\t\t\t\texpectedScroll = 8;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\texpectedScroll = 10;\n\t\t\t}\n\t\t\texpect(element.id).toEqual('textarea_1');\n\t\t\texpect(element.scroll_y).toEqual(expectedScroll);\n\t\t\tdone();\n\t\t});\n\n\t\tvar textarea_1 = elements.textarea_1;\n\t\trobot.moveMouse(textarea_1.x, textarea_1.y);\n\t\trobot.mouseClick();\n\t\trobot.scrollMouse(0, -10);\n\t});\n\n\tit('scrolls horizontally', done => {\n\t\ttarget.once('scroll', element => {\n\t\t\t/**\n\t\t\t *  TODO: This is gross! The scroll distance is different for each OS. I want\n\t\t\t *  to look into this further, but at least these numbers are consistent.\n\t\t\t */\n\t\t\tvar expectedScroll;\n\t\t\tswitch(os.platform()) {\n\t\t\t\tcase 'linux':\n\t\t\t\t\texpectedScroll = 530;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'win32':\n\t\t\t\t\texpectedScroll = 8;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\texpectedScroll = 10;\n\t\t\t}\n\t\t\texpect(element.id).toEqual('textarea_1');\n\t\t\texpect(element.scroll_x).toEqual(expectedScroll);\n\t\t\tdone();\n\t\t});\n\n\t\tvar textarea_1 = elements.textarea_1;\n\t\trobot.moveMouse(textarea_1.x, textarea_1.y);\n\t\trobot.mouseClick();\n\t\trobot.scrollMouse(-10, 0);\n\t});\n});\n"
  },
  {
    "path": "test/integration/screen.js",
    "content": "/* jshint esversion: 6 */\nvar robot = require('../..');\nvar targetpractice = require('targetpractice/index.js');\nvar elements, target;\n\ndescribe('Integration/Screen', () => {\n\tbeforeEach(done => {\n\t\ttarget = targetpractice.start();\n\t\ttarget.once('elements', message => {\n\t\t\telements = message;\n\t\t\tdone();\n\t\t});\n\t});\n\n\tafterEach(() => {\n\t\ttargetpractice.stop();\n\t\ttarget = null;\n\t});\n\n\tit('reads a pixel color', (done) => {\n\t\tconst maxDelay = 1000\n\t\tjasmine.DEFAULT_TIMEOUT_INTERVAL = maxDelay + 1000\n\t\tconst expected = 'c0ff33'\n\t\tconst color_1 = elements.color_1;\n\t\tconst sleepTime = robot.getPixelColor(color_1.x, color_1.y) === expected ? 0\n\t\t\t: maxDelay\n\n\t\tsetTimeout(() => {\n\t\t\tconst color = robot.getPixelColor(color_1.x, color_1.y);\n\t\t\texpect(color).toEqual(expected);\n\t\t\tdone()\n\t\t}, sleepTime)\n\t});\n});\n"
  },
  {
    "path": "test/keyboard.js",
    "content": "var robot = require('..');\nvar os = require('os');\n\n// TODO: Need tests for keyToggle, typeString, typeStringDelayed, and setKeyboardDelay.\n\ndescribe('Keyboard', () => {\n  it('Tap a key.', function() {\n    expect(() => robot.keyTap('a')).not.toThrow();\n    expect(() => robot.keyTap('a', 'control')).not.toThrow();\n    expect(() => robot.keyTap()).toThrowError(/Invalid number/);\n  });\n\n  // This it won't fail if there's an issue, but it will help you identify an issue if ran locally.\n  it('Tap all keys.', function()\n  {\n    var chars = 'abcdefghijklmnopqrstuvwxyz1234567890,./;\\'[]\\\\'.split('');\n\n    for (var x in chars)\n    {\n      expect(() => robot.keyTap(chars[x])).not.toThrow();\n    }\n  });\n\n  // This it won't fail if there's an issue, but it will help you identify an issue if ran locally.\n  it('Tap all numpad keys.', function()\n  {\n    var nums = '0123456789'.split('');\n\n    for (var x in nums)\n    {\n      if (os.platform() === 'linux')\n      {\n        expect(() => robot.keyTap('numpad_' + nums[x])).toThrowError(/Invalid key code/);\n      }\n      else\n      {\n        expect(() => robot.keyTap('numpad_' + nums[x])).not.toThrow();\n      }\n    }\n  });\n});\n\ntest('Tap a Unicode character.', function(t)\n{\n\tt.plan(7);\n\tt.ok(robot.unicodeTap(\"r\".charCodeAt(0)) === 1, 'successfully tapped \"r\".');\n\tt.ok(robot.unicodeTap(\"ά\".charCodeAt(0)) === 1, 'successfully tapped \"ά\".');\n\tt.ok(robot.unicodeTap(\"ö\".charCodeAt(0)) === 1, 'successfully tapped \"ö\".');\n\tt.ok(robot.unicodeTap(\"ち\".charCodeAt(0)) === 1, 'successfully tapped \"ち\".');\n\tt.ok(robot.unicodeTap(\"嗨\".charCodeAt(0)) === 1, 'successfully tapped \"嗨\".');\n\tt.ok(robot.unicodeTap(\"ఝ\".charCodeAt(0)) === 1, 'successfully tapped \"ఝ\".');\n\n\tt.throws(function()\n\t{\n\t\trobot.unicodeTap();\n\t}, /Invalid character typed./, 'tap nothing.');\n});\n\ntest('Test Key Toggle.', function(t)\n{\n\tt.plan(4);\n\n\tt.ok(robot.keyToggle(\"a\", \"down\") === 1, 'Successfully pressed a.');\n\tt.ok(robot.keyToggle(\"a\", \"up\") === 1, 'Successfully released a.');\n\n\tt.throws(function()\n\t{\n\t\tt.ok(robot.keyToggle(\"ά\", \"down\") === 1, 'Successfully pressed ά.');\n\t\tt.ok(robot.keyToggle(\"ά\", \"up\") === 1, 'Successfully released ά.');\n\t}, /Invalid key code specified./, 'exception tapping ά.');\n\n\tt.throws(function()\n\t{\n\t\tt.ok(robot.keyToggle(\"嗨\", \"down\") === 1, 'Successfully pressed 嗨.');\n\t\tt.ok(robot.keyToggle(\"嗨\", \"up\") === 1, 'Successfully released 嗨.');\n\t}, /Invalid key code specified./, 'exception tapping 嗨.');\n});\n\ntest('Type Ctrl+Shift+RightArrow.', function(t)\n{\n\tt.plan(2);\n\n\tvar modifiers = []\n    modifiers.push('shift')\n\tmodifiers.push('control')\n\n\tt.ok(robot.keyToggle(\"right\", \"down\", modifiers) === 1, 'Successfully pressed Ctrl+Shift+RightArrow.');\n\tt.ok(robot.keyToggle(\"right\", \"up\", modifiers) === 1, 'Successfully released Ctrl+Shift+RightArrow.');\n});\n\ntest('Type a string.', function(t)\n{\n\tt.plan(2);\n\tt.ok(robot.typeString(\"Typed rάöち嗨ఝ 1\") === 1, 'successfully typed \"Typed rάöち嗨ఝ 1\".');\n\n\tt.throws(function()\n\t{\n\t\tt.ok(robot.typeString() === 1, 'Successfully typed nothing.');\n\t}, /Invalid number of arguments./, 'exception tapping nothing.');\n});\n\ntest('Type a string with delay.', function(t)\n{\n\tt.plan(2);\n\n\t// 10 characters per minute -> 3 seconds to write the whole sentence here.\n\tt.ok(robot.typeStringDelayed(\"Typed rάöち嗨ఝ with delay 1\", 600) === 1, 'successfully typed with delay \"Typed rάöち嗨ఝ with delay 1\".');\n\n\tt.throws(function()\n\t{\n\t\tt.ok(robot.typeStringDelayed() === 1, 'Successfully typed nothing.');\n\t}, /Invalid number of arguments./, 'exception tapping nothing.');\n});\n"
  },
  {
    "path": "test/mouse.js",
    "content": "var robot = require('..');\nvar lastKnownPos, currentPos;\n\n//Increase delay to help it reliability.\nrobot.setMouseDelay(100);\n\ndescribe('Mouse', () => {\n  it('Get the initial mouse position.', function()\n  {\n    expect(lastKnownPos = robot.getMousePos()).toBeTruthy();\n    expect(lastKnownPos.x !== undefined).toBeTruthy();\n    expect(lastKnownPos.y !== undefined).toBeTruthy();\n  });\n\n  it('Move the mouse.', function()\n  {\n    lastKnownPos = robot.moveMouse(0, 0);\n    expect(robot.moveMouse(100, 100) === 1).toBeTruthy();\n    currentPos = robot.getMousePos();\n    expect(currentPos.x === 100).toBeTruthy();\n    expect(currentPos.y === 100).toBeTruthy();\n\n    expect(function()\n    {\n      robot.moveMouse(0, 1, 2, 3);\n    }).toThrowError(/Invalid number/);\n\n    expect(function()\n    {\n      robot.moveMouse(0);\n    }).toThrowError(/Invalid number/);\n\n    expect(robot.moveMouse(\"0\", \"0\") === 1).toBeTruthy();\n\n  });\n\n  it('Move the mouse smoothly.', function()\n  {\n    lastKnownPos = robot.moveMouseSmooth(0, 0);\n    expect(robot.moveMouseSmooth(100, 100) === 1).toBeTruthy();\n    currentPos = robot.getMousePos();\n    expect(currentPos.x).toEqual(100);\n    expect(currentPos.y).toEqual(100);\n\n    expect(function()\n    {\n      robot.moveMouseSmooth(0, 1, 2, 3);\n    }).toThrowError(/Invalid number/);\n\n    expect(function()\n    {\n      robot.moveMouseSmooth(0);\n    }).toThrowError(/Invalid number/);\n\n    expect(robot.moveMouseSmooth(\"0\", \"0\") === 1).toBeTruthy();\n\n  });\n\n  it('Click the mouse.', function()\n  {\n    expect(robot.mouseClick()).toBeTruthy();\n    expect(robot.mouseClick(\"left\") === 1).toBeTruthy();\n    expect(robot.mouseClick(\"middle\") === 1).toBeTruthy();\n    expect(robot.mouseClick(\"right\") === 1).toBeTruthy();\n\n    expect(robot.mouseClick(\"left\", 1)).toBeTruthy();\n\n    expect(function()\n    {\n      robot.mouseClick(\"party\");\n    }).toThrowError(/Invalid mouse/);\n\n    expect(function()\n    {\n      robot.mouseClick(\"0\");\n    }).toThrowError(/Invalid mouse/);\n\n    expect(function()\n    {\n      robot.mouseClick(\"left\", 0, \"it\");\n    }).toThrowError(/Invalid number/);\n\n  });\n\n  it('Drag the mouse.', function()\n  {\n\n    expect(robot.dragMouse(5, 5) === 1).toBeTruthy();\n\n    expect(function()\n    {\n      robot.dragMouse(0);\n    }).toThrowError(/Invalid number/);\n\n    expect(function()\n    {\n      robot.dragMouse(1, 1, \"left\", 5);\n    }).toThrowError(/Invalid number/);\n\n    expect(function()\n    {\n      robot.dragMouse(2, 2, \"party\");\n    }).toThrowError(/Invalid mouse/);\n\n  });\n\n  it('Mouse scroll.', function()\n  {\n    expect(lastKnownPos = robot.getMousePos()).toBeTruthy();\n    expect(robot.mouseClick() === 1).toBeTruthy();\n    expect(robot.scrollMouse(0, 1 * 120) === 1).toBeTruthy();\n    expect(robot.scrollMouse(0, 20 * 120) === 1).toBeTruthy();\n    expect(robot.scrollMouse(0, -5 * 120) === 1).toBeTruthy();\n    expect(robot.scrollMouse(1 * 120, 0) === 1).toBeTruthy();\n    expect(robot.scrollMouse(20 * 120, 0) === 1).toBeTruthy();\n    expect(robot.scrollMouse(-5 * 120, 0) === 1).toBeTruthy();\n    expect(robot.scrollMouse(-5 * 120, -5 * 120) === 1).toBeTruthy();\n  });\n\n  it('Mouse Toggle', function()\n  {\n    expect(lastKnownPos = robot.getMousePos()).toBeTruthy();\n    expect(robot.mouseToggle('up', 'right') === 1).toBeTruthy();\n  });\n});\n"
  },
  {
    "path": "test/screen.js",
    "content": "var robot = require('..');\nvar pixelColor, screenSize;\n\ndescribe('Screen', () => {\n  it('Get pixel color.', function()\n  {\n    expect(pixelColor = robot.getPixelColor(5, 5)).toBeTruthy();\n    expect(pixelColor !== undefined).toBeTruthy();\n    expect(pixelColor.length === 6).toBeTruthy();\n    expect(/^[0-9A-F]{6}$/i.test(pixelColor)).toBeTruthy();\n\n    expect(function()\n    {\n      robot.getPixelColor(9999999999999, 9999999999999);\n    }).toThrowError(/outside the main screen/);\n\n    expect(function()\n    {\n      robot.getPixelColor(-1, -1);\n    }).toThrowError(/outside the main screen/);\n\n    expect(function()\n    {\n      robot.getPixelColor(0);\n    }).toThrowError(/Invalid number/);\n\n    expect(function()\n    {\n      robot.getPixelColor(1, 2, 3);\n    }).toThrowError(/Invalid number/);\n  });\n\n  it('Get screen size.', function()\n  {\n    expect(screenSize = robot.getScreenSize()).toBeTruthy();\n    expect(screenSize.width !== undefined).toBeTruthy();\n    expect(screenSize.height !== undefined).toBeTruthy();\n  });\n});\n"
  }
]