/src/java/org/jnativehook/GlobalScreen.java

http://jnativehook.googlecode.com/ · Java · 459 lines · 202 code · 45 blank · 212 comment · 38 complexity · b1de14fdc887b12f95075001ea98d19b MD5 · raw file

  1. /* JNativeHook: Global keyboard and mouse hooking for Java.
  2. * Copyright (C) 2006-2012 Alexander Barker. All Rights Received.
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. package org.jnativehook;
  18. //Imports
  19. import java.io.File;
  20. import java.io.FileOutputStream;
  21. import java.io.InputStream;
  22. import java.lang.reflect.Field;
  23. import java.net.URISyntaxException;
  24. import java.util.EventListener;
  25. import java.util.jar.JarEntry;
  26. import java.util.jar.JarFile;
  27. import javax.swing.event.EventListenerList;
  28. import org.jnativehook.keyboard.NativeKeyEvent;
  29. import org.jnativehook.keyboard.NativeKeyListener;
  30. import org.jnativehook.mouse.NativeMouseEvent;
  31. import org.jnativehook.mouse.NativeMouseListener;
  32. import org.jnativehook.mouse.NativeMouseMotionListener;
  33. import org.jnativehook.mouse.NativeMouseWheelEvent;
  34. import org.jnativehook.mouse.NativeMouseWheelListener;
  35. /**
  36. * GlobalScreen is used to represent the native screen area that Java does not
  37. * usually have access to. This class can be thought of as the source component
  38. * for native events.
  39. * <p>
  40. * This class also handles the loading, unpacking and communication with the
  41. * native library. That includes registering new key and button hooks and the
  42. * event dispatchers for each.
  43. *
  44. * @author Alexander Barker (<a href="mailto:alex@1stleg.com">alex@1stleg.com</a>)
  45. * @version 1.1
  46. */
  47. public class GlobalScreen {
  48. /** The GlobalScreen singleton. */
  49. private static final GlobalScreen instance = new GlobalScreen();
  50. /** The list of event listeners to notify. */
  51. private EventListenerList eventListeners;
  52. /**
  53. * Private constructor to prevent multiple instances of the global screen.
  54. * The {@link #registerNativeHook} method will be called on construction to unpack
  55. * and load the native library.
  56. */
  57. private GlobalScreen() {
  58. //Setup instance variables.
  59. eventListeners = new EventListenerList();
  60. //Unpack and Load the native library.
  61. GlobalScreen.loadNativeLibrary();
  62. }
  63. /**
  64. * A deconstructor that will perform native cleanup by calling the
  65. * {@link #unregisterNativeHook} method. This method will not run until the
  66. * class is garbage collected.
  67. *
  68. * @throws Throwable The <code>Exception</code> raised by this method.
  69. * @see Object#finalize
  70. */
  71. @Override
  72. protected void finalize() throws Throwable {
  73. try {
  74. GlobalScreen.unloadNativeLibrary();
  75. }
  76. catch(Exception e) {
  77. //Do Nothing
  78. }
  79. finally {
  80. super.finalize();
  81. }
  82. }
  83. /**
  84. * Gets the single instance of GlobalScreen.
  85. *
  86. * @return single instance of GlobalScreen
  87. */
  88. public static final GlobalScreen getInstance() {
  89. return GlobalScreen.instance;
  90. }
  91. /**
  92. * Adds the specified native key listener to receive key events from the
  93. * native system. If listener is null, no exception is thrown and no action
  94. * is performed.
  95. *
  96. * @param listener the native key listener
  97. */
  98. public void addNativeKeyListener(NativeKeyListener listener) {
  99. if (listener != null) {
  100. eventListeners.add(NativeKeyListener.class, listener);
  101. }
  102. }
  103. /**
  104. * Removes the specified native key listener so that it no longer receives
  105. * key events from the native system. This method performs no function if
  106. * the listener specified by the argument was not previously added. If
  107. * listener is null, no exception is thrown and no action is performed.
  108. *
  109. * @param listener the native key listener
  110. */
  111. public void removeNativeKeyListener(NativeKeyListener listener) {
  112. if (listener != null) {
  113. eventListeners.remove(NativeKeyListener.class, listener);
  114. }
  115. }
  116. /**
  117. * Adds the specified native mouse listener to receive mouse events from the
  118. * native system. If listener is null, no exception is thrown and no action
  119. * is performed.
  120. *
  121. * @param listener the native mouse listener
  122. */
  123. public void addNativeMouseListener(NativeMouseListener listener) {
  124. if (listener != null) {
  125. eventListeners.add(NativeMouseListener.class, listener);
  126. }
  127. }
  128. /**
  129. * Removes the specified native mouse listener so that it no longer receives
  130. * mouse events from the native system. This method performs no function if
  131. * the listener specified by the argument was not previously added. If
  132. * listener is null, no exception is thrown and no action is performed.
  133. *
  134. * @param listener the native mouse listener
  135. */
  136. public void removeNativeMouseListener(NativeMouseListener listener) {
  137. if (listener != null) {
  138. eventListeners.remove(NativeMouseListener.class, listener);
  139. }
  140. }
  141. /**
  142. * Adds the specified native mouse motion listener to receive mouse motion
  143. * events from the native system. If listener is null, no exception is
  144. * thrown and no action is performed.
  145. *
  146. * @param listener the native mouse motion listener
  147. */
  148. public void addNativeMouseMotionListener(NativeMouseMotionListener listener) {
  149. if (listener != null) {
  150. eventListeners.add(NativeMouseMotionListener.class, listener);
  151. }
  152. }
  153. /**
  154. * Removes the specified native mouse motion listener so that it no longer
  155. * receives mouse motion events from the native system. This method performs
  156. * no function if the listener specified by the argument was not previously
  157. * added. If listener is null, no exception is thrown and no action is
  158. * performed.
  159. *
  160. * @param listener the native mouse motion listener
  161. */
  162. public void removeNativeMouseMotionListener(NativeMouseMotionListener listener) {
  163. if (listener != null) {
  164. eventListeners.remove(NativeMouseMotionListener.class, listener);
  165. }
  166. }
  167. /**
  168. * Adds the specified native mouse wheel listener to receive mouse wheel
  169. * events from the native system. If listener is null, no exception is
  170. * thrown and no action is performed.
  171. *
  172. * @param listener the native mouse wheel listener
  173. *
  174. * @since 1.1
  175. */
  176. public void addNativeMouseWheelListener(NativeMouseWheelListener listener) {
  177. if (listener != null) {
  178. eventListeners.add(NativeMouseWheelListener.class, listener);
  179. }
  180. }
  181. /**
  182. * Removes the specified native mouse wheel listener so that it no longer
  183. * receives mouse wheel events from the native system. This method performs
  184. * no function if the listener specified by the argument was not previously
  185. * added. If listener is null, no exception is thrown and no action is
  186. * performed.
  187. *
  188. * @param listener the native mouse wheel listener
  189. *
  190. * @since 1.1
  191. */
  192. public void removeNativeMouseWheelListener(NativeMouseWheelListener listener) {
  193. if (listener != null) {
  194. eventListeners.remove(NativeMouseWheelListener.class, listener);
  195. }
  196. }
  197. /**
  198. * Enable the native hook if it is not currently running. If it is running
  199. * the function has no effect. <b>Note that this method may block the AWT
  200. * event dispatching thread.</b> It is recomended to call this method from
  201. * outside the scope of the graphical user interface event queue.
  202. *
  203. * @throws NativeHookException the native hook exception
  204. *
  205. * @since 1.1
  206. */
  207. public native void registerNativeHook() throws NativeHookException;
  208. /**
  209. * Disable the native hook if it is currently running. If it is not running
  210. * the function has no effect. <b>Note that this method may block the AWT
  211. * event dispatching thread.</b> It is recomended to call this method from
  212. * outside the scope of the graphical user interface event queue.
  213. *
  214. * @throws NativeHookException the native hook exception
  215. *
  216. * @since 1.1
  217. */
  218. public native void unregisterNativeHook() throws NativeHookException;
  219. /**
  220. * Gets the current state of the native hook.
  221. *
  222. * @return the state of the native hook.
  223. * @throws NativeHookException the native hook exception
  224. *
  225. * @since 1.1
  226. */
  227. public native boolean isNativeHookRegistered();
  228. /**
  229. * Returns true if the current thread is the native event dispatching thread.
  230. *
  231. * @return true if the current thread is the native event dispatching thread.
  232. * @throws NativeHookException the native hook exception
  233. *
  234. * @since 1.1
  235. */
  236. public static native boolean isNativeDispatchThread();
  237. /**
  238. * Dispatches an event to the appropriate processor. This method is
  239. * generally called by the native library but maybe used to synthesize
  240. * native events from Java.
  241. *
  242. * @param e the native input event
  243. */
  244. public final void dispatchEvent(NativeInputEvent e) {
  245. if (e instanceof NativeKeyEvent) {
  246. processKeyEvent((NativeKeyEvent) e);
  247. }
  248. else if (e instanceof NativeMouseWheelEvent) {
  249. processMouseWheelEvent((NativeMouseWheelEvent) e);
  250. }
  251. else if (e instanceof NativeMouseEvent) {
  252. processMouseEvent((NativeMouseEvent) e);
  253. }
  254. }
  255. /**
  256. * Processes native key events by dispatching them to all registered
  257. * <code>NativeKeyListener</code> objects.
  258. *
  259. * @param e The <code>NativeKeyEvent</code> to dispatch.
  260. * @see NativeKeyEvent
  261. * @see NativeKeyListener
  262. * @see #addNativeKeyListener(NativeKeyListener)
  263. */
  264. protected void processKeyEvent(NativeKeyEvent e) {
  265. int id = e.getID();
  266. EventListener[] listeners = eventListeners.getListeners(NativeKeyListener.class);
  267. for (int i = 0; i < listeners.length; i++) {
  268. switch (id) {
  269. case NativeKeyEvent.NATIVE_KEY_PRESSED:
  270. ((NativeKeyListener) listeners[i]).keyPressed(e);
  271. break;
  272. case NativeKeyEvent.NATIVE_KEY_TYPED:
  273. ((NativeKeyListener) listeners[i]).keyTyped(e);
  274. break;
  275. case NativeKeyEvent.NATIVE_KEY_RELEASED:
  276. ((NativeKeyListener) listeners[i]).keyReleased(e);
  277. break;
  278. }
  279. }
  280. }
  281. /**
  282. * Processes native mouse events by dispatching them to all registered
  283. * <code>NativeMouseListener</code> objects.
  284. *
  285. * @param e The <code>NativeMouseEvent</code> to dispatch.
  286. * @see NativeMouseEvent
  287. * @see NativeMouseListener
  288. * @see #addNativeMouseListener(NativeMouseListener)
  289. */
  290. protected void processMouseEvent(NativeMouseEvent e) {
  291. int id = e.getID();
  292. EventListener[] listeners;
  293. if (id == NativeMouseEvent.NATIVE_MOUSE_MOVED || id == NativeMouseEvent.NATIVE_MOUSE_DRAGGED) {
  294. listeners = eventListeners.getListeners(NativeMouseMotionListener.class);
  295. }
  296. else {
  297. listeners = eventListeners.getListeners(NativeMouseListener.class);
  298. }
  299. for (int i = 0; i < listeners.length; i++) {
  300. switch (id) {
  301. case NativeMouseEvent.NATIVE_MOUSE_CLICKED:
  302. ((NativeMouseListener) listeners[i]).mouseClicked(e);
  303. break;
  304. case NativeMouseEvent.NATIVE_MOUSE_PRESSED:
  305. ((NativeMouseListener) listeners[i]).mousePressed(e);
  306. break;
  307. case NativeMouseEvent.NATIVE_MOUSE_RELEASED:
  308. ((NativeMouseListener) listeners[i]).mouseReleased(e);
  309. break;
  310. case NativeMouseEvent.NATIVE_MOUSE_MOVED:
  311. ((NativeMouseMotionListener) listeners[i]).mouseMoved(e);
  312. break;
  313. case NativeMouseEvent.NATIVE_MOUSE_DRAGGED:
  314. ((NativeMouseMotionListener) listeners[i]).mouseDragged(e);
  315. break;
  316. }
  317. }
  318. }
  319. /**
  320. * Processes native mouse wheel events by dispatching them to all registered
  321. * <code>NativeMouseWheelListener</code> objects.
  322. *
  323. * @param e The <code>NativeMouseWheelEvent</code> to dispatch.
  324. * @see NativeMouseWheelEvent
  325. * @see NativeMouseWheelListener
  326. * @see #addNativeMouseWheelListener(NativeMouseWheelListener)
  327. *
  328. * @since 1.1
  329. */
  330. protected void processMouseWheelEvent(NativeMouseWheelEvent e) {
  331. EventListener[] listeners = eventListeners.getListeners(NativeMouseWheelListener.class);
  332. for (int i = 0; i < listeners.length; i++) {
  333. ((NativeMouseWheelListener) listeners[i]).mouseWheelMoved(e);
  334. }
  335. }
  336. /**
  337. * Perform procedures to interface with the native library. These procedures
  338. * include unpacking and loading the library into the Java Virtual Machine.
  339. */
  340. protected static void loadNativeLibrary() {
  341. String libName = "JNativeHook";
  342. try {
  343. //Try to load the native library assuming the java.library.path was
  344. //set correctly at launch.
  345. System.loadLibrary(libName);
  346. }
  347. catch (UnsatisfiedLinkError linkError) {
  348. //The library is not in the java.library.path so try to extract it.
  349. try {
  350. //Try to locate the jar file
  351. String jarLibPath =
  352. "org/jnativehook/lib/" +
  353. NativeSystem.getFamily().toString().toLowerCase() + "/" +
  354. NativeSystem.getArchitecture().toString().toLowerCase() + "/";
  355. File classFile = new File(GlobalScreen.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getAbsoluteFile();
  356. if (classFile.isFile()) {
  357. //Load the jar file and get the lib entry.
  358. JarFile jarFile = new JarFile(classFile);
  359. JarEntry jarLibEntry = jarFile.getJarEntry(jarLibPath + System.mapLibraryName(libName));
  360. //Create a temp lib file in the systems tmp folder.
  361. File tmpLibFile = new File(System.getProperty("java.io.tmpdir") + System.getProperty("file.separator", File.separator) + System.mapLibraryName(libName));
  362. //Extract the lib from inside of the jar file.
  363. InputStream jarInputStream = jarFile.getInputStream(jarLibEntry);
  364. FileOutputStream tempLibOutputStream = new FileOutputStream(tmpLibFile);
  365. byte[] array = new byte[8192];
  366. int read = 0;
  367. while ( (read = jarInputStream.read(array)) > 0) {
  368. tempLibOutputStream.write(array, 0, read);
  369. }
  370. tempLibOutputStream.close();
  371. tmpLibFile.deleteOnExit();
  372. System.load(tmpLibFile.getPath());
  373. }
  374. else if (classFile.isDirectory()) {
  375. //Probably IDE environment, possible manual unpack.
  376. //Setup the java.library.path to the load path and attempt a lib load.
  377. File libFolder = new File(classFile.getAbsoluteFile() + "/" + jarLibPath);
  378. if (libFolder.isDirectory()) {
  379. System.setProperty("java.library.path", System.getProperty("java.library.path", ".") + System.getProperty("path.separator", ":") + libFolder.getPath());
  380. //Refresh the library path
  381. Field sysPath = ClassLoader.class.getDeclaredField("sys_paths");
  382. sysPath.setAccessible(true);
  383. if (sysPath != null) {
  384. sysPath.set(System.class.getClassLoader(), null);
  385. }
  386. //Try to load the native library
  387. System.loadLibrary(libName);
  388. }
  389. }
  390. }
  391. catch (URISyntaxException e) {
  392. //Tried and Failed to unpak the JAR container.
  393. throw new RuntimeException(e.getMessage());
  394. }
  395. catch (Exception e) {
  396. //Tried and Failed to manually setup the java.library.path
  397. throw new RuntimeException(e.getMessage());
  398. }
  399. }
  400. }
  401. /**
  402. * Perform procedures to cleanup the native library. This method is called
  403. * on garbage collection to ensure proper native cleanup.
  404. */
  405. protected static void unloadNativeLibrary() {
  406. try {
  407. //Make sure the native thread has stopped.
  408. instance.unregisterNativeHook();
  409. }
  410. catch (NativeHookException e) {
  411. e.printStackTrace();
  412. }
  413. }
  414. }