← Back to team overview

sikuli-driver team mailing list archive

[Merge] lp:~vgod/sikuli/sandbox into lp:sikuli

 

Tsung-Hsiang Chang has proposed merging lp:~vgod/sikuli/sandbox into lp:sikuli.

Requested reviews:
  Sikuli Drivers (sikuli-driver)

For more details, see:
https://code.launchpad.net/~vgod/sikuli/sandbox/+merge/72322

This branch implements a global hotkey manager for all platforms(Win/Mac/Linux), which was requested in Bug #635806.

-- 
https://code.launchpad.net/~vgod/sikuli/sandbox/+merge/72322
Your team Sikuli Drivers is requested to review the proposed merge of lp:~vgod/sikuli/sandbox into lp:sikuli.
=== modified file 'sikuli-ide/CMakeLists.txt' (properties changed: -x to +x)
--- sikuli-ide/CMakeLists.txt	2011-08-01 19:38:29 +0000
+++ sikuli-ide/CMakeLists.txt	2011-08-21 03:38:17 +0000
@@ -43,8 +43,6 @@
 SET(SWING_LAYOUT_JAR "${COMMON_LIB_DIR}/swing-layout-1.0.1.jar")
 SET(COMMONS_CLI_JAR "${COMMON_LIB_DIR}/commons-cli-1.2.jar")
 SET(JSON_SIMPLE_JAR "${COMMON_LIB_DIR}/json_simple-1.1.jar")
-SET(JXGRABKEY_JAR "${COMMON_LIB_DIR}/jxgrabkey/lib/JXGrabKey.jar")
-SET(JINTELLITYPE_JAR "${COMMON_LIB_DIR}/jintellitype-1.3.6/jintellitype-1.3.6.jar")
 SET(SWINGX_JAR "${COMMON_LIB_DIR}/swingx-core-1.6.2.jar")
 SET(MAC_WIDGETS_JAR "${COMMON_LIB_DIR}/mac_widgets.jar")
 SET(FORMS_JAR "${COMMON_LIB_DIR}/forms-1.2.1.jar")
@@ -89,7 +87,7 @@
    SET(CLASSPATH ${CLASSPATH}${SEP}${JXGRABKEY_JAR})
 ELSEIF(WIN32)
    SET(MANIFEST ${RESOURCE_DIR}/META-INF/MANIFEST-win32.MF)
-   SET(CLASSPATH ${CLASSPATH}${SEP}${JINTELLITYPE_JAR})
+   #SET(CLASSPATH ${CLASSPATH}${SEP}${JINTELLITYPE_JAR})
 ELSE()
    SET(MANIFEST ${RESOURCE_DIR}/META-INF/MANIFEST.MF)
 ENDIF()
@@ -152,7 +150,7 @@
    SET(FRAMEWORKS_IN_APP_DIR  ${JAR_IN_APP_DIR}/libs)
    SET(JAR_LIB_DIR  ${JAR_DIR}/META-INF/lib)
 
-   LIST(APPEND INCLUDE_JARS ${JINTELLITYPE_JAR})
+   #LIST(APPEND INCLUDE_JARS ${JINTELLITYPE_JAR})
    FILE(GLOB sikuli_script_jnilibs "${BASE_DIR}/../sikuli-script/target/lib/*.dll")
    SET(jnilibs ${sikuli_script_jnilibs})
 ENDIF()
@@ -222,12 +220,6 @@
    else()
       SET(LIB_GRABKEY "${COMMON_LIB_DIR}/jxgrabkey/lib/libJXGrabKey-32.so")
    endif()
