View Javadoc

1   /**
2    * JIntellitype ----------------- Copyright 2005-2008 Emil A. Lefkof III,
3    * Melloware Inc. I always give it my best shot to make a program useful and
4    * solid, but remeber that there is absolutely no warranty for using this
5    * program as stated in the following terms: Licensed under the Apache License,
6    * Version 2.0 (the "License"); you may not use this file except in compliance
7    * with the License. You may obtain a copy of the License at
8    * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
9    * or agreed to in writing, software distributed under the License is
10   * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11   * KIND, either express or implied. See the License for the specific language
12   * governing permissions and limitations under the License.
13   */
14  package com.melloware.jintellitype;
15  
16  import java.awt.event.InputEvent;
17  import java.awt.event.KeyEvent;
18  import java.io.File;
19  import java.io.FileOutputStream;
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.io.OutputStream;
23  import java.util.Collections;
24  import java.util.HashMap;
25  import java.util.List;
26  import java.util.concurrent.CopyOnWriteArrayList;
27  
28  import javax.swing.SwingUtilities;
29  
30  /**
31   * JIntellitype A Java Implementation for using the Windows API Intellitype
32   * commands and the RegisterHotKey and UnRegisterHotkey API calls for globally
33   * responding to key events. Intellitype are commands that are using for Play,
34   * Stop, Next on Media keyboards or some laptops that have those special keys.
35   * <p>
36   * JIntellitype class that is used to call Windows API calls using the
37   * JIntellitype.dll.
38   * <p>
39   * This file comes with native code in JINTELLITYPE.DLL The DLL should go in
40   * C:/WINDOWS/SYSTEM or in your current directory
41   * <p>
42   * <p>
43   * Copyright (c) 1999-2008 Melloware, Inc. <http://www.melloware.com>
44   * @author Emil A. Lefkof III <info@melloware.com>
45   * @version 1.3.1
46   */
47  public final class JIntellitype implements JIntellitypeConstants {
48  
49     /**
50      * Static variable to hold singleton.
51      */
52     private static JIntellitype jintellitype = null;
53  
54     /**
55      * Static variable for double checked thread safety.
56      */
57     private static boolean isInitialized = false;
58  
59     /**
60      * Static variable to hold the libary location if set
61      */
62     private static String libraryLocation = null;
63  
64     /**
65      * Listeners collection for Hotkey events
66      */
67     private final List<HotkeyListener> hotkeyListeners = Collections
68              .synchronizedList(new CopyOnWriteArrayList<HotkeyListener>());
69  
70     /**
71      * Listeners collection for Hotkey events
72      */
73     private final List<IntellitypeListener> intellitypeListeners = Collections
74              .synchronizedList(new CopyOnWriteArrayList<IntellitypeListener>());
75  
76     /**
77      * Handler is used by JNI code to keep different JVM instances separate
78      */
79     @SuppressWarnings("unused")
80     private final int handler = 0;
81  
82     /**
83      * Map containing key->keycode mapping
84      * @see #registerHotKey(int, String)
85      * @see #getKey2KeycodeMapping()
86      */
87     private final HashMap<String, Integer> keycodeMap;
88  
89     /**
90      * Private Constructor to prevent instantiation. Initialize the library for
91      * calling.
92      */
93     private JIntellitype() {
94        // Load JNI library
95        try {
96           if (getLibraryLocation() != null) {
97              System.load(getLibraryLocation());
98           } else {
99              String jarPath = "com/melloware/jintellitype/";
100             String tmpDir = System.getProperty("java.io.tmpdir");
101             try {
102                String dll = "JIntellitype.dll";
103                fromJarToFs(jarPath + dll, tmpDir + dll);
104                System.load(tmpDir + dll);
105             } catch (UnsatisfiedLinkError e) {
106                String dll = "JIntellitype64.dll";
107                fromJarToFs(jarPath + dll, tmpDir + dll);
108                System.load(tmpDir + dll);
109             }
110          }
111          initializeLibrary();
112       } catch (IOException ex) {
113          throw new JIntellitypeException(ex);
114       } catch (UnsatisfiedLinkError ex) {
115          throw new JIntellitypeException(ex);
116       } catch (RuntimeException ex) {
117          throw new JIntellitypeException(ex);
118       }
119       this.keycodeMap = getKey2KeycodeMapping();
120    }
121 
122    /**
123     * Pulls a file out of the JAR and puts it on the File Path.
124     * <p>
125     * @param jarPath the path to the JAR
126     * @param filePath the file path to extract to
127     * @throws IOException if any IO error occurs
128     */
129    private void fromJarToFs(String jarPath, String filePath) throws IOException {
130       File file = new File(filePath);
131       if (file.exists()) {
132          boolean success = file.delete();
133          if (!success) {
134             throw new IOException("couldn't delete " + filePath);
135          }
136       }
137       InputStream is = null;
138       OutputStream os = null;
139       try {
140          is = ClassLoader.getSystemClassLoader().getResourceAsStream(jarPath);
141          os = new FileOutputStream(filePath);
142          byte[] buffer = new byte[8192];
143          int bytesRead;
144          while ((bytesRead = is.read(buffer)) != -1) {
145             os.write(buffer, 0, bytesRead);
146          }
147       } finally {
148          if (is != null) {
149             is.close();
150          }
151          if (os != null) {
152             os.close();
153          }
154       }
155    }
156 
157    /**
158     * Gets the singleton instance of the JIntellitype object.
159     * <p>
160     * But the possibility of creation of more instance is only before the
161     * instance is created. Since all code defined inside getInstance method is
162     * in the synchronized block, even the subsequent requests will also come and
163     * wait in the synchronized block. This is a performance issue. The same can
164     * be solved using double-checked lock. Following is the implementation of
165     * Singleton with lazy initialization and double-checked lock.
166     * <p>
167     * @return an instance of JIntellitype class
168     */
169    public static JIntellitype getInstance() {
170       if (!isInitialized) {
171          synchronized (JIntellitype.class) {
172             if (!isInitialized) {
173                jintellitype = new JIntellitype();
174                isInitialized = true;
175             }
176          }
177       }
178       return jintellitype;
179    }
180 
181    /**
182     * Adds a listener for hotkeys.
183     * <p>
184     * @param listener the HotKeyListener to be added
185     */
186    public void addHotKeyListener(HotkeyListener listener) {
187       hotkeyListeners.add(listener);
188    }
189 
190    /**
191     * Adds a listener for intellitype commands.
192     * <p>
193     * @param listener the IntellitypeListener to be added
194     */
195    public void addIntellitypeListener(IntellitypeListener listener) {
196       intellitypeListeners.add(listener);
197    }
198 
199    /**
200     * Cleans up all resources used by JIntellitype.
201     */
202    public void cleanUp() {
203       try {
204          terminate();
205       } catch (UnsatisfiedLinkError ex) {
206          throw new JIntellitypeException(ERROR_MESSAGE, ex);
207       } catch (RuntimeException ex) {
208          throw new JIntellitypeException(ex);
209       }
210    }
211 
212    /**
213     * Registers a Hotkey with windows. This combination will be responded to by
214     * all registered HotKeyListeners. Uses the JIntellitypeConstants for MOD,
215     * ALT, CTRL, and WINDOWS keys.
216     * <p>
217     * @param identifier a unique identifier for this key combination
218     * @param modifier MOD_SHIFT, MOD_ALT, MOD_CONTROL, MOD_WIN from
219     *           JIntellitypeConstants, or 0 if no modifier needed
220     * @param keycode the key to respond to in Ascii integer, 65 for A
221     */
222    public void registerHotKey(int identifier, int modifier, int keycode) {
223       try {
224          int modifiers = swingToIntelliType(modifier);
225          if (modifiers == 0) {
226             modifiers = modifier;
227          }
228          regHotKey(identifier, modifier, keycode);
229       } catch (UnsatisfiedLinkError ex) {
230          throw new JIntellitypeException(ERROR_MESSAGE, ex);
231       } catch (RuntimeException ex) {
232          throw new JIntellitypeException(ex);
233       }
234    }
235 
236    /**
237     * Registers a Hotkey with windows. This combination will be responded to by
238     * all registered HotKeyListeners. Use the Swing InputEvent constants from
239     * java.awt.InputEvent.
240     * <p>
241     * @param identifier a unique identifier for this key combination
242     * @param modifier InputEvent.SHIFT_MASK, InputEvent.ALT_MASK,
243     *           InputEvent.CTRL_MASK, or 0 if no modifier needed
244     * @param keycode the key to respond to in Ascii integer, 65 for A
245     */
246    public void registerSwingHotKey(int identifier, int modifier, int keycode) {
247       try {
248          regHotKey(identifier, swingToIntelliType(modifier), keycode);
249       } catch (UnsatisfiedLinkError ex) {
250          throw new JIntellitypeException(ERROR_MESSAGE, ex);
251       } catch (RuntimeException ex) {
252          throw new JIntellitypeException(ex);
253       }
254    }
255 
256    /**
257     * Registers a Hotkey with windows. This combination will be responded to by
258     * all registered HotKeyListeners. Use the identifiers CTRL, SHIFT, ALT
259     * and/or WIN.
260     * <p>
261     * @param identifier a unique identifier for this key combination
262     * @param modifierAndKeyCode String with modifiers separated by + and keycode
263     *           (e.g. CTRL+SHIFT+A)
264     * @see #registerHotKey(int, int, int)
265     * @see #registerSwingHotKey(int, int, int)
266     */
267    public void registerHotKey(int identifier, String modifierAndKeyCode) {
268       String[] split = modifierAndKeyCode.split("\\+");
269       int mask = 0;
270       int keycode = 0;
271 
272       for (int i = 0; i < split.length; i++) {
273          if ("ALT".equalsIgnoreCase(split[i])) {
274             mask += JIntellitype.MOD_ALT;
275          } else if ("CTRL".equalsIgnoreCase(split[i]) || "CONTROL".equalsIgnoreCase(split[i])) {
276             mask += JIntellitype.MOD_CONTROL;
277          } else if ("SHIFT".equalsIgnoreCase(split[i])) {
278             mask += JIntellitype.MOD_SHIFT;
279          } else if ("WIN".equalsIgnoreCase(split[i])) {
280             mask += JIntellitype.MOD_WIN;
281          } else if (keycodeMap.containsKey(split[i].toLowerCase())) {
282             keycode = keycodeMap.get(split[i].toLowerCase());
283          }
284       }
285       registerHotKey(identifier, mask, keycode);
286    }
287 
288    /**
289     * Removes a listener for hotkeys.
290     */
291    public void removeHotKeyListener(HotkeyListener listener) {
292       hotkeyListeners.remove(listener);
293    }
294 
295    /**
296     * Removes a listener for intellitype commands.
297     */
298    public void removeIntellitypeListener(IntellitypeListener listener) {
299       intellitypeListeners.remove(listener);
300    }
301 
302    /**
303     * Unregisters a previously registered Hotkey identified by its unique
304     * identifier.
305     * <p>
306     * @param identifier the unique identifer of this Hotkey
307     */
308    public void unregisterHotKey(int identifier) {
309       try {
310          unregHotKey(identifier);
311       } catch (UnsatisfiedLinkError ex) {
312          throw new JIntellitypeException(ERROR_MESSAGE, ex);
313       } catch (RuntimeException ex) {
314          throw new JIntellitypeException(ex);
315       }
316    }
317 
318    /**
319     * Checks to see if this application is already running.
320     * <p>
321     * @param appTitle the name of the application to check for
322     * @return true if running, false if not running
323     */
324    public static boolean checkInstanceAlreadyRunning(String appTitle) {
325       return getInstance().isRunning(appTitle);
326    }
327 
328    /**
329     * Checks to make sure the OS is a Windows flavor and that the JIntellitype
330     * DLL is found in the path and the JDK is 32 bit not 64 bit. The DLL
331     * currently only supports 32 bit JDK.
332     * <p>
333     * @return true if Jintellitype may be used, false if not
334     */
335    public static boolean isJIntellitypeSupported() {
336       boolean result = false;
337       String os = "none";
338 
339       try {
340          os = System.getProperty("os.name").toLowerCase();
341       } catch (SecurityException ex) {
342          // we are not allowed to look at this property
343          System.err.println("Caught a SecurityException reading the system property "
344                   + "'os.name'; the SystemUtils property value will default to null.");
345       }
346 
347       // only works on Windows OS currently
348       if (os.startsWith("windows")) {
349          // try an get the instance and if it succeeds then return true
350          try {
351             getInstance();
352             result = true;
353          } catch (Exception e) {
354             result = false;
355          }
356       }
357 
358       return result;
359    }
360 
361    /**
362     * Gets the libraryLocation.
363     * <p>
364     * @return Returns the libraryLocation.
365     */
366    public static String getLibraryLocation() {
367       return libraryLocation;
368    }
369 
370    /**
371     * Sets the libraryLocation.
372     * <p>
373     * @param libraryLocation The libraryLocation to set.
374     */
375    public static void setLibraryLocation(String libraryLocation) {
376       final File dll = new File(libraryLocation);
377       if (!dll.isAbsolute()) {
378          JIntellitype.libraryLocation = dll.getAbsolutePath();
379       } else {
380          // absolute path, no further calculation needed
381          JIntellitype.libraryLocation = libraryLocation;
382       }
383    }
384 
385    /**
386     * Notifies all listeners that Hotkey was pressed.
387     * <p>
388     * @param identifier the unique identifier received
389     */
390    protected void onHotKey(final int identifier) {
391       for (final HotkeyListener hotkeyListener : hotkeyListeners) {
392          SwingUtilities.invokeLater(new Runnable() {
393             public void run() {
394                hotkeyListener.onHotKey(identifier);
395             }
396          });
397       }
398    }
399 
400    /**
401     * Notifies all listeners that Intellitype command was received.
402     * <p>
403     * @param command the unique WM_APPCOMMAND received
404     */
405    protected void onIntellitype(final int command) {
406       for (final IntellitypeListener intellitypeListener : intellitypeListeners) {
407          SwingUtilities.invokeLater(new Runnable() {
408             public void run() {
409                intellitypeListener.onIntellitype(command);
410             }
411          });
412       }
413    }
414 
415    /**
416     * Swing modifier value to Jintellipad conversion. If no conversion needed
417     * just return the original value. This lets users pass either the original
418     * JIntellitype constants or Swing InputEvent constants.
419     * <p>
420     * @param swingKeystrokeModifier the Swing KeystrokeModifier to check
421     * @return Jintellitype the JIntellitype modifier value
422     */
423    protected static int swingToIntelliType(int swingKeystrokeModifier) {
424       int mask = 0;
425       if ((swingKeystrokeModifier & InputEvent.SHIFT_MASK) == InputEvent.SHIFT_MASK) {
426          mask += JIntellitype.MOD_SHIFT;
427       }
428       if ((swingKeystrokeModifier & InputEvent.ALT_MASK) == InputEvent.ALT_MASK) {
429          mask += JIntellitype.MOD_ALT;
430       }
431       if ((swingKeystrokeModifier & InputEvent.CTRL_MASK) == InputEvent.CTRL_MASK) {
432          mask += JIntellitype.MOD_CONTROL;
433       }
434       if ((swingKeystrokeModifier & InputEvent.SHIFT_DOWN_MASK) == InputEvent.SHIFT_DOWN_MASK) {
435          mask += JIntellitype.MOD_SHIFT;
436       }
437       if ((swingKeystrokeModifier & InputEvent.ALT_DOWN_MASK) == InputEvent.ALT_DOWN_MASK) {
438          mask += JIntellitype.MOD_ALT;
439       }
440       if ((swingKeystrokeModifier & InputEvent.CTRL_DOWN_MASK) == InputEvent.CTRL_DOWN_MASK) {
441          mask += JIntellitype.MOD_CONTROL;
442       }
443       return mask;
444    }
445 
446    /**
447     * Puts all constants from {@link java.awt.event.KeyEvent} in a keycodeMap.
448     * The key is the lower case form of it.
449     * @return Map containing key->keycode mapping DOCU Now enables the user to
450     *         use all keys specified here instead of just [A-Z],[0-9] as before
451     */
452    private HashMap<String, Integer> getKey2KeycodeMapping() {
453       HashMap<String, Integer> map = new HashMap<String, Integer>();
454 
455       map.put("first", KeyEvent.KEY_FIRST);
456       map.put("last", KeyEvent.KEY_LAST);
457       map.put("typed", KeyEvent.KEY_TYPED);
458       map.put("pressed", KeyEvent.KEY_PRESSED);
459       map.put("released", KeyEvent.KEY_RELEASED);
460       map.put("enter", KeyEvent.VK_ENTER);
461       map.put("back_space", KeyEvent.VK_BACK_SPACE);
462       map.put("tab", KeyEvent.VK_TAB);
463       map.put("cancel", KeyEvent.VK_CANCEL);
464       map.put("clear", KeyEvent.VK_CLEAR);
465       map.put("pause", KeyEvent.VK_PAUSE);
466       map.put("caps_lock", KeyEvent.VK_CAPS_LOCK);
467       map.put("escape", KeyEvent.VK_ESCAPE);
468       map.put("space", KeyEvent.VK_SPACE);
469       map.put("page_up", KeyEvent.VK_PAGE_UP);
470       map.put("page_down", KeyEvent.VK_PAGE_DOWN);
471       map.put("end", KeyEvent.VK_END);
472       map.put("home", KeyEvent.VK_HOME);
473       map.put("left", KeyEvent.VK_LEFT);
474       map.put("up", KeyEvent.VK_UP);
475       map.put("right", KeyEvent.VK_RIGHT);
476       map.put("down", KeyEvent.VK_DOWN);
477       map.put("comma", KeyEvent.VK_COMMA);
478       map.put("minus", KeyEvent.VK_MINUS);
479       map.put("period", KeyEvent.VK_PERIOD);
480       map.put("slash", KeyEvent.VK_SLASH);
481       map.put("0", KeyEvent.VK_0);
482       map.put("1", KeyEvent.VK_1);
483       map.put("2", KeyEvent.VK_2);
484       map.put("3", KeyEvent.VK_3);
485       map.put("4", KeyEvent.VK_4);
486       map.put("5", KeyEvent.VK_5);
487       map.put("6", KeyEvent.VK_6);
488       map.put("7", KeyEvent.VK_7);
489       map.put("8", KeyEvent.VK_8);
490       map.put("9", KeyEvent.VK_9);
491       map.put("semicolon", KeyEvent.VK_SEMICOLON);
492       map.put("equals", KeyEvent.VK_EQUALS);
493       map.put("a", KeyEvent.VK_A);
494       map.put("b", KeyEvent.VK_B);
495       map.put("c", KeyEvent.VK_C);
496       map.put("d", KeyEvent.VK_D);
497       map.put("e", KeyEvent.VK_E);
498       map.put("f", KeyEvent.VK_F);
499       map.put("g", KeyEvent.VK_G);
500       map.put("h", KeyEvent.VK_H);
501       map.put("i", KeyEvent.VK_I);
502       map.put("j", KeyEvent.VK_J);
503       map.put("k", KeyEvent.VK_K);
504       map.put("l", KeyEvent.VK_L);
505       map.put("m", KeyEvent.VK_M);
506       map.put("n", KeyEvent.VK_N);
507       map.put("o", KeyEvent.VK_O);
508       map.put("p", KeyEvent.VK_P);
509       map.put("q", KeyEvent.VK_Q);
510       map.put("r", KeyEvent.VK_R);
511       map.put("s", KeyEvent.VK_S);
512       map.put("t", KeyEvent.VK_T);
513       map.put("u", KeyEvent.VK_U);
514       map.put("v", KeyEvent.VK_V);
515       map.put("w", KeyEvent.VK_W);
516       map.put("x", KeyEvent.VK_X);
517       map.put("y", KeyEvent.VK_Y);
518       map.put("z", KeyEvent.VK_Z);
519       map.put("open_bracket", KeyEvent.VK_OPEN_BRACKET);
520       map.put("back_slash", KeyEvent.VK_BACK_SLASH);
521       map.put("close_bracket", KeyEvent.VK_CLOSE_BRACKET);
522       map.put("numpad0", KeyEvent.VK_NUMPAD0);
523       map.put("numpad1", KeyEvent.VK_NUMPAD1);
524       map.put("numpad2", KeyEvent.VK_NUMPAD2);
525       map.put("numpad3", KeyEvent.VK_NUMPAD3);
526       map.put("numpad4", KeyEvent.VK_NUMPAD4);
527       map.put("numpad5", KeyEvent.VK_NUMPAD5);
528       map.put("numpad6", KeyEvent.VK_NUMPAD6);
529       map.put("numpad7", KeyEvent.VK_NUMPAD7);
530       map.put("numpad8", KeyEvent.VK_NUMPAD8);
531       map.put("numpad9", KeyEvent.VK_NUMPAD9);
532       map.put("multiply", KeyEvent.VK_MULTIPLY);
533       map.put("add", KeyEvent.VK_ADD);
534       map.put("separator", KeyEvent.VK_SEPARATOR);
535       map.put("subtract", KeyEvent.VK_SUBTRACT);
536       map.put("decimal", KeyEvent.VK_DECIMAL);
537       map.put("divide", KeyEvent.VK_DIVIDE);
538       map.put("delete", KeyEvent.VK_DELETE);
539       map.put("num_lock", KeyEvent.VK_NUM_LOCK);
540       map.put("scroll_lock", KeyEvent.VK_SCROLL_LOCK);
541       map.put("f1", KeyEvent.VK_F1);
542       map.put("f2", KeyEvent.VK_F2);
543       map.put("f3", KeyEvent.VK_F3);
544       map.put("f4", KeyEvent.VK_F4);
545       map.put("f5", KeyEvent.VK_F5);
546       map.put("f6", KeyEvent.VK_F6);
547       map.put("f7", KeyEvent.VK_F7);
548       map.put("f8", KeyEvent.VK_F8);
549       map.put("f9", KeyEvent.VK_F9);
550       map.put("f10", KeyEvent.VK_F10);
551       map.put("f11", KeyEvent.VK_F11);
552       map.put("f12", KeyEvent.VK_F12);
553       map.put("f13", KeyEvent.VK_F13);
554       map.put("f14", KeyEvent.VK_F14);
555       map.put("f15", KeyEvent.VK_F15);
556       map.put("f16", KeyEvent.VK_F16);
557       map.put("f17", KeyEvent.VK_F17);
558       map.put("f18", KeyEvent.VK_F18);
559       map.put("f19", KeyEvent.VK_F19);
560       map.put("f20", KeyEvent.VK_F20);
561       map.put("f21", KeyEvent.VK_F21);
562       map.put("f22", KeyEvent.VK_F22);
563       map.put("f23", KeyEvent.VK_F23);
564       map.put("f24", KeyEvent.VK_F24);
565       map.put("printscreen", KeyEvent.VK_PRINTSCREEN);
566       map.put("insert", KeyEvent.VK_INSERT);
567       map.put("help", KeyEvent.VK_HELP);
568       map.put("meta", KeyEvent.VK_META);
569       map.put("back_quote", KeyEvent.VK_BACK_QUOTE);
570       map.put("quote", KeyEvent.VK_QUOTE);
571       map.put("kp_up", KeyEvent.VK_KP_UP);
572       map.put("kp_down", KeyEvent.VK_KP_DOWN);
573       map.put("kp_left", KeyEvent.VK_KP_LEFT);
574       map.put("kp_right", KeyEvent.VK_KP_RIGHT);
575       map.put("dead_grave", KeyEvent.VK_DEAD_GRAVE);
576       map.put("dead_acute", KeyEvent.VK_DEAD_ACUTE);
577       map.put("dead_circumflex", KeyEvent.VK_DEAD_CIRCUMFLEX);
578       map.put("dead_tilde", KeyEvent.VK_DEAD_TILDE);
579       map.put("dead_macron", KeyEvent.VK_DEAD_MACRON);
580       map.put("dead_breve", KeyEvent.VK_DEAD_BREVE);
581       map.put("dead_abovedot", KeyEvent.VK_DEAD_ABOVEDOT);
582       map.put("dead_diaeresis", KeyEvent.VK_DEAD_DIAERESIS);
583       map.put("dead_abovering", KeyEvent.VK_DEAD_ABOVERING);
584       map.put("dead_doubleacute", KeyEvent.VK_DEAD_DOUBLEACUTE);
585       map.put("dead_caron", KeyEvent.VK_DEAD_CARON);
586       map.put("dead_cedilla", KeyEvent.VK_DEAD_CEDILLA);
587       map.put("dead_ogonek", KeyEvent.VK_DEAD_OGONEK);
588       map.put("dead_iota", KeyEvent.VK_DEAD_IOTA);
589       map.put("dead_voiced_sound", KeyEvent.VK_DEAD_VOICED_SOUND);
590       map.put("dead_semivoiced_sound", KeyEvent.VK_DEAD_SEMIVOICED_SOUND);
591       map.put("ampersand", KeyEvent.VK_AMPERSAND);
592       map.put("asterisk", KeyEvent.VK_ASTERISK);
593       map.put("quotedbl", KeyEvent.VK_QUOTEDBL);
594       map.put("less", KeyEvent.VK_LESS);
595       map.put("greater", KeyEvent.VK_GREATER);
596       map.put("braceleft", KeyEvent.VK_BRACELEFT);
597       map.put("braceright", KeyEvent.VK_BRACERIGHT);
598       map.put("at", KeyEvent.VK_AT);
599       map.put("colon", KeyEvent.VK_COLON);
600       map.put("circumflex", KeyEvent.VK_CIRCUMFLEX);
601       map.put("dollar", KeyEvent.VK_DOLLAR);
602       map.put("euro_sign", KeyEvent.VK_EURO_SIGN);
603       map.put("exclamation_mark", KeyEvent.VK_EXCLAMATION_MARK);
604       map.put("inverted_exclamation_mark", KeyEvent.VK_INVERTED_EXCLAMATION_MARK);
605       map.put("left_parenthesis", KeyEvent.VK_LEFT_PARENTHESIS);
606       map.put("number_sign", KeyEvent.VK_NUMBER_SIGN);
607       map.put("plus", KeyEvent.VK_PLUS);
608       map.put("right_parenthesis", KeyEvent.VK_RIGHT_PARENTHESIS);
609       map.put("underscore", KeyEvent.VK_UNDERSCORE);
610       map.put("context_menu", KeyEvent.VK_CONTEXT_MENU);
611       map.put("final", KeyEvent.VK_FINAL);
612       map.put("convert", KeyEvent.VK_CONVERT);
613       map.put("nonconvert", KeyEvent.VK_NONCONVERT);
614       map.put("accept", KeyEvent.VK_ACCEPT);
615       map.put("modechange", KeyEvent.VK_MODECHANGE);
616       map.put("kana", KeyEvent.VK_KANA);
617       map.put("kanji", KeyEvent.VK_KANJI);
618       map.put("alphanumeric", KeyEvent.VK_ALPHANUMERIC);
619       map.put("katakana", KeyEvent.VK_KATAKANA);
620       map.put("hiragana", KeyEvent.VK_HIRAGANA);
621       map.put("full_width", KeyEvent.VK_FULL_WIDTH);
622       map.put("half_width", KeyEvent.VK_HALF_WIDTH);
623       map.put("roman_characters", KeyEvent.VK_ROMAN_CHARACTERS);
624       map.put("all_candidates", KeyEvent.VK_ALL_CANDIDATES);
625       map.put("previous_candidate", KeyEvent.VK_PREVIOUS_CANDIDATE);
626       map.put("code_input", KeyEvent.VK_CODE_INPUT);
627       map.put("japanese_katakana", KeyEvent.VK_JAPANESE_KATAKANA);
628       map.put("japanese_hiragana", KeyEvent.VK_JAPANESE_HIRAGANA);
629       map.put("japanese_roman", KeyEvent.VK_JAPANESE_ROMAN);
630       map.put("kana_lock", KeyEvent.VK_KANA_LOCK);
631       map.put("input_method_on_off", KeyEvent.VK_INPUT_METHOD_ON_OFF);
632       map.put("cut", KeyEvent.VK_CUT);
633       map.put("copy", KeyEvent.VK_COPY);
634       map.put("paste", KeyEvent.VK_PASTE);
635       map.put("undo", KeyEvent.VK_UNDO);
636       map.put("again", KeyEvent.VK_AGAIN);
637       map.put("find", KeyEvent.VK_FIND);
638       map.put("props", KeyEvent.VK_PROPS);
639       map.put("stop", KeyEvent.VK_STOP);
640       map.put("compose", KeyEvent.VK_COMPOSE);
641       map.put("alt_graph", KeyEvent.VK_ALT_GRAPH);
642       map.put("begin", KeyEvent.VK_BEGIN);
643 
644       return map;
645    }
646 
647    private synchronized native void initializeLibrary() throws UnsatisfiedLinkError;
648 
649    private synchronized native void regHotKey(int identifier, int modifier, int keycode) throws UnsatisfiedLinkError;
650 
651    private synchronized native void terminate() throws UnsatisfiedLinkError;
652 
653    private synchronized native void unregHotKey(int identifier) throws UnsatisfiedLinkError;
654 
655    /**
656     * Checks if there's an instance with hidden window title = appName running
657     * Can be used to detect that another instance of your app is already running
658     * (so exit..)
659     * <p>
660     * @param appName = the title of the hidden window to search for
661     */
662    private synchronized native boolean isRunning(String appName);
663 }