package lejos.nxt; import lejos.util.Delay; /** * Abstraction for an NXT button. * Example:<p> * <code><pre> * Button.ENTER.waitForPressAndRelease(); * Sound.playTone (1000, 1); * </pre></code> */ public class Button implements ListenerCaller { public static final int ID_ENTER = 0x1; public static final int ID_LEFT = 0x2; public static final int ID_RIGHT = 0x4; public static final int ID_ESCAPE = 0x8; private int iCode; private ButtonListener[] iListeners; private int iNumListeners; private static int [] clickFreq = new int[16]; private static int clickVol; private static int clickLen; private static int curButtons = 0; public static final String VOL_SETTING = "lejos.keyclick_volume"; /** * Static constructor to force loading of system settings. */ static { loadSettings(); } /** * The Enter button. */ public static final Button ENTER = new Button (ID_ENTER); /** * The Left button. */ public static final Button LEFT = new Button (ID_LEFT); /** * The Right button. */ public static final Button RIGHT = new Button (ID_RIGHT); /** * The Escape button. */ public static final Button ESCAPE = new Button (ID_ESCAPE); /** * Array containing ENTER, LEFT, RIGHT, ESCAPE, in that order. */ public static final Button[] BUTTONS = { Button.ENTER, Button.LEFT, Button.RIGHT, Button.ESCAPE }; private Button (int aCode) { iCode = aCode; } /** * Return the ID of the button. One of 1, 2, 4 or 8. */ public final int getId() { return iCode; } /** * Check if the button is pressed. * @return <code>true</code> if button is pressed, <code>false</code> otherwise. */ public final boolean isPressed() { return (readButtons() & iCode) != 0; } /** * Wait until the button is released. */ public final void waitForPressAndRelease() { while(!isPressed()) Delay.msDelay(50); while(isPressed()) Thread.yield(); } /** * wait for some button to be pressed (and released). * @param timeout The number of milliseconds to wait. * @return the ID of that button, the same as readButtons(); 0 if timeout */ public static int waitForPress(int timeout) { long end = (timeout == 0 ? 0x7fffffffffffffffL : System.currentTimeMillis() + timeout); int button = 0; // Wait for the button to be up while(0 < readButtons()) { Delay.msDelay(50); if (System.currentTimeMillis() > end) return 0; } // Wait for it to be pressed while(0 == (button = readButtons())) { Delay.msDelay(50); if (System.currentTimeMillis() > end) return 0; } // and wait for it to be released while(0 < readButtons()) Thread.yield(); return button; } /** * wait for some button to be pressed (and released). * @return the ID of that button, the same as readButtons(); */ public static int waitForPress() { return waitForPress(0); } /** * Adds a listener of button events. Each button can serve at most * 4 listeners. */ public synchronized void addButtonListener (ButtonListener aListener) { if (iListeners == null) { iListeners = new ButtonListener[4]; } iListeners[iNumListeners++] = aListener; ListenerThread.get().addButtonToMask(iCode, this); } /** * <i>Low-level API</i> that reads status of buttons. * @return An integer with possibly some bits set: 0x01 (ENTER button pressed) * 0x02 (LEFT button pressed), 0x04 (RIGHT button pressed), 0x08 (ESCAPE button pressed). * If all buttons are released, this method returns 0. */ static native int getButtons(); /** * <i>Low-level API</i> that reads status of buttons. * @return An integer with possibly some bits set: 0x01 (ENTER button pressed) * 0x02 (LEFT button pressed), 0x04 (RIGHT button pressed), 0x08 (ESCAPE button pressed). * If all buttons are released, this method returns 0. */ public static int readButtons() { int newButtons = getButtons(); if (newButtons != curButtons && clickVol != 0) { int tone = clickFreq[newButtons]; if (tone != 0) Sound.playTone(tone, clickLen, -clickVol); } curButtons = newButtons; return newButtons; } /** * Call Button Listeners. Used by ListenerThread. */ public synchronized void callListeners() { for( int i = 0; i < iNumListeners; i++) { if( isPressed()) iListeners[i].buttonPressed( this); else iListeners[i].buttonReleased( this); } } /** * Set the volume used for key clicks * @param vol */ public static void setKeyClickVolume(int vol) { clickVol = vol; } /** * Return the current key click volume. * @return current click volume */ public static int getKeyClickVolume() { return clickVol; } /** * Set the len used for key clicks * @param len the click duration */ public static void setKeyClickLength(int len) { clickLen = len; } /** * Return the current key click length. * @return key click duration */ public static int getKeyClickLength() { return clickLen; } /** * Set the frequency used for a particular key. Setting this to 0 disables * the click. Note that key may also be a corded set of keys. * @param key the NXT key * @param freq the frequency */ public static void setKeyClickTone(int key, int freq) { clickFreq[key] = freq; } /** * Return the click freq for a particular key. * @return key click duration */ public static int getKeyClickTone(int key) { return clickFreq[key]; } /** * Load the current system settings associated with this class. Called * automatically to initialize the class. May be called if it is required * to reload any settings. */ public static void loadSettings() { clickVol = SystemSettings.getIntSetting(VOL_SETTING, 20); clickLen = 50; // setup default tones for the keys and enter+key chords clickFreq[1] = 209 + 697; clickFreq[2] = 209 + 770; clickFreq[4] = 209 + 852; clickFreq[8] = 209 + 941; clickFreq[1+2] = 633 + 770; clickFreq[1+4] = 633 + 852; clickFreq[1+8] = 633 + 941; } }