-   add_custom_target( ${JAR_FILE}.framework 
-      COMMAND ${CMAKE_COMMAND} -E make_directory ${JAR_LIB_DIR}
-      COMMAND cp ${LIB_GRABKEY} ${JAR_LIB_DIR}/libJXGrabKey.so
-      COMMENT "Packaging Frameworks in Jar"
-   )
-   add_dependencies( ${JAR_FILE} ${JAR_FILE}.framework ) 
 
    add_custom_target( ${APP_FILE}.framework 
       COMMAND ${CMAKE_COMMAND} -E make_directory ${FRAMEWORKS_IN_APP_DIR}
@@ -393,7 +385,7 @@
 add_dependencies( ${JAR_FILE}.prepare
         ${JAR_FILE}.resources
         ${JAR_FILE}.classes-in-jar 
-        ${JAR_FILE}.libs-in-jar
+        #        ${JAR_FILE}.libs-in-jar
 )
 
 add_dependencies( ${JAR_FILE} ${JAR_FILE}.prepare )
@@ -403,7 +395,7 @@
 
 
 ADD_SUBDIRECTORY( ${JAVA_SRC_DIR} )
-ADD_SUBDIRECTORY( ${NATIVE_SRC_DIR} )
+#ADD_SUBDIRECTORY( ${NATIVE_SRC_DIR} )
 ADD_SUBDIRECTORY( ${JAVA_TEST_DIR} )
 
 

=== modified file 'sikuli-ide/src/main/java/org/sikuli/ide/NativeLayer.java'
--- sikuli-ide/src/main/java/org/sikuli/ide/NativeLayer.java	2011-05-05 15:07:14 +0000
+++ sikuli-ide/src/main/java/org/sikuli/ide/NativeLayer.java	2011-08-21 03:38:17 +0000
@@ -3,13 +3,10 @@
  * Released under the MIT License.
  *
  */
-package org.sikuli.ide;
-
-public interface NativeLayer {
-   public void initApp();
-   public void initIDE(SikuliIDE ide);
-   public void installHotkey(int keyCode, int modifiers, 
-                              SikuliIDE ide, 
-                              String callbackMethod, String callbackType);
-}
-
+package org.sikuli.ide;
+
+public interface NativeLayer {
+   public void initApp();
+   public void initIDE(SikuliIDE ide);
+}
+

=== modified file 'sikuli-ide/src/main/java/org/sikuli/ide/NativeLayerForLinux.java'
--- sikuli-ide/src/main/java/org/sikuli/ide/NativeLayerForLinux.java	2011-05-05 15:07:14 +0000
+++ sikuli-ide/src/main/java/org/sikuli/ide/NativeLayerForLinux.java	2011-08-21 03:38:17 +0000
@@ -3,91 +3,14 @@
  * Released under the MIT License.
  *
  */
-package org.sikuli.ide;
-
-import java.lang.reflect.*;
-import java.awt.Event;
-import java.awt.event.*;
-import java.util.*;
-import java.io.*;
-import com.wapmx.nativeutils.jniloader.NativeLoader;
-import jxgrabkey.HotkeyConflictException;
-import jxgrabkey.HotkeyListener;
-import jxgrabkey.JXGrabKey;
-
-
-import org.sikuli.script.Debug;
-
-public class NativeLayerForLinux implements NativeLayer {
-   private Map<String, Integer> _callbackIdMap = new HashMap<String,Integer>();
-   private Map<Integer, String> _idCallbackMap = new HashMap<Integer,String>();
-
-   public void initApp(){}
-
-   public void initIDE(SikuliIDE ide){
-      try{
-         NativeLoader.loadLibrary("JXGrabKey");      
-      }
-      catch(IOException e){
-         Debug.error("Can't load native lib JXGrabKey");
-         e.printStackTrace();
-      }
-   }
-
-   public void installHotkey(int key, int mod, 
-                              final SikuliIDE ide, 
-			      final String callbackMethod, String callbackType){ 
-
-      String txtMod = KeyEvent.getKeyModifiersText(mod).toUpperCase();
-      txtMod = txtMod.replace("META","WIN");
-      txtMod = txtMod.replace("WINDOWS","WIN");
-      String txtCode = KeyEvent.getKeyText(key).toUpperCase();
-      Debug.info("install hotkey: " + txtMod + "+" + txtCode + 
-                   " for " + callbackMethod);
-
-      JXGrabKey grabKey = JXGrabKey.getInstance();
-      int id;
-      if( _callbackIdMap.containsKey(callbackMethod) ){
-         id = _callbackIdMap.get(callbackMethod);
-         grabKey.unregisterHotKey(id); 
-      }
-      else{
-         id = _callbackIdMap.size()+1;
-         _callbackIdMap.put(callbackMethod, id);
-         _idCallbackMap.put(id, callbackMethod);
-      }
-      try{
-         //JXGrabKey.setDebugOutput(true);
-         grabKey.registerAwtHotkey(id, mod, key);
-      }catch(HotkeyConflictException e){
-         Debug.error("Hot key conflicts");
-         grabKey.cleanUp(); 
-         return;
-      }
-				
-		//Implement HotkeyListener
-
-      if(_callbackIdMap.size()==1){
-         HotkeyListener hotkeyListener = new jxgrabkey.HotkeyListener(){
-            public void onHotkey(int id) {
-               Debug.log(2, "onHotkey: " + id);
-               String callbackFunc = _idCallbackMap.get(id);
-               Class params[] = {};
-               Object paramsObj[] = {};
-               Class cls = ide.getClass();
-               try{
-                  Method callback = cls.getDeclaredMethod(callbackFunc, params);
-                  callback.invoke(ide, paramsObj);
-               }
-               catch(Exception e){
-                  e.printStackTrace();
-               }
-            }
-         };
-         JXGrabKey.getInstance().addHotkeyListener(hotkeyListener);
-      }
-		
-   }
-}
-
-
+package org.sikuli.ide;
+
+public class NativeLayerForLinux implements NativeLayer {
+   public void initApp(){}
+
+   public void initIDE(SikuliIDE ide){
+   }
+
+}
+
+

=== modified file 'sikuli-ide/src/main/java/org/sikuli/ide/NativeLayerForMac.java'
--- sikuli-ide/src/main/java/org/sikuli/ide/NativeLayerForMac.java	2011-05-05 15:07:14 +0000
+++ sikuli-ide/src/main/java/org/sikuli/ide/NativeLayerForMac.java	2011-08-21 03:38:17 +0000
@@ -3,216 +3,75 @@
  * Released under the MIT License.
  *
  */
-package org.sikuli.ide;
-
-import java.awt.event.KeyEvent;
-import java.awt.event.InputEvent;
-import javax.swing.*;
-import java.io.IOException;
-import java.util.prefs.*;
-import com.apple.eawt.*;
-import com.wapmx.nativeutils.jniloader.NativeLoader;
-
-import org.sikuli.script.Debug;
-
-// http://lists.apple.com/archives/mac-games-dev/2001/Sep/msg00113.html
-// full key table: http://www.mactech.com/articles/mactech/Vol.04/04.12/Macinkeys/
-// modifiers code: http://www.mactech.com/macintosh-c/chap02-1.html
-
-public class NativeLayerForMac implements NativeLayer {
-   static final int CARBON_MASK_CMD = 0x0100;
-   static final int CARBON_MASK_SHIFT = 0x0200;
-   static final int CARBON_MASK_OPT = 0x0800;
-   static final int CARBON_MASK_CTRL = 0x1000;
-
-   static {
-      try{
-         NativeLoader.loadLibrary("NativeLayerForMac");      
-      }
-      catch(IOException e){
-         e.printStackTrace();
-      }
-   }
-
-   public void initIDE(final SikuliIDE ide){
-   }
-
-   public void initApp(){
-      Application app = Application.getApplication();
-      app.addPreferencesMenuItem();
-      app.setEnabledPreferencesMenu(true);
-      app.addApplicationListener(
-         new ApplicationAdapter() {
-            public void handleOpenApplication(ApplicationEvent event){
-               Debug.info("open application: Sikuli-IDE");
-               System.setProperty("apple.laf.useScreenMenuBar", "true");
-               System.setProperty("com.apple.mrj.application.apple.menu.about.name", "Sikuli IDE");
-            }
-
-            public void handleOpenFile(ApplicationEvent evt) {
-               final String fname = evt.getFilename();
-               Debug.log(1, "opening " + fname);
-               if(fname.endsWith(".skl")){
-                  SikuliIDE._runningSkl = true;
-                  Thread t = new Thread() {
-                     public void run() {
-                        try{
-                           SikuliIDE.runSkl(fname, null); 
-                        }
-                        catch(IOException e){
-                           e.printStackTrace();
-                        }
-                     }
-                  };
-                  t.setDaemon(false);
-                  t.start();
-               }
-               else if(fname.endsWith(".sikuli")){
-                  SikuliIDE ide = SikuliIDE.getInstance(null);
-                  ide.loadFile(fname);
-               }
-            }
-
-            public void handlePreferences(ApplicationEvent evt){
-               Debug.log(1, "opening preferences setting");
-               SikuliIDE ide = SikuliIDE.getInstance();
-               ide.showPreferencesWindow();
-            }
-
-            public void handleQuit(ApplicationEvent event){
-               SikuliIDE.getInstance().quit();
-            }
-         }
-      ); 
-   }
-
-   public void installHotkey(int key, int mod, 
-                              SikuliIDE ide, 
-                              String callbackMethod, String callbackType){
-      int ckey = convertToCarbonKey(key);
-      int cmod = convertToCarbonModifiers(mod);
-      Debug.log(2, "register hotkey Java:" + key + "(" +mod+ 
-                   ") Carbon: " + ckey + "(" + cmod + ")");
-      installGlobalHotkey(ckey, cmod, ide, 
-                          callbackMethod, callbackType);
-   }
-
-   private native void installGlobalHotkey(int keyCode, int modifiers, 
-                              SikuliIDE ide, 
-                              String callbackMethod, String callbackType);
-
-   private int convertToCarbonModifiers(int mod){
-      int cmod = 0;
-      if((mod&InputEvent.SHIFT_MASK) != 0) cmod |= CARBON_MASK_SHIFT;
-      if((mod&InputEvent.META_MASK) != 0) cmod |= CARBON_MASK_CMD;
-      if((mod&InputEvent.ALT_MASK) != 0) cmod |= CARBON_MASK_OPT;
-      if((mod&InputEvent.CTRL_MASK) != 0) cmod |= CARBON_MASK_CTRL;
-      return cmod;
-   }
-
-   private int convertToCarbonKey(int keycode){
-      switch(keycode){
-         case KeyEvent.VK_BACK_SPACE: return 0x33;
-         case KeyEvent.VK_TAB: return 0x30;
-         case KeyEvent.VK_CLEAR: return 0x47;
-         case KeyEvent.VK_ENTER: return 0x24;
-         case KeyEvent.VK_SHIFT: return 0xF0;
-         case KeyEvent.VK_CONTROL: return 0xF1;
-         case KeyEvent.VK_META: return 0xF2; 
-         case KeyEvent.VK_PAUSE: return 0x71; // = F15
-         case KeyEvent.VK_ESCAPE: return 0x35;
-         case KeyEvent.VK_SPACE: return 0x31;
-         case KeyEvent.VK_OPEN_BRACKET: return 0x21;
-         case KeyEvent.VK_BACK_SLASH: return 0x2A;
-         case KeyEvent.VK_CLOSE_BRACKET: return 0x1E;
-         case KeyEvent.VK_SLASH: return 0x2C;
-         case KeyEvent.VK_PERIOD: return 0x2F;
-         case KeyEvent.VK_COMMA: return 0x2B;
-         case KeyEvent.VK_SEMICOLON: return 0x29;
-         case KeyEvent.VK_END: return 0x77;
-         case KeyEvent.VK_HOME: return 0x73;
-         case KeyEvent.VK_LEFT: return 0x7B;
-         case KeyEvent.VK_UP: return 0x7E;
-         case KeyEvent.VK_RIGHT: return 0x7C;
-         case KeyEvent.VK_DOWN: return 0x7D;
-         case KeyEvent.VK_PRINTSCREEN: return 0x69; // F13
-         case KeyEvent.VK_INSERT: return 0x72; // help
-         case KeyEvent.VK_DELETE: return 0x75;
-         case KeyEvent.VK_HELP: return 0x72;
-         case KeyEvent.VK_0: return 0x1D;
-         case KeyEvent.VK_1: return 0x12;
-         case KeyEvent.VK_2: return 0x13;
-         case KeyEvent.VK_3: return 0x14;
-         case KeyEvent.VK_4: return 0x15;
-         case KeyEvent.VK_5: return 0x17;
-         case KeyEvent.VK_6: return 0x16;
-         case KeyEvent.VK_7: return 0x1A;
-         case KeyEvent.VK_8: return 0x1C;
-         case KeyEvent.VK_9: return 0x19;
-         case KeyEvent.VK_MINUS: return 0x1B;
-         case KeyEvent.VK_EQUALS: return 0x18;
-         case KeyEvent.VK_A: return 0x00;
-         case KeyEvent.VK_B: return 0x0B;
-         case KeyEvent.VK_C: return 0x08;
-         case KeyEvent.VK_D: return 0x02;
-         case KeyEvent.VK_E: return 0x0E;
-         case KeyEvent.VK_F: return 0x03;
-         case KeyEvent.VK_G: return 0x05;
-         case KeyEvent.VK_H: return 0x04;
-         case KeyEvent.VK_I: return 0x22;
-         case KeyEvent.VK_J: return 0x26;
-         case KeyEvent.VK_K: return 0x28;
-         case KeyEvent.VK_L: return 0x25;
-         case KeyEvent.VK_M: return 0x2E;
-         case KeyEvent.VK_N: return 0x2D;
-         case KeyEvent.VK_O: return 0x1F;
-         case KeyEvent.VK_P: return 0x23;
-         case KeyEvent.VK_Q: return 0x0C;
-         case KeyEvent.VK_R: return 0x0F;
-         case KeyEvent.VK_S: return 0x01;
-         case KeyEvent.VK_T: return 0x11;
-         case KeyEvent.VK_U: return 0x20;
-         case KeyEvent.VK_V: return 0x09;
-         case KeyEvent.VK_W: return 0x0D;
-         case KeyEvent.VK_X: return 0x07;
-         case KeyEvent.VK_Y: return 0x10;
-         case KeyEvent.VK_Z: return 0x06;
-         case KeyEvent.VK_NUMPAD0: return 0x52;
-         case KeyEvent.VK_NUMPAD1: return 0x53;
-         case KeyEvent.VK_NUMPAD2: return 0x54;
-         case KeyEvent.VK_NUMPAD3: return 0x55;
-         case KeyEvent.VK_NUMPAD4: return 0x56;
-         case KeyEvent.VK_NUMPAD5: return 0x57;
-         case KeyEvent.VK_NUMPAD6: return 0x58;
-         case KeyEvent.VK_NUMPAD7: return 0x59;
-         case KeyEvent.VK_NUMPAD8: return 0x5B;
-         case KeyEvent.VK_NUMPAD9: return 0x5C;
-         case KeyEvent.VK_MULTIPLY: return 0x43;
-         case KeyEvent.VK_ADD: return 0x45;
-         case KeyEvent.VK_SEPARATOR: return 0xFF; // not supported with Button or GetKeys
-         case KeyEvent.VK_SUBTRACT: return 0x4E;
-         case KeyEvent.VK_DECIMAL: return 0x41;
-         case KeyEvent.VK_DIVIDE: return 0x4B;
-         case KeyEvent.VK_F1: return 0x7A;
-         case KeyEvent.VK_F2: return 0x7B;
-         case KeyEvent.VK_F3: return 0x63;
-         case KeyEvent.VK_F4: return 0x76;
-         case KeyEvent.VK_F5: return 0x60;
-         case KeyEvent.VK_F6: return 0x61;
-         case KeyEvent.VK_F7: return 0x62;
-         case KeyEvent.VK_F8: return 0x64;
-         case KeyEvent.VK_F9: return 0x65;
-         case KeyEvent.VK_F10: return 0x6D;
-         case KeyEvent.VK_F11: return 0x67;
-         case KeyEvent.VK_F12: return 0x6F;
-         case KeyEvent.VK_F13: return 0x69;
-         case KeyEvent.VK_F14: return 0x6B;
-         case KeyEvent.VK_F15: return 0x71;
-         case KeyEvent.VK_NUM_LOCK: return 0x47;
-         default: return 0xFF;
-      }
-
-   }
-}
-
-
+package org.sikuli.ide;
+
+import java.awt.event.KeyEvent;
+import java.awt.event.InputEvent;
+import javax.swing.*;
+import java.io.IOException;
+import java.util.prefs.*;
+import com.apple.eawt.*;
+
+import org.sikuli.script.Debug;
+
+// http://lists.apple.com/archives/mac-games-dev/2001/Sep/msg00113.html
+// full key table: http://www.mactech.com/articles/mactech/Vol.04/04.12/Macinkeys/
+// modifiers code: http://www.mactech.com/macintosh-c/chap02-1.html
+
+public class NativeLayerForMac implements NativeLayer {
+
+   public void initIDE(final SikuliIDE ide){
+   }
+
+   public void initApp(){
+      Application app = Application.getApplication();
+      app.addPreferencesMenuItem();
+      app.setEnabledPreferencesMenu(true);
+      app.addApplicationListener(
+         new ApplicationAdapter() {
+            public void handleOpenApplication(ApplicationEvent event){
+               Debug.info("open application: Sikuli-IDE");
+               System.setProperty("apple.laf.useScreenMenuBar", "true");
+               System.setProperty("com.apple.mrj.application.apple.menu.about.name", "Sikuli IDE");
+            }
+
+            public void handleOpenFile(ApplicationEvent evt) {
+               final String fname = evt.getFilename();
+               Debug.log(1, "opening " + fname);
+               if(fname.endsWith(".skl")){
+                  SikuliIDE._runningSkl = true;
+                  Thread t = new Thread() {
+                     public void run() {
+                        try{
+                           SikuliIDE.runSkl(fname, null); 
+                        }
+                        catch(IOException e){
+                           e.printStackTrace();
+                        }
+                     }
+                  };
+                  t.setDaemon(false);
+                  t.start();
+               }
+               else if(fname.endsWith(".sikuli")){
+                  SikuliIDE ide = SikuliIDE.getInstance(null);
+                  ide.loadFile(fname);
+               }
+            }
+
+            public void handlePreferences(ApplicationEvent evt){
+               Debug.log(1, "opening preferences setting");
+               SikuliIDE ide = SikuliIDE.getInstance();
+               ide.showPreferencesWindow();
+            }
+
+            public void handleQuit(ApplicationEvent event){
+               SikuliIDE.getInstance().quit();
+            }
+         }
+      ); 
+   }
+
+}
+
+

=== modified file 'sikuli-ide/src/main/java/org/sikuli/ide/NativeLayerForWindows.java'
--- sikuli-ide/src/main/java/org/sikuli/ide/NativeLayerForWindows.java	2011-05-05 15:07:14 +0000
+++ sikuli-ide/src/main/java/org/sikuli/ide/NativeLayerForWindows.java	2011-08-21 03:38:17 +0000
@@ -3,71 +3,15 @@
  * Released under the MIT License.
  *
  */
-package org.sikuli.ide;
-
-import java.lang.reflect.*;
-import java.awt.Event;
-import java.awt.event.*;
-import java.util.*;
-import com.melloware.jintellitype.HotkeyListener;
-import com.melloware.jintellitype.IntellitypeListener;
-import com.melloware.jintellitype.JIntellitype;
-
-import org.sikuli.script.Debug;
-
-public class NativeLayerForWindows implements NativeLayer {
-   private Map<String, Integer> _callbackIdMap = new HashMap<String,Integer>();
-   private Map<Integer, String> _idCallbackMap = new HashMap<Integer,String>();
-
-   public void initApp(){
-   }
-   public void initIDE(SikuliIDE ide){
-   }
-
-   public void installHotkey(int key, int mod, 
-                              final SikuliIDE ide, 
-                              final String callbackMethod, String callbackType){
-      JIntellitype itype = JIntellitype.getInstance();
-      String txtMod = KeyEvent.getKeyModifiersText(mod).toUpperCase();
-      txtMod = txtMod.replace("META","WIN");
-      txtMod = txtMod.replace("WINDOWS","WIN");
-      String txtCode = KeyEvent.getKeyText(key).toUpperCase();
-      Debug.info("install hotkey: " + txtMod + "+" + txtCode + 
-                   " for " + callbackMethod);
-
-      int id;
-      if( _callbackIdMap.containsKey(callbackMethod) ){
-         id = _callbackIdMap.get(callbackMethod);
-         itype.unregisterHotKey(id);
-      }
-      else{
-         id = _callbackIdMap.size()+1;
-         _callbackIdMap.put(callbackMethod, id);
-         _idCallbackMap.put(id, callbackMethod);
-      }
-
-      //itype.registerHotKey(id, txtMod + "+" + txtCode);
-      itype.registerSwingHotKey(id, mod, key);
-      //Debug.log(1, "[WIN] " + callbackMethod + " " + id);
-      if(_callbackIdMap.size()==1){
-         itype.addHotKeyListener(new HotkeyListener(){
-            public void onHotKey(int id){
-               Debug.log(2, "Hotkey pressed");
-               String callbackFunc = _idCallbackMap.get(id);
-               Class params[] = {};
-               Object paramsObj[] = {};
-               Class cls = ide.getClass();
-               try{
-                  Method callback = cls.getDeclaredMethod(callbackFunc, params);
-                  callback.invoke(ide, paramsObj);
-               }
-               catch(Exception e){
-                  e.printStackTrace();
-               }
-            }
-         });
-      }
-   }
-}
-
-
+package org.sikuli.ide;
+
+
+public class NativeLayerForWindows implements NativeLayer {
+   public void initApp(){
+   }
+   public void initIDE(SikuliIDE ide){
+   }
+
+}
+
+

=== modified file 'sikuli-ide/src/main/java/org/sikuli/ide/PreferencesWin.java'
--- sikuli-ide/src/main/java/org/sikuli/ide/PreferencesWin.java	2011-05-05 15:07:14 +0000
+++ sikuli-ide/src/main/java/org/sikuli/ide/PreferencesWin.java	2011-08-21 03:38:17 +0000
@@ -93,7 +93,7 @@
             _radOCR.isSelected()?UserPreferences.AUTO_NAMING_OCR:
             UserPreferences.AUTO_NAMING_OFF);
       if(_old_cap_hkey != _cap_hkey || _old_cap_mod != _cap_mod){
-         //FIXME: remove the old hotkey
+         ide.removeCaptureHotkey(_old_cap_hkey, _old_cap_mod);
          ide.installCaptureHotkey(_cap_hkey, _cap_mod);
       }
       pref.setCheckUpdate(_chkAutoUpdate.isSelected());

=== modified file 'sikuli-ide/src/main/java/org/sikuli/ide/SikuliIDE.java'
--- sikuli-ide/src/main/java/org/sikuli/ide/SikuliIDE.java	2011-05-05 15:07:14 +0000
+++ sikuli-ide/src/main/java/org/sikuli/ide/SikuliIDE.java	2011-08-21 03:38:17 +0000
@@ -39,6 +39,9 @@
 import org.sikuli.script.ScreenImage;
 import org.sikuli.script.Observer;
 import org.sikuli.script.Subject;
+import org.sikuli.script.Env;
+import org.sikuli.script.HotkeyListener;
+import org.sikuli.script.HotkeyEvent;
 
 import org.apache.commons.cli.CommandLine;
 
@@ -744,9 +747,16 @@
    public boolean isInited(){ return _inited; }
 
 
