Repository: go-vgo/robotgo Branch: master Commit: 7dee03f3f83b Files: 99 Total size: 300.2 KB Directory structure: gitextract_108ox7j1/ ├── .circleci/ │ ├── config.yml │ └── images/ │ └── primary/ │ └── Dockerfile ├── .github/ │ ├── CODEOWNERS │ ├── CONTRIBUTING.md │ ├── issue_template.md │ ├── pull_request_template.md │ └── workflows/ │ └── go.yml ├── .gitignore ├── .travis.yml ├── CLA.md ├── CONTRIBUTING.md ├── LICENSE ├── README.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 │ ├── archive/ │ │ ├── README_zh.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/ │ ├── key.go │ ├── key_windows.go │ ├── keycode.h │ ├── keycode_c.h │ ├── keypress.h │ └── keypress_c.h ├── key.go ├── 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/ │ ├── goScreen.h │ ├── screen.go │ ├── screen_c.h │ └── screengrab_c.h ├── screen.go ├── test/ │ └── index.html ├── wayland_n.go ├── window/ │ ├── alert_c.h │ ├── goWindow.h │ ├── pub.h │ ├── win_sys.h │ ├── window.go │ └── window.h └── windows_n.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .circleci/config.yml ================================================ version: 2 jobs: build: docker: # using custom image, see .circleci/images/primary/Dockerfile # - image: govgo/robotgoci:1.10.3 - image: golang:1.25.0 working_directory: /gopath/src/github.com/go-vgo/robotgo steps: - checkout # specify any bash command here prefixed with `run: ` - run: apt update - run: apt -y install gcc libc6-dev libx11-dev xorg-dev libxtst-dev xsel xclip # libpng++-dev # xcb libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxkbcommon-x11-dev libxkbcommon-dev - run: apt -y install xvfb # # override: - run: go get -v -t -d ./... - run: xvfb-run go test -v ./... # # codecov.io # - run: xvfb-run go test -v -covermode=count -coverprofile=coverage.out # - run: bash <(curl -s https://codecov.io/bash) ================================================ FILE: .circleci/images/primary/Dockerfile ================================================ # FROM golang:1.10.1 FROM golang:1.24.2-stretch AS build # FROM govgo/go:1.11.1 RUN apt update && apt install -y --no-install-recommends \ # customize dependencies libx11-dev xorg-dev \ libxtst-dev \ # Bitmap libpng++-dev \ # Event: xcb libxcb-xkb-dev \ x11-xkb-utils libx11-xcb-dev \ libxkbcommon-x11-dev libxkbcommon-dev \ # Clipboard: xsel xclip && \ # apt remove --purge --auto-remove && \ apt clean && \ rm -rf /var/lib/apt/lists/* RUN go get -u github.com/go-vgo/robotgo ================================================ FILE: .github/CODEOWNERS ================================================ # general * @vcaesar ================================================ FILE: .github/CONTRIBUTING.md ================================================ # Contributing to Robotgo ================================================ FILE: .github/issue_template.md ================================================ 1. Please **speak English (English only)**, this is the language everybody of us can speak and write. 2. Please take a moment to **search** that an issue **doesn't already exist**. 3. Please make sure `Golang, GCC` is installed correctly before installing RobotGo. 4. Please give all relevant information below for bug reports, incomplete details will be handled as an invalid report. **You MUST delete the content above including this line before posting, otherwise your issue will be invalid.** - Robotgo version (or commit ref): - Go version: - Gcc version: - Operating system and bit: - Resolution: - Can you reproduce the bug at [Examples](https://github.com/go-vgo/robotgo/blob/master/examples/): - [ ] Yes (provide example code) - [ ] No - [ ] Not relevant - Provide example code: ```Go ``` - Log gist: ## Description ... ================================================ FILE: .github/pull_request_template.md ================================================ The pull request will be closed without any reasons if it does not satisfy any of following requirements: 1. Make sure you are targeting the `master` branch, pull requests on release branches are only allowed for bug fixes. 2. Add new features, please provide the reasons and test code. 3. Please read contributing guidelines: [CONTRIBUTING](https://github.com/go-vgo/robotgo/blob/master/CONTRIBUTING.md) and sign the CLA. 4. Describe what your pull request does and which issue you're targeting (if any and **Please use English**) 5. ... if it is not related to any particular issues, explain why we should not reject your pull request. 6. The Commits must **use English**, must be test and No useless submissions. **You MUST delete the content above including this line before posting, otherwise your pull request will be invalid.** **Please provide Issues links to:** - Issues: #1 **Provide test code:** ```Go ``` ## Description ... ================================================ FILE: .github/workflows/go.yml ================================================ name: Go on: [push] jobs: test: # name: build strategy: matrix: # go: [1.12.x, 1.13.x] os: [macOS-latest, windows-latest] # ubuntu-latest runs-on: ${{ matrix.os }} steps: - name: Set up Go 1.26.0 uses: actions/setup-go@v6 with: go-version: 1.26.x id: go - name: Check out code into the Go module directory uses: actions/checkout@v6 - name: Get dependencies run: | go get -v -t -d ./... - name: Build run: go build -v . - name: Test run: go test -v robot_info_test.go # run: go test -v . ================================================ FILE: .gitignore ================================================ # Object files .DS_Store .vscode .idea robot_test.png # Examples examples/screen/screen examples/screen/saveCapture.png # examples/bitmap/test.png examples/bitmap/test2.png examples/bitmap/test.tif examples/bitmap/test31.tif examples/bitmap/tocbitmap.png examples/bitmap/teststr.png test/test.png # Debug files *.dSYM/ *.su debug # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.o *.ko *.obj *.elf # Precompiled Headers *.gch *.pch # cdeps/hook event/hook vendor # Libraries *.lib # !cdeps/win32/libpng.lib # !cdeps/win32/zlib.lib # !cdeps/win64/libpng.lib # !cdeps/win64/zlib.lib # ## *.a !cdeps/mac/libpng.a !cdeps/mac/amd/libpng.a !cdeps/mac/m1/libpng.a # !cdeps/win32/libpng.a !cdeps/win64/libpng.a !cdeps/win/amd/win32/libpng.a !cdeps/win/amd/win64/libpng.a !cdeps/win/arm/libpng.a # *.la *.lo # Shared objects (inc. Windows DLLs) *.dll *.so *.so.* *.dylib # Executables *.exe *.out *.app *.i*86 *.x86_64 *.hex # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 .glide/ # examples/bitmap/test_IMG.png examples/bitmap/imgToBitmap/test_01.png examples/bitmap/imgToBitmap/test_002.jpeg examples/bitmap/imgToBitmap/test_003.jpeg examples/bitmap/imgToBitmap/test_1.png examples/bitmap/imgToBitmap/test_2.jpeg examples/bitmap/imgToBitmap/test.png examples/bitmap/imgToBitmap/test_7.jpeg robot_img.png examples/bitmap/bitmapTobytes/out.jpg ================================================ FILE: .travis.yml ================================================ language: go os: - linux - osx go: # - 1.7.x # - 1.8.x # - 1.9.x # - 1.10.x # - 1.11.x # - 1.12.x # - 1.13.x # - 1.14.x # - 1.15.x # - 1.16.x # - 1.17.x # - 1.18.x - 1.19.x # - tip addons: apt: packages: - libx11-dev xorg-dev - libxtst-dev libpng++-dev - xcb libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxkbcommon-x11-dev # - libusb-dev - libxkbcommon-dev - xsel - xclip # - xvfb # script: # - sudo apt update # - sudo apt install libx11-dev # - sudo apt install xorg-dev # - sudo apt install libxtst-dev libpng++-dev # # Event: # - sudo apt install xcb libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxkbcommon-x11-dev # - sudo apt install libxkbcommon-dev # # Clipboard: # - sudo apt install xsel # - sudo apt install xclip # - go test -v . before_install: - export PATH=$PATH:$HOME/gopath/bin - go get -v -t -d ./... script: - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then xvfb-run go test -v ./...; fi - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then go test -v ./...; fi ================================================ FILE: CLA.md ================================================ ### AtomAI Individual Contributor License Agreement Version: 2025-11-19 (https://cla-assistant.io/) Thank you for your interest in contributing to open source software projects (“Projects”) made available by AtomAI SE or its affiliates (“AtomAI”). This Individual Contributor License Agreement (“Agreement”) sets out the terms governing any source code, object code, bug fixes, configuration changes, tools, specifications, documentation, data, materials, feedback, information or other works of authorship that you submit or have submitted, in any form and in any manner, to AtomAI in respect of any of the Projects (collectively “Contributions”). If you have any questions respecting this Agreement, please contact info@atomai.cc. You agree that the following terms apply to all of your past, present and future Contributions. Except for the licenses granted in this Agreement, you retain all of your right, title and interest in and to your Contributions. **Copyright License.** You hereby grant, and agree to grant, to AtomAI a non-exclusive, perpetual, irrevocable, worldwide, fully-paid, royalty-free, transferable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, and distribute your Contributions and such derivative works, with the right to sublicense the foregoing rights through multiple tiers of sublicensees. **Patent License.** You hereby grant, and agree to grant, to AtomAI a non-exclusive, perpetual, irrevocable, worldwide, fully-paid, royalty-free, transferable patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer your Contributions, where such license applies only to those patent claims licensable by you that are necessarily infringed by your Contributions alone or by combination of your Contributions with the Project to which such Contributions were submitted, with the right to sublicense the foregoing rights through multiple tiers of sublicensees. **Moral Rights.** To the fullest extent permitted under applicable law, you hereby waive, and agree not to assert, all of your “moral rights” in or relating to your Contributions for the benefit of AtomAI, its assigns, and their respective direct and indirect sublicensees. **Third Party Content/Rights.** If your Contribution includes or is based on any source code, object code, bug fixes, configuration changes, tools, specifications, documentation, data, materials, feedback, information or other works of authorship that were not authored by you (“Third Party Content”) or if you are aware of any third party intellectual property or proprietary rights associated with your Contribution (“Third Party Rights”), then you agree to include with the submission of your Contribution full details respecting such Third Party Content and Third Party Rights, including, without limitation, identification of which aspects of your Contribution contain Third Party Content or are associated with Third Party Rights, the owner/author of the Third Party Content and Third Party Rights, where you obtained the Third Party Content, and any applicable third party license terms or restrictions respecting the Third Party Content and Third Party Rights. For greater certainty, the foregoing obligations respecting the identification of Third Party Content and Third Party Rights do not apply to any portion of a Project that is incorporated into your Contribution to that same Project. **Representations.** You represent that, other than the Third Party Content and Third Party Rights identified by you in accordance with this Agreement, you are the sole author of your Contributions and are legally entitled to grant the foregoing licenses and waivers in respect of your Contributions. If your Contributions were created in the course of your employment with your past or present employer(s), you represent that such employer(s) has authorized you to make your Contributions on behalf of such employer(s) or such employer (s) has waived all of their right, title or interest in or to your Contributions. **Disclaimer.** To the fullest extent permitted under applicable law, your Contributions are provided on an "asis" basis, without any warranties or conditions, express or implied, including, without limitation, any implied warranties or conditions of non-infringement, merchantability or fitness for a particular purpose. You are not required to provide support for your Contributions, except to the extent you desire to provide support. **No Obligation.** You acknowledge that AtomAI is under no obligation to use or incorporate your Contributions into any of the Projects. The decision to use or incorporate your Contributions into any of the Projects will be made at the sole discretion of AtomAI or its authorized delegates. **Disputes.** This Agreement shall be governed by and construed in accordance with the laws of the State of Wyoming, United States of America, without giving effect to its principles or rules regarding conflicts of laws, other than such principles directing application of Wyoming law. The parties hereby submit to venue in, and jurisdiction of the courts located in Wyoming, Wyoming for purposes relating to this Agreement. In the event that any of the provisions of this Agreement shall be held by a court or other tribunal of competent jurisdiction to be unenforceable, the remaining portions hereof shall remain in full force and effect. **Assignment.** You agree that AtomAI may assign this Agreement, and all of its rights, obligations and licenses hereunder. ================================================ FILE: CONTRIBUTING.md ================================================ # Contribution Guidelines ## Introduction 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). Sensitive security-related issues should be reported to [info@atomai.cc](info@atomaicc). ## Bug reports Please search the issues on the issue tracker with a variety of keywords to ensure your bug is not already reported. 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. 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). Please be kind, remember that Robotgo comes at no cost to you, and you're getting free help. ## Discuss your design 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. 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. 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. ## Testing redux 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. ## Code review 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. ## Sign your work 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. ## Maintainers 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. ## Owners Since Robotgo is a pure community organization without any company support, Copyright 2016 The go-vgo Project Developers. ## Versions 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. 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. ## Copyright Code that you contribute should use the standard copyright header: ``` // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. ``` 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. ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ # Robotgo [![Build Status](https://github.com/go-vgo/robotgo/workflows/Go/badge.svg)](https://github.com/go-vgo/robotgo/commits/master) [![CircleCI Status](https://circleci.com/gh/go-vgo/robotgo.svg?style=shield)](https://circleci.com/gh/go-vgo/robotgo) ![Appveyor](https://ci.appveyor.com/api/projects/status/github/go-vgo/robotgo?branch=master&svg=true) [![Go Report Card](https://goreportcard.com/badge/github.com/go-vgo/robotgo)](https://goreportcard.com/report/github.com/go-vgo/robotgo) [![GoDoc](https://pkg.go.dev/badge/github.com/go-vgo/robotgo?status.svg)](https://pkg.go.dev/github.com/go-vgo/robotgo?tab=doc) [![GitHub release](https://img.shields.io/github/release/go-vgo/robotgo.svg)](https://github.com/go-vgo/robotgo/releases/latest) > Golang Desktop Automation, auto test and AI Computer Use.
> Control the mouse, keyboard, read the screen, process, Window Handle, image and bitmap and global event listener. RobotGo supports Mac, Windows, and Linux (X11); and robotgo supports arm64 and x86-amd64. [RobotGo-Pro](https://github.com/vcaesar/robotgo-pro) get the JavaScript, Python, Lua and others version, tech supports, new features and newest robotgo version (such as Wayland support, "no open-source version forever"). ## Contents - [Docs](#docs) - [Binding](#binding) - [Requirements](#requirements) - [Installation](#installation) - [Update](#update) - [Examples](#examples) - [Type Conversion and keys](https://github.com/go-vgo/robotgo/blob/master/docs/keys.md) - [Cross-Compiling](https://github.com/go-vgo/robotgo/blob/master/docs/install.md#crosscompiling) - [Authors](#authors) - [Plans](#plans) - [License](#license) ## Docs - [GoDoc](https://godoc.org/github.com/go-vgo/robotgo)
- [API Docs](https://github.com/go-vgo/robotgo/blob/master/docs/doc.md) (Deprecated, no updated) ## Binding: [ADB](https://github.com/vcaesar/adb), packaging android adb API. ## Requirements: Now, Please make sure `Golang, GCC` is installed correctly before installing RobotGo. ### ALL: ``` Golang GCC ``` #### For MacOS: ``` brew install go ``` Xcode Command Line Tools;
And Privacy setting, add Screen Recording and Accessibility under:
`System Settings > Privacy & Security > Accessibility, Screen & System Audio Recording`. ``` xcode-select --install ``` #### For Windows: ``` winget install Golang.go ``` [llvm-mingw](https://github.com/mstorsjo/llvm-mingw) ``` winget install MartinStorsjo.LLVM-MinGW.UCRT ``` or [Mingw-w64](https://sourceforge.net/projects/mingw-w64/files) ``` winget install BrechtSanders.WinLibs.POSIX.UCRT ``` Or Download the [Mingw-w64](https://sourceforge.net/projects/mingw-w64/files) and the others gcc, then set system environment variables like `C:\mingw64\bin` to the env `Path`. [Set environment variables to run GCC from command line](https://www.youtube.com/results?search_query=Set+environment+variables+to+run+GCC+from+command+line). `Or The others GCC` (Except the Mingw-w64, you should compile the "libpng" with yourself when use the [bitmap](https://github.com/vcaesar/bitmap).) #### For everything else: ``` GCC X11 with the XTest extension (the Xtst library) "Clipboard": xsel xclip "Bitmap": libpng (Just used by the "bitmap".) "Event-Gohook": xcb, xkb, libxkbcommon (Just used by the "hook".) ``` ##### Ubuntu: ```yml # sudo apt install golang sudo snap install go --classic # gcc sudo apt install gcc libc6-dev # x11 sudo apt install libx11-dev xorg-dev libxtst-dev # Clipboard sudo apt install xsel xclip # Bitmap sudo apt install libpng++-dev # GoHook sudo apt install xcb libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxkbcommon-x11-dev libxkbcommon-dev ``` ##### Fedora: ```yml # x11 sudo dnf install libXtst-devel # Clipboard sudo dnf install xsel xclip # Bitmap sudo dnf install libpng-devel # GoHook sudo dnf install libxkbcommon-devel libxkbcommon-x11-devel xorg-x11-xkb-utils-devel ``` ## Installation: With Go module support (Go 1.11+), just import: ```go import "github.com/go-vgo/robotgo" ``` Otherwise, to install the robotgo package, run the command: ``` go get github.com/go-vgo/robotgo ``` png.h: No such file or directory? Please see [issues/47](https://github.com/go-vgo/robotgo/issues/47). ## Update: ``` go get -u github.com/go-vgo/robotgo ``` Note go1.10.x C file compilation cache problem, [golang #24355](https://github.com/golang/go/issues/24355). `go mod vendor` problem, [golang #26366](https://github.com/golang/go/issues/26366). ## [Examples:](https://github.com/go-vgo/robotgo/blob/master/examples) #### [Mouse](https://github.com/go-vgo/robotgo/blob/master/examples/mouse/main.go) ```Go package main import ( "fmt" "github.com/go-vgo/robotgo" ) func main() { robotgo.MouseSleep = 300 robotgo.Move(100, 100) fmt.Println(robotgo.Location()) robotgo.Move(100, -200) // multi screen supported robotgo.MoveSmooth(120, -150) fmt.Println(robotgo.Location()) robotgo.ScrollDir(10, "up") robotgo.ScrollDir(20, "right") robotgo.Scroll(0, -10) robotgo.Scroll(100, 0) robotgo.MilliSleep(100) robotgo.ScrollSmooth(-10, 6) // robotgo.ScrollRelative(10, -100) robotgo.Move(10, 20) robotgo.MoveRelative(0, -10) robotgo.DragSmooth(10, 10) robotgo.Click("wheelRight") robotgo.Click("left", true) robotgo.MoveSmooth(100, 200, 1.0, 10.0) robotgo.Toggle("left") robotgo.Toggle("left", "up") } ``` #### [Keyboard](https://github.com/go-vgo/robotgo/blob/master/examples/key/main.go) ```Go package main import ( "fmt" "github.com/go-vgo/robotgo" ) func main() { robotgo.Type("Hello World") robotgo.Type("だんしゃり", 0, 1) // robotgo.Type("テストする") robotgo.Type("Hi, Seattle space needle, Golden gate bridge, One world trade center.") robotgo.Type("Hi galaxy, hi stars, hi MT.Rainier, hi sea. こんにちは世界.") robotgo.Sleep(1) // ustr := uint32(robotgo.CharCodeAt("Test", 0)) // robotgo.UnicodeType(ustr) robotgo.KeySleep = 100 robotgo.KeyTap("enter") // robotgo.Type("en") robotgo.KeyTap("i", "alt", "cmd") arr := []string{"alt", "cmd"} robotgo.KeyTap("i", arr) robotgo.MilliSleep(100) robotgo.KeyToggle("a") robotgo.KeyToggle("a", "up") robotgo.WriteAll("Test") text, err := robotgo.ReadAll() if err == nil { fmt.Println(text) } } ``` #### [Screen](https://github.com/go-vgo/robotgo/blob/master/examples/screen/main.go) ```Go package main import ( "fmt" "strconv" "github.com/go-vgo/robotgo" "github.com/vcaesar/imgo" ) func main() { x, y := robotgo.Location() fmt.Println("pos: ", x, y) color := robotgo.GetPixelColor(100, 200) fmt.Println("color---- ", color) sx, sy := robotgo.GetScreenSize() fmt.Println("get screen size: ", sx, sy) bit := robotgo.CaptureScreen(10, 10, 30, 30) defer robotgo.FreeBitmap(bit) img := robotgo.ToImage(bit) imgo.Save("test.png", img) num := robotgo.DisplaysNum() for i := 0; i < num; i++ { robotgo.DisplayID = i img1, _ := robotgo.CaptureImg() path1 := "save_" + strconv.Itoa(i) robotgo.Save(img1, path1+".png") robotgo.SaveJpeg(img1, path1+".jpeg", 50) img2, _ := robotgo.CaptureImg(10, 10, 20, 20) robotgo.Save(img2, "test_"+strconv.Itoa(i)+".png") x, y, w, h := robotgo.GetDisplayBounds(i) img3, err := robotgo.CaptureImg(x, y, w, h) fmt.Println("Capture error: ", err) robotgo.Save(img3, path1+"_1.png") } } ``` #### [Bitmap](https://github.com/vcaesar/bitmap/blob/main/examples/main.go) ```Go package main import ( "fmt" "github.com/go-vgo/robotgo" "github.com/vcaesar/bitmap" ) func main() { bit := robotgo.CaptureScreen(10, 20, 30, 40) // use `defer robotgo.FreeBitmap(bit)` to free the bitmap defer robotgo.FreeBitmap(bit) fmt.Println("bitmap...", bit) img := robotgo.ToImage(bit) // robotgo.SavePng(img, "test_1.png") robotgo.Save(img, "test_1.png") bit2 := robotgo.ToCBitmap(robotgo.ImgToBitmap(img)) fx, fy := bitmap.Find(bit2) fmt.Println("FindBitmap------ ", fx, fy) robotgo.Move(fx, fy) arr := bitmap.FindAll(bit2) fmt.Println("Find all bitmap: ", arr) fx, fy = bitmap.Find(bit) fmt.Println("FindBitmap------ ", fx, fy) bitmap.Save(bit, "test.png") } ``` #### [OpenCV](https://github.com/vcaesar/gcv) ```Go package main import ( "fmt" "math/rand" "github.com/go-vgo/robotgo" "github.com/vcaesar/gcv" "github.com/vcaesar/bitmap" ) func main() { opencv() } func opencv() { name := "test.png" name1 := "test_001.png" robotgo.SaveCapture(name1, 10, 10, 30, 30) robotgo.SaveCapture(name) fmt.Print("gcv find image: ") fmt.Println(gcv.FindImgFile(name1, name)) fmt.Println(gcv.FindAllImgFile(name1, name)) bit := bitmap.Open(name1) defer robotgo.FreeBitmap(bit) fmt.Print("find bitmap: ") fmt.Println(bitmap.Find(bit)) // bit0 := robotgo.CaptureScreen() // img := robotgo.ToImage(bit0) // bit1 := robotgo.CaptureScreen(10, 10, 30, 30) // img1 := robotgo.ToImage(bit1) // defer robotgo.FreeBitmapArr(bit0, bit1) img, _ := robotgo.CaptureImg() img1, _ := robotgo.CaptureImg(10, 10, 30, 30) fmt.Print("gcv find image: ") fmt.Println(gcv.FindImg(img1, img)) fmt.Println() res := gcv.FindAllImg(img1, img) fmt.Println(res[0].TopLeft.Y, res[0].Rects.TopLeft.X, res) x, y := res[0].TopLeft.X, res[0].TopLeft.Y robotgo.Move(x, y-rand.Intn(5)) robotgo.MilliSleep(100) robotgo.Click() res = gcv.FindAll(img1, img) // use find template and sift fmt.Println("find all: ", res) res1 := gcv.Find(img1, img) fmt.Println("find: ", res1) img2, _, _ := robotgo.DecodeImg("test_001.png") x, y = gcv.FindX(img2, img) fmt.Println(x, y) } ``` #### [Event](https://github.com/robotn/gohook/blob/master/examples/main.go) ```Go package main import ( "fmt" // "github.com/go-vgo/robotgo" hook "github.com/robotn/gohook" ) func main() { add() low() event() } func add() { fmt.Println("--- Please press ctrl + shift + q to stop hook ---") hook.Register(hook.KeyDown, []string{"q", "ctrl", "shift"}, func(e hook.Event) { fmt.Println("ctrl-shift-q") hook.End() }) fmt.Println("--- Please press w---") hook.Register(hook.KeyDown, []string{"w"}, func(e hook.Event) { fmt.Println("w") }) s := hook.Start() <-hook.Process(s) } func low() { evChan := hook.Start() defer hook.End() for ev := range evChan { fmt.Println("hook: ", ev) } } func event() { ok := hook.AddEvents("q", "ctrl", "shift") if ok { fmt.Println("add events...") } keve := hook.AddEvent("k") if keve { fmt.Println("you press... ", "k") } mleft := hook.AddEvent("mleft") if mleft { fmt.Println("you press... ", "mouse left button") } } ``` #### [Window](https://github.com/go-vgo/robotgo/blob/master/examples/window/main.go) ```Go package main import ( "fmt" "github.com/go-vgo/robotgo" ) func main() { fpid, err := robotgo.FindIds("Google") if err == nil { fmt.Println("pids... ", fpid) if len(fpid) > 0 { robotgo.Type("Hi galaxy!", fpid[0]) robotgo.KeyTap("a", fpid[0], "cmd") robotgo.KeyToggle("a", fpid[0]) robotgo.KeyToggle("a", fpid[0], "up") robotgo.ActivePid(fpid[0]) robotgo.Kill(fpid[0]) } } robotgo.ActiveName("chrome") isExist, err := robotgo.PidExists(100) if err == nil && isExist { fmt.Println("pid exists is", isExist) robotgo.Kill(100) } abool := robotgo.Alert("test", "robotgo") if abool { fmt.Println("ok@@@ ", "ok") } title := robotgo.GetTitle() fmt.Println("title@@@ ", title) } ``` ## Authors - [The author is Evans](https://github.com/vcaesar) - [Maintainers](https://github.com/orgs/go-vgo/people) ## Plans - Refactor some C code to Go (such as x11, windows) - Better multiscreen support - Wayland support - Update Window Handle - Try to support Android and IOS ## Contributors - See [contributors page](https://github.com/go-vgo/robotgo/graphs/contributors) for full list of contributors. - See [Contribution Guidelines](https://github.com/go-vgo/robotgo/blob/master/CONTRIBUTING.md). ## License Robotgo is primarily distributed under the terms of "the Apache License (Version 2.0)", with portions covered by various BSD-like licenses. See [LICENSE-APACHE](http://www.apache.org/licenses/LICENSE-2.0), [LICENSE](https://github.com/go-vgo/robotgo/blob/master/LICENSE). ================================================ FILE: appveyor.yml ================================================ # version format version: "{build}" # Operating system (build VM template) # os: Windows Server 2012 R2 os: Visual Studio 2017 # Platform. # platform: # - x64 # - x86 clone_folder: c:\gopath\src\github.com\go-vgo\robotgo # Environment variables environment: global: GOPATH: C:\gopath CC: gcc.exe matrix: - GOARCH: amd64 # GOVERSION: 1.9.3 GETH_ARCH: amd64 MSYS2_ARCH: x86_64 MSYS2_BITS: 64 MSYSTEM: MINGW64 PATH: C:\msys64\mingw64\bin\;C:\Program Files (x86)\NSIS\;%PATH% - GOARCH: 386 # GOVERSION: 1.9.3 GETH_ARCH: 386 MSYS2_ARCH: i686 MSYS2_BITS: 32 MSYSTEM: MINGW32 PATH: C:\msys64\mingw32\bin\;C:\Program Files (x86)\NSIS\;%PATH% # - COMPILER: MINGW_W64 # ARCHITECTURE: x64 GOVERSION: 1.25.0 # GOPATH: c:\gopath # scripts that run after cloning repository # install: # - set PATH=%GOPATH%\bin;c:\go\bin;%PATH% # - go version # - go env # - gcc --version # - python --version install: - set PATH=%GOPATH%\bin;c:\go\bin;%PATH% - git submodule update --init - rmdir C:\go /s /q - appveyor DownloadFile https://storage.googleapis.com/golang/go%GOVERSION%.windows-%GETH_ARCH%.zip - 7z x go%GOVERSION%.windows-%GETH_ARCH%.zip -y -oC:\ > NUL - go version - go env - gcc --version # To run your custom scripts instead of automatic MSBuild build_script: # We need to disable firewall - https://github.com/appveyor/ci/issues/1579#issuecomment-309830648 - ps: Disable-NetFirewallRule -DisplayName 'File and Printer Sharing (SMB-Out)' - cd c:\gopath\src\github.com\go-vgo\robotgo - git branch - go get -t ./... # To run your custom scripts instead of automatic tests test_script: # Unit tests - ps: Add-AppveyorTest "Unit Tests" -Outcome Running - go test -v github.com/go-vgo/robotgo/... - ps: Update-AppveyorTest "Unit Tests" -Outcome Passed # notifications: # - provider: Email # to: # - .io # on_build_failure: true # on_build_status_changed: true # to disable deployment deploy: off ================================================ FILE: base/LICENSE ================================================ The software is licensed under the terms of the MIT license. Copyright 2010 Michael Sanders, AE and the go-vgo Project Developers. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: base/MMBitmap.h ================================================ #pragma once #ifndef MMBITMAP_H #define MMBITMAP_H #include "types.h" #include "rgb.h" #include #include struct _MMBitmap { uint8_t *imageBuffer; /* Pixels stored in Quad I format; */ int32_t width; /* Never 0, unless image is NULL. */ int32_t height; /* Never 0, unless image is NULL. */ int32_t bytewidth; /* The aligned width (width + padding). */ uint8_t bitsPerPixel; /* Should be either 24 or 32. */ uint8_t bytesPerPixel; /* For convenience; should be bitsPerPixel / 8. */ }; typedef struct _MMBitmap MMBitmap; typedef MMBitmap *MMBitmapRef; #define MMBitmapPointInBounds(image, p) ((p).x < (image)->width && (p).y < (image)->height) /* Get pointer to pixel of MMBitmapRef. No bounds checking is performed */ #define MMRGBColorRefAtPoint(image, x, y) \ (MMRGBColor *)(assert(MMBitmapPointInBounds(image, MMPointInt32Make(x, y))), \ ((image)->imageBuffer) + (((image)->bytewidth * (y)) + ((x) * (image)->bytesPerPixel))) /* Dereference pixel of MMBitmapRef. Again, no bounds checking is performed. */ #define MMRGBColorAtPoint(image, x, y) *MMRGBColorRefAtPoint(image, x, y) /* Hex/integer value of color at point. */ #define MMRGBHexAtPoint(image, x, y) hexFromMMRGB(MMRGBColorAtPoint(image, x, y)) #endif /* MMBITMAP_H */ ================================================ FILE: base/base.go ================================================ // https://github.com/golang/go/issues/26366 package base ================================================ FILE: base/bitmap_free_c.h ================================================ #include "MMBitmap.h" #include #include MMBitmapRef createMMBitmap_c(uint8_t *buffer, int32_t width, int32_t height, int32_t bytewidth, uint8_t bitsPerPixel, uint8_t bytesPerPixel ) { MMBitmapRef bitmap = malloc(sizeof(MMBitmap)); if (bitmap == NULL) { return NULL; } bitmap->imageBuffer = buffer; bitmap->width = width; bitmap->height = height; bitmap->bytewidth = bytewidth; bitmap->bitsPerPixel = bitsPerPixel; bitmap->bytesPerPixel = bytesPerPixel; return bitmap; } void destroyMMBitmap(MMBitmapRef bitmap) { assert(bitmap != NULL); if (bitmap->imageBuffer != NULL) { free(bitmap->imageBuffer); bitmap->imageBuffer = NULL; } free(bitmap); } void destroyMMBitmapBuffer(char * bitmapBuffer, void * hint) { if (bitmapBuffer != NULL) { free(bitmapBuffer); } } ================================================ FILE: base/deadbeef_rand.h ================================================ #ifndef DEADBEEF_RAND_H #define DEADBEEF_RAND_H #include #define DEADBEEF_MAX UINT32_MAX /* Dead Beef Random Number Generator From: http://inglorion.net/software/deadbeef_rand */ /* Generates a random number between 0 and DEADBEEF_MAX. */ uint32_t deadbeef_rand(void); /* Seeds with the given integer. */ void deadbeef_srand(uint32_t x); /* Generates seed from the current time. */ uint32_t deadbeef_generate_seed(void); /* Seeds with the above function. */ #define deadbeef_srand_time() deadbeef_srand(deadbeef_generate_seed()) /* Returns random double in the range [a, b).*/ #define DEADBEEF_UNIFORM(a, b) \ ((a) + (deadbeef_rand() / (((double)DEADBEEF_MAX / (b - a) + 1)))) /* Returns random integer in the range [a, b).*/ #define DEADBEEF_RANDRANGE(a, b) (uint32_t)DEADBEEF_UNIFORM(a, b) #endif /* DEADBEEF_RAND_H */ ================================================ FILE: base/deadbeef_rand_c.h ================================================ #include "deadbeef_rand.h" #include static uint32_t deadbeef_seed; static uint32_t deadbeef_beef = 0xdeadbeef; uint32_t deadbeef_rand(void) { deadbeef_seed = (deadbeef_seed << 7) ^ ((deadbeef_seed >> 25) + deadbeef_beef); deadbeef_beef = (deadbeef_beef << 7) ^ ((deadbeef_beef >> 25) + 0xdeadbeef); return deadbeef_seed; } void deadbeef_srand(uint32_t x) { deadbeef_seed = x; deadbeef_beef = 0xdeadbeef; } /* Taken directly from the documentation: http://inglorion.net/software/cstuff/deadbeef_rand/ */ uint32_t deadbeef_generate_seed(void) { uint32_t t = (uint32_t)time(NULL); uint32_t c = (uint32_t)clock(); return (t << 24) ^ (c << 11) ^ t ^ (size_t) &c; } ================================================ FILE: base/inline_keywords.h ================================================ #pragma once /* A complicated, portable model for declaring inline functions in header files. */ #if !defined(H_INLINE) #if defined(__GNUC__) #define H_INLINE static __inline__ __attribute__((always_inline)) #elif defined(__MWERKS__) || defined(__cplusplus) #define H_INLINE static inline #elif defined(_MSC_VER) #define H_INLINE static __inline #elif TARGET_OS_WIN32 #define H_INLINE static __inline__ #endif #endif /* H_INLINE */ ================================================ FILE: base/microsleep.h ================================================ #pragma once #ifndef MICROSLEEP_H #define MICROSLEEP_H #include "os.h" #include "inline_keywords.h" // todo: removed #if !defined(IS_WINDOWS) /* Make sure nanosleep gets defined even when using C89. */ #if !defined(__USE_POSIX199309) || !__USE_POSIX199309 #define __USE_POSIX199309 1 #endif #include /* For nanosleep() */ #endif /* A more widely supported alternative to usleep(), based on Sleep() in Windows and nanosleep() */ H_INLINE void microsleep(double milliseconds) { #if defined(IS_WINDOWS) Sleep((DWORD)milliseconds); /* (Unfortunately truncated to a 32-bit integer.) */ #else /* Technically, nanosleep() is not an ANSI function */ struct timespec sleepytime; sleepytime.tv_sec = milliseconds / 1000; sleepytime.tv_nsec = (milliseconds - (sleepytime.tv_sec * 1000)) * 1000000; nanosleep(&sleepytime, NULL); #endif } #endif /* MICROSLEEP_H */ ================================================ FILE: base/os.h ================================================ #pragma once #ifndef OS_H #define OS_H #if !defined(IS_MACOSX) && defined(__APPLE__) && defined(__MACH__) #define IS_MACOSX #endif /* IS_MACOSX */ #if !defined(IS_WINDOWS) && (defined(WIN32) || defined(_WIN32) || \ defined(__WIN32__) || defined(__WINDOWS__) || defined(__CYGWIN__)) #define IS_WINDOWS #endif /* IS_WINDOWS */ #if !defined(USE_X11) && !defined(NUSE_X11) && !defined(IS_MACOSX) && !defined(IS_WINDOWS) #define USE_X11 #endif /* USE_X11 */ #if defined(IS_WINDOWS) #define STRICT /* Require use of exact types. */ #define WIN32_LEAN_AND_MEAN 1 /* Speed up compilation. */ #include #elif !defined(IS_MACOSX) && !defined(USE_X11) #error "Sorry, this platform isn't supported yet!" #endif /* Interval to align by for large buffers (e.g. bitmaps). Must be a power of 2. */ #ifndef BYTE_ALIGN #define BYTE_ALIGN 4 /* Bytes to align pixel buffers to. */ /* #include */ /* #define BYTE_ALIGN (sizeof(size_t)) */ #endif /* BYTE_ALIGN */ #if BYTE_ALIGN == 0 /* No alignment needed. */ #define ADD_PADDING(width) (width) #else /* Aligns given width to padding. */ #define ADD_PADDING(width) (BYTE_ALIGN + (((width) - 1) & ~(BYTE_ALIGN - 1))) #endif #if defined(IS_WINDOWS) #if defined (_WIN64) #define RobotGo_64 #else #define RobotGo_32 #endif #else #if defined (__x86_64__) #define RobotGo_64 #else #define RobotGo_32 #endif #endif #endif /* OS_H */ ================================================ FILE: base/pubs.h ================================================ #if defined(IS_WINDOWS) BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { uint32_t *count = (uint32_t*)dwData; (*count)++; return TRUE; } typedef struct{ HWND hWnd; DWORD dwPid; }WNDINFO; BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam){ WNDINFO* pInfo = (WNDINFO*)lParam; DWORD dwProcessId = 0; GetWindowThreadProcessId(hWnd, &dwProcessId); if (dwProcessId == pInfo->dwPid) { pInfo->hWnd = hWnd; return FALSE; } return TRUE; } HWND GetHwndByPid(DWORD dwProcessId) { WNDINFO info = {0}; info.hWnd = NULL; info.dwPid = dwProcessId; EnumWindows(EnumWindowsProc, (LPARAM)&info); return info.hWnd; } #endif ================================================ FILE: base/rgb.h ================================================ #pragma once #ifndef RGB_H #define RGB_H #include /* For abs() */ #include #include "inline_keywords.h" /* For H_INLINE */ #include /* #define MMRGB_IS_BGR (offsetof(MMRGBColor, red) > offsetof(MMRGBColor, blue)) */ #define MMRGB_IS_BGR 1 struct _MMRGBColor { uint8_t blue; uint8_t green; uint8_t red; }; typedef struct _MMRGBColor MMRGBColor; /* MMRGBHex is a hexadecimal color value*/ typedef uint32_t MMRGBHex; #define MMRGBHEX_MIN 0x000000 #define MMRGBHEX_MAX 0xFFFFFF /* Converts rgb color to hexadecimal value. */ #define RGB_TO_HEX(red, green, blue) (((red) << 16) | ((green) << 8) | (blue)) /* Convenience wrapper for MMRGBColors. */ H_INLINE MMRGBHex hexFromMMRGB(MMRGBColor rgb) { return RGB_TO_HEX(rgb.red, rgb.green, rgb.blue); } #define RED_FROM_HEX(hex) ((hex >> 16) & 0xFF) #define GREEN_FROM_HEX(hex) ((hex >> 8) & 0xFF) #define BLUE_FROM_HEX(hex) (hex & 0xFF) /* Converts hexadecimal color to MMRGBColor. */ H_INLINE MMRGBColor MMRGBFromHex(MMRGBHex hex) { MMRGBColor color; color.red = RED_FROM_HEX(hex); color.green = GREEN_FROM_HEX(hex); color.blue = BLUE_FROM_HEX(hex); return color; } /* Check absolute equality of two RGB colors. */ #define MMRGBColorEqualToColor(c1, c2) ((c1).red == (c2).red && \ (c1).blue == (c2).blue && \ (c1).green == (c2).green) /* Returns whether two colors are similar within the given range, |tolerance|.*/ H_INLINE int MMRGBColorSimilarToColor(MMRGBColor c1, MMRGBColor c2, float tolerance) { /* Speedy case */ if (tolerance <= 0.0f) { return MMRGBColorEqualToColor(c1, c2); } else { /* Otherwise, use a Euclidean space to determine similarity */ uint8_t d1 = c1.red - c2.red; uint8_t d2 = c1.green - c2.green; uint8_t d3 = c1.blue - c2.blue; return sqrt((double)(d1 * d1) + (d2 * d2) + (d3 * d3)) <= (tolerance * 442.0f); } } /* Identical to MMRGBColorSimilarToColor, only for hex values. */ H_INLINE int MMRGBHexSimilarToColor(MMRGBHex h1, MMRGBHex h2, float tolerance) { if (tolerance <= 0.0f) { return h1 == h2; } else { int d1 = RED_FROM_HEX(h1) - RED_FROM_HEX(h2); int d2 = GREEN_FROM_HEX(h1) - GREEN_FROM_HEX(h2); int d3 = BLUE_FROM_HEX(h1) - BLUE_FROM_HEX(h2); return sqrt((double)(d1 * d1) + (d2 * d2) + (d3 * d3)) <= (tolerance * 442.0f); } } #endif /* RGB_H */ ================================================ FILE: base/types.h ================================================ #pragma once #ifndef TYPES_H #define TYPES_H #include "os.h" #include "inline_keywords.h" /* For H_INLINE */ #include #include #include /* Some generic, cross-platform types. */ #ifdef RobotGo_64 typedef int64_t intptr; typedef uint64_t uintptr; #else typedef int32_t intptr; typedef uint32_t uintptr; // Unsigned pointer integer #endif struct _MMPointInt32 { int32_t x; int32_t y; }; typedef struct _MMPointInt32 MMPointInt32; struct _MMSizeInt32 { int32_t w; int32_t h; }; typedef struct _MMSizeInt32 MMSizeInt32; struct _MMRectInt32 { MMPointInt32 origin; MMSizeInt32 size; }; typedef struct _MMRectInt32 MMRectInt32; H_INLINE MMPointInt32 MMPointInt32Make(int32_t x, int32_t y) { MMPointInt32 point; point.x = x; point.y = y; return point; } H_INLINE MMSizeInt32 MMSizeInt32Make(int32_t w, int32_t h) { MMSizeInt32 size; size.w = w; size.h = h; return size; } H_INLINE MMRectInt32 MMRectInt32Make(int32_t x, int32_t y, int32_t w, int32_t h) { MMRectInt32 rect; rect.origin = MMPointInt32Make(x, y); rect.size = MMSizeInt32Make(w, h); return rect; } #define MMPointZero MMPointInt32Make(0, 0) #if defined(IS_MACOSX) #define CGPointFromMMPointInt32(p) CGPointMake((CGFloat)(p).x, (CGFloat)(p).y) #define MMPointInt32FromCGPoint(p) MMPointInt32Make((int32_t)(p).x, (int32_t)(p).y) #elif defined(IS_WINDOWS) #define MMPointInt32FromPOINT(p) MMPointInt32Make((int32_t)p.x, (int32_t)p.y) #endif #endif /* TYPES_H */ ================================================ FILE: base/xdisplay_c.h ================================================ #include /* For fputs() */ #include /* For atexit() */ #include static Display *mainDisplay = NULL; static int registered = 0; static char *displayName = NULL; static int hasDisplayNameChanged = 0; void XCloseMainDisplay(void) { if (mainDisplay != NULL) { XCloseDisplay(mainDisplay); mainDisplay = NULL; } } Display *XGetMainDisplay(void) { /* Close the display if displayName has changed */ if (hasDisplayNameChanged) { XCloseMainDisplay(); hasDisplayNameChanged = 0; } if (mainDisplay == NULL) { /* First try the user set displayName */ mainDisplay = XOpenDisplay(displayName); /* Then try using environment variable DISPLAY */ if (mainDisplay == NULL && displayName != NULL) { mainDisplay = XOpenDisplay(NULL); } /* Fall back to the most likely :0.0*/ if (mainDisplay == NULL) { mainDisplay = XOpenDisplay(":0.0"); } if (mainDisplay == NULL) { fputs("Could not open main display\n", stderr); } else if (!registered) { atexit(&XCloseMainDisplay); registered = 1; } } return mainDisplay; } void setXDisplay(char *name) { displayName = strdup(name); hasDisplayNameChanged = 1; } char *getXDisplay(void) { return displayName; } ================================================ FILE: clipboard/README.md ================================================ This directory based on [clipboard](https://github.com/atotto/clipboard). [![Build Status](https://travis-ci.org/atotto/clipboard.svg?branch=master)](https://travis-ci.org/atotto/clipboard) [![GoDoc](https://godoc.org/github.com/atotto/clipboard?status.svg)](http://godoc.org/github.com/atotto/clipboard) # Clipboard for Go Provide copying and pasting to the Clipboard for Go. Build: $ go get github.com/atotto/clipboard Platforms: * OSX * Windows 7 (probably work on other Windows) * Linux, Unix (requires 'xclip' or 'xsel' command to be installed) Document: * http://godoc.org/github.com/atotto/clipboard Notes: * Text string only * UTF-8 text encoding only (no conversion) TODO: * Clipboard watcher(?) ## Commands: paste shell command: $ go get github.com/atotto/clipboard/cmd/gopaste $ # example: $ gopaste > document.txt copy shell command: $ go get github.com/atotto/clipboard/cmd/gocopy $ # example: $ cat document.txt | gocopy ================================================ FILE: clipboard/clipboard.go ================================================ // Copyright 2013 @atotto. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. /* Package clipboard read/write on clipboard */ package clipboard // import () // ReadAll read string from clipboard func ReadAll() (string, error) { return readAll() } // WriteAll write string to clipboard func WriteAll(text string) error { return writeAll(text) } // Unsupported might be set true during clipboard init, // to help callers decide whether or not to // offer clipboard options. var Unsupported bool ================================================ FILE: clipboard/clipboard_darwin.go ================================================ // Copyright 2013 @atotto. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build darwin // +build darwin package clipboard import ( "os/exec" ) var ( pasteCmdArgs = "pbpaste" copyCmdArgs = "pbcopy" ) func getPasteCommand() *exec.Cmd { return exec.Command(pasteCmdArgs) } func getCopyCommand() *exec.Cmd { return exec.Command(copyCmdArgs) } func readAll() (string, error) { pasteCmd := getPasteCommand() out, err := pasteCmd.Output() if err != nil { return "", err } return string(out), nil } func writeAll(text string) error { copyCmd := getCopyCommand() in, err := copyCmd.StdinPipe() if err != nil { return err } if err := copyCmd.Start(); err != nil { return err } if _, err := in.Write([]byte(text)); err != nil { return err } if err := in.Close(); err != nil { return err } return copyCmd.Wait() } ================================================ FILE: clipboard/clipboard_test.go ================================================ // Copyright 2013 @atotto. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build darwin || windows // +build darwin windows package clipboard_test import ( "testing" "github.com/go-vgo/robotgo/clipboard" ) func TestCopyAndPaste(t *testing.T) { expected := "日本語" err := clipboard.WriteAll(expected) if err != nil { t.Fatal(err) } actual, err := clipboard.ReadAll() if err != nil { t.Fatal(err) } if actual != expected { t.Errorf("want %s, got %s", expected, actual) } } func TestMultiCopyAndPaste(t *testing.T) { expected1 := "French: éèêëàùœç" expected2 := "Weird UTF-8: 💩☃" err := clipboard.WriteAll(expected1) if err != nil { t.Fatal(err) } actual1, err := clipboard.ReadAll() if err != nil { t.Fatal(err) } if actual1 != expected1 { t.Errorf("want %s, got %s", expected1, actual1) } err = clipboard.WriteAll(expected2) if err != nil { t.Fatal(err) } actual2, err := clipboard.ReadAll() if err != nil { t.Fatal(err) } if actual2 != expected2 { t.Errorf("want %s, got %s", expected2, actual2) } } func BenchmarkReadAll(b *testing.B) { for i := 0; i < b.N; i++ { clipboard.ReadAll() } } func BenchmarkWriteAll(b *testing.B) { text := "いろはにほへと" for i := 0; i < b.N; i++ { clipboard.WriteAll(text) } } ================================================ FILE: clipboard/clipboard_unix.go ================================================ // Copyright 2013 @atotto. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build freebsd || linux || netbsd || openbsd || solaris || dragonfly // +build freebsd linux netbsd openbsd solaris dragonfly package clipboard import ( "errors" "os/exec" ) const ( xsel = "xsel" xclip = "xclip" ) var ( // Primary choose primary mode on unix Primary bool pasteCmdArgs, copyCmdArgs []string xselPasteArgs = []string{xsel, "--output", "--clipboard"} xselCopyArgs = []string{xsel, "--input", "--clipboard"} xclipPasteArgs = []string{xclip, "-out", "-selection", "clipboard"} xclipCopyArgs = []string{xclip, "-in", "-selection", "clipboard"} errMissingCommands = errors.New("No clipboard utilities available. Please install xsel or xclip") ) func init() { pasteCmdArgs = xclipPasteArgs copyCmdArgs = xclipCopyArgs if _, err := exec.LookPath(xclip); err == nil { return } pasteCmdArgs = xselPasteArgs copyCmdArgs = xselCopyArgs if _, err := exec.LookPath(xsel); err == nil { return } Unsupported = true } func getPasteCommand() *exec.Cmd { if Primary { pasteCmdArgs = pasteCmdArgs[:1] } return exec.Command(pasteCmdArgs[0], pasteCmdArgs[1:]...) } func getCopyCommand() *exec.Cmd { if Primary { copyCmdArgs = copyCmdArgs[:1] } return exec.Command(copyCmdArgs[0], copyCmdArgs[1:]...) } func readAll() (string, error) { if Unsupported { return "", errMissingCommands } pasteCmd := getPasteCommand() out, err := pasteCmd.Output() if err != nil { return "", err } return string(out), nil } func writeAll(text string) error { if Unsupported { return errMissingCommands } copyCmd := getCopyCommand() in, err := copyCmd.StdinPipe() if err != nil { return err } if err := copyCmd.Start(); err != nil { return err } if _, err := in.Write([]byte(text)); err != nil { return err } if err := in.Close(); err != nil { return err } return copyCmd.Wait() } ================================================ FILE: clipboard/clipboard_windows.go ================================================ // Copyright 2013 @atotto. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build windows // +build windows package clipboard import ( "syscall" "time" "unsafe" ) const ( cfUnicodetext = 13 // gmemFixed = 0x0000 gmemMoveable = 0x0002 ) var ( user32 = syscall.MustLoadDLL("user32") openClipboard = user32.MustFindProc("OpenClipboard") closeClipboard = user32.MustFindProc("CloseClipboard") emptyClipboard = user32.MustFindProc("EmptyClipboard") getClipboardData = user32.MustFindProc("GetClipboardData") setClipboardData = user32.MustFindProc("SetClipboardData") kernel32 = syscall.NewLazyDLL("kernel32") globalAlloc = kernel32.NewProc("GlobalAlloc") globalFree = kernel32.NewProc("GlobalFree") globalLock = kernel32.NewProc("GlobalLock") globalUnlock = kernel32.NewProc("GlobalUnlock") lstrcpy = kernel32.NewProc("lstrcpyW") ) // waitOpenClipboard opens the clipboard, waiting for up to a second to do so. func waitOpenClipboard() error { started := time.Now() limit := started.Add(time.Second) var ( r uintptr err error ) for time.Now().Before(limit) { r, _, err = openClipboard.Call(0) if r != 0 { return nil } time.Sleep(time.Millisecond) } return err } func readAll() (string, error) { // r, _, err := openClipboard.Call(0) err := waitOpenClipboard() if err != nil { return "", err } defer closeClipboard.Call() h, _, err := getClipboardData.Call(cfUnicodetext) if h == 0 { return "", err } l, _, err := globalLock.Call(h) if l == 0 { return "", err } text := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(l))[:]) r, _, err := globalUnlock.Call(h) if r == 0 { return "", err } return text, nil } func writeAll(text string) error { err := waitOpenClipboard() if err != nil { return err } defer closeClipboard.Call() r, _, err := emptyClipboard.Call(0) if r == 0 { return err } data := syscall.StringToUTF16(text) // "If the hMem parameter identifies a memory object, the object must have // been allocated using the function with the GMEM_MOVEABLE flag." h, _, err := globalAlloc.Call(gmemMoveable, uintptr(len(data)*int(unsafe.Sizeof(data[0])))) if h == 0 { return err } defer func() { if h != 0 { globalFree.Call(h) } }() l, _, err := globalLock.Call(h) if l == 0 { return err } r, _, err = lstrcpy.Call(l, uintptr(unsafe.Pointer(&data[0]))) if r == 0 { return err } r, _, err = globalUnlock.Call(h) if r == 0 { if err.(syscall.Errno) != 0 { return err } } r, _, err = setClipboardData.Call(cfUnicodetext, h) if r == 0 { return err } h = 0 // suppress deferred cleanup return nil } ================================================ FILE: clipboard/cmd/gocopy/gocopy.go ================================================ package main import ( "io" "os" "github.com/go-vgo/robotgo/clipboard" ) func main() { out, err := io.ReadAll(os.Stdin) if err != nil { panic(err) } if err := clipboard.WriteAll(string(out)); err != nil { panic(err) } } ================================================ FILE: clipboard/cmd/gopaste/gopaste.go ================================================ package main import ( "fmt" "github.com/go-vgo/robotgo/clipboard" ) func main() { text, err := clipboard.ReadAll() if err != nil { panic(err) } fmt.Print(text) } ================================================ FILE: clipboard/example/example.go ================================================ package main import ( "log" "github.com/go-vgo/robotgo/clipboard" ) func main() { err := clipboard.WriteAll("日本語") if err != nil { log.Println("clipboard write all error: ", err) } text, err := clipboard.ReadAll() if err != nil { log.Println("clipboard read all error: ", err) return } if text != "" { log.Println("text is: ", text) // Output: 日本語 } } ================================================ FILE: cv/README.md ================================================ robotgo gocv ================================================ FILE: cv/gocv.go ================================================ package cv ================================================ FILE: doc.go ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. package robotgo /* Keys are supported: "A-Z a-z 0-9" "backspace" "delete" "enter" "tab" "esc" "escape" "up" Up arrow key "down" Down arrow key "right" Right arrow key "left" Left arrow key "home" "end" "pageup" "pagedown" "f1" "f2" "f3" "f4" "f5" "f6" "f7" "f8" "f9" "f10" "f11" "f12" "f13" "f14" "f15" "f16" "f17" "f18" "f19" "f20" "f21" "f22" "f23" "f24" "cmd" this is the "win" key for windows "lcmd" left command "rcmd" right command // "command" "alt" "lalt" left alt "ralt" right alt "ctrl" "lctrl" left ctrl "rctrl" right ctrl "control" "shift" "lshift" left shift "rshift" right shift // "right_shift" "capslock" "space" "print" "printscreen" // No Mac support "insert" "menu" Windows only "audio_mute" Mute the volume "audio_vol_down" Lower the volume "audio_vol_up" Increase the volume "audio_play" "audio_stop" "audio_pause" "audio_prev" Previous Track "audio_next" Next Track "audio_rewind" Linux only "audio_forward" Linux only "audio_repeat" Linux only "audio_random" Linux only "num0" "num1" "num2" "num3" "num4" "num5" "num6" "num7" "num8" "num9" "num_lock" "num." "num+" "num-" "num*" "num/" "num_clear" "num_enter" "num_equal" "lights_mon_up" Turn up monitor brightness No Windows support "lights_mon_down" Turn down monitor brightness No Windows support "lights_kbd_toggle" Toggle keyboard backlight on/off No Windows support "lights_kbd_up" Turn up keyboard backlight brightness No Windows support "lights_kbd_down" Turn down keyboard backlight brightness No Windows support */ /* ## Type Conversion | | type conversion | func |-----|---------------------|---------------------- | * | robotgo.Bitmap -> robotgo.CBitmap | robotgo.ToCBitmap() | | robotgo.Bitmap -> *image.RGBA | robotgo.ToRGBAGo() | * | robotgo.CBitmap -> C.MMBitmapRef | robotgo.ToMMBitmapRef() | | robotgo.CBitmap -> robotgo.Bitmap | robotgo.ToBitmap() | | robotgo.CBitmap -> image.Image | robotgo.ToImage() | | robotgo.CBitmap -> *image.RGBA | robotgo.ToRGBA() | * | C.MMBitmapRef -> robotgo.CBitmap | robotgo.CBitmap() | * | image.Image -> robotgo.Bitmap | robotgo.ImgToBitmap() | | image.Image -> robotgo.CBitmap | robotgo.ImgToCBitmap() | | image.Image -> []byte | robotgo.ToByteImg() | | image.Image -> string | robotgo.ToStringImg() | * | *image.RGBA -> robotgo.Bitmap | robotgo.RGBAToBitmap() | * | []byte -> image.Image | robotgo.ByteToImg() | | []byte-> robotgo.CBitmap | robotgo.ByteToCBitmap() | | []byte -> string | string() | * | string -> image.Image | robotgo.StrToImg() | | string -> byte | []byte() */ ================================================ FILE: docs/CHANGELOG.md ================================================ # CHANGELOG ## RobotGo v0.100.0, MT. Baker; Enhancement bitmap and image, add arm support... ### Add - [NEW] add more image function support - [NEW] add ImgToBitmap(), ToImg(), FindEveryBitmap(), FindEveryColor(), Svae(), Read(), SaveJpeg() and other function support - [NEW] add func ToImage: convert C.MMBitmapRef to standard image.Image - [NEW] add ToImage examples code - [NEW] add more image function and update go mod - [NEW] add find every color function - [NEW] add free and find all point function - [NEW] update find bitmap and color - [NEW] add byte to image function - [NEW] add more image convert function - [NEW] add mac os M1 support - [NEW] add windows arm support - [NEW] add more key toggle and press function - [NEW] add ToRGBA() function support - [NEW] add ImgToBitmap and RGBAToBitmap support - [NEW] Update and move image function to img.go - [NEW] add more img to bitmap examples code and Update file name ### Update - [NEW] Update README.md and CHANGELOG.md - [NEW] add macOS to .travis.yml - [NEW] update go mod pkg - [NEW] Update hook examples link to newest - [NEW] update dockerfile and appveyor to go1.14.4 - [NEW] Update README.md, add more examples - [NEW] move hook and event to gohook - [NEW] move cbitmap and bitmap-bytes to bitmap dir - [NEW] update some name - [NEW] update dockerfile and appveyor.yml - [NEW] update clipboard code - [NEW] update hook code and more API - [NEW] optimize code and update version - [NEW] add paste string err return and optimize code - [NEW] update go.yml and travis.yml to go1.15 - [NEW] Update Ubuntu apt-get to apt - [NEW] update go version and key code - [NEW] update test code and go mod - [NEW] Update README.md and test code - [NEW] update parameter name and version - [NEW] update dockerfile and appveyor.yml - [NEW] update error return and print - [NEW] update ShowAlert optimize return code - [NEW] add more test and update go mod - [NEW] compatible with 32-bit platforms - [NEW] add more bitmap examples - [NEW] update point structure to public - [NEW] add more examples - [NEW] update examples and version - [NEW] Update clipboard example code - [NEW] Update README.md Section ####Other to windows (#348) … - [NEW] Update png.h path - [NEW] Update go mod - [NEW] Update circle.yml and travis.yml - [NEW] Remove unless example code and update circle.yml - [NEW] Removed drop api example code and Update README.md - [NEW] Update go mod and xx.yml - [NEW] Update README.md and example - [NEW] add more bitmap examples code - [NEW] Update go mod and Update README.md - [NEW] gofmt to 1.17 build tag - [NEW] Update bitmap examples code - [NEW] Update version and keycode - [NEW] Update docs remove drop API ### Fixed - [FIX] Update go mod and fixed #290 - [FIX] Update gohook to v0.30.2 fixed bug - [FIX] Fixed Mouse buttons reversed type - [FIX] Fixed returns "Invalid key code specified." if specified character is not v… … add keyCodeForCharFallBack - [FIX] This fixes the disappearing backslash issue #351 - [FIX] Export ToUC function and update test code - [FIX] Fixes #258: char* arrays in C not being copied correctly - [FIX] Fixed Linux TypeStr() function double quote - [FIX] update free bitmap fixed #333 - [FIX] update gops to v0.20.0 fixed bug and other mod pkg - [FIX] update gohook fixed warning ## RobotGo v0.90.0, MT. Rainier ### Add add gohook modern and concurrent API add new gohook examples, thks for cauefcr Support for multiple screens add getMousePos() multiple screens support add move smooth multiple screens support add all platform system scale support add get screen size test code add screen and bitmap multiple screens support add int32_t types support update keycode type use uint16 with gohook, not type convert add ToBitmapBytes func (#204) gohook: sched_yield support for non-POSIX windows gcc add gops test code support add Process() function test code add more gops test code add more win32 function export add get mouse color function add uint32 to Chex function support add key_Toggles() c function add keyTap and keyToggle "...string" parameters support, Fixed #209 add robotgo simple test code add Is64Bit() c and go function add process FindPath() function add keycode "delete" support and fixed "\\" error add more keycode support, "up, down, left, right"... export hook keycode and add godoc use robotn fork xgb and update go mod add hook example to robotgo examples update gohook and tt mod file add more and update test code add drag smooth function support and examples add ShowAlert() test support update keypress rand sleep [reduce] and update code style, update c delay default value to 0 add mouse toggle return and add more test add SetDelay function code and update other code add scaled function code add go opencv file add readme.md file add move mouse and move smooth relative code add move mouse and move smooth relative examples add more test code and update go tt mod add more bitmap test code add SaveImg function code add drop function hint print support add more key test code add more test code add paste string test code add xvfb run codecov test add keycode test support add FindPath example code add KeyTap() args[2] delay support add find bitmap nil args support add find color nil args support add drag and move mouse multiple screens support add drag mouse test code Use CGDisplayBounds not CGDisplayPixelsWide, optimize get mac display size … Update TypeStr function, add type delay and speed support update PasteStr function code return error ### Update Update robot info test code and Add go.yml test support use while not for match special key map remove unless x11 special key and sort update go mod pkg update mod vendor remove vendor and update .gitignore update and fmt config.yml, add Linux go test support update Linux CI support x11 test move hook to hook.go update appveyor and test code update version and code style update move mouse smooth test code update clipboard code and add test code update test code and add codecov support update show alert test code update keycode.go update window examples code update test code remove windows alert test move gops code to ps.go update version update unix get title type gofmt go code and update code style add ToBitmapBytes examples code update example code, fixed golint warning update bitmap example code Update CHANGELOG.md update code style update godoc update keytap code and code style update Bitmap struct delete fuzzy api update key examples code add bitmap from string clear api update go mod vendor update go mod pkg not proxy update bitmap example code update test code fixed appveyor CI update test code fixed equal error update hook godoc update event example code update godoc and code style update key example code Update example README.md update and tidy go mod update code remove duplicate code and update godoc update xgb getXid log update GetBounds x11 error log update cgo code and version update TypeString function code [Drop] update key example code Update TypeStr function, optimize x11 type string Update TypeStrDelay function, remove unused code update code fixed x11 type sleep Update key example code use gops to simplify code update key examples code update bitmap examples code update colorpicker and findcolor example code update bitmap example code update robotgo test code, add more test Update README.md rename type names make clearer update types.h code and fixed bug remove unused code fixed x11 build error update robot info test code and appveyor Update README.md, Add more CI badge update gohook pkg and robot info test code Update linux upper code, add more special key support Create go.yml Update go.yml add more test and update go.yml Update dockerfile to go1.13.5 update dockerfile and appveyor.yml Update dockerfile and appveyor.yml to go1.14.3 remove Travis go1.11.x update appveyor and dockerfile to go1.13.1 update dockerfile, go.yml and appveyor.yml to go1.14 update travis.yml to go1.14.x and remove go1.13.x Update and fmt appveyor.ymlu update dockerfile and appveyor to go1.12.5 update appveyor and dockerfile to go1.12.6 add CI go1.13 support update config.yml update and fmt travis.yml Update Travis remove go1.12.x Update issue and pull request template ### Fix Update to utf-code function Fixed #189 Update x11 keypress upper code Fixed #243 type conversion needed in addMouse (#201) update hook, Fixed #202 fatal error: concurrent map writes add key Kind Fixed #203 optimize get title code, Fixed #165 and typo Fixed gohook#3 mouse is_drag error on x11 Fixed #213 AddEvents() can't listen correctly multiple times update clipboard error hand Fixed #212 Update go.mod fixing issue "invalid pseudo-version: does not match version … update keyboard example code, #238 update go mod file Fixed #239 update gops and other mod files fixed bug ## RobotGo v0.80.0, Sierra Nevada # Sierra Nevada ### Add - [NEW] Add asynchronous event support - [NEW] Add multiple keypress event listener support - [NEW] Add hook start and end func - [NEW] Add AddEvents, AddMouse, AddMousePos hook function - [NEW] Add mul() scale func and optimize code - [NEW] Refactor AddEvent() func and add keycode.go, update example - [NEW] Add mouse map keycode - [NEW] Add android null file - [NEW] Add AddEvent "center" support - [NEW] Update README.md, Add binding link
- [NEW] Format README.md and docs markdown - [NEW] Update bitmap_save return code - [NEW] Optimize code not defer and remove useless code - [NEW] Update code style and godoc - [NEW] Update go mod vendor - [NEW] Add more event examples - [NEW] add AddEvents, AddMouse, AddMousePos examples code ### Update - [NEW] Update event example code add print hint - [NEW] Update godoc - [NEW] Update CHANGELOG.md - [NEW] Update .gitignore - [NEW] Update code style and examples - [NEW] Update pkg to newest - [NEW] Update CI add go1.12.x support - [NEW] Move GetText() func code ### Fix - [FIX] Add AddEvents func, Fixed #98, #61, #69... - [FIX] Add asynchronous event support, Fixed #196, #89... - [FIX] add AddMouse func, Fixed #138 - [FIX] Update _Ctype_char to C.char, Fixed go1.12 build error #191 - [FIX] Update hook, Fixed #195 warning and json break bug - [FIX] Fixed color picker, Update README.md and docs See Commits for more details, after Jan 7. ## RobotGo v0.70.0, Caloris Montes # Caloris Montes ### Add - [NEW] Update keyboard use sendInput not keybd_event - [NEW] Update mouse use sendInput not mouse_event - [NEW] Add drag mouse other button support - [NEW] Add more numpad key support - [NEW] Add numpad key and escape abbreviation support - [NEW] Add new window10 zoom ratio - [NEW] Add linux numpad key support - [NEW] Add key "insert, printscreen" mac support - [NEW] Add check mouse button func - [NEW] Add keyTap run error return support and update godoc
- [NEW] Optimize and clearer keytap code - [NEW] Optimize and clean keyToggle code - [NEW] Update dockerfile clean image - [NEW] Add color picker and getMousePos() example - [NEW] Use go mod not dep, add go.mod remove dep files - [NEW] Add GetColors func return string - [NEW] Optimize defer code
- [NEW] Add more godoc - [NEW] Add add key "ctrl", "cmd" [ abbreviation ] support - [NEW] Add add key "capslock", "numpad_lock" support - [NEW] Add left and right "Ctrl, Shift, Alt, command" key support - [NEW] Update check key flags support "cmd" and "ctrl" - [NEW] Update key docs - [NEW] Add millisleep func and update godoc - [NEW] Add AddEvent() key "cmd" support - [NEW] Update key example code - [NEW] Update README.md, add Note go1.10.x issue - [NEW] Update keytap and toggle return "" and code style ### Update - [NEW] Update issue template more obvious - [NEW] Update godoc - [NEW] Update CHANGELOG.md - [NEW] Update .gitignore - [NEW] Update code style and examples - [NEW] Update pkg to newest - [NEW] Add more scale example - [NEW] Add drag mouse example
- [NEW] Update key docs and clear file name - [NEW] Remove old useless code - [NEW] Update README.md - [NEW] Update CI add go1.11.4 version ### Fix - [FIX] Fixed bitmapClick() parameter bug - [FIX] Fixed some README.md typo - [FIX] Update scale example code close #153 - [FIX] Update code style and fixed #endif error See Commits for more details, after Otc 8. ## RobotGo v0.60.0, Mount Olympus: Mytikas ### Add - [NEW] Add GetBounds func support (add get_client and get_frame C_func) - [NEW] Add GetXId and GetXidFromPid func - [NEW] Refactoring GetTitle() func allow by pid - [NEW] Refactoring CloseWindow() allow by pid - [NEW] Add SetHandPid() and GetHandPid() func support - [NEW] Add FindCBitmap func support
- [NEW] Refactoring bitmap example code - [NEW] Refactoring key example code - [NEW] Refactoring window example code - [NEW] Add an cbitmap example [#169] - [NEW] Refactoring screen and event example code - [NEW] Refactoring mouse example code
- [NEW] Add more godoc - [NEW] Add getTitle example by pid - [NEW] Add close window example by pid - [NEW] Add getBounds example - [NEW] Split func and remove dep more clean - [NEW] Simplify SaveCapture code - [NEW] Update and merged get_pixel_color remove duplicate code - [NEW] Update README.md, add Note go1.10.x ### Update - [NEW] Update issue template more obvious - [NEW] Move public mdata to pub - [NEW] Update godoc - [NEW] Update CHANGELOG.md - [NEW] Move some pub method to pub.h and rename some c_func - [NEW] Update code style and name style ( key, window and other ) - [NEW] Update robotgo unix export getXidFromPid func - [NEW] Update set handle return use bool
- [NEW] Update code style and move scale to win_sys.h - [NEW] Update example add more lang - [NEW] Update pkg to newest - [NEW] Remove duplicate code and old useless code - [NEW] Update and clean README.md - [NEW] Update CI add go1.11.x version - [NEW] Update scroll godoc and clearer parameter name - [NEW] Update hint and code style - [NEW] Update FindIds doc and only set name once in loop ### Fix - [FIX] Update type_string fixed #155, fixed window missing some character - [FIX] Fixed GetWindowText return address of local variable and not use ternary operator ( GetTittle ) - [FIX] Update README.md Fixed Release badge See Commits for more details, after Aug 8. ## RobotGo v0.50.0, The Appalachian Mountains ## Add - [NEW] Add simple ocr support - [NEW] Add max and min window api and win32.h file - [NEW] Automatic free internal bitmap and add bitmapStr example - [NEW] Update findBitmap and findColor default tolerance 0.5 to 0.01, [improve find accuracy and time] - [NEW] Add more Window example - [NEW] Add cross compile docs - [NEW] Add free bitmap and tolerance godoc - [NEW] Add GetForegroundWindow and FindWindow func support - [NEW] Add bitmap to CBitmap func, Add ToCBitmap example to examples - [NEW] Add get Scale and GetScaleSize func, get primary display DPI scale factor fix #129, #135 Add Custom DPI Scaling support, Add scale default value, Add scale example ## Update - [NEW] Update README.md [add freeBitmap example] - [NEW] Optimize findColor and uniform API with findBitmap - [NEW] Update godoc, CI and README.md - [NEW] Update CHANGELOG.md - [NEW] Update examples [add freeBitmap and update findColor] - [NEW] Optimize bitmap code, optimize args and not try [many methods] - [NEW] Update getPid type to int32 - [NEW] Update var and other code style, fix non-standard code Update code and update some name - [NEW] Update pkg to newest - [NEW] Remove duplicate code and old useless code - [NEW] Update mouse click and fix moveClick and movesClick args - [NEW] Update code style use if not try - [NEW] Update clipboard example - [NEW] Update typestr use return not else - [NEW] Update mouse toggle, keytap and savebitmap func args - [NEW] Update examples remove duplicate code - [NEW] Update bitmap and other examples - [NEW] Simplify linux dependency installation commands - [NEW] Update issue_template.md -[NEW] Update pull_request_template.md - [NEW] Move govendor to dep - [NEW] Update robotgo ci to 1.10.3 ## Fix - [FIX] Update active pid to fix #140, fixed linux activePid - [FIX] Fixed findBitmap and findPic memory leak - [FIX] Add getPxColor destroyMMBitmap fix memory leak - [FIX] Fix float args not float32 - [FIX] Fix windows clipboard memory leak - [FIX] Update macos .a downgrade to 10.10 just warning not exit [fix #102, #128, #134] - [FIX] use 10.10 to compile .a verifyed multi os - [FIX] Fix #145 not assert - [FIX] Fix some warning use supplemental code See Commits for more details, after Apr 30. ## RobotGo v0.49.0, Olympus Mons ### Add - [NEW] Add get image size func - [NEW] Add linux type string utf-8 support - [NEW] Add scroll mouse support x, y - [NEW] Add AddEvent() "esc" support fix #105 - [NEW] Add AddEvent "space" fix #110 - [NEW] Add clipboard choose primary mode on unix - [NEW] Add move smooth return - [NEW] Add more bitmap func and examples - [NEW] Add MicroSleep func - [NEW] Add find image by path ### Update - [NEW] Update KeyToggle code - [NEW] Update activePid allow Windows via hwnd - [NEW] Update godoc and README.md - [NEW] Update CHANGELOG.md - [NEW] Update Kill() parameter and examples - [NEW] Update examples and remove useless function - [NEW] Update appveyor, circle and dockerfile - [NEW] Update code style - [NEW] Update and optimize func - [NEW] Update travis support go 1.10 - [NEW] Update CI (use custom go image) and add func internalFindBitmap - [NEW] Update godoc and deprecated GetBHandle - [NEW] Optimize code func args and name ### Fix - [FIX] Fix mac input method keytap not work - [FIX] Fix clipboard golint - [FIX] Update move smooth fix #96 (set mouse smooth speed) - [FIX] Fix Getportion param to go type - [FIX] Fix XFlush wait for events flushing See Commits for more details, after Jan 25. ## RobotGo v0.48.0, Ben Nevis ### Add - [NEW] Add active window by name func ActiveName - [NEW] Add type string utf-8 support Add func CharCodeAt, UnicodeType, PasteStr and update TypeStr, TypeString - [NEW] Add count of bitmap func CountBitmap - [NEW] Add func SaveCapture and examples - [NEW] Add time sleep func Sleep - [NEW] Add more key listen - [NEW] Add func PointInBounds and examples - [NEW] Add func GetPxColor return C.MMRGBHex - [NEW] Add FindColorCS param tolerance - [NEW] Add func ToBitmap and examples - [NEW] Add CBitmap type and examples - [NEW] Add more examples - [NEW] Add func ToMMBitmapRef - [NEW] Add func BitmapClick and MovesClick - [NEW] Add func ToMMRGBHex convert color hex - [NEW] Add func count bitmap color and CountColorCS - [NEW] Add more color processing and conversion Add func ToMMRGBHex, U32ToHex, U8ToHex, PadHex, HexToRgb, RgbToHex and examples - [NEW] Add func tochar bitmap and gostring and fmt code ### Update - [NEW] Remove robot and examples - [NEW] Update vendor and appveyor.yml - [NEW] Update keyboard code - [NEW] Update godoc - [NEW] Update CHANGELOG.md - [NEW] Change TostringBitmap return string - [NEW] Update C language code and other naming - [NEW] Update code and code style - [NEW] Update move mouse smooth ### Fix - [FIX] Fix mac set active and active by pid - [FIX] Fix windows active by pid #101 - [FIX] Fix FindColor param tolerance - [FIX] Fix find bitmap float args - [FIX] Fix some range error - [FIX] Update doc fix #97 - [FIX] Update README.md fix link error See Commits for more details, after Dec 13. ## RobotGo v0.47.0, Mount Cook ### Add - [NEW] Add windows 32bit and 64bit dependency - [NEW] Add macOs dependency - [NEW] Add pkg to vendor Solve the problem of dependence, remove zlib/libpng dependencies - [NEW] Add FindColorCS(x, y, w, h int, color CHex), CHex type and examples #84 - [NEW] Add kill the process - [NEW] Add public event and update code - [NEW] Add Windows 32bit and 64bit Appveyor CI ### Update - [NEW] Update png io - [NEW] Update cgo link - [NEW] Update .gitignore - [NEW] Update README.md and godoc - [NEW] Update CHANGELOG.md - [NEW] Update circle to 2.0, add robotgo Dockerfile custom image - [NEW] Update and fmt C code - [NEW] Update GetTitle default value "null" to "" ### Fix - [FIX] Fix FindColor inconvenient parameters - [FIX] Fix installation requirements #72 - [FIX] Fix GetTitle `return address of local variable` in the higher gcc version. #81 See Commits for more details, after Nov 10. ## RobotGo v0.46.6, Pyrenees Mountains: Aneto Peak ## RobotGo v0.46.0, Pyrenees Mountains ### Add - [NEW] Add ActivePID - [NEW] Add FindBit - [NEW] Add robot branch, where there is no zlib and libpng dependency ### Update - [NEW] Update README.md - [NEW] Update FindIds - [NEW] Update examples - [NEW] Update vendor - [NEW] Update godoc and docs - [NEW] Update and fix bitmap ### Fix - [FIX] Fix MoveMouseSmooth args - [FIX] Fix name err - [FIX] Fix FindBitmap ## RobotGo v0.45.0, Mount Qomolangma ### Add - [NEW] Add Process - [NEW] Add TypeStr - [NEW] Add DeepCopyBit - [NEW] Add CopyBitpb - [NEW] Add ReadBitmap - [NEW] Add vendor.json - [NEW] Add ReadAll: clipboard - [NEW] Add WriteAll: clipboard - [NEW] Add Pids : get the all process id - [NEW] Add FindName: find the process name by the process id - [NEW] Add FindNames: find the all process name - [NEW] Add PidExists: determine whether the process exists - [NEW] Add FindIds: find the process id by the process name - [NEW] Add FreeBitmap and Update docs ### Update - [NEW] Update docs - [NEW] Update test - [NEW] Update godoc - [NEW] Update CHANGELOG.md - [NEW] Update .gitignore - [NEW] Update examples and docs - [NEW] Update examples link - [NEW] Update README.md and clipboard ### Fix - [FIX] Fix release key - [FIX] Fix godoc error ## RobotGo v0.44.0, Mount Kailash ### Add - Add CHANGELOG.md - Format some code - Add fedora dependencies ### Update - Update test - Update keys.md - Update and Split example - Update godoc and docs - Update and Cleanup README.md - Update CONTRIBUTING.md and issue_template.md ### Fix - Fix typesetting and MD error - Fix fedora dependencies #55 - Fix doc.md and README.md ================================================ FILE: docs/README.md ================================================ # Docs Documents are not necessarily updated synchronously, slower than godoc, please see examples and godoc. ================================================ FILE: docs/archive/README_zh.md ================================================ # Robotgo # !!! Warning: this page not updated !!! [![Build Status](https://github.com/go-vgo/robotgo/workflows/Go/badge.svg)](https://github.com/go-vgo/robotgo/commits/master) [![CircleCI Status](https://circleci.com/gh/go-vgo/robotgo.svg?style=shield)](https://circleci.com/gh/go-vgo/robotgo) [![Build Status](https://travis-ci.org/go-vgo/robotgo.svg)](https://travis-ci.org/go-vgo/robotgo) ![Appveyor](https://ci.appveyor.com/api/projects/status/github/go-vgo/robotgo?branch=master&svg=true) [![Go Report Card](https://goreportcard.com/badge/github.com/go-vgo/robotgo)](https://goreportcard.com/report/github.com/go-vgo/robotgo) [![GoDoc](https://godoc.org/github.com/go-vgo/robotgo?status.svg)](https://godoc.org/github.com/go-vgo/robotgo) [![GitHub release](https://img.shields.io/github/release/go-vgo/robotgo.svg)](https://github.com/go-vgo/robotgo/releases/latest) [![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) > Golang 跨平台自动化系统,控制键盘、鼠标、位图、图像、读取屏幕,进程、窗口句柄以及全局事件监听 RobotGo 支持 Mac, Windows, and Linux(X11).
提 Issues 请到 [Github](https://github.com/go-vgo/robotgo), 便于统一管理和即时更新; `REDAME_zh.md 已废弃, 不再更新` ## Contents - [Docs](#docs) - [Binding](#binding) - [Requirements](#requirements) - [Installation](#installation) - [Update](#update) - [Examples](#examples) - [Cross-Compiling](#crosscompiling) - [Authors](#authors) - [Plans](#plans) - [Donate](#donate) - [Contributors](#contributors) - [License](#license) ## Docs - [GoDoc](https://godoc.org/github.com/go-vgo/robotgo)
## Binding: [Robotn](https://github.com/vcaesar/robotn), binding JavaScript and other, support more language. ## Requirements: 环境要求: 在安装 RobotGo 之前, 请确保 `Golang、GCC` 被正确安装 ### ALL: ``` Golang GCC ``` #### For Mac OS X: Xcode Command Line Tools (And Privacy setting: [#277](https://github.com/go-vgo/robotgo/issues/277) ) ``` xcode-select --install ``` #### For Windows: [MinGW-w64](https://sourceforge.net/projects/mingw-w64/files) (推荐使用) ``` Or the other GCC (But you should compile the "libpng" with yourself. Or you can removed the bitmap.go.) ``` #### For everything else (Linux 等其他系统): ``` GCC, libpng(bitmap) X11 with the XTest extension (also known as the Xtst library) 事件: xcb, xkb, libxkbcommon Clipboard: xsel xclip ``` ##### Ubuntu: ```yml sudo apt install gcc libc6-dev sudo apt install libx11-dev xorg-dev libxtst-dev libpng++-dev sudo apt install xcb libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxkbcommon-x11-dev libxkbcommon-dev sudo apt install xsel xclip ``` ##### Fedora: ```yml sudo dnf install libXtst-devel libxkbcommon-devel libxkbcommon-x11-devel xorg-x11-xkb-utils-devel sudo dnf install libpng-devel sudo dnf install xsel xclip ``` ## Installation: With Go module support (Go 1.11+), just import: ```go import "github.com/go-vgo/robotgo" ``` Otherwise, to install the robotgo package, run the command: ``` go get github.com/go-vgo/robotgo ``` png.h: No such file or directory? Please see [issues/47](https://github.com/go-vgo/robotgo/issues/47). ## Update: ``` go get -u github.com/go-vgo/robotgo ``` 注意 go1.10.x C 文件编译缓存问题, [golang #24355](https://github.com/golang/go/issues/24355). `go mod vendor` problem, [golang #26366](https://github.com/golang/go/issues/26366). ## [Examples:](https://github.com/go-vgo/robotgo/blob/master/examples) #### [鼠标](https://github.com/go-vgo/robotgo/blob/master/examples/mouse/main.go) ```Go package main import ( "github.com/go-vgo/robotgo" ) func main() { // robotgo.ScrollMouse(10, "up") robotgo.Scroll(0, -10) robotgo.Scroll(100, 0) robotgo.MilliSleep(100) // robotgo.ScrollRelative(10, -100) robotgo.ScrollSmooth(-10, 6) robotgo.MouseSleep = 100 robotgo.Move(10, 20) robotgo.MoveRelative(0, -10) robotgo.Drag(10, 10) robotgo.Click("left", true) robotgo.MoveSmooth(100, 200, 1.0, 10.0) robotgo.Toggle("left") robotgo.Toggle("left", "up") } ``` #### [键盘](https://github.com/go-vgo/robotgo/blob/master/examples/key/main.go) ```Go package main import ( "fmt" "github.com/go-vgo/robotgo" ) func main() { robotgo.TypeStr("Hello World. Winter is coming!") robotgo.TypeStr("だんしゃり", 1.0) // robotgo.TypeStr("テストする") robotgo.TypeStr("Hi galaxy. こんにちは世界.") robotgo.Sleep(1) // ustr := uint32(robotgo.CharCodeAt("テストする", 0)) // robotgo.UnicodeType(ustr) robotgo.KeySleep = 100 robotgo.KeyTap("enter") // robotgo.TypeStr("en") robotgo.KeyTap("i", "alt", "command") arr := []string{"alt", "command"} robotgo.KeyTap("i", arr) robotgo.MilliSleep(100) robotgo.KeyToggle("a") robotgo.KeyToggle("a", "up") robotgo.WriteAll("テストする") text, err := robotgo.ReadAll() if err == nil { fmt.Println(text) } } ``` #### [屏幕](https://github.com/go-vgo/robotgo/blob/master/examples/screen/main.go) ```Go package main import ( "fmt" "github.com/go-vgo/robotgo" "github.com/vcaesar/imgo" ) func main() { x, y := robotgo.GetMousePos() fmt.Println("pos: ", x, y) color := robotgo.GetPixelColor(100, 200) fmt.Println("color----", color) sx, sy := robotgo.GetScreenSize() fmt.Println("get screen size: ", sx, sy) bit := robotgo.CaptureScreen(10, 10, 30, 30) defer robotgo.FreeBitmap(bit) robotgo.SaveBitmap(bit, "test_1.png") img := robotgo.ToImage(bit) imgo.Save("test.png", img) } ``` #### [位图](https://github.com/go-vgo/robotgo/blob/master/examples/bitmap/main.go) ```Go package main import ( "fmt" "github.com/go-vgo/robotgo" ) func main() { bitmap := robotgo.CaptureScreen(10, 20, 30, 40) // use `defer robotgo.FreeBitmap(bit)` to free the bitmap defer robotgo.FreeBitmap(bitmap) fmt.Println("bitmap...", bitmap) img := robotgo.ToImage(bitmap) robotgo.SavePng(img, "test_1.png") bit2 := robotgo.ToCBitmap(robotgo.ImgToBitmap(img)) fx, fy := robotgo.FindBitmap(bit2) fmt.Println("FindBitmap------ ", fx, fy) robotgo.Move(fx, fy) arr := robotgo.FindAllBitmap(bit2) fmt.Println("Find all bitmap: ", arr) robotgo.SaveBitmap(bitmap, "test.png") fx, fy = robotgo.FindBitmap(bitmap) fmt.Println("FindBitmap------", fx, fy) robotgo.SaveBitmap(bitmap, "test.png") } ``` #### [OpenCV](https://github.com/vcaesar/gcv) ```Go package main import ( "fmt" "math/rand" "github.com/go-vgo/robotgo" "github.com/vcaesar/gcv" ) func main() { opencv() } func opencv() { name := "test.png" name1 := "test_001.png" robotgo.SaveCapture(name1, 10, 10, 30, 30) robotgo.SaveCapture(name) fmt.Print("gcv find image: ") fmt.Println(gcv.FindImgFile(name1, name)) fmt.Println(gcv.FindAllImgFile(name1, name)) bit := robotgo.OpenBitmap(name1) defer robotgo.FindBitmap(bit) fmt.Print("find bitmap: ") fmt.Println(robotgo.FindBitmap(bit)) // bit0 := robotgo.CaptureScreen() // img := robotgo.ToImage(bit0) // bit1 := robotgo.CaptureScreen(10, 10, 30, 30) // img1 := robotgo.ToImage(bit1) // defer robotgo.FreeBitmapArr(bit0, bit1) img := robotgo.CaptureImg() img1 := robotgo.CaptureImg(10, 10, 30, 30) fmt.Print("gcv find image: ") fmt.Println(gcv.FindImg(img1, img)) fmt.Println() res := gcv.FindAllImg(img1, img) fmt.Println(res[0].TopLeft.Y, res[0].Rects.TopLeft.X, res) x, y := res[0].TopLeft.X, res[0].TopLeft.Y robotgo.Move(x, y-rand.Intn(5)) robotgo.MilliSleep(100) robotgo.Click() res = gcv.FindAll(img1, img) // use find template and sift fmt.Println("find all: ", res) res1 := gcv.Find(img1, img) fmt.Println("find: ", res1) img2, _, _ := robotgo.DecodeImg("test_001.png") x, y = gcv.FindX(img2, img) fmt.Println(x, y) } ``` #### [事件](https://github.com/go-vgo/robotgo/blob/master/examples/gohook/main.go) ```Go package main import ( "fmt" "github.com/go-vgo/robotgo" hook "github.com/robotn/gohook" ) func main() { add() low() event() } func add() { fmt.Println("--- Please press ctrl + shift + q to stop hook ---") robotgo.EventHook(hook.KeyDown, []string{"q", "ctrl", "shift"}, func(e hook.Event) { fmt.Println("ctrl-shift-q") robotgo.EventEnd() }) fmt.Println("--- Please press w---") robotgo.EventHook(hook.KeyDown, []string{"w"}, func(e hook.Event) { fmt.Println("w") }) s := robotgo.EventStart() <-robotgo.EventProcess(s) } func low() { evChan := hook.Start() defer hook.End() for ev := range evChan { fmt.Println("hook: ", ev) } } func event() { ok := robotgo.AddEvents("q", "ctrl", "shift") if ok { fmt.Println("add events...") } keve := robotgo.AddEvent("k") if keve { fmt.Println("you press... ", "k") } mleft := robotgo.AddEvent("mleft") if mleft { fmt.Println("you press... ", "mouse left button") } } ``` #### [窗口句柄](https://github.com/go-vgo/robotgo/blob/master/examples/window/main.go) ```Go package main import ( "fmt" "github.com/go-vgo/robotgo" ) func main() { fpid, err := robotgo.FindIds("Google") if err == nil { fmt.Println("pids...", fpid) if len(fpid) > 0 { robotgo.ActivePID(fpid[0]) robotgo.Kill(fpid[0]) } } robotgo.ActiveName("chrome") isExist, err := robotgo.PidExists(100) if err == nil && isExist { fmt.Println("pid exists is", isExist) robotgo.Kill(100) } abool := robotgo.Alert("test", "robotgo") if abool { fmt.Println("ok@@@ ", "ok") } title := robotgo.GetTitle() fmt.Println("title@@@ ", title) } ``` ## CrossCompiling ##### Windows64 to windows32 ```Go SET CGO_ENABLED=1 SET GOARCH=386 go build main.go ``` #### Other to windows Install Requirements (Ubuntu): ```bash sudo apt install gcc-multilib sudo apt install gcc-mingw-w64 sudo apt install libz-mingw-w64-dev ``` Build the binary: ```Go GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ go build -x ./ ``` ``` // CC=mingw-w64\x86_64-7.2.0-win32-seh-rt_v5-rev1\mingw64\bin\gcc.exe // CXX=mingw-w64\x86_64-7.2.0-win32-seh-rt_v5-rev1\mingw64\bin\g++.exe ``` ## Authors - [The author is vz](https://github.com/vcaesar) - [Maintainers](https://github.com/orgs/go-vgo/people) - [Contributors](https://github.com/go-vgo/robotgo/graphs/contributors) ## Plans - 更新 Find an image on screen, read pixels from an image - 更新 Window Handle - 尝试支持 Android, 也许支持 IOS ## Contributors - See [contributors page](https://github.com/go-vgo/robotgo/graphs/contributors) for full list of contributors. - See [Contribution Guidelines](https://github.com/go-vgo/robotgo/blob/master/CONTRIBUTING.md). ## License 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. See [LICENSE-APACHE](http://www.apache.org/licenses/LICENSE-2.0), [LICENSE-MIT](https://github.com/go-vgo/robotgo/blob/master/LICENSE). ================================================ FILE: docs/archive/doc.md ================================================ # Methods: ##### [GetVersion](#GetVersion) ## [Keyboard](#Keyboard) ##### [Keys](https://github.com/go-vgo/robotgo/blob/master/docs/keys.md) ##### [SetKeyDelay](#SetKeyDelay) ##### [KeyTap](#KeyTap) ##### [KeyToggle](#KeyToggle) ##### [TypeStr](#TypeStr) ##### [TypeStrDelay](#TypeStrDelay) ##### [WriteAll](#WriteAll) ##### [ReadAll](#ReadAll) ## [Mouse](#Mouse) ##### [SetMouseDelay](#SetMouseDelay) ##### [MoveMouse](#MoveMouse) ##### [Move](#MoveMouse) (Equivalent to MoveMouse) ##### [MoveMouseSmooth](#MoveMouseSmooth) ##### [MoveSmooth](#MoveMouseSmooth) (Equivalent to MoveMouseSmooth) ##### [MouseClick](#MouseClick) ##### [Click](#MouseClick) (Equivalent to MouseClick) ##### [MoveClick](#MoveClick) ##### [MouseToggle](#MouseToggle) ##### [DragMouse](#DragMouse) ##### [Drag](#DragMouse) (Equivalent to DragMouse) ##### [GetMousePos](#GetMousePos) ##### [ScrollMouse](#ScrollMouse) ## [Screen](#Screen) ##### [GetPixelColor](#GetPixelColor) ##### [GetScreenSize](#GetScreenSize) ##### [CaptureScreen](#CaptureScreen) ##### [GetXDisplayName(Linux)](#GetXDisplayName) ##### [SetXDisplayName(Linux)](#SetXDisplayName) ## [Bitmap](#Bitmap) This is a work in progress. ##### [FindBitmap](#FindBitmap) ##### [OpenBitmap](#OpenBitmap) ##### [SaveBitmap](#SaveBitmap) ##### [TostringBitmap](#TostringBitmap) ##### [GetPortion](#GetPortion) ##### [Convert](#Convert) #### [FreeBitmap](#FreeBitmap) #### [ReadBitmap](#ReadBitmap) #### [CopyBitpb](#CopyBitpb) #### [DeepCopyBit](#DeepCopyBit) ## [Event](#Event) ##### [AddEvent](#AddEvent) ##### [StopEvent](#StopEvent) ## [Window](#Window) This is a work in progress. ##### [ShowAlert](#ShowAlert) ##### [CloseWindow](#CloseWindow) ##### [IsValid](#IsValid) ##### [SetActive](#SetActive) ##### [GetActive](#GetActive) ##### [SetHandle](#SetHandle) ##### [GetHandle](#GetHandle) ##### [GetTitle](#GetTitle) ##### [GetPID](#GetPID) ##### [Pids](#Pids) ##### [PidExists](#PidExists) ##### [Process](#Process) ##### [FindName](#FindName) ##### [FindNames](#FindNames) ##### [FindIds](#FindIds) #### [ActivePID](#ActivePID) ###

.GetVersion()

Get robotgo version ##

Keyboard

###

.SetKeyDelay(ms)

Sets the delay in milliseconds to sleep after a keyboard event. This is 10ms by default. #### Arguments: ms - Time to sleep in milliseconds. ###

.KeyTap(key, modifier)

Press a single key. #### Arguments: key - See [keys](https://github.com/go-vgo/robotgo/blob/master/docs/keys.md). modifier (optional, string or array) - Accepts alt, command (win), control, and shift. #### Examples: ```Go robotgo.KeyTap("h", "command") robotgo.KeyTap("i", "alt", "command") arr := []string{"alt", "command"} robotgo.KeyTap("i", arr) ``` ###

.KeyToggle(key, down, modifier)

Hold down or release a key. #### Arguments: key - See [keys](https://github.com/go-vgo/robotgo/blob/master/docs/keys.md). down - Accepts 'down' or 'up'. modifier (optional, string or array) - Accepts alt, command (mac), control, and shift. #### Return: return KeyToggle status ###

.TypeStr(string)

#### Arguments: string - The string to send. ###

.TypeStrDelay(string, cpm)

#### Arguments: string - The string to send. cpm - Characters per minute. ###

.WriteAll(text string)

#### Arguments: text string #### Return: ###

.ReadAll()

#### Arguments: #### Return: text, error ##

Mouse

###

.SetMouseDelay(ms)

Sets the delay in milliseconds to sleep after a mouse event. This is 10ms by default. #### Arguments: ms - Time to sleep in milliseconds. ###

.MoveMouse(x, y)

Moves mouse to x, y instantly, with the mouse button up. #### Arguments: x, y #### Examples: ```Go // Move the mouse to 100, 100 on the screen. robotgo.MoveMouse(100, 100) ``` ###

.MoveMouseSmooth(x, y)

Moves mouse to x, y human like, with the mouse button up. #### Arguments: x, y lowspeed, highspeed #### Examples: ```Go robotgo.MoveMouseSmooth(100, 200) robotgo.MoveMouseSmooth(100, 200, 1.0, 100.0) ``` ###

.MouseClick(button, double)

Clicks the mouse. #### Arguments: button (optional) - Accepts "left", "right", or "center". Defaults to left. double (optional) - Set to true to perform a double click. Defaults to false. #### Examples: ```Go robotgo.MouseClick() robotgo.MouseClick("left", true) ``` ###

.MoveClick(x, y, button, double)

Move and click the mouse. #### Arguments: x, y, button (optional) - Accepts "left", "right", or "center". Defaults to left. double (optional) - Set to true to perform a double click. Defaults to false. #### Examples: ```Go robotgo.MoveClick(10, 20) robotgo.MoveClick(10, 20, "left", true) ``` ###

.MouseToggle(down, button)

Toggles mouse button. #### Arguments: down (optional) - Accepts down or up. Defaults to down. button (optional) - Accepts "left", "right", or "center". Defaults to left. #### Examples: ```Go robotgo.MouseToggle("down") robotgo.MouseToggle("down", "right") ``` ###

.DragMouse(x, y)

Moves mouse to x, y instantly, with the mouse button held down. #### Arguments: x, y #### Examples: ```Go // Mouse down at 0, 0 and then drag to 100, 100 and release. robotgo.MoveMouse(0, 0) robotgo.MouseToggle("down") robotgo.DragMouse(100, 100) robotgo.MouseToggle("up") ``` ###

.GetMousePos()

Gets the mouse coordinates. #### Return: Returns an object with keys x and y. #### Examples: ```Go x,y := robotgo.GetMousePos() fmt.Println("pos:", x, y) ``` ###

.ScrollMouse(magnitude, direction)

Scrolls the mouse either up or down. #### Arguments: magnitude - The amount to scroll. direction - Accepts down or up. #### Examples: ```Go robotgo.ScrollMouse(50, "up") robotgo.ScrollMouse(50, "down") ``` ##

Screen

###

.GetPixelColor(x, y) Gets the pixel color at x, y. This function is perfect for getting a pixel or two, but if you'll be reading large portions of the screen use screen.capture. #### Arguments: x, y #### Return: Returns the hex color code of the pixel at x, y. ###

.GetScreenSize()

Gets the screen width and height. #### Return: Returns an object with .width and .height. ###

.CaptureScreen

// CaptureScreen Gets part or all of the screen. GoCaptureScreen Returns a go struct #### Arguments: x (optional) y (optional) height (optional) width (optional) If no arguments are provided, capturescreen(screencapture) will get the full screen. #### Return: Returns a bitmap object. ##

Bitmap

This is a work in progress. ###

.FindBitmap

find bitmap. #### Arguments: bitmap; rect(optional): x, y, w, h #### Return: Returns a position x and y ###

.OpenBitmap

open bitmap. #### Arguments: bitmap image path, MMImageType (optional) #### Return: Returns a bitmap ###

.SaveBitmap

Save a image with bitmap. #### Arguments: bitmap, path, imagetype (int) #### Return: return save image status ###

.TostringBitmap

bitmap to string #### Arguments: bitmap #### Return: Return a sting bitmap ###

.GetPortion

bitmap from a portion #### Arguments: bitmap, rect: x, y, w, h #### Return: Returns new bitmap object created from a portion of another ###

.Convert(openpath, savepath, MMImageType)

Convert the image format #### Arguments: openpath, savepath, MMImageType (optional) #### Examples: ```Go robotgo.Convert("test.png", "test.tif") ``` ###

.FreeBitmap(MMBitmapRef)

FreeBitmap free and dealloc bitmap #### Arguments: MMBitmapRef #### Examples: ```Go robotgo.FreeBitmap(bitmap) ``` ###

.ReadBitmap(MMBitmapRef)

ReadBitmap returns false and sets error if |bitmap| is NULL #### Arguments: MMBitmapRef #### Return: bool #### Examples: ```Go robotgo.ReadBitmap(bitmap) ``` ###

.CopyBitpb(MMBitmapRef)

CopyBitpb copy bitmap to pasteboard #### Arguments: MMBitmapRef #### Return: bool #### Examples: ```Go robotgo.CopyBitpb(bitmap) ``` ###

.DeepCopyBit(MMBitmapRef)

DeepCopyBit deep copy bitmap #### Arguments: MMBitmapRef #### Return: MMBitmapRef #### Examples: ```Go robotgo.DeepCopyBit(bitmap) ``` ##

Event

###

.AddEvent(string)

Listening global event #### Arguments: string (mouse arguments: mleft, mright, wheelDown, wheelUp, wheelLeft, wheelRight) #### Return: if listened return 0 #### Examples: ```Go package main import ( "fmt" "github.com/go-vgo/robotgo" ) func main() { keve := robotgo.AddEvent("k") if keve { fmt.Println("you press...", "k") } mleft := robotgo.AddEvent("mleft") if mleft { fmt.Println("you press...", "mouse left button") } } ``` ###

.StopEvent()

stop listen global event ##

Window

###

.ShowAlert(title, msg, defaultButton, cancelButton string)

Displays alert with the given attributes. If cancelButton is not given, only the defaultButton is displayed #### Arguments: title (string), msg (string), defaultButton (optional string), cancelButton (optional string) #### Return: Returns 0(True) if the default button was pressed, or 1(False) if cancelled. ###

.CloseWindow()

Close the Window #### Arguments: None #### Return: None ###

.IsValid()

Valid the Window #### Arguments: None #### Return: Returns true if a window has been selected ###

.SetActive()

Set the Active Window #### Arguments: hwnd #### Return: void ###

.GetActive()

Get the Active Window #### Arguments: None #### Return: Returns hwnd ###

.SetHandle()

Set the Window Handle #### Arguments: int #### Return: bool ###

.GetHandle()

Get the Window Handle #### Arguments: None #### Return: Returns hwnd ###

.GetTitle()

Get the Window Title #### Arguments: None #### Return: Returns Window Title ###

.GetPID()

Get the process id #### Arguments: None #### Return: Returns the process id ###

.Pids()

Pids get the all process id #### Arguments: None #### Return: Returns all process id ###

.PidExists()

PidExists determine whether the process exists #### Arguments: pid #### Return: Returns bool ###

.Process()

Process get the all process struct #### Arguments: none #### Return: Returns []Nps, error ###

.FindName()

FindName find the process name by the process id #### Arguments: pid #### Return: Returns string, error ###

.FindNames()

FindNames find the all process name #### Arguments: none #### Return: Returns []string, error ###

.FindIds()

FindIds find the process id by the process name #### Arguments: name string #### Return: Returns []int32, error ###

.ActivePID()

ActivePID window active by PID #### Arguments: pid int32 #### Return: none ================================================ FILE: docs/archive/doc_zh.md ================================================ # 方法: ##### [GetVersion](#GetVersion) ## [键盘](#Keyboard) ##### [Keys](https://github.com/go-vgo/robotgo/blob/master/docs/keys.md) ##### [SetKeyDelay](#SetKeyDelay) ##### [KeyTap](#KeyTap) ##### [KeyToggle](#KeyToggle) ##### [TypeStr](#TypeStr) ##### [TypeStrDelay](#TypeStrDelay) ##### [WriteAll](#WriteAll) ##### [ReadAll](#ReadAll) ## [鼠标](#Mouse) ##### [SetMouseDelay](#SetMouseDelay) ##### [MoveMouse](#MoveMouse) ##### [Move](#MoveMouse) (相当于 MoveMouse) ##### [MoveMouseSmooth](#MoveMouseSmooth) ##### [MoveSmooth](#MoveMouseSmooth) (相当与 MoveMouseSmooth) ##### [MouseClick](#MouseClick) ##### [Click](#MouseClick) (相当于 MouseClick) ##### [MoveClick](#MoveClick) ##### [MouseToggle](#MouseToggle) ##### [DragMouse](#DragMouse) ##### [Drag](#DragMouse) (相当于 DragMouse) ##### [GetMousePos](#GetMousePos) ##### [ScrollMouse](#ScrollMouse) ###

.GetVersion()

获取 robotgo 版本 ## [屏幕](#Screen) ##### [GetPixelColor](#GetPixelColor) ##### [GetScreenSize](#GetScreenSize) ##### [CaptureScreen](#CaptureScreen) ##### [GetXDisplayName(Linux)](#GetXDisplayName) ##### [SetXDisplayName(Linux)](#SetXDisplayName) ## [位图](#Bitmap) This is a work in progress. (工作正在进行中) ##### [FindBitmap](#FindBitmap) ##### [OpenBitmap](#OpenBitmap) ##### [SaveBitmap](#SaveBitmap) ##### [TostringBitmap](#TostringBitmap) ##### [GetPortion](#GetPortion) ##### [Convert](#Convert) #### [FreeBitmap](#FreeBitmap) #### [ReadBitmap](#ReadBitmap) #### [CopyBitpb](#CopyBitpb) #### [DeepCopyBit](#DeepCopyBit) ## [事件](#Event) ##### [AddEvent](#AddEvent) ##### [StopEvent](#StopEvent) ## [窗口](#Window) This is a work in progress. ##### [ShowAlert](#ShowAlert) ##### [CloseWindow](#CloseWindow) ##### [IsValid](#IsValid) ##### [SetActive](#SetActive) ##### [GetActive](#GetActive) ##### [SetHandle](#SetHandle) ##### [GetHandle](#GetHandle) ##### [GetTitle](#GetTitle) ##### [GetPID](#GetPID) ##### [Pids](#Pids) ##### [PidExists](#PidExists) ##### [Process](#Process) ##### [FindName](#FindName) ##### [FindNames](#FindNames) ##### [FindIds](#FindIds) #### [ActivePID](#ActivePID) ##

键盘

###

.SetKeyDelay(ms)

设置键盘延迟 (在键盘一个事件后), 单位 ms, 默认值 10ms Sets the delay in milliseconds to sleep after a keyboard event. This is 10ms by default. #### 参数: 延迟时间,单位 ms ms - Time to sleep in milliseconds. ###

.KeyTap(key, modifier)

模拟键盘按键 Press a single key. #### 参数: 键盘值 修饰值 (可选类型, 字符串或者数组) - 可选值: alt, command (win), control, and shift. key - See [keys](https://github.com/go-vgo/robotgo/blob/master/docs/keys.md). modifier (optional, string or array) - Accepts alt, command (win), control, and shift. #### 示例: ```Go robotgo.KeyTap("h", "command") robotgo.KeyTap("i", "alt", "command") arr := []string{"alt", "command"} robotgo.KeyTap("i", arr) ``` ###

.KeyToggle(key, down, modifier)

键盘切换, 按住或释放一个键位 Hold down or release a key. #### 参数: key - See [keys](https://github.com/go-vgo/robotgo/blob/master/docs/keys.md). down - Accepts 'down' or 'up'. modifier (optional, string or array) - Accepts alt, command (mac), control, and shift. ### 返回值: 返回 KeyToggle 状态 ###

.TypeStr(string)

#### 参数: string - The string to send. ###

.TypeStrDelay(string, cpm)

#### 参数: string - The string to send. cpm - Characters per minute. ###

.WriteAll(text string)

#### 参数: text string #### 返回值: ###

.ReadAll()

#### 参数: #### 返回值: text, error ##

鼠标

###

.SetMouseDelay(ms)

设置鼠标延迟 (在一个鼠标事件后), 单位 ms, 默认值 10 ms Sets the delay in milliseconds to sleep after a mouse event. This is 10ms by default. #### 参数: ms - Time to sleep in milliseconds. ###

.MoveMouse(x, y)

移动鼠标 Moves mouse to x, y instantly, with the mouse button up. #### 参数: x, y #### 示例: ```Go // Move the mouse to 100, 100 on the screen. robotgo.MoveMouse(100, 100) ``` ###

.MoveMouseSmooth(x, y)

模拟鼠标向 X,Y 平滑移动(像人类一样),用鼠标按钮向上 Moves mouse to x, y human like, with the mouse button up. #### 参数: x, y lowspeed, highspeed #### 示例: ```Go robotgo.MoveMouseSmooth(100, 200) robotgo.MoveMouseSmooth(100, 200, 1.0, 100.0) ``` ###

.MouseClick(button, double)

鼠标点击 Clicks the mouse. #### 参数: button (optional) - Accepts left, right, or center. Defaults to left. double (optional) - Set to true to perform a double click. Defaults to false. #### 示例: ```Go robotgo.MouseClick() robotgo.MouseClick("left", true) ``` ###

.MoveClick(x, y, button, double)

移动并点击鼠标 #### 参数: x, y, button (optional) - Accepts "left", "right", or "center". Defaults to left. double (optional) - Set to true to perform a double click. Defaults to false. #### 示例: ```Go robotgo.MoveClick(10, 20) robotgo.MoveClick(10, 20, "left", true) ``` ###

.MouseToggle(down, button)

鼠标切换 Toggles mouse button. #### 参数: down (optional) - Accepts down or up. Defaults to down. button (optional) - Accepts "left", "right", or "center". Defaults to left. #### 示例: ```Go robotgo.MouseToggle("down") robotgo.MouseToggle("down", "right") ``` ###

.DragMouse(x, y)

拖动鼠标 Moves mouse to x, y instantly, with the mouse button held down. #### 参数: x, y #### 示例: ```Go // Mouse down at 0, 0 and then drag to 100, 100 and release. robotgo.MoveMouse(0, 0) robotgo.MouseToggle("down") robotgo.DragMouse(100, 100) robotgo.MouseToggle("up") ``` ###

.GetMousePos()

获取鼠标的位置 Gets the mouse coordinates. #### 返回值: Returns an object with keys x and y. #### 示例: ```Go x, y := robotgo.GetMousePos() fmt.Println("pos:", x, y) ``` ###

.ScrollMouse(magnitude, direction)

滚动鼠标 Scrolls the mouse either up or down. #### 参数: 滚动位置的大小 滚动方向: up (向上滚动) down (向下滚动) magnitude - The amount to scroll. direction - Accepts down or up. #### 示例: ```Go robotgo.ScrollMouse(50, "up") robotgo.ScrollMouse(50, "down") ``` ##

屏幕

###

.GetPixelColor(x, y) 获取坐标为 x, y 位置处的颜色 Gets the pixel color at x, y. This function is perfect for getting a pixel or two, but if you'll be reading large portions of the screen use screen.capture. #### 参数: x, y #### 返回值: Returns the hex color code of the pixel at x, y. ###

.GetScreenSize()

获取屏幕大小 Gets the screen width and height. #### 返回值: Returns an object with .width and .height. ###

.CaptureScreen

// CaptureScreen 获取部分或者全部屏幕 Gets part or all of the screen. GoCaptureScreen Returns a go struct #### 参数: x (optional) y (optional) height (optional) width (optional) If no arguments are provided, capturescreen(screencapture) will get the full screen. #### 返回值: 返回一个 bitmap object. ##

位图

This is a work in progress. ###

.FindBitmap

查找 bitmap. #### 参数: bitmap; rect (可选参数): x, y, w, h #### Return: 查找到, 返回 bitmap 的 x 和y 坐标; 没有返回 nil ###

.OpenBitmap

打开 bitmap 图片. #### 参数: bitmap 图片路径, MMImageType (可选) #### 返回值: 返回一个 bitmap 对象 ###

.SaveBitmap

保存一个 bitmap 图片. #### 参数: bitmap 对象, 保存路径, imagetype (int) #### 返回值: 保存图片, 返回保存状态 ###

.TostringBitmap

将一个 bitmap 对象转换为字符串对象. #### 参数: bitmap 对象 #### Return: 返回一个 bitmap 字符串 ###

.GetPortion

bitmap from a portion #### 参数: bitmap, rect: x, y, w, h #### 返回值: Returns new bitmap object created from a portion of another ###

.Convert(openpath, savepath, MMImageType)

转换 bitmap 图片格式 #### 参数: openpath, savepath, MMImageType (可选) #### 示例: ```Go robotgo.Convert("test.png", "test.tif") ``` ###

.FreeBitmap(MMBitmapRef)

FreeBitmap free and dealloc bitmap #### 参数: MMBitmapRef #### 示例: ```Go robotgo.FreeBitmap(bitmap) ``` ###

.ReadBitmap(MMBitmapRef)

ReadBitmap returns false and sets error if |bitmap| is NULL #### 参数: MMBitmapRef #### 返回值: bool #### 示例: ```Go robotgo.ReadBitmap(bitmap) ``` ###

.CopyBitpb(MMBitmapRef)

CopyBitpb copy bitmap to pasteboard #### 参数: MMBitmapRef #### 返回值: bool #### 示例: ```Go robotgo.CopyBitpb(bitmap) ``` ###

.DeepCopyBit(MMBitmapRef)

DeepCopyBit deep copy bitmap #### 参数: MMBitmapRef #### 返回值: MMBitmapRef #### 示例: ```Go robotgo.DeepCopyBit(bitmap) ``` ##

事件

###

.AddEvent(string)

监听全局事件 #### 参数: string (鼠标参数: mleft, mright, wheelDown, wheelUp, wheelLeft, wheelRight) #### 返回值: 监听成功返回 0 #### 示例: ```Go package main import ( "fmt" "github.com/go-vgo/robotgo" ) func main() { keve := robotgo.AddEvent("k") if keve { fmt.Println("you press...", "k") } mleft := robotgo.AddEvent("mleft") if mleft { fmt.Println("you press...", "mouse left button") } } ``` ###

.StopEvent()

停止事件监听 ##

窗口

###

.ShowAlert(title, msg, defaultButton, cancelButton string)

Displays alert with the given attributes. If cancelButton is not given, only the defaultButton is displayed #### 参数: title (string), msg (string), defaultButton (optional string), cancelButton (optional string) #### 返回值: Returns 0 (True) if the default button was pressed, or 1 (False) if cancelled. ###

.CloseWindow()

关闭窗口 #### 参数: 无 #### 返回值: 无 ###

.IsValid()

Valid the Window #### 参数: 无 #### 返回值: 如果窗口 selected 返回 true ###

.SetActive()

设为当前窗口 #### 参数: hwnd #### 返回值: void ###

.GetActive()

获取当前窗口 #### 参数: 无 #### 返回值: Returns hwnd ###

.SetHandle()

Set the Window Handle #### 参数: int #### 返回值: bool ###

.GetHandle()

获取窗口 Handle #### 参数: 无 #### 返回值: Returns hwnd ###

.GetTitle()

获取窗口标题 #### 参数: #### 返回值: 返回窗口标题 ###

.GetPID()

获取进程 id #### 参数: 无 #### 返回值: 返回进程 id ###

.Pids()

获取所有进程 id #### 参数: 无 #### 返回值: 返回进程 id ###

.PidExists()

判断进程 id 是否存在 #### 参数: pid #### 返回值: 返回 bool ###

.Process()

Process get the all process struct #### 参数: 无 #### 返回值: Returns []Nps, error ###

.FindName()

FindName find the process name by the process id #### 参数: pid #### 返回值: Returns string, error ###

.FindNames()

FindNames find the all process name #### Arguments: none #### Return: Returns []string, error ###

.FindIds()

FindIds find the process id by the process name #### Arguments: name string #### Return: Returns []int32, error ###

.ActivePID()

ActivePID window active by PID #### Arguments: pid int32 #### Return: none ================================================ FILE: docs/install.md ================================================ ## CrossCompiling ##### Windows64 to windows32 ```Go SET CGO_ENABLED=1 SET GOARCH=386 go build main.go ``` #### Other to windows Install Requirements (Ubuntu): ```bash sudo apt install gcc-multilib sudo apt install gcc-mingw-w64 # fix err: zlib.h: No such file or directory, Just used by bitmap. sudo apt install libz-mingw-w64-dev ``` Build the binary: ```Go GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ go build -x ./ ``` ``` // CC=mingw-w64\x86_64-7.2.0-win32-seh-rt_v5-rev1\mingw64\bin\gcc.exe // CXX=mingw-w64\x86_64-7.2.0-win32-seh-rt_v5-rev1\mingw64\bin\g++.exe ``` 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). ================================================ FILE: docs/keys.md ================================================ ## Type Conversion | | type conversion | func | | --- | --------------------------------- | ----------------------- | | \* | robotgo.Bitmap -> robotgo.CBitmap | robotgo.ToCBitmap() | | | robotgo.Bitmap -> \*image.RGBA | robotgo.ToRGBAGo() | | \* | robotgo.CBitmap -> C.MMBitmapRef | robotgo.ToMMBitmapRef() | | | robotgo.CBitmap -> robotgo.Bitmap | robotgo.ToBitmap() | | | robotgo.CBitmap -> image.Image | robotgo.ToImage() | | | robotgo.CBitmap -> \*image.RGBA | robotgo.ToRGBA() | | \* | C.MMBitmapRef -> robotgo.CBitmap | robotgo.CBitmap() | | \* | image.Image -> robotgo.Bitmap | robotgo.ImgToBitmap() | | | image.Image -> robotgo.CBitmap | robotgo.ImgToCBitmap() | | | image.Image -> []byte | robotgo.ToByteImg() | | | image.Image -> string | robotgo.ToStringImg() | | \* | \*image.RGBA -> robotgo.Bitmap | robotgo.RGBAToBitmap() | | \* | []byte -> image.Image | robotgo.ByteToImg() | | | []byte-> robotgo.CBitmap | robotgo.ByteToCBitmap() | | | []byte -> string | string() | | \* | string -> image.Image | robotgo.StrToImg() | | | string -> byte | []byte() | # Keys ```Go "A-Z a-z 0-9" "backspace" "delete" "enter" "tab" "esc" "escape" "up" Up arrow key "down" Down arrow key "right" Right arrow key "left" Left arrow key "home" "end" "pageup" "pagedown" "f1" "f2" "f3" "f4" "f5" "f6" "f7" "f8" "f9" "f10" "f11" "f12" "f13" "f14" "f15" "f16" "f17" "f18" "f19" "f20" "f21" "f22" "f23" "f24" "cmd" is the "win" key for windows "lcmd" left command "rcmd" right command // "command" "alt" "lalt" left alt "ralt" right alt "ctrl" "lctrl" left ctrl "rctrl" right ctrl "control" "shift" "lshift" left shift "rshift" right shift // "right_shift" "capslock" "space" "print" "printscreen" // No Mac support "insert" "menu" Windows only "audio_mute" Mute the volume "audio_vol_down" Lower the volume "audio_vol_up" Increase the volume "audio_play" "audio_stop" "audio_pause" "audio_prev" Previous Track "audio_next" Next Track "audio_rewind" Linux only "audio_forward" Linux only "audio_repeat" Linux only "audio_random" Linux only "num0" "num1" "num2" "num3" "num4" "num5" "num6" "num7" "num8" "num9" "num_lock" "num." "num+" "num-" "num*" "num/" "num_clear" "num_enter" "num_equal" // // "numpad_0" No Linux support // "numpad_0" // "numpad_1" // "numpad_2" // "numpad_3" // "numpad_4" // "numpad_5" // "numpad_6" // "numpad_7" // "numpad_8" // "numpad_9" // "numpad_lock" "lights_mon_up" Turn up monitor brightness No Windows support "lights_mon_down" Turn down monitor brightness No Windows support "lights_kbd_toggle" Toggle keyboard backlight on/off No Windows support "lights_kbd_up" Turn up keyboard backlight brightness No Windows support "lights_kbd_down" Turn down keyboard backlight brightness No Windows support ``` ================================================ FILE: event/android/event_c.h ================================================ #include #include ================================================ FILE: event/ios/event_c.h ================================================ #include #include ================================================ FILE: examples/README.md ================================================ # Robotgo examples ## Install: ``` go get -u github.com/go-vgo/robotgo ``` ## [Examples:](https://github.com/go-vgo/robotgo/blob/master/examples) #### [Mouse](https://github.com/go-vgo/robotgo/blob/master/examples/mouse/main.go) ```Go package main import ( "github.com/go-vgo/robotgo" ) func main() { // robotgo.ScrollMouse(10, "up") robotgo.Scroll(0, 10) robotgo.MouseClick("left", true) robotgo.MoveSmooth(100, 200, 1.0, 100.0) } ``` #### [Keyboard](https://github.com/go-vgo/robotgo/blob/master/examples/key/main.go) ```Go package main import ( "fmt" "github.com/go-vgo/robotgo" ) func main() { robotgo.TypeStr("Hello World") // robotgo.TypeStr("だんしゃり") robotgo.TypeStr("だんしゃり") // ustr := uint32(robotgo.CharCodeAt("だんしゃり", 0)) // robotgo.UnicodeType(ustr) robotgo.KeyTap("enter") robotgo.TypeStr("en") robotgo.KeyTap("i", "alt", "command") arr := []string{"alt", "command"} robotgo.KeyTap("i", arr) robotgo.WriteAll("Test") text, err := robotgo.ReadAll() if err == nil { fmt.Println(text) } } ``` #### [Screen](https://github.com/go-vgo/robotgo/blob/master/examples/screen/main.go) ```Go package main import ( "fmt" "github.com/go-vgo/robotgo" ) func main() { x, y := robotgo.Location() fmt.Println("pos:", x, y) color := robotgo.GetPixelColor(100, 200) fmt.Println("color----", color) } ``` #### [Bitmap](https://github.com/go-vgo/robotgo/blob/master/examples/bitmap/main.go) ```Go package main import ( "fmt" "github.com/go-vgo/robotgo" ) func main() { bitmap := robotgo.CaptureScreen(10, 20, 30, 40) // use `defer robotgo.FreeBitmap(bit)` to free the bitmap defer robotgo.FreeBitmap(bitmap) fmt.Println("...", bitmap) fx, fy := robotgo.FindBitmap(bitmap) fmt.Println("FindBitmap------", fx, fy) robotgo.SaveBitmap(bitmap, "test.png") } ``` #### [Event](https://github.com/go-vgo/robotgo/blob/master/examples/event/main.go) ```Go package main import ( "fmt" "github.com/go-vgo/robotgo" ) func main() { keve := robotgo.AddEvent("k") if keve { fmt.Println("you press...", "k") } mleft := robotgo.AddEvent("mleft") if mleft { fmt.Println("you press...", "mouse left button") } } ``` #### [Window](https://github.com/go-vgo/robotgo/blob/master/examples/window/main.go) ```Go package main import ( "fmt" "github.com/go-vgo/robotgo" ) func main() { fpid, err := robotgo.FindIds("Google") if err == nil { fmt.Println("pids...", fpid) if len(fpid) > 0 { robotgo.ActivePID(fpid[0]) robotgo.Kill(fpid[0]) } } robotgo.ActiveName("chrome") isExist, err := robotgo.PidExists(100) if err == nil && isExist { fmt.Println("pid exists is", isExist) robotgo.Kill(100) } abool := robotgo.ShowAlert("test", "robotgo") if abool == 0 { fmt.Println("ok@@@", "ok") } title := robotgo.GetTitle() fmt.Println("title@@@", title) } ``` ================================================ FILE: examples/key/main.go ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. package main import ( "fmt" "github.com/go-vgo/robotgo" // "go-vgo/robotgo" ) func typeStr() { // typing "Hello World" robotgo.Type("Hello World!", 0, 1) robotgo.KeySleep = 100 robotgo.Type("だんしゃり") robotgo.Type("Hi galaxy, hi stars, hi MT.Rainier, hi sea. こんにちは世界.") robotgo.Type("So, hi, bye! 你好, 再见!") robotgo.Sleep(1) robotgo.Type("Hi, Seattle space needle, Golden gate bridge, One world trade center.") robotgo.MilliSleep(100) ustr := uint32(robotgo.CharCodeAt("So, hi, bye!", 0)) robotgo.UnicodeType(ustr) err := robotgo.Paste("paste string") fmt.Println("PasteStr: ", err) } func keyTap() { // press "enter" robotgo.KeyTap("enter") robotgo.KeyTap(robotgo.Enter) robotgo.KeySleep = 200 robotgo.KeyTap("a") robotgo.MilliSleep(100) robotgo.KeyTap("a", "ctrl") // hide window err := robotgo.KeyTap("h", "cmd") if err != nil { fmt.Println("robotgo.KeyTap run error is: ", err) } robotgo.KeyTap("h", "cmd") // press "i", "alt", "command" Key combination robotgo.KeyTap(robotgo.KeyI, robotgo.Alt, robotgo.Cmd) robotgo.KeyTap("i", "alt", "cmd") arr := []string{"alt", "cmd"} robotgo.KeyTap("i", arr) robotgo.KeyTap("i", arr) robotgo.KeyTap("i", "cmd", " alt", "shift") // close window robotgo.KeyTap("w", "cmd") // minimize window robotgo.KeyTap("m", "cmd") robotgo.KeyTap("f1", "ctrl") robotgo.KeyTap("a", "control") } func special() { robotgo.Type("{}") robotgo.KeyTap("[", "]") robotgo.KeyToggle("(") robotgo.KeyToggle("(", "up") } func keyToggle() { // robotgo.KeySleep = 150 robotgo.KeyToggle(robotgo.KeyA) robotgo.KeyToggle("a", "down", "alt") robotgo.Sleep(1) robotgo.KeyToggle("a", "up", "alt", "cmd") robotgo.MilliSleep(100) robotgo.KeyToggle("q", "up", "alt", "cmd", "shift") err := robotgo.KeyToggle(robotgo.Enter) if err != nil { fmt.Println("robotgo.KeyToggle run error is: ", err) } } func cilp() { // robotgo.TypeStr("en") // write string to clipboard e := robotgo.WriteAll("テストする") if e != nil { fmt.Println("robotgo.WriteAll err is: ", e) } // read string from clipboard text, err := robotgo.ReadAll() if err != nil { fmt.Println("robotgo.ReadAll err is: ", err) } fmt.Println("text: ", text) } func key() { //////////////////////////////////////////////////////////////////////////////// // Control the keyboard //////////////////////////////////////////////////////////////////////////////// typeStr() special() keyTap() keyToggle() cilp() } func main() { key() } ================================================ FILE: examples/main.go ================================================ // Copyright (c) 2016-2025 AtomAI go-vgo, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. package main import ( "fmt" "github.com/go-vgo/robotgo" // "go-vgo/robotgo" ) func main() { ver := robotgo.GetVersion() fmt.Println("robotgo version is: ", ver) // Control the keyboard // key() // Control the mouse // mouse() // Read the screen // screen() // Bitmap and image processing // bitmap() // Global event listener // event() // Window Handle and progress // window() } ================================================ FILE: examples/mouse/main.go ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. package main import ( "fmt" "github.com/go-vgo/robotgo" // "go-vgo/robotgo" ) func move() { robotgo.MouseSleep = 100 robotgo.Move(100, 200) robotgo.MoveRelative(10, -200) // move the mouse to 100, 200 robotgo.Move(100, 200) // drag mouse with smooth robotgo.DragSmooth(10, 10) robotgo.DragSmooth(100, 200, 1.0, 100.0) // smooth move the mouse to 100, 200 robotgo.MoveSmooth(100, 200) robotgo.MoveSmooth(100, 200, 1.0, 100.0) robotgo.MoveSmoothRelative(10, -100, 1.0, 30.0) for i := 0; i < 1080; i += 1000 { fmt.Println("i: ", i) // MoveMouse(800, i) robotgo.Move(800, i) } } func click() { // click the left mouse button robotgo.Click() // click the right mouse button robotgo.Click("right", false) // double click the left mouse button robotgo.Click("left", true) } func get() { // gets the mouse coordinates x, y := robotgo.Location() fmt.Println("pos:", x, y) if x == 456 && y == 586 { fmt.Println("mouse...", "586") } robotgo.Move(x, y) } func toggleAndScroll() { // scrolls the mouse either up robotgo.ScrollDir(10, "up") robotgo.ScrollDir(10, "right") robotgo.Scroll(100, 10) robotgo.Scroll(0, -10) robotgo.Toggle("left") robotgo.Toggle("left", "up") // toggles the right mouse button robotgo.Toggle("right") robotgo.Toggle("right", "up") } func mouse() { //////////////////////////////////////////////////////////////////////////////// // Control the mouse //////////////////////////////////////////////////////////////////////////////// move() click() get() toggleAndScroll() } func main() { mouse() } ================================================ FILE: examples/scale/main.go ================================================ package main import ( "fmt" "github.com/go-vgo/robotgo" ) func main() { // syscall.NewLazyDLL("user32.dll").NewProc("SetProcessDPIAware").Call() width, height := robotgo.GetScaleSize() fmt.Println("get scale screen size: ", width, height) bitmap := robotgo.CaptureScreen(0, 0, width, height) defer robotgo.FreeBitmap(bitmap) // bitmap.Save(bitmap, "test.png") robotgo.Save(robotgo.ToImage(bitmap), "test.png") robotgo.Scale = true robotgo.Move(10, 10) robotgo.MoveSmooth(100, 100) fmt.Println(robotgo.Location()) num := robotgo.DisplaysNum() for i := 0; i < num; i++ { rect := robotgo.GetScreenRect(i) fmt.Println("rect: ", rect) } } func old() { sx := robotgo.ScaleX() // Deprecated s := robotgo.Scale1() // Deprecated, use the ScaleF() function robotx, roboty := 35*s/100, 25*s/100 fmt.Println("scale: ", sx, s, " pos: ", robotx, roboty) mx, my := robotgo.Location() sx, sy := mx*s/100, my*s/100 rx, ry, rw, rh := sx, sy, robotx, roboty // bit1 := robotgo.CaptureScreen(10, 20, robotw, roboth) bit1 := robotgo.CaptureScreen(rx, ry, rw, rh) defer robotgo.FreeBitmap(bit1) // bitmap.Save(bit1, "test2.png") robotgo.Save(robotgo.ToImage(bit1), "test2.png") clo := robotgo.GetPixelColor(robotx, roboty) fmt.Println("GetPixelColor...", clo) } ================================================ FILE: examples/screen/main.go ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. package main import ( "fmt" "strconv" "github.com/go-vgo/robotgo" // "go-vgo/robotgo" ) func bitmap() { bit := robotgo.CaptureScreen() defer robotgo.FreeBitmap(bit) fmt.Println("abitMap...", bit) gbit := robotgo.ToBitmap(bit) fmt.Println("bitmap...", gbit.Width) gbitMap := robotgo.CaptureGo() fmt.Println("Go CaptureScreen...", gbitMap.Width) // fmt.Println("...", gbitmap.Width, gbitmap.BytesPerPixel) robotgo.SaveCapture("saveCapture.png", 10, 20, 100, 100) img, err := robotgo.CaptureImg() fmt.Println("error: ", err) robotgo.Save(img, "save.png") num := robotgo.DisplaysNum() for i := 0; i < num; i++ { robotgo.DisplayID = i img1, _ := robotgo.CaptureImg() path1 := "save_" + strconv.Itoa(i) robotgo.Save(img1, path1+".png") robotgo.SaveJpeg(img1, path1+".jpeg", 50) img2, _ := robotgo.CaptureImg(10, 10, 20, 20) path2 := "test_" + strconv.Itoa(i) robotgo.Save(img2, path2+".png") robotgo.SaveJpeg(img2, path2+".jpeg", 50) x, y, w, h := robotgo.GetDisplayBounds(i) img3, err := robotgo.CaptureImg(x, y, w, h) fmt.Println("Capture error: ", err) robotgo.Save(img3, path2+"_1.png") } } func color() { // gets the pixel color at 100, 200. color := robotgo.GetPixelColor(100, 200) fmt.Println("color----", color, "-----------------") clo := robotgo.GetPxColor(100, 200) fmt.Println("color...", clo) clostr := robotgo.PadHex(clo) fmt.Println("color...", clostr) rgb := robotgo.RgbToHex(255, 100, 200) rgbstr := robotgo.PadHex(robotgo.U32ToHex(rgb)) fmt.Println("rgb...", rgbstr) hex := robotgo.HexToRgb(uint32(rgb)) fmt.Println("hex...", hex) hexh := robotgo.PadHex(robotgo.U8ToHex(hex)) fmt.Println("HexToRgb...", hexh) // gets the pixel color at 10, 20. color2 := robotgo.GetPixelColor(10, 20) fmt.Println("color---", color2) } func screen() { //////////////////////////////////////////////////////////////////////////////// // Read the screen //////////////////////////////////////////////////////////////////////////////// bitmap() // gets the screen width and height sx, sy := robotgo.GetScreenSize() fmt.Println("get screen size: ", sx, sy) for i := 0; i < robotgo.DisplaysNum(); i++ { s1 := robotgo.ScaleF(i) fmt.Println("ScaleF: ", s1) } sx, sy = robotgo.GetScaleSize() fmt.Println("get screen scale size: ", sx, sy) color() } func main() { screen() } ================================================ FILE: examples/window/main.go ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. package main import ( "fmt" "github.com/go-vgo/robotgo" // "go-vgo/robotgo" ) func alert() { // show Alert Window abool := robotgo.Alert("hello", "robotgo") if abool { fmt.Println("ok@@@", "ok") } robotgo.Alert("hello", "robotgo", "Ok", "Cancel") } func get() { // get the current process id pid := robotgo.GetPid() fmt.Println("pid----", pid) // get current Window Active mdata := robotgo.GetActive() // get current Window Handle hwnd := robotgo.GetHandle() fmt.Println("hwnd---", hwnd) // get current Window title title := robotgo.GetTitle() fmt.Println("title-----", title) // set Window Active robotgo.SetActive(mdata) } func findIds() { // find the process id by the process name fpid, err := robotgo.FindIds("Google") if err != nil { fmt.Println(err) return } if len(fpid) > 0 { robotgo.KeyTap("a", fpid[0]) robotgo.TypeStr("Hi galaxy!", fpid[0]) robotgo.KeyToggle("a", fpid[0], "cmd") robotgo.KeyToggle("a", fpid[0], "cmd", "up") } fmt.Println("pids...", fpid) if len(fpid) > 0 { err = robotgo.ActivePid(fpid[0]) if err != nil { fmt.Println(err) } tl := robotgo.GetTitle(fpid[0]) fmt.Println("pid[0] title is: ", tl) x, y, w, h := robotgo.GetBounds(fpid[0]) fmt.Println("GetBounds is: ", x, y, w, h) // Windows // hwnd := robotgo.FindWindow("google") // hwnd := robotgo.GetHWND() robotgo.MinWindow(fpid[0]) robotgo.MaxWindow(fpid[0]) robotgo.CloseWindow(fpid[0]) robotgo.Kill(fpid[0]) } } func active() { robotgo.ActivePid(100) // robotgo.Sleep(2) robotgo.ActiveName("code") robotgo.Sleep(1) robotgo.ActiveName("chrome") } func findName() { // find the process name by the process id name, err := robotgo.FindName(100) if err == nil { fmt.Println("name: ", name) } // find the all process name names, err := robotgo.FindNames() if err == nil { fmt.Println("name: ", names) } p, err := robotgo.FindPath(100) if err == nil { fmt.Println("path: ", p) } } func ps() { // determine whether the process exists isExist, err := robotgo.PidExists(100) if err == nil && isExist { fmt.Println("pid exists is", isExist) robotgo.Kill(100) } // get the all process id pids, err := robotgo.Pids() if err == nil { fmt.Println("pids: ", pids) } // get the all process struct ps, err := robotgo.Process() if err == nil { fmt.Println("process: ", ps) } } func window() { //////////////////////////////////////////////////////////////////////////////// // Window Handle //////////////////////////////////////////////////////////////////////////////// alert() // get() findIds() active() findName() // ps() // close current Window robotgo.CloseWindow() } func main() { window() } ================================================ FILE: go.mod ================================================ module github.com/go-vgo/robotgo go 1.24.0 require ( github.com/otiai10/gosseract/v2 v2.4.1 // github.com/robotn/gohook v0.31.3 github.com/robotn/xgb v0.10.0 github.com/robotn/xgbutil v0.10.0 github.com/tailscale/win v0.0.0-20250627215312-f4da2b8ee071 github.com/vcaesar/gops v0.41.0 github.com/vcaesar/imgo v0.41.0 github.com/vcaesar/keycode v0.10.1 github.com/vcaesar/screenshot v0.11.1 github.com/vcaesar/tt v0.20.1 ) require ( github.com/dblohm7/wingoes v0.0.0-20250822163801-6d8e6105c62d // indirect github.com/ebitengine/purego v0.9.1 // indirect github.com/gen2brain/shm v0.1.1 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/godbus/dbus/v5 v5.2.0 // indirect github.com/jezek/xgb v1.2.0 // indirect github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/shirou/gopsutil/v4 v4.25.10 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 // indirect golang.org/x/image v0.33.0 // indirect golang.org/x/sys v0.38.0 // indirect ) // replace golang.org/x/sys => github.com/golang/sys v0.0.0-20190109145017-48ac38b7c8cb // go 1.13 ================================================ FILE: go.sum ================================================ github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298/go.mod h1:D+QujdIlUNfa0igpNMk6UIvlb6C252URs4yupRUV4lQ= github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966/go.mod h1:Mid70uvE93zn9wgF92A/r5ixgnvX8Lh68fxp9KQBaI0= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dblohm7/wingoes v0.0.0-20250822163801-6d8e6105c62d h1:QRKpU+9ZBDs62LyBfwhZkJdB5DJX2Sm3p4kUh7l1aA0= github.com/dblohm7/wingoes v0.0.0-20250822163801-6d8e6105c62d/go.mod h1:SUxUaAK/0UG5lYyZR1L1nC4AaYYvSSYTWQSH3FPcxKU= github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A= github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/gen2brain/shm v0.1.1 h1:1cTVA5qcsUFixnDHl14TmRoxgfWEEZlTezpUj1vm5uQ= github.com/gen2brain/shm v0.1.1/go.mod h1:UgIcVtvmOu+aCJpqJX7GOtiN7X2ct+TKLg4RTxwPIUA= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/godbus/dbus/v5 v5.2.0 h1:3WexO+U+yg9T70v9FdHr9kCxYlazaAXUhx2VMkbfax8= github.com/godbus/dbus/v5 v5.2.0/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/jezek/xgb v1.2.0 h1:LzgkD11wOrPnxXEqo588cnjUt4NwMHrFh/tgajo50Q0= github.com/jezek/xgb v1.2.0/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/otiai10/gosseract/v2 v2.4.1 h1:G8AyBpXEeSlcq8TI85LH/pM5SXk8Djy2GEXisgyblRw= github.com/otiai10/gosseract/v2 v2.4.1/go.mod h1:1gNWP4Hgr2o7yqWfs6r5bZxAatjOIdqWxJLWsTsembk= github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs= github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/robotn/xgb v0.0.0-20190912153532-2cb92d044934/go.mod h1:SxQhJskUJ4rleVU44YvnrdvxQr0tKy5SRSigBrCgyyQ= github.com/robotn/xgb v0.10.0 h1:O3kFbIwtwZ3pgLbp1h5slCQ4OpY8BdwugJLrUe6GPIM= github.com/robotn/xgb v0.10.0/go.mod h1:SxQhJskUJ4rleVU44YvnrdvxQr0tKy5SRSigBrCgyyQ= github.com/robotn/xgbutil v0.10.0 h1:gvf7mGQqCWQ68aHRtCxgdewRk+/KAJui6l3MJQQRCKw= github.com/robotn/xgbutil v0.10.0/go.mod h1:svkDXUDQjUiWzLrA0OZgHc4lbOts3C+uRfP6/yjwYnU= github.com/shirou/gopsutil/v4 v4.25.10 h1:at8lk/5T1OgtuCp+AwrDofFRjnvosn0nkN2OLQ6g8tA= github.com/shirou/gopsutil/v4 v4.25.10/go.mod h1:+kSwyC8DRUD9XXEHCAFjK+0nuArFJM0lva+StQAcskM= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tailscale/win v0.0.0-20250627215312-f4da2b8ee071 h1:qo7kOhoN5DHioXNlFytBzIoA5glW6lsb8YqV0lP3IyE= github.com/tailscale/win v0.0.0-20250627215312-f4da2b8ee071/go.mod h1:aMd4yDHLjbOuYP6fMxj1d9ACDQlSWwYztcpybGHCQc8= github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA= github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= github.com/vcaesar/gops v0.41.0 h1:FG748Jyw3FOuZnbzSgB+CQSx2e5LbLCPWV2JU1brFdc= github.com/vcaesar/gops v0.41.0/go.mod h1:/3048L7Rj7QjQKTSB+kKc7hDm63YhTWy5QJ10TCP37A= github.com/vcaesar/imgo v0.41.0 h1:kNLYGrThXhB9Dd6IwFmfPnxq9P6yat2g7dpPjr7OWO8= github.com/vcaesar/imgo v0.41.0/go.mod h1:/LGOge8etlzaVu/7l+UfhJxR6QqaoX5yeuzGIMfWb4I= github.com/vcaesar/keycode v0.10.1 h1:0DesGmMAPWpYTCYddOFiCMKCDKgNnwiQa2QXindVUHw= github.com/vcaesar/keycode v0.10.1/go.mod h1:JNlY7xbKsh+LAGfY2j4M3znVrGEm5W1R8s/Uv6BJcfQ= github.com/vcaesar/screenshot v0.11.1 h1:GgPuN89XC4Yh38dLx4quPlSo3YiWWhwIria/j3LtrqU= github.com/vcaesar/screenshot v0.11.1/go.mod h1:gJNwHBiP1v1v7i8TQ4yV1XJtcyn2I/OJL7OziVQkwjs= github.com/vcaesar/tt v0.20.1 h1:D/jUeeVCNbq3ad8M7hhtB3J9x5RZ6I1n1eZ0BJp7M+4= github.com/vcaesar/tt v0.20.1/go.mod h1:cH2+AwGAJm19Wa6xvEa+0r+sXDJBT0QgNQey6mwqLeU= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 h1:DHNhtq3sNNzrvduZZIiFyXWOL9IWaDPHqTnLJp+rCBY= golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= golang.org/x/image v0.33.0 h1:LXRZRnv1+zGd5XBUVRFmYEphyyKJjQjCRiOuAP3sZfQ= golang.org/x/image v0.33.0/go.mod h1:DD3OsTYT9chzuzTQt+zMcOlBHgfoKQb1gry8p76Y1sc= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= ================================================ FILE: img.go ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. package robotgo import ( "image" "os/exec" "unsafe" "github.com/vcaesar/imgo" ) // DecodeImg decode the image to image.Image and return func DecodeImg(path string) (image.Image, string, error) { return imgo.DecodeFile(path) } // OpenImg open the image return []byte func OpenImg(path string) ([]byte, error) { return imgo.ImgToBytes(path) } // Read read the file return image.Image func Read(path string) (image.Image, error) { return imgo.Read(path) } // Save create a image file with the image.Image func Save(img image.Image, path string, quality ...int) error { return imgo.Save(path, img, quality...) } // SaveImg save the image by []byte func SaveImg(b []byte, path string) error { return imgo.SaveByte(path, b) } // SavePng save the image by image.Image func SavePng(img image.Image, path string) error { return imgo.SaveToPNG(path, img) } // SaveJpeg save the image by image.Image func SaveJpeg(img image.Image, path string, quality ...int) error { return imgo.SaveToJpeg(path, img, quality...) } // ToByteImg convert image.Image to []byte func ToByteImg(img image.Image, fm ...string) []byte { return imgo.ToByte(img, fm...) } // ToStringImg convert image.Image to string func ToStringImg(img image.Image, fm ...string) string { return string(ToByteImg(img, fm...)) } // StrToImg convert base64 string to image.Image func StrToImg(data string) (image.Image, error) { return imgo.StrToImg(data) } // ByteToImg convert []byte to image.Image func ByteToImg(b []byte) (image.Image, error) { return imgo.ByteToImg(b) } // ImgSize get the file image size func ImgSize(path string) (int, int, error) { return imgo.GetSize(path) } // Width return the image.Image width func Width(img image.Image) int { return img.Bounds().Max.X } // Height return the image.Image height func Height(img image.Image) int { return img.Bounds().Max.Y } // RGBAToBitmap convert the standard image.RGBA to Bitmap func RGBAToBitmap(r1 *image.RGBA) (bit Bitmap) { bit.Width = r1.Bounds().Size().X bit.Height = r1.Bounds().Size().Y bit.Bytewidth = r1.Stride src := ToUint8p(r1.Pix) bit.ImgBuf = src bit.BitsPixel = 32 bit.BytesPerPixel = 32 / 8 return } // ImgToBitmap convert the standard image.Image to Bitmap func ImgToBitmap(m image.Image) (bit Bitmap) { bit.Width = m.Bounds().Size().X bit.Height = m.Bounds().Size().Y pix, stride, _ := imgo.EncodeImg(m) bit.Bytewidth = stride src := ToUint8p(pix) bit.ImgBuf = src // bit.BitsPixel = 32 bit.BytesPerPixel = 32 / 8 return } // ToUint8p convert the []uint8 to uint8 pointer func ToUint8p(dst []uint8) *uint8 { src := make([]uint8, len(dst)+10) for i := 0; i <= len(dst)-4; i += 4 { src[i+3] = dst[i+3] src[i] = dst[i+2] src[i+1] = dst[i+1] src[i+2] = dst[i] } ptr := unsafe.Pointer(&src[0]) return (*uint8)(ptr) } // ToRGBAGo convert Bitmap to standard image.RGBA func ToRGBAGo(bmp1 Bitmap) *image.RGBA { img1 := image.NewRGBA(image.Rect(0, 0, bmp1.Width, bmp1.Height)) img1.Pix = make([]uint8, bmp1.Bytewidth*bmp1.Height) copyToVUint8A(img1.Pix, bmp1.ImgBuf) img1.Stride = bmp1.Bytewidth return img1 } func val(p *uint8, n int) uint8 { addr := uintptr(unsafe.Pointer(p)) addr += uintptr(n) p1 := (*uint8)(unsafe.Pointer(addr)) return *p1 } func copyToVUint8A(dst []uint8, src *uint8) { for i := 0; i <= len(dst)-4; i += 4 { dst[i] = val(src, i+2) dst[i+1] = val(src, i+1) dst[i+2] = val(src, i) dst[i+3] = val(src, i+3) } } // GetText get the image text by tesseract ocr // // robotgo.GetText(imgPath, lang string) func GetText(imgPath string, args ...string) (string, error) { var lang = "eng" if len(args) > 0 { lang = args[0] if lang == "zh" { lang = "chi_sim" } } body, err := exec.Command("tesseract", imgPath, "stdout", "-l", lang).Output() if err != nil { return "", err } return string(body), nil } ================================================ FILE: key/key.go ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. package key ================================================ FILE: key/key_windows.go ================================================ //go:build windows // +build windows package key ================================================ FILE: key/keycode.h ================================================ #pragma once #ifndef KEYCODE_H #define KEYCODE_H #include "../base/os.h" #if defined(IS_MACOSX) #include /* Really only need */ #include #import enum _MMKeyCode { // a-z, 0-9 K_NOT_A_KEY = 9999, K_BACKSPACE = kVK_Delete, K_DELETE = kVK_ForwardDelete, K_RETURN = kVK_Return, K_TAB = kVK_Tab, K_ESCAPE = kVK_Escape, K_UP = kVK_UpArrow, K_DOWN = kVK_DownArrow, K_RIGHT = kVK_RightArrow, K_LEFT = kVK_LeftArrow, K_HOME = kVK_Home, K_END = kVK_End, K_PAGEUP = kVK_PageUp, K_PAGEDOWN = kVK_PageDown, K_F1 = kVK_F1, K_F2 = kVK_F2, K_F3 = kVK_F3, K_F4 = kVK_F4, K_F5 = kVK_F5, K_F6 = kVK_F6, K_F7 = kVK_F7, K_F8 = kVK_F8, K_F9 = kVK_F9, K_F10 = kVK_F10, K_F11 = kVK_F11, K_F12 = kVK_F12, K_F13 = kVK_F13, K_F14 = kVK_F14, K_F15 = kVK_F15, K_F16 = kVK_F16, K_F17 = kVK_F17, K_F18 = kVK_F18, K_F19 = kVK_F19, K_F20 = kVK_F20, K_F21 = K_NOT_A_KEY, K_F22 = K_NOT_A_KEY, K_F23 = K_NOT_A_KEY, K_F24 = K_NOT_A_KEY, K_META = kVK_Command, K_LMETA = kVK_Command, K_RMETA = kVK_RightCommand, K_ALT = kVK_Option, K_LALT = kVK_Option, K_RALT = kVK_RightOption, K_CONTROL = kVK_Control, K_LCONTROL = kVK_Control, K_RCONTROL = kVK_RightControl, K_SHIFT = kVK_Shift, K_LSHIFT = kVK_Shift, K_RSHIFT = kVK_RightShift, K_CAPSLOCK = kVK_CapsLock, K_SPACE = kVK_Space, K_INSERT = kVK_Help, // K_PRINTSCREEN = K_NOT_A_KEY, K_PRINTSCREEN = kVK_F13, K_MENU = K_NOT_A_KEY, K_NUMPAD_0 = kVK_ANSI_Keypad0, K_NUMPAD_1 = kVK_ANSI_Keypad1, K_NUMPAD_2 = kVK_ANSI_Keypad2, K_NUMPAD_3 = kVK_ANSI_Keypad3, K_NUMPAD_4 = kVK_ANSI_Keypad4, K_NUMPAD_5 = kVK_ANSI_Keypad5, K_NUMPAD_6 = kVK_ANSI_Keypad6, K_NUMPAD_7 = kVK_ANSI_Keypad7, K_NUMPAD_8 = kVK_ANSI_Keypad8, K_NUMPAD_9 = kVK_ANSI_Keypad9, K_NUMPAD_LOCK = kVK_ANSI_KeypadClear, // K_NUMPAD_DECIMAL = kVK_ANSI_KeypadDecimal, K_NUMPAD_PLUS = kVK_ANSI_KeypadPlus, K_NUMPAD_MINUS = kVK_ANSI_KeypadMinus, K_NUMPAD_MUL = kVK_ANSI_KeypadMultiply, K_NUMPAD_DIV = kVK_ANSI_KeypadDivide, K_NUMPAD_CLEAR = kVK_ANSI_KeypadClear, K_NUMPAD_ENTER = kVK_ANSI_KeypadEnter, K_NUMPAD_EQUAL = kVK_ANSI_KeypadEquals, K_NUMPAD_LB = kVK_ANSI_LeftBracket, K_NUMPAD_RB = kVK_ANSI_RightBracket, K_Backslash = kVK_ANSI_Backslash, K_Semicolon = kVK_ANSI_Semicolon, K_Quote = kVK_ANSI_Quote, K_Slash = kVK_ANSI_Slash, K_Grave = kVK_ANSI_Grave, K_AUDIO_VOLUME_MUTE = 1007, K_AUDIO_VOLUME_DOWN = 1001, K_AUDIO_VOLUME_UP = 1000, K_AUDIO_PLAY = 1016, K_AUDIO_STOP = K_NOT_A_KEY, K_AUDIO_PAUSE = 1016, K_AUDIO_PREV = 1018, K_AUDIO_NEXT = 1017, K_AUDIO_REWIND = K_NOT_A_KEY, K_AUDIO_FORWARD = K_NOT_A_KEY, K_AUDIO_REPEAT = K_NOT_A_KEY, K_AUDIO_RANDOM = K_NOT_A_KEY, K_LIGHTS_MON_UP = 1002, K_LIGHTS_MON_DOWN = 1003, K_LIGHTS_KBD_TOGGLE = 1023, K_LIGHTS_KBD_UP = 1021, K_LIGHTS_KBD_DOWN = 1022 }; typedef CGKeyCode MMKeyCode; #elif defined(USE_X11) #include #include enum _MMKeyCode { K_NOT_A_KEY = 9999, K_BACKSPACE = XK_BackSpace, K_DELETE = XK_Delete, K_RETURN = XK_Return, K_TAB = XK_Tab, K_ESCAPE = XK_Escape, K_UP = XK_Up, K_DOWN = XK_Down, K_RIGHT = XK_Right, K_LEFT = XK_Left, K_HOME = XK_Home, K_END = XK_End, K_PAGEUP = XK_Page_Up, K_PAGEDOWN = XK_Page_Down, K_F1 = XK_F1, K_F2 = XK_F2, K_F3 = XK_F3, K_F4 = XK_F4, K_F5 = XK_F5, K_F6 = XK_F6, K_F7 = XK_F7, K_F8 = XK_F8, K_F9 = XK_F9, K_F10 = XK_F10, K_F11 = XK_F11, K_F12 = XK_F12, K_F13 = XK_F13, K_F14 = XK_F14, K_F15 = XK_F15, K_F16 = XK_F16, K_F17 = XK_F17, K_F18 = XK_F18, K_F19 = XK_F19, K_F20 = XK_F20, K_F21 = XK_F21, K_F22 = XK_F22, K_F23 = XK_F23, K_F24 = XK_F24, K_META = XK_Super_L, K_LMETA = XK_Super_L, K_RMETA = XK_Super_R, K_ALT = XK_Alt_L, K_LALT = XK_Alt_L, K_RALT = XK_Alt_R, K_CONTROL = XK_Control_L, K_LCONTROL = XK_Control_L, K_RCONTROL = XK_Control_R, K_SHIFT = XK_Shift_L, K_LSHIFT = XK_Shift_L, K_RSHIFT = XK_Shift_R, K_CAPSLOCK = XK_Caps_Lock, K_SPACE = XK_space, K_INSERT = XK_Insert, K_PRINTSCREEN = XK_Print, K_MENU = K_NOT_A_KEY, // K_NUMPAD_0 = K_NOT_A_KEY, K_NUMPAD_0 = XK_KP_0, K_NUMPAD_1 = XK_KP_1, K_NUMPAD_2 = XK_KP_2, K_NUMPAD_3 = XK_KP_3, K_NUMPAD_4 = XK_KP_4, K_NUMPAD_5 = XK_KP_5, K_NUMPAD_6 = XK_KP_6, K_NUMPAD_7 = XK_KP_7, K_NUMPAD_8 = XK_KP_8, K_NUMPAD_9 = XK_KP_9, K_NUMPAD_LOCK = XK_Num_Lock, // K_NUMPAD_DECIMAL = XK_KP_Decimal, K_NUMPAD_PLUS = 78, // XK_KP_Add K_NUMPAD_MINUS = 74, // XK_KP_Subtract K_NUMPAD_MUL = 55, // XK_KP_Multiply K_NUMPAD_DIV = 98, // XK_KP_Divide K_NUMPAD_CLEAR = K_NOT_A_KEY, K_NUMPAD_ENTER = 96, // XK_KP_Enter K_NUMPAD_EQUAL = XK_equal, K_NUMPAD_LB = XK_bracketleft, K_NUMPAD_RB = XK_bracketright, K_Backslash = XK_backslash, K_Semicolon = XK_semicolon, K_Quote = XK_apostrophe, K_Slash = XK_slash, K_Grave = XK_grave, K_AUDIO_VOLUME_MUTE = XF86XK_AudioMute, K_AUDIO_VOLUME_DOWN = XF86XK_AudioLowerVolume, K_AUDIO_VOLUME_UP = XF86XK_AudioRaiseVolume, K_AUDIO_PLAY = XF86XK_AudioPlay, K_AUDIO_STOP = XF86XK_AudioStop, K_AUDIO_PAUSE = XF86XK_AudioPause, K_AUDIO_PREV = XF86XK_AudioPrev, K_AUDIO_NEXT = XF86XK_AudioNext, K_AUDIO_REWIND = XF86XK_AudioRewind, K_AUDIO_FORWARD = XF86XK_AudioForward, K_AUDIO_REPEAT = XF86XK_AudioRepeat, K_AUDIO_RANDOM = XF86XK_AudioRandomPlay, K_LIGHTS_MON_UP = XF86XK_MonBrightnessUp, K_LIGHTS_MON_DOWN = XF86XK_MonBrightnessDown, K_LIGHTS_KBD_TOGGLE = XF86XK_KbdLightOnOff, K_LIGHTS_KBD_UP = XF86XK_KbdBrightnessUp, K_LIGHTS_KBD_DOWN = XF86XK_KbdBrightnessDown }; typedef KeySym MMKeyCode; /* * Structs to store key mappings not handled by XStringToKeysym() on some * Linux systems. */ struct XSpecialCharacterMapping { char name; MMKeyCode code; }; struct XSpecialCharacterMapping XSpecialCharacterTable[] = { {'~', XK_asciitilde}, {'_', XK_underscore}, {'[', XK_bracketleft}, {']', XK_bracketright}, {'!', XK_exclam}, {'#', XK_numbersign}, {'$', XK_dollar}, {'%', XK_percent}, {'&', XK_ampersand}, {'*', XK_asterisk}, {'+', XK_plus}, {',', XK_comma}, {'-', XK_minus}, {'.', XK_period}, {'?', XK_question}, {'<', XK_less}, {'>', XK_greater}, {'=', XK_equal}, {'@', XK_at}, {':', XK_colon}, {';', XK_semicolon}, {'{', XK_braceleft}, {'}', XK_braceright}, {'|', XK_bar}, {'^', XK_asciicircum}, {'(', XK_parenleft}, {')', XK_parenright}, {' ', XK_space}, {'/', XK_slash}, {'\\', XK_backslash}, {'`', XK_grave}, {'"', XK_quoteright}, {'\'', XK_quotedbl}, {'\t', XK_Tab}, {'\n', XK_Return} }; #elif defined(IS_WINDOWS) enum _MMKeyCode { K_NOT_A_KEY = 9999, K_BACKSPACE = VK_BACK, K_DELETE = VK_DELETE, K_RETURN = VK_RETURN, K_TAB = VK_TAB, K_ESCAPE = VK_ESCAPE, K_UP = VK_UP, K_DOWN = VK_DOWN, K_RIGHT = VK_RIGHT, K_LEFT = VK_LEFT, K_HOME = VK_HOME, K_END = VK_END, K_PAGEUP = VK_PRIOR, K_PAGEDOWN = VK_NEXT, K_F1 = VK_F1, K_F2 = VK_F2, K_F3 = VK_F3, K_F4 = VK_F4, K_F5 = VK_F5, K_F6 = VK_F6, K_F7 = VK_F7, K_F8 = VK_F8, K_F9 = VK_F9, K_F10 = VK_F10, K_F11 = VK_F11, K_F12 = VK_F12, K_F13 = VK_F13, K_F14 = VK_F14, K_F15 = VK_F15, K_F16 = VK_F16, K_F17 = VK_F17, K_F18 = VK_F18, K_F19 = VK_F19, K_F20 = VK_F20, K_F21 = VK_F21, K_F22 = VK_F22, K_F23 = VK_F23, K_F24 = VK_F24, K_META = VK_LWIN, K_LMETA = VK_LWIN, K_RMETA = VK_RWIN, K_ALT = VK_MENU, K_LALT = VK_LMENU, K_RALT = VK_RMENU, K_CONTROL = VK_CONTROL, K_LCONTROL = VK_LCONTROL, K_RCONTROL = VK_RCONTROL, K_SHIFT = VK_SHIFT, K_LSHIFT = VK_LSHIFT, K_RSHIFT = VK_RSHIFT, K_CAPSLOCK = VK_CAPITAL, K_SPACE = VK_SPACE, K_PRINTSCREEN = VK_SNAPSHOT, K_INSERT = VK_INSERT, K_MENU = VK_APPS, K_NUMPAD_0 = VK_NUMPAD0, K_NUMPAD_1 = VK_NUMPAD1, K_NUMPAD_2 = VK_NUMPAD2, K_NUMPAD_3 = VK_NUMPAD3, K_NUMPAD_4 = VK_NUMPAD4, K_NUMPAD_5 = VK_NUMPAD5, K_NUMPAD_6 = VK_NUMPAD6, K_NUMPAD_7 = VK_NUMPAD7, K_NUMPAD_8 = VK_NUMPAD8, K_NUMPAD_9 = VK_NUMPAD9, K_NUMPAD_LOCK = VK_NUMLOCK, // VK_NUMPAD_ K_NUMPAD_DECIMAL = VK_DECIMAL, K_NUMPAD_PLUS = VK_ADD, K_NUMPAD_MINUS = VK_SUBTRACT, K_NUMPAD_MUL = VK_MULTIPLY, K_NUMPAD_DIV = VK_DIVIDE, K_NUMPAD_CLEAR = K_NOT_A_KEY, K_NUMPAD_ENTER = VK_RETURN, K_NUMPAD_EQUAL = VK_OEM_PLUS, K_NUMPAD_LB = VK_OEM_4, K_NUMPAD_RB = VK_OEM_6, K_Backslash = VK_OEM_5, K_Semicolon = VK_OEM_1, K_Quote = VK_OEM_7, K_Slash = VK_OEM_2, K_Grave = VK_OEM_3, K_AUDIO_VOLUME_MUTE = VK_VOLUME_MUTE, K_AUDIO_VOLUME_DOWN = VK_VOLUME_DOWN, K_AUDIO_VOLUME_UP = VK_VOLUME_UP, K_AUDIO_PLAY = VK_MEDIA_PLAY_PAUSE, K_AUDIO_STOP = VK_MEDIA_STOP, K_AUDIO_PAUSE = VK_MEDIA_PLAY_PAUSE, K_AUDIO_PREV = VK_MEDIA_PREV_TRACK, K_AUDIO_NEXT = VK_MEDIA_NEXT_TRACK, K_AUDIO_REWIND = K_NOT_A_KEY, K_AUDIO_FORWARD = K_NOT_A_KEY, K_AUDIO_REPEAT = K_NOT_A_KEY, K_AUDIO_RANDOM = K_NOT_A_KEY, K_LIGHTS_MON_UP = K_NOT_A_KEY, K_LIGHTS_MON_DOWN = K_NOT_A_KEY, K_LIGHTS_KBD_TOGGLE = K_NOT_A_KEY, K_LIGHTS_KBD_UP = K_NOT_A_KEY, K_LIGHTS_KBD_DOWN = K_NOT_A_KEY }; typedef int MMKeyCode; #endif /* Returns the keyCode corresponding to the current keyboard layout for the * given ASCII character. */ MMKeyCode keyCodeForChar(const char c); #endif /* KEYCODE_H */ ================================================ FILE: key/keycode_c.h ================================================ #include "keycode.h" #if defined(IS_MACOSX) #include #include /* For kVK_ constants, and TIS functions. */ /* Returns string representation of key, if it is printable. Ownership follows the Create Rule; it is the caller's responsibility to release the returned object. */ CFStringRef createStringForKey(CGKeyCode keyCode); #endif MMKeyCode keyCodeForChar(const char c) { #if defined(IS_MACOSX) /* OS X does not appear to have a built-in function for this, so instead it to write our own. */ static CFMutableDictionaryRef charToCodeDict = NULL; CGKeyCode code; UniChar character = c; CFStringRef charStr = NULL; /* Generate table of keycodes and characters. */ if (charToCodeDict == NULL) { size_t i; charToCodeDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 128, &kCFCopyStringDictionaryKeyCallBacks, NULL); if (charToCodeDict == NULL) { return K_NOT_A_KEY; } /* Loop through every keycode (0 - 127) to find its current mapping. */ for (i = 0; i < 128; ++i) { CFStringRef string = createStringForKey((CGKeyCode)i); if (string != NULL) { CFDictionaryAddValue(charToCodeDict, string, (const void *)i); CFRelease(string); } } } charStr = CFStringCreateWithCharacters(kCFAllocatorDefault, &character, 1); /* Our values may be NULL (0), so we need to use this function. */ /* Use pointer-sized variable to avoid stack overflow on 64-bit systems */ const void *codePtr = NULL; if (!CFDictionaryGetValueIfPresent(charToCodeDict, charStr, &codePtr)) { code = UINT16_MAX; /* Error */ } else { code = (CGKeyCode)(uintptr_t)codePtr; } CFRelease(charStr); // TISGetInputSourceProperty may return nil so we need fallback if (code == UINT16_MAX) { return K_NOT_A_KEY; } return (MMKeyCode)code; #elif defined(IS_WINDOWS) MMKeyCode code; code = VkKeyScan(c); if (code == 0xFFFF) { return K_NOT_A_KEY; } return code; #elif defined(USE_X11) char buf[2]; buf[0] = c; buf[1] = '\0'; MMKeyCode code = XStringToKeysym(buf); if (code == NoSymbol) { /* Some special keys are apparently not handled properly */ struct XSpecialCharacterMapping* xs = XSpecialCharacterTable; while (xs->name) { if (c == xs->name) { code = xs->code; // break; } xs++; } } if (code == NoSymbol) { return K_NOT_A_KEY; } // x11 key bug if (c == 60) { code = 44; } return code; #endif } #if defined(IS_MACOSX) CFStringRef createStringForKey(CGKeyCode keyCode){ // TISInputSourceRef currentKeyboard = TISCopyCurrentASCIICapableKeyboardInputSource(); TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardLayoutInputSource(); /* Check if currentKeyboard is NULL to avoid crash */ if (currentKeyboard == NULL) { return NULL; } CFDataRef layoutData = (CFDataRef) TISGetInputSourceProperty( currentKeyboard, kTISPropertyUnicodeKeyLayoutData); if (layoutData == nil) { CFRelease(currentKeyboard); /* Fix memory leak */ return NULL; } const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *) CFDataGetBytePtr(layoutData); UInt32 keysDown = 0; UniChar chars[4]; UniCharCount realLength; UCKeyTranslate(keyboardLayout, keyCode, kUCKeyActionDisplay, 0, LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit, &keysDown, sizeof(chars) / sizeof(chars[0]), &realLength, chars); CFRelease(currentKeyboard); return CFStringCreateWithCharacters(kCFAllocatorDefault, chars, 1); } #endif ================================================ FILE: key/keypress.h ================================================ #pragma once #ifndef KEYPRESS_H #define KEYPRESS_H #include #include "../base/os.h" #include "../base/types.h" #include "keycode.h" #include #if defined(IS_MACOSX) typedef enum { MOD_NONE = 0, MOD_META = kCGEventFlagMaskCommand, MOD_ALT = kCGEventFlagMaskAlternate, MOD_CONTROL = kCGEventFlagMaskControl, MOD_SHIFT = kCGEventFlagMaskShift } MMKeyFlags; #elif defined(USE_X11) enum _MMKeyFlags { MOD_NONE = 0, MOD_META = Mod4Mask, MOD_ALT = Mod1Mask, MOD_CONTROL = ControlMask, MOD_SHIFT = ShiftMask }; typedef unsigned int MMKeyFlags; #elif defined(IS_WINDOWS) enum _MMKeyFlags { MOD_NONE = 0, /* These are already defined by the Win32 API */ /* MOD_ALT = 0, MOD_CONTROL = 0, MOD_SHIFT = 0, */ MOD_META = MOD_WIN }; typedef unsigned int MMKeyFlags; #endif #if defined(IS_WINDOWS) /* Send win32 key event for given key. */ void win32KeyEvent(int key, MMKeyFlags flags, uintptr pid, int8_t isPid); #endif #endif /* KEYPRESS_H */ ================================================ FILE: key/keypress_c.h ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. #include "../base/deadbeef_rand_c.h" #include "../base/microsleep.h" #include "keypress.h" #include "keycode_c.h" #include /* For isupper() */ #if defined(IS_MACOSX) #include #import #import #elif defined(USE_X11) #include // #include "../base/xdisplay_c.h" #endif /* Convenience wrappers around ugly APIs. */ #if defined(IS_WINDOWS) HWND GetHwndByPid(DWORD dwProcessId); HWND getHwnd(uintptr pid, int8_t isPid) { HWND hwnd = (HWND) pid; if (isPid == 0) { hwnd = GetHwndByPid(pid); } return hwnd; } void WIN32_KEY_EVENT_WAIT(MMKeyCode key, DWORD flags, uintptr pid) { win32KeyEvent(key, flags, pid, 0); Sleep(DEADBEEF_RANDRANGE(0, 1)); } #elif defined(USE_X11) Display *XGetMainDisplay(void); void X_KEY_EVENT(Display *display, MMKeyCode key, bool is_press) { XTestFakeKeyEvent(display, XKeysymToKeycode(display, key), is_press, CurrentTime); XSync(display, false); } void X_KEY_EVENT_WAIT(Display *display, MMKeyCode key, bool is_press) { X_KEY_EVENT(display, key, is_press); microsleep(DEADBEEF_UNIFORM(0.0, 0.5)); } #endif #if defined(IS_MACOSX) int SendTo(uintptr pid, CGEventRef event) { if (pid != 0) { CGEventPostToPid(pid, event); } else { CGEventPost(kCGHIDEventTap, event); } CFRelease(event); return 0; } static io_connect_t _getAuxiliaryKeyDriver(void) { static mach_port_t sEventDrvrRef = 0; mach_port_t masterPort, service, iter; kern_return_t kr; if (!sEventDrvrRef) { kr = IOMasterPort(bootstrap_port, &masterPort); assert(KERN_SUCCESS == kr); kr = IOServiceGetMatchingServices(masterPort, IOServiceMatching(kIOHIDSystemClass), &iter); assert(KERN_SUCCESS == kr); service = IOIteratorNext(iter); assert(service); kr = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &sEventDrvrRef); assert(KERN_SUCCESS == kr); IOObjectRelease(service); IOObjectRelease(iter); } return sEventDrvrRef; } #elif defined(IS_WINDOWS) void win32KeyEvent(int key, MMKeyFlags flags, uintptr pid, int8_t isPid) { int scan = MapVirtualKey(key & 0xff, MAPVK_VK_TO_VSC); /* Set the scan code for extended keys */ switch (key){ case VK_RCONTROL: case VK_SNAPSHOT: /* Print Screen */ case VK_RMENU: /* Right Alt / Alt Gr */ case VK_PAUSE: /* Pause / Break */ case VK_HOME: case VK_UP: case VK_PRIOR: /* Page up */ case VK_LEFT: case VK_RIGHT: case VK_END: case VK_DOWN: case VK_NEXT: /* 'Page Down' */ case VK_INSERT: case VK_DELETE: case VK_LWIN: case VK_RWIN: case VK_APPS: /* Application */ case VK_VOLUME_MUTE: case VK_VOLUME_DOWN: case VK_VOLUME_UP: case VK_MEDIA_NEXT_TRACK: case VK_MEDIA_PREV_TRACK: case VK_MEDIA_STOP: case VK_MEDIA_PLAY_PAUSE: case VK_BROWSER_BACK: case VK_BROWSER_FORWARD: case VK_BROWSER_REFRESH: case VK_BROWSER_STOP: case VK_BROWSER_SEARCH: case VK_BROWSER_FAVORITES: case VK_BROWSER_HOME: case VK_LAUNCH_MAIL: { flags |= KEYEVENTF_EXTENDEDKEY; break; } } // todo: test this if (pid != 0) { HWND hwnd = getHwnd(pid, isPid); int down = (flags == 0 ? WM_KEYDOWN : WM_KEYUP); // SendMessage(hwnd, down, key, 0); PostMessageW(hwnd, down, key, 0); return; } /* Set the scan code for keyup */ // if ( flags & KEYEVENTF_KEYUP ) { // scan |= 0x80; // } // keybd_event(key, scan, flags, 0); INPUT keyInput; keyInput.type = INPUT_KEYBOARD; keyInput.ki.wVk = key; keyInput.ki.wScan = scan; keyInput.ki.dwFlags = flags; keyInput.ki.time = 0; keyInput.ki.dwExtraInfo = 0; SendInput(1, &keyInput, sizeof(keyInput)); } #endif void toggleKeyCode(MMKeyCode code, const bool down, MMKeyFlags flags, uintptr pid) { #if defined(IS_MACOSX) /* The media keys all have 1000 added to them to help us detect them. */ if (code >= 1000) { code = code - 1000; /* Get the real keycode. */ NXEventData event; kern_return_t kr; IOGPoint loc = { 0, 0 }; UInt32 evtInfo = code << 16 | (down?NX_KEYDOWN:NX_KEYUP) << 8; bzero(&event, sizeof(NXEventData)); event.compound.subType = NX_SUBTYPE_AUX_CONTROL_BUTTONS; event.compound.misc.L[0] = evtInfo; kr = IOHIDPostEvent(_getAuxiliaryKeyDriver(), NX_SYSDEFINED, loc, &event, kNXEventDataVersion, 0, FALSE); assert(KERN_SUCCESS == kr); } else { CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); CGEventRef keyEvent = CGEventCreateKeyboardEvent(source, (CGKeyCode)code, down); assert(keyEvent != NULL); CGEventSetType(keyEvent, down ? kCGEventKeyDown : kCGEventKeyUp); if (flags != 0) { CGEventSetFlags(keyEvent, (CGEventFlags) flags); } SendTo(pid, keyEvent); CFRelease(source); } #elif defined(IS_WINDOWS) const DWORD dwFlags = down ? 0 : KEYEVENTF_KEYUP; /* Parse modifier keys. */ if (flags & MOD_META) { WIN32_KEY_EVENT_WAIT(K_META, dwFlags, pid); } if (flags & MOD_ALT) { WIN32_KEY_EVENT_WAIT(K_ALT, dwFlags, pid); } if (flags & MOD_CONTROL) { WIN32_KEY_EVENT_WAIT(K_CONTROL, dwFlags, pid); } if (flags & MOD_SHIFT) { WIN32_KEY_EVENT_WAIT(K_SHIFT, dwFlags, pid); } win32KeyEvent(code, dwFlags, pid, 0); #elif defined(USE_X11) Display *display = XGetMainDisplay(); const Bool is_press = down ? True : False; /* Just to be safe. */ /* Parse modifier keys. */ if (flags & MOD_META) { X_KEY_EVENT_WAIT(display, K_META, is_press); } if (flags & MOD_ALT) { X_KEY_EVENT_WAIT(display, K_ALT, is_press); } if (flags & MOD_CONTROL) { X_KEY_EVENT_WAIT(display, K_CONTROL, is_press); } if (flags & MOD_SHIFT) { X_KEY_EVENT_WAIT(display, K_SHIFT, is_press); } X_KEY_EVENT(display, code, is_press); #endif } // void tapKeyCode(MMKeyCode code, MMKeyFlags flags){ // toggleKeyCode(code, true, flags); // microsleep(5.0); // toggleKeyCode(code, false, flags); // } #if defined(USE_X11) bool toUpper(char c) { if (isupper(c)) { return true; } char *special = "~!@#$%^&*()_+{}|:\"<>?"; while (*special) { if (*special == c) { return true; } special++; } return false; } #endif void toggleKey(char c, const bool down, MMKeyFlags flags, uintptr pid) { MMKeyCode keyCode = keyCodeForChar(c); #if defined(USE_X11) if (toUpper(c) && !(flags & MOD_SHIFT)) { flags |= MOD_SHIFT; /* Not sure if this is safe for all layouts. */ } #else if (isupper(c) && !(flags & MOD_SHIFT)) { flags |= MOD_SHIFT; /* Not sure if this is safe for all layouts. */ } #endif #if defined(IS_WINDOWS) int modifiers = keyCode >> 8; // Pull out modifers. if ((modifiers & 1) != 0) { flags |= MOD_SHIFT; } // Uptdate flags from keycode modifiers. if ((modifiers & 2) != 0) { flags |= MOD_CONTROL; } if ((modifiers & 4) != 0) { flags |= MOD_ALT; } keyCode = keyCode & 0xff; // Mask out modifiers. #endif toggleKeyCode(keyCode, down, flags, pid); } // void tapKey(char c, MMKeyFlags flags){ // toggleKey(c, true, flags); // microsleep(5.0); // toggleKey(c, false, flags); // } #if defined(IS_MACOSX) void toggleUnicode(UniChar ch, const bool down, uintptr pid) { /* This function relies on the convenient CGEventKeyboardSetUnicodeString(), convert characters to a keycode, but does not support adding modifier flags. It is only used in typeString(). -- if you need modifier keys, use the above functions instead. */ CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); CGEventRef keyEvent = CGEventCreateKeyboardEvent(source, 0, down); if (keyEvent == NULL) { fputs("Could not create keyboard event.\n", stderr); return; } CGEventKeyboardSetUnicodeString(keyEvent, 1, &ch); SendTo(pid, keyEvent); CFRelease(source); } #else #define toggleUniKey(c, down) toggleKey(c, down, MOD_NONE, 0) #endif // unicode type void unicodeType(const unsigned value, uintptr pid, int8_t isPid) { #if defined(IS_MACOSX) UniChar ch = (UniChar)value; // Convert to unsigned char toggleUnicode(ch, true, pid); microsleep(5.0); toggleUnicode(ch, false, pid); #elif defined(IS_WINDOWS) if (pid != 0) { HWND hwnd = getHwnd(pid, isPid); // SendMessage(hwnd, down, value, 0); PostMessageW(hwnd, WM_CHAR, value, 0); return; } INPUT input[2]; memset(input, 0, sizeof(input)); input[0].type = INPUT_KEYBOARD; input[0].ki.wVk = 0; input[0].ki.wScan = value; input[0].ki.dwFlags = 0x4; // KEYEVENTF_UNICODE; input[1].type = INPUT_KEYBOARD; input[1].ki.wVk = 0; input[1].ki.wScan = value; input[1].ki.dwFlags = KEYEVENTF_KEYUP | 0x4; // KEYEVENTF_UNICODE; SendInput(2, input, sizeof(INPUT)); #elif defined(USE_X11) toggleUniKey(value, true); microsleep(5.0); toggleUniKey(value, false); #endif } #if defined(USE_X11) int input_utf(const char *utf) { Display *dpy = XOpenDisplay(NULL); KeySym sym = XStringToKeysym(utf); // KeySym sym = XKeycodeToKeysym(dpy, utf); int min, max, numcodes; XDisplayKeycodes(dpy, &min, &max); KeySym *keysym; keysym = XGetKeyboardMapping(dpy, min, max-min+1, &numcodes); keysym[(max-min-1)*numcodes] = sym; XChangeKeyboardMapping(dpy, min, numcodes, keysym, (max-min)); XFree(keysym); XFlush(dpy); KeyCode code = XKeysymToKeycode(dpy, sym); XTestFakeKeyEvent(dpy, code, True, 1); XTestFakeKeyEvent(dpy, code, False, 1); XFlush(dpy); XCloseDisplay(dpy); return 0; } #else int input_utf(const char *utf){ return 0; } #endif ================================================ FILE: key.go ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. package robotgo /* // #include "key/keycode.h" #include "key/keypress_c.h" */ import "C" import ( "errors" "math/rand" "reflect" "runtime" "strconv" "strings" "unicode" "unsafe" "github.com/go-vgo/robotgo/clipboard" ) // Defining a bunch of constants. const ( // KeyA define key "a" KeyA = "a" KeyB = "b" KeyC = "c" KeyD = "d" KeyE = "e" KeyF = "f" KeyG = "g" KeyH = "h" KeyI = "i" KeyJ = "j" KeyK = "k" KeyL = "l" KeyM = "m" KeyN = "n" KeyO = "o" KeyP = "p" KeyQ = "q" KeyR = "r" KeyS = "s" KeyT = "t" KeyU = "u" KeyV = "v" KeyW = "w" KeyX = "x" KeyY = "y" KeyZ = "z" // CapA = "A" CapB = "B" CapC = "C" CapD = "D" CapE = "E" CapF = "F" CapG = "G" CapH = "H" CapI = "I" CapJ = "J" CapK = "K" CapL = "L" CapM = "M" CapN = "N" CapO = "O" CapP = "P" CapQ = "Q" CapR = "R" CapS = "S" CapT = "T" CapU = "U" CapV = "V" CapW = "W" CapX = "X" CapY = "Y" CapZ = "Z" // Key0 = "0" Key1 = "1" Key2 = "2" Key3 = "3" Key4 = "4" Key5 = "5" Key6 = "6" Key7 = "7" Key8 = "8" Key9 = "9" KeyGrave = "`" KeyQuoter = '"' KeyQuote = "'" // Backspace backspace key string Backspace = "backspace" Delete = "delete" Enter = "enter" Tab = "tab" Esc = "esc" Escape = "escape" Up = "up" // Up arrow key Down = "down" // Down arrow key Right = "right" // Right arrow key Left = "left" // Left arrow key Home = "home" End = "end" Pageup = "pageup" Pagedown = "pagedown" F1 = "f1" F2 = "f2" F3 = "f3" F4 = "f4" F5 = "f5" F6 = "f6" F7 = "f7" F8 = "f8" F9 = "f9" F10 = "f10" F11 = "f11" F12 = "f12" F13 = "f13" F14 = "f14" F15 = "f15" F16 = "f16" F17 = "f17" F18 = "f18" F19 = "f19" F20 = "f20" F21 = "f21" F22 = "f22" F23 = "f23" F24 = "f24" Cmd = "cmd" // is the "win" key for windows Lcmd = "lcmd" // left command Rcmd = "rcmd" // right command // "command" Alt = "alt" Lalt = "lalt" // left alt Ralt = "ralt" // right alt Ctrl = "ctrl" Lctrl = "lctrl" // left ctrl Rctrl = "rctrl" // right ctrl Control = "control" Shift = "shift" Lshift = "lshift" // left shift Rshift = "rshift" // right shift // "right_shift" Capslock = "capslock" Space = "space" Print = "print" Printscreen = "printscreen" // No Mac support Insert = "insert" Menu = "menu" // Windows only AudioMute = "audio_mute" // Mute the volume AudioVolDown = "audio_vol_down" // Lower the volume AudioVolUp = "audio_vol_up" // Increase the volume AudioPlay = "audio_play" AudioStop = "audio_stop" AudioPause = "audio_pause" AudioPrev = "audio_prev" // Previous Track AudioNext = "audio_next" // Next Track AudioRewind = "audio_rewind" // Linux only AudioForward = "audio_forward" // Linux only AudioRepeat = "audio_repeat" // Linux only AudioRandom = "audio_random" // Linux only Num0 = "num0" // numpad 0 Num1 = "num1" Num2 = "num2" Num3 = "num3" Num4 = "num4" Num5 = "num5" Num6 = "num6" Num7 = "num7" Num8 = "num8" Num9 = "num9" NumLock = "num_lock" NumDecimal = "num." NumPlus = "num+" NumMinus = "num-" NumMul = "num*" NumDiv = "num/" NumClear = "num_clear" NumEnter = "num_enter" NumEqual = "num_equal" LightsMonUp = "lights_mon_up" // Turn up monitor brightness No Windows support LightsMonDown = "lights_mon_down" // Turn down monitor brightness No Windows support LightsKbdToggle = "lights_kbd_toggle" // Toggle keyboard backlight on/off No Windows support LightsKbdUp = "lights_kbd_up" // Turn up keyboard backlight brightness No Windows support LightsKbdDown = "lights_kbd_down" ) // keyNames define a map of key names to MMKeyCode var keyNames = map[string]C.MMKeyCode{ "backspace": C.K_BACKSPACE, "delete": C.K_DELETE, "enter": C.K_RETURN, "tab": C.K_TAB, "esc": C.K_ESCAPE, "escape": C.K_ESCAPE, "up": C.K_UP, "down": C.K_DOWN, "right": C.K_RIGHT, "left": C.K_LEFT, "home": C.K_HOME, "end": C.K_END, "pageup": C.K_PAGEUP, "pagedown": C.K_PAGEDOWN, // "f1": C.K_F1, "f2": C.K_F2, "f3": C.K_F3, "f4": C.K_F4, "f5": C.K_F5, "f6": C.K_F6, "f7": C.K_F7, "f8": C.K_F8, "f9": C.K_F9, "f10": C.K_F10, "f11": C.K_F11, "f12": C.K_F12, "f13": C.K_F13, "f14": C.K_F14, "f15": C.K_F15, "f16": C.K_F16, "f17": C.K_F17, "f18": C.K_F18, "f19": C.K_F19, "f20": C.K_F20, "f21": C.K_F21, "f22": C.K_F22, "f23": C.K_F23, "f24": C.K_F24, // "cmd": C.K_META, "lcmd": C.K_LMETA, "rcmd": C.K_RMETA, "command": C.K_META, "alt": C.K_ALT, "lalt": C.K_LALT, "ralt": C.K_RALT, "ctrl": C.K_CONTROL, "lctrl": C.K_LCONTROL, "rctrl": C.K_RCONTROL, "control": C.K_CONTROL, "shift": C.K_SHIFT, "lshift": C.K_LSHIFT, "rshift": C.K_RSHIFT, "right_shift": C.K_RSHIFT, "capslock": C.K_CAPSLOCK, "space": C.K_SPACE, "print": C.K_PRINTSCREEN, "printscreen": C.K_PRINTSCREEN, "insert": C.K_INSERT, "menu": C.K_MENU, "audio_mute": C.K_AUDIO_VOLUME_MUTE, "audio_vol_down": C.K_AUDIO_VOLUME_DOWN, "audio_vol_up": C.K_AUDIO_VOLUME_UP, "audio_play": C.K_AUDIO_PLAY, "audio_stop": C.K_AUDIO_STOP, "audio_pause": C.K_AUDIO_PAUSE, "audio_prev": C.K_AUDIO_PREV, "audio_next": C.K_AUDIO_NEXT, "audio_rewind": C.K_AUDIO_REWIND, "audio_forward": C.K_AUDIO_FORWARD, "audio_repeat": C.K_AUDIO_REPEAT, "audio_random": C.K_AUDIO_RANDOM, "num0": C.K_NUMPAD_0, "num1": C.K_NUMPAD_1, "num2": C.K_NUMPAD_2, "num3": C.K_NUMPAD_3, "num4": C.K_NUMPAD_4, "num5": C.K_NUMPAD_5, "num6": C.K_NUMPAD_6, "num7": C.K_NUMPAD_7, "num8": C.K_NUMPAD_8, "num9": C.K_NUMPAD_9, "num_lock": C.K_NUMPAD_LOCK, // todo: removed "numpad_0": C.K_NUMPAD_0, "numpad_1": C.K_NUMPAD_1, "numpad_2": C.K_NUMPAD_2, "numpad_3": C.K_NUMPAD_3, "numpad_4": C.K_NUMPAD_4, "numpad_5": C.K_NUMPAD_5, "numpad_6": C.K_NUMPAD_6, "numpad_7": C.K_NUMPAD_7, "numpad_8": C.K_NUMPAD_8, "numpad_9": C.K_NUMPAD_9, "numpad_lock": C.K_NUMPAD_LOCK, "num.": C.K_NUMPAD_DECIMAL, "num+": C.K_NUMPAD_PLUS, "num-": C.K_NUMPAD_MINUS, "num*": C.K_NUMPAD_MUL, "num/": C.K_NUMPAD_DIV, "num_clear": C.K_NUMPAD_CLEAR, "num_enter": C.K_NUMPAD_ENTER, "num_equal": C.K_NUMPAD_EQUAL, "lights_mon_up": C.K_LIGHTS_MON_UP, "lights_mon_down": C.K_LIGHTS_MON_DOWN, "lights_kbd_toggle": C.K_LIGHTS_KBD_TOGGLE, "lights_kbd_up": C.K_LIGHTS_KBD_UP, "lights_kbd_down": C.K_LIGHTS_KBD_DOWN, // { NULL: C.K_NOT_A_KEY } } // CmdCtrl If the operating system is macOS, return the key string "cmd", // otherwise return the key string "ctrl func CmdCtrl() string { if runtime.GOOS == "darwin" { return "cmd" } return "ctrl" } // It sends a key press and release to the active application func tapKeyCode(code C.MMKeyCode, flags C.MMKeyFlags, pid C.uintptr) { C.toggleKeyCode(code, true, flags, pid) MilliSleep(3) C.toggleKeyCode(code, false, flags, pid) } var keyErr = errors.New("Invalid key flag specified.") func checkKeyCodes(k string) (key C.MMKeyCode, err error) { if k == "" { return } if len(k) == 1 { val1 := C.CString(k) defer C.free(unsafe.Pointer(val1)) key = C.keyCodeForChar(*val1) if key == C.K_NOT_A_KEY { err = keyErr return } return } if v, ok := keyNames[k]; ok { key = v if key == C.K_NOT_A_KEY { err = keyErr return } } return } func checkKeyFlags(f string) (flags C.MMKeyFlags) { m := map[string]C.MMKeyFlags{ "alt": C.MOD_ALT, "ralt": C.MOD_ALT, "lalt": C.MOD_ALT, "cmd": C.MOD_META, "rcmd": C.MOD_META, "lcmd": C.MOD_META, "ctrl": C.MOD_CONTROL, "rctrl": C.MOD_CONTROL, "lctrl": C.MOD_CONTROL, "shift": C.MOD_SHIFT, "rshift": C.MOD_SHIFT, "lshift": C.MOD_SHIFT, "none": C.MOD_NONE, } if v, ok := m[f]; ok { return v } return } func getFlagsFromValue(value []string) (flags C.MMKeyFlags) { if len(value) <= 0 { return } for i := 0; i < len(value); i++ { var f C.MMKeyFlags = C.MOD_NONE f = checkKeyFlags(value[i]) flags = (C.MMKeyFlags)(flags | f) } return } func upKeyArr(keyArr []string, pid int) { for i := 0; i < len(keyArr); i++ { key1, _ := checkKeyCodes(keyArr[i]) C.toggleKeyCode(key1, false, C.MOD_NONE, C.uintptr(pid)) } } func keyTaps(k string, keyArr []string, pid int) error { flags := getFlagsFromValue(keyArr) key, err := checkKeyCodes(k) if err != nil { return err } tapKeyCode(key, flags, C.uintptr(pid)) MilliSleep(KeySleep) upKeyArr(keyArr, pid) return nil } func getKeyDown(keyArr []string) (bool, []string) { if len(keyArr) <= 0 { keyArr = append(keyArr, "down") } down := true if keyArr[0] == "up" { down = false } if keyArr[0] == "up" || keyArr[0] == "down" { keyArr = keyArr[1:] } return down, keyArr } func keyTogglesB(k string, down bool, keyArr []string, pid int) error { flags := getFlagsFromValue(keyArr) key, err := checkKeyCodes(k) if err != nil { return err } C.toggleKeyCode(key, C.bool(down), flags, C.uintptr(pid)) MilliSleep(KeySleep) if !down { upKeyArr(keyArr, pid) } return nil } func keyToggles(k string, keyArr []string, pid int) error { down, keyArr1 := getKeyDown(keyArr) return keyTogglesB(k, down, keyArr1, pid) } /* __ ___ ___________ ____ .______ ______ ___ .______ _______ | |/ / | ____\ \ / / | _ \ / __ \ / \ | _ \ | \ | ' / | |__ \ \/ / | |_) | | | | | / ^ \ | |_) | | .--. | | < | __| \_ _/ | _ < | | | | / /_\ \ | / | | | | | . \ | |____ | | | |_) | | `--' | / _____ \ | |\ \----.| '--' | |__|\__\ |_______| |__| |______/ \______/ /__/ \__\ | _| `._____||_______/ */ // ToInterfaces convert []string to []interface{} func ToInterfaces(fields []string) []interface{} { res := make([]interface{}, 0, len(fields)) for _, s := range fields { res = append(res, s) } return res } // ToStrings convert []interface{} to []string func ToStrings(fields []interface{}) []string { res := make([]string, 0, len(fields)) for _, s := range fields { res = append(res, s.(string)) } return res } // toErr it converts a C string to a Go error func toErr(str *C.char) error { gstr := C.GoString(str) if gstr == "" { return nil } return errors.New(gstr) } func appendShift(key string, len1 int, args ...interface{}) (string, []interface{}) { if len(key) > 0 && unicode.IsUpper([]rune(key)[0]) { args = append(args, "shift") } key = strings.ToLower(key) if _, ok := Special[key]; ok { key = Special[key] if len(args) <= len1 { args = append(args, "shift") } } return key, args } // KeyTap taps the keyboard code; // // See keys supported: // // https://github.com/go-vgo/robotgo/blob/master/docs/keys.md#keys // // Examples: // // robotgo.KeySleep = 100 // 100 millisecond // robotgo.KeyTap("a") // robotgo.KeyTap("i", "alt", "command") // // arr := []string{"alt", "command"} // robotgo.KeyTap("i", arr) // // robotgo.KeyTap("k", pid int) func KeyTap(key string, args ...interface{}) error { var keyArr []string key, args = appendShift(key, 0, args...) pid := 0 if len(args) > 0 { if reflect.TypeOf(args[0]) == reflect.TypeOf(keyArr) { keyArr = args[0].([]string) } else { if reflect.TypeOf(args[0]) == reflect.TypeOf(pid) { pid = args[0].(int) keyArr = ToStrings(args[1:]) } else { keyArr = ToStrings(args) } } } return keyTaps(key, keyArr, pid) } func getToggleArgs(args ...interface{}) (pid int, keyArr []string) { if len(args) > 0 && reflect.TypeOf(args[0]) == reflect.TypeOf(pid) { pid = args[0].(int) keyArr = ToStrings(args[1:]) } else { keyArr = ToStrings(args) } return } // KeyToggle toggles the keyboard, if there not have args default is "down" // // See keys: // // https://github.com/go-vgo/robotgo/blob/master/docs/keys.md#keys // // Examples: // // robotgo.KeyToggle("a") // robotgo.KeyToggle("a", "up") // // robotgo.KeyToggle("a", "up", "alt", "cmd") // robotgo.KeyToggle("k", pid int) func KeyToggle(key string, args ...interface{}) error { key, args = appendShift(key, 1, args...) pid, keyArr := getToggleArgs(args...) return keyToggles(key, keyArr, pid) } // KeyPress press key string func KeyPress(key string, args ...interface{}) error { err := KeyDown(key, args...) if err != nil { return err } MilliSleep(1 + rand.Intn(3)) return KeyUp(key, args...) } // KeyDown press down a key func KeyDown(key string, args ...interface{}) error { return KeyToggle(key, args...) } // KeyUp press up a key func KeyUp(key string, args ...interface{}) error { arr := []interface{}{"up"} arr = append(arr, args...) return KeyToggle(key, arr...) } // ReadAll read string from clipboard func ReadAll() (string, error) { return clipboard.ReadAll() } // WriteAll write string to clipboard func WriteAll(text string) error { return clipboard.WriteAll(text) } // CharCodeAt char code at utf-8 func CharCodeAt(s string, n int) rune { i := 0 for _, r := range s { if i == n { return r } i++ } return 0 } // UnicodeType tap the uint32 unicode func UnicodeType(str uint32, args ...int) { cstr := C.uint(str) pid := 0 if len(args) > 0 { pid = args[0] } isPid := 0 if len(args) > 1 { isPid = args[1] } C.unicodeType(cstr, C.uintptr(pid), C.int8_t(isPid)) } // ToUC trans string to unicode []string func ToUC(text string) []string { var uc []string for _, r := range text { textQ := strconv.QuoteToASCII(string(r)) textUnQ := textQ[1 : len(textQ)-1] st := strings.Replace(textUnQ, "\\u", "U", -1) if st == "\\\\" { st = "\\" } if st == `\"` { st = `"` } uc = append(uc, st) } return uc } func inputUTF(str string) { cstr := C.CString(str) C.input_utf(cstr) C.free(unsafe.Pointer(cstr)) } // TypeStr tap a string // // Deprecated: use the Type() func TypeStr(str string, args ...int) { Type(str, args...) } // Type type a string (supported UTF-8) // // robotgo.Type(string: "The string to send", int: pid, "milli_sleep time", "x11 option") // // Examples: // // robotgo.Type("abc@123, Hi galaxy, こんにちは") // robotgo.Type("To be or not to be, this is questions.", pid int) func Type(str string, args ...int) { var tm, tm1 = 0, 7 if len(args) > 1 { tm = args[1] } if len(args) > 2 { tm1 = args[2] } pid := 0 if len(args) > 0 { pid = args[0] } if runtime.GOOS == "linux" { strUc := ToUC(str) for i := 0; i < len(strUc); i++ { ru := []rune(strUc[i]) if len(ru) <= 1 { ustr := uint32(CharCodeAt(strUc[i], 0)) UnicodeType(ustr, pid) } else { inputUTF(strUc[i]) MilliSleep(tm1) } MilliSleep(tm) } return } for i := 0; i < len([]rune(str)); i++ { ustr := uint32(CharCodeAt(str, i)) UnicodeType(ustr, pid) // if len(args) > 0 { MilliSleep(tm) // } } MilliSleep(KeySleep) } // PasteStr paste a string // // Deprecated: use the Paste() func PasteStr(str string) error { return Paste(str) } // Paste paste a string (supported UTF-8), // write the string to clipboard and tap `cmd + v` func Paste(str string) error { err := clipboard.WriteAll(str) if err != nil { return err } return CmdV() } // CmdV tap key command + v or control + v func CmdV() error { if runtime.GOOS == "darwin" { return KeyTap("v", "command") } return KeyTap("v", "control") } // TypeStrDelay type string width delay // // Deprecated: use the TypeDelay() func TypeStrDelay(str string, delay int) { TypeDelay(str, delay) } // TypeDelay type string with delayed // And you can use robotgo.KeySleep = 100 to delayed not this function func TypeDelay(str string, delay int) { TypeStr(str) MilliSleep(delay) } // SetDelay sets the key and mouse delay // robotgo.SetDelay(100) option the robotgo.KeySleep and robotgo.MouseSleep = d func SetDelay(d ...int) { v := 10 if len(d) > 0 { v = d[0] } KeySleep = v MouseSleep = v } ================================================ FILE: keycode.go ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. package robotgo import ( "github.com/vcaesar/keycode" ) type uMap map[string]uint16 // MouseMap robotgo hook mouse's code map var MouseMap = keycode.MouseMap const ( // Mleft mouse left button Mleft = "left" Mright = "right" Center = "center" WheelDown = "wheelDown" WheelUp = "wheelUp" WheelLeft = "wheelLeft" WheelRight = "wheelRight" ) // Keycode robotgo hook key's code map var Keycode = keycode.Keycode // Special is the special key map var Special = keycode.Special ================================================ FILE: mouse/mouse.go ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. package mouse ================================================ FILE: mouse/mouse.h ================================================ #pragma once #ifndef MOUSE_H #define MOUSE_H #include "../base/os.h" #include "../base/types.h" #include #if defined(IS_MACOSX) #include typedef enum { LEFT_BUTTON = kCGMouseButtonLeft, RIGHT_BUTTON = kCGMouseButtonRight, CENTER_BUTTON = kCGMouseButtonCenter, WheelDown = 4, WheelUp = 5, WheelLeft = 6, WheelRight = 7, } MMMouseButton; #elif defined(USE_X11) enum _MMMouseButton { LEFT_BUTTON = 1, CENTER_BUTTON = 2, RIGHT_BUTTON = 3, WheelDown = 4, WheelUp = 5, WheelLeft = 6, WheelRight = 7, }; typedef unsigned int MMMouseButton; #elif defined(IS_WINDOWS) enum _MMMouseButton { LEFT_BUTTON = 1, CENTER_BUTTON = 2, RIGHT_BUTTON = 3, WheelDown = 4, WheelUp = 5, WheelLeft = 6, WheelRight = 7, }; typedef unsigned int MMMouseButton; #else #error "No mouse button constants set for platform" #endif #endif /* MOUSE_H */ ================================================ FILE: mouse/mouse_c.h ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. #include "mouse.h" #include "../base/deadbeef_rand.h" #include "../base/microsleep.h" #include /* For floor() */ #if defined(IS_MACOSX) // #include #include // #include #elif defined(USE_X11) #include #include #include #endif /* Some convenience macros for converting our enums to the system API types. */ #if defined(IS_MACOSX) CGEventType MMMouseDownToCGEventType(MMMouseButton button) { if (button == LEFT_BUTTON) { return kCGEventLeftMouseDown; } if (button == RIGHT_BUTTON) { return kCGEventRightMouseDown; } return kCGEventOtherMouseDown; } CGEventType MMMouseUpToCGEventType(MMMouseButton button) { if (button == LEFT_BUTTON) { return kCGEventLeftMouseUp; } if (button == RIGHT_BUTTON) { return kCGEventRightMouseUp; } return kCGEventOtherMouseUp; } CGEventType MMMouseDragToCGEventType(MMMouseButton button) { if (button == LEFT_BUTTON) { return kCGEventLeftMouseDragged; } if (button == RIGHT_BUTTON) { return kCGEventRightMouseDragged; } return kCGEventOtherMouseDragged; } CGEventType MMMouseToCGEventType(bool down, MMMouseButton button) { if (down) { return MMMouseDownToCGEventType(button); } return MMMouseUpToCGEventType(button); } #elif defined(IS_WINDOWS) DWORD MMMouseUpToMEventF(MMMouseButton button) { if (button == LEFT_BUTTON) { return MOUSEEVENTF_LEFTUP; } if (button == RIGHT_BUTTON) { return MOUSEEVENTF_RIGHTUP; } return MOUSEEVENTF_MIDDLEUP; } DWORD MMMouseDownToMEventF(MMMouseButton button) { if (button == LEFT_BUTTON) { return MOUSEEVENTF_LEFTDOWN; } if (button == RIGHT_BUTTON) { return MOUSEEVENTF_RIGHTDOWN; } return MOUSEEVENTF_MIDDLEDOWN; } DWORD MMMouseToMEventF(bool down, MMMouseButton button) { if (down) { return MMMouseDownToMEventF(button); } return MMMouseUpToMEventF(button); } #endif #if defined(IS_MACOSX) /* Calculate the delta for a mouse move and add them to the event. */ void calculateDeltas(CGEventRef *event, MMPointInt32 point) { /* The next few lines are a workaround for games not detecting mouse moves. */ CGEventRef get = CGEventCreate(NULL); CGPoint mouse = CGEventGetLocation(get); // Calculate the deltas. int64_t deltaX = point.x - mouse.x; int64_t deltaY = point.y - mouse.y; CGEventSetIntegerValueField(*event, kCGMouseEventDeltaX, deltaX); CGEventSetIntegerValueField(*event, kCGMouseEventDeltaY, deltaY); CFRelease(get); } #endif /* Move the mouse to a specific point. */ void moveMouse(MMPointInt32 point){ #if defined(IS_MACOSX) CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); CGEventRef move = CGEventCreateMouseEvent(source, kCGEventMouseMoved, CGPointFromMMPointInt32(point), kCGMouseButtonLeft); calculateDeltas(&move, point); CGEventPost(kCGHIDEventTap, move); CFRelease(move); CFRelease(source); #elif defined(USE_X11) Display *display = XGetMainDisplay(); XWarpPointer(display, None, DefaultRootWindow(display), 0, 0, 0, 0, point.x, point.y); XSync(display, false); #elif defined(IS_WINDOWS) SetCursorPos(point.x, point.y); #endif } void dragMouse(MMPointInt32 point, const MMMouseButton button){ #if defined(IS_MACOSX) const CGEventType dragType = MMMouseDragToCGEventType(button); CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); CGEventRef drag = CGEventCreateMouseEvent(source, dragType, CGPointFromMMPointInt32(point), (CGMouseButton)button); calculateDeltas(&drag, point); CGEventPost(kCGHIDEventTap, drag); CFRelease(drag); CFRelease(source); #else moveMouse(point); #endif } MMPointInt32 location() { #if defined(IS_MACOSX) CGEventRef event = CGEventCreate(NULL); CGPoint point = CGEventGetLocation(event); CFRelease(event); return MMPointInt32FromCGPoint(point); #elif defined(USE_X11) int x, y; /* This is all we care about. Seriously. */ Window garb1, garb2; /* Why you can't specify NULL as a parameter */ int garb_x, garb_y; /* is beyond me. */ unsigned int more_garbage; Display *display = XGetMainDisplay(); XQueryPointer(display, XDefaultRootWindow(display), &garb1, &garb2, &x, &y, &garb_x, &garb_y, &more_garbage); return MMPointInt32Make(x, y); #elif defined(IS_WINDOWS) POINT point; GetCursorPos(&point); return MMPointInt32FromPOINT(point); #endif } /* Press down a button, or release it. */ int toggleMouse(bool down, MMMouseButton button) { #if defined(IS_MACOSX) const CGPoint currentPos = CGPointFromMMPointInt32(location()); const CGEventType mouseType = MMMouseToCGEventType(down, button); CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); CGEventRef event = CGEventCreateMouseEvent(source, mouseType, currentPos, (CGMouseButton)button); if (event == NULL) { CFRelease(source); return (int)kCGErrorCannotComplete; } CGEventPost(kCGHIDEventTap, event); CFRelease(event); CFRelease(source); return 0; #elif defined(USE_X11) Display *display = XGetMainDisplay(); Status status = XTestFakeButtonEvent(display, button, down ? True : False, CurrentTime); XSync(display, false); return status ? 0 : 1; #elif defined(IS_WINDOWS) INPUT mouseInput; mouseInput.type = INPUT_MOUSE; mouseInput.mi.dx = 0; mouseInput.mi.dy = 0; mouseInput.mi.dwFlags = MMMouseToMEventF(down, button); mouseInput.mi.time = 0; mouseInput.mi.dwExtraInfo = 0; mouseInput.mi.mouseData = 0; UINT sent = SendInput(1, &mouseInput, sizeof(mouseInput)); return sent == 1 ? 0 : (int)GetLastError(); #endif } int clickMouse(MMMouseButton button){ int err = toggleMouse(true, button); if (err != 0) { return err; } microsleep(5.0); return toggleMouse(false, button); } /* Special function for sending double clicks, needed for MacOS. */ int doubleClick(MMMouseButton button, int count){ #if defined(IS_MACOSX) /* Double click for Mac. */ const CGPoint currentPos = CGPointFromMMPointInt32(location()); const CGEventType mouseTypeDown = MMMouseToCGEventType(true, button); const CGEventType mouseTypeUP = MMMouseToCGEventType(false, button); CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); CGEventRef event = CGEventCreateMouseEvent(source, mouseTypeDown, currentPos, kCGMouseButtonLeft); if (event == NULL) { CFRelease(source); return (int)kCGErrorCannotComplete; } /* Set event to double click. */ CGEventSetIntegerValueField(event, kCGMouseEventClickState, count); CGEventPost(kCGHIDEventTap, event); CGEventSetType(event, mouseTypeUP); CGEventPost(kCGHIDEventTap, event); CFRelease(event); CFRelease(source); return 0; #else /* Double click for everything else. */ int err = clickMouse(button); if (err != 0) { return err; } microsleep(200); return clickMouse(button); #endif } /* Function used to scroll the screen in the required direction. */ void scrollMouseXY(int x, int y) { #if defined(IS_WINDOWS) // Fix for #97, C89 needs variables declared on top of functions (mouseScrollInput) INPUT mouseScrollInputH; INPUT mouseScrollInputV; #endif #if defined(IS_MACOSX) CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); CGEventRef event = CGEventCreateScrollWheelEvent(source, kCGScrollEventUnitPixel, 2, y, x); CGEventPost(kCGHIDEventTap, event); CFRelease(event); CFRelease(source); #elif defined(USE_X11) int ydir = 4; /* Button 4 is up, 5 is down. */ int xdir = 6; Display *display = XGetMainDisplay(); if (y < 0) { ydir = 5; } if (x < 0) { xdir = 7; } int xi; int yi; for (xi = 0; xi < abs(x); xi++) { XTestFakeButtonEvent(display, xdir, 1, CurrentTime); XTestFakeButtonEvent(display, xdir, 0, CurrentTime); } for (yi = 0; yi < abs(y); yi++) { XTestFakeButtonEvent(display, ydir, 1, CurrentTime); XTestFakeButtonEvent(display, ydir, 0, CurrentTime); } XSync(display, false); #elif defined(IS_WINDOWS) mouseScrollInputH.type = INPUT_MOUSE; mouseScrollInputH.mi.dx = 0; mouseScrollInputH.mi.dy = 0; mouseScrollInputH.mi.dwFlags = MOUSEEVENTF_WHEEL; mouseScrollInputH.mi.time = 0; mouseScrollInputH.mi.dwExtraInfo = 0; mouseScrollInputH.mi.mouseData = WHEEL_DELTA * x; mouseScrollInputV.type = INPUT_MOUSE; mouseScrollInputV.mi.dx = 0; mouseScrollInputV.mi.dy = 0; mouseScrollInputV.mi.dwFlags = MOUSEEVENTF_WHEEL; mouseScrollInputV.mi.time = 0; mouseScrollInputV.mi.dwExtraInfo = 0; mouseScrollInputV.mi.mouseData = WHEEL_DELTA * y; SendInput(1, &mouseScrollInputH, sizeof(mouseScrollInputH)); SendInput(1, &mouseScrollInputV, sizeof(mouseScrollInputV)); #endif } /* A crude, fast hypot() approximation to get around the fact that hypot() is not a standard ANSI C function. */ #if !defined(M_SQRT2) #define M_SQRT2 1.4142135623730950488016887 /* Fix for MSVC. */ #endif static double crude_hypot(double x, double y){ double big = fabs(x); /* max(|x|, |y|) */ double small = fabs(y); /* min(|x|, |y|) */ if (big > small) { double temp = big; big = small; small = temp; } return ((M_SQRT2 - 1.0) * small) + big; } static bool smoothlyMoveMouseImpl(MMPointInt32 endPoint, double lowSpeed, double highSpeed, bool drag, MMMouseButton button){ MMPointInt32 pos = location(); // MMSizeInt32 screenSize = getMainDisplaySize(); double velo_x = 0.0, velo_y = 0.0; double distance; while ((distance =crude_hypot((double)pos.x - endPoint.x, (double)pos.y - endPoint.y)) > 1.0) { double gravity = DEADBEEF_UNIFORM(5.0, 500.0); // double gravity = DEADBEEF_UNIFORM(lowSpeed, highSpeed); double veloDistance; velo_x += (gravity * ((double)endPoint.x - pos.x)) / distance; velo_y += (gravity * ((double)endPoint.y - pos.y)) / distance; /* Normalize velocity to get a unit vector of length 1. */ veloDistance = crude_hypot(velo_x, velo_y); velo_x /= veloDistance; velo_y /= veloDistance; pos.x += floor(velo_x + 0.5); pos.y += floor(velo_y + 0.5); /* Make sure we are in the screen boundaries! */ // if (pos.x >= screenSize.w || pos.y >= screenSize.h) { // return false; // } if (drag) { dragMouse(pos, button); } else { moveMouse(pos); } /* Wait 1 - 3 milliseconds. */ microsleep(DEADBEEF_UNIFORM(lowSpeed, highSpeed)); // microsleep(DEADBEEF_UNIFORM(1.0, 3.0)); } return true; } bool smoothlyMoveMouse(MMPointInt32 endPoint, double lowSpeed, double highSpeed){ return smoothlyMoveMouseImpl(endPoint, lowSpeed, highSpeed, false, LEFT_BUTTON); } bool smoothlyDragMouse(MMPointInt32 endPoint, double lowSpeed, double highSpeed, MMMouseButton button){ return smoothlyMoveMouseImpl(endPoint, lowSpeed, highSpeed, true, button); } ================================================ FILE: mouse/mouse_darwin.go ================================================ //go:build darwin // +build darwin package mouse ================================================ FILE: mouse/mouse_windows.go ================================================ //go:build windows // +build windows package mouse ================================================ FILE: mouse/mouse_x11.go ================================================ //go:build !darwin && !windows // +build !darwin,!windows package mouse ================================================ FILE: ps.go ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. package robotgo import ps "github.com/vcaesar/gops" // Nps process struct type Nps struct { Pid int Name string } // Pids get the all process id func Pids() ([]int, error) { return ps.Pids() } // PidExists determine whether the process exists func PidExists(pid int) (bool, error) { return ps.PidExists(pid) } // Process get the all process struct func Process() ([]Nps, error) { var npsArr []Nps nps, err := ps.Process() for i := 0; i < len(nps); i++ { np := Nps{ nps[i].Pid, nps[i].Name, } npsArr = append(npsArr, np) } return npsArr, err } // FindName find the process name by the process id func FindName(pid int) (string, error) { return ps.FindName(pid) } // FindNames find the all process name func FindNames() ([]string, error) { return ps.FindNames() } // FindIds finds the all processes named with a subset // of "name" (case insensitive), // return matched IDs. func FindIds(name string) ([]int, error) { return ps.FindIds(name) } // FindPath find the process path by the process pid func FindPath(pid int) (string, error) { return ps.FindPath(pid) } // Run run a cmd shell func Run(path string) ([]byte, error) { return ps.Run(path) } // Kill kill the process by PID func Kill(pid int) error { return ps.Kill(pid) } ================================================ FILE: robot_info_test.go ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. package robotgo_test import ( "fmt" "log" "runtime" "testing" "github.com/go-vgo/robotgo" "github.com/vcaesar/tt" ) func TestGetVer(t *testing.T) { fmt.Println("go version: ", runtime.Version()) ver := robotgo.GetVersion() tt.Expect(t, robotgo.Version, ver) } func TestGetScreenSize(t *testing.T) { x, y := robotgo.GetScreenSize() log.Println("Get screen size: ", x, y) rect := robotgo.GetScreenRect() fmt.Println("Get screen rect: ", rect) x, y = robotgo.Location() fmt.Println("Get location: ", x, y) } func TestGetSysScale(t *testing.T) { s := robotgo.SysScale() log.Println("SysScale: ", s) f := robotgo.ScaleF() log.Println("scale: ", f) } func TestGetTitle(t *testing.T) { // just exercise the function, it used to crash with a segfault + "Maximum // number of clients reached" for i := 0; i < 128; i++ { robotgo.GetTitle() } } ================================================ FILE: robotgo.go ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. /* Package robotgo Go native cross-platform system automation. Please make sure Golang, GCC is installed correctly before installing RobotGo; See Requirements: https://github.com/go-vgo/robotgo#requirements Installation: With Go module support (Go 1.11+), just import: import "github.com/go-vgo/robotgo" Otherwise, to install the robotgo package, run the command: go get -u github.com/go-vgo/robotgo */ package robotgo /* #cgo darwin CFLAGS: -x objective-c -Wno-deprecated-declarations #cgo darwin LDFLAGS: -framework Cocoa -framework CoreFoundation -framework IOKit #cgo darwin LDFLAGS: -framework Carbon -framework OpenGL // #if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 140400 #cgo darwin LDFLAGS: -framework ScreenCaptureKit #endif #cgo linux CFLAGS: -I/usr/src #cgo linux LDFLAGS: -L/usr/src -lm -lX11 -lXtst #cgo windows LDFLAGS: -lgdi32 -luser32 // #include "screen/goScreen.h" #include "mouse/mouse_c.h" #include "window/goWindow.h" */ import "C" import ( "errors" "fmt" "image" "runtime" "syscall" "time" "unsafe" "github.com/vcaesar/tt" ) const ( // Version get the robotgo version Version = "v1.00.0.1189, MT. Baker!" ) // GetVersion get the robotgo version func GetVersion() string { return Version } var ( // MouseSleep set the mouse default millisecond sleep time MouseSleep = 0 // KeySleep set the key default millisecond sleep time KeySleep = 10 // DisplayID set the screen display id DisplayID = -1 // NotPid used the hwnd not pid in windows NotPid bool // Scale option the os screen scale Scale bool ) type ( // Map a map[string]interface{} Map map[string]interface{} // CHex define CHex as c rgb Hex type (C.MMRGBHex) CHex C.MMRGBHex // CBitmap define CBitmap as C.MMBitmapRef type CBitmap C.MMBitmapRef // Handle define window Handle as C.MData type Handle C.MData ) // Bitmap define the go Bitmap struct // // The common type conversion of bitmap: // // https://github.com/go-vgo/robotgo/blob/master/docs/keys.md#type-conversion type Bitmap struct { ImgBuf *uint8 Width, Height int Bytewidth int BitsPixel uint8 BytesPerPixel uint8 } // Point is point struct type Point struct { X int Y int } // Size is size structure type Size struct { W, H int } // Rect is rect structure type Rect struct { Point Size } // Try handler(err) func Try(fun func(), handler func(interface{})) { defer func() { if err := recover(); err != nil { handler(err) } }() fun() } // MilliSleep sleep tm milli second func MilliSleep(tm int) { time.Sleep(time.Duration(tm) * time.Millisecond) } // Sleep time.Sleep tm second func Sleep(tm int) { time.Sleep(time.Duration(tm) * time.Second) } // Deprecated: use the MilliSleep(), // // MicroSleep time C.microsleep(tm) func MicroSleep(tm float64) { C.microsleep(C.double(tm)) } // GoString trans C.char to string func GoString(char *C.char) string { return C.GoString(char) } /* _______. ______ .______ _______ _______ .__ __. / | / || _ \ | ____|| ____|| \ | | | (----`| ,----'| |_) | | |__ | |__ | \| | \ \ | | | / | __| | __| | . ` | .----) | | `----.| |\ \----.| |____ | |____ | |\ | |_______/ \______|| _| `._____||_______||_______||__| \__| */ // ToMMRGBHex trans CHex to C.MMRGBHex func ToMMRGBHex(hex CHex) C.MMRGBHex { return C.MMRGBHex(hex) } // UintToHex trans uint32 to robotgo.CHex func UintToHex(u uint32) CHex { hex := U32ToHex(C.uint32_t(u)) return CHex(hex) } // U32ToHex trans C.uint32_t to C.MMRGBHex func U32ToHex(hex C.uint32_t) C.MMRGBHex { return C.MMRGBHex(hex) } // U8ToHex trans *C.uint8_t to C.MMRGBHex func U8ToHex(hex *C.uint8_t) C.MMRGBHex { return C.MMRGBHex(*hex) } // PadHex trans C.MMRGBHex to string func PadHex(hex C.MMRGBHex) string { color := C.pad_hex(hex) gcolor := C.GoString(color) C.free(unsafe.Pointer(color)) return gcolor } // PadHexs trans CHex to string func PadHexs(hex CHex) string { return PadHex(C.MMRGBHex(hex)) } // HexToRgb trans hex to rgb func HexToRgb(hex uint32) *C.uint8_t { return C.color_hex_to_rgb(C.uint32_t(hex)) } // RgbToHex trans rgb to hex func RgbToHex(r, g, b uint8) C.uint32_t { return C.color_rgb_to_hex(C.uint8_t(r), C.uint8_t(g), C.uint8_t(b)) } // GetPxColor get the pixel color return C.MMRGBHex func GetPxColor(x, y int, displayId ...int) C.MMRGBHex { cx := C.int32_t(x) cy := C.int32_t(y) display := displayIdx(displayId...) color := C.get_px_color(cx, cy, C.int32_t(display)) return color } // GetPixelColor get the pixel color return string func GetPixelColor(x, y int, displayId ...int) string { return PadHex(GetPxColor(x, y, displayId...)) } // GetLocationColor get the location pos's color func GetLocationColor(displayId ...int) string { x, y := Location() return GetPixelColor(x, y, displayId...) } // IsMain is main display func IsMain(displayId int) bool { return displayId == GetMainId() } func displayIdx(id ...int) int { display := -1 if DisplayID != -1 { display = DisplayID } if len(id) > 0 { display = id[0] } return display } func getNumDisplays() int { return int(C.get_num_displays()) } // GetHWNDByPid get the hwnd by pid func GetHWNDByPid(pid int) int { return int(C.get_hwnd_by_pid(C.uintptr(pid))) } // SysScale get the sys scale func SysScale(displayId ...int) float64 { display := displayIdx(displayId...) s := C.sys_scale(C.int32_t(display)) return float64(s) } // Scaled get the screen scaled return scale size func Scaled(x int, displayId ...int) int { f := ScaleF(displayId...) return Scaled0(x, f) } // Scaled0 return int(x * f) func Scaled0(x int, f float64) int { return int(float64(x) * f) } // Scaled1 return int(x / f) func Scaled1(x int, f float64) int { return int(float64(x) / f) } // GetScreenSize get the screen size func GetScreenSize() (int, int) { size := C.getMainDisplaySize() return int(size.w), int(size.h) } // GetScreenRect get the screen rect (x, y, w, h) func GetScreenRect(displayId ...int) Rect { display := -1 if len(displayId) > 0 { display = displayId[0] } rect := C.getScreenRect(C.int32_t(display)) x, y, w, h := int(rect.origin.x), int(rect.origin.y), int(rect.size.w), int(rect.size.h) if runtime.GOOS == "windows" { // f := ScaleF(displayId...) f := ScaleF() x, y, w, h = Scaled0(x, f), Scaled0(y, f), Scaled0(w, f), Scaled0(h, f) } return Rect{ Point{X: x, Y: y}, Size{W: w, H: h}, } } // GetScaleSize get the screen scale size func GetScaleSize(displayId ...int) (int, int) { x, y := GetScreenSize() f := ScaleF(displayId...) return int(float64(x) * f), int(float64(y) * f) } // CaptureScreen capture the screen return bitmap(c struct), // use `defer robotgo.FreeBitmap(bitmap)` to free the bitmap // // robotgo.CaptureScreen(x, y, w, h int) func CaptureScreen(args ...int) CBitmap { var x, y, w, h C.int32_t displayId := -1 if DisplayID != -1 { displayId = DisplayID } if len(args) > 4 { displayId = args[4] } if len(args) > 3 { x = C.int32_t(args[0]) y = C.int32_t(args[1]) w = C.int32_t(args[2]) h = C.int32_t(args[3]) } else { // Get the main screen rect. rect := GetScreenRect(displayId) if runtime.GOOS == "windows" { x = C.int32_t(rect.X) y = C.int32_t(rect.Y) } w = C.int32_t(rect.W) h = C.int32_t(rect.H) } isPid := 0 if NotPid || len(args) > 5 { isPid = 1 } bit := C.capture_screen(x, y, w, h, C.int32_t(displayId), C.int8_t(isPid)) return CBitmap(bit) } // CaptureGo capture the screen and return bitmap(go struct) func CaptureGo(args ...int) Bitmap { bit := CaptureScreen(args...) defer FreeBitmap(bit) return ToBitmap(bit) } // CaptureImg capture the screen and return image.Image, error func CaptureImg(args ...int) (image.Image, error) { bit := CaptureScreen(args...) if unsafe.Pointer(bit) == nil { return nil, errors.New("Capture image not found.") } defer FreeBitmap(bit) return ToImage(bit), nil } // FreeBitmap free and dealloc the C bitmap func FreeBitmap(bitmap CBitmap) { // C.destroyMMBitmap(bitmap) C.bitmap_dealloc(C.MMBitmapRef(bitmap)) } // FreeBitmapArr free and dealloc the C bitmap array func FreeBitmapArr(bit ...CBitmap) { for i := 0; i < len(bit); i++ { FreeBitmap(bit[i]) } } // ToMMBitmapRef trans CBitmap to C.MMBitmapRef func ToMMBitmapRef(bit CBitmap) C.MMBitmapRef { return C.MMBitmapRef(bit) } // ToBitmap trans C.MMBitmapRef to Bitmap func ToBitmap(bit CBitmap) Bitmap { bitmap := Bitmap{ ImgBuf: (*uint8)(bit.imageBuffer), Width: int(bit.width), Height: int(bit.height), Bytewidth: int(bit.bytewidth), BitsPixel: uint8(bit.bitsPerPixel), BytesPerPixel: uint8(bit.bytesPerPixel), } return bitmap } // ToCBitmap trans Bitmap to C.MMBitmapRef func ToCBitmap(bit Bitmap) CBitmap { cbitmap := C.createMMBitmap_c( (*C.uint8_t)(bit.ImgBuf), C.int32_t(bit.Width), C.int32_t(bit.Height), C.int32_t(bit.Bytewidth), C.uint8_t(bit.BitsPixel), C.uint8_t(bit.BytesPerPixel), ) return CBitmap(cbitmap) } // ToImage convert C.MMBitmapRef to standard image.Image func ToImage(bit CBitmap) image.Image { return ToRGBA(bit) } // ToRGBA convert C.MMBitmapRef to standard image.RGBA func ToRGBA(bit CBitmap) *image.RGBA { bmp1 := ToBitmap(bit) return ToRGBAGo(bmp1) } // ImgToCBitmap trans image.Image to CBitmap func ImgToCBitmap(img image.Image) CBitmap { return ToCBitmap(ImgToBitmap(img)) } // ByteToCBitmap trans []byte to CBitmap func ByteToCBitmap(by []byte) CBitmap { img, _ := ByteToImg(by) return ImgToCBitmap(img) } // SetXDisplayName set XDisplay name (Linux) func SetXDisplayName(name string) error { cname := C.CString(name) str := C.set_XDisplay_name(cname) C.free(unsafe.Pointer(cname)) return toErr(str) } // GetXDisplayName get XDisplay name (Linux) func GetXDisplayName() string { name := C.get_XDisplay_name() gname := C.GoString(name) C.free(unsafe.Pointer(name)) return gname } // CloseMainDisplay close the main X11 display func CloseMainDisplay() { C.close_main_display() } // Deprecated: use the ScaledF(), // // ScaleX get the primary display horizontal DPI scale factor, drop func ScaleX() int { return int(C.scaleX()) } /* .___ ___. ______ __ __ _______. _______ | \/ | / __ \ | | | | / || ____| | \ / | | | | | | | | | | (----`| |__ | |\/| | | | | | | | | | \ \ | __| | | | | | `--' | | `--' | .----) | | |____ |__| |__| \______/ \______/ |_______/ |_______| */ // CheckMouse check the mouse button func CheckMouse(btn string) C.MMMouseButton { // button = args[0].(C.MMMouseButton) m1 := map[string]C.MMMouseButton{ "left": C.LEFT_BUTTON, "center": C.CENTER_BUTTON, "right": C.RIGHT_BUTTON, "wheelDown": C.WheelDown, "wheelUp": C.WheelUp, "wheelLeft": C.WheelLeft, "wheelRight": C.WheelRight, } if v, ok := m1[btn]; ok { return v } return C.LEFT_BUTTON } // MouseButtonString converts a C.MMMouseButton to a readable name. func MouseButtonString(btn C.MMMouseButton) string { m1 := map[C.MMMouseButton]string{ C.LEFT_BUTTON: "left", C.CENTER_BUTTON: "center", C.RIGHT_BUTTON: "right", C.WheelDown: "wheelDown", C.WheelUp: "wheelUp", C.WheelLeft: "wheelLeft", C.WheelRight: "wheelRight", } if v, ok := m1[btn]; ok { return v } return fmt.Sprintf("button%d", btn) } // MoveScale calculate the os scale factor x, y func MoveScale(x, y int, displayId ...int) (int, int) { if Scale || runtime.GOOS == "windows" { f := ScaleF() x, y = Scaled1(x, f), Scaled1(y, f) } return x, y } // Move move the mouse to (x, y) // // Examples: // // robotgo.MouseSleep = 100 // 100 millisecond // robotgo.Move(10, 10) func Move(x, y int, displayId ...int) { x, y = MoveScale(x, y, displayId...) cx := C.int32_t(x) cy := C.int32_t(y) C.moveMouse(C.MMPointInt32Make(cx, cy)) MilliSleep(MouseSleep) } // Deprecated: use the DragSmooth(), // // Drag drag the mouse to (x, y), // It's not valid now, use the DragSmooth() func Drag(x, y int, args ...string) { x, y = MoveScale(x, y) var button C.MMMouseButton = C.LEFT_BUTTON cx := C.int32_t(x) cy := C.int32_t(y) if len(args) > 0 { button = CheckMouse(args[0]) } C.dragMouse(C.MMPointInt32Make(cx, cy), button) MilliSleep(MouseSleep) } // DragSmooth drag the mouse like smooth to (x, y) // // Examples: // // robotgo.DragSmooth(10, 10) func DragSmooth(x, y int, args ...interface{}) { Toggle("left") MilliSleep(50) smoothMove(x, y, true, args...) Toggle("left", "up") } func smoothMove(x, y int, drag bool, args ...interface{}) bool { x, y = MoveScale(x, y) cx := C.int32_t(x) cy := C.int32_t(y) var ( mouseDelay = 1 low C.double high C.double ) if len(args) > 2 { mouseDelay = args[2].(int) } if len(args) > 1 { low = C.double(args[0].(float64)) high = C.double(args[1].(float64)) } else { low = 1.0 high = 3.0 } var cbool C.bool if drag { cbool = C.smoothlyDragMouse(C.MMPointInt32Make(cx, cy), low, high, C.LEFT_BUTTON) } else { cbool = C.smoothlyMoveMouse(C.MMPointInt32Make(cx, cy), low, high) } MilliSleep(MouseSleep + mouseDelay) return bool(cbool) } // MoveSmooth move the mouse smooth, // moves mouse to x, y human like, with the mouse button up. // // robotgo.MoveSmooth(x, y int, low, high float64, mouseDelay int) // // Examples: // // robotgo.MoveSmooth(10, 10) // robotgo.MoveSmooth(10, 10, 1.0, 2.0) func MoveSmooth(x, y int, args ...interface{}) bool { return smoothMove(x, y, false, args...) } // MoveArgs get the mouse relative args func MoveArgs(x, y int) (int, int) { mx, my := Location() mx = mx + x my = my + y return mx, my } // MoveRelative move mouse with relative func MoveRelative(x, y int) { Move(MoveArgs(x, y)) } // MoveSmoothRelative move mouse smooth with relative func MoveSmoothRelative(x, y int, args ...interface{}) { mx, my := MoveArgs(x, y) MoveSmooth(mx, my, args...) } // Location get the mouse location position return x, y func Location() (int, int) { pos := C.location() x := int(pos.x) y := int(pos.y) if Scale || runtime.GOOS == "windows" { f := ScaleF() x, y = Scaled0(x, f), Scaled0(y, f) } return x, y } // ClickV1 click the mouse button // // robotgo.Click(button string, double bool) // // Examples: // // robotgo.Click() // default is left button // robotgo.Click("right") // robotgo.Click("wheelLeft") func ClickV1(args ...interface{}) { var ( button C.MMMouseButton = C.LEFT_BUTTON double bool ) if len(args) > 0 { button = CheckMouse(args[0].(string)) } if len(args) > 1 { double = args[1].(bool) } if !double { C.clickMouse(button) } else { C.doubleClick(button, 2) } MilliSleep(MouseSleep) } // Click click the mouse button and return error // // robotgo.Click(button string, double bool) // // Examples: // // err := robotgo.Click() // default is left button // err := robotgo.Click("right") func Click(args ...interface{}) error { var ( button C.MMMouseButton = C.LEFT_BUTTON double bool count int ) if len(args) > 0 { btn, ok := args[0].(string) if !ok { return errors.New("first argument must be a button string") } button = CheckMouse(btn) } if len(args) > 1 { dbl, ok := args[1].(bool) if !ok { return errors.New("second argument must be a bool indicating double click") } double = dbl } if len(args) > 2 { count = args[2].(int) } defer MilliSleep(MouseSleep) if !double { if code := C.toggleMouse(true, button); code != 0 { return formatClickError(int(code), button, "down", count) } MilliSleep(5) code := C.toggleMouse(false, button) return formatClickError(int(code), button, "up", count) } code := C.doubleClick(button, 2) return formatClickError(int(code), button, "double", 2) } // MultiClick performs multiple clicks and returns error // // robotgo.MultiClick(button string, count int) func MultiClick(button string, count int, click ...bool) error { if count < 1 { return nil } defer MilliSleep(MouseSleep) if runtime.GOOS == "darwin" && len(click) <= 0 { btn := CheckMouse(button) code := C.doubleClick(btn, C.int(count)) return formatClickError(int(code), btn, "down", count) } for i := 0; i < count; i++ { if err := Click(button, false, i+1); err != nil { return err } } return nil } func formatClickError(code int, button C.MMMouseButton, stage string, count int) error { if code == 0 { return nil } btnName := MouseButtonString(button) detail := "" switch runtime.GOOS { case "windows": if code != 0 { detail = syscall.Errno(code).Error() } case "darwin": cgErrors := map[int]string{ 0: "kCGErrorSuccess", 1000: "kCGErrorFailure", 1001: "kCGErrorIllegalArgument", 1002: "kCGErrorInvalidConnection", 1003: "kCGErrorInvalidContext", 1004: "kCGErrorCannotComplete", 1005: "kCGErrorNotImplemented", 1006: "kCGErrorRangeCheck", 1007: "kCGErrorTypeCheck", 1008: "kCGErrorNoCurrentPoint", 1010: "kCGErrorInvalidOperation", } if v, ok := cgErrors[code]; ok { detail = v } default: if code == 1 { detail = "XTestFakeButtonEvent returned false" } } if detail != "" { return fmt.Errorf("click %s failed (%s, count=%d): %s (code=%d)", stage, btnName, count, detail, code) } return fmt.Errorf("click %s failed (%s, count=%d), code=%d", stage, btnName, count, code) } // MoveClick move and click the mouse // // robotgo.MoveClick(x, y int, button string, double bool) // // Examples: // // robotgo.MouseSleep = 100 // robotgo.MoveClick(10, 10) func MoveClick(x, y int, args ...interface{}) { Move(x, y) MilliSleep(50) Click(args...) } // MovesClick move smooth and click the mouse // // use the `robotgo.MouseSleep = 100` func MovesClick(x, y int, args ...interface{}) { MoveSmooth(x, y) MilliSleep(50) Click(args...) } // Toggle toggle the mouse, support button: // // "left", "center", "right", // "wheelDown", "wheelUp", "wheelLeft", "wheelRight" // // Examples: // // robotgo.Toggle("left") // default is down // robotgo.Toggle("left", "up") func Toggle(key ...interface{}) error { var button C.MMMouseButton = C.LEFT_BUTTON if len(key) > 0 { button = CheckMouse(key[0].(string)) } down := true if len(key) > 1 && key[1].(string) == "up" { down = false } code := C.toggleMouse(C.bool(down), button) if len(key) > 2 { MilliSleep(MouseSleep) } return formatClickError(int(code), button, "down", 1) } // MouseDown send mouse down event func MouseDown(key ...interface{}) error { return Toggle(key...) } // MouseUp send mouse up event func MouseUp(key ...interface{}) error { if len(key) <= 0 { key = append(key, "left") } return Toggle(append(key, "up")...) } // Scroll scroll the mouse to (x, y) // // robotgo.Scroll(x, y, msDelay int) // // Examples: // // robotgo.Scroll(10, 10) func Scroll(x, y int, args ...int) { var msDelay = 10 if len(args) > 0 { msDelay = args[0] } cx := C.int(x) cy := C.int(y) C.scrollMouseXY(cx, cy) MilliSleep(MouseSleep + msDelay) } // ScrollDir scroll the mouse with direction to (x, "up") // supported: "up", "down", "left", "right" // // Examples: // // robotgo.ScrollDir(10, "down") // robotgo.ScrollDir(10, "up") func ScrollDir(x int, direction ...interface{}) { d := "down" if len(direction) > 0 { d = direction[0].(string) } if d == "down" { Scroll(0, -x) } if d == "up" { Scroll(0, x) } if d == "left" { Scroll(x, 0) } if d == "right" { Scroll(-x, 0) } // MilliSleep(MouseSleep) } // ScrollSmooth scroll the mouse smooth, // default scroll 5 times and sleep 100 millisecond // // robotgo.ScrollSmooth(toy, num, sleep, tox) // // Examples: // // robotgo.ScrollSmooth(-10) // robotgo.ScrollSmooth(-10, 6, 200, -10) func ScrollSmooth(to int, args ...int) { i := 0 num := 5 if len(args) > 0 { num = args[0] } tm := 100 if len(args) > 1 { tm = args[1] } tox := 0 if len(args) > 2 { tox = args[2] } for { Scroll(tox, to) MilliSleep(tm) i++ if i == num { break } } MilliSleep(MouseSleep) } // ScrollRelative scroll mouse with relative // // Examples: // // robotgo.ScrollRelative(10, 10) func ScrollRelative(x, y int, args ...int) { mx, my := MoveArgs(x, y) Scroll(mx, my, args...) } /* ____ __ ____ __ .__ __. _______ ______ ____ __ ____ \ \ / \ / / | | | \ | | | \ / __ \ \ \ / \ / / \ \/ \/ / | | | \| | | .--. | | | | \ \/ \/ / \ / | | | . ` | | | | | | | | \ / \ /\ / | | | |\ | | '--' | `--' | \ /\ / \__/ \__/ |__| |__| \__| |_______/ \______/ \__/ \__/ */ func alertArgs(args ...string) (string, string) { var ( defaultBtn = "Ok" cancelBtn = "Cancel" ) if len(args) > 0 { defaultBtn = args[0] } if len(args) > 1 { cancelBtn = args[1] } return defaultBtn, cancelBtn } func showAlert(title, msg string, args ...string) bool { defaultBtn, cancelBtn := alertArgs(args...) cTitle := C.CString(title) cMsg := C.CString(msg) defaultButton := C.CString(defaultBtn) cancelButton := C.CString(cancelBtn) cbool := C.showAlert(cTitle, cMsg, defaultButton, cancelButton) ibool := int(cbool) C.free(unsafe.Pointer(cTitle)) C.free(unsafe.Pointer(cMsg)) C.free(unsafe.Pointer(defaultButton)) C.free(unsafe.Pointer(cancelButton)) return ibool == 0 } // IsValid valid the window func IsValid() bool { abool := C.is_valid() gbool := bool(abool) return gbool } // SetActive set the window active func SetActive(win Handle) { SetActiveC(C.MData(win)) } // SetActiveC set the window active func SetActiveC(win C.MData) { C.set_active(win) } // GetActive get the active window func GetActive() Handle { return Handle(GetActiveC()) } // GetActiveC get the active window func GetActiveC() C.MData { mdata := C.get_active() // fmt.Println("active----", mdata) return mdata } // MinWindow set the window min func MinWindow(pid int, args ...interface{}) { var ( state = true isPid int ) if len(args) > 0 { state = args[0].(bool) } if len(args) > 1 || NotPid { isPid = 1 } C.min_window(C.uintptr(pid), C.bool(state), C.int8_t(isPid)) } // MaxWindow set the window max func MaxWindow(pid int, args ...interface{}) { var ( state = true isPid int ) if len(args) > 0 { state = args[0].(bool) } if len(args) > 1 || NotPid { isPid = 1 } C.max_window(C.uintptr(pid), C.bool(state), C.int8_t(isPid)) } // CloseWindow close the window func CloseWindow(args ...int) { if len(args) <= 0 { C.close_main_window() return } var pid, isPid int if len(args) > 0 { pid = args[0] } if len(args) > 1 || NotPid { isPid = 1 } C.close_window_by_PId(C.uintptr(pid), C.int8_t(isPid)) } // SetHandle set the window handle func SetHandle(hwnd int) { chwnd := C.uintptr(hwnd) C.setHandle(chwnd) } // SetHandlePid set the window handle by pid func SetHandlePid(pid int, args ...int) { var isPid int if len(args) > 0 || NotPid { isPid = 1 } C.set_handle_pid_mData(C.uintptr(pid), C.int8_t(isPid)) } // GetHandById get handle mdata by id func GetHandById(id int, args ...int) Handle { isPid := 1 if len(args) > 0 { isPid = args[0] } return GetHandByPid(id, isPid) } // GetHandByPid get handle mdata by pid func GetHandByPid(pid int, args ...int) Handle { return Handle(GetHandByPidC(pid, args...)) } // Deprecated: use the GetHandByPid(), // // GetHandPid get handle mdata by pid func GetHandPid(pid int, args ...int) Handle { return GetHandByPid(pid, args...) } // GetHandByPidC get handle mdata by pid func GetHandByPidC(pid int, args ...int) C.MData { var isPid int if len(args) > 0 || NotPid { isPid = 1 } return C.set_handle_pid(C.uintptr(pid), C.int8_t(isPid)) } // GetHandle get the window handle func GetHandle() int { hwnd := C.get_handle() ghwnd := int(hwnd) // fmt.Println("gethwnd---", ghwnd) return ghwnd } // Deprecated: use the GetHandle(), // // # GetBHandle get the window handle, Wno-deprecated // // This function will be removed in version v1.0.0 func GetBHandle() int { tt.Drop("GetBHandle", "GetHandle") hwnd := C.b_get_handle() ghwnd := int(hwnd) //fmt.Println("gethwnd---", ghwnd) return ghwnd } func cgetTitle(pid, isPid int) string { title := C.get_title_by_pid(C.uintptr(pid), C.int8_t(isPid)) gtitle := C.GoString(title) return gtitle } // GetTitle get the window title return string // // Examples: // // fmt.Println(robotgo.GetTitle()) // // ids, _ := robotgo.FindIds() // robotgo.GetTitle(ids[0]) func GetTitle(args ...int) string { if len(args) <= 0 { title := C.get_main_title() gtitle := C.GoString(title) return gtitle } if len(args) > 1 { return internalGetTitle(args[0], args[1]) } return internalGetTitle(args[0]) } // GetPid get the process id return int32 func GetPid() int { pid := C.get_PID() return int(pid) } // internalGetBounds get the window bounds func internalGetBounds(pid, isPid int) (int, int, int, int) { bounds := C.get_bounds(C.uintptr(pid), C.int8_t(isPid)) return int(bounds.X), int(bounds.Y), int(bounds.W), int(bounds.H) } // internalGetClient get the window client bounds func internalGetClient(pid, isPid int) (int, int, int, int) { bounds := C.get_client(C.uintptr(pid), C.int8_t(isPid)) return int(bounds.X), int(bounds.Y), int(bounds.W), int(bounds.H) } // Is64Bit determine whether the sys is 64bit func Is64Bit() bool { b := C.Is64Bit() return bool(b) } func internalActive(pid, isPid int) { C.active_PID(C.uintptr(pid), C.int8_t(isPid)) } // ActivePid active the window by Pid, // If args[0] > 0 on the Windows platform via a window handle to active // func ActivePid(pid int32, args ...int) { // var isPid int // if len(args) > 0 { // isPid = args[0] // } // C.active_PID(C.uintptr(pid), C.uintptr(isPid)) // } // ActiveName active the window by name // // Examples: // // robotgo.ActiveName("chrome") func ActiveName(name string) error { pids, err := FindIds(name) if err == nil && len(pids) > 0 { return ActivePid(pids[0]) } return err } ================================================ FILE: robotgo_adb.go ================================================ package robotgo ================================================ FILE: robotgo_android.go ================================================ package robotgo ================================================ FILE: robotgo_fn_v1.go ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. package robotgo import "github.com/vcaesar/tt" // Deprecated: use the Move(), // // MoveMouse move the mouse func MoveMouse(x, y int) { Move(x, y) } // Deprecated: use the DragSmooth(), // // DragMouse drag the mouse to (x, y), // It's same with the DragSmooth() now func DragMouse(x, y int, args ...interface{}) { DragSmooth(x, y, args...) } // Deprecated: use the MoveSmooth(), // // MoveMouseSmooth move the mouse smooth, // moves mouse to x, y human like, with the mouse button up. func MoveMouseSmooth(x, y int, args ...interface{}) bool { return MoveSmooth(x, y, args...) } // Deprecated: use the function Location() // // GetMousePos get the mouse's position return x, y func GetMousePos() (int, int) { return Location() } // Deprecated: use the Click(), // // # MouseClick click the mouse // // robotgo.MouseClick(button string, double bool) func MouseClick(args ...interface{}) { Click(args...) } // Deprecated: use the TypeStr(), // // # TypeStringDelayed type string delayed, Wno-deprecated // // This function will be removed in version v1.0.0 func TypeStringDelayed(str string, delay int) { tt.Drop("TypeStringDelayed", "TypeStrDelay") TypeStrDelay(str, delay) } // Deprecated: use the ScaledF(), // // Scale1 get the screen scale (only windows old), drop func Scale1() int { dpi := map[int]int{ 0: 100, // DPI Scaling Level 96: 100, 120: 125, 144: 150, 168: 175, 192: 200, 216: 225, // Custom DPI 240: 250, 288: 300, 384: 400, 480: 500, } x := ScaleX() return dpi[x] } // Deprecated: use the ScaledF(), // // Scale0 return ScaleX() / 0.96, drop func Scale0() int { return int(float64(ScaleX()) / 0.96) } // Deprecated: use the ScaledF(), // // Mul mul the scale, drop func Mul(x int) int { s := Scale1() return x * s / 100 } ================================================ FILE: robotgo_mac.go ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. //go:build darwin // +build darwin package robotgo /* #include */ import "C" // GetMainId get the main display id func GetMainId() int { return int(C.CGMainDisplayID()) } ================================================ FILE: robotgo_mac_unix.go ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. //go:build !windows // +build !windows package robotgo // ScaleF get the system scale val func ScaleF(displayId ...int) float64 { f := SysScale(displayId...) if f == 0.0 { f = 1.0 } return f } ================================================ FILE: robotgo_mac_win.go ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. //go:build darwin || windows // +build darwin windows package robotgo // GetBounds get the window bounds func GetBounds(pid int, args ...int) (int, int, int, int) { var isPid int if len(args) > 0 || NotPid { isPid = 1 } return internalGetBounds(pid, isPid) } // GetClient get the window client bounds func GetClient(pid int, args ...int) (int, int, int, int) { var isPid int if len(args) > 0 || NotPid { isPid = 1 } return internalGetClient(pid, isPid) } // internalGetTitle get the window title func internalGetTitle(pid int, args ...int) string { var isPid int if len(args) > 0 || NotPid { isPid = 1 } gtitle := cgetTitle(pid, isPid) return gtitle } // ActivePid active the window by PID, // // If args[0] > 0 on the Windows platform via a window handle to active // // Examples: // // ids, _ := robotgo.FindIds() // robotgo.ActivePid(ids[0]) func ActivePid(pid int, args ...int) error { var isPid int if len(args) > 0 || NotPid { isPid = 1 } internalActive(pid, isPid) return nil } // DisplaysNum get the count of displays func DisplaysNum() int { return getNumDisplays() } // Alert show a alert window // Displays alert with the attributes. // If cancel button is not given, only the default button is displayed // // Examples: // // robotgo.Alert("hi", "window", "ok", "cancel") func Alert(title, msg string, args ...string) bool { return showAlert(title, msg, args...) } ================================================ FILE: robotgo_ocr.go ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. //go:build ocr // +build ocr package robotgo import ( "github.com/otiai10/gosseract/v2" ) // GetText get the image text by tesseract ocr func GetText(imgPath string, args ...string) (string, error) { var lang = "eng" if len(args) > 0 { lang = args[0] if lang == "zh" { lang = "chi_sim" } } client := gosseract.NewClient() defer client.Close() client.SetImage(imgPath) client.SetLanguage(lang) return client.Text() } ================================================ FILE: robotgo_test.go ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. //go:build darwin || windows // +build darwin windows package robotgo import ( "testing" "github.com/vcaesar/tt" ) func TestColor(t *testing.T) { s := GetPixelColor(10, 10) tt.IsType(t, "string", s) tt.NotEmpty(t, s) c := GetPxColor(10, 10) s1 := PadHex(c) tt.Equal(t, s, s1) } func TestSize(t *testing.T) { x, y := GetScreenSize() tt.NotZero(t, x) tt.NotZero(t, y) x, y = GetScaleSize() tt.NotZero(t, x) tt.NotZero(t, y) } func TestMoveMouse(t *testing.T) { Move(20, 20) MilliSleep(50) x, y := Location() tt.Equal(t, 20, x) tt.Equal(t, 20, y) } func TestMoveMouseSmooth(t *testing.T) { b := MoveSmooth(100, 100) MilliSleep(50) x, y := Location() tt.True(t, b) tt.Equal(t, 100, x) tt.Equal(t, 100, y) } func TestDragMouse(t *testing.T) { DragSmooth(500, 500) MilliSleep(50) x, y := Location() tt.Equal(t, 500, x) tt.Equal(t, 500, y) } func TestScrollMouse(t *testing.T) { ScrollDir(120, "up") ScrollDir(100, "right") Scroll(0, 120) MilliSleep(100) Scroll(210, 210) MilliSleep(10) } func TestMoveRelative(t *testing.T) { Move(200, 200) MilliSleep(50) MoveRelative(10, -10) MilliSleep(50) x, y := Location() tt.Equal(t, 210, x) tt.Equal(t, 190, y) } func TestMoveSmoothRelative(t *testing.T) { Move(200, 200) MilliSleep(50) MoveSmoothRelative(10, -10) MilliSleep(50) x, y := Location() tt.Equal(t, 210, x) tt.Equal(t, 190, y) } func TestMouseToggle(t *testing.T) { e := Toggle("right") tt.Nil(t, e) e = Toggle("right", "up") tt.Nil(t, e) e = MouseDown("left") tt.Nil(t, e) e = MouseUp("left") tt.Nil(t, e) } func TestKey(t *testing.T) { e := KeyTap("v", "cmd") tt.Nil(t, e) e = KeyTap("enter") tt.Nil(t, e) e = KeyToggle("v", "up") tt.Nil(t, e) e = KeyDown("a") tt.Nil(t, e) e = KeyUp("a") tt.Nil(t, e) e = KeyPress("b") tt.Nil(t, e) } func TestClip(t *testing.T) { err := WriteAll("s") tt.Nil(t, err) s, e := ReadAll() tt.Equal(t, "s", s) tt.Nil(t, e) } func TestTypeStr(t *testing.T) { c := CharCodeAt("s", 0) tt.Equal(t, 115, c) e := PasteStr("s") tt.Nil(t, e) s1 := "abc\\\\cd/s@世界" uc := ToUC(s1) tt.Equal(t, "[a b c \\ \\ c d / s @ U4e16 U754c]", uc) } func TestKeyCode(t *testing.T) { m := MouseMap["left"] tt.Equal(t, 1, m) k := Keycode["1"] tt.Equal(t, 2, k) s := Special["+"] tt.Equal(t, "=", s) tt.Equal(t, "0", Key0) tt.Equal(t, "a", KeyA) } func TestImage(t *testing.T) { bit := CaptureScreen() defer FreeBitmap(bit) tt.NotNil(t, bit) img := ToImage(bit) err := SavePng(img, "robot_test.png") tt.Nil(t, err) img1, err := CaptureImg(10, 10, 20, 20) tt.Nil(t, err) e := Save(img1, "robot_img.jpeg", 50) tt.Nil(t, e) tt.Equal(t, 20, Width(img1)) tt.Equal(t, 20, Height(img1)) bit1 := ImgToBitmap(img1) tt.Equal(t, bit1.Width, Width(img1)) tt.Equal(t, bit1.Height, Height(img1)) } func TestPs(t *testing.T) { id, err := Pids() tt.Not(t, "[]", id) tt.IsType(t, "[]int", id) tt.Nil(t, err) ps, e := Process() tt.Not(t, "[]", ps) tt.IsType(t, "[]robotgo.Nps", ps) tt.Nil(t, e) b, e := PidExists(id[0]) tt.Bool(t, b) tt.Nil(t, e) n, e := FindName(id[0]) tt.NotEmpty(t, n) tt.Nil(t, e) n1, e := FindNames() tt.Not(t, "[]", n1) tt.IsType(t, "[]string", n1) tt.Nil(t, e) id, err = FindIds(n1[0]) tt.Not(t, "[]", id) tt.IsType(t, "[]int", id) tt.Nil(t, err) if len(id) > 0 { e := KeyTap("v", id[0], "cmd") tt.Nil(t, e) } // n, e = FindPath(id[0]) // tt.NotEmpty(t, n) // tt.Nil(t, e) } // func TestAlert(t *testing.T) { // go func() { // MilliSleep(200) // KeyTap("enter") // log.Println("tap...") // }() // i := Alert("t", "msg") // tt.True(t, i) // } ================================================ FILE: robotgo_win.go ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. //go:build windows // +build windows package robotgo import ( "syscall" "unsafe" // "github.com/lxn/win" "github.com/tailscale/win" ) // FindWindow find window hwnd by name func FindWindow(name string) win.HWND { u1, _ := syscall.UTF16PtrFromString(name) hwnd := win.FindWindow(nil, u1) return hwnd } // GetHWND get foreground window hwnd func GetHWND() win.HWND { hwnd := win.GetForegroundWindow() return hwnd } // SendInput send n input event func SendInput(nInputs uint32, pInputs unsafe.Pointer, cbSize int32) uint32 { return win.SendInput(nInputs, pInputs, cbSize) } // SendMsg send a message with hwnd func SendMsg(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr { return win.SendMessage(hwnd, msg, wParam, lParam) } // SetActiveWindow set window active with hwnd func SetActiveWindow(hwnd win.HWND) win.HWND { return win.SetActiveWindow(hwnd) } // SetFocus set window focus with hwnd func SetFocus(hwnd win.HWND) win.HWND { return win.SetFocus(hwnd) } // SetForeg set the window into the foreground by hwnd func SetForeg(hwnd win.HWND) bool { return win.SetForegroundWindow(hwnd) } // GetMain get the main display hwnd func GetMain() win.HWND { return win.GetActiveWindow() } // GetMainId get the main display id func GetMainId() int { return int(GetMain()) } // ScaleF get the system scale value // if "displayId == -2" this function will get the desktop scale value func ScaleF(displayId ...int) (f float64) { if len(displayId) > 0 && displayId[0] != -1 { if displayId[0] >= 0 { dpi := GetDPI(win.HWND(displayId[0])) f = float64(dpi) / 96.0 } if displayId[0] == -2 { f = float64(GetDPI(GetDesktopWindow())) / 96.0 } } else { f = float64(GetMainDPI()) / 96.0 } if f == 0.0 { f = 1.0 } return f } // GetDesktopWindow get the desktop window hwnd id func GetDesktopWindow() win.HWND { return win.GetDesktopWindow() } // GetMainDPI get the display dpi func GetMainDPI() int { return int(GetDPI(GetHWND())) } // GetDPI get the window dpi func GetDPI(hwnd win.HWND) uint32 { return win.GetDpiForWindow(hwnd) } // GetSysDPI get the system metrics dpi func GetSysDPI(idx int32, dpi uint32) int32 { return win.GetSystemMetricsForDpi(idx, dpi) } ================================================ FILE: robotgo_x11.go ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. //go:build !darwin && !windows // +build !darwin,!windows package robotgo import ( "errors" "log" "github.com/robotn/xgb" "github.com/robotn/xgb/xinerama" "github.com/robotn/xgb/xproto" "github.com/robotn/xgbutil" "github.com/robotn/xgbutil/ewmh" ) var xu *xgbutil.XUtil // GetBounds get the window bounds func GetBounds(pid int, args ...int) (int, int, int, int) { var isPid int if len(args) > 0 || NotPid { isPid = 1 return internalGetBounds(pid, isPid) } xid, err := GetXid(xu, pid) if err != nil { log.Println("Get Xid from Pid errors is: ", err) return 0, 0, 0, 0 } return internalGetBounds(int(xid), isPid) } // GetClient get the window client bounds func GetClient(pid int, args ...int) (int, int, int, int) { var isPid int if len(args) > 0 || NotPid { isPid = 1 return internalGetClient(pid, isPid) } xid, err := GetXid(xu, pid) if err != nil { log.Println("Get Xid from Pid errors is: ", err) return 0, 0, 0, 0 } return internalGetClient(int(xid), isPid) } // internalGetTitle get the window title func internalGetTitle(pid int, args ...int) string { var isPid int if len(args) > 0 || NotPid { isPid = 1 return cgetTitle(pid, isPid) } xid, err := GetXid(xu, pid) if err != nil { log.Println("Get Xid from Pid errors is: ", err) return "" } return cgetTitle(int(xid), isPid) } // ActivePidC active the window by Pid, // If args[0] > 0 on the unix platform via a xid to active func ActivePidC(pid int, args ...int) error { var isPid int if len(args) > 0 || NotPid { isPid = 1 internalActive(pid, isPid) return nil } xid, err := GetXid(xu, pid) if err != nil { log.Println("Get Xid from Pid errors is: ", err) return err } internalActive(int(xid), isPid) return nil } // ActivePid active the window by Pid, // // If args[0] > 0 on the Windows platform via a window handle to active, // If args[0] > 0 on the unix platform via a xid to active func ActivePid(pid int, args ...int) error { if xu == nil { var err error xu, err = xgbutil.NewConn() if err != nil { return err } } if len(args) > 0 { err := ewmh.ActiveWindowReq(xu, xproto.Window(pid)) if err != nil { return err } return nil } // get the xid from pid xid, err := GetXidByPid(xu, pid) if err != nil { return err } err = ewmh.ActiveWindowReq(xu, xid) if err != nil { return err } return nil } // GetXid get the xid return window and error func GetXid(xu *xgbutil.XUtil, pid int) (xproto.Window, error) { if xu == nil { var err error xu, err = xgbutil.NewConn() if err != nil { // log.Println("xgbutil.NewConn errors is: ", err) return 0, err } } xid, err := GetXidByPid(xu, pid) return xid, err } // Deprecated: use the GetXidByPid(), // // GetXidFromPid get the xid from pid func GetXidFromPid(xu *xgbutil.XUtil, pid int) (xproto.Window, error) { return GetXidByPid(xu, pid) } // GetXidByPid get the xid from pid func GetXidByPid(xu *xgbutil.XUtil, pid int) (xproto.Window, error) { windows, err := ewmh.ClientListGet(xu) if err != nil { return 0, err } for _, window := range windows { wmPid, err := ewmh.WmPidGet(xu, window) if err != nil { return 0, err } if uint(pid) == wmPid { return window, nil } } return 0, errors.New("failed to find a window with a matching pid.") } // DisplaysNum get the count of displays func DisplaysNum() int { c, err := xgb.NewConn() if err != nil { return 0 } defer c.Close() err = xinerama.Init(c) if err != nil { return 0 } reply, err := xinerama.QueryScreens(c).Reply() if err != nil { return 0 } return int(reply.Number) } // GetMainId get the main display id func GetMainId() int { conn, err := xgb.NewConn() if err != nil { return -1 } setup := xproto.Setup(conn) defaultScreen := setup.DefaultScreen(conn) id := -1 for i, screen := range setup.Roots { if defaultScreen.Root == screen.Root { id = i break } } return id } // Alert show a alert window // Displays alert with the attributes. // If cancel button is not given, only the default button is displayed // // Examples: // // robotgo.Alert("hi", "window", "ok", "cancel") func Alert(title, msg string, args ...string) bool { defaultBtn, cancelBtn := alertArgs(args...) c := `xmessage -center ` + msg + ` -title ` + title + ` -buttons ` + defaultBtn + ":0," if cancelBtn != "" { c += cancelBtn + ":1" } c += ` -default ` + defaultBtn c += ` -geometry 400x200` out, err := Run(c) if err != nil { // fmt.Println("Alert: ", err, ". ", string(out)) return false } if string(out) == "1" { return false } return true } ================================================ FILE: screen/goScreen.h ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. #include "../base/types.h" #include "../base/pubs.h" #include "../base/rgb.h" #include "screengrab_c.h" #include void padHex(MMRGBHex color, char* hex) { // Length needs to be 7 because snprintf includes a terminating null. snprintf(hex, 7, "%06x", color); } char* pad_hex(MMRGBHex color) { char hex[7]; padHex(color, hex); // destroyMMBitmap(bitmap); char* str = (char*)calloc(100, sizeof(char*)); if (str) { strcpy(str, hex); } return str; } static uint8_t rgb[3]; uint8_t* color_hex_to_rgb(uint32_t h) { rgb[0] = RED_FROM_HEX(h); rgb[1] = GREEN_FROM_HEX(h); rgb[2] = BLUE_FROM_HEX(h); return rgb; } uint32_t color_rgb_to_hex(uint8_t r, uint8_t g, uint8_t b) { return RGB_TO_HEX(r, g, b); } MMRGBHex get_px_color(int32_t x, int32_t y, int32_t display_id) { MMBitmapRef bitmap; MMRGBHex color; if (!pointVisibleOnMainDisplay(MMPointInt32Make(x, y))) { return color; } bitmap = copyMMBitmapFromDisplayInRect(MMRectInt32Make(x, y, 1, 1), display_id, 0); color = MMRGBHexAtPoint(bitmap, 0, 0); destroyMMBitmap(bitmap); return color; } char* set_XDisplay_name(char* name) { #if defined(USE_X11) setXDisplay(name); return ""; #else return "SetXDisplayName is only supported on Linux"; #endif } char* get_XDisplay_name() { #if defined(USE_X11) const char* display = getXDisplay(); char* sd = (char*)calloc(100, sizeof(char*)); if (sd) { strcpy(sd, display); } return sd; #else return "GetXDisplayName is only supported on Linux"; #endif } void close_main_display() { #if defined(USE_X11) XCloseMainDisplay(); #else // #endif } uint32_t get_num_displays() { #if defined(IS_MACOSX) uint32_t count = 0; if (CGGetActiveDisplayList(0, nil, &count) == kCGErrorSuccess) { return count; } return 0; #elif defined(USE_X11) return 0; #elif defined(IS_WINDOWS) uint32_t count = 0; if (EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&count)) { return count; } return 0; #endif } uintptr get_hwnd_by_pid(uintptr pid) { #if defined(IS_WINDOWS) HWND hwnd = GetHwndByPid(pid); return (uintptr)hwnd; #else return 0; #endif } void bitmap_dealloc(MMBitmapRef bitmap) { if (bitmap != NULL) { destroyMMBitmap(bitmap); bitmap = NULL; } } // capture_screen capture screen MMBitmapRef capture_screen(int32_t x, int32_t y, int32_t w, int32_t h, int32_t display_id, int8_t isPid) { MMBitmapRef bitmap = copyMMBitmapFromDisplayInRect(MMRectInt32Make(x, y, w, h), display_id, isPid); return bitmap; } ================================================ FILE: screen/screen.go ================================================ package screen ================================================ FILE: screen/screen_c.h ================================================ //#include "../base/os.h" #if defined(IS_MACOSX) #include #elif defined(USE_X11) #include #include // #include "../base/xdisplay_c.h" #endif intptr scaleX(); double sys_scale(int32_t display_id) { #if defined(IS_MACOSX) CGDirectDisplayID displayID = (CGDirectDisplayID) display_id; if (displayID == -1) { displayID = CGMainDisplayID(); } CGDisplayModeRef modeRef = CGDisplayCopyDisplayMode(displayID); double pixelWidth = CGDisplayModeGetPixelWidth(modeRef); double targetWidth = CGDisplayModeGetWidth(modeRef); return pixelWidth / targetWidth; #elif defined(USE_X11) Display *dpy = XOpenDisplay(NULL); int scr = 0; /* Screen number */ double xres = ((((double) DisplayWidth(dpy, scr)) * 25.4) / ((double) DisplayWidthMM(dpy, scr))); char *rms = XResourceManagerString(dpy); if (rms) { XrmDatabase db = XrmGetStringDatabase(rms); if (db) { XrmValue value; char *type = NULL; if (XrmGetResource(db, "Xft.dpi", "String", &type, &value)) { if (value.addr) { xres = atof(value.addr); } } XrmDestroyDatabase(db); } } XCloseDisplay (dpy); return xres / 96.0; #elif defined(IS_WINDOWS) double s = scaleX() / 96.0; return s; #endif } intptr scaleX(){ #if defined(IS_MACOSX) return 0; #elif defined(USE_X11) return 0; #elif defined(IS_WINDOWS) // Get desktop dc HDC desktopDc = GetDC(NULL); // Get native resolution intptr horizontalDPI = GetDeviceCaps(desktopDc, LOGPIXELSX); return horizontalDPI; #endif } MMSizeInt32 getMainDisplaySize(void) { #if defined(IS_MACOSX) CGDirectDisplayID displayID = CGMainDisplayID(); CGRect displayRect = CGDisplayBounds(displayID); CGSize size = displayRect.size; return MMSizeInt32Make((int32_t)size.width, (int32_t)size.height); #elif defined(USE_X11) Display *display = XGetMainDisplay(); const int screen = DefaultScreen(display); return MMSizeInt32Make( (int32_t)DisplayWidth(display, screen), (int32_t)DisplayHeight(display, screen)); #elif defined(IS_WINDOWS) return MMSizeInt32Make( (int32_t)GetSystemMetrics(SM_CXSCREEN), (int32_t)GetSystemMetrics(SM_CYSCREEN)); #endif } MMRectInt32 getScreenRect(int32_t display_id) { #if defined(IS_MACOSX) CGDirectDisplayID displayID = (CGDirectDisplayID) display_id; if (display_id == -1) { displayID = CGMainDisplayID(); } CGRect displayRect = CGDisplayBounds(displayID); CGPoint point = displayRect.origin; CGSize size = displayRect.size; return MMRectInt32Make( (int32_t)point.x, (int32_t)point.y, (int32_t)size.width, (int32_t)size.height); #elif defined(USE_X11) Display *display = XGetMainDisplay(); const int screen = DefaultScreen(display); return MMRectInt32Make( (int32_t)0, (int32_t)0, (int32_t)DisplayWidth(display, screen), (int32_t)DisplayHeight(display, screen)); #elif defined(IS_WINDOWS) if (GetSystemMetrics(SM_CMONITORS) == 1 || display_id == -1 || display_id == 0) { return MMRectInt32Make( (int32_t)0, (int32_t)0, (int32_t)GetSystemMetrics(SM_CXSCREEN), (int32_t)GetSystemMetrics(SM_CYSCREEN)); } else { return MMRectInt32Make( (int32_t)GetSystemMetrics(SM_XVIRTUALSCREEN), (int32_t)GetSystemMetrics(SM_YVIRTUALSCREEN), (int32_t)GetSystemMetrics(SM_CXVIRTUALSCREEN), (int32_t)GetSystemMetrics(SM_CYVIRTUALSCREEN)); } #endif } bool pointVisibleOnMainDisplay(MMPointInt32 point){ MMSizeInt32 displaySize = getMainDisplaySize(); return point.x < displaySize.w && point.y < displaySize.h; } ================================================ FILE: screen/screengrab_c.h ================================================ #include "../base/bitmap_free_c.h" #include /* malloc() */ #if defined(IS_MACOSX) #include #include #include #include #elif defined(USE_X11) #include #include #include "../base/xdisplay_c.h" #elif defined(IS_WINDOWS) #include #endif #include "screen_c.h" #if defined(IS_MACOSX) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 140400 static CGImageRef capture15(CGDirectDisplayID id, CGRect diIntersectDisplayLocal, CGColorSpaceRef colorSpace) { dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); __block CGImageRef image1 = nil; [SCShareableContent getShareableContentWithCompletionHandler:^(SCShareableContent* content, NSError* error) { @autoreleasepool { if (error) { dispatch_semaphore_signal(semaphore); return; } SCDisplay* target = nil; for (SCDisplay *display in content.displays) { if (display.displayID == id) { target = display; break; } } if (!target) { dispatch_semaphore_signal(semaphore); return; } SCContentFilter* filter = [[SCContentFilter alloc] initWithDisplay:target excludingWindows:@[]]; SCStreamConfiguration* config = [[SCStreamConfiguration alloc] init]; config.queueDepth = 5; config.sourceRect = diIntersectDisplayLocal; config.width = diIntersectDisplayLocal.size.width * sys_scale(id); config.height = diIntersectDisplayLocal.size.height * sys_scale(id); config.scalesToFit = false; config.captureResolution = 1; [SCScreenshotManager captureImageWithFilter:filter configuration:config completionHandler:^(CGImageRef img, NSError* error) { if (!error) { image1 = CGImageCreateCopyWithColorSpace(img, colorSpace); } dispatch_semaphore_signal(semaphore); }]; } }]; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); dispatch_release(semaphore); return image1; } #endif MMBitmapRef copyMMBitmapFromDisplayInRect(MMRectInt32 rect, int32_t display_id, int8_t isPid) { #if defined(IS_MACOSX) MMBitmapRef bitmap = NULL; uint8_t *buffer = NULL; size_t bufferSize = 0; CGDirectDisplayID displayID = (CGDirectDisplayID) display_id; if (displayID == -1 || displayID == 0) { displayID = CGMainDisplayID(); } MMPointInt32 o = rect.origin; MMSizeInt32 s = rect.size; #if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 140400 CGColorSpaceRef color = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); CGImageRef image = capture15(displayID, CGRectMake(o.x, o.y, s.w, s.h), color); CGColorSpaceRelease(color); #else // This API is deprecated in macos 15, use ScreenCaptureKit's captureScreenshot CGImageRef image = CGDisplayCreateImageForRect(displayID, CGRectMake(o.x, o.y, s.w, s.h)); #endif if (!image) { return NULL; } CFDataRef imageData = CGDataProviderCopyData(CGImageGetDataProvider(image)); if (!imageData) { return NULL; } bufferSize = CFDataGetLength(imageData); buffer = malloc(bufferSize); CFDataGetBytes(imageData, CFRangeMake(0, bufferSize), buffer); bitmap = createMMBitmap_c(buffer, CGImageGetWidth(image), CGImageGetHeight(image), CGImageGetBytesPerRow(image), CGImageGetBitsPerPixel(image), CGImageGetBitsPerPixel(image) / 8); CFRelease(imageData); CGImageRelease(image); return bitmap; #elif defined(USE_X11) MMBitmapRef bitmap; Display *display; if (display_id == -1) { display = XOpenDisplay(NULL); } else { display = XGetMainDisplay(); } MMPointInt32 o = rect.origin; MMSizeInt32 s = rect.size; XImage *image = XGetImage(display, XDefaultRootWindow(display), (int)o.x, (int)o.y, (unsigned int)s.w, (unsigned int)s.h, AllPlanes, ZPixmap); XCloseDisplay(display); if (image == NULL) { return NULL; } bitmap = createMMBitmap_c((uint8_t *)image->data, s.w, s.h, (size_t)image->bytes_per_line, (uint8_t)image->bits_per_pixel, (uint8_t)image->bits_per_pixel / 8); image->data = NULL; /* Steal ownership of bitmap data so we don't have to copy it. */ XDestroyImage(image); return bitmap; #elif defined(IS_WINDOWS) MMBitmapRef bitmap; void *data; HDC screen = NULL, screenMem = NULL; HBITMAP dib; BITMAPINFO bi; int32_t x = rect.origin.x, y = rect.origin.y; int32_t w = rect.size.w, h = rect.size.h; /* Initialize bitmap info. */ bi.bmiHeader.biSize = sizeof(bi.bmiHeader); bi.bmiHeader.biWidth = (long) w; bi.bmiHeader.biHeight = -(long) h; /* Non-cartesian, please */ bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biBitCount = 32; bi.bmiHeader.biCompression = BI_RGB; bi.bmiHeader.biSizeImage = (DWORD)(4 * w * h); bi.bmiHeader.biXPelsPerMeter = 0; bi.bmiHeader.biYPelsPerMeter = 0; bi.bmiHeader.biClrUsed = 0; bi.bmiHeader.biClrImportant = 0; HWND hwnd; if (display_id == -1 || isPid == 0) { // screen = GetDC(NULL); /* Get entire screen */ hwnd = GetDesktopWindow(); } else { hwnd = (HWND) (uintptr) display_id; } screen = GetDC(hwnd); if (screen == NULL) { return NULL; } // Todo: Use DXGI screenMem = CreateCompatibleDC(screen); /* Get screen data in display device context. */ dib = CreateDIBSection(screen, &bi, DIB_RGB_COLORS, &data, NULL, 0); /* Copy the data into a bitmap struct. */ BOOL b = (screenMem == NULL) || SelectObject(screenMem, dib) == NULL || !BitBlt(screenMem, (int)0, (int)0, (int)w, (int)h, screen, x, y, SRCCOPY); if (b) { /* Error copying data. */ ReleaseDC(hwnd, screen); DeleteObject(dib); if (screenMem != NULL) { DeleteDC(screenMem); } return NULL; } bitmap = createMMBitmap_c(NULL, w, h, 4 * w, (uint8_t)bi.bmiHeader.biBitCount, 4); /* Copy the data to our pixel buffer. */ if (bitmap != NULL) { bitmap->imageBuffer = malloc(bitmap->bytewidth * bitmap->height); memcpy(bitmap->imageBuffer, data, bitmap->bytewidth * bitmap->height); } ReleaseDC(hwnd, screen); DeleteObject(dib); DeleteDC(screenMem); return bitmap; #endif } ================================================ FILE: screen.go ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. package robotgo import ( "image" // "github.com/kbinani/screenshot" "github.com/vcaesar/screenshot" ) // GetDisplayBounds gets the display screen bounds func GetDisplayBounds(i int) (x, y, w, h int) { bs := screenshot.GetDisplayBounds(i) return bs.Min.X, bs.Min.Y, bs.Dx(), bs.Dy() } // GetDisplayRect gets the display rect func GetDisplayRect(i int) Rect { x, y, w, h := GetDisplayBounds(i) return Rect{ Point{X: x, Y: y}, Size{W: w, H: h}} } // Capture capture the screenshot, use the CaptureImg default func Capture(args ...int) (*image.RGBA, error) { displayId := 0 if DisplayID != -1 { displayId = DisplayID } if len(args) > 4 { displayId = args[4] } var x, y, w, h int if len(args) > 3 { x, y, w, h = args[0], args[1], args[2], args[3] } else { x, y, w, h = GetDisplayBounds(displayId) } return screenshot.Capture(x, y, w, h) } // SaveCapture capture screen and save the screenshot to image func SaveCapture(path string, args ...int) error { img, err := CaptureImg(args...) if err != nil { return err } return Save(img, path) } ================================================ FILE: test/index.html ================================================

Type and check the console

================================================ FILE: wayland_n.go ================================================ // +bulid linux,next package robotgo ================================================ FILE: window/alert_c.h ================================================ // #include "os.h" #if defined(IS_MACOSX) #include #endif #if defined(IS_MACOSX) CFStringRef CFStringCreateWithUTF8String(const char *title) { if (title == NULL) { return NULL; } return CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8); } #endif int showAlert(const char *title, const char *msg, const char *defaultButton, const char *cancelButton) { #if defined(IS_MACOSX) CFStringRef alertHeader = CFStringCreateWithUTF8String(title); CFStringRef alertMessage = CFStringCreateWithUTF8String(msg); CFStringRef defaultButtonTitle = CFStringCreateWithUTF8String(defaultButton); CFStringRef cancelButtonTitle = CFStringCreateWithUTF8String(cancelButton); CFOptionFlags responseFlags; SInt32 err = CFUserNotificationDisplayAlert( 0.0, kCFUserNotificationNoteAlertLevel, NULL, NULL, NULL, alertHeader, alertMessage, defaultButtonTitle, cancelButtonTitle, NULL, &responseFlags); if (alertHeader != NULL) CFRelease(alertHeader); if (alertMessage != NULL) CFRelease(alertMessage); if (defaultButtonTitle != NULL) CFRelease(defaultButtonTitle); if (cancelButtonTitle != NULL) CFRelease(cancelButtonTitle); if (err != 0) { return -1; } return (responseFlags == kCFUserNotificationDefaultResponse) ? 0 : 1; #elif defined(USE_X11) return 0; #else /* TODO: Display custom buttons instead of the pre-defined "OK" and "Cancel". */ int response = MessageBox(NULL, msg, title, (cancelButton == NULL) ? MB_OK : MB_OKCANCEL ); return (response == IDOK) ? 0 : 1; #endif } ================================================ FILE: window/goWindow.h ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. #include "alert_c.h" #include "window.h" #include "win_sys.h" void min_window(uintptr pid, bool state, int8_t isPid){ #if defined(IS_MACOSX) // return 0; AXUIElementRef axID = AXUIElementCreateApplication(pid); AXUIElementSetAttributeValue(axID, kAXMinimizedAttribute, state ? kCFBooleanTrue : kCFBooleanFalse); #elif defined(USE_X11) // Ignore X errors XDismissErrors(); // SetState((Window)pid, STATE_MINIMIZE, state); #elif defined(IS_WINDOWS) HWND hwnd = getHwnd(pid, isPid); win_min(hwnd, state); #endif } void max_window(uintptr pid, bool state, int8_t isPid){ #if defined(IS_MACOSX) // return 0; #elif defined(USE_X11) XDismissErrors(); // SetState((Window)pid, STATE_MINIMIZE, false); // SetState((Window)pid, STATE_MAXIMIZE, state); #elif defined(IS_WINDOWS) HWND hwnd = getHwnd(pid, isPid); win_max(hwnd, state); #endif } uintptr get_handle(){ MData mData = get_active(); #if defined(IS_MACOSX) return (uintptr)mData.CgID; #elif defined(USE_X11) return (uintptr)mData.XWin; #elif defined(IS_WINDOWS) return (uintptr)mData.HWnd; #endif } uintptr b_get_handle() { #if defined(IS_MACOSX) return (uintptr)pub_mData.CgID; #elif defined(USE_X11) return (uintptr)pub_mData.XWin; #elif defined(IS_WINDOWS) return (uintptr)pub_mData.HWnd; #endif } void active_PID(uintptr pid, int8_t isPid){ MData win = set_handle_pid(pid, isPid); set_active(win); } ================================================ FILE: window/pub.h ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. // #include "../base/os.h" #if defined(IS_MACOSX) #include #elif defined(USE_X11) #include #endif struct _MData{ #if defined(IS_MACOSX) CGWindowID CgID; // Handle to a CGWindowID AXUIElementRef AxID; // Handle to a AXUIElementRef #elif defined(USE_X11) Window XWin; // Handle to an X11 window #elif defined(IS_WINDOWS) HWND HWnd; // Handle to a window HWND TCHAR Title[512]; #endif }; typedef struct _MData MData; MData pub_mData; struct _Bounds { int32_t X; // Top left X coordinate int32_t Y; // Top left Y coordinate int32_t W; // bounds width int32_t H; // bounds height }; typedef struct _Bounds Bounds; #if defined(IS_MACOSX) static Boolean(*gAXIsProcessTrustedWithOptions) (CFDictionaryRef); static CFStringRef* gkAXTrustedCheckOptionPrompt; AXError _AXUIElementGetWindow(AXUIElementRef, CGWindowID* out); static AXUIElementRef GetUIElement(CGWindowID win){ intptr pid = 0; // double_t pid = 0; // Create array storing window CGWindowID window[1] = { win }; CFArrayRef wlist = CFArrayCreate(NULL, (const void**)window, 1, NULL); // Get window info CFArrayRef info = CGWindowListCreateDescriptionFromArray(wlist); CFRelease(wlist); // Check whether the resulting array is populated if (info != NULL && CFArrayGetCount(info) > 0) { // Retrieve description from info array CFDictionaryRef desc = (CFDictionaryRef)CFArrayGetValueAtIndex(info, 0); // Get window PID CFNumberRef data = (CFNumberRef) CFDictionaryGetValue(desc, kCGWindowOwnerPID); if (data != NULL) { CFNumberGetValue(data, kCFNumberIntType, &pid); } // Return result CFRelease(info); } // Check if PID was retrieved if (pid <= 0) { return NULL; } // Create an accessibility object using retrieved PID AXUIElementRef application = AXUIElementCreateApplication(pid); if (application == 0) {return NULL;} CFArrayRef windows = NULL; // Get all windows associated with the app AXUIElementCopyAttributeValues(application, kAXWindowsAttribute, 0, 1024, &windows); // Reference to resulting value AXUIElementRef result = NULL; if (windows != NULL) { int count = CFArrayGetCount(windows); // Loop all windows in the process for (CFIndex i = 0; i < count; ++i){ // Get the element at the index AXUIElementRef element = (AXUIElementRef) CFArrayGetValueAtIndex(windows, i); CGWindowID temp = 0; // Use undocumented API to get WindowID _AXUIElementGetWindow(element, &temp); if (temp == win) { // Retain element CFRetain(element); result = element; break; } } CFRelease(windows); } CFRelease(application); return result; } #elif defined(USE_X11) // Error Handling typedef int (*XErrorHandler) (Display*, XErrorEvent*); static int XHandleError(Display* dp, XErrorEvent* e) { return 0; } XErrorHandler mOld; void XDismissErrors (void) { Display *rDisplay = XOpenDisplay(NULL); // Save old handler and dismiss errors mOld = XSetErrorHandler(XHandleError); // Flush output buffer XSync(rDisplay, False); // Reinstate old handler XSetErrorHandler(mOld); XCloseDisplay(rDisplay); } // Definitions struct Hints{ unsigned long Flags; unsigned long Funcs; unsigned long Decorations; signed long Mode; unsigned long Stat; }; static Atom WM_STATE = None; static Atom WM_ABOVE = None; static Atom WM_HIDDEN = None; static Atom WM_HMAX = None; static Atom WM_VMAX = None; static Atom WM_DESKTOP = None; static Atom WM_CURDESK = None; static Atom WM_NAME = None; static Atom WM_UTF8 = None; static Atom WM_PID = None; static Atom WM_ACTIVE = None; static Atom WM_HINTS = None; static Atom WM_EXTENTS = None; //////////////////////////////////////////////////////////////////////////////// static void LoadAtoms (void){ Display *rDisplay = XOpenDisplay(NULL); WM_STATE = XInternAtom(rDisplay, "_NET_WM_STATE", True); WM_ABOVE = XInternAtom(rDisplay, "_NET_WM_STATE_ABOVE", True); WM_HIDDEN = XInternAtom(rDisplay, "_NET_WM_STATE_HIDDEN", True); WM_HMAX = XInternAtom(rDisplay, "_NET_WM_STATE_MAXIMIZED_HORZ", True); WM_VMAX = XInternAtom(rDisplay, "_NET_WM_STATE_MAXIMIZED_VERT", True); WM_DESKTOP = XInternAtom(rDisplay, "_NET_WM_DESKTOP", True); WM_CURDESK = XInternAtom(rDisplay, "_NET_CURRENT_DESKTOP", True); WM_NAME = XInternAtom(rDisplay, "_NET_WM_NAME", True); WM_UTF8 = XInternAtom(rDisplay, "UTF8_STRING", True); WM_PID = XInternAtom(rDisplay, "_NET_WM_PID", True); WM_ACTIVE = XInternAtom(rDisplay, "_NET_ACTIVE_WINDOW", True); WM_HINTS = XInternAtom(rDisplay, "_MOTIF_WM_HINTS", True); WM_EXTENTS = XInternAtom(rDisplay, "_NET_FRAME_EXTENTS", True); XCloseDisplay(rDisplay); } // Functions static void* GetWindowProperty(MData win, Atom atom, uint32_t* items) { // Property variables Atom type; int format; unsigned long nItems; unsigned long bAfter; unsigned char* result = NULL; Display *rDisplay = XOpenDisplay(NULL); // Check the atom if (atom != None) { // Retrieve and validate the specified property if (!XGetWindowProperty(rDisplay, win.XWin, atom, 0, BUFSIZ, False, AnyPropertyType, &type, &format, &nItems, &bAfter, &result) && result && nItems) { // Copy items result if (items != NULL) { *items = (uint32_t) nItems; } XCloseDisplay(rDisplay); return result; } } // Reset the items result if valid if (items != NULL) { *items = 0; } if (result != NULL) { XFree(result); } XCloseDisplay(rDisplay); return NULL; } ////// #define STATE_TOPMOST 0 #define STATE_MINIMIZE 1 #define STATE_MAXIMIZE 2 ////// static void SetDesktopForWindow(MData win){ Display *rDisplay = XOpenDisplay(NULL); // Validate every atom that we want to use if (WM_DESKTOP != None && WM_CURDESK != None) { // Get desktop property long* desktop = (long*)GetWindowProperty(win, WM_DESKTOP,NULL); // Check result value if (desktop != NULL) { // Retrieve the screen number XWindowAttributes attr = { 0 }; XGetWindowAttributes(rDisplay, win.XWin, &attr); int s = XScreenNumberOfScreen(attr.screen); Window root = XRootWindow(rDisplay, s); // Prepare an event XClientMessageEvent e = { 0 }; e.window = root; e.format = 32; e.message_type = WM_CURDESK; e.display = rDisplay; e.type = ClientMessage; e.data.l[0] = *desktop; e.data.l[1] = CurrentTime; // Send the message XSendEvent(rDisplay, root, False, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent*) &e); XFree(desktop); } } XCloseDisplay(rDisplay); } static Bounds GetFrame(MData win){ Bounds frame; // Retrieve frame bounds if (WM_EXTENTS != None) { long* result; uint32_t nItems = 0; // Get the window extents property result = (long*) GetWindowProperty(win, WM_EXTENTS, &nItems); if (result != NULL) { if (nItems == 4) { frame.X = (int32_t) result[0]; frame.Y = (int32_t) result[2]; frame.W = (int32_t) result[0] + (int32_t) result[1]; frame.H = (int32_t) result[2] + (int32_t) result[3]; } XFree(result); } } return frame; } #elif defined(IS_WINDOWS) HWND getHwnd(uintptr pid, int8_t isPid); void win_min(HWND hwnd, bool state){ if (state) { ShowWindow(hwnd, SW_MINIMIZE); } else { ShowWindow(hwnd, SW_RESTORE); } } void win_max(HWND hwnd, bool state){ if (state) { ShowWindow(hwnd, SW_MAXIMIZE); } else { ShowWindow(hwnd, SW_RESTORE); } } #endif ================================================ FILE: window/win_sys.h ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. // #if defined(USE_X11) // #include // #endif Bounds get_client(uintptr pid, int8_t isPid); Bounds get_bounds(uintptr pid, int8_t isPid){ // Check if the window is valid Bounds bounds; if (!is_valid()) { return bounds; } #if defined(IS_MACOSX) // Bounds bounds; AXValueRef axp = NULL; AXValueRef axs = NULL; AXUIElementRef AxID = AXUIElementCreateApplication(pid); AXUIElementRef AxWin = NULL; // Get the window from the application if (AXUIElementCopyAttributeValue(AxID, kAXFocusedWindowAttribute, (CFTypeRef *)&AxWin) != kAXErrorSuccess || AxWin == NULL) { // If no focused window, try to get the main window if (AXUIElementCopyAttributeValue(AxID, kAXMainWindowAttribute, (CFTypeRef *)&AxWin) != kAXErrorSuccess || AxWin == NULL) { goto exit; } } // Determine the current point of the window if (AXUIElementCopyAttributeValue(AxWin, kAXPositionAttribute, (CFTypeRef*) &axp) != kAXErrorSuccess || axp == NULL) { goto exit; } // Determine the current size of the window if (AXUIElementCopyAttributeValue(AxWin, kAXSizeAttribute, (CFTypeRef*) &axs) != kAXErrorSuccess || axs == NULL) { goto exit; } CGPoint p; CGSize s; // Attempt to convert both values into atomic types if (AXValueGetValue(axp, kAXValueCGPointType, &p) && AXValueGetValue(axs, kAXValueCGSizeType, &s)) { bounds.X = p.x; bounds.Y = p.y; bounds.W = s.width; bounds.H = s.height; } exit: if (axp != NULL) { CFRelease(axp); } if (axs != NULL) { CFRelease(axs); } if (AxWin != NULL) { CFRelease(AxWin); } if (AxID != NULL) { CFRelease(AxID); } return bounds; #elif defined(USE_X11) // Ignore X errors XDismissErrors(); MData win; win.XWin = (Window)pid; Bounds client = get_client(pid, isPid); Bounds frame = GetFrame(win); bounds.X = client.X - frame.X; bounds.Y = client.Y - frame.Y; bounds.W = client.W + frame.W; bounds.H = client.H + frame.H; return bounds; #elif defined(IS_WINDOWS) HWND hwnd = getHwnd(pid, isPid); RECT rect = { 0 }; GetWindowRect(hwnd, &rect); bounds.X = rect.left; bounds.Y = rect.top; bounds.W = rect.right - rect.left; bounds.H = rect.bottom - rect.top; return bounds; #endif } Bounds get_client(uintptr pid, int8_t isPid) { // Check if the window is valid Bounds bounds; if (!is_valid()) { return bounds; } #if defined(IS_MACOSX) return get_bounds(pid, isPid); #elif defined(USE_X11) Display *rDisplay = XOpenDisplay(NULL); // Ignore X errors XDismissErrors(); MData win; win.XWin = (Window)pid; // Property variables Window root, parent; Window* children; unsigned int count; int32_t x = 0, y = 0; // Check if the window is the root XQueryTree(rDisplay, win.XWin, &root, &parent, &children, &count); if (children) { XFree(children); } // Retrieve window attributes XWindowAttributes attr = { 0 }; XGetWindowAttributes(rDisplay, win.XWin, &attr); // Coordinates must be translated if (parent != attr.root) { XTranslateCoordinates(rDisplay, win.XWin, attr.root, attr.x, attr.y, &x, &y, &parent); } else { x = attr.x; y = attr.y; } // Return resulting window bounds bounds.X = x; bounds.Y = y; bounds.W = attr.width; bounds.H = attr.height; XCloseDisplay(rDisplay); return bounds; #elif defined(IS_WINDOWS) HWND hwnd = getHwnd(pid, isPid); RECT rect = { 0 }; GetClientRect(hwnd, &rect); POINT point; point.x = rect.left; point.y = rect.top; // Convert the client point to screen ClientToScreen(hwnd, &point); bounds.X = point.x; bounds.Y = point.y; bounds.W = rect.right - rect.left; bounds.H = rect.bottom - rect.top; return bounds; #endif } ================================================ FILE: window/window.go ================================================ package window ================================================ FILE: window/window.h ================================================ // Copyright (c) 2016-2025 AtomAI, All rights reserved. // // See the COPYRIGHT file at the top-level directory of this distribution and at // https://github.com/go-vgo/robotgo/blob/master/LICENSE // // Licensed under the Apache License, Version 2.0 // // This file may not be copied, modified, or distributed // except according to those terms. #include "pub.h" bool setHandle(uintptr handle); bool is_valid(); bool IsAxEnabled(bool options); MData get_active(void); void initWindow(uintptr handle); char* get_title_by_hand(MData m_data); void close_window_by_Id(MData m_data); // int findwindow() uintptr initHandle = 0; void initWindow(uintptr handle){ #if defined(IS_MACOSX) pub_mData.CgID = 0; pub_mData.AxID = 0; #elif defined(USE_X11) Display *rDisplay = XOpenDisplay(NULL); // If atoms loaded if (WM_PID == None) { // Load all necessary atom properties if (rDisplay != NULL) {LoadAtoms();} } pub_mData.XWin = 0; XCloseDisplay(rDisplay); #elif defined(IS_WINDOWS) pub_mData.HWnd = 0; #endif setHandle(handle); } bool Is64Bit() { #ifdef RobotGo_64 return true; #endif return false; } MData set_handle_pid(uintptr pid, int8_t isPid){ MData win; #if defined(IS_MACOSX) // Handle to a AXUIElementRef win.AxID = AXUIElementCreateApplication(pid); #elif defined(USE_X11) win.XWin = (Window)pid; // Handle to an X11 window #elif defined(IS_WINDOWS) // win.HWnd = (HWND)pid; // Handle to a window HWND win.HWnd = getHwnd(pid, isPid); #endif return win; } void set_handle_pid_mData(uintptr pid, int8_t isPid){ MData win = set_handle_pid(pid, isPid); pub_mData = win; } bool is_valid() { initWindow(initHandle); if (!IsAxEnabled(true)) { printf("%s\n", "Window: Accessibility API is disabled! " "Failed to enable access for assistive devices. \n"); } MData actdata = get_active(); #if defined(IS_MACOSX) pub_mData.CgID = actdata.CgID; pub_mData.AxID = actdata.AxID; if (pub_mData.CgID == 0 || pub_mData.AxID == 0) { return false; } CFTypeRef r = NULL; // Attempt to get the window role if (AXUIElementCopyAttributeValue(pub_mData.AxID, kAXRoleAttribute, &r) == kAXErrorSuccess && r){ CFRelease(r); return true; } return false; #elif defined(USE_X11) pub_mData.XWin = actdata.XWin; if (pub_mData.XWin == 0) { return false; } Display *rDisplay = XOpenDisplay(NULL); // Check for a valid X-Window display if (rDisplay == NULL) { return false; } // Ignore X errors XDismissErrors(); // Get the window PID property void* result = GetWindowProperty(pub_mData, WM_PID,NULL); if (result == NULL) { XCloseDisplay(rDisplay); return false; } // Free result and return true XFree(result); XCloseDisplay(rDisplay); return true; #elif defined(IS_WINDOWS) pub_mData.HWnd = actdata.HWnd; if (pub_mData.HWnd == 0) { return false; } return IsWindow(pub_mData.HWnd) != 0; #endif } bool IsAxEnabled(bool options){ #if defined(IS_MACOSX) // Statically load all required functions one time static dispatch_once_t once; dispatch_once (&once, ^{ // Open the framework void* handle = dlopen("/System/Library/Frameworks/Application" "Services.framework/ApplicationServices", RTLD_LAZY); // Validate the handle if (handle != NULL) { *(void**) (&gAXIsProcessTrustedWithOptions) = dlsym (handle, "AXIsProcessTrustedWithOptions"); gkAXTrustedCheckOptionPrompt = (CFStringRef*) dlsym (handle, "kAXTrustedCheckOptionPrompt"); } }); // Check for new OSX 10.9 function if (gAXIsProcessTrustedWithOptions) { // Check whether to show prompt CFBooleanRef displayPrompt = options ? kCFBooleanTrue : kCFBooleanFalse; // Convert display prompt value into a dictionary const void* k[] = { *gkAXTrustedCheckOptionPrompt }; const void* v[] = { displayPrompt }; CFDictionaryRef o = CFDictionaryCreate(NULL, k, v, 1, NULL, NULL); // Determine whether the process is actually trusted bool result = (*gAXIsProcessTrustedWithOptions)(o); // Free memory CFRelease(o); return result; } else { // Ignore deprecated warnings #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" // Check whether we have accessibility access return AXAPIEnabled() || AXIsProcessTrusted(); #pragma clang diagnostic pop } #elif defined(USE_X11) return true; #elif defined(IS_WINDOWS) return true; #endif } // int bool setHandle(uintptr handle){ #if defined(IS_MACOSX) // Release the AX element if (pub_mData.AxID != NULL) { CFRelease(pub_mData.AxID); } // Reset both values pub_mData.CgID = 0; pub_mData.AxID = 0; if (handle == 0) { // return 0; return true; } // Retrieve the window element CGWindowID cgID = (CGWindowID)handle; AXUIElementRef axID = GetUIElement(cgID); if (axID != NULL){ pub_mData.CgID = cgID; pub_mData.AxID = axID; // return 0; return true; } // return 1; return false; #elif defined(USE_X11) pub_mData.XWin = (Window)handle; if (handle == 0) { return true; } if (is_valid()) { return true; } pub_mData.XWin = 0; return false; #elif defined(IS_WINDOWS) pub_mData.HWnd = (HWND)handle; if (handle == 0) { return true; } if (is_valid()) { return true; } pub_mData.HWnd = 0; return false; #endif } bool IsTopMost(void){ // Check the window validity if (!is_valid()) { return false; } #if defined(IS_MACOSX) return false; // WARNING: Unavailable #elif defined(USE_X11) // Ignore X errors // XDismissErrors (); // return GetState (mData.XWin, STATE_TOPMOST); #elif defined(IS_WINDOWS) return (GetWindowLongPtr(pub_mData.HWnd, GWL_EXSTYLE) & WS_EX_TOPMOST) != 0; #endif } bool IsMinimized(void){ // Check the window validity if (!is_valid()) { return false; } #if defined(IS_MACOSX) CFBooleanRef data = NULL; // Determine whether the window is minimized if (AXUIElementCopyAttributeValue(pub_mData.AxID, kAXMinimizedAttribute, (CFTypeRef*) &data) == kAXErrorSuccess && data != NULL) { // Convert resulting data into a bool bool result = CFBooleanGetValue(data); CFRelease(data); return result; } return false; #elif defined(USE_X11) // Ignore X errors // XDismissErrors(); // return GetState(mData.XWin, STATE_MINIMIZE); #elif defined(IS_WINDOWS) return (GetWindowLongPtr(pub_mData.HWnd, GWL_STYLE) & WS_MINIMIZE) != 0; #endif } ////// bool IsMaximized(void){ // Check the window validity if (!is_valid()) { return false; } #if defined(IS_MACOSX) return false; // WARNING: Unavailable #elif defined(USE_X11) // Ignore X errors // XDismissErrors(); // return GetState(mData.XWin, STATE_MAXIMIZE); #elif defined(IS_WINDOWS) return (GetWindowLongPtr(pub_mData.HWnd, GWL_STYLE) & WS_MAXIMIZE) != 0; #endif } void set_active(const MData win) { // Check if the window is valid if (!is_valid()) { return; } #if defined(IS_MACOSX) // Attempt to raise the specified window object if (AXUIElementPerformAction(win.AxID, kAXRaiseAction) != kAXErrorSuccess) { pid_t pid = 0; // Attempt to retrieve the PID of the window if (AXUIElementGetPid(win.AxID, &pid) != kAXErrorSuccess || !pid) { return; } // Ignore deprecated warnings #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" ProcessSerialNumber psn; // Attempt to retrieve the process psn if (GetProcessForPID(pid, &psn) == 0) { // Gracefully activate process SetFrontProcessWithOptions(&psn, kSetFrontProcessFrontWindowOnly); } #pragma clang diagnostic pop } #elif defined(USE_X11) // Ignore X errors XDismissErrors(); // Go to the specified window's desktop SetDesktopForWindow(win); Display *rDisplay = XOpenDisplay(NULL); // Check the atom value if (WM_ACTIVE != None) { // Retrieve the screen number XWindowAttributes attr = { 0 }; XGetWindowAttributes(rDisplay, win.XWin, &attr); int s = XScreenNumberOfScreen(attr.screen); // Prepare an event XClientMessageEvent e = { 0 }; e.window = win.XWin; e.format = 32; e.message_type = WM_ACTIVE; e.display = rDisplay; e.type = ClientMessage; e.data.l[0] = 2; e.data.l[1] = CurrentTime; // Send the message XSendEvent(rDisplay, XRootWindow(rDisplay, s), False, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent*) &e); } else { // Attempt to raise the specified window XRaiseWindow(rDisplay, win.XWin); // Set the specified window's input focus XSetInputFocus(rDisplay, win.XWin, RevertToParent, CurrentTime); } XCloseDisplay(rDisplay); #elif defined(IS_WINDOWS) if (IsMinimized()) { ShowWindow(win.HWnd, SW_RESTORE); } SetForegroundWindow(win.HWnd); #endif } MData get_active(void) { #if defined(IS_MACOSX) MData result = {0}; // Ignore deprecated warnings #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" ProcessSerialNumber psn; pid_t pid; // Attempt to retrieve the front process if (GetFrontProcess(&psn) != 0 || GetProcessPID(&psn, &pid) != 0) { return result; } #pragma clang diagnostic pop // Create accessibility object using focused PID AXUIElementRef focused = AXUIElementCreateApplication(pid); if (focused == NULL) { return result; } // Verify AXUIElementRef element = NULL; CGWindowID win = 0; // Retrieve the currently focused window if (AXUIElementCopyAttributeValue(focused, kAXFocusedWindowAttribute, (CFTypeRef*) &element) == kAXErrorSuccess && element) { // Use undocumented API to get WID if (_AXUIElementGetWindow(element, &win) == kAXErrorSuccess && win) { // Manually set internals result.CgID = win; result.AxID = element; } else { CFRelease(element); } } CFRelease(focused); return result; #elif defined(USE_X11) MData result = {0}; Display *rDisplay = XOpenDisplay(NULL); // Check X-Window display if (WM_ACTIVE == None || rDisplay == NULL) { return result; } // Ignore X errors XDismissErrors(); // Get the current active window result.XWin = XDefaultRootWindow(rDisplay); void* active = GetWindowProperty(result, WM_ACTIVE, NULL); // Check result value if (active != NULL) { // Extract window from the result long window = *((long*)active); XFree(active); if (window != 0) { // Set and return the foreground window result.XWin = (Window)window; XCloseDisplay(rDisplay); return result; } } // Use input focus instead Window window = None; int revert = RevertToNone; XGetInputFocus(rDisplay, &window, &revert); XCloseDisplay(rDisplay); // Return foreground window result.XWin = window; return result; #elif defined(IS_WINDOWS) // Attempt to get the foreground window multiple times in case MData result = {0}; uint8_t times = 0; while (++times < 20) { HWND handle; handle = GetForegroundWindow(); if (handle != NULL) { // mData.HWnd = (uintptr) handle; result.HWnd = (HWND)handle; return result; } Sleep(20); } return result; #endif } void SetTopMost(bool state){ // Check window validity if (!is_valid()) { return; } #if defined(IS_MACOSX) // WARNING: Unavailable #elif defined(USE_X11) // Ignore X errors // XDismissErrors(); // SetState(pub_mData.XWin, STATE_TOPMOST, state); #elif defined(IS_WINDOWS) SetWindowPos(pub_mData.HWnd, state ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); #endif } void close_main_window () { // Check if the window is valid if (!is_valid()) { return; } close_window_by_Id(pub_mData); } void close_window_by_PId(uintptr pid, int8_t isPid){ MData win = set_handle_pid(pid, isPid); close_window_by_Id(win); } // CloseWindow void close_window_by_Id(MData m_data){ // Check window validity if (!is_valid()) { return; } #if defined(IS_MACOSX) AXUIElementRef b = NULL; // Retrieve the close button of this window if (AXUIElementCopyAttributeValue(m_data.AxID, kAXCloseButtonAttribute, (CFTypeRef*) &b) == kAXErrorSuccess && b != NULL) { // Simulate button press on the close button AXUIElementPerformAction(b, kAXPressAction); CFRelease(b); } #elif defined(USE_X11) Display *rDisplay = XOpenDisplay(NULL); // Ignore X errors XDismissErrors(); // Close the window XDestroyWindow(rDisplay, m_data.XWin); XCloseDisplay(rDisplay); #elif defined(IS_WINDOWS) PostMessage(m_data.HWnd, WM_CLOSE, 0, 0); #endif } char* get_main_title(){ // Check if the window is valid if (!is_valid()) { return "is_valid failed."; } return get_title_by_hand(pub_mData); } char* get_title_by_pid(uintptr pid, int8_t isPid){ MData win = set_handle_pid(pid, isPid); return get_title_by_hand(win); } char* named(void *result) { char *name = (char*)calloc(strlen(result)+1, sizeof(char*)); char *rptr = (char*)result; char *nptr = name; while (*rptr) { *nptr = *rptr; nptr++; rptr++; } *nptr = '\0'; return name; } char* get_title_by_hand(MData m_data){ // Check if the window is valid if (!is_valid()) { return "is_valid failed."; } #if defined(IS_MACOSX) CFStringRef data = NULL; // Determine the current title of the window if (AXUIElementCopyAttributeValue(m_data.AxID, kAXTitleAttribute, (CFTypeRef*) &data) == kAXErrorSuccess && data != NULL) { char conv[512]; // Convert result to a C-String CFStringGetCString(data, conv, 512, kCFStringEncodingUTF8); CFRelease(data); char* s = (char*)calloc(100, sizeof(char*)); if (s) { strcpy(s, conv); } // return (char *)&conv; return s; } return ""; #elif defined(USE_X11) void* result; // Ignore X errors XDismissErrors(); // Get window title (UTF-8) result = GetWindowProperty(m_data, WM_NAME, NULL); // Check result value if (result != NULL) { // Convert result to a string char* name = named(result); XFree(result); if (name != NULL) { return name; } } // Get window title (ASCII) result = GetWindowProperty(m_data, XA_WM_NAME, NULL); // Check result value if (result != NULL) { // Convert result to a string char* name = named(result); XFree(result); return name; } return ""; #elif defined(IS_WINDOWS) if (GetWindowText(m_data.HWnd, m_data.Title, 512) > 0){ char* name = m_data.Title; char* str = (char*)calloc(100, sizeof(char*)); if (str) { strcpy(str, name); } return str; } return ""; #endif } int32_t get_PID(void) { // Check window validity if (!is_valid()) { return 0; } #if defined(IS_MACOSX) pid_t pid = 0; // Attempt to retrieve the window pid if (AXUIElementGetPid(pub_mData.AxID, &pid)== kAXErrorSuccess) { return pid; } return 0; #elif defined(USE_X11) // Ignore X errors XDismissErrors(); // Get the window PID long* result = (long*)GetWindowProperty(pub_mData, WM_PID,NULL); // Check result and convert it if (result == NULL) { return 0; } int32_t pid = (int32_t) *result; XFree(result); return pid; #elif defined(IS_WINDOWS) DWORD id = 0; GetWindowThreadProcessId(pub_mData.HWnd, &id); return id; #endif } ================================================ FILE: windows_n.go ================================================ // +bulid windows,next package robotgo