001package com.randomnoun.common;
002
003/* (c) 2013 randomnoun. All Rights Reserved. This work is licensed under a
004 * BSD Simplified License. (http://www.randomnoun.com/bsd-simplified.html)
005 */
006import static java.awt.event.KeyEvent.VK_0;
007import static java.awt.event.KeyEvent.VK_1;
008import static java.awt.event.KeyEvent.VK_2;
009import static java.awt.event.KeyEvent.VK_3;
010import static java.awt.event.KeyEvent.VK_4;
011import static java.awt.event.KeyEvent.VK_5;
012import static java.awt.event.KeyEvent.VK_6;
013import static java.awt.event.KeyEvent.VK_7;
014import static java.awt.event.KeyEvent.VK_8;
015import static java.awt.event.KeyEvent.VK_9;
016import static java.awt.event.KeyEvent.VK_A;
017import static java.awt.event.KeyEvent.VK_AMPERSAND;
018import static java.awt.event.KeyEvent.VK_ASTERISK;
019import static java.awt.event.KeyEvent.VK_AT;
020import static java.awt.event.KeyEvent.VK_B;
021import static java.awt.event.KeyEvent.VK_BACK_QUOTE;
022import static java.awt.event.KeyEvent.VK_BACK_SLASH;
023import static java.awt.event.KeyEvent.VK_C;
024import static java.awt.event.KeyEvent.VK_CIRCUMFLEX;
025import static java.awt.event.KeyEvent.VK_CLOSE_BRACKET;
026import static java.awt.event.KeyEvent.VK_COMMA;
027import static java.awt.event.KeyEvent.VK_D;
028import static java.awt.event.KeyEvent.VK_DOLLAR;
029import static java.awt.event.KeyEvent.VK_E;
030import static java.awt.event.KeyEvent.VK_ENTER;
031import static java.awt.event.KeyEvent.VK_EQUALS;
032import static java.awt.event.KeyEvent.VK_EXCLAMATION_MARK;
033import static java.awt.event.KeyEvent.VK_F;
034import static java.awt.event.KeyEvent.VK_G;
035import static java.awt.event.KeyEvent.VK_GREATER;
036import static java.awt.event.KeyEvent.VK_H;
037import static java.awt.event.KeyEvent.VK_I;
038import static java.awt.event.KeyEvent.VK_J;
039import static java.awt.event.KeyEvent.VK_K;
040import static java.awt.event.KeyEvent.VK_L;
041import static java.awt.event.KeyEvent.VK_LEFT_PARENTHESIS;
042import static java.awt.event.KeyEvent.VK_LESS;
043import static java.awt.event.KeyEvent.VK_M;
044import static java.awt.event.KeyEvent.VK_MINUS;
045import static java.awt.event.KeyEvent.VK_N;
046import static java.awt.event.KeyEvent.VK_NUMBER_SIGN;
047import static java.awt.event.KeyEvent.VK_O;
048import static java.awt.event.KeyEvent.VK_OPEN_BRACKET;
049import static java.awt.event.KeyEvent.VK_P;
050import static java.awt.event.KeyEvent.VK_PERIOD;
051import static java.awt.event.KeyEvent.VK_PLUS;
052import static java.awt.event.KeyEvent.VK_Q;
053import static java.awt.event.KeyEvent.VK_QUOTE;
054import static java.awt.event.KeyEvent.VK_QUOTEDBL;
055import static java.awt.event.KeyEvent.VK_R;
056import static java.awt.event.KeyEvent.VK_RIGHT_PARENTHESIS;
057import static java.awt.event.KeyEvent.VK_S;
058import static java.awt.event.KeyEvent.VK_SEMICOLON;
059import static java.awt.event.KeyEvent.VK_SHIFT;
060import static java.awt.event.KeyEvent.VK_SLASH;
061import static java.awt.event.KeyEvent.VK_SPACE;
062import static java.awt.event.KeyEvent.VK_T;
063import static java.awt.event.KeyEvent.VK_TAB;
064import static java.awt.event.KeyEvent.VK_U;
065import static java.awt.event.KeyEvent.VK_UNDERSCORE;
066import static java.awt.event.KeyEvent.VK_V;
067import static java.awt.event.KeyEvent.VK_W;
068import static java.awt.event.KeyEvent.VK_X;
069import static java.awt.event.KeyEvent.VK_Y;
070import static java.awt.event.KeyEvent.VK_Z;
071
072import java.awt.AWTException;
073import java.awt.Robot;
074
075/** Use the AWT Robot class to send strings to another application. 
076 * Based on the code at 
077 * <a href="http://stackoverflow.com/questions/1248510/convert-string-to-keyevents">http://stackoverflow.com/questions/1248510/convert-string-to-keyevents</a>
078 * 
079 * @author knoxg
080 */
081public class Keyboard {
082
083    /** Robot class responsible for creating virtual keystrokes */
084    private Robot robot;
085
086    /** The size of the 'keyboard buffer'. Once this many characters have been
087     * typed using the {@link #type(CharSequence)} method, an artificial delay is
088     * introduced to allow the program handling input to process keystrokes.
089     */
090    private int bufferSize = -1;
091    
092    /** Internal counter of number of characters typed so far */
093    private int delayChars = 0;
094    
095    /** Delay to introduce after {@link #bufferSize} characters have been typed, 
096     * in milliseconds */
097    private int delayMillis = 100;
098
099    /** Create a new Keyboard object */
100    public Keyboard() throws AWTException {
101        this.robot = new Robot();
102    }
103
104    /** Create a new Keyboard object, using the supplied AWT Robot class to create
105     * virtual keystrokes.
106     *  
107     * @param robot AWT Robot class to create virtual keystrokes.
108     */
109    public Keyboard(Robot robot) {
110        this.robot = robot;
111    }
112
113    /** Sets the size of the 'keyboard buffer'. Once this many characters have been
114     * typed using the {@link #type(CharSequence)} method, an artificial delay is
115     * introduced to allow the program handling input to process keystrokes.
116     * 
117     * @param bufferSize the size of the 'keyboard buffer'
118     */
119    public void setBufferSize(int bufferSize) {
120        this.bufferSize = bufferSize;
121    }
122
123    /** Sets the delay to introduce after {@link #bufferSize} characters have been typed, 
124     * in milliseconds 
125     *
126     * @param delayMillis delay in milliseconds
127     */
128    public void setDelayMillis(int delayMillis) {
129        this.delayMillis = delayMillis;
130    }
131
132    /** Resets the keyboard buffer; i.e. sets the internal count of number of characters
133     * typed to zero.
134     */
135    public void resetBuffer() {
136        this.delayChars = 0;
137    }
138
139    /** Type the supplied character sequence (typically a {@link java.lang.String}).
140     * An artificial delay will be introduced every bufferSize characters, of size
141     * delayMillis.
142     *  
143     * @param characters characters to type
144     */
145    public void type(CharSequence characters) {
146        int length = characters.length();
147        for (int i = 0; i < length; i++) {
148            char character = characters.charAt(i);
149            type(character);
150            delayChars++;
151            if (bufferSize>0 && delayChars % bufferSize==0) {
152                try { Thread.sleep(delayMillis); } catch (InterruptedException ie) { }
153            }
154        }
155    }
156
157    /** Type an individual character
158     * 
159     * @param character character to type
160     */
161    public void type(char character) {
162        switch (character) {
163                case 'a': doType(VK_A); break;
164                case 'b': doType(VK_B); break;
165                case 'c': doType(VK_C); break;
166                case 'd': doType(VK_D); break;
167                case 'e': doType(VK_E); break;
168                case 'f': doType(VK_F); break;
169                case 'g': doType(VK_G); break;
170                case 'h': doType(VK_H); break;
171                case 'i': doType(VK_I); break;
172                case 'j': doType(VK_J); break;
173                case 'k': doType(VK_K); break;
174                case 'l': doType(VK_L); break;
175                case 'm': doType(VK_M); break;
176                case 'n': doType(VK_N); break;
177                case 'o': doType(VK_O); break;
178                case 'p': doType(VK_P); break;
179                case 'q': doType(VK_Q); break;
180                case 'r': doType(VK_R); break;
181                case 's': doType(VK_S); break;
182                case 't': doType(VK_T); break;
183                case 'u': doType(VK_U); break;
184                case 'v': doType(VK_V); break;
185                case 'w': doType(VK_W); break;
186                case 'x': doType(VK_X); break;
187                case 'y': doType(VK_Y); break;
188                case 'z': doType(VK_Z); break;
189                case 'A': doType(VK_SHIFT, VK_A); break;
190                case 'B': doType(VK_SHIFT, VK_B); break;
191                case 'C': doType(VK_SHIFT, VK_C); break;
192                case 'D': doType(VK_SHIFT, VK_D); break;
193                case 'E': doType(VK_SHIFT, VK_E); break;
194                case 'F': doType(VK_SHIFT, VK_F); break;
195                case 'G': doType(VK_SHIFT, VK_G); break;
196                case 'H': doType(VK_SHIFT, VK_H); break;
197                case 'I': doType(VK_SHIFT, VK_I); break;
198                case 'J': doType(VK_SHIFT, VK_J); break;
199                case 'K': doType(VK_SHIFT, VK_K); break;
200                case 'L': doType(VK_SHIFT, VK_L); break;
201                case 'M': doType(VK_SHIFT, VK_M); break;
202                case 'N': doType(VK_SHIFT, VK_N); break;
203                case 'O': doType(VK_SHIFT, VK_O); break;
204                case 'P': doType(VK_SHIFT, VK_P); break;
205                case 'Q': doType(VK_SHIFT, VK_Q); break;
206                case 'R': doType(VK_SHIFT, VK_R); break;
207                case 'S': doType(VK_SHIFT, VK_S); break;
208                case 'T': doType(VK_SHIFT, VK_T); break;
209                case 'U': doType(VK_SHIFT, VK_U); break;
210                case 'V': doType(VK_SHIFT, VK_V); break;
211                case 'W': doType(VK_SHIFT, VK_W); break;
212                case 'X': doType(VK_SHIFT, VK_X); break;
213                case 'Y': doType(VK_SHIFT, VK_Y); break;
214                case 'Z': doType(VK_SHIFT, VK_Z); break;
215                case '`': doType(VK_BACK_QUOTE); break;
216                case '0': doType(VK_0); break;
217                case '1': doType(VK_1); break;
218                case '2': doType(VK_2); break;
219                case '3': doType(VK_3); break;
220                case '4': doType(VK_4); break;
221                case '5': doType(VK_5); break;
222                case '6': doType(VK_6); break;
223                case '7': doType(VK_7); break;
224                case '8': doType(VK_8); break;
225                case '9': doType(VK_9); break;
226                case '-': doType(VK_MINUS); break;
227                case '=': doType(VK_EQUALS); break;
228                case '~': doType(VK_SHIFT, VK_BACK_QUOTE); break;
229                case '!': doType(VK_EXCLAMATION_MARK); break;
230                case '@': doType(VK_AT); break;
231                case '#': doType(VK_NUMBER_SIGN); break;
232                case '$': doType(VK_DOLLAR); break;
233                case '%': doType(VK_SHIFT, VK_5); break;
234                case '^': doType(VK_CIRCUMFLEX); break;
235                case '&': doType(VK_AMPERSAND); break;
236                case '*': doType(VK_ASTERISK); break;
237                case '(': doType(VK_LEFT_PARENTHESIS); break;
238                case ')': doType(VK_RIGHT_PARENTHESIS); break;
239                case '_': doType(VK_UNDERSCORE); break;
240                case '+': doType(VK_PLUS); break;
241                case '\t': doType(VK_TAB); break;
242                case '\n': doType(VK_ENTER); break;
243                case '[': doType(VK_OPEN_BRACKET); break;
244                case ']': doType(VK_CLOSE_BRACKET); break;
245                case '\\': doType(VK_BACK_SLASH); break;
246                case '{': doType(VK_SHIFT, VK_OPEN_BRACKET); break;
247                case '}': doType(VK_SHIFT, VK_CLOSE_BRACKET); break;
248                case '|': doType(VK_SHIFT, VK_BACK_SLASH); break;
249                case ';': doType(VK_SEMICOLON); break;
250                // case ':': doType(VK_COLON); break;
251                case ':': doType(VK_SHIFT, VK_SEMICOLON); break;
252                case '\'': doType(VK_QUOTE); break;
253                case '"': doType(VK_QUOTEDBL); break;
254                case ',': doType(VK_COMMA); break;
255                case '<': doType(VK_LESS); break;
256                case '.': doType(VK_PERIOD); break;
257                case '>': doType(VK_GREATER); break;
258                case '/': doType(VK_SLASH); break;
259                case '?': doType(VK_SHIFT, VK_SLASH); break;
260                case ' ': doType(VK_SPACE); break;
261                default:
262                throw new IllegalArgumentException("Cannot type character " + character);
263        }
264    }
265
266    /** Type a series of keyCodes. 
267     * Is equivalent to calling <code>doType(keyCodes, 0, keyCodes.length);</code>.
268     * The bufferSize and delayMillis settings are not used by this method.
269     * 
270     * @param keyCodes keyCodes to type
271     * 
272     * @see #doType(int[], int, int) 
273     */
274    public void doType(int... keyCodes) {
275        doType(keyCodes, 0, keyCodes.length);
276    }
277
278    /** Type a series of keyCodes. The bufferSize and delayMillis settings are not
279     * used by this method.
280     * 
281     * @param keyCodes keyCodes to type
282     */
283    public void doType(int[] keyCodes, int offset, int length) {
284        if (length == 0) {
285            return;
286        }
287        robot.keyPress(keyCodes[offset]);
288        doType(keyCodes, offset + 1, length - 1);
289        robot.keyRelease(keyCodes[offset]);
290    }
291
292}