+   public void removeCaptureHotkey(int key, int mod){
+      Env.removeHotkey(key, mod);
+   }
+
    public void installCaptureHotkey(int key, int mod){
-      _native.installHotkey(key, mod, this, 
-                          "onQuickCapture", "(Ljava/lang/String;)V");
+      Env.addHotkey(key, mod, new HotkeyListener(){
+         public void hotkeyPressed(HotkeyEvent e){
+            onQuickCapture();
+         }
+      });
    }
 
    private void initHotkeys(){
@@ -756,8 +766,11 @@
       installCaptureHotkey(key, mod);
       key = pref.getStopHotkey();
       mod = pref.getStopHotkeyModifiers();
-      _native.installHotkey(key, mod, this, 
-                          "onStopRunning", "()V");
+      Env.addHotkey(key, mod, new HotkeyListener(){
+         public void hotkeyPressed(HotkeyEvent e){
+            onStopRunning();
+         }
+      });
    }
 
    static private void initNativeLayer(){

=== removed file 'sikuli-ide/src/main/native/CMakeLists.txt'
--- sikuli-ide/src/main/native/CMakeLists.txt	2011-05-05 15:07:14 +0000
+++ sikuli-ide/src/main/native/CMakeLists.txt	1970-01-01 00:00:00 +0000
@@ -1,65 +0,0 @@
-# Copyright 2010-2011, Sikuli.org
-# Released under the MIT License.
-ENABLE_LANGUAGE(CXX)
-
-SET(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../../cmake_modules/")
-INCLUDE("${CMAKE_MODULE_PATH}/common.cmake")
-
-FIND_PROGRAM ( JAVA_JAVA_H javah PATHS ${JAVA_BIN_PATH} )
-SET(JNI_HEADERS
-   org_sikuli_ide_NativeLayerForMac.h
-)
-
-SET(LIBRARY_OUTPUT_PATH ${BINARY_LIB_DIR})
-
-FIND_PACKAGE(JNI REQUIRED)
-IF(APPLE)
-   FIND_LIBRARY(CARBON_LIBRARY Carbon)
-   FIND_LIBRARY(COREFOUNDATION_LIBRARY CoreFoundation)
-   SET(EXTRA_LIBS ${CARBON_LIBRARY} ${COREFOUNDATION_LIBRARY})
-ENDIF(APPLE)
-
-INCLUDE_DIRECTORIES(${JNI_INCLUDE_DIRS})
-
-
-IF(APPLE)
-   SET(BUILD_TARGETS
-      NativeLayerForMac
-   )
-
-   ADD_LIBRARY(NativeLayerForMac SHARED
-      NativeLayerForMac.cc
-      org_sikuli_ide_NativeLayerForMac.h
-   )
-
-ENDIF(APPLE)
-
-foreach(JNI_HEADER ${JNI_HEADERS})
-   STRING(REGEX REPLACE "_" "." JNI_CLASS ${JNI_HEADER})
-   STRING(REGEX REPLACE "\\.h$" "" JNI_CLASS ${JNI_CLASS})
-   STRING(REGEX REPLACE "\\." "/" JNI_JAVA_SOURCE ${JNI_CLASS})
-   SET(JNI_JAVA_SOURCE "${JNI_JAVA_SOURCE}.java")
-   ADD_CUSTOM_COMMAND(
-      OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${JNI_HEADER}
-      COMMAND ${JAVA_JAVA_H} -d ${CMAKE_CURRENT_SOURCE_DIR} 
-                             -classpath ${BINARY_CLASS_DIR} ${JNI_CLASS}
-      DEPENDS ${JAVA_SRC_DIR}/${JNI_JAVA_SOURCE}
-   )
-endforeach(JNI_HEADER ${JNI_HEADERS})
-
-
-foreach(BUILD_TARGET ${BUILD_TARGETS})
-   TARGET_LINK_LIBRARIES(${BUILD_TARGET} ${EXTRA_LIBS})
-   if(APPLE)
-      set_target_properties(${BUILD_TARGET} PROPERTIES SUFFIX ".jnilib")
-   endif(APPLE)
-endforeach(BUILD_TARGET ${BUILD_TARGETS})
-
-add_custom_target(${JAR_FILE}.libs-in-jar
-   COMMAND ${CMAKE_COMMAND} -E make_directory ${JAR_DIR}/META-INF
-   COMMAND ${CMAKE_COMMAND} -E copy_directory ${BINARY_LIB_DIR} ${JAR_DIR}/META-INF/lib
-)
-
-IF(EXISTS ${BUILD_TARGETS})
-   add_dependencies(${JAR_FILE}.libs-in-jar ${BUILD_TARGETS})
-ENDIF()

=== removed file 'sikuli-ide/src/main/native/NativeLayerForMac.cc'
--- sikuli-ide/src/main/native/NativeLayerForMac.cc	2011-05-05 15:07:14 +0000
+++ sikuli-ide/src/main/native/NativeLayerForMac.cc	1970-01-01 00:00:00 +0000
@@ -1,114 +0,0 @@
-/*
- * Copyright 2010-2011, Sikuli.org
- * Released under the MIT License.
- *
- */
-#include "org_sikuli_ide_NativeLayerForMac.h"
-
-#include <Carbon/Carbon.h>
-#include <CoreFoundation/CoreFoundation.h>
-
-#include<iostream>
-#include<map>
-
-using namespace std;
-
-struct CallbackData {
-   JavaVM *vm;
-   int hotkey, mods;
-   const char *func, *func_t;
-   jobject cls;
-
-   CallbackData(JavaVM *vm_, int hotkey_, int mods_, const char* func_, 
-                const char* func_t_, jobject cls_){
-      vm = vm_;
-      hotkey = hotkey_;
-      mods = mods_;
-      func = func_;
-      func_t = func_t_;
-      cls = cls_;
-   }
-};
-
-
-void callJavaMethod(JavaVM *jvm, jobject obj, 
-                    const char* func, const char* func_t){
-   JNIEnv *env;
-   jvm->GetEnv((void**)&env, JNI_VERSION_1_4);
-   jvm->AttachCurrentThread((void **)&env, NULL);
-   jclass cls = env->GetObjectClass(obj);
-   jmethodID mid = env->GetMethodID(cls, func, func_t);
-   if( mid == NULL ){
-      cerr << "Callback method " << func << " not found." << endl;
-      return;
-   }
-   env->CallVoidMethod(obj, mid);
-}
-
-OSStatus shortcutHandler( EventHandlerCallRef inCaller, EventRef inEvent, 
-                          void* inIdDataMap )
-{
-   EventHotKeyID hkId;
-   GetEventParameter(inEvent, kEventParamDirectObject, typeEventHotKeyID, NULL,
-                     sizeof(hkId), NULL, &hkId);
-   map<int,CallbackData*> 
-      *idDataMap = reinterpret_cast<map<int,CallbackData*>*>(inIdDataMap);
-   CallbackData *data = (*idDataMap)[hkId.id];
-   int hotkey = data->hotkey;
-   cout << "[JNI] shortcut pressed. " << hotkey << endl;
-   callJavaMethod(data->vm, data->cls, data->func, data->func_t);
-   return noErr;
-}
-
-void installShortcutHandler( CallbackData *data ){
-   static map<string, int> callbackIdMap;
-   static map<int, CallbackData*> idDataMap;
-   static map<string, EventHotKeyRef> callbackRefMap;
-   EventTypeSpec shortcutEvents[] = {
-      { kEventClassKeyboard, kEventHotKeyPressed },
-   };
-   EventHotKeyRef myHotKeyRef;
-   EventHotKeyID myHotKeyID;
-   myHotKeyID.signature='htk1';
-   map<string, int>::iterator it;
-   if( (it=callbackIdMap.find(data->func)) == callbackIdMap.end() ){
-      myHotKeyID.id = callbackIdMap.size()+1;
-      callbackIdMap[data->func] = myHotKeyID.id;
-      cout << "[JNI] " << data->func << " id: " << myHotKeyID.id << endl;
-   }
-   else{
-      myHotKeyID.id = it->second;
-      myHotKeyRef = callbackRefMap[data->func];
-      UnregisterEventHotKey(myHotKeyRef);
-   }
-   idDataMap[myHotKeyID.id] = data;
-
-   if(callbackIdMap.size() == 1){
-      InstallApplicationEventHandler(&shortcutHandler, 1, shortcutEvents,
-                                     &idDataMap, NULL);
-   }
-
-   OSStatus err = RegisterEventHotKey(data->hotkey, data->mods,
-                      myHotKeyID, GetApplicationEventTarget(), 0, 
-                      &myHotKeyRef);
-   
-   if(err)
-      cerr << "Error registering shortcut handler.. " << err << endl;
-   else
-      callbackRefMap[data->func] = myHotKeyRef;
-}
-
-JNIEXPORT void JNICALL Java_org_sikuli_ide_NativeLayerForMac_installGlobalHotkey (JNIEnv *env, jobject jobj, jint hotkey, jint modifiers, jobject jIde, 
-       jstring jCallbackName, jstring jCallbackType){
-
-   cout << "[JNI] install global hotkey: " << hotkey << endl;
-   const char *cName = env->GetStringUTFChars(jCallbackName, NULL);
-   const char *cType = env->GetStringUTFChars(jCallbackType, NULL);
-   JavaVM* vm = NULL;
-   env->GetJavaVM(&vm);
-   jobject ide = env->NewGlobalRef(jIde);
-   env->DeleteLocalRef(jIde);
-   CallbackData *data = new CallbackData(vm, hotkey, modifiers, 
-                                         cName, cType, ide);
-   installShortcutHandler(data);
-}

=== removed file 'sikuli-ide/src/main/native/org_sikuli_ide_NativeLayerForMac.h'
--- sikuli-ide/src/main/native/org_sikuli_ide_NativeLayerForMac.h	2011-05-30 19:54:10 +0000
+++ sikuli-ide/src/main/native/org_sikuli_ide_NativeLayerForMac.h	1970-01-01 00:00:00 +0000
@@ -1,29 +0,0 @@
-/* DO NOT EDIT THIS FILE - it is machine generated */
-#include <jni.h>
-/* Header for class org_sikuli_ide_NativeLayerForMac */
-
-#ifndef _Included_org_sikuli_ide_NativeLayerForMac
-#define _Included_org_sikuli_ide_NativeLayerForMac
-#ifdef __cplusplus
-extern "C" {
-#endif
-#undef org_sikuli_ide_NativeLayerForMac_CARBON_MASK_CMD
-#define org_sikuli_ide_NativeLayerForMac_CARBON_MASK_CMD 256L
-#undef org_sikuli_ide_NativeLayerForMac_CARBON_MASK_SHIFT
-#define org_sikuli_ide_NativeLayerForMac_CARBON_MASK_SHIFT 512L
-#undef org_sikuli_ide_NativeLayerForMac_CARBON_MASK_OPT
-#define org_sikuli_ide_NativeLayerForMac_CARBON_MASK_OPT 2048L
-#undef org_sikuli_ide_NativeLayerForMac_CARBON_MASK_CTRL
-#define org_sikuli_ide_NativeLayerForMac_CARBON_MASK_CTRL 4096L
-/*
- * Class:     org_sikuli_ide_NativeLayerForMac
- * Method:    installGlobalHotkey
- * Signature: (IILorg/sikuli/ide/SikuliIDE;Ljava/lang/String;Ljava/lang/String;)V
- */
-JNIEXPORT void JNICALL Java_org_sikuli_ide_NativeLayerForMac_installGlobalHotkey
-  (JNIEnv *, jobject, jint, jint, jobject, jstring, jstring);
-
-#ifdef __cplusplus
-}
-#endif
-#endif

=== modified file 'sikuli-script/CMakeLists.txt'
--- sikuli-script/CMakeLists.txt	2011-08-03 19:47:33 +0000
+++ sikuli-script/CMakeLists.txt	2011-08-21 03:38:17 +0000
@@ -36,13 +36,20 @@
 SET(CMAKE_SWIG_OUTDIR ${CMAKE_BINARY_DIR}/${NATIVE_PACKAGE_DIR})
 
 SET(COMMON_LIB_DIR "${BASE_DIR}/../lib")
+
+# dependent libs
+SET(JXGRABKEY_JAR "${COMMON_LIB_DIR}/jxgrabkey/lib/JXGrabKey.jar")
+SET(JINTELLITYPE_JAR "${COMMON_LIB_DIR}/jintellitype-1.3.6/jintellitype-1.3.6.jar")
+
 #SET(INCLUDE_LIB_DIR "${BASE_DIR}/lib")
 SET(INCLUDE_LIB_DIR "${COMMON_LIB_DIR}/mx-native-loader-1.2/target/classes")
+
 SET(BINARY_DIR "${BASE_DIR}/target")
 SET(BINARY_CLASS_DIR "${BINARY_DIR}/classes")
 SET(TEST_CLASS_DIR "${CMAKE_BINARY_DIR}/test")
 SET(BINARY_LIB_DIR "${BINARY_DIR}/lib")
 SET(JAR_DIR "${BINARY_DIR}/jar")
+SET(JAR_LIB_DIR  ${JAR_DIR}/META-INF/lib)
 
 SET(JUNIT_JAR "${COMMON_LIB_DIR}/junit-4.8.2.jar")
 SET(MOCKITO_JAR "${COMMON_LIB_DIR}/mockito-all-1.8.5.jar")
@@ -53,6 +60,13 @@
    ${INCLUDE_LIB_DIR}${SEP}${JYTHON_JAR}${SEP}.
 )
 
+IF(LINUX)
+   SET(CLASSPATH ${CLASSPATH}${SEP}${JXGRABKEY_JAR})
+ELSEIF(WIN32)
+   SET(CLASSPATH ${CLASSPATH}${SEP}${JINTELLITYPE_JAR})
+ENDIF()
+
+
 SET(TEST_CLASSPATH
    ${CLASSPATH}${SEP}${JUNIT_JAR}${SEP}${BINARY_CLASS_DIR}${SEP}${MOCKITO_JAR}
 )
@@ -83,6 +97,7 @@
    )
 ENDIF()
 
