Showing preview only (365K chars total). Download the full file or copy to clipboard to get everything.
Repository: octalmage/robotjs
Branch: master
Commit: 3ec96be5fd19
Files: 71
Total size: 346.8 KB
Directory structure:
gitextract_my6o19ab/
├── .github/
│ ├── CONTRIBUTING.md
│ └── ISSUE_TEMPLATE.md
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── appveyor.yml
├── binding.gyp
├── index.d.ts
├── index.js
├── package.json
├── src/
│ ├── MMBitmap.c
│ ├── MMBitmap.h
│ ├── MMPointArray.c
│ ├── MMPointArray.h
│ ├── UTHashTable.c
│ ├── UTHashTable.h
│ ├── alert.c
│ ├── alert.h
│ ├── base64.c
│ ├── base64.h
│ ├── bitmap_find.c
│ ├── bitmap_find.h
│ ├── bmp_io.c
│ ├── bmp_io.h
│ ├── color_find.c
│ ├── color_find.h
│ ├── deadbeef_rand.c
│ ├── deadbeef_rand.h
│ ├── endian.h
│ ├── inline_keywords.h
│ ├── io.c
│ ├── io.h
│ ├── keycode.c
│ ├── keycode.h
│ ├── keypress.c
│ ├── keypress.h
│ ├── microsleep.h
│ ├── mouse.c
│ ├── mouse.h
│ ├── ms_stdbool.h
│ ├── ms_stdint.h
│ ├── os.h
│ ├── pasteboard.c
│ ├── pasteboard.h
│ ├── png_io.c
│ ├── png_io.h
│ ├── rgb.h
│ ├── robotjs.cc
│ ├── screen.c
│ ├── screen.h
│ ├── screengrab.c
│ ├── screengrab.h
│ ├── snprintf.c
│ ├── snprintf.h
│ ├── str_io.c
│ ├── str_io.h
│ ├── types.h
│ ├── uthash.h
│ ├── xdisplay.c
│ ├── xdisplay.h
│ ├── zlib_util.c
│ └── zlib_util.h
└── test/
├── bitmap.js
├── integration/
│ ├── keyboard.js
│ ├── mouse.js
│ └── screen.js
├── keyboard.js
├── mouse.js
└── screen.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/CONTRIBUTING.md
================================================
## How to contribute
* Please try to match the style of the code around the feature/bug you're working on.
* Use tabs not spaces.
* 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.
* 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.
================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
<!--- Provide a general summary of the issue in the Title above. -->
## Expected Behavior
<!--- If you're describing a bug, tell us what should happen. -->
<!--- If you're suggesting a change/improvement, tell us how it should work. -->
## Current Behavior
<!--- If describing a bug, tell us what happens instead of the expected behavior. -->
<!--- If suggesting a change/improvement, explain the difference from current behavior. -->
## Possible Solution
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
<!--- or ideas how to implement the addition or change. -->
## Steps to Reproduce (for bugs)
<!--- Please provide an unambiguous set of steps to reproduce this bug. -->
<!--- Include code to reproduce. -->
1.
2.
3.
4.
## Context
<!--- How has this issue affected you? What are you trying to accomplish? -->
<!--- Providing context helps us come up with a solution that is most useful in the real world. -->
## Your Environment
<!--- Include as many relevant details about the environment you experienced the bug in. -->
* RobotJS version:
* Node.js version:
* npm version:
* Operating System:
================================================
FILE: .gitignore
================================================
build/
node_modules/
prebuilds/
/.idea
================================================
FILE: .travis.yml
================================================
sudo: false
language: cpp
env:
matrix:
- TRAVIS_NODE_VERSION="8"
- TRAVIS_NODE_VERSION="10"
- TRAVIS_NODE_VERSION="12"
- TRAVIS_NODE_VERSION="stable"
os:
- linux
- osx
cache:
directories:
- node_modules
git:
depth: 5
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- libx11-dev
- zlib1g-dev
- libpng12-dev
- libxtst-dev
- g++-4.8
- gcc-4.8
before_install:
- 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
- nvm install $TRAVIS_NODE_VERSION
- PATH=$PATH:`pwd`/node_modules/.bin
# print versions
- node --version
- npm --version
# use g++-4.8 on Linux
- if [[ $TRAVIS_OS_NAME == "linux" ]]; then export CXX=g++-4.8; fi
- $CXX --version
install:
- npm install
script:
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then npm test; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then xvfb-run npm test; fi
after_success:
- if [[ $TRAVIS_TAG != "" ]]; then npm run prebuild -- -u $GITHUB_TOKEN; fi
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/e737dd5170be50cdba95
on_success: change
on_failure: always
on_start: never
================================================
FILE: CHANGELOG.md
================================================
## 0.7.0 (2026-03-11)
* build: Bump prebuild version and MACOSX_DEPLOYMENT_TARGET. ([7bcd7ff](https://github.com/octalmage/robotjs/commit/7bcd7ff))
* 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)
* Don't wait for a response from X when typing a key ([087ec35](https://github.com/octalmage/robotjs/commit/087ec35))
* Apply to mouse as well ([bf54f85](https://github.com/octalmage/robotjs/commit/bf54f85))
* Fixed scrollMouse() bug on windows ([c10ffdd](https://github.com/octalmage/robotjs/commit/c10ffdd))
* src/keycode: fix buffer overflow ([274d03b](https://github.com/octalmage/robotjs/commit/274d03b))
* Support typing of Unicode Characters (similar to keyTap) ([94ed67c](https://github.com/octalmage/robotjs/commit/94ed67c))
* Support Unicode for typeString() in Windows. ([3d7e7cf](https://github.com/octalmage/robotjs/commit/3d7e7cf))
* fix: scrolling on Windows ([cb0da09](https://github.com/octalmage/robotjs/commit/cb0da09))
* fix: improve y scrolling on Windows by send y-scrolling event first ([a348c0e](https://github.com/octalmage/robotjs/commit/a348c0e))
## 0.6.0 (2019-12-08)
* 0.6.0 ([99021d8](https://github.com/octalmage/robotjs/commit/99021d8))
* Add changelog ([7e067cf](https://github.com/octalmage/robotjs/commit/7e067cf))
* Fix deprecations ([4f16371](https://github.com/octalmage/robotjs/commit/4f16371))
* Fix cpp deprecation ([3b93d07](https://github.com/octalmage/robotjs/commit/3b93d07))
* Update packages ([77d7cd9](https://github.com/octalmage/robotjs/commit/77d7cd9))
* Update 3rd party builder configs ([28a428d](https://github.com/octalmage/robotjs/commit/28a428d))
* Make keypress/release order more "human" ([ec88aa8](https://github.com/octalmage/robotjs/commit/ec88aa8))
* Use scancodes instead of virtual keys ([bd93bdf](https://github.com/octalmage/robotjs/commit/bd93bdf))
* Added parameter to change the speed of mouse in movemouseSmooth ([1fad4c8](https://github.com/octalmage/robotjs/commit/1fad4c8))
* Fix error with mouse moving in Windows with multiple displays ([45e323a](https://github.com/octalmage/robotjs/commit/45e323a))
* Migrate to SendInput for toggleMouse ([1c7b96a](https://github.com/octalmage/robotjs/commit/1c7b96a))
* Upgrade to SendInput for win32KeyEvent ([218e208](https://github.com/octalmage/robotjs/commit/218e208))
* Migrate to windows-build-tools as a dev dependency for Windows ([e5469fa](https://github.com/octalmage/robotjs/commit/e5469fa))
* Reduce compilation warnings ([da17c61](https://github.com/octalmage/robotjs/commit/da17c61))
* Reduce repeated SendInput calls in scroll Mouse ([ee432a6](https://github.com/octalmage/robotjs/commit/ee432a6))
* Updated node_version 12 ([42e6d28](https://github.com/octalmage/robotjs/commit/42e6d28))
* node v8 deprecated errors fixed. Added support for node v12.x.x ([77f741f](https://github.com/octalmage/robotjs/commit/77f741f))
* v8::{Handle => Local} for node 12 compatibility ([9a3b161](https://github.com/octalmage/robotjs/commit/9a3b161))
## <small>0.5.1 (2018-03-03)</small>
* 0.5.1 ([b9b0ab6](https://github.com/octalmage/robotjs/commit/b9b0ab6))
## 0.5.0 (2018-03-03)
* 0.5.0 ([0687a34](https://github.com/octalmage/robotjs/commit/0687a34))
* Another attempt. ([f5f4a60](https://github.com/octalmage/robotjs/commit/f5f4a60))
* Only build on tag. ([3b8cf98](https://github.com/octalmage/robotjs/commit/3b8cf98))
* Only run on master. ([91a5397](https://github.com/octalmage/robotjs/commit/91a5397))
* Pass token through npm. ([b69246c](https://github.com/octalmage/robotjs/commit/b69246c))
* Should be tag name. ([eb1c889](https://github.com/octalmage/robotjs/commit/eb1c889))
* Try using defined. ([dd96c25](https://github.com/octalmage/robotjs/commit/dd96c25))
* Upload Windows build. ([5d92169](https://github.com/octalmage/robotjs/commit/5d92169))
* Use EQU instead. ([755bcd1](https://github.com/octalmage/robotjs/commit/755bcd1))
* Use on_success. ([e7dc303](https://github.com/octalmage/robotjs/commit/e7dc303))
## <small>0.4.8 (2018-02-26)</small>
* - package lock ([34d3653](https://github.com/octalmage/robotjs/commit/34d3653))
* Add integration tests. ([c29d20d](https://github.com/octalmage/robotjs/commit/c29d20d))
* Add test for screen. ([eb11444](https://github.com/octalmage/robotjs/commit/eb11444))
* Added right_control and left_control to key flags as well as right_shift ([33f838d](https://github.com/octalmage/robotjs/commit/33f838d))
* Allow negative coordinates for mouseMove and dragMouse ([bb63305](https://github.com/octalmage/robotjs/commit/bb63305))
* Cat for testing. ([02a7f94](https://github.com/octalmage/robotjs/commit/02a7f94))
* Change to prebuild command. ([895db63](https://github.com/octalmage/robotjs/commit/895db63))
* Deploy scripts. ([9c26d95](https://github.com/octalmage/robotjs/commit/9c26d95))
* Finish Jasmine migration. ([98eb2ca](https://github.com/octalmage/robotjs/commit/98eb2ca))
* Fix API URL ([a2f6da3](https://github.com/octalmage/robotjs/commit/a2f6da3))
* Fix comment. ([f2bbeab](https://github.com/octalmage/robotjs/commit/f2bbeab))
* Fix createStringForKey failure on Mac OS for non-Latin languages ([0045e18](https://github.com/octalmage/robotjs/commit/0045e18))
* Fix for Windows. ([facc34f](https://github.com/octalmage/robotjs/commit/facc34f))
* Fix scrolling issue. ([d95866f](https://github.com/octalmage/robotjs/commit/d95866f))
* Fix test for Linux. ([643b925](https://github.com/octalmage/robotjs/commit/643b925))
* Fixed Linux Y-axis scrolling bug ([798f761](https://github.com/octalmage/robotjs/commit/798f761))
* Flip x on Windows to match other platforms. ([db15ff7](https://github.com/octalmage/robotjs/commit/db15ff7))
* horizontal scroll test. ([0ebd39e](https://github.com/octalmage/robotjs/commit/0ebd39e))
* Increase timeout. ([4c053ea](https://github.com/octalmage/robotjs/commit/4c053ea))
* Jasmine migration in progress. ([24ed91d](https://github.com/octalmage/robotjs/commit/24ed91d))
* Link in readme to docs goes straight to website ([64f5400](https://github.com/octalmage/robotjs/commit/64f5400))
* Move keyboard tests to it's own file. ([37e3165](https://github.com/octalmage/robotjs/commit/37e3165))
* Remove old header tags. ([da920c2](https://github.com/octalmage/robotjs/commit/da920c2))
* Remove unsupported Node versions. ([112a9a4](https://github.com/octalmage/robotjs/commit/112a9a4))
* Remove unsupported Node.js versions for Appveyor. ([452bd6c](https://github.com/octalmage/robotjs/commit/452bd6c))
* 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)
* Support older versions of Node. ([07d6f0e](https://github.com/octalmage/robotjs/commit/07d6f0e))
* Test cleanup. ([cc03080](https://github.com/octalmage/robotjs/commit/cc03080))
* The quotes break tests on windows. ([1f80fb1](https://github.com/octalmage/robotjs/commit/1f80fb1))
* Update package.json to remove tape. ([6584c95](https://github.com/octalmage/robotjs/commit/6584c95))
* Update screen file. ([23f9513](https://github.com/octalmage/robotjs/commit/23f9513))
* Update scroll mouse for 0.4.6 ([cdb0b76](https://github.com/octalmage/robotjs/commit/cdb0b76)), closes [#194](https://github.com/octalmage/robotjs/issues/194)
* Update target practice. ([092f3c7](https://github.com/octalmage/robotjs/commit/092f3c7))
* Updated link to docs in under #API ([f7170e5](https://github.com/octalmage/robotjs/commit/f7170e5))
* Use XSync instead of XFlush everywhere - fixes https://github.com/octalmage/robotjs/issues/347 ([9f3a4c4](https://github.com/octalmage/robotjs/commit/9f3a4c4))
* Windows scrolling bug ([b4869dd](https://github.com/octalmage/robotjs/commit/b4869dd)), closes [#360](https://github.com/octalmage/robotjs/issues/360)
## <small>0.4.7 (2017-03-26)</small>
* 0.4.7 ([06dedb4](https://github.com/octalmage/robotjs/commit/06dedb4))
* Fix trailing comma. ([733caae](https://github.com/octalmage/robotjs/commit/733caae))
* Use prebuild-install. ([7bd769c](https://github.com/octalmage/robotjs/commit/7bd769c))
## <small>0.4.6 (2017-03-26)</small>
* 0.4.6 ([d935e41](https://github.com/octalmage/robotjs/commit/d935e41))
* Accidentally added old scroll code back. ([6f8d2b6](https://github.com/octalmage/robotjs/commit/6f8d2b6))
* Add "menu" key (windows only) ([96220f1](https://github.com/octalmage/robotjs/commit/96220f1))
* Added left and right control keys ([c1f04e0](https://github.com/octalmage/robotjs/commit/c1f04e0))
* Bump prebuild. ([ee210f1](https://github.com/octalmage/robotjs/commit/ee210f1))
* Do not break linux or mac build ([85c5b9f](https://github.com/octalmage/robotjs/commit/85c5b9f))
* Encode to utf-16 for CGEventKeyboardSetUnicodeString ([ea45808](https://github.com/octalmage/robotjs/commit/ea45808))
* Fix errors reported by CI on undeclared variables ([daadbcc](https://github.com/octalmage/robotjs/commit/daadbcc))
* Fixed spacing ([630b198](https://github.com/octalmage/robotjs/commit/630b198))
* Fixed spacing. ([3ff4434](https://github.com/octalmage/robotjs/commit/3ff4434))
* Modified the mouseToggle method signature, since according to the documentation the button parameter ([cfac5ab](https://github.com/octalmage/robotjs/commit/cfac5ab))
* Support Unicode Characters for .typeString() ([7236ff3](https://github.com/octalmage/robotjs/commit/7236ff3))
## <small>0.4.5 (2016-10-16)</small>
* 0.4.5 ([dfd7e60](https://github.com/octalmage/robotjs/commit/dfd7e60))
* Add CONTRIBUTING.md. ([76e56a6](https://github.com/octalmage/robotjs/commit/76e56a6))
* Add f13-24. ([f4854ce](https://github.com/octalmage/robotjs/commit/f4854ce))
* Add ISSUE_TEMPLATE.md. ([30dfce7](https://github.com/octalmage/robotjs/commit/30dfce7))
* Add LICENSE.md. ([60b36ca](https://github.com/octalmage/robotjs/commit/60b36ca))
* Add link to Electron instructions. ([b4adaaa](https://github.com/octalmage/robotjs/commit/b4adaaa))
* Add spaces before comments. ([a9e12ed](https://github.com/octalmage/robotjs/commit/a9e12ed))
* Add typescript declaration file ([56b90e0](https://github.com/octalmage/robotjs/commit/56b90e0))
* Add waffle.io link. ([b4c8108](https://github.com/octalmage/robotjs/commit/b4c8108))
* better argument parsing ([4501a8b](https://github.com/octalmage/robotjs/commit/4501a8b))
* change function style, matching rest of code ([9257211](https://github.com/octalmage/robotjs/commit/9257211))
* Fix merge conflict. ([d6d9e23](https://github.com/octalmage/robotjs/commit/d6d9e23))
* fix os compatibility ([91cd9ce](https://github.com/octalmage/robotjs/commit/91cd9ce))
* Fix tests for higher density screens. ([299a66d](https://github.com/octalmage/robotjs/commit/299a66d))
* Fixed ascii art. ([efa3582](https://github.com/octalmage/robotjs/commit/efa3582))
* Fixed formatting ([9623852](https://github.com/octalmage/robotjs/commit/9623852))
* get and set display name for XOpenDisplay() ([cdbf8df](https://github.com/octalmage/robotjs/commit/cdbf8df))
* improve variable names, move hasDisplayNameChanged reset ([0e457ba](https://github.com/octalmage/robotjs/commit/0e457ba))
* No saving code for now. ([42a2562](https://github.com/octalmage/robotjs/commit/42a2562))
* Remove .save. ([4e0f44c](https://github.com/octalmage/robotjs/commit/4e0f44c))
* Remove bitmap save method. ([f58e519](https://github.com/octalmage/robotjs/commit/f58e519))
* 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)
* Use a smarter fix for tests. ([f4707f2](https://github.com/octalmage/robotjs/commit/f4707f2))
## <small>0.4.4 (2016-06-01)</small>
* 0.4.4 ([483b46d](https://github.com/octalmage/robotjs/commit/483b46d))
* Add right_shift. ([666cc6f](https://github.com/octalmage/robotjs/commit/666cc6f))
## <small>0.4.3 (2016-05-30)</small>
* 0.4.3 ([3e5f02b](https://github.com/octalmage/robotjs/commit/3e5f02b))
* Add missing word. ([a76792a](https://github.com/octalmage/robotjs/commit/a76792a))
* Add numpad 0-9. ([afc4863](https://github.com/octalmage/robotjs/commit/afc4863))
* Add numpad tests. ([8e24802](https://github.com/octalmage/robotjs/commit/8e24802))
* Added safety checks. ([ab802ba](https://github.com/octalmage/robotjs/commit/ab802ba))
* Align AppVeyor build versions with Travis CI. ([d2fc806](https://github.com/octalmage/robotjs/commit/d2fc806))
* Build on Mac and Linux. ([efd4ec6](https://github.com/octalmage/robotjs/commit/efd4ec6))
* Changed mouseScroll to use X and Y as direction. ([9917f49](https://github.com/octalmage/robotjs/commit/9917f49))
* Compile and test our module. ([c688f04](https://github.com/octalmage/robotjs/commit/c688f04))
* Don't run numpad tests on Linux. ([d7cc60e](https://github.com/octalmage/robotjs/commit/d7cc60e))
* Fix whitespace. ([041df96](https://github.com/octalmage/robotjs/commit/041df96))
* Fixed error: ‘for’ loop initial declarations are only allowed in C99 mode ([3f3c5c5](https://github.com/octalmage/robotjs/commit/3f3c5c5))
* Fixed old variable left over. ([b6eecbf](https://github.com/octalmage/robotjs/commit/b6eecbf))
* Fixed scroll now using signed ints. ([51082e9](https://github.com/octalmage/robotjs/commit/51082e9))
* Fixed unused variable warning. ([60021fc](https://github.com/octalmage/robotjs/commit/60021fc))
* Ignore prebuilds directory. ([4d003a5](https://github.com/octalmage/robotjs/commit/4d003a5))
* Migrate to SendInput from mouse_event (#181) ([66611b7](https://github.com/octalmage/robotjs/commit/66611b7)), closes [#181](https://github.com/octalmage/robotjs/issues/181)
* New .travis.yml for Mac testing. ([25e4dcf](https://github.com/octalmage/robotjs/commit/25e4dcf))
* New screen capture code. ([fdbbff9](https://github.com/octalmage/robotjs/commit/fdbbff9))
* New sine wave mouse gif. ([6e09549](https://github.com/octalmage/robotjs/commit/6e09549))
* Not sure how this file got here. ([e22fec6](https://github.com/octalmage/robotjs/commit/e22fec6))
* Only start Xvfb on Linux. ([00effc9](https://github.com/octalmage/robotjs/commit/00effc9))
* Readme updates! ([2756b3e](https://github.com/octalmage/robotjs/commit/2756b3e))
* Release mode and pixEnc. ([58f6c52](https://github.com/octalmage/robotjs/commit/58f6c52))
* Remove whitespace. ([cea1cae](https://github.com/octalmage/robotjs/commit/cea1cae))
* Seperate before_install and before_script. ([f0e067a](https://github.com/octalmage/robotjs/commit/f0e067a))
* Space before comment. ([e2ff6cd](https://github.com/octalmage/robotjs/commit/e2ff6cd))
* Test that numpad throws on Linux. ([7543df7](https://github.com/octalmage/robotjs/commit/7543df7))
## <small>0.4.2 (2016-05-08)</small>
* 0.4.2 ([d3226d2](https://github.com/octalmage/robotjs/commit/d3226d2))
* add insert-key ([b910e7e](https://github.com/octalmage/robotjs/commit/b910e7e))
* fix spacing ([7720012](https://github.com/octalmage/robotjs/commit/7720012))
* fixing insert-key for x11 ([3ce729f](https://github.com/octalmage/robotjs/commit/3ce729f))
* fixing whitespaces caused by editor-settings ([037abf3](https://github.com/octalmage/robotjs/commit/037abf3))
* Insert isn't a valid key on Mac. ([ac0d16b](https://github.com/octalmage/robotjs/commit/ac0d16b))
* Migrate to SendInput for Mouse movement ([39f89f0](https://github.com/octalmage/robotjs/commit/39f89f0))
* Revert "Test fix for node 0.8 failing." ([d5e3668](https://github.com/octalmage/robotjs/commit/d5e3668))
* Update to nan 2.2.1. ([54c65c3](https://github.com/octalmage/robotjs/commit/54c65c3))
* Use prebuild. ([7b48ed5](https://github.com/octalmage/robotjs/commit/7b48ed5))
## <small>0.4.1 (2016-04-17)</small>
* 0.4.1 ([e2b7108](https://github.com/octalmage/robotjs/commit/e2b7108))
* Add new test to help identify keycode issues. ([3d8ba52](https://github.com/octalmage/robotjs/commit/3d8ba52))
* Remove keyCodeRequiresSystemDefinedEvent and use new keycodes. ([bfcafa4](https://github.com/octalmage/robotjs/commit/bfcafa4))
* Use new keycode for multimedia keys. ([7a4d90c](https://github.com/octalmage/robotjs/commit/7a4d90c))
## 0.4.0 (2016-04-17)
* 0 is the image, 1 is x, and 2 is y. :| ([c411267](https://github.com/octalmage/robotjs/commit/c411267))
* 0 is the image, 1 is x, and 2 is y. :| ([c41f382](https://github.com/octalmage/robotjs/commit/c41f382))
* 0.4.0 ([9f3818e](https://github.com/octalmage/robotjs/commit/9f3818e))
* Add notifications to TravisCI builds. ([c8cfc1c](https://github.com/octalmage/robotjs/commit/c8cfc1c))
* Add tests for bitmap. ([ab29b45](https://github.com/octalmage/robotjs/commit/ab29b45))
* Added BMP class and buildBMP. ([bcac2ac](https://github.com/octalmage/robotjs/commit/bcac2ac))
* Added KEYEVENTF_EXTENDEDKEY flag when extended keys are triggered on win32 ([8e0cc92](https://github.com/octalmage/robotjs/commit/8e0cc92))
* Added KEYEVENTF_EXTENDEDKEY flag when extended keys are triggered on win32 ([081978e](https://github.com/octalmage/robotjs/commit/081978e))
* Added typeStringDelayed. ([45afde6](https://github.com/octalmage/robotjs/commit/45afde6))
* Allow x, y, width, and height to be passed. ([bb2ab38](https://github.com/octalmage/robotjs/commit/bb2ab38))
* Argument checking for getPixelColor. ([4a5572f](https://github.com/octalmage/robotjs/commit/4a5572f))
* Better error handling for dragMouse. ([93417d3](https://github.com/octalmage/robotjs/commit/93417d3))
* Better error handling for moveMouse. ([dd6e658](https://github.com/octalmage/robotjs/commit/dd6e658))
* Better error handling for moveMouseSmooth. ([2e221ba](https://github.com/octalmage/robotjs/commit/2e221ba))
* Better tests for getPixelColor. ([d8cbb74](https://github.com/octalmage/robotjs/commit/d8cbb74))
* Better tests for moveMouse. ([0f09623](https://github.com/octalmage/robotjs/commit/0f09623))
* Call captureScreen with arguments if passed. ([da59926](https://github.com/octalmage/robotjs/commit/da59926))
* Convert the rest of the spaces. ([f653b92](https://github.com/octalmage/robotjs/commit/f653b92))
* Correct buffer size. ([cb76f5e](https://github.com/octalmage/robotjs/commit/cb76f5e))
* Create calculateDeltas function and use it in dragMouse. ([7a199d4](https://github.com/octalmage/robotjs/commit/7a199d4))
* Create calculateDeltas function and use it in dragMouse. ([ad713f0](https://github.com/octalmage/robotjs/commit/ad713f0))
* Define colorAt at declaration. ([03759cb](https://github.com/octalmage/robotjs/commit/03759cb))
* 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)
* Make sure coords are in bounds. ([b1f2852](https://github.com/octalmage/robotjs/commit/b1f2852))
* Map virtual key before triggering keybd_event ([c47f8b0](https://github.com/octalmage/robotjs/commit/c47f8b0))
* Only define calculateDeltas for Mac. ([3eef8cb](https://github.com/octalmage/robotjs/commit/3eef8cb))
* Only define calculateDeltas for Mac. ([f7fac28](https://github.com/octalmage/robotjs/commit/f7fac28))
* Remove extra spaces. ([2ad647b](https://github.com/octalmage/robotjs/commit/2ad647b))
* Remove unnecessary space. ([a764c66](https://github.com/octalmage/robotjs/commit/a764c66))
* Remove unused code. ([6539910](https://github.com/octalmage/robotjs/commit/6539910))
* Run keyboard tests. ([4d2c20d](https://github.com/octalmage/robotjs/commit/4d2c20d))
* Starting tests for keyTap. ([c9063ca](https://github.com/octalmage/robotjs/commit/c9063ca))
* Starting to implement bitmap sharing. ([b58962f](https://github.com/octalmage/robotjs/commit/b58962f))
* Starts at 0. ([0cd6db2](https://github.com/octalmage/robotjs/commit/0cd6db2))
* Test fix for node 0.8 failing. ([eb18a1f](https://github.com/octalmage/robotjs/commit/eb18a1f))
* Tests for dragMouse. ([dfa83f6](https://github.com/octalmage/robotjs/commit/dfa83f6))
* Tests for mouseClick. ([b0cfad0](https://github.com/octalmage/robotjs/commit/b0cfad0))
* Tests for moveMouseSmooth. ([ad7e5dc](https://github.com/octalmage/robotjs/commit/ad7e5dc))
* 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)
* Throw catchable error if requested pixels are outside the bitmap's dimensions. ([d65576b](https://github.com/octalmage/robotjs/commit/d65576b))
* Un-mix up the deltaX and deltaY. ([e3f084e](https://github.com/octalmage/robotjs/commit/e3f084e))
* Un-mix up the deltaX and deltaY. ([1cf70f1](https://github.com/octalmage/robotjs/commit/1cf70f1))
* Update keypress.c ([faddf9c](https://github.com/octalmage/robotjs/commit/faddf9c))
* Updated TravisCI and Appveyor configs to allow 0.8.x failures. ([38f85c0](https://github.com/octalmage/robotjs/commit/38f85c0))
* Use tabs instead of spaces. ([8ecd518](https://github.com/octalmage/robotjs/commit/8ecd518))
* Use tabs instead of spaces. ([47b901f](https://github.com/octalmage/robotjs/commit/47b901f))
* Workaround for games not detecting mouse moves. ([9e3a067](https://github.com/octalmage/robotjs/commit/9e3a067))
* Workaround for games not detecting mouse moves. ([6fd31a8](https://github.com/octalmage/robotjs/commit/6fd31a8))
* TODO: Need tests for mouseToggle, and scrollMouse. ([648fff4](https://github.com/octalmage/robotjs/commit/648fff4))
## <small>0.3.7 (2015-12-29)</small>
* 0.3.7 ([4843f0b](https://github.com/octalmage/robotjs/commit/4843f0b))
* Fixed F9 key ([48c432f](https://github.com/octalmage/robotjs/commit/48c432f))
* Keep code style consistent. ([69ab661](https://github.com/octalmage/robotjs/commit/69ab661))
* Screen Capture Fix. ([bcec0bc](https://github.com/octalmage/robotjs/commit/bcec0bc))
* Updated comments to match code style. ([d5096fe](https://github.com/octalmage/robotjs/commit/d5096fe))
* 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)
## <small>0.3.6 (2015-12-14)</small>
* 0.3.6 ([1a3df83](https://github.com/octalmage/robotjs/commit/1a3df83))
## <small>0.3.5 (2015-12-07)</small>
* 0.3.5 ([3e9d533](https://github.com/octalmage/robotjs/commit/3e9d533))
* Link to relevant wiki sections. ([727313b](https://github.com/octalmage/robotjs/commit/727313b))
* Prevent unused variable warning for Mac and Linux. ([c3edbc2](https://github.com/octalmage/robotjs/commit/c3edbc2))
* Update README URLs based on HTTP redirects ([2072907](https://github.com/octalmage/robotjs/commit/2072907))
## <small>0.3.4 (2015-11-22)</small>
* 0.3.4 ([4b28b99](https://github.com/octalmage/robotjs/commit/4b28b99))
* Add FAQ section. ([ee0fe6d](https://github.com/octalmage/robotjs/commit/ee0fe6d))
* Add link to create a new issue. ([1e740ec](https://github.com/octalmage/robotjs/commit/1e740ec))
* Add multi-monitor question to FAQ. ([9592dc9](https://github.com/octalmage/robotjs/commit/9592dc9))
* Added dragMouse function ([406acfb](https://github.com/octalmage/robotjs/commit/406acfb)), closes [#127](https://github.com/octalmage/robotjs/issues/127)
* Added support for arrays of key flags ([c2e62d6](https://github.com/octalmage/robotjs/commit/c2e62d6))
* Center badges. ([a264ed8](https://github.com/octalmage/robotjs/commit/a264ed8))
* Center logo. ([b615176](https://github.com/octalmage/robotjs/commit/b615176))
* Change wording a bit. ([721bb8d](https://github.com/octalmage/robotjs/commit/721bb8d))
* Link to Twitter and blog. ([5fd2c51](https://github.com/octalmage/robotjs/commit/5fd2c51))
* Made compatible with older versions of Windows. ([d99f3bb](https://github.com/octalmage/robotjs/commit/d99f3bb))
* Passing constant instead of zero for mouseButton on mouse move ([78603ff](https://github.com/octalmage/robotjs/commit/78603ff))
* Remove blog section. ([9edeefc](https://github.com/octalmage/robotjs/commit/9edeefc))
* Use new blog URL. ([f0a6f2f](https://github.com/octalmage/robotjs/commit/f0a6f2f))
* Using v8::Handle instead of v8::Local for older versions of Node ([e24ee51](https://github.com/octalmage/robotjs/commit/e24ee51))
## <small>0.3.3 (2015-11-02)</small>
* 0.3.3 ([eeb7633](https://github.com/octalmage/robotjs/commit/eeb7633))
* Add some new keywords. ([dd59242](https://github.com/octalmage/robotjs/commit/dd59242))
* Fix for #118. ([570f453](https://github.com/octalmage/robotjs/commit/570f453)), closes [#118](https://github.com/octalmage/robotjs/issues/118)
## <small>0.3.2 (2015-10-28)</small>
* 0.3.2 ([9d5d2e1](https://github.com/octalmage/robotjs/commit/9d5d2e1))
* Add AppVeyor badge. ([7a89376](https://github.com/octalmage/robotjs/commit/7a89376))
* Add desktop keyword. ([b1bc0ef](https://github.com/octalmage/robotjs/commit/b1bc0ef))
* Added special multimedia keycodes + restructured code ([ef4f36d](https://github.com/octalmage/robotjs/commit/ef4f36d))
* Another attempt to fix the accuracy. ([637feed](https://github.com/octalmage/robotjs/commit/637feed))
* Basic appveyor.yml. ([cfc1f78](https://github.com/octalmage/robotjs/commit/cfc1f78))
* Dynamic nan include. ([af0a9f1](https://github.com/octalmage/robotjs/commit/af0a9f1))
* Fix several differences ([523d8e1](https://github.com/octalmage/robotjs/commit/523d8e1))
* 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))
* Fixed the API link ([d5d7c5c](https://github.com/octalmage/robotjs/commit/d5d7c5c))
* Improve conversion accuracy. ([b0258ad](https://github.com/octalmage/robotjs/commit/b0258ad))
* Increase delay to help test reliability. ([66b31b6](https://github.com/octalmage/robotjs/commit/66b31b6))
* Log currentPos to help debug. ([84edc0c](https://github.com/octalmage/robotjs/commit/84edc0c))
* Log mouse positions to help debug. ([49cc03f](https://github.com/octalmage/robotjs/commit/49cc03f))
* New mouse example! ([7121302](https://github.com/octalmage/robotjs/commit/7121302))
* Remove extra newline between badges. ([db69a4f](https://github.com/octalmage/robotjs/commit/db69a4f))
* Remove logging. ([60c86d1](https://github.com/octalmage/robotjs/commit/60c86d1))
* Remove more newlines. ([0522faf](https://github.com/octalmage/robotjs/commit/0522faf))
* Remove newline after example descriptions. ([a1048b9](https://github.com/octalmage/robotjs/commit/a1048b9))
* Remove Waffle.io badge. ([3d193ce](https://github.com/octalmage/robotjs/commit/3d193ce))
* Reorganize badges. ([53adde4](https://github.com/octalmage/robotjs/commit/53adde4))
* Test the latest 4.x.x Node.js version. ([716d3a2](https://github.com/octalmage/robotjs/commit/716d3a2))
## <small>0.3.1 (2015-10-12)</small>
* 0.3.1 ([6ecb2f1](https://github.com/octalmage/robotjs/commit/6ecb2f1))
* Add some comments. ([07a6b11](https://github.com/octalmage/robotjs/commit/07a6b11))
* Allow up or down for second argument. ([718c822](https://github.com/octalmage/robotjs/commit/718c822))
* Bitmap header! ([13cf060](https://github.com/octalmage/robotjs/commit/13cf060))
* Change color to colorAt. ([b413365](https://github.com/octalmage/robotjs/commit/b413365))
* Create and use padHex function. ([547ba93](https://github.com/octalmage/robotjs/commit/547ba93))
* Create imageBuffer manually. ([2425d4a](https://github.com/octalmage/robotjs/commit/2425d4a))
* Destroy bitmap after getting hex. ([e0f616a](https://github.com/octalmage/robotjs/commit/e0f616a))
* Export captureScreen to Node.js. ([5dc3557](https://github.com/octalmage/robotjs/commit/5dc3557))
* Export getColor. ([c27054e](https://github.com/octalmage/robotjs/commit/c27054e))
* Fix key state error. ([1526cf3](https://github.com/octalmage/robotjs/commit/1526cf3))
* Fix mouseToggle ([446d40f](https://github.com/octalmage/robotjs/commit/446d40f))
* Get the correct argument. ([b39d416](https://github.com/octalmage/robotjs/commit/b39d416))
* Get the correct arguments. ([752d2b9](https://github.com/octalmage/robotjs/commit/752d2b9))
* Implemented captureScreen. #13 ([ec4f953](https://github.com/octalmage/robotjs/commit/ec4f953)), closes [#13](https://github.com/octalmage/robotjs/issues/13)
* More accurate types. ([e28cb2a](https://github.com/octalmage/robotjs/commit/e28cb2a))
* No spaces before comment. ([5e81b63](https://github.com/octalmage/robotjs/commit/5e81b63))
* Pass the buffer to createMMBitmap. ([edf9412](https://github.com/octalmage/robotjs/commit/edf9412))
* Pass x and y in with the bitmap object. ([ea719a2](https://github.com/octalmage/robotjs/commit/ea719a2))
* Re-arrange order. ([96d8547](https://github.com/octalmage/robotjs/commit/96d8547))
* Remove unnecessary printf. ([fc9df2a](https://github.com/octalmage/robotjs/commit/fc9df2a))
* RobotJS type. ([634259e](https://github.com/octalmage/robotjs/commit/634259e))
* Started getColor function. ([df737d1](https://github.com/octalmage/robotjs/commit/df737d1))
* Use nan to define strings. ([d2faaf0](https://github.com/octalmage/robotjs/commit/d2faaf0))
* Use Nan::Utf8String ([f114464](https://github.com/octalmage/robotjs/commit/f114464))
* Use x and y. ([54a9cae](https://github.com/octalmage/robotjs/commit/54a9cae))
## 0.3.0 (2015-10-04)
* __MMMouseWheelDirection is the same on all OSes, so we only need to define it once. ([534b4b6](https://github.com/octalmage/robotjs/commit/534b4b6))
* 0.3.0 ([679989c](https://github.com/octalmage/robotjs/commit/679989c))
* Accept "up" or "down" for direction. ([a3346ab](https://github.com/octalmage/robotjs/commit/a3346ab))
* Added header for doubleClick function. ([47de9e1](https://github.com/octalmage/robotjs/commit/47de9e1))
* Added header to moveMouse. ([be280d8](https://github.com/octalmage/robotjs/commit/be280d8))
* Added header to toggleMouse. ([5501e7e](https://github.com/octalmage/robotjs/commit/5501e7e))
* Added link to the blog. ([e2d20c8](https://github.com/octalmage/robotjs/commit/e2d20c8))
* Added microsleep to scrollMouse. ([351176b](https://github.com/octalmage/robotjs/commit/351176b))
* Added missing period. ([be33adc](https://github.com/octalmage/robotjs/commit/be33adc))
* Added printscreen to CheckKeyCodes. ([ae004c8](https://github.com/octalmage/robotjs/commit/ae004c8))
* Added VK_SNAPSHOT to accessible keycodes on Windows. ([61b0584](https://github.com/octalmage/robotjs/commit/61b0584))
* Changed getting started to examples ([cf7c392](https://github.com/octalmage/robotjs/commit/cf7c392))
* Commented this code out for now. ([062be3f](https://github.com/octalmage/robotjs/commit/062be3f)), closes [#50](https://github.com/octalmage/robotjs/issues/50)
* Fixed Examples link. ([21ce43c](https://github.com/octalmage/robotjs/commit/21ce43c))
* Fixed TravisCI config. ([25590a2](https://github.com/octalmage/robotjs/commit/25590a2))
* printscreen is only supported on Windows. ([09d8b21](https://github.com/octalmage/robotjs/commit/09d8b21))
* Reorganize readme ([b009e13](https://github.com/octalmage/robotjs/commit/b009e13))
* uncomment line 43 ([a65488c](https://github.com/octalmage/robotjs/commit/a65488c))
* Update README.md ([0dc62ab](https://github.com/octalmage/robotjs/commit/0dc62ab))
* Updated package.json to use nan 2.0.9. ([945663f](https://github.com/octalmage/robotjs/commit/945663f))
* updated robotjs to compile with nan-2.0.9 ([811a813](https://github.com/octalmage/robotjs/commit/811a813))
* updating travis config file ([11beae0](https://github.com/octalmage/robotjs/commit/11beae0))
* Use nan macro and fix conversion for Windows. ([e8da392](https://github.com/octalmage/robotjs/commit/e8da392))
## <small>0.2.4 (2015-08-28)</small>
* 0.2.4 ([94bdb16](https://github.com/octalmage/robotjs/commit/94bdb16))
* Define MMMouseWheelDirection for Mac. ([1725c10](https://github.com/octalmage/robotjs/commit/1725c10))
* Implemented scrollMouse for Mac. ([ded851c](https://github.com/octalmage/robotjs/commit/ded851c))
* Indent the description. ([630822c](https://github.com/octalmage/robotjs/commit/630822c))
* Linux scrollMouse implementation. ([0403623](https://github.com/octalmage/robotjs/commit/0403623))
* Make global delays configurable. #14 ([dc52bc2](https://github.com/octalmage/robotjs/commit/dc52bc2)), closes [#14](https://github.com/octalmage/robotjs/issues/14)
* MMMouseWheelDirection for Linux. ([58ff736](https://github.com/octalmage/robotjs/commit/58ff736))
* Needed for abs(); ([13a5e15](https://github.com/octalmage/robotjs/commit/13a5e15))
* Show build status for master branch. ([ce6274f](https://github.com/octalmage/robotjs/commit/ce6274f))
* Use global delays for mouse and keyboard functions. ([3cbfc50](https://github.com/octalmage/robotjs/commit/3cbfc50))
## <small>0.2.3 (2015-08-05)</small>
* 0.2.3 ([b6326bc](https://github.com/octalmage/robotjs/commit/b6326bc))
* Added detailed install instructions. ([2e9fc20](https://github.com/octalmage/robotjs/commit/2e9fc20))
* Added doubleClick function. ([f1afaab](https://github.com/octalmage/robotjs/commit/f1afaab))
* Better example titles. ([5711f7b](https://github.com/octalmage/robotjs/commit/5711f7b))
* Fixed mouseClick bug on Windows. ([291890c](https://github.com/octalmage/robotjs/commit/291890c))
* io.js v3 is not supported. ([efb0fd7](https://github.com/octalmage/robotjs/commit/efb0fd7))
* Removed the Windows specific double click. ([d2e33e1](https://github.com/octalmage/robotjs/commit/d2e33e1))
* Sleep for 200 milliseconds instead of 500. ([6e56e16](https://github.com/octalmage/robotjs/commit/6e56e16))
* Updated mouseClick to support double clicks. ([b5db4cb](https://github.com/octalmage/robotjs/commit/b5db4cb))
## <small>0.2.2 (2015-08-01)</small>
* 0.2.2 ([ec1fd14](https://github.com/octalmage/robotjs/commit/ec1fd14))
* Add spaces around assignment operator. ([c1e62de](https://github.com/octalmage/robotjs/commit/c1e62de))
* Added whitespace ([4159921](https://github.com/octalmage/robotjs/commit/4159921))
* Change "specially" to "especially" ([bc1c330](https://github.com/octalmage/robotjs/commit/bc1c330))
* Changed readme wording. ([c2f49a7](https://github.com/octalmage/robotjs/commit/c2f49a7))
* Fix for #15 ([e7983b7](https://github.com/octalmage/robotjs/commit/e7983b7)), closes [#15](https://github.com/octalmage/robotjs/issues/15)
* Fixed minor typo ([673ea16](https://github.com/octalmage/robotjs/commit/673ea16))
* MouseScroll feature addition ([4052946](https://github.com/octalmage/robotjs/commit/4052946))
* Something like this :cheers: ([c632f78](https://github.com/octalmage/robotjs/commit/c632f78))
* Update README.md ([38ec22c](https://github.com/octalmage/robotjs/commit/38ec22c))
* Update README.md ([34421b4](https://github.com/octalmage/robotjs/commit/34421b4))
* Update README.md ([2f67943](https://github.com/octalmage/robotjs/commit/2f67943))
* Update README.md ([1f2dd31](https://github.com/octalmage/robotjs/commit/1f2dd31))
* Update robotjs.cc ([fb4198c](https://github.com/octalmage/robotjs/commit/fb4198c))
## <small>0.2.1 (2015-07-26)</small>
* 0.2.1 ([b0dc3bc](https://github.com/octalmage/robotjs/commit/b0dc3bc))
* Confirm that the mouse is in the correct location. ([8081dd1](https://github.com/octalmage/robotjs/commit/8081dd1))
* Move the mouse in the moveMouse test. ([cf93363](https://github.com/octalmage/robotjs/commit/cf93363))
* Sleep after mouse events. #14 ([939518a](https://github.com/octalmage/robotjs/commit/939518a)), closes [#14](https://github.com/octalmage/robotjs/issues/14)
* Test moving the mouse. ([3043db2](https://github.com/octalmage/robotjs/commit/3043db2))
## 0.2.0 (2015-07-25)
* 0.2.0 ([cac32ba](https://github.com/octalmage/robotjs/commit/cac32ba))
* Add Windows support. ([bcc8164](https://github.com/octalmage/robotjs/commit/bcc8164))
* Added link to list of projects. ([491f675](https://github.com/octalmage/robotjs/commit/491f675))
* Fixed formatting. ([7222506](https://github.com/octalmage/robotjs/commit/7222506))
* getPixelColor bug fix. ([20c9a19](https://github.com/octalmage/robotjs/commit/20c9a19))
* Include the portable snprintf. ([fc4016d](https://github.com/octalmage/robotjs/commit/fc4016d))
* Line up arguments. ([056b6b1](https://github.com/octalmage/robotjs/commit/056b6b1))
* Make the portable snprintf C++ compatible. ([5e1dadf](https://github.com/octalmage/robotjs/commit/5e1dadf))
* No ms_stdint.h. ([12c1ff7](https://github.com/octalmage/robotjs/commit/12c1ff7))
* Remove sponsor line. ([547aa0c](https://github.com/octalmage/robotjs/commit/547aa0c))
* RobotJS is now cross platform! ([6561826](https://github.com/octalmage/robotjs/commit/6561826))
* Run the screen tests. ([64e9e4a](https://github.com/octalmage/robotjs/commit/64e9e4a))
* Tests for getPixelColor and getScreenSize. ([790c3c6](https://github.com/octalmage/robotjs/commit/790c3c6))
* These throw errors, need to come back to this. ([5bc2eb2](https://github.com/octalmage/robotjs/commit/5bc2eb2))
* Typecast from int to double. ([8730e94](https://github.com/octalmage/robotjs/commit/8730e94))
* Unnecessary and causes warnings. ([25c3b84](https://github.com/octalmage/robotjs/commit/25c3b84))
* Updated to match code style. ([1208cf8](https://github.com/octalmage/robotjs/commit/1208cf8))
* Use cross platform microsleep instead of mssleep. ([a511724](https://github.com/octalmage/robotjs/commit/a511724))
## <small>0.1.4 (2015-07-19)</small>
* 0.1.4 ([c982f0a](https://github.com/octalmage/robotjs/commit/c982f0a))
* Added space to keycode list. ([523cf3b](https://github.com/octalmage/robotjs/commit/523cf3b))
* Added space to keyTap and keyToggle. ([589ca78](https://github.com/octalmage/robotjs/commit/589ca78))
* Corrected spacebar keycode. ([e935825](https://github.com/octalmage/robotjs/commit/e935825))
* Fixed buffer overflow. Closes #19. ([1da780f](https://github.com/octalmage/robotjs/commit/1da780f)), closes [#19](https://github.com/octalmage/robotjs/issues/19)
* Updating description. ([e2541ef](https://github.com/octalmage/robotjs/commit/e2541ef))
## <small>0.1.3 (2015-07-18)</small>
* 0.1.3 ([71dceb0](https://github.com/octalmage/robotjs/commit/71dceb0))
* add framework to accept modifiers on keys. set up keytoggle. ([527f9d9](https://github.com/octalmage/robotjs/commit/527f9d9))
* Added "maintained by" line. ([fcd4c98](https://github.com/octalmage/robotjs/commit/fcd4c98))
* Added missing periods. ([431abe5](https://github.com/octalmage/robotjs/commit/431abe5))
* Added note about a sponsor. ([10a171e](https://github.com/octalmage/robotjs/commit/10a171e))
* Added space before License header. ([eb64d14](https://github.com/octalmage/robotjs/commit/eb64d14))
* Changed wording to hint that Linux is supported. ([cd38d0e](https://github.com/octalmage/robotjs/commit/cd38d0e))
* Fixed code style. ([36c5297](https://github.com/octalmage/robotjs/commit/36c5297))
* Fixed style of keyToggle. ([0ba1797](https://github.com/octalmage/robotjs/commit/0ba1797))
* fixed typo ([2a42cc3](https://github.com/octalmage/robotjs/commit/2a42cc3))
* Fixed typo ([c2f6ee0](https://github.com/octalmage/robotjs/commit/c2f6ee0))
* 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))
* get keytap working for cmd-tab, started on keytoggle ([ebd2bb4](https://github.com/octalmage/robotjs/commit/ebd2bb4))
* More spaces to tabs. ([d3f03d0](https://github.com/octalmage/robotjs/commit/d3f03d0))
* removed wiki link ([8c221d9](https://github.com/octalmage/robotjs/commit/8c221d9))
* Spaces to tabs. ([72a4e94](https://github.com/octalmage/robotjs/commit/72a4e94))
* Switched to tab. ([16cfd6c](https://github.com/octalmage/robotjs/commit/16cfd6c))
* Update README.md ([b9c57a4](https://github.com/octalmage/robotjs/commit/b9c57a4))
* Updated readme with new description. ([41b762c](https://github.com/octalmage/robotjs/commit/41b762c))
* Updating progress chart for keyboard. ([6e89410](https://github.com/octalmage/robotjs/commit/6e89410))
## <small>0.1.2 (2015-04-26)</small>
* 0.1.2 ([82df310](https://github.com/octalmage/robotjs/commit/82df310))
* A travis configuration that I reckon will work (including xvfb) ([9246dac](https://github.com/octalmage/robotjs/commit/9246dac))
* Add required apt packages ([779d238](https://github.com/octalmage/robotjs/commit/779d238))
* Add xdisplay to the compilation list for linux ([36d1951](https://github.com/octalmage/robotjs/commit/36d1951))
* Added build status. ([30f753d](https://github.com/octalmage/robotjs/commit/30f753d))
* Added compiler warnings as per autopy ([b04a3bf](https://github.com/octalmage/robotjs/commit/b04a3bf))
* Added getScreenSize, closes #25. ([d94ca48](https://github.com/octalmage/robotjs/commit/d94ca48)), closes [#25](https://github.com/octalmage/robotjs/issues/25)
* Added link to wiki. ([7ad2129](https://github.com/octalmage/robotjs/commit/7ad2129))
* Added linux link settings ([7e705e2](https://github.com/octalmage/robotjs/commit/7e705e2))
* Added Tab key to keyTap ([d1e7b01](https://github.com/octalmage/robotjs/commit/d1e7b01))
* MIT bitches. ([f415f54](https://github.com/octalmage/robotjs/commit/f415f54))
* pandering to my OCD - sorry :/ ([099a21d](https://github.com/octalmage/robotjs/commit/099a21d))
* Reformat to use single quotes as that seems to be the standard for gyp ([b70a67c](https://github.com/octalmage/robotjs/commit/b70a67c))
* shadow too noisy when working with V8 ([4cdeb86](https://github.com/octalmage/robotjs/commit/4cdeb86))
* Some basic tests to get the ball rolling ([86714bf](https://github.com/octalmage/robotjs/commit/86714bf))
* This was suppose to be MIT. ([aca285b](https://github.com/octalmage/robotjs/commit/aca285b))
* Updated progress chart. ([1da2e14](https://github.com/octalmage/robotjs/commit/1da2e14))
* Use tape for testing ([4f4b043](https://github.com/octalmage/robotjs/commit/4f4b043))
## <small>0.1.1 (2015-02-06)</small>
* Added additional keys for keyTap. ([1895133](https://github.com/octalmage/robotjs/commit/1895133))
* Fixed extern location. ([581cbdc](https://github.com/octalmage/robotjs/commit/581cbdc))
* Updated keyTap example. ([7d056e8](https://github.com/octalmage/robotjs/commit/7d056e8))
* Updated keyTap for correct use. Closes #3. ([ec8adb9](https://github.com/octalmage/robotjs/commit/ec8adb9)), closes [#3](https://github.com/octalmage/robotjs/issues/3)
* Updated to version 0.1.1. ([764dd11](https://github.com/octalmage/robotjs/commit/764dd11))
## 0.1.0 (2015-02-04)
* Add periods to all errors. ([b11648a](https://github.com/octalmage/robotjs/commit/b11648a))
* Added mouseToggle, closes #8. ([3f4918b](https://github.com/octalmage/robotjs/commit/3f4918b)), closes [#8](https://github.com/octalmage/robotjs/issues/8)
* Added right/middle click support, closes #6. ([2fc100a](https://github.com/octalmage/robotjs/commit/2fc100a)), closes [#6](https://github.com/octalmage/robotjs/issues/6)
* Changed button variable name. ([6113ec5](https://github.com/octalmage/robotjs/commit/6113ec5))
* Uniform syntax. ([ca191c1](https://github.com/octalmage/robotjs/commit/ca191c1))
* Version 0.1.0. ([245a862](https://github.com/octalmage/robotjs/commit/245a862))
## <small>0.0.5 (2015-02-04)</small>
* Added additional examples. ([ba5cb80](https://github.com/octalmage/robotjs/commit/ba5cb80))
* Added getPixelColor. ([f250355](https://github.com/octalmage/robotjs/commit/f250355))
* Added more badges. ([39ef2f7](https://github.com/octalmage/robotjs/commit/39ef2f7))
* Added mouseMoveSmooth. ([ecdd16a](https://github.com/octalmage/robotjs/commit/ecdd16a))
* Added waffle.io badge! ([1488a75](https://github.com/octalmage/robotjs/commit/1488a75))
* Converted mouseClick function. ([87fe58e](https://github.com/octalmage/robotjs/commit/87fe58e))
* Converted rest of code to nan. ([d44b1a2](https://github.com/octalmage/robotjs/commit/d44b1a2))
* Converted typeString to use nan. Closes #1. ([ea62efa](https://github.com/octalmage/robotjs/commit/ea62efa)), closes [#1](https://github.com/octalmage/robotjs/issues/1)
* Fixed code style. ([de175e7](https://github.com/octalmage/robotjs/commit/de175e7))
* Fixed heading syntax. ([d2102c8](https://github.com/octalmage/robotjs/commit/d2102c8))
* Fixed plans. ([17d7f24](https://github.com/octalmage/robotjs/commit/17d7f24))
* Include screen related code. ([ea69110](https://github.com/octalmage/robotjs/commit/ea69110))
* Link to syntax issue. ([9b8b765](https://github.com/octalmage/robotjs/commit/9b8b765))
* Linked to nan issue. ([9f7cdba](https://github.com/octalmage/robotjs/commit/9f7cdba))
* nan rewrite done! ([f217e9a](https://github.com/octalmage/robotjs/commit/f217e9a))
* Removed node_modules. ([b4f2085](https://github.com/octalmage/robotjs/commit/b4f2085))
* Removed unused functions. ([c9509bf](https://github.com/octalmage/robotjs/commit/c9509bf))
* Removed unused helper functions. ([6f60b53](https://github.com/octalmage/robotjs/commit/6f60b53))
* Updated to be compatible with C++. ([cacd8f9](https://github.com/octalmage/robotjs/commit/cacd8f9))
* Updated version to 0.0.4. ([9f97237](https://github.com/octalmage/robotjs/commit/9f97237))
* Updated version to 0.0.5. ([e981548](https://github.com/octalmage/robotjs/commit/e981548))
## <small>0.0.3 (2015-01-20)</small>
* Added build instructions. ([4a9e8c6](https://github.com/octalmage/robotjs/commit/4a9e8c6))
* Added keyTap and typeString, started on bitmap. ([8cb04e5](https://github.com/octalmage/robotjs/commit/8cb04e5))
* Added more details about plans. ([97ead45](https://github.com/octalmage/robotjs/commit/97ead45))
* Added nan module. ([bcf1111](https://github.com/octalmage/robotjs/commit/bcf1111))
* Added note about progress. ([c5da2d2](https://github.com/octalmage/robotjs/commit/c5da2d2))
* Added story section. ([0ff5407](https://github.com/octalmage/robotjs/commit/0ff5407))
* Added Window module to progress table. ([b9da81d](https://github.com/octalmage/robotjs/commit/b9da81d))
* Cleaning up the code, moving window code to new branch. ([16f67af](https://github.com/octalmage/robotjs/commit/16f67af))
* Excluding build/ ([5ba0e11](https://github.com/octalmage/robotjs/commit/5ba0e11))
* Experimenting with Window manipulation. ([ea34961](https://github.com/octalmage/robotjs/commit/ea34961))
* Including nan. ([1f407d8](https://github.com/octalmage/robotjs/commit/1f407d8))
* Moved screen related functions to it's own branch. ([2b4564d](https://github.com/octalmage/robotjs/commit/2b4564d))
* New release. ([0361eb4](https://github.com/octalmage/robotjs/commit/0361eb4))
* Removed screen related files from build. ([919fc80](https://github.com/octalmage/robotjs/commit/919fc80))
* Started converting to nan. ([19261d7](https://github.com/octalmage/robotjs/commit/19261d7))
* Update README.md ([cbeb5af](https://github.com/octalmage/robotjs/commit/cbeb5af))
* Updated dependencies to include nan. ([106788b](https://github.com/octalmage/robotjs/commit/106788b))
* Updated install instructions. ([1d1d841](https://github.com/octalmage/robotjs/commit/1d1d841))
* Updated package version. ([d9b1d98](https://github.com/octalmage/robotjs/commit/d9b1d98))
* Updating progress. ([29a293d](https://github.com/octalmage/robotjs/commit/29a293d))
## <small>0.0.2 (2014-09-01)</small>
* Changed name to RobotJS. This is final! ([9651d26](https://github.com/octalmage/robotjs/commit/9651d26))
* Fixed install instructions. ([8a94cc6](https://github.com/octalmage/robotjs/commit/8a94cc6))
* Published to NPM! ([1401e63](https://github.com/octalmage/robotjs/commit/1401e63))
* Updated README.md ([5e05785](https://github.com/octalmage/robotjs/commit/5e05785))
## <small>0.0.1 (2014-09-01)</small>
* Added build instructions. ([98ef54e](https://github.com/octalmage/robotjs/commit/98ef54e))
* Added mouseClick function. ([e4ba3c0](https://github.com/octalmage/robotjs/commit/e4ba3c0))
* Added progress. ([7f2090d](https://github.com/octalmage/robotjs/commit/7f2090d))
* Create README.md ([d78304b](https://github.com/octalmage/robotjs/commit/d78304b))
* Getting ready for NPM. ([33f7d82](https://github.com/octalmage/robotjs/commit/33f7d82))
* Got some basic mouse functions working. ([d84e253](https://github.com/octalmage/robotjs/commit/d84e253))
* Initial commit. ([38c1700](https://github.com/octalmage/robotjs/commit/38c1700))
* Moved some files around. ([aad9e6d](https://github.com/octalmage/robotjs/commit/aad9e6d))
* Removed unnecessary files. ([d2f1a3d](https://github.com/octalmage/robotjs/commit/d2f1a3d))
* Update README.md ([4cee4c5](https://github.com/octalmage/robotjs/commit/4cee4c5))
================================================
FILE: LICENSE.md
================================================
Copyright (c) 2014 Jason Stallings
Permission 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:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE 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.
================================================
FILE: README.md
================================================
<p align="center"><img src="https://cldup.com/1ATDf2JMtv.png"></p>
> Node.js Desktop Automation. Control the mouse, keyboard, and read the screen.
RobotJS supports Mac, [Windows](https://github.com/octalmage/robotjs/issues/2), and [Linux](https://github.com/octalmage/robotjs/issues/17).
This 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)
[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!
## Contents
- [Installation](#installation)
- [Examples](#examples)
- [API](https://robotjs.dev/docs/syntax)
- [Building](#building)
- [Plans](#plans)
- [Progress](#progress)
- [FAQ](#faq)
- [License](#license)
## Installation
Install RobotJS using npm:
```
npm install robotjs
```
It's that easy! npm will download one of the prebuilt [binaries](https://github.com/octalmage/robotjs/releases/latest) for your OS.
You can get npm [here](https://nodejs.org/en/download/) if you don't have it installed.
If you need to build RobotJS, see the [building](#building) section. Instructions for [Electron](https://github.com/octalmage/robotjs/wiki/Electron).
## Examples
##### [Mouse](https://github.com/octalmage/robotjs/wiki/Syntax#mouse)
<p align="center"><img src="https://cldup.com/lugVjjAkEi.gif"></p>
```JavaScript
// Move the mouse across the screen as a sine wave.
var robot = require("robotjs");
// Speed up the mouse.
robot.setMouseDelay(2);
var twoPI = Math.PI * 2.0;
var screenSize = robot.getScreenSize();
var height = (screenSize.height / 2) - 10;
var width = screenSize.width;
for (var x = 0; x < width; x++)
{
y = height * Math.sin((twoPI * x) / width) + height;
robot.moveMouse(x, y);
}
```
##### [Keyboard](https://github.com/octalmage/robotjs/wiki/Syntax#keyboard)
```JavaScript
// Type "Hello World" then press enter.
var robot = require("robotjs");
// Type "Hello World".
robot.typeString("Hello World");
// Press enter.
robot.keyTap("enter");
```
##### [Screen](https://github.com/octalmage/robotjs/wiki/Syntax#screen)
```JavaScript
// Get pixel color under the mouse.
var robot = require("robotjs");
// Get mouse position.
var mouse = robot.getMousePos();
// Get pixel color in hex format.
var hex = robot.getPixelColor(mouse.x, mouse.y);
console.log("#" + hex + " at x:" + mouse.x + " y:" + mouse.y);
```
Read the [Wiki](https://github.com/octalmage/robotjs/wiki) for more information!
## [API](http://robotjs.dev/docs/syntax)
The RobotJS API is hosted at <https://robotjs.dev/docs/syntax>.
## Building
Please ensure you have the required dependencies before installing:
* Windows
* windows-build-tools npm package (`npm install --global --production windows-build-tools` from an elevated PowerShell or CMD.exe)
* Mac
* Xcode Command Line Tools.
* Linux
* Python (v2.7 recommended, v3.x.x is not supported).
* make.
* A C/C++ compiler like GCC.
* libxtst-dev and libpng++-dev (`sudo apt-get install libxtst-dev libpng++-dev`).
Install node-gyp using npm:
```
npm install -g node-gyp
```
Then build:
```
node-gyp rebuild
```
See the [node-gyp readme](https://github.com/nodejs/node-gyp#installation) for more details.
## Plans
* √ Control the mouse by changing the mouse position, left/right clicking, and dragging.
* √ Control the keyboard by pressing keys, holding keys down, and typing words.
* √ Read pixel color from the screen and capture the screen.
* Find an image on screen, read pixels from an image.
* Possibly include window management?
## Progress
| Module | Status | Notes |
| ------------- |-------------: | ------- |
| Mouse | 100% | All planned features implemented. |
| Keyboard | 100% | All planned features implemented. |
| Screen | 85% | Image search, pixel search. |
| Bitmap | 0% | Saving/opening, png support. |
## FAQ
#### Does RobotJS support global hotkeys?
Not 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.
#### Can I take a screenshot with RobotJS?
Soon! 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.
#### Why is <insert key> missing from the keyboard functions?
We've been implementing keys as we need them. Feel free to create an issue or submit a pull request!
#### How about multi-monitor support?
The 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.
For any other questions please [submit an issue](https://github.com/octalmage/robotjs/issues/new).
## Story
I'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.
I 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.
**TLDR:** There's nothing like AutoHotkey on Mac, so I'm making it.
## License
MIT
Based on [autopy](https://github.com/msanders/autopy).
Maintained by [Jason Stallings](http://jason.stallin.gs).
================================================
FILE: appveyor.yml
================================================
# http://www.appveyor.com/docs/appveyor-yml
# Test against these versions of Io.js and Node.js.
environment:
matrix:
# node.js
- nodejs_version: "10"
- nodejs_version: "12"
- nodejs_version: "14"
- nodejs_version: "Stable"
cache:
- node_modules
clone_depth: 5
platform:
- x86
- x64
# Install scripts. (runs after repo cloning)
install:
# Get the latest stable version of Node 0.STABLE.latest
- ps: Install-Product node $env:nodejs_version
- npm -g install npm
- set PATH=%APPDATA%\npm;%PATH%
# Typical npm stuff.
- npm install
test_script:
# Output useful info for debugging.
- node --version
- npm --version
# run tests
- npm test
on_success:
- IF defined APPVEYOR_REPO_TAG_NAME npm run prebuild -- -u %GITHUB_TOKEN%
build: off
version: "{build}"
================================================
FILE: binding.gyp
================================================
{
'targets': [{
'target_name': 'robotjs',
'cflags!': [ '-fno-exceptions' ],
'cflags_cc!': [ '-fno-exceptions' ],
'xcode_settings': { 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
'CLANG_CXX_LIBRARY': 'libc++',
'MACOSX_DEPLOYMENT_TARGET': '10.15',
},
'msvs_settings': {
'VCCLCompilerTool': { 'ExceptionHandling': 1 },
},
'include_dirs': [
'<!(node -p "require(\'node-addon-api\').include_dir")',
],
'conditions': [
['OS == "mac"', {
'include_dirs': [
'<!(node -p "require(\'node-addon-api\').include_dir")',
'System/Library/Frameworks/CoreFoundation.Framework/Headers',
'System/Library/Frameworks/Carbon.Framework/Headers',
'System/Library/Frameworks/ApplicationServices.framework/Headers',
'System/Library/Frameworks/OpenGL.framework/Headers',
],
'link_settings': {
'libraries': [
'-framework Carbon',
'-framework CoreFoundation',
'-framework ApplicationServices',
'-framework OpenGL'
]
}
}],
['OS == "linux"', {
'link_settings': {
'libraries': [
'-lpng',
'-lz',
'-lX11',
'-lXtst'
]
},
'sources': [
'src/xdisplay.c'
]
}],
["OS=='win'", {
'defines': ['IS_WINDOWS']
}]
],
'sources': [
'src/robotjs.cc',
'src/deadbeef_rand.c',
'src/mouse.c',
'src/keypress.c',
'src/keycode.c',
'src/screen.c',
'src/screengrab.c',
'src/snprintf.c',
'src/MMBitmap.c'
]
}]
}
================================================
FILE: index.d.ts
================================================
export interface Bitmap {
width: number
height: number
image: any
byteWidth: number
bitsPerPixel: number
bytesPerPixel: number
colorAt(x: number, y: number): string
}
export interface Screen {
capture(x?: number, y?: number, width?: number, height?: number): Bitmap
}
export function setKeyboardDelay(ms: number) : void
export function keyTap(key: string, modifier?: string | string[]) : void
export function keyToggle(key: string, down: string, modifier?: string | string[]) : void
export function unicodeTap(value: number) : void
export function typeString(string: string) : void
export function typeStringDelayed(string: string, cpm: number) : void
export function setMouseDelay(delay: number) : void
export function updateScreenMetrics() : void
export function moveMouse(x: number, y: number) : void
export function moveMouseSmooth(x: number, y: number,speed?:number) : void
export function mouseClick(button?: string, double?: boolean) : void
export function mouseToggle(down?: string, button?: string) : void
export function dragMouse(x: number, y: number) : void
export function scrollMouse(x: number, y: number) : void
export function getMousePos(): { x: number, y: number }
export function getPixelColor(x: number, y: number): string
export function getScreenSize(): { width: number, height: number }
export var screen: Screen
================================================
FILE: index.js
================================================
var robotjs = require('./build/Release/robotjs.node');
module.exports = robotjs;
module.exports.screen = {};
function bitmap(width, height, byteWidth, bitsPerPixel, bytesPerPixel, image)
{
this.width = width;
this.height = height;
this.byteWidth = byteWidth;
this.bitsPerPixel = bitsPerPixel;
this.bytesPerPixel = bytesPerPixel;
this.image = image;
this.colorAt = function(x, y)
{
return robotjs.getColor(this, x, y);
};
}
module.exports.screen.capture = function(x, y, width, height)
{
//If coords have been passed, use them.
if (typeof x !== "undefined" && typeof y !== "undefined" && typeof width !== "undefined" && typeof height !== "undefined")
{
b = robotjs.captureScreen(x, y, width, height);
}
else
{
b = robotjs.captureScreen();
}
return new bitmap(b.width, b.height, b.byteWidth, b.bitsPerPixel, b.bytesPerPixel, b.image);
};
================================================
FILE: package.json
================================================
{
"name": "robotjs",
"version": "0.7.0",
"description": "Node.js Desktop Automation.",
"main": "index.js",
"typings": "index.d.ts",
"scripts": {
"test": "run-script-os",
"test:darwin:linux": "jasmine test/**/*.js",
"test-keyboard": "node test/keyboard.js",
"test:win32": "jasmine test/**/*.js",
"install": "prebuild-install --runtime napi || node-gyp rebuild",
"install-debug": "prebuild-install --runtime napi || node-gyp rebuild --debug",
"prebuild": "prebuild --all --runtime napi",
"build": "node-gyp rebuild"
},
"repository": {
"type": "git",
"url": "https://github.com/octalmage/robotjs.git"
},
"keywords": [
"Automation",
"GUI",
"mouse",
"keyboard",
"screenshot",
"image",
"pixel",
"desktop",
"robotjs",
"screen",
"recognition",
"autohotkey",
"machine",
"learning",
"color"
],
"author": "Jason Stallings",
"license": "MIT",
"gypfile": true,
"binary": {
"napi_versions": [
3
]
},
"bugs": {
"url": "https://github.com/octalmage/robotjs/issues"
},
"homepage": "https://github.com/octalmage/robotjs",
"dependencies": {
"node-addon-api": "^4.2.0",
"prebuild-install": "^5.3.3"
},
"devDependencies": {
"jasmine": "^2.99.0",
"node-gyp": "^12.2.0",
"prebuild": "^13",
"run-script-os": "^1.0.3",
"tape": "^4.8.0"
}
}
================================================
FILE: src/MMBitmap.c
================================================
#include "MMBitmap.h"
#include <assert.h>
#include <string.h>
MMBitmapRef createMMBitmap(uint8_t *buffer,
size_t width,
size_t height,
size_t bytewidth,
uint8_t bitsPerPixel,
uint8_t bytesPerPixel)
{
MMBitmapRef bitmap = malloc(sizeof(MMBitmap));
if (bitmap == NULL) return NULL;
bitmap->imageBuffer = buffer;
bitmap->width = width;
bitmap->height = height;
bitmap->bytewidth = bytewidth;
bitmap->bitsPerPixel = bitsPerPixel;
bitmap->bytesPerPixel = bytesPerPixel;
return bitmap;
}
void destroyMMBitmap(MMBitmapRef bitmap)
{
assert(bitmap != NULL);
if (bitmap->imageBuffer != NULL) {
free(bitmap->imageBuffer);
bitmap->imageBuffer = NULL;
}
free(bitmap);
}
void destroyMMBitmapBuffer(char * bitmapBuffer, void * hint)
{
if (bitmapBuffer != NULL)
{
free(bitmapBuffer);
}
}
MMBitmapRef copyMMBitmap(MMBitmapRef bitmap)
{
uint8_t *copiedBuf = NULL;
assert(bitmap != NULL);
if (bitmap->imageBuffer != NULL) {
const size_t bufsize = bitmap->height * bitmap->bytewidth;
copiedBuf = malloc(bufsize);
if (copiedBuf == NULL) return NULL;
memcpy(copiedBuf, bitmap->imageBuffer, bufsize);
}
return createMMBitmap(copiedBuf,
bitmap->width,
bitmap->height,
bitmap->bytewidth,
bitmap->bitsPerPixel,
bitmap->bytesPerPixel);
}
MMBitmapRef copyMMBitmapFromPortion(MMBitmapRef source, MMRect rect)
{
assert(source != NULL);
if (source->imageBuffer == NULL || !MMBitmapRectInBounds(source, rect)) {
return NULL;
} else {
uint8_t *copiedBuf = NULL;
const size_t bufsize = rect.size.height * source->bytewidth;
const size_t offset = (source->bytewidth * rect.origin.y) +
(rect.origin.x * source->bytesPerPixel);
/* Don't go over the bounds, programmer! */
assert((bufsize + offset) <= (source->bytewidth * source->height));
copiedBuf = malloc(bufsize);
if (copiedBuf == NULL) return NULL;
memcpy(copiedBuf, source->imageBuffer + offset, bufsize);
return createMMBitmap(copiedBuf,
rect.size.width,
rect.size.height,
source->bytewidth,
source->bitsPerPixel,
source->bytesPerPixel);
}
}
================================================
FILE: src/MMBitmap.h
================================================
#pragma once
#ifndef MMBITMAP_H
#define MMBITMAP_H
#include "types.h"
#include "rgb.h"
#include <assert.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C"
{
#endif
struct _MMBitmap {
uint8_t *imageBuffer; /* Pixels stored in Quad I format; i.e., origin is in
* top left. Length should be height * bytewidth. */
size_t width; /* Never 0, unless image is NULL. */
size_t height; /* Never 0, unless image is NULL. */
size_t bytewidth; /* The aligned width (width + padding). */
uint8_t bitsPerPixel; /* Should be either 24 or 32. */
uint8_t bytesPerPixel; /* For convenience; should be bitsPerPixel / 8. */
};
typedef struct _MMBitmap MMBitmap;
typedef MMBitmap *MMBitmapRef;
/* Creates new MMBitmap with the given values.
* Follows the Create Rule (caller is responsible for destroy()'ing object). */
MMBitmapRef createMMBitmap(uint8_t *buffer, size_t width, size_t height,
size_t bytewidth, uint8_t bitsPerPixel,
uint8_t bytesPerPixel);
/* Releases memory occupied by MMBitmap. */
void destroyMMBitmap(MMBitmapRef bitmap);
/* Releases memory occupied by MMBitmap. Acts via CallBack method*/
void destroyMMBitmapBuffer(char * bitmapBuffer, void * hint);
/* Returns copy of MMBitmap, to be destroy()'d by caller. */
MMBitmapRef copyMMBitmap(MMBitmapRef bitmap);
/* Returns copy of one MMBitmap juxtaposed in another (to be destroy()'d
* by the caller.), or NULL on error. */
MMBitmapRef copyMMBitmapFromPortion(MMBitmapRef source, MMRect rect);
#define MMBitmapPointInBounds(image, p) ((p).x < (image)->width && \
(p).y < (image)->height)
#define MMBitmapRectInBounds(image, r) \
(((r).origin.x + (r).size.width <= (image)->width) && \
((r).origin.y + (r).size.height <= (image)->height))
#define MMBitmapGetBounds(image) MMRectMake(0, 0, image->width, image->height)
/* Get pointer to pixel of MMBitmapRef. No bounds checking is performed (check
* yourself before calling this with MMBitmapPointInBounds(). */
#define MMRGBColorRefAtPoint(image, x, y) \
(MMRGBColor *)(assert(MMBitmapPointInBounds(bitmap, MMPointMake(x, y))), \
((image)->imageBuffer) + (((image)->bytewidth * (y)) \
+ ((x) * (image)->bytesPerPixel)))
/* Dereference pixel of MMBitmapRef. Again, no bounds checking is performed. */
#define MMRGBColorAtPoint(image, x, y) *MMRGBColorRefAtPoint(image, x, y)
/* Hex/integer value of color at point. */
#define MMRGBHexAtPoint(image, x, y) \
hexFromMMRGB(MMRGBColorAtPoint(image, x, y))
/* Increment either point.x or point.y depending on the position of point.x.
* That is, if x + 1 is >= width, increment y and start x at the beginning.
* Otherwise, increment x.
*
* This is used as a convenience macro to scan rows when calling functions such
* as findColorInRectAt() and findBitmapInBitmapAt(). */
#define ITER_NEXT_POINT(pixel, width, start_x) \
do { \
if (++(pixel).x >= (width)) { \
(pixel).x = start_x; \
++(point).y; \
} \
} while (0);
#ifdef __cplusplus
}
#endif
#endif /* MMBITMAP_H */
================================================
FILE: src/MMPointArray.c
================================================
#include "MMPointArray.h"
#include <stdlib.h>
MMPointArrayRef createMMPointArray(size_t initialCount)
{
MMPointArrayRef pointArray = calloc(1, sizeof(MMPointArray));
if (initialCount == 0) initialCount = 1;
pointArray->_allocedCount = initialCount;
pointArray->array = malloc(pointArray->_allocedCount * sizeof(MMPoint));
if (pointArray->array == NULL) return NULL;
return pointArray;
}
void destroyMMPointArray(MMPointArrayRef pointArray)
{
if (pointArray->array != NULL) {
free(pointArray->array);
pointArray->array = NULL;
}
free(pointArray);
}
void MMPointArrayAppendPoint(MMPointArrayRef pointArray, MMPoint point)
{
const size_t newCount = ++(pointArray->count);
if (pointArray->_allocedCount < newCount) {
do {
/* Double size each time to avoid calls to realloc(). */
pointArray->_allocedCount <<= 1;
} while (pointArray->_allocedCount < newCount);
pointArray->array = realloc(pointArray->array,
sizeof(point) *
pointArray->_allocedCount);
}
pointArray->array[pointArray->count - 1] = point;
}
================================================
FILE: src/MMPointArray.h
================================================
#pragma once
#ifndef MMARRAY_H
#define MMARRAY_H
#include "types.h"
struct _MMPointArray {
MMPoint *array; /* Pointer to actual data. */
size_t count; /* Number of elements in array. */
size_t _allocedCount; /* Private; do not use outside of MMPointArray.c. */
};
typedef struct _MMPointArray MMPointArray;
typedef MMPointArray *MMPointArrayRef;
/* Creates array of an initial size (the maximum size is still limitless).
* This follows the "Create" Rule; i.e., responsibility for "destroying" the
* array is given to the caller. */
MMPointArrayRef createMMPointArray(size_t initialCount);
/* Frees memory occupied by |pointArray|. Does not accept NULL. */
void destroyMMPointArray(MMPointArrayRef pointArray);
/* Appends a point to an array, increasing the internal size if necessary. */
void MMPointArrayAppendPoint(MMPointArrayRef pointArray, MMPoint point);
/* Retrieve point from array. */
#define MMPointArrayGetItem(a, i) ((a)->array)[i]
/* Set point in array. */
#define MMPointArraySetItem(a, i, item) ((a)->array[i] = item)
#endif /* MMARRAY_H */
================================================
FILE: src/UTHashTable.c
================================================
#include "UTHashTable.h"
#include <stdlib.h>
#include <assert.h>
/* Base struct class (all nodes must contain at least the elements in
* this struct). */
struct _UTHashNode {
UTHashNode_HEAD
};
typedef struct _UTHashNode UTHashNode;
void initHashTable(UTHashTable *table, size_t initialCount, size_t nodeSize)
{
assert(table != NULL);
assert(nodeSize >= sizeof(UTHashNode));
table->uttable = NULL; /* Must be set to NULL for uthash. */
table->allocedNodeCount = (initialCount == 0) ? 1 : initialCount;
table->nodeCount = 0;
table->nodeSize = nodeSize;
table->nodes = calloc(table->nodeSize, nodeSize * table->allocedNodeCount);
}
void destroyHashTable(UTHashTable *table)
{
UTHashNode *uttable = table->uttable;
UTHashNode *node;
/* Let uthash do its magic. */
while (uttable != NULL) {
node = uttable; /* Grab pointer to first item. */
HASH_DEL(uttable, node); /* Delete it (table advances to next). */
}
/* Only giant malloc'd block containing each node must be freed. */
if (table->nodes != NULL) free(table->nodes);
table->uttable = table->nodes = NULL;
}
void *getNewNode(UTHashTable *table)
{
/* Increment node count, resizing table if necessary. */
const size_t newNodeCount = ++(table->nodeCount);
if (table->allocedNodeCount < newNodeCount) {
do {
/* Double size each time to avoid calls to realloc(). */
table->allocedNodeCount <<= 1;
} while (table->allocedNodeCount < newNodeCount);
table->nodes = realloc(table->nodes, table->nodeSize *
table->allocedNodeCount);
}
return (char *)table->nodes + (table->nodeSize * (table->nodeCount - 1));
}
================================================
FILE: src/UTHashTable.h
================================================
#pragma once
#ifndef UTHASHTABLE_H
#define UTHASHTABLE_H
#include <stddef.h>
#include "uthash.h"
/* All node structs must begin with this (note that there is NO semicolon). */
#define UTHashNode_HEAD UT_hash_handle hh;
/* This file contains convenience macros and a standard struct for working with
* uthash hash tables.
*
* The main purpose of this is for convenience of creating/freeing nodes. */
struct _UTHashTable {
void *uttable; /* The uthash table -- must start out as NULL. */
void *nodes; /* Contiguous array of nodes. */
size_t allocedNodeCount; /* Node count currently allocated for. */
size_t nodeCount; /* Current node count. */
size_t nodeSize; /* Size of each node. */
};
typedef struct _UTHashTable UTHashTable;
/* Initiates a hash table to the default values. |table| should point to an
* already allocated UTHashTable struct.
*
* If the |initialCount| argument in initHashTable is given, |nodes| is
* allocated immediately to the maximum size and new nodes are simply slices of
* that array. This can save calls to malloc if many nodes are to be added, and
* the a reasonable maximum number is known ahead of time.
*
* If the node count goes over this maximum, or if |initialCount| is 0, the
* array is dynamically reallocated to fit the size.
*/
void initHashTable(UTHashTable *table, size_t initialCount, size_t nodeSize);
/* Frees memory occupied by a UTHashTable's members.
*
* Note that this does NOT free memory for the UTHashTable pointed to by
* |table| itself; if that was allocated on the heap, you must free() it
* yourself after calling this. */
void destroyHashTable(UTHashTable *table);
/* Returns memory allocated for a new node. Responsibility for freeing this is
* up to the destroyHashTable() macro; this should NOT be freed by the caller.
*
* This is intended to be used with a HASH_ADD() macro, e.g.:
* {%
* struct myNode *uttable = utHashTable->uttable;
* struct myNode *node = getNewNode(utHashTable);
* node->key = 42;
* node->value = someValue;
* HASH_ADD_INT(uttable, key, node);
* utHashTable->uttable = uttable;
* %}
*
* Or, use the UTHASHTABLE_ADD_INT or UTHASHTABLE_ADD_STR macros
* for convenience (they are exactly equivalent):
* {%
* struct myNode *node = getNewNode(utHashTable);
* node->key = 42;
* node->value = someValue;
* UTHASHTABLE_ADD_INT(utHashTable, key, node, struct myNode);
* %}
*/
void *getNewNode(UTHashTable *table);
#define UTHASHTABLE_ADD_INT(tablePtr, keyName, node, nodeType) \
do { \
nodeType *uttable = (tablePtr)->uttable; \
HASH_ADD_INT(uttable, keyName, node); \
(tablePtr)->uttable = uttable; \
} while (0)
#define UTHASHTABLE_ADD_STR(tablePtr, keyName, node, nodeType) \
do { \
nodeType *uttable = (tablePtr)->uttable; \
HASH_ADD_STR(uttable, keyName, node); \
(tablePtr)->uttable = uttable; \
} while (0)
#endif /* MMHASHTABLE_H */
================================================
FILE: src/alert.c
================================================
#include "alert.h"
#include "os.h"
#include <assert.h>
#if defined(IS_MACOSX)
#include <CoreFoundation/CoreFoundation.h>
#elif defined(USE_X11)
#include <stdio.h> /* For fputs() */
#include <stdlib.h> /* For exit() */
#include <sys/wait.h> /* For wait() */
#include <unistd.h> /* For fork() */
#include <sys/types.h> /* For pid_t */
#include "snprintf.h" /* For asprintf() */
#endif
#if defined(USE_X11)
enum {
TASK_SUCCESS = 0,
FORK_FAILED = -1,
EXEC_FAILED = -2
};
/*
* Unfortunately, X has no standard method of displaying alerts, so instead we
* have to rely on the shell command "xmessage" (or nicer-looking equivalents).
*
* The return value and arguments are the same as those from to runTask()
* (see below).
*/
static int xmessage(char *argv[], int *exit_status);
#elif defined(IS_MACOSX)
#define CFStringCreateWithUTF8String(string) \
((string) == NULL ? NULL : CFStringCreateWithCString(NULL, \
string, \
kCFStringEncodingUTF8))
#endif
int showAlert(const char *title, const char *msg, const char *defaultButton,
const char *cancelButton)
{
#if defined(IS_MACOSX)
CFStringRef alertHeader = CFStringCreateWithUTF8String(title);
CFStringRef alertMessage = CFStringCreateWithUTF8String(msg);
CFStringRef defaultButtonTitle = CFStringCreateWithUTF8String(defaultButton);
CFStringRef cancelButtonTitle = CFStringCreateWithUTF8String(cancelButton);
CFOptionFlags responseFlags;
SInt32 err = CFUserNotificationDisplayAlert(0.0,
kCFUserNotificationNoteAlertLevel,
NULL,
NULL,
NULL,
alertHeader,
alertMessage,
defaultButtonTitle,
cancelButtonTitle,
NULL,
&responseFlags);
if (alertHeader != NULL) CFRelease(alertHeader);
if (alertMessage != NULL) CFRelease(alertMessage);
if (defaultButtonTitle != NULL) CFRelease(defaultButtonTitle);
if (cancelButtonTitle != NULL) CFRelease(cancelButtonTitle);
if (err != 0) return -1;
return (responseFlags == kCFUserNotificationDefaultResponse) ? 0 : 1;
#elif defined(USE_X11)
/* Note that args[0] is set by the xmessage() function. */
const char *args[10] = {NULL, msg, "-title", title, "-center"};
int response, ret;
char *buttonList = NULL; /* To be free()'d. */
if (defaultButton == NULL) defaultButton = "OK";
if (cancelButton == NULL) {
asprintf(&buttonList, "%s:2", defaultButton);
} else {
asprintf(&buttonList, "%s:2,%s:3", defaultButton, cancelButton);
}
if (buttonList == NULL) return -1; /* asprintf() failed. */
args[5] = "-buttons";
args[6] = buttonList;
args[7] = "-default";
args[8] = defaultButton;
args[9] = NULL;
ret = xmessage((char **)args, &response);
if (buttonList != NULL) {
free(buttonList);
buttonList = NULL;
}
if (ret != TASK_SUCCESS) {
if (ret == EXEC_FAILED) {
fputs("xmessage or equivalent not found.\n", stderr);
}
return -1;
}
return (response == 2) ? 0 : 1;
#else
/* TODO: Display custom buttons instead of the pre-defined "OK"
* and "Cancel". */
int response = MessageBox(NULL, msg, title,
(cancelButton == NULL) ? MB_OK : MB_OKCANCEL);
return (response == IDOK) ? 0 : 1;
#endif
}
#if defined(USE_X11)
/*
* Attempts to run the given task synchronously with the given arguments.
*
* If |exit_status| is non-NULL and the task ran successfully, |exit_status| is
* set to the exit code of the task on return.
*
* Returns -1 if process could not be forked, -2 if the task could not be run,
* or 0 if the task was ran successfully.
*/
static int runTask(const char *taskname, char * const argv[], int *exit_status);
static int xmessage(char *argv[], int *exit_status)
{
static const char * const MSG_PROGS[] = {"gmessage", "gxmessage",
"kmessage", "xmessage"};
static int PREV_MSG_INDEX = -1;
#define MSG_PROGS_LEN (sizeof(MSG_PROGS) / sizeof(MSG_PROGS[0]))
char *prog = NULL;
int ret;
/* Save some fork()'ing and attempt to use last program if possible. */
if (PREV_MSG_INDEX >= 0) {
assert(PREV_MSG_INDEX < MSG_PROGS_LEN);
prog = argv[0] = (char *)MSG_PROGS[PREV_MSG_INDEX];
ret = runTask(prog, argv, exit_status);
} else {
/* Otherwise, try running each xmessage alternative until one works or
* we run out of options. */
size_t i;
for (i = 0; i < MSG_PROGS_LEN; ++i) {
prog = argv[0] = (char *)MSG_PROGS[i];
ret = runTask(prog, argv, exit_status);
if (ret != EXEC_FAILED) break;
}
if (ret == TASK_SUCCESS) PREV_MSG_INDEX = i;
}
return ret;
}
static int runTask(const char *taskname, char * const argv[], int *exit_status)
{
pid_t pid;
int status;
switch (pid = fork()) {
case -1: /* Failed to fork */
perror("fork");
return FORK_FAILED; /* Failed to fork. */
case 0: /* Child process */
execvp(taskname, argv);
exit(42); /* Failed to run task. */
default: /* Parent process */
wait(&status); /* Block execution until finished. */
if (!WIFEXITED(status) || (status = WEXITSTATUS(status)) == 42) {
return EXEC_FAILED; /* Task failed to run. */
}
if (exit_status != NULL) *exit_status = status;
return TASK_SUCCESS; /* Success! */
}
}
#endif
================================================
FILE: src/alert.h
================================================
#pragma once
#ifndef ALERT_H
#define ALERT_H
#if defined(_MSC_VER)
#include "ms_stdbool.h"
#else
#include <stdbool.h>
#endif
/* Displays alert with given attributes, and blocks execution until the user
* responds. Returns 0 if defaultButton was pressed, 1 if cancelButton was
* pressed, or -1 if an error occurred. */
int showAlert(const char *title, const char *msg, const char *defaultButton,
const char *cancelButton);
#endif /* ALERT_H */
================================================
FILE: src/base64.c
================================================
#include "base64.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
/* Encoding table as described in RFC1113. */
const static uint8_t b64_encode_table[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz0123456789+/";
/* Decoding table. */
const static int8_t b64_decode_table[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 00-0F */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10-1F */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 20-2F */
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 30-3F */
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 40-4F */
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 50-5F */
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 60-6F */
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 70-7F */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80-8F */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90-9F */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A0-AF */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* B0-BF */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* C0-CF */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* D0-DF */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* E0-EF */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* F0-FF */
};
uint8_t *base64decode(const uint8_t *src, const size_t buflen, size_t *retlen)
{
int8_t digit, lastdigit;
size_t i, j;
uint8_t *decoded;
const size_t maxlen = ((buflen + 3) / 4) * 3;
/* Sanity check */
assert(src != NULL);
digit = lastdigit = j = 0;
decoded = malloc(maxlen + 1);
if (decoded == NULL) return NULL;
for (i = 0; i < buflen; ++i) {
if ((digit = b64_decode_table[src[i]]) != -1) {
/* Decode block */
switch (i % 4) {
case 1:
decoded[j++] = ((lastdigit << 2) | ((digit & 0x30) >> 4));
break;
case 2:
decoded[j++] = (((lastdigit & 0xF) << 4) | ((digit & 0x3C) >> 2));
break;
case 3:
decoded[j++] = (((lastdigit & 0x03) << 6) | digit);
break;
}
lastdigit = digit;
}
}
if (retlen != NULL) *retlen = j;
decoded[j] = '\0';
return decoded; /* Must be free()'d by caller */
}
uint8_t *base64encode(const uint8_t *src, const size_t buflen, size_t *retlen)
{
size_t i, j;
const size_t maxlen = (((buflen + 3) & ~3)) * 4;
uint8_t *encoded = malloc(maxlen + 1);
if (encoded == NULL) return NULL;
/* Sanity check */
assert(src != NULL);
assert(buflen > 0);
j = 0;
for (i = 0; i < buflen + 1; ++i) {
/* Encode block */
switch (i % 3) {
case 0:
encoded[j++] = b64_encode_table[src[i] >> 2];
encoded[j++] = b64_encode_table[((src[i] & 0x03) << 4) |
((src[i + 1] & 0xF0) >> 4)];
break;
case 1:
encoded[j++] = b64_encode_table[((src[i] & 0x0F) << 2) |
((src[i + 1] & 0xC0) >> 6)];
break;
case 2:
encoded[j++] = b64_encode_table[(src[i] & 0x3F)];
break;
}
}
/* Add padding if necessary */
if ((j % 4) != 0) {
const size_t with_padding = ((j + 3) & ~3); /* Align to 4 bytes */
do {
encoded[j++] = '=';
} while (j < with_padding);
}
assert(j <= maxlen);
if (retlen != NULL) *retlen = j;
encoded[j] = '\0';
return encoded; /* Must be free()'d by caller */
}
================================================
FILE: src/base64.h
================================================
#pragma once
#ifndef BASE64_H
#define BASE64_H
#include <stddef.h>
#if defined(_MSC_VER)
#include "ms_stdint.h"
#else
#include <stdint.h>
#endif
/* Decode a base64 encoded string discarding line breaks and noise.
*
* Returns a new string to be free()'d by caller, or NULL on error.
* Returned string is guaranteed to be NUL-terminated.
*
* If |retlen| is not NULL, it is set to the length of the returned string
* (minus the NUL-terminator) on successful return. */
uint8_t *base64decode(const uint8_t *buf, const size_t buflen, size_t *retlen);
/* Encode a base64 encoded string without line breaks or noise.
*
* Returns a new string to be free()'d by caller, or NULL on error.
* Returned string is guaranteed to be NUL-terminated with the correct padding.
*
* If |retlen| is not NULL, it is set to the length of the returned string
* (minus the NUL-terminator) on successful return. */
uint8_t *base64encode(const uint8_t *buf, const size_t buflen, size_t *retlen);
#endif /* BASE64_H */
================================================
FILE: src/bitmap_find.c
================================================
#include "bitmap_find.h"
#include "UTHashTable.h"
#include <assert.h>
/* Node to be used in hash table. */
struct shiftNode {
UTHashNode_HEAD /* Make structure hashable */
MMRGBHex color; /* Key */
MMPoint offset; /* Value */
};
/* --- Hash table helper functions --- */
/* Adds hex-color/offset pair to jump table. */
static void addNodeToTable(UTHashTable *table, MMRGBHex color, MMPoint offset);
/* Returns node associated with color in jump table, or NULL if it
* doesn't exist. */
static struct shiftNode *nodeForColor(UTHashTable *table, MMRGBHex color);
/* Returns nonzero (true) if table has key, or zero (false) if not. */
#define tableHasKey(table, color) (nodeForColor(table, color) != NULL)
/* --- Boyer-Moore helper functions --- */
/* Calculates the first table for use in a Boyer-Moore search algorithm.
* Table is in the form [colors: shift_values], where colors are those in
* |needle|, and the shift values are each color's distance from the rightmost
* offset. All other colors are assumed to have a shift value equal to the
* length of needle.
*/
static void initBadShiftTable(UTHashTable *jumpTable, MMBitmapRef needle);
/* Frees memory occupied by calling initBadShiftTable().
* Currently this is just an alias for destroyHashTable(). */
#define destroyBadShiftTable(jumpTable) destroyHashTable(jumpTable)
/* Returns true if |needle| is found in |haystack| at |offset|. */
static int needleAtOffset(MMBitmapRef needle, MMBitmapRef haystack,
MMPoint offset, float tolerance);
/* --- --- */
/* An modification of the Boyer-Moore-Horspool Algorithm, only applied to
* bitmaps and colors instead of strings and characters.
*
* TODO: The Boyer-Moore algorithm (with the second jump table) would probably
* be more efficient, but this was simpler (for now).
*
* The jump table (|badShiftTable|) is passed as a parameter to avoid being
* recalculated each time. It should be a pointer to a UTHashTable init'd with
* initBadShiftTable().
*
* Returns 0 and sets |point| to the starting point of |needle| in |haystack|
* if |needle| was found in |haystack|, or returns -1 if not. */
static int findBitmapInRectAt(MMBitmapRef needle,
MMBitmapRef haystack,
MMPoint *point,
MMRect rect,
float tolerance,
MMPoint startPoint,
UTHashTable *badShiftTable)
{
const size_t scanHeight = rect.size.height - needle->height;
const size_t scanWidth = rect.size.width - needle->width;
MMPoint pointOffset = startPoint;
/* const MMPoint lastPoint = MMPointMake(needle->width - 1, needle->height - 1); */
/* Sanity check */
if (needle->height > haystack->height || needle->width > haystack->width ||
!MMBitmapRectInBounds(haystack, rect)) {
return -1;
}
assert(point != NULL);
assert(needle != NULL);
assert(needle->height > 0 && needle->width > 0);
assert(haystack != NULL);
assert(haystack->height > 0 && haystack->width > 0);
assert(badShiftTable != NULL);
/* Search |haystack|, while |needle| can still be within it. */
while (pointOffset.y <= scanHeight) {
/* struct shiftNode *node = NULL;
MMRGBHex lastColor; */
while (pointOffset.x <= scanWidth) {
/* Check offset in |haystack| for |needle|. */
if (needleAtOffset(needle, haystack, pointOffset, tolerance)) {
++pointOffset.x;
++pointOffset.y;
*point = pointOffset;
return 0;
}
/* Otherwise, calculate next x offset to check. */
/*
* Note that here we are getting the skip value based on the last
* color of |needle|, no matter where we didn't match. The
* alternative of pretending that the mismatched color was the previous
* color is slower in the normal case.
*/
/* lastColor = MMRGBHexAtPoint(haystack, pointOffset.x + lastPoint.x,
pointOffset.y + lastPoint.y); */
/* TODO: This fails on certain edge cases (issue#7). */
/* When a color is encountered that does not occur in |needle|, we can
* safely skip ahead for the whole length of |needle|.
* Otherwise, use the value stored in the jump table. */
/* node = nodeForColor(badShiftTable, lastColor);
pointOffset.x += (node == NULL) ? needle->width : (node->offset).x; */
/* For now, be naive. */
++pointOffset.x;
}
pointOffset.x = rect.origin.x;
/* lastColor = MMRGBHexAtPoint(haystack, pointOffset.x + lastPoint.x,
pointOffset.y + lastPoint.y);
node = nodeForColor(badShiftTable, lastColor);
pointOffset.y += node == NULL ? lastPoint.y : (node->offset).y; */
/* TODO: The above commented out code fails at certain edge cases, e.g.:
* Needle: [B, b
* b, b,
* B, b]
* Haystack: [w, w, w, w, w
* w, w, w, w, b
* w, w, w, b, b
* w, w, w, w, b]
* The previous algorithm noticed that the first 3 x 3 block had nothing
* in common with the image, and thus, after scanning the first row,
* skipped three blocks downward to scan the next (which didn't exist,
* so the loop ended). However, the needle was hidden IN-BETWEEN this
* jump -- skipping was appropriate for scanning the column but not
* the row.
*
* I need to figure out a more optimal solution; temporarily I am just
* scanning every single y coordinate, only skipping on x's. This
* always works, but is probably not optimal.
*/
++pointOffset.y;
}
return -1;
}
int findBitmapInRect(MMBitmapRef needle,
MMBitmapRef haystack,
MMPoint *point,
MMRect rect,
float tolerance)
{
UTHashTable badShiftTable;
int ret;
initBadShiftTable(&badShiftTable, needle);
ret = findBitmapInRectAt(needle, haystack, point, rect,
tolerance, MMPointZero, &badShiftTable);
destroyBadShiftTable(&badShiftTable);
return ret;
}
MMPointArrayRef findAllBitmapInRect(MMBitmapRef needle, MMBitmapRef haystack,
MMRect rect, float tolerance)
{
MMPointArrayRef pointArray = createMMPointArray(0);
MMPoint point = MMPointZero;
UTHashTable badShiftTable;
initBadShiftTable(&badShiftTable, needle);
while (findBitmapInRectAt(needle, haystack, &point, rect,
tolerance, point, &badShiftTable) == 0) {
const size_t scanWidth = (haystack->width - needle->width) + 1;
MMPointArrayAppendPoint(pointArray, point);
ITER_NEXT_POINT(point, scanWidth, 0);
}
destroyBadShiftTable(&badShiftTable);
return pointArray;
}
size_t countOfBitmapInRect(MMBitmapRef needle, MMBitmapRef haystack,
MMRect rect, float tolerance)
{
size_t count = 0;
MMPoint point = MMPointZero;
UTHashTable badShiftTable;
initBadShiftTable(&badShiftTable, needle);
while (findBitmapInRectAt(needle, haystack, &point, rect,
tolerance, point, &badShiftTable) == 0) {
const size_t scanWidth = (haystack->width - needle->width) + 1;
++count;
ITER_NEXT_POINT(point, scanWidth, 0);
}
destroyBadShiftTable(&badShiftTable);
return count;
}
/* --- Boyer-Moore helper functions --- */
static void initBadShiftTable(UTHashTable *jumpTable, MMBitmapRef needle)
{
const MMPoint lastPoint = MMPointMake(needle->width - 1, needle->height - 1);
const size_t maxColors = needle->width * needle->height;
MMPoint scan;
/* Allocate max size initially to avoid a million calls to malloc(). */
initHashTable(jumpTable, maxColors, sizeof(struct shiftNode));
/* Populate jumpTable with analysis of |needle|. */
for (scan.y = lastPoint.y; ; --scan.y) {
for (scan.x = lastPoint.x; ; --scan.x) {
MMRGBHex color = MMRGBHexAtPoint(needle, scan.x, scan.y);
if (!tableHasKey(jumpTable, color)) {
addNodeToTable(jumpTable, color,
MMPointMake(needle->width - scan.x,
needle->height - scan.y));
}
if (scan.x == 0) break; /* Avoid infinite loop from unsigned type. */
}
if (scan.y == 0) break;
}
}
static int needleAtOffset(MMBitmapRef needle, MMBitmapRef haystack,
MMPoint offset, float tolerance)
{
const MMPoint lastPoint = MMPointMake(needle->width - 1, needle->height - 1);
MMPoint scan;
/* Note that |needle| is searched backwards, in accordance with the
* Boyer-Moore search algorithm. */
for (scan.y = lastPoint.y; ; --scan.y) {
for (scan.x = lastPoint.x; ; --scan.x) {
MMRGBHex ncolor = MMRGBHexAtPoint(needle, scan.x, scan.y);
MMRGBHex hcolor = MMRGBHexAtPoint(haystack, offset.x + scan.x,
offset.y + scan.y);
if (!MMRGBHexSimilarToColor(ncolor, hcolor, tolerance)) return 0;
if (scan.x == 0) break; /* Avoid infinite loop from unsigned type. */
}
if (scan.y == 0) break;
}
return 1;
}
/* --- Hash table helper functions --- */
static void addNodeToTable(UTHashTable *table,
MMRGBHex hexColor,
MMPoint offset)
{
struct shiftNode *node = getNewNode(table);
node->color = hexColor;
node->offset = offset;
UTHASHTABLE_ADD_INT(table, color, node, struct shiftNode);
}
static struct shiftNode *nodeForColor(UTHashTable *table,
MMRGBHex color)
{
struct shiftNode *uttable = table->uttable;
struct shiftNode *node;
HASH_FIND_INT(uttable, &color, node);
return node;
}
================================================
FILE: src/bitmap_find.h
================================================
#pragma once
#ifndef BITMAP_H
#define BITMAP_H
#include "types.h"
#include "MMBitmap.h"
#include "MMPointArray.h"
/* Convenience wrapper around findBitmapInRect(), where |rect| is the bounds
* of |haystack|. */
#define findBitmapInBitmap(needle, haystack, pointPtr, tol) \
findBitmapInRect(needle, haystack, pointPtr, MMBitmapGetBounds(haystack), tol)
/* Returns 0 and sets |point| to the origin of |needle| in |haystack| if
* |needle| was found in |haystack| inside of |rect|, or returns -1 if not.
*
* |tolerance| should be in the range 0.0f - 1.0f, denoting how closely the
* colors in the bitmaps need to match, with 0 being exact and 1 being any.
*/
int findBitmapInRect(MMBitmapRef needle, MMBitmapRef haystack,
MMPoint *point, MMRect rect, float tolerance);
/* Convenience wrapper around findAllBitmapInRect(), where |rect| is the bounds
* of |haystack|. */
#define findAllBitmapInBitmap(needle, haystack, tolerance) \
findAllBitmapInRect(needle, haystack, \
MMBitmapGetBounds(haystack), tolerance)
/* Returns MMPointArray of all occurrences of |needle| in |haystack| inside of
* |rect|. Note that an is returned regardless of whether |needle| was found;
* check array->count to see if it actually was.
*
* |tolerance| should be in the range 0.0f - 1.0f, denoting how closely the
* colors in the bitmaps need to match, with 0 being exact and 1 being any.
*
* Responsibility for freeing the MMPointArray with destroyMMPointArray() is
* given to the caller.
*/
MMPointArrayRef findAllBitmapInRect(MMBitmapRef needle, MMBitmapRef haystack,
MMRect rect, float tolerance);
/* Convenience wrapper around countOfBitmapInRect(), where |rect| is the bounds
* of |haystack|. */
#define countOfBitmapInBitmap(needle, haystack, tolerance) \
countOfBitmapInRect(needle, haystack, MMBitmapGetBounds(haystack), tolerance)
/* Returns the number of occurences of |needle| in |haystack| inside
* of |rect|. */
size_t countOfBitmapInRect(MMBitmapRef needle, MMBitmapRef haystack,
MMRect rect, float tolerance);
#endif /* BITMAP_H */
================================================
FILE: src/bmp_io.c
================================================
#include "bmp_io.h"
#include "os.h"
#include "endian.h"
#include <stdio.h> /* fopen() */
#include <string.h> /* memcpy() */
#if defined(_MSC_VER)
#include "ms_stdbool.h"
#include "ms_stdint.h"
#else
#include <stdbool.h>
#include <stdint.h>
#endif
#pragma pack(push, 1) /* The following structs should be continguous, so we can
* copy them in one read. */
/*
* Standard, initial BMP Header
*/
struct BITMAP_FILE_HEADER {
uint16_t magic; /* First two byes of the file; should be 0x4D42. */
uint32_t fileSize; /* Size of the BMP file in bytes (unreliable). */
uint32_t reserved; /* Application-specific. */
uint32_t imageOffset; /* Offset to bitmap data. */
};
#define BMP_MAGIC 0x4D42 /* The starting key that marks the file as a BMP. */
enum _BMP_COMPRESSION {
kBMP_RGB = 0, /* No compression. */
kBMP_RLE8 = 1, /* Can only be used with 8-bit bitmaps. */
kBMP_RLE4 = 2, /* Can only be used with 4-bit bitmaps. */
kBMP_BITFIELDS = 3, /* Can only be used with 16/32-bit bitmaps. */
kBMP_JPEG = 4, /* Bitmap contains a JPEG image. */
kBMP_PNG = 5 /* Bitmap contains a PNG image. */
};
typedef uint32_t BMP_COMPRESSION;
/*
* Windows 3 Header
*/
struct BITMAP_INFO_HEADER {
uint32_t headerSize; /* The size of this header (40 bytes). */
int32_t width; /* The bitmap width in pixels. */
int32_t height; /* The bitmap height in pixels. */
/* (A negative value denotes that the image
* is flipped.) */
uint16_t colorPlanes; /* The number of color planes; must be 1. */
uint16_t bitsPerPixel; /* The color depth of the image (1, 4, 8, 16,
* 24, or 32). */
BMP_COMPRESSION compression; /* The compression method being used. */
uint32_t imageSize; /* Size of the bitmap in bytes (unreliable).*/
int32_t xRes; /* The horizontal resolution (unreliable). */
int32_t yRes; /* The vertical resolution (unreliable). */
uint32_t colorsUsed; /* The number of colors in the color table,
* or 0 to default to 2^n. */
uint32_t colorsImportant; /* Colors important for displaying bitmap,
* or 0 when every color is equally important;
* ignored. */
};
/*
* OS/2 v1 Header
*/
struct BITMAP_CORE_HEADER {
uint32_t headerSize; /* The size of this header (12 bytes). */
uint16_t width; /* The bitmap width in pixels. */
uint16_t height; /* The bitmap height in pixels. */
uint16_t colorPlanes; /* The number of color planes; must be 1. */
uint16_t bitsPerPixel; /* Color depth of the image (1, 4, 8, or 24). */
};
#pragma pack(pop) /* Let the compiler do what it wants now. */
/* BMP files are always saved in little endian format (x86), so we need to
* convert them if we're not on a little endian machine (e.g., ARM & ppc). */
#if __BYTE_ORDER == __BIG_ENDIAN
/* Converts bitmap file header from to and from little endian, if and only if
* host is big endian. */
static void convertBitmapFileHeader(struct BITMAP_FILE_HEADER *header)
{
header->magic = swapLittleAndHost16(header->magic);
swapLittleAndHost32(header->fileSize);
swapLittleAndHost32(header->reserved);
swapLittleAndHost32(header->imageOffset);
}
/* Converts bitmap info header from to and from little endian, if and only if
* host is big endian. */
static void convertBitmapInfoHeader(struct BITMAP_INFO_HEADER *header)
{
header->headerSize = swapLittleAndHost32(header->headerSize);
header->width = swapLittleAndHost32(header->width);
header->height = swapLittleAndHost32(header->height);
header->colorPlanes = swapLittleAndHost16(header->colorPlanes);
header->bitsPerPixel = swapLittleAndHost16(header->bitsPerPixel);
header->compression = swapLittleAndHost32(header->compression);
header->imageSize = swapLittleAndHost32(header->imageSize);
header->xRes = swapLittleAndHost32(header->xRes);
header->yRes = swapLittleAndHost32(header->yRes);
header->colorsUsed = swapLittleAndHost32(header->colorsUsed);
header->colorsImportant = swapLittleAndHost32(header->colorsImportant);
}
#elif __BYTE_ORDER == __LITTLE_ENDIAN
/* No conversion necessary if we are already little endian. */
#define convertBitmapFileHeader(header)
#define convertBitmapInfoHeader(header)
#endif
/* Returns newly alloc'd image data from bitmap file. The current position of
* the file must be at the start of the image before calling this. */
static uint8_t *readImageData(FILE *fp, size_t width, size_t height,
uint8_t bytesPerPixel, size_t bytewidth);
/* Copys image buffer from |bitmap| to |dest| in BGR format. */
static void copyBGRDataFromMMBitmap(MMBitmapRef bitmap, uint8_t *dest);
const char *MMBMPReadErrorString(MMIOError error)
{
switch (error) {
case kBMPAccessError:
return "Could not open file";
case kBMPInvalidKeyError:
return "Not a BMP file";
case kBMPUnsupportedHeaderError:
return "Unsupported BMP header";
case kBMPInvalidColorPanesError:
return "Invalid number of color panes in BMP file";
case kBMPUnsupportedColorDepthError:
return "Unsupported color depth in BMP file";
case kBMPUnsupportedCompressionError:
return "Unsupported file compression in BMP file";
case kBMPInvalidPixelDataError:
return "Could not read BMP pixel data";
default:
return NULL;
}
}
MMBitmapRef newMMBitmapFromBMP(const char *path, MMBMPReadError *err)
{
FILE *fp;
struct BITMAP_FILE_HEADER fileHeader = {0}; /* Initialize elements to 0. */
struct BITMAP_INFO_HEADER dibHeader = {0};
uint32_t headerSize = 0;
uint8_t bytesPerPixel;
size_t bytewidth;
uint8_t *imageBuf;
if ((fp = fopen(path, "rb")) == NULL) {
if (err != NULL) *err = kBMPAccessError;
return NULL;
}
/* Initialize error code to generic value. */
if (err != NULL) *err = kBMPGenericError;
if (fread(&fileHeader, sizeof(fileHeader), 1, fp) == 0) goto bail;
/* Convert from little-endian if it's not already. */
convertBitmapFileHeader(&fileHeader);
/* First two bytes should always be 0x4D42. */
if (fileHeader.magic != BMP_MAGIC) {
if (err != NULL) *err = kBMPInvalidKeyError;
goto bail;
}
/* Get header size. */
if (fread(&headerSize, sizeof(headerSize), 1, fp) == 0) goto bail;
headerSize = swapLittleAndHost32(headerSize);
/* Back up before reading header. */
if (fseek(fp, -(long)sizeof(headerSize), SEEK_CUR) < 0) goto bail;
if (headerSize == 12) { /* OS/2 v1 header */
struct BITMAP_CORE_HEADER coreHeader = {0};
if (fread(&coreHeader, sizeof(coreHeader), 1, fp) == 0) goto bail;
dibHeader.width = coreHeader.width;
dibHeader.height = coreHeader.height;
dibHeader.colorPlanes = coreHeader.colorPlanes;
dibHeader.bitsPerPixel = coreHeader.bitsPerPixel;
} else if (headerSize == 40 || headerSize == 108 || headerSize == 124) {
/* Windows v3/v4/v5 header */
/* Read only the common part (v3) and skip over the rest. */
if (fread(&dibHeader, sizeof(dibHeader), 1, fp) == 0) goto bail;
} else {
if (err != NULL) *err = kBMPUnsupportedHeaderError;
goto bail;
}
convertBitmapInfoHeader(&dibHeader);
if (dibHeader.colorPlanes != 1) {
if (err != NULL) *err = kBMPInvalidColorPanesError;
goto bail;
}
/* Currently only 24-bit and 32-bit are supported. */
if (dibHeader.bitsPerPixel != 24 && dibHeader.bitsPerPixel != 32) {
if (err != NULL) *err = kBMPUnsupportedColorDepthError;
goto bail;
}
if (dibHeader.compression != kBMP_RGB) {
if (err != NULL) *err = kBMPUnsupportedCompressionError;
goto bail;
}
/* This can happen because we don't fully parse Windows v4/v5 headers. */
if (ftell(fp) != (long)fileHeader.imageOffset) {
fseek(fp, fileHeader.imageOffset, SEEK_SET);
}
/* Get bytes per row, including padding. */
bytesPerPixel = dibHeader.bitsPerPixel / 8;
bytewidth = ADD_PADDING(dibHeader.width * bytesPerPixel);
imageBuf = readImageData(fp, dibHeader.width, abs(dibHeader.height),
bytesPerPixel, bytewidth);
fclose(fp);
if (imageBuf == NULL) {
if (err != NULL) *err = kBMPInvalidPixelDataError;
return NULL;
}
/* A negative height indicates that the image is flipped.
*
* We store our bitmaps as "flipped" according to the BMP format; i.e., (0, 0)
* is the top left, not bottom left. So we only need to flip the bitmap if
* the height is NOT negative. */
if (dibHeader.height < 0) {
dibHeader.height = -dibHeader.height;
} else {
flipBitmapData(imageBuf, dibHeader.width, dibHeader.height, bytewidth);
}
return createMMBitmap(imageBuf, dibHeader.width, dibHeader.height,
bytewidth, (uint8_t)dibHeader.bitsPerPixel,
bytesPerPixel);
bail:
fclose(fp);
return NULL;
}
uint8_t *createBitmapData(MMBitmapRef bitmap, size_t *len)
{
/* BMP files are always aligned to 4 bytes. */
const size_t bytewidth = ((bitmap->width * bitmap->bytesPerPixel) + 3) & ~3;
const size_t imageSize = bytewidth * bitmap->height;
struct BITMAP_FILE_HEADER *fileHeader;
struct BITMAP_INFO_HEADER *dibHeader;
/* Should always be 54. */
const size_t imageOffset = sizeof(*fileHeader) + sizeof(*dibHeader);
uint8_t *data;
const size_t dataLen = imageOffset + imageSize;
data = calloc(1, dataLen);
if (data == NULL) return NULL;
/* Save top header. */
fileHeader = (struct BITMAP_FILE_HEADER *)data;
fileHeader->magic = BMP_MAGIC;
fileHeader->fileSize = (uint32_t)(sizeof(*dibHeader) + imageSize);
fileHeader->imageOffset = (uint32_t)imageOffset;
/* BMP files are always stored as little-endian, so we need to convert back
* if necessary. */
convertBitmapFileHeader(fileHeader);
/* Copy Windows v3 header. */
dibHeader = (struct BITMAP_INFO_HEADER *)(data + sizeof(*fileHeader));
dibHeader->headerSize = sizeof(*dibHeader); /* Should always be 40. */
dibHeader->width = (int32_t)bitmap->width;
dibHeader->height = -(int32_t)bitmap->height; /* Our bitmaps are "flipped". */
dibHeader->colorPlanes = 1;
dibHeader->bitsPerPixel = bitmap->bitsPerPixel;
dibHeader->compression = kBMP_RGB; /* Don't save with compression. */
dibHeader->imageSize = (uint32_t)imageSize;
convertBitmapInfoHeader(dibHeader);
/* Lastly, copy the pixel data. */
copyBGRDataFromMMBitmap(bitmap, data + imageOffset);
if (len != NULL) *len = dataLen;
return data;
}
int saveMMBitmapAsBMP(MMBitmapRef bitmap, const char *path)
{
FILE *fp;
size_t dataLen;
uint8_t *data;
if ((fp = fopen(path, "wb")) == NULL) return -1;
if ((data = createBitmapData(bitmap, &dataLen)) == NULL) {
fclose(fp);
return -1;
}
if (fwrite(data, dataLen, 1, fp) == 0) {
free(data);
fclose(fp);
return -1;
}
free(data);
fclose(fp);
return 0;
}
static uint8_t *readImageData(FILE *fp, size_t width, size_t height,
uint8_t bytesPerPixel, size_t bytewidth)
{
size_t imageSize = bytewidth * height;
uint8_t *imageBuf = calloc(1, imageSize);
if (MMRGB_IS_BGR && (bytewidth % 4) == 0) { /* No conversion needed. */
if (fread(imageBuf, imageSize, 1, fp) == 0) {
free(imageBuf);
return NULL;
}
} else { /* Convert from BGR with 4-byte alignment. */
uint8_t *row = malloc(bytewidth);
size_t y;
const size_t bmp_bytewidth = (width * bytesPerPixel + 3) & ~3;
if (row == NULL) return NULL;
assert(bmp_bytewidth <= bytewidth);
/* Read image data row by row. */
for (y = 0; y < height; ++y) {
const size_t rowOffset = y * bytewidth;
size_t x;
uint8_t *rowptr = row;
if (fread(row, bmp_bytewidth, 1, fp) == 0) {
free(imageBuf);
free(row);
return NULL;
}
for (x = 0; x < width; ++x) {
const size_t colOffset = x * bytesPerPixel;
MMRGBColor *color = (MMRGBColor *)(imageBuf +
rowOffset + colOffset);
/* BMP files are stored in BGR format. */
color->blue = rowptr[0];
color->green = rowptr[1];
color->red = rowptr[2];
rowptr += bytesPerPixel;
}
}
free(row);
}
return imageBuf;
}
static void copyBGRDataFromMMBitmap(MMBitmapRef bitmap, uint8_t *dest)
{
if (MMRGB_IS_BGR && (bitmap->bytewidth % 4) == 0) { /* No conversion needed. */
memcpy(dest, bitmap->imageBuffer, bitmap->bytewidth * bitmap->height);
} else { /* Convert to RGB with other-than-4-byte alignment. */
const size_t bytewidth = (bitmap->width * bitmap->bytesPerPixel + 3) & ~3;
size_t y;
/* Copy image data row by row. */
for (y = 0; y < bitmap->height; ++y) {
uint8_t *rowptr = dest + (y * bytewidth);
size_t x;
for (x = 0; x < bitmap->width; ++x) {
MMRGBColor *color = MMRGBColorRefAtPoint(bitmap, x, y);
/* BMP files are stored in BGR format. */
rowptr[0] = color->blue;
rowptr[1] = color->green;
rowptr[2] = color->red;
rowptr += bitmap->bytesPerPixel;
}
}
}
}
/* Perform an in-place swap from Quadrant 1 to Quadrant III format (upside-down
* PostScript/GL to right side up QD/CG raster format) We do this in-place,
* which requires more copying, but will touch only half the pages.
*
* This is blatantly copied from Apple's glGrab example code. */
void flipBitmapData(void *data, size_t width, size_t height, size_t bytewidth)
{
size_t top, bottom;
void *topP;
void *bottomP;
void *tempbuf;
if (height <= 1) return; /* No flipping necessary if height is <= 1. */
top = 0;
bottom = height - 1;
tempbuf = malloc(bytewidth);
if (tempbuf == NULL) return;
while (top < bottom) {
topP = (void *)((top * bytewidth) + (intptr_t)data);
bottomP = (void *)((bottom * bytewidth) + (intptr_t)data);
/* Save and swap scanlines.
* Does a simple in-place exchange with a temp buffer. */
memcpy(tempbuf, topP, bytewidth);
memcpy(topP, bottomP, bytewidth);
memcpy(bottomP, tempbuf, bytewidth);
++top;
--bottom;
}
free(tempbuf);
}
================================================
FILE: src/bmp_io.h
================================================
#pragma once
#ifndef BMP_IO_H
#define BMP_IO_H
#include "MMBitmap.h"
#include "io.h"
#ifdef __cplusplus
extern "C"
{
#endif
enum _BMPReadError {
kBMPGenericError = 0,
kBMPAccessError,
kBMPInvalidKeyError,
kBMPUnsupportedHeaderError,
kBMPInvalidColorPanesError,
kBMPUnsupportedColorDepthError,
kBMPUnsupportedCompressionError,
kBMPInvalidPixelDataError
};
typedef MMIOError MMBMPReadError;
/* Returns description of given MMBMPReadError.
* Returned string is constant and hence should not be freed. */
const char *MMBMPReadErrorString(MMIOError error);
/* Attempts to read bitmap file at path; returns new MMBitmap on success, or
* NULL on error. If |error| is non-NULL, it will be set to the error code
* on return.
*
* Currently supports:
* - Uncompressed Windows v3/v4/v5 24-bit or 32-bit BMP.
* - OS/2 v1 or v2 24-bit BMP.
* - Does NOT yet support: 1-bit, 4-bit, 8-bit, 16-bit, compressed bitmaps,
* or PNGs/JPEGs disguised as BMPs (and returns NULL if those are given).
*
* Responsibility for destroy()'ing returned MMBitmap is left up to caller. */
MMBitmapRef newMMBitmapFromBMP(const char *path, MMBMPReadError *error);
/* Returns a buffer containing the raw BMP file data in Windows v3 BMP format,
* ready to be saved to a file. If |len| is not NULL, it will be set to the
* number of bytes allocated in the returned buffer.
*
* Responsibility for free()'ing data is left up to the caller. */
uint8_t *createBitmapData(MMBitmapRef bitmap, size_t *len);
/* Saves bitmap to file in Windows v3 BMP format.
* Returns 0 on success, -1 on error. */
int saveMMBitmapAsBMP(MMBitmapRef bitmap, const char *path);
/* Swaps bitmap from Quadrant 1 to Quadran III format, or vice versa
* (upside-down Cartesian/PostScript/GL <-> right side up QD/CG raster format).
*/
void flipBitmapData(void *data, size_t width, size_t height, size_t bytewidth);
#ifdef __cplusplus
}
#endif
#endif /* BMP_IO_H */
================================================
FILE: src/color_find.c
================================================
#include "color_find.h"
#include "screen.h"
#include <stdlib.h>
/* Abstracted, general function to avoid repeated code. */
static int findColorInRectAt(MMBitmapRef image, MMRGBHex color, MMPoint *point,
MMRect rect, float tolerance, MMPoint startPoint)
{
MMPoint scan = startPoint;
if (!MMBitmapRectInBounds(image, rect)) return -1;
for (; scan.y < rect.size.height; ++scan.y) {
for (; scan.x < rect.size.width; ++scan.x) {
MMRGBHex found = MMRGBHexAtPoint(image, scan.x, scan.y);
if (MMRGBHexSimilarToColor(color, found, tolerance)) {
if (point != NULL) *point = scan;
return 0;
}
}
scan.x = rect.origin.x;
}
return -1;
}
int findColorInRect(MMBitmapRef image, MMRGBHex color,
MMPoint *point, MMRect rect, float tolerance)
{
return findColorInRectAt(image, color, point, rect, tolerance, rect.origin);
}
MMPointArrayRef findAllColorInRect(MMBitmapRef image, MMRGBHex color,
MMRect rect, float tolerance)
{
MMPointArrayRef pointArray = createMMPointArray(0);
MMPoint point = MMPointZero;
while (findColorInRectAt(image, color, &point, rect, tolerance, point) == 0) {
MMPointArrayAppendPoint(pointArray, point);
ITER_NEXT_POINT(point, rect.size.width, rect.origin.x);
}
return pointArray;
}
size_t countOfColorsInRect(MMBitmapRef image, MMRGBHex color, MMRect rect,
float tolerance)
{
size_t count = 0;
MMPoint point = MMPointZero;
while (findColorInRectAt(image, color, &point, rect, tolerance, point) == 0) {
ITER_NEXT_POINT(point, rect.size.width, rect.origin.x);
++count;
}
return count;
}
================================================
FILE: src/color_find.h
================================================
#pragma once
#ifndef COLOR_FIND_H
#define COLOR_FIND_H
#include "MMBitmap.h"
#include "MMPointArray.h"
/* Convenience wrapper around findColorInRect(), where |rect| is the bounds of
* the image. */
#define findColorInImage(image, color, pointPtr, tolerance) \
findColorInRect(image, color, pointPtr, MMBitmapGetBounds(image), tolerance)
/* Attempt to find a pixel with the given color in |image| inside |rect|.
* Returns 0 on success, non-zero on failure. If the color was found and
* |point| is not NULL, it will be initialized to the (x, y) coordinates the
* RGB color.
*
* |tolerance| should be in the range 0.0f - 1.0f, denoting how closely the
* colors need to match, with 0 being exact and 1 being any. */
int findColorInRect(MMBitmapRef image, MMRGBHex color, MMPoint *point,
MMRect rect, float tolerance);
/* Convenience wrapper around findAllRGBInRect(), where |rect| is the bounds of
* the image. */
#define findAllColorInImage(image, color, tolerance) \
findAllColorInRect(image, color, MMBitmapGetBounds(image), tolerance)
/* Returns MMPointArray of all pixels of given color in |image| inside of
* |rect|. Note that an array is returned regardless of whether the color was
* found; check array->count to see if it actually was.
*
* Responsibility for freeing the MMPointArray with destroyMMPointArray() is
* given to the caller.
*
* |tolerance| should be in the range 0.0f - 1.0f, denoting how closely the
* colors need to match, with 0 being exact and 1 being any. */
MMPointArrayRef findAllColorInRect(MMBitmapRef image, MMRGBHex color,
MMRect rect, float tolerance);
/* Convenience wrapper around countOfColorsInRect, where |rect| is the bounds
* of the image. */
#define countOfColorsInImage(image, color, tolerance) \
countOfColorsInRect(image, color, MMBitmapGetBounds(image), tolerance)
/* Returns the count of the given color in |rect| inside of |image|. */
size_t countOfColorsInRect(MMBitmapRef image, MMRGBHex color, MMRect rect,
float tolerance);
#endif /* COLOR_FIND_H */
================================================
FILE: src/deadbeef_rand.c
================================================
#include "deadbeef_rand.h"
#include <time.h>
static uint32_t deadbeef_seed;
static uint32_t deadbeef_beef = 0xdeadbeef;
uint32_t deadbeef_rand(void)
{
deadbeef_seed = (deadbeef_seed << 7) ^ ((deadbeef_seed >> 25) + deadbeef_beef);
deadbeef_beef = (deadbeef_beef << 7) ^ ((deadbeef_beef >> 25) + 0xdeadbeef);
return deadbeef_seed;
}
void deadbeef_srand(uint32_t x)
{
deadbeef_seed = x;
deadbeef_beef = 0xdeadbeef;
}
/* Taken directly from the documentation:
* http://inglorion.net/software/cstuff/deadbeef_rand/ */
uint32_t deadbeef_generate_seed(void)
{
uint32_t t = (uint32_t)time(NULL);
uint32_t c = (uint32_t)clock();
return (t << 24) ^ (c << 11) ^ t ^ (size_t) &c;
}
================================================
FILE: src/deadbeef_rand.h
================================================
#ifndef DEADBEEF_RAND_H
#define DEADBEEF_RAND_H
#include <stdint.h>
#define DEADBEEF_MAX UINT32_MAX
/* Dead Beef Random Number Generator
* From: http://inglorion.net/software/deadbeef_rand
* A fast, portable psuedo-random number generator by BJ Amsterdam Zuidoost.
* Stated in license terms: "Feel free to use the code in your own software." */
/* Generates a random number between 0 and DEADBEEF_MAX. */
uint32_t deadbeef_rand(void);
/* Seeds with the given integer. */
void deadbeef_srand(uint32_t x);
/* Generates seed from the current time. */
uint32_t deadbeef_generate_seed(void);
/* Seeds with the above function. */
#define deadbeef_srand_time() deadbeef_srand(deadbeef_generate_seed())
/* Returns random double in the range [a, b).
* Taken directly from the rand() man page. */
#define DEADBEEF_UNIFORM(a, b) \
((a) + (deadbeef_rand() / (((double)DEADBEEF_MAX / (b - a) + 1))))
/* Returns random integer in the range [a, b).
* Also taken from the rand() man page. */
#define DEADBEEF_RANDRANGE(a, b) \
(uint32_t)DEADBEEF_UNIFORM(a, b)
#endif /* DEADBEEF_RAND_H */
================================================
FILE: src/endian.h
================================================
#pragma once
#ifndef ENDIAN_H
#define ENDIAN_H
#include "os.h"
/*
* (Mostly) cross-platform endian definitions and bit swapping macros.
* Unfortunately, there is no standard C header for this, so we just
* include the most common ones and fallback to our own custom macros.
*/
#if defined(__linux__) /* Linux */
#include <endian.h>
#include <byteswap.h>
#elif (defined(__FreeBSD__) && __FreeBSD_version >= 470000) || \
defined(__OpenBSD__) || defined(__NetBSD__) /* (Free|Open|Net)BSD */
#include <sys/endian.h>
#define __BIG_ENDIAN BIG_ENDIAN
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#define __BYTE_ORDER BYTE_ORDER
#elif defined(IS_MACOSX) || (defined(BSD) && (BSD >= 199103)) /* Other BSD */
#include <machine/endian.h>
#define __BIG_ENDIAN BIG_ENDIAN
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#define __BYTE_ORDER BYTE_ORDER
#elif defined(IS_WINDOWS) /* Windows is assumed to be little endian only. */
#define __BIG_ENDIAN 4321
#define __LITTLE_ENDIAN 1234
#define __BYTE_ORDER __LITTLE_ENDIAN
#endif
/* Fallback to custom constants. */
#if !defined(__BIG_ENDIAN)
#define __BIG_ENDIAN 4321
#endif
#if !defined(__LITTLE_ENDIAN)
#define __LITTLE_ENDIAN 1234
#endif
/* Prefer compiler flag settings if given. */
#if defined(MM_BIG_ENDIAN)
#undef __BYTE_ORDER /* Avoid redefined macro compiler warning. */
#define __BYTE_ORDER __BIG_ENDIAN
#elif defined(MM_LITTLE_ENDIAN)
#undef __BYTE_ORDER /* Avoid redefined macro compiler warning. */
#define __BYTE_ORDER __LITTLE_ENDIAN
#endif
/* Define default endian-ness. */
#ifndef __LITTLE_ENDIAN
#define __LITTLE_ENDIAN 1234
#endif /* __LITTLE_ENDIAN */
#ifndef __BIG_ENDIAN
#define __BIG_ENDIAN 4321
#endif /* __BIG_ENDIAN */
#ifndef __BYTE_ORDER
#warning "Byte order not defined on your system; assuming little endian"
#define __BYTE_ORDER __LITTLE_ENDIAN
#endif /* __BYTE_ORDER */
#if __BYTE_ORDER != __BIG_ENDIAN && __BYTE_ORDER != __LITTLE_ENDIAN
#error "__BYTE_ORDER set to unknown byte order"
#endif
#if defined(IS_MACOSX)
#include <libkern/OSByteOrder.h>
/* OS X system functions. */
#define bitswap16(i) OSSwapInt16(i)
#define bitswap32(i) OSSwapInt32(i)
#define swapLittleAndHost32(i) OSSwapLittleToHostInt32(i)
#define swapLittleAndHost16(i) OSSwapLittleToHostInt16(i)
#else
#ifndef bitswap16
#if defined(bswap16)
#define bitswap16(i) bswap16(i) /* FreeBSD system function */
#elif defined(bswap_16)
#define bitswap16(i) bswap_16(i) /* Linux system function */
#else /* Default macro */
#define bitswap16(i) (((uint16_t)(i) & 0xFF00) >> 8) | \
(((uint16_t)(i) & 0x00FF) << 8)
#endif
#endif /* bitswap16 */
#ifndef bitswap32
#if defined(bswap32)
#define bitswap32(i) bswap32(i) /* FreeBSD system function. */
#elif defined(bswap_32)
#define bitswap32(i) bswap_32(i) /* Linux system function. */
#else /* Default macro */
#define bitswap32(i) (((uint32_t)(i) & 0xFF000000) >> 24) | \
((uint32_t)((i) & 0x00FF0000) >> 8) | \
((uint32_t)((i) & 0x0000FF00) << 8) | \
((uint32_t)((i) & 0x000000FF) << 24)
#endif
#endif /* bitswap32 */
#endif
#if __BYTE_ORDER == __BIG_ENDIAN
/* Little endian to/from host byte order (big endian). */
#ifndef swapLittleAndHost16
#define swapLittleAndHost16(i) bitswap16(i)
#endif /* swapLittleAndHost16 */
#ifndef swapLittleAndHost32
#define swapLittleAndHost32(i) bitswap32(i)
#endif /* swapLittleAndHost32 */
#elif __BYTE_ORDER == __LITTLE_ENDIAN
/* We are already little endian, so no conversion is needed. */
#ifndef swapLittleAndHost16
#define swapLittleAndHost16(i) i
#endif /* swapLittleAndHost16 */
#ifndef swapLittleAndHost32
#define swapLittleAndHost32(i) i
#endif /* swapLittleAndHost32 */
#endif
#endif /* ENDIAN_H */
================================================
FILE: src/inline_keywords.h
================================================
#pragma once
/* A complicated, portable model for declaring inline functions in
* header files. */
#if !defined(H_INLINE)
#if defined(__GNUC__)
#define H_INLINE static __inline__ __attribute__((always_inline))
#elif defined(__MWERKS__) || defined(__cplusplus)
#define H_INLINE static inline
#elif defined(_MSC_VER)
#define H_INLINE static __inline
#elif TARGET_OS_WIN32
#define H_INLINE static __inline__
#endif
#endif /* H_INLINE */
================================================
FILE: src/io.c
================================================
#include "io.h"
#include "os.h"
#include "bmp_io.h"
#include "png_io.h"
#include <stdio.h> /* For fputs() */
#include <string.h> /* For strcmp() */
#include <ctype.h> /* For tolower() */
const char *getExtension(const char *fname, size_t len)
{
if (fname == NULL || len <= 0) return NULL;
while (--len > 0 && fname[len] != '.' && fname[len] != '\0')
;
return fname + len + 1;
}
MMImageType imageTypeFromExtension(const char *extension)
{
char ext[4];
const size_t maxlen = sizeof(ext) / sizeof(ext[0]);
size_t i;
for (i = 0; extension[i] != '\0'; ++i) {
if (i >= maxlen) return kInvalidImageType;
ext[i] = tolower(extension[i]);
}
ext[i] = '\0';
if (strcmp(ext, "png") == 0) {
return kPNGImageType;
} else if (strcmp(ext, "bmp") == 0) {
return kBMPImageType;
} else {
return kInvalidImageType;
}
}
MMBitmapRef newMMBitmapFromFile(const char *path,
MMImageType type,
MMIOError *err)
{
switch (type) {
case kBMPImageType:
return newMMBitmapFromBMP(path, err);
case kPNGImageType:
return newMMBitmapFromPNG(path, err);
default:
if (err != NULL) *err = kMMIOUnsupportedTypeError;
return NULL;
}
}
int saveMMBitmapToFile(MMBitmapRef bitmap,
const char *path,
MMImageType type)
{
switch (type) {
case kBMPImageType:
return saveMMBitmapAsBMP(bitmap, path);
case kPNGImageType:
return saveMMBitmapAsPNG(bitmap, path);
default:
return -1;
}
}
const char *MMIOErrorString(MMImageType type, MMIOError error)
{
switch (type) {
case kBMPImageType:
return MMBMPReadErrorString(error);
case kPNGImageType:
return MMPNGReadErrorString(error);
default:
return "Unsupported image type";
}
}
================================================
FILE: src/io.h
================================================
#pragma once
#ifndef IO_H
#define IO_H
#include "MMBitmap.h"
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C"
{
#endif
enum _MMImageType {
kInvalidImageType = 0,
kPNGImageType,
kBMPImageType /* Currently only PNG and BMP are supported. */
};
typedef uint16_t MMImageType;
enum _MMIOError {
kMMIOUnsupportedTypeError = 0
};
typedef uint16_t MMIOError;
const char *getExtension(const char *fname, size_t len);
/* Returns best guess at the MMImageType based on a file extension, or
* |kInvalidImageType| if no matching type was found. */
MMImageType imageTypeFromExtension(const char *ext);
/* Attempts to parse the file of the given type at the given path.
* |filepath| is an ASCII string describing the absolute POSIX path.
* Returns new bitmap (to be destroy()'d by caller) on success, NULL on error.
* If |error| is non-NULL, it will be set to the error code on return.
*/
MMBitmapRef newMMBitmapFromFile(const char *path, MMImageType type, MMIOError *err);
/* Saves |bitmap| to a file of the given type at the given path.
* |filepath| is an ASCII string describing the absolute POSIX path.
* Returns 0 on success, -1 on error. */
int saveMMBitmapToFile(MMBitmapRef bitmap, const char *path, MMImageType type);
/* Returns description of given error code.
* Returned string is constant and hence should not be freed. */
const char *MMIOErrorString(MMImageType type, MMIOError error);
#ifdef __cplusplus
}
#endif
#endif /* IO_H */
================================================
FILE: src/keycode.c
================================================
#include "keycode.h"
#if defined(IS_MACOSX)
#include <CoreFoundation/CoreFoundation.h>
#include <Carbon/Carbon.h> /* For kVK_ constants, and TIS functions. */
/* Returns string representation of key, if it is printable.
* Ownership follows the Create Rule; that is, it is the caller's
* responsibility to release the returned object. */
CFStringRef createStringForKey(CGKeyCode keyCode);
#elif defined(USE_X11)
/*
* Structs to store key mappings not handled by XStringToKeysym() on some
* Linux systems.
*/
struct XSpecialCharacterMapping {
char name;
MMKeyCode code;
};
struct XSpecialCharacterMapping XSpecialCharacterTable[] = {
{'~', XK_asciitilde},
{'_', XK_underscore},
{'[', XK_bracketleft},
{']', XK_bracketright},
{'!', XK_exclam},
{'\'', XK_quotedbl},
{'#', XK_numbersign},
{'$', XK_dollar},
{'%', XK_percent},
{'&', XK_ampersand},
{'\'', XK_quoteright},
{'*', XK_asterisk},
{'+', XK_plus},
{',', XK_comma},
{'-', XK_minus},
{'.', XK_period},
{'?', XK_question},
{'<', XK_less},
{'>', XK_greater},
{'=', XK_equal},
{'@', XK_at},
{':', XK_colon},
{';', XK_semicolon},
{'\\', XK_backslash},
{'`', XK_grave},
{'{', XK_braceleft},
{'}', XK_braceright},
{'|', XK_bar},
{'^', XK_asciicircum},
{'(', XK_parenleft},
{')', XK_parenright},
{' ', XK_space},
{'/', XK_slash},
{'\t', XK_Tab},
{'\n', XK_Return}
};
#endif
MMKeyCode keyCodeForChar(const char c)
{
#if defined(IS_MACOSX)
/* OS X does not appear to have a built-in function for this, so instead we
* have to write our own. */
static CFMutableDictionaryRef charToCodeDict = NULL;
size_t code;
UniChar character = c;
CFStringRef charStr = NULL;
/* Generate table of keycodes and characters. */
if (charToCodeDict == NULL) {
size_t i;
charToCodeDict = CFDictionaryCreateMutable(kCFAllocatorDefault,
128,
&kCFCopyStringDictionaryKeyCallBacks,
NULL);
if (charToCodeDict == NULL) return UINT16_MAX;
/* Loop through every keycode (0 - 127) to find its current mapping. */
for (i = 0; i < 128; ++i) {
CFStringRef string = createStringForKey((CGKeyCode)i);
if (string != NULL) {
CFDictionaryAddValue(charToCodeDict, string, (const void *)i);
CFRelease(string);
}
}
}
charStr = CFStringCreateWithCharacters(kCFAllocatorDefault, &character, 1);
/* Our values may be NULL (0), so we need to use this function. */
if (!CFDictionaryGetValueIfPresent(charToCodeDict, charStr,
(const void **)&code)) {
code = UINT16_MAX; /* Error */
}
CFRelease(charStr);
return (MMKeyCode)code;
#elif defined(IS_WINDOWS)
return VkKeyScan(c);
#elif defined(USE_X11)
MMKeyCode code;
char buf[2];
buf[0] = c;
buf[1] = '\0';
code = XStringToKeysym(buf);
if (code == NoSymbol) {
/* Some special keys are apparently not handled properly by
* XStringToKeysym() on some systems, so search for them instead in our
* mapping table. */
size_t i;
const size_t specialCharacterCount =
sizeof(XSpecialCharacterTable) / sizeof(XSpecialCharacterTable[0]);
for (i = 0; i < specialCharacterCount; ++i) {
if (c == XSpecialCharacterTable[i].name) {
code = XSpecialCharacterTable[i].code;
break;
}
}
}
return code;
#endif
}
#if defined(IS_MACOSX)
CFStringRef createStringForKey(CGKeyCode keyCode)
{
TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardLayoutInputSource();
CFDataRef layoutData =
TISGetInputSourceProperty(currentKeyboard,
kTISPropertyUnicodeKeyLayoutData);
const UCKeyboardLayout *keyboardLayout =
(const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
UInt32 keysDown = 0;
UniChar chars[4];
UniCharCount realLength;
UCKeyTranslate(keyboardLayout,
keyCode,
kUCKeyActionDisplay,
0,
LMGetKbdType(),
kUCKeyTranslateNoDeadKeysBit,
&keysDown,
sizeof(chars) / sizeof(chars[0]),
&realLength,
chars);
CFRelease(currentKeyboard);
return CFStringCreateWithCharacters(kCFAllocatorDefault, chars, 1);
}
#endif
================================================
FILE: src/keycode.h
================================================
#pragma once
#ifndef KEYCODE_H
#define KEYCODE_H
#include "os.h"
#ifdef __cplusplus
extern "C"
{
#endif
#if defined(IS_MACOSX)
#include <Carbon/Carbon.h> /* Really only need <HIToolbox/Events.h> */
#include <ApplicationServices/ApplicationServices.h>
#import <IOKit/hidsystem/ev_keymap.h>
enum _MMKeyCode {
K_NOT_A_KEY = 9999,
K_BACKSPACE = kVK_Delete,
K_DELETE = kVK_ForwardDelete,
K_RETURN = kVK_Return,
K_TAB = kVK_Tab,
K_ESCAPE = kVK_Escape,
K_UP = kVK_UpArrow,
K_DOWN = kVK_DownArrow,
K_RIGHT = kVK_RightArrow,
K_LEFT = kVK_LeftArrow,
K_HOME = kVK_Home,
K_END = kVK_End,
K_PAGEUP = kVK_PageUp,
K_PAGEDOWN = kVK_PageDown,
K_F1 = kVK_F1,
K_F2 = kVK_F2,
K_F3 = kVK_F3,
K_F4 = kVK_F4,
K_F5 = kVK_F5,
K_F6 = kVK_F6,
K_F7 = kVK_F7,
K_F8 = kVK_F8,
K_F9 = kVK_F9,
K_F10 = kVK_F10,
K_F11 = kVK_F11,
K_F12 = kVK_F12,
K_F13 = kVK_F13,
K_F14 = kVK_F14,
K_F15 = kVK_F15,
K_F16 = kVK_F16,
K_F17 = kVK_F17,
K_F18 = kVK_F18,
K_F19 = kVK_F19,
K_F20 = kVK_F20,
K_F21 = K_NOT_A_KEY,
K_F22 = K_NOT_A_KEY,
K_F23 = K_NOT_A_KEY,
K_F24 = K_NOT_A_KEY,
K_META = kVK_Command,
K_ALT = kVK_Option,
K_RIGHT_ALT = kVK_Option,
K_CONTROL = kVK_Control,
K_LEFT_CONTROL = kVK_Control,
K_RIGHT_CONTROL = kVK_RightControl,
K_SHIFT = kVK_Shift,
K_RIGHTSHIFT = kVK_RightShift,
K_CAPSLOCK = kVK_CapsLock,
K_SPACE = kVK_Space,
K_INSERT = K_NOT_A_KEY,
K_PRINTSCREEN = K_NOT_A_KEY,
K_MENU = K_NOT_A_KEY,
K_NUMPAD_LOCK = K_NOT_A_KEY,
K_NUMPAD_0 = kVK_ANSI_Keypad0,
K_NUMPAD_1 = kVK_ANSI_Keypad1,
K_NUMPAD_2 = kVK_ANSI_Keypad2,
K_NUMPAD_3 = kVK_ANSI_Keypad3,
K_NUMPAD_4 = kVK_ANSI_Keypad4,
K_NUMPAD_5 = kVK_ANSI_Keypad5,
K_NUMPAD_6 = kVK_ANSI_Keypad6,
K_NUMPAD_7 = kVK_ANSI_Keypad7,
K_NUMPAD_8 = kVK_ANSI_Keypad8,
K_NUMPAD_9 = kVK_ANSI_Keypad9,
K_NUMPAD_PLUS = kVK_ANSI_KeypadPlus,
K_NUMPAD_MINUS = kVK_ANSI_KeypadMinus,
K_NUMPAD_MULTIPLY = kVK_ANSI_KeypadMultiply,
K_NUMPAD_DIVIDE = kVK_ANSI_KeypadDivide,
K_NUMPAD_DECIMAL = kVK_ANSI_KeypadDecimal,
K_AUDIO_VOLUME_MUTE = 1007,
K_AUDIO_VOLUME_DOWN = 1001,
K_AUDIO_VOLUME_UP = 1000,
K_AUDIO_PLAY = 1016,
K_AUDIO_STOP = K_NOT_A_KEY,
K_AUDIO_PAUSE = 1016,
K_AUDIO_PREV = 1018,
K_AUDIO_NEXT = 1017,
K_AUDIO_REWIND = K_NOT_A_KEY,
K_AUDIO_FORWARD = K_NOT_A_KEY,
K_AUDIO_REPEAT = K_NOT_A_KEY,
K_AUDIO_RANDOM = K_NOT_A_KEY,
K_LIGHTS_MON_UP = 1002,
K_LIGHTS_MON_DOWN = 1003,
K_LIGHTS_KBD_TOGGLE = 1023,
K_LIGHTS_KBD_UP = 1021,
K_LIGHTS_KBD_DOWN = 1022
};
typedef CGKeyCode MMKeyCode;
#elif defined(USE_X11)
#include <X11/Xutil.h>
#include <X11/XF86keysym.h>
enum _MMKeyCode {
K_NOT_A_KEY = 9999,
K_BACKSPACE = XK_BackSpace,
K_DELETE = XK_Delete,
K_RETURN = XK_Return,
K_TAB = XK_Tab,
K_ESCAPE = XK_Escape,
K_UP = XK_Up,
K_DOWN = XK_Down,
K_RIGHT = XK_Right,
K_LEFT = XK_Left,
K_HOME = XK_Home,
K_END = XK_End,
K_PAGEUP = XK_Page_Up,
K_PAGEDOWN = XK_Page_Down,
K_F1 = XK_F1,
K_F2 = XK_F2,
K_F3 = XK_F3,
K_F4 = XK_F4,
K_F5 = XK_F5,
K_F6 = XK_F6,
K_F7 = XK_F7,
K_F8 = XK_F8,
K_F9 = XK_F9,
K_F10 = XK_F10,
K_F11 = XK_F11,
K_F12 = XK_F12,
K_F13 = XK_F13,
K_F14 = XK_F14,
K_F15 = XK_F15,
K_F16 = XK_F16,
K_F17 = XK_F17,
K_F18 = XK_F18,
K_F19 = XK_F19,
K_F20 = XK_F20,
K_F21 = XK_F21,
K_F22 = XK_F22,
K_F23 = XK_F23,
K_F24 = XK_F24,
K_META = XK_Super_L,
K_ALT = XK_Alt_L,
K_RIGHT_ALT = XK_Alt_R,
K_CONTROL = XK_Control_L,
K_LEFT_CONTROL = XK_Control_L,
K_RIGHT_CONTROL = XK_Control_R,
K_SHIFT = XK_Shift_L,
K_RIGHTSHIFT = XK_Shift_R,
K_CAPSLOCK = XK_Shift_Lock,
K_SPACE = XK_space,
K_INSERT = XK_Insert,
K_PRINTSCREEN = XK_Print,
K_MENU = K_NOT_A_KEY,
K_NUMPAD_LOCK = K_NOT_A_KEY,
K_NUMPAD_0 = K_NOT_A_KEY,
K_NUMPAD_1 = K_NOT_A_KEY,
K_NUMPAD_2 = K_NOT_A_KEY,
K_NUMPAD_3 = K_NOT_A_KEY,
K_NUMPAD_4 = K_NOT_A_KEY,
K_NUMPAD_5 = K_NOT_A_KEY,
K_NUMPAD_6 = K_NOT_A_KEY,
K_NUMPAD_7 = K_NOT_A_KEY,
K_NUMPAD_8 = K_NOT_A_KEY,
K_NUMPAD_9 = K_NOT_A_KEY,
K_NUMPAD_PLUS = K_NOT_A_KEY,
K_NUMPAD_MINUS = K_NOT_A_KEY,
K_NUMPAD_MULTIPLY = K_NOT_A_KEY,
K_NUMPAD_DIVIDE = K_NOT_A_KEY,
K_NUMPAD_DECIMAL = K_NOT_A_KEY,
K_AUDIO_VOLUME_MUTE = XF86XK_AudioMute,
K_AUDIO_VOLUME_DOWN = XF86XK_AudioLowerVolume,
K_AUDIO_VOLUME_UP = XF86XK_AudioRaiseVolume,
K_AUDIO_PLAY = XF86XK_AudioPlay,
K_AUDIO_STOP = XF86XK_AudioStop,
K_AUDIO_PAUSE = XF86XK_AudioPause,
K_AUDIO_PREV = XF86XK_AudioPrev,
K_AUDIO_NEXT = XF86XK_AudioNext,
K_AUDIO_REWIND = XF86XK_AudioRewind,
K_AUDIO_FORWARD = XF86XK_AudioForward,
K_AUDIO_REPEAT = XF86XK_AudioRepeat,
K_AUDIO_RANDOM = XF86XK_AudioRandomPlay,
K_LIGHTS_MON_UP = XF86XK_MonBrightnessUp,
K_LIGHTS_MON_DOWN = XF86XK_MonBrightnessDown,
K_LIGHTS_KBD_TOGGLE = XF86XK_KbdLightOnOff,
K_LIGHTS_KBD_UP = XF86XK_KbdBrightnessUp,
K_LIGHTS_KBD_DOWN = XF86XK_KbdBrightnessDown
};
typedef KeySym MMKeyCode;
#elif defined(IS_WINDOWS)
enum _MMKeyCode {
K_NOT_A_KEY = 9999,
K_BACKSPACE = VK_BACK,
K_DELETE = VK_DELETE,
K_RETURN = VK_RETURN,
K_TAB = VK_TAB,
K_ESCAPE = VK_ESCAPE,
K_UP = VK_UP,
K_DOWN = VK_DOWN,
K_RIGHT = VK_RIGHT,
K_LEFT = VK_LEFT,
K_HOME = VK_HOME,
K_END = VK_END,
K_PAGEUP = VK_PRIOR,
K_PAGEDOWN = VK_NEXT,
K_F1 = VK_F1,
K_F2 = VK_F2,
K_F3 = VK_F3,
K_F4 = VK_F4,
K_F5 = VK_F5,
K_F6 = VK_F6,
K_F7 = VK_F7,
K_F8 = VK_F8,
K_F9 = VK_F9,
K_F10 = VK_F10,
K_F11 = VK_F11,
K_F12 = VK_F12,
K_F13 = VK_F13,
K_F14 = VK_F14,
K_F15 = VK_F15,
K_F16 = VK_F16,
K_F17 = VK_F17,
K_F18 = VK_F18,
K_F19 = VK_F19,
K_F20 = VK_F20,
K_F21 = VK_F21,
K_F22 = VK_F22,
K_F23 = VK_F23,
K_F24 = VK_F24,
K_META = VK_LWIN,
K_CONTROL = VK_CONTROL,
K_LEFT_CONTROL = VK_LCONTROL,
K_RIGHT_CONTROL = VK_RCONTROL,
K_SHIFT = VK_SHIFT,
K_RIGHTSHIFT = VK_RSHIFT,
K_ALT = VK_MENU,
K_RIGHT_ALT = VK_MENU,
K_CAPSLOCK = VK_CAPITAL,
K_SPACE = VK_SPACE,
K_PRINTSCREEN = VK_SNAPSHOT,
K_INSERT = VK_INSERT,
K_MENU = VK_APPS,
K_NUMPAD_LOCK = VK_NUMLOCK,
K_NUMPAD_0 = VK_NUMPAD0,
K_NUMPAD_1 = VK_NUMPAD1,
K_NUMPAD_2 = VK_NUMPAD2,
K_NUMPAD_3 = VK_NUMPAD3,
K_NUMPAD_4 = VK_NUMPAD4,
K_NUMPAD_5 = VK_NUMPAD5,
K_NUMPAD_6 = VK_NUMPAD6,
K_NUMPAD_7 = VK_NUMPAD7,
K_NUMPAD_8 = VK_NUMPAD8,
K_NUMPAD_9 = VK_NUMPAD9,
K_NUMPAD_PLUS = VK_ADD,
K_NUMPAD_MINUS = VK_SUBTRACT,
K_NUMPAD_MULTIPLY = VK_MULTIPLY,
K_NUMPAD_DIVIDE = VK_DIVIDE,
K_NUMPAD_DECIMAL = VK_DECIMAL,
K_AUDIO_VOLUME_MUTE = VK_VOLUME_MUTE,
K_AUDIO_VOLUME_DOWN = VK_VOLUME_DOWN,
K_AUDIO_VOLUME_UP = VK_VOLUME_UP,
K_AUDIO_PLAY = VK_MEDIA_PLAY_PAUSE,
K_AUDIO_STOP = VK_MEDIA_STOP,
K_AUDIO_PAUSE = VK_MEDIA_PLAY_PAUSE,
K_AUDIO_PREV = VK_MEDIA_PREV_TRACK,
K_AUDIO_NEXT = VK_MEDIA_NEXT_TRACK,
K_AUDIO_REWIND = K_NOT_A_KEY,
K_AUDIO_FORWARD = K_NOT_A_KEY,
K_AUDIO_REPEAT = K_NOT_A_KEY,
K_AUDIO_RANDOM = K_NOT_A_KEY,
K_LIGHTS_MON_UP = K_NOT_A_KEY,
K_LIGHTS_MON_DOWN = K_NOT_A_KEY,
K_LIGHTS_KBD_TOGGLE = K_NOT_A_KEY,
K_LIGHTS_KBD_UP = K_NOT_A_KEY,
K_LIGHTS_KBD_DOWN = K_NOT_A_KEY
};
typedef int MMKeyCode;
#endif
/* Returns the keyCode corresponding to the current keyboard layout for the
* given ASCII character. */
MMKeyCode keyCodeForChar(const char c);
#endif /* KEYCODE_H */
#ifdef __cplusplus
}
#endif
================================================
FILE: src/keypress.c
================================================
#include "keypress.h"
#include "deadbeef_rand.h"
#include "microsleep.h"
#include <ctype.h> /* For isupper() */
#if defined(IS_MACOSX)
#include <ApplicationServices/ApplicationServices.h>
#import <IOKit/hidsystem/IOHIDLib.h>
#import <IOKit/hidsystem/ev_keymap.h>
#elif defined(USE_X11)
#include <X11/extensions/XTest.h>
#include "xdisplay.h"
#endif
/* Convenience wrappers around ugly APIs. */
#if defined(IS_WINDOWS)
#define WIN32_KEY_EVENT_WAIT(key, flags) \
(win32KeyEvent(key, flags))
#elif defined(USE_X11)
#define X_KEY_EVENT(display, key, is_press) \
(XTestFakeKeyEvent(display, \
XKeysymToKeycode(display, key), \
is_press, CurrentTime), \
XFlush(display))
#define X_KEY_EVENT_WAIT(display, key, is_press) \
(X_KEY_EVENT(display, key, is_press))
#endif
#if defined(IS_MACOSX)
static io_connect_t _getAuxiliaryKeyDriver(void)
{
static mach_port_t sEventDrvrRef = 0;
mach_port_t masterPort, service, iter;
kern_return_t kr;
if (!sEventDrvrRef) {
kr = IOMasterPort( bootstrap_port, &masterPort );
assert(KERN_SUCCESS == kr);
kr = IOServiceGetMatchingServices(masterPort, IOServiceMatching( kIOHIDSystemClass), &iter );
assert(KERN_SUCCESS == kr);
service = IOIteratorNext( iter );
assert(service);
kr = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &sEventDrvrRef );
assert(KERN_SUCCESS == kr);
IOObjectRelease(service);
IOObjectRelease(iter);
}
return sEventDrvrRef;
}
#endif
#if defined(IS_WINDOWS)
void win32KeyEvent(int key, MMKeyFlags flags)
{
int scan = MapVirtualKey(key & 0xff, MAPVK_VK_TO_VSC);
/* Set the scan code for extended keys */
switch (key)
{
case VK_RCONTROL:
case VK_SNAPSHOT: /* Print Screen */
case VK_RMENU: /* Right Alt / Alt Gr */
case VK_PAUSE: /* Pause / Break */
case VK_HOME:
case VK_UP:
case VK_PRIOR: /* Page up */
case VK_LEFT:
case VK_RIGHT:
case VK_END:
case VK_DOWN:
case VK_NEXT: /* 'Page Down' */
case VK_INSERT:
case VK_DELETE:
case VK_LWIN:
case VK_RWIN:
case VK_APPS: /* Application */
case VK_VOLUME_MUTE:
case VK_VOLUME_DOWN:
case VK_VOLUME_UP:
case VK_MEDIA_NEXT_TRACK:
case VK_MEDIA_PREV_TRACK:
case VK_MEDIA_STOP:
case VK_MEDIA_PLAY_PAUSE:
case VK_BROWSER_BACK:
case VK_BROWSER_FORWARD:
case VK_BROWSER_REFRESH:
case VK_BROWSER_STOP:
case VK_BROWSER_SEARCH:
case VK_BROWSER_FAVORITES:
case VK_BROWSER_HOME:
case VK_LAUNCH_MAIL:
{
flags |= KEYEVENTF_EXTENDEDKEY;
break;
}
}
flags |= KEYEVENTF_SCANCODE;
INPUT keyboardInput;
keyboardInput.type = INPUT_KEYBOARD;
keyboardInput.ki.wVk = 0;
keyboardInput.ki.wScan = scan;
keyboardInput.ki.dwFlags = flags;
keyboardInput.ki.time = 0;
keyboardInput.ki.dwExtraInfo = 0;
SendInput(1, &keyboardInput, sizeof(keyboardInput));
}
#endif
void toggleKeyCode(MMKeyCode code, const bool down, MMKeyFlags flags)
{
#if defined(IS_MACOSX)
/* The media keys all have 1000 added to them to help us detect them. */
if (code >= 1000) {
code = code - 1000; /* Get the real keycode. */
NXEventData event;
kern_return_t kr;
IOGPoint loc = { 0, 0 };
UInt32 evtInfo = code << 16 | (down?NX_KEYDOWN:NX_KEYUP) << 8;
bzero(&event, sizeof(NXEventData));
event.compound.subType = NX_SUBTYPE_AUX_CONTROL_BUTTONS;
event.compound.misc.L[0] = evtInfo;
kr = IOHIDPostEvent( _getAuxiliaryKeyDriver(), NX_SYSDEFINED, loc, &event, kNXEventDataVersion, 0, FALSE );
assert( KERN_SUCCESS == kr );
} else {
CGEventRef keyEvent = CGEventCreateKeyboardEvent(NULL,
(CGKeyCode)code, down);
assert(keyEvent != NULL);
CGEventSetType(keyEvent, down ? kCGEventKeyDown : kCGEventKeyUp);
CGEventSetFlags(keyEvent, flags);
CGEventPost(kCGSessionEventTap, keyEvent);
CFRelease(keyEvent);
}
#elif defined(IS_WINDOWS)
const DWORD dwFlags = down ? 0 : KEYEVENTF_KEYUP;
if (down) {
/* Parse modifier keys. */
if (flags & MOD_META) WIN32_KEY_EVENT_WAIT(K_META, dwFlags);
if (flags & MOD_ALT) WIN32_KEY_EVENT_WAIT(K_ALT, dwFlags);
if (flags & MOD_CONTROL) WIN32_KEY_EVENT_WAIT(K_CONTROL, dwFlags);
if (flags & MOD_SHIFT) WIN32_KEY_EVENT_WAIT(K_SHIFT, dwFlags);
WIN32_KEY_EVENT_WAIT(code, dwFlags);
} else {
/* Reverse order for key up */
WIN32_KEY_EVENT_WAIT(code, dwFlags);
/* Parse modifier keys. */
if (flags & MOD_META) win32KeyEvent(K_META, dwFlags);
if (flags & MOD_ALT) win32KeyEvent(K_ALT, dwFlags);
if (flags & MOD_CONTROL) win32KeyEvent(K_CONTROL, dwFlags);
if (flags & MOD_SHIFT) win32KeyEvent(K_SHIFT, dwFlags);
}
#elif defined(USE_X11)
Display *display = XGetMainDisplay();
const Bool is_press = down ? True : False; /* Just to be safe. */
if (down) {
/* Parse modifier keys. */
if (flags & MOD_META) X_KEY_EVENT_WAIT(display, K_META, is_press);
if (flags & MOD_ALT) X_KEY_EVENT_WAIT(display, K_ALT, is_press);
if (flags & MOD_CONTROL) X_KEY_EVENT_WAIT(display, K_CONTROL, is_press);
if (flags & MOD_SHIFT) X_KEY_EVENT_WAIT(display, K_SHIFT, is_press);
X_KEY_EVENT_WAIT(display, code, is_press);
} else {
/* Reverse order for key up */
X_KEY_EVENT_WAIT(display, code, is_press);
/* Parse modifier keys. */
if (flags & MOD_META) X_KEY_EVENT(display, K_META, is_press);
if (flags & MOD_ALT) X_KEY_EVENT(display, K_ALT, is_press);
if (flags & MOD_CONTROL) X_KEY_EVENT(display, K_CONTROL, is_press);
if (flags & MOD_SHIFT) X_KEY_EVENT(display, K_SHIFT, is_press);
}
#endif
}
void tapKeyCode(MMKeyCode code, MMKeyFlags flags)
{
toggleKeyCode(code, true, flags);
toggleKeyCode(code, false, flags);
}
void toggleKey(char c, const bool down, MMKeyFlags flags)
{
MMKeyCode keyCode = keyCodeForChar(c);
//Prevent unused variable warning for Mac and Linux.
#if defined(IS_WINDOWS)
int modifiers;
#endif
if (isupper(c) && !(flags & MOD_SHIFT)) {
flags |= MOD_SHIFT; /* Not sure if this is safe for all layouts. */
}
#if defined(IS_WINDOWS)
modifiers = keyCode >> 8; // Pull out modifers.
if ((modifiers & 1) != 0) flags |= MOD_SHIFT; // Uptdate flags from keycode modifiers.
if ((modifiers & 2) != 0) flags |= MOD_CONTROL;
if ((modifiers & 4) != 0) flags |= MOD_ALT;
keyCode = keyCode & 0xff; // Mask out modifiers.
#endif
toggleKeyCode(keyCode, down, flags);
}
void tapKey(char c, MMKeyFlags flags)
{
toggleKey(c, true, flags);
toggleKey(c, false, flags);
}
#if defined(IS_MACOSX)
void toggleUnicode(UniChar ch, const bool down)
{
/* This function relies on the convenient
* CGEventKeyboardSetUnicodeString(), which allows us to not have to
* convert characters to a keycode, but does not support adding modifier
* flags. It is therefore only used in typeStringDelayed()
* -- if you need modifier keys, use the above functions instead. */
CGEventRef keyEvent = CGEventCreateKeyboardEvent(NULL, 0, down);
if (keyEvent == NULL) {
fputs("Could not create keyboard event.\n", stderr);
return;
}
if (ch > 0xFFFF) {
// encode to utf-16 if necessary
UniChar surrogates[2] = {
0xD800 + ((ch - 0x10000) >> 10),
0xDC00 + (ch & 0x3FF)
};
CGEventKeyboardSetUnicodeString(keyEvent, 2, (UniChar*) &surrogates);
} else {
CGEventKeyboardSetUnicodeString(keyEvent, 1, (UniChar*) &ch);
}
CGEventPost(kCGSessionEventTap, keyEvent);
CFRelease(keyEvent);
}
#elif defined(USE_X11)
#define toggleUniKey(c, down) toggleKey(c, down, MOD_NONE)
#endif
void unicodeTap(const unsigned value)
{
#if defined(USE_X11)
char ch = (char)value;
toggleUniKey(ch, true);
toggleUniKey(ch, false);
#elif defined(IS_MACOSX)
UniChar ch = (UniChar)value; // Convert to unsigned char
toggleUnicode(ch, true);
toggleUnicode(ch, false);
#elif defined(IS_WINDOWS)
INPUT ip;
// Set up a generic keyboard event.
ip.type = INPUT_KEYBOARD;
ip.ki.wVk = 0; // Virtual-key code
ip.ki.wScan = value; // Hardware scan code for key
ip.ki.time = 0; // System will provide its own time stamp.
ip.ki.dwExtraInfo = 0; // No extra info. Use the GetMessageExtraInfo function to obtain this information if needed.
ip.ki.dwFlags = KEYEVENTF_UNICODE; // KEYEVENTF_KEYUP for key release.
SendInput(1, &ip, sizeof(INPUT));
#endif
}
void typeStringDelayed(const char *str, const unsigned cpm)
{
unsigned long n;
unsigned short c;
unsigned short c1;
unsigned short c2;
unsigned short c3;
/* Characters per second */
const double cps = (double)cpm / 60.0;
/* Average milli-seconds per character */
const double mspc = (cps == 0.0) ? 0.0 : 1000.0 / cps;
while (*str != '\0') {
c = *str++;
// warning, the following utf8 decoder
// doesn't perform validation
if (c <= 0x7F) {
// 0xxxxxxx one byte
n = c;
} else if ((c & 0xE0) == 0xC0) {
// 110xxxxx two bytes
c1 = (*str++) & 0x3F;
n = ((c & 0x1F) << 6) | c1;
} else if ((c & 0xF0) == 0xE0) {
// 1110xxxx three bytes
c1 = (*str++) & 0x3F;
c2 = (*str++) & 0x3F;
n = ((c & 0x0F) << 12) | (c1 << 6) | c2;
} else if ((c & 0xF8) == 0xF0) {
// 11110xxx four bytes
c1 = (*str++) & 0x3F;
c2 = (*str++) & 0x3F;
c3 = (*str++) & 0x3F;
n = ((c & 0x07) << 18) | (c1 << 12) | (c2 << 6) | c3;
}
unicodeTap(n);
if (mspc > 0) {
microsleep(mspc);
}
}
}
================================================
FILE: src/keypress.h
================================================
#pragma once
#ifndef KEYPRESS_H
#define KEYPRESS_H
#include "os.h"
#include "keycode.h"
#if defined(_MSC_VER)
#include "ms_stdbool.h"
#else
#include <stdbool.h>
#endif
#ifdef __cplusplus
extern "C"
{
#endif
#if defined(IS_MACOSX)
typedef enum {
MOD_NONE = 0,
MOD_META = kCGEventFlagMaskCommand,
MOD_ALT = kCGEventFlagMaskAlternate,
MOD_CONTROL = kCGEventFlagMaskControl,
MOD_SHIFT = kCGEventFlagMaskShift
} MMKeyFlags;
#elif defined(USE_X11)
enum _MMKeyFlags {
MOD_NONE = 0,
MOD_META = Mod4Mask,
MOD_ALT = Mod1Mask,
MOD_CONTROL = ControlMask,
MOD_SHIFT = ShiftMask
};
typedef unsigned int MMKeyFlags;
#elif defined(IS_WINDOWS)
enum _MMKeyFlags {
MOD_NONE = 0,
/* These are already defined by the Win32 API */
/* MOD_ALT = 0,
MOD_CONTROL = 0,
MOD_SHIFT = 0, */
MOD_META = MOD_WIN
};
typedef unsigned int MMKeyFlags;
#endif
#if defined(IS_WINDOWS)
/* Send win32 key event for given key. */
void win32KeyEvent(int key, MMKeyFlags flags);
#endif
/* Toggles the given key down or up. */
void toggleKeyCode(MMKeyCode code, const bool down, MMKeyFlags flags);
/* Toggles the key down and then up. */
void tapKeyCode(MMKeyCode code, MMKeyFlags flags);
/* Toggles the key corresponding to the given UTF character up or down. */
void toggleKey(char c, const bool down, MMKeyFlags flags);
void tapKey(char c, MMKeyFlags flags);
/* Sends a Unicode character without modifiers. */
void unicodeTap(const unsigned value);
/* Macro to convert WPM to CPM integers.
* (the average English word length is 5.1 characters.) */
#define WPM_TO_CPM(WPM) (unsigned)(5.1 * WPM)
/* Sends a UTF-8 string without modifiers and with partially random delays between each letter.
* Note that deadbeef_srand() must be called before this function if you actually want
* randomness. */
void typeStringDelayed(const char *str, const unsigned cpm);
#ifdef __cplusplus
}
#endif
#endif /* KEYPRESS_H */
================================================
FILE: src/microsleep.h
================================================
#pragma once
#ifndef MICROSLEEP_H
#define MICROSLEEP_H
#include "os.h"
#include "inline_keywords.h"
#if !defined(IS_WINDOWS)
/* Make sure nanosleep gets defined even when using C89. */
#if !defined(__USE_POSIX199309) || !__USE_POSIX199309
#define __USE_POSIX199309 1
#endif
#include <time.h> /* For nanosleep() */
#endif
/*
* A more widely supported alternative to usleep(), based on Sleep() in Windows
* and nanosleep() everywhere else.
*
* Pauses execution for the given amount of milliseconds.
*/
H_INLINE void microsleep(double milliseconds)
{
#if defined(IS_WINDOWS)
Sleep((DWORD)milliseconds); /* (Unfortunately truncated to a 32-bit integer.) */
#else
/* Technically, nanosleep() is not an ANSI function, but it is the most
* supported precise sleeping function I can find.
*
* If it is really necessary, it may be possible to emulate this with some
* hack using select() in the future if we really have to. */
struct timespec sleepytime;
sleepytime.tv_sec = milliseconds / 1000;
sleepytime.tv_nsec = (milliseconds - (sleepytime.tv_sec * 1000)) * 1000000;
nanosleep(&sleepytime, NULL);
#endif
}
#endif /* MICROSLEEP_H */
================================================
FILE: src/mouse.c
================================================
#include "mouse.h"
#include "screen.h"
#include "deadbeef_rand.h"
#include "microsleep.h"
#include <math.h> /* For floor() */
#if defined(IS_MACOSX)
#include <ApplicationServices/ApplicationServices.h>
#elif defined(USE_X11)
#include <X11/Xlib.h>
#include <X11/extensions/XTest.h>
#include <stdlib.h>
#include "xdisplay.h"
#endif
#if !defined(M_SQRT2)
#define M_SQRT2 1.4142135623730950488016887 /* Fix for MSVC. */
#endif
/* Some convenience macros for converting our enums to the system API types. */
#if defined(IS_MACOSX)
#define MMMouseToCGEventType(down, button) \
(down ? MMMouseDownToCGEventType(button) : MMMouseUpToCGEventType(button))
#define MMMouseDownToCGEventType(button) \
((button) == (LEFT_BUTTON) ? kCGEventLeftMouseDown \
: ((button) == RIGHT_BUTTON ? kCGEventRightMouseDown \
: kCGEventOtherMouseDown))
#define MMMouseUpToCGEventType(button) \
((button) == LEFT_BUTTON ? kCGEventLeftMouseUp \
: ((button) == RIGHT_BUTTON ? kCGEventRightMouseUp \
: kCGEventOtherMouseUp))
#define MMMouseDragToCGEventType(button) \
((button) == LEFT_BUTTON ? kCGEventLeftMouseDragged \
: ((button) == RIGHT_BUTTON ? kCGEventRightMouseDragged \
: kCGEventOtherMouseDragged))
#elif defined(IS_WINDOWS)
// The width of the virtual screen, in pixels.
static int vscreenWidth = -1; // not initialized
// The height of the virtual screen, in pixels.
static int vscreenHeight = -1; // not initialized
// The coordinates for the left side of the virtual screen.
static int vscreenMinX = 0;
// The coordinates for the top of the virtual screen.
static int vscreenMinY = 0;
#define MMMouseToMEventF(down, button) \
(down ? MMMouseDownToMEventF(button) : MMMouseUpToMEventF(button))
#define MMMouseUpToMEventF(button) \
((button) == LEFT_BUTTON ? MOUSEEVENTF_LEFTUP \
: ((button) == RIGHT_BUTTON ? MOUSEEVENTF_RIGHTUP \
: MOUSEEVENTF_MIDDLEUP))
#define MMMouseDownToMEventF(button) \
((button) == LEFT_BUTTON ? MOUSEEVENTF_LEFTDOWN \
: ((button) == RIGHT_BUTTON ? MOUSEEVENTF_RIGHTDOWN \
: MOUSEEVENTF_MIDDLEDOWN))
#endif
#if defined(IS_MACOSX)
/**
* Calculate the delta for a mouse move and add them to the event.
* @param event The mouse move event (by ref).
* @param point The new mouse x and y.
*/
void calculateDeltas(CGEventRef *event, MMSignedPoint point)
{
/**
* The next few lines are a workaround for games not detecting mouse moves.
* See this issue for more information:
* https://github.com/octalmage/robotjs/issues/159
*/
CGEventRef get = CGEventCreate(NULL);
CGPoint mouse = CGEventGetLocation(get);
// Calculate the deltas.
int64_t deltaX = point.x - mouse.x;
int64_t deltaY = point.y - mouse.y;
CGEventSetIntegerValueField(*event, kCGMouseEventDeltaX, deltaX);
CGEventSetIntegerValueField(*event, kCGMouseEventDeltaY, deltaY);
CFRelease(get);
}
#endif
void updateScreenMetrics()
{
#if defined(IS_WINDOWS)
vscreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
vscreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
vscreenMinX = GetSystemMetrics(SM_XVIRTUALSCREEN);
vscreenMinY = GetSystemMetrics(SM_YVIRTUALSCREEN);
#endif
}
/**
* Move the mouse to a specific point.
* @param point The coordinates to move the mouse to (x, y).
*/
void moveMouse(MMSignedPoint point)
{
#if defined(IS_MACOSX)
CGEventRef move = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved,
CGPointFromMMSignedPoint(point),
kCGMouseButtonLeft);
calculateDeltas(&move, point);
CGEventPost(kCGSessionEventTap, move);
CFRelease(move);
#elif defined(USE_X11)
Display *display = XGetMainDisplay();
XWarpPointer(display, None, DefaultRootWindow(display),
0, 0, 0, 0, point.x, point.y);
XFlush(display);
#elif defined(IS_WINDOWS)
if(vscreenWidth<0 || vscreenHeight<0)
updateScreenMetrics();
//Mouse motion is now done using SendInput with MOUSEINPUT. We use Absolute mouse positioning
#define MOUSE_COORD_TO_ABS(coord, width_or_height) ((65536 * (coord) / width_or_height) + ((coord) < 0 ? -1 : 1))
size_t x = MOUSE_COORD_TO_ABS(point.x-vscreenMinX, vscreenWidth);
size_t y = MOUSE_COORD_TO_ABS(point.y-vscreenMinY, vscreenHeight);
INPUT mouseInput = {0};
mouseInput.type = INPUT_MOUSE;
mouseInput.mi.dx = x;
mouseInput.mi.dy = y;
mouseInput.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE | MOUSEEVENTF_VIRTUALDESK;
mouseInput.mi.time = 0; //System will provide the timestamp
SendInput(1, &mouseInput, sizeof(mouseInput));
#endif
}
void dragMouse(MMSignedPoint point, const MMMouseButton button)
{
#if defined(IS_MACOSX)
const CGEventType dragType = MMMouseDragToCGEventType(button);
CGEventRef drag = CGEventCreateMouseEvent(NULL, dragType,
CGPointFromMMSignedPoint(point),
(CGMouseButton)button);
calculateDeltas(&drag, point);
CGEventPost(kCGSessionEventTap, drag);
CFRelease(drag);
#else
moveMouse(point);
#endif
}
MMSignedPoint getMousePos()
{
#if defined(IS_MACOSX)
CGEventRef event = CGEventCreate(NULL);
CGPoint point = CGEventGetLocation(event);
CFRelease(event);
return MMSignedPointFromCGPoint(point);
#elif defined(USE_X11)
int x, y; /* This is all we care about. Seriously. */
Window garb1, garb2; /* Why you can't specify NULL as a parameter */
int garb_x, garb_y; /* is beyond me. */
unsigned int more_garbage;
Display *display = XGetMainDisplay();
XQueryPointer(display, XDefaultRootWindow(display), &garb1, &garb2,
&x, &y, &garb_x, &garb_y, &more_garbage);
return MMSignedPointMake(x, y);
#elif defined(IS_WINDOWS)
POINT point;
GetCursorPos(&point);
return MMSignedPointFromPOINT(point);
#endif
}
/**
* Press down a button, or release it.
* @param down True for down, false for up.
* @param button The button to press down or release.
*/
void toggleMouse(bool down, MMMouseButton button)
{
#if defined(IS_MACOSX)
const CGPoint currentPos = CGPointFromMMSignedPoint(getMousePos());
const CGEventType mouseType = MMMouseToCGEventType(down, button);
CGEventRef event = CGEventCreateMouseEvent(NULL,
mouseType,
currentPos,
(CGMouseButton)button);
CGEventPost(kCGSessionEventTap, event);
CFRelease(event);
#elif defined(USE_X11)
Display *display = XGetMainDisplay();
XTestFakeButtonEvent(display, button, down ? True : False, CurrentTime);
XFlush(display);
#elif defined(IS_WINDOWS)
INPUT mouseInput;
mouseInput.type = INPUT_MOUSE;
mouseInput.mi.dx = 0;
mouseInput.mi.dy = 0;
mouseInput.mi.dwFlags = MMMouseToMEventF(down, button);
mouseInput.mi.time = 0; //System will provide the timestamp
mouseInput.mi.dwExtraInfo = 0;
mouseInput.mi.mouseData = 0;
SendInput(1, &mouseInput, sizeof(mouseInput));
#endif
}
void clickMouse(MMMouseButton button)
{
toggleMouse(true, button);
toggleMouse(false, button);
}
/**
* Special function for sending double clicks, needed for Mac OS X.
* @param button Button to click.
*/
void doubleClick(MMMouseButton button)
{
#if defined(IS_MACOSX)
/* Double click for Mac. */
const CGPoint currentPos = CGPointFromMMSignedPoint(getMousePos());
const CGEventType mouseTypeDown = MMMouseToCGEventType(true, button);
const CGEventType mouseTypeUP = MMMouseToCGEventType(false, button);
CGEventRef event = CGEventCreateMouseEvent(NULL, mouseTypeDown, currentPos, kCGMouseButtonLeft);
/* Set event to double click. */
CGEventSetIntegerValueField(event, kCGMouseEventClickState, 2);
CGEventPost(kCGHIDEventTap, event);
CGEventSetType(event, mouseTypeUP);
CGEventPost(kCGHIDEventTap, event);
CFRelease(event);
#else
/* Double click for everything else. */
clickMouse(button);
microsleep(200);
clickMouse(button);
#endif
}
void scrollMouse(int x, int y)
{
#if defined(IS_WINDOWS)
// Fix for #97 https://github.com/octalmage/robotjs/issues/97,
// C89 needs variables declared on top of functions (mouseScrollInput)
INPUT mouseScrollInputs[2];
#endif
/* Direction should only be considered based on the scrollDirection. This
* Should not interfere. */
/* Set up the OS specific solution */
#if defined(__APPLE__)
CGEventRef event;
event = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, 2, y, x);
CGEventPost(kCGHIDEventTap, event);
CFRelease(event);
#elif defined(USE_X11)
/*
X11 Mouse Button Numbering
1 = left button
2 = middle button (pressing the scroll wheel)
3 = right button
4 = turn scroll wheel up
5 = turn scroll wheel down
6 = push scroll wheel left
7 = push scroll wheel right
8 = 4th button (aka browser backward button)
9 = 5th button (aka browser forward button)
*/
int ydir = 4; // Button 4 is up, 5 is down.
int xdir = 6; // Button 6 is left, 7 is right.
Display *display = XGetMainDisplay();
if (y < 0){
ydir = 5;
}
if (x < 0){
xdir = 7;
}
int xi;
int yi;
for (xi = 0; xi < abs(x); xi++) {
XTestFakeButtonEvent(display, xdir, 1, CurrentTime);
XTestFakeButtonEvent(display, xdir, 0, CurrentTime);
}
for (yi = 0; yi < abs(y); yi++) {
XTestFakeButtonEvent(display, ydir, 1, CurrentTime);
XTestFakeButtonEvent(display, ydir, 0, CurrentTime);
}
XFlush(display);
#elif defined(IS_WINDOWS)
// Must send y first, otherwise we get stuck when scrolling on y axis
mouseScrollInputs[0].type = INPUT_MOUSE;
mouseScrollInputs[0].mi.dx = 0;
mouseScrollInputs[0].mi.dy = 0;
mouseScrollInputs[0].mi.dwFlags = MOUSEEVENTF_WHEEL;
mouseScrollInputs[0].mi.time = 0;
mouseScrollInputs[0].mi.dwExtraInfo = 0;
// Flip x to match other platforms.
mouseScrollInputs[0].mi.mouseData = -x;
mouseScrollInputs[1].type = INPUT_MOUSE;
mouseScrollInputs[1].mi.dx = 0;
mouseScrollInputs[1].mi.dy = 0;
mouseScrollInputs[1].mi.dwFlags = MOUSEEVENTF_HWHEEL;
mouseScrollInputs[1].mi.time = 0;
mouseScrollInputs[1].mi.dwExtraInfo = 0;
mouseScrollInputs[1].mi.mouseData = y;
SendInput(2, mouseScrollInputs, sizeof(INPUT));
#endif
}
/*
* A crude, fast hypot() approximation to get around the fact that hypot() is
* not a standard ANSI C function.
*
* It is not particularly accurate but that does not matter for our use case.
*
* Taken from this StackOverflow answer:
* http://stackoverflow.com/questions/3506404/fast-hypotenuse-algorithm-for-embedded-processor#3507882
*
*/
static double crude_hypot(double x, double y)
{
double big = fabs(x); /* max(|x|, |y|) */
double small = fabs(y); /* min(|x|, |y|) */
if (big > small) {
double temp = big;
big = small;
small = temp;
}
return ((M_SQRT2 - 1.0) * small) + big;
}
bool smoothlyMoveMouse(MMPoint endPoint,double speed)
{
MMSignedPoint pos = getMousePos();
MMSize screenSize = getMainDisplaySize();
double velo_x = 0.0, velo_y = 0.0;
double distance;
while ((distance = crude_hypot((double)pos.x - endPoint.x,
(double)pos.y - endPoint.y)) > 1.0) {
double gravity = DEADBEEF_UNIFORM(5.0, 500.0);
double veloDistance;
velo_x += (gravity * ((double)endPoint.x - pos.x)) / distance;
velo_y += (gravity * ((double)endPoint.y - pos.y)) / distance;
/* Normalize velocity to get a unit vector of length 1. */
veloDistance = crude_hypot(velo_x, velo_y);
velo_x /= veloDistance;
velo_y /= veloDistance;
pos.x += floor(velo_x + 0.5);
pos.y += floor(velo_y + 0.5);
/* Make sure we are in the screen boundaries!
* (Strange things will happen if we are not.) */
if (pos.x >= (int32_t)screenSize.width || pos.y >= (int32_t)screenSize.height) {
return false;
}
moveMouse(pos);
/* Wait 1 - (speed) milliseconds. */
microsleep(DEADBEEF_UNIFORM(0.7, speed));
}
return true;
}
================================================
FILE: src/mouse.h
================================================
#pragma once
#ifndef MOUSE_H
#define MOUSE_H
#include "os.h"
#include "types.h"
#if defined(_MSC_VER)
#include "ms_stdbool.h"
#else
#include <stdbool.h>
#endif
#ifdef __cplusplus
extern "C"
{
#endif
#if defined(IS_MACOSX)
#include <ApplicationServices/ApplicationServices.h>
typedef enum {
LEFT_BUTTON = kCGMouseButtonLeft,
RIGHT_BUTTON = kCGMouseButtonRight,
CENTER_BUTTON = kCGMouseButtonCenter
} MMMouseButton;
#elif defined(USE_X11)
enum _MMMouseButton {
LEFT_BUTTON = 1,
CENTER_BUTTON = 2,
RIGHT_BUTTON = 3
};
typedef unsigned int MMMouseButton;
#elif defined(IS_WINDOWS)
enum _MMMouseButton {
LEFT_BUTTON = 1,
CENTER_BUTTON = 2,
RIGHT_BUTTON = 3
};
typedef unsigned int MMMouseButton;
#else
#error "No mouse button constants set for platform"
#endif
#define MMMouseButtonIsValid(button) \
(button == LEFT_BUTTON || button == RIGHT_BUTTON || \
button == CENTER_BUTTON)
enum __MMMouseWheelDirection
{
DIRECTION_DOWN = -1,
DIRECTION_UP = 1
};
typedef int MMMouseWheelDirection;
/* Updates information about current virtual screen size and coordinates
* in Windows
* It is up to the caller to ensure that this called before mouse moving
*/
void updateScreenMetrics();
/* Immediately moves the mouse to the given point on-screen.
* It is up to the caller to ensure that this point is within the
* screen boundaries. */
void moveMouse(MMSignedPoint point);
/* Like moveMouse, moves the mouse to the given point on-screen, but marks
* the event as the mouse being dragged on platforms where it is supported.
* It is up to the caller to ensure that this point is within the screen
* boundaries. */
void dragMouse(MMSignedPoint point, const MMMouseButton button);
/* Smoothly moves the mouse from the current position to the given point.
* deadbeef_srand() should be called before using this function.
*
* Returns false if unsuccessful (i.e. a point was hit that is outside of the
* screen boundaries), or true if successful. */
bool smoothlyMoveMouse(MMPoint point,double speed);
/* Returns the coordinates of the mouse on the current screen. */
MMSignedPoint getMousePos(void);
/* Holds down or releases the mouse with the given button in the current
* position. */
void toggleMouse(bool down, MMMouseButton button);
/* Clicks the mouse with the given button in the current position. */
void clickMouse(MMMouseButton button);
/* Double clicks the mouse with the given button. */
void doubleClick(MMMouseButton button);
/* Scrolls the mouse in the stated direction.
* TODO: Add a smoothly scroll mouse next. */
void scrollMouse(int x, int y);
#endif /* MOUSE_H */
#ifdef __cplusplus
}
#endif
================================================
FILE: src/ms_stdbool.h
================================================
#pragma once
#if !defined(MS_STDBOOL_H) && \
(!defined(__bool_true_false_are_defined) || __bool_true_false_are_defined)
#define MS_STDBOOL_H
#ifndef _MSC_VER
#error "Use this header only with Microsoft Visual C++ compilers!"
#endif /* _MSC_VER */
#define __bool_true_false_are_defined 1
#ifndef __cplusplus
#if defined(true) || defined(false) || defined(bool)
#error "Boolean type already defined"
#endif
enum {
false = 0,
true = 1
};
typedef unsigned char bool;
#endif /* !__cplusplus */
#endif /* MS_STDBOOL_H */
================================================
FILE: src/ms_stdint.h
================================================
/* ISO C9x compliant stdint.h for Microsoft Visual Studio
* Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
*
* Copyright (c) 2006-2008 Alexander Chemeris
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. The name of the author may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _MSC_VER
#error "Use this header only with Microsoft Visual C++ compilers!"
#endif /* _MSC_VER */
#ifndef MSC_STDINT_H
#define MSC_STDINT_H
#if _MSC_VER > 1000
#pragma once
#endif
#include <limits.h>
/* For Visual Studio 6 in C++ mode and for many Visual Studio versions when
* compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
* or compiler give many errors like this: */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
#include <wchar.h>
#if defined(__cplusplus)
}
#endif /* __cplusplus */
/* Define _W64 macros to mark types changing their size, like intptr_t. */
#ifndef _W64
#if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
#define _W64 __w64
#else
#define _W64
#endif
#endif
/* 7.18.1 Integer types */
/* 7.18.1.1 Exact-width integer types */
/* Visual Studio 6 and Embedded Visual C++ 4 doesn't
* realize that, e.g. char has the same size as __int8
* so we give up on __intX for them. */
#if _MSC_VER < 1300
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
#else
typedef signed __int8 int8_t;
typedef signed __int16 int16_t;
typedef signed __int32 int32_t;
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
#endif
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
/* 7.18.1.2 Minimum-width integer types */
typedef int8_t int_least8_t;
typedef int16_t int_least16_t;
typedef int32_t int_least32_t;
typedef int64_t int_least64_t;
typedef uint8_t uint_least8_t;
typedef uint16_t uint_least16_t;
typedef uint32_t uint_least32_t;
typedef uint64_t uint_least64_t;
/* 7.18.1.3 Fastest minimum-width integer types */
typedef int8_t int_fast8_t;
typedef int16_t int_fast16_t;
typedef int32_t int_fast32_t;
typedef int64_t int_fast64_t;
typedef uint8_t uint_fast8_t;
typedef uint16_t uint_fast16_t;
typedef uint32_t uint_fast32_t;
typedef uint64_t uint_fast64_t;
/* 7.18.1.4 Integer types capable of holding object pointers */
#if defined(_WIN64)
typedef signed __int64 intptr_t;
typedef unsigned __int64 uintptr_t;
#else
typedef _W64 signed int intptr_t;
typedef _W64 unsigned int uintptr_t;
#endif /* _WIN64 ] */
/* 7.18.1.5 Greatest-width integer types */
typedef int64_t intmax_t;
typedef uint64_t uintmax_t;
/* 7.18.2 Limits of specified-width integer types */
/* See footnote 220 at page 257 and footnote 221 at page 259 */
#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS)
/* 7.18.2.1 Limits of exact-width integer types */
#define INT8_MIN ((int8_t)_I8_MIN)
#define INT8_MAX _I8_MAX
#define INT16_MIN ((int16_t)_I16_MIN)
#define INT16_MAX _I16_MAX
#define INT32_MIN ((int32_t)_I32_MIN)
#define INT32_MAX _I32_MAX
#define INT64_MIN ((int64_t)_I64_MIN)
#define INT64_MAX _I64_MAX
#define UINT8_MAX _UI8_MAX
#define UINT16_MAX _UI16_MAX
#define UINT32_MAX _UI32_MAX
#define UINT64_MAX _UI64_MAX
/* 7.18.2.2 Limits of minimum-width integer types */
#define INT_LEAST8_MIN INT8_MIN
#define INT_LEAST8_MAX INT8_MAX
#define INT_LEAST16_MIN INT16_MIN
#define INT_LEAST16_MAX INT16_MAX
#define INT_LEAST32_MIN INT32_MIN
#define INT_LEAST32_MAX INT32_MAX
#define INT_LEAST64_MIN INT64_MIN
#define INT_LEAST64_MAX INT64_MAX
#define UINT_LEAST8_MAX UINT8_MAX
#define UINT_LEAST16_MAX UINT16_MAX
#define UINT_LEAST32_MAX UINT32_MAX
#define UINT_LEAST64_MAX UINT64_MAX
/* 7.18.2.4 Limits of integer types capable of holding object pointers */
#if defined(_WIN64)
#define INTPTR_MIN INT64_MIN
#define INTPTR_MAX INT64_MAX
#define UINTPTR_MAX UINT64_MAX
#else
#define INTPTR_MIN INT32_MIN
#define INTPTR_MAX INT32_MAX
#define UINTPTR_MAX UINT32_MAX
#endif
/* 7.18.3 Limits of other integer types */
#if defined(_WIN64)
#define PTRDIFF_MIN _I64_MIN
#define PTRDIFF_MAX _I64_MAX
#else
#define PTRDIFF_MIN _I32_MIN
#define PTRDIFF_MAX _I32_MAX
#endif /* _WIN64 */
#define SIG_ATOMIC_MIN INT_MIN
#define SIG_ATOMIC_MAX INT_MAX
#ifndef SIZE_MAX
#if defined(_WIN64)
#define SIZE_MAX _UI64_MAX
#else
#define SIZE_MAX _UI32_MAX
#endif
#endif
/* WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h> */
#ifndef WCHAR_MIN
#define WCHAR_MIN 0
#endif /* WCHAR_MIN */
#ifndef WCHAR_MAX
#define WCHAR_MAX _UI16_MAX
#endif /* WCHAR_MAX */
#define WINT_MIN 0
#define WINT_MAX _UI16_MAX
#endif /* __STDC_LIMIT_MACROS */
/* 7.18.4 Limits of other integer types */
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) /* See footnote 224 at page 260 */
/* 7.18.4.1 Macros for minimum-width integer constants */
#define INT8_C(val) val##i8
#define INT16_C(val) val##i16
#define INT32_C(val) val##i32
#define INT64_C(val) val##i64
#define UINT8_C(val) val##ui8
#define UINT16_C(val) val##ui16
#define UINT32_C(val) val##ui32
#define UINT64_C(val) val##ui64
/* 7.18.4.2 Macros for greatest-width integer constants */
#define INTMAX_C INT64_C
#define UINTMAX_C UINT64_C
#endif /* __STDC_CONSTANT_MACROS */
#endif /* MSC_STDINT_H */
================================================
FILE: src/os.h
================================================
#pragma once
#ifndef OS_H
#define OS_H
/* Python versions under 2.5 don't support this macro, but it's not
* terribly difficult to replicate: */
#ifndef PyModule_AddIntMacro
#define PyModule_AddIntMacro(module, macro) \
PyModule_AddIntConstant(module, #macro, macro)
#endif /* PyModule_AddIntMacro */
#if !defined(IS_MACOSX) && defined(__APPLE__) && defined(__MACH__)
#define IS_MACOSX
#endif /* IS_MACOSX */
#if !defined(IS_WINDOWS) && (defined(WIN32) || defined(_WIN32) || \
defined(__WIN32__) || defined(__WINDOWS__))
#define IS_WINDOWS
#endif /* IS_WINDOWS */
#if !defined(USE_X11) && !defined(NUSE_X11) && !defined(IS_MACOSX) && !defined(IS_WINDOWS)
#define USE_X11
#endif /* USE_X11 */
#if defined(IS_WINDOWS)
#define STRICT /* Require use of exact types. */
#define WIN32_LEAN_AND_MEAN 1 /* Speed up compilation. */
#include <windows.h>
#elif !defined(IS_MACOSX) && !defined(USE_X11)
#error "Sorry, this platform isn't supported yet!"
#endif
/* Interval to align by for large buffers (e.g. bitmaps). */
/* Must be a power of 2. */
#ifndef BYTE_ALIGN
#define BYTE_ALIGN 4 /* Bytes to align pixel buffers to. */
/* #include <stddef.h> */
/* #define BYTE_ALIGN (sizeof(size_t)) */
#endif /* BYTE_ALIGN */
#if BYTE_ALIGN == 0
/* No alignment needed. */
#define ADD_PADDING(width) (width)
#else
/* Aligns given width to padding. */
#define ADD_PADDING(width) (BYTE_ALIGN + (((width) - 1) & ~(BYTE_ALIGN - 1)))
#endif
#endif /* OS_H */
================================================
FILE: src/pasteboard.c
================================================
#include "pasteboard.h"
#include "os.h"
#if defined(IS_MACOSX)
#include "png_io.h"
#include <ApplicationServices/ApplicationServices.h>
#elif defined(IS_WINDOWS)
#include "bmp_io.h"
#endif
MMPasteError copyMMBitmapToPasteboard(MMBitmapRef bitmap)
{
#if defined(IS_MACOSX)
PasteboardRef clipboard;
size_t len;
uint8_t *pngbuf;
CFDataRef data;
OSStatus err;
if (PasteboardCreate(kPasteboardClipboard, &clipboard) != noErr) {
return kMMPasteOpenError;
}
if (PasteboardClear(clipboard) != noErr) {
CFRelease(clipboard);
return kMMPasteClearError;
}
pngbuf = createPNGData(bitmap, &len);
if (pngbuf == NULL) {
CFRelease(clipboard);
return kMMPasteDataError;
}
data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, pngbuf, len,
kCFAllocatorNull);
if (data == NULL) {
CFRelease(clipboard);
free(pngbuf);
return kMMPasteDataError;
}
err = PasteboardPutItemFlavor(clipboard, bitmap, kUTTypePNG, data, 0);
CFRelease(data);
CFRelease(clipboard);
free(pngbuf);
return (err == noErr) ? kMMPasteNoError : kMMPastePasteError;
#elif defined(IS_WINDOWS)
MMPasteError ret = kMMPasteNoError;
uint8_t *bmpData;
size_t len;
HGLOBAL handle;
if (!OpenClipboard(NULL)) return kMMPasteOpenError;
if (!EmptyClipboard()) return kMMPasteClearError;
bmpData = createBitmapData(bitmap, &len);
if (bmpData == NULL) return kMMPasteDataError;
/* CF_DIB does not include the BITMAPFILEHEADER struct (and displays a
* cryptic error if it is included). */
len -= sizeof(BITMAPFILEHEADER);
/* SetClipboardData() needs a "handle", not just a buffer, so we have to
* allocate one with GlobalAlloc(). */
if ((handle = GlobalAlloc(GMEM_MOVEABLE, len)) == NULL) {
CloseClipboard();
free(bmpData);
return kMMPasteDataError;
}
memcpy(GlobalLock(handle), bmpData + sizeof(BITMAPFILEHEADER), len);
GlobalUnlock(handle);
free(bmpData);
if (SetClipboardData(CF_DIB, handle) == NULL) {
ret = kMMPastePasteError;
}
CloseClipboard();
GlobalFree(handle);
return ret;
#elif defined(USE_X11)
/* TODO (X11's clipboard is _weird_.) */
return kMMPasteUnsupportedError;
#endif
}
const char *MMPasteErrorString(MMPasteError err)
{
switch (err) {
case kMMPasteOpenError:
return "Could not open pasteboard";
case kMMPasteClearError:
return "Could not clear pasteboard";
case kMMPasteDataError:
return "Could not create image data from bitmap";
case kMMPastePasteError:
return "Could not paste data";
case kMMPasteUnsupportedError:
return "Unsupported platform";
default:
return NULL;
}
}
================================================
FILE: src/pasteboard.h
================================================
#pragma once
#ifndef PASTEBOARD_H
#define PASTEBOARD_H
#include "MMBitmap.h"
#include "io.h"
enum _MMBitmapPasteError {
kMMPasteNoError = 0,
kMMPasteGenericError,
kMMPasteOpenError,
kMMPasteClearError,
kMMPasteDataError,
kMMPastePasteError,
kMMPasteUnsupportedError
};
typedef MMIOError MMPasteError;
/* Copies |bitmap| to the pasteboard as a PNG.
* Returns 0 on success, non-zero on error. */
MMPasteError copyMMBitmapToPasteboard(MMBitmapRef bitmap);
/* Returns description of given MMPasteError.
* Returned string is constant and hence should not be freed. */
const char *MMPasteErrorString(MMPasteError error);
#endif /* PASTEBOARD_H */
================================================
FILE: src/png_io.c
================================================
#include "png_io.h"
#include "os.h"
#include <png.h>
#include <stdio.h> /* fopen() */
#include <stdlib.h> /* malloc/realloc */
#include <assert.h>
#if defined(_MSC_VER)
#include "ms_stdint.h"
#include "ms_stdbool.h"
#else
#include <stdint.h>
#include <stdbool.h>
#endif
const char *MMPNGReadErrorString(MMIOError error)
{
switch (error) {
case kPNGAccessError:
return "Could not open file";
case kPNGReadError:
return "Could not read file";
case kPNGInvalidHeaderError:
return "Not a PNG file";
default:
return NULL;
}
}
MMBitmapRef newMMBitmapFromPNG(const char *path, MMPNGReadError *err)
{
FILE *fp;
uint8_t header[8];
png_struct *png_ptr = NULL;
png_info *info_ptr = NULL;
png_byte bit_depth, color_type;
uint8_t *row, *bitmapData;
uint8_t bytesPerPixel;
png_uint_32 width, height, y;
uint32_t bytewidth;
if ((fp = fopen(path, "rb")) == NULL) {
if (err != NULL) *err = kPNGAccessError;
return NULL;
}
/* Initialize error code to generic value. */
if (err != NULL) *err = kPNGGenericError;
/* Validate the PNG. */
if (fread(header, 1, sizeof header, fp) == 0) {
if (err != NULL) *err = kPNGReadError;
goto bail;
} else if (png_sig_cmp(header, 0, sizeof(header)) != 0) {
if (err != NULL) *err = kPNGInvalidHeaderError;
goto bail;
}
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) goto bail;
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) goto bail;
/* Set up error handling. */
if (setjmp(png_jmpbuf(png_ptr))) {
goto bail;
}
png_init_io(png_ptr, fp);
/* Skip past the header. */
png_set_sig_bytes(png_ptr, sizeof header);
png_read_info(png_ptr, info_ptr);
/* Convert different image types to common type to be read. */
bit_depth = png_get_bit_depth(png_ptr, info_ptr);
color_type = png_get_color_type(png_ptr, info_ptr);
/* Convert color palettes to RGB. */
if (color_type == PNG_COLOR_TYPE_PALETTE) {
png_set_palette_to_rgb(png_ptr);
}
/* Convert PNG to bit depth of 8. */
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
png_set_expand_gray_1_2_4_to_8(png_ptr);
} else if (bit_depth == 16) {
png_set_strip_16(png_ptr);
}
/* Convert transparency chunk to alpha channel. */
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
png_set_tRNS_to_alpha(png_ptr);
}
/* Convert gray images to RGB. */
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
png_set_gray_to_rgb(png_ptr);
}
/* Ignore alpha for now. */
if (color_type & PNG_COLOR_MASK_ALPHA) {
png_set_strip_alpha(png_ptr);
}
/* Get image attributes. */
width = png_get_image_width(png_ptr, info_ptr);
height = png_get_image_height(png_ptr, info_ptr);
bytesPerPixel = 3; /* All images decompress to this size. */
bytewidth = ADD_PADDING(width * bytesPerPixel); /* Align width. */
/* Decompress the PNG row by row. */
bitmapData = calloc(1, bytewidth * height);
row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr));
if (bitmapData == NULL || row == NULL) goto bail;
for (y = 0; y < height; ++y) {
png_uint_32 x;
const uint32_t rowOffset = y * bytewidth;
uint8_t *rowptr = row;
png_read_row(png_ptr, (png_byte *)row, NULL);
for (x = 0; x < width; ++x) {
const uint32_t colOffset = x * bytesPerPixel;
MMRGBColor *color = (MMRGBColor *)(bitmapData + rowOffset + colOffset);
color->red = *rowptr++;
color->green = *rowptr++;
color->blue = *rowptr++;
}
}
free(row);
/* Finish reading. */
png_read_end(png_ptr, NULL);
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(fp);
return createMMBitmap(bitmapData, width, height,
bytewidth, bytesPerPixel * 8, bytesPerPixel);
bail:
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(fp);
return NULL;
}
struct _PNGWriteInfo {
png_struct *png_ptr;
png_info *info_ptr;
png_byte **row_pointers;
size_t row_count;
bool free_row_pointers;
};
typedef struct _PNGWriteInfo PNGWriteInfo;
typedef PNGWriteInfo *PNGWriteInfoRef;
/* Returns pointer to PNGWriteInfo struct containing data ready to be used with
* functions such as png_write_png().
*
* It is the caller's responsibility to destroy() the returned structure with
* destroyPNGWriteInfo(). */
static PNGWriteInfoRef createPNGWriteInfo(MMBitmapRef bitmap)
{
PNGWriteInfoRef info = malloc(sizeof(PNGWriteInfo));
png_uint_32 y;
if (info == NULL) return NULL;
info->png_ptr = NULL;
info->info_ptr = NULL;
info->row_pointers = NULL;
assert(bitmap != NULL);
/* Initialize the write struct. */
info->png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
NULL, NULL, NULL);
if (info->png_ptr == NULL) goto bail;
/* Set up error handling. */
if (setjmp(png_jmpbuf(info->png_ptr))) {
png_destroy_write_struct(&(info->png_ptr), &(info->info_ptr));
goto bail;
}
/* Initialize the info struct. */
info->info_ptr = png_create_info_struct(info->png_ptr);
if (info->info_ptr == NULL) {
png_destroy_write_struct(&(info->png_ptr), NULL);
goto bail;
}
/* Set image attributes. */
png_set_IHDR(info->png_ptr,
info->info_ptr,
(png_uint_32)bitmap->width,
(png_uint_32)bitmap->height,
8,
PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
info->row_count = bitmap->height;
info->row_pointers = png_malloc(info->png_ptr,
sizeof(png_byte *) * info->row_count);
if (bitmap->bytesPerPixel == 3) {
/* No alpha channel; image data can be copied directly. */
for (y = 0; y < info->row_count; ++y) {
info->row_pointers[y] = bitmap->imageBuffer + (bitmap->bytewidth * y);
}
info->free_row_pointers = false;
/* Convert BGR to RGB if necessary. */
if (MMRGB_IS_BGR) {
png_set_bgr(info->png_ptr);
}
} else {
/* Ignore alpha channel; copy image data row by row. */
const size_t bytesPerPixel = 3;
const size_t bytewidth = ADD_PADDING(bitmap->width * bytesPerPixel);
for (y = 0; y < info->row_count; ++y) {
png_uint_32 x;
png_byte *row_ptr = png_malloc(info->png_ptr, bytewidth);
info->row_pointers[y] = row_ptr;
for (x = 0; x < bitmap->width; ++x) {
MMRGBColor *color = MMRGBColorRefAtPoint(bitmap, x, y);
row_ptr[0] = color->red;
row_ptr[1] = color->green;
row_ptr[2] = color->blue;
row_ptr += bytesPerPixel;
}
}
info->free_row_pointers = true;
}
png_set_rows(info->png_ptr, info->info_ptr, info->row_pointers);
return info;
bail:
if (info != NULL) free(info);
return NULL;
}
/* Free memory in use by |info|. */
static void destroyPNGWriteInfo(PNGWriteInfoRef info)
{
assert(info != NULL);
if (info->row_pointers != NULL) {
if (info->free_row_pointers) {
size_t y;
for (y = 0; y < info->row_count; ++y) {
free(info->row_pointers[y]);
}
}
png_free(info->png_ptr, info->row_pointers);
}
png_destroy_write_struct(&(info->png_ptr), &(info->info_ptr));
free(info);
}
int saveMMBitmapAsPNG(MMBitmapRef bitmap, const char *path)
{
FILE *fp = fopen(path, "wb");
PNGWriteInfoRef info;
if (fp == NULL) return -1;
if ((info = createPNGWriteInfo(bitmap)) == NULL) {
fclose(fp);
return -1;
}
png_init_io(info->png_ptr, fp);
png_write_png(info->png_ptr, info->info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
fclose(fp);
destroyPNGWriteInfo(info);
return 0;
}
/* Structure to store PNG image bytes. */
struct io_data
{
uint8_t *buffer; /* Pointer to raw file data. */
size_t size; /* Number of bytes actually written to buffer. */
size_t allocedSize; /* Number of bytes allocated for buffer. */
};
/* Called each time libpng attempts to write data in createPNGData(). */
void png_append_data(png_struct *png_ptr,
png_byte *new_data,
png_size_t length)
{
struct io_data *data = png_get_io_ptr(png_ptr);
data->size += length;
/* Allocate or grow buffer. */
if (data->buffer == NULL) {
data->allocedSize = data->size;
data->buffer = png_malloc(png_ptr, data->allocedSize);
assert(data->buffer != NULL);
} else if (data->allocedSize < data->size) {
do {
/* Double size each time to avoid calls to realloc. */
data->allocedSize <<= 1;
} while (data->allocedSize < data->size);
data->buffer = realloc(data->buffer, data->allocedSize);
}
/* Copy new bytes to end of buffer. */
memcpy(data->buffer + data->size - length, new_data, length);
}
uint8_t *createPNGData(MMBitmapRef bitmap, size_t *len)
{
PNGWriteInfoRef info = NULL;
struct io_data data = {NULL, 0, 0};
assert(bitmap != NULL);
assert(len != NULL);
if ((info = createPNGWriteInfo(bitmap)) == NULL) return NULL;
png_set_write_fn(info->png_ptr, &data, &png_append_data, NULL);
png_write_png(info->png_ptr, info->info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
destroyPNGWriteInfo(info);
*len = data.size;
return data.buffer;
}
================================================
FILE: src/png_io.h
================================================
#pragma once
#ifndef PNG_IO_H
#define PNG_IO_H
#include "MMBitmap.h"
#include "io.h"
enum _PNGReadError {
kPNGGenericError = 0,
kPNGReadError,
kPNGAccessError,
kPNGInvalidHeaderError
};
typedef MMIOError MMPNGReadError;
/* Returns description of given MMPNGReadError.
* Returned string is constant and hence should not be freed. */
const char *MMPNGReadErrorString(MMIOError error);
/* Attempts to read PNG file at path; returns new MMBitmap on success, or
* NULL on error. If |error| is non-NULL, it will be set to the error code
* on return.
* Responsibility for destroy()'ing returned MMBitmap is left up to caller. */
MMBitmapRef newMMBitmapFromPNG(const char *path, MMPNGReadError *error);
/* Attempts to write PNG at path; returns 0 on success, -1 on error. */
int saveMMBitmapAsPNG(MMBitmapRef bitmap, const char *path);
/* Returns a buffer containing the raw PNG file data, ready to be saved to a
* file. |len| will be set to the number of bytes allocated in the returned
* buffer (it cannot be NULL).
*
* Responsibility for free()'ing data is left up to the caller. */
uint8_t *createPNGData(MMBitmapRef bitmap, size_t *len);
#endif /* PNG_IO_H */
================================================
FILE: src/rgb.h
================================================
#pragma once
#ifndef RGB_H
#define RGB_H
#include <stdlib.h> /* For abs() */
#include <math.h>
#include "inline_keywords.h" /* For H_INLINE */
#include <stdint.h>
/* RGB colors in MMBitmaps are stored as BGR for convenience in converting
* to/from certain formats (mainly OpenGL).
*
* It is best not to rely on the order (simply use rgb.{blue,green,red} to
* access values), but some situations (e.g., glReadPixels) require one to
* do so. In that case, check to make sure to use MMRGB_IS_BGR for future
* compatibility. */
/* #define MMRGB_IS_BGR (offsetof(MMRGBColor, red) > offsetof(MMRGBColor, blue)) */
#define MMRGB_IS_BGR 1
struct _MMRGBColor {
uint8_t blue;
uint8_t green;
uint8_t red;
};
typedef struct _MMRGBColor MMRGBColor;
/* MMRGBHex is a hexadecimal color value, akin to HTML's, in the form 0xRRGGBB
* where RR is the red value expressed as hexadecimal, GG is the green value,
* and BB is the blue value. */
typedef uint32_t MMRGBHex;
#define MMRGBHEX_MIN 0x000000
#define MMRGBHEX_MAX 0xFFFFFF
/* Converts rgb color to hexadecimal value.
* |red|, |green|, and |blue| should each be of the type |uint8_t|, where the
* range is 0 - 255. */
#define RGB_TO_HEX(red, green, blue) (((red) << 16) | ((green) << 8) | (blue))
/* Convenience wrapper for MMRGBColors. */
H_INLINE MMRGBHex hexFromMMRGB(MMRGBColor rgb)
{
return RGB_TO_HEX(rgb.red, rgb.green, rgb.blue);
}
#define RED_FROM_HEX(hex) ((hex >> 16) & 0xFF)
#define GREEN_FROM_HEX(hex) ((hex >> 8) & 0xFF)
#define BLUE_FROM_HEX(hex) (hex & 0xFF)
/* Converts hexadecimal color to MMRGBColor. */
H_INLINE MMRGBColor MMRGBFromHex(MMRGBHex hex)
{
MMRGBColor color;
color.red = RED_FROM_HEX(hex);
color.green = GREEN_FROM_HEX(hex);
color.blue = BLUE_FROM_HEX(hex);
return color;
}
/* Check absolute equality of two RGB colors. */
#define MMRGBColorEqualToColor(c1, c2) ((c1).red == (c2).red && \
(c1).blue == (c2).blue && \
(c1).green == (c2).green)
/* Returns whether two colors are similar within the given range, |tolerance|.
* Tolerance can be in the range 0.0f - 1.0f, where 0 denotes the exact
* color and 1 denotes any color. */
H_INLINE int MMRGBColorSimilarToColor(MMRGBColor c1, MMRGBColor c2,
float tolerance)
{
/* Speedy case */
if (tolerance <= 0.0f) {
return MMRGBColorEqualToColor(c1, c2);
} else { /* Otherwise, use a Euclidean space to determine similarity */
uint8_t d1 = c1.red - c2.red;
uint8_t d2 = c1.green - c2.green;
uint8_t d3 = c1.blue - c2.blue;
return sqrt((double)(d1 * d1) +
(d2 * d2) +
(d3 * d3)) <= (tolerance * 442.0f);
}
}
/* Identical to MMRGBColorSimilarToColor, only for hex values. */
H_INLINE int MMRGBHexSimilarToColor(MMRGBHex h1, MMRGBHex h2, float tolerance)
{
if (tolerance <= 0.0f) {
return h1 == h2;
} else {
uint8_t d1 = RED_FROM_HEX(h1) - RED_FROM_HEX(h2);
uint8_t d2 = GREEN_FROM_HEX(h1) - GREEN_FROM_HEX(h2);
uint8_t d3 = BLUE_FROM_HEX(h1) - BLUE_FROM_HEX(h2);
return sqrt((double)(d1 * d1) +
(d2 * d2) +
(d3 * d3)) <= (tolerance * 442.0f);
}
}
#endif /* RGB_H */
================================================
FILE: src/robotjs.cc
================================================
#include <napi.h>
#include <vector>
#include "mouse.h"
#include "deadbeef_rand.h"
#include "keypress.h"
#include "screen.h"
#include "screengrab.h"
#include "MMBitmap.h"
#include "snprintf.h"
#include "microsleep.h"
#if defined(USE_X11)
#include "xdisplay.h"
#endif
//Global delays.
int mouseDelay = 10;
int keyboardDelay = 10;
/*
__ __
| \/ | ___ _ _ ___ ___
| |\/| |/ _ \| | | / __|/ _ \
| | | | (_) | |_| \__ \ __/
|_| |_|\___/ \__,_|___/\___|
*/
int CheckMouseButton(const char * const b, MMMouseButton * const button)
{
if (!button) return -1;
if (strcmp(b, "left") == 0)
{
*button = LEFT_BUTTON;
}
else if (strcmp(b, "right") == 0)
{
*button = RIGHT_BUTTON;
}
else if (strcmp(b, "middle") == 0)
{
*button = CENTER_BUTTON;
}
else
{
return -2;
}
return 0;
}
Napi::Value dragMouseWrapper(const Napi::CallbackInfo& info)
{
Napi::Env env = info.Env();
if (info.Length() < 2 || info.Length() > 3)
{
Napi::Error::New(
gitextract_my6o19ab/
├── .github/
│ ├── CONTRIBUTING.md
│ └── ISSUE_TEMPLATE.md
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── appveyor.yml
├── binding.gyp
├── index.d.ts
├── index.js
├── package.json
├── src/
│ ├── MMBitmap.c
│ ├── MMBitmap.h
│ ├── MMPointArray.c
│ ├── MMPointArray.h
│ ├── UTHashTable.c
│ ├── UTHashTable.h
│ ├── alert.c
│ ├── alert.h
│ ├── base64.c
│ ├── base64.h
│ ├── bitmap_find.c
│ ├── bitmap_find.h
│ ├── bmp_io.c
│ ├── bmp_io.h
│ ├── color_find.c
│ ├── color_find.h
│ ├── deadbeef_rand.c
│ ├── deadbeef_rand.h
│ ├── endian.h
│ ├── inline_keywords.h
│ ├── io.c
│ ├── io.h
│ ├── keycode.c
│ ├── keycode.h
│ ├── keypress.c
│ ├── keypress.h
│ ├── microsleep.h
│ ├── mouse.c
│ ├── mouse.h
│ ├── ms_stdbool.h
│ ├── ms_stdint.h
│ ├── os.h
│ ├── pasteboard.c
│ ├── pasteboard.h
│ ├── png_io.c
│ ├── png_io.h
│ ├── rgb.h
│ ├── robotjs.cc
│ ├── screen.c
│ ├── screen.h
│ ├── screengrab.c
│ ├── screengrab.h
│ ├── snprintf.c
│ ├── snprintf.h
│ ├── str_io.c
│ ├── str_io.h
│ ├── types.h
│ ├── uthash.h
│ ├── xdisplay.c
│ ├── xdisplay.h
│ ├── zlib_util.c
│ └── zlib_util.h
└── test/
├── bitmap.js
├── integration/
│ ├── keyboard.js
│ ├── mouse.js
│ └── screen.js
├── keyboard.js
├── mouse.js
└── screen.js
SYMBOL INDEX (214 symbols across 38 files)
FILE: index.d.ts
type Bitmap (line 1) | interface Bitmap {
type Screen (line 11) | interface Screen {
FILE: index.js
function bitmap (line 7) | function bitmap(width, height, byteWidth, bitsPerPixel, bytesPerPixel, i...
FILE: src/MMBitmap.c
function MMBitmapRef (line 5) | MMBitmapRef createMMBitmap(uint8_t *buffer,
function destroyMMBitmap (line 25) | void destroyMMBitmap(MMBitmapRef bitmap)
function destroyMMBitmapBuffer (line 37) | void destroyMMBitmapBuffer(char * bitmapBuffer, void * hint)
function MMBitmapRef (line 45) | MMBitmapRef copyMMBitmap(MMBitmapRef bitmap)
function MMBitmapRef (line 66) | MMBitmapRef copyMMBitmapFromPortion(MMBitmapRef source, MMRect rect)
FILE: src/MMBitmap.h
type _MMBitmap (line 15) | struct _MMBitmap {
type MMBitmap (line 25) | typedef struct _MMBitmap MMBitmap;
type MMBitmap (line 26) | typedef MMBitmap *MMBitmapRef;
FILE: src/MMPointArray.c
function MMPointArrayRef (line 4) | MMPointArrayRef createMMPointArray(size_t initialCount)
function destroyMMPointArray (line 17) | void destroyMMPointArray(MMPointArrayRef pointArray)
function MMPointArrayAppendPoint (line 27) | void MMPointArrayAppendPoint(MMPointArrayRef pointArray, MMPoint point)
FILE: src/MMPointArray.h
type _MMPointArray (line 7) | struct _MMPointArray {
type MMPointArray (line 13) | typedef struct _MMPointArray MMPointArray;
type MMPointArray (line 14) | typedef MMPointArray *MMPointArrayRef;
FILE: src/UTHashTable.c
type _UTHashNode (line 7) | struct _UTHashNode {
type UTHashNode (line 11) | typedef struct _UTHashNode UTHashNode;
function initHashTable (line 13) | void initHashTable(UTHashTable *table, size_t initialCount, size_t nodeS...
function destroyHashTable (line 25) | void destroyHashTable(UTHashTable *table)
FILE: src/UTHashTable.h
type _UTHashTable (line 15) | struct _UTHashTable {
type UTHashTable (line 23) | typedef struct _UTHashTable UTHashTable;
FILE: src/alert.c
function showAlert (line 40) | int showAlert(const char *title, const char *msg, const char *defaultBut...
function xmessage (line 124) | static int xmessage(char *argv[], int *exit_status)
function runTask (line 156) | static int runTask(const char *taskname, char * const argv[], int *exit_...
FILE: src/bitmap_find.c
type shiftNode (line 6) | struct shiftNode {
type shiftNode (line 19) | struct shiftNode
function findBitmapInRectAt (line 55) | static int findBitmapInRectAt(MMBitmapRef needle,
function findBitmapInRect (line 148) | int findBitmapInRect(MMBitmapRef needle,
function MMPointArrayRef (line 164) | MMPointArrayRef findAllBitmapInRect(MMBitmapRef needle, MMBitmapRef hays...
function countOfBitmapInRect (line 183) | size_t countOfBitmapInRect(MMBitmapRef needle, MMBitmapRef haystack,
function initBadShiftTable (line 204) | static void initBadShiftTable(UTHashTable *jumpTable, MMBitmapRef needle)
function needleAtOffset (line 229) | static int needleAtOffset(MMBitmapRef needle, MMBitmapRef haystack,
function addNodeToTable (line 253) | static void addNodeToTable(UTHashTable *table,
type shiftNode (line 263) | struct shiftNode
type shiftNode (line 266) | struct shiftNode
type shiftNode (line 267) | struct shiftNode
FILE: src/bmp_io.c
type BITMAP_FILE_HEADER (line 20) | struct BITMAP_FILE_HEADER {
type _BMP_COMPRESSION (line 29) | enum _BMP_COMPRESSION {
type BMP_COMPRESSION (line 38) | typedef uint32_t BMP_COMPRESSION;
type BITMAP_INFO_HEADER (line 43) | struct BITMAP_INFO_HEADER {
type BITMAP_CORE_HEADER (line 66) | struct BITMAP_CORE_HEADER {
function convertBitmapFileHeader (line 83) | static void convertBitmapFileHeader(struct BITMAP_FILE_HEADER *header)
function convertBitmapInfoHeader (line 93) | static void convertBitmapInfoHeader(struct BITMAP_INFO_HEADER *header)
function MMBitmapRef (line 144) | MMBitmapRef newMMBitmapFromBMP(const char *path, MMBMPReadError *err)
type BITMAP_FILE_HEADER (line 259) | struct BITMAP_FILE_HEADER
type BITMAP_INFO_HEADER (line 260) | struct BITMAP_INFO_HEADER
type BITMAP_FILE_HEADER (line 271) | struct BITMAP_FILE_HEADER
type BITMAP_INFO_HEADER (line 281) | struct BITMAP_INFO_HEADER
function saveMMBitmapAsBMP (line 299) | int saveMMBitmapAsBMP(MMBitmapRef bitmap, const char *path)
function copyBGRDataFromMMBitmap (line 372) | static void copyBGRDataFromMMBitmap(MMBitmapRef bitmap, uint8_t *dest)
function flipBitmapData (line 403) | void flipBitmapData(void *data, size_t width, size_t height, size_t byte...
FILE: src/bmp_io.h
type _BMPReadError (line 13) | enum _BMPReadError {
type MMIOError (line 24) | typedef MMIOError MMBMPReadError;
FILE: src/color_find.c
function findColorInRectAt (line 6) | static int findColorInRectAt(MMBitmapRef image, MMRGBHex color, MMPoint ...
function findColorInRect (line 26) | int findColorInRect(MMBitmapRef image, MMRGBHex color,
function MMPointArrayRef (line 32) | MMPointArrayRef findAllColorInRect(MMBitmapRef image, MMRGBHex color,
function countOfColorsInRect (line 46) | size_t countOfColorsInRect(MMBitmapRef image, MMRGBHex color, MMRect rect,
FILE: src/deadbeef_rand.c
function deadbeef_rand (line 7) | uint32_t deadbeef_rand(void)
function deadbeef_srand (line 14) | void deadbeef_srand(uint32_t x)
function deadbeef_generate_seed (line 22) | uint32_t deadbeef_generate_seed(void)
FILE: src/io.c
function MMImageType (line 19) | MMImageType imageTypeFromExtension(const char *extension)
function MMBitmapRef (line 40) | MMBitmapRef newMMBitmapFromFile(const char *path,
function saveMMBitmapToFile (line 55) | int saveMMBitmapToFile(MMBitmapRef bitmap,
FILE: src/io.h
type _MMImageType (line 14) | enum _MMImageType {
type MMImageType (line 20) | typedef uint16_t MMImageType;
type _MMIOError (line 22) | enum _MMIOError {
type MMIOError (line 26) | typedef uint16_t MMIOError;
FILE: src/keycode.c
type XSpecialCharacterMapping (line 20) | struct XSpecialCharacterMapping {
type XSpecialCharacterMapping (line 25) | struct XSpecialCharacterMapping
function MMKeyCode (line 65) | MMKeyCode keyCodeForChar(const char c)
function CFStringRef (line 135) | CFStringRef createStringForKey(CGKeyCode keyCode)
FILE: src/keycode.h
type _MMKeyCode (line 18) | enum _MMKeyCode {
type CGKeyCode (line 108) | typedef CGKeyCode MMKeyCode;
type _MMKeyCode (line 115) | enum _MMKeyCode {
type KeySym (line 205) | typedef KeySym MMKeyCode;
type _MMKeyCode (line 209) | enum _MMKeyCode {
type MMKeyCode (line 299) | typedef int MMKeyCode;
FILE: src/keypress.c
function io_connect_t (line 31) | static io_connect_t _getAuxiliaryKeyDriver(void)
function win32KeyEvent (line 54) | void win32KeyEvent(int key, MMKeyFlags flags)
function toggleKeyCode (line 112) | void toggleKeyCode(MMKeyCode code, const bool down, MMKeyFlags flags)
function tapKeyCode (line 183) | void tapKeyCode(MMKeyCode code, MMKeyFlags flags)
function toggleKey (line 189) | void toggleKey(char c, const bool down, MMKeyFlags flags)
function tapKey (line 212) | void tapKey(char c, MMKeyFlags flags)
function toggleUnicode (line 219) | void toggleUnicode(UniChar ch, const bool down)
function unicodeTap (line 251) | void unicodeTap(const unsigned value)
function typeStringDelayed (line 278) | void typeStringDelayed(const char *str, const unsigned cpm)
FILE: src/keypress.h
type MMKeyFlags (line 19) | typedef enum {
type _MMKeyFlags (line 29) | enum _MMKeyFlags {
type MMKeyFlags (line 37) | typedef unsigned int MMKeyFlags;
type _MMKeyFlags (line 41) | enum _MMKeyFlags {
type MMKeyFlags (line 50) | typedef unsigned int MMKeyFlags;
FILE: src/microsleep.h
function H_INLINE (line 23) | H_INLINE void microsleep(double milliseconds)
FILE: src/mouse.c
function calculateDeltas (line 77) | void calculateDeltas(CGEventRef *event, MMSignedPoint point)
function updateScreenMetrics (line 98) | void updateScreenMetrics()
function moveMouse (line 111) | void moveMouse(MMSignedPoint point)
function dragMouse (line 149) | void dragMouse(MMSignedPoint point, const MMMouseButton button)
function MMSignedPoint (line 165) | MMSignedPoint getMousePos()
function toggleMouse (line 197) | void toggleMouse(bool down, MMMouseButton button)
function clickMouse (line 225) | void clickMouse(MMMouseButton button)
function doubleClick (line 235) | void doubleClick(MMMouseButton button)
function scrollMouse (line 267) | void scrollMouse(int x, int y)
function crude_hypot (line 360) | static double crude_hypot(double x, double y)
function smoothlyMoveMouse (line 374) | bool smoothlyMoveMouse(MMPoint endPoint,double speed)
FILE: src/mouse.h
type MMMouseButton (line 21) | typedef enum {
type _MMMouseButton (line 29) | enum _MMMouseButton {
type MMMouseButton (line 34) | typedef unsigned int MMMouseButton;
type _MMMouseButton (line 38) | enum _MMMouseButton {
type MMMouseButton (line 43) | typedef unsigned int MMMouseButton;
type __MMMouseWheelDirection (line 53) | enum __MMMouseWheelDirection
type MMMouseWheelDirection (line 58) | typedef int MMMouseWheelDirection;
FILE: src/ms_stdint.h
type int_least8_t (line 91) | typedef int8_t int_least8_t;
type int_least16_t (line 92) | typedef int16_t int_least16_t;
type int_least32_t (line 93) | typedef int32_t int_least32_t;
type int_least64_t (line 94) | typedef int64_t int_least64_t;
type uint_least8_t (line 95) | typedef uint8_t uint_least8_t;
type uint_least16_t (line 96) | typedef uint16_t uint_least16_t;
type uint_least32_t (line 97) | typedef uint32_t uint_least32_t;
type uint_least64_t (line 98) | typedef uint64_t uint_least64_t;
type int_fast8_t (line 101) | typedef int8_t int_fast8_t;
type int_fast16_t (line 102) | typedef int16_t int_fast16_t;
type int_fast32_t (line 103) | typedef int32_t int_fast32_t;
type int_fast64_t (line 104) | typedef int64_t int_fast64_t;
type uint_fast8_t (line 105) | typedef uint8_t uint_fast8_t;
type uint_fast16_t (line 106) | typedef uint16_t uint_fast16_t;
type uint_fast32_t (line 107) | typedef uint32_t uint_fast32_t;
type uint_fast64_t (line 108) | typedef uint64_t uint_fast64_t;
type intmax_t (line 120) | typedef int64_t intmax_t;
type uintmax_t (line 121) | typedef uint64_t uintmax_t;
FILE: src/pasteboard.c
function MMPasteError (line 11) | MMPasteError copyMMBitmapToPasteboard(MMBitmapRef bitmap)
FILE: src/pasteboard.h
type _MMBitmapPasteError (line 8) | enum _MMBitmapPasteError {
type MMIOError (line 18) | typedef MMIOError MMPasteError;
FILE: src/png_io.c
function MMBitmapRef (line 30) | MMBitmapRef newMMBitmapFromPNG(const char *path, MMPNGReadError *err)
type _PNGWriteInfo (line 149) | struct _PNGWriteInfo {
type PNGWriteInfo (line 157) | typedef struct _PNGWriteInfo PNGWriteInfo;
type PNGWriteInfo (line 158) | typedef PNGWriteInfo *PNGWriteInfoRef;
function PNGWriteInfoRef (line 165) | static PNGWriteInfoRef createPNGWriteInfo(MMBitmapRef bitmap)
function destroyPNGWriteInfo (line 251) | static void destroyPNGWriteInfo(PNGWriteInfoRef info)
function saveMMBitmapAsPNG (line 268) | int saveMMBitmapAsPNG(MMBitmapRef bitmap, const char *path)
type io_data (line 288) | struct io_data
function png_append_data (line 296) | void png_append_data(png_struct *png_ptr,
type io_data (line 324) | struct io_data
FILE: src/png_io.h
type _PNGReadError (line 8) | enum _PNGReadError {
type MMIOError (line 15) | typedef MMIOError MMPNGReadError;
FILE: src/rgb.h
type _MMRGBColor (line 22) | struct _MMRGBColor {
type MMRGBColor (line 28) | typedef struct _MMRGBColor MMRGBColor;
type MMRGBHex (line 33) | typedef uint32_t MMRGBHex;
function H_INLINE (line 44) | H_INLINE MMRGBHex hexFromMMRGB(MMRGBColor rgb)
function H_INLINE (line 54) | H_INLINE MMRGBColor MMRGBFromHex(MMRGBHex hex)
function H_INLINE (line 71) | H_INLINE int MMRGBColorSimilarToColor(MMRGBColor c1, MMRGBColor c2,
function H_INLINE (line 89) | H_INLINE int MMRGBHexSimilarToColor(MMRGBHex h1, MMRGBHex h2, float tole...
FILE: src/robotjs.cc
function CheckMouseButton (line 28) | int CheckMouseButton(const char * const b, MMMouseButton * const button)
function dragMouseWrapper (line 52) | Napi::Value dragMouseWrapper(const Napi::CallbackInfo& info)
function updateScreenMetricsWrapper (line 92) | Napi::Value updateScreenMetricsWrapper(const Napi::CallbackInfo& info)
function moveMouseWrapper (line 101) | Napi::Value moveMouseWrapper(const Napi::CallbackInfo& info)
function moveMouseSmoothWrapper (line 122) | Napi::Value moveMouseSmoothWrapper(const Napi::CallbackInfo& info)
function getMousePosWrapper (line 150) | Napi::Value getMousePosWrapper(const Napi::CallbackInfo& info)
function mouseClickWrapper (line 163) | Napi::Value mouseClickWrapper(const Napi::CallbackInfo& info)
function mouseToggleWrapper (line 212) | Napi::Value mouseToggleWrapper(const Napi::CallbackInfo& info)
function setMouseDelayWrapper (line 270) | Napi::Value setMouseDelayWrapper(const Napi::CallbackInfo& info)
function scrollMouseWrapper (line 285) | Napi::Value scrollMouseWrapper(const Napi::CallbackInfo& info)
type KeyNames (line 311) | struct KeyNames
function CheckKeyCodes (line 410) | int CheckKeyCodes(const char* k, MMKeyCode *key)
function CheckKeyFlags (line 441) | int CheckKeyFlags(const char* f, MMKeyFlags* flags)
function GetFlagsFromString (line 473) | int GetFlagsFromString(Napi::Value value, MMKeyFlags* flags) {
function GetFlagsFromValue (line 479) | int GetFlagsFromValue(Napi::Value value, MMKeyFlags* flags) {
function keyTapWrapper (line 506) | Napi::Value keyTapWrapper(const Napi::CallbackInfo& info)
function keyToggleWrapper (line 561) | Napi::Value keyToggleWrapper(const Napi::CallbackInfo& info)
function unicodeTapWrapper (line 643) | Napi::Value unicodeTapWrapper(const Napi::CallbackInfo& info)
function typeStringWrapper (line 659) | Napi::Value typeStringWrapper(const Napi::CallbackInfo& info)
function typeStringDelayedWrapper (line 678) | Napi::Value typeStringDelayedWrapper(const Napi::CallbackInfo& info)
function setKeyboardDelayWrapper (line 699) | Napi::Value setKeyboardDelayWrapper(const Napi::CallbackInfo& info)
function padHex (line 728) | void padHex(MMRGBHex color, char* hex)
function getPixelColorWrapper (line 735) | Napi::Value getPixelColorWrapper(const Napi::CallbackInfo& info)
function getScreenSizeWrapper (line 770) | Napi::Value getScreenSizeWrapper(const Napi::CallbackInfo& info)
function getXDisplayNameWrapper (line 786) | Napi::Value getXDisplayNameWrapper(const Napi::CallbackInfo& info)
function setXDisplayNameWrapper (line 799) | Napi::Value setXDisplayNameWrapper(const Napi::CallbackInfo& info)
function captureScreenWrapper (line 813) | Napi::Value captureScreenWrapper(const Napi::CallbackInfo& info)
class BMP (line 869) | class BMP
function BMP (line 881) | BMP buildBMP(Napi::Object obj)
function getColorWrapper (line 900) | Napi::Value getColorWrapper(const Napi::CallbackInfo& info)
function InitAll (line 934) | Napi::Object InitAll(Napi::Env env, Napi::Object exports)
FILE: src/screen.c
function MMSize (line 11) | MMSize getMainDisplaySize(void)
function pointVisibleOnMainDisplay (line 29) | bool pointVisibleOnMainDisplay(MMPoint point)
FILE: src/screengrab.c
function MMBitmapRef (line 18) | MMBitmapRef copyMMBitmapFromDisplayInRect(MMRect rect)
FILE: src/snprintf.c
function asprintf (line 414) | int asprintf(char **ptr, const char *fmt, /*args*/ ...) {
function vasprintf (line 438) | int vasprintf(char **ptr, const char *fmt, va_list ap) {
function asnprintf (line 460) | int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) {
function vasnprintf (line 488) | int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) {
function portable_snprintf (line 520) | int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/...
FILE: src/str_io.c
function MMBitmapRef (line 53) | MMBitmapRef createMMBitmapFromString(const uint8_t *buffer, size_t buflen,
function getSizeFromString (line 143) | static bool getSizeFromString(const uint8_t *buf, size_t buflen,
function parseDimension (line 167) | static uint32_t parseDimension(const uint8_t *buf, size_t buflen,
FILE: src/str_io.h
type _MMBMPStringError (line 10) | enum _MMBMPStringError {
type MMIOError (line 20) | typedef MMIOError MMBMPStringError;
FILE: src/types.h
type _MMPoint (line 12) | struct _MMPoint {
type MMPoint (line 17) | typedef struct _MMPoint MMPoint;
type _MMSignedPoint (line 20) | struct _MMSignedPoint {
type MMSignedPoint (line 25) | typedef struct _MMSignedPoint MMSignedPoint;
type _MMSize (line 27) | struct _MMSize {
type MMSize (line 32) | typedef struct _MMSize MMSize;
type _MMRect (line 34) | struct _MMRect {
type MMRect (line 39) | typedef struct _MMRect MMRect;
function H_INLINE (line 41) | H_INLINE MMPoint MMPointMake(size_t x, size_t y)
function H_INLINE (line 49) | H_INLINE MMSignedPoint MMSignedPointMake(int32_t x, int32_t y)
function H_INLINE (line 57) | H_INLINE MMSize MMSizeMake(size_t width, size_t height)
function H_INLINE (line 65) | H_INLINE MMRect MMRectMake(size_t x, size_t y, size_t width, size_t height)
FILE: src/uthash.h
type UT_hash_bucket (line 861) | typedef struct UT_hash_bucket {
type UT_hash_table (line 885) | typedef struct UT_hash_table {
type UT_hash_handle (line 918) | typedef struct UT_hash_handle {
FILE: src/xdisplay.c
function Display (line 10) | Display *XGetMainDisplay(void)
function XCloseMainDisplay (line 38) | void XCloseMainDisplay(void)
function setXDisplay (line 51) | void setXDisplay(const char *name)
Condensed preview — 71 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (375K chars).
[
{
"path": ".github/CONTRIBUTING.md",
"chars": 558,
"preview": "## 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 "
},
{
"path": ".github/ISSUE_TEMPLATE.md",
"chars": 1119,
"preview": "<!--- Provide a general summary of the issue in the Title above. -->\n\n## Expected Behavior\n<!--- If you're describing a "
},
{
"path": ".gitignore",
"chars": 38,
"preview": "build/\nnode_modules/\nprebuilds/\n/.idea"
},
{
"path": ".travis.yml",
"chars": 1278,
"preview": "sudo: false\nlanguage: cpp\n\nenv:\n matrix:\n - TRAVIS_NODE_VERSION=\"8\"\n - TRAVIS_NODE_VERSION=\"10\"\n - TRAVIS_NODE"
},
{
"path": "CHANGELOG.md",
"chars": 48546,
"preview": "## 0.7.0 (2026-03-11)\n\n* build: Bump prebuild version and MACOSX_DEPLOYMENT_TARGET. ([7bcd7ff](https://github.com/octalm"
},
{
"path": "LICENSE.md",
"chars": 1058,
"preview": "Copyright (c) 2014 Jason Stallings\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this"
},
{
"path": "README.md",
"chars": 6321,
"preview": "<p align=\"center\"><img src=\"https://cldup.com/1ATDf2JMtv.png\"></p>\n\n> Node.js Desktop Automation. Control the mouse, key"
},
{
"path": "appveyor.yml",
"chars": 806,
"preview": "# http://www.appveyor.com/docs/appveyor-yml\n\n# Test against these versions of Io.js and Node.js.\nenvironment:\n matrix:\n"
},
{
"path": "binding.gyp",
"chars": 1724,
"preview": "{\n 'targets': [{\n 'target_name': 'robotjs',\n 'cflags!': [ '-fno-exceptions' ],\n 'cflags_cc!': [ '-fno-exce"
},
{
"path": "index.d.ts",
"chars": 1355,
"preview": "export interface Bitmap {\n width: number\n height: number\n image: any\n byteWidth: number\n bitsPerPixel: number\n byt"
},
{
"path": "index.js",
"chars": 937,
"preview": "var robotjs = require('./build/Release/robotjs.node');\n\nmodule.exports = robotjs;\n\nmodule.exports.screen = {};\n\nfunction"
},
{
"path": "package.json",
"chars": 1407,
"preview": "{\n \"name\": \"robotjs\",\n \"version\": \"0.7.0\",\n \"description\": \"Node.js Desktop Automation.\",\n \"main\": \"index.js\",\n \"ty"
},
{
"path": "src/MMBitmap.c",
"chars": 2434,
"preview": "#include \"MMBitmap.h\"\n#include <assert.h>\n#include <string.h>\n\nMMBitmapRef createMMBitmap(uint8_t *buffer,\n "
},
{
"path": "src/MMBitmap.h",
"chars": 3310,
"preview": "#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 <st"
},
{
"path": "src/MMPointArray.c",
"chars": 1098,
"preview": "#include \"MMPointArray.h\"\n#include <stdlib.h>\n\nMMPointArrayRef createMMPointArray(size_t initialCount)\n{\n\tMMPointArrayRe"
},
{
"path": "src/MMPointArray.h",
"chars": 1072,
"preview": "#pragma once\n#ifndef MMARRAY_H\n#define MMARRAY_H\n\n#include \"types.h\"\n\nstruct _MMPointArray {\n\tMMPoint *array; /* Pointer"
},
{
"path": "src/UTHashTable.c",
"chars": 1641,
"preview": "#include \"UTHashTable.h\"\n#include <stdlib.h>\n#include <assert.h>\n\n/* Base struct class (all nodes must contain at least "
},
{
"path": "src/UTHashTable.h",
"chars": 3019,
"preview": "#pragma once\n#ifndef UTHASHTABLE_H\n#define UTHASHTABLE_H\n\n#include <stddef.h>\n#include \"uthash.h\"\n\n/* All node structs m"
},
{
"path": "src/alert.c",
"chars": 5640,
"preview": "#include \"alert.h\"\n#include \"os.h\"\n#include <assert.h>\n\n#if defined(IS_MACOSX)\n\t#include <CoreFoundation/CoreFoundation."
},
{
"path": "src/alert.h",
"chars": 464,
"preview": "#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>"
},
{
"path": "src/base64.c",
"chars": 3535,
"preview": "#include \"base64.h\"\n#include <string.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n\n/* Encoding table as"
},
{
"path": "src/base64.h",
"chars": 1009,
"preview": "#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"
},
{
"path": "src/bitmap_find.c",
"chars": 9536,
"preview": "#include \"bitmap_find.h\"\n#include \"UTHashTable.h\"\n#include <assert.h>\n\n/* Node to be used in hash table. */\nstruct shift"
},
{
"path": "src/bitmap_find.h",
"chars": 2149,
"preview": "#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/* C"
},
{
"path": "src/bmp_io.c",
"chars": 13848,
"preview": "#include \"bmp_io.h\"\n#include \"os.h\"\n#include \"endian.h\"\n#include <stdio.h> /* fopen() */\n#include <string.h> /* memcpy()"
},
{
"path": "src/bmp_io.h",
"chars": 1949,
"preview": "#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"
},
{
"path": "src/color_find.c",
"chars": 1661,
"preview": "#include \"color_find.h\"\n#include \"screen.h\"\n#include <stdlib.h>\n\n/* Abstracted, general function to avoid repeated code."
},
{
"path": "src/color_find.h",
"chars": 2108,
"preview": "#pragma once\n#ifndef COLOR_FIND_H\n#define COLOR_FIND_H\n\n#include \"MMBitmap.h\"\n#include \"MMPointArray.h\"\n\n/* Convenience "
},
{
"path": "src/deadbeef_rand.c",
"chars": 690,
"preview": "#include \"deadbeef_rand.h\"\n#include <time.h>\n\nstatic uint32_t deadbeef_seed;\nstatic uint32_t deadbeef_beef = 0xdeadbeef;"
},
{
"path": "src/deadbeef_rand.h",
"chars": 1090,
"preview": "#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 Rand"
},
{
"path": "src/endian.h",
"chars": 3823,
"preview": "#pragma once\n#ifndef ENDIAN_H\n#define ENDIAN_H\n\n#include \"os.h\"\n\n/*\n * (Mostly) cross-platform endian definitions and bi"
},
{
"path": "src/inline_keywords.h",
"chars": 488,
"preview": "#pragma once\n\n/* A complicated, portable model for declaring inline functions in\n * header files. */\n#if !defined(H_INLI"
},
{
"path": "src/io.c",
"chars": 1772,
"preview": "#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 <s"
},
{
"path": "src/io.h",
"chars": 1478,
"preview": "#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 __cplusplu"
},
{
"path": "src/keycode.c",
"chars": 4306,
"preview": "#include \"keycode.h\"\n\n#if defined(IS_MACOSX)\n\n#include <CoreFoundation/CoreFoundation.h>\n#include <Carbon/Carbon.h> /* F"
},
{
"path": "src/keycode.h",
"chars": 7078,
"preview": "#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("
},
{
"path": "src/keypress.c",
"chars": 9207,
"preview": "#include \"keypress.h\"\n#include \"deadbeef_rand.h\"\n#include \"microsleep.h\"\n\n#include <ctype.h> /* For isupper() */\n\n#if de"
},
{
"path": "src/keypress.h",
"chars": 1930,
"preview": "#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#includ"
},
{
"path": "src/microsleep.h",
"chars": 1158,
"preview": "#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_WI"
},
{
"path": "src/mouse.c",
"chars": 12118,
"preview": "#include \"mouse.h\"\n#include \"screen.h\"\n#include \"deadbeef_rand.h\"\n#include \"microsleep.h\"\n\n#include <math.h> /* For floo"
},
{
"path": "src/mouse.h",
"chars": 2662,
"preview": "#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_st"
},
{
"path": "src/ms_stdbool.h",
"chars": 536,
"preview": "#pragma once\n#if !defined(MS_STDBOOL_H) && \\\n\t(!defined(__bool_true_false_are_defined) || __bool_true_false_are_defined)"
},
{
"path": "src/ms_stdint.h",
"chars": 6934,
"preview": "/* ISO C9x compliant stdint.h for Microsoft Visual Studio\n * Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG"
},
{
"path": "src/os.h",
"chars": 1492,
"preview": "#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 "
},
{
"path": "src/pasteboard.c",
"chars": 2587,
"preview": "#include \"pasteboard.h\"\n#include \"os.h\"\n\n#if defined(IS_MACOSX)\n\t#include \"png_io.h\"\n\t#include <ApplicationServices/Appl"
},
{
"path": "src/pasteboard.h",
"chars": 656,
"preview": "#pragma once\n#ifndef PASTEBOARD_H\n#define PASTEBOARD_H\n\n#include \"MMBitmap.h\"\n#include \"io.h\"\n\nenum _MMBitmapPasteError "
},
{
"path": "src/png_io.c",
"chars": 8990,
"preview": "#include \"png_io.h\"\n#include \"os.h\"\n#include <png.h>\n#include <stdio.h> /* fopen() */\n#include <stdlib.h> /* malloc/real"
},
{
"path": "src/png_io.h",
"chars": 1177,
"preview": "#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\tkPNGGeneric"
},
{
"path": "src/rgb.h",
"chars": 3213,
"preview": "#pragma once\n#ifndef RGB_H\n#define RGB_H\n\n#include <stdlib.h> /* For abs() */\n#include <math.h>\n#include \"inline_keyword"
},
{
"path": "src/robotjs.cc",
"chars": 23863,
"preview": "#include <napi.h>\n#include <vector>\n#include \"mouse.h\"\n#include \"deadbeef_rand.h\"\n#include \"keypress.h\"\n#include \"screen"
},
{
"path": "src/screen.c",
"chars": 942,
"preview": "#include \"screen.h\"\n#include \"os.h\"\n\n#if defined(IS_MACOSX)\n\t#include <ApplicationServices/ApplicationServices.h>\n#elif "
},
{
"path": "src/screen.h",
"chars": 473,
"preview": "#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"
},
{
"path": "src/screengrab.c",
"chars": 4139,
"preview": "#include \"screengrab.h\"\n#include \"bmp_io.h\"\n#include \"endian.h\"\n#include <stdlib.h> /* malloc() */\n\n#if defined(IS_MACOS"
},
{
"path": "src/screengrab.h",
"chars": 361,
"preview": "#pragma once\n#ifndef SCREENGRAB_H\n#define SCREENGRAB_H\n\n#include \"types.h\"\n#include \"MMBitmap.h\"\n\n#ifdef __cplusplus\next"
},
{
"path": "src/snprintf.c",
"chars": 41584,
"preview": "/*\n * snprintf.c - a portable implementation of snprintf\n *\n * AUTHOR\n * Mark Martinec <mark.martinec@ijs.si>, April 1"
},
{
"path": "src/snprintf.h",
"chars": 1181,
"preview": "#ifndef _PORTABLE_SNPRINTF_H_\n#define _PORTABLE_SNPRINTF_H_\n\n#define PORTABLE_SNPRINTF_VERSION_MAJOR 2\n#define PORTABLE_"
},
{
"path": "src/str_io.c",
"chars": 5803,
"preview": "#include \"str_io.h\"\n#include \"zlib_util.h\"\n#include \"base64.h\"\n#include \"snprintf.h\" /* snprintf() */\n#include <stdio.h>"
},
{
"path": "src/str_io.h",
"chars": 1695,
"preview": "#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 _MMBMPS"
},
{
"path": "src/types.h",
"chars": 1468,
"preview": "#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 <"
},
{
"path": "src/uthash.h",
"chars": 62091,
"preview": "/*\n * Copyright (c) 2003-2009, Troy D. Hanson http://uthash.sourceforge.net\n * All rights reserved.\n *\n * Redistribu"
},
{
"path": "src/xdisplay.c",
"chars": 1098,
"preview": "#include \"xdisplay.h\"\n#include <stdio.h> /* For fputs() */\n#include <stdlib.h> /* For atexit() */\n\nstatic Display *mainD"
},
{
"path": "src/xdisplay.h",
"chars": 644,
"preview": "#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"
},
{
"path": "src/zlib_util.c",
"chars": 2397,
"preview": "#include \"zlib_util.h\"\n#include <zlib.h>\n#include <stdio.h> /* fprintf() */\n#include <stdlib.h> /* malloc() */\n#include "
},
{
"path": "src/zlib_util.h",
"chars": 989,
"preview": "#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\""
},
{
"path": "test/bitmap.js",
"chars": 1572,
"preview": "var robot = require('..');\n\ndescribe('Bitmap', () => {\n var params = {\n\t\t'width': 'number',\n\t\t'height': 'number',\n\t\t'by"
},
{
"path": "test/integration/keyboard.js",
"chars": 878,
"preview": "/* jshint esversion: 6 */\nvar robot = require('../..');\nvar targetpractice = require('targetpractice/index.js');\nvar os "
},
{
"path": "test/integration/mouse.js",
"chars": 2245,
"preview": "/* jshint esversion: 6 */\nvar robot = require('../..');\nvar targetpractice = require('targetpractice/index.js');\nvar os "
},
{
"path": "test/integration/screen.js",
"chars": 810,
"preview": "/* jshint esversion: 6 */\nvar robot = require('../..');\nvar targetpractice = require('targetpractice/index.js');\nvar ele"
},
{
"path": "test/keyboard.js",
"chars": 3543,
"preview": "var robot = require('..');\nvar os = require('os');\n\n// TODO: Need tests for keyToggle, typeString, typeStringDelayed, an"
},
{
"path": "test/mouse.js",
"chars": 3288,
"preview": "var robot = require('..');\nvar lastKnownPos, currentPos;\n\n//Increase delay to help it reliability.\nrobot.setMouseDelay(1"
},
{
"path": "test/screen.js",
"chars": 1030,
"preview": "var robot = require('..');\nvar pixelColor, screenSize;\n\ndescribe('Screen', () => {\n it('Get pixel color.', function()\n "
}
]
About this extraction
This page contains the full source code of the octalmage/robotjs GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 71 files (346.8 KB), approximately 97.4k tokens, and a symbol index with 214 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.