├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── doc └── intro.md ├── images └── theme.gif ├── project.clj ├── robot.iml ├── src └── robot │ └── core.clj └── test └── robot └── core_test.clj /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | profiles.clj 5 | pom.xml 6 | pom.xml.asc 7 | *.jar 8 | *.class 9 | /.lein-* 10 | /.nrepl-port 11 | .hgignore 12 | .hg/ 13 | .idea 14 | robot.iml 15 | 16 | # Lint 17 | .clj-kondo/* 18 | !.clj-kondo/config.edn 19 | .calva/output-window/ 20 | .lsp/.cache/ 21 | .lsp/ 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). 3 | 4 | ## [Unreleased] 5 | ### Changed 6 | - Add a new arity to `make-widget-async` to provide a different widget shape. 7 | 8 | ## [0.1.1] - 2020-07-14 9 | ### Changed 10 | - Documentation on how to make the widgets. 11 | 12 | ### Removed 13 | - `make-widget-sync` - we're all async, all the time. 14 | 15 | ### Fixed 16 | - Fixed widget maker to keep working when daylight savings switches over. 17 | 18 | ## 0.1.0 - 2020-07-14 19 | ### Added 20 | - Files from the new template. 21 | - Widget maker public API - `make-widget-sync`. 22 | 23 | [Unreleased]: https://github.com/your-name/robot/compare/0.1.1...HEAD 24 | [0.1.1]: https://github.com/your-name/robot/compare/0.1.0...0.1.1 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 2.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE 4 | PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION 5 | OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a) in the case of the initial Contributor, the initial content 12 | Distributed under this Agreement, and 13 | 14 | b) in the case of each subsequent Contributor: 15 | i) changes to the Program, and 16 | ii) additions to the Program; 17 | where such changes and/or additions to the Program originate from 18 | and are Distributed by that particular Contributor. A Contribution 19 | "originates" from a Contributor if it was added to the Program by 20 | such Contributor itself or anyone acting on such Contributor's behalf. 21 | Contributions do not include changes or additions to the Program that 22 | are not Modified Works. 23 | 24 | "Contributor" means any person or entity that Distributes the Program. 25 | 26 | "Licensed Patents" mean patent claims licensable by a Contributor which 27 | are necessarily infringed by the use or sale of its Contribution alone 28 | or when combined with the Program. 29 | 30 | "Program" means the Contributions Distributed in accordance with this 31 | Agreement. 32 | 33 | "Recipient" means anyone who receives the Program under this Agreement 34 | or any Secondary License (as applicable), including Contributors. 35 | 36 | "Derivative Works" shall mean any work, whether in Source Code or other 37 | form, that is based on (or derived from) the Program and for which the 38 | editorial revisions, annotations, elaborations, or other modifications 39 | represent, as a whole, an original work of authorship. 40 | 41 | "Modified Works" shall mean any work in Source Code or other form that 42 | results from an addition to, deletion from, or modification of the 43 | contents of the Program, including, for purposes of clarity any new file 44 | in Source Code form that contains any contents of the Program. Modified 45 | Works shall not include works that contain only declarations, 46 | interfaces, types, classes, structures, or files of the Program solely 47 | in each case in order to link to, bind by name, or subclass the Program 48 | or Modified Works thereof. 49 | 50 | "Distribute" means the acts of a) distributing or b) making available 51 | in any manner that enables the transfer of a copy. 52 | 53 | "Source Code" means the form of a Program preferred for making 54 | modifications, including but not limited to software source code, 55 | documentation source, and configuration files. 56 | 57 | "Secondary License" means either the GNU General Public License, 58 | Version 2.0, or any later versions of that license, including any 59 | exceptions or additional permissions as identified by the initial 60 | Contributor. 61 | 62 | 2. GRANT OF RIGHTS 63 | 64 | a) Subject to the terms of this Agreement, each Contributor hereby 65 | grants Recipient a non-exclusive, worldwide, royalty-free copyright 66 | license to reproduce, prepare Derivative Works of, publicly display, 67 | publicly perform, Distribute and sublicense the Contribution of such 68 | Contributor, if any, and such Derivative Works. 69 | 70 | b) Subject to the terms of this Agreement, each Contributor hereby 71 | grants Recipient a non-exclusive, worldwide, royalty-free patent 72 | license under Licensed Patents to make, use, sell, offer to sell, 73 | import and otherwise transfer the Contribution of such Contributor, 74 | if any, in Source Code or other form. This patent license shall 75 | apply to the combination of the Contribution and the Program if, at 76 | the time the Contribution is added by the Contributor, such addition 77 | of the Contribution causes such combination to be covered by the 78 | Licensed Patents. The patent license shall not apply to any other 79 | combinations which include the Contribution. No hardware per se is 80 | licensed hereunder. 81 | 82 | c) Recipient understands that although each Contributor grants the 83 | licenses to its Contributions set forth herein, no assurances are 84 | provided by any Contributor that the Program does not infringe the 85 | patent or other intellectual property rights of any other entity. 86 | Each Contributor disclaims any liability to Recipient for claims 87 | brought by any other entity based on infringement of intellectual 88 | property rights or otherwise. As a condition to exercising the 89 | rights and licenses granted hereunder, each Recipient hereby 90 | assumes sole responsibility to secure any other intellectual 91 | property rights needed, if any. For example, if a third party 92 | patent license is required to allow Recipient to Distribute the 93 | Program, it is Recipient's responsibility to acquire that license 94 | before distributing the Program. 95 | 96 | d) Each Contributor represents that to its knowledge it has 97 | sufficient copyright rights in its Contribution, if any, to grant 98 | the copyright license set forth in this Agreement. 99 | 100 | e) Notwithstanding the terms of any Secondary License, no 101 | Contributor makes additional grants to any Recipient (other than 102 | those set forth in this Agreement) as a result of such Recipient's 103 | receipt of the Program under the terms of a Secondary License 104 | (if permitted under the terms of Section 3). 105 | 106 | 3. REQUIREMENTS 107 | 108 | 3.1 If a Contributor Distributes the Program in any form, then: 109 | 110 | a) the Program must also be made available as Source Code, in 111 | accordance with section 3.2, and the Contributor must accompany 112 | the Program with a statement that the Source Code for the Program 113 | is available under this Agreement, and informs Recipients how to 114 | obtain it in a reasonable manner on or through a medium customarily 115 | used for software exchange; and 116 | 117 | b) the Contributor may Distribute the Program under a license 118 | different than this Agreement, provided that such license: 119 | i) effectively disclaims on behalf of all other Contributors all 120 | warranties and conditions, express and implied, including 121 | warranties or conditions of title and non-infringement, and 122 | implied warranties or conditions of merchantability and fitness 123 | for a particular purpose; 124 | 125 | ii) effectively excludes on behalf of all other Contributors all 126 | liability for damages, including direct, indirect, special, 127 | incidental and consequential damages, such as lost profits; 128 | 129 | iii) does not attempt to limit or alter the recipients' rights 130 | in the Source Code under section 3.2; and 131 | 132 | iv) requires any subsequent distribution of the Program by any 133 | party to be under a license that satisfies the requirements 134 | of this section 3. 135 | 136 | 3.2 When the Program is Distributed as Source Code: 137 | 138 | a) it must be made available under this Agreement, or if the 139 | Program (i) is combined with other material in a separate file or 140 | files made available under a Secondary License, and (ii) the initial 141 | Contributor attached to the Source Code the notice described in 142 | Exhibit A of this Agreement, then the Program may be made available 143 | under the terms of such Secondary Licenses, and 144 | 145 | b) a copy of this Agreement must be included with each copy of 146 | the Program. 147 | 148 | 3.3 Contributors may not remove or alter any copyright, patent, 149 | trademark, attribution notices, disclaimers of warranty, or limitations 150 | of liability ("notices") contained within the Program from any copy of 151 | the Program which they Distribute, provided that Contributors may add 152 | their own appropriate notices. 153 | 154 | 4. COMMERCIAL DISTRIBUTION 155 | 156 | Commercial distributors of software may accept certain responsibilities 157 | with respect to end users, business partners and the like. While this 158 | license is intended to facilitate the commercial use of the Program, 159 | the Contributor who includes the Program in a commercial product 160 | offering should do so in a manner which does not create potential 161 | liability for other Contributors. Therefore, if a Contributor includes 162 | the Program in a commercial product offering, such Contributor 163 | ("Commercial Contributor") hereby agrees to defend and indemnify every 164 | other Contributor ("Indemnified Contributor") against any losses, 165 | damages and costs (collectively "Losses") arising from claims, lawsuits 166 | and other legal actions brought by a third party against the Indemnified 167 | Contributor to the extent caused by the acts or omissions of such 168 | Commercial Contributor in connection with its distribution of the Program 169 | in a commercial product offering. The obligations in this section do not 170 | apply to any claims or Losses relating to any actual or alleged 171 | intellectual property infringement. In order to qualify, an Indemnified 172 | Contributor must: a) promptly notify the Commercial Contributor in 173 | writing of such claim, and b) allow the Commercial Contributor to control, 174 | and cooperate with the Commercial Contributor in, the defense and any 175 | related settlement negotiations. The Indemnified Contributor may 176 | participate in any such claim at its own expense. 177 | 178 | For example, a Contributor might include the Program in a commercial 179 | product offering, Product X. That Contributor is then a Commercial 180 | Contributor. If that Commercial Contributor then makes performance 181 | claims, or offers warranties related to Product X, those performance 182 | claims and warranties are such Commercial Contributor's responsibility 183 | alone. Under this section, the Commercial Contributor would have to 184 | defend claims against the other Contributors related to those performance 185 | claims and warranties, and if a court requires any other Contributor to 186 | pay any damages as a result, the Commercial Contributor must pay 187 | those damages. 188 | 189 | 5. NO WARRANTY 190 | 191 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT 192 | PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS" 193 | BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 194 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF 195 | TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR 196 | PURPOSE. Each Recipient is solely responsible for determining the 197 | appropriateness of using and distributing the Program and assumes all 198 | risks associated with its exercise of rights under this Agreement, 199 | including but not limited to the risks and costs of program errors, 200 | compliance with applicable laws, damage to or loss of data, programs 201 | or equipment, and unavailability or interruption of operations. 202 | 203 | 6. DISCLAIMER OF LIABILITY 204 | 205 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT 206 | PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS 207 | SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 208 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST 209 | PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 210 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 211 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 212 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE 213 | POSSIBILITY OF SUCH DAMAGES. 214 | 215 | 7. GENERAL 216 | 217 | If any provision of this Agreement is invalid or unenforceable under 218 | applicable law, it shall not affect the validity or enforceability of 219 | the remainder of the terms of this Agreement, and without further 220 | action by the parties hereto, such provision shall be reformed to the 221 | minimum extent necessary to make such provision valid and enforceable. 222 | 223 | If Recipient institutes patent litigation against any entity 224 | (including a cross-claim or counterclaim in a lawsuit) alleging that the 225 | Program itself (excluding combinations of the Program with other software 226 | or hardware) infringes such Recipient's patent(s), then such Recipient's 227 | rights granted under Section 2(b) shall terminate as of the date such 228 | litigation is filed. 229 | 230 | All Recipient's rights under this Agreement shall terminate if it 231 | fails to comply with any of the material terms or conditions of this 232 | Agreement and does not cure such failure in a reasonable period of 233 | time after becoming aware of such noncompliance. If all Recipient's 234 | rights under this Agreement terminate, Recipient agrees to cease use 235 | and distribution of the Program as soon as reasonably practicable. 236 | However, Recipient's obligations under this Agreement and any licenses 237 | granted by Recipient relating to the Program shall continue and survive. 238 | 239 | Everyone is permitted to copy and distribute copies of this Agreement, 240 | but in order to avoid inconsistency the Agreement is copyrighted and 241 | may only be modified in the following manner. The Agreement Steward 242 | reserves the right to publish new versions (including revisions) of 243 | this Agreement from time to time. No one other than the Agreement 244 | Steward has the right to modify this Agreement. The Eclipse Foundation 245 | is the initial Agreement Steward. The Eclipse Foundation may assign the 246 | responsibility to serve as the Agreement Steward to a suitable separate 247 | entity. Each new version of the Agreement will be given a distinguishing 248 | version number. The Program (including Contributions) may always be 249 | Distributed subject to the version of the Agreement under which it was 250 | received. In addition, after a new version of the Agreement is published, 251 | Contributor may elect to Distribute the Program (including its 252 | Contributions) under the new version. 253 | 254 | Except as expressly stated in Sections 2(a) and 2(b) above, Recipient 255 | receives no rights or licenses to the intellectual property of any 256 | Contributor under this Agreement, whether expressly, by implication, 257 | estoppel or otherwise. All rights in the Program not expressly granted 258 | under this Agreement are reserved. Nothing in this Agreement is intended 259 | to be enforceable by any entity that is not a Contributor or Recipient. 260 | No third-party beneficiary rights are created under this Agreement. 261 | 262 | Exhibit A - Form of Secondary Licenses Notice 263 | 264 | "This Source Code may also be made available under the following 265 | Secondary Licenses when the conditions for such availability set forth 266 | in the Eclipse Public License, v. 2.0 are satisfied: GNU General Public 267 | License as published by the Free Software Foundation, either version 2 268 | of the License, or (at your option) any later version, with the GNU 269 | Classpath Exception which is available at 270 | https://www.gnu.org/software/classpath/license.html." 271 | 272 | Simply including a copy of this Agreement, including this Exhibit A 273 | is not sufficient to license the Source Code under Secondary Licenses. 274 | 275 | If it is not possible or desirable to put the notice in a particular 276 | file, then You may include the notice in a location (such as a LICENSE 277 | file in a relevant directory) where a recipient would be likely to 278 | look for such a notice. 279 | 280 | You may add additional accurate notices of copyright ownership. 281 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # robot 2 | 3 | A Clojure library designed to simplify using java.awt for handling desktop manipulation commands. 4 | For example, I use robot to change theme in apps: 5 | 6 | [![Clojars Project](https://img.shields.io/clojars/v/robot.svg)](https://clojars.org/robot) 7 | 8 | alt text 9 | 10 | ### Launching apps 11 | 12 | ```clj 13 | ;; Open google.com in default browser 14 | (r/launch-uri! "https://google.com") 15 | 16 | ;; Open default email app 17 | (r/launch-uri! "mailto:arturdumchev@gmail.com") 18 | ``` 19 | 20 | ### Keyboard 21 | ```clojure 22 | (require '[robot.core :as r]) 23 | 24 | ;; simulate pressing a single key 25 | (r/type! :shift) 26 | 27 | ;; simulate pressing hot keys 28 | (r/hot-keys! [:cmd :space]) 29 | 30 | ;; type whole text 31 | (r/type-text! "typing this letters") 32 | ``` 33 | 34 | ### Mouse 35 | ```clojure 36 | ;; move cursor to position 37 | (r/mouse-move! 280 1200) 38 | 39 | ;; getting mouse position 40 | (r/mouse-pos) ;; => [280 1200] 41 | 42 | ;; simulate mouse click 43 | (r/mouse-click!) 44 | 45 | ;; simulate mouse wheel 46 | (r/scroll! 10) 47 | ``` 48 | 49 | ### Clipboard 50 | ```clojure 51 | ;; put into clipboard 52 | (r/clipboard-put! "text to put in clipboard") 53 | 54 | ;; get from clipboard 55 | (r/clipboard-get-string) ;; => text that was in the clipboard 56 | ``` 57 | 58 | ### Delay 59 | ```clojure 60 | ;; you can delay, which will use thread/sleep under the hood 61 | (r/sleep 50) 62 | 63 | ;; you can also pass delays inside typing functions 64 | (r/type! :k 50) ;; passing millis between press and release 65 | 66 | ;; the same with mouse 67 | (r/mouse-click! 100) 68 | ``` 69 | 70 | ### Pixel colors 71 | 72 | ```clojure 73 | (def x 280) 74 | (def y 1200) 75 | (def width 60) 76 | 77 | ;; get pixel color as argb integer 78 | (pixel-argb-int x y) ;=> -16777216 79 | 80 | (r/int->argb -16777216) ;=> {:alpha 255, :red 0, :green 0, :blue 0} 81 | (pixel-argb x y) ;=> {:alpha 255, :red 0, :green 0, :blue 0} 82 | 83 | ;; get pixel color as java.awt.Color 84 | (pixel-color x y) 85 | 86 | ;; get list of pixel corols as {:keys [alpha red green blue]} 87 | (r/pixel-rgb-range-hor x y width r/int->argb) 88 | 89 | (r/pixel-rgb-range x y 3 2) ;=> ((-11332 -11332 -11332) (-11332 -11332 -11332)) 90 | ``` 91 | 92 | ### Java interop 93 | ```clojure 94 | (import java.awt.event.KeyEvent) 95 | 96 | (r/type! KeyEvent/VK_A) 97 | 98 | (r/hot-keys! [KeyEvent/VK_ALT KeyEvent/VK_SPACE]) 99 | ``` 100 | 101 | ## Finding particular key 102 | There is a function that returns a map with key-codes to key-names: 103 | ```clojure 104 | (r/get-my-keyboard) 105 | ;; => 106 | {3 "⎋", 8 "⌫", 9 "⇥", 10 "⏎", 12 "⌧", 16 "⇧", 17 "⌃", 18 "⌥" ... } 107 | ```` 108 | There is also a function to get the name of the particular key-code: 109 | ```clojure 110 | (r/get-key-name 61440) 111 | ;; => "F13" 112 | ``` 113 | 114 | ## Examples 115 | 116 | - [Article](https://github.com/D00mch/working-script) about writing a clojure script to open docker and two terminal windows 117 | - [Script](https://github.com/Liverm0r/dotfiles/blob/master/clj_scripts/trutenko/src/trutenko/core.clj) to notify developer about standup in Slack 118 | - [Script](https://github.com/Liverm0r/dotfiles/blob/master/clj_scripts/theme/src/theme/core.clj) to change theme (vim, emacs, chrome, slack, osx, telegram) 119 | 120 | ## License 121 | 122 | Copyright © 2020 Artur Dumchev 123 | 124 | This program and the accompanying materials are made available under the 125 | terms of the Eclipse Public License 2.0 which is available at 126 | http://www.eclipse.org/legal/epl-2.0. 127 | 128 | This Source Code may also be made available under the following Secondary 129 | Licenses when the conditions for such availability set forth in the Eclipse 130 | Public License, v. 2.0 are satisfied: GNU General Public License as published by 131 | the Free Software Foundation, either version 2 of the License, or (at your 132 | option) any later version, with the GNU Classpath Exception which is available 133 | at https://www.gnu.org/software/classpath/license.html. 134 | -------------------------------------------------------------------------------- /doc/intro.md: -------------------------------------------------------------------------------- 1 | # robot 2 | 3 | A Clojure library designed to simplify using java.awt for handling desktop manipulation commands. 4 | For example, I use robot to change theme in apps: 5 | 6 | alt text 7 | 8 | ### Keyboard 9 | ```clojure 10 | (require '[robot.core :as r]) 11 | 12 | ;; simulate pressing a single key 13 | (r/type! :shift) 14 | 15 | ;; simulate pressing hot keys 16 | (r/hot-keys! [:cmd :space]) 17 | 18 | ;; type whole text 19 | (r/type-text! "typing this letters") 20 | ``` 21 | 22 | ### Mouse 23 | ```clojure 24 | ;; move cursor to position 25 | (r/mouse-move! 280 1200) 26 | 27 | ;; getting mouse position 28 | (r/mouse-pos) ;; => [280 1200] 29 | 30 | ;; simulate mouse click 31 | (r/mouse-click!) 32 | 33 | ;; simulate mouse wheel 34 | (r/scroll! 10) 35 | 36 | ;; get pixel color at position 37 | (pixel-color 280 1200) 38 | ``` 39 | 40 | ### Clipboard 41 | ```clojure 42 | ;; put into clipboard 43 | (r/clipboard-put! "text to put in clipboard") 44 | 45 | ;; get from clipboard 46 | (r/clipboard-get-string) ;; => text to put in clipboard 47 | ``` 48 | 49 | ### Delay 50 | ```clojure 51 | ;; you can delay, which will use thread/sleep under the hood 52 | (r/sleep 50) 53 | 54 | ;; you can also pass delays inside typing functions 55 | (r/type! :k 50) ;; passing millis between press and release 56 | 57 | ;; same with mouse 58 | (r/mouse-click! 100) 59 | ``` 60 | 61 | ## Examples 62 | 63 | [Script](https://github.com/Liverm0r/dotfiles/blob/master/clj_scripts/trutenko/src/trutenko/core.clj) to notify developer about standup in Slack 64 | [Script](https://github.com/Liverm0r/dotfiles/blob/master/clj_scripts/theme/src/theme/core.clj) to change theme (vim, emacs, chrome, slack, osx, telegram) 65 | -------------------------------------------------------------------------------- /images/theme.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/D00mch/robot/3f92bae14f00cef379d7d5a1813d4c3b54e9644d/images/theme.gif -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject robot "0.3.5-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0" 5 | :url "https://www.eclipse.org/legal/epl-2.0/"} 6 | :dependencies [[org.clojure/clojure "1.11.1"]] 7 | :repl-options {:init-ns robot.core}) 8 | -------------------------------------------------------------------------------- /robot.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/robot/core.clj: -------------------------------------------------------------------------------- 1 | (ns robot.core 2 | (:gen-class) 3 | (:require 4 | [clojure.java.shell :use [sh]] 5 | [clojure.string :as str]) 6 | (:import 7 | (java.awt 8 | Desktop 9 | MouseInfo 10 | Rectangle 11 | Robot 12 | Toolkit) 13 | (java.awt.datatransfer 14 | Clipboard 15 | DataFlavor 16 | StringSelection 17 | Transferable) 18 | (java.awt.event InputEvent KeyEvent) 19 | (java.awt.im InputContext) 20 | [java.net URI])) 21 | 22 | (comment 23 | (set! *warn-on-reflection* true)) 24 | 25 | (def ^{:private true} key-events-map 26 | {:a KeyEvent/VK_A :b KeyEvent/VK_B :c KeyEvent/VK_C :d KeyEvent/VK_D :e KeyEvent/VK_E 27 | :f KeyEvent/VK_F :g KeyEvent/VK_G :h KeyEvent/VK_H :i KeyEvent/VK_I :j KeyEvent/VK_J 28 | :k KeyEvent/VK_K :l KeyEvent/VK_L :m KeyEvent/VK_M :n KeyEvent/VK_N :o KeyEvent/VK_O 29 | :p KeyEvent/VK_P :q KeyEvent/VK_Q :r KeyEvent/VK_R :s KeyEvent/VK_S :t KeyEvent/VK_T 30 | :u KeyEvent/VK_U :v KeyEvent/VK_V :w KeyEvent/VK_W :x KeyEvent/VK_X :y KeyEvent/VK_Y 31 | :z KeyEvent/VK_Z 32 | :1 KeyEvent/VK_1 :2 KeyEvent/VK_2 :3 KeyEvent/VK_3 :4 KeyEvent/VK_4 :5 KeyEvent/VK_5 33 | :6 KeyEvent/VK_6 :7 KeyEvent/VK_7 :8 KeyEvent/VK_8 :9 KeyEvent/VK_9 :0 KeyEvent/VK_0 34 | :cmd KeyEvent/VK_META :meta KeyEvent/VK_META 35 | :shift KeyEvent/VK_SHIFT 36 | :alt KeyEvent/VK_ALT 37 | :esc KeyEvent/VK_ESCAPE 38 | :enter KeyEvent/VK_ENTER 39 | :back KeyEvent/VK_BACK_SPACE :backspace KeyEvent/VK_BACK_SPACE 40 | :bq KeyEvent/VK_BACK_QUOTE ; back quote 41 | :quote KeyEvent/VK_QUOTE 42 | :tab KeyEvent/VK_TAB 43 | :caps KeyEvent/VK_CAPS_LOCK 44 | :ctrl KeyEvent/VK_CONTROL 45 | :space KeyEvent/VK_SPACE 46 | :win KeyEvent/VK_WINDOWS 47 | :page-down KeyEvent/VK_PAGE_DOWN 48 | :page-up KeyEvent/VK_PAGE_UP 49 | :home KeyEvent/VK_HOME 50 | :end KeyEvent/VK_END 51 | :f1 KeyEvent/VK_F1 :f2 KeyEvent/VK_F2 :f3 KeyEvent/VK_F3 :f4 KeyEvent/VK_F4 52 | :f5 KeyEvent/VK_F5 :f6 KeyEvent/VK_F6 :f7 KeyEvent/VK_F7 :f8 KeyEvent/VK_F8 53 | :f9 KeyEvent/VK_F9 :f10 KeyEvent/VK_F10 :f11 KeyEvent/VK_F11 :f12 KeyEvent/VK_F12 54 | :left KeyEvent/VK_LEFT :right KeyEvent/VK_R :up KeyEvent/VK_UP :down KeyEvent/VK_DOWN}) 55 | 56 | ;; KEYBOARD-API 57 | (def ^Robot robot (Robot.)) 58 | 59 | (defn sleep [millis] 60 | (.delay robot millis)) 61 | 62 | (defn- keys->key-events [keys] 63 | (map #(% key-events-map) keys)) 64 | 65 | (defn type! [key & [delay]] 66 | (let [key (if (number? key) key (key key-events-map))] 67 | (doto robot 68 | (.keyPress key) 69 | (.delay (or delay 40)) 70 | (.keyRelease key)))) 71 | 72 | (defn hot-keys! "takes seq of ints (KeyEvent) or :keys" 73 | [keys & [delay-between-press delay-before-release]] 74 | (let [keys (if (number? (first keys)) keys (keys->key-events keys))] 75 | (doseq [key keys] 76 | (doto robot 77 | (.keyPress key) 78 | (.delay (or delay-between-press 10)))) 79 | (.delay robot (or delay-before-release 100)) 80 | (doseq [key (reverse keys)] (.keyRelease robot key)))) 81 | 82 | (defn type-text! [^String s & [delay-before-press delay-before-release]] 83 | (doseq [byte (.getBytes s) 84 | :let [code (int byte) 85 | code (cond 86 | (< 96 code 123) (- code 32) 87 | :else code)]] 88 | (case code 89 | 33 (hot-keys! [KeyEvent/VK_SHIFT KeyEvent/VK_1]) 90 | 34 (hot-keys! [KeyEvent/VK_SHIFT KeyEvent/VK_QUOTE]) 91 | 35 (hot-keys! [KeyEvent/VK_SHIFT KeyEvent/VK_3]) 92 | 36 (hot-keys! [KeyEvent/VK_SHIFT KeyEvent/VK_4]) 93 | 37 (hot-keys! [KeyEvent/VK_SHIFT KeyEvent/VK_5]) 94 | 38 (hot-keys! [KeyEvent/VK_SHIFT KeyEvent/VK_7]) 95 | 39 (type! KeyEvent/VK_QUOTE) 96 | 40 (hot-keys! [KeyEvent/VK_SHIFT KeyEvent/VK_9]) 97 | 41 (hot-keys! [KeyEvent/VK_SHIFT KeyEvent/VK_0]) 98 | 42 (hot-keys! [KeyEvent/VK_SHIFT KeyEvent/VK_8]) 99 | 91 (type! KeyEvent/VK_OPEN_BRACKET) 100 | 93 (type! KeyEvent/VK_CLOSE_BRACKET) 101 | 94 (hot-keys! [KeyEvent/VK_SHIFT KeyEvent/VK_6]) 102 | 96 (type! KeyEvent/VK_BACK_QUOTE) 103 | 58 (hot-keys! [KeyEvent/VK_SHIFT KeyEvent/VK_SEMICOLON]) 104 | 64 (hot-keys! [KeyEvent/VK_SHIFT KeyEvent/VK_2]) 105 | 123 (hot-keys! [KeyEvent/VK_SHIFT KeyEvent/VK_OPEN_BRACKET]) 106 | 125 (hot-keys! [KeyEvent/VK_SHIFT KeyEvent/VK_CLOSE_BRACKET]) 107 | 126 (hot-keys! [KeyEvent/VK_SHIFT KeyEvent/VK_BACK_QUOTE]) 108 | (doto robot 109 | (.delay (or delay-before-press 70)) 110 | (.keyPress code) 111 | (.delay (or delay-before-release 0)) 112 | (.keyRelease code))))) 113 | 114 | ;; INFO 115 | 116 | (defn get-key-name [i] 117 | (KeyEvent/getKeyText i)) 118 | 119 | (defn get-my-keyboard [] 120 | (into (sorted-map) 121 | (for [i (range 100000) 122 | :let [text (get-key-name i)] 123 | :when (not (.contains ^String text "Unknown keyCode: "))] 124 | [i text]))) 125 | 126 | (defn get-screen-size [] 127 | (let [screen (.. Toolkit getDefaultToolkit getScreenSize)] 128 | [(.width screen) 129 | (.height screen)])) 130 | 131 | (defn get-current-layout [] 132 | (.getLocale (InputContext/getInstance))) 133 | 134 | ;; MOUSE 135 | 136 | (defn mouse-click! 137 | "mouse-button: #{:mouse1, :mouse2, :mouse3}" 138 | ([] 139 | (mouse-click! :mouse1)) 140 | ([mouse-button & [delay]] 141 | (let [btn (case mouse-button 142 | :mouse1 InputEvent/BUTTON1_DOWN_MASK 143 | :mouse2 InputEvent/BUTTON2_DOWN_MASK 144 | :mouse3 InputEvent/BUTTON3_DOWN_MASK)] 145 | (doto robot 146 | (.mousePress btn) 147 | (.delay (or delay 70)) 148 | (.mouseRelease btn))))) 149 | 150 | (defn mouse-pos "returns mouse position [x, y]" [] 151 | (let [mouse-info (.. MouseInfo getPointerInfo getLocation)] 152 | [(. mouse-info x) (. mouse-info y)])) 153 | 154 | (def get-mouse-pos mouse-pos) 155 | 156 | (defn mouse-move! 157 | ([[x y]] (mouse-move! x y)) 158 | ([x y] (.mouseMove robot x y))) 159 | 160 | (defn scroll! [i] (.mouseWheel ^Robot robot i)) 161 | 162 | ;; COLORS 163 | 164 | (defn pixel-color 165 | "Don't use this function in loops, it's slow. 166 | If you need range, use `pixel-rgb-range`" 167 | ([] 168 | (pixel-color (mouse-pos))) 169 | ([[x y]] 170 | (pixel-color x y)) 171 | ([x y] 172 | (.getPixelColor robot x y))) 173 | 174 | (defn pixel-argb-int 175 | "Don't use this function in loops, it's slow. 176 | If you need range, use `pixel-rgb-range(-ver|-hor)`" 177 | [x y] 178 | (.getRGB (.createScreenCapture robot (Rectangle. x y 1 1)) 0 0)) 179 | 180 | (defrecord ARGB [alpha red green blue]) 181 | 182 | (defn int->argb 183 | "Get integer argb, retuns ARGB record {:keys [apha red green blue]} 184 | Don't use in loops, consider `pixel-rgb-range(-ver|-hor) ` instead." 185 | [color-int] 186 | (->ARGB (bit-and (bit-shift-right color-int 24) 255) 187 | (bit-and (bit-shift-right color-int 16) 255) 188 | (bit-and (bit-shift-right color-int 8) 255) 189 | (bit-and color-int 255))) 190 | 191 | (defn pixel-argb 192 | "Don't use this function in loops, it's slow. 193 | If you need range, use `pixel-rgb-range(-ver|-hor)`" 194 | ([] 195 | (pixel-argb (mouse-pos))) 196 | ([[x y]] 197 | (pixel-argb x y)) 198 | ([x y] 199 | (int->argb (pixel-argb-int x y)))) 200 | 201 | (defn pixel-rgb-range 202 | "Pass `map-fn` to modify each pixel value (argb by default) " 203 | ([x y width height] 204 | (pixel-rgb-range x y width height identity)) 205 | ([x y width height map-fn] 206 | (let [rec (.createScreenCapture robot (Rectangle. x y width height))] 207 | (for [yn (range height)] 208 | (for [xn (range width)] 209 | (map-fn (.getRGB rec xn yn))))))) 210 | 211 | (defn pixel-rgb-range-hor 212 | "Horizontal range. See `pixel-rgb-range` docs" 213 | ([[x y] width] 214 | (pixel-rgb-range-hor x y width identity)) 215 | ([x y width] 216 | (pixel-rgb-range-hor x y width identity)) 217 | ([x y width map-fn] 218 | (first (pixel-rgb-range x y width 1 map-fn)))) 219 | 220 | (defn pixel-rgb-range-ver 221 | "Horizontal range. See `pixel-rgb-range` docs" 222 | ([[x y] height] 223 | (pixel-rgb-range-ver x y height identity)) 224 | ([x y height] 225 | (pixel-rgb-range-ver x y height identity)) 226 | ([x y height map-fn] 227 | (let [rec (.createScreenCapture robot (Rectangle. x y 1 height))] 228 | (for [n (range height)] 229 | (map-fn (.getRGB rec 0 n)))))) 230 | 231 | ;; CLIPBOARD 232 | 233 | (def ^Clipboard clipboard (.. Toolkit getDefaultToolkit getSystemClipboard)) 234 | 235 | (defn clipboard-put! [^String s] 236 | (try 237 | (.setContents clipboard (StringSelection. s) nil) 238 | (catch Exception e 239 | (sleep 20) 240 | (.setContents clipboard (StringSelection. s) nil)))) 241 | 242 | (defn clipboard-get-string 243 | "returns string from buffer or nil" 244 | [] 245 | (let [^Transferable content (try (.getContents clipboard nil) 246 | (catch Throwable e 247 | (sleep 20) 248 | (.getContents clipboard nil))) 249 | has-text (and (some? content) 250 | (.isDataFlavorSupported content DataFlavor/stringFlavor))] 251 | (when has-text 252 | (try 253 | (.getTransferData content DataFlavor/stringFlavor) 254 | (catch Exception e (.printStackTrace e)))))) 255 | 256 | ;; LAUNCH 257 | 258 | (defn launch-uri! [uri-str] 259 | (.browse (Desktop/getDesktop) (URI. uri-str))) 260 | -------------------------------------------------------------------------------- /test/robot/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns robot.core-test 2 | (:require [clojure.test :refer :all] 3 | [robot.core :refer :all])) 4 | 5 | (deftest a-test 6 | (testing "FIXME, I fail." 7 | (is (= 0 1)))) 8 | --------------------------------------------------------------------------------