+
 SET(LIB_IN_JAR_DIR "${JAR_DIR}/Lib")
 add_custom_target( ${JAR_FILE}.python-src-in-jar
    COMMAND ${CMAKE_COMMAND} -E make_directory ${LIB_IN_JAR_DIR}
@@ -132,6 +147,12 @@
       COMMENT "Update version number to ${VERSION}"
 )
 
+add_custom_target(${JAR_FILE}.libs-in-jar
+   COMMAND ${CMAKE_COMMAND} -E make_directory ${JAR_DIR}/META-INF
+   COMMAND ${CMAKE_COMMAND} -E copy_directory ${BINARY_LIB_DIR} ${JAR_LIB_DIR}
+)
+
+
 # Dependencies
 
 add_dependencies(${JAR_FILE} 
@@ -144,6 +165,54 @@
         ${JAR_FILE}.jython-libs-in-jar
 )
 
+IF(WIN32)
+   IF(NOT EXISTS ${JAR_DIR}/com/melloware/jintellitype)
+      add_custom_target( ${JAR_FILE}.jintellitype-in-jar
+         COMMAND ${CMAKE_COMMAND} -E chdir ${JAR_DIR} ${JAVA_ARCHIVE} xf ${JINTELLITYPE_JAR}
+         COMMENT "Merging Jintellitype's JAR"
+      )
+   ENDIF()
+   add_dependencies(${JAR_FILE} 
+      ${JAR_FILE}.jintellitype-in-jar
+   )
+
+   SET(LIB_GRABKEY "${COMMON_LIB_DIR}/jintellitype-1.3.6/JIntellitype.dll")
+   add_custom_target( ${JAR_FILE}.hotkey-lib 
+      COMMAND ${CMAKE_COMMAND} -E copy ${LIB_GRABKEY} ${BINARY_LIB_DIR}/
+      COMMENT "Packaging JIntellitype in Jar"
+   )
+
+ENDIF(WIN32)
+
+IF(LINUX)
+   IF(NOT EXISTS ${JAR_DIR}/jxgrabkey)
+      add_custom_target( ${JAR_FILE}.jxgrabkey-in-jar
+         COMMAND ${CMAKE_COMMAND} -E chdir ${JAR_DIR} ${JAVA_ARCHIVE} xf ${JXGRABKEY_JAR}
+         COMMENT "Merging JXGrabKey's JAR"
+      )
+   ENDIF()
+
+   add_dependencies(${JAR_FILE} 
+      ${JAR_FILE}.jxgrabkey-in-jar
+   )
+ENDIF(LINUX)
+
+IF(LINUX)
+   EXEC_PROGRAM(uname ARGS -m OUTPUT_VARIABLE SYSTEM_ARCH)
+   if(SYSTEM_ARCH MATCHES "x86_64")
+      SET(LIB_GRABKEY "${COMMON_LIB_DIR}/jxgrabkey/lib/libJXGrabKey-64.so")
+   else()
+      SET(LIB_GRABKEY "${COMMON_LIB_DIR}/jxgrabkey/lib/libJXGrabKey-32.so")
+   endif()
+   add_custom_target( ${JAR_FILE}.hotkey-lib 
+      COMMAND cp ${LIB_GRABKEY} ${BINARY_LIB_DIR}/libJXGrabKey.so
+      COMMENT "Packaging JXGrabKey in Jar"
+   )
+ENDIF(LINUX)
+add_dependencies( ${JAR_FILE} ${JAR_FILE}.hotkey-lib ) 
+add_dependencies( ${JAR_FILE}.libs-in-jar ${JAR_FILE}.hotkey-lib ) 
+
+
 ADD_SUBDIRECTORY(${NATIVE_SRC_DIR})
 ADD_SUBDIRECTORY(${JAVA_SRC_DIR})
 ADD_SUBDIRECTORY(${JAVA_TEST_DIR})

=== modified file 'sikuli-script/hudson-test.xml'
--- sikuli-script/hudson-test.xml	2011-05-30 18:13:11 +0000
+++ sikuli-script/hudson-test.xml	2011-08-21 03:38:17 +0000
@@ -15,6 +15,7 @@
       <pathelement location="target/sikuli-script.jar" />
    </path>
    <path id="classpath.test">
+      <pathelement location="../lib/jintellitype-1.3.6/jintellitype-1.3.6.jar" />
       <pathelement location="../lib/junit-4.8.2.jar" />
       <pathelement location="../lib/mockito-all-1.8.5.jar" />
       <pathelement location="${tst-dir}" />

=== modified file 'sikuli-script/src/main/java/CMakeLists.txt'
--- sikuli-script/src/main/java/CMakeLists.txt	2011-06-28 22:11:24 +0000
+++ sikuli-script/src/main/java/CMakeLists.txt	2011-08-21 03:38:17 +0000
@@ -1,11 +1,20 @@
 # Copyright 2010-2011, Sikuli.org
 # Released under the MIT License.
 IF(APPLE)
-   SET(OS_EXTRA_SOURCE_FILES org/sikuli/script/MacUtil.java)
+   SET(OS_EXTRA_SOURCE_FILES 
+      org/sikuli/script/MacUtil.java
+      org/sikuli/script/internal/hotkey/MacHotkeyManager.java
+   )
 ELSEIF(LINUX)
-   SET(OS_EXTRA_SOURCE_FILES org/sikuli/script/LinuxUtil.java)
+   SET(OS_EXTRA_SOURCE_FILES 
+      org/sikuli/script/LinuxUtil.java
+      org/sikuli/script/internal/hotkey/LinuxHotkeyManager.java
+      )
 ELSEIF(WIN32)
-   SET(OS_EXTRA_SOURCE_FILES org/sikuli/script/Win32Util.java)
+   SET(OS_EXTRA_SOURCE_FILES 
+      org/sikuli/script/Win32Util.java
+      org/sikuli/script/internal/hotkey/WindowsHotkeyManager.java
+      )
 ENDIF()
 
 
@@ -56,6 +65,7 @@
    org/sikuli/script/KeyModifier.java
    org/sikuli/script/Button.java
    org/sikuli/script/Constants.java
+   org/sikuli/script/internal/hotkey/HotkeyManager.java
 )
 
 add_custom_target( ${PROJECT_NAME}.classes

=== modified file 'sikuli-script/src/main/java/org/sikuli/script/DesktopRobot.java'
--- sikuli-script/src/main/java/org/sikuli/script/DesktopRobot.java	2011-07-28 05:18:05 +0000
+++ sikuli-script/src/main/java/org/sikuli/script/DesktopRobot.java	2011-08-21 03:38:17 +0000
@@ -115,159 +115,7 @@
    }
 
    public void typeChar(char character, KeyMode mode) {
-      switch (character) {
-         case 'a': doType(mode,KeyEvent.VK_A); break;
-         case 'b': doType(mode,KeyEvent.VK_B); break;
-         case 'c': doType(mode,KeyEvent.VK_C); break;
-         case 'd': doType(mode,KeyEvent.VK_D); break;
-         case 'e': doType(mode,KeyEvent.VK_E); break;
-         case 'f': doType(mode,KeyEvent.VK_F); break;
-         case 'g': doType(mode,KeyEvent.VK_G); break;
-         case 'h': doType(mode,KeyEvent.VK_H); break;
-         case 'i': doType(mode,KeyEvent.VK_I); break;
-         case 'j': doType(mode,KeyEvent.VK_J); break;
-         case 'k': doType(mode,KeyEvent.VK_K); break;
-         case 'l': doType(mode,KeyEvent.VK_L); break;
-         case 'm': doType(mode,KeyEvent.VK_M); break;
-         case 'n': doType(mode,KeyEvent.VK_N); break;
-         case 'o': doType(mode,KeyEvent.VK_O); break;
-         case 'p': doType(mode,KeyEvent.VK_P); break;
-         case 'q': doType(mode,KeyEvent.VK_Q); break;
-         case 'r': doType(mode,KeyEvent.VK_R); break;
-         case 's': doType(mode,KeyEvent.VK_S); break;
-         case 't': doType(mode,KeyEvent.VK_T); break;
-         case 'u': doType(mode,KeyEvent.VK_U); break;
-         case 'v': doType(mode,KeyEvent.VK_V); break;
-         case 'w': doType(mode,KeyEvent.VK_W); break;
-         case 'x': doType(mode,KeyEvent.VK_X); break;
-         case 'y': doType(mode,KeyEvent.VK_Y); break;
-         case 'z': doType(mode,KeyEvent.VK_Z); break;
-         case 'A': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_A); break;
-         case 'B': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_B); break;
-         case 'C': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_C); break;
-         case 'D': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_D); break;
-         case 'E': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_E); break;
-         case 'F': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_F); break;
-         case 'G': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_G); break;
-         case 'H': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_H); break;
-         case 'I': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_I); break;
-         case 'J': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_J); break;
-         case 'K': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_K); break;
-         case 'L': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_L); break;
-         case 'M': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_M); break;
-         case 'N': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_N); break;
-         case 'O': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_O); break;
-         case 'P': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_P); break;
-         case 'Q': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_Q); break;
-         case 'R': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_R); break;
-         case 'S': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_S); break;
-         case 'T': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_T); break;
-         case 'U': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_U); break;
-         case 'V': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_V); break;
-         case 'W': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_W); break;
-         case 'X': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_X); break;
-         case 'Y': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_Y); break;
-         case 'Z': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_Z); break;
-         case '`': doType(mode,KeyEvent.VK_BACK_QUOTE); break;
-         case '0': doType(mode,KeyEvent.VK_0); break;
-         case '1': doType(mode,KeyEvent.VK_1); break;
-         case '2': doType(mode,KeyEvent.VK_2); break;
-         case '3': doType(mode,KeyEvent.VK_3); break;
-         case '4': doType(mode,KeyEvent.VK_4); break;
-         case '5': doType(mode,KeyEvent.VK_5); break;
-         case '6': doType(mode,KeyEvent.VK_6); break;
-         case '7': doType(mode,KeyEvent.VK_7); break;
-         case '8': doType(mode,KeyEvent.VK_8); break;
-         case '9': doType(mode,KeyEvent.VK_9); break;
-         case '-': doType(mode,KeyEvent.VK_MINUS); break;
-         case '=': doType(mode,KeyEvent.VK_EQUALS); break;
-         case '~': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_BACK_QUOTE); break;
-         case '!': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_1); break;
-         case '@': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_2); break;
-         case '#': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_3); break;
-         case '$': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_4); break;
-         case '%': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_5); break;
-         case '^': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_6); break;
-         case '&': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_7); break;
-         case '*': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_8); break;
-         case '(': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_9); break;
-         case ')': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_0); break;
-         case '_': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_MINUS); break;
-         case '+': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_EQUALS); break;
-         case '\b': doType(mode,KeyEvent.VK_BACK_SPACE); break;
-         case '\t': doType(mode,KeyEvent.VK_TAB); break;
-         case '\r': doType(mode,KeyEvent.VK_ENTER); break;
-         case '\n': doType(mode,KeyEvent.VK_ENTER); break;
-         case '[': doType(mode,KeyEvent.VK_OPEN_BRACKET); break;
-         case ']': doType(mode,KeyEvent.VK_CLOSE_BRACKET); break;
-         case '\\': doType(mode,KeyEvent.VK_BACK_SLASH); break;
-         case '{': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_OPEN_BRACKET); break;
-         case '}': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_CLOSE_BRACKET); break;
-         case '|': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_BACK_SLASH); break;
-         case ';': doType(mode,KeyEvent.VK_SEMICOLON); break;
-         case ':': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_SEMICOLON); break;
-         case '\'': doType(mode,KeyEvent.VK_QUOTE); break;
-         case '"': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_QUOTE); break;
-         case ',': doType(mode,KeyEvent.VK_COMMA); break;
-         case '<': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_COMMA); break;
-         case '.': doType(mode,KeyEvent.VK_PERIOD); break;
-         case '>': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_PERIOD); break;
-         case '/': doType(mode,KeyEvent.VK_SLASH); break;
-         case '?': doType(mode,KeyEvent.VK_SHIFT, KeyEvent.VK_SLASH); break;
-         case ' ': doType(mode,KeyEvent.VK_SPACE); break;
-         case Key.ESC        : doType(mode,KeyEvent.VK_ESCAPE); break;
-         case Key.UP         : doType(mode,KeyEvent.VK_UP); break;
-         case Key.RIGHT      : doType(mode,KeyEvent.VK_RIGHT); break;
-         case Key.DOWN       : doType(mode,KeyEvent.VK_DOWN); break;
-         case Key.LEFT       : doType(mode,KeyEvent.VK_LEFT); break;
-         case Key.PAGE_UP    : doType(mode,KeyEvent.VK_PAGE_UP); break;
-         case Key.PAGE_DOWN  : doType(mode,KeyEvent.VK_PAGE_DOWN); break;
-         case Key.DELETE     : doType(mode,KeyEvent.VK_DELETE); break;
-         case Key.END        : doType(mode,KeyEvent.VK_END); break;
-         case Key.HOME       : doType(mode,KeyEvent.VK_HOME); break;
-         case Key.INSERT     : doType(mode,KeyEvent.VK_INSERT); break;
-         case Key.F1         : doType(mode,KeyEvent.VK_F1); break;
-         case Key.F2         : doType(mode,KeyEvent.VK_F2); break;
-         case Key.F3         : doType(mode,KeyEvent.VK_F3); break;
-         case Key.F4         : doType(mode,KeyEvent.VK_F4); break;
-         case Key.F5         : doType(mode,KeyEvent.VK_F5); break;
-         case Key.F6         : doType(mode,KeyEvent.VK_F6); break;
-         case Key.F7         : doType(mode,KeyEvent.VK_F7); break;
-         case Key.F8         : doType(mode,KeyEvent.VK_F8); break;
-         case Key.F9         : doType(mode,KeyEvent.VK_F9); break;
-         case Key.F10        : doType(mode,KeyEvent.VK_F10); break;
-         case Key.F11        : doType(mode,KeyEvent.VK_F11); break;
-         case Key.F12        : doType(mode,KeyEvent.VK_F12); break;
-         case Key.F13        : doType(mode,KeyEvent.VK_F13); break;
-         case Key.F14        : doType(mode,KeyEvent.VK_F14); break;
-         case Key.F15        : doType(mode,KeyEvent.VK_F15); break;
-         case Key.SHIFT      : doType(mode,KeyEvent.VK_SHIFT); break;
-         case Key.CTRL       : doType(mode,KeyEvent.VK_CONTROL); break;
-         case Key.ALT        : doType(mode,KeyEvent.VK_ALT); break;
-         case Key.META       : doType(mode,KeyEvent.VK_META); break;
-         case Key.PRINTSCREEN: doType(mode,KeyEvent.VK_PRINTSCREEN); break;
-         case Key.SCROLL_LOCK: doType(mode,KeyEvent.VK_SCROLL_LOCK); break;
-         case Key.PAUSE      : doType(mode,KeyEvent.VK_PAUSE); break;
-         case Key.CAPS_LOCK  : doType(mode,KeyEvent.VK_CAPS_LOCK); break;
-         case Key.NUM0       : doType(mode,KeyEvent.VK_NUMPAD0); break;
-         case Key.NUM1       : doType(mode,KeyEvent.VK_NUMPAD1); break;
-         case Key.NUM2       : doType(mode,KeyEvent.VK_NUMPAD2); break;
-         case Key.NUM3       : doType(mode,KeyEvent.VK_NUMPAD3); break;
-         case Key.NUM4       : doType(mode,KeyEvent.VK_NUMPAD4); break;
-         case Key.NUM5       : doType(mode,KeyEvent.VK_NUMPAD5); break;
-         case Key.NUM6       : doType(mode,KeyEvent.VK_NUMPAD6); break;
-         case Key.NUM7       : doType(mode,KeyEvent.VK_NUMPAD7); break;
-         case Key.NUM8       : doType(mode,KeyEvent.VK_NUMPAD8); break;
-         case Key.NUM9       : doType(mode,KeyEvent.VK_NUMPAD9); break;
-         case Key.SEPARATOR  : doType(mode,KeyEvent.VK_SEPARATOR); break;
-         case Key.NUM_LOCK   : doType(mode,KeyEvent.VK_NUM_LOCK); break;
-         case Key.ADD        : doType(mode,KeyEvent.VK_ADD); break;
-         case Key.MINUS      : doType(mode,KeyEvent.VK_MINUS); break;
-         case Key.MULTIPLY   : doType(mode,KeyEvent.VK_MULTIPLY); break;
-         case Key.DIVIDE     : doType(mode,KeyEvent.VK_DIVIDE); break;
-         default:
-            throw new IllegalArgumentException("Cannot type character " + character);
-      }
+      doType(mode, Key.toJavaKeyCode(character));
    }
 
 }

