├── .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 | [](https://clojars.org/robot)
7 |
8 |
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 |
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 |
--------------------------------------------------------------------------------