User-Space Java Bluetooth (JSR-82) Keyboard Driver
... stay tuned!
Motivation
Requirements
- JSR-82 implementation, e.g., BlueCove
Explanation
import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.KeyEvent;
import java.io.IOException;
import javax.bluetooth.L2CAPConnection;
import javax.microedition.io.Connector;
public class BluetoothKeyboard {
public static void main(String[] args) throws
AWTException,
InterruptedException,
IOException {
new BluetoothKeyboard().go();
}
void go() throws IOException, AWTException {
// allow connections to PSM below 0x1001
System.setProperty("bluecove.jsr82.psm_minimum_off", "true");
// connect
L2CAPConnection con = (L2CAPConnection) Connector.open("btl2cap://"
+ kbAddr + ":13;authenticate=true;");
System.out.println("Connected");
// process reports
byte hidReport[] = new byte[255];
while (true){
// get packet from L2CAP connection
int packetLen = con.receive(hidReport);
// dump report
dumpBuffer(hidReport, packetLen);
// process event
handlePacket(hidReport, packetLen);
}
}
void handlePacket(byte[] hidReport, int packetLen) throws
IOException,
AWTException {
// decode keys, only process up to NUM_KEYS
int modifier = hidReport[2];
int nrKeys = packetLen - 4;
if (nrKeys > NUM_KEYS){
nrKeys = NUM_KEYS;
}
// modifier keys
boolean ctrl_pressed = (modifier & 0x11) != 0;
boolean shift_pressed = (modifier & 0x22) != 0;
boolean alt_pressed = (modifier & 0x44) != 0;
boolean command_pressed = (modifier & 0x88) != 0;
// process events
for (int i=0; i< nrKeys; i++){
// find key in last state
int new_event = hidReport[4+i];
if (new_event == 0 || new_event == 44) continue;
for (int j=0; j<NUM_KEYS; j++){
if (new_event == last_keyboard_state[j]){
new_event = 0;
break;
}
}
if (new_event == 0) continue;
// get from table
int new_key = 0;
if (new_event <= 57){
new_key = keytable_us_none[new_event];
} else if (new_event >= 0x4f && new_event <= 0x52) {
new_key = cursor_keys[ new_event - 0x4f];
} else {
continue;
}
injectKey(new_key, shift_pressed, ctrl_pressed,
alt_pressed, command_pressed);
}
// store keyboard state
System.arraycopy(hidReport, 4, last_keyboard_state, 0, NUM_KEYS);
last_modifier = modifier;
}
void injectKey(int new_key, boolean shift_pressed,
boolean ctrl_pressed, boolean alt_pressed,
boolean command_pressed) throws AWTException {
// create Robot instance if necessary
if (robot == null){
robot = new Robot();
}
// use upper case chars
char key = Character.toUpperCase((char) new_key);
// set up modifier keys
if (shift_pressed) {
robot.keyPress(KeyEvent.VK_SHIFT);
}
if (ctrl_pressed) {
robot.keyPress(KeyEvent.VK_CONTROL);
}
if (alt_pressed) {
robot.keyPress(KeyEvent.VK_ALT);
}
if (command_pressed){
robot.keyPress(KeyEvent.VK_META);
}
// inject single press
robot.keyPress(key);
robot.keyRelease(key);
// unset modifier keys
if (command_pressed){
robot.keyRelease(KeyEvent.VK_META);
}
if (alt_pressed) {
robot.keyRelease(KeyEvent.VK_ALT);
}
if (ctrl_pressed) {
robot.keyRelease(KeyEvent.VK_CONTROL);
}
if (shift_pressed) {
robot.keyRelease(KeyEvent.VK_SHIFT);
}
}
public void dumpBuffer(byte[] data, int len){
System.out.print("DATA: ");
for (int i=0;i<len;i++){
int value = data[i];
if (value<0){
value += 256;
}
System.out.print( Integer.toHexString(value) + " ");
}
System.out.println();
}
public static final int cursor_keys[] = {
KeyEvent.VK_RIGHT,
KeyEvent.VK_LEFT,
KeyEvent.VK_DOWN,
KeyEvent.VK_UP
};
public static int keytable_us_none [] = {
0, 0, 0, 0, /* 0-3 */
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', /* 4 - 13 */
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', /* 14 - 23 */
'u', 'v', 'w', 'x', 'y', 'z', /* 24 - 29 */
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', /* 30 - 39 */
KeyEvent.VK_ENTER, 27, KeyEvent.VK_BACK_SPACE, /* 40 - 42 */
KeyEvent.VK_TAB, ' ', /* 42 - 44 */
'-', '=', '[', ']', '\\', 0, ';', '\'', '`', ',', /* 45 - 54 */
'.', '/', KeyEvent.VK_ENTER /* 55 - 57 */
};
public static final int NUM_KEYS = 5;
Robot robot;
byte last_keyboard_state[] = new byte[NUM_KEYS];
int last_modifier = 0;
// address of your keyboard
public static final String kbAddr = "001b63fa61c3";
}
Further Exploration
Build your own HalfKeyboard (tm).