=== modified file 'sikuli-script/src/main/java/org/sikuli/script/Env.java'
--- sikuli-script/src/main/java/org/sikuli/script/Env.java	2011-05-05 15:04:08 +0000
+++ sikuli-script/src/main/java/org/sikuli/script/Env.java	2011-08-21 03:38:17 +0000
@@ -13,6 +13,8 @@
 import java.lang.reflect.Constructor;
 
 
+import org.sikuli.script.internal.hotkey.HotkeyManager;
+
 
 public class Env {
    final static String SikuliVersion = "X-1.0rc3";
@@ -138,4 +140,15 @@
       return SikuliVersion;
    }
 
+   public static boolean addHotkey(char key, int modifiers, HotkeyListener listener){
+      return HotkeyManager.getInstance().addHotkey(key, modifiers, listener);
+   }
+
+   public static boolean removeHotkey(char key, int modifiers){
+      return HotkeyManager.getInstance().removeHotkey(key, modifiers);
+   }
+
+   public static void cleanUp(){
+      HotkeyManager.getInstance().cleanUp();
+   }
 }

=== added file 'sikuli-script/src/main/java/org/sikuli/script/HotkeyEvent.java'
--- sikuli-script/src/main/java/org/sikuli/script/HotkeyEvent.java	1970-01-01 00:00:00 +0000
+++ sikuli-script/src/main/java/org/sikuli/script/HotkeyEvent.java	2011-08-21 03:38:17 +0000
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2010-2011, Sikuli.org
+ * Released under the MIT License.
+ *
+ */
+package org.sikuli.script;
+
+
+public class HotkeyEvent {
+   public int keyCode;
+   public int modifiers;
+
+   public HotkeyEvent(int code_, int mod_){
+      init(code_, mod_);
+   }
+
+   void init(int code_, int mod_){
+      keyCode = code_;
+      modifiers = mod_;
+   }
+}
+

=== added file 'sikuli-script/src/main/java/org/sikuli/script/HotkeyListener.java'
--- sikuli-script/src/main/java/org/sikuli/script/HotkeyListener.java	1970-01-01 00:00:00 +0000
+++ sikuli-script/src/main/java/org/sikuli/script/HotkeyListener.java	2011-08-21 03:38:17 +0000
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2010-2011, Sikuli.org
+ * Released under the MIT License.
+ *
+ */
+package org.sikuli.script;
+
+
+public interface HotkeyListener {
+   public void hotkeyPressed(HotkeyEvent e);
+}

=== modified file 'sikuli-script/src/main/java/org/sikuli/script/Key.java'
--- sikuli-script/src/main/java/org/sikuli/script/Key.java	2011-07-02 01:55:39 +0000
+++ sikuli-script/src/main/java/org/sikuli/script/Key.java	2011-08-21 03:38:17 +0000
@@ -5,7 +5,10 @@
  */
 package org.sikuli.script;
 
+import java.awt.event.KeyEvent;
+
 public class Key {
+   public static final char SPACE       = ' ';
    public static final char ENTER       = '\n';
    public static final char BACKSPACE   = '\b';
    public static final char TAB         = '\t';
@@ -61,4 +64,163 @@
    public static final char MINUS       = '\ue03D';
    public static final char MULTIPLY    = '\ue03E';
    public static final char DIVIDE      = '\ue03F';
+
+   /**
+    *  Convert Sikuli Key to Java virtual key code
+    */ 
+   public static int[] toJavaKeyCode(char key){
+      switch (key) {
+         case 'a': return new int[] {KeyEvent.VK_A};
+         case 'b': return new int[] {KeyEvent.VK_B};
+         case 'c': return new int[] {KeyEvent.VK_C};
+         case 'd': return new int[] {KeyEvent.VK_D};
+         case 'e': return new int[] {KeyEvent.VK_E};
+         case 'f': return new int[] {KeyEvent.VK_F};
+         case 'g': return new int[] {KeyEvent.VK_G};
+         case 'h': return new int[] {KeyEvent.VK_H};
+         case 'i': return new int[] {KeyEvent.VK_I};
+         case 'j': return new int[] {KeyEvent.VK_J};
+         case 'k': return new int[] {KeyEvent.VK_K};
+         case 'l': return new int[] {KeyEvent.VK_L};
+         case 'm': return new int[] {KeyEvent.VK_M};
+         case 'n': return new int[] {KeyEvent.VK_N};
+         case 'o': return new int[] {KeyEvent.VK_O};
+         case 'p': return new int[] {KeyEvent.VK_P};
+         case 'q': return new int[] {KeyEvent.VK_Q};
+         case 'r': return new int[] {KeyEvent.VK_R};
+         case 's': return new int[] {KeyEvent.VK_S};
+         case 't': return new int[] {KeyEvent.VK_T};
+         case 'u': return new int[] {KeyEvent.VK_U};
+         case 'v': return new int[] {KeyEvent.VK_V};
+         case 'w': return new int[] {KeyEvent.VK_W};
+         case 'x': return new int[] {KeyEvent.VK_X};
+         case 'y': return new int[] {KeyEvent.VK_Y};
+         case 'z': return new int[] {KeyEvent.VK_Z};
+         case 'A': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_A};
+         case 'B': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_B};
+         case 'C': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_C};
+         case 'D': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_D};
+         case 'E': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_E};
+         case 'F': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_F};
+         case 'G': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_G};
+         case 'H': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_H};
+         case 'I': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_I};
+         case 'J': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_J};
+         case 'K': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_K};
+         case 'L': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_L};
+         case 'M': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_M};
+         case 'N': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_N};
+         case 'O': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_O};
+         case 'P': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_P};
+         case 'Q': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_Q};
+         case 'R': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_R};
+         case 'S': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_S};
+         case 'T': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_T};
+         case 'U': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_U};
+         case 'V': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_V};
+         case 'W': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_W};
+         case 'X': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_X};
+         case 'Y': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_Y};
+         case 'Z': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_Z};
+         case '`': return new int[] {KeyEvent.VK_BACK_QUOTE};
+         case '0': return new int[] {KeyEvent.VK_0};
+         case '1': return new int[] {KeyEvent.VK_1};
+         case '2': return new int[] {KeyEvent.VK_2};
+         case '3': return new int[] {KeyEvent.VK_3};
+         case '4': return new int[] {KeyEvent.VK_4};
+         case '5': return new int[] {KeyEvent.VK_5};
+         case '6': return new int[] {KeyEvent.VK_6};
+         case '7': return new int[] {KeyEvent.VK_7};
+         case '8': return new int[] {KeyEvent.VK_8};
+         case '9': return new int[] {KeyEvent.VK_9};
+         case '-': return new int[] {KeyEvent.VK_MINUS};
+         case '=': return new int[] {KeyEvent.VK_EQUALS};
+         case '~': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_BACK_QUOTE};
+         case '!': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_1};
+         case '@': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_2};
+         case '#': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_3};
+         case '$': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_4};
+         case '%': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_5};
+         case '^': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_6};
+         case '&': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_7};
+         case '*': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_8};
+         case '(': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_9};
+         case ')': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_0};
+         case '_': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_MINUS};
+         case '+': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_EQUALS};
+         case '\b':return new int[] {KeyEvent.VK_BACK_SPACE};
+         case '\t':return new int[] {KeyEvent.VK_TAB};
+         case '\r':return new int[] {KeyEvent.VK_ENTER};
+         case '\n':return new int[] {KeyEvent.VK_ENTER};
+         case '[': return new int[] {KeyEvent.VK_OPEN_BRACKET};
+         case ']': return new int[] {KeyEvent.VK_CLOSE_BRACKET};
+         case '\\':return new int[] {KeyEvent.VK_BACK_SLASH};
+         case '{': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_OPEN_BRACKET};
+         case '}': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_CLOSE_BRACKET};
+         case '|': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_BACK_SLASH};
+         case ';': return new int[] {KeyEvent.VK_SEMICOLON};
+         case ':': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_SEMICOLON};
+         case '\'':return new int[] {KeyEvent.VK_QUOTE};
+         case '"': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_QUOTE};
+         case ',': return new int[] {KeyEvent.VK_COMMA};
+         case '<': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_COMMA};
+         case '.': return new int[] {KeyEvent.VK_PERIOD};
+         case '>': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_PERIOD};
+         case '/': return new int[] {KeyEvent.VK_SLASH};
+         case '?': return new int[] {KeyEvent.VK_SHIFT, KeyEvent.VK_SLASH};
+         case ' ': return new int[] {KeyEvent.VK_SPACE};
+         case Key.ESC        : return new int[] {KeyEvent.VK_ESCAPE};
+         case Key.UP         : return new int[] {KeyEvent.VK_UP};
+         case Key.RIGHT      : return new int[] {KeyEvent.VK_RIGHT};
+         case Key.DOWN       : return new int[] {KeyEvent.VK_DOWN};
+         case Key.LEFT       : return new int[] {KeyEvent.VK_LEFT};
+         case Key.PAGE_UP    : return new int[] {KeyEvent.VK_PAGE_UP};
+         case Key.PAGE_DOWN  : return new int[] {KeyEvent.VK_PAGE_DOWN};
+         case Key.DELETE     : return new int[] {KeyEvent.VK_DELETE};
+         case Key.END        : return new int[] {KeyEvent.VK_END};
+         case Key.HOME       : return new int[] {KeyEvent.VK_HOME};
+         case Key.INSERT     : return new int[] {KeyEvent.VK_INSERT};
+         case Key.F1         : return new int[] {KeyEvent.VK_F1};
+         case Key.F2         : return new int[] {KeyEvent.VK_F2};
+         case Key.F3         : return new int[] {KeyEvent.VK_F3};
+         case Key.F4         : return new int[] {KeyEvent.VK_F4};
+         case Key.F5         : return new int[] {KeyEvent.VK_F5};
+         case Key.F6         : return new int[] {KeyEvent.VK_F6};
+         case Key.F7         : return new int[] {KeyEvent.VK_F7};
+         case Key.F8         : return new int[] {KeyEvent.VK_F8};
+         case Key.F9         : return new int[] {KeyEvent.VK_F9};
+         case Key.F10        : return new int[] {KeyEvent.VK_F10};
+         case Key.F11        : return new int[] {KeyEvent.VK_F11};
+         case Key.F12        : return new int[] {KeyEvent.VK_F12};
+         case Key.F13        : return new int[] {KeyEvent.VK_F13};
+         case Key.F14        : return new int[] {KeyEvent.VK_F14};
+         case Key.F15        : return new int[] {KeyEvent.VK_F15};
+         case Key.SHIFT      : return new int[] {KeyEvent.VK_SHIFT};
+         case Key.CTRL       : return new int[] {KeyEvent.VK_CONTROL};
+         case Key.ALT        : return new int[] {KeyEvent.VK_ALT};
+         case Key.META       : return new int[] {KeyEvent.VK_META};
+         case Key.PRINTSCREEN: return new int[] {KeyEvent.VK_PRINTSCREEN};
+         case Key.SCROLL_LOCK: return new int[] {KeyEvent.VK_SCROLL_LOCK};
+         case Key.PAUSE      : return new int[] {KeyEvent.VK_PAUSE};
+         case Key.CAPS_LOCK  : return new int[] {KeyEvent.VK_CAPS_LOCK};
+         case Key.NUM0       : return new int[] {KeyEvent.VK_NUMPAD0};
+         case Key.NUM1       : return new int[] {KeyEvent.VK_NUMPAD1};
+         case Key.NUM2       : return new int[] {KeyEvent.VK_NUMPAD2};
+         case Key.NUM3       : return new int[] {KeyEvent.VK_NUMPAD3};
+         case Key.NUM4       : return new int[] {KeyEvent.VK_NUMPAD4};
+         case Key.NUM5       : return new int[] {KeyEvent.VK_NUMPAD5};
+         case Key.NUM6       : return new int[] {KeyEvent.VK_NUMPAD6};
+         case Key.NUM7       : return new int[] {KeyEvent.VK_NUMPAD7};
+         case Key.NUM8       : return new int[] {KeyEvent.VK_NUMPAD8};
+         case Key.NUM9       : return new int[] {KeyEvent.VK_NUMPAD9};
+         case Key.SEPARATOR  : return new int[] {KeyEvent.VK_SEPARATOR};
+         case Key.NUM_LOCK   : return new int[] {KeyEvent.VK_NUM_LOCK};
+         case Key.ADD        : return new int[] {KeyEvent.VK_ADD};
+         case Key.MINUS      : return new int[] {KeyEvent.VK_MINUS};
+         case Key.MULTIPLY   : return new int[] {KeyEvent.VK_MULTIPLY};
+         case Key.DIVIDE     : return new int[] {KeyEvent.VK_DIVIDE};
+         default:
+            throw new IllegalArgumentException("Cannot convert character " + key);
+      }
+   }
 }

