/* Copyright 2008 Edwin Stang (edwinstang@gmail.com), * * This file is part of JXGrabKey. * * JXGrabKey is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * JXGrabKey is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with JXGrabKey. If not, see <http://www.gnu.org/licenses/>. */ package jxgrabkey; import java.awt.event.KeyEvent; import java.util.Vector; /** * This class implements the API access. * All public methods are synchronized, hence thread-safe. * * @author subes */ public class JXGrabKey { private static final int SLEEP_WHILE_LISTEN_EXITS = 100; private static boolean debug; private static JXGrabKey instance; private static Thread thread; private static Vector<HotkeyListener> listeners = new Vector<HotkeyListener>(); /** * This constructor starts a seperate Thread for the main listen loop. */ private JXGrabKey() { thread = new Thread(){ @Override public void run() { listen(); debugCallback("-- listen()"); } }; thread.start(); } /** * Retrieves the singleton. Initializes it, if not yet done. * * @return */ public static synchronized JXGrabKey getInstance(){ if(instance == null){ instance = new JXGrabKey(); } return instance; } /** * Adds a HotkeyListener. * * @param listener */ public void addHotkeyListener(HotkeyListener listener){ if(listener == null){ throw new IllegalArgumentException("listener must not be null"); } JXGrabKey.listeners.add(listener); } /** * Removes a HotkeyListener. * * @param listener */ public void removeHotkeyListener(HotkeyListener listener){ if(listener == null){ throw new IllegalArgumentException("listener must not be null"); } JXGrabKey.listeners.remove(listener); } /** * Unregisters all hotkeys, removes all HotkeyListeners, * stops the main listen loop and deinitializes the singleton. */ public void cleanUp(){ clean(); if(thread.isAlive()){ while(thread.isAlive()){ try { Thread.sleep(SLEEP_WHILE_LISTEN_EXITS); } catch (InterruptedException e) { debugCallback("cleanUp() - InterruptedException: "+e.getMessage()); } } instance = null; //next time getInstance is called, reinitialize JXGrabKey } if(listeners.size() > 0){ listeners.clear(); } } /** * Registers a X11 hotkey. * * @param id * @param x11Mask * @param x11Keysym * @throws jxgrabkey.HotkeyConflictException */ public void registerX11Hotkey(int id, int x11Mask, int x11Keysym) throws HotkeyConflictException{ registerHotkey(id, x11Mask, x11Keysym); } /** * Converts an AWT hotkey into a X11 hotkey and registers it. * * @param id * @param awtMask * @param awtKey * @throws jxgrabkey.HotkeyConflictException */ public void registerAwtHotkey(int id, int awtMask, int awtKey) throws HotkeyConflictException{ debugCallback("++ registerAwtHotkey("+id+", 0x"+ Integer.toHexString(awtMask)+", 0x"+ Integer.toHexString(awtKey)+")"); int x11Mask = X11MaskDefinitions.awtMaskToX11Mask(awtMask); int x11Keysym = X11KeysymDefinitions.awtKeyToX11Keysym(awtKey); debugCallback("registerAwtHotkey() - converted AWT mask '"+ KeyEvent.getKeyModifiersText(awtMask)+"' (0x"+Integer.toHexString(awtMask)+ ") to X11 mask (0x"+Integer.toHexString(x11Mask)+")"); debugCallback("registerAwtHotkey() - converted AWT key '"+ KeyEvent.getKeyText(awtKey)+"' (0x"+Integer.toHexString(awtKey)+ ") to X11 keysym (0x"+Integer.toHexString(x11Keysym)+")"); registerHotkey(id, x11Mask, x11Keysym); debugCallback("-- registerAwtHotkey()"); } /** * Enables/Disables printing of debug messages. * * @param enabled */ public static void setDebugOutput(boolean enabled){ debug = enabled; setDebug(enabled); } /** * Notifies HotkeyListeners about a received KeyEvent. * * This method is used by the C++ code. * Do not use this method from externally. * * @param id */ public static void fireKeyEvent(int id){ for(int i = 0; i < listeners.size(); i++){ listeners.get(i).onHotkey(id); } } /** * Either gives debug messages to a HotkeyListenerDebugEnabled if registered, * or prints to console otherwise. * Does only print if debug is enabled. * * This method is both used by the C++ and Java code, so it should not be synchronized. * Don't use this method from externally. * * @param debugmessage */ public static void debugCallback(String debugmessage){ if(debug){ debugmessage.trim(); if(debugmessage.charAt(debugmessage.length()-1) != '\n'){ debugmessage += "\n"; }else{ while(debugmessage.endsWith("\n\n")){ debugmessage = debugmessage.substring(0, debugmessage.length()-1); } } boolean found = false; for(HotkeyListener l : listeners){ if(l instanceof HotkeyListenerDebugEnabled){ ((HotkeyListenerDebugEnabled)l).debugCallback(debugmessage); found = true; } } if(found == false){ System.out.print(debugmessage); } } } /** * This method unregisters a hotkey. * If the hotkey is not yet registered, nothing will happen. * * @param id */ public native void unregisterHotKey(int id); private native void listen(); private static native void setDebug(boolean debug); private native void clean(); private native void registerHotkey(int id, int mask, int key) throws HotkeyConflictException; }