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