=== added directory 'sikuli-script/src/main/java/org/sikuli/script/internal'
=== added directory 'sikuli-script/src/main/java/org/sikuli/script/internal/hotkey'
=== added file 'sikuli-script/src/main/java/org/sikuli/script/internal/hotkey/HotkeyManager.java'
--- sikuli-script/src/main/java/org/sikuli/script/internal/hotkey/HotkeyManager.java	1970-01-01 00:00:00 +0000
+++ sikuli-script/src/main/java/org/sikuli/script/internal/hotkey/HotkeyManager.java	2011-08-21 03:38:17 +0000
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2010-2011, Sikuli.org
+ * Released under the MIT License.
+ *
+ */
+package org.sikuli.script.internal.hotkey;
+
+import java.lang.reflect.Constructor;
+
+import java.awt.event.KeyEvent;
+
+import org.sikuli.script.HotkeyListener;
+import org.sikuli.script.Env;
+import org.sikuli.script.Debug;
+import org.sikuli.script.Key;
+
+public abstract class HotkeyManager {
+   protected static HotkeyManager _instance = null;
+
+   private static String getOSHotkeyManagerClass(){
+      String pkg = "org.sikuli.script.internal.hotkey.";
+      switch(Env.getOS()){
+         case MAC:       return pkg+"MacHotkeyManager";
+         case WINDOWS:   return pkg+"WindowsHotkeyManager";
+         case LINUX:     return pkg+"LinuxHotkeyManager";
+         default:
+                         Debug.error("Error: Hotkey registration is not supported on your OS.");
+      }
+      return null;
+   }
+
+   protected String getKeyCodeText(int key){
+      return KeyEvent.getKeyText(key).toUpperCase();
+   }
+
+   protected String getKeyModifierText(int modifiers){
+      String txtMod = KeyEvent.getKeyModifiersText(modifiers).toUpperCase();
+      if(Env.isMac()){
+         txtMod = txtMod.replace("META","CMD");
+         txtMod = txtMod.replace("WINDOWS","CMD");
+      }
+      else{
+         txtMod = txtMod.replace("META","WIN");
+         txtMod = txtMod.replace("WINDOWS","WIN");
+      }
+      return txtMod;
+   }
+
+   public static HotkeyManager getInstance(){
+      if(_instance==null){
+         String cls = getOSHotkeyManagerClass();
+         if(cls != null){
+            try{
+               Class c = Class.forName(cls);
+               Constructor constr = c.getConstructor();
+               _instance = (HotkeyManager)constr.newInstance();
+            }
+            catch(Exception e){
+               Debug.error("Can't create " + cls + ": " + e.getMessage());
+            }
+         }
+      }
+      return _instance;
+   }
+   
+   /**
+    *  install a hotkey listener.
+    *
+    *  @return true if success. false otherwise.
+    */
+   public boolean addHotkey(char key, int modifiers, HotkeyListener listener){
+      int[] keyCodes = Key.toJavaKeyCode(key);
+      int keyCode = keyCodes[keyCodes.length-1];
+      String txtMod = getKeyModifierText(modifiers);
+      String txtCode = getKeyCodeText(keyCode);
+      Debug.info("add hotkey: " + txtMod + " " + txtCode);
+      return _instance._addHotkey(keyCode, modifiers, listener);
+   }
+
+
+   /**
+    *  uninstall a hotkey listener.
+    *
+    *  @return true if success. false otherwise.
+    */
+   public boolean removeHotkey(char key, int modifiers){
+      int[] keyCodes = Key.toJavaKeyCode(key);
+      int keyCode = keyCodes[keyCodes.length-1];
+      String txtMod = getKeyModifierText(modifiers);
+      String txtCode = getKeyCodeText(keyCode);
+      Debug.info("remove hotkey: " + txtMod + " " + txtCode);
+      return _instance._removeHotkey(keyCode, modifiers);
+   }
+
+
+   abstract protected boolean _addHotkey(int keyCode, int modifiers, HotkeyListener listener);
+   abstract protected boolean _removeHotkey(int keyCode, int modifiers);
+   abstract public void cleanUp();
+}

=== added file 'sikuli-script/src/main/java/org/sikuli/script/internal/hotkey/LinuxHotkeyManager.java'
--- sikuli-script/src/main/java/org/sikuli/script/internal/hotkey/LinuxHotkeyManager.java	1970-01-01 00:00:00 +0000
+++ sikuli-script/src/main/java/org/sikuli/script/internal/hotkey/LinuxHotkeyManager.java	2011-08-21 03:38:17 +0000
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2010-2011, Sikuli.org
+ * Released under the MIT License.
+ *
+ */
+package org.sikuli.script.internal.hotkey;
+
+import java.lang.reflect.*;
+import java.awt.Event;
+import java.awt.event.*;
+import java.util.*;
+import java.io.IOException;
+
+import org.sikuli.script.Debug;
+import org.sikuli.script.HotkeyListener;
+import org.sikuli.script.HotkeyEvent;
+
+import com.wapmx.nativeutils.jniloader.NativeLoader;
+import jxgrabkey.HotkeyConflictException;
+import jxgrabkey.JXGrabKey;
+
+public class LinuxHotkeyManager extends HotkeyManager {
+   static{
+      try{
+         NativeLoader.loadLibrary("JXGrabKey");
+      }
+      catch(IOException e){
+         Debug.error("Can't load native lib JXGrabKey");
+         e.printStackTrace();
+      }
+   }
+
+   class HotkeyData {
+      int key, modifiers;
+      HotkeyListener listener;
+
+      public HotkeyData(int key_, int mod_, HotkeyListener l_){
+         key = key_;
+         modifiers = mod_;
+         listener = l_;
+      }
+   };
+
+   class MyHotkeyHandler implements jxgrabkey.HotkeyListener{
+      public void onHotkey(int id){
+         Debug.log(4, "Hotkey pressed");
+         HotkeyData data = _idCallbackMap.get(id);
+         HotkeyEvent e = new HotkeyEvent(data.key, data.modifiers);
+         data.listener.hotkeyPressed(e);
+      }
+   };
+
+   private Map<Integer, HotkeyData> _idCallbackMap = new HashMap<Integer,HotkeyData >();
+   private int _gHotkeyId = 1;
+
+   protected boolean _addHotkey(int keyCode, int modifiers, HotkeyListener listener){
+      JXGrabKey grabKey = JXGrabKey.getInstance();
+
+      if(_gHotkeyId == 1){
+         grabKey.addHotkeyListener(new MyHotkeyHandler());
+      }
+
+      _removeHotkey(keyCode, modifiers);
+      int id = _gHotkeyId++;
+      HotkeyData data = new HotkeyData(keyCode, modifiers, listener);
+      _idCallbackMap.put(id, data);
+
+      try{
+         //JXGrabKey.setDebugOutput(true);
+         grabKey.registerAwtHotkey(id, modifiers, keyCode);
+      }catch(HotkeyConflictException e){
+         Debug.error("Hot key conflicts: " + txtMod + "+" + txtCode);
+         return false;
+      }
+      return true;
+   }
+
+   protected boolean _removeHotkey(int keyCode, int modifiers){
+      for( Map.Entry<Integer, HotkeyData> entry : _idCallbackMap.entrySet() ){
+         HotkeyData data = entry.getValue();
+         if(data.key == keyCode && data.modifiers == modifiers){
+            JXGrabKey grabKey = JXGrabKey.getInstance();
+            int id = entry.getKey();
+            grabKey.unregisterHotKey(id); 
+            _idCallbackMap.remove(id);
+            return true;
+         }
+      }
+      return false;
+   }
+
+
+   public void cleanUp(){
+      JXGrabKey grabKey = JXGrabKey.getInstance();
+      for( Map.Entry<Integer, HotkeyData> entry : _idCallbackMap.entrySet() ){
+         int id = entry.getKey();
+         grabKey.unregisterHotKey(id); 
+      }
+      _gHotkeyId = 1;
+      _idCallbackMap.clear();
+      grabKey.getInstance().cleanUp(); 
+   }
+
+}
+
+

