/** * Copyright 2009 Marc Stogaitis and Mimi Sun * * 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. */ package org.gmote.server; import java.awt.AWTException; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.MouseInfo; import java.awt.Point; import java.awt.PointerInfo; import java.awt.Rectangle; import java.awt.Robot; import java.awt.event.KeyEvent; import java.util.logging.Level; import java.util.logging.Logger; import org.gmote.common.Protocol.MouseEvent; import org.gmote.common.packet.KeyboardEventPacket; import org.gmote.common.packet.MouseClickPacket; import org.gmote.common.packet.MouseWheelPacket; /** * Handles requests to move the mouse send keyboard commands. * @author Marc * */ public class TrackpadHandler { private static final Logger LOGGER = Logger.getLogger(TrackpadHandler.class .getName()); private static TrackpadHandler instance = null; Robot robot = null; int mouseX = 1; int mouseY = 1; /** * Private constructor to prevent instantiation. */ private TrackpadHandler() { try { robot = new Robot(); } catch (AWTException e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); } } /** * Gets an instance of this class. */ public static TrackpadHandler instance() { if (instance == null) { instance = new TrackpadHandler(); } return instance; } /** * Moves the mouse on the screen of a user specified amount. */ public void handleMoveMouseCommand(short diffX, short diffY) { PointerInfo pointerInfo = MouseInfo.getPointerInfo(); // On mac, sometimes the pointerinfo is null. This is why we initialize the // values based on where the mouse previously was. int newX = mouseX + diffX; int newY = mouseY + diffY; if (pointerInfo != null) { Point location = pointerInfo.getLocation(); if (location != null) { int currentX = location.x; int currentY = location.y; newX = currentX + diffX; newY = currentY + diffY; } } if (!pointIsOnAScreen(newX, newY)) { // Move the cursor to the edge. Rectangle currentScreen = pointerInfo.getDevice().getDefaultConfiguration().getBounds(); newX = clampToScreen(newX, currentScreen.x, currentScreen.width); newY = clampToScreen(newY, currentScreen.y, currentScreen.height); } robot.mouseMove(newX, newY); mouseX = newX; mouseY = newY; } private int clampToScreen(int newCoordinate, int screenCoordinate, int screenSize) { if (newCoordinate < screenCoordinate) { newCoordinate = screenCoordinate; } else if (newCoordinate >= screenCoordinate + screenSize) { newCoordinate = screenCoordinate + screenSize - 1; } return newCoordinate; } private boolean pointIsOnAScreen(int newX, int newY) { GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice[] gs = ge.getScreenDevices(); for (GraphicsDevice g : gs) { if (g.getDefaultConfiguration().getBounds().contains(newX, newY)) { return true; } } return false; } /** * Performs a mouse click based on what was sent by the user. */ public void hanldeMouseClickCommand(MouseClickPacket packet) { MouseEvent mouseEvent = packet.getMouseEvent(); MouseUtil.doMouseEvent(mouseEvent, robot); } public void handleKeyPressCommand(KeyboardEventPacket packet) { int keyCode = packet.getKeyCode(); if (keyCode < 0) { // Handle special characters if (keyCode == KeyboardEventPacket.DELETE_KEYCODE) { type(new int[] {KeyEvent.VK_BACK_SPACE}); } } else { char keyChar = (char)keyCode; type(keyCodes(keyChar)); } } public void handleMouseWheelCommand(MouseWheelPacket packet) { if (PlatformUtil.isMac()) { robot.mouseWheel(packet.getWheelAmount() * -1); } else { robot.mouseWheel(packet.getWheelAmount()); } } /** * Type the codes provided. Multiple codes can result if the character is uppercase * or special e.g. SHIFT + a (A) or SHIFT + 1 (!) */ private void type(int[] code) { int count = code.length; int pos = 0; try { while (count > 1 && pos < count - 1) { robot.keyPress(code[pos++]); } try { robot.keyPress(code[pos]); robot.keyRelease(code[pos]); } catch (IllegalArgumentException e) { // Catch the exception here so that we have a chance to // do the keyRelease on the keys the previously worked. LOGGER.log(Level.SEVERE, e.getMessage(), e); } while (count > 1 && pos > 0) { robot.keyRelease(code[--pos]); } } catch (IllegalArgumentException e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); } } /** * Maps the specified character to key code acceptable to the Robot. The keys * are mapped corresponding to the code from the * <code>java.awt.event.KeyEvent</code> associated with the SHIFT key (if * necessary). * * @param key * the character code to be converted. * * @return the array of key codes. If the character is uppercase or special, * the first code returned in the array is the SHIFT key. */ private int[] keyCodes(char key) { int[] codes = new int[0]; if (key >= '0' && key <= '9') { codes = new int[] { Character.getNumericValue(key) + 48 }; } else if (key >= 'a' && key <= 'z') { codes = new int[] { Character.getNumericValue(key) + 55 }; } else if (key >= 'A' && key <= 'Z') { codes = new int[] { KeyEvent.VK_SHIFT, Character.getNumericValue(key) + 55 }; } else if (key == '!') { codes = new int[] { KeyEvent.VK_SHIFT, KeyEvent.VK_1 }; } else if (key == '@') { codes = new int[] { KeyEvent.VK_SHIFT, KeyEvent.VK_2 }; } else if (key == '#') { codes = new int[] { KeyEvent.VK_SHIFT, KeyEvent.VK_3 }; } else if (key == '$') { codes = new int[] { KeyEvent.VK_SHIFT, KeyEvent.VK_4 }; } else if (key == '%') { codes = new int[] { KeyEvent.VK_SHIFT, KeyEvent.VK_5 }; } else if (key == '^') { codes = new int[] { KeyEvent.VK_SHIFT, KeyEvent.VK_6 }; } else if (key == '&') { codes = new int[] { KeyEvent.VK_SHIFT, KeyEvent.VK_7 }; } else if (key == '*') { codes = new int[] { KeyEvent.VK_SHIFT, KeyEvent.VK_8 }; } else if (key == '(') { codes = new int[] { KeyEvent.VK_SHIFT, KeyEvent.VK_9 }; } else if (key == ')') { codes = new int[] { KeyEvent.VK_SHIFT, KeyEvent.VK_0 }; } else if (key == '<') { codes = new int[] { KeyEvent.VK_SHIFT, KeyEvent.VK_COMMA }; } else if (key == '>') { codes = new int[] { KeyEvent.VK_SHIFT, KeyEvent.VK_PERIOD }; } else if (key == '?') { codes = new int[] { KeyEvent.VK_SHIFT, KeyEvent.VK_SLASH }; } else if (key == '|') { codes = new int[] { KeyEvent.VK_SHIFT, KeyEvent.VK_BACK_SLASH }; } else if (key == '_') { codes = new int[] { KeyEvent.VK_SHIFT, KeyEvent.VK_MINUS }; } else if (key == '+') { codes = new int[] { KeyEvent.VK_SHIFT, KeyEvent.VK_EQUALS }; } else if (key == '{') { codes = new int[] { KeyEvent.VK_SHIFT, KeyEvent.VK_OPEN_BRACKET }; } else if (key == '}') { codes = new int[] { KeyEvent.VK_SHIFT, KeyEvent.VK_CLOSE_BRACKET }; } else if (key == ':') { codes = new int[] { KeyEvent.VK_SHIFT, KeyEvent.VK_SEMICOLON }; } else if (key == '\"') { codes = new int[] { KeyEvent.VK_SHIFT, KeyEvent.VK_QUOTE }; } else if (key == '~') { codes = new int[] { KeyEvent.VK_SHIFT, KeyEvent.VK_BACK_QUOTE }; } else { codes = new int[] { keyCode(key) }; } return codes; } /** * Maps the specified character to key code acceptable to the Robot. This key * can be directly mapped to the code from the * <code>java.awt.event.KeyEvent</code> class. * * @param key * the character code to be converted. * * @return the corresponding code mapped from the * <code>java.awt.event.KeyEvent</code> class. */ private int keyCode(char key) { int code; switch (key) { case '\\': code = KeyEvent.VK_BACK_SLASH; break; case '[': code = KeyEvent.VK_OPEN_BRACKET; break; case ']': code = KeyEvent.VK_CLOSE_BRACKET; break; case '.': code = KeyEvent.VK_PERIOD; break; case '\'': code = KeyEvent.VK_QUOTE; break; case '/': code = KeyEvent.VK_DIVIDE; break; case '-': code = KeyEvent.VK_MINUS; break; case ',': code = KeyEvent.VK_COMMA; break; case ';': code = KeyEvent.VK_SEMICOLON; break; case '\t': code = KeyEvent.VK_TAB; break; default: int keyValue = (int)key; if (keyValue == 247) { code = KeyEvent.VK_DIVIDE; } else if (keyValue == 215) { code = KeyEvent.VK_MULTIPLY; } else if (keyValue == 61) { code = KeyEvent.VK_EQUALS; } else if (keyValue == 96) { code = KeyEvent.VK_BACK_QUOTE; } else if (keyValue == 10) { code = KeyEvent.VK_ENTER; } else { code = KeyEvent.VK_SPACE; } break; } return code; } // Implemented by http://www.codeproject.com/KB/cs/runawayapp.aspx }