├── .circleci ├── config.yml └── images │ └── primary │ └── Dockerfile ├── .github ├── CODEOWNERS ├── CONTRIBUTING.md ├── issue_template.md ├── pull_request_template.md └── workflows │ └── go.yml ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── README_zh.md ├── appveyor.yml ├── base ├── LICENSE ├── MMBitmap.h ├── base.go ├── bitmap_free_c.h ├── deadbeef_rand.h ├── deadbeef_rand_c.h ├── inline_keywords.h ├── microsleep.h ├── os.h ├── pubs.h ├── rgb.h ├── types.h └── xdisplay_c.h ├── clipboard ├── README.md ├── clipboard.go ├── clipboard_darwin.go ├── clipboard_test.go ├── clipboard_unix.go ├── clipboard_windows.go ├── cmd │ ├── gocopy │ │ └── gocopy.go │ └── gopaste │ │ └── gopaste.go └── example │ └── example.go ├── cv ├── README.md └── gocv.go ├── doc.go ├── docs ├── CHANGELOG.md ├── README.md ├── doc.md ├── doc_zh.md ├── install.md └── keys.md ├── event ├── android │ └── event_c.h └── ios │ └── event_c.h ├── examples ├── README.md ├── key │ └── main.go ├── main.go ├── mouse │ └── main.go ├── scale │ └── main.go ├── screen │ └── main.go └── window │ └── main.go ├── go.mod ├── go.sum ├── img.go ├── key.go ├── key ├── key.go ├── key_windows.go ├── keycode.h ├── keycode_c.h ├── keypress.h └── keypress_c.h ├── keycode.go ├── mouse ├── mouse.go ├── mouse.h ├── mouse_c.h ├── mouse_darwin.go ├── mouse_windows.go └── mouse_x11.go ├── ps.go ├── robot_info_test.go ├── robotgo.go ├── robotgo_adb.go ├── robotgo_android.go ├── robotgo_fn_v1.go ├── robotgo_mac.go ├── robotgo_mac_unix.go ├── robotgo_mac_win.go ├── robotgo_ocr.go ├── robotgo_test.go ├── robotgo_win.go ├── robotgo_x11.go ├── screen.go ├── screen ├── goScreen.h ├── screen.go ├── screen_c.h └── screengrab_c.h ├── test └── index.html ├── wayland_n.go ├── window ├── alert_c.h ├── goWindow.h ├── pub.h ├── win_sys.h ├── window.go └── window.h └── windows_n.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | jobs: 4 | build: 5 | docker: 6 | # using custom image, see .circleci/images/primary/Dockerfile 7 | # - image: govgo/robotgoci:1.10.3 8 | - image: golang:1.23.0 9 | working_directory: /gopath/src/github.com/go-vgo/robotgo 10 | steps: 11 | - checkout 12 | # specify any bash command here prefixed with `run: ` 13 | - run: apt update 14 | - run: 15 | apt -y install gcc libc6-dev 16 | libx11-dev xorg-dev libxtst-dev 17 | xsel xclip 18 | # libpng++-dev 19 | # xcb libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxkbcommon-x11-dev libxkbcommon-dev 20 | - run: apt -y install xvfb 21 | # 22 | # override: 23 | - run: go get -v -t -d ./... 24 | - run: xvfb-run go test -v ./... 25 | # 26 | # codecov.io 27 | # - run: xvfb-run go test -v -covermode=count -coverprofile=coverage.out 28 | # - run: bash <(curl -s https://codecov.io/bash) 29 | -------------------------------------------------------------------------------- /.circleci/images/primary/Dockerfile: -------------------------------------------------------------------------------- 1 | # FROM golang:1.10.1 2 | FROM golang:1.24.2-stretch AS build 3 | # FROM govgo/go:1.11.1 4 | 5 | RUN apt update && apt install -y --no-install-recommends \ 6 | # customize dependencies 7 | libx11-dev xorg-dev \ 8 | libxtst-dev \ 9 | # Bitmap 10 | libpng++-dev \ 11 | # Event: 12 | xcb libxcb-xkb-dev \ 13 | x11-xkb-utils libx11-xcb-dev \ 14 | libxkbcommon-x11-dev libxkbcommon-dev \ 15 | # Clipboard: 16 | xsel xclip && \ 17 | # 18 | apt remove --purge --auto-remove && \ 19 | apt clean && \ 20 | rm -rf /var/lib/apt/lists/* 21 | 22 | RUN go get -u github.com/go-vgo/robotgo -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # general 2 | * @vcaesar -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Robotgo 2 | 3 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | 1. Please **speak English (English only)**, this is the language everybody of us can speak and write. 2 | 2. Please take a moment to **search** that an issue **doesn't already exist**. 3 | 3. Please make sure `Golang, GCC` is installed correctly before installing RobotGo. 4 | 5 | 5. Please give all relevant information below for bug reports, incomplete details will be handled as an invalid report. 6 | 7 | **You MUST delete the content above including this line before posting, otherwise your issue will be invalid.** 8 | 9 | 10 | - Robotgo version (or commit ref): 11 | - Go version: 12 | - Gcc version: 13 | - Operating system and bit: 14 | - Resolution: 15 | - Can you reproduce the bug at [Examples](https://github.com/go-vgo/robotgo/blob/master/examples/main.go): 16 | - [ ] Yes (provide example code) 17 | - [ ] No 18 | - [ ] Not relevant 19 | - Provide example code: 20 | 21 | ```Go 22 | 23 | ``` 24 | - Log gist: 25 | 26 | ## Description 27 | 28 | ... 29 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | The pull request will be closed without any reasons if it does not satisfy any of following requirements: 2 | 3 | 1. Make sure you are targeting the `master` branch, pull requests on release branches are only allowed for bug fixes. 4 | 2. Add new features, please provide the reasons and test code. 5 | 3. Please read contributing guidelines: [CONTRIBUTING](https://github.com/go-vgo/robotgo/blob/master/CONTRIBUTING.md) 6 | 4. Describe what your pull request does and which issue you're targeting (if any and **Please use English**) 7 | 5. ... if it is not related to any particular issues, explain why we should not reject your pull request. 8 | 6. The Commits must **use English**, must be test and No useless submissions. 9 | 10 | **You MUST delete the content above including this line before posting, otherwise your pull request will be invalid.** 11 | 12 | 13 | **Please provide Issues links to:** 14 | 15 | - Issues: #1 16 | 17 | **Provide test code:** 18 | 19 | ```Go 20 | 21 | ``` 22 | 23 | ## Description 24 | 25 | ... 26 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | on: [push] 3 | jobs: 4 | test: 5 | # name: build 6 | strategy: 7 | matrix: 8 | # go: [1.12.x, 1.13.x] 9 | os: [macOS-latest, windows-latest] # ubuntu-latest 10 | runs-on: ${{ matrix.os }} 11 | 12 | steps: 13 | - name: Set up Go 1.24.0 14 | uses: actions/setup-go@v1 15 | with: 16 | go-version: 1.24.0 17 | id: go 18 | 19 | - name: Check out code into the Go module directory 20 | uses: actions/checkout@v1 21 | 22 | - name: Get dependencies 23 | run: | 24 | go get -v -t -d ./... 25 | 26 | - name: Build 27 | run: go build -v . 28 | - name: Test 29 | run: go test -v robot_info_test.go 30 | # run: go test -v . 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | .DS_Store 3 | .vscode 4 | .idea 5 | 6 | robot_test.png 7 | # Examples 8 | examples/screen/screen 9 | examples/screen/saveCapture.png 10 | # 11 | examples/bitmap/test.png 12 | examples/bitmap/test2.png 13 | examples/bitmap/test.tif 14 | examples/bitmap/test31.tif 15 | examples/bitmap/tocbitmap.png 16 | examples/bitmap/teststr.png 17 | test/test.png 18 | 19 | # Debug files 20 | *.dSYM/ 21 | *.su 22 | debug 23 | 24 | # Architecture specific extensions/prefixes 25 | *.[568vq] 26 | [568vq].out 27 | 28 | *.cgo1.go 29 | *.cgo2.c 30 | _cgo_defun.c 31 | _cgo_gotypes.go 32 | _cgo_export.* 33 | 34 | _testmain.go 35 | 36 | *.o 37 | *.ko 38 | *.obj 39 | *.elf 40 | 41 | # Precompiled Headers 42 | *.gch 43 | *.pch 44 | 45 | # 46 | cdeps/hook 47 | event/hook 48 | vendor 49 | 50 | # Libraries 51 | *.lib 52 | # !cdeps/win32/libpng.lib 53 | # !cdeps/win32/zlib.lib 54 | # !cdeps/win64/libpng.lib 55 | # !cdeps/win64/zlib.lib 56 | # ## 57 | *.a 58 | !cdeps/mac/libpng.a 59 | !cdeps/mac/amd/libpng.a 60 | !cdeps/mac/m1/libpng.a 61 | # 62 | !cdeps/win32/libpng.a 63 | !cdeps/win64/libpng.a 64 | !cdeps/win/amd/win32/libpng.a 65 | !cdeps/win/amd/win64/libpng.a 66 | !cdeps/win/arm/libpng.a 67 | # 68 | *.la 69 | *.lo 70 | 71 | # Shared objects (inc. Windows DLLs) 72 | *.dll 73 | *.so 74 | *.so.* 75 | *.dylib 76 | 77 | # Executables 78 | *.exe 79 | *.out 80 | *.app 81 | *.i*86 82 | *.x86_64 83 | *.hex 84 | 85 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 86 | .glide/ 87 | 88 | # 89 | examples/bitmap/test_IMG.png 90 | examples/bitmap/imgToBitmap/test_01.png 91 | examples/bitmap/imgToBitmap/test_002.jpeg 92 | examples/bitmap/imgToBitmap/test_003.jpeg 93 | examples/bitmap/imgToBitmap/test_1.png 94 | examples/bitmap/imgToBitmap/test_2.jpeg 95 | examples/bitmap/imgToBitmap/test.png 96 | examples/bitmap/imgToBitmap/test_7.jpeg 97 | robot_img.png 98 | examples/bitmap/bitmapTobytes/out.jpg 99 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | os: 4 | - linux 5 | - osx 6 | 7 | go: 8 | # - 1.7.x 9 | # - 1.8.x 10 | # - 1.9.x 11 | # - 1.10.x 12 | # - 1.11.x 13 | # - 1.12.x 14 | # - 1.13.x 15 | # - 1.14.x 16 | # - 1.15.x 17 | # - 1.16.x 18 | # - 1.17.x 19 | # - 1.18.x 20 | - 1.19.x 21 | # - tip 22 | 23 | addons: 24 | apt: 25 | packages: 26 | - libx11-dev xorg-dev 27 | - libxtst-dev libpng++-dev 28 | - xcb libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxkbcommon-x11-dev 29 | # - libusb-dev 30 | - libxkbcommon-dev 31 | - xsel 32 | - xclip 33 | # 34 | - xvfb 35 | # script: 36 | # - sudo apt update 37 | # - sudo apt install libx11-dev 38 | # - sudo apt install xorg-dev 39 | # - sudo apt install libxtst-dev libpng++-dev 40 | # # Event: 41 | # - sudo apt install xcb libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxkbcommon-x11-dev 42 | # - sudo apt install libxkbcommon-dev 43 | # # Clipboard: 44 | # - sudo apt install xsel 45 | # - sudo apt install xclip 46 | # - go test -v . 47 | 48 | before_install: 49 | - export PATH=$PATH:$HOME/gopath/bin 50 | - go get -v -t -d ./... 51 | 52 | script: 53 | - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then xvfb-run go test -v ./...; fi 54 | - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then go test -v ./...; fi 55 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | ## Introduction 4 | 5 | This document explains how to contribute changes to the Robotgo project. It assumes you have followed the README.md and [API Document](https://github.com/go-vgo/robotgo/blob/master/docs/doc.md). 6 | 7 | ## Bug reports 8 | 9 | Please search the issues on the issue tracker with a variety of keywords to ensure your bug is not already reported. 10 | 11 | If unique, [open an issue](https://github.com/go-vgo/robotgo/issues/new) and answer the questions so we can understand and reproduce the problematic behavior. 12 | 13 | The burden is on you to convince us that it is actually a bug in Robotgo. This is easiest to do when you write clear, concise instructions so we can reproduce the behavior (even if it seems obvious). The more detailed and specific you are, the faster we will be able to help you. Check out [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html). 14 | 15 | Please be kind, remember that Robotgo comes at no cost to you, and you're getting free help. 16 | 17 | ## Discuss your design 18 | 19 | The project welcomes submissions but please let everyone know what you're working on if you want to change or add something to the Robotgo repositories. 20 | 21 | Before starting to write something new for the Robotgo project, please [file an issue](https://github.com/go-vgo/robotgo/issues/new). Significant changes must go through the [change proposal process](https://github.com/go-vgo/proposals) before they can be accepted. 22 | 23 | This process gives everyone a chance to validate the design, helps prevent duplication of effort, and ensures that the idea fits inside the goals for the project and tools. It also checks that the design is sound before code is written; the code review tool is not the place for high-level discussions. 24 | 25 | ## Testing redux 26 | 27 | Before sending code out for review, run all the tests for the whole tree to make sure the changes don't break other usage and keep the compatibility on upgrade. You must be test on Mac, Windows, Linux and other. You should install the CLI for Circle CI, as we are using the server for continuous testing. 28 | 29 | ## Code review 30 | 31 | In addition to the owner, Changes to Robotgo must be reviewed before they are accepted, no matter who makes the change even if it is a maintainer. We use GitHub's pull request workflow to do that and we also use [LGTM](http://lgtm.co) to ensure every PR is reviewed by vz or least 2 maintainers. 32 | 33 | 34 | ## Sign your work 35 | 36 | The sign-off is a simple line at the end of the explanation for the patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. 37 | 38 | ## Maintainers 39 | 40 | To make sure every PR is checked, we got team maintainers. A maintainer should be a contributor of Robotgo and contributed at least 4 accepted PRs. 41 | 42 | ## Owners 43 | 44 | Since Robotgo is a pure community organization without any company support, Copyright 2016 The go-vgo Project Developers. 45 | 46 | 47 | ## Versions 48 | 49 | Robotgo has the `master` branch as a tip branch and has version branches such as `v0.30.0`. `v0.40.0` is a release branch and we will tag `v0.40.0` for binary download. If `v0.40.0` has bugs, we will accept pull requests on the `v0.40.0` branch and publish a `v0.40.1` tag, after bringing the bug fix also to the master branch. 50 | 51 | Since the `master` branch is a tip version, if you wish to use Robotgo in production, please download the latest release tag version. All the branches will be protected via GitHub, all the PRs to every branch must be reviewed by two maintainers and must pass the automatic tests. 52 | 53 | ## Copyright 54 | 55 | Code that you contribute should use the standard copyright header: 56 | 57 | ``` 58 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 59 | // file at the top-level directory of this distribution and at 60 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 61 | // 62 | // Licensed under the Apache License, Version 2.0 or the MIT license 64 | // , at your 65 | // option. This file may not be copied, modified, or distributed 66 | // except according to those terms. 67 | ``` 68 | 69 | Files in the repository contain copyright from the year they are added to the year they are last changed. If the copyright author is changed, just paste the header below the old one. 70 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README_zh.md: -------------------------------------------------------------------------------- 1 | # Robotgo 2 | 3 | ## !!! Warning: this page not updated !!! 4 | 5 | [![Build Status](https://github.com/go-vgo/robotgo/workflows/Go/badge.svg)](https://github.com/go-vgo/robotgo/commits/master) 6 | [![CircleCI Status](https://circleci.com/gh/go-vgo/robotgo.svg?style=shield)](https://circleci.com/gh/go-vgo/robotgo) 7 | [![Build Status](https://travis-ci.org/go-vgo/robotgo.svg)](https://travis-ci.org/go-vgo/robotgo) 8 | ![Appveyor](https://ci.appveyor.com/api/projects/status/github/go-vgo/robotgo?branch=master&svg=true) 9 | [![Go Report Card](https://goreportcard.com/badge/github.com/go-vgo/robotgo)](https://goreportcard.com/report/github.com/go-vgo/robotgo) 10 | [![GoDoc](https://godoc.org/github.com/go-vgo/robotgo?status.svg)](https://godoc.org/github.com/go-vgo/robotgo) 11 | [![GitHub release](https://img.shields.io/github/release/go-vgo/robotgo.svg)](https://github.com/go-vgo/robotgo/releases/latest) 12 | [![Join the chat at https://gitter.im/go-vgo/robotgo](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-vgo/robotgo?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 13 | 14 | > Golang 跨平台自动化系统,控制键盘、鼠标、位图、图像、读取屏幕,进程、窗口句柄以及全局事件监听 15 | 16 | RobotGo 支持 Mac, Windows, and Linux(X11). 17 | 18 |
19 | 20 | 提 Issues 请到 [Github](https://github.com/go-vgo/robotgo), 便于统一管理和即时更新; `REDAME_zh.md 已废弃, 不再更新` 21 | 22 | ## Contents 23 | - [Docs](#docs) 24 | - [Binding](#binding) 25 | - [Requirements](#requirements) 26 | - [Installation](#installation) 27 | - [Update](#update) 28 | - [Examples](#examples) 29 | - [Cross-Compiling](#crosscompiling) 30 | - [Authors](#authors) 31 | - [Plans](#plans) 32 | - [Donate](#donate) 33 | - [Contributors](#contributors) 34 | - [License](#license) 35 | 36 | ## Docs 37 | - [GoDoc](https://godoc.org/github.com/go-vgo/robotgo)
38 | 39 | 41 | 42 | ## Binding: 43 | 44 | [Robotn](https://github.com/vcaesar/robotn), binding JavaScript and other, support more language. 45 | 46 | ## Requirements: 47 | 环境要求: 48 | 49 | 在安装 RobotGo 之前, 请确保 `Golang、GCC` 被正确安装 50 | 51 | ### ALL: 52 | ``` 53 | Golang 54 | 55 | GCC 56 | ``` 57 | 58 | #### For Mac OS X: 59 | 60 | Xcode Command Line Tools (And Privacy setting: [#277](https://github.com/go-vgo/robotgo/issues/277) ) 61 | 62 | ``` 63 | xcode-select --install 64 | ``` 65 | 66 | #### For Windows: 67 | 68 | [MinGW-w64](https://sourceforge.net/projects/mingw-w64/files) (推荐使用) 69 | 70 | ``` 71 | Or the other GCC (But you should compile the "libpng" with yourself. 72 | Or you can removed the bitmap.go.) 73 | ``` 74 | 75 | #### For everything else (Linux 等其他系统): 76 | 77 | ``` 78 | GCC, 79 | libpng(bitmap) 80 | 81 | X11 with the XTest extension (also known as the Xtst library) 82 | 83 | 事件: 84 | 85 | xcb, xkb, libxkbcommon 86 | 87 | Clipboard: 88 | 89 | xsel xclip 90 | ``` 91 | 92 | ##### Ubuntu: 93 | 94 | ```yml 95 | sudo apt install gcc libc6-dev 96 | 97 | sudo apt install libx11-dev xorg-dev libxtst-dev libpng++-dev 98 | 99 | sudo apt install xcb libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxkbcommon-x11-dev libxkbcommon-dev 100 | 101 | sudo apt install xsel xclip 102 | 103 | ``` 104 | 105 | ##### Fedora: 106 | 107 | ```yml 108 | sudo dnf install libXtst-devel libxkbcommon-devel libxkbcommon-x11-devel xorg-x11-xkb-utils-devel 109 | 110 | sudo dnf install libpng-devel 111 | 112 | sudo dnf install xsel xclip 113 | ``` 114 | 115 | ## Installation: 116 | With Go module support (Go 1.11+), just import: 117 | 118 | ```go 119 | import "github.com/go-vgo/robotgo" 120 | ``` 121 | 122 | Otherwise, to install the robotgo package, run the command: 123 | 124 | ``` 125 | go get github.com/go-vgo/robotgo 126 | ``` 127 | 128 | png.h: No such file or directory? Please see [issues/47](https://github.com/go-vgo/robotgo/issues/47). 129 | 130 | ## Update: 131 | ``` 132 | go get -u github.com/go-vgo/robotgo 133 | ``` 134 | 135 | 注意 go1.10.x C 文件编译缓存问题, [golang #24355](https://github.com/golang/go/issues/24355). 136 | `go mod vendor` problem, [golang #26366](https://github.com/golang/go/issues/26366). 137 | 138 | 139 | ## [Examples:](https://github.com/go-vgo/robotgo/blob/master/examples) 140 | 141 | #### [鼠标](https://github.com/go-vgo/robotgo/blob/master/examples/mouse/main.go) 142 | 143 | ```Go 144 | package main 145 | 146 | import ( 147 | "github.com/go-vgo/robotgo" 148 | ) 149 | 150 | func main() { 151 | // robotgo.ScrollMouse(10, "up") 152 | robotgo.Scroll(0, -10) 153 | robotgo.Scroll(100, 0) 154 | robotgo.MilliSleep(100) 155 | // robotgo.ScrollRelative(10, -100) 156 | robotgo.ScrollSmooth(-10, 6) 157 | 158 | robotgo.MouseSleep = 100 159 | robotgo.Move(10, 20) 160 | robotgo.MoveRelative(0, -10) 161 | robotgo.Drag(10, 10) 162 | 163 | robotgo.Click("left", true) 164 | robotgo.MoveSmooth(100, 200, 1.0, 10.0) 165 | 166 | robotgo.Toggle("left") 167 | robotgo.Toggle("left", "up") 168 | } 169 | ``` 170 | 171 | #### [键盘](https://github.com/go-vgo/robotgo/blob/master/examples/key/main.go) 172 | 173 | ```Go 174 | package main 175 | 176 | import ( 177 | "fmt" 178 | 179 | "github.com/go-vgo/robotgo" 180 | ) 181 | 182 | func main() { 183 | robotgo.TypeStr("Hello World. Winter is coming!") 184 | robotgo.TypeStr("だんしゃり", 1.0) 185 | // robotgo.TypeStr("テストする") 186 | 187 | robotgo.TypeStr("Hi galaxy. こんにちは世界.") 188 | robotgo.Sleep(1) 189 | 190 | // ustr := uint32(robotgo.CharCodeAt("テストする", 0)) 191 | // robotgo.UnicodeType(ustr) 192 | 193 | robotgo.KeySleep = 100 194 | robotgo.KeyTap("enter") 195 | // robotgo.TypeStr("en") 196 | robotgo.KeyTap("i", "alt", "command") 197 | 198 | arr := []string{"alt", "command"} 199 | robotgo.KeyTap("i", arr) 200 | 201 | robotgo.MilliSleep(100) 202 | robotgo.KeyToggle("a") 203 | robotgo.KeyToggle("a", "up") 204 | 205 | robotgo.WriteAll("テストする") 206 | text, err := robotgo.ReadAll() 207 | if err == nil { 208 | fmt.Println(text) 209 | } 210 | } 211 | ``` 212 | 213 | #### [屏幕](https://github.com/go-vgo/robotgo/blob/master/examples/screen/main.go) 214 | 215 | ```Go 216 | package main 217 | 218 | import ( 219 | "fmt" 220 | 221 | "github.com/go-vgo/robotgo" 222 | "github.com/vcaesar/imgo" 223 | ) 224 | 225 | func main() { 226 | x, y := robotgo.GetMousePos() 227 | fmt.Println("pos: ", x, y) 228 | 229 | color := robotgo.GetPixelColor(100, 200) 230 | fmt.Println("color----", color) 231 | 232 | sx, sy := robotgo.GetScreenSize() 233 | fmt.Println("get screen size: ", sx, sy) 234 | 235 | bit := robotgo.CaptureScreen(10, 10, 30, 30) 236 | defer robotgo.FreeBitmap(bit) 237 | robotgo.SaveBitmap(bit, "test_1.png") 238 | 239 | img := robotgo.ToImage(bit) 240 | imgo.Save("test.png", img) 241 | } 242 | ``` 243 | 244 | #### [位图](https://github.com/go-vgo/robotgo/blob/master/examples/bitmap/main.go) 245 | 246 | ```Go 247 | package main 248 | 249 | import ( 250 | "fmt" 251 | 252 | "github.com/go-vgo/robotgo" 253 | ) 254 | 255 | func main() { 256 | bitmap := robotgo.CaptureScreen(10, 20, 30, 40) 257 | // use `defer robotgo.FreeBitmap(bit)` to free the bitmap 258 | defer robotgo.FreeBitmap(bitmap) 259 | 260 | fmt.Println("bitmap...", bitmap) 261 | img := robotgo.ToImage(bitmap) 262 | robotgo.SavePng(img, "test_1.png") 263 | 264 | bit2 := robotgo.ToCBitmap(robotgo.ImgToBitmap(img)) 265 | fx, fy := robotgo.FindBitmap(bit2) 266 | fmt.Println("FindBitmap------ ", fx, fy) 267 | robotgo.Move(fx, fy) 268 | 269 | arr := robotgo.FindAllBitmap(bit2) 270 | fmt.Println("Find all bitmap: ", arr) 271 | robotgo.SaveBitmap(bitmap, "test.png") 272 | 273 | fx, fy = robotgo.FindBitmap(bitmap) 274 | fmt.Println("FindBitmap------", fx, fy) 275 | 276 | robotgo.SaveBitmap(bitmap, "test.png") 277 | } 278 | ``` 279 | 280 | #### [OpenCV](https://github.com/vcaesar/gcv) 281 | 282 | ```Go 283 | package main 284 | 285 | import ( 286 | "fmt" 287 | "math/rand" 288 | 289 | "github.com/go-vgo/robotgo" 290 | "github.com/vcaesar/gcv" 291 | ) 292 | 293 | func main() { 294 | opencv() 295 | } 296 | 297 | func opencv() { 298 | name := "test.png" 299 | name1 := "test_001.png" 300 | robotgo.SaveCapture(name1, 10, 10, 30, 30) 301 | robotgo.SaveCapture(name) 302 | 303 | fmt.Print("gcv find image: ") 304 | fmt.Println(gcv.FindImgFile(name1, name)) 305 | fmt.Println(gcv.FindAllImgFile(name1, name)) 306 | 307 | bit := robotgo.OpenBitmap(name1) 308 | defer robotgo.FindBitmap(bit) 309 | fmt.Print("find bitmap: ") 310 | fmt.Println(robotgo.FindBitmap(bit)) 311 | 312 | // bit0 := robotgo.CaptureScreen() 313 | // img := robotgo.ToImage(bit0) 314 | // bit1 := robotgo.CaptureScreen(10, 10, 30, 30) 315 | // img1 := robotgo.ToImage(bit1) 316 | // defer robotgo.FreeBitmapArr(bit0, bit1) 317 | img := robotgo.CaptureImg() 318 | img1 := robotgo.CaptureImg(10, 10, 30, 30) 319 | 320 | fmt.Print("gcv find image: ") 321 | fmt.Println(gcv.FindImg(img1, img)) 322 | fmt.Println() 323 | 324 | res := gcv.FindAllImg(img1, img) 325 | fmt.Println(res[0].TopLeft.Y, res[0].Rects.TopLeft.X, res) 326 | x, y := res[0].TopLeft.X, res[0].TopLeft.Y 327 | robotgo.Move(x, y-rand.Intn(5)) 328 | robotgo.MilliSleep(100) 329 | robotgo.Click() 330 | 331 | res = gcv.FindAll(img1, img) // use find template and sift 332 | fmt.Println("find all: ", res) 333 | res1 := gcv.Find(img1, img) 334 | fmt.Println("find: ", res1) 335 | 336 | img2, _, _ := robotgo.DecodeImg("test_001.png") 337 | x, y = gcv.FindX(img2, img) 338 | fmt.Println(x, y) 339 | } 340 | ``` 341 | 342 | #### [事件](https://github.com/go-vgo/robotgo/blob/master/examples/gohook/main.go) 343 | 344 | ```Go 345 | package main 346 | 347 | import ( 348 | "fmt" 349 | 350 | "github.com/go-vgo/robotgo" 351 | hook "github.com/robotn/gohook" 352 | ) 353 | 354 | func main() { 355 | add() 356 | low() 357 | event() 358 | } 359 | 360 | func add() { 361 | fmt.Println("--- Please press ctrl + shift + q to stop hook ---") 362 | robotgo.EventHook(hook.KeyDown, []string{"q", "ctrl", "shift"}, func(e hook.Event) { 363 | fmt.Println("ctrl-shift-q") 364 | robotgo.EventEnd() 365 | }) 366 | 367 | fmt.Println("--- Please press w---") 368 | robotgo.EventHook(hook.KeyDown, []string{"w"}, func(e hook.Event) { 369 | fmt.Println("w") 370 | }) 371 | 372 | s := robotgo.EventStart() 373 | <-robotgo.EventProcess(s) 374 | } 375 | 376 | func low() { 377 | evChan := hook.Start() 378 | defer hook.End() 379 | 380 | for ev := range evChan { 381 | fmt.Println("hook: ", ev) 382 | } 383 | } 384 | 385 | func event() { 386 | ok := robotgo.AddEvents("q", "ctrl", "shift") 387 | if ok { 388 | fmt.Println("add events...") 389 | } 390 | 391 | keve := robotgo.AddEvent("k") 392 | if keve { 393 | fmt.Println("you press... ", "k") 394 | } 395 | 396 | mleft := robotgo.AddEvent("mleft") 397 | if mleft { 398 | fmt.Println("you press... ", "mouse left button") 399 | } 400 | } 401 | ``` 402 | 403 | #### [窗口句柄](https://github.com/go-vgo/robotgo/blob/master/examples/window/main.go) 404 | 405 | ```Go 406 | package main 407 | 408 | import ( 409 | "fmt" 410 | 411 | "github.com/go-vgo/robotgo" 412 | ) 413 | 414 | func main() { 415 | fpid, err := robotgo.FindIds("Google") 416 | if err == nil { 417 | fmt.Println("pids...", fpid) 418 | 419 | if len(fpid) > 0 { 420 | robotgo.ActivePID(fpid[0]) 421 | 422 | robotgo.Kill(fpid[0]) 423 | } 424 | } 425 | 426 | robotgo.ActiveName("chrome") 427 | 428 | isExist, err := robotgo.PidExists(100) 429 | if err == nil && isExist { 430 | fmt.Println("pid exists is", isExist) 431 | 432 | robotgo.Kill(100) 433 | } 434 | 435 | abool := robotgo.Alert("test", "robotgo") 436 | if abool { 437 | fmt.Println("ok@@@ ", "ok") 438 | } 439 | 440 | title := robotgo.GetTitle() 441 | fmt.Println("title@@@ ", title) 442 | } 443 | ``` 444 | 445 | ## CrossCompiling 446 | 447 | ##### Windows64 to windows32 448 | ```Go 449 | SET CGO_ENABLED=1 450 | SET GOARCH=386 451 | go build main.go 452 | ``` 453 | 454 | #### Other to windows 455 | 456 | Install Requirements (Ubuntu): 457 | ```bash 458 | sudo apt install gcc-multilib 459 | sudo apt install gcc-mingw-w64 460 | sudo apt install libz-mingw-w64-dev 461 | ``` 462 | 463 | Build the binary: 464 | 465 | ```Go 466 | GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ go build -x ./ 467 | ``` 468 | 469 | ``` 470 | // CC=mingw-w64\x86_64-7.2.0-win32-seh-rt_v5-rev1\mingw64\bin\gcc.exe 471 | // CXX=mingw-w64\x86_64-7.2.0-win32-seh-rt_v5-rev1\mingw64\bin\g++.exe 472 | ``` 473 | 474 | ## Authors 475 | * [The author is vz](https://github.com/vcaesar) 476 | * [Maintainers](https://github.com/orgs/go-vgo/people) 477 | * [Contributors](https://github.com/go-vgo/robotgo/graphs/contributors) 478 | 479 | ## Plans 480 | - 更新 Find an image on screen, read pixels from an image 481 | - 更新 Window Handle 482 | - 尝试支持 Android, 也许支持 IOS 483 | 484 | ## Contributors 485 | 486 | - See [contributors page](https://github.com/go-vgo/robotgo/graphs/contributors) for full list of contributors. 487 | - See [Contribution Guidelines](https://github.com/go-vgo/robotgo/blob/master/CONTRIBUTING.md). 488 | 489 | ## License 490 | 491 | Robotgo is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0), with portions covered by various BSD-like licenses. 492 | 493 | See [LICENSE-APACHE](http://www.apache.org/licenses/LICENSE-2.0), [LICENSE-MIT](https://github.com/go-vgo/robotgo/blob/master/LICENSE). -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # version format 2 | version: "{build}" 3 | 4 | # Operating system (build VM template) 5 | # os: Windows Server 2012 R2 6 | os: Visual Studio 2017 7 | 8 | # Platform. 9 | # platform: 10 | # - x64 11 | # - x86 12 | 13 | clone_folder: c:\gopath\src\github.com\go-vgo\robotgo 14 | 15 | # Environment variables 16 | environment: 17 | global: 18 | GOPATH: C:\gopath 19 | CC: gcc.exe 20 | matrix: 21 | - GOARCH: amd64 22 | # GOVERSION: 1.9.3 23 | GETH_ARCH: amd64 24 | MSYS2_ARCH: x86_64 25 | MSYS2_BITS: 64 26 | MSYSTEM: MINGW64 27 | PATH: C:\msys64\mingw64\bin\;C:\Program Files (x86)\NSIS\;%PATH% 28 | - GOARCH: 386 29 | # GOVERSION: 1.9.3 30 | GETH_ARCH: 386 31 | MSYS2_ARCH: i686 32 | MSYS2_BITS: 32 33 | MSYSTEM: MINGW32 34 | PATH: C:\msys64\mingw32\bin\;C:\Program Files (x86)\NSIS\;%PATH% 35 | # - COMPILER: MINGW_W64 36 | # ARCHITECTURE: x64 37 | GOVERSION: 1.23.0 38 | # GOPATH: c:\gopath 39 | 40 | # scripts that run after cloning repository 41 | # install: 42 | # - set PATH=%GOPATH%\bin;c:\go\bin;%PATH% 43 | # - go version 44 | # - go env 45 | # - gcc --version 46 | # - python --version 47 | 48 | install: 49 | - set PATH=%GOPATH%\bin;c:\go\bin;%PATH% 50 | - git submodule update --init 51 | - rmdir C:\go /s /q 52 | - appveyor DownloadFile https://storage.googleapis.com/golang/go%GOVERSION%.windows-%GETH_ARCH%.zip 53 | - 7z x go%GOVERSION%.windows-%GETH_ARCH%.zip -y -oC:\ > NUL 54 | - go version 55 | - go env 56 | - gcc --version 57 | 58 | # To run your custom scripts instead of automatic MSBuild 59 | build_script: 60 | # We need to disable firewall - https://github.com/appveyor/ci/issues/1579#issuecomment-309830648 61 | - ps: Disable-NetFirewallRule -DisplayName 'File and Printer Sharing (SMB-Out)' 62 | - cd c:\gopath\src\github.com\go-vgo\robotgo 63 | - git branch 64 | - go get -t ./... 65 | 66 | # To run your custom scripts instead of automatic tests 67 | test_script: 68 | # Unit tests 69 | - ps: Add-AppveyorTest "Unit Tests" -Outcome Running 70 | - go test -v github.com/go-vgo/robotgo/... 71 | - ps: Update-AppveyorTest "Unit Tests" -Outcome Passed 72 | 73 | # notifications: 74 | # - provider: Email 75 | # to: 76 | # - .io 77 | # on_build_failure: true 78 | # on_build_status_changed: true 79 | # to disable deployment 80 | deploy: off 81 | -------------------------------------------------------------------------------- /base/LICENSE: -------------------------------------------------------------------------------- 1 | The software is licensed under the terms of the MIT license. 2 | 3 | Copyright 2010 Michael Sanders, AE and the go-vgo Project Developers. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /base/MMBitmap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef MMBITMAP_H 3 | #define MMBITMAP_H 4 | 5 | #include "types.h" 6 | #include "rgb.h" 7 | #include 8 | #include 9 | 10 | struct _MMBitmap { 11 | uint8_t *imageBuffer; /* Pixels stored in Quad I format; */ 12 | int32_t width; /* Never 0, unless image is NULL. */ 13 | int32_t height; /* Never 0, unless image is NULL. */ 14 | 15 | int32_t bytewidth; /* The aligned width (width + padding). */ 16 | uint8_t bitsPerPixel; /* Should be either 24 or 32. */ 17 | uint8_t bytesPerPixel; /* For convenience; should be bitsPerPixel / 8. */ 18 | }; 19 | 20 | typedef struct _MMBitmap MMBitmap; 21 | typedef MMBitmap *MMBitmapRef; 22 | 23 | #define MMBitmapPointInBounds(image, p) ((p).x < (image)->width && (p).y < (image)->height) 24 | 25 | /* Get pointer to pixel of MMBitmapRef. No bounds checking is performed */ 26 | #define MMRGBColorRefAtPoint(image, x, y) \ 27 | (MMRGBColor *)(assert(MMBitmapPointInBounds(image, MMPointInt32Make(x, y))), \ 28 | ((image)->imageBuffer) + (((image)->bytewidth * (y)) + ((x) * (image)->bytesPerPixel))) 29 | 30 | /* Dereference pixel of MMBitmapRef. Again, no bounds checking is performed. */ 31 | #define MMRGBColorAtPoint(image, x, y) *MMRGBColorRefAtPoint(image, x, y) 32 | 33 | /* Hex/integer value of color at point. */ 34 | #define MMRGBHexAtPoint(image, x, y) hexFromMMRGB(MMRGBColorAtPoint(image, x, y)) 35 | 36 | #endif /* MMBITMAP_H */ -------------------------------------------------------------------------------- /base/base.go: -------------------------------------------------------------------------------- 1 | // https://github.com/golang/go/issues/26366 2 | package base 3 | -------------------------------------------------------------------------------- /base/bitmap_free_c.h: -------------------------------------------------------------------------------- 1 | #include "MMBitmap.h" 2 | #include 3 | #include 4 | 5 | MMBitmapRef createMMBitmap_c(uint8_t *buffer, int32_t width, int32_t height, 6 | int32_t bytewidth, uint8_t bitsPerPixel, uint8_t bytesPerPixel 7 | ) { 8 | MMBitmapRef bitmap = malloc(sizeof(MMBitmap)); 9 | if (bitmap == NULL) { return NULL; } 10 | 11 | bitmap->imageBuffer = buffer; 12 | bitmap->width = width; 13 | bitmap->height = height; 14 | bitmap->bytewidth = bytewidth; 15 | bitmap->bitsPerPixel = bitsPerPixel; 16 | bitmap->bytesPerPixel = bytesPerPixel; 17 | 18 | return bitmap; 19 | } 20 | 21 | void destroyMMBitmap(MMBitmapRef bitmap) { 22 | assert(bitmap != NULL); 23 | 24 | if (bitmap->imageBuffer != NULL) { 25 | free(bitmap->imageBuffer); 26 | bitmap->imageBuffer = NULL; 27 | } 28 | 29 | free(bitmap); 30 | } 31 | 32 | void destroyMMBitmapBuffer(char * bitmapBuffer, void * hint) { 33 | if (bitmapBuffer != NULL) { 34 | free(bitmapBuffer); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /base/deadbeef_rand.h: -------------------------------------------------------------------------------- 1 | #ifndef DEADBEEF_RAND_H 2 | #define DEADBEEF_RAND_H 3 | 4 | #include 5 | 6 | #define DEADBEEF_MAX UINT32_MAX 7 | /* Dead Beef Random Number Generator From: http://inglorion.net/software/deadbeef_rand */ 8 | 9 | /* Generates a random number between 0 and DEADBEEF_MAX. */ 10 | uint32_t deadbeef_rand(void); 11 | 12 | /* Seeds with the given integer. */ 13 | void deadbeef_srand(uint32_t x); 14 | 15 | /* Generates seed from the current time. */ 16 | uint32_t deadbeef_generate_seed(void); 17 | 18 | /* Seeds with the above function. */ 19 | #define deadbeef_srand_time() deadbeef_srand(deadbeef_generate_seed()) 20 | 21 | /* Returns random double in the range [a, b).*/ 22 | #define DEADBEEF_UNIFORM(a, b) \ 23 | ((a) + (deadbeef_rand() / (((double)DEADBEEF_MAX / (b - a) + 1)))) 24 | 25 | /* Returns random integer in the range [a, b).*/ 26 | #define DEADBEEF_RANDRANGE(a, b) (uint32_t)DEADBEEF_UNIFORM(a, b) 27 | 28 | #endif /* DEADBEEF_RAND_H */ 29 | -------------------------------------------------------------------------------- /base/deadbeef_rand_c.h: -------------------------------------------------------------------------------- 1 | #include "deadbeef_rand.h" 2 | #include 3 | 4 | static uint32_t deadbeef_seed; 5 | static uint32_t deadbeef_beef = 0xdeadbeef; 6 | 7 | uint32_t deadbeef_rand(void) { 8 | deadbeef_seed = (deadbeef_seed << 7) ^ ((deadbeef_seed >> 25) + deadbeef_beef); 9 | deadbeef_beef = (deadbeef_beef << 7) ^ ((deadbeef_beef >> 25) + 0xdeadbeef); 10 | return deadbeef_seed; 11 | } 12 | 13 | void deadbeef_srand(uint32_t x) { 14 | deadbeef_seed = x; 15 | deadbeef_beef = 0xdeadbeef; 16 | } 17 | 18 | /* Taken directly from the documentation: http://inglorion.net/software/cstuff/deadbeef_rand/ */ 19 | uint32_t deadbeef_generate_seed(void) { 20 | uint32_t t = (uint32_t)time(NULL); 21 | uint32_t c = (uint32_t)clock(); 22 | return (t << 24) ^ (c << 11) ^ t ^ (size_t) &c; 23 | } 24 | -------------------------------------------------------------------------------- /base/inline_keywords.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* A complicated, portable model for declaring inline functions in header files. */ 4 | #if !defined(H_INLINE) 5 | #if defined(__GNUC__) 6 | #define H_INLINE static __inline__ __attribute__((always_inline)) 7 | #elif defined(__MWERKS__) || defined(__cplusplus) 8 | #define H_INLINE static inline 9 | #elif defined(_MSC_VER) 10 | #define H_INLINE static __inline 11 | #elif TARGET_OS_WIN32 12 | #define H_INLINE static __inline__ 13 | #endif 14 | #endif /* H_INLINE */ 15 | -------------------------------------------------------------------------------- /base/microsleep.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef MICROSLEEP_H 3 | #define MICROSLEEP_H 4 | 5 | #include "os.h" 6 | #include "inline_keywords.h" 7 | 8 | // todo: removed 9 | #if !defined(IS_WINDOWS) 10 | /* Make sure nanosleep gets defined even when using C89. */ 11 | #if !defined(__USE_POSIX199309) || !__USE_POSIX199309 12 | #define __USE_POSIX199309 1 13 | #endif 14 | 15 | #include /* For nanosleep() */ 16 | #endif 17 | 18 | /* A more widely supported alternative to usleep(), based on Sleep() in Windows and nanosleep() */ 19 | H_INLINE void microsleep(double milliseconds) { 20 | #if defined(IS_WINDOWS) 21 | Sleep((DWORD)milliseconds); /* (Unfortunately truncated to a 32-bit integer.) */ 22 | #else 23 | /* Technically, nanosleep() is not an ANSI function */ 24 | struct timespec sleepytime; 25 | sleepytime.tv_sec = milliseconds / 1000; 26 | sleepytime.tv_nsec = (milliseconds - (sleepytime.tv_sec * 1000)) * 1000000; 27 | nanosleep(&sleepytime, NULL); 28 | #endif 29 | } 30 | 31 | #endif /* MICROSLEEP_H */ 32 | -------------------------------------------------------------------------------- /base/os.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef OS_H 3 | #define OS_H 4 | 5 | #if !defined(IS_MACOSX) && defined(__APPLE__) && defined(__MACH__) 6 | #define IS_MACOSX 7 | #endif /* IS_MACOSX */ 8 | 9 | #if !defined(IS_WINDOWS) && (defined(WIN32) || defined(_WIN32) || \ 10 | defined(__WIN32__) || defined(__WINDOWS__) || defined(__CYGWIN__)) 11 | #define IS_WINDOWS 12 | #endif /* IS_WINDOWS */ 13 | 14 | #if !defined(USE_X11) && !defined(NUSE_X11) && !defined(IS_MACOSX) && !defined(IS_WINDOWS) 15 | #define USE_X11 16 | #endif /* USE_X11 */ 17 | 18 | #if defined(IS_WINDOWS) 19 | #define STRICT /* Require use of exact types. */ 20 | #define WIN32_LEAN_AND_MEAN 1 /* Speed up compilation. */ 21 | #include 22 | #elif !defined(IS_MACOSX) && !defined(USE_X11) 23 | #error "Sorry, this platform isn't supported yet!" 24 | #endif 25 | 26 | /* Interval to align by for large buffers (e.g. bitmaps). Must be a power of 2. */ 27 | #ifndef BYTE_ALIGN 28 | #define BYTE_ALIGN 4 /* Bytes to align pixel buffers to. */ 29 | /* #include */ 30 | /* #define BYTE_ALIGN (sizeof(size_t)) */ 31 | #endif /* BYTE_ALIGN */ 32 | 33 | #if BYTE_ALIGN == 0 34 | /* No alignment needed. */ 35 | #define ADD_PADDING(width) (width) 36 | #else 37 | /* Aligns given width to padding. */ 38 | #define ADD_PADDING(width) (BYTE_ALIGN + (((width) - 1) & ~(BYTE_ALIGN - 1))) 39 | #endif 40 | 41 | #if defined(IS_WINDOWS) 42 | #if defined (_WIN64) 43 | #define RobotGo_64 44 | #else 45 | #define RobotGo_32 46 | #endif 47 | #else 48 | #if defined (__x86_64__) 49 | #define RobotGo_64 50 | #else 51 | #define RobotGo_32 52 | #endif 53 | #endif 54 | 55 | #endif /* OS_H */ 56 | -------------------------------------------------------------------------------- /base/pubs.h: -------------------------------------------------------------------------------- 1 | #if defined(IS_WINDOWS) 2 | BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { 3 | uint32_t *count = (uint32_t*)dwData; 4 | (*count)++; 5 | return TRUE; 6 | } 7 | 8 | typedef struct{ 9 | HWND hWnd; 10 | DWORD dwPid; 11 | }WNDINFO; 12 | 13 | BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam){ 14 | WNDINFO* pInfo = (WNDINFO*)lParam; 15 | DWORD dwProcessId = 0; 16 | GetWindowThreadProcessId(hWnd, &dwProcessId); 17 | 18 | if (dwProcessId == pInfo->dwPid) { 19 | pInfo->hWnd = hWnd; 20 | return FALSE; 21 | } 22 | return TRUE; 23 | } 24 | 25 | HWND GetHwndByPid(DWORD dwProcessId) { 26 | WNDINFO info = {0}; 27 | info.hWnd = NULL; 28 | info.dwPid = dwProcessId; 29 | EnumWindows(EnumWindowsProc, (LPARAM)&info); 30 | 31 | return info.hWnd; 32 | } 33 | #endif -------------------------------------------------------------------------------- /base/rgb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef RGB_H 3 | #define RGB_H 4 | 5 | #include /* For abs() */ 6 | #include 7 | #include "inline_keywords.h" /* For H_INLINE */ 8 | #include 9 | 10 | /* #define MMRGB_IS_BGR (offsetof(MMRGBColor, red) > offsetof(MMRGBColor, blue)) */ 11 | #define MMRGB_IS_BGR 1 12 | 13 | struct _MMRGBColor { 14 | uint8_t blue; 15 | uint8_t green; 16 | uint8_t red; 17 | }; 18 | typedef struct _MMRGBColor MMRGBColor; 19 | 20 | /* MMRGBHex is a hexadecimal color value*/ 21 | typedef uint32_t MMRGBHex; 22 | 23 | #define MMRGBHEX_MIN 0x000000 24 | #define MMRGBHEX_MAX 0xFFFFFF 25 | 26 | /* Converts rgb color to hexadecimal value. */ 27 | #define RGB_TO_HEX(red, green, blue) (((red) << 16) | ((green) << 8) | (blue)) 28 | 29 | /* Convenience wrapper for MMRGBColors. */ 30 | H_INLINE MMRGBHex hexFromMMRGB(MMRGBColor rgb) { 31 | return RGB_TO_HEX(rgb.red, rgb.green, rgb.blue); 32 | } 33 | 34 | #define RED_FROM_HEX(hex) ((hex >> 16) & 0xFF) 35 | #define GREEN_FROM_HEX(hex) ((hex >> 8) & 0xFF) 36 | #define BLUE_FROM_HEX(hex) (hex & 0xFF) 37 | 38 | /* Converts hexadecimal color to MMRGBColor. */ 39 | H_INLINE MMRGBColor MMRGBFromHex(MMRGBHex hex) { 40 | MMRGBColor color; 41 | color.red = RED_FROM_HEX(hex); 42 | color.green = GREEN_FROM_HEX(hex); 43 | color.blue = BLUE_FROM_HEX(hex); 44 | return color; 45 | } 46 | 47 | /* Check absolute equality of two RGB colors. */ 48 | #define MMRGBColorEqualToColor(c1, c2) ((c1).red == (c2).red && \ 49 | (c1).blue == (c2).blue && \ 50 | (c1).green == (c2).green) 51 | 52 | /* Returns whether two colors are similar within the given range, |tolerance|.*/ 53 | H_INLINE int MMRGBColorSimilarToColor(MMRGBColor c1, MMRGBColor c2, float tolerance) { 54 | /* Speedy case */ 55 | if (tolerance <= 0.0f) { 56 | return MMRGBColorEqualToColor(c1, c2); 57 | } else { /* Otherwise, use a Euclidean space to determine similarity */ 58 | uint8_t d1 = c1.red - c2.red; 59 | uint8_t d2 = c1.green - c2.green; 60 | uint8_t d3 = c1.blue - c2.blue; 61 | return sqrt((double)(d1 * d1) + (d2 * d2) + 62 | (d3 * d3)) <= (tolerance * 442.0f); 63 | } 64 | } 65 | 66 | /* Identical to MMRGBColorSimilarToColor, only for hex values. */ 67 | H_INLINE int MMRGBHexSimilarToColor(MMRGBHex h1, MMRGBHex h2, float tolerance) { 68 | if (tolerance <= 0.0f) { 69 | return h1 == h2; 70 | } else { 71 | int d1 = RED_FROM_HEX(h1) - RED_FROM_HEX(h2); 72 | int d2 = GREEN_FROM_HEX(h1) - GREEN_FROM_HEX(h2); 73 | int d3 = BLUE_FROM_HEX(h1) - BLUE_FROM_HEX(h2); 74 | return sqrt((double)(d1 * d1) + (d2 * d2) + 75 | (d3 * d3)) <= (tolerance * 442.0f); 76 | } 77 | } 78 | 79 | #endif /* RGB_H */ 80 | -------------------------------------------------------------------------------- /base/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef TYPES_H 3 | #define TYPES_H 4 | 5 | #include "os.h" 6 | #include "inline_keywords.h" /* For H_INLINE */ 7 | #include 8 | #include 9 | #include 10 | 11 | /* Some generic, cross-platform types. */ 12 | #ifdef RobotGo_64 13 | typedef int64_t intptr; 14 | typedef uint64_t uintptr; 15 | #else 16 | typedef int32_t intptr; 17 | typedef uint32_t uintptr; // Unsigned pointer integer 18 | #endif 19 | 20 | struct _MMPointInt32 { 21 | int32_t x; 22 | int32_t y; 23 | }; 24 | typedef struct _MMPointInt32 MMPointInt32; 25 | 26 | struct _MMSizeInt32 { 27 | int32_t w; 28 | int32_t h; 29 | }; 30 | typedef struct _MMSizeInt32 MMSizeInt32; 31 | 32 | struct _MMRectInt32 { 33 | MMPointInt32 origin; 34 | MMSizeInt32 size; 35 | }; 36 | typedef struct _MMRectInt32 MMRectInt32; 37 | 38 | H_INLINE MMPointInt32 MMPointInt32Make(int32_t x, int32_t y) { 39 | MMPointInt32 point; 40 | point.x = x; 41 | point.y = y; 42 | return point; 43 | } 44 | 45 | H_INLINE MMSizeInt32 MMSizeInt32Make(int32_t w, int32_t h) { 46 | MMSizeInt32 size; 47 | size.w = w; 48 | size.h = h; 49 | return size; 50 | } 51 | 52 | H_INLINE MMRectInt32 MMRectInt32Make(int32_t x, int32_t y, int32_t w, int32_t h) { 53 | MMRectInt32 rect; 54 | rect.origin = MMPointInt32Make(x, y); 55 | rect.size = MMSizeInt32Make(w, h); 56 | return rect; 57 | } 58 | 59 | #define MMPointZero MMPointInt32Make(0, 0) 60 | 61 | #if defined(IS_MACOSX) 62 | #define CGPointFromMMPointInt32(p) CGPointMake((CGFloat)(p).x, (CGFloat)(p).y) 63 | #define MMPointInt32FromCGPoint(p) MMPointInt32Make((int32_t)(p).x, (int32_t)(p).y) 64 | #elif defined(IS_WINDOWS) 65 | #define MMPointInt32FromPOINT(p) MMPointInt32Make((int32_t)p.x, (int32_t)p.y) 66 | #endif 67 | 68 | #endif /* TYPES_H */ 69 | -------------------------------------------------------------------------------- /base/xdisplay_c.h: -------------------------------------------------------------------------------- 1 | #include /* For fputs() */ 2 | #include /* For atexit() */ 3 | #include 4 | 5 | static Display *mainDisplay = NULL; 6 | static int registered = 0; 7 | 8 | static char *displayName = NULL; 9 | static int hasDisplayNameChanged = 0; 10 | 11 | void XCloseMainDisplay(void) { 12 | if (mainDisplay != NULL) { 13 | XCloseDisplay(mainDisplay); 14 | mainDisplay = NULL; 15 | } 16 | } 17 | 18 | Display *XGetMainDisplay(void) { 19 | /* Close the display if displayName has changed */ 20 | if (hasDisplayNameChanged) { 21 | XCloseMainDisplay(); 22 | hasDisplayNameChanged = 0; 23 | } 24 | 25 | if (mainDisplay == NULL) { 26 | /* First try the user set displayName */ 27 | mainDisplay = XOpenDisplay(displayName); 28 | 29 | /* Then try using environment variable DISPLAY */ 30 | if (mainDisplay == NULL && displayName != NULL) { 31 | mainDisplay = XOpenDisplay(NULL); 32 | } 33 | 34 | /* Fall back to the most likely :0.0*/ 35 | if (mainDisplay == NULL) { 36 | mainDisplay = XOpenDisplay(":0.0"); 37 | } 38 | 39 | if (mainDisplay == NULL) { 40 | fputs("Could not open main display\n", stderr); 41 | } else if (!registered) { 42 | atexit(&XCloseMainDisplay); 43 | registered = 1; 44 | } 45 | } 46 | 47 | return mainDisplay; 48 | } 49 | 50 | void setXDisplay(char *name) { 51 | displayName = strdup(name); 52 | hasDisplayNameChanged = 1; 53 | } 54 | 55 | char *getXDisplay(void) { 56 | return displayName; 57 | } 58 | -------------------------------------------------------------------------------- /clipboard/README.md: -------------------------------------------------------------------------------- 1 | This directory based on [clipboard](https://github.com/atotto/clipboard). 2 | 3 | [![Build Status](https://travis-ci.org/atotto/clipboard.svg?branch=master)](https://travis-ci.org/atotto/clipboard) 4 | [![GoDoc](https://godoc.org/github.com/atotto/clipboard?status.svg)](http://godoc.org/github.com/atotto/clipboard) 5 | 6 | 7 | # Clipboard for Go 8 | 9 | Provide copying and pasting to the Clipboard for Go. 10 | 11 | 12 | 13 | Build: 14 | 15 | $ go get github.com/atotto/clipboard 16 | 17 | Platforms: 18 | 19 | * OSX 20 | * Windows 7 (probably work on other Windows) 21 | * Linux, Unix (requires 'xclip' or 'xsel' command to be installed) 22 | 23 | 24 | Document: 25 | 26 | * http://godoc.org/github.com/atotto/clipboard 27 | 28 | Notes: 29 | 30 | * Text string only 31 | * UTF-8 text encoding only (no conversion) 32 | 33 | TODO: 34 | 35 | * Clipboard watcher(?) 36 | 37 | ## Commands: 38 | 39 | paste shell command: 40 | 41 | $ go get github.com/atotto/clipboard/cmd/gopaste 42 | $ # example: 43 | $ gopaste > document.txt 44 | 45 | copy shell command: 46 | 47 | $ go get github.com/atotto/clipboard/cmd/gocopy 48 | $ # example: 49 | $ cat document.txt | gocopy 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /clipboard/clipboard.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 @atotto. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package clipboard read/write on clipboard 7 | */ 8 | package clipboard 9 | 10 | // import () 11 | 12 | // ReadAll read string from clipboard 13 | func ReadAll() (string, error) { 14 | return readAll() 15 | } 16 | 17 | // WriteAll write string to clipboard 18 | func WriteAll(text string) error { 19 | return writeAll(text) 20 | } 21 | 22 | // Unsupported might be set true during clipboard init, 23 | // to help callers decide whether or not to 24 | // offer clipboard options. 25 | var Unsupported bool 26 | -------------------------------------------------------------------------------- /clipboard/clipboard_darwin.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 @atotto. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build darwin 6 | // +build darwin 7 | 8 | package clipboard 9 | 10 | import ( 11 | "os/exec" 12 | ) 13 | 14 | var ( 15 | pasteCmdArgs = "pbpaste" 16 | copyCmdArgs = "pbcopy" 17 | ) 18 | 19 | func getPasteCommand() *exec.Cmd { 20 | return exec.Command(pasteCmdArgs) 21 | } 22 | 23 | func getCopyCommand() *exec.Cmd { 24 | return exec.Command(copyCmdArgs) 25 | } 26 | 27 | func readAll() (string, error) { 28 | pasteCmd := getPasteCommand() 29 | out, err := pasteCmd.Output() 30 | if err != nil { 31 | return "", err 32 | } 33 | return string(out), nil 34 | } 35 | 36 | func writeAll(text string) error { 37 | copyCmd := getCopyCommand() 38 | in, err := copyCmd.StdinPipe() 39 | if err != nil { 40 | return err 41 | } 42 | 43 | if err := copyCmd.Start(); err != nil { 44 | return err 45 | } 46 | if _, err := in.Write([]byte(text)); err != nil { 47 | return err 48 | } 49 | if err := in.Close(); err != nil { 50 | return err 51 | } 52 | 53 | return copyCmd.Wait() 54 | } 55 | -------------------------------------------------------------------------------- /clipboard/clipboard_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 @atotto. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build darwin || windows 6 | // +build darwin windows 7 | 8 | package clipboard_test 9 | 10 | import ( 11 | "testing" 12 | 13 | "github.com/go-vgo/robotgo/clipboard" 14 | ) 15 | 16 | func TestCopyAndPaste(t *testing.T) { 17 | expected := "日本語" 18 | 19 | err := clipboard.WriteAll(expected) 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | 24 | actual, err := clipboard.ReadAll() 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | 29 | if actual != expected { 30 | t.Errorf("want %s, got %s", expected, actual) 31 | } 32 | } 33 | 34 | func TestMultiCopyAndPaste(t *testing.T) { 35 | expected1 := "French: éèêëàùœç" 36 | expected2 := "Weird UTF-8: 💩☃" 37 | 38 | err := clipboard.WriteAll(expected1) 39 | if err != nil { 40 | t.Fatal(err) 41 | } 42 | 43 | actual1, err := clipboard.ReadAll() 44 | if err != nil { 45 | t.Fatal(err) 46 | } 47 | if actual1 != expected1 { 48 | t.Errorf("want %s, got %s", expected1, actual1) 49 | } 50 | 51 | err = clipboard.WriteAll(expected2) 52 | if err != nil { 53 | t.Fatal(err) 54 | } 55 | 56 | actual2, err := clipboard.ReadAll() 57 | if err != nil { 58 | t.Fatal(err) 59 | } 60 | if actual2 != expected2 { 61 | t.Errorf("want %s, got %s", expected2, actual2) 62 | } 63 | } 64 | 65 | func BenchmarkReadAll(b *testing.B) { 66 | for i := 0; i < b.N; i++ { 67 | clipboard.ReadAll() 68 | } 69 | } 70 | 71 | func BenchmarkWriteAll(b *testing.B) { 72 | text := "いろはにほへと" 73 | for i := 0; i < b.N; i++ { 74 | clipboard.WriteAll(text) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /clipboard/clipboard_unix.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 @atotto. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build freebsd || linux || netbsd || openbsd || solaris || dragonfly 6 | // +build freebsd linux netbsd openbsd solaris dragonfly 7 | 8 | package clipboard 9 | 10 | import ( 11 | "errors" 12 | "os/exec" 13 | ) 14 | 15 | const ( 16 | xsel = "xsel" 17 | xclip = "xclip" 18 | ) 19 | 20 | var ( 21 | // Primary choose primary mode on unix 22 | Primary bool 23 | 24 | pasteCmdArgs, copyCmdArgs []string 25 | 26 | xselPasteArgs = []string{xsel, "--output", "--clipboard"} 27 | xselCopyArgs = []string{xsel, "--input", "--clipboard"} 28 | 29 | xclipPasteArgs = []string{xclip, "-out", "-selection", "clipboard"} 30 | xclipCopyArgs = []string{xclip, "-in", "-selection", "clipboard"} 31 | 32 | errMissingCommands = errors.New("No clipboard utilities available. Please install xsel or xclip") 33 | ) 34 | 35 | func init() { 36 | pasteCmdArgs = xclipPasteArgs 37 | copyCmdArgs = xclipCopyArgs 38 | 39 | if _, err := exec.LookPath(xclip); err == nil { 40 | return 41 | } 42 | 43 | pasteCmdArgs = xselPasteArgs 44 | copyCmdArgs = xselCopyArgs 45 | 46 | if _, err := exec.LookPath(xsel); err == nil { 47 | return 48 | } 49 | 50 | Unsupported = true 51 | } 52 | 53 | func getPasteCommand() *exec.Cmd { 54 | if Primary { 55 | pasteCmdArgs = pasteCmdArgs[:1] 56 | } 57 | return exec.Command(pasteCmdArgs[0], pasteCmdArgs[1:]...) 58 | } 59 | 60 | func getCopyCommand() *exec.Cmd { 61 | if Primary { 62 | copyCmdArgs = copyCmdArgs[:1] 63 | } 64 | return exec.Command(copyCmdArgs[0], copyCmdArgs[1:]...) 65 | } 66 | 67 | func readAll() (string, error) { 68 | if Unsupported { 69 | return "", errMissingCommands 70 | } 71 | 72 | pasteCmd := getPasteCommand() 73 | out, err := pasteCmd.Output() 74 | if err != nil { 75 | return "", err 76 | } 77 | 78 | return string(out), nil 79 | } 80 | 81 | func writeAll(text string) error { 82 | if Unsupported { 83 | return errMissingCommands 84 | } 85 | copyCmd := getCopyCommand() 86 | in, err := copyCmd.StdinPipe() 87 | if err != nil { 88 | return err 89 | } 90 | 91 | if err := copyCmd.Start(); err != nil { 92 | return err 93 | } 94 | if _, err := in.Write([]byte(text)); err != nil { 95 | return err 96 | } 97 | if err := in.Close(); err != nil { 98 | return err 99 | } 100 | 101 | return copyCmd.Wait() 102 | } 103 | -------------------------------------------------------------------------------- /clipboard/clipboard_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 @atotto. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build windows 6 | // +build windows 7 | 8 | package clipboard 9 | 10 | import ( 11 | "syscall" 12 | "time" 13 | "unsafe" 14 | ) 15 | 16 | const ( 17 | cfUnicodetext = 13 18 | // gmemFixed = 0x0000 19 | gmemMoveable = 0x0002 20 | ) 21 | 22 | var ( 23 | user32 = syscall.MustLoadDLL("user32") 24 | openClipboard = user32.MustFindProc("OpenClipboard") 25 | closeClipboard = user32.MustFindProc("CloseClipboard") 26 | emptyClipboard = user32.MustFindProc("EmptyClipboard") 27 | getClipboardData = user32.MustFindProc("GetClipboardData") 28 | setClipboardData = user32.MustFindProc("SetClipboardData") 29 | 30 | kernel32 = syscall.NewLazyDLL("kernel32") 31 | globalAlloc = kernel32.NewProc("GlobalAlloc") 32 | globalFree = kernel32.NewProc("GlobalFree") 33 | globalLock = kernel32.NewProc("GlobalLock") 34 | globalUnlock = kernel32.NewProc("GlobalUnlock") 35 | lstrcpy = kernel32.NewProc("lstrcpyW") 36 | ) 37 | 38 | // waitOpenClipboard opens the clipboard, waiting for up to a second to do so. 39 | func waitOpenClipboard() error { 40 | started := time.Now() 41 | limit := started.Add(time.Second) 42 | var ( 43 | r uintptr 44 | err error 45 | ) 46 | for time.Now().Before(limit) { 47 | r, _, err = openClipboard.Call(0) 48 | if r != 0 { 49 | return nil 50 | } 51 | time.Sleep(time.Millisecond) 52 | } 53 | 54 | return err 55 | } 56 | 57 | func readAll() (string, error) { 58 | // r, _, err := openClipboard.Call(0) 59 | err := waitOpenClipboard() 60 | if err != nil { 61 | return "", err 62 | } 63 | defer closeClipboard.Call() 64 | 65 | h, _, err := getClipboardData.Call(cfUnicodetext) 66 | if h == 0 { 67 | return "", err 68 | } 69 | 70 | l, _, err := globalLock.Call(h) 71 | if l == 0 { 72 | return "", err 73 | } 74 | 75 | text := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(l))[:]) 76 | 77 | r, _, err := globalUnlock.Call(h) 78 | if r == 0 { 79 | return "", err 80 | } 81 | 82 | return text, nil 83 | } 84 | 85 | func writeAll(text string) error { 86 | err := waitOpenClipboard() 87 | if err != nil { 88 | return err 89 | } 90 | defer closeClipboard.Call() 91 | 92 | r, _, err := emptyClipboard.Call(0) 93 | if r == 0 { 94 | return err 95 | } 96 | 97 | data := syscall.StringToUTF16(text) 98 | 99 | // "If the hMem parameter identifies a memory object, the object must have 100 | // been allocated using the function with the GMEM_MOVEABLE flag." 101 | h, _, err := globalAlloc.Call(gmemMoveable, 102 | uintptr(len(data)*int(unsafe.Sizeof(data[0])))) 103 | if h == 0 { 104 | return err 105 | } 106 | 107 | defer func() { 108 | if h != 0 { 109 | globalFree.Call(h) 110 | } 111 | }() 112 | 113 | l, _, err := globalLock.Call(h) 114 | if l == 0 { 115 | return err 116 | } 117 | 118 | r, _, err = lstrcpy.Call(l, uintptr(unsafe.Pointer(&data[0]))) 119 | if r == 0 { 120 | return err 121 | } 122 | 123 | r, _, err = globalUnlock.Call(h) 124 | if r == 0 { 125 | if err.(syscall.Errno) != 0 { 126 | return err 127 | } 128 | } 129 | 130 | r, _, err = setClipboardData.Call(cfUnicodetext, h) 131 | if r == 0 { 132 | return err 133 | } 134 | 135 | h = 0 // suppress deferred cleanup 136 | return nil 137 | } 138 | -------------------------------------------------------------------------------- /clipboard/cmd/gocopy/gocopy.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "os" 6 | 7 | "github.com/go-vgo/robotgo/clipboard" 8 | ) 9 | 10 | func main() { 11 | out, err := io.ReadAll(os.Stdin) 12 | if err != nil { 13 | panic(err) 14 | } 15 | 16 | if err := clipboard.WriteAll(string(out)); err != nil { 17 | panic(err) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /clipboard/cmd/gopaste/gopaste.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/go-vgo/robotgo/clipboard" 7 | ) 8 | 9 | func main() { 10 | text, err := clipboard.ReadAll() 11 | if err != nil { 12 | panic(err) 13 | } 14 | 15 | fmt.Print(text) 16 | } 17 | -------------------------------------------------------------------------------- /clipboard/example/example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/go-vgo/robotgo/clipboard" 7 | ) 8 | 9 | func main() { 10 | err := clipboard.WriteAll("日本語") 11 | if err != nil { 12 | log.Println("clipboard write all error: ", err) 13 | } 14 | 15 | text, err := clipboard.ReadAll() 16 | if err != nil { 17 | log.Println("clipboard read all error: ", err) 18 | return 19 | } 20 | 21 | if text != "" { 22 | log.Println("text is: ", text) 23 | // Output: 日本語 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /cv/README.md: -------------------------------------------------------------------------------- 1 | robotgo gocv 2 | 3 | -------------------------------------------------------------------------------- /cv/gocv.go: -------------------------------------------------------------------------------- 1 | package cv 2 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package robotgo 12 | 13 | /* 14 | Keys are supported: 15 | "A-Z a-z 0-9" 16 | 17 | "backspace" 18 | "delete" 19 | "enter" 20 | "tab" 21 | "esc" 22 | "escape" 23 | "up" Up arrow key 24 | "down" Down arrow key 25 | "right" Right arrow key 26 | "left" Left arrow key 27 | "home" 28 | "end" 29 | "pageup" 30 | "pagedown" 31 | 32 | "f1" 33 | "f2" 34 | "f3" 35 | "f4" 36 | "f5" 37 | "f6" 38 | "f7" 39 | "f8" 40 | "f9" 41 | "f10" 42 | "f11" 43 | "f12" 44 | "f13" 45 | "f14" 46 | "f15" 47 | "f16" 48 | "f17" 49 | "f18" 50 | "f19" 51 | "f20" 52 | "f21" 53 | "f22" 54 | "f23" 55 | "f24" 56 | 57 | "cmd" this is the "win" key for windows 58 | "lcmd" left command 59 | "rcmd" right command 60 | // "command" 61 | "alt" 62 | "lalt" left alt 63 | "ralt" right alt 64 | "ctrl" 65 | "lctrl" left ctrl 66 | "rctrl" right ctrl 67 | "control" 68 | "shift" 69 | "lshift" left shift 70 | "rshift" right shift 71 | // "right_shift" 72 | "capslock" 73 | "space" 74 | "print" 75 | "printscreen" // No Mac support 76 | "insert" 77 | "menu" Windows only 78 | 79 | "audio_mute" Mute the volume 80 | "audio_vol_down" Lower the volume 81 | "audio_vol_up" Increase the volume 82 | "audio_play" 83 | "audio_stop" 84 | "audio_pause" 85 | "audio_prev" Previous Track 86 | "audio_next" Next Track 87 | "audio_rewind" Linux only 88 | "audio_forward" Linux only 89 | "audio_repeat" Linux only 90 | "audio_random" Linux only 91 | 92 | 93 | "num0" 94 | "num1" 95 | "num2" 96 | "num3" 97 | "num4" 98 | "num5" 99 | "num6" 100 | "num7" 101 | "num8" 102 | "num9" 103 | "num_lock" 104 | 105 | "num." 106 | "num+" 107 | "num-" 108 | "num*" 109 | "num/" 110 | "num_clear" 111 | "num_enter" 112 | "num_equal" 113 | 114 | "lights_mon_up" Turn up monitor brightness No Windows support 115 | "lights_mon_down" Turn down monitor brightness No Windows support 116 | "lights_kbd_toggle" Toggle keyboard backlight on/off No Windows support 117 | "lights_kbd_up" Turn up keyboard backlight brightness No Windows support 118 | "lights_kbd_down" Turn down keyboard backlight brightness No Windows support 119 | */ 120 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Docs 2 | 3 | Documents are not necessarily updated synchronously, slower than godoc, please see examples and godoc. -------------------------------------------------------------------------------- /docs/install.md: -------------------------------------------------------------------------------- 1 | ## CrossCompiling 2 | 3 | ##### Windows64 to windows32 4 | 5 | ```Go 6 | SET CGO_ENABLED=1 7 | SET GOARCH=386 8 | go build main.go 9 | ``` 10 | 11 | #### Other to windows 12 | 13 | Install Requirements (Ubuntu): 14 | 15 | ```bash 16 | sudo apt install gcc-multilib 17 | sudo apt install gcc-mingw-w64 18 | # fix err: zlib.h: No such file or directory, Just used by bitmap. 19 | sudo apt install libz-mingw-w64-dev 20 | ``` 21 | 22 | Build the binary: 23 | 24 | ```Go 25 | GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ go build -x ./ 26 | ``` 27 | 28 | ``` 29 | // CC=mingw-w64\x86_64-7.2.0-win32-seh-rt_v5-rev1\mingw64\bin\gcc.exe 30 | // CXX=mingw-w64\x86_64-7.2.0-win32-seh-rt_v5-rev1\mingw64\bin\g++.exe 31 | ``` 32 | 33 | Some discussions and questions, please see [issues/228](https://github.com/go-vgo/robotgo/issues/228), [issues/143](https://github.com/go-vgo/robotgo/issues/143). 34 | -------------------------------------------------------------------------------- /docs/keys.md: -------------------------------------------------------------------------------- 1 | ## Type Conversion 2 | 3 | | | type conversion | func | 4 | | --- | --------------------------------- | ----------------------- | 5 | | \* | robotgo.Bitmap -> robotgo.CBitmap | robotgo.ToCBitmap() | 6 | | | robotgo.Bitmap -> \*image.RGBA | robotgo.ToRGBAGo() | 7 | | \* | robotgo.CBitmap -> C.MMBitmapRef | robotgo.ToMMBitmapRef() | 8 | | | robotgo.CBitmap -> robotgo.Bitmap | robotgo.ToBitmap() | 9 | | | robotgo.CBitmap -> image.Image | robotgo.ToImage() | 10 | | | robotgo.CBitmap -> \*image.RGBA | robotgo.ToRGBA() | 11 | | \* | C.MMBitmapRef -> robotgo.CBitmap | robotgo.CBitmap() | 12 | | \* | image.Image -> robotgo.Bitmap | robotgo.ImgToBitmap() | 13 | | | image.Image -> robotgo.CBitmap | robotgo.ImgToCBitmap() | 14 | | | image.Image -> []byte | robotgo.ToByteImg() | 15 | | | image.Image -> string | robotgo.ToStringImg() | 16 | | \* | \*image.RGBA -> robotgo.Bitmap | robotgo.RGBAToBitmap() | 17 | | \* | []byte -> image.Image | robotgo.ByteToImg() | 18 | | | []byte-> robotgo.CBitmap | robotgo.ByteToCBitmap() | 19 | | | []byte -> string | string() | 20 | | \* | string -> image.Image | robotgo.StrToImg() | 21 | | | string -> byte | []byte() | 22 | 23 | # Keys 24 | 25 | ```Go 26 | "A-Z a-z 0-9" 27 | 28 | "backspace" 29 | "delete" 30 | "enter" 31 | "tab" 32 | "esc" 33 | "escape" 34 | "up" Up arrow key 35 | "down" Down arrow key 36 | "right" Right arrow key 37 | "left" Left arrow key 38 | "home" 39 | "end" 40 | "pageup" 41 | "pagedown" 42 | 43 | "f1" 44 | "f2" 45 | "f3" 46 | "f4" 47 | "f5" 48 | "f6" 49 | "f7" 50 | "f8" 51 | "f9" 52 | "f10" 53 | "f11" 54 | "f12" 55 | "f13" 56 | "f14" 57 | "f15" 58 | "f16" 59 | "f17" 60 | "f18" 61 | "f19" 62 | "f20" 63 | "f21" 64 | "f22" 65 | "f23" 66 | "f24" 67 | 68 | "cmd" is the "win" key for windows 69 | "lcmd" left command 70 | "rcmd" right command 71 | // "command" 72 | "alt" 73 | "lalt" left alt 74 | "ralt" right alt 75 | "ctrl" 76 | "lctrl" left ctrl 77 | "rctrl" right ctrl 78 | "control" 79 | "shift" 80 | "lshift" left shift 81 | "rshift" right shift 82 | // "right_shift" 83 | "capslock" 84 | "space" 85 | "print" 86 | "printscreen" // No Mac support 87 | "insert" 88 | "menu" Windows only 89 | 90 | "audio_mute" Mute the volume 91 | "audio_vol_down" Lower the volume 92 | "audio_vol_up" Increase the volume 93 | "audio_play" 94 | "audio_stop" 95 | "audio_pause" 96 | "audio_prev" Previous Track 97 | "audio_next" Next Track 98 | "audio_rewind" Linux only 99 | "audio_forward" Linux only 100 | "audio_repeat" Linux only 101 | "audio_random" Linux only 102 | 103 | 104 | "num0" 105 | "num1" 106 | "num2" 107 | "num3" 108 | "num4" 109 | "num5" 110 | "num6" 111 | "num7" 112 | "num8" 113 | "num9" 114 | "num_lock" 115 | 116 | "num." 117 | "num+" 118 | "num-" 119 | "num*" 120 | "num/" 121 | "num_clear" 122 | "num_enter" 123 | "num_equal" 124 | 125 | // // "numpad_0" No Linux support 126 | // "numpad_0" 127 | // "numpad_1" 128 | // "numpad_2" 129 | // "numpad_3" 130 | // "numpad_4" 131 | // "numpad_5" 132 | // "numpad_6" 133 | // "numpad_7" 134 | // "numpad_8" 135 | // "numpad_9" 136 | // "numpad_lock" 137 | 138 | "lights_mon_up" Turn up monitor brightness No Windows support 139 | "lights_mon_down" Turn down monitor brightness No Windows support 140 | "lights_kbd_toggle" Toggle keyboard backlight on/off No Windows support 141 | "lights_kbd_up" Turn up keyboard backlight brightness No Windows support 142 | "lights_kbd_down" Turn down keyboard backlight brightness No Windows support 143 | ``` 144 | -------------------------------------------------------------------------------- /event/android/event_c.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | -------------------------------------------------------------------------------- /event/ios/event_c.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Robotgo examples 2 | 3 | ## Install: 4 | ``` 5 | go get -u github.com/go-vgo/robotgo 6 | ``` 7 | 8 | ## [Examples:](https://github.com/go-vgo/robotgo/blob/master/examples) 9 | 10 | #### [Mouse](https://github.com/go-vgo/robotgo/blob/master/examples/mouse/main.go) 11 | 12 | ```Go 13 | package main 14 | 15 | import ( 16 | "github.com/go-vgo/robotgo" 17 | ) 18 | 19 | func main() { 20 | // robotgo.ScrollMouse(10, "up") 21 | robotgo.Scroll(0, 10) 22 | robotgo.MouseClick("left", true) 23 | robotgo.MoveSmooth(100, 200, 1.0, 100.0) 24 | } 25 | ``` 26 | 27 | #### [Keyboard](https://github.com/go-vgo/robotgo/blob/master/examples/key/main.go) 28 | 29 | ```Go 30 | package main 31 | 32 | import ( 33 | "fmt" 34 | 35 | "github.com/go-vgo/robotgo" 36 | ) 37 | 38 | func main() { 39 | robotgo.TypeStr("Hello World") 40 | // robotgo.TypeStr("だんしゃり") 41 | robotgo.TypeStr("だんしゃり") 42 | // ustr := uint32(robotgo.CharCodeAt("だんしゃり", 0)) 43 | // robotgo.UnicodeType(ustr) 44 | 45 | robotgo.KeyTap("enter") 46 | robotgo.TypeStr("en") 47 | robotgo.KeyTap("i", "alt", "command") 48 | arr := []string{"alt", "command"} 49 | robotgo.KeyTap("i", arr) 50 | 51 | robotgo.WriteAll("Test") 52 | text, err := robotgo.ReadAll() 53 | if err == nil { 54 | fmt.Println(text) 55 | } 56 | } 57 | ``` 58 | 59 | #### [Screen](https://github.com/go-vgo/robotgo/blob/master/examples/screen/main.go) 60 | 61 | ```Go 62 | package main 63 | 64 | import ( 65 | "fmt" 66 | 67 | "github.com/go-vgo/robotgo" 68 | ) 69 | 70 | func main() { 71 | x, y := robotgo.Location() 72 | fmt.Println("pos:", x, y) 73 | color := robotgo.GetPixelColor(100, 200) 74 | fmt.Println("color----", color) 75 | } 76 | ``` 77 | 78 | #### [Bitmap](https://github.com/go-vgo/robotgo/blob/master/examples/bitmap/main.go) 79 | 80 | ```Go 81 | package main 82 | 83 | import ( 84 | "fmt" 85 | 86 | "github.com/go-vgo/robotgo" 87 | ) 88 | 89 | func main() { 90 | bitmap := robotgo.CaptureScreen(10, 20, 30, 40) 91 | // use `defer robotgo.FreeBitmap(bit)` to free the bitmap 92 | defer robotgo.FreeBitmap(bitmap) 93 | fmt.Println("...", bitmap) 94 | 95 | fx, fy := robotgo.FindBitmap(bitmap) 96 | fmt.Println("FindBitmap------", fx, fy) 97 | 98 | robotgo.SaveBitmap(bitmap, "test.png") 99 | } 100 | ``` 101 | 102 | #### [Event](https://github.com/go-vgo/robotgo/blob/master/examples/event/main.go) 103 | 104 | ```Go 105 | package main 106 | 107 | import ( 108 | "fmt" 109 | 110 | "github.com/go-vgo/robotgo" 111 | ) 112 | 113 | func main() { 114 | keve := robotgo.AddEvent("k") 115 | if keve { 116 | fmt.Println("you press...", "k") 117 | } 118 | 119 | mleft := robotgo.AddEvent("mleft") 120 | if mleft { 121 | fmt.Println("you press...", "mouse left button") 122 | } 123 | } 124 | ``` 125 | 126 | #### [Window](https://github.com/go-vgo/robotgo/blob/master/examples/window/main.go) 127 | 128 | ```Go 129 | package main 130 | 131 | import ( 132 | "fmt" 133 | 134 | "github.com/go-vgo/robotgo" 135 | ) 136 | 137 | func main() { 138 | fpid, err := robotgo.FindIds("Google") 139 | if err == nil { 140 | fmt.Println("pids...", fpid) 141 | 142 | if len(fpid) > 0 { 143 | robotgo.ActivePID(fpid[0]) 144 | 145 | robotgo.Kill(fpid[0]) 146 | } 147 | } 148 | 149 | robotgo.ActiveName("chrome") 150 | 151 | isExist, err := robotgo.PidExists(100) 152 | if err == nil && isExist { 153 | fmt.Println("pid exists is", isExist) 154 | 155 | robotgo.Kill(100) 156 | } 157 | 158 | abool := robotgo.ShowAlert("test", "robotgo") 159 | if abool == 0 { 160 | fmt.Println("ok@@@", "ok") 161 | } 162 | 163 | title := robotgo.GetTitle() 164 | fmt.Println("title@@@", title) 165 | } 166 | ``` -------------------------------------------------------------------------------- /examples/key/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package main 12 | 13 | import ( 14 | "fmt" 15 | 16 | "github.com/go-vgo/robotgo" 17 | // "go-vgo/robotgo" 18 | ) 19 | 20 | func typeStr() { 21 | // typing "Hello World" 22 | robotgo.TypeStr("Hello World!", 0, 1) 23 | robotgo.KeySleep = 100 24 | robotgo.TypeStr("だんしゃり") 25 | 26 | robotgo.TypeStr("Hi galaxy, hi stars, hi MT.Rainier, hi sea. こんにちは世界.") 27 | robotgo.TypeStr("So, hi, bye! 你好, 再见!") 28 | robotgo.Sleep(1) 29 | 30 | robotgo.TypeStr("Hi, Seattle space needle, Golden gate bridge, One world trade center.") 31 | robotgo.MilliSleep(100) 32 | 33 | ustr := uint32(robotgo.CharCodeAt("So, hi, bye!", 0)) 34 | robotgo.UnicodeType(ustr) 35 | 36 | err := robotgo.PasteStr("paste string") 37 | fmt.Println("PasteStr: ", err) 38 | } 39 | 40 | func keyTap() { 41 | // press "enter" 42 | robotgo.KeyTap("enter") 43 | robotgo.KeyTap(robotgo.Enter) 44 | robotgo.KeySleep = 200 45 | robotgo.KeyTap("a") 46 | robotgo.MilliSleep(100) 47 | robotgo.KeyTap("a", "ctrl") 48 | 49 | // hide window 50 | err := robotgo.KeyTap("h", "cmd") 51 | if err != nil { 52 | fmt.Println("robotgo.KeyTap run error is: ", err) 53 | } 54 | 55 | robotgo.KeyTap("h", "cmd") 56 | 57 | // press "i", "alt", "command" Key combination 58 | robotgo.KeyTap(robotgo.KeyI, robotgo.Alt, robotgo.Cmd) 59 | robotgo.KeyTap("i", "alt", "cmd") 60 | 61 | arr := []string{"alt", "cmd"} 62 | robotgo.KeyTap("i", arr) 63 | robotgo.KeyTap("i", arr) 64 | 65 | robotgo.KeyTap("i", "cmd", " alt", "shift") 66 | 67 | // close window 68 | robotgo.KeyTap("w", "cmd") 69 | 70 | // minimize window 71 | robotgo.KeyTap("m", "cmd") 72 | 73 | robotgo.KeyTap("f1", "ctrl") 74 | robotgo.KeyTap("a", "control") 75 | } 76 | 77 | func special() { 78 | robotgo.TypeStr("{}") 79 | robotgo.KeyTap("[", "]") 80 | 81 | robotgo.KeyToggle("(") 82 | robotgo.KeyToggle("(", "up") 83 | } 84 | 85 | func keyToggle() { 86 | // robotgo.KeySleep = 150 87 | robotgo.KeyToggle(robotgo.KeyA) 88 | robotgo.KeyToggle("a", "down", "alt") 89 | robotgo.Sleep(1) 90 | 91 | robotgo.KeyToggle("a", "up", "alt", "cmd") 92 | robotgo.MilliSleep(100) 93 | robotgo.KeyToggle("q", "up", "alt", "cmd", "shift") 94 | 95 | err := robotgo.KeyToggle(robotgo.Enter) 96 | if err != nil { 97 | fmt.Println("robotgo.KeyToggle run error is: ", err) 98 | } 99 | } 100 | 101 | func cilp() { 102 | // robotgo.TypeStr("en") 103 | 104 | // write string to clipboard 105 | e := robotgo.WriteAll("テストする") 106 | if e != nil { 107 | fmt.Println("robotgo.WriteAll err is: ", e) 108 | } 109 | 110 | // read string from clipboard 111 | text, err := robotgo.ReadAll() 112 | if err != nil { 113 | fmt.Println("robotgo.ReadAll err is: ", err) 114 | } 115 | fmt.Println("text: ", text) 116 | } 117 | 118 | func key() { 119 | //////////////////////////////////////////////////////////////////////////////// 120 | // Control the keyboard 121 | //////////////////////////////////////////////////////////////////////////////// 122 | 123 | typeStr() 124 | special() 125 | 126 | keyTap() 127 | keyToggle() 128 | 129 | cilp() 130 | } 131 | 132 | func main() { 133 | key() 134 | } 135 | -------------------------------------------------------------------------------- /examples/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package main 12 | 13 | import ( 14 | "fmt" 15 | 16 | "github.com/go-vgo/robotgo" 17 | // "go-vgo/robotgo" 18 | ) 19 | 20 | func main() { 21 | ver := robotgo.GetVersion() 22 | fmt.Println("robotgo version is: ", ver) 23 | 24 | // Control the keyboard 25 | // key() 26 | 27 | // Control the mouse 28 | // mouse() 29 | 30 | // Read the screen 31 | // screen() 32 | 33 | // Bitmap and image processing 34 | // bitmap() 35 | 36 | // Global event listener 37 | // event() 38 | 39 | // Window Handle and progress 40 | // window() 41 | } 42 | -------------------------------------------------------------------------------- /examples/mouse/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package main 12 | 13 | import ( 14 | "fmt" 15 | 16 | "github.com/go-vgo/robotgo" 17 | // "go-vgo/robotgo" 18 | ) 19 | 20 | func move() { 21 | robotgo.MouseSleep = 100 22 | robotgo.Move(100, 200) 23 | robotgo.MoveRelative(10, -200) 24 | 25 | // move the mouse to 100, 200 26 | robotgo.Move(100, 200) 27 | 28 | // drag mouse with smooth 29 | robotgo.DragSmooth(10, 10) 30 | robotgo.DragSmooth(100, 200, 1.0, 100.0) 31 | 32 | // smooth move the mouse to 100, 200 33 | robotgo.MoveSmooth(100, 200) 34 | robotgo.MoveSmooth(100, 200, 1.0, 100.0) 35 | robotgo.MoveSmoothRelative(10, -100, 1.0, 30.0) 36 | 37 | for i := 0; i < 1080; i += 1000 { 38 | fmt.Println("i: ", i) 39 | // MoveMouse(800, i) 40 | robotgo.Move(800, i) 41 | } 42 | } 43 | 44 | func click() { 45 | 46 | // click the left mouse button 47 | robotgo.Click() 48 | 49 | // click the right mouse button 50 | robotgo.Click("right", false) 51 | 52 | // double click the left mouse button 53 | robotgo.Click("left", true) 54 | } 55 | 56 | func get() { 57 | // gets the mouse coordinates 58 | x, y := robotgo.Location() 59 | fmt.Println("pos:", x, y) 60 | if x == 456 && y == 586 { 61 | fmt.Println("mouse...", "586") 62 | } 63 | 64 | robotgo.Move(x, y) 65 | } 66 | 67 | func toggleAndScroll() { 68 | // scrolls the mouse either up 69 | robotgo.ScrollDir(10, "up") 70 | robotgo.ScrollDir(10, "right") 71 | 72 | robotgo.Scroll(100, 10) 73 | robotgo.Scroll(0, -10) 74 | 75 | robotgo.Toggle("left") 76 | robotgo.Toggle("left", "up") 77 | 78 | // toggles the right mouse button 79 | robotgo.Toggle("right") 80 | robotgo.Toggle("right", "up") 81 | } 82 | 83 | func mouse() { 84 | //////////////////////////////////////////////////////////////////////////////// 85 | // Control the mouse 86 | //////////////////////////////////////////////////////////////////////////////// 87 | 88 | move() 89 | 90 | click() 91 | 92 | get() 93 | 94 | toggleAndScroll() 95 | } 96 | 97 | func main() { 98 | mouse() 99 | } 100 | -------------------------------------------------------------------------------- /examples/scale/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/go-vgo/robotgo" 7 | ) 8 | 9 | func main() { 10 | // syscall.NewLazyDLL("user32.dll").NewProc("SetProcessDPIAware").Call() 11 | 12 | width, height := robotgo.GetScaleSize() 13 | fmt.Println("get scale screen size: ", width, height) 14 | 15 | bitmap := robotgo.CaptureScreen(0, 0, width, height) 16 | defer robotgo.FreeBitmap(bitmap) 17 | // bitmap.Save(bitmap, "test.png") 18 | robotgo.Save(robotgo.ToImage(bitmap), "test.png") 19 | 20 | robotgo.Scale = true 21 | robotgo.Move(10, 10) 22 | robotgo.MoveSmooth(100, 100) 23 | 24 | fmt.Println(robotgo.Location()) 25 | 26 | num := robotgo.DisplaysNum() 27 | for i := 0; i < num; i++ { 28 | rect := robotgo.GetScreenRect(i) 29 | fmt.Println("rect: ", rect) 30 | } 31 | } 32 | 33 | func old() { 34 | sx := robotgo.ScaleX() // Deprecated 35 | s := robotgo.Scale1() // Deprecated, use the ScaleF() function 36 | robotx, roboty := 35*s/100, 25*s/100 37 | fmt.Println("scale: ", sx, s, " pos: ", robotx, roboty) 38 | 39 | mx, my := robotgo.Location() 40 | sx, sy := mx*s/100, my*s/100 41 | 42 | rx, ry, rw, rh := sx, sy, robotx, roboty 43 | // bit1 := robotgo.CaptureScreen(10, 20, robotw, roboth) 44 | bit1 := robotgo.CaptureScreen(rx, ry, rw, rh) 45 | defer robotgo.FreeBitmap(bit1) 46 | // bitmap.Save(bit1, "test2.png") 47 | robotgo.Save(robotgo.ToImage(bit1), "test2.png") 48 | 49 | clo := robotgo.GetPixelColor(robotx, roboty) 50 | fmt.Println("GetPixelColor...", clo) 51 | } 52 | -------------------------------------------------------------------------------- /examples/screen/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package main 12 | 13 | import ( 14 | "fmt" 15 | "strconv" 16 | 17 | "github.com/go-vgo/robotgo" 18 | // "go-vgo/robotgo" 19 | ) 20 | 21 | func bitmap() { 22 | bit := robotgo.CaptureScreen() 23 | defer robotgo.FreeBitmap(bit) 24 | fmt.Println("abitMap...", bit) 25 | 26 | gbit := robotgo.ToBitmap(bit) 27 | fmt.Println("bitmap...", gbit.Width) 28 | 29 | gbitMap := robotgo.CaptureGo() 30 | fmt.Println("Go CaptureScreen...", gbitMap.Width) 31 | // fmt.Println("...", gbitmap.Width, gbitmap.BytesPerPixel) 32 | robotgo.SaveCapture("saveCapture.png", 10, 20, 100, 100) 33 | 34 | img, err := robotgo.CaptureImg() 35 | fmt.Println("error: ", err) 36 | robotgo.Save(img, "save.png") 37 | 38 | num := robotgo.DisplaysNum() 39 | for i := 0; i < num; i++ { 40 | robotgo.DisplayID = i 41 | img1, _ := robotgo.CaptureImg() 42 | path1 := "save_" + strconv.Itoa(i) 43 | robotgo.Save(img1, path1+".png") 44 | robotgo.SaveJpeg(img1, path1+".jpeg", 50) 45 | 46 | img2, _ := robotgo.CaptureImg(10, 10, 20, 20) 47 | path2 := "test_" + strconv.Itoa(i) 48 | robotgo.Save(img2, path2+".png") 49 | robotgo.SaveJpeg(img2, path2+".jpeg", 50) 50 | 51 | x, y, w, h := robotgo.GetDisplayBounds(i) 52 | img3, err := robotgo.CaptureImg(x, y, w, h) 53 | fmt.Println("Capture error: ", err) 54 | robotgo.Save(img3, path2+"_1.png") 55 | } 56 | } 57 | 58 | func color() { 59 | // gets the pixel color at 100, 200. 60 | color := robotgo.GetPixelColor(100, 200) 61 | fmt.Println("color----", color, "-----------------") 62 | 63 | clo := robotgo.GetPxColor(100, 200) 64 | fmt.Println("color...", clo) 65 | clostr := robotgo.PadHex(clo) 66 | fmt.Println("color...", clostr) 67 | 68 | rgb := robotgo.RgbToHex(255, 100, 200) 69 | rgbstr := robotgo.PadHex(robotgo.U32ToHex(rgb)) 70 | fmt.Println("rgb...", rgbstr) 71 | 72 | hex := robotgo.HexToRgb(uint32(rgb)) 73 | fmt.Println("hex...", hex) 74 | hexh := robotgo.PadHex(robotgo.U8ToHex(hex)) 75 | fmt.Println("HexToRgb...", hexh) 76 | 77 | // gets the pixel color at 10, 20. 78 | color2 := robotgo.GetPixelColor(10, 20) 79 | fmt.Println("color---", color2) 80 | } 81 | 82 | func screen() { 83 | //////////////////////////////////////////////////////////////////////////////// 84 | // Read the screen 85 | //////////////////////////////////////////////////////////////////////////////// 86 | 87 | bitmap() 88 | 89 | // gets the screen width and height 90 | sx, sy := robotgo.GetScreenSize() 91 | fmt.Println("get screen size: ", sx, sy) 92 | for i := 0; i < robotgo.DisplaysNum(); i++ { 93 | s1 := robotgo.ScaleF(i) 94 | fmt.Println("ScaleF: ", s1) 95 | } 96 | sx, sy = robotgo.GetScaleSize() 97 | fmt.Println("get screen scale size: ", sx, sy) 98 | 99 | color() 100 | } 101 | 102 | func main() { 103 | screen() 104 | } 105 | -------------------------------------------------------------------------------- /examples/window/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package main 12 | 13 | import ( 14 | "fmt" 15 | 16 | "github.com/go-vgo/robotgo" 17 | // "go-vgo/robotgo" 18 | ) 19 | 20 | func alert() { 21 | // show Alert Window 22 | abool := robotgo.Alert("hello", "robotgo") 23 | if abool { 24 | fmt.Println("ok@@@", "ok") 25 | } 26 | robotgo.Alert("hello", "robotgo", "Ok", "Cancel") 27 | } 28 | 29 | func get() { 30 | // get the current process id 31 | pid := robotgo.GetPid() 32 | fmt.Println("pid----", pid) 33 | 34 | // get current Window Active 35 | mdata := robotgo.GetActive() 36 | 37 | // get current Window Handle 38 | hwnd := robotgo.GetHandle() 39 | fmt.Println("hwnd---", hwnd) 40 | 41 | // get current Window title 42 | title := robotgo.GetTitle() 43 | fmt.Println("title-----", title) 44 | 45 | // set Window Active 46 | robotgo.SetActive(mdata) 47 | } 48 | 49 | func findIds() { 50 | // find the process id by the process name 51 | fpid, err := robotgo.FindIds("Google") 52 | if err != nil { 53 | fmt.Println(err) 54 | return 55 | } 56 | 57 | if len(fpid) > 0 { 58 | robotgo.KeyTap("a", fpid[0]) 59 | robotgo.TypeStr("Hi galaxy!", fpid[0]) 60 | 61 | robotgo.KeyToggle("a", fpid[0], "cmd") 62 | robotgo.KeyToggle("a", fpid[0], "cmd", "up") 63 | } 64 | 65 | fmt.Println("pids...", fpid) 66 | if len(fpid) > 0 { 67 | err = robotgo.ActivePid(fpid[0]) 68 | if err != nil { 69 | fmt.Println(err) 70 | } 71 | 72 | tl := robotgo.GetTitle(fpid[0]) 73 | fmt.Println("pid[0] title is: ", tl) 74 | 75 | x, y, w, h := robotgo.GetBounds(fpid[0]) 76 | fmt.Println("GetBounds is: ", x, y, w, h) 77 | 78 | // Windows 79 | // hwnd := robotgo.FindWindow("google") 80 | // hwnd := robotgo.GetHWND() 81 | robotgo.MinWindow(fpid[0]) 82 | robotgo.MaxWindow(fpid[0]) 83 | robotgo.CloseWindow(fpid[0]) 84 | 85 | robotgo.Kill(fpid[0]) 86 | } 87 | } 88 | 89 | func active() { 90 | robotgo.ActivePid(100) 91 | // robotgo.Sleep(2) 92 | robotgo.ActiveName("code") 93 | robotgo.Sleep(1) 94 | robotgo.ActiveName("chrome") 95 | } 96 | 97 | func findName() { 98 | // find the process name by the process id 99 | name, err := robotgo.FindName(100) 100 | if err == nil { 101 | fmt.Println("name: ", name) 102 | } 103 | 104 | // find the all process name 105 | names, err := robotgo.FindNames() 106 | if err == nil { 107 | fmt.Println("name: ", names) 108 | } 109 | 110 | p, err := robotgo.FindPath(100) 111 | if err == nil { 112 | fmt.Println("path: ", p) 113 | } 114 | } 115 | 116 | func ps() { 117 | // determine whether the process exists 118 | isExist, err := robotgo.PidExists(100) 119 | if err == nil && isExist { 120 | fmt.Println("pid exists is", isExist) 121 | 122 | robotgo.Kill(100) 123 | } 124 | 125 | // get the all process id 126 | pids, err := robotgo.Pids() 127 | if err == nil { 128 | fmt.Println("pids: ", pids) 129 | } 130 | 131 | // get the all process struct 132 | ps, err := robotgo.Process() 133 | if err == nil { 134 | fmt.Println("process: ", ps) 135 | } 136 | } 137 | 138 | func window() { 139 | //////////////////////////////////////////////////////////////////////////////// 140 | // Window Handle 141 | //////////////////////////////////////////////////////////////////////////////// 142 | 143 | alert() 144 | // 145 | get() 146 | 147 | findIds() 148 | active() 149 | 150 | findName() 151 | // 152 | ps() 153 | 154 | // close current Window 155 | robotgo.CloseWindow() 156 | } 157 | 158 | func main() { 159 | window() 160 | } 161 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/go-vgo/robotgo 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.6 6 | 7 | require ( 8 | github.com/otiai10/gosseract/v2 v2.4.1 9 | // github.com/robotn/gohook v0.31.3 10 | github.com/robotn/xgb v0.10.0 11 | github.com/robotn/xgbutil v0.10.0 12 | github.com/tailscale/win v0.0.0-20250213223159-5992cb43ca35 13 | github.com/vcaesar/gops v0.41.0 14 | github.com/vcaesar/imgo v0.41.0 15 | github.com/vcaesar/keycode v0.10.1 16 | github.com/vcaesar/tt v0.20.1 17 | ) 18 | 19 | require ( 20 | github.com/dblohm7/wingoes v0.0.0-20240820181039-f2b84150679e // indirect 21 | github.com/ebitengine/purego v0.8.3 // indirect 22 | github.com/gen2brain/shm v0.1.1 // indirect 23 | github.com/go-ole/go-ole v1.3.0 // indirect 24 | github.com/godbus/dbus/v5 v5.1.0 // indirect 25 | github.com/jezek/xgb v1.1.1 // indirect 26 | github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect 27 | github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect 28 | github.com/shirou/gopsutil/v4 v4.25.4 // indirect 29 | github.com/tklauser/go-sysconf v0.3.15 // indirect 30 | github.com/tklauser/numcpus v0.10.0 // indirect 31 | github.com/vcaesar/screenshot v0.11.1 32 | github.com/yusufpapurcu/wmi v1.2.4 // indirect 33 | golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect 34 | golang.org/x/image v0.27.0 // indirect 35 | golang.org/x/sys v0.33.0 // indirect 36 | ) 37 | 38 | // replace golang.org/x/sys => github.com/golang/sys v0.0.0-20190109145017-48ac38b7c8cb 39 | 40 | // go 1.13 41 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298/go.mod h1:D+QujdIlUNfa0igpNMk6UIvlb6C252URs4yupRUV4lQ= 2 | github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966/go.mod h1:Mid70uvE93zn9wgF92A/r5ixgnvX8Lh68fxp9KQBaI0= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/dblohm7/wingoes v0.0.0-20240820181039-f2b84150679e h1:L+XrFvD0vBIBm+Wf9sFN6aU395t7JROoai0qXZraA4U= 6 | github.com/dblohm7/wingoes v0.0.0-20240820181039-f2b84150679e/go.mod h1:SUxUaAK/0UG5lYyZR1L1nC4AaYYvSSYTWQSH3FPcxKU= 7 | github.com/ebitengine/purego v0.8.3 h1:K+0AjQp63JEZTEMZiwsI9g0+hAMNohwUOtY0RPGexmc= 8 | github.com/ebitengine/purego v0.8.3/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= 9 | github.com/gen2brain/shm v0.1.1 h1:1cTVA5qcsUFixnDHl14TmRoxgfWEEZlTezpUj1vm5uQ= 10 | github.com/gen2brain/shm v0.1.1/go.mod h1:UgIcVtvmOu+aCJpqJX7GOtiN7X2ct+TKLg4RTxwPIUA= 11 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 12 | github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= 13 | github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= 14 | github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= 15 | github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 16 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 17 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 18 | github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4= 19 | github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= 20 | github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc= 21 | github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= 22 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= 23 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= 24 | github.com/otiai10/gosseract/v2 v2.4.1 h1:G8AyBpXEeSlcq8TI85LH/pM5SXk8Djy2GEXisgyblRw= 25 | github.com/otiai10/gosseract/v2 v2.4.1/go.mod h1:1gNWP4Hgr2o7yqWfs6r5bZxAatjOIdqWxJLWsTsembk= 26 | github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs= 27 | github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= 28 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 29 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 30 | github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= 31 | github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 32 | github.com/robotn/xgb v0.0.0-20190912153532-2cb92d044934/go.mod h1:SxQhJskUJ4rleVU44YvnrdvxQr0tKy5SRSigBrCgyyQ= 33 | github.com/robotn/xgb v0.10.0 h1:O3kFbIwtwZ3pgLbp1h5slCQ4OpY8BdwugJLrUe6GPIM= 34 | github.com/robotn/xgb v0.10.0/go.mod h1:SxQhJskUJ4rleVU44YvnrdvxQr0tKy5SRSigBrCgyyQ= 35 | github.com/robotn/xgbutil v0.10.0 h1:gvf7mGQqCWQ68aHRtCxgdewRk+/KAJui6l3MJQQRCKw= 36 | github.com/robotn/xgbutil v0.10.0/go.mod h1:svkDXUDQjUiWzLrA0OZgHc4lbOts3C+uRfP6/yjwYnU= 37 | github.com/shirou/gopsutil/v4 v4.25.4 h1:cdtFO363VEOOFrUCjZRh4XVJkb548lyF0q0uTeMqYPw= 38 | github.com/shirou/gopsutil/v4 v4.25.4/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA= 39 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 40 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 41 | github.com/tailscale/win v0.0.0-20250213223159-5992cb43ca35 h1:wAZbkTZkqDzWsqxPh2qkBd3KvFU7tcxV0BP0Rnhkxog= 42 | github.com/tailscale/win v0.0.0-20250213223159-5992cb43ca35/go.mod h1:aMd4yDHLjbOuYP6fMxj1d9ACDQlSWwYztcpybGHCQc8= 43 | github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA= 44 | github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk= 45 | github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= 46 | github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= 47 | github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= 48 | github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= 49 | github.com/vcaesar/gops v0.41.0 h1:FG748Jyw3FOuZnbzSgB+CQSx2e5LbLCPWV2JU1brFdc= 50 | github.com/vcaesar/gops v0.41.0/go.mod h1:/3048L7Rj7QjQKTSB+kKc7hDm63YhTWy5QJ10TCP37A= 51 | github.com/vcaesar/imgo v0.41.0 h1:kNLYGrThXhB9Dd6IwFmfPnxq9P6yat2g7dpPjr7OWO8= 52 | github.com/vcaesar/imgo v0.41.0/go.mod h1:/LGOge8etlzaVu/7l+UfhJxR6QqaoX5yeuzGIMfWb4I= 53 | github.com/vcaesar/keycode v0.10.1 h1:0DesGmMAPWpYTCYddOFiCMKCDKgNnwiQa2QXindVUHw= 54 | github.com/vcaesar/keycode v0.10.1/go.mod h1:JNlY7xbKsh+LAGfY2j4M3znVrGEm5W1R8s/Uv6BJcfQ= 55 | github.com/vcaesar/screenshot v0.11.1 h1:GgPuN89XC4Yh38dLx4quPlSo3YiWWhwIria/j3LtrqU= 56 | github.com/vcaesar/screenshot v0.11.1/go.mod h1:gJNwHBiP1v1v7i8TQ4yV1XJtcyn2I/OJL7OziVQkwjs= 57 | github.com/vcaesar/tt v0.20.1 h1:D/jUeeVCNbq3ad8M7hhtB3J9x5RZ6I1n1eZ0BJp7M+4= 58 | github.com/vcaesar/tt v0.20.1/go.mod h1:cH2+AwGAJm19Wa6xvEa+0r+sXDJBT0QgNQey6mwqLeU= 59 | github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= 60 | github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 61 | golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= 62 | golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= 63 | golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w= 64 | golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g= 65 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 66 | golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 67 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 68 | golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= 69 | golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 70 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 71 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 72 | -------------------------------------------------------------------------------- /img.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package robotgo 12 | 13 | import ( 14 | "image" 15 | "os/exec" 16 | "unsafe" 17 | 18 | "github.com/vcaesar/imgo" 19 | ) 20 | 21 | // DecodeImg decode the image to image.Image and return 22 | func DecodeImg(path string) (image.Image, string, error) { 23 | return imgo.DecodeFile(path) 24 | } 25 | 26 | // OpenImg open the image return []byte 27 | func OpenImg(path string) ([]byte, error) { 28 | return imgo.ImgToBytes(path) 29 | } 30 | 31 | // Read read the file return image.Image 32 | func Read(path string) (image.Image, error) { 33 | return imgo.Read(path) 34 | } 35 | 36 | // Save create a image file with the image.Image 37 | func Save(img image.Image, path string, quality ...int) error { 38 | return imgo.Save(path, img, quality...) 39 | } 40 | 41 | // SaveImg save the image by []byte 42 | func SaveImg(b []byte, path string) error { 43 | return imgo.SaveByte(path, b) 44 | } 45 | 46 | // SavePng save the image by image.Image 47 | func SavePng(img image.Image, path string) error { 48 | return imgo.SaveToPNG(path, img) 49 | } 50 | 51 | // SaveJpeg save the image by image.Image 52 | func SaveJpeg(img image.Image, path string, quality ...int) error { 53 | return imgo.SaveToJpeg(path, img, quality...) 54 | } 55 | 56 | // ToByteImg convert image.Image to []byte 57 | func ToByteImg(img image.Image, fm ...string) []byte { 58 | return imgo.ToByte(img, fm...) 59 | } 60 | 61 | // ToStringImg convert image.Image to string 62 | func ToStringImg(img image.Image, fm ...string) string { 63 | return string(ToByteImg(img, fm...)) 64 | } 65 | 66 | // StrToImg convert base64 string to image.Image 67 | func StrToImg(data string) (image.Image, error) { 68 | return imgo.StrToImg(data) 69 | } 70 | 71 | // ByteToImg convert []byte to image.Image 72 | func ByteToImg(b []byte) (image.Image, error) { 73 | return imgo.ByteToImg(b) 74 | } 75 | 76 | // ImgSize get the file image size 77 | func ImgSize(path string) (int, int, error) { 78 | return imgo.GetSize(path) 79 | } 80 | 81 | // Width return the image.Image width 82 | func Width(img image.Image) int { 83 | return img.Bounds().Max.X 84 | } 85 | 86 | // Height return the image.Image height 87 | func Height(img image.Image) int { 88 | return img.Bounds().Max.Y 89 | } 90 | 91 | // RGBAToBitmap convert the standard image.RGBA to Bitmap 92 | func RGBAToBitmap(r1 *image.RGBA) (bit Bitmap) { 93 | bit.Width = r1.Bounds().Size().X 94 | bit.Height = r1.Bounds().Size().Y 95 | bit.Bytewidth = r1.Stride 96 | 97 | src := ToUint8p(r1.Pix) 98 | bit.ImgBuf = src 99 | 100 | bit.BitsPixel = 32 101 | bit.BytesPerPixel = 32 / 8 102 | 103 | return 104 | } 105 | 106 | // ImgToBitmap convert the standard image.Image to Bitmap 107 | func ImgToBitmap(m image.Image) (bit Bitmap) { 108 | bit.Width = m.Bounds().Size().X 109 | bit.Height = m.Bounds().Size().Y 110 | 111 | pix, stride, _ := imgo.EncodeImg(m) 112 | bit.Bytewidth = stride 113 | 114 | src := ToUint8p(pix) 115 | bit.ImgBuf = src 116 | // 117 | bit.BitsPixel = 32 118 | bit.BytesPerPixel = 32 / 8 119 | return 120 | } 121 | 122 | // ToUint8p convert the []uint8 to uint8 pointer 123 | func ToUint8p(dst []uint8) *uint8 { 124 | src := make([]uint8, len(dst)+10) 125 | for i := 0; i <= len(dst)-4; i += 4 { 126 | src[i+3] = dst[i+3] 127 | src[i] = dst[i+2] 128 | src[i+1] = dst[i+1] 129 | src[i+2] = dst[i] 130 | } 131 | 132 | ptr := unsafe.Pointer(&src[0]) 133 | return (*uint8)(ptr) 134 | } 135 | 136 | // ToRGBAGo convert Bitmap to standard image.RGBA 137 | func ToRGBAGo(bmp1 Bitmap) *image.RGBA { 138 | img1 := image.NewRGBA(image.Rect(0, 0, bmp1.Width, bmp1.Height)) 139 | img1.Pix = make([]uint8, bmp1.Bytewidth*bmp1.Height) 140 | 141 | copyToVUint8A(img1.Pix, bmp1.ImgBuf) 142 | img1.Stride = bmp1.Bytewidth 143 | return img1 144 | } 145 | 146 | func val(p *uint8, n int) uint8 { 147 | addr := uintptr(unsafe.Pointer(p)) 148 | addr += uintptr(n) 149 | p1 := (*uint8)(unsafe.Pointer(addr)) 150 | return *p1 151 | } 152 | 153 | func copyToVUint8A(dst []uint8, src *uint8) { 154 | for i := 0; i <= len(dst)-4; i += 4 { 155 | dst[i] = val(src, i+2) 156 | dst[i+1] = val(src, i+1) 157 | dst[i+2] = val(src, i) 158 | dst[i+3] = val(src, i+3) 159 | } 160 | } 161 | 162 | // GetText get the image text by tesseract ocr 163 | // 164 | // robotgo.GetText(imgPath, lang string) 165 | func GetText(imgPath string, args ...string) (string, error) { 166 | var lang = "eng" 167 | 168 | if len(args) > 0 { 169 | lang = args[0] 170 | if lang == "zh" { 171 | lang = "chi_sim" 172 | } 173 | } 174 | 175 | body, err := exec.Command("tesseract", imgPath, 176 | "stdout", "-l", lang).Output() 177 | if err != nil { 178 | return "", err 179 | } 180 | return string(body), nil 181 | } 182 | -------------------------------------------------------------------------------- /key/key.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package key 12 | -------------------------------------------------------------------------------- /key/key_windows.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | // +build windows 3 | 4 | package key 5 | -------------------------------------------------------------------------------- /key/keycode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef KEYCODE_H 3 | #define KEYCODE_H 4 | 5 | #include "../base/os.h" 6 | 7 | #if defined(IS_MACOSX) 8 | 9 | #include /* Really only need */ 10 | #include 11 | #import 12 | 13 | enum _MMKeyCode { 14 | // a-z, 0-9 15 | K_NOT_A_KEY = 9999, 16 | K_BACKSPACE = kVK_Delete, 17 | K_DELETE = kVK_ForwardDelete, 18 | K_RETURN = kVK_Return, 19 | K_TAB = kVK_Tab, 20 | K_ESCAPE = kVK_Escape, 21 | K_UP = kVK_UpArrow, 22 | K_DOWN = kVK_DownArrow, 23 | K_RIGHT = kVK_RightArrow, 24 | K_LEFT = kVK_LeftArrow, 25 | K_HOME = kVK_Home, 26 | K_END = kVK_End, 27 | K_PAGEUP = kVK_PageUp, 28 | K_PAGEDOWN = kVK_PageDown, 29 | 30 | K_F1 = kVK_F1, 31 | K_F2 = kVK_F2, 32 | K_F3 = kVK_F3, 33 | K_F4 = kVK_F4, 34 | K_F5 = kVK_F5, 35 | K_F6 = kVK_F6, 36 | K_F7 = kVK_F7, 37 | K_F8 = kVK_F8, 38 | K_F9 = kVK_F9, 39 | K_F10 = kVK_F10, 40 | K_F11 = kVK_F11, 41 | K_F12 = kVK_F12, 42 | K_F13 = kVK_F13, 43 | K_F14 = kVK_F14, 44 | K_F15 = kVK_F15, 45 | K_F16 = kVK_F16, 46 | K_F17 = kVK_F17, 47 | K_F18 = kVK_F18, 48 | K_F19 = kVK_F19, 49 | K_F20 = kVK_F20, 50 | K_F21 = K_NOT_A_KEY, 51 | K_F22 = K_NOT_A_KEY, 52 | K_F23 = K_NOT_A_KEY, 53 | K_F24 = K_NOT_A_KEY, 54 | 55 | K_META = kVK_Command, 56 | K_LMETA = kVK_Command, 57 | K_RMETA = kVK_RightCommand, 58 | K_ALT = kVK_Option, 59 | K_LALT = kVK_Option, 60 | K_RALT = kVK_RightOption, 61 | K_CONTROL = kVK_Control, 62 | K_LCONTROL = kVK_Control, 63 | K_RCONTROL = kVK_RightControl, 64 | K_SHIFT = kVK_Shift, 65 | K_LSHIFT = kVK_Shift, 66 | K_RSHIFT = kVK_RightShift, 67 | K_CAPSLOCK = kVK_CapsLock, 68 | K_SPACE = kVK_Space, 69 | K_INSERT = kVK_Help, 70 | // K_PRINTSCREEN = K_NOT_A_KEY, 71 | K_PRINTSCREEN = kVK_F13, 72 | K_MENU = K_NOT_A_KEY, 73 | 74 | K_NUMPAD_0 = kVK_ANSI_Keypad0, 75 | K_NUMPAD_1 = kVK_ANSI_Keypad1, 76 | K_NUMPAD_2 = kVK_ANSI_Keypad2, 77 | K_NUMPAD_3 = kVK_ANSI_Keypad3, 78 | K_NUMPAD_4 = kVK_ANSI_Keypad4, 79 | K_NUMPAD_5 = kVK_ANSI_Keypad5, 80 | K_NUMPAD_6 = kVK_ANSI_Keypad6, 81 | K_NUMPAD_7 = kVK_ANSI_Keypad7, 82 | K_NUMPAD_8 = kVK_ANSI_Keypad8, 83 | K_NUMPAD_9 = kVK_ANSI_Keypad9, 84 | K_NUMPAD_LOCK = kVK_ANSI_KeypadClear, 85 | // 86 | K_NUMPAD_DECIMAL = kVK_ANSI_KeypadDecimal, 87 | K_NUMPAD_PLUS = kVK_ANSI_KeypadPlus, 88 | K_NUMPAD_MINUS = kVK_ANSI_KeypadMinus, 89 | K_NUMPAD_MUL = kVK_ANSI_KeypadMultiply, 90 | K_NUMPAD_DIV = kVK_ANSI_KeypadDivide, 91 | K_NUMPAD_CLEAR = kVK_ANSI_KeypadClear, 92 | K_NUMPAD_ENTER = kVK_ANSI_KeypadEnter, 93 | K_NUMPAD_EQUAL = kVK_ANSI_KeypadEquals, 94 | K_NUMPAD_LB = kVK_ANSI_LeftBracket, 95 | K_NUMPAD_RB = kVK_ANSI_RightBracket, 96 | K_Backslash = kVK_ANSI_Backslash, 97 | K_Semicolon = kVK_ANSI_Semicolon, 98 | K_Quote = kVK_ANSI_Quote, 99 | K_Slash = kVK_ANSI_Slash, 100 | K_Grave = kVK_ANSI_Grave, 101 | 102 | K_AUDIO_VOLUME_MUTE = 1007, 103 | K_AUDIO_VOLUME_DOWN = 1001, 104 | K_AUDIO_VOLUME_UP = 1000, 105 | K_AUDIO_PLAY = 1016, 106 | K_AUDIO_STOP = K_NOT_A_KEY, 107 | K_AUDIO_PAUSE = 1016, 108 | K_AUDIO_PREV = 1018, 109 | K_AUDIO_NEXT = 1017, 110 | K_AUDIO_REWIND = K_NOT_A_KEY, 111 | K_AUDIO_FORWARD = K_NOT_A_KEY, 112 | K_AUDIO_REPEAT = K_NOT_A_KEY, 113 | K_AUDIO_RANDOM = K_NOT_A_KEY, 114 | 115 | K_LIGHTS_MON_UP = 1002, 116 | K_LIGHTS_MON_DOWN = 1003, 117 | K_LIGHTS_KBD_TOGGLE = 1023, 118 | K_LIGHTS_KBD_UP = 1021, 119 | K_LIGHTS_KBD_DOWN = 1022 120 | }; 121 | 122 | typedef CGKeyCode MMKeyCode; 123 | 124 | #elif defined(USE_X11) 125 | 126 | #include 127 | #include 128 | 129 | enum _MMKeyCode { 130 | K_NOT_A_KEY = 9999, 131 | K_BACKSPACE = XK_BackSpace, 132 | K_DELETE = XK_Delete, 133 | K_RETURN = XK_Return, 134 | K_TAB = XK_Tab, 135 | K_ESCAPE = XK_Escape, 136 | K_UP = XK_Up, 137 | K_DOWN = XK_Down, 138 | K_RIGHT = XK_Right, 139 | K_LEFT = XK_Left, 140 | K_HOME = XK_Home, 141 | K_END = XK_End, 142 | K_PAGEUP = XK_Page_Up, 143 | K_PAGEDOWN = XK_Page_Down, 144 | 145 | K_F1 = XK_F1, 146 | K_F2 = XK_F2, 147 | K_F3 = XK_F3, 148 | K_F4 = XK_F4, 149 | K_F5 = XK_F5, 150 | K_F6 = XK_F6, 151 | K_F7 = XK_F7, 152 | K_F8 = XK_F8, 153 | K_F9 = XK_F9, 154 | K_F10 = XK_F10, 155 | K_F11 = XK_F11, 156 | K_F12 = XK_F12, 157 | K_F13 = XK_F13, 158 | K_F14 = XK_F14, 159 | K_F15 = XK_F15, 160 | K_F16 = XK_F16, 161 | K_F17 = XK_F17, 162 | K_F18 = XK_F18, 163 | K_F19 = XK_F19, 164 | K_F20 = XK_F20, 165 | K_F21 = XK_F21, 166 | K_F22 = XK_F22, 167 | K_F23 = XK_F23, 168 | K_F24 = XK_F24, 169 | 170 | K_META = XK_Super_L, 171 | K_LMETA = XK_Super_L, 172 | K_RMETA = XK_Super_R, 173 | K_ALT = XK_Alt_L, 174 | K_LALT = XK_Alt_L, 175 | K_RALT = XK_Alt_R, 176 | K_CONTROL = XK_Control_L, 177 | K_LCONTROL = XK_Control_L, 178 | K_RCONTROL = XK_Control_R, 179 | K_SHIFT = XK_Shift_L, 180 | K_LSHIFT = XK_Shift_L, 181 | K_RSHIFT = XK_Shift_R, 182 | K_CAPSLOCK = XK_Caps_Lock, 183 | K_SPACE = XK_space, 184 | K_INSERT = XK_Insert, 185 | K_PRINTSCREEN = XK_Print, 186 | K_MENU = K_NOT_A_KEY, 187 | 188 | // K_NUMPAD_0 = K_NOT_A_KEY, 189 | K_NUMPAD_0 = XK_KP_0, 190 | K_NUMPAD_1 = XK_KP_1, 191 | K_NUMPAD_2 = XK_KP_2, 192 | K_NUMPAD_3 = XK_KP_3, 193 | K_NUMPAD_4 = XK_KP_4, 194 | K_NUMPAD_5 = XK_KP_5, 195 | K_NUMPAD_6 = XK_KP_6, 196 | K_NUMPAD_7 = XK_KP_7, 197 | K_NUMPAD_8 = XK_KP_8, 198 | K_NUMPAD_9 = XK_KP_9, 199 | K_NUMPAD_LOCK = XK_Num_Lock, 200 | // 201 | K_NUMPAD_DECIMAL = XK_KP_Decimal, 202 | K_NUMPAD_PLUS = 78, // XK_KP_Add 203 | K_NUMPAD_MINUS = 74, // XK_KP_Subtract 204 | K_NUMPAD_MUL = 55, // XK_KP_Multiply 205 | K_NUMPAD_DIV = 98, // XK_KP_Divide 206 | K_NUMPAD_CLEAR = K_NOT_A_KEY, 207 | K_NUMPAD_ENTER = 96, // XK_KP_Enter 208 | K_NUMPAD_EQUAL = XK_equal, 209 | K_NUMPAD_LB = XK_bracketleft, 210 | K_NUMPAD_RB = XK_bracketright, 211 | K_Backslash = XK_backslash, 212 | K_Semicolon = XK_semicolon, 213 | K_Quote = XK_apostrophe, 214 | K_Slash = XK_slash, 215 | K_Grave = XK_grave, 216 | 217 | K_AUDIO_VOLUME_MUTE = XF86XK_AudioMute, 218 | K_AUDIO_VOLUME_DOWN = XF86XK_AudioLowerVolume, 219 | K_AUDIO_VOLUME_UP = XF86XK_AudioRaiseVolume, 220 | K_AUDIO_PLAY = XF86XK_AudioPlay, 221 | K_AUDIO_STOP = XF86XK_AudioStop, 222 | K_AUDIO_PAUSE = XF86XK_AudioPause, 223 | K_AUDIO_PREV = XF86XK_AudioPrev, 224 | K_AUDIO_NEXT = XF86XK_AudioNext, 225 | K_AUDIO_REWIND = XF86XK_AudioRewind, 226 | K_AUDIO_FORWARD = XF86XK_AudioForward, 227 | K_AUDIO_REPEAT = XF86XK_AudioRepeat, 228 | K_AUDIO_RANDOM = XF86XK_AudioRandomPlay, 229 | 230 | K_LIGHTS_MON_UP = XF86XK_MonBrightnessUp, 231 | K_LIGHTS_MON_DOWN = XF86XK_MonBrightnessDown, 232 | K_LIGHTS_KBD_TOGGLE = XF86XK_KbdLightOnOff, 233 | K_LIGHTS_KBD_UP = XF86XK_KbdBrightnessUp, 234 | K_LIGHTS_KBD_DOWN = XF86XK_KbdBrightnessDown 235 | }; 236 | 237 | typedef KeySym MMKeyCode; 238 | 239 | /* 240 | * Structs to store key mappings not handled by XStringToKeysym() on some 241 | * Linux systems. 242 | */ 243 | struct XSpecialCharacterMapping { 244 | char name; 245 | MMKeyCode code; 246 | }; 247 | 248 | struct XSpecialCharacterMapping XSpecialCharacterTable[] = { 249 | {'~', XK_asciitilde}, 250 | {'_', XK_underscore}, 251 | {'[', XK_bracketleft}, 252 | {']', XK_bracketright}, 253 | {'!', XK_exclam}, 254 | {'#', XK_numbersign}, 255 | {'$', XK_dollar}, 256 | {'%', XK_percent}, 257 | {'&', XK_ampersand}, 258 | {'*', XK_asterisk}, 259 | {'+', XK_plus}, 260 | {',', XK_comma}, 261 | {'-', XK_minus}, 262 | {'.', XK_period}, 263 | {'?', XK_question}, 264 | {'<', XK_less}, 265 | {'>', XK_greater}, 266 | {'=', XK_equal}, 267 | {'@', XK_at}, 268 | {':', XK_colon}, 269 | {';', XK_semicolon}, 270 | {'{', XK_braceleft}, 271 | {'}', XK_braceright}, 272 | {'|', XK_bar}, 273 | {'^', XK_asciicircum}, 274 | {'(', XK_parenleft}, 275 | {')', XK_parenright}, 276 | {' ', XK_space}, 277 | {'/', XK_slash}, 278 | {'\\', XK_backslash}, 279 | {'`', XK_grave}, 280 | {'"', XK_quoteright}, 281 | {'\'', XK_quotedbl}, 282 | {'\t', XK_Tab}, 283 | {'\n', XK_Return} 284 | }; 285 | 286 | #elif defined(IS_WINDOWS) 287 | 288 | enum _MMKeyCode { 289 | K_NOT_A_KEY = 9999, 290 | K_BACKSPACE = VK_BACK, 291 | K_DELETE = VK_DELETE, 292 | K_RETURN = VK_RETURN, 293 | K_TAB = VK_TAB, 294 | K_ESCAPE = VK_ESCAPE, 295 | K_UP = VK_UP, 296 | K_DOWN = VK_DOWN, 297 | K_RIGHT = VK_RIGHT, 298 | K_LEFT = VK_LEFT, 299 | K_HOME = VK_HOME, 300 | K_END = VK_END, 301 | K_PAGEUP = VK_PRIOR, 302 | K_PAGEDOWN = VK_NEXT, 303 | 304 | K_F1 = VK_F1, 305 | K_F2 = VK_F2, 306 | K_F3 = VK_F3, 307 | K_F4 = VK_F4, 308 | K_F5 = VK_F5, 309 | K_F6 = VK_F6, 310 | K_F7 = VK_F7, 311 | K_F8 = VK_F8, 312 | K_F9 = VK_F9, 313 | K_F10 = VK_F10, 314 | K_F11 = VK_F11, 315 | K_F12 = VK_F12, 316 | K_F13 = VK_F13, 317 | K_F14 = VK_F14, 318 | K_F15 = VK_F15, 319 | K_F16 = VK_F16, 320 | K_F17 = VK_F17, 321 | K_F18 = VK_F18, 322 | K_F19 = VK_F19, 323 | K_F20 = VK_F20, 324 | K_F21 = VK_F21, 325 | K_F22 = VK_F22, 326 | K_F23 = VK_F23, 327 | K_F24 = VK_F24, 328 | 329 | K_META = VK_LWIN, 330 | K_LMETA = VK_LWIN, 331 | K_RMETA = VK_RWIN, 332 | K_ALT = VK_MENU, 333 | K_LALT = VK_LMENU, 334 | K_RALT = VK_RMENU, 335 | K_CONTROL = VK_CONTROL, 336 | K_LCONTROL = VK_LCONTROL, 337 | K_RCONTROL = VK_RCONTROL, 338 | K_SHIFT = VK_SHIFT, 339 | K_LSHIFT = VK_LSHIFT, 340 | K_RSHIFT = VK_RSHIFT, 341 | K_CAPSLOCK = VK_CAPITAL, 342 | K_SPACE = VK_SPACE, 343 | K_PRINTSCREEN = VK_SNAPSHOT, 344 | K_INSERT = VK_INSERT, 345 | K_MENU = VK_APPS, 346 | 347 | K_NUMPAD_0 = VK_NUMPAD0, 348 | K_NUMPAD_1 = VK_NUMPAD1, 349 | K_NUMPAD_2 = VK_NUMPAD2, 350 | K_NUMPAD_3 = VK_NUMPAD3, 351 | K_NUMPAD_4 = VK_NUMPAD4, 352 | K_NUMPAD_5 = VK_NUMPAD5, 353 | K_NUMPAD_6 = VK_NUMPAD6, 354 | K_NUMPAD_7 = VK_NUMPAD7, 355 | K_NUMPAD_8 = VK_NUMPAD8, 356 | K_NUMPAD_9 = VK_NUMPAD9, 357 | K_NUMPAD_LOCK = VK_NUMLOCK, 358 | // VK_NUMPAD_ 359 | K_NUMPAD_DECIMAL = VK_DECIMAL, 360 | K_NUMPAD_PLUS = VK_ADD, 361 | K_NUMPAD_MINUS = VK_SUBTRACT, 362 | K_NUMPAD_MUL = VK_MULTIPLY, 363 | K_NUMPAD_DIV = VK_DIVIDE, 364 | K_NUMPAD_CLEAR = K_NOT_A_KEY, 365 | K_NUMPAD_ENTER = VK_RETURN, 366 | K_NUMPAD_EQUAL = VK_OEM_PLUS, 367 | K_NUMPAD_LB = VK_OEM_4, 368 | K_NUMPAD_RB = VK_OEM_6, 369 | K_Backslash = VK_OEM_5, 370 | K_Semicolon = VK_OEM_1, 371 | K_Quote = VK_OEM_7, 372 | K_Slash = VK_OEM_2, 373 | K_Grave = VK_OEM_3, 374 | 375 | K_AUDIO_VOLUME_MUTE = VK_VOLUME_MUTE, 376 | K_AUDIO_VOLUME_DOWN = VK_VOLUME_DOWN, 377 | K_AUDIO_VOLUME_UP = VK_VOLUME_UP, 378 | K_AUDIO_PLAY = VK_MEDIA_PLAY_PAUSE, 379 | K_AUDIO_STOP = VK_MEDIA_STOP, 380 | K_AUDIO_PAUSE = VK_MEDIA_PLAY_PAUSE, 381 | K_AUDIO_PREV = VK_MEDIA_PREV_TRACK, 382 | K_AUDIO_NEXT = VK_MEDIA_NEXT_TRACK, 383 | K_AUDIO_REWIND = K_NOT_A_KEY, 384 | K_AUDIO_FORWARD = K_NOT_A_KEY, 385 | K_AUDIO_REPEAT = K_NOT_A_KEY, 386 | K_AUDIO_RANDOM = K_NOT_A_KEY, 387 | 388 | K_LIGHTS_MON_UP = K_NOT_A_KEY, 389 | K_LIGHTS_MON_DOWN = K_NOT_A_KEY, 390 | K_LIGHTS_KBD_TOGGLE = K_NOT_A_KEY, 391 | K_LIGHTS_KBD_UP = K_NOT_A_KEY, 392 | K_LIGHTS_KBD_DOWN = K_NOT_A_KEY 393 | }; 394 | 395 | typedef int MMKeyCode; 396 | 397 | #endif 398 | 399 | /* Returns the keyCode corresponding to the current keyboard layout for the 400 | * given ASCII character. */ 401 | MMKeyCode keyCodeForChar(const char c); 402 | 403 | #endif /* KEYCODE_H */ 404 | -------------------------------------------------------------------------------- /key/keycode_c.h: -------------------------------------------------------------------------------- 1 | #include "keycode.h" 2 | 3 | #if defined(IS_MACOSX) 4 | #include 5 | #include /* For kVK_ constants, and TIS functions. */ 6 | 7 | /* Returns string representation of key, if it is printable. 8 | Ownership follows the Create Rule; 9 | it is the caller's responsibility to release the returned object. */ 10 | CFStringRef createStringForKey(CGKeyCode keyCode); 11 | #endif 12 | 13 | MMKeyCode keyCodeForChar(const char c) { 14 | #if defined(IS_MACOSX) 15 | /* OS X does not appear to have a built-in function for this, 16 | so instead it to write our own. */ 17 | static CFMutableDictionaryRef charToCodeDict = NULL; 18 | CGKeyCode code; 19 | UniChar character = c; 20 | CFStringRef charStr = NULL; 21 | 22 | /* Generate table of keycodes and characters. */ 23 | if (charToCodeDict == NULL) { 24 | size_t i; 25 | charToCodeDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 128, 26 | &kCFCopyStringDictionaryKeyCallBacks, NULL); 27 | if (charToCodeDict == NULL) { return K_NOT_A_KEY; } 28 | 29 | /* Loop through every keycode (0 - 127) to find its current mapping. */ 30 | for (i = 0; i < 128; ++i) { 31 | CFStringRef string = createStringForKey((CGKeyCode)i); 32 | if (string != NULL) { 33 | CFDictionaryAddValue(charToCodeDict, string, (const void *)i); 34 | CFRelease(string); 35 | } 36 | } 37 | } 38 | 39 | charStr = CFStringCreateWithCharacters(kCFAllocatorDefault, &character, 1); 40 | /* Our values may be NULL (0), so we need to use this function. */ 41 | if (!CFDictionaryGetValueIfPresent(charToCodeDict, charStr, (const void **)&code)) { 42 | code = UINT16_MAX; /* Error */ 43 | } 44 | CFRelease(charStr); 45 | 46 | // TISGetInputSourceProperty may return nil so we need fallback 47 | if (code == UINT16_MAX) { 48 | return K_NOT_A_KEY; 49 | } 50 | 51 | return (MMKeyCode)code; 52 | #elif defined(IS_WINDOWS) 53 | MMKeyCode code; 54 | code = VkKeyScan(c); 55 | if (code == 0xFFFF) { 56 | return K_NOT_A_KEY; 57 | } 58 | 59 | return code; 60 | #elif defined(USE_X11) 61 | char buf[2]; 62 | buf[0] = c; 63 | buf[1] = '\0'; 64 | 65 | MMKeyCode code = XStringToKeysym(buf); 66 | if (code == NoSymbol) { 67 | /* Some special keys are apparently not handled properly */ 68 | struct XSpecialCharacterMapping* xs = XSpecialCharacterTable; 69 | while (xs->name) { 70 | if (c == xs->name) { 71 | code = xs->code; 72 | // 73 | break; 74 | } 75 | xs++; 76 | } 77 | } 78 | 79 | if (code == NoSymbol) { 80 | return K_NOT_A_KEY; 81 | } 82 | 83 | // x11 key bug 84 | if (c == 60) { 85 | code = 44; 86 | } 87 | return code; 88 | #endif 89 | } 90 | 91 | #if defined(IS_MACOSX) 92 | CFStringRef createStringForKey(CGKeyCode keyCode){ 93 | // TISInputSourceRef currentKeyboard = TISCopyCurrentASCIICapableKeyboardInputSource(); 94 | TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardLayoutInputSource(); 95 | CFDataRef layoutData = (CFDataRef) TISGetInputSourceProperty( 96 | currentKeyboard, kTISPropertyUnicodeKeyLayoutData); 97 | 98 | if (layoutData == nil) { return 0; } 99 | 100 | const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *) CFDataGetBytePtr(layoutData); 101 | UInt32 keysDown = 0; 102 | UniChar chars[4]; 103 | UniCharCount realLength; 104 | 105 | UCKeyTranslate(keyboardLayout, keyCode, kUCKeyActionDisplay, 0, LMGetKbdType(), 106 | kUCKeyTranslateNoDeadKeysBit, &keysDown, 107 | sizeof(chars) / sizeof(chars[0]), &realLength, chars); 108 | CFRelease(currentKeyboard); 109 | 110 | return CFStringCreateWithCharacters(kCFAllocatorDefault, chars, 1); 111 | } 112 | #endif 113 | -------------------------------------------------------------------------------- /key/keypress.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef KEYPRESS_H 3 | #define KEYPRESS_H 4 | 5 | #include 6 | #include "../base/os.h" 7 | #include "../base/types.h" 8 | 9 | #include "keycode.h" 10 | #include 11 | 12 | #if defined(IS_MACOSX) 13 | typedef enum { 14 | MOD_NONE = 0, 15 | MOD_META = kCGEventFlagMaskCommand, 16 | MOD_ALT = kCGEventFlagMaskAlternate, 17 | MOD_CONTROL = kCGEventFlagMaskControl, 18 | MOD_SHIFT = kCGEventFlagMaskShift 19 | } MMKeyFlags; 20 | #elif defined(USE_X11) 21 | enum _MMKeyFlags { 22 | MOD_NONE = 0, 23 | MOD_META = Mod4Mask, 24 | MOD_ALT = Mod1Mask, 25 | MOD_CONTROL = ControlMask, 26 | MOD_SHIFT = ShiftMask 27 | }; 28 | typedef unsigned int MMKeyFlags; 29 | #elif defined(IS_WINDOWS) 30 | enum _MMKeyFlags { 31 | MOD_NONE = 0, 32 | /* These are already defined by the Win32 API */ 33 | /* MOD_ALT = 0, 34 | MOD_CONTROL = 0, 35 | MOD_SHIFT = 0, */ 36 | MOD_META = MOD_WIN 37 | }; 38 | typedef unsigned int MMKeyFlags; 39 | #endif 40 | 41 | #if defined(IS_WINDOWS) 42 | /* Send win32 key event for given key. */ 43 | void win32KeyEvent(int key, MMKeyFlags flags, uintptr pid, int8_t isPid); 44 | #endif 45 | 46 | #endif /* KEYPRESS_H */ 47 | -------------------------------------------------------------------------------- /key/keypress_c.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | #include "../base/deadbeef_rand_c.h" 12 | #include "../base/microsleep.h" 13 | #include "keypress.h" 14 | #include "keycode_c.h" 15 | 16 | #include /* For isupper() */ 17 | #if defined(IS_MACOSX) 18 | #include 19 | #import 20 | #import 21 | #elif defined(USE_X11) 22 | #include 23 | // #include "../base/xdisplay_c.h" 24 | #endif 25 | 26 | /* Convenience wrappers around ugly APIs. */ 27 | #if defined(IS_WINDOWS) 28 | HWND GetHwndByPid(DWORD dwProcessId); 29 | 30 | HWND getHwnd(uintptr pid, int8_t isPid) { 31 | HWND hwnd = (HWND) pid; 32 | if (isPid == 0) { 33 | hwnd = GetHwndByPid(pid); 34 | } 35 | return hwnd; 36 | } 37 | 38 | void WIN32_KEY_EVENT_WAIT(MMKeyCode key, DWORD flags, uintptr pid) { 39 | win32KeyEvent(key, flags, pid, 0); 40 | Sleep(DEADBEEF_RANDRANGE(0, 1)); 41 | } 42 | #elif defined(USE_X11) 43 | Display *XGetMainDisplay(void); 44 | 45 | void X_KEY_EVENT(Display *display, MMKeyCode key, bool is_press) { 46 | XTestFakeKeyEvent(display, XKeysymToKeycode(display, key), is_press, CurrentTime); 47 | XSync(display, false); 48 | } 49 | 50 | void X_KEY_EVENT_WAIT(Display *display, MMKeyCode key, bool is_press) { 51 | X_KEY_EVENT(display, key, is_press); 52 | microsleep(DEADBEEF_UNIFORM(0.0, 0.5)); 53 | } 54 | #endif 55 | 56 | #if defined(IS_MACOSX) 57 | int SendTo(uintptr pid, CGEventRef event) { 58 | if (pid != 0) { 59 | CGEventPostToPid(pid, event); 60 | } else { 61 | CGEventPost(kCGHIDEventTap, event); 62 | } 63 | 64 | CFRelease(event); 65 | return 0; 66 | } 67 | 68 | static io_connect_t _getAuxiliaryKeyDriver(void) { 69 | static mach_port_t sEventDrvrRef = 0; 70 | mach_port_t masterPort, service, iter; 71 | kern_return_t kr; 72 | 73 | if (!sEventDrvrRef) { 74 | kr = IOMasterPort(bootstrap_port, &masterPort); 75 | assert(KERN_SUCCESS == kr); 76 | kr = IOServiceGetMatchingServices(masterPort, IOServiceMatching(kIOHIDSystemClass), &iter); 77 | assert(KERN_SUCCESS == kr); 78 | 79 | service = IOIteratorNext(iter); 80 | assert(service); 81 | 82 | kr = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &sEventDrvrRef); 83 | assert(KERN_SUCCESS == kr); 84 | 85 | IOObjectRelease(service); 86 | IOObjectRelease(iter); 87 | } 88 | return sEventDrvrRef; 89 | } 90 | #elif defined(IS_WINDOWS) 91 | 92 | void win32KeyEvent(int key, MMKeyFlags flags, uintptr pid, int8_t isPid) { 93 | int scan = MapVirtualKey(key & 0xff, MAPVK_VK_TO_VSC); 94 | 95 | /* Set the scan code for extended keys */ 96 | switch (key){ 97 | case VK_RCONTROL: 98 | case VK_SNAPSHOT: /* Print Screen */ 99 | case VK_RMENU: /* Right Alt / Alt Gr */ 100 | case VK_PAUSE: /* Pause / Break */ 101 | case VK_HOME: 102 | case VK_UP: 103 | case VK_PRIOR: /* Page up */ 104 | case VK_LEFT: 105 | case VK_RIGHT: 106 | case VK_END: 107 | case VK_DOWN: 108 | case VK_NEXT: /* 'Page Down' */ 109 | case VK_INSERT: 110 | case VK_DELETE: 111 | case VK_LWIN: 112 | case VK_RWIN: 113 | case VK_APPS: /* Application */ 114 | case VK_VOLUME_MUTE: 115 | case VK_VOLUME_DOWN: 116 | case VK_VOLUME_UP: 117 | case VK_MEDIA_NEXT_TRACK: 118 | case VK_MEDIA_PREV_TRACK: 119 | case VK_MEDIA_STOP: 120 | case VK_MEDIA_PLAY_PAUSE: 121 | case VK_BROWSER_BACK: 122 | case VK_BROWSER_FORWARD: 123 | case VK_BROWSER_REFRESH: 124 | case VK_BROWSER_STOP: 125 | case VK_BROWSER_SEARCH: 126 | case VK_BROWSER_FAVORITES: 127 | case VK_BROWSER_HOME: 128 | case VK_LAUNCH_MAIL: 129 | { 130 | flags |= KEYEVENTF_EXTENDEDKEY; 131 | break; 132 | } 133 | } 134 | 135 | // todo: test this 136 | if (pid != 0) { 137 | HWND hwnd = getHwnd(pid, isPid); 138 | 139 | int down = (flags == 0 ? WM_KEYDOWN : WM_KEYUP); 140 | // SendMessage(hwnd, down, key, 0); 141 | PostMessageW(hwnd, down, key, 0); 142 | return; 143 | } 144 | 145 | /* Set the scan code for keyup */ 146 | // if ( flags & KEYEVENTF_KEYUP ) { 147 | // scan |= 0x80; 148 | // } 149 | // keybd_event(key, scan, flags, 0); 150 | 151 | INPUT keyInput; 152 | 153 | keyInput.type = INPUT_KEYBOARD; 154 | keyInput.ki.wVk = key; 155 | keyInput.ki.wScan = scan; 156 | keyInput.ki.dwFlags = flags; 157 | keyInput.ki.time = 0; 158 | keyInput.ki.dwExtraInfo = 0; 159 | SendInput(1, &keyInput, sizeof(keyInput)); 160 | } 161 | #endif 162 | 163 | void toggleKeyCode(MMKeyCode code, const bool down, MMKeyFlags flags, uintptr pid) { 164 | #if defined(IS_MACOSX) 165 | /* The media keys all have 1000 added to them to help us detect them. */ 166 | if (code >= 1000) { 167 | code = code - 1000; /* Get the real keycode. */ 168 | NXEventData event; 169 | kern_return_t kr; 170 | 171 | IOGPoint loc = { 0, 0 }; 172 | UInt32 evtInfo = code << 16 | (down?NX_KEYDOWN:NX_KEYUP) << 8; 173 | 174 | bzero(&event, sizeof(NXEventData)); 175 | event.compound.subType = NX_SUBTYPE_AUX_CONTROL_BUTTONS; 176 | event.compound.misc.L[0] = evtInfo; 177 | 178 | kr = IOHIDPostEvent(_getAuxiliaryKeyDriver(), 179 | NX_SYSDEFINED, loc, &event, kNXEventDataVersion, 0, FALSE); 180 | assert(KERN_SUCCESS == kr); 181 | } else { 182 | CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); 183 | CGEventRef keyEvent = CGEventCreateKeyboardEvent(source, (CGKeyCode)code, down); 184 | assert(keyEvent != NULL); 185 | 186 | CGEventSetType(keyEvent, down ? kCGEventKeyDown : kCGEventKeyUp); 187 | if (flags != 0) { 188 | CGEventSetFlags(keyEvent, (CGEventFlags) flags); 189 | } 190 | 191 | SendTo(pid, keyEvent); 192 | CFRelease(source); 193 | } 194 | #elif defined(IS_WINDOWS) 195 | const DWORD dwFlags = down ? 0 : KEYEVENTF_KEYUP; 196 | 197 | /* Parse modifier keys. */ 198 | if (flags & MOD_META) { WIN32_KEY_EVENT_WAIT(K_META, dwFlags, pid); } 199 | if (flags & MOD_ALT) { WIN32_KEY_EVENT_WAIT(K_ALT, dwFlags, pid); } 200 | if (flags & MOD_CONTROL) { WIN32_KEY_EVENT_WAIT(K_CONTROL, dwFlags, pid); } 201 | if (flags & MOD_SHIFT) { WIN32_KEY_EVENT_WAIT(K_SHIFT, dwFlags, pid); } 202 | 203 | win32KeyEvent(code, dwFlags, pid, 0); 204 | #elif defined(USE_X11) 205 | Display *display = XGetMainDisplay(); 206 | const Bool is_press = down ? True : False; /* Just to be safe. */ 207 | 208 | /* Parse modifier keys. */ 209 | if (flags & MOD_META) { X_KEY_EVENT_WAIT(display, K_META, is_press); } 210 | if (flags & MOD_ALT) { X_KEY_EVENT_WAIT(display, K_ALT, is_press); } 211 | if (flags & MOD_CONTROL) { X_KEY_EVENT_WAIT(display, K_CONTROL, is_press); } 212 | if (flags & MOD_SHIFT) { X_KEY_EVENT_WAIT(display, K_SHIFT, is_press); } 213 | 214 | X_KEY_EVENT(display, code, is_press); 215 | #endif 216 | } 217 | 218 | // void tapKeyCode(MMKeyCode code, MMKeyFlags flags){ 219 | // toggleKeyCode(code, true, flags); 220 | // microsleep(5.0); 221 | // toggleKeyCode(code, false, flags); 222 | // } 223 | 224 | #if defined(USE_X11) 225 | bool toUpper(char c) { 226 | if (isupper(c)) { 227 | return true; 228 | } 229 | 230 | char *special = "~!@#$%^&*()_+{}|:\"<>?"; 231 | while (*special) { 232 | if (*special == c) { 233 | return true; 234 | } 235 | special++; 236 | } 237 | return false; 238 | } 239 | #endif 240 | 241 | void toggleKey(char c, const bool down, MMKeyFlags flags, uintptr pid) { 242 | MMKeyCode keyCode = keyCodeForChar(c); 243 | 244 | #if defined(USE_X11) 245 | if (toUpper(c) && !(flags & MOD_SHIFT)) { 246 | flags |= MOD_SHIFT; /* Not sure if this is safe for all layouts. */ 247 | } 248 | #else 249 | if (isupper(c) && !(flags & MOD_SHIFT)) { 250 | flags |= MOD_SHIFT; /* Not sure if this is safe for all layouts. */ 251 | } 252 | #endif 253 | 254 | #if defined(IS_WINDOWS) 255 | int modifiers = keyCode >> 8; // Pull out modifers. 256 | 257 | if ((modifiers & 1) != 0) { flags |= MOD_SHIFT; } // Uptdate flags from keycode modifiers. 258 | if ((modifiers & 2) != 0) { flags |= MOD_CONTROL; } 259 | if ((modifiers & 4) != 0) { flags |= MOD_ALT; } 260 | keyCode = keyCode & 0xff; // Mask out modifiers. 261 | #endif 262 | 263 | toggleKeyCode(keyCode, down, flags, pid); 264 | } 265 | 266 | // void tapKey(char c, MMKeyFlags flags){ 267 | // toggleKey(c, true, flags); 268 | // microsleep(5.0); 269 | // toggleKey(c, false, flags); 270 | // } 271 | 272 | #if defined(IS_MACOSX) 273 | void toggleUnicode(UniChar ch, const bool down, uintptr pid) { 274 | /* This function relies on the convenient CGEventKeyboardSetUnicodeString(), 275 | convert characters to a keycode, but does not support adding modifier flags. 276 | It is only used in typeString(). 277 | -- if you need modifier keys, use the above functions instead. */ 278 | CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); 279 | CGEventRef keyEvent = CGEventCreateKeyboardEvent(source, 0, down); 280 | if (keyEvent == NULL) { 281 | fputs("Could not create keyboard event.\n", stderr); 282 | return; 283 | } 284 | 285 | CGEventKeyboardSetUnicodeString(keyEvent, 1, &ch); 286 | 287 | SendTo(pid, keyEvent); 288 | CFRelease(source); 289 | } 290 | #else 291 | #define toggleUniKey(c, down) toggleKey(c, down, MOD_NONE, 0) 292 | #endif 293 | 294 | // unicode type 295 | void unicodeType(const unsigned value, uintptr pid, int8_t isPid) { 296 | #if defined(IS_MACOSX) 297 | UniChar ch = (UniChar)value; // Convert to unsigned char 298 | 299 | toggleUnicode(ch, true, pid); 300 | microsleep(5.0); 301 | toggleUnicode(ch, false, pid); 302 | #elif defined(IS_WINDOWS) 303 | if (pid != 0) { 304 | HWND hwnd = getHwnd(pid, isPid); 305 | 306 | // SendMessage(hwnd, down, value, 0); 307 | PostMessageW(hwnd, WM_CHAR, value, 0); 308 | return; 309 | } 310 | 311 | INPUT input[2]; 312 | memset(input, 0, sizeof(input)); 313 | 314 | input[0].type = INPUT_KEYBOARD; 315 | input[0].ki.wVk = 0; 316 | input[0].ki.wScan = value; 317 | input[0].ki.dwFlags = 0x4; // KEYEVENTF_UNICODE; 318 | 319 | input[1].type = INPUT_KEYBOARD; 320 | input[1].ki.wVk = 0; 321 | input[1].ki.wScan = value; 322 | input[1].ki.dwFlags = KEYEVENTF_KEYUP | 0x4; // KEYEVENTF_UNICODE; 323 | 324 | SendInput(2, input, sizeof(INPUT)); 325 | #elif defined(USE_X11) 326 | toggleUniKey(value, true); 327 | microsleep(5.0); 328 | toggleUniKey(value, false); 329 | #endif 330 | } 331 | 332 | #if defined(USE_X11) 333 | int input_utf(const char *utf) { 334 | Display *dpy = XOpenDisplay(NULL); 335 | KeySym sym = XStringToKeysym(utf); 336 | // KeySym sym = XKeycodeToKeysym(dpy, utf); 337 | 338 | int min, max, numcodes; 339 | XDisplayKeycodes(dpy, &min, &max); 340 | KeySym *keysym; 341 | keysym = XGetKeyboardMapping(dpy, min, max-min+1, &numcodes); 342 | keysym[(max-min-1)*numcodes] = sym; 343 | XChangeKeyboardMapping(dpy, min, numcodes, keysym, (max-min)); 344 | XFree(keysym); 345 | XFlush(dpy); 346 | 347 | KeyCode code = XKeysymToKeycode(dpy, sym); 348 | XTestFakeKeyEvent(dpy, code, True, 1); 349 | XTestFakeKeyEvent(dpy, code, False, 1); 350 | 351 | XFlush(dpy); 352 | XCloseDisplay(dpy); 353 | return 0; 354 | } 355 | #else 356 | int input_utf(const char *utf){ 357 | return 0; 358 | } 359 | #endif -------------------------------------------------------------------------------- /keycode.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package robotgo 12 | 13 | import ( 14 | "github.com/vcaesar/keycode" 15 | ) 16 | 17 | type uMap map[string]uint16 18 | 19 | // MouseMap robotgo hook mouse's code map 20 | var MouseMap = keycode.MouseMap 21 | 22 | const ( 23 | // Mleft mouse left button 24 | Mleft = "left" 25 | Mright = "right" 26 | Center = "center" 27 | WheelDown = "wheelDown" 28 | WheelUp = "wheelUp" 29 | WheelLeft = "wheelLeft" 30 | WheelRight = "wheelRight" 31 | ) 32 | 33 | // Keycode robotgo hook key's code map 34 | var Keycode = keycode.Keycode 35 | 36 | // Special is the special key map 37 | var Special = keycode.Special 38 | -------------------------------------------------------------------------------- /mouse/mouse.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package mouse 12 | -------------------------------------------------------------------------------- /mouse/mouse.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef MOUSE_H 3 | #define MOUSE_H 4 | 5 | #include "../base/os.h" 6 | #include "../base/types.h" 7 | #include 8 | 9 | #if defined(IS_MACOSX) 10 | #include 11 | 12 | typedef enum { 13 | LEFT_BUTTON = kCGMouseButtonLeft, 14 | RIGHT_BUTTON = kCGMouseButtonRight, 15 | CENTER_BUTTON = kCGMouseButtonCenter, 16 | WheelDown = 4, 17 | WheelUp = 5, 18 | WheelLeft = 6, 19 | WheelRight = 7, 20 | } MMMouseButton; 21 | #elif defined(USE_X11) 22 | enum _MMMouseButton { 23 | LEFT_BUTTON = 1, 24 | CENTER_BUTTON = 2, 25 | RIGHT_BUTTON = 3, 26 | WheelDown = 4, 27 | WheelUp = 5, 28 | WheelLeft = 6, 29 | WheelRight = 7, 30 | }; 31 | typedef unsigned int MMMouseButton; 32 | #elif defined(IS_WINDOWS) 33 | enum _MMMouseButton { 34 | LEFT_BUTTON = 1, 35 | CENTER_BUTTON = 2, 36 | RIGHT_BUTTON = 3, 37 | WheelDown = 4, 38 | WheelUp = 5, 39 | WheelLeft = 6, 40 | WheelRight = 7, 41 | }; 42 | typedef unsigned int MMMouseButton; 43 | #else 44 | #error "No mouse button constants set for platform" 45 | #endif 46 | 47 | #endif /* MOUSE_H */ -------------------------------------------------------------------------------- /mouse/mouse_c.h: -------------------------------------------------------------------------------- 1 | #include "mouse.h" 2 | #include "../base/deadbeef_rand.h" 3 | #include "../base/microsleep.h" 4 | 5 | #include /* For floor() */ 6 | #if defined(IS_MACOSX) 7 | // #include 8 | #include 9 | // #include 10 | #elif defined(USE_X11) 11 | #include 12 | #include 13 | #include 14 | #endif 15 | 16 | /* Some convenience macros for converting our enums to the system API types. */ 17 | #if defined(IS_MACOSX) 18 | CGEventType MMMouseDownToCGEventType(MMMouseButton button) { 19 | if (button == LEFT_BUTTON) { 20 | return kCGEventLeftMouseDown; 21 | } 22 | if (button == RIGHT_BUTTON) { 23 | return kCGEventRightMouseDown; 24 | } 25 | return kCGEventOtherMouseDown; 26 | } 27 | 28 | CGEventType MMMouseUpToCGEventType(MMMouseButton button) { 29 | if (button == LEFT_BUTTON) { return kCGEventLeftMouseUp; } 30 | if (button == RIGHT_BUTTON) { return kCGEventRightMouseUp; } 31 | return kCGEventOtherMouseUp; 32 | } 33 | 34 | CGEventType MMMouseDragToCGEventType(MMMouseButton button) { 35 | if (button == LEFT_BUTTON) { return kCGEventLeftMouseDragged; } 36 | if (button == RIGHT_BUTTON) { return kCGEventRightMouseDragged; } 37 | return kCGEventOtherMouseDragged; 38 | } 39 | 40 | CGEventType MMMouseToCGEventType(bool down, MMMouseButton button) { 41 | if (down) { return MMMouseDownToCGEventType(button); } 42 | return MMMouseUpToCGEventType(button); 43 | } 44 | 45 | #elif defined(IS_WINDOWS) 46 | 47 | DWORD MMMouseUpToMEventF(MMMouseButton button) { 48 | if (button == LEFT_BUTTON) { return MOUSEEVENTF_LEFTUP; } 49 | if (button == RIGHT_BUTTON) { return MOUSEEVENTF_RIGHTUP; } 50 | return MOUSEEVENTF_MIDDLEUP; 51 | } 52 | 53 | DWORD MMMouseDownToMEventF(MMMouseButton button) { 54 | if (button == LEFT_BUTTON) { return MOUSEEVENTF_LEFTDOWN; } 55 | if (button == RIGHT_BUTTON) { return MOUSEEVENTF_RIGHTDOWN; } 56 | return MOUSEEVENTF_MIDDLEDOWN; 57 | } 58 | 59 | DWORD MMMouseToMEventF(bool down, MMMouseButton button) { 60 | if (down) { return MMMouseDownToMEventF(button); } 61 | return MMMouseUpToMEventF(button); 62 | } 63 | #endif 64 | 65 | #if defined(IS_MACOSX) 66 | /* Calculate the delta for a mouse move and add them to the event. */ 67 | void calculateDeltas(CGEventRef *event, MMPointInt32 point) { 68 | /* The next few lines are a workaround for games not detecting mouse moves. */ 69 | CGEventRef get = CGEventCreate(NULL); 70 | CGPoint mouse = CGEventGetLocation(get); 71 | 72 | // Calculate the deltas. 73 | int64_t deltaX = point.x - mouse.x; 74 | int64_t deltaY = point.y - mouse.y; 75 | 76 | CGEventSetIntegerValueField(*event, kCGMouseEventDeltaX, deltaX); 77 | CGEventSetIntegerValueField(*event, kCGMouseEventDeltaY, deltaY); 78 | 79 | CFRelease(get); 80 | } 81 | #endif 82 | 83 | /* Move the mouse to a specific point. */ 84 | void moveMouse(MMPointInt32 point){ 85 | #if defined(IS_MACOSX) 86 | CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); 87 | CGEventRef move = CGEventCreateMouseEvent(source, kCGEventMouseMoved, 88 | CGPointFromMMPointInt32(point), kCGMouseButtonLeft); 89 | 90 | calculateDeltas(&move, point); 91 | 92 | CGEventPost(kCGHIDEventTap, move); 93 | CFRelease(move); 94 | CFRelease(source); 95 | #elif defined(USE_X11) 96 | Display *display = XGetMainDisplay(); 97 | XWarpPointer(display, None, DefaultRootWindow(display), 0, 0, 0, 0, point.x, point.y); 98 | 99 | XSync(display, false); 100 | #elif defined(IS_WINDOWS) 101 | SetCursorPos(point.x, point.y); 102 | #endif 103 | } 104 | 105 | void dragMouse(MMPointInt32 point, const MMMouseButton button){ 106 | #if defined(IS_MACOSX) 107 | const CGEventType dragType = MMMouseDragToCGEventType(button); 108 | CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); 109 | CGEventRef drag = CGEventCreateMouseEvent(source, dragType, 110 | CGPointFromMMPointInt32(point), (CGMouseButton)button); 111 | 112 | calculateDeltas(&drag, point); 113 | 114 | CGEventPost(kCGHIDEventTap, drag); 115 | CFRelease(drag); 116 | CFRelease(source); 117 | #else 118 | moveMouse(point); 119 | #endif 120 | } 121 | 122 | MMPointInt32 location() { 123 | #if defined(IS_MACOSX) 124 | CGEventRef event = CGEventCreate(NULL); 125 | CGPoint point = CGEventGetLocation(event); 126 | CFRelease(event); 127 | 128 | return MMPointInt32FromCGPoint(point); 129 | #elif defined(USE_X11) 130 | int x, y; /* This is all we care about. Seriously. */ 131 | Window garb1, garb2; /* Why you can't specify NULL as a parameter */ 132 | int garb_x, garb_y; /* is beyond me. */ 133 | unsigned int more_garbage; 134 | 135 | Display *display = XGetMainDisplay(); 136 | XQueryPointer(display, XDefaultRootWindow(display), &garb1, &garb2, &x, &y, 137 | &garb_x, &garb_y, &more_garbage); 138 | 139 | return MMPointInt32Make(x, y); 140 | #elif defined(IS_WINDOWS) 141 | POINT point; 142 | GetCursorPos(&point); 143 | return MMPointInt32FromPOINT(point); 144 | #endif 145 | } 146 | 147 | /* Press down a button, or release it. */ 148 | void toggleMouse(bool down, MMMouseButton button) { 149 | #if defined(IS_MACOSX) 150 | const CGPoint currentPos = CGPointFromMMPointInt32(location()); 151 | const CGEventType mouseType = MMMouseToCGEventType(down, button); 152 | CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); 153 | CGEventRef event = CGEventCreateMouseEvent(source, mouseType, currentPos, (CGMouseButton)button); 154 | 155 | CGEventPost(kCGHIDEventTap, event); 156 | CFRelease(event); 157 | CFRelease(source); 158 | #elif defined(USE_X11) 159 | Display *display = XGetMainDisplay(); 160 | XTestFakeButtonEvent(display, button, down ? True : False, CurrentTime); 161 | XSync(display, false); 162 | #elif defined(IS_WINDOWS) 163 | // mouse_event(MMMouseToMEventF(down, button), 0, 0, 0, 0); 164 | INPUT mouseInput; 165 | 166 | mouseInput.type = INPUT_MOUSE; 167 | mouseInput.mi.dx = 0; 168 | mouseInput.mi.dy = 0; 169 | mouseInput.mi.dwFlags = MMMouseToMEventF(down, button); 170 | mouseInput.mi.time = 0; 171 | mouseInput.mi.dwExtraInfo = 0; 172 | mouseInput.mi.mouseData = 0; 173 | SendInput(1, &mouseInput, sizeof(mouseInput)); 174 | #endif 175 | } 176 | 177 | void clickMouse(MMMouseButton button){ 178 | toggleMouse(true, button); 179 | microsleep(5.0); 180 | toggleMouse(false, button); 181 | } 182 | 183 | /* Special function for sending double clicks, needed for MacOS. */ 184 | void doubleClick(MMMouseButton button){ 185 | #if defined(IS_MACOSX) 186 | /* Double click for Mac. */ 187 | const CGPoint currentPos = CGPointFromMMPointInt32(location()); 188 | const CGEventType mouseTypeDown = MMMouseToCGEventType(true, button); 189 | const CGEventType mouseTypeUP = MMMouseToCGEventType(false, button); 190 | 191 | CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); 192 | CGEventRef event = CGEventCreateMouseEvent(source, mouseTypeDown, currentPos, kCGMouseButtonLeft); 193 | 194 | /* Set event to double click. */ 195 | CGEventSetIntegerValueField(event, kCGMouseEventClickState, 2); 196 | CGEventPost(kCGHIDEventTap, event); 197 | 198 | CGEventSetType(event, mouseTypeUP); 199 | CGEventPost(kCGHIDEventTap, event); 200 | 201 | CFRelease(event); 202 | CFRelease(source); 203 | #else 204 | /* Double click for everything else. */ 205 | clickMouse(button); 206 | microsleep(200); 207 | clickMouse(button); 208 | #endif 209 | } 210 | 211 | /* Function used to scroll the screen in the required direction. */ 212 | void scrollMouseXY(int x, int y) { 213 | #if defined(IS_WINDOWS) 214 | // Fix for #97, C89 needs variables declared on top of functions (mouseScrollInput) 215 | INPUT mouseScrollInputH; 216 | INPUT mouseScrollInputV; 217 | #endif 218 | 219 | #if defined(IS_MACOSX) 220 | CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); 221 | CGEventRef event = CGEventCreateScrollWheelEvent(source, kCGScrollEventUnitPixel, 2, y, x); 222 | CGEventPost(kCGHIDEventTap, event); 223 | 224 | CFRelease(event); 225 | CFRelease(source); 226 | #elif defined(USE_X11) 227 | int ydir = 4; /* Button 4 is up, 5 is down. */ 228 | int xdir = 6; 229 | Display *display = XGetMainDisplay(); 230 | 231 | if (y < 0) { ydir = 5; } 232 | if (x < 0) { xdir = 7; } 233 | 234 | int xi; int yi; 235 | for (xi = 0; xi < abs(x); xi++) { 236 | XTestFakeButtonEvent(display, xdir, 1, CurrentTime); 237 | XTestFakeButtonEvent(display, xdir, 0, CurrentTime); 238 | } 239 | for (yi = 0; yi < abs(y); yi++) { 240 | XTestFakeButtonEvent(display, ydir, 1, CurrentTime); 241 | XTestFakeButtonEvent(display, ydir, 0, CurrentTime); 242 | } 243 | 244 | XSync(display, false); 245 | #elif defined(IS_WINDOWS) 246 | mouseScrollInputH.type = INPUT_MOUSE; 247 | mouseScrollInputH.mi.dx = 0; 248 | mouseScrollInputH.mi.dy = 0; 249 | mouseScrollInputH.mi.dwFlags = MOUSEEVENTF_WHEEL; 250 | mouseScrollInputH.mi.time = 0; 251 | mouseScrollInputH.mi.dwExtraInfo = 0; 252 | mouseScrollInputH.mi.mouseData = WHEEL_DELTA * x; 253 | 254 | mouseScrollInputV.type = INPUT_MOUSE; 255 | mouseScrollInputV.mi.dx = 0; 256 | mouseScrollInputV.mi.dy = 0; 257 | mouseScrollInputV.mi.dwFlags = MOUSEEVENTF_WHEEL; 258 | mouseScrollInputV.mi.time = 0; 259 | mouseScrollInputV.mi.dwExtraInfo = 0; 260 | mouseScrollInputV.mi.mouseData = WHEEL_DELTA * y; 261 | 262 | SendInput(1, &mouseScrollInputH, sizeof(mouseScrollInputH)); 263 | SendInput(1, &mouseScrollInputV, sizeof(mouseScrollInputV)); 264 | #endif 265 | } 266 | 267 | /* A crude, fast hypot() approximation to get around the fact that hypot() is not a standard ANSI C function. */ 268 | #if !defined(M_SQRT2) 269 | #define M_SQRT2 1.4142135623730950488016887 /* Fix for MSVC. */ 270 | #endif 271 | 272 | static double crude_hypot(double x, double y){ 273 | double big = fabs(x); /* max(|x|, |y|) */ 274 | double small = fabs(y); /* min(|x|, |y|) */ 275 | 276 | if (big > small) { 277 | double temp = big; 278 | big = small; 279 | small = temp; 280 | } 281 | 282 | return ((M_SQRT2 - 1.0) * small) + big; 283 | } 284 | 285 | bool smoothlyMoveMouse(MMPointInt32 endPoint, double lowSpeed, double highSpeed){ 286 | MMPointInt32 pos = location(); 287 | // MMSizeInt32 screenSize = getMainDisplaySize(); 288 | double velo_x = 0.0, velo_y = 0.0; 289 | double distance; 290 | 291 | while ((distance =crude_hypot((double)pos.x - endPoint.x, (double)pos.y - endPoint.y)) > 1.0) { 292 | double gravity = DEADBEEF_UNIFORM(5.0, 500.0); 293 | // double gravity = DEADBEEF_UNIFORM(lowSpeed, highSpeed); 294 | double veloDistance; 295 | velo_x += (gravity * ((double)endPoint.x - pos.x)) / distance; 296 | velo_y += (gravity * ((double)endPoint.y - pos.y)) / distance; 297 | 298 | /* Normalize velocity to get a unit vector of length 1. */ 299 | veloDistance = crude_hypot(velo_x, velo_y); 300 | velo_x /= veloDistance; 301 | velo_y /= veloDistance; 302 | 303 | pos.x += floor(velo_x + 0.5); 304 | pos.y += floor(velo_y + 0.5); 305 | 306 | /* Make sure we are in the screen boundaries! (Strange things will happen if we are not.) */ 307 | // if (pos.x >= screenSize.w || pos.y >= screenSize.h) { 308 | // return false; 309 | // } 310 | moveMouse(pos); 311 | 312 | /* Wait 1 - 3 milliseconds. */ 313 | microsleep(DEADBEEF_UNIFORM(lowSpeed, highSpeed)); 314 | // microsleep(DEADBEEF_UNIFORM(1.0, 3.0)); 315 | } 316 | 317 | return true; 318 | } 319 | -------------------------------------------------------------------------------- /mouse/mouse_darwin.go: -------------------------------------------------------------------------------- 1 | //go:build darwin 2 | // +build darwin 3 | 4 | package mouse 5 | -------------------------------------------------------------------------------- /mouse/mouse_windows.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | // +build windows 3 | 4 | package mouse 5 | -------------------------------------------------------------------------------- /mouse/mouse_x11.go: -------------------------------------------------------------------------------- 1 | //go:build !darwin && !windows 2 | // +build !darwin,!windows 3 | 4 | package mouse 5 | -------------------------------------------------------------------------------- /ps.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package robotgo 12 | 13 | import ps "github.com/vcaesar/gops" 14 | 15 | // Nps process struct 16 | type Nps struct { 17 | Pid int 18 | Name string 19 | } 20 | 21 | // Pids get the all process id 22 | func Pids() ([]int, error) { 23 | return ps.Pids() 24 | } 25 | 26 | // PidExists determine whether the process exists 27 | func PidExists(pid int) (bool, error) { 28 | return ps.PidExists(pid) 29 | } 30 | 31 | // Process get the all process struct 32 | func Process() ([]Nps, error) { 33 | var npsArr []Nps 34 | nps, err := ps.Process() 35 | for i := 0; i < len(nps); i++ { 36 | np := Nps{ 37 | nps[i].Pid, 38 | nps[i].Name, 39 | } 40 | 41 | npsArr = append(npsArr, np) 42 | } 43 | 44 | return npsArr, err 45 | } 46 | 47 | // FindName find the process name by the process id 48 | func FindName(pid int) (string, error) { 49 | return ps.FindName(pid) 50 | } 51 | 52 | // FindNames find the all process name 53 | func FindNames() ([]string, error) { 54 | return ps.FindNames() 55 | } 56 | 57 | // FindIds finds the all processes named with a subset 58 | // of "name" (case insensitive), 59 | // return matched IDs. 60 | func FindIds(name string) ([]int, error) { 61 | return ps.FindIds(name) 62 | } 63 | 64 | // FindPath find the process path by the process pid 65 | func FindPath(pid int) (string, error) { 66 | return ps.FindPath(pid) 67 | } 68 | 69 | // Run run a cmd shell 70 | func Run(path string) ([]byte, error) { 71 | return ps.Run(path) 72 | } 73 | 74 | // Kill kill the process by PID 75 | func Kill(pid int) error { 76 | return ps.Kill(pid) 77 | } 78 | -------------------------------------------------------------------------------- /robot_info_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package robotgo_test 12 | 13 | import ( 14 | "fmt" 15 | "log" 16 | "runtime" 17 | "testing" 18 | 19 | "github.com/go-vgo/robotgo" 20 | "github.com/vcaesar/tt" 21 | ) 22 | 23 | func TestGetVer(t *testing.T) { 24 | fmt.Println("go version: ", runtime.Version()) 25 | ver := robotgo.GetVersion() 26 | 27 | tt.Expect(t, robotgo.Version, ver) 28 | } 29 | 30 | func TestGetScreenSize(t *testing.T) { 31 | x, y := robotgo.GetScreenSize() 32 | log.Println("Get screen size: ", x, y) 33 | 34 | rect := robotgo.GetScreenRect() 35 | fmt.Println("Get screen rect: ", rect) 36 | 37 | x, y = robotgo.Location() 38 | fmt.Println("Get location: ", x, y) 39 | } 40 | 41 | func TestGetSysScale(t *testing.T) { 42 | s := robotgo.SysScale() 43 | log.Println("SysScale: ", s) 44 | 45 | f := robotgo.ScaleF() 46 | log.Println("scale: ", f) 47 | } 48 | 49 | func TestGetTitle(t *testing.T) { 50 | // just exercise the function, it used to crash with a segfault + "Maximum 51 | // number of clients reached" 52 | for i := 0; i < 128; i++ { 53 | robotgo.GetTitle() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /robotgo_adb.go: -------------------------------------------------------------------------------- 1 | package robotgo 2 | -------------------------------------------------------------------------------- /robotgo_android.go: -------------------------------------------------------------------------------- 1 | package robotgo 2 | -------------------------------------------------------------------------------- /robotgo_fn_v1.go: -------------------------------------------------------------------------------- 1 | package robotgo 2 | 3 | import "github.com/vcaesar/tt" 4 | 5 | // Deprecated: use the Move(), 6 | // 7 | // MoveMouse move the mouse 8 | func MoveMouse(x, y int) { 9 | Move(x, y) 10 | } 11 | 12 | // Deprecated: use the DragSmooth(), 13 | // 14 | // DragMouse drag the mouse to (x, y), 15 | // It's same with the DragSmooth() now 16 | func DragMouse(x, y int, args ...interface{}) { 17 | Toggle("left") 18 | MilliSleep(50) 19 | // Drag(x, y, args...) 20 | MoveSmooth(x, y, args...) 21 | Toggle("left", "up") 22 | } 23 | 24 | // Deprecated: use the MoveSmooth(), 25 | // 26 | // MoveMouseSmooth move the mouse smooth, 27 | // moves mouse to x, y human like, with the mouse button up. 28 | func MoveMouseSmooth(x, y int, args ...interface{}) bool { 29 | return MoveSmooth(x, y, args...) 30 | } 31 | 32 | // Deprecated: use the function Location() 33 | // 34 | // GetMousePos get the mouse's position return x, y 35 | func GetMousePos() (int, int) { 36 | return Location() 37 | } 38 | 39 | // Deprecated: use the Click(), 40 | // 41 | // # MouseClick click the mouse 42 | // 43 | // robotgo.MouseClick(button string, double bool) 44 | func MouseClick(args ...interface{}) { 45 | Click(args...) 46 | } 47 | 48 | // Deprecated: use the TypeStr(), 49 | // 50 | // # TypeStringDelayed type string delayed, Wno-deprecated 51 | // 52 | // This function will be removed in version v1.0.0 53 | func TypeStringDelayed(str string, delay int) { 54 | tt.Drop("TypeStringDelayed", "TypeStrDelay") 55 | TypeStrDelay(str, delay) 56 | } 57 | 58 | // Deprecated: use the ScaledF(), 59 | // 60 | // Scale1 get the screen scale (only windows old), drop 61 | func Scale1() int { 62 | dpi := map[int]int{ 63 | 0: 100, 64 | // DPI Scaling Level 65 | 96: 100, 66 | 120: 125, 67 | 144: 150, 68 | 168: 175, 69 | 192: 200, 70 | 216: 225, 71 | // Custom DPI 72 | 240: 250, 73 | 288: 300, 74 | 384: 400, 75 | 480: 500, 76 | } 77 | 78 | x := ScaleX() 79 | return dpi[x] 80 | } 81 | 82 | // Deprecated: use the ScaledF(), 83 | // 84 | // Scale0 return ScaleX() / 0.96, drop 85 | func Scale0() int { 86 | return int(float64(ScaleX()) / 0.96) 87 | } 88 | 89 | // Deprecated: use the ScaledF(), 90 | // 91 | // Mul mul the scale, drop 92 | func Mul(x int) int { 93 | s := Scale1() 94 | return x * s / 100 95 | } 96 | -------------------------------------------------------------------------------- /robotgo_mac.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //go:build darwin 12 | // +build darwin 13 | 14 | package robotgo 15 | 16 | /* 17 | #include 18 | */ 19 | import "C" 20 | 21 | // GetMainId get the main display id 22 | func GetMainId() int { 23 | return int(C.CGMainDisplayID()) 24 | } 25 | -------------------------------------------------------------------------------- /robotgo_mac_unix.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //go:build !windows 12 | // +build !windows 13 | 14 | package robotgo 15 | 16 | // ScaleF get the system scale val 17 | func ScaleF(displayId ...int) float64 { 18 | f := SysScale(displayId...) 19 | if f == 0.0 { 20 | f = 1.0 21 | } 22 | return f 23 | } 24 | -------------------------------------------------------------------------------- /robotgo_mac_win.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //go:build darwin || windows 12 | // +build darwin windows 13 | 14 | package robotgo 15 | 16 | // GetBounds get the window bounds 17 | func GetBounds(pid int, args ...int) (int, int, int, int) { 18 | var isPid int 19 | if len(args) > 0 || NotPid { 20 | isPid = 1 21 | } 22 | 23 | return internalGetBounds(pid, isPid) 24 | } 25 | 26 | // GetClient get the window client bounds 27 | func GetClient(pid int, args ...int) (int, int, int, int) { 28 | var isPid int 29 | if len(args) > 0 || NotPid { 30 | isPid = 1 31 | } 32 | 33 | return internalGetClient(pid, isPid) 34 | } 35 | 36 | // internalGetTitle get the window title 37 | func internalGetTitle(pid int, args ...int) string { 38 | var isPid int 39 | if len(args) > 0 || NotPid { 40 | isPid = 1 41 | } 42 | gtitle := cgetTitle(pid, isPid) 43 | 44 | return gtitle 45 | } 46 | 47 | // ActivePid active the window by PID, 48 | // 49 | // If args[0] > 0 on the Windows platform via a window handle to active 50 | // 51 | // Examples: 52 | // 53 | // ids, _ := robotgo.FindIds() 54 | // robotgo.ActivePid(ids[0]) 55 | func ActivePid(pid int, args ...int) error { 56 | var isPid int 57 | if len(args) > 0 || NotPid { 58 | isPid = 1 59 | } 60 | 61 | internalActive(pid, isPid) 62 | return nil 63 | } 64 | 65 | // DisplaysNum get the count of displays 66 | func DisplaysNum() int { 67 | return getNumDisplays() 68 | } 69 | 70 | // Alert show a alert window 71 | // Displays alert with the attributes. 72 | // If cancel button is not given, only the default button is displayed 73 | // 74 | // Examples: 75 | // 76 | // robotgo.Alert("hi", "window", "ok", "cancel") 77 | func Alert(title, msg string, args ...string) bool { 78 | return showAlert(title, msg, args...) 79 | } 80 | -------------------------------------------------------------------------------- /robotgo_ocr.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //go:build ocr 12 | // +build ocr 13 | 14 | package robotgo 15 | 16 | import ( 17 | "github.com/otiai10/gosseract" 18 | ) 19 | 20 | // GetText get the image text by tesseract ocr 21 | func GetText(imgPath string, args ...string) (string, error) { 22 | var lang = "eng" 23 | 24 | if len(args) > 0 { 25 | lang = args[0] 26 | if lang == "zh" { 27 | lang = "chi_sim" 28 | } 29 | } 30 | 31 | client := gosseract.NewClient() 32 | defer client.Close() 33 | 34 | client.SetImage(imgPath) 35 | client.SetLanguage(lang) 36 | return client.Text() 37 | } 38 | -------------------------------------------------------------------------------- /robotgo_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //go:build darwin || windows 12 | // +build darwin windows 13 | 14 | package robotgo 15 | 16 | import ( 17 | "testing" 18 | 19 | "github.com/vcaesar/tt" 20 | ) 21 | 22 | func TestColor(t *testing.T) { 23 | s := GetPixelColor(10, 10) 24 | tt.IsType(t, "string", s) 25 | tt.NotEmpty(t, s) 26 | 27 | c := GetPxColor(10, 10) 28 | s1 := PadHex(c) 29 | tt.Equal(t, s, s1) 30 | } 31 | 32 | func TestSize(t *testing.T) { 33 | x, y := GetScreenSize() 34 | tt.NotZero(t, x) 35 | tt.NotZero(t, y) 36 | 37 | x, y = GetScaleSize() 38 | tt.NotZero(t, x) 39 | tt.NotZero(t, y) 40 | } 41 | 42 | func TestMoveMouse(t *testing.T) { 43 | Move(20, 20) 44 | MilliSleep(50) 45 | x, y := Location() 46 | 47 | tt.Equal(t, 20, x) 48 | tt.Equal(t, 20, y) 49 | } 50 | 51 | func TestMoveMouseSmooth(t *testing.T) { 52 | b := MoveSmooth(100, 100) 53 | MilliSleep(50) 54 | x, y := Location() 55 | 56 | tt.True(t, b) 57 | tt.Equal(t, 100, x) 58 | tt.Equal(t, 100, y) 59 | } 60 | 61 | func TestDragMouse(t *testing.T) { 62 | DragSmooth(500, 500) 63 | MilliSleep(50) 64 | x, y := Location() 65 | 66 | tt.Equal(t, 500, x) 67 | tt.Equal(t, 500, y) 68 | } 69 | 70 | func TestScrollMouse(t *testing.T) { 71 | ScrollDir(120, "up") 72 | ScrollDir(100, "right") 73 | 74 | Scroll(0, 120) 75 | MilliSleep(100) 76 | 77 | Scroll(210, 210) 78 | MilliSleep(10) 79 | } 80 | 81 | func TestMoveRelative(t *testing.T) { 82 | Move(200, 200) 83 | MilliSleep(50) 84 | 85 | MoveRelative(10, -10) 86 | MilliSleep(50) 87 | 88 | x, y := Location() 89 | tt.Equal(t, 210, x) 90 | tt.Equal(t, 190, y) 91 | } 92 | 93 | func TestMoveSmoothRelative(t *testing.T) { 94 | Move(200, 200) 95 | MilliSleep(50) 96 | 97 | MoveSmoothRelative(10, -10) 98 | MilliSleep(50) 99 | 100 | x, y := Location() 101 | tt.Equal(t, 210, x) 102 | tt.Equal(t, 190, y) 103 | } 104 | 105 | func TestMouseToggle(t *testing.T) { 106 | e := Toggle("right") 107 | tt.Nil(t, e) 108 | 109 | e = Toggle("right", "up") 110 | tt.Nil(t, e) 111 | 112 | e = MouseDown("left") 113 | tt.Nil(t, e) 114 | 115 | e = MouseUp("left") 116 | tt.Nil(t, e) 117 | } 118 | 119 | func TestKey(t *testing.T) { 120 | e := KeyTap("v", "cmd") 121 | tt.Nil(t, e) 122 | 123 | e = KeyTap("enter") 124 | tt.Nil(t, e) 125 | 126 | e = KeyToggle("v", "up") 127 | tt.Nil(t, e) 128 | 129 | e = KeyDown("a") 130 | tt.Nil(t, e) 131 | e = KeyUp("a") 132 | tt.Nil(t, e) 133 | 134 | e = KeyPress("b") 135 | tt.Nil(t, e) 136 | } 137 | 138 | func TestClip(t *testing.T) { 139 | err := WriteAll("s") 140 | tt.Nil(t, err) 141 | 142 | s, e := ReadAll() 143 | tt.Equal(t, "s", s) 144 | tt.Nil(t, e) 145 | } 146 | 147 | func TestTypeStr(t *testing.T) { 148 | c := CharCodeAt("s", 0) 149 | tt.Equal(t, 115, c) 150 | 151 | e := PasteStr("s") 152 | tt.Nil(t, e) 153 | 154 | s1 := "abc\\\\cd/s@世界" 155 | uc := ToUC(s1) 156 | tt.Equal(t, "[a b c \\ \\ c d / s @ U4e16 U754c]", uc) 157 | } 158 | 159 | func TestKeyCode(t *testing.T) { 160 | m := MouseMap["left"] 161 | tt.Equal(t, 1, m) 162 | 163 | k := Keycode["1"] 164 | tt.Equal(t, 2, k) 165 | 166 | s := Special["+"] 167 | tt.Equal(t, "=", s) 168 | 169 | tt.Equal(t, "0", Key0) 170 | tt.Equal(t, "a", KeyA) 171 | } 172 | 173 | func TestImage(t *testing.T) { 174 | bit := CaptureScreen() 175 | defer FreeBitmap(bit) 176 | tt.NotNil(t, bit) 177 | 178 | img := ToImage(bit) 179 | err := SavePng(img, "robot_test.png") 180 | tt.Nil(t, err) 181 | 182 | img1, err := CaptureImg(10, 10, 20, 20) 183 | tt.Nil(t, err) 184 | e := Save(img1, "robot_img.jpeg", 50) 185 | tt.Nil(t, e) 186 | 187 | tt.Equal(t, 20, Width(img1)) 188 | tt.Equal(t, 20, Height(img1)) 189 | 190 | bit1 := ImgToBitmap(img1) 191 | tt.Equal(t, bit1.Width, Width(img1)) 192 | tt.Equal(t, bit1.Height, Height(img1)) 193 | } 194 | 195 | func TestPs(t *testing.T) { 196 | id, err := Pids() 197 | tt.Not(t, "[]", id) 198 | tt.IsType(t, "[]int", id) 199 | tt.Nil(t, err) 200 | 201 | ps, e := Process() 202 | tt.Not(t, "[]", ps) 203 | tt.IsType(t, "[]robotgo.Nps", ps) 204 | tt.Nil(t, e) 205 | 206 | b, e := PidExists(id[0]) 207 | tt.Bool(t, b) 208 | tt.Nil(t, e) 209 | 210 | n, e := FindName(id[0]) 211 | tt.NotEmpty(t, n) 212 | tt.Nil(t, e) 213 | 214 | n1, e := FindNames() 215 | tt.Not(t, "[]", n1) 216 | tt.IsType(t, "[]string", n1) 217 | tt.Nil(t, e) 218 | 219 | id, err = FindIds(n1[0]) 220 | tt.Not(t, "[]", id) 221 | tt.IsType(t, "[]int", id) 222 | tt.Nil(t, err) 223 | 224 | if len(id) > 0 { 225 | e := KeyTap("v", id[0], "cmd") 226 | tt.Nil(t, e) 227 | } 228 | 229 | // n, e = FindPath(id[0]) 230 | // tt.NotEmpty(t, n) 231 | // tt.Nil(t, e) 232 | } 233 | 234 | // func TestAlert(t *testing.T) { 235 | // go func() { 236 | // MilliSleep(200) 237 | // KeyTap("enter") 238 | // log.Println("tap...") 239 | // }() 240 | 241 | // i := Alert("t", "msg") 242 | // tt.True(t, i) 243 | // } 244 | -------------------------------------------------------------------------------- /robotgo_win.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //go:build windows 12 | // +build windows 13 | 14 | package robotgo 15 | 16 | import ( 17 | "syscall" 18 | "unsafe" 19 | 20 | // "github.com/lxn/win" 21 | "github.com/tailscale/win" 22 | ) 23 | 24 | // FindWindow find window hwnd by name 25 | func FindWindow(name string) win.HWND { 26 | hwnd := win.FindWindow(nil, syscall.StringToUTF16Ptr(name)) 27 | return hwnd 28 | } 29 | 30 | // GetHWND get foreground window hwnd 31 | func GetHWND() win.HWND { 32 | hwnd := win.GetForegroundWindow() 33 | return hwnd 34 | } 35 | 36 | // SendInput send n input event 37 | func SendInput(nInputs uint32, pInputs unsafe.Pointer, cbSize int32) uint32 { 38 | return win.SendInput(nInputs, pInputs, cbSize) 39 | } 40 | 41 | // SendMsg send a message with hwnd 42 | func SendMsg(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr { 43 | return win.SendMessage(hwnd, msg, wParam, lParam) 44 | } 45 | 46 | // SetActiveWindow set window active with hwnd 47 | func SetActiveWindow(hwnd win.HWND) win.HWND { 48 | return win.SetActiveWindow(hwnd) 49 | } 50 | 51 | // SetFocus set window focus with hwnd 52 | func SetFocus(hwnd win.HWND) win.HWND { 53 | return win.SetFocus(hwnd) 54 | } 55 | 56 | // SetForeg set the window into the foreground by hwnd 57 | func SetForeg(hwnd win.HWND) bool { 58 | return win.SetForegroundWindow(hwnd) 59 | } 60 | 61 | // GetMain get the main display hwnd 62 | func GetMain() win.HWND { 63 | return win.GetActiveWindow() 64 | } 65 | 66 | // GetMainId get the main display id 67 | func GetMainId() int { 68 | return int(GetMain()) 69 | } 70 | 71 | // ScaleF get the system scale value 72 | // if "displayId == -2" this function will get the desktop scale value 73 | func ScaleF(displayId ...int) (f float64) { 74 | if len(displayId) > 0 && displayId[0] != -1 { 75 | if displayId[0] >= 0 { 76 | dpi := GetDPI(win.HWND(displayId[0])) 77 | f = float64(dpi) / 96.0 78 | } 79 | 80 | if displayId[0] == -2 { 81 | f = float64(GetDPI(GetDesktopWindow())) / 96.0 82 | } 83 | } else { 84 | f = float64(GetMainDPI()) / 96.0 85 | } 86 | 87 | if f == 0.0 { 88 | f = 1.0 89 | } 90 | return f 91 | } 92 | 93 | // GetDesktopWindow get the desktop window hwnd id 94 | func GetDesktopWindow() win.HWND { 95 | return win.GetDesktopWindow() 96 | } 97 | 98 | // GetMainDPI get the display dpi 99 | func GetMainDPI() int { 100 | return int(GetDPI(GetHWND())) 101 | } 102 | 103 | // GetDPI get the window dpi 104 | func GetDPI(hwnd win.HWND) uint32 { 105 | return win.GetDpiForWindow(hwnd) 106 | } 107 | 108 | // GetSysDPI get the system metrics dpi 109 | func GetSysDPI(idx int32, dpi uint32) int32 { 110 | return win.GetSystemMetricsForDpi(idx, dpi) 111 | } 112 | -------------------------------------------------------------------------------- /robotgo_x11.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //go:build !darwin && !windows 12 | // +build !darwin,!windows 13 | 14 | package robotgo 15 | 16 | import ( 17 | "errors" 18 | "log" 19 | 20 | "github.com/robotn/xgb" 21 | "github.com/robotn/xgb/xinerama" 22 | "github.com/robotn/xgb/xproto" 23 | "github.com/robotn/xgbutil" 24 | "github.com/robotn/xgbutil/ewmh" 25 | ) 26 | 27 | var xu *xgbutil.XUtil 28 | 29 | // GetBounds get the window bounds 30 | func GetBounds(pid int, args ...int) (int, int, int, int) { 31 | var isPid int 32 | if len(args) > 0 || NotPid { 33 | isPid = 1 34 | return internalGetBounds(pid, isPid) 35 | } 36 | 37 | xid, err := GetXid(xu, pid) 38 | if err != nil { 39 | log.Println("Get Xid from Pid errors is: ", err) 40 | return 0, 0, 0, 0 41 | } 42 | 43 | return internalGetBounds(int(xid), isPid) 44 | } 45 | 46 | // GetClient get the window client bounds 47 | func GetClient(pid int, args ...int) (int, int, int, int) { 48 | var isPid int 49 | if len(args) > 0 || NotPid { 50 | isPid = 1 51 | return internalGetClient(pid, isPid) 52 | } 53 | 54 | xid, err := GetXid(xu, pid) 55 | if err != nil { 56 | log.Println("Get Xid from Pid errors is: ", err) 57 | return 0, 0, 0, 0 58 | } 59 | 60 | return internalGetClient(int(xid), isPid) 61 | } 62 | 63 | // internalGetTitle get the window title 64 | func internalGetTitle(pid int, args ...int) string { 65 | var isPid int 66 | if len(args) > 0 || NotPid { 67 | isPid = 1 68 | return cgetTitle(pid, isPid) 69 | } 70 | 71 | xid, err := GetXid(xu, pid) 72 | if err != nil { 73 | log.Println("Get Xid from Pid errors is: ", err) 74 | return "" 75 | } 76 | 77 | return cgetTitle(int(xid), isPid) 78 | } 79 | 80 | // ActivePidC active the window by Pid, 81 | // If args[0] > 0 on the unix platform via a xid to active 82 | func ActivePidC(pid int, args ...int) error { 83 | var isPid int 84 | if len(args) > 0 || NotPid { 85 | isPid = 1 86 | internalActive(pid, isPid) 87 | return nil 88 | } 89 | 90 | xid, err := GetXid(xu, pid) 91 | if err != nil { 92 | log.Println("Get Xid from Pid errors is: ", err) 93 | return err 94 | } 95 | 96 | internalActive(int(xid), isPid) 97 | return nil 98 | } 99 | 100 | // ActivePid active the window by Pid, 101 | // 102 | // If args[0] > 0 on the Windows platform via a window handle to active, 103 | // If args[0] > 0 on the unix platform via a xid to active 104 | func ActivePid(pid int, args ...int) error { 105 | if xu == nil { 106 | var err error 107 | xu, err = xgbutil.NewConn() 108 | if err != nil { 109 | return err 110 | } 111 | } 112 | 113 | if len(args) > 0 { 114 | err := ewmh.ActiveWindowReq(xu, xproto.Window(pid)) 115 | if err != nil { 116 | return err 117 | } 118 | 119 | return nil 120 | } 121 | 122 | // get the xid from pid 123 | xid, err := GetXidFromPid(xu, pid) 124 | if err != nil { 125 | return err 126 | } 127 | 128 | err = ewmh.ActiveWindowReq(xu, xid) 129 | if err != nil { 130 | return err 131 | } 132 | 133 | return nil 134 | } 135 | 136 | // GetXid get the xid return window and error 137 | func GetXid(xu *xgbutil.XUtil, pid int) (xproto.Window, error) { 138 | if xu == nil { 139 | var err error 140 | xu, err = xgbutil.NewConn() 141 | if err != nil { 142 | // log.Println("xgbutil.NewConn errors is: ", err) 143 | return 0, err 144 | } 145 | } 146 | 147 | xid, err := GetXidFromPid(xu, pid) 148 | return xid, err 149 | } 150 | 151 | // GetXidFromPid get the xid from pid 152 | func GetXidFromPid(xu *xgbutil.XUtil, pid int) (xproto.Window, error) { 153 | windows, err := ewmh.ClientListGet(xu) 154 | if err != nil { 155 | return 0, err 156 | } 157 | 158 | for _, window := range windows { 159 | wmPid, err := ewmh.WmPidGet(xu, window) 160 | if err != nil { 161 | return 0, err 162 | } 163 | 164 | if uint(pid) == wmPid { 165 | return window, nil 166 | } 167 | } 168 | 169 | return 0, errors.New("failed to find a window with a matching pid.") 170 | } 171 | 172 | // DisplaysNum get the count of displays 173 | func DisplaysNum() int { 174 | c, err := xgb.NewConn() 175 | if err != nil { 176 | return 0 177 | } 178 | defer c.Close() 179 | 180 | err = xinerama.Init(c) 181 | if err != nil { 182 | return 0 183 | } 184 | 185 | reply, err := xinerama.QueryScreens(c).Reply() 186 | if err != nil { 187 | return 0 188 | } 189 | 190 | return int(reply.Number) 191 | } 192 | 193 | // GetMainId get the main display id 194 | func GetMainId() int { 195 | conn, err := xgb.NewConn() 196 | if err != nil { 197 | return -1 198 | } 199 | 200 | setup := xproto.Setup(conn) 201 | defaultScreen := setup.DefaultScreen(conn) 202 | id := -1 203 | for i, screen := range setup.Roots { 204 | if defaultScreen.Root == screen.Root { 205 | id = i 206 | break 207 | } 208 | } 209 | return id 210 | } 211 | 212 | // Alert show a alert window 213 | // Displays alert with the attributes. 214 | // If cancel button is not given, only the default button is displayed 215 | // 216 | // Examples: 217 | // 218 | // robotgo.Alert("hi", "window", "ok", "cancel") 219 | func Alert(title, msg string, args ...string) bool { 220 | defaultBtn, cancelBtn := alertArgs(args...) 221 | c := `xmessage -center ` + msg + 222 | ` -title ` + title + ` -buttons ` + defaultBtn + ":0," 223 | if cancelBtn != "" { 224 | c += cancelBtn + ":1" 225 | } 226 | c += ` -default ` + defaultBtn 227 | c += ` -geometry 400x200` 228 | 229 | out, err := Run(c) 230 | if err != nil { 231 | // fmt.Println("Alert: ", err, ". ", string(out)) 232 | return false 233 | } 234 | 235 | if string(out) == "1" { 236 | return false 237 | } 238 | return true 239 | } 240 | -------------------------------------------------------------------------------- /screen.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package robotgo 12 | 13 | import ( 14 | "image" 15 | 16 | // "github.com/kbinani/screenshot" 17 | "github.com/vcaesar/screenshot" 18 | ) 19 | 20 | // GetDisplayBounds gets the display screen bounds 21 | func GetDisplayBounds(i int) (x, y, w, h int) { 22 | bs := screenshot.GetDisplayBounds(i) 23 | return bs.Min.X, bs.Min.Y, bs.Dx(), bs.Dy() 24 | } 25 | 26 | // GetDisplayRect gets the display rect 27 | func GetDisplayRect(i int) Rect { 28 | x, y, w, h := GetDisplayBounds(i) 29 | return Rect{ 30 | Point{X: x, Y: y}, 31 | Size{W: w, H: h}} 32 | } 33 | 34 | // Capture capture the screenshot, use the CaptureImg default 35 | func Capture(args ...int) (*image.RGBA, error) { 36 | displayId := 0 37 | if DisplayID != -1 { 38 | displayId = DisplayID 39 | } 40 | 41 | if len(args) > 4 { 42 | displayId = args[4] 43 | } 44 | 45 | var x, y, w, h int 46 | if len(args) > 3 { 47 | x, y, w, h = args[0], args[1], args[2], args[3] 48 | } else { 49 | x, y, w, h = GetDisplayBounds(displayId) 50 | } 51 | 52 | return screenshot.Capture(x, y, w, h) 53 | } 54 | 55 | // SaveCapture capture screen and save the screenshot to image 56 | func SaveCapture(path string, args ...int) error { 57 | img, err := CaptureImg(args...) 58 | if err != nil { 59 | return err 60 | } 61 | 62 | return Save(img, path) 63 | } 64 | -------------------------------------------------------------------------------- /screen/goScreen.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | #include "../base/types.h" 12 | #include "../base/pubs.h" 13 | #include "../base/rgb.h" 14 | #include "screengrab_c.h" 15 | #include 16 | 17 | void padHex(MMRGBHex color, char* hex) { 18 | // Length needs to be 7 because snprintf includes a terminating null. 19 | snprintf(hex, 7, "%06x", color); 20 | } 21 | 22 | char* pad_hex(MMRGBHex color) { 23 | char hex[7]; 24 | padHex(color, hex); 25 | // destroyMMBitmap(bitmap); 26 | 27 | char* str = (char*)calloc(100, sizeof(char*)); 28 | if (str) { strcpy(str, hex); } 29 | return str; 30 | } 31 | 32 | static uint8_t rgb[3]; 33 | 34 | uint8_t* color_hex_to_rgb(uint32_t h) { 35 | rgb[0] = RED_FROM_HEX(h); 36 | rgb[1] = GREEN_FROM_HEX(h); 37 | rgb[2] = BLUE_FROM_HEX(h); 38 | return rgb; 39 | } 40 | 41 | uint32_t color_rgb_to_hex(uint8_t r, uint8_t g, uint8_t b) { 42 | return RGB_TO_HEX(r, g, b); 43 | } 44 | 45 | MMRGBHex get_px_color(int32_t x, int32_t y, int32_t display_id) { 46 | MMBitmapRef bitmap; 47 | MMRGBHex color; 48 | 49 | if (!pointVisibleOnMainDisplay(MMPointInt32Make(x, y))) { 50 | return color; 51 | } 52 | 53 | bitmap = copyMMBitmapFromDisplayInRect(MMRectInt32Make(x, y, 1, 1), display_id, 0); 54 | color = MMRGBHexAtPoint(bitmap, 0, 0); 55 | destroyMMBitmap(bitmap); 56 | 57 | return color; 58 | } 59 | 60 | char* set_XDisplay_name(char* name) { 61 | #if defined(USE_X11) 62 | setXDisplay(name); 63 | return ""; 64 | #else 65 | return "SetXDisplayName is only supported on Linux"; 66 | #endif 67 | } 68 | 69 | char* get_XDisplay_name() { 70 | #if defined(USE_X11) 71 | const char* display = getXDisplay(); 72 | 73 | char* sd = (char*)calloc(100, sizeof(char*)); 74 | if (sd) { strcpy(sd, display); } 75 | return sd; 76 | #else 77 | return "GetXDisplayName is only supported on Linux"; 78 | #endif 79 | } 80 | 81 | void close_main_display() { 82 | #if defined(USE_X11) 83 | XCloseMainDisplay(); 84 | #else 85 | // 86 | #endif 87 | } 88 | 89 | uint32_t get_num_displays() { 90 | #if defined(IS_MACOSX) 91 | uint32_t count = 0; 92 | if (CGGetActiveDisplayList(0, nil, &count) == kCGErrorSuccess) { 93 | return count; 94 | } 95 | return 0; 96 | #elif defined(USE_X11) 97 | return 0; 98 | #elif defined(IS_WINDOWS) 99 | uint32_t count = 0; 100 | if (EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&count)) { 101 | return count; 102 | } 103 | return 0; 104 | #endif 105 | } 106 | 107 | uintptr get_hwnd_by_pid(uintptr pid) { 108 | #if defined(IS_WINDOWS) 109 | HWND hwnd = GetHwndByPid(pid); 110 | return (uintptr)hwnd; 111 | #else 112 | return 0; 113 | #endif 114 | } 115 | 116 | void bitmap_dealloc(MMBitmapRef bitmap) { 117 | if (bitmap != NULL) { 118 | destroyMMBitmap(bitmap); 119 | bitmap = NULL; 120 | } 121 | } 122 | 123 | // capture_screen capture screen 124 | MMBitmapRef capture_screen(int32_t x, int32_t y, int32_t w, int32_t h, int32_t display_id, int8_t isPid) { 125 | MMBitmapRef bitmap = copyMMBitmapFromDisplayInRect(MMRectInt32Make(x, y, w, h), display_id, isPid); 126 | return bitmap; 127 | } 128 | 129 | -------------------------------------------------------------------------------- /screen/screen.go: -------------------------------------------------------------------------------- 1 | package screen 2 | -------------------------------------------------------------------------------- /screen/screen_c.h: -------------------------------------------------------------------------------- 1 | //#include "../base/os.h" 2 | 3 | #if defined(IS_MACOSX) 4 | #include 5 | #elif defined(USE_X11) 6 | #include 7 | #include 8 | // #include "../base/xdisplay_c.h" 9 | #endif 10 | 11 | intptr scaleX(); 12 | 13 | double sys_scale(int32_t display_id) { 14 | #if defined(IS_MACOSX) 15 | CGDirectDisplayID displayID = (CGDirectDisplayID) display_id; 16 | if (displayID == -1) { 17 | displayID = CGMainDisplayID(); 18 | } 19 | 20 | CGDisplayModeRef modeRef = CGDisplayCopyDisplayMode(displayID); 21 | double pixelWidth = CGDisplayModeGetPixelWidth(modeRef); 22 | double targetWidth = CGDisplayModeGetWidth(modeRef); 23 | 24 | return pixelWidth / targetWidth; 25 | #elif defined(USE_X11) 26 | Display *dpy = XOpenDisplay(NULL); 27 | 28 | int scr = 0; /* Screen number */ 29 | double xres = ((((double) DisplayWidth(dpy, scr)) * 25.4) / 30 | ((double) DisplayWidthMM(dpy, scr))); 31 | 32 | char *rms = XResourceManagerString(dpy); 33 | if (rms) { 34 | XrmDatabase db = XrmGetStringDatabase(rms); 35 | if (db) { 36 | XrmValue value; 37 | char *type = NULL; 38 | 39 | if (XrmGetResource(db, "Xft.dpi", "String", &type, &value)) { 40 | if (value.addr) { 41 | xres = atof(value.addr); 42 | } 43 | } 44 | 45 | XrmDestroyDatabase(db); 46 | } 47 | } 48 | XCloseDisplay (dpy); 49 | 50 | return xres / 96.0; 51 | #elif defined(IS_WINDOWS) 52 | double s = scaleX() / 96.0; 53 | return s; 54 | #endif 55 | } 56 | 57 | intptr scaleX(){ 58 | #if defined(IS_MACOSX) 59 | return 0; 60 | #elif defined(USE_X11) 61 | return 0; 62 | #elif defined(IS_WINDOWS) 63 | // Get desktop dc 64 | HDC desktopDc = GetDC(NULL); 65 | // Get native resolution 66 | intptr horizontalDPI = GetDeviceCaps(desktopDc, LOGPIXELSX); 67 | return horizontalDPI; 68 | #endif 69 | } 70 | 71 | MMSizeInt32 getMainDisplaySize(void) { 72 | #if defined(IS_MACOSX) 73 | CGDirectDisplayID displayID = CGMainDisplayID(); 74 | CGRect displayRect = CGDisplayBounds(displayID); 75 | 76 | CGSize size = displayRect.size; 77 | return MMSizeInt32Make((int32_t)size.width, (int32_t)size.height); 78 | #elif defined(USE_X11) 79 | Display *display = XGetMainDisplay(); 80 | const int screen = DefaultScreen(display); 81 | 82 | return MMSizeInt32Make( 83 | (int32_t)DisplayWidth(display, screen), 84 | (int32_t)DisplayHeight(display, screen)); 85 | #elif defined(IS_WINDOWS) 86 | return MMSizeInt32Make( 87 | (int32_t)GetSystemMetrics(SM_CXSCREEN), 88 | (int32_t)GetSystemMetrics(SM_CYSCREEN)); 89 | #endif 90 | } 91 | 92 | MMRectInt32 getScreenRect(int32_t display_id) { 93 | #if defined(IS_MACOSX) 94 | CGDirectDisplayID displayID = (CGDirectDisplayID) display_id; 95 | if (display_id == -1) { 96 | displayID = CGMainDisplayID(); 97 | } 98 | CGRect displayRect = CGDisplayBounds(displayID); 99 | 100 | CGPoint point = displayRect.origin; 101 | CGSize size = displayRect.size; 102 | return MMRectInt32Make( 103 | (int32_t)point.x, (int32_t)point.y, 104 | (int32_t)size.width, (int32_t)size.height); 105 | #elif defined(USE_X11) 106 | Display *display = XGetMainDisplay(); 107 | const int screen = DefaultScreen(display); 108 | 109 | return MMRectInt32Make( 110 | (int32_t)0, (int32_t)0, 111 | (int32_t)DisplayWidth(display, screen), 112 | (int32_t)DisplayHeight(display, screen)); 113 | #elif defined(IS_WINDOWS) 114 | if (GetSystemMetrics(SM_CMONITORS) == 1 115 | || display_id == -1 || display_id == 0) { 116 | return MMRectInt32Make( 117 | (int32_t)0, 118 | (int32_t)0, 119 | (int32_t)GetSystemMetrics(SM_CXSCREEN), 120 | (int32_t)GetSystemMetrics(SM_CYSCREEN)); 121 | } else { 122 | return MMRectInt32Make( 123 | (int32_t)GetSystemMetrics(SM_XVIRTUALSCREEN), 124 | (int32_t)GetSystemMetrics(SM_YVIRTUALSCREEN), 125 | (int32_t)GetSystemMetrics(SM_CXVIRTUALSCREEN), 126 | (int32_t)GetSystemMetrics(SM_CYVIRTUALSCREEN)); 127 | } 128 | #endif 129 | } 130 | 131 | bool pointVisibleOnMainDisplay(MMPointInt32 point){ 132 | MMSizeInt32 displaySize = getMainDisplaySize(); 133 | return point.x < displaySize.w && point.y < displaySize.h; 134 | } 135 | -------------------------------------------------------------------------------- /screen/screengrab_c.h: -------------------------------------------------------------------------------- 1 | #include "../base/bitmap_free_c.h" 2 | #include /* malloc() */ 3 | 4 | #if defined(IS_MACOSX) 5 | #include 6 | #include 7 | #include 8 | #include 9 | #elif defined(USE_X11) 10 | #include 11 | #include 12 | #include "../base/xdisplay_c.h" 13 | #elif defined(IS_WINDOWS) 14 | #include 15 | #endif 16 | #include "screen_c.h" 17 | 18 | #if defined(IS_MACOSX) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 140400 19 | static CGImageRef capture15(CGDirectDisplayID id, CGRect diIntersectDisplayLocal, CGColorSpaceRef colorSpace) { 20 | dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 21 | __block CGImageRef image1 = nil; 22 | [SCShareableContent getShareableContentWithCompletionHandler:^(SCShareableContent* content, NSError* error) { 23 | @autoreleasepool { 24 | if (error) { 25 | dispatch_semaphore_signal(semaphore); 26 | return; 27 | } 28 | 29 | SCDisplay* target = nil; 30 | for (SCDisplay *display in content.displays) { 31 | if (display.displayID == id) { 32 | target = display; 33 | break; 34 | } 35 | } 36 | if (!target) { 37 | dispatch_semaphore_signal(semaphore); 38 | return; 39 | } 40 | 41 | SCContentFilter* filter = [[SCContentFilter alloc] initWithDisplay:target excludingWindows:@[]]; 42 | SCStreamConfiguration* config = [[SCStreamConfiguration alloc] init]; 43 | config.queueDepth = 5; 44 | config.sourceRect = diIntersectDisplayLocal; 45 | config.width = diIntersectDisplayLocal.size.width * sys_scale(id); 46 | config.height = diIntersectDisplayLocal.size.height * sys_scale(id); 47 | config.scalesToFit = false; 48 | config.captureResolution = 1; 49 | 50 | [SCScreenshotManager captureImageWithFilter:filter 51 | configuration:config 52 | completionHandler:^(CGImageRef img, NSError* error) { 53 | if (!error) { 54 | image1 = CGImageCreateCopyWithColorSpace(img, colorSpace); 55 | } 56 | dispatch_semaphore_signal(semaphore); 57 | }]; 58 | } 59 | }]; 60 | 61 | dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 62 | dispatch_release(semaphore); 63 | return image1; 64 | } 65 | #endif 66 | 67 | MMBitmapRef copyMMBitmapFromDisplayInRect(MMRectInt32 rect, int32_t display_id, int8_t isPid) { 68 | #if defined(IS_MACOSX) 69 | MMBitmapRef bitmap = NULL; 70 | uint8_t *buffer = NULL; 71 | size_t bufferSize = 0; 72 | 73 | CGDirectDisplayID displayID = (CGDirectDisplayID) display_id; 74 | if (displayID == -1 || displayID == 0) { 75 | displayID = CGMainDisplayID(); 76 | } 77 | 78 | MMPointInt32 o = rect.origin; MMSizeInt32 s = rect.size; 79 | #if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 140400 80 | CGColorSpaceRef color = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); 81 | CGImageRef image = capture15(displayID, CGRectMake(o.x, o.y, s.w, s.h), color); 82 | CGColorSpaceRelease(color); 83 | #else 84 | // This API is deprecated in macos 15, use ScreenCaptureKit's captureScreenshot 85 | CGImageRef image = CGDisplayCreateImageForRect(displayID, CGRectMake(o.x, o.y, s.w, s.h)); 86 | #endif 87 | if (!image) { return NULL; } 88 | 89 | CFDataRef imageData = CGDataProviderCopyData(CGImageGetDataProvider(image)); 90 | if (!imageData) { return NULL; } 91 | 92 | bufferSize = CFDataGetLength(imageData); 93 | buffer = malloc(bufferSize); 94 | CFDataGetBytes(imageData, CFRangeMake(0, bufferSize), buffer); 95 | 96 | bitmap = createMMBitmap_c(buffer, 97 | CGImageGetWidth(image), CGImageGetHeight(image), CGImageGetBytesPerRow(image), 98 | CGImageGetBitsPerPixel(image), CGImageGetBitsPerPixel(image) / 8); 99 | 100 | CFRelease(imageData); 101 | CGImageRelease(image); 102 | 103 | return bitmap; 104 | #elif defined(USE_X11) 105 | MMBitmapRef bitmap; 106 | Display *display; 107 | if (display_id == -1) { 108 | display = XOpenDisplay(NULL); 109 | } else { 110 | display = XGetMainDisplay(); 111 | } 112 | 113 | MMPointInt32 o = rect.origin; MMSizeInt32 s = rect.size; 114 | XImage *image = XGetImage(display, XDefaultRootWindow(display), 115 | (int)o.x, (int)o.y, (unsigned int)s.w, (unsigned int)s.h, 116 | AllPlanes, ZPixmap); 117 | XCloseDisplay(display); 118 | if (image == NULL) { return NULL; } 119 | 120 | bitmap = createMMBitmap_c((uint8_t *)image->data, 121 | s.w, s.h, (size_t)image->bytes_per_line, 122 | (uint8_t)image->bits_per_pixel, (uint8_t)image->bits_per_pixel / 8); 123 | image->data = NULL; /* Steal ownership of bitmap data so we don't have to copy it. */ 124 | XDestroyImage(image); 125 | 126 | return bitmap; 127 | #elif defined(IS_WINDOWS) 128 | MMBitmapRef bitmap; 129 | void *data; 130 | HDC screen = NULL, screenMem = NULL; 131 | HBITMAP dib; 132 | BITMAPINFO bi; 133 | 134 | int32_t x = rect.origin.x, y = rect.origin.y; 135 | int32_t w = rect.size.w, h = rect.size.h; 136 | 137 | /* Initialize bitmap info. */ 138 | bi.bmiHeader.biSize = sizeof(bi.bmiHeader); 139 | bi.bmiHeader.biWidth = (long) w; 140 | bi.bmiHeader.biHeight = -(long) h; /* Non-cartesian, please */ 141 | bi.bmiHeader.biPlanes = 1; 142 | bi.bmiHeader.biBitCount = 32; 143 | bi.bmiHeader.biCompression = BI_RGB; 144 | bi.bmiHeader.biSizeImage = (DWORD)(4 * w * h); 145 | bi.bmiHeader.biXPelsPerMeter = 0; 146 | bi.bmiHeader.biYPelsPerMeter = 0; 147 | bi.bmiHeader.biClrUsed = 0; 148 | bi.bmiHeader.biClrImportant = 0; 149 | 150 | HWND hwnd; 151 | if (display_id == -1 || isPid == 0) { 152 | // screen = GetDC(NULL); /* Get entire screen */ 153 | hwnd = GetDesktopWindow(); 154 | } else { 155 | hwnd = (HWND) (uintptr) display_id; 156 | } 157 | screen = GetDC(hwnd); 158 | 159 | if (screen == NULL) { return NULL; } 160 | 161 | // Todo: Use DXGI 162 | screenMem = CreateCompatibleDC(screen); 163 | /* Get screen data in display device context. */ 164 | dib = CreateDIBSection(screen, &bi, DIB_RGB_COLORS, &data, NULL, 0); 165 | 166 | /* Copy the data into a bitmap struct. */ 167 | BOOL b = (screenMem == NULL) || 168 | SelectObject(screenMem, dib) == NULL || 169 | !BitBlt(screenMem, (int)0, (int)0, (int)w, (int)h, screen, x, y, SRCCOPY); 170 | if (b) { 171 | /* Error copying data. */ 172 | ReleaseDC(hwnd, screen); 173 | DeleteObject(dib); 174 | if (screenMem != NULL) { DeleteDC(screenMem); } 175 | 176 | return NULL; 177 | } 178 | 179 | bitmap = createMMBitmap_c(NULL, w, h, 4 * w, (uint8_t)bi.bmiHeader.biBitCount, 4); 180 | 181 | /* Copy the data to our pixel buffer. */ 182 | if (bitmap != NULL) { 183 | bitmap->imageBuffer = malloc(bitmap->bytewidth * bitmap->height); 184 | memcpy(bitmap->imageBuffer, data, bitmap->bytewidth * bitmap->height); 185 | } 186 | 187 | ReleaseDC(hwnd, screen); 188 | DeleteObject(dib); 189 | DeleteDC(screenMem); 190 | 191 | return bitmap; 192 | #endif 193 | } 194 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 |

Type and check the console

2 | 3 | -------------------------------------------------------------------------------- /wayland_n.go: -------------------------------------------------------------------------------- 1 | // +bulid linux,next 2 | package robotgo 3 | -------------------------------------------------------------------------------- /window/alert_c.h: -------------------------------------------------------------------------------- 1 | // #include "os.h" 2 | #if defined(IS_MACOSX) 3 | #include 4 | #endif 5 | 6 | #if defined(IS_MACOSX) 7 | CFStringRef CFStringCreateWithUTF8String(const char *title) { 8 | if (title == NULL) { return NULL; } 9 | return CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8); 10 | } 11 | #endif 12 | 13 | int showAlert(const char *title, const char *msg, 14 | const char *defaultButton, const char *cancelButton) { 15 | #if defined(IS_MACOSX) 16 | CFStringRef alertHeader = CFStringCreateWithUTF8String(title); 17 | CFStringRef alertMessage = CFStringCreateWithUTF8String(msg); 18 | CFStringRef defaultButtonTitle = CFStringCreateWithUTF8String(defaultButton); 19 | CFStringRef cancelButtonTitle = CFStringCreateWithUTF8String(cancelButton); 20 | CFOptionFlags responseFlags; 21 | 22 | SInt32 err = CFUserNotificationDisplayAlert( 23 | 0.0, kCFUserNotificationNoteAlertLevel, NULL, NULL, NULL, alertHeader, alertMessage, 24 | defaultButtonTitle, cancelButtonTitle, NULL, &responseFlags); 25 | 26 | if (alertHeader != NULL) CFRelease(alertHeader); 27 | if (alertMessage != NULL) CFRelease(alertMessage); 28 | if (defaultButtonTitle != NULL) CFRelease(defaultButtonTitle); 29 | if (cancelButtonTitle != NULL) CFRelease(cancelButtonTitle); 30 | 31 | if (err != 0) { return -1; } 32 | return (responseFlags == kCFUserNotificationDefaultResponse) ? 0 : 1; 33 | #elif defined(USE_X11) 34 | return 0; 35 | #else 36 | /* TODO: Display custom buttons instead of the pre-defined "OK" and "Cancel". */ 37 | int response = MessageBox(NULL, msg, title, 38 | (cancelButton == NULL) ? MB_OK : MB_OKCANCEL ); 39 | return (response == IDOK) ? 0 : 1; 40 | #endif 41 | } 42 | 43 | -------------------------------------------------------------------------------- /window/goWindow.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | #include "alert_c.h" 12 | #include "window.h" 13 | #include "win_sys.h" 14 | 15 | void min_window(uintptr pid, bool state, int8_t isPid){ 16 | #if defined(IS_MACOSX) 17 | // return 0; 18 | AXUIElementRef axID = AXUIElementCreateApplication(pid); 19 | AXUIElementSetAttributeValue(axID, kAXMinimizedAttribute, 20 | state ? kCFBooleanTrue : kCFBooleanFalse); 21 | #elif defined(USE_X11) 22 | // Ignore X errors 23 | XDismissErrors(); 24 | // SetState((Window)pid, STATE_MINIMIZE, state); 25 | #elif defined(IS_WINDOWS) 26 | HWND hwnd = getHwnd(pid, isPid); 27 | win_min(hwnd, state); 28 | #endif 29 | } 30 | 31 | void max_window(uintptr pid, bool state, int8_t isPid){ 32 | #if defined(IS_MACOSX) 33 | // return 0; 34 | #elif defined(USE_X11) 35 | XDismissErrors(); 36 | // SetState((Window)pid, STATE_MINIMIZE, false); 37 | // SetState((Window)pid, STATE_MAXIMIZE, state); 38 | #elif defined(IS_WINDOWS) 39 | HWND hwnd = getHwnd(pid, isPid); 40 | win_max(hwnd, state); 41 | #endif 42 | } 43 | 44 | uintptr get_handle(){ 45 | MData mData = get_active(); 46 | 47 | #if defined(IS_MACOSX) 48 | return (uintptr)mData.CgID; 49 | #elif defined(USE_X11) 50 | return (uintptr)mData.XWin; 51 | #elif defined(IS_WINDOWS) 52 | return (uintptr)mData.HWnd; 53 | #endif 54 | } 55 | 56 | uintptr b_get_handle() { 57 | #if defined(IS_MACOSX) 58 | return (uintptr)pub_mData.CgID; 59 | #elif defined(USE_X11) 60 | return (uintptr)pub_mData.XWin; 61 | #elif defined(IS_WINDOWS) 62 | return (uintptr)pub_mData.HWnd; 63 | #endif 64 | } 65 | 66 | void active_PID(uintptr pid, int8_t isPid){ 67 | MData win = set_handle_pid(pid, isPid); 68 | set_active(win); 69 | } 70 | -------------------------------------------------------------------------------- /window/pub.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | // #include "../base/os.h" 12 | #if defined(IS_MACOSX) 13 | #include 14 | #elif defined(USE_X11) 15 | #include 16 | #endif 17 | 18 | struct _MData{ 19 | #if defined(IS_MACOSX) 20 | CGWindowID CgID; // Handle to a CGWindowID 21 | AXUIElementRef AxID; // Handle to a AXUIElementRef 22 | #elif defined(USE_X11) 23 | Window XWin; // Handle to an X11 window 24 | #elif defined(IS_WINDOWS) 25 | HWND HWnd; // Handle to a window HWND 26 | TCHAR Title[512]; 27 | #endif 28 | }; 29 | 30 | typedef struct _MData MData; 31 | MData pub_mData; 32 | 33 | struct _Bounds { 34 | int32_t X; // Top left X coordinate 35 | int32_t Y; // Top left Y coordinate 36 | int32_t W; // bounds width 37 | int32_t H; // bounds height 38 | }; 39 | typedef struct _Bounds Bounds; 40 | 41 | #if defined(IS_MACOSX) 42 | static Boolean(*gAXIsProcessTrustedWithOptions) (CFDictionaryRef); 43 | static CFStringRef* gkAXTrustedCheckOptionPrompt; 44 | 45 | AXError _AXUIElementGetWindow(AXUIElementRef, CGWindowID* out); 46 | static AXUIElementRef GetUIElement(CGWindowID win){ 47 | intptr pid = 0; 48 | // double_t pid = 0; 49 | // Create array storing window 50 | CGWindowID window[1] = { win }; 51 | CFArrayRef wlist = CFArrayCreate(NULL, (const void**)window, 1, NULL); 52 | 53 | // Get window info 54 | CFArrayRef info = CGWindowListCreateDescriptionFromArray(wlist); 55 | CFRelease(wlist); 56 | 57 | // Check whether the resulting array is populated 58 | if (info != NULL && CFArrayGetCount(info) > 0) { 59 | // Retrieve description from info array 60 | CFDictionaryRef desc = (CFDictionaryRef)CFArrayGetValueAtIndex(info, 0); 61 | // Get window PID 62 | CFNumberRef data = (CFNumberRef) CFDictionaryGetValue(desc, kCGWindowOwnerPID); 63 | if (data != NULL) { 64 | CFNumberGetValue(data, kCFNumberIntType, &pid); 65 | } 66 | 67 | // Return result 68 | CFRelease(info); 69 | } 70 | 71 | // Check if PID was retrieved 72 | if (pid <= 0) { return NULL; } 73 | 74 | // Create an accessibility object using retrieved PID 75 | AXUIElementRef application = AXUIElementCreateApplication(pid); 76 | if (application == 0) {return NULL;} 77 | 78 | CFArrayRef windows = NULL; 79 | // Get all windows associated with the app 80 | AXUIElementCopyAttributeValues(application, kAXWindowsAttribute, 0, 1024, &windows); 81 | 82 | // Reference to resulting value 83 | AXUIElementRef result = NULL; 84 | 85 | if (windows != NULL) { 86 | int count = CFArrayGetCount(windows); 87 | // Loop all windows in the process 88 | for (CFIndex i = 0; i < count; ++i){ 89 | // Get the element at the index 90 | AXUIElementRef element = (AXUIElementRef) CFArrayGetValueAtIndex(windows, i); 91 | CGWindowID temp = 0; 92 | // Use undocumented API to get WindowID 93 | _AXUIElementGetWindow(element, &temp); 94 | 95 | if (temp == win) { 96 | // Retain element 97 | CFRetain(element); 98 | result = element; 99 | break; 100 | } 101 | } 102 | 103 | CFRelease(windows); 104 | } 105 | 106 | CFRelease(application); 107 | return result; 108 | } 109 | #elif defined(USE_X11) 110 | // Error Handling 111 | typedef int (*XErrorHandler) (Display*, XErrorEvent*); 112 | 113 | static int XHandleError(Display* dp, XErrorEvent* e) { return 0; } 114 | XErrorHandler mOld; 115 | void XDismissErrors (void) { 116 | Display *rDisplay = XOpenDisplay(NULL); 117 | // Save old handler and dismiss errors 118 | mOld = XSetErrorHandler(XHandleError); 119 | // Flush output buffer 120 | XSync(rDisplay, False); 121 | 122 | // Reinstate old handler 123 | XSetErrorHandler(mOld); 124 | XCloseDisplay(rDisplay); 125 | } 126 | 127 | // Definitions 128 | struct Hints{ 129 | unsigned long Flags; 130 | unsigned long Funcs; 131 | unsigned long Decorations; 132 | signed long Mode; 133 | unsigned long Stat; 134 | }; 135 | 136 | static Atom WM_STATE = None; 137 | static Atom WM_ABOVE = None; 138 | static Atom WM_HIDDEN = None; 139 | static Atom WM_HMAX = None; 140 | static Atom WM_VMAX = None; 141 | 142 | static Atom WM_DESKTOP = None; 143 | static Atom WM_CURDESK = None; 144 | 145 | static Atom WM_NAME = None; 146 | static Atom WM_UTF8 = None; 147 | static Atom WM_PID = None; 148 | static Atom WM_ACTIVE = None; 149 | static Atom WM_HINTS = None; 150 | static Atom WM_EXTENTS = None; 151 | 152 | //////////////////////////////////////////////////////////////////////////////// 153 | 154 | static void LoadAtoms (void){ 155 | Display *rDisplay = XOpenDisplay(NULL); 156 | WM_STATE = XInternAtom(rDisplay, "_NET_WM_STATE", True); 157 | WM_ABOVE = XInternAtom(rDisplay, "_NET_WM_STATE_ABOVE", True); 158 | WM_HIDDEN = XInternAtom(rDisplay, "_NET_WM_STATE_HIDDEN", True); 159 | WM_HMAX = XInternAtom(rDisplay, "_NET_WM_STATE_MAXIMIZED_HORZ", True); 160 | WM_VMAX = XInternAtom(rDisplay, "_NET_WM_STATE_MAXIMIZED_VERT", True); 161 | 162 | WM_DESKTOP = XInternAtom(rDisplay, "_NET_WM_DESKTOP", True); 163 | WM_CURDESK = XInternAtom(rDisplay, "_NET_CURRENT_DESKTOP", True); 164 | 165 | WM_NAME = XInternAtom(rDisplay, "_NET_WM_NAME", True); 166 | WM_UTF8 = XInternAtom(rDisplay, "UTF8_STRING", True); 167 | WM_PID = XInternAtom(rDisplay, "_NET_WM_PID", True); 168 | WM_ACTIVE = XInternAtom(rDisplay, "_NET_ACTIVE_WINDOW", True); 169 | WM_HINTS = XInternAtom(rDisplay, "_MOTIF_WM_HINTS", True); 170 | WM_EXTENTS = XInternAtom(rDisplay, "_NET_FRAME_EXTENTS", True); 171 | XCloseDisplay(rDisplay); 172 | } 173 | 174 | // Functions 175 | static void* GetWindowProperty(MData win, Atom atom, uint32_t* items) { 176 | // Property variables 177 | Atom type; int format; 178 | unsigned long nItems; 179 | unsigned long bAfter; 180 | unsigned char* result = NULL; 181 | 182 | Display *rDisplay = XOpenDisplay(NULL); 183 | // Check the atom 184 | if (atom != None) { 185 | // Retrieve and validate the specified property 186 | if (!XGetWindowProperty(rDisplay, win.XWin, atom, 0, 187 | BUFSIZ, False, AnyPropertyType, &type, &format, &nItems, &bAfter, &result) 188 | && result && nItems) { 189 | 190 | // Copy items result 191 | if (items != NULL) { 192 | *items = (uint32_t) nItems; 193 | } 194 | XCloseDisplay(rDisplay); 195 | return result; 196 | } 197 | } 198 | 199 | // Reset the items result if valid 200 | if (items != NULL) { *items = 0; } 201 | if (result != NULL) { 202 | XFree(result); 203 | } 204 | 205 | XCloseDisplay(rDisplay); 206 | return NULL; 207 | } 208 | 209 | ////// 210 | #define STATE_TOPMOST 0 211 | #define STATE_MINIMIZE 1 212 | #define STATE_MAXIMIZE 2 213 | 214 | ////// 215 | static void SetDesktopForWindow(MData win){ 216 | Display *rDisplay = XOpenDisplay(NULL); 217 | // Validate every atom that we want to use 218 | if (WM_DESKTOP != None && WM_CURDESK != None) { 219 | // Get desktop property 220 | long* desktop = (long*)GetWindowProperty(win, WM_DESKTOP,NULL); 221 | // Check result value 222 | if (desktop != NULL) { 223 | // Retrieve the screen number 224 | XWindowAttributes attr = { 0 }; 225 | XGetWindowAttributes(rDisplay, win.XWin, &attr); 226 | int s = XScreenNumberOfScreen(attr.screen); 227 | Window root = XRootWindow(rDisplay, s); 228 | 229 | // Prepare an event 230 | XClientMessageEvent e = { 0 }; 231 | e.window = root; e.format = 32; 232 | e.message_type = WM_CURDESK; 233 | e.display = rDisplay; 234 | e.type = ClientMessage; 235 | e.data.l[0] = *desktop; 236 | e.data.l[1] = CurrentTime; 237 | 238 | // Send the message 239 | XSendEvent(rDisplay, root, False, SubstructureNotifyMask | SubstructureRedirectMask, 240 | (XEvent*) &e); 241 | 242 | XFree(desktop); 243 | } 244 | } 245 | XCloseDisplay(rDisplay); 246 | } 247 | 248 | static Bounds GetFrame(MData win){ 249 | Bounds frame; 250 | // Retrieve frame bounds 251 | if (WM_EXTENTS != None) { 252 | long* result; uint32_t nItems = 0; 253 | // Get the window extents property 254 | result = (long*) GetWindowProperty(win, WM_EXTENTS, &nItems); 255 | if (result != NULL) { 256 | if (nItems == 4) { 257 | frame.X = (int32_t) result[0]; 258 | frame.Y = (int32_t) result[2]; 259 | frame.W = (int32_t) result[0] + (int32_t) result[1]; 260 | frame.H = (int32_t) result[2] + (int32_t) result[3]; 261 | } 262 | 263 | XFree(result); 264 | } 265 | } 266 | return frame; 267 | } 268 | 269 | 270 | #elif defined(IS_WINDOWS) 271 | HWND getHwnd(uintptr pid, int8_t isPid); 272 | 273 | void win_min(HWND hwnd, bool state){ 274 | if (state) { 275 | ShowWindow(hwnd, SW_MINIMIZE); 276 | } else { 277 | ShowWindow(hwnd, SW_RESTORE); 278 | } 279 | } 280 | 281 | void win_max(HWND hwnd, bool state){ 282 | if (state) { 283 | ShowWindow(hwnd, SW_MAXIMIZE); 284 | } else { 285 | ShowWindow(hwnd, SW_RESTORE); 286 | } 287 | } 288 | #endif -------------------------------------------------------------------------------- /window/win_sys.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-vgo/robotgo/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | // #if defined(USE_X11) 12 | // #include 13 | // #endif 14 | 15 | Bounds get_client(uintptr pid, int8_t isPid); 16 | 17 | Bounds get_bounds(uintptr pid, int8_t isPid){ 18 | // Check if the window is valid 19 | Bounds bounds; 20 | if (!is_valid()) { return bounds; } 21 | 22 | #if defined(IS_MACOSX) 23 | // Bounds bounds; 24 | AXValueRef axp = NULL; 25 | AXValueRef axs = NULL; 26 | AXUIElementRef AxID = AXUIElementCreateApplication(pid); 27 | 28 | // Determine the current point of the window 29 | if (AXUIElementCopyAttributeValue(AxID, kAXPositionAttribute, (CFTypeRef*) &axp) 30 | != kAXErrorSuccess || axp == NULL){ 31 | goto exit; 32 | } 33 | 34 | // Determine the current size of the window 35 | if (AXUIElementCopyAttributeValue(AxID, kAXSizeAttribute, (CFTypeRef*) &axs) 36 | != kAXErrorSuccess || axs == NULL){ 37 | goto exit; 38 | } 39 | 40 | CGPoint p; CGSize s; 41 | // Attempt to convert both values into atomic types 42 | if (AXValueGetValue(axp, kAXValueCGPointType, &p) && 43 | AXValueGetValue(axs, kAXValueCGSizeType, &s)){ 44 | bounds.X = p.x; 45 | bounds.Y = p.y; 46 | bounds.W = s.width; 47 | bounds.H = s.height; 48 | } 49 | 50 | // return bounds; 51 | exit: 52 | if (axp != NULL) { CFRelease(axp); } 53 | if (axs != NULL) { CFRelease(axs); } 54 | 55 | return bounds; 56 | #elif defined(USE_X11) 57 | // Ignore X errors 58 | XDismissErrors(); 59 | MData win; 60 | win.XWin = (Window)pid; 61 | 62 | Bounds client = get_client(pid, isPid); 63 | Bounds frame = GetFrame(win); 64 | 65 | bounds.X = client.X - frame.X; 66 | bounds.Y = client.Y - frame.Y; 67 | bounds.W = client.W + frame.W; 68 | bounds.H = client.H + frame.H; 69 | 70 | return bounds; 71 | #elif defined(IS_WINDOWS) 72 | HWND hwnd = getHwnd(pid, isPid); 73 | 74 | RECT rect = { 0 }; 75 | GetWindowRect(hwnd, &rect); 76 | 77 | bounds.X = rect.left; 78 | bounds.Y = rect.top; 79 | bounds.W = rect.right - rect.left; 80 | bounds.H = rect.bottom - rect.top; 81 | 82 | return bounds; 83 | #endif 84 | } 85 | 86 | Bounds get_client(uintptr pid, int8_t isPid) { 87 | // Check if the window is valid 88 | Bounds bounds; 89 | if (!is_valid()) { return bounds; } 90 | 91 | #if defined(IS_MACOSX) 92 | return get_bounds(pid, isPid); 93 | #elif defined(USE_X11) 94 | Display *rDisplay = XOpenDisplay(NULL); 95 | 96 | // Ignore X errors 97 | XDismissErrors(); 98 | MData win; 99 | win.XWin = (Window)pid; 100 | 101 | // Property variables 102 | Window root, parent; 103 | Window* children; 104 | unsigned int count; 105 | int32_t x = 0, y = 0; 106 | 107 | // Check if the window is the root 108 | XQueryTree(rDisplay, win.XWin, &root, &parent, &children, &count); 109 | if (children) { XFree(children); } 110 | 111 | // Retrieve window attributes 112 | XWindowAttributes attr = { 0 }; 113 | XGetWindowAttributes(rDisplay, win.XWin, &attr); 114 | 115 | // Coordinates must be translated 116 | if (parent != attr.root) { 117 | XTranslateCoordinates(rDisplay, win.XWin, attr.root, attr.x, attr.y, &x, &y, &parent); 118 | } else { 119 | x = attr.x; 120 | y = attr.y; 121 | } 122 | 123 | // Return resulting window bounds 124 | bounds.X = x; 125 | bounds.Y = y; 126 | bounds.W = attr.width; 127 | bounds.H = attr.height; 128 | XCloseDisplay(rDisplay); 129 | 130 | return bounds; 131 | #elif defined(IS_WINDOWS) 132 | HWND hwnd = getHwnd(pid, isPid); 133 | 134 | RECT rect = { 0 }; 135 | GetClientRect(hwnd, &rect); 136 | 137 | POINT point; 138 | point.x = rect.left; 139 | point.y = rect.top; 140 | 141 | // Convert the client point to screen 142 | ClientToScreen(hwnd, &point); 143 | 144 | bounds.X = point.x; 145 | bounds.Y = point.y; 146 | bounds.W = rect.right - rect.left; 147 | bounds.H = rect.bottom - rect.top; 148 | 149 | return bounds; 150 | #endif 151 | } -------------------------------------------------------------------------------- /window/window.go: -------------------------------------------------------------------------------- 1 | package window 2 | -------------------------------------------------------------------------------- /windows_n.go: -------------------------------------------------------------------------------- 1 | // +bulid windows,next 2 | package robotgo 3 | --------------------------------------------------------------------------------