=== added file 'sikuli-script/src/main/java/org/sikuli/script/internal/hotkey/MacHotkeyManager.java'
--- sikuli-script/src/main/java/org/sikuli/script/internal/hotkey/MacHotkeyManager.java	1970-01-01 00:00:00 +0000
+++ sikuli-script/src/main/java/org/sikuli/script/internal/hotkey/MacHotkeyManager.java	2011-08-21 03:38:17 +0000
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2010-2011, Sikuli.org
+ * Released under the MIT License.
+ *
+ */
+package org.sikuli.script.internal.hotkey;
+
+import java.awt.event.KeyEvent;
+import java.awt.event.InputEvent;
+import javax.swing.*;
+import java.io.IOException;
+import java.util.prefs.*;
+import com.apple.eawt.*;
+import com.wapmx.nativeutils.jniloader.NativeLoader;
+
+import org.sikuli.script.Debug;
+import org.sikuli.script.Env;
+import org.sikuli.script.HotkeyListener;
+
+// http://lists.apple.com/archives/mac-games-dev/2001/Sep/msg00113.html
+// full key table: http://www.mactech.com/articles/mactech/Vol.04/04.12/Macinkeys/
+// modifiers code: http://www.mactech.com/macintosh-c/chap02-1.html
+
+public class MacHotkeyManager extends HotkeyManager {
+   static final int CARBON_MASK_CMD = 0x0100;
+   static final int CARBON_MASK_SHIFT = 0x0200;
+   static final int CARBON_MASK_OPT = 0x0800;
+   static final int CARBON_MASK_CTRL = 0x1000;
+
+   static {
+      try{
+         NativeLoader.loadLibrary("MacHotkeyManager");      
+      }
+      catch(IOException e){
+         e.printStackTrace();
+      }
+   }
+
+   protected boolean _addHotkey(int keyCode, int modifiers, HotkeyListener listener){
+      int ckey = convertToCarbonKey(keyCode);
+      int cmod = convertToCarbonModifiers(modifiers);
+      return installGlobalHotkey(keyCode, modifiers, ckey, cmod, listener);
+   }
+
+
+   protected boolean _removeHotkey(int keyCode, int modifiers){
+      int ckey = convertToCarbonKey(keyCode);
+      int cmod = convertToCarbonModifiers(modifiers);
+      return uninstallGlobalHotkey(ckey, cmod);
+   }
+
+
+   private native boolean installGlobalHotkey(int jKey, int jMod, int keyCode, int modifiers, HotkeyListener listener);
+   private native boolean uninstallGlobalHotkey(int keyCode, int modifiers);
+   public native void cleanUp();
+
+   private int convertToCarbonModifiers(int mod){
+      int cmod = 0;
+      if((mod&InputEvent.SHIFT_MASK) != 0) cmod |= CARBON_MASK_SHIFT;
+      if((mod&InputEvent.META_MASK) != 0) cmod |= CARBON_MASK_CMD;
+      if((mod&InputEvent.ALT_MASK) != 0) cmod |= CARBON_MASK_OPT;
+      if((mod&InputEvent.CTRL_MASK) != 0) cmod |= CARBON_MASK_CTRL;
+      return cmod;
+   }
+
+   private int convertToCarbonKey(int keycode){
+      switch(keycode){
+         case KeyEvent.VK_BACK_SPACE: return 0x33;
+         case KeyEvent.VK_TAB: return 0x30;
+         case KeyEvent.VK_CLEAR: return 0x47;
+         case KeyEvent.VK_ENTER: return 0x24;
+         case KeyEvent.VK_SHIFT: return 0xF0;
+         case KeyEvent.VK_CONTROL: return 0xF1;
+         case KeyEvent.VK_META: return 0xF2; 
+         case KeyEvent.VK_PAUSE: return 0x71; // = F15
+         case KeyEvent.VK_ESCAPE: return 0x35;
+         case KeyEvent.VK_SPACE: return 0x31;
+         case KeyEvent.VK_OPEN_BRACKET: return 0x21;
+         case KeyEvent.VK_BACK_SLASH: return 0x2A;
+         case KeyEvent.VK_CLOSE_BRACKET: return 0x1E;
+         case KeyEvent.VK_SLASH: return 0x2C;
+         case KeyEvent.VK_PERIOD: return 0x2F;
+         case KeyEvent.VK_COMMA: return 0x2B;
+         case KeyEvent.VK_SEMICOLON: return 0x29;
+         case KeyEvent.VK_END: return 0x77;
+         case KeyEvent.VK_HOME: return 0x73;
+         case KeyEvent.VK_LEFT: return 0x7B;
+         case KeyEvent.VK_UP: return 0x7E;
+         case KeyEvent.VK_RIGHT: return 0x7C;
+         case KeyEvent.VK_DOWN: return 0x7D;
+         case KeyEvent.VK_PRINTSCREEN: return 0x69; // F13
+         case KeyEvent.VK_INSERT: return 0x72; // help
+         case KeyEvent.VK_DELETE: return 0x75;
+         case KeyEvent.VK_HELP: return 0x72;
+         case KeyEvent.VK_0: return 0x1D;
+         case KeyEvent.VK_1: return 0x12;
+         case KeyEvent.VK_2: return 0x13;
+         case KeyEvent.VK_3: return 0x14;
+         case KeyEvent.VK_4: return 0x15;
+         case KeyEvent.VK_5: return 0x17;
+         case KeyEvent.VK_6: return 0x16;
+         case KeyEvent.VK_7: return 0x1A;
+         case KeyEvent.VK_8: return 0x1C;
+         case KeyEvent.VK_9: return 0x19;
+         case KeyEvent.VK_MINUS: return 0x1B;
+         case KeyEvent.VK_EQUALS: return 0x18;
+         case KeyEvent.VK_A: return 0x00;
+         case KeyEvent.VK_B: return 0x0B;
+         case KeyEvent.VK_C: return 0x08;
+         case KeyEvent.VK_D: return 0x02;
+         case KeyEvent.VK_E: return 0x0E;
+         case KeyEvent.VK_F: return 0x03;
+         case KeyEvent.VK_G: return 0x05;
+         case KeyEvent.VK_H: return 0x04;
+         case KeyEvent.VK_I: return 0x22;
+         case KeyEvent.VK_J: return 0x26;
+         case KeyEvent.VK_K: return 0x28;
+         case KeyEvent.VK_L: return 0x25;
+         case KeyEvent.VK_M: return 0x2E;
+         case KeyEvent.VK_N: return 0x2D;
+         case KeyEvent.VK_O: return 0x1F;
+         case KeyEvent.VK_P: return 0x23;
+         case KeyEvent.VK_Q: return 0x0C;
+         case KeyEvent.VK_R: return 0x0F;
+         case KeyEvent.VK_S: return 0x01;
+         case KeyEvent.VK_T: return 0x11;
+         case KeyEvent.VK_U: return 0x20;
+         case KeyEvent.VK_V: return 0x09;
+         case KeyEvent.VK_W: return 0x0D;
+         case KeyEvent.VK_X: return 0x07;
+         case KeyEvent.VK_Y: return 0x10;
+         case KeyEvent.VK_Z: return 0x06;
+         case KeyEvent.VK_NUMPAD0: return 0x52;
+         case KeyEvent.VK_NUMPAD1: return 0x53;
+         case KeyEvent.VK_NUMPAD2: return 0x54;
+         case KeyEvent.VK_NUMPAD3: return 0x55;
+         case KeyEvent.VK_NUMPAD4: return 0x56;
+         case KeyEvent.VK_NUMPAD5: return 0x57;
+         case KeyEvent.VK_NUMPAD6: return 0x58;
+         case KeyEvent.VK_NUMPAD7: return 0x59;
+         case KeyEvent.VK_NUMPAD8: return 0x5B;
+         case KeyEvent.VK_NUMPAD9: return 0x5C;
+         case KeyEvent.VK_MULTIPLY: return 0x43;
+         case KeyEvent.VK_ADD: return 0x45;
+         case KeyEvent.VK_SEPARATOR: return 0xFF; // not supported with Button or GetKeys
+         case KeyEvent.VK_SUBTRACT: return 0x4E;
+         case KeyEvent.VK_DECIMAL: return 0x41;
+         case KeyEvent.VK_DIVIDE: return 0x4B;
+         case KeyEvent.VK_F1: return 0x7A;
+         case KeyEvent.VK_F2: return 0x7B;
+         case KeyEvent.VK_F3: return 0x63;
+         case KeyEvent.VK_F4: return 0x76;
+         case KeyEvent.VK_F5: return 0x60;
+         case KeyEvent.VK_F6: return 0x61;
+         case KeyEvent.VK_F7: return 0x62;
+         case KeyEvent.VK_F8: return 0x64;
+         case KeyEvent.VK_F9: return 0x65;
+         case KeyEvent.VK_F10: return 0x6D;
+         case KeyEvent.VK_F11: return 0x67;
+         case KeyEvent.VK_F12: return 0x6F;
+         case KeyEvent.VK_F13: return 0x69;
+         case KeyEvent.VK_F14: return 0x6B;
+         case KeyEvent.VK_F15: return 0x71;
+         case KeyEvent.VK_NUM_LOCK: return 0x47;
+         default: return 0xFF;
+      }
+
+   }
+}
+
+

=== added file 'sikuli-script/src/main/java/org/sikuli/script/internal/hotkey/WindowsHotkeyManager.java'
--- sikuli-script/src/main/java/org/sikuli/script/internal/hotkey/WindowsHotkeyManager.java	1970-01-01 00:00:00 +0000
+++ sikuli-script/src/main/java/org/sikuli/script/internal/hotkey/WindowsHotkeyManager.java	2011-08-21 03:38:17 +0000
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2010-2011, Sikuli.org
+ * Released under the MIT License.
+ *
+ */
+package org.sikuli.script.internal.hotkey;
+
+import java.lang.reflect.*;
+import java.awt.Event;
+import java.awt.event.*;
+import java.util.*;
+import com.melloware.jintellitype.IntellitypeListener;
+import com.melloware.jintellitype.JIntellitype;
+
+import org.sikuli.script.Debug;
+import org.sikuli.script.HotkeyListener;
+import org.sikuli.script.HotkeyEvent;
+
+public class WindowsHotkeyManager extends HotkeyManager {
+   class HotkeyData {
+      int key, modifiers;
+      HotkeyListener listener;
+
+      public HotkeyData(int key_, int mod_, HotkeyListener l_){
+         key = key_;
+         modifiers = mod_;
+         listener = l_;
+      }
+   };
+
+   class JIntellitypeHandler implements 
+                               com.melloware.jintellitype.HotkeyListener{
+      public void onHotKey(int id){
+         Debug.log(4, "Hotkey pressed");
+         HotkeyData data = _idCallbackMap.get(id);
+         HotkeyEvent e = new HotkeyEvent(data.key, data.modifiers);
+         data.listener.hotkeyPressed(e);
+      }
+   };
+
+   private Map<Integer, HotkeyData> _idCallbackMap = new HashMap<Integer,HotkeyData >();
+   private int _gHotkeyId = 1;
+
+   protected boolean _addHotkey(int keyCode, int modifiers, HotkeyListener listener){
+      JIntellitype itype = JIntellitype.getInstance();
+
+      if(_gHotkeyId == 1){
+         itype.addHotKeyListener(new JIntellitypeHandler());
+      }
+
+      _removeHotkey(keyCode, modifiers);
+      int id = _gHotkeyId++;
+      HotkeyData data = new HotkeyData(keyCode, modifiers, listener);
+      _idCallbackMap.put(id, data);
+
+      itype.registerSwingHotKey(id, modifiers, keyCode);
+      return true;
+   }
+   
+   protected boolean _removeHotkey(int keyCode, int modifiers){
+      for( Map.Entry<Integer, HotkeyData> entry : _idCallbackMap.entrySet() ){
+         HotkeyData data = entry.getValue();
+         if(data.key == keyCode && data.modifiers == modifiers){
+            JIntellitype itype = JIntellitype.getInstance();
+            int id = entry.getKey();
+            itype.unregisterHotKey(id);
+            _idCallbackMap.remove(id);
+            return true;
+         }
+      }
+      return false;
+   }
+
+
+   public void cleanUp(){
+      JIntellitype itype = JIntellitype.getInstance();
+      for( Map.Entry<Integer, HotkeyData> entry : _idCallbackMap.entrySet() ){
+         int id = entry.getKey();
+         itype.unregisterHotKey(id); 
+      }
+      _gHotkeyId = 1;
+      _idCallbackMap.clear();
+      itype.cleanUp();
+   }
+
+}
+
+

=== modified file 'sikuli-script/src/main/native/CMakeLists.txt'
--- sikuli-script/src/main/native/CMakeLists.txt	2011-05-05 15:04:08 +0000
+++ sikuli-script/src/main/native/CMakeLists.txt	2011-08-21 03:38:17 +0000
@@ -18,6 +18,7 @@
    org_sikuli_script_VDictProxy.h
    org_sikuli_script_Win32Util.h
    org_sikuli_script_MacUtil.h
+   org_sikuli_script_internal_hotkey_MacHotkeyManager.h
 )
 
 SET(LIBRARY_OUTPUT_PATH ${BINARY_LIB_DIR})
@@ -90,6 +91,7 @@
    TARGET_LINK_LIBRARIES(Win32Util ${JAVA_AWT_LIBRARY})
 ENDIF()
 
+
 IF(APPLE)
    LIST(APPEND BUILD_TARGETS MacUtil)
    FIND_LIBRARY(JAVA_LIBRARY JavaVM)
@@ -103,6 +105,17 @@
       org_sikuli_script_MacUtil.h
    )
    TARGET_LINK_LIBRARIES(MacUtil ${EXTRA_LIBS})
+
+   SET(BUILD_TARGETS
+      MacHotkeyManager
+   )
+
+   ADD_LIBRARY(MacHotkeyManager SHARED
+      MacHotkeyManager.cc
+      sikuli-debug.cpp
+      org_sikuli_script_internal_hotkey_MacHotkeyManager.h
+   )
+   TARGET_LINK_LIBRARIES(MacHotkeyManager ${EXTRA_LIBS})
 ENDIF()
 
 SET(NATIVE_LIBS
@@ -114,6 +127,7 @@
    VDictProxy.cc
    org_sikuli_script_VDictProxy.h
 )
+TARGET_LINK_LIBRARIES(VDictProxy ${OpenCV_LIBS})
 
 
 ## SWIG: generate a JNI wrapper 
@@ -155,9 +169,4 @@
    endif(APPLE)
 endforeach(BUILD_TARGET ${BUILD_TARGETS})
 
-add_custom_target(${JAR_FILE}.libs-in-jar
-   COMMAND ${CMAKE_COMMAND} -E make_directory ${JAR_DIR}/META-INF
-   COMMAND ${CMAKE_COMMAND} -E copy_directory ${BINARY_LIB_DIR} ${JAR_DIR}/META-INF/lib
-)
-
 add_dependencies(${JAR_FILE}.libs-in-jar ${NATIVE_LIBS})

=== added file 'sikuli-script/src/main/native/MacHotkeyManager.cc'
--- sikuli-script/src/main/native/MacHotkeyManager.cc	1970-01-01 00:00:00 +0000
+++ sikuli-script/src/main/native/MacHotkeyManager.cc	2011-08-21 03:38:17 +0000
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2010-2011, Sikuli.org
+ * Released under the MIT License.
+ *
+ */
+#include "org_sikuli_script_internal_hotkey_MacHotkeyManager.h"
+
+#include <Carbon/Carbon.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+#include<iostream>
+#include<map>
+
+#include "sikuli-debug.h"
+
+using namespace std;
+using namespace sikuli;
+
+#define HOTKEY_LISTENER_METHOD "hotkeyPressed"
+#define HOTKEY_LISTENER_SIGNATURE "(Lorg/sikuli/script/HotkeyEvent;)V"
+#define HOTKEY_EVENT_CLASS "org/sikuli/script/HotkeyEvent"
+
+struct CallbackData {
+   JavaVM *vm;
+   int hotkey, mods;
+   int jHotkey, jModifiers;
+   jobject listener;
+   EventHotKeyRef ref;
+   EventHotKeyID id;
+
+   CallbackData(JavaVM *vm_, int hotkey_, int mods_, jobject listener_){
+      vm = vm_;
+      hotkey = hotkey_;
+      mods = mods_;
+      listener = listener_;
+   }
+};
+
+jobject CallbackDataToHotkeyEvent(JNIEnv* env, CallbackData* data){
+   jclass clsHkEvent = env->FindClass(HOTKEY_EVENT_CLASS);
+   jobject ret = env->AllocObject(clsHkEvent);
+   jmethodID initMethod = env->GetMethodID(clsHkEvent, "init", "(II)V");
+   env->CallVoidMethod(ret, initMethod, data->jHotkey, data->jModifiers);
+   env->DeleteLocalRef(clsHkEvent);
+   return ret;
+}
+
+
+
+void callJavaMethod(JavaVM *jvm, jobject listener, CallbackData* data){
+   JNIEnv *env;
+   jvm->GetEnv((void**)&env, JNI_VERSION_1_4);
+   jvm->AttachCurrentThread((void **)&env, NULL);
+   jclass cls = env->GetObjectClass(listener);
+   jmethodID mid = env->GetMethodID(cls, HOTKEY_LISTENER_METHOD, HOTKEY_LISTENER_SIGNATURE); 
+   if( mid == NULL ){
+      cerr << "Callback method not found." << endl;
+      return;
+   }
+   jobject hkEvent = CallbackDataToHotkeyEvent(env, data);
+   env->CallVoidMethod(listener, mid, hkEvent);
+}
+
+static map<int, CallbackData*> regHotkeys;
+static int gHotkeyId = 0;
+
+OSStatus shortcutHandler( EventHandlerCallRef inCaller, EventRef inEvent, 
+                          void* args )
+{
+   EventHotKeyID hkId;
+   GetEventParameter(inEvent, kEventParamDirectObject, typeEventHotKeyID, NULL,
+                     sizeof(hkId), NULL, &hkId);
+   CallbackData *data = regHotkeys[hkId.id];
+   int hotkey = data->hotkey;
+   dout("MacHotkeyManager") << "shortcut pressed. " << hotkey << endl;
+   callJavaMethod(data->vm, data->listener, data);
+   return noErr;
+}
+
+
+bool unregisterHotkey(CallbackData *data){
+   map<int, CallbackData*>::iterator it;
+   for(it = regHotkeys.begin(); it != regHotkeys.end(); ++it){
+      CallbackData *itdata = it->second;
+      if( itdata->hotkey == data->hotkey && itdata->mods == data->mods){
+         UnregisterEventHotKey(itdata->ref);
+         data->id = itdata->id;
+         data->ref = itdata->ref;
+         regHotkeys.erase(it);
+         return true;
+      }
+   }
+   return false;
+}
+
+
+bool installShortcutHandler( CallbackData *data ){
+   EventTypeSpec shortcutEvents[] = {
+      { kEventClassKeyboard, kEventHotKeyPressed },
+   };
+   
+   if(gHotkeyId == 0){
+      OSErr err = InstallApplicationEventHandler( &shortcutHandler,
+        GetEventTypeCount(shortcutEvents), shortcutEvents, NULL, NULL);
+      if (err != noErr)
+         cerr << "InstallApplicationEventHandler failed" << endl;
+   }
+
+   bool registered = unregisterHotkey(data); 
+   if(!registered){
+      data->id.id = gHotkeyId++;
+      data->id.signature='htk1';
+   }
+
+   OSStatus err = RegisterEventHotKey(data->hotkey, data->mods,
+                      data->id, GetApplicationEventTarget(), 0, 
+                      &(data->ref));
+   if(!err){
+      regHotkeys[data->id.id] = data;
+      return true;
+   }
+   return false;
+}
+
+/*
+ * Class:     org_sikuli_script_internal_hotkey_MacHotkeyManager
+ * Method:    installGlobalHotkey
+ * Signature: (IIIILorg/sikuli/script/internal/hotkey/HotkeyListener;)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_sikuli_script_internal_hotkey_MacHotkeyManager_installGlobalHotkey
+(JNIEnv *env, jobject jobj, jint jHotkey, jint jModifiers, jint hotkey, jint modifiers, jobject listener){
+   dout("MacHotkeyManager") << "install global hotkey: " << hotkey << " mod: " << modifiers << endl;
+   JavaVM* vm = NULL;
+   env->GetJavaVM(&vm);
+   jobject gListener = env->NewGlobalRef(listener);
+   env->DeleteLocalRef(listener);
+   CallbackData *data = new CallbackData(vm, hotkey, modifiers, gListener);
+   data->jHotkey = jHotkey;
+   data->jModifiers = jModifiers;
+   return installShortcutHandler(data);
+}
+
+
+/*
+ * Class:     org_sikuli_script_internal_hotkey_MacHotkeyManager
+ * Method:    uninstallGlobalHotkey
+ * Signature: (II)Z
+ */
+JNIEXPORT jboolean JNICALL 
+Java_org_sikuli_script_internal_hotkey_MacHotkeyManager_uninstallGlobalHotkey
+(JNIEnv *env, jobject jobj, jint hotkey, jint modifiers){
+   CallbackData *data = new CallbackData(NULL, hotkey, modifiers, NULL);
+   return unregisterHotkey(data);
+}
+
+/*
+ * Class:     org_sikuli_script_internal_hotkey_MacHotkeyManager
+ * Method:    cleanUp
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_org_sikuli_script_internal_hotkey_MacHotkeyManager_cleanUp
+(JNIEnv *env, jobject jobj){
+   map<int, CallbackData*>::iterator it;
+   for(it = regHotkeys.begin(); it != regHotkeys.end(); ++it){
+      CallbackData *itdata = it->second;
+      UnregisterEventHotKey(itdata->ref);
+   }
+   regHotkeys.clear();
+   gHotkeyId = 0;
+}

=== added file 'sikuli-script/src/main/native/org_sikuli_script_internal_hotkey_MacHotkeyManager.h'
--- sikuli-script/src/main/native/org_sikuli_script_internal_hotkey_MacHotkeyManager.h	1970-01-01 00:00:00 +0000
+++ sikuli-script/src/main/native/org_sikuli_script_internal_hotkey_MacHotkeyManager.h	2011-08-21 03:38:17 +0000
@@ -0,0 +1,45 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_sikuli_script_internal_hotkey_MacHotkeyManager */
+
+#ifndef _Included_org_sikuli_script_internal_hotkey_MacHotkeyManager
+#define _Included_org_sikuli_script_internal_hotkey_MacHotkeyManager
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef org_sikuli_script_internal_hotkey_MacHotkeyManager_CARBON_MASK_CMD
+#define org_sikuli_script_internal_hotkey_MacHotkeyManager_CARBON_MASK_CMD 256L
+#undef org_sikuli_script_internal_hotkey_MacHotkeyManager_CARBON_MASK_SHIFT
+#define org_sikuli_script_internal_hotkey_MacHotkeyManager_CARBON_MASK_SHIFT 512L
+#undef org_sikuli_script_internal_hotkey_MacHotkeyManager_CARBON_MASK_OPT
+#define org_sikuli_script_internal_hotkey_MacHotkeyManager_CARBON_MASK_OPT 2048L
+#undef org_sikuli_script_internal_hotkey_MacHotkeyManager_CARBON_MASK_CTRL
+#define org_sikuli_script_internal_hotkey_MacHotkeyManager_CARBON_MASK_CTRL 4096L
+/*
+ * Class:     org_sikuli_script_internal_hotkey_MacHotkeyManager
+ * Method:    installGlobalHotkey
+ * Signature: (IIIILorg/sikuli/script/HotkeyListener;)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_sikuli_script_internal_hotkey_MacHotkeyManager_installGlobalHotkey
+  (JNIEnv *, jobject, jint, jint, jint, jint, jobject);
+
+/*
+ * Class:     org_sikuli_script_internal_hotkey_MacHotkeyManager
+ * Method:    uninstallGlobalHotkey
+ * Signature: (II)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_sikuli_script_internal_hotkey_MacHotkeyManager_uninstallGlobalHotkey
+  (JNIEnv *, jobject, jint, jint);
+
+/*
+ * Class:     org_sikuli_script_internal_hotkey_MacHotkeyManager
+ * Method:    cleanUp
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_org_sikuli_script_internal_hotkey_MacHotkeyManager_cleanUp
+  (JNIEnv *, jobject);
+
+#ifdef __cplusplus
+}
+#endif
+#endif

=== modified file 'sikuli-script/src/main/native/sikuli-debug.cpp'
--- sikuli-script/src/main/native/sikuli-debug.cpp	2011-05-05 15:04:08 +0000
+++ sikuli-script/src/main/native/sikuli-debug.cpp	2011-08-21 03:38:17 +0000
@@ -23,16 +23,16 @@
 }
 
 std::ostream& sikuli::dout(const char* name){
-#ifdef ENABLE_OCR_DEBUG
-   return cout;
+#ifdef SHOW_DEBUG_MESSAGE
+   return cerr << "[" << name << "] ";
 #else
    return null_out;
 #endif
 }
 
 std::ostream& sikuli::dhead(const char* name){
-#ifdef ENABLE_OCR_DEBUG
-   return cout << "[" << name << "] ";
+#ifdef SHOW_DEBUG_MESSAGE
+   return cerr << "[" << name << "]\n----------------------------\n";
 #else
    return null_out;
 #endif

=== added file 'sikuli-script/src/main/python/sikuli/Env.py'
--- sikuli-script/src/main/python/sikuli/Env.py	1970-01-01 00:00:00 +0000
+++ sikuli-script/src/main/python/sikuli/Env.py	2011-08-21 03:38:17 +0000
@@ -0,0 +1,15 @@
+# Copyright 2010-2011, Sikuli.org
+# Released under the MIT License.
+from org.sikuli.script import Env as JEnv
+from org.sikuli.script import HotkeyListener
+
+class Env(JEnv):
+
+   @classmethod
+   def addHotkey(cls, key, modifiers, handler):
+      class AnonyListener(HotkeyListener):
+         def hotkeyPressed(self, event):
+            handler(event)
+      return JEnv.addHotkey(key, modifiers, AnonyListener())
+
+

=== modified file 'sikuli-script/src/main/python/sikuli/Sikuli.py'
--- sikuli-script/src/main/python/sikuli/Sikuli.py	2011-06-28 22:14:07 +0000
+++ sikuli-script/src/main/python/sikuli/Sikuli.py	2011-08-21 03:38:17 +0000
@@ -22,7 +22,6 @@
 from org.sikuli.script import Finder
 from org.sikuli.script import Location
 from org.sikuli.script import Settings
-from org.sikuli.script import Env
 from org.sikuli.script import OS
 from org.sikuli.script import App
 from org.sikuli.script import ScreenHighlighter
@@ -36,6 +35,7 @@
 from Screen import *
 from VDict import *
 from Helper import *
+from Env import *
 import SikuliImporter
 
 _si = SikuliScript()

=== added file 'sikuli-script/src/test/java/org/sikuli/script/HotkeyTest.java'
--- sikuli-script/src/test/java/org/sikuli/script/HotkeyTest.java	1970-01-01 00:00:00 +0000
+++ sikuli-script/src/test/java/org/sikuli/script/HotkeyTest.java	2011-08-21 03:38:17 +0000
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2010-2011, Sikuli.org
+ * Released under the MIT License.
+ *
+ */
+package org.sikuli.script;
+
+import org.junit.* ;
+import static org.junit.Assert.* ;
+
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+
+import javax.swing.JFrame;
+
+import org.sikuli.script.internal.hotkey.HotkeyManager;
+import org.sikuli.script.HotkeyListener;
+import org.sikuli.script.HotkeyEvent;
+import org.sikuli.script.Debug;
+
+public class HotkeyTest 
+{
+   public boolean pressed = false;
+
+
+   private void sleep(double secs){
+       int count = 0;
+       while(count < secs*10){
+          try{
+             Thread.sleep(100);
+             count++;
+          }
+          catch(InterruptedException e){}
+       }
+   }
+
+    //@Ignore("ignore this in automated test")
+    @Test
+    public void test_hotkey_install()
+    {
+       JFrame f = new JFrame("hello"); // need this to hook in the event loop
+       boolean ret = Env.addHotkey(Key.F6, 0,
+         new HotkeyListener(){
+            public void hotkeyPressed(HotkeyEvent e){
+               HotkeyTest.this.pressed = true;
+               Debug.log("hotkey pressed!" + e.keyCode + ", " + e.modifiers);
+            }
+       });
+       Debug.log("install hotkey F6: " + ret);
+       ret = Env.addHotkey('2', KeyModifier.ALT,
+         new HotkeyListener(){
+            public void hotkeyPressed(HotkeyEvent e){
+               HotkeyTest.this.pressed = true;
+               Debug.log("hotkey 2 pressed!");
+            }
+       });
+       Debug.log("install hotkey ALT-2: " + ret);
+       Debug.log("press the hot key now.");
+       sleep(5);
+       ret = Env.removeHotkey(Key.F6, 0);
+       Debug.log("remove hotkey F6: " + ret);
+       Debug.log("press the hot key again.");
+       sleep(2);
+       ret = Env.addHotkey(Key.F7, 0,
+         new HotkeyListener(){
+            public void hotkeyPressed(HotkeyEvent e){
+               HotkeyTest.this.pressed = true;
+               Debug.log("hotkey 3 pressed!" + e.keyCode + ", " + e.modifiers);
+            }
+       });
+       Debug.log("install hotkey F7: " + ret);
+       Debug.log("press the hot key now.");
+       sleep(3);
+       //hkm.cleanUp();
+    }
+
+    public static void main(String args[]){
+      (new HotkeyTest()).test_hotkey_install();
+    }
+}
+

=== added file 'sikuli-script/src/test/python/test_hotkey.py'
--- sikuli-script/src/test/python/test_hotkey.py	1970-01-01 00:00:00 +0000
+++ sikuli-script/src/test/python/test_hotkey.py	2011-08-21 03:38:17 +0000
@@ -0,0 +1,46 @@
+import unittest
+from sikuli import *
+from java.awt.event import KeyEvent
+from javax.swing import JFrame
+
+not_pressed = True
+WAIT_TIME = 4
+
+def pressed(event):
+   global not_pressed
+   not_pressed = False
+   print "hotkey pressed! %d %d" %(event.modifiers,event.keyCode)
+
+class TestHotkey(unittest.TestCase):
+   def testAddHotkey(self):
+      self.assertTrue(Env.addHotkey(Key.F6, 0, pressed))
+
+   def testAddHotkeyReal(self):
+      #f = JFrame("hello")
+      global not_pressed
+      Env.addHotkey(Key.F6, 0, pressed)
+      self.assertTrue(not_pressed)
+      count = 0
+      while not_pressed and count < WAIT_TIME:
+         count += 1
+         wait(1)
+         keyDown(Key.F6)
+         keyUp(Key.F6)
+      self.assertFalse(not_pressed) 
+      #f.dispose()
+
+   def testRemoveHotkey(self):
+      self.assertFalse(Env.removeHotkey(Key.F7, 0))
+      self.assertTrue(Env.addHotkey(Key.F7, 0, pressed))
+      self.assertTrue(Env.removeHotkey(Key.F7, 0))
+
+
+   def setUp(self):
+      global not_pressed
+      not_pressed = True
+
+   @classmethod
+   def tearDownClass(self):
+      print "clean up"
+      Env.cleanUp()
+